diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/INSTALLER b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE-3RD-PARTY.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE-3RD-PARTY.txt new file mode 100644 index 0000000000000000000000000000000000000000..1f06ebe0179f7680a98dd399ccae18d6cb7bf0a2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE-3RD-PARTY.txt @@ -0,0 +1,3617 @@ +OpenCV library is redistributed within opencv-python package. +This license applies to OpenCV binary in the directory cv2/. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------ +libvpx is redistributed within all opencv-python Linux packages. +This license applies to libvpx binary in the directory cv2/. + +Copyright (c) 2010, The WebM Project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google, nor the WebM Project, nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +FFmpeg is redistributed within all opencv-python packages. + +Libbluray, libgnutls, libnettle, libhogweed, libintl, libmp3lame, libp11, +librtmp, libsoxr and libtasn1 are redistributed within all opencv-python macOS packages. + +This license applies to the above library binaries in the directory cv2/. + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + +------------------------------------------------------------------------------ +Qt 5 is redistributed within non-headless opencv-python Linux and macOS packages. +libgmp is redistributed within opencv-python macOS packages. +libidn2 is redistributed within opencv-python macOS packages. +libunistring is redistributed within opencv-python macOS packages. +This license applies to the above binaries in the directory cv2/. + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +------------------------------------------------------------------------------ +bzip2 is redistributed within all opencv-python Linux packages. +This license applies to libbz2 binary in the directory cv2/. + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2010 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@bzip.org +bzip2/libbzip2 version 1.0.6 of 6 September 2010 + +------------------------------------------------------------------------------ +libcrypto and libssl are redistributed within all opencv-python Linux and macOS packages. +libopencore-amrnb and libopencore-amrwb are redistributed within all opencv-python Linux and macOS packages. +This license applies to above binaries in the directory cv2/. + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a double license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are adhered to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the routines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publicly available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +------------------------------------------------------------------------------ +libfontconfig is redistributed within all opencv-python macOS packages. +This license applies to libfontconfig binary in the directory cv2/. + +Copyright © 2000,2001,2002,2003,2004,2006,2007 Keith Packard +Copyright © 2005 Patrick Lam +Copyright © 2009 Roozbeh Pournader +Copyright © 2008,2009 Red Hat, Inc. +Copyright © 2008 Danilo Šegan +Copyright © 2012 Google, Inc. + + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of the author(s) not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. The authors make no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------------------------------------------------------------------------------ +libfreetype is redistributed within opencv-python Linux and macOS packages. +This license applies to libfreetype binary in the directory cv2/. + + The FreeType Project LICENSE + ---------------------------- + + 2006-Jan-27 + + Copyright 1996-2002, 2006 by + David Turner, Robert Wilhelm, and Werner Lemberg + + + +Introduction +============ + + The FreeType Project is distributed in several archive packages; + some of them may contain, in addition to the FreeType font engine, + various tools and contributions which rely on, or relate to, the + FreeType Project. + + This license applies to all files found in such packages, and + which do not fall under their own explicit license. The license + affects thus the FreeType font engine, the test programs, + documentation and makefiles, at the very least. + + This license was inspired by the BSD, Artistic, and IJG + (Independent JPEG Group) licenses, which all encourage inclusion + and use of free software in commercial and freeware products + alike. As a consequence, its main points are that: + + o We don't promise that this software works. However, we will be + interested in any kind of bug reports. (`as is' distribution) + + o You can use this software for whatever you want, in parts or + full form, without having to pay us. (`royalty-free' usage) + + o You may not pretend that you wrote this software. If you use + it, or only parts of it, in a program, you must acknowledge + somewhere in your documentation that you have used the + FreeType code. (`credits') + + We specifically permit and encourage the inclusion of this + software, with or without modifications, in commercial products. + We disclaim all warranties covering The FreeType Project and + assume no liability related to The FreeType Project. + + + Finally, many people asked us for a preferred form for a + credit/disclaimer to use in compliance with this license. We thus + encourage you to use the following text: + + """ + Portions of this software are copyright © The FreeType + Project (www.freetype.org). All rights reserved. + """ + + Please replace with the value from the FreeType version you + actually use. + + +Legal Terms +=========== + +0. Definitions +-------------- + + Throughout this license, the terms `package', `FreeType Project', + and `FreeType archive' refer to the set of files originally + distributed by the authors (David Turner, Robert Wilhelm, and + Werner Lemberg) as the `FreeType Project', be they named as alpha, + beta or final release. + + `You' refers to the licensee, or person using the project, where + `using' is a generic term including compiling the project's source + code as well as linking it to form a `program' or `executable'. + This program is referred to as `a program using the FreeType + engine'. + + This license applies to all files distributed in the original + FreeType Project, including all source code, binaries and + documentation, unless otherwise stated in the file in its + original, unmodified form as distributed in the original archive. + If you are unsure whether or not a particular file is covered by + this license, you must contact us to verify this. + + The FreeType Project is copyright (C) 1996-2000 by David Turner, + Robert Wilhelm, and Werner Lemberg. All rights reserved except as + specified below. + +1. No Warranty +-------------- + + THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO + USE, OF THE FREETYPE PROJECT. + +2. Redistribution +----------------- + + This license grants a worldwide, royalty-free, perpetual and + irrevocable right and license to use, execute, perform, compile, + display, copy, create derivative works of, distribute and + sublicense the FreeType Project (in both source and object code + forms) and derivative works thereof for any purpose; and to + authorize others to exercise some or all of the rights granted + herein, subject to the following conditions: + + o Redistribution of source code must retain this license file + (`FTL.TXT') unaltered; any additions, deletions or changes to + the original files must be clearly indicated in accompanying + documentation. The copyright notices of the unaltered, + original files must be preserved in all copies of source + files. + + o Redistribution in binary form must provide a disclaimer that + states that the software is based in part of the work of the + FreeType Team, in the distribution documentation. We also + encourage you to put an URL to the FreeType web page in your + documentation, though this isn't mandatory. + + These conditions apply to any software derived from or based on + the FreeType Project, not just the unmodified files. If you use + our work, you must acknowledge us. However, no fee need be paid + to us. + +3. Advertising +-------------- + + Neither the FreeType authors and contributors nor you shall use + the name of the other for commercial, advertising, or promotional + purposes without specific prior written permission. + + We suggest, but do not require, that you use one or more of the + following phrases to refer to this software in your documentation + or advertising materials: `FreeType Project', `FreeType Engine', + `FreeType library', or `FreeType Distribution'. + + As you have not signed this license, you are not required to + accept it. However, as the FreeType Project is copyrighted + material, only this license, or another one contracted with the + authors, grants you the right to use, distribute, and modify it. + Therefore, by using, distributing, or modifying the FreeType + Project, you indicate that you understand and accept all the terms + of this license. + +4. Contacts +----------- + + There are two mailing lists related to FreeType: + + o freetype@nongnu.org + + Discusses general use and applications of FreeType, as well as + future and wanted additions to the library and distribution. + If you are looking for support, start in this list if you + haven't found anything to help you in the documentation. + + o freetype-devel@nongnu.org + + Discusses bugs, as well as engine internals, design issues, + specific licenses, porting, etc. + + Our home page can be found at + + https://www.freetype.org + +------------------------------------------------------------------------------ +libpng is redistributed within all opencv-python Linux and macOS packages. +This license applies to libpng binary in the directory cv2/. + +PNG Reference Library License version 2 +--------------------------------------- + + * Copyright (c) 1995-2019 The PNG Reference Library Authors. + * Copyright (c) 2018-2019 Cosmin Truta. + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. + * Copyright (c) 1996-1997 Andreas Dilger. + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +The software is supplied "as is", without warranty of any kind, +express or implied, including, without limitation, the warranties +of merchantability, fitness for a particular purpose, title, and +non-infringement. In no event shall the Copyright owners, or +anyone distributing the software, be liable for any damages or +other liability, whether in contract, tort or otherwise, arising +from, out of, or in connection with the software, or the use or +other dealings in the software, even if advised of the possibility +of such damage. + +Permission is hereby granted to use, copy, modify, and distribute +this software, or portions hereof, for any purpose, without fee, +subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you + use this software in a product, an acknowledgment in the product + documentation would be appreciated, but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + + +PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) +----------------------------------------------------------------------- + +libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are +Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are +derived from libpng-1.0.6, and are distributed according to the same +disclaimer and license as libpng-1.0.6 with the following individuals +added to the list of Contributing Authors: + + Simon-Pierre Cadieux + Eric S. Raymond + Mans Rullgard + Cosmin Truta + Gilles Vollant + James Yu + Mandar Sahastrabuddhe + Google Inc. + Vadim Barkov + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of + the library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is + with the user. + +Some files in the "contrib" directory and some configure-generated +files that are distributed with libpng have other copyright owners, and +are released under other open source licenses. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from +libpng-0.96, and are distributed according to the same disclaimer and +license as libpng-0.96, with the following individuals added to the +list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, +and are distributed according to the same disclaimer and license as +libpng-0.88, with the following individuals added to the list of +Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +Some files in the "scripts" directory have other copyright owners, +but are released under this license. + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing +Authors and Group 42, Inc. disclaim all warranties, expressed or +implied, including, without limitation, the warranties of +merchantability and of fitness for any purpose. The Contributing +Authors and Group 42, Inc. assume no liability for direct, indirect, +incidental, special, exemplary, or consequential damages, which may +result from the use of the PNG Reference Library, even if advised of +the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, +without fee, and encourage the use of this source code as a component +to supporting the PNG file format in commercial products. If you use +this source code in a product, acknowledgment is not required but would +be appreciated. + +------------------------------------------------------------------------------ +libz is redistributed within all opencv-python Linux packages. +This license applies to libz binary in the directory cv2/. + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +------------------------------------------------------------------------------ +libdav1d is redistributed within opencv-python macOS packages. +This license applies to libdav1d binary in the directory cv2/. + +Copyright © 2018-2019, VideoLAN and dav1d authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libffi is redistributed within opencv-python macOS packages. +This license applies to libffi binary in the directory cv2/. + +libffi - Copyright (c) 1996-2020 Anthony Green, Red Hat, Inc and others. +See source files for details. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------ +libogg is redistributed within opencv-python macOS packages. +This license applies to libogg binary in the directory cv2/. + +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libopenjp2 is redistributed within opencv-python macOS packages. +This license applies to libopenjp2 binary in the directory cv2/. + +The copyright in this software is being made available under the 2-clauses +BSD License, included below. This software may be subject to other third +party and contributor rights, including patent rights, and no such rights +are granted under this license. + +Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium +Copyright (c) 2002-2014, Professor Benoit Macq +Copyright (c) 2003-2014, Antonin Descampe +Copyright (c) 2003-2009, Francois-Olivier Devaux +Copyright (c) 2005, Herve Drolon, FreeImage Team +Copyright (c) 2002-2003, Yannick Verschueren +Copyright (c) 2001-2003, David Janssens +Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France +Copyright (c) 2012, CS Systemes d'Information, France + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libopus is redistributed within opencv-python macOS packages. +This license applies to libopus binary in the directory cv2/. + +Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic, + Jean-Marc Valin, Timothy B. Terriberry, + CSIRO, Gregory Maxwell, Mark Borgerding, + Erik de Castro Lopo + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of Internet Society, IETF or IETF Trust, nor the +names of specific contributors, may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Opus is subject to the royalty-free patent licenses which are +specified at: + +Xiph.Org Foundation: +https://datatracker.ietf.org/ipr/1524/ + +Microsoft Corporation: +https://datatracker.ietf.org/ipr/1914/ + +Broadcom Corporation: +https://datatracker.ietf.org/ipr/1526/ + +------------------------------------------------------------------------------ +librav1e is redistributed within opencv-python macOS packages. +This license applies to librav1e binary in the directory cv2/. + +BSD 2-Clause License + +Copyright (c) 2017-2020, the rav1e contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libsnappy is redistributed within opencv-python macOS packages. +This license applies to libsnappy binary in the directory cv2/. + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libspeex is redistributed within opencv-python macOS packages. +This license applies to libspeex binary in the directory cv2/. + +Copyright 2002-2008 Xiph.org Foundation +Copyright 2002-2008 Jean-Marc Valin +Copyright 2005-2007 Analog Devices Inc. +Copyright 2005-2008 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) +Copyright 1993, 2002, 2006 David Rowe +Copyright 2003 EpicGames +Copyright 1992-1994 Jutta Degener, Carsten Bormann + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libsrt is redistributed within opencv-python macOS packages. +This license applies to libsrt binary in the directory cv2/. + +/* + * + * Copyright (c) 2001-2017 Cisco Systems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of the Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + + Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +------------------------------------------------------------------------------ +libtheoradec and libtheoraenc are redistributed within opencv-python macOS packages. +This license applies to libtheoradec and libtheoraenc binaries in the directory cv2/. + + Copyright (C) 2002-2009 Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libwebp and libwebpmux are redistributed within all opencv-python packages. +This license applies to libwebp and libwebpmux binaries in the directory cv2/. + +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +libvorbis and libvorbisenc are redistributed within opencv-python macOS packages. +This license applies to libvorbis and libvorbisenc binaries in the directory cv2/. + +Copyright (c) 2002-2020 Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +Libxcb utility libraries are redistributed within opencv-python non-headless Linux packages. +This license applies to libxcb related binaries in the directory cv2/. + +Copyright (C) 2001-2006 Bart Massey, Jamey Sharp, and Josh Triplett. +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors +or their institutions shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this +Software without prior written authorization from the +authors. + +------------------------------------------------------------------------------ +Libxcb-image is redistributed within opencv-python non-headless Linux packages. +This license applies to libxcb-image binary in the directory cv2/. + +Copyright © 2007-2008 Bart Massey +Copyright © 2008 Julien Danjou +Copyright © 2008 Keith Packard + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors or +their institutions shall not be used in advertising or otherwise to +promote the sale, use or other dealings in this Software without +prior written authorization from the authors. + +------------------------------------------------------------------------------ +Libxcb-util is redistributed within opencv-python non-headless Linux packages. +This license applies to libxcb-util binary in the directory cv2/. + +Copyright © 2008 Bart Massey +Copyright © 2008 Ian Osgood +Copyright © 2008 Jamey Sharp +Copyright © 2008 Josh Triplett +Copyright © 2008-2009 Julien Danjou + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors or +their institutions shall not be used in advertising or otherwise to +promote the sale, use or other dealings in this Software without +prior written authorization from the authors. + +------------------------------------------------------------------------------ +Libxcb-render-util is redistributed within opencv-python non-headless Linux packages. +This license applies to libxcb-render-util binary in the directory cv2/. + +Copyright © 2000 Keith Packard + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Keith Packard not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. Keith Packard makes no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +Copyright © 2006 Jamey Sharp. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors or their +institutions shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization from the authors. + +Copyright © 2006 Ian Osgood + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors or their +institutions shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization from the authors. + +------------------------------------------------------------------------------ +Libxcb-icccm is redistributed within opencv-python non-headless Linux packages. +This license applies to Libxcb-icccm binary in the directory cv2/. + +Copyright © 2008-2011 Arnaud Fontaine +Copyright © 2007-2008 Vincent Torri + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the authors or +their institutions shall not be used in advertising or otherwise to +promote the sale, use or other dealings in this Software without +prior written authorization from the authors. + +------------------------------------------------------------------------------ +libXau is redistributed within opencv-python non-headless Linux packages. +This license applies to libXau binary in the directory cv2/. + +Copyright 1988, 1993, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +------------------------------------------------------------------------------ +Vulkan headers are redistributed within all opencv-python packages. +This license applies to Vulkan headers in the directory 3rdparty/include/vulkan. + +Copyright (c) 2015-2018 The Khronos Group Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------------ +Libjpeg-turbo is redistributed within all opencv-python packages as build option. + +libjpeg-turbo Licenses +====================== + +libjpeg-turbo is covered by three compatible BSD-style open source licenses: + +- The IJG (Independent JPEG Group) License, which is listed in + [README.ijg](README.ijg) + + This license applies to the libjpeg API library and associated programs + (any code inherited from libjpeg, and any modifications to that code.) + +- The Modified (3-clause) BSD License, which is listed below + + This license covers the TurboJPEG API library and associated programs, as + well as the build system. + +- The [zlib License](https://opensource.org/licenses/Zlib) + + This license is a subset of the other two, and it covers the libjpeg-turbo + SIMD extensions. + + +Complying with the libjpeg-turbo Licenses +========================================= + +This section provides a roll-up of the libjpeg-turbo licensing terms, to the +best of our understanding. + +1. If you are distributing a modified version of the libjpeg-turbo source, + then: + + 1. You cannot alter or remove any existing copyright or license notices + from the source. + + **Origin** + - Clause 1 of the IJG License + - Clause 1 of the Modified BSD License + - Clauses 1 and 3 of the zlib License + + 2. You must add your own copyright notice to the header of each source + file you modified, so others can tell that you modified that file (if + there is not an existing copyright header in that file, then you can + simply add a notice stating that you modified the file.) + + **Origin** + - Clause 1 of the IJG License + - Clause 2 of the zlib License + + 3. You must include the IJG README file, and you must not alter any of the + copyright or license text in that file. + + **Origin** + - Clause 1 of the IJG License + +2. If you are distributing only libjpeg-turbo binaries without the source, or + if you are distributing an application that statically links with + libjpeg-turbo, then: + + 1. Your product documentation must include a message stating: + + This software is based in part on the work of the Independent JPEG + Group. + + **Origin** + - Clause 2 of the IJG license + + 2. If your binary distribution includes or uses the TurboJPEG API, then + your product documentation must include the text of the Modified BSD + License (see below.) + + **Origin** + - Clause 2 of the Modified BSD License + +3. You cannot use the name of the IJG or The libjpeg-turbo Project or the + contributors thereof in advertising, publicity, etc. + + **Origin** + - IJG License + - Clause 3 of the Modified BSD License + +4. The IJG and The libjpeg-turbo Project do not warrant libjpeg-turbo to be + free of defects, nor do we accept any liability for undesirable + consequences resulting from your use of the software. + + **Origin** + - IJG License + - Modified BSD License + - zlib License + + +The Modified (3-clause) BSD License +=================================== + +Copyright (C)2009-2022 D. R. Commander. All Rights Reserved.
+Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +- Neither the name of the libjpeg-turbo Project nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + +Why Three Licenses? +=================== + +The zlib License could have been used instead of the Modified (3-clause) BSD +License, and since the IJG License effectively subsumes the distribution +conditions of the zlib License, this would have effectively placed +libjpeg-turbo binary distributions under the IJG License. However, the IJG +License specifically refers to the Independent JPEG Group and does not extend +attribution and endorsement protections to other entities. Thus, it was +desirable to choose a license that granted us the same protections for new code +that were granted to the IJG for code derived from their software. + +------------------------------------------------------------------------------ +Libspng is redistributed within all opencv-python packages as build option. + +BSD 2-Clause License + +Copyright (c) 2018-2022, Randy +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +QUIRC library is redistributed within all opencv-python packages. + +quirc -- QR-code recognition library +Copyright (C) 2010-2012 Daniel Beer + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all +copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------------------------------------------------------------------------------ +Flatbuffers library is redistributed within all opencv-python packages. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------ +Protobuf library is redistributed within all opencv-python packages. + +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + +------------------------------------------------------------------------------ +OpenJPEG library is redistributed within all opencv-python packages. + +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium + * Copyright (c) 2002-2014, Professor Benoit Macq + * Copyright (c) 2003-2014, Antonin Descampe + * Copyright (c) 2003-2009, Francois-Olivier Devaux + * Copyright (c) 2005, Herve Drolon, FreeImage Team + * Copyright (c) 2002-2003, Yannick Verschueren + * Copyright (c) 2001-2003, David Janssens + * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France + * Copyright (c) 2012, CS Systemes d'Information, France + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +------------------------------------------------------------------------------ +TIFF library is redistributed within all opencv-python packages. + +Copyright (c) 1988-1997 Sam Leffler +Copyright (c) 1991-1997 Silicon Graphics, Inc. + +Permission to use, copy, modify, distribute, and sell this software and +its documentation for any purpose is hereby granted without fee, provided +that (i) the above copyright notices and this permission notice appear in +all copies of the software and related documentation, and (ii) the names of +Sam Leffler and Silicon Graphics may not be used in any advertising or +publicity relating to the software without the specific, prior written +permission of Sam Leffler and Silicon Graphics. + +THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR +ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF +LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. + +------------------------------------------------------------------------------ +Intel(R) IPP ICV library statically linked within x86 and x86_64 opencv-python packages. + +Intel(R) Integrated Performance Primitives 2021 Update 10 + +Intel Simplified Software License (Version October 2022) + +Intel(R) Integrated Performance Primitives (Intel(R) IPP) : Copyright (C) 1997 Intel Corporation + +Use and Redistribution. You may use and redistribute the software, which is +provided in binary form only, (the "Software"), without modification, +provided the following conditions are met: + +* Redistributions must reproduce the above copyright notice and these + terms of use in the Software and in the documentation and/or other materials + provided with the distribution. +* Neither the name of Intel nor the names of its suppliers may be used to + endorse or promote products derived from this Software without specific + prior written permission. +* No reverse engineering, decompilation, or disassembly of the Software is + permitted, nor any modification or alteration of the Software or its operation + at any time, including during execution. + +No other licenses. Except as provided in the preceding section, Intel grants no +licenses or other rights by implication, estoppel or otherwise to, patent, +copyright, trademark, trade name, service mark or other intellectual property +licenses or rights of Intel. + +Third party software. "Third Party Software" means the files (if any) listed +in the "third-party-software.txt" or other similarly-named text file that may +be included with the Software. Third Party Software, even if included with the +distribution of the Software, may be governed by separate license terms, including +without limitation, third party license terms, open source software notices and +terms, and/or other Intel software license terms. These separate license terms +solely govern Your use of the Third Party Software. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE +DISCLAIMED. THIS SOFTWARE IS NOT INTENDED FOR USE IN SYSTEMS OR APPLICATIONS +WHERE FAILURE OF THE SOFTWARE MAY CAUSE PERSONAL INJURY OR DEATH AND YOU AGREE +THAT YOU ARE FULLY RESPONSIBLE FOR ANY CLAIMS, COSTS, DAMAGES, EXPENSES, AND +ATTORNEYS' FEES ARISING OUT OF ANY SUCH USE, EVEN IF ANY CLAIM ALLEGES THAT +INTEL WAS NEGLIGENT REGARDING THE DESIGN OR MANUFACTURE OF THE SOFTWARE. + +LIMITATION OF LIABILITY. IN NO EVENT WILL INTEL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +No support. Intel may make changes to the Software, at any time without notice, +and is not obligated to support, update or provide training for the Software. + +Termination. Your right to use the Software is terminated in the event of your +breach of this license. + +Feedback. Should you provide Intel with comments, modifications, corrections, +enhancements or other input ("Feedback") related to the Software, Intel will be +free to use, disclose, reproduce, license or otherwise distribute or exploit the +Feedback in its sole discretion without any obligations or restrictions of any +kind, including without limitation, intellectual property rights or licensing +obligations. + +Compliance with laws. You agree to comply with all relevant laws and regulations +governing your use, transfer, import or export (or prohibition thereof) of the +Software. + +Governing law. All disputes will be governed by the laws of the United States of +America and the State of Delaware without reference to conflict of law +principles and subject to the exclusive jurisdiction of the state or federal +courts sitting in the State of Delaware, and each party agrees that it submits +to the personal jurisdiction and venue of those courts and waives any +objections. THE UNITED NATIONS CONVENTION ON CONTRACTS FOR THE INTERNATIONAL +SALE OF GOODS (1980) IS SPECIFICALLY EXCLUDED AND WILL NOT APPLY TO THE SOFTWARE. + +------------------------------------------------------------------------------ +Orbbec SDK distributed with arm64 MacOS packages. + +MIT License + +Copyright (c) 2023 OrbbecDeveloper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +libavif library and it's dependnecies are redistributed within all opencv-python packages. + +Copyright 2019 Joe Drago. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +Files: src/obu.c + +Copyright © 2018-2019, VideoLAN and dav1d authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +Files: third_party/iccjpeg/* + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, +with respect to this software, its quality, accuracy, merchantability, or +fitness for a particular purpose. This software is provided "AS IS", and you, +its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-2013, Thomas G. Lane, Guido Vollbeding. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + + +The Unix configuration script "configure" was produced with GNU Autoconf. +It is copyright by the Free Software Foundation but is freely distributable. +The same holds for its supporting scripts (config.guess, config.sub, +ltmain.sh). Another support script, install-sh, is copyright by X Consortium +but is also freely distributable. + +The IJG distribution formerly included code to read and write GIF files. +To avoid entanglement with the Unisys LZW patent, GIF reading support has +been removed altogether, and the GIF writer has been simplified to produce +"uncompressed GIFs". This technique does not use the LZW algorithm; the +resulting GIF files are larger than usual, but are readable by all standard +GIF decoders. + +We are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + +------------------------------------------------------------------------------ + +Files: contrib/gdk-pixbuf/* + +Copyright 2020 Emmanuel Gil Peyrot. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +Files: android_jni/gradlew* + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------ + +Files: third_party/libyuv/* + +Copyright 2011 The LibYuv Project Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +aom library and it's dependnecies are redistributed within all opencv-python packages. + +Copyright (c) 2016, Alliance for Open Media. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +KAZE Features library is redistributed within all opencv-python packages. + +Copyright (c) 2012, Pablo Fernández Alcantarilla +All Rights Reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the copyright holders nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +AKAZE Features library is redistributed within all opencv-python packages. + +Copyright (c) 2014, Pablo Fernandez Alcantarilla, Jesus Nuevo +All Rights Reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the copyright holders nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +MLAS library is redistributed within all opencv-python packages. + +MIT License + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +CLapack library is redistributed within opencv-python packages for Windows. + +Copyright (c) 1992-2017 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. +Copyright (c) 2000-2017 The University of California Berkeley. All + rights reserved. +Copyright (c) 2006-2017 The University of Colorado Denver. All rights + reserved. + +$COPYRIGHT$ + +Additional copyrights may follow + +$HEADER$ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + +- Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +The copyright holders provide no reassurances that the source code +provided does not infringe any patent, copyright, or any other +intellectual property rights of third parties. The copyright holders +disclaim any liability to any recipient for claims brought against +recipient by any third party for infringement of that parties +intellectual property rights. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..328bf50632a988cf1cc494d557936d84fec16335 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Olli-Pekka Heinisuo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/METADATA b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..cfe397e9a05a2302868f6135ed6fe0d32f8d9fef --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/METADATA @@ -0,0 +1,301 @@ +Metadata-Version: 2.1 +Name: opencv-python-headless +Version: 5.0.0.93 +Summary: Wrapper package for OpenCV python bindings. +Home-page: https://github.com/opencv/opencv-python +Maintainer: OpenCV Team +License: Apache 2.0 +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: C++ +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Image Recognition +Classifier: Topic :: Software Development +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE-3RD-PARTY.txt +License-File: LICENSE.txt +Requires-Dist: numpy<2.0; python_version < "3.9" +Requires-Dist: numpy>=2; python_version >= "3.9" + +[![Downloads](https://static.pepy.tech/badge/opencv-python)](http://pepy.tech/project/opencv-python) + +### Keep OpenCV Free + +OpenCV is raising funds to keep the library free for everyone, and we need the support of the entire community to do it. [Donate to OpenCV on Github](https://github.com/sponsors/opencv) to show your support. + +- [OpenCV on Wheels](#opencv-on-wheels) + - [Installation and Usage](#installation-and-usage) +- [Frequently Asked Questions](#frequently-asked-questions) +- [Documentation for opencv-python](#documentation-for-opencv-python) + - [CI build process](#ci-build-process) + - [Manual builds](#manual-builds) + - [Manual debug builds](#manual-debug-builds) + - [Source distributions](#source-distributions) + - [Licensing](#licensing) + - [Versioning](#versioning) + - [Releases](#releases) + - [Development builds](#development-builds) + - [Manylinux wheels](#manylinux-wheels) + - [Supported Python versions](#supported-python-versions) + - [Backward compatibility](#backward-compatibility) + +## OpenCV on Wheels + +Pre-built CPU-only OpenCV packages for Python. + +Check the manual build section if you wish to compile the bindings from source to enable additional modules such as CUDA. + +### Installation and Usage + +1. If you have previous/other manually installed (= not installed via ``pip``) version of OpenCV installed (e.g. cv2 module in the root of Python's site-packages), remove it before installation to avoid conflicts. +2. Make sure that your `pip` version is up-to-date (19.3 is the minimum supported version): `pip install --upgrade pip`. Check version with `pip -V`. For example Linux distributions ship usually with very old `pip` versions which cause a lot of unexpected problems especially with the `manylinux` format. +3. Select the correct package for your environment: + + There are four different packages (see options 1, 2, 3 and 4 below) and you should **SELECT ONLY ONE OF THEM**. Do not install multiple different packages in the same environment. There is no plugin architecture: all the packages use the same namespace (`cv2`). If you installed multiple different packages in the same environment, uninstall them all with ``pip uninstall`` and reinstall only one package. + + **a.** Packages for standard desktop environments (Windows, macOS, almost any GNU/Linux distribution) + + - Option 1 - Main modules package: ``pip install opencv-python`` + - Option 2 - Full package (contains both main modules and contrib/extra modules): ``pip install opencv-contrib-python`` (check contrib/extra modules listing from [OpenCV documentation](https://docs.opencv.org/master/)) + + **b.** Packages for server (headless) environments (such as Docker, cloud environments etc.), no GUI library dependencies + + These packages are smaller than the two other packages above because they do not contain any GUI functionality (not compiled with Qt / other GUI components). This means that the packages avoid a heavy dependency chain to X11 libraries and you will have for example smaller Docker images as a result. You should always use these packages if you do not use `cv2.imshow` et al. or you are using some other package (such as PyQt) than OpenCV to create your GUI. + + - Option 3 - Headless main modules package: ``pip install opencv-python-headless`` + - Option 4 - Headless full package (contains both main modules and contrib/extra modules): ``pip install opencv-contrib-python-headless`` (check contrib/extra modules listing from [OpenCV documentation](https://docs.opencv.org/master/)) + +4. Import the package: + + ``import cv2`` + + All packages contain Haar cascade files. ``cv2.data.haarcascades`` can be used as a shortcut to the data folder. For example: + + ``cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")`` + +5. Read [OpenCV documentation](https://docs.opencv.org/master/) + +6. Before opening a new issue, read the FAQ below and have a look at the other issues which are already open. + +Frequently Asked Questions +-------------------------- + +**Q: Do I need to install also OpenCV separately?** + +A: No, the packages are special wheel binary packages and they already contain statically built OpenCV binaries. + +**Q: Pip install fails with ``ModuleNotFoundError: No module named 'skbuild'``?** + +Since ``opencv-python`` version 4.3.0.\*, ``manylinux1`` wheels were replaced by ``manylinux2014`` wheels. If your pip is too old, it will try to use the new source distribution introduced in 4.3.0.38 to manually build OpenCV because it does not know how to install ``manylinux2014`` wheels. However, source build will also fail because of too old ``pip`` because it does not understand build dependencies in ``pyproject.toml``. To use the new ``manylinux2014`` pre-built wheels (or to build from source), your ``pip`` version must be >= 19.3. Please upgrade ``pip`` with ``pip install --upgrade pip``. + +**Q: Import fails on Windows: ``ImportError: DLL load failed: The specified module could not be found.``?** + +A: If the import fails on Windows, make sure you have [Visual C++ redistributable 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48145) installed. If you are using older Windows version than Windows 10 and latest system updates are not installed, [Universal C Runtime](https://support.microsoft.com/en-us/help/2999226/update-for-universal-c-runtime-in-windows) might be also required. + +Windows N and KN editions do not include Media Feature Pack which is required by OpenCV. If you are using Windows N or KN edition, please install also [Windows Media Feature Pack](https://support.microsoft.com/en-us/help/3145500/media-feature-pack-list-for-windows-n-editions). + +If you have Windows Server 2012+, media DLLs are probably missing too; please install the Feature called "Media Foundation" in the Server Manager. Beware, some posts advise to install "Windows Server Essentials Media Pack", but this one requires the "Windows Server Essentials Experience" role, and this role will deeply affect your Windows Server configuration (by enforcing active directory integration etc.); so just installing the "Media Foundation" should be a safer choice. + +If the above does not help, check if you are using Anaconda. Old Anaconda versions have a bug which causes the error, see [this issue](https://github.com/opencv/opencv-python/issues/36) for a manual fix. + +If you still encounter the error after you have checked all the previous solutions, download [Dependencies](https://github.com/lucasg/Dependencies) and open the ``cv2.pyd`` (located usually at ``C:\Users\username\AppData\Local\Programs\Python\PythonXX\Lib\site-packages\cv2``) file with it to debug missing DLL issues. + +**Q: I have some other import errors?** + +A: Make sure you have removed old manual installations of OpenCV Python bindings (cv2.so or cv2.pyd in site-packages). + +**Q: Function foo() or method bar() returns wrong result, throws exception or crashes interpreter. What should I do?** + +A: The repository contains only OpenCV-Python package build scripts, but not OpenCV itself. Python bindings for OpenCV are developed in official OpenCV repository and it's the best place to report issues. Also please check [OpenCV wiki](https://github.com/opencv/opencv/wiki) and [the official OpenCV forum](https://forum.opencv.org/) before file new bugs. + +**Q: Why the packages do not include non-free algorithms?** + +A: Non-free algorithms such as SURF are not included in these packages because they are patented / non-free and therefore cannot be distributed as built binaries. Note that SIFT is included in the builds due to patent expiration since OpenCV versions 4.3.0 and 3.4.10. See this issue for more info: https://github.com/skvark/opencv-python/issues/126 + +**Q: Why the package and import are different (opencv-python vs. cv2)?** + +A: It's easier for users to understand ``opencv-python`` than ``cv2`` and it makes it easier to find the package with search engines. `cv2` (old interface in old OpenCV versions was named as `cv`) is the name that OpenCV developers chose when they created the binding generators. This is kept as the import name to be consistent with different kind of tutorials around the internet. Changing the import name or behaviour would be also confusing to experienced users who are accustomed to the ``import cv2``. + +## Documentation for opencv-python + +[![Windows Build Status](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_windows.yml/badge.svg)](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_windows.yml) +[![(Linux Build status)](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_linux.yml/badge.svg)](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_linux.yml) +[![(Mac OS Build status)](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_macos.yml/badge.svg)](https://github.com/opencv/opencv-python/actions/workflows/build_wheels_macos.yml) + +The aim of this repository is to provide means to package each new [OpenCV release](https://github.com/opencv/opencv/releases) for the most used Python versions and platforms. + +### CI build process + +The project is structured like a normal Python package with a standard ``setup.py`` file. +The build process for a single entry in the build matrices is as follows (see for example `.github/workflows/build_wheels_linux.yml` file): + +0. In Linux and MacOS build: get OpenCV's optional C dependencies that we compile against + +1. Checkout repository and submodules + + - OpenCV is included as submodule and the version is updated + manually by maintainers when a new OpenCV release has been made + - Contrib modules are also included as a submodule + +2. Find OpenCV version from the sources + +3. Build OpenCV + + - tests are disabled, otherwise build time increases too much + - there are 4 build matrix entries for each build combination: with and without contrib modules, with and without GUI (headless) + - Linux builds run in manylinux Docker containers (CentOS 5) + - source distributions are separate entries in the build matrix + +4. Rearrange OpenCV's build result, add our custom files and generate wheel + +5. Linux and macOS wheels are transformed with auditwheel and delocate, correspondingly + +6. Install the generated wheel +7. Test that Python can import the library and run some sanity checks +8. Use twine to upload the generated wheel to PyPI (only in release builds) + +Steps 1--4 are handled by ``pip wheel``. + +The build can be customized with environment variables. In addition to any variables that OpenCV's build accepts, we recognize: + +- ``CI_BUILD``. Set to ``1`` to emulate the CI environment build behaviour. Used only in CI builds to force certain build flags on in ``setup.py``. Do not use this unless you know what you are doing. +- ``ENABLE_CONTRIB`` and ``ENABLE_HEADLESS``. Set to ``1`` to build the contrib and/or headless version +- ``ENABLE_JAVA``, Set to ``1`` to enable the Java client build. This is disabled by default. +- ``CMAKE_ARGS``. Additional arguments for OpenCV's CMake invocation. You can use this to make a custom build. + +See the next section for more info about manual builds outside the CI environment. + +### Manual builds + +If some dependency is not enabled in the pre-built wheels, you can also run the build locally to create a custom wheel. + +1. Clone this repository: `git clone --recursive https://github.com/opencv/opencv-python.git` +2. ``cd opencv-python`` + - you can use `git` to checkout some other version of OpenCV in the `opencv` and `opencv_contrib` submodules if needed +3. Add custom Cmake flags if needed, for example: `export CMAKE_ARGS="-DSOME_FLAG=ON -DSOME_OTHER_FLAG=OFF"` (in Windows you need to set environment variables differently depending on Command Line or PowerShell) +4. Select the package flavor which you wish to build with `ENABLE_CONTRIB` and `ENABLE_HEADLESS`: i.e. `export ENABLE_CONTRIB=1` if you wish to build `opencv-contrib-python` +5. Run ``pip wheel . --verbose``. NOTE: make sure you have the latest ``pip`` version, the ``pip wheel`` command replaces the old ``python setup.py bdist_wheel`` command which does not support ``pyproject.toml``. + - this might take anything from 5 minutes to over 2 hours depending on your hardware +6. Pip will print fresh wheel location at the end of build procedure. If you use old approach with `setup.py` file wheel package will be placed in `dist` folder. Package is ready and you can do with that whatever you wish. + - Optional: on Linux use some of the `manylinux` images as a build hosts if maximum portability is needed and run `auditwheel` for the wheel after build + - Optional: on macOS use ``delocate`` (same as ``auditwheel`` but for macOS) for better portability + +#### Manual debug builds + +In order to build `opencv-python` in an unoptimized debug build, you need to side-step the normal process a bit. + +1. Install the packages `scikit-build` and `numpy` via pip. +2. Run the command `python setup.py bdist_wheel --build-type=Debug`. +3. Install the generated wheel file in the `dist/` folder with `pip install dist/wheelname.whl`. + +If you would like the build produce all compiler commands, then the following combination of flags and environment variables has been tested to work on Linux: +``` +export CMAKE_ARGS='-DCMAKE_VERBOSE_MAKEFILE=ON' +export VERBOSE=1 + +python3 setup.py bdist_wheel --build-type=Debug +``` + +See this issue for more discussion: https://github.com/opencv/opencv-python/issues/424 + +#### Source distributions + +Since OpenCV version 4.3.0, also source distributions are provided in PyPI. This means that if your system is not compatible with any of the wheels in PyPI, ``pip`` will attempt to build OpenCV from sources. If you need a OpenCV version which is not available in PyPI as a source distribution, please follow the manual build guidance above instead of this one. + +You can also force ``pip`` to build the wheels from the source distribution. Some examples: + +- ``pip install --no-binary opencv-python opencv-python`` +- ``pip install --no-binary :all: opencv-python`` + +If you need contrib modules or headless version, just change the package name (step 4 in the previous section is not needed). However, any additional CMake flags can be provided via environment variables as described in step 3 of the manual build section. If none are provided, OpenCV's CMake scripts will attempt to find and enable any suitable dependencies. Headless distributions have hard coded CMake flags which disable all possible GUI dependencies. + +On slow systems such as Raspberry Pi the full build may take several hours. On a 8-core Ryzen 7 3700X the build takes about 6 minutes. + +### Licensing + +Opencv-python package (scripts in this repository) is available under MIT license. + +OpenCV itself is available under [Apache 2](https://github.com/opencv/opencv/blob/master/LICENSE) license. + +Third party package licenses are at [LICENSE-3RD-PARTY.txt](https://github.com/opencv/opencv-python/blob/master/LICENSE-3RD-PARTY.txt). + +All wheels ship with [FFmpeg](http://ffmpeg.org) licensed under the [LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). + +Non-headless Linux wheels ship with [Qt 5](http://doc.qt.io/qt-5/lgpl.html) licensed under the [LGPLv3](http://www.gnu.org/licenses/lgpl-3.0.html). + +The packages include also other binaries. Full list of licenses can be found from [LICENSE-3RD-PARTY.txt](https://github.com/opencv/opencv-python/blob/master/LICENSE-3RD-PARTY.txt). + +### Versioning + +``find_version.py`` script searches for the version information from OpenCV sources and appends also a revision number specific to this repository to the version string. It saves the version information to ``version.py`` file under ``cv2`` in addition to some other flags. + +### Releases + +A release is made and uploaded to PyPI when a new tag is pushed to master branch. These tags differentiate packages (this repo might have modifications but OpenCV version stays same) and should be incremented sequentially. In practice, release version numbers look like this: + +``cv_major.cv_minor.cv_revision.package_revision`` e.g. ``3.1.0.0`` + +The master branch follows OpenCV master branch releases. 3.4 branch follows OpenCV 3.4 bugfix releases. + +### Development builds + +Every commit to the master branch of this repo will be built. Possible build artifacts use local version identifiers: + +``cv_major.cv_minor.cv_revision+git_hash_of_this_repo`` e.g. ``3.1.0+14a8d39`` + +These artifacts can't be and will not be uploaded to PyPI. + +### Manylinux wheels + +Linux wheels are built using [manylinux2014](https://github.com/pypa/manylinux). These wheels should work out of the box for most of the distros (which use GNU C standard library) out there since they are built against an old version of glibc. + +The default ``manylinux2014`` images have been extended with some OpenCV dependencies. See [Docker folder](https://github.com/skvark/opencv-python/tree/master/docker) for more info. + +### Supported Python versions + +Python 3.x compatible pre-built wheels are provided for the officially supported Python versions (not in EOL): + +- 3.7 +- 3.8 +- 3.9 +- 3.10 +- 3.11 +- 3.12 +- 3.13 +- 3.14 + +### Backward compatibility + +Starting from 4.2.0 and 3.4.9 builds the macOS Travis build environment was updated to XCode 9.4. The change effectively dropped support for older than 10.13 macOS versions. + +Starting from 4.3.0 and 3.4.10 builds the Linux build environment was updated from `manylinux1` to `manylinux2014`. This dropped support for old Linux distributions. + +Starting from version 4.7.0 the Mac OS GitHub Actions build environment was update to version 11. Mac OS 10.x support deprecated. See https://github.com/actions/runner-images/issues/5583 + +Starting from version 4.9.0 the Mac OS GitHub Actions build environment was update to version 12. Mac OS 10.x support deprecated by Brew and most of used packages. + + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/RECORD b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..fa57e6330ec89d479cd4bc8ef365d36131be5cd7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/RECORD @@ -0,0 +1,158 @@ +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/config-3.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/config.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/data/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/load_config_py2.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/load_config_py3.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/mat_wrapper/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/misc/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/misc/version.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/typing/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/utils/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/wangpy11/Documents/blog/3D-scenes/Scene_04/.venv/lib/python3.9/site-packages/cv2/version.cpython-39.pyc,, +cv2/.dylibs/libIex-3_3.32.3.3.4.dylib,sha256=359aqFQtDnqb0qZGGpiUAkpBvS-MgfKg4R-UCGt_RVA,571216 +cv2/.dylibs/libIlmThread-3_3.32.3.3.4.dylib,sha256=-4sgOB7VxSyWx08JctC1F0EznS1u6P3j5omXuvVfewE,69728 +cv2/.dylibs/libImath-3_1.29.11.0.dylib,sha256=HyDZBQUaEl2KpOSBufDBxC4swNSgiund7fiORRoJ7wE,357696 +cv2/.dylibs/libOpenEXR-3_3.32.3.3.4.dylib,sha256=8d9dLbrz44wLomSeNqWvH-GnZiE0b454SEu9rIKLkGo,1054288 +cv2/.dylibs/libOpenEXRCore-3_3.32.3.3.4.dylib,sha256=Ts5UxlIGp3-JEBlEbZOorD677qivTBwFYcKwxhPilxg,834224 +cv2/.dylibs/libSDL2-2.0.0.dylib,sha256=LpM_EKpDKCubZKH2A4sq-yhMvsZQr2Nx2i867d271VA,1529968 +cv2/.dylibs/libSvtAv1Enc.3.0.2.dylib,sha256=J11Pmx7aikS9mObawGF9nxLNaQLmcYHIRekbxN_gL3g,3168384 +cv2/.dylibs/libX11.6.dylib,sha256=GJuYzBG2bG2A5fBhebXaOg-mz4erVn_PBxcu4XYEr2s,1025536 +cv2/.dylibs/libXau.6.dylib,sha256=9s2d7gK38A1kv0R2vXGfnhscFrl3h8os_17LIPfc_TM,53312 +cv2/.dylibs/libXdmcp.6.dylib,sha256=RcLjJY2iKzyk0nKyhGggiJOkDd7Vv8w9Rysk6a6aGvQ,54224 +cv2/.dylibs/libaom.3.12.1.dylib,sha256=lT3as_oWg0hTdGWO3ZUSzHJ482XA6Vq_mipXI5Jh5zE,3938096 +cv2/.dylibs/libarchive.13.dylib,sha256=_R0dCa05JQMJeBzCtKZxLDTjX7mBSMUgqXUHRPJlARA,706752 +cv2/.dylibs/libaribb24.0.dylib,sha256=DN4igq9oNO6NULh8KVyJrPsf0Ae20b3X8KmdIi37-ZY,122752 +cv2/.dylibs/libass.9.dylib,sha256=UNj2UJT7_CbJVMgT2T9Ss6fd5Gr2S0_QS4gymiADLbg,247008 +cv2/.dylibs/libavcodec.61.19.101.dylib,sha256=IIJa1_p1UIe1M1ldJOu31UJBFL70KAMrIzU3xOfx4J8,10298272 +cv2/.dylibs/libavdevice.61.3.100.dylib,sha256=PXefPaoItRRPVFuq3l8HjFX7kYypg0F1F2CrOQQ94uU,206464 +cv2/.dylibs/libavfilter.10.4.100.dylib,sha256=xshPHTvZDy0nC0kI1KRGnspigo89_UUXyIN8NlhAbYA,3161360 +cv2/.dylibs/libavformat.61.7.100.dylib,sha256=1nBVJlS-EVefL8MdZrmU1626rqnvK7W1WZtSyJLJIDM,2364224 +cv2/.dylibs/libavif.16.3.0.dylib,sha256=1999n8wRMLygtfResHfqyWf-7b9LX5FdMRGkbi0QAOQ,222064 +cv2/.dylibs/libavutil.59.39.100.dylib,sha256=847URfi0epHyb2uwPBZ5TTV8b_cjgtn3_8oUXEmuSlg,643248 +cv2/.dylibs/libb2.1.dylib,sha256=Ib7qYxL9Qggh_CVzQQBopJ9RGIIuJi9laeQ4rO8Zt6M,86288 +cv2/.dylibs/libbluray.2.dylib,sha256=a0RAKzhE8_CuhKrGO_1aT6Rz7Sox6R9YkLVE2msQB8A,346240 +cv2/.dylibs/libbrotlicommon.1.1.0.dylib,sha256=FQxwHBEPzvrodGDDfBeL84odHcT8uA4PQhqBOCD6hto,184688 +cv2/.dylibs/libbrotlidec.1.1.0.dylib,sha256=7X9F-MUnO8W940xU6XWrRO1o06-dIcc16zdg-vf-XWM,104544 +cv2/.dylibs/libbrotlienc.1.1.0.dylib,sha256=XTqhtSqqhFls7-4T8RKSra-TO-QcoBtmOJp9H5HDGA4,655808 +cv2/.dylibs/libcjson.1.7.18.dylib,sha256=XDmaISOhOlOimc9v-qptTyZu34Ug0IZgZkbKqL8jsH0,73792 +cv2/.dylibs/libcrypto.3.dylib,sha256=hzji5AFiDPBjNF1A0_u3yaeDY-Je39248WgAoJMFOI8,4852320 +cv2/.dylibs/libdav1d.7.dylib,sha256=vz_ZEYsomcUXkGh2bIK9-rZXj8jp1sWH_jbdZdCT2q0,788592 +cv2/.dylibs/libdeflate.0.dylib,sha256=MkJLMsuWQDIEwfzrzybtQIyN4IpAyaJ2uVQvIqSRZnw,105104 +cv2/.dylibs/libfontconfig.1.dylib,sha256=5S0FPlAc6D81MEBImGcrMFfCjF827cxo_uV1BNG_kvA,306048 +cv2/.dylibs/libfreetype.6.dylib,sha256=m1WANaa-nx5D-n4Ly1UpKebEbG-XrOv35RS22ijT6FY,641712 +cv2/.dylibs/libfribidi.0.dylib,sha256=7C5G-47SbnACXs0QvgKP_qIeIDPPiS3NommpwVXX97I,171920 +cv2/.dylibs/libgif.7.2.0.dylib,sha256=yycjxSwZLBChxIzoTDO-RXYwFyx3tUm6zyRwmUCsT4s,72816 +cv2/.dylibs/libglib-2.0.0.dylib,sha256=Eax81Q5JvG-BXnnYLs6ErHA6PQX6NRkal81fd3xUm4g,1235008 +cv2/.dylibs/libgmp.10.dylib,sha256=rJ4akY0MtNqY0tJe9tfWN87XExzIhhmrS1SCkMdxOkY,452912 +cv2/.dylibs/libgnutls.30.dylib,sha256=K49gLl1XaodGDW5kg4pY_gXMvPKFrEi4U3hrtni1KuY,1906912 +cv2/.dylibs/libgraphite2.3.2.1.dylib,sha256=8dyEdW2B6TqjMx3Rx7x62VxIQxakDb16dht3OkP2mdk,155200 +cv2/.dylibs/libharfbuzz.0.dylib,sha256=eFjzw5uphtlfwBB6jhvLckaoRcTcAdzRivZFmu5XplY,1279248 +cv2/.dylibs/libhogweed.6.10.dylib,sha256=3oTi9SUstc2TpaQDfJlOmrGwbMLhCQvwXrEpVwT_g_8,316512 +cv2/.dylibs/libhwy.1.2.0.dylib,sha256=1LCbL1Eza6fs8eMNV8DD8yp6TaZWLlGhYIxm81d5yyg,96336 +cv2/.dylibs/libidn2.0.dylib,sha256=K7TJCJ-oGkyHiycBtCwQId51qe-6USMBsaLGm1nUFi8,259392 +cv2/.dylibs/libintl.8.dylib,sha256=kkBFkE9DMBZRch8Rz8VN_LjE6v7-o3bQ0eVpIZnlais,230112 +cv2/.dylibs/libjpeg.8.3.2.dylib,sha256=4uBEBWOh9gmF_w55ZgQWYqbsYB6KdJfD56_FrzJBr3w,473216 +cv2/.dylibs/libjxl.0.11.1.dylib,sha256=3s_PT9dqjPw5hiRq7u_OQ378xuKYQHI3y8T3TIHJJEI,1714272 +cv2/.dylibs/libjxl_cms.0.11.1.dylib,sha256=TdUXXqhWck1-JTpaDnNYNfOjGfX0avZkdTlIJTnAGZk,111328 +cv2/.dylibs/libjxl_threads.0.11.1.dylib,sha256=P3IBhWoZhl09TnsEPchr5aDk51pgFpnGBA302peN9KY,56752 +cv2/.dylibs/liblcms2.2.dylib,sha256=MthGtnzRjav1wsN2FJaJ0TsQNHVnamjoxyyJJvmH_l0,374176 +cv2/.dylibs/libleptonica.6.dylib,sha256=doBUXwHlTMw_hJnYePQTP-JUnYI1qIwjlHyXgb_gaZs,2237808 +cv2/.dylibs/liblz4.1.10.0.dylib,sha256=V7G4p5IZ25U5M2qlKR3ickjpuYPuegLG4VRsUjx5VT0,176960 +cv2/.dylibs/liblzma.5.dylib,sha256=PqiYr8Xl7Xh7-TXnBGFtE1qQvZp3C00hIZVPy31gsEk,185520 +cv2/.dylibs/libmbedcrypto.3.6.3.dylib,sha256=RufapBqxDkARVmFeWtVNTYikxmcOIwzKqa588w94yf0,531536 +cv2/.dylibs/libmp3lame.0.dylib,sha256=9KDB_v1WOZ50ERYCKJne9x0YjqJGcDwtGkYlfBgsYgA,322672 +cv2/.dylibs/libnettle.8.10.dylib,sha256=yM19NldLEduz-p5C1prg_wB0G0DWYJEewAEkJm4dRe0,334064 +cv2/.dylibs/libogg.0.dylib,sha256=6wyvgiYQN8-jw8WT5SQf21Oc0--0xYhgXmU9R6dT3m0,88656 +cv2/.dylibs/libopencore-amrnb.0.dylib,sha256=jUSCgsBQTZuT5Bnt3SZFOkJHoXaXryit8dDTOHVJdkU,194992 +cv2/.dylibs/libopencore-amrwb.0.dylib,sha256=5Zcnf90HK3TH-wLfDPu6qJH_ekeGuOBL2sMM9st7wv8,122624 +cv2/.dylibs/libopenjp2.2.5.3.dylib,sha256=IoJ-7wvJEf4oUbQmOzoc_9HnEmfYZ11jnHAiEB0-buE,325968 +cv2/.dylibs/libopus.0.dylib,sha256=uXv9WRq91q5tRODHW32XJtsOx7o_wK1er73FMwRX7LQ,357584 +cv2/.dylibs/libp11-kit.0.dylib,sha256=Nwzt7VqVpGx0Dv7e--qhgJMGoTCU2rdf3-beHJYAYSE,1469168 +cv2/.dylibs/libpcre2-8.0.dylib,sha256=Dgwyq4Y6W_XaPfVtzzcOAVldVTK576YLDtu4ISBAGMY,580272 +cv2/.dylibs/libpng16.16.dylib,sha256=9dxGJUv4fcCpQGl_ORIOWMBHgwcgW9MZJENPApQjb7s,209392 +cv2/.dylibs/libpostproc.58.3.100.dylib,sha256=CeCf8JzyUJM_3bGKsOHLNnddHcJeD4GxM031jXhkLNU,85872 +cv2/.dylibs/librav1e.0.8.0.dylib,sha256=0-yMp0ZFfykC_XRxHTT5SpYkAGmy3cuW5eeYgKQp7Pk,2986176 +cv2/.dylibs/librist.4.dylib,sha256=AD4END8upnXFDaotKAGDJ2HLYvDOwHRjhkKSZ2SYzc4,188224 +cv2/.dylibs/librubberband.3.dylib,sha256=rIrrSejGp3wpCG-waW7oj6qj9qIDY0d6AVwij61dVoY,403760 +cv2/.dylibs/libsamplerate.0.2.2.dylib,sha256=XboNoFzqqoeIIusvj3sjaJPGU6Js0mHEjXW1TKUD-Wk,1556784 +cv2/.dylibs/libsharpyuv.0.1.1.dylib,sha256=bVtHhNgE1FPbZ6ki4X1ko5MK7B7i7tNq4Ohjk0Fp5nQ,69872 +cv2/.dylibs/libsnappy.1.2.2.dylib,sha256=2GabCtqTQ7pulahYL1CYfqdwMzI1xhAP3m48wmajFSg,79392 +cv2/.dylibs/libsodium.26.dylib,sha256=ZDEZS0qlhXbeHqJpXu6O-ntlAsqWsTaWIdI5KF8Re9A,290544 +cv2/.dylibs/libsoxr.0.1.2.dylib,sha256=07e0XV4kj7YPN72sRUxXNvw9txe-YkP2x1AGqDBQLM4,193904 +cv2/.dylibs/libspeex.1.dylib,sha256=oRFd8bwlmgoKQr2IUcNoPcg1FcCiSmOQ0plDK-G5XKw,142864 +cv2/.dylibs/libsrt.1.5.4.dylib,sha256=ncHYkK9If2zR_JjpN65OCfwo7qYMiCmPSIJ26vyLrbI,730032 +cv2/.dylibs/libssh.4.10.1.dylib,sha256=iVnTt1g2bb-NIoIoD4J_yWzQm-VU_vucRj9QEN5kPfg,480368 +cv2/.dylibs/libssl.3.dylib,sha256=4XEz2AyyAZGgPIaRZDzlBtKpFUQSofn78bOH-CNVZSc,900176 +cv2/.dylibs/libswresample.5.3.100.dylib,sha256=tHuGbVo5Tq3LCw7bfpSB-adueJIskXCzbt1RRut76hw,122096 +cv2/.dylibs/libswscale.8.3.100.dylib,sha256=2FoliXd1-pAYBhSLJfAKi8DYs4Bj_WjxAsnEsG5-X-M,453520 +cv2/.dylibs/libtasn1.6.dylib,sha256=35rDwe60-m0JLDTGNacVwZGE-CsyhVBRw-irJ7iJGXM,108240 +cv2/.dylibs/libtesseract.5.dylib,sha256=0vvB3jSF2wN7srRJgJ-OKK6rF1_0eBmWIm7jYLWkXSU,2898656 +cv2/.dylibs/libtheoradec.1.dylib,sha256=7eD_dG0WlAOes_dDW0XXV7gvk3TZhYe8xFJbZu2lxG0,108688 +cv2/.dylibs/libtheoraenc.1.dylib,sha256=scDGmVTO4YBjQczfSY5A0E8rCKXoqmI4DE6QFo8B0Xo,211104 +cv2/.dylibs/libtiff.6.dylib,sha256=2rwUFThLoWzVSrGk7y58p92bisZSTr3c1IEOLjlVccM,543120 +cv2/.dylibs/libunibreak.6.dylib,sha256=mUDwIL3oeVhKzHa85gMq0JY-nJiEkCRzq8emVtU71ww,170544 +cv2/.dylibs/libunistring.5.dylib,sha256=6u8wR3y8LP-iFiwiM7WX73v87EcLyjjfo2JyXmo032E,2018384 +cv2/.dylibs/libvidstab.1.2.dylib,sha256=FRzh8hXbrNUndzWeT09i0Xm7xFN-kzrbD0-iVCoT3oI,112416 +cv2/.dylibs/libvmaf.3.dylib,sha256=xUXJp7snHG_5fHwiqvRmnsverQwAdPsUksh1cXrFeyo,747328 +cv2/.dylibs/libvorbis.0.dylib,sha256=GzXSBV48u-AAA_JjYoFC03PsHWUhxTGDc7SJ2HovIZ4,210576 +cv2/.dylibs/libvorbisenc.2.dylib,sha256=0Gqk3uKsVyVdKxa_vHxEHVwZCmZJlZqSHhw39cGIEck,731584 +cv2/.dylibs/libvpx.11.dylib,sha256=pQ-8XbX1Pv1c0kYkSlEP_9L5rBobIPfAa0KfSsZV8ac,1758128 +cv2/.dylibs/libwebp.7.1.10.dylib,sha256=4fxANxmHvukLaXyuHT1pZa67s1Y5atQZv7dfe3dWQQc,353904 +cv2/.dylibs/libwebpmux.3.1.1.dylib,sha256=La2k0j21PuIJQsa06W8JslbiZHSX-jKD9Fmxe15BTG4,73552 +cv2/.dylibs/libx264.164.dylib,sha256=fwLq2LWYfXRBxWUhqAo-iUtE-l9JpX-N1RWsOz9zNWQ,1312400 +cv2/.dylibs/libx265.215.dylib,sha256=hgo8dwECZMSY54G1mZgPa7jNqrmJsF6hUihEurlhu6w,4954576 +cv2/.dylibs/libxcb-shape.0.0.0.dylib,sha256=6QfWhZwPQ94H2oi-Uon2JLx_LR1a5C2Esrdy33_d7fQ,71328 +cv2/.dylibs/libxcb-shm.0.0.0.dylib,sha256=8vYeE4dOj50yeepwIBSowSQxeCwiCBSUZA6-mwosnQ4,70432 +cv2/.dylibs/libxcb-xfixes.0.0.0.dylib,sha256=AEm5CmOyPl4a0IiCYmjoPp1rj86uHgjijUVurPoIYx8,81024 +cv2/.dylibs/libxcb.1.1.0.dylib,sha256=KAv4muUZsDV3aYuwROzkX7F54T4F2sdiYdLXfqXRybM,194768 +cv2/.dylibs/libzimg.2.dylib,sha256=YlN2--cVuAaIHLYwLqtd83bLrz14kVhTeBYQeLkz_oA,399008 +cv2/.dylibs/libzmq.5.dylib,sha256=_tIFz6_ZA8qagz9ykUVJAbpi5asnTxReBSsip1sIfSw,650928 +cv2/.dylibs/libzstd.1.5.7.dylib,sha256=BKboq6WTZ8v5sYjnY4MNVKKOdNssNO1sYwNBHXjTOvo,670096 +cv2/Error/__init__.pyi,sha256=A6NKtoMeZAvZWHC6DrJiwMVChY7LLxFfvuZ2dW4KSm8,4076 +cv2/LICENSE-3RD-PARTY.txt,sha256=JTf1ZTNF23Ix_xLzB7z6TImAfUXtHEu46_tvJvYbFgo,179634 +cv2/LICENSE.txt,sha256=CdcZBY54Kse8cbohyUThE2zeK7lXwOiIEh8CGNa18Cw,1070 +cv2/__init__.py,sha256=aRkpBN6tOto3yLMF7oUKx0dJJHMIganV4LI5CUTrWq4,6630 +cv2/__init__.pyi,sha256=9ahY3aelxBuQ6Ww7zRIe4lnVKsFdOWT7y2vri7EQr9A,336920 +cv2/aruco/__init__.pyi,sha256=imRb8yJiOxITW7GhsiatOyZGcJPj8yEIK3Q0n4CbWV8,16281 +cv2/barcode/__init__.pyi,sha256=i_hQbZ1zQz6iiloYEhvjtNhcYwaNMEAHjTRnSpSCjjw,1467 +cv2/ccm/__init__.pyi,sha256=9U8q8Od4b7MTkLbuuXyGuh5RYBrya1mVz87tnrZYCEo,6146 +cv2/config-3.py,sha256=mnqt9yS4IgAfXpY7Af1ON11F4su-Mo0sp7QqRAwIOhw,724 +cv2/config.py,sha256=HbNGOpb1ioISOoR9FQcijZ6KgABt5nQBroNZF2WOvoc,109 +cv2/cuda/__init__.pyi,sha256=_-_JAT6uq75LxhpVVnCARGHmjzkUYyfaj68UNU0Ue9A,16205 +cv2/cv2.abi3.so,sha256=WHl1jxpPDYOta-FoSEeNOxCaqwmkAhOfl2jftjD98HI,42303696 +cv2/data/__init__.py,sha256=125Pcte_OtB55ZxjWg5ko8ugpnogZ1sRMyP48dtBCMw,70 +cv2/detail/__init__.pyi,sha256=so6i649OjMIyfeB8V6inNVcRn1GsPdvmj0oHrgYnDak,20093 +cv2/dnn/__init__.pyi,sha256=JNKQYBiW0JVR2n_nGrdZwuxSlnSwIj123M04QGjovco,29244 +cv2/fisheye/__init__.pyi,sha256=-9SNasmsOC0j49VcDdgDjpcYmoSGlFjx1Dwgu7VCbzU,9641 +cv2/flann/__init__.pyi,sha256=omYA7HGlyepKxUPSCJQMn30KQkhCPZQWrE0a06EogL0,2744 +cv2/instr/__init__.pyi,sha256=FbUVh-3xxNRPFfx8wojJXyJO0OMsxWk9owlC9AkcXRw,437 +cv2/ipp/__init__.pyi,sha256=WSHVIqIT97vmudtuJjhOJYiZ0iBdYx4AtB0iJqtdD0o,223 +cv2/load_config_py2.py,sha256=xP_h2pObzfbN8tONV7CAQmGh94fQ-0t0HysrXDDlt_Q,151 +cv2/load_config_py3.py,sha256=nCJ2ZFSprC9oadN4MLLvxpnnuMMSVITpl2xuZ3wGYCk,244 +cv2/mat_wrapper/__init__.py,sha256=i2JwY6kmDL_s7YXzIl-JZuWCMVYkRi4F6j60W3j4P9A,1124 +cv2/mcc/__init__.pyi,sha256=bUhBTaZyRhDnwMkoRZBXMCXXXmnq9EZ1hixKm1N5k5k,3393 +cv2/misc/__init__.py,sha256=yr9PkxKslxRc87hhtIJRn5RommP9jaqksYr-ZDuj7cU,37 +cv2/misc/version.py,sha256=iTExq1jwGgAv3jtYQHRI8pSpmfzPsjkG9brsH0bdYhk,90 +cv2/ocl/__init__.pyi,sha256=qv_ilpHZosfPEMHEEqqQLe6cJpsb9PiiwIZMbd---ho,5527 +cv2/ogl/__init__.pyi,sha256=KxTX9DHYyXg2ipvOJiFeAsRivAjmvBkqeiLZV-0snII,1472 +cv2/parallel/__init__.pyi,sha256=tc5nNoWrTkD7VAfhbajumKF79LBolpqlKjYX-lY2__8,129 +cv2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cv2/samples/__init__.pyi,sha256=cjSW5vo2oMpIWHwP-3IY4hWjlKUTz8gd1MX7pLOCWKo,324 +cv2/segmentation/__init__.pyi,sha256=jwKBUCRaXhHAM3FdzpLuGucGfNLWxWu5CDfLOpkcan4,1739 +cv2/typing/__init__.py,sha256=sewYfCGEsdmFrp5_un5enibZNBPOzqC-CduLM9UexSw,4306 +cv2/utils/__init__.py,sha256=fuw4GHHOXsxxKc-AadAEOKQq_I1Gr4G3yMlRvAbTP30,330 +cv2/utils/__init__.pyi,sha256=RBhJPc5ckuH0jllH2yIFc3imFH2fKqzXh3zwIKxuQGg,3633 +cv2/utils/fs/__init__.pyi,sha256=lu2cK1Dbd7wRTOTju_kVVCvU4mNB5v5hSVpBxSXXvJg,87 +cv2/utils/logging/__init__.pyi,sha256=D2Ka8xmbpOKNeF6dku99KE5UM9OKgDnYeJU8gR1snTs,505 +cv2/utils/nested/__init__.pyi,sha256=n2J3aSxC2MrPKaKb4igY_d49luuuQqW7A_YTx6eZz9Q,573 +cv2/version.py,sha256=5xhYchJQis3LKWg5wofH-jRu22IQBf-xmcJjl1klRO0,91 +cv2/videoio_registry/__init__.pyi,sha256=ef4uptXuTkAZVXwsyNSc-Xhs-hJlg6O2AcmoYkzEx80,962 +opencv_python_headless-5.0.0.93.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opencv_python_headless-5.0.0.93.dist-info/LICENSE-3RD-PARTY.txt,sha256=JTf1ZTNF23Ix_xLzB7z6TImAfUXtHEu46_tvJvYbFgo,179634 +opencv_python_headless-5.0.0.93.dist-info/LICENSE.txt,sha256=CdcZBY54Kse8cbohyUThE2zeK7lXwOiIEh8CGNa18Cw,1070 +opencv_python_headless-5.0.0.93.dist-info/METADATA,sha256=-OkQS03OWPcI1Mx6-V69il1lo-NiCdQjhuthBlrlDho,19758 +opencv_python_headless-5.0.0.93.dist-info/RECORD,, +opencv_python_headless-5.0.0.93.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opencv_python_headless-5.0.0.93.dist-info/WHEEL,sha256=B6uMVHIuPPIs_i9BSlEilXuWBK8ACnx5NsPY4j3OVBU,102 +opencv_python_headless-5.0.0.93.dist-info/top_level.txt,sha256=SY8vrf_sYOg99OP9euhz7q36pPy_2VK5vbeEWXwwSoc,4 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/REQUESTED b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/WHEEL b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..16df7824c75d3f55be7d44b3cac0d841798f14a7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: skbuild 0.18.1 +Root-Is-Purelib: false +Tag: cp37-abi3-macosx_13_0_arm64 + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/top_level.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..6f372d8a15f09406879e280cf72b22712505404d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/opencv_python_headless-5.0.0.93.dist-info/top_level.txt @@ -0,0 +1 @@ +cv2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7d6c3c27febc0449e463afbb5aa10e5d3581c37e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/__init__.py @@ -0,0 +1,111 @@ +""" +============================================== +Discrete Fourier transforms (:mod:`scipy.fft`) +============================================== + +.. currentmodule:: scipy.fft + +Fast Fourier Transforms (FFTs) +============================== + +.. autosummary:: + :toctree: generated/ + + fft - Fast (discrete) Fourier Transform (FFT) + ifft - Inverse FFT + fft2 - 2-D FFT + ifft2 - 2-D inverse FFT + fftn - N-D FFT + ifftn - N-D inverse FFT + rfft - FFT of strictly real-valued sequence + irfft - Inverse of rfft + rfft2 - 2-D FFT of real sequence + irfft2 - Inverse of rfft2 + rfftn - N-D FFT of real sequence + irfftn - Inverse of rfftn + hfft - FFT of a Hermitian sequence (real spectrum) + ihfft - Inverse of hfft + hfft2 - 2-D FFT of a Hermitian sequence + ihfft2 - Inverse of hfft2 + hfftn - N-D FFT of a Hermitian sequence + ihfftn - Inverse of hfftn + +Discrete Sin and Cosine Transforms (DST and DCT) +================================================ + +.. autosummary:: + :toctree: generated/ + + dct - Discrete cosine transform + idct - Inverse discrete cosine transform + dctn - N-D Discrete cosine transform + idctn - N-D Inverse discrete cosine transform + dst - Discrete sine transform + idst - Inverse discrete sine transform + dstn - N-D Discrete sine transform + idstn - N-D Inverse discrete sine transform + +Fast Hankel Transforms +====================== + +.. autosummary:: + :toctree: generated/ + + fht - Fast Hankel transform + ifht - Inverse of fht + +Helper functions +================ + +.. autosummary:: + :toctree: generated/ + + fftshift - Shift the zero-frequency component to the center of the spectrum + ifftshift - The inverse of `fftshift` + fftfreq - Return the Discrete Fourier Transform sample frequencies + rfftfreq - DFT sample frequencies (for usage with rfft, irfft) + fhtoffset - Compute an optimal offset for the Fast Hankel Transform + next_fast_len - Find the optimal length to zero-pad an FFT for speed + set_workers - Context manager to set default number of workers + get_workers - Get the current default number of workers + +Backend control +=============== + +.. autosummary:: + :toctree: generated/ + + set_backend - Context manager to set the backend within a fixed scope + skip_backend - Context manager to skip a backend within a fixed scope + set_global_backend - Sets the global fft backend + register_backend - Register a backend for permanent use + +""" + +from ._basic import ( + fft, ifft, fft2, ifft2, fftn, ifftn, + rfft, irfft, rfft2, irfft2, rfftn, irfftn, + hfft, ihfft, hfft2, ihfft2, hfftn, ihfftn) +from ._realtransforms import dct, idct, dst, idst, dctn, idctn, dstn, idstn +from ._fftlog import fht, ifht, fhtoffset +from ._helper import next_fast_len, fftfreq, rfftfreq, fftshift, ifftshift +from ._backend import (set_backend, skip_backend, set_global_backend, + register_backend) +from ._pocketfft.helper import set_workers, get_workers + +__all__ = [ + 'fft', 'ifft', 'fft2', 'ifft2', 'fftn', 'ifftn', + 'rfft', 'irfft', 'rfft2', 'irfft2', 'rfftn', 'irfftn', + 'hfft', 'ihfft', 'hfft2', 'ihfft2', 'hfftn', 'ihfftn', + 'fftfreq', 'rfftfreq', 'fftshift', 'ifftshift', + 'next_fast_len', + 'dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn', + 'fht', 'ifht', + 'fhtoffset', + 'set_backend', 'skip_backend', 'set_global_backend', 'register_backend', + 'get_workers', 'set_workers'] + + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..c1e5cfcad5c4cbc43276e151d2da33039368630d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_backend.py @@ -0,0 +1,196 @@ +import scipy._lib.uarray as ua +from . import _basic_backend +from . import _realtransforms_backend +from . import _fftlog_backend + + +class _ScipyBackend: + """The default backend for fft calculations + + Notes + ----- + We use the domain ``numpy.scipy`` rather than ``scipy`` because ``uarray`` + treats the domain as a hierarchy. This means the user can install a single + backend for ``numpy`` and have it implement ``numpy.scipy.fft`` as well. + """ + __ua_domain__ = "numpy.scipy.fft" + + @staticmethod + def __ua_function__(method, args, kwargs): + + fn = getattr(_basic_backend, method.__name__, None) + if fn is None: + fn = getattr(_realtransforms_backend, method.__name__, None) + if fn is None: + fn = getattr(_fftlog_backend, method.__name__, None) + if fn is None: + return NotImplemented + return fn(*args, **kwargs) + + +_named_backends = { + 'scipy': _ScipyBackend, +} + + +def _backend_from_arg(backend): + """Maps strings to known backends and validates the backend""" + + if isinstance(backend, str): + try: + backend = _named_backends[backend] + except KeyError as e: + raise ValueError(f'Unknown backend {backend}') from e + + if backend.__ua_domain__ != 'numpy.scipy.fft': + raise ValueError('Backend does not implement "numpy.scipy.fft"') + + return backend + + +def set_global_backend(backend, coerce=False, only=False, try_last=False): + """Sets the global fft backend + + This utility method replaces the default backend for permanent use. It + will be tried in the list of backends automatically, unless the + ``only`` flag is set on a backend. This will be the first tried + backend outside the :obj:`set_backend` context manager. + + Parameters + ---------- + backend : {object, 'scipy'} + The backend to use. + Can either be a ``str`` containing the name of a known backend + {'scipy'} or an object that implements the uarray protocol. + coerce : bool + Whether to coerce input types when trying this backend. + only : bool + If ``True``, no more backends will be tried if this fails. + Implied by ``coerce=True``. + try_last : bool + If ``True``, the global backend is tried after registered backends. + + Raises + ------ + ValueError: If the backend does not implement ``numpy.scipy.fft``. + + Notes + ----- + This will overwrite the previously set global backend, which, by default, is + the SciPy implementation. + + Examples + -------- + We can set the global fft backend: + + >>> from scipy.fft import fft, set_global_backend + >>> set_global_backend("scipy") # Sets global backend (default is "scipy"). + >>> fft([1]) # Calls the global backend + array([1.+0.j]) + """ + backend = _backend_from_arg(backend) + ua.set_global_backend(backend, coerce=coerce, only=only, try_last=try_last) + + +def register_backend(backend): + """ + Register a backend for permanent use. + + Registered backends have the lowest priority and will be tried after the + global backend. + + Parameters + ---------- + backend : {object, 'scipy'} + The backend to use. + Can either be a ``str`` containing the name of a known backend + {'scipy'} or an object that implements the uarray protocol. + + Raises + ------ + ValueError: If the backend does not implement ``numpy.scipy.fft``. + + Examples + -------- + We can register a new fft backend: + + >>> from scipy.fft import fft, register_backend, set_global_backend + >>> class NoopBackend: # Define an invalid Backend + ... __ua_domain__ = "numpy.scipy.fft" + ... def __ua_function__(self, func, args, kwargs): + ... return NotImplemented + >>> set_global_backend(NoopBackend()) # Set the invalid backend as global + >>> register_backend("scipy") # Register a new backend + # The registered backend is called because + # the global backend returns `NotImplemented` + >>> fft([1]) + array([1.+0.j]) + >>> set_global_backend("scipy") # Restore global backend to default + + """ + backend = _backend_from_arg(backend) + ua.register_backend(backend) + + +def set_backend(backend, coerce=False, only=False): + """Context manager to set the backend within a fixed scope. + + Upon entering the ``with`` statement, the given backend will be added to + the list of available backends with the highest priority. Upon exit, the + backend is reset to the state before entering the scope. + + Parameters + ---------- + backend : {object, 'scipy'} + The backend to use. + Can either be a ``str`` containing the name of a known backend + {'scipy'} or an object that implements the uarray protocol. + coerce : bool, optional + Whether to allow expensive conversions for the ``x`` parameter. e.g., + copying a NumPy array to the GPU for a CuPy backend. Implies ``only``. + only : bool, optional + If only is ``True`` and this backend returns ``NotImplemented``, then a + BackendNotImplemented error will be raised immediately. Ignoring any + lower priority backends. + + Examples + -------- + >>> import scipy.fft as fft + >>> with fft.set_backend('scipy', only=True): + ... fft.fft([1]) # Always calls the scipy implementation + array([1.+0.j]) + """ + backend = _backend_from_arg(backend) + return ua.set_backend(backend, coerce=coerce, only=only) + + +def skip_backend(backend): + """Context manager to skip a backend within a fixed scope. + + Within the context of a ``with`` statement, the given backend will not be + called. This covers backends registered both locally and globally. Upon + exit, the backend will again be considered. + + Parameters + ---------- + backend : {object, 'scipy'} + The backend to skip. + Can either be a ``str`` containing the name of a known backend + {'scipy'} or an object that implements the uarray protocol. + + Examples + -------- + >>> import scipy.fft as fft + >>> fft.fft([1]) # Calls default SciPy backend + array([1.+0.j]) + >>> with fft.skip_backend('scipy'): # We explicitly skip the SciPy backend + ... fft.fft([1]) # leaving no implementation available + Traceback (most recent call last): + ... + BackendNotImplementedError: No selected backends had an implementation ... + """ + backend = _backend_from_arg(backend) + return ua.skip_backend(backend) + + +set_global_backend('scipy', try_last=True) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..a3fc021c9ef9b7c2a40bf7b5138158df8e276ae6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic.py @@ -0,0 +1,1630 @@ +from scipy._lib.uarray import generate_multimethod, Dispatchable +import numpy as np + + +def _x_replacer(args, kwargs, dispatchables): + """ + uarray argument replacer to replace the transform input array (``x``) + """ + if len(args) > 0: + return (dispatchables[0],) + args[1:], kwargs + kw = kwargs.copy() + kw['x'] = dispatchables[0] + return args, kw + + +def _dispatch(func): + """ + Function annotation that creates a uarray multimethod from the function + """ + return generate_multimethod(func, _x_replacer, domain="numpy.scipy.fft") + + +@_dispatch +def fft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 1-D discrete Fourier Transform. + + This function computes the 1-D *n*-point discrete Fourier + Transform (DFT) with the efficient Fast Fourier Transform (FFT) + algorithm [1]_. + + Parameters + ---------- + x : array_like + Input array, can be complex. + n : int, optional + Length of the transformed axis of the output. + If `n` is smaller than the length of the input, the input is cropped. + If it is larger, the input is padded with zeros. If `n` is not given, + the length of the input along the axis specified by `axis` is used. + axis : int, optional + Axis over which to compute the FFT. If not given, the last axis is + used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode. Default is "backward", meaning no normalization on + the forward transforms and scaling by ``1/n`` on the `ifft`. + "forward" instead applies the ``1/n`` factor on the forward transform. + For ``norm="ortho"``, both directions are scaled by ``1/sqrt(n)``. + + .. versionadded:: 1.6.0 + ``norm={"forward", "backward"}`` options were added + + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See the notes below for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. See below for more + details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + + Raises + ------ + IndexError + if `axes` is larger than the last axis of `x`. + + See Also + -------- + ifft : The inverse of `fft`. + fft2 : The 2-D FFT. + fftn : The N-D FFT. + rfftn : The N-D FFT of real input. + fftfreq : Frequency bins for given FFT parameters. + next_fast_len : Size to pad input to for most efficient transforms + + Notes + ----- + FFT (Fast Fourier Transform) refers to a way the discrete Fourier Transform + (DFT) can be calculated efficiently, by using symmetries in the calculated + terms. The symmetry is highest when `n` is a power of 2, and the transform + is therefore most efficient for these sizes. For poorly factorizable sizes, + `scipy.fft` uses Bluestein's algorithm [2]_ and so is never worse than + O(`n` log `n`). Further performance improvements may be seen by zero-padding + the input using `next_fast_len`. + + If ``x`` is a 1d array, then the `fft` is equivalent to :: + + y[k] = np.sum(x * np.exp(-2j * np.pi * k * np.arange(n)/n)) + + The frequency term ``f=k/n`` is found at ``y[k]``. At ``y[n/2]`` we reach + the Nyquist frequency and wrap around to the negative-frequency terms. So, + for an 8-point transform, the frequencies of the result are + [0, 1, 2, 3, -4, -3, -2, -1]. To rearrange the fft output so that the + zero-frequency component is centered, like [-4, -3, -2, -1, 0, 1, 2, 3], + use `fftshift`. + + Transforms can be done in single, double, or extended precision (long + double) floating point. Half precision inputs will be converted to single + precision and non-floating-point inputs will be converted to double + precision. + + If the data type of ``x`` is real, a "real FFT" algorithm is automatically + used, which roughly halves the computation time. To increase efficiency + a little further, use `rfft`, which does the same calculation, but only + outputs half of the symmetrical spectrum. If the data are both real and + symmetrical, the `dct` can again double the efficiency, by generating + half of the spectrum from half of the signal. + + When ``overwrite_x=True`` is specified, the memory referenced by ``x`` may + be used by the implementation in any way. This may include reusing the + memory for the result, but this is in no way guaranteed. You should not + rely on the contents of ``x`` after the transform as this may change in + future without warning. + + The ``workers`` argument specifies the maximum number of parallel jobs to + split the FFT computation into. This will execute independent 1-D + FFTs within ``x``. So, ``x`` must be at least 2-D and the + non-transformed axes must be large enough to split into chunks. If ``x`` is + too small, fewer jobs may be used than requested. + + References + ---------- + .. [1] Cooley, James W., and John W. Tukey, 1965, "An algorithm for the + machine calculation of complex Fourier series," *Math. Comput.* + 19: 297-301. + .. [2] Bluestein, L., 1970, "A linear filtering approach to the + computation of discrete Fourier transform". *IEEE Transactions on + Audio and Electroacoustics.* 18 (4): 451-455. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> scipy.fft.fft(np.exp(2j * np.pi * np.arange(8) / 8)) + array([-2.33486982e-16+1.14423775e-17j, 8.00000000e+00-1.25557246e-15j, + 2.33486982e-16+2.33486982e-16j, 0.00000000e+00+1.22464680e-16j, + -1.14423775e-17+2.33486982e-16j, 0.00000000e+00+5.20784380e-16j, + 1.14423775e-17+1.14423775e-17j, 0.00000000e+00+1.22464680e-16j]) + + In this example, real input has an FFT which is Hermitian, i.e., symmetric + in the real part and anti-symmetric in the imaginary part: + + >>> from scipy.fft import fft, fftfreq, fftshift + >>> import matplotlib.pyplot as plt + >>> t = np.arange(256) + >>> sp = fftshift(fft(np.sin(t))) + >>> freq = fftshift(fftfreq(t.shape[-1])) + >>> plt.plot(freq, sp.real, freq, sp.imag) + [, + ] + >>> plt.show() + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ifft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 1-D inverse discrete Fourier Transform. + + This function computes the inverse of the 1-D *n*-point + discrete Fourier transform computed by `fft`. In other words, + ``ifft(fft(x)) == x`` to within numerical accuracy. + + The input should be ordered in the same way as is returned by `fft`, + i.e., + + * ``x[0]`` should contain the zero frequency term, + * ``x[1:n//2]`` should contain the positive-frequency terms, + * ``x[n//2 + 1:]`` should contain the negative-frequency terms, in + increasing order starting from the most negative frequency. + + For an even number of input points, ``x[n//2]`` represents the sum of + the values at the positive and negative Nyquist frequencies, as the two + are aliased together. See `fft` for details. + + Parameters + ---------- + x : array_like + Input array, can be complex. + n : int, optional + Length of the transformed axis of the output. + If `n` is smaller than the length of the input, the input is cropped. + If it is larger, the input is padded with zeros. If `n` is not given, + the length of the input along the axis specified by `axis` is used. + See notes about padding issues. + axis : int, optional + Axis over which to compute the inverse DFT. If not given, the last + axis is used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + + Raises + ------ + IndexError + If `axes` is larger than the last axis of `x`. + + See Also + -------- + fft : The 1-D (forward) FFT, of which `ifft` is the inverse. + ifft2 : The 2-D inverse FFT. + ifftn : The N-D inverse FFT. + + Notes + ----- + If the input parameter `n` is larger than the size of the input, the input + is padded by appending zeros at the end. Even though this is the common + approach, it might lead to surprising results. If a different padding is + desired, it must be performed before calling `ifft`. + + If ``x`` is a 1-D array, then the `ifft` is equivalent to :: + + y[k] = np.sum(x * np.exp(2j * np.pi * k * np.arange(n)/n)) / len(x) + + As with `fft`, `ifft` has support for all floating point types and is + optimized for real input. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> scipy.fft.ifft([0, 4, 0, 0]) + array([ 1.+0.j, 0.+1.j, -1.+0.j, 0.-1.j]) # may vary + + Create and plot a band-limited signal with random phases: + + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> t = np.arange(400) + >>> n = np.zeros((400,), dtype=complex) + >>> n[40:60] = np.exp(1j*rng.uniform(0, 2*np.pi, (20,))) + >>> s = scipy.fft.ifft(n) + >>> plt.plot(t, s.real, 'b-', t, s.imag, 'r--') + [, ] + >>> plt.legend(('real', 'imaginary')) + + >>> plt.show() + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def rfft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 1-D discrete Fourier Transform for real input. + + This function computes the 1-D *n*-point discrete Fourier + Transform (DFT) of a real-valued array by means of an efficient algorithm + called the Fast Fourier Transform (FFT). + + Parameters + ---------- + x : array_like + Input array + n : int, optional + Number of points along transformation axis in the input to use. + If `n` is smaller than the length of the input, the input is cropped. + If it is larger, the input is padded with zeros. If `n` is not given, + the length of the input along the axis specified by `axis` is used. + axis : int, optional + Axis over which to compute the FFT. If not given, the last axis is + used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + If `n` is even, the length of the transformed axis is ``(n/2)+1``. + If `n` is odd, the length is ``(n+1)/2``. + + Raises + ------ + IndexError + If `axis` is larger than the last axis of `a`. + + See Also + -------- + irfft : The inverse of `rfft`. + fft : The 1-D FFT of general (complex) input. + fftn : The N-D FFT. + rfft2 : The 2-D FFT of real input. + rfftn : The N-D FFT of real input. + + Notes + ----- + When the DFT is computed for purely real input, the output is + Hermitian-symmetric, i.e., the negative frequency terms are just the complex + conjugates of the corresponding positive-frequency terms, and the + negative-frequency terms are therefore redundant. This function does not + compute the negative frequency terms, and the length of the transformed + axis of the output is therefore ``n//2 + 1``. + + When ``X = rfft(x)`` and fs is the sampling frequency, ``X[0]`` contains + the zero-frequency term 0*fs, which is real due to Hermitian symmetry. + + If `n` is even, ``A[-1]`` contains the term representing both positive + and negative Nyquist frequency (+fs/2 and -fs/2), and must also be purely + real. If `n` is odd, there is no term at fs/2; ``A[-1]`` contains + the largest positive frequency (fs/2*(n-1)/n), and is complex in the + general case. + + If the input `a` contains an imaginary part, it is silently discarded. + + Examples + -------- + >>> import scipy.fft + >>> scipy.fft.fft([0, 1, 0, 0]) + array([ 1.+0.j, 0.-1.j, -1.+0.j, 0.+1.j]) # may vary + >>> scipy.fft.rfft([0, 1, 0, 0]) + array([ 1.+0.j, 0.-1.j, -1.+0.j]) # may vary + + Notice how the final element of the `fft` output is the complex conjugate + of the second element, for real input. For `rfft`, this symmetry is + exploited to compute only the non-negative frequency terms. + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def irfft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Computes the inverse of `rfft`. + + This function computes the inverse of the 1-D *n*-point + discrete Fourier Transform of real input computed by `rfft`. + In other words, ``irfft(rfft(x), len(x)) == x`` to within numerical + accuracy. (See Notes below for why ``len(a)`` is necessary here.) + + The input is expected to be in the form returned by `rfft`, i.e., the + real zero-frequency term followed by the complex positive frequency terms + in order of increasing frequency. Since the discrete Fourier Transform of + real input is Hermitian-symmetric, the negative frequency terms are taken + to be the complex conjugates of the corresponding positive frequency terms. + + Parameters + ---------- + x : array_like + The input array. + n : int, optional + Length of the transformed axis of the output. + For `n` output points, ``n//2+1`` input points are necessary. If the + input is longer than this, it is cropped. If it is shorter than this, + it is padded with zeros. If `n` is not given, it is taken to be + ``2*(m-1)``, where ``m`` is the length of the input along the axis + specified by `axis`. + axis : int, optional + Axis over which to compute the inverse FFT. If not given, the last + axis is used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + The length of the transformed axis is `n`, or, if `n` is not given, + ``2*(m-1)`` where ``m`` is the length of the transformed axis of the + input. To get an odd number of output points, `n` must be specified. + + Raises + ------ + IndexError + If `axis` is larger than the last axis of `x`. + + See Also + -------- + rfft : The 1-D FFT of real input, of which `irfft` is inverse. + fft : The 1-D FFT. + irfft2 : The inverse of the 2-D FFT of real input. + irfftn : The inverse of the N-D FFT of real input. + + Notes + ----- + Returns the real valued `n`-point inverse discrete Fourier transform + of `x`, where `x` contains the non-negative frequency terms of a + Hermitian-symmetric sequence. `n` is the length of the result, not the + input. + + If you specify an `n` such that `a` must be zero-padded or truncated, the + extra/removed values will be added/removed at high frequencies. One can + thus resample a series to `m` points via Fourier interpolation by: + ``a_resamp = irfft(rfft(a), m)``. + + The default value of `n` assumes an even output length. By the Hermitian + symmetry, the last imaginary component must be 0 and so is ignored. To + avoid losing information, the correct length of the real input *must* be + given. + + Examples + -------- + >>> import scipy.fft + >>> scipy.fft.ifft([1, -1j, -1, 1j]) + array([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]) # may vary + >>> scipy.fft.irfft([1, -1j, -1]) + array([0., 1., 0., 0.]) + + Notice how the last term in the input to the ordinary `ifft` is the + complex conjugate of the second term, and the output has zero imaginary + part everywhere. When calling `irfft`, the negative frequencies are not + specified, and the output array is purely real. + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def hfft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the FFT of a signal that has Hermitian symmetry, i.e., a real + spectrum. + + Parameters + ---------- + x : array_like + The input array. + n : int, optional + Length of the transformed axis of the output. For `n` output + points, ``n//2 + 1`` input points are necessary. If the input is + longer than this, it is cropped. If it is shorter than this, it is + padded with zeros. If `n` is not given, it is taken to be ``2*(m-1)``, + where ``m`` is the length of the input along the axis specified by + `axis`. + axis : int, optional + Axis over which to compute the FFT. If not given, the last + axis is used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See `fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + The length of the transformed axis is `n`, or, if `n` is not given, + ``2*m - 2``, where ``m`` is the length of the transformed axis of + the input. To get an odd number of output points, `n` must be + specified, for instance, as ``2*m - 1`` in the typical case, + + Raises + ------ + IndexError + If `axis` is larger than the last axis of `a`. + + See Also + -------- + rfft : Compute the 1-D FFT for real input. + ihfft : The inverse of `hfft`. + hfftn : Compute the N-D FFT of a Hermitian signal. + + Notes + ----- + `hfft`/`ihfft` are a pair analogous to `rfft`/`irfft`, but for the + opposite case: here the signal has Hermitian symmetry in the time + domain and is real in the frequency domain. So, here, it's `hfft`, for + which you must supply the length of the result if it is to be odd. + * even: ``ihfft(hfft(a, 2*len(a) - 2) == a``, within roundoff error, + * odd: ``ihfft(hfft(a, 2*len(a) - 1) == a``, within roundoff error. + + Examples + -------- + >>> from scipy.fft import fft, hfft + >>> import numpy as np + >>> a = 2 * np.pi * np.arange(10) / 10 + >>> signal = np.cos(a) + 3j * np.sin(3 * a) + >>> fft(signal).round(10) + array([ -0.+0.j, 5.+0.j, -0.+0.j, 15.-0.j, 0.+0.j, 0.+0.j, + -0.+0.j, -15.-0.j, 0.+0.j, 5.+0.j]) + >>> hfft(signal[:6]).round(10) # Input first half of signal + array([ 0., 5., 0., 15., -0., 0., 0., -15., -0., 5.]) + >>> hfft(signal, 10) # Input entire signal and truncate + array([ 0., 5., 0., 15., -0., 0., 0., -15., -0., 5.]) + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ihfft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the inverse FFT of a signal that has Hermitian symmetry. + + Parameters + ---------- + x : array_like + Input array. + n : int, optional + Length of the inverse FFT, the number of points along + transformation axis in the input to use. If `n` is smaller than + the length of the input, the input is cropped. If it is larger, + the input is padded with zeros. If `n` is not given, the length of + the input along the axis specified by `axis` is used. + axis : int, optional + Axis over which to compute the inverse FFT. If not given, the last + axis is used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See `fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axis + indicated by `axis`, or the last one if `axis` is not specified. + The length of the transformed axis is ``n//2 + 1``. + + See Also + -------- + hfft, irfft + + Notes + ----- + `hfft`/`ihfft` are a pair analogous to `rfft`/`irfft`, but for the + opposite case: here, the signal has Hermitian symmetry in the time + domain and is real in the frequency domain. So, here, it's `hfft`, for + which you must supply the length of the result if it is to be odd: + * even: ``ihfft(hfft(a, 2*len(a) - 2) == a``, within roundoff error, + * odd: ``ihfft(hfft(a, 2*len(a) - 1) == a``, within roundoff error. + + Examples + -------- + >>> from scipy.fft import ifft, ihfft + >>> import numpy as np + >>> spectrum = np.array([ 15, -4, 0, -1, 0, -4]) + >>> ifft(spectrum) + array([1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 3.+0.j, 2.+0.j]) # may vary + >>> ihfft(spectrum) + array([ 1.-0.j, 2.-0.j, 3.-0.j, 4.-0.j]) # may vary + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def fftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the N-D discrete Fourier Transform. + + This function computes the N-D discrete Fourier Transform over + any number of axes in an M-D array by means of the Fast Fourier + Transform (FFT). + + Parameters + ---------- + x : array_like + Input array, can be complex. + s : sequence of ints, optional + Shape (length of each transformed axis) of the output + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). + This corresponds to ``n`` for ``fft(x, n)``. + Along any axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. + axes : sequence of ints, optional + Axes over which to compute the FFT. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` and `x`, + as explained in the parameters section above. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + ifftn : The inverse of `fftn`, the inverse N-D FFT. + fft : The 1-D FFT, with definitions and conventions used. + rfftn : The N-D FFT of real input. + fft2 : The 2-D FFT. + fftshift : Shifts zero-frequency terms to centre of array. + + Notes + ----- + The output, analogously to `fft`, contains the term for zero frequency in + the low-order corner of all axes, the positive frequency terms in the + first half of all axes, the term for the Nyquist frequency in the middle + of all axes and the negative frequency terms in the second half of all + axes, in order of decreasingly negative frequency. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.mgrid[:3, :3, :3][0] + >>> scipy.fft.fftn(x, axes=(1, 2)) + array([[[ 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]], + [[ 9.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]], + [[18.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]]]) + >>> scipy.fft.fftn(x, (2, 2), axes=(0, 1)) + array([[[ 2.+0.j, 2.+0.j, 2.+0.j], # may vary + [ 0.+0.j, 0.+0.j, 0.+0.j]], + [[-2.+0.j, -2.+0.j, -2.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]]]) + + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> [X, Y] = np.meshgrid(2 * np.pi * np.arange(200) / 12, + ... 2 * np.pi * np.arange(200) / 34) + >>> S = np.sin(X) + np.cos(Y) + rng.uniform(0, 1, X.shape) + >>> FS = scipy.fft.fftn(S) + >>> plt.imshow(np.log(np.abs(scipy.fft.fftshift(FS))**2)) + + >>> plt.show() + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ifftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the N-D inverse discrete Fourier Transform. + + This function computes the inverse of the N-D discrete + Fourier Transform over any number of axes in an M-D array by + means of the Fast Fourier Transform (FFT). In other words, + ``ifftn(fftn(x)) == x`` to within numerical accuracy. + + The input, analogously to `ifft`, should be ordered in the same way as is + returned by `fftn`, i.e., it should have the term for zero frequency + in all axes in the low-order corner, the positive frequency terms in the + first half of all axes, the term for the Nyquist frequency in the middle + of all axes and the negative frequency terms in the second half of all + axes, in order of decreasingly negative frequency. + + Parameters + ---------- + x : array_like + Input array, can be complex. + s : sequence of ints, optional + Shape (length of each transformed axis) of the output + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). + This corresponds to ``n`` for ``ifft(x, n)``. + Along any axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. See notes for issue on `ifft` zero padding. + axes : sequence of ints, optional + Axes over which to compute the IFFT. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` or `x`, + as explained in the parameters section above. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + fftn : The forward N-D FFT, of which `ifftn` is the inverse. + ifft : The 1-D inverse FFT. + ifft2 : The 2-D inverse FFT. + ifftshift : Undoes `fftshift`, shifts zero-frequency terms to beginning + of array. + + Notes + ----- + Zero-padding, analogously with `ifft`, is performed by appending zeros to + the input along the specified dimension. Although this is the common + approach, it might lead to surprising results. If another form of zero + padding is desired, it must be performed before `ifftn` is called. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.eye(4) + >>> scipy.fft.ifftn(scipy.fft.fftn(x, axes=(0,)), axes=(1,)) + array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]]) + + + Create and plot an image with band-limited frequency content: + + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> n = np.zeros((200,200), dtype=complex) + >>> n[60:80, 20:40] = np.exp(1j*rng.uniform(0, 2*np.pi, (20, 20))) + >>> im = scipy.fft.ifftn(n).real + >>> plt.imshow(im) + + >>> plt.show() + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def fft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 2-D discrete Fourier Transform + + This function computes the N-D discrete Fourier Transform + over any axes in an M-D array by means of the + Fast Fourier Transform (FFT). By default, the transform is computed over + the last two axes of the input array, i.e., a 2-dimensional FFT. + + Parameters + ---------- + x : array_like + Input array, can be complex + s : sequence of ints, optional + Shape (length of each transformed axis) of the output + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). + This corresponds to ``n`` for ``fft(x, n)``. + Along each axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. + axes : sequence of ints, optional + Axes over which to compute the FFT. If not given, the last two axes are + used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or the last two axes if `axes` is not given. + + Raises + ------ + ValueError + If `s` and `axes` have different length, or `axes` not given and + ``len(s) != 2``. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + ifft2 : The inverse 2-D FFT. + fft : The 1-D FFT. + fftn : The N-D FFT. + fftshift : Shifts zero-frequency terms to the center of the array. + For 2-D input, swaps first and third quadrants, and second + and fourth quadrants. + + Notes + ----- + `fft2` is just `fftn` with a different default for `axes`. + + The output, analogously to `fft`, contains the term for zero frequency in + the low-order corner of the transformed axes, the positive frequency terms + in the first half of these axes, the term for the Nyquist frequency in the + middle of the axes and the negative frequency terms in the second half of + the axes, in order of decreasingly negative frequency. + + See `fftn` for details and a plotting example, and `fft` for + definitions and conventions used. + + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.mgrid[:5, :5][0] + >>> scipy.fft.fft2(x) + array([[ 50. +0.j , 0. +0.j , 0. +0.j , # may vary + 0. +0.j , 0. +0.j ], + [-12.5+17.20477401j, 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5 +4.0614962j , 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5 -4.0614962j , 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5-17.20477401j, 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ifft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 2-D inverse discrete Fourier Transform. + + This function computes the inverse of the 2-D discrete Fourier + Transform over any number of axes in an M-D array by means of + the Fast Fourier Transform (FFT). In other words, ``ifft2(fft2(x)) == x`` + to within numerical accuracy. By default, the inverse transform is + computed over the last two axes of the input array. + + The input, analogously to `ifft`, should be ordered in the same way as is + returned by `fft2`, i.e., it should have the term for zero frequency + in the low-order corner of the two axes, the positive frequency terms in + the first half of these axes, the term for the Nyquist frequency in the + middle of the axes and the negative frequency terms in the second half of + both axes, in order of decreasingly negative frequency. + + Parameters + ---------- + x : array_like + Input array, can be complex. + s : sequence of ints, optional + Shape (length of each axis) of the output (``s[0]`` refers to axis 0, + ``s[1]`` to axis 1, etc.). This corresponds to `n` for ``ifft(x, n)``. + Along each axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. See notes for issue on `ifft` zero padding. + axes : sequence of ints, optional + Axes over which to compute the FFT. If not given, the last two + axes are used. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or the last two axes if `axes` is not given. + + Raises + ------ + ValueError + If `s` and `axes` have different length, or `axes` not given and + ``len(s) != 2``. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + fft2 : The forward 2-D FFT, of which `ifft2` is the inverse. + ifftn : The inverse of the N-D FFT. + fft : The 1-D FFT. + ifft : The 1-D inverse FFT. + + Notes + ----- + `ifft2` is just `ifftn` with a different default for `axes`. + + See `ifftn` for details and a plotting example, and `fft` for + definition and conventions used. + + Zero-padding, analogously with `ifft`, is performed by appending zeros to + the input along the specified dimension. Although this is the common + approach, it might lead to surprising results. If another form of zero + padding is desired, it must be performed before `ifft2` is called. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = 4 * np.eye(4) + >>> scipy.fft.ifft2(x) + array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j], + [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], + [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def rfftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the N-D discrete Fourier Transform for real input. + + This function computes the N-D discrete Fourier Transform over + any number of axes in an M-D real array by means of the Fast + Fourier Transform (FFT). By default, all axes are transformed, with the + real transform performed over the last axis, while the remaining + transforms are complex. + + Parameters + ---------- + x : array_like + Input array, taken to be real. + s : sequence of ints, optional + Shape (length along each transformed axis) to use from the input. + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). + The final element of `s` corresponds to `n` for ``rfft(x, n)``, while + for the remaining axes, it corresponds to `n` for ``fft(x, n)``. + Along any axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. + axes : sequence of ints, optional + Axes over which to compute the FFT. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` and `x`, + as explained in the parameters section above. + The length of the last axis transformed will be ``s[-1]//2+1``, + while the remaining transformed axes will have lengths according to + `s`, or unchanged from the input. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + irfftn : The inverse of `rfftn`, i.e., the inverse of the N-D FFT + of real input. + fft : The 1-D FFT, with definitions and conventions used. + rfft : The 1-D FFT of real input. + fftn : The N-D FFT. + rfft2 : The 2-D FFT of real input. + + Notes + ----- + The transform for real input is performed over the last transformation + axis, as by `rfft`, then the transform over the remaining axes is + performed as by `fftn`. The order of the output is as for `rfft` for the + final transformation axis, and as for `fftn` for the remaining + transformation axes. + + See `fft` for details, definitions and conventions used. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.ones((2, 2, 2)) + >>> scipy.fft.rfftn(x) + array([[[8.+0.j, 0.+0.j], # may vary + [0.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) + + >>> scipy.fft.rfftn(x, axes=(2, 0)) + array([[[4.+0.j, 0.+0.j], # may vary + [4.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def rfft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 2-D FFT of a real array. + + Parameters + ---------- + x : array + Input array, taken to be real. + s : sequence of ints, optional + Shape of the FFT. + axes : sequence of ints, optional + Axes over which to compute the FFT. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The result of the real 2-D FFT. + + See Also + -------- + irfft2 : The inverse of the 2-D FFT of real input. + rfft : The 1-D FFT of real input. + rfftn : Compute the N-D discrete Fourier Transform for real + input. + + Notes + ----- + This is really just `rfftn` with different default behavior. + For more details see `rfftn`. + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def irfftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Computes the inverse of `rfftn` + + This function computes the inverse of the N-D discrete + Fourier Transform for real input over any number of axes in an + M-D array by means of the Fast Fourier Transform (FFT). In + other words, ``irfftn(rfftn(x), x.shape) == x`` to within numerical + accuracy. (The ``a.shape`` is necessary like ``len(a)`` is for `irfft`, + and for the same reason.) + + The input should be ordered in the same way as is returned by `rfftn`, + i.e., as for `irfft` for the final transformation axis, and as for `ifftn` + along all the other axes. + + Parameters + ---------- + x : array_like + Input array. + s : sequence of ints, optional + Shape (length of each transformed axis) of the output + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). `s` is also the + number of input points used along this axis, except for the last axis, + where ``s[-1]//2+1`` points of the input are used. + Along any axis, if the shape indicated by `s` is smaller than that of + the input, the input is cropped. If it is larger, the input is padded + with zeros. If `s` is not given, the shape of the input along the axes + specified by axes is used. Except for the last axis which is taken to be + ``2*(m-1)``, where ``m`` is the length of the input along that axis. + axes : sequence of ints, optional + Axes over which to compute the inverse FFT. If not given, the last + `len(s)` axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` or `x`, + as explained in the parameters section above. + The length of each transformed axis is as given by the corresponding + element of `s`, or the length of the input in every axis except for the + last one if `s` is not given. In the final transformed axis the length + of the output when `s` is not given is ``2*(m-1)``, where ``m`` is the + length of the final transformed axis of the input. To get an odd + number of output points in the final axis, `s` must be specified. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + rfftn : The forward N-D FFT of real input, + of which `ifftn` is the inverse. + fft : The 1-D FFT, with definitions and conventions used. + irfft : The inverse of the 1-D FFT of real input. + irfft2 : The inverse of the 2-D FFT of real input. + + Notes + ----- + See `fft` for definitions and conventions used. + + See `rfft` for definitions and conventions used for real input. + + The default value of `s` assumes an even output length in the final + transformation axis. When performing the final complex to real + transformation, the Hermitian symmetry requires that the last imaginary + component along that axis must be 0 and so it is ignored. To avoid losing + information, the correct length of the real input *must* be given. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.zeros((3, 2, 2)) + >>> x[0, 0, 0] = 3 * 2 * 2 + >>> scipy.fft.irfftn(x) + array([[[1., 1.], + [1., 1.]], + [[1., 1.], + [1., 1.]], + [[1., 1.], + [1., 1.]]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def irfft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Computes the inverse of `rfft2` + + Parameters + ---------- + x : array_like + The input array + s : sequence of ints, optional + Shape of the real output to the inverse FFT. + axes : sequence of ints, optional + The axes over which to compute the inverse fft. + Default is the last two axes. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The result of the inverse real 2-D FFT. + + See Also + -------- + rfft2 : The 2-D FFT of real input. + irfft : The inverse of the 1-D FFT of real input. + irfftn : The inverse of the N-D FFT of real input. + + Notes + ----- + This is really `irfftn` with different defaults. + For more details see `irfftn`. + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def hfftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the N-D FFT of Hermitian symmetric complex input, i.e., a + signal with a real spectrum. + + This function computes the N-D discrete Fourier Transform for a + Hermitian symmetric complex input over any number of axes in an + M-D array by means of the Fast Fourier Transform (FFT). In other + words, ``ihfftn(hfftn(x, s)) == x`` to within numerical accuracy. (``s`` + here is ``x.shape`` with ``s[-1] = x.shape[-1] * 2 - 1``, this is necessary + for the same reason ``x.shape`` would be necessary for `irfft`.) + + Parameters + ---------- + x : array_like + Input array. + s : sequence of ints, optional + Shape (length of each transformed axis) of the output + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). `s` is also the + number of input points used along this axis, except for the last axis, + where ``s[-1]//2+1`` points of the input are used. + Along any axis, if the shape indicated by `s` is smaller than that of + the input, the input is cropped. If it is larger, the input is padded + with zeros. If `s` is not given, the shape of the input along the axes + specified by axes is used. Except for the last axis which is taken to be + ``2*(m-1)`` where ``m`` is the length of the input along that axis. + axes : sequence of ints, optional + Axes over which to compute the inverse FFT. If not given, the last + `len(s)` axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` or `x`, + as explained in the parameters section above. + The length of each transformed axis is as given by the corresponding + element of `s`, or the length of the input in every axis except for the + last one if `s` is not given. In the final transformed axis the length + of the output when `s` is not given is ``2*(m-1)`` where ``m`` is the + length of the final transformed axis of the input. To get an odd + number of output points in the final axis, `s` must be specified. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + ihfftn : The inverse N-D FFT with real spectrum. Inverse of `hfftn`. + fft : The 1-D FFT, with definitions and conventions used. + rfft : Forward FFT of real input. + + Notes + ----- + For a 1-D signal ``x`` to have a real spectrum, it must satisfy + the Hermitian property:: + + x[i] == np.conj(x[-i]) for all i + + This generalizes into higher dimensions by reflecting over each axis in + turn:: + + x[i, j, k, ...] == np.conj(x[-i, -j, -k, ...]) for all i, j, k, ... + + This should not be confused with a Hermitian matrix, for which the + transpose is its own conjugate:: + + x[i, j] == np.conj(x[j, i]) for all i, j + + + The default value of `s` assumes an even output length in the final + transformation axis. When performing the final complex to real + transformation, the Hermitian symmetry requires that the last imaginary + component along that axis must be 0 and so it is ignored. To avoid losing + information, the correct length of the real input *must* be given. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.ones((3, 2, 2)) + >>> scipy.fft.hfftn(x) + array([[[12., 0.], + [ 0., 0.]], + [[ 0., 0.], + [ 0., 0.]], + [[ 0., 0.], + [ 0., 0.]]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def hfft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 2-D FFT of a Hermitian complex array. + + Parameters + ---------- + x : array + Input array, taken to be Hermitian complex. + s : sequence of ints, optional + Shape of the real output. + axes : sequence of ints, optional + Axes over which to compute the FFT. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See `fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The real result of the 2-D Hermitian complex real FFT. + + See Also + -------- + hfftn : Compute the N-D discrete Fourier Transform for Hermitian + complex input. + + Notes + ----- + This is really just `hfftn` with different default behavior. + For more details see `hfftn`. + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ihfftn(x, s=None, axes=None, norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the N-D inverse discrete Fourier Transform for a real + spectrum. + + This function computes the N-D inverse discrete Fourier Transform + over any number of axes in an M-D real array by means of the Fast + Fourier Transform (FFT). By default, all axes are transformed, with the + real transform performed over the last axis, while the remaining transforms + are complex. + + Parameters + ---------- + x : array_like + Input array, taken to be real. + s : sequence of ints, optional + Shape (length along each transformed axis) to use from the input. + (``s[0]`` refers to axis 0, ``s[1]`` to axis 1, etc.). + Along any axis, if the given shape is smaller than that of the input, + the input is cropped. If it is larger, the input is padded with zeros. + if `s` is not given, the shape of the input along the axes specified + by `axes` is used. + axes : sequence of ints, optional + Axes over which to compute the FFT. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : complex ndarray + The truncated or zero-padded input, transformed along the axes + indicated by `axes`, or by a combination of `s` and `x`, + as explained in the parameters section above. + The length of the last axis transformed will be ``s[-1]//2+1``, + while the remaining transformed axes will have lengths according to + `s`, or unchanged from the input. + + Raises + ------ + ValueError + If `s` and `axes` have different length. + IndexError + If an element of `axes` is larger than the number of axes of `x`. + + See Also + -------- + hfftn : The forward N-D FFT of Hermitian input. + hfft : The 1-D FFT of Hermitian input. + fft : The 1-D FFT, with definitions and conventions used. + fftn : The N-D FFT. + hfft2 : The 2-D FFT of Hermitian input. + + Notes + ----- + The transform for real input is performed over the last transformation + axis, as by `ihfft`, then the transform over the remaining axes is + performed as by `ifftn`. The order of the output is the positive part of + the Hermitian output signal, in the same format as `rfft`. + + Examples + -------- + >>> import scipy.fft + >>> import numpy as np + >>> x = np.ones((2, 2, 2)) + >>> scipy.fft.ihfftn(x) + array([[[1.+0.j, 0.+0.j], # may vary + [0.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) + >>> scipy.fft.ihfftn(x, axes=(2, 0)) + array([[[1.+0.j, 0.+0.j], # may vary + [1.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def ihfft2(x, s=None, axes=(-2, -1), norm=None, overwrite_x=False, workers=None, *, + plan=None): + """ + Compute the 2-D inverse FFT of a real spectrum. + + Parameters + ---------- + x : array_like + The input array + s : sequence of ints, optional + Shape of the real input to the inverse FFT. + axes : sequence of ints, optional + The axes over which to compute the inverse fft. + Default is the last two axes. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see `fft`). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + See :func:`fft` for more details. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + plan : object, optional + This argument is reserved for passing in a precomputed plan provided + by downstream FFT vendors. It is currently not used in SciPy. + + .. versionadded:: 1.5.0 + + Returns + ------- + out : ndarray + The result of the inverse real 2-D FFT. + + See Also + -------- + ihfftn : Compute the inverse of the N-D FFT of Hermitian input. + + Notes + ----- + This is really `ihfftn` with different defaults. + For more details see `ihfftn`. + + """ + return (Dispatchable(x, np.ndarray),) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..14b77661482d8b2284dd129d98b04a9b7947867e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_basic_backend.py @@ -0,0 +1,176 @@ +from scipy._lib._array_api import ( + array_namespace, is_numpy, xp_unsupported_param_msg, is_complex +) +from . import _pocketfft +import numpy as np + + +def _validate_fft_args(workers, plan, norm): + if workers is not None: + raise ValueError(xp_unsupported_param_msg("workers")) + if plan is not None: + raise ValueError(xp_unsupported_param_msg("plan")) + if norm is None: + norm = 'backward' + return norm + + +# pocketfft is used whenever SCIPY_ARRAY_API is not set, +# or x is a NumPy array or array-like. +# When SCIPY_ARRAY_API is set, we try to use xp.fft for CuPy arrays, +# PyTorch arrays and other array API standard supporting objects. +# If xp.fft does not exist, we attempt to convert to np and back to use pocketfft. + +def _execute_1D(func_str, pocketfft_func, x, n, axis, norm, overwrite_x, workers, plan): + xp = array_namespace(x) + + if is_numpy(xp): + return pocketfft_func(x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + norm = _validate_fft_args(workers, plan, norm) + if hasattr(xp, 'fft'): + xp_func = getattr(xp.fft, func_str) + return xp_func(x, n=n, axis=axis, norm=norm) + + x = np.asarray(x) + y = pocketfft_func(x, n=n, axis=axis, norm=norm) + return xp.asarray(y) + + +def _execute_nD(func_str, pocketfft_func, x, s, axes, norm, overwrite_x, workers, plan): + xp = array_namespace(x) + + if is_numpy(xp): + return pocketfft_func(x, s=s, axes=axes, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + norm = _validate_fft_args(workers, plan, norm) + if hasattr(xp, 'fft'): + xp_func = getattr(xp.fft, func_str) + return xp_func(x, s=s, axes=axes, norm=norm) + + x = np.asarray(x) + y = pocketfft_func(x, s=s, axes=axes, norm=norm) + return xp.asarray(y) + + +def fft(x, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_1D('fft', _pocketfft.fft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def ifft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, + plan=None): + return _execute_1D('ifft', _pocketfft.ifft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def rfft(x, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_1D('rfft', _pocketfft.rfft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def irfft(x, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_1D('irfft', _pocketfft.irfft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def hfft(x, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_1D('hfft', _pocketfft.hfft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def ihfft(x, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_1D('ihfft', _pocketfft.ihfft, x, n=n, axis=axis, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def fftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_nD('fftn', _pocketfft.fftn, x, s=s, axes=axes, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + + +def ifftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_nD('ifftn', _pocketfft.ifftn, x, s=s, axes=axes, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def fft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return fftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + + +def ifft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return ifftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + + +def rfftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_nD('rfftn', _pocketfft.rfftn, x, s=s, axes=axes, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def rfft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return rfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + + +def irfftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + return _execute_nD('irfftn', _pocketfft.irfftn, x, s=s, axes=axes, norm=norm, + overwrite_x=overwrite_x, workers=workers, plan=plan) + + +def irfft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return irfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + + +def _swap_direction(norm): + if norm in (None, 'backward'): + norm = 'forward' + elif norm == 'forward': + norm = 'backward' + elif norm != 'ortho': + raise ValueError('Invalid norm value %s; should be "backward", ' + '"ortho", or "forward".' % norm) + return norm + + +def hfftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + xp = array_namespace(x) + if is_numpy(xp): + return _pocketfft.hfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + if is_complex(x, xp): + x = xp.conj(x) + return irfftn(x, s, axes, _swap_direction(norm), + overwrite_x, workers, plan=plan) + + +def hfft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return hfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + + +def ihfftn(x, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, plan=None): + xp = array_namespace(x) + if is_numpy(xp): + return _pocketfft.ihfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) + return xp.conj(rfftn(x, s, axes, _swap_direction(norm), + overwrite_x, workers, plan=plan)) + +def ihfft2(x, s=None, axes=(-2, -1), norm=None, + overwrite_x=False, workers=None, *, plan=None): + return ihfftn(x, s, axes, norm, overwrite_x, workers, plan=plan) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_debug_backends.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_debug_backends.py new file mode 100644 index 0000000000000000000000000000000000000000..c9647c5d6ceddc73b97d95f562662ada02c1ae74 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_debug_backends.py @@ -0,0 +1,22 @@ +import numpy as np + +class NumPyBackend: + """Backend that uses numpy.fft""" + __ua_domain__ = "numpy.scipy.fft" + + @staticmethod + def __ua_function__(method, args, kwargs): + kwargs.pop("overwrite_x", None) + + fn = getattr(np.fft, method.__name__, None) + return (NotImplemented if fn is None + else fn(*args, **kwargs)) + + +class EchoBackend: + """Backend that just prints the __ua_function__ arguments""" + __ua_domain__ = "numpy.scipy.fft" + + @staticmethod + def __ua_function__(method, args, kwargs): + print(method, args, kwargs, sep='\n') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog.py new file mode 100644 index 0000000000000000000000000000000000000000..8960242989c7c1d062af4fe1960c2384abaab94f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog.py @@ -0,0 +1,223 @@ +"""Fast Hankel transforms using the FFTLog algorithm. + +The implementation closely follows the Fortran code of Hamilton (2000). + +added: 14/11/2020 Nicolas Tessore +""" + +from ._basic import _dispatch +from scipy._lib.uarray import Dispatchable +from ._fftlog_backend import fhtoffset +import numpy as np + +__all__ = ['fht', 'ifht', 'fhtoffset'] + + +@_dispatch +def fht(a, dln, mu, offset=0.0, bias=0.0): + r'''Compute the fast Hankel transform. + + Computes the discrete Hankel transform of a logarithmically spaced periodic + sequence using the FFTLog algorithm [1]_, [2]_. + + Parameters + ---------- + a : array_like (..., n) + Real periodic input array, uniformly logarithmically spaced. For + multidimensional input, the transform is performed over the last axis. + dln : float + Uniform logarithmic spacing of the input array. + mu : float + Order of the Hankel transform, any positive or negative real number. + offset : float, optional + Offset of the uniform logarithmic spacing of the output array. + bias : float, optional + Exponent of power law bias, any positive or negative real number. + + Returns + ------- + A : array_like (..., n) + The transformed output array, which is real, periodic, uniformly + logarithmically spaced, and of the same shape as the input array. + + See Also + -------- + ifht : The inverse of `fht`. + fhtoffset : Return an optimal offset for `fht`. + + Notes + ----- + This function computes a discrete version of the Hankel transform + + .. math:: + + A(k) = \int_{0}^{\infty} \! a(r) \, J_\mu(kr) \, k \, dr \;, + + where :math:`J_\mu` is the Bessel function of order :math:`\mu`. The index + :math:`\mu` may be any real number, positive or negative. Note that the + numerical Hankel transform uses an integrand of :math:`k \, dr`, while the + mathematical Hankel transform is commonly defined using :math:`r \, dr`. + + The input array `a` is a periodic sequence of length :math:`n`, uniformly + logarithmically spaced with spacing `dln`, + + .. math:: + + a_j = a(r_j) \;, \quad + r_j = r_c \exp[(j-j_c) \, \mathtt{dln}] + + centred about the point :math:`r_c`. Note that the central index + :math:`j_c = (n-1)/2` is half-integral if :math:`n` is even, so that + :math:`r_c` falls between two input elements. Similarly, the output + array `A` is a periodic sequence of length :math:`n`, also uniformly + logarithmically spaced with spacing `dln` + + .. math:: + + A_j = A(k_j) \;, \quad + k_j = k_c \exp[(j-j_c) \, \mathtt{dln}] + + centred about the point :math:`k_c`. + + The centre points :math:`r_c` and :math:`k_c` of the periodic intervals may + be chosen arbitrarily, but it would be usual to choose the product + :math:`k_c r_c = k_j r_{n-1-j} = k_{n-1-j} r_j` to be unity. This can be + changed using the `offset` parameter, which controls the logarithmic offset + :math:`\log(k_c) = \mathtt{offset} - \log(r_c)` of the output array. + Choosing an optimal value for `offset` may reduce ringing of the discrete + Hankel transform. + + If the `bias` parameter is nonzero, this function computes a discrete + version of the biased Hankel transform + + .. math:: + + A(k) = \int_{0}^{\infty} \! a_q(r) \, (kr)^q \, J_\mu(kr) \, k \, dr + + where :math:`q` is the value of `bias`, and a power law bias + :math:`a_q(r) = a(r) \, (kr)^{-q}` is applied to the input sequence. + Biasing the transform can help approximate the continuous transform of + :math:`a(r)` if there is a value :math:`q` such that :math:`a_q(r)` is + close to a periodic sequence, in which case the resulting :math:`A(k)` will + be close to the continuous transform. + + References + ---------- + .. [1] Talman J. D., 1978, J. Comp. Phys., 29, 35 + .. [2] Hamilton A. J. S., 2000, MNRAS, 312, 257 (astro-ph/9905191) + + Examples + -------- + + This example is the adapted version of ``fftlogtest.f`` which is provided + in [2]_. It evaluates the integral + + .. math:: + + \int^\infty_0 r^{\mu+1} \exp(-r^2/2) J_\mu(k, r) k dr + = k^{\mu+1} \exp(-k^2/2) . + + >>> import numpy as np + >>> from scipy import fft + >>> import matplotlib.pyplot as plt + + Parameters for the transform. + + >>> mu = 0.0 # Order mu of Bessel function + >>> r = np.logspace(-7, 1, 128) # Input evaluation points + >>> dln = np.log(r[1]/r[0]) # Step size + >>> offset = fft.fhtoffset(dln, initial=-6*np.log(10), mu=mu) + >>> k = np.exp(offset)/r[::-1] # Output evaluation points + + Define the analytical function. + + >>> def f(x, mu): + ... """Analytical function: x^(mu+1) exp(-x^2/2).""" + ... return x**(mu + 1)*np.exp(-x**2/2) + + Evaluate the function at ``r`` and compute the corresponding values at + ``k`` using FFTLog. + + >>> a_r = f(r, mu) + >>> fht = fft.fht(a_r, dln, mu=mu, offset=offset) + + For this example we can actually compute the analytical response (which in + this case is the same as the input function) for comparison and compute the + relative error. + + >>> a_k = f(k, mu) + >>> rel_err = abs((fht-a_k)/a_k) + + Plot the result. + + >>> figargs = {'sharex': True, 'sharey': True, 'constrained_layout': True} + >>> fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), **figargs) + >>> ax1.set_title(r'$r^{\mu+1}\ \exp(-r^2/2)$') + >>> ax1.loglog(r, a_r, 'k', lw=2) + >>> ax1.set_xlabel('r') + >>> ax2.set_title(r'$k^{\mu+1} \exp(-k^2/2)$') + >>> ax2.loglog(k, a_k, 'k', lw=2, label='Analytical') + >>> ax2.loglog(k, fht, 'C3--', lw=2, label='FFTLog') + >>> ax2.set_xlabel('k') + >>> ax2.legend(loc=3, framealpha=1) + >>> ax2.set_ylim([1e-10, 1e1]) + >>> ax2b = ax2.twinx() + >>> ax2b.loglog(k, rel_err, 'C0', label='Rel. Error (-)') + >>> ax2b.set_ylabel('Rel. Error (-)', color='C0') + >>> ax2b.tick_params(axis='y', labelcolor='C0') + >>> ax2b.legend(loc=4, framealpha=1) + >>> ax2b.set_ylim([1e-9, 1e-3]) + >>> plt.show() + + ''' + return (Dispatchable(a, np.ndarray),) + + +@_dispatch +def ifht(A, dln, mu, offset=0.0, bias=0.0): + r"""Compute the inverse fast Hankel transform. + + Computes the discrete inverse Hankel transform of a logarithmically spaced + periodic sequence. This is the inverse operation to `fht`. + + Parameters + ---------- + A : array_like (..., n) + Real periodic input array, uniformly logarithmically spaced. For + multidimensional input, the transform is performed over the last axis. + dln : float + Uniform logarithmic spacing of the input array. + mu : float + Order of the Hankel transform, any positive or negative real number. + offset : float, optional + Offset of the uniform logarithmic spacing of the output array. + bias : float, optional + Exponent of power law bias, any positive or negative real number. + + Returns + ------- + a : array_like (..., n) + The transformed output array, which is real, periodic, uniformly + logarithmically spaced, and of the same shape as the input array. + + See Also + -------- + fht : Definition of the fast Hankel transform. + fhtoffset : Return an optimal offset for `ifht`. + + Notes + ----- + This function computes a discrete version of the Hankel transform + + .. math:: + + a(r) = \int_{0}^{\infty} \! A(k) \, J_\mu(kr) \, r \, dk \;, + + where :math:`J_\mu` is the Bessel function of order :math:`\mu`. The index + :math:`\mu` may be any real number, positive or negative. Note that the + numerical inverse Hankel transform uses an integrand of :math:`r \, dk`, while the + mathematical inverse Hankel transform is commonly defined using :math:`k \, dk`. + + See `fht` for further details. + """ + return (Dispatchable(A, np.ndarray),) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..0f262b22af74a888633069b96ab408cd4a936f32 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_fftlog_backend.py @@ -0,0 +1,197 @@ +import numpy as np +from warnings import warn +from ._basic import rfft, irfft +from ..special import loggamma, poch + +from scipy._lib._array_api import array_namespace, copy + +__all__ = ['fht', 'ifht', 'fhtoffset'] + +# constants +LN_2 = np.log(2) + + +def fht(a, dln, mu, offset=0.0, bias=0.0): + xp = array_namespace(a) + + # size of transform + n = a.shape[-1] + + # bias input array + if bias != 0: + # a_q(r) = a(r) (r/r_c)^{-q} + j_c = (n-1)/2 + j = xp.arange(n, dtype=xp.float64) + a = a * xp.exp(-bias*(j - j_c)*dln) + + # compute FHT coefficients + u = xp.asarray(fhtcoeff(n, dln, mu, offset=offset, bias=bias)) + + # transform + A = _fhtq(a, u, xp=xp) + + # bias output array + if bias != 0: + # A(k) = A_q(k) (k/k_c)^{-q} (k_c r_c)^{-q} + A *= xp.exp(-bias*((j - j_c)*dln + offset)) + + return A + + +def ifht(A, dln, mu, offset=0.0, bias=0.0): + xp = array_namespace(A) + + # size of transform + n = A.shape[-1] + + # bias input array + if bias != 0: + # A_q(k) = A(k) (k/k_c)^{q} (k_c r_c)^{q} + j_c = (n-1)/2 + j = xp.arange(n, dtype=xp.float64) + A = A * xp.exp(bias*((j - j_c)*dln + offset)) + + # compute FHT coefficients + u = xp.asarray(fhtcoeff(n, dln, mu, offset=offset, bias=bias, inverse=True)) + + # transform + a = _fhtq(A, u, inverse=True, xp=xp) + + # bias output array + if bias != 0: + # a(r) = a_q(r) (r/r_c)^{q} + a /= xp.exp(-bias*(j - j_c)*dln) + + return a + + +def fhtcoeff(n, dln, mu, offset=0.0, bias=0.0, inverse=False): + """Compute the coefficient array for a fast Hankel transform.""" + lnkr, q = offset, bias + + # Hankel transform coefficients + # u_m = (kr)^{-i 2m pi/(n dlnr)} U_mu(q + i 2m pi/(n dlnr)) + # with U_mu(x) = 2^x Gamma((mu+1+x)/2)/Gamma((mu+1-x)/2) + xp = (mu+1+q)/2 + xm = (mu+1-q)/2 + y = np.linspace(0, np.pi*(n//2)/(n*dln), n//2+1) + u = np.empty(n//2+1, dtype=complex) + v = np.empty(n//2+1, dtype=complex) + u.imag[:] = y + u.real[:] = xm + loggamma(u, out=v) + u.real[:] = xp + loggamma(u, out=u) + y *= 2*(LN_2 - lnkr) + u.real -= v.real + u.real += LN_2*q + u.imag += v.imag + u.imag += y + np.exp(u, out=u) + + # fix last coefficient to be real + u.imag[-1] = 0 + + # deal with special cases + if not np.isfinite(u[0]): + # write u_0 = 2^q Gamma(xp)/Gamma(xm) = 2^q poch(xm, xp-xm) + # poch() handles special cases for negative integers correctly + u[0] = 2**q * poch(xm, xp-xm) + # the coefficient may be inf or 0, meaning the transform or the + # inverse transform, respectively, is singular + + # check for singular transform or singular inverse transform + if np.isinf(u[0]) and not inverse: + warn('singular transform; consider changing the bias', stacklevel=3) + # fix coefficient to obtain (potentially correct) transform anyway + u = copy(u) + u[0] = 0 + elif u[0] == 0 and inverse: + warn('singular inverse transform; consider changing the bias', stacklevel=3) + # fix coefficient to obtain (potentially correct) inverse anyway + u = copy(u) + u[0] = np.inf + + return u + + +def fhtoffset(dln, mu, initial=0.0, bias=0.0): + """Return optimal offset for a fast Hankel transform. + + Returns an offset close to `initial` that fulfils the low-ringing + condition of [1]_ for the fast Hankel transform `fht` with logarithmic + spacing `dln`, order `mu` and bias `bias`. + + Parameters + ---------- + dln : float + Uniform logarithmic spacing of the transform. + mu : float + Order of the Hankel transform, any positive or negative real number. + initial : float, optional + Initial value for the offset. Returns the closest value that fulfils + the low-ringing condition. + bias : float, optional + Exponent of power law bias, any positive or negative real number. + + Returns + ------- + offset : float + Optimal offset of the uniform logarithmic spacing of the transform that + fulfils a low-ringing condition. + + Examples + -------- + >>> from scipy.fft import fhtoffset + >>> dln = 0.1 + >>> mu = 2.0 + >>> initial = 0.5 + >>> bias = 0.0 + >>> offset = fhtoffset(dln, mu, initial, bias) + >>> offset + 0.5454581477676637 + + See Also + -------- + fht : Definition of the fast Hankel transform. + + References + ---------- + .. [1] Hamilton A. J. S., 2000, MNRAS, 312, 257 (astro-ph/9905191) + + """ + + lnkr, q = initial, bias + + xp = (mu+1+q)/2 + xm = (mu+1-q)/2 + y = np.pi/(2*dln) + zp = loggamma(xp + 1j*y) + zm = loggamma(xm + 1j*y) + arg = (LN_2 - lnkr)/dln + (zp.imag + zm.imag)/np.pi + return lnkr + (arg - np.round(arg))*dln + + +def _fhtq(a, u, inverse=False, *, xp=None): + """Compute the biased fast Hankel transform. + + This is the basic FFTLog routine. + """ + if xp is None: + xp = np + + # size of transform + n = a.shape[-1] + + # biased fast Hankel transform via real FFT + A = rfft(a, axis=-1) + if not inverse: + # forward transform + A *= u + else: + # backward transform + A /= xp.conj(u) + A = irfft(A, n, axis=-1) + A = xp.flip(A, axis=-1) + + return A diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_helper.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..aaa9a107be35f9efacf4b15f958ad57787723443 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_helper.py @@ -0,0 +1,313 @@ +from functools import update_wrapper, lru_cache +import inspect + +from ._pocketfft import helper as _helper + +import numpy as np +from scipy._lib._array_api import array_namespace + + +def next_fast_len(target, real=False): + """Find the next fast size of input data to ``fft``, for zero-padding, etc. + + SciPy's FFT algorithms gain their speed by a recursive divide and conquer + strategy. This relies on efficient functions for small prime factors of the + input length. Thus, the transforms are fastest when using composites of the + prime factors handled by the fft implementation. If there are efficient + functions for all radices <= `n`, then the result will be a number `x` + >= ``target`` with only prime factors < `n`. (Also known as `n`-smooth + numbers) + + Parameters + ---------- + target : int + Length to start searching from. Must be a positive integer. + real : bool, optional + True if the FFT involves real input or output (e.g., `rfft` or `hfft` + but not `fft`). Defaults to False. + + Returns + ------- + out : int + The smallest fast length greater than or equal to ``target``. + + Notes + ----- + The result of this function may change in future as performance + considerations change, for example, if new prime factors are added. + + Calling `fft` or `ifft` with real input data performs an ``'R2C'`` + transform internally. + + Examples + -------- + On a particular machine, an FFT of prime length takes 11.4 ms: + + >>> from scipy import fft + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> min_len = 93059 # prime length is worst case for speed + >>> a = rng.standard_normal(min_len) + >>> b = fft.fft(a) + + Zero-padding to the next regular length reduces computation time to + 1.6 ms, a speedup of 7.3 times: + + >>> fft.next_fast_len(min_len, real=True) + 93312 + >>> b = fft.fft(a, 93312) + + Rounding up to the next power of 2 is not optimal, taking 3.0 ms to + compute; 1.9 times longer than the size given by ``next_fast_len``: + + >>> b = fft.fft(a, 131072) + + """ + pass + + +# Directly wrap the c-function good_size but take the docstring etc., from the +# next_fast_len function above +_sig = inspect.signature(next_fast_len) +next_fast_len = update_wrapper(lru_cache(_helper.good_size), next_fast_len) +next_fast_len.__wrapped__ = _helper.good_size +next_fast_len.__signature__ = _sig + + +def _init_nd_shape_and_axes(x, shape, axes): + """Handle shape and axes arguments for N-D transforms. + + Returns the shape and axes in a standard form, taking into account negative + values and checking for various potential errors. + + Parameters + ---------- + x : array_like + The input array. + shape : int or array_like of ints or None + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If `shape` is -1, the size of the corresponding dimension of `x` is + used. + axes : int or array_like of ints or None + Axes along which the calculation is computed. + The default is over all axes. + Negative indices are automatically converted to their positive + counterparts. + + Returns + ------- + shape : tuple + The shape of the result as a tuple of integers. + axes : list + Axes along which the calculation is computed, as a list of integers. + + """ + x = np.asarray(x) + return _helper._init_nd_shape_and_axes(x, shape, axes) + + +def fftfreq(n, d=1.0, *, xp=None, device=None): + """Return the Discrete Fourier Transform sample frequencies. + + The returned float array `f` contains the frequency bin centers in cycles + per unit of the sample spacing (with zero at the start). For instance, if + the sample spacing is in seconds, then the frequency unit is cycles/second. + + Given a window length `n` and a sample spacing `d`:: + + f = [0, 1, ..., n/2-1, -n/2, ..., -1] / (d*n) if n is even + f = [0, 1, ..., (n-1)/2, -(n-1)/2, ..., -1] / (d*n) if n is odd + + Parameters + ---------- + n : int + Window length. + d : scalar, optional + Sample spacing (inverse of the sampling rate). Defaults to 1. + xp : array_namespace, optional + The namespace for the return array. Default is None, where NumPy is used. + device : device, optional + The device for the return array. + Only valid when `xp.fft.fftfreq` implements the device parameter. + + Returns + ------- + f : ndarray + Array of length `n` containing the sample frequencies. + + Examples + -------- + >>> import numpy as np + >>> import scipy.fft + >>> signal = np.array([-2, 8, 6, 4, 1, 0, 3, 5], dtype=float) + >>> fourier = scipy.fft.fft(signal) + >>> n = signal.size + >>> timestep = 0.1 + >>> freq = scipy.fft.fftfreq(n, d=timestep) + >>> freq + array([ 0. , 1.25, 2.5 , ..., -3.75, -2.5 , -1.25]) + + """ + xp = np if xp is None else xp + # numpy does not yet support the `device` keyword + # `xp.__name__ != 'numpy'` should be removed when numpy is compatible + if hasattr(xp, 'fft') and xp.__name__ != 'numpy': + return xp.fft.fftfreq(n, d=d, device=device) + if device is not None: + raise ValueError('device parameter is not supported for input array type') + return np.fft.fftfreq(n, d=d) + + +def rfftfreq(n, d=1.0, *, xp=None, device=None): + """Return the Discrete Fourier Transform sample frequencies + (for usage with rfft, irfft). + + The returned float array `f` contains the frequency bin centers in cycles + per unit of the sample spacing (with zero at the start). For instance, if + the sample spacing is in seconds, then the frequency unit is cycles/second. + + Given a window length `n` and a sample spacing `d`:: + + f = [0, 1, ..., n/2-1, n/2] / (d*n) if n is even + f = [0, 1, ..., (n-1)/2-1, (n-1)/2] / (d*n) if n is odd + + Unlike `fftfreq` (but like `scipy.fftpack.rfftfreq`) + the Nyquist frequency component is considered to be positive. + + Parameters + ---------- + n : int + Window length. + d : scalar, optional + Sample spacing (inverse of the sampling rate). Defaults to 1. + xp : array_namespace, optional + The namespace for the return array. Default is None, where NumPy is used. + device : device, optional + The device for the return array. + Only valid when `xp.fft.rfftfreq` implements the device parameter. + + Returns + ------- + f : ndarray + Array of length ``n//2 + 1`` containing the sample frequencies. + + Examples + -------- + >>> import numpy as np + >>> import scipy.fft + >>> signal = np.array([-2, 8, 6, 4, 1, 0, 3, 5, -3, 4], dtype=float) + >>> fourier = scipy.fft.rfft(signal) + >>> n = signal.size + >>> sample_rate = 100 + >>> freq = scipy.fft.fftfreq(n, d=1./sample_rate) + >>> freq + array([ 0., 10., 20., ..., -30., -20., -10.]) + >>> freq = scipy.fft.rfftfreq(n, d=1./sample_rate) + >>> freq + array([ 0., 10., 20., 30., 40., 50.]) + + """ + xp = np if xp is None else xp + # numpy does not yet support the `device` keyword + # `xp.__name__ != 'numpy'` should be removed when numpy is compatible + if hasattr(xp, 'fft') and xp.__name__ != 'numpy': + return xp.fft.rfftfreq(n, d=d, device=device) + if device is not None: + raise ValueError('device parameter is not supported for input array type') + return np.fft.rfftfreq(n, d=d) + + +def fftshift(x, axes=None): + """Shift the zero-frequency component to the center of the spectrum. + + This function swaps half-spaces for all axes listed (defaults to all). + Note that ``y[0]`` is the Nyquist component only if ``len(x)`` is even. + + Parameters + ---------- + x : array_like + Input array. + axes : int or shape tuple, optional + Axes over which to shift. Default is None, which shifts all axes. + + Returns + ------- + y : ndarray + The shifted array. + + See Also + -------- + ifftshift : The inverse of `fftshift`. + + Examples + -------- + >>> import numpy as np + >>> freqs = np.fft.fftfreq(10, 0.1) + >>> freqs + array([ 0., 1., 2., ..., -3., -2., -1.]) + >>> np.fft.fftshift(freqs) + array([-5., -4., -3., -2., -1., 0., 1., 2., 3., 4.]) + + Shift the zero-frequency component only along the second axis: + + >>> freqs = np.fft.fftfreq(9, d=1./9).reshape(3, 3) + >>> freqs + array([[ 0., 1., 2.], + [ 3., 4., -4.], + [-3., -2., -1.]]) + >>> np.fft.fftshift(freqs, axes=(1,)) + array([[ 2., 0., 1.], + [-4., 3., 4.], + [-1., -3., -2.]]) + + """ + xp = array_namespace(x) + if hasattr(xp, 'fft'): + return xp.fft.fftshift(x, axes=axes) + x = np.asarray(x) + y = np.fft.fftshift(x, axes=axes) + return xp.asarray(y) + + +def ifftshift(x, axes=None): + """The inverse of `fftshift`. Although identical for even-length `x`, the + functions differ by one sample for odd-length `x`. + + Parameters + ---------- + x : array_like + Input array. + axes : int or shape tuple, optional + Axes over which to calculate. Defaults to None, which shifts all axes. + + Returns + ------- + y : ndarray + The shifted array. + + See Also + -------- + fftshift : Shift zero-frequency component to the center of the spectrum. + + Examples + -------- + >>> import numpy as np + >>> freqs = np.fft.fftfreq(9, d=1./9).reshape(3, 3) + >>> freqs + array([[ 0., 1., 2.], + [ 3., 4., -4.], + [-3., -2., -1.]]) + >>> np.fft.ifftshift(np.fft.fftshift(freqs)) + array([[ 0., 1., 2.], + [ 3., 4., -4.], + [-3., -2., -1.]]) + + """ + xp = array_namespace(x) + if hasattr(xp, 'fft'): + return xp.fft.ifftshift(x, axes=axes) + x = np.asarray(x) + y = np.fft.ifftshift(x, axes=axes) + return xp.asarray(y) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/LICENSE.md b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..1b5163d8435976c24988afbd39ded304947178cb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/LICENSE.md @@ -0,0 +1,25 @@ +Copyright (C) 2010-2019 Max-Planck-Society +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of the copyright holder nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0671484c9a0780df353b9b783813b6fa7492d38d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/__init__.py @@ -0,0 +1,9 @@ +""" FFT backend using pypocketfft """ + +from .basic import * +from .realtransforms import * +from .helper import * + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/basic.py new file mode 100644 index 0000000000000000000000000000000000000000..bd2d0d33958021c431171b72f72c37363ac98e03 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/basic.py @@ -0,0 +1,251 @@ +""" +Discrete Fourier Transforms - basic.py +""" +import numpy as np +import functools +from . import pypocketfft as pfft +from .helper import (_asfarray, _init_nd_shape_and_axes, _datacopied, + _fix_shape, _fix_shape_1d, _normalization, + _workers) + +def c2c(forward, x, n=None, axis=-1, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """ Return discrete Fourier transform of real or complex sequence. """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + overwrite_x = overwrite_x or _datacopied(tmp, x) + norm = _normalization(norm, forward) + workers = _workers(workers) + + if n is not None: + tmp, copied = _fix_shape_1d(tmp, n, axis) + overwrite_x = overwrite_x or copied + elif tmp.shape[axis] < 1: + message = f"invalid number of data points ({tmp.shape[axis]}) specified" + raise ValueError(message) + + out = (tmp if overwrite_x and tmp.dtype.kind == 'c' else None) + + return pfft.c2c(tmp, (axis,), forward, norm, out, workers) + + +fft = functools.partial(c2c, True) +fft.__name__ = 'fft' +ifft = functools.partial(c2c, False) +ifft.__name__ = 'ifft' + + +def r2c(forward, x, n=None, axis=-1, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """ + Discrete Fourier transform of a real sequence. + """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + norm = _normalization(norm, forward) + workers = _workers(workers) + + if not np.isrealobj(tmp): + raise TypeError("x must be a real sequence") + + if n is not None: + tmp, _ = _fix_shape_1d(tmp, n, axis) + elif tmp.shape[axis] < 1: + raise ValueError(f"invalid number of data points ({tmp.shape[axis]}) specified") + + # Note: overwrite_x is not utilised + return pfft.r2c(tmp, (axis,), forward, norm, None, workers) + + +rfft = functools.partial(r2c, True) +rfft.__name__ = 'rfft' +ihfft = functools.partial(r2c, False) +ihfft.__name__ = 'ihfft' + + +def c2r(forward, x, n=None, axis=-1, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """ + Return inverse discrete Fourier transform of real sequence x. + """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + norm = _normalization(norm, forward) + workers = _workers(workers) + + # TODO: Optimize for hermitian and real? + if np.isrealobj(tmp): + tmp = tmp + 0.j + + # Last axis utilizes hermitian symmetry + if n is None: + n = (tmp.shape[axis] - 1) * 2 + if n < 1: + raise ValueError(f"Invalid number of data points ({n}) specified") + else: + tmp, _ = _fix_shape_1d(tmp, (n//2) + 1, axis) + + # Note: overwrite_x is not utilized + return pfft.c2r(tmp, (axis,), n, forward, norm, None, workers) + + +hfft = functools.partial(c2r, True) +hfft.__name__ = 'hfft' +irfft = functools.partial(c2r, False) +irfft.__name__ = 'irfft' + + +def hfft2(x, s=None, axes=(-2,-1), norm=None, overwrite_x=False, workers=None, + *, plan=None): + """ + 2-D discrete Fourier transform of a Hermitian sequence + """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + return hfftn(x, s, axes, norm, overwrite_x, workers) + + +def ihfft2(x, s=None, axes=(-2,-1), norm=None, overwrite_x=False, workers=None, + *, plan=None): + """ + 2-D discrete inverse Fourier transform of a Hermitian sequence + """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + return ihfftn(x, s, axes, norm, overwrite_x, workers) + + +def c2cn(forward, x, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """ + Return multidimensional discrete Fourier transform. + """ + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + + shape, axes = _init_nd_shape_and_axes(tmp, s, axes) + overwrite_x = overwrite_x or _datacopied(tmp, x) + workers = _workers(workers) + + if len(axes) == 0: + return x + + tmp, copied = _fix_shape(tmp, shape, axes) + overwrite_x = overwrite_x or copied + + norm = _normalization(norm, forward) + out = (tmp if overwrite_x and tmp.dtype.kind == 'c' else None) + + return pfft.c2c(tmp, axes, forward, norm, out, workers) + + +fftn = functools.partial(c2cn, True) +fftn.__name__ = 'fftn' +ifftn = functools.partial(c2cn, False) +ifftn.__name__ = 'ifftn' + +def r2cn(forward, x, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """Return multidimensional discrete Fourier transform of real input""" + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + + if not np.isrealobj(tmp): + raise TypeError("x must be a real sequence") + + shape, axes = _init_nd_shape_and_axes(tmp, s, axes) + tmp, _ = _fix_shape(tmp, shape, axes) + norm = _normalization(norm, forward) + workers = _workers(workers) + + if len(axes) == 0: + raise ValueError("at least 1 axis must be transformed") + + # Note: overwrite_x is not utilized + return pfft.r2c(tmp, axes, forward, norm, None, workers) + + +rfftn = functools.partial(r2cn, True) +rfftn.__name__ = 'rfftn' +ihfftn = functools.partial(r2cn, False) +ihfftn.__name__ = 'ihfftn' + + +def c2rn(forward, x, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, *, plan=None): + """Multidimensional inverse discrete fourier transform with real output""" + if plan is not None: + raise NotImplementedError('Passing a precomputed plan is not yet ' + 'supported by scipy.fft functions') + tmp = _asfarray(x) + + # TODO: Optimize for hermitian and real? + if np.isrealobj(tmp): + tmp = tmp + 0.j + + noshape = s is None + shape, axes = _init_nd_shape_and_axes(tmp, s, axes) + + if len(axes) == 0: + raise ValueError("at least 1 axis must be transformed") + + shape = list(shape) + if noshape: + shape[-1] = (x.shape[axes[-1]] - 1) * 2 + + norm = _normalization(norm, forward) + workers = _workers(workers) + + # Last axis utilizes hermitian symmetry + lastsize = shape[-1] + shape[-1] = (shape[-1] // 2) + 1 + + tmp, _ = tuple(_fix_shape(tmp, shape, axes)) + + # Note: overwrite_x is not utilized + return pfft.c2r(tmp, axes, lastsize, forward, norm, None, workers) + + +hfftn = functools.partial(c2rn, True) +hfftn.__name__ = 'hfftn' +irfftn = functools.partial(c2rn, False) +irfftn.__name__ = 'irfftn' + + +def r2r_fftpack(forward, x, n=None, axis=-1, norm=None, overwrite_x=False): + """FFT of a real sequence, returning fftpack half complex format""" + tmp = _asfarray(x) + overwrite_x = overwrite_x or _datacopied(tmp, x) + norm = _normalization(norm, forward) + workers = _workers(None) + + if tmp.dtype.kind == 'c': + raise TypeError('x must be a real sequence') + + if n is not None: + tmp, copied = _fix_shape_1d(tmp, n, axis) + overwrite_x = overwrite_x or copied + elif tmp.shape[axis] < 1: + raise ValueError(f"invalid number of data points ({tmp.shape[axis]}) specified") + + out = (tmp if overwrite_x else None) + + return pfft.r2r_fftpack(tmp, (axis,), forward, forward, norm, out, workers) + + +rfft_fftpack = functools.partial(r2r_fftpack, True) +rfft_fftpack.__name__ = 'rfft_fftpack' +irfft_fftpack = functools.partial(r2r_fftpack, False) +irfft_fftpack.__name__ = 'irfft_fftpack' diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/helper.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/helper.py new file mode 100644 index 0000000000000000000000000000000000000000..47e54063fe8e790dc4a92126a7ea0fc69e7b2bb6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/helper.py @@ -0,0 +1,221 @@ +from numbers import Number +import operator +import os +import threading +import contextlib + +import numpy as np + +from scipy._lib._util import copy_if_needed + +# good_size is exposed (and used) from this import +from .pypocketfft import good_size + + +__all__ = ['good_size', 'set_workers', 'get_workers'] + +_config = threading.local() +_cpu_count = os.cpu_count() + + +def _iterable_of_int(x, name=None): + """Convert ``x`` to an iterable sequence of int + + Parameters + ---------- + x : value, or sequence of values, convertible to int + name : str, optional + Name of the argument being converted, only used in the error message + + Returns + ------- + y : ``List[int]`` + """ + if isinstance(x, Number): + x = (x,) + + try: + x = [operator.index(a) for a in x] + except TypeError as e: + name = name or "value" + raise ValueError(f"{name} must be a scalar or iterable of integers") from e + + return x + + +def _init_nd_shape_and_axes(x, shape, axes): + """Handles shape and axes arguments for nd transforms""" + noshape = shape is None + noaxes = axes is None + + if not noaxes: + axes = _iterable_of_int(axes, 'axes') + axes = [a + x.ndim if a < 0 else a for a in axes] + + if any(a >= x.ndim or a < 0 for a in axes): + raise ValueError("axes exceeds dimensionality of input") + if len(set(axes)) != len(axes): + raise ValueError("all axes must be unique") + + if not noshape: + shape = _iterable_of_int(shape, 'shape') + + if axes and len(axes) != len(shape): + raise ValueError("when given, axes and shape arguments" + " have to be of the same length") + if noaxes: + if len(shape) > x.ndim: + raise ValueError("shape requires more axes than are present") + axes = range(x.ndim - len(shape), x.ndim) + + shape = [x.shape[a] if s == -1 else s for s, a in zip(shape, axes)] + elif noaxes: + shape = list(x.shape) + axes = range(x.ndim) + else: + shape = [x.shape[a] for a in axes] + + if any(s < 1 for s in shape): + raise ValueError( + f"invalid number of data points ({shape}) specified") + + return tuple(shape), list(axes) + + +def _asfarray(x): + """ + Convert to array with floating or complex dtype. + + float16 values are also promoted to float32. + """ + if not hasattr(x, "dtype"): + x = np.asarray(x) + + if x.dtype == np.float16: + return np.asarray(x, np.float32) + elif x.dtype.kind not in 'fc': + return np.asarray(x, np.float64) + + # Require native byte order + dtype = x.dtype.newbyteorder('=') + # Always align input + copy = True if not x.flags['ALIGNED'] else copy_if_needed + return np.array(x, dtype=dtype, copy=copy) + +def _datacopied(arr, original): + """ + Strict check for `arr` not sharing any data with `original`, + under the assumption that arr = asarray(original) + """ + if arr is original: + return False + if not isinstance(original, np.ndarray) and hasattr(original, '__array__'): + return False + return arr.base is None + + +def _fix_shape(x, shape, axes): + """Internal auxiliary function for _raw_fft, _raw_fftnd.""" + must_copy = False + + # Build an nd slice with the dimensions to be read from x + index = [slice(None)]*x.ndim + for n, ax in zip(shape, axes): + if x.shape[ax] >= n: + index[ax] = slice(0, n) + else: + index[ax] = slice(0, x.shape[ax]) + must_copy = True + + index = tuple(index) + + if not must_copy: + return x[index], False + + s = list(x.shape) + for n, axis in zip(shape, axes): + s[axis] = n + + z = np.zeros(s, x.dtype) + z[index] = x[index] + return z, True + + +def _fix_shape_1d(x, n, axis): + if n < 1: + raise ValueError( + f"invalid number of data points ({n}) specified") + + return _fix_shape(x, (n,), (axis,)) + + +_NORM_MAP = {None: 0, 'backward': 0, 'ortho': 1, 'forward': 2} + + +def _normalization(norm, forward): + """Returns the pypocketfft normalization mode from the norm argument""" + try: + inorm = _NORM_MAP[norm] + return inorm if forward else (2 - inorm) + except KeyError: + raise ValueError( + f'Invalid norm value {norm!r}, should ' + 'be "backward", "ortho" or "forward"') from None + + +def _workers(workers): + if workers is None: + return getattr(_config, 'default_workers', 1) + + if workers < 0: + if workers >= -_cpu_count: + workers += 1 + _cpu_count + else: + raise ValueError(f"workers value out of range; got {workers}, must not be" + f" less than {-_cpu_count}") + elif workers == 0: + raise ValueError("workers must not be zero") + + return workers + + +@contextlib.contextmanager +def set_workers(workers): + """Context manager for the default number of workers used in `scipy.fft` + + Parameters + ---------- + workers : int + The default number of workers to use + + Examples + -------- + >>> import numpy as np + >>> from scipy import fft, signal + >>> rng = np.random.default_rng() + >>> x = rng.standard_normal((128, 64)) + >>> with fft.set_workers(4): + ... y = signal.fftconvolve(x, x) + + """ + old_workers = get_workers() + _config.default_workers = _workers(operator.index(workers)) + try: + yield + finally: + _config.default_workers = old_workers + + +def get_workers(): + """Returns the default number of workers within the current context + + Examples + -------- + >>> from scipy import fft + >>> fft.get_workers() + 1 + >>> with fft.set_workers(4): + ... fft.get_workers() + 4 + """ + return getattr(_config, 'default_workers', 1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/realtransforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/realtransforms.py new file mode 100644 index 0000000000000000000000000000000000000000..5a0c616742305444d51258e650344c060129dfab --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/realtransforms.py @@ -0,0 +1,109 @@ +import numpy as np +from . import pypocketfft as pfft +from .helper import (_asfarray, _init_nd_shape_and_axes, _datacopied, + _fix_shape, _fix_shape_1d, _normalization, _workers) +import functools + + +def _r2r(forward, transform, x, type=2, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + """Forward or backward 1-D DCT/DST + + Parameters + ---------- + forward : bool + Transform direction (determines type and normalisation) + transform : {pypocketfft.dct, pypocketfft.dst} + The transform to perform + """ + tmp = _asfarray(x) + overwrite_x = overwrite_x or _datacopied(tmp, x) + norm = _normalization(norm, forward) + workers = _workers(workers) + + if not forward: + if type == 2: + type = 3 + elif type == 3: + type = 2 + + if n is not None: + tmp, copied = _fix_shape_1d(tmp, n, axis) + overwrite_x = overwrite_x or copied + elif tmp.shape[axis] < 1: + raise ValueError(f"invalid number of data points ({tmp.shape[axis]}) specified") + + out = (tmp if overwrite_x else None) + + # For complex input, transform real and imaginary components separably + if np.iscomplexobj(x): + out = np.empty_like(tmp) if out is None else out + transform(tmp.real, type, (axis,), norm, out.real, workers) + transform(tmp.imag, type, (axis,), norm, out.imag, workers) + return out + + return transform(tmp, type, (axis,), norm, out, workers, orthogonalize) + + +dct = functools.partial(_r2r, True, pfft.dct) +dct.__name__ = 'dct' +idct = functools.partial(_r2r, False, pfft.dct) +idct.__name__ = 'idct' + +dst = functools.partial(_r2r, True, pfft.dst) +dst.__name__ = 'dst' +idst = functools.partial(_r2r, False, pfft.dst) +idst.__name__ = 'idst' + + +def _r2rn(forward, transform, x, type=2, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + """Forward or backward nd DCT/DST + + Parameters + ---------- + forward : bool + Transform direction (determines type and normalisation) + transform : {pypocketfft.dct, pypocketfft.dst} + The transform to perform + """ + tmp = _asfarray(x) + + shape, axes = _init_nd_shape_and_axes(tmp, s, axes) + overwrite_x = overwrite_x or _datacopied(tmp, x) + + if len(axes) == 0: + return x + + tmp, copied = _fix_shape(tmp, shape, axes) + overwrite_x = overwrite_x or copied + + if not forward: + if type == 2: + type = 3 + elif type == 3: + type = 2 + + norm = _normalization(norm, forward) + workers = _workers(workers) + out = (tmp if overwrite_x else None) + + # For complex input, transform real and imaginary components separably + if np.iscomplexobj(x): + out = np.empty_like(tmp) if out is None else out + transform(tmp.real, type, axes, norm, out.real, workers) + transform(tmp.imag, type, axes, norm, out.imag, workers) + return out + + return transform(tmp, type, axes, norm, out, workers, orthogonalize) + + +dctn = functools.partial(_r2rn, True, pfft.dct) +dctn.__name__ = 'dctn' +idctn = functools.partial(_r2rn, False, pfft.dct) +idctn.__name__ = 'idctn' + +dstn = functools.partial(_r2rn, True, pfft.dst) +dstn.__name__ = 'dstn' +idstn = functools.partial(_r2rn, False, pfft.dst) +idstn.__name__ = 'idstn' diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..8960cace3e081368d00efbad77059f91cef4dbdd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_basic.py @@ -0,0 +1,1005 @@ +# Created by Pearu Peterson, September 2002 + +from numpy.testing import (assert_, assert_equal, assert_array_almost_equal, + assert_array_almost_equal_nulp, assert_array_less, + assert_allclose) +import pytest +from pytest import raises as assert_raises +from scipy.fft._pocketfft import (ifft, fft, fftn, ifftn, + rfft, irfft, rfftn, irfftn, + hfft, ihfft, hfftn, ihfftn) + +from numpy import (arange, array, asarray, zeros, dot, exp, pi, + swapaxes, cdouble) +import numpy as np +import numpy.fft +from numpy.random import rand + +# "large" composite numbers supported by FFT._PYPOCKETFFT +LARGE_COMPOSITE_SIZES = [ + 2**13, + 2**5 * 3**5, + 2**3 * 3**3 * 5**2, +] +SMALL_COMPOSITE_SIZES = [ + 2, + 2*3*5, + 2*2*3*3, +] +# prime +LARGE_PRIME_SIZES = [ + 2011 +] +SMALL_PRIME_SIZES = [ + 29 +] + + +def _assert_close_in_norm(x, y, rtol, size, rdt): + # helper function for testing + err_msg = f"size: {size} rdt: {rdt}" + assert_array_less(np.linalg.norm(x - y), rtol*np.linalg.norm(x), err_msg) + + +def random(size): + return rand(*size) + +def swap_byteorder(arr): + """Returns the same array with swapped byteorder""" + dtype = arr.dtype.newbyteorder('S') + return arr.astype(dtype) + +def direct_dft(x): + x = asarray(x) + n = len(x) + y = zeros(n, dtype=cdouble) + w = -arange(n)*(2j*pi/n) + for i in range(n): + y[i] = dot(exp(i*w), x) + return y + + +def direct_idft(x): + x = asarray(x) + n = len(x) + y = zeros(n, dtype=cdouble) + w = arange(n)*(2j*pi/n) + for i in range(n): + y[i] = dot(exp(i*w), x)/n + return y + + +def direct_dftn(x): + x = asarray(x) + for axis in range(x.ndim): + x = fft(x, axis=axis) + return x + + +def direct_idftn(x): + x = asarray(x) + for axis in range(x.ndim): + x = ifft(x, axis=axis) + return x + + +def direct_rdft(x): + x = asarray(x) + n = len(x) + w = -arange(n)*(2j*pi/n) + y = zeros(n//2+1, dtype=cdouble) + for i in range(n//2+1): + y[i] = dot(exp(i*w), x) + return y + + +def direct_irdft(x, n): + x = asarray(x) + x1 = zeros(n, dtype=cdouble) + for i in range(n//2+1): + x1[i] = x[i] + if i > 0 and 2*i < n: + x1[n-i] = np.conj(x[i]) + return direct_idft(x1).real + + +def direct_rdftn(x): + return fftn(rfft(x), axes=range(x.ndim - 1)) + + +class _TestFFTBase: + def setup_method(self): + self.cdt = None + self.rdt = None + np.random.seed(1234) + + def test_definition(self): + x = np.array([1,2,3,4+1j,1,2,3,4+2j], dtype=self.cdt) + y = fft(x) + assert_equal(y.dtype, self.cdt) + y1 = direct_dft(x) + assert_array_almost_equal(y,y1) + x = np.array([1,2,3,4+0j,5], dtype=self.cdt) + assert_array_almost_equal(fft(x),direct_dft(x)) + + def test_n_argument_real(self): + x1 = np.array([1,2,3,4], dtype=self.rdt) + x2 = np.array([1,2,3,4], dtype=self.rdt) + y = fft([x1,x2],n=4) + assert_equal(y.dtype, self.cdt) + assert_equal(y.shape,(2,4)) + assert_array_almost_equal(y[0],direct_dft(x1)) + assert_array_almost_equal(y[1],direct_dft(x2)) + + def _test_n_argument_complex(self): + x1 = np.array([1,2,3,4+1j], dtype=self.cdt) + x2 = np.array([1,2,3,4+1j], dtype=self.cdt) + y = fft([x1,x2],n=4) + assert_equal(y.dtype, self.cdt) + assert_equal(y.shape,(2,4)) + assert_array_almost_equal(y[0],direct_dft(x1)) + assert_array_almost_equal(y[1],direct_dft(x2)) + + def test_djbfft(self): + for i in range(2,14): + n = 2**i + x = np.arange(n) + y = fft(x.astype(complex)) + y2 = numpy.fft.fft(x) + assert_array_almost_equal(y,y2) + y = fft(x) + assert_array_almost_equal(y,y2) + + def test_invalid_sizes(self): + assert_raises(ValueError, fft, []) + assert_raises(ValueError, fft, [[1,1],[2,2]], -5) + + +class TestLongDoubleFFT(_TestFFTBase): + def setup_method(self): + self.cdt = np.clongdouble + self.rdt = np.longdouble + + +class TestDoubleFFT(_TestFFTBase): + def setup_method(self): + self.cdt = np.cdouble + self.rdt = np.float64 + + +class TestSingleFFT(_TestFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + + +class TestFloat16FFT: + + def test_1_argument_real(self): + x1 = np.array([1, 2, 3, 4], dtype=np.float16) + y = fft(x1, n=4) + assert_equal(y.dtype, np.complex64) + assert_equal(y.shape, (4, )) + assert_array_almost_equal(y, direct_dft(x1.astype(np.float32))) + + def test_n_argument_real(self): + x1 = np.array([1, 2, 3, 4], dtype=np.float16) + x2 = np.array([1, 2, 3, 4], dtype=np.float16) + y = fft([x1, x2], n=4) + assert_equal(y.dtype, np.complex64) + assert_equal(y.shape, (2, 4)) + assert_array_almost_equal(y[0], direct_dft(x1.astype(np.float32))) + assert_array_almost_equal(y[1], direct_dft(x2.astype(np.float32))) + + +class _TestIFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = np.array([1,2,3,4+1j,1,2,3,4+2j], self.cdt) + y = ifft(x) + y1 = direct_idft(x) + assert_equal(y.dtype, self.cdt) + assert_array_almost_equal(y,y1) + + x = np.array([1,2,3,4+0j,5], self.cdt) + assert_array_almost_equal(ifft(x),direct_idft(x)) + + def test_definition_real(self): + x = np.array([1,2,3,4,1,2,3,4], self.rdt) + y = ifft(x) + assert_equal(y.dtype, self.cdt) + y1 = direct_idft(x) + assert_array_almost_equal(y,y1) + + x = np.array([1,2,3,4,5], dtype=self.rdt) + assert_equal(y.dtype, self.cdt) + assert_array_almost_equal(ifft(x),direct_idft(x)) + + def test_djbfft(self): + for i in range(2,14): + n = 2**i + x = np.arange(n) + y = ifft(x.astype(self.cdt)) + y2 = numpy.fft.ifft(x.astype(self.cdt)) + assert_allclose(y,y2, rtol=self.rtol, atol=self.atol) + y = ifft(x) + assert_allclose(y,y2, rtol=self.rtol, atol=self.atol) + + def test_random_complex(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.cdt) + x = random([size]).astype(self.cdt) + 1j*x + y1 = ifft(fft(x)) + y2 = fft(ifft(x)) + assert_equal(y1.dtype, self.cdt) + assert_equal(y2.dtype, self.cdt) + assert_array_almost_equal(y1, x) + assert_array_almost_equal(y2, x) + + def test_random_real(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.rdt) + y1 = ifft(fft(x)) + y2 = fft(ifft(x)) + assert_equal(y1.dtype, self.cdt) + assert_equal(y2.dtype, self.cdt) + assert_array_almost_equal(y1, x) + assert_array_almost_equal(y2, x) + + def test_size_accuracy(self): + # Sanity check for the accuracy for prime and non-prime sized inputs + for size in LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES: + np.random.seed(1234) + x = np.random.rand(size).astype(self.rdt) + y = ifft(fft(x)) + _assert_close_in_norm(x, y, self.rtol, size, self.rdt) + y = fft(ifft(x)) + _assert_close_in_norm(x, y, self.rtol, size, self.rdt) + + x = (x + 1j*np.random.rand(size)).astype(self.cdt) + y = ifft(fft(x)) + _assert_close_in_norm(x, y, self.rtol, size, self.rdt) + y = fft(ifft(x)) + _assert_close_in_norm(x, y, self.rtol, size, self.rdt) + + def test_invalid_sizes(self): + assert_raises(ValueError, ifft, []) + assert_raises(ValueError, ifft, [[1,1],[2,2]], -5) + + +@pytest.mark.skipif(np.longdouble is np.float64, + reason="Long double is aliased to double") +class TestLongDoubleIFFT(_TestIFFTBase): + def setup_method(self): + self.cdt = np.clongdouble + self.rdt = np.longdouble + self.rtol = 1e-10 + self.atol = 1e-10 + + +class TestDoubleIFFT(_TestIFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + self.rtol = 1e-10 + self.atol = 1e-10 + + +class TestSingleIFFT(_TestIFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + self.rtol = 1e-5 + self.atol = 1e-4 + + +class _TestRFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + for t in [[1, 2, 3, 4, 1, 2, 3, 4], [1, 2, 3, 4, 1, 2, 3, 4, 5]]: + x = np.array(t, dtype=self.rdt) + y = rfft(x) + y1 = direct_rdft(x) + assert_array_almost_equal(y,y1) + assert_equal(y.dtype, self.cdt) + + def test_djbfft(self): + for i in range(2,14): + n = 2**i + x = np.arange(n) + y1 = np.fft.rfft(x) + y = rfft(x) + assert_array_almost_equal(y,y1) + + def test_invalid_sizes(self): + assert_raises(ValueError, rfft, []) + assert_raises(ValueError, rfft, [[1,1],[2,2]], -5) + + def test_complex_input(self): + x = np.zeros(10, dtype=self.cdt) + with assert_raises(TypeError, match="x must be a real sequence"): + rfft(x) + + # See gh-5790 + class MockSeries: + def __init__(self, data): + self.data = np.asarray(data) + + def __getattr__(self, item): + try: + return getattr(self.data, item) + except AttributeError as e: + raise AttributeError("'MockSeries' object " + f"has no attribute '{item}'") from e + + def test_non_ndarray_with_dtype(self): + x = np.array([1., 2., 3., 4., 5.]) + xs = _TestRFFTBase.MockSeries(x) + + expected = [1, 2, 3, 4, 5] + rfft(xs) + + # Data should not have been overwritten + assert_equal(x, expected) + assert_equal(xs.data, expected) + +@pytest.mark.skipif(np.longdouble is np.float64, + reason="Long double is aliased to double") +class TestRFFTLongDouble(_TestRFFTBase): + def setup_method(self): + self.cdt = np.clongdouble + self.rdt = np.longdouble + + +class TestRFFTDouble(_TestRFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + + +class TestRFFTSingle(_TestRFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + + +class _TestIRFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x1 = [1,2+3j,4+1j,1+2j,3+4j] + x1_1 = [1,2+3j,4+1j,2+3j,4,2-3j,4-1j,2-3j] + x1 = x1_1[:5] + x2_1 = [1,2+3j,4+1j,2+3j,4+5j,4-5j,2-3j,4-1j,2-3j] + x2 = x2_1[:5] + + def _test(x, xr): + y = irfft(np.array(x, dtype=self.cdt), n=len(xr)) + y1 = direct_irdft(x, len(xr)) + assert_equal(y.dtype, self.rdt) + assert_array_almost_equal(y,y1, decimal=self.ndec) + assert_array_almost_equal(y,ifft(xr), decimal=self.ndec) + + _test(x1, x1_1) + _test(x2, x2_1) + + def test_djbfft(self): + for i in range(2,14): + n = 2**i + x = np.arange(-1, n, 2) + 1j * np.arange(0, n+1, 2) + x[0] = 0 + if n % 2 == 0: + x[-1] = np.real(x[-1]) + y1 = np.fft.irfft(x) + y = irfft(x) + assert_array_almost_equal(y,y1) + + def test_random_real(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.rdt) + y1 = irfft(rfft(x), n=size) + y2 = rfft(irfft(x, n=(size*2-1))) + assert_equal(y1.dtype, self.rdt) + assert_equal(y2.dtype, self.cdt) + assert_array_almost_equal(y1, x, decimal=self.ndec, + err_msg="size=%d" % size) + assert_array_almost_equal(y2, x, decimal=self.ndec, + err_msg="size=%d" % size) + + def test_size_accuracy(self): + # Sanity check for the accuracy for prime and non-prime sized inputs + if self.rdt == np.float32: + rtol = 1e-5 + elif self.rdt == np.float64: + rtol = 1e-10 + + for size in LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES: + np.random.seed(1234) + x = np.random.rand(size).astype(self.rdt) + y = irfft(rfft(x), len(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + y = rfft(irfft(x, 2 * len(x) - 1)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + + def test_invalid_sizes(self): + assert_raises(ValueError, irfft, []) + assert_raises(ValueError, irfft, [[1,1],[2,2]], -5) + + +# self.ndec is bogus; we should have a assert_array_approx_equal for number of +# significant digits + +@pytest.mark.skipif(np.longdouble is np.float64, + reason="Long double is aliased to double") +class TestIRFFTLongDouble(_TestIRFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + self.ndec = 14 + + +class TestIRFFTDouble(_TestIRFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + self.ndec = 14 + + +class TestIRFFTSingle(_TestIRFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + self.ndec = 5 + + +class TestFftnSingle: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(np.array(x, np.float32)) + assert_(y.dtype == np.complex64, + msg="double precision output with single precision") + + y_r = np.array(fftn(x), np.complex64) + assert_array_almost_equal_nulp(y, y_r) + + @pytest.mark.parametrize('size', SMALL_COMPOSITE_SIZES + SMALL_PRIME_SIZES) + def test_size_accuracy_small(self, size): + x = np.random.rand(size, size) + 1j*np.random.rand(size, size) + y1 = fftn(x.real.astype(np.float32)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2000) + + @pytest.mark.parametrize('size', LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES) + def test_size_accuracy_large(self, size): + x = np.random.rand(size, 3) + 1j*np.random.rand(size, 3) + y1 = fftn(x.real.astype(np.float32)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2000) + + def test_definition_float16(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(np.array(x, np.float16)) + assert_equal(y.dtype, np.complex64) + y_r = np.array(fftn(x), np.complex64) + assert_array_almost_equal_nulp(y, y_r) + + @pytest.mark.parametrize('size', SMALL_COMPOSITE_SIZES + SMALL_PRIME_SIZES) + def test_float16_input_small(self, size): + x = np.random.rand(size, size) + 1j*np.random.rand(size, size) + y1 = fftn(x.real.astype(np.float16)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 5e5) + + @pytest.mark.parametrize('size', LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES) + def test_float16_input_large(self, size): + x = np.random.rand(size, 3) + 1j*np.random.rand(size, 3) + y1 = fftn(x.real.astype(np.float16)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2e6) + + +class TestFftn: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(x) + assert_array_almost_equal(y, direct_dftn(x)) + + x = random((20, 26)) + assert_array_almost_equal(fftn(x), direct_dftn(x)) + + x = random((5, 4, 3, 20)) + assert_array_almost_equal(fftn(x), direct_dftn(x)) + + def test_axes_argument(self): + # plane == ji_plane, x== kji_space + plane1 = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + plane2 = [[10, 11, 12], + [13, 14, 15], + [16, 17, 18]] + plane3 = [[19, 20, 21], + [22, 23, 24], + [25, 26, 27]] + ki_plane1 = [[1, 2, 3], + [10, 11, 12], + [19, 20, 21]] + ki_plane2 = [[4, 5, 6], + [13, 14, 15], + [22, 23, 24]] + ki_plane3 = [[7, 8, 9], + [16, 17, 18], + [25, 26, 27]] + jk_plane1 = [[1, 10, 19], + [4, 13, 22], + [7, 16, 25]] + jk_plane2 = [[2, 11, 20], + [5, 14, 23], + [8, 17, 26]] + jk_plane3 = [[3, 12, 21], + [6, 15, 24], + [9, 18, 27]] + kj_plane1 = [[1, 4, 7], + [10, 13, 16], [19, 22, 25]] + kj_plane2 = [[2, 5, 8], + [11, 14, 17], [20, 23, 26]] + kj_plane3 = [[3, 6, 9], + [12, 15, 18], [21, 24, 27]] + ij_plane1 = [[1, 4, 7], + [2, 5, 8], + [3, 6, 9]] + ij_plane2 = [[10, 13, 16], + [11, 14, 17], + [12, 15, 18]] + ij_plane3 = [[19, 22, 25], + [20, 23, 26], + [21, 24, 27]] + ik_plane1 = [[1, 10, 19], + [2, 11, 20], + [3, 12, 21]] + ik_plane2 = [[4, 13, 22], + [5, 14, 23], + [6, 15, 24]] + ik_plane3 = [[7, 16, 25], + [8, 17, 26], + [9, 18, 27]] + ijk_space = [jk_plane1, jk_plane2, jk_plane3] + ikj_space = [kj_plane1, kj_plane2, kj_plane3] + jik_space = [ik_plane1, ik_plane2, ik_plane3] + jki_space = [ki_plane1, ki_plane2, ki_plane3] + kij_space = [ij_plane1, ij_plane2, ij_plane3] + x = array([plane1, plane2, plane3]) + + assert_array_almost_equal(fftn(x), + fftn(x, axes=(-3, -2, -1))) # kji_space + assert_array_almost_equal(fftn(x), fftn(x, axes=(0, 1, 2))) + assert_array_almost_equal(fftn(x, axes=(0, 2)), fftn(x, axes=(0, -1))) + y = fftn(x, axes=(2, 1, 0)) # ijk_space + assert_array_almost_equal(swapaxes(y, -1, -3), fftn(ijk_space)) + y = fftn(x, axes=(2, 0, 1)) # ikj_space + assert_array_almost_equal(swapaxes(swapaxes(y, -1, -3), -1, -2), + fftn(ikj_space)) + y = fftn(x, axes=(1, 2, 0)) # jik_space + assert_array_almost_equal(swapaxes(swapaxes(y, -1, -3), -3, -2), + fftn(jik_space)) + y = fftn(x, axes=(1, 0, 2)) # jki_space + assert_array_almost_equal(swapaxes(y, -2, -3), fftn(jki_space)) + y = fftn(x, axes=(0, 2, 1)) # kij_space + assert_array_almost_equal(swapaxes(y, -2, -1), fftn(kij_space)) + + y = fftn(x, axes=(-2, -1)) # ji_plane + assert_array_almost_equal(fftn(plane1), y[0]) + assert_array_almost_equal(fftn(plane2), y[1]) + assert_array_almost_equal(fftn(plane3), y[2]) + + y = fftn(x, axes=(1, 2)) # ji_plane + assert_array_almost_equal(fftn(plane1), y[0]) + assert_array_almost_equal(fftn(plane2), y[1]) + assert_array_almost_equal(fftn(plane3), y[2]) + + y = fftn(x, axes=(-3, -2)) # kj_plane + assert_array_almost_equal(fftn(x[:, :, 0]), y[:, :, 0]) + assert_array_almost_equal(fftn(x[:, :, 1]), y[:, :, 1]) + assert_array_almost_equal(fftn(x[:, :, 2]), y[:, :, 2]) + + y = fftn(x, axes=(-3, -1)) # ki_plane + assert_array_almost_equal(fftn(x[:, 0, :]), y[:, 0, :]) + assert_array_almost_equal(fftn(x[:, 1, :]), y[:, 1, :]) + assert_array_almost_equal(fftn(x[:, 2, :]), y[:, 2, :]) + + y = fftn(x, axes=(-1, -2)) # ij_plane + assert_array_almost_equal(fftn(ij_plane1), swapaxes(y[0], -2, -1)) + assert_array_almost_equal(fftn(ij_plane2), swapaxes(y[1], -2, -1)) + assert_array_almost_equal(fftn(ij_plane3), swapaxes(y[2], -2, -1)) + + y = fftn(x, axes=(-1, -3)) # ik_plane + assert_array_almost_equal(fftn(ik_plane1), + swapaxes(y[:, 0, :], -1, -2)) + assert_array_almost_equal(fftn(ik_plane2), + swapaxes(y[:, 1, :], -1, -2)) + assert_array_almost_equal(fftn(ik_plane3), + swapaxes(y[:, 2, :], -1, -2)) + + y = fftn(x, axes=(-2, -3)) # jk_plane + assert_array_almost_equal(fftn(jk_plane1), + swapaxes(y[:, :, 0], -1, -2)) + assert_array_almost_equal(fftn(jk_plane2), + swapaxes(y[:, :, 1], -1, -2)) + assert_array_almost_equal(fftn(jk_plane3), + swapaxes(y[:, :, 2], -1, -2)) + + y = fftn(x, axes=(-1,)) # i_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[i, j, :]), y[i, j, :]) + y = fftn(x, axes=(-2,)) # j_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[i, :, j]), y[i, :, j]) + y = fftn(x, axes=(0,)) # k_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[:, i, j]), y[:, i, j]) + + y = fftn(x, axes=()) # point + assert_array_almost_equal(y, x) + + def test_shape_argument(self): + small_x = [[1, 2, 3], + [4, 5, 6]] + large_x1 = [[1, 2, 3, 0], + [4, 5, 6, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]] + + y = fftn(small_x, s=(4, 4)) + assert_array_almost_equal(y, fftn(large_x1)) + + y = fftn(small_x, s=(3, 4)) + assert_array_almost_equal(y, fftn(large_x1[:-1])) + + def test_shape_axes_argument(self): + small_x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + large_x1 = array([[1, 2, 3, 0], + [4, 5, 6, 0], + [7, 8, 9, 0], + [0, 0, 0, 0]]) + y = fftn(small_x, s=(4, 4), axes=(-2, -1)) + assert_array_almost_equal(y, fftn(large_x1)) + y = fftn(small_x, s=(4, 4), axes=(-1, -2)) + + assert_array_almost_equal(y, swapaxes( + fftn(swapaxes(large_x1, -1, -2)), -1, -2)) + + def test_shape_axes_argument2(self): + # Change shape of the last axis + x = numpy.random.random((10, 5, 3, 7)) + y = fftn(x, axes=(-1,), s=(8,)) + assert_array_almost_equal(y, fft(x, axis=-1, n=8)) + + # Change shape of an arbitrary axis which is not the last one + x = numpy.random.random((10, 5, 3, 7)) + y = fftn(x, axes=(-2,), s=(8,)) + assert_array_almost_equal(y, fft(x, axis=-2, n=8)) + + # Change shape of axes: cf #244, where shape and axes were mixed up + x = numpy.random.random((4, 4, 2)) + y = fftn(x, axes=(-3, -2), s=(8, 8)) + assert_array_almost_equal(y, + numpy.fft.fftn(x, axes=(-3, -2), s=(8, 8))) + + def test_shape_argument_more(self): + x = zeros((4, 4, 2)) + with assert_raises(ValueError, + match="shape requires more axes than are present"): + fftn(x, s=(8, 8, 2, 1)) + + def test_invalid_sizes(self): + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[1, 0\]\) specified"): + fftn([[]]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[4, -3\]\) specified"): + fftn([[1, 1], [2, 2]], (4, -3)) + + def test_no_axes(self): + x = numpy.random.random((2,2,2)) + assert_allclose(fftn(x, axes=[]), x, atol=1e-7) + + def test_regression_244(self): + """FFT returns wrong result with axes parameter.""" + # fftn (and hence fft2) used to break when both axes and shape were used + x = numpy.ones((4, 4, 2)) + y = fftn(x, s=(8, 8), axes=(-3, -2)) + y_r = numpy.fft.fftn(x, s=(8, 8), axes=(-3, -2)) + assert_allclose(y, y_r) + + +class TestIfftn: + dtype = None + cdtype = None + + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.parametrize('dtype,cdtype,maxnlp', + [(np.float64, np.complex128, 2000), + (np.float32, np.complex64, 3500)]) + def test_definition(self, dtype, cdtype, maxnlp): + x = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], dtype=dtype) + y = ifftn(x) + assert_equal(y.dtype, cdtype) + assert_array_almost_equal_nulp(y, direct_idftn(x), maxnlp) + + x = random((20, 26)) + assert_array_almost_equal_nulp(ifftn(x), direct_idftn(x), maxnlp) + + x = random((5, 4, 3, 20)) + assert_array_almost_equal_nulp(ifftn(x), direct_idftn(x), maxnlp) + + @pytest.mark.parametrize('maxnlp', [2000, 3500]) + @pytest.mark.parametrize('size', [1, 2, 51, 32, 64, 92]) + def test_random_complex(self, maxnlp, size): + x = random([size, size]) + 1j*random([size, size]) + assert_array_almost_equal_nulp(ifftn(fftn(x)), x, maxnlp) + assert_array_almost_equal_nulp(fftn(ifftn(x)), x, maxnlp) + + def test_invalid_sizes(self): + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[1, 0\]\) specified"): + ifftn([[]]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[4, -3\]\) specified"): + ifftn([[1, 1], [2, 2]], (4, -3)) + + def test_no_axes(self): + x = numpy.random.random((2,2,2)) + assert_allclose(ifftn(x, axes=[]), x, atol=1e-7) + +class TestRfftn: + dtype = None + cdtype = None + + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.parametrize('dtype,cdtype,maxnlp', + [(np.float64, np.complex128, 2000), + (np.float32, np.complex64, 3500)]) + def test_definition(self, dtype, cdtype, maxnlp): + x = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], dtype=dtype) + y = rfftn(x) + assert_equal(y.dtype, cdtype) + assert_array_almost_equal_nulp(y, direct_rdftn(x), maxnlp) + + x = random((20, 26)) + assert_array_almost_equal_nulp(rfftn(x), direct_rdftn(x), maxnlp) + + x = random((5, 4, 3, 20)) + assert_array_almost_equal_nulp(rfftn(x), direct_rdftn(x), maxnlp) + + @pytest.mark.parametrize('size', [1, 2, 51, 32, 64, 92]) + def test_random(self, size): + x = random([size, size]) + assert_allclose(irfftn(rfftn(x), x.shape), x, atol=1e-10) + + @pytest.mark.parametrize('func', [rfftn, irfftn]) + def test_invalid_sizes(self, func): + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[1, 0\]\) specified"): + func([[]]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[4, -3\]\) specified"): + func([[1, 1], [2, 2]], (4, -3)) + + @pytest.mark.parametrize('func', [rfftn, irfftn]) + def test_no_axes(self, func): + with assert_raises(ValueError, + match="at least 1 axis must be transformed"): + func([], axes=[]) + + def test_complex_input(self): + with assert_raises(TypeError, match="x must be a real sequence"): + rfftn(np.zeros(10, dtype=np.complex64)) + + +class FakeArray: + def __init__(self, data): + self._data = data + self.__array_interface__ = data.__array_interface__ + + +class FakeArray2: + def __init__(self, data): + self._data = data + + def __array__(self, dtype=None, copy=None): + return self._data + +# TODO: Is this test actually valuable? The behavior it's testing shouldn't be +# relied upon by users except for overwrite_x = False +class TestOverwrite: + """Check input overwrite behavior of the FFT functions.""" + + real_dtypes = [np.float32, np.float64, np.longdouble] + dtypes = real_dtypes + [np.complex64, np.complex128, np.clongdouble] + fftsizes = [8, 16, 32] + + def _check(self, x, routine, fftsize, axis, overwrite_x, should_overwrite): + x2 = x.copy() + for fake in [lambda x: x, FakeArray, FakeArray2]: + routine(fake(x2), fftsize, axis, overwrite_x=overwrite_x) + + sig = "{}({}{!r}, {!r}, axis={!r}, overwrite_x={!r})".format( + routine.__name__, x.dtype, x.shape, fftsize, axis, overwrite_x) + if not should_overwrite: + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + def _check_1d(self, routine, dtype, shape, axis, overwritable_dtypes, + fftsize, overwrite_x): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + + should_overwrite = (overwrite_x + and dtype in overwritable_dtypes + and fftsize <= shape[axis]) + self._check(data, routine, fftsize, axis, + overwrite_x=overwrite_x, + should_overwrite=should_overwrite) + + @pytest.mark.parametrize('dtype', dtypes) + @pytest.mark.parametrize('fftsize', fftsizes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), -1), + ((16, 2), 0), + ((2, 16), 1)]) + def test_fft_ifft(self, dtype, fftsize, overwrite_x, shape, axes): + overwritable = (np.clongdouble, np.complex128, np.complex64) + self._check_1d(fft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + self._check_1d(ifft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + + @pytest.mark.parametrize('dtype', real_dtypes) + @pytest.mark.parametrize('fftsize', fftsizes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), -1), + ((16, 2), 0), + ((2, 16), 1)]) + def test_rfft_irfft(self, dtype, fftsize, overwrite_x, shape, axes): + overwritable = self.real_dtypes + self._check_1d(irfft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + self._check_1d(rfft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + + def _check_nd_one(self, routine, dtype, shape, axes, overwritable_dtypes, + overwrite_x): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + + def fftshape_iter(shp): + if len(shp) <= 0: + yield () + else: + for j in (shp[0]//2, shp[0], shp[0]*2): + for rest in fftshape_iter(shp[1:]): + yield (j,) + rest + + def part_shape(shape, axes): + if axes is None: + return shape + else: + return tuple(np.take(shape, axes)) + + def should_overwrite(data, shape, axes): + s = part_shape(data.shape, axes) + return (overwrite_x and + np.prod(shape) <= np.prod(s) + and dtype in overwritable_dtypes) + + for fftshape in fftshape_iter(part_shape(shape, axes)): + self._check(data, routine, fftshape, axes, + overwrite_x=overwrite_x, + should_overwrite=should_overwrite(data, fftshape, axes)) + if data.ndim > 1: + # check fortran order + self._check(data.T, routine, fftshape, axes, + overwrite_x=overwrite_x, + should_overwrite=should_overwrite( + data.T, fftshape, axes)) + + @pytest.mark.parametrize('dtype', dtypes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), None), + ((16,), (0,)), + ((16, 2), (0,)), + ((2, 16), (1,)), + ((8, 16), None), + ((8, 16), (0, 1)), + ((8, 16, 2), (0, 1)), + ((8, 16, 2), (1, 2)), + ((8, 16, 2), (0,)), + ((8, 16, 2), (1,)), + ((8, 16, 2), (2,)), + ((8, 16, 2), None), + ((8, 16, 2), (0, 1, 2))]) + def test_fftn_ifftn(self, dtype, overwrite_x, shape, axes): + overwritable = (np.clongdouble, np.complex128, np.complex64) + self._check_nd_one(fftn, dtype, shape, axes, overwritable, + overwrite_x) + self._check_nd_one(ifftn, dtype, shape, axes, overwritable, + overwrite_x) + + +@pytest.mark.parametrize('func', [fft, ifft, fftn, ifftn, + rfft, irfft, rfftn, irfftn]) +def test_invalid_norm(func): + x = np.arange(10, dtype=float) + with assert_raises(ValueError, + match='Invalid norm value \'o\', should be' + ' "backward", "ortho" or "forward"'): + func(x, norm='o') + + +@pytest.mark.parametrize('func', [fft, ifft, fftn, ifftn, + irfft, irfftn, hfft, hfftn]) +def test_swapped_byte_order_complex(func): + rng = np.random.RandomState(1234) + x = rng.rand(10) + 1j * rng.rand(10) + assert_allclose(func(swap_byteorder(x)), func(x)) + + +@pytest.mark.parametrize('func', [ihfft, ihfftn, rfft, rfftn]) +def test_swapped_byte_order_real(func): + rng = np.random.RandomState(1234) + x = rng.rand(10) + assert_allclose(func(swap_byteorder(x)), func(x)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_real_transforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_real_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..2cb47f40c6bc0a251a79bb3660fcc9a0f1b10725 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_pocketfft/tests/test_real_transforms.py @@ -0,0 +1,494 @@ +from os.path import join, dirname +from typing import Callable, Union + +import numpy as np +from numpy.testing import ( + assert_array_almost_equal, assert_equal, assert_allclose) +import pytest +from pytest import raises as assert_raises + +from scipy.fft._pocketfft.realtransforms import ( + dct, idct, dst, idst, dctn, idctn, dstn, idstn) + +fftpack_test_dir = join(dirname(__file__), '..', '..', '..', 'fftpack', 'tests') + +MDATA_COUNT = 8 +FFTWDATA_COUNT = 14 + +def is_longdouble_binary_compatible(): + try: + one = np.frombuffer( + b'\x00\x00\x00\x00\x00\x00\x00\x80\xff\x3f\x00\x00\x00\x00\x00\x00', + dtype=' decimal +dec_map: DecMapType = { + # DCT + (dct, np.float64, 1): 13, + (dct, np.float32, 1): 6, + + (dct, np.float64, 2): 14, + (dct, np.float32, 2): 5, + + (dct, np.float64, 3): 14, + (dct, np.float32, 3): 5, + + (dct, np.float64, 4): 13, + (dct, np.float32, 4): 6, + + # IDCT + (idct, np.float64, 1): 14, + (idct, np.float32, 1): 6, + + (idct, np.float64, 2): 14, + (idct, np.float32, 2): 5, + + (idct, np.float64, 3): 14, + (idct, np.float32, 3): 5, + + (idct, np.float64, 4): 14, + (idct, np.float32, 4): 6, + + # DST + (dst, np.float64, 1): 13, + (dst, np.float32, 1): 6, + + (dst, np.float64, 2): 14, + (dst, np.float32, 2): 6, + + (dst, np.float64, 3): 14, + (dst, np.float32, 3): 7, + + (dst, np.float64, 4): 13, + (dst, np.float32, 4): 5, + + # IDST + (idst, np.float64, 1): 14, + (idst, np.float32, 1): 6, + + (idst, np.float64, 2): 14, + (idst, np.float32, 2): 6, + + (idst, np.float64, 3): 14, + (idst, np.float32, 3): 6, + + (idst, np.float64, 4): 14, + (idst, np.float32, 4): 6, +} + +for k,v in dec_map.copy().items(): + if k[1] == np.float64: + dec_map[(k[0], np.longdouble, k[2])] = v + elif k[1] == np.float32: + dec_map[(k[0], int, k[2])] = v + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +@pytest.mark.parametrize('type', [1, 2, 3, 4]) +class TestDCT: + def test_definition(self, rdt, type, fftwdata_size, reference_data): + x, yr, dt = fftw_dct_ref(type, fftwdata_size, rdt, reference_data) + y = dct(x, type=type) + assert_equal(y.dtype, dt) + dec = dec_map[(dct, rdt, type)] + assert_allclose(y, yr, rtol=0., atol=np.max(yr)*10**(-dec)) + + @pytest.mark.parametrize('size', [7, 8, 9, 16, 32, 64]) + def test_axis(self, rdt, type, size): + nt = 2 + dec = dec_map[(dct, rdt, type)] + x = np.random.randn(nt, size) + y = dct(x, type=type) + for j in range(nt): + assert_array_almost_equal(y[j], dct(x[j], type=type), + decimal=dec) + + x = x.T + y = dct(x, axis=0, type=type) + for j in range(nt): + assert_array_almost_equal(y[:,j], dct(x[:,j], type=type), + decimal=dec) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dct1_definition_ortho(rdt, mdata_x): + # Test orthornomal mode. + dec = dec_map[(dct, rdt, 1)] + x = np.array(mdata_x, dtype=rdt) + dt = np.result_type(np.float32, rdt) + y = dct(x, norm='ortho', type=1) + y2 = naive_dct1(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_allclose(y, y2, rtol=0., atol=np.max(y2)*10**(-dec)) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dct2_definition_matlab(mdata_xy, rdt): + # Test correspondence with matlab (orthornomal mode). + dt = np.result_type(np.float32, rdt) + x = np.array(mdata_xy[0], dtype=dt) + + yr = mdata_xy[1] + y = dct(x, norm="ortho", type=2) + dec = dec_map[(dct, rdt, 2)] + assert_equal(y.dtype, dt) + assert_array_almost_equal(y, yr, decimal=dec) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dct3_definition_ortho(mdata_x, rdt): + # Test orthornomal mode. + x = np.array(mdata_x, dtype=rdt) + dt = np.result_type(np.float32, rdt) + y = dct(x, norm='ortho', type=2) + xi = dct(y, norm="ortho", type=3) + dec = dec_map[(dct, rdt, 3)] + assert_equal(xi.dtype, dt) + assert_array_almost_equal(xi, x, decimal=dec) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dct4_definition_ortho(mdata_x, rdt): + # Test orthornomal mode. + x = np.array(mdata_x, dtype=rdt) + dt = np.result_type(np.float32, rdt) + y = dct(x, norm='ortho', type=4) + y2 = naive_dct4(x, norm='ortho') + dec = dec_map[(dct, rdt, 4)] + assert_equal(y.dtype, dt) + assert_allclose(y, y2, rtol=0., atol=np.max(y2)*10**(-dec)) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +@pytest.mark.parametrize('type', [1, 2, 3, 4]) +def test_idct_definition(fftwdata_size, rdt, type, reference_data): + xr, yr, dt = fftw_dct_ref(type, fftwdata_size, rdt, reference_data) + x = idct(yr, type=type) + dec = dec_map[(idct, rdt, type)] + assert_equal(x.dtype, dt) + assert_allclose(x, xr, rtol=0., atol=np.max(xr)*10**(-dec)) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +@pytest.mark.parametrize('type', [1, 2, 3, 4]) +def test_definition(fftwdata_size, rdt, type, reference_data): + xr, yr, dt = fftw_dst_ref(type, fftwdata_size, rdt, reference_data) + y = dst(xr, type=type) + dec = dec_map[(dst, rdt, type)] + assert_equal(y.dtype, dt) + assert_allclose(y, yr, rtol=0., atol=np.max(yr)*10**(-dec)) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dst1_definition_ortho(rdt, mdata_x): + # Test orthornomal mode. + dec = dec_map[(dst, rdt, 1)] + x = np.array(mdata_x, dtype=rdt) + dt = np.result_type(np.float32, rdt) + y = dst(x, norm='ortho', type=1) + y2 = naive_dst1(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_allclose(y, y2, rtol=0., atol=np.max(y2)*10**(-dec)) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +def test_dst4_definition_ortho(rdt, mdata_x): + # Test orthornomal mode. + dec = dec_map[(dst, rdt, 4)] + x = np.array(mdata_x, dtype=rdt) + dt = np.result_type(np.float32, rdt) + y = dst(x, norm='ortho', type=4) + y2 = naive_dst4(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_array_almost_equal(y, y2, decimal=dec) + + +@pytest.mark.parametrize('rdt', [np.longdouble, np.float64, np.float32, int]) +@pytest.mark.parametrize('type', [1, 2, 3, 4]) +def test_idst_definition(fftwdata_size, rdt, type, reference_data): + xr, yr, dt = fftw_dst_ref(type, fftwdata_size, rdt, reference_data) + x = idst(yr, type=type) + dec = dec_map[(idst, rdt, type)] + assert_equal(x.dtype, dt) + assert_allclose(x, xr, rtol=0., atol=np.max(xr)*10**(-dec)) + + +@pytest.mark.parametrize('routine', [dct, dst, idct, idst]) +@pytest.mark.parametrize('dtype', [np.float32, np.float64, np.longdouble]) +@pytest.mark.parametrize('shape, axis', [ + ((16,), -1), ((16, 2), 0), ((2, 16), 1) +]) +@pytest.mark.parametrize('type', [1, 2, 3, 4]) +@pytest.mark.parametrize('overwrite_x', [True, False]) +@pytest.mark.parametrize('norm', [None, 'ortho']) +def test_overwrite(routine, dtype, shape, axis, type, norm, overwrite_x): + # Check input overwrite behavior + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + x = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + x = np.random.randn(*shape) + x = x.astype(dtype) + x2 = x.copy() + routine(x2, type, None, axis, norm, overwrite_x=overwrite_x) + + sig = "{}({}{!r}, {!r}, axis={!r}, overwrite_x={!r})".format( + routine.__name__, x.dtype, x.shape, None, axis, overwrite_x) + if not overwrite_x: + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + +class Test_DCTN_IDCTN: + dec = 14 + dct_type = [1, 2, 3, 4] + norms = [None, 'backward', 'ortho', 'forward'] + rstate = np.random.RandomState(1234) + shape = (32, 16) + data = rstate.randn(*shape) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + @pytest.mark.parametrize('axes', [None, + 1, (1,), [1], + 0, (0,), [0], + (0, 1), [0, 1], + (-2, -1), [-2, -1]]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', ['ortho']) + def test_axes_round_trip(self, fforward, finverse, axes, dct_type, norm): + tmp = fforward(self.data, type=dct_type, axes=axes, norm=norm) + tmp = finverse(tmp, type=dct_type, axes=axes, norm=norm) + assert_array_almost_equal(self.data, tmp, decimal=12) + + @pytest.mark.parametrize('funcn,func', [(dctn, dct), (dstn, dst)]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', norms) + def test_dctn_vs_2d_reference(self, funcn, func, dct_type, norm): + y1 = funcn(self.data, type=dct_type, axes=None, norm=norm) + y2 = ref_2d(func, self.data, type=dct_type, norm=norm) + assert_array_almost_equal(y1, y2, decimal=11) + + @pytest.mark.parametrize('funcn,func', [(idctn, idct), (idstn, idst)]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', norms) + def test_idctn_vs_2d_reference(self, funcn, func, dct_type, norm): + fdata = dctn(self.data, type=dct_type, norm=norm) + y1 = funcn(fdata, type=dct_type, norm=norm) + y2 = ref_2d(func, fdata, type=dct_type, norm=norm) + assert_array_almost_equal(y1, y2, decimal=11) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + def test_axes_and_shape(self, fforward, finverse): + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fforward(self.data, s=self.data.shape[0], axes=(0, 1)) + + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fforward(self.data, s=self.data.shape, axes=0) + + @pytest.mark.parametrize('fforward', [dctn, dstn]) + def test_shape(self, fforward): + tmp = fforward(self.data, s=(128, 128), axes=None) + assert_equal(tmp.shape, (128, 128)) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + @pytest.mark.parametrize('axes', [1, (1,), [1], + 0, (0,), [0]]) + def test_shape_is_none_with_axes(self, fforward, finverse, axes): + tmp = fforward(self.data, s=None, axes=axes, norm='ortho') + tmp = finverse(tmp, s=None, axes=axes, norm='ortho') + assert_array_almost_equal(self.data, tmp, decimal=self.dec) + + +@pytest.mark.parametrize('func', [dct, dctn, idct, idctn, + dst, dstn, idst, idstn]) +def test_swapped_byte_order(func): + rng = np.random.RandomState(1234) + x = rng.rand(10) + swapped_dt = x.dtype.newbyteorder('S') + assert_allclose(func(x.astype(swapped_dt)), func(x)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms.py new file mode 100644 index 0000000000000000000000000000000000000000..1c7a3d683dd78d3227a7de88f5c47569d2f4e17f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms.py @@ -0,0 +1,693 @@ +from ._basic import _dispatch +from scipy._lib.uarray import Dispatchable +import numpy as np + +__all__ = ['dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn'] + + +@_dispatch +def dctn(x, type=2, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, *, orthogonalize=None): + """ + Return multidimensional Discrete Cosine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + s : int or array_like of ints or None, optional + The shape of the result. If both `s` and `axes` (see below) are None, + `s` is ``x.shape``; if `s` is None but `axes` is not None, then `s` is + ``numpy.take(x.shape, axes, axis=0)``. + If ``s[i] > x.shape[i]``, the ith dimension of the input is padded with zeros. + If ``s[i] < x.shape[i]``, the ith dimension of the input is truncated to length + ``s[i]``. + If any element of `s` is -1, the size of the corresponding dimension of + `x` is used. + axes : int or array_like of ints or None, optional + Axes over which the DCT is computed. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized DCT variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idctn : Inverse multidimensional DCT + + Notes + ----- + For full details of the DCT types and normalization modes, as well as + references, see `dct`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fft import dctn, idctn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idctn(dctn(y))) + True + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def idctn(x, type=2, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, orthogonalize=None): + """ + Return multidimensional Inverse Discrete Cosine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + s : int or array_like of ints or None, optional + The shape of the result. If both `s` and `axes` (see below) are + None, `s` is ``x.shape``; if `s` is None but `axes` is + not None, then `s` is ``numpy.take(x.shape, axes, axis=0)``. + If ``s[i] > x.shape[i]``, the ith dimension of the input is padded with zeros. + If ``s[i] < x.shape[i]``, the ith dimension of the input is truncated to length + ``s[i]``. + If any element of `s` is -1, the size of the corresponding dimension of + `x` is used. + axes : int or array_like of ints or None, optional + Axes over which the IDCT is computed. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized IDCT variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + dctn : multidimensional DCT + + Notes + ----- + For full details of the IDCT types and normalization modes, as well as + references, see `idct`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fft import dctn, idctn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idctn(dctn(y))) + True + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def dstn(x, type=2, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, orthogonalize=None): + """ + Return multidimensional Discrete Sine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + s : int or array_like of ints or None, optional + The shape of the result. If both `s` and `axes` (see below) are None, + `s` is ``x.shape``; if `s` is None but `axes` is not None, then `s` is + ``numpy.take(x.shape, axes, axis=0)``. + If ``s[i] > x.shape[i]``, the ith dimension of the input is padded with zeros. + If ``s[i] < x.shape[i]``, the ith dimension of the input is truncated to length + ``s[i]``. + If any element of `shape` is -1, the size of the corresponding dimension + of `x` is used. + axes : int or array_like of ints or None, optional + Axes over which the DST is computed. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized DST variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idstn : Inverse multidimensional DST + + Notes + ----- + For full details of the DST types and normalization modes, as well as + references, see `dst`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fft import dstn, idstn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idstn(dstn(y))) + True + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def idstn(x, type=2, s=None, axes=None, norm=None, overwrite_x=False, + workers=None, orthogonalize=None): + """ + Return multidimensional Inverse Discrete Sine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + s : int or array_like of ints or None, optional + The shape of the result. If both `s` and `axes` (see below) are None, + `s` is ``x.shape``; if `s` is None but `axes` is not None, then `s` is + ``numpy.take(x.shape, axes, axis=0)``. + If ``s[i] > x.shape[i]``, the ith dimension of the input is padded with zeros. + If ``s[i] < x.shape[i]``, the ith dimension of the input is truncated to length + ``s[i]``. + If any element of `s` is -1, the size of the corresponding dimension of + `x` is used. + axes : int or array_like of ints or None, optional + Axes over which the IDST is computed. If not given, the last ``len(s)`` + axes are used, or all axes if `s` is also not specified. + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized IDST variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + dstn : multidimensional DST + + Notes + ----- + For full details of the IDST types and normalization modes, as well as + references, see `idst`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fft import dstn, idstn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idstn(dstn(y))) + True + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def dct(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, + orthogonalize=None): + r"""Return the Discrete Cosine Transform of arbitrary type sequence x. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the dct is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized DCT variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idct : Inverse DCT + + Notes + ----- + For a single dimension array ``x``, ``dct(x, norm='ortho')`` is equal to + MATLAB ``dct(x)``. + + .. warning:: For ``type in {1, 2, 3}``, ``norm="ortho"`` breaks the direct + correspondence with the direct Fourier transform. To recover + it you must specify ``orthogonalize=False``. + + For ``norm="ortho"`` both the `dct` and `idct` are scaled by the same + overall factor in both directions. By default, the transform is also + orthogonalized which for types 1, 2 and 3 means the transform definition is + modified to give orthogonality of the DCT matrix (see below). + + For ``norm="backward"``, there is no scaling on `dct` and the `idct` is + scaled by ``1/N`` where ``N`` is the "logical" size of the DCT. For + ``norm="forward"`` the ``1/N`` normalization is applied to the forward + `dct` instead and the `idct` is unnormalized. + + There are, theoretically, 8 types of the DCT, only the first 4 types are + implemented in SciPy.'The' DCT generally refers to DCT type 2, and 'the' + Inverse DCT generally refers to DCT type 3. + + **Type I** + + There are several definitions of the DCT-I; we use the following + (for ``norm="backward"``) + + .. math:: + + y_k = x_0 + (-1)^k x_{N-1} + 2 \sum_{n=1}^{N-2} x_n \cos\left( + \frac{\pi k n}{N-1} \right) + + If ``orthogonalize=True``, ``x[0]`` and ``x[N-1]`` are multiplied by a + scaling factor of :math:`\sqrt{2}`, and ``y[0]`` and ``y[N-1]`` are divided + by :math:`\sqrt{2}`. When combined with ``norm="ortho"``, this makes the + corresponding matrix of coefficients orthonormal (``O @ O.T = np.eye(N)``). + + .. note:: + The DCT-I is only supported for input size > 1. + + **Type II** + + There are several definitions of the DCT-II; we use the following + (for ``norm="backward"``) + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \cos\left(\frac{\pi k(2n+1)}{2N} \right) + + If ``orthogonalize=True``, ``y[0]`` is divided by :math:`\sqrt{2}` which, + when combined with ``norm="ortho"``, makes the corresponding matrix of + coefficients orthonormal (``O @ O.T = np.eye(N)``). + + **Type III** + + There are several definitions, we use the following (for + ``norm="backward"``) + + .. math:: + + y_k = x_0 + 2 \sum_{n=1}^{N-1} x_n \cos\left(\frac{\pi(2k+1)n}{2N}\right) + + If ``orthogonalize=True``, ``x[0]`` terms are multiplied by + :math:`\sqrt{2}` which, when combined with ``norm="ortho"``, makes the + corresponding matrix of coefficients orthonormal (``O @ O.T = np.eye(N)``). + + The (unnormalized) DCT-III is the inverse of the (unnormalized) DCT-II, up + to a factor `2N`. The orthonormalized DCT-III is exactly the inverse of + the orthonormalized DCT-II. + + **Type IV** + + There are several definitions of the DCT-IV; we use the following + (for ``norm="backward"``) + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \cos\left(\frac{\pi(2k+1)(2n+1)}{4N} \right) + + ``orthogonalize`` has no effect here, as the DCT-IV matrix is already + orthogonal up to a scale factor of ``2N``. + + References + ---------- + .. [1] 'A Fast Cosine Transform in One and Two Dimensions', by J. + Makhoul, `IEEE Transactions on acoustics, speech and signal + processing` vol. 28(1), pp. 27-34, + :doi:`10.1109/TASSP.1980.1163351` (1980). + .. [2] Wikipedia, "Discrete cosine transform", + https://en.wikipedia.org/wiki/Discrete_cosine_transform + + Examples + -------- + The Type 1 DCT is equivalent to the FFT (though faster) for real, + even-symmetrical inputs. The output is also real and even-symmetrical. + Half of the FFT input is used to generate half of the FFT output: + + >>> from scipy.fft import fft, dct + >>> import numpy as np + >>> fft(np.array([4., 3., 5., 10., 5., 3.])).real + array([ 30., -8., 6., -2., 6., -8.]) + >>> dct(np.array([4., 3., 5., 10.]), 1) + array([ 30., -8., 6., -2.]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def idct(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False, + workers=None, orthogonalize=None): + """ + Return the Inverse Discrete Cosine Transform of an arbitrary type sequence. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the idct is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized IDCT variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + idct : ndarray of real + The transformed input array. + + See Also + -------- + dct : Forward DCT + + Notes + ----- + For a single dimension array `x`, ``idct(x, norm='ortho')`` is equal to + MATLAB ``idct(x)``. + + .. warning:: For ``type in {1, 2, 3}``, ``norm="ortho"`` breaks the direct + correspondence with the inverse direct Fourier transform. To + recover it you must specify ``orthogonalize=False``. + + For ``norm="ortho"`` both the `dct` and `idct` are scaled by the same + overall factor in both directions. By default, the transform is also + orthogonalized which for types 1, 2 and 3 means the transform definition is + modified to give orthogonality of the IDCT matrix (see `dct` for the full + definitions). + + 'The' IDCT is the IDCT-II, which is the same as the normalized DCT-III. + + The IDCT is equivalent to a normal DCT except for the normalization and + type. DCT type 1 and 4 are their own inverse and DCTs 2 and 3 are each + other's inverses. + + Examples + -------- + The Type 1 DCT is equivalent to the DFT for real, even-symmetrical + inputs. The output is also real and even-symmetrical. Half of the IFFT + input is used to generate half of the IFFT output: + + >>> from scipy.fft import ifft, idct + >>> import numpy as np + >>> ifft(np.array([ 30., -8., 6., -2., 6., -8.])).real + array([ 4., 3., 5., 10., 5., 3.]) + >>> idct(np.array([ 30., -8., 6., -2.]), 1) + array([ 4., 3., 5., 10.]) + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def dst(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, + orthogonalize=None): + r""" + Return the Discrete Sine Transform of arbitrary type sequence x. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the dst is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized DST variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + dst : ndarray of reals + The transformed input array. + + See Also + -------- + idst : Inverse DST + + Notes + ----- + .. warning:: For ``type in {2, 3}``, ``norm="ortho"`` breaks the direct + correspondence with the direct Fourier transform. To recover + it you must specify ``orthogonalize=False``. + + For ``norm="ortho"`` both the `dst` and `idst` are scaled by the same + overall factor in both directions. By default, the transform is also + orthogonalized which for types 2 and 3 means the transform definition is + modified to give orthogonality of the DST matrix (see below). + + For ``norm="backward"``, there is no scaling on the `dst` and the `idst` is + scaled by ``1/N`` where ``N`` is the "logical" size of the DST. + + There are, theoretically, 8 types of the DST for different combinations of + even/odd boundary conditions and boundary off sets [1]_, only the first + 4 types are implemented in SciPy. + + **Type I** + + There are several definitions of the DST-I; we use the following for + ``norm="backward"``. DST-I assumes the input is odd around :math:`n=-1` and + :math:`n=N`. + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(k+1)(n+1)}{N+1}\right) + + Note that the DST-I is only supported for input size > 1. + The (unnormalized) DST-I is its own inverse, up to a factor :math:`2(N+1)`. + The orthonormalized DST-I is exactly its own inverse. + + ``orthogonalize`` has no effect here, as the DST-I matrix is already + orthogonal up to a scale factor of ``2N``. + + **Type II** + + There are several definitions of the DST-II; we use the following for + ``norm="backward"``. DST-II assumes the input is odd around :math:`n=-1/2` and + :math:`n=N-1/2`; the output is odd around :math:`k=-1` and even around :math:`k=N-1` + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(k+1)(2n+1)}{2N}\right) + + If ``orthogonalize=True``, ``y[-1]`` is divided :math:`\sqrt{2}` which, when + combined with ``norm="ortho"``, makes the corresponding matrix of + coefficients orthonormal (``O @ O.T = np.eye(N)``). + + **Type III** + + There are several definitions of the DST-III, we use the following (for + ``norm="backward"``). DST-III assumes the input is odd around :math:`n=-1` and + even around :math:`n=N-1` + + .. math:: + + y_k = (-1)^k x_{N-1} + 2 \sum_{n=0}^{N-2} x_n \sin\left( + \frac{\pi(2k+1)(n+1)}{2N}\right) + + If ``orthogonalize=True``, ``x[-1]`` is multiplied by :math:`\sqrt{2}` + which, when combined with ``norm="ortho"``, makes the corresponding matrix + of coefficients orthonormal (``O @ O.T = np.eye(N)``). + + The (unnormalized) DST-III is the inverse of the (unnormalized) DST-II, up + to a factor :math:`2N`. The orthonormalized DST-III is exactly the inverse of the + orthonormalized DST-II. + + **Type IV** + + There are several definitions of the DST-IV, we use the following (for + ``norm="backward"``). DST-IV assumes the input is odd around :math:`n=-0.5` and + even around :math:`n=N-0.5` + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(2k+1)(2n+1)}{4N}\right) + + ``orthogonalize`` has no effect here, as the DST-IV matrix is already + orthogonal up to a scale factor of ``2N``. + + The (unnormalized) DST-IV is its own inverse, up to a factor :math:`2N`. The + orthonormalized DST-IV is exactly its own inverse. + + References + ---------- + .. [1] Wikipedia, "Discrete sine transform", + https://en.wikipedia.org/wiki/Discrete_sine_transform + + """ + return (Dispatchable(x, np.ndarray),) + + +@_dispatch +def idst(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False, + workers=None, orthogonalize=None): + """ + Return the Inverse Discrete Sine Transform of an arbitrary type sequence. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the idst is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {"backward", "ortho", "forward"}, optional + Normalization mode (see Notes). Default is "backward". + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + workers : int, optional + Maximum number of workers to use for parallel computation. If negative, + the value wraps around from ``os.cpu_count()``. + See :func:`~scipy.fft.fft` for more details. + orthogonalize : bool, optional + Whether to use the orthogonalized IDST variant (see Notes). + Defaults to ``True`` when ``norm="ortho"`` and ``False`` otherwise. + + .. versionadded:: 1.8.0 + + Returns + ------- + idst : ndarray of real + The transformed input array. + + See Also + -------- + dst : Forward DST + + Notes + ----- + .. warning:: For ``type in {2, 3}``, ``norm="ortho"`` breaks the direct + correspondence with the inverse direct Fourier transform. + + For ``norm="ortho"`` both the `dst` and `idst` are scaled by the same + overall factor in both directions. By default, the transform is also + orthogonalized which for types 2 and 3 means the transform definition is + modified to give orthogonality of the DST matrix (see `dst` for the full + definitions). + + 'The' IDST is the IDST-II, which is the same as the normalized DST-III. + + The IDST is equivalent to a normal DST except for the normalization and + type. DST type 1 and 4 are their own inverse and DSTs 2 and 3 are each + other's inverses. + + """ + return (Dispatchable(x, np.ndarray),) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..2042453733bec54860974cc1e20ba908e8c9b94d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/_realtransforms_backend.py @@ -0,0 +1,63 @@ +from scipy._lib._array_api import array_namespace +import numpy as np +from . import _pocketfft + +__all__ = ['dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn'] + + +def _execute(pocketfft_func, x, type, s, axes, norm, + overwrite_x, workers, orthogonalize): + xp = array_namespace(x) + x = np.asarray(x) + y = pocketfft_func(x, type, s, axes, norm, + overwrite_x=overwrite_x, workers=workers, + orthogonalize=orthogonalize) + return xp.asarray(y) + + +def dctn(x, type=2, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, orthogonalize=None): + return _execute(_pocketfft.dctn, x, type, s, axes, norm, + overwrite_x, workers, orthogonalize) + + +def idctn(x, type=2, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, orthogonalize=None): + return _execute(_pocketfft.idctn, x, type, s, axes, norm, + overwrite_x, workers, orthogonalize) + + +def dstn(x, type=2, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + return _execute(_pocketfft.dstn, x, type, s, axes, norm, + overwrite_x, workers, orthogonalize) + + +def idstn(x, type=2, s=None, axes=None, norm=None, + overwrite_x=False, workers=None, *, orthogonalize=None): + return _execute(_pocketfft.idstn, x, type, s, axes, norm, + overwrite_x, workers, orthogonalize) + + +def dct(x, type=2, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + return _execute(_pocketfft.dct, x, type, n, axis, norm, + overwrite_x, workers, orthogonalize) + + +def idct(x, type=2, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + return _execute(_pocketfft.idct, x, type, n, axis, norm, + overwrite_x, workers, orthogonalize) + + +def dst(x, type=2, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + return _execute(_pocketfft.dst, x, type, n, axis, norm, + overwrite_x, workers, orthogonalize) + + +def idst(x, type=2, n=None, axis=-1, norm=None, + overwrite_x=False, workers=None, orthogonalize=None): + return _execute(_pocketfft.idst, x, type, n, axis, norm, + overwrite_x, workers, orthogonalize) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/mock_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/mock_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..c57a88e0af291ffd68a2a1d62218e8c9459986d5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/mock_backend.py @@ -0,0 +1,92 @@ +import numpy as np +import scipy.fft + +class _MockFunction: + def __init__(self, return_value = None): + self.number_calls = 0 + self.return_value = return_value + self.last_args = ([], {}) + + def __call__(self, *args, **kwargs): + self.number_calls += 1 + self.last_args = (args, kwargs) + return self.return_value + + +fft = _MockFunction(np.random.random(10)) +fft2 = _MockFunction(np.random.random(10)) +fftn = _MockFunction(np.random.random(10)) + +ifft = _MockFunction(np.random.random(10)) +ifft2 = _MockFunction(np.random.random(10)) +ifftn = _MockFunction(np.random.random(10)) + +rfft = _MockFunction(np.random.random(10)) +rfft2 = _MockFunction(np.random.random(10)) +rfftn = _MockFunction(np.random.random(10)) + +irfft = _MockFunction(np.random.random(10)) +irfft2 = _MockFunction(np.random.random(10)) +irfftn = _MockFunction(np.random.random(10)) + +hfft = _MockFunction(np.random.random(10)) +hfft2 = _MockFunction(np.random.random(10)) +hfftn = _MockFunction(np.random.random(10)) + +ihfft = _MockFunction(np.random.random(10)) +ihfft2 = _MockFunction(np.random.random(10)) +ihfftn = _MockFunction(np.random.random(10)) + +dct = _MockFunction(np.random.random(10)) +idct = _MockFunction(np.random.random(10)) +dctn = _MockFunction(np.random.random(10)) +idctn = _MockFunction(np.random.random(10)) + +dst = _MockFunction(np.random.random(10)) +idst = _MockFunction(np.random.random(10)) +dstn = _MockFunction(np.random.random(10)) +idstn = _MockFunction(np.random.random(10)) + +fht = _MockFunction(np.random.random(10)) +ifht = _MockFunction(np.random.random(10)) + + +__ua_domain__ = "numpy.scipy.fft" + + +_implements = { + scipy.fft.fft: fft, + scipy.fft.fft2: fft2, + scipy.fft.fftn: fftn, + scipy.fft.ifft: ifft, + scipy.fft.ifft2: ifft2, + scipy.fft.ifftn: ifftn, + scipy.fft.rfft: rfft, + scipy.fft.rfft2: rfft2, + scipy.fft.rfftn: rfftn, + scipy.fft.irfft: irfft, + scipy.fft.irfft2: irfft2, + scipy.fft.irfftn: irfftn, + scipy.fft.hfft: hfft, + scipy.fft.hfft2: hfft2, + scipy.fft.hfftn: hfftn, + scipy.fft.ihfft: ihfft, + scipy.fft.ihfft2: ihfft2, + scipy.fft.ihfftn: ihfftn, + scipy.fft.dct: dct, + scipy.fft.idct: idct, + scipy.fft.dctn: dctn, + scipy.fft.idctn: idctn, + scipy.fft.dst: dst, + scipy.fft.idst: idst, + scipy.fft.dstn: dstn, + scipy.fft.idstn: idstn, + scipy.fft.fht: fht, + scipy.fft.ifht: ifht +} + + +def __ua_function__(method, args, kwargs): + fn = _implements.get(method) + return (fn(*args, **kwargs) if fn is not None + else NotImplemented) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_backend.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..352ca8ff2a7ab1181b0a3226663dad7ef36ac0fe --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_backend.py @@ -0,0 +1,98 @@ +from functools import partial + +import numpy as np +import scipy.fft +from scipy.fft import _fftlog, _pocketfft, set_backend +from scipy.fft.tests import mock_backend + +from numpy.testing import assert_allclose, assert_equal +import pytest + +fnames = ('fft', 'fft2', 'fftn', + 'ifft', 'ifft2', 'ifftn', + 'rfft', 'rfft2', 'rfftn', + 'irfft', 'irfft2', 'irfftn', + 'dct', 'idct', 'dctn', 'idctn', + 'dst', 'idst', 'dstn', 'idstn', + 'fht', 'ifht') + +np_funcs = (np.fft.fft, np.fft.fft2, np.fft.fftn, + np.fft.ifft, np.fft.ifft2, np.fft.ifftn, + np.fft.rfft, np.fft.rfft2, np.fft.rfftn, + np.fft.irfft, np.fft.irfft2, np.fft.irfftn, + np.fft.hfft, _pocketfft.hfft2, _pocketfft.hfftn, # np has no hfftn + np.fft.ihfft, _pocketfft.ihfft2, _pocketfft.ihfftn, + _pocketfft.dct, _pocketfft.idct, _pocketfft.dctn, _pocketfft.idctn, + _pocketfft.dst, _pocketfft.idst, _pocketfft.dstn, _pocketfft.idstn, + # must provide required kwargs for fht, ifht + partial(_fftlog.fht, dln=2, mu=0.5), + partial(_fftlog.ifht, dln=2, mu=0.5)) + +funcs = (scipy.fft.fft, scipy.fft.fft2, scipy.fft.fftn, + scipy.fft.ifft, scipy.fft.ifft2, scipy.fft.ifftn, + scipy.fft.rfft, scipy.fft.rfft2, scipy.fft.rfftn, + scipy.fft.irfft, scipy.fft.irfft2, scipy.fft.irfftn, + scipy.fft.hfft, scipy.fft.hfft2, scipy.fft.hfftn, + scipy.fft.ihfft, scipy.fft.ihfft2, scipy.fft.ihfftn, + scipy.fft.dct, scipy.fft.idct, scipy.fft.dctn, scipy.fft.idctn, + scipy.fft.dst, scipy.fft.idst, scipy.fft.dstn, scipy.fft.idstn, + # must provide required kwargs for fht, ifht + partial(scipy.fft.fht, dln=2, mu=0.5), + partial(scipy.fft.ifht, dln=2, mu=0.5)) + +mocks = (mock_backend.fft, mock_backend.fft2, mock_backend.fftn, + mock_backend.ifft, mock_backend.ifft2, mock_backend.ifftn, + mock_backend.rfft, mock_backend.rfft2, mock_backend.rfftn, + mock_backend.irfft, mock_backend.irfft2, mock_backend.irfftn, + mock_backend.hfft, mock_backend.hfft2, mock_backend.hfftn, + mock_backend.ihfft, mock_backend.ihfft2, mock_backend.ihfftn, + mock_backend.dct, mock_backend.idct, + mock_backend.dctn, mock_backend.idctn, + mock_backend.dst, mock_backend.idst, + mock_backend.dstn, mock_backend.idstn, + mock_backend.fht, mock_backend.ifht) + + +@pytest.mark.parametrize("func, np_func, mock", zip(funcs, np_funcs, mocks)) +def test_backend_call(func, np_func, mock): + x = np.arange(20).reshape((10,2)) + answer = np_func(x.astype(np.float64)) + assert_allclose(func(x), answer, atol=1e-10) + + with set_backend(mock_backend, only=True): + mock.number_calls = 0 + y = func(x) + assert_equal(y, mock.return_value) + assert_equal(mock.number_calls, 1) + + assert_allclose(func(x), answer, atol=1e-10) + + +plan_funcs = (scipy.fft.fft, scipy.fft.fft2, scipy.fft.fftn, + scipy.fft.ifft, scipy.fft.ifft2, scipy.fft.ifftn, + scipy.fft.rfft, scipy.fft.rfft2, scipy.fft.rfftn, + scipy.fft.irfft, scipy.fft.irfft2, scipy.fft.irfftn, + scipy.fft.hfft, scipy.fft.hfft2, scipy.fft.hfftn, + scipy.fft.ihfft, scipy.fft.ihfft2, scipy.fft.ihfftn) + +plan_mocks = (mock_backend.fft, mock_backend.fft2, mock_backend.fftn, + mock_backend.ifft, mock_backend.ifft2, mock_backend.ifftn, + mock_backend.rfft, mock_backend.rfft2, mock_backend.rfftn, + mock_backend.irfft, mock_backend.irfft2, mock_backend.irfftn, + mock_backend.hfft, mock_backend.hfft2, mock_backend.hfftn, + mock_backend.ihfft, mock_backend.ihfft2, mock_backend.ihfftn) + + +@pytest.mark.parametrize("func, mock", zip(plan_funcs, plan_mocks)) +def test_backend_plan(func, mock): + x = np.arange(20).reshape((10, 2)) + + with pytest.raises(NotImplementedError, match='precomputed plan'): + func(x, plan='foo') + + with set_backend(mock_backend, only=True): + mock.number_calls = 0 + y = func(x, plan='foo') + assert_equal(y, mock.return_value) + assert_equal(mock.number_calls, 1) + assert_equal(mock.last_args[1]['plan'], 'foo') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..55d0f84624704b4172e15e66bdc14faba26db0cc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_basic.py @@ -0,0 +1,504 @@ +import queue +import threading +import multiprocessing +import numpy as np +import pytest +from numpy.random import random +from numpy.testing import assert_array_almost_equal, assert_allclose +from pytest import raises as assert_raises +import scipy.fft as fft +from scipy.conftest import array_api_compatible +from scipy._lib._array_api import ( + array_namespace, size, xp_assert_close, xp_assert_equal +) + +pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_if_array_api")] +skip_if_array_api = pytest.mark.skip_if_array_api + + +# Expected input dtypes. Note that `scipy.fft` is more flexible for numpy, +# but for C2C transforms like `fft.fft`, the array API standard only mandates +# that complex dtypes should work, float32/float64 aren't guaranteed to. +def get_expected_input_dtype(func, xp): + if func in [fft.fft, fft.fftn, fft.fft2, + fft.ifft, fft.ifftn, fft.ifft2, + fft.hfft, fft.hfftn, fft.hfft2, + fft.irfft, fft.irfftn, fft.irfft2]: + dtype = xp.complex128 + elif func in [fft.rfft, fft.rfftn, fft.rfft2, + fft.ihfft, fft.ihfftn, fft.ihfft2]: + dtype = xp.float64 + else: + raise ValueError(f'Unknown FFT function: {func}') + + return dtype + + +def fft1(x): + L = len(x) + phase = -2j*np.pi*(np.arange(L)/float(L)) + phase = np.arange(L).reshape(-1, 1) * phase + return np.sum(x*np.exp(phase), axis=1) + + +class TestFFTShift: + + def test_fft_n(self, xp): + x = xp.asarray([1, 2, 3], dtype=xp.complex128) + if xp.__name__ == 'torch': + assert_raises(RuntimeError, fft.fft, x, 0) + else: + assert_raises(ValueError, fft.fft, x, 0) + + +class TestFFT1D: + + def test_identity(self, xp): + maxlen = 512 + x = xp.asarray(random(maxlen) + 1j*random(maxlen)) + xr = xp.asarray(random(maxlen)) + for i in range(1, maxlen): + xp_assert_close(fft.ifft(fft.fft(x[0:i])), x[0:i], rtol=1e-9, atol=0) + xp_assert_close(fft.irfft(fft.rfft(xr[0:i]), i), xr[0:i], rtol=1e-9, atol=0) + + def test_fft(self, xp): + x = random(30) + 1j*random(30) + expect = xp.asarray(fft1(x)) + x = xp.asarray(x) + xp_assert_close(fft.fft(x), expect) + xp_assert_close(fft.fft(x, norm="backward"), expect) + xp_assert_close(fft.fft(x, norm="ortho"), + expect / xp.sqrt(xp.asarray(30, dtype=xp.float64)),) + xp_assert_close(fft.fft(x, norm="forward"), expect / 30) + + def test_ifft(self, xp): + x = xp.asarray(random(30) + 1j*random(30)) + xp_assert_close(fft.ifft(fft.fft(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.ifft(fft.fft(x, norm=norm), norm=norm), x) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_fft2(self, xp): + x = xp.asarray(random((30, 20)) + 1j*random((30, 20))) + expect = fft.fft(fft.fft(x, axis=1), axis=0) + xp_assert_close(fft.fft2(x), expect) + xp_assert_close(fft.fft2(x, norm="backward"), expect) + xp_assert_close(fft.fft2(x, norm="ortho"), + expect / xp.sqrt(xp.asarray(30 * 20, dtype=xp.float64))) + xp_assert_close(fft.fft2(x, norm="forward"), expect / (30 * 20)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_ifft2(self, xp): + x = xp.asarray(random((30, 20)) + 1j*random((30, 20))) + expect = fft.ifft(fft.ifft(x, axis=1), axis=0) + xp_assert_close(fft.ifft2(x), expect) + xp_assert_close(fft.ifft2(x, norm="backward"), expect) + xp_assert_close(fft.ifft2(x, norm="ortho"), + expect * xp.sqrt(xp.asarray(30 * 20, dtype=xp.float64))) + xp_assert_close(fft.ifft2(x, norm="forward"), expect * (30 * 20)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_fftn(self, xp): + x = xp.asarray(random((30, 20, 10)) + 1j*random((30, 20, 10))) + expect = fft.fft(fft.fft(fft.fft(x, axis=2), axis=1), axis=0) + xp_assert_close(fft.fftn(x), expect) + xp_assert_close(fft.fftn(x, norm="backward"), expect) + xp_assert_close(fft.fftn(x, norm="ortho"), + expect / xp.sqrt(xp.asarray(30 * 20 * 10, dtype=xp.float64))) + xp_assert_close(fft.fftn(x, norm="forward"), expect / (30 * 20 * 10)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_ifftn(self, xp): + x = xp.asarray(random((30, 20, 10)) + 1j*random((30, 20, 10))) + expect = fft.ifft(fft.ifft(fft.ifft(x, axis=2), axis=1), axis=0) + xp_assert_close(fft.ifftn(x), expect) + xp_assert_close(fft.ifftn(x, norm="backward"), expect) + xp_assert_close( + fft.ifftn(x, norm="ortho"), + fft.ifftn(x) * xp.sqrt(xp.asarray(30 * 20 * 10, dtype=xp.float64)) + ) + xp_assert_close(fft.ifftn(x, norm="forward"), expect * (30 * 20 * 10)) + + def test_rfft(self, xp): + x = xp.asarray(random(29), dtype=xp.float64) + for n in [size(x), 2*size(x)]: + for norm in [None, "backward", "ortho", "forward"]: + xp_assert_close(fft.rfft(x, n=n, norm=norm), + fft.fft(xp.asarray(x, dtype=xp.complex128), + n=n, norm=norm)[:(n//2 + 1)]) + xp_assert_close( + fft.rfft(x, n=n, norm="ortho"), + fft.rfft(x, n=n) / xp.sqrt(xp.asarray(n, dtype=xp.float64)) + ) + + def test_irfft(self, xp): + x = xp.asarray(random(30)) + xp_assert_close(fft.irfft(fft.rfft(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.irfft(fft.rfft(x, norm=norm), norm=norm), x) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_rfft2(self, xp): + x = xp.asarray(random((30, 20)), dtype=xp.float64) + expect = fft.fft2(xp.asarray(x, dtype=xp.complex128))[:, :11] + xp_assert_close(fft.rfft2(x), expect) + xp_assert_close(fft.rfft2(x, norm="backward"), expect) + xp_assert_close(fft.rfft2(x, norm="ortho"), + expect / xp.sqrt(xp.asarray(30 * 20, dtype=xp.float64))) + xp_assert_close(fft.rfft2(x, norm="forward"), expect / (30 * 20)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_irfft2(self, xp): + x = xp.asarray(random((30, 20))) + xp_assert_close(fft.irfft2(fft.rfft2(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.irfft2(fft.rfft2(x, norm=norm), norm=norm), x) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_rfftn(self, xp): + x = xp.asarray(random((30, 20, 10)), dtype=xp.float64) + expect = fft.fftn(xp.asarray(x, dtype=xp.complex128))[:, :, :6] + xp_assert_close(fft.rfftn(x), expect) + xp_assert_close(fft.rfftn(x, norm="backward"), expect) + xp_assert_close(fft.rfftn(x, norm="ortho"), + expect / xp.sqrt(xp.asarray(30 * 20 * 10, dtype=xp.float64))) + xp_assert_close(fft.rfftn(x, norm="forward"), expect / (30 * 20 * 10)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_irfftn(self, xp): + x = xp.asarray(random((30, 20, 10))) + xp_assert_close(fft.irfftn(fft.rfftn(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.irfftn(fft.rfftn(x, norm=norm), norm=norm), x) + + def test_hfft(self, xp): + x = random(14) + 1j*random(14) + x_herm = np.concatenate((random(1), x, random(1))) + x = np.concatenate((x_herm, x[::-1].conj())) + x = xp.asarray(x) + x_herm = xp.asarray(x_herm) + expect = xp.real(fft.fft(x)) + xp_assert_close(fft.hfft(x_herm), expect) + xp_assert_close(fft.hfft(x_herm, norm="backward"), expect) + xp_assert_close(fft.hfft(x_herm, norm="ortho"), + expect / xp.sqrt(xp.asarray(30, dtype=xp.float64))) + xp_assert_close(fft.hfft(x_herm, norm="forward"), expect / 30) + + def test_ihfft(self, xp): + x = random(14) + 1j*random(14) + x_herm = np.concatenate((random(1), x, random(1))) + x = np.concatenate((x_herm, x[::-1].conj())) + x = xp.asarray(x) + x_herm = xp.asarray(x_herm) + xp_assert_close(fft.ihfft(fft.hfft(x_herm)), x_herm) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.ihfft(fft.hfft(x_herm, norm=norm), norm=norm), x_herm) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_hfft2(self, xp): + x = xp.asarray(random((30, 20))) + xp_assert_close(fft.hfft2(fft.ihfft2(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.hfft2(fft.ihfft2(x, norm=norm), norm=norm), x) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_ihfft2(self, xp): + x = xp.asarray(random((30, 20)), dtype=xp.float64) + expect = fft.ifft2(xp.asarray(x, dtype=xp.complex128))[:, :11] + xp_assert_close(fft.ihfft2(x), expect) + xp_assert_close(fft.ihfft2(x, norm="backward"), expect) + xp_assert_close( + fft.ihfft2(x, norm="ortho"), + expect * xp.sqrt(xp.asarray(30 * 20, dtype=xp.float64)) + ) + xp_assert_close(fft.ihfft2(x, norm="forward"), expect * (30 * 20)) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_hfftn(self, xp): + x = xp.asarray(random((30, 20, 10))) + xp_assert_close(fft.hfftn(fft.ihfftn(x)), x) + for norm in ["backward", "ortho", "forward"]: + xp_assert_close(fft.hfftn(fft.ihfftn(x, norm=norm), norm=norm), x) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + def test_ihfftn(self, xp): + x = xp.asarray(random((30, 20, 10)), dtype=xp.float64) + expect = fft.ifftn(xp.asarray(x, dtype=xp.complex128))[:, :, :6] + xp_assert_close(expect, fft.ihfftn(x)) + xp_assert_close(expect, fft.ihfftn(x, norm="backward")) + xp_assert_close( + fft.ihfftn(x, norm="ortho"), + expect * xp.sqrt(xp.asarray(30 * 20 * 10, dtype=xp.float64)) + ) + xp_assert_close(fft.ihfftn(x, norm="forward"), expect * (30 * 20 * 10)) + + def _check_axes(self, op, xp): + dtype = get_expected_input_dtype(op, xp) + x = xp.asarray(random((30, 20, 10)), dtype=dtype) + axes = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] + xp_test = array_namespace(x) + for a in axes: + op_tr = op(xp_test.permute_dims(x, axes=a)) + tr_op = xp_test.permute_dims(op(x, axes=a), axes=a) + xp_assert_close(op_tr, tr_op) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + @pytest.mark.parametrize("op", [fft.fftn, fft.ifftn, fft.rfftn, fft.irfftn]) + def test_axes_standard(self, op, xp): + self._check_axes(op, xp) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + @pytest.mark.parametrize("op", [fft.hfftn, fft.ihfftn]) + def test_axes_non_standard(self, op, xp): + self._check_axes(op, xp) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + @pytest.mark.parametrize("op", [fft.fftn, fft.ifftn, + fft.rfftn, fft.irfftn]) + def test_axes_subset_with_shape_standard(self, op, xp): + dtype = get_expected_input_dtype(op, xp) + x = xp.asarray(random((16, 8, 4)), dtype=dtype) + axes = [(0, 1, 2), (0, 2, 1), (1, 2, 0)] + xp_test = array_namespace(x) + for a in axes: + # different shape on the first two axes + shape = tuple([2*x.shape[ax] if ax in a[:2] else x.shape[ax] + for ax in range(x.ndim)]) + # transform only the first two axes + op_tr = op(xp_test.permute_dims(x, axes=a), + s=shape[:2], axes=(0, 1)) + tr_op = xp_test.permute_dims(op(x, s=shape[:2], axes=a[:2]), + axes=a) + xp_assert_close(op_tr, tr_op) + + @skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) + @pytest.mark.parametrize("op", [fft.fft2, fft.ifft2, + fft.rfft2, fft.irfft2, + fft.hfft2, fft.ihfft2, + fft.hfftn, fft.ihfftn]) + def test_axes_subset_with_shape_non_standard(self, op, xp): + dtype = get_expected_input_dtype(op, xp) + x = xp.asarray(random((16, 8, 4)), dtype=dtype) + axes = [(0, 1, 2), (0, 2, 1), (1, 2, 0)] + xp_test = array_namespace(x) + for a in axes: + # different shape on the first two axes + shape = tuple([2*x.shape[ax] if ax in a[:2] else x.shape[ax] + for ax in range(x.ndim)]) + # transform only the first two axes + op_tr = op(xp_test.permute_dims(x, axes=a), s=shape[:2], axes=(0, 1)) + tr_op = xp_test.permute_dims(op(x, s=shape[:2], axes=a[:2]), axes=a) + xp_assert_close(op_tr, tr_op) + + def test_all_1d_norm_preserving(self, xp): + # verify that round-trip transforms are norm-preserving + x = xp.asarray(random(30), dtype=xp.float64) + xp_test = array_namespace(x) + x_norm = xp_test.linalg.vector_norm(x) + n = size(x) * 2 + func_pairs = [(fft.rfft, fft.irfft), + # hfft: order so the first function takes x.size samples + # (necessary for comparison to x_norm above) + (fft.ihfft, fft.hfft), + # functions that expect complex dtypes at the end + (fft.fft, fft.ifft), + ] + for forw, back in func_pairs: + if forw == fft.fft: + x = xp.asarray(x, dtype=xp.complex128) + x_norm = xp_test.linalg.vector_norm(x) + for n in [size(x), 2*size(x)]: + for norm in ['backward', 'ortho', 'forward']: + tmp = forw(x, n=n, norm=norm) + tmp = back(tmp, n=n, norm=norm) + xp_assert_close(xp_test.linalg.vector_norm(tmp), x_norm) + + @skip_if_array_api(np_only=True) + @pytest.mark.parametrize("dtype", [np.float16, np.longdouble]) + def test_dtypes_nonstandard(self, dtype): + x = random(30).astype(dtype) + out_dtypes = {np.float16: np.complex64, np.longdouble: np.clongdouble} + x_complex = x.astype(out_dtypes[dtype]) + + res_fft = fft.ifft(fft.fft(x)) + res_rfft = fft.irfft(fft.rfft(x)) + res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) + # Check both numerical results and exact dtype matches + assert_array_almost_equal(res_fft, x_complex) + assert_array_almost_equal(res_rfft, x) + assert_array_almost_equal(res_hfft, x) + assert res_fft.dtype == x_complex.dtype + assert res_rfft.dtype == np.result_type(np.float32, x.dtype) + assert res_hfft.dtype == np.result_type(np.float32, x.dtype) + + @pytest.mark.parametrize("dtype", ["float32", "float64"]) + def test_dtypes_real(self, dtype, xp): + x = xp.asarray(random(30), dtype=getattr(xp, dtype)) + + res_rfft = fft.irfft(fft.rfft(x)) + res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) + # Check both numerical results and exact dtype matches + rtol = {"float32": 1.2e-4, "float64": 1e-8}[dtype] + xp_assert_close(res_rfft, x, rtol=rtol, atol=0) + xp_assert_close(res_hfft, x, rtol=rtol, atol=0) + + @pytest.mark.parametrize("dtype", ["complex64", "complex128"]) + def test_dtypes_complex(self, dtype, xp): + x = xp.asarray(random(30), dtype=getattr(xp, dtype)) + + res_fft = fft.ifft(fft.fft(x)) + # Check both numerical results and exact dtype matches + rtol = {"complex64": 1.2e-4, "complex128": 1e-8}[dtype] + xp_assert_close(res_fft, x, rtol=rtol, atol=0) + +@skip_if_array_api(np_only=True) +@pytest.mark.parametrize( + "dtype", + [np.float32, np.float64, np.longdouble, + np.complex64, np.complex128, np.clongdouble]) +@pytest.mark.parametrize("order", ["F", 'non-contiguous']) +@pytest.mark.parametrize( + "fft", + [fft.fft, fft.fft2, fft.fftn, + fft.ifft, fft.ifft2, fft.ifftn]) +def test_fft_with_order(dtype, order, fft): + # Check that FFT/IFFT produces identical results for C, Fortran and + # non contiguous arrays + rng = np.random.RandomState(42) + X = rng.rand(8, 7, 13).astype(dtype, copy=False) + if order == 'F': + Y = np.asfortranarray(X) + else: + # Make a non contiguous array + Y = X[::-1] + X = np.ascontiguousarray(X[::-1]) + + if fft.__name__.endswith('fft'): + for axis in range(3): + X_res = fft(X, axis=axis) + Y_res = fft(Y, axis=axis) + assert_array_almost_equal(X_res, Y_res) + elif fft.__name__.endswith(('fft2', 'fftn')): + axes = [(0, 1), (1, 2), (0, 2)] + if fft.__name__.endswith('fftn'): + axes.extend([(0,), (1,), (2,), None]) + for ax in axes: + X_res = fft(X, axes=ax) + Y_res = fft(Y, axes=ax) + assert_array_almost_equal(X_res, Y_res) + else: + raise ValueError + + +class TestFFTThreadSafe: + threads = 16 + input_shape = (800, 200) + + def _test_mtsame(self, func, *args, xp=None): + def worker(args, q): + q.put(func(*args)) + + q = queue.Queue() + expected = func(*args) + + # Spin off a bunch of threads to call the same function simultaneously + t = [threading.Thread(target=worker, args=(args, q)) + for i in range(self.threads)] + [x.start() for x in t] + + [x.join() for x in t] + + # Make sure all threads returned the correct value + for i in range(self.threads): + xp_assert_equal( + q.get(timeout=5), expected, + err_msg='Function returned wrong value in multithreaded context' + ) + + def test_fft(self, xp): + a = xp.ones(self.input_shape, dtype=xp.complex128) + self._test_mtsame(fft.fft, a, xp=xp) + + def test_ifft(self, xp): + a = xp.full(self.input_shape, 1+0j) + self._test_mtsame(fft.ifft, a, xp=xp) + + def test_rfft(self, xp): + a = xp.ones(self.input_shape) + self._test_mtsame(fft.rfft, a, xp=xp) + + def test_irfft(self, xp): + a = xp.full(self.input_shape, 1+0j) + self._test_mtsame(fft.irfft, a, xp=xp) + + def test_hfft(self, xp): + a = xp.ones(self.input_shape, dtype=xp.complex64) + self._test_mtsame(fft.hfft, a, xp=xp) + + def test_ihfft(self, xp): + a = xp.ones(self.input_shape) + self._test_mtsame(fft.ihfft, a, xp=xp) + + +@skip_if_array_api(np_only=True) +@pytest.mark.parametrize("func", [fft.fft, fft.ifft, fft.rfft, fft.irfft]) +def test_multiprocess(func): + # Test that fft still works after fork (gh-10422) + + with multiprocessing.Pool(2) as p: + res = p.map(func, [np.ones(100) for _ in range(4)]) + + expect = func(np.ones(100)) + for x in res: + assert_allclose(x, expect) + + +@skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) +class TestIRFFTN: + + def test_not_last_axis_success(self, xp): + ar, ai = np.random.random((2, 16, 8, 32)) + a = ar + 1j*ai + a = xp.asarray(a) + + axes = (-2,) + + # Should not raise error + fft.irfftn(a, axes=axes) + + +@skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) +@pytest.mark.parametrize("func", [fft.fft, fft.ifft, fft.rfft, fft.irfft, + fft.fftn, fft.ifftn, + fft.rfftn, fft.irfftn, fft.hfft, fft.ihfft]) +def test_non_standard_params(func, xp): + if func in [fft.rfft, fft.rfftn, fft.ihfft]: + dtype = xp.float64 + else: + dtype = xp.complex128 + + if xp.__name__ != 'numpy': + x = xp.asarray([1, 2, 3], dtype=dtype) + # func(x) should not raise an exception + func(x) + assert_raises(ValueError, func, x, workers=2) + # `plan` param is not tested since SciPy does not use it currently + # but should be tested if it comes into use diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_fftlog.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_fftlog.py new file mode 100644 index 0000000000000000000000000000000000000000..d9652facb776e8c791f2b209c1aec69d24ffe6c2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_fftlog.py @@ -0,0 +1,169 @@ +import warnings +import numpy as np +import pytest + +from scipy.fft._fftlog import fht, ifht, fhtoffset +from scipy.special import poch + +from scipy.conftest import array_api_compatible +from scipy._lib._array_api import xp_assert_close + +pytestmark = array_api_compatible + + +def test_fht_agrees_with_fftlog(xp): + # check that fht numerically agrees with the output from Fortran FFTLog, + # the results were generated with the provided `fftlogtest` program, + # after fixing how the k array is generated (divide range by n-1, not n) + + # test function, analytical Hankel transform is of the same form + def f(r, mu): + return r**(mu+1)*np.exp(-r**2/2) + + r = np.logspace(-4, 4, 16) + + dln = np.log(r[1]/r[0]) + mu = 0.3 + offset = 0.0 + bias = 0.0 + + a = xp.asarray(f(r, mu)) + + # test 1: compute as given + ours = fht(a, dln, mu, offset=offset, bias=bias) + theirs = [-0.1159922613593045E-02, +0.1625822618458832E-02, + -0.1949518286432330E-02, +0.3789220182554077E-02, + +0.5093959119952945E-03, +0.2785387803618774E-01, + +0.9944952700848897E-01, +0.4599202164586588E+00, + +0.3157462160881342E+00, -0.8201236844404755E-03, + -0.7834031308271878E-03, +0.3931444945110708E-03, + -0.2697710625194777E-03, +0.3568398050238820E-03, + -0.5554454827797206E-03, +0.8286331026468585E-03] + theirs = xp.asarray(theirs, dtype=xp.float64) + xp_assert_close(ours, theirs) + + # test 2: change to optimal offset + offset = fhtoffset(dln, mu, bias=bias) + ours = fht(a, dln, mu, offset=offset, bias=bias) + theirs = [+0.4353768523152057E-04, -0.9197045663594285E-05, + +0.3150140927838524E-03, +0.9149121960963704E-03, + +0.5808089753959363E-02, +0.2548065256377240E-01, + +0.1339477692089897E+00, +0.4821530509479356E+00, + +0.2659899781579785E+00, -0.1116475278448113E-01, + +0.1791441617592385E-02, -0.4181810476548056E-03, + +0.1314963536765343E-03, -0.5422057743066297E-04, + +0.3208681804170443E-04, -0.2696849476008234E-04] + theirs = xp.asarray(theirs, dtype=xp.float64) + xp_assert_close(ours, theirs) + + # test 3: positive bias + bias = 0.8 + offset = fhtoffset(dln, mu, bias=bias) + ours = fht(a, dln, mu, offset=offset, bias=bias) + theirs = [-7.3436673558316850E+00, +0.1710271207817100E+00, + +0.1065374386206564E+00, -0.5121739602708132E-01, + +0.2636649319269470E-01, +0.1697209218849693E-01, + +0.1250215614723183E+00, +0.4739583261486729E+00, + +0.2841149874912028E+00, -0.8312764741645729E-02, + +0.1024233505508988E-02, -0.1644902767389120E-03, + +0.3305775476926270E-04, -0.7786993194882709E-05, + +0.1962258449520547E-05, -0.8977895734909250E-06] + theirs = xp.asarray(theirs, dtype=xp.float64) + xp_assert_close(ours, theirs) + + # test 4: negative bias + bias = -0.8 + offset = fhtoffset(dln, mu, bias=bias) + ours = fht(a, dln, mu, offset=offset, bias=bias) + theirs = [+0.8985777068568745E-05, +0.4074898209936099E-04, + +0.2123969254700955E-03, +0.1009558244834628E-02, + +0.5131386375222176E-02, +0.2461678673516286E-01, + +0.1235812845384476E+00, +0.4719570096404403E+00, + +0.2893487490631317E+00, -0.1686570611318716E-01, + +0.2231398155172505E-01, -0.1480742256379873E-01, + +0.1692387813500801E+00, +0.3097490354365797E+00, + +2.7593607182401860E+00, 10.5251075070045800E+00] + theirs = xp.asarray(theirs, dtype=xp.float64) + xp_assert_close(ours, theirs) + + +@pytest.mark.parametrize('optimal', [True, False]) +@pytest.mark.parametrize('offset', [0.0, 1.0, -1.0]) +@pytest.mark.parametrize('bias', [0, 0.1, -0.1]) +@pytest.mark.parametrize('n', [64, 63]) +def test_fht_identity(n, bias, offset, optimal, xp): + rng = np.random.RandomState(3491349965) + + a = xp.asarray(rng.standard_normal(n)) + dln = rng.uniform(-1, 1) + mu = rng.uniform(-2, 2) + + if optimal: + offset = fhtoffset(dln, mu, initial=offset, bias=bias) + + A = fht(a, dln, mu, offset=offset, bias=bias) + a_ = ifht(A, dln, mu, offset=offset, bias=bias) + + xp_assert_close(a_, a) + + +def test_fht_special_cases(xp): + rng = np.random.RandomState(3491349965) + + a = xp.asarray(rng.standard_normal(64)) + dln = rng.uniform(-1, 1) + + # let x = (mu+1+q)/2, y = (mu+1-q)/2, M = {0, -1, -2, ...} + + # case 1: x in M, y in M => well-defined transform + mu, bias = -4.0, 1.0 + with warnings.catch_warnings(record=True) as record: + fht(a, dln, mu, bias=bias) + assert not record, 'fht warned about a well-defined transform' + + # case 2: x not in M, y in M => well-defined transform + mu, bias = -2.5, 0.5 + with warnings.catch_warnings(record=True) as record: + fht(a, dln, mu, bias=bias) + assert not record, 'fht warned about a well-defined transform' + + # case 3: x in M, y not in M => singular transform + mu, bias = -3.5, 0.5 + with pytest.warns(Warning) as record: + fht(a, dln, mu, bias=bias) + assert record, 'fht did not warn about a singular transform' + + # case 4: x not in M, y in M => singular inverse transform + mu, bias = -2.5, 0.5 + with pytest.warns(Warning) as record: + ifht(a, dln, mu, bias=bias) + assert record, 'ifht did not warn about a singular transform' + + +@pytest.mark.parametrize('n', [64, 63]) +def test_fht_exact(n, xp): + rng = np.random.RandomState(3491349965) + + # for a(r) a power law r^\gamma, the fast Hankel transform produces the + # exact continuous Hankel transform if biased with q = \gamma + + mu = rng.uniform(0, 3) + + # convergence of HT: -1-mu < gamma < 1/2 + gamma = rng.uniform(-1-mu, 1/2) + + r = np.logspace(-2, 2, n) + a = xp.asarray(r**gamma) + + dln = np.log(r[1]/r[0]) + + offset = fhtoffset(dln, mu, initial=0.0, bias=gamma) + + A = fht(a, dln, mu, offset=offset, bias=gamma) + + k = np.exp(offset)/r[::-1] + + # analytical result + At = xp.asarray((2/k)**gamma * poch((mu+1-gamma)/2, gamma)) + + xp_assert_close(A, At) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_helper.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..c99fea40f7f58480ba3532cb490c78cde48ce4a5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_helper.py @@ -0,0 +1,445 @@ +"""Includes test functions for fftpack.helper module + +Copied from fftpack.helper by Pearu Peterson, October 2005 +Modified for Array API, 2023 + +""" +from scipy.fft._helper import next_fast_len, _init_nd_shape_and_axes +from numpy.testing import assert_equal +from pytest import raises as assert_raises +import pytest +import numpy as np +import sys +from scipy.conftest import array_api_compatible +from scipy._lib._array_api import xp_assert_close, SCIPY_DEVICE +from scipy import fft + +pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_if_array_api")] +skip_if_array_api = pytest.mark.skip_if_array_api + +_5_smooth_numbers = [ + 2, 3, 4, 5, 6, 8, 9, 10, + 2 * 3 * 5, + 2**3 * 3**5, + 2**3 * 3**3 * 5**2, +] + +def test_next_fast_len(): + for n in _5_smooth_numbers: + assert_equal(next_fast_len(n), n) + + +def _assert_n_smooth(x, n): + x_orig = x + if n < 2: + assert False + + while True: + q, r = divmod(x, 2) + if r != 0: + break + x = q + + for d in range(3, n+1, 2): + while True: + q, r = divmod(x, d) + if r != 0: + break + x = q + + assert x == 1, \ + f'x={x_orig} is not {n}-smooth, remainder={x}' + + +@skip_if_array_api(np_only=True) +class TestNextFastLen: + + def test_next_fast_len(self): + np.random.seed(1234) + + def nums(): + yield from range(1, 1000) + yield 2**5 * 3**5 * 4**5 + 1 + + for n in nums(): + m = next_fast_len(n) + _assert_n_smooth(m, 11) + assert m == next_fast_len(n, False) + + m = next_fast_len(n, True) + _assert_n_smooth(m, 5) + + def test_np_integers(self): + ITYPES = [np.int16, np.int32, np.int64, np.uint16, np.uint32, np.uint64] + for ityp in ITYPES: + x = ityp(12345) + testN = next_fast_len(x) + assert_equal(testN, next_fast_len(int(x))) + + def testnext_fast_len_small(self): + hams = { + 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 8, 8: 8, 14: 15, 15: 15, + 16: 16, 17: 18, 1021: 1024, 1536: 1536, 51200000: 51200000 + } + for x, y in hams.items(): + assert_equal(next_fast_len(x, True), y) + + @pytest.mark.xfail(sys.maxsize < 2**32, + reason="Hamming Numbers too large for 32-bit", + raises=ValueError, strict=True) + def testnext_fast_len_big(self): + hams = { + 510183360: 510183360, 510183360 + 1: 512000000, + 511000000: 512000000, + 854296875: 854296875, 854296875 + 1: 859963392, + 196608000000: 196608000000, 196608000000 + 1: 196830000000, + 8789062500000: 8789062500000, 8789062500000 + 1: 8796093022208, + 206391214080000: 206391214080000, + 206391214080000 + 1: 206624260800000, + 470184984576000: 470184984576000, + 470184984576000 + 1: 470715894135000, + 7222041363087360: 7222041363087360, + 7222041363087360 + 1: 7230196133913600, + # power of 5 5**23 + 11920928955078125: 11920928955078125, + 11920928955078125 - 1: 11920928955078125, + # power of 3 3**34 + 16677181699666569: 16677181699666569, + 16677181699666569 - 1: 16677181699666569, + # power of 2 2**54 + 18014398509481984: 18014398509481984, + 18014398509481984 - 1: 18014398509481984, + # above this, int(ceil(n)) == int(ceil(n+1)) + 19200000000000000: 19200000000000000, + 19200000000000000 + 1: 19221679687500000, + 288230376151711744: 288230376151711744, + 288230376151711744 + 1: 288325195312500000, + 288325195312500000 - 1: 288325195312500000, + 288325195312500000: 288325195312500000, + 288325195312500000 + 1: 288555831593533440, + } + for x, y in hams.items(): + assert_equal(next_fast_len(x, True), y) + + def test_keyword_args(self): + assert next_fast_len(11, real=True) == 12 + assert next_fast_len(target=7, real=False) == 7 + + +@skip_if_array_api(cpu_only=True) +class Test_init_nd_shape_and_axes: + + def test_py_0d_defaults(self, xp): + x = xp.asarray(4) + shape = None + axes = None + + shape_expected = () + axes_expected = [] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_0d_defaults(self, xp): + x = xp.asarray(7.) + shape = None + axes = None + + shape_expected = () + axes_expected = [] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_py_1d_defaults(self, xp): + x = xp.asarray([1, 2, 3]) + shape = None + axes = None + + shape_expected = (3,) + axes_expected = [0] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_1d_defaults(self, xp): + x = xp.arange(0, 1, .1) + shape = None + axes = None + + shape_expected = (10,) + axes_expected = [0] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_py_2d_defaults(self, xp): + x = xp.asarray([[1, 2, 3, 4], + [5, 6, 7, 8]]) + shape = None + axes = None + + shape_expected = (2, 4) + axes_expected = [0, 1] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_2d_defaults(self, xp): + x = xp.arange(0, 1, .1) + x = xp.reshape(x, (5, 2)) + shape = None + axes = None + + shape_expected = (5, 2) + axes_expected = [0, 1] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_5d_defaults(self, xp): + x = xp.zeros([6, 2, 5, 3, 4]) + shape = None + axes = None + + shape_expected = (6, 2, 5, 3, 4) + axes_expected = [0, 1, 2, 3, 4] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_5d_set_shape(self, xp): + x = xp.zeros([6, 2, 5, 3, 4]) + shape = [10, -1, -1, 1, 4] + axes = None + + shape_expected = (10, 2, 5, 1, 4) + axes_expected = [0, 1, 2, 3, 4] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_5d_set_axes(self, xp): + x = xp.zeros([6, 2, 5, 3, 4]) + shape = None + axes = [4, 1, 2] + + shape_expected = (4, 2, 5) + axes_expected = [4, 1, 2] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_xp_5d_set_shape_axes(self, xp): + x = xp.zeros([6, 2, 5, 3, 4]) + shape = [10, -1, 2] + axes = [1, 0, 3] + + shape_expected = (10, 6, 2) + axes_expected = [1, 0, 3] + + shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes) + + assert shape_res == shape_expected + assert axes_res == axes_expected + + def test_shape_axes_subset(self, xp): + x = xp.zeros((2, 3, 4, 5)) + shape, axes = _init_nd_shape_and_axes(x, shape=(5, 5, 5), axes=None) + + assert shape == (5, 5, 5) + assert axes == [1, 2, 3] + + def test_errors(self, xp): + x = xp.zeros(1) + with assert_raises(ValueError, match="axes must be a scalar or " + "iterable of integers"): + _init_nd_shape_and_axes(x, shape=None, axes=[[1, 2], [3, 4]]) + + with assert_raises(ValueError, match="axes must be a scalar or " + "iterable of integers"): + _init_nd_shape_and_axes(x, shape=None, axes=[1., 2., 3., 4.]) + + with assert_raises(ValueError, + match="axes exceeds dimensionality of input"): + _init_nd_shape_and_axes(x, shape=None, axes=[1]) + + with assert_raises(ValueError, + match="axes exceeds dimensionality of input"): + _init_nd_shape_and_axes(x, shape=None, axes=[-2]) + + with assert_raises(ValueError, + match="all axes must be unique"): + _init_nd_shape_and_axes(x, shape=None, axes=[0, 0]) + + with assert_raises(ValueError, match="shape must be a scalar or " + "iterable of integers"): + _init_nd_shape_and_axes(x, shape=[[1, 2], [3, 4]], axes=None) + + with assert_raises(ValueError, match="shape must be a scalar or " + "iterable of integers"): + _init_nd_shape_and_axes(x, shape=[1., 2., 3., 4.], axes=None) + + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + _init_nd_shape_and_axes(xp.zeros([1, 1, 1, 1]), + shape=[1, 2, 3], axes=[1]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[0\]\) specified"): + _init_nd_shape_and_axes(x, shape=[0], axes=None) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[-2\]\) specified"): + _init_nd_shape_and_axes(x, shape=-2, axes=None) + + +@skip_if_array_api('torch', + reasons=['torch.fft not yet implemented by array-api-compat']) +class TestFFTShift: + + def test_definition(self, xp): + x = xp.asarray([0., 1, 2, 3, 4, -4, -3, -2, -1]) + y = xp.asarray([-4., -3, -2, -1, 0, 1, 2, 3, 4]) + xp_assert_close(fft.fftshift(x), y) + xp_assert_close(fft.ifftshift(y), x) + x = xp.asarray([0., 1, 2, 3, 4, -5, -4, -3, -2, -1]) + y = xp.asarray([-5., -4, -3, -2, -1, 0, 1, 2, 3, 4]) + xp_assert_close(fft.fftshift(x), y) + xp_assert_close(fft.ifftshift(y), x) + + def test_inverse(self, xp): + for n in [1, 4, 9, 100, 211]: + x = xp.asarray(np.random.random((n,))) + xp_assert_close(fft.ifftshift(fft.fftshift(x)), x) + + def test_axes_keyword(self, xp): + freqs = xp.asarray([[0., 1, 2], [3, 4, -4], [-3, -2, -1]]) + shifted = xp.asarray([[-1., -3, -2], [2, 0, 1], [-4, 3, 4]]) + xp_assert_close(fft.fftshift(freqs, axes=(0, 1)), shifted) + xp_assert_close(fft.fftshift(freqs, axes=0), fft.fftshift(freqs, axes=(0,))) + xp_assert_close(fft.ifftshift(shifted, axes=(0, 1)), freqs) + xp_assert_close(fft.ifftshift(shifted, axes=0), + fft.ifftshift(shifted, axes=(0,))) + xp_assert_close(fft.fftshift(freqs), shifted) + xp_assert_close(fft.ifftshift(shifted), freqs) + + def test_uneven_dims(self, xp): + """ Test 2D input, which has uneven dimension sizes """ + freqs = xp.asarray([ + [0, 1], + [2, 3], + [4, 5] + ], dtype=xp.float64) + + # shift in dimension 0 + shift_dim0 = xp.asarray([ + [4, 5], + [0, 1], + [2, 3] + ], dtype=xp.float64) + xp_assert_close(fft.fftshift(freqs, axes=0), shift_dim0) + xp_assert_close(fft.ifftshift(shift_dim0, axes=0), freqs) + xp_assert_close(fft.fftshift(freqs, axes=(0,)), shift_dim0) + xp_assert_close(fft.ifftshift(shift_dim0, axes=[0]), freqs) + + # shift in dimension 1 + shift_dim1 = xp.asarray([ + [1, 0], + [3, 2], + [5, 4] + ], dtype=xp.float64) + xp_assert_close(fft.fftshift(freqs, axes=1), shift_dim1) + xp_assert_close(fft.ifftshift(shift_dim1, axes=1), freqs) + + # shift in both dimensions + shift_dim_both = xp.asarray([ + [5, 4], + [1, 0], + [3, 2] + ], dtype=xp.float64) + xp_assert_close(fft.fftshift(freqs, axes=(0, 1)), shift_dim_both) + xp_assert_close(fft.ifftshift(shift_dim_both, axes=(0, 1)), freqs) + xp_assert_close(fft.fftshift(freqs, axes=[0, 1]), shift_dim_both) + xp_assert_close(fft.ifftshift(shift_dim_both, axes=[0, 1]), freqs) + + # axes=None (default) shift in all dimensions + xp_assert_close(fft.fftshift(freqs, axes=None), shift_dim_both) + xp_assert_close(fft.ifftshift(shift_dim_both, axes=None), freqs) + xp_assert_close(fft.fftshift(freqs), shift_dim_both) + xp_assert_close(fft.ifftshift(shift_dim_both), freqs) + + +@skip_if_array_api('array_api_strict', 'cupy', + reasons=['fft not yet implemented by array-api-strict', + 'cupy.fft not yet implemented by array-api-compat']) +class TestFFTFreq: + + def test_definition(self, xp): + device = SCIPY_DEVICE + try: + x = xp.asarray([0, 1, 2, 3, 4, -4, -3, -2, -1], + dtype=xp.float64, device=device) + x2 = xp.asarray([0, 1, 2, 3, 4, -5, -4, -3, -2, -1], + dtype=xp.float64, device=device) + except TypeError: + x = xp.asarray([0, 1, 2, 3, 4, -4, -3, -2, -1], dtype=xp.float64) + x2 = xp.asarray([0, 1, 2, 3, 4, -5, -4, -3, -2, -1], + dtype=xp.float64) + + y = xp.asarray(9 * fft.fftfreq(9, xp=xp), dtype=xp.float64) + xp_assert_close(y, x) + y = xp.asarray(9 * xp.pi * fft.fftfreq(9, xp.pi, xp=xp), dtype=xp.float64) + xp_assert_close(y, x) + + y = xp.asarray(10 * fft.fftfreq(10, xp=xp), dtype=xp.float64) + xp_assert_close(y, x2) + y = xp.asarray(10 * xp.pi * fft.fftfreq(10, xp.pi, xp=xp), dtype=xp.float64) + xp_assert_close(y, x2) + + +@skip_if_array_api('array_api_strict', 'cupy', + reasons=['fft not yet implemented by array-api-strict', + 'cupy.fft not yet implemented by array-api-compat']) +class TestRFFTFreq: + + def test_definition(self, xp): + device = SCIPY_DEVICE + try: + x = xp.asarray([0, 1, 2, 3, 4], dtype=xp.float64, device=device) + x2 = xp.asarray([0, 1, 2, 3, 4, 5], dtype=xp.float64, device=device) + except TypeError: + # work around the `device` keyword not being implemented in numpy yet + x = xp.asarray([0, 1, 2, 3, 4], dtype=xp.float64) + x2 = xp.asarray([0, 1, 2, 3, 4, 5], dtype=xp.float64) + + y = xp.asarray(9 * fft.rfftfreq(9, xp=xp), dtype=xp.float64) + xp_assert_close(y, x) + y = xp.asarray(9 * xp.pi * fft.rfftfreq(9, xp.pi, xp=xp), dtype=xp.float64) + xp_assert_close(y, x) + + y = xp.asarray(10 * fft.rfftfreq(10, xp=xp), dtype=xp.float64) + xp_assert_close(y, x2) + y = xp.asarray(10 * xp.pi * fft.rfftfreq(10, xp.pi, xp=xp), dtype=xp.float64) + xp_assert_close(y, x2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_multithreading.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_multithreading.py new file mode 100644 index 0000000000000000000000000000000000000000..e771aff63b173d2e939913ccc26467e80ba670c8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_multithreading.py @@ -0,0 +1,83 @@ +from scipy import fft +import numpy as np +import pytest +from numpy.testing import assert_allclose +import multiprocessing +import os + + +@pytest.fixture(scope='module') +def x(): + return np.random.randn(512, 128) # Must be large enough to qualify for mt + + +@pytest.mark.parametrize("func", [ + fft.fft, fft.ifft, fft.fft2, fft.ifft2, fft.fftn, fft.ifftn, + fft.rfft, fft.irfft, fft.rfft2, fft.irfft2, fft.rfftn, fft.irfftn, + fft.hfft, fft.ihfft, fft.hfft2, fft.ihfft2, fft.hfftn, fft.ihfftn, + fft.dct, fft.idct, fft.dctn, fft.idctn, + fft.dst, fft.idst, fft.dstn, fft.idstn, +]) +@pytest.mark.parametrize("workers", [2, -1]) +def test_threaded_same(x, func, workers): + expected = func(x, workers=1) + actual = func(x, workers=workers) + assert_allclose(actual, expected) + + +def _mt_fft(x): + return fft.fft(x, workers=2) + + +def test_mixed_threads_processes(x): + # Test that the fft threadpool is safe to use before & after fork + + expect = fft.fft(x, workers=2) + + with multiprocessing.Pool(2) as p: + res = p.map(_mt_fft, [x for _ in range(4)]) + + for r in res: + assert_allclose(r, expect) + + fft.fft(x, workers=2) + + +def test_invalid_workers(x): + cpus = os.cpu_count() + + fft.ifft([1], workers=-cpus) + + with pytest.raises(ValueError, match='workers must not be zero'): + fft.fft(x, workers=0) + + with pytest.raises(ValueError, match='workers value out of range'): + fft.ifft(x, workers=-cpus-1) + + +def test_set_get_workers(): + cpus = os.cpu_count() + assert fft.get_workers() == 1 + with fft.set_workers(4): + assert fft.get_workers() == 4 + + with fft.set_workers(-1): + assert fft.get_workers() == cpus + + assert fft.get_workers() == 4 + + assert fft.get_workers() == 1 + + with fft.set_workers(-cpus): + assert fft.get_workers() == 1 + + +def test_set_workers_invalid(): + + with pytest.raises(ValueError, match='workers must not be zero'): + with fft.set_workers(0): + pass + + with pytest.raises(ValueError, match='workers value out of range'): + with fft.set_workers(-os.cpu_count()-1): + pass diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_real_transforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_real_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..9eb5b52d77067771d73cc4f9d418634618d8d469 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fft/tests/test_real_transforms.py @@ -0,0 +1,234 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_array_equal +import pytest +import math + +from scipy.fft import dct, idct, dctn, idctn, dst, idst, dstn, idstn +import scipy.fft as fft +from scipy import fftpack +from scipy.conftest import array_api_compatible +from scipy._lib._array_api import copy, xp_assert_close + +pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_if_array_api")] +skip_if_array_api = pytest.mark.skip_if_array_api + +SQRT_2 = math.sqrt(2) + +# scipy.fft wraps the fftpack versions but with normalized inverse transforms. +# So, the forward transforms and definitions are already thoroughly tested in +# fftpack/test_real_transforms.py + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("forward, backward", [(dct, idct), (dst, idst)]) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +@pytest.mark.parametrize("n", [2, 3, 4, 5, 10, 16]) +@pytest.mark.parametrize("axis", [0, 1]) +@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward']) +@pytest.mark.parametrize("orthogonalize", [False, True]) +def test_identity_1d(forward, backward, type, n, axis, norm, orthogonalize, xp): + # Test the identity f^-1(f(x)) == x + x = xp.asarray(np.random.rand(n, n)) + + y = forward(x, type, axis=axis, norm=norm, orthogonalize=orthogonalize) + z = backward(y, type, axis=axis, norm=norm, orthogonalize=orthogonalize) + xp_assert_close(z, x) + + pad = [(0, 0)] * 2 + pad[axis] = (0, 4) + + y2 = xp.asarray(np.pad(np.asarray(y), pad, mode='edge')) + z2 = backward(y2, type, n, axis, norm, orthogonalize=orthogonalize) + xp_assert_close(z2, x) + + +@skip_if_array_api(np_only=True, + reasons=['`overwrite_x` only supported for NumPy backend.']) +@pytest.mark.parametrize("forward, backward", [(dct, idct), (dst, idst)]) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +@pytest.mark.parametrize("dtype", [np.float16, np.float32, np.float64, + np.complex64, np.complex128]) +@pytest.mark.parametrize("axis", [0, 1]) +@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward']) +@pytest.mark.parametrize("overwrite_x", [True, False]) +def test_identity_1d_overwrite(forward, backward, type, dtype, axis, norm, + overwrite_x): + # Test the identity f^-1(f(x)) == x + x = np.random.rand(7, 8).astype(dtype) + x_orig = x.copy() + + y = forward(x, type, axis=axis, norm=norm, overwrite_x=overwrite_x) + y_orig = y.copy() + z = backward(y, type, axis=axis, norm=norm, overwrite_x=overwrite_x) + if not overwrite_x: + assert_allclose(z, x, rtol=1e-6, atol=1e-6) + assert_array_equal(x, x_orig) + assert_array_equal(y, y_orig) + else: + assert_allclose(z, x_orig, rtol=1e-6, atol=1e-6) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("forward, backward", [(dctn, idctn), (dstn, idstn)]) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +@pytest.mark.parametrize("shape, axes", + [ + ((4, 4), 0), + ((4, 4), 1), + ((4, 4), None), + ((4, 4), (0, 1)), + ((10, 12), None), + ((10, 12), (0, 1)), + ((4, 5, 6), None), + ((4, 5, 6), 1), + ((4, 5, 6), (0, 2)), + ]) +@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward']) +@pytest.mark.parametrize("orthogonalize", [False, True]) +def test_identity_nd(forward, backward, type, shape, axes, norm, + orthogonalize, xp): + # Test the identity f^-1(f(x)) == x + + x = xp.asarray(np.random.random(shape)) + + if axes is not None: + shape = np.take(shape, axes) + + y = forward(x, type, axes=axes, norm=norm, orthogonalize=orthogonalize) + z = backward(y, type, axes=axes, norm=norm, orthogonalize=orthogonalize) + xp_assert_close(z, x) + + if axes is None: + pad = [(0, 4)] * x.ndim + elif isinstance(axes, int): + pad = [(0, 0)] * x.ndim + pad[axes] = (0, 4) + else: + pad = [(0, 0)] * x.ndim + + for a in axes: + pad[a] = (0, 4) + + # TODO write an array-agnostic pad + y2 = xp.asarray(np.pad(np.asarray(y), pad, mode='edge')) + z2 = backward(y2, type, shape, axes, norm, orthogonalize=orthogonalize) + xp_assert_close(z2, x) + + +@skip_if_array_api(np_only=True, + reasons=['`overwrite_x` only supported for NumPy backend.']) +@pytest.mark.parametrize("forward, backward", [(dctn, idctn), (dstn, idstn)]) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +@pytest.mark.parametrize("shape, axes", + [ + ((4, 5), 0), + ((4, 5), 1), + ((4, 5), None), + ]) +@pytest.mark.parametrize("dtype", [np.float16, np.float32, np.float64, + np.complex64, np.complex128]) +@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward']) +@pytest.mark.parametrize("overwrite_x", [False, True]) +def test_identity_nd_overwrite(forward, backward, type, shape, axes, dtype, + norm, overwrite_x): + # Test the identity f^-1(f(x)) == x + + x = np.random.random(shape).astype(dtype) + x_orig = x.copy() + + if axes is not None: + shape = np.take(shape, axes) + + y = forward(x, type, axes=axes, norm=norm) + y_orig = y.copy() + z = backward(y, type, axes=axes, norm=norm) + if overwrite_x: + assert_allclose(z, x_orig, rtol=1e-6, atol=1e-6) + else: + assert_allclose(z, x, rtol=1e-6, atol=1e-6) + assert_array_equal(x, x_orig) + assert_array_equal(y, y_orig) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("func", ['dct', 'dst', 'dctn', 'dstn']) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward']) +def test_fftpack_equivalience(func, type, norm, xp): + x = np.random.rand(8, 16) + fftpack_res = xp.asarray(getattr(fftpack, func)(x, type, norm=norm)) + x = xp.asarray(x) + fft_res = getattr(fft, func)(x, type, norm=norm) + + xp_assert_close(fft_res, fftpack_res) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("func", [dct, dst, dctn, dstn]) +@pytest.mark.parametrize("type", [1, 2, 3, 4]) +def test_orthogonalize_default(func, type, xp): + # Test orthogonalize is the default when norm="ortho", but not otherwise + x = xp.asarray(np.random.rand(100)) + + for norm, ortho in [ + ("forward", False), + ("backward", False), + ("ortho", True), + ]: + a = func(x, type=type, norm=norm, orthogonalize=ortho) + b = func(x, type=type, norm=norm) + xp_assert_close(a, b) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"]) +@pytest.mark.parametrize("func, type", [ + (dct, 4), (dst, 1), (dst, 4)]) +def test_orthogonalize_noop(func, type, norm, xp): + # Transforms where orthogonalize is a no-op + x = xp.asarray(np.random.rand(100)) + y1 = func(x, type=type, norm=norm, orthogonalize=True) + y2 = func(x, type=type, norm=norm, orthogonalize=False) + xp_assert_close(y1, y2) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"]) +def test_orthogonalize_dct1(norm, xp): + x = xp.asarray(np.random.rand(100)) + + x2 = copy(x, xp=xp) + x2[0] *= SQRT_2 + x2[-1] *= SQRT_2 + + y1 = dct(x, type=1, norm=norm, orthogonalize=True) + y2 = dct(x2, type=1, norm=norm, orthogonalize=False) + + y2[0] /= SQRT_2 + y2[-1] /= SQRT_2 + xp_assert_close(y1, y2) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"]) +@pytest.mark.parametrize("func", [dct, dst]) +def test_orthogonalize_dcst2(func, norm, xp): + x = xp.asarray(np.random.rand(100)) + y1 = func(x, type=2, norm=norm, orthogonalize=True) + y2 = func(x, type=2, norm=norm, orthogonalize=False) + + y2[0 if func == dct else -1] /= SQRT_2 + xp_assert_close(y1, y2) + + +@skip_if_array_api(cpu_only=True) +@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"]) +@pytest.mark.parametrize("func", [dct, dst]) +def test_orthogonalize_dcst3(func, norm, xp): + x = xp.asarray(np.random.rand(100)) + x2 = copy(x, xp=xp) + x2[0 if func == dct else -1] *= SQRT_2 + + y1 = func(x, type=3, norm=norm, orthogonalize=True) + y2 = func(x2, type=3, norm=norm, orthogonalize=False) + xp_assert_close(y1, y2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_import.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_import.py new file mode 100644 index 0000000000000000000000000000000000000000..8a978d9651e54c3dd2b3ae5e15ac6635f08d8cc1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_import.py @@ -0,0 +1,31 @@ +"""Test possibility of patching fftpack with pyfftw. + +No module source outside of scipy.fftpack should contain an import of +the form `from scipy.fftpack import ...`, so that a simple replacement +of scipy.fftpack by the corresponding fftw interface completely swaps +the two FFT implementations. + +Because this simply inspects source files, we only need to run the test +on one version of Python. +""" + + +from pathlib import Path +import re +import tokenize +from numpy.testing import assert_ +import scipy + +class TestFFTPackImport: + def test_fftpack_import(self): + base = Path(scipy.__file__).parent + regexp = r"\s*from.+\.fftpack import .*\n" + for path in base.rglob("*.py"): + if base / "fftpack" in path.parents: + continue + # use tokenize to auto-detect encoding on systems where no + # default encoding is defined (e.g., LANG='C') + with tokenize.open(str(path)) as file: + assert_(all(not re.fullmatch(regexp, line) + for line in file), + f"{path} contains an import from fftpack") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py new file mode 100644 index 0000000000000000000000000000000000000000..cec131caced4ccf9cf7c34255f7693769e2ebb12 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py @@ -0,0 +1,380 @@ +# Created by Pearu Peterson, September 2002 + +__usage__ = """ +Build fftpack: + python setup_fftpack.py build +Run tests if scipy is installed: + python -c 'import scipy;scipy.fftpack.test()' +Run tests if fftpack is not installed: + python tests/test_pseudo_diffs.py [] +""" + +from numpy.testing import (assert_equal, assert_almost_equal, + assert_array_almost_equal) +from scipy.fftpack import (diff, fft, ifft, tilbert, itilbert, hilbert, + ihilbert, shift, fftfreq, cs_diff, sc_diff, + ss_diff, cc_diff) + +import numpy as np +from numpy import arange, sin, cos, pi, exp, tanh, sum, sign +from numpy.random import random + + +def direct_diff(x,k=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*2j*pi/period*n + if k < 0: + w = 1 / w**k + w[0] = 0.0 + else: + w = w**k + if n > 2000: + w[250:n-250] = 0.0 + return ifft(w*fx).real + + +def direct_tilbert(x,h=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*h*2*pi/period*n + w[0] = 1 + w = 1j/tanh(w) + w[0] = 0j + return ifft(w*fx) + + +def direct_itilbert(x,h=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*h*2*pi/period*n + w = -1j*tanh(w) + return ifft(w*fx) + + +def direct_hilbert(x): + fx = fft(x) + n = len(fx) + w = fftfreq(n)*n + w = 1j*sign(w) + return ifft(w*fx) + + +def direct_ihilbert(x): + return -direct_hilbert(x) + + +def direct_shift(x,a,period=None): + n = len(x) + if period is None: + k = fftfreq(n)*1j*n + else: + k = fftfreq(n)*2j*pi/period*n + return ifft(fft(x)*exp(k*a)).real + + +class TestDiff: + + def test_definition(self): + for n in [16,17,64,127,32]: + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x)),direct_diff(sin(x))) + assert_array_almost_equal(diff(sin(x),2),direct_diff(sin(x),2)) + assert_array_almost_equal(diff(sin(x),3),direct_diff(sin(x),3)) + assert_array_almost_equal(diff(sin(x),4),direct_diff(sin(x),4)) + assert_array_almost_equal(diff(sin(x),5),direct_diff(sin(x),5)) + assert_array_almost_equal(diff(sin(2*x),3),direct_diff(sin(2*x),3)) + assert_array_almost_equal(diff(sin(2*x),4),direct_diff(sin(2*x),4)) + assert_array_almost_equal(diff(cos(x)),direct_diff(cos(x))) + assert_array_almost_equal(diff(cos(x),2),direct_diff(cos(x),2)) + assert_array_almost_equal(diff(cos(x),3),direct_diff(cos(x),3)) + assert_array_almost_equal(diff(cos(x),4),direct_diff(cos(x),4)) + assert_array_almost_equal(diff(cos(2*x)),direct_diff(cos(2*x))) + assert_array_almost_equal(diff(sin(x*n/8)),direct_diff(sin(x*n/8))) + assert_array_almost_equal(diff(cos(x*n/8)),direct_diff(cos(x*n/8))) + for k in range(5): + assert_array_almost_equal(diff(sin(4*x),k),direct_diff(sin(4*x),k)) + assert_array_almost_equal(diff(cos(4*x),k),direct_diff(cos(4*x),k)) + + def test_period(self): + for n in [17,64]: + x = arange(n)/float(n) + assert_array_almost_equal(diff(sin(2*pi*x),period=1), + 2*pi*cos(2*pi*x)) + assert_array_almost_equal(diff(sin(2*pi*x),3,period=1), + -(2*pi)**3*cos(2*pi*x)) + + def test_sin(self): + for n in [32,64,77]: + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x)),cos(x)) + assert_array_almost_equal(diff(cos(x)),-sin(x)) + assert_array_almost_equal(diff(sin(x),2),-sin(x)) + assert_array_almost_equal(diff(sin(x),4),sin(x)) + assert_array_almost_equal(diff(sin(4*x)),4*cos(4*x)) + assert_array_almost_equal(diff(sin(sin(x))),cos(x)*cos(sin(x))) + + def test_expr(self): + for n in [64,77,100,128,256,512,1024,2048,4096,8192][:5]: + x = arange(n)*2*pi/n + f = sin(x)*cos(4*x)+exp(sin(3*x)) + df = cos(x)*cos(4*x)-4*sin(x)*sin(4*x)+3*cos(3*x)*exp(sin(3*x)) + ddf = -17*sin(x)*cos(4*x)-8*cos(x)*sin(4*x)\ + - 9*sin(3*x)*exp(sin(3*x))+9*cos(3*x)**2*exp(sin(3*x)) + d1 = diff(f) + assert_array_almost_equal(d1,df) + assert_array_almost_equal(diff(df),ddf) + assert_array_almost_equal(diff(f,2),ddf) + assert_array_almost_equal(diff(ddf,-1),df) + + def test_expr_large(self): + for n in [2048,4096]: + x = arange(n)*2*pi/n + f = sin(x)*cos(4*x)+exp(sin(3*x)) + df = cos(x)*cos(4*x)-4*sin(x)*sin(4*x)+3*cos(3*x)*exp(sin(3*x)) + ddf = -17*sin(x)*cos(4*x)-8*cos(x)*sin(4*x)\ + - 9*sin(3*x)*exp(sin(3*x))+9*cos(3*x)**2*exp(sin(3*x)) + assert_array_almost_equal(diff(f),df) + assert_array_almost_equal(diff(df),ddf) + assert_array_almost_equal(diff(ddf,-1),df) + assert_array_almost_equal(diff(f,2),ddf) + + def test_int(self): + n = 64 + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x),-1),-cos(x)) + assert_array_almost_equal(diff(sin(x),-2),-sin(x)) + assert_array_almost_equal(diff(sin(x),-4),sin(x)) + assert_array_almost_equal(diff(2*cos(2*x),-1),sin(2*x)) + + def test_random_even(self): + for k in [0,2,4,6]: + for n in [60,32,64,56,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + def test_random_odd(self): + for k in [0,1,2,3,4,5,6]: + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + def test_zero_nyquist(self): + for k in [0,1,2,3,4,5,6]: + for n in [32,33,64,56,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + +class TestTilbert: + + def test_definition(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = tilbert(sin(x),h) + y1 = direct_tilbert(sin(x),h) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(tilbert(sin(x),h), + direct_tilbert(sin(x),h)) + assert_array_almost_equal(tilbert(sin(2*x),h), + direct_tilbert(sin(2*x),h)) + + def test_random_even(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [32,64,56]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(direct_tilbert(direct_itilbert(f,h),h),f) + + def test_random_odd(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(itilbert(tilbert(f,h),h),f) + assert_array_almost_equal(tilbert(itilbert(f,h),h),f) + + +class TestITilbert: + + def test_definition(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = itilbert(sin(x),h) + y1 = direct_itilbert(sin(x),h) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(itilbert(sin(x),h), + direct_itilbert(sin(x),h)) + assert_array_almost_equal(itilbert(sin(2*x),h), + direct_itilbert(sin(2*x),h)) + + +class TestHilbert: + + def test_definition(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = hilbert(sin(x)) + y1 = direct_hilbert(sin(x)) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(hilbert(sin(2*x)), + direct_hilbert(sin(2*x))) + + def test_tilbert_relation(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + f = sin(x)+cos(2*x)*sin(x) + y = hilbert(f) + y1 = direct_hilbert(f) + assert_array_almost_equal(y,y1) + y2 = tilbert(f,h=10) + assert_array_almost_equal(y,y2) + + def test_random_odd(self): + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(ihilbert(hilbert(f)),f) + assert_array_almost_equal(hilbert(ihilbert(f)),f) + + def test_random_even(self): + for n in [32,64,56]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(direct_hilbert(direct_ihilbert(f)),f) + assert_array_almost_equal(hilbert(ihilbert(f)),f) + + +class TestIHilbert: + + def test_definition(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = ihilbert(sin(x)) + y1 = direct_ihilbert(sin(x)) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(ihilbert(sin(2*x)), + direct_ihilbert(sin(2*x))) + + def test_itilbert_relation(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + f = sin(x)+cos(2*x)*sin(x) + y = ihilbert(f) + y1 = direct_ihilbert(f) + assert_array_almost_equal(y,y1) + y2 = itilbert(f,h=10) + assert_array_almost_equal(y,y2) + + +class TestShift: + + def test_definition(self): + for n in [18,17,64,127,32,2048,256]: + x = arange(n)*2*pi/n + for a in [0.1,3]: + assert_array_almost_equal(shift(sin(x),a),direct_shift(sin(x),a)) + assert_array_almost_equal(shift(sin(x),a),sin(x+a)) + assert_array_almost_equal(shift(cos(x),a),cos(x+a)) + assert_array_almost_equal(shift(cos(2*x)+sin(x),a), + cos(2*(x+a))+sin(x+a)) + assert_array_almost_equal(shift(exp(sin(x)),a),exp(sin(x+a))) + assert_array_almost_equal(shift(sin(x),2*pi),sin(x)) + assert_array_almost_equal(shift(sin(x),pi),-sin(x)) + assert_array_almost_equal(shift(sin(x),pi/2),cos(x)) + + +class TestOverwrite: + """Check input overwrite behavior """ + + real_dtypes = (np.float32, np.float64) + dtypes = real_dtypes + (np.complex64, np.complex128) + + def _check(self, x, routine, *args, **kwargs): + x2 = x.copy() + routine(x2, *args, **kwargs) + sig = routine.__name__ + if args: + sig += repr(args) + if kwargs: + sig += repr(kwargs) + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + def _check_1d(self, routine, dtype, shape, *args, **kwargs): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + self._check(data, routine, *args, **kwargs) + + def test_diff(self): + for dtype in self.dtypes: + self._check_1d(diff, dtype, (16,)) + + def test_tilbert(self): + for dtype in self.dtypes: + self._check_1d(tilbert, dtype, (16,), 1.6) + + def test_itilbert(self): + for dtype in self.dtypes: + self._check_1d(itilbert, dtype, (16,), 1.6) + + def test_hilbert(self): + for dtype in self.dtypes: + self._check_1d(hilbert, dtype, (16,)) + + def test_cs_diff(self): + for dtype in self.dtypes: + self._check_1d(cs_diff, dtype, (16,), 1.0, 4.0) + + def test_sc_diff(self): + for dtype in self.dtypes: + self._check_1d(sc_diff, dtype, (16,), 1.0, 4.0) + + def test_ss_diff(self): + for dtype in self.dtypes: + self._check_1d(ss_diff, dtype, (16,), 1.0, 4.0) + + def test_cc_diff(self): + for dtype in self.dtypes: + self._check_1d(cc_diff, dtype, (16,), 1.0, 4.0) + + def test_shift(self): + for dtype in self.dtypes: + self._check_1d(shift, dtype, (16,), 1.0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9ea5ba9d88b91c252e7533249aba47998a24610d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/__init__.py @@ -0,0 +1,201 @@ +""" +======================================== +Interpolation (:mod:`scipy.interpolate`) +======================================== + +.. currentmodule:: scipy.interpolate + +Sub-package for objects used in interpolation. + +As listed below, this sub-package contains spline functions and classes, +1-D and multidimensional (univariate and multivariate) +interpolation classes, Lagrange and Taylor polynomial interpolators, and +wrappers for `FITPACK `__ +and DFITPACK functions. + +Univariate interpolation +======================== + +.. autosummary:: + :toctree: generated/ + + interp1d + BarycentricInterpolator + KroghInterpolator + barycentric_interpolate + krogh_interpolate + pchip_interpolate + CubicHermiteSpline + PchipInterpolator + Akima1DInterpolator + CubicSpline + PPoly + BPoly + + +Multivariate interpolation +========================== + +Unstructured data: + +.. autosummary:: + :toctree: generated/ + + griddata + LinearNDInterpolator + NearestNDInterpolator + CloughTocher2DInterpolator + RBFInterpolator + Rbf + interp2d + +For data on a grid: + +.. autosummary:: + :toctree: generated/ + + interpn + RegularGridInterpolator + RectBivariateSpline + +.. seealso:: + + `scipy.ndimage.map_coordinates` + +Tensor product polynomials: + +.. autosummary:: + :toctree: generated/ + + NdPPoly + NdBSpline + +1-D Splines +=========== + +.. autosummary:: + :toctree: generated/ + + BSpline + make_interp_spline + make_lsq_spline + make_smoothing_spline + +Functional interface to FITPACK routines: + +.. autosummary:: + :toctree: generated/ + + splrep + splprep + splev + splint + sproot + spalde + splder + splantider + insert + +Object-oriented FITPACK interface: + +.. autosummary:: + :toctree: generated/ + + UnivariateSpline + InterpolatedUnivariateSpline + LSQUnivariateSpline + + + +2-D Splines +=========== + +For data on a grid: + +.. autosummary:: + :toctree: generated/ + + RectBivariateSpline + RectSphereBivariateSpline + +For unstructured data: + +.. autosummary:: + :toctree: generated/ + + BivariateSpline + SmoothBivariateSpline + SmoothSphereBivariateSpline + LSQBivariateSpline + LSQSphereBivariateSpline + +Low-level interface to FITPACK functions: + +.. autosummary:: + :toctree: generated/ + + bisplrep + bisplev + +Additional tools +================ + +.. autosummary:: + :toctree: generated/ + + lagrange + approximate_taylor_polynomial + pade + +.. seealso:: + + `scipy.ndimage.map_coordinates`, + `scipy.ndimage.spline_filter`, + `scipy.signal.resample`, + `scipy.signal.bspline`, + `scipy.signal.gauss_spline`, + `scipy.signal.qspline1d`, + `scipy.signal.cspline1d`, + `scipy.signal.qspline1d_eval`, + `scipy.signal.cspline1d_eval`, + `scipy.signal.qspline2d`, + `scipy.signal.cspline2d`. + +``pchip`` is an alias of `PchipInterpolator` for backward compatibility +(should not be used in new code). +""" +from ._interpolate import * +from ._fitpack_py import * + +# New interface to fitpack library: +from ._fitpack2 import * + +from ._rbf import Rbf + +from ._rbfinterp import * + +from ._polyint import * + +from ._cubic import * + +from ._ndgriddata import * + +from ._bsplines import * + +from ._pade import * + +from ._rgi import * + +from ._ndbspline import NdBSpline + +# Deprecated namespaces, to be removed in v2.0.0 +from . import fitpack, fitpack2, interpolate, ndgriddata, polyint, rbf + +__all__ = [s for s in dir() if not s.startswith('_')] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester + +# Backward compatibility +pchip = PchipInterpolator diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_bsplines.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_bsplines.py new file mode 100644 index 0000000000000000000000000000000000000000..8cc6eb5411ede7cc7ca1b597c41125b2c0220af4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_bsplines.py @@ -0,0 +1,2215 @@ +import operator +from math import prod + +import numpy as np +from scipy._lib._util import normalize_axis_index +from scipy.linalg import (get_lapack_funcs, LinAlgError, + cholesky_banded, cho_solve_banded, + solve, solve_banded) +from scipy.optimize import minimize_scalar +from . import _bspl +from . import _fitpack_impl +from scipy.sparse import csr_array +from scipy.special import poch +from itertools import combinations + +__all__ = ["BSpline", "make_interp_spline", "make_lsq_spline", + "make_smoothing_spline"] + + +def _get_dtype(dtype): + """Return np.complex128 for complex dtypes, np.float64 otherwise.""" + if np.issubdtype(dtype, np.complexfloating): + return np.complex128 + else: + return np.float64 + + +def _as_float_array(x, check_finite=False): + """Convert the input into a C contiguous float array. + + NB: Upcasts half- and single-precision floats to double precision. + """ + x = np.ascontiguousarray(x) + dtyp = _get_dtype(x.dtype) + x = x.astype(dtyp, copy=False) + if check_finite and not np.isfinite(x).all(): + raise ValueError("Array must not contain infs or nans.") + return x + + +def _dual_poly(j, k, t, y): + """ + Dual polynomial of the B-spline B_{j,k,t} - + polynomial which is associated with B_{j,k,t}: + $p_{j,k}(y) = (y - t_{j+1})(y - t_{j+2})...(y - t_{j+k})$ + """ + if k == 0: + return 1 + return np.prod([(y - t[j + i]) for i in range(1, k + 1)]) + + +def _diff_dual_poly(j, k, y, d, t): + """ + d-th derivative of the dual polynomial $p_{j,k}(y)$ + """ + if d == 0: + return _dual_poly(j, k, t, y) + if d == k: + return poch(1, k) + comb = list(combinations(range(j + 1, j + k + 1), d)) + res = 0 + for i in range(len(comb) * len(comb[0])): + res += np.prod([(y - t[j + p]) for p in range(1, k + 1) + if (j + p) not in comb[i//d]]) + return res + + +class BSpline: + r"""Univariate spline in the B-spline basis. + + .. math:: + + S(x) = \sum_{j=0}^{n-1} c_j B_{j, k; t}(x) + + where :math:`B_{j, k; t}` are B-spline basis functions of degree `k` + and knots `t`. + + Parameters + ---------- + t : ndarray, shape (n+k+1,) + knots + c : ndarray, shape (>=n, ...) + spline coefficients + k : int + B-spline degree + extrapolate : bool or 'periodic', optional + whether to extrapolate beyond the base interval, ``t[k] .. t[n]``, + or to return nans. + If True, extrapolates the first and last polynomial pieces of b-spline + functions active on the base interval. + If 'periodic', periodic extrapolation is used. + Default is True. + axis : int, optional + Interpolation axis. Default is zero. + + Attributes + ---------- + t : ndarray + knot vector + c : ndarray + spline coefficients + k : int + spline degree + extrapolate : bool + If True, extrapolates the first and last polynomial pieces of b-spline + functions active on the base interval. + axis : int + Interpolation axis. + tck : tuple + A read-only equivalent of ``(self.t, self.c, self.k)`` + + Methods + ------- + __call__ + basis_element + derivative + antiderivative + integrate + insert_knot + construct_fast + design_matrix + from_power_basis + + Notes + ----- + B-spline basis elements are defined via + + .. math:: + + B_{i, 0}(x) = 1, \textrm{if $t_i \le x < t_{i+1}$, otherwise $0$,} + + B_{i, k}(x) = \frac{x - t_i}{t_{i+k} - t_i} B_{i, k-1}(x) + + \frac{t_{i+k+1} - x}{t_{i+k+1} - t_{i+1}} B_{i+1, k-1}(x) + + **Implementation details** + + - At least ``k+1`` coefficients are required for a spline of degree `k`, + so that ``n >= k+1``. Additional coefficients, ``c[j]`` with + ``j > n``, are ignored. + + - B-spline basis elements of degree `k` form a partition of unity on the + *base interval*, ``t[k] <= x <= t[n]``. + + + Examples + -------- + + Translating the recursive definition of B-splines into Python code, we have: + + >>> def B(x, k, i, t): + ... if k == 0: + ... return 1.0 if t[i] <= x < t[i+1] else 0.0 + ... if t[i+k] == t[i]: + ... c1 = 0.0 + ... else: + ... c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t) + ... if t[i+k+1] == t[i+1]: + ... c2 = 0.0 + ... else: + ... c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t) + ... return c1 + c2 + + >>> def bspline(x, t, c, k): + ... n = len(t) - k - 1 + ... assert (n >= k+1) and (len(c) >= n) + ... return sum(c[i] * B(x, k, i, t) for i in range(n)) + + Note that this is an inefficient (if straightforward) way to + evaluate B-splines --- this spline class does it in an equivalent, + but much more efficient way. + + Here we construct a quadratic spline function on the base interval + ``2 <= x <= 4`` and compare with the naive way of evaluating the spline: + + >>> from scipy.interpolate import BSpline + >>> k = 2 + >>> t = [0, 1, 2, 3, 4, 5, 6] + >>> c = [-1, 2, 0, -1] + >>> spl = BSpline(t, c, k) + >>> spl(2.5) + array(1.375) + >>> bspline(2.5, t, c, k) + 1.375 + + Note that outside of the base interval results differ. This is because + `BSpline` extrapolates the first and last polynomial pieces of B-spline + functions active on the base interval. + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> fig, ax = plt.subplots() + >>> xx = np.linspace(1.5, 4.5, 50) + >>> ax.plot(xx, [bspline(x, t, c ,k) for x in xx], 'r-', lw=3, label='naive') + >>> ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline') + >>> ax.grid(True) + >>> ax.legend(loc='best') + >>> plt.show() + + + References + ---------- + .. [1] Tom Lyche and Knut Morken, Spline methods, + http://www.uio.no/studier/emner/matnat/ifi/INF-MAT5340/v05/undervisningsmateriale/ + .. [2] Carl de Boor, A practical guide to splines, Springer, 2001. + + """ + + def __init__(self, t, c, k, extrapolate=True, axis=0): + super().__init__() + + self.k = operator.index(k) + self.c = np.asarray(c) + self.t = np.ascontiguousarray(t, dtype=np.float64) + + if extrapolate == 'periodic': + self.extrapolate = extrapolate + else: + self.extrapolate = bool(extrapolate) + + n = self.t.shape[0] - self.k - 1 + + axis = normalize_axis_index(axis, self.c.ndim) + + # Note that the normalized axis is stored in the object. + self.axis = axis + if axis != 0: + # roll the interpolation axis to be the first one in self.c + # More specifically, the target shape for self.c is (n, ...), + # and axis !=0 means that we have c.shape (..., n, ...) + # ^ + # axis + self.c = np.moveaxis(self.c, axis, 0) + + if k < 0: + raise ValueError("Spline order cannot be negative.") + if self.t.ndim != 1: + raise ValueError("Knot vector must be one-dimensional.") + if n < self.k + 1: + raise ValueError("Need at least %d knots for degree %d" % + (2*k + 2, k)) + if (np.diff(self.t) < 0).any(): + raise ValueError("Knots must be in a non-decreasing order.") + if len(np.unique(self.t[k:n+1])) < 2: + raise ValueError("Need at least two internal knots.") + if not np.isfinite(self.t).all(): + raise ValueError("Knots should not have nans or infs.") + if self.c.ndim < 1: + raise ValueError("Coefficients must be at least 1-dimensional.") + if self.c.shape[0] < n: + raise ValueError("Knots, coefficients and degree are inconsistent.") + + dt = _get_dtype(self.c.dtype) + self.c = np.ascontiguousarray(self.c, dtype=dt) + + @classmethod + def construct_fast(cls, t, c, k, extrapolate=True, axis=0): + """Construct a spline without making checks. + + Accepts same parameters as the regular constructor. Input arrays + `t` and `c` must of correct shape and dtype. + """ + self = object.__new__(cls) + self.t, self.c, self.k = t, c, k + self.extrapolate = extrapolate + self.axis = axis + return self + + @property + def tck(self): + """Equivalent to ``(self.t, self.c, self.k)`` (read-only). + """ + return self.t, self.c, self.k + + @classmethod + def basis_element(cls, t, extrapolate=True): + """Return a B-spline basis element ``B(x | t[0], ..., t[k+1])``. + + Parameters + ---------- + t : ndarray, shape (k+2,) + internal knots + extrapolate : bool or 'periodic', optional + whether to extrapolate beyond the base interval, ``t[0] .. t[k+1]``, + or to return nans. + If 'periodic', periodic extrapolation is used. + Default is True. + + Returns + ------- + basis_element : callable + A callable representing a B-spline basis element for the knot + vector `t`. + + Notes + ----- + The degree of the B-spline, `k`, is inferred from the length of `t` as + ``len(t)-2``. The knot vector is constructed by appending and prepending + ``k+1`` elements to internal knots `t`. + + Examples + -------- + + Construct a cubic B-spline: + + >>> import numpy as np + >>> from scipy.interpolate import BSpline + >>> b = BSpline.basis_element([0, 1, 2, 3, 4]) + >>> k = b.k + >>> b.t[k:-k] + array([ 0., 1., 2., 3., 4.]) + >>> k + 3 + + Construct a quadratic B-spline on ``[0, 1, 1, 2]``, and compare + to its explicit form: + + >>> t = [0, 1, 1, 2] + >>> b = BSpline.basis_element(t) + >>> def f(x): + ... return np.where(x < 1, x*x, (2. - x)**2) + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> x = np.linspace(0, 2, 51) + >>> ax.plot(x, b(x), 'g', lw=3) + >>> ax.plot(x, f(x), 'r', lw=8, alpha=0.4) + >>> ax.grid(True) + >>> plt.show() + + """ + k = len(t) - 2 + t = _as_float_array(t) + t = np.r_[(t[0]-1,) * k, t, (t[-1]+1,) * k] + c = np.zeros_like(t) + c[k] = 1. + return cls.construct_fast(t, c, k, extrapolate) + + @classmethod + def design_matrix(cls, x, t, k, extrapolate=False): + """ + Returns a design matrix as a CSR format sparse array. + + Parameters + ---------- + x : array_like, shape (n,) + Points to evaluate the spline at. + t : array_like, shape (nt,) + Sorted 1D array of knots. + k : int + B-spline degree. + extrapolate : bool or 'periodic', optional + Whether to extrapolate based on the first and last intervals + or raise an error. If 'periodic', periodic extrapolation is used. + Default is False. + + .. versionadded:: 1.10.0 + + Returns + ------- + design_matrix : `csr_array` object + Sparse matrix in CSR format where each row contains all the basis + elements of the input row (first row = basis elements of x[0], + ..., last row = basis elements x[-1]). + + Examples + -------- + Construct a design matrix for a B-spline + + >>> from scipy.interpolate import make_interp_spline, BSpline + >>> import numpy as np + >>> x = np.linspace(0, np.pi * 2, 4) + >>> y = np.sin(x) + >>> k = 3 + >>> bspl = make_interp_spline(x, y, k=k) + >>> design_matrix = bspl.design_matrix(x, bspl.t, k) + >>> design_matrix.toarray() + [[1. , 0. , 0. , 0. ], + [0.2962963 , 0.44444444, 0.22222222, 0.03703704], + [0.03703704, 0.22222222, 0.44444444, 0.2962963 ], + [0. , 0. , 0. , 1. ]] + + Construct a design matrix for some vector of knots + + >>> k = 2 + >>> t = [-1, 0, 1, 2, 3, 4, 5, 6] + >>> x = [1, 2, 3, 4] + >>> design_matrix = BSpline.design_matrix(x, t, k).toarray() + >>> design_matrix + [[0.5, 0.5, 0. , 0. , 0. ], + [0. , 0.5, 0.5, 0. , 0. ], + [0. , 0. , 0.5, 0.5, 0. ], + [0. , 0. , 0. , 0.5, 0.5]] + + This result is equivalent to the one created in the sparse format + + >>> c = np.eye(len(t) - k - 1) + >>> design_matrix_gh = BSpline(t, c, k)(x) + >>> np.allclose(design_matrix, design_matrix_gh, atol=1e-14) + True + + Notes + ----- + .. versionadded:: 1.8.0 + + In each row of the design matrix all the basis elements are evaluated + at the certain point (first row - x[0], ..., last row - x[-1]). + + `nt` is a length of the vector of knots: as far as there are + `nt - k - 1` basis elements, `nt` should be not less than `2 * k + 2` + to have at least `k + 1` basis element. + + Out of bounds `x` raises a ValueError. + """ + x = _as_float_array(x, True) + t = _as_float_array(t, True) + + if extrapolate != 'periodic': + extrapolate = bool(extrapolate) + + if k < 0: + raise ValueError("Spline order cannot be negative.") + if t.ndim != 1 or np.any(t[1:] < t[:-1]): + raise ValueError(f"Expect t to be a 1-D sorted array_like, but " + f"got t={t}.") + # There are `nt - k - 1` basis elements in a BSpline built on the + # vector of knots with length `nt`, so to have at least `k + 1` basis + # elements we need to have at least `2 * k + 2` elements in the vector + # of knots. + if len(t) < 2 * k + 2: + raise ValueError(f"Length t is not enough for k={k}.") + + if extrapolate == 'periodic': + # With periodic extrapolation we map x to the segment + # [t[k], t[n]]. + n = t.size - k - 1 + x = t[k] + (x - t[k]) % (t[n] - t[k]) + extrapolate = False + elif not extrapolate and ( + (min(x) < t[k]) or (max(x) > t[t.shape[0] - k - 1]) + ): + # Checks from `find_interval` function + raise ValueError(f'Out of bounds w/ x = {x}.') + + # Compute number of non-zeros of final CSR array in order to determine + # the dtype of indices and indptr of the CSR array. + n = x.shape[0] + nnz = n * (k + 1) + if nnz < np.iinfo(np.int32).max: + int_dtype = np.int32 + else: + int_dtype = np.int64 + # Preallocate indptr and indices + indices = np.empty(n * (k + 1), dtype=int_dtype) + indptr = np.arange(0, (n + 1) * (k + 1), k + 1, dtype=int_dtype) + + # indptr is not passed to Cython as it is already fully computed + data, indices = _bspl._make_design_matrix( + x, t, k, extrapolate, indices + ) + return csr_array( + (data, indices, indptr), + shape=(x.shape[0], t.shape[0] - k - 1) + ) + + def __call__(self, x, nu=0, extrapolate=None): + """ + Evaluate a spline function. + + Parameters + ---------- + x : array_like + points to evaluate the spline at. + nu : int, optional + derivative to evaluate (default is 0). + extrapolate : bool or 'periodic', optional + whether to extrapolate based on the first and last intervals + or return nans. If 'periodic', periodic extrapolation is used. + Default is `self.extrapolate`. + + Returns + ------- + y : array_like + Shape is determined by replacing the interpolation axis + in the coefficient array with the shape of `x`. + + """ + if extrapolate is None: + extrapolate = self.extrapolate + x = np.asarray(x) + x_shape, x_ndim = x.shape, x.ndim + x = np.ascontiguousarray(x.ravel(), dtype=np.float64) + + # With periodic extrapolation we map x to the segment + # [self.t[k], self.t[n]]. + if extrapolate == 'periodic': + n = self.t.size - self.k - 1 + x = self.t[self.k] + (x - self.t[self.k]) % (self.t[n] - + self.t[self.k]) + extrapolate = False + + out = np.empty((len(x), prod(self.c.shape[1:])), dtype=self.c.dtype) + self._ensure_c_contiguous() + self._evaluate(x, nu, extrapolate, out) + out = out.reshape(x_shape + self.c.shape[1:]) + if self.axis != 0: + # transpose to move the calculated values to the interpolation axis + l = list(range(out.ndim)) + l = l[x_ndim:x_ndim+self.axis] + l[:x_ndim] + l[x_ndim+self.axis:] + out = out.transpose(l) + return out + + def _evaluate(self, xp, nu, extrapolate, out): + _bspl.evaluate_spline(self.t, self.c.reshape(self.c.shape[0], -1), + self.k, xp, nu, extrapolate, out) + + def _ensure_c_contiguous(self): + """ + c and t may be modified by the user. The Cython code expects + that they are C contiguous. + + """ + if not self.t.flags.c_contiguous: + self.t = self.t.copy() + if not self.c.flags.c_contiguous: + self.c = self.c.copy() + + def derivative(self, nu=1): + """Return a B-spline representing the derivative. + + Parameters + ---------- + nu : int, optional + Derivative order. + Default is 1. + + Returns + ------- + b : BSpline object + A new instance representing the derivative. + + See Also + -------- + splder, splantider + + """ + c = self.c + # pad the c array if needed + ct = len(self.t) - len(c) + if ct > 0: + c = np.r_[c, np.zeros((ct,) + c.shape[1:])] + tck = _fitpack_impl.splder((self.t, c, self.k), nu) + return self.construct_fast(*tck, extrapolate=self.extrapolate, + axis=self.axis) + + def antiderivative(self, nu=1): + """Return a B-spline representing the antiderivative. + + Parameters + ---------- + nu : int, optional + Antiderivative order. Default is 1. + + Returns + ------- + b : BSpline object + A new instance representing the antiderivative. + + Notes + ----- + If antiderivative is computed and ``self.extrapolate='periodic'``, + it will be set to False for the returned instance. This is done because + the antiderivative is no longer periodic and its correct evaluation + outside of the initially given x interval is difficult. + + See Also + -------- + splder, splantider + + """ + c = self.c + # pad the c array if needed + ct = len(self.t) - len(c) + if ct > 0: + c = np.r_[c, np.zeros((ct,) + c.shape[1:])] + tck = _fitpack_impl.splantider((self.t, c, self.k), nu) + + if self.extrapolate == 'periodic': + extrapolate = False + else: + extrapolate = self.extrapolate + + return self.construct_fast(*tck, extrapolate=extrapolate, + axis=self.axis) + + def integrate(self, a, b, extrapolate=None): + """Compute a definite integral of the spline. + + Parameters + ---------- + a : float + Lower limit of integration. + b : float + Upper limit of integration. + extrapolate : bool or 'periodic', optional + whether to extrapolate beyond the base interval, + ``t[k] .. t[-k-1]``, or take the spline to be zero outside of the + base interval. If 'periodic', periodic extrapolation is used. + If None (default), use `self.extrapolate`. + + Returns + ------- + I : array_like + Definite integral of the spline over the interval ``[a, b]``. + + Examples + -------- + Construct the linear spline ``x if x < 1 else 2 - x`` on the base + interval :math:`[0, 2]`, and integrate it + + >>> from scipy.interpolate import BSpline + >>> b = BSpline.basis_element([0, 1, 2]) + >>> b.integrate(0, 1) + array(0.5) + + If the integration limits are outside of the base interval, the result + is controlled by the `extrapolate` parameter + + >>> b.integrate(-1, 1) + array(0.0) + >>> b.integrate(-1, 1, extrapolate=False) + array(0.5) + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.grid(True) + >>> ax.axvline(0, c='r', lw=5, alpha=0.5) # base interval + >>> ax.axvline(2, c='r', lw=5, alpha=0.5) + >>> xx = [-1, 1, 2] + >>> ax.plot(xx, b(xx)) + >>> plt.show() + + """ + if extrapolate is None: + extrapolate = self.extrapolate + + # Prepare self.t and self.c. + self._ensure_c_contiguous() + + # Swap integration bounds if needed. + sign = 1 + if b < a: + a, b = b, a + sign = -1 + n = self.t.size - self.k - 1 + + if extrapolate != "periodic" and not extrapolate: + # Shrink the integration interval, if needed. + a = max(a, self.t[self.k]) + b = min(b, self.t[n]) + + if self.c.ndim == 1: + # Fast path: use FITPACK's routine + # (cf _fitpack_impl.splint). + integral = _fitpack_impl.splint(a, b, self.tck) + return integral * sign + + out = np.empty((2, prod(self.c.shape[1:])), dtype=self.c.dtype) + + # Compute the antiderivative. + c = self.c + ct = len(self.t) - len(c) + if ct > 0: + c = np.r_[c, np.zeros((ct,) + c.shape[1:])] + ta, ca, ka = _fitpack_impl.splantider((self.t, c, self.k), 1) + + if extrapolate == 'periodic': + # Split the integral into the part over period (can be several + # of them) and the remaining part. + + ts, te = self.t[self.k], self.t[n] + period = te - ts + interval = b - a + n_periods, left = divmod(interval, period) + + if n_periods > 0: + # Evaluate the difference of antiderivatives. + x = np.asarray([ts, te], dtype=np.float64) + _bspl.evaluate_spline(ta, ca.reshape(ca.shape[0], -1), + ka, x, 0, False, out) + integral = out[1] - out[0] + integral *= n_periods + else: + integral = np.zeros((1, prod(self.c.shape[1:])), + dtype=self.c.dtype) + + # Map a to [ts, te], b is always a + left. + a = ts + (a - ts) % period + b = a + left + + # If b <= te then we need to integrate over [a, b], otherwise + # over [a, te] and from xs to what is remained. + if b <= te: + x = np.asarray([a, b], dtype=np.float64) + _bspl.evaluate_spline(ta, ca.reshape(ca.shape[0], -1), + ka, x, 0, False, out) + integral += out[1] - out[0] + else: + x = np.asarray([a, te], dtype=np.float64) + _bspl.evaluate_spline(ta, ca.reshape(ca.shape[0], -1), + ka, x, 0, False, out) + integral += out[1] - out[0] + + x = np.asarray([ts, ts + b - te], dtype=np.float64) + _bspl.evaluate_spline(ta, ca.reshape(ca.shape[0], -1), + ka, x, 0, False, out) + integral += out[1] - out[0] + else: + # Evaluate the difference of antiderivatives. + x = np.asarray([a, b], dtype=np.float64) + _bspl.evaluate_spline(ta, ca.reshape(ca.shape[0], -1), + ka, x, 0, extrapolate, out) + integral = out[1] - out[0] + + integral *= sign + return integral.reshape(ca.shape[1:]) + + @classmethod + def from_power_basis(cls, pp, bc_type='not-a-knot'): + r""" + Construct a polynomial in the B-spline basis + from a piecewise polynomial in the power basis. + + For now, accepts ``CubicSpline`` instances only. + + Parameters + ---------- + pp : CubicSpline + A piecewise polynomial in the power basis, as created + by ``CubicSpline`` + bc_type : string, optional + Boundary condition type as in ``CubicSpline``: one of the + ``not-a-knot``, ``natural``, ``clamped``, or ``periodic``. + Necessary for construction an instance of ``BSpline`` class. + Default is ``not-a-knot``. + + Returns + ------- + b : BSpline object + A new instance representing the initial polynomial + in the B-spline basis. + + Notes + ----- + .. versionadded:: 1.8.0 + + Accepts only ``CubicSpline`` instances for now. + + The algorithm follows from differentiation + the Marsden's identity [1]: each of coefficients of spline + interpolation function in the B-spline basis is computed as follows: + + .. math:: + + c_j = \sum_{m=0}^{k} \frac{(k-m)!}{k!} + c_{m,i} (-1)^{k-m} D^m p_{j,k}(x_i) + + :math:`c_{m, i}` - a coefficient of CubicSpline, + :math:`D^m p_{j, k}(x_i)` - an m-th defivative of a dual polynomial + in :math:`x_i`. + + ``k`` always equals 3 for now. + + First ``n - 2`` coefficients are computed in :math:`x_i = x_j`, e.g. + + .. math:: + + c_1 = \sum_{m=0}^{k} \frac{(k-1)!}{k!} c_{m,1} D^m p_{j,3}(x_1) + + Last ``nod + 2`` coefficients are computed in ``x[-2]``, + ``nod`` - number of derivatives at the ends. + + For example, consider :math:`x = [0, 1, 2, 3, 4]`, + :math:`y = [1, 1, 1, 1, 1]` and bc_type = ``natural`` + + The coefficients of CubicSpline in the power basis: + + :math:`[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], [1, 1, 1, 1, 1]]` + + The knot vector: :math:`t = [0, 0, 0, 0, 1, 2, 3, 4, 4, 4, 4]` + + In this case + + .. math:: + + c_j = \frac{0!}{k!} c_{3, i} k! = c_{3, i} = 1,~j = 0, ..., 6 + + References + ---------- + .. [1] Tom Lyche and Knut Morken, Spline Methods, 2005, Section 3.1.2 + + """ + from ._cubic import CubicSpline + if not isinstance(pp, CubicSpline): + raise NotImplementedError("Only CubicSpline objects are accepted" + "for now. Got %s instead." % type(pp)) + x = pp.x + coef = pp.c + k = pp.c.shape[0] - 1 + n = x.shape[0] + + if bc_type == 'not-a-knot': + t = _not_a_knot(x, k) + elif bc_type == 'natural' or bc_type == 'clamped': + t = _augknt(x, k) + elif bc_type == 'periodic': + t = _periodic_knots(x, k) + else: + raise TypeError('Unknown boundary condition: %s' % bc_type) + + nod = t.shape[0] - (n + k + 1) # number of derivatives at the ends + c = np.zeros(n + nod, dtype=pp.c.dtype) + for m in range(k + 1): + for i in range(n - 2): + c[i] += poch(k + 1, -m) * coef[m, i]\ + * np.power(-1, k - m)\ + * _diff_dual_poly(i, k, x[i], m, t) + for j in range(n - 2, n + nod): + c[j] += poch(k + 1, -m) * coef[m, n - 2]\ + * np.power(-1, k - m)\ + * _diff_dual_poly(j, k, x[n - 2], m, t) + return cls.construct_fast(t, c, k, pp.extrapolate, pp.axis) + + def insert_knot(self, x, m=1): + """Insert a new knot at `x` of multiplicity `m`. + + Given the knots and coefficients of a B-spline representation, create a + new B-spline with a knot inserted `m` times at point `x`. + + Parameters + ---------- + x : float + The position of the new knot + m : int, optional + The number of times to insert the given knot (its multiplicity). + Default is 1. + + Returns + ------- + spl : BSpline object + A new BSpline object with the new knot inserted. + + Notes + ----- + Based on algorithms from [1]_ and [2]_. + + In case of a periodic spline (``self.extrapolate == "periodic"``) + there must be either at least k interior knots t(j) satisfying + ``t(k+1)>> import numpy as np + >>> from scipy.interpolate import BSpline, make_interp_spline + >>> x = np.linspace(0, 10, 5) + >>> y = np.sin(x) + >>> spl = make_interp_spline(x, y, k=3) + >>> spl.t + array([ 0., 0., 0., 0., 5., 10., 10., 10., 10.]) + + Insert a single knot + + >>> spl_1 = spl.insert_knot(3) + >>> spl_1.t + array([ 0., 0., 0., 0., 3., 5., 10., 10., 10., 10.]) + + Insert a multiple knot + + >>> spl_2 = spl.insert_knot(8, m=3) + >>> spl_2.t + array([ 0., 0., 0., 0., 5., 8., 8., 8., 10., 10., 10., 10.]) + + """ + if x < self.t[self.k] or x > self.t[-self.k-1]: + raise ValueError(f"Cannot insert a knot at {x}.") + if m <= 0: + raise ValueError(f"`m` must be positive, got {m = }.") + + extradim = self.c.shape[1:] + num_extra = prod(extradim) + + tt = self.t.copy() + cc = self.c.copy() + cc = cc.reshape(-1, num_extra) + + for _ in range(m): + tt, cc = _bspl.insert(x, tt, cc, self.k, self.extrapolate == "periodic") + + return self.construct_fast( + tt, cc.reshape((-1,) + extradim), self.k, self.extrapolate, self.axis + ) + + +################################# +# Interpolating spline helpers # +################################# + +def _not_a_knot(x, k): + """Given data x, construct the knot vector w/ not-a-knot BC. + cf de Boor, XIII(12).""" + x = np.asarray(x) + if k % 2 != 1: + raise ValueError("Odd degree for now only. Got %s." % k) + + m = (k - 1) // 2 + t = x[m+1:-m-1] + t = np.r_[(x[0],)*(k+1), t, (x[-1],)*(k+1)] + return t + + +def _augknt(x, k): + """Construct a knot vector appropriate for the order-k interpolation.""" + return np.r_[(x[0],)*k, x, (x[-1],)*k] + + +def _convert_string_aliases(deriv, target_shape): + if isinstance(deriv, str): + if deriv == "clamped": + deriv = [(1, np.zeros(target_shape))] + elif deriv == "natural": + deriv = [(2, np.zeros(target_shape))] + else: + raise ValueError("Unknown boundary condition : %s" % deriv) + return deriv + + +def _process_deriv_spec(deriv): + if deriv is not None: + try: + ords, vals = zip(*deriv) + except TypeError as e: + msg = ("Derivatives, `bc_type`, should be specified as a pair of " + "iterables of pairs of (order, value).") + raise ValueError(msg) from e + else: + ords, vals = [], [] + return np.atleast_1d(ords, vals) + + +def _woodbury_algorithm(A, ur, ll, b, k): + ''' + Solve a cyclic banded linear system with upper right + and lower blocks of size ``(k-1) / 2`` using + the Woodbury formula + + Parameters + ---------- + A : 2-D array, shape(k, n) + Matrix of diagonals of original matrix (see + ``solve_banded`` documentation). + ur : 2-D array, shape(bs, bs) + Upper right block matrix. + ll : 2-D array, shape(bs, bs) + Lower left block matrix. + b : 1-D array, shape(n,) + Vector of constant terms of the system of linear equations. + k : int + B-spline degree. + + Returns + ------- + c : 1-D array, shape(n,) + Solution of the original system of linear equations. + + Notes + ----- + This algorithm works only for systems with banded matrix A plus + a correction term U @ V.T, where the matrix U @ V.T gives upper right + and lower left block of A + The system is solved with the following steps: + 1. New systems of linear equations are constructed: + A @ z_i = u_i, + u_i - column vector of U, + i = 1, ..., k - 1 + 2. Matrix Z is formed from vectors z_i: + Z = [ z_1 | z_2 | ... | z_{k - 1} ] + 3. Matrix H = (1 + V.T @ Z)^{-1} + 4. The system A' @ y = b is solved + 5. x = y - Z @ (H @ V.T @ y) + Also, ``n`` should be greater than ``k``, otherwise corner block + elements will intersect with diagonals. + + Examples + -------- + Consider the case of n = 8, k = 5 (size of blocks - 2 x 2). + The matrix of a system: U: V: + x x x * * a b a b 0 0 0 0 1 0 + x x x x * * c 0 c 0 0 0 0 0 1 + x x x x x * * 0 0 0 0 0 0 0 0 + * x x x x x * 0 0 0 0 0 0 0 0 + * * x x x x x 0 0 0 0 0 0 0 0 + d * * x x x x 0 0 d 0 1 0 0 0 + e f * * x x x 0 0 e f 0 1 0 0 + + References + ---------- + .. [1] William H. Press, Saul A. Teukolsky, William T. Vetterling + and Brian P. Flannery, Numerical Recipes, 2007, Section 2.7.3 + + ''' + k_mod = k - k % 2 + bs = int((k - 1) / 2) + (k + 1) % 2 + + n = A.shape[1] + 1 + U = np.zeros((n - 1, k_mod)) + VT = np.zeros((k_mod, n - 1)) # V transpose + + # upper right block + U[:bs, :bs] = ur + VT[np.arange(bs), np.arange(bs) - bs] = 1 + + # lower left block + U[-bs:, -bs:] = ll + VT[np.arange(bs) - bs, np.arange(bs)] = 1 + + Z = solve_banded((bs, bs), A, U) + + H = solve(np.identity(k_mod) + VT @ Z, np.identity(k_mod)) + + y = solve_banded((bs, bs), A, b) + c = y - Z @ (H @ (VT @ y)) + + return c + + +def _periodic_knots(x, k): + ''' + returns vector of nodes on circle + ''' + xc = np.copy(x) + n = len(xc) + if k % 2 == 0: + dx = np.diff(xc) + xc[1: -1] -= dx[:-1] / 2 + dx = np.diff(xc) + t = np.zeros(n + 2 * k) + t[k: -k] = xc + for i in range(0, k): + # filling first `k` elements in descending order + t[k - i - 1] = t[k - i] - dx[-(i % (n - 1)) - 1] + # filling last `k` elements in ascending order + t[-k + i] = t[-k + i - 1] + dx[i % (n - 1)] + return t + + +def _make_interp_per_full_matr(x, y, t, k): + ''' + Returns a solution of a system for B-spline interpolation with periodic + boundary conditions. First ``k - 1`` rows of matrix are conditions of + periodicity (continuity of ``k - 1`` derivatives at the boundary points). + Last ``n`` rows are interpolation conditions. + RHS is ``k - 1`` zeros and ``n`` ordinates in this case. + + Parameters + ---------- + x : 1-D array, shape (n,) + Values of x - coordinate of a given set of points. + y : 1-D array, shape (n,) + Values of y - coordinate of a given set of points. + t : 1-D array, shape(n+2*k,) + Vector of knots. + k : int + The maximum degree of spline + + Returns + ------- + c : 1-D array, shape (n+k-1,) + B-spline coefficients + + Notes + ----- + ``t`` is supposed to be taken on circle. + + ''' + + x, y, t = map(np.asarray, (x, y, t)) + + n = x.size + # LHS: the collocation matrix + derivatives at edges + matr = np.zeros((n + k - 1, n + k - 1)) + + # derivatives at x[0] and x[-1]: + for i in range(k - 1): + bb = _bspl.evaluate_all_bspl(t, k, x[0], k, nu=i + 1) + matr[i, : k + 1] += bb + bb = _bspl.evaluate_all_bspl(t, k, x[-1], n + k - 1, nu=i + 1)[:-1] + matr[i, -k:] -= bb + + # collocation matrix + for i in range(n): + xval = x[i] + # find interval + if xval == t[k]: + left = k + else: + left = np.searchsorted(t, xval) - 1 + + # fill a row + bb = _bspl.evaluate_all_bspl(t, k, xval, left) + matr[i + k - 1, left-k:left+1] = bb + + # RHS + b = np.r_[[0] * (k - 1), y] + + c = solve(matr, b) + return c + + +def _make_periodic_spline(x, y, t, k, axis): + ''' + Compute the (coefficients of) interpolating B-spline with periodic + boundary conditions. + + Parameters + ---------- + x : array_like, shape (n,) + Abscissas. + y : array_like, shape (n,) + Ordinates. + k : int + B-spline degree. + t : array_like, shape (n + 2 * k,). + Knots taken on a circle, ``k`` on the left and ``k`` on the right + of the vector ``x``. + + Returns + ------- + b : a BSpline object of the degree ``k`` and with knots ``t``. + + Notes + ----- + The original system is formed by ``n + k - 1`` equations where the first + ``k - 1`` of them stand for the ``k - 1`` derivatives continuity on the + edges while the other equations correspond to an interpolating case + (matching all the input points). Due to a special form of knot vector, it + can be proved that in the original system the first and last ``k`` + coefficients of a spline function are the same, respectively. It follows + from the fact that all ``k - 1`` derivatives are equal term by term at ends + and that the matrix of the original system of linear equations is + non-degenerate. So, we can reduce the number of equations to ``n - 1`` + (first ``k - 1`` equations could be reduced). Another trick of this + implementation is cyclic shift of values of B-splines due to equality of + ``k`` unknown coefficients. With this we can receive matrix of the system + with upper right and lower left blocks, and ``k`` diagonals. It allows + to use Woodbury formula to optimize the computations. + + ''' + n = y.shape[0] + + extradim = prod(y.shape[1:]) + y_new = y.reshape(n, extradim) + c = np.zeros((n + k - 1, extradim)) + + # n <= k case is solved with full matrix + if n <= k: + for i in range(extradim): + c[:, i] = _make_interp_per_full_matr(x, y_new[:, i], t, k) + c = np.ascontiguousarray(c.reshape((n + k - 1,) + y.shape[1:])) + return BSpline.construct_fast(t, c, k, extrapolate='periodic', axis=axis) + + nt = len(t) - k - 1 + + # size of block elements + kul = int(k / 2) + + # kl = ku = k + ab = np.zeros((3 * k + 1, nt), dtype=np.float64, order='F') + + # upper right and lower left blocks + ur = np.zeros((kul, kul)) + ll = np.zeros_like(ur) + + # `offset` is made to shift all the non-zero elements to the end of the + # matrix + _bspl._colloc(x, t, k, ab, offset=k) + + # remove zeros before the matrix + ab = ab[-k - (k + 1) % 2:, :] + + # The least elements in rows (except repetitions) are diagonals + # of block matrices. Upper right matrix is an upper triangular + # matrix while lower left is a lower triangular one. + for i in range(kul): + ur += np.diag(ab[-i - 1, i: kul], k=i) + ll += np.diag(ab[i, -kul - (k % 2): n - 1 + 2 * kul - i], k=-i) + + # remove elements that occur in the last point + # (first and last points are equivalent) + A = ab[:, kul: -k + kul] + + for i in range(extradim): + cc = _woodbury_algorithm(A, ur, ll, y_new[:, i][:-1], k) + c[:, i] = np.concatenate((cc[-kul:], cc, cc[:kul + k % 2])) + c = np.ascontiguousarray(c.reshape((n + k - 1,) + y.shape[1:])) + return BSpline.construct_fast(t, c, k, extrapolate='periodic', axis=axis) + + +def make_interp_spline(x, y, k=3, t=None, bc_type=None, axis=0, + check_finite=True): + """Compute the (coefficients of) interpolating B-spline. + + Parameters + ---------- + x : array_like, shape (n,) + Abscissas. + y : array_like, shape (n, ...) + Ordinates. + k : int, optional + B-spline degree. Default is cubic, ``k = 3``. + t : array_like, shape (nt + k + 1,), optional. + Knots. + The number of knots needs to agree with the number of data points and + the number of derivatives at the edges. Specifically, ``nt - n`` must + equal ``len(deriv_l) + len(deriv_r)``. + bc_type : 2-tuple or None + Boundary conditions. + Default is None, which means choosing the boundary conditions + automatically. Otherwise, it must be a length-two tuple where the first + element (``deriv_l``) sets the boundary conditions at ``x[0]`` and + the second element (``deriv_r``) sets the boundary conditions at + ``x[-1]``. Each of these must be an iterable of pairs + ``(order, value)`` which gives the values of derivatives of specified + orders at the given edge of the interpolation interval. + Alternatively, the following string aliases are recognized: + + * ``"clamped"``: The first derivatives at the ends are zero. This is + equivalent to ``bc_type=([(1, 0.0)], [(1, 0.0)])``. + * ``"natural"``: The second derivatives at ends are zero. This is + equivalent to ``bc_type=([(2, 0.0)], [(2, 0.0)])``. + * ``"not-a-knot"`` (default): The first and second segments are the + same polynomial. This is equivalent to having ``bc_type=None``. + * ``"periodic"``: The values and the first ``k-1`` derivatives at the + ends are equivalent. + + axis : int, optional + Interpolation axis. Default is 0. + check_finite : bool, optional + Whether to check that the input arrays contain only finite numbers. + Disabling may give a performance gain, but may result in problems + (crashes, non-termination) if the inputs do contain infinities or NaNs. + Default is True. + + Returns + ------- + b : a BSpline object of the degree ``k`` and with knots ``t``. + + See Also + -------- + BSpline : base class representing the B-spline objects + CubicSpline : a cubic spline in the polynomial basis + make_lsq_spline : a similar factory function for spline fitting + UnivariateSpline : a wrapper over FITPACK spline fitting routines + splrep : a wrapper over FITPACK spline fitting routines + + Examples + -------- + + Use cubic interpolation on Chebyshev nodes: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> def cheb_nodes(N): + ... jj = 2.*np.arange(N) + 1 + ... x = np.cos(np.pi * jj / 2 / N)[::-1] + ... return x + + >>> x = cheb_nodes(20) + >>> y = np.sqrt(1 - x**2) + + >>> from scipy.interpolate import BSpline, make_interp_spline + >>> b = make_interp_spline(x, y) + >>> np.allclose(b(x), y) + True + + Note that the default is a cubic spline with a not-a-knot boundary condition + + >>> b.k + 3 + + Here we use a 'natural' spline, with zero 2nd derivatives at edges: + + >>> l, r = [(2, 0.0)], [(2, 0.0)] + >>> b_n = make_interp_spline(x, y, bc_type=(l, r)) # or, bc_type="natural" + >>> np.allclose(b_n(x), y) + True + >>> x0, x1 = x[0], x[-1] + >>> np.allclose([b_n(x0, 2), b_n(x1, 2)], [0, 0]) + True + + Interpolation of parametric curves is also supported. As an example, we + compute a discretization of a snail curve in polar coordinates + + >>> phi = np.linspace(0, 2.*np.pi, 40) + >>> r = 0.3 + np.cos(phi) + >>> x, y = r*np.cos(phi), r*np.sin(phi) # convert to Cartesian coordinates + + Build an interpolating curve, parameterizing it by the angle + + >>> spl = make_interp_spline(phi, np.c_[x, y]) + + Evaluate the interpolant on a finer grid (note that we transpose the result + to unpack it into a pair of x- and y-arrays) + + >>> phi_new = np.linspace(0, 2.*np.pi, 100) + >>> x_new, y_new = spl(phi_new).T + + Plot the result + + >>> plt.plot(x, y, 'o') + >>> plt.plot(x_new, y_new, '-') + >>> plt.show() + + Build a B-spline curve with 2 dimensional y + + >>> x = np.linspace(0, 2*np.pi, 10) + >>> y = np.array([np.sin(x), np.cos(x)]) + + Periodic condition is satisfied because y coordinates of points on the ends + are equivalent + + >>> ax = plt.axes(projection='3d') + >>> xx = np.linspace(0, 2*np.pi, 100) + >>> bspl = make_interp_spline(x, y, k=5, bc_type='periodic', axis=1) + >>> ax.plot3D(xx, *bspl(xx)) + >>> ax.scatter3D(x, *y, color='red') + >>> plt.show() + + """ + # convert string aliases for the boundary conditions + if bc_type is None or bc_type == 'not-a-knot' or bc_type == 'periodic': + deriv_l, deriv_r = None, None + elif isinstance(bc_type, str): + deriv_l, deriv_r = bc_type, bc_type + else: + try: + deriv_l, deriv_r = bc_type + except TypeError as e: + raise ValueError("Unknown boundary condition: %s" % bc_type) from e + + y = np.asarray(y) + + axis = normalize_axis_index(axis, y.ndim) + + x = _as_float_array(x, check_finite) + y = _as_float_array(y, check_finite) + + y = np.moveaxis(y, axis, 0) # now internally interp axis is zero + + # sanity check the input + if bc_type == 'periodic' and not np.allclose(y[0], y[-1], atol=1e-15): + raise ValueError("First and last points does not match while " + "periodic case expected") + if x.size != y.shape[0]: + raise ValueError(f'Shapes of x {x.shape} and y {y.shape} are incompatible') + if np.any(x[1:] == x[:-1]): + raise ValueError("Expect x to not have duplicates") + if x.ndim != 1 or np.any(x[1:] < x[:-1]): + raise ValueError("Expect x to be a 1D strictly increasing sequence.") + + # special-case k=0 right away + if k == 0: + if any(_ is not None for _ in (t, deriv_l, deriv_r)): + raise ValueError("Too much info for k=0: t and bc_type can only " + "be None.") + t = np.r_[x, x[-1]] + c = np.asarray(y) + c = np.ascontiguousarray(c, dtype=_get_dtype(c.dtype)) + return BSpline.construct_fast(t, c, k, axis=axis) + + # special-case k=1 (e.g., Lyche and Morken, Eq.(2.16)) + if k == 1 and t is None: + if not (deriv_l is None and deriv_r is None): + raise ValueError("Too much info for k=1: bc_type can only be None.") + t = np.r_[x[0], x, x[-1]] + c = np.asarray(y) + c = np.ascontiguousarray(c, dtype=_get_dtype(c.dtype)) + return BSpline.construct_fast(t, c, k, axis=axis) + + k = operator.index(k) + + if bc_type == 'periodic' and t is not None: + raise NotImplementedError("For periodic case t is constructed " + "automatically and can not be passed " + "manually") + + # come up with a sensible knot vector, if needed + if t is None: + if deriv_l is None and deriv_r is None: + if bc_type == 'periodic': + t = _periodic_knots(x, k) + elif k == 2: + # OK, it's a bit ad hoc: Greville sites + omit + # 2nd and 2nd-to-last points, a la not-a-knot + t = (x[1:] + x[:-1]) / 2. + t = np.r_[(x[0],)*(k+1), + t[1:-1], + (x[-1],)*(k+1)] + else: + t = _not_a_knot(x, k) + else: + t = _augknt(x, k) + + t = _as_float_array(t, check_finite) + + if k < 0: + raise ValueError("Expect non-negative k.") + if t.ndim != 1 or np.any(t[1:] < t[:-1]): + raise ValueError("Expect t to be a 1-D sorted array_like.") + if t.size < x.size + k + 1: + raise ValueError('Got %d knots, need at least %d.' % + (t.size, x.size + k + 1)) + if (x[0] < t[k]) or (x[-1] > t[-k]): + raise ValueError('Out of bounds w/ x = %s.' % x) + + if bc_type == 'periodic': + return _make_periodic_spline(x, y, t, k, axis) + + # Here : deriv_l, r = [(nu, value), ...] + deriv_l = _convert_string_aliases(deriv_l, y.shape[1:]) + deriv_l_ords, deriv_l_vals = _process_deriv_spec(deriv_l) + nleft = deriv_l_ords.shape[0] + + deriv_r = _convert_string_aliases(deriv_r, y.shape[1:]) + deriv_r_ords, deriv_r_vals = _process_deriv_spec(deriv_r) + nright = deriv_r_ords.shape[0] + + # have `n` conditions for `nt` coefficients; need nt-n derivatives + n = x.size + nt = t.size - k - 1 + + if nt - n != nleft + nright: + raise ValueError("The number of derivatives at boundaries does not " + f"match: expected {nt-n}, got {nleft}+{nright}") + + # bail out if the `y` array is zero-sized + if y.size == 0: + c = np.zeros((nt,) + y.shape[1:], dtype=float) + return BSpline.construct_fast(t, c, k, axis=axis) + + # set up the LHS: the collocation matrix + derivatives at boundaries + kl = ku = k + ab = np.zeros((2*kl + ku + 1, nt), dtype=np.float64, order='F') + _bspl._colloc(x, t, k, ab, offset=nleft) + if nleft > 0: + _bspl._handle_lhs_derivatives(t, k, x[0], ab, kl, ku, + deriv_l_ords.astype(np.dtype("long"))) + if nright > 0: + _bspl._handle_lhs_derivatives(t, k, x[-1], ab, kl, ku, + deriv_r_ords.astype(np.dtype("long")), + offset=nt-nright) + + # set up the RHS: values to interpolate (+ derivative values, if any) + extradim = prod(y.shape[1:]) + rhs = np.empty((nt, extradim), dtype=y.dtype) + if nleft > 0: + rhs[:nleft] = deriv_l_vals.reshape(-1, extradim) + rhs[nleft:nt - nright] = y.reshape(-1, extradim) + if nright > 0: + rhs[nt - nright:] = deriv_r_vals.reshape(-1, extradim) + + # solve Ab @ x = rhs; this is the relevant part of linalg.solve_banded + if check_finite: + ab, rhs = map(np.asarray_chkfinite, (ab, rhs)) + gbsv, = get_lapack_funcs(('gbsv',), (ab, rhs)) + lu, piv, c, info = gbsv(kl, ku, ab, rhs, + overwrite_ab=True, overwrite_b=True) + + if info > 0: + raise LinAlgError("Collocation matrix is singular.") + elif info < 0: + raise ValueError('illegal value in %d-th argument of internal gbsv' % -info) + + c = np.ascontiguousarray(c.reshape((nt,) + y.shape[1:])) + return BSpline.construct_fast(t, c, k, axis=axis) + + +def make_lsq_spline(x, y, t, k=3, w=None, axis=0, check_finite=True): + r"""Compute the (coefficients of) an LSQ (Least SQuared) based + fitting B-spline. + + The result is a linear combination + + .. math:: + + S(x) = \sum_j c_j B_j(x; t) + + of the B-spline basis elements, :math:`B_j(x; t)`, which minimizes + + .. math:: + + \sum_{j} \left( w_j \times (S(x_j) - y_j) \right)^2 + + Parameters + ---------- + x : array_like, shape (m,) + Abscissas. + y : array_like, shape (m, ...) + Ordinates. + t : array_like, shape (n + k + 1,). + Knots. + Knots and data points must satisfy Schoenberg-Whitney conditions. + k : int, optional + B-spline degree. Default is cubic, ``k = 3``. + w : array_like, shape (m,), optional + Weights for spline fitting. Must be positive. If ``None``, + then weights are all equal. + Default is ``None``. + axis : int, optional + Interpolation axis. Default is zero. + check_finite : bool, optional + Whether to check that the input arrays contain only finite numbers. + Disabling may give a performance gain, but may result in problems + (crashes, non-termination) if the inputs do contain infinities or NaNs. + Default is True. + + Returns + ------- + b : a BSpline object of the degree ``k`` with knots ``t``. + + See Also + -------- + BSpline : base class representing the B-spline objects + make_interp_spline : a similar factory function for interpolating splines + LSQUnivariateSpline : a FITPACK-based spline fitting routine + splrep : a FITPACK-based fitting routine + + Notes + ----- + The number of data points must be larger than the spline degree ``k``. + + Knots ``t`` must satisfy the Schoenberg-Whitney conditions, + i.e., there must be a subset of data points ``x[j]`` such that + ``t[j] < x[j] < t[j+k+1]``, for ``j=0, 1,...,n-k-2``. + + Examples + -------- + Generate some noisy data: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> x = np.linspace(-3, 3, 50) + >>> y = np.exp(-x**2) + 0.1 * rng.standard_normal(50) + + Now fit a smoothing cubic spline with a pre-defined internal knots. + Here we make the knot vector (k+1)-regular by adding boundary knots: + + >>> from scipy.interpolate import make_lsq_spline, BSpline + >>> t = [-1, 0, 1] + >>> k = 3 + >>> t = np.r_[(x[0],)*(k+1), + ... t, + ... (x[-1],)*(k+1)] + >>> spl = make_lsq_spline(x, y, t, k) + + For comparison, we also construct an interpolating spline for the same + set of data: + + >>> from scipy.interpolate import make_interp_spline + >>> spl_i = make_interp_spline(x, y) + + Plot both: + + >>> xs = np.linspace(-3, 3, 100) + >>> plt.plot(x, y, 'ro', ms=5) + >>> plt.plot(xs, spl(xs), 'g-', lw=3, label='LSQ spline') + >>> plt.plot(xs, spl_i(xs), 'b-', lw=3, alpha=0.7, label='interp spline') + >>> plt.legend(loc='best') + >>> plt.show() + + **NaN handling**: If the input arrays contain ``nan`` values, the result is + not useful since the underlying spline fitting routines cannot deal with + ``nan``. A workaround is to use zero weights for not-a-number data points: + + >>> y[8] = np.nan + >>> w = np.isnan(y) + >>> y[w] = 0. + >>> tck = make_lsq_spline(x, y, t, w=~w) + + Notice the need to replace a ``nan`` by a numerical value (precise value + does not matter as long as the corresponding weight is zero.) + + """ + x = _as_float_array(x, check_finite) + y = _as_float_array(y, check_finite) + t = _as_float_array(t, check_finite) + if w is not None: + w = _as_float_array(w, check_finite) + else: + w = np.ones_like(x) + k = operator.index(k) + + axis = normalize_axis_index(axis, y.ndim) + + y = np.moveaxis(y, axis, 0) # now internally interp axis is zero + + if x.ndim != 1 or np.any(x[1:] - x[:-1] <= 0): + raise ValueError("Expect x to be a 1-D sorted array_like.") + if x.shape[0] < k+1: + raise ValueError("Need more x points.") + if k < 0: + raise ValueError("Expect non-negative k.") + if t.ndim != 1 or np.any(t[1:] - t[:-1] < 0): + raise ValueError("Expect t to be a 1-D sorted array_like.") + if x.size != y.shape[0]: + raise ValueError(f'Shapes of x {x.shape} and y {y.shape} are incompatible') + if k > 0 and np.any((x < t[k]) | (x > t[-k])): + raise ValueError('Out of bounds w/ x = %s.' % x) + if x.size != w.size: + raise ValueError(f'Shapes of x {x.shape} and w {w.shape} are incompatible') + + # number of coefficients + n = t.size - k - 1 + + # construct A.T @ A and rhs with A the collocation matrix, and + # rhs = A.T @ y for solving the LSQ problem ``A.T @ A @ c = A.T @ y`` + lower = True + extradim = prod(y.shape[1:]) + ab = np.zeros((k+1, n), dtype=np.float64, order='F') + rhs = np.zeros((n, extradim), dtype=y.dtype, order='F') + _bspl._norm_eq_lsq(x, t, k, + y.reshape(-1, extradim), + w, + ab, rhs) + rhs = rhs.reshape((n,) + y.shape[1:]) + + # have observation matrix & rhs, can solve the LSQ problem + cho_decomp = cholesky_banded(ab, overwrite_ab=True, lower=lower, + check_finite=check_finite) + c = cho_solve_banded((cho_decomp, lower), rhs, overwrite_b=True, + check_finite=check_finite) + + c = np.ascontiguousarray(c) + return BSpline.construct_fast(t, c, k, axis=axis) + + +############################# +# Smoothing spline helpers # +############################# + +def _compute_optimal_gcv_parameter(X, wE, y, w): + """ + Returns an optimal regularization parameter from the GCV criteria [1]. + + Parameters + ---------- + X : array, shape (5, n) + 5 bands of the design matrix ``X`` stored in LAPACK banded storage. + wE : array, shape (5, n) + 5 bands of the penalty matrix :math:`W^{-1} E` stored in LAPACK banded + storage. + y : array, shape (n,) + Ordinates. + w : array, shape (n,) + Vector of weights. + + Returns + ------- + lam : float + An optimal from the GCV criteria point of view regularization + parameter. + + Notes + ----- + No checks are performed. + + References + ---------- + .. [1] G. Wahba, "Estimating the smoothing parameter" in Spline models + for observational data, Philadelphia, Pennsylvania: Society for + Industrial and Applied Mathematics, 1990, pp. 45-65. + :doi:`10.1137/1.9781611970128` + + """ + + def compute_banded_symmetric_XT_W_Y(X, w, Y): + """ + Assuming that the product :math:`X^T W Y` is symmetric and both ``X`` + and ``Y`` are 5-banded, compute the unique bands of the product. + + Parameters + ---------- + X : array, shape (5, n) + 5 bands of the matrix ``X`` stored in LAPACK banded storage. + w : array, shape (n,) + Array of weights + Y : array, shape (5, n) + 5 bands of the matrix ``Y`` stored in LAPACK banded storage. + + Returns + ------- + res : array, shape (4, n) + The result of the product :math:`X^T Y` stored in the banded way. + + Notes + ----- + As far as the matrices ``X`` and ``Y`` are 5-banded, their product + :math:`X^T W Y` is 7-banded. It is also symmetric, so we can store only + unique diagonals. + + """ + # compute W Y + W_Y = np.copy(Y) + + W_Y[2] *= w + for i in range(2): + W_Y[i, 2 - i:] *= w[:-2 + i] + W_Y[3 + i, :-1 - i] *= w[1 + i:] + + n = X.shape[1] + res = np.zeros((4, n)) + for i in range(n): + for j in range(min(n-i, 4)): + res[-j-1, i + j] = sum(X[j:, i] * W_Y[:5-j, i + j]) + return res + + def compute_b_inv(A): + """ + Inverse 3 central bands of matrix :math:`A=U^T D^{-1} U` assuming that + ``U`` is a unit upper triangular banded matrix using an algorithm + proposed in [1]. + + Parameters + ---------- + A : array, shape (4, n) + Matrix to inverse, stored in LAPACK banded storage. + + Returns + ------- + B : array, shape (4, n) + 3 unique bands of the symmetric matrix that is an inverse to ``A``. + The first row is filled with zeros. + + Notes + ----- + The algorithm is based on the cholesky decomposition and, therefore, + in case matrix ``A`` is close to not positive defined, the function + raises LinalgError. + + Both matrices ``A`` and ``B`` are stored in LAPACK banded storage. + + References + ---------- + .. [1] M. F. Hutchinson and F. R. de Hoog, "Smoothing noisy data with + spline functions," Numerische Mathematik, vol. 47, no. 1, + pp. 99-106, 1985. + :doi:`10.1007/BF01389878` + + """ + + def find_b_inv_elem(i, j, U, D, B): + rng = min(3, n - i - 1) + rng_sum = 0. + if j == 0: + # use 2-nd formula from [1] + for k in range(1, rng + 1): + rng_sum -= U[-k - 1, i + k] * B[-k - 1, i + k] + rng_sum += D[i] + B[-1, i] = rng_sum + else: + # use 1-st formula from [1] + for k in range(1, rng + 1): + diag = abs(k - j) + ind = i + min(k, j) + rng_sum -= U[-k - 1, i + k] * B[-diag - 1, ind + diag] + B[-j - 1, i + j] = rng_sum + + U = cholesky_banded(A) + for i in range(2, 5): + U[-i, i-1:] /= U[-1, :-i+1] + D = 1. / (U[-1])**2 + U[-1] /= U[-1] + + n = U.shape[1] + + B = np.zeros(shape=(4, n)) + for i in range(n - 1, -1, -1): + for j in range(min(3, n - i - 1), -1, -1): + find_b_inv_elem(i, j, U, D, B) + # the first row contains garbage and should be removed + B[0] = [0.] * n + return B + + def _gcv(lam, X, XtWX, wE, XtE): + r""" + Computes the generalized cross-validation criteria [1]. + + Parameters + ---------- + lam : float, (:math:`\lambda \geq 0`) + Regularization parameter. + X : array, shape (5, n) + Matrix is stored in LAPACK banded storage. + XtWX : array, shape (4, n) + Product :math:`X^T W X` stored in LAPACK banded storage. + wE : array, shape (5, n) + Matrix :math:`W^{-1} E` stored in LAPACK banded storage. + XtE : array, shape (4, n) + Product :math:`X^T E` stored in LAPACK banded storage. + + Returns + ------- + res : float + Value of the GCV criteria with the regularization parameter + :math:`\lambda`. + + Notes + ----- + Criteria is computed from the formula (1.3.2) [3]: + + .. math: + + GCV(\lambda) = \dfrac{1}{n} \sum\limits_{k = 1}^{n} \dfrac{ \left( + y_k - f_{\lambda}(x_k) \right)^2}{\left( 1 - \Tr{A}/n\right)^2}$. + The criteria is discussed in section 1.3 [3]. + + The numerator is computed using (2.2.4) [3] and the denominator is + computed using an algorithm from [2] (see in the ``compute_b_inv`` + function). + + References + ---------- + .. [1] G. Wahba, "Estimating the smoothing parameter" in Spline models + for observational data, Philadelphia, Pennsylvania: Society for + Industrial and Applied Mathematics, 1990, pp. 45-65. + :doi:`10.1137/1.9781611970128` + .. [2] M. F. Hutchinson and F. R. de Hoog, "Smoothing noisy data with + spline functions," Numerische Mathematik, vol. 47, no. 1, + pp. 99-106, 1985. + :doi:`10.1007/BF01389878` + .. [3] E. Zemlyanoy, "Generalized cross-validation smoothing splines", + BSc thesis, 2022. Might be available (in Russian) + `here `_ + + """ + # Compute the numerator from (2.2.4) [3] + n = X.shape[1] + c = solve_banded((2, 2), X + lam * wE, y) + res = np.zeros(n) + # compute ``W^{-1} E c`` with respect to banded-storage of ``E`` + tmp = wE * c + for i in range(n): + for j in range(max(0, i - n + 3), min(5, i + 3)): + res[i] += tmp[j, i + 2 - j] + numer = np.linalg.norm(lam * res)**2 / n + + # compute the denominator + lhs = XtWX + lam * XtE + try: + b_banded = compute_b_inv(lhs) + # compute the trace of the product b_banded @ XtX + tr = b_banded * XtWX + tr[:-1] *= 2 + # find the denominator + denom = (1 - sum(sum(tr)) / n)**2 + except LinAlgError: + # cholesky decomposition cannot be performed + raise ValueError('Seems like the problem is ill-posed') + + res = numer / denom + + return res + + n = X.shape[1] + + XtWX = compute_banded_symmetric_XT_W_Y(X, w, X) + XtE = compute_banded_symmetric_XT_W_Y(X, w, wE) + + def fun(lam): + return _gcv(lam, X, XtWX, wE, XtE) + + gcv_est = minimize_scalar(fun, bounds=(0, n), method='Bounded') + if gcv_est.success: + return gcv_est.x + raise ValueError(f"Unable to find minimum of the GCV " + f"function: {gcv_est.message}") + + +def _coeff_of_divided_diff(x): + """ + Returns the coefficients of the divided difference. + + Parameters + ---------- + x : array, shape (n,) + Array which is used for the computation of divided difference. + + Returns + ------- + res : array_like, shape (n,) + Coefficients of the divided difference. + + Notes + ----- + Vector ``x`` should have unique elements, otherwise an error division by + zero might be raised. + + No checks are performed. + + """ + n = x.shape[0] + res = np.zeros(n) + for i in range(n): + pp = 1. + for k in range(n): + if k != i: + pp *= (x[i] - x[k]) + res[i] = 1. / pp + return res + + +def make_smoothing_spline(x, y, w=None, lam=None): + r""" + Compute the (coefficients of) smoothing cubic spline function using + ``lam`` to control the tradeoff between the amount of smoothness of the + curve and its proximity to the data. In case ``lam`` is None, using the + GCV criteria [1] to find it. + + A smoothing spline is found as a solution to the regularized weighted + linear regression problem: + + .. math:: + + \sum\limits_{i=1}^n w_i\lvert y_i - f(x_i) \rvert^2 + + \lambda\int\limits_{x_1}^{x_n} (f^{(2)}(u))^2 d u + + where :math:`f` is a spline function, :math:`w` is a vector of weights and + :math:`\lambda` is a regularization parameter. + + If ``lam`` is None, we use the GCV criteria to find an optimal + regularization parameter, otherwise we solve the regularized weighted + linear regression problem with given parameter. The parameter controls + the tradeoff in the following way: the larger the parameter becomes, the + smoother the function gets. + + Parameters + ---------- + x : array_like, shape (n,) + Abscissas. `n` must be at least 5. + y : array_like, shape (n,) + Ordinates. `n` must be at least 5. + w : array_like, shape (n,), optional + Vector of weights. Default is ``np.ones_like(x)``. + lam : float, (:math:`\lambda \geq 0`), optional + Regularization parameter. If ``lam`` is None, then it is found from + the GCV criteria. Default is None. + + Returns + ------- + func : a BSpline object. + A callable representing a spline in the B-spline basis + as a solution of the problem of smoothing splines using + the GCV criteria [1] in case ``lam`` is None, otherwise using the + given parameter ``lam``. + + Notes + ----- + This algorithm is a clean room reimplementation of the algorithm + introduced by Woltring in FORTRAN [2]. The original version cannot be used + in SciPy source code because of the license issues. The details of the + reimplementation are discussed here (available only in Russian) [4]. + + If the vector of weights ``w`` is None, we assume that all the points are + equal in terms of weights, and vector of weights is vector of ones. + + Note that in weighted residual sum of squares, weights are not squared: + :math:`\sum\limits_{i=1}^n w_i\lvert y_i - f(x_i) \rvert^2` while in + ``splrep`` the sum is built from the squared weights. + + In cases when the initial problem is ill-posed (for example, the product + :math:`X^T W X` where :math:`X` is a design matrix is not a positive + defined matrix) a ValueError is raised. + + References + ---------- + .. [1] G. Wahba, "Estimating the smoothing parameter" in Spline models for + observational data, Philadelphia, Pennsylvania: Society for Industrial + and Applied Mathematics, 1990, pp. 45-65. + :doi:`10.1137/1.9781611970128` + .. [2] H. J. Woltring, A Fortran package for generalized, cross-validatory + spline smoothing and differentiation, Advances in Engineering + Software, vol. 8, no. 2, pp. 104-113, 1986. + :doi:`10.1016/0141-1195(86)90098-7` + .. [3] T. Hastie, J. Friedman, and R. Tisbshirani, "Smoothing Splines" in + The elements of Statistical Learning: Data Mining, Inference, and + prediction, New York: Springer, 2017, pp. 241-249. + :doi:`10.1007/978-0-387-84858-7` + .. [4] E. Zemlyanoy, "Generalized cross-validation smoothing splines", + BSc thesis, 2022. + ``_ (in + Russian) + + Examples + -------- + Generate some noisy data + + >>> import numpy as np + >>> np.random.seed(1234) + >>> n = 200 + >>> def func(x): + ... return x**3 + x**2 * np.sin(4 * x) + >>> x = np.sort(np.random.random_sample(n) * 4 - 2) + >>> y = func(x) + np.random.normal(scale=1.5, size=n) + + Make a smoothing spline function + + >>> from scipy.interpolate import make_smoothing_spline + >>> spl = make_smoothing_spline(x, y) + + Plot both + + >>> import matplotlib.pyplot as plt + >>> grid = np.linspace(x[0], x[-1], 400) + >>> plt.plot(grid, spl(grid), label='Spline') + >>> plt.plot(grid, func(grid), label='Original function') + >>> plt.scatter(x, y, marker='.') + >>> plt.legend(loc='best') + >>> plt.show() + + """ + + x = np.ascontiguousarray(x, dtype=float) + y = np.ascontiguousarray(y, dtype=float) + + if any(x[1:] - x[:-1] <= 0): + raise ValueError('``x`` should be an ascending array') + + if x.ndim != 1 or y.ndim != 1 or x.shape[0] != y.shape[0]: + raise ValueError('``x`` and ``y`` should be one dimensional and the' + ' same size') + + if w is None: + w = np.ones(len(x)) + else: + w = np.ascontiguousarray(w) + if any(w <= 0): + raise ValueError('Invalid vector of weights') + + t = np.r_[[x[0]] * 3, x, [x[-1]] * 3] + n = x.shape[0] + + if n <= 4: + raise ValueError('``x`` and ``y`` length must be at least 5') + + # It is known that the solution to the stated minimization problem exists + # and is a natural cubic spline with vector of knots equal to the unique + # elements of ``x`` [3], so we will solve the problem in the basis of + # natural splines. + + # create design matrix in the B-spline basis + X_bspl = BSpline.design_matrix(x, t, 3) + # move from B-spline basis to the basis of natural splines using equations + # (2.1.7) [4] + # central elements + X = np.zeros((5, n)) + for i in range(1, 4): + X[i, 2: -2] = X_bspl[i: i - 4, 3: -3][np.diag_indices(n - 4)] + + # first elements + X[1, 1] = X_bspl[0, 0] + X[2, :2] = ((x[2] + x[1] - 2 * x[0]) * X_bspl[0, 0], + X_bspl[1, 1] + X_bspl[1, 2]) + X[3, :2] = ((x[2] - x[0]) * X_bspl[1, 1], X_bspl[2, 2]) + + # last elements + X[1, -2:] = (X_bspl[-3, -3], (x[-1] - x[-3]) * X_bspl[-2, -2]) + X[2, -2:] = (X_bspl[-2, -3] + X_bspl[-2, -2], + (2 * x[-1] - x[-2] - x[-3]) * X_bspl[-1, -1]) + X[3, -2] = X_bspl[-1, -1] + + # create penalty matrix and divide it by vector of weights: W^{-1} E + wE = np.zeros((5, n)) + wE[2:, 0] = _coeff_of_divided_diff(x[:3]) / w[:3] + wE[1:, 1] = _coeff_of_divided_diff(x[:4]) / w[:4] + for j in range(2, n - 2): + wE[:, j] = (x[j+2] - x[j-2]) * _coeff_of_divided_diff(x[j-2:j+3])\ + / w[j-2: j+3] + + wE[:-1, -2] = -_coeff_of_divided_diff(x[-4:]) / w[-4:] + wE[:-2, -1] = _coeff_of_divided_diff(x[-3:]) / w[-3:] + wE *= 6 + + if lam is None: + lam = _compute_optimal_gcv_parameter(X, wE, y, w) + elif lam < 0.: + raise ValueError('Regularization parameter should be non-negative') + + # solve the initial problem in the basis of natural splines + c = solve_banded((2, 2), X + lam * wE, y) + # move back to B-spline basis using equations (2.2.10) [4] + c_ = np.r_[c[0] * (t[5] + t[4] - 2 * t[3]) + c[1], + c[0] * (t[5] - t[3]) + c[1], + c[1: -1], + c[-1] * (t[-4] - t[-6]) + c[-2], + c[-1] * (2 * t[-4] - t[-5] - t[-6]) + c[-2]] + + return BSpline.construct_fast(t, c_, 3) + + +######################## +# FITPACK look-alikes # +######################## + +def fpcheck(x, t, k): + """ Check consistency of the data vector `x` and the knot vector `t`. + + Return None if inputs are consistent, raises a ValueError otherwise. + """ + # This routine is a clone of the `fpchec` Fortran routine, + # https://github.com/scipy/scipy/blob/main/scipy/interpolate/fitpack/fpchec.f + # which carries the following comment: + # + # subroutine fpchec verifies the number and the position of the knots + # t(j),j=1,2,...,n of a spline of degree k, in relation to the number + # and the position of the data points x(i),i=1,2,...,m. if all of the + # following conditions are fulfilled, the error parameter ier is set + # to zero. if one of the conditions is violated ier is set to ten. + # 1) k+1 <= n-k-1 <= m + # 2) t(1) <= t(2) <= ... <= t(k+1) + # t(n-k) <= t(n-k+1) <= ... <= t(n) + # 3) t(k+1) < t(k+2) < ... < t(n-k) + # 4) t(k+1) <= x(i) <= t(n-k) + # 5) the conditions specified by schoenberg and whitney must hold + # for at least one subset of data points, i.e. there must be a + # subset of data points y(j) such that + # t(j) < y(j) < t(j+k+1), j=1,2,...,n-k-1 + x = np.asarray(x) + t = np.asarray(t) + + if x.ndim != 1 or t.ndim != 1: + raise ValueError(f"Expect `x` and `t` be 1D sequences. Got {x = } and {t = }") + + m = x.shape[0] + n = t.shape[0] + nk1 = n - k - 1 + + # check condition no 1 + # c 1) k+1 <= n-k-1 <= m + if not (k + 1 <= nk1 <= m): + raise ValueError(f"Need k+1 <= n-k-1 <= m. Got {m = }, {n = } and {k = }.") + + # check condition no 2 + # c 2) t(1) <= t(2) <= ... <= t(k+1) + # c t(n-k) <= t(n-k+1) <= ... <= t(n) + if (t[:k+1] > t[1:k+2]).any(): + raise ValueError(f"First k knots must be ordered; got {t = }.") + + if (t[nk1:] < t[nk1-1:-1]).any(): + raise ValueError(f"Last k knots must be ordered; got {t = }.") + + # c check condition no 3 + # c 3) t(k+1) < t(k+2) < ... < t(n-k) + if (t[k+1:n-k] <= t[k:n-k-1]).any(): + raise ValueError(f"Internal knots must be distinct. Got {t = }.") + + # c check condition no 4 + # c 4) t(k+1) <= x(i) <= t(n-k) + # NB: FITPACK's fpchec only checks x[0] & x[-1], so we follow. + if (x[0] < t[k]) or (x[-1] > t[n-k-1]): + raise ValueError(f"Out of bounds: {x = } and {t = }.") + + # c check condition no 5 + # c 5) the conditions specified by schoenberg and whitney must hold + # c for at least one subset of data points, i.e. there must be a + # c subset of data points y(j) such that + # c t(j) < y(j) < t(j+k+1), j=1,2,...,n-k-1 + mesg = f"Schoenberg-Whitney condition is violated with {t = } and {x =}." + + if (x[0] >= t[k+1]) or (x[-1] <= t[n-k-2]): + raise ValueError(mesg) + + m = x.shape[0] + l = k+1 + nk3 = n - k - 3 + if nk3 < 2: + return + for j in range(1, nk3+1): + tj = t[j] + l += 1 + tl = t[l] + i = np.argmax(x > tj) + if i >= m-1: + raise ValueError(mesg) + if x[i] >= tl: + raise ValueError(mesg) + return diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_cubic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_cubic.py new file mode 100644 index 0000000000000000000000000000000000000000..997776150afa44ff3f6d14356f2b1bc3b1fb5e5e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_cubic.py @@ -0,0 +1,970 @@ +"""Interpolation algorithms using piecewise cubic polynomials.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import warnings + +import numpy as np + +from scipy.linalg import solve, solve_banded + +from . import PPoly +from ._polyint import _isscalar + +if TYPE_CHECKING: + from typing import Literal + +__all__ = ["CubicHermiteSpline", "PchipInterpolator", "pchip_interpolate", + "Akima1DInterpolator", "CubicSpline"] + + +def prepare_input(x, y, axis, dydx=None): + """Prepare input for cubic spline interpolators. + + All data are converted to numpy arrays and checked for correctness. + Axes equal to `axis` of arrays `y` and `dydx` are moved to be the 0th + axis. The value of `axis` is converted to lie in + [0, number of dimensions of `y`). + """ + + x, y = map(np.asarray, (x, y)) + if np.issubdtype(x.dtype, np.complexfloating): + raise ValueError("`x` must contain real values.") + x = x.astype(float) + + if np.issubdtype(y.dtype, np.complexfloating): + dtype = complex + else: + dtype = float + + if dydx is not None: + dydx = np.asarray(dydx) + if y.shape != dydx.shape: + raise ValueError("The shapes of `y` and `dydx` must be identical.") + if np.issubdtype(dydx.dtype, np.complexfloating): + dtype = complex + dydx = dydx.astype(dtype, copy=False) + + y = y.astype(dtype, copy=False) + axis = axis % y.ndim + if x.ndim != 1: + raise ValueError("`x` must be 1-dimensional.") + if x.shape[0] < 2: + raise ValueError("`x` must contain at least 2 elements.") + if x.shape[0] != y.shape[axis]: + raise ValueError(f"The length of `y` along `axis`={axis} doesn't " + "match the length of `x`") + + if not np.all(np.isfinite(x)): + raise ValueError("`x` must contain only finite values.") + if not np.all(np.isfinite(y)): + raise ValueError("`y` must contain only finite values.") + + if dydx is not None and not np.all(np.isfinite(dydx)): + raise ValueError("`dydx` must contain only finite values.") + + dx = np.diff(x) + if np.any(dx <= 0): + raise ValueError("`x` must be strictly increasing sequence.") + + y = np.moveaxis(y, axis, 0) + if dydx is not None: + dydx = np.moveaxis(dydx, axis, 0) + + return x, dx, y, axis, dydx + + +class CubicHermiteSpline(PPoly): + """Piecewise-cubic interpolator matching values and first derivatives. + + The result is represented as a `PPoly` instance. + + Parameters + ---------- + x : array_like, shape (n,) + 1-D array containing values of the independent variable. + Values must be real, finite and in strictly increasing order. + y : array_like + Array containing values of the dependent variable. It can have + arbitrary number of dimensions, but the length along ``axis`` + (see below) must match the length of ``x``. Values must be finite. + dydx : array_like + Array containing derivatives of the dependent variable. It can have + arbitrary number of dimensions, but the length along ``axis`` + (see below) must match the length of ``x``. Values must be finite. + axis : int, optional + Axis along which `y` is assumed to be varying. Meaning that for + ``x[i]`` the corresponding values are ``np.take(y, i, axis=axis)``. + Default is 0. + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. If 'periodic', + periodic extrapolation is used. If None (default), it is set to True. + + Attributes + ---------- + x : ndarray, shape (n,) + Breakpoints. The same ``x`` which was passed to the constructor. + c : ndarray, shape (4, n-1, ...) + Coefficients of the polynomials on each segment. The trailing + dimensions match the dimensions of `y`, excluding ``axis``. + For example, if `y` is 1-D, then ``c[k, i]`` is a coefficient for + ``(x-x[i])**(3-k)`` on the segment between ``x[i]`` and ``x[i+1]``. + axis : int + Interpolation axis. The same axis which was passed to the + constructor. + + Methods + ------- + __call__ + derivative + antiderivative + integrate + roots + + See Also + -------- + Akima1DInterpolator : Akima 1D interpolator. + PchipInterpolator : PCHIP 1-D monotonic cubic interpolator. + CubicSpline : Cubic spline data interpolator. + PPoly : Piecewise polynomial in terms of coefficients and breakpoints + + Notes + ----- + If you want to create a higher-order spline matching higher-order + derivatives, use `BPoly.from_derivatives`. + + References + ---------- + .. [1] `Cubic Hermite spline + `_ + on Wikipedia. + """ + + def __init__(self, x, y, dydx, axis=0, extrapolate=None): + if extrapolate is None: + extrapolate = True + + x, dx, y, axis, dydx = prepare_input(x, y, axis, dydx) + + dxr = dx.reshape([dx.shape[0]] + [1] * (y.ndim - 1)) + slope = np.diff(y, axis=0) / dxr + t = (dydx[:-1] + dydx[1:] - 2 * slope) / dxr + + c = np.empty((4, len(x) - 1) + y.shape[1:], dtype=t.dtype) + c[0] = t / dxr + c[1] = (slope - dydx[:-1]) / dxr - t + c[2] = dydx[:-1] + c[3] = y[:-1] + + super().__init__(c, x, extrapolate=extrapolate) + self.axis = axis + + +class PchipInterpolator(CubicHermiteSpline): + r"""PCHIP 1-D monotonic cubic interpolation. + + ``x`` and ``y`` are arrays of values used to approximate some function f, + with ``y = f(x)``. The interpolant uses monotonic cubic splines + to find the value of new points. (PCHIP stands for Piecewise Cubic + Hermite Interpolating Polynomial). + + Parameters + ---------- + x : ndarray, shape (npoints, ) + A 1-D array of monotonically increasing real values. ``x`` cannot + include duplicate values (otherwise f is overspecified) + y : ndarray, shape (..., npoints, ...) + A N-D array of real values. ``y``'s length along the interpolation + axis must be equal to the length of ``x``. Use the ``axis`` + parameter to select the interpolation axis. + + .. deprecated:: 1.13.0 + Complex data is deprecated and will raise an error in SciPy 1.15.0. + If you are trying to use the real components of the passed array, + use ``np.real`` on ``y``. + + axis : int, optional + Axis in the ``y`` array corresponding to the x-coordinate values. Defaults + to ``axis=0``. + extrapolate : bool, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. + + Methods + ------- + __call__ + derivative + antiderivative + roots + + See Also + -------- + CubicHermiteSpline : Piecewise-cubic interpolator. + Akima1DInterpolator : Akima 1D interpolator. + CubicSpline : Cubic spline data interpolator. + PPoly : Piecewise polynomial in terms of coefficients and breakpoints. + + Notes + ----- + The interpolator preserves monotonicity in the interpolation data and does + not overshoot if the data is not smooth. + + The first derivatives are guaranteed to be continuous, but the second + derivatives may jump at :math:`x_k`. + + Determines the derivatives at the points :math:`x_k`, :math:`f'_k`, + by using PCHIP algorithm [1]_. + + Let :math:`h_k = x_{k+1} - x_k`, and :math:`d_k = (y_{k+1} - y_k) / h_k` + are the slopes at internal points :math:`x_k`. + If the signs of :math:`d_k` and :math:`d_{k-1}` are different or either of + them equals zero, then :math:`f'_k = 0`. Otherwise, it is given by the + weighted harmonic mean + + .. math:: + + \frac{w_1 + w_2}{f'_k} = \frac{w_1}{d_{k-1}} + \frac{w_2}{d_k} + + where :math:`w_1 = 2 h_k + h_{k-1}` and :math:`w_2 = h_k + 2 h_{k-1}`. + + The end slopes are set using a one-sided scheme [2]_. + + + References + ---------- + .. [1] F. N. Fritsch and J. Butland, + A method for constructing local + monotone piecewise cubic interpolants, + SIAM J. Sci. Comput., 5(2), 300-304 (1984). + :doi:`10.1137/0905021`. + .. [2] see, e.g., C. Moler, Numerical Computing with Matlab, 2004. + :doi:`10.1137/1.9780898717952` + + """ + + def __init__(self, x, y, axis=0, extrapolate=None): + x, _, y, axis, _ = prepare_input(x, y, axis) + if np.iscomplexobj(y): + msg = ("`PchipInterpolator` only works with real values for `y`. " + "Passing an array with a complex dtype for `y` is deprecated " + "and will raise an error in SciPy 1.15.0. If you are trying to " + "use the real components of the passed array, use `np.real` on " + "the array before passing to `PchipInterpolator`.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) + xp = x.reshape((x.shape[0],) + (1,)*(y.ndim-1)) + dk = self._find_derivatives(xp, y) + super().__init__(x, y, dk, axis=0, extrapolate=extrapolate) + self.axis = axis + + @staticmethod + def _edge_case(h0, h1, m0, m1): + # one-sided three-point estimate for the derivative + d = ((2*h0 + h1)*m0 - h0*m1) / (h0 + h1) + + # try to preserve shape + mask = np.sign(d) != np.sign(m0) + mask2 = (np.sign(m0) != np.sign(m1)) & (np.abs(d) > 3.*np.abs(m0)) + mmm = (~mask) & mask2 + + d[mask] = 0. + d[mmm] = 3.*m0[mmm] + + return d + + @staticmethod + def _find_derivatives(x, y): + # Determine the derivatives at the points y_k, d_k, by using + # PCHIP algorithm is: + # We choose the derivatives at the point x_k by + # Let m_k be the slope of the kth segment (between k and k+1) + # If m_k=0 or m_{k-1}=0 or sgn(m_k) != sgn(m_{k-1}) then d_k == 0 + # else use weighted harmonic mean: + # w_1 = 2h_k + h_{k-1}, w_2 = h_k + 2h_{k-1} + # 1/d_k = 1/(w_1 + w_2)*(w_1 / m_k + w_2 / m_{k-1}) + # where h_k is the spacing between x_k and x_{k+1} + y_shape = y.shape + if y.ndim == 1: + # So that _edge_case doesn't end up assigning to scalars + x = x[:, None] + y = y[:, None] + + hk = x[1:] - x[:-1] + mk = (y[1:] - y[:-1]) / hk + + if y.shape[0] == 2: + # edge case: only have two points, use linear interpolation + dk = np.zeros_like(y) + dk[0] = mk + dk[1] = mk + return dk.reshape(y_shape) + + smk = np.sign(mk) + condition = (smk[1:] != smk[:-1]) | (mk[1:] == 0) | (mk[:-1] == 0) + + w1 = 2*hk[1:] + hk[:-1] + w2 = hk[1:] + 2*hk[:-1] + + # values where division by zero occurs will be excluded + # by 'condition' afterwards + with np.errstate(divide='ignore', invalid='ignore'): + whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2) + + dk = np.zeros_like(y) + dk[1:-1][condition] = 0.0 + dk[1:-1][~condition] = 1.0 / whmean[~condition] + + # special case endpoints, as suggested in + # Cleve Moler, Numerical Computing with MATLAB, Chap 3.6 (pchiptx.m) + dk[0] = PchipInterpolator._edge_case(hk[0], hk[1], mk[0], mk[1]) + dk[-1] = PchipInterpolator._edge_case(hk[-1], hk[-2], mk[-1], mk[-2]) + + return dk.reshape(y_shape) + + +def pchip_interpolate(xi, yi, x, der=0, axis=0): + """ + Convenience function for pchip interpolation. + + xi and yi are arrays of values used to approximate some function f, + with ``yi = f(xi)``. The interpolant uses monotonic cubic splines + to find the value of new points x and the derivatives there. + + See `scipy.interpolate.PchipInterpolator` for details. + + Parameters + ---------- + xi : array_like + A sorted list of x-coordinates, of length N. + yi : array_like + A 1-D array of real values. `yi`'s length along the interpolation + axis must be equal to the length of `xi`. If N-D array, use axis + parameter to select correct axis. + + .. deprecated:: 1.13.0 + Complex data is deprecated and will raise an error in + SciPy 1.15.0. If you are trying to use the real components of + the passed array, use ``np.real`` on `yi`. + + x : scalar or array_like + Of length M. + der : int or list, optional + Derivatives to extract. The 0th derivative can be included to + return the function value. + axis : int, optional + Axis in the yi array corresponding to the x-coordinate values. + + Returns + ------- + y : scalar or array_like + The result, of length R or length M or M by R. + + See Also + -------- + PchipInterpolator : PCHIP 1-D monotonic cubic interpolator. + + Examples + -------- + We can interpolate 2D observed data using pchip interpolation: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import pchip_interpolate + >>> x_observed = np.linspace(0.0, 10.0, 11) + >>> y_observed = np.sin(x_observed) + >>> x = np.linspace(min(x_observed), max(x_observed), num=100) + >>> y = pchip_interpolate(x_observed, y_observed, x) + >>> plt.plot(x_observed, y_observed, "o", label="observation") + >>> plt.plot(x, y, label="pchip interpolation") + >>> plt.legend() + >>> plt.show() + + """ + P = PchipInterpolator(xi, yi, axis=axis) + + if der == 0: + return P(x) + elif _isscalar(der): + return P.derivative(der)(x) + else: + return [P.derivative(nu)(x) for nu in der] + + +class Akima1DInterpolator(CubicHermiteSpline): + r""" + Akima interpolator + + Fit piecewise cubic polynomials, given vectors x and y. The interpolation + method by Akima uses a continuously differentiable sub-spline built from + piecewise cubic polynomials. The resultant curve passes through the given + data points and will appear smooth and natural. + + Parameters + ---------- + x : ndarray, shape (npoints, ) + 1-D array of monotonically increasing real values. + y : ndarray, shape (..., npoints, ...) + N-D array of real values. The length of ``y`` along the interpolation axis + must be equal to the length of ``x``. Use the ``axis`` parameter to + select the interpolation axis. + + .. deprecated:: 1.13.0 + Complex data is deprecated and will raise an error in SciPy 1.15.0. + If you are trying to use the real components of the passed array, + use ``np.real`` on ``y``. + + axis : int, optional + Axis in the ``y`` array corresponding to the x-coordinate values. Defaults + to ``axis=0``. + method : {'akima', 'makima'}, optional + If ``"makima"``, use the modified Akima interpolation [2]_. + Defaults to ``"akima"``, use the Akima interpolation [1]_. + + .. versionadded:: 1.13.0 + + Methods + ------- + __call__ + derivative + antiderivative + roots + + See Also + -------- + PchipInterpolator : PCHIP 1-D monotonic cubic interpolator. + CubicSpline : Cubic spline data interpolator. + PPoly : Piecewise polynomial in terms of coefficients and breakpoints + + Notes + ----- + .. versionadded:: 0.14 + + Use only for precise data, as the fitted curve passes through the given + points exactly. This routine is useful for plotting a pleasingly smooth + curve through a few given points for purposes of plotting. + + Let :math:`\delta_i = (y_{i+1} - y_i) / (x_{i+1} - x_i)` be the slopes of + the interval :math:`\left[x_i, x_{i+1}\right)`. Akima's derivative at + :math:`x_i` is defined as: + + .. math:: + + d_i = \frac{w_1}{w_1 + w_2}\delta_{i-1} + \frac{w_2}{w_1 + w_2}\delta_i + + In the Akima interpolation [1]_ (``method="akima"``), the weights are: + + .. math:: + + \begin{aligned} + w_1 &= |\delta_{i+1} - \delta_i| \\ + w_2 &= |\delta_{i-1} - \delta_{i-2}| + \end{aligned} + + In the modified Akima interpolation [2]_ (``method="makima"``), + to eliminate overshoot and avoid edge cases of both numerator and + denominator being equal to 0, the weights are modified as follows: + + .. math:: + + \begin{align*} + w_1 &= |\delta_{i+1} - \delta_i| + |\delta_{i+1} + \delta_i| / 2 \\ + w_2 &= |\delta_{i-1} - \delta_{i-2}| + |\delta_{i-1} + \delta_{i-2}| / 2 + \end{align*} + + Examples + -------- + Comparison of ``method="akima"`` and ``method="makima"``: + + >>> import numpy as np + >>> from scipy.interpolate import Akima1DInterpolator + >>> import matplotlib.pyplot as plt + >>> x = np.linspace(1, 7, 7) + >>> y = np.array([-1, -1, -1, 0, 1, 1, 1]) + >>> xs = np.linspace(min(x), max(x), num=100) + >>> y_akima = Akima1DInterpolator(x, y, method="akima")(xs) + >>> y_makima = Akima1DInterpolator(x, y, method="makima")(xs) + + >>> fig, ax = plt.subplots() + >>> ax.plot(x, y, "o", label="data") + >>> ax.plot(xs, y_akima, label="akima") + >>> ax.plot(xs, y_makima, label="makima") + >>> ax.legend() + >>> fig.show() + + The overshoot that occured in ``"akima"`` has been avoided in ``"makima"``. + + References + ---------- + .. [1] A new method of interpolation and smooth curve fitting based + on local procedures. Hiroshi Akima, J. ACM, October 1970, 17(4), + 589-602. :doi:`10.1145/321607.321609` + .. [2] Makima Piecewise Cubic Interpolation. Cleve Moler and Cosmin Ionita, 2019. + https://blogs.mathworks.com/cleve/2019/04/29/makima-piecewise-cubic-interpolation/ + + """ + + def __init__(self, x, y, axis=0, *, method: Literal["akima", "makima"]="akima"): + if method not in {"akima", "makima"}: + raise NotImplementedError(f"`method`={method} is unsupported.") + # Original implementation in MATLAB by N. Shamsundar (BSD licensed), see + # https://www.mathworks.com/matlabcentral/fileexchange/1814-akima-interpolation + x, dx, y, axis, _ = prepare_input(x, y, axis) + + if np.iscomplexobj(y): + msg = ("`Akima1DInterpolator` only works with real values for `y`. " + "Passing an array with a complex dtype for `y` is deprecated " + "and will raise an error in SciPy 1.15.0. If you are trying to " + "use the real components of the passed array, use `np.real` on " + "the array before passing to `Akima1DInterpolator`.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + # determine slopes between breakpoints + m = np.empty((x.size + 3, ) + y.shape[1:]) + dx = dx[(slice(None), ) + (None, ) * (y.ndim - 1)] + m[2:-2] = np.diff(y, axis=0) / dx + + # add two additional points on the left ... + m[1] = 2. * m[2] - m[3] + m[0] = 2. * m[1] - m[2] + # ... and on the right + m[-2] = 2. * m[-3] - m[-4] + m[-1] = 2. * m[-2] - m[-3] + + # if m1 == m2 != m3 == m4, the slope at the breakpoint is not + # defined. This is the fill value: + t = .5 * (m[3:] + m[:-3]) + # get the denominator of the slope t + dm = np.abs(np.diff(m, axis=0)) + if method == "makima": + pm = np.abs(m[1:] + m[:-1]) + f1 = dm[2:] + 0.5 * pm[2:] + f2 = dm[:-2] + 0.5 * pm[:-2] + else: + f1 = dm[2:] + f2 = dm[:-2] + f12 = f1 + f2 + # These are the mask of where the slope at breakpoint is defined: + ind = np.nonzero(f12 > 1e-9 * np.max(f12, initial=-np.inf)) + x_ind, y_ind = ind[0], ind[1:] + # Set the slope at breakpoint + t[ind] = (f1[ind] * m[(x_ind + 1,) + y_ind] + + f2[ind] * m[(x_ind + 2,) + y_ind]) / f12[ind] + + super().__init__(x, y, t, axis=0, extrapolate=False) + self.axis = axis + + def extend(self, c, x, right=True): + raise NotImplementedError("Extending a 1-D Akima interpolator is not " + "yet implemented") + + # These are inherited from PPoly, but they do not produce an Akima + # interpolator. Hence stub them out. + @classmethod + def from_spline(cls, tck, extrapolate=None): + raise NotImplementedError("This method does not make sense for " + "an Akima interpolator.") + + @classmethod + def from_bernstein_basis(cls, bp, extrapolate=None): + raise NotImplementedError("This method does not make sense for " + "an Akima interpolator.") + + +class CubicSpline(CubicHermiteSpline): + """Cubic spline data interpolator. + + Interpolate data with a piecewise cubic polynomial which is twice + continuously differentiable [1]_. The result is represented as a `PPoly` + instance with breakpoints matching the given data. + + Parameters + ---------- + x : array_like, shape (n,) + 1-D array containing values of the independent variable. + Values must be real, finite and in strictly increasing order. + y : array_like + Array containing values of the dependent variable. It can have + arbitrary number of dimensions, but the length along ``axis`` + (see below) must match the length of ``x``. Values must be finite. + axis : int, optional + Axis along which `y` is assumed to be varying. Meaning that for + ``x[i]`` the corresponding values are ``np.take(y, i, axis=axis)``. + Default is 0. + bc_type : string or 2-tuple, optional + Boundary condition type. Two additional equations, given by the + boundary conditions, are required to determine all coefficients of + polynomials on each segment [2]_. + + If `bc_type` is a string, then the specified condition will be applied + at both ends of a spline. Available conditions are: + + * 'not-a-knot' (default): The first and second segment at a curve end + are the same polynomial. It is a good default when there is no + information on boundary conditions. + * 'periodic': The interpolated functions is assumed to be periodic + of period ``x[-1] - x[0]``. The first and last value of `y` must be + identical: ``y[0] == y[-1]``. This boundary condition will result in + ``y'[0] == y'[-1]`` and ``y''[0] == y''[-1]``. + * 'clamped': The first derivative at curves ends are zero. Assuming + a 1D `y`, ``bc_type=((1, 0.0), (1, 0.0))`` is the same condition. + * 'natural': The second derivative at curve ends are zero. Assuming + a 1D `y`, ``bc_type=((2, 0.0), (2, 0.0))`` is the same condition. + + If `bc_type` is a 2-tuple, the first and the second value will be + applied at the curve start and end respectively. The tuple values can + be one of the previously mentioned strings (except 'periodic') or a + tuple `(order, deriv_values)` allowing to specify arbitrary + derivatives at curve ends: + + * `order`: the derivative order, 1 or 2. + * `deriv_value`: array_like containing derivative values, shape must + be the same as `y`, excluding ``axis`` dimension. For example, if + `y` is 1-D, then `deriv_value` must be a scalar. If `y` is 3-D with + the shape (n0, n1, n2) and axis=2, then `deriv_value` must be 2-D + and have the shape (n0, n1). + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. If 'periodic', + periodic extrapolation is used. If None (default), ``extrapolate`` is + set to 'periodic' for ``bc_type='periodic'`` and to True otherwise. + + Attributes + ---------- + x : ndarray, shape (n,) + Breakpoints. The same ``x`` which was passed to the constructor. + c : ndarray, shape (4, n-1, ...) + Coefficients of the polynomials on each segment. The trailing + dimensions match the dimensions of `y`, excluding ``axis``. + For example, if `y` is 1-d, then ``c[k, i]`` is a coefficient for + ``(x-x[i])**(3-k)`` on the segment between ``x[i]`` and ``x[i+1]``. + axis : int + Interpolation axis. The same axis which was passed to the + constructor. + + Methods + ------- + __call__ + derivative + antiderivative + integrate + roots + + See Also + -------- + Akima1DInterpolator : Akima 1D interpolator. + PchipInterpolator : PCHIP 1-D monotonic cubic interpolator. + PPoly : Piecewise polynomial in terms of coefficients and breakpoints. + + Notes + ----- + Parameters `bc_type` and ``extrapolate`` work independently, i.e. the + former controls only construction of a spline, and the latter only + evaluation. + + When a boundary condition is 'not-a-knot' and n = 2, it is replaced by + a condition that the first derivative is equal to the linear interpolant + slope. When both boundary conditions are 'not-a-knot' and n = 3, the + solution is sought as a parabola passing through given points. + + When 'not-a-knot' boundary conditions is applied to both ends, the + resulting spline will be the same as returned by `splrep` (with ``s=0``) + and `InterpolatedUnivariateSpline`, but these two methods use a + representation in B-spline basis. + + .. versionadded:: 0.18.0 + + Examples + -------- + In this example the cubic spline is used to interpolate a sampled sinusoid. + You can see that the spline continuity property holds for the first and + second derivatives and violates only for the third derivative. + + >>> import numpy as np + >>> from scipy.interpolate import CubicSpline + >>> import matplotlib.pyplot as plt + >>> x = np.arange(10) + >>> y = np.sin(x) + >>> cs = CubicSpline(x, y) + >>> xs = np.arange(-0.5, 9.6, 0.1) + >>> fig, ax = plt.subplots(figsize=(6.5, 4)) + >>> ax.plot(x, y, 'o', label='data') + >>> ax.plot(xs, np.sin(xs), label='true') + >>> ax.plot(xs, cs(xs), label="S") + >>> ax.plot(xs, cs(xs, 1), label="S'") + >>> ax.plot(xs, cs(xs, 2), label="S''") + >>> ax.plot(xs, cs(xs, 3), label="S'''") + >>> ax.set_xlim(-0.5, 9.5) + >>> ax.legend(loc='lower left', ncol=2) + >>> plt.show() + + In the second example, the unit circle is interpolated with a spline. A + periodic boundary condition is used. You can see that the first derivative + values, ds/dx=0, ds/dy=1 at the periodic point (1, 0) are correctly + computed. Note that a circle cannot be exactly represented by a cubic + spline. To increase precision, more breakpoints would be required. + + >>> theta = 2 * np.pi * np.linspace(0, 1, 5) + >>> y = np.c_[np.cos(theta), np.sin(theta)] + >>> cs = CubicSpline(theta, y, bc_type='periodic') + >>> print("ds/dx={:.1f} ds/dy={:.1f}".format(cs(0, 1)[0], cs(0, 1)[1])) + ds/dx=0.0 ds/dy=1.0 + >>> xs = 2 * np.pi * np.linspace(0, 1, 100) + >>> fig, ax = plt.subplots(figsize=(6.5, 4)) + >>> ax.plot(y[:, 0], y[:, 1], 'o', label='data') + >>> ax.plot(np.cos(xs), np.sin(xs), label='true') + >>> ax.plot(cs(xs)[:, 0], cs(xs)[:, 1], label='spline') + >>> ax.axes.set_aspect('equal') + >>> ax.legend(loc='center') + >>> plt.show() + + The third example is the interpolation of a polynomial y = x**3 on the + interval 0 <= x<= 1. A cubic spline can represent this function exactly. + To achieve that we need to specify values and first derivatives at + endpoints of the interval. Note that y' = 3 * x**2 and thus y'(0) = 0 and + y'(1) = 3. + + >>> cs = CubicSpline([0, 1], [0, 1], bc_type=((1, 0), (1, 3))) + >>> x = np.linspace(0, 1) + >>> np.allclose(x**3, cs(x)) + True + + References + ---------- + .. [1] `Cubic Spline Interpolation + `_ + on Wikiversity. + .. [2] Carl de Boor, "A Practical Guide to Splines", Springer-Verlag, 1978. + """ + + def __init__(self, x, y, axis=0, bc_type='not-a-knot', extrapolate=None): + x, dx, y, axis, _ = prepare_input(x, y, axis) + n = len(x) + + bc, y = self._validate_bc(bc_type, y, y.shape[1:], axis) + + if extrapolate is None: + if bc[0] == 'periodic': + extrapolate = 'periodic' + else: + extrapolate = True + + if y.size == 0: + # bail out early for zero-sized arrays + s = np.zeros_like(y) + else: + dxr = dx.reshape([dx.shape[0]] + [1] * (y.ndim - 1)) + slope = np.diff(y, axis=0) / dxr + + # If bc is 'not-a-knot' this change is just a convention. + # If bc is 'periodic' then we already checked that y[0] == y[-1], + # and the spline is just a constant, we handle this case in the + # same way by setting the first derivatives to slope, which is 0. + if n == 2: + if bc[0] in ['not-a-knot', 'periodic']: + bc[0] = (1, slope[0]) + if bc[1] in ['not-a-knot', 'periodic']: + bc[1] = (1, slope[0]) + + # This is a special case, when both conditions are 'not-a-knot' + # and n == 3. In this case 'not-a-knot' can't be handled regularly + # as the both conditions are identical. We handle this case by + # constructing a parabola passing through given points. + if n == 3 and bc[0] == 'not-a-knot' and bc[1] == 'not-a-knot': + A = np.zeros((3, 3)) # This is a standard matrix. + b = np.empty((3,) + y.shape[1:], dtype=y.dtype) + + A[0, 0] = 1 + A[0, 1] = 1 + A[1, 0] = dx[1] + A[1, 1] = 2 * (dx[0] + dx[1]) + A[1, 2] = dx[0] + A[2, 1] = 1 + A[2, 2] = 1 + + b[0] = 2 * slope[0] + b[1] = 3 * (dxr[0] * slope[1] + dxr[1] * slope[0]) + b[2] = 2 * slope[1] + + s = solve(A, b, overwrite_a=True, overwrite_b=True, + check_finite=False) + elif n == 3 and bc[0] == 'periodic': + # In case when number of points is 3 we compute the derivatives + # manually + t = (slope / dxr).sum(0) / (1. / dxr).sum(0) + s = np.broadcast_to(t, (n,) + y.shape[1:]) + else: + # Find derivative values at each x[i] by solving a tridiagonal + # system. + A = np.zeros((3, n)) # This is a banded matrix representation. + b = np.empty((n,) + y.shape[1:], dtype=y.dtype) + + # Filling the system for i=1..n-2 + # (x[i-1] - x[i]) * s[i-1] +\ + # 2 * ((x[i] - x[i-1]) + (x[i+1] - x[i])) * s[i] +\ + # (x[i] - x[i-1]) * s[i+1] =\ + # 3 * ((x[i+1] - x[i])*(y[i] - y[i-1])/(x[i] - x[i-1]) +\ + # (x[i] - x[i-1])*(y[i+1] - y[i])/(x[i+1] - x[i])) + + A[1, 1:-1] = 2 * (dx[:-1] + dx[1:]) # The diagonal + A[0, 2:] = dx[:-1] # The upper diagonal + A[-1, :-2] = dx[1:] # The lower diagonal + + b[1:-1] = 3 * (dxr[1:] * slope[:-1] + dxr[:-1] * slope[1:]) + + bc_start, bc_end = bc + + if bc_start == 'periodic': + # Due to the periodicity, and because y[-1] = y[0], the + # linear system has (n-1) unknowns/equations instead of n: + A = A[:, 0:-1] + A[1, 0] = 2 * (dx[-1] + dx[0]) + A[0, 1] = dx[-1] + + b = b[:-1] + + # Also, due to the periodicity, the system is not tri-diagonal. + # We need to compute a "condensed" matrix of shape (n-2, n-2). + # See https://web.archive.org/web/20151220180652/http://www.cfm.brown.edu/people/gk/chap6/node14.html + # for more explanations. + # The condensed matrix is obtained by removing the last column + # and last row of the (n-1, n-1) system matrix. The removed + # values are saved in scalar variables with the (n-1, n-1) + # system matrix indices forming their names: + a_m1_0 = dx[-2] # lower left corner value: A[-1, 0] + a_m1_m2 = dx[-1] + a_m1_m1 = 2 * (dx[-1] + dx[-2]) + a_m2_m1 = dx[-3] + a_0_m1 = dx[0] + + b[0] = 3 * (dxr[0] * slope[-1] + dxr[-1] * slope[0]) + b[-1] = 3 * (dxr[-1] * slope[-2] + dxr[-2] * slope[-1]) + + Ac = A[:, :-1] + b1 = b[:-1] + b2 = np.zeros_like(b1) + b2[0] = -a_0_m1 + b2[-1] = -a_m2_m1 + + # s1 and s2 are the solutions of (n-2, n-2) system + s1 = solve_banded((1, 1), Ac, b1, overwrite_ab=False, + overwrite_b=False, check_finite=False) + + s2 = solve_banded((1, 1), Ac, b2, overwrite_ab=False, + overwrite_b=False, check_finite=False) + + # computing the s[n-2] solution: + s_m1 = ((b[-1] - a_m1_0 * s1[0] - a_m1_m2 * s1[-1]) / + (a_m1_m1 + a_m1_0 * s2[0] + a_m1_m2 * s2[-1])) + + # s is the solution of the (n, n) system: + s = np.empty((n,) + y.shape[1:], dtype=y.dtype) + s[:-2] = s1 + s_m1 * s2 + s[-2] = s_m1 + s[-1] = s[0] + else: + if bc_start == 'not-a-knot': + A[1, 0] = dx[1] + A[0, 1] = x[2] - x[0] + d = x[2] - x[0] + b[0] = ((dxr[0] + 2*d) * dxr[1] * slope[0] + + dxr[0]**2 * slope[1]) / d + elif bc_start[0] == 1: + A[1, 0] = 1 + A[0, 1] = 0 + b[0] = bc_start[1] + elif bc_start[0] == 2: + A[1, 0] = 2 * dx[0] + A[0, 1] = dx[0] + b[0] = -0.5 * bc_start[1] * dx[0]**2 + 3 * (y[1] - y[0]) + + if bc_end == 'not-a-knot': + A[1, -1] = dx[-2] + A[-1, -2] = x[-1] - x[-3] + d = x[-1] - x[-3] + b[-1] = ((dxr[-1]**2*slope[-2] + + (2*d + dxr[-1])*dxr[-2]*slope[-1]) / d) + elif bc_end[0] == 1: + A[1, -1] = 1 + A[-1, -2] = 0 + b[-1] = bc_end[1] + elif bc_end[0] == 2: + A[1, -1] = 2 * dx[-1] + A[-1, -2] = dx[-1] + b[-1] = 0.5 * bc_end[1] * dx[-1]**2 + 3 * (y[-1] - y[-2]) + + s = solve_banded((1, 1), A, b, overwrite_ab=True, + overwrite_b=True, check_finite=False) + + super().__init__(x, y, s, axis=0, extrapolate=extrapolate) + self.axis = axis + + @staticmethod + def _validate_bc(bc_type, y, expected_deriv_shape, axis): + """Validate and prepare boundary conditions. + + Returns + ------- + validated_bc : 2-tuple + Boundary conditions for a curve start and end. + y : ndarray + y casted to complex dtype if one of the boundary conditions has + complex dtype. + """ + if isinstance(bc_type, str): + if bc_type == 'periodic': + if not np.allclose(y[0], y[-1], rtol=1e-15, atol=1e-15): + raise ValueError( + f"The first and last `y` point along axis {axis} must " + "be identical (within machine precision) when " + "bc_type='periodic'.") + + bc_type = (bc_type, bc_type) + + else: + if len(bc_type) != 2: + raise ValueError("`bc_type` must contain 2 elements to " + "specify start and end conditions.") + + if 'periodic' in bc_type: + raise ValueError("'periodic' `bc_type` is defined for both " + "curve ends and cannot be used with other " + "boundary conditions.") + + validated_bc = [] + for bc in bc_type: + if isinstance(bc, str): + if bc == 'clamped': + validated_bc.append((1, np.zeros(expected_deriv_shape))) + elif bc == 'natural': + validated_bc.append((2, np.zeros(expected_deriv_shape))) + elif bc in ['not-a-knot', 'periodic']: + validated_bc.append(bc) + else: + raise ValueError(f"bc_type={bc} is not allowed.") + else: + try: + deriv_order, deriv_value = bc + except Exception as e: + raise ValueError( + "A specified derivative value must be " + "given in the form (order, value)." + ) from e + + if deriv_order not in [1, 2]: + raise ValueError("The specified derivative order must " + "be 1 or 2.") + + deriv_value = np.asarray(deriv_value) + if deriv_value.shape != expected_deriv_shape: + raise ValueError( + "`deriv_value` shape {} is not the expected one {}." + .format(deriv_value.shape, expected_deriv_shape)) + + if np.issubdtype(deriv_value.dtype, np.complexfloating): + y = y.astype(complex, copy=False) + + validated_bc.append((deriv_order, deriv_value)) + + return validated_bc, y diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack2.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack2.py new file mode 100644 index 0000000000000000000000000000000000000000..88e916287c98ea12f75d06871dc4a493082493a2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack2.py @@ -0,0 +1,2362 @@ +""" +fitpack --- curve and surface fitting with splines + +fitpack is based on a collection of Fortran routines DIERCKX +by P. Dierckx (see http://www.netlib.org/dierckx/) transformed +to double routines by Pearu Peterson. +""" +# Created by Pearu Peterson, June,August 2003 +__all__ = [ + 'UnivariateSpline', + 'InterpolatedUnivariateSpline', + 'LSQUnivariateSpline', + 'BivariateSpline', + 'LSQBivariateSpline', + 'SmoothBivariateSpline', + 'LSQSphereBivariateSpline', + 'SmoothSphereBivariateSpline', + 'RectBivariateSpline', + 'RectSphereBivariateSpline'] + + +import warnings + +from numpy import zeros, concatenate, ravel, diff, array, ones # noqa:F401 +import numpy as np + +from . import _fitpack_impl +from . import dfitpack + + +dfitpack_int = dfitpack.types.intvar.dtype + + +# ############### Univariate spline #################### + +_curfit_messages = {1: """ +The required storage space exceeds the available storage space, as +specified by the parameter nest: nest too small. If nest is already +large (say nest > m/2), it may also indicate that s is too small. +The approximation returned is the weighted least-squares spline +according to the knots t[0],t[1],...,t[n-1]. (n=nest) the parameter fp +gives the corresponding weighted sum of squared residuals (fp>s). +""", + 2: """ +A theoretically impossible result was found during the iteration +process for finding a smoothing spline with fp = s: s too small. +There is an approximation returned but the corresponding weighted sum +of squared residuals does not satisfy the condition abs(fp-s)/s < tol.""", + 3: """ +The maximal number of iterations maxit (set to 20 by the program) +allowed for finding a smoothing spline with fp=s has been reached: s +too small. +There is an approximation returned but the corresponding weighted sum +of squared residuals does not satisfy the condition abs(fp-s)/s < tol.""", + 10: """ +Error on entry, no approximation returned. The following conditions +must hold: +xb<=x[0]0, i=0..m-1 +if iopt=-1: + xb>> import numpy as np + >>> from scipy.interpolate import UnivariateSpline + >>> x, y = np.array([1, 2, 3, 4]), np.array([1, np.nan, 3, 4]) + >>> w = np.isnan(y) + >>> y[w] = 0. + >>> spl = UnivariateSpline(x, y, w=~w) + + Notice the need to replace a ``nan`` by a numerical value (precise value + does not matter as long as the corresponding weight is zero.) + + References + ---------- + Based on algorithms described in [1]_, [2]_, [3]_, and [4]_: + + .. [1] P. Dierckx, "An algorithm for smoothing, differentiation and + integration of experimental data using spline functions", + J.Comp.Appl.Maths 1 (1975) 165-184. + .. [2] P. Dierckx, "A fast algorithm for smoothing data on a rectangular + grid while using spline functions", SIAM J.Numer.Anal. 19 (1982) + 1286-1304. + .. [3] P. Dierckx, "An improved algorithm for curve fitting with spline + functions", report tw54, Dept. Computer Science,K.U. Leuven, 1981. + .. [4] P. Dierckx, "Curve and surface fitting with splines", Monographs on + Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import UnivariateSpline + >>> rng = np.random.default_rng() + >>> x = np.linspace(-3, 3, 50) + >>> y = np.exp(-x**2) + 0.1 * rng.standard_normal(50) + >>> plt.plot(x, y, 'ro', ms=5) + + Use the default value for the smoothing parameter: + + >>> spl = UnivariateSpline(x, y) + >>> xs = np.linspace(-3, 3, 1000) + >>> plt.plot(xs, spl(xs), 'g', lw=3) + + Manually change the amount of smoothing: + + >>> spl.set_smoothing_factor(0.5) + >>> plt.plot(xs, spl(xs), 'b', lw=3) + >>> plt.show() + + """ + + def __init__(self, x, y, w=None, bbox=[None]*2, k=3, s=None, + ext=0, check_finite=False): + + x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, s, ext, + check_finite) + + # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier + data = dfitpack.fpcurf0(x, y, k, w=w, xb=bbox[0], + xe=bbox[1], s=s) + if data[-1] == 1: + # nest too small, setting to maximum bound + data = self._reset_nest(data) + self._data = data + self._reset_class() + + @staticmethod + def validate_input(x, y, w, bbox, k, s, ext, check_finite): + x, y, bbox = np.asarray(x), np.asarray(y), np.asarray(bbox) + if w is not None: + w = np.asarray(w) + if check_finite: + w_finite = np.isfinite(w).all() if w is not None else True + if (not np.isfinite(x).all() or not np.isfinite(y).all() or + not w_finite): + raise ValueError("x and y array must not contain " + "NaNs or infs.") + if s is None or s > 0: + if not np.all(diff(x) >= 0.0): + raise ValueError("x must be increasing if s > 0") + else: + if not np.all(diff(x) > 0.0): + raise ValueError("x must be strictly increasing if s = 0") + if x.size != y.size: + raise ValueError("x and y should have a same length") + elif w is not None and not x.size == y.size == w.size: + raise ValueError("x, y, and w should have a same length") + elif bbox.shape != (2,): + raise ValueError("bbox shape should be (2,)") + elif not (1 <= k <= 5): + raise ValueError("k should be 1 <= k <= 5") + elif s is not None and not s >= 0.0: + raise ValueError("s should be s >= 0.0") + + try: + ext = _extrap_modes[ext] + except KeyError as e: + raise ValueError("Unknown extrapolation mode %s." % ext) from e + + return x, y, w, bbox, ext + + @classmethod + def _from_tck(cls, tck, ext=0): + """Construct a spline object from given tck""" + self = cls.__new__(cls) + t, c, k = tck + self._eval_args = tck + # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier + self._data = (None, None, None, None, None, k, None, len(t), t, + c, None, None, None, None) + self.ext = ext + return self + + def _reset_class(self): + data = self._data + n, t, c, k, ier = data[7], data[8], data[9], data[5], data[-1] + self._eval_args = t[:n], c[:n], k + if ier == 0: + # the spline returned has a residual sum of squares fp + # such that abs(fp-s)/s <= tol with tol a relative + # tolerance set to 0.001 by the program + pass + elif ier == -1: + # the spline returned is an interpolating spline + self._set_class(InterpolatedUnivariateSpline) + elif ier == -2: + # the spline returned is the weighted least-squares + # polynomial of degree k. In this extreme case fp gives + # the upper bound fp0 for the smoothing factor s. + self._set_class(LSQUnivariateSpline) + else: + # error + if ier == 1: + self._set_class(LSQUnivariateSpline) + message = _curfit_messages.get(ier, 'ier=%s' % (ier)) + warnings.warn(message, stacklevel=3) + + def _set_class(self, cls): + self._spline_class = cls + if self.__class__ in (UnivariateSpline, InterpolatedUnivariateSpline, + LSQUnivariateSpline): + self.__class__ = cls + else: + # It's an unknown subclass -- don't change class. cf. #731 + pass + + def _reset_nest(self, data, nest=None): + n = data[10] + if nest is None: + k, m = data[5], len(data[0]) + nest = m+k+1 # this is the maximum bound for nest + else: + if not n <= nest: + raise ValueError("`nest` can only be increased") + t, c, fpint, nrdata = (np.resize(data[j], nest) for j in + [8, 9, 11, 12]) + + args = data[:8] + (t, c, n, fpint, nrdata, data[13]) + data = dfitpack.fpcurf1(*args) + return data + + def set_smoothing_factor(self, s): + """ Continue spline computation with the given smoothing + factor s and with the knots found at the last call. + + This routine modifies the spline in place. + + """ + data = self._data + if data[6] == -1: + warnings.warn('smoothing factor unchanged for' + 'LSQ spline with fixed knots', + stacklevel=2) + return + args = data[:6] + (s,) + data[7:] + data = dfitpack.fpcurf1(*args) + if data[-1] == 1: + # nest too small, setting to maximum bound + data = self._reset_nest(data) + self._data = data + self._reset_class() + + def __call__(self, x, nu=0, ext=None): + """ + Evaluate spline (or its nu-th derivative) at positions x. + + Parameters + ---------- + x : array_like + A 1-D array of points at which to return the value of the smoothed + spline or its derivatives. Note: `x` can be unordered but the + evaluation is more efficient if `x` is (partially) ordered. + nu : int + The order of derivative of the spline to compute. + ext : int + Controls the value returned for elements of `x` not in the + interval defined by the knot sequence. + + * if ext=0 or 'extrapolate', return the extrapolated value. + * if ext=1 or 'zeros', return 0 + * if ext=2 or 'raise', raise a ValueError + * if ext=3 or 'const', return the boundary value. + + The default value is 0, passed from the initialization of + UnivariateSpline. + + """ + x = np.asarray(x) + # empty input yields empty output + if x.size == 0: + return array([]) + if ext is None: + ext = self.ext + else: + try: + ext = _extrap_modes[ext] + except KeyError as e: + raise ValueError("Unknown extrapolation mode %s." % ext) from e + return _fitpack_impl.splev(x, self._eval_args, der=nu, ext=ext) + + def get_knots(self): + """ Return positions of interior knots of the spline. + + Internally, the knot vector contains ``2*k`` additional boundary knots. + """ + data = self._data + k, n = data[5], data[7] + return data[8][k:n-k] + + def get_coeffs(self): + """Return spline coefficients.""" + data = self._data + k, n = data[5], data[7] + return data[9][:n-k-1] + + def get_residual(self): + """Return weighted sum of squared residuals of the spline approximation. + + This is equivalent to:: + + sum((w[i] * (y[i]-spl(x[i])))**2, axis=0) + + """ + return self._data[10] + + def integral(self, a, b): + """ Return definite integral of the spline between two given points. + + Parameters + ---------- + a : float + Lower limit of integration. + b : float + Upper limit of integration. + + Returns + ------- + integral : float + The value of the definite integral of the spline between limits. + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import UnivariateSpline + >>> x = np.linspace(0, 3, 11) + >>> y = x**2 + >>> spl = UnivariateSpline(x, y) + >>> spl.integral(0, 3) + 9.0 + + which agrees with :math:`\\int x^2 dx = x^3 / 3` between the limits + of 0 and 3. + + A caveat is that this routine assumes the spline to be zero outside of + the data limits: + + >>> spl.integral(-1, 4) + 9.0 + >>> spl.integral(-1, 0) + 0.0 + + """ + return _fitpack_impl.splint(a, b, self._eval_args) + + def derivatives(self, x): + """ Return all derivatives of the spline at the point x. + + Parameters + ---------- + x : float + The point to evaluate the derivatives at. + + Returns + ------- + der : ndarray, shape(k+1,) + Derivatives of the orders 0 to k. + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import UnivariateSpline + >>> x = np.linspace(0, 3, 11) + >>> y = x**2 + >>> spl = UnivariateSpline(x, y) + >>> spl.derivatives(1.5) + array([2.25, 3.0, 2.0, 0]) + + """ + return _fitpack_impl.spalde(x, self._eval_args) + + def roots(self): + """ Return the zeros of the spline. + + Notes + ----- + Restriction: only cubic splines are supported by FITPACK. For non-cubic + splines, use `PPoly.root` (see below for an example). + + Examples + -------- + + For some data, this method may miss a root. This happens when one of + the spline knots (which FITPACK places automatically) happens to + coincide with the true root. A workaround is to convert to `PPoly`, + which uses a different root-finding algorithm. + + For example, + + >>> x = [1.96, 1.97, 1.98, 1.99, 2.00, 2.01, 2.02, 2.03, 2.04, 2.05] + >>> y = [-6.365470e-03, -4.790580e-03, -3.204320e-03, -1.607270e-03, + ... 4.440892e-16, 1.616930e-03, 3.243000e-03, 4.877670e-03, + ... 6.520430e-03, 8.170770e-03] + >>> from scipy.interpolate import UnivariateSpline + >>> spl = UnivariateSpline(x, y, s=0) + >>> spl.roots() + array([], dtype=float64) + + Converting to a PPoly object does find the roots at `x=2`: + + >>> from scipy.interpolate import splrep, PPoly + >>> tck = splrep(x, y, s=0) + >>> ppoly = PPoly.from_spline(tck) + >>> ppoly.roots(extrapolate=False) + array([2.]) + + See Also + -------- + sproot + PPoly.roots + + """ + k = self._data[5] + if k == 3: + t = self._eval_args[0] + mest = 3 * (len(t) - 7) + return _fitpack_impl.sproot(self._eval_args, mest=mest) + raise NotImplementedError('finding roots unsupported for ' + 'non-cubic splines') + + def derivative(self, n=1): + """ + Construct a new spline representing the derivative of this spline. + + Parameters + ---------- + n : int, optional + Order of derivative to evaluate. Default: 1 + + Returns + ------- + spline : UnivariateSpline + Spline of order k2=k-n representing the derivative of this + spline. + + See Also + -------- + splder, antiderivative + + Notes + ----- + + .. versionadded:: 0.13.0 + + Examples + -------- + This can be used for finding maxima of a curve: + + >>> import numpy as np + >>> from scipy.interpolate import UnivariateSpline + >>> x = np.linspace(0, 10, 70) + >>> y = np.sin(x) + >>> spl = UnivariateSpline(x, y, k=4, s=0) + + Now, differentiate the spline and find the zeros of the + derivative. (NB: `sproot` only works for order 3 splines, so we + fit an order 4 spline): + + >>> spl.derivative().roots() / np.pi + array([ 0.50000001, 1.5 , 2.49999998]) + + This agrees well with roots :math:`\\pi/2 + n\\pi` of + :math:`\\cos(x) = \\sin'(x)`. + + """ + tck = _fitpack_impl.splder(self._eval_args, n) + # if self.ext is 'const', derivative.ext will be 'zeros' + ext = 1 if self.ext == 3 else self.ext + return UnivariateSpline._from_tck(tck, ext=ext) + + def antiderivative(self, n=1): + """ + Construct a new spline representing the antiderivative of this spline. + + Parameters + ---------- + n : int, optional + Order of antiderivative to evaluate. Default: 1 + + Returns + ------- + spline : UnivariateSpline + Spline of order k2=k+n representing the antiderivative of this + spline. + + Notes + ----- + + .. versionadded:: 0.13.0 + + See Also + -------- + splantider, derivative + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import UnivariateSpline + >>> x = np.linspace(0, np.pi/2, 70) + >>> y = 1 / np.sqrt(1 - 0.8*np.sin(x)**2) + >>> spl = UnivariateSpline(x, y, s=0) + + The derivative is the inverse operation of the antiderivative, + although some floating point error accumulates: + + >>> spl(1.7), spl.antiderivative().derivative()(1.7) + (array(2.1565429877197317), array(2.1565429877201865)) + + Antiderivative can be used to evaluate definite integrals: + + >>> ispl = spl.antiderivative() + >>> ispl(np.pi/2) - ispl(0) + 2.2572053588768486 + + This is indeed an approximation to the complete elliptic integral + :math:`K(m) = \\int_0^{\\pi/2} [1 - m\\sin^2 x]^{-1/2} dx`: + + >>> from scipy.special import ellipk + >>> ellipk(0.8) + 2.2572053268208538 + + """ + tck = _fitpack_impl.splantider(self._eval_args, n) + return UnivariateSpline._from_tck(tck, self.ext) + + +class InterpolatedUnivariateSpline(UnivariateSpline): + """ + 1-D interpolating spline for a given set of data points. + + Fits a spline y = spl(x) of degree `k` to the provided `x`, `y` data. + Spline function passes through all provided points. Equivalent to + `UnivariateSpline` with `s` = 0. + + Parameters + ---------- + x : (N,) array_like + Input dimension of data points -- must be strictly increasing + y : (N,) array_like + input dimension of data points + w : (N,) array_like, optional + Weights for spline fitting. Must be positive. If None (default), + weights are all 1. + bbox : (2,) array_like, optional + 2-sequence specifying the boundary of the approximation interval. If + None (default), ``bbox=[x[0], x[-1]]``. + k : int, optional + Degree of the smoothing spline. Must be ``1 <= k <= 5``. Default is + ``k = 3``, a cubic spline. + ext : int or str, optional + Controls the extrapolation mode for elements + not in the interval defined by the knot sequence. + + * if ext=0 or 'extrapolate', return the extrapolated value. + * if ext=1 or 'zeros', return 0 + * if ext=2 or 'raise', raise a ValueError + * if ext=3 of 'const', return the boundary value. + + The default value is 0. + + check_finite : bool, optional + Whether to check that the input arrays contain only finite numbers. + Disabling may give a performance gain, but may result in problems + (crashes, non-termination or non-sensical results) if the inputs + do contain infinities or NaNs. + Default is False. + + See Also + -------- + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + LSQUnivariateSpline : + a spline for which knots are user-selected + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + splrep : + a function to find the B-spline representation of a 1-D curve + splev : + a function to evaluate a B-spline or its derivatives + sproot : + a function to find the roots of a cubic B-spline + splint : + a function to evaluate the definite integral of a B-spline between two + given points + spalde : + a function to evaluate all derivatives of a B-spline + + Notes + ----- + The number of data points must be larger than the spline degree `k`. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import InterpolatedUnivariateSpline + >>> rng = np.random.default_rng() + >>> x = np.linspace(-3, 3, 50) + >>> y = np.exp(-x**2) + 0.1 * rng.standard_normal(50) + >>> spl = InterpolatedUnivariateSpline(x, y) + >>> plt.plot(x, y, 'ro', ms=5) + >>> xs = np.linspace(-3, 3, 1000) + >>> plt.plot(xs, spl(xs), 'g', lw=3, alpha=0.7) + >>> plt.show() + + Notice that the ``spl(x)`` interpolates `y`: + + >>> spl.get_residual() + 0.0 + + """ + + def __init__(self, x, y, w=None, bbox=[None]*2, k=3, + ext=0, check_finite=False): + + x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, None, + ext, check_finite) + if not np.all(diff(x) > 0.0): + raise ValueError('x must be strictly increasing') + + # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier + self._data = dfitpack.fpcurf0(x, y, k, w=w, xb=bbox[0], + xe=bbox[1], s=0) + self._reset_class() + + +_fpchec_error_string = """The input parameters have been rejected by fpchec. \ +This means that at least one of the following conditions is violated: + +1) k+1 <= n-k-1 <= m +2) t(1) <= t(2) <= ... <= t(k+1) + t(n-k) <= t(n-k+1) <= ... <= t(n) +3) t(k+1) < t(k+2) < ... < t(n-k) +4) t(k+1) <= x(i) <= t(n-k) +5) The conditions specified by Schoenberg and Whitney must hold + for at least one subset of data points, i.e., there must be a + subset of data points y(j) such that + t(j) < y(j) < t(j+k+1), j=1,2,...,n-k-1 +""" + + +class LSQUnivariateSpline(UnivariateSpline): + """ + 1-D spline with explicit internal knots. + + Fits a spline y = spl(x) of degree `k` to the provided `x`, `y` data. `t` + specifies the internal knots of the spline + + Parameters + ---------- + x : (N,) array_like + Input dimension of data points -- must be increasing + y : (N,) array_like + Input dimension of data points + t : (M,) array_like + interior knots of the spline. Must be in ascending order and:: + + bbox[0] < t[0] < ... < t[-1] < bbox[-1] + + w : (N,) array_like, optional + weights for spline fitting. Must be positive. If None (default), + weights are all 1. + bbox : (2,) array_like, optional + 2-sequence specifying the boundary of the approximation interval. If + None (default), ``bbox = [x[0], x[-1]]``. + k : int, optional + Degree of the smoothing spline. Must be 1 <= `k` <= 5. + Default is `k` = 3, a cubic spline. + ext : int or str, optional + Controls the extrapolation mode for elements + not in the interval defined by the knot sequence. + + * if ext=0 or 'extrapolate', return the extrapolated value. + * if ext=1 or 'zeros', return 0 + * if ext=2 or 'raise', raise a ValueError + * if ext=3 of 'const', return the boundary value. + + The default value is 0. + + check_finite : bool, optional + Whether to check that the input arrays contain only finite numbers. + Disabling may give a performance gain, but may result in problems + (crashes, non-termination or non-sensical results) if the inputs + do contain infinities or NaNs. + Default is False. + + Raises + ------ + ValueError + If the interior knots do not satisfy the Schoenberg-Whitney conditions + + See Also + -------- + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + InterpolatedUnivariateSpline : + a interpolating univariate spline for a given set of data points. + splrep : + a function to find the B-spline representation of a 1-D curve + splev : + a function to evaluate a B-spline or its derivatives + sproot : + a function to find the roots of a cubic B-spline + splint : + a function to evaluate the definite integral of a B-spline between two + given points + spalde : + a function to evaluate all derivatives of a B-spline + + Notes + ----- + The number of data points must be larger than the spline degree `k`. + + Knots `t` must satisfy the Schoenberg-Whitney conditions, + i.e., there must be a subset of data points ``x[j]`` such that + ``t[j] < x[j] < t[j+k+1]``, for ``j=0, 1,...,n-k-2``. + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import LSQUnivariateSpline, UnivariateSpline + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> x = np.linspace(-3, 3, 50) + >>> y = np.exp(-x**2) + 0.1 * rng.standard_normal(50) + + Fit a smoothing spline with a pre-defined internal knots: + + >>> t = [-1, 0, 1] + >>> spl = LSQUnivariateSpline(x, y, t) + + >>> xs = np.linspace(-3, 3, 1000) + >>> plt.plot(x, y, 'ro', ms=5) + >>> plt.plot(xs, spl(xs), 'g-', lw=3) + >>> plt.show() + + Check the knot vector: + + >>> spl.get_knots() + array([-3., -1., 0., 1., 3.]) + + Constructing lsq spline using the knots from another spline: + + >>> x = np.arange(10) + >>> s = UnivariateSpline(x, x, s=0) + >>> s.get_knots() + array([ 0., 2., 3., 4., 5., 6., 7., 9.]) + >>> knt = s.get_knots() + >>> s1 = LSQUnivariateSpline(x, x, knt[1:-1]) # Chop 1st and last knot + >>> s1.get_knots() + array([ 0., 2., 3., 4., 5., 6., 7., 9.]) + + """ + + def __init__(self, x, y, t, w=None, bbox=[None]*2, k=3, + ext=0, check_finite=False): + + x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, None, + ext, check_finite) + if not np.all(diff(x) >= 0.0): + raise ValueError('x must be increasing') + + # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier + xb = bbox[0] + xe = bbox[1] + if xb is None: + xb = x[0] + if xe is None: + xe = x[-1] + t = concatenate(([xb]*(k+1), t, [xe]*(k+1))) + n = len(t) + if not np.all(t[k+1:n-k]-t[k:n-k-1] > 0, axis=0): + raise ValueError('Interior knots t must satisfy ' + 'Schoenberg-Whitney conditions') + if not dfitpack.fpchec(x, t, k) == 0: + raise ValueError(_fpchec_error_string) + data = dfitpack.fpcurfm1(x, y, k, t, w=w, xb=xb, xe=xe) + self._data = data[:-3] + (None, None, data[-1]) + self._reset_class() + + +# ############### Bivariate spline #################### + +class _BivariateSplineBase: + """ Base class for Bivariate spline s(x,y) interpolation on the rectangle + [xb,xe] x [yb, ye] calculated from a given set of data points + (x,y,z). + + See Also + -------- + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + BivariateSpline : + a base class for bivariate splines. + SphereBivariateSpline : + a bivariate spline on a spherical grid + """ + + @classmethod + def _from_tck(cls, tck): + """Construct a spline object from given tck and degree""" + self = cls.__new__(cls) + if len(tck) != 5: + raise ValueError("tck should be a 5 element tuple of tx," + " ty, c, kx, ky") + self.tck = tck[:3] + self.degrees = tck[3:] + return self + + def get_residual(self): + """ Return weighted sum of squared residuals of the spline + approximation: sum ((w[i]*(z[i]-s(x[i],y[i])))**2,axis=0) + """ + return self.fp + + def get_knots(self): + """ Return a tuple (tx,ty) where tx,ty contain knots positions + of the spline with respect to x-, y-variable, respectively. + The position of interior and additional knots are given as + t[k+1:-k-1] and t[:k+1]=b, t[-k-1:]=e, respectively. + """ + return self.tck[:2] + + def get_coeffs(self): + """ Return spline coefficients.""" + return self.tck[2] + + def __call__(self, x, y, dx=0, dy=0, grid=True): + """ + Evaluate the spline or its derivatives at given positions. + + Parameters + ---------- + x, y : array_like + Input coordinates. + + If `grid` is False, evaluate the spline at points ``(x[i], + y[i]), i=0, ..., len(x)-1``. Standard Numpy broadcasting + is obeyed. + + If `grid` is True: evaluate spline at the grid points + defined by the coordinate arrays x, y. The arrays must be + sorted to increasing order. + + The ordering of axes is consistent with + ``np.meshgrid(..., indexing="ij")`` and inconsistent with the + default ordering ``np.meshgrid(..., indexing="xy")``. + dx : int + Order of x-derivative + + .. versionadded:: 0.14.0 + dy : int + Order of y-derivative + + .. versionadded:: 0.14.0 + grid : bool + Whether to evaluate the results on a grid spanned by the + input arrays, or at points specified by the input arrays. + + .. versionadded:: 0.14.0 + + Examples + -------- + Suppose that we want to bilinearly interpolate an exponentially decaying + function in 2 dimensions. + + >>> import numpy as np + >>> from scipy.interpolate import RectBivariateSpline + + We sample the function on a coarse grid. Note that the default indexing="xy" + of meshgrid would result in an unexpected (transposed) result after + interpolation. + + >>> xarr = np.linspace(-3, 3, 100) + >>> yarr = np.linspace(-3, 3, 100) + >>> xgrid, ygrid = np.meshgrid(xarr, yarr, indexing="ij") + + The function to interpolate decays faster along one axis than the other. + + >>> zdata = np.exp(-np.sqrt((xgrid / 2) ** 2 + ygrid**2)) + + Next we sample on a finer grid using interpolation (kx=ky=1 for bilinear). + + >>> rbs = RectBivariateSpline(xarr, yarr, zdata, kx=1, ky=1) + >>> xarr_fine = np.linspace(-3, 3, 200) + >>> yarr_fine = np.linspace(-3, 3, 200) + >>> xgrid_fine, ygrid_fine = np.meshgrid(xarr_fine, yarr_fine, indexing="ij") + >>> zdata_interp = rbs(xgrid_fine, ygrid_fine, grid=False) + + And check that the result agrees with the input by plotting both. + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(1, 2, 1, aspect="equal") + >>> ax2 = fig.add_subplot(1, 2, 2, aspect="equal") + >>> ax1.imshow(zdata) + >>> ax2.imshow(zdata_interp) + >>> plt.show() + """ + x = np.asarray(x) + y = np.asarray(y) + + tx, ty, c = self.tck[:3] + kx, ky = self.degrees + if grid: + if x.size == 0 or y.size == 0: + return np.zeros((x.size, y.size), dtype=self.tck[2].dtype) + + if (x.size >= 2) and (not np.all(np.diff(x) >= 0.0)): + raise ValueError("x must be strictly increasing when `grid` is True") + if (y.size >= 2) and (not np.all(np.diff(y) >= 0.0)): + raise ValueError("y must be strictly increasing when `grid` is True") + + if dx or dy: + z, ier = dfitpack.parder(tx, ty, c, kx, ky, dx, dy, x, y) + if not ier == 0: + raise ValueError("Error code returned by parder: %s" % ier) + else: + z, ier = dfitpack.bispev(tx, ty, c, kx, ky, x, y) + if not ier == 0: + raise ValueError("Error code returned by bispev: %s" % ier) + else: + # standard Numpy broadcasting + if x.shape != y.shape: + x, y = np.broadcast_arrays(x, y) + + shape = x.shape + x = x.ravel() + y = y.ravel() + + if x.size == 0 or y.size == 0: + return np.zeros(shape, dtype=self.tck[2].dtype) + + if dx or dy: + z, ier = dfitpack.pardeu(tx, ty, c, kx, ky, dx, dy, x, y) + if not ier == 0: + raise ValueError("Error code returned by pardeu: %s" % ier) + else: + z, ier = dfitpack.bispeu(tx, ty, c, kx, ky, x, y) + if not ier == 0: + raise ValueError("Error code returned by bispeu: %s" % ier) + + z = z.reshape(shape) + return z + + def partial_derivative(self, dx, dy): + """Construct a new spline representing a partial derivative of this + spline. + + Parameters + ---------- + dx, dy : int + Orders of the derivative in x and y respectively. They must be + non-negative integers and less than the respective degree of the + original spline (self) in that direction (``kx``, ``ky``). + + Returns + ------- + spline : + A new spline of degrees (``kx - dx``, ``ky - dy``) representing the + derivative of this spline. + + Notes + ----- + + .. versionadded:: 1.9.0 + + """ + if dx == 0 and dy == 0: + return self + else: + kx, ky = self.degrees + if not (dx >= 0 and dy >= 0): + raise ValueError("order of derivative must be positive or" + " zero") + if not (dx < kx and dy < ky): + raise ValueError("order of derivative must be less than" + " degree of spline") + tx, ty, c = self.tck[:3] + newc, ier = dfitpack.pardtc(tx, ty, c, kx, ky, dx, dy) + if ier != 0: + # This should not happen under normal conditions. + raise ValueError("Unexpected error code returned by" + " pardtc: %d" % ier) + nx = len(tx) + ny = len(ty) + newtx = tx[dx:nx - dx] + newty = ty[dy:ny - dy] + newkx, newky = kx - dx, ky - dy + newclen = (nx - dx - kx - 1) * (ny - dy - ky - 1) + return _DerivedBivariateSpline._from_tck((newtx, newty, + newc[:newclen], + newkx, newky)) + + +_surfit_messages = {1: """ +The required storage space exceeds the available storage space: nxest +or nyest too small, or s too small. +The weighted least-squares spline corresponds to the current set of +knots.""", + 2: """ +A theoretically impossible result was found during the iteration +process for finding a smoothing spline with fp = s: s too small or +badly chosen eps. +Weighted sum of squared residuals does not satisfy abs(fp-s)/s < tol.""", + 3: """ +the maximal number of iterations maxit (set to 20 by the program) +allowed for finding a smoothing spline with fp=s has been reached: +s too small. +Weighted sum of squared residuals does not satisfy abs(fp-s)/s < tol.""", + 4: """ +No more knots can be added because the number of b-spline coefficients +(nx-kx-1)*(ny-ky-1) already exceeds the number of data points m: +either s or m too small. +The weighted least-squares spline corresponds to the current set of +knots.""", + 5: """ +No more knots can be added because the additional knot would (quasi) +coincide with an old one: s too small or too large a weight to an +inaccurate data point. +The weighted least-squares spline corresponds to the current set of +knots.""", + 10: """ +Error on entry, no approximation returned. The following conditions +must hold: +xb<=x[i]<=xe, yb<=y[i]<=ye, w[i]>0, i=0..m-1 +If iopt==-1, then + xb>> import numpy as np + >>> from scipy.interpolate import RectBivariateSpline + >>> def f(x, y): + ... return np.exp(-np.sqrt((x / 2) ** 2 + y**2)) + + We sample the function on a coarse grid and set up the interpolator. Note that + the default ``indexing="xy"`` of meshgrid would result in an unexpected + (transposed) result after interpolation. + + >>> xarr = np.linspace(-3, 3, 21) + >>> yarr = np.linspace(-3, 3, 21) + >>> xgrid, ygrid = np.meshgrid(xarr, yarr, indexing="ij") + >>> zdata = f(xgrid, ygrid) + >>> rbs = RectBivariateSpline(xarr, yarr, zdata, kx=1, ky=1) + + Next we sample the function along a diagonal slice through the coordinate space + on a finer grid using interpolation. + + >>> xinterp = np.linspace(-3, 3, 201) + >>> yinterp = np.linspace(3, -3, 201) + >>> zinterp = rbs.ev(xinterp, yinterp) + + And check that the interpolation passes through the function evaluations as a + function of the distance from the origin along the slice. + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(1, 1, 1) + >>> ax1.plot(np.sqrt(xarr**2 + yarr**2), np.diag(zdata), "or") + >>> ax1.plot(np.sqrt(xinterp**2 + yinterp**2), zinterp, "-b") + >>> plt.show() + """ + return self.__call__(xi, yi, dx=dx, dy=dy, grid=False) + + def integral(self, xa, xb, ya, yb): + """ + Evaluate the integral of the spline over area [xa,xb] x [ya,yb]. + + Parameters + ---------- + xa, xb : float + The end-points of the x integration interval. + ya, yb : float + The end-points of the y integration interval. + + Returns + ------- + integ : float + The value of the resulting integral. + + """ + tx, ty, c = self.tck[:3] + kx, ky = self.degrees + return dfitpack.dblint(tx, ty, c, kx, ky, xa, xb, ya, yb) + + @staticmethod + def _validate_input(x, y, z, w, kx, ky, eps): + x, y, z = np.asarray(x), np.asarray(y), np.asarray(z) + if not x.size == y.size == z.size: + raise ValueError('x, y, and z should have a same length') + + if w is not None: + w = np.asarray(w) + if x.size != w.size: + raise ValueError('x, y, z, and w should have a same length') + elif not np.all(w >= 0.0): + raise ValueError('w should be positive') + if (eps is not None) and (not 0.0 < eps < 1.0): + raise ValueError('eps should be between (0, 1)') + if not x.size >= (kx + 1) * (ky + 1): + raise ValueError('The length of x, y and z should be at least' + ' (kx+1) * (ky+1)') + return x, y, z, w + + +class _DerivedBivariateSpline(_BivariateSplineBase): + """Bivariate spline constructed from the coefficients and knots of another + spline. + + Notes + ----- + The class is not meant to be instantiated directly from the data to be + interpolated or smoothed. As a result, its ``fp`` attribute and + ``get_residual`` method are inherited but overridden; ``AttributeError`` is + raised when they are accessed. + + The other inherited attributes can be used as usual. + """ + _invalid_why = ("is unavailable, because _DerivedBivariateSpline" + " instance is not constructed from data that are to be" + " interpolated or smoothed, but derived from the" + " underlying knots and coefficients of another spline" + " object") + + @property + def fp(self): + raise AttributeError("attribute \"fp\" %s" % self._invalid_why) + + def get_residual(self): + raise AttributeError("method \"get_residual\" %s" % self._invalid_why) + + +class SmoothBivariateSpline(BivariateSpline): + """ + Smooth bivariate spline approximation. + + Parameters + ---------- + x, y, z : array_like + 1-D sequences of data points (order is not important). + w : array_like, optional + Positive 1-D sequence of weights, of same length as `x`, `y` and `z`. + bbox : array_like, optional + Sequence of length 4 specifying the boundary of the rectangular + approximation domain. By default, + ``bbox=[min(x), max(x), min(y), max(y)]``. + kx, ky : ints, optional + Degrees of the bivariate spline. Default is 3. + s : float, optional + Positive smoothing factor defined for estimation condition: + ``sum((w[i]*(z[i]-s(x[i], y[i])))**2, axis=0) <= s`` + Default ``s=len(w)`` which should be a good value if ``1/w[i]`` is an + estimate of the standard deviation of ``z[i]``. + eps : float, optional + A threshold for determining the effective rank of an over-determined + linear system of equations. `eps` should have a value within the open + interval ``(0, 1)``, the default is 1e-16. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + RectSphereBivariateSpline : + a bivariate spline over a rectangular mesh on a sphere + SmoothSphereBivariateSpline : + a smoothing bivariate spline in spherical coordinates + LSQSphereBivariateSpline : + a bivariate spline in spherical coordinates using weighted + least-squares fitting + RectBivariateSpline : + a bivariate spline over a rectangular mesh + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + The length of `x`, `y` and `z` should be at least ``(kx+1) * (ky+1)``. + + If the input data is such that input dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolating. + + This routine constructs spline knot vectors automatically via the FITPACK + algorithm. The spline knots may be placed away from the data points. For + some data sets, this routine may fail to construct an interpolating spline, + even if one is requested via ``s=0`` parameter. In such situations, it is + recommended to use `bisplrep` / `bisplev` directly instead of this routine + and, if needed, increase the values of ``nxest`` and ``nyest`` parameters + of `bisplrep`. + + For linear interpolation, prefer `LinearNDInterpolator`. + See ``https://gist.github.com/ev-br/8544371b40f414b7eaf3fe6217209bff`` + for discussion. + + """ + + def __init__(self, x, y, z, w=None, bbox=[None] * 4, kx=3, ky=3, s=None, + eps=1e-16): + + x, y, z, w = self._validate_input(x, y, z, w, kx, ky, eps) + bbox = ravel(bbox) + if not bbox.shape == (4,): + raise ValueError('bbox shape should be (4,)') + if s is not None and not s >= 0.0: + raise ValueError("s should be s >= 0.0") + + xb, xe, yb, ye = bbox + nx, tx, ny, ty, c, fp, wrk1, ier = dfitpack.surfit_smth(x, y, z, w, + xb, xe, yb, + ye, kx, ky, + s=s, eps=eps, + lwrk2=1) + if ier > 10: # lwrk2 was to small, re-run + nx, tx, ny, ty, c, fp, wrk1, ier = dfitpack.surfit_smth(x, y, z, w, + xb, xe, yb, + ye, kx, ky, + s=s, + eps=eps, + lwrk2=ier) + if ier in [0, -1, -2]: # normal return + pass + else: + message = _surfit_messages.get(ier, 'ier=%s' % (ier)) + warnings.warn(message, stacklevel=2) + + self.fp = fp + self.tck = tx[:nx], ty[:ny], c[:(nx-kx-1)*(ny-ky-1)] + self.degrees = kx, ky + + +class LSQBivariateSpline(BivariateSpline): + """ + Weighted least-squares bivariate spline approximation. + + Parameters + ---------- + x, y, z : array_like + 1-D sequences of data points (order is not important). + tx, ty : array_like + Strictly ordered 1-D sequences of knots coordinates. + w : array_like, optional + Positive 1-D array of weights, of the same length as `x`, `y` and `z`. + bbox : (4,) array_like, optional + Sequence of length 4 specifying the boundary of the rectangular + approximation domain. By default, + ``bbox=[min(x,tx),max(x,tx), min(y,ty),max(y,ty)]``. + kx, ky : ints, optional + Degrees of the bivariate spline. Default is 3. + eps : float, optional + A threshold for determining the effective rank of an over-determined + linear system of equations. `eps` should have a value within the open + interval ``(0, 1)``, the default is 1e-16. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + RectSphereBivariateSpline : + a bivariate spline over a rectangular mesh on a sphere + SmoothSphereBivariateSpline : + a smoothing bivariate spline in spherical coordinates + LSQSphereBivariateSpline : + a bivariate spline in spherical coordinates using weighted + least-squares fitting + RectBivariateSpline : + a bivariate spline over a rectangular mesh. + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + The length of `x`, `y` and `z` should be at least ``(kx+1) * (ky+1)``. + + If the input data is such that input dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolating. + + """ + + def __init__(self, x, y, z, tx, ty, w=None, bbox=[None]*4, kx=3, ky=3, + eps=None): + + x, y, z, w = self._validate_input(x, y, z, w, kx, ky, eps) + bbox = ravel(bbox) + if not bbox.shape == (4,): + raise ValueError('bbox shape should be (4,)') + + nx = 2*kx+2+len(tx) + ny = 2*ky+2+len(ty) + # The Fortran subroutine "surfit" (called as dfitpack.surfit_lsq) + # requires that the knot arrays passed as input should be "real + # array(s) of dimension nmax" where "nmax" refers to the greater of nx + # and ny. We pad the tx1/ty1 arrays here so that this is satisfied, and + # slice them to the desired sizes upon return. + nmax = max(nx, ny) + tx1 = zeros((nmax,), float) + ty1 = zeros((nmax,), float) + tx1[kx+1:nx-kx-1] = tx + ty1[ky+1:ny-ky-1] = ty + + xb, xe, yb, ye = bbox + tx1, ty1, c, fp, ier = dfitpack.surfit_lsq(x, y, z, nx, tx1, ny, ty1, + w, xb, xe, yb, ye, + kx, ky, eps, lwrk2=1) + if ier > 10: + tx1, ty1, c, fp, ier = dfitpack.surfit_lsq(x, y, z, + nx, tx1, ny, ty1, w, + xb, xe, yb, ye, + kx, ky, eps, lwrk2=ier) + if ier in [0, -1, -2]: # normal return + pass + else: + if ier < -2: + deficiency = (nx-kx-1)*(ny-ky-1)+ier + message = _surfit_messages.get(-3) % (deficiency) + else: + message = _surfit_messages.get(ier, 'ier=%s' % (ier)) + warnings.warn(message, stacklevel=2) + self.fp = fp + self.tck = tx1[:nx], ty1[:ny], c + self.degrees = kx, ky + + +class RectBivariateSpline(BivariateSpline): + """ + Bivariate spline approximation over a rectangular mesh. + + Can be used for both smoothing and interpolating data. + + Parameters + ---------- + x,y : array_like + 1-D arrays of coordinates in strictly ascending order. + Evaluated points outside the data range will be extrapolated. + z : array_like + 2-D array of data with shape (x.size,y.size). + bbox : array_like, optional + Sequence of length 4 specifying the boundary of the rectangular + approximation domain, which means the start and end spline knots of + each dimension are set by these values. By default, + ``bbox=[min(x), max(x), min(y), max(y)]``. + kx, ky : ints, optional + Degrees of the bivariate spline. Default is 3. + s : float, optional + Positive smoothing factor defined for estimation condition: + ``sum((z[i]-f(x[i], y[i]))**2, axis=0) <= s`` where f is a spline + function. Default is ``s=0``, which is for interpolation. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + RectSphereBivariateSpline : + a bivariate spline over a rectangular mesh on a sphere + SmoothSphereBivariateSpline : + a smoothing bivariate spline in spherical coordinates + LSQSphereBivariateSpline : + a bivariate spline in spherical coordinates using weighted + least-squares fitting + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + + If the input data is such that input dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolating. + + """ + + def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0): + x, y, bbox = ravel(x), ravel(y), ravel(bbox) + z = np.asarray(z) + if not np.all(diff(x) > 0.0): + raise ValueError('x must be strictly increasing') + if not np.all(diff(y) > 0.0): + raise ValueError('y must be strictly increasing') + if not x.size == z.shape[0]: + raise ValueError('x dimension of z must have same number of ' + 'elements as x') + if not y.size == z.shape[1]: + raise ValueError('y dimension of z must have same number of ' + 'elements as y') + if not bbox.shape == (4,): + raise ValueError('bbox shape should be (4,)') + if s is not None and not s >= 0.0: + raise ValueError("s should be s >= 0.0") + + z = ravel(z) + xb, xe, yb, ye = bbox + nx, tx, ny, ty, c, fp, ier = dfitpack.regrid_smth(x, y, z, xb, xe, yb, + ye, kx, ky, s) + + if ier not in [0, -1, -2]: + msg = _surfit_messages.get(ier, 'ier=%s' % (ier)) + raise ValueError(msg) + + self.fp = fp + self.tck = tx[:nx], ty[:ny], c[:(nx - kx - 1) * (ny - ky - 1)] + self.degrees = kx, ky + + +_spherefit_messages = _surfit_messages.copy() +_spherefit_messages[10] = """ +ERROR. On entry, the input data are controlled on validity. The following + restrictions must be satisfied: + -1<=iopt<=1, m>=2, ntest>=8 ,npest >=8, 00, i=1,...,m + lwrk1 >= 185+52*v+10*u+14*u*v+8*(u-1)*v**2+8*m + kwrk >= m+(ntest-7)*(npest-7) + if iopt=-1: 8<=nt<=ntest , 9<=np<=npest + 0=0: s>=0 + if one of these conditions is found to be violated,control + is immediately repassed to the calling program. in that + case there is no approximation returned.""" +_spherefit_messages[-3] = """ +WARNING. The coefficients of the spline returned have been computed as the + minimal norm least-squares solution of a (numerically) rank + deficient system (deficiency=%i, rank=%i). Especially if the rank + deficiency, which is computed by 6+(nt-8)*(np-7)+ier, is large, + the results may be inaccurate. They could also seriously depend on + the value of eps.""" + + +class SphereBivariateSpline(_BivariateSplineBase): + """ + Bivariate spline s(x,y) of degrees 3 on a sphere, calculated from a + given set of data points (theta,phi,r). + + .. versionadded:: 0.11.0 + + See Also + -------- + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQUnivariateSpline : + a univariate spline using weighted least-squares fitting + """ + + def __call__(self, theta, phi, dtheta=0, dphi=0, grid=True): + """ + Evaluate the spline or its derivatives at given positions. + + Parameters + ---------- + theta, phi : array_like + Input coordinates. + + If `grid` is False, evaluate the spline at points + ``(theta[i], phi[i]), i=0, ..., len(x)-1``. Standard + Numpy broadcasting is obeyed. + + If `grid` is True: evaluate spline at the grid points + defined by the coordinate arrays theta, phi. The arrays + must be sorted to increasing order. + The ordering of axes is consistent with + ``np.meshgrid(..., indexing="ij")`` and inconsistent with the + default ordering ``np.meshgrid(..., indexing="xy")``. + dtheta : int, optional + Order of theta-derivative + + .. versionadded:: 0.14.0 + dphi : int + Order of phi-derivative + + .. versionadded:: 0.14.0 + grid : bool + Whether to evaluate the results on a grid spanned by the + input arrays, or at points specified by the input arrays. + + .. versionadded:: 0.14.0 + + Examples + -------- + + Suppose that we want to use splines to interpolate a bivariate function on a + sphere. The value of the function is known on a grid of longitudes and + colatitudes. + + >>> import numpy as np + >>> from scipy.interpolate import RectSphereBivariateSpline + >>> def f(theta, phi): + ... return np.sin(theta) * np.cos(phi) + + We evaluate the function on the grid. Note that the default indexing="xy" + of meshgrid would result in an unexpected (transposed) result after + interpolation. + + >>> thetaarr = np.linspace(0, np.pi, 22)[1:-1] + >>> phiarr = np.linspace(0, 2 * np.pi, 21)[:-1] + >>> thetagrid, phigrid = np.meshgrid(thetaarr, phiarr, indexing="ij") + >>> zdata = f(thetagrid, phigrid) + + We next set up the interpolator and use it to evaluate the function + on a finer grid. + + >>> rsbs = RectSphereBivariateSpline(thetaarr, phiarr, zdata) + >>> thetaarr_fine = np.linspace(0, np.pi, 200) + >>> phiarr_fine = np.linspace(0, 2 * np.pi, 200) + >>> zdata_fine = rsbs(thetaarr_fine, phiarr_fine) + + Finally we plot the coarsly-sampled input data alongside the + finely-sampled interpolated data to check that they agree. + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(1, 2, 1) + >>> ax2 = fig.add_subplot(1, 2, 2) + >>> ax1.imshow(zdata) + >>> ax2.imshow(zdata_fine) + >>> plt.show() + """ + theta = np.asarray(theta) + phi = np.asarray(phi) + + if theta.size > 0 and (theta.min() < 0. or theta.max() > np.pi): + raise ValueError("requested theta out of bounds.") + + return _BivariateSplineBase.__call__(self, theta, phi, + dx=dtheta, dy=dphi, grid=grid) + + def ev(self, theta, phi, dtheta=0, dphi=0): + """ + Evaluate the spline at points + + Returns the interpolated value at ``(theta[i], phi[i]), + i=0,...,len(theta)-1``. + + Parameters + ---------- + theta, phi : array_like + Input coordinates. Standard Numpy broadcasting is obeyed. + The ordering of axes is consistent with + np.meshgrid(..., indexing="ij") and inconsistent with the + default ordering np.meshgrid(..., indexing="xy"). + dtheta : int, optional + Order of theta-derivative + + .. versionadded:: 0.14.0 + dphi : int, optional + Order of phi-derivative + + .. versionadded:: 0.14.0 + + Examples + -------- + Suppose that we want to use splines to interpolate a bivariate function on a + sphere. The value of the function is known on a grid of longitudes and + colatitudes. + + >>> import numpy as np + >>> from scipy.interpolate import RectSphereBivariateSpline + >>> def f(theta, phi): + ... return np.sin(theta) * np.cos(phi) + + We evaluate the function on the grid. Note that the default indexing="xy" + of meshgrid would result in an unexpected (transposed) result after + interpolation. + + >>> thetaarr = np.linspace(0, np.pi, 22)[1:-1] + >>> phiarr = np.linspace(0, 2 * np.pi, 21)[:-1] + >>> thetagrid, phigrid = np.meshgrid(thetaarr, phiarr, indexing="ij") + >>> zdata = f(thetagrid, phigrid) + + We next set up the interpolator and use it to evaluate the function + at points not on the original grid. + + >>> rsbs = RectSphereBivariateSpline(thetaarr, phiarr, zdata) + >>> thetainterp = np.linspace(thetaarr[0], thetaarr[-1], 200) + >>> phiinterp = np.linspace(phiarr[0], phiarr[-1], 200) + >>> zinterp = rsbs.ev(thetainterp, phiinterp) + + Finally we plot the original data for a diagonal slice through the + initial grid, and the spline approximation along the same slice. + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(1, 1, 1) + >>> ax1.plot(np.sin(thetaarr) * np.sin(phiarr), np.diag(zdata), "or") + >>> ax1.plot(np.sin(thetainterp) * np.sin(phiinterp), zinterp, "-b") + >>> plt.show() + """ + return self.__call__(theta, phi, dtheta=dtheta, dphi=dphi, grid=False) + + +class SmoothSphereBivariateSpline(SphereBivariateSpline): + """ + Smooth bivariate spline approximation in spherical coordinates. + + .. versionadded:: 0.11.0 + + Parameters + ---------- + theta, phi, r : array_like + 1-D sequences of data points (order is not important). Coordinates + must be given in radians. Theta must lie within the interval + ``[0, pi]``, and phi must lie within the interval ``[0, 2pi]``. + w : array_like, optional + Positive 1-D sequence of weights. + s : float, optional + Positive smoothing factor defined for estimation condition: + ``sum((w(i)*(r(i) - s(theta(i), phi(i))))**2, axis=0) <= s`` + Default ``s=len(w)`` which should be a good value if ``1/w[i]`` is an + estimate of the standard deviation of ``r[i]``. + eps : float, optional + A threshold for determining the effective rank of an over-determined + linear system of equations. `eps` should have a value within the open + interval ``(0, 1)``, the default is 1e-16. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + RectSphereBivariateSpline : + a bivariate spline over a rectangular mesh on a sphere + LSQSphereBivariateSpline : + a bivariate spline in spherical coordinates using weighted + least-squares fitting + RectBivariateSpline : + a bivariate spline over a rectangular mesh. + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + For more information, see the FITPACK_ site about this function. + + .. _FITPACK: http://www.netlib.org/dierckx/sphere.f + + Examples + -------- + Suppose we have global data on a coarse grid (the input data does not + have to be on a grid): + + >>> import numpy as np + >>> theta = np.linspace(0., np.pi, 7) + >>> phi = np.linspace(0., 2*np.pi, 9) + >>> data = np.empty((theta.shape[0], phi.shape[0])) + >>> data[:,0], data[0,:], data[-1,:] = 0., 0., 0. + >>> data[1:-1,1], data[1:-1,-1] = 1., 1. + >>> data[1,1:-1], data[-2,1:-1] = 1., 1. + >>> data[2:-2,2], data[2:-2,-2] = 2., 2. + >>> data[2,2:-2], data[-3,2:-2] = 2., 2. + >>> data[3,3:-2] = 3. + >>> data = np.roll(data, 4, 1) + + We need to set up the interpolator object + + >>> lats, lons = np.meshgrid(theta, phi) + >>> from scipy.interpolate import SmoothSphereBivariateSpline + >>> lut = SmoothSphereBivariateSpline(lats.ravel(), lons.ravel(), + ... data.T.ravel(), s=3.5) + + As a first test, we'll see what the algorithm returns when run on the + input coordinates + + >>> data_orig = lut(theta, phi) + + Finally we interpolate the data to a finer grid + + >>> fine_lats = np.linspace(0., np.pi, 70) + >>> fine_lons = np.linspace(0., 2 * np.pi, 90) + + >>> data_smth = lut(fine_lats, fine_lons) + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(131) + >>> ax1.imshow(data, interpolation='nearest') + >>> ax2 = fig.add_subplot(132) + >>> ax2.imshow(data_orig, interpolation='nearest') + >>> ax3 = fig.add_subplot(133) + >>> ax3.imshow(data_smth, interpolation='nearest') + >>> plt.show() + + """ + + def __init__(self, theta, phi, r, w=None, s=0., eps=1E-16): + + theta, phi, r = np.asarray(theta), np.asarray(phi), np.asarray(r) + + # input validation + if not ((0.0 <= theta).all() and (theta <= np.pi).all()): + raise ValueError('theta should be between [0, pi]') + if not ((0.0 <= phi).all() and (phi <= 2.0 * np.pi).all()): + raise ValueError('phi should be between [0, 2pi]') + if w is not None: + w = np.asarray(w) + if not (w >= 0.0).all(): + raise ValueError('w should be positive') + if not s >= 0.0: + raise ValueError('s should be positive') + if not 0.0 < eps < 1.0: + raise ValueError('eps should be between (0, 1)') + + nt_, tt_, np_, tp_, c, fp, ier = dfitpack.spherfit_smth(theta, phi, + r, w=w, s=s, + eps=eps) + if ier not in [0, -1, -2]: + message = _spherefit_messages.get(ier, 'ier=%s' % (ier)) + raise ValueError(message) + + self.fp = fp + self.tck = tt_[:nt_], tp_[:np_], c[:(nt_ - 4) * (np_ - 4)] + self.degrees = (3, 3) + + def __call__(self, theta, phi, dtheta=0, dphi=0, grid=True): + + theta = np.asarray(theta) + phi = np.asarray(phi) + + if phi.size > 0 and (phi.min() < 0. or phi.max() > 2. * np.pi): + raise ValueError("requested phi out of bounds.") + + return SphereBivariateSpline.__call__(self, theta, phi, dtheta=dtheta, + dphi=dphi, grid=grid) + + +class LSQSphereBivariateSpline(SphereBivariateSpline): + """ + Weighted least-squares bivariate spline approximation in spherical + coordinates. + + Determines a smoothing bicubic spline according to a given + set of knots in the `theta` and `phi` directions. + + .. versionadded:: 0.11.0 + + Parameters + ---------- + theta, phi, r : array_like + 1-D sequences of data points (order is not important). Coordinates + must be given in radians. Theta must lie within the interval + ``[0, pi]``, and phi must lie within the interval ``[0, 2pi]``. + tt, tp : array_like + Strictly ordered 1-D sequences of knots coordinates. + Coordinates must satisfy ``0 < tt[i] < pi``, ``0 < tp[i] < 2*pi``. + w : array_like, optional + Positive 1-D sequence of weights, of the same length as `theta`, `phi` + and `r`. + eps : float, optional + A threshold for determining the effective rank of an over-determined + linear system of equations. `eps` should have a value within the + open interval ``(0, 1)``, the default is 1e-16. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + RectSphereBivariateSpline : + a bivariate spline over a rectangular mesh on a sphere + SmoothSphereBivariateSpline : + a smoothing bivariate spline in spherical coordinates + RectBivariateSpline : + a bivariate spline over a rectangular mesh. + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + For more information, see the FITPACK_ site about this function. + + .. _FITPACK: http://www.netlib.org/dierckx/sphere.f + + Examples + -------- + Suppose we have global data on a coarse grid (the input data does not + have to be on a grid): + + >>> from scipy.interpolate import LSQSphereBivariateSpline + >>> import numpy as np + >>> import matplotlib.pyplot as plt + + >>> theta = np.linspace(0, np.pi, num=7) + >>> phi = np.linspace(0, 2*np.pi, num=9) + >>> data = np.empty((theta.shape[0], phi.shape[0])) + >>> data[:,0], data[0,:], data[-1,:] = 0., 0., 0. + >>> data[1:-1,1], data[1:-1,-1] = 1., 1. + >>> data[1,1:-1], data[-2,1:-1] = 1., 1. + >>> data[2:-2,2], data[2:-2,-2] = 2., 2. + >>> data[2,2:-2], data[-3,2:-2] = 2., 2. + >>> data[3,3:-2] = 3. + >>> data = np.roll(data, 4, 1) + + We need to set up the interpolator object. Here, we must also specify the + coordinates of the knots to use. + + >>> lats, lons = np.meshgrid(theta, phi) + >>> knotst, knotsp = theta.copy(), phi.copy() + >>> knotst[0] += .0001 + >>> knotst[-1] -= .0001 + >>> knotsp[0] += .0001 + >>> knotsp[-1] -= .0001 + >>> lut = LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + ... data.T.ravel(), knotst, knotsp) + + As a first test, we'll see what the algorithm returns when run on the + input coordinates + + >>> data_orig = lut(theta, phi) + + Finally we interpolate the data to a finer grid + + >>> fine_lats = np.linspace(0., np.pi, 70) + >>> fine_lons = np.linspace(0., 2*np.pi, 90) + >>> data_lsq = lut(fine_lats, fine_lons) + + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(131) + >>> ax1.imshow(data, interpolation='nearest') + >>> ax2 = fig.add_subplot(132) + >>> ax2.imshow(data_orig, interpolation='nearest') + >>> ax3 = fig.add_subplot(133) + >>> ax3.imshow(data_lsq, interpolation='nearest') + >>> plt.show() + + """ + + def __init__(self, theta, phi, r, tt, tp, w=None, eps=1E-16): + + theta, phi, r = np.asarray(theta), np.asarray(phi), np.asarray(r) + tt, tp = np.asarray(tt), np.asarray(tp) + + if not ((0.0 <= theta).all() and (theta <= np.pi).all()): + raise ValueError('theta should be between [0, pi]') + if not ((0.0 <= phi).all() and (phi <= 2*np.pi).all()): + raise ValueError('phi should be between [0, 2pi]') + if not ((0.0 < tt).all() and (tt < np.pi).all()): + raise ValueError('tt should be between (0, pi)') + if not ((0.0 < tp).all() and (tp < 2*np.pi).all()): + raise ValueError('tp should be between (0, 2pi)') + if w is not None: + w = np.asarray(w) + if not (w >= 0.0).all(): + raise ValueError('w should be positive') + if not 0.0 < eps < 1.0: + raise ValueError('eps should be between (0, 1)') + + nt_, np_ = 8 + len(tt), 8 + len(tp) + tt_, tp_ = zeros((nt_,), float), zeros((np_,), float) + tt_[4:-4], tp_[4:-4] = tt, tp + tt_[-4:], tp_[-4:] = np.pi, 2. * np.pi + tt_, tp_, c, fp, ier = dfitpack.spherfit_lsq(theta, phi, r, tt_, tp_, + w=w, eps=eps) + if ier > 0: + message = _spherefit_messages.get(ier, 'ier=%s' % (ier)) + raise ValueError(message) + + self.fp = fp + self.tck = tt_, tp_, c + self.degrees = (3, 3) + + def __call__(self, theta, phi, dtheta=0, dphi=0, grid=True): + + theta = np.asarray(theta) + phi = np.asarray(phi) + + if phi.size > 0 and (phi.min() < 0. or phi.max() > 2. * np.pi): + raise ValueError("requested phi out of bounds.") + + return SphereBivariateSpline.__call__(self, theta, phi, dtheta=dtheta, + dphi=dphi, grid=grid) + + +_spfit_messages = _surfit_messages.copy() +_spfit_messages[10] = """ +ERROR: on entry, the input data are controlled on validity + the following restrictions must be satisfied. + -1<=iopt(1)<=1, 0<=iopt(2)<=1, 0<=iopt(3)<=1, + -1<=ider(1)<=1, 0<=ider(2)<=1, ider(2)=0 if iopt(2)=0. + -1<=ider(3)<=1, 0<=ider(4)<=1, ider(4)=0 if iopt(3)=0. + mu >= mumin (see above), mv >= 4, nuest >=8, nvest >= 8, + kwrk>=5+mu+mv+nuest+nvest, + lwrk >= 12+nuest*(mv+nvest+3)+nvest*24+4*mu+8*mv+max(nuest,mv+nvest) + 0< u(i-1)=0: s>=0 + if s=0: nuest>=mu+6+iopt(2)+iopt(3), nvest>=mv+7 + if one of these conditions is found to be violated,control is + immediately repassed to the calling program. in that case there is no + approximation returned.""" + + +class RectSphereBivariateSpline(SphereBivariateSpline): + """ + Bivariate spline approximation over a rectangular mesh on a sphere. + + Can be used for smoothing data. + + .. versionadded:: 0.11.0 + + Parameters + ---------- + u : array_like + 1-D array of colatitude coordinates in strictly ascending order. + Coordinates must be given in radians and lie within the open interval + ``(0, pi)``. + v : array_like + 1-D array of longitude coordinates in strictly ascending order. + Coordinates must be given in radians. First element (``v[0]``) must lie + within the interval ``[-pi, pi)``. Last element (``v[-1]``) must satisfy + ``v[-1] <= v[0] + 2*pi``. + r : array_like + 2-D array of data with shape ``(u.size, v.size)``. + s : float, optional + Positive smoothing factor defined for estimation condition + (``s=0`` is for interpolation). + pole_continuity : bool or (bool, bool), optional + Order of continuity at the poles ``u=0`` (``pole_continuity[0]``) and + ``u=pi`` (``pole_continuity[1]``). The order of continuity at the pole + will be 1 or 0 when this is True or False, respectively. + Defaults to False. + pole_values : float or (float, float), optional + Data values at the poles ``u=0`` and ``u=pi``. Either the whole + parameter or each individual element can be None. Defaults to None. + pole_exact : bool or (bool, bool), optional + Data value exactness at the poles ``u=0`` and ``u=pi``. If True, the + value is considered to be the right function value, and it will be + fitted exactly. If False, the value will be considered to be a data + value just like the other data values. Defaults to False. + pole_flat : bool or (bool, bool), optional + For the poles at ``u=0`` and ``u=pi``, specify whether or not the + approximation has vanishing derivatives. Defaults to False. + + See Also + -------- + BivariateSpline : + a base class for bivariate splines. + UnivariateSpline : + a smooth univariate spline to fit a given set of data points. + SmoothBivariateSpline : + a smoothing bivariate spline through the given points + LSQBivariateSpline : + a bivariate spline using weighted least-squares fitting + SmoothSphereBivariateSpline : + a smoothing bivariate spline in spherical coordinates + LSQSphereBivariateSpline : + a bivariate spline in spherical coordinates using weighted + least-squares fitting + RectBivariateSpline : + a bivariate spline over a rectangular mesh. + bisplrep : + a function to find a bivariate B-spline representation of a surface + bisplev : + a function to evaluate a bivariate B-spline and its derivatives + + Notes + ----- + Currently, only the smoothing spline approximation (``iopt[0] = 0`` and + ``iopt[0] = 1`` in the FITPACK routine) is supported. The exact + least-squares spline approximation is not implemented yet. + + When actually performing the interpolation, the requested `v` values must + lie within the same length 2pi interval that the original `v` values were + chosen from. + + For more information, see the FITPACK_ site about this function. + + .. _FITPACK: http://www.netlib.org/dierckx/spgrid.f + + Examples + -------- + Suppose we have global data on a coarse grid + + >>> import numpy as np + >>> lats = np.linspace(10, 170, 9) * np.pi / 180. + >>> lons = np.linspace(0, 350, 18) * np.pi / 180. + >>> data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, + ... np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T + + We want to interpolate it to a global one-degree grid + + >>> new_lats = np.linspace(1, 180, 180) * np.pi / 180 + >>> new_lons = np.linspace(1, 360, 360) * np.pi / 180 + >>> new_lats, new_lons = np.meshgrid(new_lats, new_lons) + + We need to set up the interpolator object + + >>> from scipy.interpolate import RectSphereBivariateSpline + >>> lut = RectSphereBivariateSpline(lats, lons, data) + + Finally we interpolate the data. The `RectSphereBivariateSpline` object + only takes 1-D arrays as input, therefore we need to do some reshaping. + + >>> data_interp = lut.ev(new_lats.ravel(), + ... new_lons.ravel()).reshape((360, 180)).T + + Looking at the original and the interpolated data, one can see that the + interpolant reproduces the original data very well: + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(211) + >>> ax1.imshow(data, interpolation='nearest') + >>> ax2 = fig.add_subplot(212) + >>> ax2.imshow(data_interp, interpolation='nearest') + >>> plt.show() + + Choosing the optimal value of ``s`` can be a delicate task. Recommended + values for ``s`` depend on the accuracy of the data values. If the user + has an idea of the statistical errors on the data, she can also find a + proper estimate for ``s``. By assuming that, if she specifies the + right ``s``, the interpolator will use a spline ``f(u,v)`` which exactly + reproduces the function underlying the data, she can evaluate + ``sum((r(i,j)-s(u(i),v(j)))**2)`` to find a good estimate for this ``s``. + For example, if she knows that the statistical errors on her + ``r(i,j)``-values are not greater than 0.1, she may expect that a good + ``s`` should have a value not larger than ``u.size * v.size * (0.1)**2``. + + If nothing is known about the statistical error in ``r(i,j)``, ``s`` must + be determined by trial and error. The best is then to start with a very + large value of ``s`` (to determine the least-squares polynomial and the + corresponding upper bound ``fp0`` for ``s``) and then to progressively + decrease the value of ``s`` (say by a factor 10 in the beginning, i.e. + ``s = fp0 / 10, fp0 / 100, ...`` and more carefully as the approximation + shows more detail) to obtain closer fits. + + The interpolation results for different values of ``s`` give some insight + into this process: + + >>> fig2 = plt.figure() + >>> s = [3e9, 2e9, 1e9, 1e8] + >>> for idx, sval in enumerate(s, 1): + ... lut = RectSphereBivariateSpline(lats, lons, data, s=sval) + ... data_interp = lut.ev(new_lats.ravel(), + ... new_lons.ravel()).reshape((360, 180)).T + ... ax = fig2.add_subplot(2, 2, idx) + ... ax.imshow(data_interp, interpolation='nearest') + ... ax.set_title(f"s = {sval:g}") + >>> plt.show() + + """ + + def __init__(self, u, v, r, s=0., pole_continuity=False, pole_values=None, + pole_exact=False, pole_flat=False): + iopt = np.array([0, 0, 0], dtype=dfitpack_int) + ider = np.array([-1, 0, -1, 0], dtype=dfitpack_int) + if pole_values is None: + pole_values = (None, None) + elif isinstance(pole_values, (float, np.float32, np.float64)): + pole_values = (pole_values, pole_values) + if isinstance(pole_continuity, bool): + pole_continuity = (pole_continuity, pole_continuity) + if isinstance(pole_exact, bool): + pole_exact = (pole_exact, pole_exact) + if isinstance(pole_flat, bool): + pole_flat = (pole_flat, pole_flat) + + r0, r1 = pole_values + iopt[1:] = pole_continuity + if r0 is None: + ider[0] = -1 + else: + ider[0] = pole_exact[0] + + if r1 is None: + ider[2] = -1 + else: + ider[2] = pole_exact[1] + + ider[1], ider[3] = pole_flat + + u, v = np.ravel(u), np.ravel(v) + r = np.asarray(r) + + if not (0.0 < u[0] and u[-1] < np.pi): + raise ValueError('u should be between (0, pi)') + if not -np.pi <= v[0] < np.pi: + raise ValueError('v[0] should be between [-pi, pi)') + if not v[-1] <= v[0] + 2*np.pi: + raise ValueError('v[-1] should be v[0] + 2pi or less ') + + if not np.all(np.diff(u) > 0.0): + raise ValueError('u must be strictly increasing') + if not np.all(np.diff(v) > 0.0): + raise ValueError('v must be strictly increasing') + + if not u.size == r.shape[0]: + raise ValueError('u dimension of r must have same number of ' + 'elements as u') + if not v.size == r.shape[1]: + raise ValueError('v dimension of r must have same number of ' + 'elements as v') + + if pole_continuity[1] is False and pole_flat[1] is True: + raise ValueError('if pole_continuity is False, so must be ' + 'pole_flat') + if pole_continuity[0] is False and pole_flat[0] is True: + raise ValueError('if pole_continuity is False, so must be ' + 'pole_flat') + + if not s >= 0.0: + raise ValueError('s should be positive') + + r = np.ravel(r) + nu, tu, nv, tv, c, fp, ier = dfitpack.regrid_smth_spher(iopt, ider, + u.copy(), + v.copy(), + r.copy(), + r0, r1, s) + + if ier not in [0, -1, -2]: + msg = _spfit_messages.get(ier, 'ier=%s' % (ier)) + raise ValueError(msg) + + self.fp = fp + self.tck = tu[:nu], tv[:nv], c[:(nu - 4) * (nv-4)] + self.degrees = (3, 3) + self.v0 = v[0] + + def __call__(self, theta, phi, dtheta=0, dphi=0, grid=True): + + theta = np.asarray(theta) + phi = np.asarray(phi) + + return SphereBivariateSpline.__call__(self, theta, phi, dtheta=dtheta, + dphi=dphi, grid=grid) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_impl.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_impl.py new file mode 100644 index 0000000000000000000000000000000000000000..99c660e2b183dca3f3f1d594bbbe6936a5eb9d7a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_impl.py @@ -0,0 +1,805 @@ +""" +fitpack (dierckx in netlib) --- A Python-C wrapper to FITPACK (by P. Dierckx). + FITPACK is a collection of FORTRAN programs for curve and surface + fitting with splines and tensor product splines. + +See + https://web.archive.org/web/20010524124604/http://www.cs.kuleuven.ac.be:80/cwis/research/nalag/research/topics/fitpack.html +or + http://www.netlib.org/dierckx/ + +Copyright 2002 Pearu Peterson all rights reserved, +Pearu Peterson +Permission to use, modify, and distribute this software is given under the +terms of the SciPy (BSD style) license. See LICENSE.txt that came with +this distribution for specifics. + +NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +TODO: Make interfaces to the following fitpack functions: + For univariate splines: cocosp, concon, fourco, insert + For bivariate splines: profil, regrid, parsur, surev +""" + +__all__ = ['splrep', 'splprep', 'splev', 'splint', 'sproot', 'spalde', + 'bisplrep', 'bisplev', 'insert', 'splder', 'splantider'] + +import warnings +import numpy as np +from . import _fitpack +from numpy import (atleast_1d, array, ones, zeros, sqrt, ravel, transpose, + empty, iinfo, asarray) + +# Try to replace _fitpack interface with +# f2py-generated version +from . import dfitpack + + +dfitpack_int = dfitpack.types.intvar.dtype + + +def _int_overflow(x, exception, msg=None): + """Cast the value to an dfitpack_int and raise an OverflowError if the value + cannot fit. + """ + if x > iinfo(dfitpack_int).max: + if msg is None: + msg = f'{x!r} cannot fit into an {dfitpack_int!r}' + raise exception(msg) + return dfitpack_int.type(x) + + +_iermess = { + 0: ["The spline has a residual sum of squares fp such that " + "abs(fp-s)/s<=0.001", None], + -1: ["The spline is an interpolating spline (fp=0)", None], + -2: ["The spline is weighted least-squares polynomial of degree k.\n" + "fp gives the upper bound fp0 for the smoothing factor s", None], + 1: ["The required storage space exceeds the available storage space.\n" + "Probable causes: data (x,y) size is too small or smoothing parameter" + "\ns is too small (fp>s).", ValueError], + 2: ["A theoretically impossible result when finding a smoothing spline\n" + "with fp = s. Probable cause: s too small. (abs(fp-s)/s>0.001)", + ValueError], + 3: ["The maximal number of iterations (20) allowed for finding smoothing\n" + "spline with fp=s has been reached. Probable cause: s too small.\n" + "(abs(fp-s)/s>0.001)", ValueError], + 10: ["Error on input data", ValueError], + 'unknown': ["An error occurred", TypeError] +} + +_iermess2 = { + 0: ["The spline has a residual sum of squares fp such that " + "abs(fp-s)/s<=0.001", None], + -1: ["The spline is an interpolating spline (fp=0)", None], + -2: ["The spline is weighted least-squares polynomial of degree kx and ky." + "\nfp gives the upper bound fp0 for the smoothing factor s", None], + -3: ["Warning. The coefficients of the spline have been computed as the\n" + "minimal norm least-squares solution of a rank deficient system.", + None], + 1: ["The required storage space exceeds the available storage space.\n" + "Probable causes: nxest or nyest too small or s is too small. (fp>s)", + ValueError], + 2: ["A theoretically impossible result when finding a smoothing spline\n" + "with fp = s. Probable causes: s too small or badly chosen eps.\n" + "(abs(fp-s)/s>0.001)", ValueError], + 3: ["The maximal number of iterations (20) allowed for finding smoothing\n" + "spline with fp=s has been reached. Probable cause: s too small.\n" + "(abs(fp-s)/s>0.001)", ValueError], + 4: ["No more knots can be added because the number of B-spline\n" + "coefficients already exceeds the number of data points m.\n" + "Probable causes: either s or m too small. (fp>s)", ValueError], + 5: ["No more knots can be added because the additional knot would\n" + "coincide with an old one. Probable cause: s too small or too large\n" + "a weight to an inaccurate data point. (fp>s)", ValueError], + 10: ["Error on input data", ValueError], + 11: ["rwrk2 too small, i.e., there is not enough workspace for computing\n" + "the minimal least-squares solution of a rank deficient system of\n" + "linear equations.", ValueError], + 'unknown': ["An error occurred", TypeError] +} + +_parcur_cache = {'t': array([], float), 'wrk': array([], float), + 'iwrk': array([], dfitpack_int), 'u': array([], float), + 'ub': 0, 'ue': 1} + + +def splprep(x, w=None, u=None, ub=None, ue=None, k=3, task=0, s=None, t=None, + full_output=0, nest=None, per=0, quiet=1): + # see the docstring of `_fitpack_py/splprep` + if task <= 0: + _parcur_cache = {'t': array([], float), 'wrk': array([], float), + 'iwrk': array([], dfitpack_int), 'u': array([], float), + 'ub': 0, 'ue': 1} + x = atleast_1d(x) + idim, m = x.shape + if per: + for i in range(idim): + if x[i][0] != x[i][-1]: + if not quiet: + warnings.warn(RuntimeWarning('Setting x[%d][%d]=x[%d][0]' % + (i, m, i)), + stacklevel=2) + x[i][-1] = x[i][0] + if not 0 < idim < 11: + raise TypeError('0 < idim < 11 must hold') + if w is None: + w = ones(m, float) + else: + w = atleast_1d(w) + ipar = (u is not None) + if ipar: + _parcur_cache['u'] = u + if ub is None: + _parcur_cache['ub'] = u[0] + else: + _parcur_cache['ub'] = ub + if ue is None: + _parcur_cache['ue'] = u[-1] + else: + _parcur_cache['ue'] = ue + else: + _parcur_cache['u'] = zeros(m, float) + if not (1 <= k <= 5): + raise TypeError('1 <= k= %d <=5 must hold' % k) + if not (-1 <= task <= 1): + raise TypeError('task must be -1, 0 or 1') + if (not len(w) == m) or (ipar == 1 and (not len(u) == m)): + raise TypeError('Mismatch of input dimensions') + if s is None: + s = m - sqrt(2*m) + if t is None and task == -1: + raise TypeError('Knots must be given for task=-1') + if t is not None: + _parcur_cache['t'] = atleast_1d(t) + n = len(_parcur_cache['t']) + if task == -1 and n < 2*k + 2: + raise TypeError('There must be at least 2*k+2 knots for task=-1') + if m <= k: + raise TypeError('m > k must hold') + if nest is None: + nest = m + 2*k + + if (task >= 0 and s == 0) or (nest < 0): + if per: + nest = m + 2*k + else: + nest = m + k + 1 + nest = max(nest, 2*k + 3) + u = _parcur_cache['u'] + ub = _parcur_cache['ub'] + ue = _parcur_cache['ue'] + t = _parcur_cache['t'] + wrk = _parcur_cache['wrk'] + iwrk = _parcur_cache['iwrk'] + t, c, o = _fitpack._parcur(ravel(transpose(x)), w, u, ub, ue, k, + task, ipar, s, t, nest, wrk, iwrk, per) + _parcur_cache['u'] = o['u'] + _parcur_cache['ub'] = o['ub'] + _parcur_cache['ue'] = o['ue'] + _parcur_cache['t'] = t + _parcur_cache['wrk'] = o['wrk'] + _parcur_cache['iwrk'] = o['iwrk'] + ier = o['ier'] + fp = o['fp'] + n = len(t) + u = o['u'] + c.shape = idim, n - k - 1 + tcku = [t, list(c), k], u + if ier <= 0 and not quiet: + warnings.warn(RuntimeWarning(_iermess[ier][0] + + "\tk=%d n=%d m=%d fp=%f s=%f" % + (k, len(t), m, fp, s)), + stacklevel=2) + if ier > 0 and not full_output: + if ier in [1, 2, 3]: + warnings.warn(RuntimeWarning(_iermess[ier][0]), stacklevel=2) + else: + try: + raise _iermess[ier][1](_iermess[ier][0]) + except KeyError as e: + raise _iermess['unknown'][1](_iermess['unknown'][0]) from e + if full_output: + try: + return tcku, fp, ier, _iermess[ier][0] + except KeyError: + return tcku, fp, ier, _iermess['unknown'][0] + else: + return tcku + + +_curfit_cache = {'t': array([], float), 'wrk': array([], float), + 'iwrk': array([], dfitpack_int)} + + +def splrep(x, y, w=None, xb=None, xe=None, k=3, task=0, s=None, t=None, + full_output=0, per=0, quiet=1): + # see the docstring of `_fitpack_py/splrep` + if task <= 0: + _curfit_cache = {} + x, y = map(atleast_1d, [x, y]) + m = len(x) + if w is None: + w = ones(m, float) + if s is None: + s = 0.0 + else: + w = atleast_1d(w) + if s is None: + s = m - sqrt(2*m) + if not len(w) == m: + raise TypeError('len(w)=%d is not equal to m=%d' % (len(w), m)) + if (m != len(y)) or (m != len(w)): + raise TypeError('Lengths of the first three arguments (x,y,w) must ' + 'be equal') + if not (1 <= k <= 5): + raise TypeError('Given degree of the spline (k=%d) is not supported. ' + '(1<=k<=5)' % k) + if m <= k: + raise TypeError('m > k must hold') + if xb is None: + xb = x[0] + if xe is None: + xe = x[-1] + if not (-1 <= task <= 1): + raise TypeError('task must be -1, 0 or 1') + if t is not None: + task = -1 + if task == -1: + if t is None: + raise TypeError('Knots must be given for task=-1') + numknots = len(t) + _curfit_cache['t'] = empty((numknots + 2*k + 2,), float) + _curfit_cache['t'][k+1:-k-1] = t + nest = len(_curfit_cache['t']) + elif task == 0: + if per: + nest = max(m + 2*k, 2*k + 3) + else: + nest = max(m + k + 1, 2*k + 3) + t = empty((nest,), float) + _curfit_cache['t'] = t + if task <= 0: + if per: + _curfit_cache['wrk'] = empty((m*(k + 1) + nest*(8 + 5*k),), float) + else: + _curfit_cache['wrk'] = empty((m*(k + 1) + nest*(7 + 3*k),), float) + _curfit_cache['iwrk'] = empty((nest,), dfitpack_int) + try: + t = _curfit_cache['t'] + wrk = _curfit_cache['wrk'] + iwrk = _curfit_cache['iwrk'] + except KeyError as e: + raise TypeError("must call with task=1 only after" + " call with task=0,-1") from e + if not per: + n, c, fp, ier = dfitpack.curfit(task, x, y, w, t, wrk, iwrk, + xb, xe, k, s) + else: + n, c, fp, ier = dfitpack.percur(task, x, y, w, t, wrk, iwrk, k, s) + tck = (t[:n], c[:n], k) + if ier <= 0 and not quiet: + _mess = (_iermess[ier][0] + "\tk=%d n=%d m=%d fp=%f s=%f" % + (k, len(t), m, fp, s)) + warnings.warn(RuntimeWarning(_mess), stacklevel=2) + if ier > 0 and not full_output: + if ier in [1, 2, 3]: + warnings.warn(RuntimeWarning(_iermess[ier][0]), stacklevel=2) + else: + try: + raise _iermess[ier][1](_iermess[ier][0]) + except KeyError as e: + raise _iermess['unknown'][1](_iermess['unknown'][0]) from e + if full_output: + try: + return tck, fp, ier, _iermess[ier][0] + except KeyError: + return tck, fp, ier, _iermess['unknown'][0] + else: + return tck + + +def splev(x, tck, der=0, ext=0): + # see the docstring of `_fitpack_py/splev` + t, c, k = tck + try: + c[0][0] + parametric = True + except Exception: + parametric = False + if parametric: + return list(map(lambda c, x=x, t=t, k=k, der=der: + splev(x, [t, c, k], der, ext), c)) + else: + if not (0 <= der <= k): + raise ValueError("0<=der=%d<=k=%d must hold" % (der, k)) + if ext not in (0, 1, 2, 3): + raise ValueError("ext = %s not in (0, 1, 2, 3) " % ext) + + x = asarray(x) + shape = x.shape + x = atleast_1d(x).ravel() + if der == 0: + y, ier = dfitpack.splev(t, c, k, x, ext) + else: + y, ier = dfitpack.splder(t, c, k, x, der, ext) + + if ier == 10: + raise ValueError("Invalid input data") + if ier == 1: + raise ValueError("Found x value not in the domain") + if ier: + raise TypeError("An error occurred") + + return y.reshape(shape) + + +def splint(a, b, tck, full_output=0): + # see the docstring of `_fitpack_py/splint` + t, c, k = tck + try: + c[0][0] + parametric = True + except Exception: + parametric = False + if parametric: + return list(map(lambda c, a=a, b=b, t=t, k=k: + splint(a, b, [t, c, k]), c)) + else: + aint, wrk = dfitpack.splint(t, c, k, a, b) + if full_output: + return aint, wrk + else: + return aint + + +def sproot(tck, mest=10): + # see the docstring of `_fitpack_py/sproot` + t, c, k = tck + if k != 3: + raise ValueError("sproot works only for cubic (k=3) splines") + try: + c[0][0] + parametric = True + except Exception: + parametric = False + if parametric: + return list(map(lambda c, t=t, k=k, mest=mest: + sproot([t, c, k], mest), c)) + else: + if len(t) < 8: + raise TypeError("The number of knots %d>=8" % len(t)) + z, m, ier = dfitpack.sproot(t, c, mest) + if ier == 10: + raise TypeError("Invalid input data. " + "t1<=..<=t4 1: + return list(map(lambda x, tck=tck: spalde(x, tck), x)) + d, ier = dfitpack.spalde(t, c, k+1, x[0]) + if ier == 0: + return d + if ier == 10: + raise TypeError("Invalid input data. t(k)<=x<=t(n-k+1) must hold.") + raise TypeError("Unknown error") + +# def _curfit(x,y,w=None,xb=None,xe=None,k=3,task=0,s=None,t=None, +# full_output=0,nest=None,per=0,quiet=1): + + +_surfit_cache = {'tx': array([], float), 'ty': array([], float), + 'wrk': array([], float), 'iwrk': array([], dfitpack_int)} + + +def bisplrep(x, y, z, w=None, xb=None, xe=None, yb=None, ye=None, + kx=3, ky=3, task=0, s=None, eps=1e-16, tx=None, ty=None, + full_output=0, nxest=None, nyest=None, quiet=1): + """ + Find a bivariate B-spline representation of a surface. + + Given a set of data points (x[i], y[i], z[i]) representing a surface + z=f(x,y), compute a B-spline representation of the surface. Based on + the routine SURFIT from FITPACK. + + Parameters + ---------- + x, y, z : ndarray + Rank-1 arrays of data points. + w : ndarray, optional + Rank-1 array of weights. By default ``w=np.ones(len(x))``. + xb, xe : float, optional + End points of approximation interval in `x`. + By default ``xb = x.min(), xe=x.max()``. + yb, ye : float, optional + End points of approximation interval in `y`. + By default ``yb=y.min(), ye = y.max()``. + kx, ky : int, optional + The degrees of the spline (1 <= kx, ky <= 5). + Third order (kx=ky=3) is recommended. + task : int, optional + If task=0, find knots in x and y and coefficients for a given + smoothing factor, s. + If task=1, find knots and coefficients for another value of the + smoothing factor, s. bisplrep must have been previously called + with task=0 or task=1. + If task=-1, find coefficients for a given set of knots tx, ty. + s : float, optional + A non-negative smoothing factor. If weights correspond + to the inverse of the standard-deviation of the errors in z, + then a good s-value should be found in the range + ``(m-sqrt(2*m),m+sqrt(2*m))`` where m=len(x). + eps : float, optional + A threshold for determining the effective rank of an + over-determined linear system of equations (0 < eps < 1). + `eps` is not likely to need changing. + tx, ty : ndarray, optional + Rank-1 arrays of the knots of the spline for task=-1 + full_output : int, optional + Non-zero to return optional outputs. + nxest, nyest : int, optional + Over-estimates of the total number of knots. If None then + ``nxest = max(kx+sqrt(m/2),2*kx+3)``, + ``nyest = max(ky+sqrt(m/2),2*ky+3)``. + quiet : int, optional + Non-zero to suppress printing of messages. + + Returns + ------- + tck : array_like + A list [tx, ty, c, kx, ky] containing the knots (tx, ty) and + coefficients (c) of the bivariate B-spline representation of the + surface along with the degree of the spline. + fp : ndarray + The weighted sum of squared residuals of the spline approximation. + ier : int + An integer flag about splrep success. Success is indicated if + ier<=0. If ier in [1,2,3] an error occurred but was not raised. + Otherwise an error is raised. + msg : str + A message corresponding to the integer flag, ier. + + See Also + -------- + splprep, splrep, splint, sproot, splev + UnivariateSpline, BivariateSpline + + Notes + ----- + See `bisplev` to evaluate the value of the B-spline given its tck + representation. + + If the input data is such that input dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolation. + + References + ---------- + .. [1] Dierckx P.:An algorithm for surface fitting with spline functions + Ima J. Numer. Anal. 1 (1981) 267-283. + .. [2] Dierckx P.:An algorithm for surface fitting with spline functions + report tw50, Dept. Computer Science,K.U.Leuven, 1980. + .. [3] Dierckx P.:Curve and surface fitting with splines, Monographs on + Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + Examples are given :ref:`in the tutorial `. + + """ + x, y, z = map(ravel, [x, y, z]) # ensure 1-d arrays. + m = len(x) + if not (m == len(y) == len(z)): + raise TypeError('len(x)==len(y)==len(z) must hold.') + if w is None: + w = ones(m, float) + else: + w = atleast_1d(w) + if not len(w) == m: + raise TypeError('len(w)=%d is not equal to m=%d' % (len(w), m)) + if xb is None: + xb = x.min() + if xe is None: + xe = x.max() + if yb is None: + yb = y.min() + if ye is None: + ye = y.max() + if not (-1 <= task <= 1): + raise TypeError('task must be -1, 0 or 1') + if s is None: + s = m - sqrt(2*m) + if tx is None and task == -1: + raise TypeError('Knots_x must be given for task=-1') + if tx is not None: + _surfit_cache['tx'] = atleast_1d(tx) + nx = len(_surfit_cache['tx']) + if ty is None and task == -1: + raise TypeError('Knots_y must be given for task=-1') + if ty is not None: + _surfit_cache['ty'] = atleast_1d(ty) + ny = len(_surfit_cache['ty']) + if task == -1 and nx < 2*kx+2: + raise TypeError('There must be at least 2*kx+2 knots_x for task=-1') + if task == -1 and ny < 2*ky+2: + raise TypeError('There must be at least 2*ky+2 knots_x for task=-1') + if not ((1 <= kx <= 5) and (1 <= ky <= 5)): + raise TypeError('Given degree of the spline (kx,ky=%d,%d) is not ' + 'supported. (1<=k<=5)' % (kx, ky)) + if m < (kx + 1)*(ky + 1): + raise TypeError('m >= (kx+1)(ky+1) must hold') + if nxest is None: + nxest = int(kx + sqrt(m/2)) + if nyest is None: + nyest = int(ky + sqrt(m/2)) + nxest, nyest = max(nxest, 2*kx + 3), max(nyest, 2*ky + 3) + if task >= 0 and s == 0: + nxest = int(kx + sqrt(3*m)) + nyest = int(ky + sqrt(3*m)) + if task == -1: + _surfit_cache['tx'] = atleast_1d(tx) + _surfit_cache['ty'] = atleast_1d(ty) + tx, ty = _surfit_cache['tx'], _surfit_cache['ty'] + wrk = _surfit_cache['wrk'] + u = nxest - kx - 1 + v = nyest - ky - 1 + km = max(kx, ky) + 1 + ne = max(nxest, nyest) + bx, by = kx*v + ky + 1, ky*u + kx + 1 + b1, b2 = bx, bx + v - ky + if bx > by: + b1, b2 = by, by + u - kx + msg = "Too many data points to interpolate" + lwrk1 = _int_overflow(u*v*(2 + b1 + b2) + + 2*(u + v + km*(m + ne) + ne - kx - ky) + b2 + 1, + OverflowError, + msg=msg) + lwrk2 = _int_overflow(u*v*(b2 + 1) + b2, OverflowError, msg=msg) + tx, ty, c, o = _fitpack._surfit(x, y, z, w, xb, xe, yb, ye, kx, ky, + task, s, eps, tx, ty, nxest, nyest, + wrk, lwrk1, lwrk2) + _curfit_cache['tx'] = tx + _curfit_cache['ty'] = ty + _curfit_cache['wrk'] = o['wrk'] + ier, fp = o['ier'], o['fp'] + tck = [tx, ty, c, kx, ky] + + ierm = min(11, max(-3, ier)) + if ierm <= 0 and not quiet: + _mess = (_iermess2[ierm][0] + + "\tkx,ky=%d,%d nx,ny=%d,%d m=%d fp=%f s=%f" % + (kx, ky, len(tx), len(ty), m, fp, s)) + warnings.warn(RuntimeWarning(_mess), stacklevel=2) + if ierm > 0 and not full_output: + if ier in [1, 2, 3, 4, 5]: + _mess = ("\n\tkx,ky=%d,%d nx,ny=%d,%d m=%d fp=%f s=%f" % + (kx, ky, len(tx), len(ty), m, fp, s)) + warnings.warn(RuntimeWarning(_iermess2[ierm][0] + _mess), stacklevel=2) + else: + try: + raise _iermess2[ierm][1](_iermess2[ierm][0]) + except KeyError as e: + raise _iermess2['unknown'][1](_iermess2['unknown'][0]) from e + if full_output: + try: + return tck, fp, ier, _iermess2[ierm][0] + except KeyError: + return tck, fp, ier, _iermess2['unknown'][0] + else: + return tck + + +def bisplev(x, y, tck, dx=0, dy=0): + """ + Evaluate a bivariate B-spline and its derivatives. + + Return a rank-2 array of spline function values (or spline derivative + values) at points given by the cross-product of the rank-1 arrays `x` and + `y`. In special cases, return an array or just a float if either `x` or + `y` or both are floats. Based on BISPEV and PARDER from FITPACK. + + Parameters + ---------- + x, y : ndarray + Rank-1 arrays specifying the domain over which to evaluate the + spline or its derivative. + tck : tuple + A sequence of length 5 returned by `bisplrep` containing the knot + locations, the coefficients, and the degree of the spline: + [tx, ty, c, kx, ky]. + dx, dy : int, optional + The orders of the partial derivatives in `x` and `y` respectively. + + Returns + ------- + vals : ndarray + The B-spline or its derivative evaluated over the set formed by + the cross-product of `x` and `y`. + + See Also + -------- + splprep, splrep, splint, sproot, splev + UnivariateSpline, BivariateSpline + + Notes + ----- + See `bisplrep` to generate the `tck` representation. + + References + ---------- + .. [1] Dierckx P. : An algorithm for surface fitting + with spline functions + Ima J. Numer. Anal. 1 (1981) 267-283. + .. [2] Dierckx P. : An algorithm for surface fitting + with spline functions + report tw50, Dept. Computer Science,K.U.Leuven, 1980. + .. [3] Dierckx P. : Curve and surface fitting with splines, + Monographs on Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + Examples are given :ref:`in the tutorial `. + + """ + tx, ty, c, kx, ky = tck + if not (0 <= dx < kx): + raise ValueError("0 <= dx = %d < kx = %d must hold" % (dx, kx)) + if not (0 <= dy < ky): + raise ValueError("0 <= dy = %d < ky = %d must hold" % (dy, ky)) + x, y = map(atleast_1d, [x, y]) + if (len(x.shape) != 1) or (len(y.shape) != 1): + raise ValueError("First two entries should be rank-1 arrays.") + + msg = "Too many data points to interpolate." + + _int_overflow(x.size * y.size, MemoryError, msg=msg) + + if dx != 0 or dy != 0: + _int_overflow((tx.size - kx - 1)*(ty.size - ky - 1), + MemoryError, msg=msg) + z, ier = dfitpack.parder(tx, ty, c, kx, ky, dx, dy, x, y) + else: + z, ier = dfitpack.bispev(tx, ty, c, kx, ky, x, y) + + if ier == 10: + raise ValueError("Invalid input data") + if ier: + raise TypeError("An error occurred") + z.shape = len(x), len(y) + if len(z) > 1: + return z + if len(z[0]) > 1: + return z[0] + return z[0][0] + + +def dblint(xa, xb, ya, yb, tck): + """Evaluate the integral of a spline over area [xa,xb] x [ya,yb]. + + Parameters + ---------- + xa, xb : float + The end-points of the x integration interval. + ya, yb : float + The end-points of the y integration interval. + tck : list [tx, ty, c, kx, ky] + A sequence of length 5 returned by bisplrep containing the knot + locations tx, ty, the coefficients c, and the degrees kx, ky + of the spline. + + Returns + ------- + integ : float + The value of the resulting integral. + """ + tx, ty, c, kx, ky = tck + return dfitpack.dblint(tx, ty, c, kx, ky, xa, xb, ya, yb) + + +def insert(x, tck, m=1, per=0): + # see the docstring of `_fitpack_py/insert` + t, c, k = tck + try: + c[0][0] + parametric = True + except Exception: + parametric = False + if parametric: + cc = [] + for c_vals in c: + tt, cc_val, kk = insert(x, [t, c_vals, k], m) + cc.append(cc_val) + return (tt, cc, kk) + else: + tt, cc, ier = _fitpack._insert(per, t, c, k, x, m) + if ier == 10: + raise ValueError("Invalid input data") + if ier: + raise TypeError("An error occurred") + return (tt, cc, k) + + +def splder(tck, n=1): + # see the docstring of `_fitpack_py/splder` + if n < 0: + return splantider(tck, -n) + + t, c, k = tck + + if n > k: + raise ValueError(f"Order of derivative (n = {n!r}) must be <= " + f"order of spline (k = {tck[2]!r})") + + # Extra axes for the trailing dims of the `c` array: + sh = (slice(None),) + ((None,)*len(c.shape[1:])) + + with np.errstate(invalid='raise', divide='raise'): + try: + for j in range(n): + # See e.g. Schumaker, Spline Functions: Basic Theory, Chapter 5 + + # Compute the denominator in the differentiation formula. + # (and append trailing dims, if necessary) + dt = t[k+1:-1] - t[1:-k-1] + dt = dt[sh] + # Compute the new coefficients + c = (c[1:-1-k] - c[:-2-k]) * k / dt + # Pad coefficient array to same size as knots (FITPACK + # convention) + c = np.r_[c, np.zeros((k,) + c.shape[1:])] + # Adjust knots + t = t[1:-1] + k -= 1 + except FloatingPointError as e: + raise ValueError(("The spline has internal repeated knots " + "and is not differentiable %d times") % n) from e + + return t, c, k + + +def splantider(tck, n=1): + # see the docstring of `_fitpack_py/splantider` + if n < 0: + return splder(tck, -n) + + t, c, k = tck + + # Extra axes for the trailing dims of the `c` array: + sh = (slice(None),) + (None,)*len(c.shape[1:]) + + for j in range(n): + # This is the inverse set of operations to splder. + + # Compute the multiplier in the antiderivative formula. + dt = t[k+1:] - t[:-k-1] + dt = dt[sh] + # Compute the new coefficients + c = np.cumsum(c[:-k-1] * dt, axis=0) / (k + 1) + c = np.r_[np.zeros((1,) + c.shape[1:]), + c, + [c[-1]] * (k+2)] + # New knots + t = np.r_[t[0], t, t[-1]] + k += 1 + + return t, c, k diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_py.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_py.py new file mode 100644 index 0000000000000000000000000000000000000000..91ee711fead98bacd3f15b4175520a4d387390df --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_fitpack_py.py @@ -0,0 +1,796 @@ +__all__ = ['splrep', 'splprep', 'splev', 'splint', 'sproot', 'spalde', + 'bisplrep', 'bisplev', 'insert', 'splder', 'splantider'] + + +import numpy as np + +# These are in the API for fitpack even if not used in fitpack.py itself. +from ._fitpack_impl import bisplrep, bisplev, dblint # noqa: F401 +from . import _fitpack_impl as _impl +from ._bsplines import BSpline + + +def splprep(x, w=None, u=None, ub=None, ue=None, k=3, task=0, s=None, t=None, + full_output=0, nest=None, per=0, quiet=1): + """ + Find the B-spline representation of an N-D curve. + + Given a list of N rank-1 arrays, `x`, which represent a curve in + N-dimensional space parametrized by `u`, find a smooth approximating + spline curve g(`u`). Uses the FORTRAN routine parcur from FITPACK. + + Parameters + ---------- + x : array_like + A list of sample vector arrays representing the curve. + w : array_like, optional + Strictly positive rank-1 array of weights the same length as `x[0]`. + The weights are used in computing the weighted least-squares spline + fit. If the errors in the `x` values have standard-deviation given by + the vector d, then `w` should be 1/d. Default is ``ones(len(x[0]))``. + u : array_like, optional + An array of parameter values. If not given, these values are + calculated automatically as ``M = len(x[0])``, where + + v[0] = 0 + + v[i] = v[i-1] + distance(`x[i]`, `x[i-1]`) + + u[i] = v[i] / v[M-1] + + ub, ue : int, optional + The end-points of the parameters interval. Defaults to + u[0] and u[-1]. + k : int, optional + Degree of the spline. Cubic splines are recommended. + Even values of `k` should be avoided especially with a small s-value. + ``1 <= k <= 5``, default is 3. + task : int, optional + If task==0 (default), find t and c for a given smoothing factor, s. + If task==1, find t and c for another value of the smoothing factor, s. + There must have been a previous call with task=0 or task=1 + for the same set of data. + If task=-1 find the weighted least square spline for a given set of + knots, t. + s : float, optional + A smoothing condition. The amount of smoothness is determined by + satisfying the conditions: ``sum((w * (y - g))**2,axis=0) <= s``, + where g(x) is the smoothed interpolation of (x,y). The user can + use `s` to control the trade-off between closeness and smoothness + of fit. Larger `s` means more smoothing while smaller values of `s` + indicate less smoothing. Recommended values of `s` depend on the + weights, w. If the weights represent the inverse of the + standard-deviation of y, then a good `s` value should be found in + the range ``(m-sqrt(2*m),m+sqrt(2*m))``, where m is the number of + data points in x, y, and w. + t : array, optional + The knots needed for ``task=-1``. + There must be at least ``2*k+2`` knots. + full_output : int, optional + If non-zero, then return optional outputs. + nest : int, optional + An over-estimate of the total number of knots of the spline to + help in determining the storage space. By default nest=m/2. + Always large enough is nest=m+k+1. + per : int, optional + If non-zero, data points are considered periodic with period + ``x[m-1] - x[0]`` and a smooth periodic spline approximation is + returned. Values of ``y[m-1]`` and ``w[m-1]`` are not used. + quiet : int, optional + Non-zero to suppress messages. + + Returns + ------- + tck : tuple + A tuple, ``(t,c,k)`` containing the vector of knots, the B-spline + coefficients, and the degree of the spline. + u : array + An array of the values of the parameter. + fp : float + The weighted sum of squared residuals of the spline approximation. + ier : int + An integer flag about splrep success. Success is indicated + if ier<=0. If ier in [1,2,3] an error occurred but was not raised. + Otherwise an error is raised. + msg : str + A message corresponding to the integer flag, ier. + + See Also + -------- + splrep, splev, sproot, spalde, splint, + bisplrep, bisplev + UnivariateSpline, BivariateSpline + BSpline + make_interp_spline + + Notes + ----- + See `splev` for evaluation of the spline and its derivatives. + The number of dimensions N must be smaller than 11. + + The number of coefficients in the `c` array is ``k+1`` less than the number + of knots, ``len(t)``. This is in contrast with `splrep`, which zero-pads + the array of coefficients to have the same length as the array of knots. + These additional coefficients are ignored by evaluation routines, `splev` + and `BSpline`. + + References + ---------- + .. [1] P. Dierckx, "Algorithms for smoothing data with periodic and + parametric splines, Computer Graphics and Image Processing", + 20 (1982) 171-184. + .. [2] P. Dierckx, "Algorithms for smoothing data with periodic and + parametric splines", report tw55, Dept. Computer Science, + K.U.Leuven, 1981. + .. [3] P. Dierckx, "Curve and surface fitting with splines", Monographs on + Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + Generate a discretization of a limacon curve in the polar coordinates: + + >>> import numpy as np + >>> phi = np.linspace(0, 2.*np.pi, 40) + >>> r = 0.5 + np.cos(phi) # polar coords + >>> x, y = r * np.cos(phi), r * np.sin(phi) # convert to cartesian + + And interpolate: + + >>> from scipy.interpolate import splprep, splev + >>> tck, u = splprep([x, y], s=0) + >>> new_points = splev(u, tck) + + Notice that (i) we force interpolation by using `s=0`, + (ii) the parameterization, ``u``, is generated automatically. + Now plot the result: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.plot(x, y, 'ro') + >>> ax.plot(new_points[0], new_points[1], 'r-') + >>> plt.show() + + """ + + res = _impl.splprep(x, w, u, ub, ue, k, task, s, t, full_output, nest, per, + quiet) + return res + + +def splrep(x, y, w=None, xb=None, xe=None, k=3, task=0, s=None, t=None, + full_output=0, per=0, quiet=1): + """ + Find the B-spline representation of a 1-D curve. + + Given the set of data points ``(x[i], y[i])`` determine a smooth spline + approximation of degree k on the interval ``xb <= x <= xe``. + + Parameters + ---------- + x, y : array_like + The data points defining a curve ``y = f(x)``. + w : array_like, optional + Strictly positive rank-1 array of weights the same length as `x` and `y`. + The weights are used in computing the weighted least-squares spline + fit. If the errors in the `y` values have standard-deviation given by the + vector ``d``, then `w` should be ``1/d``. Default is ``ones(len(x))``. + xb, xe : float, optional + The interval to fit. If None, these default to ``x[0]`` and ``x[-1]`` + respectively. + k : int, optional + The degree of the spline fit. It is recommended to use cubic splines. + Even values of `k` should be avoided especially with small `s` values. + ``1 <= k <= 5``. + task : {1, 0, -1}, optional + If ``task==0``, find ``t`` and ``c`` for a given smoothing factor, `s`. + + If ``task==1`` find ``t`` and ``c`` for another value of the smoothing factor, + `s`. There must have been a previous call with ``task=0`` or ``task=1`` for + the same set of data (``t`` will be stored an used internally) + + If ``task=-1`` find the weighted least square spline for a given set of + knots, ``t``. These should be interior knots as knots on the ends will be + added automatically. + s : float, optional + A smoothing condition. The amount of smoothness is determined by + satisfying the conditions: ``sum((w * (y - g))**2,axis=0) <= s`` where ``g(x)`` + is the smoothed interpolation of ``(x,y)``. The user can use `s` to control + the tradeoff between closeness and smoothness of fit. Larger `s` means + more smoothing while smaller values of `s` indicate less smoothing. + Recommended values of `s` depend on the weights, `w`. If the weights + represent the inverse of the standard-deviation of `y`, then a good `s` + value should be found in the range ``(m-sqrt(2*m),m+sqrt(2*m))`` where ``m`` is + the number of datapoints in `x`, `y`, and `w`. default : ``s=m-sqrt(2*m)`` if + weights are supplied. ``s = 0.0`` (interpolating) if no weights are + supplied. + t : array_like, optional + The knots needed for ``task=-1``. If given then task is automatically set + to ``-1``. + full_output : bool, optional + If non-zero, then return optional outputs. + per : bool, optional + If non-zero, data points are considered periodic with period ``x[m-1]`` - + ``x[0]`` and a smooth periodic spline approximation is returned. Values of + ``y[m-1]`` and ``w[m-1]`` are not used. + The default is zero, corresponding to boundary condition 'not-a-knot'. + quiet : bool, optional + Non-zero to suppress messages. + + Returns + ------- + tck : tuple + A tuple ``(t,c,k)`` containing the vector of knots, the B-spline + coefficients, and the degree of the spline. + fp : array, optional + The weighted sum of squared residuals of the spline approximation. + ier : int, optional + An integer flag about splrep success. Success is indicated if ``ier<=0``. + If ``ier in [1,2,3]``, an error occurred but was not raised. Otherwise an + error is raised. + msg : str, optional + A message corresponding to the integer flag, `ier`. + + See Also + -------- + UnivariateSpline, BivariateSpline + splprep, splev, sproot, spalde, splint + bisplrep, bisplev + BSpline + make_interp_spline + + Notes + ----- + See `splev` for evaluation of the spline and its derivatives. Uses the + FORTRAN routine ``curfit`` from FITPACK. + + The user is responsible for assuring that the values of `x` are unique. + Otherwise, `splrep` will not return sensible results. + + If provided, knots `t` must satisfy the Schoenberg-Whitney conditions, + i.e., there must be a subset of data points ``x[j]`` such that + ``t[j] < x[j] < t[j+k+1]``, for ``j=0, 1,...,n-k-2``. + + This routine zero-pads the coefficients array ``c`` to have the same length + as the array of knots ``t`` (the trailing ``k + 1`` coefficients are ignored + by the evaluation routines, `splev` and `BSpline`.) This is in contrast with + `splprep`, which does not zero-pad the coefficients. + + The default boundary condition is 'not-a-knot', i.e. the first and second + segment at a curve end are the same polynomial. More boundary conditions are + available in `CubicSpline`. + + References + ---------- + Based on algorithms described in [1]_, [2]_, [3]_, and [4]_: + + .. [1] P. Dierckx, "An algorithm for smoothing, differentiation and + integration of experimental data using spline functions", + J.Comp.Appl.Maths 1 (1975) 165-184. + .. [2] P. Dierckx, "A fast algorithm for smoothing data on a rectangular + grid while using spline functions", SIAM J.Numer.Anal. 19 (1982) + 1286-1304. + .. [3] P. Dierckx, "An improved algorithm for curve fitting with spline + functions", report tw54, Dept. Computer Science,K.U. Leuven, 1981. + .. [4] P. Dierckx, "Curve and surface fitting with splines", Monographs on + Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + You can interpolate 1-D points with a B-spline curve. + Further examples are given in + :ref:`in the tutorial `. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import splev, splrep + >>> x = np.linspace(0, 10, 10) + >>> y = np.sin(x) + >>> spl = splrep(x, y) + >>> x2 = np.linspace(0, 10, 200) + >>> y2 = splev(x2, spl) + >>> plt.plot(x, y, 'o', x2, y2) + >>> plt.show() + + """ + res = _impl.splrep(x, y, w, xb, xe, k, task, s, t, full_output, per, quiet) + return res + + +def splev(x, tck, der=0, ext=0): + """ + Evaluate a B-spline or its derivatives. + + Given the knots and coefficients of a B-spline representation, evaluate + the value of the smoothing polynomial and its derivatives. This is a + wrapper around the FORTRAN routines splev and splder of FITPACK. + + Parameters + ---------- + x : array_like + An array of points at which to return the value of the smoothed + spline or its derivatives. If `tck` was returned from `splprep`, + then the parameter values, u should be given. + tck : 3-tuple or a BSpline object + If a tuple, then it should be a sequence of length 3 returned by + `splrep` or `splprep` containing the knots, coefficients, and degree + of the spline. (Also see Notes.) + der : int, optional + The order of derivative of the spline to compute (must be less than + or equal to k, the degree of the spline). + ext : int, optional + Controls the value returned for elements of ``x`` not in the + interval defined by the knot sequence. + + * if ext=0, return the extrapolated value. + * if ext=1, return 0 + * if ext=2, raise a ValueError + * if ext=3, return the boundary value. + + The default value is 0. + + Returns + ------- + y : ndarray or list of ndarrays + An array of values representing the spline function evaluated at + the points in `x`. If `tck` was returned from `splprep`, then this + is a list of arrays representing the curve in an N-D space. + + See Also + -------- + splprep, splrep, sproot, spalde, splint + bisplrep, bisplev + BSpline + + Notes + ----- + Manipulating the tck-tuples directly is not recommended. In new code, + prefer using `BSpline` objects. + + References + ---------- + .. [1] C. de Boor, "On calculating with b-splines", J. Approximation + Theory, 6, p.50-62, 1972. + .. [2] M. G. Cox, "The numerical evaluation of b-splines", J. Inst. Maths + Applics, 10, p.134-149, 1972. + .. [3] P. Dierckx, "Curve and surface fitting with splines", Monographs + on Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + Examples are given :ref:`in the tutorial `. + + """ + if isinstance(tck, BSpline): + if tck.c.ndim > 1: + mesg = ("Calling splev() with BSpline objects with c.ndim > 1 is " + "not allowed. Use BSpline.__call__(x) instead.") + raise ValueError(mesg) + + # remap the out-of-bounds behavior + try: + extrapolate = {0: True, }[ext] + except KeyError as e: + raise ValueError("Extrapolation mode %s is not supported " + "by BSpline." % ext) from e + + return tck(x, der, extrapolate=extrapolate) + else: + return _impl.splev(x, tck, der, ext) + + +def splint(a, b, tck, full_output=0): + """ + Evaluate the definite integral of a B-spline between two given points. + + Parameters + ---------- + a, b : float + The end-points of the integration interval. + tck : tuple or a BSpline instance + If a tuple, then it should be a sequence of length 3, containing the + vector of knots, the B-spline coefficients, and the degree of the + spline (see `splev`). + full_output : int, optional + Non-zero to return optional output. + + Returns + ------- + integral : float + The resulting integral. + wrk : ndarray + An array containing the integrals of the normalized B-splines + defined on the set of knots. + (Only returned if `full_output` is non-zero) + + See Also + -------- + splprep, splrep, sproot, spalde, splev + bisplrep, bisplev + BSpline + + Notes + ----- + `splint` silently assumes that the spline function is zero outside the data + interval (`a`, `b`). + + Manipulating the tck-tuples directly is not recommended. In new code, + prefer using the `BSpline` objects. + + References + ---------- + .. [1] P.W. Gaffney, The calculation of indefinite integrals of b-splines", + J. Inst. Maths Applics, 17, p.37-41, 1976. + .. [2] P. Dierckx, "Curve and surface fitting with splines", Monographs + on Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + Examples are given :ref:`in the tutorial `. + + """ + if isinstance(tck, BSpline): + if tck.c.ndim > 1: + mesg = ("Calling splint() with BSpline objects with c.ndim > 1 is " + "not allowed. Use BSpline.integrate() instead.") + raise ValueError(mesg) + + if full_output != 0: + mesg = ("full_output = %s is not supported. Proceeding as if " + "full_output = 0" % full_output) + + return tck.integrate(a, b, extrapolate=False) + else: + return _impl.splint(a, b, tck, full_output) + + +def sproot(tck, mest=10): + """ + Find the roots of a cubic B-spline. + + Given the knots (>=8) and coefficients of a cubic B-spline return the + roots of the spline. + + Parameters + ---------- + tck : tuple or a BSpline object + If a tuple, then it should be a sequence of length 3, containing the + vector of knots, the B-spline coefficients, and the degree of the + spline. + The number of knots must be >= 8, and the degree must be 3. + The knots must be a montonically increasing sequence. + mest : int, optional + An estimate of the number of zeros (Default is 10). + + Returns + ------- + zeros : ndarray + An array giving the roots of the spline. + + See Also + -------- + splprep, splrep, splint, spalde, splev + bisplrep, bisplev + BSpline + + Notes + ----- + Manipulating the tck-tuples directly is not recommended. In new code, + prefer using the `BSpline` objects. + + References + ---------- + .. [1] C. de Boor, "On calculating with b-splines", J. Approximation + Theory, 6, p.50-62, 1972. + .. [2] M. G. Cox, "The numerical evaluation of b-splines", J. Inst. Maths + Applics, 10, p.134-149, 1972. + .. [3] P. Dierckx, "Curve and surface fitting with splines", Monographs + on Numerical Analysis, Oxford University Press, 1993. + + Examples + -------- + + For some data, this method may miss a root. This happens when one of + the spline knots (which FITPACK places automatically) happens to + coincide with the true root. A workaround is to convert to `PPoly`, + which uses a different root-finding algorithm. + + For example, + + >>> x = [1.96, 1.97, 1.98, 1.99, 2.00, 2.01, 2.02, 2.03, 2.04, 2.05] + >>> y = [-6.365470e-03, -4.790580e-03, -3.204320e-03, -1.607270e-03, + ... 4.440892e-16, 1.616930e-03, 3.243000e-03, 4.877670e-03, + ... 6.520430e-03, 8.170770e-03] + >>> from scipy.interpolate import splrep, sproot, PPoly + >>> tck = splrep(x, y, s=0) + >>> sproot(tck) + array([], dtype=float64) + + Converting to a PPoly object does find the roots at `x=2`: + + >>> ppoly = PPoly.from_spline(tck) + >>> ppoly.roots(extrapolate=False) + array([2.]) + + + Further examples are given :ref:`in the tutorial + `. + + """ + if isinstance(tck, BSpline): + if tck.c.ndim > 1: + mesg = ("Calling sproot() with BSpline objects with c.ndim > 1 is " + "not allowed.") + raise ValueError(mesg) + + t, c, k = tck.tck + + # _impl.sproot expects the interpolation axis to be last, so roll it. + # NB: This transpose is a no-op if c is 1D. + sh = tuple(range(c.ndim)) + c = c.transpose(sh[1:] + (0,)) + return _impl.sproot((t, c, k), mest) + else: + return _impl.sproot(tck, mest) + + +def spalde(x, tck): + """ + Evaluate all derivatives of a B-spline. + + Given the knots and coefficients of a cubic B-spline compute all + derivatives up to order k at a point (or set of points). + + Parameters + ---------- + x : array_like + A point or a set of points at which to evaluate the derivatives. + Note that ``t(k) <= x <= t(n-k+1)`` must hold for each `x`. + tck : tuple + A tuple (t,c,k) containing the vector of knots, + the B-spline coefficients, and the degree of the spline. + + Returns + ------- + results : {ndarray, list of ndarrays} + An array (or a list of arrays) containing all derivatives + up to order k inclusive for each point `x`. + + See Also + -------- + splprep, splrep, splint, sproot, splev, bisplrep, bisplev, + UnivariateSpline, BivariateSpline + + References + ---------- + .. [1] de Boor C : On calculating with b-splines, J. Approximation Theory + 6 (1972) 50-62. + .. [2] Cox M.G. : The numerical evaluation of b-splines, J. Inst. Maths + applics 10 (1972) 134-149. + .. [3] Dierckx P. : Curve and surface fitting with splines, Monographs on + Numerical Analysis, Oxford University Press, 1993. + + """ + if isinstance(tck, BSpline): + raise TypeError("spalde does not accept BSpline instances.") + else: + return _impl.spalde(x, tck) + + +def insert(x, tck, m=1, per=0): + """ + Insert knots into a B-spline. + + Given the knots and coefficients of a B-spline representation, create a + new B-spline with a knot inserted `m` times at point `x`. + This is a wrapper around the FORTRAN routine insert of FITPACK. + + Parameters + ---------- + x (u) : float + A knot value at which to insert a new knot. If `tck` was returned + from ``splprep``, then the parameter values, u should be given. + tck : a `BSpline` instance or a tuple + If tuple, then it is expected to be a tuple (t,c,k) containing + the vector of knots, the B-spline coefficients, and the degree of + the spline. + m : int, optional + The number of times to insert the given knot (its multiplicity). + Default is 1. + per : int, optional + If non-zero, the input spline is considered periodic. + + Returns + ------- + BSpline instance or a tuple + A new B-spline with knots t, coefficients c, and degree k. + ``t(k+1) <= x <= t(n-k)``, where k is the degree of the spline. + In case of a periodic spline (``per != 0``) there must be + either at least k interior knots t(j) satisfying ``t(k+1)>> from scipy.interpolate import splrep, insert + >>> import numpy as np + >>> x = np.linspace(0, 10, 5) + >>> y = np.sin(x) + >>> tck = splrep(x, y) + >>> tck[0] + array([ 0., 0., 0., 0., 5., 10., 10., 10., 10.]) + + A knot is inserted: + + >>> tck_inserted = insert(3, tck) + >>> tck_inserted[0] + array([ 0., 0., 0., 0., 3., 5., 10., 10., 10., 10.]) + + Some knots are inserted: + + >>> tck_inserted2 = insert(8, tck, m=3) + >>> tck_inserted2[0] + array([ 0., 0., 0., 0., 5., 8., 8., 8., 10., 10., 10., 10.]) + + """ + if isinstance(tck, BSpline): + + t, c, k = tck.tck + + # FITPACK expects the interpolation axis to be last, so roll it over + # NB: if c array is 1D, transposes are no-ops + sh = tuple(range(c.ndim)) + c = c.transpose(sh[1:] + (0,)) + t_, c_, k_ = _impl.insert(x, (t, c, k), m, per) + + # and roll the last axis back + c_ = np.asarray(c_) + c_ = c_.transpose((sh[-1],) + sh[:-1]) + return BSpline(t_, c_, k_) + else: + return _impl.insert(x, tck, m, per) + + +def splder(tck, n=1): + """ + Compute the spline representation of the derivative of a given spline + + Parameters + ---------- + tck : BSpline instance or a tuple of (t, c, k) + Spline whose derivative to compute + n : int, optional + Order of derivative to evaluate. Default: 1 + + Returns + ------- + `BSpline` instance or tuple + Spline of order k2=k-n representing the derivative + of the input spline. + A tuple is returned iff the input argument `tck` is a tuple, otherwise + a BSpline object is constructed and returned. + + See Also + -------- + splantider, splev, spalde + BSpline + + Notes + ----- + + .. versionadded:: 0.13.0 + + Examples + -------- + This can be used for finding maxima of a curve: + + >>> from scipy.interpolate import splrep, splder, sproot + >>> import numpy as np + >>> x = np.linspace(0, 10, 70) + >>> y = np.sin(x) + >>> spl = splrep(x, y, k=4) + + Now, differentiate the spline and find the zeros of the + derivative. (NB: `sproot` only works for order 3 splines, so we + fit an order 4 spline): + + >>> dspl = splder(spl) + >>> sproot(dspl) / np.pi + array([ 0.50000001, 1.5 , 2.49999998]) + + This agrees well with roots :math:`\\pi/2 + n\\pi` of + :math:`\\cos(x) = \\sin'(x)`. + + """ + if isinstance(tck, BSpline): + return tck.derivative(n) + else: + return _impl.splder(tck, n) + + +def splantider(tck, n=1): + """ + Compute the spline for the antiderivative (integral) of a given spline. + + Parameters + ---------- + tck : BSpline instance or a tuple of (t, c, k) + Spline whose antiderivative to compute + n : int, optional + Order of antiderivative to evaluate. Default: 1 + + Returns + ------- + BSpline instance or a tuple of (t2, c2, k2) + Spline of order k2=k+n representing the antiderivative of the input + spline. + A tuple is returned iff the input argument `tck` is a tuple, otherwise + a BSpline object is constructed and returned. + + See Also + -------- + splder, splev, spalde + BSpline + + Notes + ----- + The `splder` function is the inverse operation of this function. + Namely, ``splder(splantider(tck))`` is identical to `tck`, modulo + rounding error. + + .. versionadded:: 0.13.0 + + Examples + -------- + >>> from scipy.interpolate import splrep, splder, splantider, splev + >>> import numpy as np + >>> x = np.linspace(0, np.pi/2, 70) + >>> y = 1 / np.sqrt(1 - 0.8*np.sin(x)**2) + >>> spl = splrep(x, y) + + The derivative is the inverse operation of the antiderivative, + although some floating point error accumulates: + + >>> splev(1.7, spl), splev(1.7, splder(splantider(spl))) + (array(2.1565429877197317), array(2.1565429877201865)) + + Antiderivative can be used to evaluate definite integrals: + + >>> ispl = splantider(spl) + >>> splev(np.pi/2, ispl) - splev(0, ispl) + 2.2572053588768486 + + This is indeed an approximation to the complete elliptic integral + :math:`K(m) = \\int_0^{\\pi/2} [1 - m\\sin^2 x]^{-1/2} dx`: + + >>> from scipy.special import ellipk + >>> ellipk(0.8) + 2.2572053268208538 + + """ + if isinstance(tck, BSpline): + return tck.antiderivative(n) + else: + return _impl.splantider(tck, n) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_interpolate.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_interpolate.py new file mode 100644 index 0000000000000000000000000000000000000000..9d9d460f52e7541721a61967750ca20896a5bfc8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_interpolate.py @@ -0,0 +1,2473 @@ +__all__ = ['interp1d', 'interp2d', 'lagrange', 'PPoly', 'BPoly', 'NdPPoly'] + +from math import prod +import warnings + +import numpy as np +from numpy import (array, transpose, searchsorted, atleast_1d, atleast_2d, + ravel, poly1d, asarray, intp) + +import scipy.special as spec +from scipy._lib._util import copy_if_needed +from scipy.special import comb + +from . import _fitpack_py +from . import dfitpack +from ._polyint import _Interpolator1D +from . import _ppoly +from .interpnd import _ndim_coords_from_arrays +from ._bsplines import make_interp_spline, BSpline + + +def lagrange(x, w): + r""" + Return a Lagrange interpolating polynomial. + + Given two 1-D arrays `x` and `w,` returns the Lagrange interpolating + polynomial through the points ``(x, w)``. + + Warning: This implementation is numerically unstable. Do not expect to + be able to use more than about 20 points even if they are chosen optimally. + + Parameters + ---------- + x : array_like + `x` represents the x-coordinates of a set of datapoints. + w : array_like + `w` represents the y-coordinates of a set of datapoints, i.e., f(`x`). + + Returns + ------- + lagrange : `numpy.poly1d` instance + The Lagrange interpolating polynomial. + + Examples + -------- + Interpolate :math:`f(x) = x^3` by 3 points. + + >>> import numpy as np + >>> from scipy.interpolate import lagrange + >>> x = np.array([0, 1, 2]) + >>> y = x**3 + >>> poly = lagrange(x, y) + + Since there are only 3 points, Lagrange polynomial has degree 2. Explicitly, + it is given by + + .. math:: + + \begin{aligned} + L(x) &= 1\times \frac{x (x - 2)}{-1} + 8\times \frac{x (x-1)}{2} \\ + &= x (-2 + 3x) + \end{aligned} + + >>> from numpy.polynomial.polynomial import Polynomial + >>> Polynomial(poly.coef[::-1]).coef + array([ 0., -2., 3.]) + + >>> import matplotlib.pyplot as plt + >>> x_new = np.arange(0, 2.1, 0.1) + >>> plt.scatter(x, y, label='data') + >>> plt.plot(x_new, Polynomial(poly.coef[::-1])(x_new), label='Polynomial') + >>> plt.plot(x_new, 3*x_new**2 - 2*x_new + 0*x_new, + ... label=r"$3 x^2 - 2 x$", linestyle='-.') + >>> plt.legend() + >>> plt.show() + + """ + + M = len(x) + p = poly1d(0.0) + for j in range(M): + pt = poly1d(w[j]) + for k in range(M): + if k == j: + continue + fac = x[j]-x[k] + pt *= poly1d([1.0, -x[k]])/fac + p += pt + return p + + +# !! Need to find argument for keeping initialize. If it isn't +# !! found, get rid of it! + + +dep_mesg = """\ +`interp2d` is deprecated in SciPy 1.10 and will be removed in SciPy 1.14.0. + +For legacy code, nearly bug-for-bug compatible replacements are +`RectBivariateSpline` on regular grids, and `bisplrep`/`bisplev` for +scattered 2D data. + +In new code, for regular grids use `RegularGridInterpolator` instead. +For scattered data, prefer `LinearNDInterpolator` or +`CloughTocher2DInterpolator`. + +For more details see +`https://scipy.github.io/devdocs/notebooks/interp_transition_guide.html` +""" + +class interp2d: + """ + interp2d(x, y, z, kind='linear', copy=True, bounds_error=False, + fill_value=None) + + .. deprecated:: 1.10.0 + + `interp2d` is deprecated in SciPy 1.10 and will be removed in SciPy + 1.14.0. + + For legacy code, nearly bug-for-bug compatible replacements are + `RectBivariateSpline` on regular grids, and `bisplrep`/`bisplev` for + scattered 2D data. + + In new code, for regular grids use `RegularGridInterpolator` instead. + For scattered data, prefer `LinearNDInterpolator` or + `CloughTocher2DInterpolator`. + + For more details see + `https://scipy.github.io/devdocs/notebooks/interp_transition_guide.html + `_ + + + Interpolate over a 2-D grid. + + `x`, `y` and `z` are arrays of values used to approximate some function + f: ``z = f(x, y)`` which returns a scalar value `z`. This class returns a + function whose call method uses spline interpolation to find the value + of new points. + + If `x` and `y` represent a regular grid, consider using + `RectBivariateSpline`. + + If `z` is a vector value, consider using `interpn`. + + Note that calling `interp2d` with NaNs present in input values, or with + decreasing values in `x` an `y` results in undefined behaviour. + + Methods + ------- + __call__ + + Parameters + ---------- + x, y : array_like + Arrays defining the data point coordinates. + The data point coordinates need to be sorted by increasing order. + + If the points lie on a regular grid, `x` can specify the column + coordinates and `y` the row coordinates, for example:: + + >>> x = [0,1,2]; y = [0,3]; z = [[1,2,3], [4,5,6]] + + Otherwise, `x` and `y` must specify the full coordinates for each + point, for example:: + + >>> x = [0,1,2,0,1,2]; y = [0,0,0,3,3,3]; z = [1,4,2,5,3,6] + + If `x` and `y` are multidimensional, they are flattened before use. + z : array_like + The values of the function to interpolate at the data points. If + `z` is a multidimensional array, it is flattened before use assuming + Fortran-ordering (order='F'). The length of a flattened `z` array + is either len(`x`)*len(`y`) if `x` and `y` specify the column and + row coordinates or ``len(z) == len(x) == len(y)`` if `x` and `y` + specify coordinates for each point. + kind : {'linear', 'cubic', 'quintic'}, optional + The kind of spline interpolation to use. Default is 'linear'. + copy : bool, optional + If True, the class makes internal copies of x, y and z. + If False, references may be used. The default is to copy. + bounds_error : bool, optional + If True, when interpolated values are requested outside of the + domain of the input data (x,y), a ValueError is raised. + If False, then `fill_value` is used. + fill_value : number, optional + If provided, the value to use for points outside of the + interpolation domain. If omitted (None), values outside + the domain are extrapolated via nearest-neighbor extrapolation. + + See Also + -------- + RectBivariateSpline : + Much faster 2-D interpolation if your input data is on a grid + bisplrep, bisplev : + Spline interpolation based on FITPACK + BivariateSpline : a more recent wrapper of the FITPACK routines + interp1d : 1-D version of this function + RegularGridInterpolator : interpolation on a regular or rectilinear grid + in arbitrary dimensions. + interpn : Multidimensional interpolation on regular grids (wraps + `RegularGridInterpolator` and `RectBivariateSpline`). + + Notes + ----- + The minimum number of data points required along the interpolation + axis is ``(k+1)**2``, with k=1 for linear, k=3 for cubic and k=5 for + quintic interpolation. + + The interpolator is constructed by `bisplrep`, with a smoothing factor + of 0. If more control over smoothing is needed, `bisplrep` should be + used directly. + + The coordinates of the data points to interpolate `xnew` and `ynew` + have to be sorted by ascending order. + `interp2d` is legacy and is not + recommended for use in new code. New code should use + `RegularGridInterpolator` instead. + + Examples + -------- + Construct a 2-D grid and interpolate on it: + + >>> import numpy as np + >>> from scipy import interpolate + >>> x = np.arange(-5.01, 5.01, 0.25) + >>> y = np.arange(-5.01, 5.01, 0.25) + >>> xx, yy = np.meshgrid(x, y) + >>> z = np.sin(xx**2+yy**2) + >>> f = interpolate.interp2d(x, y, z, kind='cubic') + + Now use the obtained interpolation function and plot the result: + + >>> import matplotlib.pyplot as plt + >>> xnew = np.arange(-5.01, 5.01, 1e-2) + >>> ynew = np.arange(-5.01, 5.01, 1e-2) + >>> znew = f(xnew, ynew) + >>> plt.plot(x, z[0, :], 'ro-', xnew, znew[0, :], 'b-') + >>> plt.show() + """ + + def __init__(self, x, y, z, kind='linear', copy=True, bounds_error=False, + fill_value=None): + warnings.warn(dep_mesg, DeprecationWarning, stacklevel=2) + + x = ravel(x) + y = ravel(y) + z = asarray(z) + + rectangular_grid = (z.size == len(x) * len(y)) + if rectangular_grid: + if z.ndim == 2: + if z.shape != (len(y), len(x)): + raise ValueError("When on a regular grid with x.size = m " + "and y.size = n, if z.ndim == 2, then z " + "must have shape (n, m)") + if not np.all(x[1:] >= x[:-1]): + j = np.argsort(x) + x = x[j] + z = z[:, j] + if not np.all(y[1:] >= y[:-1]): + j = np.argsort(y) + y = y[j] + z = z[j, :] + z = ravel(z.T) + else: + z = ravel(z) + if len(x) != len(y): + raise ValueError( + "x and y must have equal lengths for non rectangular grid") + if len(z) != len(x): + raise ValueError( + "Invalid length for input z for non rectangular grid") + + interpolation_types = {'linear': 1, 'cubic': 3, 'quintic': 5} + try: + kx = ky = interpolation_types[kind] + except KeyError as e: + raise ValueError( + f"Unsupported interpolation type {repr(kind)}, must be " + f"either of {', '.join(map(repr, interpolation_types))}." + ) from e + + if not rectangular_grid: + # TODO: surfit is really not meant for interpolation! + self.tck = _fitpack_py.bisplrep(x, y, z, kx=kx, ky=ky, s=0.0) + else: + nx, tx, ny, ty, c, fp, ier = dfitpack.regrid_smth( + x, y, z, None, None, None, None, + kx=kx, ky=ky, s=0.0) + self.tck = (tx[:nx], ty[:ny], c[:(nx - kx - 1) * (ny - ky - 1)], + kx, ky) + + self.bounds_error = bounds_error + self.fill_value = fill_value + self.x, self.y, self.z = (array(a, copy=copy) for a in (x, y, z)) + + self.x_min, self.x_max = np.amin(x), np.amax(x) + self.y_min, self.y_max = np.amin(y), np.amax(y) + + def __call__(self, x, y, dx=0, dy=0, assume_sorted=False): + """Interpolate the function. + + Parameters + ---------- + x : 1-D array + x-coordinates of the mesh on which to interpolate. + y : 1-D array + y-coordinates of the mesh on which to interpolate. + dx : int >= 0, < kx + Order of partial derivatives in x. + dy : int >= 0, < ky + Order of partial derivatives in y. + assume_sorted : bool, optional + If False, values of `x` and `y` can be in any order and they are + sorted first. + If True, `x` and `y` have to be arrays of monotonically + increasing values. + + Returns + ------- + z : 2-D array with shape (len(y), len(x)) + The interpolated values. + """ + warnings.warn(dep_mesg, DeprecationWarning, stacklevel=2) + + x = atleast_1d(x) + y = atleast_1d(y) + + if x.ndim != 1 or y.ndim != 1: + raise ValueError("x and y should both be 1-D arrays") + + if not assume_sorted: + x = np.sort(x, kind="mergesort") + y = np.sort(y, kind="mergesort") + + if self.bounds_error or self.fill_value is not None: + out_of_bounds_x = (x < self.x_min) | (x > self.x_max) + out_of_bounds_y = (y < self.y_min) | (y > self.y_max) + + any_out_of_bounds_x = np.any(out_of_bounds_x) + any_out_of_bounds_y = np.any(out_of_bounds_y) + + if self.bounds_error and (any_out_of_bounds_x or any_out_of_bounds_y): + raise ValueError( + f"Values out of range; x must be in {(self.x_min, self.x_max)!r}, " + f"y in {(self.y_min, self.y_max)!r}" + ) + + z = _fitpack_py.bisplev(x, y, self.tck, dx, dy) + z = atleast_2d(z) + z = transpose(z) + + if self.fill_value is not None: + if any_out_of_bounds_x: + z[:, out_of_bounds_x] = self.fill_value + if any_out_of_bounds_y: + z[out_of_bounds_y, :] = self.fill_value + + if len(z) == 1: + z = z[0] + return array(z) + + +def _check_broadcast_up_to(arr_from, shape_to, name): + """Helper to check that arr_from broadcasts up to shape_to""" + shape_from = arr_from.shape + if len(shape_to) >= len(shape_from): + for t, f in zip(shape_to[::-1], shape_from[::-1]): + if f != 1 and f != t: + break + else: # all checks pass, do the upcasting that we need later + if arr_from.size != 1 and arr_from.shape != shape_to: + arr_from = np.ones(shape_to, arr_from.dtype) * arr_from + return arr_from.ravel() + # at least one check failed + raise ValueError(f'{name} argument must be able to broadcast up ' + f'to shape {shape_to} but had shape {shape_from}') + + +def _do_extrapolate(fill_value): + """Helper to check if fill_value == "extrapolate" without warnings""" + return (isinstance(fill_value, str) and + fill_value == 'extrapolate') + + +class interp1d(_Interpolator1D): + """ + Interpolate a 1-D function. + + .. legacy:: class + + For a guide to the intended replacements for `interp1d` see + :ref:`tutorial-interpolate_1Dsection`. + + `x` and `y` are arrays of values used to approximate some function f: + ``y = f(x)``. This class returns a function whose call method uses + interpolation to find the value of new points. + + Parameters + ---------- + x : (npoints, ) array_like + A 1-D array of real values. + y : (..., npoints, ...) array_like + A N-D array of real values. The length of `y` along the interpolation + axis must be equal to the length of `x`. Use the ``axis`` parameter + to select correct axis. Unlike other interpolators, the default + interpolation axis is the last axis of `y`. + kind : str or int, optional + Specifies the kind of interpolation as a string or as an integer + specifying the order of the spline interpolator to use. + The string has to be one of 'linear', 'nearest', 'nearest-up', 'zero', + 'slinear', 'quadratic', 'cubic', 'previous', or 'next'. 'zero', + 'slinear', 'quadratic' and 'cubic' refer to a spline interpolation of + zeroth, first, second or third order; 'previous' and 'next' simply + return the previous or next value of the point; 'nearest-up' and + 'nearest' differ when interpolating half-integers (e.g. 0.5, 1.5) + in that 'nearest-up' rounds up and 'nearest' rounds down. Default + is 'linear'. + axis : int, optional + Axis in the ``y`` array corresponding to the x-coordinate values. Unlike + other interpolators, defaults to ``axis=-1``. + copy : bool, optional + If ``True``, the class makes internal copies of x and y. If ``False``, + references to ``x`` and ``y`` are used if possible. The default is to copy. + bounds_error : bool, optional + If True, a ValueError is raised any time interpolation is attempted on + a value outside of the range of x (where extrapolation is + necessary). If False, out of bounds values are assigned `fill_value`. + By default, an error is raised unless ``fill_value="extrapolate"``. + fill_value : array-like or (array-like, array_like) or "extrapolate", optional + - if a ndarray (or float), this value will be used to fill in for + requested points outside of the data range. If not provided, then + the default is NaN. The array-like must broadcast properly to the + dimensions of the non-interpolation axes. + - If a two-element tuple, then the first element is used as a + fill value for ``x_new < x[0]`` and the second element is used for + ``x_new > x[-1]``. Anything that is not a 2-element tuple (e.g., + list or ndarray, regardless of shape) is taken to be a single + array-like argument meant to be used for both bounds as + ``below, above = fill_value, fill_value``. Using a two-element tuple + or ndarray requires ``bounds_error=False``. + + .. versionadded:: 0.17.0 + - If "extrapolate", then points outside the data range will be + extrapolated. + + .. versionadded:: 0.17.0 + assume_sorted : bool, optional + If False, values of `x` can be in any order and they are sorted first. + If True, `x` has to be an array of monotonically increasing values. + + Attributes + ---------- + fill_value + + Methods + ------- + __call__ + + See Also + -------- + splrep, splev + Spline interpolation/smoothing based on FITPACK. + UnivariateSpline : An object-oriented wrapper of the FITPACK routines. + interp2d : 2-D interpolation + + Notes + ----- + Calling `interp1d` with NaNs present in input values results in + undefined behaviour. + + Input values `x` and `y` must be convertible to `float` values like + `int` or `float`. + + If the values in `x` are not unique, the resulting behavior is + undefined and specific to the choice of `kind`, i.e., changing + `kind` will change the behavior for duplicates. + + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy import interpolate + >>> x = np.arange(0, 10) + >>> y = np.exp(-x/3.0) + >>> f = interpolate.interp1d(x, y) + + >>> xnew = np.arange(0, 9, 0.1) + >>> ynew = f(xnew) # use interpolation function returned by `interp1d` + >>> plt.plot(x, y, 'o', xnew, ynew, '-') + >>> plt.show() + """ + + def __init__(self, x, y, kind='linear', axis=-1, + copy=True, bounds_error=None, fill_value=np.nan, + assume_sorted=False): + """ Initialize a 1-D linear interpolation class.""" + _Interpolator1D.__init__(self, x, y, axis=axis) + + self.bounds_error = bounds_error # used by fill_value setter + + # `copy` keyword semantics changed in NumPy 2.0, once that is + # the minimum version this can use `copy=None`. + self.copy = copy + if not copy: + self.copy = copy_if_needed + + if kind in ['zero', 'slinear', 'quadratic', 'cubic']: + order = {'zero': 0, 'slinear': 1, + 'quadratic': 2, 'cubic': 3}[kind] + kind = 'spline' + elif isinstance(kind, int): + order = kind + kind = 'spline' + elif kind not in ('linear', 'nearest', 'nearest-up', 'previous', + 'next'): + raise NotImplementedError("%s is unsupported: Use fitpack " + "routines for other types." % kind) + x = array(x, copy=self.copy) + y = array(y, copy=self.copy) + + if not assume_sorted: + ind = np.argsort(x, kind="mergesort") + x = x[ind] + y = np.take(y, ind, axis=axis) + + if x.ndim != 1: + raise ValueError("the x array must have exactly one dimension.") + if y.ndim == 0: + raise ValueError("the y array must have at least one dimension.") + + # Force-cast y to a floating-point type, if it's not yet one + if not issubclass(y.dtype.type, np.inexact): + y = y.astype(np.float64) + + # Backward compatibility + self.axis = axis % y.ndim + + # Interpolation goes internally along the first axis + self.y = y + self._y = self._reshape_yi(self.y) + self.x = x + del y, x # clean up namespace to prevent misuse; use attributes + self._kind = kind + + # Adjust to interpolation kind; store reference to *unbound* + # interpolation methods, in order to avoid circular references to self + # stored in the bound instance methods, and therefore delayed garbage + # collection. See: https://docs.python.org/reference/datamodel.html + if kind in ('linear', 'nearest', 'nearest-up', 'previous', 'next'): + # Make a "view" of the y array that is rotated to the interpolation + # axis. + minval = 1 + if kind == 'nearest': + # Do division before addition to prevent possible integer + # overflow + self._side = 'left' + self.x_bds = self.x / 2.0 + self.x_bds = self.x_bds[1:] + self.x_bds[:-1] + + self._call = self.__class__._call_nearest + elif kind == 'nearest-up': + # Do division before addition to prevent possible integer + # overflow + self._side = 'right' + self.x_bds = self.x / 2.0 + self.x_bds = self.x_bds[1:] + self.x_bds[:-1] + + self._call = self.__class__._call_nearest + elif kind == 'previous': + # Side for np.searchsorted and index for clipping + self._side = 'left' + self._ind = 0 + # Move x by one floating point value to the left + self._x_shift = np.nextafter(self.x, -np.inf) + self._call = self.__class__._call_previousnext + if _do_extrapolate(fill_value): + self._check_and_update_bounds_error_for_extrapolation() + # assume y is sorted by x ascending order here. + fill_value = (np.nan, np.take(self.y, -1, axis)) + elif kind == 'next': + self._side = 'right' + self._ind = 1 + # Move x by one floating point value to the right + self._x_shift = np.nextafter(self.x, np.inf) + self._call = self.__class__._call_previousnext + if _do_extrapolate(fill_value): + self._check_and_update_bounds_error_for_extrapolation() + # assume y is sorted by x ascending order here. + fill_value = (np.take(self.y, 0, axis), np.nan) + else: + # Check if we can delegate to numpy.interp (2x-10x faster). + np_dtypes = (np.dtype(np.float64), np.dtype(int)) + cond = self.x.dtype in np_dtypes and self.y.dtype in np_dtypes + cond = cond and self.y.ndim == 1 + cond = cond and not _do_extrapolate(fill_value) + + if cond: + self._call = self.__class__._call_linear_np + else: + self._call = self.__class__._call_linear + else: + minval = order + 1 + + rewrite_nan = False + xx, yy = self.x, self._y + if order > 1: + # Quadratic or cubic spline. If input contains even a single + # nan, then the output is all nans. We cannot just feed data + # with nans to make_interp_spline because it calls LAPACK. + # So, we make up a bogus x and y with no nans and use it + # to get the correct shape of the output, which we then fill + # with nans. + # For slinear or zero order spline, we just pass nans through. + mask = np.isnan(self.x) + if mask.any(): + sx = self.x[~mask] + if sx.size == 0: + raise ValueError("`x` array is all-nan") + xx = np.linspace(np.nanmin(self.x), + np.nanmax(self.x), + len(self.x)) + rewrite_nan = True + if np.isnan(self._y).any(): + yy = np.ones_like(self._y) + rewrite_nan = True + + self._spline = make_interp_spline(xx, yy, k=order, + check_finite=False) + if rewrite_nan: + self._call = self.__class__._call_nan_spline + else: + self._call = self.__class__._call_spline + + if len(self.x) < minval: + raise ValueError("x and y arrays must have at " + "least %d entries" % minval) + + self.fill_value = fill_value # calls the setter, can modify bounds_err + + @property + def fill_value(self): + """The fill value.""" + # backwards compat: mimic a public attribute + return self._fill_value_orig + + @fill_value.setter + def fill_value(self, fill_value): + # extrapolation only works for nearest neighbor and linear methods + if _do_extrapolate(fill_value): + self._check_and_update_bounds_error_for_extrapolation() + self._extrapolate = True + else: + broadcast_shape = (self.y.shape[:self.axis] + + self.y.shape[self.axis + 1:]) + if len(broadcast_shape) == 0: + broadcast_shape = (1,) + # it's either a pair (_below_range, _above_range) or a single value + # for both above and below range + if isinstance(fill_value, tuple) and len(fill_value) == 2: + below_above = [np.asarray(fill_value[0]), + np.asarray(fill_value[1])] + names = ('fill_value (below)', 'fill_value (above)') + for ii in range(2): + below_above[ii] = _check_broadcast_up_to( + below_above[ii], broadcast_shape, names[ii]) + else: + fill_value = np.asarray(fill_value) + below_above = [_check_broadcast_up_to( + fill_value, broadcast_shape, 'fill_value')] * 2 + self._fill_value_below, self._fill_value_above = below_above + self._extrapolate = False + if self.bounds_error is None: + self.bounds_error = True + # backwards compat: fill_value was a public attr; make it writeable + self._fill_value_orig = fill_value + + def _check_and_update_bounds_error_for_extrapolation(self): + if self.bounds_error: + raise ValueError("Cannot extrapolate and raise " + "at the same time.") + self.bounds_error = False + + def _call_linear_np(self, x_new): + # Note that out-of-bounds values are taken care of in self._evaluate + return np.interp(x_new, self.x, self.y) + + def _call_linear(self, x_new): + # 2. Find where in the original data, the values to interpolate + # would be inserted. + # Note: If x_new[n] == x[m], then m is returned by searchsorted. + x_new_indices = searchsorted(self.x, x_new) + + # 3. Clip x_new_indices so that they are within the range of + # self.x indices and at least 1. Removes mis-interpolation + # of x_new[n] = x[0] + x_new_indices = x_new_indices.clip(1, len(self.x)-1).astype(int) + + # 4. Calculate the slope of regions that each x_new value falls in. + lo = x_new_indices - 1 + hi = x_new_indices + + x_lo = self.x[lo] + x_hi = self.x[hi] + y_lo = self._y[lo] + y_hi = self._y[hi] + + # Note that the following two expressions rely on the specifics of the + # broadcasting semantics. + slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None] + + # 5. Calculate the actual value for each entry in x_new. + y_new = slope*(x_new - x_lo)[:, None] + y_lo + + return y_new + + def _call_nearest(self, x_new): + """ Find nearest neighbor interpolated y_new = f(x_new).""" + + # 2. Find where in the averaged data the values to interpolate + # would be inserted. + # Note: use side='left' (right) to searchsorted() to define the + # halfway point to be nearest to the left (right) neighbor + x_new_indices = searchsorted(self.x_bds, x_new, side=self._side) + + # 3. Clip x_new_indices so that they are within the range of x indices. + x_new_indices = x_new_indices.clip(0, len(self.x)-1).astype(intp) + + # 4. Calculate the actual value for each entry in x_new. + y_new = self._y[x_new_indices] + + return y_new + + def _call_previousnext(self, x_new): + """Use previous/next neighbor of x_new, y_new = f(x_new).""" + + # 1. Get index of left/right value + x_new_indices = searchsorted(self._x_shift, x_new, side=self._side) + + # 2. Clip x_new_indices so that they are within the range of x indices. + x_new_indices = x_new_indices.clip(1-self._ind, + len(self.x)-self._ind).astype(intp) + + # 3. Calculate the actual value for each entry in x_new. + y_new = self._y[x_new_indices+self._ind-1] + + return y_new + + def _call_spline(self, x_new): + return self._spline(x_new) + + def _call_nan_spline(self, x_new): + out = self._spline(x_new) + out[...] = np.nan + return out + + def _evaluate(self, x_new): + # 1. Handle values in x_new that are outside of x. Throw error, + # or return a list of mask array indicating the outofbounds values. + # The behavior is set by the bounds_error variable. + x_new = asarray(x_new) + y_new = self._call(self, x_new) + if not self._extrapolate: + below_bounds, above_bounds = self._check_bounds(x_new) + if len(y_new) > 0: + # Note fill_value must be broadcast up to the proper size + # and flattened to work here + y_new[below_bounds] = self._fill_value_below + y_new[above_bounds] = self._fill_value_above + return y_new + + def _check_bounds(self, x_new): + """Check the inputs for being in the bounds of the interpolated data. + + Parameters + ---------- + x_new : array + + Returns + ------- + out_of_bounds : bool array + The mask on x_new of values that are out of the bounds. + """ + + # If self.bounds_error is True, we raise an error if any x_new values + # fall outside the range of x. Otherwise, we return an array indicating + # which values are outside the boundary region. + below_bounds = x_new < self.x[0] + above_bounds = x_new > self.x[-1] + + if self.bounds_error and below_bounds.any(): + below_bounds_value = x_new[np.argmax(below_bounds)] + raise ValueError(f"A value ({below_bounds_value}) in x_new is below " + f"the interpolation range's minimum value ({self.x[0]}).") + if self.bounds_error and above_bounds.any(): + above_bounds_value = x_new[np.argmax(above_bounds)] + raise ValueError(f"A value ({above_bounds_value}) in x_new is above " + f"the interpolation range's maximum value ({self.x[-1]}).") + + # !! Should we emit a warning if some values are out of bounds? + # !! matlab does not. + return below_bounds, above_bounds + + +class _PPolyBase: + """Base class for piecewise polynomials.""" + __slots__ = ('c', 'x', 'extrapolate', 'axis') + + def __init__(self, c, x, extrapolate=None, axis=0): + self.c = np.asarray(c) + self.x = np.ascontiguousarray(x, dtype=np.float64) + + if extrapolate is None: + extrapolate = True + elif extrapolate != 'periodic': + extrapolate = bool(extrapolate) + self.extrapolate = extrapolate + + if self.c.ndim < 2: + raise ValueError("Coefficients array must be at least " + "2-dimensional.") + + if not (0 <= axis < self.c.ndim - 1): + raise ValueError(f"axis={axis} must be between 0 and {self.c.ndim-1}") + + self.axis = axis + if axis != 0: + # move the interpolation axis to be the first one in self.c + # More specifically, the target shape for self.c is (k, m, ...), + # and axis !=0 means that we have c.shape (..., k, m, ...) + # ^ + # axis + # So we roll two of them. + self.c = np.moveaxis(self.c, axis+1, 0) + self.c = np.moveaxis(self.c, axis+1, 0) + + if self.x.ndim != 1: + raise ValueError("x must be 1-dimensional") + if self.x.size < 2: + raise ValueError("at least 2 breakpoints are needed") + if self.c.ndim < 2: + raise ValueError("c must have at least 2 dimensions") + if self.c.shape[0] == 0: + raise ValueError("polynomial must be at least of order 0") + if self.c.shape[1] != self.x.size-1: + raise ValueError("number of coefficients != len(x)-1") + dx = np.diff(self.x) + if not (np.all(dx >= 0) or np.all(dx <= 0)): + raise ValueError("`x` must be strictly increasing or decreasing.") + + dtype = self._get_dtype(self.c.dtype) + self.c = np.ascontiguousarray(self.c, dtype=dtype) + + def _get_dtype(self, dtype): + if np.issubdtype(dtype, np.complexfloating) \ + or np.issubdtype(self.c.dtype, np.complexfloating): + return np.complex128 + else: + return np.float64 + + @classmethod + def construct_fast(cls, c, x, extrapolate=None, axis=0): + """ + Construct the piecewise polynomial without making checks. + + Takes the same parameters as the constructor. Input arguments + ``c`` and ``x`` must be arrays of the correct shape and type. The + ``c`` array can only be of dtypes float and complex, and ``x`` + array must have dtype float. + """ + self = object.__new__(cls) + self.c = c + self.x = x + self.axis = axis + if extrapolate is None: + extrapolate = True + self.extrapolate = extrapolate + return self + + def _ensure_c_contiguous(self): + """ + c and x may be modified by the user. The Cython code expects + that they are C contiguous. + """ + if not self.x.flags.c_contiguous: + self.x = self.x.copy() + if not self.c.flags.c_contiguous: + self.c = self.c.copy() + + def extend(self, c, x): + """ + Add additional breakpoints and coefficients to the polynomial. + + Parameters + ---------- + c : ndarray, size (k, m, ...) + Additional coefficients for polynomials in intervals. Note that + the first additional interval will be formed using one of the + ``self.x`` end points. + x : ndarray, size (m,) + Additional breakpoints. Must be sorted in the same order as + ``self.x`` and either to the right or to the left of the current + breakpoints. + """ + + c = np.asarray(c) + x = np.asarray(x) + + if c.ndim < 2: + raise ValueError("invalid dimensions for c") + if x.ndim != 1: + raise ValueError("invalid dimensions for x") + if x.shape[0] != c.shape[1]: + raise ValueError(f"Shapes of x {x.shape} and c {c.shape} are incompatible") + if c.shape[2:] != self.c.shape[2:] or c.ndim != self.c.ndim: + raise ValueError("Shapes of c {} and self.c {} are incompatible" + .format(c.shape, self.c.shape)) + + if c.size == 0: + return + + dx = np.diff(x) + if not (np.all(dx >= 0) or np.all(dx <= 0)): + raise ValueError("`x` is not sorted.") + + if self.x[-1] >= self.x[0]: + if not x[-1] >= x[0]: + raise ValueError("`x` is in the different order " + "than `self.x`.") + + if x[0] >= self.x[-1]: + action = 'append' + elif x[-1] <= self.x[0]: + action = 'prepend' + else: + raise ValueError("`x` is neither on the left or on the right " + "from `self.x`.") + else: + if not x[-1] <= x[0]: + raise ValueError("`x` is in the different order " + "than `self.x`.") + + if x[0] <= self.x[-1]: + action = 'append' + elif x[-1] >= self.x[0]: + action = 'prepend' + else: + raise ValueError("`x` is neither on the left or on the right " + "from `self.x`.") + + dtype = self._get_dtype(c.dtype) + + k2 = max(c.shape[0], self.c.shape[0]) + c2 = np.zeros((k2, self.c.shape[1] + c.shape[1]) + self.c.shape[2:], + dtype=dtype) + + if action == 'append': + c2[k2-self.c.shape[0]:, :self.c.shape[1]] = self.c + c2[k2-c.shape[0]:, self.c.shape[1]:] = c + self.x = np.r_[self.x, x] + elif action == 'prepend': + c2[k2-self.c.shape[0]:, :c.shape[1]] = c + c2[k2-c.shape[0]:, c.shape[1]:] = self.c + self.x = np.r_[x, self.x] + + self.c = c2 + + def __call__(self, x, nu=0, extrapolate=None): + """ + Evaluate the piecewise polynomial or its derivative. + + Parameters + ---------- + x : array_like + Points to evaluate the interpolant at. + nu : int, optional + Order of derivative to evaluate. Must be non-negative. + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. + If None (default), use `self.extrapolate`. + + Returns + ------- + y : array_like + Interpolated values. Shape is determined by replacing + the interpolation axis in the original array with the shape of x. + + Notes + ----- + Derivatives are evaluated piecewise for each polynomial + segment, even if the polynomial is not differentiable at the + breakpoints. The polynomial intervals are considered half-open, + ``[a, b)``, except for the last interval which is closed + ``[a, b]``. + """ + if extrapolate is None: + extrapolate = self.extrapolate + x = np.asarray(x) + x_shape, x_ndim = x.shape, x.ndim + x = np.ascontiguousarray(x.ravel(), dtype=np.float64) + + # With periodic extrapolation we map x to the segment + # [self.x[0], self.x[-1]]. + if extrapolate == 'periodic': + x = self.x[0] + (x - self.x[0]) % (self.x[-1] - self.x[0]) + extrapolate = False + + out = np.empty((len(x), prod(self.c.shape[2:])), dtype=self.c.dtype) + self._ensure_c_contiguous() + self._evaluate(x, nu, extrapolate, out) + out = out.reshape(x_shape + self.c.shape[2:]) + if self.axis != 0: + # transpose to move the calculated values to the interpolation axis + l = list(range(out.ndim)) + l = l[x_ndim:x_ndim+self.axis] + l[:x_ndim] + l[x_ndim+self.axis:] + out = out.transpose(l) + return out + + +class PPoly(_PPolyBase): + """ + Piecewise polynomial in terms of coefficients and breakpoints + + The polynomial between ``x[i]`` and ``x[i + 1]`` is written in the + local power basis:: + + S = sum(c[m, i] * (xp - x[i])**(k-m) for m in range(k+1)) + + where ``k`` is the degree of the polynomial. + + Parameters + ---------- + c : ndarray, shape (k, m, ...) + Polynomial coefficients, order `k` and `m` intervals. + x : ndarray, shape (m+1,) + Polynomial breakpoints. Must be sorted in either increasing or + decreasing order. + extrapolate : bool or 'periodic', optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. If 'periodic', + periodic extrapolation is used. Default is True. + axis : int, optional + Interpolation axis. Default is zero. + + Attributes + ---------- + x : ndarray + Breakpoints. + c : ndarray + Coefficients of the polynomials. They are reshaped + to a 3-D array with the last dimension representing + the trailing dimensions of the original coefficient array. + axis : int + Interpolation axis. + + Methods + ------- + __call__ + derivative + antiderivative + integrate + solve + roots + extend + from_spline + from_bernstein_basis + construct_fast + + See also + -------- + BPoly : piecewise polynomials in the Bernstein basis + + Notes + ----- + High-order polynomials in the power basis can be numerically + unstable. Precision problems can start to appear for orders + larger than 20-30. + """ + + def _evaluate(self, x, nu, extrapolate, out): + _ppoly.evaluate(self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, x, nu, bool(extrapolate), out) + + def derivative(self, nu=1): + """ + Construct a new piecewise polynomial representing the derivative. + + Parameters + ---------- + nu : int, optional + Order of derivative to evaluate. Default is 1, i.e., compute the + first derivative. If negative, the antiderivative is returned. + + Returns + ------- + pp : PPoly + Piecewise polynomial of order k2 = k - n representing the derivative + of this polynomial. + + Notes + ----- + Derivatives are evaluated piecewise for each polynomial + segment, even if the polynomial is not differentiable at the + breakpoints. The polynomial intervals are considered half-open, + ``[a, b)``, except for the last interval which is closed + ``[a, b]``. + """ + if nu < 0: + return self.antiderivative(-nu) + + # reduce order + if nu == 0: + c2 = self.c.copy() + else: + c2 = self.c[:-nu, :].copy() + + if c2.shape[0] == 0: + # derivative of order 0 is zero + c2 = np.zeros((1,) + c2.shape[1:], dtype=c2.dtype) + + # multiply by the correct rising factorials + factor = spec.poch(np.arange(c2.shape[0], 0, -1), nu) + c2 *= factor[(slice(None),) + (None,)*(c2.ndim-1)] + + # construct a compatible polynomial + return self.construct_fast(c2, self.x, self.extrapolate, self.axis) + + def antiderivative(self, nu=1): + """ + Construct a new piecewise polynomial representing the antiderivative. + + Antiderivative is also the indefinite integral of the function, + and derivative is its inverse operation. + + Parameters + ---------- + nu : int, optional + Order of antiderivative to evaluate. Default is 1, i.e., compute + the first integral. If negative, the derivative is returned. + + Returns + ------- + pp : PPoly + Piecewise polynomial of order k2 = k + n representing + the antiderivative of this polynomial. + + Notes + ----- + The antiderivative returned by this function is continuous and + continuously differentiable to order n-1, up to floating point + rounding error. + + If antiderivative is computed and ``self.extrapolate='periodic'``, + it will be set to False for the returned instance. This is done because + the antiderivative is no longer periodic and its correct evaluation + outside of the initially given x interval is difficult. + """ + if nu <= 0: + return self.derivative(-nu) + + c = np.zeros((self.c.shape[0] + nu, self.c.shape[1]) + self.c.shape[2:], + dtype=self.c.dtype) + c[:-nu] = self.c + + # divide by the correct rising factorials + factor = spec.poch(np.arange(self.c.shape[0], 0, -1), nu) + c[:-nu] /= factor[(slice(None),) + (None,)*(c.ndim-1)] + + # fix continuity of added degrees of freedom + self._ensure_c_contiguous() + _ppoly.fix_continuity(c.reshape(c.shape[0], c.shape[1], -1), + self.x, nu - 1) + + if self.extrapolate == 'periodic': + extrapolate = False + else: + extrapolate = self.extrapolate + + # construct a compatible polynomial + return self.construct_fast(c, self.x, extrapolate, self.axis) + + def integrate(self, a, b, extrapolate=None): + """ + Compute a definite integral over a piecewise polynomial. + + Parameters + ---------- + a : float + Lower integration bound + b : float + Upper integration bound + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. + If None (default), use `self.extrapolate`. + + Returns + ------- + ig : array_like + Definite integral of the piecewise polynomial over [a, b] + """ + if extrapolate is None: + extrapolate = self.extrapolate + + # Swap integration bounds if needed + sign = 1 + if b < a: + a, b = b, a + sign = -1 + + range_int = np.empty((prod(self.c.shape[2:]),), dtype=self.c.dtype) + self._ensure_c_contiguous() + + # Compute the integral. + if extrapolate == 'periodic': + # Split the integral into the part over period (can be several + # of them) and the remaining part. + + xs, xe = self.x[0], self.x[-1] + period = xe - xs + interval = b - a + n_periods, left = divmod(interval, period) + + if n_periods > 0: + _ppoly.integrate( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, xs, xe, False, out=range_int) + range_int *= n_periods + else: + range_int.fill(0) + + # Map a to [xs, xe], b is always a + left. + a = xs + (a - xs) % period + b = a + left + + # If b <= xe then we need to integrate over [a, b], otherwise + # over [a, xe] and from xs to what is remained. + remainder_int = np.empty_like(range_int) + if b <= xe: + _ppoly.integrate( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, a, b, False, out=remainder_int) + range_int += remainder_int + else: + _ppoly.integrate( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, a, xe, False, out=remainder_int) + range_int += remainder_int + + _ppoly.integrate( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, xs, xs + left + a - xe, False, out=remainder_int) + range_int += remainder_int + else: + _ppoly.integrate( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, a, b, bool(extrapolate), out=range_int) + + # Return + range_int *= sign + return range_int.reshape(self.c.shape[2:]) + + def solve(self, y=0., discontinuity=True, extrapolate=None): + """ + Find real solutions of the equation ``pp(x) == y``. + + Parameters + ---------- + y : float, optional + Right-hand side. Default is zero. + discontinuity : bool, optional + Whether to report sign changes across discontinuities at + breakpoints as roots. + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to return roots from the polynomial + extrapolated based on first and last intervals, 'periodic' works + the same as False. If None (default), use `self.extrapolate`. + + Returns + ------- + roots : ndarray + Roots of the polynomial(s). + + If the PPoly object describes multiple polynomials, the + return value is an object array whose each element is an + ndarray containing the roots. + + Notes + ----- + This routine works only on real-valued polynomials. + + If the piecewise polynomial contains sections that are + identically zero, the root list will contain the start point + of the corresponding interval, followed by a ``nan`` value. + + If the polynomial is discontinuous across a breakpoint, and + there is a sign change across the breakpoint, this is reported + if the `discont` parameter is True. + + Examples + -------- + + Finding roots of ``[x**2 - 1, (x - 1)**2]`` defined on intervals + ``[-2, 1], [1, 2]``: + + >>> import numpy as np + >>> from scipy.interpolate import PPoly + >>> pp = PPoly(np.array([[1, -4, 3], [1, 0, 0]]).T, [-2, 1, 2]) + >>> pp.solve() + array([-1., 1.]) + """ + if extrapolate is None: + extrapolate = self.extrapolate + + self._ensure_c_contiguous() + + if np.issubdtype(self.c.dtype, np.complexfloating): + raise ValueError("Root finding is only for " + "real-valued polynomials") + + y = float(y) + r = _ppoly.real_roots(self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, y, bool(discontinuity), + bool(extrapolate)) + if self.c.ndim == 2: + return r[0] + else: + r2 = np.empty(prod(self.c.shape[2:]), dtype=object) + # this for-loop is equivalent to ``r2[...] = r``, but that's broken + # in NumPy 1.6.0 + for ii, root in enumerate(r): + r2[ii] = root + + return r2.reshape(self.c.shape[2:]) + + def roots(self, discontinuity=True, extrapolate=None): + """ + Find real roots of the piecewise polynomial. + + Parameters + ---------- + discontinuity : bool, optional + Whether to report sign changes across discontinuities at + breakpoints as roots. + extrapolate : {bool, 'periodic', None}, optional + If bool, determines whether to return roots from the polynomial + extrapolated based on first and last intervals, 'periodic' works + the same as False. If None (default), use `self.extrapolate`. + + Returns + ------- + roots : ndarray + Roots of the polynomial(s). + + If the PPoly object describes multiple polynomials, the + return value is an object array whose each element is an + ndarray containing the roots. + + See Also + -------- + PPoly.solve + """ + return self.solve(0, discontinuity, extrapolate) + + @classmethod + def from_spline(cls, tck, extrapolate=None): + """ + Construct a piecewise polynomial from a spline + + Parameters + ---------- + tck + A spline, as returned by `splrep` or a BSpline object. + extrapolate : bool or 'periodic', optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. Default is True. + + Examples + -------- + Construct an interpolating spline and convert it to a `PPoly` instance + + >>> import numpy as np + >>> from scipy.interpolate import splrep, PPoly + >>> x = np.linspace(0, 1, 11) + >>> y = np.sin(2*np.pi*x) + >>> tck = splrep(x, y, s=0) + >>> p = PPoly.from_spline(tck) + >>> isinstance(p, PPoly) + True + + Note that this function only supports 1D splines out of the box. + + If the ``tck`` object represents a parametric spline (e.g. constructed + by `splprep` or a `BSpline` with ``c.ndim > 1``), you will need to loop + over the dimensions manually. + + >>> from scipy.interpolate import splprep, splev + >>> t = np.linspace(0, 1, 11) + >>> x = np.sin(2*np.pi*t) + >>> y = np.cos(2*np.pi*t) + >>> (t, c, k), u = splprep([x, y], s=0) + + Note that ``c`` is a list of two arrays of length 11. + + >>> unew = np.arange(0, 1.01, 0.01) + >>> out = splev(unew, (t, c, k)) + + To convert this spline to the power basis, we convert each + component of the list of b-spline coefficients, ``c``, into the + corresponding cubic polynomial. + + >>> polys = [PPoly.from_spline((t, cj, k)) for cj in c] + >>> polys[0].c.shape + (4, 14) + + Note that the coefficients of the polynomials `polys` are in the + power basis and their dimensions reflect just that: here 4 is the order + (degree+1), and 14 is the number of intervals---which is nothing but + the length of the knot array of the original `tck` minus one. + + Optionally, we can stack the components into a single `PPoly` along + the third dimension: + + >>> cc = np.dstack([p.c for p in polys]) # has shape = (4, 14, 2) + >>> poly = PPoly(cc, polys[0].x) + >>> np.allclose(poly(unew).T, # note the transpose to match `splev` + ... out, atol=1e-15) + True + + """ + if isinstance(tck, BSpline): + t, c, k = tck.tck + if extrapolate is None: + extrapolate = tck.extrapolate + else: + t, c, k = tck + + cvals = np.empty((k + 1, len(t)-1), dtype=c.dtype) + for m in range(k, -1, -1): + y = _fitpack_py.splev(t[:-1], tck, der=m) + cvals[k - m, :] = y/spec.gamma(m+1) + + return cls.construct_fast(cvals, t, extrapolate) + + @classmethod + def from_bernstein_basis(cls, bp, extrapolate=None): + """ + Construct a piecewise polynomial in the power basis + from a polynomial in Bernstein basis. + + Parameters + ---------- + bp : BPoly + A Bernstein basis polynomial, as created by BPoly + extrapolate : bool or 'periodic', optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. Default is True. + """ + if not isinstance(bp, BPoly): + raise TypeError(".from_bernstein_basis only accepts BPoly instances. " + "Got %s instead." % type(bp)) + + dx = np.diff(bp.x) + k = bp.c.shape[0] - 1 # polynomial order + + rest = (None,)*(bp.c.ndim-2) + + c = np.zeros_like(bp.c) + for a in range(k+1): + factor = (-1)**a * comb(k, a) * bp.c[a] + for s in range(a, k+1): + val = comb(k-a, s-a) * (-1)**s + c[k-s] += factor * val / dx[(slice(None),)+rest]**s + + if extrapolate is None: + extrapolate = bp.extrapolate + + return cls.construct_fast(c, bp.x, extrapolate, bp.axis) + + +class BPoly(_PPolyBase): + """Piecewise polynomial in terms of coefficients and breakpoints. + + The polynomial between ``x[i]`` and ``x[i + 1]`` is written in the + Bernstein polynomial basis:: + + S = sum(c[a, i] * b(a, k; x) for a in range(k+1)), + + where ``k`` is the degree of the polynomial, and:: + + b(a, k; x) = binom(k, a) * t**a * (1 - t)**(k - a), + + with ``t = (x - x[i]) / (x[i+1] - x[i])`` and ``binom`` is the binomial + coefficient. + + Parameters + ---------- + c : ndarray, shape (k, m, ...) + Polynomial coefficients, order `k` and `m` intervals + x : ndarray, shape (m+1,) + Polynomial breakpoints. Must be sorted in either increasing or + decreasing order. + extrapolate : bool, optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. If 'periodic', + periodic extrapolation is used. Default is True. + axis : int, optional + Interpolation axis. Default is zero. + + Attributes + ---------- + x : ndarray + Breakpoints. + c : ndarray + Coefficients of the polynomials. They are reshaped + to a 3-D array with the last dimension representing + the trailing dimensions of the original coefficient array. + axis : int + Interpolation axis. + + Methods + ------- + __call__ + extend + derivative + antiderivative + integrate + construct_fast + from_power_basis + from_derivatives + + See also + -------- + PPoly : piecewise polynomials in the power basis + + Notes + ----- + Properties of Bernstein polynomials are well documented in the literature, + see for example [1]_ [2]_ [3]_. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Bernstein_polynomial + + .. [2] Kenneth I. Joy, Bernstein polynomials, + http://www.idav.ucdavis.edu/education/CAGDNotes/Bernstein-Polynomials.pdf + + .. [3] E. H. Doha, A. H. Bhrawy, and M. A. Saker, Boundary Value Problems, + vol 2011, article ID 829546, :doi:`10.1155/2011/829543`. + + Examples + -------- + >>> from scipy.interpolate import BPoly + >>> x = [0, 1] + >>> c = [[1], [2], [3]] + >>> bp = BPoly(c, x) + + This creates a 2nd order polynomial + + .. math:: + + B(x) = 1 \\times b_{0, 2}(x) + 2 \\times b_{1, 2}(x) + 3 + \\times b_{2, 2}(x) \\\\ + = 1 \\times (1-x)^2 + 2 \\times 2 x (1 - x) + 3 \\times x^2 + + """ # noqa: E501 + + def _evaluate(self, x, nu, extrapolate, out): + _ppoly.evaluate_bernstein( + self.c.reshape(self.c.shape[0], self.c.shape[1], -1), + self.x, x, nu, bool(extrapolate), out) + + def derivative(self, nu=1): + """ + Construct a new piecewise polynomial representing the derivative. + + Parameters + ---------- + nu : int, optional + Order of derivative to evaluate. Default is 1, i.e., compute the + first derivative. If negative, the antiderivative is returned. + + Returns + ------- + bp : BPoly + Piecewise polynomial of order k - nu representing the derivative of + this polynomial. + + """ + if nu < 0: + return self.antiderivative(-nu) + + if nu > 1: + bp = self + for k in range(nu): + bp = bp.derivative() + return bp + + # reduce order + if nu == 0: + c2 = self.c.copy() + else: + # For a polynomial + # B(x) = \sum_{a=0}^{k} c_a b_{a, k}(x), + # we use the fact that + # b'_{a, k} = k ( b_{a-1, k-1} - b_{a, k-1} ), + # which leads to + # B'(x) = \sum_{a=0}^{k-1} (c_{a+1} - c_a) b_{a, k-1} + # + # finally, for an interval [y, y + dy] with dy != 1, + # we need to correct for an extra power of dy + + rest = (None,)*(self.c.ndim-2) + + k = self.c.shape[0] - 1 + dx = np.diff(self.x)[(None, slice(None))+rest] + c2 = k * np.diff(self.c, axis=0) / dx + + if c2.shape[0] == 0: + # derivative of order 0 is zero + c2 = np.zeros((1,) + c2.shape[1:], dtype=c2.dtype) + + # construct a compatible polynomial + return self.construct_fast(c2, self.x, self.extrapolate, self.axis) + + def antiderivative(self, nu=1): + """ + Construct a new piecewise polynomial representing the antiderivative. + + Parameters + ---------- + nu : int, optional + Order of antiderivative to evaluate. Default is 1, i.e., compute + the first integral. If negative, the derivative is returned. + + Returns + ------- + bp : BPoly + Piecewise polynomial of order k + nu representing the + antiderivative of this polynomial. + + Notes + ----- + If antiderivative is computed and ``self.extrapolate='periodic'``, + it will be set to False for the returned instance. This is done because + the antiderivative is no longer periodic and its correct evaluation + outside of the initially given x interval is difficult. + """ + if nu <= 0: + return self.derivative(-nu) + + if nu > 1: + bp = self + for k in range(nu): + bp = bp.antiderivative() + return bp + + # Construct the indefinite integrals on individual intervals + c, x = self.c, self.x + k = c.shape[0] + c2 = np.zeros((k+1,) + c.shape[1:], dtype=c.dtype) + + c2[1:, ...] = np.cumsum(c, axis=0) / k + delta = x[1:] - x[:-1] + c2 *= delta[(None, slice(None)) + (None,)*(c.ndim-2)] + + # Now fix continuity: on the very first interval, take the integration + # constant to be zero; on an interval [x_j, x_{j+1}) with j>0, + # the integration constant is then equal to the jump of the `bp` at x_j. + # The latter is given by the coefficient of B_{n+1, n+1} + # *on the previous interval* (other B. polynomials are zero at the + # breakpoint). Finally, use the fact that BPs form a partition of unity. + c2[:,1:] += np.cumsum(c2[k, :], axis=0)[:-1] + + if self.extrapolate == 'periodic': + extrapolate = False + else: + extrapolate = self.extrapolate + + return self.construct_fast(c2, x, extrapolate, axis=self.axis) + + def integrate(self, a, b, extrapolate=None): + """ + Compute a definite integral over a piecewise polynomial. + + Parameters + ---------- + a : float + Lower integration bound + b : float + Upper integration bound + extrapolate : {bool, 'periodic', None}, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. If 'periodic', periodic + extrapolation is used. If None (default), use `self.extrapolate`. + + Returns + ------- + array_like + Definite integral of the piecewise polynomial over [a, b] + + """ + # XXX: can probably use instead the fact that + # \int_0^{1} B_{j, n}(x) \dx = 1/(n+1) + ib = self.antiderivative() + if extrapolate is None: + extrapolate = self.extrapolate + + # ib.extrapolate shouldn't be 'periodic', it is converted to + # False for 'periodic. in antiderivative() call. + if extrapolate != 'periodic': + ib.extrapolate = extrapolate + + if extrapolate == 'periodic': + # Split the integral into the part over period (can be several + # of them) and the remaining part. + + # For simplicity and clarity convert to a <= b case. + if a <= b: + sign = 1 + else: + a, b = b, a + sign = -1 + + xs, xe = self.x[0], self.x[-1] + period = xe - xs + interval = b - a + n_periods, left = divmod(interval, period) + res = n_periods * (ib(xe) - ib(xs)) + + # Map a and b to [xs, xe]. + a = xs + (a - xs) % period + b = a + left + + # If b <= xe then we need to integrate over [a, b], otherwise + # over [a, xe] and from xs to what is remained. + if b <= xe: + res += ib(b) - ib(a) + else: + res += ib(xe) - ib(a) + ib(xs + left + a - xe) - ib(xs) + + return sign * res + else: + return ib(b) - ib(a) + + def extend(self, c, x): + k = max(self.c.shape[0], c.shape[0]) + self.c = self._raise_degree(self.c, k - self.c.shape[0]) + c = self._raise_degree(c, k - c.shape[0]) + return _PPolyBase.extend(self, c, x) + extend.__doc__ = _PPolyBase.extend.__doc__ + + @classmethod + def from_power_basis(cls, pp, extrapolate=None): + """ + Construct a piecewise polynomial in Bernstein basis + from a power basis polynomial. + + Parameters + ---------- + pp : PPoly + A piecewise polynomial in the power basis + extrapolate : bool or 'periodic', optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. Default is True. + """ + if not isinstance(pp, PPoly): + raise TypeError(".from_power_basis only accepts PPoly instances. " + "Got %s instead." % type(pp)) + + dx = np.diff(pp.x) + k = pp.c.shape[0] - 1 # polynomial order + + rest = (None,)*(pp.c.ndim-2) + + c = np.zeros_like(pp.c) + for a in range(k+1): + factor = pp.c[a] / comb(k, k-a) * dx[(slice(None),)+rest]**(k-a) + for j in range(k-a, k+1): + c[j] += factor * comb(j, k-a) + + if extrapolate is None: + extrapolate = pp.extrapolate + + return cls.construct_fast(c, pp.x, extrapolate, pp.axis) + + @classmethod + def from_derivatives(cls, xi, yi, orders=None, extrapolate=None): + """Construct a piecewise polynomial in the Bernstein basis, + compatible with the specified values and derivatives at breakpoints. + + Parameters + ---------- + xi : array_like + sorted 1-D array of x-coordinates + yi : array_like or list of array_likes + ``yi[i][j]`` is the ``j``\\ th derivative known at ``xi[i]`` + orders : None or int or array_like of ints. Default: None. + Specifies the degree of local polynomials. If not None, some + derivatives are ignored. + extrapolate : bool or 'periodic', optional + If bool, determines whether to extrapolate to out-of-bounds points + based on first and last intervals, or to return NaNs. + If 'periodic', periodic extrapolation is used. Default is True. + + Notes + ----- + If ``k`` derivatives are specified at a breakpoint ``x``, the + constructed polynomial is exactly ``k`` times continuously + differentiable at ``x``, unless the ``order`` is provided explicitly. + In the latter case, the smoothness of the polynomial at + the breakpoint is controlled by the ``order``. + + Deduces the number of derivatives to match at each end + from ``order`` and the number of derivatives available. If + possible it uses the same number of derivatives from + each end; if the number is odd it tries to take the + extra one from y2. In any case if not enough derivatives + are available at one end or another it draws enough to + make up the total from the other end. + + If the order is too high and not enough derivatives are available, + an exception is raised. + + Examples + -------- + + >>> from scipy.interpolate import BPoly + >>> BPoly.from_derivatives([0, 1], [[1, 2], [3, 4]]) + + Creates a polynomial `f(x)` of degree 3, defined on `[0, 1]` + such that `f(0) = 1, df/dx(0) = 2, f(1) = 3, df/dx(1) = 4` + + >>> BPoly.from_derivatives([0, 1, 2], [[0, 1], [0], [2]]) + + Creates a piecewise polynomial `f(x)`, such that + `f(0) = f(1) = 0`, `f(2) = 2`, and `df/dx(0) = 1`. + Based on the number of derivatives provided, the order of the + local polynomials is 2 on `[0, 1]` and 1 on `[1, 2]`. + Notice that no restriction is imposed on the derivatives at + ``x = 1`` and ``x = 2``. + + Indeed, the explicit form of the polynomial is:: + + f(x) = | x * (1 - x), 0 <= x < 1 + | 2 * (x - 1), 1 <= x <= 2 + + So that f'(1-0) = -1 and f'(1+0) = 2 + + """ + xi = np.asarray(xi) + if len(xi) != len(yi): + raise ValueError("xi and yi need to have the same length") + if np.any(xi[1:] - xi[:1] <= 0): + raise ValueError("x coordinates are not in increasing order") + + # number of intervals + m = len(xi) - 1 + + # global poly order is k-1, local orders are <=k and can vary + try: + k = max(len(yi[i]) + len(yi[i+1]) for i in range(m)) + except TypeError as e: + raise ValueError( + "Using a 1-D array for y? Please .reshape(-1, 1)." + ) from e + + if orders is None: + orders = [None] * m + else: + if isinstance(orders, (int, np.integer)): + orders = [orders] * m + k = max(k, max(orders)) + + if any(o <= 0 for o in orders): + raise ValueError("Orders must be positive.") + + c = [] + for i in range(m): + y1, y2 = yi[i], yi[i+1] + if orders[i] is None: + n1, n2 = len(y1), len(y2) + else: + n = orders[i]+1 + n1 = min(n//2, len(y1)) + n2 = min(n - n1, len(y2)) + n1 = min(n - n2, len(y2)) + if n1+n2 != n: + mesg = ("Point %g has %d derivatives, point %g" + " has %d derivatives, but order %d requested" % ( + xi[i], len(y1), xi[i+1], len(y2), orders[i])) + raise ValueError(mesg) + + if not (n1 <= len(y1) and n2 <= len(y2)): + raise ValueError("`order` input incompatible with" + " length y1 or y2.") + + b = BPoly._construct_from_derivatives(xi[i], xi[i+1], + y1[:n1], y2[:n2]) + if len(b) < k: + b = BPoly._raise_degree(b, k - len(b)) + c.append(b) + + c = np.asarray(c) + return cls(c.swapaxes(0, 1), xi, extrapolate) + + @staticmethod + def _construct_from_derivatives(xa, xb, ya, yb): + r"""Compute the coefficients of a polynomial in the Bernstein basis + given the values and derivatives at the edges. + + Return the coefficients of a polynomial in the Bernstein basis + defined on ``[xa, xb]`` and having the values and derivatives at the + endpoints `xa` and `xb` as specified by `ya` and `yb`. + The polynomial constructed is of the minimal possible degree, i.e., + if the lengths of `ya` and `yb` are `na` and `nb`, the degree + of the polynomial is ``na + nb - 1``. + + Parameters + ---------- + xa : float + Left-hand end point of the interval + xb : float + Right-hand end point of the interval + ya : array_like + Derivatives at `xa`. ``ya[0]`` is the value of the function, and + ``ya[i]`` for ``i > 0`` is the value of the ``i``\ th derivative. + yb : array_like + Derivatives at `xb`. + + Returns + ------- + array + coefficient array of a polynomial having specified derivatives + + Notes + ----- + This uses several facts from life of Bernstein basis functions. + First of all, + + .. math:: b'_{a, n} = n (b_{a-1, n-1} - b_{a, n-1}) + + If B(x) is a linear combination of the form + + .. math:: B(x) = \sum_{a=0}^{n} c_a b_{a, n}, + + then :math: B'(x) = n \sum_{a=0}^{n-1} (c_{a+1} - c_{a}) b_{a, n-1}. + Iterating the latter one, one finds for the q-th derivative + + .. math:: B^{q}(x) = n!/(n-q)! \sum_{a=0}^{n-q} Q_a b_{a, n-q}, + + with + + .. math:: Q_a = \sum_{j=0}^{q} (-)^{j+q} comb(q, j) c_{j+a} + + This way, only `a=0` contributes to :math: `B^{q}(x = xa)`, and + `c_q` are found one by one by iterating `q = 0, ..., na`. + + At ``x = xb`` it's the same with ``a = n - q``. + + """ + ya, yb = np.asarray(ya), np.asarray(yb) + if ya.shape[1:] != yb.shape[1:]: + raise ValueError('Shapes of ya {} and yb {} are incompatible' + .format(ya.shape, yb.shape)) + + dta, dtb = ya.dtype, yb.dtype + if (np.issubdtype(dta, np.complexfloating) or + np.issubdtype(dtb, np.complexfloating)): + dt = np.complex128 + else: + dt = np.float64 + + na, nb = len(ya), len(yb) + n = na + nb + + c = np.empty((na+nb,) + ya.shape[1:], dtype=dt) + + # compute coefficients of a polynomial degree na+nb-1 + # walk left-to-right + for q in range(0, na): + c[q] = ya[q] / spec.poch(n - q, q) * (xb - xa)**q + for j in range(0, q): + c[q] -= (-1)**(j+q) * comb(q, j) * c[j] + + # now walk right-to-left + for q in range(0, nb): + c[-q-1] = yb[q] / spec.poch(n - q, q) * (-1)**q * (xb - xa)**q + for j in range(0, q): + c[-q-1] -= (-1)**(j+1) * comb(q, j+1) * c[-q+j] + + return c + + @staticmethod + def _raise_degree(c, d): + r"""Raise a degree of a polynomial in the Bernstein basis. + + Given the coefficients of a polynomial degree `k`, return (the + coefficients of) the equivalent polynomial of degree `k+d`. + + Parameters + ---------- + c : array_like + coefficient array, 1-D + d : integer + + Returns + ------- + array + coefficient array, 1-D array of length `c.shape[0] + d` + + Notes + ----- + This uses the fact that a Bernstein polynomial `b_{a, k}` can be + identically represented as a linear combination of polynomials of + a higher degree `k+d`: + + .. math:: b_{a, k} = comb(k, a) \sum_{j=0}^{d} b_{a+j, k+d} \ + comb(d, j) / comb(k+d, a+j) + + """ + if d == 0: + return c + + k = c.shape[0] - 1 + out = np.zeros((c.shape[0] + d,) + c.shape[1:], dtype=c.dtype) + + for a in range(c.shape[0]): + f = c[a] * comb(k, a) + for j in range(d+1): + out[a+j] += f * comb(d, j) / comb(k+d, a+j) + return out + + +class NdPPoly: + """ + Piecewise tensor product polynomial + + The value at point ``xp = (x', y', z', ...)`` is evaluated by first + computing the interval indices `i` such that:: + + x[0][i[0]] <= x' < x[0][i[0]+1] + x[1][i[1]] <= y' < x[1][i[1]+1] + ... + + and then computing:: + + S = sum(c[k0-m0-1,...,kn-mn-1,i[0],...,i[n]] + * (xp[0] - x[0][i[0]])**m0 + * ... + * (xp[n] - x[n][i[n]])**mn + for m0 in range(k[0]+1) + ... + for mn in range(k[n]+1)) + + where ``k[j]`` is the degree of the polynomial in dimension j. This + representation is the piecewise multivariate power basis. + + Parameters + ---------- + c : ndarray, shape (k0, ..., kn, m0, ..., mn, ...) + Polynomial coefficients, with polynomial order `kj` and + `mj+1` intervals for each dimension `j`. + x : ndim-tuple of ndarrays, shapes (mj+1,) + Polynomial breakpoints for each dimension. These must be + sorted in increasing order. + extrapolate : bool, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. Default: True. + + Attributes + ---------- + x : tuple of ndarrays + Breakpoints. + c : ndarray + Coefficients of the polynomials. + + Methods + ------- + __call__ + derivative + antiderivative + integrate + integrate_1d + construct_fast + + See also + -------- + PPoly : piecewise polynomials in 1D + + Notes + ----- + High-order polynomials in the power basis can be numerically + unstable. + + """ + + def __init__(self, c, x, extrapolate=None): + self.x = tuple(np.ascontiguousarray(v, dtype=np.float64) for v in x) + self.c = np.asarray(c) + if extrapolate is None: + extrapolate = True + self.extrapolate = bool(extrapolate) + + ndim = len(self.x) + if any(v.ndim != 1 for v in self.x): + raise ValueError("x arrays must all be 1-dimensional") + if any(v.size < 2 for v in self.x): + raise ValueError("x arrays must all contain at least 2 points") + if c.ndim < 2*ndim: + raise ValueError("c must have at least 2*len(x) dimensions") + if any(np.any(v[1:] - v[:-1] < 0) for v in self.x): + raise ValueError("x-coordinates are not in increasing order") + if any(a != b.size - 1 for a, b in zip(c.shape[ndim:2*ndim], self.x)): + raise ValueError("x and c do not agree on the number of intervals") + + dtype = self._get_dtype(self.c.dtype) + self.c = np.ascontiguousarray(self.c, dtype=dtype) + + @classmethod + def construct_fast(cls, c, x, extrapolate=None): + """ + Construct the piecewise polynomial without making checks. + + Takes the same parameters as the constructor. Input arguments + ``c`` and ``x`` must be arrays of the correct shape and type. The + ``c`` array can only be of dtypes float and complex, and ``x`` + array must have dtype float. + + """ + self = object.__new__(cls) + self.c = c + self.x = x + if extrapolate is None: + extrapolate = True + self.extrapolate = extrapolate + return self + + def _get_dtype(self, dtype): + if np.issubdtype(dtype, np.complexfloating) \ + or np.issubdtype(self.c.dtype, np.complexfloating): + return np.complex128 + else: + return np.float64 + + def _ensure_c_contiguous(self): + if not self.c.flags.c_contiguous: + self.c = self.c.copy() + if not isinstance(self.x, tuple): + self.x = tuple(self.x) + + def __call__(self, x, nu=None, extrapolate=None): + """ + Evaluate the piecewise polynomial or its derivative + + Parameters + ---------- + x : array-like + Points to evaluate the interpolant at. + nu : tuple, optional + Orders of derivatives to evaluate. Each must be non-negative. + extrapolate : bool, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. + + Returns + ------- + y : array-like + Interpolated values. Shape is determined by replacing + the interpolation axis in the original array with the shape of x. + + Notes + ----- + Derivatives are evaluated piecewise for each polynomial + segment, even if the polynomial is not differentiable at the + breakpoints. The polynomial intervals are considered half-open, + ``[a, b)``, except for the last interval which is closed + ``[a, b]``. + + """ + if extrapolate is None: + extrapolate = self.extrapolate + else: + extrapolate = bool(extrapolate) + + ndim = len(self.x) + + x = _ndim_coords_from_arrays(x) + x_shape = x.shape + x = np.ascontiguousarray(x.reshape(-1, x.shape[-1]), dtype=np.float64) + + if nu is None: + nu = np.zeros((ndim,), dtype=np.intc) + else: + nu = np.asarray(nu, dtype=np.intc) + if nu.ndim != 1 or nu.shape[0] != ndim: + raise ValueError("invalid number of derivative orders nu") + + dim1 = prod(self.c.shape[:ndim]) + dim2 = prod(self.c.shape[ndim:2*ndim]) + dim3 = prod(self.c.shape[2*ndim:]) + ks = np.array(self.c.shape[:ndim], dtype=np.intc) + + out = np.empty((x.shape[0], dim3), dtype=self.c.dtype) + self._ensure_c_contiguous() + + _ppoly.evaluate_nd(self.c.reshape(dim1, dim2, dim3), + self.x, + ks, + x, + nu, + bool(extrapolate), + out) + + return out.reshape(x_shape[:-1] + self.c.shape[2*ndim:]) + + def _derivative_inplace(self, nu, axis): + """ + Compute 1-D derivative along a selected dimension in-place + May result to non-contiguous c array. + """ + if nu < 0: + return self._antiderivative_inplace(-nu, axis) + + ndim = len(self.x) + axis = axis % ndim + + # reduce order + if nu == 0: + # noop + return + else: + sl = [slice(None)]*ndim + sl[axis] = slice(None, -nu, None) + c2 = self.c[tuple(sl)] + + if c2.shape[axis] == 0: + # derivative of order 0 is zero + shp = list(c2.shape) + shp[axis] = 1 + c2 = np.zeros(shp, dtype=c2.dtype) + + # multiply by the correct rising factorials + factor = spec.poch(np.arange(c2.shape[axis], 0, -1), nu) + sl = [None]*c2.ndim + sl[axis] = slice(None) + c2 *= factor[tuple(sl)] + + self.c = c2 + + def _antiderivative_inplace(self, nu, axis): + """ + Compute 1-D antiderivative along a selected dimension + May result to non-contiguous c array. + """ + if nu <= 0: + return self._derivative_inplace(-nu, axis) + + ndim = len(self.x) + axis = axis % ndim + + perm = list(range(ndim)) + perm[0], perm[axis] = perm[axis], perm[0] + perm = perm + list(range(ndim, self.c.ndim)) + + c = self.c.transpose(perm) + + c2 = np.zeros((c.shape[0] + nu,) + c.shape[1:], + dtype=c.dtype) + c2[:-nu] = c + + # divide by the correct rising factorials + factor = spec.poch(np.arange(c.shape[0], 0, -1), nu) + c2[:-nu] /= factor[(slice(None),) + (None,)*(c.ndim-1)] + + # fix continuity of added degrees of freedom + perm2 = list(range(c2.ndim)) + perm2[1], perm2[ndim+axis] = perm2[ndim+axis], perm2[1] + + c2 = c2.transpose(perm2) + c2 = c2.copy() + _ppoly.fix_continuity(c2.reshape(c2.shape[0], c2.shape[1], -1), + self.x[axis], nu-1) + + c2 = c2.transpose(perm2) + c2 = c2.transpose(perm) + + # Done + self.c = c2 + + def derivative(self, nu): + """ + Construct a new piecewise polynomial representing the derivative. + + Parameters + ---------- + nu : ndim-tuple of int + Order of derivatives to evaluate for each dimension. + If negative, the antiderivative is returned. + + Returns + ------- + pp : NdPPoly + Piecewise polynomial of orders (k[0] - nu[0], ..., k[n] - nu[n]) + representing the derivative of this polynomial. + + Notes + ----- + Derivatives are evaluated piecewise for each polynomial + segment, even if the polynomial is not differentiable at the + breakpoints. The polynomial intervals in each dimension are + considered half-open, ``[a, b)``, except for the last interval + which is closed ``[a, b]``. + + """ + p = self.construct_fast(self.c.copy(), self.x, self.extrapolate) + + for axis, n in enumerate(nu): + p._derivative_inplace(n, axis) + + p._ensure_c_contiguous() + return p + + def antiderivative(self, nu): + """ + Construct a new piecewise polynomial representing the antiderivative. + + Antiderivative is also the indefinite integral of the function, + and derivative is its inverse operation. + + Parameters + ---------- + nu : ndim-tuple of int + Order of derivatives to evaluate for each dimension. + If negative, the derivative is returned. + + Returns + ------- + pp : PPoly + Piecewise polynomial of order k2 = k + n representing + the antiderivative of this polynomial. + + Notes + ----- + The antiderivative returned by this function is continuous and + continuously differentiable to order n-1, up to floating point + rounding error. + + """ + p = self.construct_fast(self.c.copy(), self.x, self.extrapolate) + + for axis, n in enumerate(nu): + p._antiderivative_inplace(n, axis) + + p._ensure_c_contiguous() + return p + + def integrate_1d(self, a, b, axis, extrapolate=None): + r""" + Compute NdPPoly representation for one dimensional definite integral + + The result is a piecewise polynomial representing the integral: + + .. math:: + + p(y, z, ...) = \int_a^b dx\, p(x, y, z, ...) + + where the dimension integrated over is specified with the + `axis` parameter. + + Parameters + ---------- + a, b : float + Lower and upper bound for integration. + axis : int + Dimension over which to compute the 1-D integrals + extrapolate : bool, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. + + Returns + ------- + ig : NdPPoly or array-like + Definite integral of the piecewise polynomial over [a, b]. + If the polynomial was 1D, an array is returned, + otherwise, an NdPPoly object. + + """ + if extrapolate is None: + extrapolate = self.extrapolate + else: + extrapolate = bool(extrapolate) + + ndim = len(self.x) + axis = int(axis) % ndim + + # reuse 1-D integration routines + c = self.c + swap = list(range(c.ndim)) + swap.insert(0, swap[axis]) + del swap[axis + 1] + swap.insert(1, swap[ndim + axis]) + del swap[ndim + axis + 1] + + c = c.transpose(swap) + p = PPoly.construct_fast(c.reshape(c.shape[0], c.shape[1], -1), + self.x[axis], + extrapolate=extrapolate) + out = p.integrate(a, b, extrapolate=extrapolate) + + # Construct result + if ndim == 1: + return out.reshape(c.shape[2:]) + else: + c = out.reshape(c.shape[2:]) + x = self.x[:axis] + self.x[axis+1:] + return self.construct_fast(c, x, extrapolate=extrapolate) + + def integrate(self, ranges, extrapolate=None): + """ + Compute a definite integral over a piecewise polynomial. + + Parameters + ---------- + ranges : ndim-tuple of 2-tuples float + Sequence of lower and upper bounds for each dimension, + ``[(a[0], b[0]), ..., (a[ndim-1], b[ndim-1])]`` + extrapolate : bool, optional + Whether to extrapolate to out-of-bounds points based on first + and last intervals, or to return NaNs. + + Returns + ------- + ig : array_like + Definite integral of the piecewise polynomial over + [a[0], b[0]] x ... x [a[ndim-1], b[ndim-1]] + + """ + + ndim = len(self.x) + + if extrapolate is None: + extrapolate = self.extrapolate + else: + extrapolate = bool(extrapolate) + + if not hasattr(ranges, '__len__') or len(ranges) != ndim: + raise ValueError("Range not a sequence of correct length") + + self._ensure_c_contiguous() + + # Reuse 1D integration routine + c = self.c + for n, (a, b) in enumerate(ranges): + swap = list(range(c.ndim)) + swap.insert(1, swap[ndim - n]) + del swap[ndim - n + 1] + + c = c.transpose(swap) + + p = PPoly.construct_fast(c, self.x[n], extrapolate=extrapolate) + out = p.integrate(a, b, extrapolate=extrapolate) + c = out.reshape(c.shape[2:]) + + return c diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndbspline.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndbspline.py new file mode 100644 index 0000000000000000000000000000000000000000..826dddb311d78bf8d5381b18e46ca1ba86c04b6d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndbspline.py @@ -0,0 +1,358 @@ +import itertools +import functools +import operator +import numpy as np + +from math import prod + +from . import _bspl # type: ignore + +import scipy.sparse.linalg as ssl +from scipy.sparse import csr_array + +from ._bsplines import _not_a_knot + +__all__ = ["NdBSpline"] + + +def _get_dtype(dtype): + """Return np.complex128 for complex dtypes, np.float64 otherwise.""" + if np.issubdtype(dtype, np.complexfloating): + return np.complex128 + else: + return np.float64 + + +class NdBSpline: + """Tensor product spline object. + + The value at point ``xp = (x1, x2, ..., xN)`` is evaluated as a linear + combination of products of one-dimensional b-splines in each of the ``N`` + dimensions:: + + c[i1, i2, ..., iN] * B(x1; i1, t1) * B(x2; i2, t2) * ... * B(xN; iN, tN) + + + Here ``B(x; i, t)`` is the ``i``-th b-spline defined by the knot vector + ``t`` evaluated at ``x``. + + Parameters + ---------- + t : tuple of 1D ndarrays + knot vectors in directions 1, 2, ... N, + ``len(t[i]) == n[i] + k + 1`` + c : ndarray, shape (n1, n2, ..., nN, ...) + b-spline coefficients + k : int or length-d tuple of integers + spline degrees. + A single integer is interpreted as having this degree for + all dimensions. + extrapolate : bool, optional + Whether to extrapolate out-of-bounds inputs, or return `nan`. + Default is to extrapolate. + + Attributes + ---------- + t : tuple of ndarrays + Knots vectors. + c : ndarray + Coefficients of the tensor-produce spline. + k : tuple of integers + Degrees for each dimension. + extrapolate : bool, optional + Whether to extrapolate or return nans for out-of-bounds inputs. + Defaults to true. + + Methods + ------- + __call__ + design_matrix + + See Also + -------- + BSpline : a one-dimensional B-spline object + NdPPoly : an N-dimensional piecewise tensor product polynomial + + """ + def __init__(self, t, c, k, *, extrapolate=None): + ndim = len(t) + + try: + len(k) + except TypeError: + # make k a tuple + k = (k,)*ndim + + if len(k) != ndim: + raise ValueError(f"{len(t) = } != {len(k) = }.") + + self.k = tuple(operator.index(ki) for ki in k) + self.t = tuple(np.ascontiguousarray(ti, dtype=float) for ti in t) + self.c = np.asarray(c) + + if extrapolate is None: + extrapolate = True + self.extrapolate = bool(extrapolate) + + self.c = np.asarray(c) + + for d in range(ndim): + td = self.t[d] + kd = self.k[d] + n = td.shape[0] - kd - 1 + if kd < 0: + raise ValueError(f"Spline degree in dimension {d} cannot be" + f" negative.") + if td.ndim != 1: + raise ValueError(f"Knot vector in dimension {d} must be" + f" one-dimensional.") + if n < kd + 1: + raise ValueError(f"Need at least {2*kd + 2} knots for degree" + f" {kd} in dimension {d}.") + if (np.diff(td) < 0).any(): + raise ValueError(f"Knots in dimension {d} must be in a" + f" non-decreasing order.") + if len(np.unique(td[kd:n + 1])) < 2: + raise ValueError(f"Need at least two internal knots in" + f" dimension {d}.") + if not np.isfinite(td).all(): + raise ValueError(f"Knots in dimension {d} should not have" + f" nans or infs.") + if self.c.ndim < ndim: + raise ValueError(f"Coefficients must be at least" + f" {d}-dimensional.") + if self.c.shape[d] != n: + raise ValueError(f"Knots, coefficients and degree in dimension" + f" {d} are inconsistent:" + f" got {self.c.shape[d]} coefficients for" + f" {len(td)} knots, need at least {n} for" + f" k={k}.") + + dt = _get_dtype(self.c.dtype) + self.c = np.ascontiguousarray(self.c, dtype=dt) + + def __call__(self, xi, *, nu=None, extrapolate=None): + """Evaluate the tensor product b-spline at ``xi``. + + Parameters + ---------- + xi : array_like, shape(..., ndim) + The coordinates to evaluate the interpolator at. + This can be a list or tuple of ndim-dimensional points + or an array with the shape (num_points, ndim). + nu : array_like, optional, shape (ndim,) + Orders of derivatives to evaluate. Each must be non-negative. + Defaults to the zeroth derivivative. + extrapolate : bool, optional + Whether to exrapolate based on first and last intervals in each + dimension, or return `nan`. Default is to ``self.extrapolate``. + + Returns + ------- + values : ndarray, shape ``xi.shape[:-1] + self.c.shape[ndim:]`` + Interpolated values at ``xi`` + """ + ndim = len(self.t) + + if extrapolate is None: + extrapolate = self.extrapolate + extrapolate = bool(extrapolate) + + if nu is None: + nu = np.zeros((ndim,), dtype=np.intc) + else: + nu = np.asarray(nu, dtype=np.intc) + if nu.ndim != 1 or nu.shape[0] != ndim: + raise ValueError( + f"invalid number of derivative orders {nu = } for " + f"ndim = {len(self.t)}.") + if any(nu < 0): + raise ValueError(f"derivatives must be positive, got {nu = }") + + # prepare xi : shape (..., m1, ..., md) -> (1, m1, ..., md) + xi = np.asarray(xi, dtype=float) + xi_shape = xi.shape + xi = xi.reshape(-1, xi_shape[-1]) + xi = np.ascontiguousarray(xi) + + if xi_shape[-1] != ndim: + raise ValueError(f"Shapes: xi.shape={xi_shape} and ndim={ndim}") + + # prepare k & t + _k = np.asarray(self.k, dtype=np.dtype("long")) + + # pack the knots into a single array + len_t = [len(ti) for ti in self.t] + _t = np.empty((ndim, max(len_t)), dtype=float) + _t.fill(np.nan) + for d in range(ndim): + _t[d, :len(self.t[d])] = self.t[d] + len_t = np.asarray(len_t, dtype=np.dtype("long")) + + # tabulate the flat indices for iterating over the (k+1)**ndim subarray + shape = tuple(kd + 1 for kd in self.k) + indices = np.unravel_index(np.arange(prod(shape)), shape) + _indices_k1d = np.asarray(indices, dtype=np.intp).T + + # prepare the coefficients: flatten the trailing dimensions + c1 = self.c.reshape(self.c.shape[:ndim] + (-1,)) + c1r = c1.ravel() + + # replacement for np.ravel_multi_index for indexing of `c1`: + _strides_c1 = np.asarray([s // c1.dtype.itemsize + for s in c1.strides], dtype=np.intp) + + num_c_tr = c1.shape[-1] # # of trailing coefficients + out = np.empty(xi.shape[:-1] + (num_c_tr,), dtype=c1.dtype) + + _bspl.evaluate_ndbspline(xi, + _t, + len_t, + _k, + nu, + extrapolate, + c1r, + num_c_tr, + _strides_c1, + _indices_k1d, + out,) + + return out.reshape(xi_shape[:-1] + self.c.shape[ndim:]) + + @classmethod + def design_matrix(cls, xvals, t, k, extrapolate=True): + """Construct the design matrix as a CSR format sparse array. + + Parameters + ---------- + xvals : ndarray, shape(npts, ndim) + Data points. ``xvals[j, :]`` gives the ``j``-th data point as an + ``ndim``-dimensional array. + t : tuple of 1D ndarrays, length-ndim + Knot vectors in directions 1, 2, ... ndim, + k : int + B-spline degree. + extrapolate : bool, optional + Whether to extrapolate out-of-bounds values of raise a `ValueError` + + Returns + ------- + design_matrix : a CSR array + Each row of the design matrix corresponds to a value in `xvals` and + contains values of b-spline basis elements which are non-zero + at this value. + + """ + xvals = np.asarray(xvals, dtype=float) + ndim = xvals.shape[-1] + if len(t) != ndim: + raise ValueError( + f"Data and knots are inconsistent: len(t) = {len(t)} for " + f" {ndim = }." + ) + try: + len(k) + except TypeError: + # make k a tuple + k = (k,)*ndim + + kk = np.asarray(k, dtype=np.int32) + data, indices, indptr = _bspl._colloc_nd(xvals, t, kk) + return csr_array((data, indices, indptr)) + + +def _iter_solve(a, b, solver=ssl.gcrotmk, **solver_args): + # work around iterative solvers not accepting multiple r.h.s. + + # also work around a.dtype == float64 and b.dtype == complex128 + # cf https://github.com/scipy/scipy/issues/19644 + if np.issubdtype(b.dtype, np.complexfloating): + real = _iter_solve(a, b.real, solver, **solver_args) + imag = _iter_solve(a, b.imag, solver, **solver_args) + return real + 1j*imag + + if b.ndim == 2 and b.shape[1] !=1: + res = np.empty_like(b) + for j in range(b.shape[1]): + res[:, j], info = solver(a, b[:, j], **solver_args) + if info != 0: + raise ValueError(f"{solver = } returns {info =} for column {j}.") + return res + else: + res, info = solver(a, b, **solver_args) + if info != 0: + raise ValueError(f"{solver = } returns {info = }.") + return res + + +def make_ndbspl(points, values, k=3, *, solver=ssl.gcrotmk, **solver_args): + """Construct an interpolating NdBspline. + + Parameters + ---------- + points : tuple of ndarrays of float, with shapes (m1,), ... (mN,) + The points defining the regular grid in N dimensions. The points in + each dimension (i.e. every element of the `points` tuple) must be + strictly ascending or descending. + values : ndarray of float, shape (m1, ..., mN, ...) + The data on the regular grid in n dimensions. + k : int, optional + The spline degree. Must be odd. Default is cubic, k=3 + solver : a `scipy.sparse.linalg` solver (iterative or direct), optional. + An iterative solver from `scipy.sparse.linalg` or a direct one, + `sparse.sparse.linalg.spsolve`. + Used to solve the sparse linear system + ``design_matrix @ coefficients = rhs`` for the coefficients. + Default is `scipy.sparse.linalg.gcrotmk` + solver_args : dict, optional + Additional arguments for the solver. The call signature is + ``solver(csr_array, rhs_vector, **solver_args)`` + + Returns + ------- + spl : NdBSpline object + + Notes + ----- + Boundary conditions are not-a-knot in all dimensions. + """ + ndim = len(points) + xi_shape = tuple(len(x) for x in points) + + try: + len(k) + except TypeError: + # make k a tuple + k = (k,)*ndim + + for d, point in enumerate(points): + numpts = len(np.atleast_1d(point)) + if numpts <= k[d]: + raise ValueError(f"There are {numpts} points in dimension {d}," + f" but order {k[d]} requires at least " + f" {k[d]+1} points per dimension.") + + t = tuple(_not_a_knot(np.asarray(points[d], dtype=float), k[d]) + for d in range(ndim)) + xvals = np.asarray([xv for xv in itertools.product(*points)], dtype=float) + + # construct the colocation matrix + matr = NdBSpline.design_matrix(xvals, t, k) + + # Solve for the coefficients given `values`. + # Trailing dimensions: first ndim dimensions are data, the rest are batch + # dimensions, so stack `values` into a 2D array for `spsolve` to undestand. + v_shape = values.shape + vals_shape = (prod(v_shape[:ndim]), prod(v_shape[ndim:])) + vals = values.reshape(vals_shape) + + if solver != ssl.spsolve: + solver = functools.partial(_iter_solve, solver=solver) + if "atol" not in solver_args: + # avoid a DeprecationWarning, grumble grumble + solver_args["atol"] = 1e-6 + + coef = solver(matr, vals, **solver_args) + coef = coef.reshape(xi_shape + v_shape[ndim:]) + return NdBSpline(t, coef, k) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndgriddata.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndgriddata.py new file mode 100644 index 0000000000000000000000000000000000000000..2724d78f61416256357c4b2789c860a604845548 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_ndgriddata.py @@ -0,0 +1,332 @@ +""" +Convenience interface to N-D interpolation + +.. versionadded:: 0.9 + +""" +import numpy as np +from .interpnd import LinearNDInterpolator, NDInterpolatorBase, \ + CloughTocher2DInterpolator, _ndim_coords_from_arrays +from scipy.spatial import cKDTree + +__all__ = ['griddata', 'NearestNDInterpolator', 'LinearNDInterpolator', + 'CloughTocher2DInterpolator'] + +#------------------------------------------------------------------------------ +# Nearest-neighbor interpolation +#------------------------------------------------------------------------------ + + +class NearestNDInterpolator(NDInterpolatorBase): + """NearestNDInterpolator(x, y). + + Nearest-neighbor interpolator in N > 1 dimensions. + + .. versionadded:: 0.9 + + Methods + ------- + __call__ + + Parameters + ---------- + x : (npoints, ndims) 2-D ndarray of floats + Data point coordinates. + y : (npoints, ) 1-D ndarray of float or complex + Data values. + rescale : boolean, optional + Rescale points to unit cube before performing interpolation. + This is useful if some of the input dimensions have + incommensurable units and differ by many orders of magnitude. + + .. versionadded:: 0.14.0 + tree_options : dict, optional + Options passed to the underlying ``cKDTree``. + + .. versionadded:: 0.17.0 + + See Also + -------- + griddata : + Interpolate unstructured D-D data. + LinearNDInterpolator : + Piecewise linear interpolator in N dimensions. + CloughTocher2DInterpolator : + Piecewise cubic, C1 smooth, curvature-minimizing interpolator in 2D. + interpn : Interpolation on a regular grid or rectilinear grid. + RegularGridInterpolator : Interpolator on a regular or rectilinear grid + in arbitrary dimensions (`interpn` wraps this + class). + + Notes + ----- + Uses ``scipy.spatial.cKDTree`` + + .. note:: For data on a regular grid use `interpn` instead. + + Examples + -------- + We can interpolate values on a 2D plane: + + >>> from scipy.interpolate import NearestNDInterpolator + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> x = rng.random(10) - 0.5 + >>> y = rng.random(10) - 0.5 + >>> z = np.hypot(x, y) + >>> X = np.linspace(min(x), max(x)) + >>> Y = np.linspace(min(y), max(y)) + >>> X, Y = np.meshgrid(X, Y) # 2D grid for interpolation + >>> interp = NearestNDInterpolator(list(zip(x, y)), z) + >>> Z = interp(X, Y) + >>> plt.pcolormesh(X, Y, Z, shading='auto') + >>> plt.plot(x, y, "ok", label="input point") + >>> plt.legend() + >>> plt.colorbar() + >>> plt.axis("equal") + >>> plt.show() + + """ + + def __init__(self, x, y, rescale=False, tree_options=None): + NDInterpolatorBase.__init__(self, x, y, rescale=rescale, + need_contiguous=False, + need_values=False) + if tree_options is None: + tree_options = dict() + self.tree = cKDTree(self.points, **tree_options) + self.values = np.asarray(y) + + def __call__(self, *args, **query_options): + """ + Evaluate interpolator at given points. + + Parameters + ---------- + x1, x2, ... xn : array-like of float + Points where to interpolate data at. + x1, x2, ... xn can be array-like of float with broadcastable shape. + or x1 can be array-like of float with shape ``(..., ndim)`` + **query_options + This allows ``eps``, ``p``, ``distance_upper_bound``, and ``workers`` + being passed to the cKDTree's query function to be explicitly set. + See `scipy.spatial.cKDTree.query` for an overview of the different options. + + .. versionadded:: 1.12.0 + + """ + # For the sake of enabling subclassing, NDInterpolatorBase._set_xi performs + # some operations which are not required by NearestNDInterpolator.__call__, + # hence here we operate on xi directly, without calling a parent class function. + xi = _ndim_coords_from_arrays(args, ndim=self.points.shape[1]) + xi = self._check_call_shape(xi) + xi = self._scale_x(xi) + + # We need to handle two important cases: + # (1) the case where xi has trailing dimensions (..., ndim), and + # (2) the case where y has trailing dimensions + # We will first flatten xi to deal with case (1), + # do the computation in flattened array while retaining y's dimensionality, + # and then reshape the interpolated values back to match xi's shape. + + # Flatten xi for the query + xi_flat = xi.reshape(-1, xi.shape[-1]) + original_shape = xi.shape + flattened_shape = xi_flat.shape + + # if distance_upper_bound is set to not be infinite, + # then we need to consider the case where cKDtree + # does not find any points within distance_upper_bound to return. + # It marks those points as having infinte distance, which is what will be used + # below to mask the array and return only the points that were deemed + # to have a close enough neighbor to return something useful. + dist, i = self.tree.query(xi_flat, **query_options) + valid_mask = np.isfinite(dist) + + # create a holder interp_values array and fill with nans. + if self.values.ndim > 1: + interp_shape = flattened_shape[:-1] + self.values.shape[1:] + else: + interp_shape = flattened_shape[:-1] + + if np.issubdtype(self.values.dtype, np.complexfloating): + interp_values = np.full(interp_shape, np.nan, dtype=self.values.dtype) + else: + interp_values = np.full(interp_shape, np.nan) + + interp_values[valid_mask] = self.values[i[valid_mask], ...] + + if self.values.ndim > 1: + new_shape = original_shape[:-1] + self.values.shape[1:] + else: + new_shape = original_shape[:-1] + interp_values = interp_values.reshape(new_shape) + + return interp_values + + +#------------------------------------------------------------------------------ +# Convenience interface function +#------------------------------------------------------------------------------ + + +def griddata(points, values, xi, method='linear', fill_value=np.nan, + rescale=False): + """ + Interpolate unstructured D-D data. + + Parameters + ---------- + points : 2-D ndarray of floats with shape (n, D), or length D tuple of 1-D ndarrays with shape (n,). + Data point coordinates. + values : ndarray of float or complex, shape (n,) + Data values. + xi : 2-D ndarray of floats with shape (m, D), or length D tuple of ndarrays broadcastable to the same shape. + Points at which to interpolate data. + method : {'linear', 'nearest', 'cubic'}, optional + Method of interpolation. One of + + ``nearest`` + return the value at the data point closest to + the point of interpolation. See `NearestNDInterpolator` for + more details. + + ``linear`` + tessellate the input point set to N-D + simplices, and interpolate linearly on each simplex. See + `LinearNDInterpolator` for more details. + + ``cubic`` (1-D) + return the value determined from a cubic + spline. + + ``cubic`` (2-D) + return the value determined from a + piecewise cubic, continuously differentiable (C1), and + approximately curvature-minimizing polynomial surface. See + `CloughTocher2DInterpolator` for more details. + fill_value : float, optional + Value used to fill in for requested points outside of the + convex hull of the input points. If not provided, then the + default is ``nan``. This option has no effect for the + 'nearest' method. + rescale : bool, optional + Rescale points to unit cube before performing interpolation. + This is useful if some of the input dimensions have + incommensurable units and differ by many orders of magnitude. + + .. versionadded:: 0.14.0 + + Returns + ------- + ndarray + Array of interpolated values. + + See Also + -------- + LinearNDInterpolator : + Piecewise linear interpolator in N dimensions. + NearestNDInterpolator : + Nearest-neighbor interpolator in N dimensions. + CloughTocher2DInterpolator : + Piecewise cubic, C1 smooth, curvature-minimizing interpolator in 2D. + interpn : Interpolation on a regular grid or rectilinear grid. + RegularGridInterpolator : Interpolator on a regular or rectilinear grid + in arbitrary dimensions (`interpn` wraps this + class). + + Notes + ----- + + .. versionadded:: 0.9 + + .. note:: For data on a regular grid use `interpn` instead. + + Examples + -------- + + Suppose we want to interpolate the 2-D function + + >>> import numpy as np + >>> def func(x, y): + ... return x*(1-x)*np.cos(4*np.pi*x) * np.sin(4*np.pi*y**2)**2 + + on a grid in [0, 1]x[0, 1] + + >>> grid_x, grid_y = np.mgrid[0:1:100j, 0:1:200j] + + but we only know its values at 1000 data points: + + >>> rng = np.random.default_rng() + >>> points = rng.random((1000, 2)) + >>> values = func(points[:,0], points[:,1]) + + This can be done with `griddata` -- below we try out all of the + interpolation methods: + + >>> from scipy.interpolate import griddata + >>> grid_z0 = griddata(points, values, (grid_x, grid_y), method='nearest') + >>> grid_z1 = griddata(points, values, (grid_x, grid_y), method='linear') + >>> grid_z2 = griddata(points, values, (grid_x, grid_y), method='cubic') + + One can see that the exact result is reproduced by all of the + methods to some degree, but for this smooth function the piecewise + cubic interpolant gives the best results: + + >>> import matplotlib.pyplot as plt + >>> plt.subplot(221) + >>> plt.imshow(func(grid_x, grid_y).T, extent=(0,1,0,1), origin='lower') + >>> plt.plot(points[:,0], points[:,1], 'k.', ms=1) + >>> plt.title('Original') + >>> plt.subplot(222) + >>> plt.imshow(grid_z0.T, extent=(0,1,0,1), origin='lower') + >>> plt.title('Nearest') + >>> plt.subplot(223) + >>> plt.imshow(grid_z1.T, extent=(0,1,0,1), origin='lower') + >>> plt.title('Linear') + >>> plt.subplot(224) + >>> plt.imshow(grid_z2.T, extent=(0,1,0,1), origin='lower') + >>> plt.title('Cubic') + >>> plt.gcf().set_size_inches(6, 6) + >>> plt.show() + + """ # numpy/numpydoc#87 # noqa: E501 + + points = _ndim_coords_from_arrays(points) + + if points.ndim < 2: + ndim = points.ndim + else: + ndim = points.shape[-1] + + if ndim == 1 and method in ('nearest', 'linear', 'cubic'): + from ._interpolate import interp1d + points = points.ravel() + if isinstance(xi, tuple): + if len(xi) != 1: + raise ValueError("invalid number of dimensions in xi") + xi, = xi + # Sort points/values together, necessary as input for interp1d + idx = np.argsort(points) + points = points[idx] + values = values[idx] + if method == 'nearest': + fill_value = 'extrapolate' + ip = interp1d(points, values, kind=method, axis=0, bounds_error=False, + fill_value=fill_value) + return ip(xi) + elif method == 'nearest': + ip = NearestNDInterpolator(points, values, rescale=rescale) + return ip(xi) + elif method == 'linear': + ip = LinearNDInterpolator(points, values, fill_value=fill_value, + rescale=rescale) + return ip(xi) + elif method == 'cubic' and ndim == 2: + ip = CloughTocher2DInterpolator(points, values, fill_value=fill_value, + rescale=rescale) + return ip(xi) + else: + raise ValueError("Unknown interpolation method %r for " + "%d dimensional data" % (method, ndim)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_pade.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_pade.py new file mode 100644 index 0000000000000000000000000000000000000000..387ef11dde5d3ace8a15324058c10fa31899c92c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_pade.py @@ -0,0 +1,67 @@ +from numpy import zeros, asarray, eye, poly1d, hstack, r_ +from scipy import linalg + +__all__ = ["pade"] + +def pade(an, m, n=None): + """ + Return Pade approximation to a polynomial as the ratio of two polynomials. + + Parameters + ---------- + an : (N,) array_like + Taylor series coefficients. + m : int + The order of the returned approximating polynomial `q`. + n : int, optional + The order of the returned approximating polynomial `p`. By default, + the order is ``len(an)-1-m``. + + Returns + ------- + p, q : Polynomial class + The Pade approximation of the polynomial defined by `an` is + ``p(x)/q(x)``. + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import pade + >>> e_exp = [1.0, 1.0, 1.0/2.0, 1.0/6.0, 1.0/24.0, 1.0/120.0] + >>> p, q = pade(e_exp, 2) + + >>> e_exp.reverse() + >>> e_poly = np.poly1d(e_exp) + + Compare ``e_poly(x)`` and the Pade approximation ``p(x)/q(x)`` + + >>> e_poly(1) + 2.7166666666666668 + + >>> p(1)/q(1) + 2.7179487179487181 + + """ + an = asarray(an) + if n is None: + n = len(an) - 1 - m + if n < 0: + raise ValueError("Order of q must be smaller than len(an)-1.") + if n < 0: + raise ValueError("Order of p must be greater than 0.") + N = m + n + if N > len(an)-1: + raise ValueError("Order of q+p must be smaller than len(an).") + an = an[:N+1] + Akj = eye(N+1, n+1, dtype=an.dtype) + Bkj = zeros((N+1, m), dtype=an.dtype) + for row in range(1, m+1): + Bkj[row,:row] = -(an[:row])[::-1] + for row in range(m+1, N+1): + Bkj[row,:] = -(an[row-m:row])[::-1] + C = hstack((Akj, Bkj)) + pq = linalg.solve(C, an) + p = pq[:n+1] + q = r_[1.0, pq[n+1:]] + return poly1d(p[::-1]), poly1d(q[::-1]) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_polyint.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_polyint.py new file mode 100644 index 0000000000000000000000000000000000000000..6ed06d8abdba9597397295a0ca3c4a2bc3b25659 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_polyint.py @@ -0,0 +1,938 @@ +import warnings + +import numpy as np +from scipy.special import factorial +from scipy._lib._util import _asarray_validated, float_factorial, check_random_state + + +__all__ = ["KroghInterpolator", "krogh_interpolate", + "BarycentricInterpolator", "barycentric_interpolate", + "approximate_taylor_polynomial"] + + +def _isscalar(x): + """Check whether x is if a scalar type, or 0-dim""" + return np.isscalar(x) or hasattr(x, 'shape') and x.shape == () + + +class _Interpolator1D: + """ + Common features in univariate interpolation + + Deal with input data type and interpolation axis rolling. The + actual interpolator can assume the y-data is of shape (n, r) where + `n` is the number of x-points, and `r` the number of variables, + and use self.dtype as the y-data type. + + Attributes + ---------- + _y_axis + Axis along which the interpolation goes in the original array + _y_extra_shape + Additional trailing shape of the input arrays, excluding + the interpolation axis. + dtype + Dtype of the y-data arrays. Can be set via _set_dtype, which + forces it to be float or complex. + + Methods + ------- + __call__ + _prepare_x + _finish_y + _reshape_yi + _set_yi + _set_dtype + _evaluate + + """ + + __slots__ = ('_y_axis', '_y_extra_shape', 'dtype') + + def __init__(self, xi=None, yi=None, axis=None): + self._y_axis = axis + self._y_extra_shape = None + self.dtype = None + if yi is not None: + self._set_yi(yi, xi=xi, axis=axis) + + def __call__(self, x): + """ + Evaluate the interpolant + + Parameters + ---------- + x : array_like + Point or points at which to evaluate the interpolant. + + Returns + ------- + y : array_like + Interpolated values. Shape is determined by replacing + the interpolation axis in the original array with the shape of `x`. + + Notes + ----- + Input values `x` must be convertible to `float` values like `int` + or `float`. + + """ + x, x_shape = self._prepare_x(x) + y = self._evaluate(x) + return self._finish_y(y, x_shape) + + def _evaluate(self, x): + """ + Actually evaluate the value of the interpolator. + """ + raise NotImplementedError() + + def _prepare_x(self, x): + """Reshape input x array to 1-D""" + x = _asarray_validated(x, check_finite=False, as_inexact=True) + x_shape = x.shape + return x.ravel(), x_shape + + def _finish_y(self, y, x_shape): + """Reshape interpolated y back to an N-D array similar to initial y""" + y = y.reshape(x_shape + self._y_extra_shape) + if self._y_axis != 0 and x_shape != (): + nx = len(x_shape) + ny = len(self._y_extra_shape) + s = (list(range(nx, nx + self._y_axis)) + + list(range(nx)) + list(range(nx+self._y_axis, nx+ny))) + y = y.transpose(s) + return y + + def _reshape_yi(self, yi, check=False): + yi = np.moveaxis(np.asarray(yi), self._y_axis, 0) + if check and yi.shape[1:] != self._y_extra_shape: + ok_shape = "{!r} + (N,) + {!r}".format(self._y_extra_shape[-self._y_axis:], + self._y_extra_shape[:-self._y_axis]) + raise ValueError("Data must be of shape %s" % ok_shape) + return yi.reshape((yi.shape[0], -1)) + + def _set_yi(self, yi, xi=None, axis=None): + if axis is None: + axis = self._y_axis + if axis is None: + raise ValueError("no interpolation axis specified") + + yi = np.asarray(yi) + + shape = yi.shape + if shape == (): + shape = (1,) + if xi is not None and shape[axis] != len(xi): + raise ValueError("x and y arrays must be equal in length along " + "interpolation axis.") + + self._y_axis = (axis % yi.ndim) + self._y_extra_shape = yi.shape[:self._y_axis] + yi.shape[self._y_axis+1:] + self.dtype = None + self._set_dtype(yi.dtype) + + def _set_dtype(self, dtype, union=False): + if np.issubdtype(dtype, np.complexfloating) \ + or np.issubdtype(self.dtype, np.complexfloating): + self.dtype = np.complex128 + else: + if not union or self.dtype != np.complex128: + self.dtype = np.float64 + + +class _Interpolator1DWithDerivatives(_Interpolator1D): + def derivatives(self, x, der=None): + """ + Evaluate several derivatives of the polynomial at the point `x` + + Produce an array of derivatives evaluated at the point `x`. + + Parameters + ---------- + x : array_like + Point or points at which to evaluate the derivatives + der : int or list or None, optional + How many derivatives to evaluate, or None for all potentially + nonzero derivatives (that is, a number equal to the number + of points), or a list of derivatives to evaluate. This number + includes the function value as the '0th' derivative. + + Returns + ------- + d : ndarray + Array with derivatives; ``d[j]`` contains the jth derivative. + Shape of ``d[j]`` is determined by replacing the interpolation + axis in the original array with the shape of `x`. + + Examples + -------- + >>> from scipy.interpolate import KroghInterpolator + >>> KroghInterpolator([0,0,0],[1,2,3]).derivatives(0) + array([1.0,2.0,3.0]) + >>> KroghInterpolator([0,0,0],[1,2,3]).derivatives([0,0]) + array([[1.0,1.0], + [2.0,2.0], + [3.0,3.0]]) + + """ + x, x_shape = self._prepare_x(x) + y = self._evaluate_derivatives(x, der) + + y = y.reshape((y.shape[0],) + x_shape + self._y_extra_shape) + if self._y_axis != 0 and x_shape != (): + nx = len(x_shape) + ny = len(self._y_extra_shape) + s = ([0] + list(range(nx+1, nx + self._y_axis+1)) + + list(range(1, nx+1)) + + list(range(nx+1+self._y_axis, nx+ny+1))) + y = y.transpose(s) + return y + + def derivative(self, x, der=1): + """ + Evaluate a single derivative of the polynomial at the point `x`. + + Parameters + ---------- + x : array_like + Point or points at which to evaluate the derivatives + + der : integer, optional + Which derivative to evaluate (default: first derivative). + This number includes the function value as 0th derivative. + + Returns + ------- + d : ndarray + Derivative interpolated at the x-points. Shape of `d` is + determined by replacing the interpolation axis in the + original array with the shape of `x`. + + Notes + ----- + This may be computed by evaluating all derivatives up to the desired + one (using self.derivatives()) and then discarding the rest. + + """ + x, x_shape = self._prepare_x(x) + y = self._evaluate_derivatives(x, der+1) + return self._finish_y(y[der], x_shape) + + def _evaluate_derivatives(self, x, der=None): + """ + Actually evaluate the derivatives. + + Parameters + ---------- + x : array_like + 1D array of points at which to evaluate the derivatives + der : integer, optional + The number of derivatives to evaluate, from 'order 0' (der=1) + to order der-1. If omitted, return all possibly-non-zero + derivatives, ie 0 to order n-1. + + Returns + ------- + d : ndarray + Array of shape ``(der, x.size, self.yi.shape[1])`` containing + the derivatives from 0 to der-1 + """ + raise NotImplementedError() + + +class KroghInterpolator(_Interpolator1DWithDerivatives): + """ + Interpolating polynomial for a set of points. + + The polynomial passes through all the pairs ``(xi, yi)``. One may + additionally specify a number of derivatives at each point `xi`; + this is done by repeating the value `xi` and specifying the + derivatives as successive `yi` values. + + Allows evaluation of the polynomial and all its derivatives. + For reasons of numerical stability, this function does not compute + the coefficients of the polynomial, although they can be obtained + by evaluating all the derivatives. + + Parameters + ---------- + xi : array_like, shape (npoints, ) + Known x-coordinates. Must be sorted in increasing order. + yi : array_like, shape (..., npoints, ...) + Known y-coordinates. When an xi occurs two or more times in + a row, the corresponding yi's represent derivative values. The length of `yi` + along the interpolation axis must be equal to the length of `xi`. Use the + `axis` parameter to select the correct axis. + axis : int, optional + Axis in the `yi` array corresponding to the x-coordinate values. Defaults to + ``axis=0``. + + Notes + ----- + Be aware that the algorithms implemented here are not necessarily + the most numerically stable known. Moreover, even in a world of + exact computation, unless the x coordinates are chosen very + carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - + polynomial interpolation itself is a very ill-conditioned process + due to the Runge phenomenon. In general, even with well-chosen + x values, degrees higher than about thirty cause problems with + numerical instability in this code. + + Based on [1]_. + + References + ---------- + .. [1] Krogh, "Efficient Algorithms for Polynomial Interpolation + and Numerical Differentiation", 1970. + + Examples + -------- + To produce a polynomial that is zero at 0 and 1 and has + derivative 2 at 0, call + + >>> from scipy.interpolate import KroghInterpolator + >>> KroghInterpolator([0,0,1],[0,2,0]) + + This constructs the quadratic :math:`2x^2-2x`. The derivative condition + is indicated by the repeated zero in the `xi` array; the corresponding + yi values are 0, the function value, and 2, the derivative value. + + For another example, given `xi`, `yi`, and a derivative `ypi` for each + point, appropriate arrays can be constructed as: + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> xi = np.linspace(0, 1, 5) + >>> yi, ypi = rng.random((2, 5)) + >>> xi_k, yi_k = np.repeat(xi, 2), np.ravel(np.dstack((yi,ypi))) + >>> KroghInterpolator(xi_k, yi_k) + + To produce a vector-valued polynomial, supply a higher-dimensional + array for `yi`: + + >>> KroghInterpolator([0,1],[[2,3],[4,5]]) + + This constructs a linear polynomial giving (2,3) at 0 and (4,5) at 1. + + """ + + def __init__(self, xi, yi, axis=0): + super().__init__(xi, yi, axis) + + self.xi = np.asarray(xi) + self.yi = self._reshape_yi(yi) + self.n, self.r = self.yi.shape + + if (deg := self.xi.size) > 30: + warnings.warn(f"{deg} degrees provided, degrees higher than about" + " thirty cause problems with numerical instability " + "with 'KroghInterpolator'", stacklevel=2) + + c = np.zeros((self.n+1, self.r), dtype=self.dtype) + c[0] = self.yi[0] + Vk = np.zeros((self.n, self.r), dtype=self.dtype) + for k in range(1, self.n): + s = 0 + while s <= k and xi[k-s] == xi[k]: + s += 1 + s -= 1 + Vk[0] = self.yi[k]/float_factorial(s) + for i in range(k-s): + if xi[i] == xi[k]: + raise ValueError("Elements of `xi` can't be equal.") + if s == 0: + Vk[i+1] = (c[i]-Vk[i])/(xi[i]-xi[k]) + else: + Vk[i+1] = (Vk[i+1]-Vk[i])/(xi[i]-xi[k]) + c[k] = Vk[k-s] + self.c = c + + def _evaluate(self, x): + pi = 1 + p = np.zeros((len(x), self.r), dtype=self.dtype) + p += self.c[0,np.newaxis,:] + for k in range(1, self.n): + w = x - self.xi[k-1] + pi = w*pi + p += pi[:,np.newaxis] * self.c[k] + return p + + def _evaluate_derivatives(self, x, der=None): + n = self.n + r = self.r + + if der is None: + der = self.n + + pi = np.zeros((n, len(x))) + w = np.zeros((n, len(x))) + pi[0] = 1 + p = np.zeros((len(x), self.r), dtype=self.dtype) + p += self.c[0, np.newaxis, :] + + for k in range(1, n): + w[k-1] = x - self.xi[k-1] + pi[k] = w[k-1] * pi[k-1] + p += pi[k, :, np.newaxis] * self.c[k] + + cn = np.zeros((max(der, n+1), len(x), r), dtype=self.dtype) + cn[:n+1, :, :] += self.c[:n+1, np.newaxis, :] + cn[0] = p + for k in range(1, n): + for i in range(1, n-k+1): + pi[i] = w[k+i-1]*pi[i-1] + pi[i] + cn[k] = cn[k] + pi[i, :, np.newaxis]*cn[k+i] + cn[k] *= float_factorial(k) + + cn[n, :, :] = 0 + return cn[:der] + + +def krogh_interpolate(xi, yi, x, der=0, axis=0): + """ + Convenience function for polynomial interpolation. + + See `KroghInterpolator` for more details. + + Parameters + ---------- + xi : array_like + Interpolation points (known x-coordinates). + yi : array_like + Known y-coordinates, of shape ``(xi.size, R)``. Interpreted as + vectors of length R, or scalars if R=1. + x : array_like + Point or points at which to evaluate the derivatives. + der : int or list or None, optional + How many derivatives to evaluate, or None for all potentially + nonzero derivatives (that is, a number equal to the number + of points), or a list of derivatives to evaluate. This number + includes the function value as the '0th' derivative. + axis : int, optional + Axis in the `yi` array corresponding to the x-coordinate values. + + Returns + ------- + d : ndarray + If the interpolator's values are R-D then the + returned array will be the number of derivatives by N by R. + If `x` is a scalar, the middle dimension will be dropped; if + the `yi` are scalars then the last dimension will be dropped. + + See Also + -------- + KroghInterpolator : Krogh interpolator + + Notes + ----- + Construction of the interpolating polynomial is a relatively expensive + process. If you want to evaluate it repeatedly consider using the class + KroghInterpolator (which is what this function uses). + + Examples + -------- + We can interpolate 2D observed data using Krogh interpolation: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import krogh_interpolate + >>> x_observed = np.linspace(0.0, 10.0, 11) + >>> y_observed = np.sin(x_observed) + >>> x = np.linspace(min(x_observed), max(x_observed), num=100) + >>> y = krogh_interpolate(x_observed, y_observed, x) + >>> plt.plot(x_observed, y_observed, "o", label="observation") + >>> plt.plot(x, y, label="krogh interpolation") + >>> plt.legend() + >>> plt.show() + """ + + P = KroghInterpolator(xi, yi, axis=axis) + if der == 0: + return P(x) + elif _isscalar(der): + return P.derivative(x, der=der) + else: + return P.derivatives(x, der=np.amax(der)+1)[der] + + +def approximate_taylor_polynomial(f,x,degree,scale,order=None): + """ + Estimate the Taylor polynomial of f at x by polynomial fitting. + + Parameters + ---------- + f : callable + The function whose Taylor polynomial is sought. Should accept + a vector of `x` values. + x : scalar + The point at which the polynomial is to be evaluated. + degree : int + The degree of the Taylor polynomial + scale : scalar + The width of the interval to use to evaluate the Taylor polynomial. + Function values spread over a range this wide are used to fit the + polynomial. Must be chosen carefully. + order : int or None, optional + The order of the polynomial to be used in the fitting; `f` will be + evaluated ``order+1`` times. If None, use `degree`. + + Returns + ------- + p : poly1d instance + The Taylor polynomial (translated to the origin, so that + for example p(0)=f(x)). + + Notes + ----- + The appropriate choice of "scale" is a trade-off; too large and the + function differs from its Taylor polynomial too much to get a good + answer, too small and round-off errors overwhelm the higher-order terms. + The algorithm used becomes numerically unstable around order 30 even + under ideal circumstances. + + Choosing order somewhat larger than degree may improve the higher-order + terms. + + Examples + -------- + We can calculate Taylor approximation polynomials of sin function with + various degrees: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import approximate_taylor_polynomial + >>> x = np.linspace(-10.0, 10.0, num=100) + >>> plt.plot(x, np.sin(x), label="sin curve") + >>> for degree in np.arange(1, 15, step=2): + ... sin_taylor = approximate_taylor_polynomial(np.sin, 0, degree, 1, + ... order=degree + 2) + ... plt.plot(x, sin_taylor(x), label=f"degree={degree}") + >>> plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', + ... borderaxespad=0.0, shadow=True) + >>> plt.tight_layout() + >>> plt.axis([-10, 10, -10, 10]) + >>> plt.show() + + """ + if order is None: + order = degree + + n = order+1 + # Choose n points that cluster near the endpoints of the interval in + # a way that avoids the Runge phenomenon. Ensure, by including the + # endpoint or not as appropriate, that one point always falls at x + # exactly. + xs = scale*np.cos(np.linspace(0,np.pi,n,endpoint=n % 1)) + x + + P = KroghInterpolator(xs, f(xs)) + d = P.derivatives(x,der=degree+1) + + return np.poly1d((d/factorial(np.arange(degree+1)))[::-1]) + + +class BarycentricInterpolator(_Interpolator1DWithDerivatives): + r"""Interpolating polynomial for a set of points. + + Constructs a polynomial that passes through a given set of points. + Allows evaluation of the polynomial and all its derivatives, + efficient changing of the y-values to be interpolated, + and updating by adding more x- and y-values. + + For reasons of numerical stability, this function does not compute + the coefficients of the polynomial. + + The values `yi` need to be provided before the function is + evaluated, but none of the preprocessing depends on them, so rapid + updates are possible. + + Parameters + ---------- + xi : array_like, shape (npoints, ) + 1-D array of x coordinates of the points the polynomial + should pass through + yi : array_like, shape (..., npoints, ...), optional + N-D array of y coordinates of the points the polynomial should pass through. + If None, the y values will be supplied later via the `set_y` method. + The length of `yi` along the interpolation axis must be equal to the length + of `xi`. Use the ``axis`` parameter to select correct axis. + axis : int, optional + Axis in the yi array corresponding to the x-coordinate values. Defaults + to ``axis=0``. + wi : array_like, optional + The barycentric weights for the chosen interpolation points `xi`. + If absent or None, the weights will be computed from `xi` (default). + This allows for the reuse of the weights `wi` if several interpolants + are being calculated using the same nodes `xi`, without re-computation. + random_state : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Notes + ----- + This class uses a "barycentric interpolation" method that treats + the problem as a special case of rational function interpolation. + This algorithm is quite stable, numerically, but even in a world of + exact computation, unless the x coordinates are chosen very + carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - + polynomial interpolation itself is a very ill-conditioned process + due to the Runge phenomenon. + + Based on Berrut and Trefethen 2004, "Barycentric Lagrange Interpolation". + + Examples + -------- + To produce a quintic barycentric interpolant approximating the function + :math:`\sin x`, and its first four derivatives, using six randomly-spaced + nodes in :math:`(0, \frac{\pi}{2})`: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import BarycentricInterpolator + >>> rng = np.random.default_rng() + >>> xi = rng.random(6) * np.pi/2 + >>> f, f_d1, f_d2, f_d3, f_d4 = np.sin, np.cos, lambda x: -np.sin(x), lambda x: -np.cos(x), np.sin + >>> P = BarycentricInterpolator(xi, f(xi), random_state=rng) + >>> fig, axs = plt.subplots(5, 1, sharex=True, layout='constrained', figsize=(7,10)) + >>> x = np.linspace(0, np.pi, 100) + >>> axs[0].plot(x, P(x), 'r:', x, f(x), 'k--', xi, f(xi), 'xk') + >>> axs[1].plot(x, P.derivative(x), 'r:', x, f_d1(x), 'k--', xi, f_d1(xi), 'xk') + >>> axs[2].plot(x, P.derivative(x, 2), 'r:', x, f_d2(x), 'k--', xi, f_d2(xi), 'xk') + >>> axs[3].plot(x, P.derivative(x, 3), 'r:', x, f_d3(x), 'k--', xi, f_d3(xi), 'xk') + >>> axs[4].plot(x, P.derivative(x, 4), 'r:', x, f_d4(x), 'k--', xi, f_d4(xi), 'xk') + >>> axs[0].set_xlim(0, np.pi) + >>> axs[4].set_xlabel(r"$x$") + >>> axs[4].set_xticks([i * np.pi / 4 for i in range(5)], + ... ["0", r"$\frac{\pi}{4}$", r"$\frac{\pi}{2}$", r"$\frac{3\pi}{4}$", r"$\pi$"]) + >>> axs[0].set_ylabel("$f(x)$") + >>> axs[1].set_ylabel("$f'(x)$") + >>> axs[2].set_ylabel("$f''(x)$") + >>> axs[3].set_ylabel("$f^{(3)}(x)$") + >>> axs[4].set_ylabel("$f^{(4)}(x)$") + >>> labels = ['Interpolation nodes', 'True function $f$', 'Barycentric interpolation'] + >>> axs[0].legend(axs[0].get_lines()[::-1], labels, bbox_to_anchor=(0., 1.02, 1., .102), + ... loc='lower left', ncols=3, mode="expand", borderaxespad=0., frameon=False) + >>> plt.show() + """ # numpy/numpydoc#87 # noqa: E501 + + def __init__(self, xi, yi=None, axis=0, *, wi=None, random_state=None): + super().__init__(xi, yi, axis) + + random_state = check_random_state(random_state) + + self.xi = np.asarray(xi, dtype=np.float64) + self.set_yi(yi) + self.n = len(self.xi) + + # cache derivative object to avoid re-computing the weights with every call. + self._diff_cij = None + + if wi is not None: + self.wi = wi + else: + # See page 510 of Berrut and Trefethen 2004 for an explanation of the + # capacity scaling and the suggestion of using a random permutation of + # the input factors. + # At the moment, the permutation is not performed for xi that are + # appended later through the add_xi interface. It's not clear to me how + # to implement that and it seems that most situations that require + # these numerical stability improvements will be able to provide all + # the points to the constructor. + self._inv_capacity = 4.0 / (np.max(self.xi) - np.min(self.xi)) + permute = random_state.permutation(self.n, ) + inv_permute = np.zeros(self.n, dtype=np.int32) + inv_permute[permute] = np.arange(self.n) + self.wi = np.zeros(self.n) + + for i in range(self.n): + dist = self._inv_capacity * (self.xi[i] - self.xi[permute]) + dist[inv_permute[i]] = 1.0 + prod = np.prod(dist) + if prod == 0.0: + raise ValueError("Interpolation points xi must be" + " distinct.") + self.wi[i] = 1.0 / prod + + def set_yi(self, yi, axis=None): + """ + Update the y values to be interpolated + + The barycentric interpolation algorithm requires the calculation + of weights, but these depend only on the `xi`. The `yi` can be changed + at any time. + + Parameters + ---------- + yi : array_like + The y-coordinates of the points the polynomial will pass through. + If None, the y values must be supplied later. + axis : int, optional + Axis in the `yi` array corresponding to the x-coordinate values. + + """ + if yi is None: + self.yi = None + return + self._set_yi(yi, xi=self.xi, axis=axis) + self.yi = self._reshape_yi(yi) + self.n, self.r = self.yi.shape + self._diff_baryint = None + + def add_xi(self, xi, yi=None): + """ + Add more x values to the set to be interpolated + + The barycentric interpolation algorithm allows easy updating by + adding more points for the polynomial to pass through. + + Parameters + ---------- + xi : array_like + The x coordinates of the points that the polynomial should pass + through. + yi : array_like, optional + The y coordinates of the points the polynomial should pass through. + Should have shape ``(xi.size, R)``; if R > 1 then the polynomial is + vector-valued. + If `yi` is not given, the y values will be supplied later. `yi` + should be given if and only if the interpolator has y values + specified. + + Notes + ----- + The new points added by `add_xi` are not randomly permuted + so there is potential for numerical instability, + especially for a large number of points. If this + happens, please reconstruct interpolation from scratch instead. + """ + if yi is not None: + if self.yi is None: + raise ValueError("No previous yi value to update!") + yi = self._reshape_yi(yi, check=True) + self.yi = np.vstack((self.yi,yi)) + else: + if self.yi is not None: + raise ValueError("No update to yi provided!") + old_n = self.n + self.xi = np.concatenate((self.xi,xi)) + self.n = len(self.xi) + self.wi **= -1 + old_wi = self.wi + self.wi = np.zeros(self.n) + self.wi[:old_n] = old_wi + for j in range(old_n, self.n): + self.wi[:j] *= self._inv_capacity * (self.xi[j]-self.xi[:j]) + self.wi[j] = np.multiply.reduce( + self._inv_capacity * (self.xi[:j]-self.xi[j]) + ) + self.wi **= -1 + self._diff_cij = None + self._diff_baryint = None + + def __call__(self, x): + """Evaluate the interpolating polynomial at the points x + + Parameters + ---------- + x : array_like + Point or points at which to evaluate the interpolant. + + Returns + ------- + y : array_like + Interpolated values. Shape is determined by replacing + the interpolation axis in the original array with the shape of `x`. + + Notes + ----- + Currently the code computes an outer product between `x` and the + weights, that is, it constructs an intermediate array of size + ``(N, len(x))``, where N is the degree of the polynomial. + """ + return _Interpolator1D.__call__(self, x) + + def _evaluate(self, x): + if x.size == 0: + p = np.zeros((0, self.r), dtype=self.dtype) + else: + c = x[..., np.newaxis] - self.xi + z = c == 0 + c[z] = 1 + c = self.wi / c + with np.errstate(divide='ignore'): + p = np.dot(c, self.yi) / np.sum(c, axis=-1)[..., np.newaxis] + # Now fix where x==some xi + r = np.nonzero(z) + if len(r) == 1: # evaluation at a scalar + if len(r[0]) > 0: # equals one of the points + p = self.yi[r[0][0]] + else: + p[r[:-1]] = self.yi[r[-1]] + return p + + def derivative(self, x, der=1): + """ + Evaluate a single derivative of the polynomial at the point x. + + Parameters + ---------- + x : array_like + Point or points at which to evaluate the derivatives + der : integer, optional + Which derivative to evaluate (default: first derivative). + This number includes the function value as 0th derivative. + + Returns + ------- + d : ndarray + Derivative interpolated at the x-points. Shape of `d` is + determined by replacing the interpolation axis in the + original array with the shape of `x`. + """ + x, x_shape = self._prepare_x(x) + y = self._evaluate_derivatives(x, der+1, all_lower=False) + return self._finish_y(y, x_shape) + + def _evaluate_derivatives(self, x, der=None, all_lower=True): + # NB: der here is not the order of the highest derivative; + # instead, it is the size of the derivatives matrix that + # would be returned with all_lower=True, including the + # '0th' derivative (the undifferentiated function). + # E.g. to evaluate the 5th derivative alone, call + # _evaluate_derivatives(x, der=6, all_lower=False). + + if (not all_lower) and (x.size == 0 or self.r == 0): + return np.zeros((0, self.r), dtype=self.dtype) + + if (not all_lower) and der == 1: + return self._evaluate(x) + + if (not all_lower) and (der > self.n): + return np.zeros((len(x), self.r), dtype=self.dtype) + + if der is None: + der = self.n + + if all_lower and (x.size == 0 or self.r == 0): + return np.zeros((der, len(x), self.r), dtype=self.dtype) + + if self._diff_cij is None: + # c[i,j] = xi[i] - xi[j] + c = self.xi[:, np.newaxis] - self.xi + + # avoid division by 0 (diagonal entries are so far zero by construction) + np.fill_diagonal(c, 1) + + # c[i,j] = (w[j] / w[i]) / (xi[i] - xi[j]) (equation 9.4) + c = self.wi/ (c * self.wi[..., np.newaxis]) + + # fill in correct diagonal entries: each column sums to 0 + np.fill_diagonal(c, 0) + + # calculate diagonal + # c[j,j] = -sum_{i != j} c[i,j] (equation 9.5) + d = -c.sum(axis=1) + # c[i,j] = l_j(x_i) + np.fill_diagonal(c, d) + + self._diff_cij = c + + if self._diff_baryint is None: + # initialise and cache derivative interpolator and cijs; + # reuse weights wi (which depend only on interpolation points xi), + # to avoid unnecessary re-computation + self._diff_baryint = BarycentricInterpolator(xi=self.xi, + yi=self._diff_cij @ self.yi, + wi=self.wi) + self._diff_baryint._diff_cij = self._diff_cij + + if all_lower: + # assemble matrix of derivatives from order 0 to order der-1, + # in the format required by _Interpolator1DWithDerivatives. + cn = np.zeros((der, len(x), self.r), dtype=self.dtype) + for d in range(der): + cn[d, :, :] = self._evaluate_derivatives(x, d+1, all_lower=False) + return cn + + # recursively evaluate only the derivative requested + return self._diff_baryint._evaluate_derivatives(x, der-1, all_lower=False) + + +def barycentric_interpolate(xi, yi, x, axis=0, *, der=0): + """ + Convenience function for polynomial interpolation. + + Constructs a polynomial that passes through a given set of points, + then evaluates the polynomial. For reasons of numerical stability, + this function does not compute the coefficients of the polynomial. + + This function uses a "barycentric interpolation" method that treats + the problem as a special case of rational function interpolation. + This algorithm is quite stable, numerically, but even in a world of + exact computation, unless the `x` coordinates are chosen very + carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - + polynomial interpolation itself is a very ill-conditioned process + due to the Runge phenomenon. + + Parameters + ---------- + xi : array_like + 1-D array of x coordinates of the points the polynomial should + pass through + yi : array_like + The y coordinates of the points the polynomial should pass through. + x : scalar or array_like + Point or points at which to evaluate the interpolant. + der : int or list or None, optional + How many derivatives to evaluate, or None for all potentially + nonzero derivatives (that is, a number equal to the number + of points), or a list of derivatives to evaluate. This number + includes the function value as the '0th' derivative. + axis : int, optional + Axis in the `yi` array corresponding to the x-coordinate values. + + Returns + ------- + y : scalar or array_like + Interpolated values. Shape is determined by replacing + the interpolation axis in the original array with the shape of `x`. + + See Also + -------- + BarycentricInterpolator : Barycentric interpolator + + Notes + ----- + Construction of the interpolation weights is a relatively slow process. + If you want to call this many times with the same xi (but possibly + varying yi or x) you should use the class `BarycentricInterpolator`. + This is what this function uses internally. + + Examples + -------- + We can interpolate 2D observed data using barycentric interpolation: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import barycentric_interpolate + >>> x_observed = np.linspace(0.0, 10.0, 11) + >>> y_observed = np.sin(x_observed) + >>> x = np.linspace(min(x_observed), max(x_observed), num=100) + >>> y = barycentric_interpolate(x_observed, y_observed, x) + >>> plt.plot(x_observed, y_observed, "o", label="observation") + >>> plt.plot(x, y, label="barycentric interpolation") + >>> plt.legend() + >>> plt.show() + + """ + P = BarycentricInterpolator(xi, yi, axis=axis) + if der == 0: + return P(x) + elif _isscalar(der): + return P.derivative(x, der=der) + else: + return P.derivatives(x, der=np.amax(der)+1)[der] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbf.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbf.py new file mode 100644 index 0000000000000000000000000000000000000000..ed52230dd1cce678e56ca4427e10bafd07e501c0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbf.py @@ -0,0 +1,290 @@ +"""rbf - Radial basis functions for interpolation/smoothing scattered N-D data. + +Written by John Travers , February 2007 +Based closely on Matlab code by Alex Chirokov +Additional, large, improvements by Robert Hetland +Some additional alterations by Travis Oliphant +Interpolation with multi-dimensional target domain by Josua Sassen + +Permission to use, modify, and distribute this software is given under the +terms of the SciPy (BSD style) license. See LICENSE.txt that came with +this distribution for specifics. + +NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +Copyright (c) 2006-2007, Robert Hetland +Copyright (c) 2007, John Travers + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Robert Hetland nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +import numpy as np + +from scipy import linalg +from scipy.special import xlogy +from scipy.spatial.distance import cdist, pdist, squareform + +__all__ = ['Rbf'] + + +class Rbf: + """ + Rbf(*args, **kwargs) + + A class for radial basis function interpolation of functions from + N-D scattered data to an M-D domain. + + .. legacy:: class + + `Rbf` is legacy code, for new usage please use `RBFInterpolator` + instead. + + Parameters + ---------- + *args : arrays + x, y, z, ..., d, where x, y, z, ... are the coordinates of the nodes + and d is the array of values at the nodes + function : str or callable, optional + The radial basis function, based on the radius, r, given by the norm + (default is Euclidean distance); the default is 'multiquadric':: + + 'multiquadric': sqrt((r/self.epsilon)**2 + 1) + 'inverse': 1.0/sqrt((r/self.epsilon)**2 + 1) + 'gaussian': exp(-(r/self.epsilon)**2) + 'linear': r + 'cubic': r**3 + 'quintic': r**5 + 'thin_plate': r**2 * log(r) + + If callable, then it must take 2 arguments (self, r). The epsilon + parameter will be available as self.epsilon. Other keyword + arguments passed in will be available as well. + + epsilon : float, optional + Adjustable constant for gaussian or multiquadrics functions + - defaults to approximate average distance between nodes (which is + a good start). + smooth : float, optional + Values greater than zero increase the smoothness of the + approximation. 0 is for interpolation (default), the function will + always go through the nodal points in this case. + norm : str, callable, optional + A function that returns the 'distance' between two points, with + inputs as arrays of positions (x, y, z, ...), and an output as an + array of distance. E.g., the default: 'euclidean', such that the result + is a matrix of the distances from each point in ``x1`` to each point in + ``x2``. For more options, see documentation of + `scipy.spatial.distances.cdist`. + mode : str, optional + Mode of the interpolation, can be '1-D' (default) or 'N-D'. When it is + '1-D' the data `d` will be considered as 1-D and flattened + internally. When it is 'N-D' the data `d` is assumed to be an array of + shape (n_samples, m), where m is the dimension of the target domain. + + + Attributes + ---------- + N : int + The number of data points (as determined by the input arrays). + di : ndarray + The 1-D array of data values at each of the data coordinates `xi`. + xi : ndarray + The 2-D array of data coordinates. + function : str or callable + The radial basis function. See description under Parameters. + epsilon : float + Parameter used by gaussian or multiquadrics functions. See Parameters. + smooth : float + Smoothing parameter. See description under Parameters. + norm : str or callable + The distance function. See description under Parameters. + mode : str + Mode of the interpolation. See description under Parameters. + nodes : ndarray + A 1-D array of node values for the interpolation. + A : internal property, do not use + + See Also + -------- + RBFInterpolator + + Examples + -------- + >>> import numpy as np + >>> from scipy.interpolate import Rbf + >>> rng = np.random.default_rng() + >>> x, y, z, d = rng.random((4, 50)) + >>> rbfi = Rbf(x, y, z, d) # radial basis function interpolator instance + >>> xi = yi = zi = np.linspace(0, 1, 20) + >>> di = rbfi(xi, yi, zi) # interpolated values + >>> di.shape + (20,) + + """ + # Available radial basis functions that can be selected as strings; + # they all start with _h_ (self._init_function relies on that) + def _h_multiquadric(self, r): + return np.sqrt((1.0/self.epsilon*r)**2 + 1) + + def _h_inverse_multiquadric(self, r): + return 1.0/np.sqrt((1.0/self.epsilon*r)**2 + 1) + + def _h_gaussian(self, r): + return np.exp(-(1.0/self.epsilon*r)**2) + + def _h_linear(self, r): + return r + + def _h_cubic(self, r): + return r**3 + + def _h_quintic(self, r): + return r**5 + + def _h_thin_plate(self, r): + return xlogy(r**2, r) + + # Setup self._function and do smoke test on initial r + def _init_function(self, r): + if isinstance(self.function, str): + self.function = self.function.lower() + _mapped = {'inverse': 'inverse_multiquadric', + 'inverse multiquadric': 'inverse_multiquadric', + 'thin-plate': 'thin_plate'} + if self.function in _mapped: + self.function = _mapped[self.function] + + func_name = "_h_" + self.function + if hasattr(self, func_name): + self._function = getattr(self, func_name) + else: + functionlist = [x[3:] for x in dir(self) + if x.startswith('_h_')] + raise ValueError("function must be a callable or one of " + + ", ".join(functionlist)) + self._function = getattr(self, "_h_"+self.function) + elif callable(self.function): + allow_one = False + if hasattr(self.function, 'func_code') or \ + hasattr(self.function, '__code__'): + val = self.function + allow_one = True + elif hasattr(self.function, "__call__"): + val = self.function.__call__.__func__ + else: + raise ValueError("Cannot determine number of arguments to " + "function") + + argcount = val.__code__.co_argcount + if allow_one and argcount == 1: + self._function = self.function + elif argcount == 2: + self._function = self.function.__get__(self, Rbf) + else: + raise ValueError("Function argument must take 1 or 2 " + "arguments.") + + a0 = self._function(r) + if a0.shape != r.shape: + raise ValueError("Callable must take array and return array of " + "the same shape") + return a0 + + def __init__(self, *args, **kwargs): + # `args` can be a variable number of arrays; we flatten them and store + # them as a single 2-D array `xi` of shape (n_args-1, array_size), + # plus a 1-D array `di` for the values. + # All arrays must have the same number of elements + self.xi = np.asarray([np.asarray(a, dtype=np.float64).flatten() + for a in args[:-1]]) + self.N = self.xi.shape[-1] + + self.mode = kwargs.pop('mode', '1-D') + + if self.mode == '1-D': + self.di = np.asarray(args[-1]).flatten() + self._target_dim = 1 + elif self.mode == 'N-D': + self.di = np.asarray(args[-1]) + self._target_dim = self.di.shape[-1] + else: + raise ValueError("Mode has to be 1-D or N-D.") + + if not all([x.size == self.di.shape[0] for x in self.xi]): + raise ValueError("All arrays must be equal length.") + + self.norm = kwargs.pop('norm', 'euclidean') + self.epsilon = kwargs.pop('epsilon', None) + if self.epsilon is None: + # default epsilon is the "the average distance between nodes" based + # on a bounding hypercube + ximax = np.amax(self.xi, axis=1) + ximin = np.amin(self.xi, axis=1) + edges = ximax - ximin + edges = edges[np.nonzero(edges)] + self.epsilon = np.power(np.prod(edges)/self.N, 1.0/edges.size) + + self.smooth = kwargs.pop('smooth', 0.0) + self.function = kwargs.pop('function', 'multiquadric') + + # attach anything left in kwargs to self for use by any user-callable + # function or to save on the object returned. + for item, value in kwargs.items(): + setattr(self, item, value) + + # Compute weights + if self._target_dim > 1: # If we have more than one target dimension, + # we first factorize the matrix + self.nodes = np.zeros((self.N, self._target_dim), dtype=self.di.dtype) + lu, piv = linalg.lu_factor(self.A) + for i in range(self._target_dim): + self.nodes[:, i] = linalg.lu_solve((lu, piv), self.di[:, i]) + else: + self.nodes = linalg.solve(self.A, self.di) + + @property + def A(self): + # this only exists for backwards compatibility: self.A was available + # and, at least technically, public. + r = squareform(pdist(self.xi.T, self.norm)) # Pairwise norm + return self._init_function(r) - np.eye(self.N)*self.smooth + + def _call_norm(self, x1, x2): + return cdist(x1.T, x2.T, self.norm) + + def __call__(self, *args): + args = [np.asarray(x) for x in args] + if not all([x.shape == y.shape for x in args for y in args]): + raise ValueError("Array lengths must be equal") + if self._target_dim > 1: + shp = args[0].shape + (self._target_dim,) + else: + shp = args[0].shape + xa = np.asarray([a.flatten() for a in args], dtype=np.float64) + r = self._call_norm(xa, self.xi) + return np.dot(self._function(r), self.nodes).reshape(shp) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbfinterp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbfinterp.py new file mode 100644 index 0000000000000000000000000000000000000000..6690e6ccf7d5499db10efffb0ef1c0139a90d2ba --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rbfinterp.py @@ -0,0 +1,550 @@ +"""Module for RBF interpolation.""" +import warnings +from itertools import combinations_with_replacement + +import numpy as np +from numpy.linalg import LinAlgError +from scipy.spatial import KDTree +from scipy.special import comb +from scipy.linalg.lapack import dgesv # type: ignore[attr-defined] + +from ._rbfinterp_pythran import (_build_system, + _build_evaluation_coefficients, + _polynomial_matrix) + + +__all__ = ["RBFInterpolator"] + + +# These RBFs are implemented. +_AVAILABLE = { + "linear", + "thin_plate_spline", + "cubic", + "quintic", + "multiquadric", + "inverse_multiquadric", + "inverse_quadratic", + "gaussian" + } + + +# The shape parameter does not need to be specified when using these RBFs. +_SCALE_INVARIANT = {"linear", "thin_plate_spline", "cubic", "quintic"} + + +# For RBFs that are conditionally positive definite of order m, the interpolant +# should include polynomial terms with degree >= m - 1. Define the minimum +# degrees here. These values are from Chapter 8 of Fasshauer's "Meshfree +# Approximation Methods with MATLAB". The RBFs that are not in this dictionary +# are positive definite and do not need polynomial terms. +_NAME_TO_MIN_DEGREE = { + "multiquadric": 0, + "linear": 0, + "thin_plate_spline": 1, + "cubic": 1, + "quintic": 2 + } + + +def _monomial_powers(ndim, degree): + """Return the powers for each monomial in a polynomial. + + Parameters + ---------- + ndim : int + Number of variables in the polynomial. + degree : int + Degree of the polynomial. + + Returns + ------- + (nmonos, ndim) int ndarray + Array where each row contains the powers for each variable in a + monomial. + + """ + nmonos = comb(degree + ndim, ndim, exact=True) + out = np.zeros((nmonos, ndim), dtype=np.dtype("long")) + count = 0 + for deg in range(degree + 1): + for mono in combinations_with_replacement(range(ndim), deg): + # `mono` is a tuple of variables in the current monomial with + # multiplicity indicating power (e.g., (0, 1, 1) represents x*y**2) + for var in mono: + out[count, var] += 1 + + count += 1 + + return out + + +def _build_and_solve_system(y, d, smoothing, kernel, epsilon, powers): + """Build and solve the RBF interpolation system of equations. + + Parameters + ---------- + y : (P, N) float ndarray + Data point coordinates. + d : (P, S) float ndarray + Data values at `y`. + smoothing : (P,) float ndarray + Smoothing parameter for each data point. + kernel : str + Name of the RBF. + epsilon : float + Shape parameter. + powers : (R, N) int ndarray + The exponents for each monomial in the polynomial. + + Returns + ------- + coeffs : (P + R, S) float ndarray + Coefficients for each RBF and monomial. + shift : (N,) float ndarray + Domain shift used to create the polynomial matrix. + scale : (N,) float ndarray + Domain scaling used to create the polynomial matrix. + + """ + lhs, rhs, shift, scale = _build_system( + y, d, smoothing, kernel, epsilon, powers + ) + _, _, coeffs, info = dgesv(lhs, rhs, overwrite_a=True, overwrite_b=True) + if info < 0: + raise ValueError(f"The {-info}-th argument had an illegal value.") + elif info > 0: + msg = "Singular matrix." + nmonos = powers.shape[0] + if nmonos > 0: + pmat = _polynomial_matrix((y - shift)/scale, powers) + rank = np.linalg.matrix_rank(pmat) + if rank < nmonos: + msg = ( + "Singular matrix. The matrix of monomials evaluated at " + "the data point coordinates does not have full column " + f"rank ({rank}/{nmonos})." + ) + + raise LinAlgError(msg) + + return shift, scale, coeffs + + +class RBFInterpolator: + """Radial basis function (RBF) interpolation in N dimensions. + + Parameters + ---------- + y : (npoints, ndims) array_like + 2-D array of data point coordinates. + d : (npoints, ...) array_like + N-D array of data values at `y`. The length of `d` along the first + axis must be equal to the length of `y`. Unlike some interpolators, the + interpolation axis cannot be changed. + neighbors : int, optional + If specified, the value of the interpolant at each evaluation point + will be computed using only this many nearest data points. All the data + points are used by default. + smoothing : float or (npoints, ) array_like, optional + Smoothing parameter. The interpolant perfectly fits the data when this + is set to 0. For large values, the interpolant approaches a least + squares fit of a polynomial with the specified degree. Default is 0. + kernel : str, optional + Type of RBF. This should be one of + + - 'linear' : ``-r`` + - 'thin_plate_spline' : ``r**2 * log(r)`` + - 'cubic' : ``r**3`` + - 'quintic' : ``-r**5`` + - 'multiquadric' : ``-sqrt(1 + r**2)`` + - 'inverse_multiquadric' : ``1/sqrt(1 + r**2)`` + - 'inverse_quadratic' : ``1/(1 + r**2)`` + - 'gaussian' : ``exp(-r**2)`` + + Default is 'thin_plate_spline'. + epsilon : float, optional + Shape parameter that scales the input to the RBF. If `kernel` is + 'linear', 'thin_plate_spline', 'cubic', or 'quintic', this defaults to + 1 and can be ignored because it has the same effect as scaling the + smoothing parameter. Otherwise, this must be specified. + degree : int, optional + Degree of the added polynomial. For some RBFs the interpolant may not + be well-posed if the polynomial degree is too small. Those RBFs and + their corresponding minimum degrees are + + - 'multiquadric' : 0 + - 'linear' : 0 + - 'thin_plate_spline' : 1 + - 'cubic' : 1 + - 'quintic' : 2 + + The default value is the minimum degree for `kernel` or 0 if there is + no minimum degree. Set this to -1 for no added polynomial. + + Notes + ----- + An RBF is a scalar valued function in N-dimensional space whose value at + :math:`x` can be expressed in terms of :math:`r=||x - c||`, where :math:`c` + is the center of the RBF. + + An RBF interpolant for the vector of data values :math:`d`, which are from + locations :math:`y`, is a linear combination of RBFs centered at :math:`y` + plus a polynomial with a specified degree. The RBF interpolant is written + as + + .. math:: + f(x) = K(x, y) a + P(x) b, + + where :math:`K(x, y)` is a matrix of RBFs with centers at :math:`y` + evaluated at the points :math:`x`, and :math:`P(x)` is a matrix of + monomials, which span polynomials with the specified degree, evaluated at + :math:`x`. The coefficients :math:`a` and :math:`b` are the solution to the + linear equations + + .. math:: + (K(y, y) + \\lambda I) a + P(y) b = d + + and + + .. math:: + P(y)^T a = 0, + + where :math:`\\lambda` is a non-negative smoothing parameter that controls + how well we want to fit the data. The data are fit exactly when the + smoothing parameter is 0. + + The above system is uniquely solvable if the following requirements are + met: + + - :math:`P(y)` must have full column rank. :math:`P(y)` always has full + column rank when `degree` is -1 or 0. When `degree` is 1, + :math:`P(y)` has full column rank if the data point locations are not + all collinear (N=2), coplanar (N=3), etc. + - If `kernel` is 'multiquadric', 'linear', 'thin_plate_spline', + 'cubic', or 'quintic', then `degree` must not be lower than the + minimum value listed above. + - If `smoothing` is 0, then each data point location must be distinct. + + When using an RBF that is not scale invariant ('multiquadric', + 'inverse_multiquadric', 'inverse_quadratic', or 'gaussian'), an appropriate + shape parameter must be chosen (e.g., through cross validation). Smaller + values for the shape parameter correspond to wider RBFs. The problem can + become ill-conditioned or singular when the shape parameter is too small. + + The memory required to solve for the RBF interpolation coefficients + increases quadratically with the number of data points, which can become + impractical when interpolating more than about a thousand data points. + To overcome memory limitations for large interpolation problems, the + `neighbors` argument can be specified to compute an RBF interpolant for + each evaluation point using only the nearest data points. + + .. versionadded:: 1.7.0 + + See Also + -------- + NearestNDInterpolator + LinearNDInterpolator + CloughTocher2DInterpolator + + References + ---------- + .. [1] Fasshauer, G., 2007. Meshfree Approximation Methods with Matlab. + World Scientific Publishing Co. + + .. [2] http://amadeus.math.iit.edu/~fass/603_ch3.pdf + + .. [3] Wahba, G., 1990. Spline Models for Observational Data. SIAM. + + .. [4] http://pages.stat.wisc.edu/~wahba/stat860public/lect/lect8/lect8.pdf + + Examples + -------- + Demonstrate interpolating scattered data to a grid in 2-D. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import RBFInterpolator + >>> from scipy.stats.qmc import Halton + + >>> rng = np.random.default_rng() + >>> xobs = 2*Halton(2, seed=rng).random(100) - 1 + >>> yobs = np.sum(xobs, axis=1)*np.exp(-6*np.sum(xobs**2, axis=1)) + + >>> xgrid = np.mgrid[-1:1:50j, -1:1:50j] + >>> xflat = xgrid.reshape(2, -1).T + >>> yflat = RBFInterpolator(xobs, yobs)(xflat) + >>> ygrid = yflat.reshape(50, 50) + + >>> fig, ax = plt.subplots() + >>> ax.pcolormesh(*xgrid, ygrid, vmin=-0.25, vmax=0.25, shading='gouraud') + >>> p = ax.scatter(*xobs.T, c=yobs, s=50, ec='k', vmin=-0.25, vmax=0.25) + >>> fig.colorbar(p) + >>> plt.show() + + """ + + def __init__(self, y, d, + neighbors=None, + smoothing=0.0, + kernel="thin_plate_spline", + epsilon=None, + degree=None): + y = np.asarray(y, dtype=float, order="C") + if y.ndim != 2: + raise ValueError("`y` must be a 2-dimensional array.") + + ny, ndim = y.shape + + d_dtype = complex if np.iscomplexobj(d) else float + d = np.asarray(d, dtype=d_dtype, order="C") + if d.shape[0] != ny: + raise ValueError( + f"Expected the first axis of `d` to have length {ny}." + ) + + d_shape = d.shape[1:] + d = d.reshape((ny, -1)) + # If `d` is complex, convert it to a float array with twice as many + # columns. Otherwise, the LHS matrix would need to be converted to + # complex and take up 2x more memory than necessary. + d = d.view(float) + + if np.isscalar(smoothing): + smoothing = np.full(ny, smoothing, dtype=float) + else: + smoothing = np.asarray(smoothing, dtype=float, order="C") + if smoothing.shape != (ny,): + raise ValueError( + "Expected `smoothing` to be a scalar or have shape " + f"({ny},)." + ) + + kernel = kernel.lower() + if kernel not in _AVAILABLE: + raise ValueError(f"`kernel` must be one of {_AVAILABLE}.") + + if epsilon is None: + if kernel in _SCALE_INVARIANT: + epsilon = 1.0 + else: + raise ValueError( + "`epsilon` must be specified if `kernel` is not one of " + f"{_SCALE_INVARIANT}." + ) + else: + epsilon = float(epsilon) + + min_degree = _NAME_TO_MIN_DEGREE.get(kernel, -1) + if degree is None: + degree = max(min_degree, 0) + else: + degree = int(degree) + if degree < -1: + raise ValueError("`degree` must be at least -1.") + elif -1 < degree < min_degree: + warnings.warn( + f"`degree` should not be below {min_degree} except -1 " + f"when `kernel` is '{kernel}'." + f"The interpolant may not be uniquely " + f"solvable, and the smoothing parameter may have an " + f"unintuitive effect.", + UserWarning, stacklevel=2 + ) + + if neighbors is None: + nobs = ny + else: + # Make sure the number of nearest neighbors used for interpolation + # does not exceed the number of observations. + neighbors = int(min(neighbors, ny)) + nobs = neighbors + + powers = _monomial_powers(ndim, degree) + # The polynomial matrix must have full column rank in order for the + # interpolant to be well-posed, which is not possible if there are + # fewer observations than monomials. + if powers.shape[0] > nobs: + raise ValueError( + f"At least {powers.shape[0]} data points are required when " + f"`degree` is {degree} and the number of dimensions is {ndim}." + ) + + if neighbors is None: + shift, scale, coeffs = _build_and_solve_system( + y, d, smoothing, kernel, epsilon, powers + ) + + # Make these attributes private since they do not always exist. + self._shift = shift + self._scale = scale + self._coeffs = coeffs + + else: + self._tree = KDTree(y) + + self.y = y + self.d = d + self.d_shape = d_shape + self.d_dtype = d_dtype + self.neighbors = neighbors + self.smoothing = smoothing + self.kernel = kernel + self.epsilon = epsilon + self.powers = powers + + def _chunk_evaluator( + self, + x, + y, + shift, + scale, + coeffs, + memory_budget=1000000 + ): + """ + Evaluate the interpolation while controlling memory consumption. + We chunk the input if we need more memory than specified. + + Parameters + ---------- + x : (Q, N) float ndarray + array of points on which to evaluate + y: (P, N) float ndarray + array of points on which we know function values + shift: (N, ) ndarray + Domain shift used to create the polynomial matrix. + scale : (N,) float ndarray + Domain scaling used to create the polynomial matrix. + coeffs: (P+R, S) float ndarray + Coefficients in front of basis functions + memory_budget: int + Total amount of memory (in units of sizeof(float)) we wish + to devote for storing the array of coefficients for + interpolated points. If we need more memory than that, we + chunk the input. + + Returns + ------- + (Q, S) float ndarray + Interpolated array + """ + nx, ndim = x.shape + if self.neighbors is None: + nnei = len(y) + else: + nnei = self.neighbors + # in each chunk we consume the same space we already occupy + chunksize = memory_budget // (self.powers.shape[0] + nnei) + 1 + if chunksize <= nx: + out = np.empty((nx, self.d.shape[1]), dtype=float) + for i in range(0, nx, chunksize): + vec = _build_evaluation_coefficients( + x[i:i + chunksize, :], + y, + self.kernel, + self.epsilon, + self.powers, + shift, + scale) + out[i:i + chunksize, :] = np.dot(vec, coeffs) + else: + vec = _build_evaluation_coefficients( + x, + y, + self.kernel, + self.epsilon, + self.powers, + shift, + scale) + out = np.dot(vec, coeffs) + return out + + def __call__(self, x): + """Evaluate the interpolant at `x`. + + Parameters + ---------- + x : (Q, N) array_like + Evaluation point coordinates. + + Returns + ------- + (Q, ...) ndarray + Values of the interpolant at `x`. + + """ + x = np.asarray(x, dtype=float, order="C") + if x.ndim != 2: + raise ValueError("`x` must be a 2-dimensional array.") + + nx, ndim = x.shape + if ndim != self.y.shape[1]: + raise ValueError("Expected the second axis of `x` to have length " + f"{self.y.shape[1]}.") + + # Our memory budget for storing RBF coefficients is + # based on how many floats in memory we already occupy + # If this number is below 1e6 we just use 1e6 + # This memory budget is used to decide how we chunk + # the inputs + memory_budget = max(x.size + self.y.size + self.d.size, 1000000) + + if self.neighbors is None: + out = self._chunk_evaluator( + x, + self.y, + self._shift, + self._scale, + self._coeffs, + memory_budget=memory_budget) + else: + # Get the indices of the k nearest observation points to each + # evaluation point. + _, yindices = self._tree.query(x, self.neighbors) + if self.neighbors == 1: + # `KDTree` squeezes the output when neighbors=1. + yindices = yindices[:, None] + + # Multiple evaluation points may have the same neighborhood of + # observation points. Make the neighborhoods unique so that we only + # compute the interpolation coefficients once for each + # neighborhood. + yindices = np.sort(yindices, axis=1) + yindices, inv = np.unique(yindices, return_inverse=True, axis=0) + inv = np.reshape(inv, (-1,)) # flatten, we need 1-D indices + # `inv` tells us which neighborhood will be used by each evaluation + # point. Now we find which evaluation points will be using each + # neighborhood. + xindices = [[] for _ in range(len(yindices))] + for i, j in enumerate(inv): + xindices[j].append(i) + + out = np.empty((nx, self.d.shape[1]), dtype=float) + for xidx, yidx in zip(xindices, yindices): + # `yidx` are the indices of the observations in this + # neighborhood. `xidx` are the indices of the evaluation points + # that are using this neighborhood. + xnbr = x[xidx] + ynbr = self.y[yidx] + dnbr = self.d[yidx] + snbr = self.smoothing[yidx] + shift, scale, coeffs = _build_and_solve_system( + ynbr, + dnbr, + snbr, + self.kernel, + self.epsilon, + self.powers, + ) + out[xidx] = self._chunk_evaluator( + xnbr, + ynbr, + shift, + scale, + coeffs, + memory_budget=memory_budget) + + out = out.view(self.d_dtype) + out = out.reshape((nx, ) + self.d_shape) + return out diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rgi.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rgi.py new file mode 100644 index 0000000000000000000000000000000000000000..eb17bf9c8b57e8716be4fcfc6296c1ee21ebb985 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/_rgi.py @@ -0,0 +1,766 @@ +__all__ = ['RegularGridInterpolator', 'interpn'] + +import itertools +import warnings + +import numpy as np + +import scipy.sparse.linalg as ssl + +from .interpnd import _ndim_coords_from_arrays +from ._cubic import PchipInterpolator +from ._rgi_cython import evaluate_linear_2d, find_indices +from ._bsplines import make_interp_spline +from ._fitpack2 import RectBivariateSpline +from ._ndbspline import make_ndbspl + + +def _check_points(points): + descending_dimensions = [] + grid = [] + for i, p in enumerate(points): + # early make points float + # see https://github.com/scipy/scipy/pull/17230 + p = np.asarray(p, dtype=float) + if not np.all(p[1:] > p[:-1]): + if np.all(p[1:] < p[:-1]): + # input is descending, so make it ascending + descending_dimensions.append(i) + p = np.flip(p) + else: + raise ValueError( + "The points in dimension %d must be strictly " + "ascending or descending" % i) + # see https://github.com/scipy/scipy/issues/17716 + p = np.ascontiguousarray(p) + grid.append(p) + return tuple(grid), tuple(descending_dimensions) + + +def _check_dimensionality(points, values): + if len(points) > values.ndim: + raise ValueError("There are %d point arrays, but values has %d " + "dimensions" % (len(points), values.ndim)) + for i, p in enumerate(points): + if not np.asarray(p).ndim == 1: + raise ValueError("The points in dimension %d must be " + "1-dimensional" % i) + if not values.shape[i] == len(p): + raise ValueError("There are %d points and %d values in " + "dimension %d" % (len(p), values.shape[i], i)) + + +class RegularGridInterpolator: + """ + Interpolator on a regular or rectilinear grid in arbitrary dimensions. + + The data must be defined on a rectilinear grid; that is, a rectangular + grid with even or uneven spacing. Linear, nearest-neighbor, spline + interpolations are supported. After setting up the interpolator object, + the interpolation method may be chosen at each evaluation. + + Parameters + ---------- + points : tuple of ndarray of float, with shapes (m1, ), ..., (mn, ) + The points defining the regular grid in n dimensions. The points in + each dimension (i.e. every elements of the points tuple) must be + strictly ascending or descending. + + values : array_like, shape (m1, ..., mn, ...) + The data on the regular grid in n dimensions. Complex data is + accepted. + + .. deprecated:: 1.13.0 + Complex data is deprecated with ``method="pchip"`` and will raise an + error in SciPy 1.15.0. This is because ``PchipInterpolator`` only + works with real values. If you are trying to use the real components of + the passed array, use ``np.real`` on ``values``. + + method : str, optional + The method of interpolation to perform. Supported are "linear", + "nearest", "slinear", "cubic", "quintic" and "pchip". This + parameter will become the default for the object's ``__call__`` + method. Default is "linear". + + bounds_error : bool, optional + If True, when interpolated values are requested outside of the + domain of the input data, a ValueError is raised. + If False, then `fill_value` is used. + Default is True. + + fill_value : float or None, optional + The value to use for points outside of the interpolation domain. + If None, values outside the domain are extrapolated. + Default is ``np.nan``. + + solver : callable, optional + Only used for methods "slinear", "cubic" and "quintic". + Sparse linear algebra solver for construction of the NdBSpline instance. + Default is the iterative solver `scipy.sparse.linalg.gcrotmk`. + + .. versionadded:: 1.13 + + solver_args: dict, optional + Additional arguments to pass to `solver`, if any. + + .. versionadded:: 1.13 + + Methods + ------- + __call__ + + Attributes + ---------- + grid : tuple of ndarrays + The points defining the regular grid in n dimensions. + This tuple defines the full grid via + ``np.meshgrid(*grid, indexing='ij')`` + values : ndarray + Data values at the grid. + method : str + Interpolation method. + fill_value : float or ``None`` + Use this value for out-of-bounds arguments to `__call__`. + bounds_error : bool + If ``True``, out-of-bounds argument raise a ``ValueError``. + + Notes + ----- + Contrary to `LinearNDInterpolator` and `NearestNDInterpolator`, this class + avoids expensive triangulation of the input data by taking advantage of the + regular grid structure. + + In other words, this class assumes that the data is defined on a + *rectilinear* grid. + + .. versionadded:: 0.14 + + The 'slinear'(k=1), 'cubic'(k=3), and 'quintic'(k=5) methods are + tensor-product spline interpolators, where `k` is the spline degree, + If any dimension has fewer points than `k` + 1, an error will be raised. + + .. versionadded:: 1.9 + + If the input data is such that dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolating. + + **Choosing a solver for spline methods** + + Spline methods, "slinear", "cubic" and "quintic" involve solving a + large sparse linear system at instantiation time. Depending on data, + the default solver may or may not be adequate. When it is not, you may + need to experiment with an optional `solver` argument, where you may + choose between the direct solver (`scipy.sparse.linalg.spsolve`) or + iterative solvers from `scipy.sparse.linalg`. You may need to supply + additional parameters via the optional `solver_args` parameter (for instance, + you may supply the starting value or target tolerance). See the + `scipy.sparse.linalg` documentation for the full list of available options. + + Alternatively, you may instead use the legacy methods, "slinear_legacy", + "cubic_legacy" and "quintic_legacy". These methods allow faster construction + but evaluations will be much slower. + + Examples + -------- + **Evaluate a function on the points of a 3-D grid** + + As a first example, we evaluate a simple example function on the points of + a 3-D grid: + + >>> from scipy.interpolate import RegularGridInterpolator + >>> import numpy as np + >>> def f(x, y, z): + ... return 2 * x**3 + 3 * y**2 - z + >>> x = np.linspace(1, 4, 11) + >>> y = np.linspace(4, 7, 22) + >>> z = np.linspace(7, 9, 33) + >>> xg, yg ,zg = np.meshgrid(x, y, z, indexing='ij', sparse=True) + >>> data = f(xg, yg, zg) + + ``data`` is now a 3-D array with ``data[i, j, k] = f(x[i], y[j], z[k])``. + Next, define an interpolating function from this data: + + >>> interp = RegularGridInterpolator((x, y, z), data) + + Evaluate the interpolating function at the two points + ``(x,y,z) = (2.1, 6.2, 8.3)`` and ``(3.3, 5.2, 7.1)``: + + >>> pts = np.array([[2.1, 6.2, 8.3], + ... [3.3, 5.2, 7.1]]) + >>> interp(pts) + array([ 125.80469388, 146.30069388]) + + which is indeed a close approximation to + + >>> f(2.1, 6.2, 8.3), f(3.3, 5.2, 7.1) + (125.54200000000002, 145.894) + + **Interpolate and extrapolate a 2D dataset** + + As a second example, we interpolate and extrapolate a 2D data set: + + >>> x, y = np.array([-2, 0, 4]), np.array([-2, 0, 2, 5]) + >>> def ff(x, y): + ... return x**2 + y**2 + + >>> xg, yg = np.meshgrid(x, y, indexing='ij') + >>> data = ff(xg, yg) + >>> interp = RegularGridInterpolator((x, y), data, + ... bounds_error=False, fill_value=None) + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax = fig.add_subplot(projection='3d') + >>> ax.scatter(xg.ravel(), yg.ravel(), data.ravel(), + ... s=60, c='k', label='data') + + Evaluate and plot the interpolator on a finer grid + + >>> xx = np.linspace(-4, 9, 31) + >>> yy = np.linspace(-4, 9, 31) + >>> X, Y = np.meshgrid(xx, yy, indexing='ij') + + >>> # interpolator + >>> ax.plot_wireframe(X, Y, interp((X, Y)), rstride=3, cstride=3, + ... alpha=0.4, color='m', label='linear interp') + + >>> # ground truth + >>> ax.plot_wireframe(X, Y, ff(X, Y), rstride=3, cstride=3, + ... alpha=0.4, label='ground truth') + >>> plt.legend() + >>> plt.show() + + Other examples are given + :ref:`in the tutorial `. + + See Also + -------- + NearestNDInterpolator : Nearest neighbor interpolator on *unstructured* + data in N dimensions + + LinearNDInterpolator : Piecewise linear interpolator on *unstructured* data + in N dimensions + + interpn : a convenience function which wraps `RegularGridInterpolator` + + scipy.ndimage.map_coordinates : interpolation on grids with equal spacing + (suitable for e.g., N-D image resampling) + + References + ---------- + .. [1] Python package *regulargrid* by Johannes Buchner, see + https://pypi.python.org/pypi/regulargrid/ + .. [2] Wikipedia, "Trilinear interpolation", + https://en.wikipedia.org/wiki/Trilinear_interpolation + .. [3] Weiser, Alan, and Sergio E. Zarantonello. "A note on piecewise linear + and multilinear table interpolation in many dimensions." MATH. + COMPUT. 50.181 (1988): 189-196. + https://www.ams.org/journals/mcom/1988-50-181/S0025-5718-1988-0917826-0/S0025-5718-1988-0917826-0.pdf + :doi:`10.1090/S0025-5718-1988-0917826-0` + + """ + # this class is based on code originally programmed by Johannes Buchner, + # see https://github.com/JohannesBuchner/regulargrid + + _SPLINE_DEGREE_MAP = {"slinear": 1, "cubic": 3, "quintic": 5, 'pchip': 3, + "slinear_legacy": 1, "cubic_legacy": 3, "quintic_legacy": 5,} + _SPLINE_METHODS_recursive = {"slinear_legacy", "cubic_legacy", + "quintic_legacy", "pchip"} + _SPLINE_METHODS_ndbspl = {"slinear", "cubic", "quintic"} + _SPLINE_METHODS = list(_SPLINE_DEGREE_MAP.keys()) + _ALL_METHODS = ["linear", "nearest"] + _SPLINE_METHODS + + def __init__(self, points, values, method="linear", bounds_error=True, + fill_value=np.nan, *, solver=None, solver_args=None): + if method not in self._ALL_METHODS: + raise ValueError("Method '%s' is not defined" % method) + elif method in self._SPLINE_METHODS: + self._validate_grid_dimensions(points, method) + self.method = method + self.bounds_error = bounds_error + self.grid, self._descending_dimensions = _check_points(points) + self.values = self._check_values(values) + self._check_dimensionality(self.grid, self.values) + self.fill_value = self._check_fill_value(self.values, fill_value) + if self._descending_dimensions: + self.values = np.flip(values, axis=self._descending_dimensions) + if self.method == "pchip" and np.iscomplexobj(self.values): + msg = ("`PchipInterpolator` only works with real values. Passing " + "complex-dtyped `values` with `method='pchip'` is deprecated " + "and will raise an error in SciPy 1.15.0. If you are trying to " + "use the real components of the passed array, use `np.real` on " + "the array before passing to `RegularGridInterpolator`.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) + if method in self._SPLINE_METHODS_ndbspl: + if solver_args is None: + solver_args = {} + self._spline = self._construct_spline(method, solver, **solver_args) + else: + if solver is not None or solver_args: + raise ValueError( + f"{method =} does not accept the 'solver' argument. Got " + f" {solver = } and with arguments {solver_args}." + ) + + def _construct_spline(self, method, solver=None, **solver_args): + if solver is None: + solver = ssl.gcrotmk + spl = make_ndbspl( + self.grid, self.values, self._SPLINE_DEGREE_MAP[method], + solver=solver, **solver_args + ) + return spl + + def _check_dimensionality(self, grid, values): + _check_dimensionality(grid, values) + + def _check_points(self, points): + return _check_points(points) + + def _check_values(self, values): + if not hasattr(values, 'ndim'): + # allow reasonable duck-typed values + values = np.asarray(values) + + if hasattr(values, 'dtype') and hasattr(values, 'astype'): + if not np.issubdtype(values.dtype, np.inexact): + values = values.astype(float) + + return values + + def _check_fill_value(self, values, fill_value): + if fill_value is not None: + fill_value_dtype = np.asarray(fill_value).dtype + if (hasattr(values, 'dtype') and not + np.can_cast(fill_value_dtype, values.dtype, + casting='same_kind')): + raise ValueError("fill_value must be either 'None' or " + "of a type compatible with values") + return fill_value + + def __call__(self, xi, method=None, *, nu=None): + """ + Interpolation at coordinates. + + Parameters + ---------- + xi : ndarray of shape (..., ndim) + The coordinates to evaluate the interpolator at. + + method : str, optional + The method of interpolation to perform. Supported are "linear", + "nearest", "slinear", "cubic", "quintic" and "pchip". Default is + the method chosen when the interpolator was created. + + nu : sequence of ints, length ndim, optional + If not None, the orders of the derivatives to evaluate. + Each entry must be non-negative. + Only allowed for methods "slinear", "cubic" and "quintic". + + .. versionadded:: 1.13 + + Returns + ------- + values_x : ndarray, shape xi.shape[:-1] + values.shape[ndim:] + Interpolated values at `xi`. See notes for behaviour when + ``xi.ndim == 1``. + + Notes + ----- + In the case that ``xi.ndim == 1`` a new axis is inserted into + the 0 position of the returned array, values_x, so its shape is + instead ``(1,) + values.shape[ndim:]``. + + Examples + -------- + Here we define a nearest-neighbor interpolator of a simple function + + >>> import numpy as np + >>> x, y = np.array([0, 1, 2]), np.array([1, 3, 7]) + >>> def f(x, y): + ... return x**2 + y**2 + >>> data = f(*np.meshgrid(x, y, indexing='ij', sparse=True)) + >>> from scipy.interpolate import RegularGridInterpolator + >>> interp = RegularGridInterpolator((x, y), data, method='nearest') + + By construction, the interpolator uses the nearest-neighbor + interpolation + + >>> interp([[1.5, 1.3], [0.3, 4.5]]) + array([2., 9.]) + + We can however evaluate the linear interpolant by overriding the + `method` parameter + + >>> interp([[1.5, 1.3], [0.3, 4.5]], method='linear') + array([ 4.7, 24.3]) + """ + method = self.method if method is None else method + is_method_changed = self.method != method + if method not in self._ALL_METHODS: + raise ValueError("Method '%s' is not defined" % method) + if is_method_changed and method in self._SPLINE_METHODS_ndbspl: + self._spline = self._construct_spline(method) + + if nu is not None and method not in self._SPLINE_METHODS_ndbspl: + raise ValueError( + f"Can only compute derivatives for methods " + f"{self._SPLINE_METHODS_ndbspl}, got {method =}." + ) + + xi, xi_shape, ndim, nans, out_of_bounds = self._prepare_xi(xi) + + if method == "linear": + indices, norm_distances = self._find_indices(xi.T) + if (ndim == 2 and hasattr(self.values, 'dtype') and + self.values.ndim == 2 and self.values.flags.writeable and + self.values.dtype in (np.float64, np.complex128) and + self.values.dtype.byteorder == '='): + # until cython supports const fused types, the fast path + # cannot support non-writeable values + # a fast path + out = np.empty(indices.shape[1], dtype=self.values.dtype) + result = evaluate_linear_2d(self.values, + indices, + norm_distances, + self.grid, + out) + else: + result = self._evaluate_linear(indices, norm_distances) + elif method == "nearest": + indices, norm_distances = self._find_indices(xi.T) + result = self._evaluate_nearest(indices, norm_distances) + elif method in self._SPLINE_METHODS: + if is_method_changed: + self._validate_grid_dimensions(self.grid, method) + if method in self._SPLINE_METHODS_recursive: + result = self._evaluate_spline(xi, method) + else: + result = self._spline(xi, nu=nu) + + if not self.bounds_error and self.fill_value is not None: + result[out_of_bounds] = self.fill_value + + # f(nan) = nan, if any + if np.any(nans): + result[nans] = np.nan + return result.reshape(xi_shape[:-1] + self.values.shape[ndim:]) + + def _prepare_xi(self, xi): + ndim = len(self.grid) + xi = _ndim_coords_from_arrays(xi, ndim=ndim) + if xi.shape[-1] != len(self.grid): + raise ValueError("The requested sample points xi have dimension " + f"{xi.shape[-1]} but this " + f"RegularGridInterpolator has dimension {ndim}") + + xi_shape = xi.shape + xi = xi.reshape(-1, xi_shape[-1]) + xi = np.asarray(xi, dtype=float) + + # find nans in input + nans = np.any(np.isnan(xi), axis=-1) + + if self.bounds_error: + for i, p in enumerate(xi.T): + if not np.logical_and(np.all(self.grid[i][0] <= p), + np.all(p <= self.grid[i][-1])): + raise ValueError("One of the requested xi is out of bounds " + "in dimension %d" % i) + out_of_bounds = None + else: + out_of_bounds = self._find_out_of_bounds(xi.T) + + return xi, xi_shape, ndim, nans, out_of_bounds + + def _evaluate_linear(self, indices, norm_distances): + # slice for broadcasting over trailing dimensions in self.values + vslice = (slice(None),) + (None,)*(self.values.ndim - len(indices)) + + # Compute shifting up front before zipping everything together + shift_norm_distances = [1 - yi for yi in norm_distances] + shift_indices = [i + 1 for i in indices] + + # The formula for linear interpolation in 2d takes the form: + # values = self.values[(i0, i1)] * (1 - y0) * (1 - y1) + \ + # self.values[(i0, i1 + 1)] * (1 - y0) * y1 + \ + # self.values[(i0 + 1, i1)] * y0 * (1 - y1) + \ + # self.values[(i0 + 1, i1 + 1)] * y0 * y1 + # We pair i with 1 - yi (zipped1) and i + 1 with yi (zipped2) + zipped1 = zip(indices, shift_norm_distances) + zipped2 = zip(shift_indices, norm_distances) + + # Take all products of zipped1 and zipped2 and iterate over them + # to get the terms in the above formula. This corresponds to iterating + # over the vertices of a hypercube. + hypercube = itertools.product(*zip(zipped1, zipped2)) + value = np.array([0.]) + for h in hypercube: + edge_indices, weights = zip(*h) + weight = np.array([1.]) + for w in weights: + weight = weight * w + term = np.asarray(self.values[edge_indices]) * weight[vslice] + value = value + term # cannot use += because broadcasting + return value + + def _evaluate_nearest(self, indices, norm_distances): + idx_res = [np.where(yi <= .5, i, i + 1) + for i, yi in zip(indices, norm_distances)] + return self.values[tuple(idx_res)] + + def _validate_grid_dimensions(self, points, method): + k = self._SPLINE_DEGREE_MAP[method] + for i, point in enumerate(points): + ndim = len(np.atleast_1d(point)) + if ndim <= k: + raise ValueError(f"There are {ndim} points in dimension {i}," + f" but method {method} requires at least " + f" {k+1} points per dimension.") + + def _evaluate_spline(self, xi, method): + # ensure xi is 2D list of points to evaluate (`m` is the number of + # points and `n` is the number of interpolation dimensions, + # ``n == len(self.grid)``.) + if xi.ndim == 1: + xi = xi.reshape((1, xi.size)) + m, n = xi.shape + + # Reorder the axes: n-dimensional process iterates over the + # interpolation axes from the last axis downwards: E.g. for a 4D grid + # the order of axes is 3, 2, 1, 0. Each 1D interpolation works along + # the 0th axis of its argument array (for 1D routine it's its ``y`` + # array). Thus permute the interpolation axes of `values` *and keep + # trailing dimensions trailing*. + axes = tuple(range(self.values.ndim)) + axx = axes[:n][::-1] + axes[n:] + values = self.values.transpose(axx) + + if method == 'pchip': + _eval_func = self._do_pchip + else: + _eval_func = self._do_spline_fit + k = self._SPLINE_DEGREE_MAP[method] + + # Non-stationary procedure: difficult to vectorize this part entirely + # into numpy-level operations. Unfortunately this requires explicit + # looping over each point in xi. + + # can at least vectorize the first pass across all points in the + # last variable of xi. + last_dim = n - 1 + first_values = _eval_func(self.grid[last_dim], + values, + xi[:, last_dim], + k) + + # the rest of the dimensions have to be on a per point-in-xi basis + shape = (m, *self.values.shape[n:]) + result = np.empty(shape, dtype=self.values.dtype) + for j in range(m): + # Main process: Apply 1D interpolate in each dimension + # sequentially, starting with the last dimension. + # These are then "folded" into the next dimension in-place. + folded_values = first_values[j, ...] + for i in range(last_dim-1, -1, -1): + # Interpolate for each 1D from the last dimensions. + # This collapses each 1D sequence into a scalar. + folded_values = _eval_func(self.grid[i], + folded_values, + xi[j, i], + k) + result[j, ...] = folded_values + + return result + + @staticmethod + def _do_spline_fit(x, y, pt, k): + local_interp = make_interp_spline(x, y, k=k, axis=0) + values = local_interp(pt) + return values + + @staticmethod + def _do_pchip(x, y, pt, k): + local_interp = PchipInterpolator(x, y, axis=0) + values = local_interp(pt) + return values + + def _find_indices(self, xi): + return find_indices(self.grid, xi) + + def _find_out_of_bounds(self, xi): + # check for out of bounds xi + out_of_bounds = np.zeros((xi.shape[1]), dtype=bool) + # iterate through dimensions + for x, grid in zip(xi, self.grid): + out_of_bounds += x < grid[0] + out_of_bounds += x > grid[-1] + return out_of_bounds + + +def interpn(points, values, xi, method="linear", bounds_error=True, + fill_value=np.nan): + """ + Multidimensional interpolation on regular or rectilinear grids. + + Strictly speaking, not all regular grids are supported - this function + works on *rectilinear* grids, that is, a rectangular grid with even or + uneven spacing. + + Parameters + ---------- + points : tuple of ndarray of float, with shapes (m1, ), ..., (mn, ) + The points defining the regular grid in n dimensions. The points in + each dimension (i.e. every elements of the points tuple) must be + strictly ascending or descending. + + values : array_like, shape (m1, ..., mn, ...) + The data on the regular grid in n dimensions. Complex data is + accepted. + + .. deprecated:: 1.13.0 + Complex data is deprecated with ``method="pchip"`` and will raise an + error in SciPy 1.15.0. This is because ``PchipInterpolator`` only + works with real values. If you are trying to use the real components of + the passed array, use ``np.real`` on ``values``. + + xi : ndarray of shape (..., ndim) + The coordinates to sample the gridded data at + + method : str, optional + The method of interpolation to perform. Supported are "linear", + "nearest", "slinear", "cubic", "quintic", "pchip", and "splinef2d". + "splinef2d" is only supported for 2-dimensional data. + + bounds_error : bool, optional + If True, when interpolated values are requested outside of the + domain of the input data, a ValueError is raised. + If False, then `fill_value` is used. + + fill_value : number, optional + If provided, the value to use for points outside of the + interpolation domain. If None, values outside + the domain are extrapolated. Extrapolation is not supported by method + "splinef2d". + + Returns + ------- + values_x : ndarray, shape xi.shape[:-1] + values.shape[ndim:] + Interpolated values at `xi`. See notes for behaviour when + ``xi.ndim == 1``. + + See Also + -------- + NearestNDInterpolator : Nearest neighbor interpolation on unstructured + data in N dimensions + LinearNDInterpolator : Piecewise linear interpolant on unstructured data + in N dimensions + RegularGridInterpolator : interpolation on a regular or rectilinear grid + in arbitrary dimensions (`interpn` wraps this + class). + RectBivariateSpline : Bivariate spline approximation over a rectangular mesh + scipy.ndimage.map_coordinates : interpolation on grids with equal spacing + (suitable for e.g., N-D image resampling) + + Notes + ----- + + .. versionadded:: 0.14 + + In the case that ``xi.ndim == 1`` a new axis is inserted into + the 0 position of the returned array, values_x, so its shape is + instead ``(1,) + values.shape[ndim:]``. + + If the input data is such that input dimensions have incommensurate + units and differ by many orders of magnitude, the interpolant may have + numerical artifacts. Consider rescaling the data before interpolation. + + Examples + -------- + Evaluate a simple example function on the points of a regular 3-D grid: + + >>> import numpy as np + >>> from scipy.interpolate import interpn + >>> def value_func_3d(x, y, z): + ... return 2 * x + 3 * y - z + >>> x = np.linspace(0, 4, 5) + >>> y = np.linspace(0, 5, 6) + >>> z = np.linspace(0, 6, 7) + >>> points = (x, y, z) + >>> values = value_func_3d(*np.meshgrid(*points, indexing='ij')) + + Evaluate the interpolating function at a point + + >>> point = np.array([2.21, 3.12, 1.15]) + >>> print(interpn(points, values, point)) + [12.63] + + """ + # sanity check 'method' kwarg + if method not in ["linear", "nearest", "cubic", "quintic", "pchip", + "splinef2d", "slinear", + "slinear_legacy", "cubic_legacy", "quintic_legacy"]: + raise ValueError("interpn only understands the methods 'linear', " + "'nearest', 'slinear', 'cubic', 'quintic', 'pchip', " + f"and 'splinef2d'. You provided {method}.") + + if not hasattr(values, 'ndim'): + values = np.asarray(values) + + ndim = values.ndim + if ndim > 2 and method == "splinef2d": + raise ValueError("The method splinef2d can only be used for " + "2-dimensional input data") + if not bounds_error and fill_value is None and method == "splinef2d": + raise ValueError("The method splinef2d does not support extrapolation.") + + # sanity check consistency of input dimensions + if len(points) > ndim: + raise ValueError("There are %d point arrays, but values has %d " + "dimensions" % (len(points), ndim)) + if len(points) != ndim and method == 'splinef2d': + raise ValueError("The method splinef2d can only be used for " + "scalar data with one point per coordinate") + + grid, descending_dimensions = _check_points(points) + _check_dimensionality(grid, values) + + # sanity check requested xi + xi = _ndim_coords_from_arrays(xi, ndim=len(grid)) + if xi.shape[-1] != len(grid): + raise ValueError("The requested sample points xi have dimension " + "%d, but this RegularGridInterpolator has " + "dimension %d" % (xi.shape[-1], len(grid))) + + if bounds_error: + for i, p in enumerate(xi.T): + if not np.logical_and(np.all(grid[i][0] <= p), + np.all(p <= grid[i][-1])): + raise ValueError("One of the requested xi is out of bounds " + "in dimension %d" % i) + + # perform interpolation + if method in RegularGridInterpolator._ALL_METHODS: + interp = RegularGridInterpolator(points, values, method=method, + bounds_error=bounds_error, + fill_value=fill_value) + return interp(xi) + elif method == "splinef2d": + xi_shape = xi.shape + xi = xi.reshape(-1, xi.shape[-1]) + + # RectBivariateSpline doesn't support fill_value; we need to wrap here + idx_valid = np.all((grid[0][0] <= xi[:, 0], xi[:, 0] <= grid[0][-1], + grid[1][0] <= xi[:, 1], xi[:, 1] <= grid[1][-1]), + axis=0) + result = np.empty_like(xi[:, 0]) + + # make a copy of values for RectBivariateSpline + interp = RectBivariateSpline(points[0], points[1], values[:]) + result[idx_valid] = interp.ev(xi[idx_valid, 0], xi[idx_valid, 1]) + result[np.logical_not(idx_valid)] = fill_value + + return result.reshape(xi_shape[:-1]) + else: + raise ValueError(f"unknown {method = }") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack.py new file mode 100644 index 0000000000000000000000000000000000000000..68a6a240961018cac8e59419245ee6791cba7a67 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack.py @@ -0,0 +1,32 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'BSpline', + 'bisplev', + 'bisplrep', + 'dblint', + 'insert', + 'spalde', + 'splantider', + 'splder', + 'splev', + 'splint', + 'splprep', + 'splrep', + 'sproot', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="fitpack", + private_modules=["_fitpack_py"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack2.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack2.py new file mode 100644 index 0000000000000000000000000000000000000000..c4848053e4d9791144483a213fe6040ef214c1e3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/fitpack2.py @@ -0,0 +1,38 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'BivariateSpline', + 'InterpolatedUnivariateSpline', + 'LSQBivariateSpline', + 'LSQSphereBivariateSpline', + 'LSQUnivariateSpline', + 'RectBivariateSpline', + 'RectSphereBivariateSpline', + 'SmoothBivariateSpline', + 'SmoothSphereBivariateSpline', + 'SphereBivariateSpline', + 'UnivariateSpline', + 'array', + 'concatenate', + 'dfitpack', + 'dfitpack_int', + 'diff', + 'ones', + 'ravel', + 'zeros', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="fitpack2", + private_modules=["_fitpack2"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/interpolate.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/interpolate.py new file mode 100644 index 0000000000000000000000000000000000000000..3c6ae3398fa036a923ab7ed0ddaf63c2d73fe559 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/interpolate.py @@ -0,0 +1,44 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'BPoly', + 'BSpline', + 'NdPPoly', + 'PPoly', + 'RectBivariateSpline', + 'RegularGridInterpolator', + 'array', + 'asarray', + 'atleast_1d', + 'atleast_2d', + 'comb', + 'dfitpack', + 'interp1d', + 'interp2d', + 'interpn', + 'intp', + 'itertools', + 'lagrange', + 'make_interp_spline', + 'poly1d', + 'prod', + 'ravel', + 'searchsorted', + 'spec', + 'transpose', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="interpolate", + private_modules=["_interpolate", "fitpack2", "_rgi"], + all=__all__, attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/ndgriddata.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/ndgriddata.py new file mode 100644 index 0000000000000000000000000000000000000000..bb2b1694d244d00b6aea9784fc0ad384a793d57d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/ndgriddata.py @@ -0,0 +1,25 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'CloughTocher2DInterpolator', + 'LinearNDInterpolator', + 'NDInterpolatorBase', + 'NearestNDInterpolator', + 'cKDTree', + 'griddata', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="ndgriddata", + private_modules=["_ndgriddata"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/polyint.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/polyint.py new file mode 100644 index 0000000000000000000000000000000000000000..dbdae2e90934e5b2c351a7add76d132413e47ee2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/polyint.py @@ -0,0 +1,26 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'BarycentricInterpolator', + 'KroghInterpolator', + 'approximate_taylor_polynomial', + 'barycentric_interpolate', + 'factorial', + 'float_factorial', + 'krogh_interpolate', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="polyint", + private_modules=["_polyint"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/rbf.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/rbf.py new file mode 100644 index 0000000000000000000000000000000000000000..7ae1facd687108fcba124e43f71eda01f45a48e3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/rbf.py @@ -0,0 +1,25 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.interpolate` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'Rbf', + 'cdist', + 'linalg', + 'pdist', + 'squareform', + 'xlogy', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="interpolate", module="rbf", + private_modules=["_rbf"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_bsplines.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_bsplines.py new file mode 100644 index 0000000000000000000000000000000000000000..3a75f4d30743aee33676ee11b7ff9da0628fe340 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_bsplines.py @@ -0,0 +1,2621 @@ +import os +import operator +import itertools + +import numpy as np +from numpy.testing import assert_equal, assert_allclose, assert_ +from pytest import raises as assert_raises +import pytest + +from scipy.interpolate import ( + BSpline, BPoly, PPoly, make_interp_spline, make_lsq_spline, _bspl, + splev, splrep, splprep, splder, splantider, sproot, splint, insert, + CubicSpline, NdBSpline, make_smoothing_spline, RegularGridInterpolator, +) +import scipy.linalg as sl +import scipy.sparse.linalg as ssl + +from scipy.interpolate._bsplines import (_not_a_knot, _augknt, + _woodbury_algorithm, _periodic_knots, + _make_interp_per_full_matr) +import scipy.interpolate._fitpack_impl as _impl +from scipy._lib._util import AxisError + +# XXX: move to the interpolate namespace +from scipy.interpolate._ndbspline import make_ndbspl + +from scipy.interpolate import dfitpack +from scipy.interpolate import _bsplines as _b + + +class TestBSpline: + + def test_ctor(self): + # knots should be an ordered 1-D array of finite real numbers + assert_raises((TypeError, ValueError), BSpline, + **dict(t=[1, 1.j], c=[1.], k=0)) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, BSpline, **dict(t=[1, np.nan], c=[1.], k=0)) + assert_raises(ValueError, BSpline, **dict(t=[1, np.inf], c=[1.], k=0)) + assert_raises(ValueError, BSpline, **dict(t=[1, -1], c=[1.], k=0)) + assert_raises(ValueError, BSpline, **dict(t=[[1], [1]], c=[1.], k=0)) + + # for n+k+1 knots and degree k need at least n coefficients + assert_raises(ValueError, BSpline, **dict(t=[0, 1, 2], c=[1], k=0)) + assert_raises(ValueError, BSpline, + **dict(t=[0, 1, 2, 3, 4], c=[1., 1.], k=2)) + + # non-integer orders + assert_raises(TypeError, BSpline, + **dict(t=[0., 0., 1., 2., 3., 4.], c=[1., 1., 1.], k="cubic")) + assert_raises(TypeError, BSpline, + **dict(t=[0., 0., 1., 2., 3., 4.], c=[1., 1., 1.], k=2.5)) + + # basic interval cannot have measure zero (here: [1..1]) + assert_raises(ValueError, BSpline, + **dict(t=[0., 0, 1, 1, 2, 3], c=[1., 1, 1], k=2)) + + # tck vs self.tck + n, k = 11, 3 + t = np.arange(n+k+1) + c = np.random.random(n) + b = BSpline(t, c, k) + + assert_allclose(t, b.t) + assert_allclose(c, b.c) + assert_equal(k, b.k) + + def test_tck(self): + b = _make_random_spline() + tck = b.tck + + assert_allclose(b.t, tck[0], atol=1e-15, rtol=1e-15) + assert_allclose(b.c, tck[1], atol=1e-15, rtol=1e-15) + assert_equal(b.k, tck[2]) + + # b.tck is read-only + with pytest.raises(AttributeError): + b.tck = 'foo' + + def test_degree_0(self): + xx = np.linspace(0, 1, 10) + + b = BSpline(t=[0, 1], c=[3.], k=0) + assert_allclose(b(xx), 3) + + b = BSpline(t=[0, 0.35, 1], c=[3, 4], k=0) + assert_allclose(b(xx), np.where(xx < 0.35, 3, 4)) + + def test_degree_1(self): + t = [0, 1, 2, 3, 4] + c = [1, 2, 3] + k = 1 + b = BSpline(t, c, k) + + x = np.linspace(1, 3, 50) + assert_allclose(c[0]*B_012(x) + c[1]*B_012(x-1) + c[2]*B_012(x-2), + b(x), atol=1e-14) + assert_allclose(splev(x, (t, c, k)), b(x), atol=1e-14) + + def test_bernstein(self): + # a special knot vector: Bernstein polynomials + k = 3 + t = np.asarray([0]*(k+1) + [1]*(k+1)) + c = np.asarray([1., 2., 3., 4.]) + bp = BPoly(c.reshape(-1, 1), [0, 1]) + bspl = BSpline(t, c, k) + + xx = np.linspace(-1., 2., 10) + assert_allclose(bp(xx, extrapolate=True), + bspl(xx, extrapolate=True), atol=1e-14) + assert_allclose(splev(xx, (t, c, k)), + bspl(xx), atol=1e-14) + + def test_rndm_naive_eval(self): + # test random coefficient spline *on the base interval*, + # t[k] <= x < t[-k-1] + b = _make_random_spline() + t, c, k = b.tck + xx = np.linspace(t[k], t[-k-1], 50) + y_b = b(xx) + + y_n = [_naive_eval(x, t, c, k) for x in xx] + assert_allclose(y_b, y_n, atol=1e-14) + + y_n2 = [_naive_eval_2(x, t, c, k) for x in xx] + assert_allclose(y_b, y_n2, atol=1e-14) + + def test_rndm_splev(self): + b = _make_random_spline() + t, c, k = b.tck + xx = np.linspace(t[k], t[-k-1], 50) + assert_allclose(b(xx), splev(xx, (t, c, k)), atol=1e-14) + + def test_rndm_splrep(self): + np.random.seed(1234) + x = np.sort(np.random.random(20)) + y = np.random.random(20) + + tck = splrep(x, y) + b = BSpline(*tck) + + t, k = b.t, b.k + xx = np.linspace(t[k], t[-k-1], 80) + assert_allclose(b(xx), splev(xx, tck), atol=1e-14) + + def test_rndm_unity(self): + b = _make_random_spline() + b.c = np.ones_like(b.c) + xx = np.linspace(b.t[b.k], b.t[-b.k-1], 100) + assert_allclose(b(xx), 1.) + + def test_vectorization(self): + n, k = 22, 3 + t = np.sort(np.random.random(n)) + c = np.random.random(size=(n, 6, 7)) + b = BSpline(t, c, k) + tm, tp = t[k], t[-k-1] + xx = tm + (tp - tm) * np.random.random((3, 4, 5)) + assert_equal(b(xx).shape, (3, 4, 5, 6, 7)) + + def test_len_c(self): + # for n+k+1 knots, only first n coefs are used. + # and BTW this is consistent with FITPACK + n, k = 33, 3 + t = np.sort(np.random.random(n+k+1)) + c = np.random.random(n) + + # pad coefficients with random garbage + c_pad = np.r_[c, np.random.random(k+1)] + + b, b_pad = BSpline(t, c, k), BSpline(t, c_pad, k) + + dt = t[-1] - t[0] + xx = np.linspace(t[0] - dt, t[-1] + dt, 50) + assert_allclose(b(xx), b_pad(xx), atol=1e-14) + assert_allclose(b(xx), splev(xx, (t, c, k)), atol=1e-14) + assert_allclose(b(xx), splev(xx, (t, c_pad, k)), atol=1e-14) + + def test_endpoints(self): + # base interval is closed + b = _make_random_spline() + t, _, k = b.tck + tm, tp = t[k], t[-k-1] + for extrap in (True, False): + assert_allclose(b([tm, tp], extrap), + b([tm + 1e-10, tp - 1e-10], extrap), atol=1e-9) + + def test_continuity(self): + # assert continuity at internal knots + b = _make_random_spline() + t, _, k = b.tck + assert_allclose(b(t[k+1:-k-1] - 1e-10), b(t[k+1:-k-1] + 1e-10), + atol=1e-9) + + def test_extrap(self): + b = _make_random_spline() + t, c, k = b.tck + dt = t[-1] - t[0] + xx = np.linspace(t[k] - dt, t[-k-1] + dt, 50) + mask = (t[k] < xx) & (xx < t[-k-1]) + + # extrap has no effect within the base interval + assert_allclose(b(xx[mask], extrapolate=True), + b(xx[mask], extrapolate=False)) + + # extrapolated values agree with FITPACK + assert_allclose(b(xx, extrapolate=True), + splev(xx, (t, c, k), ext=0)) + + def test_default_extrap(self): + # BSpline defaults to extrapolate=True + b = _make_random_spline() + t, _, k = b.tck + xx = [t[0] - 1, t[-1] + 1] + yy = b(xx) + assert_(not np.all(np.isnan(yy))) + + def test_periodic_extrap(self): + np.random.seed(1234) + t = np.sort(np.random.random(8)) + c = np.random.random(4) + k = 3 + b = BSpline(t, c, k, extrapolate='periodic') + n = t.size - (k + 1) + + dt = t[-1] - t[0] + xx = np.linspace(t[k] - dt, t[n] + dt, 50) + xy = t[k] + (xx - t[k]) % (t[n] - t[k]) + assert_allclose(b(xx), splev(xy, (t, c, k))) + + # Direct check + xx = [-1, 0, 0.5, 1] + xy = t[k] + (xx - t[k]) % (t[n] - t[k]) + assert_equal(b(xx, extrapolate='periodic'), b(xy, extrapolate=True)) + + def test_ppoly(self): + b = _make_random_spline() + t, c, k = b.tck + pp = PPoly.from_spline((t, c, k)) + + xx = np.linspace(t[k], t[-k], 100) + assert_allclose(b(xx), pp(xx), atol=1e-14, rtol=1e-14) + + def test_derivative_rndm(self): + b = _make_random_spline() + t, c, k = b.tck + xx = np.linspace(t[0], t[-1], 50) + xx = np.r_[xx, t] + + for der in range(1, k+1): + yd = splev(xx, (t, c, k), der=der) + assert_allclose(yd, b(xx, nu=der), atol=1e-14) + + # higher derivatives all vanish + assert_allclose(b(xx, nu=k+1), 0, atol=1e-14) + + def test_derivative_jumps(self): + # example from de Boor, Chap IX, example (24) + # NB: knots augmented & corresp coefs are zeroed out + # in agreement with the convention (29) + k = 2 + t = [-1, -1, 0, 1, 1, 3, 4, 6, 6, 6, 7, 7] + np.random.seed(1234) + c = np.r_[0, 0, np.random.random(5), 0, 0] + b = BSpline(t, c, k) + + # b is continuous at x != 6 (triple knot) + x = np.asarray([1, 3, 4, 6]) + assert_allclose(b(x[x != 6] - 1e-10), + b(x[x != 6] + 1e-10)) + assert_(not np.allclose(b(6.-1e-10), b(6+1e-10))) + + # 1st derivative jumps at double knots, 1 & 6: + x0 = np.asarray([3, 4]) + assert_allclose(b(x0 - 1e-10, nu=1), + b(x0 + 1e-10, nu=1)) + x1 = np.asarray([1, 6]) + assert_(not np.all(np.allclose(b(x1 - 1e-10, nu=1), + b(x1 + 1e-10, nu=1)))) + + # 2nd derivative is not guaranteed to be continuous either + assert_(not np.all(np.allclose(b(x - 1e-10, nu=2), + b(x + 1e-10, nu=2)))) + + def test_basis_element_quadratic(self): + xx = np.linspace(-1, 4, 20) + b = BSpline.basis_element(t=[0, 1, 2, 3]) + assert_allclose(b(xx), + splev(xx, (b.t, b.c, b.k)), atol=1e-14) + assert_allclose(b(xx), + B_0123(xx), atol=1e-14) + + b = BSpline.basis_element(t=[0, 1, 1, 2]) + xx = np.linspace(0, 2, 10) + assert_allclose(b(xx), + np.where(xx < 1, xx*xx, (2.-xx)**2), atol=1e-14) + + def test_basis_element_rndm(self): + b = _make_random_spline() + t, c, k = b.tck + xx = np.linspace(t[k], t[-k-1], 20) + assert_allclose(b(xx), _sum_basis_elements(xx, t, c, k), atol=1e-14) + + def test_cmplx(self): + b = _make_random_spline() + t, c, k = b.tck + cc = c * (1. + 3.j) + + b = BSpline(t, cc, k) + b_re = BSpline(t, b.c.real, k) + b_im = BSpline(t, b.c.imag, k) + + xx = np.linspace(t[k], t[-k-1], 20) + assert_allclose(b(xx).real, b_re(xx), atol=1e-14) + assert_allclose(b(xx).imag, b_im(xx), atol=1e-14) + + def test_nan(self): + # nan in, nan out. + b = BSpline.basis_element([0, 1, 1, 2]) + assert_(np.isnan(b(np.nan))) + + def test_derivative_method(self): + b = _make_random_spline(k=5) + t, c, k = b.tck + b0 = BSpline(t, c, k) + xx = np.linspace(t[k], t[-k-1], 20) + for j in range(1, k): + b = b.derivative() + assert_allclose(b0(xx, j), b(xx), atol=1e-12, rtol=1e-12) + + def test_antiderivative_method(self): + b = _make_random_spline() + t, c, k = b.tck + xx = np.linspace(t[k], t[-k-1], 20) + assert_allclose(b.antiderivative().derivative()(xx), + b(xx), atol=1e-14, rtol=1e-14) + + # repeat with N-D array for c + c = np.c_[c, c, c] + c = np.dstack((c, c)) + b = BSpline(t, c, k) + assert_allclose(b.antiderivative().derivative()(xx), + b(xx), atol=1e-14, rtol=1e-14) + + def test_integral(self): + b = BSpline.basis_element([0, 1, 2]) # x for x < 1 else 2 - x + assert_allclose(b.integrate(0, 1), 0.5) + assert_allclose(b.integrate(1, 0), -1 * 0.5) + assert_allclose(b.integrate(1, 0), -0.5) + + # extrapolate or zeros outside of [0, 2]; default is yes + assert_allclose(b.integrate(-1, 1), 0) + assert_allclose(b.integrate(-1, 1, extrapolate=True), 0) + assert_allclose(b.integrate(-1, 1, extrapolate=False), 0.5) + assert_allclose(b.integrate(1, -1, extrapolate=False), -1 * 0.5) + + # Test ``_fitpack._splint()`` + assert_allclose(b.integrate(1, -1, extrapolate=False), + _impl.splint(1, -1, b.tck)) + + # Test ``extrapolate='periodic'``. + b.extrapolate = 'periodic' + i = b.antiderivative() + period_int = i(2) - i(0) + + assert_allclose(b.integrate(0, 2), period_int) + assert_allclose(b.integrate(2, 0), -1 * period_int) + assert_allclose(b.integrate(-9, -7), period_int) + assert_allclose(b.integrate(-8, -4), 2 * period_int) + + assert_allclose(b.integrate(0.5, 1.5), i(1.5) - i(0.5)) + assert_allclose(b.integrate(1.5, 3), i(1) - i(0) + i(2) - i(1.5)) + assert_allclose(b.integrate(1.5 + 12, 3 + 12), + i(1) - i(0) + i(2) - i(1.5)) + assert_allclose(b.integrate(1.5, 3 + 12), + i(1) - i(0) + i(2) - i(1.5) + 6 * period_int) + + assert_allclose(b.integrate(0, -1), i(0) - i(1)) + assert_allclose(b.integrate(-9, -10), i(0) - i(1)) + assert_allclose(b.integrate(0, -9), i(1) - i(2) - 4 * period_int) + + def test_integrate_ppoly(self): + # test .integrate method to be consistent with PPoly.integrate + x = [0, 1, 2, 3, 4] + b = make_interp_spline(x, x) + b.extrapolate = 'periodic' + p = PPoly.from_spline(b) + + for x0, x1 in [(-5, 0.5), (0.5, 5), (-4, 13)]: + assert_allclose(b.integrate(x0, x1), + p.integrate(x0, x1)) + + def test_subclassing(self): + # classmethods should not decay to the base class + class B(BSpline): + pass + + b = B.basis_element([0, 1, 2, 2]) + assert_equal(b.__class__, B) + assert_equal(b.derivative().__class__, B) + assert_equal(b.antiderivative().__class__, B) + + @pytest.mark.parametrize('axis', range(-4, 4)) + def test_axis(self, axis): + n, k = 22, 3 + t = np.linspace(0, 1, n + k + 1) + sh = [6, 7, 8] + # We need the positive axis for some of the indexing and slices used + # in this test. + pos_axis = axis % 4 + sh.insert(pos_axis, n) # [22, 6, 7, 8] etc + c = np.random.random(size=sh) + b = BSpline(t, c, k, axis=axis) + assert_equal(b.c.shape, + [sh[pos_axis],] + sh[:pos_axis] + sh[pos_axis+1:]) + + xp = np.random.random((3, 4, 5)) + assert_equal(b(xp).shape, + sh[:pos_axis] + list(xp.shape) + sh[pos_axis+1:]) + + # -c.ndim <= axis < c.ndim + for ax in [-c.ndim - 1, c.ndim]: + assert_raises(AxisError, BSpline, + **dict(t=t, c=c, k=k, axis=ax)) + + # derivative, antiderivative keeps the axis + for b1 in [BSpline(t, c, k, axis=axis).derivative(), + BSpline(t, c, k, axis=axis).derivative(2), + BSpline(t, c, k, axis=axis).antiderivative(), + BSpline(t, c, k, axis=axis).antiderivative(2)]: + assert_equal(b1.axis, b.axis) + + def test_neg_axis(self): + k = 2 + t = [0, 1, 2, 3, 4, 5, 6] + c = np.array([[-1, 2, 0, -1], [2, 0, -3, 1]]) + + spl = BSpline(t, c, k, axis=-1) + spl0 = BSpline(t, c[0], k) + spl1 = BSpline(t, c[1], k) + assert_equal(spl(2.5), [spl0(2.5), spl1(2.5)]) + + def test_design_matrix_bc_types(self): + ''' + Splines with different boundary conditions are built on different + types of vectors of knots. As far as design matrix depends only on + vector of knots, `k` and `x` it is useful to make tests for different + boundary conditions (and as following different vectors of knots). + ''' + def run_design_matrix_tests(n, k, bc_type): + ''' + To avoid repetition of code the following function is provided. + ''' + np.random.seed(1234) + x = np.sort(np.random.random_sample(n) * 40 - 20) + y = np.random.random_sample(n) * 40 - 20 + if bc_type == "periodic": + y[0] = y[-1] + + bspl = make_interp_spline(x, y, k=k, bc_type=bc_type) + + c = np.eye(len(bspl.t) - k - 1) + des_matr_def = BSpline(bspl.t, c, k)(x) + des_matr_csr = BSpline.design_matrix(x, + bspl.t, + k).toarray() + assert_allclose(des_matr_csr @ bspl.c, y, atol=1e-14) + assert_allclose(des_matr_def, des_matr_csr, atol=1e-14) + + # "clamped" and "natural" work only with `k = 3` + n = 11 + k = 3 + for bc in ["clamped", "natural"]: + run_design_matrix_tests(n, k, bc) + + # "not-a-knot" works with odd `k` + for k in range(3, 8, 2): + run_design_matrix_tests(n, k, "not-a-knot") + + # "periodic" works with any `k` (even more than `n`) + n = 5 # smaller `n` to test `k > n` case + for k in range(2, 7): + run_design_matrix_tests(n, k, "periodic") + + @pytest.mark.parametrize('extrapolate', [False, True, 'periodic']) + @pytest.mark.parametrize('degree', range(5)) + def test_design_matrix_same_as_BSpline_call(self, extrapolate, degree): + """Test that design_matrix(x) is equivalent to BSpline(..)(x).""" + np.random.seed(1234) + x = np.random.random_sample(10 * (degree + 1)) + xmin, xmax = np.amin(x), np.amax(x) + k = degree + t = np.r_[np.linspace(xmin - 2, xmin - 1, degree), + np.linspace(xmin, xmax, 2 * (degree + 1)), + np.linspace(xmax + 1, xmax + 2, degree)] + c = np.eye(len(t) - k - 1) + bspline = BSpline(t, c, k, extrapolate) + assert_allclose( + bspline(x), BSpline.design_matrix(x, t, k, extrapolate).toarray() + ) + + # extrapolation regime + x = np.array([xmin - 10, xmin - 1, xmax + 1.5, xmax + 10]) + if not extrapolate: + with pytest.raises(ValueError): + BSpline.design_matrix(x, t, k, extrapolate) + else: + assert_allclose( + bspline(x), + BSpline.design_matrix(x, t, k, extrapolate).toarray() + ) + + def test_design_matrix_x_shapes(self): + # test for different `x` shapes + np.random.seed(1234) + n = 10 + k = 3 + x = np.sort(np.random.random_sample(n) * 40 - 20) + y = np.random.random_sample(n) * 40 - 20 + + bspl = make_interp_spline(x, y, k=k) + for i in range(1, 4): + xc = x[:i] + yc = y[:i] + des_matr_csr = BSpline.design_matrix(xc, + bspl.t, + k).toarray() + assert_allclose(des_matr_csr @ bspl.c, yc, atol=1e-14) + + def test_design_matrix_t_shapes(self): + # test for minimal possible `t` shape + t = [1., 1., 1., 2., 3., 4., 4., 4.] + des_matr = BSpline.design_matrix(2., t, 3).toarray() + assert_allclose(des_matr, + [[0.25, 0.58333333, 0.16666667, 0.]], + atol=1e-14) + + def test_design_matrix_asserts(self): + np.random.seed(1234) + n = 10 + k = 3 + x = np.sort(np.random.random_sample(n) * 40 - 20) + y = np.random.random_sample(n) * 40 - 20 + bspl = make_interp_spline(x, y, k=k) + # invalid vector of knots (should be a 1D non-descending array) + # here the actual vector of knots is reversed, so it is invalid + with assert_raises(ValueError): + BSpline.design_matrix(x, bspl.t[::-1], k) + k = 2 + t = [0., 1., 2., 3., 4., 5.] + x = [1., 2., 3., 4.] + # out of bounds + with assert_raises(ValueError): + BSpline.design_matrix(x, t, k) + + @pytest.mark.parametrize('bc_type', ['natural', 'clamped', + 'periodic', 'not-a-knot']) + def test_from_power_basis(self, bc_type): + np.random.seed(1234) + x = np.sort(np.random.random(20)) + y = np.random.random(20) + if bc_type == 'periodic': + y[-1] = y[0] + cb = CubicSpline(x, y, bc_type=bc_type) + bspl = BSpline.from_power_basis(cb, bc_type=bc_type) + xx = np.linspace(0, 1, 20) + assert_allclose(cb(xx), bspl(xx), atol=1e-15) + bspl_new = make_interp_spline(x, y, bc_type=bc_type) + assert_allclose(bspl.c, bspl_new.c, atol=1e-15) + + @pytest.mark.parametrize('bc_type', ['natural', 'clamped', + 'periodic', 'not-a-knot']) + def test_from_power_basis_complex(self, bc_type): + np.random.seed(1234) + x = np.sort(np.random.random(20)) + y = np.random.random(20) + np.random.random(20) * 1j + if bc_type == 'periodic': + y[-1] = y[0] + cb = CubicSpline(x, y, bc_type=bc_type) + bspl = BSpline.from_power_basis(cb, bc_type=bc_type) + bspl_new_real = make_interp_spline(x, y.real, bc_type=bc_type) + bspl_new_imag = make_interp_spline(x, y.imag, bc_type=bc_type) + assert_equal(bspl.c.dtype, (bspl_new_real.c + + 1j * bspl_new_imag.c).dtype) + assert_allclose(bspl.c, bspl_new_real.c + + 1j * bspl_new_imag.c, atol=1e-15) + + def test_from_power_basis_exmp(self): + ''' + For x = [0, 1, 2, 3, 4] and y = [1, 1, 1, 1, 1] + the coefficients of Cubic Spline in the power basis: + + $[[0, 0, 0, 0, 0],\\$ + $[0, 0, 0, 0, 0],\\$ + $[0, 0, 0, 0, 0],\\$ + $[1, 1, 1, 1, 1]]$ + + It could be shown explicitly that coefficients of the interpolating + function in B-spline basis are c = [1, 1, 1, 1, 1, 1, 1] + ''' + x = np.array([0, 1, 2, 3, 4]) + y = np.array([1, 1, 1, 1, 1]) + bspl = BSpline.from_power_basis(CubicSpline(x, y, bc_type='natural'), + bc_type='natural') + assert_allclose(bspl.c, [1, 1, 1, 1, 1, 1, 1], atol=1e-15) + + def test_read_only(self): + # BSpline must work on read-only knots and coefficients. + t = np.array([0, 1]) + c = np.array([3.0]) + t.setflags(write=False) + c.setflags(write=False) + + xx = np.linspace(0, 1, 10) + xx.setflags(write=False) + + b = BSpline(t=t, c=c, k=0) + assert_allclose(b(xx), 3) + + +class TestInsert: + + @pytest.mark.parametrize('xval', [0.0, 1.0, 2.5, 4, 6.5, 7.0]) + def test_insert(self, xval): + # insert a knot, incl edges (0.0, 7.0) and exactly at an existing knot (4.0) + x = np.arange(8) + y = np.sin(x)**3 + spl = make_interp_spline(x, y, k=3) + + spl_1f = insert(xval, spl) # FITPACK + spl_1 = spl.insert_knot(xval) + + assert_allclose(spl_1.t, spl_1f.t, atol=1e-15) + assert_allclose(spl_1.c, spl_1f.c[:-spl.k-1], atol=1e-15) + + # knot insertion preserves values, unless multiplicity >= k+1 + xx = x if xval != x[-1] else x[:-1] + xx = np.r_[xx, 0.5*(x[1:] + x[:-1])] + assert_allclose(spl(xx), spl_1(xx), atol=1e-15) + + # ... repeat with ndim > 1 + y1 = np.cos(x)**3 + spl_y1 = make_interp_spline(x, y1, k=3) + spl_yy = make_interp_spline(x, np.c_[y, y1], k=3) + spl_yy1 = spl_yy.insert_knot(xval) + + assert_allclose(spl_yy1.t, spl_1.t, atol=1e-15) + assert_allclose(spl_yy1.c, np.c_[spl.insert_knot(xval).c, + spl_y1.insert_knot(xval).c], atol=1e-15) + + xx = x if xval != x[-1] else x[:-1] + xx = np.r_[xx, 0.5*(x[1:] + x[:-1])] + assert_allclose(spl_yy(xx), spl_yy1(xx), atol=1e-15) + + + @pytest.mark.parametrize( + 'xval, m', [(0.0, 2), (1.0, 3), (1.5, 5), (4, 2), (7.0, 2)] + ) + def test_insert_multi(self, xval, m): + x = np.arange(8) + y = np.sin(x)**3 + spl = make_interp_spline(x, y, k=3) + + spl_1f = insert(xval, spl, m=m) + spl_1 = spl.insert_knot(xval, m) + + assert_allclose(spl_1.t, spl_1f.t, atol=1e-15) + assert_allclose(spl_1.c, spl_1f.c[:-spl.k-1], atol=1e-15) + + xx = x if xval != x[-1] else x[:-1] + xx = np.r_[xx, 0.5*(x[1:] + x[:-1])] + assert_allclose(spl(xx), spl_1(xx), atol=1e-15) + + def test_insert_random(self): + rng = np.random.default_rng(12345) + n, k = 11, 3 + + t = np.sort(rng.uniform(size=n+k+1)) + c = rng.uniform(size=(n, 3, 2)) + spl = BSpline(t, c, k) + + xv = rng.uniform(low=t[k+1], high=t[-k-1]) + spl_1 = spl.insert_knot(xv) + + xx = rng.uniform(low=t[k+1], high=t[-k-1], size=33) + assert_allclose(spl(xx), spl_1(xx), atol=1e-15) + + @pytest.mark.parametrize('xv', [0, 0.1, 2.0, 4.0, 4.5, # l.h. edge + 5.5, 6.0, 6.1, 7.0] # r.h. edge + ) + def test_insert_periodic(self, xv): + x = np.arange(8) + y = np.sin(x)**3 + tck = splrep(x, y, k=3) + spl = BSpline(*tck, extrapolate="periodic") + + spl_1 = spl.insert_knot(xv) + tf, cf, k = insert(xv, spl.tck, per=True) + + assert_allclose(spl_1.t, tf, atol=1e-15) + assert_allclose(spl_1.c[:-k-1], cf[:-k-1], atol=1e-15) + + xx = np.random.default_rng(1234).uniform(low=0, high=7, size=41) + assert_allclose(spl_1(xx), splev(xx, (tf, cf, k)), atol=1e-15) + + def test_insert_periodic_too_few_internal_knots(self): + # both FITPACK and spl.insert_knot raise when there's not enough + # internal knots to make a periodic extension. + # Below the internal knots are 2, 3, , 4, 5 + # ^ + # 2, 3, 3.5, 4, 5 + # so two knots from each side from the new one, while need at least + # from either left or right. + xv = 3.5 + k = 3 + t = np.array([0]*(k+1) + [2, 3, 4, 5] + [7]*(k+1)) + c = np.ones(len(t) - k - 1) + spl = BSpline(t, c, k, extrapolate="periodic") + + with assert_raises(ValueError): + insert(xv, (t, c, k), per=True) + + with assert_raises(ValueError): + spl.insert_knot(xv) + + def test_insert_no_extrap(self): + k = 3 + t = np.array([0]*(k+1) + [2, 3, 4, 5] + [7]*(k+1)) + c = np.ones(len(t) - k - 1) + spl = BSpline(t, c, k) + + with assert_raises(ValueError): + spl.insert_knot(-1) + + with assert_raises(ValueError): + spl.insert_knot(8) + + with assert_raises(ValueError): + spl.insert_knot(3, m=0) + + +def test_knots_multiplicity(): + # Take a spline w/ random coefficients, throw in knots of varying + # multiplicity. + + def check_splev(b, j, der=0, atol=1e-14, rtol=1e-14): + # check evaluations against FITPACK, incl extrapolations + t, c, k = b.tck + x = np.unique(t) + x = np.r_[t[0]-0.1, 0.5*(x[1:] + x[:1]), t[-1]+0.1] + assert_allclose(splev(x, (t, c, k), der), b(x, der), + atol=atol, rtol=rtol, err_msg=f'der = {der} k = {b.k}') + + # test loop itself + # [the index `j` is for interpreting the traceback in case of a failure] + for k in [1, 2, 3, 4, 5]: + b = _make_random_spline(k=k) + for j, b1 in enumerate(_make_multiples(b)): + check_splev(b1, j) + for der in range(1, k+1): + check_splev(b1, j, der, 1e-12, 1e-12) + + +### stolen from @pv, verbatim +def _naive_B(x, k, i, t): + """ + Naive way to compute B-spline basis functions. Useful only for testing! + computes B(x; t[i],..., t[i+k+1]) + """ + if k == 0: + return 1.0 if t[i] <= x < t[i+1] else 0.0 + if t[i+k] == t[i]: + c1 = 0.0 + else: + c1 = (x - t[i])/(t[i+k] - t[i]) * _naive_B(x, k-1, i, t) + if t[i+k+1] == t[i+1]: + c2 = 0.0 + else: + c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * _naive_B(x, k-1, i+1, t) + return (c1 + c2) + + +### stolen from @pv, verbatim +def _naive_eval(x, t, c, k): + """ + Naive B-spline evaluation. Useful only for testing! + """ + if x == t[k]: + i = k + else: + i = np.searchsorted(t, x) - 1 + assert t[i] <= x <= t[i+1] + assert i >= k and i < len(t) - k + return sum(c[i-j] * _naive_B(x, k, i-j, t) for j in range(0, k+1)) + + +def _naive_eval_2(x, t, c, k): + """Naive B-spline evaluation, another way.""" + n = len(t) - (k+1) + assert n >= k+1 + assert len(c) >= n + assert t[k] <= x <= t[n] + return sum(c[i] * _naive_B(x, k, i, t) for i in range(n)) + + +def _sum_basis_elements(x, t, c, k): + n = len(t) - (k+1) + assert n >= k+1 + assert len(c) >= n + s = 0. + for i in range(n): + b = BSpline.basis_element(t[i:i+k+2], extrapolate=False)(x) + s += c[i] * np.nan_to_num(b) # zero out out-of-bounds elements + return s + + +def B_012(x): + """ A linear B-spline function B(x | 0, 1, 2).""" + x = np.atleast_1d(x) + return np.piecewise(x, [(x < 0) | (x > 2), + (x >= 0) & (x < 1), + (x >= 1) & (x <= 2)], + [lambda x: 0., lambda x: x, lambda x: 2.-x]) + + +def B_0123(x, der=0): + """A quadratic B-spline function B(x | 0, 1, 2, 3).""" + x = np.atleast_1d(x) + conds = [x < 1, (x > 1) & (x < 2), x > 2] + if der == 0: + funcs = [lambda x: x*x/2., + lambda x: 3./4 - (x-3./2)**2, + lambda x: (3.-x)**2 / 2] + elif der == 2: + funcs = [lambda x: 1., + lambda x: -2., + lambda x: 1.] + else: + raise ValueError('never be here: der=%s' % der) + pieces = np.piecewise(x, conds, funcs) + return pieces + + +def _make_random_spline(n=35, k=3): + np.random.seed(123) + t = np.sort(np.random.random(n+k+1)) + c = np.random.random(n) + return BSpline.construct_fast(t, c, k) + + +def _make_multiples(b): + """Increase knot multiplicity.""" + c, k = b.c, b.k + + t1 = b.t.copy() + t1[17:19] = t1[17] + t1[22] = t1[21] + yield BSpline(t1, c, k) + + t1 = b.t.copy() + t1[:k+1] = t1[0] + yield BSpline(t1, c, k) + + t1 = b.t.copy() + t1[-k-1:] = t1[-1] + yield BSpline(t1, c, k) + + +class TestInterop: + # + # Test that FITPACK-based spl* functions can deal with BSpline objects + # + def setup_method(self): + xx = np.linspace(0, 4.*np.pi, 41) + yy = np.cos(xx) + b = make_interp_spline(xx, yy) + self.tck = (b.t, b.c, b.k) + self.xx, self.yy, self.b = xx, yy, b + + self.xnew = np.linspace(0, 4.*np.pi, 21) + + c2 = np.c_[b.c, b.c, b.c] + self.c2 = np.dstack((c2, c2)) + self.b2 = BSpline(b.t, self.c2, b.k) + + def test_splev(self): + xnew, b, b2 = self.xnew, self.b, self.b2 + + # check that splev works with 1-D array of coefficients + # for array and scalar `x` + assert_allclose(splev(xnew, b), + b(xnew), atol=1e-15, rtol=1e-15) + assert_allclose(splev(xnew, b.tck), + b(xnew), atol=1e-15, rtol=1e-15) + assert_allclose([splev(x, b) for x in xnew], + b(xnew), atol=1e-15, rtol=1e-15) + + # With N-D coefficients, there's a quirck: + # splev(x, BSpline) is equivalent to BSpline(x) + with assert_raises(ValueError, match="Calling splev.. with BSpline"): + splev(xnew, b2) + + # However, splev(x, BSpline.tck) needs some transposes. This is because + # BSpline interpolates along the first axis, while the legacy FITPACK + # wrapper does list(map(...)) which effectively interpolates along the + # last axis. Like so: + sh = tuple(range(1, b2.c.ndim)) + (0,) # sh = (1, 2, 0) + cc = b2.c.transpose(sh) + tck = (b2.t, cc, b2.k) + assert_allclose(splev(xnew, tck), + b2(xnew).transpose(sh), atol=1e-15, rtol=1e-15) + + def test_splrep(self): + x, y = self.xx, self.yy + # test that "new" splrep is equivalent to _impl.splrep + tck = splrep(x, y) + t, c, k = _impl.splrep(x, y) + assert_allclose(tck[0], t, atol=1e-15) + assert_allclose(tck[1], c, atol=1e-15) + assert_equal(tck[2], k) + + # also cover the `full_output=True` branch + tck_f, _, _, _ = splrep(x, y, full_output=True) + assert_allclose(tck_f[0], t, atol=1e-15) + assert_allclose(tck_f[1], c, atol=1e-15) + assert_equal(tck_f[2], k) + + # test that the result of splrep roundtrips with splev: + # evaluate the spline on the original `x` points + yy = splev(x, tck) + assert_allclose(y, yy, atol=1e-15) + + # ... and also it roundtrips if wrapped in a BSpline + b = BSpline(*tck) + assert_allclose(y, b(x), atol=1e-15) + + def test_splrep_errors(self): + # test that both "old" and "new" splrep raise for an N-D ``y`` array + # with n > 1 + x, y = self.xx, self.yy + y2 = np.c_[y, y] + with assert_raises(ValueError): + splrep(x, y2) + with assert_raises(ValueError): + _impl.splrep(x, y2) + + # input below minimum size + with assert_raises(TypeError, match="m > k must hold"): + splrep(x[:3], y[:3]) + with assert_raises(TypeError, match="m > k must hold"): + _impl.splrep(x[:3], y[:3]) + + def test_splprep(self): + x = np.arange(15).reshape((3, 5)) + b, u = splprep(x) + tck, u1 = _impl.splprep(x) + + # test the roundtrip with splev for both "old" and "new" output + assert_allclose(u, u1, atol=1e-15) + assert_allclose(splev(u, b), x, atol=1e-15) + assert_allclose(splev(u, tck), x, atol=1e-15) + + # cover the ``full_output=True`` branch + (b_f, u_f), _, _, _ = splprep(x, s=0, full_output=True) + assert_allclose(u, u_f, atol=1e-15) + assert_allclose(splev(u_f, b_f), x, atol=1e-15) + + def test_splprep_errors(self): + # test that both "old" and "new" code paths raise for x.ndim > 2 + x = np.arange(3*4*5).reshape((3, 4, 5)) + with assert_raises(ValueError, match="too many values to unpack"): + splprep(x) + with assert_raises(ValueError, match="too many values to unpack"): + _impl.splprep(x) + + # input below minimum size + x = np.linspace(0, 40, num=3) + with assert_raises(TypeError, match="m > k must hold"): + splprep([x]) + with assert_raises(TypeError, match="m > k must hold"): + _impl.splprep([x]) + + # automatically calculated parameters are non-increasing + # see gh-7589 + x = [-50.49072266, -50.49072266, -54.49072266, -54.49072266] + with assert_raises(ValueError, match="Invalid inputs"): + splprep([x]) + with assert_raises(ValueError, match="Invalid inputs"): + _impl.splprep([x]) + + # given non-increasing parameter values u + x = [1, 3, 2, 4] + u = [0, 0.3, 0.2, 1] + with assert_raises(ValueError, match="Invalid inputs"): + splprep(*[[x], None, u]) + + def test_sproot(self): + b, b2 = self.b, self.b2 + roots = np.array([0.5, 1.5, 2.5, 3.5])*np.pi + # sproot accepts a BSpline obj w/ 1-D coef array + assert_allclose(sproot(b), roots, atol=1e-7, rtol=1e-7) + assert_allclose(sproot((b.t, b.c, b.k)), roots, atol=1e-7, rtol=1e-7) + + # ... and deals with trailing dimensions if coef array is N-D + with assert_raises(ValueError, match="Calling sproot.. with BSpline"): + sproot(b2, mest=50) + + # and legacy behavior is preserved for a tck tuple w/ N-D coef + c2r = b2.c.transpose(1, 2, 0) + rr = np.asarray(sproot((b2.t, c2r, b2.k), mest=50)) + assert_equal(rr.shape, (3, 2, 4)) + assert_allclose(rr - roots, 0, atol=1e-12) + + def test_splint(self): + # test that splint accepts BSpline objects + b, b2 = self.b, self.b2 + assert_allclose(splint(0, 1, b), + splint(0, 1, b.tck), atol=1e-14) + assert_allclose(splint(0, 1, b), + b.integrate(0, 1), atol=1e-14) + + # ... and deals with N-D arrays of coefficients + with assert_raises(ValueError, match="Calling splint.. with BSpline"): + splint(0, 1, b2) + + # and the legacy behavior is preserved for a tck tuple w/ N-D coef + c2r = b2.c.transpose(1, 2, 0) + integr = np.asarray(splint(0, 1, (b2.t, c2r, b2.k))) + assert_equal(integr.shape, (3, 2)) + assert_allclose(integr, + splint(0, 1, b), atol=1e-14) + + def test_splder(self): + for b in [self.b, self.b2]: + # pad the c array (FITPACK convention) + ct = len(b.t) - len(b.c) + if ct > 0: + b.c = np.r_[b.c, np.zeros((ct,) + b.c.shape[1:])] + + for n in [1, 2, 3]: + bd = splder(b) + tck_d = _impl.splder((b.t, b.c, b.k)) + assert_allclose(bd.t, tck_d[0], atol=1e-15) + assert_allclose(bd.c, tck_d[1], atol=1e-15) + assert_equal(bd.k, tck_d[2]) + assert_(isinstance(bd, BSpline)) + assert_(isinstance(tck_d, tuple)) # back-compat: tck in and out + + def test_splantider(self): + for b in [self.b, self.b2]: + # pad the c array (FITPACK convention) + ct = len(b.t) - len(b.c) + if ct > 0: + b.c = np.r_[b.c, np.zeros((ct,) + b.c.shape[1:])] + + for n in [1, 2, 3]: + bd = splantider(b) + tck_d = _impl.splantider((b.t, b.c, b.k)) + assert_allclose(bd.t, tck_d[0], atol=1e-15) + assert_allclose(bd.c, tck_d[1], atol=1e-15) + assert_equal(bd.k, tck_d[2]) + assert_(isinstance(bd, BSpline)) + assert_(isinstance(tck_d, tuple)) # back-compat: tck in and out + + def test_insert(self): + b, b2, xx = self.b, self.b2, self.xx + + j = b.t.size // 2 + tn = 0.5*(b.t[j] + b.t[j+1]) + + bn, tck_n = insert(tn, b), insert(tn, (b.t, b.c, b.k)) + assert_allclose(splev(xx, bn), + splev(xx, tck_n), atol=1e-15) + assert_(isinstance(bn, BSpline)) + assert_(isinstance(tck_n, tuple)) # back-compat: tck in, tck out + + # for N-D array of coefficients, BSpline.c needs to be transposed + # after that, the results are equivalent. + sh = tuple(range(b2.c.ndim)) + c_ = b2.c.transpose(sh[1:] + (0,)) + tck_n2 = insert(tn, (b2.t, c_, b2.k)) + + bn2 = insert(tn, b2) + + # need a transpose for comparing the results, cf test_splev + assert_allclose(np.asarray(splev(xx, tck_n2)).transpose(2, 0, 1), + bn2(xx), atol=1e-15) + assert_(isinstance(bn2, BSpline)) + assert_(isinstance(tck_n2, tuple)) # back-compat: tck in, tck out + + +class TestInterp: + # + # Test basic ways of constructing interpolating splines. + # + xx = np.linspace(0., 2.*np.pi) + yy = np.sin(xx) + + def test_non_int_order(self): + with assert_raises(TypeError): + make_interp_spline(self.xx, self.yy, k=2.5) + + def test_order_0(self): + b = make_interp_spline(self.xx, self.yy, k=0) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + b = make_interp_spline(self.xx, self.yy, k=0, axis=-1) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + + def test_linear(self): + b = make_interp_spline(self.xx, self.yy, k=1) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + b = make_interp_spline(self.xx, self.yy, k=1, axis=-1) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + + @pytest.mark.parametrize('k', [0, 1, 2, 3]) + def test_incompatible_x_y(self, k): + x = [0, 1, 2, 3, 4, 5] + y = [0, 1, 2, 3, 4, 5, 6, 7] + with assert_raises(ValueError, match="Shapes of x"): + make_interp_spline(x, y, k=k) + + @pytest.mark.parametrize('k', [0, 1, 2, 3]) + def test_broken_x(self, k): + x = [0, 1, 1, 2, 3, 4] # duplicates + y = [0, 1, 2, 3, 4, 5] + with assert_raises(ValueError, match="x to not have duplicates"): + make_interp_spline(x, y, k=k) + + x = [0, 2, 1, 3, 4, 5] # unsorted + with assert_raises(ValueError, match="Expect x to be a 1D strictly"): + make_interp_spline(x, y, k=k) + + x = [0, 1, 2, 3, 4, 5] + x = np.asarray(x).reshape((1, -1)) # 1D + with assert_raises(ValueError, match="Expect x to be a 1D strictly"): + make_interp_spline(x, y, k=k) + + def test_not_a_knot(self): + for k in [3, 5]: + b = make_interp_spline(self.xx, self.yy, k) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + + def test_periodic(self): + # k = 5 here for more derivatives + b = make_interp_spline(self.xx, self.yy, k=5, bc_type='periodic') + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + # in periodic case it is expected equality of k-1 first + # derivatives at the boundaries + for i in range(1, 5): + assert_allclose(b(self.xx[0], nu=i), b(self.xx[-1], nu=i), atol=1e-11) + # tests for axis=-1 + b = make_interp_spline(self.xx, self.yy, k=5, bc_type='periodic', axis=-1) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + for i in range(1, 5): + assert_allclose(b(self.xx[0], nu=i), b(self.xx[-1], nu=i), atol=1e-11) + + @pytest.mark.parametrize('k', [2, 3, 4, 5, 6, 7]) + def test_periodic_random(self, k): + # tests for both cases (k > n and k <= n) + n = 5 + np.random.seed(1234) + x = np.sort(np.random.random_sample(n) * 10) + y = np.random.random_sample(n) * 100 + y[0] = y[-1] + b = make_interp_spline(x, y, k=k, bc_type='periodic') + assert_allclose(b(x), y, atol=1e-14) + + def test_periodic_axis(self): + n = self.xx.shape[0] + np.random.seed(1234) + x = np.random.random_sample(n) * 2 * np.pi + x = np.sort(x) + x[0] = 0. + x[-1] = 2 * np.pi + y = np.zeros((2, n)) + y[0] = np.sin(x) + y[1] = np.cos(x) + b = make_interp_spline(x, y, k=5, bc_type='periodic', axis=1) + for i in range(n): + assert_allclose(b(x[i]), y[:, i], atol=1e-14) + assert_allclose(b(x[0]), b(x[-1]), atol=1e-14) + + def test_periodic_points_exception(self): + # first and last points should match when periodic case expected + np.random.seed(1234) + k = 5 + n = 8 + x = np.sort(np.random.random_sample(n)) + y = np.random.random_sample(n) + y[0] = y[-1] - 1 # to be sure that they are not equal + with assert_raises(ValueError): + make_interp_spline(x, y, k=k, bc_type='periodic') + + def test_periodic_knots_exception(self): + # `periodic` case does not work with passed vector of knots + np.random.seed(1234) + k = 3 + n = 7 + x = np.sort(np.random.random_sample(n)) + y = np.random.random_sample(n) + t = np.zeros(n + 2 * k) + with assert_raises(ValueError): + make_interp_spline(x, y, k, t, 'periodic') + + @pytest.mark.parametrize('k', [2, 3, 4, 5]) + def test_periodic_splev(self, k): + # comparison values of periodic b-spline with splev + b = make_interp_spline(self.xx, self.yy, k=k, bc_type='periodic') + tck = splrep(self.xx, self.yy, per=True, k=k) + spl = splev(self.xx, tck) + assert_allclose(spl, b(self.xx), atol=1e-14) + + # comparison derivatives of periodic b-spline with splev + for i in range(1, k): + spl = splev(self.xx, tck, der=i) + assert_allclose(spl, b(self.xx, nu=i), atol=1e-10) + + def test_periodic_cubic(self): + # comparison values of cubic periodic b-spline with CubicSpline + b = make_interp_spline(self.xx, self.yy, k=3, bc_type='periodic') + cub = CubicSpline(self.xx, self.yy, bc_type='periodic') + assert_allclose(b(self.xx), cub(self.xx), atol=1e-14) + + # edge case: Cubic interpolation on 3 points + n = 3 + x = np.sort(np.random.random_sample(n) * 10) + y = np.random.random_sample(n) * 100 + y[0] = y[-1] + b = make_interp_spline(x, y, k=3, bc_type='periodic') + cub = CubicSpline(x, y, bc_type='periodic') + assert_allclose(b(x), cub(x), atol=1e-14) + + def test_periodic_full_matrix(self): + # comparison values of cubic periodic b-spline with + # solution of the system with full matrix + k = 3 + b = make_interp_spline(self.xx, self.yy, k=k, bc_type='periodic') + t = _periodic_knots(self.xx, k) + c = _make_interp_per_full_matr(self.xx, self.yy, t, k) + b1 = np.vectorize(lambda x: _naive_eval(x, t, c, k)) + assert_allclose(b(self.xx), b1(self.xx), atol=1e-14) + + def test_quadratic_deriv(self): + der = [(1, 8.)] # order, value: f'(x) = 8. + + # derivative at right-hand edge + b = make_interp_spline(self.xx, self.yy, k=2, bc_type=(None, der)) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + assert_allclose(b(self.xx[-1], 1), der[0][1], atol=1e-14, rtol=1e-14) + + # derivative at left-hand edge + b = make_interp_spline(self.xx, self.yy, k=2, bc_type=(der, None)) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + assert_allclose(b(self.xx[0], 1), der[0][1], atol=1e-14, rtol=1e-14) + + def test_cubic_deriv(self): + k = 3 + + # first derivatives at left & right edges: + der_l, der_r = [(1, 3.)], [(1, 4.)] + b = make_interp_spline(self.xx, self.yy, k, bc_type=(der_l, der_r)) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + assert_allclose([b(self.xx[0], 1), b(self.xx[-1], 1)], + [der_l[0][1], der_r[0][1]], atol=1e-14, rtol=1e-14) + + # 'natural' cubic spline, zero out 2nd derivatives at the boundaries + der_l, der_r = [(2, 0)], [(2, 0)] + b = make_interp_spline(self.xx, self.yy, k, bc_type=(der_l, der_r)) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + + def test_quintic_derivs(self): + k, n = 5, 7 + x = np.arange(n).astype(np.float64) + y = np.sin(x) + der_l = [(1, -12.), (2, 1)] + der_r = [(1, 8.), (2, 3.)] + b = make_interp_spline(x, y, k=k, bc_type=(der_l, der_r)) + assert_allclose(b(x), y, atol=1e-14, rtol=1e-14) + assert_allclose([b(x[0], 1), b(x[0], 2)], + [val for (nu, val) in der_l]) + assert_allclose([b(x[-1], 1), b(x[-1], 2)], + [val for (nu, val) in der_r]) + + @pytest.mark.xfail(reason='unstable') + def test_cubic_deriv_unstable(self): + # 1st and 2nd derivative at x[0], no derivative information at x[-1] + # The problem is not that it fails [who would use this anyway], + # the problem is that it fails *silently*, and I've no idea + # how to detect this sort of instability. + # In this particular case: it's OK for len(t) < 20, goes haywire + # at larger `len(t)`. + k = 3 + t = _augknt(self.xx, k) + + der_l = [(1, 3.), (2, 4.)] + b = make_interp_spline(self.xx, self.yy, k, t, bc_type=(der_l, None)) + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + + def test_knots_not_data_sites(self): + # Knots need not coincide with the data sites. + # use a quadratic spline, knots are at data averages, + # two additional constraints are zero 2nd derivatives at edges + k = 2 + t = np.r_[(self.xx[0],)*(k+1), + (self.xx[1:] + self.xx[:-1]) / 2., + (self.xx[-1],)*(k+1)] + b = make_interp_spline(self.xx, self.yy, k, t, + bc_type=([(2, 0)], [(2, 0)])) + + assert_allclose(b(self.xx), self.yy, atol=1e-14, rtol=1e-14) + assert_allclose([b(self.xx[0], 2), b(self.xx[-1], 2)], [0., 0.], + atol=1e-14) + + def test_minimum_points_and_deriv(self): + # interpolation of f(x) = x**3 between 0 and 1. f'(x) = 3 * xx**2 and + # f'(0) = 0, f'(1) = 3. + k = 3 + x = [0., 1.] + y = [0., 1.] + b = make_interp_spline(x, y, k, bc_type=([(1, 0.)], [(1, 3.)])) + + xx = np.linspace(0., 1.) + yy = xx**3 + assert_allclose(b(xx), yy, atol=1e-14, rtol=1e-14) + + def test_deriv_spec(self): + # If one of the derivatives is omitted, the spline definition is + # incomplete. + x = y = [1.0, 2, 3, 4, 5, 6] + + with assert_raises(ValueError): + make_interp_spline(x, y, bc_type=([(1, 0.)], None)) + + with assert_raises(ValueError): + make_interp_spline(x, y, bc_type=(1, 0.)) + + with assert_raises(ValueError): + make_interp_spline(x, y, bc_type=[(1, 0.)]) + + with assert_raises(ValueError): + make_interp_spline(x, y, bc_type=42) + + # CubicSpline expects`bc_type=(left_pair, right_pair)`, while + # here we expect `bc_type=(iterable, iterable)`. + l, r = (1, 0.0), (1, 0.0) + with assert_raises(ValueError): + make_interp_spline(x, y, bc_type=(l, r)) + + def test_complex(self): + k = 3 + xx = self.xx + yy = self.yy + 1.j*self.yy + + # first derivatives at left & right edges: + der_l, der_r = [(1, 3.j)], [(1, 4.+2.j)] + b = make_interp_spline(xx, yy, k, bc_type=(der_l, der_r)) + assert_allclose(b(xx), yy, atol=1e-14, rtol=1e-14) + assert_allclose([b(xx[0], 1), b(xx[-1], 1)], + [der_l[0][1], der_r[0][1]], atol=1e-14, rtol=1e-14) + + # also test zero and first order + for k in (0, 1): + b = make_interp_spline(xx, yy, k=k) + assert_allclose(b(xx), yy, atol=1e-14, rtol=1e-14) + + def test_int_xy(self): + x = np.arange(10).astype(int) + y = np.arange(10).astype(int) + + # Cython chokes on "buffer type mismatch" (construction) or + # "no matching signature found" (evaluation) + for k in (0, 1, 2, 3): + b = make_interp_spline(x, y, k=k) + b(x) + + def test_sliced_input(self): + # Cython code chokes on non C contiguous arrays + xx = np.linspace(-1, 1, 100) + + x = xx[::5] + y = xx[::5] + + for k in (0, 1, 2, 3): + make_interp_spline(x, y, k=k) + + def test_check_finite(self): + # check_finite defaults to True; nans and such trigger a ValueError + x = np.arange(10).astype(float) + y = x**2 + + for z in [np.nan, np.inf, -np.inf]: + y[-1] = z + assert_raises(ValueError, make_interp_spline, x, y) + + @pytest.mark.parametrize('k', [1, 2, 3, 5]) + def test_list_input(self, k): + # regression test for gh-8714: TypeError for x, y being lists and k=2 + x = list(range(10)) + y = [a**2 for a in x] + make_interp_spline(x, y, k=k) + + def test_multiple_rhs(self): + yy = np.c_[np.sin(self.xx), np.cos(self.xx)] + der_l = [(1, [1., 2.])] + der_r = [(1, [3., 4.])] + + b = make_interp_spline(self.xx, yy, k=3, bc_type=(der_l, der_r)) + assert_allclose(b(self.xx), yy, atol=1e-14, rtol=1e-14) + assert_allclose(b(self.xx[0], 1), der_l[0][1], atol=1e-14, rtol=1e-14) + assert_allclose(b(self.xx[-1], 1), der_r[0][1], atol=1e-14, rtol=1e-14) + + def test_shapes(self): + np.random.seed(1234) + k, n = 3, 22 + x = np.sort(np.random.random(size=n)) + y = np.random.random(size=(n, 5, 6, 7)) + + b = make_interp_spline(x, y, k) + assert_equal(b.c.shape, (n, 5, 6, 7)) + + # now throw in some derivatives + d_l = [(1, np.random.random((5, 6, 7)))] + d_r = [(1, np.random.random((5, 6, 7)))] + b = make_interp_spline(x, y, k, bc_type=(d_l, d_r)) + assert_equal(b.c.shape, (n + k - 1, 5, 6, 7)) + + def test_string_aliases(self): + yy = np.sin(self.xx) + + # a single string is duplicated + b1 = make_interp_spline(self.xx, yy, k=3, bc_type='natural') + b2 = make_interp_spline(self.xx, yy, k=3, bc_type=([(2, 0)], [(2, 0)])) + assert_allclose(b1.c, b2.c, atol=1e-15) + + # two strings are handled + b1 = make_interp_spline(self.xx, yy, k=3, + bc_type=('natural', 'clamped')) + b2 = make_interp_spline(self.xx, yy, k=3, + bc_type=([(2, 0)], [(1, 0)])) + assert_allclose(b1.c, b2.c, atol=1e-15) + + # one-sided BCs are OK + b1 = make_interp_spline(self.xx, yy, k=2, bc_type=(None, 'clamped')) + b2 = make_interp_spline(self.xx, yy, k=2, bc_type=(None, [(1, 0.0)])) + assert_allclose(b1.c, b2.c, atol=1e-15) + + # 'not-a-knot' is equivalent to None + b1 = make_interp_spline(self.xx, yy, k=3, bc_type='not-a-knot') + b2 = make_interp_spline(self.xx, yy, k=3, bc_type=None) + assert_allclose(b1.c, b2.c, atol=1e-15) + + # unknown strings do not pass + with assert_raises(ValueError): + make_interp_spline(self.xx, yy, k=3, bc_type='typo') + + # string aliases are handled for 2D values + yy = np.c_[np.sin(self.xx), np.cos(self.xx)] + der_l = [(1, [0., 0.])] + der_r = [(2, [0., 0.])] + b2 = make_interp_spline(self.xx, yy, k=3, bc_type=(der_l, der_r)) + b1 = make_interp_spline(self.xx, yy, k=3, + bc_type=('clamped', 'natural')) + assert_allclose(b1.c, b2.c, atol=1e-15) + + # ... and for N-D values: + np.random.seed(1234) + k, n = 3, 22 + x = np.sort(np.random.random(size=n)) + y = np.random.random(size=(n, 5, 6, 7)) + + # now throw in some derivatives + d_l = [(1, np.zeros((5, 6, 7)))] + d_r = [(1, np.zeros((5, 6, 7)))] + b1 = make_interp_spline(x, y, k, bc_type=(d_l, d_r)) + b2 = make_interp_spline(x, y, k, bc_type='clamped') + assert_allclose(b1.c, b2.c, atol=1e-15) + + def test_full_matrix(self): + np.random.seed(1234) + k, n = 3, 7 + x = np.sort(np.random.random(size=n)) + y = np.random.random(size=n) + t = _not_a_knot(x, k) + + b = make_interp_spline(x, y, k, t) + cf = make_interp_full_matr(x, y, t, k) + assert_allclose(b.c, cf, atol=1e-14, rtol=1e-14) + + def test_woodbury(self): + ''' + Random elements in diagonal matrix with blocks in the + left lower and right upper corners checking the + implementation of Woodbury algorithm. + ''' + np.random.seed(1234) + n = 201 + for k in range(3, 32, 2): + offset = int((k - 1) / 2) + a = np.diagflat(np.random.random((1, n))) + for i in range(1, offset + 1): + a[:-i, i:] += np.diagflat(np.random.random((1, n - i))) + a[i:, :-i] += np.diagflat(np.random.random((1, n - i))) + ur = np.random.random((offset, offset)) + a[:offset, -offset:] = ur + ll = np.random.random((offset, offset)) + a[-offset:, :offset] = ll + d = np.zeros((k, n)) + for i, j in enumerate(range(offset, -offset - 1, -1)): + if j < 0: + d[i, :j] = np.diagonal(a, offset=j) + else: + d[i, j:] = np.diagonal(a, offset=j) + b = np.random.random(n) + assert_allclose(_woodbury_algorithm(d, ur, ll, b, k), + np.linalg.solve(a, b), atol=1e-14) + + +def make_interp_full_matr(x, y, t, k): + """Assemble an spline order k with knots t to interpolate + y(x) using full matrices. + Not-a-knot BC only. + + This routine is here for testing only (even though it's functional). + """ + assert x.size == y.size + assert t.size == x.size + k + 1 + n = x.size + + A = np.zeros((n, n), dtype=np.float64) + + for j in range(n): + xval = x[j] + if xval == t[k]: + left = k + else: + left = np.searchsorted(t, xval) - 1 + + # fill a row + bb = _bspl.evaluate_all_bspl(t, k, xval, left) + A[j, left-k:left+1] = bb + + c = sl.solve(A, y) + return c + + +def make_lsq_full_matrix(x, y, t, k=3): + """Make the least-square spline, full matrices.""" + x, y, t = map(np.asarray, (x, y, t)) + m = x.size + n = t.size - k - 1 + + A = np.zeros((m, n), dtype=np.float64) + + for j in range(m): + xval = x[j] + # find interval + if xval == t[k]: + left = k + else: + left = np.searchsorted(t, xval) - 1 + + # fill a row + bb = _bspl.evaluate_all_bspl(t, k, xval, left) + A[j, left-k:left+1] = bb + + # have observation matrix, can solve the LSQ problem + B = np.dot(A.T, A) + Y = np.dot(A.T, y) + c = sl.solve(B, Y) + + return c, (A, Y) + + +class TestLSQ: + # + # Test make_lsq_spline + # + np.random.seed(1234) + n, k = 13, 3 + x = np.sort(np.random.random(n)) + y = np.random.random(n) + t = _augknt(np.linspace(x[0], x[-1], 7), k) + + def test_lstsq(self): + # check LSQ construction vs a full matrix version + x, y, t, k = self.x, self.y, self.t, self.k + + c0, AY = make_lsq_full_matrix(x, y, t, k) + b = make_lsq_spline(x, y, t, k) + + assert_allclose(b.c, c0) + assert_equal(b.c.shape, (t.size - k - 1,)) + + # also check against numpy.lstsq + aa, yy = AY + c1, _, _, _ = np.linalg.lstsq(aa, y, rcond=-1) + assert_allclose(b.c, c1) + + def test_weights(self): + # weights = 1 is same as None + x, y, t, k = self.x, self.y, self.t, self.k + w = np.ones_like(x) + + b = make_lsq_spline(x, y, t, k) + b_w = make_lsq_spline(x, y, t, k, w=w) + + assert_allclose(b.t, b_w.t, atol=1e-14) + assert_allclose(b.c, b_w.c, atol=1e-14) + assert_equal(b.k, b_w.k) + + def test_multiple_rhs(self): + x, t, k, n = self.x, self.t, self.k, self.n + y = np.random.random(size=(n, 5, 6, 7)) + + b = make_lsq_spline(x, y, t, k) + assert_equal(b.c.shape, (t.size-k-1, 5, 6, 7)) + + def test_complex(self): + # cmplx-valued `y` + x, t, k = self.x, self.t, self.k + yc = self.y * (1. + 2.j) + + b = make_lsq_spline(x, yc, t, k) + b_re = make_lsq_spline(x, yc.real, t, k) + b_im = make_lsq_spline(x, yc.imag, t, k) + + assert_allclose(b(x), b_re(x) + 1.j*b_im(x), atol=1e-15, rtol=1e-15) + + def test_int_xy(self): + x = np.arange(10).astype(int) + y = np.arange(10).astype(int) + t = _augknt(x, k=1) + # Cython chokes on "buffer type mismatch" + make_lsq_spline(x, y, t, k=1) + + def test_sliced_input(self): + # Cython code chokes on non C contiguous arrays + xx = np.linspace(-1, 1, 100) + + x = xx[::3] + y = xx[::3] + t = _augknt(x, 1) + make_lsq_spline(x, y, t, k=1) + + def test_checkfinite(self): + # check_finite defaults to True; nans and such trigger a ValueError + x = np.arange(12).astype(float) + y = x**2 + t = _augknt(x, 3) + + for z in [np.nan, np.inf, -np.inf]: + y[-1] = z + assert_raises(ValueError, make_lsq_spline, x, y, t) + + def test_read_only(self): + # Check that make_lsq_spline works with read only arrays + x, y, t = self.x, self.y, self.t + x.setflags(write=False) + y.setflags(write=False) + t.setflags(write=False) + make_lsq_spline(x=x, y=y, t=t) + + +def data_file(basename): + return os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'data', basename) + + +class TestSmoothingSpline: + # + # test make_smoothing_spline + # + def test_invalid_input(self): + np.random.seed(1234) + n = 100 + x = np.sort(np.random.random_sample(n) * 4 - 2) + y = x**2 * np.sin(4 * x) + x**3 + np.random.normal(0., 1.5, n) + + # ``x`` and ``y`` should have same shapes (1-D array) + with assert_raises(ValueError): + make_smoothing_spline(x, y[1:]) + with assert_raises(ValueError): + make_smoothing_spline(x[1:], y) + with assert_raises(ValueError): + make_smoothing_spline(x.reshape(1, n), y) + + # ``x`` should be an ascending array + with assert_raises(ValueError): + make_smoothing_spline(x[::-1], y) + + x_dupl = np.copy(x) + x_dupl[0] = x_dupl[1] + + with assert_raises(ValueError): + make_smoothing_spline(x_dupl, y) + + # x and y length must be >= 5 + x = np.arange(4) + y = np.ones(4) + exception_message = "``x`` and ``y`` length must be at least 5" + with pytest.raises(ValueError, match=exception_message): + make_smoothing_spline(x, y) + + def test_compare_with_GCVSPL(self): + """ + Data is generated in the following way: + >>> np.random.seed(1234) + >>> n = 100 + >>> x = np.sort(np.random.random_sample(n) * 4 - 2) + >>> y = np.sin(x) + np.random.normal(scale=.5, size=n) + >>> np.savetxt('x.csv', x) + >>> np.savetxt('y.csv', y) + + We obtain the result of performing the GCV smoothing splines + package (by Woltring, gcvspl) on the sample data points + using its version for Octave (https://github.com/srkuberski/gcvspl). + In order to use this implementation, one should clone the repository + and open the folder in Octave. + In Octave, we load up ``x`` and ``y`` (generated from Python code + above): + + >>> x = csvread('x.csv'); + >>> y = csvread('y.csv'); + + Then, in order to access the implementation, we compile gcvspl files in + Octave: + + >>> mex gcvsplmex.c gcvspl.c + >>> mex spldermex.c gcvspl.c + + The first function computes the vector of unknowns from the dataset + (x, y) while the second one evaluates the spline in certain points + with known vector of coefficients. + + >>> c = gcvsplmex( x, y, 2 ); + >>> y0 = spldermex( x, c, 2, x, 0 ); + + If we want to compare the results of the gcvspl code, we can save + ``y0`` in csv file: + + >>> csvwrite('y0.csv', y0); + + """ + # load the data sample + with np.load(data_file('gcvspl.npz')) as data: + # data points + x = data['x'] + y = data['y'] + + y_GCVSPL = data['y_GCVSPL'] + y_compr = make_smoothing_spline(x, y)(x) + + # such tolerance is explained by the fact that the spline is built + # using an iterative algorithm for minimizing the GCV criteria. These + # algorithms may vary, so the tolerance should be rather low. + assert_allclose(y_compr, y_GCVSPL, atol=1e-4, rtol=1e-4) + + def test_non_regularized_case(self): + """ + In case the regularization parameter is 0, the resulting spline + is an interpolation spline with natural boundary conditions. + """ + # create data sample + np.random.seed(1234) + n = 100 + x = np.sort(np.random.random_sample(n) * 4 - 2) + y = x**2 * np.sin(4 * x) + x**3 + np.random.normal(0., 1.5, n) + + spline_GCV = make_smoothing_spline(x, y, lam=0.) + spline_interp = make_interp_spline(x, y, 3, bc_type='natural') + + grid = np.linspace(x[0], x[-1], 2 * n) + assert_allclose(spline_GCV(grid), + spline_interp(grid), + atol=1e-15) + + def test_weighted_smoothing_spline(self): + # create data sample + np.random.seed(1234) + n = 100 + x = np.sort(np.random.random_sample(n) * 4 - 2) + y = x**2 * np.sin(4 * x) + x**3 + np.random.normal(0., 1.5, n) + + spl = make_smoothing_spline(x, y) + + # in order not to iterate over all of the indices, we select 10 of + # them randomly + for ind in np.random.choice(range(100), size=10): + w = np.ones(n) + w[ind] = 30. + spl_w = make_smoothing_spline(x, y, w) + # check that spline with weight in a certain point is closer to the + # original point than the one without weights + orig = abs(spl(x[ind]) - y[ind]) + weighted = abs(spl_w(x[ind]) - y[ind]) + + if orig < weighted: + raise ValueError(f'Spline with weights should be closer to the' + f' points than the original one: {orig:.4} < ' + f'{weighted:.4}') + + +################################ +# NdBSpline tests +def bspline2(xy, t, c, k): + """A naive 2D tensort product spline evaluation.""" + x, y = xy + tx, ty = t + nx = len(tx) - k - 1 + assert (nx >= k+1) + ny = len(ty) - k - 1 + assert (ny >= k+1) + return sum(c[ix, iy] * B(x, k, ix, tx) * B(y, k, iy, ty) + for ix in range(nx) for iy in range(ny)) + + +def B(x, k, i, t): + if k == 0: + return 1.0 if t[i] <= x < t[i+1] else 0.0 + if t[i+k] == t[i]: + c1 = 0.0 + else: + c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t) + if t[i+k+1] == t[i+1]: + c2 = 0.0 + else: + c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t) + return c1 + c2 + + +def bspline(x, t, c, k): + n = len(t) - k - 1 + assert (n >= k+1) and (len(c) >= n) + return sum(c[i] * B(x, k, i, t) for i in range(n)) + + +class NdBSpline0: + def __init__(self, t, c, k=3): + """Tensor product spline object. + + c[i1, i2, ..., id] * B(x1, i1) * B(x2, i2) * ... * B(xd, id) + + Parameters + ---------- + c : ndarray, shape (n1, n2, ..., nd, ...) + b-spline coefficients + t : tuple of 1D ndarrays + knot vectors in directions 1, 2, ... d + ``len(t[i]) == n[i] + k + 1`` + k : int or length-d tuple of integers + spline degrees. + """ + ndim = len(t) + assert ndim <= len(c.shape) + + try: + len(k) + except TypeError: + # make k a tuple + k = (k,)*ndim + + self.k = tuple(operator.index(ki) for ki in k) + self.t = tuple(np.asarray(ti, dtype=float) for ti in t) + self.c = c + + def __call__(self, x): + ndim = len(self.t) + # a single evaluation point: `x` is a 1D array_like, shape (ndim,) + assert len(x) == ndim + + # get the indices in an ndim-dimensional vector + i = ['none', ]*ndim + for d in range(ndim): + td, xd = self.t[d], x[d] + k = self.k[d] + + # find the index for x[d] + if xd == td[k]: + i[d] = k + else: + i[d] = np.searchsorted(td, xd) - 1 + assert td[i[d]] <= xd <= td[i[d]+1] + assert i[d] >= k and i[d] < len(td) - k + i = tuple(i) + + # iterate over the dimensions, form linear combinations of + # products B(x_1) * B(x_2) * ... B(x_N) of (k+1)**N b-splines + # which are non-zero at `i = (i_1, i_2, ..., i_N)`. + result = 0 + iters = [range(i[d] - self.k[d], i[d] + 1) for d in range(ndim)] + for idx in itertools.product(*iters): + term = self.c[idx] * np.prod([B(x[d], self.k[d], idx[d], self.t[d]) + for d in range(ndim)]) + result += term + return result + + +class TestNdBSpline: + + def test_1D(self): + # test ndim=1 agrees with BSpline + rng = np.random.default_rng(12345) + n, k = 11, 3 + n_tr = 7 + t = np.sort(rng.uniform(size=n + k + 1)) + c = rng.uniform(size=(n, n_tr)) + + b = BSpline(t, c, k) + nb = NdBSpline((t,), c, k) + + xi = rng.uniform(size=21) + # NdBSpline expects xi.shape=(npts, ndim) + assert_allclose(nb(xi[:, None]), + b(xi), atol=1e-14) + assert nb(xi[:, None]).shape == (xi.shape[0], c.shape[1]) + + def make_2d_case(self): + # make a 2D separable spline + x = np.arange(6) + y = x**3 + spl = make_interp_spline(x, y, k=3) + + y_1 = x**3 + 2*x + spl_1 = make_interp_spline(x, y_1, k=3) + + t2 = (spl.t, spl_1.t) + c2 = spl.c[:, None] * spl_1.c[None, :] + + return t2, c2, 3 + + def make_2d_mixed(self): + # make a 2D separable spline w/ kx=3, ky=2 + x = np.arange(6) + y = x**3 + spl = make_interp_spline(x, y, k=3) + + x = np.arange(5) + 1.5 + y_1 = x**2 + 2*x + spl_1 = make_interp_spline(x, y_1, k=2) + + t2 = (spl.t, spl_1.t) + c2 = spl.c[:, None] * spl_1.c[None, :] + + return t2, c2, spl.k, spl_1.k + + def test_2D_separable(self): + xi = [(1.5, 2.5), (2.5, 1), (0.5, 1.5)] + t2, c2, k = self.make_2d_case() + target = [x**3 * (y**3 + 2*y) for (x, y) in xi] + + # sanity check: bspline2 gives the product as constructed + assert_allclose([bspline2(xy, t2, c2, k) for xy in xi], + target, + atol=1e-14) + + # check evaluation on a 2D array: the 1D array of 2D points + bspl2 = NdBSpline(t2, c2, k=3) + assert bspl2(xi).shape == (len(xi), ) + assert_allclose(bspl2(xi), + target, atol=1e-14) + + # now check on a multidim xi + rng = np.random.default_rng(12345) + xi = rng.uniform(size=(4, 3, 2)) * 5 + result = bspl2(xi) + assert result.shape == (4, 3) + + # also check the values + x, y = xi.reshape((-1, 2)).T + assert_allclose(result.ravel(), + x**3 * (y**3 + 2*y), atol=1e-14) + + def test_2D_separable_2(self): + # test `c` with trailing dimensions, i.e. c.ndim > ndim + ndim = 2 + xi = [(1.5, 2.5), (2.5, 1), (0.5, 1.5)] + target = [x**3 * (y**3 + 2*y) for (x, y) in xi] + + t2, c2, k = self.make_2d_case() + c2_4 = np.dstack((c2, c2, c2, c2)) # c22.shape = (6, 6, 4) + + xy = (1.5, 2.5) + bspl2_4 = NdBSpline(t2, c2_4, k=3) + result = bspl2_4(xy) + val_single = NdBSpline(t2, c2, k)(xy) + assert result.shape == (4,) + assert_allclose(result, + [val_single, ]*4, atol=1e-14) + + # now try the array xi : the output.shape is (3, 4) where 3 + # is the number of points in xi and 4 is the trailing dimension of c + assert bspl2_4(xi).shape == np.shape(xi)[:-1] + bspl2_4.c.shape[ndim:] + assert_allclose(bspl2_4(xi) - np.asarray(target)[:, None], + 0, atol=5e-14) + + # two trailing dimensions + c2_22 = c2_4.reshape((6, 6, 2, 2)) + bspl2_22 = NdBSpline(t2, c2_22, k=3) + + result = bspl2_22(xy) + assert result.shape == (2, 2) + assert_allclose(result, + [[val_single, val_single], + [val_single, val_single]], atol=1e-14) + + # now try the array xi : the output shape is (3, 2, 2) + # for 3 points in xi and c trailing dimensions being (2, 2) + assert (bspl2_22(xi).shape == + np.shape(xi)[:-1] + bspl2_22.c.shape[ndim:]) + assert_allclose(bspl2_22(xi) - np.asarray(target)[:, None, None], + 0, atol=5e-14) + + def test_2D_random(self): + rng = np.random.default_rng(12345) + k = 3 + tx = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=7)) * 3, 3, 3, 3, 3] + ty = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + c = rng.uniform(size=(tx.size-k-1, ty.size-k-1)) + + spl = NdBSpline((tx, ty), c, k=k) + + xi = (1., 1.) + assert_allclose(spl(xi), + bspline2(xi, (tx, ty), c, k), atol=1e-14) + + xi = np.c_[[1, 1.5, 2], + [1.1, 1.6, 2.1]] + assert_allclose(spl(xi), + [bspline2(xy, (tx, ty), c, k) for xy in xi], + atol=1e-14) + + def test_2D_mixed(self): + t2, c2, kx, ky = self.make_2d_mixed() + xi = [(1.4, 4.5), (2.5, 2.4), (4.5, 3.5)] + target = [x**3 * (y**2 + 2*y) for (x, y) in xi] + bspl2 = NdBSpline(t2, c2, k=(kx, ky)) + assert bspl2(xi).shape == (len(xi), ) + assert_allclose(bspl2(xi), + target, atol=1e-14) + + def test_2D_derivative(self): + t2, c2, kx, ky = self.make_2d_mixed() + xi = [(1.4, 4.5), (2.5, 2.4), (4.5, 3.5)] + bspl2 = NdBSpline(t2, c2, k=(kx, ky)) + + der = bspl2(xi, nu=(1, 0)) + assert_allclose(der, + [3*x**2 * (y**2 + 2*y) for x, y in xi], atol=1e-14) + + der = bspl2(xi, nu=(1, 1)) + assert_allclose(der, + [3*x**2 * (2*y + 2) for x, y in xi], atol=1e-14) + + der = bspl2(xi, nu=(0, 0)) + assert_allclose(der, + [x**3 * (y**2 + 2*y) for x, y in xi], atol=1e-14) + + with assert_raises(ValueError): + # all(nu >= 0) + der = bspl2(xi, nu=(-1, 0)) + + with assert_raises(ValueError): + # len(nu) == ndim + der = bspl2(xi, nu=(-1, 0, 1)) + + def test_2D_mixed_random(self): + rng = np.random.default_rng(12345) + kx, ky = 2, 3 + tx = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=7)) * 3, 3, 3, 3, 3] + ty = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + c = rng.uniform(size=(tx.size - kx - 1, ty.size - ky - 1)) + + xi = np.c_[[1, 1.5, 2], + [1.1, 1.6, 2.1]] + + bspl2 = NdBSpline((tx, ty), c, k=(kx, ky)) + bspl2_0 = NdBSpline0((tx, ty), c, k=(kx, ky)) + + assert_allclose(bspl2(xi), + [bspl2_0(xp) for xp in xi], atol=1e-14) + + def test_tx_neq_ty(self): + # 2D separable spline w/ len(tx) != len(ty) + x = np.arange(6) + y = np.arange(7) + 1.5 + + spl_x = make_interp_spline(x, x**3, k=3) + spl_y = make_interp_spline(y, y**2 + 2*y, k=3) + cc = spl_x.c[:, None] * spl_y.c[None, :] + bspl = NdBSpline((spl_x.t, spl_y.t), cc, (spl_x.k, spl_y.k)) + + values = (x**3)[:, None] * (y**2 + 2*y)[None, :] + rgi = RegularGridInterpolator((x, y), values) + + xi = [(a, b) for a, b in itertools.product(x, y)] + bxi = bspl(xi) + + assert not np.isnan(bxi).any() + assert_allclose(bxi, rgi(xi), atol=1e-14) + assert_allclose(bxi.reshape(values.shape), values, atol=1e-14) + + def make_3d_case(self): + # make a 3D separable spline + x = np.arange(6) + y = x**3 + spl = make_interp_spline(x, y, k=3) + + y_1 = x**3 + 2*x + spl_1 = make_interp_spline(x, y_1, k=3) + + y_2 = x**3 + 3*x + 1 + spl_2 = make_interp_spline(x, y_2, k=3) + + t2 = (spl.t, spl_1.t, spl_2.t) + c2 = (spl.c[:, None, None] * + spl_1.c[None, :, None] * + spl_2.c[None, None, :]) + + return t2, c2, 3 + + def test_3D_separable(self): + rng = np.random.default_rng(12345) + x, y, z = rng.uniform(size=(3, 11)) * 5 + target = x**3 * (y**3 + 2*y) * (z**3 + 3*z + 1) + + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3) + + xi = [_ for _ in zip(x, y, z)] + result = bspl3(xi) + assert result.shape == (11,) + assert_allclose(result, target, atol=1e-14) + + def test_3D_derivative(self): + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3) + rng = np.random.default_rng(12345) + x, y, z = rng.uniform(size=(3, 11)) * 5 + xi = [_ for _ in zip(x, y, z)] + + assert_allclose(bspl3(xi, nu=(1, 0, 0)), + 3*x**2 * (y**3 + 2*y) * (z**3 + 3*z + 1), atol=1e-14) + + assert_allclose(bspl3(xi, nu=(2, 0, 0)), + 6*x * (y**3 + 2*y) * (z**3 + 3*z + 1), atol=1e-14) + + assert_allclose(bspl3(xi, nu=(2, 1, 0)), + 6*x * (3*y**2 + 2) * (z**3 + 3*z + 1), atol=1e-14) + + assert_allclose(bspl3(xi, nu=(2, 1, 3)), + 6*x * (3*y**2 + 2) * (6), atol=1e-14) + + assert_allclose(bspl3(xi, nu=(2, 1, 4)), + np.zeros(len(xi)), atol=1e-14) + + def test_3D_random(self): + rng = np.random.default_rng(12345) + k = 3 + tx = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=7)) * 3, 3, 3, 3, 3] + ty = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + tz = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + c = rng.uniform(size=(tx.size-k-1, ty.size-k-1, tz.size-k-1)) + + spl = NdBSpline((tx, ty, tz), c, k=k) + spl_0 = NdBSpline0((tx, ty, tz), c, k=k) + + xi = (1., 1., 1) + assert_allclose(spl(xi), spl_0(xi), atol=1e-14) + + xi = np.c_[[1, 1.5, 2], + [1.1, 1.6, 2.1], + [0.9, 1.4, 1.9]] + assert_allclose(spl(xi), [spl_0(xp) for xp in xi], atol=1e-14) + + def test_3D_random_complex(self): + rng = np.random.default_rng(12345) + k = 3 + tx = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=7)) * 3, 3, 3, 3, 3] + ty = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + tz = np.r_[0, 0, 0, 0, np.sort(rng.uniform(size=8)) * 4, 4, 4, 4, 4] + c = (rng.uniform(size=(tx.size-k-1, ty.size-k-1, tz.size-k-1)) + + rng.uniform(size=(tx.size-k-1, ty.size-k-1, tz.size-k-1))*1j) + + spl = NdBSpline((tx, ty, tz), c, k=k) + spl_re = NdBSpline((tx, ty, tz), c.real, k=k) + spl_im = NdBSpline((tx, ty, tz), c.imag, k=k) + + xi = np.c_[[1, 1.5, 2], + [1.1, 1.6, 2.1], + [0.9, 1.4, 1.9]] + assert_allclose(spl(xi), + spl_re(xi) + 1j*spl_im(xi), atol=1e-14) + + @pytest.mark.parametrize('cls_extrap', [None, True]) + @pytest.mark.parametrize('call_extrap', [None, True]) + def test_extrapolate_3D_separable(self, cls_extrap, call_extrap): + # test that extrapolate=True does extrapolate + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3, extrapolate=cls_extrap) + + # evaluate out of bounds + x, y, z = [-2, -1, 7], [-3, -0.5, 6.5], [-1, -1.5, 7.5] + x, y, z = map(np.asarray, (x, y, z)) + xi = [_ for _ in zip(x, y, z)] + target = x**3 * (y**3 + 2*y) * (z**3 + 3*z + 1) + + result = bspl3(xi, extrapolate=call_extrap) + assert_allclose(result, target, atol=1e-14) + + @pytest.mark.parametrize('extrap', [(False, True), (True, None)]) + def test_extrapolate_3D_separable_2(self, extrap): + # test that call(..., extrapolate=None) defers to self.extrapolate, + # otherwise supersedes self.extrapolate + t3, c3, k = self.make_3d_case() + cls_extrap, call_extrap = extrap + bspl3 = NdBSpline(t3, c3, k=3, extrapolate=cls_extrap) + + # evaluate out of bounds + x, y, z = [-2, -1, 7], [-3, -0.5, 6.5], [-1, -1.5, 7.5] + x, y, z = map(np.asarray, (x, y, z)) + xi = [_ for _ in zip(x, y, z)] + target = x**3 * (y**3 + 2*y) * (z**3 + 3*z + 1) + + result = bspl3(xi, extrapolate=call_extrap) + assert_allclose(result, target, atol=1e-14) + + def test_extrapolate_false_3D_separable(self): + # test that extrapolate=False produces nans for out-of-bounds values + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3) + + # evaluate out of bounds and inside + x, y, z = [-2, 1, 7], [-3, 0.5, 6.5], [-1, 1.5, 7.5] + x, y, z = map(np.asarray, (x, y, z)) + xi = [_ for _ in zip(x, y, z)] + target = x**3 * (y**3 + 2*y) * (z**3 + 3*z + 1) + + result = bspl3(xi, extrapolate=False) + assert np.isnan(result[0]) + assert np.isnan(result[-1]) + assert_allclose(result[1:-1], target[1:-1], atol=1e-14) + + def test_x_nan_3D(self): + # test that spline(nan) is nan + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3) + + # evaluate out of bounds and inside + x = np.asarray([-2, 3, np.nan, 1, 2, 7, np.nan]) + y = np.asarray([-3, 3.5, 1, np.nan, 3, 6.5, 6.5]) + z = np.asarray([-1, 3.5, 2, 3, np.nan, 7.5, 7.5]) + xi = [_ for _ in zip(x, y, z)] + target = x**3 * (y**3 + 2*y) * (z**3 + 3*z + 1) + mask = np.isnan(x) | np.isnan(y) | np.isnan(z) + target[mask] = np.nan + + result = bspl3(xi) + assert np.isnan(result[mask]).all() + assert_allclose(result, target, atol=1e-14) + + def test_non_c_contiguous(self): + # check that non C-contiguous inputs are OK + rng = np.random.default_rng(12345) + kx, ky = 3, 3 + tx = np.sort(rng.uniform(low=0, high=4, size=16)) + tx = np.r_[(tx[0],)*kx, tx, (tx[-1],)*kx] + ty = np.sort(rng.uniform(low=0, high=4, size=16)) + ty = np.r_[(ty[0],)*ky, ty, (ty[-1],)*ky] + + assert not tx[::2].flags.c_contiguous + assert not ty[::2].flags.c_contiguous + + c = rng.uniform(size=(tx.size//2 - kx - 1, ty.size//2 - ky - 1)) + c = c.T + assert not c.flags.c_contiguous + + xi = np.c_[[1, 1.5, 2], + [1.1, 1.6, 2.1]] + + bspl2 = NdBSpline((tx[::2], ty[::2]), c, k=(kx, ky)) + bspl2_0 = NdBSpline0((tx[::2], ty[::2]), c, k=(kx, ky)) + + assert_allclose(bspl2(xi), + [bspl2_0(xp) for xp in xi], atol=1e-14) + + def test_readonly(self): + t3, c3, k = self.make_3d_case() + bspl3 = NdBSpline(t3, c3, k=3) + + for i in range(3): + t3[i].flags.writeable = False + c3.flags.writeable = False + + bspl3_ = NdBSpline(t3, c3, k=3) + + assert bspl3((1, 2, 3)) == bspl3_((1, 2, 3)) + + def test_design_matrix(self): + t3, c3, k = self.make_3d_case() + + xi = np.asarray([[1, 2, 3], [4, 5, 6]]) + dm = NdBSpline(t3, c3, k).design_matrix(xi, t3, k) + dm1 = NdBSpline.design_matrix(xi, t3, [k, k, k]) + assert dm.shape[0] == xi.shape[0] + assert_allclose(dm.todense(), dm1.todense(), atol=1e-16) + + with assert_raises(ValueError): + NdBSpline.design_matrix([1, 2, 3], t3, [k]*3) + + with assert_raises(ValueError, match="Data and knots*"): + NdBSpline.design_matrix([[1, 2]], t3, [k]*3) + + +class TestMakeND: + def test_2D_separable_simple(self): + x = np.arange(6) + y = np.arange(6) + 0.5 + values = x[:, None]**3 * (y**3 + 2*y)[None, :] + xi = [(a, b) for a, b in itertools.product(x, y)] + + bspl = make_ndbspl((x, y), values, k=1) + assert_allclose(bspl(xi), values.ravel(), atol=1e-15) + + # test the coefficients vs outer product of 1D coefficients + spl_x = make_interp_spline(x, x**3, k=1) + spl_y = make_interp_spline(y, y**3 + 2*y, k=1) + cc = spl_x.c[:, None] * spl_y.c[None, :] + assert_allclose(cc, bspl.c, atol=1e-11, rtol=0) + + # test against RGI + from scipy.interpolate import RegularGridInterpolator as RGI + rgi = RGI((x, y), values, method='linear') + assert_allclose(rgi(xi), bspl(xi), atol=1e-14) + + def test_2D_separable_trailing_dims(self): + # test `c` with trailing dimensions, i.e. c.ndim > ndim + x = np.arange(6) + y = np.arange(6) + xi = [(a, b) for a, b in itertools.product(x, y)] + + # make values4.shape = (6, 6, 4) + values = x[:, None]**3 * (y**3 + 2*y)[None, :] + values4 = np.dstack((values, values, values, values)) + bspl = make_ndbspl((x, y), values4, k=3, solver=ssl.spsolve) + + result = bspl(xi) + target = np.dstack((values, values, values, values)) + assert result.shape == (36, 4) + assert_allclose(result.reshape(6, 6, 4), + target, atol=1e-14) + + # now two trailing dimensions + values22 = values4.reshape((6, 6, 2, 2)) + bspl = make_ndbspl((x, y), values22, k=3, solver=ssl.spsolve) + + result = bspl(xi) + assert result.shape == (36, 2, 2) + assert_allclose(result.reshape(6, 6, 2, 2), + target.reshape((6, 6, 2, 2)), atol=1e-14) + + @pytest.mark.parametrize('k', [(3, 3), (1, 1), (3, 1), (1, 3), (3, 5)]) + def test_2D_mixed(self, k): + # make a 2D separable spline w/ len(tx) != len(ty) + x = np.arange(6) + y = np.arange(7) + 1.5 + xi = [(a, b) for a, b in itertools.product(x, y)] + + values = (x**3)[:, None] * (y**2 + 2*y)[None, :] + bspl = make_ndbspl((x, y), values, k=k, solver=ssl.spsolve) + assert_allclose(bspl(xi), values.ravel(), atol=1e-15) + + def _get_sample_2d_data(self): + # from test_rgi.py::TestIntepN + x = np.array([.5, 2., 3., 4., 5.5, 6.]) + y = np.array([.5, 2., 3., 4., 5.5, 6.]) + z = np.array( + [ + [1, 2, 1, 2, 1, 1], + [1, 2, 1, 2, 1, 1], + [1, 2, 3, 2, 1, 1], + [1, 2, 2, 2, 1, 1], + [1, 2, 1, 2, 1, 1], + [1, 2, 2, 2, 1, 1], + ] + ) + return x, y, z + + def test_2D_vs_RGI_linear(self): + x, y, z = self._get_sample_2d_data() + bspl = make_ndbspl((x, y), z, k=1) + rgi = RegularGridInterpolator((x, y), z, method='linear') + + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + + assert_allclose(bspl(xi), rgi(xi), atol=1e-14) + + def test_2D_vs_RGI_cubic(self): + x, y, z = self._get_sample_2d_data() + bspl = make_ndbspl((x, y), z, k=3, solver=ssl.spsolve) + rgi = RegularGridInterpolator((x, y), z, method='cubic_legacy') + + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + + assert_allclose(bspl(xi), rgi(xi), atol=1e-14) + + @pytest.mark.parametrize('solver', [ssl.gmres, ssl.gcrotmk]) + def test_2D_vs_RGI_cubic_iterative(self, solver): + # same as `test_2D_vs_RGI_cubic`, only with an iterative solver. + # Note the need to add an explicit `rtol` solver_arg to achieve the + # target accuracy of 1e-14. (the relation between solver atol/rtol + # and the accuracy of the final result is not direct and needs experimenting) + x, y, z = self._get_sample_2d_data() + bspl = make_ndbspl((x, y), z, k=3, solver=solver, rtol=1e-6) + rgi = RegularGridInterpolator((x, y), z, method='cubic_legacy') + + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + + assert_allclose(bspl(xi), rgi(xi), atol=1e-14) + + def test_2D_vs_RGI_quintic(self): + x, y, z = self._get_sample_2d_data() + bspl = make_ndbspl((x, y), z, k=5, solver=ssl.spsolve) + rgi = RegularGridInterpolator((x, y), z, method='quintic_legacy') + + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + + assert_allclose(bspl(xi), rgi(xi), atol=1e-14) + + @pytest.mark.parametrize( + 'k, meth', [(1, 'linear'), (3, 'cubic_legacy'), (5, 'quintic_legacy')] + ) + def test_3D_random_vs_RGI(self, k, meth): + rndm = np.random.default_rng(123456) + x = np.cumsum(rndm.uniform(size=6)) + y = np.cumsum(rndm.uniform(size=7)) + z = np.cumsum(rndm.uniform(size=8)) + values = rndm.uniform(size=(6, 7, 8)) + + bspl = make_ndbspl((x, y, z), values, k=k, solver=ssl.spsolve) + rgi = RegularGridInterpolator((x, y, z), values, method=meth) + + xi = np.random.uniform(low=0.7, high=2.1, size=(11, 3)) + assert_allclose(bspl(xi), rgi(xi), atol=1e-14) + + def test_solver_err_not_converged(self): + x, y, z = self._get_sample_2d_data() + solver_args = {'maxiter': 1} + with assert_raises(ValueError, match='solver'): + make_ndbspl((x, y), z, k=3, **solver_args) + + with assert_raises(ValueError, match='solver'): + make_ndbspl((x, y), np.dstack((z, z)), k=3, **solver_args) + + +class TestFpchec: + # https://github.com/scipy/scipy/blob/main/scipy/interpolate/fitpack/fpchec.f + + def test_1D_x_t(self): + k = 1 + t = np.arange(12).reshape(2, 6) + x = np.arange(12) + + with pytest.raises(ValueError, match="1D sequence"): + _b.fpcheck(x, t, k) + + with pytest.raises(ValueError, match="1D sequence"): + _b.fpcheck(t, x, k) + + def test_condition_1(self): + # c 1) k+1 <= n-k-1 <= m + k = 3 + n = 2*(k + 1) - 1 # not OK + m = n + 11 # OK + t = np.arange(n) + x = np.arange(m) + + assert dfitpack.fpchec(x, t, k) == 10 + with pytest.raises(ValueError, match="Need k+1*"): + _b.fpcheck(x, t, k) + + n = 2*(k+1) + 1 # OK + m = n - k - 2 # not OK + t = np.arange(n) + x = np.arange(m) + + assert dfitpack.fpchec(x, t, k) == 10 + with pytest.raises(ValueError, match="Need k+1*"): + _b.fpcheck(x, t, k) + + def test_condition_2(self): + # c 2) t(1) <= t(2) <= ... <= t(k+1) + # c t(n-k) <= t(n-k+1) <= ... <= t(n) + k = 3 + t = [0]*(k+1) + [2] + [5]*(k+1) # this is OK + x = [1, 2, 3, 4, 4.5] + + assert dfitpack.fpchec(x, t, k) == 0 + assert _b.fpcheck(x, t, k) is None # does not raise + + tt = t.copy() + tt[-1] = tt[0] # not OK + assert dfitpack.fpchec(x, tt, k) == 20 + with pytest.raises(ValueError, match="Last k knots*"): + _b.fpcheck(x, tt, k) + + tt = t.copy() + tt[0] = tt[-1] # not OK + assert dfitpack.fpchec(x, tt, k) == 20 + with pytest.raises(ValueError, match="First k knots*"): + _b.fpcheck(x, tt, k) + + def test_condition_3(self): + # c 3) t(k+1) < t(k+2) < ... < t(n-k) + k = 3 + t = [0]*(k+1) + [2, 3] + [5]*(k+1) # this is OK + x = [1, 2, 3, 3.5, 4, 4.5] + assert dfitpack.fpchec(x, t, k) == 0 + assert _b.fpcheck(x, t, k) is None + + t = [0]*(k+1) + [2, 2] + [5]*(k+1) # this is not OK + assert dfitpack.fpchec(x, t, k) == 30 + with pytest.raises(ValueError, match="Internal knots*"): + _b.fpcheck(x, t, k) + + def test_condition_4(self): + # c 4) t(k+1) <= x(i) <= t(n-k) + # NB: FITPACK's fpchec only checks x[0] & x[-1], so we follow. + k = 3 + t = [0]*(k+1) + [5]*(k+1) + x = [1, 2, 3, 3.5, 4, 4.5] # this is OK + assert dfitpack.fpchec(x, t, k) == 0 + assert _b.fpcheck(x, t, k) is None + + xx = x.copy() + xx[0] = t[0] # still OK + assert dfitpack.fpchec(xx, t, k) == 0 + assert _b.fpcheck(x, t, k) is None + + xx = x.copy() + xx[0] = t[0] - 1 # not OK + assert dfitpack.fpchec(xx, t, k) == 40 + with pytest.raises(ValueError, match="Out of bounds*"): + _b.fpcheck(xx, t, k) + + xx = x.copy() + xx[-1] = t[-1] + 1 # not OK + assert dfitpack.fpchec(xx, t, k) == 40 + with pytest.raises(ValueError, match="Out of bounds*"): + _b.fpcheck(xx, t, k) + + # ### Test the S-W condition (no 5) + # c 5) the conditions specified by schoenberg and whitney must hold + # c for at least one subset of data points, i.e. there must be a + # c subset of data points y(j) such that + # c t(j) < y(j) < t(j+k+1), j=1,2,...,n-k-1 + def test_condition_5_x1xm(self): + # x(1).ge.t(k2) .or. x(m).le.t(nk1) + k = 1 + t = [0, 0, 1, 2, 2] + x = [1.1, 1.1, 1.1] + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + + x = [0.5, 0.5, 0.5] + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + + def test_condition_5_k1(self): + # special case nk3 (== n - k - 2) < 2 + k = 1 + t = [0, 0, 1, 1] + x = [0.5, 0.6] + assert dfitpack.fpchec(x, t, k) == 0 + assert _b.fpcheck(x, t, k) is None + + def test_condition_5_1(self): + # basically, there can't be an interval of t[j]..t[j+k+1] with no x + k = 3 + t = [0]*(k+1) + [2] + [5]*(k+1) + x = [3]*5 + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + + t = [0]*(k+1) + [2] + [5]*(k+1) + x = [1]*5 + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + + def test_condition_5_2(self): + # same as _5_1, only the empty interval is in the middle + k = 3 + t = [0]*(k+1) + [2, 3] + [5]*(k+1) + x = [1.1]*5 + [4] + + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + + # and this one is OK + x = [1.1]*4 + [4, 4] + assert dfitpack.fpchec(x, t, k) == 0 + assert _b.fpcheck(x, t, k) is None + + def test_condition_5_3(self): + # similar to _5_2, covers a different failure branch + k = 1 + t = [0, 0, 2, 3, 4, 5, 6, 7, 7] + x = [1, 1, 1, 5.2, 5.2, 5.2, 6.5] + + assert dfitpack.fpchec(x, t, k) == 50 + with pytest.raises(ValueError, match="Schoenberg-Whitney*"): + _b.fpcheck(x, t, k) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack.py new file mode 100644 index 0000000000000000000000000000000000000000..c76178681de063e21fda5afedd4759248e8e19bb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack.py @@ -0,0 +1,503 @@ +import itertools +import os + +import numpy as np +from numpy.testing import (assert_equal, assert_allclose, assert_, + assert_almost_equal, assert_array_almost_equal) +from pytest import raises as assert_raises +import pytest +from scipy._lib._testutils import check_free_memory + +from scipy.interpolate import RectBivariateSpline + +from scipy.interpolate._fitpack_py import (splrep, splev, bisplrep, bisplev, + sproot, splprep, splint, spalde, splder, splantider, insert, dblint) +from scipy.interpolate.dfitpack import regrid_smth +from scipy.interpolate._fitpack2 import dfitpack_int + + +def data_file(basename): + return os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'data', basename) + + +def norm2(x): + return np.sqrt(np.dot(x.T, x)) + + +def f1(x, d=0): + """Derivatives of sin->cos->-sin->-cos.""" + if d % 4 == 0: + return np.sin(x) + if d % 4 == 1: + return np.cos(x) + if d % 4 == 2: + return -np.sin(x) + if d % 4 == 3: + return -np.cos(x) + + +def makepairs(x, y): + """Helper function to create an array of pairs of x and y.""" + xy = np.array(list(itertools.product(np.asarray(x), np.asarray(y)))) + return xy.T + + +class TestSmokeTests: + """ + Smoke tests (with a few asserts) for fitpack routines -- mostly + check that they are runnable + """ + def check_1(self, per=0, s=0, a=0, b=2*np.pi, at_nodes=False, + xb=None, xe=None): + if xb is None: + xb = a + if xe is None: + xe = b + + N = 20 + # nodes and middle points of the nodes + x = np.linspace(a, b, N + 1) + x1 = a + (b - a) * np.arange(1, N, dtype=float) / float(N - 1) + v = f1(x) + + def err_est(k, d): + # Assume f has all derivatives < 1 + h = 1.0 / N + tol = 5 * h**(.75*(k-d)) + if s > 0: + tol += 1e5*s + return tol + + for k in range(1, 6): + tck = splrep(x, v, s=s, per=per, k=k, xe=xe) + tt = tck[0][k:-k] if at_nodes else x1 + + for d in range(k+1): + tol = err_est(k, d) + err = norm2(f1(tt, d) - splev(tt, tck, d)) / norm2(f1(tt, d)) + assert err < tol + + def check_2(self, per=0, N=20, ia=0, ib=2*np.pi): + a, b, dx = 0, 2*np.pi, 0.2*np.pi + x = np.linspace(a, b, N+1) # nodes + v = np.sin(x) + + def err_est(k, d): + # Assume f has all derivatives < 1 + h = 1.0 / N + tol = 5 * h**(.75*(k-d)) + return tol + + nk = [] + for k in range(1, 6): + tck = splrep(x, v, s=0, per=per, k=k, xe=b) + nk.append([splint(ia, ib, tck), spalde(dx, tck)]) + + k = 1 + for r in nk: + d = 0 + for dr in r[1]: + tol = err_est(k, d) + assert_allclose(dr, f1(dx, d), atol=0, rtol=tol) + d = d+1 + k = k+1 + + def test_smoke_splrep_splev(self): + self.check_1(s=1e-6) + self.check_1(b=1.5*np.pi) + self.check_1(b=1.5*np.pi, xe=2*np.pi, per=1, s=1e-1) + + @pytest.mark.parametrize('per', [0, 1]) + @pytest.mark.parametrize('at_nodes', [True, False]) + def test_smoke_splrep_splev_2(self, per, at_nodes): + self.check_1(per=per, at_nodes=at_nodes) + + @pytest.mark.parametrize('N', [20, 50]) + @pytest.mark.parametrize('per', [0, 1]) + def test_smoke_splint_spalde(self, N, per): + self.check_2(per=per, N=N) + + @pytest.mark.parametrize('N', [20, 50]) + @pytest.mark.parametrize('per', [0, 1]) + def test_smoke_splint_spalde_iaib(self, N, per): + self.check_2(ia=0.2*np.pi, ib=np.pi, N=N, per=per) + + def test_smoke_sproot(self): + # sproot is only implemented for k=3 + a, b = 0.1, 15 + x = np.linspace(a, b, 20) + v = np.sin(x) + + for k in [1, 2, 4, 5]: + tck = splrep(x, v, s=0, per=0, k=k, xe=b) + with assert_raises(ValueError): + sproot(tck) + + k = 3 + tck = splrep(x, v, s=0, k=3) + roots = sproot(tck) + assert_allclose(splev(roots, tck), 0, atol=1e-10, rtol=1e-10) + assert_allclose(roots, np.pi * np.array([1, 2, 3, 4]), rtol=1e-3) + + @pytest.mark.parametrize('N', [20, 50]) + @pytest.mark.parametrize('k', [1, 2, 3, 4, 5]) + def test_smoke_splprep_splrep_splev(self, N, k): + a, b, dx = 0, 2.*np.pi, 0.2*np.pi + x = np.linspace(a, b, N+1) # nodes + v = np.sin(x) + + tckp, u = splprep([x, v], s=0, per=0, k=k, nest=-1) + uv = splev(dx, tckp) + err1 = abs(uv[1] - np.sin(uv[0])) + assert err1 < 1e-2 + + tck = splrep(x, v, s=0, per=0, k=k) + err2 = abs(splev(uv[0], tck) - np.sin(uv[0])) + assert err2 < 1e-2 + + # Derivatives of parametric cubic spline at u (first function) + if k == 3: + tckp, u = splprep([x, v], s=0, per=0, k=k, nest=-1) + for d in range(1, k+1): + uv = splev(dx, tckp, d) + + def test_smoke_bisplrep_bisplev(self): + xb, xe = 0, 2.*np.pi + yb, ye = 0, 2.*np.pi + kx, ky = 3, 3 + Nx, Ny = 20, 20 + + def f2(x, y): + return np.sin(x+y) + + x = np.linspace(xb, xe, Nx + 1) + y = np.linspace(yb, ye, Ny + 1) + xy = makepairs(x, y) + tck = bisplrep(xy[0], xy[1], f2(xy[0], xy[1]), s=0, kx=kx, ky=ky) + + tt = [tck[0][kx:-kx], tck[1][ky:-ky]] + t2 = makepairs(tt[0], tt[1]) + v1 = bisplev(tt[0], tt[1], tck) + v2 = f2(t2[0], t2[1]) + v2.shape = len(tt[0]), len(tt[1]) + + assert norm2(np.ravel(v1 - v2)) < 1e-2 + + +class TestSplev: + def test_1d_shape(self): + x = [1,2,3,4,5] + y = [4,5,6,7,8] + tck = splrep(x, y) + z = splev([1], tck) + assert_equal(z.shape, (1,)) + z = splev(1, tck) + assert_equal(z.shape, ()) + + def test_2d_shape(self): + x = [1, 2, 3, 4, 5] + y = [4, 5, 6, 7, 8] + tck = splrep(x, y) + t = np.array([[1.0, 1.5, 2.0, 2.5], + [3.0, 3.5, 4.0, 4.5]]) + z = splev(t, tck) + z0 = splev(t[0], tck) + z1 = splev(t[1], tck) + assert_equal(z, np.vstack((z0, z1))) + + def test_extrapolation_modes(self): + # test extrapolation modes + # * if ext=0, return the extrapolated value. + # * if ext=1, return 0 + # * if ext=2, raise a ValueError + # * if ext=3, return the boundary value. + x = [1,2,3] + y = [0,2,4] + tck = splrep(x, y, k=1) + + rstl = [[-2, 6], [0, 0], None, [0, 4]] + for ext in (0, 1, 3): + assert_array_almost_equal(splev([0, 4], tck, ext=ext), rstl[ext]) + + assert_raises(ValueError, splev, [0, 4], tck, ext=2) + + +class TestSplder: + def setup_method(self): + # non-uniform grid, just to make it sure + x = np.linspace(0, 1, 100)**3 + y = np.sin(20 * x) + self.spl = splrep(x, y) + + # double check that knots are non-uniform + assert_(np.ptp(np.diff(self.spl[0])) > 0) + + def test_inverse(self): + # Check that antiderivative + derivative is identity. + for n in range(5): + spl2 = splantider(self.spl, n) + spl3 = splder(spl2, n) + assert_allclose(self.spl[0], spl3[0]) + assert_allclose(self.spl[1], spl3[1]) + assert_equal(self.spl[2], spl3[2]) + + def test_splder_vs_splev(self): + # Check derivative vs. FITPACK + + for n in range(3+1): + # Also extrapolation! + xx = np.linspace(-1, 2, 2000) + if n == 3: + # ... except that FITPACK extrapolates strangely for + # order 0, so let's not check that. + xx = xx[(xx >= 0) & (xx <= 1)] + + dy = splev(xx, self.spl, n) + spl2 = splder(self.spl, n) + dy2 = splev(xx, spl2) + if n == 1: + assert_allclose(dy, dy2, rtol=2e-6) + else: + assert_allclose(dy, dy2) + + def test_splantider_vs_splint(self): + # Check antiderivative vs. FITPACK + spl2 = splantider(self.spl) + + # no extrapolation, splint assumes function is zero outside + # range + xx = np.linspace(0, 1, 20) + + for x1 in xx: + for x2 in xx: + y1 = splint(x1, x2, self.spl) + y2 = splev(x2, spl2) - splev(x1, spl2) + assert_allclose(y1, y2) + + def test_order0_diff(self): + assert_raises(ValueError, splder, self.spl, 4) + + def test_kink(self): + # Should refuse to differentiate splines with kinks + + spl2 = insert(0.5, self.spl, m=2) + splder(spl2, 2) # Should work + assert_raises(ValueError, splder, spl2, 3) + + spl2 = insert(0.5, self.spl, m=3) + splder(spl2, 1) # Should work + assert_raises(ValueError, splder, spl2, 2) + + spl2 = insert(0.5, self.spl, m=4) + assert_raises(ValueError, splder, spl2, 1) + + def test_multidim(self): + # c can have trailing dims + for n in range(3): + t, c, k = self.spl + c2 = np.c_[c, c, c] + c2 = np.dstack((c2, c2)) + + spl2 = splantider((t, c2, k), n) + spl3 = splder(spl2, n) + + assert_allclose(t, spl3[0]) + assert_allclose(c2, spl3[1]) + assert_equal(k, spl3[2]) + + +class TestSplint: + def test_len_c(self): + n, k = 7, 3 + x = np.arange(n) + y = x**3 + t, c, k = splrep(x, y, s=0) + + # note that len(c) == len(t) == 11 (== len(x) + 2*(k-1)) + assert len(t) == len(c) == n + 2*(k-1) + + # integrate directly: $\int_0^6 x^3 dx = 6^4 / 4$ + res = splint(0, 6, (t, c, k)) + assert_allclose(res, 6**4 / 4, atol=1e-15) + + # check that the coefficients past len(t) - k - 1 are ignored + c0 = c.copy() + c0[len(t)-k-1:] = np.nan + res0 = splint(0, 6, (t, c0, k)) + assert_allclose(res0, 6**4 / 4, atol=1e-15) + + # however, all other coefficients *are* used + c0[6] = np.nan + assert np.isnan(splint(0, 6, (t, c0, k))) + + # check that the coefficient array can have length `len(t) - k - 1` + c1 = c[:len(t) - k - 1] + res1 = splint(0, 6, (t, c1, k)) + assert_allclose(res1, 6**4 / 4, atol=1e-15) + + # however shorter c arrays raise. The error from f2py is a + # `dftipack.error`, which is an Exception but not ValueError etc. + with assert_raises(Exception, match=r">=n-k-1"): + splint(0, 1, (np.ones(10), np.ones(5), 3)) + + +class TestBisplrep: + def test_overflow(self): + from numpy.lib.stride_tricks import as_strided + if dfitpack_int.itemsize == 8: + size = 1500000**2 + else: + size = 400**2 + # Don't allocate a real array, as it's very big, but rely + # on that it's not referenced + x = as_strided(np.zeros(()), shape=(size,)) + assert_raises(OverflowError, bisplrep, x, x, x, w=x, + xb=0, xe=1, yb=0, ye=1, s=0) + + def test_regression_1310(self): + # Regression test for gh-1310 + with np.load(data_file('bug-1310.npz')) as loaded_data: + data = loaded_data['data'] + + # Shouldn't crash -- the input data triggers work array sizes + # that caused previously some data to not be aligned on + # sizeof(double) boundaries in memory, which made the Fortran + # code to crash when compiled with -O3 + bisplrep(data[:,0], data[:,1], data[:,2], kx=3, ky=3, s=0, + full_output=True) + + @pytest.mark.skipif(dfitpack_int != np.int64, reason="needs ilp64 fitpack") + def test_ilp64_bisplrep(self): + check_free_memory(28000) # VM size, doesn't actually use the pages + x = np.linspace(0, 1, 400) + y = np.linspace(0, 1, 400) + x, y = np.meshgrid(x, y) + z = np.zeros_like(x) + tck = bisplrep(x, y, z, kx=3, ky=3, s=0) + assert_allclose(bisplev(0.5, 0.5, tck), 0.0) + + +def test_dblint(): + # Basic test to see it runs and gives the correct result on a trivial + # problem. Note that `dblint` is not exposed in the interpolate namespace. + x = np.linspace(0, 1) + y = np.linspace(0, 1) + xx, yy = np.meshgrid(x, y) + rect = RectBivariateSpline(x, y, 4 * xx * yy) + tck = list(rect.tck) + tck.extend(rect.degrees) + + assert_almost_equal(dblint(0, 1, 0, 1, tck), 1) + assert_almost_equal(dblint(0, 0.5, 0, 1, tck), 0.25) + assert_almost_equal(dblint(0.5, 1, 0, 1, tck), 0.75) + assert_almost_equal(dblint(-100, 100, -100, 100, tck), 1) + + +def test_splev_der_k(): + # regression test for gh-2188: splev(x, tck, der=k) gives garbage or crashes + # for x outside of knot range + + # test case from gh-2188 + tck = (np.array([0., 0., 2.5, 2.5]), + np.array([-1.56679978, 2.43995873, 0., 0.]), + 1) + t, c, k = tck + x = np.array([-3, 0, 2.5, 3]) + + # an explicit form of the linear spline + assert_allclose(splev(x, tck), c[0] + (c[1] - c[0]) * x/t[2]) + assert_allclose(splev(x, tck, 1), (c[1]-c[0]) / t[2]) + + # now check a random spline vs splder + np.random.seed(1234) + x = np.sort(np.random.random(30)) + y = np.random.random(30) + t, c, k = splrep(x, y) + + x = [t[0] - 1., t[-1] + 1.] + tck2 = splder((t, c, k), k) + assert_allclose(splev(x, (t, c, k), k), splev(x, tck2)) + + +def test_splprep_segfault(): + # regression test for gh-3847: splprep segfaults if knots are specified + # for task=-1 + t = np.arange(0, 1.1, 0.1) + x = np.sin(2*np.pi*t) + y = np.cos(2*np.pi*t) + tck, u = splprep([x, y], s=0) + np.arange(0, 1.01, 0.01) + + uknots = tck[0] # using the knots from the previous fitting + tck, u = splprep([x, y], task=-1, t=uknots) # here is the crash + + +def test_bisplev_integer_overflow(): + np.random.seed(1) + + x = np.linspace(0, 1, 11) + y = x + z = np.random.randn(11, 11).ravel() + kx = 1 + ky = 1 + + nx, tx, ny, ty, c, fp, ier = regrid_smth( + x, y, z, None, None, None, None, kx=kx, ky=ky, s=0.0) + tck = (tx[:nx], ty[:ny], c[:(nx - kx - 1) * (ny - ky - 1)], kx, ky) + + xp = np.zeros([2621440]) + yp = np.zeros([2621440]) + + assert_raises((RuntimeError, MemoryError), bisplev, xp, yp, tck) + + +@pytest.mark.xslow +def test_gh_1766(): + # this should fail gracefully instead of segfaulting (int overflow) + size = 22 + kx, ky = 3, 3 + def f2(x, y): + return np.sin(x+y) + + x = np.linspace(0, 10, size) + y = np.linspace(50, 700, size) + xy = makepairs(x, y) + tck = bisplrep(xy[0], xy[1], f2(xy[0], xy[1]), s=0, kx=kx, ky=ky) + # the size value here can either segfault + # or produce a MemoryError on main + tx_ty_size = 500000 + tck[0] = np.arange(tx_ty_size) + tck[1] = np.arange(tx_ty_size) * 4 + tt_0 = np.arange(50) + tt_1 = np.arange(50) * 3 + with pytest.raises(MemoryError): + bisplev(tt_0, tt_1, tck, 1, 1) + + +def test_spalde_scalar_input(): + # Ticket #629 + x = np.linspace(0, 10) + y = x**3 + tck = splrep(x, y, k=3, t=[5]) + res = spalde(np.float64(1), tck) + des = np.array([1., 3., 6., 6.]) + assert_almost_equal(res, des) + + +def test_spalde_nc(): + # regression test for https://github.com/scipy/scipy/issues/19002 + # here len(t) = 29 and len(c) = 25 (== len(t) - k - 1) + x = np.asarray([-10., -9., -8., -7., -6., -5., -4., -3., -2.5, -2., -1.5, + -1., -0.5, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 5., 6.], + dtype="float") + t = [-10.0, -10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, + -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, + 5.0, 6.0, 6.0, 6.0, 6.0] + c = np.asarray([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + k = 3 + + res = spalde(x, (t, c, k)) + res_splev = np.asarray([splev(x, (t, c, k), nu) for nu in range(4)]) + assert_allclose(res, res_splev.T, atol=1e-15) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack2.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack2.py new file mode 100644 index 0000000000000000000000000000000000000000..f1ebf0d26e8795c5230b4336a02126a8e1e53096 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_fitpack2.py @@ -0,0 +1,1355 @@ +# Created by Pearu Peterson, June 2003 +import itertools +import numpy as np +from numpy.testing import (assert_equal, assert_almost_equal, assert_array_equal, + assert_array_almost_equal, assert_allclose, suppress_warnings) +from pytest import raises as assert_raises + +from numpy import array, diff, linspace, meshgrid, ones, pi, shape +from scipy.interpolate._fitpack_py import bisplrep, bisplev, splrep, spalde +from scipy.interpolate._fitpack2 import (UnivariateSpline, + LSQUnivariateSpline, InterpolatedUnivariateSpline, + LSQBivariateSpline, SmoothBivariateSpline, RectBivariateSpline, + LSQSphereBivariateSpline, SmoothSphereBivariateSpline, + RectSphereBivariateSpline) + + +class TestUnivariateSpline: + def test_linear_constant(self): + x = [1,2,3] + y = [3,3,3] + lut = UnivariateSpline(x,y,k=1) + assert_array_almost_equal(lut.get_knots(),[1,3]) + assert_array_almost_equal(lut.get_coeffs(),[3,3]) + assert_almost_equal(lut.get_residual(),0.0) + assert_array_almost_equal(lut([1,1.5,2]),[3,3,3]) + + def test_preserve_shape(self): + x = [1, 2, 3] + y = [0, 2, 4] + lut = UnivariateSpline(x, y, k=1) + arg = 2 + assert_equal(shape(arg), shape(lut(arg))) + assert_equal(shape(arg), shape(lut(arg, nu=1))) + arg = [1.5, 2, 2.5] + assert_equal(shape(arg), shape(lut(arg))) + assert_equal(shape(arg), shape(lut(arg, nu=1))) + + def test_linear_1d(self): + x = [1,2,3] + y = [0,2,4] + lut = UnivariateSpline(x,y,k=1) + assert_array_almost_equal(lut.get_knots(),[1,3]) + assert_array_almost_equal(lut.get_coeffs(),[0,4]) + assert_almost_equal(lut.get_residual(),0.0) + assert_array_almost_equal(lut([1,1.5,2]),[0,1,2]) + + def test_subclassing(self): + # See #731 + + class ZeroSpline(UnivariateSpline): + def __call__(self, x): + return 0*array(x) + + sp = ZeroSpline([1,2,3,4,5], [3,2,3,2,3], k=2) + assert_array_equal(sp([1.5, 2.5]), [0., 0.]) + + def test_empty_input(self): + # Test whether empty input returns an empty output. Ticket 1014 + x = [1,3,5,7,9] + y = [0,4,9,12,21] + spl = UnivariateSpline(x, y, k=3) + assert_array_equal(spl([]), array([])) + + def test_roots(self): + x = [1, 3, 5, 7, 9] + y = [0, 4, 9, 12, 21] + spl = UnivariateSpline(x, y, k=3) + assert_almost_equal(spl.roots()[0], 1.050290639101332) + + def test_roots_length(self): # for gh18335 + x = np.linspace(0, 50 * np.pi, 1000) + y = np.cos(x) + spl = UnivariateSpline(x, y, s=0) + assert_equal(len(spl.roots()), 50) + + def test_derivatives(self): + x = [1, 3, 5, 7, 9] + y = [0, 4, 9, 12, 21] + spl = UnivariateSpline(x, y, k=3) + assert_almost_equal(spl.derivatives(3.5), + [5.5152902, 1.7146577, -0.1830357, 0.3125]) + + def test_derivatives_2(self): + x = np.arange(8) + y = x**3 + 2.*x**2 + + tck = splrep(x, y, s=0) + ders = spalde(3, tck) + assert_allclose(ders, [45., # 3**3 + 2*(3)**2 + 39., # 3*(3)**2 + 4*(3) + 22., # 6*(3) + 4 + 6.], # 6*3**0 + atol=1e-15) + spl = UnivariateSpline(x, y, s=0, k=3) + assert_allclose(spl.derivatives(3), + ders, + atol=1e-15) + + def test_resize_regression(self): + """Regression test for #1375.""" + x = [-1., -0.65016502, -0.58856235, -0.26903553, -0.17370892, + -0.10011001, 0., 0.10011001, 0.17370892, 0.26903553, 0.58856235, + 0.65016502, 1.] + y = [1.,0.62928599, 0.5797223, 0.39965815, 0.36322694, 0.3508061, + 0.35214793, 0.3508061, 0.36322694, 0.39965815, 0.5797223, + 0.62928599, 1.] + w = [1.00000000e+12, 6.88875973e+02, 4.89314737e+02, 4.26864807e+02, + 6.07746770e+02, 4.51341444e+02, 3.17480210e+02, 4.51341444e+02, + 6.07746770e+02, 4.26864807e+02, 4.89314737e+02, 6.88875973e+02, + 1.00000000e+12] + spl = UnivariateSpline(x=x, y=y, w=w, s=None) + desired = array([0.35100374, 0.51715855, 0.87789547, 0.98719344]) + assert_allclose(spl([0.1, 0.5, 0.9, 0.99]), desired, atol=5e-4) + + def test_out_of_range_regression(self): + # Test different extrapolation modes. See ticket 3557 + x = np.arange(5, dtype=float) + y = x**3 + + xp = linspace(-8, 13, 100) + xp_zeros = xp.copy() + xp_zeros[np.logical_or(xp_zeros < 0., xp_zeros > 4.)] = 0 + xp_clip = xp.copy() + xp_clip[xp_clip < x[0]] = x[0] + xp_clip[xp_clip > x[-1]] = x[-1] + + for cls in [UnivariateSpline, InterpolatedUnivariateSpline]: + spl = cls(x=x, y=y) + for ext in [0, 'extrapolate']: + assert_allclose(spl(xp, ext=ext), xp**3, atol=1e-16) + assert_allclose(cls(x, y, ext=ext)(xp), xp**3, atol=1e-16) + for ext in [1, 'zeros']: + assert_allclose(spl(xp, ext=ext), xp_zeros**3, atol=1e-16) + assert_allclose(cls(x, y, ext=ext)(xp), xp_zeros**3, atol=1e-16) + for ext in [2, 'raise']: + assert_raises(ValueError, spl, xp, **dict(ext=ext)) + for ext in [3, 'const']: + assert_allclose(spl(xp, ext=ext), xp_clip**3, atol=1e-16) + assert_allclose(cls(x, y, ext=ext)(xp), xp_clip**3, atol=1e-16) + + # also test LSQUnivariateSpline [which needs explicit knots] + t = spl.get_knots()[3:4] # interior knots w/ default k=3 + spl = LSQUnivariateSpline(x, y, t) + assert_allclose(spl(xp, ext=0), xp**3, atol=1e-16) + assert_allclose(spl(xp, ext=1), xp_zeros**3, atol=1e-16) + assert_raises(ValueError, spl, xp, **dict(ext=2)) + assert_allclose(spl(xp, ext=3), xp_clip**3, atol=1e-16) + + # also make sure that unknown values for `ext` are caught early + for ext in [-1, 'unknown']: + spl = UnivariateSpline(x, y) + assert_raises(ValueError, spl, xp, **dict(ext=ext)) + assert_raises(ValueError, UnivariateSpline, + **dict(x=x, y=y, ext=ext)) + + def test_lsq_fpchec(self): + xs = np.arange(100) * 1. + ys = np.arange(100) * 1. + knots = np.linspace(0, 99, 10) + bbox = (-1, 101) + assert_raises(ValueError, LSQUnivariateSpline, xs, ys, knots, + bbox=bbox) + + def test_derivative_and_antiderivative(self): + # Thin wrappers to splder/splantider, so light smoke test only. + x = np.linspace(0, 1, 70)**3 + y = np.cos(x) + + spl = UnivariateSpline(x, y, s=0) + spl2 = spl.antiderivative(2).derivative(2) + assert_allclose(spl(0.3), spl2(0.3)) + + spl2 = spl.antiderivative(1) + assert_allclose(spl2(0.6) - spl2(0.2), + spl.integral(0.2, 0.6)) + + def test_derivative_extrapolation(self): + # Regression test for gh-10195: for a const-extrapolation spline + # its derivative evaluates to zero for extrapolation + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5, 5] + f = UnivariateSpline(x_values, y_values, ext='const', k=3) + + x = [-1, 0, -0.5, 9, 9.5, 10] + assert_allclose(f.derivative()(x), 0, atol=1e-15) + + def test_integral_out_of_bounds(self): + # Regression test for gh-7906: .integral(a, b) is wrong if both + # a and b are out-of-bounds + x = np.linspace(0., 1., 7) + for ext in range(4): + f = UnivariateSpline(x, x, s=0, ext=ext) + for (a, b) in [(1, 1), (1, 5), (2, 5), + (0, 0), (-2, 0), (-2, -1)]: + assert_allclose(f.integral(a, b), 0, atol=1e-15) + + def test_nan(self): + # bail out early if the input data contains nans + x = np.arange(10, dtype=float) + y = x**3 + w = np.ones_like(x) + # also test LSQUnivariateSpline [which needs explicit knots] + spl = UnivariateSpline(x, y, check_finite=True) + t = spl.get_knots()[3:4] # interior knots w/ default k=3 + y_end = y[-1] + for z in [np.nan, np.inf, -np.inf]: + y[-1] = z + assert_raises(ValueError, UnivariateSpline, + **dict(x=x, y=y, check_finite=True)) + assert_raises(ValueError, InterpolatedUnivariateSpline, + **dict(x=x, y=y, check_finite=True)) + assert_raises(ValueError, LSQUnivariateSpline, + **dict(x=x, y=y, t=t, check_finite=True)) + y[-1] = y_end # check valid y but invalid w + w[-1] = z + assert_raises(ValueError, UnivariateSpline, + **dict(x=x, y=y, w=w, check_finite=True)) + assert_raises(ValueError, InterpolatedUnivariateSpline, + **dict(x=x, y=y, w=w, check_finite=True)) + assert_raises(ValueError, LSQUnivariateSpline, + **dict(x=x, y=y, t=t, w=w, check_finite=True)) + + def test_strictly_increasing_x(self): + # Test the x is required to be strictly increasing for + # UnivariateSpline if s=0 and for InterpolatedUnivariateSpline, + # but merely increasing for UnivariateSpline if s>0 + # and for LSQUnivariateSpline; see gh-8535 + xx = np.arange(10, dtype=float) + yy = xx**3 + x = np.arange(10, dtype=float) + x[1] = x[0] + y = x**3 + w = np.ones_like(x) + # also test LSQUnivariateSpline [which needs explicit knots] + spl = UnivariateSpline(xx, yy, check_finite=True) + t = spl.get_knots()[3:4] # interior knots w/ default k=3 + UnivariateSpline(x=x, y=y, w=w, s=1, check_finite=True) + LSQUnivariateSpline(x=x, y=y, t=t, w=w, check_finite=True) + assert_raises(ValueError, UnivariateSpline, + **dict(x=x, y=y, s=0, check_finite=True)) + assert_raises(ValueError, InterpolatedUnivariateSpline, + **dict(x=x, y=y, check_finite=True)) + + def test_increasing_x(self): + # Test that x is required to be increasing, see gh-8535 + xx = np.arange(10, dtype=float) + yy = xx**3 + x = np.arange(10, dtype=float) + x[1] = x[0] - 1.0 + y = x**3 + w = np.ones_like(x) + # also test LSQUnivariateSpline [which needs explicit knots] + spl = UnivariateSpline(xx, yy, check_finite=True) + t = spl.get_knots()[3:4] # interior knots w/ default k=3 + assert_raises(ValueError, UnivariateSpline, + **dict(x=x, y=y, check_finite=True)) + assert_raises(ValueError, InterpolatedUnivariateSpline, + **dict(x=x, y=y, check_finite=True)) + assert_raises(ValueError, LSQUnivariateSpline, + **dict(x=x, y=y, t=t, w=w, check_finite=True)) + + def test_invalid_input_for_univariate_spline(self): + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5] + UnivariateSpline(x_values, y_values) + assert "x and y should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5, 2.8] + w_values = [-1.0, 1.0, 1.0, 1.0] + UnivariateSpline(x_values, y_values, w=w_values) + assert "x, y, and w should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + bbox = (-1) + UnivariateSpline(x_values, y_values, bbox=bbox) + assert "bbox shape should be (2,)" in str(info.value) + + with assert_raises(ValueError) as info: + UnivariateSpline(x_values, y_values, k=6) + assert "k should be 1 <= k <= 5" in str(info.value) + + with assert_raises(ValueError) as info: + UnivariateSpline(x_values, y_values, s=-1.0) + assert "s should be s >= 0.0" in str(info.value) + + def test_invalid_input_for_interpolated_univariate_spline(self): + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5] + InterpolatedUnivariateSpline(x_values, y_values) + assert "x and y should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5, 2.8] + w_values = [-1.0, 1.0, 1.0, 1.0] + InterpolatedUnivariateSpline(x_values, y_values, w=w_values) + assert "x, y, and w should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + bbox = (-1) + InterpolatedUnivariateSpline(x_values, y_values, bbox=bbox) + assert "bbox shape should be (2,)" in str(info.value) + + with assert_raises(ValueError) as info: + InterpolatedUnivariateSpline(x_values, y_values, k=6) + assert "k should be 1 <= k <= 5" in str(info.value) + + def test_invalid_input_for_lsq_univariate_spline(self): + + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5, 2.8] + spl = UnivariateSpline(x_values, y_values, check_finite=True) + t_values = spl.get_knots()[3:4] # interior knots w/ default k=3 + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5] + LSQUnivariateSpline(x_values, y_values, t_values) + assert "x and y should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + x_values = [1, 2, 4, 6, 8.5] + y_values = [0.5, 0.8, 1.3, 2.5, 2.8] + w_values = [1.0, 1.0, 1.0, 1.0] + LSQUnivariateSpline(x_values, y_values, t_values, w=w_values) + assert "x, y, and w should have a same length" in str(info.value) + + message = "Interior knots t must satisfy Schoenberg-Whitney conditions" + with assert_raises(ValueError, match=message) as info: + bbox = (100, -100) + LSQUnivariateSpline(x_values, y_values, t_values, bbox=bbox) + + with assert_raises(ValueError) as info: + bbox = (-1) + LSQUnivariateSpline(x_values, y_values, t_values, bbox=bbox) + assert "bbox shape should be (2,)" in str(info.value) + + with assert_raises(ValueError) as info: + LSQUnivariateSpline(x_values, y_values, t_values, k=6) + assert "k should be 1 <= k <= 5" in str(info.value) + + def test_array_like_input(self): + x_values = np.array([1, 2, 4, 6, 8.5]) + y_values = np.array([0.5, 0.8, 1.3, 2.5, 2.8]) + w_values = np.array([1.0, 1.0, 1.0, 1.0, 1.0]) + bbox = np.array([-100, 100]) + # np.array input + spl1 = UnivariateSpline(x=x_values, y=y_values, w=w_values, + bbox=bbox) + # list input + spl2 = UnivariateSpline(x=x_values.tolist(), y=y_values.tolist(), + w=w_values.tolist(), bbox=bbox.tolist()) + + assert_allclose(spl1([0.1, 0.5, 0.9, 0.99]), + spl2([0.1, 0.5, 0.9, 0.99])) + + def test_fpknot_oob_crash(self): + # https://github.com/scipy/scipy/issues/3691 + x = range(109) + y = [0., 0., 0., 0., 0., 10.9, 0., 11., 0., + 0., 0., 10.9, 0., 0., 0., 0., 0., 0., + 10.9, 0., 0., 0., 11., 0., 0., 0., 10.9, + 0., 0., 0., 10.5, 0., 0., 0., 10.7, 0., + 0., 0., 11., 0., 0., 0., 0., 0., 0., + 10.9, 0., 0., 10.7, 0., 0., 0., 10.6, 0., + 0., 0., 10.5, 0., 0., 10.7, 0., 0., 10.5, + 0., 0., 11.5, 0., 0., 0., 10.7, 0., 0., + 10.7, 0., 0., 10.9, 0., 0., 10.8, 0., 0., + 0., 10.7, 0., 0., 10.6, 0., 0., 0., 10.4, + 0., 0., 10.6, 0., 0., 10.5, 0., 0., 0., + 10.7, 0., 0., 0., 10.4, 0., 0., 0., 10.8, 0.] + with suppress_warnings() as sup: + r = sup.record( + UserWarning, + r""" +The maximal number of iterations maxit \(set to 20 by the program\) +allowed for finding a smoothing spline with fp=s has been reached: s +too small. +There is an approximation returned but the corresponding weighted sum +of squared residuals does not satisfy the condition abs\(fp-s\)/s < tol.""") + UnivariateSpline(x, y, k=1) + assert_equal(len(r), 1) + + +class TestLSQBivariateSpline: + # NOTE: The systems in this test class are rank-deficient + def test_linear_constant(self): + x = [1,1,1,2,2,2,3,3,3] + y = [1,2,3,1,2,3,1,2,3] + z = [3,3,3,3,3,3,3,3,3] + s = 0.1 + tx = [1+s,3-s] + ty = [1+s,3-s] + with suppress_warnings() as sup: + r = sup.record(UserWarning, "\nThe coefficients of the spline") + lut = LSQBivariateSpline(x,y,z,tx,ty,kx=1,ky=1) + assert_equal(len(r), 1) + + assert_almost_equal(lut(2,2), 3.) + + def test_bilinearity(self): + x = [1,1,1,2,2,2,3,3,3] + y = [1,2,3,1,2,3,1,2,3] + z = [0,7,8,3,4,7,1,3,4] + s = 0.1 + tx = [1+s,3-s] + ty = [1+s,3-s] + with suppress_warnings() as sup: + # This seems to fail (ier=1, see ticket 1642). + sup.filter(UserWarning, "\nThe coefficients of the spline") + lut = LSQBivariateSpline(x,y,z,tx,ty,kx=1,ky=1) + + tx, ty = lut.get_knots() + for xa, xb in zip(tx[:-1], tx[1:]): + for ya, yb in zip(ty[:-1], ty[1:]): + for t in [0.1, 0.5, 0.9]: + for s in [0.3, 0.4, 0.7]: + xp = xa*(1-t) + xb*t + yp = ya*(1-s) + yb*s + zp = (+ lut(xa, ya)*(1-t)*(1-s) + + lut(xb, ya)*t*(1-s) + + lut(xa, yb)*(1-t)*s + + lut(xb, yb)*t*s) + assert_almost_equal(lut(xp,yp), zp) + + def test_integral(self): + x = [1,1,1,2,2,2,8,8,8] + y = [1,2,3,1,2,3,1,2,3] + z = array([0,7,8,3,4,7,1,3,4]) + + s = 0.1 + tx = [1+s,3-s] + ty = [1+s,3-s] + with suppress_warnings() as sup: + r = sup.record(UserWarning, "\nThe coefficients of the spline") + lut = LSQBivariateSpline(x, y, z, tx, ty, kx=1, ky=1) + assert_equal(len(r), 1) + tx, ty = lut.get_knots() + tz = lut(tx, ty) + trpz = .25*(diff(tx)[:,None]*diff(ty)[None,:] + * (tz[:-1,:-1]+tz[1:,:-1]+tz[:-1,1:]+tz[1:,1:])).sum() + + assert_almost_equal(lut.integral(tx[0], tx[-1], ty[0], ty[-1]), + trpz) + + def test_empty_input(self): + # Test whether empty inputs returns an empty output. Ticket 1014 + x = [1,1,1,2,2,2,3,3,3] + y = [1,2,3,1,2,3,1,2,3] + z = [3,3,3,3,3,3,3,3,3] + s = 0.1 + tx = [1+s,3-s] + ty = [1+s,3-s] + with suppress_warnings() as sup: + r = sup.record(UserWarning, "\nThe coefficients of the spline") + lut = LSQBivariateSpline(x, y, z, tx, ty, kx=1, ky=1) + assert_equal(len(r), 1) + + assert_array_equal(lut([], []), np.zeros((0,0))) + assert_array_equal(lut([], [], grid=False), np.zeros((0,))) + + def test_invalid_input(self): + s = 0.1 + tx = [1 + s, 3 - s] + ty = [1 + s, 3 - s] + + with assert_raises(ValueError) as info: + x = np.linspace(1.0, 10.0) + y = np.linspace(1.0, 10.0) + z = np.linspace(1.0, 10.0, num=10) + LSQBivariateSpline(x, y, z, tx, ty) + assert "x, y, and z should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + x = np.linspace(1.0, 10.0) + y = np.linspace(1.0, 10.0) + z = np.linspace(1.0, 10.0) + w = np.linspace(1.0, 10.0, num=20) + LSQBivariateSpline(x, y, z, tx, ty, w=w) + assert "x, y, z, and w should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + w = np.linspace(-1.0, 10.0) + LSQBivariateSpline(x, y, z, tx, ty, w=w) + assert "w should be positive" in str(info.value) + + with assert_raises(ValueError) as info: + bbox = (-100, 100, -100) + LSQBivariateSpline(x, y, z, tx, ty, bbox=bbox) + assert "bbox shape should be (4,)" in str(info.value) + + with assert_raises(ValueError) as info: + LSQBivariateSpline(x, y, z, tx, ty, kx=10, ky=10) + assert "The length of x, y and z should be at least (kx+1) * (ky+1)" in \ + str(info.value) + + with assert_raises(ValueError) as exc_info: + LSQBivariateSpline(x, y, z, tx, ty, eps=0.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + LSQBivariateSpline(x, y, z, tx, ty, eps=1.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + def test_array_like_input(self): + s = 0.1 + tx = np.array([1 + s, 3 - s]) + ty = np.array([1 + s, 3 - s]) + x = np.linspace(1.0, 10.0) + y = np.linspace(1.0, 10.0) + z = np.linspace(1.0, 10.0) + w = np.linspace(1.0, 10.0) + bbox = np.array([1.0, 10.0, 1.0, 10.0]) + + with suppress_warnings() as sup: + r = sup.record(UserWarning, "\nThe coefficients of the spline") + # np.array input + spl1 = LSQBivariateSpline(x, y, z, tx, ty, w=w, bbox=bbox) + # list input + spl2 = LSQBivariateSpline(x.tolist(), y.tolist(), z.tolist(), + tx.tolist(), ty.tolist(), w=w.tolist(), + bbox=bbox) + assert_allclose(spl1(2.0, 2.0), spl2(2.0, 2.0)) + assert_equal(len(r), 2) + + def test_unequal_length_of_knots(self): + """Test for the case when the input knot-location arrays in x and y are + of different lengths. + """ + x, y = np.mgrid[0:100, 0:100] + x = x.ravel() + y = y.ravel() + z = 3.0 * np.ones_like(x) + tx = np.linspace(0.1, 98.0, 29) + ty = np.linspace(0.1, 98.0, 33) + with suppress_warnings() as sup: + r = sup.record(UserWarning, "\nThe coefficients of the spline") + lut = LSQBivariateSpline(x,y,z,tx,ty) + assert_equal(len(r), 1) + + assert_almost_equal(lut(x, y, grid=False), z) + + +class TestSmoothBivariateSpline: + def test_linear_constant(self): + x = [1,1,1,2,2,2,3,3,3] + y = [1,2,3,1,2,3,1,2,3] + z = [3,3,3,3,3,3,3,3,3] + lut = SmoothBivariateSpline(x,y,z,kx=1,ky=1) + assert_array_almost_equal(lut.get_knots(),([1,1,3,3],[1,1,3,3])) + assert_array_almost_equal(lut.get_coeffs(),[3,3,3,3]) + assert_almost_equal(lut.get_residual(),0.0) + assert_array_almost_equal(lut([1,1.5,2],[1,1.5]),[[3,3],[3,3],[3,3]]) + + def test_linear_1d(self): + x = [1,1,1,2,2,2,3,3,3] + y = [1,2,3,1,2,3,1,2,3] + z = [0,0,0,2,2,2,4,4,4] + lut = SmoothBivariateSpline(x,y,z,kx=1,ky=1) + assert_array_almost_equal(lut.get_knots(),([1,1,3,3],[1,1,3,3])) + assert_array_almost_equal(lut.get_coeffs(),[0,0,4,4]) + assert_almost_equal(lut.get_residual(),0.0) + assert_array_almost_equal(lut([1,1.5,2],[1,1.5]),[[0,0],[1,1],[2,2]]) + + def test_integral(self): + x = [1,1,1,2,2,2,4,4,4] + y = [1,2,3,1,2,3,1,2,3] + z = array([0,7,8,3,4,7,1,3,4]) + + with suppress_warnings() as sup: + # This seems to fail (ier=1, see ticket 1642). + sup.filter(UserWarning, "\nThe required storage space") + lut = SmoothBivariateSpline(x, y, z, kx=1, ky=1, s=0) + + tx = [1,2,4] + ty = [1,2,3] + + tz = lut(tx, ty) + trpz = .25*(diff(tx)[:,None]*diff(ty)[None,:] + * (tz[:-1,:-1]+tz[1:,:-1]+tz[:-1,1:]+tz[1:,1:])).sum() + assert_almost_equal(lut.integral(tx[0], tx[-1], ty[0], ty[-1]), trpz) + + lut2 = SmoothBivariateSpline(x, y, z, kx=2, ky=2, s=0) + assert_almost_equal(lut2.integral(tx[0], tx[-1], ty[0], ty[-1]), trpz, + decimal=0) # the quadratures give 23.75 and 23.85 + + tz = lut(tx[:-1], ty[:-1]) + trpz = .25*(diff(tx[:-1])[:,None]*diff(ty[:-1])[None,:] + * (tz[:-1,:-1]+tz[1:,:-1]+tz[:-1,1:]+tz[1:,1:])).sum() + assert_almost_equal(lut.integral(tx[0], tx[-2], ty[0], ty[-2]), trpz) + + def test_rerun_lwrk2_too_small(self): + # in this setting, lwrk2 is too small in the default run. Here we + # check for equality with the bisplrep/bisplev output because there, + # an automatic re-run of the spline representation is done if ier>10. + x = np.linspace(-2, 2, 80) + y = np.linspace(-2, 2, 80) + z = x + y + xi = np.linspace(-1, 1, 100) + yi = np.linspace(-2, 2, 100) + tck = bisplrep(x, y, z) + res1 = bisplev(xi, yi, tck) + interp_ = SmoothBivariateSpline(x, y, z) + res2 = interp_(xi, yi) + assert_almost_equal(res1, res2) + + def test_invalid_input(self): + + with assert_raises(ValueError) as info: + x = np.linspace(1.0, 10.0) + y = np.linspace(1.0, 10.0) + z = np.linspace(1.0, 10.0, num=10) + SmoothBivariateSpline(x, y, z) + assert "x, y, and z should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + x = np.linspace(1.0, 10.0) + y = np.linspace(1.0, 10.0) + z = np.linspace(1.0, 10.0) + w = np.linspace(1.0, 10.0, num=20) + SmoothBivariateSpline(x, y, z, w=w) + assert "x, y, z, and w should have a same length" in str(info.value) + + with assert_raises(ValueError) as info: + w = np.linspace(-1.0, 10.0) + SmoothBivariateSpline(x, y, z, w=w) + assert "w should be positive" in str(info.value) + + with assert_raises(ValueError) as info: + bbox = (-100, 100, -100) + SmoothBivariateSpline(x, y, z, bbox=bbox) + assert "bbox shape should be (4,)" in str(info.value) + + with assert_raises(ValueError) as info: + SmoothBivariateSpline(x, y, z, kx=10, ky=10) + assert "The length of x, y and z should be at least (kx+1) * (ky+1)" in\ + str(info.value) + + with assert_raises(ValueError) as info: + SmoothBivariateSpline(x, y, z, s=-1.0) + assert "s should be s >= 0.0" in str(info.value) + + with assert_raises(ValueError) as exc_info: + SmoothBivariateSpline(x, y, z, eps=0.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + SmoothBivariateSpline(x, y, z, eps=1.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + def test_array_like_input(self): + x = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3]) + y = np.array([1, 2, 3, 1, 2, 3, 1, 2, 3]) + z = np.array([3, 3, 3, 3, 3, 3, 3, 3, 3]) + w = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1]) + bbox = np.array([1.0, 3.0, 1.0, 3.0]) + # np.array input + spl1 = SmoothBivariateSpline(x, y, z, w=w, bbox=bbox, kx=1, ky=1) + # list input + spl2 = SmoothBivariateSpline(x.tolist(), y.tolist(), z.tolist(), + bbox=bbox.tolist(), w=w.tolist(), + kx=1, ky=1) + assert_allclose(spl1(0.1, 0.5), spl2(0.1, 0.5)) + + +class TestLSQSphereBivariateSpline: + def setup_method(self): + # define the input data and coordinates + ntheta, nphi = 70, 90 + theta = linspace(0.5/(ntheta - 1), 1 - 0.5/(ntheta - 1), ntheta) * pi + phi = linspace(0.5/(nphi - 1), 1 - 0.5/(nphi - 1), nphi) * 2. * pi + data = ones((theta.shape[0], phi.shape[0])) + # define knots and extract data values at the knots + knotst = theta[::5] + knotsp = phi[::5] + knotdata = data[::5, ::5] + # calculate spline coefficients + lats, lons = meshgrid(theta, phi) + lut_lsq = LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, knotsp) + self.lut_lsq = lut_lsq + self.data = knotdata + self.new_lons, self.new_lats = knotsp, knotst + + def test_linear_constant(self): + assert_almost_equal(self.lut_lsq.get_residual(), 0.0) + assert_array_almost_equal(self.lut_lsq(self.new_lats, self.new_lons), + self.data) + + def test_empty_input(self): + assert_array_almost_equal(self.lut_lsq([], []), np.zeros((0,0))) + assert_array_almost_equal(self.lut_lsq([], [], grid=False), np.zeros((0,))) + + def test_invalid_input(self): + ntheta, nphi = 70, 90 + theta = linspace(0.5 / (ntheta - 1), 1 - 0.5 / (ntheta - 1), + ntheta) * pi + phi = linspace(0.5 / (nphi - 1), 1 - 0.5 / (nphi - 1), nphi) * 2. * pi + data = ones((theta.shape[0], phi.shape[0])) + # define knots and extract data values at the knots + knotst = theta[::5] + knotsp = phi[::5] + + with assert_raises(ValueError) as exc_info: + invalid_theta = linspace(-0.1, 1.0, num=ntheta) * pi + invalid_lats, lons = meshgrid(invalid_theta, phi) + LSQSphereBivariateSpline(invalid_lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, knotsp) + assert "theta should be between [0, pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_theta = linspace(0.1, 1.1, num=ntheta) * pi + invalid_lats, lons = meshgrid(invalid_theta, phi) + LSQSphereBivariateSpline(invalid_lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, knotsp) + assert "theta should be between [0, pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_phi = linspace(-0.1, 1.0, num=ntheta) * 2.0 * pi + lats, invalid_lons = meshgrid(theta, invalid_phi) + LSQSphereBivariateSpline(lats.ravel(), invalid_lons.ravel(), + data.T.ravel(), knotst, knotsp) + assert "phi should be between [0, 2pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_phi = linspace(0.0, 1.1, num=ntheta) * 2.0 * pi + lats, invalid_lons = meshgrid(theta, invalid_phi) + LSQSphereBivariateSpline(lats.ravel(), invalid_lons.ravel(), + data.T.ravel(), knotst, knotsp) + assert "phi should be between [0, 2pi]" in str(exc_info.value) + + lats, lons = meshgrid(theta, phi) + + with assert_raises(ValueError) as exc_info: + invalid_knotst = np.copy(knotst) + invalid_knotst[0] = -0.1 + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), invalid_knotst, knotsp) + assert "tt should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_knotst = np.copy(knotst) + invalid_knotst[0] = pi + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), invalid_knotst, knotsp) + assert "tt should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_knotsp = np.copy(knotsp) + invalid_knotsp[0] = -0.1 + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, invalid_knotsp) + assert "tp should be between (0, 2pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_knotsp = np.copy(knotsp) + invalid_knotsp[0] = 2 * pi + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, invalid_knotsp) + assert "tp should be between (0, 2pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_w = array([-1.0, 1.0, 1.5, 0.5, 1.0, 1.5, 0.5, 1.0, 1.0]) + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(), + knotst, knotsp, w=invalid_w) + assert "w should be positive" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(), + knotst, knotsp, eps=0.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(), + knotst, knotsp, eps=1.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + def test_array_like_input(self): + ntheta, nphi = 70, 90 + theta = linspace(0.5 / (ntheta - 1), 1 - 0.5 / (ntheta - 1), + ntheta) * pi + phi = linspace(0.5 / (nphi - 1), 1 - 0.5 / (nphi - 1), + nphi) * 2. * pi + lats, lons = meshgrid(theta, phi) + data = ones((theta.shape[0], phi.shape[0])) + # define knots and extract data values at the knots + knotst = theta[::5] + knotsp = phi[::5] + w = ones(lats.ravel().shape[0]) + + # np.array input + spl1 = LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), + data.T.ravel(), knotst, knotsp, w=w) + # list input + spl2 = LSQSphereBivariateSpline(lats.ravel().tolist(), + lons.ravel().tolist(), + data.T.ravel().tolist(), + knotst.tolist(), + knotsp.tolist(), w=w.tolist()) + assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0)) + + +class TestSmoothSphereBivariateSpline: + def setup_method(self): + theta = array([.25*pi, .25*pi, .25*pi, .5*pi, .5*pi, .5*pi, .75*pi, + .75*pi, .75*pi]) + phi = array([.5 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, .5 * pi, pi, + 1.5 * pi]) + r = array([3, 3, 3, 3, 3, 3, 3, 3, 3]) + self.lut = SmoothSphereBivariateSpline(theta, phi, r, s=1E10) + + def test_linear_constant(self): + assert_almost_equal(self.lut.get_residual(), 0.) + assert_array_almost_equal(self.lut([1, 1.5, 2],[1, 1.5]), + [[3, 3], [3, 3], [3, 3]]) + + def test_empty_input(self): + assert_array_almost_equal(self.lut([], []), np.zeros((0,0))) + assert_array_almost_equal(self.lut([], [], grid=False), np.zeros((0,))) + + def test_invalid_input(self): + theta = array([.25 * pi, .25 * pi, .25 * pi, .5 * pi, .5 * pi, .5 * pi, + .75 * pi, .75 * pi, .75 * pi]) + phi = array([.5 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, .5 * pi, pi, + 1.5 * pi]) + r = array([3, 3, 3, 3, 3, 3, 3, 3, 3]) + + with assert_raises(ValueError) as exc_info: + invalid_theta = array([-0.1 * pi, .25 * pi, .25 * pi, .5 * pi, + .5 * pi, .5 * pi, .75 * pi, .75 * pi, + .75 * pi]) + SmoothSphereBivariateSpline(invalid_theta, phi, r, s=1E10) + assert "theta should be between [0, pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_theta = array([.25 * pi, .25 * pi, .25 * pi, .5 * pi, + .5 * pi, .5 * pi, .75 * pi, .75 * pi, + 1.1 * pi]) + SmoothSphereBivariateSpline(invalid_theta, phi, r, s=1E10) + assert "theta should be between [0, pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_phi = array([-.1 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, + .5 * pi, pi, 1.5 * pi]) + SmoothSphereBivariateSpline(theta, invalid_phi, r, s=1E10) + assert "phi should be between [0, 2pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_phi = array([1.0 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, + .5 * pi, pi, 2.1 * pi]) + SmoothSphereBivariateSpline(theta, invalid_phi, r, s=1E10) + assert "phi should be between [0, 2pi]" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + invalid_w = array([-1.0, 1.0, 1.5, 0.5, 1.0, 1.5, 0.5, 1.0, 1.0]) + SmoothSphereBivariateSpline(theta, phi, r, w=invalid_w, s=1E10) + assert "w should be positive" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + SmoothSphereBivariateSpline(theta, phi, r, s=-1.0) + assert "s should be positive" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + SmoothSphereBivariateSpline(theta, phi, r, eps=-1.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + SmoothSphereBivariateSpline(theta, phi, r, eps=1.0) + assert "eps should be between (0, 1)" in str(exc_info.value) + + def test_array_like_input(self): + theta = np.array([.25 * pi, .25 * pi, .25 * pi, .5 * pi, .5 * pi, + .5 * pi, .75 * pi, .75 * pi, .75 * pi]) + phi = np.array([.5 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, .5 * pi, + pi, 1.5 * pi]) + r = np.array([3, 3, 3, 3, 3, 3, 3, 3, 3]) + w = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) + + # np.array input + spl1 = SmoothSphereBivariateSpline(theta, phi, r, w=w, s=1E10) + + # list input + spl2 = SmoothSphereBivariateSpline(theta.tolist(), phi.tolist(), + r.tolist(), w=w.tolist(), s=1E10) + assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0)) + + +class TestRectBivariateSpline: + def test_defaults(self): + x = array([1,2,3,4,5]) + y = array([1,2,3,4,5]) + z = array([[1,2,1,2,1],[1,2,1,2,1],[1,2,3,2,1],[1,2,2,2,1],[1,2,1,2,1]]) + lut = RectBivariateSpline(x,y,z) + assert_array_almost_equal(lut(x,y),z) + + def test_evaluate(self): + x = array([1,2,3,4,5]) + y = array([1,2,3,4,5]) + z = array([[1,2,1,2,1],[1,2,1,2,1],[1,2,3,2,1],[1,2,2,2,1],[1,2,1,2,1]]) + lut = RectBivariateSpline(x,y,z) + + xi = [1, 2.3, 5.3, 0.5, 3.3, 1.2, 3] + yi = [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3] + zi = lut.ev(xi, yi) + zi2 = array([lut(xp, yp)[0,0] for xp, yp in zip(xi, yi)]) + + assert_almost_equal(zi, zi2) + + def test_derivatives_grid(self): + x = array([1,2,3,4,5]) + y = array([1,2,3,4,5]) + z = array([[1,2,1,2,1],[1,2,1,2,1],[1,2,3,2,1],[1,2,2,2,1],[1,2,1,2,1]]) + dx = array([[0,0,-20,0,0],[0,0,13,0,0],[0,0,4,0,0], + [0,0,-11,0,0],[0,0,4,0,0]])/6. + dy = array([[4,-1,0,1,-4],[4,-1,0,1,-4],[0,1.5,0,-1.5,0], + [2,.25,0,-.25,-2],[4,-1,0,1,-4]]) + dxdy = array([[40,-25,0,25,-40],[-26,16.25,0,-16.25,26], + [-8,5,0,-5,8],[22,-13.75,0,13.75,-22],[-8,5,0,-5,8]])/6. + lut = RectBivariateSpline(x,y,z) + assert_array_almost_equal(lut(x,y,dx=1),dx) + assert_array_almost_equal(lut(x,y,dy=1),dy) + assert_array_almost_equal(lut(x,y,dx=1,dy=1),dxdy) + + def test_derivatives(self): + x = array([1,2,3,4,5]) + y = array([1,2,3,4,5]) + z = array([[1,2,1,2,1],[1,2,1,2,1],[1,2,3,2,1],[1,2,2,2,1],[1,2,1,2,1]]) + dx = array([0,0,2./3,0,0]) + dy = array([4,-1,0,-.25,-4]) + dxdy = array([160,65,0,55,32])/24. + lut = RectBivariateSpline(x,y,z) + assert_array_almost_equal(lut(x,y,dx=1,grid=False),dx) + assert_array_almost_equal(lut(x,y,dy=1,grid=False),dy) + assert_array_almost_equal(lut(x,y,dx=1,dy=1,grid=False),dxdy) + + def test_partial_derivative_method_grid(self): + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], + [1, 2, 1, 2, 1], + [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], + [1, 2, 1, 2, 1]]) + dx = array([[0, 0, -20, 0, 0], + [0, 0, 13, 0, 0], + [0, 0, 4, 0, 0], + [0, 0, -11, 0, 0], + [0, 0, 4, 0, 0]]) / 6. + dy = array([[4, -1, 0, 1, -4], + [4, -1, 0, 1, -4], + [0, 1.5, 0, -1.5, 0], + [2, .25, 0, -.25, -2], + [4, -1, 0, 1, -4]]) + dxdy = array([[40, -25, 0, 25, -40], + [-26, 16.25, 0, -16.25, 26], + [-8, 5, 0, -5, 8], + [22, -13.75, 0, 13.75, -22], + [-8, 5, 0, -5, 8]]) / 6. + lut = RectBivariateSpline(x, y, z) + assert_array_almost_equal(lut.partial_derivative(1, 0)(x, y), dx) + assert_array_almost_equal(lut.partial_derivative(0, 1)(x, y), dy) + assert_array_almost_equal(lut.partial_derivative(1, 1)(x, y), dxdy) + + def test_partial_derivative_method(self): + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], + [1, 2, 1, 2, 1], + [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], + [1, 2, 1, 2, 1]]) + dx = array([0, 0, 2./3, 0, 0]) + dy = array([4, -1, 0, -.25, -4]) + dxdy = array([160, 65, 0, 55, 32]) / 24. + lut = RectBivariateSpline(x, y, z) + assert_array_almost_equal(lut.partial_derivative(1, 0)(x, y, + grid=False), + dx) + assert_array_almost_equal(lut.partial_derivative(0, 1)(x, y, + grid=False), + dy) + assert_array_almost_equal(lut.partial_derivative(1, 1)(x, y, + grid=False), + dxdy) + + def test_partial_derivative_order_too_large(self): + x = array([0, 1, 2, 3, 4], dtype=float) + y = x.copy() + z = ones((x.size, y.size)) + lut = RectBivariateSpline(x, y, z) + with assert_raises(ValueError): + lut.partial_derivative(4, 1) + + def test_broadcast(self): + x = array([1,2,3,4,5]) + y = array([1,2,3,4,5]) + z = array([[1,2,1,2,1],[1,2,1,2,1],[1,2,3,2,1],[1,2,2,2,1],[1,2,1,2,1]]) + lut = RectBivariateSpline(x,y,z) + assert_allclose(lut(x, y), lut(x[:,None], y[None,:], grid=False)) + + def test_invalid_input(self): + + with assert_raises(ValueError) as info: + x = array([6, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + RectBivariateSpline(x, y, z) + assert "x must be strictly increasing" in str(info.value) + + with assert_raises(ValueError) as info: + x = array([1, 2, 3, 4, 5]) + y = array([2, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + RectBivariateSpline(x, y, z) + assert "y must be strictly increasing" in str(info.value) + + with assert_raises(ValueError) as info: + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1]]) + RectBivariateSpline(x, y, z) + assert "x dimension of z must have same number of elements as x"\ + in str(info.value) + + with assert_raises(ValueError) as info: + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2], [1, 2, 1, 2], [1, 2, 3, 2], + [1, 2, 2, 2], [1, 2, 1, 2]]) + RectBivariateSpline(x, y, z) + assert "y dimension of z must have same number of elements as y"\ + in str(info.value) + + with assert_raises(ValueError) as info: + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + bbox = (-100, 100, -100) + RectBivariateSpline(x, y, z, bbox=bbox) + assert "bbox shape should be (4,)" in str(info.value) + + with assert_raises(ValueError) as info: + RectBivariateSpline(x, y, z, s=-1.0) + assert "s should be s >= 0.0" in str(info.value) + + def test_array_like_input(self): + x = array([1, 2, 3, 4, 5]) + y = array([1, 2, 3, 4, 5]) + z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + bbox = array([1, 5, 1, 5]) + + spl1 = RectBivariateSpline(x, y, z, bbox=bbox) + spl2 = RectBivariateSpline(x.tolist(), y.tolist(), z.tolist(), + bbox=bbox.tolist()) + assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0)) + + def test_not_increasing_input(self): + # gh-8565 + NSamp = 20 + Theta = np.random.uniform(0, np.pi, NSamp) + Phi = np.random.uniform(0, 2 * np.pi, NSamp) + Data = np.ones(NSamp) + + Interpolator = SmoothSphereBivariateSpline(Theta, Phi, Data, s=3.5) + + NLon = 6 + NLat = 3 + GridPosLats = np.arange(NLat) / NLat * np.pi + GridPosLons = np.arange(NLon) / NLon * 2 * np.pi + + # No error + Interpolator(GridPosLats, GridPosLons) + + nonGridPosLats = GridPosLats.copy() + nonGridPosLats[2] = 0.001 + with assert_raises(ValueError) as exc_info: + Interpolator(nonGridPosLats, GridPosLons) + assert "x must be strictly increasing" in str(exc_info.value) + + nonGridPosLons = GridPosLons.copy() + nonGridPosLons[2] = 0.001 + with assert_raises(ValueError) as exc_info: + Interpolator(GridPosLats, nonGridPosLons) + assert "y must be strictly increasing" in str(exc_info.value) + + +class TestRectSphereBivariateSpline: + def test_defaults(self): + y = linspace(0.01, 2*pi-0.01, 7) + x = linspace(0.01, pi-0.01, 7) + z = array([[1,2,1,2,1,2,1],[1,2,1,2,1,2,1],[1,2,3,2,1,2,1], + [1,2,2,2,1,2,1],[1,2,1,2,1,2,1],[1,2,2,2,1,2,1], + [1,2,1,2,1,2,1]]) + lut = RectSphereBivariateSpline(x,y,z) + assert_array_almost_equal(lut(x,y),z) + + def test_evaluate(self): + y = linspace(0.01, 2*pi-0.01, 7) + x = linspace(0.01, pi-0.01, 7) + z = array([[1,2,1,2,1,2,1],[1,2,1,2,1,2,1],[1,2,3,2,1,2,1], + [1,2,2,2,1,2,1],[1,2,1,2,1,2,1],[1,2,2,2,1,2,1], + [1,2,1,2,1,2,1]]) + lut = RectSphereBivariateSpline(x,y,z) + yi = [0.2, 1, 2.3, 2.35, 3.0, 3.99, 5.25] + xi = [1.5, 0.4, 1.1, 0.45, 0.2345, 1., 0.0001] + zi = lut.ev(xi, yi) + zi2 = array([lut(xp, yp)[0,0] for xp, yp in zip(xi, yi)]) + assert_almost_equal(zi, zi2) + + def test_invalid_input(self): + data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, + np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(-1, 170, 9) * np.pi / 180. + lons = np.linspace(0, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "u should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 181, 9) * np.pi / 180. + lons = np.linspace(0, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "u should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(-181, 10, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "v[0] should be between [-pi, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(-10, 360, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "v[-1] should be v[0] + 2pi or less" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(10, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data, s=-1) + assert "s should be positive" in str(exc_info.value) + + def test_derivatives_grid(self): + y = linspace(0.01, 2*pi-0.01, 7) + x = linspace(0.01, pi-0.01, 7) + z = array([[1,2,1,2,1,2,1],[1,2,1,2,1,2,1],[1,2,3,2,1,2,1], + [1,2,2,2,1,2,1],[1,2,1,2,1,2,1],[1,2,2,2,1,2,1], + [1,2,1,2,1,2,1]]) + + lut = RectSphereBivariateSpline(x,y,z) + + y = linspace(0.02, 2*pi-0.02, 7) + x = linspace(0.02, pi-0.02, 7) + + assert_allclose(lut(x, y, dtheta=1), _numdiff_2d(lut, x, y, dx=1), + rtol=1e-4, atol=1e-4) + assert_allclose(lut(x, y, dphi=1), _numdiff_2d(lut, x, y, dy=1), + rtol=1e-4, atol=1e-4) + assert_allclose(lut(x, y, dtheta=1, dphi=1), + _numdiff_2d(lut, x, y, dx=1, dy=1, eps=1e-6), + rtol=1e-3, atol=1e-3) + + assert_array_equal(lut(x, y, dtheta=1), + lut.partial_derivative(1, 0)(x, y)) + assert_array_equal(lut(x, y, dphi=1), + lut.partial_derivative(0, 1)(x, y)) + assert_array_equal(lut(x, y, dtheta=1, dphi=1), + lut.partial_derivative(1, 1)(x, y)) + + assert_array_equal(lut(x, y, dtheta=1, grid=False), + lut.partial_derivative(1, 0)(x, y, grid=False)) + assert_array_equal(lut(x, y, dphi=1, grid=False), + lut.partial_derivative(0, 1)(x, y, grid=False)) + assert_array_equal(lut(x, y, dtheta=1, dphi=1, grid=False), + lut.partial_derivative(1, 1)(x, y, grid=False)) + + def test_derivatives(self): + y = linspace(0.01, 2*pi-0.01, 7) + x = linspace(0.01, pi-0.01, 7) + z = array([[1,2,1,2,1,2,1],[1,2,1,2,1,2,1],[1,2,3,2,1,2,1], + [1,2,2,2,1,2,1],[1,2,1,2,1,2,1],[1,2,2,2,1,2,1], + [1,2,1,2,1,2,1]]) + + lut = RectSphereBivariateSpline(x,y,z) + + y = linspace(0.02, 2*pi-0.02, 7) + x = linspace(0.02, pi-0.02, 7) + + assert_equal(lut(x, y, dtheta=1, grid=False).shape, x.shape) + assert_allclose(lut(x, y, dtheta=1, grid=False), + _numdiff_2d(lambda x,y: lut(x,y,grid=False), x, y, dx=1), + rtol=1e-4, atol=1e-4) + assert_allclose(lut(x, y, dphi=1, grid=False), + _numdiff_2d(lambda x,y: lut(x,y,grid=False), x, y, dy=1), + rtol=1e-4, atol=1e-4) + assert_allclose(lut(x, y, dtheta=1, dphi=1, grid=False), + _numdiff_2d(lambda x,y: lut(x,y,grid=False), + x, y, dx=1, dy=1, eps=1e-6), + rtol=1e-3, atol=1e-3) + + def test_invalid_input_2(self): + data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, + np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(0, 170, 9) * np.pi / 180. + lons = np.linspace(0, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "u should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 180, 9) * np.pi / 180. + lons = np.linspace(0, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "u should be between (0, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(-181, 10, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "v[0] should be between [-pi, pi)" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(-10, 360, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data) + assert "v[-1] should be v[0] + 2pi or less" in str(exc_info.value) + + with assert_raises(ValueError) as exc_info: + lats = np.linspace(10, 170, 9) * np.pi / 180. + lons = np.linspace(10, 350, 18) * np.pi / 180. + RectSphereBivariateSpline(lats, lons, data, s=-1) + assert "s should be positive" in str(exc_info.value) + + def test_array_like_input(self): + y = linspace(0.01, 2 * pi - 0.01, 7) + x = linspace(0.01, pi - 0.01, 7) + z = array([[1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], + [1, 2, 3, 2, 1, 2, 1], + [1, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], + [1, 2, 2, 2, 1, 2, 1], + [1, 2, 1, 2, 1, 2, 1]]) + # np.array input + spl1 = RectSphereBivariateSpline(x, y, z) + # list input + spl2 = RectSphereBivariateSpline(x.tolist(), y.tolist(), z.tolist()) + assert_array_almost_equal(spl1(x, y), spl2(x, y)) + + def test_negative_evaluation(self): + lats = np.array([25, 30, 35, 40, 45]) + lons = np.array([-90, -85, -80, -75, 70]) + mesh = np.meshgrid(lats, lons) + data = mesh[0] + mesh[1] # lon + lat value + lat_r = np.radians(lats) + lon_r = np.radians(lons) + interpolator = RectSphereBivariateSpline(lat_r, lon_r, data) + query_lat = np.radians(np.array([35, 37.5])) + query_lon = np.radians(np.array([-80, -77.5])) + data_interp = interpolator(query_lat, query_lon) + ans = np.array([[-45.0, -42.480862], + [-49.0625, -46.54315]]) + assert_array_almost_equal(data_interp, ans) + + def test_pole_continuity_gh_14591(self): + # regression test for https://github.com/scipy/scipy/issues/14591 + # with pole_continuty=(True, True), the internal work array size + # was too small, leading to a FITPACK data validation error. + + # The reproducer in gh-14591 was using a NetCDF4 file with + # 361x507 arrays, so here we trivialize array sizes to a minimum + # which still demonstrates the issue. + u = np.arange(1, 10) * np.pi / 10 + v = np.arange(1, 10) * np.pi / 10 + r = np.zeros((9, 9)) + for p in [(True, True), (True, False), (False, False)]: + RectSphereBivariateSpline(u, v, r, s=0, pole_continuity=p) + + +def _numdiff_2d(func, x, y, dx=0, dy=0, eps=1e-8): + if dx == 0 and dy == 0: + return func(x, y) + elif dx == 1 and dy == 0: + return (func(x + eps, y) - func(x - eps, y)) / (2*eps) + elif dx == 0 and dy == 1: + return (func(x, y + eps) - func(x, y - eps)) / (2*eps) + elif dx == 1 and dy == 1: + return (func(x + eps, y + eps) - func(x - eps, y + eps) + - func(x + eps, y - eps) + func(x - eps, y - eps)) / (2*eps)**2 + else: + raise ValueError("invalid derivative order") + + +class Test_DerivedBivariateSpline: + """Test the creation, usage, and attribute access of the (private) + _DerivedBivariateSpline class. + """ + def setup_method(self): + x = np.concatenate(list(zip(range(10), range(10)))) + y = np.concatenate(list(zip(range(10), range(1, 11)))) + z = np.concatenate((np.linspace(3, 1, 10), np.linspace(1, 3, 10))) + with suppress_warnings() as sup: + sup.record(UserWarning, "\nThe coefficients of the spline") + self.lut_lsq = LSQBivariateSpline(x, y, z, + linspace(0.5, 19.5, 4), + linspace(1.5, 20.5, 4), + eps=1e-2) + self.lut_smooth = SmoothBivariateSpline(x, y, z) + xx = linspace(0, 1, 20) + yy = xx + 1.0 + zz = array([np.roll(z, i) for i in range(z.size)]) + self.lut_rect = RectBivariateSpline(xx, yy, zz) + self.orders = list(itertools.product(range(3), range(3))) + + def test_creation_from_LSQ(self): + for nux, nuy in self.orders: + lut_der = self.lut_lsq.partial_derivative(nux, nuy) + a = lut_der(3.5, 3.5, grid=False) + b = self.lut_lsq(3.5, 3.5, dx=nux, dy=nuy, grid=False) + assert_equal(a, b) + + def test_creation_from_Smooth(self): + for nux, nuy in self.orders: + lut_der = self.lut_smooth.partial_derivative(nux, nuy) + a = lut_der(5.5, 5.5, grid=False) + b = self.lut_smooth(5.5, 5.5, dx=nux, dy=nuy, grid=False) + assert_equal(a, b) + + def test_creation_from_Rect(self): + for nux, nuy in self.orders: + lut_der = self.lut_rect.partial_derivative(nux, nuy) + a = lut_der(0.5, 1.5, grid=False) + b = self.lut_rect(0.5, 1.5, dx=nux, dy=nuy, grid=False) + assert_equal(a, b) + + def test_invalid_attribute_fp(self): + der = self.lut_rect.partial_derivative(1, 1) + with assert_raises(AttributeError): + der.fp + + def test_invalid_attribute_get_residual(self): + der = self.lut_smooth.partial_derivative(1, 1) + with assert_raises(AttributeError): + der.get_residual() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_gil.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_gil.py new file mode 100644 index 0000000000000000000000000000000000000000..0902308fb6af6802ba216e3aeec499d0ddfb1407 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_gil.py @@ -0,0 +1,65 @@ +import itertools +import threading +import time + +import numpy as np +from numpy.testing import assert_equal +import pytest +import scipy.interpolate + + +class TestGIL: + """Check if the GIL is properly released by scipy.interpolate functions.""" + + def setup_method(self): + self.messages = [] + + def log(self, message): + self.messages.append(message) + + def make_worker_thread(self, target, args): + log = self.log + + class WorkerThread(threading.Thread): + def run(self): + log('interpolation started') + target(*args) + log('interpolation complete') + + return WorkerThread() + + @pytest.mark.slow + @pytest.mark.xfail(reason='race conditions, may depend on system load') + def test_rectbivariatespline(self): + def generate_params(n_points): + x = y = np.linspace(0, 1000, n_points) + x_grid, y_grid = np.meshgrid(x, y) + z = x_grid * y_grid + return x, y, z + + def calibrate_delay(requested_time): + for n_points in itertools.count(5000, 1000): + args = generate_params(n_points) + time_started = time.time() + interpolate(*args) + if time.time() - time_started > requested_time: + return args + + def interpolate(x, y, z): + scipy.interpolate.RectBivariateSpline(x, y, z) + + args = calibrate_delay(requested_time=3) + worker_thread = self.make_worker_thread(interpolate, args) + worker_thread.start() + for i in range(3): + time.sleep(0.5) + self.log('working') + worker_thread.join() + assert_equal(self.messages, [ + 'interpolation started', + 'working', + 'working', + 'working', + 'interpolation complete', + ]) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpnd.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpnd.py new file mode 100644 index 0000000000000000000000000000000000000000..2c7d52b422fb79971ddf86247196e92ea606a22d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpnd.py @@ -0,0 +1,387 @@ +import os + +import numpy as np +from numpy.testing import (assert_equal, assert_allclose, assert_almost_equal, + suppress_warnings) +from pytest import raises as assert_raises +import pytest + +import scipy.interpolate.interpnd as interpnd +import scipy.spatial._qhull as qhull + +import pickle + + +def data_file(basename): + return os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'data', basename) + + +class TestLinearNDInterpolation: + def test_smoketest(self): + # Test at single points + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + + yi = interpnd.LinearNDInterpolator(x, y)(x) + assert_almost_equal(y, yi) + + def test_smoketest_alternate(self): + # Test at single points, alternate calling convention + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + + yi = interpnd.LinearNDInterpolator((x[:,0], x[:,1]), y)(x[:,0], x[:,1]) + assert_almost_equal(y, yi) + + def test_complex_smoketest(self): + # Test at single points + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + yi = interpnd.LinearNDInterpolator(x, y)(x) + assert_almost_equal(y, yi) + + def test_tri_input(self): + # Test at single points + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + yi = interpnd.LinearNDInterpolator(tri, y)(x) + assert_almost_equal(y, yi) + + def test_square(self): + # Test barycentric interpolation on a square against a manual + # implementation + + points = np.array([(0,0), (0,1), (1,1), (1,0)], dtype=np.float64) + values = np.array([1., 2., -3., 5.], dtype=np.float64) + + # NB: assume triangles (0, 1, 3) and (1, 2, 3) + # + # 1----2 + # | \ | + # | \ | + # 0----3 + + def ip(x, y): + t1 = (x + y <= 1) + t2 = ~t1 + + x1 = x[t1] + y1 = y[t1] + + x2 = x[t2] + y2 = y[t2] + + z = 0*x + + z[t1] = (values[0]*(1 - x1 - y1) + + values[1]*y1 + + values[3]*x1) + + z[t2] = (values[2]*(x2 + y2 - 1) + + values[1]*(1 - x2) + + values[3]*(1 - y2)) + return z + + xx, yy = np.broadcast_arrays(np.linspace(0, 1, 14)[:,None], + np.linspace(0, 1, 14)[None,:]) + xx = xx.ravel() + yy = yy.ravel() + + xi = np.array([xx, yy]).T.copy() + zi = interpnd.LinearNDInterpolator(points, values)(xi) + + assert_almost_equal(zi, ip(xx, yy)) + + def test_smoketest_rescale(self): + # Test at single points + x = np.array([(0, 0), (-5, -5), (-5, 5), (5, 5), (2.5, 3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + + yi = interpnd.LinearNDInterpolator(x, y, rescale=True)(x) + assert_almost_equal(y, yi) + + def test_square_rescale(self): + # Test barycentric interpolation on a rectangle with rescaling + # agaings the same implementation without rescaling + + points = np.array([(0,0), (0,100), (10,100), (10,0)], dtype=np.float64) + values = np.array([1., 2., -3., 5.], dtype=np.float64) + + xx, yy = np.broadcast_arrays(np.linspace(0, 10, 14)[:,None], + np.linspace(0, 100, 14)[None,:]) + xx = xx.ravel() + yy = yy.ravel() + xi = np.array([xx, yy]).T.copy() + zi = interpnd.LinearNDInterpolator(points, values)(xi) + zi_rescaled = interpnd.LinearNDInterpolator(points, values, + rescale=True)(xi) + + assert_almost_equal(zi, zi_rescaled) + + def test_tripoints_input_rescale(self): + # Test at single points + x = np.array([(0,0), (-5,-5), (-5,5), (5, 5), (2.5, 3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + yi = interpnd.LinearNDInterpolator(tri.points, y)(x) + yi_rescale = interpnd.LinearNDInterpolator(tri.points, y, + rescale=True)(x) + assert_almost_equal(yi, yi_rescale) + + def test_tri_input_rescale(self): + # Test at single points + x = np.array([(0,0), (-5,-5), (-5,5), (5, 5), (2.5, 3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + match = ("Rescaling is not supported when passing a " + "Delaunay triangulation as ``points``.") + with pytest.raises(ValueError, match=match): + interpnd.LinearNDInterpolator(tri, y, rescale=True)(x) + + def test_pickle(self): + # Test at single points + np.random.seed(1234) + x = np.random.rand(30, 2) + y = np.random.rand(30) + 1j*np.random.rand(30) + + ip = interpnd.LinearNDInterpolator(x, y) + ip2 = pickle.loads(pickle.dumps(ip)) + + assert_almost_equal(ip(0.5, 0.5), ip2(0.5, 0.5)) + + +class TestEstimateGradients2DGlobal: + def test_smoketest(self): + x = np.array([(0, 0), (0, 2), + (1, 0), (1, 2), (0.25, 0.75), (0.6, 0.8)], dtype=float) + tri = qhull.Delaunay(x) + + # Should be exact for linear functions, independent of triangulation + + funcs = [ + (lambda x, y: 0*x + 1, (0, 0)), + (lambda x, y: 0 + x, (1, 0)), + (lambda x, y: -2 + y, (0, 1)), + (lambda x, y: 3 + 3*x + 14.15*y, (3, 14.15)) + ] + + for j, (func, grad) in enumerate(funcs): + z = func(x[:,0], x[:,1]) + dz = interpnd.estimate_gradients_2d_global(tri, z, tol=1e-6) + + assert_equal(dz.shape, (6, 2)) + assert_allclose(dz, np.array(grad)[None,:] + 0*dz, + rtol=1e-5, atol=1e-5, err_msg="item %d" % j) + + def test_regression_2359(self): + # Check regression --- for certain point sets, gradient + # estimation could end up in an infinite loop + points = np.load(data_file('estimate_gradients_hang.npy')) + values = np.random.rand(points.shape[0]) + tri = qhull.Delaunay(points) + + # This should not hang + with suppress_warnings() as sup: + sup.filter(interpnd.GradientEstimationWarning, + "Gradient estimation did not converge") + interpnd.estimate_gradients_2d_global(tri, values, maxiter=1) + + +class TestCloughTocher2DInterpolator: + + def _check_accuracy(self, func, x=None, tol=1e-6, alternate=False, + rescale=False, **kw): + np.random.seed(1234) + if x is None: + x = np.array([(0, 0), (0, 1), + (1, 0), (1, 1), (0.25, 0.75), (0.6, 0.8), + (0.5, 0.2)], + dtype=float) + + if not alternate: + ip = interpnd.CloughTocher2DInterpolator(x, func(x[:,0], x[:,1]), + tol=1e-6, rescale=rescale) + else: + ip = interpnd.CloughTocher2DInterpolator((x[:,0], x[:,1]), + func(x[:,0], x[:,1]), + tol=1e-6, rescale=rescale) + + p = np.random.rand(50, 2) + + if not alternate: + a = ip(p) + else: + a = ip(p[:,0], p[:,1]) + b = func(p[:,0], p[:,1]) + + try: + assert_allclose(a, b, **kw) + except AssertionError: + print("_check_accuracy: abs(a-b):", abs(a - b)) + print("ip.grad:", ip.grad) + raise + + def test_linear_smoketest(self): + # Should be exact for linear functions, independent of triangulation + funcs = [ + lambda x, y: 0*x + 1, + lambda x, y: 0 + x, + lambda x, y: -2 + y, + lambda x, y: 3 + 3*x + 14.15*y, + ] + + for j, func in enumerate(funcs): + self._check_accuracy(func, tol=1e-13, atol=1e-7, rtol=1e-7, + err_msg="Function %d" % j) + self._check_accuracy(func, tol=1e-13, atol=1e-7, rtol=1e-7, + alternate=True, + err_msg="Function (alternate) %d" % j) + # check rescaling + self._check_accuracy(func, tol=1e-13, atol=1e-7, rtol=1e-7, + err_msg="Function (rescaled) %d" % j, rescale=True) + self._check_accuracy(func, tol=1e-13, atol=1e-7, rtol=1e-7, + alternate=True, rescale=True, + err_msg="Function (alternate, rescaled) %d" % j) + + def test_quadratic_smoketest(self): + # Should be reasonably accurate for quadratic functions + funcs = [ + lambda x, y: x**2, + lambda x, y: y**2, + lambda x, y: x**2 - y**2, + lambda x, y: x*y, + ] + + for j, func in enumerate(funcs): + self._check_accuracy(func, tol=1e-9, atol=0.22, rtol=0, + err_msg="Function %d" % j) + self._check_accuracy(func, tol=1e-9, atol=0.22, rtol=0, + err_msg="Function %d" % j, rescale=True) + + def test_tri_input(self): + # Test at single points + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + yi = interpnd.CloughTocher2DInterpolator(tri, y)(x) + assert_almost_equal(y, yi) + + def test_tri_input_rescale(self): + # Test at single points + x = np.array([(0,0), (-5,-5), (-5,5), (5, 5), (2.5, 3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + match = ("Rescaling is not supported when passing a " + "Delaunay triangulation as ``points``.") + with pytest.raises(ValueError, match=match): + interpnd.CloughTocher2DInterpolator(tri, y, rescale=True)(x) + + def test_tripoints_input_rescale(self): + # Test at single points + x = np.array([(0,0), (-5,-5), (-5,5), (5, 5), (2.5, 3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 3j*y + + tri = qhull.Delaunay(x) + yi = interpnd.CloughTocher2DInterpolator(tri.points, y)(x) + yi_rescale = interpnd.CloughTocher2DInterpolator(tri.points, y, rescale=True)(x) + assert_almost_equal(yi, yi_rescale) + + def test_dense(self): + # Should be more accurate for dense meshes + funcs = [ + lambda x, y: x**2, + lambda x, y: y**2, + lambda x, y: x**2 - y**2, + lambda x, y: x*y, + lambda x, y: np.cos(2*np.pi*x)*np.sin(2*np.pi*y) + ] + + np.random.seed(4321) # use a different seed than the check! + grid = np.r_[np.array([(0,0), (0,1), (1,0), (1,1)], dtype=float), + np.random.rand(30*30, 2)] + + for j, func in enumerate(funcs): + self._check_accuracy(func, x=grid, tol=1e-9, atol=5e-3, rtol=1e-2, + err_msg="Function %d" % j) + self._check_accuracy(func, x=grid, tol=1e-9, atol=5e-3, rtol=1e-2, + err_msg="Function %d" % j, rescale=True) + + def test_wrong_ndim(self): + x = np.random.randn(30, 3) + y = np.random.randn(30) + assert_raises(ValueError, interpnd.CloughTocher2DInterpolator, x, y) + + def test_pickle(self): + # Test at single points + np.random.seed(1234) + x = np.random.rand(30, 2) + y = np.random.rand(30) + 1j*np.random.rand(30) + + ip = interpnd.CloughTocher2DInterpolator(x, y) + ip2 = pickle.loads(pickle.dumps(ip)) + + assert_almost_equal(ip(0.5, 0.5), ip2(0.5, 0.5)) + + def test_boundary_tri_symmetry(self): + # Interpolation at neighbourless triangles should retain + # symmetry with mirroring the triangle. + + # Equilateral triangle + points = np.array([(0, 0), (1, 0), (0.5, np.sqrt(3)/2)]) + values = np.array([1, 0, 0]) + + ip = interpnd.CloughTocher2DInterpolator(points, values) + + # Set gradient to zero at vertices + ip.grad[...] = 0 + + # Interpolation should be symmetric vs. bisector + alpha = 0.3 + p1 = np.array([0.5 * np.cos(alpha), 0.5 * np.sin(alpha)]) + p2 = np.array([0.5 * np.cos(np.pi/3 - alpha), 0.5 * np.sin(np.pi/3 - alpha)]) + + v1 = ip(p1) + v2 = ip(p2) + assert_allclose(v1, v2) + + # ... and affine invariant + np.random.seed(1) + A = np.random.randn(2, 2) + b = np.random.randn(2) + + points = A.dot(points.T).T + b[None,:] + p1 = A.dot(p1) + b + p2 = A.dot(p2) + b + + ip = interpnd.CloughTocher2DInterpolator(points, values) + ip.grad[...] = 0 + + w1 = ip(p1) + w2 = ip(p2) + assert_allclose(w1, v1) + assert_allclose(w2, v2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpolate.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpolate.py new file mode 100644 index 0000000000000000000000000000000000000000..e5a1dd600c16cbe1886a31d979b561438df7c13a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_interpolate.py @@ -0,0 +1,2584 @@ +from numpy.testing import (assert_, assert_equal, assert_almost_equal, + assert_array_almost_equal, assert_array_equal, + assert_allclose, suppress_warnings) +from pytest import raises as assert_raises +import pytest + +from numpy import mgrid, pi, sin, ogrid, poly1d, linspace +import numpy as np + +from scipy.interpolate import (interp1d, interp2d, lagrange, PPoly, BPoly, + splrep, splev, splantider, splint, sproot, Akima1DInterpolator, + NdPPoly, BSpline) + +from scipy.special import poch, gamma + +from scipy.interpolate import _ppoly + +from scipy._lib._gcutils import assert_deallocated, IS_PYPY + +from scipy.integrate import nquad + +from scipy.special import binom + + +class TestInterp2D: + def test_interp2d(self): + y, x = mgrid[0:2:20j, 0:pi:21j] + z = sin(x+0.5*y) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + II = interp2d(x, y, z) + assert_almost_equal(II(1.0, 2.0), sin(2.0), decimal=2) + + v, u = ogrid[0:2:24j, 0:pi:25j] + assert_almost_equal(II(u.ravel(), v.ravel()), + sin(u+0.5*v), decimal=2) + + def test_interp2d_meshgrid_input(self): + # Ticket #703 + x = linspace(0, 2, 16) + y = linspace(0, pi, 21) + z = sin(x[None, :] + y[:, None]/2.) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + II = interp2d(x, y, z) + assert_almost_equal(II(1.0, 2.0), sin(2.0), decimal=2) + + def test_interp2d_meshgrid_input_unsorted(self): + np.random.seed(1234) + x = linspace(0, 2, 16) + y = linspace(0, pi, 21) + + z = sin(x[None, :] + y[:, None] / 2.) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + ip1 = interp2d(x.copy(), y.copy(), z, kind='cubic') + + np.random.shuffle(x) + z = sin(x[None, :] + y[:, None]/2.) + ip2 = interp2d(x.copy(), y.copy(), z, kind='cubic') + + np.random.shuffle(x) + np.random.shuffle(y) + z = sin(x[None, :] + y[:, None] / 2.) + ip3 = interp2d(x, y, z, kind='cubic') + + x = linspace(0, 2, 31) + y = linspace(0, pi, 30) + + assert_equal(ip1(x, y), ip2(x, y)) + assert_equal(ip1(x, y), ip3(x, y)) + + def test_interp2d_eval_unsorted(self): + y, x = mgrid[0:2:20j, 0:pi:21j] + z = sin(x + 0.5*y) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + func = interp2d(x, y, z) + + xe = np.array([3, 4, 5]) + ye = np.array([5.3, 7.1]) + assert_allclose(func(xe, ye), func(xe, ye[::-1])) + + assert_raises(ValueError, func, xe, ye[::-1], 0, 0, True) + + def test_interp2d_linear(self): + # Ticket #898 + a = np.zeros([5, 5]) + a[2, 2] = 1.0 + x = y = np.arange(5) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + b = interp2d(x, y, a, 'linear') + assert_almost_equal(b(2.0, 1.5), np.array([0.5]), decimal=2) + assert_almost_equal(b(2.0, 2.5), np.array([0.5]), decimal=2) + + def test_interp2d_bounds(self): + x = np.linspace(0, 1, 5) + y = np.linspace(0, 2, 7) + z = x[None, :]**2 + y[:, None] + + ix = np.linspace(-1, 3, 31) + iy = np.linspace(-1, 3, 33) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning) + + b = interp2d(x, y, z, bounds_error=True) + assert_raises(ValueError, b, ix, iy) + + b = interp2d(x, y, z, fill_value=np.nan) + iz = b(ix, iy) + mx = (ix < 0) | (ix > 1) + my = (iy < 0) | (iy > 2) + assert_(np.isnan(iz[my, :]).all()) + assert_(np.isnan(iz[:, mx]).all()) + assert_(np.isfinite(iz[~my, :][:, ~mx]).all()) + + +class TestInterp1D: + + def setup_method(self): + self.x5 = np.arange(5.) + self.x10 = np.arange(10.) + self.y10 = np.arange(10.) + self.x25 = self.x10.reshape((2,5)) + self.x2 = np.arange(2.) + self.y2 = np.arange(2.) + self.x1 = np.array([0.]) + self.y1 = np.array([0.]) + + self.y210 = np.arange(20.).reshape((2, 10)) + self.y102 = np.arange(20.).reshape((10, 2)) + self.y225 = np.arange(20.).reshape((2, 2, 5)) + self.y25 = np.arange(10.).reshape((2, 5)) + self.y235 = np.arange(30.).reshape((2, 3, 5)) + self.y325 = np.arange(30.).reshape((3, 2, 5)) + + # Edge updated test matrix 1 + # array([[ 30, 1, 2, 3, 4, 5, 6, 7, 8, -30], + # [ 30, 11, 12, 13, 14, 15, 16, 17, 18, -30]]) + self.y210_edge_updated = np.arange(20.).reshape((2, 10)) + self.y210_edge_updated[:, 0] = 30 + self.y210_edge_updated[:, -1] = -30 + + # Edge updated test matrix 2 + # array([[ 30, 30], + # [ 2, 3], + # [ 4, 5], + # [ 6, 7], + # [ 8, 9], + # [ 10, 11], + # [ 12, 13], + # [ 14, 15], + # [ 16, 17], + # [-30, -30]]) + self.y102_edge_updated = np.arange(20.).reshape((10, 2)) + self.y102_edge_updated[0, :] = 30 + self.y102_edge_updated[-1, :] = -30 + + self.fill_value = -100.0 + + def test_validation(self): + # Make sure that appropriate exceptions are raised when invalid values + # are given to the constructor. + + # These should all work. + for kind in ('nearest', 'nearest-up', 'zero', 'linear', 'slinear', + 'quadratic', 'cubic', 'previous', 'next'): + interp1d(self.x10, self.y10, kind=kind) + interp1d(self.x10, self.y10, kind=kind, fill_value="extrapolate") + interp1d(self.x10, self.y10, kind='linear', fill_value=(-1, 1)) + interp1d(self.x10, self.y10, kind='linear', + fill_value=np.array([-1])) + interp1d(self.x10, self.y10, kind='linear', + fill_value=(-1,)) + interp1d(self.x10, self.y10, kind='linear', + fill_value=-1) + interp1d(self.x10, self.y10, kind='linear', + fill_value=(-1, -1)) + interp1d(self.x10, self.y10, kind=0) + interp1d(self.x10, self.y10, kind=1) + interp1d(self.x10, self.y10, kind=2) + interp1d(self.x10, self.y10, kind=3) + interp1d(self.x10, self.y210, kind='linear', axis=-1, + fill_value=(-1, -1)) + interp1d(self.x2, self.y210, kind='linear', axis=0, + fill_value=np.ones(10)) + interp1d(self.x2, self.y210, kind='linear', axis=0, + fill_value=(np.ones(10), np.ones(10))) + interp1d(self.x2, self.y210, kind='linear', axis=0, + fill_value=(np.ones(10), -1)) + + # x array must be 1D. + assert_raises(ValueError, interp1d, self.x25, self.y10) + + # y array cannot be a scalar. + assert_raises(ValueError, interp1d, self.x10, np.array(0)) + + # Check for x and y arrays having the same length. + assert_raises(ValueError, interp1d, self.x10, self.y2) + assert_raises(ValueError, interp1d, self.x2, self.y10) + assert_raises(ValueError, interp1d, self.x10, self.y102) + interp1d(self.x10, self.y210) + interp1d(self.x10, self.y102, axis=0) + + # Check for x and y having at least 1 element. + assert_raises(ValueError, interp1d, self.x1, self.y10) + assert_raises(ValueError, interp1d, self.x10, self.y1) + + # Bad fill values + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=(-1, -1, -1)) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=[-1, -1, -1]) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=np.array((-1, -1, -1))) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=[[-1]]) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=[-1, -1]) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=np.array([])) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x10, self.y10, kind='linear', + fill_value=()) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x2, self.y210, kind='linear', + axis=0, fill_value=[-1, -1]) # doesn't broadcast + assert_raises(ValueError, interp1d, self.x2, self.y210, kind='linear', + axis=0, fill_value=(0., [-1, -1])) # above doesn't bc + + def test_init(self): + # Check that the attributes are initialized appropriately by the + # constructor. + assert_(interp1d(self.x10, self.y10).copy) + assert_(not interp1d(self.x10, self.y10, copy=False).copy) + assert_(interp1d(self.x10, self.y10).bounds_error) + assert_(not interp1d(self.x10, self.y10, bounds_error=False).bounds_error) + assert_(np.isnan(interp1d(self.x10, self.y10).fill_value)) + assert_equal(interp1d(self.x10, self.y10, fill_value=3.0).fill_value, + 3.0) + assert_equal(interp1d(self.x10, self.y10, fill_value=(1.0, 2.0)).fill_value, + (1.0, 2.0)) + assert_equal(interp1d(self.x10, self.y10).axis, 0) + assert_equal(interp1d(self.x10, self.y210).axis, 1) + assert_equal(interp1d(self.x10, self.y102, axis=0).axis, 0) + assert_array_equal(interp1d(self.x10, self.y10).x, self.x10) + assert_array_equal(interp1d(self.x10, self.y10).y, self.y10) + assert_array_equal(interp1d(self.x10, self.y210).y, self.y210) + + def test_assume_sorted(self): + # Check for unsorted arrays + interp10 = interp1d(self.x10, self.y10) + interp10_unsorted = interp1d(self.x10[::-1], self.y10[::-1]) + + assert_array_almost_equal(interp10_unsorted(self.x10), self.y10) + assert_array_almost_equal(interp10_unsorted(1.2), np.array([1.2])) + assert_array_almost_equal(interp10_unsorted([2.4, 5.6, 6.0]), + interp10([2.4, 5.6, 6.0])) + + # Check assume_sorted keyword (defaults to False) + interp10_assume_kw = interp1d(self.x10[::-1], self.y10[::-1], + assume_sorted=False) + assert_array_almost_equal(interp10_assume_kw(self.x10), self.y10) + + interp10_assume_kw2 = interp1d(self.x10[::-1], self.y10[::-1], + assume_sorted=True) + # Should raise an error for unsorted input if assume_sorted=True + assert_raises(ValueError, interp10_assume_kw2, self.x10) + + # Check that if y is a 2-D array, things are still consistent + interp10_y_2d = interp1d(self.x10, self.y210) + interp10_y_2d_unsorted = interp1d(self.x10[::-1], self.y210[:, ::-1]) + assert_array_almost_equal(interp10_y_2d(self.x10), + interp10_y_2d_unsorted(self.x10)) + + def test_linear(self): + for kind in ['linear', 'slinear']: + self._check_linear(kind) + + def _check_linear(self, kind): + # Check the actual implementation of linear interpolation. + interp10 = interp1d(self.x10, self.y10, kind=kind) + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array([1.2])) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2.4, 5.6, 6.0])) + + # test fill_value="extrapolate" + extrapolator = interp1d(self.x10, self.y10, kind=kind, + fill_value='extrapolate') + assert_allclose(extrapolator([-1., 0, 9, 11]), + [-1, 0, 9, 11], rtol=1e-14) + + opts = dict(kind=kind, + fill_value='extrapolate', + bounds_error=True) + assert_raises(ValueError, interp1d, self.x10, self.y10, **opts) + + def test_linear_dtypes(self): + # regression test for gh-5898, where 1D linear interpolation has been + # delegated to numpy.interp for all float dtypes, and the latter was + # not handling e.g. np.float128. + for dtyp in [np.float16, + np.float32, + np.float64, + np.longdouble]: + x = np.arange(8, dtype=dtyp) + y = x + yp = interp1d(x, y, kind='linear')(x) + assert_equal(yp.dtype, dtyp) + assert_allclose(yp, y, atol=1e-15) + + # regression test for gh-14531, where 1D linear interpolation has been + # has been extended to delegate to numpy.interp for integer dtypes + x = [0, 1, 2] + y = [np.nan, 0, 1] + yp = interp1d(x, y)(x) + assert_allclose(yp, y, atol=1e-15) + + def test_slinear_dtypes(self): + # regression test for gh-7273: 1D slinear interpolation fails with + # float32 inputs + dt_r = [np.float16, np.float32, np.float64] + dt_rc = dt_r + [np.complex64, np.complex128] + spline_kinds = ['slinear', 'zero', 'quadratic', 'cubic'] + for dtx in dt_r: + x = np.arange(0, 10, dtype=dtx) + for dty in dt_rc: + y = np.exp(-x/3.0).astype(dty) + for dtn in dt_r: + xnew = x.astype(dtn) + for kind in spline_kinds: + f = interp1d(x, y, kind=kind, bounds_error=False) + assert_allclose(f(xnew), y, atol=1e-7, + err_msg=f"{dtx}, {dty} {dtn}") + + def test_cubic(self): + # Check the actual implementation of spline interpolation. + interp10 = interp1d(self.x10, self.y10, kind='cubic') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array([1.2])) + assert_array_almost_equal(interp10(1.5), np.array([1.5])) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2.4, 5.6, 6.0]),) + + def test_nearest(self): + # Check the actual implementation of nearest-neighbour interpolation. + # Nearest asserts that half-integer case (1.5) rounds down to 1 + interp10 = interp1d(self.x10, self.y10, kind='nearest') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array(1.)) + assert_array_almost_equal(interp10(1.5), np.array(1.)) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2., 6., 6.]),) + + # test fill_value="extrapolate" + extrapolator = interp1d(self.x10, self.y10, kind='nearest', + fill_value='extrapolate') + assert_allclose(extrapolator([-1., 0, 9, 11]), + [0, 0, 9, 9], rtol=1e-14) + + opts = dict(kind='nearest', + fill_value='extrapolate', + bounds_error=True) + assert_raises(ValueError, interp1d, self.x10, self.y10, **opts) + + def test_nearest_up(self): + # Check the actual implementation of nearest-neighbour interpolation. + # Nearest-up asserts that half-integer case (1.5) rounds up to 2 + interp10 = interp1d(self.x10, self.y10, kind='nearest-up') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array(1.)) + assert_array_almost_equal(interp10(1.5), np.array(2.)) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2., 6., 6.]),) + + # test fill_value="extrapolate" + extrapolator = interp1d(self.x10, self.y10, kind='nearest-up', + fill_value='extrapolate') + assert_allclose(extrapolator([-1., 0, 9, 11]), + [0, 0, 9, 9], rtol=1e-14) + + opts = dict(kind='nearest-up', + fill_value='extrapolate', + bounds_error=True) + assert_raises(ValueError, interp1d, self.x10, self.y10, **opts) + + def test_previous(self): + # Check the actual implementation of previous interpolation. + interp10 = interp1d(self.x10, self.y10, kind='previous') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array(1.)) + assert_array_almost_equal(interp10(1.5), np.array(1.)) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2., 5., 6.]),) + + # test fill_value="extrapolate" + extrapolator = interp1d(self.x10, self.y10, kind='previous', + fill_value='extrapolate') + assert_allclose(extrapolator([-1., 0, 9, 11]), + [np.nan, 0, 9, 9], rtol=1e-14) + + # Tests for gh-9591 + interpolator1D = interp1d(self.x10, self.y10, kind="previous", + fill_value='extrapolate') + assert_allclose(interpolator1D([-1, -2, 5, 8, 12, 25]), + [np.nan, np.nan, 5, 8, 9, 9]) + + interpolator2D = interp1d(self.x10, self.y210, kind="previous", + fill_value='extrapolate') + assert_allclose(interpolator2D([-1, -2, 5, 8, 12, 25]), + [[np.nan, np.nan, 5, 8, 9, 9], + [np.nan, np.nan, 15, 18, 19, 19]]) + + interpolator2DAxis0 = interp1d(self.x10, self.y102, kind="previous", + axis=0, fill_value='extrapolate') + assert_allclose(interpolator2DAxis0([-2, 5, 12]), + [[np.nan, np.nan], + [10, 11], + [18, 19]]) + + opts = dict(kind='previous', + fill_value='extrapolate', + bounds_error=True) + assert_raises(ValueError, interp1d, self.x10, self.y10, **opts) + + # Tests for gh-16813 + interpolator1D = interp1d([0, 1, 2], + [0, 1, -1], kind="previous", + fill_value='extrapolate', + assume_sorted=True) + assert_allclose(interpolator1D([-2, -1, 0, 1, 2, 3, 5]), + [np.nan, np.nan, 0, 1, -1, -1, -1]) + + interpolator1D = interp1d([2, 0, 1], # x is not ascending + [-1, 0, 1], kind="previous", + fill_value='extrapolate', + assume_sorted=False) + assert_allclose(interpolator1D([-2, -1, 0, 1, 2, 3, 5]), + [np.nan, np.nan, 0, 1, -1, -1, -1]) + + interpolator2D = interp1d(self.x10, self.y210_edge_updated, + kind="previous", + fill_value='extrapolate') + assert_allclose(interpolator2D([-1, -2, 5, 8, 12, 25]), + [[np.nan, np.nan, 5, 8, -30, -30], + [np.nan, np.nan, 15, 18, -30, -30]]) + + interpolator2DAxis0 = interp1d(self.x10, self.y102_edge_updated, + kind="previous", + axis=0, fill_value='extrapolate') + assert_allclose(interpolator2DAxis0([-2, 5, 12]), + [[np.nan, np.nan], + [10, 11], + [-30, -30]]) + + def test_next(self): + # Check the actual implementation of next interpolation. + interp10 = interp1d(self.x10, self.y10, kind='next') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array(2.)) + assert_array_almost_equal(interp10(1.5), np.array(2.)) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([3., 6., 6.]),) + + # test fill_value="extrapolate" + extrapolator = interp1d(self.x10, self.y10, kind='next', + fill_value='extrapolate') + assert_allclose(extrapolator([-1., 0, 9, 11]), + [0, 0, 9, np.nan], rtol=1e-14) + + # Tests for gh-9591 + interpolator1D = interp1d(self.x10, self.y10, kind="next", + fill_value='extrapolate') + assert_allclose(interpolator1D([-1, -2, 5, 8, 12, 25]), + [0, 0, 5, 8, np.nan, np.nan]) + + interpolator2D = interp1d(self.x10, self.y210, kind="next", + fill_value='extrapolate') + assert_allclose(interpolator2D([-1, -2, 5, 8, 12, 25]), + [[0, 0, 5, 8, np.nan, np.nan], + [10, 10, 15, 18, np.nan, np.nan]]) + + interpolator2DAxis0 = interp1d(self.x10, self.y102, kind="next", + axis=0, fill_value='extrapolate') + assert_allclose(interpolator2DAxis0([-2, 5, 12]), + [[0, 1], + [10, 11], + [np.nan, np.nan]]) + + opts = dict(kind='next', + fill_value='extrapolate', + bounds_error=True) + assert_raises(ValueError, interp1d, self.x10, self.y10, **opts) + + # Tests for gh-16813 + interpolator1D = interp1d([0, 1, 2], + [0, 1, -1], kind="next", + fill_value='extrapolate', + assume_sorted=True) + assert_allclose(interpolator1D([-2, -1, 0, 1, 2, 3, 5]), + [0, 0, 0, 1, -1, np.nan, np.nan]) + + interpolator1D = interp1d([2, 0, 1], # x is not ascending + [-1, 0, 1], kind="next", + fill_value='extrapolate', + assume_sorted=False) + assert_allclose(interpolator1D([-2, -1, 0, 1, 2, 3, 5]), + [0, 0, 0, 1, -1, np.nan, np.nan]) + + interpolator2D = interp1d(self.x10, self.y210_edge_updated, + kind="next", + fill_value='extrapolate') + assert_allclose(interpolator2D([-1, -2, 5, 8, 12, 25]), + [[30, 30, 5, 8, np.nan, np.nan], + [30, 30, 15, 18, np.nan, np.nan]]) + + interpolator2DAxis0 = interp1d(self.x10, self.y102_edge_updated, + kind="next", + axis=0, fill_value='extrapolate') + assert_allclose(interpolator2DAxis0([-2, 5, 12]), + [[30, 30], + [10, 11], + [np.nan, np.nan]]) + + def test_zero(self): + # Check the actual implementation of zero-order spline interpolation. + interp10 = interp1d(self.x10, self.y10, kind='zero') + assert_array_almost_equal(interp10(self.x10), self.y10) + assert_array_almost_equal(interp10(1.2), np.array(1.)) + assert_array_almost_equal(interp10(1.5), np.array(1.)) + assert_array_almost_equal(interp10([2.4, 5.6, 6.0]), + np.array([2., 5., 6.])) + + def bounds_check_helper(self, interpolant, test_array, fail_value): + # Asserts that a ValueError is raised and that the error message + # contains the value causing this exception. + assert_raises(ValueError, interpolant, test_array) + try: + interpolant(test_array) + except ValueError as err: + assert (f"{fail_value}" in str(err)) + + def _bounds_check(self, kind='linear'): + # Test that our handling of out-of-bounds input is correct. + extrap10 = interp1d(self.x10, self.y10, fill_value=self.fill_value, + bounds_error=False, kind=kind) + + assert_array_equal(extrap10(11.2), np.array(self.fill_value)) + assert_array_equal(extrap10(-3.4), np.array(self.fill_value)) + assert_array_equal(extrap10([[[11.2], [-3.4], [12.6], [19.3]]]), + np.array(self.fill_value),) + assert_array_equal(extrap10._check_bounds( + np.array([-1.0, 0.0, 5.0, 9.0, 11.0])), + np.array([[True, False, False, False, False], + [False, False, False, False, True]])) + + raises_bounds_error = interp1d(self.x10, self.y10, bounds_error=True, + kind=kind) + + self.bounds_check_helper(raises_bounds_error, -1.0, -1.0) + self.bounds_check_helper(raises_bounds_error, 11.0, 11.0) + self.bounds_check_helper(raises_bounds_error, [0.0, -1.0, 0.0], -1.0) + self.bounds_check_helper(raises_bounds_error, [0.0, 1.0, 21.0], 21.0) + + raises_bounds_error([0.0, 5.0, 9.0]) + + def _bounds_check_int_nan_fill(self, kind='linear'): + x = np.arange(10).astype(int) + y = np.arange(10).astype(int) + c = interp1d(x, y, kind=kind, fill_value=np.nan, bounds_error=False) + yi = c(x - 1) + assert_(np.isnan(yi[0])) + assert_array_almost_equal(yi, np.r_[np.nan, y[:-1]]) + + def test_bounds(self): + for kind in ('linear', 'cubic', 'nearest', 'previous', 'next', + 'slinear', 'zero', 'quadratic'): + self._bounds_check(kind) + self._bounds_check_int_nan_fill(kind) + + def _check_fill_value(self, kind): + interp = interp1d(self.x10, self.y10, kind=kind, + fill_value=(-100, 100), bounds_error=False) + assert_array_almost_equal(interp(10), 100) + assert_array_almost_equal(interp(-10), -100) + assert_array_almost_equal(interp([-10, 10]), [-100, 100]) + + # Proper broadcasting: + # interp along axis of length 5 + # other dim=(2, 3), (3, 2), (2, 2), or (2,) + + # one singleton fill_value (works for all) + for y in (self.y235, self.y325, self.y225, self.y25): + interp = interp1d(self.x5, y, kind=kind, axis=-1, + fill_value=100, bounds_error=False) + assert_array_almost_equal(interp(10), 100) + assert_array_almost_equal(interp(-10), 100) + assert_array_almost_equal(interp([-10, 10]), 100) + + # singleton lower, singleton upper + interp = interp1d(self.x5, y, kind=kind, axis=-1, + fill_value=(-100, 100), bounds_error=False) + assert_array_almost_equal(interp(10), 100) + assert_array_almost_equal(interp(-10), -100) + if y.ndim == 3: + result = [[[-100, 100]] * y.shape[1]] * y.shape[0] + else: + result = [[-100, 100]] * y.shape[0] + assert_array_almost_equal(interp([-10, 10]), result) + + # one broadcastable (3,) fill_value + fill_value = [100, 200, 300] + for y in (self.y325, self.y225): + assert_raises(ValueError, interp1d, self.x5, y, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + interp = interp1d(self.x5, self.y235, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), [[100, 200, 300]] * 2) + assert_array_almost_equal(interp(-10), [[100, 200, 300]] * 2) + assert_array_almost_equal(interp([-10, 10]), [[[100, 100], + [200, 200], + [300, 300]]] * 2) + + # one broadcastable (2,) fill_value + fill_value = [100, 200] + assert_raises(ValueError, interp1d, self.x5, self.y235, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for y in (self.y225, self.y325, self.y25): + interp = interp1d(self.x5, y, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + result = [100, 200] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp(10), result) + assert_array_almost_equal(interp(-10), result) + result = [[100, 100], [200, 200]] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp([-10, 10]), result) + + # broadcastable (3,) lower, singleton upper + fill_value = (np.array([-100, -200, -300]), 100) + for y in (self.y325, self.y225): + assert_raises(ValueError, interp1d, self.x5, y, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + interp = interp1d(self.x5, self.y235, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), 100) + assert_array_almost_equal(interp(-10), [[-100, -200, -300]] * 2) + assert_array_almost_equal(interp([-10, 10]), [[[-100, 100], + [-200, 100], + [-300, 100]]] * 2) + + # broadcastable (2,) lower, singleton upper + fill_value = (np.array([-100, -200]), 100) + assert_raises(ValueError, interp1d, self.x5, self.y235, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for y in (self.y225, self.y325, self.y25): + interp = interp1d(self.x5, y, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), 100) + result = [-100, -200] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp(-10), result) + result = [[-100, 100], [-200, 100]] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp([-10, 10]), result) + + # broadcastable (3,) lower, broadcastable (3,) upper + fill_value = ([-100, -200, -300], [100, 200, 300]) + for y in (self.y325, self.y225): + assert_raises(ValueError, interp1d, self.x5, y, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for ii in range(2): # check ndarray as well as list here + if ii == 1: + fill_value = tuple(np.array(f) for f in fill_value) + interp = interp1d(self.x5, self.y235, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), [[100, 200, 300]] * 2) + assert_array_almost_equal(interp(-10), [[-100, -200, -300]] * 2) + assert_array_almost_equal(interp([-10, 10]), [[[-100, 100], + [-200, 200], + [-300, 300]]] * 2) + # broadcastable (2,) lower, broadcastable (2,) upper + fill_value = ([-100, -200], [100, 200]) + assert_raises(ValueError, interp1d, self.x5, self.y235, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for y in (self.y325, self.y225, self.y25): + interp = interp1d(self.x5, y, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + result = [100, 200] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp(10), result) + result = [-100, -200] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp(-10), result) + result = [[-100, 100], [-200, 200]] + if y.ndim == 3: + result = [result] * y.shape[0] + assert_array_almost_equal(interp([-10, 10]), result) + + # one broadcastable (2, 2) array-like + fill_value = [[100, 200], [1000, 2000]] + for y in (self.y235, self.y325, self.y25): + assert_raises(ValueError, interp1d, self.x5, y, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for ii in range(2): + if ii == 1: + fill_value = np.array(fill_value) + interp = interp1d(self.x5, self.y225, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), [[100, 200], [1000, 2000]]) + assert_array_almost_equal(interp(-10), [[100, 200], [1000, 2000]]) + assert_array_almost_equal(interp([-10, 10]), [[[100, 100], + [200, 200]], + [[1000, 1000], + [2000, 2000]]]) + + # broadcastable (2, 2) lower, broadcastable (2, 2) upper + fill_value = ([[-100, -200], [-1000, -2000]], + [[100, 200], [1000, 2000]]) + for y in (self.y235, self.y325, self.y25): + assert_raises(ValueError, interp1d, self.x5, y, kind=kind, + axis=-1, fill_value=fill_value, bounds_error=False) + for ii in range(2): + if ii == 1: + fill_value = (np.array(fill_value[0]), np.array(fill_value[1])) + interp = interp1d(self.x5, self.y225, kind=kind, axis=-1, + fill_value=fill_value, bounds_error=False) + assert_array_almost_equal(interp(10), [[100, 200], [1000, 2000]]) + assert_array_almost_equal(interp(-10), [[-100, -200], + [-1000, -2000]]) + assert_array_almost_equal(interp([-10, 10]), [[[-100, 100], + [-200, 200]], + [[-1000, 1000], + [-2000, 2000]]]) + + def test_fill_value(self): + # test that two-element fill value works + for kind in ('linear', 'nearest', 'cubic', 'slinear', 'quadratic', + 'zero', 'previous', 'next'): + self._check_fill_value(kind) + + def test_fill_value_writeable(self): + # backwards compat: fill_value is a public writeable attribute + interp = interp1d(self.x10, self.y10, fill_value=123.0) + assert_equal(interp.fill_value, 123.0) + interp.fill_value = 321.0 + assert_equal(interp.fill_value, 321.0) + + def _nd_check_interp(self, kind='linear'): + # Check the behavior when the inputs and outputs are multidimensional. + + # Multidimensional input. + interp10 = interp1d(self.x10, self.y10, kind=kind) + assert_array_almost_equal(interp10(np.array([[3., 5.], [2., 7.]])), + np.array([[3., 5.], [2., 7.]])) + + # Scalar input -> 0-dim scalar array output + assert_(isinstance(interp10(1.2), np.ndarray)) + assert_equal(interp10(1.2).shape, ()) + + # Multidimensional outputs. + interp210 = interp1d(self.x10, self.y210, kind=kind) + assert_array_almost_equal(interp210(1.), np.array([1., 11.])) + assert_array_almost_equal(interp210(np.array([1., 2.])), + np.array([[1., 2.], [11., 12.]])) + + interp102 = interp1d(self.x10, self.y102, axis=0, kind=kind) + assert_array_almost_equal(interp102(1.), np.array([2.0, 3.0])) + assert_array_almost_equal(interp102(np.array([1., 3.])), + np.array([[2., 3.], [6., 7.]])) + + # Both at the same time! + x_new = np.array([[3., 5.], [2., 7.]]) + assert_array_almost_equal(interp210(x_new), + np.array([[[3., 5.], [2., 7.]], + [[13., 15.], [12., 17.]]])) + assert_array_almost_equal(interp102(x_new), + np.array([[[6., 7.], [10., 11.]], + [[4., 5.], [14., 15.]]])) + + def _nd_check_shape(self, kind='linear'): + # Check large N-D output shape + a = [4, 5, 6, 7] + y = np.arange(np.prod(a)).reshape(*a) + for n, s in enumerate(a): + x = np.arange(s) + z = interp1d(x, y, axis=n, kind=kind) + assert_array_almost_equal(z(x), y, err_msg=kind) + + x2 = np.arange(2*3*1).reshape((2,3,1)) / 12. + b = list(a) + b[n:n+1] = [2,3,1] + assert_array_almost_equal(z(x2).shape, b, err_msg=kind) + + def test_nd(self): + for kind in ('linear', 'cubic', 'slinear', 'quadratic', 'nearest', + 'zero', 'previous', 'next'): + self._nd_check_interp(kind) + self._nd_check_shape(kind) + + def _check_complex(self, dtype=np.complex128, kind='linear'): + x = np.array([1, 2.5, 3, 3.1, 4, 6.4, 7.9, 8.0, 9.5, 10]) + y = x * x ** (1 + 2j) + y = y.astype(dtype) + + # simple test + c = interp1d(x, y, kind=kind) + assert_array_almost_equal(y[:-1], c(x)[:-1]) + + # check against interpolating real+imag separately + xi = np.linspace(1, 10, 31) + cr = interp1d(x, y.real, kind=kind) + ci = interp1d(x, y.imag, kind=kind) + assert_array_almost_equal(c(xi).real, cr(xi)) + assert_array_almost_equal(c(xi).imag, ci(xi)) + + def test_complex(self): + for kind in ('linear', 'nearest', 'cubic', 'slinear', 'quadratic', + 'zero', 'previous', 'next'): + self._check_complex(np.complex64, kind) + self._check_complex(np.complex128, kind) + + @pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy") + def test_circular_refs(self): + # Test interp1d can be automatically garbage collected + x = np.linspace(0, 1) + y = np.linspace(0, 1) + # Confirm interp can be released from memory after use + with assert_deallocated(interp1d, x, y) as interp: + interp([0.1, 0.2]) + del interp + + def test_overflow_nearest(self): + # Test that the x range doesn't overflow when given integers as input + for kind in ('nearest', 'previous', 'next'): + x = np.array([0, 50, 127], dtype=np.int8) + ii = interp1d(x, x, kind=kind) + assert_array_almost_equal(ii(x), x) + + def test_local_nans(self): + # check that for local interpolation kinds (slinear, zero) a single nan + # only affects its local neighborhood + x = np.arange(10).astype(float) + y = x.copy() + y[6] = np.nan + for kind in ('zero', 'slinear'): + ir = interp1d(x, y, kind=kind) + vals = ir([4.9, 7.0]) + assert_(np.isfinite(vals).all()) + + def test_spline_nans(self): + # Backwards compat: a single nan makes the whole spline interpolation + # return nans in an array of the correct shape. And it doesn't raise, + # just quiet nans because of backcompat. + x = np.arange(8).astype(float) + y = x.copy() + yn = y.copy() + yn[3] = np.nan + + for kind in ['quadratic', 'cubic']: + ir = interp1d(x, y, kind=kind) + irn = interp1d(x, yn, kind=kind) + for xnew in (6, [1, 6], [[1, 6], [3, 5]]): + xnew = np.asarray(xnew) + out, outn = ir(x), irn(x) + assert_(np.isnan(outn).all()) + assert_equal(out.shape, outn.shape) + + def test_all_nans(self): + # regression test for gh-11637: interp1d core dumps with all-nan `x` + x = np.ones(10) * np.nan + y = np.arange(10) + with assert_raises(ValueError): + interp1d(x, y, kind='cubic') + + def test_read_only(self): + x = np.arange(0, 10) + y = np.exp(-x / 3.0) + xnew = np.arange(0, 9, 0.1) + # Check both read-only and not read-only: + for xnew_writeable in (True, False): + xnew.flags.writeable = xnew_writeable + x.flags.writeable = False + for kind in ('linear', 'nearest', 'zero', 'slinear', 'quadratic', + 'cubic'): + f = interp1d(x, y, kind=kind) + vals = f(xnew) + assert_(np.isfinite(vals).all()) + + @pytest.mark.parametrize( + "kind", ("linear", "nearest", "nearest-up", "previous", "next") + ) + def test_single_value(self, kind): + # https://github.com/scipy/scipy/issues/4043 + f = interp1d([1.5], [6], kind=kind, bounds_error=False, + fill_value=(2, 10)) + assert_array_equal(f([1, 1.5, 2]), [2, 6, 10]) + # check still error if bounds_error=True + f = interp1d([1.5], [6], kind=kind, bounds_error=True) + with assert_raises(ValueError, match="x_new is above"): + f(2.0) + + +class TestLagrange: + + def test_lagrange(self): + p = poly1d([5,2,1,4,3]) + xs = np.arange(len(p.coeffs)) + ys = p(xs) + pl = lagrange(xs,ys) + assert_array_almost_equal(p.coeffs,pl.coeffs) + + +class TestAkima1DInterpolator: + def test_eval(self): + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + ak = Akima1DInterpolator(x, y) + xi = np.array([0., 0.5, 1., 1.5, 2.5, 3.5, 4.5, 5.1, 6.5, 7.2, + 8.6, 9.9, 10.]) + yi = np.array([0., 1.375, 2., 1.5, 1.953125, 2.484375, + 4.1363636363636366866103344, 5.9803623910336236590978842, + 5.5067291516462386624652936, 5.2031367459745245795943447, + 4.1796554159017080820603951, 3.4110386597938129327189927, + 3.]) + assert_allclose(ak(xi), yi) + + def test_eval_mod(self): + # Reference values generated with the following MATLAB code: + # format longG + # x = 0:10; y = [0. 2. 1. 3. 2. 6. 5.5 5.5 2.7 5.1 3.]; + # xi = [0. 0.5 1. 1.5 2.5 3.5 4.5 5.1 6.5 7.2 8.6 9.9 10.]; + # makima(x, y, xi) + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + ak = Akima1DInterpolator(x, y, method="makima") + xi = np.array([0., 0.5, 1., 1.5, 2.5, 3.5, 4.5, 5.1, 6.5, 7.2, + 8.6, 9.9, 10.]) + yi = np.array([ + 0.0, 1.34471153846154, 2.0, 1.44375, 1.94375, 2.51939102564103, + 4.10366931918656, 5.98501550899192, 5.51756330960439, 5.1757231914014, + 4.12326636931311, 3.32931513157895, 3.0]) + assert_allclose(ak(xi), yi) + + def test_eval_2d(self): + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + y = np.column_stack((y, 2. * y)) + ak = Akima1DInterpolator(x, y) + xi = np.array([0., 0.5, 1., 1.5, 2.5, 3.5, 4.5, 5.1, 6.5, 7.2, + 8.6, 9.9, 10.]) + yi = np.array([0., 1.375, 2., 1.5, 1.953125, 2.484375, + 4.1363636363636366866103344, + 5.9803623910336236590978842, + 5.5067291516462386624652936, + 5.2031367459745245795943447, + 4.1796554159017080820603951, + 3.4110386597938129327189927, 3.]) + yi = np.column_stack((yi, 2. * yi)) + assert_allclose(ak(xi), yi) + + def test_eval_3d(self): + x = np.arange(0., 11.) + y_ = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + y = np.empty((11, 2, 2)) + y[:, 0, 0] = y_ + y[:, 1, 0] = 2. * y_ + y[:, 0, 1] = 3. * y_ + y[:, 1, 1] = 4. * y_ + ak = Akima1DInterpolator(x, y) + xi = np.array([0., 0.5, 1., 1.5, 2.5, 3.5, 4.5, 5.1, 6.5, 7.2, + 8.6, 9.9, 10.]) + yi = np.empty((13, 2, 2)) + yi_ = np.array([0., 1.375, 2., 1.5, 1.953125, 2.484375, + 4.1363636363636366866103344, + 5.9803623910336236590978842, + 5.5067291516462386624652936, + 5.2031367459745245795943447, + 4.1796554159017080820603951, + 3.4110386597938129327189927, 3.]) + yi[:, 0, 0] = yi_ + yi[:, 1, 0] = 2. * yi_ + yi[:, 0, 1] = 3. * yi_ + yi[:, 1, 1] = 4. * yi_ + assert_allclose(ak(xi), yi) + + def test_degenerate_case_multidimensional(self): + # This test is for issue #5683. + x = np.array([0, 1, 2]) + y = np.vstack((x, x**2)).T + ak = Akima1DInterpolator(x, y) + x_eval = np.array([0.5, 1.5]) + y_eval = ak(x_eval) + assert_allclose(y_eval, np.vstack((x_eval, x_eval**2)).T) + + def test_extend(self): + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + ak = Akima1DInterpolator(x, y) + match = "Extending a 1-D Akima interpolator is not yet implemented" + with pytest.raises(NotImplementedError, match=match): + ak.extend(None, None) + + def test_mod_invalid_method(self): + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + match = "`method`=invalid is unsupported." + with pytest.raises(NotImplementedError, match=match): + Akima1DInterpolator(x, y, method="invalid") # type: ignore + + def test_complex(self): + # Complex-valued data deprecated + x = np.arange(0., 11.) + y = np.array([0., 2., 1., 3., 2., 6., 5.5, 5.5, 2.7, 5.1, 3.]) + y = y - 2j*y + # actually raises ComplexWarning, which subclasses RuntimeWarning, see + # https://github.com/numpy/numpy/blob/main/numpy/exceptions.py + msg = "Passing an array with a complex.*|Casting complex values to real.*" + with pytest.warns((RuntimeWarning, DeprecationWarning), match=msg): + Akima1DInterpolator(x, y) + + +class TestPPolyCommon: + # test basic functionality for PPoly and BPoly + def test_sort_check(self): + c = np.array([[1, 4], [2, 5], [3, 6]]) + x = np.array([0, 1, 0.5]) + assert_raises(ValueError, PPoly, c, x) + assert_raises(ValueError, BPoly, c, x) + + def test_ctor_c(self): + # wrong shape: `c` must be at least 2D + with assert_raises(ValueError): + PPoly([1, 2], [0, 1]) + + def test_extend(self): + # Test adding new points to the piecewise polynomial + np.random.seed(1234) + + order = 3 + x = np.unique(np.r_[0, 10 * np.random.rand(30), 10]) + c = 2*np.random.rand(order+1, len(x)-1, 2, 3) - 1 + + for cls in (PPoly, BPoly): + pp = cls(c[:,:9], x[:10]) + pp.extend(c[:,9:], x[10:]) + + pp2 = cls(c[:, 10:], x[10:]) + pp2.extend(c[:, :10], x[:10]) + + pp3 = cls(c, x) + + assert_array_equal(pp.c, pp3.c) + assert_array_equal(pp.x, pp3.x) + assert_array_equal(pp2.c, pp3.c) + assert_array_equal(pp2.x, pp3.x) + + def test_extend_diff_orders(self): + # Test extending polynomial with different order one + np.random.seed(1234) + + x = np.linspace(0, 1, 6) + c = np.random.rand(2, 5) + + x2 = np.linspace(1, 2, 6) + c2 = np.random.rand(4, 5) + + for cls in (PPoly, BPoly): + pp1 = cls(c, x) + pp2 = cls(c2, x2) + + pp_comb = cls(c, x) + pp_comb.extend(c2, x2[1:]) + + # NB. doesn't match to pp1 at the endpoint, because pp1 is not + # continuous with pp2 as we took random coefs. + xi1 = np.linspace(0, 1, 300, endpoint=False) + xi2 = np.linspace(1, 2, 300) + + assert_allclose(pp1(xi1), pp_comb(xi1)) + assert_allclose(pp2(xi2), pp_comb(xi2)) + + def test_extend_descending(self): + np.random.seed(0) + + order = 3 + x = np.sort(np.random.uniform(0, 10, 20)) + c = np.random.rand(order + 1, x.shape[0] - 1, 2, 3) + + for cls in (PPoly, BPoly): + p = cls(c, x) + + p1 = cls(c[:, :9], x[:10]) + p1.extend(c[:, 9:], x[10:]) + + p2 = cls(c[:, 10:], x[10:]) + p2.extend(c[:, :10], x[:10]) + + assert_array_equal(p1.c, p.c) + assert_array_equal(p1.x, p.x) + assert_array_equal(p2.c, p.c) + assert_array_equal(p2.x, p.x) + + def test_shape(self): + np.random.seed(1234) + c = np.random.rand(8, 12, 5, 6, 7) + x = np.sort(np.random.rand(13)) + xp = np.random.rand(3, 4) + for cls in (PPoly, BPoly): + p = cls(c, x) + assert_equal(p(xp).shape, (3, 4, 5, 6, 7)) + + # 'scalars' + for cls in (PPoly, BPoly): + p = cls(c[..., 0, 0, 0], x) + + assert_equal(np.shape(p(0.5)), ()) + assert_equal(np.shape(p(np.array(0.5))), ()) + + assert_raises(ValueError, p, np.array([[0.1, 0.2], [0.4]], dtype=object)) + + def test_complex_coef(self): + np.random.seed(12345) + x = np.sort(np.random.random(13)) + c = np.random.random((8, 12)) * (1. + 0.3j) + c_re, c_im = c.real, c.imag + xp = np.random.random(5) + for cls in (PPoly, BPoly): + p, p_re, p_im = cls(c, x), cls(c_re, x), cls(c_im, x) + for nu in [0, 1, 2]: + assert_allclose(p(xp, nu).real, p_re(xp, nu)) + assert_allclose(p(xp, nu).imag, p_im(xp, nu)) + + def test_axis(self): + np.random.seed(12345) + c = np.random.rand(3, 4, 5, 6, 7, 8) + c_s = c.shape + xp = np.random.random((1, 2)) + for axis in (0, 1, 2, 3): + m = c.shape[axis+1] + x = np.sort(np.random.rand(m+1)) + for cls in (PPoly, BPoly): + p = cls(c, x, axis=axis) + assert_equal(p.c.shape, + c_s[axis:axis+2] + c_s[:axis] + c_s[axis+2:]) + res = p(xp) + targ_shape = c_s[:axis] + xp.shape + c_s[2+axis:] + assert_equal(res.shape, targ_shape) + + # deriv/antideriv does not drop the axis + for p1 in [cls(c, x, axis=axis).derivative(), + cls(c, x, axis=axis).derivative(2), + cls(c, x, axis=axis).antiderivative(), + cls(c, x, axis=axis).antiderivative(2)]: + assert_equal(p1.axis, p.axis) + + # c array needs two axes for the coefficients and intervals, so + # 0 <= axis < c.ndim-1; raise otherwise + for axis in (-1, 4, 5, 6): + for cls in (BPoly, PPoly): + assert_raises(ValueError, cls, **dict(c=c, x=x, axis=axis)) + + +class TestPolySubclassing: + class P(PPoly): + pass + + class B(BPoly): + pass + + def _make_polynomials(self): + np.random.seed(1234) + x = np.sort(np.random.random(3)) + c = np.random.random((4, 2)) + return self.P(c, x), self.B(c, x) + + def test_derivative(self): + pp, bp = self._make_polynomials() + for p in (pp, bp): + pd = p.derivative() + assert_equal(p.__class__, pd.__class__) + + ppa = pp.antiderivative() + assert_equal(pp.__class__, ppa.__class__) + + def test_from_spline(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0) + pp = self.P.from_spline(spl) + assert_equal(pp.__class__, self.P) + + def test_conversions(self): + pp, bp = self._make_polynomials() + + pp1 = self.P.from_bernstein_basis(bp) + assert_equal(pp1.__class__, self.P) + + bp1 = self.B.from_power_basis(pp) + assert_equal(bp1.__class__, self.B) + + def test_from_derivatives(self): + x = [0, 1, 2] + y = [[1], [2], [3]] + bp = self.B.from_derivatives(x, y) + assert_equal(bp.__class__, self.B) + + +class TestPPoly: + def test_simple(self): + c = np.array([[1, 4], [2, 5], [3, 6]]) + x = np.array([0, 0.5, 1]) + p = PPoly(c, x) + assert_allclose(p(0.3), 1*0.3**2 + 2*0.3 + 3) + assert_allclose(p(0.7), 4*(0.7-0.5)**2 + 5*(0.7-0.5) + 6) + + def test_periodic(self): + c = np.array([[1, 4], [2, 5], [3, 6]]) + x = np.array([0, 0.5, 1]) + p = PPoly(c, x, extrapolate='periodic') + + assert_allclose(p(1.3), 1 * 0.3 ** 2 + 2 * 0.3 + 3) + assert_allclose(p(-0.3), 4 * (0.7 - 0.5) ** 2 + 5 * (0.7 - 0.5) + 6) + + assert_allclose(p(1.3, 1), 2 * 0.3 + 2) + assert_allclose(p(-0.3, 1), 8 * (0.7 - 0.5) + 5) + + def test_read_only(self): + c = np.array([[1, 4], [2, 5], [3, 6]]) + x = np.array([0, 0.5, 1]) + xnew = np.array([0, 0.1, 0.2]) + PPoly(c, x, extrapolate='periodic') + + for writeable in (True, False): + x.flags.writeable = writeable + c.flags.writeable = writeable + f = PPoly(c, x) + vals = f(xnew) + assert_(np.isfinite(vals).all()) + + def test_descending(self): + def binom_matrix(power): + n = np.arange(power + 1).reshape(-1, 1) + k = np.arange(power + 1) + B = binom(n, k) + return B[::-1, ::-1] + + np.random.seed(0) + + power = 3 + for m in [10, 20, 30]: + x = np.sort(np.random.uniform(0, 10, m + 1)) + ca = np.random.uniform(-2, 2, size=(power + 1, m)) + + h = np.diff(x) + h_powers = h[None, :] ** np.arange(power + 1)[::-1, None] + B = binom_matrix(power) + cap = ca * h_powers + cdp = np.dot(B.T, cap) + cd = cdp / h_powers + + pa = PPoly(ca, x, extrapolate=True) + pd = PPoly(cd[:, ::-1], x[::-1], extrapolate=True) + + x_test = np.random.uniform(-10, 20, 100) + assert_allclose(pa(x_test), pd(x_test), rtol=1e-13) + assert_allclose(pa(x_test, 1), pd(x_test, 1), rtol=1e-13) + + pa_d = pa.derivative() + pd_d = pd.derivative() + + assert_allclose(pa_d(x_test), pd_d(x_test), rtol=1e-13) + + # Antiderivatives won't be equal because fixing continuity is + # done in the reverse order, but surely the differences should be + # equal. + pa_i = pa.antiderivative() + pd_i = pd.antiderivative() + for a, b in np.random.uniform(-10, 20, (5, 2)): + int_a = pa.integrate(a, b) + int_d = pd.integrate(a, b) + assert_allclose(int_a, int_d, rtol=1e-13) + assert_allclose(pa_i(b) - pa_i(a), pd_i(b) - pd_i(a), + rtol=1e-13) + + roots_d = pd.roots() + roots_a = pa.roots() + assert_allclose(roots_a, np.sort(roots_d), rtol=1e-12) + + def test_multi_shape(self): + c = np.random.rand(6, 2, 1, 2, 3) + x = np.array([0, 0.5, 1]) + p = PPoly(c, x) + assert_equal(p.x.shape, x.shape) + assert_equal(p.c.shape, c.shape) + assert_equal(p(0.3).shape, c.shape[2:]) + + assert_equal(p(np.random.rand(5, 6)).shape, (5, 6) + c.shape[2:]) + + dp = p.derivative() + assert_equal(dp.c.shape, (5, 2, 1, 2, 3)) + ip = p.antiderivative() + assert_equal(ip.c.shape, (7, 2, 1, 2, 3)) + + def test_construct_fast(self): + np.random.seed(1234) + c = np.array([[1, 4], [2, 5], [3, 6]], dtype=float) + x = np.array([0, 0.5, 1]) + p = PPoly.construct_fast(c, x) + assert_allclose(p(0.3), 1*0.3**2 + 2*0.3 + 3) + assert_allclose(p(0.7), 4*(0.7-0.5)**2 + 5*(0.7-0.5) + 6) + + def test_vs_alternative_implementations(self): + np.random.seed(1234) + c = np.random.rand(3, 12, 22) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + + p = PPoly(c, x) + + xp = np.r_[0.3, 0.5, 0.33, 0.6] + expected = _ppoly_eval_1(c, x, xp) + assert_allclose(p(xp), expected) + + expected = _ppoly_eval_2(c[:,:,0], x, xp) + assert_allclose(p(xp)[:,0], expected) + + def test_from_spline(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0) + pp = PPoly.from_spline(spl) + + xi = np.linspace(0, 1, 200) + assert_allclose(pp(xi), splev(xi, spl)) + + # make sure .from_spline accepts BSpline objects + b = BSpline(*spl) + ppp = PPoly.from_spline(b) + assert_allclose(ppp(xi), b(xi)) + + # BSpline's extrapolate attribute propagates unless overridden + t, c, k = spl + for extrap in (None, True, False): + b = BSpline(t, c, k, extrapolate=extrap) + p = PPoly.from_spline(b) + assert_equal(p.extrapolate, b.extrapolate) + + def test_derivative_simple(self): + np.random.seed(1234) + c = np.array([[4, 3, 2, 1]]).T + dc = np.array([[3*4, 2*3, 2]]).T + ddc = np.array([[2*3*4, 1*2*3]]).T + x = np.array([0, 1]) + + pp = PPoly(c, x) + dpp = PPoly(dc, x) + ddpp = PPoly(ddc, x) + + assert_allclose(pp.derivative().c, dpp.c) + assert_allclose(pp.derivative(2).c, ddpp.c) + + def test_derivative_eval(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0) + pp = PPoly.from_spline(spl) + + xi = np.linspace(0, 1, 200) + for dx in range(0, 3): + assert_allclose(pp(xi, dx), splev(xi, spl, dx)) + + def test_derivative(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0, k=5) + pp = PPoly.from_spline(spl) + + xi = np.linspace(0, 1, 200) + for dx in range(0, 10): + assert_allclose(pp(xi, dx), pp.derivative(dx)(xi), + err_msg="dx=%d" % (dx,)) + + def test_antiderivative_of_constant(self): + # https://github.com/scipy/scipy/issues/4216 + p = PPoly([[1.]], [0, 1]) + assert_equal(p.antiderivative().c, PPoly([[1], [0]], [0, 1]).c) + assert_equal(p.antiderivative().x, PPoly([[1], [0]], [0, 1]).x) + + def test_antiderivative_regression_4355(self): + # https://github.com/scipy/scipy/issues/4355 + p = PPoly([[1., 0.5]], [0, 1, 2]) + q = p.antiderivative() + assert_equal(q.c, [[1, 0.5], [0, 1]]) + assert_equal(q.x, [0, 1, 2]) + assert_allclose(p.integrate(0, 2), 1.5) + assert_allclose(q(2) - q(0), 1.5) + + def test_antiderivative_simple(self): + np.random.seed(1234) + # [ p1(x) = 3*x**2 + 2*x + 1, + # p2(x) = 1.6875] + c = np.array([[3, 2, 1], [0, 0, 1.6875]]).T + # [ pp1(x) = x**3 + x**2 + x, + # pp2(x) = 1.6875*(x - 0.25) + pp1(0.25)] + ic = np.array([[1, 1, 1, 0], [0, 0, 1.6875, 0.328125]]).T + # [ ppp1(x) = (1/4)*x**4 + (1/3)*x**3 + (1/2)*x**2, + # ppp2(x) = (1.6875/2)*(x - 0.25)**2 + pp1(0.25)*x + ppp1(0.25)] + iic = np.array([[1/4, 1/3, 1/2, 0, 0], + [0, 0, 1.6875/2, 0.328125, 0.037434895833333336]]).T + x = np.array([0, 0.25, 1]) + + pp = PPoly(c, x) + ipp = pp.antiderivative() + iipp = pp.antiderivative(2) + iipp2 = ipp.antiderivative() + + assert_allclose(ipp.x, x) + assert_allclose(ipp.c.T, ic.T) + assert_allclose(iipp.c.T, iic.T) + assert_allclose(iipp2.c.T, iic.T) + + def test_antiderivative_vs_derivative(self): + np.random.seed(1234) + x = np.linspace(0, 1, 30)**2 + y = np.random.rand(len(x)) + spl = splrep(x, y, s=0, k=5) + pp = PPoly.from_spline(spl) + + for dx in range(0, 10): + ipp = pp.antiderivative(dx) + + # check that derivative is inverse op + pp2 = ipp.derivative(dx) + assert_allclose(pp.c, pp2.c) + + # check continuity + for k in range(dx): + pp2 = ipp.derivative(k) + + r = 1e-13 + endpoint = r*pp2.x[:-1] + (1 - r)*pp2.x[1:] + + assert_allclose(pp2(pp2.x[1:]), pp2(endpoint), + rtol=1e-7, err_msg="dx=%d k=%d" % (dx, k)) + + def test_antiderivative_vs_spline(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0, k=5) + pp = PPoly.from_spline(spl) + + for dx in range(0, 10): + pp2 = pp.antiderivative(dx) + spl2 = splantider(spl, dx) + + xi = np.linspace(0, 1, 200) + assert_allclose(pp2(xi), splev(xi, spl2), + rtol=1e-7) + + def test_antiderivative_continuity(self): + c = np.array([[2, 1, 2, 2], [2, 1, 3, 3]]).T + x = np.array([0, 0.5, 1]) + + p = PPoly(c, x) + ip = p.antiderivative() + + # check continuity + assert_allclose(ip(0.5 - 1e-9), ip(0.5 + 1e-9), rtol=1e-8) + + # check that only lowest order coefficients were changed + p2 = ip.derivative() + assert_allclose(p2.c, p.c) + + def test_integrate(self): + np.random.seed(1234) + x = np.sort(np.r_[0, np.random.rand(11), 1]) + y = np.random.rand(len(x)) + + spl = splrep(x, y, s=0, k=5) + pp = PPoly.from_spline(spl) + + a, b = 0.3, 0.9 + ig = pp.integrate(a, b) + + ipp = pp.antiderivative() + assert_allclose(ig, ipp(b) - ipp(a)) + assert_allclose(ig, splint(a, b, spl)) + + a, b = -0.3, 0.9 + ig = pp.integrate(a, b, extrapolate=True) + assert_allclose(ig, ipp(b) - ipp(a)) + + assert_(np.isnan(pp.integrate(a, b, extrapolate=False)).all()) + + def test_integrate_readonly(self): + x = np.array([1, 2, 4]) + c = np.array([[0., 0.], [-1., -1.], [2., -0.], [1., 2.]]) + + for writeable in (True, False): + x.flags.writeable = writeable + + P = PPoly(c, x) + vals = P.integrate(1, 4) + + assert_(np.isfinite(vals).all()) + + def test_integrate_periodic(self): + x = np.array([1, 2, 4]) + c = np.array([[0., 0.], [-1., -1.], [2., -0.], [1., 2.]]) + + P = PPoly(c, x, extrapolate='periodic') + I = P.antiderivative() + + period_int = I(4) - I(1) + + assert_allclose(P.integrate(1, 4), period_int) + assert_allclose(P.integrate(-10, -7), period_int) + assert_allclose(P.integrate(-10, -4), 2 * period_int) + + assert_allclose(P.integrate(1.5, 2.5), I(2.5) - I(1.5)) + assert_allclose(P.integrate(3.5, 5), I(2) - I(1) + I(4) - I(3.5)) + assert_allclose(P.integrate(3.5 + 12, 5 + 12), + I(2) - I(1) + I(4) - I(3.5)) + assert_allclose(P.integrate(3.5, 5 + 12), + I(2) - I(1) + I(4) - I(3.5) + 4 * period_int) + + assert_allclose(P.integrate(0, -1), I(2) - I(3)) + assert_allclose(P.integrate(-9, -10), I(2) - I(3)) + assert_allclose(P.integrate(0, -10), I(2) - I(3) - 3 * period_int) + + def test_roots(self): + x = np.linspace(0, 1, 31)**2 + y = np.sin(30*x) + + spl = splrep(x, y, s=0, k=3) + pp = PPoly.from_spline(spl) + + r = pp.roots() + r = r[(r >= 0 - 1e-15) & (r <= 1 + 1e-15)] + assert_allclose(r, sproot(spl), atol=1e-15) + + def test_roots_idzero(self): + # Roots for piecewise polynomials with identically zero + # sections. + c = np.array([[-1, 0.25], [0, 0], [-1, 0.25]]).T + x = np.array([0, 0.4, 0.6, 1.0]) + + pp = PPoly(c, x) + assert_array_equal(pp.roots(), + [0.25, 0.4, np.nan, 0.6 + 0.25]) + + # ditto for p.solve(const) with sections identically equal const + const = 2. + c1 = c.copy() + c1[1, :] += const + pp1 = PPoly(c1, x) + + assert_array_equal(pp1.solve(const), + [0.25, 0.4, np.nan, 0.6 + 0.25]) + + def test_roots_all_zero(self): + # test the code path for the polynomial being identically zero everywhere + c = [[0], [0]] + x = [0, 1] + p = PPoly(c, x) + assert_array_equal(p.roots(), [0, np.nan]) + assert_array_equal(p.solve(0), [0, np.nan]) + assert_array_equal(p.solve(1), []) + + c = [[0, 0], [0, 0]] + x = [0, 1, 2] + p = PPoly(c, x) + assert_array_equal(p.roots(), [0, np.nan, 1, np.nan]) + assert_array_equal(p.solve(0), [0, np.nan, 1, np.nan]) + assert_array_equal(p.solve(1), []) + + def test_roots_repeated(self): + # Check roots repeated in multiple sections are reported only + # once. + + # [(x + 1)**2 - 1, -x**2] ; x == 0 is a repeated root + c = np.array([[1, 0, -1], [-1, 0, 0]]).T + x = np.array([-1, 0, 1]) + + pp = PPoly(c, x) + assert_array_equal(pp.roots(), [-2, 0]) + assert_array_equal(pp.roots(extrapolate=False), [0]) + + def test_roots_discont(self): + # Check that a discontinuity across zero is reported as root + c = np.array([[1], [-1]]).T + x = np.array([0, 0.5, 1]) + pp = PPoly(c, x) + assert_array_equal(pp.roots(), [0.5]) + assert_array_equal(pp.roots(discontinuity=False), []) + + # ditto for a discontinuity across y: + assert_array_equal(pp.solve(0.5), [0.5]) + assert_array_equal(pp.solve(0.5, discontinuity=False), []) + + assert_array_equal(pp.solve(1.5), []) + assert_array_equal(pp.solve(1.5, discontinuity=False), []) + + def test_roots_random(self): + # Check high-order polynomials with random coefficients + np.random.seed(1234) + + num = 0 + + for extrapolate in (True, False): + for order in range(0, 20): + x = np.unique(np.r_[0, 10 * np.random.rand(30), 10]) + c = 2*np.random.rand(order+1, len(x)-1, 2, 3) - 1 + + pp = PPoly(c, x) + for y in [0, np.random.random()]: + r = pp.solve(y, discontinuity=False, extrapolate=extrapolate) + + for i in range(2): + for j in range(3): + rr = r[i,j] + if rr.size > 0: + # Check that the reported roots indeed are roots + num += rr.size + val = pp(rr, extrapolate=extrapolate)[:,i,j] + cmpval = pp(rr, nu=1, + extrapolate=extrapolate)[:,i,j] + msg = f"({extrapolate!r}) r = {repr(rr)}" + assert_allclose((val-y) / cmpval, 0, atol=1e-7, + err_msg=msg) + + # Check that we checked a number of roots + assert_(num > 100, repr(num)) + + def test_roots_croots(self): + # Test the complex root finding algorithm + np.random.seed(1234) + + for k in range(1, 15): + c = np.random.rand(k, 1, 130) + + if k == 3: + # add a case with zero discriminant + c[:,0,0] = 1, 2, 1 + + for y in [0, np.random.random()]: + w = np.empty(c.shape, dtype=complex) + _ppoly._croots_poly1(c, w) + + if k == 1: + assert_(np.isnan(w).all()) + continue + + res = 0 + cres = 0 + for i in range(k): + res += c[i,None] * w**(k-1-i) + cres += abs(c[i,None] * w**(k-1-i)) + with np.errstate(invalid='ignore'): + res /= cres + res = res.ravel() + res = res[~np.isnan(res)] + assert_allclose(res, 0, atol=1e-10) + + def test_extrapolate_attr(self): + # [ 1 - x**2 ] + c = np.array([[-1, 0, 1]]).T + x = np.array([0, 1]) + + for extrapolate in [True, False, None]: + pp = PPoly(c, x, extrapolate=extrapolate) + pp_d = pp.derivative() + pp_i = pp.antiderivative() + + if extrapolate is False: + assert_(np.isnan(pp([-0.1, 1.1])).all()) + assert_(np.isnan(pp_i([-0.1, 1.1])).all()) + assert_(np.isnan(pp_d([-0.1, 1.1])).all()) + assert_equal(pp.roots(), [1]) + else: + assert_allclose(pp([-0.1, 1.1]), [1-0.1**2, 1-1.1**2]) + assert_(not np.isnan(pp_i([-0.1, 1.1])).any()) + assert_(not np.isnan(pp_d([-0.1, 1.1])).any()) + assert_allclose(pp.roots(), [1, -1]) + + +class TestBPoly: + def test_simple(self): + x = [0, 1] + c = [[3]] + bp = BPoly(c, x) + assert_allclose(bp(0.1), 3.) + + def test_simple2(self): + x = [0, 1] + c = [[3], [1]] + bp = BPoly(c, x) # 3*(1-x) + 1*x + assert_allclose(bp(0.1), 3*0.9 + 1.*0.1) + + def test_simple3(self): + x = [0, 1] + c = [[3], [1], [4]] + bp = BPoly(c, x) # 3 * (1-x)**2 + 2 * x (1-x) + 4 * x**2 + assert_allclose(bp(0.2), + 3 * 0.8*0.8 + 1 * 2*0.2*0.8 + 4 * 0.2*0.2) + + def test_simple4(self): + x = [0, 1] + c = [[1], [1], [1], [2]] + bp = BPoly(c, x) + assert_allclose(bp(0.3), 0.7**3 + + 3 * 0.7**2 * 0.3 + + 3 * 0.7 * 0.3**2 + + 2 * 0.3**3) + + def test_simple5(self): + x = [0, 1] + c = [[1], [1], [8], [2], [1]] + bp = BPoly(c, x) + assert_allclose(bp(0.3), 0.7**4 + + 4 * 0.7**3 * 0.3 + + 8 * 6 * 0.7**2 * 0.3**2 + + 2 * 4 * 0.7 * 0.3**3 + + 0.3**4) + + def test_periodic(self): + x = [0, 1, 3] + c = [[3, 0], [0, 0], [0, 2]] + # [3*(1-x)**2, 2*((x-1)/2)**2] + bp = BPoly(c, x, extrapolate='periodic') + + assert_allclose(bp(3.4), 3 * 0.6**2) + assert_allclose(bp(-1.3), 2 * (0.7/2)**2) + + assert_allclose(bp(3.4, 1), -6 * 0.6) + assert_allclose(bp(-1.3, 1), 2 * (0.7/2)) + + def test_descending(self): + np.random.seed(0) + + power = 3 + for m in [10, 20, 30]: + x = np.sort(np.random.uniform(0, 10, m + 1)) + ca = np.random.uniform(-0.1, 0.1, size=(power + 1, m)) + # We need only to flip coefficients to get it right! + cd = ca[::-1].copy() + + pa = BPoly(ca, x, extrapolate=True) + pd = BPoly(cd[:, ::-1], x[::-1], extrapolate=True) + + x_test = np.random.uniform(-10, 20, 100) + assert_allclose(pa(x_test), pd(x_test), rtol=1e-13) + assert_allclose(pa(x_test, 1), pd(x_test, 1), rtol=1e-13) + + pa_d = pa.derivative() + pd_d = pd.derivative() + + assert_allclose(pa_d(x_test), pd_d(x_test), rtol=1e-13) + + # Antiderivatives won't be equal because fixing continuity is + # done in the reverse order, but surely the differences should be + # equal. + pa_i = pa.antiderivative() + pd_i = pd.antiderivative() + for a, b in np.random.uniform(-10, 20, (5, 2)): + int_a = pa.integrate(a, b) + int_d = pd.integrate(a, b) + assert_allclose(int_a, int_d, rtol=1e-12) + assert_allclose(pa_i(b) - pa_i(a), pd_i(b) - pd_i(a), + rtol=1e-12) + + def test_multi_shape(self): + c = np.random.rand(6, 2, 1, 2, 3) + x = np.array([0, 0.5, 1]) + p = BPoly(c, x) + assert_equal(p.x.shape, x.shape) + assert_equal(p.c.shape, c.shape) + assert_equal(p(0.3).shape, c.shape[2:]) + assert_equal(p(np.random.rand(5,6)).shape, + (5,6)+c.shape[2:]) + + dp = p.derivative() + assert_equal(dp.c.shape, (5, 2, 1, 2, 3)) + + def test_interval_length(self): + x = [0, 2] + c = [[3], [1], [4]] + bp = BPoly(c, x) + xval = 0.1 + s = xval / 2 # s = (x - xa) / (xb - xa) + assert_allclose(bp(xval), 3 * (1-s)*(1-s) + 1 * 2*s*(1-s) + 4 * s*s) + + def test_two_intervals(self): + x = [0, 1, 3] + c = [[3, 0], [0, 0], [0, 2]] + bp = BPoly(c, x) # [3*(1-x)**2, 2*((x-1)/2)**2] + + assert_allclose(bp(0.4), 3 * 0.6*0.6) + assert_allclose(bp(1.7), 2 * (0.7/2)**2) + + def test_extrapolate_attr(self): + x = [0, 2] + c = [[3], [1], [4]] + bp = BPoly(c, x) + + for extrapolate in (True, False, None): + bp = BPoly(c, x, extrapolate=extrapolate) + bp_d = bp.derivative() + if extrapolate is False: + assert_(np.isnan(bp([-0.1, 2.1])).all()) + assert_(np.isnan(bp_d([-0.1, 2.1])).all()) + else: + assert_(not np.isnan(bp([-0.1, 2.1])).any()) + assert_(not np.isnan(bp_d([-0.1, 2.1])).any()) + + +class TestBPolyCalculus: + def test_derivative(self): + x = [0, 1, 3] + c = [[3, 0], [0, 0], [0, 2]] + bp = BPoly(c, x) # [3*(1-x)**2, 2*((x-1)/2)**2] + bp_der = bp.derivative() + assert_allclose(bp_der(0.4), -6*(0.6)) + assert_allclose(bp_der(1.7), 0.7) + + # derivatives in-place + assert_allclose([bp(0.4, nu=1), bp(0.4, nu=2), bp(0.4, nu=3)], + [-6*(1-0.4), 6., 0.]) + assert_allclose([bp(1.7, nu=1), bp(1.7, nu=2), bp(1.7, nu=3)], + [0.7, 1., 0]) + + def test_derivative_ppoly(self): + # make sure it's consistent w/ power basis + np.random.seed(1234) + m, k = 5, 8 # number of intervals, order + x = np.sort(np.random.random(m)) + c = np.random.random((k, m-1)) + bp = BPoly(c, x) + pp = PPoly.from_bernstein_basis(bp) + + for d in range(k): + bp = bp.derivative() + pp = pp.derivative() + xp = np.linspace(x[0], x[-1], 21) + assert_allclose(bp(xp), pp(xp)) + + def test_deriv_inplace(self): + np.random.seed(1234) + m, k = 5, 8 # number of intervals, order + x = np.sort(np.random.random(m)) + c = np.random.random((k, m-1)) + + # test both real and complex coefficients + for cc in [c.copy(), c*(1. + 2.j)]: + bp = BPoly(cc, x) + xp = np.linspace(x[0], x[-1], 21) + for i in range(k): + assert_allclose(bp(xp, i), bp.derivative(i)(xp)) + + def test_antiderivative_simple(self): + # f(x) = x for x \in [0, 1), + # (x-1)/2 for x \in [1, 3] + # + # antiderivative is then + # F(x) = x**2 / 2 for x \in [0, 1), + # 0.5*x*(x/2 - 1) + A for x \in [1, 3] + # where A = 3/4 for continuity at x = 1. + x = [0, 1, 3] + c = [[0, 0], [1, 1]] + + bp = BPoly(c, x) + bi = bp.antiderivative() + + xx = np.linspace(0, 3, 11) + assert_allclose(bi(xx), + np.where(xx < 1, xx**2 / 2., + 0.5 * xx * (xx/2. - 1) + 3./4), + atol=1e-12, rtol=1e-12) + + def test_der_antider(self): + np.random.seed(1234) + x = np.sort(np.random.random(11)) + c = np.random.random((4, 10, 2, 3)) + bp = BPoly(c, x) + + xx = np.linspace(x[0], x[-1], 100) + assert_allclose(bp.antiderivative().derivative()(xx), + bp(xx), atol=1e-12, rtol=1e-12) + + def test_antider_ppoly(self): + np.random.seed(1234) + x = np.sort(np.random.random(11)) + c = np.random.random((4, 10, 2, 3)) + bp = BPoly(c, x) + pp = PPoly.from_bernstein_basis(bp) + + xx = np.linspace(x[0], x[-1], 10) + + assert_allclose(bp.antiderivative(2)(xx), + pp.antiderivative(2)(xx), atol=1e-12, rtol=1e-12) + + def test_antider_continuous(self): + np.random.seed(1234) + x = np.sort(np.random.random(11)) + c = np.random.random((4, 10)) + bp = BPoly(c, x).antiderivative() + + xx = bp.x[1:-1] + assert_allclose(bp(xx - 1e-14), + bp(xx + 1e-14), atol=1e-12, rtol=1e-12) + + def test_integrate(self): + np.random.seed(1234) + x = np.sort(np.random.random(11)) + c = np.random.random((4, 10)) + bp = BPoly(c, x) + pp = PPoly.from_bernstein_basis(bp) + assert_allclose(bp.integrate(0, 1), + pp.integrate(0, 1), atol=1e-12, rtol=1e-12) + + def test_integrate_extrap(self): + c = [[1]] + x = [0, 1] + b = BPoly(c, x) + + # default is extrapolate=True + assert_allclose(b.integrate(0, 2), 2., atol=1e-14) + + # .integrate argument overrides self.extrapolate + b1 = BPoly(c, x, extrapolate=False) + assert_(np.isnan(b1.integrate(0, 2))) + assert_allclose(b1.integrate(0, 2, extrapolate=True), 2., atol=1e-14) + + def test_integrate_periodic(self): + x = np.array([1, 2, 4]) + c = np.array([[0., 0.], [-1., -1.], [2., -0.], [1., 2.]]) + + P = BPoly.from_power_basis(PPoly(c, x), extrapolate='periodic') + I = P.antiderivative() + + period_int = I(4) - I(1) + + assert_allclose(P.integrate(1, 4), period_int) + assert_allclose(P.integrate(-10, -7), period_int) + assert_allclose(P.integrate(-10, -4), 2 * period_int) + + assert_allclose(P.integrate(1.5, 2.5), I(2.5) - I(1.5)) + assert_allclose(P.integrate(3.5, 5), I(2) - I(1) + I(4) - I(3.5)) + assert_allclose(P.integrate(3.5 + 12, 5 + 12), + I(2) - I(1) + I(4) - I(3.5)) + assert_allclose(P.integrate(3.5, 5 + 12), + I(2) - I(1) + I(4) - I(3.5) + 4 * period_int) + + assert_allclose(P.integrate(0, -1), I(2) - I(3)) + assert_allclose(P.integrate(-9, -10), I(2) - I(3)) + assert_allclose(P.integrate(0, -10), I(2) - I(3) - 3 * period_int) + + def test_antider_neg(self): + # .derivative(-nu) ==> .andiderivative(nu) and vice versa + c = [[1]] + x = [0, 1] + b = BPoly(c, x) + + xx = np.linspace(0, 1, 21) + + assert_allclose(b.derivative(-1)(xx), b.antiderivative()(xx), + atol=1e-12, rtol=1e-12) + assert_allclose(b.derivative(1)(xx), b.antiderivative(-1)(xx), + atol=1e-12, rtol=1e-12) + + +class TestPolyConversions: + def test_bp_from_pp(self): + x = [0, 1, 3] + c = [[3, 2], [1, 8], [4, 3]] + pp = PPoly(c, x) + bp = BPoly.from_power_basis(pp) + pp1 = PPoly.from_bernstein_basis(bp) + + xp = [0.1, 1.4] + assert_allclose(pp(xp), bp(xp)) + assert_allclose(pp(xp), pp1(xp)) + + def test_bp_from_pp_random(self): + np.random.seed(1234) + m, k = 5, 8 # number of intervals, order + x = np.sort(np.random.random(m)) + c = np.random.random((k, m-1)) + pp = PPoly(c, x) + bp = BPoly.from_power_basis(pp) + pp1 = PPoly.from_bernstein_basis(bp) + + xp = np.linspace(x[0], x[-1], 21) + assert_allclose(pp(xp), bp(xp)) + assert_allclose(pp(xp), pp1(xp)) + + def test_pp_from_bp(self): + x = [0, 1, 3] + c = [[3, 3], [1, 1], [4, 2]] + bp = BPoly(c, x) + pp = PPoly.from_bernstein_basis(bp) + bp1 = BPoly.from_power_basis(pp) + + xp = [0.1, 1.4] + assert_allclose(bp(xp), pp(xp)) + assert_allclose(bp(xp), bp1(xp)) + + def test_broken_conversions(self): + # regression test for gh-10597: from_power_basis only accepts PPoly etc. + x = [0, 1, 3] + c = [[3, 3], [1, 1], [4, 2]] + pp = PPoly(c, x) + with assert_raises(TypeError): + PPoly.from_bernstein_basis(pp) + + bp = BPoly(c, x) + with assert_raises(TypeError): + BPoly.from_power_basis(bp) + + +class TestBPolyFromDerivatives: + def test_make_poly_1(self): + c1 = BPoly._construct_from_derivatives(0, 1, [2], [3]) + assert_allclose(c1, [2., 3.]) + + def test_make_poly_2(self): + c1 = BPoly._construct_from_derivatives(0, 1, [1, 0], [1]) + assert_allclose(c1, [1., 1., 1.]) + + # f'(0) = 3 + c2 = BPoly._construct_from_derivatives(0, 1, [2, 3], [1]) + assert_allclose(c2, [2., 7./2, 1.]) + + # f'(1) = 3 + c3 = BPoly._construct_from_derivatives(0, 1, [2], [1, 3]) + assert_allclose(c3, [2., -0.5, 1.]) + + def test_make_poly_3(self): + # f'(0)=2, f''(0)=3 + c1 = BPoly._construct_from_derivatives(0, 1, [1, 2, 3], [4]) + assert_allclose(c1, [1., 5./3, 17./6, 4.]) + + # f'(1)=2, f''(1)=3 + c2 = BPoly._construct_from_derivatives(0, 1, [1], [4, 2, 3]) + assert_allclose(c2, [1., 19./6, 10./3, 4.]) + + # f'(0)=2, f'(1)=3 + c3 = BPoly._construct_from_derivatives(0, 1, [1, 2], [4, 3]) + assert_allclose(c3, [1., 5./3, 3., 4.]) + + def test_make_poly_12(self): + np.random.seed(12345) + ya = np.r_[0, np.random.random(5)] + yb = np.r_[0, np.random.random(5)] + + c = BPoly._construct_from_derivatives(0, 1, ya, yb) + pp = BPoly(c[:, None], [0, 1]) + for j in range(6): + assert_allclose([pp(0.), pp(1.)], [ya[j], yb[j]]) + pp = pp.derivative() + + def test_raise_degree(self): + np.random.seed(12345) + x = [0, 1] + k, d = 8, 5 + c = np.random.random((k, 1, 2, 3, 4)) + bp = BPoly(c, x) + + c1 = BPoly._raise_degree(c, d) + bp1 = BPoly(c1, x) + + xp = np.linspace(0, 1, 11) + assert_allclose(bp(xp), bp1(xp)) + + def test_xi_yi(self): + assert_raises(ValueError, BPoly.from_derivatives, [0, 1], [0]) + + def test_coords_order(self): + xi = [0, 0, 1] + yi = [[0], [0], [0]] + assert_raises(ValueError, BPoly.from_derivatives, xi, yi) + + def test_zeros(self): + xi = [0, 1, 2, 3] + yi = [[0, 0], [0], [0, 0], [0, 0]] # NB: will have to raise the degree + pp = BPoly.from_derivatives(xi, yi) + assert_(pp.c.shape == (4, 3)) + + ppd = pp.derivative() + for xp in [0., 0.1, 1., 1.1, 1.9, 2., 2.5]: + assert_allclose([pp(xp), ppd(xp)], [0., 0.]) + + def _make_random_mk(self, m, k): + # k derivatives at each breakpoint + np.random.seed(1234) + xi = np.asarray([1. * j**2 for j in range(m+1)]) + yi = [np.random.random(k) for j in range(m+1)] + return xi, yi + + def test_random_12(self): + m, k = 5, 12 + xi, yi = self._make_random_mk(m, k) + pp = BPoly.from_derivatives(xi, yi) + + for order in range(k//2): + assert_allclose(pp(xi), [yy[order] for yy in yi]) + pp = pp.derivative() + + def test_order_zero(self): + m, k = 5, 12 + xi, yi = self._make_random_mk(m, k) + assert_raises(ValueError, BPoly.from_derivatives, + **dict(xi=xi, yi=yi, orders=0)) + + def test_orders_too_high(self): + m, k = 5, 12 + xi, yi = self._make_random_mk(m, k) + + BPoly.from_derivatives(xi, yi, orders=2*k-1) # this is still ok + assert_raises(ValueError, BPoly.from_derivatives, # but this is not + **dict(xi=xi, yi=yi, orders=2*k)) + + def test_orders_global(self): + m, k = 5, 12 + xi, yi = self._make_random_mk(m, k) + + # ok, this is confusing. Local polynomials will be of the order 5 + # which means that up to the 2nd derivatives will be used at each point + order = 5 + pp = BPoly.from_derivatives(xi, yi, orders=order) + + for j in range(order//2+1): + assert_allclose(pp(xi[1:-1] - 1e-12), pp(xi[1:-1] + 1e-12)) + pp = pp.derivative() + assert_(not np.allclose(pp(xi[1:-1] - 1e-12), pp(xi[1:-1] + 1e-12))) + + # now repeat with `order` being even: on each interval, it uses + # order//2 'derivatives' @ the right-hand endpoint and + # order//2+1 @ 'derivatives' the left-hand endpoint + order = 6 + pp = BPoly.from_derivatives(xi, yi, orders=order) + for j in range(order//2): + assert_allclose(pp(xi[1:-1] - 1e-12), pp(xi[1:-1] + 1e-12)) + pp = pp.derivative() + assert_(not np.allclose(pp(xi[1:-1] - 1e-12), pp(xi[1:-1] + 1e-12))) + + def test_orders_local(self): + m, k = 7, 12 + xi, yi = self._make_random_mk(m, k) + + orders = [o + 1 for o in range(m)] + for i, x in enumerate(xi[1:-1]): + pp = BPoly.from_derivatives(xi, yi, orders=orders) + for j in range(orders[i] // 2 + 1): + assert_allclose(pp(x - 1e-12), pp(x + 1e-12)) + pp = pp.derivative() + assert_(not np.allclose(pp(x - 1e-12), pp(x + 1e-12))) + + def test_yi_trailing_dims(self): + m, k = 7, 5 + xi = np.sort(np.random.random(m+1)) + yi = np.random.random((m+1, k, 6, 7, 8)) + pp = BPoly.from_derivatives(xi, yi) + assert_equal(pp.c.shape, (2*k, m, 6, 7, 8)) + + def test_gh_5430(self): + # At least one of these raises an error unless gh-5430 is + # fixed. In py2k an int is implemented using a C long, so + # which one fails depends on your system. In py3k there is only + # one arbitrary precision integer type, so both should fail. + orders = np.int32(1) + p = BPoly.from_derivatives([0, 1], [[0], [0]], orders=orders) + assert_almost_equal(p(0), 0) + orders = np.int64(1) + p = BPoly.from_derivatives([0, 1], [[0], [0]], orders=orders) + assert_almost_equal(p(0), 0) + orders = 1 + # This worked before; make sure it still works + p = BPoly.from_derivatives([0, 1], [[0], [0]], orders=orders) + assert_almost_equal(p(0), 0) + orders = 1 + + +class TestNdPPoly: + def test_simple_1d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5) + x = np.linspace(0, 1, 5+1) + + xi = np.random.rand(200) + + p = NdPPoly(c, (x,)) + v1 = p((xi,)) + + v2 = _ppoly_eval_1(c[:,:,None], x, xi).ravel() + assert_allclose(v1, v2) + + def test_simple_2d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5, 6, 7) + x = np.linspace(0, 1, 6+1) + y = np.linspace(0, 1, 7+1)**2 + + xi = np.random.rand(200) + yi = np.random.rand(200) + + v1 = np.empty([len(xi), 1], dtype=c.dtype) + v1.fill(np.nan) + _ppoly.evaluate_nd(c.reshape(4*5, 6*7, 1), + (x, y), + np.array([4, 5], dtype=np.intc), + np.c_[xi, yi], + np.array([0, 0], dtype=np.intc), + 1, + v1) + v1 = v1.ravel() + v2 = _ppoly2d_eval(c, (x, y), xi, yi) + assert_allclose(v1, v2) + + p = NdPPoly(c, (x, y)) + for nu in (None, (0, 0), (0, 1), (1, 0), (2, 3), (9, 2)): + v1 = p(np.c_[xi, yi], nu=nu) + v2 = _ppoly2d_eval(c, (x, y), xi, yi, nu=nu) + assert_allclose(v1, v2, err_msg=repr(nu)) + + def test_simple_3d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5, 6, 7, 8, 9) + x = np.linspace(0, 1, 7+1) + y = np.linspace(0, 1, 8+1)**2 + z = np.linspace(0, 1, 9+1)**3 + + xi = np.random.rand(40) + yi = np.random.rand(40) + zi = np.random.rand(40) + + p = NdPPoly(c, (x, y, z)) + + for nu in (None, (0, 0, 0), (0, 1, 0), (1, 0, 0), (2, 3, 0), + (6, 0, 2)): + v1 = p((xi, yi, zi), nu=nu) + v2 = _ppoly3d_eval(c, (x, y, z), xi, yi, zi, nu=nu) + assert_allclose(v1, v2, err_msg=repr(nu)) + + def test_simple_4d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5, 6, 7, 8, 9, 10, 11) + x = np.linspace(0, 1, 8+1) + y = np.linspace(0, 1, 9+1)**2 + z = np.linspace(0, 1, 10+1)**3 + u = np.linspace(0, 1, 11+1)**4 + + xi = np.random.rand(20) + yi = np.random.rand(20) + zi = np.random.rand(20) + ui = np.random.rand(20) + + p = NdPPoly(c, (x, y, z, u)) + v1 = p((xi, yi, zi, ui)) + + v2 = _ppoly4d_eval(c, (x, y, z, u), xi, yi, zi, ui) + assert_allclose(v1, v2) + + def test_deriv_1d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5) + x = np.linspace(0, 1, 5+1) + + p = NdPPoly(c, (x,)) + + # derivative + dp = p.derivative(nu=[1]) + p1 = PPoly(c, x) + dp1 = p1.derivative() + assert_allclose(dp.c, dp1.c) + + # antiderivative + dp = p.antiderivative(nu=[2]) + p1 = PPoly(c, x) + dp1 = p1.antiderivative(2) + assert_allclose(dp.c, dp1.c) + + def test_deriv_3d(self): + np.random.seed(1234) + + c = np.random.rand(4, 5, 6, 7, 8, 9) + x = np.linspace(0, 1, 7+1) + y = np.linspace(0, 1, 8+1)**2 + z = np.linspace(0, 1, 9+1)**3 + + p = NdPPoly(c, (x, y, z)) + + # differentiate vs x + p1 = PPoly(c.transpose(0, 3, 1, 2, 4, 5), x) + dp = p.derivative(nu=[2]) + dp1 = p1.derivative(2) + assert_allclose(dp.c, + dp1.c.transpose(0, 2, 3, 1, 4, 5)) + + # antidifferentiate vs y + p1 = PPoly(c.transpose(1, 4, 0, 2, 3, 5), y) + dp = p.antiderivative(nu=[0, 1, 0]) + dp1 = p1.antiderivative(1) + assert_allclose(dp.c, + dp1.c.transpose(2, 0, 3, 4, 1, 5)) + + # differentiate vs z + p1 = PPoly(c.transpose(2, 5, 0, 1, 3, 4), z) + dp = p.derivative(nu=[0, 0, 3]) + dp1 = p1.derivative(3) + assert_allclose(dp.c, + dp1.c.transpose(2, 3, 0, 4, 5, 1)) + + def test_deriv_3d_simple(self): + # Integrate to obtain function x y**2 z**4 / (2! 4!) + + c = np.ones((1, 1, 1, 3, 4, 5)) + x = np.linspace(0, 1, 3+1)**1 + y = np.linspace(0, 1, 4+1)**2 + z = np.linspace(0, 1, 5+1)**3 + + p = NdPPoly(c, (x, y, z)) + ip = p.antiderivative((1, 0, 4)) + ip = ip.antiderivative((0, 2, 0)) + + xi = np.random.rand(20) + yi = np.random.rand(20) + zi = np.random.rand(20) + + assert_allclose(ip((xi, yi, zi)), + xi * yi**2 * zi**4 / (gamma(3)*gamma(5))) + + def test_integrate_2d(self): + np.random.seed(1234) + c = np.random.rand(4, 5, 16, 17) + x = np.linspace(0, 1, 16+1)**1 + y = np.linspace(0, 1, 17+1)**2 + + # make continuously differentiable so that nquad() has an + # easier time + c = c.transpose(0, 2, 1, 3) + cx = c.reshape(c.shape[0], c.shape[1], -1).copy() + _ppoly.fix_continuity(cx, x, 2) + c = cx.reshape(c.shape) + c = c.transpose(0, 2, 1, 3) + c = c.transpose(1, 3, 0, 2) + cx = c.reshape(c.shape[0], c.shape[1], -1).copy() + _ppoly.fix_continuity(cx, y, 2) + c = cx.reshape(c.shape) + c = c.transpose(2, 0, 3, 1).copy() + + # Check integration + p = NdPPoly(c, (x, y)) + + for ranges in [[(0, 1), (0, 1)], + [(0, 0.5), (0, 1)], + [(0, 1), (0, 0.5)], + [(0.3, 0.7), (0.6, 0.2)]]: + + ig = p.integrate(ranges) + ig2, err2 = nquad(lambda x, y: p((x, y)), ranges, + opts=[dict(epsrel=1e-5, epsabs=1e-5)]*2) + assert_allclose(ig, ig2, rtol=1e-5, atol=1e-5, + err_msg=repr(ranges)) + + def test_integrate_1d(self): + np.random.seed(1234) + c = np.random.rand(4, 5, 6, 16, 17, 18) + x = np.linspace(0, 1, 16+1)**1 + y = np.linspace(0, 1, 17+1)**2 + z = np.linspace(0, 1, 18+1)**3 + + # Check 1-D integration + p = NdPPoly(c, (x, y, z)) + + u = np.random.rand(200) + v = np.random.rand(200) + a, b = 0.2, 0.7 + + px = p.integrate_1d(a, b, axis=0) + pax = p.antiderivative((1, 0, 0)) + assert_allclose(px((u, v)), pax((b, u, v)) - pax((a, u, v))) + + py = p.integrate_1d(a, b, axis=1) + pay = p.antiderivative((0, 1, 0)) + assert_allclose(py((u, v)), pay((u, b, v)) - pay((u, a, v))) + + pz = p.integrate_1d(a, b, axis=2) + paz = p.antiderivative((0, 0, 1)) + assert_allclose(pz((u, v)), paz((u, v, b)) - paz((u, v, a))) + + +def _ppoly_eval_1(c, x, xps): + """Evaluate piecewise polynomial manually""" + out = np.zeros((len(xps), c.shape[2])) + for i, xp in enumerate(xps): + if xp < 0 or xp > 1: + out[i,:] = np.nan + continue + j = np.searchsorted(x, xp) - 1 + d = xp - x[j] + assert_(x[j] <= xp < x[j+1]) + r = sum(c[k,j] * d**(c.shape[0]-k-1) + for k in range(c.shape[0])) + out[i,:] = r + return out + + +def _ppoly_eval_2(coeffs, breaks, xnew, fill=np.nan): + """Evaluate piecewise polynomial manually (another way)""" + a = breaks[0] + b = breaks[-1] + K = coeffs.shape[0] + + saveshape = np.shape(xnew) + xnew = np.ravel(xnew) + res = np.empty_like(xnew) + mask = (xnew >= a) & (xnew <= b) + res[~mask] = fill + xx = xnew.compress(mask) + indxs = np.searchsorted(breaks, xx)-1 + indxs = indxs.clip(0, len(breaks)) + pp = coeffs + diff = xx - breaks.take(indxs) + V = np.vander(diff, N=K) + values = np.array([np.dot(V[k, :], pp[:, indxs[k]]) for k in range(len(xx))]) + res[mask] = values + res.shape = saveshape + return res + + +def _dpow(x, y, n): + """ + d^n (x**y) / dx^n + """ + if n < 0: + raise ValueError("invalid derivative order") + elif n > y: + return 0 + else: + return poch(y - n + 1, n) * x**(y - n) + + +def _ppoly2d_eval(c, xs, xnew, ynew, nu=None): + """ + Straightforward evaluation of 2-D piecewise polynomial + """ + if nu is None: + nu = (0, 0) + + out = np.empty((len(xnew),), dtype=c.dtype) + + nx, ny = c.shape[:2] + + for jout, (x, y) in enumerate(zip(xnew, ynew)): + if not ((xs[0][0] <= x <= xs[0][-1]) and + (xs[1][0] <= y <= xs[1][-1])): + out[jout] = np.nan + continue + + j1 = np.searchsorted(xs[0], x) - 1 + j2 = np.searchsorted(xs[1], y) - 1 + + s1 = x - xs[0][j1] + s2 = y - xs[1][j2] + + val = 0 + + for k1 in range(c.shape[0]): + for k2 in range(c.shape[1]): + val += (c[nx-k1-1,ny-k2-1,j1,j2] + * _dpow(s1, k1, nu[0]) + * _dpow(s2, k2, nu[1])) + + out[jout] = val + + return out + + +def _ppoly3d_eval(c, xs, xnew, ynew, znew, nu=None): + """ + Straightforward evaluation of 3-D piecewise polynomial + """ + if nu is None: + nu = (0, 0, 0) + + out = np.empty((len(xnew),), dtype=c.dtype) + + nx, ny, nz = c.shape[:3] + + for jout, (x, y, z) in enumerate(zip(xnew, ynew, znew)): + if not ((xs[0][0] <= x <= xs[0][-1]) and + (xs[1][0] <= y <= xs[1][-1]) and + (xs[2][0] <= z <= xs[2][-1])): + out[jout] = np.nan + continue + + j1 = np.searchsorted(xs[0], x) - 1 + j2 = np.searchsorted(xs[1], y) - 1 + j3 = np.searchsorted(xs[2], z) - 1 + + s1 = x - xs[0][j1] + s2 = y - xs[1][j2] + s3 = z - xs[2][j3] + + val = 0 + for k1 in range(c.shape[0]): + for k2 in range(c.shape[1]): + for k3 in range(c.shape[2]): + val += (c[nx-k1-1,ny-k2-1,nz-k3-1,j1,j2,j3] + * _dpow(s1, k1, nu[0]) + * _dpow(s2, k2, nu[1]) + * _dpow(s3, k3, nu[2])) + + out[jout] = val + + return out + + +def _ppoly4d_eval(c, xs, xnew, ynew, znew, unew, nu=None): + """ + Straightforward evaluation of 4-D piecewise polynomial + """ + if nu is None: + nu = (0, 0, 0, 0) + + out = np.empty((len(xnew),), dtype=c.dtype) + + mx, my, mz, mu = c.shape[:4] + + for jout, (x, y, z, u) in enumerate(zip(xnew, ynew, znew, unew)): + if not ((xs[0][0] <= x <= xs[0][-1]) and + (xs[1][0] <= y <= xs[1][-1]) and + (xs[2][0] <= z <= xs[2][-1]) and + (xs[3][0] <= u <= xs[3][-1])): + out[jout] = np.nan + continue + + j1 = np.searchsorted(xs[0], x) - 1 + j2 = np.searchsorted(xs[1], y) - 1 + j3 = np.searchsorted(xs[2], z) - 1 + j4 = np.searchsorted(xs[3], u) - 1 + + s1 = x - xs[0][j1] + s2 = y - xs[1][j2] + s3 = z - xs[2][j3] + s4 = u - xs[3][j4] + + val = 0 + for k1 in range(c.shape[0]): + for k2 in range(c.shape[1]): + for k3 in range(c.shape[2]): + for k4 in range(c.shape[3]): + val += (c[mx-k1-1,my-k2-1,mz-k3-1,mu-k4-1,j1,j2,j3,j4] + * _dpow(s1, k1, nu[0]) + * _dpow(s2, k2, nu[1]) + * _dpow(s3, k3, nu[2]) + * _dpow(s4, k4, nu[3])) + + out[jout] = val + + return out diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_ndgriddata.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_ndgriddata.py new file mode 100644 index 0000000000000000000000000000000000000000..e3e1ed8968ae5fd55e2f55d8a24c0ee2fa4ade45 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_ndgriddata.py @@ -0,0 +1,284 @@ +import numpy as np +from numpy.testing import assert_equal, assert_array_equal, assert_allclose +import pytest +from pytest import raises as assert_raises + +from scipy.interpolate import (griddata, NearestNDInterpolator, + LinearNDInterpolator, + CloughTocher2DInterpolator) + + +parametrize_interpolators = pytest.mark.parametrize( + "interpolator", [NearestNDInterpolator, LinearNDInterpolator, + CloughTocher2DInterpolator] +) + +class TestGriddata: + def test_fill_value(self): + x = [(0,0), (0,1), (1,0)] + y = [1, 2, 3] + + yi = griddata(x, y, [(1,1), (1,2), (0,0)], fill_value=-1) + assert_array_equal(yi, [-1., -1, 1]) + + yi = griddata(x, y, [(1,1), (1,2), (0,0)]) + assert_array_equal(yi, [np.nan, np.nan, 1]) + + def test_alternative_call(self): + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = (np.arange(x.shape[0], dtype=np.float64)[:,None] + + np.array([0,1])[None,:]) + + for method in ('nearest', 'linear', 'cubic'): + for rescale in (True, False): + msg = repr((method, rescale)) + yi = griddata((x[:,0], x[:,1]), y, (x[:,0], x[:,1]), method=method, + rescale=rescale) + assert_allclose(y, yi, atol=1e-14, err_msg=msg) + + def test_multivalue_2d(self): + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = (np.arange(x.shape[0], dtype=np.float64)[:,None] + + np.array([0,1])[None,:]) + + for method in ('nearest', 'linear', 'cubic'): + for rescale in (True, False): + msg = repr((method, rescale)) + yi = griddata(x, y, x, method=method, rescale=rescale) + assert_allclose(y, yi, atol=1e-14, err_msg=msg) + + def test_multipoint_2d(self): + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + + xi = x[:,None,:] + np.array([0,0,0])[None,:,None] + + for method in ('nearest', 'linear', 'cubic'): + for rescale in (True, False): + msg = repr((method, rescale)) + yi = griddata(x, y, xi, method=method, rescale=rescale) + + assert_equal(yi.shape, (5, 3), err_msg=msg) + assert_allclose(yi, np.tile(y[:,None], (1, 3)), + atol=1e-14, err_msg=msg) + + def test_complex_2d(self): + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 2j*y[::-1] + + xi = x[:,None,:] + np.array([0,0,0])[None,:,None] + + for method in ('nearest', 'linear', 'cubic'): + for rescale in (True, False): + msg = repr((method, rescale)) + yi = griddata(x, y, xi, method=method, rescale=rescale) + + assert_equal(yi.shape, (5, 3), err_msg=msg) + assert_allclose(yi, np.tile(y[:,None], (1, 3)), + atol=1e-14, err_msg=msg) + + def test_1d(self): + x = np.array([1, 2.5, 3, 4.5, 5, 6]) + y = np.array([1, 2, 0, 3.9, 2, 1]) + + for method in ('nearest', 'linear', 'cubic'): + assert_allclose(griddata(x, y, x, method=method), y, + err_msg=method, atol=1e-14) + assert_allclose(griddata(x.reshape(6, 1), y, x, method=method), y, + err_msg=method, atol=1e-14) + assert_allclose(griddata((x,), y, (x,), method=method), y, + err_msg=method, atol=1e-14) + + def test_1d_borders(self): + # Test for nearest neighbor case with xi outside + # the range of the values. + x = np.array([1, 2.5, 3, 4.5, 5, 6]) + y = np.array([1, 2, 0, 3.9, 2, 1]) + xi = np.array([0.9, 6.5]) + yi_should = np.array([1.0, 1.0]) + + method = 'nearest' + assert_allclose(griddata(x, y, xi, + method=method), yi_should, + err_msg=method, + atol=1e-14) + assert_allclose(griddata(x.reshape(6, 1), y, xi, + method=method), yi_should, + err_msg=method, + atol=1e-14) + assert_allclose(griddata((x, ), y, (xi, ), + method=method), yi_should, + err_msg=method, + atol=1e-14) + + def test_1d_unsorted(self): + x = np.array([2.5, 1, 4.5, 5, 6, 3]) + y = np.array([1, 2, 0, 3.9, 2, 1]) + + for method in ('nearest', 'linear', 'cubic'): + assert_allclose(griddata(x, y, x, method=method), y, + err_msg=method, atol=1e-10) + assert_allclose(griddata(x.reshape(6, 1), y, x, method=method), y, + err_msg=method, atol=1e-10) + assert_allclose(griddata((x,), y, (x,), method=method), y, + err_msg=method, atol=1e-10) + + def test_square_rescale_manual(self): + points = np.array([(0,0), (0,100), (10,100), (10,0), (1, 5)], dtype=np.float64) + points_rescaled = np.array([(0,0), (0,1), (1,1), (1,0), (0.1, 0.05)], + dtype=np.float64) + values = np.array([1., 2., -3., 5., 9.], dtype=np.float64) + + xx, yy = np.broadcast_arrays(np.linspace(0, 10, 14)[:,None], + np.linspace(0, 100, 14)[None,:]) + xx = xx.ravel() + yy = yy.ravel() + xi = np.array([xx, yy]).T.copy() + + for method in ('nearest', 'linear', 'cubic'): + msg = method + zi = griddata(points_rescaled, values, xi/np.array([10, 100.]), + method=method) + zi_rescaled = griddata(points, values, xi, method=method, + rescale=True) + assert_allclose(zi, zi_rescaled, err_msg=msg, + atol=1e-12) + + def test_xi_1d(self): + # Check that 1-D xi is interpreted as a coordinate + x = np.array([(0,0), (-0.5,-0.5), (-0.5,0.5), (0.5, 0.5), (0.25, 0.3)], + dtype=np.float64) + y = np.arange(x.shape[0], dtype=np.float64) + y = y - 2j*y[::-1] + + xi = np.array([0.5, 0.5]) + + for method in ('nearest', 'linear', 'cubic'): + p1 = griddata(x, y, xi, method=method) + p2 = griddata(x, y, xi[None,:], method=method) + assert_allclose(p1, p2, err_msg=method) + + xi1 = np.array([0.5]) + xi3 = np.array([0.5, 0.5, 0.5]) + assert_raises(ValueError, griddata, x, y, xi1, + method=method) + assert_raises(ValueError, griddata, x, y, xi3, + method=method) + + +class TestNearestNDInterpolator: + def test_nearest_options(self): + # smoke test that NearestNDInterpolator accept cKDTree options + npts, nd = 4, 3 + x = np.arange(npts*nd).reshape((npts, nd)) + y = np.arange(npts) + nndi = NearestNDInterpolator(x, y) + + opts = {'balanced_tree': False, 'compact_nodes': False} + nndi_o = NearestNDInterpolator(x, y, tree_options=opts) + assert_allclose(nndi(x), nndi_o(x), atol=1e-14) + + def test_nearest_list_argument(self): + nd = np.array([[0, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 2]]) + d = nd[:, 3:] + + # z is np.array + NI = NearestNDInterpolator((d[0], d[1]), d[2]) + assert_array_equal(NI([0.1, 0.9], [0.1, 0.9]), [0, 2]) + + # z is list + NI = NearestNDInterpolator((d[0], d[1]), list(d[2])) + assert_array_equal(NI([0.1, 0.9], [0.1, 0.9]), [0, 2]) + + def test_nearest_query_options(self): + nd = np.array([[0, 0.5, 0, 1], + [0, 0, 0.5, 1], + [0, 1, 1, 2]]) + delta = 0.1 + query_points = [0 + delta, 1 + delta], [0 + delta, 1 + delta] + + # case 1 - query max_dist is smaller than + # the query points' nearest distance to nd. + NI = NearestNDInterpolator((nd[0], nd[1]), nd[2]) + distance_upper_bound = np.sqrt(delta ** 2 + delta ** 2) - 1e-7 + assert_array_equal(NI(query_points, distance_upper_bound=distance_upper_bound), + [np.nan, np.nan]) + + # case 2 - query p is inf, will return [0, 2] + distance_upper_bound = np.sqrt(delta ** 2 + delta ** 2) - 1e-7 + p = np.inf + assert_array_equal( + NI(query_points, distance_upper_bound=distance_upper_bound, p=p), + [0, 2] + ) + + # case 3 - query max_dist is larger, so should return non np.nan + distance_upper_bound = np.sqrt(delta ** 2 + delta ** 2) + 1e-7 + assert_array_equal( + NI(query_points, distance_upper_bound=distance_upper_bound), + [0, 2] + ) + + def test_nearest_query_valid_inputs(self): + nd = np.array([[0, 1, 0, 1], + [0, 0, 1, 1], + [0, 1, 1, 2]]) + NI = NearestNDInterpolator((nd[0], nd[1]), nd[2]) + with assert_raises(TypeError): + NI([0.5, 0.5], query_options="not a dictionary") + + +class TestNDInterpolators: + @parametrize_interpolators + def test_broadcastable_input(self, interpolator): + # input data + np.random.seed(0) + x = np.random.random(10) + y = np.random.random(10) + z = np.hypot(x, y) + + # x-y grid for interpolation + X = np.linspace(min(x), max(x)) + Y = np.linspace(min(y), max(y)) + X, Y = np.meshgrid(X, Y) + XY = np.vstack((X.ravel(), Y.ravel())).T + interp = interpolator(list(zip(x, y)), z) + # single array input + interp_points0 = interp(XY) + # tuple input + interp_points1 = interp((X, Y)) + interp_points2 = interp((X, 0.0)) + # broadcastable input + interp_points3 = interp(X, Y) + interp_points4 = interp(X, 0.0) + + assert_equal(interp_points0.size == + interp_points1.size == + interp_points2.size == + interp_points3.size == + interp_points4.size, True) + + @parametrize_interpolators + def test_read_only(self, interpolator): + # input data + np.random.seed(0) + xy = np.random.random((10, 2)) + x, y = xy[:, 0], xy[:, 1] + z = np.hypot(x, y) + + # interpolation points + XY = np.random.random((50, 2)) + + xy.setflags(write=False) + z.setflags(write=False) + XY.setflags(write=False) + + interp = interpolator(xy, z) + interp(XY) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_pade.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_pade.py new file mode 100644 index 0000000000000000000000000000000000000000..f58e01e5e730d2e5c4630f41933a0b77a17efc77 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_pade.py @@ -0,0 +1,104 @@ +from numpy.testing import (assert_array_equal, assert_array_almost_equal) +from scipy.interpolate import pade + +def test_pade_trivial(): + nump, denomp = pade([1.0], 0) + assert_array_equal(nump.c, [1.0]) + assert_array_equal(denomp.c, [1.0]) + + nump, denomp = pade([1.0], 0, 0) + assert_array_equal(nump.c, [1.0]) + assert_array_equal(denomp.c, [1.0]) + + +def test_pade_4term_exp(): + # First four Taylor coefficients of exp(x). + # Unlike poly1d, the first array element is the zero-order term. + an = [1.0, 1.0, 0.5, 1.0/6] + + nump, denomp = pade(an, 0) + assert_array_almost_equal(nump.c, [1.0/6, 0.5, 1.0, 1.0]) + assert_array_almost_equal(denomp.c, [1.0]) + + nump, denomp = pade(an, 1) + assert_array_almost_equal(nump.c, [1.0/6, 2.0/3, 1.0]) + assert_array_almost_equal(denomp.c, [-1.0/3, 1.0]) + + nump, denomp = pade(an, 2) + assert_array_almost_equal(nump.c, [1.0/3, 1.0]) + assert_array_almost_equal(denomp.c, [1.0/6, -2.0/3, 1.0]) + + nump, denomp = pade(an, 3) + assert_array_almost_equal(nump.c, [1.0]) + assert_array_almost_equal(denomp.c, [-1.0/6, 0.5, -1.0, 1.0]) + + # Testing inclusion of optional parameter + nump, denomp = pade(an, 0, 3) + assert_array_almost_equal(nump.c, [1.0/6, 0.5, 1.0, 1.0]) + assert_array_almost_equal(denomp.c, [1.0]) + + nump, denomp = pade(an, 1, 2) + assert_array_almost_equal(nump.c, [1.0/6, 2.0/3, 1.0]) + assert_array_almost_equal(denomp.c, [-1.0/3, 1.0]) + + nump, denomp = pade(an, 2, 1) + assert_array_almost_equal(nump.c, [1.0/3, 1.0]) + assert_array_almost_equal(denomp.c, [1.0/6, -2.0/3, 1.0]) + + nump, denomp = pade(an, 3, 0) + assert_array_almost_equal(nump.c, [1.0]) + assert_array_almost_equal(denomp.c, [-1.0/6, 0.5, -1.0, 1.0]) + + # Testing reducing array. + nump, denomp = pade(an, 0, 2) + assert_array_almost_equal(nump.c, [0.5, 1.0, 1.0]) + assert_array_almost_equal(denomp.c, [1.0]) + + nump, denomp = pade(an, 1, 1) + assert_array_almost_equal(nump.c, [1.0/2, 1.0]) + assert_array_almost_equal(denomp.c, [-1.0/2, 1.0]) + + nump, denomp = pade(an, 2, 0) + assert_array_almost_equal(nump.c, [1.0]) + assert_array_almost_equal(denomp.c, [1.0/2, -1.0, 1.0]) + + +def test_pade_ints(): + # Simple test sequences (one of ints, one of floats). + an_int = [1, 2, 3, 4] + an_flt = [1.0, 2.0, 3.0, 4.0] + + # Make sure integer arrays give the same result as float arrays with same values. + for i in range(0, len(an_int)): + for j in range(0, len(an_int) - i): + + # Create float and int pade approximation for given order. + nump_int, denomp_int = pade(an_int, i, j) + nump_flt, denomp_flt = pade(an_flt, i, j) + + # Check that they are the same. + assert_array_equal(nump_int.c, nump_flt.c) + assert_array_equal(denomp_int.c, denomp_flt.c) + + +def test_pade_complex(): + # Test sequence with known solutions - see page 6 of 10.1109/PESGM.2012.6344759. + # Variable x is parameter - these tests will work with any complex number. + x = 0.2 + 0.6j + an = [1.0, x, -x*x.conjugate(), x.conjugate()*(x**2) + x*(x.conjugate()**2), + -(x**3)*x.conjugate() - 3*(x*x.conjugate())**2 - x*(x.conjugate()**3)] + + nump, denomp = pade(an, 1, 1) + assert_array_almost_equal(nump.c, [x + x.conjugate(), 1.0]) + assert_array_almost_equal(denomp.c, [x.conjugate(), 1.0]) + + nump, denomp = pade(an, 1, 2) + assert_array_almost_equal(nump.c, [x**2, 2*x + x.conjugate(), 1.0]) + assert_array_almost_equal(denomp.c, [x + x.conjugate(), 1.0]) + + nump, denomp = pade(an, 2, 2) + assert_array_almost_equal( + nump.c, + [x**2 + x*x.conjugate() + x.conjugate()**2, 2*(x + x.conjugate()), 1.0] + ) + assert_array_almost_equal(denomp.c, [x.conjugate()**2, x + 2*x.conjugate(), 1.0]) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_polyint.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_polyint.py new file mode 100644 index 0000000000000000000000000000000000000000..31215b1e986a9fc57a3b0f18823bcc6ffd9e81f9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_polyint.py @@ -0,0 +1,941 @@ +import warnings +import io +import numpy as np + +from numpy.testing import ( + assert_almost_equal, assert_array_equal, assert_array_almost_equal, + assert_allclose, assert_equal, assert_) +from pytest import raises as assert_raises +import pytest + +from scipy.interpolate import ( + KroghInterpolator, krogh_interpolate, + BarycentricInterpolator, barycentric_interpolate, + approximate_taylor_polynomial, CubicHermiteSpline, pchip, + PchipInterpolator, pchip_interpolate, Akima1DInterpolator, CubicSpline, + make_interp_spline) + + +def check_shape(interpolator_cls, x_shape, y_shape, deriv_shape=None, axis=0, + extra_args={}): + np.random.seed(1234) + + x = [-1, 0, 1, 2, 3, 4] + s = list(range(1, len(y_shape)+1)) + s.insert(axis % (len(y_shape)+1), 0) + y = np.random.rand(*((6,) + y_shape)).transpose(s) + + xi = np.zeros(x_shape) + if interpolator_cls is CubicHermiteSpline: + dydx = np.random.rand(*((6,) + y_shape)).transpose(s) + yi = interpolator_cls(x, y, dydx, axis=axis, **extra_args)(xi) + else: + yi = interpolator_cls(x, y, axis=axis, **extra_args)(xi) + + target_shape = ((deriv_shape or ()) + y.shape[:axis] + + x_shape + y.shape[axis:][1:]) + assert_equal(yi.shape, target_shape) + + # check it works also with lists + if x_shape and y.size > 0: + if interpolator_cls is CubicHermiteSpline: + interpolator_cls(list(x), list(y), list(dydx), axis=axis, + **extra_args)(list(xi)) + else: + interpolator_cls(list(x), list(y), axis=axis, + **extra_args)(list(xi)) + + # check also values + if xi.size > 0 and deriv_shape is None: + bs_shape = y.shape[:axis] + (1,)*len(x_shape) + y.shape[axis:][1:] + yv = y[((slice(None,),)*(axis % y.ndim)) + (1,)] + yv = yv.reshape(bs_shape) + + yi, y = np.broadcast_arrays(yi, yv) + assert_allclose(yi, y) + + +SHAPES = [(), (0,), (1,), (6, 2, 5)] + + +def test_shapes(): + + def spl_interp(x, y, axis): + return make_interp_spline(x, y, axis=axis) + + for ip in [KroghInterpolator, BarycentricInterpolator, CubicHermiteSpline, + pchip, Akima1DInterpolator, CubicSpline, spl_interp]: + for s1 in SHAPES: + for s2 in SHAPES: + for axis in range(-len(s2), len(s2)): + if ip != CubicSpline: + check_shape(ip, s1, s2, None, axis) + else: + for bc in ['natural', 'clamped']: + extra = {'bc_type': bc} + check_shape(ip, s1, s2, None, axis, extra) + +def test_derivs_shapes(): + for ip in [KroghInterpolator, BarycentricInterpolator]: + def interpolator_derivs(x, y, axis=0): + return ip(x, y, axis).derivatives + + for s1 in SHAPES: + for s2 in SHAPES: + for axis in range(-len(s2), len(s2)): + check_shape(interpolator_derivs, s1, s2, (6,), axis) + + +def test_deriv_shapes(): + def krogh_deriv(x, y, axis=0): + return KroghInterpolator(x, y, axis).derivative + + def bary_deriv(x, y, axis=0): + return BarycentricInterpolator(x, y, axis).derivative + + def pchip_deriv(x, y, axis=0): + return pchip(x, y, axis).derivative() + + def pchip_deriv2(x, y, axis=0): + return pchip(x, y, axis).derivative(2) + + def pchip_antideriv(x, y, axis=0): + return pchip(x, y, axis).antiderivative() + + def pchip_antideriv2(x, y, axis=0): + return pchip(x, y, axis).antiderivative(2) + + def pchip_deriv_inplace(x, y, axis=0): + class P(PchipInterpolator): + def __call__(self, x): + return PchipInterpolator.__call__(self, x, 1) + pass + return P(x, y, axis) + + def akima_deriv(x, y, axis=0): + return Akima1DInterpolator(x, y, axis).derivative() + + def akima_antideriv(x, y, axis=0): + return Akima1DInterpolator(x, y, axis).antiderivative() + + def cspline_deriv(x, y, axis=0): + return CubicSpline(x, y, axis).derivative() + + def cspline_antideriv(x, y, axis=0): + return CubicSpline(x, y, axis).antiderivative() + + def bspl_deriv(x, y, axis=0): + return make_interp_spline(x, y, axis=axis).derivative() + + def bspl_antideriv(x, y, axis=0): + return make_interp_spline(x, y, axis=axis).antiderivative() + + for ip in [krogh_deriv, bary_deriv, pchip_deriv, pchip_deriv2, pchip_deriv_inplace, + pchip_antideriv, pchip_antideriv2, akima_deriv, akima_antideriv, + cspline_deriv, cspline_antideriv, bspl_deriv, bspl_antideriv]: + for s1 in SHAPES: + for s2 in SHAPES: + for axis in range(-len(s2), len(s2)): + check_shape(ip, s1, s2, (), axis) + + +def test_complex(): + x = [1, 2, 3, 4] + y = [1, 2, 1j, 3] + + for ip in [KroghInterpolator, BarycentricInterpolator, CubicSpline]: + p = ip(x, y) + assert_allclose(y, p(x)) + + dydx = [0, -1j, 2, 3j] + p = CubicHermiteSpline(x, y, dydx) + assert_allclose(y, p(x)) + assert_allclose(dydx, p(x, 1)) + + +class TestKrogh: + def setup_method(self): + self.true_poly = np.polynomial.Polynomial([-4, 5, 1, 3, -2]) + self.test_xs = np.linspace(-1,1,100) + self.xs = np.linspace(-1,1,5) + self.ys = self.true_poly(self.xs) + + def test_lagrange(self): + P = KroghInterpolator(self.xs,self.ys) + assert_almost_equal(self.true_poly(self.test_xs),P(self.test_xs)) + + def test_scalar(self): + P = KroghInterpolator(self.xs,self.ys) + assert_almost_equal(self.true_poly(7),P(7)) + assert_almost_equal(self.true_poly(np.array(7)), P(np.array(7))) + + def test_derivatives(self): + P = KroghInterpolator(self.xs,self.ys) + D = P.derivatives(self.test_xs) + for i in range(D.shape[0]): + assert_almost_equal(self.true_poly.deriv(i)(self.test_xs), + D[i]) + + def test_low_derivatives(self): + P = KroghInterpolator(self.xs,self.ys) + D = P.derivatives(self.test_xs,len(self.xs)+2) + for i in range(D.shape[0]): + assert_almost_equal(self.true_poly.deriv(i)(self.test_xs), + D[i]) + + def test_derivative(self): + P = KroghInterpolator(self.xs,self.ys) + m = 10 + r = P.derivatives(self.test_xs,m) + for i in range(m): + assert_almost_equal(P.derivative(self.test_xs,i),r[i]) + + def test_high_derivative(self): + P = KroghInterpolator(self.xs,self.ys) + for i in range(len(self.xs), 2*len(self.xs)): + assert_almost_equal(P.derivative(self.test_xs,i), + np.zeros(len(self.test_xs))) + + def test_ndim_derivatives(self): + poly1 = self.true_poly + poly2 = np.polynomial.Polynomial([-2, 5, 3, -1]) + poly3 = np.polynomial.Polynomial([12, -3, 4, -5, 6]) + ys = np.stack((poly1(self.xs), poly2(self.xs), poly3(self.xs)), axis=-1) + + P = KroghInterpolator(self.xs, ys, axis=0) + D = P.derivatives(self.test_xs) + for i in range(D.shape[0]): + assert_allclose(D[i], + np.stack((poly1.deriv(i)(self.test_xs), + poly2.deriv(i)(self.test_xs), + poly3.deriv(i)(self.test_xs)), + axis=-1)) + + def test_ndim_derivative(self): + poly1 = self.true_poly + poly2 = np.polynomial.Polynomial([-2, 5, 3, -1]) + poly3 = np.polynomial.Polynomial([12, -3, 4, -5, 6]) + ys = np.stack((poly1(self.xs), poly2(self.xs), poly3(self.xs)), axis=-1) + + P = KroghInterpolator(self.xs, ys, axis=0) + for i in range(P.n): + assert_allclose(P.derivative(self.test_xs, i), + np.stack((poly1.deriv(i)(self.test_xs), + poly2.deriv(i)(self.test_xs), + poly3.deriv(i)(self.test_xs)), + axis=-1)) + + def test_hermite(self): + P = KroghInterpolator(self.xs,self.ys) + assert_almost_equal(self.true_poly(self.test_xs),P(self.test_xs)) + + def test_vector(self): + xs = [0, 1, 2] + ys = np.array([[0,1],[1,0],[2,1]]) + P = KroghInterpolator(xs,ys) + Pi = [KroghInterpolator(xs,ys[:,i]) for i in range(ys.shape[1])] + test_xs = np.linspace(-1,3,100) + assert_almost_equal(P(test_xs), + np.asarray([p(test_xs) for p in Pi]).T) + assert_almost_equal(P.derivatives(test_xs), + np.transpose(np.asarray([p.derivatives(test_xs) for p in Pi]), + (1,2,0))) + + def test_empty(self): + P = KroghInterpolator(self.xs,self.ys) + assert_array_equal(P([]), []) + + def test_shapes_scalarvalue(self): + P = KroghInterpolator(self.xs,self.ys) + assert_array_equal(np.shape(P(0)), ()) + assert_array_equal(np.shape(P(np.array(0))), ()) + assert_array_equal(np.shape(P([0])), (1,)) + assert_array_equal(np.shape(P([0,1])), (2,)) + + def test_shapes_scalarvalue_derivative(self): + P = KroghInterpolator(self.xs,self.ys) + n = P.n + assert_array_equal(np.shape(P.derivatives(0)), (n,)) + assert_array_equal(np.shape(P.derivatives(np.array(0))), (n,)) + assert_array_equal(np.shape(P.derivatives([0])), (n,1)) + assert_array_equal(np.shape(P.derivatives([0,1])), (n,2)) + + def test_shapes_vectorvalue(self): + P = KroghInterpolator(self.xs,np.outer(self.ys,np.arange(3))) + assert_array_equal(np.shape(P(0)), (3,)) + assert_array_equal(np.shape(P([0])), (1,3)) + assert_array_equal(np.shape(P([0,1])), (2,3)) + + def test_shapes_1d_vectorvalue(self): + P = KroghInterpolator(self.xs,np.outer(self.ys,[1])) + assert_array_equal(np.shape(P(0)), (1,)) + assert_array_equal(np.shape(P([0])), (1,1)) + assert_array_equal(np.shape(P([0,1])), (2,1)) + + def test_shapes_vectorvalue_derivative(self): + P = KroghInterpolator(self.xs,np.outer(self.ys,np.arange(3))) + n = P.n + assert_array_equal(np.shape(P.derivatives(0)), (n,3)) + assert_array_equal(np.shape(P.derivatives([0])), (n,1,3)) + assert_array_equal(np.shape(P.derivatives([0,1])), (n,2,3)) + + def test_wrapper(self): + P = KroghInterpolator(self.xs, self.ys) + ki = krogh_interpolate + assert_almost_equal(P(self.test_xs), ki(self.xs, self.ys, self.test_xs)) + assert_almost_equal(P.derivative(self.test_xs, 2), + ki(self.xs, self.ys, self.test_xs, der=2)) + assert_almost_equal(P.derivatives(self.test_xs, 2), + ki(self.xs, self.ys, self.test_xs, der=[0, 1])) + + def test_int_inputs(self): + # Check input args are cast correctly to floats, gh-3669 + x = [0, 234, 468, 702, 936, 1170, 1404, 2340, 3744, 6084, 8424, + 13104, 60000] + offset_cdf = np.array([-0.95, -0.86114777, -0.8147762, -0.64072425, + -0.48002351, -0.34925329, -0.26503107, + -0.13148093, -0.12988833, -0.12979296, + -0.12973574, -0.08582937, 0.05]) + f = KroghInterpolator(x, offset_cdf) + + assert_allclose(abs((f(x) - offset_cdf) / f.derivative(x, 1)), + 0, atol=1e-10) + + def test_derivatives_complex(self): + # regression test for gh-7381: krogh.derivatives(0) fails complex y + x, y = np.array([-1, -1, 0, 1, 1]), np.array([1, 1.0j, 0, -1, 1.0j]) + func = KroghInterpolator(x, y) + cmplx = func.derivatives(0) + + cmplx2 = (KroghInterpolator(x, y.real).derivatives(0) + + 1j*KroghInterpolator(x, y.imag).derivatives(0)) + assert_allclose(cmplx, cmplx2, atol=1e-15) + + def test_high_degree_warning(self): + with pytest.warns(UserWarning, match="40 degrees provided,"): + KroghInterpolator(np.arange(40), np.ones(40)) + + +class TestTaylor: + def test_exponential(self): + degree = 5 + p = approximate_taylor_polynomial(np.exp, 0, degree, 1, 15) + for i in range(degree+1): + assert_almost_equal(p(0),1) + p = p.deriv() + assert_almost_equal(p(0),0) + + +class TestBarycentric: + def setup_method(self): + self.true_poly = np.polynomial.Polynomial([-4, 5, 1, 3, -2]) + self.test_xs = np.linspace(-1, 1, 100) + self.xs = np.linspace(-1, 1, 5) + self.ys = self.true_poly(self.xs) + + def test_lagrange(self): + P = BarycentricInterpolator(self.xs, self.ys) + assert_allclose(P(self.test_xs), self.true_poly(self.test_xs)) + + def test_scalar(self): + P = BarycentricInterpolator(self.xs, self.ys) + assert_allclose(P(7), self.true_poly(7)) + assert_allclose(P(np.array(7)), self.true_poly(np.array(7))) + + def test_derivatives(self): + P = BarycentricInterpolator(self.xs, self.ys) + D = P.derivatives(self.test_xs) + for i in range(D.shape[0]): + assert_allclose(self.true_poly.deriv(i)(self.test_xs), D[i]) + + def test_low_derivatives(self): + P = BarycentricInterpolator(self.xs, self.ys) + D = P.derivatives(self.test_xs, len(self.xs)+2) + for i in range(D.shape[0]): + assert_allclose(self.true_poly.deriv(i)(self.test_xs), + D[i], + atol=1e-12) + + def test_derivative(self): + P = BarycentricInterpolator(self.xs, self.ys) + m = 10 + r = P.derivatives(self.test_xs, m) + for i in range(m): + assert_allclose(P.derivative(self.test_xs, i), r[i]) + + def test_high_derivative(self): + P = BarycentricInterpolator(self.xs, self.ys) + for i in range(len(self.xs), 5*len(self.xs)): + assert_allclose(P.derivative(self.test_xs, i), + np.zeros(len(self.test_xs))) + + def test_ndim_derivatives(self): + poly1 = self.true_poly + poly2 = np.polynomial.Polynomial([-2, 5, 3, -1]) + poly3 = np.polynomial.Polynomial([12, -3, 4, -5, 6]) + ys = np.stack((poly1(self.xs), poly2(self.xs), poly3(self.xs)), axis=-1) + + P = BarycentricInterpolator(self.xs, ys, axis=0) + D = P.derivatives(self.test_xs) + for i in range(D.shape[0]): + assert_allclose(D[i], + np.stack((poly1.deriv(i)(self.test_xs), + poly2.deriv(i)(self.test_xs), + poly3.deriv(i)(self.test_xs)), + axis=-1), + atol=1e-12) + + def test_ndim_derivative(self): + poly1 = self.true_poly + poly2 = np.polynomial.Polynomial([-2, 5, 3, -1]) + poly3 = np.polynomial.Polynomial([12, -3, 4, -5, 6]) + ys = np.stack((poly1(self.xs), poly2(self.xs), poly3(self.xs)), axis=-1) + + P = BarycentricInterpolator(self.xs, ys, axis=0) + for i in range(P.n): + assert_allclose(P.derivative(self.test_xs, i), + np.stack((poly1.deriv(i)(self.test_xs), + poly2.deriv(i)(self.test_xs), + poly3.deriv(i)(self.test_xs)), + axis=-1), + atol=1e-12) + + def test_delayed(self): + P = BarycentricInterpolator(self.xs) + P.set_yi(self.ys) + assert_almost_equal(self.true_poly(self.test_xs), P(self.test_xs)) + + def test_append(self): + P = BarycentricInterpolator(self.xs[:3], self.ys[:3]) + P.add_xi(self.xs[3:], self.ys[3:]) + assert_almost_equal(self.true_poly(self.test_xs), P(self.test_xs)) + + def test_vector(self): + xs = [0, 1, 2] + ys = np.array([[0, 1], [1, 0], [2, 1]]) + BI = BarycentricInterpolator + P = BI(xs, ys) + Pi = [BI(xs, ys[:, i]) for i in range(ys.shape[1])] + test_xs = np.linspace(-1, 3, 100) + assert_almost_equal(P(test_xs), + np.asarray([p(test_xs) for p in Pi]).T) + + def test_shapes_scalarvalue(self): + P = BarycentricInterpolator(self.xs, self.ys) + assert_array_equal(np.shape(P(0)), ()) + assert_array_equal(np.shape(P(np.array(0))), ()) + assert_array_equal(np.shape(P([0])), (1,)) + assert_array_equal(np.shape(P([0, 1])), (2,)) + + def test_shapes_scalarvalue_derivative(self): + P = BarycentricInterpolator(self.xs,self.ys) + n = P.n + assert_array_equal(np.shape(P.derivatives(0)), (n,)) + assert_array_equal(np.shape(P.derivatives(np.array(0))), (n,)) + assert_array_equal(np.shape(P.derivatives([0])), (n,1)) + assert_array_equal(np.shape(P.derivatives([0,1])), (n,2)) + + def test_shapes_vectorvalue(self): + P = BarycentricInterpolator(self.xs, np.outer(self.ys, np.arange(3))) + assert_array_equal(np.shape(P(0)), (3,)) + assert_array_equal(np.shape(P([0])), (1, 3)) + assert_array_equal(np.shape(P([0, 1])), (2, 3)) + + def test_shapes_1d_vectorvalue(self): + P = BarycentricInterpolator(self.xs, np.outer(self.ys, [1])) + assert_array_equal(np.shape(P(0)), (1,)) + assert_array_equal(np.shape(P([0])), (1, 1)) + assert_array_equal(np.shape(P([0,1])), (2, 1)) + + def test_shapes_vectorvalue_derivative(self): + P = BarycentricInterpolator(self.xs,np.outer(self.ys,np.arange(3))) + n = P.n + assert_array_equal(np.shape(P.derivatives(0)), (n,3)) + assert_array_equal(np.shape(P.derivatives([0])), (n,1,3)) + assert_array_equal(np.shape(P.derivatives([0,1])), (n,2,3)) + + def test_wrapper(self): + P = BarycentricInterpolator(self.xs, self.ys) + bi = barycentric_interpolate + assert_allclose(P(self.test_xs), bi(self.xs, self.ys, self.test_xs)) + assert_allclose(P.derivative(self.test_xs, 2), + bi(self.xs, self.ys, self.test_xs, der=2)) + assert_allclose(P.derivatives(self.test_xs, 2), + bi(self.xs, self.ys, self.test_xs, der=[0, 1])) + + def test_int_input(self): + x = 1000 * np.arange(1, 11) # np.prod(x[-1] - x[:-1]) overflows + y = np.arange(1, 11) + value = barycentric_interpolate(x, y, 1000 * 9.5) + assert_almost_equal(value, 9.5) + + def test_large_chebyshev(self): + # The weights for Chebyshev points of the second kind have analytically + # solvable weights. Naive calculation of barycentric weights will fail + # for large N because of numerical underflow and overflow. We test + # correctness for large N against analytical Chebyshev weights. + + # Without capacity scaling or permutation, n=800 fails, + # With just capacity scaling, n=1097 fails + # With both capacity scaling and random permutation, n=30000 succeeds + n = 1100 + j = np.arange(n + 1).astype(np.float64) + x = np.cos(j * np.pi / n) + + # See page 506 of Berrut and Trefethen 2004 for this formula + w = (-1) ** j + w[0] *= 0.5 + w[-1] *= 0.5 + + P = BarycentricInterpolator(x) + + # It's okay to have a constant scaling factor in the weights because it + # cancels out in the evaluation of the polynomial. + factor = P.wi[0] + assert_almost_equal(P.wi / (2 * factor), w) + + def test_warning(self): + # Test if the divide-by-zero warning is properly ignored when computing + # interpolated values equals to interpolation points + P = BarycentricInterpolator([0, 1], [1, 2]) + with np.errstate(divide='raise'): + yi = P(P.xi) + + # Check if the interpolated values match the input values + # at the nodes + assert_almost_equal(yi, P.yi.ravel()) + + def test_repeated_node(self): + # check that a repeated node raises a ValueError + # (computing the weights requires division by xi[i] - xi[j]) + xis = np.array([0.1, 0.5, 0.9, 0.5]) + ys = np.array([1, 2, 3, 4]) + with pytest.raises(ValueError, + match="Interpolation points xi must be distinct."): + BarycentricInterpolator(xis, ys) + + +class TestPCHIP: + def _make_random(self, npts=20): + np.random.seed(1234) + xi = np.sort(np.random.random(npts)) + yi = np.random.random(npts) + return pchip(xi, yi), xi, yi + + def test_overshoot(self): + # PCHIP should not overshoot + p, xi, yi = self._make_random() + for i in range(len(xi)-1): + x1, x2 = xi[i], xi[i+1] + y1, y2 = yi[i], yi[i+1] + if y1 > y2: + y1, y2 = y2, y1 + xp = np.linspace(x1, x2, 10) + yp = p(xp) + assert_(((y1 <= yp + 1e-15) & (yp <= y2 + 1e-15)).all()) + + def test_monotone(self): + # PCHIP should preserve monotonicty + p, xi, yi = self._make_random() + for i in range(len(xi)-1): + x1, x2 = xi[i], xi[i+1] + y1, y2 = yi[i], yi[i+1] + xp = np.linspace(x1, x2, 10) + yp = p(xp) + assert_(((y2-y1) * (yp[1:] - yp[:1]) > 0).all()) + + def test_cast(self): + # regression test for integer input data, see gh-3453 + data = np.array([[0, 4, 12, 27, 47, 60, 79, 87, 99, 100], + [-33, -33, -19, -2, 12, 26, 38, 45, 53, 55]]) + xx = np.arange(100) + curve = pchip(data[0], data[1])(xx) + + data1 = data * 1.0 + curve1 = pchip(data1[0], data1[1])(xx) + + assert_allclose(curve, curve1, atol=1e-14, rtol=1e-14) + + def test_nag(self): + # Example from NAG C implementation, + # http://nag.com/numeric/cl/nagdoc_cl25/html/e01/e01bec.html + # suggested in gh-5326 as a smoke test for the way the derivatives + # are computed (see also gh-3453) + dataStr = ''' + 7.99 0.00000E+0 + 8.09 0.27643E-4 + 8.19 0.43750E-1 + 8.70 0.16918E+0 + 9.20 0.46943E+0 + 10.00 0.94374E+0 + 12.00 0.99864E+0 + 15.00 0.99992E+0 + 20.00 0.99999E+0 + ''' + data = np.loadtxt(io.StringIO(dataStr)) + pch = pchip(data[:,0], data[:,1]) + + resultStr = ''' + 7.9900 0.0000 + 9.1910 0.4640 + 10.3920 0.9645 + 11.5930 0.9965 + 12.7940 0.9992 + 13.9950 0.9998 + 15.1960 0.9999 + 16.3970 1.0000 + 17.5980 1.0000 + 18.7990 1.0000 + 20.0000 1.0000 + ''' + result = np.loadtxt(io.StringIO(resultStr)) + assert_allclose(result[:,1], pch(result[:,0]), rtol=0., atol=5e-5) + + def test_endslopes(self): + # this is a smoke test for gh-3453: PCHIP interpolator should not + # set edge slopes to zero if the data do not suggest zero edge derivatives + x = np.array([0.0, 0.1, 0.25, 0.35]) + y1 = np.array([279.35, 0.5e3, 1.0e3, 2.5e3]) + y2 = np.array([279.35, 2.5e3, 1.50e3, 1.0e3]) + for pp in (pchip(x, y1), pchip(x, y2)): + for t in (x[0], x[-1]): + assert_(pp(t, 1) != 0) + + def test_all_zeros(self): + x = np.arange(10) + y = np.zeros_like(x) + + # this should work and not generate any warnings + with warnings.catch_warnings(): + warnings.filterwarnings('error') + pch = pchip(x, y) + + xx = np.linspace(0, 9, 101) + assert_equal(pch(xx), 0.) + + def test_two_points(self): + # regression test for gh-6222: pchip([0, 1], [0, 1]) fails because + # it tries to use a three-point scheme to estimate edge derivatives, + # while there are only two points available. + # Instead, it should construct a linear interpolator. + x = np.linspace(0, 1, 11) + p = pchip([0, 1], [0, 2]) + assert_allclose(p(x), 2*x, atol=1e-15) + + def test_pchip_interpolate(self): + assert_array_almost_equal( + pchip_interpolate([1,2,3], [4,5,6], [0.5], der=1), + [1.]) + + assert_array_almost_equal( + pchip_interpolate([1,2,3], [4,5,6], [0.5], der=0), + [3.5]) + + assert_array_almost_equal( + pchip_interpolate([1,2,3], [4,5,6], [0.5], der=[0, 1]), + [[3.5], [1]]) + + def test_roots(self): + # regression test for gh-6357: .roots method should work + p = pchip([0, 1], [-1, 1]) + r = p.roots() + assert_allclose(r, 0.5) + + +class TestCubicSpline: + @staticmethod + def check_correctness(S, bc_start='not-a-knot', bc_end='not-a-knot', + tol=1e-14): + """Check that spline coefficients satisfy the continuity and boundary + conditions.""" + x = S.x + c = S.c + dx = np.diff(x) + dx = dx.reshape([dx.shape[0]] + [1] * (c.ndim - 2)) + dxi = dx[:-1] + + # Check C2 continuity. + assert_allclose(c[3, 1:], c[0, :-1] * dxi**3 + c[1, :-1] * dxi**2 + + c[2, :-1] * dxi + c[3, :-1], rtol=tol, atol=tol) + assert_allclose(c[2, 1:], 3 * c[0, :-1] * dxi**2 + + 2 * c[1, :-1] * dxi + c[2, :-1], rtol=tol, atol=tol) + assert_allclose(c[1, 1:], 3 * c[0, :-1] * dxi + c[1, :-1], + rtol=tol, atol=tol) + + # Check that we found a parabola, the third derivative is 0. + if x.size == 3 and bc_start == 'not-a-knot' and bc_end == 'not-a-knot': + assert_allclose(c[0], 0, rtol=tol, atol=tol) + return + + # Check periodic boundary conditions. + if bc_start == 'periodic': + assert_allclose(S(x[0], 0), S(x[-1], 0), rtol=tol, atol=tol) + assert_allclose(S(x[0], 1), S(x[-1], 1), rtol=tol, atol=tol) + assert_allclose(S(x[0], 2), S(x[-1], 2), rtol=tol, atol=tol) + return + + # Check other boundary conditions. + if bc_start == 'not-a-knot': + if x.size == 2: + slope = (S(x[1]) - S(x[0])) / dx[0] + assert_allclose(S(x[0], 1), slope, rtol=tol, atol=tol) + else: + assert_allclose(c[0, 0], c[0, 1], rtol=tol, atol=tol) + elif bc_start == 'clamped': + assert_allclose(S(x[0], 1), 0, rtol=tol, atol=tol) + elif bc_start == 'natural': + assert_allclose(S(x[0], 2), 0, rtol=tol, atol=tol) + else: + order, value = bc_start + assert_allclose(S(x[0], order), value, rtol=tol, atol=tol) + + if bc_end == 'not-a-knot': + if x.size == 2: + slope = (S(x[1]) - S(x[0])) / dx[0] + assert_allclose(S(x[1], 1), slope, rtol=tol, atol=tol) + else: + assert_allclose(c[0, -1], c[0, -2], rtol=tol, atol=tol) + elif bc_end == 'clamped': + assert_allclose(S(x[-1], 1), 0, rtol=tol, atol=tol) + elif bc_end == 'natural': + assert_allclose(S(x[-1], 2), 0, rtol=2*tol, atol=2*tol) + else: + order, value = bc_end + assert_allclose(S(x[-1], order), value, rtol=tol, atol=tol) + + def check_all_bc(self, x, y, axis): + deriv_shape = list(y.shape) + del deriv_shape[axis] + first_deriv = np.empty(deriv_shape) + first_deriv.fill(2) + second_deriv = np.empty(deriv_shape) + second_deriv.fill(-1) + bc_all = [ + 'not-a-knot', + 'natural', + 'clamped', + (1, first_deriv), + (2, second_deriv) + ] + for bc in bc_all[:3]: + S = CubicSpline(x, y, axis=axis, bc_type=bc) + self.check_correctness(S, bc, bc) + + for bc_start in bc_all: + for bc_end in bc_all: + S = CubicSpline(x, y, axis=axis, bc_type=(bc_start, bc_end)) + self.check_correctness(S, bc_start, bc_end, tol=2e-14) + + def test_general(self): + x = np.array([-1, 0, 0.5, 2, 4, 4.5, 5.5, 9]) + y = np.array([0, -0.5, 2, 3, 2.5, 1, 1, 0.5]) + for n in [2, 3, x.size]: + self.check_all_bc(x[:n], y[:n], 0) + + Y = np.empty((2, n, 2)) + Y[0, :, 0] = y[:n] + Y[0, :, 1] = y[:n] - 1 + Y[1, :, 0] = y[:n] + 2 + Y[1, :, 1] = y[:n] + 3 + self.check_all_bc(x[:n], Y, 1) + + def test_periodic(self): + for n in [2, 3, 5]: + x = np.linspace(0, 2 * np.pi, n) + y = np.cos(x) + S = CubicSpline(x, y, bc_type='periodic') + self.check_correctness(S, 'periodic', 'periodic') + + Y = np.empty((2, n, 2)) + Y[0, :, 0] = y + Y[0, :, 1] = y + 2 + Y[1, :, 0] = y - 1 + Y[1, :, 1] = y + 5 + S = CubicSpline(x, Y, axis=1, bc_type='periodic') + self.check_correctness(S, 'periodic', 'periodic') + + def test_periodic_eval(self): + x = np.linspace(0, 2 * np.pi, 10) + y = np.cos(x) + S = CubicSpline(x, y, bc_type='periodic') + assert_almost_equal(S(1), S(1 + 2 * np.pi), decimal=15) + + def test_second_derivative_continuity_gh_11758(self): + # gh-11758: C2 continuity fail + x = np.array([0.9, 1.3, 1.9, 2.1, 2.6, 3.0, 3.9, 4.4, 4.7, 5.0, 6.0, + 7.0, 8.0, 9.2, 10.5, 11.3, 11.6, 12.0, 12.6, 13.0, 13.3]) + y = np.array([1.3, 1.5, 1.85, 2.1, 2.6, 2.7, 2.4, 2.15, 2.05, 2.1, + 2.25, 2.3, 2.25, 1.95, 1.4, 0.9, 0.7, 0.6, 0.5, 0.4, 1.3]) + S = CubicSpline(x, y, bc_type='periodic', extrapolate='periodic') + self.check_correctness(S, 'periodic', 'periodic') + + def test_three_points(self): + # gh-11758: Fails computing a_m2_m1 + # In this case, s (first derivatives) could be found manually by solving + # system of 2 linear equations. Due to solution of this system, + # s[i] = (h1m2 + h2m1) / (h1 + h2), where h1 = x[1] - x[0], h2 = x[2] - x[1], + # m1 = (y[1] - y[0]) / h1, m2 = (y[2] - y[1]) / h2 + x = np.array([1.0, 2.75, 3.0]) + y = np.array([1.0, 15.0, 1.0]) + S = CubicSpline(x, y, bc_type='periodic') + self.check_correctness(S, 'periodic', 'periodic') + assert_allclose(S.derivative(1)(x), np.array([-48.0, -48.0, -48.0])) + + def test_periodic_three_points_multidim(self): + # make sure one multidimensional interpolator does the same as multiple + # one-dimensional interpolators + x = np.array([0.0, 1.0, 3.0]) + y = np.array([[0.0, 1.0], [1.0, 0.0], [0.0, 1.0]]) + S = CubicSpline(x, y, bc_type="periodic") + self.check_correctness(S, 'periodic', 'periodic') + S0 = CubicSpline(x, y[:, 0], bc_type="periodic") + S1 = CubicSpline(x, y[:, 1], bc_type="periodic") + q = np.linspace(0, 2, 5) + assert_allclose(S(q)[:, 0], S0(q)) + assert_allclose(S(q)[:, 1], S1(q)) + + def test_dtypes(self): + x = np.array([0, 1, 2, 3], dtype=int) + y = np.array([-5, 2, 3, 1], dtype=int) + S = CubicSpline(x, y) + self.check_correctness(S) + + y = np.array([-1+1j, 0.0, 1-1j, 0.5-1.5j]) + S = CubicSpline(x, y) + self.check_correctness(S) + + S = CubicSpline(x, x ** 3, bc_type=("natural", (1, 2j))) + self.check_correctness(S, "natural", (1, 2j)) + + y = np.array([-5, 2, 3, 1]) + S = CubicSpline(x, y, bc_type=[(1, 2 + 0.5j), (2, 0.5 - 1j)]) + self.check_correctness(S, (1, 2 + 0.5j), (2, 0.5 - 1j)) + + def test_small_dx(self): + rng = np.random.RandomState(0) + x = np.sort(rng.uniform(size=100)) + y = 1e4 + rng.uniform(size=100) + S = CubicSpline(x, y) + self.check_correctness(S, tol=1e-13) + + def test_incorrect_inputs(self): + x = np.array([1, 2, 3, 4]) + y = np.array([1, 2, 3, 4]) + xc = np.array([1 + 1j, 2, 3, 4]) + xn = np.array([np.nan, 2, 3, 4]) + xo = np.array([2, 1, 3, 4]) + yn = np.array([np.nan, 2, 3, 4]) + y3 = [1, 2, 3] + x1 = [1] + y1 = [1] + + assert_raises(ValueError, CubicSpline, xc, y) + assert_raises(ValueError, CubicSpline, xn, y) + assert_raises(ValueError, CubicSpline, x, yn) + assert_raises(ValueError, CubicSpline, xo, y) + assert_raises(ValueError, CubicSpline, x, y3) + assert_raises(ValueError, CubicSpline, x[:, np.newaxis], y) + assert_raises(ValueError, CubicSpline, x1, y1) + + wrong_bc = [('periodic', 'clamped'), + ((2, 0), (3, 10)), + ((1, 0), ), + (0., 0.), + 'not-a-typo'] + + for bc_type in wrong_bc: + assert_raises(ValueError, CubicSpline, x, y, 0, bc_type, True) + + # Shapes mismatch when giving arbitrary derivative values: + Y = np.c_[y, y] + bc1 = ('clamped', (1, 0)) + bc2 = ('clamped', (1, [0, 0, 0])) + bc3 = ('clamped', (1, [[0, 0]])) + assert_raises(ValueError, CubicSpline, x, Y, 0, bc1, True) + assert_raises(ValueError, CubicSpline, x, Y, 0, bc2, True) + assert_raises(ValueError, CubicSpline, x, Y, 0, bc3, True) + + # periodic condition, y[-1] must be equal to y[0]: + assert_raises(ValueError, CubicSpline, x, y, 0, 'periodic', True) + + +def test_CubicHermiteSpline_correctness(): + x = [0, 2, 7] + y = [-1, 2, 3] + dydx = [0, 3, 7] + s = CubicHermiteSpline(x, y, dydx) + assert_allclose(s(x), y, rtol=1e-15) + assert_allclose(s(x, 1), dydx, rtol=1e-15) + + +def test_CubicHermiteSpline_error_handling(): + x = [1, 2, 3] + y = [0, 3, 5] + dydx = [1, -1, 2, 3] + assert_raises(ValueError, CubicHermiteSpline, x, y, dydx) + + dydx_with_nan = [1, 0, np.nan] + assert_raises(ValueError, CubicHermiteSpline, x, y, dydx_with_nan) + + +def test_roots_extrapolate_gh_11185(): + x = np.array([0.001, 0.002]) + y = np.array([1.66066935e-06, 1.10410807e-06]) + dy = np.array([-1.60061854, -1.600619]) + p = CubicHermiteSpline(x, y, dy) + + # roots(extrapolate=True) for a polynomial with a single interval + # should return all three real roots + r = p.roots(extrapolate=True) + assert_equal(p.c.shape[1], 1) + assert_equal(r.size, 3) + + +class TestZeroSizeArrays: + # regression tests for gh-17241 : CubicSpline et al must not segfault + # when y.size == 0 + # The two methods below are _almost_ the same, but not quite: + # one is for objects which have the `bc_type` argument (CubicSpline) + # and the other one is for those which do not (Pchip, Akima1D) + + @pytest.mark.parametrize('y', [np.zeros((10, 0, 5)), + np.zeros((10, 5, 0))]) + @pytest.mark.parametrize('bc_type', + ['not-a-knot', 'periodic', 'natural', 'clamped']) + @pytest.mark.parametrize('axis', [0, 1, 2]) + @pytest.mark.parametrize('cls', [make_interp_spline, CubicSpline]) + def test_zero_size(self, cls, y, bc_type, axis): + x = np.arange(10) + xval = np.arange(3) + + obj = cls(x, y, bc_type=bc_type) + assert obj(xval).size == 0 + assert obj(xval).shape == xval.shape + y.shape[1:] + + # Also check with an explicit non-default axis + yt = np.moveaxis(y, 0, axis) # (10, 0, 5) --> (0, 10, 5) if axis=1 etc + + obj = cls(x, yt, bc_type=bc_type, axis=axis) + sh = yt.shape[:axis] + (xval.size, ) + yt.shape[axis+1:] + assert obj(xval).size == 0 + assert obj(xval).shape == sh + + @pytest.mark.parametrize('y', [np.zeros((10, 0, 5)), + np.zeros((10, 5, 0))]) + @pytest.mark.parametrize('axis', [0, 1, 2]) + @pytest.mark.parametrize('cls', [PchipInterpolator, Akima1DInterpolator]) + def test_zero_size_2(self, cls, y, axis): + x = np.arange(10) + xval = np.arange(3) + + obj = cls(x, y) + assert obj(xval).size == 0 + assert obj(xval).shape == xval.shape + y.shape[1:] + + # Also check with an explicit non-default axis + yt = np.moveaxis(y, 0, axis) # (10, 0, 5) --> (0, 10, 5) if axis=1 etc + + obj = cls(x, yt, axis=axis) + sh = yt.shape[:axis] + (xval.size, ) + yt.shape[axis+1:] + assert obj(xval).size == 0 + assert obj(xval).shape == sh diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbf.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbf.py new file mode 100644 index 0000000000000000000000000000000000000000..418042c65a906430ddecc5dabd98af2777747184 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbf.py @@ -0,0 +1,222 @@ +# Created by John Travers, Robert Hetland, 2007 +""" Test functions for rbf module """ + +import numpy as np +from numpy.testing import (assert_, assert_array_almost_equal, + assert_almost_equal) +from numpy import linspace, sin, cos, random, exp, allclose +from scipy.interpolate._rbf import Rbf + +FUNCTIONS = ('multiquadric', 'inverse multiquadric', 'gaussian', + 'cubic', 'quintic', 'thin-plate', 'linear') + + +def check_rbf1d_interpolation(function): + # Check that the Rbf function interpolates through the nodes (1D) + x = linspace(0,10,9) + y = sin(x) + rbf = Rbf(x, y, function=function) + yi = rbf(x) + assert_array_almost_equal(y, yi) + assert_almost_equal(rbf(float(x[0])), y[0]) + + +def check_rbf2d_interpolation(function): + # Check that the Rbf function interpolates through the nodes (2D). + x = random.rand(50,1)*4-2 + y = random.rand(50,1)*4-2 + z = x*exp(-x**2-1j*y**2) + rbf = Rbf(x, y, z, epsilon=2, function=function) + zi = rbf(x, y) + zi.shape = x.shape + assert_array_almost_equal(z, zi) + + +def check_rbf3d_interpolation(function): + # Check that the Rbf function interpolates through the nodes (3D). + x = random.rand(50, 1)*4 - 2 + y = random.rand(50, 1)*4 - 2 + z = random.rand(50, 1)*4 - 2 + d = x*exp(-x**2 - y**2) + rbf = Rbf(x, y, z, d, epsilon=2, function=function) + di = rbf(x, y, z) + di.shape = x.shape + assert_array_almost_equal(di, d) + + +def test_rbf_interpolation(): + for function in FUNCTIONS: + check_rbf1d_interpolation(function) + check_rbf2d_interpolation(function) + check_rbf3d_interpolation(function) + + +def check_2drbf1d_interpolation(function): + # Check that the 2-D Rbf function interpolates through the nodes (1D) + x = linspace(0, 10, 9) + y0 = sin(x) + y1 = cos(x) + y = np.vstack([y0, y1]).T + rbf = Rbf(x, y, function=function, mode='N-D') + yi = rbf(x) + assert_array_almost_equal(y, yi) + assert_almost_equal(rbf(float(x[0])), y[0]) + + +def check_2drbf2d_interpolation(function): + # Check that the 2-D Rbf function interpolates through the nodes (2D). + x = random.rand(50, ) * 4 - 2 + y = random.rand(50, ) * 4 - 2 + z0 = x * exp(-x ** 2 - 1j * y ** 2) + z1 = y * exp(-y ** 2 - 1j * x ** 2) + z = np.vstack([z0, z1]).T + rbf = Rbf(x, y, z, epsilon=2, function=function, mode='N-D') + zi = rbf(x, y) + zi.shape = z.shape + assert_array_almost_equal(z, zi) + + +def check_2drbf3d_interpolation(function): + # Check that the 2-D Rbf function interpolates through the nodes (3D). + x = random.rand(50, ) * 4 - 2 + y = random.rand(50, ) * 4 - 2 + z = random.rand(50, ) * 4 - 2 + d0 = x * exp(-x ** 2 - y ** 2) + d1 = y * exp(-y ** 2 - x ** 2) + d = np.vstack([d0, d1]).T + rbf = Rbf(x, y, z, d, epsilon=2, function=function, mode='N-D') + di = rbf(x, y, z) + di.shape = d.shape + assert_array_almost_equal(di, d) + + +def test_2drbf_interpolation(): + for function in FUNCTIONS: + check_2drbf1d_interpolation(function) + check_2drbf2d_interpolation(function) + check_2drbf3d_interpolation(function) + + +def check_rbf1d_regularity(function, atol): + # Check that the Rbf function approximates a smooth function well away + # from the nodes. + x = linspace(0, 10, 9) + y = sin(x) + rbf = Rbf(x, y, function=function) + xi = linspace(0, 10, 100) + yi = rbf(xi) + msg = "abs-diff: %f" % abs(yi - sin(xi)).max() + assert_(allclose(yi, sin(xi), atol=atol), msg) + + +def test_rbf_regularity(): + tolerances = { + 'multiquadric': 0.1, + 'inverse multiquadric': 0.15, + 'gaussian': 0.15, + 'cubic': 0.15, + 'quintic': 0.1, + 'thin-plate': 0.1, + 'linear': 0.2 + } + for function in FUNCTIONS: + check_rbf1d_regularity(function, tolerances.get(function, 1e-2)) + + +def check_2drbf1d_regularity(function, atol): + # Check that the 2-D Rbf function approximates a smooth function well away + # from the nodes. + x = linspace(0, 10, 9) + y0 = sin(x) + y1 = cos(x) + y = np.vstack([y0, y1]).T + rbf = Rbf(x, y, function=function, mode='N-D') + xi = linspace(0, 10, 100) + yi = rbf(xi) + msg = "abs-diff: %f" % abs(yi - np.vstack([sin(xi), cos(xi)]).T).max() + assert_(allclose(yi, np.vstack([sin(xi), cos(xi)]).T, atol=atol), msg) + + +def test_2drbf_regularity(): + tolerances = { + 'multiquadric': 0.1, + 'inverse multiquadric': 0.15, + 'gaussian': 0.15, + 'cubic': 0.15, + 'quintic': 0.1, + 'thin-plate': 0.15, + 'linear': 0.2 + } + for function in FUNCTIONS: + check_2drbf1d_regularity(function, tolerances.get(function, 1e-2)) + + +def check_rbf1d_stability(function): + # Check that the Rbf function with default epsilon is not subject + # to overshoot. Regression for issue #4523. + # + # Generate some data (fixed random seed hence deterministic) + np.random.seed(1234) + x = np.linspace(0, 10, 50) + z = x + 4.0 * np.random.randn(len(x)) + + rbf = Rbf(x, z, function=function) + xi = np.linspace(0, 10, 1000) + yi = rbf(xi) + + # subtract the linear trend and make sure there no spikes + assert_(np.abs(yi-xi).max() / np.abs(z-x).max() < 1.1) + +def test_rbf_stability(): + for function in FUNCTIONS: + check_rbf1d_stability(function) + + +def test_default_construction(): + # Check that the Rbf class can be constructed with the default + # multiquadric basis function. Regression test for ticket #1228. + x = linspace(0,10,9) + y = sin(x) + rbf = Rbf(x, y) + yi = rbf(x) + assert_array_almost_equal(y, yi) + + +def test_function_is_callable(): + # Check that the Rbf class can be constructed with function=callable. + x = linspace(0,10,9) + y = sin(x) + def linfunc(x): + return x + rbf = Rbf(x, y, function=linfunc) + yi = rbf(x) + assert_array_almost_equal(y, yi) + + +def test_two_arg_function_is_callable(): + # Check that the Rbf class can be constructed with a two argument + # function=callable. + def _func(self, r): + return self.epsilon + r + + x = linspace(0,10,9) + y = sin(x) + rbf = Rbf(x, y, function=_func) + yi = rbf(x) + assert_array_almost_equal(y, yi) + + +def test_rbf_epsilon_none(): + x = linspace(0, 10, 9) + y = sin(x) + Rbf(x, y, epsilon=None) + + +def test_rbf_epsilon_none_collinear(): + # Check that collinear points in one dimension doesn't cause an error + # due to epsilon = 0 + x = [1, 2, 3] + y = [4, 4, 4] + z = [5, 6, 7] + rbf = Rbf(x, y, z, epsilon=None) + assert_(rbf.epsilon > 0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbfinterp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbfinterp.py new file mode 100644 index 0000000000000000000000000000000000000000..74f28dadaf0fcff1fba33124cd8467ad3ce5cd3c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rbfinterp.py @@ -0,0 +1,516 @@ +import pickle +import pytest +import numpy as np +from numpy.linalg import LinAlgError +from numpy.testing import assert_allclose +from scipy.stats.qmc import Halton +from scipy.spatial import cKDTree +from scipy.interpolate._rbfinterp import ( + _AVAILABLE, _SCALE_INVARIANT, _NAME_TO_MIN_DEGREE, _monomial_powers, + RBFInterpolator + ) +from scipy.interpolate import _rbfinterp_pythran + + +def _vandermonde(x, degree): + # Returns a matrix of monomials that span polynomials with the specified + # degree evaluated at x. + powers = _monomial_powers(x.shape[1], degree) + return _rbfinterp_pythran._polynomial_matrix(x, powers) + + +def _1d_test_function(x): + # Test function used in Wahba's "Spline Models for Observational Data". + # domain ~= (0, 3), range ~= (-1.0, 0.2) + x = x[:, 0] + y = 4.26*(np.exp(-x) - 4*np.exp(-2*x) + 3*np.exp(-3*x)) + return y + + +def _2d_test_function(x): + # Franke's test function. + # domain ~= (0, 1) X (0, 1), range ~= (0.0, 1.2) + x1, x2 = x[:, 0], x[:, 1] + term1 = 0.75 * np.exp(-(9*x1-2)**2/4 - (9*x2-2)**2/4) + term2 = 0.75 * np.exp(-(9*x1+1)**2/49 - (9*x2+1)/10) + term3 = 0.5 * np.exp(-(9*x1-7)**2/4 - (9*x2-3)**2/4) + term4 = -0.2 * np.exp(-(9*x1-4)**2 - (9*x2-7)**2) + y = term1 + term2 + term3 + term4 + return y + + +def _is_conditionally_positive_definite(kernel, m): + # Tests whether the kernel is conditionally positive definite of order m. + # See chapter 7 of Fasshauer's "Meshfree Approximation Methods with + # MATLAB". + nx = 10 + ntests = 100 + for ndim in [1, 2, 3, 4, 5]: + # Generate sample points with a Halton sequence to avoid samples that + # are too close to each other, which can make the matrix singular. + seq = Halton(ndim, scramble=False, seed=np.random.RandomState()) + for _ in range(ntests): + x = 2*seq.random(nx) - 1 + A = _rbfinterp_pythran._kernel_matrix(x, kernel) + P = _vandermonde(x, m - 1) + Q, R = np.linalg.qr(P, mode='complete') + # Q2 forms a basis spanning the space where P.T.dot(x) = 0. Project + # A onto this space, and then see if it is positive definite using + # the Cholesky decomposition. If not, then the kernel is not c.p.d. + # of order m. + Q2 = Q[:, P.shape[1]:] + B = Q2.T.dot(A).dot(Q2) + try: + np.linalg.cholesky(B) + except np.linalg.LinAlgError: + return False + + return True + + +# Sorting the parametrize arguments is necessary to avoid a parallelization +# issue described here: https://github.com/pytest-dev/pytest-xdist/issues/432. +@pytest.mark.parametrize('kernel', sorted(_AVAILABLE)) +def test_conditionally_positive_definite(kernel): + # Test if each kernel in _AVAILABLE is conditionally positive definite of + # order m, where m comes from _NAME_TO_MIN_DEGREE. This is a necessary + # condition for the smoothed RBF interpolant to be well-posed in general. + m = _NAME_TO_MIN_DEGREE.get(kernel, -1) + 1 + assert _is_conditionally_positive_definite(kernel, m) + + +class _TestRBFInterpolator: + @pytest.mark.parametrize('kernel', sorted(_SCALE_INVARIANT)) + def test_scale_invariance_1d(self, kernel): + # Verify that the functions in _SCALE_INVARIANT are insensitive to the + # shape parameter (when smoothing == 0) in 1d. + seq = Halton(1, scramble=False, seed=np.random.RandomState()) + x = 3*seq.random(50) + y = _1d_test_function(x) + xitp = 3*seq.random(50) + yitp1 = self.build(x, y, epsilon=1.0, kernel=kernel)(xitp) + yitp2 = self.build(x, y, epsilon=2.0, kernel=kernel)(xitp) + assert_allclose(yitp1, yitp2, atol=1e-8) + + @pytest.mark.parametrize('kernel', sorted(_SCALE_INVARIANT)) + def test_scale_invariance_2d(self, kernel): + # Verify that the functions in _SCALE_INVARIANT are insensitive to the + # shape parameter (when smoothing == 0) in 2d. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + x = seq.random(100) + y = _2d_test_function(x) + xitp = seq.random(100) + yitp1 = self.build(x, y, epsilon=1.0, kernel=kernel)(xitp) + yitp2 = self.build(x, y, epsilon=2.0, kernel=kernel)(xitp) + assert_allclose(yitp1, yitp2, atol=1e-8) + + @pytest.mark.parametrize('kernel', sorted(_AVAILABLE)) + def test_extreme_domains(self, kernel): + # Make sure the interpolant remains numerically stable for very + # large/small domains. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + scale = 1e50 + shift = 1e55 + + x = seq.random(100) + y = _2d_test_function(x) + xitp = seq.random(100) + + if kernel in _SCALE_INVARIANT: + yitp1 = self.build(x, y, kernel=kernel)(xitp) + yitp2 = self.build( + x*scale + shift, y, + kernel=kernel + )(xitp*scale + shift) + else: + yitp1 = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp) + yitp2 = self.build( + x*scale + shift, y, + epsilon=5.0/scale, + kernel=kernel + )(xitp*scale + shift) + + assert_allclose(yitp1, yitp2, atol=1e-8) + + def test_polynomial_reproduction(self): + # If the observed data comes from a polynomial, then the interpolant + # should be able to reproduce the polynomial exactly, provided that + # `degree` is sufficiently high. + rng = np.random.RandomState(0) + seq = Halton(2, scramble=False, seed=rng) + degree = 3 + + x = seq.random(50) + xitp = seq.random(50) + + P = _vandermonde(x, degree) + Pitp = _vandermonde(xitp, degree) + + poly_coeffs = rng.normal(0.0, 1.0, P.shape[1]) + + y = P.dot(poly_coeffs) + yitp1 = Pitp.dot(poly_coeffs) + yitp2 = self.build(x, y, degree=degree)(xitp) + + assert_allclose(yitp1, yitp2, atol=1e-8) + + @pytest.mark.slow + def test_chunking(self, monkeypatch): + # If the observed data comes from a polynomial, then the interpolant + # should be able to reproduce the polynomial exactly, provided that + # `degree` is sufficiently high. + rng = np.random.RandomState(0) + seq = Halton(2, scramble=False, seed=rng) + degree = 3 + + largeN = 1000 + 33 + # this is large to check that chunking of the RBFInterpolator is tested + x = seq.random(50) + xitp = seq.random(largeN) + + P = _vandermonde(x, degree) + Pitp = _vandermonde(xitp, degree) + + poly_coeffs = rng.normal(0.0, 1.0, P.shape[1]) + + y = P.dot(poly_coeffs) + yitp1 = Pitp.dot(poly_coeffs) + interp = self.build(x, y, degree=degree) + ce_real = interp._chunk_evaluator + + def _chunk_evaluator(*args, **kwargs): + kwargs.update(memory_budget=100) + return ce_real(*args, **kwargs) + + monkeypatch.setattr(interp, '_chunk_evaluator', _chunk_evaluator) + yitp2 = interp(xitp) + assert_allclose(yitp1, yitp2, atol=1e-8) + + def test_vector_data(self): + # Make sure interpolating a vector field is the same as interpolating + # each component separately. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + + x = seq.random(100) + xitp = seq.random(100) + + y = np.array([_2d_test_function(x), + _2d_test_function(x[:, ::-1])]).T + + yitp1 = self.build(x, y)(xitp) + yitp2 = self.build(x, y[:, 0])(xitp) + yitp3 = self.build(x, y[:, 1])(xitp) + + assert_allclose(yitp1[:, 0], yitp2) + assert_allclose(yitp1[:, 1], yitp3) + + def test_complex_data(self): + # Interpolating complex input should be the same as interpolating the + # real and complex components. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + + x = seq.random(100) + xitp = seq.random(100) + + y = _2d_test_function(x) + 1j*_2d_test_function(x[:, ::-1]) + + yitp1 = self.build(x, y)(xitp) + yitp2 = self.build(x, y.real)(xitp) + yitp3 = self.build(x, y.imag)(xitp) + + assert_allclose(yitp1.real, yitp2) + assert_allclose(yitp1.imag, yitp3) + + @pytest.mark.parametrize('kernel', sorted(_AVAILABLE)) + def test_interpolation_misfit_1d(self, kernel): + # Make sure that each kernel, with its default `degree` and an + # appropriate `epsilon`, does a good job at interpolation in 1d. + seq = Halton(1, scramble=False, seed=np.random.RandomState()) + + x = 3*seq.random(50) + xitp = 3*seq.random(50) + + y = _1d_test_function(x) + ytrue = _1d_test_function(xitp) + yitp = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp) + + mse = np.mean((yitp - ytrue)**2) + assert mse < 1.0e-4 + + @pytest.mark.parametrize('kernel', sorted(_AVAILABLE)) + def test_interpolation_misfit_2d(self, kernel): + # Make sure that each kernel, with its default `degree` and an + # appropriate `epsilon`, does a good job at interpolation in 2d. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + + x = seq.random(100) + xitp = seq.random(100) + + y = _2d_test_function(x) + ytrue = _2d_test_function(xitp) + yitp = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp) + + mse = np.mean((yitp - ytrue)**2) + assert mse < 2.0e-4 + + @pytest.mark.parametrize('kernel', sorted(_AVAILABLE)) + def test_smoothing_misfit(self, kernel): + # Make sure we can find a smoothing parameter for each kernel that + # removes a sufficient amount of noise. + rng = np.random.RandomState(0) + seq = Halton(1, scramble=False, seed=rng) + + noise = 0.2 + rmse_tol = 0.1 + smoothing_range = 10**np.linspace(-4, 1, 20) + + x = 3*seq.random(100) + y = _1d_test_function(x) + rng.normal(0.0, noise, (100,)) + ytrue = _1d_test_function(x) + rmse_within_tol = False + for smoothing in smoothing_range: + ysmooth = self.build( + x, y, + epsilon=1.0, + smoothing=smoothing, + kernel=kernel)(x) + rmse = np.sqrt(np.mean((ysmooth - ytrue)**2)) + if rmse < rmse_tol: + rmse_within_tol = True + break + + assert rmse_within_tol + + def test_array_smoothing(self): + # Test using an array for `smoothing` to give less weight to a known + # outlier. + rng = np.random.RandomState(0) + seq = Halton(1, scramble=False, seed=rng) + degree = 2 + + x = seq.random(50) + P = _vandermonde(x, degree) + poly_coeffs = rng.normal(0.0, 1.0, P.shape[1]) + y = P.dot(poly_coeffs) + y_with_outlier = np.copy(y) + y_with_outlier[10] += 1.0 + smoothing = np.zeros((50,)) + smoothing[10] = 1000.0 + yitp = self.build(x, y_with_outlier, smoothing=smoothing)(x) + # Should be able to reproduce the uncorrupted data almost exactly. + assert_allclose(yitp, y, atol=1e-4) + + def test_inconsistent_x_dimensions_error(self): + # ValueError should be raised if the observation points and evaluation + # points have a different number of dimensions. + y = Halton(2, scramble=False, seed=np.random.RandomState()).random(10) + d = _2d_test_function(y) + x = Halton(1, scramble=False, seed=np.random.RandomState()).random(10) + match = 'Expected the second axis of `x`' + with pytest.raises(ValueError, match=match): + self.build(y, d)(x) + + def test_inconsistent_d_length_error(self): + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(1) + match = 'Expected the first axis of `d`' + with pytest.raises(ValueError, match=match): + self.build(y, d) + + def test_y_not_2d_error(self): + y = np.linspace(0, 1, 5) + d = np.zeros(5) + match = '`y` must be a 2-dimensional array.' + with pytest.raises(ValueError, match=match): + self.build(y, d) + + def test_inconsistent_smoothing_length_error(self): + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(5) + smoothing = np.ones(1) + match = 'Expected `smoothing` to be' + with pytest.raises(ValueError, match=match): + self.build(y, d, smoothing=smoothing) + + def test_invalid_kernel_name_error(self): + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(5) + match = '`kernel` must be one of' + with pytest.raises(ValueError, match=match): + self.build(y, d, kernel='test') + + def test_epsilon_not_specified_error(self): + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(5) + for kernel in _AVAILABLE: + if kernel in _SCALE_INVARIANT: + continue + + match = '`epsilon` must be specified' + with pytest.raises(ValueError, match=match): + self.build(y, d, kernel=kernel) + + def test_x_not_2d_error(self): + y = np.linspace(0, 1, 5)[:, None] + x = np.linspace(0, 1, 5) + d = np.zeros(5) + match = '`x` must be a 2-dimensional array.' + with pytest.raises(ValueError, match=match): + self.build(y, d)(x) + + def test_not_enough_observations_error(self): + y = np.linspace(0, 1, 1)[:, None] + d = np.zeros(1) + match = 'At least 2 data points are required' + with pytest.raises(ValueError, match=match): + self.build(y, d, kernel='thin_plate_spline') + + def test_degree_warning(self): + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(5) + for kernel, deg in _NAME_TO_MIN_DEGREE.items(): + # Only test for kernels that its minimum degree is not 0. + if deg >= 1: + match = f'`degree` should not be below {deg}' + with pytest.warns(Warning, match=match): + self.build(y, d, epsilon=1.0, kernel=kernel, degree=deg-1) + + def test_minus_one_degree(self): + # Make sure a degree of -1 is accepted without any warning. + y = np.linspace(0, 1, 5)[:, None] + d = np.zeros(5) + for kernel, _ in _NAME_TO_MIN_DEGREE.items(): + self.build(y, d, epsilon=1.0, kernel=kernel, degree=-1) + + def test_rank_error(self): + # An error should be raised when `kernel` is "thin_plate_spline" and + # observations are 2-D and collinear. + y = np.array([[2.0, 0.0], [1.0, 0.0], [0.0, 0.0]]) + d = np.array([0.0, 0.0, 0.0]) + match = 'does not have full column rank' + with pytest.raises(LinAlgError, match=match): + self.build(y, d, kernel='thin_plate_spline')(y) + + def test_single_point(self): + # Make sure interpolation still works with only one point (in 1, 2, and + # 3 dimensions). + for dim in [1, 2, 3]: + y = np.zeros((1, dim)) + d = np.ones((1,)) + f = self.build(y, d, kernel='linear')(y) + assert_allclose(d, f) + + def test_pickleable(self): + # Make sure we can pickle and unpickle the interpolant without any + # changes in the behavior. + seq = Halton(1, scramble=False, seed=np.random.RandomState(2305982309)) + + x = 3*seq.random(50) + xitp = 3*seq.random(50) + + y = _1d_test_function(x) + + interp = self.build(x, y) + + yitp1 = interp(xitp) + yitp2 = pickle.loads(pickle.dumps(interp))(xitp) + + assert_allclose(yitp1, yitp2, atol=1e-16) + + +class TestRBFInterpolatorNeighborsNone(_TestRBFInterpolator): + def build(self, *args, **kwargs): + return RBFInterpolator(*args, **kwargs) + + def test_smoothing_limit_1d(self): + # For large smoothing parameters, the interpolant should approach a + # least squares fit of a polynomial with the specified degree. + seq = Halton(1, scramble=False, seed=np.random.RandomState()) + + degree = 3 + smoothing = 1e8 + + x = 3*seq.random(50) + xitp = 3*seq.random(50) + + y = _1d_test_function(x) + + yitp1 = self.build( + x, y, + degree=degree, + smoothing=smoothing + )(xitp) + + P = _vandermonde(x, degree) + Pitp = _vandermonde(xitp, degree) + yitp2 = Pitp.dot(np.linalg.lstsq(P, y, rcond=None)[0]) + + assert_allclose(yitp1, yitp2, atol=1e-8) + + def test_smoothing_limit_2d(self): + # For large smoothing parameters, the interpolant should approach a + # least squares fit of a polynomial with the specified degree. + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + + degree = 3 + smoothing = 1e8 + + x = seq.random(100) + xitp = seq.random(100) + + y = _2d_test_function(x) + + yitp1 = self.build( + x, y, + degree=degree, + smoothing=smoothing + )(xitp) + + P = _vandermonde(x, degree) + Pitp = _vandermonde(xitp, degree) + yitp2 = Pitp.dot(np.linalg.lstsq(P, y, rcond=None)[0]) + + assert_allclose(yitp1, yitp2, atol=1e-8) + + +class TestRBFInterpolatorNeighbors20(_TestRBFInterpolator): + # RBFInterpolator using 20 nearest neighbors. + def build(self, *args, **kwargs): + return RBFInterpolator(*args, **kwargs, neighbors=20) + + def test_equivalent_to_rbf_interpolator(self): + seq = Halton(2, scramble=False, seed=np.random.RandomState()) + + x = seq.random(100) + xitp = seq.random(100) + + y = _2d_test_function(x) + + yitp1 = self.build(x, y)(xitp) + + yitp2 = [] + tree = cKDTree(x) + for xi in xitp: + _, nbr = tree.query(xi, 20) + yitp2.append(RBFInterpolator(x[nbr], y[nbr])(xi[None])[0]) + + assert_allclose(yitp1, yitp2, atol=1e-8) + + +class TestRBFInterpolatorNeighborsInf(TestRBFInterpolatorNeighborsNone): + # RBFInterpolator using neighbors=np.inf. This should give exactly the same + # results as neighbors=None, but it will be slower. + def build(self, *args, **kwargs): + return RBFInterpolator(*args, **kwargs, neighbors=np.inf) + + def test_equivalent_to_rbf_interpolator(self): + seq = Halton(1, scramble=False, seed=np.random.RandomState()) + + x = 3*seq.random(50) + xitp = 3*seq.random(50) + + y = _1d_test_function(x) + yitp1 = self.build(x, y)(xitp) + yitp2 = RBFInterpolator(x, y)(xitp) + + assert_allclose(yitp1, yitp2, atol=1e-8) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rgi.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rgi.py new file mode 100644 index 0000000000000000000000000000000000000000..95b5e2253031cbd194fffe87c48db0c5ce5bf16d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/interpolate/tests/test_rgi.py @@ -0,0 +1,1115 @@ +import itertools + +import pytest +import numpy as np + +from numpy.testing import (assert_allclose, assert_equal, assert_warns, + assert_array_almost_equal, assert_array_equal) +from pytest import raises as assert_raises + +from scipy.interpolate import (RegularGridInterpolator, interpn, + RectBivariateSpline, + NearestNDInterpolator, LinearNDInterpolator) + +from scipy.sparse._sputils import matrix +from scipy._lib._util import ComplexWarning + + +parametrize_rgi_interp_methods = pytest.mark.parametrize( + "method", RegularGridInterpolator._ALL_METHODS +) + +class TestRegularGridInterpolator: + def _get_sample_4d(self): + # create a 4-D grid of 3 points in each dimension + points = [(0., .5, 1.)] * 4 + values = np.asarray([0., .5, 1.]) + values0 = values[:, np.newaxis, np.newaxis, np.newaxis] + values1 = values[np.newaxis, :, np.newaxis, np.newaxis] + values2 = values[np.newaxis, np.newaxis, :, np.newaxis] + values3 = values[np.newaxis, np.newaxis, np.newaxis, :] + values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000) + return points, values + + def _get_sample_4d_2(self): + # create another 4-D grid of 3 points in each dimension + points = [(0., .5, 1.)] * 2 + [(0., 5., 10.)] * 2 + values = np.asarray([0., .5, 1.]) + values0 = values[:, np.newaxis, np.newaxis, np.newaxis] + values1 = values[np.newaxis, :, np.newaxis, np.newaxis] + values2 = values[np.newaxis, np.newaxis, :, np.newaxis] + values3 = values[np.newaxis, np.newaxis, np.newaxis, :] + values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000) + return points, values + + def _get_sample_4d_3(self): + # create another 4-D grid of 7 points in each dimension + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0)] * 4 + values = np.asarray([0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]) + values0 = values[:, np.newaxis, np.newaxis, np.newaxis] + values1 = values[np.newaxis, :, np.newaxis, np.newaxis] + values2 = values[np.newaxis, np.newaxis, :, np.newaxis] + values3 = values[np.newaxis, np.newaxis, np.newaxis, :] + values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000) + return points, values + + def _get_sample_4d_4(self): + # create another 4-D grid of 2 points in each dimension + points = [(0.0, 1.0)] * 4 + values = np.asarray([0.0, 1.0]) + values0 = values[:, np.newaxis, np.newaxis, np.newaxis] + values1 = values[np.newaxis, :, np.newaxis, np.newaxis] + values2 = values[np.newaxis, np.newaxis, :, np.newaxis] + values3 = values[np.newaxis, np.newaxis, np.newaxis, :] + values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000) + return points, values + + @parametrize_rgi_interp_methods + def test_list_input(self, method): + points, values = self._get_sample_4d_3() + + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + + interp = RegularGridInterpolator(points, + values.tolist(), + method=method) + v1 = interp(sample.tolist()) + interp = RegularGridInterpolator(points, + values, + method=method) + v2 = interp(sample) + assert_allclose(v1, v2) + + @pytest.mark.parametrize('method', ['cubic', 'quintic', 'pchip']) + def test_spline_dim_error(self, method): + points, values = self._get_sample_4d_4() + match = "points in dimension" + + # Check error raise when creating interpolator + with pytest.raises(ValueError, match=match): + RegularGridInterpolator(points, values, method=method) + + # Check error raise when creating interpolator + interp = RegularGridInterpolator(points, values) + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + with pytest.raises(ValueError, match=match): + interp(sample, method=method) + + @pytest.mark.parametrize( + "points_values, sample", + [ + ( + _get_sample_4d, + np.asarray( + [[0.1, 0.1, 1.0, 0.9], + [0.2, 0.1, 0.45, 0.8], + [0.5, 0.5, 0.5, 0.5]] + ), + ), + (_get_sample_4d_2, np.asarray([0.1, 0.1, 10.0, 9.0])), + ], + ) + def test_linear_and_slinear_close(self, points_values, sample): + points, values = points_values(self) + interp = RegularGridInterpolator(points, values, method="linear") + v1 = interp(sample) + interp = RegularGridInterpolator(points, values, method="slinear") + v2 = interp(sample) + assert_allclose(v1, v2) + + def test_derivatives(self): + points, values = self._get_sample_4d() + sample = np.array([[0.1 , 0.1 , 1. , 0.9 ], + [0.2 , 0.1 , 0.45, 0.8 ], + [0.5 , 0.5 , 0.5 , 0.5 ]]) + interp = RegularGridInterpolator(points, values, method="slinear") + + with assert_raises(ValueError): + # wrong number of derivatives (need 4) + interp(sample, nu=1) + + assert_allclose(interp(sample, nu=(1, 0, 0, 0)), + [1, 1, 1], atol=1e-15) + assert_allclose(interp(sample, nu=(0, 1, 0, 0)), + [10, 10, 10], atol=1e-15) + + # 2nd derivatives of a linear function are zero + assert_allclose(interp(sample, nu=(0, 1, 1, 0)), + [0, 0, 0], atol=2e-12) + + @parametrize_rgi_interp_methods + def test_complex(self, method): + if method == "pchip": + pytest.skip("pchip does not make sense for complex data") + points, values = self._get_sample_4d_3() + values = values - 2j*values + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + + interp = RegularGridInterpolator(points, values, method=method) + rinterp = RegularGridInterpolator(points, values.real, method=method) + iinterp = RegularGridInterpolator(points, values.imag, method=method) + + v1 = interp(sample) + v2 = rinterp(sample) + 1j*iinterp(sample) + assert_allclose(v1, v2) + + def test_cubic_vs_pchip(self): + x, y = [1, 2, 3, 4], [1, 2, 3, 4] + xg, yg = np.meshgrid(x, y, indexing='ij') + + values = (lambda x, y: x**4 * y**4)(xg, yg) + cubic = RegularGridInterpolator((x, y), values, method='cubic') + pchip = RegularGridInterpolator((x, y), values, method='pchip') + + vals_cubic = cubic([1.5, 2]) + vals_pchip = pchip([1.5, 2]) + assert not np.allclose(vals_cubic, vals_pchip, atol=1e-14, rtol=0) + + def test_linear_xi1d(self): + points, values = self._get_sample_4d_2() + interp = RegularGridInterpolator(points, values) + sample = np.asarray([0.1, 0.1, 10., 9.]) + wanted = 1001.1 + assert_array_almost_equal(interp(sample), wanted) + + def test_linear_xi3d(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values) + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + wanted = np.asarray([1001.1, 846.2, 555.5]) + assert_array_almost_equal(interp(sample), wanted) + + @pytest.mark.parametrize( + "sample, wanted", + [ + (np.asarray([0.1, 0.1, 0.9, 0.9]), 1100.0), + (np.asarray([0.1, 0.1, 0.1, 0.1]), 0.0), + (np.asarray([0.0, 0.0, 0.0, 0.0]), 0.0), + (np.asarray([1.0, 1.0, 1.0, 1.0]), 1111.0), + (np.asarray([0.1, 0.4, 0.6, 0.9]), 1055.0), + ], + ) + def test_nearest(self, sample, wanted): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values, method="nearest") + assert_array_almost_equal(interp(sample), wanted) + + def test_linear_edges(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values) + sample = np.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.]]) + wanted = np.asarray([0., 1111.]) + assert_array_almost_equal(interp(sample), wanted) + + def test_valid_create(self): + # create a 2-D grid of 3 points in each dimension + points = [(0., .5, 1.), (0., 1., .5)] + values = np.asarray([0., .5, 1.]) + values0 = values[:, np.newaxis] + values1 = values[np.newaxis, :] + values = (values0 + values1 * 10) + assert_raises(ValueError, RegularGridInterpolator, points, values) + points = [((0., .5, 1.), ), (0., .5, 1.)] + assert_raises(ValueError, RegularGridInterpolator, points, values) + points = [(0., .5, .75, 1.), (0., .5, 1.)] + assert_raises(ValueError, RegularGridInterpolator, points, values) + points = [(0., .5, 1.), (0., .5, 1.), (0., .5, 1.)] + assert_raises(ValueError, RegularGridInterpolator, points, values) + points = [(0., .5, 1.), (0., .5, 1.)] + assert_raises(ValueError, RegularGridInterpolator, points, values, + method="undefmethod") + + def test_valid_call(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values) + sample = np.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.]]) + assert_raises(ValueError, interp, sample, "undefmethod") + sample = np.asarray([[0., 0., 0.], [1., 1., 1.]]) + assert_raises(ValueError, interp, sample) + sample = np.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.1]]) + assert_raises(ValueError, interp, sample) + + def test_out_of_bounds_extrap(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values, bounds_error=False, + fill_value=None) + sample = np.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1], + [21, 2.1, -1.1, -11], [2.1, 2.1, -1.1, -1.1]]) + wanted = np.asarray([0., 1111., 11., 11.]) + assert_array_almost_equal(interp(sample, method="nearest"), wanted) + wanted = np.asarray([-111.1, 1222.1, -11068., -1186.9]) + assert_array_almost_equal(interp(sample, method="linear"), wanted) + + def test_out_of_bounds_extrap2(self): + points, values = self._get_sample_4d_2() + interp = RegularGridInterpolator(points, values, bounds_error=False, + fill_value=None) + sample = np.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1], + [21, 2.1, -1.1, -11], [2.1, 2.1, -1.1, -1.1]]) + wanted = np.asarray([0., 11., 11., 11.]) + assert_array_almost_equal(interp(sample, method="nearest"), wanted) + wanted = np.asarray([-12.1, 133.1, -1069., -97.9]) + assert_array_almost_equal(interp(sample, method="linear"), wanted) + + def test_out_of_bounds_fill(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values, bounds_error=False, + fill_value=np.nan) + sample = np.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1], + [2.1, 2.1, -1.1, -1.1]]) + wanted = np.asarray([np.nan, np.nan, np.nan]) + assert_array_almost_equal(interp(sample, method="nearest"), wanted) + assert_array_almost_equal(interp(sample, method="linear"), wanted) + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + wanted = np.asarray([1001.1, 846.2, 555.5]) + assert_array_almost_equal(interp(sample), wanted) + + def test_nearest_compare_qhull(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values, method="nearest") + points_qhull = itertools.product(*points) + points_qhull = [p for p in points_qhull] + points_qhull = np.asarray(points_qhull) + values_qhull = values.reshape(-1) + interp_qhull = NearestNDInterpolator(points_qhull, values_qhull) + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + assert_array_almost_equal(interp(sample), interp_qhull(sample)) + + def test_linear_compare_qhull(self): + points, values = self._get_sample_4d() + interp = RegularGridInterpolator(points, values) + points_qhull = itertools.product(*points) + points_qhull = [p for p in points_qhull] + points_qhull = np.asarray(points_qhull) + values_qhull = values.reshape(-1) + interp_qhull = LinearNDInterpolator(points_qhull, values_qhull) + sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8], + [0.5, 0.5, .5, .5]]) + assert_array_almost_equal(interp(sample), interp_qhull(sample)) + + @pytest.mark.parametrize("method", ["nearest", "linear"]) + def test_duck_typed_values(self, method): + x = np.linspace(0, 2, 5) + y = np.linspace(0, 1, 7) + + values = MyValue((5, 7)) + + interp = RegularGridInterpolator((x, y), values, method=method) + v1 = interp([0.4, 0.7]) + + interp = RegularGridInterpolator((x, y), values._v, method=method) + v2 = interp([0.4, 0.7]) + assert_allclose(v1, v2) + + def test_invalid_fill_value(self): + np.random.seed(1234) + x = np.linspace(0, 2, 5) + y = np.linspace(0, 1, 7) + values = np.random.rand(5, 7) + + # integers can be cast to floats + RegularGridInterpolator((x, y), values, fill_value=1) + + # complex values cannot + assert_raises(ValueError, RegularGridInterpolator, + (x, y), values, fill_value=1+2j) + + def test_fillvalue_type(self): + # from #3703; test that interpolator object construction succeeds + values = np.ones((10, 20, 30), dtype='>f4') + points = [np.arange(n) for n in values.shape] + # xi = [(1, 1, 1)] + RegularGridInterpolator(points, values) + RegularGridInterpolator(points, values, fill_value=0.) + + def test_length_one_axis(self): + # gh-5890, gh-9524 : length-1 axis is legal for method='linear'. + # Along the axis it's linear interpolation; away from the length-1 + # axis, it's an extrapolation, so fill_value should be used. + def f(x, y): + return x + y + x = np.linspace(1, 1, 1) + y = np.linspace(1, 10, 10) + data = f(*np.meshgrid(x, y, indexing="ij", sparse=True)) + + interp = RegularGridInterpolator((x, y), data, method="linear", + bounds_error=False, fill_value=101) + + # check values at the grid + assert_allclose(interp(np.array([[1, 1], [1, 5], [1, 10]])), + [2, 6, 11], + atol=1e-14) + + # check off-grid interpolation is indeed linear + assert_allclose(interp(np.array([[1, 1.4], [1, 5.3], [1, 10]])), + [2.4, 6.3, 11], + atol=1e-14) + + # check exrapolation w/ fill_value + assert_allclose(interp(np.array([1.1, 2.4])), + interp.fill_value, + atol=1e-14) + + # check extrapolation: linear along the `y` axis, const along `x` + interp.fill_value = None + assert_allclose(interp([[1, 0.3], [1, 11.5]]), + [1.3, 12.5], atol=1e-15) + + assert_allclose(interp([[1.5, 0.3], [1.9, 11.5]]), + [1.3, 12.5], atol=1e-15) + + # extrapolation with method='nearest' + interp = RegularGridInterpolator((x, y), data, method="nearest", + bounds_error=False, fill_value=None) + assert_allclose(interp([[1.5, 1.8], [-4, 5.1]]), + [3, 6], + atol=1e-15) + + @pytest.mark.parametrize("fill_value", [None, np.nan, np.pi]) + @pytest.mark.parametrize("method", ['linear', 'nearest']) + def test_length_one_axis2(self, fill_value, method): + options = {"fill_value": fill_value, "bounds_error": False, + "method": method} + + x = np.linspace(0, 2*np.pi, 20) + z = np.sin(x) + + fa = RegularGridInterpolator((x,), z[:], **options) + fb = RegularGridInterpolator((x, [0]), z[:, None], **options) + + x1a = np.linspace(-1, 2*np.pi+1, 100) + za = fa(x1a) + + # evaluated at provided y-value, fb should behave exactly as fa + y1b = np.zeros(100) + zb = fb(np.vstack([x1a, y1b]).T) + assert_allclose(zb, za) + + # evaluated at a different y-value, fb should return fill value + y1b = np.ones(100) + zb = fb(np.vstack([x1a, y1b]).T) + if fill_value is None: + assert_allclose(zb, za) + else: + assert_allclose(zb, fill_value) + + @pytest.mark.parametrize("method", ['nearest', 'linear']) + def test_nan_x_1d(self, method): + # gh-6624 : if x is nan, result should be nan + f = RegularGridInterpolator(([1, 2, 3],), [10, 20, 30], fill_value=1, + bounds_error=False, method=method) + assert np.isnan(f([np.nan])) + + # test arbitrary nan pattern + rng = np.random.default_rng(8143215468) + x = rng.random(size=100)*4 + i = rng.random(size=100) > 0.5 + x[i] = np.nan + with np.errstate(invalid='ignore'): + # out-of-bounds comparisons, `out_of_bounds += x < grid[0]`, + # generate numpy warnings if `x` contains nans. + # These warnings should propagate to user (since `x` is user + # input) and we simply filter them out. + res = f(x) + + assert_equal(res[i], np.nan) + assert_equal(res[~i], f(x[~i])) + + # also test the length-one axis f(nan) + x = [1, 2, 3] + y = [1, ] + data = np.ones((3, 1)) + f = RegularGridInterpolator((x, y), data, fill_value=1, + bounds_error=False, method=method) + assert np.isnan(f([np.nan, 1])) + assert np.isnan(f([1, np.nan])) + + @pytest.mark.parametrize("method", ['nearest', 'linear']) + def test_nan_x_2d(self, method): + x, y = np.array([0, 1, 2]), np.array([1, 3, 7]) + + def f(x, y): + return x**2 + y**2 + + xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True) + data = f(xg, yg) + interp = RegularGridInterpolator((x, y), data, + method=method, bounds_error=False) + + with np.errstate(invalid='ignore'): + res = interp([[1.5, np.nan], [1, 1]]) + assert_allclose(res[1], 2, atol=1e-14) + assert np.isnan(res[0]) + + # test arbitrary nan pattern + rng = np.random.default_rng(8143215468) + x = rng.random(size=100)*4-1 + y = rng.random(size=100)*8 + i1 = rng.random(size=100) > 0.5 + i2 = rng.random(size=100) > 0.5 + i = i1 | i2 + x[i1] = np.nan + y[i2] = np.nan + z = np.array([x, y]).T + with np.errstate(invalid='ignore'): + # out-of-bounds comparisons, `out_of_bounds += x < grid[0]`, + # generate numpy warnings if `x` contains nans. + # These warnings should propagate to user (since `x` is user + # input) and we simply filter them out. + res = interp(z) + + assert_equal(res[i], np.nan) + assert_equal(res[~i], interp(z[~i])) + + @parametrize_rgi_interp_methods + @pytest.mark.parametrize(("ndims", "func"), [ + (2, lambda x, y: 2 * x ** 3 + 3 * y ** 2), + (3, lambda x, y, z: 2 * x ** 3 + 3 * y ** 2 - z), + (4, lambda x, y, z, a: 2 * x ** 3 + 3 * y ** 2 - z + a), + (5, lambda x, y, z, a, b: 2 * x ** 3 + 3 * y ** 2 - z + a * b), + ]) + def test_descending_points_nd(self, method, ndims, func): + + if ndims == 5 and method in {"cubic", "quintic"}: + pytest.skip("too slow; OOM (quintic); or nearly so (cubic)") + + rng = np.random.default_rng(42) + sample_low = 1 + sample_high = 5 + test_points = rng.uniform(sample_low, sample_high, size=(2, ndims)) + + ascending_points = [np.linspace(sample_low, sample_high, 12) + for _ in range(ndims)] + + ascending_values = func(*np.meshgrid(*ascending_points, + indexing="ij", + sparse=True)) + + ascending_interp = RegularGridInterpolator(ascending_points, + ascending_values, + method=method) + ascending_result = ascending_interp(test_points) + + descending_points = [xi[::-1] for xi in ascending_points] + descending_values = func(*np.meshgrid(*descending_points, + indexing="ij", + sparse=True)) + descending_interp = RegularGridInterpolator(descending_points, + descending_values, + method=method) + descending_result = descending_interp(test_points) + + assert_array_equal(ascending_result, descending_result) + + def test_invalid_points_order(self): + def val_func_2d(x, y): + return 2 * x ** 3 + 3 * y ** 2 + + x = np.array([.5, 2., 0., 4., 5.5]) # not ascending or descending + y = np.array([.5, 2., 3., 4., 5.5]) + points = (x, y) + values = val_func_2d(*np.meshgrid(*points, indexing='ij', + sparse=True)) + match = "must be strictly ascending or descending" + with pytest.raises(ValueError, match=match): + RegularGridInterpolator(points, values) + + @parametrize_rgi_interp_methods + def test_fill_value(self, method): + interp = RegularGridInterpolator([np.arange(6)], np.ones(6), + method=method, bounds_error=False) + assert np.isnan(interp([10])) + + @parametrize_rgi_interp_methods + def test_nonscalar_values(self, method): + + if method == "quintic": + pytest.skip("Way too slow.") + + # Verify that non-scalar valued values also works + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] * 2 + [ + (0.0, 5.0, 10.0, 15.0, 20, 25.0) + ] * 2 + + rng = np.random.default_rng(1234) + values = rng.random((6, 6, 6, 6, 8)) + sample = rng.random((7, 3, 4)) + + interp = RegularGridInterpolator(points, values, method=method, + bounds_error=False) + v = interp(sample) + assert_equal(v.shape, (7, 3, 8), err_msg=method) + + vs = [] + for j in range(8): + interp = RegularGridInterpolator(points, values[..., j], + method=method, + bounds_error=False) + vs.append(interp(sample)) + v2 = np.array(vs).transpose(1, 2, 0) + + assert_allclose(v, v2, atol=1e-14, err_msg=method) + + @parametrize_rgi_interp_methods + @pytest.mark.parametrize("flip_points", [False, True]) + def test_nonscalar_values_2(self, method, flip_points): + + if method in {"cubic", "quintic"}: + pytest.skip("Way too slow.") + + # Verify that non-scalar valued values also work : use different + # lengths of axes to simplify tracing the internals + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5), + (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0), + (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0), + (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0, 47)] + + # verify, that strictly decreasing dimensions work + if flip_points: + points = [tuple(reversed(p)) for p in points] + + rng = np.random.default_rng(1234) + + trailing_points = (3, 2) + # NB: values has a `num_trailing_dims` trailing dimension + values = rng.random((6, 7, 8, 9, *trailing_points)) + sample = rng.random(4) # a single sample point ! + + interp = RegularGridInterpolator(points, values, method=method, + bounds_error=False) + v = interp(sample) + + # v has a single sample point *per entry in the trailing dimensions* + assert v.shape == (1, *trailing_points) + + # check the values, too : manually loop over the trailing dimensions + vs = np.empty(values.shape[-2:]) + for i in range(values.shape[-2]): + for j in range(values.shape[-1]): + interp = RegularGridInterpolator(points, values[..., i, j], + method=method, + bounds_error=False) + vs[i, j] = interp(sample).item() + v2 = np.expand_dims(vs, axis=0) + assert_allclose(v, v2, atol=1e-14, err_msg=method) + + def test_nonscalar_values_linear_2D(self): + # Verify that non-scalar values work in the 2D fast path + method = 'linear' + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5), + (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0), ] + + rng = np.random.default_rng(1234) + + trailing_points = (3, 4) + # NB: values has a `num_trailing_dims` trailing dimension + values = rng.random((6, 7, *trailing_points)) + sample = rng.random(2) # a single sample point ! + + interp = RegularGridInterpolator(points, values, method=method, + bounds_error=False) + v = interp(sample) + + # v has a single sample point *per entry in the trailing dimensions* + assert v.shape == (1, *trailing_points) + + # check the values, too : manually loop over the trailing dimensions + vs = np.empty(values.shape[-2:]) + for i in range(values.shape[-2]): + for j in range(values.shape[-1]): + interp = RegularGridInterpolator(points, values[..., i, j], + method=method, + bounds_error=False) + vs[i, j] = interp(sample).item() + v2 = np.expand_dims(vs, axis=0) + assert_allclose(v, v2, atol=1e-14, err_msg=method) + + @pytest.mark.parametrize( + "dtype", + [np.float32, np.float64, np.complex64, np.complex128] + ) + @pytest.mark.parametrize("xi_dtype", [np.float32, np.float64]) + def test_float32_values(self, dtype, xi_dtype): + # regression test for gh-17718: values.dtype=float32 fails + def f(x, y): + return 2 * x**3 + 3 * y**2 + + x = np.linspace(1, 4, 11) + y = np.linspace(4, 7, 22) + + xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True) + data = f(xg, yg) + + data = data.astype(dtype) + + interp = RegularGridInterpolator((x, y), data) + + pts = np.array([[2.1, 6.2], + [3.3, 5.2]], dtype=xi_dtype) + + # the values here are just what the call returns; the test checks that + # that the call succeeds at all, instead of failing with cython not + # having a float32 kernel + assert_allclose(interp(pts), [134.10469388, 153.40069388], atol=1e-7) + + def test_bad_solver(self): + x = np.linspace(0, 3, 7) + y = np.linspace(0, 3, 7) + xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True) + data = xg + yg + + # default method 'linear' does not accept 'solver' + with assert_raises(ValueError): + RegularGridInterpolator((x, y), data, solver=lambda x: x) + + with assert_raises(TypeError): + # wrong solver interface + RegularGridInterpolator( + (x, y), data, method='slinear', solver=lambda x: x + ) + + with assert_raises(TypeError): + # unknown argument + RegularGridInterpolator( + (x, y), data, method='slinear', solver=lambda x: x, woof='woof' + ) + + with assert_raises(TypeError): + # unknown argument + RegularGridInterpolator( + (x, y), data, method='slinear', solver_args={'woof': 42} + ) + + +class MyValue: + """ + Minimal indexable object + """ + + def __init__(self, shape): + self.ndim = 2 + self.shape = shape + self._v = np.arange(np.prod(shape)).reshape(shape) + + def __getitem__(self, idx): + return self._v[idx] + + def __array_interface__(self): + return None + + def __array__(self, dtype=None, copy=None): + raise RuntimeError("No array representation") + + +class TestInterpN: + def _sample_2d_data(self): + x = np.array([.5, 2., 3., 4., 5.5, 6.]) + y = np.array([.5, 2., 3., 4., 5.5, 6.]) + z = np.array( + [ + [1, 2, 1, 2, 1, 1], + [1, 2, 1, 2, 1, 1], + [1, 2, 3, 2, 1, 1], + [1, 2, 2, 2, 1, 1], + [1, 2, 1, 2, 1, 1], + [1, 2, 2, 2, 1, 1], + ] + ) + return x, y, z + + def test_spline_2d(self): + x, y, z = self._sample_2d_data() + lut = RectBivariateSpline(x, y, z) + + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + assert_array_almost_equal(interpn((x, y), z, xi, method="splinef2d"), + lut.ev(xi[:, 0], xi[:, 1])) + + @parametrize_rgi_interp_methods + def test_list_input(self, method): + x, y, z = self._sample_2d_data() + xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + v1 = interpn((x, y), z, xi, method=method) + v2 = interpn( + (x.tolist(), y.tolist()), z.tolist(), xi.tolist(), method=method + ) + assert_allclose(v1, v2, err_msg=method) + + def test_spline_2d_outofbounds(self): + x = np.array([.5, 2., 3., 4., 5.5]) + y = np.array([.5, 2., 3., 4., 5.5]) + z = np.array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + lut = RectBivariateSpline(x, y, z) + + xi = np.array([[1, 2.3, 6.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, -4.0, 5.0, 1.0, 3]]).T + actual = interpn((x, y), z, xi, method="splinef2d", + bounds_error=False, fill_value=999.99) + expected = lut.ev(xi[:, 0], xi[:, 1]) + expected[2:4] = 999.99 + assert_array_almost_equal(actual, expected) + + # no extrapolation for splinef2d + assert_raises(ValueError, interpn, (x, y), z, xi, method="splinef2d", + bounds_error=False, fill_value=None) + + def _sample_4d_data(self): + points = [(0., .5, 1.)] * 2 + [(0., 5., 10.)] * 2 + values = np.asarray([0., .5, 1.]) + values0 = values[:, np.newaxis, np.newaxis, np.newaxis] + values1 = values[np.newaxis, :, np.newaxis, np.newaxis] + values2 = values[np.newaxis, np.newaxis, :, np.newaxis] + values3 = values[np.newaxis, np.newaxis, np.newaxis, :] + values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000) + return points, values + + def test_linear_4d(self): + # create a 4-D grid of 3 points in each dimension + points, values = self._sample_4d_data() + interp_rg = RegularGridInterpolator(points, values) + sample = np.asarray([[0.1, 0.1, 10., 9.]]) + wanted = interpn(points, values, sample, method="linear") + assert_array_almost_equal(interp_rg(sample), wanted) + + def test_4d_linear_outofbounds(self): + # create a 4-D grid of 3 points in each dimension + points, values = self._sample_4d_data() + sample = np.asarray([[0.1, -0.1, 10.1, 9.]]) + wanted = 999.99 + actual = interpn(points, values, sample, method="linear", + bounds_error=False, fill_value=999.99) + assert_array_almost_equal(actual, wanted) + + def test_nearest_4d(self): + # create a 4-D grid of 3 points in each dimension + points, values = self._sample_4d_data() + interp_rg = RegularGridInterpolator(points, values, method="nearest") + sample = np.asarray([[0.1, 0.1, 10., 9.]]) + wanted = interpn(points, values, sample, method="nearest") + assert_array_almost_equal(interp_rg(sample), wanted) + + def test_4d_nearest_outofbounds(self): + # create a 4-D grid of 3 points in each dimension + points, values = self._sample_4d_data() + sample = np.asarray([[0.1, -0.1, 10.1, 9.]]) + wanted = 999.99 + actual = interpn(points, values, sample, method="nearest", + bounds_error=False, fill_value=999.99) + assert_array_almost_equal(actual, wanted) + + def test_xi_1d(self): + # verify that 1-D xi works as expected + points, values = self._sample_4d_data() + sample = np.asarray([0.1, 0.1, 10., 9.]) + v1 = interpn(points, values, sample, bounds_error=False) + v2 = interpn(points, values, sample[None,:], bounds_error=False) + assert_allclose(v1, v2) + + def test_xi_nd(self): + # verify that higher-d xi works as expected + points, values = self._sample_4d_data() + + np.random.seed(1234) + sample = np.random.rand(2, 3, 4) + + v1 = interpn(points, values, sample, method='nearest', + bounds_error=False) + assert_equal(v1.shape, (2, 3)) + + v2 = interpn(points, values, sample.reshape(-1, 4), + method='nearest', bounds_error=False) + assert_allclose(v1, v2.reshape(v1.shape)) + + @parametrize_rgi_interp_methods + def test_xi_broadcast(self, method): + # verify that the interpolators broadcast xi + x, y, values = self._sample_2d_data() + points = (x, y) + + xi = np.linspace(0, 1, 2) + yi = np.linspace(0, 3, 3) + + sample = (xi[:, None], yi[None, :]) + v1 = interpn(points, values, sample, method=method, bounds_error=False) + assert_equal(v1.shape, (2, 3)) + + xx, yy = np.meshgrid(xi, yi) + sample = np.c_[xx.T.ravel(), yy.T.ravel()] + + v2 = interpn(points, values, sample, + method=method, bounds_error=False) + assert_allclose(v1, v2.reshape(v1.shape)) + + @parametrize_rgi_interp_methods + def test_nonscalar_values(self, method): + + if method == "quintic": + pytest.skip("Way too slow.") + + # Verify that non-scalar valued values also works + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] * 2 + [ + (0.0, 5.0, 10.0, 15.0, 20, 25.0) + ] * 2 + + rng = np.random.default_rng(1234) + values = rng.random((6, 6, 6, 6, 8)) + sample = rng.random((7, 3, 4)) + + v = interpn(points, values, sample, method=method, + bounds_error=False) + assert_equal(v.shape, (7, 3, 8), err_msg=method) + + vs = [interpn(points, values[..., j], sample, method=method, + bounds_error=False) for j in range(8)] + v2 = np.array(vs).transpose(1, 2, 0) + + assert_allclose(v, v2, atol=1e-14, err_msg=method) + + @parametrize_rgi_interp_methods + def test_nonscalar_values_2(self, method): + + if method in {"cubic", "quintic"}: + pytest.skip("Way too slow.") + + # Verify that non-scalar valued values also work : use different + # lengths of axes to simplify tracing the internals + points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5), + (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0), + (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0), + (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0, 47)] + + rng = np.random.default_rng(1234) + + trailing_points = (3, 2) + # NB: values has a `num_trailing_dims` trailing dimension + values = rng.random((6, 7, 8, 9, *trailing_points)) + sample = rng.random(4) # a single sample point ! + + v = interpn(points, values, sample, method=method, bounds_error=False) + + # v has a single sample point *per entry in the trailing dimensions* + assert v.shape == (1, *trailing_points) + + # check the values, too : manually loop over the trailing dimensions + vs = [[ + interpn(points, values[..., i, j], sample, method=method, + bounds_error=False) for i in range(values.shape[-2]) + ] for j in range(values.shape[-1])] + + assert_allclose(v, np.asarray(vs).T, atol=1e-14, err_msg=method) + + def test_non_scalar_values_splinef2d(self): + # Vector-valued splines supported with fitpack + points, values = self._sample_4d_data() + + np.random.seed(1234) + values = np.random.rand(3, 3, 3, 3, 6) + sample = np.random.rand(7, 11, 4) + assert_raises(ValueError, interpn, points, values, sample, + method='splinef2d') + + @parametrize_rgi_interp_methods + def test_complex(self, method): + if method == "pchip": + pytest.skip("pchip does not make sense for complex data") + + x, y, values = self._sample_2d_data() + points = (x, y) + values = values - 2j*values + + sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + + v1 = interpn(points, values, sample, method=method) + v2r = interpn(points, values.real, sample, method=method) + v2i = interpn(points, values.imag, sample, method=method) + v2 = v2r + 1j*v2i + + assert_allclose(v1, v2) + + def test_complex_pchip(self): + # Complex-valued data deprecated for pchip + x, y, values = self._sample_2d_data() + points = (x, y) + values = values - 2j*values + + sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + with pytest.deprecated_call(match='complex'): + interpn(points, values, sample, method='pchip') + + def test_complex_spline2fd(self): + # Complex-valued data not supported by spline2fd + x, y, values = self._sample_2d_data() + points = (x, y) + values = values - 2j*values + + sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T + with assert_warns(ComplexWarning): + interpn(points, values, sample, method='splinef2d') + + @pytest.mark.parametrize( + "method", + ["linear", "nearest"] + ) + def test_duck_typed_values(self, method): + x = np.linspace(0, 2, 5) + y = np.linspace(0, 1, 7) + + values = MyValue((5, 7)) + + v1 = interpn((x, y), values, [0.4, 0.7], method=method) + v2 = interpn((x, y), values._v, [0.4, 0.7], method=method) + assert_allclose(v1, v2) + + @parametrize_rgi_interp_methods + def test_matrix_input(self, method): + x = np.linspace(0, 2, 6) + y = np.linspace(0, 1, 7) + + values = matrix(np.random.rand(6, 7)) + + sample = np.random.rand(3, 7, 2) + + v1 = interpn((x, y), values, sample, method=method) + v2 = interpn((x, y), np.asarray(values), sample, method=method) + if method == "quintic": + # https://github.com/scipy/scipy/issues/20472 + assert_allclose(v1, v2, atol=5e-5, rtol=2e-6) + else: + assert_allclose(v1, v2) + + def test_length_one_axis(self): + # gh-5890, gh-9524 : length-1 axis is legal for method='linear'. + # Along the axis it's linear interpolation; away from the length-1 + # axis, it's an extrapolation, so fill_value should be used. + + values = np.array([[0.1, 1, 10]]) + xi = np.array([[1, 2.2], [1, 3.2], [1, 3.8]]) + + res = interpn(([1], [2, 3, 4]), values, xi) + wanted = [0.9*0.2 + 0.1, # on [2, 3) it's 0.9*(x-2) + 0.1 + 9*0.2 + 1, # on [3, 4] it's 9*(x-3) + 1 + 9*0.8 + 1] + + assert_allclose(res, wanted, atol=1e-15) + + # check extrapolation + xi = np.array([[1.1, 2.2], [1.5, 3.2], [-2.3, 3.8]]) + res = interpn(([1], [2, 3, 4]), values, xi, + bounds_error=False, fill_value=None) + + assert_allclose(res, wanted, atol=1e-15) + + def test_descending_points(self): + def value_func_4d(x, y, z, a): + return 2 * x ** 3 + 3 * y ** 2 - z - a + + x1 = np.array([0, 1, 2, 3]) + x2 = np.array([0, 10, 20, 30]) + x3 = np.array([0, 10, 20, 30]) + x4 = np.array([0, .1, .2, .30]) + points = (x1, x2, x3, x4) + values = value_func_4d( + *np.meshgrid(*points, indexing='ij', sparse=True)) + pts = (0.1, 0.3, np.transpose(np.linspace(0, 30, 4)), + np.linspace(0, 0.3, 4)) + correct_result = interpn(points, values, pts) + + x1_descend = x1[::-1] + x2_descend = x2[::-1] + x3_descend = x3[::-1] + x4_descend = x4[::-1] + points_shuffled = (x1_descend, x2_descend, x3_descend, x4_descend) + values_shuffled = value_func_4d( + *np.meshgrid(*points_shuffled, indexing='ij', sparse=True)) + test_result = interpn(points_shuffled, values_shuffled, pts) + + assert_array_equal(correct_result, test_result) + + def test_invalid_points_order(self): + x = np.array([.5, 2., 0., 4., 5.5]) # not ascending or descending + y = np.array([.5, 2., 3., 4., 5.5]) + z = np.array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1], + [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]]) + xi = np.array([[1, 2.3, 6.3, 0.5, 3.3, 1.2, 3], + [1, 3.3, 1.2, -4.0, 5.0, 1.0, 3]]).T + + match = "must be strictly ascending or descending" + with pytest.raises(ValueError, match=match): + interpn((x, y), z, xi) + + def test_invalid_xi_dimensions(self): + # https://github.com/scipy/scipy/issues/16519 + points = [(0, 1)] + values = [0, 1] + xi = np.ones((1, 1, 3)) + msg = ("The requested sample points xi have dimension 3, but this " + "RegularGridInterpolator has dimension 1") + with assert_raises(ValueError, match=msg): + interpn(points, values, xi) + + def test_readonly_grid(self): + # https://github.com/scipy/scipy/issues/17716 + x = np.linspace(0, 4, 5) + y = np.linspace(0, 5, 6) + z = np.linspace(0, 6, 7) + points = (x, y, z) + values = np.ones((5, 6, 7)) + point = np.array([2.21, 3.12, 1.15]) + for d in points: + d.flags.writeable = False + values.flags.writeable = False + point.flags.writeable = False + interpn(points, values, point) + RegularGridInterpolator(points, values)(point) + + def test_2d_readonly_grid(self): + # https://github.com/scipy/scipy/issues/17716 + # test special 2d case + x = np.linspace(0, 4, 5) + y = np.linspace(0, 5, 6) + points = (x, y) + values = np.ones((5, 6)) + point = np.array([2.21, 3.12]) + for d in points: + d.flags.writeable = False + values.flags.writeable = False + point.flags.writeable = False + interpn(points, values, point) + RegularGridInterpolator(points, values)(point) + + def test_non_c_contiguous_grid(self): + # https://github.com/scipy/scipy/issues/17716 + x = np.linspace(0, 4, 5) + x = np.vstack((x, np.empty_like(x))).T.copy()[:, 0] + assert not x.flags.c_contiguous + y = np.linspace(0, 5, 6) + z = np.linspace(0, 6, 7) + points = (x, y, z) + values = np.ones((5, 6, 7)) + point = np.array([2.21, 3.12, 1.15]) + interpn(points, values, point) + RegularGridInterpolator(points, values)(point) + + @pytest.mark.parametrize("dtype", ['>f8', '` and \ + its inverse. + stft -- Compute the Short Time Fourier Transform (legacy). + istft -- Compute the Inverse Short Time Fourier Transform (legacy). + check_COLA -- Check the COLA constraint for iSTFT reconstruction. + check_NOLA -- Check the NOLA constraint for iSTFT reconstruction. + +Chirp Z-transform and Zoom FFT +============================================ + +.. autosummary:: + :toctree: generated/ + + czt - Chirp z-transform convenience function + zoom_fft - Zoom FFT convenience function + CZT - Chirp z-transform function generator + ZoomFFT - Zoom FFT function generator + czt_points - Output the z-plane points sampled by a chirp z-transform + +The functions are simpler to use than the classes, but are less efficient when +using the same transform on many arrays of the same length, since they +repeatedly generate the same chirp signal with every call. In these cases, +use the classes to create a reusable function instead. + +""" + +from . import _sigtools, windows +from ._waveforms import * +from ._max_len_seq import max_len_seq +from ._upfirdn import upfirdn + +from ._spline import ( + cspline2d, + qspline2d, + sepfir2d, + symiirorder1, + symiirorder2, +) + +from ._bsplines import * +from ._filter_design import * +from ._fir_filter_design import * +from ._ltisys import * +from ._lti_conversion import * +from ._signaltools import * +from ._savitzky_golay import savgol_coeffs, savgol_filter +from ._spectral_py import * +from ._short_time_fft import * +from ._wavelets import * +from ._peak_finding import * +from ._czt import * +from .windows import get_window # keep this one in signal namespace + +# Deprecated namespaces, to be removed in v2.0.0 +from . import ( + bsplines, filter_design, fir_filter_design, lti_conversion, ltisys, + spectral, signaltools, waveforms, wavelets, spline +) + +__all__ = [ + s for s in dir() if not s.startswith("_") +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_arraytools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_arraytools.py new file mode 100644 index 0000000000000000000000000000000000000000..87ce75d8d892a64021da7abc5d149556c22cf983 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_arraytools.py @@ -0,0 +1,264 @@ +""" +Functions for acting on a axis of an array. +""" +import numpy as np + + +def axis_slice(a, start=None, stop=None, step=None, axis=-1): + """Take a slice along axis 'axis' from 'a'. + + Parameters + ---------- + a : numpy.ndarray + The array to be sliced. + start, stop, step : int or None + The slice parameters. + axis : int, optional + The axis of `a` to be sliced. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._arraytools import axis_slice + >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + >>> axis_slice(a, start=0, stop=1, axis=1) + array([[1], + [4], + [7]]) + >>> axis_slice(a, start=1, axis=0) + array([[4, 5, 6], + [7, 8, 9]]) + + Notes + ----- + The keyword arguments start, stop and step are used by calling + slice(start, stop, step). This implies axis_slice() does not + handle its arguments the exactly the same as indexing. To select + a single index k, for example, use + axis_slice(a, start=k, stop=k+1) + In this case, the length of the axis 'axis' in the result will + be 1; the trivial dimension is not removed. (Use numpy.squeeze() + to remove trivial axes.) + """ + a_slice = [slice(None)] * a.ndim + a_slice[axis] = slice(start, stop, step) + b = a[tuple(a_slice)] + return b + + +def axis_reverse(a, axis=-1): + """Reverse the 1-D slices of `a` along axis `axis`. + + Returns axis_slice(a, step=-1, axis=axis). + """ + return axis_slice(a, step=-1, axis=axis) + + +def odd_ext(x, n, axis=-1): + """ + Odd extension at the boundaries of an array + + Generate a new ndarray by making an odd extension of `x` along an axis. + + Parameters + ---------- + x : ndarray + The array to be extended. + n : int + The number of elements by which to extend `x` at each end of the axis. + axis : int, optional + The axis along which to extend `x`. Default is -1. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._arraytools import odd_ext + >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) + >>> odd_ext(a, 2) + array([[-1, 0, 1, 2, 3, 4, 5, 6, 7], + [-4, -1, 0, 1, 4, 9, 16, 23, 28]]) + + Odd extension is a "180 degree rotation" at the endpoints of the original + array: + + >>> t = np.linspace(0, 1.5, 100) + >>> a = 0.9 * np.sin(2 * np.pi * t**2) + >>> b = odd_ext(a, 40) + >>> import matplotlib.pyplot as plt + >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='odd extension') + >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') + >>> plt.legend(loc='best') + >>> plt.show() + """ + if n < 1: + return x + if n > x.shape[axis] - 1: + raise ValueError(("The extension length n (%d) is too big. " + + "It must not exceed x.shape[axis]-1, which is %d.") + % (n, x.shape[axis] - 1)) + left_end = axis_slice(x, start=0, stop=1, axis=axis) + left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis) + right_end = axis_slice(x, start=-1, axis=axis) + right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis) + ext = np.concatenate((2 * left_end - left_ext, + x, + 2 * right_end - right_ext), + axis=axis) + return ext + + +def even_ext(x, n, axis=-1): + """ + Even extension at the boundaries of an array + + Generate a new ndarray by making an even extension of `x` along an axis. + + Parameters + ---------- + x : ndarray + The array to be extended. + n : int + The number of elements by which to extend `x` at each end of the axis. + axis : int, optional + The axis along which to extend `x`. Default is -1. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._arraytools import even_ext + >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) + >>> even_ext(a, 2) + array([[ 3, 2, 1, 2, 3, 4, 5, 4, 3], + [ 4, 1, 0, 1, 4, 9, 16, 9, 4]]) + + Even extension is a "mirror image" at the boundaries of the original array: + + >>> t = np.linspace(0, 1.5, 100) + >>> a = 0.9 * np.sin(2 * np.pi * t**2) + >>> b = even_ext(a, 40) + >>> import matplotlib.pyplot as plt + >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='even extension') + >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') + >>> plt.legend(loc='best') + >>> plt.show() + """ + if n < 1: + return x + if n > x.shape[axis] - 1: + raise ValueError(("The extension length n (%d) is too big. " + + "It must not exceed x.shape[axis]-1, which is %d.") + % (n, x.shape[axis] - 1)) + left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis) + right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis) + ext = np.concatenate((left_ext, + x, + right_ext), + axis=axis) + return ext + + +def const_ext(x, n, axis=-1): + """ + Constant extension at the boundaries of an array + + Generate a new ndarray that is a constant extension of `x` along an axis. + + The extension repeats the values at the first and last element of + the axis. + + Parameters + ---------- + x : ndarray + The array to be extended. + n : int + The number of elements by which to extend `x` at each end of the axis. + axis : int, optional + The axis along which to extend `x`. Default is -1. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._arraytools import const_ext + >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) + >>> const_ext(a, 2) + array([[ 1, 1, 1, 2, 3, 4, 5, 5, 5], + [ 0, 0, 0, 1, 4, 9, 16, 16, 16]]) + + Constant extension continues with the same values as the endpoints of the + array: + + >>> t = np.linspace(0, 1.5, 100) + >>> a = 0.9 * np.sin(2 * np.pi * t**2) + >>> b = const_ext(a, 40) + >>> import matplotlib.pyplot as plt + >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='constant extension') + >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original') + >>> plt.legend(loc='best') + >>> plt.show() + """ + if n < 1: + return x + left_end = axis_slice(x, start=0, stop=1, axis=axis) + ones_shape = [1] * x.ndim + ones_shape[axis] = n + ones = np.ones(ones_shape, dtype=x.dtype) + left_ext = ones * left_end + right_end = axis_slice(x, start=-1, axis=axis) + right_ext = ones * right_end + ext = np.concatenate((left_ext, + x, + right_ext), + axis=axis) + return ext + + +def zero_ext(x, n, axis=-1): + """ + Zero padding at the boundaries of an array + + Generate a new ndarray that is a zero-padded extension of `x` along + an axis. + + Parameters + ---------- + x : ndarray + The array to be extended. + n : int + The number of elements by which to extend `x` at each end of the + axis. + axis : int, optional + The axis along which to extend `x`. Default is -1. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._arraytools import zero_ext + >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]]) + >>> zero_ext(a, 2) + array([[ 0, 0, 1, 2, 3, 4, 5, 0, 0], + [ 0, 0, 0, 1, 4, 9, 16, 0, 0]]) + """ + if n < 1: + return x + zeros_shape = list(x.shape) + zeros_shape[axis] = n + zeros = np.zeros(zeros_shape, dtype=x.dtype) + ext = np.concatenate((zeros, x, zeros), axis=axis) + return ext + + +def _validate_fs(fs, allow_none=True): + """ + Check if the given sampling frequency is a scalar and raises an exception + otherwise. If allow_none is False, also raises an exception for none + sampling rates. Returns the sampling frequency as float or none if the + input is none. + """ + if fs is None: + if not allow_none: + raise ValueError("Sampling frequency can not be none.") + else: # should be float + if not np.isscalar(fs): + raise ValueError("Sampling frequency fs must be a single scalar.") + fs = float(fs) + return fs diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_bsplines.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_bsplines.py new file mode 100644 index 0000000000000000000000000000000000000000..c9b136da7c5543f517e03f09f88666290f1590d7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_bsplines.py @@ -0,0 +1,519 @@ +from numpy import (asarray, pi, zeros_like, + array, arctan2, tan, ones, arange, floor, + r_, atleast_1d, sqrt, exp, greater, cos, add, sin) + +# From splinemodule.c +from ._spline import cspline2d, sepfir2d +from ._signaltools import lfilter, sosfilt, lfiltic + +from scipy.interpolate import BSpline + +__all__ = ['spline_filter', 'gauss_spline', + 'cspline1d', 'qspline1d', 'cspline1d_eval', 'qspline1d_eval'] + + +def spline_filter(Iin, lmbda=5.0): + """Smoothing spline (cubic) filtering of a rank-2 array. + + Filter an input data set, `Iin`, using a (cubic) smoothing spline of + fall-off `lmbda`. + + Parameters + ---------- + Iin : array_like + input data set + lmbda : float, optional + spline smooghing fall-off value, default is `5.0`. + + Returns + ------- + res : ndarray + filtered input data + + Examples + -------- + We can filter an multi dimensional signal (ex: 2D image) using cubic + B-spline filter: + + >>> import numpy as np + >>> from scipy.signal import spline_filter + >>> import matplotlib.pyplot as plt + >>> orig_img = np.eye(20) # create an image + >>> orig_img[10, :] = 1.0 + >>> sp_filter = spline_filter(orig_img, lmbda=0.1) + >>> f, ax = plt.subplots(1, 2, sharex=True) + >>> for ind, data in enumerate([[orig_img, "original image"], + ... [sp_filter, "spline filter"]]): + ... ax[ind].imshow(data[0], cmap='gray_r') + ... ax[ind].set_title(data[1]) + >>> plt.tight_layout() + >>> plt.show() + + """ + intype = Iin.dtype.char + hcol = array([1.0, 4.0, 1.0], 'f') / 6.0 + if intype in ['F', 'D']: + Iin = Iin.astype('F') + ckr = cspline2d(Iin.real, lmbda) + cki = cspline2d(Iin.imag, lmbda) + outr = sepfir2d(ckr, hcol, hcol) + outi = sepfir2d(cki, hcol, hcol) + out = (outr + 1j * outi).astype(intype) + elif intype in ['f', 'd']: + ckr = cspline2d(Iin, lmbda) + out = sepfir2d(ckr, hcol, hcol) + out = out.astype(intype) + else: + raise TypeError("Invalid data type for Iin") + return out + + +_splinefunc_cache = {} + + +def gauss_spline(x, n): + r"""Gaussian approximation to B-spline basis function of order n. + + Parameters + ---------- + x : array_like + a knot vector + n : int + The order of the spline. Must be non-negative, i.e., n >= 0 + + Returns + ------- + res : ndarray + B-spline basis function values approximated by a zero-mean Gaussian + function. + + Notes + ----- + The B-spline basis function can be approximated well by a zero-mean + Gaussian function with standard-deviation equal to :math:`\sigma=(n+1)/12` + for large `n` : + + .. math:: \frac{1}{\sqrt {2\pi\sigma^2}}exp(-\frac{x^2}{2\sigma}) + + References + ---------- + .. [1] Bouma H., Vilanova A., Bescos J.O., ter Haar Romeny B.M., Gerritsen + F.A. (2007) Fast and Accurate Gaussian Derivatives Based on B-Splines. In: + Sgallari F., Murli A., Paragios N. (eds) Scale Space and Variational + Methods in Computer Vision. SSVM 2007. Lecture Notes in Computer + Science, vol 4485. Springer, Berlin, Heidelberg + .. [2] http://folk.uio.no/inf3330/scripting/doc/python/SciPy/tutorial/old/node24.html + + Examples + -------- + We can calculate B-Spline basis functions approximated by a gaussian + distribution: + + >>> import numpy as np + >>> from scipy.signal import gauss_spline + >>> knots = np.array([-1.0, 0.0, -1.0]) + >>> gauss_spline(knots, 3) + array([0.15418033, 0.6909883, 0.15418033]) # may vary + + """ + x = asarray(x) + signsq = (n + 1) / 12.0 + return 1 / sqrt(2 * pi * signsq) * exp(-x ** 2 / 2 / signsq) + + +def _cubic(x): + x = asarray(x, dtype=float) + b = BSpline.basis_element([-2, -1, 0, 1, 2], extrapolate=False) + out = b(x) + out[(x < -2) | (x > 2)] = 0 + return out + + +def _quadratic(x): + x = abs(asarray(x, dtype=float)) + b = BSpline.basis_element([-1.5, -0.5, 0.5, 1.5], extrapolate=False) + out = b(x) + out[(x < -1.5) | (x > 1.5)] = 0 + return out + + +def _coeff_smooth(lam): + xi = 1 - 96 * lam + 24 * lam * sqrt(3 + 144 * lam) + omeg = arctan2(sqrt(144 * lam - 1), sqrt(xi)) + rho = (24 * lam - 1 - sqrt(xi)) / (24 * lam) + rho = rho * sqrt((48 * lam + 24 * lam * sqrt(3 + 144 * lam)) / xi) + return rho, omeg + + +def _hc(k, cs, rho, omega): + return (cs / sin(omega) * (rho ** k) * sin(omega * (k + 1)) * + greater(k, -1)) + + +def _hs(k, cs, rho, omega): + c0 = (cs * cs * (1 + rho * rho) / (1 - rho * rho) / + (1 - 2 * rho * rho * cos(2 * omega) + rho ** 4)) + gamma = (1 - rho * rho) / (1 + rho * rho) / tan(omega) + ak = abs(k) + return c0 * rho ** ak * (cos(omega * ak) + gamma * sin(omega * ak)) + + +def _cubic_smooth_coeff(signal, lamb): + rho, omega = _coeff_smooth(lamb) + cs = 1 - 2 * rho * cos(omega) + rho * rho + K = len(signal) + k = arange(K) + + zi_2 = (_hc(0, cs, rho, omega) * signal[0] + + add.reduce(_hc(k + 1, cs, rho, omega) * signal)) + zi_1 = (_hc(0, cs, rho, omega) * signal[0] + + _hc(1, cs, rho, omega) * signal[1] + + add.reduce(_hc(k + 2, cs, rho, omega) * signal)) + + # Forward filter: + # for n in range(2, K): + # yp[n] = (cs * signal[n] + 2 * rho * cos(omega) * yp[n - 1] - + # rho * rho * yp[n - 2]) + zi = lfiltic(cs, r_[1, -2 * rho * cos(omega), rho * rho], r_[zi_1, zi_2]) + zi = zi.reshape(1, -1) + + sos = r_[cs, 0, 0, 1, -2 * rho * cos(omega), rho * rho] + sos = sos.reshape(1, -1) + + yp, _ = sosfilt(sos, signal[2:], zi=zi) + yp = r_[zi_2, zi_1, yp] + + # Reverse filter: + # for n in range(K - 3, -1, -1): + # y[n] = (cs * yp[n] + 2 * rho * cos(omega) * y[n + 1] - + # rho * rho * y[n + 2]) + + zi_2 = add.reduce((_hs(k, cs, rho, omega) + + _hs(k + 1, cs, rho, omega)) * signal[::-1]) + zi_1 = add.reduce((_hs(k - 1, cs, rho, omega) + + _hs(k + 2, cs, rho, omega)) * signal[::-1]) + + zi = lfiltic(cs, r_[1, -2 * rho * cos(omega), rho * rho], r_[zi_1, zi_2]) + zi = zi.reshape(1, -1) + y, _ = sosfilt(sos, yp[-3::-1], zi=zi) + y = r_[y[::-1], zi_1, zi_2] + return y + + +def _cubic_coeff(signal): + zi = -2 + sqrt(3) + K = len(signal) + powers = zi ** arange(K) + + if K == 1: + yplus = signal[0] + zi * add.reduce(powers * signal) + output = zi / (zi - 1) * yplus + return atleast_1d(output) + + # Forward filter: + # yplus[0] = signal[0] + zi * add.reduce(powers * signal) + # for k in range(1, K): + # yplus[k] = signal[k] + zi * yplus[k - 1] + + state = lfiltic(1, r_[1, -zi], atleast_1d(add.reduce(powers * signal))) + + b = ones(1) + a = r_[1, -zi] + yplus, _ = lfilter(b, a, signal, zi=state) + + # Reverse filter: + # output[K - 1] = zi / (zi - 1) * yplus[K - 1] + # for k in range(K - 2, -1, -1): + # output[k] = zi * (output[k + 1] - yplus[k]) + out_last = zi / (zi - 1) * yplus[K - 1] + state = lfiltic(-zi, r_[1, -zi], atleast_1d(out_last)) + + b = asarray([-zi]) + output, _ = lfilter(b, a, yplus[-2::-1], zi=state) + output = r_[output[::-1], out_last] + return output * 6.0 + + +def _quadratic_coeff(signal): + zi = -3 + 2 * sqrt(2.0) + K = len(signal) + powers = zi ** arange(K) + + if K == 1: + yplus = signal[0] + zi * add.reduce(powers * signal) + output = zi / (zi - 1) * yplus + return atleast_1d(output) + + # Forward filter: + # yplus[0] = signal[0] + zi * add.reduce(powers * signal) + # for k in range(1, K): + # yplus[k] = signal[k] + zi * yplus[k - 1] + + state = lfiltic(1, r_[1, -zi], atleast_1d(add.reduce(powers * signal))) + + b = ones(1) + a = r_[1, -zi] + yplus, _ = lfilter(b, a, signal, zi=state) + + # Reverse filter: + # output[K - 1] = zi / (zi - 1) * yplus[K - 1] + # for k in range(K - 2, -1, -1): + # output[k] = zi * (output[k + 1] - yplus[k]) + out_last = zi / (zi - 1) * yplus[K - 1] + state = lfiltic(-zi, r_[1, -zi], atleast_1d(out_last)) + + b = asarray([-zi]) + output, _ = lfilter(b, a, yplus[-2::-1], zi=state) + output = r_[output[::-1], out_last] + return output * 8.0 + + +def cspline1d(signal, lamb=0.0): + """ + Compute cubic spline coefficients for rank-1 array. + + Find the cubic spline coefficients for a 1-D signal assuming + mirror-symmetric boundary conditions. To obtain the signal back from the + spline representation mirror-symmetric-convolve these coefficients with a + length 3 FIR window [1.0, 4.0, 1.0]/ 6.0 . + + Parameters + ---------- + signal : ndarray + A rank-1 array representing samples of a signal. + lamb : float, optional + Smoothing coefficient, default is 0.0. + + Returns + ------- + c : ndarray + Cubic spline coefficients. + + See Also + -------- + cspline1d_eval : Evaluate a cubic spline at the new set of points. + + Examples + -------- + We can filter a signal to reduce and smooth out high-frequency noise with + a cubic spline: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import cspline1d, cspline1d_eval + >>> rng = np.random.default_rng() + >>> sig = np.repeat([0., 1., 0.], 100) + >>> sig += rng.standard_normal(len(sig))*0.05 # add noise + >>> time = np.linspace(0, len(sig)) + >>> filtered = cspline1d_eval(cspline1d(sig), time) + >>> plt.plot(sig, label="signal") + >>> plt.plot(time, filtered, label="filtered") + >>> plt.legend() + >>> plt.show() + + """ + if lamb != 0.0: + return _cubic_smooth_coeff(signal, lamb) + else: + return _cubic_coeff(signal) + + +def qspline1d(signal, lamb=0.0): + """Compute quadratic spline coefficients for rank-1 array. + + Parameters + ---------- + signal : ndarray + A rank-1 array representing samples of a signal. + lamb : float, optional + Smoothing coefficient (must be zero for now). + + Returns + ------- + c : ndarray + Quadratic spline coefficients. + + See Also + -------- + qspline1d_eval : Evaluate a quadratic spline at the new set of points. + + Notes + ----- + Find the quadratic spline coefficients for a 1-D signal assuming + mirror-symmetric boundary conditions. To obtain the signal back from the + spline representation mirror-symmetric-convolve these coefficients with a + length 3 FIR window [1.0, 6.0, 1.0]/ 8.0 . + + Examples + -------- + We can filter a signal to reduce and smooth out high-frequency noise with + a quadratic spline: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import qspline1d, qspline1d_eval + >>> rng = np.random.default_rng() + >>> sig = np.repeat([0., 1., 0.], 100) + >>> sig += rng.standard_normal(len(sig))*0.05 # add noise + >>> time = np.linspace(0, len(sig)) + >>> filtered = qspline1d_eval(qspline1d(sig), time) + >>> plt.plot(sig, label="signal") + >>> plt.plot(time, filtered, label="filtered") + >>> plt.legend() + >>> plt.show() + + """ + if lamb != 0.0: + raise ValueError("Smoothing quadratic splines not supported yet.") + else: + return _quadratic_coeff(signal) + + +def cspline1d_eval(cj, newx, dx=1.0, x0=0): + """Evaluate a cubic spline at the new set of points. + + `dx` is the old sample-spacing while `x0` was the old origin. In + other-words the old-sample points (knot-points) for which the `cj` + represent spline coefficients were at equally-spaced points of: + + oldx = x0 + j*dx j=0...N-1, with N=len(cj) + + Edges are handled using mirror-symmetric boundary conditions. + + Parameters + ---------- + cj : ndarray + cublic spline coefficients + newx : ndarray + New set of points. + dx : float, optional + Old sample-spacing, the default value is 1.0. + x0 : int, optional + Old origin, the default value is 0. + + Returns + ------- + res : ndarray + Evaluated a cubic spline points. + + See Also + -------- + cspline1d : Compute cubic spline coefficients for rank-1 array. + + Examples + -------- + We can filter a signal to reduce and smooth out high-frequency noise with + a cubic spline: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import cspline1d, cspline1d_eval + >>> rng = np.random.default_rng() + >>> sig = np.repeat([0., 1., 0.], 100) + >>> sig += rng.standard_normal(len(sig))*0.05 # add noise + >>> time = np.linspace(0, len(sig)) + >>> filtered = cspline1d_eval(cspline1d(sig), time) + >>> plt.plot(sig, label="signal") + >>> plt.plot(time, filtered, label="filtered") + >>> plt.legend() + >>> plt.show() + + """ + newx = (asarray(newx) - x0) / float(dx) + res = zeros_like(newx, dtype=cj.dtype) + if res.size == 0: + return res + N = len(cj) + cond1 = newx < 0 + cond2 = newx > (N - 1) + cond3 = ~(cond1 | cond2) + # handle general mirror-symmetry + res[cond1] = cspline1d_eval(cj, -newx[cond1]) + res[cond2] = cspline1d_eval(cj, 2 * (N - 1) - newx[cond2]) + newx = newx[cond3] + if newx.size == 0: + return res + result = zeros_like(newx, dtype=cj.dtype) + jlower = floor(newx - 2).astype(int) + 1 + for i in range(4): + thisj = jlower + i + indj = thisj.clip(0, N - 1) # handle edge cases + result += cj[indj] * _cubic(newx - thisj) + res[cond3] = result + return res + + +def qspline1d_eval(cj, newx, dx=1.0, x0=0): + """Evaluate a quadratic spline at the new set of points. + + Parameters + ---------- + cj : ndarray + Quadratic spline coefficients + newx : ndarray + New set of points. + dx : float, optional + Old sample-spacing, the default value is 1.0. + x0 : int, optional + Old origin, the default value is 0. + + Returns + ------- + res : ndarray + Evaluated a quadratic spline points. + + See Also + -------- + qspline1d : Compute quadratic spline coefficients for rank-1 array. + + Notes + ----- + `dx` is the old sample-spacing while `x0` was the old origin. In + other-words the old-sample points (knot-points) for which the `cj` + represent spline coefficients were at equally-spaced points of:: + + oldx = x0 + j*dx j=0...N-1, with N=len(cj) + + Edges are handled using mirror-symmetric boundary conditions. + + Examples + -------- + We can filter a signal to reduce and smooth out high-frequency noise with + a quadratic spline: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import qspline1d, qspline1d_eval + >>> rng = np.random.default_rng() + >>> sig = np.repeat([0., 1., 0.], 100) + >>> sig += rng.standard_normal(len(sig))*0.05 # add noise + >>> time = np.linspace(0, len(sig)) + >>> filtered = qspline1d_eval(qspline1d(sig), time) + >>> plt.plot(sig, label="signal") + >>> plt.plot(time, filtered, label="filtered") + >>> plt.legend() + >>> plt.show() + + """ + newx = (asarray(newx) - x0) / dx + res = zeros_like(newx) + if res.size == 0: + return res + N = len(cj) + cond1 = newx < 0 + cond2 = newx > (N - 1) + cond3 = ~(cond1 | cond2) + # handle general mirror-symmetry + res[cond1] = qspline1d_eval(cj, -newx[cond1]) + res[cond2] = qspline1d_eval(cj, 2 * (N - 1) - newx[cond2]) + newx = newx[cond3] + if newx.size == 0: + return res + result = zeros_like(newx) + jlower = floor(newx - 1.5).astype(int) + 1 + for i in range(3): + thisj = jlower + i + indj = thisj.clip(0, N - 1) # handle edge cases + result += cj[indj] * _quadratic(newx - thisj) + res[cond3] = result + return res diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_czt.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_czt.py new file mode 100644 index 0000000000000000000000000000000000000000..c5e5715b460fb2719b68d4694474bc1efc0a9fa0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_czt.py @@ -0,0 +1,575 @@ +# This program is public domain +# Authors: Paul Kienzle, Nadav Horesh +""" +Chirp z-transform. + +We provide two interfaces to the chirp z-transform: an object interface +which precalculates part of the transform and can be applied efficiently +to many different data sets, and a functional interface which is applied +only to the given data set. + +Transforms +---------- + +CZT : callable (x, axis=-1) -> array + Define a chirp z-transform that can be applied to different signals. +ZoomFFT : callable (x, axis=-1) -> array + Define a Fourier transform on a range of frequencies. + +Functions +--------- + +czt : array + Compute the chirp z-transform for a signal. +zoom_fft : array + Compute the Fourier transform on a range of frequencies. +""" + +import cmath +import numbers +import numpy as np +from numpy import pi, arange +from scipy.fft import fft, ifft, next_fast_len + +__all__ = ['czt', 'zoom_fft', 'CZT', 'ZoomFFT', 'czt_points'] + + +def _validate_sizes(n, m): + if n < 1 or not isinstance(n, numbers.Integral): + raise ValueError('Invalid number of CZT data ' + f'points ({n}) specified. ' + 'n must be positive and integer type.') + + if m is None: + m = n + elif m < 1 or not isinstance(m, numbers.Integral): + raise ValueError('Invalid number of CZT output ' + f'points ({m}) specified. ' + 'm must be positive and integer type.') + + return m + + +def czt_points(m, w=None, a=1+0j): + """ + Return the points at which the chirp z-transform is computed. + + Parameters + ---------- + m : int + The number of points desired. + w : complex, optional + The ratio between points in each step. + Defaults to equally spaced points around the entire unit circle. + a : complex, optional + The starting point in the complex plane. Default is 1+0j. + + Returns + ------- + out : ndarray + The points in the Z plane at which `CZT` samples the z-transform, + when called with arguments `m`, `w`, and `a`, as complex numbers. + + See Also + -------- + CZT : Class that creates a callable chirp z-transform function. + czt : Convenience function for quickly calculating CZT. + + Examples + -------- + Plot the points of a 16-point FFT: + + >>> import numpy as np + >>> from scipy.signal import czt_points + >>> points = czt_points(16) + >>> import matplotlib.pyplot as plt + >>> plt.plot(points.real, points.imag, 'o') + >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3)) + >>> plt.axis('equal') + >>> plt.show() + + and a 91-point logarithmic spiral that crosses the unit circle: + + >>> m, w, a = 91, 0.995*np.exp(-1j*np.pi*.05), 0.8*np.exp(1j*np.pi/6) + >>> points = czt_points(m, w, a) + >>> plt.plot(points.real, points.imag, 'o') + >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3)) + >>> plt.axis('equal') + >>> plt.show() + """ + m = _validate_sizes(1, m) + + k = arange(m) + + a = 1.0 * a # at least float + + if w is None: + # Nothing specified, default to FFT + return a * np.exp(2j * pi * k / m) + else: + # w specified + w = 1.0 * w # at least float + return a * w**-k + + +class CZT: + """ + Create a callable chirp z-transform function. + + Transform to compute the frequency response around a spiral. + Objects of this class are callables which can compute the + chirp z-transform on their inputs. This object precalculates the constant + chirps used in the given transform. + + Parameters + ---------- + n : int + The size of the signal. + m : int, optional + The number of output points desired. Default is `n`. + w : complex, optional + The ratio between points in each step. This must be precise or the + accumulated error will degrade the tail of the output sequence. + Defaults to equally spaced points around the entire unit circle. + a : complex, optional + The starting point in the complex plane. Default is 1+0j. + + Returns + ------- + f : CZT + Callable object ``f(x, axis=-1)`` for computing the chirp z-transform + on `x`. + + See Also + -------- + czt : Convenience function for quickly calculating CZT. + ZoomFFT : Class that creates a callable partial FFT function. + + Notes + ----- + The defaults are chosen such that ``f(x)`` is equivalent to + ``fft.fft(x)`` and, if ``m > len(x)``, that ``f(x, m)`` is equivalent to + ``fft.fft(x, m)``. + + If `w` does not lie on the unit circle, then the transform will be + around a spiral with exponentially-increasing radius. Regardless, + angle will increase linearly. + + For transforms that do lie on the unit circle, accuracy is better when + using `ZoomFFT`, since any numerical error in `w` is + accumulated for long data lengths, drifting away from the unit circle. + + The chirp z-transform can be faster than an equivalent FFT with + zero padding. Try it with your own array sizes to see. + + However, the chirp z-transform is considerably less precise than the + equivalent zero-padded FFT. + + As this CZT is implemented using the Bluestein algorithm, it can compute + large prime-length Fourier transforms in O(N log N) time, rather than the + O(N**2) time required by the direct DFT calculation. (`scipy.fft` also + uses Bluestein's algorithm'.) + + (The name "chirp z-transform" comes from the use of a chirp in the + Bluestein algorithm. It does not decompose signals into chirps, like + other transforms with "chirp" in the name.) + + References + ---------- + .. [1] Leo I. Bluestein, "A linear filtering approach to the computation + of the discrete Fourier transform," Northeast Electronics Research + and Engineering Meeting Record 10, 218-219 (1968). + .. [2] Rabiner, Schafer, and Rader, "The chirp z-transform algorithm and + its application," Bell Syst. Tech. J. 48, 1249-1292 (1969). + + Examples + -------- + Compute multiple prime-length FFTs: + + >>> from scipy.signal import CZT + >>> import numpy as np + >>> a = np.random.rand(7) + >>> b = np.random.rand(7) + >>> c = np.random.rand(7) + >>> czt_7 = CZT(n=7) + >>> A = czt_7(a) + >>> B = czt_7(b) + >>> C = czt_7(c) + + Display the points at which the FFT is calculated: + + >>> czt_7.points() + array([ 1.00000000+0.j , 0.62348980+0.78183148j, + -0.22252093+0.97492791j, -0.90096887+0.43388374j, + -0.90096887-0.43388374j, -0.22252093-0.97492791j, + 0.62348980-0.78183148j]) + >>> import matplotlib.pyplot as plt + >>> plt.plot(czt_7.points().real, czt_7.points().imag, 'o') + >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3)) + >>> plt.axis('equal') + >>> plt.show() + """ + + def __init__(self, n, m=None, w=None, a=1+0j): + m = _validate_sizes(n, m) + + k = arange(max(m, n), dtype=np.min_scalar_type(-max(m, n)**2)) + + if w is None: + # Nothing specified, default to FFT-like + w = cmath.exp(-2j*pi/m) + wk2 = np.exp(-(1j * pi * ((k**2) % (2*m))) / m) + else: + # w specified + wk2 = w**(k**2/2.) + + a = 1.0 * a # at least float + + self.w, self.a = w, a + self.m, self.n = m, n + + nfft = next_fast_len(n + m - 1) + self._Awk2 = a**-k[:n] * wk2[:n] + self._nfft = nfft + self._Fwk2 = fft(1/np.hstack((wk2[n-1:0:-1], wk2[:m])), nfft) + self._wk2 = wk2[:m] + self._yidx = slice(n-1, n+m-1) + + def __call__(self, x, *, axis=-1): + """ + Calculate the chirp z-transform of a signal. + + Parameters + ---------- + x : array + The signal to transform. + axis : int, optional + Axis over which to compute the FFT. If not given, the last axis is + used. + + Returns + ------- + out : ndarray + An array of the same dimensions as `x`, but with the length of the + transformed axis set to `m`. + """ + x = np.asarray(x) + if x.shape[axis] != self.n: + raise ValueError(f"CZT defined for length {self.n}, not " + f"{x.shape[axis]}") + # Calculate transpose coordinates, to allow operation on any given axis + trnsp = np.arange(x.ndim) + trnsp[[axis, -1]] = [-1, axis] + x = x.transpose(*trnsp) + y = ifft(self._Fwk2 * fft(x*self._Awk2, self._nfft)) + y = y[..., self._yidx] * self._wk2 + return y.transpose(*trnsp) + + def points(self): + """ + Return the points at which the chirp z-transform is computed. + """ + return czt_points(self.m, self.w, self.a) + + +class ZoomFFT(CZT): + """ + Create a callable zoom FFT transform function. + + This is a specialization of the chirp z-transform (`CZT`) for a set of + equally-spaced frequencies around the unit circle, used to calculate a + section of the FFT more efficiently than calculating the entire FFT and + truncating. + + Parameters + ---------- + n : int + The size of the signal. + fn : array_like + A length-2 sequence [`f1`, `f2`] giving the frequency range, or a + scalar, for which the range [0, `fn`] is assumed. + m : int, optional + The number of points to evaluate. Default is `n`. + fs : float, optional + The sampling frequency. If ``fs=10`` represented 10 kHz, for example, + then `f1` and `f2` would also be given in kHz. + The default sampling frequency is 2, so `f1` and `f2` should be + in the range [0, 1] to keep the transform below the Nyquist + frequency. + endpoint : bool, optional + If True, `f2` is the last sample. Otherwise, it is not included. + Default is False. + + Returns + ------- + f : ZoomFFT + Callable object ``f(x, axis=-1)`` for computing the zoom FFT on `x`. + + See Also + -------- + zoom_fft : Convenience function for calculating a zoom FFT. + + Notes + ----- + The defaults are chosen such that ``f(x, 2)`` is equivalent to + ``fft.fft(x)`` and, if ``m > len(x)``, that ``f(x, 2, m)`` is equivalent to + ``fft.fft(x, m)``. + + Sampling frequency is 1/dt, the time step between samples in the + signal `x`. The unit circle corresponds to frequencies from 0 up + to the sampling frequency. The default sampling frequency of 2 + means that `f1`, `f2` values up to the Nyquist frequency are in the + range [0, 1). For `f1`, `f2` values expressed in radians, a sampling + frequency of 2*pi should be used. + + Remember that a zoom FFT can only interpolate the points of the existing + FFT. It cannot help to resolve two separate nearby frequencies. + Frequency resolution can only be increased by increasing acquisition + time. + + These functions are implemented using Bluestein's algorithm (as is + `scipy.fft`). [2]_ + + References + ---------- + .. [1] Steve Alan Shilling, "A study of the chirp z-transform and its + applications", pg 29 (1970) + https://krex.k-state.edu/dspace/bitstream/handle/2097/7844/LD2668R41972S43.pdf + .. [2] Leo I. Bluestein, "A linear filtering approach to the computation + of the discrete Fourier transform," Northeast Electronics Research + and Engineering Meeting Record 10, 218-219 (1968). + + Examples + -------- + To plot the transform results use something like the following: + + >>> import numpy as np + >>> from scipy.signal import ZoomFFT + >>> t = np.linspace(0, 1, 1021) + >>> x = np.cos(2*np.pi*15*t) + np.sin(2*np.pi*17*t) + >>> f1, f2 = 5, 27 + >>> transform = ZoomFFT(len(x), [f1, f2], len(x), fs=1021) + >>> X = transform(x) + >>> f = np.linspace(f1, f2, len(x)) + >>> import matplotlib.pyplot as plt + >>> plt.plot(f, 20*np.log10(np.abs(X))) + >>> plt.show() + """ + + def __init__(self, n, fn, m=None, *, fs=2, endpoint=False): + m = _validate_sizes(n, m) + + k = arange(max(m, n), dtype=np.min_scalar_type(-max(m, n)**2)) + + if np.size(fn) == 2: + f1, f2 = fn + elif np.size(fn) == 1: + f1, f2 = 0.0, fn + else: + raise ValueError('fn must be a scalar or 2-length sequence') + + self.f1, self.f2, self.fs = f1, f2, fs + + if endpoint: + scale = ((f2 - f1) * m) / (fs * (m - 1)) + else: + scale = (f2 - f1) / fs + a = cmath.exp(2j * pi * f1/fs) + wk2 = np.exp(-(1j * pi * scale * k**2) / m) + + self.w = cmath.exp(-2j*pi/m * scale) + self.a = a + self.m, self.n = m, n + + ak = np.exp(-2j * pi * f1/fs * k[:n]) + self._Awk2 = ak * wk2[:n] + + nfft = next_fast_len(n + m - 1) + self._nfft = nfft + self._Fwk2 = fft(1/np.hstack((wk2[n-1:0:-1], wk2[:m])), nfft) + self._wk2 = wk2[:m] + self._yidx = slice(n-1, n+m-1) + + +def czt(x, m=None, w=None, a=1+0j, *, axis=-1): + """ + Compute the frequency response around a spiral in the Z plane. + + Parameters + ---------- + x : array + The signal to transform. + m : int, optional + The number of output points desired. Default is the length of the + input data. + w : complex, optional + The ratio between points in each step. This must be precise or the + accumulated error will degrade the tail of the output sequence. + Defaults to equally spaced points around the entire unit circle. + a : complex, optional + The starting point in the complex plane. Default is 1+0j. + axis : int, optional + Axis over which to compute the FFT. If not given, the last axis is + used. + + Returns + ------- + out : ndarray + An array of the same dimensions as `x`, but with the length of the + transformed axis set to `m`. + + See Also + -------- + CZT : Class that creates a callable chirp z-transform function. + zoom_fft : Convenience function for partial FFT calculations. + + Notes + ----- + The defaults are chosen such that ``signal.czt(x)`` is equivalent to + ``fft.fft(x)`` and, if ``m > len(x)``, that ``signal.czt(x, m)`` is + equivalent to ``fft.fft(x, m)``. + + If the transform needs to be repeated, use `CZT` to construct a + specialized transform function which can be reused without + recomputing constants. + + An example application is in system identification, repeatedly evaluating + small slices of the z-transform of a system, around where a pole is + expected to exist, to refine the estimate of the pole's true location. [1]_ + + References + ---------- + .. [1] Steve Alan Shilling, "A study of the chirp z-transform and its + applications", pg 20 (1970) + https://krex.k-state.edu/dspace/bitstream/handle/2097/7844/LD2668R41972S43.pdf + + Examples + -------- + Generate a sinusoid: + + >>> import numpy as np + >>> f1, f2, fs = 8, 10, 200 # Hz + >>> t = np.linspace(0, 1, fs, endpoint=False) + >>> x = np.sin(2*np.pi*t*f2) + >>> import matplotlib.pyplot as plt + >>> plt.plot(t, x) + >>> plt.axis([0, 1, -1.1, 1.1]) + >>> plt.show() + + Its discrete Fourier transform has all of its energy in a single frequency + bin: + + >>> from scipy.fft import rfft, rfftfreq + >>> from scipy.signal import czt, czt_points + >>> plt.plot(rfftfreq(fs, 1/fs), abs(rfft(x))) + >>> plt.margins(0, 0.1) + >>> plt.show() + + However, if the sinusoid is logarithmically-decaying: + + >>> x = np.exp(-t*f1) * np.sin(2*np.pi*t*f2) + >>> plt.plot(t, x) + >>> plt.axis([0, 1, -1.1, 1.1]) + >>> plt.show() + + the DFT will have spectral leakage: + + >>> plt.plot(rfftfreq(fs, 1/fs), abs(rfft(x))) + >>> plt.margins(0, 0.1) + >>> plt.show() + + While the DFT always samples the z-transform around the unit circle, the + chirp z-transform allows us to sample the Z-transform along any + logarithmic spiral, such as a circle with radius smaller than unity: + + >>> M = fs // 2 # Just positive frequencies, like rfft + >>> a = np.exp(-f1/fs) # Starting point of the circle, radius < 1 + >>> w = np.exp(-1j*np.pi/M) # "Step size" of circle + >>> points = czt_points(M + 1, w, a) # M + 1 to include Nyquist + >>> plt.plot(points.real, points.imag, '.') + >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3)) + >>> plt.axis('equal'); plt.axis([-1.05, 1.05, -0.05, 1.05]) + >>> plt.show() + + With the correct radius, this transforms the decaying sinusoid (and others + with the same decay rate) without spectral leakage: + + >>> z_vals = czt(x, M + 1, w, a) # Include Nyquist for comparison to rfft + >>> freqs = np.angle(points)*fs/(2*np.pi) # angle = omega, radius = sigma + >>> plt.plot(freqs, abs(z_vals)) + >>> plt.margins(0, 0.1) + >>> plt.show() + """ + x = np.asarray(x) + transform = CZT(x.shape[axis], m=m, w=w, a=a) + return transform(x, axis=axis) + + +def zoom_fft(x, fn, m=None, *, fs=2, endpoint=False, axis=-1): + """ + Compute the DFT of `x` only for frequencies in range `fn`. + + Parameters + ---------- + x : array + The signal to transform. + fn : array_like + A length-2 sequence [`f1`, `f2`] giving the frequency range, or a + scalar, for which the range [0, `fn`] is assumed. + m : int, optional + The number of points to evaluate. The default is the length of `x`. + fs : float, optional + The sampling frequency. If ``fs=10`` represented 10 kHz, for example, + then `f1` and `f2` would also be given in kHz. + The default sampling frequency is 2, so `f1` and `f2` should be + in the range [0, 1] to keep the transform below the Nyquist + frequency. + endpoint : bool, optional + If True, `f2` is the last sample. Otherwise, it is not included. + Default is False. + axis : int, optional + Axis over which to compute the FFT. If not given, the last axis is + used. + + Returns + ------- + out : ndarray + The transformed signal. The Fourier transform will be calculated + at the points f1, f1+df, f1+2df, ..., f2, where df=(f2-f1)/m. + + See Also + -------- + ZoomFFT : Class that creates a callable partial FFT function. + + Notes + ----- + The defaults are chosen such that ``signal.zoom_fft(x, 2)`` is equivalent + to ``fft.fft(x)`` and, if ``m > len(x)``, that ``signal.zoom_fft(x, 2, m)`` + is equivalent to ``fft.fft(x, m)``. + + To graph the magnitude of the resulting transform, use:: + + plot(linspace(f1, f2, m, endpoint=False), abs(zoom_fft(x, [f1, f2], m))) + + If the transform needs to be repeated, use `ZoomFFT` to construct + a specialized transform function which can be reused without + recomputing constants. + + Examples + -------- + To plot the transform results use something like the following: + + >>> import numpy as np + >>> from scipy.signal import zoom_fft + >>> t = np.linspace(0, 1, 1021) + >>> x = np.cos(2*np.pi*15*t) + np.sin(2*np.pi*17*t) + >>> f1, f2 = 5, 27 + >>> X = zoom_fft(x, [f1, f2], len(x), fs=1021) + >>> f = np.linspace(f1, f2, len(x)) + >>> import matplotlib.pyplot as plt + >>> plt.plot(f, 20*np.log10(np.abs(X))) + >>> plt.show() + """ + x = np.asarray(x) + transform = ZoomFFT(x.shape[axis], fn, m=m, fs=fs, endpoint=endpoint) + return transform(x, axis=axis) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..3f52d8e39ae5eee9faf3efc1b76eaa7d94af0ee9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_filter_design.py @@ -0,0 +1,5624 @@ +"""Filter design.""" +import math +import operator +import warnings + +import numpy +import numpy as np +from numpy import (atleast_1d, poly, polyval, roots, real, asarray, + resize, pi, absolute, sqrt, tan, log10, + arcsinh, sin, exp, cosh, arccosh, ceil, conjugate, + zeros, sinh, append, concatenate, prod, ones, full, array, + mintypecode) +from numpy.polynomial.polynomial import polyval as npp_polyval +from numpy.polynomial.polynomial import polyvalfromroots + +from scipy import special, optimize, fft as sp_fft +from scipy.special import comb +from scipy._lib._util import float_factorial +from scipy.signal._arraytools import _validate_fs + + +__all__ = ['findfreqs', 'freqs', 'freqz', 'tf2zpk', 'zpk2tf', 'normalize', + 'lp2lp', 'lp2hp', 'lp2bp', 'lp2bs', 'bilinear', 'iirdesign', + 'iirfilter', 'butter', 'cheby1', 'cheby2', 'ellip', 'bessel', + 'band_stop_obj', 'buttord', 'cheb1ord', 'cheb2ord', 'ellipord', + 'buttap', 'cheb1ap', 'cheb2ap', 'ellipap', 'besselap', + 'BadCoefficients', 'freqs_zpk', 'freqz_zpk', + 'tf2sos', 'sos2tf', 'zpk2sos', 'sos2zpk', 'group_delay', + 'sosfreqz', 'iirnotch', 'iirpeak', 'bilinear_zpk', + 'lp2lp_zpk', 'lp2hp_zpk', 'lp2bp_zpk', 'lp2bs_zpk', + 'gammatone', 'iircomb'] + + +class BadCoefficients(UserWarning): + """Warning about badly conditioned filter coefficients""" + pass + + +abs = absolute + + +def _is_int_type(x): + """ + Check if input is of a scalar integer type (so ``5`` and ``array(5)`` will + pass, while ``5.0`` and ``array([5])`` will fail. + """ + if np.ndim(x) != 0: + # Older versions of NumPy did not raise for np.array([1]).__index__() + # This is safe to remove when support for those versions is dropped + return False + try: + operator.index(x) + except TypeError: + return False + else: + return True + + +def findfreqs(num, den, N, kind='ba'): + """ + Find array of frequencies for computing the response of an analog filter. + + Parameters + ---------- + num, den : array_like, 1-D + The polynomial coefficients of the numerator and denominator of the + transfer function of the filter or LTI system, where the coefficients + are ordered from highest to lowest degree. Or, the roots of the + transfer function numerator and denominator (i.e., zeroes and poles). + N : int + The length of the array to be computed. + kind : str {'ba', 'zp'}, optional + Specifies whether the numerator and denominator are specified by their + polynomial coefficients ('ba'), or their roots ('zp'). + + Returns + ------- + w : (N,) ndarray + A 1-D array of frequencies, logarithmically spaced. + + Examples + -------- + Find a set of nine frequencies that span the "interesting part" of the + frequency response for the filter with the transfer function + + H(s) = s / (s^2 + 8s + 25) + + >>> from scipy import signal + >>> signal.findfreqs([1, 0], [1, 8, 25], N=9) + array([ 1.00000000e-02, 3.16227766e-02, 1.00000000e-01, + 3.16227766e-01, 1.00000000e+00, 3.16227766e+00, + 1.00000000e+01, 3.16227766e+01, 1.00000000e+02]) + """ + if kind == 'ba': + ep = atleast_1d(roots(den)) + 0j + tz = atleast_1d(roots(num)) + 0j + elif kind == 'zp': + ep = atleast_1d(den) + 0j + tz = atleast_1d(num) + 0j + else: + raise ValueError("input must be one of {'ba', 'zp'}") + + if len(ep) == 0: + ep = atleast_1d(-1000) + 0j + + ez = np.r_[ep[ep.imag >= 0], tz[(np.abs(tz) < 1e5) & (tz.imag >= 0)]] + + integ = np.abs(ez) < 1e-10 + hfreq = np.round(np.log10(np.max(3 * np.abs(ez.real + integ) + + 1.5 * ez.imag)) + 0.5) + lfreq = np.round(np.log10(0.1 * np.min(np.abs((ez + integ).real) + + 2 * ez.imag)) - 0.5) + + w = np.logspace(lfreq, hfreq, N) + return w + + +def freqs(b, a, worN=200, plot=None): + """ + Compute frequency response of analog filter. + + Given the M-order numerator `b` and N-order denominator `a` of an analog + filter, compute its frequency response:: + + b[0]*(jw)**M + b[1]*(jw)**(M-1) + ... + b[M] + H(w) = ---------------------------------------------- + a[0]*(jw)**N + a[1]*(jw)**(N-1) + ... + a[N] + + Parameters + ---------- + b : array_like + Numerator of a linear filter. + a : array_like + Denominator of a linear filter. + worN : {None, int, array_like}, optional + If None, then compute at 200 frequencies around the interesting parts + of the response curve (determined by pole-zero locations). If a single + integer, then compute at that many frequencies. Otherwise, compute the + response at the angular frequencies (e.g., rad/s) given in `worN`. + plot : callable, optional + A callable that takes two arguments. If given, the return parameters + `w` and `h` are passed to plot. Useful for plotting the frequency + response inside `freqs`. + + Returns + ------- + w : ndarray + The angular frequencies at which `h` was computed. + h : ndarray + The frequency response. + + See Also + -------- + freqz : Compute the frequency response of a digital filter. + + Notes + ----- + Using Matplotlib's "plot" function as the callable for `plot` produces + unexpected results, this plots the real part of the complex transfer + function, not the magnitude. Try ``lambda w, h: plot(w, abs(h))``. + + Examples + -------- + >>> from scipy.signal import freqs, iirfilter + >>> import numpy as np + + >>> b, a = iirfilter(4, [1, 10], 1, 60, analog=True, ftype='cheby1') + + >>> w, h = freqs(b, a, worN=np.logspace(-1, 2, 1000)) + + >>> import matplotlib.pyplot as plt + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.xlabel('Frequency') + >>> plt.ylabel('Amplitude response [dB]') + >>> plt.grid(True) + >>> plt.show() + + """ + if worN is None: + # For backwards compatibility + w = findfreqs(b, a, 200) + elif _is_int_type(worN): + w = findfreqs(b, a, worN) + else: + w = atleast_1d(worN) + + s = 1j * w + h = polyval(b, s) / polyval(a, s) + if plot is not None: + plot(w, h) + + return w, h + + +def freqs_zpk(z, p, k, worN=200): + """ + Compute frequency response of analog filter. + + Given the zeros `z`, poles `p`, and gain `k` of a filter, compute its + frequency response:: + + (jw-z[0]) * (jw-z[1]) * ... * (jw-z[-1]) + H(w) = k * ---------------------------------------- + (jw-p[0]) * (jw-p[1]) * ... * (jw-p[-1]) + + Parameters + ---------- + z : array_like + Zeroes of a linear filter + p : array_like + Poles of a linear filter + k : scalar + Gain of a linear filter + worN : {None, int, array_like}, optional + If None, then compute at 200 frequencies around the interesting parts + of the response curve (determined by pole-zero locations). If a single + integer, then compute at that many frequencies. Otherwise, compute the + response at the angular frequencies (e.g., rad/s) given in `worN`. + + Returns + ------- + w : ndarray + The angular frequencies at which `h` was computed. + h : ndarray + The frequency response. + + See Also + -------- + freqs : Compute the frequency response of an analog filter in TF form + freqz : Compute the frequency response of a digital filter in TF form + freqz_zpk : Compute the frequency response of a digital filter in ZPK form + + Notes + ----- + .. versionadded:: 0.19.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import freqs_zpk, iirfilter + + >>> z, p, k = iirfilter(4, [1, 10], 1, 60, analog=True, ftype='cheby1', + ... output='zpk') + + >>> w, h = freqs_zpk(z, p, k, worN=np.logspace(-1, 2, 1000)) + + >>> import matplotlib.pyplot as plt + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.xlabel('Frequency') + >>> plt.ylabel('Amplitude response [dB]') + >>> plt.grid(True) + >>> plt.show() + + """ + k = np.asarray(k) + if k.size > 1: + raise ValueError('k must be a single scalar gain') + + if worN is None: + # For backwards compatibility + w = findfreqs(z, p, 200, kind='zp') + elif _is_int_type(worN): + w = findfreqs(z, p, worN, kind='zp') + else: + w = worN + + w = atleast_1d(w) + s = 1j * w + num = polyvalfromroots(s, z) + den = polyvalfromroots(s, p) + h = k * num/den + return w, h + + +def freqz(b, a=1, worN=512, whole=False, plot=None, fs=2*pi, + include_nyquist=False): + """ + Compute the frequency response of a digital filter. + + Given the M-order numerator `b` and N-order denominator `a` of a digital + filter, compute its frequency response:: + + jw -jw -jwM + jw B(e ) b[0] + b[1]e + ... + b[M]e + H(e ) = ------ = ----------------------------------- + jw -jw -jwN + A(e ) a[0] + a[1]e + ... + a[N]e + + Parameters + ---------- + b : array_like + Numerator of a linear filter. If `b` has dimension greater than 1, + it is assumed that the coefficients are stored in the first dimension, + and ``b.shape[1:]``, ``a.shape[1:]``, and the shape of the frequencies + array must be compatible for broadcasting. + a : array_like + Denominator of a linear filter. If `b` has dimension greater than 1, + it is assumed that the coefficients are stored in the first dimension, + and ``b.shape[1:]``, ``a.shape[1:]``, and the shape of the frequencies + array must be compatible for broadcasting. + worN : {None, int, array_like}, optional + If a single integer, then compute at that many frequencies (default is + N=512). This is a convenient alternative to:: + + np.linspace(0, fs if whole else fs/2, N, endpoint=include_nyquist) + + Using a number that is fast for FFT computations can result in + faster computations (see Notes). + + If an array_like, compute the response at the frequencies given. + These are in the same units as `fs`. + whole : bool, optional + Normally, frequencies are computed from 0 to the Nyquist frequency, + fs/2 (upper-half of unit-circle). If `whole` is True, compute + frequencies from 0 to fs. Ignored if worN is array_like. + plot : callable + A callable that takes two arguments. If given, the return parameters + `w` and `h` are passed to plot. Useful for plotting the frequency + response inside `freqz`. + fs : float, optional + The sampling frequency of the digital system. Defaults to 2*pi + radians/sample (so w is from 0 to pi). + + .. versionadded:: 1.2.0 + include_nyquist : bool, optional + If `whole` is False and `worN` is an integer, setting `include_nyquist` + to True will include the last frequency (Nyquist frequency) and is + otherwise ignored. + + .. versionadded:: 1.5.0 + + Returns + ------- + w : ndarray + The frequencies at which `h` was computed, in the same units as `fs`. + By default, `w` is normalized to the range [0, pi) (radians/sample). + h : ndarray + The frequency response, as complex numbers. + + See Also + -------- + freqz_zpk + sosfreqz + + Notes + ----- + Using Matplotlib's :func:`matplotlib.pyplot.plot` function as the callable + for `plot` produces unexpected results, as this plots the real part of the + complex transfer function, not the magnitude. + Try ``lambda w, h: plot(w, np.abs(h))``. + + A direct computation via (R)FFT is used to compute the frequency response + when the following conditions are met: + + 1. An integer value is given for `worN`. + 2. `worN` is fast to compute via FFT (i.e., + `next_fast_len(worN) ` equals `worN`). + 3. The denominator coefficients are a single value (``a.shape[0] == 1``). + 4. `worN` is at least as long as the numerator coefficients + (``worN >= b.shape[0]``). + 5. If ``b.ndim > 1``, then ``b.shape[-1] == 1``. + + For long FIR filters, the FFT approach can have lower error and be much + faster than the equivalent direct polynomial calculation. + + Examples + -------- + >>> from scipy import signal + >>> import numpy as np + >>> b = signal.firwin(80, 0.5, window=('kaiser', 8)) + >>> w, h = signal.freqz(b) + + >>> import matplotlib.pyplot as plt + >>> fig, ax1 = plt.subplots() + >>> ax1.set_title('Digital filter frequency response') + + >>> ax1.plot(w, 20 * np.log10(abs(h)), 'b') + >>> ax1.set_ylabel('Amplitude [dB]', color='b') + >>> ax1.set_xlabel('Frequency [rad/sample]') + + >>> ax2 = ax1.twinx() + >>> angles = np.unwrap(np.angle(h)) + >>> ax2.plot(w, angles, 'g') + >>> ax2.set_ylabel('Angle (radians)', color='g') + >>> ax2.grid(True) + >>> ax2.axis('tight') + >>> plt.show() + + Broadcasting Examples + + Suppose we have two FIR filters whose coefficients are stored in the + rows of an array with shape (2, 25). For this demonstration, we'll + use random data: + + >>> rng = np.random.default_rng() + >>> b = rng.random((2, 25)) + + To compute the frequency response for these two filters with one call + to `freqz`, we must pass in ``b.T``, because `freqz` expects the first + axis to hold the coefficients. We must then extend the shape with a + trivial dimension of length 1 to allow broadcasting with the array + of frequencies. That is, we pass in ``b.T[..., np.newaxis]``, which has + shape (25, 2, 1): + + >>> w, h = signal.freqz(b.T[..., np.newaxis], worN=1024) + >>> w.shape + (1024,) + >>> h.shape + (2, 1024) + + Now, suppose we have two transfer functions, with the same numerator + coefficients ``b = [0.5, 0.5]``. The coefficients for the two denominators + are stored in the first dimension of the 2-D array `a`:: + + a = [ 1 1 ] + [ -0.25, -0.5 ] + + >>> b = np.array([0.5, 0.5]) + >>> a = np.array([[1, 1], [-0.25, -0.5]]) + + Only `a` is more than 1-D. To make it compatible for + broadcasting with the frequencies, we extend it with a trivial dimension + in the call to `freqz`: + + >>> w, h = signal.freqz(b, a[..., np.newaxis], worN=1024) + >>> w.shape + (1024,) + >>> h.shape + (2, 1024) + + """ + b = atleast_1d(b) + a = atleast_1d(a) + + fs = _validate_fs(fs, allow_none=False) + + if worN is None: + # For backwards compatibility + worN = 512 + + h = None + + if _is_int_type(worN): + N = operator.index(worN) + del worN + if N < 0: + raise ValueError(f'worN must be nonnegative, got {N}') + lastpoint = 2 * pi if whole else pi + # if include_nyquist is true and whole is false, w should + # include end point + w = np.linspace(0, lastpoint, N, + endpoint=include_nyquist and not whole) + n_fft = N if whole else 2 * (N - 1) if include_nyquist else 2 * N + if (a.size == 1 and (b.ndim == 1 or (b.shape[-1] == 1)) + and n_fft >= b.shape[0] + and n_fft > 0): # TODO: review threshold acc. to benchmark? + if np.isrealobj(b) and np.isrealobj(a): + fft_func = sp_fft.rfft + else: + fft_func = sp_fft.fft + h = fft_func(b, n=n_fft, axis=0)[:N] + h /= a + if fft_func is sp_fft.rfft and whole: + # exclude DC and maybe Nyquist (no need to use axis_reverse + # here because we can build reversal with the truncation) + stop = -1 if n_fft % 2 == 1 else -2 + h_flip = slice(stop, 0, -1) + h = np.concatenate((h, h[h_flip].conj())) + if b.ndim > 1: + # Last axis of h has length 1, so drop it. + h = h[..., 0] + # Move the first axis of h to the end. + h = np.moveaxis(h, 0, -1) + else: + w = atleast_1d(worN) + del worN + w = 2*pi*w/fs + + if h is None: # still need to compute using freqs w + zm1 = exp(-1j * w) + h = (npp_polyval(zm1, b, tensor=False) / + npp_polyval(zm1, a, tensor=False)) + + w = w*(fs/(2*pi)) + + if plot is not None: + plot(w, h) + + return w, h + + +def freqz_zpk(z, p, k, worN=512, whole=False, fs=2*pi): + r""" + Compute the frequency response of a digital filter in ZPK form. + + Given the Zeros, Poles and Gain of a digital filter, compute its frequency + response: + + :math:`H(z)=k \prod_i (z - Z[i]) / \prod_j (z - P[j])` + + where :math:`k` is the `gain`, :math:`Z` are the `zeros` and :math:`P` are + the `poles`. + + Parameters + ---------- + z : array_like + Zeroes of a linear filter + p : array_like + Poles of a linear filter + k : scalar + Gain of a linear filter + worN : {None, int, array_like}, optional + If a single integer, then compute at that many frequencies (default is + N=512). + + If an array_like, compute the response at the frequencies given. + These are in the same units as `fs`. + whole : bool, optional + Normally, frequencies are computed from 0 to the Nyquist frequency, + fs/2 (upper-half of unit-circle). If `whole` is True, compute + frequencies from 0 to fs. Ignored if w is array_like. + fs : float, optional + The sampling frequency of the digital system. Defaults to 2*pi + radians/sample (so w is from 0 to pi). + + .. versionadded:: 1.2.0 + + Returns + ------- + w : ndarray + The frequencies at which `h` was computed, in the same units as `fs`. + By default, `w` is normalized to the range [0, pi) (radians/sample). + h : ndarray + The frequency response, as complex numbers. + + See Also + -------- + freqs : Compute the frequency response of an analog filter in TF form + freqs_zpk : Compute the frequency response of an analog filter in ZPK form + freqz : Compute the frequency response of a digital filter in TF form + + Notes + ----- + .. versionadded:: 0.19.0 + + Examples + -------- + Design a 4th-order digital Butterworth filter with cut-off of 100 Hz in a + system with sample rate of 1000 Hz, and plot the frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> z, p, k = signal.butter(4, 100, output='zpk', fs=1000) + >>> w, h = signal.freqz_zpk(z, p, k, fs=1000) + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(1, 1, 1) + >>> ax1.set_title('Digital filter frequency response') + + >>> ax1.plot(w, 20 * np.log10(abs(h)), 'b') + >>> ax1.set_ylabel('Amplitude [dB]', color='b') + >>> ax1.set_xlabel('Frequency [Hz]') + >>> ax1.grid(True) + + >>> ax2 = ax1.twinx() + >>> angles = np.unwrap(np.angle(h)) + >>> ax2.plot(w, angles, 'g') + >>> ax2.set_ylabel('Angle [radians]', color='g') + + >>> plt.axis('tight') + >>> plt.show() + + """ + z, p = map(atleast_1d, (z, p)) + + fs = _validate_fs(fs, allow_none=False) + + if whole: + lastpoint = 2 * pi + else: + lastpoint = pi + + if worN is None: + # For backwards compatibility + w = numpy.linspace(0, lastpoint, 512, endpoint=False) + elif _is_int_type(worN): + w = numpy.linspace(0, lastpoint, worN, endpoint=False) + else: + w = atleast_1d(worN) + w = 2*pi*w/fs + + zm1 = exp(1j * w) + h = k * polyvalfromroots(zm1, z) / polyvalfromroots(zm1, p) + + w = w*(fs/(2*pi)) + + return w, h + + +def group_delay(system, w=512, whole=False, fs=2*pi): + r"""Compute the group delay of a digital filter. + + The group delay measures by how many samples amplitude envelopes of + various spectral components of a signal are delayed by a filter. + It is formally defined as the derivative of continuous (unwrapped) phase:: + + d jw + D(w) = - -- arg H(e) + dw + + Parameters + ---------- + system : tuple of array_like (b, a) + Numerator and denominator coefficients of a filter transfer function. + w : {None, int, array_like}, optional + If a single integer, then compute at that many frequencies (default is + N=512). + + If an array_like, compute the delay at the frequencies given. These + are in the same units as `fs`. + whole : bool, optional + Normally, frequencies are computed from 0 to the Nyquist frequency, + fs/2 (upper-half of unit-circle). If `whole` is True, compute + frequencies from 0 to fs. Ignored if w is array_like. + fs : float, optional + The sampling frequency of the digital system. Defaults to 2*pi + radians/sample (so w is from 0 to pi). + + .. versionadded:: 1.2.0 + + Returns + ------- + w : ndarray + The frequencies at which group delay was computed, in the same units + as `fs`. By default, `w` is normalized to the range [0, pi) + (radians/sample). + gd : ndarray + The group delay. + + See Also + -------- + freqz : Frequency response of a digital filter + + Notes + ----- + The similar function in MATLAB is called `grpdelay`. + + If the transfer function :math:`H(z)` has zeros or poles on the unit + circle, the group delay at corresponding frequencies is undefined. + When such a case arises the warning is raised and the group delay + is set to 0 at those frequencies. + + For the details of numerical computation of the group delay refer to [1]_. + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] Richard G. Lyons, "Understanding Digital Signal Processing, + 3rd edition", p. 830. + + Examples + -------- + >>> from scipy import signal + >>> b, a = signal.iirdesign(0.1, 0.3, 5, 50, ftype='cheby1') + >>> w, gd = signal.group_delay((b, a)) + + >>> import matplotlib.pyplot as plt + >>> plt.title('Digital filter group delay') + >>> plt.plot(w, gd) + >>> plt.ylabel('Group delay [samples]') + >>> plt.xlabel('Frequency [rad/sample]') + >>> plt.show() + + """ + if w is None: + # For backwards compatibility + w = 512 + + fs = _validate_fs(fs, allow_none=False) + + if _is_int_type(w): + if whole: + w = np.linspace(0, 2 * pi, w, endpoint=False) + else: + w = np.linspace(0, pi, w, endpoint=False) + else: + w = np.atleast_1d(w) + w = 2*pi*w/fs + + b, a = map(np.atleast_1d, system) + c = np.convolve(b, a[::-1]) + cr = c * np.arange(c.size) + z = np.exp(-1j * w) + num = np.polyval(cr[::-1], z) + den = np.polyval(c[::-1], z) + gd = np.real(num / den) - a.size + 1 + singular = ~np.isfinite(gd) + near_singular = np.absolute(den) < 10 * EPSILON + + if np.any(singular): + gd[singular] = 0 + warnings.warn( + "The group delay is singular at frequencies [{}], setting to 0". + format(", ".join(f"{ws:.3f}" for ws in w[singular])), + stacklevel=2 + ) + + elif np.any(near_singular): + warnings.warn( + "The filter's denominator is extremely small at frequencies [{}], \ + around which a singularity may be present". + format(", ".join(f"{ws:.3f}" for ws in w[near_singular])), + stacklevel=2 + ) + + w = w*(fs/(2*pi)) + + return w, gd + + +def _validate_sos(sos): + """Helper to validate a SOS input""" + sos = np.atleast_2d(sos) + if sos.ndim != 2: + raise ValueError('sos array must be 2D') + n_sections, m = sos.shape + if m != 6: + raise ValueError('sos array must be shape (n_sections, 6)') + if not (sos[:, 3] == 1).all(): + raise ValueError('sos[:, 3] should be all ones') + return sos, n_sections + + +def sosfreqz(sos, worN=512, whole=False, fs=2*pi): + r""" + Compute the frequency response of a digital filter in SOS format. + + Given `sos`, an array with shape (n, 6) of second order sections of + a digital filter, compute the frequency response of the system function:: + + B0(z) B1(z) B{n-1}(z) + H(z) = ----- * ----- * ... * --------- + A0(z) A1(z) A{n-1}(z) + + for z = exp(omega*1j), where B{k}(z) and A{k}(z) are numerator and + denominator of the transfer function of the k-th second order section. + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. Each row corresponds to a second-order + section, with the first three columns providing the numerator + coefficients and the last three providing the denominator + coefficients. + worN : {None, int, array_like}, optional + If a single integer, then compute at that many frequencies (default is + N=512). Using a number that is fast for FFT computations can result + in faster computations (see Notes of `freqz`). + + If an array_like, compute the response at the frequencies given (must + be 1-D). These are in the same units as `fs`. + whole : bool, optional + Normally, frequencies are computed from 0 to the Nyquist frequency, + fs/2 (upper-half of unit-circle). If `whole` is True, compute + frequencies from 0 to fs. + fs : float, optional + The sampling frequency of the digital system. Defaults to 2*pi + radians/sample (so w is from 0 to pi). + + .. versionadded:: 1.2.0 + + Returns + ------- + w : ndarray + The frequencies at which `h` was computed, in the same units as `fs`. + By default, `w` is normalized to the range [0, pi) (radians/sample). + h : ndarray + The frequency response, as complex numbers. + + See Also + -------- + freqz, sosfilt + + Notes + ----- + .. versionadded:: 0.19.0 + + Examples + -------- + Design a 15th-order bandpass filter in SOS format. + + >>> from scipy import signal + >>> import numpy as np + >>> sos = signal.ellip(15, 0.5, 60, (0.2, 0.4), btype='bandpass', + ... output='sos') + + Compute the frequency response at 1500 points from DC to Nyquist. + + >>> w, h = signal.sosfreqz(sos, worN=1500) + + Plot the response. + + >>> import matplotlib.pyplot as plt + >>> plt.subplot(2, 1, 1) + >>> db = 20*np.log10(np.maximum(np.abs(h), 1e-5)) + >>> plt.plot(w/np.pi, db) + >>> plt.ylim(-75, 5) + >>> plt.grid(True) + >>> plt.yticks([0, -20, -40, -60]) + >>> plt.ylabel('Gain [dB]') + >>> plt.title('Frequency Response') + >>> plt.subplot(2, 1, 2) + >>> plt.plot(w/np.pi, np.angle(h)) + >>> plt.grid(True) + >>> plt.yticks([-np.pi, -0.5*np.pi, 0, 0.5*np.pi, np.pi], + ... [r'$-\pi$', r'$-\pi/2$', '0', r'$\pi/2$', r'$\pi$']) + >>> plt.ylabel('Phase [rad]') + >>> plt.xlabel('Normalized frequency (1.0 = Nyquist)') + >>> plt.show() + + If the same filter is implemented as a single transfer function, + numerical error corrupts the frequency response: + + >>> b, a = signal.ellip(15, 0.5, 60, (0.2, 0.4), btype='bandpass', + ... output='ba') + >>> w, h = signal.freqz(b, a, worN=1500) + >>> plt.subplot(2, 1, 1) + >>> db = 20*np.log10(np.maximum(np.abs(h), 1e-5)) + >>> plt.plot(w/np.pi, db) + >>> plt.ylim(-75, 5) + >>> plt.grid(True) + >>> plt.yticks([0, -20, -40, -60]) + >>> plt.ylabel('Gain [dB]') + >>> plt.title('Frequency Response') + >>> plt.subplot(2, 1, 2) + >>> plt.plot(w/np.pi, np.angle(h)) + >>> plt.grid(True) + >>> plt.yticks([-np.pi, -0.5*np.pi, 0, 0.5*np.pi, np.pi], + ... [r'$-\pi$', r'$-\pi/2$', '0', r'$\pi/2$', r'$\pi$']) + >>> plt.ylabel('Phase [rad]') + >>> plt.xlabel('Normalized frequency (1.0 = Nyquist)') + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=False) + + sos, n_sections = _validate_sos(sos) + if n_sections == 0: + raise ValueError('Cannot compute frequencies with no sections') + h = 1. + for row in sos: + w, rowh = freqz(row[:3], row[3:], worN=worN, whole=whole, fs=fs) + h *= rowh + return w, h + + +def _cplxreal(z, tol=None): + """ + Split into complex and real parts, combining conjugate pairs. + + The 1-D input vector `z` is split up into its complex (`zc`) and real (`zr`) + elements. Every complex element must be part of a complex-conjugate pair, + which are combined into a single number (with positive imaginary part) in + the output. Two complex numbers are considered a conjugate pair if their + real and imaginary parts differ in magnitude by less than ``tol * abs(z)``. + + Parameters + ---------- + z : array_like + Vector of complex numbers to be sorted and split + tol : float, optional + Relative tolerance for testing realness and conjugate equality. + Default is ``100 * spacing(1)`` of `z`'s data type (i.e., 2e-14 for + float64) + + Returns + ------- + zc : ndarray + Complex elements of `z`, with each pair represented by a single value + having positive imaginary part, sorted first by real part, and then + by magnitude of imaginary part. The pairs are averaged when combined + to reduce error. + zr : ndarray + Real elements of `z` (those having imaginary part less than + `tol` times their magnitude), sorted by value. + + Raises + ------ + ValueError + If there are any complex numbers in `z` for which a conjugate + cannot be found. + + See Also + -------- + _cplxpair + + Examples + -------- + >>> from scipy.signal._filter_design import _cplxreal + >>> a = [4, 3, 1, 2-2j, 2+2j, 2-1j, 2+1j, 2-1j, 2+1j, 1+1j, 1-1j] + >>> zc, zr = _cplxreal(a) + >>> print(zc) + [ 1.+1.j 2.+1.j 2.+1.j 2.+2.j] + >>> print(zr) + [ 1. 3. 4.] + """ + + z = atleast_1d(z) + if z.size == 0: + return z, z + elif z.ndim != 1: + raise ValueError('_cplxreal only accepts 1-D input') + + if tol is None: + # Get tolerance from dtype of input + tol = 100 * np.finfo((1.0 * z).dtype).eps + + # Sort by real part, magnitude of imaginary part (speed up further sorting) + z = z[np.lexsort((abs(z.imag), z.real))] + + # Split reals from conjugate pairs + real_indices = abs(z.imag) <= tol * abs(z) + zr = z[real_indices].real + + if len(zr) == len(z): + # Input is entirely real + return array([]), zr + + # Split positive and negative halves of conjugates + z = z[~real_indices] + zp = z[z.imag > 0] + zn = z[z.imag < 0] + + if len(zp) != len(zn): + raise ValueError('Array contains complex value with no matching ' + 'conjugate.') + + # Find runs of (approximately) the same real part + same_real = np.diff(zp.real) <= tol * abs(zp[:-1]) + diffs = numpy.diff(concatenate(([0], same_real, [0]))) + run_starts = numpy.nonzero(diffs > 0)[0] + run_stops = numpy.nonzero(diffs < 0)[0] + + # Sort each run by their imaginary parts + for i in range(len(run_starts)): + start = run_starts[i] + stop = run_stops[i] + 1 + for chunk in (zp[start:stop], zn[start:stop]): + chunk[...] = chunk[np.lexsort([abs(chunk.imag)])] + + # Check that negatives match positives + if any(abs(zp - zn.conj()) > tol * abs(zn)): + raise ValueError('Array contains complex value with no matching ' + 'conjugate.') + + # Average out numerical inaccuracy in real vs imag parts of pairs + zc = (zp + zn.conj()) / 2 + + return zc, zr + + +def _cplxpair(z, tol=None): + """ + Sort into pairs of complex conjugates. + + Complex conjugates in `z` are sorted by increasing real part. In each + pair, the number with negative imaginary part appears first. + + If pairs have identical real parts, they are sorted by increasing + imaginary magnitude. + + Two complex numbers are considered a conjugate pair if their real and + imaginary parts differ in magnitude by less than ``tol * abs(z)``. The + pairs are forced to be exact complex conjugates by averaging the positive + and negative values. + + Purely real numbers are also sorted, but placed after the complex + conjugate pairs. A number is considered real if its imaginary part is + smaller than `tol` times the magnitude of the number. + + Parameters + ---------- + z : array_like + 1-D input array to be sorted. + tol : float, optional + Relative tolerance for testing realness and conjugate equality. + Default is ``100 * spacing(1)`` of `z`'s data type (i.e., 2e-14 for + float64) + + Returns + ------- + y : ndarray + Complex conjugate pairs followed by real numbers. + + Raises + ------ + ValueError + If there are any complex numbers in `z` for which a conjugate + cannot be found. + + See Also + -------- + _cplxreal + + Examples + -------- + >>> from scipy.signal._filter_design import _cplxpair + >>> a = [4, 3, 1, 2-2j, 2+2j, 2-1j, 2+1j, 2-1j, 2+1j, 1+1j, 1-1j] + >>> z = _cplxpair(a) + >>> print(z) + [ 1.-1.j 1.+1.j 2.-1.j 2.+1.j 2.-1.j 2.+1.j 2.-2.j 2.+2.j 1.+0.j + 3.+0.j 4.+0.j] + """ + + z = atleast_1d(z) + if z.size == 0 or np.isrealobj(z): + return np.sort(z) + + if z.ndim != 1: + raise ValueError('z must be 1-D') + + zc, zr = _cplxreal(z, tol) + + # Interleave complex values and their conjugates, with negative imaginary + # parts first in each pair + zc = np.dstack((zc.conj(), zc)).flatten() + z = np.append(zc, zr) + return z + + +def tf2zpk(b, a): + r"""Return zero, pole, gain (z, p, k) representation from a numerator, + denominator representation of a linear filter. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + + Returns + ------- + z : ndarray + Zeros of the transfer function. + p : ndarray + Poles of the transfer function. + k : float + System gain. + + Notes + ----- + If some values of `b` are too close to 0, they are removed. In that case, + a BadCoefficients warning is emitted. + + The `b` and `a` arrays are interpreted as coefficients for positive, + descending powers of the transfer function variable. So the inputs + :math:`b = [b_0, b_1, ..., b_M]` and :math:`a =[a_0, a_1, ..., a_N]` + can represent an analog filter of the form: + + .. math:: + + H(s) = \frac + {b_0 s^M + b_1 s^{(M-1)} + \cdots + b_M} + {a_0 s^N + a_1 s^{(N-1)} + \cdots + a_N} + + or a discrete-time filter of the form: + + .. math:: + + H(z) = \frac + {b_0 z^M + b_1 z^{(M-1)} + \cdots + b_M} + {a_0 z^N + a_1 z^{(N-1)} + \cdots + a_N} + + This "positive powers" form is found more commonly in controls + engineering. If `M` and `N` are equal (which is true for all filters + generated by the bilinear transform), then this happens to be equivalent + to the "negative powers" discrete-time form preferred in DSP: + + .. math:: + + H(z) = \frac + {b_0 + b_1 z^{-1} + \cdots + b_M z^{-M}} + {a_0 + a_1 z^{-1} + \cdots + a_N z^{-N}} + + Although this is true for common filters, remember that this is not true + in the general case. If `M` and `N` are not equal, the discrete-time + transfer function coefficients must first be converted to the "positive + powers" form before finding the poles and zeros. + + Examples + -------- + Find the zeroes, poles and gain of + a filter with the transfer function + + .. math:: + + H(s) = \frac{3s^2}{s^2 + 5s + 13} + + >>> from scipy.signal import tf2zpk + >>> tf2zpk([3, 0, 0], [1, 5, 13]) + ( array([ 0. , 0. ]), + array([ -2.5+2.59807621j , -2.5-2.59807621j]), + 3.0) + """ + b, a = normalize(b, a) + b = (b + 0.0) / a[0] + a = (a + 0.0) / a[0] + k = b[0] + b /= b[0] + z = roots(b) + p = roots(a) + return z, p, k + + +def zpk2tf(z, p, k): + r""" + Return polynomial transfer function representation from zeros and poles + + Parameters + ---------- + z : array_like + Zeros of the transfer function. + p : array_like + Poles of the transfer function. + k : float + System gain. + + Returns + ------- + b : ndarray + Numerator polynomial coefficients. + a : ndarray + Denominator polynomial coefficients. + + Examples + -------- + Find the polynomial representation of a transfer function H(s) + using its 'zpk' (Zero-Pole-Gain) representation. + + .. math:: + + H(z) = 5 \frac + { (s - 2)(s - 6) } + { (s - 1)(s - 8) } + + >>> from scipy.signal import zpk2tf + >>> z = [2, 6] + >>> p = [1, 8] + >>> k = 5 + >>> zpk2tf(z, p, k) + ( array([ 5., -40., 60.]), array([ 1., -9., 8.])) + """ + z = atleast_1d(z) + k = atleast_1d(k) + if len(z.shape) > 1: + temp = poly(z[0]) + b = np.empty((z.shape[0], z.shape[1] + 1), temp.dtype.char) + if len(k) == 1: + k = [k[0]] * z.shape[0] + for i in range(z.shape[0]): + b[i] = k[i] * poly(z[i]) + else: + b = k * poly(z) + a = atleast_1d(poly(p)) + + # Use real output if possible. Copied from numpy.poly, since + # we can't depend on a specific version of numpy. + if issubclass(b.dtype.type, numpy.complexfloating): + # if complex roots are all complex conjugates, the roots are real. + roots = numpy.asarray(z, complex) + pos_roots = numpy.compress(roots.imag > 0, roots) + neg_roots = numpy.conjugate(numpy.compress(roots.imag < 0, roots)) + if len(pos_roots) == len(neg_roots): + if numpy.all(numpy.sort_complex(neg_roots) == + numpy.sort_complex(pos_roots)): + b = b.real.copy() + + if issubclass(a.dtype.type, numpy.complexfloating): + # if complex roots are all complex conjugates, the roots are real. + roots = numpy.asarray(p, complex) + pos_roots = numpy.compress(roots.imag > 0, roots) + neg_roots = numpy.conjugate(numpy.compress(roots.imag < 0, roots)) + if len(pos_roots) == len(neg_roots): + if numpy.all(numpy.sort_complex(neg_roots) == + numpy.sort_complex(pos_roots)): + a = a.real.copy() + + return b, a + + +def tf2sos(b, a, pairing=None, *, analog=False): + r""" + Return second-order sections from transfer function representation + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + pairing : {None, 'nearest', 'keep_odd', 'minimal'}, optional + The method to use to combine pairs of poles and zeros into sections. + See `zpk2sos` for information and restrictions on `pairing` and + `analog` arguments. + analog : bool, optional + If True, system is analog, otherwise discrete. + + .. versionadded:: 1.8.0 + + Returns + ------- + sos : ndarray + Array of second-order filter coefficients, with shape + ``(n_sections, 6)``. See `sosfilt` for the SOS filter format + specification. + + See Also + -------- + zpk2sos, sosfilt + + Notes + ----- + It is generally discouraged to convert from TF to SOS format, since doing + so usually will not improve numerical precision errors. Instead, consider + designing filters in ZPK format and converting directly to SOS. TF is + converted to SOS by first converting to ZPK format, then converting + ZPK to SOS. + + .. versionadded:: 0.16.0 + + Examples + -------- + Find the 'sos' (second-order sections) of the transfer function H(s) + using its polynomial representation. + + .. math:: + + H(s) = \frac{s^2 - 3.5s - 2}{s^4 + 3s^3 - 15s^2 - 19s + 30} + + >>> from scipy.signal import tf2sos + >>> tf2sos([1, -3.5, -2], [1, 3, -15, -19, 30], analog=True) + array([[ 0. , 0. , 1. , 1. , 2. , -15. ], + [ 1. , -3.5, -2. , 1. , 1. , -2. ]]) + """ + return zpk2sos(*tf2zpk(b, a), pairing=pairing, analog=analog) + + +def sos2tf(sos): + r""" + Return a single transfer function from a series of second-order sections + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. See `sosfilt` for the SOS filter format + specification. + + Returns + ------- + b : ndarray + Numerator polynomial coefficients. + a : ndarray + Denominator polynomial coefficients. + + Notes + ----- + .. versionadded:: 0.16.0 + + Examples + -------- + Find the polynomial representation of an elliptic filter + using its 'sos' (second-order sections) format. + + >>> from scipy.signal import sos2tf + >>> from scipy import signal + >>> sos = signal.ellip(1, 0.001, 50, 0.1, output='sos') + >>> sos2tf(sos) + ( array([0.91256522, 0.91256522, 0. ]), + array([1. , 0.82513043, 0. ])) + """ + sos = np.asarray(sos) + result_type = sos.dtype + if result_type.kind in 'bui': + result_type = np.float64 + + b = np.array([1], dtype=result_type) + a = np.array([1], dtype=result_type) + n_sections = sos.shape[0] + for section in range(n_sections): + b = np.polymul(b, sos[section, :3]) + a = np.polymul(a, sos[section, 3:]) + return b, a + + +def sos2zpk(sos): + """ + Return zeros, poles, and gain of a series of second-order sections + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. See `sosfilt` for the SOS filter format + specification. + + Returns + ------- + z : ndarray + Zeros of the transfer function. + p : ndarray + Poles of the transfer function. + k : float + System gain. + + Notes + ----- + The number of zeros and poles returned will be ``n_sections * 2`` + even if some of these are (effectively) zero. + + .. versionadded:: 0.16.0 + """ + sos = np.asarray(sos) + n_sections = sos.shape[0] + z = np.zeros(n_sections*2, np.complex128) + p = np.zeros(n_sections*2, np.complex128) + k = 1. + for section in range(n_sections): + zpk = tf2zpk(sos[section, :3], sos[section, 3:]) + z[2*section:2*section+len(zpk[0])] = zpk[0] + p[2*section:2*section+len(zpk[1])] = zpk[1] + k *= zpk[2] + return z, p, k + + +def _nearest_real_complex_idx(fro, to, which): + """Get the next closest real or complex element based on distance""" + assert which in ('real', 'complex', 'any') + order = np.argsort(np.abs(fro - to)) + if which == 'any': + return order[0] + else: + mask = np.isreal(fro[order]) + if which == 'complex': + mask = ~mask + return order[np.nonzero(mask)[0][0]] + + +def _single_zpksos(z, p, k): + """Create one second-order section from up to two zeros and poles""" + sos = np.zeros(6) + b, a = zpk2tf(z, p, k) + sos[3-len(b):3] = b + sos[6-len(a):6] = a + return sos + + +def zpk2sos(z, p, k, pairing=None, *, analog=False): + """Return second-order sections from zeros, poles, and gain of a system + + Parameters + ---------- + z : array_like + Zeros of the transfer function. + p : array_like + Poles of the transfer function. + k : float + System gain. + pairing : {None, 'nearest', 'keep_odd', 'minimal'}, optional + The method to use to combine pairs of poles and zeros into sections. + If analog is False and pairing is None, pairing is set to 'nearest'; + if analog is True, pairing must be 'minimal', and is set to that if + it is None. + analog : bool, optional + If True, system is analog, otherwise discrete. + + .. versionadded:: 1.8.0 + + Returns + ------- + sos : ndarray + Array of second-order filter coefficients, with shape + ``(n_sections, 6)``. See `sosfilt` for the SOS filter format + specification. + + See Also + -------- + sosfilt + + Notes + ----- + The algorithm used to convert ZPK to SOS format is designed to + minimize errors due to numerical precision issues. The pairing + algorithm attempts to minimize the peak gain of each biquadratic + section. This is done by pairing poles with the nearest zeros, starting + with the poles closest to the unit circle for discrete-time systems, and + poles closest to the imaginary axis for continuous-time systems. + + ``pairing='minimal'`` outputs may not be suitable for `sosfilt`, + and ``analog=True`` outputs will never be suitable for `sosfilt`. + + *Algorithms* + + The steps in the ``pairing='nearest'``, ``pairing='keep_odd'``, + and ``pairing='minimal'`` algorithms are mostly shared. The + ``'nearest'`` algorithm attempts to minimize the peak gain, while + ``'keep_odd'`` minimizes peak gain under the constraint that + odd-order systems should retain one section as first order. + ``'minimal'`` is similar to ``'keep_odd'``, but no additional + poles or zeros are introduced + + The algorithm steps are as follows: + + As a pre-processing step for ``pairing='nearest'``, + ``pairing='keep_odd'``, add poles or zeros to the origin as + necessary to obtain the same number of poles and zeros for + pairing. If ``pairing == 'nearest'`` and there are an odd number + of poles, add an additional pole and a zero at the origin. + + The following steps are then iterated over until no more poles or + zeros remain: + + 1. Take the (next remaining) pole (complex or real) closest to the + unit circle (or imaginary axis, for ``analog=True``) to + begin a new filter section. + + 2. If the pole is real and there are no other remaining real poles [#]_, + add the closest real zero to the section and leave it as a first + order section. Note that after this step we are guaranteed to be + left with an even number of real poles, complex poles, real zeros, + and complex zeros for subsequent pairing iterations. + + 3. Else: + + 1. If the pole is complex and the zero is the only remaining real + zero*, then pair the pole with the *next* closest zero + (guaranteed to be complex). This is necessary to ensure that + there will be a real zero remaining to eventually create a + first-order section (thus keeping the odd order). + + 2. Else pair the pole with the closest remaining zero (complex or + real). + + 3. Proceed to complete the second-order section by adding another + pole and zero to the current pole and zero in the section: + + 1. If the current pole and zero are both complex, add their + conjugates. + + 2. Else if the pole is complex and the zero is real, add the + conjugate pole and the next closest real zero. + + 3. Else if the pole is real and the zero is complex, add the + conjugate zero and the real pole closest to those zeros. + + 4. Else (we must have a real pole and real zero) add the next + real pole closest to the unit circle, and then add the real + zero closest to that pole. + + .. [#] This conditional can only be met for specific odd-order inputs + with the ``pairing = 'keep_odd'`` or ``'minimal'`` methods. + + .. versionadded:: 0.16.0 + + Examples + -------- + + Design a 6th order low-pass elliptic digital filter for a system with a + sampling rate of 8000 Hz that has a pass-band corner frequency of + 1000 Hz. The ripple in the pass-band should not exceed 0.087 dB, and + the attenuation in the stop-band should be at least 90 dB. + + In the following call to `ellip`, we could use ``output='sos'``, + but for this example, we'll use ``output='zpk'``, and then convert + to SOS format with `zpk2sos`: + + >>> from scipy import signal + >>> import numpy as np + >>> z, p, k = signal.ellip(6, 0.087, 90, 1000/(0.5*8000), output='zpk') + + Now convert to SOS format. + + >>> sos = signal.zpk2sos(z, p, k) + + The coefficients of the numerators of the sections: + + >>> sos[:, :3] + array([[0.0014152 , 0.00248677, 0.0014152 ], + [1. , 0.72976874, 1. ], + [1. , 0.17607852, 1. ]]) + + The symmetry in the coefficients occurs because all the zeros are on the + unit circle. + + The coefficients of the denominators of the sections: + + >>> sos[:, 3:] + array([[ 1. , -1.32544025, 0.46989976], + [ 1. , -1.26118294, 0.62625924], + [ 1. , -1.2570723 , 0.8619958 ]]) + + The next example shows the effect of the `pairing` option. We have a + system with three poles and three zeros, so the SOS array will have + shape (2, 6). The means there is, in effect, an extra pole and an extra + zero at the origin in the SOS representation. + + >>> z1 = np.array([-1, -0.5-0.5j, -0.5+0.5j]) + >>> p1 = np.array([0.75, 0.8+0.1j, 0.8-0.1j]) + + With ``pairing='nearest'`` (the default), we obtain + + >>> signal.zpk2sos(z1, p1, 1) + array([[ 1. , 1. , 0.5 , 1. , -0.75, 0. ], + [ 1. , 1. , 0. , 1. , -1.6 , 0.65]]) + + The first section has the zeros {-0.5-0.05j, -0.5+0.5j} and the poles + {0, 0.75}, and the second section has the zeros {-1, 0} and poles + {0.8+0.1j, 0.8-0.1j}. Note that the extra pole and zero at the origin + have been assigned to different sections. + + With ``pairing='keep_odd'``, we obtain: + + >>> signal.zpk2sos(z1, p1, 1, pairing='keep_odd') + array([[ 1. , 1. , 0. , 1. , -0.75, 0. ], + [ 1. , 1. , 0.5 , 1. , -1.6 , 0.65]]) + + The extra pole and zero at the origin are in the same section. + The first section is, in effect, a first-order section. + + With ``pairing='minimal'``, the first-order section doesn't have + the extra pole and zero at the origin: + + >>> signal.zpk2sos(z1, p1, 1, pairing='minimal') + array([[ 0. , 1. , 1. , 0. , 1. , -0.75], + [ 1. , 1. , 0.5 , 1. , -1.6 , 0.65]]) + + """ + # TODO in the near future: + # 1. Add SOS capability to `filtfilt`, `freqz`, etc. somehow (#3259). + # 2. Make `decimate` use `sosfilt` instead of `lfilter`. + # 3. Make sosfilt automatically simplify sections to first order + # when possible. Note this might make `sosfiltfilt` a bit harder (ICs). + # 4. Further optimizations of the section ordering / pole-zero pairing. + # See the wiki for other potential issues. + + if pairing is None: + pairing = 'minimal' if analog else 'nearest' + + valid_pairings = ['nearest', 'keep_odd', 'minimal'] + if pairing not in valid_pairings: + raise ValueError(f'pairing must be one of {valid_pairings}, not {pairing}') + + if analog and pairing != 'minimal': + raise ValueError('for analog zpk2sos conversion, ' + 'pairing must be "minimal"') + + if len(z) == len(p) == 0: + if not analog: + return np.array([[k, 0., 0., 1., 0., 0.]]) + else: + return np.array([[0., 0., k, 0., 0., 1.]]) + + if pairing != 'minimal': + # ensure we have the same number of poles and zeros, and make copies + p = np.concatenate((p, np.zeros(max(len(z) - len(p), 0)))) + z = np.concatenate((z, np.zeros(max(len(p) - len(z), 0)))) + n_sections = (max(len(p), len(z)) + 1) // 2 + + if len(p) % 2 == 1 and pairing == 'nearest': + p = np.concatenate((p, [0.])) + z = np.concatenate((z, [0.])) + assert len(p) == len(z) + else: + if len(p) < len(z): + raise ValueError('for analog zpk2sos conversion, ' + 'must have len(p)>=len(z)') + + n_sections = (len(p) + 1) // 2 + + # Ensure we have complex conjugate pairs + # (note that _cplxreal only gives us one element of each complex pair): + z = np.concatenate(_cplxreal(z)) + p = np.concatenate(_cplxreal(p)) + if not np.isreal(k): + raise ValueError('k must be real') + k = k.real + + if not analog: + # digital: "worst" is the closest to the unit circle + def idx_worst(p): + return np.argmin(np.abs(1 - np.abs(p))) + else: + # analog: "worst" is the closest to the imaginary axis + def idx_worst(p): + return np.argmin(np.abs(np.real(p))) + + sos = np.zeros((n_sections, 6)) + + # Construct the system, reversing order so the "worst" are last + for si in range(n_sections-1, -1, -1): + # Select the next "worst" pole + p1_idx = idx_worst(p) + p1 = p[p1_idx] + p = np.delete(p, p1_idx) + + # Pair that pole with a zero + + if np.isreal(p1) and np.isreal(p).sum() == 0: + # Special case (1): last remaining real pole + if pairing != 'minimal': + z1_idx = _nearest_real_complex_idx(z, p1, 'real') + z1 = z[z1_idx] + z = np.delete(z, z1_idx) + sos[si] = _single_zpksos([z1, 0], [p1, 0], 1) + elif len(z) > 0: + z1_idx = _nearest_real_complex_idx(z, p1, 'real') + z1 = z[z1_idx] + z = np.delete(z, z1_idx) + sos[si] = _single_zpksos([z1], [p1], 1) + else: + sos[si] = _single_zpksos([], [p1], 1) + + elif (len(p) + 1 == len(z) + and not np.isreal(p1) + and np.isreal(p).sum() == 1 + and np.isreal(z).sum() == 1): + + # Special case (2): there's one real pole and one real zero + # left, and an equal number of poles and zeros to pair up. + # We *must* pair with a complex zero + + z1_idx = _nearest_real_complex_idx(z, p1, 'complex') + z1 = z[z1_idx] + z = np.delete(z, z1_idx) + sos[si] = _single_zpksos([z1, z1.conj()], [p1, p1.conj()], 1) + + else: + if np.isreal(p1): + prealidx = np.flatnonzero(np.isreal(p)) + p2_idx = prealidx[idx_worst(p[prealidx])] + p2 = p[p2_idx] + p = np.delete(p, p2_idx) + else: + p2 = p1.conj() + + # find closest zero + if len(z) > 0: + z1_idx = _nearest_real_complex_idx(z, p1, 'any') + z1 = z[z1_idx] + z = np.delete(z, z1_idx) + + if not np.isreal(z1): + sos[si] = _single_zpksos([z1, z1.conj()], [p1, p2], 1) + else: + if len(z) > 0: + z2_idx = _nearest_real_complex_idx(z, p1, 'real') + z2 = z[z2_idx] + assert np.isreal(z2) + z = np.delete(z, z2_idx) + sos[si] = _single_zpksos([z1, z2], [p1, p2], 1) + else: + sos[si] = _single_zpksos([z1], [p1, p2], 1) + else: + # no more zeros + sos[si] = _single_zpksos([], [p1, p2], 1) + + assert len(p) == len(z) == 0 # we've consumed all poles and zeros + del p, z + + # put gain in first sos + sos[0][:3] *= k + return sos + + +def _align_nums(nums): + """Aligns the shapes of multiple numerators. + + Given an array of numerator coefficient arrays [[a_1, a_2,..., + a_n],..., [b_1, b_2,..., b_m]], this function pads shorter numerator + arrays with zero's so that all numerators have the same length. Such + alignment is necessary for functions like 'tf2ss', which needs the + alignment when dealing with SIMO transfer functions. + + Parameters + ---------- + nums: array_like + Numerator or list of numerators. Not necessarily with same length. + + Returns + ------- + nums: array + The numerator. If `nums` input was a list of numerators then a 2-D + array with padded zeros for shorter numerators is returned. Otherwise + returns ``np.asarray(nums)``. + """ + try: + # The statement can throw a ValueError if one + # of the numerators is a single digit and another + # is array-like e.g. if nums = [5, [1, 2, 3]] + nums = asarray(nums) + + if not np.issubdtype(nums.dtype, np.number): + raise ValueError("dtype of numerator is non-numeric") + + return nums + + except ValueError: + nums = [np.atleast_1d(num) for num in nums] + max_width = max(num.size for num in nums) + + # pre-allocate + aligned_nums = np.zeros((len(nums), max_width)) + + # Create numerators with padded zeros + for index, num in enumerate(nums): + aligned_nums[index, -num.size:] = num + + return aligned_nums + + +def normalize(b, a): + """Normalize numerator/denominator of a continuous-time transfer function. + + If values of `b` are too close to 0, they are removed. In that case, a + BadCoefficients warning is emitted. + + Parameters + ---------- + b: array_like + Numerator of the transfer function. Can be a 2-D array to normalize + multiple transfer functions. + a: array_like + Denominator of the transfer function. At most 1-D. + + Returns + ------- + num: array + The numerator of the normalized transfer function. At least a 1-D + array. A 2-D array if the input `num` is a 2-D array. + den: 1-D array + The denominator of the normalized transfer function. + + Notes + ----- + Coefficients for both the numerator and denominator should be specified in + descending exponent order (e.g., ``s^2 + 3s + 5`` would be represented as + ``[1, 3, 5]``). + + Examples + -------- + >>> from scipy.signal import normalize + + Normalize the coefficients of the transfer function + ``(3*s^2 - 2*s + 5) / (2*s^2 + 3*s + 1)``: + + >>> b = [3, -2, 5] + >>> a = [2, 3, 1] + >>> normalize(b, a) + (array([ 1.5, -1. , 2.5]), array([1. , 1.5, 0.5])) + + A warning is generated if, for example, the first coefficient of + `b` is 0. In the following example, the result is as expected: + + >>> import warnings + >>> with warnings.catch_warnings(record=True) as w: + ... num, den = normalize([0, 3, 6], [2, -5, 4]) + + >>> num + array([1.5, 3. ]) + >>> den + array([ 1. , -2.5, 2. ]) + + >>> print(w[0].message) + Badly conditioned filter coefficients (numerator): the results may be meaningless + + """ + num, den = b, a + + den = np.atleast_1d(den) + num = np.atleast_2d(_align_nums(num)) + + if den.ndim != 1: + raise ValueError("Denominator polynomial must be rank-1 array.") + if num.ndim > 2: + raise ValueError("Numerator polynomial must be rank-1 or" + " rank-2 array.") + if np.all(den == 0): + raise ValueError("Denominator must have at least on nonzero element.") + + # Trim leading zeros in denominator, leave at least one. + den = np.trim_zeros(den, 'f') + + # Normalize transfer function + num, den = num / den[0], den / den[0] + + # Count numerator columns that are all zero + leading_zeros = 0 + for col in num.T: + if np.allclose(col, 0, atol=1e-14): + leading_zeros += 1 + else: + break + + # Trim leading zeros of numerator + if leading_zeros > 0: + warnings.warn("Badly conditioned filter coefficients (numerator): the " + "results may be meaningless", + BadCoefficients, stacklevel=2) + # Make sure at least one column remains + if leading_zeros == num.shape[1]: + leading_zeros -= 1 + num = num[:, leading_zeros:] + + # Squeeze first dimension if singular + if num.shape[0] == 1: + num = num[0, :] + + return num, den + + +def lp2lp(b, a, wo=1.0): + r""" + Transform a lowpass filter prototype to a different frequency. + + Return an analog low-pass filter with cutoff frequency `wo` + from an analog low-pass filter prototype with unity cutoff frequency, in + transfer function ('ba') representation. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + wo : float + Desired cutoff, as angular frequency (e.g. rad/s). + Defaults to no change. + + Returns + ------- + b : array_like + Numerator polynomial coefficients of the transformed low-pass filter. + a : array_like + Denominator polynomial coefficients of the transformed low-pass filter. + + See Also + -------- + lp2hp, lp2bp, lp2bs, bilinear + lp2lp_zpk + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s}{\omega_0} + + Examples + -------- + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> lp = signal.lti([1.0], [1.0, 1.0]) + >>> lp2 = signal.lti(*signal.lp2lp(lp.num, lp.den, 2)) + >>> w, mag_lp, p_lp = lp.bode() + >>> w, mag_lp2, p_lp2 = lp2.bode(w) + + >>> plt.plot(w, mag_lp, label='Lowpass') + >>> plt.plot(w, mag_lp2, label='Transformed Lowpass') + >>> plt.semilogx() + >>> plt.grid(True) + >>> plt.xlabel('Frequency [rad/s]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.legend() + + """ + a, b = map(atleast_1d, (a, b)) + try: + wo = float(wo) + except TypeError: + wo = float(wo[0]) + d = len(a) + n = len(b) + M = max((d, n)) + pwo = pow(wo, numpy.arange(M - 1, -1, -1)) + start1 = max((n - d, 0)) + start2 = max((d - n, 0)) + b = b * pwo[start1] / pwo[start2:] + a = a * pwo[start1] / pwo[start1:] + return normalize(b, a) + + +def lp2hp(b, a, wo=1.0): + r""" + Transform a lowpass filter prototype to a highpass filter. + + Return an analog high-pass filter with cutoff frequency `wo` + from an analog low-pass filter prototype with unity cutoff frequency, in + transfer function ('ba') representation. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + wo : float + Desired cutoff, as angular frequency (e.g., rad/s). + Defaults to no change. + + Returns + ------- + b : array_like + Numerator polynomial coefficients of the transformed high-pass filter. + a : array_like + Denominator polynomial coefficients of the transformed high-pass filter. + + See Also + -------- + lp2lp, lp2bp, lp2bs, bilinear + lp2hp_zpk + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{\omega_0}{s} + + This maintains symmetry of the lowpass and highpass responses on a + logarithmic scale. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> lp = signal.lti([1.0], [1.0, 1.0]) + >>> hp = signal.lti(*signal.lp2hp(lp.num, lp.den)) + >>> w, mag_lp, p_lp = lp.bode() + >>> w, mag_hp, p_hp = hp.bode(w) + + >>> plt.plot(w, mag_lp, label='Lowpass') + >>> plt.plot(w, mag_hp, label='Highpass') + >>> plt.semilogx() + >>> plt.grid(True) + >>> plt.xlabel('Frequency [rad/s]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.legend() + + """ + a, b = map(atleast_1d, (a, b)) + try: + wo = float(wo) + except TypeError: + wo = float(wo[0]) + d = len(a) + n = len(b) + if wo != 1: + pwo = pow(wo, numpy.arange(max((d, n)))) + else: + pwo = numpy.ones(max((d, n)), b.dtype.char) + if d >= n: + outa = a[::-1] * pwo + outb = resize(b, (d,)) + outb[n:] = 0.0 + outb[:n] = b[::-1] * pwo[:n] + else: + outb = b[::-1] * pwo + outa = resize(a, (n,)) + outa[d:] = 0.0 + outa[:d] = a[::-1] * pwo[:d] + + return normalize(outb, outa) + + +def lp2bp(b, a, wo=1.0, bw=1.0): + r""" + Transform a lowpass filter prototype to a bandpass filter. + + Return an analog band-pass filter with center frequency `wo` and + bandwidth `bw` from an analog low-pass filter prototype with unity + cutoff frequency, in transfer function ('ba') representation. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + wo : float + Desired passband center, as angular frequency (e.g., rad/s). + Defaults to no change. + bw : float + Desired passband width, as angular frequency (e.g., rad/s). + Defaults to 1. + + Returns + ------- + b : array_like + Numerator polynomial coefficients of the transformed band-pass filter. + a : array_like + Denominator polynomial coefficients of the transformed band-pass filter. + + See Also + -------- + lp2lp, lp2hp, lp2bs, bilinear + lp2bp_zpk + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s^2 + {\omega_0}^2}{s \cdot \mathrm{BW}} + + This is the "wideband" transformation, producing a passband with + geometric (log frequency) symmetry about `wo`. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> lp = signal.lti([1.0], [1.0, 1.0]) + >>> bp = signal.lti(*signal.lp2bp(lp.num, lp.den)) + >>> w, mag_lp, p_lp = lp.bode() + >>> w, mag_bp, p_bp = bp.bode(w) + + >>> plt.plot(w, mag_lp, label='Lowpass') + >>> plt.plot(w, mag_bp, label='Bandpass') + >>> plt.semilogx() + >>> plt.grid(True) + >>> plt.xlabel('Frequency [rad/s]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.legend() + """ + + a, b = map(atleast_1d, (a, b)) + D = len(a) - 1 + N = len(b) - 1 + artype = mintypecode((a, b)) + ma = max([N, D]) + Np = N + ma + Dp = D + ma + bprime = numpy.empty(Np + 1, artype) + aprime = numpy.empty(Dp + 1, artype) + wosq = wo * wo + for j in range(Np + 1): + val = 0.0 + for i in range(0, N + 1): + for k in range(0, i + 1): + if ma - i + 2 * k == j: + val += comb(i, k) * b[N - i] * (wosq) ** (i - k) / bw ** i + bprime[Np - j] = val + for j in range(Dp + 1): + val = 0.0 + for i in range(0, D + 1): + for k in range(0, i + 1): + if ma - i + 2 * k == j: + val += comb(i, k) * a[D - i] * (wosq) ** (i - k) / bw ** i + aprime[Dp - j] = val + + return normalize(bprime, aprime) + + +def lp2bs(b, a, wo=1.0, bw=1.0): + r""" + Transform a lowpass filter prototype to a bandstop filter. + + Return an analog band-stop filter with center frequency `wo` and + bandwidth `bw` from an analog low-pass filter prototype with unity + cutoff frequency, in transfer function ('ba') representation. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + wo : float + Desired stopband center, as angular frequency (e.g., rad/s). + Defaults to no change. + bw : float + Desired stopband width, as angular frequency (e.g., rad/s). + Defaults to 1. + + Returns + ------- + b : array_like + Numerator polynomial coefficients of the transformed band-stop filter. + a : array_like + Denominator polynomial coefficients of the transformed band-stop filter. + + See Also + -------- + lp2lp, lp2hp, lp2bp, bilinear + lp2bs_zpk + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s \cdot \mathrm{BW}}{s^2 + {\omega_0}^2} + + This is the "wideband" transformation, producing a stopband with + geometric (log frequency) symmetry about `wo`. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> lp = signal.lti([1.0], [1.0, 1.5]) + >>> bs = signal.lti(*signal.lp2bs(lp.num, lp.den)) + >>> w, mag_lp, p_lp = lp.bode() + >>> w, mag_bs, p_bs = bs.bode(w) + >>> plt.plot(w, mag_lp, label='Lowpass') + >>> plt.plot(w, mag_bs, label='Bandstop') + >>> plt.semilogx() + >>> plt.grid(True) + >>> plt.xlabel('Frequency [rad/s]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.legend() + """ + a, b = map(atleast_1d, (a, b)) + D = len(a) - 1 + N = len(b) - 1 + artype = mintypecode((a, b)) + M = max([N, D]) + Np = M + M + Dp = M + M + bprime = numpy.empty(Np + 1, artype) + aprime = numpy.empty(Dp + 1, artype) + wosq = wo * wo + for j in range(Np + 1): + val = 0.0 + for i in range(0, N + 1): + for k in range(0, M - i + 1): + if i + 2 * k == j: + val += (comb(M - i, k) * b[N - i] * + (wosq) ** (M - i - k) * bw ** i) + bprime[Np - j] = val + for j in range(Dp + 1): + val = 0.0 + for i in range(0, D + 1): + for k in range(0, M - i + 1): + if i + 2 * k == j: + val += (comb(M - i, k) * a[D - i] * + (wosq) ** (M - i - k) * bw ** i) + aprime[Dp - j] = val + + return normalize(bprime, aprime) + + +def bilinear(b, a, fs=1.0): + r""" + Return a digital IIR filter from an analog one using a bilinear transform. + + Transform a set of poles and zeros from the analog s-plane to the digital + z-plane using Tustin's method, which substitutes ``2*fs*(z-1) / (z+1)`` for + ``s``, maintaining the shape of the frequency response. + + Parameters + ---------- + b : array_like + Numerator of the analog filter transfer function. + a : array_like + Denominator of the analog filter transfer function. + fs : float + Sample rate, as ordinary frequency (e.g., hertz). No prewarping is + done in this function. + + Returns + ------- + b : ndarray + Numerator of the transformed digital filter transfer function. + a : ndarray + Denominator of the transformed digital filter transfer function. + + See Also + -------- + lp2lp, lp2hp, lp2bp, lp2bs + bilinear_zpk + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> fs = 100 + >>> bf = 2 * np.pi * np.array([7, 13]) + >>> filts = signal.lti(*signal.butter(4, bf, btype='bandpass', + ... analog=True)) + >>> filtz = signal.lti(*signal.bilinear(filts.num, filts.den, fs)) + >>> wz, hz = signal.freqz(filtz.num, filtz.den) + >>> ws, hs = signal.freqs(filts.num, filts.den, worN=fs*wz) + + >>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hz).clip(1e-15)), + ... label=r'$|H_z(e^{j \omega})|$') + >>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hs).clip(1e-15)), + ... label=r'$|H(j \omega)|$') + >>> plt.legend() + >>> plt.xlabel('Frequency [Hz]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.grid(True) + """ + fs = _validate_fs(fs, allow_none=False) + a, b = map(atleast_1d, (a, b)) + D = len(a) - 1 + N = len(b) - 1 + artype = float + M = max([N, D]) + Np = M + Dp = M + bprime = numpy.empty(Np + 1, artype) + aprime = numpy.empty(Dp + 1, artype) + for j in range(Np + 1): + val = 0.0 + for i in range(N + 1): + for k in range(i + 1): + for l in range(M - i + 1): + if k + l == j: + val += (comb(i, k) * comb(M - i, l) * b[N - i] * + pow(2 * fs, i) * (-1) ** k) + bprime[j] = real(val) + for j in range(Dp + 1): + val = 0.0 + for i in range(D + 1): + for k in range(i + 1): + for l in range(M - i + 1): + if k + l == j: + val += (comb(i, k) * comb(M - i, l) * a[D - i] * + pow(2 * fs, i) * (-1) ** k) + aprime[j] = real(val) + + return normalize(bprime, aprime) + + +def _validate_gpass_gstop(gpass, gstop): + + if gpass <= 0.0: + raise ValueError("gpass should be larger than 0.0") + elif gstop <= 0.0: + raise ValueError("gstop should be larger than 0.0") + elif gpass > gstop: + raise ValueError("gpass should be smaller than gstop") + + +def iirdesign(wp, ws, gpass, gstop, analog=False, ftype='ellip', output='ba', + fs=None): + """Complete IIR digital and analog filter design. + + Given passband and stopband frequencies and gains, construct an analog or + digital IIR filter of minimum order for a given basic type. Return the + output in numerator, denominator ('ba'), pole-zero ('zpk') or second order + sections ('sos') form. + + Parameters + ---------- + wp, ws : float or array like, shape (2,) + Passband and stopband edge frequencies. Possible values are scalars + (for lowpass and highpass filters) or ranges (for bandpass and bandstop + filters). + For digital filters, these are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. For example: + + - Lowpass: wp = 0.2, ws = 0.3 + - Highpass: wp = 0.3, ws = 0.2 + - Bandpass: wp = [0.2, 0.5], ws = [0.1, 0.6] + - Bandstop: wp = [0.1, 0.6], ws = [0.2, 0.5] + + For analog filters, `wp` and `ws` are angular frequencies (e.g., rad/s). + Note, that for bandpass and bandstop filters passband must lie strictly + inside stopband or vice versa. + gpass : float + The maximum loss in the passband (dB). + gstop : float + The minimum attenuation in the stopband (dB). + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + ftype : str, optional + The type of IIR filter to design: + + - Butterworth : 'butter' + - Chebyshev I : 'cheby1' + - Chebyshev II : 'cheby2' + - Cauer/elliptic: 'ellip' + + output : {'ba', 'zpk', 'sos'}, optional + Filter form of the output: + + - second-order sections (recommended): 'sos' + - numerator/denominator (default) : 'ba' + - pole-zero : 'zpk' + + In general the second-order sections ('sos') form is + recommended because inferring the coefficients for the + numerator/denominator form ('ba') suffers from numerical + instabilities. For reasons of backward compatibility the default + form is the numerator/denominator form ('ba'), where the 'b' + and the 'a' in 'ba' refer to the commonly used names of the + coefficients used. + + Note: Using the second-order sections form ('sos') is sometimes + associated with additional computational costs: for + data-intense use cases it is therefore recommended to also + investigate the numerator/denominator form ('ba'). + + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + butter : Filter design using order and critical points + cheby1, cheby2, ellip, bessel + buttord : Find order and critical points from passband and stopband spec + cheb1ord, cheb2ord, ellipord + iirfilter : General filter design using order and critical frequencies + + Notes + ----- + The ``'sos'`` output parameter was added in 0.16.0. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import matplotlib.ticker + + >>> wp = 0.2 + >>> ws = 0.3 + >>> gpass = 1 + >>> gstop = 40 + + >>> system = signal.iirdesign(wp, ws, gpass, gstop) + >>> w, h = signal.freqz(*system) + + >>> fig, ax1 = plt.subplots() + >>> ax1.set_title('Digital filter frequency response') + >>> ax1.plot(w, 20 * np.log10(abs(h)), 'b') + >>> ax1.set_ylabel('Amplitude [dB]', color='b') + >>> ax1.set_xlabel('Frequency [rad/sample]') + >>> ax1.grid(True) + >>> ax1.set_ylim([-120, 20]) + >>> ax2 = ax1.twinx() + >>> angles = np.unwrap(np.angle(h)) + >>> ax2.plot(w, angles, 'g') + >>> ax2.set_ylabel('Angle (radians)', color='g') + >>> ax2.grid(True) + >>> ax2.axis('tight') + >>> ax2.set_ylim([-6, 1]) + >>> nticks = 8 + >>> ax1.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks)) + >>> ax2.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks)) + + """ + try: + ordfunc = filter_dict[ftype][1] + except KeyError as e: + raise ValueError("Invalid IIR filter type: %s" % ftype) from e + except IndexError as e: + raise ValueError(("%s does not have order selection. Use " + "iirfilter function.") % ftype) from e + + _validate_gpass_gstop(gpass, gstop) + + wp = atleast_1d(wp) + ws = atleast_1d(ws) + + fs = _validate_fs(fs, allow_none=True) + + if wp.shape[0] != ws.shape[0] or wp.shape not in [(1,), (2,)]: + raise ValueError("wp and ws must have one or two elements each, and" + f"the same shape, got {wp.shape} and {ws.shape}") + + if any(wp <= 0) or any(ws <= 0): + raise ValueError("Values for wp, ws must be greater than 0") + + if not analog: + if fs is None: + if any(wp >= 1) or any(ws >= 1): + raise ValueError("Values for wp, ws must be less than 1") + elif any(wp >= fs/2) or any(ws >= fs/2): + raise ValueError("Values for wp, ws must be less than fs/2" + f" (fs={fs} -> fs/2={fs/2})") + + if wp.shape[0] == 2: + if not ((ws[0] < wp[0] and wp[1] < ws[1]) or + (wp[0] < ws[0] and ws[1] < wp[1])): + raise ValueError("Passband must lie strictly inside stopband" + " or vice versa") + + band_type = 2 * (len(wp) - 1) + band_type += 1 + if wp[0] >= ws[0]: + band_type += 1 + + btype = {1: 'lowpass', 2: 'highpass', + 3: 'bandstop', 4: 'bandpass'}[band_type] + + N, Wn = ordfunc(wp, ws, gpass, gstop, analog=analog, fs=fs) + return iirfilter(N, Wn, rp=gpass, rs=gstop, analog=analog, btype=btype, + ftype=ftype, output=output, fs=fs) + + +def iirfilter(N, Wn, rp=None, rs=None, btype='band', analog=False, + ftype='butter', output='ba', fs=None): + """ + IIR digital and analog filter design given order and critical points. + + Design an Nth-order digital or analog filter and return the filter + coefficients. + + Parameters + ---------- + N : int + The order of the filter. + Wn : array_like + A scalar or length-2 sequence giving the critical frequencies. + + For digital filters, `Wn` are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`Wn` is thus in + half-cycles / sample.) + + For analog filters, `Wn` is an angular frequency (e.g., rad/s). + + When Wn is a length-2 sequence, ``Wn[0]`` must be less than ``Wn[1]``. + rp : float, optional + For Chebyshev and elliptic filters, provides the maximum ripple + in the passband. (dB) + rs : float, optional + For Chebyshev and elliptic filters, provides the minimum attenuation + in the stop band. (dB) + btype : {'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional + The type of filter. Default is 'bandpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + ftype : str, optional + The type of IIR filter to design: + + - Butterworth : 'butter' + - Chebyshev I : 'cheby1' + - Chebyshev II : 'cheby2' + - Cauer/elliptic: 'ellip' + - Bessel/Thomson: 'bessel' + + output : {'ba', 'zpk', 'sos'}, optional + Filter form of the output: + + - second-order sections (recommended): 'sos' + - numerator/denominator (default) : 'ba' + - pole-zero : 'zpk' + + In general the second-order sections ('sos') form is + recommended because inferring the coefficients for the + numerator/denominator form ('ba') suffers from numerical + instabilities. For reasons of backward compatibility the default + form is the numerator/denominator form ('ba'), where the 'b' + and the 'a' in 'ba' refer to the commonly used names of the + coefficients used. + + Note: Using the second-order sections form ('sos') is sometimes + associated with additional computational costs: for + data-intense use cases it is therefore recommended to also + investigate the numerator/denominator form ('ba'). + + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + butter : Filter design using order and critical points + cheby1, cheby2, ellip, bessel + buttord : Find order and critical points from passband and stopband spec + cheb1ord, cheb2ord, ellipord + iirdesign : General filter design using passband and stopband spec + + Notes + ----- + The ``'sos'`` output parameter was added in 0.16.0. + + Examples + -------- + Generate a 17th-order Chebyshev II analog bandpass filter from 50 Hz to + 200 Hz and plot the frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> b, a = signal.iirfilter(17, [2*np.pi*50, 2*np.pi*200], rs=60, + ... btype='band', analog=True, ftype='cheby2') + >>> w, h = signal.freqs(b, a, 1000) + >>> fig = plt.figure() + >>> ax = fig.add_subplot(1, 1, 1) + >>> ax.semilogx(w / (2*np.pi), 20 * np.log10(np.maximum(abs(h), 1e-5))) + >>> ax.set_title('Chebyshev Type II bandpass frequency response') + >>> ax.set_xlabel('Frequency [Hz]') + >>> ax.set_ylabel('Amplitude [dB]') + >>> ax.axis((10, 1000, -100, 10)) + >>> ax.grid(which='both', axis='both') + >>> plt.show() + + Create a digital filter with the same properties, in a system with + sampling rate of 2000 Hz, and plot the frequency response. (Second-order + sections implementation is required to ensure stability of a filter of + this order): + + >>> sos = signal.iirfilter(17, [50, 200], rs=60, btype='band', + ... analog=False, ftype='cheby2', fs=2000, + ... output='sos') + >>> w, h = signal.sosfreqz(sos, 2000, fs=2000) + >>> fig = plt.figure() + >>> ax = fig.add_subplot(1, 1, 1) + >>> ax.semilogx(w, 20 * np.log10(np.maximum(abs(h), 1e-5))) + >>> ax.set_title('Chebyshev Type II bandpass frequency response') + >>> ax.set_xlabel('Frequency [Hz]') + >>> ax.set_ylabel('Amplitude [dB]') + >>> ax.axis((10, 1000, -100, 10)) + >>> ax.grid(which='both', axis='both') + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + ftype, btype, output = (x.lower() for x in (ftype, btype, output)) + Wn = asarray(Wn) + if fs is not None: + if analog: + raise ValueError("fs cannot be specified for an analog filter") + Wn = 2*Wn/fs + + if numpy.any(Wn <= 0): + raise ValueError("filter critical frequencies must be greater than 0") + + if Wn.size > 1 and not Wn[0] < Wn[1]: + raise ValueError("Wn[0] must be less than Wn[1]") + + try: + btype = band_dict[btype] + except KeyError as e: + raise ValueError("'%s' is an invalid bandtype for filter." % btype) from e + + try: + typefunc = filter_dict[ftype][0] + except KeyError as e: + raise ValueError("'%s' is not a valid basic IIR filter." % ftype) from e + + if output not in ['ba', 'zpk', 'sos']: + raise ValueError("'%s' is not a valid output form." % output) + + if rp is not None and rp < 0: + raise ValueError("passband ripple (rp) must be positive") + + if rs is not None and rs < 0: + raise ValueError("stopband attenuation (rs) must be positive") + + # Get analog lowpass prototype + if typefunc == buttap: + z, p, k = typefunc(N) + elif typefunc == besselap: + z, p, k = typefunc(N, norm=bessel_norms[ftype]) + elif typefunc == cheb1ap: + if rp is None: + raise ValueError("passband ripple (rp) must be provided to " + "design a Chebyshev I filter.") + z, p, k = typefunc(N, rp) + elif typefunc == cheb2ap: + if rs is None: + raise ValueError("stopband attenuation (rs) must be provided to " + "design an Chebyshev II filter.") + z, p, k = typefunc(N, rs) + elif typefunc == ellipap: + if rs is None or rp is None: + raise ValueError("Both rp and rs must be provided to design an " + "elliptic filter.") + z, p, k = typefunc(N, rp, rs) + else: + raise NotImplementedError("'%s' not implemented in iirfilter." % ftype) + + # Pre-warp frequencies for digital filter design + if not analog: + if numpy.any(Wn <= 0) or numpy.any(Wn >= 1): + if fs is not None: + raise ValueError("Digital filter critical frequencies must " + f"be 0 < Wn < fs/2 (fs={fs} -> fs/2={fs/2})") + raise ValueError("Digital filter critical frequencies " + "must be 0 < Wn < 1") + fs = 2.0 + warped = 2 * fs * tan(pi * Wn / fs) + else: + warped = Wn + + # transform to lowpass, bandpass, highpass, or bandstop + if btype in ('lowpass', 'highpass'): + if numpy.size(Wn) != 1: + raise ValueError('Must specify a single critical frequency Wn ' + 'for lowpass or highpass filter') + + if btype == 'lowpass': + z, p, k = lp2lp_zpk(z, p, k, wo=warped) + elif btype == 'highpass': + z, p, k = lp2hp_zpk(z, p, k, wo=warped) + elif btype in ('bandpass', 'bandstop'): + try: + bw = warped[1] - warped[0] + wo = sqrt(warped[0] * warped[1]) + except IndexError as e: + raise ValueError('Wn must specify start and stop frequencies for ' + 'bandpass or bandstop filter') from e + + if btype == 'bandpass': + z, p, k = lp2bp_zpk(z, p, k, wo=wo, bw=bw) + elif btype == 'bandstop': + z, p, k = lp2bs_zpk(z, p, k, wo=wo, bw=bw) + else: + raise NotImplementedError("'%s' not implemented in iirfilter." % btype) + + # Find discrete equivalent if necessary + if not analog: + z, p, k = bilinear_zpk(z, p, k, fs=fs) + + # Transform to proper out type (pole-zero, state-space, numer-denom) + if output == 'zpk': + return z, p, k + elif output == 'ba': + return zpk2tf(z, p, k) + elif output == 'sos': + return zpk2sos(z, p, k, analog=analog) + + +def _relative_degree(z, p): + """ + Return relative degree of transfer function from zeros and poles + """ + degree = len(p) - len(z) + if degree < 0: + raise ValueError("Improper transfer function. " + "Must have at least as many poles as zeros.") + else: + return degree + + +def bilinear_zpk(z, p, k, fs): + r""" + Return a digital IIR filter from an analog one using a bilinear transform. + + Transform a set of poles and zeros from the analog s-plane to the digital + z-plane using Tustin's method, which substitutes ``2*fs*(z-1) / (z+1)`` for + ``s``, maintaining the shape of the frequency response. + + Parameters + ---------- + z : array_like + Zeros of the analog filter transfer function. + p : array_like + Poles of the analog filter transfer function. + k : float + System gain of the analog filter transfer function. + fs : float + Sample rate, as ordinary frequency (e.g., hertz). No prewarping is + done in this function. + + Returns + ------- + z : ndarray + Zeros of the transformed digital filter transfer function. + p : ndarray + Poles of the transformed digital filter transfer function. + k : float + System gain of the transformed digital filter. + + See Also + -------- + lp2lp_zpk, lp2hp_zpk, lp2bp_zpk, lp2bs_zpk + bilinear + + Notes + ----- + .. versionadded:: 1.1.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> fs = 100 + >>> bf = 2 * np.pi * np.array([7, 13]) + >>> filts = signal.lti(*signal.butter(4, bf, btype='bandpass', analog=True, + ... output='zpk')) + >>> filtz = signal.lti(*signal.bilinear_zpk(filts.zeros, filts.poles, + ... filts.gain, fs)) + >>> wz, hz = signal.freqz_zpk(filtz.zeros, filtz.poles, filtz.gain) + >>> ws, hs = signal.freqs_zpk(filts.zeros, filts.poles, filts.gain, + ... worN=fs*wz) + >>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hz).clip(1e-15)), + ... label=r'$|H_z(e^{j \omega})|$') + >>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hs).clip(1e-15)), + ... label=r'$|H(j \omega)|$') + >>> plt.legend() + >>> plt.xlabel('Frequency [Hz]') + >>> plt.ylabel('Magnitude [dB]') + >>> plt.grid(True) + """ + z = atleast_1d(z) + p = atleast_1d(p) + + fs = _validate_fs(fs, allow_none=False) + + degree = _relative_degree(z, p) + + fs2 = 2.0*fs + + # Bilinear transform the poles and zeros + z_z = (fs2 + z) / (fs2 - z) + p_z = (fs2 + p) / (fs2 - p) + + # Any zeros that were at infinity get moved to the Nyquist frequency + z_z = append(z_z, -ones(degree)) + + # Compensate for gain change + k_z = k * real(prod(fs2 - z) / prod(fs2 - p)) + + return z_z, p_z, k_z + + +def lp2lp_zpk(z, p, k, wo=1.0): + r""" + Transform a lowpass filter prototype to a different frequency. + + Return an analog low-pass filter with cutoff frequency `wo` + from an analog low-pass filter prototype with unity cutoff frequency, + using zeros, poles, and gain ('zpk') representation. + + Parameters + ---------- + z : array_like + Zeros of the analog filter transfer function. + p : array_like + Poles of the analog filter transfer function. + k : float + System gain of the analog filter transfer function. + wo : float + Desired cutoff, as angular frequency (e.g., rad/s). + Defaults to no change. + + Returns + ------- + z : ndarray + Zeros of the transformed low-pass filter transfer function. + p : ndarray + Poles of the transformed low-pass filter transfer function. + k : float + System gain of the transformed low-pass filter. + + See Also + -------- + lp2hp_zpk, lp2bp_zpk, lp2bs_zpk, bilinear + lp2lp + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s}{\omega_0} + + .. versionadded:: 1.1.0 + + Examples + -------- + Use the 'zpk' (Zero-Pole-Gain) representation of a lowpass filter to + transform it to a new 'zpk' representation associated with a cutoff frequency wo. + + >>> from scipy.signal import lp2lp_zpk + >>> z = [7, 2] + >>> p = [5, 13] + >>> k = 0.8 + >>> wo = 0.4 + >>> lp2lp_zpk(z, p, k, wo) + ( array([2.8, 0.8]), array([2. , 5.2]), 0.8) + """ + z = atleast_1d(z) + p = atleast_1d(p) + wo = float(wo) # Avoid int wraparound + + degree = _relative_degree(z, p) + + # Scale all points radially from origin to shift cutoff frequency + z_lp = wo * z + p_lp = wo * p + + # Each shifted pole decreases gain by wo, each shifted zero increases it. + # Cancel out the net change to keep overall gain the same + k_lp = k * wo**degree + + return z_lp, p_lp, k_lp + + +def lp2hp_zpk(z, p, k, wo=1.0): + r""" + Transform a lowpass filter prototype to a highpass filter. + + Return an analog high-pass filter with cutoff frequency `wo` + from an analog low-pass filter prototype with unity cutoff frequency, + using zeros, poles, and gain ('zpk') representation. + + Parameters + ---------- + z : array_like + Zeros of the analog filter transfer function. + p : array_like + Poles of the analog filter transfer function. + k : float + System gain of the analog filter transfer function. + wo : float + Desired cutoff, as angular frequency (e.g., rad/s). + Defaults to no change. + + Returns + ------- + z : ndarray + Zeros of the transformed high-pass filter transfer function. + p : ndarray + Poles of the transformed high-pass filter transfer function. + k : float + System gain of the transformed high-pass filter. + + See Also + -------- + lp2lp_zpk, lp2bp_zpk, lp2bs_zpk, bilinear + lp2hp + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{\omega_0}{s} + + This maintains symmetry of the lowpass and highpass responses on a + logarithmic scale. + + .. versionadded:: 1.1.0 + + Examples + -------- + Use the 'zpk' (Zero-Pole-Gain) representation of a lowpass filter to + transform it to a highpass filter with a cutoff frequency wo. + + >>> from scipy.signal import lp2hp_zpk + >>> z = [ -2 + 3j , -0.5 - 0.8j ] + >>> p = [ -1 , -4 ] + >>> k = 10 + >>> wo = 0.6 + >>> lp2hp_zpk(z, p, k, wo) + ( array([-0.09230769-0.13846154j, -0.33707865+0.53932584j]), + array([-0.6 , -0.15]), + 8.5) + """ + z = atleast_1d(z) + p = atleast_1d(p) + wo = float(wo) + + degree = _relative_degree(z, p) + + # Invert positions radially about unit circle to convert LPF to HPF + # Scale all points radially from origin to shift cutoff frequency + z_hp = wo / z + p_hp = wo / p + + # If lowpass had zeros at infinity, inverting moves them to origin. + z_hp = append(z_hp, zeros(degree)) + + # Cancel out gain change caused by inversion + k_hp = k * real(prod(-z) / prod(-p)) + + return z_hp, p_hp, k_hp + + +def lp2bp_zpk(z, p, k, wo=1.0, bw=1.0): + r""" + Transform a lowpass filter prototype to a bandpass filter. + + Return an analog band-pass filter with center frequency `wo` and + bandwidth `bw` from an analog low-pass filter prototype with unity + cutoff frequency, using zeros, poles, and gain ('zpk') representation. + + Parameters + ---------- + z : array_like + Zeros of the analog filter transfer function. + p : array_like + Poles of the analog filter transfer function. + k : float + System gain of the analog filter transfer function. + wo : float + Desired passband center, as angular frequency (e.g., rad/s). + Defaults to no change. + bw : float + Desired passband width, as angular frequency (e.g., rad/s). + Defaults to 1. + + Returns + ------- + z : ndarray + Zeros of the transformed band-pass filter transfer function. + p : ndarray + Poles of the transformed band-pass filter transfer function. + k : float + System gain of the transformed band-pass filter. + + See Also + -------- + lp2lp_zpk, lp2hp_zpk, lp2bs_zpk, bilinear + lp2bp + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s^2 + {\omega_0}^2}{s \cdot \mathrm{BW}} + + This is the "wideband" transformation, producing a passband with + geometric (log frequency) symmetry about `wo`. + + .. versionadded:: 1.1.0 + + Examples + -------- + Use the 'zpk' (Zero-Pole-Gain) representation of a lowpass filter to + transform it to a bandpass filter with a center frequency wo and + bandwidth bw. + + >>> from scipy.signal import lp2bp_zpk + >>> z = [ 5 + 2j , 5 - 2j ] + >>> p = [ 7 , -16 ] + >>> k = 0.8 + >>> wo = 0.62 + >>> bw = 15 + >>> lp2bp_zpk(z, p, k, wo, bw) + ( array([7.49955815e+01+3.00017676e+01j, 7.49955815e+01-3.00017676e+01j, + 4.41850748e-03-1.76761126e-03j, 4.41850748e-03+1.76761126e-03j]), + array([1.04996339e+02+0.j, -1.60167736e-03+0.j, 3.66108003e-03+0.j, + -2.39998398e+02+0.j]), 0.8) + """ + z = atleast_1d(z) + p = atleast_1d(p) + wo = float(wo) + bw = float(bw) + + degree = _relative_degree(z, p) + + # Scale poles and zeros to desired bandwidth + z_lp = z * bw/2 + p_lp = p * bw/2 + + # Square root needs to produce complex result, not NaN + z_lp = z_lp.astype(complex) + p_lp = p_lp.astype(complex) + + # Duplicate poles and zeros and shift from baseband to +wo and -wo + z_bp = concatenate((z_lp + sqrt(z_lp**2 - wo**2), + z_lp - sqrt(z_lp**2 - wo**2))) + p_bp = concatenate((p_lp + sqrt(p_lp**2 - wo**2), + p_lp - sqrt(p_lp**2 - wo**2))) + + # Move degree zeros to origin, leaving degree zeros at infinity for BPF + z_bp = append(z_bp, zeros(degree)) + + # Cancel out gain change from frequency scaling + k_bp = k * bw**degree + + return z_bp, p_bp, k_bp + + +def lp2bs_zpk(z, p, k, wo=1.0, bw=1.0): + r""" + Transform a lowpass filter prototype to a bandstop filter. + + Return an analog band-stop filter with center frequency `wo` and + stopband width `bw` from an analog low-pass filter prototype with unity + cutoff frequency, using zeros, poles, and gain ('zpk') representation. + + Parameters + ---------- + z : array_like + Zeros of the analog filter transfer function. + p : array_like + Poles of the analog filter transfer function. + k : float + System gain of the analog filter transfer function. + wo : float + Desired stopband center, as angular frequency (e.g., rad/s). + Defaults to no change. + bw : float + Desired stopband width, as angular frequency (e.g., rad/s). + Defaults to 1. + + Returns + ------- + z : ndarray + Zeros of the transformed band-stop filter transfer function. + p : ndarray + Poles of the transformed band-stop filter transfer function. + k : float + System gain of the transformed band-stop filter. + + See Also + -------- + lp2lp_zpk, lp2hp_zpk, lp2bp_zpk, bilinear + lp2bs + + Notes + ----- + This is derived from the s-plane substitution + + .. math:: s \rightarrow \frac{s \cdot \mathrm{BW}}{s^2 + {\omega_0}^2} + + This is the "wideband" transformation, producing a stopband with + geometric (log frequency) symmetry about `wo`. + + .. versionadded:: 1.1.0 + + Examples + -------- + Transform a low-pass filter represented in 'zpk' (Zero-Pole-Gain) form + into a bandstop filter represented in 'zpk' form, with a center frequency wo and + bandwidth bw. + + >>> from scipy.signal import lp2bs_zpk + >>> z = [ ] + >>> p = [ 0.7 , -1 ] + >>> k = 9 + >>> wo = 0.5 + >>> bw = 10 + >>> lp2bs_zpk(z, p, k, wo, bw) + ( array([0.+0.5j, 0.+0.5j, 0.-0.5j, 0.-0.5j]), + array([14.2681928 +0.j, -0.02506281+0.j, 0.01752149+0.j, -9.97493719+0.j]), + -12.857142857142858) + """ + z = atleast_1d(z) + p = atleast_1d(p) + wo = float(wo) + bw = float(bw) + + degree = _relative_degree(z, p) + + # Invert to a highpass filter with desired bandwidth + z_hp = (bw/2) / z + p_hp = (bw/2) / p + + # Square root needs to produce complex result, not NaN + z_hp = z_hp.astype(complex) + p_hp = p_hp.astype(complex) + + # Duplicate poles and zeros and shift from baseband to +wo and -wo + z_bs = concatenate((z_hp + sqrt(z_hp**2 - wo**2), + z_hp - sqrt(z_hp**2 - wo**2))) + p_bs = concatenate((p_hp + sqrt(p_hp**2 - wo**2), + p_hp - sqrt(p_hp**2 - wo**2))) + + # Move any zeros that were at infinity to the center of the stopband + z_bs = append(z_bs, full(degree, +1j*wo)) + z_bs = append(z_bs, full(degree, -1j*wo)) + + # Cancel out gain change caused by inversion + k_bs = k * real(prod(-z) / prod(-p)) + + return z_bs, p_bs, k_bs + + +def butter(N, Wn, btype='low', analog=False, output='ba', fs=None): + """ + Butterworth digital and analog filter design. + + Design an Nth-order digital or analog Butterworth filter and return + the filter coefficients. + + Parameters + ---------- + N : int + The order of the filter. For 'bandpass' and 'bandstop' filters, + the resulting order of the final second-order sections ('sos') + matrix is ``2*N``, with `N` the number of biquad sections + of the desired system. + Wn : array_like + The critical frequency or frequencies. For lowpass and highpass + filters, Wn is a scalar; for bandpass and bandstop filters, + Wn is a length-2 sequence. + + For a Butterworth filter, this is the point at which the gain + drops to 1/sqrt(2) that of the passband (the "-3 dB point"). + + For digital filters, if `fs` is not specified, `Wn` units are + normalized from 0 to 1, where 1 is the Nyquist frequency (`Wn` is + thus in half cycles / sample and defined as 2*critical frequencies + / `fs`). If `fs` is specified, `Wn` is in the same units as `fs`. + + For analog filters, `Wn` is an angular frequency (e.g. rad/s). + btype : {'lowpass', 'highpass', 'bandpass', 'bandstop'}, optional + The type of filter. Default is 'lowpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + output : {'ba', 'zpk', 'sos'}, optional + Type of output: numerator/denominator ('ba'), pole-zero ('zpk'), or + second-order sections ('sos'). Default is 'ba' for backwards + compatibility, but 'sos' should be used for general-purpose filtering. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + buttord, buttap + + Notes + ----- + The Butterworth filter has maximally flat frequency response in the + passband. + + The ``'sos'`` output parameter was added in 0.16.0. + + If the transfer function form ``[b, a]`` is requested, numerical + problems can occur since the conversion between roots and + the polynomial coefficients is a numerically sensitive operation, + even for N >= 4. It is recommended to work with the SOS + representation. + + .. warning:: + Designing high-order and narrowband IIR filters in TF form can + result in unstable or incorrect filtering due to floating point + numerical precision issues. Consider inspecting output filter + characteristics `freqz` or designing the filters with second-order + sections via ``output='sos'``. + + Examples + -------- + Design an analog filter and plot its frequency response, showing the + critical points: + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.butter(4, 100, 'low', analog=True) + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Butterworth filter frequency response') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.show() + + Generate a signal made up of 10 Hz and 20 Hz, sampled at 1 kHz + + >>> t = np.linspace(0, 1, 1000, False) # 1 second + >>> sig = np.sin(2*np.pi*10*t) + np.sin(2*np.pi*20*t) + >>> fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + >>> ax1.plot(t, sig) + >>> ax1.set_title('10 Hz and 20 Hz sinusoids') + >>> ax1.axis([0, 1, -2, 2]) + + Design a digital high-pass filter at 15 Hz to remove the 10 Hz tone, and + apply it to the signal. (It's recommended to use second-order sections + format when filtering, to avoid numerical error with transfer function + (``ba``) format): + + >>> sos = signal.butter(10, 15, 'hp', fs=1000, output='sos') + >>> filtered = signal.sosfilt(sos, sig) + >>> ax2.plot(t, filtered) + >>> ax2.set_title('After 15 Hz high-pass filter') + >>> ax2.axis([0, 1, -2, 2]) + >>> ax2.set_xlabel('Time [seconds]') + >>> plt.tight_layout() + >>> plt.show() + """ + return iirfilter(N, Wn, btype=btype, analog=analog, + output=output, ftype='butter', fs=fs) + + +def cheby1(N, rp, Wn, btype='low', analog=False, output='ba', fs=None): + """ + Chebyshev type I digital and analog filter design. + + Design an Nth-order digital or analog Chebyshev type I filter and + return the filter coefficients. + + Parameters + ---------- + N : int + The order of the filter. + rp : float + The maximum ripple allowed below unity gain in the passband. + Specified in decibels, as a positive number. + Wn : array_like + A scalar or length-2 sequence giving the critical frequencies. + For Type I filters, this is the point in the transition band at which + the gain first drops below -`rp`. + + For digital filters, `Wn` are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`Wn` is thus in + half-cycles / sample.) + + For analog filters, `Wn` is an angular frequency (e.g., rad/s). + btype : {'lowpass', 'highpass', 'bandpass', 'bandstop'}, optional + The type of filter. Default is 'lowpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + output : {'ba', 'zpk', 'sos'}, optional + Type of output: numerator/denominator ('ba'), pole-zero ('zpk'), or + second-order sections ('sos'). Default is 'ba' for backwards + compatibility, but 'sos' should be used for general-purpose filtering. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + cheb1ord, cheb1ap + + Notes + ----- + The Chebyshev type I filter maximizes the rate of cutoff between the + frequency response's passband and stopband, at the expense of ripple in + the passband and increased ringing in the step response. + + Type I filters roll off faster than Type II (`cheby2`), but Type II + filters do not have any ripple in the passband. + + The equiripple passband has N maxima or minima (for example, a + 5th-order filter has 3 maxima and 2 minima). Consequently, the DC gain is + unity for odd-order filters, or -rp dB for even-order filters. + + The ``'sos'`` output parameter was added in 0.16.0. + + Examples + -------- + Design an analog filter and plot its frequency response, showing the + critical points: + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.cheby1(4, 5, 100, 'low', analog=True) + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Chebyshev Type I frequency response (rp=5)') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.axhline(-5, color='green') # rp + >>> plt.show() + + Generate a signal made up of 10 Hz and 20 Hz, sampled at 1 kHz + + >>> t = np.linspace(0, 1, 1000, False) # 1 second + >>> sig = np.sin(2*np.pi*10*t) + np.sin(2*np.pi*20*t) + >>> fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + >>> ax1.plot(t, sig) + >>> ax1.set_title('10 Hz and 20 Hz sinusoids') + >>> ax1.axis([0, 1, -2, 2]) + + Design a digital high-pass filter at 15 Hz to remove the 10 Hz tone, and + apply it to the signal. (It's recommended to use second-order sections + format when filtering, to avoid numerical error with transfer function + (``ba``) format): + + >>> sos = signal.cheby1(10, 1, 15, 'hp', fs=1000, output='sos') + >>> filtered = signal.sosfilt(sos, sig) + >>> ax2.plot(t, filtered) + >>> ax2.set_title('After 15 Hz high-pass filter') + >>> ax2.axis([0, 1, -2, 2]) + >>> ax2.set_xlabel('Time [seconds]') + >>> plt.tight_layout() + >>> plt.show() + """ + return iirfilter(N, Wn, rp=rp, btype=btype, analog=analog, + output=output, ftype='cheby1', fs=fs) + + +def cheby2(N, rs, Wn, btype='low', analog=False, output='ba', fs=None): + """ + Chebyshev type II digital and analog filter design. + + Design an Nth-order digital or analog Chebyshev type II filter and + return the filter coefficients. + + Parameters + ---------- + N : int + The order of the filter. + rs : float + The minimum attenuation required in the stop band. + Specified in decibels, as a positive number. + Wn : array_like + A scalar or length-2 sequence giving the critical frequencies. + For Type II filters, this is the point in the transition band at which + the gain first reaches -`rs`. + + For digital filters, `Wn` are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`Wn` is thus in + half-cycles / sample.) + + For analog filters, `Wn` is an angular frequency (e.g., rad/s). + btype : {'lowpass', 'highpass', 'bandpass', 'bandstop'}, optional + The type of filter. Default is 'lowpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + output : {'ba', 'zpk', 'sos'}, optional + Type of output: numerator/denominator ('ba'), pole-zero ('zpk'), or + second-order sections ('sos'). Default is 'ba' for backwards + compatibility, but 'sos' should be used for general-purpose filtering. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + cheb2ord, cheb2ap + + Notes + ----- + The Chebyshev type II filter maximizes the rate of cutoff between the + frequency response's passband and stopband, at the expense of ripple in + the stopband and increased ringing in the step response. + + Type II filters do not roll off as fast as Type I (`cheby1`). + + The ``'sos'`` output parameter was added in 0.16.0. + + Examples + -------- + Design an analog filter and plot its frequency response, showing the + critical points: + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.cheby2(4, 40, 100, 'low', analog=True) + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Chebyshev Type II frequency response (rs=40)') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.axhline(-40, color='green') # rs + >>> plt.show() + + Generate a signal made up of 10 Hz and 20 Hz, sampled at 1 kHz + + >>> t = np.linspace(0, 1, 1000, False) # 1 second + >>> sig = np.sin(2*np.pi*10*t) + np.sin(2*np.pi*20*t) + >>> fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + >>> ax1.plot(t, sig) + >>> ax1.set_title('10 Hz and 20 Hz sinusoids') + >>> ax1.axis([0, 1, -2, 2]) + + Design a digital high-pass filter at 17 Hz to remove the 10 Hz tone, and + apply it to the signal. (It's recommended to use second-order sections + format when filtering, to avoid numerical error with transfer function + (``ba``) format): + + >>> sos = signal.cheby2(12, 20, 17, 'hp', fs=1000, output='sos') + >>> filtered = signal.sosfilt(sos, sig) + >>> ax2.plot(t, filtered) + >>> ax2.set_title('After 17 Hz high-pass filter') + >>> ax2.axis([0, 1, -2, 2]) + >>> ax2.set_xlabel('Time [seconds]') + >>> plt.show() + """ + return iirfilter(N, Wn, rs=rs, btype=btype, analog=analog, + output=output, ftype='cheby2', fs=fs) + + +def ellip(N, rp, rs, Wn, btype='low', analog=False, output='ba', fs=None): + """ + Elliptic (Cauer) digital and analog filter design. + + Design an Nth-order digital or analog elliptic filter and return + the filter coefficients. + + Parameters + ---------- + N : int + The order of the filter. + rp : float + The maximum ripple allowed below unity gain in the passband. + Specified in decibels, as a positive number. + rs : float + The minimum attenuation required in the stop band. + Specified in decibels, as a positive number. + Wn : array_like + A scalar or length-2 sequence giving the critical frequencies. + For elliptic filters, this is the point in the transition band at + which the gain first drops below -`rp`. + + For digital filters, `Wn` are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`Wn` is thus in + half-cycles / sample.) + + For analog filters, `Wn` is an angular frequency (e.g., rad/s). + btype : {'lowpass', 'highpass', 'bandpass', 'bandstop'}, optional + The type of filter. Default is 'lowpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + output : {'ba', 'zpk', 'sos'}, optional + Type of output: numerator/denominator ('ba'), pole-zero ('zpk'), or + second-order sections ('sos'). Default is 'ba' for backwards + compatibility, but 'sos' should be used for general-purpose filtering. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + See Also + -------- + ellipord, ellipap + + Notes + ----- + Also known as Cauer or Zolotarev filters, the elliptical filter maximizes + the rate of transition between the frequency response's passband and + stopband, at the expense of ripple in both, and increased ringing in the + step response. + + As `rp` approaches 0, the elliptical filter becomes a Chebyshev + type II filter (`cheby2`). As `rs` approaches 0, it becomes a Chebyshev + type I filter (`cheby1`). As both approach 0, it becomes a Butterworth + filter (`butter`). + + The equiripple passband has N maxima or minima (for example, a + 5th-order filter has 3 maxima and 2 minima). Consequently, the DC gain is + unity for odd-order filters, or -rp dB for even-order filters. + + The ``'sos'`` output parameter was added in 0.16.0. + + Examples + -------- + Design an analog filter and plot its frequency response, showing the + critical points: + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.ellip(4, 5, 40, 100, 'low', analog=True) + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Elliptic filter frequency response (rp=5, rs=40)') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.axhline(-40, color='green') # rs + >>> plt.axhline(-5, color='green') # rp + >>> plt.show() + + Generate a signal made up of 10 Hz and 20 Hz, sampled at 1 kHz + + >>> t = np.linspace(0, 1, 1000, False) # 1 second + >>> sig = np.sin(2*np.pi*10*t) + np.sin(2*np.pi*20*t) + >>> fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) + >>> ax1.plot(t, sig) + >>> ax1.set_title('10 Hz and 20 Hz sinusoids') + >>> ax1.axis([0, 1, -2, 2]) + + Design a digital high-pass filter at 17 Hz to remove the 10 Hz tone, and + apply it to the signal. (It's recommended to use second-order sections + format when filtering, to avoid numerical error with transfer function + (``ba``) format): + + >>> sos = signal.ellip(8, 1, 100, 17, 'hp', fs=1000, output='sos') + >>> filtered = signal.sosfilt(sos, sig) + >>> ax2.plot(t, filtered) + >>> ax2.set_title('After 17 Hz high-pass filter') + >>> ax2.axis([0, 1, -2, 2]) + >>> ax2.set_xlabel('Time [seconds]') + >>> plt.tight_layout() + >>> plt.show() + """ + return iirfilter(N, Wn, rs=rs, rp=rp, btype=btype, analog=analog, + output=output, ftype='elliptic', fs=fs) + + +def bessel(N, Wn, btype='low', analog=False, output='ba', norm='phase', + fs=None): + """ + Bessel/Thomson digital and analog filter design. + + Design an Nth-order digital or analog Bessel filter and return the + filter coefficients. + + Parameters + ---------- + N : int + The order of the filter. + Wn : array_like + A scalar or length-2 sequence giving the critical frequencies (defined + by the `norm` parameter). + For analog filters, `Wn` is an angular frequency (e.g., rad/s). + + For digital filters, `Wn` are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`Wn` is thus in + half-cycles / sample.) + btype : {'lowpass', 'highpass', 'bandpass', 'bandstop'}, optional + The type of filter. Default is 'lowpass'. + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. (See Notes.) + output : {'ba', 'zpk', 'sos'}, optional + Type of output: numerator/denominator ('ba'), pole-zero ('zpk'), or + second-order sections ('sos'). Default is 'ba'. + norm : {'phase', 'delay', 'mag'}, optional + Critical frequency normalization: + + ``phase`` + The filter is normalized such that the phase response reaches its + midpoint at angular (e.g. rad/s) frequency `Wn`. This happens for + both low-pass and high-pass filters, so this is the + "phase-matched" case. + + The magnitude response asymptotes are the same as a Butterworth + filter of the same order with a cutoff of `Wn`. + + This is the default, and matches MATLAB's implementation. + + ``delay`` + The filter is normalized such that the group delay in the passband + is 1/`Wn` (e.g., seconds). This is the "natural" type obtained by + solving Bessel polynomials. + + ``mag`` + The filter is normalized such that the gain magnitude is -3 dB at + angular frequency `Wn`. + + .. versionadded:: 0.18.0 + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (`b`) and denominator (`a`) polynomials of the IIR filter. + Only returned if ``output='ba'``. + z, p, k : ndarray, ndarray, float + Zeros, poles, and system gain of the IIR filter transfer + function. Only returned if ``output='zpk'``. + sos : ndarray + Second-order sections representation of the IIR filter. + Only returned if ``output='sos'``. + + Notes + ----- + Also known as a Thomson filter, the analog Bessel filter has maximally + flat group delay and maximally linear phase response, with very little + ringing in the step response. [1]_ + + The Bessel is inherently an analog filter. This function generates digital + Bessel filters using the bilinear transform, which does not preserve the + phase response of the analog filter. As such, it is only approximately + correct at frequencies below about fs/4. To get maximally-flat group + delay at higher frequencies, the analog Bessel filter must be transformed + using phase-preserving techniques. + + See `besselap` for implementation details and references. + + The ``'sos'`` output parameter was added in 0.16.0. + + References + ---------- + .. [1] Thomson, W.E., "Delay Networks having Maximally Flat Frequency + Characteristics", Proceedings of the Institution of Electrical + Engineers, Part III, November 1949, Vol. 96, No. 44, pp. 487-490. + + Examples + -------- + Plot the phase-normalized frequency response, showing the relationship + to the Butterworth's cutoff frequency (green): + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.butter(4, 100, 'low', analog=True) + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(np.abs(h)), color='silver', ls='dashed') + >>> b, a = signal.bessel(4, 100, 'low', analog=True, norm='phase') + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(np.abs(h))) + >>> plt.title('Bessel filter magnitude response (with Butterworth)') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.show() + + and the phase midpoint: + + >>> plt.figure() + >>> plt.semilogx(w, np.unwrap(np.angle(h))) + >>> plt.axvline(100, color='green') # cutoff frequency + >>> plt.axhline(-np.pi, color='red') # phase midpoint + >>> plt.title('Bessel filter phase response') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Phase [radians]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.show() + + Plot the magnitude-normalized frequency response, showing the -3 dB cutoff: + + >>> b, a = signal.bessel(3, 10, 'low', analog=True, norm='mag') + >>> w, h = signal.freqs(b, a) + >>> plt.semilogx(w, 20 * np.log10(np.abs(h))) + >>> plt.axhline(-3, color='red') # -3 dB magnitude + >>> plt.axvline(10, color='green') # cutoff frequency + >>> plt.title('Magnitude-normalized Bessel filter frequency response') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.show() + + Plot the delay-normalized filter, showing the maximally-flat group delay + at 0.1 seconds: + + >>> b, a = signal.bessel(5, 1/0.1, 'low', analog=True, norm='delay') + >>> w, h = signal.freqs(b, a) + >>> plt.figure() + >>> plt.semilogx(w[1:], -np.diff(np.unwrap(np.angle(h)))/np.diff(w)) + >>> plt.axhline(0.1, color='red') # 0.1 seconds group delay + >>> plt.title('Bessel filter group delay') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Group delay [seconds]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.show() + + """ + return iirfilter(N, Wn, btype=btype, analog=analog, + output=output, ftype='bessel_'+norm, fs=fs) + + +def maxflat(): + pass + + +def yulewalk(): + pass + + +def band_stop_obj(wp, ind, passb, stopb, gpass, gstop, type): + """ + Band Stop Objective Function for order minimization. + + Returns the non-integer order for an analog band stop filter. + + Parameters + ---------- + wp : scalar + Edge of passband `passb`. + ind : int, {0, 1} + Index specifying which `passb` edge to vary (0 or 1). + passb : ndarray + Two element sequence of fixed passband edges. + stopb : ndarray + Two element sequence of fixed stopband edges. + gstop : float + Amount of attenuation in stopband in dB. + gpass : float + Amount of ripple in the passband in dB. + type : {'butter', 'cheby', 'ellip'} + Type of filter. + + Returns + ------- + n : scalar + Filter order (possibly non-integer). + + """ + + _validate_gpass_gstop(gpass, gstop) + + passbC = passb.copy() + passbC[ind] = wp + nat = (stopb * (passbC[0] - passbC[1]) / + (stopb ** 2 - passbC[0] * passbC[1])) + nat = min(abs(nat)) + + if type == 'butter': + GSTOP = 10 ** (0.1 * abs(gstop)) + GPASS = 10 ** (0.1 * abs(gpass)) + n = (log10((GSTOP - 1.0) / (GPASS - 1.0)) / (2 * log10(nat))) + elif type == 'cheby': + GSTOP = 10 ** (0.1 * abs(gstop)) + GPASS = 10 ** (0.1 * abs(gpass)) + n = arccosh(sqrt((GSTOP - 1.0) / (GPASS - 1.0))) / arccosh(nat) + elif type == 'ellip': + GSTOP = 10 ** (0.1 * gstop) + GPASS = 10 ** (0.1 * gpass) + arg1 = sqrt((GPASS - 1.0) / (GSTOP - 1.0)) + arg0 = 1.0 / nat + d0 = special.ellipk([arg0 ** 2, 1 - arg0 ** 2]) + d1 = special.ellipk([arg1 ** 2, 1 - arg1 ** 2]) + n = (d0[0] * d1[1] / (d0[1] * d1[0])) + else: + raise ValueError("Incorrect type: %s" % type) + return n + + +def _pre_warp(wp, ws, analog): + # Pre-warp frequencies for digital filter design + if not analog: + passb = np.tan(pi * wp / 2.0) + stopb = np.tan(pi * ws / 2.0) + else: + passb = wp * 1.0 + stopb = ws * 1.0 + return passb, stopb + + +def _validate_wp_ws(wp, ws, fs, analog): + wp = atleast_1d(wp) + ws = atleast_1d(ws) + if fs is not None: + if analog: + raise ValueError("fs cannot be specified for an analog filter") + wp = 2 * wp / fs + ws = 2 * ws / fs + + filter_type = 2 * (len(wp) - 1) + 1 + if wp[0] >= ws[0]: + filter_type += 1 + + return wp, ws, filter_type + + +def _find_nat_freq(stopb, passb, gpass, gstop, filter_type, filter_kind): + if filter_type == 1: # low + nat = stopb / passb + elif filter_type == 2: # high + nat = passb / stopb + elif filter_type == 3: # stop + + ### breakpoint() + + wp0 = optimize.fminbound(band_stop_obj, passb[0], stopb[0] - 1e-12, + args=(0, passb, stopb, gpass, gstop, + filter_kind), + disp=0) + passb[0] = wp0 + wp1 = optimize.fminbound(band_stop_obj, stopb[1] + 1e-12, passb[1], + args=(1, passb, stopb, gpass, gstop, + filter_kind), + disp=0) + passb[1] = wp1 + nat = ((stopb * (passb[0] - passb[1])) / + (stopb ** 2 - passb[0] * passb[1])) + elif filter_type == 4: # pass + nat = ((stopb ** 2 - passb[0] * passb[1]) / + (stopb * (passb[0] - passb[1]))) + else: + raise ValueError(f"should not happen: {filter_type =}.") + + nat = min(abs(nat)) + return nat, passb + + +def _postprocess_wn(WN, analog, fs): + wn = WN if analog else np.arctan(WN) * 2.0 / pi + if len(wn) == 1: + wn = wn[0] + if fs is not None: + wn = wn * fs / 2 + return wn + + +def buttord(wp, ws, gpass, gstop, analog=False, fs=None): + """Butterworth filter order selection. + + Return the order of the lowest order digital or analog Butterworth filter + that loses no more than `gpass` dB in the passband and has at least + `gstop` dB attenuation in the stopband. + + Parameters + ---------- + wp, ws : float + Passband and stopband edge frequencies. + + For digital filters, these are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`wp` and `ws` are thus in + half-cycles / sample.) For example: + + - Lowpass: wp = 0.2, ws = 0.3 + - Highpass: wp = 0.3, ws = 0.2 + - Bandpass: wp = [0.2, 0.5], ws = [0.1, 0.6] + - Bandstop: wp = [0.1, 0.6], ws = [0.2, 0.5] + + For analog filters, `wp` and `ws` are angular frequencies (e.g., rad/s). + gpass : float + The maximum loss in the passband (dB). + gstop : float + The minimum attenuation in the stopband (dB). + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + ord : int + The lowest order for a Butterworth filter which meets specs. + wn : ndarray or float + The Butterworth natural frequency (i.e. the "3dB frequency"). Should + be used with `butter` to give filter results. If `fs` is specified, + this is in the same units, and `fs` must also be passed to `butter`. + + See Also + -------- + butter : Filter design using order and critical points + cheb1ord : Find order and critical points from passband and stopband spec + cheb2ord, ellipord + iirfilter : General filter design using order and critical frequencies + iirdesign : General filter design using passband and stopband spec + + Examples + -------- + Design an analog bandpass filter with passband within 3 dB from 20 to + 50 rad/s, while rejecting at least -40 dB below 14 and above 60 rad/s. + Plot its frequency response, showing the passband and stopband + constraints in gray. + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> N, Wn = signal.buttord([20, 50], [14, 60], 3, 40, True) + >>> b, a = signal.butter(N, Wn, 'band', True) + >>> w, h = signal.freqs(b, a, np.logspace(1, 2, 500)) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Butterworth bandpass filter fit to constraints') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.grid(which='both', axis='both') + >>> plt.fill([1, 14, 14, 1], [-40, -40, 99, 99], '0.9', lw=0) # stop + >>> plt.fill([20, 20, 50, 50], [-99, -3, -3, -99], '0.9', lw=0) # pass + >>> plt.fill([60, 60, 1e9, 1e9], [99, -40, -40, 99], '0.9', lw=0) # stop + >>> plt.axis([10, 100, -60, 3]) + >>> plt.show() + + """ + _validate_gpass_gstop(gpass, gstop) + fs = _validate_fs(fs, allow_none=True) + wp, ws, filter_type = _validate_wp_ws(wp, ws, fs, analog) + passb, stopb = _pre_warp(wp, ws, analog) + nat, passb = _find_nat_freq(stopb, passb, gpass, gstop, filter_type, 'butter') + + GSTOP = 10 ** (0.1 * abs(gstop)) + GPASS = 10 ** (0.1 * abs(gpass)) + ord = int(ceil(log10((GSTOP - 1.0) / (GPASS - 1.0)) / (2 * log10(nat)))) + + # Find the Butterworth natural frequency WN (or the "3dB" frequency") + # to give exactly gpass at passb. + try: + W0 = (GPASS - 1.0) ** (-1.0 / (2.0 * ord)) + except ZeroDivisionError: + W0 = 1.0 + warnings.warn("Order is zero...check input parameters.", + RuntimeWarning, stacklevel=2) + + # now convert this frequency back from lowpass prototype + # to the original analog filter + + if filter_type == 1: # low + WN = W0 * passb + elif filter_type == 2: # high + WN = passb / W0 + elif filter_type == 3: # stop + WN = numpy.empty(2, float) + discr = sqrt((passb[1] - passb[0]) ** 2 + + 4 * W0 ** 2 * passb[0] * passb[1]) + WN[0] = ((passb[1] - passb[0]) + discr) / (2 * W0) + WN[1] = ((passb[1] - passb[0]) - discr) / (2 * W0) + WN = numpy.sort(abs(WN)) + elif filter_type == 4: # pass + W0 = numpy.array([-W0, W0], float) + WN = (-W0 * (passb[1] - passb[0]) / 2.0 + + sqrt(W0 ** 2 / 4.0 * (passb[1] - passb[0]) ** 2 + + passb[0] * passb[1])) + WN = numpy.sort(abs(WN)) + else: + raise ValueError("Bad type: %s" % filter_type) + + wn = _postprocess_wn(WN, analog, fs) + + return ord, wn + + +def cheb1ord(wp, ws, gpass, gstop, analog=False, fs=None): + """Chebyshev type I filter order selection. + + Return the order of the lowest order digital or analog Chebyshev Type I + filter that loses no more than `gpass` dB in the passband and has at + least `gstop` dB attenuation in the stopband. + + Parameters + ---------- + wp, ws : float + Passband and stopband edge frequencies. + + For digital filters, these are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`wp` and `ws` are thus in + half-cycles / sample.) For example: + + - Lowpass: wp = 0.2, ws = 0.3 + - Highpass: wp = 0.3, ws = 0.2 + - Bandpass: wp = [0.2, 0.5], ws = [0.1, 0.6] + - Bandstop: wp = [0.1, 0.6], ws = [0.2, 0.5] + + For analog filters, `wp` and `ws` are angular frequencies (e.g., rad/s). + gpass : float + The maximum loss in the passband (dB). + gstop : float + The minimum attenuation in the stopband (dB). + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + ord : int + The lowest order for a Chebyshev type I filter that meets specs. + wn : ndarray or float + The Chebyshev natural frequency (the "3dB frequency") for use with + `cheby1` to give filter results. If `fs` is specified, + this is in the same units, and `fs` must also be passed to `cheby1`. + + See Also + -------- + cheby1 : Filter design using order and critical points + buttord : Find order and critical points from passband and stopband spec + cheb2ord, ellipord + iirfilter : General filter design using order and critical frequencies + iirdesign : General filter design using passband and stopband spec + + Examples + -------- + Design a digital lowpass filter such that the passband is within 3 dB up + to 0.2*(fs/2), while rejecting at least -40 dB above 0.3*(fs/2). Plot its + frequency response, showing the passband and stopband constraints in gray. + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> N, Wn = signal.cheb1ord(0.2, 0.3, 3, 40) + >>> b, a = signal.cheby1(N, 3, Wn, 'low') + >>> w, h = signal.freqz(b, a) + >>> plt.semilogx(w / np.pi, 20 * np.log10(abs(h))) + >>> plt.title('Chebyshev I lowpass filter fit to constraints') + >>> plt.xlabel('Normalized frequency') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.grid(which='both', axis='both') + >>> plt.fill([.01, 0.2, 0.2, .01], [-3, -3, -99, -99], '0.9', lw=0) # stop + >>> plt.fill([0.3, 0.3, 2, 2], [ 9, -40, -40, 9], '0.9', lw=0) # pass + >>> plt.axis([0.08, 1, -60, 3]) + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + _validate_gpass_gstop(gpass, gstop) + wp, ws, filter_type = _validate_wp_ws(wp, ws, fs, analog) + passb, stopb = _pre_warp(wp, ws, analog) + nat, passb = _find_nat_freq(stopb, passb, gpass, gstop, filter_type, 'cheby') + + GSTOP = 10 ** (0.1 * abs(gstop)) + GPASS = 10 ** (0.1 * abs(gpass)) + v_pass_stop = np.arccosh(np.sqrt((GSTOP - 1.0) / (GPASS - 1.0))) + ord = int(ceil(v_pass_stop / np.arccosh(nat))) + + # Natural frequencies are just the passband edges + wn = _postprocess_wn(passb, analog, fs) + + return ord, wn + + +def cheb2ord(wp, ws, gpass, gstop, analog=False, fs=None): + """Chebyshev type II filter order selection. + + Return the order of the lowest order digital or analog Chebyshev Type II + filter that loses no more than `gpass` dB in the passband and has at least + `gstop` dB attenuation in the stopband. + + Parameters + ---------- + wp, ws : float + Passband and stopband edge frequencies. + + For digital filters, these are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`wp` and `ws` are thus in + half-cycles / sample.) For example: + + - Lowpass: wp = 0.2, ws = 0.3 + - Highpass: wp = 0.3, ws = 0.2 + - Bandpass: wp = [0.2, 0.5], ws = [0.1, 0.6] + - Bandstop: wp = [0.1, 0.6], ws = [0.2, 0.5] + + For analog filters, `wp` and `ws` are angular frequencies (e.g., rad/s). + gpass : float + The maximum loss in the passband (dB). + gstop : float + The minimum attenuation in the stopband (dB). + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + ord : int + The lowest order for a Chebyshev type II filter that meets specs. + wn : ndarray or float + The Chebyshev natural frequency (the "3dB frequency") for use with + `cheby2` to give filter results. If `fs` is specified, + this is in the same units, and `fs` must also be passed to `cheby2`. + + See Also + -------- + cheby2 : Filter design using order and critical points + buttord : Find order and critical points from passband and stopband spec + cheb1ord, ellipord + iirfilter : General filter design using order and critical frequencies + iirdesign : General filter design using passband and stopband spec + + Examples + -------- + Design a digital bandstop filter which rejects -60 dB from 0.2*(fs/2) to + 0.5*(fs/2), while staying within 3 dB below 0.1*(fs/2) or above + 0.6*(fs/2). Plot its frequency response, showing the passband and + stopband constraints in gray. + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> N, Wn = signal.cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60) + >>> b, a = signal.cheby2(N, 60, Wn, 'stop') + >>> w, h = signal.freqz(b, a) + >>> plt.semilogx(w / np.pi, 20 * np.log10(abs(h))) + >>> plt.title('Chebyshev II bandstop filter fit to constraints') + >>> plt.xlabel('Normalized frequency') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.grid(which='both', axis='both') + >>> plt.fill([.01, .1, .1, .01], [-3, -3, -99, -99], '0.9', lw=0) # stop + >>> plt.fill([.2, .2, .5, .5], [ 9, -60, -60, 9], '0.9', lw=0) # pass + >>> plt.fill([.6, .6, 2, 2], [-99, -3, -3, -99], '0.9', lw=0) # stop + >>> plt.axis([0.06, 1, -80, 3]) + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + _validate_gpass_gstop(gpass, gstop) + wp, ws, filter_type = _validate_wp_ws(wp, ws, fs, analog) + passb, stopb = _pre_warp(wp, ws, analog) + nat, passb = _find_nat_freq(stopb, passb, gpass, gstop, filter_type, 'cheby') + + GSTOP = 10 ** (0.1 * abs(gstop)) + GPASS = 10 ** (0.1 * abs(gpass)) + v_pass_stop = np.arccosh(np.sqrt((GSTOP - 1.0) / (GPASS - 1.0))) + ord = int(ceil(v_pass_stop / arccosh(nat))) + + # Find frequency where analog response is -gpass dB. + # Then convert back from low-pass prototype to the original filter. + + new_freq = cosh(1.0 / ord * v_pass_stop) + new_freq = 1.0 / new_freq + + if filter_type == 1: + nat = passb / new_freq + elif filter_type == 2: + nat = passb * new_freq + elif filter_type == 3: + nat = numpy.empty(2, float) + nat[0] = (new_freq / 2.0 * (passb[0] - passb[1]) + + sqrt(new_freq ** 2 * (passb[1] - passb[0]) ** 2 / 4.0 + + passb[1] * passb[0])) + nat[1] = passb[1] * passb[0] / nat[0] + elif filter_type == 4: + nat = numpy.empty(2, float) + nat[0] = (1.0 / (2.0 * new_freq) * (passb[0] - passb[1]) + + sqrt((passb[1] - passb[0]) ** 2 / (4.0 * new_freq ** 2) + + passb[1] * passb[0])) + nat[1] = passb[0] * passb[1] / nat[0] + + wn = _postprocess_wn(nat, analog, fs) + + return ord, wn + + +_POW10_LOG10 = np.log(10) + + +def _pow10m1(x): + """10 ** x - 1 for x near 0""" + return np.expm1(_POW10_LOG10 * x) + + +def ellipord(wp, ws, gpass, gstop, analog=False, fs=None): + """Elliptic (Cauer) filter order selection. + + Return the order of the lowest order digital or analog elliptic filter + that loses no more than `gpass` dB in the passband and has at least + `gstop` dB attenuation in the stopband. + + Parameters + ---------- + wp, ws : float + Passband and stopband edge frequencies. + + For digital filters, these are in the same units as `fs`. By default, + `fs` is 2 half-cycles/sample, so these are normalized from 0 to 1, + where 1 is the Nyquist frequency. (`wp` and `ws` are thus in + half-cycles / sample.) For example: + + - Lowpass: wp = 0.2, ws = 0.3 + - Highpass: wp = 0.3, ws = 0.2 + - Bandpass: wp = [0.2, 0.5], ws = [0.1, 0.6] + - Bandstop: wp = [0.1, 0.6], ws = [0.2, 0.5] + + For analog filters, `wp` and `ws` are angular frequencies (e.g., rad/s). + gpass : float + The maximum loss in the passband (dB). + gstop : float + The minimum attenuation in the stopband (dB). + analog : bool, optional + When True, return an analog filter, otherwise a digital filter is + returned. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + ord : int + The lowest order for an Elliptic (Cauer) filter that meets specs. + wn : ndarray or float + The Chebyshev natural frequency (the "3dB frequency") for use with + `ellip` to give filter results. If `fs` is specified, + this is in the same units, and `fs` must also be passed to `ellip`. + + See Also + -------- + ellip : Filter design using order and critical points + buttord : Find order and critical points from passband and stopband spec + cheb1ord, cheb2ord + iirfilter : General filter design using order and critical frequencies + iirdesign : General filter design using passband and stopband spec + + Examples + -------- + Design an analog highpass filter such that the passband is within 3 dB + above 30 rad/s, while rejecting -60 dB at 10 rad/s. Plot its + frequency response, showing the passband and stopband constraints in gray. + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> N, Wn = signal.ellipord(30, 10, 3, 60, True) + >>> b, a = signal.ellip(N, 3, 60, Wn, 'high', True) + >>> w, h = signal.freqs(b, a, np.logspace(0, 3, 500)) + >>> plt.semilogx(w, 20 * np.log10(abs(h))) + >>> plt.title('Elliptical highpass filter fit to constraints') + >>> plt.xlabel('Frequency [radians / second]') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.grid(which='both', axis='both') + >>> plt.fill([.1, 10, 10, .1], [1e4, 1e4, -60, -60], '0.9', lw=0) # stop + >>> plt.fill([30, 30, 1e9, 1e9], [-99, -3, -3, -99], '0.9', lw=0) # pass + >>> plt.axis([1, 300, -80, 3]) + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + _validate_gpass_gstop(gpass, gstop) + wp, ws, filter_type = _validate_wp_ws(wp, ws, fs, analog) + passb, stopb = _pre_warp(wp, ws, analog) + nat, passb = _find_nat_freq(stopb, passb, gpass, gstop, filter_type, 'ellip') + + arg1_sq = _pow10m1(0.1 * gpass) / _pow10m1(0.1 * gstop) + arg0 = 1.0 / nat + d0 = special.ellipk(arg0 ** 2), special.ellipkm1(arg0 ** 2) + d1 = special.ellipk(arg1_sq), special.ellipkm1(arg1_sq) + ord = int(ceil(d0[0] * d1[1] / (d0[1] * d1[0]))) + + wn = _postprocess_wn(passb, analog, fs) + + return ord, wn + + +def buttap(N): + """Return (z,p,k) for analog prototype of Nth-order Butterworth filter. + + The filter will have an angular (e.g., rad/s) cutoff frequency of 1. + + See Also + -------- + butter : Filter design function using this prototype + + """ + if abs(int(N)) != N: + raise ValueError("Filter order must be a nonnegative integer") + z = numpy.array([]) + m = numpy.arange(-N+1, N, 2) + # Middle value is 0 to ensure an exactly real pole + p = -numpy.exp(1j * pi * m / (2 * N)) + k = 1 + return z, p, k + + +def cheb1ap(N, rp): + """ + Return (z,p,k) for Nth-order Chebyshev type I analog lowpass filter. + + The returned filter prototype has `rp` decibels of ripple in the passband. + + The filter's angular (e.g. rad/s) cutoff frequency is normalized to 1, + defined as the point at which the gain first drops below ``-rp``. + + See Also + -------- + cheby1 : Filter design function using this prototype + + """ + if abs(int(N)) != N: + raise ValueError("Filter order must be a nonnegative integer") + elif N == 0: + # Avoid divide-by-zero error + # Even order filters have DC gain of -rp dB + return numpy.array([]), numpy.array([]), 10**(-rp/20) + z = numpy.array([]) + + # Ripple factor (epsilon) + eps = numpy.sqrt(10 ** (0.1 * rp) - 1.0) + mu = 1.0 / N * arcsinh(1 / eps) + + # Arrange poles in an ellipse on the left half of the S-plane + m = numpy.arange(-N+1, N, 2) + theta = pi * m / (2*N) + p = -sinh(mu + 1j*theta) + + k = numpy.prod(-p, axis=0).real + if N % 2 == 0: + k = k / sqrt(1 + eps * eps) + + return z, p, k + + +def cheb2ap(N, rs): + """ + Return (z,p,k) for Nth-order Chebyshev type II analog lowpass filter. + + The returned filter prototype has attenuation of at least ``rs`` decibels + in the stopband. + + The filter's angular (e.g. rad/s) cutoff frequency is normalized to 1, + defined as the point at which the attenuation first reaches ``rs``. + + See Also + -------- + cheby2 : Filter design function using this prototype + + """ + if abs(int(N)) != N: + raise ValueError("Filter order must be a nonnegative integer") + elif N == 0: + # Avoid divide-by-zero warning + return numpy.array([]), numpy.array([]), 1 + + # Ripple factor (epsilon) + de = 1.0 / sqrt(10 ** (0.1 * rs) - 1) + mu = arcsinh(1.0 / de) / N + + if N % 2: + m = numpy.concatenate((numpy.arange(-N+1, 0, 2), + numpy.arange(2, N, 2))) + else: + m = numpy.arange(-N+1, N, 2) + + z = -conjugate(1j / sin(m * pi / (2.0 * N))) + + # Poles around the unit circle like Butterworth + p = -exp(1j * pi * numpy.arange(-N+1, N, 2) / (2 * N)) + # Warp into Chebyshev II + p = sinh(mu) * p.real + 1j * cosh(mu) * p.imag + p = 1.0 / p + + k = (numpy.prod(-p, axis=0) / numpy.prod(-z, axis=0)).real + return z, p, k + + +EPSILON = 2e-16 + +# number of terms in solving degree equation +_ELLIPDEG_MMAX = 7 + + +def _ellipdeg(n, m1): + """Solve degree equation using nomes + + Given n, m1, solve + n * K(m) / K'(m) = K1(m1) / K1'(m1) + for m + + See [1], Eq. (49) + + References + ---------- + .. [1] Orfanidis, "Lecture Notes on Elliptic Filter Design", + https://www.ece.rutgers.edu/~orfanidi/ece521/notes.pdf + """ + K1 = special.ellipk(m1) + K1p = special.ellipkm1(m1) + + q1 = np.exp(-np.pi * K1p / K1) + q = q1 ** (1/n) + + mnum = np.arange(_ELLIPDEG_MMAX + 1) + mden = np.arange(1, _ELLIPDEG_MMAX + 2) + + num = np.sum(q ** (mnum * (mnum+1))) + den = 1 + 2 * np.sum(q ** (mden**2)) + + return 16 * q * (num / den) ** 4 + + +# Maximum number of iterations in Landen transformation recursion +# sequence. 10 is conservative; unit tests pass with 4, Orfanidis +# (see _arc_jac_cn [1]) suggests 5. +_ARC_JAC_SN_MAXITER = 10 + + +def _arc_jac_sn(w, m): + """Inverse Jacobian elliptic sn + + Solve for z in w = sn(z, m) + + Parameters + ---------- + w : complex scalar + argument + + m : scalar + modulus; in interval [0, 1] + + + See [1], Eq. (56) + + References + ---------- + .. [1] Orfanidis, "Lecture Notes on Elliptic Filter Design", + https://www.ece.rutgers.edu/~orfanidi/ece521/notes.pdf + + """ + + def _complement(kx): + # (1-k**2) ** 0.5; the expression below + # works for small kx + return ((1 - kx) * (1 + kx)) ** 0.5 + + k = m ** 0.5 + + if k > 1: + return np.nan + elif k == 1: + return np.arctanh(w) + + ks = [k] + niter = 0 + while ks[-1] != 0: + k_ = ks[-1] + k_p = _complement(k_) + ks.append((1 - k_p) / (1 + k_p)) + niter += 1 + if niter > _ARC_JAC_SN_MAXITER: + raise ValueError('Landen transformation not converging') + + K = np.prod(1 + np.array(ks[1:])) * np.pi/2 + + wns = [w] + + for kn, knext in zip(ks[:-1], ks[1:]): + wn = wns[-1] + wnext = (2 * wn / + ((1 + knext) * (1 + _complement(kn * wn)))) + wns.append(wnext) + + u = 2 / np.pi * np.arcsin(wns[-1]) + + z = K * u + return z + + +def _arc_jac_sc1(w, m): + """Real inverse Jacobian sc, with complementary modulus + + Solve for z in w = sc(z, 1-m) + + w - real scalar + + m - modulus + + From [1], sc(z, m) = -i * sn(i * z, 1 - m) + + References + ---------- + # noqa: E501 + .. [1] https://functions.wolfram.com/EllipticFunctions/JacobiSC/introductions/JacobiPQs/ShowAll.html, + "Representations through other Jacobi functions" + + """ + + zcomplex = _arc_jac_sn(1j * w, m) + if abs(zcomplex.real) > 1e-14: + raise ValueError + + return zcomplex.imag + + +def ellipap(N, rp, rs): + """Return (z,p,k) of Nth-order elliptic analog lowpass filter. + + The filter is a normalized prototype that has `rp` decibels of ripple + in the passband and a stopband `rs` decibels down. + + The filter's angular (e.g., rad/s) cutoff frequency is normalized to 1, + defined as the point at which the gain first drops below ``-rp``. + + See Also + -------- + ellip : Filter design function using this prototype + + References + ---------- + .. [1] Lutova, Tosic, and Evans, "Filter Design for Signal Processing", + Chapters 5 and 12. + + .. [2] Orfanidis, "Lecture Notes on Elliptic Filter Design", + https://www.ece.rutgers.edu/~orfanidi/ece521/notes.pdf + + """ + if abs(int(N)) != N: + raise ValueError("Filter order must be a nonnegative integer") + elif N == 0: + # Avoid divide-by-zero warning + # Even order filters have DC gain of -rp dB + return numpy.array([]), numpy.array([]), 10**(-rp/20) + elif N == 1: + p = -sqrt(1.0 / _pow10m1(0.1 * rp)) + k = -p + z = [] + return asarray(z), asarray(p), k + + eps_sq = _pow10m1(0.1 * rp) + + eps = np.sqrt(eps_sq) + ck1_sq = eps_sq / _pow10m1(0.1 * rs) + if ck1_sq == 0: + raise ValueError("Cannot design a filter with given rp and rs" + " specifications.") + + val = special.ellipk(ck1_sq), special.ellipkm1(ck1_sq) + + m = _ellipdeg(N, ck1_sq) + + capk = special.ellipk(m) + + j = numpy.arange(1 - N % 2, N, 2) + jj = len(j) + + [s, c, d, phi] = special.ellipj(j * capk / N, m * numpy.ones(jj)) + snew = numpy.compress(abs(s) > EPSILON, s, axis=-1) + z = 1.0 / (sqrt(m) * snew) + z = 1j * z + z = numpy.concatenate((z, conjugate(z))) + + r = _arc_jac_sc1(1. / eps, ck1_sq) + v0 = capk * r / (N * val[0]) + + [sv, cv, dv, phi] = special.ellipj(v0, 1 - m) + p = -(c * d * sv * cv + 1j * s * dv) / (1 - (d * sv) ** 2.0) + + if N % 2: + newp = numpy.compress(abs(p.imag) > EPSILON * + numpy.sqrt(numpy.sum(p * numpy.conjugate(p), + axis=0).real), + p, axis=-1) + p = numpy.concatenate((p, conjugate(newp))) + else: + p = numpy.concatenate((p, conjugate(p))) + + k = (numpy.prod(-p, axis=0) / numpy.prod(-z, axis=0)).real + if N % 2 == 0: + k = k / numpy.sqrt(1 + eps_sq) + + return z, p, k + + +# TODO: Make this a real public function scipy.misc.ff +def _falling_factorial(x, n): + r""" + Return the factorial of `x` to the `n` falling. + + This is defined as: + + .. math:: x^\underline n = (x)_n = x (x-1) \cdots (x-n+1) + + This can more efficiently calculate ratios of factorials, since: + + n!/m! == falling_factorial(n, n-m) + + where n >= m + + skipping the factors that cancel out + + the usual factorial n! == ff(n, n) + """ + val = 1 + for k in range(x - n + 1, x + 1): + val *= k + return val + + +def _bessel_poly(n, reverse=False): + """ + Return the coefficients of Bessel polynomial of degree `n` + + If `reverse` is true, a reverse Bessel polynomial is output. + + Output is a list of coefficients: + [1] = 1 + [1, 1] = 1*s + 1 + [1, 3, 3] = 1*s^2 + 3*s + 3 + [1, 6, 15, 15] = 1*s^3 + 6*s^2 + 15*s + 15 + [1, 10, 45, 105, 105] = 1*s^4 + 10*s^3 + 45*s^2 + 105*s + 105 + etc. + + Output is a Python list of arbitrary precision long ints, so n is only + limited by your hardware's memory. + + Sequence is http://oeis.org/A001498, and output can be confirmed to + match http://oeis.org/A001498/b001498.txt : + + >>> from scipy.signal._filter_design import _bessel_poly + >>> i = 0 + >>> for n in range(51): + ... for x in _bessel_poly(n, reverse=True): + ... print(i, x) + ... i += 1 + + """ + if abs(int(n)) != n: + raise ValueError("Polynomial order must be a nonnegative integer") + else: + n = int(n) # np.int32 doesn't work, for instance + + out = [] + for k in range(n + 1): + num = _falling_factorial(2*n - k, n) + den = 2**(n - k) * math.factorial(k) + out.append(num // den) + + if reverse: + return out[::-1] + else: + return out + + +def _campos_zeros(n): + """ + Return approximate zero locations of Bessel polynomials y_n(x) for order + `n` using polynomial fit (Campos-Calderon 2011) + """ + if n == 1: + return asarray([-1+0j]) + + s = npp_polyval(n, [0, 0, 2, 0, -3, 1]) + b3 = npp_polyval(n, [16, -8]) / s + b2 = npp_polyval(n, [-24, -12, 12]) / s + b1 = npp_polyval(n, [8, 24, -12, -2]) / s + b0 = npp_polyval(n, [0, -6, 0, 5, -1]) / s + + r = npp_polyval(n, [0, 0, 2, 1]) + a1 = npp_polyval(n, [-6, -6]) / r + a2 = 6 / r + + k = np.arange(1, n+1) + x = npp_polyval(k, [0, a1, a2]) + y = npp_polyval(k, [b0, b1, b2, b3]) + + return x + 1j*y + + +def _aberth(f, fp, x0, tol=1e-15, maxiter=50): + """ + Given a function `f`, its first derivative `fp`, and a set of initial + guesses `x0`, simultaneously find the roots of the polynomial using the + Aberth-Ehrlich method. + + ``len(x0)`` should equal the number of roots of `f`. + + (This is not a complete implementation of Bini's algorithm.) + """ + + N = len(x0) + + x = array(x0, complex) + beta = np.empty_like(x0) + + for iteration in range(maxiter): + alpha = -f(x) / fp(x) # Newton's method + + # Model "repulsion" between zeros + for k in range(N): + beta[k] = np.sum(1/(x[k] - x[k+1:])) + beta[k] += np.sum(1/(x[k] - x[:k])) + + x += alpha / (1 + alpha * beta) + + if not all(np.isfinite(x)): + raise RuntimeError('Root-finding calculation failed') + + # Mekwi: The iterative process can be stopped when |hn| has become + # less than the largest error one is willing to permit in the root. + if all(abs(alpha) <= tol): + break + else: + raise Exception('Zeros failed to converge') + + return x + + +def _bessel_zeros(N): + """ + Find zeros of ordinary Bessel polynomial of order `N`, by root-finding of + modified Bessel function of the second kind + """ + if N == 0: + return asarray([]) + + # Generate starting points + x0 = _campos_zeros(N) + + # Zeros are the same for exp(1/x)*K_{N+0.5}(1/x) and Nth-order ordinary + # Bessel polynomial y_N(x) + def f(x): + return special.kve(N+0.5, 1/x) + + # First derivative of above + def fp(x): + return (special.kve(N-0.5, 1/x)/(2*x**2) - + special.kve(N+0.5, 1/x)/(x**2) + + special.kve(N+1.5, 1/x)/(2*x**2)) + + # Starting points converge to true zeros + x = _aberth(f, fp, x0) + + # Improve precision using Newton's method on each + for i in range(len(x)): + x[i] = optimize.newton(f, x[i], fp, tol=1e-15) + + # Average complex conjugates to make them exactly symmetrical + x = np.mean((x, x[::-1].conj()), 0) + + # Zeros should sum to -1 + if abs(np.sum(x) + 1) > 1e-15: + raise RuntimeError('Generated zeros are inaccurate') + + return x + + +def _norm_factor(p, k): + """ + Numerically find frequency shift to apply to delay-normalized filter such + that -3 dB point is at 1 rad/sec. + + `p` is an array_like of polynomial poles + `k` is a float gain + + First 10 values are listed in "Bessel Scale Factors" table, + "Bessel Filters Polynomials, Poles and Circuit Elements 2003, C. Bond." + """ + p = asarray(p, dtype=complex) + + def G(w): + """ + Gain of filter + """ + return abs(k / prod(1j*w - p)) + + def cutoff(w): + """ + When gain = -3 dB, return 0 + """ + return G(w) - 1/np.sqrt(2) + + return optimize.newton(cutoff, 1.5) + + +def besselap(N, norm='phase'): + """ + Return (z,p,k) for analog prototype of an Nth-order Bessel filter. + + Parameters + ---------- + N : int + The order of the filter. + norm : {'phase', 'delay', 'mag'}, optional + Frequency normalization: + + ``phase`` + The filter is normalized such that the phase response reaches its + midpoint at an angular (e.g., rad/s) cutoff frequency of 1. This + happens for both low-pass and high-pass filters, so this is the + "phase-matched" case. [6]_ + + The magnitude response asymptotes are the same as a Butterworth + filter of the same order with a cutoff of `Wn`. + + This is the default, and matches MATLAB's implementation. + + ``delay`` + The filter is normalized such that the group delay in the passband + is 1 (e.g., 1 second). This is the "natural" type obtained by + solving Bessel polynomials + + ``mag`` + The filter is normalized such that the gain magnitude is -3 dB at + angular frequency 1. This is called "frequency normalization" by + Bond. [1]_ + + .. versionadded:: 0.18.0 + + Returns + ------- + z : ndarray + Zeros of the transfer function. Is always an empty array. + p : ndarray + Poles of the transfer function. + k : scalar + Gain of the transfer function. For phase-normalized, this is always 1. + + See Also + -------- + bessel : Filter design function using this prototype + + Notes + ----- + To find the pole locations, approximate starting points are generated [2]_ + for the zeros of the ordinary Bessel polynomial [3]_, then the + Aberth-Ehrlich method [4]_ [5]_ is used on the Kv(x) Bessel function to + calculate more accurate zeros, and these locations are then inverted about + the unit circle. + + References + ---------- + .. [1] C.R. Bond, "Bessel Filter Constants", + http://www.crbond.com/papers/bsf.pdf + .. [2] Campos and Calderon, "Approximate closed-form formulas for the + zeros of the Bessel Polynomials", :arXiv:`1105.0957`. + .. [3] Thomson, W.E., "Delay Networks having Maximally Flat Frequency + Characteristics", Proceedings of the Institution of Electrical + Engineers, Part III, November 1949, Vol. 96, No. 44, pp. 487-490. + .. [4] Aberth, "Iteration Methods for Finding all Zeros of a Polynomial + Simultaneously", Mathematics of Computation, Vol. 27, No. 122, + April 1973 + .. [5] Ehrlich, "A modified Newton method for polynomials", Communications + of the ACM, Vol. 10, Issue 2, pp. 107-108, Feb. 1967, + :DOI:`10.1145/363067.363115` + .. [6] Miller and Bohn, "A Bessel Filter Crossover, and Its Relation to + Others", RaneNote 147, 1998, + https://www.ranecommercial.com/legacy/note147.html + + """ + if abs(int(N)) != N: + raise ValueError("Filter order must be a nonnegative integer") + + N = int(N) # calculation below doesn't always fit in np.int64 + if N == 0: + p = [] + k = 1 + else: + # Find roots of reverse Bessel polynomial + p = 1/_bessel_zeros(N) + + a_last = _falling_factorial(2*N, N) // 2**N + + # Shift them to a different normalization if required + if norm in ('delay', 'mag'): + # Normalized for group delay of 1 + k = a_last + if norm == 'mag': + # -3 dB magnitude point is at 1 rad/sec + norm_factor = _norm_factor(p, k) + p /= norm_factor + k = norm_factor**-N * a_last + elif norm == 'phase': + # Phase-matched (1/2 max phase shift at 1 rad/sec) + # Asymptotes are same as Butterworth filter + p *= 10**(-math.log10(a_last)/N) + k = 1 + else: + raise ValueError('normalization not understood') + + return asarray([]), asarray(p, dtype=complex), float(k) + + +def iirnotch(w0, Q, fs=2.0): + """ + Design second-order IIR notch digital filter. + + A notch filter is a band-stop filter with a narrow bandwidth + (high quality factor). It rejects a narrow frequency band and + leaves the rest of the spectrum little changed. + + Parameters + ---------- + w0 : float + Frequency to remove from a signal. If `fs` is specified, this is in + the same units as `fs`. By default, it is a normalized scalar that must + satisfy ``0 < w0 < 1``, with ``w0 = 1`` corresponding to half of the + sampling frequency. + Q : float + Quality factor. Dimensionless parameter that characterizes + notch filter -3 dB bandwidth ``bw`` relative to its center + frequency, ``Q = w0/bw``. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (``b``) and denominator (``a``) polynomials + of the IIR filter. + + See Also + -------- + iirpeak + + Notes + ----- + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Sophocles J. Orfanidis, "Introduction To Signal Processing", + Prentice-Hall, 1996 + + Examples + -------- + Design and plot filter to remove the 60 Hz component from a + signal sampled at 200 Hz, using a quality factor Q = 30 + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> fs = 200.0 # Sample frequency (Hz) + >>> f0 = 60.0 # Frequency to be removed from signal (Hz) + >>> Q = 30.0 # Quality factor + >>> # Design notch filter + >>> b, a = signal.iirnotch(f0, Q, fs) + + >>> # Frequency response + >>> freq, h = signal.freqz(b, a, fs=fs) + >>> # Plot + >>> fig, ax = plt.subplots(2, 1, figsize=(8, 6)) + >>> ax[0].plot(freq, 20*np.log10(abs(h)), color='blue') + >>> ax[0].set_title("Frequency Response") + >>> ax[0].set_ylabel("Amplitude (dB)", color='blue') + >>> ax[0].set_xlim([0, 100]) + >>> ax[0].set_ylim([-25, 10]) + >>> ax[0].grid(True) + >>> ax[1].plot(freq, np.unwrap(np.angle(h))*180/np.pi, color='green') + >>> ax[1].set_ylabel("Angle (degrees)", color='green') + >>> ax[1].set_xlabel("Frequency (Hz)") + >>> ax[1].set_xlim([0, 100]) + >>> ax[1].set_yticks([-90, -60, -30, 0, 30, 60, 90]) + >>> ax[1].set_ylim([-90, 90]) + >>> ax[1].grid(True) + >>> plt.show() + """ + + return _design_notch_peak_filter(w0, Q, "notch", fs) + + +def iirpeak(w0, Q, fs=2.0): + """ + Design second-order IIR peak (resonant) digital filter. + + A peak filter is a band-pass filter with a narrow bandwidth + (high quality factor). It rejects components outside a narrow + frequency band. + + Parameters + ---------- + w0 : float + Frequency to be retained in a signal. If `fs` is specified, this is in + the same units as `fs`. By default, it is a normalized scalar that must + satisfy ``0 < w0 < 1``, with ``w0 = 1`` corresponding to half of the + sampling frequency. + Q : float + Quality factor. Dimensionless parameter that characterizes + peak filter -3 dB bandwidth ``bw`` relative to its center + frequency, ``Q = w0/bw``. + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (``b``) and denominator (``a``) polynomials + of the IIR filter. + + See Also + -------- + iirnotch + + Notes + ----- + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Sophocles J. Orfanidis, "Introduction To Signal Processing", + Prentice-Hall, 1996 + + Examples + -------- + Design and plot filter to remove the frequencies other than the 300 Hz + component from a signal sampled at 1000 Hz, using a quality factor Q = 30 + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> fs = 1000.0 # Sample frequency (Hz) + >>> f0 = 300.0 # Frequency to be retained (Hz) + >>> Q = 30.0 # Quality factor + >>> # Design peak filter + >>> b, a = signal.iirpeak(f0, Q, fs) + + >>> # Frequency response + >>> freq, h = signal.freqz(b, a, fs=fs) + >>> # Plot + >>> fig, ax = plt.subplots(2, 1, figsize=(8, 6)) + >>> ax[0].plot(freq, 20*np.log10(np.maximum(abs(h), 1e-5)), color='blue') + >>> ax[0].set_title("Frequency Response") + >>> ax[0].set_ylabel("Amplitude (dB)", color='blue') + >>> ax[0].set_xlim([0, 500]) + >>> ax[0].set_ylim([-50, 10]) + >>> ax[0].grid(True) + >>> ax[1].plot(freq, np.unwrap(np.angle(h))*180/np.pi, color='green') + >>> ax[1].set_ylabel("Angle (degrees)", color='green') + >>> ax[1].set_xlabel("Frequency (Hz)") + >>> ax[1].set_xlim([0, 500]) + >>> ax[1].set_yticks([-90, -60, -30, 0, 30, 60, 90]) + >>> ax[1].set_ylim([-90, 90]) + >>> ax[1].grid(True) + >>> plt.show() + """ + + return _design_notch_peak_filter(w0, Q, "peak", fs) + + +def _design_notch_peak_filter(w0, Q, ftype, fs=2.0): + """ + Design notch or peak digital filter. + + Parameters + ---------- + w0 : float + Normalized frequency to remove from a signal. If `fs` is specified, + this is in the same units as `fs`. By default, it is a normalized + scalar that must satisfy ``0 < w0 < 1``, with ``w0 = 1`` + corresponding to half of the sampling frequency. + Q : float + Quality factor. Dimensionless parameter that characterizes + notch filter -3 dB bandwidth ``bw`` relative to its center + frequency, ``Q = w0/bw``. + ftype : str + The type of IIR filter to design: + + - notch filter : ``notch`` + - peak filter : ``peak`` + fs : float, optional + The sampling frequency of the digital system. + + .. versionadded:: 1.2.0: + + Returns + ------- + b, a : ndarray, ndarray + Numerator (``b``) and denominator (``a``) polynomials + of the IIR filter. + """ + fs = _validate_fs(fs, allow_none=False) + + # Guarantee that the inputs are floats + w0 = float(w0) + Q = float(Q) + w0 = 2*w0/fs + + # Checks if w0 is within the range + if w0 > 1.0 or w0 < 0.0: + raise ValueError("w0 should be such that 0 < w0 < 1") + + # Get bandwidth + bw = w0/Q + + # Normalize inputs + bw = bw*np.pi + w0 = w0*np.pi + + # Compute -3dB attenuation + gb = 1/np.sqrt(2) + + if ftype == "notch": + # Compute beta: formula 11.3.4 (p.575) from reference [1] + beta = (np.sqrt(1.0-gb**2.0)/gb)*np.tan(bw/2.0) + elif ftype == "peak": + # Compute beta: formula 11.3.19 (p.579) from reference [1] + beta = (gb/np.sqrt(1.0-gb**2.0))*np.tan(bw/2.0) + else: + raise ValueError("Unknown ftype.") + + # Compute gain: formula 11.3.6 (p.575) from reference [1] + gain = 1.0/(1.0+beta) + + # Compute numerator b and denominator a + # formulas 11.3.7 (p.575) and 11.3.21 (p.579) + # from reference [1] + if ftype == "notch": + b = gain*np.array([1.0, -2.0*np.cos(w0), 1.0]) + else: + b = (1.0-gain)*np.array([1.0, 0.0, -1.0]) + a = np.array([1.0, -2.0*gain*np.cos(w0), (2.0*gain-1.0)]) + + return b, a + + +def iircomb(w0, Q, ftype='notch', fs=2.0, *, pass_zero=False): + """ + Design IIR notching or peaking digital comb filter. + + A notching comb filter consists of regularly-spaced band-stop filters with + a narrow bandwidth (high quality factor). Each rejects a narrow frequency + band and leaves the rest of the spectrum little changed. + + A peaking comb filter consists of regularly-spaced band-pass filters with + a narrow bandwidth (high quality factor). Each rejects components outside + a narrow frequency band. + + Parameters + ---------- + w0 : float + The fundamental frequency of the comb filter (the spacing between its + peaks). This must evenly divide the sampling frequency. If `fs` is + specified, this is in the same units as `fs`. By default, it is + a normalized scalar that must satisfy ``0 < w0 < 1``, with + ``w0 = 1`` corresponding to half of the sampling frequency. + Q : float + Quality factor. Dimensionless parameter that characterizes + notch filter -3 dB bandwidth ``bw`` relative to its center + frequency, ``Q = w0/bw``. + ftype : {'notch', 'peak'} + The type of comb filter generated by the function. If 'notch', then + the Q factor applies to the notches. If 'peak', then the Q factor + applies to the peaks. Default is 'notch'. + fs : float, optional + The sampling frequency of the signal. Default is 2.0. + pass_zero : bool, optional + If False (default), the notches (nulls) of the filter are centered on + frequencies [0, w0, 2*w0, ...], and the peaks are centered on the + midpoints [w0/2, 3*w0/2, 5*w0/2, ...]. If True, the peaks are centered + on [0, w0, 2*w0, ...] (passing zero frequency) and vice versa. + + .. versionadded:: 1.9.0 + + Returns + ------- + b, a : ndarray, ndarray + Numerator (``b``) and denominator (``a``) polynomials + of the IIR filter. + + Raises + ------ + ValueError + If `w0` is less than or equal to 0 or greater than or equal to + ``fs/2``, if `fs` is not divisible by `w0`, if `ftype` + is not 'notch' or 'peak' + + See Also + -------- + iirnotch + iirpeak + + Notes + ----- + For implementation details, see [1]_. The TF implementation of the + comb filter is numerically stable even at higher orders due to the + use of a single repeated pole, which won't suffer from precision loss. + + References + ---------- + .. [1] Sophocles J. Orfanidis, "Introduction To Signal Processing", + Prentice-Hall, 1996, ch. 11, "Digital Filter Design" + + Examples + -------- + Design and plot notching comb filter at 20 Hz for a + signal sampled at 200 Hz, using quality factor Q = 30 + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> fs = 200.0 # Sample frequency (Hz) + >>> f0 = 20.0 # Frequency to be removed from signal (Hz) + >>> Q = 30.0 # Quality factor + >>> # Design notching comb filter + >>> b, a = signal.iircomb(f0, Q, ftype='notch', fs=fs) + + >>> # Frequency response + >>> freq, h = signal.freqz(b, a, fs=fs) + >>> response = abs(h) + >>> # To avoid divide by zero when graphing + >>> response[response == 0] = 1e-20 + >>> # Plot + >>> fig, ax = plt.subplots(2, 1, figsize=(8, 6), sharex=True) + >>> ax[0].plot(freq, 20*np.log10(abs(response)), color='blue') + >>> ax[0].set_title("Frequency Response") + >>> ax[0].set_ylabel("Amplitude (dB)", color='blue') + >>> ax[0].set_xlim([0, 100]) + >>> ax[0].set_ylim([-30, 10]) + >>> ax[0].grid(True) + >>> ax[1].plot(freq, (np.angle(h)*180/np.pi+180)%360 - 180, color='green') + >>> ax[1].set_ylabel("Angle (degrees)", color='green') + >>> ax[1].set_xlabel("Frequency (Hz)") + >>> ax[1].set_xlim([0, 100]) + >>> ax[1].set_yticks([-90, -60, -30, 0, 30, 60, 90]) + >>> ax[1].set_ylim([-90, 90]) + >>> ax[1].grid(True) + >>> plt.show() + + Design and plot peaking comb filter at 250 Hz for a + signal sampled at 1000 Hz, using quality factor Q = 30 + + >>> fs = 1000.0 # Sample frequency (Hz) + >>> f0 = 250.0 # Frequency to be retained (Hz) + >>> Q = 30.0 # Quality factor + >>> # Design peaking filter + >>> b, a = signal.iircomb(f0, Q, ftype='peak', fs=fs, pass_zero=True) + + >>> # Frequency response + >>> freq, h = signal.freqz(b, a, fs=fs) + >>> response = abs(h) + >>> # To avoid divide by zero when graphing + >>> response[response == 0] = 1e-20 + >>> # Plot + >>> fig, ax = plt.subplots(2, 1, figsize=(8, 6), sharex=True) + >>> ax[0].plot(freq, 20*np.log10(np.maximum(abs(h), 1e-5)), color='blue') + >>> ax[0].set_title("Frequency Response") + >>> ax[0].set_ylabel("Amplitude (dB)", color='blue') + >>> ax[0].set_xlim([0, 500]) + >>> ax[0].set_ylim([-80, 10]) + >>> ax[0].grid(True) + >>> ax[1].plot(freq, (np.angle(h)*180/np.pi+180)%360 - 180, color='green') + >>> ax[1].set_ylabel("Angle (degrees)", color='green') + >>> ax[1].set_xlabel("Frequency (Hz)") + >>> ax[1].set_xlim([0, 500]) + >>> ax[1].set_yticks([-90, -60, -30, 0, 30, 60, 90]) + >>> ax[1].set_ylim([-90, 90]) + >>> ax[1].grid(True) + >>> plt.show() + """ + + # Convert w0, Q, and fs to float + w0 = float(w0) + Q = float(Q) + fs = _validate_fs(fs, allow_none=False) + + # Check for invalid cutoff frequency or filter type + ftype = ftype.lower() + if not 0 < w0 < fs / 2: + raise ValueError(f"w0 must be between 0 and {fs / 2}" + f" (nyquist), but given {w0}.") + if ftype not in ('notch', 'peak'): + raise ValueError('ftype must be either notch or peak.') + + # Compute the order of the filter + N = round(fs / w0) + + # Check for cutoff frequency divisibility + if abs(w0 - fs/N)/fs > 1e-14: + raise ValueError('fs must be divisible by w0.') + + # Compute frequency in radians and filter bandwidth + # Eq. 11.3.1 (p. 574) from reference [1] + w0 = (2 * np.pi * w0) / fs + w_delta = w0 / Q + + # Define base gain values depending on notch or peak filter + # Compute -3dB attenuation + # Eqs. 11.4.1 and 11.4.2 (p. 582) from reference [1] + if ftype == 'notch': + G0, G = 1, 0 + elif ftype == 'peak': + G0, G = 0, 1 + GB = 1 / np.sqrt(2) + + # Compute beta + # Eq. 11.5.3 (p. 591) from reference [1] + beta = np.sqrt((GB**2 - G0**2) / (G**2 - GB**2)) * np.tan(N * w_delta / 4) + + # Compute filter coefficients + # Eq 11.5.1 (p. 590) variables a, b, c from reference [1] + ax = (1 - beta) / (1 + beta) + bx = (G0 + G * beta) / (1 + beta) + cx = (G0 - G * beta) / (1 + beta) + + # Last coefficients are negative to get peaking comb that passes zero or + # notching comb that doesn't. + negative_coef = ((ftype == 'peak' and pass_zero) or + (ftype == 'notch' and not pass_zero)) + + # Compute numerator coefficients + # Eq 11.5.1 (p. 590) or Eq 11.5.4 (p. 591) from reference [1] + # b - cz^-N or b + cz^-N + b = np.zeros(N + 1) + b[0] = bx + if negative_coef: + b[-1] = -cx + else: + b[-1] = +cx + + # Compute denominator coefficients + # Eq 11.5.1 (p. 590) or Eq 11.5.4 (p. 591) from reference [1] + # 1 - az^-N or 1 + az^-N + a = np.zeros(N + 1) + a[0] = 1 + if negative_coef: + a[-1] = -ax + else: + a[-1] = +ax + + return b, a + + +def _hz_to_erb(hz): + """ + Utility for converting from frequency (Hz) to the + Equivalent Rectangular Bandwidth (ERB) scale + ERB = frequency / EarQ + minBW + """ + EarQ = 9.26449 + minBW = 24.7 + return hz / EarQ + minBW + + +def gammatone(freq, ftype, order=None, numtaps=None, fs=None): + """ + Gammatone filter design. + + This function computes the coefficients of an FIR or IIR gammatone + digital filter [1]_. + + Parameters + ---------- + freq : float + Center frequency of the filter (expressed in the same units + as `fs`). + ftype : {'fir', 'iir'} + The type of filter the function generates. If 'fir', the function + will generate an Nth order FIR gammatone filter. If 'iir', the + function will generate an 8th order digital IIR filter, modeled as + as 4th order gammatone filter. + order : int, optional + The order of the filter. Only used when ``ftype='fir'``. + Default is 4 to model the human auditory system. Must be between + 0 and 24. + numtaps : int, optional + Length of the filter. Only used when ``ftype='fir'``. + Default is ``fs*0.015`` if `fs` is greater than 1000, + 15 if `fs` is less than or equal to 1000. + fs : float, optional + The sampling frequency of the signal. `freq` must be between + 0 and ``fs/2``. Default is 2. + + Returns + ------- + b, a : ndarray, ndarray + Numerator (``b``) and denominator (``a``) polynomials of the filter. + + Raises + ------ + ValueError + If `freq` is less than or equal to 0 or greater than or equal to + ``fs/2``, if `ftype` is not 'fir' or 'iir', if `order` is less than + or equal to 0 or greater than 24 when ``ftype='fir'`` + + See Also + -------- + firwin + iirfilter + + References + ---------- + .. [1] Slaney, Malcolm, "An Efficient Implementation of the + Patterson-Holdsworth Auditory Filter Bank", Apple Computer + Technical Report 35, 1993, pp.3-8, 34-39. + + Examples + -------- + 16-sample 4th order FIR Gammatone filter centered at 440 Hz + + >>> from scipy import signal + >>> signal.gammatone(440, 'fir', numtaps=16, fs=16000) + (array([ 0.00000000e+00, 2.22196719e-07, 1.64942101e-06, 4.99298227e-06, + 1.01993969e-05, 1.63125770e-05, 2.14648940e-05, 2.29947263e-05, + 1.76776931e-05, 2.04980537e-06, -2.72062858e-05, -7.28455299e-05, + -1.36651076e-04, -2.19066855e-04, -3.18905076e-04, -4.33156712e-04]), + [1.0]) + + IIR Gammatone filter centered at 440 Hz + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + + >>> b, a = signal.gammatone(440, 'iir', fs=16000) + >>> w, h = signal.freqz(b, a) + >>> plt.plot(w / ((2 * np.pi) / 16000), 20 * np.log10(abs(h))) + >>> plt.xscale('log') + >>> plt.title('Gammatone filter frequency response') + >>> plt.xlabel('Frequency') + >>> plt.ylabel('Amplitude [dB]') + >>> plt.margins(0, 0.1) + >>> plt.grid(which='both', axis='both') + >>> plt.axvline(440, color='green') # cutoff frequency + >>> plt.show() + """ + # Converts freq to float + freq = float(freq) + + # Set sampling rate if not passed + if fs is None: + fs = 2 + fs = _validate_fs(fs, allow_none=False) + + # Check for invalid cutoff frequency or filter type + ftype = ftype.lower() + filter_types = ['fir', 'iir'] + if not 0 < freq < fs / 2: + raise ValueError(f"The frequency must be between 0 and {fs / 2}" + f" (nyquist), but given {freq}.") + if ftype not in filter_types: + raise ValueError('ftype must be either fir or iir.') + + # Calculate FIR gammatone filter + if ftype == 'fir': + # Set order and numtaps if not passed + if order is None: + order = 4 + order = operator.index(order) + + if numtaps is None: + numtaps = max(int(fs * 0.015), 15) + numtaps = operator.index(numtaps) + + # Check for invalid order + if not 0 < order <= 24: + raise ValueError("Invalid order: order must be > 0 and <= 24.") + + # Gammatone impulse response settings + t = np.arange(numtaps) / fs + bw = 1.019 * _hz_to_erb(freq) + + # Calculate the FIR gammatone filter + b = (t ** (order - 1)) * np.exp(-2 * np.pi * bw * t) + b *= np.cos(2 * np.pi * freq * t) + + # Scale the FIR filter so the frequency response is 1 at cutoff + scale_factor = 2 * (2 * np.pi * bw) ** (order) + scale_factor /= float_factorial(order - 1) + scale_factor /= fs + b *= scale_factor + a = [1.0] + + # Calculate IIR gammatone filter + elif ftype == 'iir': + # Raise warning if order and/or numtaps is passed + if order is not None: + warnings.warn('order is not used for IIR gammatone filter.', stacklevel=2) + if numtaps is not None: + warnings.warn('numtaps is not used for IIR gammatone filter.', stacklevel=2) + + # Gammatone impulse response settings + T = 1./fs + bw = 2 * np.pi * 1.019 * _hz_to_erb(freq) + fr = 2 * freq * np.pi * T + bwT = bw * T + + # Calculate the gain to normalize the volume at the center frequency + g1 = -2 * np.exp(2j * fr) * T + g2 = 2 * np.exp(-(bwT) + 1j * fr) * T + g3 = np.sqrt(3 + 2 ** (3 / 2)) * np.sin(fr) + g4 = np.sqrt(3 - 2 ** (3 / 2)) * np.sin(fr) + g5 = np.exp(2j * fr) + + g = g1 + g2 * (np.cos(fr) - g4) + g *= (g1 + g2 * (np.cos(fr) + g4)) + g *= (g1 + g2 * (np.cos(fr) - g3)) + g *= (g1 + g2 * (np.cos(fr) + g3)) + g /= ((-2 / np.exp(2 * bwT) - 2 * g5 + 2 * (1 + g5) / np.exp(bwT)) ** 4) + g = np.abs(g) + + # Create empty filter coefficient lists + b = np.empty(5) + a = np.empty(9) + + # Calculate the numerator coefficients + b[0] = (T ** 4) / g + b[1] = -4 * T ** 4 * np.cos(fr) / np.exp(bw * T) / g + b[2] = 6 * T ** 4 * np.cos(2 * fr) / np.exp(2 * bw * T) / g + b[3] = -4 * T ** 4 * np.cos(3 * fr) / np.exp(3 * bw * T) / g + b[4] = T ** 4 * np.cos(4 * fr) / np.exp(4 * bw * T) / g + + # Calculate the denominator coefficients + a[0] = 1 + a[1] = -8 * np.cos(fr) / np.exp(bw * T) + a[2] = 4 * (4 + 3 * np.cos(2 * fr)) / np.exp(2 * bw * T) + a[3] = -8 * (6 * np.cos(fr) + np.cos(3 * fr)) + a[3] /= np.exp(3 * bw * T) + a[4] = 2 * (18 + 16 * np.cos(2 * fr) + np.cos(4 * fr)) + a[4] /= np.exp(4 * bw * T) + a[5] = -8 * (6 * np.cos(fr) + np.cos(3 * fr)) + a[5] /= np.exp(5 * bw * T) + a[6] = 4 * (4 + 3 * np.cos(2 * fr)) / np.exp(6 * bw * T) + a[7] = -8 * np.cos(fr) / np.exp(7 * bw * T) + a[8] = np.exp(-8 * bw * T) + + return b, a + + +filter_dict = {'butter': [buttap, buttord], + 'butterworth': [buttap, buttord], + + 'cauer': [ellipap, ellipord], + 'elliptic': [ellipap, ellipord], + 'ellip': [ellipap, ellipord], + + 'bessel': [besselap], + 'bessel_phase': [besselap], + 'bessel_delay': [besselap], + 'bessel_mag': [besselap], + + 'cheby1': [cheb1ap, cheb1ord], + 'chebyshev1': [cheb1ap, cheb1ord], + 'chebyshevi': [cheb1ap, cheb1ord], + + 'cheby2': [cheb2ap, cheb2ord], + 'chebyshev2': [cheb2ap, cheb2ord], + 'chebyshevii': [cheb2ap, cheb2ord], + } + +band_dict = {'band': 'bandpass', + 'bandpass': 'bandpass', + 'pass': 'bandpass', + 'bp': 'bandpass', + + 'bs': 'bandstop', + 'bandstop': 'bandstop', + 'bands': 'bandstop', + 'stop': 'bandstop', + + 'l': 'lowpass', + 'low': 'lowpass', + 'lowpass': 'lowpass', + 'lp': 'lowpass', + + 'high': 'highpass', + 'highpass': 'highpass', + 'h': 'highpass', + 'hp': 'highpass', + } + +bessel_norms = {'bessel': 'phase', + 'bessel_phase': 'phase', + 'bessel_delay': 'delay', + 'bessel_mag': 'mag'} diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_fir_filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_fir_filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..418bca652022b2dbb09d35b5f1def383f2c238c4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_fir_filter_design.py @@ -0,0 +1,1301 @@ +"""Functions for FIR filter design.""" + +from math import ceil, log +import operator +import warnings + +import numpy as np +from numpy.fft import irfft, fft, ifft +from scipy.special import sinc +from scipy.linalg import (toeplitz, hankel, solve, LinAlgError, LinAlgWarning, + lstsq) +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args +from scipy.signal._arraytools import _validate_fs + +from . import _sigtools + +__all__ = ['kaiser_beta', 'kaiser_atten', 'kaiserord', + 'firwin', 'firwin2', 'remez', 'firls', 'minimum_phase'] + + +def _get_fs(fs, nyq): + """ + Utility for replacing the argument 'nyq' (with default 1) with 'fs'. + """ + if nyq is _NoValue and fs is None: + fs = 2 + elif nyq is not _NoValue: + if fs is not None: + raise ValueError("Values cannot be given for both 'nyq' and 'fs'.") + msg = ("Keyword argument 'nyq' is deprecated in favour of 'fs' and " + "will be removed in SciPy 1.14.0.") + warnings.warn(msg, DeprecationWarning, stacklevel=3) + if nyq is None: + fs = 2 + else: + fs = 2*nyq + return fs + + +# Some notes on function parameters: +# +# `cutoff` and `width` are given as numbers between 0 and 1. These are +# relative frequencies, expressed as a fraction of the Nyquist frequency. +# For example, if the Nyquist frequency is 2 KHz, then width=0.15 is a width +# of 300 Hz. +# +# The `order` of a FIR filter is one less than the number of taps. +# This is a potential source of confusion, so in the following code, +# we will always use the number of taps as the parameterization of +# the 'size' of the filter. The "number of taps" means the number +# of coefficients, which is the same as the length of the impulse +# response of the filter. + + +def kaiser_beta(a): + """Compute the Kaiser parameter `beta`, given the attenuation `a`. + + Parameters + ---------- + a : float + The desired attenuation in the stopband and maximum ripple in + the passband, in dB. This should be a *positive* number. + + Returns + ------- + beta : float + The `beta` parameter to be used in the formula for a Kaiser window. + + References + ---------- + Oppenheim, Schafer, "Discrete-Time Signal Processing", p.475-476. + + Examples + -------- + Suppose we want to design a lowpass filter, with 65 dB attenuation + in the stop band. The Kaiser window parameter to be used in the + window method is computed by ``kaiser_beta(65)``: + + >>> from scipy.signal import kaiser_beta + >>> kaiser_beta(65) + 6.20426 + + """ + if a > 50: + beta = 0.1102 * (a - 8.7) + elif a > 21: + beta = 0.5842 * (a - 21) ** 0.4 + 0.07886 * (a - 21) + else: + beta = 0.0 + return beta + + +def kaiser_atten(numtaps, width): + """Compute the attenuation of a Kaiser FIR filter. + + Given the number of taps `N` and the transition width `width`, compute the + attenuation `a` in dB, given by Kaiser's formula: + + a = 2.285 * (N - 1) * pi * width + 7.95 + + Parameters + ---------- + numtaps : int + The number of taps in the FIR filter. + width : float + The desired width of the transition region between passband and + stopband (or, in general, at any discontinuity) for the filter, + expressed as a fraction of the Nyquist frequency. + + Returns + ------- + a : float + The attenuation of the ripple, in dB. + + See Also + -------- + kaiserord, kaiser_beta + + Examples + -------- + Suppose we want to design a FIR filter using the Kaiser window method + that will have 211 taps and a transition width of 9 Hz for a signal that + is sampled at 480 Hz. Expressed as a fraction of the Nyquist frequency, + the width is 9/(0.5*480) = 0.0375. The approximate attenuation (in dB) + is computed as follows: + + >>> from scipy.signal import kaiser_atten + >>> kaiser_atten(211, 0.0375) + 64.48099630593983 + + """ + a = 2.285 * (numtaps - 1) * np.pi * width + 7.95 + return a + + +def kaiserord(ripple, width): + """ + Determine the filter window parameters for the Kaiser window method. + + The parameters returned by this function are generally used to create + a finite impulse response filter using the window method, with either + `firwin` or `firwin2`. + + Parameters + ---------- + ripple : float + Upper bound for the deviation (in dB) of the magnitude of the + filter's frequency response from that of the desired filter (not + including frequencies in any transition intervals). That is, if w + is the frequency expressed as a fraction of the Nyquist frequency, + A(w) is the actual frequency response of the filter and D(w) is the + desired frequency response, the design requirement is that:: + + abs(A(w) - D(w))) < 10**(-ripple/20) + + for 0 <= w <= 1 and w not in a transition interval. + width : float + Width of transition region, normalized so that 1 corresponds to pi + radians / sample. That is, the frequency is expressed as a fraction + of the Nyquist frequency. + + Returns + ------- + numtaps : int + The length of the Kaiser window. + beta : float + The beta parameter for the Kaiser window. + + See Also + -------- + kaiser_beta, kaiser_atten + + Notes + ----- + There are several ways to obtain the Kaiser window: + + - ``signal.windows.kaiser(numtaps, beta, sym=True)`` + - ``signal.get_window(beta, numtaps)`` + - ``signal.get_window(('kaiser', beta), numtaps)`` + + The empirical equations discovered by Kaiser are used. + + References + ---------- + Oppenheim, Schafer, "Discrete-Time Signal Processing", pp.475-476. + + Examples + -------- + We will use the Kaiser window method to design a lowpass FIR filter + for a signal that is sampled at 1000 Hz. + + We want at least 65 dB rejection in the stop band, and in the pass + band the gain should vary no more than 0.5%. + + We want a cutoff frequency of 175 Hz, with a transition between the + pass band and the stop band of 24 Hz. That is, in the band [0, 163], + the gain varies no more than 0.5%, and in the band [187, 500], the + signal is attenuated by at least 65 dB. + + >>> import numpy as np + >>> from scipy.signal import kaiserord, firwin, freqz + >>> import matplotlib.pyplot as plt + >>> fs = 1000.0 + >>> cutoff = 175 + >>> width = 24 + + The Kaiser method accepts just a single parameter to control the pass + band ripple and the stop band rejection, so we use the more restrictive + of the two. In this case, the pass band ripple is 0.005, or 46.02 dB, + so we will use 65 dB as the design parameter. + + Use `kaiserord` to determine the length of the filter and the + parameter for the Kaiser window. + + >>> numtaps, beta = kaiserord(65, width/(0.5*fs)) + >>> numtaps + 167 + >>> beta + 6.20426 + + Use `firwin` to create the FIR filter. + + >>> taps = firwin(numtaps, cutoff, window=('kaiser', beta), + ... scale=False, fs=fs) + + Compute the frequency response of the filter. ``w`` is the array of + frequencies, and ``h`` is the corresponding complex array of frequency + responses. + + >>> w, h = freqz(taps, worN=8000) + >>> w *= 0.5*fs/np.pi # Convert w to Hz. + + Compute the deviation of the magnitude of the filter's response from + that of the ideal lowpass filter. Values in the transition region are + set to ``nan``, so they won't appear in the plot. + + >>> ideal = w < cutoff # The "ideal" frequency response. + >>> deviation = np.abs(np.abs(h) - ideal) + >>> deviation[(w > cutoff - 0.5*width) & (w < cutoff + 0.5*width)] = np.nan + + Plot the deviation. A close look at the left end of the stop band shows + that the requirement for 65 dB attenuation is violated in the first lobe + by about 0.125 dB. This is not unusual for the Kaiser window method. + + >>> plt.plot(w, 20*np.log10(np.abs(deviation))) + >>> plt.xlim(0, 0.5*fs) + >>> plt.ylim(-90, -60) + >>> plt.grid(alpha=0.25) + >>> plt.axhline(-65, color='r', ls='--', alpha=0.3) + >>> plt.xlabel('Frequency (Hz)') + >>> plt.ylabel('Deviation from ideal (dB)') + >>> plt.title('Lowpass Filter Frequency Response') + >>> plt.show() + + """ + A = abs(ripple) # in case somebody is confused as to what's meant + if A < 8: + # Formula for N is not valid in this range. + raise ValueError("Requested maximum ripple attenuation %f is too " + "small for the Kaiser formula." % A) + beta = kaiser_beta(A) + + # Kaiser's formula (as given in Oppenheim and Schafer) is for the filter + # order, so we have to add 1 to get the number of taps. + numtaps = (A - 7.95) / 2.285 / (np.pi * width) + 1 + + return int(ceil(numtaps)), beta + + +@_deprecate_positional_args(version="1.14") +def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True, + scale=True, nyq=_NoValue, fs=None): + """ + FIR filter design using the window method. + + This function computes the coefficients of a finite impulse response + filter. The filter will have linear phase; it will be Type I if + `numtaps` is odd and Type II if `numtaps` is even. + + Type II filters always have zero response at the Nyquist frequency, so a + ValueError exception is raised if firwin is called with `numtaps` even and + having a passband whose right end is at the Nyquist frequency. + + Parameters + ---------- + numtaps : int + Length of the filter (number of coefficients, i.e. the filter + order + 1). `numtaps` must be odd if a passband includes the + Nyquist frequency. + cutoff : float or 1-D array_like + Cutoff frequency of filter (expressed in the same units as `fs`) + OR an array of cutoff frequencies (that is, band edges). In the + latter case, the frequencies in `cutoff` should be positive and + monotonically increasing between 0 and `fs/2`. The values 0 and + `fs/2` must not be included in `cutoff`. + width : float or None, optional + If `width` is not None, then assume it is the approximate width + of the transition region (expressed in the same units as `fs`) + for use in Kaiser FIR filter design. In this case, the `window` + argument is ignored. + window : string or tuple of string and parameter values, optional + Desired window to use. See `scipy.signal.get_window` for a list + of windows and required parameters. + pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional + If True, the gain at the frequency 0 (i.e., the "DC gain") is 1. + If False, the DC gain is 0. Can also be a string argument for the + desired filter type (equivalent to ``btype`` in IIR design functions). + + .. versionadded:: 1.3.0 + Support for string arguments. + scale : bool, optional + Set to True to scale the coefficients so that the frequency + response is exactly unity at a certain frequency. + That frequency is either: + + - 0 (DC) if the first passband starts at 0 (i.e. pass_zero + is True) + - `fs/2` (the Nyquist frequency) if the first passband ends at + `fs/2` (i.e the filter is a single band highpass filter); + center of first passband otherwise + + nyq : float, optional, deprecated + This is the Nyquist frequency. Each frequency in `cutoff` must be + between 0 and `nyq`. Default is 1. + + .. deprecated:: 1.0.0 + `firwin` keyword argument `nyq` is deprecated in favour of `fs` and + will be removed in SciPy 1.14.0. + fs : float, optional + The sampling frequency of the signal. Each frequency in `cutoff` + must be between 0 and ``fs/2``. Default is 2. + + Returns + ------- + h : (numtaps,) ndarray + Coefficients of length `numtaps` FIR filter. + + Raises + ------ + ValueError + If any value in `cutoff` is less than or equal to 0 or greater + than or equal to ``fs/2``, if the values in `cutoff` are not strictly + monotonically increasing, or if `numtaps` is even but a passband + includes the Nyquist frequency. + + See Also + -------- + firwin2 + firls + minimum_phase + remez + + Examples + -------- + Low-pass from 0 to f: + + >>> from scipy import signal + >>> numtaps = 3 + >>> f = 0.1 + >>> signal.firwin(numtaps, f) + array([ 0.06799017, 0.86401967, 0.06799017]) + + Use a specific window function: + + >>> signal.firwin(numtaps, f, window='nuttall') + array([ 3.56607041e-04, 9.99286786e-01, 3.56607041e-04]) + + High-pass ('stop' from 0 to f): + + >>> signal.firwin(numtaps, f, pass_zero=False) + array([-0.00859313, 0.98281375, -0.00859313]) + + Band-pass: + + >>> f1, f2 = 0.1, 0.2 + >>> signal.firwin(numtaps, [f1, f2], pass_zero=False) + array([ 0.06301614, 0.88770441, 0.06301614]) + + Band-stop: + + >>> signal.firwin(numtaps, [f1, f2]) + array([-0.00801395, 1.0160279 , -0.00801395]) + + Multi-band (passbands are [0, f1], [f2, f3] and [f4, 1]): + + >>> f3, f4 = 0.3, 0.4 + >>> signal.firwin(numtaps, [f1, f2, f3, f4]) + array([-0.01376344, 1.02752689, -0.01376344]) + + Multi-band (passbands are [f1, f2] and [f3,f4]): + + >>> signal.firwin(numtaps, [f1, f2, f3, f4], pass_zero=False) + array([ 0.04890915, 0.91284326, 0.04890915]) + + """ + # The major enhancements to this function added in November 2010 were + # developed by Tom Krauss (see ticket #902). + fs = _validate_fs(fs, allow_none=True) + + nyq = 0.5 * _get_fs(fs, nyq) + + cutoff = np.atleast_1d(cutoff) / float(nyq) + + # Check for invalid input. + if cutoff.ndim > 1: + raise ValueError("The cutoff argument must be at most " + "one-dimensional.") + if cutoff.size == 0: + raise ValueError("At least one cutoff frequency must be given.") + if cutoff.min() <= 0 or cutoff.max() >= 1: + raise ValueError("Invalid cutoff frequency: frequencies must be " + "greater than 0 and less than fs/2.") + if np.any(np.diff(cutoff) <= 0): + raise ValueError("Invalid cutoff frequencies: the frequencies " + "must be strictly increasing.") + + if width is not None: + # A width was given. Find the beta parameter of the Kaiser window + # and set `window`. This overrides the value of `window` passed in. + atten = kaiser_atten(numtaps, float(width) / nyq) + beta = kaiser_beta(atten) + window = ('kaiser', beta) + + if isinstance(pass_zero, str): + if pass_zero in ('bandstop', 'lowpass'): + if pass_zero == 'lowpass': + if cutoff.size != 1: + raise ValueError('cutoff must have one element if ' + f'pass_zero=="lowpass", got {cutoff.shape}') + elif cutoff.size <= 1: + raise ValueError('cutoff must have at least two elements if ' + f'pass_zero=="bandstop", got {cutoff.shape}') + pass_zero = True + elif pass_zero in ('bandpass', 'highpass'): + if pass_zero == 'highpass': + if cutoff.size != 1: + raise ValueError('cutoff must have one element if ' + f'pass_zero=="highpass", got {cutoff.shape}') + elif cutoff.size <= 1: + raise ValueError('cutoff must have at least two elements if ' + f'pass_zero=="bandpass", got {cutoff.shape}') + pass_zero = False + else: + raise ValueError('pass_zero must be True, False, "bandpass", ' + '"lowpass", "highpass", or "bandstop", got ' + f'{pass_zero}') + pass_zero = bool(operator.index(pass_zero)) # ensure bool-like + + pass_nyquist = bool(cutoff.size & 1) ^ pass_zero + if pass_nyquist and numtaps % 2 == 0: + raise ValueError("A filter with an even number of coefficients must " + "have zero response at the Nyquist frequency.") + + # Insert 0 and/or 1 at the ends of cutoff so that the length of cutoff + # is even, and each pair in cutoff corresponds to passband. + cutoff = np.hstack(([0.0] * pass_zero, cutoff, [1.0] * pass_nyquist)) + + # `bands` is a 2-D array; each row gives the left and right edges of + # a passband. + bands = cutoff.reshape(-1, 2) + + # Build up the coefficients. + alpha = 0.5 * (numtaps - 1) + m = np.arange(0, numtaps) - alpha + h = 0 + for left, right in bands: + h += right * sinc(right * m) + h -= left * sinc(left * m) + + # Get and apply the window function. + from .windows import get_window + win = get_window(window, numtaps, fftbins=False) + h *= win + + # Now handle scaling if desired. + if scale: + # Get the first passband. + left, right = bands[0] + if left == 0: + scale_frequency = 0.0 + elif right == 1: + scale_frequency = 1.0 + else: + scale_frequency = 0.5 * (left + right) + c = np.cos(np.pi * m * scale_frequency) + s = np.sum(h * c) + h /= s + + return h + + +# Original version of firwin2 from scipy ticket #457, submitted by "tash". +# +# Rewritten by Warren Weckesser, 2010. +@_deprecate_positional_args(version="1.14") +def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming', nyq=_NoValue, + antisymmetric=False, fs=None): + """ + FIR filter design using the window method. + + From the given frequencies `freq` and corresponding gains `gain`, + this function constructs an FIR filter with linear phase and + (approximately) the given frequency response. + + Parameters + ---------- + numtaps : int + The number of taps in the FIR filter. `numtaps` must be less than + `nfreqs`. + freq : array_like, 1-D + The frequency sampling points. Typically 0.0 to 1.0 with 1.0 being + Nyquist. The Nyquist frequency is half `fs`. + The values in `freq` must be nondecreasing. A value can be repeated + once to implement a discontinuity. The first value in `freq` must + be 0, and the last value must be ``fs/2``. Values 0 and ``fs/2`` must + not be repeated. + gain : array_like + The filter gains at the frequency sampling points. Certain + constraints to gain values, depending on the filter type, are applied, + see Notes for details. + nfreqs : int, optional + The size of the interpolation mesh used to construct the filter. + For most efficient behavior, this should be a power of 2 plus 1 + (e.g, 129, 257, etc). The default is one more than the smallest + power of 2 that is not less than `numtaps`. `nfreqs` must be greater + than `numtaps`. + window : string or (string, float) or float, or None, optional + Window function to use. Default is "hamming". See + `scipy.signal.get_window` for the complete list of possible values. + If None, no window function is applied. + nyq : float, optional, deprecated + This is the Nyquist frequency. Each frequency in `freq` must be + between 0 and `nyq`. Default is 1. + + .. deprecated:: 1.0.0 + `firwin2` keyword argument `nyq` is deprecated in favour of `fs` and + will be removed in SciPy 1.14.0. + antisymmetric : bool, optional + Whether resulting impulse response is symmetric/antisymmetric. + See Notes for more details. + fs : float, optional + The sampling frequency of the signal. Each frequency in `cutoff` + must be between 0 and ``fs/2``. Default is 2. + + Returns + ------- + taps : ndarray + The filter coefficients of the FIR filter, as a 1-D array of length + `numtaps`. + + See Also + -------- + firls + firwin + minimum_phase + remez + + Notes + ----- + From the given set of frequencies and gains, the desired response is + constructed in the frequency domain. The inverse FFT is applied to the + desired response to create the associated convolution kernel, and the + first `numtaps` coefficients of this kernel, scaled by `window`, are + returned. + + The FIR filter will have linear phase. The type of filter is determined by + the value of 'numtaps` and `antisymmetric` flag. + There are four possible combinations: + + - odd `numtaps`, `antisymmetric` is False, type I filter is produced + - even `numtaps`, `antisymmetric` is False, type II filter is produced + - odd `numtaps`, `antisymmetric` is True, type III filter is produced + - even `numtaps`, `antisymmetric` is True, type IV filter is produced + + Magnitude response of all but type I filters are subjects to following + constraints: + + - type II -- zero at the Nyquist frequency + - type III -- zero at zero and Nyquist frequencies + - type IV -- zero at zero frequency + + .. versionadded:: 0.9.0 + + References + ---------- + .. [1] Oppenheim, A. V. and Schafer, R. W., "Discrete-Time Signal + Processing", Prentice-Hall, Englewood Cliffs, New Jersey (1989). + (See, for example, Section 7.4.) + + .. [2] Smith, Steven W., "The Scientist and Engineer's Guide to Digital + Signal Processing", Ch. 17. http://www.dspguide.com/ch17/1.htm + + Examples + -------- + A lowpass FIR filter with a response that is 1 on [0.0, 0.5], and + that decreases linearly on [0.5, 1.0] from 1 to 0: + + >>> from scipy import signal + >>> taps = signal.firwin2(150, [0.0, 0.5, 1.0], [1.0, 1.0, 0.0]) + >>> print(taps[72:78]) + [-0.02286961 -0.06362756 0.57310236 0.57310236 -0.06362756 -0.02286961] + + """ + fs = _validate_fs(fs, allow_none=True) + nyq = 0.5 * _get_fs(fs, nyq) + + if len(freq) != len(gain): + raise ValueError('freq and gain must be of same length.') + + if nfreqs is not None and numtaps >= nfreqs: + raise ValueError(('ntaps must be less than nfreqs, but firwin2 was ' + 'called with ntaps=%d and nfreqs=%s') % + (numtaps, nfreqs)) + + if freq[0] != 0 or freq[-1] != nyq: + raise ValueError('freq must start with 0 and end with fs/2.') + d = np.diff(freq) + if (d < 0).any(): + raise ValueError('The values in freq must be nondecreasing.') + d2 = d[:-1] + d[1:] + if (d2 == 0).any(): + raise ValueError('A value in freq must not occur more than twice.') + if freq[1] == 0: + raise ValueError('Value 0 must not be repeated in freq') + if freq[-2] == nyq: + raise ValueError('Value fs/2 must not be repeated in freq') + + if antisymmetric: + if numtaps % 2 == 0: + ftype = 4 + else: + ftype = 3 + else: + if numtaps % 2 == 0: + ftype = 2 + else: + ftype = 1 + + if ftype == 2 and gain[-1] != 0.0: + raise ValueError("A Type II filter must have zero gain at the " + "Nyquist frequency.") + elif ftype == 3 and (gain[0] != 0.0 or gain[-1] != 0.0): + raise ValueError("A Type III filter must have zero gain at zero " + "and Nyquist frequencies.") + elif ftype == 4 and gain[0] != 0.0: + raise ValueError("A Type IV filter must have zero gain at zero " + "frequency.") + + if nfreqs is None: + nfreqs = 1 + 2 ** int(ceil(log(numtaps, 2))) + + if (d == 0).any(): + # Tweak any repeated values in freq so that interp works. + freq = np.array(freq, copy=True) + eps = np.finfo(float).eps * nyq + for k in range(len(freq) - 1): + if freq[k] == freq[k + 1]: + freq[k] = freq[k] - eps + freq[k + 1] = freq[k + 1] + eps + # Check if freq is strictly increasing after tweak + d = np.diff(freq) + if (d <= 0).any(): + raise ValueError("freq cannot contain numbers that are too close " + "(within eps * (fs/2): " + f"{eps}) to a repeated value") + + # Linearly interpolate the desired response on a uniform mesh `x`. + x = np.linspace(0.0, nyq, nfreqs) + fx = np.interp(x, freq, gain) + + # Adjust the phases of the coefficients so that the first `ntaps` of the + # inverse FFT are the desired filter coefficients. + shift = np.exp(-(numtaps - 1) / 2. * 1.j * np.pi * x / nyq) + if ftype > 2: + shift *= 1j + + fx2 = fx * shift + + # Use irfft to compute the inverse FFT. + out_full = irfft(fx2) + + if window is not None: + # Create the window to apply to the filter coefficients. + from .windows import get_window + wind = get_window(window, numtaps, fftbins=False) + else: + wind = 1 + + # Keep only the first `numtaps` coefficients in `out`, and multiply by + # the window. + out = out_full[:numtaps] * wind + + if ftype == 3: + out[out.size // 2] = 0.0 + + return out + + +@_deprecate_positional_args(version="1.14") +def remez(numtaps, bands, desired, *, weight=None, Hz=_NoValue, type='bandpass', + maxiter=25, grid_density=16, fs=None): + """ + Calculate the minimax optimal filter using the Remez exchange algorithm. + + Calculate the filter-coefficients for the finite impulse response + (FIR) filter whose transfer function minimizes the maximum error + between the desired gain and the realized gain in the specified + frequency bands using the Remez exchange algorithm. + + Parameters + ---------- + numtaps : int + The desired number of taps in the filter. The number of taps is + the number of terms in the filter, or the filter order plus one. + bands : array_like + A monotonic sequence containing the band edges. + All elements must be non-negative and less than half the sampling + frequency as given by `fs`. + desired : array_like + A sequence half the size of bands containing the desired gain + in each of the specified bands. + weight : array_like, optional + A relative weighting to give to each band region. The length of + `weight` has to be half the length of `bands`. + Hz : scalar, optional, deprecated + The sampling frequency in Hz. Default is 1. + + .. deprecated:: 1.0.0 + `remez` keyword argument `Hz` is deprecated in favour of `fs` and + will be removed in SciPy 1.14.0. + type : {'bandpass', 'differentiator', 'hilbert'}, optional + The type of filter: + + * 'bandpass' : flat response in bands. This is the default. + + * 'differentiator' : frequency proportional response in bands. + + * 'hilbert' : filter with odd symmetry, that is, type III + (for even order) or type IV (for odd order) + linear phase filters. + + maxiter : int, optional + Maximum number of iterations of the algorithm. Default is 25. + grid_density : int, optional + Grid density. The dense grid used in `remez` is of size + ``(numtaps + 1) * grid_density``. Default is 16. + fs : float, optional + The sampling frequency of the signal. Default is 1. + + Returns + ------- + out : ndarray + A rank-1 array containing the coefficients of the optimal + (in a minimax sense) filter. + + See Also + -------- + firls + firwin + firwin2 + minimum_phase + + References + ---------- + .. [1] J. H. McClellan and T. W. Parks, "A unified approach to the + design of optimum FIR linear phase digital filters", + IEEE Trans. Circuit Theory, vol. CT-20, pp. 697-701, 1973. + .. [2] J. H. McClellan, T. W. Parks and L. R. Rabiner, "A Computer + Program for Designing Optimum FIR Linear Phase Digital + Filters", IEEE Trans. Audio Electroacoust., vol. AU-21, + pp. 506-525, 1973. + + Examples + -------- + In these examples, `remez` is used to design low-pass, high-pass, + band-pass and band-stop filters. The parameters that define each filter + are the filter order, the band boundaries, the transition widths of the + boundaries, the desired gains in each band, and the sampling frequency. + + We'll use a sample frequency of 22050 Hz in all the examples. In each + example, the desired gain in each band is either 0 (for a stop band) + or 1 (for a pass band). + + `freqz` is used to compute the frequency response of each filter, and + the utility function ``plot_response`` defined below is used to plot + the response. + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> fs = 22050 # Sample rate, Hz + + >>> def plot_response(w, h, title): + ... "Utility function to plot response functions" + ... fig = plt.figure() + ... ax = fig.add_subplot(111) + ... ax.plot(w, 20*np.log10(np.abs(h))) + ... ax.set_ylim(-40, 5) + ... ax.grid(True) + ... ax.set_xlabel('Frequency (Hz)') + ... ax.set_ylabel('Gain (dB)') + ... ax.set_title(title) + + The first example is a low-pass filter, with cutoff frequency 8 kHz. + The filter length is 325, and the transition width from pass to stop + is 100 Hz. + + >>> cutoff = 8000.0 # Desired cutoff frequency, Hz + >>> trans_width = 100 # Width of transition from pass to stop, Hz + >>> numtaps = 325 # Size of the FIR filter. + >>> taps = signal.remez(numtaps, [0, cutoff, cutoff + trans_width, 0.5*fs], + ... [1, 0], fs=fs) + >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs) + >>> plot_response(w, h, "Low-pass Filter") + >>> plt.show() + + This example shows a high-pass filter: + + >>> cutoff = 2000.0 # Desired cutoff frequency, Hz + >>> trans_width = 250 # Width of transition from pass to stop, Hz + >>> numtaps = 125 # Size of the FIR filter. + >>> taps = signal.remez(numtaps, [0, cutoff - trans_width, cutoff, 0.5*fs], + ... [0, 1], fs=fs) + >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs) + >>> plot_response(w, h, "High-pass Filter") + >>> plt.show() + + This example shows a band-pass filter with a pass-band from 2 kHz to + 5 kHz. The transition width is 260 Hz and the length of the filter + is 63, which is smaller than in the other examples: + + >>> band = [2000, 5000] # Desired pass band, Hz + >>> trans_width = 260 # Width of transition from pass to stop, Hz + >>> numtaps = 63 # Size of the FIR filter. + >>> edges = [0, band[0] - trans_width, band[0], band[1], + ... band[1] + trans_width, 0.5*fs] + >>> taps = signal.remez(numtaps, edges, [0, 1, 0], fs=fs) + >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs) + >>> plot_response(w, h, "Band-pass Filter") + >>> plt.show() + + The low order leads to higher ripple and less steep transitions. + + The next example shows a band-stop filter. + + >>> band = [6000, 8000] # Desired stop band, Hz + >>> trans_width = 200 # Width of transition from pass to stop, Hz + >>> numtaps = 175 # Size of the FIR filter. + >>> edges = [0, band[0] - trans_width, band[0], band[1], + ... band[1] + trans_width, 0.5*fs] + >>> taps = signal.remez(numtaps, edges, [1, 0, 1], fs=fs) + >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs) + >>> plot_response(w, h, "Band-stop Filter") + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + if Hz is _NoValue and fs is None: + fs = 1.0 + elif Hz is not _NoValue: + if fs is not None: + raise ValueError("Values cannot be given for both 'Hz' and 'fs'.") + msg = ("'remez' keyword argument 'Hz' is deprecated in favour of 'fs'" + " and will be removed in SciPy 1.14.0.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) + fs = Hz + + # Convert type + try: + tnum = {'bandpass': 1, 'differentiator': 2, 'hilbert': 3}[type] + except KeyError as e: + raise ValueError("Type must be 'bandpass', 'differentiator', " + "or 'hilbert'") from e + + # Convert weight + if weight is None: + weight = [1] * len(desired) + + bands = np.asarray(bands).copy() + return _sigtools._remez(numtaps, bands, desired, weight, tnum, fs, + maxiter, grid_density) + + +@_deprecate_positional_args(version="1.14") +def firls(numtaps, bands, desired, *, weight=None, nyq=_NoValue, fs=None): + """ + FIR filter design using least-squares error minimization. + + Calculate the filter coefficients for the linear-phase finite + impulse response (FIR) filter which has the best approximation + to the desired frequency response described by `bands` and + `desired` in the least squares sense (i.e., the integral of the + weighted mean-squared error within the specified bands is + minimized). + + Parameters + ---------- + numtaps : int + The number of taps in the FIR filter. `numtaps` must be odd. + bands : array_like + A monotonic nondecreasing sequence containing the band edges in + Hz. All elements must be non-negative and less than or equal to + the Nyquist frequency given by `nyq`. The bands are specified as + frequency pairs, thus, if using a 1D array, its length must be + even, e.g., `np.array([0, 1, 2, 3, 4, 5])`. Alternatively, the + bands can be specified as an nx2 sized 2D array, where n is the + number of bands, e.g, `np.array([[0, 1], [2, 3], [4, 5]])`. + desired : array_like + A sequence the same size as `bands` containing the desired gain + at the start and end point of each band. + weight : array_like, optional + A relative weighting to give to each band region when solving + the least squares problem. `weight` has to be half the size of + `bands`. + nyq : float, optional, deprecated + This is the Nyquist frequency. Each frequency in `bands` must be + between 0 and `nyq` (inclusive). Default is 1. + + .. deprecated:: 1.0.0 + `firls` keyword argument `nyq` is deprecated in favour of `fs` and + will be removed in SciPy 1.14.0. + fs : float, optional + The sampling frequency of the signal. Each frequency in `bands` + must be between 0 and ``fs/2`` (inclusive). Default is 2. + + Returns + ------- + coeffs : ndarray + Coefficients of the optimal (in a least squares sense) FIR filter. + + See Also + -------- + firwin + firwin2 + minimum_phase + remez + + Notes + ----- + This implementation follows the algorithm given in [1]_. + As noted there, least squares design has multiple advantages: + + 1. Optimal in a least-squares sense. + 2. Simple, non-iterative method. + 3. The general solution can obtained by solving a linear + system of equations. + 4. Allows the use of a frequency dependent weighting function. + + This function constructs a Type I linear phase FIR filter, which + contains an odd number of `coeffs` satisfying for :math:`n < numtaps`: + + .. math:: coeffs(n) = coeffs(numtaps - 1 - n) + + The odd number of coefficients and filter symmetry avoid boundary + conditions that could otherwise occur at the Nyquist and 0 frequencies + (e.g., for Type II, III, or IV variants). + + .. versionadded:: 0.18 + + References + ---------- + .. [1] Ivan Selesnick, Linear-Phase Fir Filter Design By Least Squares. + OpenStax CNX. Aug 9, 2005. + http://cnx.org/contents/eb1ecb35-03a9-4610-ba87-41cd771c95f2@7 + + Examples + -------- + We want to construct a band-pass filter. Note that the behavior in the + frequency ranges between our stop bands and pass bands is unspecified, + and thus may overshoot depending on the parameters of our filter: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> fig, axs = plt.subplots(2) + >>> fs = 10.0 # Hz + >>> desired = (0, 0, 1, 1, 0, 0) + >>> for bi, bands in enumerate(((0, 1, 2, 3, 4, 5), (0, 1, 2, 4, 4.5, 5))): + ... fir_firls = signal.firls(73, bands, desired, fs=fs) + ... fir_remez = signal.remez(73, bands, desired[::2], fs=fs) + ... fir_firwin2 = signal.firwin2(73, bands, desired, fs=fs) + ... hs = list() + ... ax = axs[bi] + ... for fir in (fir_firls, fir_remez, fir_firwin2): + ... freq, response = signal.freqz(fir) + ... hs.append(ax.semilogy(0.5*fs*freq/np.pi, np.abs(response))[0]) + ... for band, gains in zip(zip(bands[::2], bands[1::2]), + ... zip(desired[::2], desired[1::2])): + ... ax.semilogy(band, np.maximum(gains, 1e-7), 'k--', linewidth=2) + ... if bi == 0: + ... ax.legend(hs, ('firls', 'remez', 'firwin2'), + ... loc='lower center', frameon=False) + ... else: + ... ax.set_xlabel('Frequency (Hz)') + ... ax.grid(True) + ... ax.set(title='Band-pass %d-%d Hz' % bands[2:4], ylabel='Magnitude') + ... + >>> fig.tight_layout() + >>> plt.show() + + """ + fs = _validate_fs(fs, allow_none=True) + nyq = 0.5 * _get_fs(fs, nyq) + + numtaps = int(numtaps) + if numtaps % 2 == 0 or numtaps < 1: + raise ValueError("numtaps must be odd and >= 1") + M = (numtaps-1) // 2 + + # normalize bands 0->1 and make it 2 columns + nyq = float(nyq) + if nyq <= 0: + raise ValueError('nyq must be positive, got %s <= 0.' % nyq) + bands = np.asarray(bands).flatten() / nyq + if len(bands) % 2 != 0: + raise ValueError("bands must contain frequency pairs.") + if (bands < 0).any() or (bands > 1).any(): + raise ValueError("bands must be between 0 and 1 relative to Nyquist") + bands.shape = (-1, 2) + + # check remaining params + desired = np.asarray(desired).flatten() + if bands.size != desired.size: + raise ValueError("desired must have one entry per frequency, got {} " + "gains for {} frequencies.".format(desired.size, bands.size)) + desired.shape = (-1, 2) + if (np.diff(bands) <= 0).any() or (np.diff(bands[:, 0]) < 0).any(): + raise ValueError("bands must be monotonically nondecreasing and have " + "width > 0.") + if (bands[:-1, 1] > bands[1:, 0]).any(): + raise ValueError("bands must not overlap.") + if (desired < 0).any(): + raise ValueError("desired must be non-negative.") + if weight is None: + weight = np.ones(len(desired)) + weight = np.asarray(weight).flatten() + if len(weight) != len(desired): + raise ValueError("weight must be the same size as the number of " + f"band pairs ({len(bands)}).") + if (weight < 0).any(): + raise ValueError("weight must be non-negative.") + + # Set up the linear matrix equation to be solved, Qa = b + + # We can express Q(k,n) = 0.5 Q1(k,n) + 0.5 Q2(k,n) + # where Q1(k,n)=q(k-n) and Q2(k,n)=q(k+n), i.e. a Toeplitz plus Hankel. + + # We omit the factor of 0.5 above, instead adding it during coefficient + # calculation. + + # We also omit the 1/π from both Q and b equations, as they cancel + # during solving. + + # We have that: + # q(n) = 1/π ∫W(ω)cos(nω)dω (over 0->π) + # Using our normalization ω=πf and with a constant weight W over each + # interval f1->f2 we get: + # q(n) = W∫cos(πnf)df (0->1) = Wf sin(πnf)/πnf + # integrated over each f1->f2 pair (i.e., value at f2 - value at f1). + n = np.arange(numtaps)[:, np.newaxis, np.newaxis] + q = np.dot(np.diff(np.sinc(bands * n) * bands, axis=2)[:, :, 0], weight) + + # Now we assemble our sum of Toeplitz and Hankel + Q1 = toeplitz(q[:M+1]) + Q2 = hankel(q[:M+1], q[M:]) + Q = Q1 + Q2 + + # Now for b(n) we have that: + # b(n) = 1/π ∫ W(ω)D(ω)cos(nω)dω (over 0->π) + # Using our normalization ω=πf and with a constant weight W over each + # interval and a linear term for D(ω) we get (over each f1->f2 interval): + # b(n) = W ∫ (mf+c)cos(πnf)df + # = f(mf+c)sin(πnf)/πnf + mf**2 cos(nπf)/(πnf)**2 + # integrated over each f1->f2 pair (i.e., value at f2 - value at f1). + n = n[:M + 1] # only need this many coefficients here + # Choose m and c such that we are at the start and end weights + m = (np.diff(desired, axis=1) / np.diff(bands, axis=1)) + c = desired[:, [0]] - bands[:, [0]] * m + b = bands * (m*bands + c) * np.sinc(bands * n) + # Use L'Hospital's rule here for cos(nπf)/(πnf)**2 @ n=0 + b[0] -= m * bands * bands / 2. + b[1:] += m * np.cos(n[1:] * np.pi * bands) / (np.pi * n[1:]) ** 2 + b = np.dot(np.diff(b, axis=2)[:, :, 0], weight) + + # Now we can solve the equation + try: # try the fast way + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + a = solve(Q, b, assume_a="pos", check_finite=False) + for ww in w: + if (ww.category == LinAlgWarning and + str(ww.message).startswith('Ill-conditioned matrix')): + raise LinAlgError(str(ww.message)) + except LinAlgError: # in case Q is rank deficient + # This is faster than pinvh, even though we don't explicitly use + # the symmetry here. gelsy was faster than gelsd and gelss in + # some non-exhaustive tests. + a = lstsq(Q, b, lapack_driver='gelsy')[0] + + # make coefficients symmetric (linear phase) + coeffs = np.hstack((a[:0:-1], 2 * a[0], a[1:])) + return coeffs + + +def _dhtm(mag): + """Compute the modified 1-D discrete Hilbert transform + + Parameters + ---------- + mag : ndarray + The magnitude spectrum. Should be 1-D with an even length, and + preferably a fast length for FFT/IFFT. + """ + # Adapted based on code by Niranjan Damera-Venkata, + # Brian L. Evans and Shawn R. McCaslin (see refs for `minimum_phase`) + sig = np.zeros(len(mag)) + # Leave Nyquist and DC at 0, knowing np.abs(fftfreq(N)[midpt]) == 0.5 + midpt = len(mag) // 2 + sig[1:midpt] = 1 + sig[midpt+1:] = -1 + # eventually if we want to support complex filters, we will need a + # np.abs() on the mag inside the log, and should remove the .real + recon = ifft(mag * np.exp(fft(sig * ifft(np.log(mag))))).real + return recon + + +def minimum_phase(h, method='homomorphic', n_fft=None): + """Convert a linear-phase FIR filter to minimum phase + + Parameters + ---------- + h : array + Linear-phase FIR filter coefficients. + method : {'hilbert', 'homomorphic'} + The method to use: + + 'homomorphic' (default) + This method [4]_ [5]_ works best with filters with an + odd number of taps, and the resulting minimum phase filter + will have a magnitude response that approximates the square + root of the original filter's magnitude response. + + 'hilbert' + This method [1]_ is designed to be used with equiripple + filters (e.g., from `remez`) with unity or zero gain + regions. + + n_fft : int + The number of points to use for the FFT. Should be at least a + few times larger than the signal length (see Notes). + + Returns + ------- + h_minimum : array + The minimum-phase version of the filter, with length + ``(length(h) + 1) // 2``. + + See Also + -------- + firwin + firwin2 + remez + + Notes + ----- + Both the Hilbert [1]_ or homomorphic [4]_ [5]_ methods require selection + of an FFT length to estimate the complex cepstrum of the filter. + + In the case of the Hilbert method, the deviation from the ideal + spectrum ``epsilon`` is related to the number of stopband zeros + ``n_stop`` and FFT length ``n_fft`` as:: + + epsilon = 2. * n_stop / n_fft + + For example, with 100 stopband zeros and a FFT length of 2048, + ``epsilon = 0.0976``. If we conservatively assume that the number of + stopband zeros is one less than the filter length, we can take the FFT + length to be the next power of 2 that satisfies ``epsilon=0.01`` as:: + + n_fft = 2 ** int(np.ceil(np.log2(2 * (len(h) - 1) / 0.01))) + + This gives reasonable results for both the Hilbert and homomorphic + methods, and gives the value used when ``n_fft=None``. + + Alternative implementations exist for creating minimum-phase filters, + including zero inversion [2]_ and spectral factorization [3]_ [4]_. + For more information, see: + + http://dspguru.com/dsp/howtos/how-to-design-minimum-phase-fir-filters + + References + ---------- + .. [1] N. Damera-Venkata and B. L. Evans, "Optimal design of real and + complex minimum phase digital FIR filters," Acoustics, Speech, + and Signal Processing, 1999. Proceedings., 1999 IEEE International + Conference on, Phoenix, AZ, 1999, pp. 1145-1148 vol.3. + :doi:`10.1109/ICASSP.1999.756179` + .. [2] X. Chen and T. W. Parks, "Design of optimal minimum phase FIR + filters by direct factorization," Signal Processing, + vol. 10, no. 4, pp. 369-383, Jun. 1986. + .. [3] T. Saramaki, "Finite Impulse Response Filter Design," in + Handbook for Digital Signal Processing, chapter 4, + New York: Wiley-Interscience, 1993. + .. [4] J. S. Lim, Advanced Topics in Signal Processing. + Englewood Cliffs, N.J.: Prentice Hall, 1988. + .. [5] A. V. Oppenheim, R. W. Schafer, and J. R. Buck, + "Discrete-Time Signal Processing," 2nd edition. + Upper Saddle River, N.J.: Prentice Hall, 1999. + + Examples + -------- + Create an optimal linear-phase filter, then convert it to minimum phase: + + >>> import numpy as np + >>> from scipy.signal import remez, minimum_phase, freqz, group_delay + >>> import matplotlib.pyplot as plt + >>> freq = [0, 0.2, 0.3, 1.0] + >>> desired = [1, 0] + >>> h_linear = remez(151, freq, desired, fs=2.) + + Convert it to minimum phase: + + >>> h_min_hom = minimum_phase(h_linear, method='homomorphic') + >>> h_min_hil = minimum_phase(h_linear, method='hilbert') + + Compare the three filters: + + >>> fig, axs = plt.subplots(4, figsize=(4, 8)) + >>> for h, style, color in zip((h_linear, h_min_hom, h_min_hil), + ... ('-', '-', '--'), ('k', 'r', 'c')): + ... w, H = freqz(h) + ... w, gd = group_delay((h, 1)) + ... w /= np.pi + ... axs[0].plot(h, color=color, linestyle=style) + ... axs[1].plot(w, np.abs(H), color=color, linestyle=style) + ... axs[2].plot(w, 20 * np.log10(np.abs(H)), color=color, linestyle=style) + ... axs[3].plot(w, gd, color=color, linestyle=style) + >>> for ax in axs: + ... ax.grid(True, color='0.5') + ... ax.fill_between(freq[1:3], *ax.get_ylim(), color='#ffeeaa', zorder=1) + >>> axs[0].set(xlim=[0, len(h_linear) - 1], ylabel='Amplitude', xlabel='Samples') + >>> axs[1].legend(['Linear', 'Min-Hom', 'Min-Hil'], title='Phase') + >>> for ax, ylim in zip(axs[1:], ([0, 1.1], [-150, 10], [-60, 60])): + ... ax.set(xlim=[0, 1], ylim=ylim, xlabel='Frequency') + >>> axs[1].set(ylabel='Magnitude') + >>> axs[2].set(ylabel='Magnitude (dB)') + >>> axs[3].set(ylabel='Group delay') + >>> plt.tight_layout() + + """ + h = np.asarray(h) + if np.iscomplexobj(h): + raise ValueError('Complex filters not supported') + if h.ndim != 1 or h.size <= 2: + raise ValueError('h must be 1-D and at least 2 samples long') + n_half = len(h) // 2 + if not np.allclose(h[-n_half:][::-1], h[:n_half]): + warnings.warn('h does not appear to by symmetric, conversion may fail', + RuntimeWarning, stacklevel=2) + if not isinstance(method, str) or method not in \ + ('homomorphic', 'hilbert',): + raise ValueError(f'method must be "homomorphic" or "hilbert", got {method!r}') + if n_fft is None: + n_fft = 2 ** int(np.ceil(np.log2(2 * (len(h) - 1) / 0.01))) + n_fft = int(n_fft) + if n_fft < len(h): + raise ValueError('n_fft must be at least len(h)==%s' % len(h)) + if method == 'hilbert': + w = np.arange(n_fft) * (2 * np.pi / n_fft * n_half) + H = np.real(fft(h, n_fft) * np.exp(1j * w)) + dp = max(H) - 1 + ds = 0 - min(H) + S = 4. / (np.sqrt(1+dp+ds) + np.sqrt(1-dp+ds)) ** 2 + H += ds + H *= S + H = np.sqrt(H, out=H) + H += 1e-10 # ensure that the log does not explode + h_minimum = _dhtm(H) + else: # method == 'homomorphic' + # zero-pad; calculate the DFT + h_temp = np.abs(fft(h, n_fft)) + # take 0.25*log(|H|**2) = 0.5*log(|H|) + h_temp += 1e-7 * h_temp[h_temp > 0].min() # don't let log blow up + np.log(h_temp, out=h_temp) + h_temp *= 0.5 + # IDFT + h_temp = ifft(h_temp).real + # multiply pointwise by the homomorphic filter + # lmin[n] = 2u[n] - d[n] + win = np.zeros(n_fft) + win[0] = 1 + stop = (len(h) + 1) // 2 + win[1:stop] = 2 + if len(h) % 2: + win[stop] = 1 + h_temp *= win + h_temp = ifft(np.exp(fft(h_temp))) + h_minimum = h_temp.real + n_out = n_half + len(h) % 2 + return h_minimum[:n_out] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_lti_conversion.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_lti_conversion.py new file mode 100644 index 0000000000000000000000000000000000000000..2adfeb0c5f65d251e3165054f1067a40ac564f0d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_lti_conversion.py @@ -0,0 +1,533 @@ +""" +ltisys -- a collection of functions to convert linear time invariant systems +from one representation to another. +""" +import numpy +import numpy as np +from numpy import (r_, eye, atleast_2d, poly, dot, + asarray, prod, zeros, array, outer) +from scipy import linalg + +from ._filter_design import tf2zpk, zpk2tf, normalize + + +__all__ = ['tf2ss', 'abcd_normalize', 'ss2tf', 'zpk2ss', 'ss2zpk', + 'cont2discrete'] + + +def tf2ss(num, den): + r"""Transfer function to state-space representation. + + Parameters + ---------- + num, den : array_like + Sequences representing the coefficients of the numerator and + denominator polynomials, in order of descending degree. The + denominator needs to be at least as long as the numerator. + + Returns + ------- + A, B, C, D : ndarray + State space representation of the system, in controller canonical + form. + + Examples + -------- + Convert the transfer function: + + .. math:: H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1} + + >>> num = [1, 3, 3] + >>> den = [1, 2, 1] + + to the state-space representation: + + .. math:: + + \dot{\textbf{x}}(t) = + \begin{bmatrix} -2 & -1 \\ 1 & 0 \end{bmatrix} \textbf{x}(t) + + \begin{bmatrix} 1 \\ 0 \end{bmatrix} \textbf{u}(t) \\ + + \textbf{y}(t) = \begin{bmatrix} 1 & 2 \end{bmatrix} \textbf{x}(t) + + \begin{bmatrix} 1 \end{bmatrix} \textbf{u}(t) + + >>> from scipy.signal import tf2ss + >>> A, B, C, D = tf2ss(num, den) + >>> A + array([[-2., -1.], + [ 1., 0.]]) + >>> B + array([[ 1.], + [ 0.]]) + >>> C + array([[ 1., 2.]]) + >>> D + array([[ 1.]]) + """ + # Controller canonical state-space representation. + # if M+1 = len(num) and K+1 = len(den) then we must have M <= K + # states are found by asserting that X(s) = U(s) / D(s) + # then Y(s) = N(s) * X(s) + # + # A, B, C, and D follow quite naturally. + # + num, den = normalize(num, den) # Strips zeros, checks arrays + nn = len(num.shape) + if nn == 1: + num = asarray([num], num.dtype) + M = num.shape[1] + K = len(den) + if M > K: + msg = "Improper transfer function. `num` is longer than `den`." + raise ValueError(msg) + if M == 0 or K == 0: # Null system + return (array([], float), array([], float), array([], float), + array([], float)) + + # pad numerator to have same number of columns has denominator + num = np.hstack((np.zeros((num.shape[0], K - M), dtype=num.dtype), num)) + + if num.shape[-1] > 0: + D = atleast_2d(num[:, 0]) + + else: + # We don't assign it an empty array because this system + # is not 'null'. It just doesn't have a non-zero D + # matrix. Thus, it should have a non-zero shape so that + # it can be operated on by functions like 'ss2tf' + D = array([[0]], float) + + if K == 1: + D = D.reshape(num.shape) + + return (zeros((1, 1)), zeros((1, D.shape[1])), + zeros((D.shape[0], 1)), D) + + frow = -array([den[1:]]) + A = r_[frow, eye(K - 2, K - 1)] + B = eye(K - 1, 1) + C = num[:, 1:] - outer(num[:, 0], den[1:]) + D = D.reshape((C.shape[0], B.shape[1])) + + return A, B, C, D + + +def _none_to_empty_2d(arg): + if arg is None: + return zeros((0, 0)) + else: + return arg + + +def _atleast_2d_or_none(arg): + if arg is not None: + return atleast_2d(arg) + + +def _shape_or_none(M): + if M is not None: + return M.shape + else: + return (None,) * 2 + + +def _choice_not_none(*args): + for arg in args: + if arg is not None: + return arg + + +def _restore(M, shape): + if M.shape == (0, 0): + return zeros(shape) + else: + if M.shape != shape: + raise ValueError("The input arrays have incompatible shapes.") + return M + + +def abcd_normalize(A=None, B=None, C=None, D=None): + """Check state-space matrices and ensure they are 2-D. + + If enough information on the system is provided, that is, enough + properly-shaped arrays are passed to the function, the missing ones + are built from this information, ensuring the correct number of + rows and columns. Otherwise a ValueError is raised. + + Parameters + ---------- + A, B, C, D : array_like, optional + State-space matrices. All of them are None (missing) by default. + See `ss2tf` for format. + + Returns + ------- + A, B, C, D : array + Properly shaped state-space matrices. + + Raises + ------ + ValueError + If not enough information on the system was provided. + + """ + A, B, C, D = map(_atleast_2d_or_none, (A, B, C, D)) + + MA, NA = _shape_or_none(A) + MB, NB = _shape_or_none(B) + MC, NC = _shape_or_none(C) + MD, ND = _shape_or_none(D) + + p = _choice_not_none(MA, MB, NC) + q = _choice_not_none(NB, ND) + r = _choice_not_none(MC, MD) + if p is None or q is None or r is None: + raise ValueError("Not enough information on the system.") + + A, B, C, D = map(_none_to_empty_2d, (A, B, C, D)) + A = _restore(A, (p, p)) + B = _restore(B, (p, q)) + C = _restore(C, (r, p)) + D = _restore(D, (r, q)) + + return A, B, C, D + + +def ss2tf(A, B, C, D, input=0): + r"""State-space to transfer function. + + A, B, C, D defines a linear state-space system with `p` inputs, + `q` outputs, and `n` state variables. + + Parameters + ---------- + A : array_like + State (or system) matrix of shape ``(n, n)`` + B : array_like + Input matrix of shape ``(n, p)`` + C : array_like + Output matrix of shape ``(q, n)`` + D : array_like + Feedthrough (or feedforward) matrix of shape ``(q, p)`` + input : int, optional + For multiple-input systems, the index of the input to use. + + Returns + ------- + num : 2-D ndarray + Numerator(s) of the resulting transfer function(s). `num` has one row + for each of the system's outputs. Each row is a sequence representation + of the numerator polynomial. + den : 1-D ndarray + Denominator of the resulting transfer function(s). `den` is a sequence + representation of the denominator polynomial. + + Examples + -------- + Convert the state-space representation: + + .. math:: + + \dot{\textbf{x}}(t) = + \begin{bmatrix} -2 & -1 \\ 1 & 0 \end{bmatrix} \textbf{x}(t) + + \begin{bmatrix} 1 \\ 0 \end{bmatrix} \textbf{u}(t) \\ + + \textbf{y}(t) = \begin{bmatrix} 1 & 2 \end{bmatrix} \textbf{x}(t) + + \begin{bmatrix} 1 \end{bmatrix} \textbf{u}(t) + + >>> A = [[-2, -1], [1, 0]] + >>> B = [[1], [0]] # 2-D column vector + >>> C = [[1, 2]] # 2-D row vector + >>> D = 1 + + to the transfer function: + + .. math:: H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1} + + >>> from scipy.signal import ss2tf + >>> ss2tf(A, B, C, D) + (array([[1., 3., 3.]]), array([ 1., 2., 1.])) + """ + # transfer function is C (sI - A)**(-1) B + D + + # Check consistency and make them all rank-2 arrays + A, B, C, D = abcd_normalize(A, B, C, D) + + nout, nin = D.shape + if input >= nin: + raise ValueError("System does not have the input specified.") + + # make SIMO from possibly MIMO system. + B = B[:, input:input + 1] + D = D[:, input:input + 1] + + try: + den = poly(A) + except ValueError: + den = 1 + + if (prod(B.shape, axis=0) == 0) and (prod(C.shape, axis=0) == 0): + num = numpy.ravel(D) + if (prod(D.shape, axis=0) == 0) and (prod(A.shape, axis=0) == 0): + den = [] + return num, den + + num_states = A.shape[0] + type_test = A[:, 0] + B[:, 0] + C[0, :] + D + 0.0 + num = numpy.empty((nout, num_states + 1), type_test.dtype) + for k in range(nout): + Ck = atleast_2d(C[k, :]) + num[k] = poly(A - dot(B, Ck)) + (D[k] - 1) * den + + return num, den + + +def zpk2ss(z, p, k): + """Zero-pole-gain representation to state-space representation + + Parameters + ---------- + z, p : sequence + Zeros and poles. + k : float + System gain. + + Returns + ------- + A, B, C, D : ndarray + State space representation of the system, in controller canonical + form. + + """ + return tf2ss(*zpk2tf(z, p, k)) + + +def ss2zpk(A, B, C, D, input=0): + """State-space representation to zero-pole-gain representation. + + A, B, C, D defines a linear state-space system with `p` inputs, + `q` outputs, and `n` state variables. + + Parameters + ---------- + A : array_like + State (or system) matrix of shape ``(n, n)`` + B : array_like + Input matrix of shape ``(n, p)`` + C : array_like + Output matrix of shape ``(q, n)`` + D : array_like + Feedthrough (or feedforward) matrix of shape ``(q, p)`` + input : int, optional + For multiple-input systems, the index of the input to use. + + Returns + ------- + z, p : sequence + Zeros and poles. + k : float + System gain. + + """ + return tf2zpk(*ss2tf(A, B, C, D, input=input)) + + +def cont2discrete(system, dt, method="zoh", alpha=None): + """ + Transform a continuous to a discrete state-space system. + + Parameters + ---------- + system : a tuple describing the system or an instance of `lti` + The following gives the number of elements in the tuple and + the interpretation: + + * 1: (instance of `lti`) + * 2: (num, den) + * 3: (zeros, poles, gain) + * 4: (A, B, C, D) + + dt : float + The discretization time step. + method : str, optional + Which method to use: + + * gbt: generalized bilinear transformation + * bilinear: Tustin's approximation ("gbt" with alpha=0.5) + * euler: Euler (or forward differencing) method ("gbt" with alpha=0) + * backward_diff: Backwards differencing ("gbt" with alpha=1.0) + * zoh: zero-order hold (default) + * foh: first-order hold (*versionadded: 1.3.0*) + * impulse: equivalent impulse response (*versionadded: 1.3.0*) + + alpha : float within [0, 1], optional + The generalized bilinear transformation weighting parameter, which + should only be specified with method="gbt", and is ignored otherwise + + Returns + ------- + sysd : tuple containing the discrete system + Based on the input type, the output will be of the form + + * (num, den, dt) for transfer function input + * (zeros, poles, gain, dt) for zeros-poles-gain input + * (A, B, C, D, dt) for state-space system input + + Notes + ----- + By default, the routine uses a Zero-Order Hold (zoh) method to perform + the transformation. Alternatively, a generalized bilinear transformation + may be used, which includes the common Tustin's bilinear approximation, + an Euler's method technique, or a backwards differencing technique. + + The Zero-Order Hold (zoh) method is based on [1]_, the generalized bilinear + approximation is based on [2]_ and [3]_, the First-Order Hold (foh) method + is based on [4]_. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models + + .. [2] http://techteach.no/publications/discretetime_signals_systems/discrete.pdf + + .. [3] G. Zhang, X. Chen, and T. Chen, Digital redesign via the generalized + bilinear transformation, Int. J. Control, vol. 82, no. 4, pp. 741-754, + 2009. + (https://www.mypolyuweb.hk/~magzhang/Research/ZCC09_IJC.pdf) + + .. [4] G. F. Franklin, J. D. Powell, and M. L. Workman, Digital control + of dynamic systems, 3rd ed. Menlo Park, Calif: Addison-Wesley, + pp. 204-206, 1998. + + Examples + -------- + We can transform a continuous state-space system to a discrete one: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import cont2discrete, lti, dlti, dstep + + Define a continuous state-space system. + + >>> A = np.array([[0, 1],[-10., -3]]) + >>> B = np.array([[0],[10.]]) + >>> C = np.array([[1., 0]]) + >>> D = np.array([[0.]]) + >>> l_system = lti(A, B, C, D) + >>> t, x = l_system.step(T=np.linspace(0, 5, 100)) + >>> fig, ax = plt.subplots() + >>> ax.plot(t, x, label='Continuous', linewidth=3) + + Transform it to a discrete state-space system using several methods. + + >>> dt = 0.1 + >>> for method in ['zoh', 'bilinear', 'euler', 'backward_diff', 'foh', 'impulse']: + ... d_system = cont2discrete((A, B, C, D), dt, method=method) + ... s, x_d = dstep(d_system) + ... ax.step(s, np.squeeze(x_d), label=method, where='post') + >>> ax.axis([t[0], t[-1], x[0], 1.4]) + >>> ax.legend(loc='best') + >>> fig.tight_layout() + >>> plt.show() + + """ + if len(system) == 1: + return system.to_discrete() + if len(system) == 2: + sysd = cont2discrete(tf2ss(system[0], system[1]), dt, method=method, + alpha=alpha) + return ss2tf(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,) + elif len(system) == 3: + sysd = cont2discrete(zpk2ss(system[0], system[1], system[2]), dt, + method=method, alpha=alpha) + return ss2zpk(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,) + elif len(system) == 4: + a, b, c, d = system + else: + raise ValueError("First argument must either be a tuple of 2 (tf), " + "3 (zpk), or 4 (ss) arrays.") + + if method == 'gbt': + if alpha is None: + raise ValueError("Alpha parameter must be specified for the " + "generalized bilinear transform (gbt) method") + elif alpha < 0 or alpha > 1: + raise ValueError("Alpha parameter must be within the interval " + "[0,1] for the gbt method") + + if method == 'gbt': + # This parameter is used repeatedly - compute once here + ima = np.eye(a.shape[0]) - alpha*dt*a + ad = linalg.solve(ima, np.eye(a.shape[0]) + (1.0-alpha)*dt*a) + bd = linalg.solve(ima, dt*b) + + # Similarly solve for the output equation matrices + cd = linalg.solve(ima.transpose(), c.transpose()) + cd = cd.transpose() + dd = d + alpha*np.dot(c, bd) + + elif method == 'bilinear' or method == 'tustin': + return cont2discrete(system, dt, method="gbt", alpha=0.5) + + elif method == 'euler' or method == 'forward_diff': + return cont2discrete(system, dt, method="gbt", alpha=0.0) + + elif method == 'backward_diff': + return cont2discrete(system, dt, method="gbt", alpha=1.0) + + elif method == 'zoh': + # Build an exponential matrix + em_upper = np.hstack((a, b)) + + # Need to stack zeros under the a and b matrices + em_lower = np.hstack((np.zeros((b.shape[1], a.shape[0])), + np.zeros((b.shape[1], b.shape[1])))) + + em = np.vstack((em_upper, em_lower)) + ms = linalg.expm(dt * em) + + # Dispose of the lower rows + ms = ms[:a.shape[0], :] + + ad = ms[:, 0:a.shape[1]] + bd = ms[:, a.shape[1]:] + + cd = c + dd = d + + elif method == 'foh': + # Size parameters for convenience + n = a.shape[0] + m = b.shape[1] + + # Build an exponential matrix similar to 'zoh' method + em_upper = linalg.block_diag(np.block([a, b]) * dt, np.eye(m)) + em_lower = zeros((m, n + 2 * m)) + em = np.block([[em_upper], [em_lower]]) + + ms = linalg.expm(em) + + # Get the three blocks from upper rows + ms11 = ms[:n, 0:n] + ms12 = ms[:n, n:n + m] + ms13 = ms[:n, n + m:] + + ad = ms11 + bd = ms12 - ms13 + ms11 @ ms13 + cd = c + dd = d + c @ ms13 + + elif method == 'impulse': + if not np.allclose(d, 0): + raise ValueError("Impulse method is only applicable" + "to strictly proper systems") + + ad = linalg.expm(a * dt) + bd = ad @ b * dt + cd = c + dd = c @ b * dt + + else: + raise ValueError("Unknown transformation method '%s'" % method) + + return ad, bd, cd, dd, dt diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_ltisys.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_ltisys.py new file mode 100644 index 0000000000000000000000000000000000000000..b0117bad7a4674e00d0ceb04e1bf3c459f63b2e7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_ltisys.py @@ -0,0 +1,3496 @@ +""" +ltisys -- a collection of classes and functions for modeling linear +time invariant systems. +""" +# +# Author: Travis Oliphant 2001 +# +# Feb 2010: Warren Weckesser +# Rewrote lsim2 and added impulse2. +# Apr 2011: Jeffrey Armstrong +# Added dlsim, dstep, dimpulse, cont2discrete +# Aug 2013: Juan Luis Cano +# Rewrote abcd_normalize. +# Jan 2015: Irvin Probst irvin DOT probst AT ensta-bretagne DOT fr +# Added pole placement +# Mar 2015: Clancy Rowley +# Rewrote lsim +# May 2015: Felix Berkenkamp +# Split lti class into subclasses +# Merged discrete systems and added dlti + +import warnings + +# np.linalg.qr fails on some tests with LinAlgError: zgeqrf returns -7 +# use scipy's qr until this is solved + +from scipy.linalg import qr as s_qr +from scipy import linalg +from scipy.interpolate import make_interp_spline +from ._filter_design import (tf2zpk, zpk2tf, normalize, freqs, freqz, freqs_zpk, + freqz_zpk) +from ._lti_conversion import (tf2ss, abcd_normalize, ss2tf, zpk2ss, ss2zpk, + cont2discrete, _atleast_2d_or_none) + +import numpy +import numpy as np +from numpy import (real, atleast_1d, squeeze, asarray, zeros, + dot, transpose, ones, linspace) +import copy + +__all__ = ['lti', 'dlti', 'TransferFunction', 'ZerosPolesGain', 'StateSpace', + 'lsim', 'impulse', 'step', 'bode', + 'freqresp', 'place_poles', 'dlsim', 'dstep', 'dimpulse', + 'dfreqresp', 'dbode'] + + +class LinearTimeInvariant: + def __new__(cls, *system, **kwargs): + """Create a new object, don't allow direct instances.""" + if cls is LinearTimeInvariant: + raise NotImplementedError('The LinearTimeInvariant class is not ' + 'meant to be used directly, use `lti` ' + 'or `dlti` instead.') + return super().__new__(cls) + + def __init__(self): + """ + Initialize the `lti` baseclass. + + The heavy lifting is done by the subclasses. + """ + super().__init__() + + self.inputs = None + self.outputs = None + self._dt = None + + @property + def dt(self): + """Return the sampling time of the system, `None` for `lti` systems.""" + return self._dt + + @property + def _dt_dict(self): + if self.dt is None: + return {} + else: + return {'dt': self.dt} + + @property + def zeros(self): + """Zeros of the system.""" + return self.to_zpk().zeros + + @property + def poles(self): + """Poles of the system.""" + return self.to_zpk().poles + + def _as_ss(self): + """Convert to `StateSpace` system, without copying. + + Returns + ------- + sys: StateSpace + The `StateSpace` system. If the class is already an instance of + `StateSpace` then this instance is returned. + """ + if isinstance(self, StateSpace): + return self + else: + return self.to_ss() + + def _as_zpk(self): + """Convert to `ZerosPolesGain` system, without copying. + + Returns + ------- + sys: ZerosPolesGain + The `ZerosPolesGain` system. If the class is already an instance of + `ZerosPolesGain` then this instance is returned. + """ + if isinstance(self, ZerosPolesGain): + return self + else: + return self.to_zpk() + + def _as_tf(self): + """Convert to `TransferFunction` system, without copying. + + Returns + ------- + sys: ZerosPolesGain + The `TransferFunction` system. If the class is already an instance of + `TransferFunction` then this instance is returned. + """ + if isinstance(self, TransferFunction): + return self + else: + return self.to_tf() + + +class lti(LinearTimeInvariant): + r""" + Continuous-time linear time invariant system base class. + + Parameters + ---------- + *system : arguments + The `lti` class can be instantiated with either 2, 3 or 4 arguments. + The following gives the number of arguments and the corresponding + continuous-time subclass that is created: + + * 2: `TransferFunction`: (numerator, denominator) + * 3: `ZerosPolesGain`: (zeros, poles, gain) + * 4: `StateSpace`: (A, B, C, D) + + Each argument can be an array or a sequence. + + See Also + -------- + ZerosPolesGain, StateSpace, TransferFunction, dlti + + Notes + ----- + `lti` instances do not exist directly. Instead, `lti` creates an instance + of one of its subclasses: `StateSpace`, `TransferFunction` or + `ZerosPolesGain`. + + If (numerator, denominator) is passed in for ``*system``, coefficients for + both the numerator and denominator should be specified in descending + exponent order (e.g., ``s^2 + 3s + 5`` would be represented as ``[1, 3, + 5]``). + + Changing the value of properties that are not directly part of the current + system representation (such as the `zeros` of a `StateSpace` system) is + very inefficient and may lead to numerical inaccuracies. It is better to + convert to the specific system representation first. For example, call + ``sys = sys.to_zpk()`` before accessing/changing the zeros, poles or gain. + + Examples + -------- + >>> from scipy import signal + + >>> signal.lti(1, 2, 3, 4) + StateSpaceContinuous( + array([[1]]), + array([[2]]), + array([[3]]), + array([[4]]), + dt: None + ) + + Construct the transfer function + :math:`H(s) = \frac{5(s - 1)(s - 2)}{(s - 3)(s - 4)}`: + + >>> signal.lti([1, 2], [3, 4], 5) + ZerosPolesGainContinuous( + array([1, 2]), + array([3, 4]), + 5, + dt: None + ) + + Construct the transfer function :math:`H(s) = \frac{3s + 4}{1s + 2}`: + + >>> signal.lti([3, 4], [1, 2]) + TransferFunctionContinuous( + array([3., 4.]), + array([1., 2.]), + dt: None + ) + + """ + def __new__(cls, *system): + """Create an instance of the appropriate subclass.""" + if cls is lti: + N = len(system) + if N == 2: + return TransferFunctionContinuous.__new__( + TransferFunctionContinuous, *system) + elif N == 3: + return ZerosPolesGainContinuous.__new__( + ZerosPolesGainContinuous, *system) + elif N == 4: + return StateSpaceContinuous.__new__(StateSpaceContinuous, + *system) + else: + raise ValueError("`system` needs to be an instance of `lti` " + "or have 2, 3 or 4 arguments.") + # __new__ was called from a subclass, let it call its own functions + return super().__new__(cls) + + def __init__(self, *system): + """ + Initialize the `lti` baseclass. + + The heavy lifting is done by the subclasses. + """ + super().__init__(*system) + + def impulse(self, X0=None, T=None, N=None): + """ + Return the impulse response of a continuous-time system. + See `impulse` for details. + """ + return impulse(self, X0=X0, T=T, N=N) + + def step(self, X0=None, T=None, N=None): + """ + Return the step response of a continuous-time system. + See `step` for details. + """ + return step(self, X0=X0, T=T, N=N) + + def output(self, U, T, X0=None): + """ + Return the response of a continuous-time system to input `U`. + See `lsim` for details. + """ + return lsim(self, U, T, X0=X0) + + def bode(self, w=None, n=100): + """ + Calculate Bode magnitude and phase data of a continuous-time system. + + Returns a 3-tuple containing arrays of frequencies [rad/s], magnitude + [dB] and phase [deg]. See `bode` for details. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> sys = signal.TransferFunction([1], [1, 1]) + >>> w, mag, phase = sys.bode() + + >>> plt.figure() + >>> plt.semilogx(w, mag) # Bode magnitude plot + >>> plt.figure() + >>> plt.semilogx(w, phase) # Bode phase plot + >>> plt.show() + + """ + return bode(self, w=w, n=n) + + def freqresp(self, w=None, n=10000): + """ + Calculate the frequency response of a continuous-time system. + + Returns a 2-tuple containing arrays of frequencies [rad/s] and + complex magnitude. + See `freqresp` for details. + """ + return freqresp(self, w=w, n=n) + + def to_discrete(self, dt, method='zoh', alpha=None): + """Return a discretized version of the current system. + + Parameters: See `cont2discrete` for details. + + Returns + ------- + sys: instance of `dlti` + """ + raise NotImplementedError('to_discrete is not implemented for this ' + 'system class.') + + +class dlti(LinearTimeInvariant): + r""" + Discrete-time linear time invariant system base class. + + Parameters + ---------- + *system: arguments + The `dlti` class can be instantiated with either 2, 3 or 4 arguments. + The following gives the number of arguments and the corresponding + discrete-time subclass that is created: + + * 2: `TransferFunction`: (numerator, denominator) + * 3: `ZerosPolesGain`: (zeros, poles, gain) + * 4: `StateSpace`: (A, B, C, D) + + Each argument can be an array or a sequence. + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to ``True`` + (unspecified sampling time). Must be specified as a keyword argument, + for example, ``dt=0.1``. + + See Also + -------- + ZerosPolesGain, StateSpace, TransferFunction, lti + + Notes + ----- + `dlti` instances do not exist directly. Instead, `dlti` creates an instance + of one of its subclasses: `StateSpace`, `TransferFunction` or + `ZerosPolesGain`. + + Changing the value of properties that are not directly part of the current + system representation (such as the `zeros` of a `StateSpace` system) is + very inefficient and may lead to numerical inaccuracies. It is better to + convert to the specific system representation first. For example, call + ``sys = sys.to_zpk()`` before accessing/changing the zeros, poles or gain. + + If (numerator, denominator) is passed in for ``*system``, coefficients for + both the numerator and denominator should be specified in descending + exponent order (e.g., ``z^2 + 3z + 5`` would be represented as ``[1, 3, + 5]``). + + .. versionadded:: 0.18.0 + + Examples + -------- + >>> from scipy import signal + + >>> signal.dlti(1, 2, 3, 4) + StateSpaceDiscrete( + array([[1]]), + array([[2]]), + array([[3]]), + array([[4]]), + dt: True + ) + + >>> signal.dlti(1, 2, 3, 4, dt=0.1) + StateSpaceDiscrete( + array([[1]]), + array([[2]]), + array([[3]]), + array([[4]]), + dt: 0.1 + ) + + Construct the transfer function + :math:`H(z) = \frac{5(z - 1)(z - 2)}{(z - 3)(z - 4)}` with a sampling time + of 0.1 seconds: + + >>> signal.dlti([1, 2], [3, 4], 5, dt=0.1) + ZerosPolesGainDiscrete( + array([1, 2]), + array([3, 4]), + 5, + dt: 0.1 + ) + + Construct the transfer function :math:`H(z) = \frac{3z + 4}{1z + 2}` with + a sampling time of 0.1 seconds: + + >>> signal.dlti([3, 4], [1, 2], dt=0.1) + TransferFunctionDiscrete( + array([3., 4.]), + array([1., 2.]), + dt: 0.1 + ) + + """ + def __new__(cls, *system, **kwargs): + """Create an instance of the appropriate subclass.""" + if cls is dlti: + N = len(system) + if N == 2: + return TransferFunctionDiscrete.__new__( + TransferFunctionDiscrete, *system, **kwargs) + elif N == 3: + return ZerosPolesGainDiscrete.__new__(ZerosPolesGainDiscrete, + *system, **kwargs) + elif N == 4: + return StateSpaceDiscrete.__new__(StateSpaceDiscrete, *system, + **kwargs) + else: + raise ValueError("`system` needs to be an instance of `dlti` " + "or have 2, 3 or 4 arguments.") + # __new__ was called from a subclass, let it call its own functions + return super().__new__(cls) + + def __init__(self, *system, **kwargs): + """ + Initialize the `lti` baseclass. + + The heavy lifting is done by the subclasses. + """ + dt = kwargs.pop('dt', True) + super().__init__(*system, **kwargs) + + self.dt = dt + + @property + def dt(self): + """Return the sampling time of the system.""" + return self._dt + + @dt.setter + def dt(self, dt): + self._dt = dt + + def impulse(self, x0=None, t=None, n=None): + """ + Return the impulse response of the discrete-time `dlti` system. + See `dimpulse` for details. + """ + return dimpulse(self, x0=x0, t=t, n=n) + + def step(self, x0=None, t=None, n=None): + """ + Return the step response of the discrete-time `dlti` system. + See `dstep` for details. + """ + return dstep(self, x0=x0, t=t, n=n) + + def output(self, u, t, x0=None): + """ + Return the response of the discrete-time system to input `u`. + See `dlsim` for details. + """ + return dlsim(self, u, t, x0=x0) + + def bode(self, w=None, n=100): + r""" + Calculate Bode magnitude and phase data of a discrete-time system. + + Returns a 3-tuple containing arrays of frequencies [rad/s], magnitude + [dB] and phase [deg]. See `dbode` for details. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + Construct the transfer function :math:`H(z) = \frac{1}{z^2 + 2z + 3}` + with sampling time 0.5s: + + >>> sys = signal.TransferFunction([1], [1, 2, 3], dt=0.5) + + Equivalent: signal.dbode(sys) + + >>> w, mag, phase = sys.bode() + + >>> plt.figure() + >>> plt.semilogx(w, mag) # Bode magnitude plot + >>> plt.figure() + >>> plt.semilogx(w, phase) # Bode phase plot + >>> plt.show() + + """ + return dbode(self, w=w, n=n) + + def freqresp(self, w=None, n=10000, whole=False): + """ + Calculate the frequency response of a discrete-time system. + + Returns a 2-tuple containing arrays of frequencies [rad/s] and + complex magnitude. + See `dfreqresp` for details. + + """ + return dfreqresp(self, w=w, n=n, whole=whole) + + +class TransferFunction(LinearTimeInvariant): + r"""Linear Time Invariant system class in transfer function form. + + Represents the system as the continuous-time transfer function + :math:`H(s)=\sum_{i=0}^N b[N-i] s^i / \sum_{j=0}^M a[M-j] s^j` or the + discrete-time transfer function + :math:`H(z)=\sum_{i=0}^N b[N-i] z^i / \sum_{j=0}^M a[M-j] z^j`, where + :math:`b` are elements of the numerator `num`, :math:`a` are elements of + the denominator `den`, and ``N == len(b) - 1``, ``M == len(a) - 1``. + `TransferFunction` systems inherit additional + functionality from the `lti`, respectively the `dlti` classes, depending on + which system representation is used. + + Parameters + ---------- + *system: arguments + The `TransferFunction` class can be instantiated with 1 or 2 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `lti` or `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 2: array_like: (numerator, denominator) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `None` + (continuous-time). Must be specified as a keyword argument, for + example, ``dt=0.1``. + + See Also + -------- + ZerosPolesGain, StateSpace, lti, dlti + tf2ss, tf2zpk, tf2sos + + Notes + ----- + Changing the value of properties that are not part of the + `TransferFunction` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. It is better to convert to the specific system + representation first. For example, call ``sys = sys.to_ss()`` before + accessing/changing the A, B, C, D system matrices. + + If (numerator, denominator) is passed in for ``*system``, coefficients + for both the numerator and denominator should be specified in descending + exponent order (e.g. ``s^2 + 3s + 5`` or ``z^2 + 3z + 5`` would be + represented as ``[1, 3, 5]``) + + Examples + -------- + Construct the transfer function + :math:`H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1}`: + + >>> from scipy import signal + + >>> num = [1, 3, 3] + >>> den = [1, 2, 1] + + >>> signal.TransferFunction(num, den) + TransferFunctionContinuous( + array([1., 3., 3.]), + array([1., 2., 1.]), + dt: None + ) + + Construct the transfer function + :math:`H(z) = \frac{z^2 + 3z + 3}{z^2 + 2z + 1}` with a sampling time of + 0.1 seconds: + + >>> signal.TransferFunction(num, den, dt=0.1) + TransferFunctionDiscrete( + array([1., 3., 3.]), + array([1., 2., 1.]), + dt: 0.1 + ) + + """ + def __new__(cls, *system, **kwargs): + """Handle object conversion if input is an instance of lti.""" + if len(system) == 1 and isinstance(system[0], LinearTimeInvariant): + return system[0].to_tf() + + # Choose whether to inherit from `lti` or from `dlti` + if cls is TransferFunction: + if kwargs.get('dt') is None: + return TransferFunctionContinuous.__new__( + TransferFunctionContinuous, + *system, + **kwargs) + else: + return TransferFunctionDiscrete.__new__( + TransferFunctionDiscrete, + *system, + **kwargs) + + # No special conversion needed + return super().__new__(cls) + + def __init__(self, *system, **kwargs): + """Initialize the state space LTI system.""" + # Conversion of lti instances is handled in __new__ + if isinstance(system[0], LinearTimeInvariant): + return + + # Remove system arguments, not needed by parents anymore + super().__init__(**kwargs) + + self._num = None + self._den = None + + self.num, self.den = normalize(*system) + + def __repr__(self): + """Return representation of the system's transfer function""" + return '{}(\n{},\n{},\ndt: {}\n)'.format( + self.__class__.__name__, + repr(self.num), + repr(self.den), + repr(self.dt), + ) + + @property + def num(self): + """Numerator of the `TransferFunction` system.""" + return self._num + + @num.setter + def num(self, num): + self._num = atleast_1d(num) + + # Update dimensions + if len(self.num.shape) > 1: + self.outputs, self.inputs = self.num.shape + else: + self.outputs = 1 + self.inputs = 1 + + @property + def den(self): + """Denominator of the `TransferFunction` system.""" + return self._den + + @den.setter + def den(self, den): + self._den = atleast_1d(den) + + def _copy(self, system): + """ + Copy the parameters of another `TransferFunction` object + + Parameters + ---------- + system : `TransferFunction` + The `StateSpace` system that is to be copied + + """ + self.num = system.num + self.den = system.den + + def to_tf(self): + """ + Return a copy of the current `TransferFunction` system. + + Returns + ------- + sys : instance of `TransferFunction` + The current system (copy) + + """ + return copy.deepcopy(self) + + def to_zpk(self): + """ + Convert system representation to `ZerosPolesGain`. + + Returns + ------- + sys : instance of `ZerosPolesGain` + Zeros, poles, gain representation of the current system + + """ + return ZerosPolesGain(*tf2zpk(self.num, self.den), + **self._dt_dict) + + def to_ss(self): + """ + Convert system representation to `StateSpace`. + + Returns + ------- + sys : instance of `StateSpace` + State space model of the current system + + """ + return StateSpace(*tf2ss(self.num, self.den), + **self._dt_dict) + + @staticmethod + def _z_to_zinv(num, den): + """Change a transfer function from the variable `z` to `z**-1`. + + Parameters + ---------- + num, den: 1d array_like + Sequences representing the coefficients of the numerator and + denominator polynomials, in order of descending degree of 'z'. + That is, ``5z**2 + 3z + 2`` is presented as ``[5, 3, 2]``. + + Returns + ------- + num, den: 1d array_like + Sequences representing the coefficients of the numerator and + denominator polynomials, in order of ascending degree of 'z**-1'. + That is, ``5 + 3 z**-1 + 2 z**-2`` is presented as ``[5, 3, 2]``. + """ + diff = len(num) - len(den) + if diff > 0: + den = np.hstack((np.zeros(diff), den)) + elif diff < 0: + num = np.hstack((np.zeros(-diff), num)) + return num, den + + @staticmethod + def _zinv_to_z(num, den): + """Change a transfer function from the variable `z` to `z**-1`. + + Parameters + ---------- + num, den: 1d array_like + Sequences representing the coefficients of the numerator and + denominator polynomials, in order of ascending degree of 'z**-1'. + That is, ``5 + 3 z**-1 + 2 z**-2`` is presented as ``[5, 3, 2]``. + + Returns + ------- + num, den: 1d array_like + Sequences representing the coefficients of the numerator and + denominator polynomials, in order of descending degree of 'z'. + That is, ``5z**2 + 3z + 2`` is presented as ``[5, 3, 2]``. + """ + diff = len(num) - len(den) + if diff > 0: + den = np.hstack((den, np.zeros(diff))) + elif diff < 0: + num = np.hstack((num, np.zeros(-diff))) + return num, den + + +class TransferFunctionContinuous(TransferFunction, lti): + r""" + Continuous-time Linear Time Invariant system in transfer function form. + + Represents the system as the transfer function + :math:`H(s)=\sum_{i=0}^N b[N-i] s^i / \sum_{j=0}^M a[M-j] s^j`, where + :math:`b` are elements of the numerator `num`, :math:`a` are elements of + the denominator `den`, and ``N == len(b) - 1``, ``M == len(a) - 1``. + Continuous-time `TransferFunction` systems inherit additional + functionality from the `lti` class. + + Parameters + ---------- + *system: arguments + The `TransferFunction` class can be instantiated with 1 or 2 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `lti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 2: array_like: (numerator, denominator) + + See Also + -------- + ZerosPolesGain, StateSpace, lti + tf2ss, tf2zpk, tf2sos + + Notes + ----- + Changing the value of properties that are not part of the + `TransferFunction` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. It is better to convert to the specific system + representation first. For example, call ``sys = sys.to_ss()`` before + accessing/changing the A, B, C, D system matrices. + + If (numerator, denominator) is passed in for ``*system``, coefficients + for both the numerator and denominator should be specified in descending + exponent order (e.g. ``s^2 + 3s + 5`` would be represented as + ``[1, 3, 5]``) + + Examples + -------- + Construct the transfer function + :math:`H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1}`: + + >>> from scipy import signal + + >>> num = [1, 3, 3] + >>> den = [1, 2, 1] + + >>> signal.TransferFunction(num, den) + TransferFunctionContinuous( + array([ 1., 3., 3.]), + array([ 1., 2., 1.]), + dt: None + ) + + """ + + def to_discrete(self, dt, method='zoh', alpha=None): + """ + Returns the discretized `TransferFunction` system. + + Parameters: See `cont2discrete` for details. + + Returns + ------- + sys: instance of `dlti` and `StateSpace` + """ + return TransferFunction(*cont2discrete((self.num, self.den), + dt, + method=method, + alpha=alpha)[:-1], + dt=dt) + + +class TransferFunctionDiscrete(TransferFunction, dlti): + r""" + Discrete-time Linear Time Invariant system in transfer function form. + + Represents the system as the transfer function + :math:`H(z)=\sum_{i=0}^N b[N-i] z^i / \sum_{j=0}^M a[M-j] z^j`, where + :math:`b` are elements of the numerator `num`, :math:`a` are elements of + the denominator `den`, and ``N == len(b) - 1``, ``M == len(a) - 1``. + Discrete-time `TransferFunction` systems inherit additional functionality + from the `dlti` class. + + Parameters + ---------- + *system: arguments + The `TransferFunction` class can be instantiated with 1 or 2 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 2: array_like: (numerator, denominator) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `True` + (unspecified sampling time). Must be specified as a keyword argument, + for example, ``dt=0.1``. + + See Also + -------- + ZerosPolesGain, StateSpace, dlti + tf2ss, tf2zpk, tf2sos + + Notes + ----- + Changing the value of properties that are not part of the + `TransferFunction` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. + + If (numerator, denominator) is passed in for ``*system``, coefficients + for both the numerator and denominator should be specified in descending + exponent order (e.g., ``z^2 + 3z + 5`` would be represented as + ``[1, 3, 5]``). + + Examples + -------- + Construct the transfer function + :math:`H(z) = \frac{z^2 + 3z + 3}{z^2 + 2z + 1}` with a sampling time of + 0.5 seconds: + + >>> from scipy import signal + + >>> num = [1, 3, 3] + >>> den = [1, 2, 1] + + >>> signal.TransferFunction(num, den, dt=0.5) + TransferFunctionDiscrete( + array([ 1., 3., 3.]), + array([ 1., 2., 1.]), + dt: 0.5 + ) + + """ + pass + + +class ZerosPolesGain(LinearTimeInvariant): + r""" + Linear Time Invariant system class in zeros, poles, gain form. + + Represents the system as the continuous- or discrete-time transfer function + :math:`H(s)=k \prod_i (s - z[i]) / \prod_j (s - p[j])`, where :math:`k` is + the `gain`, :math:`z` are the `zeros` and :math:`p` are the `poles`. + `ZerosPolesGain` systems inherit additional functionality from the `lti`, + respectively the `dlti` classes, depending on which system representation + is used. + + Parameters + ---------- + *system : arguments + The `ZerosPolesGain` class can be instantiated with 1 or 3 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `lti` or `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 3: array_like: (zeros, poles, gain) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `None` + (continuous-time). Must be specified as a keyword argument, for + example, ``dt=0.1``. + + + See Also + -------- + TransferFunction, StateSpace, lti, dlti + zpk2ss, zpk2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `ZerosPolesGain` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. It is better to convert to the specific system + representation first. For example, call ``sys = sys.to_ss()`` before + accessing/changing the A, B, C, D system matrices. + + Examples + -------- + Construct the transfer function + :math:`H(s) = \frac{5(s - 1)(s - 2)}{(s - 3)(s - 4)}`: + + >>> from scipy import signal + + >>> signal.ZerosPolesGain([1, 2], [3, 4], 5) + ZerosPolesGainContinuous( + array([1, 2]), + array([3, 4]), + 5, + dt: None + ) + + Construct the transfer function + :math:`H(z) = \frac{5(z - 1)(z - 2)}{(z - 3)(z - 4)}` with a sampling time + of 0.1 seconds: + + >>> signal.ZerosPolesGain([1, 2], [3, 4], 5, dt=0.1) + ZerosPolesGainDiscrete( + array([1, 2]), + array([3, 4]), + 5, + dt: 0.1 + ) + + """ + def __new__(cls, *system, **kwargs): + """Handle object conversion if input is an instance of `lti`""" + if len(system) == 1 and isinstance(system[0], LinearTimeInvariant): + return system[0].to_zpk() + + # Choose whether to inherit from `lti` or from `dlti` + if cls is ZerosPolesGain: + if kwargs.get('dt') is None: + return ZerosPolesGainContinuous.__new__( + ZerosPolesGainContinuous, + *system, + **kwargs) + else: + return ZerosPolesGainDiscrete.__new__( + ZerosPolesGainDiscrete, + *system, + **kwargs + ) + + # No special conversion needed + return super().__new__(cls) + + def __init__(self, *system, **kwargs): + """Initialize the zeros, poles, gain system.""" + # Conversion of lti instances is handled in __new__ + if isinstance(system[0], LinearTimeInvariant): + return + + super().__init__(**kwargs) + + self._zeros = None + self._poles = None + self._gain = None + + self.zeros, self.poles, self.gain = system + + def __repr__(self): + """Return representation of the `ZerosPolesGain` system.""" + return '{}(\n{},\n{},\n{},\ndt: {}\n)'.format( + self.__class__.__name__, + repr(self.zeros), + repr(self.poles), + repr(self.gain), + repr(self.dt), + ) + + @property + def zeros(self): + """Zeros of the `ZerosPolesGain` system.""" + return self._zeros + + @zeros.setter + def zeros(self, zeros): + self._zeros = atleast_1d(zeros) + + # Update dimensions + if len(self.zeros.shape) > 1: + self.outputs, self.inputs = self.zeros.shape + else: + self.outputs = 1 + self.inputs = 1 + + @property + def poles(self): + """Poles of the `ZerosPolesGain` system.""" + return self._poles + + @poles.setter + def poles(self, poles): + self._poles = atleast_1d(poles) + + @property + def gain(self): + """Gain of the `ZerosPolesGain` system.""" + return self._gain + + @gain.setter + def gain(self, gain): + self._gain = gain + + def _copy(self, system): + """ + Copy the parameters of another `ZerosPolesGain` system. + + Parameters + ---------- + system : instance of `ZerosPolesGain` + The zeros, poles gain system that is to be copied + + """ + self.poles = system.poles + self.zeros = system.zeros + self.gain = system.gain + + def to_tf(self): + """ + Convert system representation to `TransferFunction`. + + Returns + ------- + sys : instance of `TransferFunction` + Transfer function of the current system + + """ + return TransferFunction(*zpk2tf(self.zeros, self.poles, self.gain), + **self._dt_dict) + + def to_zpk(self): + """ + Return a copy of the current 'ZerosPolesGain' system. + + Returns + ------- + sys : instance of `ZerosPolesGain` + The current system (copy) + + """ + return copy.deepcopy(self) + + def to_ss(self): + """ + Convert system representation to `StateSpace`. + + Returns + ------- + sys : instance of `StateSpace` + State space model of the current system + + """ + return StateSpace(*zpk2ss(self.zeros, self.poles, self.gain), + **self._dt_dict) + + +class ZerosPolesGainContinuous(ZerosPolesGain, lti): + r""" + Continuous-time Linear Time Invariant system in zeros, poles, gain form. + + Represents the system as the continuous time transfer function + :math:`H(s)=k \prod_i (s - z[i]) / \prod_j (s - p[j])`, where :math:`k` is + the `gain`, :math:`z` are the `zeros` and :math:`p` are the `poles`. + Continuous-time `ZerosPolesGain` systems inherit additional functionality + from the `lti` class. + + Parameters + ---------- + *system : arguments + The `ZerosPolesGain` class can be instantiated with 1 or 3 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `lti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 3: array_like: (zeros, poles, gain) + + See Also + -------- + TransferFunction, StateSpace, lti + zpk2ss, zpk2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `ZerosPolesGain` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. It is better to convert to the specific system + representation first. For example, call ``sys = sys.to_ss()`` before + accessing/changing the A, B, C, D system matrices. + + Examples + -------- + Construct the transfer function + :math:`H(s)=\frac{5(s - 1)(s - 2)}{(s - 3)(s - 4)}`: + + >>> from scipy import signal + + >>> signal.ZerosPolesGain([1, 2], [3, 4], 5) + ZerosPolesGainContinuous( + array([1, 2]), + array([3, 4]), + 5, + dt: None + ) + + """ + + def to_discrete(self, dt, method='zoh', alpha=None): + """ + Returns the discretized `ZerosPolesGain` system. + + Parameters: See `cont2discrete` for details. + + Returns + ------- + sys: instance of `dlti` and `ZerosPolesGain` + """ + return ZerosPolesGain( + *cont2discrete((self.zeros, self.poles, self.gain), + dt, + method=method, + alpha=alpha)[:-1], + dt=dt) + + +class ZerosPolesGainDiscrete(ZerosPolesGain, dlti): + r""" + Discrete-time Linear Time Invariant system in zeros, poles, gain form. + + Represents the system as the discrete-time transfer function + :math:`H(z)=k \prod_i (z - q[i]) / \prod_j (z - p[j])`, where :math:`k` is + the `gain`, :math:`q` are the `zeros` and :math:`p` are the `poles`. + Discrete-time `ZerosPolesGain` systems inherit additional functionality + from the `dlti` class. + + Parameters + ---------- + *system : arguments + The `ZerosPolesGain` class can be instantiated with 1 or 3 + arguments. The following gives the number of input arguments and their + interpretation: + + * 1: `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 3: array_like: (zeros, poles, gain) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `True` + (unspecified sampling time). Must be specified as a keyword argument, + for example, ``dt=0.1``. + + See Also + -------- + TransferFunction, StateSpace, dlti + zpk2ss, zpk2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `ZerosPolesGain` system representation (such as the `A`, `B`, `C`, `D` + state-space matrices) is very inefficient and may lead to numerical + inaccuracies. It is better to convert to the specific system + representation first. For example, call ``sys = sys.to_ss()`` before + accessing/changing the A, B, C, D system matrices. + + Examples + -------- + Construct the transfer function + :math:`H(s) = \frac{5(s - 1)(s - 2)}{(s - 3)(s - 4)}`: + + >>> from scipy import signal + + >>> signal.ZerosPolesGain([1, 2], [3, 4], 5) + ZerosPolesGainContinuous( + array([1, 2]), + array([3, 4]), + 5, + dt: None + ) + + Construct the transfer function + :math:`H(z) = \frac{5(z - 1)(z - 2)}{(z - 3)(z - 4)}` with a sampling time + of 0.1 seconds: + + >>> signal.ZerosPolesGain([1, 2], [3, 4], 5, dt=0.1) + ZerosPolesGainDiscrete( + array([1, 2]), + array([3, 4]), + 5, + dt: 0.1 + ) + + """ + pass + + +class StateSpace(LinearTimeInvariant): + r""" + Linear Time Invariant system in state-space form. + + Represents the system as the continuous-time, first order differential + equation :math:`\dot{x} = A x + B u` or the discrete-time difference + equation :math:`x[k+1] = A x[k] + B u[k]`. `StateSpace` systems + inherit additional functionality from the `lti`, respectively the `dlti` + classes, depending on which system representation is used. + + Parameters + ---------- + *system: arguments + The `StateSpace` class can be instantiated with 1 or 4 arguments. + The following gives the number of input arguments and their + interpretation: + + * 1: `lti` or `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 4: array_like: (A, B, C, D) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `None` + (continuous-time). Must be specified as a keyword argument, for + example, ``dt=0.1``. + + See Also + -------- + TransferFunction, ZerosPolesGain, lti, dlti + ss2zpk, ss2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `StateSpace` system representation (such as `zeros` or `poles`) is very + inefficient and may lead to numerical inaccuracies. It is better to + convert to the specific system representation first. For example, call + ``sys = sys.to_zpk()`` before accessing/changing the zeros, poles or gain. + + Examples + -------- + >>> from scipy import signal + >>> import numpy as np + >>> a = np.array([[0, 1], [0, 0]]) + >>> b = np.array([[0], [1]]) + >>> c = np.array([[1, 0]]) + >>> d = np.array([[0]]) + + >>> sys = signal.StateSpace(a, b, c, d) + >>> print(sys) + StateSpaceContinuous( + array([[0, 1], + [0, 0]]), + array([[0], + [1]]), + array([[1, 0]]), + array([[0]]), + dt: None + ) + + >>> sys.to_discrete(0.1) + StateSpaceDiscrete( + array([[1. , 0.1], + [0. , 1. ]]), + array([[0.005], + [0.1 ]]), + array([[1, 0]]), + array([[0]]), + dt: 0.1 + ) + + >>> a = np.array([[1, 0.1], [0, 1]]) + >>> b = np.array([[0.005], [0.1]]) + + >>> signal.StateSpace(a, b, c, d, dt=0.1) + StateSpaceDiscrete( + array([[1. , 0.1], + [0. , 1. ]]), + array([[0.005], + [0.1 ]]), + array([[1, 0]]), + array([[0]]), + dt: 0.1 + ) + + """ + + # Override NumPy binary operations and ufuncs + __array_priority__ = 100.0 + __array_ufunc__ = None + + def __new__(cls, *system, **kwargs): + """Create new StateSpace object and settle inheritance.""" + # Handle object conversion if input is an instance of `lti` + if len(system) == 1 and isinstance(system[0], LinearTimeInvariant): + return system[0].to_ss() + + # Choose whether to inherit from `lti` or from `dlti` + if cls is StateSpace: + if kwargs.get('dt') is None: + return StateSpaceContinuous.__new__(StateSpaceContinuous, + *system, **kwargs) + else: + return StateSpaceDiscrete.__new__(StateSpaceDiscrete, + *system, **kwargs) + + # No special conversion needed + return super().__new__(cls) + + def __init__(self, *system, **kwargs): + """Initialize the state space lti/dlti system.""" + # Conversion of lti instances is handled in __new__ + if isinstance(system[0], LinearTimeInvariant): + return + + # Remove system arguments, not needed by parents anymore + super().__init__(**kwargs) + + self._A = None + self._B = None + self._C = None + self._D = None + + self.A, self.B, self.C, self.D = abcd_normalize(*system) + + def __repr__(self): + """Return representation of the `StateSpace` system.""" + return '{}(\n{},\n{},\n{},\n{},\ndt: {}\n)'.format( + self.__class__.__name__, + repr(self.A), + repr(self.B), + repr(self.C), + repr(self.D), + repr(self.dt), + ) + + def _check_binop_other(self, other): + return isinstance(other, (StateSpace, np.ndarray, float, complex, + np.number, int)) + + def __mul__(self, other): + """ + Post-multiply another system or a scalar + + Handles multiplication of systems in the sense of a frequency domain + multiplication. That means, given two systems E1(s) and E2(s), their + multiplication, H(s) = E1(s) * E2(s), means that applying H(s) to U(s) + is equivalent to first applying E2(s), and then E1(s). + + Notes + ----- + For SISO systems the order of system application does not matter. + However, for MIMO systems, where the two systems are matrices, the + order above ensures standard Matrix multiplication rules apply. + """ + if not self._check_binop_other(other): + return NotImplemented + + if isinstance(other, StateSpace): + # Disallow mix of discrete and continuous systems. + if type(other) is not type(self): + return NotImplemented + + if self.dt != other.dt: + raise TypeError('Cannot multiply systems with different `dt`.') + + n1 = self.A.shape[0] + n2 = other.A.shape[0] + + # Interconnection of systems + # x1' = A1 x1 + B1 u1 + # y1 = C1 x1 + D1 u1 + # x2' = A2 x2 + B2 y1 + # y2 = C2 x2 + D2 y1 + # + # Plugging in with u1 = y2 yields + # [x1'] [A1 B1*C2 ] [x1] [B1*D2] + # [x2'] = [0 A2 ] [x2] + [B2 ] u2 + # [x1] + # y2 = [C1 D1*C2] [x2] + D1*D2 u2 + a = np.vstack((np.hstack((self.A, np.dot(self.B, other.C))), + np.hstack((zeros((n2, n1)), other.A)))) + b = np.vstack((np.dot(self.B, other.D), other.B)) + c = np.hstack((self.C, np.dot(self.D, other.C))) + d = np.dot(self.D, other.D) + else: + # Assume that other is a scalar / matrix + # For post multiplication the input gets scaled + a = self.A + b = np.dot(self.B, other) + c = self.C + d = np.dot(self.D, other) + + common_dtype = np.result_type(a.dtype, b.dtype, c.dtype, d.dtype) + return StateSpace(np.asarray(a, dtype=common_dtype), + np.asarray(b, dtype=common_dtype), + np.asarray(c, dtype=common_dtype), + np.asarray(d, dtype=common_dtype), + **self._dt_dict) + + def __rmul__(self, other): + """Pre-multiply a scalar or matrix (but not StateSpace)""" + if not self._check_binop_other(other) or isinstance(other, StateSpace): + return NotImplemented + + # For pre-multiplication only the output gets scaled + a = self.A + b = self.B + c = np.dot(other, self.C) + d = np.dot(other, self.D) + + common_dtype = np.result_type(a.dtype, b.dtype, c.dtype, d.dtype) + return StateSpace(np.asarray(a, dtype=common_dtype), + np.asarray(b, dtype=common_dtype), + np.asarray(c, dtype=common_dtype), + np.asarray(d, dtype=common_dtype), + **self._dt_dict) + + def __neg__(self): + """Negate the system (equivalent to pre-multiplying by -1).""" + return StateSpace(self.A, self.B, -self.C, -self.D, **self._dt_dict) + + def __add__(self, other): + """ + Adds two systems in the sense of frequency domain addition. + """ + if not self._check_binop_other(other): + return NotImplemented + + if isinstance(other, StateSpace): + # Disallow mix of discrete and continuous systems. + if type(other) is not type(self): + raise TypeError(f'Cannot add {type(self)} and {type(other)}') + + if self.dt != other.dt: + raise TypeError('Cannot add systems with different `dt`.') + # Interconnection of systems + # x1' = A1 x1 + B1 u + # y1 = C1 x1 + D1 u + # x2' = A2 x2 + B2 u + # y2 = C2 x2 + D2 u + # y = y1 + y2 + # + # Plugging in yields + # [x1'] [A1 0 ] [x1] [B1] + # [x2'] = [0 A2] [x2] + [B2] u + # [x1] + # y = [C1 C2] [x2] + [D1 + D2] u + a = linalg.block_diag(self.A, other.A) + b = np.vstack((self.B, other.B)) + c = np.hstack((self.C, other.C)) + d = self.D + other.D + else: + other = np.atleast_2d(other) + if self.D.shape == other.shape: + # A scalar/matrix is really just a static system (A=0, B=0, C=0) + a = self.A + b = self.B + c = self.C + d = self.D + other + else: + raise ValueError("Cannot add systems with incompatible " + f"dimensions ({self.D.shape} and {other.shape})") + + common_dtype = np.result_type(a.dtype, b.dtype, c.dtype, d.dtype) + return StateSpace(np.asarray(a, dtype=common_dtype), + np.asarray(b, dtype=common_dtype), + np.asarray(c, dtype=common_dtype), + np.asarray(d, dtype=common_dtype), + **self._dt_dict) + + def __sub__(self, other): + if not self._check_binop_other(other): + return NotImplemented + + return self.__add__(-other) + + def __radd__(self, other): + if not self._check_binop_other(other): + return NotImplemented + + return self.__add__(other) + + def __rsub__(self, other): + if not self._check_binop_other(other): + return NotImplemented + + return (-self).__add__(other) + + def __truediv__(self, other): + """ + Divide by a scalar + """ + # Division by non-StateSpace scalars + if not self._check_binop_other(other) or isinstance(other, StateSpace): + return NotImplemented + + if isinstance(other, np.ndarray) and other.ndim > 0: + # It's ambiguous what this means, so disallow it + raise ValueError("Cannot divide StateSpace by non-scalar numpy arrays") + + return self.__mul__(1/other) + + @property + def A(self): + """State matrix of the `StateSpace` system.""" + return self._A + + @A.setter + def A(self, A): + self._A = _atleast_2d_or_none(A) + + @property + def B(self): + """Input matrix of the `StateSpace` system.""" + return self._B + + @B.setter + def B(self, B): + self._B = _atleast_2d_or_none(B) + self.inputs = self.B.shape[-1] + + @property + def C(self): + """Output matrix of the `StateSpace` system.""" + return self._C + + @C.setter + def C(self, C): + self._C = _atleast_2d_or_none(C) + self.outputs = self.C.shape[0] + + @property + def D(self): + """Feedthrough matrix of the `StateSpace` system.""" + return self._D + + @D.setter + def D(self, D): + self._D = _atleast_2d_or_none(D) + + def _copy(self, system): + """ + Copy the parameters of another `StateSpace` system. + + Parameters + ---------- + system : instance of `StateSpace` + The state-space system that is to be copied + + """ + self.A = system.A + self.B = system.B + self.C = system.C + self.D = system.D + + def to_tf(self, **kwargs): + """ + Convert system representation to `TransferFunction`. + + Parameters + ---------- + kwargs : dict, optional + Additional keywords passed to `ss2zpk` + + Returns + ------- + sys : instance of `TransferFunction` + Transfer function of the current system + + """ + return TransferFunction(*ss2tf(self._A, self._B, self._C, self._D, + **kwargs), **self._dt_dict) + + def to_zpk(self, **kwargs): + """ + Convert system representation to `ZerosPolesGain`. + + Parameters + ---------- + kwargs : dict, optional + Additional keywords passed to `ss2zpk` + + Returns + ------- + sys : instance of `ZerosPolesGain` + Zeros, poles, gain representation of the current system + + """ + return ZerosPolesGain(*ss2zpk(self._A, self._B, self._C, self._D, + **kwargs), **self._dt_dict) + + def to_ss(self): + """ + Return a copy of the current `StateSpace` system. + + Returns + ------- + sys : instance of `StateSpace` + The current system (copy) + + """ + return copy.deepcopy(self) + + +class StateSpaceContinuous(StateSpace, lti): + r""" + Continuous-time Linear Time Invariant system in state-space form. + + Represents the system as the continuous-time, first order differential + equation :math:`\dot{x} = A x + B u`. + Continuous-time `StateSpace` systems inherit additional functionality + from the `lti` class. + + Parameters + ---------- + *system: arguments + The `StateSpace` class can be instantiated with 1 or 3 arguments. + The following gives the number of input arguments and their + interpretation: + + * 1: `lti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 4: array_like: (A, B, C, D) + + See Also + -------- + TransferFunction, ZerosPolesGain, lti + ss2zpk, ss2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `StateSpace` system representation (such as `zeros` or `poles`) is very + inefficient and may lead to numerical inaccuracies. It is better to + convert to the specific system representation first. For example, call + ``sys = sys.to_zpk()`` before accessing/changing the zeros, poles or gain. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + + >>> a = np.array([[0, 1], [0, 0]]) + >>> b = np.array([[0], [1]]) + >>> c = np.array([[1, 0]]) + >>> d = np.array([[0]]) + + >>> sys = signal.StateSpace(a, b, c, d) + >>> print(sys) + StateSpaceContinuous( + array([[0, 1], + [0, 0]]), + array([[0], + [1]]), + array([[1, 0]]), + array([[0]]), + dt: None + ) + + """ + + def to_discrete(self, dt, method='zoh', alpha=None): + """ + Returns the discretized `StateSpace` system. + + Parameters: See `cont2discrete` for details. + + Returns + ------- + sys: instance of `dlti` and `StateSpace` + """ + return StateSpace(*cont2discrete((self.A, self.B, self.C, self.D), + dt, + method=method, + alpha=alpha)[:-1], + dt=dt) + + +class StateSpaceDiscrete(StateSpace, dlti): + r""" + Discrete-time Linear Time Invariant system in state-space form. + + Represents the system as the discrete-time difference equation + :math:`x[k+1] = A x[k] + B u[k]`. + `StateSpace` systems inherit additional functionality from the `dlti` + class. + + Parameters + ---------- + *system: arguments + The `StateSpace` class can be instantiated with 1 or 3 arguments. + The following gives the number of input arguments and their + interpretation: + + * 1: `dlti` system: (`StateSpace`, `TransferFunction` or + `ZerosPolesGain`) + * 4: array_like: (A, B, C, D) + dt: float, optional + Sampling time [s] of the discrete-time systems. Defaults to `True` + (unspecified sampling time). Must be specified as a keyword argument, + for example, ``dt=0.1``. + + See Also + -------- + TransferFunction, ZerosPolesGain, dlti + ss2zpk, ss2tf, zpk2sos + + Notes + ----- + Changing the value of properties that are not part of the + `StateSpace` system representation (such as `zeros` or `poles`) is very + inefficient and may lead to numerical inaccuracies. It is better to + convert to the specific system representation first. For example, call + ``sys = sys.to_zpk()`` before accessing/changing the zeros, poles or gain. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + + >>> a = np.array([[1, 0.1], [0, 1]]) + >>> b = np.array([[0.005], [0.1]]) + >>> c = np.array([[1, 0]]) + >>> d = np.array([[0]]) + + >>> signal.StateSpace(a, b, c, d, dt=0.1) + StateSpaceDiscrete( + array([[ 1. , 0.1], + [ 0. , 1. ]]), + array([[ 0.005], + [ 0.1 ]]), + array([[1, 0]]), + array([[0]]), + dt: 0.1 + ) + + """ + pass + + +def lsim(system, U, T, X0=None, interp=True): + """ + Simulate output of a continuous-time linear system. + + Parameters + ---------- + system : an instance of the LTI class or a tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1: (instance of `lti`) + * 2: (num, den) + * 3: (zeros, poles, gain) + * 4: (A, B, C, D) + + U : array_like + An input array describing the input at each time `T` + (interpolation is assumed between given times). If there are + multiple inputs, then each column of the rank-2 array + represents an input. If U = 0 or None, a zero input is used. + T : array_like + The time steps at which the input is defined and at which the + output is desired. Must be nonnegative, increasing, and equally spaced. + X0 : array_like, optional + The initial conditions on the state vector (zero by default). + interp : bool, optional + Whether to use linear (True, the default) or zero-order-hold (False) + interpolation for the input array. + + Returns + ------- + T : 1D ndarray + Time values for the output. + yout : 1D ndarray + System response. + xout : ndarray + Time evolution of the state vector. + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``s^2 + 3s + 5`` would be represented as ``[1, 3, 5]``). + + Examples + -------- + We'll use `lsim` to simulate an analog Bessel filter applied to + a signal. + + >>> import numpy as np + >>> from scipy.signal import bessel, lsim + >>> import matplotlib.pyplot as plt + + Create a low-pass Bessel filter with a cutoff of 12 Hz. + + >>> b, a = bessel(N=5, Wn=2*np.pi*12, btype='lowpass', analog=True) + + Generate data to which the filter is applied. + + >>> t = np.linspace(0, 1.25, 500, endpoint=False) + + The input signal is the sum of three sinusoidal curves, with + frequencies 4 Hz, 40 Hz, and 80 Hz. The filter should mostly + eliminate the 40 Hz and 80 Hz components, leaving just the 4 Hz signal. + + >>> u = (np.cos(2*np.pi*4*t) + 0.6*np.sin(2*np.pi*40*t) + + ... 0.5*np.cos(2*np.pi*80*t)) + + Simulate the filter with `lsim`. + + >>> tout, yout, xout = lsim((b, a), U=u, T=t) + + Plot the result. + + >>> plt.plot(t, u, 'r', alpha=0.5, linewidth=1, label='input') + >>> plt.plot(tout, yout, 'k', linewidth=1.5, label='output') + >>> plt.legend(loc='best', shadow=True, framealpha=1) + >>> plt.grid(alpha=0.3) + >>> plt.xlabel('t') + >>> plt.show() + + In a second example, we simulate a double integrator ``y'' = u``, with + a constant input ``u = 1``. We'll use the state space representation + of the integrator. + + >>> from scipy.signal import lti + >>> A = np.array([[0.0, 1.0], [0.0, 0.0]]) + >>> B = np.array([[0.0], [1.0]]) + >>> C = np.array([[1.0, 0.0]]) + >>> D = 0.0 + >>> system = lti(A, B, C, D) + + `t` and `u` define the time and input signal for the system to + be simulated. + + >>> t = np.linspace(0, 5, num=50) + >>> u = np.ones_like(t) + + Compute the simulation, and then plot `y`. As expected, the plot shows + the curve ``y = 0.5*t**2``. + + >>> tout, y, x = lsim(system, u, t) + >>> plt.plot(t, y) + >>> plt.grid(alpha=0.3) + >>> plt.xlabel('t') + >>> plt.show() + + """ + if isinstance(system, lti): + sys = system._as_ss() + elif isinstance(system, dlti): + raise AttributeError('lsim can only be used with continuous-time ' + 'systems.') + else: + sys = lti(*system)._as_ss() + T = atleast_1d(T) + if len(T.shape) != 1: + raise ValueError("T must be a rank-1 array.") + + A, B, C, D = map(np.asarray, (sys.A, sys.B, sys.C, sys.D)) + n_states = A.shape[0] + n_inputs = B.shape[1] + + n_steps = T.size + if X0 is None: + X0 = zeros(n_states, sys.A.dtype) + xout = np.empty((n_steps, n_states), sys.A.dtype) + + if T[0] == 0: + xout[0] = X0 + elif T[0] > 0: + # step forward to initial time, with zero input + xout[0] = dot(X0, linalg.expm(transpose(A) * T[0])) + else: + raise ValueError("Initial time must be nonnegative") + + no_input = (U is None or + (isinstance(U, (int, float)) and U == 0.) or + not np.any(U)) + + if n_steps == 1: + yout = squeeze(xout @ C.T) + if not no_input: + yout += squeeze(U @ D.T) + return T, yout, squeeze(xout) + + dt = T[1] - T[0] + if not np.allclose(np.diff(T), dt): + raise ValueError("Time steps are not equally spaced.") + + if no_input: + # Zero input: just use matrix exponential + # take transpose because state is a row vector + expAT_dt = linalg.expm(A.T * dt) + for i in range(1, n_steps): + xout[i] = xout[i-1] @ expAT_dt + yout = squeeze(xout @ C.T) + return T, yout, squeeze(xout) + + # Nonzero input + U = atleast_1d(U) + if U.ndim == 1: + U = U[:, np.newaxis] + + if U.shape[0] != n_steps: + raise ValueError("U must have the same number of rows " + "as elements in T.") + + if U.shape[1] != n_inputs: + raise ValueError("System does not define that many inputs.") + + if not interp: + # Zero-order hold + # Algorithm: to integrate from time 0 to time dt, we solve + # xdot = A x + B u, x(0) = x0 + # udot = 0, u(0) = u0. + # + # Solution is + # [ x(dt) ] [ A*dt B*dt ] [ x0 ] + # [ u(dt) ] = exp [ 0 0 ] [ u0 ] + M = np.vstack([np.hstack([A * dt, B * dt]), + np.zeros((n_inputs, n_states + n_inputs))]) + # transpose everything because the state and input are row vectors + expMT = linalg.expm(M.T) + Ad = expMT[:n_states, :n_states] + Bd = expMT[n_states:, :n_states] + for i in range(1, n_steps): + xout[i] = xout[i-1] @ Ad + U[i-1] @ Bd + else: + # Linear interpolation between steps + # Algorithm: to integrate from time 0 to time dt, with linear + # interpolation between inputs u(0) = u0 and u(dt) = u1, we solve + # xdot = A x + B u, x(0) = x0 + # udot = (u1 - u0) / dt, u(0) = u0. + # + # Solution is + # [ x(dt) ] [ A*dt B*dt 0 ] [ x0 ] + # [ u(dt) ] = exp [ 0 0 I ] [ u0 ] + # [u1 - u0] [ 0 0 0 ] [u1 - u0] + M = np.vstack([np.hstack([A * dt, B * dt, + np.zeros((n_states, n_inputs))]), + np.hstack([np.zeros((n_inputs, n_states + n_inputs)), + np.identity(n_inputs)]), + np.zeros((n_inputs, n_states + 2 * n_inputs))]) + expMT = linalg.expm(M.T) + Ad = expMT[:n_states, :n_states] + Bd1 = expMT[n_states+n_inputs:, :n_states] + Bd0 = expMT[n_states:n_states + n_inputs, :n_states] - Bd1 + for i in range(1, n_steps): + xout[i] = xout[i-1] @ Ad + U[i-1] @ Bd0 + U[i] @ Bd1 + + yout = squeeze(xout @ C.T) + squeeze(U @ D.T) + return T, yout, squeeze(xout) + + +def _default_response_times(A, n): + """Compute a reasonable set of time samples for the response time. + + This function is used by `impulse` and `step` to compute the response time + when the `T` argument to the function is None. + + Parameters + ---------- + A : array_like + The system matrix, which is square. + n : int + The number of time samples to generate. + + Returns + ------- + t : ndarray + The 1-D array of length `n` of time samples at which the response + is to be computed. + """ + # Create a reasonable time interval. + # TODO: This could use some more work. + # For example, what is expected when the system is unstable? + vals = linalg.eigvals(A) + r = min(abs(real(vals))) + if r == 0.0: + r = 1.0 + tc = 1.0 / r + t = linspace(0.0, 7 * tc, n) + return t + + +def impulse(system, X0=None, T=None, N=None): + """Impulse response of continuous-time system. + + Parameters + ---------- + system : an instance of the LTI class or a tuple of array_like + describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `lti`) + * 2 (num, den) + * 3 (zeros, poles, gain) + * 4 (A, B, C, D) + + X0 : array_like, optional + Initial state-vector. Defaults to zero. + T : array_like, optional + Time points. Computed if not given. + N : int, optional + The number of time points to compute (if `T` is not given). + + Returns + ------- + T : ndarray + A 1-D array of time points. + yout : ndarray + A 1-D array containing the impulse response of the system (except for + singularities at zero). + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``s^2 + 3s + 5`` would be represented as ``[1, 3, 5]``). + + Examples + -------- + Compute the impulse response of a second order system with a repeated + root: ``x''(t) + 2*x'(t) + x(t) = u(t)`` + + >>> from scipy import signal + >>> system = ([1.0], [1.0, 2.0, 1.0]) + >>> t, y = signal.impulse(system) + >>> import matplotlib.pyplot as plt + >>> plt.plot(t, y) + + """ + if isinstance(system, lti): + sys = system._as_ss() + elif isinstance(system, dlti): + raise AttributeError('impulse can only be used with continuous-time ' + 'systems.') + else: + sys = lti(*system)._as_ss() + if X0 is None: + X = squeeze(sys.B) + else: + X = squeeze(sys.B + X0) + if N is None: + N = 100 + if T is None: + T = _default_response_times(sys.A, N) + else: + T = asarray(T) + + _, h, _ = lsim(sys, 0., T, X, interp=False) + return T, h + + +def step(system, X0=None, T=None, N=None): + """Step response of continuous-time system. + + Parameters + ---------- + system : an instance of the LTI class or a tuple of array_like + describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `lti`) + * 2 (num, den) + * 3 (zeros, poles, gain) + * 4 (A, B, C, D) + + X0 : array_like, optional + Initial state-vector (default is zero). + T : array_like, optional + Time points (computed if not given). + N : int, optional + Number of time points to compute if `T` is not given. + + Returns + ------- + T : 1D ndarray + Output time points. + yout : 1D ndarray + Step response of system. + + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``s^2 + 3s + 5`` would be represented as ``[1, 3, 5]``). + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> lti = signal.lti([1.0], [1.0, 1.0]) + >>> t, y = signal.step(lti) + >>> plt.plot(t, y) + >>> plt.xlabel('Time [s]') + >>> plt.ylabel('Amplitude') + >>> plt.title('Step response for 1. Order Lowpass') + >>> plt.grid() + + """ + if isinstance(system, lti): + sys = system._as_ss() + elif isinstance(system, dlti): + raise AttributeError('step can only be used with continuous-time ' + 'systems.') + else: + sys = lti(*system)._as_ss() + if N is None: + N = 100 + if T is None: + T = _default_response_times(sys.A, N) + else: + T = asarray(T) + U = ones(T.shape, sys.A.dtype) + vals = lsim(sys, U, T, X0=X0, interp=False) + return vals[0], vals[1] + + +def bode(system, w=None, n=100): + """ + Calculate Bode magnitude and phase data of a continuous-time system. + + Parameters + ---------- + system : an instance of the LTI class or a tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `lti`) + * 2 (num, den) + * 3 (zeros, poles, gain) + * 4 (A, B, C, D) + + w : array_like, optional + Array of frequencies (in rad/s). Magnitude and phase data is calculated + for every value in this array. If not given a reasonable set will be + calculated. + n : int, optional + Number of frequency points to compute if `w` is not given. The `n` + frequencies are logarithmically spaced in an interval chosen to + include the influence of the poles and zeros of the system. + + Returns + ------- + w : 1D ndarray + Frequency array [rad/s] + mag : 1D ndarray + Magnitude array [dB] + phase : 1D ndarray + Phase array [deg] + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``s^2 + 3s + 5`` would be represented as ``[1, 3, 5]``). + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> sys = signal.TransferFunction([1], [1, 1]) + >>> w, mag, phase = signal.bode(sys) + + >>> plt.figure() + >>> plt.semilogx(w, mag) # Bode magnitude plot + >>> plt.figure() + >>> plt.semilogx(w, phase) # Bode phase plot + >>> plt.show() + + """ + w, y = freqresp(system, w=w, n=n) + + mag = 20.0 * numpy.log10(abs(y)) + phase = numpy.unwrap(numpy.arctan2(y.imag, y.real)) * 180.0 / numpy.pi + + return w, mag, phase + + +def freqresp(system, w=None, n=10000): + r"""Calculate the frequency response of a continuous-time system. + + Parameters + ---------- + system : an instance of the `lti` class or a tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `lti`) + * 2 (num, den) + * 3 (zeros, poles, gain) + * 4 (A, B, C, D) + + w : array_like, optional + Array of frequencies (in rad/s). Magnitude and phase data is + calculated for every value in this array. If not given, a reasonable + set will be calculated. + n : int, optional + Number of frequency points to compute if `w` is not given. The `n` + frequencies are logarithmically spaced in an interval chosen to + include the influence of the poles and zeros of the system. + + Returns + ------- + w : 1D ndarray + Frequency array [rad/s] + H : 1D ndarray + Array of complex magnitude values + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``s^2 + 3s + 5`` would be represented as ``[1, 3, 5]``). + + Examples + -------- + Generating the Nyquist plot of a transfer function + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + Construct the transfer function :math:`H(s) = \frac{5}{(s-1)^3}`: + + >>> s1 = signal.ZerosPolesGain([], [1, 1, 1], [5]) + + >>> w, H = signal.freqresp(s1) + + >>> plt.figure() + >>> plt.plot(H.real, H.imag, "b") + >>> plt.plot(H.real, -H.imag, "r") + >>> plt.show() + """ + if isinstance(system, lti): + if isinstance(system, (TransferFunction, ZerosPolesGain)): + sys = system + else: + sys = system._as_zpk() + elif isinstance(system, dlti): + raise AttributeError('freqresp can only be used with continuous-time ' + 'systems.') + else: + sys = lti(*system)._as_zpk() + + if sys.inputs != 1 or sys.outputs != 1: + raise ValueError("freqresp() requires a SISO (single input, single " + "output) system.") + + if w is not None: + worN = w + else: + worN = n + + if isinstance(sys, TransferFunction): + # In the call to freqs(), sys.num.ravel() is used because there are + # cases where sys.num is a 2-D array with a single row. + w, h = freqs(sys.num.ravel(), sys.den, worN=worN) + + elif isinstance(sys, ZerosPolesGain): + w, h = freqs_zpk(sys.zeros, sys.poles, sys.gain, worN=worN) + + return w, h + + +# This class will be used by place_poles to return its results +# see https://code.activestate.com/recipes/52308/ +class Bunch: + def __init__(self, **kwds): + self.__dict__.update(kwds) + + +def _valid_inputs(A, B, poles, method, rtol, maxiter): + """ + Check the poles come in complex conjugage pairs + Check shapes of A, B and poles are compatible. + Check the method chosen is compatible with provided poles + Return update method to use and ordered poles + + """ + poles = np.asarray(poles) + if poles.ndim > 1: + raise ValueError("Poles must be a 1D array like.") + # Will raise ValueError if poles do not come in complex conjugates pairs + poles = _order_complex_poles(poles) + if A.ndim > 2: + raise ValueError("A must be a 2D array/matrix.") + if B.ndim > 2: + raise ValueError("B must be a 2D array/matrix") + if A.shape[0] != A.shape[1]: + raise ValueError("A must be square") + if len(poles) > A.shape[0]: + raise ValueError("maximum number of poles is %d but you asked for %d" % + (A.shape[0], len(poles))) + if len(poles) < A.shape[0]: + raise ValueError("number of poles is %d but you should provide %d" % + (len(poles), A.shape[0])) + r = np.linalg.matrix_rank(B) + for p in poles: + if sum(p == poles) > r: + raise ValueError("at least one of the requested pole is repeated " + "more than rank(B) times") + # Choose update method + update_loop = _YT_loop + if method not in ('KNV0','YT'): + raise ValueError("The method keyword must be one of 'YT' or 'KNV0'") + + if method == "KNV0": + update_loop = _KNV0_loop + if not all(np.isreal(poles)): + raise ValueError("Complex poles are not supported by KNV0") + + if maxiter < 1: + raise ValueError("maxiter must be at least equal to 1") + + # We do not check rtol <= 0 as the user can use a negative rtol to + # force maxiter iterations + if rtol > 1: + raise ValueError("rtol can not be greater than 1") + + return update_loop, poles + + +def _order_complex_poles(poles): + """ + Check we have complex conjugates pairs and reorder P according to YT, ie + real_poles, complex_i, conjugate complex_i, .... + The lexicographic sort on the complex poles is added to help the user to + compare sets of poles. + """ + ordered_poles = np.sort(poles[np.isreal(poles)]) + im_poles = [] + for p in np.sort(poles[np.imag(poles) < 0]): + if np.conj(p) in poles: + im_poles.extend((p, np.conj(p))) + + ordered_poles = np.hstack((ordered_poles, im_poles)) + + if poles.shape[0] != len(ordered_poles): + raise ValueError("Complex poles must come with their conjugates") + return ordered_poles + + +def _KNV0(B, ker_pole, transfer_matrix, j, poles): + """ + Algorithm "KNV0" Kautsky et Al. Robust pole + assignment in linear state feedback, Int journal of Control + 1985, vol 41 p 1129->1155 + https://la.epfl.ch/files/content/sites/la/files/ + users/105941/public/KautskyNicholsDooren + + """ + # Remove xj form the base + transfer_matrix_not_j = np.delete(transfer_matrix, j, axis=1) + # If we QR this matrix in full mode Q=Q0|Q1 + # then Q1 will be a single column orthogonnal to + # Q0, that's what we are looking for ! + + # After merge of gh-4249 great speed improvements could be achieved + # using QR updates instead of full QR in the line below + + # To debug with numpy qr uncomment the line below + # Q, R = np.linalg.qr(transfer_matrix_not_j, mode="complete") + Q, R = s_qr(transfer_matrix_not_j, mode="full") + + mat_ker_pj = np.dot(ker_pole[j], ker_pole[j].T) + yj = np.dot(mat_ker_pj, Q[:, -1]) + + # If Q[:, -1] is "almost" orthogonal to ker_pole[j] its + # projection into ker_pole[j] will yield a vector + # close to 0. As we are looking for a vector in ker_pole[j] + # simply stick with transfer_matrix[:, j] (unless someone provides me with + # a better choice ?) + + if not np.allclose(yj, 0): + xj = yj/np.linalg.norm(yj) + transfer_matrix[:, j] = xj + + # KNV does not support complex poles, using YT technique the two lines + # below seem to work 9 out of 10 times but it is not reliable enough: + # transfer_matrix[:, j]=real(xj) + # transfer_matrix[:, j+1]=imag(xj) + + # Add this at the beginning of this function if you wish to test + # complex support: + # if ~np.isreal(P[j]) and (j>=B.shape[0]-1 or P[j]!=np.conj(P[j+1])): + # return + # Problems arise when imag(xj)=>0 I have no idea on how to fix this + + +def _YT_real(ker_pole, Q, transfer_matrix, i, j): + """ + Applies algorithm from YT section 6.1 page 19 related to real pairs + """ + # step 1 page 19 + u = Q[:, -2, np.newaxis] + v = Q[:, -1, np.newaxis] + + # step 2 page 19 + m = np.dot(np.dot(ker_pole[i].T, np.dot(u, v.T) - + np.dot(v, u.T)), ker_pole[j]) + + # step 3 page 19 + um, sm, vm = np.linalg.svd(m) + # mu1, mu2 two first columns of U => 2 first lines of U.T + mu1, mu2 = um.T[:2, :, np.newaxis] + # VM is V.T with numpy we want the first two lines of V.T + nu1, nu2 = vm[:2, :, np.newaxis] + + # what follows is a rough python translation of the formulas + # in section 6.2 page 20 (step 4) + transfer_matrix_j_mo_transfer_matrix_j = np.vstack(( + transfer_matrix[:, i, np.newaxis], + transfer_matrix[:, j, np.newaxis])) + + if not np.allclose(sm[0], sm[1]): + ker_pole_imo_mu1 = np.dot(ker_pole[i], mu1) + ker_pole_i_nu1 = np.dot(ker_pole[j], nu1) + ker_pole_mu_nu = np.vstack((ker_pole_imo_mu1, ker_pole_i_nu1)) + else: + ker_pole_ij = np.vstack(( + np.hstack((ker_pole[i], + np.zeros(ker_pole[i].shape))), + np.hstack((np.zeros(ker_pole[j].shape), + ker_pole[j])) + )) + mu_nu_matrix = np.vstack( + (np.hstack((mu1, mu2)), np.hstack((nu1, nu2))) + ) + ker_pole_mu_nu = np.dot(ker_pole_ij, mu_nu_matrix) + transfer_matrix_ij = np.dot(np.dot(ker_pole_mu_nu, ker_pole_mu_nu.T), + transfer_matrix_j_mo_transfer_matrix_j) + if not np.allclose(transfer_matrix_ij, 0): + transfer_matrix_ij = (np.sqrt(2)*transfer_matrix_ij / + np.linalg.norm(transfer_matrix_ij)) + transfer_matrix[:, i] = transfer_matrix_ij[ + :transfer_matrix[:, i].shape[0], 0 + ] + transfer_matrix[:, j] = transfer_matrix_ij[ + transfer_matrix[:, i].shape[0]:, 0 + ] + else: + # As in knv0 if transfer_matrix_j_mo_transfer_matrix_j is orthogonal to + # Vect{ker_pole_mu_nu} assign transfer_matrixi/transfer_matrix_j to + # ker_pole_mu_nu and iterate. As we are looking for a vector in + # Vect{Matker_pole_MU_NU} (see section 6.1 page 19) this might help + # (that's a guess, not a claim !) + transfer_matrix[:, i] = ker_pole_mu_nu[ + :transfer_matrix[:, i].shape[0], 0 + ] + transfer_matrix[:, j] = ker_pole_mu_nu[ + transfer_matrix[:, i].shape[0]:, 0 + ] + + +def _YT_complex(ker_pole, Q, transfer_matrix, i, j): + """ + Applies algorithm from YT section 6.2 page 20 related to complex pairs + """ + # step 1 page 20 + ur = np.sqrt(2)*Q[:, -2, np.newaxis] + ui = np.sqrt(2)*Q[:, -1, np.newaxis] + u = ur + 1j*ui + + # step 2 page 20 + ker_pole_ij = ker_pole[i] + m = np.dot(np.dot(np.conj(ker_pole_ij.T), np.dot(u, np.conj(u).T) - + np.dot(np.conj(u), u.T)), ker_pole_ij) + + # step 3 page 20 + e_val, e_vec = np.linalg.eig(m) + # sort eigenvalues according to their module + e_val_idx = np.argsort(np.abs(e_val)) + mu1 = e_vec[:, e_val_idx[-1], np.newaxis] + mu2 = e_vec[:, e_val_idx[-2], np.newaxis] + + # what follows is a rough python translation of the formulas + # in section 6.2 page 20 (step 4) + + # remember transfer_matrix_i has been split as + # transfer_matrix[i]=real(transfer_matrix_i) and + # transfer_matrix[j]=imag(transfer_matrix_i) + transfer_matrix_j_mo_transfer_matrix_j = ( + transfer_matrix[:, i, np.newaxis] + + 1j*transfer_matrix[:, j, np.newaxis] + ) + if not np.allclose(np.abs(e_val[e_val_idx[-1]]), + np.abs(e_val[e_val_idx[-2]])): + ker_pole_mu = np.dot(ker_pole_ij, mu1) + else: + mu1_mu2_matrix = np.hstack((mu1, mu2)) + ker_pole_mu = np.dot(ker_pole_ij, mu1_mu2_matrix) + transfer_matrix_i_j = np.dot(np.dot(ker_pole_mu, np.conj(ker_pole_mu.T)), + transfer_matrix_j_mo_transfer_matrix_j) + + if not np.allclose(transfer_matrix_i_j, 0): + transfer_matrix_i_j = (transfer_matrix_i_j / + np.linalg.norm(transfer_matrix_i_j)) + transfer_matrix[:, i] = np.real(transfer_matrix_i_j[:, 0]) + transfer_matrix[:, j] = np.imag(transfer_matrix_i_j[:, 0]) + else: + # same idea as in YT_real + transfer_matrix[:, i] = np.real(ker_pole_mu[:, 0]) + transfer_matrix[:, j] = np.imag(ker_pole_mu[:, 0]) + + +def _YT_loop(ker_pole, transfer_matrix, poles, B, maxiter, rtol): + """ + Algorithm "YT" Tits, Yang. Globally Convergent + Algorithms for Robust Pole Assignment by State Feedback + https://hdl.handle.net/1903/5598 + The poles P have to be sorted accordingly to section 6.2 page 20 + + """ + # The IEEE edition of the YT paper gives useful information on the + # optimal update order for the real poles in order to minimize the number + # of times we have to loop over all poles, see page 1442 + nb_real = poles[np.isreal(poles)].shape[0] + # hnb => Half Nb Real + hnb = nb_real // 2 + + # Stick to the indices in the paper and then remove one to get numpy array + # index it is a bit easier to link the code to the paper this way even if it + # is not very clean. The paper is unclear about what should be done when + # there is only one real pole => use KNV0 on this real pole seem to work + if nb_real > 0: + #update the biggest real pole with the smallest one + update_order = [[nb_real], [1]] + else: + update_order = [[],[]] + + r_comp = np.arange(nb_real+1, len(poles)+1, 2) + # step 1.a + r_p = np.arange(1, hnb+nb_real % 2) + update_order[0].extend(2*r_p) + update_order[1].extend(2*r_p+1) + # step 1.b + update_order[0].extend(r_comp) + update_order[1].extend(r_comp+1) + # step 1.c + r_p = np.arange(1, hnb+1) + update_order[0].extend(2*r_p-1) + update_order[1].extend(2*r_p) + # step 1.d + if hnb == 0 and np.isreal(poles[0]): + update_order[0].append(1) + update_order[1].append(1) + update_order[0].extend(r_comp) + update_order[1].extend(r_comp+1) + # step 2.a + r_j = np.arange(2, hnb+nb_real % 2) + for j in r_j: + for i in range(1, hnb+1): + update_order[0].append(i) + update_order[1].append(i+j) + # step 2.b + if hnb == 0 and np.isreal(poles[0]): + update_order[0].append(1) + update_order[1].append(1) + update_order[0].extend(r_comp) + update_order[1].extend(r_comp+1) + # step 2.c + r_j = np.arange(2, hnb+nb_real % 2) + for j in r_j: + for i in range(hnb+1, nb_real+1): + idx_1 = i+j + if idx_1 > nb_real: + idx_1 = i+j-nb_real + update_order[0].append(i) + update_order[1].append(idx_1) + # step 2.d + if hnb == 0 and np.isreal(poles[0]): + update_order[0].append(1) + update_order[1].append(1) + update_order[0].extend(r_comp) + update_order[1].extend(r_comp+1) + # step 3.a + for i in range(1, hnb+1): + update_order[0].append(i) + update_order[1].append(i+hnb) + # step 3.b + if hnb == 0 and np.isreal(poles[0]): + update_order[0].append(1) + update_order[1].append(1) + update_order[0].extend(r_comp) + update_order[1].extend(r_comp+1) + + update_order = np.array(update_order).T-1 + stop = False + nb_try = 0 + while nb_try < maxiter and not stop: + det_transfer_matrixb = np.abs(np.linalg.det(transfer_matrix)) + for i, j in update_order: + if i == j: + assert i == 0, "i!=0 for KNV call in YT" + assert np.isreal(poles[i]), "calling KNV on a complex pole" + _KNV0(B, ker_pole, transfer_matrix, i, poles) + else: + transfer_matrix_not_i_j = np.delete(transfer_matrix, (i, j), + axis=1) + # after merge of gh-4249 great speed improvements could be + # achieved using QR updates instead of full QR in the line below + + #to debug with numpy qr uncomment the line below + #Q, _ = np.linalg.qr(transfer_matrix_not_i_j, mode="complete") + Q, _ = s_qr(transfer_matrix_not_i_j, mode="full") + + if np.isreal(poles[i]): + assert np.isreal(poles[j]), "mixing real and complex " + \ + "in YT_real" + str(poles) + _YT_real(ker_pole, Q, transfer_matrix, i, j) + else: + assert ~np.isreal(poles[i]), "mixing real and complex " + \ + "in YT_real" + str(poles) + _YT_complex(ker_pole, Q, transfer_matrix, i, j) + + det_transfer_matrix = np.max((np.sqrt(np.spacing(1)), + np.abs(np.linalg.det(transfer_matrix)))) + cur_rtol = np.abs( + (det_transfer_matrix - + det_transfer_matrixb) / + det_transfer_matrix) + if cur_rtol < rtol and det_transfer_matrix > np.sqrt(np.spacing(1)): + # Convergence test from YT page 21 + stop = True + nb_try += 1 + return stop, cur_rtol, nb_try + + +def _KNV0_loop(ker_pole, transfer_matrix, poles, B, maxiter, rtol): + """ + Loop over all poles one by one and apply KNV method 0 algorithm + """ + # This method is useful only because we need to be able to call + # _KNV0 from YT without looping over all poles, otherwise it would + # have been fine to mix _KNV0_loop and _KNV0 in a single function + stop = False + nb_try = 0 + while nb_try < maxiter and not stop: + det_transfer_matrixb = np.abs(np.linalg.det(transfer_matrix)) + for j in range(B.shape[0]): + _KNV0(B, ker_pole, transfer_matrix, j, poles) + + det_transfer_matrix = np.max((np.sqrt(np.spacing(1)), + np.abs(np.linalg.det(transfer_matrix)))) + cur_rtol = np.abs((det_transfer_matrix - det_transfer_matrixb) / + det_transfer_matrix) + if cur_rtol < rtol and det_transfer_matrix > np.sqrt(np.spacing(1)): + # Convergence test from YT page 21 + stop = True + + nb_try += 1 + return stop, cur_rtol, nb_try + + +def place_poles(A, B, poles, method="YT", rtol=1e-3, maxiter=30): + """ + Compute K such that eigenvalues (A - dot(B, K))=poles. + + K is the gain matrix such as the plant described by the linear system + ``AX+BU`` will have its closed-loop poles, i.e the eigenvalues ``A - B*K``, + as close as possible to those asked for in poles. + + SISO, MISO and MIMO systems are supported. + + Parameters + ---------- + A, B : ndarray + State-space representation of linear system ``AX + BU``. + poles : array_like + Desired real poles and/or complex conjugates poles. + Complex poles are only supported with ``method="YT"`` (default). + method: {'YT', 'KNV0'}, optional + Which method to choose to find the gain matrix K. One of: + + - 'YT': Yang Tits + - 'KNV0': Kautsky, Nichols, Van Dooren update method 0 + + See References and Notes for details on the algorithms. + rtol: float, optional + After each iteration the determinant of the eigenvectors of + ``A - B*K`` is compared to its previous value, when the relative + error between these two values becomes lower than `rtol` the algorithm + stops. Default is 1e-3. + maxiter: int, optional + Maximum number of iterations to compute the gain matrix. + Default is 30. + + Returns + ------- + full_state_feedback : Bunch object + full_state_feedback is composed of: + gain_matrix : 1-D ndarray + The closed loop matrix K such as the eigenvalues of ``A-BK`` + are as close as possible to the requested poles. + computed_poles : 1-D ndarray + The poles corresponding to ``A-BK`` sorted as first the real + poles in increasing order, then the complex congugates in + lexicographic order. + requested_poles : 1-D ndarray + The poles the algorithm was asked to place sorted as above, + they may differ from what was achieved. + X : 2-D ndarray + The transfer matrix such as ``X * diag(poles) = (A - B*K)*X`` + (see Notes) + rtol : float + The relative tolerance achieved on ``det(X)`` (see Notes). + `rtol` will be NaN if it is possible to solve the system + ``diag(poles) = (A - B*K)``, or 0 when the optimization + algorithms can't do anything i.e when ``B.shape[1] == 1``. + nb_iter : int + The number of iterations performed before converging. + `nb_iter` will be NaN if it is possible to solve the system + ``diag(poles) = (A - B*K)``, or 0 when the optimization + algorithms can't do anything i.e when ``B.shape[1] == 1``. + + Notes + ----- + The Tits and Yang (YT), [2]_ paper is an update of the original Kautsky et + al. (KNV) paper [1]_. KNV relies on rank-1 updates to find the transfer + matrix X such that ``X * diag(poles) = (A - B*K)*X``, whereas YT uses + rank-2 updates. This yields on average more robust solutions (see [2]_ + pp 21-22), furthermore the YT algorithm supports complex poles whereas KNV + does not in its original version. Only update method 0 proposed by KNV has + been implemented here, hence the name ``'KNV0'``. + + KNV extended to complex poles is used in Matlab's ``place`` function, YT is + distributed under a non-free licence by Slicot under the name ``robpole``. + It is unclear and undocumented how KNV0 has been extended to complex poles + (Tits and Yang claim on page 14 of their paper that their method can not be + used to extend KNV to complex poles), therefore only YT supports them in + this implementation. + + As the solution to the problem of pole placement is not unique for MIMO + systems, both methods start with a tentative transfer matrix which is + altered in various way to increase its determinant. Both methods have been + proven to converge to a stable solution, however depending on the way the + initial transfer matrix is chosen they will converge to different + solutions and therefore there is absolutely no guarantee that using + ``'KNV0'`` will yield results similar to Matlab's or any other + implementation of these algorithms. + + Using the default method ``'YT'`` should be fine in most cases; ``'KNV0'`` + is only provided because it is needed by ``'YT'`` in some specific cases. + Furthermore ``'YT'`` gives on average more robust results than ``'KNV0'`` + when ``abs(det(X))`` is used as a robustness indicator. + + [2]_ is available as a technical report on the following URL: + https://hdl.handle.net/1903/5598 + + References + ---------- + .. [1] J. Kautsky, N.K. Nichols and P. van Dooren, "Robust pole assignment + in linear state feedback", International Journal of Control, Vol. 41 + pp. 1129-1155, 1985. + .. [2] A.L. Tits and Y. Yang, "Globally convergent algorithms for robust + pole assignment by state feedback", IEEE Transactions on Automatic + Control, Vol. 41, pp. 1432-1452, 1996. + + Examples + -------- + A simple example demonstrating real pole placement using both KNV and YT + algorithms. This is example number 1 from section 4 of the reference KNV + publication ([1]_): + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> A = np.array([[ 1.380, -0.2077, 6.715, -5.676 ], + ... [-0.5814, -4.290, 0, 0.6750 ], + ... [ 1.067, 4.273, -6.654, 5.893 ], + ... [ 0.0480, 4.273, 1.343, -2.104 ]]) + >>> B = np.array([[ 0, 5.679 ], + ... [ 1.136, 1.136 ], + ... [ 0, 0, ], + ... [-3.146, 0 ]]) + >>> P = np.array([-0.2, -0.5, -5.0566, -8.6659]) + + Now compute K with KNV method 0, with the default YT method and with the YT + method while forcing 100 iterations of the algorithm and print some results + after each call. + + >>> fsf1 = signal.place_poles(A, B, P, method='KNV0') + >>> fsf1.gain_matrix + array([[ 0.20071427, -0.96665799, 0.24066128, -0.10279785], + [ 0.50587268, 0.57779091, 0.51795763, -0.41991442]]) + + >>> fsf2 = signal.place_poles(A, B, P) # uses YT method + >>> fsf2.computed_poles + array([-8.6659, -5.0566, -0.5 , -0.2 ]) + + >>> fsf3 = signal.place_poles(A, B, P, rtol=-1, maxiter=100) + >>> fsf3.X + array([[ 0.52072442+0.j, -0.08409372+0.j, -0.56847937+0.j, 0.74823657+0.j], + [-0.04977751+0.j, -0.80872954+0.j, 0.13566234+0.j, -0.29322906+0.j], + [-0.82266932+0.j, -0.19168026+0.j, -0.56348322+0.j, -0.43815060+0.j], + [ 0.22267347+0.j, 0.54967577+0.j, -0.58387806+0.j, -0.40271926+0.j]]) + + The absolute value of the determinant of X is a good indicator to check the + robustness of the results, both ``'KNV0'`` and ``'YT'`` aim at maximizing + it. Below a comparison of the robustness of the results above: + + >>> abs(np.linalg.det(fsf1.X)) < abs(np.linalg.det(fsf2.X)) + True + >>> abs(np.linalg.det(fsf2.X)) < abs(np.linalg.det(fsf3.X)) + True + + Now a simple example for complex poles: + + >>> A = np.array([[ 0, 7/3., 0, 0 ], + ... [ 0, 0, 0, 7/9. ], + ... [ 0, 0, 0, 0 ], + ... [ 0, 0, 0, 0 ]]) + >>> B = np.array([[ 0, 0 ], + ... [ 0, 0 ], + ... [ 1, 0 ], + ... [ 0, 1 ]]) + >>> P = np.array([-3, -1, -2-1j, -2+1j]) / 3. + >>> fsf = signal.place_poles(A, B, P, method='YT') + + We can plot the desired and computed poles in the complex plane: + + >>> t = np.linspace(0, 2*np.pi, 401) + >>> plt.plot(np.cos(t), np.sin(t), 'k--') # unit circle + >>> plt.plot(fsf.requested_poles.real, fsf.requested_poles.imag, + ... 'wo', label='Desired') + >>> plt.plot(fsf.computed_poles.real, fsf.computed_poles.imag, 'bx', + ... label='Placed') + >>> plt.grid() + >>> plt.axis('image') + >>> plt.axis([-1.1, 1.1, -1.1, 1.1]) + >>> plt.legend(bbox_to_anchor=(1.05, 1), loc=2, numpoints=1) + + """ + # Move away all the inputs checking, it only adds noise to the code + update_loop, poles = _valid_inputs(A, B, poles, method, rtol, maxiter) + + # The current value of the relative tolerance we achieved + cur_rtol = 0 + # The number of iterations needed before converging + nb_iter = 0 + + # Step A: QR decomposition of B page 1132 KN + # to debug with numpy qr uncomment the line below + # u, z = np.linalg.qr(B, mode="complete") + u, z = s_qr(B, mode="full") + rankB = np.linalg.matrix_rank(B) + u0 = u[:, :rankB] + u1 = u[:, rankB:] + z = z[:rankB, :] + + # If we can use the identity matrix as X the solution is obvious + if B.shape[0] == rankB: + # if B is square and full rank there is only one solution + # such as (A+BK)=inv(X)*diag(P)*X with X=eye(A.shape[0]) + # i.e K=inv(B)*(diag(P)-A) + # if B has as many lines as its rank (but not square) there are many + # solutions and we can choose one using least squares + # => use lstsq in both cases. + # In both cases the transfer matrix X will be eye(A.shape[0]) and I + # can hardly think of a better one so there is nothing to optimize + # + # for complex poles we use the following trick + # + # |a -b| has for eigenvalues a+b and a-b + # |b a| + # + # |a+bi 0| has the obvious eigenvalues a+bi and a-bi + # |0 a-bi| + # + # e.g solving the first one in R gives the solution + # for the second one in C + diag_poles = np.zeros(A.shape) + idx = 0 + while idx < poles.shape[0]: + p = poles[idx] + diag_poles[idx, idx] = np.real(p) + if ~np.isreal(p): + diag_poles[idx, idx+1] = -np.imag(p) + diag_poles[idx+1, idx+1] = np.real(p) + diag_poles[idx+1, idx] = np.imag(p) + idx += 1 # skip next one + idx += 1 + gain_matrix = np.linalg.lstsq(B, diag_poles-A, rcond=-1)[0] + transfer_matrix = np.eye(A.shape[0]) + cur_rtol = np.nan + nb_iter = np.nan + else: + # step A (p1144 KNV) and beginning of step F: decompose + # dot(U1.T, A-P[i]*I).T and build our set of transfer_matrix vectors + # in the same loop + ker_pole = [] + + # flag to skip the conjugate of a complex pole + skip_conjugate = False + # select orthonormal base ker_pole for each Pole and vectors for + # transfer_matrix + for j in range(B.shape[0]): + if skip_conjugate: + skip_conjugate = False + continue + pole_space_j = np.dot(u1.T, A-poles[j]*np.eye(B.shape[0])).T + + # after QR Q=Q0|Q1 + # only Q0 is used to reconstruct the qr'ed (dot Q, R) matrix. + # Q1 is orthogonnal to Q0 and will be multiplied by the zeros in + # R when using mode "complete". In default mode Q1 and the zeros + # in R are not computed + + # To debug with numpy qr uncomment the line below + # Q, _ = np.linalg.qr(pole_space_j, mode="complete") + Q, _ = s_qr(pole_space_j, mode="full") + + ker_pole_j = Q[:, pole_space_j.shape[1]:] + + # We want to select one vector in ker_pole_j to build the transfer + # matrix, however qr returns sometimes vectors with zeros on the + # same line for each pole and this yields very long convergence + # times. + # Or some other times a set of vectors, one with zero imaginary + # part and one (or several) with imaginary parts. After trying + # many ways to select the best possible one (eg ditch vectors + # with zero imaginary part for complex poles) I ended up summing + # all vectors in ker_pole_j, this solves 100% of the problems and + # is a valid choice for transfer_matrix. + # This way for complex poles we are sure to have a non zero + # imaginary part that way, and the problem of lines full of zeros + # in transfer_matrix is solved too as when a vector from + # ker_pole_j has a zero the other one(s) when + # ker_pole_j.shape[1]>1) for sure won't have a zero there. + + transfer_matrix_j = np.sum(ker_pole_j, axis=1)[:, np.newaxis] + transfer_matrix_j = (transfer_matrix_j / + np.linalg.norm(transfer_matrix_j)) + if ~np.isreal(poles[j]): # complex pole + transfer_matrix_j = np.hstack([np.real(transfer_matrix_j), + np.imag(transfer_matrix_j)]) + ker_pole.extend([ker_pole_j, ker_pole_j]) + + # Skip next pole as it is the conjugate + skip_conjugate = True + else: # real pole, nothing to do + ker_pole.append(ker_pole_j) + + if j == 0: + transfer_matrix = transfer_matrix_j + else: + transfer_matrix = np.hstack((transfer_matrix, transfer_matrix_j)) + + if rankB > 1: # otherwise there is nothing we can optimize + stop, cur_rtol, nb_iter = update_loop(ker_pole, transfer_matrix, + poles, B, maxiter, rtol) + if not stop and rtol > 0: + # if rtol<=0 the user has probably done that on purpose, + # don't annoy him + err_msg = ( + "Convergence was not reached after maxiter iterations.\n" + f"You asked for a tolerance of {rtol}, we got {cur_rtol}." + ) + warnings.warn(err_msg, stacklevel=2) + + # reconstruct transfer_matrix to match complex conjugate pairs, + # ie transfer_matrix_j/transfer_matrix_j+1 are + # Re(Complex_pole), Im(Complex_pole) now and will be Re-Im/Re+Im after + transfer_matrix = transfer_matrix.astype(complex) + idx = 0 + while idx < poles.shape[0]-1: + if ~np.isreal(poles[idx]): + rel = transfer_matrix[:, idx].copy() + img = transfer_matrix[:, idx+1] + # rel will be an array referencing a column of transfer_matrix + # if we don't copy() it will changer after the next line and + # and the line after will not yield the correct value + transfer_matrix[:, idx] = rel-1j*img + transfer_matrix[:, idx+1] = rel+1j*img + idx += 1 # skip next one + idx += 1 + + try: + m = np.linalg.solve(transfer_matrix.T, np.dot(np.diag(poles), + transfer_matrix.T)).T + gain_matrix = np.linalg.solve(z, np.dot(u0.T, m-A)) + except np.linalg.LinAlgError as e: + raise ValueError("The poles you've chosen can't be placed. " + "Check the controllability matrix and try " + "another set of poles") from e + + # Beware: Kautsky solves A+BK but the usual form is A-BK + gain_matrix = -gain_matrix + # K still contains complex with ~=0j imaginary parts, get rid of them + gain_matrix = np.real(gain_matrix) + + full_state_feedback = Bunch() + full_state_feedback.gain_matrix = gain_matrix + full_state_feedback.computed_poles = _order_complex_poles( + np.linalg.eig(A - np.dot(B, gain_matrix))[0] + ) + full_state_feedback.requested_poles = poles + full_state_feedback.X = transfer_matrix + full_state_feedback.rtol = cur_rtol + full_state_feedback.nb_iter = nb_iter + + return full_state_feedback + + +def dlsim(system, u, t=None, x0=None): + """ + Simulate output of a discrete-time linear system. + + Parameters + ---------- + system : tuple of array_like or instance of `dlti` + A tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1: (instance of `dlti`) + * 3: (num, den, dt) + * 4: (zeros, poles, gain, dt) + * 5: (A, B, C, D, dt) + + u : array_like + An input array describing the input at each time `t` (interpolation is + assumed between given times). If there are multiple inputs, then each + column of the rank-2 array represents an input. + t : array_like, optional + The time steps at which the input is defined. If `t` is given, it + must be the same length as `u`, and the final value in `t` determines + the number of steps returned in the output. + x0 : array_like, optional + The initial conditions on the state vector (zero by default). + + Returns + ------- + tout : ndarray + Time values for the output, as a 1-D array. + yout : ndarray + System response, as a 1-D array. + xout : ndarray, optional + Time-evolution of the state-vector. Only generated if the input is a + `StateSpace` system. + + See Also + -------- + lsim, dstep, dimpulse, cont2discrete + + Examples + -------- + A simple integrator transfer function with a discrete time step of 1.0 + could be implemented as: + + >>> import numpy as np + >>> from scipy import signal + >>> tf = ([1.0,], [1.0, -1.0], 1.0) + >>> t_in = [0.0, 1.0, 2.0, 3.0] + >>> u = np.asarray([0.0, 0.0, 1.0, 1.0]) + >>> t_out, y = signal.dlsim(tf, u, t=t_in) + >>> y.T + array([[ 0., 0., 0., 1.]]) + + """ + # Convert system to dlti-StateSpace + if isinstance(system, lti): + raise AttributeError('dlsim can only be used with discrete-time dlti ' + 'systems.') + elif not isinstance(system, dlti): + system = dlti(*system[:-1], dt=system[-1]) + + # Condition needed to ensure output remains compatible + is_ss_input = isinstance(system, StateSpace) + system = system._as_ss() + + u = np.atleast_1d(u) + + if u.ndim == 1: + u = np.atleast_2d(u).T + + if t is None: + out_samples = len(u) + stoptime = (out_samples - 1) * system.dt + else: + stoptime = t[-1] + out_samples = int(np.floor(stoptime / system.dt)) + 1 + + # Pre-build output arrays + xout = np.zeros((out_samples, system.A.shape[0])) + yout = np.zeros((out_samples, system.C.shape[0])) + tout = np.linspace(0.0, stoptime, num=out_samples) + + # Check initial condition + if x0 is None: + xout[0, :] = np.zeros((system.A.shape[1],)) + else: + xout[0, :] = np.asarray(x0) + + # Pre-interpolate inputs into the desired time steps + if t is None: + u_dt = u + else: + if len(u.shape) == 1: + u = u[:, np.newaxis] + + u_dt = make_interp_spline(t, u, k=1)(tout) + + # Simulate the system + for i in range(0, out_samples - 1): + xout[i+1, :] = (np.dot(system.A, xout[i, :]) + + np.dot(system.B, u_dt[i, :])) + yout[i, :] = (np.dot(system.C, xout[i, :]) + + np.dot(system.D, u_dt[i, :])) + + # Last point + yout[out_samples-1, :] = (np.dot(system.C, xout[out_samples-1, :]) + + np.dot(system.D, u_dt[out_samples-1, :])) + + if is_ss_input: + return tout, yout, xout + else: + return tout, yout + + +def dimpulse(system, x0=None, t=None, n=None): + """ + Impulse response of discrete-time system. + + Parameters + ---------- + system : tuple of array_like or instance of `dlti` + A tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1: (instance of `dlti`) + * 3: (num, den, dt) + * 4: (zeros, poles, gain, dt) + * 5: (A, B, C, D, dt) + + x0 : array_like, optional + Initial state-vector. Defaults to zero. + t : array_like, optional + Time points. Computed if not given. + n : int, optional + The number of time points to compute (if `t` is not given). + + Returns + ------- + tout : ndarray + Time values for the output, as a 1-D array. + yout : tuple of ndarray + Impulse response of system. Each element of the tuple represents + the output of the system based on an impulse in each input. + + See Also + -------- + impulse, dstep, dlsim, cont2discrete + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> butter = signal.dlti(*signal.butter(3, 0.5)) + >>> t, y = signal.dimpulse(butter, n=25) + >>> plt.step(t, np.squeeze(y)) + >>> plt.grid() + >>> plt.xlabel('n [samples]') + >>> plt.ylabel('Amplitude') + + """ + # Convert system to dlti-StateSpace + if isinstance(system, dlti): + system = system._as_ss() + elif isinstance(system, lti): + raise AttributeError('dimpulse can only be used with discrete-time ' + 'dlti systems.') + else: + system = dlti(*system[:-1], dt=system[-1])._as_ss() + + # Default to 100 samples if unspecified + if n is None: + n = 100 + + # If time is not specified, use the number of samples + # and system dt + if t is None: + t = np.linspace(0, n * system.dt, n, endpoint=False) + else: + t = np.asarray(t) + + # For each input, implement a step change + yout = None + for i in range(0, system.inputs): + u = np.zeros((t.shape[0], system.inputs)) + u[0, i] = 1.0 + + one_output = dlsim(system, u, t=t, x0=x0) + + if yout is None: + yout = (one_output[1],) + else: + yout = yout + (one_output[1],) + + tout = one_output[0] + + return tout, yout + + +def dstep(system, x0=None, t=None, n=None): + """ + Step response of discrete-time system. + + Parameters + ---------- + system : tuple of array_like + A tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1: (instance of `dlti`) + * 3: (num, den, dt) + * 4: (zeros, poles, gain, dt) + * 5: (A, B, C, D, dt) + + x0 : array_like, optional + Initial state-vector. Defaults to zero. + t : array_like, optional + Time points. Computed if not given. + n : int, optional + The number of time points to compute (if `t` is not given). + + Returns + ------- + tout : ndarray + Output time points, as a 1-D array. + yout : tuple of ndarray + Step response of system. Each element of the tuple represents + the output of the system based on a step response to each input. + + See Also + -------- + step, dimpulse, dlsim, cont2discrete + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> butter = signal.dlti(*signal.butter(3, 0.5)) + >>> t, y = signal.dstep(butter, n=25) + >>> plt.step(t, np.squeeze(y)) + >>> plt.grid() + >>> plt.xlabel('n [samples]') + >>> plt.ylabel('Amplitude') + """ + # Convert system to dlti-StateSpace + if isinstance(system, dlti): + system = system._as_ss() + elif isinstance(system, lti): + raise AttributeError('dstep can only be used with discrete-time dlti ' + 'systems.') + else: + system = dlti(*system[:-1], dt=system[-1])._as_ss() + + # Default to 100 samples if unspecified + if n is None: + n = 100 + + # If time is not specified, use the number of samples + # and system dt + if t is None: + t = np.linspace(0, n * system.dt, n, endpoint=False) + else: + t = np.asarray(t) + + # For each input, implement a step change + yout = None + for i in range(0, system.inputs): + u = np.zeros((t.shape[0], system.inputs)) + u[:, i] = np.ones((t.shape[0],)) + + one_output = dlsim(system, u, t=t, x0=x0) + + if yout is None: + yout = (one_output[1],) + else: + yout = yout + (one_output[1],) + + tout = one_output[0] + + return tout, yout + + +def dfreqresp(system, w=None, n=10000, whole=False): + r""" + Calculate the frequency response of a discrete-time system. + + Parameters + ---------- + system : an instance of the `dlti` class or a tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `dlti`) + * 2 (numerator, denominator, dt) + * 3 (zeros, poles, gain, dt) + * 4 (A, B, C, D, dt) + + w : array_like, optional + Array of frequencies (in radians/sample). Magnitude and phase data is + calculated for every value in this array. If not given a reasonable + set will be calculated. + n : int, optional + Number of frequency points to compute if `w` is not given. The `n` + frequencies are logarithmically spaced in an interval chosen to + include the influence of the poles and zeros of the system. + whole : bool, optional + Normally, if 'w' is not given, frequencies are computed from 0 to the + Nyquist frequency, pi radians/sample (upper-half of unit-circle). If + `whole` is True, compute frequencies from 0 to 2*pi radians/sample. + + Returns + ------- + w : 1D ndarray + Frequency array [radians/sample] + H : 1D ndarray + Array of complex magnitude values + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``z^2 + 3z + 5`` would be represented as ``[1, 3, 5]``). + + .. versionadded:: 0.18.0 + + Examples + -------- + Generating the Nyquist plot of a transfer function + + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + Construct the transfer function + :math:`H(z) = \frac{1}{z^2 + 2z + 3}` with a sampling time of 0.05 + seconds: + + >>> sys = signal.TransferFunction([1], [1, 2, 3], dt=0.05) + + >>> w, H = signal.dfreqresp(sys) + + >>> plt.figure() + >>> plt.plot(H.real, H.imag, "b") + >>> plt.plot(H.real, -H.imag, "r") + >>> plt.show() + + """ + if not isinstance(system, dlti): + if isinstance(system, lti): + raise AttributeError('dfreqresp can only be used with ' + 'discrete-time systems.') + + system = dlti(*system[:-1], dt=system[-1]) + + if isinstance(system, StateSpace): + # No SS->ZPK code exists right now, just SS->TF->ZPK + system = system._as_tf() + + if not isinstance(system, (TransferFunction, ZerosPolesGain)): + raise ValueError('Unknown system type') + + if system.inputs != 1 or system.outputs != 1: + raise ValueError("dfreqresp requires a SISO (single input, single " + "output) system.") + + if w is not None: + worN = w + else: + worN = n + + if isinstance(system, TransferFunction): + # Convert numerator and denominator from polynomials in the variable + # 'z' to polynomials in the variable 'z^-1', as freqz expects. + num, den = TransferFunction._z_to_zinv(system.num.ravel(), system.den) + w, h = freqz(num, den, worN=worN, whole=whole) + + elif isinstance(system, ZerosPolesGain): + w, h = freqz_zpk(system.zeros, system.poles, system.gain, worN=worN, + whole=whole) + + return w, h + + +def dbode(system, w=None, n=100): + r""" + Calculate Bode magnitude and phase data of a discrete-time system. + + Parameters + ---------- + system : an instance of the LTI class or a tuple describing the system. + The following gives the number of elements in the tuple and + the interpretation: + + * 1 (instance of `dlti`) + * 2 (num, den, dt) + * 3 (zeros, poles, gain, dt) + * 4 (A, B, C, D, dt) + + w : array_like, optional + Array of frequencies (in radians/sample). Magnitude and phase data is + calculated for every value in this array. If not given a reasonable + set will be calculated. + n : int, optional + Number of frequency points to compute if `w` is not given. The `n` + frequencies are logarithmically spaced in an interval chosen to + include the influence of the poles and zeros of the system. + + Returns + ------- + w : 1D ndarray + Frequency array [rad/time_unit] + mag : 1D ndarray + Magnitude array [dB] + phase : 1D ndarray + Phase array [deg] + + Notes + ----- + If (num, den) is passed in for ``system``, coefficients for both the + numerator and denominator should be specified in descending exponent + order (e.g. ``z^2 + 3z + 5`` would be represented as ``[1, 3, 5]``). + + .. versionadded:: 0.18.0 + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + Construct the transfer function :math:`H(z) = \frac{1}{z^2 + 2z + 3}` with + a sampling time of 0.05 seconds: + + >>> sys = signal.TransferFunction([1], [1, 2, 3], dt=0.05) + + Equivalent: sys.bode() + + >>> w, mag, phase = signal.dbode(sys) + + >>> plt.figure() + >>> plt.semilogx(w, mag) # Bode magnitude plot + >>> plt.figure() + >>> plt.semilogx(w, phase) # Bode phase plot + >>> plt.show() + + """ + w, y = dfreqresp(system, w=w, n=n) + + if isinstance(system, dlti): + dt = system.dt + else: + dt = system[-1] + + mag = 20.0 * numpy.log10(abs(y)) + phase = numpy.rad2deg(numpy.unwrap(numpy.angle(y))) + + return w / dt, mag, phase diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq.py new file mode 100644 index 0000000000000000000000000000000000000000..4d64beaca86d1da50b563668679b6fc52c954ab0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq.py @@ -0,0 +1,139 @@ +# Author: Eric Larson +# 2014 + +"""Tools for MLS generation""" + +import numpy as np + +from ._max_len_seq_inner import _max_len_seq_inner + +__all__ = ['max_len_seq'] + + +# These are definitions of linear shift register taps for use in max_len_seq() +_mls_taps = {2: [1], 3: [2], 4: [3], 5: [3], 6: [5], 7: [6], 8: [7, 6, 1], + 9: [5], 10: [7], 11: [9], 12: [11, 10, 4], 13: [12, 11, 8], + 14: [13, 12, 2], 15: [14], 16: [15, 13, 4], 17: [14], + 18: [11], 19: [18, 17, 14], 20: [17], 21: [19], 22: [21], + 23: [18], 24: [23, 22, 17], 25: [22], 26: [25, 24, 20], + 27: [26, 25, 22], 28: [25], 29: [27], 30: [29, 28, 7], + 31: [28], 32: [31, 30, 10]} + +def max_len_seq(nbits, state=None, length=None, taps=None): + """ + Maximum length sequence (MLS) generator. + + Parameters + ---------- + nbits : int + Number of bits to use. Length of the resulting sequence will + be ``(2**nbits) - 1``. Note that generating long sequences + (e.g., greater than ``nbits == 16``) can take a long time. + state : array_like, optional + If array, must be of length ``nbits``, and will be cast to binary + (bool) representation. If None, a seed of ones will be used, + producing a repeatable representation. If ``state`` is all + zeros, an error is raised as this is invalid. Default: None. + length : int, optional + Number of samples to compute. If None, the entire length + ``(2**nbits) - 1`` is computed. + taps : array_like, optional + Polynomial taps to use (e.g., ``[7, 6, 1]`` for an 8-bit sequence). + If None, taps will be automatically selected (for up to + ``nbits == 32``). + + Returns + ------- + seq : array + Resulting MLS sequence of 0's and 1's. + state : array + The final state of the shift register. + + Notes + ----- + The algorithm for MLS generation is generically described in: + + https://en.wikipedia.org/wiki/Maximum_length_sequence + + The default values for taps are specifically taken from the first + option listed for each value of ``nbits`` in: + + https://web.archive.org/web/20181001062252/http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm + + .. versionadded:: 0.15.0 + + Examples + -------- + MLS uses binary convention: + + >>> from scipy.signal import max_len_seq + >>> max_len_seq(4)[0] + array([1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0], dtype=int8) + + MLS has a white spectrum (except for DC): + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from numpy.fft import fft, ifft, fftshift, fftfreq + >>> seq = max_len_seq(6)[0]*2-1 # +1 and -1 + >>> spec = fft(seq) + >>> N = len(seq) + >>> plt.plot(fftshift(fftfreq(N)), fftshift(np.abs(spec)), '.-') + >>> plt.margins(0.1, 0.1) + >>> plt.grid(True) + >>> plt.show() + + Circular autocorrelation of MLS is an impulse: + + >>> acorrcirc = ifft(spec * np.conj(spec)).real + >>> plt.figure() + >>> plt.plot(np.arange(-N/2+1, N/2+1), fftshift(acorrcirc), '.-') + >>> plt.margins(0.1, 0.1) + >>> plt.grid(True) + >>> plt.show() + + Linear autocorrelation of MLS is approximately an impulse: + + >>> acorr = np.correlate(seq, seq, 'full') + >>> plt.figure() + >>> plt.plot(np.arange(-N+1, N), acorr, '.-') + >>> plt.margins(0.1, 0.1) + >>> plt.grid(True) + >>> plt.show() + + """ + taps_dtype = np.int32 if np.intp().itemsize == 4 else np.int64 + if taps is None: + if nbits not in _mls_taps: + known_taps = np.array(list(_mls_taps.keys())) + raise ValueError(f'nbits must be between {known_taps.min()} and ' + f'{known_taps.max()} if taps is None') + taps = np.array(_mls_taps[nbits], taps_dtype) + else: + taps = np.unique(np.array(taps, taps_dtype))[::-1] + if np.any(taps < 0) or np.any(taps > nbits) or taps.size < 1: + raise ValueError('taps must be non-empty with values between ' + 'zero and nbits (inclusive)') + taps = np.array(taps) # needed for Cython and Pythran + n_max = (2**nbits) - 1 + if length is None: + length = n_max + else: + length = int(length) + if length < 0: + raise ValueError('length must be greater than or equal to 0') + # We use int8 instead of bool here because NumPy arrays of bools + # don't seem to work nicely with Cython + if state is None: + state = np.ones(nbits, dtype=np.int8, order='c') + else: + # makes a copy if need be, ensuring it's 0's and 1's + state = np.array(state, dtype=bool, order='c').astype(np.int8) + if state.ndim != 1 or state.size != nbits: + raise ValueError('state must be a 1-D array of size nbits') + if np.all(state == 0): + raise ValueError('state must not be all zeros') + + seq = np.empty(length, dtype=np.int8, order='c') + state = _max_len_seq_inner(taps, state, nbits, length, seq) + return seq, state diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq_inner.cpython-39-darwin.so b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq_inner.cpython-39-darwin.so new file mode 100644 index 0000000000000000000000000000000000000000..922a375fd3a2b76a5c3b8d9253de023d542d47cc Binary files /dev/null and b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_max_len_seq_inner.cpython-39-darwin.so differ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_peak_finding.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_peak_finding.py new file mode 100644 index 0000000000000000000000000000000000000000..3dbb04126325414e66080cac19b98cc675441ec1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_peak_finding.py @@ -0,0 +1,1312 @@ +""" +Functions for identifying peaks in signals. +""" +import math +import numpy as np + +from scipy.signal._wavelets import _cwt, _ricker +from scipy.stats import scoreatpercentile + +from ._peak_finding_utils import ( + _local_maxima_1d, + _select_by_peak_distance, + _peak_prominences, + _peak_widths +) + + +__all__ = ['argrelmin', 'argrelmax', 'argrelextrema', 'peak_prominences', + 'peak_widths', 'find_peaks', 'find_peaks_cwt'] + + +def _boolrelextrema(data, comparator, axis=0, order=1, mode='clip'): + """ + Calculate the relative extrema of `data`. + + Relative extrema are calculated by finding locations where + ``comparator(data[n], data[n+1:n+order+1])`` is True. + + Parameters + ---------- + data : ndarray + Array in which to find the relative extrema. + comparator : callable + Function to use to compare two data points. + Should take two arrays as arguments. + axis : int, optional + Axis over which to select from `data`. Default is 0. + order : int, optional + How many points on each side to use for the comparison + to consider ``comparator(n,n+x)`` to be True. + mode : str, optional + How the edges of the vector are treated. 'wrap' (wrap around) or + 'clip' (treat overflow as the same as the last (or first) element). + Default 'clip'. See numpy.take. + + Returns + ------- + extrema : ndarray + Boolean array of the same shape as `data` that is True at an extrema, + False otherwise. + + See also + -------- + argrelmax, argrelmin + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._peak_finding import _boolrelextrema + >>> testdata = np.array([1,2,3,2,1]) + >>> _boolrelextrema(testdata, np.greater, axis=0) + array([False, False, True, False, False], dtype=bool) + + """ + if (int(order) != order) or (order < 1): + raise ValueError('Order must be an int >= 1') + + datalen = data.shape[axis] + locs = np.arange(0, datalen) + + results = np.ones(data.shape, dtype=bool) + main = data.take(locs, axis=axis, mode=mode) + for shift in range(1, order + 1): + plus = data.take(locs + shift, axis=axis, mode=mode) + minus = data.take(locs - shift, axis=axis, mode=mode) + results &= comparator(main, plus) + results &= comparator(main, minus) + if ~results.any(): + return results + return results + + +def argrelmin(data, axis=0, order=1, mode='clip'): + """ + Calculate the relative minima of `data`. + + Parameters + ---------- + data : ndarray + Array in which to find the relative minima. + axis : int, optional + Axis over which to select from `data`. Default is 0. + order : int, optional + How many points on each side to use for the comparison + to consider ``comparator(n, n+x)`` to be True. + mode : str, optional + How the edges of the vector are treated. + Available options are 'wrap' (wrap around) or 'clip' (treat overflow + as the same as the last (or first) element). + Default 'clip'. See numpy.take. + + Returns + ------- + extrema : tuple of ndarrays + Indices of the minima in arrays of integers. ``extrema[k]`` is + the array of indices of axis `k` of `data`. Note that the + return value is a tuple even when `data` is 1-D. + + See Also + -------- + argrelextrema, argrelmax, find_peaks + + Notes + ----- + This function uses `argrelextrema` with np.less as comparator. Therefore, it + requires a strict inequality on both sides of a value to consider it a + minimum. This means flat minima (more than one sample wide) are not detected. + In case of 1-D `data` `find_peaks` can be used to detect all + local minima, including flat ones, by calling it with negated `data`. + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import argrelmin + >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0]) + >>> argrelmin(x) + (array([1, 5]),) + >>> y = np.array([[1, 2, 1, 2], + ... [2, 2, 0, 0], + ... [5, 3, 4, 4]]) + ... + >>> argrelmin(y, axis=1) + (array([0, 2]), array([2, 1])) + + """ + return argrelextrema(data, np.less, axis, order, mode) + + +def argrelmax(data, axis=0, order=1, mode='clip'): + """ + Calculate the relative maxima of `data`. + + Parameters + ---------- + data : ndarray + Array in which to find the relative maxima. + axis : int, optional + Axis over which to select from `data`. Default is 0. + order : int, optional + How many points on each side to use for the comparison + to consider ``comparator(n, n+x)`` to be True. + mode : str, optional + How the edges of the vector are treated. + Available options are 'wrap' (wrap around) or 'clip' (treat overflow + as the same as the last (or first) element). + Default 'clip'. See `numpy.take`. + + Returns + ------- + extrema : tuple of ndarrays + Indices of the maxima in arrays of integers. ``extrema[k]`` is + the array of indices of axis `k` of `data`. Note that the + return value is a tuple even when `data` is 1-D. + + See Also + -------- + argrelextrema, argrelmin, find_peaks + + Notes + ----- + This function uses `argrelextrema` with np.greater as comparator. Therefore, + it requires a strict inequality on both sides of a value to consider it a + maximum. This means flat maxima (more than one sample wide) are not detected. + In case of 1-D `data` `find_peaks` can be used to detect all + local maxima, including flat ones. + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import argrelmax + >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0]) + >>> argrelmax(x) + (array([3, 6]),) + >>> y = np.array([[1, 2, 1, 2], + ... [2, 2, 0, 0], + ... [5, 3, 4, 4]]) + ... + >>> argrelmax(y, axis=1) + (array([0]), array([1])) + """ + return argrelextrema(data, np.greater, axis, order, mode) + + +def argrelextrema(data, comparator, axis=0, order=1, mode='clip'): + """ + Calculate the relative extrema of `data`. + + Parameters + ---------- + data : ndarray + Array in which to find the relative extrema. + comparator : callable + Function to use to compare two data points. + Should take two arrays as arguments. + axis : int, optional + Axis over which to select from `data`. Default is 0. + order : int, optional + How many points on each side to use for the comparison + to consider ``comparator(n, n+x)`` to be True. + mode : str, optional + How the edges of the vector are treated. 'wrap' (wrap around) or + 'clip' (treat overflow as the same as the last (or first) element). + Default is 'clip'. See `numpy.take`. + + Returns + ------- + extrema : tuple of ndarrays + Indices of the maxima in arrays of integers. ``extrema[k]`` is + the array of indices of axis `k` of `data`. Note that the + return value is a tuple even when `data` is 1-D. + + See Also + -------- + argrelmin, argrelmax + + Notes + ----- + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import argrelextrema + >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0]) + >>> argrelextrema(x, np.greater) + (array([3, 6]),) + >>> y = np.array([[1, 2, 1, 2], + ... [2, 2, 0, 0], + ... [5, 3, 4, 4]]) + ... + >>> argrelextrema(y, np.less, axis=1) + (array([0, 2]), array([2, 1])) + + """ + results = _boolrelextrema(data, comparator, + axis, order, mode) + return np.nonzero(results) + + +def _arg_x_as_expected(value): + """Ensure argument `x` is a 1-D C-contiguous array of dtype('float64'). + + Used in `find_peaks`, `peak_prominences` and `peak_widths` to make `x` + compatible with the signature of the wrapped Cython functions. + + Returns + ------- + value : ndarray + A 1-D C-contiguous array with dtype('float64'). + """ + value = np.asarray(value, order='C', dtype=np.float64) + if value.ndim != 1: + raise ValueError('`x` must be a 1-D array') + return value + + +def _arg_peaks_as_expected(value): + """Ensure argument `peaks` is a 1-D C-contiguous array of dtype('intp'). + + Used in `peak_prominences` and `peak_widths` to make `peaks` compatible + with the signature of the wrapped Cython functions. + + Returns + ------- + value : ndarray + A 1-D C-contiguous array with dtype('intp'). + """ + value = np.asarray(value) + if value.size == 0: + # Empty arrays default to np.float64 but are valid input + value = np.array([], dtype=np.intp) + try: + # Safely convert to C-contiguous array of type np.intp + value = value.astype(np.intp, order='C', casting='safe', + subok=False, copy=False) + except TypeError as e: + raise TypeError("cannot safely cast `peaks` to dtype('intp')") from e + if value.ndim != 1: + raise ValueError('`peaks` must be a 1-D array') + return value + + +def _arg_wlen_as_expected(value): + """Ensure argument `wlen` is of type `np.intp` and larger than 1. + + Used in `peak_prominences` and `peak_widths`. + + Returns + ------- + value : np.intp + The original `value` rounded up to an integer or -1 if `value` was + None. + """ + if value is None: + # _peak_prominences expects an intp; -1 signals that no value was + # supplied by the user + value = -1 + elif 1 < value: + # Round up to a positive integer + if isinstance(value, float): + value = math.ceil(value) + value = np.intp(value) + else: + raise ValueError(f'`wlen` must be larger than 1, was {value}') + return value + + +def peak_prominences(x, peaks, wlen=None): + """ + Calculate the prominence of each peak in a signal. + + The prominence of a peak measures how much a peak stands out from the + surrounding baseline of the signal and is defined as the vertical distance + between the peak and its lowest contour line. + + Parameters + ---------- + x : sequence + A signal with peaks. + peaks : sequence + Indices of peaks in `x`. + wlen : int, optional + A window length in samples that optionally limits the evaluated area for + each peak to a subset of `x`. The peak is always placed in the middle of + the window therefore the given length is rounded up to the next odd + integer. This parameter can speed up the calculation (see Notes). + + Returns + ------- + prominences : ndarray + The calculated prominences for each peak in `peaks`. + left_bases, right_bases : ndarray + The peaks' bases as indices in `x` to the left and right of each peak. + The higher base of each pair is a peak's lowest contour line. + + Raises + ------ + ValueError + If a value in `peaks` is an invalid index for `x`. + + Warns + ----- + PeakPropertyWarning + For indices in `peaks` that don't point to valid local maxima in `x`, + the returned prominence will be 0 and this warning is raised. This + also happens if `wlen` is smaller than the plateau size of a peak. + + Warnings + -------- + This function may return unexpected results for data containing NaNs. To + avoid this, NaNs should either be removed or replaced. + + See Also + -------- + find_peaks + Find peaks inside a signal based on peak properties. + peak_widths + Calculate the width of peaks. + + Notes + ----- + Strategy to compute a peak's prominence: + + 1. Extend a horizontal line from the current peak to the left and right + until the line either reaches the window border (see `wlen`) or + intersects the signal again at the slope of a higher peak. An + intersection with a peak of the same height is ignored. + 2. On each side find the minimal signal value within the interval defined + above. These points are the peak's bases. + 3. The higher one of the two bases marks the peak's lowest contour line. The + prominence can then be calculated as the vertical difference between the + peaks height itself and its lowest contour line. + + Searching for the peak's bases can be slow for large `x` with periodic + behavior because large chunks or even the full signal need to be evaluated + for the first algorithmic step. This evaluation area can be limited with the + parameter `wlen` which restricts the algorithm to a window around the + current peak and can shorten the calculation time if the window length is + short in relation to `x`. + However, this may stop the algorithm from finding the true global contour + line if the peak's true bases are outside this window. Instead, a higher + contour line is found within the restricted window leading to a smaller + calculated prominence. In practice, this is only relevant for the highest set + of peaks in `x`. This behavior may even be used intentionally to calculate + "local" prominences. + + .. versionadded:: 1.1.0 + + References + ---------- + .. [1] Wikipedia Article for Topographic Prominence: + https://en.wikipedia.org/wiki/Topographic_prominence + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import find_peaks, peak_prominences + >>> import matplotlib.pyplot as plt + + Create a test signal with two overlaid harmonics + + >>> x = np.linspace(0, 6 * np.pi, 1000) + >>> x = np.sin(x) + 0.6 * np.sin(2.6 * x) + + Find all peaks and calculate prominences + + >>> peaks, _ = find_peaks(x) + >>> prominences = peak_prominences(x, peaks)[0] + >>> prominences + array([1.24159486, 0.47840168, 0.28470524, 3.10716793, 0.284603 , + 0.47822491, 2.48340261, 0.47822491]) + + Calculate the height of each peak's contour line and plot the results + + >>> contour_heights = x[peaks] - prominences + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.vlines(x=peaks, ymin=contour_heights, ymax=x[peaks]) + >>> plt.show() + + Let's evaluate a second example that demonstrates several edge cases for + one peak at index 5. + + >>> x = np.array([0, 1, 0, 3, 1, 3, 0, 4, 0]) + >>> peaks = np.array([5]) + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.show() + >>> peak_prominences(x, peaks) # -> (prominences, left_bases, right_bases) + (array([3.]), array([2]), array([6])) + + Note how the peak at index 3 of the same height is not considered as a + border while searching for the left base. Instead, two minima at 0 and 2 + are found in which case the one closer to the evaluated peak is always + chosen. On the right side, however, the base must be placed at 6 because the + higher peak represents the right border to the evaluated area. + + >>> peak_prominences(x, peaks, wlen=3.1) + (array([2.]), array([4]), array([6])) + + Here, we restricted the algorithm to a window from 3 to 7 (the length is 5 + samples because `wlen` was rounded up to the next odd integer). Thus, the + only two candidates in the evaluated area are the two neighboring samples + and a smaller prominence is calculated. + """ + x = _arg_x_as_expected(x) + peaks = _arg_peaks_as_expected(peaks) + wlen = _arg_wlen_as_expected(wlen) + return _peak_prominences(x, peaks, wlen) + + +def peak_widths(x, peaks, rel_height=0.5, prominence_data=None, wlen=None): + """ + Calculate the width of each peak in a signal. + + This function calculates the width of a peak in samples at a relative + distance to the peak's height and prominence. + + Parameters + ---------- + x : sequence + A signal with peaks. + peaks : sequence + Indices of peaks in `x`. + rel_height : float, optional + Chooses the relative height at which the peak width is measured as a + percentage of its prominence. 1.0 calculates the width of the peak at + its lowest contour line while 0.5 evaluates at half the prominence + height. Must be at least 0. See notes for further explanation. + prominence_data : tuple, optional + A tuple of three arrays matching the output of `peak_prominences` when + called with the same arguments `x` and `peaks`. This data are calculated + internally if not provided. + wlen : int, optional + A window length in samples passed to `peak_prominences` as an optional + argument for internal calculation of `prominence_data`. This argument + is ignored if `prominence_data` is given. + + Returns + ------- + widths : ndarray + The widths for each peak in samples. + width_heights : ndarray + The height of the contour lines at which the `widths` where evaluated. + left_ips, right_ips : ndarray + Interpolated positions of left and right intersection points of a + horizontal line at the respective evaluation height. + + Raises + ------ + ValueError + If `prominence_data` is supplied but doesn't satisfy the condition + ``0 <= left_base <= peak <= right_base < x.shape[0]`` for each peak, + has the wrong dtype, is not C-contiguous or does not have the same + shape. + + Warns + ----- + PeakPropertyWarning + Raised if any calculated width is 0. This may stem from the supplied + `prominence_data` or if `rel_height` is set to 0. + + Warnings + -------- + This function may return unexpected results for data containing NaNs. To + avoid this, NaNs should either be removed or replaced. + + See Also + -------- + find_peaks + Find peaks inside a signal based on peak properties. + peak_prominences + Calculate the prominence of peaks. + + Notes + ----- + The basic algorithm to calculate a peak's width is as follows: + + * Calculate the evaluation height :math:`h_{eval}` with the formula + :math:`h_{eval} = h_{Peak} - P \\cdot R`, where :math:`h_{Peak}` is the + height of the peak itself, :math:`P` is the peak's prominence and + :math:`R` a positive ratio specified with the argument `rel_height`. + * Draw a horizontal line at the evaluation height to both sides, starting at + the peak's current vertical position until the lines either intersect a + slope, the signal border or cross the vertical position of the peak's + base (see `peak_prominences` for an definition). For the first case, + intersection with the signal, the true intersection point is estimated + with linear interpolation. + * Calculate the width as the horizontal distance between the chosen + endpoints on both sides. As a consequence of this the maximal possible + width for each peak is the horizontal distance between its bases. + + As shown above to calculate a peak's width its prominence and bases must be + known. You can supply these yourself with the argument `prominence_data`. + Otherwise, they are internally calculated (see `peak_prominences`). + + .. versionadded:: 1.1.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import chirp, find_peaks, peak_widths + >>> import matplotlib.pyplot as plt + + Create a test signal with two overlaid harmonics + + >>> x = np.linspace(0, 6 * np.pi, 1000) + >>> x = np.sin(x) + 0.6 * np.sin(2.6 * x) + + Find all peaks and calculate their widths at the relative height of 0.5 + (contour line at half the prominence height) and 1 (at the lowest contour + line at full prominence height). + + >>> peaks, _ = find_peaks(x) + >>> results_half = peak_widths(x, peaks, rel_height=0.5) + >>> results_half[0] # widths + array([ 64.25172825, 41.29465463, 35.46943289, 104.71586081, + 35.46729324, 41.30429622, 181.93835853, 45.37078546]) + >>> results_full = peak_widths(x, peaks, rel_height=1) + >>> results_full[0] # widths + array([181.9396084 , 72.99284945, 61.28657872, 373.84622694, + 61.78404617, 72.48822812, 253.09161876, 79.36860878]) + + Plot signal, peaks and contour lines at which the widths where calculated + + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.hlines(*results_half[1:], color="C2") + >>> plt.hlines(*results_full[1:], color="C3") + >>> plt.show() + """ + x = _arg_x_as_expected(x) + peaks = _arg_peaks_as_expected(peaks) + if prominence_data is None: + # Calculate prominence if not supplied and use wlen if supplied. + wlen = _arg_wlen_as_expected(wlen) + prominence_data = _peak_prominences(x, peaks, wlen) + return _peak_widths(x, peaks, rel_height, *prominence_data) + + +def _unpack_condition_args(interval, x, peaks): + """ + Parse condition arguments for `find_peaks`. + + Parameters + ---------- + interval : number or ndarray or sequence + Either a number or ndarray or a 2-element sequence of the former. The + first value is always interpreted as `imin` and the second, if supplied, + as `imax`. + x : ndarray + The signal with `peaks`. + peaks : ndarray + An array with indices used to reduce `imin` and / or `imax` if those are + arrays. + + Returns + ------- + imin, imax : number or ndarray or None + Minimal and maximal value in `argument`. + + Raises + ------ + ValueError : + If interval border is given as array and its size does not match the size + of `x`. + + Notes + ----- + + .. versionadded:: 1.1.0 + """ + try: + imin, imax = interval + except (TypeError, ValueError): + imin, imax = (interval, None) + + # Reduce arrays if arrays + if isinstance(imin, np.ndarray): + if imin.size != x.size: + raise ValueError('array size of lower interval border must match x') + imin = imin[peaks] + if isinstance(imax, np.ndarray): + if imax.size != x.size: + raise ValueError('array size of upper interval border must match x') + imax = imax[peaks] + + return imin, imax + + +def _select_by_property(peak_properties, pmin, pmax): + """ + Evaluate where the generic property of peaks confirms to an interval. + + Parameters + ---------- + peak_properties : ndarray + An array with properties for each peak. + pmin : None or number or ndarray + Lower interval boundary for `peak_properties`. ``None`` is interpreted as + an open border. + pmax : None or number or ndarray + Upper interval boundary for `peak_properties`. ``None`` is interpreted as + an open border. + + Returns + ------- + keep : bool + A boolean mask evaluating to true where `peak_properties` confirms to the + interval. + + See Also + -------- + find_peaks + + Notes + ----- + + .. versionadded:: 1.1.0 + """ + keep = np.ones(peak_properties.size, dtype=bool) + if pmin is not None: + keep &= (pmin <= peak_properties) + if pmax is not None: + keep &= (peak_properties <= pmax) + return keep + + +def _select_by_peak_threshold(x, peaks, tmin, tmax): + """ + Evaluate which peaks fulfill the threshold condition. + + Parameters + ---------- + x : ndarray + A 1-D array which is indexable by `peaks`. + peaks : ndarray + Indices of peaks in `x`. + tmin, tmax : scalar or ndarray or None + Minimal and / or maximal required thresholds. If supplied as ndarrays + their size must match `peaks`. ``None`` is interpreted as an open + border. + + Returns + ------- + keep : bool + A boolean mask evaluating to true where `peaks` fulfill the threshold + condition. + left_thresholds, right_thresholds : ndarray + Array matching `peak` containing the thresholds of each peak on + both sides. + + Notes + ----- + + .. versionadded:: 1.1.0 + """ + # Stack thresholds on both sides to make min / max operations easier: + # tmin is compared with the smaller, and tmax with the greater threshold to + # each peak's side + stacked_thresholds = np.vstack([x[peaks] - x[peaks - 1], + x[peaks] - x[peaks + 1]]) + keep = np.ones(peaks.size, dtype=bool) + if tmin is not None: + min_thresholds = np.min(stacked_thresholds, axis=0) + keep &= (tmin <= min_thresholds) + if tmax is not None: + max_thresholds = np.max(stacked_thresholds, axis=0) + keep &= (max_thresholds <= tmax) + + return keep, stacked_thresholds[0], stacked_thresholds[1] + + +def find_peaks(x, height=None, threshold=None, distance=None, + prominence=None, width=None, wlen=None, rel_height=0.5, + plateau_size=None): + """ + Find peaks inside a signal based on peak properties. + + This function takes a 1-D array and finds all local maxima by + simple comparison of neighboring values. Optionally, a subset of these + peaks can be selected by specifying conditions for a peak's properties. + + Parameters + ---------- + x : sequence + A signal with peaks. + height : number or ndarray or sequence, optional + Required height of peaks. Either a number, ``None``, an array matching + `x` or a 2-element sequence of the former. The first element is + always interpreted as the minimal and the second, if supplied, as the + maximal required height. + threshold : number or ndarray or sequence, optional + Required threshold of peaks, the vertical distance to its neighboring + samples. Either a number, ``None``, an array matching `x` or a + 2-element sequence of the former. The first element is always + interpreted as the minimal and the second, if supplied, as the maximal + required threshold. + distance : number, optional + Required minimal horizontal distance (>= 1) in samples between + neighbouring peaks. Smaller peaks are removed first until the condition + is fulfilled for all remaining peaks. + prominence : number or ndarray or sequence, optional + Required prominence of peaks. Either a number, ``None``, an array + matching `x` or a 2-element sequence of the former. The first + element is always interpreted as the minimal and the second, if + supplied, as the maximal required prominence. + width : number or ndarray or sequence, optional + Required width of peaks in samples. Either a number, ``None``, an array + matching `x` or a 2-element sequence of the former. The first + element is always interpreted as the minimal and the second, if + supplied, as the maximal required width. + wlen : int, optional + Used for calculation of the peaks prominences, thus it is only used if + one of the arguments `prominence` or `width` is given. See argument + `wlen` in `peak_prominences` for a full description of its effects. + rel_height : float, optional + Used for calculation of the peaks width, thus it is only used if `width` + is given. See argument `rel_height` in `peak_widths` for a full + description of its effects. + plateau_size : number or ndarray or sequence, optional + Required size of the flat top of peaks in samples. Either a number, + ``None``, an array matching `x` or a 2-element sequence of the former. + The first element is always interpreted as the minimal and the second, + if supplied as the maximal required plateau size. + + .. versionadded:: 1.2.0 + + Returns + ------- + peaks : ndarray + Indices of peaks in `x` that satisfy all given conditions. + properties : dict + A dictionary containing properties of the returned peaks which were + calculated as intermediate results during evaluation of the specified + conditions: + + * 'peak_heights' + If `height` is given, the height of each peak in `x`. + * 'left_thresholds', 'right_thresholds' + If `threshold` is given, these keys contain a peaks vertical + distance to its neighbouring samples. + * 'prominences', 'right_bases', 'left_bases' + If `prominence` is given, these keys are accessible. See + `peak_prominences` for a description of their content. + * 'width_heights', 'left_ips', 'right_ips' + If `width` is given, these keys are accessible. See `peak_widths` + for a description of their content. + * 'plateau_sizes', left_edges', 'right_edges' + If `plateau_size` is given, these keys are accessible and contain + the indices of a peak's edges (edges are still part of the + plateau) and the calculated plateau sizes. + + .. versionadded:: 1.2.0 + + To calculate and return properties without excluding peaks, provide the + open interval ``(None, None)`` as a value to the appropriate argument + (excluding `distance`). + + Warns + ----- + PeakPropertyWarning + Raised if a peak's properties have unexpected values (see + `peak_prominences` and `peak_widths`). + + Warnings + -------- + This function may return unexpected results for data containing NaNs. To + avoid this, NaNs should either be removed or replaced. + + See Also + -------- + find_peaks_cwt + Find peaks using the wavelet transformation. + peak_prominences + Directly calculate the prominence of peaks. + peak_widths + Directly calculate the width of peaks. + + Notes + ----- + In the context of this function, a peak or local maximum is defined as any + sample whose two direct neighbours have a smaller amplitude. For flat peaks + (more than one sample of equal amplitude wide) the index of the middle + sample is returned (rounded down in case the number of samples is even). + For noisy signals the peak locations can be off because the noise might + change the position of local maxima. In those cases consider smoothing the + signal before searching for peaks or use other peak finding and fitting + methods (like `find_peaks_cwt`). + + Some additional comments on specifying conditions: + + * Almost all conditions (excluding `distance`) can be given as half-open or + closed intervals, e.g., ``1`` or ``(1, None)`` defines the half-open + interval :math:`[1, \\infty]` while ``(None, 1)`` defines the interval + :math:`[-\\infty, 1]`. The open interval ``(None, None)`` can be specified + as well, which returns the matching properties without exclusion of peaks. + * The border is always included in the interval used to select valid peaks. + * For several conditions the interval borders can be specified with + arrays matching `x` in shape which enables dynamic constrains based on + the sample position. + * The conditions are evaluated in the following order: `plateau_size`, + `height`, `threshold`, `distance`, `prominence`, `width`. In most cases + this order is the fastest one because faster operations are applied first + to reduce the number of peaks that need to be evaluated later. + * While indices in `peaks` are guaranteed to be at least `distance` samples + apart, edges of flat peaks may be closer than the allowed `distance`. + * Use `wlen` to reduce the time it takes to evaluate the conditions for + `prominence` or `width` if `x` is large or has many local maxima + (see `peak_prominences`). + + .. versionadded:: 1.1.0 + + Examples + -------- + To demonstrate this function's usage we use a signal `x` supplied with + SciPy (see `scipy.datasets.electrocardiogram`). Let's find all peaks (local + maxima) in `x` whose amplitude lies above 0. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.datasets import electrocardiogram + >>> from scipy.signal import find_peaks + >>> x = electrocardiogram()[2000:4000] + >>> peaks, _ = find_peaks(x, height=0) + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.plot(np.zeros_like(x), "--", color="gray") + >>> plt.show() + + We can select peaks below 0 with ``height=(None, 0)`` or use arrays matching + `x` in size to reflect a changing condition for different parts of the + signal. + + >>> border = np.sin(np.linspace(0, 3 * np.pi, x.size)) + >>> peaks, _ = find_peaks(x, height=(-border, border)) + >>> plt.plot(x) + >>> plt.plot(-border, "--", color="gray") + >>> plt.plot(border, ":", color="gray") + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.show() + + Another useful condition for periodic signals can be given with the + `distance` argument. In this case, we can easily select the positions of + QRS complexes within the electrocardiogram (ECG) by demanding a distance of + at least 150 samples. + + >>> peaks, _ = find_peaks(x, distance=150) + >>> np.diff(peaks) + array([186, 180, 177, 171, 177, 169, 167, 164, 158, 162, 172]) + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.show() + + Especially for noisy signals peaks can be easily grouped by their + prominence (see `peak_prominences`). E.g., we can select all peaks except + for the mentioned QRS complexes by limiting the allowed prominence to 0.6. + + >>> peaks, properties = find_peaks(x, prominence=(None, 0.6)) + >>> properties["prominences"].max() + 0.5049999999999999 + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.show() + + And, finally, let's examine a different section of the ECG which contains + beat forms of different shape. To select only the atypical heart beats, we + combine two conditions: a minimal prominence of 1 and width of at least 20 + samples. + + >>> x = electrocardiogram()[17000:18000] + >>> peaks, properties = find_peaks(x, prominence=1, width=20) + >>> properties["prominences"], properties["widths"] + (array([1.495, 2.3 ]), array([36.93773946, 39.32723577])) + >>> plt.plot(x) + >>> plt.plot(peaks, x[peaks], "x") + >>> plt.vlines(x=peaks, ymin=x[peaks] - properties["prominences"], + ... ymax = x[peaks], color = "C1") + >>> plt.hlines(y=properties["width_heights"], xmin=properties["left_ips"], + ... xmax=properties["right_ips"], color = "C1") + >>> plt.show() + """ + # _argmaxima1d expects array of dtype 'float64' + x = _arg_x_as_expected(x) + if distance is not None and distance < 1: + raise ValueError('`distance` must be greater or equal to 1') + + peaks, left_edges, right_edges = _local_maxima_1d(x) + properties = {} + + if plateau_size is not None: + # Evaluate plateau size + plateau_sizes = right_edges - left_edges + 1 + pmin, pmax = _unpack_condition_args(plateau_size, x, peaks) + keep = _select_by_property(plateau_sizes, pmin, pmax) + peaks = peaks[keep] + properties["plateau_sizes"] = plateau_sizes + properties["left_edges"] = left_edges + properties["right_edges"] = right_edges + properties = {key: array[keep] for key, array in properties.items()} + + if height is not None: + # Evaluate height condition + peak_heights = x[peaks] + hmin, hmax = _unpack_condition_args(height, x, peaks) + keep = _select_by_property(peak_heights, hmin, hmax) + peaks = peaks[keep] + properties["peak_heights"] = peak_heights + properties = {key: array[keep] for key, array in properties.items()} + + if threshold is not None: + # Evaluate threshold condition + tmin, tmax = _unpack_condition_args(threshold, x, peaks) + keep, left_thresholds, right_thresholds = _select_by_peak_threshold( + x, peaks, tmin, tmax) + peaks = peaks[keep] + properties["left_thresholds"] = left_thresholds + properties["right_thresholds"] = right_thresholds + properties = {key: array[keep] for key, array in properties.items()} + + if distance is not None: + # Evaluate distance condition + keep = _select_by_peak_distance(peaks, x[peaks], distance) + peaks = peaks[keep] + properties = {key: array[keep] for key, array in properties.items()} + + if prominence is not None or width is not None: + # Calculate prominence (required for both conditions) + wlen = _arg_wlen_as_expected(wlen) + properties.update(zip( + ['prominences', 'left_bases', 'right_bases'], + _peak_prominences(x, peaks, wlen=wlen) + )) + + if prominence is not None: + # Evaluate prominence condition + pmin, pmax = _unpack_condition_args(prominence, x, peaks) + keep = _select_by_property(properties['prominences'], pmin, pmax) + peaks = peaks[keep] + properties = {key: array[keep] for key, array in properties.items()} + + if width is not None: + # Calculate widths + properties.update(zip( + ['widths', 'width_heights', 'left_ips', 'right_ips'], + _peak_widths(x, peaks, rel_height, properties['prominences'], + properties['left_bases'], properties['right_bases']) + )) + # Evaluate width condition + wmin, wmax = _unpack_condition_args(width, x, peaks) + keep = _select_by_property(properties['widths'], wmin, wmax) + peaks = peaks[keep] + properties = {key: array[keep] for key, array in properties.items()} + + return peaks, properties + + +def _identify_ridge_lines(matr, max_distances, gap_thresh): + """ + Identify ridges in the 2-D matrix. + + Expect that the width of the wavelet feature increases with increasing row + number. + + Parameters + ---------- + matr : 2-D ndarray + Matrix in which to identify ridge lines. + max_distances : 1-D sequence + At each row, a ridge line is only connected + if the relative max at row[n] is within + `max_distances`[n] from the relative max at row[n+1]. + gap_thresh : int + If a relative maximum is not found within `max_distances`, + there will be a gap. A ridge line is discontinued if + there are more than `gap_thresh` points without connecting + a new relative maximum. + + Returns + ------- + ridge_lines : tuple + Tuple of 2 1-D sequences. `ridge_lines`[ii][0] are the rows of the + ii-th ridge-line, `ridge_lines`[ii][1] are the columns. Empty if none + found. Each ridge-line will be sorted by row (increasing), but the + order of the ridge lines is not specified. + + References + ---------- + .. [1] Bioinformatics (2006) 22 (17): 2059-2065. + :doi:`10.1093/bioinformatics/btl355` + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal._peak_finding import _identify_ridge_lines + >>> rng = np.random.default_rng() + >>> data = rng.random((5,5)) + >>> max_dist = 3 + >>> max_distances = np.full(20, max_dist) + >>> ridge_lines = _identify_ridge_lines(data, max_distances, 1) + + Notes + ----- + This function is intended to be used in conjunction with `cwt` + as part of `find_peaks_cwt`. + + """ + if len(max_distances) < matr.shape[0]: + raise ValueError('Max_distances must have at least as many rows ' + 'as matr') + + all_max_cols = _boolrelextrema(matr, np.greater, axis=1, order=1) + # Highest row for which there are any relative maxima + has_relmax = np.nonzero(all_max_cols.any(axis=1))[0] + if len(has_relmax) == 0: + return [] + start_row = has_relmax[-1] + # Each ridge line is a 3-tuple: + # rows, cols,Gap number + ridge_lines = [[[start_row], + [col], + 0] for col in np.nonzero(all_max_cols[start_row])[0]] + final_lines = [] + rows = np.arange(start_row - 1, -1, -1) + cols = np.arange(0, matr.shape[1]) + for row in rows: + this_max_cols = cols[all_max_cols[row]] + + # Increment gap number of each line, + # set it to zero later if appropriate + for line in ridge_lines: + line[2] += 1 + + # XXX These should always be all_max_cols[row] + # But the order might be different. Might be an efficiency gain + # to make sure the order is the same and avoid this iteration + prev_ridge_cols = np.array([line[1][-1] for line in ridge_lines]) + # Look through every relative maximum found at current row + # Attempt to connect them with existing ridge lines. + for ind, col in enumerate(this_max_cols): + # If there is a previous ridge line within + # the max_distance to connect to, do so. + # Otherwise start a new one. + line = None + if len(prev_ridge_cols) > 0: + diffs = np.abs(col - prev_ridge_cols) + closest = np.argmin(diffs) + if diffs[closest] <= max_distances[row]: + line = ridge_lines[closest] + if line is not None: + # Found a point close enough, extend current ridge line + line[1].append(col) + line[0].append(row) + line[2] = 0 + else: + new_line = [[row], + [col], + 0] + ridge_lines.append(new_line) + + # Remove the ridge lines with gap_number too high + # XXX Modifying a list while iterating over it. + # Should be safe, since we iterate backwards, but + # still tacky. + for ind in range(len(ridge_lines) - 1, -1, -1): + line = ridge_lines[ind] + if line[2] > gap_thresh: + final_lines.append(line) + del ridge_lines[ind] + + out_lines = [] + for line in (final_lines + ridge_lines): + sortargs = np.array(np.argsort(line[0])) + rows, cols = np.zeros_like(sortargs), np.zeros_like(sortargs) + rows[sortargs] = line[0] + cols[sortargs] = line[1] + out_lines.append([rows, cols]) + + return out_lines + + +def _filter_ridge_lines(cwt, ridge_lines, window_size=None, min_length=None, + min_snr=1, noise_perc=10): + """ + Filter ridge lines according to prescribed criteria. Intended + to be used for finding relative maxima. + + Parameters + ---------- + cwt : 2-D ndarray + Continuous wavelet transform from which the `ridge_lines` were defined. + ridge_lines : 1-D sequence + Each element should contain 2 sequences, the rows and columns + of the ridge line (respectively). + window_size : int, optional + Size of window to use to calculate noise floor. + Default is ``cwt.shape[1] / 20``. + min_length : int, optional + Minimum length a ridge line needs to be acceptable. + Default is ``cwt.shape[0] / 4``, ie 1/4-th the number of widths. + min_snr : float, optional + Minimum SNR ratio. Default 1. The signal is the value of + the cwt matrix at the shortest length scale (``cwt[0, loc]``), the + noise is the `noise_perc`\\ th percentile of datapoints contained within a + window of `window_size` around ``cwt[0, loc]``. + noise_perc : float, optional + When calculating the noise floor, percentile of data points + examined below which to consider noise. Calculated using + scipy.stats.scoreatpercentile. + + References + ---------- + .. [1] Bioinformatics (2006) 22 (17): 2059-2065. + :doi:`10.1093/bioinformatics/btl355` + + """ + num_points = cwt.shape[1] + if min_length is None: + min_length = np.ceil(cwt.shape[0] / 4) + if window_size is None: + window_size = np.ceil(num_points / 20) + + window_size = int(window_size) + hf_window, odd = divmod(window_size, 2) + + # Filter based on SNR + row_one = cwt[0, :] + noises = np.empty_like(row_one) + for ind, val in enumerate(row_one): + window_start = max(ind - hf_window, 0) + window_end = min(ind + hf_window + odd, num_points) + noises[ind] = scoreatpercentile(row_one[window_start:window_end], + per=noise_perc) + + def filt_func(line): + if len(line[0]) < min_length: + return False + snr = abs(cwt[line[0][0], line[1][0]] / noises[line[1][0]]) + if snr < min_snr: + return False + return True + + return list(filter(filt_func, ridge_lines)) + + +def find_peaks_cwt(vector, widths, wavelet=None, max_distances=None, + gap_thresh=None, min_length=None, + min_snr=1, noise_perc=10, window_size=None): + """ + Find peaks in a 1-D array with wavelet transformation. + + The general approach is to smooth `vector` by convolving it with + `wavelet(width)` for each width in `widths`. Relative maxima which + appear at enough length scales, and with sufficiently high SNR, are + accepted. + + Parameters + ---------- + vector : ndarray + 1-D array in which to find the peaks. + widths : float or sequence + Single width or 1-D array-like of widths to use for calculating + the CWT matrix. In general, + this range should cover the expected width of peaks of interest. + wavelet : callable, optional + Should take two parameters and return a 1-D array to convolve + with `vector`. The first parameter determines the number of points + of the returned wavelet array, the second parameter is the scale + (`width`) of the wavelet. Should be normalized and symmetric. + Default is the ricker wavelet. + max_distances : ndarray, optional + At each row, a ridge line is only connected if the relative max at + row[n] is within ``max_distances[n]`` from the relative max at + ``row[n+1]``. Default value is ``widths/4``. + gap_thresh : float, optional + If a relative maximum is not found within `max_distances`, + there will be a gap. A ridge line is discontinued if there are more + than `gap_thresh` points without connecting a new relative maximum. + Default is the first value of the widths array i.e. widths[0]. + min_length : int, optional + Minimum length a ridge line needs to be acceptable. + Default is ``cwt.shape[0] / 4``, ie 1/4-th the number of widths. + min_snr : float, optional + Minimum SNR ratio. Default 1. The signal is the maximum CWT coefficient + on the largest ridge line. The noise is `noise_perc` th percentile of + datapoints contained within the same ridge line. + noise_perc : float, optional + When calculating the noise floor, percentile of data points + examined below which to consider noise. Calculated using + `stats.scoreatpercentile`. Default is 10. + window_size : int, optional + Size of window to use to calculate noise floor. + Default is ``cwt.shape[1] / 20``. + + Returns + ------- + peaks_indices : ndarray + Indices of the locations in the `vector` where peaks were found. + The list is sorted. + + See Also + -------- + cwt + Continuous wavelet transform. + find_peaks + Find peaks inside a signal based on peak properties. + + Notes + ----- + This approach was designed for finding sharp peaks among noisy data, + however with proper parameter selection it should function well for + different peak shapes. + + The algorithm is as follows: + 1. Perform a continuous wavelet transform on `vector`, for the supplied + `widths`. This is a convolution of `vector` with `wavelet(width)` for + each width in `widths`. See `cwt`. + 2. Identify "ridge lines" in the cwt matrix. These are relative maxima + at each row, connected across adjacent rows. See identify_ridge_lines + 3. Filter the ridge_lines using filter_ridge_lines. + + .. versionadded:: 0.11.0 + + References + ---------- + .. [1] Bioinformatics (2006) 22 (17): 2059-2065. + :doi:`10.1093/bioinformatics/btl355` + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> xs = np.arange(0, np.pi, 0.05) + >>> data = np.sin(xs) + >>> peakind = signal.find_peaks_cwt(data, np.arange(1,10)) + >>> peakind, xs[peakind], data[peakind] + ([32], array([ 1.6]), array([ 0.9995736])) + + """ + widths = np.atleast_1d(np.asarray(widths)) + + if gap_thresh is None: + gap_thresh = np.ceil(widths[0]) + if max_distances is None: + max_distances = widths / 4.0 + if wavelet is None: + wavelet = _ricker + + cwt_dat = _cwt(vector, wavelet, widths) + ridge_lines = _identify_ridge_lines(cwt_dat, max_distances, gap_thresh) + filtered = _filter_ridge_lines(cwt_dat, ridge_lines, min_length=min_length, + window_size=window_size, min_snr=min_snr, + noise_perc=noise_perc) + max_locs = np.asarray([x[1][0] for x in filtered]) + max_locs.sort() + + return max_locs diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_savitzky_golay.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_savitzky_golay.py new file mode 100644 index 0000000000000000000000000000000000000000..339178fbbf48cad935875d32776d0f57d13f089d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_savitzky_golay.py @@ -0,0 +1,357 @@ +import numpy as np +from scipy.linalg import lstsq +from scipy._lib._util import float_factorial +from scipy.ndimage import convolve1d +from ._arraytools import axis_slice + + +def savgol_coeffs(window_length, polyorder, deriv=0, delta=1.0, pos=None, + use="conv"): + """Compute the coefficients for a 1-D Savitzky-Golay FIR filter. + + Parameters + ---------- + window_length : int + The length of the filter window (i.e., the number of coefficients). + polyorder : int + The order of the polynomial used to fit the samples. + `polyorder` must be less than `window_length`. + deriv : int, optional + The order of the derivative to compute. This must be a + nonnegative integer. The default is 0, which means to filter + the data without differentiating. + delta : float, optional + The spacing of the samples to which the filter will be applied. + This is only used if deriv > 0. + pos : int or None, optional + If pos is not None, it specifies evaluation position within the + window. The default is the middle of the window. + use : str, optional + Either 'conv' or 'dot'. This argument chooses the order of the + coefficients. The default is 'conv', which means that the + coefficients are ordered to be used in a convolution. With + use='dot', the order is reversed, so the filter is applied by + dotting the coefficients with the data set. + + Returns + ------- + coeffs : 1-D ndarray + The filter coefficients. + + See Also + -------- + savgol_filter + + Notes + ----- + .. versionadded:: 0.14.0 + + References + ---------- + A. Savitzky, M. J. E. Golay, Smoothing and Differentiation of Data by + Simplified Least Squares Procedures. Analytical Chemistry, 1964, 36 (8), + pp 1627-1639. + Jianwen Luo, Kui Ying, and Jing Bai. 2005. Savitzky-Golay smoothing and + differentiation filter for even number data. Signal Process. + 85, 7 (July 2005), 1429-1434. + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import savgol_coeffs + >>> savgol_coeffs(5, 2) + array([-0.08571429, 0.34285714, 0.48571429, 0.34285714, -0.08571429]) + >>> savgol_coeffs(5, 2, deriv=1) + array([ 2.00000000e-01, 1.00000000e-01, 2.07548111e-16, -1.00000000e-01, + -2.00000000e-01]) + + Note that use='dot' simply reverses the coefficients. + + >>> savgol_coeffs(5, 2, pos=3) + array([ 0.25714286, 0.37142857, 0.34285714, 0.17142857, -0.14285714]) + >>> savgol_coeffs(5, 2, pos=3, use='dot') + array([-0.14285714, 0.17142857, 0.34285714, 0.37142857, 0.25714286]) + >>> savgol_coeffs(4, 2, pos=3, deriv=1, use='dot') + array([0.45, -0.85, -0.65, 1.05]) + + `x` contains data from the parabola x = t**2, sampled at + t = -1, 0, 1, 2, 3. `c` holds the coefficients that will compute the + derivative at the last position. When dotted with `x` the result should + be 6. + + >>> x = np.array([1, 0, 1, 4, 9]) + >>> c = savgol_coeffs(5, 2, pos=4, deriv=1, use='dot') + >>> c.dot(x) + 6.0 + """ + + # An alternative method for finding the coefficients when deriv=0 is + # t = np.arange(window_length) + # unit = (t == pos).astype(int) + # coeffs = np.polyval(np.polyfit(t, unit, polyorder), t) + # The method implemented here is faster. + + # To recreate the table of sample coefficients shown in the chapter on + # the Savitzy-Golay filter in the Numerical Recipes book, use + # window_length = nL + nR + 1 + # pos = nL + 1 + # c = savgol_coeffs(window_length, M, pos=pos, use='dot') + + if polyorder >= window_length: + raise ValueError("polyorder must be less than window_length.") + + halflen, rem = divmod(window_length, 2) + + if pos is None: + if rem == 0: + pos = halflen - 0.5 + else: + pos = halflen + + if not (0 <= pos < window_length): + raise ValueError("pos must be nonnegative and less than " + "window_length.") + + if use not in ['conv', 'dot']: + raise ValueError("`use` must be 'conv' or 'dot'") + + if deriv > polyorder: + coeffs = np.zeros(window_length) + return coeffs + + # Form the design matrix A. The columns of A are powers of the integers + # from -pos to window_length - pos - 1. The powers (i.e., rows) range + # from 0 to polyorder. (That is, A is a vandermonde matrix, but not + # necessarily square.) + x = np.arange(-pos, window_length - pos, dtype=float) + + if use == "conv": + # Reverse so that result can be used in a convolution. + x = x[::-1] + + order = np.arange(polyorder + 1).reshape(-1, 1) + A = x ** order + + # y determines which order derivative is returned. + y = np.zeros(polyorder + 1) + # The coefficient assigned to y[deriv] scales the result to take into + # account the order of the derivative and the sample spacing. + y[deriv] = float_factorial(deriv) / (delta ** deriv) + + # Find the least-squares solution of A*c = y + coeffs, _, _, _ = lstsq(A, y) + + return coeffs + + +def _polyder(p, m): + """Differentiate polynomials represented with coefficients. + + p must be a 1-D or 2-D array. In the 2-D case, each column gives + the coefficients of a polynomial; the first row holds the coefficients + associated with the highest power. m must be a nonnegative integer. + (numpy.polyder doesn't handle the 2-D case.) + """ + + if m == 0: + result = p + else: + n = len(p) + if n <= m: + result = np.zeros_like(p[:1, ...]) + else: + dp = p[:-m].copy() + for k in range(m): + rng = np.arange(n - k - 1, m - k - 1, -1) + dp *= rng.reshape((n - m,) + (1,) * (p.ndim - 1)) + result = dp + return result + + +def _fit_edge(x, window_start, window_stop, interp_start, interp_stop, + axis, polyorder, deriv, delta, y): + """ + Given an N-d array `x` and the specification of a slice of `x` from + `window_start` to `window_stop` along `axis`, create an interpolating + polynomial of each 1-D slice, and evaluate that polynomial in the slice + from `interp_start` to `interp_stop`. Put the result into the + corresponding slice of `y`. + """ + + # Get the edge into a (window_length, -1) array. + x_edge = axis_slice(x, start=window_start, stop=window_stop, axis=axis) + if axis == 0 or axis == -x.ndim: + xx_edge = x_edge + swapped = False + else: + xx_edge = x_edge.swapaxes(axis, 0) + swapped = True + xx_edge = xx_edge.reshape(xx_edge.shape[0], -1) + + # Fit the edges. poly_coeffs has shape (polyorder + 1, -1), + # where '-1' is the same as in xx_edge. + poly_coeffs = np.polyfit(np.arange(0, window_stop - window_start), + xx_edge, polyorder) + + if deriv > 0: + poly_coeffs = _polyder(poly_coeffs, deriv) + + # Compute the interpolated values for the edge. + i = np.arange(interp_start - window_start, interp_stop - window_start) + values = np.polyval(poly_coeffs, i.reshape(-1, 1)) / (delta ** deriv) + + # Now put the values into the appropriate slice of y. + # First reshape values to match y. + shp = list(y.shape) + shp[0], shp[axis] = shp[axis], shp[0] + values = values.reshape(interp_stop - interp_start, *shp[1:]) + if swapped: + values = values.swapaxes(0, axis) + # Get a view of the data to be replaced by values. + y_edge = axis_slice(y, start=interp_start, stop=interp_stop, axis=axis) + y_edge[...] = values + + +def _fit_edges_polyfit(x, window_length, polyorder, deriv, delta, axis, y): + """ + Use polynomial interpolation of x at the low and high ends of the axis + to fill in the halflen values in y. + + This function just calls _fit_edge twice, once for each end of the axis. + """ + halflen = window_length // 2 + _fit_edge(x, 0, window_length, 0, halflen, axis, + polyorder, deriv, delta, y) + n = x.shape[axis] + _fit_edge(x, n - window_length, n, n - halflen, n, axis, + polyorder, deriv, delta, y) + + +def savgol_filter(x, window_length, polyorder, deriv=0, delta=1.0, + axis=-1, mode='interp', cval=0.0): + """ Apply a Savitzky-Golay filter to an array. + + This is a 1-D filter. If `x` has dimension greater than 1, `axis` + determines the axis along which the filter is applied. + + Parameters + ---------- + x : array_like + The data to be filtered. If `x` is not a single or double precision + floating point array, it will be converted to type ``numpy.float64`` + before filtering. + window_length : int + The length of the filter window (i.e., the number of coefficients). + If `mode` is 'interp', `window_length` must be less than or equal + to the size of `x`. + polyorder : int + The order of the polynomial used to fit the samples. + `polyorder` must be less than `window_length`. + deriv : int, optional + The order of the derivative to compute. This must be a + nonnegative integer. The default is 0, which means to filter + the data without differentiating. + delta : float, optional + The spacing of the samples to which the filter will be applied. + This is only used if deriv > 0. Default is 1.0. + axis : int, optional + The axis of the array `x` along which the filter is to be applied. + Default is -1. + mode : str, optional + Must be 'mirror', 'constant', 'nearest', 'wrap' or 'interp'. This + determines the type of extension to use for the padded signal to + which the filter is applied. When `mode` is 'constant', the padding + value is given by `cval`. See the Notes for more details on 'mirror', + 'constant', 'wrap', and 'nearest'. + When the 'interp' mode is selected (the default), no extension + is used. Instead, a degree `polyorder` polynomial is fit to the + last `window_length` values of the edges, and this polynomial is + used to evaluate the last `window_length // 2` output values. + cval : scalar, optional + Value to fill past the edges of the input if `mode` is 'constant'. + Default is 0.0. + + Returns + ------- + y : ndarray, same shape as `x` + The filtered data. + + See Also + -------- + savgol_coeffs + + Notes + ----- + Details on the `mode` options: + + 'mirror': + Repeats the values at the edges in reverse order. The value + closest to the edge is not included. + 'nearest': + The extension contains the nearest input value. + 'constant': + The extension contains the value given by the `cval` argument. + 'wrap': + The extension contains the values from the other end of the array. + + For example, if the input is [1, 2, 3, 4, 5, 6, 7, 8], and + `window_length` is 7, the following shows the extended data for + the various `mode` options (assuming `cval` is 0):: + + mode | Ext | Input | Ext + -----------+---------+------------------------+--------- + 'mirror' | 4 3 2 | 1 2 3 4 5 6 7 8 | 7 6 5 + 'nearest' | 1 1 1 | 1 2 3 4 5 6 7 8 | 8 8 8 + 'constant' | 0 0 0 | 1 2 3 4 5 6 7 8 | 0 0 0 + 'wrap' | 6 7 8 | 1 2 3 4 5 6 7 8 | 1 2 3 + + .. versionadded:: 0.14.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import savgol_filter + >>> np.set_printoptions(precision=2) # For compact display. + >>> x = np.array([2, 2, 5, 2, 1, 0, 1, 4, 9]) + + Filter with a window length of 5 and a degree 2 polynomial. Use + the defaults for all other parameters. + + >>> savgol_filter(x, 5, 2) + array([1.66, 3.17, 3.54, 2.86, 0.66, 0.17, 1. , 4. , 9. ]) + + Note that the last five values in x are samples of a parabola, so + when mode='interp' (the default) is used with polyorder=2, the last + three values are unchanged. Compare that to, for example, + `mode='nearest'`: + + >>> savgol_filter(x, 5, 2, mode='nearest') + array([1.74, 3.03, 3.54, 2.86, 0.66, 0.17, 1. , 4.6 , 7.97]) + + """ + if mode not in ["mirror", "constant", "nearest", "interp", "wrap"]: + raise ValueError("mode must be 'mirror', 'constant', 'nearest' " + "'wrap' or 'interp'.") + + x = np.asarray(x) + # Ensure that x is either single or double precision floating point. + if x.dtype != np.float64 and x.dtype != np.float32: + x = x.astype(np.float64) + + coeffs = savgol_coeffs(window_length, polyorder, deriv=deriv, delta=delta) + + if mode == "interp": + if window_length > x.shape[axis]: + raise ValueError("If mode is 'interp', window_length must be less " + "than or equal to the size of x.") + + # Do not pad. Instead, for the elements within `window_length // 2` + # of the ends of the sequence, use the polynomial that is fitted to + # the last `window_length` elements. + y = convolve1d(x, coeffs, axis=axis, mode="constant") + _fit_edges_polyfit(x, window_length, polyorder, deriv, delta, axis, y) + else: + # Any mode other than 'interp' is passed on to ndimage.convolve1d. + y = convolve1d(x, coeffs, axis=axis, mode=mode, cval=cval) + + return y diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_short_time_fft.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_short_time_fft.py new file mode 100644 index 0000000000000000000000000000000000000000..a43545957105a7ff7088dca49dbb4ea7b717d9eb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_short_time_fft.py @@ -0,0 +1,1676 @@ +"""Implementation of an FFT-based Short-time Fourier Transform. """ + +# Implementation Notes for this file (as of 2023-07) +# -------------------------------------------------- +# * MyPy version 1.1.1 does not seem to support decorated property methods +# properly. Hence, applying ``@property`` to methods decorated with `@cache`` +# (as tried with the ``lower_border_end`` method) causes a mypy error when +# accessing it as an index (e.g., ``SFT.lower_border_end[0]``). +# * Since the method `stft` and `istft` have identical names as the legacy +# functions in the signal module, referencing them as HTML link in the +# docstrings has to be done by an explicit `~ShortTimeFFT.stft` instead of an +# ambiguous `stft` (The ``~`` hides the class / module name). +# * The HTML documentation currently renders each method/property on a separate +# page without reference to the parent class. Thus, a link to `ShortTimeFFT` +# was added to the "See Also" section of each method/property. These links +# can be removed, when SciPy updates ``pydata-sphinx-theme`` to >= 0.13.3 +# (currently 0.9). Consult Issue 18512 and PR 16660 for further details. +# + +# Provides typing union operator ``|`` in Python 3.9: +from __future__ import annotations +# Linter does not allow to import ``Generator`` from ``typing`` module: +from collections.abc import Generator +from functools import cache, lru_cache, partial +from typing import Callable, get_args, Literal + +import numpy as np + +import scipy.fft as fft_lib +from scipy.signal import detrend +from scipy.signal.windows import get_window + +__all__ = ['ShortTimeFFT'] + + +#: Allowed values for parameter `padding` of method `ShortTimeFFT.stft()`: +PAD_TYPE = Literal['zeros', 'edge', 'even', 'odd'] + +#: Allowed values for property `ShortTimeFFT.fft_mode`: +FFT_MODE_TYPE = Literal['twosided', 'centered', 'onesided', 'onesided2X'] + + +def _calc_dual_canonical_window(win: np.ndarray, hop: int) -> np.ndarray: + """Calculate canonical dual window for 1d window `win` and a time step + of `hop` samples. + + A ``ValueError`` is raised, if the inversion fails. + + This is a separate function not a method, since it is also used in the + class method ``ShortTimeFFT.from_dual()``. + """ + if hop > len(win): + raise ValueError(f"{hop=} is larger than window length of {len(win)}" + + " => STFT not invertible!") + if issubclass(win.dtype.type, np.integer): + raise ValueError("Parameter 'win' cannot be of integer type, but " + + f"{win.dtype=} => STFT not invertible!") + # The calculation of `relative_resolution` does not work for ints. + # Furthermore, `win / DD` casts the integers away, thus an implicit + # cast is avoided, which can always cause confusion when using 32-Bit + # floats. + + w2 = win.real**2 + win.imag**2 # win*win.conj() does not ensure w2 is real + DD = w2.copy() + for k_ in range(hop, len(win), hop): + DD[k_:] += w2[:-k_] + DD[:-k_] += w2[k_:] + + # check DD > 0: + relative_resolution = np.finfo(win.dtype).resolution * max(DD) + if not np.all(DD >= relative_resolution): + raise ValueError("Short-time Fourier Transform not invertible!") + + return win / DD + + +# noinspection PyShadowingNames +class ShortTimeFFT: + r"""Provide a parametrized discrete Short-time Fourier transform (stft) + and its inverse (istft). + + .. currentmodule:: scipy.signal.ShortTimeFFT + + The `~ShortTimeFFT.stft` calculates sequential FFTs by sliding a + window (`win`) over an input signal by `hop` increments. It can be used to + quantify the change of the spectrum over time. + + The `~ShortTimeFFT.stft` is represented by a complex-valued matrix S[q,p] + where the p-th column represents an FFT with the window centered at the + time t[p] = p * `delta_t` = p * `hop` * `T` where `T` is the sampling + interval of the input signal. The q-th row represents the values at the + frequency f[q] = q * `delta_f` with `delta_f` = 1 / (`mfft` * `T`) being + the bin width of the FFT. + + The inverse STFT `~ShortTimeFFT.istft` is calculated by reversing the steps + of the STFT: Take the IFFT of the p-th slice of S[q,p] and multiply the + result with the so-called dual window (see `dual_win`). Shift the result by + p * `delta_t` and add the result to previous shifted results to reconstruct + the signal. If only the dual window is known and the STFT is invertible, + `from_dual` can be used to instantiate this class. + + Due to the convention of time t = 0 being at the first sample of the input + signal, the STFT values typically have negative time slots. Hence, + negative indexes like `p_min` or `k_min` do not indicate counting + backwards from an array's end like in standard Python indexing but being + left of t = 0. + + More detailed information can be found in the :ref:`tutorial_stft` section + of the :ref:`user_guide`. + + Note that all parameters of the initializer, except `scale_to` (which uses + `scaling`) have identical named attributes. + + Parameters + ---------- + win : np.ndarray + The window must be a real- or complex-valued 1d array. + hop : int + The increment in samples, by which the window is shifted in each step. + fs : float + Sampling frequency of input signal and window. Its relation to the + sampling interval `T` is ``T = 1 / fs``. + fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X' + Mode of FFT to be used (default 'onesided'). + See property `fft_mode` for details. + mfft: int | None + Length of the FFT used, if a zero padded FFT is desired. + If ``None`` (default), the length of the window `win` is used. + dual_win : np.ndarray | None + The dual window of `win`. If set to ``None``, it is calculated if + needed. + scale_to : 'magnitude', 'psd' | None + If not ``None`` (default) the window function is scaled, so each STFT + column represents either a 'magnitude' or a power spectral density + ('psd') spectrum. This parameter sets the property `scaling` to the + same value. See method `scale_to` for details. + phase_shift : int | None + If set, add a linear phase `phase_shift` / `mfft` * `f` to each + frequency `f`. The default value 0 ensures that there is no phase shift + on the zeroth slice (in which t=0 is centered). See property + `phase_shift` for more details. + + Examples + -------- + The following example shows the magnitude of the STFT of a sine with + varying frequency :math:`f_i(t)` (marked by a red dashed line in the plot): + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import ShortTimeFFT + >>> from scipy.signal.windows import gaussian + ... + >>> T_x, N = 1 / 20, 1000 # 20 Hz sampling rate for 50 s signal + >>> t_x = np.arange(N) * T_x # time indexes for signal + >>> f_i = 1 * np.arctan((t_x - t_x[N // 2]) / 2) + 5 # varying frequency + >>> x = np.sin(2*np.pi*np.cumsum(f_i)*T_x) # the signal + + The utilized Gaussian window is 50 samples or 2.5 s long. The parameter + ``mfft=200`` in `ShortTimeFFT` causes the spectrum to be oversampled + by a factor of 4: + + >>> g_std = 8 # standard deviation for Gaussian window in samples + >>> w = gaussian(50, std=g_std, sym=True) # symmetric Gaussian window + >>> SFT = ShortTimeFFT(w, hop=10, fs=1/T_x, mfft=200, scale_to='magnitude') + >>> Sx = SFT.stft(x) # perform the STFT + + In the plot, the time extent of the signal `x` is marked by vertical dashed + lines. Note that the SFT produces values outside the time range of `x`. The + shaded areas on the left and the right indicate border effects caused + by the window slices in that area not fully being inside time range of + `x`: + + >>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit + >>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot + >>> ax1.set_title(rf"STFT ({SFT.m_num*SFT.T:g}$\,s$ Gaussian window, " + + ... rf"$\sigma_t={g_std*SFT.T}\,$s)") + >>> ax1.set(xlabel=f"Time $t$ in seconds ({SFT.p_num(N)} slices, " + + ... rf"$\Delta t = {SFT.delta_t:g}\,$s)", + ... ylabel=f"Freq. $f$ in Hz ({SFT.f_pts} bins, " + + ... rf"$\Delta f = {SFT.delta_f:g}\,$Hz)", + ... xlim=(t_lo, t_hi)) + ... + >>> im1 = ax1.imshow(abs(Sx), origin='lower', aspect='auto', + ... extent=SFT.extent(N), cmap='viridis') + >>> ax1.plot(t_x, f_i, 'r--', alpha=.5, label='$f_i(t)$') + >>> fig1.colorbar(im1, label="Magnitude $|S_x(t, f)|$") + ... + >>> # Shade areas where window slices stick out to the side: + >>> for t0_, t1_ in [(t_lo, SFT.lower_border_end[0] * SFT.T), + ... (SFT.upper_border_begin(N)[0] * SFT.T, t_hi)]: + ... ax1.axvspan(t0_, t1_, color='w', linewidth=0, alpha=.2) + >>> for t_ in [0, N * SFT.T]: # mark signal borders with vertical line: + ... ax1.axvline(t_, color='y', linestyle='--', alpha=0.5) + >>> ax1.legend() + >>> fig1.tight_layout() + >>> plt.show() + + Reconstructing the signal with the `~ShortTimeFFT.istft` is + straightforward, but note that the length of `x1` should be specified, + since the SFT length increases in `hop` steps: + + >>> SFT.invertible # check if invertible + True + >>> x1 = SFT.istft(Sx, k1=N) + >>> np.allclose(x, x1) + True + + It is possible to calculate the SFT of signal parts: + + >>> p_q = SFT.nearest_k_p(N // 2) + >>> Sx0 = SFT.stft(x[:p_q]) + >>> Sx1 = SFT.stft(x[p_q:]) + + When assembling sequential STFT parts together, the overlap needs to be + considered: + + >>> p0_ub = SFT.upper_border_begin(p_q)[1] - SFT.p_min + >>> p1_le = SFT.lower_border_end[1] - SFT.p_min + >>> Sx01 = np.hstack((Sx0[:, :p0_ub], + ... Sx0[:, p0_ub:] + Sx1[:, :p1_le], + ... Sx1[:, p1_le:])) + >>> np.allclose(Sx01, Sx) # Compare with SFT of complete signal + True + + It is also possible to calculate the `itsft` for signal parts: + + >>> y_p = SFT.istft(Sx, N//3, N//2) + >>> np.allclose(y_p, x[N//3:N//2]) + True + + """ + # immutable attributes (only have getters but no setters): + _win: np.ndarray # window + _dual_win: np.ndarray | None = None # canonical dual window + _hop: int # Step of STFT in number of samples + + # mutable attributes: + _fs: float # sampling frequency of input signal and window + _fft_mode: FFT_MODE_TYPE = 'onesided' # Mode of FFT to use + _mfft: int # length of FFT used - defaults to len(win) + _scaling: Literal['magnitude', 'psd'] | None = None # Scaling of _win + _phase_shift: int | None # amount to shift phase of FFT in samples + + # attributes for caching calculated values: + _fac_mag: float | None = None + _fac_psd: float | None = None + _lower_border_end: tuple[int, int] | None = None + + def __init__(self, win: np.ndarray, hop: int, fs: float, *, + fft_mode: FFT_MODE_TYPE = 'onesided', + mfft: int | None = None, + dual_win: np.ndarray | None = None, + scale_to: Literal['magnitude', 'psd'] | None = None, + phase_shift: int | None = 0): + if not (win.ndim == 1 and win.size > 0): + raise ValueError(f"Parameter win must be 1d, but {win.shape=}!") + if not all(np.isfinite(win)): + raise ValueError("Parameter win must have finite entries!") + if not (hop >= 1 and isinstance(hop, int)): + raise ValueError(f"Parameter {hop=} is not an integer >= 1!") + self._win, self._hop, self.fs = win, hop, fs + + self.mfft = len(win) if mfft is None else mfft + + if dual_win is not None: + if dual_win.shape != win.shape: + raise ValueError(f"{dual_win.shape=} must equal {win.shape=}!") + if not all(np.isfinite(dual_win)): + raise ValueError("Parameter dual_win must be a finite array!") + self._dual_win = dual_win # needs to be set before scaling + + if scale_to is not None: # needs to be set before fft_mode + self.scale_to(scale_to) + + self.fft_mode, self.phase_shift = fft_mode, phase_shift + + @classmethod + def from_dual(cls, dual_win: np.ndarray, hop: int, fs: float, *, + fft_mode: FFT_MODE_TYPE = 'onesided', + mfft: int | None = None, + scale_to: Literal['magnitude', 'psd'] | None = None, + phase_shift: int | None = 0): + r"""Instantiate a `ShortTimeFFT` by only providing a dual window. + + If an STFT is invertible, it is possible to calculate the window `win` + from a given dual window `dual_win`. All other parameters have the + same meaning as in the initializer of `ShortTimeFFT`. + + As explained in the :ref:`tutorial_stft` section of the + :ref:`user_guide`, an invertible STFT can be interpreted as series + expansion of time-shifted and frequency modulated dual windows. E.g., + the series coefficient S[q,p] belongs to the term, which shifted + `dual_win` by p * `delta_t` and multiplied it by + exp( 2 * j * pi * t * q * `delta_f`). + + + Examples + -------- + The following example discusses decomposing a signal into time- and + frequency-shifted Gaussians. A Gaussian with standard deviation of + one made up of 51 samples will be used: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import ShortTimeFFT + >>> from scipy.signal.windows import gaussian + ... + >>> T, N = 0.1, 51 + >>> d_win = gaussian(N, std=1/T, sym=True) # symmetric Gaussian window + >>> t = T * (np.arange(N) - N//2) + ... + >>> fg1, ax1 = plt.subplots() + >>> ax1.set_title(r"Dual Window: Gaussian with $\sigma_t=1$") + >>> ax1.set(xlabel=f"Time $t$ in seconds ({N} samples, $T={T}$ s)", + ... xlim=(t[0], t[-1]), ylim=(0, 1.1*max(d_win))) + >>> ax1.plot(t, d_win, 'C0-') + + The following plot with the overlap of 41, 11 and 2 samples show how + the `hop` interval affects the shape of the window `win`: + + >>> fig2, axx = plt.subplots(3, 1, sharex='all') + ... + >>> axx[0].set_title(r"Windows for hop$\in\{10, 40, 49\}$") + >>> for c_, h_ in enumerate([10, 40, 49]): + ... SFT = ShortTimeFFT.from_dual(d_win, h_, 1/T) + ... axx[c_].plot(t + h_ * T, SFT.win, 'k--', alpha=.3, label=None) + ... axx[c_].plot(t - h_ * T, SFT.win, 'k:', alpha=.3, label=None) + ... axx[c_].plot(t, SFT.win, f'C{c_+1}', + ... label=r"$\Delta t=%0.1f\,$s" % SFT.delta_t) + ... axx[c_].set_ylim(0, 1.1*max(SFT.win)) + ... axx[c_].legend(loc='center') + >>> axx[-1].set(xlabel=f"Time $t$ in seconds ({N} samples, $T={T}$ s)", + ... xlim=(t[0], t[-1])) + >>> plt.show() + + Beside the window `win` centered at t = 0 the previous (t = -`delta_t`) + and following window (t = `delta_t`) are depicted. It can be seen that + for small `hop` intervals, the window is compact and smooth, having a + good time-frequency concentration in the STFT. For the large `hop` + interval of 4.9 s, the window has small values around t = 0, which are + not covered by the overlap of the adjacent windows, which could lead to + numeric inaccuracies. Furthermore, the peaky shape at the beginning and + the end of the window points to a higher bandwidth, resulting in a + poorer time-frequency resolution of the STFT. + Hence, the choice of the `hop` interval will be a compromise between + a time-frequency resolution and memory requirements demanded by small + `hop` sizes. + + See Also + -------- + from_window: Create instance by wrapping `get_window`. + ShortTimeFFT: Create instance using standard initializer. + """ + win = _calc_dual_canonical_window(dual_win, hop) + return cls(win=win, hop=hop, fs=fs, fft_mode=fft_mode, mfft=mfft, + dual_win=dual_win, scale_to=scale_to, + phase_shift=phase_shift) + + @classmethod + def from_window(cls, win_param: str | tuple | float, + fs: float, nperseg: int, noverlap: int, *, + symmetric_win: bool = False, + fft_mode: FFT_MODE_TYPE = 'onesided', + mfft: int | None = None, + scale_to: Literal['magnitude', 'psd'] | None = None, + phase_shift: int | None = 0): + """Instantiate `ShortTimeFFT` by using `get_window`. + + The method `get_window` is used to create a window of length + `nperseg`. The parameter names `noverlap`, and `nperseg` are used here, + since they more inline with other classical STFT libraries. + + Parameters + ---------- + win_param: Union[str, tuple, float], + Parameters passed to `get_window`. For windows with no parameters, + it may be a string (e.g., ``'hann'``), for parametrized windows a + tuple, (e.g., ``('gaussian', 2.)``) or a single float specifying + the shape parameter of a kaiser window (i.e. ``4.`` and + ``('kaiser', 4.)`` are equal. See `get_window` for more details. + fs : float + Sampling frequency of input signal. Its relation to the + sampling interval `T` is ``T = 1 / fs``. + nperseg: int + Window length in samples, which corresponds to the `m_num`. + noverlap: int + Window overlap in samples. It relates to the `hop` increment by + ``hop = npsereg - noverlap``. + symmetric_win: bool + If ``True`` then a symmetric window is generated, else a periodic + window is generated (default). Though symmetric windows seem for + most applications to be more sensible, the default of a periodic + windows was chosen to correspond to the default of `get_window`. + fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X' + Mode of FFT to be used (default 'onesided'). + See property `fft_mode` for details. + mfft: int | None + Length of the FFT used, if a zero padded FFT is desired. + If ``None`` (default), the length of the window `win` is used. + scale_to : 'magnitude', 'psd' | None + If not ``None`` (default) the window function is scaled, so each + STFT column represents either a 'magnitude' or a power spectral + density ('psd') spectrum. This parameter sets the property + `scaling` to the same value. See method `scale_to` for details. + phase_shift : int | None + If set, add a linear phase `phase_shift` / `mfft` * `f` to each + frequency `f`. The default value 0 ensures that there is no phase + shift on the zeroth slice (in which t=0 is centered). See property + `phase_shift` for more details. + + Examples + -------- + The following instances ``SFT0`` and ``SFT1`` are equivalent: + + >>> from scipy.signal import ShortTimeFFT, get_window + >>> nperseg = 9 # window length + >>> w = get_window(('gaussian', 2.), nperseg) + >>> fs = 128 # sampling frequency + >>> hop = 3 # increment of STFT time slice + >>> SFT0 = ShortTimeFFT(w, hop, fs=fs) + >>> SFT1 = ShortTimeFFT.from_window(('gaussian', 2.), fs, nperseg, + ... noverlap=nperseg-hop) + + See Also + -------- + scipy.signal.get_window: Return a window of a given length and type. + from_dual: Create instance using dual window. + ShortTimeFFT: Create instance using standard initializer. + """ + win = get_window(win_param, nperseg, fftbins=not symmetric_win) + return cls(win, hop=nperseg-noverlap, fs=fs, fft_mode=fft_mode, + mfft=mfft, scale_to=scale_to, phase_shift=phase_shift) + + @property + def win(self) -> np.ndarray: + """Window function as real- or complex-valued 1d array. + + This attribute is read only, since `dual_win` depends on it. + + See Also + -------- + dual_win: Canonical dual window. + m_num: Number of samples in window `win`. + m_num_mid: Center index of window `win`. + mfft: Length of input for the FFT used - may be larger than `m_num`. + hop: ime increment in signal samples for sliding window. + win: Window function as real- or complex-valued 1d array. + ShortTimeFFT: Class this property belongs to. + """ + return self._win + + @property + def hop(self) -> int: + """Time increment in signal samples for sliding window. + + This attribute is read only, since `dual_win` depends on it. + + See Also + -------- + delta_t: Time increment of STFT (``hop*T``) + m_num: Number of samples in window `win`. + m_num_mid: Center index of window `win`. + mfft: Length of input for the FFT used - may be larger than `m_num`. + T: Sampling interval of input signal and of the window. + win: Window function as real- or complex-valued 1d array. + ShortTimeFFT: Class this property belongs to. + """ + return self._hop + + @property + def T(self) -> float: + """Sampling interval of input signal and of the window. + + A ``ValueError`` is raised if it is set to a non-positive value. + + See Also + -------- + delta_t: Time increment of STFT (``hop*T``) + hop: Time increment in signal samples for sliding window. + fs: Sampling frequency (being ``1/T``) + t: Times of STFT for an input signal with `n` samples. + ShortTimeFFT: Class this property belongs to. + """ + return 1 / self._fs + + @T.setter + def T(self, v: float): + """Sampling interval of input signal and of the window. + + A ``ValueError`` is raised if it is set to a non-positive value. + """ + if not (v > 0): + raise ValueError(f"Sampling interval T={v} must be positive!") + self._fs = 1 / v + + @property + def fs(self) -> float: + """Sampling frequency of input signal and of the window. + + The sampling frequency is the inverse of the sampling interval `T`. + A ``ValueError`` is raised if it is set to a non-positive value. + + See Also + -------- + delta_t: Time increment of STFT (``hop*T``) + hop: Time increment in signal samples for sliding window. + T: Sampling interval of input signal and of the window (``1/fs``). + ShortTimeFFT: Class this property belongs to. + """ + return self._fs + + @fs.setter + def fs(self, v: float): + """Sampling frequency of input signal and of the window. + + The sampling frequency is the inverse of the sampling interval `T`. + A ``ValueError`` is raised if it is set to a non-positive value. + """ + if not (v > 0): + raise ValueError(f"Sampling frequency fs={v} must be positive!") + self._fs = v + + @property + def fft_mode(self) -> FFT_MODE_TYPE: + """Mode of utilized FFT ('twosided', 'centered', 'onesided' or + 'onesided2X'). + + It can have the following values: + + 'twosided': + Two-sided FFT, where values for the negative frequencies are in + upper half of the array. Corresponds to :func:`~scipy.fft.fft()`. + 'centered': + Two-sided FFT with the values being ordered along monotonically + increasing frequencies. Corresponds to applying + :func:`~scipy.fft.fftshift()` to :func:`~scipy.fft.fft()`. + 'onesided': + Calculates only values for non-negative frequency values. + Corresponds to :func:`~scipy.fft.rfft()`. + 'onesided2X': + Like `onesided`, but the non-zero frequencies are doubled if + `scaling` is set to 'magnitude' or multiplied by ``sqrt(2)`` if + set to 'psd'. If `scaling` is ``None``, setting `fft_mode` to + `onesided2X` is not allowed. + If the FFT length `mfft` is even, the last FFT value is not paired, + and thus it is not scaled. + + Note that `onesided` and `onesided2X` do not work for complex-valued signals or + complex-valued windows. Furthermore, the frequency values can be obtained by + reading the `f` property, and the number of samples by accessing the `f_pts` + property. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + f: Frequencies values of the STFT. + f_pts: Width of the frequency bins of the STFT. + onesided_fft: True if a one-sided FFT is used. + scaling: Normalization applied to the window function + ShortTimeFFT: Class this property belongs to. + """ + return self._fft_mode + + @fft_mode.setter + def fft_mode(self, t: FFT_MODE_TYPE): + """Set mode of FFT. + + Allowed values are 'twosided', 'centered', 'onesided', 'onesided2X'. + See the property `fft_mode` for more details. + """ + if t not in (fft_mode_types := get_args(FFT_MODE_TYPE)): + raise ValueError(f"fft_mode='{t}' not in {fft_mode_types}!") + + if t in {'onesided', 'onesided2X'} and np.iscomplexobj(self.win): + raise ValueError(f"One-sided spectra, i.e., fft_mode='{t}', " + + "are not allowed for complex-valued windows!") + + if t == 'onesided2X' and self.scaling is None: + raise ValueError(f"For scaling is None, fft_mode='{t}' is invalid!" + "Do scale_to('psd') or scale_to('magnitude')!") + self._fft_mode = t + + @property + def mfft(self) -> int: + """Length of input for the FFT used - may be larger than window + length `m_num`. + + If not set, `mfft` defaults to the window length `m_num`. + + See Also + -------- + f_pts: Number of points along the frequency axis. + f: Frequencies values of the STFT. + m_num: Number of samples in window `win`. + ShortTimeFFT: Class this property belongs to. + """ + return self._mfft + + @mfft.setter + def mfft(self, n_: int): + """Setter for the length of FFT utilized. + + See the property `mfft` for further details. + """ + if not (n_ >= self.m_num): + raise ValueError(f"Attribute mfft={n_} needs to be at least the " + + f"window length m_num={self.m_num}!") + self._mfft = n_ + + @property + def scaling(self) -> Literal['magnitude', 'psd'] | None: + """Normalization applied to the window function + ('magnitude', 'psd' or ``None``). + + If not ``None``, the FFTs can be either interpreted as a magnitude or + a power spectral density spectrum. + + The window function can be scaled by calling the `scale_to` method, + or it is set by the initializer parameter ``scale_to``. + + See Also + -------- + fac_magnitude: Scaling factor for to a magnitude spectrum. + fac_psd: Scaling factor for to a power spectral density spectrum. + fft_mode: Mode of utilized FFT + scale_to: Scale window to obtain 'magnitude' or 'psd' scaling. + ShortTimeFFT: Class this property belongs to. + """ + return self._scaling + + def scale_to(self, scaling: Literal['magnitude', 'psd']): + """Scale window to obtain 'magnitude' or 'psd' scaling for the STFT. + + The window of a 'magnitude' spectrum has an integral of one, i.e., unit + area for non-negative windows. This ensures that absolute the values of + spectrum does not change if the length of the window changes (given + the input signal is stationary). + + To represent the power spectral density ('psd') for varying length + windows the area of the absolute square of the window needs to be + unity. + + The `scaling` property shows the current scaling. The properties + `fac_magnitude` and `fac_psd` show the scaling factors required to + scale the STFT values to a magnitude or a psd spectrum. + + This method is called, if the initializer parameter `scale_to` is set. + + See Also + -------- + fac_magnitude: Scaling factor for to a magnitude spectrum. + fac_psd: Scaling factor for to a power spectral density spectrum. + fft_mode: Mode of utilized FFT + scaling: Normalization applied to the window function. + ShortTimeFFT: Class this method belongs to. + """ + if scaling not in (scaling_values := {'magnitude', 'psd'}): + raise ValueError(f"{scaling=} not in {scaling_values}!") + if self._scaling == scaling: # do nothing + return + + s_fac = self.fac_psd if scaling == 'psd' else self.fac_magnitude + self._win = self._win * s_fac + if self._dual_win is not None: + self._dual_win = self._dual_win / s_fac + self._fac_mag, self._fac_psd = None, None # reset scaling factors + self._scaling = scaling + + @property + def phase_shift(self) -> int | None: + """If set, add linear phase `phase_shift` / `mfft` * `f` to each FFT + slice of frequency `f`. + + Shifting (more precisely `rolling`) an `mfft`-point FFT input by + `phase_shift` samples results in a multiplication of the output by + ``np.exp(2j*np.pi*q*phase_shift/mfft)`` at the frequency q * `delta_f`. + + The default value 0 ensures that there is no phase shift on the + zeroth slice (in which t=0 is centered). + No phase shift (``phase_shift is None``) is equivalent to + ``phase_shift = -mfft//2``. In this case slices are not shifted + before calculating the FFT. + + The absolute value of `phase_shift` is limited to be less than `mfft`. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + f: Frequencies values of the STFT. + mfft: Length of input for the FFT used + ShortTimeFFT: Class this property belongs to. + """ + return self._phase_shift + + @phase_shift.setter + def phase_shift(self, v: int | None): + """The absolute value of the phase shift needs to be less than mfft + samples. + + See the `phase_shift` getter method for more details. + """ + if v is None: + self._phase_shift = v + return + if not isinstance(v, int): + raise ValueError(f"phase_shift={v} has the unit samples. Hence " + + "it needs to be an int or it may be None!") + if not (-self.mfft < v < self.mfft): + raise ValueError("-mfft < phase_shift < mfft does not hold " + + f"for mfft={self.mfft}, phase_shift={v}!") + self._phase_shift = v + + def _x_slices(self, x: np.ndarray, k_off: int, p0: int, p1: int, + padding: PAD_TYPE) -> Generator[np.ndarray, None, None]: + """Generate signal slices along last axis of `x`. + + This method is only used by `stft_detrend`. The parameters are + described in `~ShortTimeFFT.stft`. + """ + if padding not in (padding_types := get_args(PAD_TYPE)): + raise ValueError(f"Parameter {padding=} not in {padding_types}!") + pad_kws: dict[str, dict] = { # possible keywords to pass to np.pad: + 'zeros': dict(mode='constant', constant_values=(0, 0)), + 'edge': dict(mode='edge'), + 'even': dict(mode='reflect', reflect_type='even'), + 'odd': dict(mode='reflect', reflect_type='odd'), + } # typing of pad_kws is needed to make mypy happy + + n, n1 = x.shape[-1], (p1 - p0) * self.hop + k0 = p0 * self.hop - self.m_num_mid + k_off # start sample + k1 = k0 + n1 + self.m_num # end sample + + i0, i1 = max(k0, 0), min(k1, n) # indexes to shorten x + # dimensions for padding x: + pad_width = [(0, 0)] * (x.ndim-1) + [(-min(k0, 0), max(k1 - n, 0))] + + x1 = np.pad(x[..., i0:i1], pad_width, **pad_kws[padding]) + for k_ in range(0, n1, self.hop): + yield x1[..., k_:k_ + self.m_num] + + def stft(self, x: np.ndarray, p0: int | None = None, + p1: int | None = None, *, k_offset: int = 0, + padding: PAD_TYPE = 'zeros', axis: int = -1) \ + -> np.ndarray: + """Perform the short-time Fourier transform. + + A two-dimensional matrix with ``p1-p0`` columns is calculated. + The `f_pts` rows represent value at the frequencies `f`. The q-th + column of the windowed FFT with the window `win` is centered at t[q]. + The columns represent the values at the frequencies `f`. + + Parameters + ---------- + x + The input signal as real or complex valued array. For complex values, the + property `fft_mode` must be set to 'twosided' or 'centered'. + p0 + The first element of the range of slices to calculate. If ``None`` + then it is set to :attr:`p_min`, which is the smallest possible + slice. + p1 + The end of the array. If ``None`` then `p_max(n)` is used. + k_offset + Index of first sample (t = 0) in `x`. + padding + Kind of values which are added, when the sliding window sticks out + on either the lower or upper end of the input `x`. Zeros are added + if the default 'zeros' is set. For 'edge' either the first or the + last value of `x` is used. 'even' pads by reflecting the + signal on the first or last sample and 'odd' additionally + multiplies it with -1. + axis + The axis of `x` over which to compute the STFT. + If not given, the last axis is used. + + Returns + ------- + S + A complex array is returned with the dimension always being larger + by one than of `x`. The last axis always represent the time slices + of the STFT. `axis` defines the frequency axis (default second to + last). E.g., for a one-dimensional `x`, a complex 2d array is + returned, with axis 0 representing frequency and axis 1 the time + slices. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + delta_t: Time increment of STFT + f: Frequencies values of the STFT. + invertible: Check if STFT is invertible. + :meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform. + p_range: Determine and validate slice index range. + stft_detrend: STFT with detrended segments. + t: Times of STFT for an input signal with `n` samples. + :class:`scipy.signal.ShortTimeFFT`: Class this method belongs to. + """ + return self.stft_detrend(x, None, p0, p1, k_offset=k_offset, + padding=padding, axis=axis) + + def stft_detrend(self, x: np.ndarray, + detr: Callable[[np.ndarray], np.ndarray] | Literal['linear', 'constant'] | None, # noqa: E501 + p0: int | None = None, p1: int | None = None, *, + k_offset: int = 0, padding: PAD_TYPE = 'zeros', + axis: int = -1) \ + -> np.ndarray: + """Short-time Fourier transform with a trend being subtracted from each + segment beforehand. + + If `detr` is set to 'constant', the mean is subtracted, if set to + "linear", the linear trend is removed. This is achieved by calling + :func:`scipy.signal.detrend`. If `detr` is a function, `detr` is + applied to each segment. + All other parameters have the same meaning as in `~ShortTimeFFT.stft`. + + Note that due to the detrending, the original signal cannot be + reconstructed by the `~ShortTimeFFT.istft`. + + See Also + -------- + invertible: Check if STFT is invertible. + :meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform. + :meth:`~ShortTimeFFT.stft`: Short-time Fourier transform + (without detrending). + :class:`scipy.signal.ShortTimeFFT`: Class this method belongs to. + """ + if self.onesided_fft and np.iscomplexobj(x): + raise ValueError(f"Complex-valued `x` not allowed for {self.fft_mode=}'! " + "Set property `fft_mode` to 'twosided' or 'centered'.") + if isinstance(detr, str): + detr = partial(detrend, type=detr) + elif not (detr is None or callable(detr)): + raise ValueError(f"Parameter {detr=} is not a str, function or " + + "None!") + n = x.shape[axis] + if not (n >= (m2p := self.m_num-self.m_num_mid)): + e_str = f'{len(x)=}' if x.ndim == 1 else f'of {axis=} of {x.shape}' + raise ValueError(f"{e_str} must be >= ceil(m_num/2) = {m2p}!") + + if x.ndim > 1: # motivated by the NumPy broadcasting mechanisms: + x = np.moveaxis(x, axis, -1) + # determine slice index range: + p0, p1 = self.p_range(n, p0, p1) + S_shape_1d = (self.f_pts, p1 - p0) + S_shape = x.shape[:-1] + S_shape_1d if x.ndim > 1 else S_shape_1d + S = np.zeros(S_shape, dtype=complex) + for p_, x_ in enumerate(self._x_slices(x, k_offset, p0, p1, padding)): + if detr is not None: + x_ = detr(x_) + S[..., :, p_] = self._fft_func(x_ * self.win.conj()) + if x.ndim > 1: + return np.moveaxis(S, -2, axis if axis >= 0 else axis-1) + return S + + def spectrogram(self, x: np.ndarray, y: np.ndarray | None = None, + detr: Callable[[np.ndarray], np.ndarray] | Literal['linear', 'constant'] | None = None, # noqa: E501 + *, + p0: int | None = None, p1: int | None = None, + k_offset: int = 0, padding: PAD_TYPE = 'zeros', + axis: int = -1) \ + -> np.ndarray: + r"""Calculate spectrogram or cross-spectrogram. + + The spectrogram is the absolute square of the STFT, i.e, it is + ``abs(S[q,p])**2`` for given ``S[q,p]`` and thus is always + non-negative. + For two STFTs ``Sx[q,p], Sy[q,p]``, the cross-spectrogram is defined + as ``Sx[q,p] * np.conj(Sx[q,p])`` and is complex-valued. + This is a convenience function for calling `~ShortTimeFFT.stft` / + `stft_detrend`, hence all parameters are discussed there. If `y` is not + ``None`` it needs to have the same shape as `x`. + + Examples + -------- + The following example shows the spectrogram of a square wave with + varying frequency :math:`f_i(t)` (marked by a green dashed line in the + plot) sampled with 20 Hz: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> from scipy.signal import square, ShortTimeFFT + >>> from scipy.signal.windows import gaussian + ... + >>> T_x, N = 1 / 20, 1000 # 20 Hz sampling rate for 50 s signal + >>> t_x = np.arange(N) * T_x # time indexes for signal + >>> f_i = 5e-3*(t_x - t_x[N // 3])**2 + 1 # varying frequency + >>> x = square(2*np.pi*np.cumsum(f_i)*T_x) # the signal + + The utitlized Gaussian window is 50 samples or 2.5 s long. The + parameter ``mfft=800`` (oversampling factor 16) and the `hop` interval + of 2 in `ShortTimeFFT` was chosen to produce a sufficient number of + points: + + >>> g_std = 12 # standard deviation for Gaussian window in samples + >>> win = gaussian(50, std=g_std, sym=True) # symmetric Gaussian wind. + >>> SFT = ShortTimeFFT(win, hop=2, fs=1/T_x, mfft=800, scale_to='psd') + >>> Sx2 = SFT.spectrogram(x) # calculate absolute square of STFT + + The plot's colormap is logarithmically scaled as the power spectral + density is in dB. The time extent of the signal `x` is marked by + vertical dashed lines and the shaded areas mark the presence of border + effects: + + >>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit + >>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot + >>> ax1.set_title(rf"Spectrogram ({SFT.m_num*SFT.T:g}$\,s$ Gaussian " + + ... rf"window, $\sigma_t={g_std*SFT.T:g}\,$s)") + >>> ax1.set(xlabel=f"Time $t$ in seconds ({SFT.p_num(N)} slices, " + + ... rf"$\Delta t = {SFT.delta_t:g}\,$s)", + ... ylabel=f"Freq. $f$ in Hz ({SFT.f_pts} bins, " + + ... rf"$\Delta f = {SFT.delta_f:g}\,$Hz)", + ... xlim=(t_lo, t_hi)) + >>> Sx_dB = 10 * np.log10(np.fmax(Sx2, 1e-4)) # limit range to -40 dB + >>> im1 = ax1.imshow(Sx_dB, origin='lower', aspect='auto', + ... extent=SFT.extent(N), cmap='magma') + >>> ax1.plot(t_x, f_i, 'g--', alpha=.5, label='$f_i(t)$') + >>> fig1.colorbar(im1, label='Power Spectral Density ' + + ... r"$20\,\log_{10}|S_x(t, f)|$ in dB") + ... + >>> # Shade areas where window slices stick out to the side: + >>> for t0_, t1_ in [(t_lo, SFT.lower_border_end[0] * SFT.T), + ... (SFT.upper_border_begin(N)[0] * SFT.T, t_hi)]: + ... ax1.axvspan(t0_, t1_, color='w', linewidth=0, alpha=.3) + >>> for t_ in [0, N * SFT.T]: # mark signal borders with vertical line + ... ax1.axvline(t_, color='c', linestyle='--', alpha=0.5) + >>> ax1.legend() + >>> fig1.tight_layout() + >>> plt.show() + + The logarithmic scaling reveals the odd harmonics of the square wave, + which are reflected at the Nyquist frequency of 10 Hz. This aliasing + is also the main source of the noise artifacts in the plot. + + + See Also + -------- + :meth:`~ShortTimeFFT.stft`: Perform the short-time Fourier transform. + stft_detrend: STFT with a trend subtracted from each segment. + :class:`scipy.signal.ShortTimeFFT`: Class this method belongs to. + """ + Sx = self.stft_detrend(x, detr, p0, p1, k_offset=k_offset, + padding=padding, axis=axis) + if y is None or y is x: # do spectrogram: + return Sx.real**2 + Sx.imag**2 + # Cross-spectrogram: + Sy = self.stft_detrend(y, detr, p0, p1, k_offset=k_offset, + padding=padding, axis=axis) + return Sx * Sy.conj() + + @property + def dual_win(self) -> np.ndarray: + """Canonical dual window. + + A STFT can be interpreted as the input signal being expressed as a + weighted sum of modulated and time-shifted dual windows. Note that for + a given window there exist many dual windows. The canonical window is + the one with the minimal energy (i.e., :math:`L_2` norm). + + `dual_win` has same length as `win`, namely `m_num` samples. + + If the dual window cannot be calculated a ``ValueError`` is raised. + This attribute is read only and calculated lazily. + + See Also + -------- + dual_win: Canonical dual window. + m_num: Number of samples in window `win`. + win: Window function as real- or complex-valued 1d array. + ShortTimeFFT: Class this property belongs to. + """ + if self._dual_win is None: + self._dual_win = _calc_dual_canonical_window(self.win, self.hop) + return self._dual_win + + @property + def invertible(self) -> bool: + """Check if STFT is invertible. + + This is achieved by trying to calculate the canonical dual window. + + See Also + -------- + :meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform. + m_num: Number of samples in window `win` and `dual_win`. + dual_win: Canonical dual window. + win: Window for STFT. + ShortTimeFFT: Class this property belongs to. + """ + try: + return len(self.dual_win) > 0 # call self.dual_win() + except ValueError: + return False + + def istft(self, S: np.ndarray, k0: int = 0, k1: int | None = None, *, + f_axis: int = -2, t_axis: int = -1) \ + -> np.ndarray: + """Inverse short-time Fourier transform. + + It returns an array of dimension ``S.ndim - 1`` which is real + if `onesided_fft` is set, else complex. If the STFT is not + `invertible`, or the parameters are out of bounds a ``ValueError`` is + raised. + + Parameters + ---------- + S + A complex valued array where `f_axis` denotes the frequency + values and the `t-axis` dimension the temporal values of the + STFT values. + k0, k1 + The start and the end index of the reconstructed signal. The + default (``k0 = 0``, ``k1 = None``) assumes that the maximum length + signal should be reconstructed. + f_axis, t_axis + The axes in `S` denoting the frequency and the time dimension. + + Notes + ----- + It is required that `S` has `f_pts` entries along the `f_axis`. For + the `t_axis` it is assumed that the first entry corresponds to + `p_min` * `delta_t` (being <= 0). The length of `t_axis` needs to be + compatible with `k1`. I.e., ``S.shape[t_axis] >= self.p_max(k1)`` must + hold, if `k1` is not ``None``. Else `k1` is set to `k_max` with:: + + q_max = S.shape[t_range] + self.p_min + k_max = (q_max - 1) * self.hop + self.m_num - self.m_num_mid + + The :ref:`tutorial_stft` section of the :ref:`user_guide` discussed the + slicing behavior by means of an example. + + See Also + -------- + invertible: Check if STFT is invertible. + :meth:`~ShortTimeFFT.stft`: Perform Short-time Fourier transform. + :class:`scipy.signal.ShortTimeFFT`: Class this method belongs to. + """ + if f_axis == t_axis: + raise ValueError(f"{f_axis=} may not be equal to {t_axis=}!") + if S.shape[f_axis] != self.f_pts: + raise ValueError(f"{S.shape[f_axis]=} must be equal to " + + f"{self.f_pts=} ({S.shape=})!") + n_min = self.m_num-self.m_num_mid # minimum signal length + if not (S.shape[t_axis] >= (q_num := self.p_num(n_min))): + raise ValueError(f"{S.shape[t_axis]=} needs to have at least " + + f"{q_num} slices ({S.shape=})!") + if t_axis != S.ndim - 1 or f_axis != S.ndim - 2: + t_axis = S.ndim + t_axis if t_axis < 0 else t_axis + f_axis = S.ndim + f_axis if f_axis < 0 else f_axis + S = np.moveaxis(S, (f_axis, t_axis), (-2, -1)) + + q_max = S.shape[-1] + self.p_min + k_max = (q_max - 1) * self.hop + self.m_num - self.m_num_mid + + k1 = k_max if k1 is None else k1 + if not (self.k_min <= k0 < k1 <= k_max): + raise ValueError(f"({self.k_min=}) <= ({k0=}) < ({k1=}) <= " + + f"({k_max=}) is false!") + if not (num_pts := k1 - k0) >= n_min: + raise ValueError(f"({k1=}) - ({k0=}) = {num_pts} has to be at " + + f"least the half the window length {n_min}!") + + q0 = (k0 // self.hop + self.p_min if k0 >= 0 else # p_min always <= 0 + k0 // self.hop) + q1 = min(self.p_max(k1), q_max) + k_q0, k_q1 = self.nearest_k_p(k0), self.nearest_k_p(k1, left=False) + n_pts = k_q1 - k_q0 + self.m_num - self.m_num_mid + x = np.zeros(S.shape[:-2] + (n_pts,), + dtype=float if self.onesided_fft else complex) + for q_ in range(q0, q1): + xs = self._ifft_func(S[..., :, q_ - self.p_min]) * self.dual_win + i0 = q_ * self.hop - self.m_num_mid + i1 = min(i0 + self.m_num, n_pts+k0) + j0, j1 = 0, i1 - i0 + if i0 < k0: # xs sticks out to the left on x: + j0 += k0 - i0 + i0 = k0 + x[..., i0-k0:i1-k0] += xs[..., j0:j1] + x = x[..., :k1-k0] + if x.ndim > 1: + x = np.moveaxis(x, -1, f_axis if f_axis < x.ndim else t_axis) + return x + + @property + def fac_magnitude(self) -> float: + """Factor to multiply the STFT values by to scale each frequency slice + to a magnitude spectrum. + + It is 1 if attribute ``scaling == 'magnitude'``. + The window can be scaled to a magnitude spectrum by using the method + `scale_to`. + + See Also + -------- + fac_psd: Scaling factor for to a power spectral density spectrum. + scale_to: Scale window to obtain 'magnitude' or 'psd' scaling. + scaling: Normalization applied to the window function. + ShortTimeFFT: Class this property belongs to. + """ + if self.scaling == 'magnitude': + return 1 + if self._fac_mag is None: + self._fac_mag = 1 / abs(sum(self.win)) + return self._fac_mag + + @property + def fac_psd(self) -> float: + """Factor to multiply the STFT values by to scale each frequency slice + to a power spectral density (PSD). + + It is 1 if attribute ``scaling == 'psd'``. + The window can be scaled to a psd spectrum by using the method + `scale_to`. + + See Also + -------- + fac_magnitude: Scaling factor for to a magnitude spectrum. + scale_to: Scale window to obtain 'magnitude' or 'psd' scaling. + scaling: Normalization applied to the window function. + ShortTimeFFT: Class this property belongs to. + """ + if self.scaling == 'psd': + return 1 + if self._fac_psd is None: + self._fac_psd = 1 / np.sqrt( + sum(self.win.real**2+self.win.imag**2) / self.T) + return self._fac_psd + + @property + def m_num(self) -> int: + """Number of samples in window `win`. + + Note that the FFT can be oversampled by zero-padding. This is achieved + by setting the `mfft` property. + + See Also + -------- + m_num_mid: Center index of window `win`. + mfft: Length of input for the FFT used - may be larger than `m_num`. + hop: Time increment in signal samples for sliding window. + win: Window function as real- or complex-valued 1d array. + ShortTimeFFT: Class this property belongs to. + """ + return len(self.win) + + @property + def m_num_mid(self) -> int: + """Center index of window `win`. + + For odd `m_num`, ``(m_num - 1) / 2`` is returned and + for even `m_num` (per definition) ``m_num / 2`` is returned. + + See Also + -------- + m_num: Number of samples in window `win`. + mfft: Length of input for the FFT used - may be larger than `m_num`. + hop: ime increment in signal samples for sliding window. + win: Window function as real- or complex-valued 1d array. + ShortTimeFFT: Class this property belongs to. + """ + return self.m_num // 2 + + @cache + def _pre_padding(self) -> tuple[int, int]: + """Smallest signal index and slice index due to padding. + + Since, per convention, for time t=0, n,q is zero, the returned values + are negative or zero. + """ + w2 = self.win.real**2 + self.win.imag**2 + # move window to the left until the overlap with t >= 0 vanishes: + n0 = -self.m_num_mid + for q_, n_ in enumerate(range(n0, n0-self.m_num-1, -self.hop)): + n_next = n_ - self.hop + if n_next + self.m_num <= 0 or all(w2[n_next:] == 0): + return n_, -q_ + raise RuntimeError("This is code line should not have been reached!") + # If this case is reached, it probably means the first slice should be + # returned, i.e.: return n0, 0 + + @property + def k_min(self) -> int: + """The smallest possible signal index of the STFT. + + `k_min` is the index of the left-most non-zero value of the lowest + slice `p_min`. Since the zeroth slice is centered over the zeroth + sample of the input signal, `k_min` is never positive. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_max: First sample index after signal end not touched by a time slice. + lower_border_end: Where pre-padding effects end. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + upper_border_begin: Where post-padding effects start. + ShortTimeFFT: Class this property belongs to. + """ + return self._pre_padding()[0] + + @property + def p_min(self) -> int: + """The smallest possible slice index. + + `p_min` is the index of the left-most slice, where the window still + sticks into the signal, i.e., has non-zero part for t >= 0. + `k_min` is the smallest index where the window function of the slice + `p_min` is non-zero. + + Since, per convention the zeroth slice is centered at t=0, + `p_min` <= 0 always holds. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + ShortTimeFFT: Class this property belongs to. + """ + return self._pre_padding()[1] + + @lru_cache(maxsize=256) + def _post_padding(self, n: int) -> tuple[int, int]: + """Largest signal index and slice index due to padding.""" + w2 = self.win.real**2 + self.win.imag**2 + # move window to the right until the overlap for t < t[n] vanishes: + q1 = n // self.hop # last slice index with t[p1] <= t[n] + k1 = q1 * self.hop - self.m_num_mid + for q_, k_ in enumerate(range(k1, n+self.m_num, self.hop), start=q1): + n_next = k_ + self.hop + if n_next >= n or all(w2[:n-n_next] == 0): + return k_ + self.m_num, q_ + 1 + raise RuntimeError("This is code line should not have been reached!") + # If this case is reached, it probably means the last slice should be + # returned, i.e.: return k1 + self.m_num - self.m_num_mid, q1 + 1 + + def k_max(self, n: int) -> int: + """First sample index after signal end not touched by a time slice. + + `k_max` - 1 is the largest sample index of the slice `p_max` for a + given input signal of `n` samples. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + ShortTimeFFT: Class this method belongs to. + """ + return self._post_padding(n)[0] + + def p_max(self, n: int) -> int: + """Index of first non-overlapping upper time slice for `n` sample + input. + + Note that center point t[p_max] = (p_max(n)-1) * `delta_t` is typically + larger than last time index t[n-1] == (`n`-1) * `T`. The upper border + of samples indexes covered by the window slices is given by `k_max`. + Furthermore, `p_max` does not denote the number of slices `p_num` since + `p_min` is typically less than zero. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + p_min: The smallest possible slice index. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + ShortTimeFFT: Class this method belongs to. + """ + return self._post_padding(n)[1] + + def p_num(self, n: int) -> int: + """Number of time slices for an input signal with `n` samples. + + It is given by `p_num` = `p_max` - `p_min` with `p_min` typically + being negative. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + lower_border_end: Where pre-padding effects end. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_range: Determine and validate slice index range. + upper_border_begin: Where post-padding effects start. + ShortTimeFFT: Class this method belongs to. + """ + return self.p_max(n) - self.p_min + + @property + def lower_border_end(self) -> tuple[int, int]: + """First signal index and first slice index unaffected by pre-padding. + + Describes the point where the window does not stick out to the left + of the signal domain. + A detailed example is provided in the :ref:`tutorial_stft_sliding_win` + section of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + lower_border_end: Where pre-padding effects end. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + upper_border_begin: Where post-padding effects start. + ShortTimeFFT: Class this property belongs to. + """ + # not using @cache decorator due to MyPy limitations + if self._lower_border_end is not None: + return self._lower_border_end + + # first non-zero element in self.win: + m0 = np.flatnonzero(self.win.real**2 + self.win.imag**2)[0] + + # move window to the right until does not stick out to the left: + k0 = -self.m_num_mid + m0 + for q_, k_ in enumerate(range(k0, self.hop + 1, self.hop)): + if k_ + self.hop >= 0: # next entry does not stick out anymore + self._lower_border_end = (k_ + self.m_num, q_ + 1) + return self._lower_border_end + self._lower_border_end = (0, max(self.p_min, 0)) # ends at first slice + return self._lower_border_end + + @lru_cache(maxsize=256) + def upper_border_begin(self, n: int) -> tuple[int, int]: + """First signal index and first slice index affected by post-padding. + + Describes the point where the window does begin stick out to the right + of the signal domain. + A detailed example is given :ref:`tutorial_stft_sliding_win` section + of the :ref:`user_guide`. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + lower_border_end: Where pre-padding effects end. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + p_range: Determine and validate slice index range. + ShortTimeFFT: Class this method belongs to. + """ + w2 = self.win.real**2 + self.win.imag**2 + q2 = n // self.hop + 1 # first t[q] >= t[n] + q1 = max((n-self.m_num) // self.hop - 1, -1) + # move window left until does not stick out to the right: + for q_ in range(q2, q1, -1): + k_ = q_ * self.hop + (self.m_num - self.m_num_mid) + if k_ < n or all(w2[n-k_:] == 0): + return (q_ + 1) * self.hop - self.m_num_mid, q_ + 1 + return 0, 0 # border starts at first slice + + @property + def delta_t(self) -> float: + """Time increment of STFT. + + The time increment `delta_t` = `T` * `hop` represents the sample + increment `hop` converted to time based on the sampling interval `T`. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + hop: Hop size in signal samples for sliding window. + t: Times of STFT for an input signal with `n` samples. + T: Sampling interval of input signal and window `win`. + ShortTimeFFT: Class this property belongs to + """ + return self.T * self.hop + + def p_range(self, n: int, p0: int | None = None, + p1: int | None = None) -> tuple[int, int]: + """Determine and validate slice index range. + + Parameters + ---------- + n : int + Number of samples of input signal, assuming t[0] = 0. + p0 : int | None + First slice index. If 0 then the first slice is centered at t = 0. + If ``None`` then `p_min` is used. Note that p0 may be < 0 if + slices are left of t = 0. + p1 : int | None + End of interval (last value is p1-1). + If ``None`` then `p_max(n)` is used. + + + Returns + ------- + p0_ : int + The fist slice index + p1_ : int + End of interval (last value is p1-1). + + Notes + ----- + A ``ValueError`` is raised if ``p_min <= p0 < p1 <= p_max(n)`` does not + hold. + + See Also + -------- + k_min: The smallest possible signal index. + k_max: First sample index after signal end not touched by a time slice. + lower_border_end: Where pre-padding effects end. + p_min: The smallest possible slice index. + p_max: Index of first non-overlapping upper time slice. + p_num: Number of time slices, i.e., `p_max` - `p_min`. + upper_border_begin: Where post-padding effects start. + ShortTimeFFT: Class this property belongs to. + """ + p_max = self.p_max(n) # shorthand + p0_ = self.p_min if p0 is None else p0 + p1_ = p_max if p1 is None else p1 + if not (self.p_min <= p0_ < p1_ <= p_max): + raise ValueError(f"Invalid Parameter {p0=}, {p1=}, i.e., " + + f"{self.p_min=} <= p0 < p1 <= {p_max=} " + + f"does not hold for signal length {n=}!") + return p0_, p1_ + + @lru_cache(maxsize=1) + def t(self, n: int, p0: int | None = None, p1: int | None = None, + k_offset: int = 0) -> np.ndarray: + """Times of STFT for an input signal with `n` samples. + + Returns a 1d array with times of the `~ShortTimeFFT.stft` values with + the same parametrization. Note that the slices are + ``delta_t = hop * T`` time units apart. + + Parameters + ---------- + n + Number of sample of the input signal. + p0 + The first element of the range of slices to calculate. If ``None`` + then it is set to :attr:`p_min`, which is the smallest possible + slice. + p1 + The end of the array. If ``None`` then `p_max(n)` is used. + k_offset + Index of first sample (t = 0) in `x`. + + + See Also + -------- + delta_t: Time increment of STFT (``hop*T``) + hop: Time increment in signal samples for sliding window. + nearest_k_p: Nearest sample index k_p for which t[k_p] == t[p] holds. + T: Sampling interval of input signal and of the window (``1/fs``). + fs: Sampling frequency (being ``1/T``) + ShortTimeFFT: Class this method belongs to. + """ + p0, p1 = self.p_range(n, p0, p1) + return np.arange(p0, p1) * self.delta_t + k_offset * self.T + + def nearest_k_p(self, k: int, left: bool = True) -> int: + """Return nearest sample index k_p for which t[k_p] == t[p] holds. + + The nearest next smaller time sample p (where t[p] is the center + position of the window of the p-th slice) is p_k = k // `hop`. + If `hop` is a divisor of `k` than `k` is returned. + If `left` is set than p_k * `hop` is returned else (p_k+1) * `hop`. + + This method can be used to slice an input signal into chunks for + calculating the STFT and iSTFT incrementally. + + See Also + -------- + delta_t: Time increment of STFT (``hop*T``) + hop: Time increment in signal samples for sliding window. + T: Sampling interval of input signal and of the window (``1/fs``). + fs: Sampling frequency (being ``1/T``) + t: Times of STFT for an input signal with `n` samples. + ShortTimeFFT: Class this method belongs to. + """ + p_q, remainder = divmod(k, self.hop) + if remainder == 0: + return k + return p_q * self.hop if left else (p_q + 1) * self.hop + + @property + def delta_f(self) -> float: + """Width of the frequency bins of the STFT. + + Return the frequency interval `delta_f` = 1 / (`mfft` * `T`). + + See Also + -------- + delta_t: Time increment of STFT. + f_pts: Number of points along the frequency axis. + f: Frequencies values of the STFT. + mfft: Length of the input for FFT used. + T: Sampling interval. + t: Times of STFT for an input signal with `n` samples. + ShortTimeFFT: Class this property belongs to. + """ + return 1 / (self.mfft * self.T) + + @property + def f_pts(self) -> int: + """Number of points along the frequency axis. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + f: Frequencies values of the STFT. + mfft: Length of the input for FFT used. + ShortTimeFFT: Class this property belongs to. + """ + return self.mfft // 2 + 1 if self.onesided_fft else self.mfft + + @property + def onesided_fft(self) -> bool: + """Return True if a one-sided FFT is used. + + Returns ``True`` if `fft_mode` is either 'onesided' or 'onesided2X'. + + See Also + -------- + fft_mode: Utilized FFT ('twosided', 'centered', 'onesided' or + 'onesided2X') + ShortTimeFFT: Class this property belongs to. + """ + return self.fft_mode in {'onesided', 'onesided2X'} + + @property + def f(self) -> np.ndarray: + """Frequencies values of the STFT. + + A 1d array of length `f_pts` with `delta_f` spaced entries is returned. + + See Also + -------- + delta_f: Width of the frequency bins of the STFT. + f_pts: Number of points along the frequency axis. + mfft: Length of the input for FFT used. + ShortTimeFFT: Class this property belongs to. + """ + if self.fft_mode in {'onesided', 'onesided2X'}: + return fft_lib.rfftfreq(self.mfft, self.T) + elif self.fft_mode == 'twosided': + return fft_lib.fftfreq(self.mfft, self.T) + elif self.fft_mode == 'centered': + return fft_lib.fftshift(fft_lib.fftfreq(self.mfft, self.T)) + # This should never happen but makes the Linters happy: + fft_modes = get_args(FFT_MODE_TYPE) + raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!") + + def _fft_func(self, x: np.ndarray) -> np.ndarray: + """FFT based on the `fft_mode`, `mfft`, `scaling` and `phase_shift` + attributes. + + For multidimensional arrays the transformation is carried out on the + last axis. + """ + if self.phase_shift is not None: + if x.shape[-1] < self.mfft: # zero pad if needed + z_shape = list(x.shape) + z_shape[-1] = self.mfft - x.shape[-1] + x = np.hstack((x, np.zeros(z_shape, dtype=x.dtype))) + p_s = (self.phase_shift + self.m_num_mid) % self.m_num + x = np.roll(x, -p_s, axis=-1) + + if self.fft_mode == 'twosided': + return fft_lib.fft(x, n=self.mfft, axis=-1) + if self.fft_mode == 'centered': + return fft_lib.fftshift(fft_lib.fft(x, self.mfft, axis=-1), axes=-1) + if self.fft_mode == 'onesided': + return fft_lib.rfft(x, n=self.mfft, axis=-1) + if self.fft_mode == 'onesided2X': + X = fft_lib.rfft(x, n=self.mfft, axis=-1) + # Either squared magnitude (psd) or magnitude is doubled: + fac = np.sqrt(2) if self.scaling == 'psd' else 2 + # For even input length, the last entry is unpaired: + X[..., 1: -1 if self.mfft % 2 == 0 else None] *= fac + return X + # This should never happen but makes the Linter happy: + fft_modes = get_args(FFT_MODE_TYPE) + raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!") + + def _ifft_func(self, X: np.ndarray) -> np.ndarray: + """Inverse to `_fft_func`. + + Returned is an array of length `m_num`. If the FFT is `onesided` + then a float array is returned else a complex array is returned. + For multidimensional arrays the transformation is carried out on the + last axis. + """ + if self.fft_mode == 'twosided': + x = fft_lib.ifft(X, n=self.mfft, axis=-1) + elif self.fft_mode == 'centered': + x = fft_lib.ifft(fft_lib.ifftshift(X, axes=-1), n=self.mfft, axis=-1) + elif self.fft_mode == 'onesided': + x = fft_lib.irfft(X, n=self.mfft, axis=-1) + elif self.fft_mode == 'onesided2X': + Xc = X.copy() # we do not want to modify function parameters + fac = np.sqrt(2) if self.scaling == 'psd' else 2 + # For even length X the last value is not paired with a negative + # value on the two-sided FFT: + q1 = -1 if self.mfft % 2 == 0 else None + Xc[..., 1:q1] /= fac + x = fft_lib.irfft(Xc, n=self.mfft, axis=-1) + else: # This should never happen but makes the Linter happy: + error_str = f"{self.fft_mode=} not in {get_args(FFT_MODE_TYPE)}!" + raise RuntimeError(error_str) + + if self.phase_shift is None: + return x[:self.m_num] + p_s = (self.phase_shift + self.m_num_mid) % self.m_num + return np.roll(x, p_s, axis=-1)[:self.m_num] + + def extent(self, n: int, axes_seq: Literal['tf', 'ft'] = 'tf', + center_bins: bool = False) -> tuple[float, float, float, float]: + """Return minimum and maximum values time-frequency values. + + A tuple with four floats ``(t0, t1, f0, f1)`` for 'tf' and + ``(f0, f1, t0, t1)`` for 'ft' is returned describing the corners + of the time-frequency domain of the `~ShortTimeFFT.stft`. + That tuple can be passed to `matplotlib.pyplot.imshow` as a parameter + with the same name. + + Parameters + ---------- + n : int + Number of samples in input signal. + axes_seq : {'tf', 'ft'} + Return time extent first and then frequency extent or vice-versa. + center_bins: bool + If set (default ``False``), the values of the time slots and + frequency bins are moved from the side the middle. This is useful, + when plotting the `~ShortTimeFFT.stft` values as step functions, + i.e., with no interpolation. + + See Also + -------- + :func:`matplotlib.pyplot.imshow`: Display data as an image. + :class:`scipy.signal.ShortTimeFFT`: Class this method belongs to. + """ + if axes_seq not in ('tf', 'ft'): + raise ValueError(f"Parameter {axes_seq=} not in ['tf', 'ft']!") + + if self.onesided_fft: + q0, q1 = 0, self.f_pts + elif self.fft_mode == 'centered': + q0 = -self.mfft // 2 + q1 = self.mfft // 2 - 1 if self.mfft % 2 == 0 else self.mfft // 2 + else: + raise ValueError(f"Attribute fft_mode={self.fft_mode} must be " + + "in ['centered', 'onesided', 'onesided2X']") + + p0, p1 = self.p_min, self.p_max(n) # shorthand + if center_bins: + t0, t1 = self.delta_t * (p0 - 0.5), self.delta_t * (p1 - 0.5) + f0, f1 = self.delta_f * (q0 - 0.5), self.delta_f * (q1 - 0.5) + else: + t0, t1 = self.delta_t * p0, self.delta_t * p1 + f0, f1 = self.delta_f * q0, self.delta_f * q1 + return (t0, t1, f0, f1) if axes_seq == 'tf' else (f0, f1, t0, t1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_signaltools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_signaltools.py new file mode 100644 index 0000000000000000000000000000000000000000..214b35c596dd524ee2eab586b65586f8886d7538 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_signaltools.py @@ -0,0 +1,4628 @@ +# Author: Travis Oliphant +# 1999 -- 2002 + +import operator +import math +from math import prod as _prod +import timeit +import warnings + +from scipy.spatial import cKDTree +from . import _sigtools +from ._ltisys import dlti +from ._upfirdn import upfirdn, _output_len, _upfirdn_modes +from scipy import linalg, fft as sp_fft +from scipy import ndimage +from scipy.fft._helper import _init_nd_shape_and_axes +import numpy as np +from scipy.special import lambertw +from .windows import get_window +from ._arraytools import axis_slice, axis_reverse, odd_ext, even_ext, const_ext +from ._filter_design import cheby1, _validate_sos, zpk2sos +from ._fir_filter_design import firwin +from ._sosfilt import _sosfilt + + +__all__ = ['correlate', 'correlation_lags', 'correlate2d', + 'convolve', 'convolve2d', 'fftconvolve', 'oaconvolve', + 'order_filter', 'medfilt', 'medfilt2d', 'wiener', 'lfilter', + 'lfiltic', 'sosfilt', 'deconvolve', 'hilbert', 'hilbert2', + 'cmplx_sort', 'unique_roots', 'invres', 'invresz', 'residue', + 'residuez', 'resample', 'resample_poly', 'detrend', + 'lfilter_zi', 'sosfilt_zi', 'sosfiltfilt', 'choose_conv_method', + 'filtfilt', 'decimate', 'vectorstrength'] + + +_modedict = {'valid': 0, 'same': 1, 'full': 2} + +_boundarydict = {'fill': 0, 'pad': 0, 'wrap': 2, 'circular': 2, 'symm': 1, + 'symmetric': 1, 'reflect': 4} + + +def _valfrommode(mode): + try: + return _modedict[mode] + except KeyError as e: + raise ValueError("Acceptable mode flags are 'valid'," + " 'same', or 'full'.") from e + + +def _bvalfromboundary(boundary): + try: + return _boundarydict[boundary] << 2 + except KeyError as e: + raise ValueError("Acceptable boundary flags are 'fill', 'circular' " + "(or 'wrap'), and 'symmetric' (or 'symm').") from e + + +def _inputs_swap_needed(mode, shape1, shape2, axes=None): + """Determine if inputs arrays need to be swapped in `"valid"` mode. + + If in `"valid"` mode, returns whether or not the input arrays need to be + swapped depending on whether `shape1` is at least as large as `shape2` in + every calculated dimension. + + This is important for some of the correlation and convolution + implementations in this module, where the larger array input needs to come + before the smaller array input when operating in this mode. + + Note that if the mode provided is not 'valid', False is immediately + returned. + + """ + if mode != 'valid': + return False + + if not shape1: + return False + + if axes is None: + axes = range(len(shape1)) + + ok1 = all(shape1[i] >= shape2[i] for i in axes) + ok2 = all(shape2[i] >= shape1[i] for i in axes) + + if not (ok1 or ok2): + raise ValueError("For 'valid' mode, one must be at least " + "as large as the other in every dimension") + + return not ok1 + + +def correlate(in1, in2, mode='full', method='auto'): + r""" + Cross-correlate two N-dimensional arrays. + + Cross-correlate `in1` and `in2`, with the output size determined by the + `mode` argument. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear cross-correlation + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + method : str {'auto', 'direct', 'fft'}, optional + A string indicating which method to use to calculate the correlation. + + ``direct`` + The correlation is determined directly from sums, the definition of + correlation. + ``fft`` + The Fast Fourier Transform is used to perform the correlation more + quickly (only available for numerical arrays.) + ``auto`` + Automatically chooses direct or Fourier method based on an estimate + of which is faster (default). See `convolve` Notes for more detail. + + .. versionadded:: 0.19.0 + + Returns + ------- + correlate : array + An N-dimensional array containing a subset of the discrete linear + cross-correlation of `in1` with `in2`. + + See Also + -------- + choose_conv_method : contains more documentation on `method`. + correlation_lags : calculates the lag / displacement indices array for 1D + cross-correlation. + + Notes + ----- + The correlation z of two d-dimensional arrays x and y is defined as:: + + z[...,k,...] = sum[..., i_l, ...] x[..., i_l,...] * conj(y[..., i_l - k,...]) + + This way, if x and y are 1-D arrays and ``z = correlate(x, y, 'full')`` + then + + .. math:: + + z[k] = (x * y)(k - N + 1) + = \sum_{l=0}^{||x||-1}x_l y_{l-k+N-1}^{*} + + for :math:`k = 0, 1, ..., ||x|| + ||y|| - 2` + + where :math:`||x||` is the length of ``x``, :math:`N = \max(||x||,||y||)`, + and :math:`y_m` is 0 when m is outside the range of y. + + ``method='fft'`` only works for numerical arrays as it relies on + `fftconvolve`. In certain cases (i.e., arrays of objects or when + rounding integers can lose precision), ``method='direct'`` is always used. + + When using "same" mode with even-length inputs, the outputs of `correlate` + and `correlate2d` differ: There is a 1-index offset between them. + + Examples + -------- + Implement a matched filter using cross-correlation, to recover a signal + that has passed through a noisy channel. + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + >>> sig = np.repeat([0., 1., 1., 0., 1., 0., 0., 1.], 128) + >>> sig_noise = sig + rng.standard_normal(len(sig)) + >>> corr = signal.correlate(sig_noise, np.ones(128), mode='same') / 128 + + >>> clock = np.arange(64, len(sig), 128) + >>> fig, (ax_orig, ax_noise, ax_corr) = plt.subplots(3, 1, sharex=True) + >>> ax_orig.plot(sig) + >>> ax_orig.plot(clock, sig[clock], 'ro') + >>> ax_orig.set_title('Original signal') + >>> ax_noise.plot(sig_noise) + >>> ax_noise.set_title('Signal with noise') + >>> ax_corr.plot(corr) + >>> ax_corr.plot(clock, corr[clock], 'ro') + >>> ax_corr.axhline(0.5, ls=':') + >>> ax_corr.set_title('Cross-correlated with rectangular pulse') + >>> ax_orig.margins(0, 0.1) + >>> fig.tight_layout() + >>> plt.show() + + Compute the cross-correlation of a noisy signal with the original signal. + + >>> x = np.arange(128) / 128 + >>> sig = np.sin(2 * np.pi * x) + >>> sig_noise = sig + rng.standard_normal(len(sig)) + >>> corr = signal.correlate(sig_noise, sig) + >>> lags = signal.correlation_lags(len(sig), len(sig_noise)) + >>> corr /= np.max(corr) + + >>> fig, (ax_orig, ax_noise, ax_corr) = plt.subplots(3, 1, figsize=(4.8, 4.8)) + >>> ax_orig.plot(sig) + >>> ax_orig.set_title('Original signal') + >>> ax_orig.set_xlabel('Sample Number') + >>> ax_noise.plot(sig_noise) + >>> ax_noise.set_title('Signal with noise') + >>> ax_noise.set_xlabel('Sample Number') + >>> ax_corr.plot(lags, corr) + >>> ax_corr.set_title('Cross-correlated signal') + >>> ax_corr.set_xlabel('Lag') + >>> ax_orig.margins(0, 0.1) + >>> ax_noise.margins(0, 0.1) + >>> ax_corr.margins(0, 0.1) + >>> fig.tight_layout() + >>> plt.show() + + """ + in1 = np.asarray(in1) + in2 = np.asarray(in2) + + if in1.ndim == in2.ndim == 0: + return in1 * in2.conj() + elif in1.ndim != in2.ndim: + raise ValueError("in1 and in2 should have the same dimensionality") + + # Don't use _valfrommode, since correlate should not accept numeric modes + try: + val = _modedict[mode] + except KeyError as e: + raise ValueError("Acceptable mode flags are 'valid'," + " 'same', or 'full'.") from e + + # this either calls fftconvolve or this function with method=='direct' + if method in ('fft', 'auto'): + return convolve(in1, _reverse_and_conj(in2), mode, method) + + elif method == 'direct': + # fastpath to faster numpy.correlate for 1d inputs when possible + if _np_conv_ok(in1, in2, mode): + return np.correlate(in1, in2, mode) + + # _correlateND is far slower when in2.size > in1.size, so swap them + # and then undo the effect afterward if mode == 'full'. Also, it fails + # with 'valid' mode if in2 is larger than in1, so swap those, too. + # Don't swap inputs for 'same' mode, since shape of in1 matters. + swapped_inputs = ((mode == 'full') and (in2.size > in1.size) or + _inputs_swap_needed(mode, in1.shape, in2.shape)) + + if swapped_inputs: + in1, in2 = in2, in1 + + if mode == 'valid': + ps = [i - j + 1 for i, j in zip(in1.shape, in2.shape)] + out = np.empty(ps, in1.dtype) + + z = _sigtools._correlateND(in1, in2, out, val) + + else: + ps = [i + j - 1 for i, j in zip(in1.shape, in2.shape)] + + # zero pad input + in1zpadded = np.zeros(ps, in1.dtype) + sc = tuple(slice(0, i) for i in in1.shape) + in1zpadded[sc] = in1.copy() + + if mode == 'full': + out = np.empty(ps, in1.dtype) + elif mode == 'same': + out = np.empty(in1.shape, in1.dtype) + + z = _sigtools._correlateND(in1zpadded, in2, out, val) + + if swapped_inputs: + # Reverse and conjugate to undo the effect of swapping inputs + z = _reverse_and_conj(z) + + return z + + else: + raise ValueError("Acceptable method flags are 'auto'," + " 'direct', or 'fft'.") + + +def correlation_lags(in1_len, in2_len, mode='full'): + r""" + Calculates the lag / displacement indices array for 1D cross-correlation. + + Parameters + ---------- + in1_len : int + First input size. + in2_len : int + Second input size. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output. + See the documentation `correlate` for more information. + + Returns + ------- + lags : array + Returns an array containing cross-correlation lag/displacement indices. + Indices can be indexed with the np.argmax of the correlation to return + the lag/displacement. + + See Also + -------- + correlate : Compute the N-dimensional cross-correlation. + + Notes + ----- + Cross-correlation for continuous functions :math:`f` and :math:`g` is + defined as: + + .. math:: + + \left ( f\star g \right )\left ( \tau \right ) + \triangleq \int_{t_0}^{t_0 +T} + \overline{f\left ( t \right )}g\left ( t+\tau \right )dt + + Where :math:`\tau` is defined as the displacement, also known as the lag. + + Cross correlation for discrete functions :math:`f` and :math:`g` is + defined as: + + .. math:: + \left ( f\star g \right )\left [ n \right ] + \triangleq \sum_{-\infty}^{\infty} + \overline{f\left [ m \right ]}g\left [ m+n \right ] + + Where :math:`n` is the lag. + + Examples + -------- + Cross-correlation of a signal with its time-delayed self. + + >>> import numpy as np + >>> from scipy import signal + >>> rng = np.random.default_rng() + >>> x = rng.standard_normal(1000) + >>> y = np.concatenate([rng.standard_normal(100), x]) + >>> correlation = signal.correlate(x, y, mode="full") + >>> lags = signal.correlation_lags(x.size, y.size, mode="full") + >>> lag = lags[np.argmax(correlation)] + """ + + # calculate lag ranges in different modes of operation + if mode == "full": + # the output is the full discrete linear convolution + # of the inputs. (Default) + lags = np.arange(-in2_len + 1, in1_len) + elif mode == "same": + # the output is the same size as `in1`, centered + # with respect to the 'full' output. + # calculate the full output + lags = np.arange(-in2_len + 1, in1_len) + # determine the midpoint in the full output + mid = lags.size // 2 + # determine lag_bound to be used with respect + # to the midpoint + lag_bound = in1_len // 2 + # calculate lag ranges for even and odd scenarios + if in1_len % 2 == 0: + lags = lags[(mid-lag_bound):(mid+lag_bound)] + else: + lags = lags[(mid-lag_bound):(mid+lag_bound)+1] + elif mode == "valid": + # the output consists only of those elements that do not + # rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + # must be at least as large as the other in every dimension. + + # the lag_bound will be either negative or positive + # this let's us infer how to present the lag range + lag_bound = in1_len - in2_len + if lag_bound >= 0: + lags = np.arange(lag_bound + 1) + else: + lags = np.arange(lag_bound, 1) + return lags + + +def _centered(arr, newshape): + # Return the center newshape portion of the array. + newshape = np.asarray(newshape) + currshape = np.array(arr.shape) + startind = (currshape - newshape) // 2 + endind = startind + newshape + myslice = [slice(startind[k], endind[k]) for k in range(len(endind))] + return arr[tuple(myslice)] + + +def _init_freq_conv_axes(in1, in2, mode, axes, sorted_axes=False): + """Handle the axes argument for frequency-domain convolution. + + Returns the inputs and axes in a standard form, eliminating redundant axes, + swapping the inputs if necessary, and checking for various potential + errors. + + Parameters + ---------- + in1 : array + First input. + in2 : array + Second input. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output. + See the documentation `fftconvolve` for more information. + axes : list of ints + Axes over which to compute the FFTs. + sorted_axes : bool, optional + If `True`, sort the axes. + Default is `False`, do not sort. + + Returns + ------- + in1 : array + The first input, possible swapped with the second input. + in2 : array + The second input, possible swapped with the first input. + axes : list of ints + Axes over which to compute the FFTs. + + """ + s1 = in1.shape + s2 = in2.shape + noaxes = axes is None + + _, axes = _init_nd_shape_and_axes(in1, shape=None, axes=axes) + + if not noaxes and not len(axes): + raise ValueError("when provided, axes cannot be empty") + + # Axes of length 1 can rely on broadcasting rules for multiply, + # no fft needed. + axes = [a for a in axes if s1[a] != 1 and s2[a] != 1] + + if sorted_axes: + axes.sort() + + if not all(s1[a] == s2[a] or s1[a] == 1 or s2[a] == 1 + for a in range(in1.ndim) if a not in axes): + raise ValueError("incompatible shapes for in1 and in2:" + f" {s1} and {s2}") + + # Check that input sizes are compatible with 'valid' mode. + if _inputs_swap_needed(mode, s1, s2, axes=axes): + # Convolution is commutative; order doesn't have any effect on output. + in1, in2 = in2, in1 + + return in1, in2, axes + + +def _freq_domain_conv(in1, in2, axes, shape, calc_fast_len=False): + """Convolve two arrays in the frequency domain. + + This function implements only base the FFT-related operations. + Specifically, it converts the signals to the frequency domain, multiplies + them, then converts them back to the time domain. Calculations of axes, + shapes, convolution mode, etc. are implemented in higher level-functions, + such as `fftconvolve` and `oaconvolve`. Those functions should be used + instead of this one. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + axes : array_like of ints + Axes over which to compute the FFTs. + shape : array_like of ints + The sizes of the FFTs. + calc_fast_len : bool, optional + If `True`, set each value of `shape` to the next fast FFT length. + Default is `False`, use `axes` as-is. + + Returns + ------- + out : array + An N-dimensional array containing the discrete linear convolution of + `in1` with `in2`. + + """ + if not len(axes): + return in1 * in2 + + complex_result = (in1.dtype.kind == 'c' or in2.dtype.kind == 'c') + + if calc_fast_len: + # Speed up FFT by padding to optimal size. + fshape = [ + sp_fft.next_fast_len(shape[a], not complex_result) for a in axes] + else: + fshape = shape + + if not complex_result: + fft, ifft = sp_fft.rfftn, sp_fft.irfftn + else: + fft, ifft = sp_fft.fftn, sp_fft.ifftn + + sp1 = fft(in1, fshape, axes=axes) + sp2 = fft(in2, fshape, axes=axes) + + ret = ifft(sp1 * sp2, fshape, axes=axes) + + if calc_fast_len: + fslice = tuple([slice(sz) for sz in shape]) + ret = ret[fslice] + + return ret + + +def _apply_conv_mode(ret, s1, s2, mode, axes): + """Calculate the convolution result shape based on the `mode` argument. + + Returns the result sliced to the correct size for the given mode. + + Parameters + ---------- + ret : array + The result array, with the appropriate shape for the 'full' mode. + s1 : list of int + The shape of the first input. + s2 : list of int + The shape of the second input. + mode : str {'full', 'valid', 'same'} + A string indicating the size of the output. + See the documentation `fftconvolve` for more information. + axes : list of ints + Axes over which to compute the convolution. + + Returns + ------- + ret : array + A copy of `res`, sliced to the correct size for the given `mode`. + + """ + if mode == "full": + return ret.copy() + elif mode == "same": + return _centered(ret, s1).copy() + elif mode == "valid": + shape_valid = [ret.shape[a] if a not in axes else s1[a] - s2[a] + 1 + for a in range(ret.ndim)] + return _centered(ret, shape_valid).copy() + else: + raise ValueError("acceptable mode flags are 'valid'," + " 'same', or 'full'") + + +def fftconvolve(in1, in2, mode="full", axes=None): + """Convolve two N-dimensional arrays using FFT. + + Convolve `in1` and `in2` using the fast Fourier transform method, with + the output size determined by the `mode` argument. + + This is generally much faster than `convolve` for large arrays (n > ~500), + but can be slower when only a few output values are needed, and can only + output float arrays (int or object array inputs will be cast to float). + + As of v0.19, `convolve` automatically chooses this method or the direct + method based on an estimation of which is faster. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear convolution + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + axes : int or array_like of ints or None, optional + Axes over which to compute the convolution. + The default is over all axes. + + Returns + ------- + out : array + An N-dimensional array containing a subset of the discrete linear + convolution of `in1` with `in2`. + + See Also + -------- + convolve : Uses the direct convolution or FFT convolution algorithm + depending on which is faster. + oaconvolve : Uses the overlap-add method to do convolution, which is + generally faster when the input arrays are large and + significantly different in size. + + Examples + -------- + Autocorrelation of white noise is an impulse. + + >>> import numpy as np + >>> from scipy import signal + >>> rng = np.random.default_rng() + >>> sig = rng.standard_normal(1000) + >>> autocorr = signal.fftconvolve(sig, sig[::-1], mode='full') + + >>> import matplotlib.pyplot as plt + >>> fig, (ax_orig, ax_mag) = plt.subplots(2, 1) + >>> ax_orig.plot(sig) + >>> ax_orig.set_title('White noise') + >>> ax_mag.plot(np.arange(-len(sig)+1,len(sig)), autocorr) + >>> ax_mag.set_title('Autocorrelation') + >>> fig.tight_layout() + >>> fig.show() + + Gaussian blur implemented using FFT convolution. Notice the dark borders + around the image, due to the zero-padding beyond its boundaries. + The `convolve2d` function allows for other types of image boundaries, + but is far slower. + + >>> from scipy import datasets + >>> face = datasets.face(gray=True) + >>> kernel = np.outer(signal.windows.gaussian(70, 8), + ... signal.windows.gaussian(70, 8)) + >>> blurred = signal.fftconvolve(face, kernel, mode='same') + + >>> fig, (ax_orig, ax_kernel, ax_blurred) = plt.subplots(3, 1, + ... figsize=(6, 15)) + >>> ax_orig.imshow(face, cmap='gray') + >>> ax_orig.set_title('Original') + >>> ax_orig.set_axis_off() + >>> ax_kernel.imshow(kernel, cmap='gray') + >>> ax_kernel.set_title('Gaussian kernel') + >>> ax_kernel.set_axis_off() + >>> ax_blurred.imshow(blurred, cmap='gray') + >>> ax_blurred.set_title('Blurred') + >>> ax_blurred.set_axis_off() + >>> fig.show() + + """ + in1 = np.asarray(in1) + in2 = np.asarray(in2) + + if in1.ndim == in2.ndim == 0: # scalar inputs + return in1 * in2 + elif in1.ndim != in2.ndim: + raise ValueError("in1 and in2 should have the same dimensionality") + elif in1.size == 0 or in2.size == 0: # empty arrays + return np.array([]) + + in1, in2, axes = _init_freq_conv_axes(in1, in2, mode, axes, + sorted_axes=False) + + s1 = in1.shape + s2 = in2.shape + + shape = [max((s1[i], s2[i])) if i not in axes else s1[i] + s2[i] - 1 + for i in range(in1.ndim)] + + ret = _freq_domain_conv(in1, in2, axes, shape, calc_fast_len=True) + + return _apply_conv_mode(ret, s1, s2, mode, axes) + + +def _calc_oa_lens(s1, s2): + """Calculate the optimal FFT lengths for overlapp-add convolution. + + The calculation is done for a single dimension. + + Parameters + ---------- + s1 : int + Size of the dimension for the first array. + s2 : int + Size of the dimension for the second array. + + Returns + ------- + block_size : int + The size of the FFT blocks. + overlap : int + The amount of overlap between two blocks. + in1_step : int + The size of each step for the first array. + in2_step : int + The size of each step for the first array. + + """ + # Set up the arguments for the conventional FFT approach. + fallback = (s1+s2-1, None, s1, s2) + + # Use conventional FFT convolve if sizes are same. + if s1 == s2 or s1 == 1 or s2 == 1: + return fallback + + if s2 > s1: + s1, s2 = s2, s1 + swapped = True + else: + swapped = False + + # There cannot be a useful block size if s2 is more than half of s1. + if s2 >= s1/2: + return fallback + + # Derivation of optimal block length + # For original formula see: + # https://en.wikipedia.org/wiki/Overlap-add_method + # + # Formula: + # K = overlap = s2-1 + # N = block_size + # C = complexity + # e = exponential, exp(1) + # + # C = (N*(log2(N)+1))/(N-K) + # C = (N*log2(2N))/(N-K) + # C = N/(N-K) * log2(2N) + # C1 = N/(N-K) + # C2 = log2(2N) = ln(2N)/ln(2) + # + # dC1/dN = (1*(N-K)-N)/(N-K)^2 = -K/(N-K)^2 + # dC2/dN = 2/(2*N*ln(2)) = 1/(N*ln(2)) + # + # dC/dN = dC1/dN*C2 + dC2/dN*C1 + # dC/dN = -K*ln(2N)/(ln(2)*(N-K)^2) + N/(N*ln(2)*(N-K)) + # dC/dN = -K*ln(2N)/(ln(2)*(N-K)^2) + 1/(ln(2)*(N-K)) + # dC/dN = -K*ln(2N)/(ln(2)*(N-K)^2) + (N-K)/(ln(2)*(N-K)^2) + # dC/dN = (-K*ln(2N) + (N-K)/(ln(2)*(N-K)^2) + # dC/dN = (N - K*ln(2N) - K)/(ln(2)*(N-K)^2) + # + # Solve for minimum, where dC/dN = 0 + # 0 = (N - K*ln(2N) - K)/(ln(2)*(N-K)^2) + # 0 * ln(2)*(N-K)^2 = N - K*ln(2N) - K + # 0 = N - K*ln(2N) - K + # 0 = N - K*(ln(2N) + 1) + # 0 = N - K*ln(2Ne) + # N = K*ln(2Ne) + # N/K = ln(2Ne) + # + # e^(N/K) = e^ln(2Ne) + # e^(N/K) = 2Ne + # 1/e^(N/K) = 1/(2*N*e) + # e^(N/-K) = 1/(2*N*e) + # e^(N/-K) = K/N*1/(2*K*e) + # N/K*e^(N/-K) = 1/(2*e*K) + # N/-K*e^(N/-K) = -1/(2*e*K) + # + # Using Lambert W function + # https://en.wikipedia.org/wiki/Lambert_W_function + # x = W(y) It is the solution to y = x*e^x + # x = N/-K + # y = -1/(2*e*K) + # + # N/-K = W(-1/(2*e*K)) + # + # N = -K*W(-1/(2*e*K)) + overlap = s2-1 + opt_size = -overlap*lambertw(-1/(2*math.e*overlap), k=-1).real + block_size = sp_fft.next_fast_len(math.ceil(opt_size)) + + # Use conventional FFT convolve if there is only going to be one block. + if block_size >= s1: + return fallback + + if not swapped: + in1_step = block_size-s2+1 + in2_step = s2 + else: + in1_step = s2 + in2_step = block_size-s2+1 + + return block_size, overlap, in1_step, in2_step + + +def oaconvolve(in1, in2, mode="full", axes=None): + """Convolve two N-dimensional arrays using the overlap-add method. + + Convolve `in1` and `in2` using the overlap-add method, with + the output size determined by the `mode` argument. + + This is generally much faster than `convolve` for large arrays (n > ~500), + and generally much faster than `fftconvolve` when one array is much + larger than the other, but can be slower when only a few output values are + needed or when the arrays are very similar in shape, and can only + output float arrays (int or object array inputs will be cast to float). + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear convolution + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + axes : int or array_like of ints or None, optional + Axes over which to compute the convolution. + The default is over all axes. + + Returns + ------- + out : array + An N-dimensional array containing a subset of the discrete linear + convolution of `in1` with `in2`. + + See Also + -------- + convolve : Uses the direct convolution or FFT convolution algorithm + depending on which is faster. + fftconvolve : An implementation of convolution using FFT. + + Notes + ----- + .. versionadded:: 1.4.0 + + References + ---------- + .. [1] Wikipedia, "Overlap-add_method". + https://en.wikipedia.org/wiki/Overlap-add_method + .. [2] Richard G. Lyons. Understanding Digital Signal Processing, + Third Edition, 2011. Chapter 13.10. + ISBN 13: 978-0137-02741-5 + + Examples + -------- + Convolve a 100,000 sample signal with a 512-sample filter. + + >>> import numpy as np + >>> from scipy import signal + >>> rng = np.random.default_rng() + >>> sig = rng.standard_normal(100000) + >>> filt = signal.firwin(512, 0.01) + >>> fsig = signal.oaconvolve(sig, filt) + + >>> import matplotlib.pyplot as plt + >>> fig, (ax_orig, ax_mag) = plt.subplots(2, 1) + >>> ax_orig.plot(sig) + >>> ax_orig.set_title('White noise') + >>> ax_mag.plot(fsig) + >>> ax_mag.set_title('Filtered noise') + >>> fig.tight_layout() + >>> fig.show() + + """ + in1 = np.asarray(in1) + in2 = np.asarray(in2) + + if in1.ndim == in2.ndim == 0: # scalar inputs + return in1 * in2 + elif in1.ndim != in2.ndim: + raise ValueError("in1 and in2 should have the same dimensionality") + elif in1.size == 0 or in2.size == 0: # empty arrays + return np.array([]) + elif in1.shape == in2.shape: # Equivalent to fftconvolve + return fftconvolve(in1, in2, mode=mode, axes=axes) + + in1, in2, axes = _init_freq_conv_axes(in1, in2, mode, axes, + sorted_axes=True) + + s1 = in1.shape + s2 = in2.shape + + if not axes: + ret = in1 * in2 + return _apply_conv_mode(ret, s1, s2, mode, axes) + + # Calculate this now since in1 is changed later + shape_final = [None if i not in axes else + s1[i] + s2[i] - 1 for i in range(in1.ndim)] + + # Calculate the block sizes for the output, steps, first and second inputs. + # It is simpler to calculate them all together than doing them in separate + # loops due to all the special cases that need to be handled. + optimal_sizes = ((-1, -1, s1[i], s2[i]) if i not in axes else + _calc_oa_lens(s1[i], s2[i]) for i in range(in1.ndim)) + block_size, overlaps, \ + in1_step, in2_step = zip(*optimal_sizes) + + # Fall back to fftconvolve if there is only one block in every dimension. + if in1_step == s1 and in2_step == s2: + return fftconvolve(in1, in2, mode=mode, axes=axes) + + # Figure out the number of steps and padding. + # This would get too complicated in a list comprehension. + nsteps1 = [] + nsteps2 = [] + pad_size1 = [] + pad_size2 = [] + for i in range(in1.ndim): + if i not in axes: + pad_size1 += [(0, 0)] + pad_size2 += [(0, 0)] + continue + + if s1[i] > in1_step[i]: + curnstep1 = math.ceil((s1[i]+1)/in1_step[i]) + if (block_size[i] - overlaps[i])*curnstep1 < shape_final[i]: + curnstep1 += 1 + + curpad1 = curnstep1*in1_step[i] - s1[i] + else: + curnstep1 = 1 + curpad1 = 0 + + if s2[i] > in2_step[i]: + curnstep2 = math.ceil((s2[i]+1)/in2_step[i]) + if (block_size[i] - overlaps[i])*curnstep2 < shape_final[i]: + curnstep2 += 1 + + curpad2 = curnstep2*in2_step[i] - s2[i] + else: + curnstep2 = 1 + curpad2 = 0 + + nsteps1 += [curnstep1] + nsteps2 += [curnstep2] + pad_size1 += [(0, curpad1)] + pad_size2 += [(0, curpad2)] + + # Pad the array to a size that can be reshaped to the desired shape + # if necessary. + if not all(curpad == (0, 0) for curpad in pad_size1): + in1 = np.pad(in1, pad_size1, mode='constant', constant_values=0) + + if not all(curpad == (0, 0) for curpad in pad_size2): + in2 = np.pad(in2, pad_size2, mode='constant', constant_values=0) + + # Reshape the overlap-add parts to input block sizes. + split_axes = [iax+i for i, iax in enumerate(axes)] + fft_axes = [iax+1 for iax in split_axes] + + # We need to put each new dimension before the corresponding dimension + # being reshaped in order to get the data in the right layout at the end. + reshape_size1 = list(in1_step) + reshape_size2 = list(in2_step) + for i, iax in enumerate(split_axes): + reshape_size1.insert(iax, nsteps1[i]) + reshape_size2.insert(iax, nsteps2[i]) + + in1 = in1.reshape(*reshape_size1) + in2 = in2.reshape(*reshape_size2) + + # Do the convolution. + fft_shape = [block_size[i] for i in axes] + ret = _freq_domain_conv(in1, in2, fft_axes, fft_shape, calc_fast_len=False) + + # Do the overlap-add. + for ax, ax_fft, ax_split in zip(axes, fft_axes, split_axes): + overlap = overlaps[ax] + if overlap is None: + continue + + ret, overpart = np.split(ret, [-overlap], ax_fft) + overpart = np.split(overpart, [-1], ax_split)[0] + + ret_overpart = np.split(ret, [overlap], ax_fft)[0] + ret_overpart = np.split(ret_overpart, [1], ax_split)[1] + ret_overpart += overpart + + # Reshape back to the correct dimensionality. + shape_ret = [ret.shape[i] if i not in fft_axes else + ret.shape[i]*ret.shape[i-1] + for i in range(ret.ndim) if i not in split_axes] + ret = ret.reshape(*shape_ret) + + # Slice to the correct size. + slice_final = tuple([slice(islice) for islice in shape_final]) + ret = ret[slice_final] + + return _apply_conv_mode(ret, s1, s2, mode, axes) + + +def _numeric_arrays(arrays, kinds='buifc'): + """ + See if a list of arrays are all numeric. + + Parameters + ---------- + arrays : array or list of arrays + arrays to check if numeric. + kinds : string-like + The dtypes of the arrays to be checked. If the dtype.kind of + the ndarrays are not in this string the function returns False and + otherwise returns True. + """ + if type(arrays) == np.ndarray: + return arrays.dtype.kind in kinds + for array_ in arrays: + if array_.dtype.kind not in kinds: + return False + return True + + +def _conv_ops(x_shape, h_shape, mode): + """ + Find the number of operations required for direct/fft methods of + convolution. The direct operations were recorded by making a dummy class to + record the number of operations by overriding ``__mul__`` and ``__add__``. + The FFT operations rely on the (well-known) computational complexity of the + FFT (and the implementation of ``_freq_domain_conv``). + + """ + if mode == "full": + out_shape = [n + k - 1 for n, k in zip(x_shape, h_shape)] + elif mode == "valid": + out_shape = [abs(n - k) + 1 for n, k in zip(x_shape, h_shape)] + elif mode == "same": + out_shape = x_shape + else: + raise ValueError("Acceptable mode flags are 'valid'," + f" 'same', or 'full', not mode={mode}") + + s1, s2 = x_shape, h_shape + if len(x_shape) == 1: + s1, s2 = s1[0], s2[0] + if mode == "full": + direct_ops = s1 * s2 + elif mode == "valid": + direct_ops = (s2 - s1 + 1) * s1 if s2 >= s1 else (s1 - s2 + 1) * s2 + elif mode == "same": + direct_ops = (s1 * s2 if s1 < s2 else + s1 * s2 - (s2 // 2) * ((s2 + 1) // 2)) + else: + if mode == "full": + direct_ops = min(_prod(s1), _prod(s2)) * _prod(out_shape) + elif mode == "valid": + direct_ops = min(_prod(s1), _prod(s2)) * _prod(out_shape) + elif mode == "same": + direct_ops = _prod(s1) * _prod(s2) + + full_out_shape = [n + k - 1 for n, k in zip(x_shape, h_shape)] + N = _prod(full_out_shape) + fft_ops = 3 * N * np.log(N) # 3 separate FFTs of size full_out_shape + return fft_ops, direct_ops + + +def _fftconv_faster(x, h, mode): + """ + See if using fftconvolve or convolve is faster. + + Parameters + ---------- + x : np.ndarray + Signal + h : np.ndarray + Kernel + mode : str + Mode passed to convolve + + Returns + ------- + fft_faster : bool + + Notes + ----- + See docstring of `choose_conv_method` for details on tuning hardware. + + See pull request 11031 for more detail: + https://github.com/scipy/scipy/pull/11031. + + """ + fft_ops, direct_ops = _conv_ops(x.shape, h.shape, mode) + offset = -1e-3 if x.ndim == 1 else -1e-4 + constants = { + "valid": (1.89095737e-9, 2.1364985e-10, offset), + "full": (1.7649070e-9, 2.1414831e-10, offset), + "same": (3.2646654e-9, 2.8478277e-10, offset) + if h.size <= x.size + else (3.21635404e-9, 1.1773253e-8, -1e-5), + } if x.ndim == 1 else { + "valid": (1.85927e-9, 2.11242e-8, offset), + "full": (1.99817e-9, 1.66174e-8, offset), + "same": (2.04735e-9, 1.55367e-8, offset), + } + O_fft, O_direct, O_offset = constants[mode] + return O_fft * fft_ops < O_direct * direct_ops + O_offset + + +def _reverse_and_conj(x): + """ + Reverse array `x` in all dimensions and perform the complex conjugate + """ + reverse = (slice(None, None, -1),) * x.ndim + return x[reverse].conj() + + +def _np_conv_ok(volume, kernel, mode): + """ + See if numpy supports convolution of `volume` and `kernel` (i.e. both are + 1D ndarrays and of the appropriate shape). NumPy's 'same' mode uses the + size of the larger input, while SciPy's uses the size of the first input. + + Invalid mode strings will return False and be caught by the calling func. + """ + if volume.ndim == kernel.ndim == 1: + if mode in ('full', 'valid'): + return True + elif mode == 'same': + return volume.size >= kernel.size + else: + return False + + +def _timeit_fast(stmt="pass", setup="pass", repeat=3): + """ + Returns the time the statement/function took, in seconds. + + Faster, less precise version of IPython's timeit. `stmt` can be a statement + written as a string or a callable. + + Will do only 1 loop (like IPython's timeit) with no repetitions + (unlike IPython) for very slow functions. For fast functions, only does + enough loops to take 5 ms, which seems to produce similar results (on + Windows at least), and avoids doing an extraneous cycle that isn't + measured. + + """ + timer = timeit.Timer(stmt, setup) + + # determine number of calls per rep so total time for 1 rep >= 5 ms + x = 0 + for p in range(0, 10): + number = 10**p + x = timer.timeit(number) # seconds + if x >= 5e-3 / 10: # 5 ms for final test, 1/10th that for this one + break + if x > 1: # second + # If it's macroscopic, don't bother with repetitions + best = x + else: + number *= 10 + r = timer.repeat(repeat, number) + best = min(r) + + sec = best / number + return sec + + +def choose_conv_method(in1, in2, mode='full', measure=False): + """ + Find the fastest convolution/correlation method. + + This primarily exists to be called during the ``method='auto'`` option in + `convolve` and `correlate`. It can also be used to determine the value of + ``method`` for many different convolutions of the same dtype/shape. + In addition, it supports timing the convolution to adapt the value of + ``method`` to a particular set of inputs and/or hardware. + + Parameters + ---------- + in1 : array_like + The first argument passed into the convolution function. + in2 : array_like + The second argument passed into the convolution function. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear convolution + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + measure : bool, optional + If True, run and time the convolution of `in1` and `in2` with both + methods and return the fastest. If False (default), predict the fastest + method using precomputed values. + + Returns + ------- + method : str + A string indicating which convolution method is fastest, either + 'direct' or 'fft' + times : dict, optional + A dictionary containing the times (in seconds) needed for each method. + This value is only returned if ``measure=True``. + + See Also + -------- + convolve + correlate + + Notes + ----- + Generally, this method is 99% accurate for 2D signals and 85% accurate + for 1D signals for randomly chosen input sizes. For precision, use + ``measure=True`` to find the fastest method by timing the convolution. + This can be used to avoid the minimal overhead of finding the fastest + ``method`` later, or to adapt the value of ``method`` to a particular set + of inputs. + + Experiments were run on an Amazon EC2 r5a.2xlarge machine to test this + function. These experiments measured the ratio between the time required + when using ``method='auto'`` and the time required for the fastest method + (i.e., ``ratio = time_auto / min(time_fft, time_direct)``). In these + experiments, we found: + + * There is a 95% chance of this ratio being less than 1.5 for 1D signals + and a 99% chance of being less than 2.5 for 2D signals. + * The ratio was always less than 2.5/5 for 1D/2D signals respectively. + * This function is most inaccurate for 1D convolutions that take between 1 + and 10 milliseconds with ``method='direct'``. A good proxy for this + (at least in our experiments) is ``1e6 <= in1.size * in2.size <= 1e7``. + + The 2D results almost certainly generalize to 3D/4D/etc because the + implementation is the same (the 1D implementation is different). + + All the numbers above are specific to the EC2 machine. However, we did find + that this function generalizes fairly decently across hardware. The speed + tests were of similar quality (and even slightly better) than the same + tests performed on the machine to tune this function's numbers (a mid-2014 + 15-inch MacBook Pro with 16GB RAM and a 2.5GHz Intel i7 processor). + + There are cases when `fftconvolve` supports the inputs but this function + returns `direct` (e.g., to protect against floating point integer + precision). + + .. versionadded:: 0.19 + + Examples + -------- + Estimate the fastest method for a given input: + + >>> import numpy as np + >>> from scipy import signal + >>> rng = np.random.default_rng() + >>> img = rng.random((32, 32)) + >>> filter = rng.random((8, 8)) + >>> method = signal.choose_conv_method(img, filter, mode='same') + >>> method + 'fft' + + This can then be applied to other arrays of the same dtype and shape: + + >>> img2 = rng.random((32, 32)) + >>> filter2 = rng.random((8, 8)) + >>> corr2 = signal.correlate(img2, filter2, mode='same', method=method) + >>> conv2 = signal.convolve(img2, filter2, mode='same', method=method) + + The output of this function (``method``) works with `correlate` and + `convolve`. + + """ + volume = np.asarray(in1) + kernel = np.asarray(in2) + + if measure: + times = {} + for method in ['fft', 'direct']: + times[method] = _timeit_fast(lambda: convolve(volume, kernel, + mode=mode, method=method)) + + chosen_method = 'fft' if times['fft'] < times['direct'] else 'direct' + return chosen_method, times + + # for integer input, + # catch when more precision required than float provides (representing an + # integer as float can lose precision in fftconvolve if larger than 2**52) + if any([_numeric_arrays([x], kinds='ui') for x in [volume, kernel]]): + max_value = int(np.abs(volume).max()) * int(np.abs(kernel).max()) + max_value *= int(min(volume.size, kernel.size)) + if max_value > 2**np.finfo('float').nmant - 1: + return 'direct' + + if _numeric_arrays([volume, kernel], kinds='b'): + return 'direct' + + if _numeric_arrays([volume, kernel]): + if _fftconv_faster(volume, kernel, mode): + return 'fft' + + return 'direct' + + +def convolve(in1, in2, mode='full', method='auto'): + """ + Convolve two N-dimensional arrays. + + Convolve `in1` and `in2`, with the output size determined by the + `mode` argument. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear convolution + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + method : str {'auto', 'direct', 'fft'}, optional + A string indicating which method to use to calculate the convolution. + + ``direct`` + The convolution is determined directly from sums, the definition of + convolution. + ``fft`` + The Fourier Transform is used to perform the convolution by calling + `fftconvolve`. + ``auto`` + Automatically chooses direct or Fourier method based on an estimate + of which is faster (default). See Notes for more detail. + + .. versionadded:: 0.19.0 + + Returns + ------- + convolve : array + An N-dimensional array containing a subset of the discrete linear + convolution of `in1` with `in2`. + + Warns + ----- + RuntimeWarning + Use of the FFT convolution on input containing NAN or INF will lead + to the entire output being NAN or INF. Use method='direct' when your + input contains NAN or INF values. + + See Also + -------- + numpy.polymul : performs polynomial multiplication (same operation, but + also accepts poly1d objects) + choose_conv_method : chooses the fastest appropriate convolution method + fftconvolve : Always uses the FFT method. + oaconvolve : Uses the overlap-add method to do convolution, which is + generally faster when the input arrays are large and + significantly different in size. + + Notes + ----- + By default, `convolve` and `correlate` use ``method='auto'``, which calls + `choose_conv_method` to choose the fastest method using pre-computed + values (`choose_conv_method` can also measure real-world timing with a + keyword argument). Because `fftconvolve` relies on floating point numbers, + there are certain constraints that may force `method=direct` (more detail + in `choose_conv_method` docstring). + + Examples + -------- + Smooth a square pulse using a Hann window: + + >>> import numpy as np + >>> from scipy import signal + >>> sig = np.repeat([0., 1., 0.], 100) + >>> win = signal.windows.hann(50) + >>> filtered = signal.convolve(sig, win, mode='same') / sum(win) + + >>> import matplotlib.pyplot as plt + >>> fig, (ax_orig, ax_win, ax_filt) = plt.subplots(3, 1, sharex=True) + >>> ax_orig.plot(sig) + >>> ax_orig.set_title('Original pulse') + >>> ax_orig.margins(0, 0.1) + >>> ax_win.plot(win) + >>> ax_win.set_title('Filter impulse response') + >>> ax_win.margins(0, 0.1) + >>> ax_filt.plot(filtered) + >>> ax_filt.set_title('Filtered signal') + >>> ax_filt.margins(0, 0.1) + >>> fig.tight_layout() + >>> fig.show() + + """ + volume = np.asarray(in1) + kernel = np.asarray(in2) + + if volume.ndim == kernel.ndim == 0: + return volume * kernel + elif volume.ndim != kernel.ndim: + raise ValueError("volume and kernel should have the same " + "dimensionality") + + if _inputs_swap_needed(mode, volume.shape, kernel.shape): + # Convolution is commutative; order doesn't have any effect on output + volume, kernel = kernel, volume + + if method == 'auto': + method = choose_conv_method(volume, kernel, mode=mode) + + if method == 'fft': + out = fftconvolve(volume, kernel, mode=mode) + result_type = np.result_type(volume, kernel) + if result_type.kind in {'u', 'i'}: + out = np.around(out) + + if np.isnan(out.flat[0]) or np.isinf(out.flat[0]): + warnings.warn("Use of fft convolution on input with NAN or inf" + " results in NAN or inf output. Consider using" + " method='direct' instead.", + category=RuntimeWarning, stacklevel=2) + + return out.astype(result_type) + elif method == 'direct': + # fastpath to faster numpy.convolve for 1d inputs when possible + if _np_conv_ok(volume, kernel, mode): + return np.convolve(volume, kernel, mode) + + return correlate(volume, _reverse_and_conj(kernel), mode, 'direct') + else: + raise ValueError("Acceptable method flags are 'auto'," + " 'direct', or 'fft'.") + + +def order_filter(a, domain, rank): + """ + Perform an order filter on an N-D array. + + Perform an order filter on the array in. The domain argument acts as a + mask centered over each pixel. The non-zero elements of domain are + used to select elements surrounding each input pixel which are placed + in a list. The list is sorted, and the output for that pixel is the + element corresponding to rank in the sorted list. + + Parameters + ---------- + a : ndarray + The N-dimensional input array. + domain : array_like + A mask array with the same number of dimensions as `a`. + Each dimension should have an odd number of elements. + rank : int + A non-negative integer which selects the element from the + sorted list (0 corresponds to the smallest element, 1 is the + next smallest element, etc.). + + Returns + ------- + out : ndarray + The results of the order filter in an array with the same + shape as `a`. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> x = np.arange(25).reshape(5, 5) + >>> domain = np.identity(3) + >>> x + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]]) + >>> signal.order_filter(x, domain, 0) + array([[ 0., 0., 0., 0., 0.], + [ 0., 0., 1., 2., 0.], + [ 0., 5., 6., 7., 0.], + [ 0., 10., 11., 12., 0.], + [ 0., 0., 0., 0., 0.]]) + >>> signal.order_filter(x, domain, 2) + array([[ 6., 7., 8., 9., 4.], + [ 11., 12., 13., 14., 9.], + [ 16., 17., 18., 19., 14.], + [ 21., 22., 23., 24., 19.], + [ 20., 21., 22., 23., 24.]]) + + """ + domain = np.asarray(domain) + for dimsize in domain.shape: + if (dimsize % 2) != 1: + raise ValueError("Each dimension of domain argument " + "should have an odd number of elements.") + + a = np.asarray(a) + if a.dtype in [object, 'float128']: + mesg = (f"Using order_filter with arrays of dtype {a.dtype} is " + f"deprecated in SciPy 1.11 and will be removed in SciPy 1.14") + warnings.warn(mesg, DeprecationWarning, stacklevel=2) + + result = _sigtools._order_filterND(a, domain, rank) + else: + result = ndimage.rank_filter(a, rank, footprint=domain, mode='constant') + + return result + + +def medfilt(volume, kernel_size=None): + """ + Perform a median filter on an N-dimensional array. + + Apply a median filter to the input array using a local window-size + given by `kernel_size`. The array will automatically be zero-padded. + + Parameters + ---------- + volume : array_like + An N-dimensional input array. + kernel_size : array_like, optional + A scalar or an N-length list giving the size of the median filter + window in each dimension. Elements of `kernel_size` should be odd. + If `kernel_size` is a scalar, then this scalar is used as the size in + each dimension. Default size is 3 for each dimension. + + Returns + ------- + out : ndarray + An array the same size as input containing the median filtered + result. + + Warns + ----- + UserWarning + If array size is smaller than kernel size along any dimension + + See Also + -------- + scipy.ndimage.median_filter + scipy.signal.medfilt2d + + Notes + ----- + The more general function `scipy.ndimage.median_filter` has a more + efficient implementation of a median filter and therefore runs much faster. + + For 2-dimensional images with ``uint8``, ``float32`` or ``float64`` dtypes, + the specialised function `scipy.signal.medfilt2d` may be faster. + + """ + volume = np.atleast_1d(volume) + if kernel_size is None: + kernel_size = [3] * volume.ndim + kernel_size = np.asarray(kernel_size) + if kernel_size.shape == (): + kernel_size = np.repeat(kernel_size.item(), volume.ndim) + + for k in range(volume.ndim): + if (kernel_size[k] % 2) != 1: + raise ValueError("Each element of kernel_size should be odd.") + if any(k > s for k, s in zip(kernel_size, volume.shape)): + warnings.warn('kernel_size exceeds volume extent: the volume will be ' + 'zero-padded.', + stacklevel=2) + + domain = np.ones(kernel_size, dtype=volume.dtype) + + numels = np.prod(kernel_size, axis=0) + order = numels // 2 + + if volume.dtype in [np.bool_, np.complex64, np.complex128, np.clongdouble, + np.float16]: + raise ValueError(f"dtype={volume.dtype} is not supported by medfilt") + + if volume.dtype.char in ['O', 'g']: + mesg = (f"Using medfilt with arrays of dtype {volume.dtype} is " + f"deprecated in SciPy 1.11 and will be removed in SciPy 1.14") + warnings.warn(mesg, DeprecationWarning, stacklevel=2) + + result = _sigtools._order_filterND(volume, domain, order) + else: + size = math.prod(kernel_size) + result = ndimage.rank_filter(volume, size // 2, size=kernel_size, + mode='constant') + + return result + + +def wiener(im, mysize=None, noise=None): + """ + Perform a Wiener filter on an N-dimensional array. + + Apply a Wiener filter to the N-dimensional array `im`. + + Parameters + ---------- + im : ndarray + An N-dimensional array. + mysize : int or array_like, optional + A scalar or an N-length list giving the size of the Wiener filter + window in each dimension. Elements of mysize should be odd. + If mysize is a scalar, then this scalar is used as the size + in each dimension. + noise : float, optional + The noise-power to use. If None, then noise is estimated as the + average of the local variance of the input. + + Returns + ------- + out : ndarray + Wiener filtered result with the same shape as `im`. + + Notes + ----- + This implementation is similar to wiener2 in Matlab/Octave. + For more details see [1]_ + + References + ---------- + .. [1] Lim, Jae S., Two-Dimensional Signal and Image Processing, + Englewood Cliffs, NJ, Prentice Hall, 1990, p. 548. + + Examples + -------- + >>> from scipy.datasets import face + >>> from scipy.signal import wiener + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> img = rng.random((40, 40)) #Create a random image + >>> filtered_img = wiener(img, (5, 5)) #Filter the image + >>> f, (plot1, plot2) = plt.subplots(1, 2) + >>> plot1.imshow(img) + >>> plot2.imshow(filtered_img) + >>> plt.show() + + """ + im = np.asarray(im) + if mysize is None: + mysize = [3] * im.ndim + mysize = np.asarray(mysize) + if mysize.shape == (): + mysize = np.repeat(mysize.item(), im.ndim) + + # Estimate the local mean + lMean = correlate(im, np.ones(mysize), 'same') / np.prod(mysize, axis=0) + + # Estimate the local variance + lVar = (correlate(im ** 2, np.ones(mysize), 'same') / + np.prod(mysize, axis=0) - lMean ** 2) + + # Estimate the noise power if needed. + if noise is None: + noise = np.mean(np.ravel(lVar), axis=0) + + res = (im - lMean) + res *= (1 - noise / lVar) + res += lMean + out = np.where(lVar < noise, lMean, res) + + return out + + +def convolve2d(in1, in2, mode='full', boundary='fill', fillvalue=0): + """ + Convolve two 2-dimensional arrays. + + Convolve `in1` and `in2` with output size determined by `mode`, and + boundary conditions determined by `boundary` and `fillvalue`. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear convolution + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + boundary : str {'fill', 'wrap', 'symm'}, optional + A flag indicating how to handle boundaries: + + ``fill`` + pad input arrays with fillvalue. (default) + ``wrap`` + circular boundary conditions. + ``symm`` + symmetrical boundary conditions. + + fillvalue : scalar, optional + Value to fill pad input arrays with. Default is 0. + + Returns + ------- + out : ndarray + A 2-dimensional array containing a subset of the discrete linear + convolution of `in1` with `in2`. + + Examples + -------- + Compute the gradient of an image by 2D convolution with a complex Scharr + operator. (Horizontal operator is real, vertical is imaginary.) Use + symmetric boundary condition to avoid creating edges at the image + boundaries. + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy import datasets + >>> ascent = datasets.ascent() + >>> scharr = np.array([[ -3-3j, 0-10j, +3 -3j], + ... [-10+0j, 0+ 0j, +10 +0j], + ... [ -3+3j, 0+10j, +3 +3j]]) # Gx + j*Gy + >>> grad = signal.convolve2d(ascent, scharr, boundary='symm', mode='same') + + >>> import matplotlib.pyplot as plt + >>> fig, (ax_orig, ax_mag, ax_ang) = plt.subplots(3, 1, figsize=(6, 15)) + >>> ax_orig.imshow(ascent, cmap='gray') + >>> ax_orig.set_title('Original') + >>> ax_orig.set_axis_off() + >>> ax_mag.imshow(np.absolute(grad), cmap='gray') + >>> ax_mag.set_title('Gradient magnitude') + >>> ax_mag.set_axis_off() + >>> ax_ang.imshow(np.angle(grad), cmap='hsv') # hsv is cyclic, like angles + >>> ax_ang.set_title('Gradient orientation') + >>> ax_ang.set_axis_off() + >>> fig.show() + + """ + in1 = np.asarray(in1) + in2 = np.asarray(in2) + + if not in1.ndim == in2.ndim == 2: + raise ValueError('convolve2d inputs must both be 2-D arrays') + + if _inputs_swap_needed(mode, in1.shape, in2.shape): + in1, in2 = in2, in1 + + val = _valfrommode(mode) + bval = _bvalfromboundary(boundary) + out = _sigtools._convolve2d(in1, in2, 1, val, bval, fillvalue) + return out + + +def correlate2d(in1, in2, mode='full', boundary='fill', fillvalue=0): + """ + Cross-correlate two 2-dimensional arrays. + + Cross correlate `in1` and `in2` with output size determined by `mode`, and + boundary conditions determined by `boundary` and `fillvalue`. + + Parameters + ---------- + in1 : array_like + First input. + in2 : array_like + Second input. Should have the same number of dimensions as `in1`. + mode : str {'full', 'valid', 'same'}, optional + A string indicating the size of the output: + + ``full`` + The output is the full discrete linear cross-correlation + of the inputs. (Default) + ``valid`` + The output consists only of those elements that do not + rely on the zero-padding. In 'valid' mode, either `in1` or `in2` + must be at least as large as the other in every dimension. + ``same`` + The output is the same size as `in1`, centered + with respect to the 'full' output. + boundary : str {'fill', 'wrap', 'symm'}, optional + A flag indicating how to handle boundaries: + + ``fill`` + pad input arrays with fillvalue. (default) + ``wrap`` + circular boundary conditions. + ``symm`` + symmetrical boundary conditions. + + fillvalue : scalar, optional + Value to fill pad input arrays with. Default is 0. + + Returns + ------- + correlate2d : ndarray + A 2-dimensional array containing a subset of the discrete linear + cross-correlation of `in1` with `in2`. + + Notes + ----- + When using "same" mode with even-length inputs, the outputs of `correlate` + and `correlate2d` differ: There is a 1-index offset between them. + + Examples + -------- + Use 2D cross-correlation to find the location of a template in a noisy + image: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy import datasets + >>> rng = np.random.default_rng() + >>> face = datasets.face(gray=True) - datasets.face(gray=True).mean() + >>> template = np.copy(face[300:365, 670:750]) # right eye + >>> template -= template.mean() + >>> face = face + rng.standard_normal(face.shape) * 50 # add noise + >>> corr = signal.correlate2d(face, template, boundary='symm', mode='same') + >>> y, x = np.unravel_index(np.argmax(corr), corr.shape) # find the match + + >>> import matplotlib.pyplot as plt + >>> fig, (ax_orig, ax_template, ax_corr) = plt.subplots(3, 1, + ... figsize=(6, 15)) + >>> ax_orig.imshow(face, cmap='gray') + >>> ax_orig.set_title('Original') + >>> ax_orig.set_axis_off() + >>> ax_template.imshow(template, cmap='gray') + >>> ax_template.set_title('Template') + >>> ax_template.set_axis_off() + >>> ax_corr.imshow(corr, cmap='gray') + >>> ax_corr.set_title('Cross-correlation') + >>> ax_corr.set_axis_off() + >>> ax_orig.plot(x, y, 'ro') + >>> fig.show() + + """ + in1 = np.asarray(in1) + in2 = np.asarray(in2) + + if not in1.ndim == in2.ndim == 2: + raise ValueError('correlate2d inputs must both be 2-D arrays') + + swapped_inputs = _inputs_swap_needed(mode, in1.shape, in2.shape) + if swapped_inputs: + in1, in2 = in2, in1 + + val = _valfrommode(mode) + bval = _bvalfromboundary(boundary) + out = _sigtools._convolve2d(in1, in2.conj(), 0, val, bval, fillvalue) + + if swapped_inputs: + out = out[::-1, ::-1] + + return out + + +def medfilt2d(input, kernel_size=3): + """ + Median filter a 2-dimensional array. + + Apply a median filter to the `input` array using a local window-size + given by `kernel_size` (must be odd). The array is zero-padded + automatically. + + Parameters + ---------- + input : array_like + A 2-dimensional input array. + kernel_size : array_like, optional + A scalar or a list of length 2, giving the size of the + median filter window in each dimension. Elements of + `kernel_size` should be odd. If `kernel_size` is a scalar, + then this scalar is used as the size in each dimension. + Default is a kernel of size (3, 3). + + Returns + ------- + out : ndarray + An array the same size as input containing the median filtered + result. + + See Also + -------- + scipy.ndimage.median_filter + + Notes + ----- + This is faster than `medfilt` when the input dtype is ``uint8``, + ``float32``, or ``float64``; for other types, this falls back to + `medfilt`. In some situations, `scipy.ndimage.median_filter` may be + faster than this function. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> x = np.arange(25).reshape(5, 5) + >>> x + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24]]) + + # Replaces i,j with the median out of 5*5 window + + >>> signal.medfilt2d(x, kernel_size=5) + array([[ 0, 0, 2, 0, 0], + [ 0, 3, 7, 4, 0], + [ 2, 8, 12, 9, 4], + [ 0, 8, 12, 9, 0], + [ 0, 0, 12, 0, 0]]) + + # Replaces i,j with the median out of default 3*3 window + + >>> signal.medfilt2d(x) + array([[ 0, 1, 2, 3, 0], + [ 1, 6, 7, 8, 4], + [ 6, 11, 12, 13, 9], + [11, 16, 17, 18, 14], + [ 0, 16, 17, 18, 0]]) + + # Replaces i,j with the median out of default 5*3 window + + >>> signal.medfilt2d(x, kernel_size=[5,3]) + array([[ 0, 1, 2, 3, 0], + [ 0, 6, 7, 8, 3], + [ 5, 11, 12, 13, 8], + [ 5, 11, 12, 13, 8], + [ 0, 11, 12, 13, 0]]) + + # Replaces i,j with the median out of default 3*5 window + + >>> signal.medfilt2d(x, kernel_size=[3,5]) + array([[ 0, 0, 2, 1, 0], + [ 1, 5, 7, 6, 3], + [ 6, 10, 12, 11, 8], + [11, 15, 17, 16, 13], + [ 0, 15, 17, 16, 0]]) + + # As seen in the examples, + # kernel numbers must be odd and not exceed original array dim + + """ + image = np.asarray(input) + + # checking dtype.type, rather than just dtype, is necessary for + # excluding np.longdouble with MS Visual C. + if image.dtype.type not in (np.ubyte, np.float32, np.float64): + return medfilt(image, kernel_size) + + if kernel_size is None: + kernel_size = [3] * 2 + kernel_size = np.asarray(kernel_size) + if kernel_size.shape == (): + kernel_size = np.repeat(kernel_size.item(), 2) + + for size in kernel_size: + if (size % 2) != 1: + raise ValueError("Each element of kernel_size should be odd.") + + return _sigtools._medfilt2d(image, kernel_size) + + +def lfilter(b, a, x, axis=-1, zi=None): + """ + Filter data along one-dimension with an IIR or FIR filter. + + Filter a data sequence, `x`, using a digital filter. This works for many + fundamental data types (including Object type). The filter is a direct + form II transposed implementation of the standard difference equation + (see Notes). + + The function `sosfilt` (and filter design using ``output='sos'``) should be + preferred over `lfilter` for most filtering tasks, as second-order sections + have fewer numerical problems. + + Parameters + ---------- + b : array_like + The numerator coefficient vector in a 1-D sequence. + a : array_like + The denominator coefficient vector in a 1-D sequence. If ``a[0]`` + is not 1, then both `a` and `b` are normalized by ``a[0]``. + x : array_like + An N-dimensional input array. + axis : int, optional + The axis of the input data array along which to apply the + linear filter. The filter is applied to each subarray along + this axis. Default is -1. + zi : array_like, optional + Initial conditions for the filter delays. It is a vector + (or array of vectors for an N-dimensional input) of length + ``max(len(a), len(b)) - 1``. If `zi` is None or is not given then + initial rest is assumed. See `lfiltic` for more information. + + Returns + ------- + y : array + The output of the digital filter. + zf : array, optional + If `zi` is None, this is not returned, otherwise, `zf` holds the + final filter delay values. + + See Also + -------- + lfiltic : Construct initial conditions for `lfilter`. + lfilter_zi : Compute initial state (steady state of step response) for + `lfilter`. + filtfilt : A forward-backward filter, to obtain a filter with zero phase. + savgol_filter : A Savitzky-Golay filter. + sosfilt: Filter data using cascaded second-order sections. + sosfiltfilt: A forward-backward filter using second-order sections. + + Notes + ----- + The filter function is implemented as a direct II transposed structure. + This means that the filter implements:: + + a[0]*y[n] = b[0]*x[n] + b[1]*x[n-1] + ... + b[M]*x[n-M] + - a[1]*y[n-1] - ... - a[N]*y[n-N] + + where `M` is the degree of the numerator, `N` is the degree of the + denominator, and `n` is the sample number. It is implemented using + the following difference equations (assuming M = N):: + + a[0]*y[n] = b[0] * x[n] + d[0][n-1] + d[0][n] = b[1] * x[n] - a[1] * y[n] + d[1][n-1] + d[1][n] = b[2] * x[n] - a[2] * y[n] + d[2][n-1] + ... + d[N-2][n] = b[N-1]*x[n] - a[N-1]*y[n] + d[N-1][n-1] + d[N-1][n] = b[N] * x[n] - a[N] * y[n] + + where `d` are the state variables. + + The rational transfer function describing this filter in the + z-transform domain is:: + + -1 -M + b[0] + b[1]z + ... + b[M] z + Y(z) = -------------------------------- X(z) + -1 -N + a[0] + a[1]z + ... + a[N] z + + Examples + -------- + Generate a noisy signal to be filtered: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> t = np.linspace(-1, 1, 201) + >>> x = (np.sin(2*np.pi*0.75*t*(1-t) + 2.1) + + ... 0.1*np.sin(2*np.pi*1.25*t + 1) + + ... 0.18*np.cos(2*np.pi*3.85*t)) + >>> xn = x + rng.standard_normal(len(t)) * 0.08 + + Create an order 3 lowpass butterworth filter: + + >>> b, a = signal.butter(3, 0.05) + + Apply the filter to xn. Use lfilter_zi to choose the initial condition of + the filter: + + >>> zi = signal.lfilter_zi(b, a) + >>> z, _ = signal.lfilter(b, a, xn, zi=zi*xn[0]) + + Apply the filter again, to have a result filtered at an order the same as + filtfilt: + + >>> z2, _ = signal.lfilter(b, a, z, zi=zi*z[0]) + + Use filtfilt to apply the filter: + + >>> y = signal.filtfilt(b, a, xn) + + Plot the original signal and the various filtered versions: + + >>> plt.figure + >>> plt.plot(t, xn, 'b', alpha=0.75) + >>> plt.plot(t, z, 'r--', t, z2, 'r', t, y, 'k') + >>> plt.legend(('noisy signal', 'lfilter, once', 'lfilter, twice', + ... 'filtfilt'), loc='best') + >>> plt.grid(True) + >>> plt.show() + + """ + b = np.atleast_1d(b) + a = np.atleast_1d(a) + if len(a) == 1: + # This path only supports types fdgFDGO to mirror _linear_filter below. + # Any of b, a, x, or zi can set the dtype, but there is no default + # casting of other types; instead a NotImplementedError is raised. + b = np.asarray(b) + a = np.asarray(a) + if b.ndim != 1 and a.ndim != 1: + raise ValueError('object of too small depth for desired array') + x = _validate_x(x) + inputs = [b, a, x] + if zi is not None: + # _linear_filter does not broadcast zi, but does do expansion of + # singleton dims. + zi = np.asarray(zi) + if zi.ndim != x.ndim: + raise ValueError('object of too small depth for desired array') + expected_shape = list(x.shape) + expected_shape[axis] = b.shape[0] - 1 + expected_shape = tuple(expected_shape) + # check the trivial case where zi is the right shape first + if zi.shape != expected_shape: + strides = zi.ndim * [None] + if axis < 0: + axis += zi.ndim + for k in range(zi.ndim): + if k == axis and zi.shape[k] == expected_shape[k]: + strides[k] = zi.strides[k] + elif k != axis and zi.shape[k] == expected_shape[k]: + strides[k] = zi.strides[k] + elif k != axis and zi.shape[k] == 1: + strides[k] = 0 + else: + raise ValueError('Unexpected shape for zi: expected ' + f'{expected_shape}, found {zi.shape}.') + zi = np.lib.stride_tricks.as_strided(zi, expected_shape, + strides) + inputs.append(zi) + dtype = np.result_type(*inputs) + + if dtype.char not in 'fdgFDGO': + raise NotImplementedError("input type '%s' not supported" % dtype) + + b = np.array(b, dtype=dtype) + a = np.asarray(a, dtype=dtype) + b /= a[0] + x = np.asarray(x, dtype=dtype) + + out_full = np.apply_along_axis(lambda y: np.convolve(b, y), axis, x) + ind = out_full.ndim * [slice(None)] + if zi is not None: + ind[axis] = slice(zi.shape[axis]) + out_full[tuple(ind)] += zi + + ind[axis] = slice(out_full.shape[axis] - len(b) + 1) + out = out_full[tuple(ind)] + + if zi is None: + return out + else: + ind[axis] = slice(out_full.shape[axis] - len(b) + 1, None) + zf = out_full[tuple(ind)] + return out, zf + else: + if zi is None: + return _sigtools._linear_filter(b, a, x, axis) + else: + return _sigtools._linear_filter(b, a, x, axis, zi) + + +def lfiltic(b, a, y, x=None): + """ + Construct initial conditions for lfilter given input and output vectors. + + Given a linear filter (b, a) and initial conditions on the output `y` + and the input `x`, return the initial conditions on the state vector zi + which is used by `lfilter` to generate the output given the input. + + Parameters + ---------- + b : array_like + Linear filter term. + a : array_like + Linear filter term. + y : array_like + Initial conditions. + + If ``N = len(a) - 1``, then ``y = {y[-1], y[-2], ..., y[-N]}``. + + If `y` is too short, it is padded with zeros. + x : array_like, optional + Initial conditions. + + If ``M = len(b) - 1``, then ``x = {x[-1], x[-2], ..., x[-M]}``. + + If `x` is not given, its initial conditions are assumed zero. + + If `x` is too short, it is padded with zeros. + + Returns + ------- + zi : ndarray + The state vector ``zi = {z_0[-1], z_1[-1], ..., z_K-1[-1]}``, + where ``K = max(M, N)``. + + See Also + -------- + lfilter, lfilter_zi + + """ + N = np.size(a) - 1 + M = np.size(b) - 1 + K = max(M, N) + y = np.asarray(y) + + if x is None: + result_type = np.result_type(np.asarray(b), np.asarray(a), y) + if result_type.kind in 'bui': + result_type = np.float64 + x = np.zeros(M, dtype=result_type) + else: + x = np.asarray(x) + + result_type = np.result_type(np.asarray(b), np.asarray(a), y, x) + if result_type.kind in 'bui': + result_type = np.float64 + x = x.astype(result_type) + + L = np.size(x) + if L < M: + x = np.r_[x, np.zeros(M - L)] + + y = y.astype(result_type) + zi = np.zeros(K, result_type) + + L = np.size(y) + if L < N: + y = np.r_[y, np.zeros(N - L)] + + for m in range(M): + zi[m] = np.sum(b[m + 1:] * x[:M - m], axis=0) + + for m in range(N): + zi[m] -= np.sum(a[m + 1:] * y[:N - m], axis=0) + + return zi + + +def deconvolve(signal, divisor): + """Deconvolves ``divisor`` out of ``signal`` using inverse filtering. + + Returns the quotient and remainder such that + ``signal = convolve(divisor, quotient) + remainder`` + + Parameters + ---------- + signal : (N,) array_like + Signal data, typically a recorded signal + divisor : (N,) array_like + Divisor data, typically an impulse response or filter that was + applied to the original signal + + Returns + ------- + quotient : ndarray + Quotient, typically the recovered original signal + remainder : ndarray + Remainder + + See Also + -------- + numpy.polydiv : performs polynomial division (same operation, but + also accepts poly1d objects) + + Examples + -------- + Deconvolve a signal that's been filtered: + + >>> from scipy import signal + >>> original = [0, 1, 0, 0, 1, 1, 0, 0] + >>> impulse_response = [2, 1] + >>> recorded = signal.convolve(impulse_response, original) + >>> recorded + array([0, 2, 1, 0, 2, 3, 1, 0, 0]) + >>> recovered, remainder = signal.deconvolve(recorded, impulse_response) + >>> recovered + array([ 0., 1., 0., 0., 1., 1., 0., 0.]) + + """ + num = np.atleast_1d(signal) + den = np.atleast_1d(divisor) + if num.ndim > 1: + raise ValueError("signal must be 1-D.") + if den.ndim > 1: + raise ValueError("divisor must be 1-D.") + N = len(num) + D = len(den) + if D > N: + quot = [] + rem = num + else: + input = np.zeros(N - D + 1, float) + input[0] = 1 + quot = lfilter(num, den, input) + rem = num - convolve(den, quot, mode='full') + return quot, rem + + +def hilbert(x, N=None, axis=-1): + """ + Compute the analytic signal, using the Hilbert transform. + + The transformation is done along the last axis by default. + + Parameters + ---------- + x : array_like + Signal data. Must be real. + N : int, optional + Number of Fourier components. Default: ``x.shape[axis]`` + axis : int, optional + Axis along which to do the transformation. Default: -1. + + Returns + ------- + xa : ndarray + Analytic signal of `x`, of each 1-D array along `axis` + + Notes + ----- + The analytic signal ``x_a(t)`` of signal ``x(t)`` is: + + .. math:: x_a = F^{-1}(F(x) 2U) = x + i y + + where `F` is the Fourier transform, `U` the unit step function, + and `y` the Hilbert transform of `x`. [1]_ + + In other words, the negative half of the frequency spectrum is zeroed + out, turning the real-valued signal into a complex signal. The Hilbert + transformed signal can be obtained from ``np.imag(hilbert(x))``, and the + original signal from ``np.real(hilbert(x))``. + + References + ---------- + .. [1] Wikipedia, "Analytic signal". + https://en.wikipedia.org/wiki/Analytic_signal + .. [2] Leon Cohen, "Time-Frequency Analysis", 1995. Chapter 2. + .. [3] Alan V. Oppenheim, Ronald W. Schafer. Discrete-Time Signal + Processing, Third Edition, 2009. Chapter 12. + ISBN 13: 978-1292-02572-8 + + Examples + -------- + In this example we use the Hilbert transform to determine the amplitude + envelope and instantaneous frequency of an amplitude-modulated signal. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import hilbert, chirp + + >>> duration = 1.0 + >>> fs = 400.0 + >>> samples = int(fs*duration) + >>> t = np.arange(samples) / fs + + We create a chirp of which the frequency increases from 20 Hz to 100 Hz and + apply an amplitude modulation. + + >>> signal = chirp(t, 20.0, t[-1], 100.0) + >>> signal *= (1.0 + 0.5 * np.sin(2.0*np.pi*3.0*t) ) + + The amplitude envelope is given by magnitude of the analytic signal. The + instantaneous frequency can be obtained by differentiating the + instantaneous phase in respect to time. The instantaneous phase corresponds + to the phase angle of the analytic signal. + + >>> analytic_signal = hilbert(signal) + >>> amplitude_envelope = np.abs(analytic_signal) + >>> instantaneous_phase = np.unwrap(np.angle(analytic_signal)) + >>> instantaneous_frequency = (np.diff(instantaneous_phase) / + ... (2.0*np.pi) * fs) + + >>> fig, (ax0, ax1) = plt.subplots(nrows=2) + >>> ax0.plot(t, signal, label='signal') + >>> ax0.plot(t, amplitude_envelope, label='envelope') + >>> ax0.set_xlabel("time in seconds") + >>> ax0.legend() + >>> ax1.plot(t[1:], instantaneous_frequency) + >>> ax1.set_xlabel("time in seconds") + >>> ax1.set_ylim(0.0, 120.0) + >>> fig.tight_layout() + + """ + x = np.asarray(x) + if np.iscomplexobj(x): + raise ValueError("x must be real.") + if N is None: + N = x.shape[axis] + if N <= 0: + raise ValueError("N must be positive.") + + Xf = sp_fft.fft(x, N, axis=axis) + h = np.zeros(N, dtype=Xf.dtype) + if N % 2 == 0: + h[0] = h[N // 2] = 1 + h[1:N // 2] = 2 + else: + h[0] = 1 + h[1:(N + 1) // 2] = 2 + + if x.ndim > 1: + ind = [np.newaxis] * x.ndim + ind[axis] = slice(None) + h = h[tuple(ind)] + x = sp_fft.ifft(Xf * h, axis=axis) + return x + + +def hilbert2(x, N=None): + """ + Compute the '2-D' analytic signal of `x` + + Parameters + ---------- + x : array_like + 2-D signal data. + N : int or tuple of two ints, optional + Number of Fourier components. Default is ``x.shape`` + + Returns + ------- + xa : ndarray + Analytic signal of `x` taken along axes (0,1). + + References + ---------- + .. [1] Wikipedia, "Analytic signal", + https://en.wikipedia.org/wiki/Analytic_signal + + """ + x = np.atleast_2d(x) + if x.ndim > 2: + raise ValueError("x must be 2-D.") + if np.iscomplexobj(x): + raise ValueError("x must be real.") + if N is None: + N = x.shape + elif isinstance(N, int): + if N <= 0: + raise ValueError("N must be positive.") + N = (N, N) + elif len(N) != 2 or np.any(np.asarray(N) <= 0): + raise ValueError("When given as a tuple, N must hold exactly " + "two positive integers") + + Xf = sp_fft.fft2(x, N, axes=(0, 1)) + h1 = np.zeros(N[0], dtype=Xf.dtype) + h2 = np.zeros(N[1], dtype=Xf.dtype) + for h in (h1, h2): + N1 = h.shape[0] + if N1 % 2 == 0: + h[0] = h[N1 // 2] = 1 + h[1:N1 // 2] = 2 + else: + h[0] = 1 + h[1:(N1 + 1) // 2] = 2 + + h = h1[:, np.newaxis] * h2[np.newaxis, :] + k = x.ndim + while k > 2: + h = h[:, np.newaxis] + k -= 1 + x = sp_fft.ifft2(Xf * h, axes=(0, 1)) + return x + + +_msg_cplx_sort="""cmplx_sort was deprecated in SciPy 1.12 and will be removed +in SciPy 1.15. The exact equivalent for a numpy array argument is +>>> def cmplx_sort(p): +... idx = np.argsort(abs(p)) +... return np.take(p, idx, 0), idx +""" + +def cmplx_sort(p): + warnings.warn(_msg_cplx_sort, DeprecationWarning, stacklevel=2) + return _cmplx_sort(p) + + +def _cmplx_sort(p): + """Sort roots based on magnitude. + + Parameters + ---------- + p : array_like + The roots to sort, as a 1-D array. + + Returns + ------- + p_sorted : ndarray + Sorted roots. + indx : ndarray + Array of indices needed to sort the input `p`. + + Examples + -------- + >>> from scipy import signal + >>> vals = [1, 4, 1+1.j, 3] + >>> p_sorted, indx = signal.cmplx_sort(vals) + >>> p_sorted + array([1.+0.j, 1.+1.j, 3.+0.j, 4.+0.j]) + >>> indx + array([0, 2, 3, 1]) + """ + p = np.asarray(p) + indx = np.argsort(abs(p)) + return np.take(p, indx, 0), indx + + +def unique_roots(p, tol=1e-3, rtype='min'): + """Determine unique roots and their multiplicities from a list of roots. + + Parameters + ---------- + p : array_like + The list of roots. + tol : float, optional + The tolerance for two roots to be considered equal in terms of + the distance between them. Default is 1e-3. Refer to Notes about + the details on roots grouping. + rtype : {'max', 'maximum', 'min', 'minimum', 'avg', 'mean'}, optional + How to determine the returned root if multiple roots are within + `tol` of each other. + + - 'max', 'maximum': pick the maximum of those roots + - 'min', 'minimum': pick the minimum of those roots + - 'avg', 'mean': take the average of those roots + + When finding minimum or maximum among complex roots they are compared + first by the real part and then by the imaginary part. + + Returns + ------- + unique : ndarray + The list of unique roots. + multiplicity : ndarray + The multiplicity of each root. + + Notes + ----- + If we have 3 roots ``a``, ``b`` and ``c``, such that ``a`` is close to + ``b`` and ``b`` is close to ``c`` (distance is less than `tol`), then it + doesn't necessarily mean that ``a`` is close to ``c``. It means that roots + grouping is not unique. In this function we use "greedy" grouping going + through the roots in the order they are given in the input `p`. + + This utility function is not specific to roots but can be used for any + sequence of values for which uniqueness and multiplicity has to be + determined. For a more general routine, see `numpy.unique`. + + Examples + -------- + >>> from scipy import signal + >>> vals = [0, 1.3, 1.31, 2.8, 1.25, 2.2, 10.3] + >>> uniq, mult = signal.unique_roots(vals, tol=2e-2, rtype='avg') + + Check which roots have multiplicity larger than 1: + + >>> uniq[mult > 1] + array([ 1.305]) + """ + if rtype in ['max', 'maximum']: + reduce = np.max + elif rtype in ['min', 'minimum']: + reduce = np.min + elif rtype in ['avg', 'mean']: + reduce = np.mean + else: + raise ValueError("`rtype` must be one of " + "{'max', 'maximum', 'min', 'minimum', 'avg', 'mean'}") + + p = np.asarray(p) + + points = np.empty((len(p), 2)) + points[:, 0] = np.real(p) + points[:, 1] = np.imag(p) + tree = cKDTree(points) + + p_unique = [] + p_multiplicity = [] + used = np.zeros(len(p), dtype=bool) + for i in range(len(p)): + if used[i]: + continue + + group = tree.query_ball_point(points[i], tol) + group = [x for x in group if not used[x]] + + p_unique.append(reduce(p[group])) + p_multiplicity.append(len(group)) + + used[group] = True + + return np.asarray(p_unique), np.asarray(p_multiplicity) + + +def invres(r, p, k, tol=1e-3, rtype='avg'): + """Compute b(s) and a(s) from partial fraction expansion. + + If `M` is the degree of numerator `b` and `N` the degree of denominator + `a`:: + + b(s) b[0] s**(M) + b[1] s**(M-1) + ... + b[M] + H(s) = ------ = ------------------------------------------ + a(s) a[0] s**(N) + a[1] s**(N-1) + ... + a[N] + + then the partial-fraction expansion H(s) is defined as:: + + r[0] r[1] r[-1] + = -------- + -------- + ... + --------- + k(s) + (s-p[0]) (s-p[1]) (s-p[-1]) + + If there are any repeated roots (closer together than `tol`), then H(s) + has terms like:: + + r[i] r[i+1] r[i+n-1] + -------- + ----------- + ... + ----------- + (s-p[i]) (s-p[i])**2 (s-p[i])**n + + This function is used for polynomials in positive powers of s or z, + such as analog filters or digital filters in controls engineering. For + negative powers of z (typical for digital filters in DSP), use `invresz`. + + Parameters + ---------- + r : array_like + Residues corresponding to the poles. For repeated poles, the residues + must be ordered to correspond to ascending by power fractions. + p : array_like + Poles. Equal poles must be adjacent. + k : array_like + Coefficients of the direct polynomial term. + tol : float, optional + The tolerance for two roots to be considered equal in terms of + the distance between them. Default is 1e-3. See `unique_roots` + for further details. + rtype : {'avg', 'min', 'max'}, optional + Method for computing a root to represent a group of identical roots. + Default is 'avg'. See `unique_roots` for further details. + + Returns + ------- + b : ndarray + Numerator polynomial coefficients. + a : ndarray + Denominator polynomial coefficients. + + See Also + -------- + residue, invresz, unique_roots + + """ + r = np.atleast_1d(r) + p = np.atleast_1d(p) + k = np.trim_zeros(np.atleast_1d(k), 'f') + + unique_poles, multiplicity = _group_poles(p, tol, rtype) + factors, denominator = _compute_factors(unique_poles, multiplicity, + include_powers=True) + + if len(k) == 0: + numerator = 0 + else: + numerator = np.polymul(k, denominator) + + for residue, factor in zip(r, factors): + numerator = np.polyadd(numerator, residue * factor) + + return numerator, denominator + + +def _compute_factors(roots, multiplicity, include_powers=False): + """Compute the total polynomial divided by factors for each root.""" + current = np.array([1]) + suffixes = [current] + for pole, mult in zip(roots[-1:0:-1], multiplicity[-1:0:-1]): + monomial = np.array([1, -pole]) + for _ in range(mult): + current = np.polymul(current, monomial) + suffixes.append(current) + suffixes = suffixes[::-1] + + factors = [] + current = np.array([1]) + for pole, mult, suffix in zip(roots, multiplicity, suffixes): + monomial = np.array([1, -pole]) + block = [] + for i in range(mult): + if i == 0 or include_powers: + block.append(np.polymul(current, suffix)) + current = np.polymul(current, monomial) + factors.extend(reversed(block)) + + return factors, current + + +def _compute_residues(poles, multiplicity, numerator): + denominator_factors, _ = _compute_factors(poles, multiplicity) + numerator = numerator.astype(poles.dtype) + + residues = [] + for pole, mult, factor in zip(poles, multiplicity, + denominator_factors): + if mult == 1: + residues.append(np.polyval(numerator, pole) / + np.polyval(factor, pole)) + else: + numer = numerator.copy() + monomial = np.array([1, -pole]) + factor, d = np.polydiv(factor, monomial) + + block = [] + for _ in range(mult): + numer, n = np.polydiv(numer, monomial) + r = n[0] / d[0] + numer = np.polysub(numer, r * factor) + block.append(r) + + residues.extend(reversed(block)) + + return np.asarray(residues) + + +def residue(b, a, tol=1e-3, rtype='avg'): + """Compute partial-fraction expansion of b(s) / a(s). + + If `M` is the degree of numerator `b` and `N` the degree of denominator + `a`:: + + b(s) b[0] s**(M) + b[1] s**(M-1) + ... + b[M] + H(s) = ------ = ------------------------------------------ + a(s) a[0] s**(N) + a[1] s**(N-1) + ... + a[N] + + then the partial-fraction expansion H(s) is defined as:: + + r[0] r[1] r[-1] + = -------- + -------- + ... + --------- + k(s) + (s-p[0]) (s-p[1]) (s-p[-1]) + + If there are any repeated roots (closer together than `tol`), then H(s) + has terms like:: + + r[i] r[i+1] r[i+n-1] + -------- + ----------- + ... + ----------- + (s-p[i]) (s-p[i])**2 (s-p[i])**n + + This function is used for polynomials in positive powers of s or z, + such as analog filters or digital filters in controls engineering. For + negative powers of z (typical for digital filters in DSP), use `residuez`. + + See Notes for details about the algorithm. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + tol : float, optional + The tolerance for two roots to be considered equal in terms of + the distance between them. Default is 1e-3. See `unique_roots` + for further details. + rtype : {'avg', 'min', 'max'}, optional + Method for computing a root to represent a group of identical roots. + Default is 'avg'. See `unique_roots` for further details. + + Returns + ------- + r : ndarray + Residues corresponding to the poles. For repeated poles, the residues + are ordered to correspond to ascending by power fractions. + p : ndarray + Poles ordered by magnitude in ascending order. + k : ndarray + Coefficients of the direct polynomial term. + + See Also + -------- + invres, residuez, numpy.poly, unique_roots + + Notes + ----- + The "deflation through subtraction" algorithm is used for + computations --- method 6 in [1]_. + + The form of partial fraction expansion depends on poles multiplicity in + the exact mathematical sense. However there is no way to exactly + determine multiplicity of roots of a polynomial in numerical computing. + Thus you should think of the result of `residue` with given `tol` as + partial fraction expansion computed for the denominator composed of the + computed poles with empirically determined multiplicity. The choice of + `tol` can drastically change the result if there are close poles. + + References + ---------- + .. [1] J. F. Mahoney, B. D. Sivazlian, "Partial fractions expansion: a + review of computational methodology and efficiency", Journal of + Computational and Applied Mathematics, Vol. 9, 1983. + """ + b = np.asarray(b) + a = np.asarray(a) + if (np.issubdtype(b.dtype, np.complexfloating) + or np.issubdtype(a.dtype, np.complexfloating)): + b = b.astype(complex) + a = a.astype(complex) + else: + b = b.astype(float) + a = a.astype(float) + + b = np.trim_zeros(np.atleast_1d(b), 'f') + a = np.trim_zeros(np.atleast_1d(a), 'f') + + if a.size == 0: + raise ValueError("Denominator `a` is zero.") + + poles = np.roots(a) + if b.size == 0: + return np.zeros(poles.shape), _cmplx_sort(poles)[0], np.array([]) + + if len(b) < len(a): + k = np.empty(0) + else: + k, b = np.polydiv(b, a) + + unique_poles, multiplicity = unique_roots(poles, tol=tol, rtype=rtype) + unique_poles, order = _cmplx_sort(unique_poles) + multiplicity = multiplicity[order] + + residues = _compute_residues(unique_poles, multiplicity, b) + + index = 0 + for pole, mult in zip(unique_poles, multiplicity): + poles[index:index + mult] = pole + index += mult + + return residues / a[0], poles, k + + +def residuez(b, a, tol=1e-3, rtype='avg'): + """Compute partial-fraction expansion of b(z) / a(z). + + If `M` is the degree of numerator `b` and `N` the degree of denominator + `a`:: + + b(z) b[0] + b[1] z**(-1) + ... + b[M] z**(-M) + H(z) = ------ = ------------------------------------------ + a(z) a[0] + a[1] z**(-1) + ... + a[N] z**(-N) + + then the partial-fraction expansion H(z) is defined as:: + + r[0] r[-1] + = --------------- + ... + ---------------- + k[0] + k[1]z**(-1) ... + (1-p[0]z**(-1)) (1-p[-1]z**(-1)) + + If there are any repeated roots (closer than `tol`), then the partial + fraction expansion has terms like:: + + r[i] r[i+1] r[i+n-1] + -------------- + ------------------ + ... + ------------------ + (1-p[i]z**(-1)) (1-p[i]z**(-1))**2 (1-p[i]z**(-1))**n + + This function is used for polynomials in negative powers of z, + such as digital filters in DSP. For positive powers, use `residue`. + + See Notes of `residue` for details about the algorithm. + + Parameters + ---------- + b : array_like + Numerator polynomial coefficients. + a : array_like + Denominator polynomial coefficients. + tol : float, optional + The tolerance for two roots to be considered equal in terms of + the distance between them. Default is 1e-3. See `unique_roots` + for further details. + rtype : {'avg', 'min', 'max'}, optional + Method for computing a root to represent a group of identical roots. + Default is 'avg'. See `unique_roots` for further details. + + Returns + ------- + r : ndarray + Residues corresponding to the poles. For repeated poles, the residues + are ordered to correspond to ascending by power fractions. + p : ndarray + Poles ordered by magnitude in ascending order. + k : ndarray + Coefficients of the direct polynomial term. + + See Also + -------- + invresz, residue, unique_roots + """ + b = np.asarray(b) + a = np.asarray(a) + if (np.issubdtype(b.dtype, np.complexfloating) + or np.issubdtype(a.dtype, np.complexfloating)): + b = b.astype(complex) + a = a.astype(complex) + else: + b = b.astype(float) + a = a.astype(float) + + b = np.trim_zeros(np.atleast_1d(b), 'b') + a = np.trim_zeros(np.atleast_1d(a), 'b') + + if a.size == 0: + raise ValueError("Denominator `a` is zero.") + elif a[0] == 0: + raise ValueError("First coefficient of determinant `a` must be " + "non-zero.") + + poles = np.roots(a) + if b.size == 0: + return np.zeros(poles.shape), _cmplx_sort(poles)[0], np.array([]) + + b_rev = b[::-1] + a_rev = a[::-1] + + if len(b_rev) < len(a_rev): + k_rev = np.empty(0) + else: + k_rev, b_rev = np.polydiv(b_rev, a_rev) + + unique_poles, multiplicity = unique_roots(poles, tol=tol, rtype=rtype) + unique_poles, order = _cmplx_sort(unique_poles) + multiplicity = multiplicity[order] + + residues = _compute_residues(1 / unique_poles, multiplicity, b_rev) + + index = 0 + powers = np.empty(len(residues), dtype=int) + for pole, mult in zip(unique_poles, multiplicity): + poles[index:index + mult] = pole + powers[index:index + mult] = 1 + np.arange(mult) + index += mult + + residues *= (-poles) ** powers / a_rev[0] + + return residues, poles, k_rev[::-1] + + +def _group_poles(poles, tol, rtype): + if rtype in ['max', 'maximum']: + reduce = np.max + elif rtype in ['min', 'minimum']: + reduce = np.min + elif rtype in ['avg', 'mean']: + reduce = np.mean + else: + raise ValueError("`rtype` must be one of " + "{'max', 'maximum', 'min', 'minimum', 'avg', 'mean'}") + + unique = [] + multiplicity = [] + + pole = poles[0] + block = [pole] + for i in range(1, len(poles)): + if abs(poles[i] - pole) <= tol: + block.append(pole) + else: + unique.append(reduce(block)) + multiplicity.append(len(block)) + pole = poles[i] + block = [pole] + + unique.append(reduce(block)) + multiplicity.append(len(block)) + + return np.asarray(unique), np.asarray(multiplicity) + + +def invresz(r, p, k, tol=1e-3, rtype='avg'): + """Compute b(z) and a(z) from partial fraction expansion. + + If `M` is the degree of numerator `b` and `N` the degree of denominator + `a`:: + + b(z) b[0] + b[1] z**(-1) + ... + b[M] z**(-M) + H(z) = ------ = ------------------------------------------ + a(z) a[0] + a[1] z**(-1) + ... + a[N] z**(-N) + + then the partial-fraction expansion H(z) is defined as:: + + r[0] r[-1] + = --------------- + ... + ---------------- + k[0] + k[1]z**(-1) ... + (1-p[0]z**(-1)) (1-p[-1]z**(-1)) + + If there are any repeated roots (closer than `tol`), then the partial + fraction expansion has terms like:: + + r[i] r[i+1] r[i+n-1] + -------------- + ------------------ + ... + ------------------ + (1-p[i]z**(-1)) (1-p[i]z**(-1))**2 (1-p[i]z**(-1))**n + + This function is used for polynomials in negative powers of z, + such as digital filters in DSP. For positive powers, use `invres`. + + Parameters + ---------- + r : array_like + Residues corresponding to the poles. For repeated poles, the residues + must be ordered to correspond to ascending by power fractions. + p : array_like + Poles. Equal poles must be adjacent. + k : array_like + Coefficients of the direct polynomial term. + tol : float, optional + The tolerance for two roots to be considered equal in terms of + the distance between them. Default is 1e-3. See `unique_roots` + for further details. + rtype : {'avg', 'min', 'max'}, optional + Method for computing a root to represent a group of identical roots. + Default is 'avg'. See `unique_roots` for further details. + + Returns + ------- + b : ndarray + Numerator polynomial coefficients. + a : ndarray + Denominator polynomial coefficients. + + See Also + -------- + residuez, unique_roots, invres + + """ + r = np.atleast_1d(r) + p = np.atleast_1d(p) + k = np.trim_zeros(np.atleast_1d(k), 'b') + + unique_poles, multiplicity = _group_poles(p, tol, rtype) + factors, denominator = _compute_factors(unique_poles, multiplicity, + include_powers=True) + + if len(k) == 0: + numerator = 0 + else: + numerator = np.polymul(k[::-1], denominator[::-1]) + + for residue, factor in zip(r, factors): + numerator = np.polyadd(numerator, residue * factor[::-1]) + + return numerator[::-1], denominator + + +def resample(x, num, t=None, axis=0, window=None, domain='time'): + """ + Resample `x` to `num` samples using Fourier method along the given axis. + + The resampled signal starts at the same value as `x` but is sampled + with a spacing of ``len(x) / num * (spacing of x)``. Because a + Fourier method is used, the signal is assumed to be periodic. + + Parameters + ---------- + x : array_like + The data to be resampled. + num : int + The number of samples in the resampled signal. + t : array_like, optional + If `t` is given, it is assumed to be the equally spaced sample + positions associated with the signal data in `x`. + axis : int, optional + The axis of `x` that is resampled. Default is 0. + window : array_like, callable, string, float, or tuple, optional + Specifies the window applied to the signal in the Fourier + domain. See below for details. + domain : string, optional + A string indicating the domain of the input `x`: + ``time`` Consider the input `x` as time-domain (Default), + ``freq`` Consider the input `x` as frequency-domain. + + Returns + ------- + resampled_x or (resampled_x, resampled_t) + Either the resampled array, or, if `t` was given, a tuple + containing the resampled array and the corresponding resampled + positions. + + See Also + -------- + decimate : Downsample the signal after applying an FIR or IIR filter. + resample_poly : Resample using polyphase filtering and an FIR filter. + + Notes + ----- + The argument `window` controls a Fourier-domain window that tapers + the Fourier spectrum before zero-padding to alleviate ringing in + the resampled values for sampled signals you didn't intend to be + interpreted as band-limited. + + If `window` is a function, then it is called with a vector of inputs + indicating the frequency bins (i.e. fftfreq(x.shape[axis]) ). + + If `window` is an array of the same length as `x.shape[axis]` it is + assumed to be the window to be applied directly in the Fourier + domain (with dc and low-frequency first). + + For any other type of `window`, the function `scipy.signal.get_window` + is called to generate the window. + + The first sample of the returned vector is the same as the first + sample of the input vector. The spacing between samples is changed + from ``dx`` to ``dx * len(x) / num``. + + If `t` is not None, then it is used solely to calculate the resampled + positions `resampled_t` + + As noted, `resample` uses FFT transformations, which can be very + slow if the number of input or output samples is large and prime; + see `scipy.fft.fft`. + + Examples + -------- + Note that the end of the resampled data rises to meet the first + sample of the next cycle: + + >>> import numpy as np + >>> from scipy import signal + + >>> x = np.linspace(0, 10, 20, endpoint=False) + >>> y = np.cos(-x**2/6.0) + >>> f = signal.resample(y, 100) + >>> xnew = np.linspace(0, 10, 100, endpoint=False) + + >>> import matplotlib.pyplot as plt + >>> plt.plot(x, y, 'go-', xnew, f, '.-', 10, y[0], 'ro') + >>> plt.legend(['data', 'resampled'], loc='best') + >>> plt.show() + """ + + if domain not in ('time', 'freq'): + raise ValueError("Acceptable domain flags are 'time' or" + f" 'freq', not domain={domain}") + + x = np.asarray(x) + Nx = x.shape[axis] + + # Check if we can use faster real FFT + real_input = np.isrealobj(x) + + if domain == 'time': + # Forward transform + if real_input: + X = sp_fft.rfft(x, axis=axis) + else: # Full complex FFT + X = sp_fft.fft(x, axis=axis) + else: # domain == 'freq' + X = x + + # Apply window to spectrum + if window is not None: + if callable(window): + W = window(sp_fft.fftfreq(Nx)) + elif isinstance(window, np.ndarray): + if window.shape != (Nx,): + raise ValueError('window must have the same length as data') + W = window + else: + W = sp_fft.ifftshift(get_window(window, Nx)) + + newshape_W = [1] * x.ndim + newshape_W[axis] = X.shape[axis] + if real_input: + # Fold the window back on itself to mimic complex behavior + W_real = W.copy() + W_real[1:] += W_real[-1:0:-1] + W_real[1:] *= 0.5 + X *= W_real[:newshape_W[axis]].reshape(newshape_W) + else: + X *= W.reshape(newshape_W) + + # Copy each half of the original spectrum to the output spectrum, either + # truncating high frequencies (downsampling) or zero-padding them + # (upsampling) + + # Placeholder array for output spectrum + newshape = list(x.shape) + if real_input: + newshape[axis] = num // 2 + 1 + else: + newshape[axis] = num + Y = np.zeros(newshape, X.dtype) + + # Copy positive frequency components (and Nyquist, if present) + N = min(num, Nx) + nyq = N // 2 + 1 # Slice index that includes Nyquist if present + sl = [slice(None)] * x.ndim + sl[axis] = slice(0, nyq) + Y[tuple(sl)] = X[tuple(sl)] + if not real_input: + # Copy negative frequency components + if N > 2: # (slice expression doesn't collapse to empty array) + sl[axis] = slice(nyq - N, None) + Y[tuple(sl)] = X[tuple(sl)] + + # Split/join Nyquist component(s) if present + # So far we have set Y[+N/2]=X[+N/2] + if N % 2 == 0: + if num < Nx: # downsampling + if real_input: + sl[axis] = slice(N//2, N//2 + 1) + Y[tuple(sl)] *= 2. + else: + # select the component of Y at frequency +N/2, + # add the component of X at -N/2 + sl[axis] = slice(-N//2, -N//2 + 1) + Y[tuple(sl)] += X[tuple(sl)] + elif Nx < num: # upsampling + # select the component at frequency +N/2 and halve it + sl[axis] = slice(N//2, N//2 + 1) + Y[tuple(sl)] *= 0.5 + if not real_input: + temp = Y[tuple(sl)] + # set the component at -N/2 equal to the component at +N/2 + sl[axis] = slice(num-N//2, num-N//2 + 1) + Y[tuple(sl)] = temp + + # Inverse transform + if real_input: + y = sp_fft.irfft(Y, num, axis=axis) + else: + y = sp_fft.ifft(Y, axis=axis, overwrite_x=True) + + y *= (float(num) / float(Nx)) + + if t is None: + return y + else: + new_t = np.arange(0, num) * (t[1] - t[0]) * Nx / float(num) + t[0] + return y, new_t + + +def resample_poly(x, up, down, axis=0, window=('kaiser', 5.0), + padtype='constant', cval=None): + """ + Resample `x` along the given axis using polyphase filtering. + + The signal `x` is upsampled by the factor `up`, a zero-phase low-pass + FIR filter is applied, and then it is downsampled by the factor `down`. + The resulting sample rate is ``up / down`` times the original sample + rate. By default, values beyond the boundary of the signal are assumed + to be zero during the filtering step. + + Parameters + ---------- + x : array_like + The data to be resampled. + up : int + The upsampling factor. + down : int + The downsampling factor. + axis : int, optional + The axis of `x` that is resampled. Default is 0. + window : string, tuple, or array_like, optional + Desired window to use to design the low-pass filter, or the FIR filter + coefficients to employ. See below for details. + padtype : string, optional + `constant`, `line`, `mean`, `median`, `maximum`, `minimum` or any of + the other signal extension modes supported by `scipy.signal.upfirdn`. + Changes assumptions on values beyond the boundary. If `constant`, + assumed to be `cval` (default zero). If `line` assumed to continue a + linear trend defined by the first and last points. `mean`, `median`, + `maximum` and `minimum` work as in `np.pad` and assume that the values + beyond the boundary are the mean, median, maximum or minimum + respectively of the array along the axis. + + .. versionadded:: 1.4.0 + cval : float, optional + Value to use if `padtype='constant'`. Default is zero. + + .. versionadded:: 1.4.0 + + Returns + ------- + resampled_x : array + The resampled array. + + See Also + -------- + decimate : Downsample the signal after applying an FIR or IIR filter. + resample : Resample up or down using the FFT method. + + Notes + ----- + This polyphase method will likely be faster than the Fourier method + in `scipy.signal.resample` when the number of samples is large and + prime, or when the number of samples is large and `up` and `down` + share a large greatest common denominator. The length of the FIR + filter used will depend on ``max(up, down) // gcd(up, down)``, and + the number of operations during polyphase filtering will depend on + the filter length and `down` (see `scipy.signal.upfirdn` for details). + + The argument `window` specifies the FIR low-pass filter design. + + If `window` is an array_like it is assumed to be the FIR filter + coefficients. Note that the FIR filter is applied after the upsampling + step, so it should be designed to operate on a signal at a sampling + frequency higher than the original by a factor of `up//gcd(up, down)`. + This function's output will be centered with respect to this array, so it + is best to pass a symmetric filter with an odd number of samples if, as + is usually the case, a zero-phase filter is desired. + + For any other type of `window`, the functions `scipy.signal.get_window` + and `scipy.signal.firwin` are called to generate the appropriate filter + coefficients. + + The first sample of the returned vector is the same as the first + sample of the input vector. The spacing between samples is changed + from ``dx`` to ``dx * down / float(up)``. + + Examples + -------- + By default, the end of the resampled data rises to meet the first + sample of the next cycle for the FFT method, and gets closer to zero + for the polyphase method: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> x = np.linspace(0, 10, 20, endpoint=False) + >>> y = np.cos(-x**2/6.0) + >>> f_fft = signal.resample(y, 100) + >>> f_poly = signal.resample_poly(y, 100, 20) + >>> xnew = np.linspace(0, 10, 100, endpoint=False) + + >>> plt.plot(xnew, f_fft, 'b.-', xnew, f_poly, 'r.-') + >>> plt.plot(x, y, 'ko-') + >>> plt.plot(10, y[0], 'bo', 10, 0., 'ro') # boundaries + >>> plt.legend(['resample', 'resamp_poly', 'data'], loc='best') + >>> plt.show() + + This default behaviour can be changed by using the padtype option: + + >>> N = 5 + >>> x = np.linspace(0, 1, N, endpoint=False) + >>> y = 2 + x**2 - 1.7*np.sin(x) + .2*np.cos(11*x) + >>> y2 = 1 + x**3 + 0.1*np.sin(x) + .1*np.cos(11*x) + >>> Y = np.stack([y, y2], axis=-1) + >>> up = 4 + >>> xr = np.linspace(0, 1, N*up, endpoint=False) + + >>> y2 = signal.resample_poly(Y, up, 1, padtype='constant') + >>> y3 = signal.resample_poly(Y, up, 1, padtype='mean') + >>> y4 = signal.resample_poly(Y, up, 1, padtype='line') + + >>> for i in [0,1]: + ... plt.figure() + ... plt.plot(xr, y4[:,i], 'g.', label='line') + ... plt.plot(xr, y3[:,i], 'y.', label='mean') + ... plt.plot(xr, y2[:,i], 'r.', label='constant') + ... plt.plot(x, Y[:,i], 'k-') + ... plt.legend() + >>> plt.show() + + """ + x = np.asarray(x) + if up != int(up): + raise ValueError("up must be an integer") + if down != int(down): + raise ValueError("down must be an integer") + up = int(up) + down = int(down) + if up < 1 or down < 1: + raise ValueError('up and down must be >= 1') + if cval is not None and padtype != 'constant': + raise ValueError('cval has no effect when padtype is ', padtype) + + # Determine our up and down factors + # Use a rational approximation to save computation time on really long + # signals + g_ = math.gcd(up, down) + up //= g_ + down //= g_ + if up == down == 1: + return x.copy() + n_in = x.shape[axis] + n_out = n_in * up + n_out = n_out // down + bool(n_out % down) + + if isinstance(window, (list, np.ndarray)): + window = np.array(window) # use array to force a copy (we modify it) + if window.ndim > 1: + raise ValueError('window must be 1-D') + half_len = (window.size - 1) // 2 + h = window + else: + # Design a linear-phase low-pass FIR filter + max_rate = max(up, down) + f_c = 1. / max_rate # cutoff of FIR filter (rel. to Nyquist) + half_len = 10 * max_rate # reasonable cutoff for sinc-like function + h = firwin(2 * half_len + 1, f_c, + window=window).astype(x.dtype) # match dtype of x + h *= up + + # Zero-pad our filter to put the output samples at the center + n_pre_pad = (down - half_len % down) + n_post_pad = 0 + n_pre_remove = (half_len + n_pre_pad) // down + # We should rarely need to do this given our filter lengths... + while _output_len(len(h) + n_pre_pad + n_post_pad, n_in, + up, down) < n_out + n_pre_remove: + n_post_pad += 1 + h = np.concatenate((np.zeros(n_pre_pad, dtype=h.dtype), h, + np.zeros(n_post_pad, dtype=h.dtype))) + n_pre_remove_end = n_pre_remove + n_out + + # Remove background depending on the padtype option + funcs = {'mean': np.mean, 'median': np.median, + 'minimum': np.amin, 'maximum': np.amax} + upfirdn_kwargs = {'mode': 'constant', 'cval': 0} + if padtype in funcs: + background_values = funcs[padtype](x, axis=axis, keepdims=True) + elif padtype in _upfirdn_modes: + upfirdn_kwargs = {'mode': padtype} + if padtype == 'constant': + if cval is None: + cval = 0 + upfirdn_kwargs['cval'] = cval + else: + raise ValueError( + 'padtype must be one of: maximum, mean, median, minimum, ' + + ', '.join(_upfirdn_modes)) + + if padtype in funcs: + x = x - background_values + + # filter then remove excess + y = upfirdn(h, x, up, down, axis=axis, **upfirdn_kwargs) + keep = [slice(None), ]*x.ndim + keep[axis] = slice(n_pre_remove, n_pre_remove_end) + y_keep = y[tuple(keep)] + + # Add background back + if padtype in funcs: + y_keep += background_values + + return y_keep + + +def vectorstrength(events, period): + ''' + Determine the vector strength of the events corresponding to the given + period. + + The vector strength is a measure of phase synchrony, how well the + timing of the events is synchronized to a single period of a periodic + signal. + + If multiple periods are used, calculate the vector strength of each. + This is called the "resonating vector strength". + + Parameters + ---------- + events : 1D array_like + An array of time points containing the timing of the events. + period : float or array_like + The period of the signal that the events should synchronize to. + The period is in the same units as `events`. It can also be an array + of periods, in which case the outputs are arrays of the same length. + + Returns + ------- + strength : float or 1D array + The strength of the synchronization. 1.0 is perfect synchronization + and 0.0 is no synchronization. If `period` is an array, this is also + an array with each element containing the vector strength at the + corresponding period. + phase : float or array + The phase that the events are most strongly synchronized to in radians. + If `period` is an array, this is also an array with each element + containing the phase for the corresponding period. + + References + ---------- + van Hemmen, JL, Longtin, A, and Vollmayr, AN. Testing resonating vector + strength: Auditory system, electric fish, and noise. + Chaos 21, 047508 (2011); + :doi:`10.1063/1.3670512`. + van Hemmen, JL. Vector strength after Goldberg, Brown, and von Mises: + biological and mathematical perspectives. Biol Cybern. + 2013 Aug;107(4):385-96. :doi:`10.1007/s00422-013-0561-7`. + van Hemmen, JL and Vollmayr, AN. Resonating vector strength: what happens + when we vary the "probing" frequency while keeping the spike times + fixed. Biol Cybern. 2013 Aug;107(4):491-94. + :doi:`10.1007/s00422-013-0560-8`. + ''' + events = np.asarray(events) + period = np.asarray(period) + if events.ndim > 1: + raise ValueError('events cannot have dimensions more than 1') + if period.ndim > 1: + raise ValueError('period cannot have dimensions more than 1') + + # we need to know later if period was originally a scalar + scalarperiod = not period.ndim + + events = np.atleast_2d(events) + period = np.atleast_2d(period) + if (period <= 0).any(): + raise ValueError('periods must be positive') + + # this converts the times to vectors + vectors = np.exp(np.dot(2j*np.pi/period.T, events)) + + # the vector strength is just the magnitude of the mean of the vectors + # the vector phase is the angle of the mean of the vectors + vectormean = np.mean(vectors, axis=1) + strength = abs(vectormean) + phase = np.angle(vectormean) + + # if the original period was a scalar, return scalars + if scalarperiod: + strength = strength[0] + phase = phase[0] + return strength, phase + + +def detrend(data, axis=-1, type='linear', bp=0, overwrite_data=False): + """ + Remove linear trend along axis from data. + + Parameters + ---------- + data : array_like + The input data. + axis : int, optional + The axis along which to detrend the data. By default this is the + last axis (-1). + type : {'linear', 'constant'}, optional + The type of detrending. If ``type == 'linear'`` (default), + the result of a linear least-squares fit to `data` is subtracted + from `data`. + If ``type == 'constant'``, only the mean of `data` is subtracted. + bp : array_like of ints, optional + A sequence of break points. If given, an individual linear fit is + performed for each part of `data` between two break points. + Break points are specified as indices into `data`. This parameter + only has an effect when ``type == 'linear'``. + overwrite_data : bool, optional + If True, perform in place detrending and avoid a copy. Default is False + + Returns + ------- + ret : ndarray + The detrended input data. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> rng = np.random.default_rng() + >>> npoints = 1000 + >>> noise = rng.standard_normal(npoints) + >>> x = 3 + 2*np.linspace(0, 1, npoints) + noise + >>> (signal.detrend(x) - noise).max() + 0.06 # random + + """ + if type not in ['linear', 'l', 'constant', 'c']: + raise ValueError("Trend type must be 'linear' or 'constant'.") + data = np.asarray(data) + dtype = data.dtype.char + if dtype not in 'dfDF': + dtype = 'd' + if type in ['constant', 'c']: + ret = data - np.mean(data, axis, keepdims=True) + return ret + else: + dshape = data.shape + N = dshape[axis] + bp = np.sort(np.unique(np.concatenate(np.atleast_1d(0, bp, N)))) + if np.any(bp > N): + raise ValueError("Breakpoints must be less than length " + "of data along given axis.") + + # Restructure data so that axis is along first dimension and + # all other dimensions are collapsed into second dimension + rnk = len(dshape) + if axis < 0: + axis = axis + rnk + newdata = np.moveaxis(data, axis, 0) + newdata_shape = newdata.shape + newdata = newdata.reshape(N, -1) + + if not overwrite_data: + newdata = newdata.copy() # make sure we have a copy + if newdata.dtype.char not in 'dfDF': + newdata = newdata.astype(dtype) + +# Nreg = len(bp) - 1 + # Find leastsq fit and remove it for each piece + for m in range(len(bp) - 1): + Npts = bp[m + 1] - bp[m] + A = np.ones((Npts, 2), dtype) + A[:, 0] = np.arange(1, Npts + 1, dtype=dtype) / Npts + sl = slice(bp[m], bp[m + 1]) + coef, resids, rank, s = linalg.lstsq(A, newdata[sl]) + newdata[sl] = newdata[sl] - A @ coef + + # Put data back in original shape. + newdata = newdata.reshape(newdata_shape) + ret = np.moveaxis(newdata, 0, axis) + return ret + + +def lfilter_zi(b, a): + """ + Construct initial conditions for lfilter for step response steady-state. + + Compute an initial state `zi` for the `lfilter` function that corresponds + to the steady state of the step response. + + A typical use of this function is to set the initial state so that the + output of the filter starts at the same value as the first element of + the signal to be filtered. + + Parameters + ---------- + b, a : array_like (1-D) + The IIR filter coefficients. See `lfilter` for more + information. + + Returns + ------- + zi : 1-D ndarray + The initial state for the filter. + + See Also + -------- + lfilter, lfiltic, filtfilt + + Notes + ----- + A linear filter with order m has a state space representation (A, B, C, D), + for which the output y of the filter can be expressed as:: + + z(n+1) = A*z(n) + B*x(n) + y(n) = C*z(n) + D*x(n) + + where z(n) is a vector of length m, A has shape (m, m), B has shape + (m, 1), C has shape (1, m) and D has shape (1, 1) (assuming x(n) is + a scalar). lfilter_zi solves:: + + zi = A*zi + B + + In other words, it finds the initial condition for which the response + to an input of all ones is a constant. + + Given the filter coefficients `a` and `b`, the state space matrices + for the transposed direct form II implementation of the linear filter, + which is the implementation used by scipy.signal.lfilter, are:: + + A = scipy.linalg.companion(a).T + B = b[1:] - a[1:]*b[0] + + assuming `a[0]` is 1.0; if `a[0]` is not 1, `a` and `b` are first + divided by a[0]. + + Examples + -------- + The following code creates a lowpass Butterworth filter. Then it + applies that filter to an array whose values are all 1.0; the + output is also all 1.0, as expected for a lowpass filter. If the + `zi` argument of `lfilter` had not been given, the output would have + shown the transient signal. + + >>> from numpy import array, ones + >>> from scipy.signal import lfilter, lfilter_zi, butter + >>> b, a = butter(5, 0.25) + >>> zi = lfilter_zi(b, a) + >>> y, zo = lfilter(b, a, ones(10), zi=zi) + >>> y + array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]) + + Another example: + + >>> x = array([0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0]) + >>> y, zf = lfilter(b, a, x, zi=zi*x[0]) + >>> y + array([ 0.5 , 0.5 , 0.5 , 0.49836039, 0.48610528, + 0.44399389, 0.35505241]) + + Note that the `zi` argument to `lfilter` was computed using + `lfilter_zi` and scaled by `x[0]`. Then the output `y` has no + transient until the input drops from 0.5 to 0.0. + + """ + + # FIXME: Can this function be replaced with an appropriate + # use of lfiltic? For example, when b,a = butter(N,Wn), + # lfiltic(b, a, y=numpy.ones_like(a), x=numpy.ones_like(b)). + # + + # We could use scipy.signal.normalize, but it uses warnings in + # cases where a ValueError is more appropriate, and it allows + # b to be 2D. + b = np.atleast_1d(b) + if b.ndim != 1: + raise ValueError("Numerator b must be 1-D.") + a = np.atleast_1d(a) + if a.ndim != 1: + raise ValueError("Denominator a must be 1-D.") + + while len(a) > 1 and a[0] == 0.0: + a = a[1:] + if a.size < 1: + raise ValueError("There must be at least one nonzero `a` coefficient.") + + if a[0] != 1.0: + # Normalize the coefficients so a[0] == 1. + b = b / a[0] + a = a / a[0] + + n = max(len(a), len(b)) + + # Pad a or b with zeros so they are the same length. + if len(a) < n: + a = np.r_[a, np.zeros(n - len(a), dtype=a.dtype)] + elif len(b) < n: + b = np.r_[b, np.zeros(n - len(b), dtype=b.dtype)] + + IminusA = np.eye(n - 1, dtype=np.result_type(a, b)) - linalg.companion(a).T + B = b[1:] - a[1:] * b[0] + # Solve zi = A*zi + B + zi = np.linalg.solve(IminusA, B) + + # For future reference: we could also use the following + # explicit formulas to solve the linear system: + # + # zi = np.zeros(n - 1) + # zi[0] = B.sum() / IminusA[:,0].sum() + # asum = 1.0 + # csum = 0.0 + # for k in range(1,n-1): + # asum += a[k] + # csum += b[k] - a[k]*b[0] + # zi[k] = asum*zi[0] - csum + + return zi + + +def sosfilt_zi(sos): + """ + Construct initial conditions for sosfilt for step response steady-state. + + Compute an initial state `zi` for the `sosfilt` function that corresponds + to the steady state of the step response. + + A typical use of this function is to set the initial state so that the + output of the filter starts at the same value as the first element of + the signal to be filtered. + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. See `sosfilt` for the SOS filter format + specification. + + Returns + ------- + zi : ndarray + Initial conditions suitable for use with ``sosfilt``, shape + ``(n_sections, 2)``. + + See Also + -------- + sosfilt, zpk2sos + + Notes + ----- + .. versionadded:: 0.16.0 + + Examples + -------- + Filter a rectangular pulse that begins at time 0, with and without + the use of the `zi` argument of `scipy.signal.sosfilt`. + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> sos = signal.butter(9, 0.125, output='sos') + >>> zi = signal.sosfilt_zi(sos) + >>> x = (np.arange(250) < 100).astype(int) + >>> f1 = signal.sosfilt(sos, x) + >>> f2, zo = signal.sosfilt(sos, x, zi=zi) + + >>> plt.plot(x, 'k--', label='x') + >>> plt.plot(f1, 'b', alpha=0.5, linewidth=2, label='filtered') + >>> plt.plot(f2, 'g', alpha=0.25, linewidth=4, label='filtered with zi') + >>> plt.legend(loc='best') + >>> plt.show() + + """ + sos = np.asarray(sos) + if sos.ndim != 2 or sos.shape[1] != 6: + raise ValueError('sos must be shape (n_sections, 6)') + + if sos.dtype.kind in 'bui': + sos = sos.astype(np.float64) + + n_sections = sos.shape[0] + zi = np.empty((n_sections, 2), dtype=sos.dtype) + scale = 1.0 + for section in range(n_sections): + b = sos[section, :3] + a = sos[section, 3:] + zi[section] = scale * lfilter_zi(b, a) + # If H(z) = B(z)/A(z) is this section's transfer function, then + # b.sum()/a.sum() is H(1), the gain at omega=0. That's the steady + # state value of this section's step response. + scale *= b.sum() / a.sum() + + return zi + + +def _filtfilt_gust(b, a, x, axis=-1, irlen=None): + """Forward-backward IIR filter that uses Gustafsson's method. + + Apply the IIR filter defined by `(b,a)` to `x` twice, first forward + then backward, using Gustafsson's initial conditions [1]_. + + Let ``y_fb`` be the result of filtering first forward and then backward, + and let ``y_bf`` be the result of filtering first backward then forward. + Gustafsson's method is to compute initial conditions for the forward + pass and the backward pass such that ``y_fb == y_bf``. + + Parameters + ---------- + b : scalar or 1-D ndarray + Numerator coefficients of the filter. + a : scalar or 1-D ndarray + Denominator coefficients of the filter. + x : ndarray + Data to be filtered. + axis : int, optional + Axis of `x` to be filtered. Default is -1. + irlen : int or None, optional + The length of the nonnegligible part of the impulse response. + If `irlen` is None, or if the length of the signal is less than + ``2 * irlen``, then no part of the impulse response is ignored. + + Returns + ------- + y : ndarray + The filtered data. + x0 : ndarray + Initial condition for the forward filter. + x1 : ndarray + Initial condition for the backward filter. + + Notes + ----- + Typically the return values `x0` and `x1` are not needed by the + caller. The intended use of these return values is in unit tests. + + References + ---------- + .. [1] F. Gustaffson. Determining the initial states in forward-backward + filtering. Transactions on Signal Processing, 46(4):988-992, 1996. + + """ + # In the comments, "Gustafsson's paper" and [1] refer to the + # paper referenced in the docstring. + + b = np.atleast_1d(b) + a = np.atleast_1d(a) + + order = max(len(b), len(a)) - 1 + if order == 0: + # The filter is just scalar multiplication, with no state. + scale = (b[0] / a[0])**2 + y = scale * x + return y, np.array([]), np.array([]) + + if axis != -1 or axis != x.ndim - 1: + # Move the axis containing the data to the end. + x = np.swapaxes(x, axis, x.ndim - 1) + + # n is the number of samples in the data to be filtered. + n = x.shape[-1] + + if irlen is None or n <= 2*irlen: + m = n + else: + m = irlen + + # Create Obs, the observability matrix (called O in the paper). + # This matrix can be interpreted as the operator that propagates + # an arbitrary initial state to the output, assuming the input is + # zero. + # In Gustafsson's paper, the forward and backward filters are not + # necessarily the same, so he has both O_f and O_b. We use the same + # filter in both directions, so we only need O. The same comment + # applies to S below. + Obs = np.zeros((m, order)) + zi = np.zeros(order) + zi[0] = 1 + Obs[:, 0] = lfilter(b, a, np.zeros(m), zi=zi)[0] + for k in range(1, order): + Obs[k:, k] = Obs[:-k, 0] + + # Obsr is O^R (Gustafsson's notation for row-reversed O) + Obsr = Obs[::-1] + + # Create S. S is the matrix that applies the filter to the reversed + # propagated initial conditions. That is, + # out = S.dot(zi) + # is the same as + # tmp, _ = lfilter(b, a, zeros(), zi=zi) # Propagate ICs. + # out = lfilter(b, a, tmp[::-1]) # Reverse and filter. + + # Equations (5) & (6) of [1] + S = lfilter(b, a, Obs[::-1], axis=0) + + # Sr is S^R (row-reversed S) + Sr = S[::-1] + + # M is [(S^R - O), (O^R - S)] + if m == n: + M = np.hstack((Sr - Obs, Obsr - S)) + else: + # Matrix described in section IV of [1]. + M = np.zeros((2*m, 2*order)) + M[:m, :order] = Sr - Obs + M[m:, order:] = Obsr - S + + # Naive forward-backward and backward-forward filters. + # These have large transients because the filters use zero initial + # conditions. + y_f = lfilter(b, a, x) + y_fb = lfilter(b, a, y_f[..., ::-1])[..., ::-1] + + y_b = lfilter(b, a, x[..., ::-1])[..., ::-1] + y_bf = lfilter(b, a, y_b) + + delta_y_bf_fb = y_bf - y_fb + if m == n: + delta = delta_y_bf_fb + else: + start_m = delta_y_bf_fb[..., :m] + end_m = delta_y_bf_fb[..., -m:] + delta = np.concatenate((start_m, end_m), axis=-1) + + # ic_opt holds the "optimal" initial conditions. + # The following code computes the result shown in the formula + # of the paper between equations (6) and (7). + if delta.ndim == 1: + ic_opt = linalg.lstsq(M, delta)[0] + else: + # Reshape delta so it can be used as an array of multiple + # right-hand-sides in linalg.lstsq. + delta2d = delta.reshape(-1, delta.shape[-1]).T + ic_opt0 = linalg.lstsq(M, delta2d)[0].T + ic_opt = ic_opt0.reshape(delta.shape[:-1] + (M.shape[-1],)) + + # Now compute the filtered signal using equation (7) of [1]. + # First, form [S^R, O^R] and call it W. + if m == n: + W = np.hstack((Sr, Obsr)) + else: + W = np.zeros((2*m, 2*order)) + W[:m, :order] = Sr + W[m:, order:] = Obsr + + # Equation (7) of [1] says + # Y_fb^opt = Y_fb^0 + W * [x_0^opt; x_{N-1}^opt] + # `wic` is (almost) the product on the right. + # W has shape (m, 2*order), and ic_opt has shape (..., 2*order), + # so we can't use W.dot(ic_opt). Instead, we dot ic_opt with W.T, + # so wic has shape (..., m). + wic = ic_opt.dot(W.T) + + # `wic` is "almost" the product of W and the optimal ICs in equation + # (7)--if we're using a truncated impulse response (m < n), `wic` + # contains only the adjustments required for the ends of the signal. + # Here we form y_opt, taking this into account if necessary. + y_opt = y_fb + if m == n: + y_opt += wic + else: + y_opt[..., :m] += wic[..., :m] + y_opt[..., -m:] += wic[..., -m:] + + x0 = ic_opt[..., :order] + x1 = ic_opt[..., -order:] + if axis != -1 or axis != x.ndim - 1: + # Restore the data axis to its original position. + x0 = np.swapaxes(x0, axis, x.ndim - 1) + x1 = np.swapaxes(x1, axis, x.ndim - 1) + y_opt = np.swapaxes(y_opt, axis, x.ndim - 1) + + return y_opt, x0, x1 + + +def filtfilt(b, a, x, axis=-1, padtype='odd', padlen=None, method='pad', + irlen=None): + """ + Apply a digital filter forward and backward to a signal. + + This function applies a linear digital filter twice, once forward and + once backwards. The combined filter has zero phase and a filter order + twice that of the original. + + The function provides options for handling the edges of the signal. + + The function `sosfiltfilt` (and filter design using ``output='sos'``) + should be preferred over `filtfilt` for most filtering tasks, as + second-order sections have fewer numerical problems. + + Parameters + ---------- + b : (N,) array_like + The numerator coefficient vector of the filter. + a : (N,) array_like + The denominator coefficient vector of the filter. If ``a[0]`` + is not 1, then both `a` and `b` are normalized by ``a[0]``. + x : array_like + The array of data to be filtered. + axis : int, optional + The axis of `x` to which the filter is applied. + Default is -1. + padtype : str or None, optional + Must be 'odd', 'even', 'constant', or None. This determines the + type of extension to use for the padded signal to which the filter + is applied. If `padtype` is None, no padding is used. The default + is 'odd'. + padlen : int or None, optional + The number of elements by which to extend `x` at both ends of + `axis` before applying the filter. This value must be less than + ``x.shape[axis] - 1``. ``padlen=0`` implies no padding. + The default value is ``3 * max(len(a), len(b))``. + method : str, optional + Determines the method for handling the edges of the signal, either + "pad" or "gust". When `method` is "pad", the signal is padded; the + type of padding is determined by `padtype` and `padlen`, and `irlen` + is ignored. When `method` is "gust", Gustafsson's method is used, + and `padtype` and `padlen` are ignored. + irlen : int or None, optional + When `method` is "gust", `irlen` specifies the length of the + impulse response of the filter. If `irlen` is None, no part + of the impulse response is ignored. For a long signal, specifying + `irlen` can significantly improve the performance of the filter. + + Returns + ------- + y : ndarray + The filtered output with the same shape as `x`. + + See Also + -------- + sosfiltfilt, lfilter_zi, lfilter, lfiltic, savgol_filter, sosfilt + + Notes + ----- + When `method` is "pad", the function pads the data along the given axis + in one of three ways: odd, even or constant. The odd and even extensions + have the corresponding symmetry about the end point of the data. The + constant extension extends the data with the values at the end points. On + both the forward and backward passes, the initial condition of the + filter is found by using `lfilter_zi` and scaling it by the end point of + the extended data. + + When `method` is "gust", Gustafsson's method [1]_ is used. Initial + conditions are chosen for the forward and backward passes so that the + forward-backward filter gives the same result as the backward-forward + filter. + + The option to use Gustaffson's method was added in scipy version 0.16.0. + + References + ---------- + .. [1] F. Gustaffson, "Determining the initial states in forward-backward + filtering", Transactions on Signal Processing, Vol. 46, pp. 988-992, + 1996. + + Examples + -------- + The examples will use several functions from `scipy.signal`. + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + First we create a one second signal that is the sum of two pure sine + waves, with frequencies 5 Hz and 250 Hz, sampled at 2000 Hz. + + >>> t = np.linspace(0, 1.0, 2001) + >>> xlow = np.sin(2 * np.pi * 5 * t) + >>> xhigh = np.sin(2 * np.pi * 250 * t) + >>> x = xlow + xhigh + + Now create a lowpass Butterworth filter with a cutoff of 0.125 times + the Nyquist frequency, or 125 Hz, and apply it to ``x`` with `filtfilt`. + The result should be approximately ``xlow``, with no phase shift. + + >>> b, a = signal.butter(8, 0.125) + >>> y = signal.filtfilt(b, a, x, padlen=150) + >>> np.abs(y - xlow).max() + 9.1086182074789912e-06 + + We get a fairly clean result for this artificial example because + the odd extension is exact, and with the moderately long padding, + the filter's transients have dissipated by the time the actual data + is reached. In general, transient effects at the edges are + unavoidable. + + The following example demonstrates the option ``method="gust"``. + + First, create a filter. + + >>> b, a = signal.ellip(4, 0.01, 120, 0.125) # Filter to be applied. + + `sig` is a random input signal to be filtered. + + >>> rng = np.random.default_rng() + >>> n = 60 + >>> sig = rng.standard_normal(n)**3 + 3*rng.standard_normal(n).cumsum() + + Apply `filtfilt` to `sig`, once using the Gustafsson method, and + once using padding, and plot the results for comparison. + + >>> fgust = signal.filtfilt(b, a, sig, method="gust") + >>> fpad = signal.filtfilt(b, a, sig, padlen=50) + >>> plt.plot(sig, 'k-', label='input') + >>> plt.plot(fgust, 'b-', linewidth=4, label='gust') + >>> plt.plot(fpad, 'c-', linewidth=1.5, label='pad') + >>> plt.legend(loc='best') + >>> plt.show() + + The `irlen` argument can be used to improve the performance + of Gustafsson's method. + + Estimate the impulse response length of the filter. + + >>> z, p, k = signal.tf2zpk(b, a) + >>> eps = 1e-9 + >>> r = np.max(np.abs(p)) + >>> approx_impulse_len = int(np.ceil(np.log(eps) / np.log(r))) + >>> approx_impulse_len + 137 + + Apply the filter to a longer signal, with and without the `irlen` + argument. The difference between `y1` and `y2` is small. For long + signals, using `irlen` gives a significant performance improvement. + + >>> x = rng.standard_normal(4000) + >>> y1 = signal.filtfilt(b, a, x, method='gust') + >>> y2 = signal.filtfilt(b, a, x, method='gust', irlen=approx_impulse_len) + >>> print(np.max(np.abs(y1 - y2))) + 2.875334415008979e-10 + + """ + b = np.atleast_1d(b) + a = np.atleast_1d(a) + x = np.asarray(x) + + if method not in ["pad", "gust"]: + raise ValueError("method must be 'pad' or 'gust'.") + + if method == "gust": + y, z1, z2 = _filtfilt_gust(b, a, x, axis=axis, irlen=irlen) + return y + + # method == "pad" + edge, ext = _validate_pad(padtype, padlen, x, axis, + ntaps=max(len(a), len(b))) + + # Get the steady state of the filter's step response. + zi = lfilter_zi(b, a) + + # Reshape zi and create x0 so that zi*x0 broadcasts + # to the correct value for the 'zi' keyword argument + # to lfilter. + zi_shape = [1] * x.ndim + zi_shape[axis] = zi.size + zi = np.reshape(zi, zi_shape) + x0 = axis_slice(ext, stop=1, axis=axis) + + # Forward filter. + (y, zf) = lfilter(b, a, ext, axis=axis, zi=zi * x0) + + # Backward filter. + # Create y0 so zi*y0 broadcasts appropriately. + y0 = axis_slice(y, start=-1, axis=axis) + (y, zf) = lfilter(b, a, axis_reverse(y, axis=axis), axis=axis, zi=zi * y0) + + # Reverse y. + y = axis_reverse(y, axis=axis) + + if edge > 0: + # Slice the actual signal from the extended signal. + y = axis_slice(y, start=edge, stop=-edge, axis=axis) + + return y + + +def _validate_pad(padtype, padlen, x, axis, ntaps): + """Helper to validate padding for filtfilt""" + if padtype not in ['even', 'odd', 'constant', None]: + raise ValueError(("Unknown value '%s' given to padtype. padtype " + "must be 'even', 'odd', 'constant', or None.") % + padtype) + + if padtype is None: + padlen = 0 + + if padlen is None: + # Original padding; preserved for backwards compatibility. + edge = ntaps * 3 + else: + edge = padlen + + # x's 'axis' dimension must be bigger than edge. + if x.shape[axis] <= edge: + raise ValueError("The length of the input vector x must be greater " + "than padlen, which is %d." % edge) + + if padtype is not None and edge > 0: + # Make an extension of length `edge` at each + # end of the input array. + if padtype == 'even': + ext = even_ext(x, edge, axis=axis) + elif padtype == 'odd': + ext = odd_ext(x, edge, axis=axis) + else: + ext = const_ext(x, edge, axis=axis) + else: + ext = x + return edge, ext + + +def _validate_x(x): + x = np.asarray(x) + if x.ndim == 0: + raise ValueError('x must be at least 1-D') + return x + + +def sosfilt(sos, x, axis=-1, zi=None): + """ + Filter data along one dimension using cascaded second-order sections. + + Filter a data sequence, `x`, using a digital IIR filter defined by + `sos`. + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. Each row corresponds to a second-order + section, with the first three columns providing the numerator + coefficients and the last three providing the denominator + coefficients. + x : array_like + An N-dimensional input array. + axis : int, optional + The axis of the input data array along which to apply the + linear filter. The filter is applied to each subarray along + this axis. Default is -1. + zi : array_like, optional + Initial conditions for the cascaded filter delays. It is a (at + least 2D) vector of shape ``(n_sections, ..., 2, ...)``, where + ``..., 2, ...`` denotes the shape of `x`, but with ``x.shape[axis]`` + replaced by 2. If `zi` is None or is not given then initial rest + (i.e. all zeros) is assumed. + Note that these initial conditions are *not* the same as the initial + conditions given by `lfiltic` or `lfilter_zi`. + + Returns + ------- + y : ndarray + The output of the digital filter. + zf : ndarray, optional + If `zi` is None, this is not returned, otherwise, `zf` holds the + final filter delay values. + + See Also + -------- + zpk2sos, sos2zpk, sosfilt_zi, sosfiltfilt, sosfreqz + + Notes + ----- + The filter function is implemented as a series of second-order filters + with direct-form II transposed structure. It is designed to minimize + numerical precision errors for high-order filters. + + .. versionadded:: 0.16.0 + + Examples + -------- + Plot a 13th-order filter's impulse response using both `lfilter` and + `sosfilt`, showing the instability that results from trying to do a + 13th-order filter in a single stage (the numerical error pushes some poles + outside of the unit circle): + + >>> import matplotlib.pyplot as plt + >>> from scipy import signal + >>> b, a = signal.ellip(13, 0.009, 80, 0.05, output='ba') + >>> sos = signal.ellip(13, 0.009, 80, 0.05, output='sos') + >>> x = signal.unit_impulse(700) + >>> y_tf = signal.lfilter(b, a, x) + >>> y_sos = signal.sosfilt(sos, x) + >>> plt.plot(y_tf, 'r', label='TF') + >>> plt.plot(y_sos, 'k', label='SOS') + >>> plt.legend(loc='best') + >>> plt.show() + + """ + x = _validate_x(x) + sos, n_sections = _validate_sos(sos) + x_zi_shape = list(x.shape) + x_zi_shape[axis] = 2 + x_zi_shape = tuple([n_sections] + x_zi_shape) + inputs = [sos, x] + if zi is not None: + inputs.append(np.asarray(zi)) + dtype = np.result_type(*inputs) + if dtype.char not in 'fdgFDGO': + raise NotImplementedError("input type '%s' not supported" % dtype) + if zi is not None: + zi = np.array(zi, dtype) # make a copy so that we can operate in place + if zi.shape != x_zi_shape: + raise ValueError('Invalid zi shape. With axis=%r, an input with ' + 'shape %r, and an sos array with %d sections, zi ' + 'must have shape %r, got %r.' % + (axis, x.shape, n_sections, x_zi_shape, zi.shape)) + return_zi = True + else: + zi = np.zeros(x_zi_shape, dtype=dtype) + return_zi = False + axis = axis % x.ndim # make positive + x = np.moveaxis(x, axis, -1) + zi = np.moveaxis(zi, [0, axis + 1], [-2, -1]) + x_shape, zi_shape = x.shape, zi.shape + x = np.reshape(x, (-1, x.shape[-1])) + x = np.array(x, dtype, order='C') # make a copy, can modify in place + zi = np.ascontiguousarray(np.reshape(zi, (-1, n_sections, 2))) + sos = sos.astype(dtype, copy=False) + _sosfilt(sos, x, zi) + x.shape = x_shape + x = np.moveaxis(x, -1, axis) + if return_zi: + zi.shape = zi_shape + zi = np.moveaxis(zi, [-2, -1], [0, axis + 1]) + out = (x, zi) + else: + out = x + return out + + +def sosfiltfilt(sos, x, axis=-1, padtype='odd', padlen=None): + """ + A forward-backward digital filter using cascaded second-order sections. + + See `filtfilt` for more complete information about this method. + + Parameters + ---------- + sos : array_like + Array of second-order filter coefficients, must have shape + ``(n_sections, 6)``. Each row corresponds to a second-order + section, with the first three columns providing the numerator + coefficients and the last three providing the denominator + coefficients. + x : array_like + The array of data to be filtered. + axis : int, optional + The axis of `x` to which the filter is applied. + Default is -1. + padtype : str or None, optional + Must be 'odd', 'even', 'constant', or None. This determines the + type of extension to use for the padded signal to which the filter + is applied. If `padtype` is None, no padding is used. The default + is 'odd'. + padlen : int or None, optional + The number of elements by which to extend `x` at both ends of + `axis` before applying the filter. This value must be less than + ``x.shape[axis] - 1``. ``padlen=0`` implies no padding. + The default value is:: + + 3 * (2 * len(sos) + 1 - min((sos[:, 2] == 0).sum(), + (sos[:, 5] == 0).sum())) + + The extra subtraction at the end attempts to compensate for poles + and zeros at the origin (e.g. for odd-order filters) to yield + equivalent estimates of `padlen` to those of `filtfilt` for + second-order section filters built with `scipy.signal` functions. + + Returns + ------- + y : ndarray + The filtered output with the same shape as `x`. + + See Also + -------- + filtfilt, sosfilt, sosfilt_zi, sosfreqz + + Notes + ----- + .. versionadded:: 0.18.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.signal import sosfiltfilt, butter + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Create an interesting signal to filter. + + >>> n = 201 + >>> t = np.linspace(0, 1, n) + >>> x = 1 + (t < 0.5) - 0.25*t**2 + 0.05*rng.standard_normal(n) + + Create a lowpass Butterworth filter, and use it to filter `x`. + + >>> sos = butter(4, 0.125, output='sos') + >>> y = sosfiltfilt(sos, x) + + For comparison, apply an 8th order filter using `sosfilt`. The filter + is initialized using the mean of the first four values of `x`. + + >>> from scipy.signal import sosfilt, sosfilt_zi + >>> sos8 = butter(8, 0.125, output='sos') + >>> zi = x[:4].mean() * sosfilt_zi(sos8) + >>> y2, zo = sosfilt(sos8, x, zi=zi) + + Plot the results. Note that the phase of `y` matches the input, while + `y2` has a significant phase delay. + + >>> plt.plot(t, x, alpha=0.5, label='x(t)') + >>> plt.plot(t, y, label='y(t)') + >>> plt.plot(t, y2, label='y2(t)') + >>> plt.legend(framealpha=1, shadow=True) + >>> plt.grid(alpha=0.25) + >>> plt.xlabel('t') + >>> plt.show() + + """ + sos, n_sections = _validate_sos(sos) + x = _validate_x(x) + + # `method` is "pad"... + ntaps = 2 * n_sections + 1 + ntaps -= min((sos[:, 2] == 0).sum(), (sos[:, 5] == 0).sum()) + edge, ext = _validate_pad(padtype, padlen, x, axis, + ntaps=ntaps) + + # These steps follow the same form as filtfilt with modifications + zi = sosfilt_zi(sos) # shape (n_sections, 2) --> (n_sections, ..., 2, ...) + zi_shape = [1] * x.ndim + zi_shape[axis] = 2 + zi.shape = [n_sections] + zi_shape + x_0 = axis_slice(ext, stop=1, axis=axis) + (y, zf) = sosfilt(sos, ext, axis=axis, zi=zi * x_0) + y_0 = axis_slice(y, start=-1, axis=axis) + (y, zf) = sosfilt(sos, axis_reverse(y, axis=axis), axis=axis, zi=zi * y_0) + y = axis_reverse(y, axis=axis) + if edge > 0: + y = axis_slice(y, start=edge, stop=-edge, axis=axis) + return y + + +def decimate(x, q, n=None, ftype='iir', axis=-1, zero_phase=True): + """ + Downsample the signal after applying an anti-aliasing filter. + + By default, an order 8 Chebyshev type I filter is used. A 30 point FIR + filter with Hamming window is used if `ftype` is 'fir'. + + Parameters + ---------- + x : array_like + The signal to be downsampled, as an N-dimensional array. + q : int + The downsampling factor. When using IIR downsampling, it is recommended + to call `decimate` multiple times for downsampling factors higher than + 13. + n : int, optional + The order of the filter (1 less than the length for 'fir'). Defaults to + 8 for 'iir' and 20 times the downsampling factor for 'fir'. + ftype : str {'iir', 'fir'} or ``dlti`` instance, optional + If 'iir' or 'fir', specifies the type of lowpass filter. If an instance + of an `dlti` object, uses that object to filter before downsampling. + axis : int, optional + The axis along which to decimate. + zero_phase : bool, optional + Prevent phase shift by filtering with `filtfilt` instead of `lfilter` + when using an IIR filter, and shifting the outputs back by the filter's + group delay when using an FIR filter. The default value of ``True`` is + recommended, since a phase shift is generally not desired. + + .. versionadded:: 0.18.0 + + Returns + ------- + y : ndarray + The down-sampled signal. + + See Also + -------- + resample : Resample up or down using the FFT method. + resample_poly : Resample using polyphase filtering and an FIR filter. + + Notes + ----- + The ``zero_phase`` keyword was added in 0.18.0. + The possibility to use instances of ``dlti`` as ``ftype`` was added in + 0.18.0. + + Examples + -------- + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + Define wave parameters. + + >>> wave_duration = 3 + >>> sample_rate = 100 + >>> freq = 2 + >>> q = 5 + + Calculate number of samples. + + >>> samples = wave_duration*sample_rate + >>> samples_decimated = int(samples/q) + + Create cosine wave. + + >>> x = np.linspace(0, wave_duration, samples, endpoint=False) + >>> y = np.cos(x*np.pi*freq*2) + + Decimate cosine wave. + + >>> ydem = signal.decimate(y, q) + >>> xnew = np.linspace(0, wave_duration, samples_decimated, endpoint=False) + + Plot original and decimated waves. + + >>> plt.plot(x, y, '.-', xnew, ydem, 'o-') + >>> plt.xlabel('Time, Seconds') + >>> plt.legend(['data', 'decimated'], loc='best') + >>> plt.show() + + """ + + x = np.asarray(x) + q = operator.index(q) + + if n is not None: + n = operator.index(n) + + result_type = x.dtype + if not np.issubdtype(result_type, np.inexact) \ + or result_type.type == np.float16: + # upcast integers and float16 to float64 + result_type = np.float64 + + if ftype == 'fir': + if n is None: + half_len = 10 * q # reasonable cutoff for our sinc-like function + n = 2 * half_len + b, a = firwin(n+1, 1. / q, window='hamming'), 1. + b = np.asarray(b, dtype=result_type) + a = np.asarray(a, dtype=result_type) + elif ftype == 'iir': + iir_use_sos = True + if n is None: + n = 8 + sos = cheby1(n, 0.05, 0.8 / q, output='sos') + sos = np.asarray(sos, dtype=result_type) + elif isinstance(ftype, dlti): + system = ftype._as_zpk() + if system.poles.shape[0] == 0: + # FIR + system = ftype._as_tf() + b, a = system.num, system.den + ftype = 'fir' + elif (any(np.iscomplex(system.poles)) + or any(np.iscomplex(system.poles)) + or np.iscomplex(system.gain)): + # sosfilt & sosfiltfilt don't handle complex coeffs + iir_use_sos = False + system = ftype._as_tf() + b, a = system.num, system.den + else: + iir_use_sos = True + sos = zpk2sos(system.zeros, system.poles, system.gain) + sos = np.asarray(sos, dtype=result_type) + else: + raise ValueError('invalid ftype') + + sl = [slice(None)] * x.ndim + + if ftype == 'fir': + b = b / a + if zero_phase: + y = resample_poly(x, 1, q, axis=axis, window=b) + else: + # upfirdn is generally faster than lfilter by a factor equal to the + # downsampling factor, since it only calculates the needed outputs + n_out = x.shape[axis] // q + bool(x.shape[axis] % q) + y = upfirdn(b, x, up=1, down=q, axis=axis) + sl[axis] = slice(None, n_out, None) + + else: # IIR case + if zero_phase: + if iir_use_sos: + y = sosfiltfilt(sos, x, axis=axis) + else: + y = filtfilt(b, a, x, axis=axis) + else: + if iir_use_sos: + y = sosfilt(sos, x, axis=axis) + else: + y = lfilter(b, a, x, axis=axis) + + sl[axis] = slice(None, None, q) + + return y[tuple(sl)] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral.cpython-39-darwin.so b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral.cpython-39-darwin.so new file mode 100644 index 0000000000000000000000000000000000000000..78b5b039b0b80dfb64d649b72aca493caaaa65f7 Binary files /dev/null and b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral.cpython-39-darwin.so differ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral_py.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral_py.py new file mode 100644 index 0000000000000000000000000000000000000000..ef7fd2e6e8b79f264f733e54420066de867310a5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spectral_py.py @@ -0,0 +1,2101 @@ +"""Tools for spectral analysis. +""" +import numpy as np +from scipy import fft as sp_fft +from . import _signaltools +from .windows import get_window +from ._spectral import _lombscargle +from ._arraytools import const_ext, even_ext, odd_ext, zero_ext +import warnings + + +__all__ = ['periodogram', 'welch', 'lombscargle', 'csd', 'coherence', + 'spectrogram', 'stft', 'istft', 'check_COLA', 'check_NOLA'] + + +def lombscargle(x, + y, + freqs, + precenter=False, + normalize=False): + """ + lombscargle(x, y, freqs) + + Computes the Lomb-Scargle periodogram. + + The Lomb-Scargle periodogram was developed by Lomb [1]_ and further + extended by Scargle [2]_ to find, and test the significance of weak + periodic signals with uneven temporal sampling. + + When *normalize* is False (default) the computed periodogram + is unnormalized, it takes the value ``(A**2) * N/4`` for a harmonic + signal with amplitude A for sufficiently large N. + + When *normalize* is True the computed periodogram is normalized by + the residuals of the data around a constant reference model (at zero). + + Input arrays should be 1-D and will be cast to float64. + + Parameters + ---------- + x : array_like + Sample times. + y : array_like + Measurement values. + freqs : array_like + Angular frequencies for output periodogram. + precenter : bool, optional + Pre-center measurement values by subtracting the mean. + normalize : bool, optional + Compute normalized periodogram. + + Returns + ------- + pgram : array_like + Lomb-Scargle periodogram. + + Raises + ------ + ValueError + If the input arrays `x` and `y` do not have the same shape. + + See Also + -------- + istft: Inverse Short Time Fourier Transform + check_COLA: Check whether the Constant OverLap Add (COLA) constraint is met + welch: Power spectral density by Welch's method + spectrogram: Spectrogram by Welch's method + csd: Cross spectral density by Welch's method + + Notes + ----- + This subroutine calculates the periodogram using a slightly + modified algorithm due to Townsend [3]_ which allows the + periodogram to be calculated using only a single pass through + the input arrays for each frequency. + + The algorithm running time scales roughly as O(x * freqs) or O(N^2) + for a large number of samples and frequencies. + + References + ---------- + .. [1] N.R. Lomb "Least-squares frequency analysis of unequally spaced + data", Astrophysics and Space Science, vol 39, pp. 447-462, 1976 + + .. [2] J.D. Scargle "Studies in astronomical time series analysis. II - + Statistical aspects of spectral analysis of unevenly spaced data", + The Astrophysical Journal, vol 263, pp. 835-853, 1982 + + .. [3] R.H.D. Townsend, "Fast calculation of the Lomb-Scargle + periodogram using graphics processing units.", The Astrophysical + Journal Supplement Series, vol 191, pp. 247-253, 2010 + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + First define some input parameters for the signal: + + >>> A = 2. + >>> w0 = 1. # rad/sec + >>> nin = 150 + >>> nout = 100000 + + Randomly generate sample times: + + >>> x = rng.uniform(0, 10*np.pi, nin) + + Plot a sine wave for the selected times: + + >>> y = A * np.cos(w0*x) + + Define the array of frequencies for which to compute the periodogram: + + >>> w = np.linspace(0.01, 10, nout) + + Calculate Lomb-Scargle periodogram: + + >>> import scipy.signal as signal + >>> pgram = signal.lombscargle(x, y, w, normalize=True) + + Now make a plot of the input data: + + >>> fig, (ax_t, ax_w) = plt.subplots(2, 1, constrained_layout=True) + >>> ax_t.plot(x, y, 'b+') + >>> ax_t.set_xlabel('Time [s]') + + Then plot the normalized periodogram: + + >>> ax_w.plot(w, pgram) + >>> ax_w.set_xlabel('Angular frequency [rad/s]') + >>> ax_w.set_ylabel('Normalized amplitude') + >>> plt.show() + + """ + x = np.ascontiguousarray(x, dtype=np.float64) + y = np.ascontiguousarray(y, dtype=np.float64) + freqs = np.ascontiguousarray(freqs, dtype=np.float64) + + assert x.ndim == 1 + assert y.ndim == 1 + assert freqs.ndim == 1 + + if precenter: + pgram = _lombscargle(x, y - y.mean(), freqs) + else: + pgram = _lombscargle(x, y, freqs) + + if normalize: + pgram *= 2 / np.dot(y, y) + + return pgram + + +def periodogram(x, fs=1.0, window='boxcar', nfft=None, detrend='constant', + return_onesided=True, scaling='density', axis=-1): + """ + Estimate power spectral density using a periodogram. + + Parameters + ---------- + x : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be equal to the length + of the axis over which the periodogram is computed. Defaults + to 'boxcar'. + nfft : int, optional + Length of the FFT used. If `None` the length of `x` will be + used. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + scaling : { 'density', 'spectrum' }, optional + Selects between computing the power spectral density ('density') + where `Pxx` has units of V**2/Hz and computing the squared magnitude + spectrum ('spectrum') where `Pxx` has units of V**2, if `x` + is measured in V and `fs` is measured in Hz. Defaults to + 'density' + axis : int, optional + Axis along which the periodogram is computed; the default is + over the last axis (i.e. ``axis=-1``). + + Returns + ------- + f : ndarray + Array of sample frequencies. + Pxx : ndarray + Power spectral density or power spectrum of `x`. + + See Also + -------- + welch: Estimate power spectral density using Welch's method + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + + Notes + ----- + Consult the :ref:`tutorial_SpectralAnalysis` section of the :ref:`user_guide` + for a discussion of the scalings of the power spectral density and + the magnitude (squared) spectrum. + + .. versionadded:: 0.12.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate a test signal, a 2 Vrms sine wave at 1234 Hz, corrupted by + 0.001 V**2/Hz of white noise sampled at 10 kHz. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 2*np.sqrt(2) + >>> freq = 1234.0 + >>> noise_power = 0.001 * fs / 2 + >>> time = np.arange(N) / fs + >>> x = amp*np.sin(2*np.pi*freq*time) + >>> x += rng.normal(scale=np.sqrt(noise_power), size=time.shape) + + Compute and plot the power spectral density. + + >>> f, Pxx_den = signal.periodogram(x, fs) + >>> plt.semilogy(f, Pxx_den) + >>> plt.ylim([1e-7, 1e2]) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('PSD [V**2/Hz]') + >>> plt.show() + + If we average the last half of the spectral density, to exclude the + peak, we can recover the noise power on the signal. + + >>> np.mean(Pxx_den[25000:]) + 0.000985320699252543 + + Now compute and plot the power spectrum. + + >>> f, Pxx_spec = signal.periodogram(x, fs, 'flattop', scaling='spectrum') + >>> plt.figure() + >>> plt.semilogy(f, np.sqrt(Pxx_spec)) + >>> plt.ylim([1e-4, 1e1]) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('Linear spectrum [V RMS]') + >>> plt.show() + + The peak height in the power spectrum is an estimate of the RMS + amplitude. + + >>> np.sqrt(Pxx_spec.max()) + 2.0077340678640727 + + """ + x = np.asarray(x) + + if x.size == 0: + return np.empty(x.shape), np.empty(x.shape) + + if window is None: + window = 'boxcar' + + if nfft is None: + nperseg = x.shape[axis] + elif nfft == x.shape[axis]: + nperseg = nfft + elif nfft > x.shape[axis]: + nperseg = x.shape[axis] + elif nfft < x.shape[axis]: + s = [np.s_[:]]*len(x.shape) + s[axis] = np.s_[:nfft] + x = x[tuple(s)] + nperseg = nfft + nfft = None + + if hasattr(window, 'size'): + if window.size != nperseg: + raise ValueError('the size of the window must be the same size ' + 'of the input on the specified axis') + + return welch(x, fs=fs, window=window, nperseg=nperseg, noverlap=0, + nfft=nfft, detrend=detrend, return_onesided=return_onesided, + scaling=scaling, axis=axis) + + +def welch(x, fs=1.0, window='hann', nperseg=None, noverlap=None, nfft=None, + detrend='constant', return_onesided=True, scaling='density', + axis=-1, average='mean'): + r""" + Estimate power spectral density using Welch's method. + + Welch's method [1]_ computes an estimate of the power spectral + density by dividing the data into overlapping segments, computing a + modified periodogram for each segment and averaging the + periodograms. + + Parameters + ---------- + x : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. + nperseg : int, optional + Length of each segment. Defaults to None, but if window is str or + tuple, is set to 256, and if window is array_like, is set to the + length of the window. + noverlap : int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 2``. Defaults to `None`. + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + scaling : { 'density', 'spectrum' }, optional + Selects between computing the power spectral density ('density') + where `Pxx` has units of V**2/Hz and computing the squared magnitude + spectrum ('spectrum') where `Pxx` has units of V**2, if `x` + is measured in V and `fs` is measured in Hz. Defaults to + 'density' + axis : int, optional + Axis along which the periodogram is computed; the default is + over the last axis (i.e. ``axis=-1``). + average : { 'mean', 'median' }, optional + Method to use when averaging periodograms. Defaults to 'mean'. + + .. versionadded:: 1.2.0 + + Returns + ------- + f : ndarray + Array of sample frequencies. + Pxx : ndarray + Power spectral density or power spectrum of x. + + See Also + -------- + periodogram: Simple, optionally modified periodogram + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + + Notes + ----- + An appropriate amount of overlap will depend on the choice of window + and on your requirements. For the default Hann window an overlap of + 50% is a reasonable trade off between accurately estimating the + signal power, while not over counting any of the data. Narrower + windows may require a larger overlap. + + If `noverlap` is 0, this method is equivalent to Bartlett's method + [2]_. + + Consult the :ref:`tutorial_SpectralAnalysis` section of the :ref:`user_guide` + for a discussion of the scalings of the power spectral density and + the (squared) magnitude spectrum. + + .. versionadded:: 0.12.0 + + References + ---------- + .. [1] P. Welch, "The use of the fast Fourier transform for the + estimation of power spectra: A method based on time averaging + over short, modified periodograms", IEEE Trans. Audio + Electroacoust. vol. 15, pp. 70-73, 1967. + .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra", + Biometrika, vol. 37, pp. 1-16, 1950. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate a test signal, a 2 Vrms sine wave at 1234 Hz, corrupted by + 0.001 V**2/Hz of white noise sampled at 10 kHz. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 2*np.sqrt(2) + >>> freq = 1234.0 + >>> noise_power = 0.001 * fs / 2 + >>> time = np.arange(N) / fs + >>> x = amp*np.sin(2*np.pi*freq*time) + >>> x += rng.normal(scale=np.sqrt(noise_power), size=time.shape) + + Compute and plot the power spectral density. + + >>> f, Pxx_den = signal.welch(x, fs, nperseg=1024) + >>> plt.semilogy(f, Pxx_den) + >>> plt.ylim([0.5e-3, 1]) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('PSD [V**2/Hz]') + >>> plt.show() + + If we average the last half of the spectral density, to exclude the + peak, we can recover the noise power on the signal. + + >>> np.mean(Pxx_den[256:]) + 0.0009924865443739191 + + Now compute and plot the power spectrum. + + >>> f, Pxx_spec = signal.welch(x, fs, 'flattop', 1024, scaling='spectrum') + >>> plt.figure() + >>> plt.semilogy(f, np.sqrt(Pxx_spec)) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('Linear spectrum [V RMS]') + >>> plt.show() + + The peak height in the power spectrum is an estimate of the RMS + amplitude. + + >>> np.sqrt(Pxx_spec.max()) + 2.0077340678640727 + + If we now introduce a discontinuity in the signal, by increasing the + amplitude of a small portion of the signal by 50, we can see the + corruption of the mean average power spectral density, but using a + median average better estimates the normal behaviour. + + >>> x[int(N//2):int(N//2)+10] *= 50. + >>> f, Pxx_den = signal.welch(x, fs, nperseg=1024) + >>> f_med, Pxx_den_med = signal.welch(x, fs, nperseg=1024, average='median') + >>> plt.semilogy(f, Pxx_den, label='mean') + >>> plt.semilogy(f_med, Pxx_den_med, label='median') + >>> plt.ylim([0.5e-3, 1]) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('PSD [V**2/Hz]') + >>> plt.legend() + >>> plt.show() + + """ + freqs, Pxx = csd(x, x, fs=fs, window=window, nperseg=nperseg, + noverlap=noverlap, nfft=nfft, detrend=detrend, + return_onesided=return_onesided, scaling=scaling, + axis=axis, average=average) + + return freqs, Pxx.real + + +def csd(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, nfft=None, + detrend='constant', return_onesided=True, scaling='density', + axis=-1, average='mean'): + r""" + Estimate the cross power spectral density, Pxy, using Welch's method. + + Parameters + ---------- + x : array_like + Time series of measurement values + y : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` and `y` time series. Defaults + to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. + nperseg : int, optional + Length of each segment. Defaults to None, but if window is str or + tuple, is set to 256, and if window is array_like, is set to the + length of the window. + noverlap: int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 2``. Defaults to `None`. + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + scaling : { 'density', 'spectrum' }, optional + Selects between computing the cross spectral density ('density') + where `Pxy` has units of V**2/Hz and computing the cross spectrum + ('spectrum') where `Pxy` has units of V**2, if `x` and `y` are + measured in V and `fs` is measured in Hz. Defaults to 'density' + axis : int, optional + Axis along which the CSD is computed for both inputs; the + default is over the last axis (i.e. ``axis=-1``). + average : { 'mean', 'median' }, optional + Method to use when averaging periodograms. If the spectrum is + complex, the average is computed separately for the real and + imaginary parts. Defaults to 'mean'. + + .. versionadded:: 1.2.0 + + Returns + ------- + f : ndarray + Array of sample frequencies. + Pxy : ndarray + Cross spectral density or cross power spectrum of x,y. + + See Also + -------- + periodogram: Simple, optionally modified periodogram + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + welch: Power spectral density by Welch's method. [Equivalent to + csd(x,x)] + coherence: Magnitude squared coherence by Welch's method. + + Notes + ----- + By convention, Pxy is computed with the conjugate FFT of X + multiplied by the FFT of Y. + + If the input series differ in length, the shorter series will be + zero-padded to match. + + An appropriate amount of overlap will depend on the choice of window + and on your requirements. For the default Hann window an overlap of + 50% is a reasonable trade off between accurately estimating the + signal power, while not over counting any of the data. Narrower + windows may require a larger overlap. + + Consult the :ref:`tutorial_SpectralAnalysis` section of the :ref:`user_guide` + for a discussion of the scalings of a spectral density and an (amplitude) spectrum. + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] P. Welch, "The use of the fast Fourier transform for the + estimation of power spectra: A method based on time averaging + over short, modified periodograms", IEEE Trans. Audio + Electroacoust. vol. 15, pp. 70-73, 1967. + .. [2] Rabiner, Lawrence R., and B. Gold. "Theory and Application of + Digital Signal Processing" Prentice-Hall, pp. 414-419, 1975 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate two test signals with some common features. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 20 + >>> freq = 1234.0 + >>> noise_power = 0.001 * fs / 2 + >>> time = np.arange(N) / fs + >>> b, a = signal.butter(2, 0.25, 'low') + >>> x = rng.normal(scale=np.sqrt(noise_power), size=time.shape) + >>> y = signal.lfilter(b, a, x) + >>> x += amp*np.sin(2*np.pi*freq*time) + >>> y += rng.normal(scale=0.1*np.sqrt(noise_power), size=time.shape) + + Compute and plot the magnitude of the cross spectral density. + + >>> f, Pxy = signal.csd(x, y, fs, nperseg=1024) + >>> plt.semilogy(f, np.abs(Pxy)) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('CSD [V**2/Hz]') + >>> plt.show() + + """ + freqs, _, Pxy = _spectral_helper(x, y, fs, window, nperseg, noverlap, + nfft, detrend, return_onesided, scaling, + axis, mode='psd') + + # Average over windows. + if len(Pxy.shape) >= 2 and Pxy.size > 0: + if Pxy.shape[-1] > 1: + if average == 'median': + # np.median must be passed real arrays for the desired result + bias = _median_bias(Pxy.shape[-1]) + if np.iscomplexobj(Pxy): + Pxy = (np.median(np.real(Pxy), axis=-1) + + 1j * np.median(np.imag(Pxy), axis=-1)) + else: + Pxy = np.median(Pxy, axis=-1) + Pxy /= bias + elif average == 'mean': + Pxy = Pxy.mean(axis=-1) + else: + raise ValueError(f'average must be "median" or "mean", got {average}') + else: + Pxy = np.reshape(Pxy, Pxy.shape[:-1]) + + return freqs, Pxy + + +def spectrogram(x, fs=1.0, window=('tukey', .25), nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1, mode='psd'): + """Compute a spectrogram with consecutive Fourier transforms (legacy function). + + Spectrograms can be used as a way of visualizing the change of a + nonstationary signal's frequency content over time. + + .. legacy:: function + + :class:`ShortTimeFFT` is a newer STFT / ISTFT implementation with more + features also including a :meth:`~ShortTimeFFT.spectrogram` method. + A :ref:`comparison ` between the + implementations can be found in the :ref:`tutorial_stft` section of + the :ref:`user_guide`. + + Parameters + ---------- + x : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. + Defaults to a Tukey window with shape parameter of 0.25. + nperseg : int, optional + Length of each segment. Defaults to None, but if window is str or + tuple, is set to 256, and if window is array_like, is set to the + length of the window. + noverlap : int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 8``. Defaults to `None`. + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + scaling : { 'density', 'spectrum' }, optional + Selects between computing the power spectral density ('density') + where `Sxx` has units of V**2/Hz and computing the power + spectrum ('spectrum') where `Sxx` has units of V**2, if `x` + is measured in V and `fs` is measured in Hz. Defaults to + 'density'. + axis : int, optional + Axis along which the spectrogram is computed; the default is over + the last axis (i.e. ``axis=-1``). + mode : str, optional + Defines what kind of return values are expected. Options are + ['psd', 'complex', 'magnitude', 'angle', 'phase']. 'complex' is + equivalent to the output of `stft` with no padding or boundary + extension. 'magnitude' returns the absolute magnitude of the + STFT. 'angle' and 'phase' return the complex angle of the STFT, + with and without unwrapping, respectively. + + Returns + ------- + f : ndarray + Array of sample frequencies. + t : ndarray + Array of segment times. + Sxx : ndarray + Spectrogram of x. By default, the last axis of Sxx corresponds + to the segment times. + + See Also + -------- + periodogram: Simple, optionally modified periodogram + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + welch: Power spectral density by Welch's method. + csd: Cross spectral density by Welch's method. + ShortTimeFFT: Newer STFT/ISTFT implementation providing more features, + which also includes a :meth:`~ShortTimeFFT.spectrogram` + method. + + Notes + ----- + An appropriate amount of overlap will depend on the choice of window + and on your requirements. In contrast to welch's method, where the + entire data stream is averaged over, one may wish to use a smaller + overlap (or perhaps none at all) when computing a spectrogram, to + maintain some statistical independence between individual segments. + It is for this reason that the default window is a Tukey window with + 1/8th of a window's length overlap at each end. + + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] Oppenheim, Alan V., Ronald W. Schafer, John R. Buck + "Discrete-Time Signal Processing", Prentice Hall, 1999. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fftshift + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate a test signal, a 2 Vrms sine wave whose frequency is slowly + modulated around 3kHz, corrupted by white noise of exponentially + decreasing magnitude sampled at 10 kHz. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 2 * np.sqrt(2) + >>> noise_power = 0.01 * fs / 2 + >>> time = np.arange(N) / float(fs) + >>> mod = 500*np.cos(2*np.pi*0.25*time) + >>> carrier = amp * np.sin(2*np.pi*3e3*time + mod) + >>> noise = rng.normal(scale=np.sqrt(noise_power), size=time.shape) + >>> noise *= np.exp(-time/5) + >>> x = carrier + noise + + Compute and plot the spectrogram. + + >>> f, t, Sxx = signal.spectrogram(x, fs) + >>> plt.pcolormesh(t, f, Sxx, shading='gouraud') + >>> plt.ylabel('Frequency [Hz]') + >>> plt.xlabel('Time [sec]') + >>> plt.show() + + Note, if using output that is not one sided, then use the following: + + >>> f, t, Sxx = signal.spectrogram(x, fs, return_onesided=False) + >>> plt.pcolormesh(t, fftshift(f), fftshift(Sxx, axes=0), shading='gouraud') + >>> plt.ylabel('Frequency [Hz]') + >>> plt.xlabel('Time [sec]') + >>> plt.show() + + """ + modelist = ['psd', 'complex', 'magnitude', 'angle', 'phase'] + if mode not in modelist: + raise ValueError(f'unknown value for mode {mode}, must be one of {modelist}') + + # need to set default for nperseg before setting default for noverlap below + window, nperseg = _triage_segments(window, nperseg, + input_length=x.shape[axis]) + + # Less overlap than welch, so samples are more statisically independent + if noverlap is None: + noverlap = nperseg // 8 + + if mode == 'psd': + freqs, time, Sxx = _spectral_helper(x, x, fs, window, nperseg, + noverlap, nfft, detrend, + return_onesided, scaling, axis, + mode='psd') + + else: + freqs, time, Sxx = _spectral_helper(x, x, fs, window, nperseg, + noverlap, nfft, detrend, + return_onesided, scaling, axis, + mode='stft') + + if mode == 'magnitude': + Sxx = np.abs(Sxx) + elif mode in ['angle', 'phase']: + Sxx = np.angle(Sxx) + if mode == 'phase': + # Sxx has one additional dimension for time strides + if axis < 0: + axis -= 1 + Sxx = np.unwrap(Sxx, axis=axis) + + # mode =='complex' is same as `stft`, doesn't need modification + + return freqs, time, Sxx + + +def check_COLA(window, nperseg, noverlap, tol=1e-10): + r"""Check whether the Constant OverLap Add (COLA) constraint is met. + + Parameters + ---------- + window : str or tuple or array_like + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. + nperseg : int + Length of each segment. + noverlap : int + Number of points to overlap between segments. + tol : float, optional + The allowed variance of a bin's weighted sum from the median bin + sum. + + Returns + ------- + verdict : bool + `True` if chosen combination satisfies COLA within `tol`, + `False` otherwise + + See Also + -------- + check_NOLA: Check whether the Nonzero Overlap Add (NOLA) constraint is met + stft: Short Time Fourier Transform + istft: Inverse Short Time Fourier Transform + + Notes + ----- + In order to enable inversion of an STFT via the inverse STFT in + `istft`, it is sufficient that the signal windowing obeys the constraint of + "Constant OverLap Add" (COLA). This ensures that every point in the input + data is equally weighted, thereby avoiding aliasing and allowing full + reconstruction. + + Some examples of windows that satisfy COLA: + - Rectangular window at overlap of 0, 1/2, 2/3, 3/4, ... + - Bartlett window at overlap of 1/2, 3/4, 5/6, ... + - Hann window at 1/2, 2/3, 3/4, ... + - Any Blackman family window at 2/3 overlap + - Any window with ``noverlap = nperseg-1`` + + A very comprehensive list of other windows may be found in [2]_, + wherein the COLA condition is satisfied when the "Amplitude + Flatness" is unity. + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Julius O. Smith III, "Spectral Audio Signal Processing", W3K + Publishing, 2011,ISBN 978-0-9745607-3-1. + .. [2] G. Heinzel, A. Ruediger and R. Schilling, "Spectrum and + spectral density estimation by the Discrete Fourier transform + (DFT), including a comprehensive list of window functions and + some new at-top windows", 2002, + http://hdl.handle.net/11858/00-001M-0000-0013-557A-5 + + Examples + -------- + >>> from scipy import signal + + Confirm COLA condition for rectangular window of 75% (3/4) overlap: + + >>> signal.check_COLA(signal.windows.boxcar(100), 100, 75) + True + + COLA is not true for 25% (1/4) overlap, though: + + >>> signal.check_COLA(signal.windows.boxcar(100), 100, 25) + False + + "Symmetrical" Hann window (for filter design) is not COLA: + + >>> signal.check_COLA(signal.windows.hann(120, sym=True), 120, 60) + False + + "Periodic" or "DFT-even" Hann window (for FFT analysis) is COLA for + overlap of 1/2, 2/3, 3/4, etc.: + + >>> signal.check_COLA(signal.windows.hann(120, sym=False), 120, 60) + True + + >>> signal.check_COLA(signal.windows.hann(120, sym=False), 120, 80) + True + + >>> signal.check_COLA(signal.windows.hann(120, sym=False), 120, 90) + True + + """ + nperseg = int(nperseg) + + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + noverlap = int(noverlap) + + if isinstance(window, str) or type(window) is tuple: + win = get_window(window, nperseg) + else: + win = np.asarray(window) + if len(win.shape) != 1: + raise ValueError('window must be 1-D') + if win.shape[0] != nperseg: + raise ValueError('window must have length of nperseg') + + step = nperseg - noverlap + binsums = sum(win[ii*step:(ii+1)*step] for ii in range(nperseg//step)) + + if nperseg % step != 0: + binsums[:nperseg % step] += win[-(nperseg % step):] + + deviation = binsums - np.median(binsums) + return np.max(np.abs(deviation)) < tol + + +def check_NOLA(window, nperseg, noverlap, tol=1e-10): + r"""Check whether the Nonzero Overlap Add (NOLA) constraint is met. + + Parameters + ---------- + window : str or tuple or array_like + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. + nperseg : int + Length of each segment. + noverlap : int + Number of points to overlap between segments. + tol : float, optional + The allowed variance of a bin's weighted sum from the median bin + sum. + + Returns + ------- + verdict : bool + `True` if chosen combination satisfies the NOLA constraint within + `tol`, `False` otherwise + + See Also + -------- + check_COLA: Check whether the Constant OverLap Add (COLA) constraint is met + stft: Short Time Fourier Transform + istft: Inverse Short Time Fourier Transform + + Notes + ----- + In order to enable inversion of an STFT via the inverse STFT in + `istft`, the signal windowing must obey the constraint of "nonzero + overlap add" (NOLA): + + .. math:: \sum_{t}w^{2}[n-tH] \ne 0 + + for all :math:`n`, where :math:`w` is the window function, :math:`t` is the + frame index, and :math:`H` is the hop size (:math:`H` = `nperseg` - + `noverlap`). + + This ensures that the normalization factors in the denominator of the + overlap-add inversion equation are not zero. Only very pathological windows + will fail the NOLA constraint. + + .. versionadded:: 1.2.0 + + References + ---------- + .. [1] Julius O. Smith III, "Spectral Audio Signal Processing", W3K + Publishing, 2011,ISBN 978-0-9745607-3-1. + .. [2] G. Heinzel, A. Ruediger and R. Schilling, "Spectrum and + spectral density estimation by the Discrete Fourier transform + (DFT), including a comprehensive list of window functions and + some new at-top windows", 2002, + http://hdl.handle.net/11858/00-001M-0000-0013-557A-5 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + + Confirm NOLA condition for rectangular window of 75% (3/4) overlap: + + >>> signal.check_NOLA(signal.windows.boxcar(100), 100, 75) + True + + NOLA is also true for 25% (1/4) overlap: + + >>> signal.check_NOLA(signal.windows.boxcar(100), 100, 25) + True + + "Symmetrical" Hann window (for filter design) is also NOLA: + + >>> signal.check_NOLA(signal.windows.hann(120, sym=True), 120, 60) + True + + As long as there is overlap, it takes quite a pathological window to fail + NOLA: + + >>> w = np.ones(64, dtype="float") + >>> w[::2] = 0 + >>> signal.check_NOLA(w, 64, 32) + False + + If there is not enough overlap, a window with zeros at the ends will not + work: + + >>> signal.check_NOLA(signal.windows.hann(64), 64, 0) + False + >>> signal.check_NOLA(signal.windows.hann(64), 64, 1) + False + >>> signal.check_NOLA(signal.windows.hann(64), 64, 2) + True + + """ + nperseg = int(nperseg) + + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg') + if noverlap < 0: + raise ValueError('noverlap must be a nonnegative integer') + noverlap = int(noverlap) + + if isinstance(window, str) or type(window) is tuple: + win = get_window(window, nperseg) + else: + win = np.asarray(window) + if len(win.shape) != 1: + raise ValueError('window must be 1-D') + if win.shape[0] != nperseg: + raise ValueError('window must have length of nperseg') + + step = nperseg - noverlap + binsums = sum(win[ii*step:(ii+1)*step]**2 for ii in range(nperseg//step)) + + if nperseg % step != 0: + binsums[:nperseg % step] += win[-(nperseg % step):]**2 + + return np.min(binsums) > tol + + +def stft(x, fs=1.0, window='hann', nperseg=256, noverlap=None, nfft=None, + detrend=False, return_onesided=True, boundary='zeros', padded=True, + axis=-1, scaling='spectrum'): + r"""Compute the Short Time Fourier Transform (legacy function). + + STFTs can be used as a way of quantifying the change of a + nonstationary signal's frequency and phase content over time. + + .. legacy:: function + + `ShortTimeFFT` is a newer STFT / ISTFT implementation with more + features. A :ref:`comparison ` between the + implementations can be found in the :ref:`tutorial_stft` section of the + :ref:`user_guide`. + + Parameters + ---------- + x : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. + nperseg : int, optional + Length of each segment. Defaults to 256. + noverlap : int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 2``. Defaults to `None`. When + specified, the COLA constraint must be met (see Notes below). + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to `False`. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + boundary : str or None, optional + Specifies whether the input signal is extended at both ends, and + how to generate the new values, in order to center the first + windowed segment on the first input point. This has the benefit + of enabling reconstruction of the first input point when the + employed window function starts at zero. Valid options are + ``['even', 'odd', 'constant', 'zeros', None]``. Defaults to + 'zeros', for zero padding extension. I.e. ``[1, 2, 3, 4]`` is + extended to ``[0, 1, 2, 3, 4, 0]`` for ``nperseg=3``. + padded : bool, optional + Specifies whether the input signal is zero-padded at the end to + make the signal fit exactly into an integer number of window + segments, so that all of the signal is included in the output. + Defaults to `True`. Padding occurs after boundary extension, if + `boundary` is not `None`, and `padded` is `True`, as is the + default. + axis : int, optional + Axis along which the STFT is computed; the default is over the + last axis (i.e. ``axis=-1``). + scaling: {'spectrum', 'psd'} + The default 'spectrum' scaling allows each frequency line of `Zxx` to + be interpreted as a magnitude spectrum. The 'psd' option scales each + line to a power spectral density - it allows to calculate the signal's + energy by numerically integrating over ``abs(Zxx)**2``. + + .. versionadded:: 1.9.0 + + Returns + ------- + f : ndarray + Array of sample frequencies. + t : ndarray + Array of segment times. + Zxx : ndarray + STFT of `x`. By default, the last axis of `Zxx` corresponds + to the segment times. + + See Also + -------- + istft: Inverse Short Time Fourier Transform + ShortTimeFFT: Newer STFT/ISTFT implementation providing more features. + check_COLA: Check whether the Constant OverLap Add (COLA) constraint + is met + check_NOLA: Check whether the Nonzero Overlap Add (NOLA) constraint is met + welch: Power spectral density by Welch's method. + spectrogram: Spectrogram by Welch's method. + csd: Cross spectral density by Welch's method. + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + + Notes + ----- + In order to enable inversion of an STFT via the inverse STFT in + `istft`, the signal windowing must obey the constraint of "Nonzero + OverLap Add" (NOLA), and the input signal must have complete + windowing coverage (i.e. ``(x.shape[axis] - nperseg) % + (nperseg-noverlap) == 0``). The `padded` argument may be used to + accomplish this. + + Given a time-domain signal :math:`x[n]`, a window :math:`w[n]`, and a hop + size :math:`H` = `nperseg - noverlap`, the windowed frame at time index + :math:`t` is given by + + .. math:: x_{t}[n]=x[n]w[n-tH] + + The overlap-add (OLA) reconstruction equation is given by + + .. math:: x[n]=\frac{\sum_{t}x_{t}[n]w[n-tH]}{\sum_{t}w^{2}[n-tH]} + + The NOLA constraint ensures that every normalization term that appears + in the denomimator of the OLA reconstruction equation is nonzero. Whether a + choice of `window`, `nperseg`, and `noverlap` satisfy this constraint can + be tested with `check_NOLA`. + + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Oppenheim, Alan V., Ronald W. Schafer, John R. Buck + "Discrete-Time Signal Processing", Prentice Hall, 1999. + .. [2] Daniel W. Griffin, Jae S. Lim "Signal Estimation from + Modified Short-Time Fourier Transform", IEEE 1984, + 10.1109/TASSP.1984.1164317 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate a test signal, a 2 Vrms sine wave whose frequency is slowly + modulated around 3kHz, corrupted by white noise of exponentially + decreasing magnitude sampled at 10 kHz. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 2 * np.sqrt(2) + >>> noise_power = 0.01 * fs / 2 + >>> time = np.arange(N) / float(fs) + >>> mod = 500*np.cos(2*np.pi*0.25*time) + >>> carrier = amp * np.sin(2*np.pi*3e3*time + mod) + >>> noise = rng.normal(scale=np.sqrt(noise_power), + ... size=time.shape) + >>> noise *= np.exp(-time/5) + >>> x = carrier + noise + + Compute and plot the STFT's magnitude. + + >>> f, t, Zxx = signal.stft(x, fs, nperseg=1000) + >>> plt.pcolormesh(t, f, np.abs(Zxx), vmin=0, vmax=amp, shading='gouraud') + >>> plt.title('STFT Magnitude') + >>> plt.ylabel('Frequency [Hz]') + >>> plt.xlabel('Time [sec]') + >>> plt.show() + + Compare the energy of the signal `x` with the energy of its STFT: + + >>> E_x = sum(x**2) / fs # Energy of x + >>> # Calculate a two-sided STFT with PSD scaling: + >>> f, t, Zxx = signal.stft(x, fs, nperseg=1000, return_onesided=False, + ... scaling='psd') + >>> # Integrate numerically over abs(Zxx)**2: + >>> df, dt = f[1] - f[0], t[1] - t[0] + >>> E_Zxx = sum(np.sum(Zxx.real**2 + Zxx.imag**2, axis=0) * df) * dt + >>> # The energy is the same, but the numerical errors are quite large: + >>> np.isclose(E_x, E_Zxx, rtol=1e-2) + True + + """ + if scaling == 'psd': + scaling = 'density' + elif scaling != 'spectrum': + raise ValueError(f"Parameter {scaling=} not in ['spectrum', 'psd']!") + + freqs, time, Zxx = _spectral_helper(x, x, fs, window, nperseg, noverlap, + nfft, detrend, return_onesided, + scaling=scaling, axis=axis, + mode='stft', boundary=boundary, + padded=padded) + + return freqs, time, Zxx + + +def istft(Zxx, fs=1.0, window='hann', nperseg=None, noverlap=None, nfft=None, + input_onesided=True, boundary=True, time_axis=-1, freq_axis=-2, + scaling='spectrum'): + r"""Perform the inverse Short Time Fourier transform (legacy function). + + .. legacy:: function + + `ShortTimeFFT` is a newer STFT / ISTFT implementation with more + features. A :ref:`comparison ` between the + implementations can be found in the :ref:`tutorial_stft` section of the + :ref:`user_guide`. + + Parameters + ---------- + Zxx : array_like + STFT of the signal to be reconstructed. If a purely real array + is passed, it will be cast to a complex data type. + fs : float, optional + Sampling frequency of the time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. Must match the window used to generate the + STFT for faithful inversion. + nperseg : int, optional + Number of data points corresponding to each STFT segment. This + parameter must be specified if the number of data points per + segment is odd, or if the STFT was padded via ``nfft > + nperseg``. If `None`, the value depends on the shape of + `Zxx` and `input_onesided`. If `input_onesided` is `True`, + ``nperseg=2*(Zxx.shape[freq_axis] - 1)``. Otherwise, + ``nperseg=Zxx.shape[freq_axis]``. Defaults to `None`. + noverlap : int, optional + Number of points to overlap between segments. If `None`, half + of the segment length. Defaults to `None`. When specified, the + COLA constraint must be met (see Notes below), and should match + the parameter used to generate the STFT. Defaults to `None`. + nfft : int, optional + Number of FFT points corresponding to each STFT segment. This + parameter must be specified if the STFT was padded via ``nfft > + nperseg``. If `None`, the default values are the same as for + `nperseg`, detailed above, with one exception: if + `input_onesided` is True and + ``nperseg==2*Zxx.shape[freq_axis] - 1``, `nfft` also takes on + that value. This case allows the proper inversion of an + odd-length unpadded STFT using ``nfft=None``. Defaults to + `None`. + input_onesided : bool, optional + If `True`, interpret the input array as one-sided FFTs, such + as is returned by `stft` with ``return_onesided=True`` and + `numpy.fft.rfft`. If `False`, interpret the input as a a + two-sided FFT. Defaults to `True`. + boundary : bool, optional + Specifies whether the input signal was extended at its + boundaries by supplying a non-`None` ``boundary`` argument to + `stft`. Defaults to `True`. + time_axis : int, optional + Where the time segments of the STFT is located; the default is + the last axis (i.e. ``axis=-1``). + freq_axis : int, optional + Where the frequency axis of the STFT is located; the default is + the penultimate axis (i.e. ``axis=-2``). + scaling: {'spectrum', 'psd'} + The default 'spectrum' scaling allows each frequency line of `Zxx` to + be interpreted as a magnitude spectrum. The 'psd' option scales each + line to a power spectral density - it allows to calculate the signal's + energy by numerically integrating over ``abs(Zxx)**2``. + + Returns + ------- + t : ndarray + Array of output data times. + x : ndarray + iSTFT of `Zxx`. + + See Also + -------- + stft: Short Time Fourier Transform + ShortTimeFFT: Newer STFT/ISTFT implementation providing more features. + check_COLA: Check whether the Constant OverLap Add (COLA) constraint + is met + check_NOLA: Check whether the Nonzero Overlap Add (NOLA) constraint is met + + Notes + ----- + In order to enable inversion of an STFT via the inverse STFT with + `istft`, the signal windowing must obey the constraint of "nonzero + overlap add" (NOLA): + + .. math:: \sum_{t}w^{2}[n-tH] \ne 0 + + This ensures that the normalization factors that appear in the denominator + of the overlap-add reconstruction equation + + .. math:: x[n]=\frac{\sum_{t}x_{t}[n]w[n-tH]}{\sum_{t}w^{2}[n-tH]} + + are not zero. The NOLA constraint can be checked with the `check_NOLA` + function. + + An STFT which has been modified (via masking or otherwise) is not + guaranteed to correspond to a exactly realizible signal. This + function implements the iSTFT via the least-squares estimation + algorithm detailed in [2]_, which produces a signal that minimizes + the mean squared error between the STFT of the returned signal and + the modified STFT. + + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Oppenheim, Alan V., Ronald W. Schafer, John R. Buck + "Discrete-Time Signal Processing", Prentice Hall, 1999. + .. [2] Daniel W. Griffin, Jae S. Lim "Signal Estimation from + Modified Short-Time Fourier Transform", IEEE 1984, + 10.1109/TASSP.1984.1164317 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate a test signal, a 2 Vrms sine wave at 50Hz corrupted by + 0.001 V**2/Hz of white noise sampled at 1024 Hz. + + >>> fs = 1024 + >>> N = 10*fs + >>> nperseg = 512 + >>> amp = 2 * np.sqrt(2) + >>> noise_power = 0.001 * fs / 2 + >>> time = np.arange(N) / float(fs) + >>> carrier = amp * np.sin(2*np.pi*50*time) + >>> noise = rng.normal(scale=np.sqrt(noise_power), + ... size=time.shape) + >>> x = carrier + noise + + Compute the STFT, and plot its magnitude + + >>> f, t, Zxx = signal.stft(x, fs=fs, nperseg=nperseg) + >>> plt.figure() + >>> plt.pcolormesh(t, f, np.abs(Zxx), vmin=0, vmax=amp, shading='gouraud') + >>> plt.ylim([f[1], f[-1]]) + >>> plt.title('STFT Magnitude') + >>> plt.ylabel('Frequency [Hz]') + >>> plt.xlabel('Time [sec]') + >>> plt.yscale('log') + >>> plt.show() + + Zero the components that are 10% or less of the carrier magnitude, + then convert back to a time series via inverse STFT + + >>> Zxx = np.where(np.abs(Zxx) >= amp/10, Zxx, 0) + >>> _, xrec = signal.istft(Zxx, fs) + + Compare the cleaned signal with the original and true carrier signals. + + >>> plt.figure() + >>> plt.plot(time, x, time, xrec, time, carrier) + >>> plt.xlim([2, 2.1]) + >>> plt.xlabel('Time [sec]') + >>> plt.ylabel('Signal') + >>> plt.legend(['Carrier + Noise', 'Filtered via STFT', 'True Carrier']) + >>> plt.show() + + Note that the cleaned signal does not start as abruptly as the original, + since some of the coefficients of the transient were also removed: + + >>> plt.figure() + >>> plt.plot(time, x, time, xrec, time, carrier) + >>> plt.xlim([0, 0.1]) + >>> plt.xlabel('Time [sec]') + >>> plt.ylabel('Signal') + >>> plt.legend(['Carrier + Noise', 'Filtered via STFT', 'True Carrier']) + >>> plt.show() + + """ + # Make sure input is an ndarray of appropriate complex dtype + Zxx = np.asarray(Zxx) + 0j + freq_axis = int(freq_axis) + time_axis = int(time_axis) + + if Zxx.ndim < 2: + raise ValueError('Input stft must be at least 2d!') + + if freq_axis == time_axis: + raise ValueError('Must specify differing time and frequency axes!') + + nseg = Zxx.shape[time_axis] + + if input_onesided: + # Assume even segment length + n_default = 2*(Zxx.shape[freq_axis] - 1) + else: + n_default = Zxx.shape[freq_axis] + + # Check windowing parameters + if nperseg is None: + nperseg = n_default + else: + nperseg = int(nperseg) + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + if nfft is None: + if (input_onesided) and (nperseg == n_default + 1): + # Odd nperseg, no FFT padding + nfft = nperseg + else: + nfft = n_default + elif nfft < nperseg: + raise ValueError('nfft must be greater than or equal to nperseg.') + else: + nfft = int(nfft) + + if noverlap is None: + noverlap = nperseg//2 + else: + noverlap = int(noverlap) + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + nstep = nperseg - noverlap + + # Rearrange axes if necessary + if time_axis != Zxx.ndim-1 or freq_axis != Zxx.ndim-2: + # Turn negative indices to positive for the call to transpose + if freq_axis < 0: + freq_axis = Zxx.ndim + freq_axis + if time_axis < 0: + time_axis = Zxx.ndim + time_axis + zouter = list(range(Zxx.ndim)) + for ax in sorted([time_axis, freq_axis], reverse=True): + zouter.pop(ax) + Zxx = np.transpose(Zxx, zouter+[freq_axis, time_axis]) + + # Get window as array + if isinstance(window, str) or type(window) is tuple: + win = get_window(window, nperseg) + else: + win = np.asarray(window) + if len(win.shape) != 1: + raise ValueError('window must be 1-D') + if win.shape[0] != nperseg: + raise ValueError(f'window must have length of {nperseg}') + + ifunc = sp_fft.irfft if input_onesided else sp_fft.ifft + xsubs = ifunc(Zxx, axis=-2, n=nfft)[..., :nperseg, :] + + # Initialize output and normalization arrays + outputlength = nperseg + (nseg-1)*nstep + x = np.zeros(list(Zxx.shape[:-2])+[outputlength], dtype=xsubs.dtype) + norm = np.zeros(outputlength, dtype=xsubs.dtype) + + if np.result_type(win, xsubs) != xsubs.dtype: + win = win.astype(xsubs.dtype) + + if scaling == 'spectrum': + xsubs *= win.sum() + elif scaling == 'psd': + xsubs *= np.sqrt(fs * sum(win**2)) + else: + raise ValueError(f"Parameter {scaling=} not in ['spectrum', 'psd']!") + + # Construct the output from the ifft segments + # This loop could perhaps be vectorized/strided somehow... + for ii in range(nseg): + # Window the ifft + x[..., ii*nstep:ii*nstep+nperseg] += xsubs[..., ii] * win + norm[..., ii*nstep:ii*nstep+nperseg] += win**2 + + # Remove extension points + if boundary: + x = x[..., nperseg//2:-(nperseg//2)] + norm = norm[..., nperseg//2:-(nperseg//2)] + + # Divide out normalization where non-tiny + if np.sum(norm > 1e-10) != len(norm): + warnings.warn( + "NOLA condition failed, STFT may not be invertible." + + (" Possibly due to missing boundary" if not boundary else ""), + stacklevel=2 + ) + x /= np.where(norm > 1e-10, norm, 1.0) + + if input_onesided: + x = x.real + + # Put axes back + if x.ndim > 1: + if time_axis != Zxx.ndim-1: + if freq_axis < time_axis: + time_axis -= 1 + x = np.moveaxis(x, -1, time_axis) + + time = np.arange(x.shape[0])/float(fs) + return time, x + + +def coherence(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', axis=-1): + r""" + Estimate the magnitude squared coherence estimate, Cxy, of + discrete-time signals X and Y using Welch's method. + + ``Cxy = abs(Pxy)**2/(Pxx*Pyy)``, where `Pxx` and `Pyy` are power + spectral density estimates of X and Y, and `Pxy` is the cross + spectral density estimate of X and Y. + + Parameters + ---------- + x : array_like + Time series of measurement values + y : array_like + Time series of measurement values + fs : float, optional + Sampling frequency of the `x` and `y` time series. Defaults + to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. + nperseg : int, optional + Length of each segment. Defaults to None, but if window is str or + tuple, is set to 256, and if window is array_like, is set to the + length of the window. + noverlap: int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 2``. Defaults to `None`. + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + axis : int, optional + Axis along which the coherence is computed for both inputs; the + default is over the last axis (i.e. ``axis=-1``). + + Returns + ------- + f : ndarray + Array of sample frequencies. + Cxy : ndarray + Magnitude squared coherence of x and y. + + See Also + -------- + periodogram: Simple, optionally modified periodogram + lombscargle: Lomb-Scargle periodogram for unevenly sampled data + welch: Power spectral density by Welch's method. + csd: Cross spectral density by Welch's method. + + Notes + ----- + An appropriate amount of overlap will depend on the choice of window + and on your requirements. For the default Hann window an overlap of + 50% is a reasonable trade off between accurately estimating the + signal power, while not over counting any of the data. Narrower + windows may require a larger overlap. + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] P. Welch, "The use of the fast Fourier transform for the + estimation of power spectra: A method based on time averaging + over short, modified periodograms", IEEE Trans. Audio + Electroacoust. vol. 15, pp. 70-73, 1967. + .. [2] Stoica, Petre, and Randolph Moses, "Spectral Analysis of + Signals" Prentice Hall, 2005 + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + + Generate two test signals with some common features. + + >>> fs = 10e3 + >>> N = 1e5 + >>> amp = 20 + >>> freq = 1234.0 + >>> noise_power = 0.001 * fs / 2 + >>> time = np.arange(N) / fs + >>> b, a = signal.butter(2, 0.25, 'low') + >>> x = rng.normal(scale=np.sqrt(noise_power), size=time.shape) + >>> y = signal.lfilter(b, a, x) + >>> x += amp*np.sin(2*np.pi*freq*time) + >>> y += rng.normal(scale=0.1*np.sqrt(noise_power), size=time.shape) + + Compute and plot the coherence. + + >>> f, Cxy = signal.coherence(x, y, fs, nperseg=1024) + >>> plt.semilogy(f, Cxy) + >>> plt.xlabel('frequency [Hz]') + >>> plt.ylabel('Coherence') + >>> plt.show() + + """ + freqs, Pxx = welch(x, fs=fs, window=window, nperseg=nperseg, + noverlap=noverlap, nfft=nfft, detrend=detrend, + axis=axis) + _, Pyy = welch(y, fs=fs, window=window, nperseg=nperseg, noverlap=noverlap, + nfft=nfft, detrend=detrend, axis=axis) + _, Pxy = csd(x, y, fs=fs, window=window, nperseg=nperseg, + noverlap=noverlap, nfft=nfft, detrend=detrend, axis=axis) + + Cxy = np.abs(Pxy)**2 / Pxx / Pyy + + return freqs, Cxy + + +def _spectral_helper(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1, mode='psd', boundary=None, + padded=False): + """Calculate various forms of windowed FFTs for PSD, CSD, etc. + + This is a helper function that implements the commonality between + the stft, psd, csd, and spectrogram functions. It is not designed to + be called externally. The windows are not averaged over; the result + from each window is returned. + + Parameters + ---------- + x : array_like + Array or sequence containing the data to be analyzed. + y : array_like + Array or sequence containing the data to be analyzed. If this is + the same object in memory as `x` (i.e. ``_spectral_helper(x, + x, ...)``), the extra computations are spared. + fs : float, optional + Sampling frequency of the time series. Defaults to 1.0. + window : str or tuple or array_like, optional + Desired window to use. If `window` is a string or tuple, it is + passed to `get_window` to generate the window values, which are + DFT-even by default. See `get_window` for a list of windows and + required parameters. If `window` is array_like it will be used + directly as the window and its length must be nperseg. Defaults + to a Hann window. + nperseg : int, optional + Length of each segment. Defaults to None, but if window is str or + tuple, is set to 256, and if window is array_like, is set to the + length of the window. + noverlap : int, optional + Number of points to overlap between segments. If `None`, + ``noverlap = nperseg // 2``. Defaults to `None`. + nfft : int, optional + Length of the FFT used, if a zero padded FFT is desired. If + `None`, the FFT length is `nperseg`. Defaults to `None`. + detrend : str or function or `False`, optional + Specifies how to detrend each segment. If `detrend` is a + string, it is passed as the `type` argument to the `detrend` + function. If it is a function, it takes a segment and returns a + detrended segment. If `detrend` is `False`, no detrending is + done. Defaults to 'constant'. + return_onesided : bool, optional + If `True`, return a one-sided spectrum for real data. If + `False` return a two-sided spectrum. Defaults to `True`, but for + complex data, a two-sided spectrum is always returned. + scaling : { 'density', 'spectrum' }, optional + Selects between computing the cross spectral density ('density') + where `Pxy` has units of V**2/Hz and computing the cross + spectrum ('spectrum') where `Pxy` has units of V**2, if `x` + and `y` are measured in V and `fs` is measured in Hz. + Defaults to 'density' + axis : int, optional + Axis along which the FFTs are computed; the default is over the + last axis (i.e. ``axis=-1``). + mode: str {'psd', 'stft'}, optional + Defines what kind of return values are expected. Defaults to + 'psd'. + boundary : str or None, optional + Specifies whether the input signal is extended at both ends, and + how to generate the new values, in order to center the first + windowed segment on the first input point. This has the benefit + of enabling reconstruction of the first input point when the + employed window function starts at zero. Valid options are + ``['even', 'odd', 'constant', 'zeros', None]``. Defaults to + `None`. + padded : bool, optional + Specifies whether the input signal is zero-padded at the end to + make the signal fit exactly into an integer number of window + segments, so that all of the signal is included in the output. + Defaults to `False`. Padding occurs after boundary extension, if + `boundary` is not `None`, and `padded` is `True`. + + Returns + ------- + freqs : ndarray + Array of sample frequencies. + t : ndarray + Array of times corresponding to each data segment + result : ndarray + Array of output data, contents dependent on *mode* kwarg. + + Notes + ----- + Adapted from matplotlib.mlab + + .. versionadded:: 0.16.0 + """ + if mode not in ['psd', 'stft']: + raise ValueError("Unknown value for mode %s, must be one of: " + "{'psd', 'stft'}" % mode) + + boundary_funcs = {'even': even_ext, + 'odd': odd_ext, + 'constant': const_ext, + 'zeros': zero_ext, + None: None} + + if boundary not in boundary_funcs: + raise ValueError("Unknown boundary option '{}', must be one of: {}" + .format(boundary, list(boundary_funcs.keys()))) + + # If x and y are the same object we can save ourselves some computation. + same_data = y is x + + if not same_data and mode != 'psd': + raise ValueError("x and y must be equal if mode is 'stft'") + + axis = int(axis) + + # Ensure we have np.arrays, get outdtype + x = np.asarray(x) + if not same_data: + y = np.asarray(y) + outdtype = np.result_type(x, y, np.complex64) + else: + outdtype = np.result_type(x, np.complex64) + + if not same_data: + # Check if we can broadcast the outer axes together + xouter = list(x.shape) + youter = list(y.shape) + xouter.pop(axis) + youter.pop(axis) + try: + outershape = np.broadcast(np.empty(xouter), np.empty(youter)).shape + except ValueError as e: + raise ValueError('x and y cannot be broadcast together.') from e + + if same_data: + if x.size == 0: + return np.empty(x.shape), np.empty(x.shape), np.empty(x.shape) + else: + if x.size == 0 or y.size == 0: + outshape = outershape + (min([x.shape[axis], y.shape[axis]]),) + emptyout = np.moveaxis(np.empty(outshape), -1, axis) + return emptyout, emptyout, emptyout + + if x.ndim > 1: + if axis != -1: + x = np.moveaxis(x, axis, -1) + if not same_data and y.ndim > 1: + y = np.moveaxis(y, axis, -1) + + # Check if x and y are the same length, zero-pad if necessary + if not same_data: + if x.shape[-1] != y.shape[-1]: + if x.shape[-1] < y.shape[-1]: + pad_shape = list(x.shape) + pad_shape[-1] = y.shape[-1] - x.shape[-1] + x = np.concatenate((x, np.zeros(pad_shape)), -1) + else: + pad_shape = list(y.shape) + pad_shape[-1] = x.shape[-1] - y.shape[-1] + y = np.concatenate((y, np.zeros(pad_shape)), -1) + + if nperseg is not None: # if specified by user + nperseg = int(nperseg) + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + # parse window; if array like, then set nperseg = win.shape + win, nperseg = _triage_segments(window, nperseg, input_length=x.shape[-1]) + + if nfft is None: + nfft = nperseg + elif nfft < nperseg: + raise ValueError('nfft must be greater than or equal to nperseg.') + else: + nfft = int(nfft) + + if noverlap is None: + noverlap = nperseg//2 + else: + noverlap = int(noverlap) + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + nstep = nperseg - noverlap + + # Padding occurs after boundary extension, so that the extended signal ends + # in zeros, instead of introducing an impulse at the end. + # I.e. if x = [..., 3, 2] + # extend then pad -> [..., 3, 2, 2, 3, 0, 0, 0] + # pad then extend -> [..., 3, 2, 0, 0, 0, 2, 3] + + if boundary is not None: + ext_func = boundary_funcs[boundary] + x = ext_func(x, nperseg//2, axis=-1) + if not same_data: + y = ext_func(y, nperseg//2, axis=-1) + + if padded: + # Pad to integer number of windowed segments + # I.e make x.shape[-1] = nperseg + (nseg-1)*nstep, with integer nseg + nadd = (-(x.shape[-1]-nperseg) % nstep) % nperseg + zeros_shape = list(x.shape[:-1]) + [nadd] + x = np.concatenate((x, np.zeros(zeros_shape)), axis=-1) + if not same_data: + zeros_shape = list(y.shape[:-1]) + [nadd] + y = np.concatenate((y, np.zeros(zeros_shape)), axis=-1) + + # Handle detrending and window functions + if not detrend: + def detrend_func(d): + return d + elif not hasattr(detrend, '__call__'): + def detrend_func(d): + return _signaltools.detrend(d, type=detrend, axis=-1) + elif axis != -1: + # Wrap this function so that it receives a shape that it could + # reasonably expect to receive. + def detrend_func(d): + d = np.moveaxis(d, -1, axis) + d = detrend(d) + return np.moveaxis(d, axis, -1) + else: + detrend_func = detrend + + if np.result_type(win, np.complex64) != outdtype: + win = win.astype(outdtype) + + if scaling == 'density': + scale = 1.0 / (fs * (win*win).sum()) + elif scaling == 'spectrum': + scale = 1.0 / win.sum()**2 + else: + raise ValueError('Unknown scaling: %r' % scaling) + + if mode == 'stft': + scale = np.sqrt(scale) + + if return_onesided: + if np.iscomplexobj(x): + sides = 'twosided' + warnings.warn('Input data is complex, switching to return_onesided=False', + stacklevel=3) + else: + sides = 'onesided' + if not same_data: + if np.iscomplexobj(y): + sides = 'twosided' + warnings.warn('Input data is complex, switching to ' + 'return_onesided=False', + stacklevel=3) + else: + sides = 'twosided' + + if sides == 'twosided': + freqs = sp_fft.fftfreq(nfft, 1/fs) + elif sides == 'onesided': + freqs = sp_fft.rfftfreq(nfft, 1/fs) + + # Perform the windowed FFTs + result = _fft_helper(x, win, detrend_func, nperseg, noverlap, nfft, sides) + + if not same_data: + # All the same operations on the y data + result_y = _fft_helper(y, win, detrend_func, nperseg, noverlap, nfft, + sides) + result = np.conjugate(result) * result_y + elif mode == 'psd': + result = np.conjugate(result) * result + + result *= scale + if sides == 'onesided' and mode == 'psd': + if nfft % 2: + result[..., 1:] *= 2 + else: + # Last point is unpaired Nyquist freq point, don't double + result[..., 1:-1] *= 2 + + time = np.arange(nperseg/2, x.shape[-1] - nperseg/2 + 1, + nperseg - noverlap)/float(fs) + if boundary is not None: + time -= (nperseg/2) / fs + + result = result.astype(outdtype) + + # All imaginary parts are zero anyways + if same_data and mode != 'stft': + result = result.real + + # Output is going to have new last axis for time/window index, so a + # negative axis index shifts down one + if axis < 0: + axis -= 1 + + # Roll frequency axis back to axis where the data came from + result = np.moveaxis(result, -1, axis) + + return freqs, time, result + + +def _fft_helper(x, win, detrend_func, nperseg, noverlap, nfft, sides): + """ + Calculate windowed FFT, for internal use by + `scipy.signal._spectral_helper`. + + This is a helper function that does the main FFT calculation for + `_spectral helper`. All input validation is performed there, and the + data axis is assumed to be the last axis of x. It is not designed to + be called externally. The windows are not averaged over; the result + from each window is returned. + + Returns + ------- + result : ndarray + Array of FFT data + + Notes + ----- + Adapted from matplotlib.mlab + + .. versionadded:: 0.16.0 + """ + # Created sliding window view of array + if nperseg == 1 and noverlap == 0: + result = x[..., np.newaxis] + else: + step = nperseg - noverlap + result = np.lib.stride_tricks.sliding_window_view( + x, window_shape=nperseg, axis=-1, writeable=True + ) + result = result[..., 0::step, :] + + # Detrend each data segment individually + result = detrend_func(result) + + # Apply window by multiplication + result = win * result + + # Perform the fft. Acts on last axis by default. Zero-pads automatically + if sides == 'twosided': + func = sp_fft.fft + else: + result = result.real + func = sp_fft.rfft + result = func(result, n=nfft) + + return result + + +def _triage_segments(window, nperseg, input_length): + """ + Parses window and nperseg arguments for spectrogram and _spectral_helper. + This is a helper function, not meant to be called externally. + + Parameters + ---------- + window : string, tuple, or ndarray + If window is specified by a string or tuple and nperseg is not + specified, nperseg is set to the default of 256 and returns a window of + that length. + If instead the window is array_like and nperseg is not specified, then + nperseg is set to the length of the window. A ValueError is raised if + the user supplies both an array_like window and a value for nperseg but + nperseg does not equal the length of the window. + + nperseg : int + Length of each segment + + input_length: int + Length of input signal, i.e. x.shape[-1]. Used to test for errors. + + Returns + ------- + win : ndarray + window. If function was called with string or tuple than this will hold + the actual array used as a window. + + nperseg : int + Length of each segment. If window is str or tuple, nperseg is set to + 256. If window is array_like, nperseg is set to the length of the + window. + """ + # parse window; if array like, then set nperseg = win.shape + if isinstance(window, str) or isinstance(window, tuple): + # if nperseg not specified + if nperseg is None: + nperseg = 256 # then change to default + if nperseg > input_length: + warnings.warn(f'nperseg = {nperseg:d} is greater than input length ' + f' = {input_length:d}, using nperseg = {input_length:d}', + stacklevel=3) + nperseg = input_length + win = get_window(window, nperseg) + else: + win = np.asarray(window) + if len(win.shape) != 1: + raise ValueError('window must be 1-D') + if input_length < win.shape[-1]: + raise ValueError('window is longer than input signal') + if nperseg is None: + nperseg = win.shape[0] + elif nperseg is not None: + if nperseg != win.shape[0]: + raise ValueError("value specified for nperseg is different" + " from length of window") + return win, nperseg + + +def _median_bias(n): + """ + Returns the bias of the median of a set of periodograms relative to + the mean. + + See Appendix B from [1]_ for details. + + Parameters + ---------- + n : int + Numbers of periodograms being averaged. + + Returns + ------- + bias : float + Calculated bias. + + References + ---------- + .. [1] B. Allen, W.G. Anderson, P.R. Brady, D.A. Brown, J.D.E. Creighton. + "FINDCHIRP: an algorithm for detection of gravitational waves from + inspiraling compact binaries", Physical Review D 85, 2012, + :arxiv:`gr-qc/0509116` + """ + ii_2 = 2 * np.arange(1., (n-1) // 2 + 1) + return 1 + np.sum(1. / (ii_2 + 1) - 1. / ii_2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spline.cpython-39-darwin.so b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spline.cpython-39-darwin.so new file mode 100644 index 0000000000000000000000000000000000000000..738a059cd1d2e9c9b3f3f9381993ec6b3d609407 Binary files /dev/null and b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_spline.cpython-39-darwin.so differ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_upfirdn.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_upfirdn.py new file mode 100644 index 0000000000000000000000000000000000000000..d64cc142ff194b1404e380507289ddbaffab3359 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_upfirdn.py @@ -0,0 +1,216 @@ +# Code adapted from "upfirdn" python library with permission: +# +# Copyright (c) 2009, Motorola, Inc +# +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Motorola nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import numpy as np + +from ._upfirdn_apply import _output_len, _apply, mode_enum + +__all__ = ['upfirdn', '_output_len'] + +_upfirdn_modes = [ + 'constant', 'wrap', 'edge', 'smooth', 'symmetric', 'reflect', + 'antisymmetric', 'antireflect', 'line', +] + + +def _pad_h(h, up): + """Store coefficients in a transposed, flipped arrangement. + + For example, suppose upRate is 3, and the + input number of coefficients is 10, represented as h[0], ..., h[9]. + + Then the internal buffer will look like this:: + + h[9], h[6], h[3], h[0], // flipped phase 0 coefs + 0, h[7], h[4], h[1], // flipped phase 1 coefs (zero-padded) + 0, h[8], h[5], h[2], // flipped phase 2 coefs (zero-padded) + + """ + h_padlen = len(h) + (-len(h) % up) + h_full = np.zeros(h_padlen, h.dtype) + h_full[:len(h)] = h + h_full = h_full.reshape(-1, up).T[:, ::-1].ravel() + return h_full + + +def _check_mode(mode): + mode = mode.lower() + enum = mode_enum(mode) + return enum + + +class _UpFIRDn: + """Helper for resampling.""" + + def __init__(self, h, x_dtype, up, down): + h = np.asarray(h) + if h.ndim != 1 or h.size == 0: + raise ValueError('h must be 1-D with non-zero length') + self._output_type = np.result_type(h.dtype, x_dtype, np.float32) + h = np.asarray(h, self._output_type) + self._up = int(up) + self._down = int(down) + if self._up < 1 or self._down < 1: + raise ValueError('Both up and down must be >= 1') + # This both transposes, and "flips" each phase for filtering + self._h_trans_flip = _pad_h(h, self._up) + self._h_trans_flip = np.ascontiguousarray(self._h_trans_flip) + self._h_len_orig = len(h) + + def apply_filter(self, x, axis=-1, mode='constant', cval=0): + """Apply the prepared filter to the specified axis of N-D signal x.""" + output_len = _output_len(self._h_len_orig, x.shape[axis], + self._up, self._down) + # Explicit use of np.int64 for output_shape dtype avoids OverflowError + # when allocating large array on platforms where intp is 32 bits. + output_shape = np.asarray(x.shape, dtype=np.int64) + output_shape[axis] = output_len + out = np.zeros(output_shape, dtype=self._output_type, order='C') + axis = axis % x.ndim + mode = _check_mode(mode) + _apply(np.asarray(x, self._output_type), + self._h_trans_flip, out, + self._up, self._down, axis, mode, cval) + return out + + +def upfirdn(h, x, up=1, down=1, axis=-1, mode='constant', cval=0): + """Upsample, FIR filter, and downsample. + + Parameters + ---------- + h : array_like + 1-D FIR (finite-impulse response) filter coefficients. + x : array_like + Input signal array. + up : int, optional + Upsampling rate. Default is 1. + down : int, optional + Downsampling rate. Default is 1. + axis : int, optional + The axis of the input data array along which to apply the + linear filter. The filter is applied to each subarray along + this axis. Default is -1. + mode : str, optional + The signal extension mode to use. The set + ``{"constant", "symmetric", "reflect", "edge", "wrap"}`` correspond to + modes provided by `numpy.pad`. ``"smooth"`` implements a smooth + extension by extending based on the slope of the last 2 points at each + end of the array. ``"antireflect"`` and ``"antisymmetric"`` are + anti-symmetric versions of ``"reflect"`` and ``"symmetric"``. The mode + `"line"` extends the signal based on a linear trend defined by the + first and last points along the ``axis``. + + .. versionadded:: 1.4.0 + cval : float, optional + The constant value to use when ``mode == "constant"``. + + .. versionadded:: 1.4.0 + + Returns + ------- + y : ndarray + The output signal array. Dimensions will be the same as `x` except + for along `axis`, which will change size according to the `h`, + `up`, and `down` parameters. + + Notes + ----- + The algorithm is an implementation of the block diagram shown on page 129 + of the Vaidyanathan text [1]_ (Figure 4.3-8d). + + The direct approach of upsampling by factor of P with zero insertion, + FIR filtering of length ``N``, and downsampling by factor of Q is + O(N*Q) per output sample. The polyphase implementation used here is + O(N/P). + + .. versionadded:: 0.18 + + References + ---------- + .. [1] P. P. Vaidyanathan, Multirate Systems and Filter Banks, + Prentice Hall, 1993. + + Examples + -------- + Simple operations: + + >>> import numpy as np + >>> from scipy.signal import upfirdn + >>> upfirdn([1, 1, 1], [1, 1, 1]) # FIR filter + array([ 1., 2., 3., 2., 1.]) + >>> upfirdn([1], [1, 2, 3], 3) # upsampling with zeros insertion + array([ 1., 0., 0., 2., 0., 0., 3.]) + >>> upfirdn([1, 1, 1], [1, 2, 3], 3) # upsampling with sample-and-hold + array([ 1., 1., 1., 2., 2., 2., 3., 3., 3.]) + >>> upfirdn([.5, 1, .5], [1, 1, 1], 2) # linear interpolation + array([ 0.5, 1. , 1. , 1. , 1. , 1. , 0.5]) + >>> upfirdn([1], np.arange(10), 1, 3) # decimation by 3 + array([ 0., 3., 6., 9.]) + >>> upfirdn([.5, 1, .5], np.arange(10), 2, 3) # linear interp, rate 2/3 + array([ 0. , 1. , 2.5, 4. , 5.5, 7. , 8.5]) + + Apply a single filter to multiple signals: + + >>> x = np.reshape(np.arange(8), (4, 2)) + >>> x + array([[0, 1], + [2, 3], + [4, 5], + [6, 7]]) + + Apply along the last dimension of ``x``: + + >>> h = [1, 1] + >>> upfirdn(h, x, 2) + array([[ 0., 0., 1., 1.], + [ 2., 2., 3., 3.], + [ 4., 4., 5., 5.], + [ 6., 6., 7., 7.]]) + + Apply along the 0th dimension of ``x``: + + >>> upfirdn(h, x, 2, axis=0) + array([[ 0., 1.], + [ 0., 1.], + [ 2., 3.], + [ 2., 3.], + [ 4., 5.], + [ 4., 5.], + [ 6., 7.], + [ 6., 7.]]) + """ + x = np.asarray(x) + ufd = _UpFIRDn(h, x.dtype, up, down) + # This is equivalent to (but faster than) using np.apply_along_axis + return ufd.apply_filter(x, axis, mode, cval) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_waveforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_waveforms.py new file mode 100644 index 0000000000000000000000000000000000000000..55bd045cdbf23a0686c25eaf4256897b940f6fd3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_waveforms.py @@ -0,0 +1,672 @@ +# Author: Travis Oliphant +# 2003 +# +# Feb. 2010: Updated by Warren Weckesser: +# Rewrote much of chirp() +# Added sweep_poly() +import numpy as np +from numpy import asarray, zeros, place, nan, mod, pi, extract, log, sqrt, \ + exp, cos, sin, polyval, polyint + + +__all__ = ['sawtooth', 'square', 'gausspulse', 'chirp', 'sweep_poly', + 'unit_impulse'] + + +def sawtooth(t, width=1): + """ + Return a periodic sawtooth or triangle waveform. + + The sawtooth waveform has a period ``2*pi``, rises from -1 to 1 on the + interval 0 to ``width*2*pi``, then drops from 1 to -1 on the interval + ``width*2*pi`` to ``2*pi``. `width` must be in the interval [0, 1]. + + Note that this is not band-limited. It produces an infinite number + of harmonics, which are aliased back and forth across the frequency + spectrum. + + Parameters + ---------- + t : array_like + Time. + width : array_like, optional + Width of the rising ramp as a proportion of the total cycle. + Default is 1, producing a rising ramp, while 0 produces a falling + ramp. `width` = 0.5 produces a triangle wave. + If an array, causes wave shape to change over time, and must be the + same length as t. + + Returns + ------- + y : ndarray + Output array containing the sawtooth waveform. + + Examples + -------- + A 5 Hz waveform sampled at 500 Hz for 1 second: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> t = np.linspace(0, 1, 500) + >>> plt.plot(t, signal.sawtooth(2 * np.pi * 5 * t)) + + """ + t, w = asarray(t), asarray(width) + w = asarray(w + (t - t)) + t = asarray(t + (w - w)) + if t.dtype.char in ['fFdD']: + ytype = t.dtype.char + else: + ytype = 'd' + y = zeros(t.shape, ytype) + + # width must be between 0 and 1 inclusive + mask1 = (w > 1) | (w < 0) + place(y, mask1, nan) + + # take t modulo 2*pi + tmod = mod(t, 2 * pi) + + # on the interval 0 to width*2*pi function is + # tmod / (pi*w) - 1 + mask2 = (1 - mask1) & (tmod < w * 2 * pi) + tsub = extract(mask2, tmod) + wsub = extract(mask2, w) + place(y, mask2, tsub / (pi * wsub) - 1) + + # on the interval width*2*pi to 2*pi function is + # (pi*(w+1)-tmod) / (pi*(1-w)) + + mask3 = (1 - mask1) & (1 - mask2) + tsub = extract(mask3, tmod) + wsub = extract(mask3, w) + place(y, mask3, (pi * (wsub + 1) - tsub) / (pi * (1 - wsub))) + return y + + +def square(t, duty=0.5): + """ + Return a periodic square-wave waveform. + + The square wave has a period ``2*pi``, has value +1 from 0 to + ``2*pi*duty`` and -1 from ``2*pi*duty`` to ``2*pi``. `duty` must be in + the interval [0,1]. + + Note that this is not band-limited. It produces an infinite number + of harmonics, which are aliased back and forth across the frequency + spectrum. + + Parameters + ---------- + t : array_like + The input time array. + duty : array_like, optional + Duty cycle. Default is 0.5 (50% duty cycle). + If an array, causes wave shape to change over time, and must be the + same length as t. + + Returns + ------- + y : ndarray + Output array containing the square waveform. + + Examples + -------- + A 5 Hz waveform sampled at 500 Hz for 1 second: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> t = np.linspace(0, 1, 500, endpoint=False) + >>> plt.plot(t, signal.square(2 * np.pi * 5 * t)) + >>> plt.ylim(-2, 2) + + A pulse-width modulated sine wave: + + >>> plt.figure() + >>> sig = np.sin(2 * np.pi * t) + >>> pwm = signal.square(2 * np.pi * 30 * t, duty=(sig + 1)/2) + >>> plt.subplot(2, 1, 1) + >>> plt.plot(t, sig) + >>> plt.subplot(2, 1, 2) + >>> plt.plot(t, pwm) + >>> plt.ylim(-1.5, 1.5) + + """ + t, w = asarray(t), asarray(duty) + w = asarray(w + (t - t)) + t = asarray(t + (w - w)) + if t.dtype.char in ['fFdD']: + ytype = t.dtype.char + else: + ytype = 'd' + + y = zeros(t.shape, ytype) + + # width must be between 0 and 1 inclusive + mask1 = (w > 1) | (w < 0) + place(y, mask1, nan) + + # on the interval 0 to duty*2*pi function is 1 + tmod = mod(t, 2 * pi) + mask2 = (1 - mask1) & (tmod < w * 2 * pi) + place(y, mask2, 1) + + # on the interval duty*2*pi to 2*pi function is + # (pi*(w+1)-tmod) / (pi*(1-w)) + mask3 = (1 - mask1) & (1 - mask2) + place(y, mask3, -1) + return y + + +def gausspulse(t, fc=1000, bw=0.5, bwr=-6, tpr=-60, retquad=False, + retenv=False): + """ + Return a Gaussian modulated sinusoid: + + ``exp(-a t^2) exp(1j*2*pi*fc*t).`` + + If `retquad` is True, then return the real and imaginary parts + (in-phase and quadrature). + If `retenv` is True, then return the envelope (unmodulated signal). + Otherwise, return the real part of the modulated sinusoid. + + Parameters + ---------- + t : ndarray or the string 'cutoff' + Input array. + fc : float, optional + Center frequency (e.g. Hz). Default is 1000. + bw : float, optional + Fractional bandwidth in frequency domain of pulse (e.g. Hz). + Default is 0.5. + bwr : float, optional + Reference level at which fractional bandwidth is calculated (dB). + Default is -6. + tpr : float, optional + If `t` is 'cutoff', then the function returns the cutoff + time for when the pulse amplitude falls below `tpr` (in dB). + Default is -60. + retquad : bool, optional + If True, return the quadrature (imaginary) as well as the real part + of the signal. Default is False. + retenv : bool, optional + If True, return the envelope of the signal. Default is False. + + Returns + ------- + yI : ndarray + Real part of signal. Always returned. + yQ : ndarray + Imaginary part of signal. Only returned if `retquad` is True. + yenv : ndarray + Envelope of signal. Only returned if `retenv` is True. + + See Also + -------- + scipy.signal.morlet + + Examples + -------- + Plot real component, imaginary component, and envelope for a 5 Hz pulse, + sampled at 100 Hz for 2 seconds: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> t = np.linspace(-1, 1, 2 * 100, endpoint=False) + >>> i, q, e = signal.gausspulse(t, fc=5, retquad=True, retenv=True) + >>> plt.plot(t, i, t, q, t, e, '--') + + """ + if fc < 0: + raise ValueError("Center frequency (fc=%.2f) must be >=0." % fc) + if bw <= 0: + raise ValueError("Fractional bandwidth (bw=%.2f) must be > 0." % bw) + if bwr >= 0: + raise ValueError("Reference level for bandwidth (bwr=%.2f) must " + "be < 0 dB" % bwr) + + # exp(-a t^2) <-> sqrt(pi/a) exp(-pi^2/a * f^2) = g(f) + + ref = pow(10.0, bwr / 20.0) + # fdel = fc*bw/2: g(fdel) = ref --- solve this for a + # + # pi^2/a * fc^2 * bw^2 /4=-log(ref) + a = -(pi * fc * bw) ** 2 / (4.0 * log(ref)) + + if isinstance(t, str): + if t == 'cutoff': # compute cut_off point + # Solve exp(-a tc**2) = tref for tc + # tc = sqrt(-log(tref) / a) where tref = 10^(tpr/20) + if tpr >= 0: + raise ValueError("Reference level for time cutoff must " + "be < 0 dB") + tref = pow(10.0, tpr / 20.0) + return sqrt(-log(tref) / a) + else: + raise ValueError("If `t` is a string, it must be 'cutoff'") + + yenv = exp(-a * t * t) + yI = yenv * cos(2 * pi * fc * t) + yQ = yenv * sin(2 * pi * fc * t) + if not retquad and not retenv: + return yI + if not retquad and retenv: + return yI, yenv + if retquad and not retenv: + return yI, yQ + if retquad and retenv: + return yI, yQ, yenv + + +def chirp(t, f0, t1, f1, method='linear', phi=0, vertex_zero=True): + """Frequency-swept cosine generator. + + In the following, 'Hz' should be interpreted as 'cycles per unit'; + there is no requirement here that the unit is one second. The + important distinction is that the units of rotation are cycles, not + radians. Likewise, `t` could be a measurement of space instead of time. + + Parameters + ---------- + t : array_like + Times at which to evaluate the waveform. + f0 : float + Frequency (e.g. Hz) at time t=0. + t1 : float + Time at which `f1` is specified. + f1 : float + Frequency (e.g. Hz) of the waveform at time `t1`. + method : {'linear', 'quadratic', 'logarithmic', 'hyperbolic'}, optional + Kind of frequency sweep. If not given, `linear` is assumed. See + Notes below for more details. + phi : float, optional + Phase offset, in degrees. Default is 0. + vertex_zero : bool, optional + This parameter is only used when `method` is 'quadratic'. + It determines whether the vertex of the parabola that is the graph + of the frequency is at t=0 or t=t1. + + Returns + ------- + y : ndarray + A numpy array containing the signal evaluated at `t` with the + requested time-varying frequency. More precisely, the function + returns ``cos(phase + (pi/180)*phi)`` where `phase` is the integral + (from 0 to `t`) of ``2*pi*f(t)``. ``f(t)`` is defined below. + + See Also + -------- + sweep_poly + + Notes + ----- + There are four options for the `method`. The following formulas give + the instantaneous frequency (in Hz) of the signal generated by + `chirp()`. For convenience, the shorter names shown below may also be + used. + + linear, lin, li: + + ``f(t) = f0 + (f1 - f0) * t / t1`` + + quadratic, quad, q: + + The graph of the frequency f(t) is a parabola through (0, f0) and + (t1, f1). By default, the vertex of the parabola is at (0, f0). + If `vertex_zero` is False, then the vertex is at (t1, f1). The + formula is: + + if vertex_zero is True: + + ``f(t) = f0 + (f1 - f0) * t**2 / t1**2`` + + else: + + ``f(t) = f1 - (f1 - f0) * (t1 - t)**2 / t1**2`` + + To use a more general quadratic function, or an arbitrary + polynomial, use the function `scipy.signal.sweep_poly`. + + logarithmic, log, lo: + + ``f(t) = f0 * (f1/f0)**(t/t1)`` + + f0 and f1 must be nonzero and have the same sign. + + This signal is also known as a geometric or exponential chirp. + + hyperbolic, hyp: + + ``f(t) = f0*f1*t1 / ((f0 - f1)*t + f1*t1)`` + + f0 and f1 must be nonzero. + + Examples + -------- + The following will be used in the examples: + + >>> import numpy as np + >>> from scipy.signal import chirp, spectrogram + >>> import matplotlib.pyplot as plt + + For the first example, we'll plot the waveform for a linear chirp + from 6 Hz to 1 Hz over 10 seconds: + + >>> t = np.linspace(0, 10, 1500) + >>> w = chirp(t, f0=6, f1=1, t1=10, method='linear') + >>> plt.plot(t, w) + >>> plt.title("Linear Chirp, f(0)=6, f(10)=1") + >>> plt.xlabel('t (sec)') + >>> plt.show() + + For the remaining examples, we'll use higher frequency ranges, + and demonstrate the result using `scipy.signal.spectrogram`. + We'll use a 4 second interval sampled at 7200 Hz. + + >>> fs = 7200 + >>> T = 4 + >>> t = np.arange(0, int(T*fs)) / fs + + We'll use this function to plot the spectrogram in each example. + + >>> def plot_spectrogram(title, w, fs): + ... ff, tt, Sxx = spectrogram(w, fs=fs, nperseg=256, nfft=576) + ... fig, ax = plt.subplots() + ... ax.pcolormesh(tt, ff[:145], Sxx[:145], cmap='gray_r', + ... shading='gouraud') + ... ax.set_title(title) + ... ax.set_xlabel('t (sec)') + ... ax.set_ylabel('Frequency (Hz)') + ... ax.grid(True) + ... + + Quadratic chirp from 1500 Hz to 250 Hz + (vertex of the parabolic curve of the frequency is at t=0): + + >>> w = chirp(t, f0=1500, f1=250, t1=T, method='quadratic') + >>> plot_spectrogram(f'Quadratic Chirp, f(0)=1500, f({T})=250', w, fs) + >>> plt.show() + + Quadratic chirp from 1500 Hz to 250 Hz + (vertex of the parabolic curve of the frequency is at t=T): + + >>> w = chirp(t, f0=1500, f1=250, t1=T, method='quadratic', + ... vertex_zero=False) + >>> plot_spectrogram(f'Quadratic Chirp, f(0)=1500, f({T})=250\\n' + + ... '(vertex_zero=False)', w, fs) + >>> plt.show() + + Logarithmic chirp from 1500 Hz to 250 Hz: + + >>> w = chirp(t, f0=1500, f1=250, t1=T, method='logarithmic') + >>> plot_spectrogram(f'Logarithmic Chirp, f(0)=1500, f({T})=250', w, fs) + >>> plt.show() + + Hyperbolic chirp from 1500 Hz to 250 Hz: + + >>> w = chirp(t, f0=1500, f1=250, t1=T, method='hyperbolic') + >>> plot_spectrogram(f'Hyperbolic Chirp, f(0)=1500, f({T})=250', w, fs) + >>> plt.show() + + """ + # 'phase' is computed in _chirp_phase, to make testing easier. + phase = _chirp_phase(t, f0, t1, f1, method, vertex_zero) + # Convert phi to radians. + phi *= pi / 180 + return cos(phase + phi) + + +def _chirp_phase(t, f0, t1, f1, method='linear', vertex_zero=True): + """ + Calculate the phase used by `chirp` to generate its output. + + See `chirp` for a description of the arguments. + + """ + t = asarray(t) + f0 = float(f0) + t1 = float(t1) + f1 = float(f1) + if method in ['linear', 'lin', 'li']: + beta = (f1 - f0) / t1 + phase = 2 * pi * (f0 * t + 0.5 * beta * t * t) + + elif method in ['quadratic', 'quad', 'q']: + beta = (f1 - f0) / (t1 ** 2) + if vertex_zero: + phase = 2 * pi * (f0 * t + beta * t ** 3 / 3) + else: + phase = 2 * pi * (f1 * t + beta * ((t1 - t) ** 3 - t1 ** 3) / 3) + + elif method in ['logarithmic', 'log', 'lo']: + if f0 * f1 <= 0.0: + raise ValueError("For a logarithmic chirp, f0 and f1 must be " + "nonzero and have the same sign.") + if f0 == f1: + phase = 2 * pi * f0 * t + else: + beta = t1 / log(f1 / f0) + phase = 2 * pi * beta * f0 * (pow(f1 / f0, t / t1) - 1.0) + + elif method in ['hyperbolic', 'hyp']: + if f0 == 0 or f1 == 0: + raise ValueError("For a hyperbolic chirp, f0 and f1 must be " + "nonzero.") + if f0 == f1: + # Degenerate case: constant frequency. + phase = 2 * pi * f0 * t + else: + # Singular point: the instantaneous frequency blows up + # when t == sing. + sing = -f1 * t1 / (f0 - f1) + phase = 2 * pi * (-sing * f0) * log(np.abs(1 - t/sing)) + + else: + raise ValueError("method must be 'linear', 'quadratic', 'logarithmic'," + " or 'hyperbolic', but a value of %r was given." + % method) + + return phase + + +def sweep_poly(t, poly, phi=0): + """ + Frequency-swept cosine generator, with a time-dependent frequency. + + This function generates a sinusoidal function whose instantaneous + frequency varies with time. The frequency at time `t` is given by + the polynomial `poly`. + + Parameters + ---------- + t : ndarray + Times at which to evaluate the waveform. + poly : 1-D array_like or instance of numpy.poly1d + The desired frequency expressed as a polynomial. If `poly` is + a list or ndarray of length n, then the elements of `poly` are + the coefficients of the polynomial, and the instantaneous + frequency is + + ``f(t) = poly[0]*t**(n-1) + poly[1]*t**(n-2) + ... + poly[n-1]`` + + If `poly` is an instance of numpy.poly1d, then the + instantaneous frequency is + + ``f(t) = poly(t)`` + + phi : float, optional + Phase offset, in degrees, Default: 0. + + Returns + ------- + sweep_poly : ndarray + A numpy array containing the signal evaluated at `t` with the + requested time-varying frequency. More precisely, the function + returns ``cos(phase + (pi/180)*phi)``, where `phase` is the integral + (from 0 to t) of ``2 * pi * f(t)``; ``f(t)`` is defined above. + + See Also + -------- + chirp + + Notes + ----- + .. versionadded:: 0.8.0 + + If `poly` is a list or ndarray of length `n`, then the elements of + `poly` are the coefficients of the polynomial, and the instantaneous + frequency is: + + ``f(t) = poly[0]*t**(n-1) + poly[1]*t**(n-2) + ... + poly[n-1]`` + + If `poly` is an instance of `numpy.poly1d`, then the instantaneous + frequency is: + + ``f(t) = poly(t)`` + + Finally, the output `s` is: + + ``cos(phase + (pi/180)*phi)`` + + where `phase` is the integral from 0 to `t` of ``2 * pi * f(t)``, + ``f(t)`` as defined above. + + Examples + -------- + Compute the waveform with instantaneous frequency:: + + f(t) = 0.025*t**3 - 0.36*t**2 + 1.25*t + 2 + + over the interval 0 <= t <= 10. + + >>> import numpy as np + >>> from scipy.signal import sweep_poly + >>> p = np.poly1d([0.025, -0.36, 1.25, 2.0]) + >>> t = np.linspace(0, 10, 5001) + >>> w = sweep_poly(t, p) + + Plot it: + + >>> import matplotlib.pyplot as plt + >>> plt.subplot(2, 1, 1) + >>> plt.plot(t, w) + >>> plt.title("Sweep Poly\\nwith frequency " + + ... "$f(t) = 0.025t^3 - 0.36t^2 + 1.25t + 2$") + >>> plt.subplot(2, 1, 2) + >>> plt.plot(t, p(t), 'r', label='f(t)') + >>> plt.legend() + >>> plt.xlabel('t') + >>> plt.tight_layout() + >>> plt.show() + + """ + # 'phase' is computed in _sweep_poly_phase, to make testing easier. + phase = _sweep_poly_phase(t, poly) + # Convert to radians. + phi *= pi / 180 + return cos(phase + phi) + + +def _sweep_poly_phase(t, poly): + """ + Calculate the phase used by sweep_poly to generate its output. + + See `sweep_poly` for a description of the arguments. + + """ + # polyint handles lists, ndarrays and instances of poly1d automatically. + intpoly = polyint(poly) + phase = 2 * pi * polyval(intpoly, t) + return phase + + +def unit_impulse(shape, idx=None, dtype=float): + """ + Unit impulse signal (discrete delta function) or unit basis vector. + + Parameters + ---------- + shape : int or tuple of int + Number of samples in the output (1-D), or a tuple that represents the + shape of the output (N-D). + idx : None or int or tuple of int or 'mid', optional + Index at which the value is 1. If None, defaults to the 0th element. + If ``idx='mid'``, the impulse will be centered at ``shape // 2`` in + all dimensions. If an int, the impulse will be at `idx` in all + dimensions. + dtype : data-type, optional + The desired data-type for the array, e.g., ``numpy.int8``. Default is + ``numpy.float64``. + + Returns + ------- + y : ndarray + Output array containing an impulse signal. + + Notes + ----- + The 1D case is also known as the Kronecker delta. + + .. versionadded:: 0.19.0 + + Examples + -------- + An impulse at the 0th element (:math:`\\delta[n]`): + + >>> from scipy import signal + >>> signal.unit_impulse(8) + array([ 1., 0., 0., 0., 0., 0., 0., 0.]) + + Impulse offset by 2 samples (:math:`\\delta[n-2]`): + + >>> signal.unit_impulse(7, 2) + array([ 0., 0., 1., 0., 0., 0., 0.]) + + 2-dimensional impulse, centered: + + >>> signal.unit_impulse((3, 3), 'mid') + array([[ 0., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 0.]]) + + Impulse at (2, 2), using broadcasting: + + >>> signal.unit_impulse((4, 4), 2) + array([[ 0., 0., 0., 0.], + [ 0., 0., 0., 0.], + [ 0., 0., 1., 0.], + [ 0., 0., 0., 0.]]) + + Plot the impulse response of a 4th-order Butterworth lowpass filter: + + >>> imp = signal.unit_impulse(100, 'mid') + >>> b, a = signal.butter(4, 0.2) + >>> response = signal.lfilter(b, a, imp) + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> plt.plot(np.arange(-50, 50), imp) + >>> plt.plot(np.arange(-50, 50), response) + >>> plt.margins(0.1, 0.1) + >>> plt.xlabel('Time [samples]') + >>> plt.ylabel('Amplitude') + >>> plt.grid(True) + >>> plt.show() + + """ + out = zeros(shape, dtype) + + shape = np.atleast_1d(shape) + + if idx is None: + idx = (0,) * len(shape) + elif idx == 'mid': + idx = tuple(shape // 2) + elif not hasattr(idx, "__iter__"): + idx = (idx,) * len(shape) + + out[idx] = 1 + return out diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_wavelets.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_wavelets.py new file mode 100644 index 0000000000000000000000000000000000000000..7cfc9f77d6f782c7b9af2d57576d55bf30d809f7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/_wavelets.py @@ -0,0 +1,556 @@ +import warnings + +import numpy as np +from scipy.linalg import eig +from scipy.special import comb +from scipy.signal import convolve + +__all__ = ['daub', 'qmf', 'cascade', 'morlet', 'ricker', 'morlet2', 'cwt'] + + +_msg="""scipy.signal.%s is deprecated in SciPy 1.12 and will be removed +in SciPy 1.15. We recommend using PyWavelets instead. +""" + + +def daub(p): + """ + The coefficients for the FIR low-pass filter producing Daubechies wavelets. + + .. deprecated:: 1.12.0 + + scipy.signal.daub is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + p>=1 gives the order of the zero at f=1/2. + There are 2p filter coefficients. + + Parameters + ---------- + p : int + Order of the zero at f=1/2, can have values from 1 to 34. + + Returns + ------- + daub : ndarray + Return + + """ + warnings.warn(_msg % 'daub', DeprecationWarning, stacklevel=2) + + sqrt = np.sqrt + if p < 1: + raise ValueError("p must be at least 1.") + if p == 1: + c = 1 / sqrt(2) + return np.array([c, c]) + elif p == 2: + f = sqrt(2) / 8 + c = sqrt(3) + return f * np.array([1 + c, 3 + c, 3 - c, 1 - c]) + elif p == 3: + tmp = 12 * sqrt(10) + z1 = 1.5 + sqrt(15 + tmp) / 6 - 1j * (sqrt(15) + sqrt(tmp - 15)) / 6 + z1c = np.conj(z1) + f = sqrt(2) / 8 + d0 = np.real((1 - z1) * (1 - z1c)) + a0 = np.real(z1 * z1c) + a1 = 2 * np.real(z1) + return f / d0 * np.array([a0, 3 * a0 - a1, 3 * a0 - 3 * a1 + 1, + a0 - 3 * a1 + 3, 3 - a1, 1]) + elif p < 35: + # construct polynomial and factor it + if p < 35: + P = [comb(p - 1 + k, k, exact=1) for k in range(p)][::-1] + yj = np.roots(P) + else: # try different polynomial --- needs work + P = [comb(p - 1 + k, k, exact=1) / 4.0**k + for k in range(p)][::-1] + yj = np.roots(P) / 4 + # for each root, compute two z roots, select the one with |z|>1 + # Build up final polynomial + c = np.poly1d([1, 1])**p + q = np.poly1d([1]) + for k in range(p - 1): + yval = yj[k] + part = 2 * sqrt(yval * (yval - 1)) + const = 1 - 2 * yval + z1 = const + part + if (abs(z1)) < 1: + z1 = const - part + q = q * [1, -z1] + + q = c * np.real(q) + # Normalize result + q = q / np.sum(q) * sqrt(2) + return q.c[::-1] + else: + raise ValueError("Polynomial factorization does not work " + "well for p too large.") + + +def qmf(hk): + """ + Return high-pass qmf filter from low-pass + + .. deprecated:: 1.12.0 + + scipy.signal.qmf is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + Parameters + ---------- + hk : array_like + Coefficients of high-pass filter. + + Returns + ------- + array_like + High-pass filter coefficients. + + """ + warnings.warn(_msg % 'qmf', DeprecationWarning, stacklevel=2) + + N = len(hk) - 1 + asgn = [{0: 1, 1: -1}[k % 2] for k in range(N + 1)] + return hk[::-1] * np.array(asgn) + + +def cascade(hk, J=7): + """ + Return (x, phi, psi) at dyadic points ``K/2**J`` from filter coefficients. + + .. deprecated:: 1.12.0 + + scipy.signal.cascade is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + Parameters + ---------- + hk : array_like + Coefficients of low-pass filter. + J : int, optional + Values will be computed at grid points ``K/2**J``. Default is 7. + + Returns + ------- + x : ndarray + The dyadic points ``K/2**J`` for ``K=0...N * (2**J)-1`` where + ``len(hk) = len(gk) = N+1``. + phi : ndarray + The scaling function ``phi(x)`` at `x`: + ``phi(x) = sum(hk * phi(2x-k))``, where k is from 0 to N. + psi : ndarray, optional + The wavelet function ``psi(x)`` at `x`: + ``phi(x) = sum(gk * phi(2x-k))``, where k is from 0 to N. + `psi` is only returned if `gk` is not None. + + Notes + ----- + The algorithm uses the vector cascade algorithm described by Strang and + Nguyen in "Wavelets and Filter Banks". It builds a dictionary of values + and slices for quick reuse. Then inserts vectors into final vector at the + end. + + """ + warnings.warn(_msg % 'cascade', DeprecationWarning, stacklevel=2) + + N = len(hk) - 1 + + if (J > 30 - np.log2(N + 1)): + raise ValueError("Too many levels.") + if (J < 1): + raise ValueError("Too few levels.") + + # construct matrices needed + nn, kk = np.ogrid[:N, :N] + s2 = np.sqrt(2) + # append a zero so that take works + thk = np.r_[hk, 0] + gk = qmf(hk) + tgk = np.r_[gk, 0] + + indx1 = np.clip(2 * nn - kk, -1, N + 1) + indx2 = np.clip(2 * nn - kk + 1, -1, N + 1) + m = np.empty((2, 2, N, N), 'd') + m[0, 0] = np.take(thk, indx1, 0) + m[0, 1] = np.take(thk, indx2, 0) + m[1, 0] = np.take(tgk, indx1, 0) + m[1, 1] = np.take(tgk, indx2, 0) + m *= s2 + + # construct the grid of points + x = np.arange(0, N * (1 << J), dtype=float) / (1 << J) + phi = 0 * x + + psi = 0 * x + + # find phi0, and phi1 + lam, v = eig(m[0, 0]) + ind = np.argmin(np.absolute(lam - 1)) + # a dictionary with a binary representation of the + # evaluation points x < 1 -- i.e. position is 0.xxxx + v = np.real(v[:, ind]) + # need scaling function to integrate to 1 so find + # eigenvector normalized to sum(v,axis=0)=1 + sm = np.sum(v) + if sm < 0: # need scaling function to integrate to 1 + v = -v + sm = -sm + bitdic = {'0': v / sm} + bitdic['1'] = np.dot(m[0, 1], bitdic['0']) + step = 1 << J + phi[::step] = bitdic['0'] + phi[(1 << (J - 1))::step] = bitdic['1'] + psi[::step] = np.dot(m[1, 0], bitdic['0']) + psi[(1 << (J - 1))::step] = np.dot(m[1, 1], bitdic['0']) + # descend down the levels inserting more and more values + # into bitdic -- store the values in the correct location once we + # have computed them -- stored in the dictionary + # for quicker use later. + prevkeys = ['1'] + for level in range(2, J + 1): + newkeys = ['%d%s' % (xx, yy) for xx in [0, 1] for yy in prevkeys] + fac = 1 << (J - level) + for key in newkeys: + # convert key to number + num = 0 + for pos in range(level): + if key[pos] == '1': + num += (1 << (level - 1 - pos)) + pastphi = bitdic[key[1:]] + ii = int(key[0]) + temp = np.dot(m[0, ii], pastphi) + bitdic[key] = temp + phi[num * fac::step] = temp + psi[num * fac::step] = np.dot(m[1, ii], pastphi) + prevkeys = newkeys + + return x, phi, psi + + +def morlet(M, w=5.0, s=1.0, complete=True): + """ + Complex Morlet wavelet. + + .. deprecated:: 1.12.0 + + scipy.signal.morlet is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + Parameters + ---------- + M : int + Length of the wavelet. + w : float, optional + Omega0. Default is 5 + s : float, optional + Scaling factor, windowed from ``-s*2*pi`` to ``+s*2*pi``. Default is 1. + complete : bool, optional + Whether to use the complete or the standard version. + + Returns + ------- + morlet : (M,) ndarray + + See Also + -------- + morlet2 : Implementation of Morlet wavelet, compatible with `cwt`. + scipy.signal.gausspulse + + Notes + ----- + The standard version:: + + pi**-0.25 * exp(1j*w*x) * exp(-0.5*(x**2)) + + This commonly used wavelet is often referred to simply as the + Morlet wavelet. Note that this simplified version can cause + admissibility problems at low values of `w`. + + The complete version:: + + pi**-0.25 * (exp(1j*w*x) - exp(-0.5*(w**2))) * exp(-0.5*(x**2)) + + This version has a correction + term to improve admissibility. For `w` greater than 5, the + correction term is negligible. + + Note that the energy of the return wavelet is not normalised + according to `s`. + + The fundamental frequency of this wavelet in Hz is given + by ``f = 2*s*w*r / M`` where `r` is the sampling rate. + + Note: This function was created before `cwt` and is not compatible + with it. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> M = 100 + >>> s = 4.0 + >>> w = 2.0 + >>> wavelet = signal.morlet(M, s, w) + >>> plt.plot(wavelet.real, label="real") + >>> plt.plot(wavelet.imag, label="imag") + >>> plt.legend() + >>> plt.show() + + """ + warnings.warn(_msg % 'morlet', DeprecationWarning, stacklevel=2) + + x = np.linspace(-s * 2 * np.pi, s * 2 * np.pi, M) + output = np.exp(1j * w * x) + + if complete: + output -= np.exp(-0.5 * (w**2)) + + output *= np.exp(-0.5 * (x**2)) * np.pi**(-0.25) + + return output + + +def ricker(points, a): + """ + Return a Ricker wavelet, also known as the "Mexican hat wavelet". + + .. deprecated:: 1.12.0 + + scipy.signal.ricker is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + It models the function: + + ``A * (1 - (x/a)**2) * exp(-0.5*(x/a)**2)``, + + where ``A = 2/(sqrt(3*a)*(pi**0.25))``. + + Parameters + ---------- + points : int + Number of points in `vector`. + Will be centered around 0. + a : scalar + Width parameter of the wavelet. + + Returns + ------- + vector : (N,) ndarray + Array of length `points` in shape of ricker curve. + + Examples + -------- + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> points = 100 + >>> a = 4.0 + >>> vec2 = signal.ricker(points, a) + >>> print(len(vec2)) + 100 + >>> plt.plot(vec2) + >>> plt.show() + + """ + warnings.warn(_msg % 'ricker', DeprecationWarning, stacklevel=2) + return _ricker(points, a) + + +def _ricker(points, a): + A = 2 / (np.sqrt(3 * a) * (np.pi**0.25)) + wsq = a**2 + vec = np.arange(0, points) - (points - 1.0) / 2 + xsq = vec**2 + mod = (1 - xsq / wsq) + gauss = np.exp(-xsq / (2 * wsq)) + total = A * mod * gauss + return total + + +def morlet2(M, s, w=5): + """ + Complex Morlet wavelet, designed to work with `cwt`. + + .. deprecated:: 1.12.0 + + scipy.signal.morlet2 is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + Returns the complete version of morlet wavelet, normalised + according to `s`:: + + exp(1j*w*x/s) * exp(-0.5*(x/s)**2) * pi**(-0.25) * sqrt(1/s) + + Parameters + ---------- + M : int + Length of the wavelet. + s : float + Width parameter of the wavelet. + w : float, optional + Omega0. Default is 5 + + Returns + ------- + morlet : (M,) ndarray + + See Also + -------- + morlet : Implementation of Morlet wavelet, incompatible with `cwt` + + Notes + ----- + + .. versionadded:: 1.4.0 + + This function was designed to work with `cwt`. Because `morlet2` + returns an array of complex numbers, the `dtype` argument of `cwt` + should be set to `complex128` for best results. + + Note the difference in implementation with `morlet`. + The fundamental frequency of this wavelet in Hz is given by:: + + f = w*fs / (2*s*np.pi) + + where ``fs`` is the sampling rate and `s` is the wavelet width parameter. + Similarly we can get the wavelet width parameter at ``f``:: + + s = w*fs / (2*f*np.pi) + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + + >>> M = 100 + >>> s = 4.0 + >>> w = 2.0 + >>> wavelet = signal.morlet2(M, s, w) + >>> plt.plot(abs(wavelet)) + >>> plt.show() + + This example shows basic use of `morlet2` with `cwt` in time-frequency + analysis: + + >>> t, dt = np.linspace(0, 1, 200, retstep=True) + >>> fs = 1/dt + >>> w = 6. + >>> sig = np.cos(2*np.pi*(50 + 10*t)*t) + np.sin(40*np.pi*t) + >>> freq = np.linspace(1, fs/2, 100) + >>> widths = w*fs / (2*freq*np.pi) + >>> cwtm = signal.cwt(sig, signal.morlet2, widths, w=w) + >>> plt.pcolormesh(t, freq, np.abs(cwtm), cmap='viridis', shading='gouraud') + >>> plt.show() + + """ + warnings.warn(_msg % 'morlet2', DeprecationWarning, stacklevel=2) + + x = np.arange(0, M) - (M - 1.0) / 2 + x = x / s + wavelet = np.exp(1j * w * x) * np.exp(-0.5 * x**2) * np.pi**(-0.25) + output = np.sqrt(1/s) * wavelet + return output + + +def cwt(data, wavelet, widths, dtype=None, **kwargs): + """ + Continuous wavelet transform. + + .. deprecated:: 1.12.0 + + scipy.signal.cwt is deprecated in SciPy 1.12 and will be removed + in SciPy 1.15. We recommend using PyWavelets instead. + + Performs a continuous wavelet transform on `data`, + using the `wavelet` function. A CWT performs a convolution + with `data` using the `wavelet` function, which is characterized + by a width parameter and length parameter. The `wavelet` function + is allowed to be complex. + + Parameters + ---------- + data : (N,) ndarray + data on which to perform the transform. + wavelet : function + Wavelet function, which should take 2 arguments. + The first argument is the number of points that the returned vector + will have (len(wavelet(length,width)) == length). + The second is a width parameter, defining the size of the wavelet + (e.g. standard deviation of a gaussian). See `ricker`, which + satisfies these requirements. + widths : (M,) sequence + Widths to use for transform. + dtype : data-type, optional + The desired data type of output. Defaults to ``float64`` if the + output of `wavelet` is real and ``complex128`` if it is complex. + + .. versionadded:: 1.4.0 + + kwargs + Keyword arguments passed to wavelet function. + + .. versionadded:: 1.4.0 + + Returns + ------- + cwt: (M, N) ndarray + Will have shape of (len(widths), len(data)). + + Notes + ----- + + .. versionadded:: 1.4.0 + + For non-symmetric, complex-valued wavelets, the input signal is convolved + with the time-reversed complex-conjugate of the wavelet data [1]. + + :: + + length = min(10 * width[ii], len(data)) + cwt[ii,:] = signal.convolve(data, np.conj(wavelet(length, width[ii], + **kwargs))[::-1], mode='same') + + References + ---------- + .. [1] S. Mallat, "A Wavelet Tour of Signal Processing (3rd Edition)", + Academic Press, 2009. + + Examples + -------- + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> t = np.linspace(-1, 1, 200, endpoint=False) + >>> sig = np.cos(2 * np.pi * 7 * t) + signal.gausspulse(t - 0.4, fc=2) + >>> widths = np.arange(1, 31) + >>> cwtmatr = signal.cwt(sig, signal.ricker, widths) + + .. note:: For cwt matrix plotting it is advisable to flip the y-axis + + >>> cwtmatr_yflip = np.flipud(cwtmatr) + >>> plt.imshow(cwtmatr_yflip, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', + ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) + >>> plt.show() + """ + warnings.warn(_msg % 'cwt', DeprecationWarning, stacklevel=2) + return _cwt(data, wavelet, widths, dtype, **kwargs) + + +def _cwt(data, wavelet, widths, dtype=None, **kwargs): + # Determine output type + if dtype is None: + if np.asarray(wavelet(1, widths[0], **kwargs)).dtype.char in 'FDG': + dtype = np.complex128 + else: + dtype = np.float64 + + output = np.empty((len(widths), len(data)), dtype=dtype) + for ind, width in enumerate(widths): + N = np.min([10 * width, len(data)]) + wavelet_data = np.conj(wavelet(N, width, **kwargs)[::-1]) + output[ind] = convolve(data, wavelet_data, mode='same') + return output diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/bsplines.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/bsplines.py new file mode 100644 index 0000000000000000000000000000000000000000..a90408cbc66684fe5b4fa7802e2cd9bd71af9680 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/bsplines.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'spline_filter', 'gauss_spline', + 'cspline1d', 'qspline1d', 'cspline1d_eval', 'qspline1d_eval', + 'zeros_like', 'array', 'arctan2', + 'tan', 'arange', 'floor', 'exp', 'greater', 'add', + 'cspline2d', 'sepfir2d' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="bsplines", + private_modules=["_bsplines"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..24f5116e687eaa23a45827ff1e1c1b50f6dd4630 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/filter_design.py @@ -0,0 +1,34 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'findfreqs', 'freqs', 'freqz', 'tf2zpk', 'zpk2tf', 'normalize', + 'lp2lp', 'lp2hp', 'lp2bp', 'lp2bs', 'bilinear', 'iirdesign', + 'iirfilter', 'butter', 'cheby1', 'cheby2', 'ellip', 'bessel', + 'band_stop_obj', 'buttord', 'cheb1ord', 'cheb2ord', 'ellipord', + 'buttap', 'cheb1ap', 'cheb2ap', 'ellipap', 'besselap', + 'BadCoefficients', 'freqs_zpk', 'freqz_zpk', + 'tf2sos', 'sos2tf', 'zpk2sos', 'sos2zpk', 'group_delay', + 'sosfreqz', 'iirnotch', 'iirpeak', 'bilinear_zpk', + 'lp2lp_zpk', 'lp2hp_zpk', 'lp2bp_zpk', 'lp2bs_zpk', + 'gammatone', 'iircomb', + 'atleast_1d', 'poly', 'polyval', 'roots', 'resize', 'absolute', + 'tan', 'log10', 'arcsinh', 'exp', 'arccosh', + 'ceil', 'conjugate', 'append', 'prod', 'full', 'array', 'mintypecode', + 'npp_polyval', 'polyvalfromroots', 'optimize', 'sp_fft', 'comb', + 'float_factorial', 'abs', 'maxflat', 'yulewalk', + 'EPSILON', 'filter_dict', 'band_dict', 'bessel_norms' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="filter_design", + private_modules=["_filter_design"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/fir_filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/fir_filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..e320fc8b4fe24002cc53895214d75e9f36719507 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/fir_filter_design.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'kaiser_beta', 'kaiser_atten', 'kaiserord', + 'firwin', 'firwin2', 'remez', 'firls', 'minimum_phase', + 'ceil', 'log', 'irfft', 'fft', 'ifft', 'sinc', 'toeplitz', + 'hankel', 'solve', 'LinAlgError', 'LinAlgWarning', 'lstsq' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="fir_filter_design", + private_modules=["_fir_filter_design"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/lti_conversion.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/lti_conversion.py new file mode 100644 index 0000000000000000000000000000000000000000..2d0399900b89292fffcae6c51df3b74b897e5812 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/lti_conversion.py @@ -0,0 +1,21 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'tf2ss', 'abcd_normalize', 'ss2tf', 'zpk2ss', 'ss2zpk', + 'cont2discrete','eye', 'atleast_2d', + 'poly', 'prod', 'array', 'outer', 'linalg', 'tf2zpk', 'zpk2tf', 'normalize' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="lti_conversion", + private_modules=["_lti_conversion"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/ltisys.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/ltisys.py new file mode 100644 index 0000000000000000000000000000000000000000..4c261d468e680f5c5a0272a65d76cbd2fa39ae54 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/ltisys.py @@ -0,0 +1,30 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'lti', 'dlti', 'TransferFunction', 'ZerosPolesGain', 'StateSpace', + 'lsim', 'impulse', 'step', 'bode', + 'freqresp', 'place_poles', 'dlsim', 'dstep', 'dimpulse', + 'dfreqresp', 'dbode', 's_qr', 'linalg', + 'tf2zpk', 'zpk2tf', 'normalize', 'freqs', + 'freqz', 'freqs_zpk', 'freqz_zpk', 'tf2ss', 'abcd_normalize', + 'ss2tf', 'zpk2ss', 'ss2zpk', 'cont2discrete', 'atleast_1d', + 'squeeze', 'transpose', 'linspace', + 'LinearTimeInvariant', 'TransferFunctionContinuous', + 'TransferFunctionDiscrete', 'ZerosPolesGainContinuous', + 'ZerosPolesGainDiscrete', 'StateSpaceContinuous', + 'StateSpaceDiscrete', 'Bunch' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="ltisys", + private_modules=["_ltisys"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/signaltools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/signaltools.py new file mode 100644 index 0000000000000000000000000000000000000000..88f52a5061c6756ce1397fb015f4822346710b41 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/signaltools.py @@ -0,0 +1,29 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'correlate', 'correlation_lags', 'correlate2d', + 'convolve', 'convolve2d', 'fftconvolve', 'oaconvolve', + 'order_filter', 'medfilt', 'medfilt2d', 'wiener', 'lfilter', + 'lfiltic', 'sosfilt', 'deconvolve', 'hilbert', 'hilbert2', + 'cmplx_sort', 'unique_roots', 'invres', 'invresz', 'residue', + 'residuez', 'resample', 'resample_poly', 'detrend', + 'lfilter_zi', 'sosfilt_zi', 'sosfiltfilt', 'choose_conv_method', + 'filtfilt', 'decimate', 'vectorstrength', + 'timeit', 'cKDTree', 'dlti', 'upfirdn', 'linalg', + 'sp_fft', 'lambertw', 'get_window', 'axis_slice', 'axis_reverse', + 'odd_ext', 'even_ext', 'const_ext', 'cheby1', 'firwin' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="signaltools", + private_modules=["_signaltools"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spectral.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spectral.py new file mode 100644 index 0000000000000000000000000000000000000000..2e38b400aa750f94fed4d65393acbbdcafdde614 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spectral.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'periodogram', 'welch', 'lombscargle', 'csd', 'coherence', + 'spectrogram', 'stft', 'istft', 'check_COLA', 'check_NOLA', + 'sp_fft', 'get_window', 'const_ext', 'even_ext', + 'odd_ext', 'zero_ext' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="spectral", + private_modules=["_spectral_py"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spline.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spline.py new file mode 100644 index 0000000000000000000000000000000000000000..cc15b2e5210ea3780fd51c866c4a2d8d8de6875d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/spline.py @@ -0,0 +1,26 @@ +# This file is not meant for public use and will be removed in the future +# versions of SciPy. Use the `scipy.signal` namespace for importing the +# functions included below. + +import warnings + +from . import _spline + +__all__ = [ # noqa: F822 + 'cspline2d', 'qspline2d', 'sepfir2d', 'symiirorder1', 'symiirorder2'] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + if name not in __all__: + raise AttributeError( + f"scipy.signal.spline is deprecated and has no attribute {name}. " + "Try looking in scipy.signal instead.") + + warnings.warn(f"Please use `{name}` from the `scipy.signal` namespace, " + "the `scipy.signal.spline` namespace is deprecated.", + category=DeprecationWarning, stacklevel=2) + return getattr(_spline, name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/_scipy_spectral_test_shim.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/_scipy_spectral_test_shim.py new file mode 100644 index 0000000000000000000000000000000000000000..c23f310bcae4fa85558f7f07cddb25874a0ec7d1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/_scipy_spectral_test_shim.py @@ -0,0 +1,488 @@ +"""Helpers to utilize existing stft / istft tests for testing `ShortTimeFFT`. + +This module provides the functions stft_compare() and istft_compare(), which, +compares the output between the existing (i)stft() and the shortTimeFFT based +_(i)stft_wrapper() implementations in this module. + +For testing add the following imports to the file ``tests/test_spectral.py``:: + + from ._scipy_spectral_test_shim import stft_compare as stft + from ._scipy_spectral_test_shim import istft_compare as istft + +and remove the existing imports of stft and istft. + +The idea of these wrappers is not to provide a backward-compatible interface +but to demonstrate that the ShortTimeFFT implementation is at least as capable +as the existing one and delivers comparable results. Furthermore, the +wrappers highlight the different philosophies of the implementations, +especially in the border handling. +""" +import platform +from typing import cast, Literal + +import numpy as np +from numpy.testing import assert_allclose + +from scipy.signal import ShortTimeFFT +from scipy.signal import csd, get_window, stft, istft +from scipy.signal._arraytools import const_ext, even_ext, odd_ext, zero_ext +from scipy.signal._short_time_fft import FFT_MODE_TYPE +from scipy.signal._spectral_py import _spectral_helper, _triage_segments, \ + _median_bias + + +def _stft_wrapper(x, fs=1.0, window='hann', nperseg=256, noverlap=None, + nfft=None, detrend=False, return_onesided=True, + boundary='zeros', padded=True, axis=-1, scaling='spectrum'): + """Wrapper for the SciPy `stft()` function based on `ShortTimeFFT` for + unit testing. + + Handling the boundary and padding is where `ShortTimeFFT` and `stft()` + differ in behavior. Parts of `_spectral_helper()` were copied to mimic + the` stft()` behavior. + + This function is meant to be solely used by `stft_compare()`. + """ + if scaling not in ('psd', 'spectrum'): # same errors as in original stft: + raise ValueError(f"Parameter {scaling=} not in ['spectrum', 'psd']!") + + # The following lines are taken from the original _spectral_helper(): + boundary_funcs = {'even': even_ext, + 'odd': odd_ext, + 'constant': const_ext, + 'zeros': zero_ext, + None: None} + + if boundary not in boundary_funcs: + raise ValueError(f"Unknown boundary option '{boundary}', must be one" + + f" of: {list(boundary_funcs.keys())}") + if x.size == 0: + return np.empty(x.shape), np.empty(x.shape), np.empty(x.shape) + + if nperseg is not None: # if specified by user + nperseg = int(nperseg) + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + # parse window; if array like, then set nperseg = win.shape + win, nperseg = _triage_segments(window, nperseg, + input_length=x.shape[axis]) + + if nfft is None: + nfft = nperseg + elif nfft < nperseg: + raise ValueError('nfft must be greater than or equal to nperseg.') + else: + nfft = int(nfft) + + if noverlap is None: + noverlap = nperseg//2 + else: + noverlap = int(noverlap) + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + nstep = nperseg - noverlap + n = x.shape[axis] + + # Padding occurs after boundary extension, so that the extended signal ends + # in zeros, instead of introducing an impulse at the end. + # I.e. if x = [..., 3, 2] + # extend then pad -> [..., 3, 2, 2, 3, 0, 0, 0] + # pad then extend -> [..., 3, 2, 0, 0, 0, 2, 3] + + if boundary is not None: + ext_func = boundary_funcs[boundary] + # Extend by nperseg//2 in front and back: + x = ext_func(x, nperseg//2, axis=axis) + + if padded: + # Pad to integer number of windowed segments + # I.e make x.shape[-1] = nperseg + (nseg-1)*nstep, with integer nseg + x = np.moveaxis(x, axis, -1) + + # This is an edge case where shortTimeFFT returns one more time slice + # than the Scipy stft() shorten to remove last time slice: + if n % 2 == 1 and nperseg % 2 == 1 and noverlap % 2 == 1: + x = x[..., :axis - 1] + + nadd = (-(x.shape[-1]-nperseg) % nstep) % nperseg + zeros_shape = list(x.shape[:-1]) + [nadd] + x = np.concatenate((x, np.zeros(zeros_shape)), axis=-1) + x = np.moveaxis(x, -1, axis) + + # ... end original _spectral_helper() code. + scale_to = {'spectrum': 'magnitude', 'psd': 'psd'}[scaling] + + if np.iscomplexobj(x) and return_onesided: + return_onesided = False + # using cast() to make mypy happy: + fft_mode = cast(FFT_MODE_TYPE, 'onesided' if return_onesided else 'twosided') + + ST = ShortTimeFFT(win, nstep, fs, fft_mode=fft_mode, mfft=nfft, + scale_to=scale_to, phase_shift=None) + + k_off = nperseg // 2 + p0 = 0 # ST.lower_border_end[1] + 1 + nn = x.shape[axis] if padded else n+k_off+1 + p1 = ST.upper_border_begin(nn)[1] # ST.p_max(n) + 1 + + # This is bad hack to pass the test test_roundtrip_boundary_extension(): + if padded is True and nperseg - noverlap == 1: + p1 -= nperseg // 2 - 1 # the reasoning behind this is not clear to me + + detr = None if detrend is False else detrend + Sxx = ST.stft_detrend(x, detr, p0, p1, k_offset=k_off, axis=axis) + t = ST.t(nn, 0, p1 - p0, k_offset=0 if boundary is not None else k_off) + if x.dtype in (np.float32, np.complex64): + Sxx = Sxx.astype(np.complex64) + + # workaround for test_average_all_segments() - seems to be buggy behavior: + if boundary is None and padded is False: + t, Sxx = t[1:-1], Sxx[..., :-2] + t -= k_off / fs + + return ST.f, t, Sxx + + +def _istft_wrapper(Zxx, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, input_onesided=True, boundary=True, time_axis=-1, + freq_axis=-2, scaling='spectrum') -> \ + tuple[np.ndarray, np.ndarray, tuple[int, int]]: + """Wrapper for the SciPy `istft()` function based on `ShortTimeFFT` for + unit testing. + + Note that only option handling is implemented as far as to handle the unit + tests. E.g., the case ``nperseg=None`` is not handled. + + This function is meant to be solely used by `istft_compare()`. + """ + # *** Lines are taken from _spectral_py.istft() ***: + if Zxx.ndim < 2: + raise ValueError('Input stft must be at least 2d!') + + if freq_axis == time_axis: + raise ValueError('Must specify differing time and frequency axes!') + + nseg = Zxx.shape[time_axis] + + if input_onesided: + # Assume even segment length + n_default = 2*(Zxx.shape[freq_axis] - 1) + else: + n_default = Zxx.shape[freq_axis] + + # Check windowing parameters + if nperseg is None: + nperseg = n_default + else: + nperseg = int(nperseg) + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + if nfft is None: + if input_onesided and (nperseg == n_default + 1): + # Odd nperseg, no FFT padding + nfft = nperseg + else: + nfft = n_default + elif nfft < nperseg: + raise ValueError('nfft must be greater than or equal to nperseg.') + else: + nfft = int(nfft) + + if noverlap is None: + noverlap = nperseg//2 + else: + noverlap = int(noverlap) + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + nstep = nperseg - noverlap + + # Get window as array + if isinstance(window, str) or type(window) is tuple: + win = get_window(window, nperseg) + else: + win = np.asarray(window) + if len(win.shape) != 1: + raise ValueError('window must be 1-D') + if win.shape[0] != nperseg: + raise ValueError(f'window must have length of {nperseg}') + + outputlength = nperseg + (nseg-1)*nstep + # *** End block of: Taken from _spectral_py.istft() *** + + # Using cast() to make mypy happy: + fft_mode = cast(FFT_MODE_TYPE, 'onesided' if input_onesided else 'twosided') + scale_to = cast(Literal['magnitude', 'psd'], + {'spectrum': 'magnitude', 'psd': 'psd'}[scaling]) + + ST = ShortTimeFFT(win, nstep, fs, fft_mode=fft_mode, mfft=nfft, + scale_to=scale_to, phase_shift=None) + + if boundary: + j = nperseg if nperseg % 2 == 0 else nperseg - 1 + k0 = ST.k_min + nperseg // 2 + k1 = outputlength - j + k0 + else: + raise NotImplementedError("boundary=False does not make sense with" + + "ShortTimeFFT.istft()!") + + x = ST.istft(Zxx, k0=k0, k1=k1, f_axis=freq_axis, t_axis=time_axis) + t = np.arange(k1 - k0) * ST.T + k_hi = ST.upper_border_begin(k1 - k0)[0] + # using cast() to make mypy happy: + return t, x, (ST.lower_border_end[0], k_hi) + + +def _csd_wrapper(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1, average='mean'): + """Wrapper for the `csd()` function based on `ShortTimeFFT` for + unit testing. + """ + freqs, _, Pxy = _csd_test_shim(x, y, fs, window, nperseg, noverlap, nfft, + detrend, return_onesided, scaling, axis) + + # The following code is taken from csd(): + if len(Pxy.shape) >= 2 and Pxy.size > 0: + if Pxy.shape[-1] > 1: + if average == 'median': + # np.median must be passed real arrays for the desired result + bias = _median_bias(Pxy.shape[-1]) + if np.iscomplexobj(Pxy): + Pxy = (np.median(np.real(Pxy), axis=-1) + + 1j * np.median(np.imag(Pxy), axis=-1)) + else: + Pxy = np.median(Pxy, axis=-1) + Pxy /= bias + elif average == 'mean': + Pxy = Pxy.mean(axis=-1) + else: + raise ValueError(f'average must be "median" or "mean", got {average}') + else: + Pxy = np.reshape(Pxy, Pxy.shape[:-1]) + + return freqs, Pxy + + +def _csd_test_shim(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1): + """Compare output of _spectral_helper() and ShortTimeFFT, more + precisely _spect_helper_csd() for used in csd_wrapper(). + + The motivation of this function is to test if the ShortTimeFFT-based + wrapper `_spect_helper_csd()` returns the same values as `_spectral_helper`. + This function should only be usd by csd() in (unit) testing. + """ + freqs, t, Pxy = _spectral_helper(x, y, fs, window, nperseg, noverlap, nfft, + detrend, return_onesided, scaling, axis, + mode='psd') + freqs1, Pxy1 = _spect_helper_csd(x, y, fs, window, nperseg, noverlap, nfft, + detrend, return_onesided, scaling, axis) + + np.testing.assert_allclose(freqs1, freqs) + amax_Pxy = max(np.abs(Pxy).max(), 1) if Pxy.size else 1 + atol = np.finfo(Pxy.dtype).resolution * amax_Pxy # needed for large Pxy + # for c_ in range(Pxy.shape[-1]): + # np.testing.assert_allclose(Pxy1[:, c_], Pxy[:, c_], atol=atol) + np.testing.assert_allclose(Pxy1, Pxy, atol=atol) + return freqs, t, Pxy + + +def _spect_helper_csd(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1): + """Wrapper for replacing _spectral_helper() by using the ShortTimeFFT + for use by csd(). + + This function should be only used by _csd_test_shim() and is only useful + for testing the ShortTimeFFT implementation. + """ + + # The following lines are taken from the original _spectral_helper(): + same_data = y is x + axis = int(axis) + + # Ensure we have np.arrays, get outdtype + x = np.asarray(x) + if not same_data: + y = np.asarray(y) + # outdtype = np.result_type(x, y, np.complex64) + # else: + # outdtype = np.result_type(x, np.complex64) + + if not same_data: + # Check if we can broadcast the outer axes together + xouter = list(x.shape) + youter = list(y.shape) + xouter.pop(axis) + youter.pop(axis) + try: + outershape = np.broadcast(np.empty(xouter), np.empty(youter)).shape + except ValueError as e: + raise ValueError('x and y cannot be broadcast together.') from e + + if same_data: + if x.size == 0: + return np.empty(x.shape), np.empty(x.shape) + else: + if x.size == 0 or y.size == 0: + outshape = outershape + (min([x.shape[axis], y.shape[axis]]),) + emptyout = np.moveaxis(np.empty(outshape), -1, axis) + return emptyout, emptyout + + if nperseg is not None: # if specified by user + nperseg = int(nperseg) + if nperseg < 1: + raise ValueError('nperseg must be a positive integer') + + # parse window; if array like, then set nperseg = win.shape + n = x.shape[axis] if same_data else max(x.shape[axis], y.shape[axis]) + win, nperseg = _triage_segments(window, nperseg, input_length=n) + + if nfft is None: + nfft = nperseg + elif nfft < nperseg: + raise ValueError('nfft must be greater than or equal to nperseg.') + else: + nfft = int(nfft) + + if noverlap is None: + noverlap = nperseg // 2 + else: + noverlap = int(noverlap) + if noverlap >= nperseg: + raise ValueError('noverlap must be less than nperseg.') + nstep = nperseg - noverlap + + if np.iscomplexobj(x) and return_onesided: + return_onesided = False + + # using cast() to make mypy happy: + fft_mode = cast(FFT_MODE_TYPE, 'onesided' if return_onesided + else 'twosided') + scale = {'spectrum': 'magnitude', 'density': 'psd'}[scaling] + SFT = ShortTimeFFT(win, nstep, fs, fft_mode=fft_mode, mfft=nfft, + scale_to=scale, phase_shift=None) + + # _spectral_helper() calculates X.conj()*Y instead of X*Y.conj(): + Pxy = SFT.spectrogram(y, x, detr=None if detrend is False else detrend, + p0=0, p1=(n-noverlap)//SFT.hop, k_offset=nperseg//2, + axis=axis).conj() + # Note: + # 'onesided2X' scaling of ShortTimeFFT conflicts with the + # scaling='spectrum' parameter, since it doubles the squared magnitude, + # which in the view of the ShortTimeFFT implementation does not make sense. + # Hence, the doubling of the square is implemented here: + if return_onesided: + f_axis = Pxy.ndim - 1 + axis if axis < 0 else axis + Pxy = np.moveaxis(Pxy, f_axis, -1) + Pxy[..., 1:-1 if SFT.mfft % 2 == 0 else None] *= 2 + Pxy = np.moveaxis(Pxy, -1, f_axis) + + return SFT.f, Pxy + + +def stft_compare(x, fs=1.0, window='hann', nperseg=256, noverlap=None, + nfft=None, detrend=False, return_onesided=True, + boundary='zeros', padded=True, axis=-1, scaling='spectrum'): + """Assert that the results from the existing `stft()` and `_stft_wrapper()` + are close to each other. + + For comparing the STFT values an absolute tolerance of the floating point + resolution was added to circumvent problems with the following tests: + * For float32 the tolerances are much higher in + TestSTFT.test_roundtrip_float32()). + * The TestSTFT.test_roundtrip_scaling() has a high relative deviation. + Interestingly this did not appear in Scipy 1.9.1 but only in the current + development version. + """ + kw = dict(x=x, fs=fs, window=window, nperseg=nperseg, noverlap=noverlap, + nfft=nfft, detrend=detrend, return_onesided=return_onesided, + boundary=boundary, padded=padded, axis=axis, scaling=scaling) + f, t, Zxx = stft(**kw) + f_wrapper, t_wrapper, Zxx_wrapper = _stft_wrapper(**kw) + + e_msg_part = " of `stft_wrapper()` differ from `stft()`." + assert_allclose(f_wrapper, f, err_msg=f"Frequencies {e_msg_part}") + assert_allclose(t_wrapper, t, err_msg=f"Time slices {e_msg_part}") + + # Adapted tolerances to account for: + atol = np.finfo(Zxx.dtype).resolution * 2 + assert_allclose(Zxx_wrapper, Zxx, atol=atol, + err_msg=f"STFT values {e_msg_part}") + return f, t, Zxx + + +def istft_compare(Zxx, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, input_onesided=True, boundary=True, time_axis=-1, + freq_axis=-2, scaling='spectrum'): + """Assert that the results from the existing `istft()` and + `_istft_wrapper()` are close to each other. + + Quirks: + * If ``boundary=False`` the comparison is skipped, since it does not + make sense with ShortTimeFFT.istft(). Only used in test + TestSTFT.test_roundtrip_boundary_extension(). + * If ShortTimeFFT.istft() decides the STFT is not invertible, the + comparison is skipped, since istft() only emits a warning and does not + return a correct result. Only used in + ShortTimeFFT.test_roundtrip_not_nola(). + * For comparing the signals an absolute tolerance of the floating point + resolution was added to account for the low accuracy of float32 (Occurs + only in TestSTFT.test_roundtrip_float32()). + """ + kw = dict(Zxx=Zxx, fs=fs, window=window, nperseg=nperseg, + noverlap=noverlap, nfft=nfft, input_onesided=input_onesided, + boundary=boundary, time_axis=time_axis, freq_axis=freq_axis, + scaling=scaling) + + t, x = istft(**kw) + if not boundary: # skip test_roundtrip_boundary_extension(): + return t, x # _istft_wrapper does() not implement this case + try: # if inversion fails, istft() only emits a warning: + t_wrapper, x_wrapper, (k_lo, k_hi) = _istft_wrapper(**kw) + except ValueError as v: # Do nothing if inversion fails: + if v.args[0] == "Short-time Fourier Transform not invertible!": + return t, x + raise v + + e_msg_part = " of `istft_wrapper()` differ from `istft()`" + assert_allclose(t, t_wrapper, err_msg=f"Sample times {e_msg_part}") + + # Adapted tolerances to account for resolution loss: + atol = np.finfo(x.dtype).resolution*2 # instead of default atol = 0 + rtol = 1e-7 # default for np.allclose() + + # Relax atol on 32-Bit platforms a bit to pass CI tests. + # - Not clear why there are discrepancies (in the FFT maybe?) + # - Not sure what changed on 'i686' since earlier on those test passed + if x.dtype == np.float32 and platform.machine() == 'i686': + # float32 gets only used by TestSTFT.test_roundtrip_float32() so + # we are using the tolerances from there to circumvent CI problems + atol, rtol = 1e-4, 1e-5 + elif platform.machine() in ('aarch64', 'i386', 'i686'): + atol = max(atol, 1e-12) # 2e-15 seems too tight for 32-Bit platforms + + assert_allclose(x_wrapper[k_lo:k_hi], x[k_lo:k_hi], atol=atol, rtol=rtol, + err_msg=f"Signal values {e_msg_part}") + return t, x + + +def csd_compare(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, + nfft=None, detrend='constant', return_onesided=True, + scaling='density', axis=-1, average='mean'): + """Assert that the results from the existing `csd()` and `_csd_wrapper()` + are close to each other. """ + kw = dict(x=x, y=y, fs=fs, window=window, nperseg=nperseg, + noverlap=noverlap, nfft=nfft, detrend=detrend, + return_onesided=return_onesided, scaling=scaling, axis=axis, + average=average) + freqs0, Pxy0 = csd(**kw) + freqs1, Pxy1 = _csd_wrapper(**kw) + + assert_allclose(freqs1, freqs0) + assert_allclose(Pxy1, Pxy0) + assert_allclose(freqs1, freqs0) + return freqs0, Pxy0 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/mpsig.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/mpsig.py new file mode 100644 index 0000000000000000000000000000000000000000..d129de74e5df00c22bc0b82c7d3f7b52483941f9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/mpsig.py @@ -0,0 +1,122 @@ +""" +Some signal functions implemented using mpmath. +""" + +try: + import mpmath +except ImportError: + mpmath = None + + +def _prod(seq): + """Returns the product of the elements in the sequence `seq`.""" + p = 1 + for elem in seq: + p *= elem + return p + + +def _relative_degree(z, p): + """ + Return relative degree of transfer function from zeros and poles. + + This is simply len(p) - len(z), which must be nonnegative. + A ValueError is raised if len(p) < len(z). + """ + degree = len(p) - len(z) + if degree < 0: + raise ValueError("Improper transfer function. " + "Must have at least as many poles as zeros.") + return degree + + +def _zpkbilinear(z, p, k, fs): + """Bilinear transformation to convert a filter from analog to digital.""" + + degree = _relative_degree(z, p) + + fs2 = 2*fs + + # Bilinear transform the poles and zeros + z_z = [(fs2 + z1) / (fs2 - z1) for z1 in z] + p_z = [(fs2 + p1) / (fs2 - p1) for p1 in p] + + # Any zeros that were at infinity get moved to the Nyquist frequency + z_z.extend([-1] * degree) + + # Compensate for gain change + numer = _prod(fs2 - z1 for z1 in z) + denom = _prod(fs2 - p1 for p1 in p) + k_z = k * numer / denom + + return z_z, p_z, k_z.real + + +def _zpklp2lp(z, p, k, wo=1): + """Transform a lowpass filter to a different cutoff frequency.""" + + degree = _relative_degree(z, p) + + # Scale all points radially from origin to shift cutoff frequency + z_lp = [wo * z1 for z1 in z] + p_lp = [wo * p1 for p1 in p] + + # Each shifted pole decreases gain by wo, each shifted zero increases it. + # Cancel out the net change to keep overall gain the same + k_lp = k * wo**degree + + return z_lp, p_lp, k_lp + + +def _butter_analog_poles(n): + """ + Poles of an analog Butterworth lowpass filter. + + This is the same calculation as scipy.signal.buttap(n) or + scipy.signal.butter(n, 1, analog=True, output='zpk'), but mpmath is used, + and only the poles are returned. + """ + poles = [-mpmath.exp(1j*mpmath.pi*k/(2*n)) for k in range(-n+1, n, 2)] + return poles + + +def butter_lp(n, Wn): + """ + Lowpass Butterworth digital filter design. + + This computes the same result as scipy.signal.butter(n, Wn, output='zpk'), + but it uses mpmath, and the results are returned in lists instead of NumPy + arrays. + """ + zeros = [] + poles = _butter_analog_poles(n) + k = 1 + fs = 2 + warped = 2 * fs * mpmath.tan(mpmath.pi * Wn / fs) + z, p, k = _zpklp2lp(zeros, poles, k, wo=warped) + z, p, k = _zpkbilinear(z, p, k, fs=fs) + return z, p, k + + +def zpkfreqz(z, p, k, worN=None): + """ + Frequency response of a filter in zpk format, using mpmath. + + This is the same calculation as scipy.signal.freqz, but the input is in + zpk format, the calculation is performed using mpath, and the results are + returned in lists instead of NumPy arrays. + """ + if worN is None or isinstance(worN, int): + N = worN or 512 + ws = [mpmath.pi * mpmath.mpf(j) / N for j in range(N)] + else: + ws = worN + + h = [] + for wk in ws: + zm1 = mpmath.exp(1j * wk) + numer = _prod([zm1 - t for t in z]) + denom = _prod([zm1 - t for t in p]) + hk = k * numer / denom + h.append(hk) + return ws, h diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_array_tools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_array_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..81503b7e267cf9f74999d283b0d33b012fd0f77c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_array_tools.py @@ -0,0 +1,111 @@ +import numpy as np + +from numpy.testing import assert_array_equal +from pytest import raises as assert_raises + +from scipy.signal._arraytools import (axis_slice, axis_reverse, + odd_ext, even_ext, const_ext, zero_ext) + + +class TestArrayTools: + + def test_axis_slice(self): + a = np.arange(12).reshape(3, 4) + + s = axis_slice(a, start=0, stop=1, axis=0) + assert_array_equal(s, a[0:1, :]) + + s = axis_slice(a, start=-1, axis=0) + assert_array_equal(s, a[-1:, :]) + + s = axis_slice(a, start=0, stop=1, axis=1) + assert_array_equal(s, a[:, 0:1]) + + s = axis_slice(a, start=-1, axis=1) + assert_array_equal(s, a[:, -1:]) + + s = axis_slice(a, start=0, step=2, axis=0) + assert_array_equal(s, a[::2, :]) + + s = axis_slice(a, start=0, step=2, axis=1) + assert_array_equal(s, a[:, ::2]) + + def test_axis_reverse(self): + a = np.arange(12).reshape(3, 4) + + r = axis_reverse(a, axis=0) + assert_array_equal(r, a[::-1, :]) + + r = axis_reverse(a, axis=1) + assert_array_equal(r, a[:, ::-1]) + + def test_odd_ext(self): + a = np.array([[1, 2, 3, 4, 5], + [9, 8, 7, 6, 5]]) + + odd = odd_ext(a, 2, axis=1) + expected = np.array([[-1, 0, 1, 2, 3, 4, 5, 6, 7], + [11, 10, 9, 8, 7, 6, 5, 4, 3]]) + assert_array_equal(odd, expected) + + odd = odd_ext(a, 1, axis=0) + expected = np.array([[-7, -4, -1, 2, 5], + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + [17, 14, 11, 8, 5]]) + assert_array_equal(odd, expected) + + assert_raises(ValueError, odd_ext, a, 2, axis=0) + assert_raises(ValueError, odd_ext, a, 5, axis=1) + + def test_even_ext(self): + a = np.array([[1, 2, 3, 4, 5], + [9, 8, 7, 6, 5]]) + + even = even_ext(a, 2, axis=1) + expected = np.array([[3, 2, 1, 2, 3, 4, 5, 4, 3], + [7, 8, 9, 8, 7, 6, 5, 6, 7]]) + assert_array_equal(even, expected) + + even = even_ext(a, 1, axis=0) + expected = np.array([[9, 8, 7, 6, 5], + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + [1, 2, 3, 4, 5]]) + assert_array_equal(even, expected) + + assert_raises(ValueError, even_ext, a, 2, axis=0) + assert_raises(ValueError, even_ext, a, 5, axis=1) + + def test_const_ext(self): + a = np.array([[1, 2, 3, 4, 5], + [9, 8, 7, 6, 5]]) + + const = const_ext(a, 2, axis=1) + expected = np.array([[1, 1, 1, 2, 3, 4, 5, 5, 5], + [9, 9, 9, 8, 7, 6, 5, 5, 5]]) + assert_array_equal(const, expected) + + const = const_ext(a, 1, axis=0) + expected = np.array([[1, 2, 3, 4, 5], + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + [9, 8, 7, 6, 5]]) + assert_array_equal(const, expected) + + def test_zero_ext(self): + a = np.array([[1, 2, 3, 4, 5], + [9, 8, 7, 6, 5]]) + + zero = zero_ext(a, 2, axis=1) + expected = np.array([[0, 0, 1, 2, 3, 4, 5, 0, 0], + [0, 0, 9, 8, 7, 6, 5, 0, 0]]) + assert_array_equal(zero, expected) + + zero = zero_ext(a, 1, axis=0) + expected = np.array([[0, 0, 0, 0, 0], + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + [0, 0, 0, 0, 0]]) + assert_array_equal(zero, expected) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_bsplines.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_bsplines.py new file mode 100644 index 0000000000000000000000000000000000000000..828276edd4df0acba0fb5a652e4305e44ce34566 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_bsplines.py @@ -0,0 +1,186 @@ +# pylint: disable=missing-docstring +import numpy as np +from numpy import array +from numpy.testing import (assert_allclose, assert_array_equal, + assert_almost_equal) +import pytest +from pytest import raises + +import scipy.signal._bsplines as bsp +from scipy import signal + + +class TestBSplines: + """Test behaviors of B-splines. Some of the values tested against were + returned as of SciPy 1.1.0 and are included for regression testing + purposes. Others (at integer points) are compared to theoretical + expressions (cf. Unser, Aldroubi, Eden, IEEE TSP 1993, Table 1).""" + + def test_spline_filter(self): + np.random.seed(12457) + # Test the type-error branch + raises(TypeError, bsp.spline_filter, array([0]), 0) + # Test the real branch + np.random.seed(12457) + data_array_real = np.random.rand(12, 12) + # make the magnitude exceed 1, and make some negative + data_array_real = 10*(1-2*data_array_real) + result_array_real = array( + [[-.463312621, 8.33391222, .697290949, 5.28390836, + 5.92066474, 6.59452137, 9.84406950, -8.78324188, + 7.20675750, -8.17222994, -4.38633345, 9.89917069], + [2.67755154, 6.24192170, -3.15730578, 9.87658581, + -9.96930425, 3.17194115, -4.50919947, 5.75423446, + 9.65979824, -8.29066885, .971416087, -2.38331897], + [-7.08868346, 4.89887705, -1.37062289, 7.70705838, + 2.51526461, 3.65885497, 5.16786604, -8.77715342e-03, + 4.10533325, 9.04761993, -.577960351, 9.86382519], + [-4.71444301, -1.68038985, 2.84695116, 1.14315938, + -3.17127091, 1.91830461, 7.13779687, -5.35737482, + -9.66586425, -9.87717456, 9.93160672, 4.71948144], + [9.49551194, -1.92958436, 6.25427993, -9.05582911, + 3.97562282, 7.68232426, -1.04514824, -5.86021443, + -8.43007451, 5.47528997, 2.06330736, -8.65968112], + [-8.91720100, 8.87065356, 3.76879937, 2.56222894, + -.828387146, 8.72288903, 6.42474741, -6.84576083, + 9.94724115, 6.90665380, -6.61084494, -9.44907391], + [9.25196790, -.774032030, 7.05371046, -2.73505725, + 2.53953305, -1.82889155, 2.95454824, -1.66362046, + 5.72478916, -3.10287679, 1.54017123, -7.87759020], + [-3.98464539, -2.44316992, -1.12708657, 1.01725672, + -8.89294671, -5.42145629, -6.16370321, 2.91775492, + 9.64132208, .702499998, -2.02622392, 1.56308431], + [-2.22050773, 7.89951554, 5.98970713, -7.35861835, + 5.45459283, -7.76427957, 3.67280490, -4.05521315, + 4.51967507, -3.22738749, -3.65080177, 3.05630155], + [-6.21240584, -.296796126, -8.34800163, 9.21564563, + -3.61958784, -4.77120006, -3.99454057, 1.05021988e-03, + -6.95982829, 6.04380797, 8.43181250, -2.71653339], + [1.19638037, 6.99718842e-02, 6.72020394, -2.13963198, + 3.75309875, -5.70076744, 5.92143551, -7.22150575, + -3.77114594, -1.11903194, -5.39151466, 3.06620093], + [9.86326886, 1.05134482, -7.75950607, -3.64429655, + 7.81848957, -9.02270373, 3.73399754, -4.71962549, + -7.71144306, 3.78263161, 6.46034818, -4.43444731]]) + assert_allclose(bsp.spline_filter(data_array_real, 0), + result_array_real) + + def test_gauss_spline(self): + np.random.seed(12459) + assert_almost_equal(bsp.gauss_spline(0, 0), 1.381976597885342) + assert_allclose(bsp.gauss_spline(array([1.]), 1), array([0.04865217])) + + def test_gauss_spline_list(self): + # regression test for gh-12152 (accept array_like) + knots = [-1.0, 0.0, -1.0] + assert_almost_equal(bsp.gauss_spline(knots, 3), + array([0.15418033, 0.6909883, 0.15418033])) + + def test_cspline1d(self): + np.random.seed(12462) + assert_array_equal(bsp.cspline1d(array([0])), [0.]) + c1d = array([1.21037185, 1.86293902, 2.98834059, 4.11660378, + 4.78893826]) + # test lamda != 0 + assert_allclose(bsp.cspline1d(array([1., 2, 3, 4, 5]), 1), c1d) + c1d0 = array([0.78683946, 2.05333735, 2.99981113, 3.94741812, + 5.21051638]) + assert_allclose(bsp.cspline1d(array([1., 2, 3, 4, 5])), c1d0) + + def test_qspline1d(self): + np.random.seed(12463) + assert_array_equal(bsp.qspline1d(array([0])), [0.]) + # test lamda != 0 + raises(ValueError, bsp.qspline1d, array([1., 2, 3, 4, 5]), 1.) + raises(ValueError, bsp.qspline1d, array([1., 2, 3, 4, 5]), -1.) + q1d0 = array([0.85350007, 2.02441743, 2.99999534, 3.97561055, + 5.14634135]) + assert_allclose(bsp.qspline1d(array([1., 2, 3, 4, 5])), q1d0) + + def test_cspline1d_eval(self): + np.random.seed(12464) + assert_allclose(bsp.cspline1d_eval(array([0., 0]), [0.]), array([0.])) + assert_array_equal(bsp.cspline1d_eval(array([1., 0, 1]), []), + array([])) + x = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] + dx = x[1]-x[0] + newx = [-6., -5.5, -5., -4.5, -4., -3.5, -3., -2.5, -2., -1.5, -1., + -0.5, 0., 0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5, 6., + 6.5, 7., 7.5, 8., 8.5, 9., 9.5, 10., 10.5, 11., 11.5, 12., + 12.5] + y = array([4.216, 6.864, 3.514, 6.203, 6.759, 7.433, 7.874, 5.879, + 1.396, 4.094]) + cj = bsp.cspline1d(y) + newy = array([6.203, 4.41570658, 3.514, 5.16924703, 6.864, 6.04643068, + 4.21600281, 6.04643068, 6.864, 5.16924703, 3.514, + 4.41570658, 6.203, 6.80717667, 6.759, 6.98971173, 7.433, + 7.79560142, 7.874, 7.41525761, 5.879, 3.18686814, 1.396, + 2.24889482, 4.094, 2.24889482, 1.396, 3.18686814, 5.879, + 7.41525761, 7.874, 7.79560142, 7.433, 6.98971173, 6.759, + 6.80717667, 6.203, 4.41570658]) + assert_allclose(bsp.cspline1d_eval(cj, newx, dx=dx, x0=x[0]), newy) + + def test_qspline1d_eval(self): + np.random.seed(12465) + assert_allclose(bsp.qspline1d_eval(array([0., 0]), [0.]), array([0.])) + assert_array_equal(bsp.qspline1d_eval(array([1., 0, 1]), []), + array([])) + x = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] + dx = x[1]-x[0] + newx = [-6., -5.5, -5., -4.5, -4., -3.5, -3., -2.5, -2., -1.5, -1., + -0.5, 0., 0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5, 6., + 6.5, 7., 7.5, 8., 8.5, 9., 9.5, 10., 10.5, 11., 11.5, 12., + 12.5] + y = array([4.216, 6.864, 3.514, 6.203, 6.759, 7.433, 7.874, 5.879, + 1.396, 4.094]) + cj = bsp.qspline1d(y) + newy = array([6.203, 4.49418159, 3.514, 5.18390821, 6.864, 5.91436915, + 4.21600002, 5.91436915, 6.864, 5.18390821, 3.514, + 4.49418159, 6.203, 6.71900226, 6.759, 7.03980488, 7.433, + 7.81016848, 7.874, 7.32718426, 5.879, 3.23872593, 1.396, + 2.34046013, 4.094, 2.34046013, 1.396, 3.23872593, 5.879, + 7.32718426, 7.874, 7.81016848, 7.433, 7.03980488, 6.759, + 6.71900226, 6.203, 4.49418159]) + assert_allclose(bsp.qspline1d_eval(cj, newx, dx=dx, x0=x[0]), newy) + + +def test_sepfir2d_invalid_filter(): + filt = np.array([1.0, 2.0, 4.0, 2.0, 1.0]) + image = np.random.rand(7, 9) + # No error for odd lengths + signal.sepfir2d(image, filt, filt[2:]) + + # Row or column filter must be odd + with pytest.raises(ValueError, match="odd length"): + signal.sepfir2d(image, filt, filt[1:]) + with pytest.raises(ValueError, match="odd length"): + signal.sepfir2d(image, filt[1:], filt) + + # Filters must be 1-dimensional + with pytest.raises(ValueError, match="object too deep"): + signal.sepfir2d(image, filt.reshape(1, -1), filt) + with pytest.raises(ValueError, match="object too deep"): + signal.sepfir2d(image, filt, filt.reshape(1, -1)) + +def test_sepfir2d_invalid_image(): + filt = np.array([1.0, 2.0, 4.0, 2.0, 1.0]) + image = np.random.rand(8, 8) + + # Image must be 2 dimensional + with pytest.raises(ValueError, match="object too deep"): + signal.sepfir2d(image.reshape(4, 4, 4), filt, filt) + + with pytest.raises(ValueError, match="object of too small depth"): + signal.sepfir2d(image[0], filt, filt) + + +def test_cspline2d(): + np.random.seed(181819142) + image = np.random.rand(71, 73) + signal.cspline2d(image, 8.0) + + +def test_qspline2d(): + np.random.seed(181819143) + image = np.random.rand(71, 73) + signal.qspline2d(image) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_cont2discrete.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_cont2discrete.py new file mode 100644 index 0000000000000000000000000000000000000000..51b9be56e29c4e0448020c2d8e8ff6e7e336f8c3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_cont2discrete.py @@ -0,0 +1,416 @@ +import numpy as np +from numpy.testing import \ + assert_array_almost_equal, assert_almost_equal, \ + assert_allclose, assert_equal + +import pytest +from scipy.signal import cont2discrete as c2d +from scipy.signal import dlsim, ss2tf, ss2zpk, lsim, lti +from scipy.signal import tf2ss, impulse, dimpulse, step, dstep + +# Author: Jeffrey Armstrong +# March 29, 2011 + + +class TestC2D: + def test_zoh(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + ad_truth = 1.648721270700128 * np.eye(2) + bd_truth = np.full((2, 1), 0.324360635350064) + # c and d in discrete should be equal to their continuous counterparts + dt_requested = 0.5 + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, method='zoh') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cc, cd) + assert_array_almost_equal(dc, dd) + assert_almost_equal(dt_requested, dt) + + def test_foh(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + # True values are verified with Matlab + ad_truth = 1.648721270700128 * np.eye(2) + bd_truth = np.full((2, 1), 0.420839287058789) + cd_truth = cc + dd_truth = np.array([[0.260262223725224], + [0.297442541400256], + [-0.144098411624840]]) + dt_requested = 0.5 + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, method='foh') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + assert_almost_equal(dt_requested, dt) + + def test_impulse(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [0.0]]) + + # True values are verified with Matlab + ad_truth = 1.648721270700128 * np.eye(2) + bd_truth = np.full((2, 1), 0.412180317675032) + cd_truth = cc + dd_truth = np.array([[0.4375], [0.5], [0.3125]]) + dt_requested = 0.5 + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='impulse') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + assert_almost_equal(dt_requested, dt) + + def test_gbt(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + dt_requested = 0.5 + alpha = 1.0 / 3.0 + + ad_truth = 1.6 * np.eye(2) + bd_truth = np.full((2, 1), 0.3) + cd_truth = np.array([[0.9, 1.2], + [1.2, 1.2], + [1.2, 0.3]]) + dd_truth = np.array([[0.175], + [0.2], + [-0.205]]) + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='gbt', alpha=alpha) + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + + def test_euler(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + dt_requested = 0.5 + + ad_truth = 1.5 * np.eye(2) + bd_truth = np.full((2, 1), 0.25) + cd_truth = np.array([[0.75, 1.0], + [1.0, 1.0], + [1.0, 0.25]]) + dd_truth = dc + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='euler') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + assert_almost_equal(dt_requested, dt) + + def test_backward_diff(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + dt_requested = 0.5 + + ad_truth = 2.0 * np.eye(2) + bd_truth = np.full((2, 1), 0.5) + cd_truth = np.array([[1.5, 2.0], + [2.0, 2.0], + [2.0, 0.5]]) + dd_truth = np.array([[0.875], + [1.0], + [0.295]]) + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='backward_diff') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + + def test_bilinear(self): + ac = np.eye(2) + bc = np.full((2, 1), 0.5) + cc = np.array([[0.75, 1.0], [1.0, 1.0], [1.0, 0.25]]) + dc = np.array([[0.0], [0.0], [-0.33]]) + + dt_requested = 0.5 + + ad_truth = (5.0 / 3.0) * np.eye(2) + bd_truth = np.full((2, 1), 1.0 / 3.0) + cd_truth = np.array([[1.0, 4.0 / 3.0], + [4.0 / 3.0, 4.0 / 3.0], + [4.0 / 3.0, 1.0 / 3.0]]) + dd_truth = np.array([[0.291666666666667], + [1.0 / 3.0], + [-0.121666666666667]]) + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='bilinear') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + assert_almost_equal(dt_requested, dt) + + # Same continuous system again, but change sampling rate + + ad_truth = 1.4 * np.eye(2) + bd_truth = np.full((2, 1), 0.2) + cd_truth = np.array([[0.9, 1.2], [1.2, 1.2], [1.2, 0.3]]) + dd_truth = np.array([[0.175], [0.2], [-0.205]]) + + dt_requested = 1.0 / 3.0 + + ad, bd, cd, dd, dt = c2d((ac, bc, cc, dc), dt_requested, + method='bilinear') + + assert_array_almost_equal(ad_truth, ad) + assert_array_almost_equal(bd_truth, bd) + assert_array_almost_equal(cd_truth, cd) + assert_array_almost_equal(dd_truth, dd) + assert_almost_equal(dt_requested, dt) + + def test_transferfunction(self): + numc = np.array([0.25, 0.25, 0.5]) + denc = np.array([0.75, 0.75, 1.0]) + + numd = np.array([[1.0 / 3.0, -0.427419169438754, 0.221654141101125]]) + dend = np.array([1.0, -1.351394049721225, 0.606530659712634]) + + dt_requested = 0.5 + + num, den, dt = c2d((numc, denc), dt_requested, method='zoh') + + assert_array_almost_equal(numd, num) + assert_array_almost_equal(dend, den) + assert_almost_equal(dt_requested, dt) + + def test_zerospolesgain(self): + zeros_c = np.array([0.5, -0.5]) + poles_c = np.array([1.j / np.sqrt(2), -1.j / np.sqrt(2)]) + k_c = 1.0 + + zeros_d = [1.23371727305860, 0.735356894461267] + polls_d = [0.938148335039729 + 0.346233593780536j, + 0.938148335039729 - 0.346233593780536j] + k_d = 1.0 + + dt_requested = 0.5 + + zeros, poles, k, dt = c2d((zeros_c, poles_c, k_c), dt_requested, + method='zoh') + + assert_array_almost_equal(zeros_d, zeros) + assert_array_almost_equal(polls_d, poles) + assert_almost_equal(k_d, k) + assert_almost_equal(dt_requested, dt) + + def test_gbt_with_sio_tf_and_zpk(self): + """Test method='gbt' with alpha=0.25 for tf and zpk cases.""" + # State space coefficients for the continuous SIO system. + A = -1.0 + B = 1.0 + C = 1.0 + D = 0.5 + + # The continuous transfer function coefficients. + cnum, cden = ss2tf(A, B, C, D) + + # Continuous zpk representation + cz, cp, ck = ss2zpk(A, B, C, D) + + h = 1.0 + alpha = 0.25 + + # Explicit formulas, in the scalar case. + Ad = (1 + (1 - alpha) * h * A) / (1 - alpha * h * A) + Bd = h * B / (1 - alpha * h * A) + Cd = C / (1 - alpha * h * A) + Dd = D + alpha * C * Bd + + # Convert the explicit solution to tf + dnum, dden = ss2tf(Ad, Bd, Cd, Dd) + + # Compute the discrete tf using cont2discrete. + c2dnum, c2dden, dt = c2d((cnum, cden), h, method='gbt', alpha=alpha) + + assert_allclose(dnum, c2dnum) + assert_allclose(dden, c2dden) + + # Convert explicit solution to zpk. + dz, dp, dk = ss2zpk(Ad, Bd, Cd, Dd) + + # Compute the discrete zpk using cont2discrete. + c2dz, c2dp, c2dk, dt = c2d((cz, cp, ck), h, method='gbt', alpha=alpha) + + assert_allclose(dz, c2dz) + assert_allclose(dp, c2dp) + assert_allclose(dk, c2dk) + + def test_discrete_approx(self): + """ + Test that the solution to the discrete approximation of a continuous + system actually approximates the solution to the continuous system. + This is an indirect test of the correctness of the implementation + of cont2discrete. + """ + + def u(t): + return np.sin(2.5 * t) + + a = np.array([[-0.01]]) + b = np.array([[1.0]]) + c = np.array([[1.0]]) + d = np.array([[0.2]]) + x0 = 1.0 + + t = np.linspace(0, 10.0, 101) + dt = t[1] - t[0] + u1 = u(t) + + # Use lsim to compute the solution to the continuous system. + t, yout, xout = lsim((a, b, c, d), T=t, U=u1, X0=x0) + + # Convert the continuous system to a discrete approximation. + dsys = c2d((a, b, c, d), dt, method='bilinear') + + # Use dlsim with the pairwise averaged input to compute the output + # of the discrete system. + u2 = 0.5 * (u1[:-1] + u1[1:]) + t2 = t[:-1] + td2, yd2, xd2 = dlsim(dsys, u=u2.reshape(-1, 1), t=t2, x0=x0) + + # ymid is the average of consecutive terms of the "exact" output + # computed by lsim2. This is what the discrete approximation + # actually approximates. + ymid = 0.5 * (yout[:-1] + yout[1:]) + + assert_allclose(yd2.ravel(), ymid, rtol=1e-4) + + def test_simo_tf(self): + # See gh-5753 + tf = ([[1, 0], [1, 1]], [1, 1]) + num, den, dt = c2d(tf, 0.01) + + assert_equal(dt, 0.01) # sanity check + assert_allclose(den, [1, -0.990404983], rtol=1e-3) + assert_allclose(num, [[1, -1], [1, -0.99004983]], rtol=1e-3) + + def test_multioutput(self): + ts = 0.01 # time step + + tf = ([[1, -3], [1, 5]], [1, 1]) + num, den, dt = c2d(tf, ts) + + tf1 = (tf[0][0], tf[1]) + num1, den1, dt1 = c2d(tf1, ts) + + tf2 = (tf[0][1], tf[1]) + num2, den2, dt2 = c2d(tf2, ts) + + # Sanity checks + assert_equal(dt, dt1) + assert_equal(dt, dt2) + + # Check that we get the same results + assert_allclose(num, np.vstack((num1, num2)), rtol=1e-13) + + # Single input, so the denominator should + # not be multidimensional like the numerator + assert_allclose(den, den1, rtol=1e-13) + assert_allclose(den, den2, rtol=1e-13) + +class TestC2dLti: + def test_c2d_ss(self): + # StateSpace + A = np.array([[-0.3, 0.1], [0.2, -0.7]]) + B = np.array([[0], [1]]) + C = np.array([[1, 0]]) + D = 0 + + A_res = np.array([[0.985136404135682, 0.004876671474795], + [0.009753342949590, 0.965629718236502]]) + B_res = np.array([[0.000122937599964], [0.049135527547844]]) + + sys_ssc = lti(A, B, C, D) + sys_ssd = sys_ssc.to_discrete(0.05) + + assert_allclose(sys_ssd.A, A_res) + assert_allclose(sys_ssd.B, B_res) + assert_allclose(sys_ssd.C, C) + assert_allclose(sys_ssd.D, D) + + def test_c2d_tf(self): + + sys = lti([0.5, 0.3], [1.0, 0.4]) + sys = sys.to_discrete(0.005) + + # Matlab results + num_res = np.array([0.5, -0.485149004980066]) + den_res = np.array([1.0, -0.980198673306755]) + + # Somehow a lot of numerical errors + assert_allclose(sys.den, den_res, atol=0.02) + assert_allclose(sys.num, num_res, atol=0.02) + + +class TestC2dInvariants: + # Some test cases for checking the invariances. + # Array of triplets: (system, sample time, number of samples) + cases = [ + (tf2ss([1, 1], [1, 1.5, 1]), 0.25, 10), + (tf2ss([1, 2], [1, 1.5, 3, 1]), 0.5, 10), + (tf2ss(0.1, [1, 1, 2, 1]), 0.5, 10), + ] + + # Check that systems discretized with the impulse-invariant + # method really hold the invariant + @pytest.mark.parametrize("sys,sample_time,samples_number", cases) + def test_impulse_invariant(self, sys, sample_time, samples_number): + time = np.arange(samples_number) * sample_time + _, yout_cont = impulse(sys, T=time) + _, yout_disc = dimpulse(c2d(sys, sample_time, method='impulse'), + n=len(time)) + assert_allclose(sample_time * yout_cont.ravel(), yout_disc[0].ravel()) + + # Step invariant should hold for ZOH discretized systems + @pytest.mark.parametrize("sys,sample_time,samples_number", cases) + def test_step_invariant(self, sys, sample_time, samples_number): + time = np.arange(samples_number) * sample_time + _, yout_cont = step(sys, T=time) + _, yout_disc = dstep(c2d(sys, sample_time, method='zoh'), n=len(time)) + assert_allclose(yout_cont.ravel(), yout_disc[0].ravel()) + + # Linear invariant should hold for FOH discretized systems + @pytest.mark.parametrize("sys,sample_time,samples_number", cases) + def test_linear_invariant(self, sys, sample_time, samples_number): + time = np.arange(samples_number) * sample_time + _, yout_cont, _ = lsim(sys, T=time, U=time) + _, yout_disc, _ = dlsim(c2d(sys, sample_time, method='foh'), u=time) + assert_allclose(yout_cont.ravel(), yout_disc.ravel()) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_czt.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_czt.py new file mode 100644 index 0000000000000000000000000000000000000000..b4a3c0e37c83812f525f2565144fcf9b1d7eeffd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_czt.py @@ -0,0 +1,219 @@ +# This program is public domain +# Authors: Paul Kienzle, Nadav Horesh +''' +A unit test module for czt.py +''' +import pytest +from numpy.testing import assert_allclose +from scipy.fft import fft +from scipy.signal import (czt, zoom_fft, czt_points, CZT, ZoomFFT) +import numpy as np + + +def check_czt(x): + # Check that czt is the equivalent of normal fft + y = fft(x) + y1 = czt(x) + assert_allclose(y1, y, rtol=1e-13) + + # Check that interpolated czt is the equivalent of normal fft + y = fft(x, 100*len(x)) + y1 = czt(x, 100*len(x)) + assert_allclose(y1, y, rtol=1e-12) + + +def check_zoom_fft(x): + # Check that zoom_fft is the equivalent of normal fft + y = fft(x) + y1 = zoom_fft(x, [0, 2-2./len(y)], endpoint=True) + assert_allclose(y1, y, rtol=1e-11, atol=1e-14) + y1 = zoom_fft(x, [0, 2]) + assert_allclose(y1, y, rtol=1e-11, atol=1e-14) + + # Test fn scalar + y1 = zoom_fft(x, 2-2./len(y), endpoint=True) + assert_allclose(y1, y, rtol=1e-11, atol=1e-14) + y1 = zoom_fft(x, 2) + assert_allclose(y1, y, rtol=1e-11, atol=1e-14) + + # Check that zoom_fft with oversampling is equivalent to zero padding + over = 10 + yover = fft(x, over*len(x)) + y2 = zoom_fft(x, [0, 2-2./len(yover)], m=len(yover), endpoint=True) + assert_allclose(y2, yover, rtol=1e-12, atol=1e-10) + y2 = zoom_fft(x, [0, 2], m=len(yover)) + assert_allclose(y2, yover, rtol=1e-12, atol=1e-10) + + # Check that zoom_fft works on a subrange + w = np.linspace(0, 2-2./len(x), len(x)) + f1, f2 = w[3], w[6] + y3 = zoom_fft(x, [f1, f2], m=3*over+1, endpoint=True) + idx3 = slice(3*over, 6*over+1) + assert_allclose(y3, yover[idx3], rtol=1e-13) + + +def test_1D(): + # Test of 1D version of the transforms + + np.random.seed(0) # Deterministic randomness + + # Random signals + lengths = np.random.randint(8, 200, 20) + np.append(lengths, 1) + for length in lengths: + x = np.random.random(length) + check_zoom_fft(x) + check_czt(x) + + # Gauss + t = np.linspace(-2, 2, 128) + x = np.exp(-t**2/0.01) + check_zoom_fft(x) + + # Linear + x = [1, 2, 3, 4, 5, 6, 7] + check_zoom_fft(x) + + # Check near powers of two + check_zoom_fft(range(126-31)) + check_zoom_fft(range(127-31)) + check_zoom_fft(range(128-31)) + check_zoom_fft(range(129-31)) + check_zoom_fft(range(130-31)) + + # Check transform on n-D array input + x = np.reshape(np.arange(3*2*28), (3, 2, 28)) + y1 = zoom_fft(x, [0, 2-2./28]) + y2 = zoom_fft(x[2, 0, :], [0, 2-2./28]) + assert_allclose(y1[2, 0], y2, rtol=1e-13, atol=1e-12) + + y1 = zoom_fft(x, [0, 2], endpoint=False) + y2 = zoom_fft(x[2, 0, :], [0, 2], endpoint=False) + assert_allclose(y1[2, 0], y2, rtol=1e-13, atol=1e-12) + + # Random (not a test condition) + x = np.random.rand(101) + check_zoom_fft(x) + + # Spikes + t = np.linspace(0, 1, 128) + x = np.sin(2*np.pi*t*5)+np.sin(2*np.pi*t*13) + check_zoom_fft(x) + + # Sines + x = np.zeros(100, dtype=complex) + x[[1, 5, 21]] = 1 + check_zoom_fft(x) + + # Sines plus complex component + x += 1j*np.linspace(0, 0.5, x.shape[0]) + check_zoom_fft(x) + + +def test_large_prime_lengths(): + np.random.seed(0) # Deterministic randomness + for N in (101, 1009, 10007): + x = np.random.rand(N) + y = fft(x) + y1 = czt(x) + assert_allclose(y, y1, rtol=1e-12) + + +@pytest.mark.slow +def test_czt_vs_fft(): + np.random.seed(123) + random_lengths = np.random.exponential(100000, size=10).astype('int') + for n in random_lengths: + a = np.random.randn(n) + assert_allclose(czt(a), fft(a), rtol=1e-11) + + +def test_empty_input(): + with pytest.raises(ValueError, match='Invalid number of CZT'): + czt([]) + with pytest.raises(ValueError, match='Invalid number of CZT'): + zoom_fft([], 0.5) + + +def test_0_rank_input(): + with pytest.raises(IndexError, match='tuple index out of range'): + czt(5) + with pytest.raises(IndexError, match='tuple index out of range'): + zoom_fft(5, 0.5) + + +@pytest.mark.parametrize('impulse', ([0, 0, 1], [0, 0, 1, 0, 0], + np.concatenate((np.array([0, 0, 1]), + np.zeros(100))))) +@pytest.mark.parametrize('m', (1, 3, 5, 8, 101, 1021)) +@pytest.mark.parametrize('a', (1, 2, 0.5, 1.1)) +# Step that tests away from the unit circle, but not so far it explodes from +# numerical error +@pytest.mark.parametrize('w', (None, 0.98534 + 0.17055j)) +def test_czt_math(impulse, m, w, a): + # z-transform of an impulse is 1 everywhere + assert_allclose(czt(impulse[2:], m=m, w=w, a=a), + np.ones(m), rtol=1e-10) + + # z-transform of a delayed impulse is z**-1 + assert_allclose(czt(impulse[1:], m=m, w=w, a=a), + czt_points(m=m, w=w, a=a)**-1, rtol=1e-10) + + # z-transform of a 2-delayed impulse is z**-2 + assert_allclose(czt(impulse, m=m, w=w, a=a), + czt_points(m=m, w=w, a=a)**-2, rtol=1e-10) + + +def test_int_args(): + # Integer argument `a` was producing all 0s + assert_allclose(abs(czt([0, 1], m=10, a=2)), 0.5*np.ones(10), rtol=1e-15) + assert_allclose(czt_points(11, w=2), 1/(2**np.arange(11)), rtol=1e-30) + + +def test_czt_points(): + for N in (1, 2, 3, 8, 11, 100, 101, 10007): + assert_allclose(czt_points(N), np.exp(2j*np.pi*np.arange(N)/N), + rtol=1e-30) + + assert_allclose(czt_points(7, w=1), np.ones(7), rtol=1e-30) + assert_allclose(czt_points(11, w=2.), 1/(2**np.arange(11)), rtol=1e-30) + + func = CZT(12, m=11, w=2., a=1) + assert_allclose(func.points(), 1/(2**np.arange(11)), rtol=1e-30) + + +@pytest.mark.parametrize('cls, args', [(CZT, (100,)), (ZoomFFT, (100, 0.2))]) +def test_CZT_size_mismatch(cls, args): + # Data size doesn't match function's expected size + myfunc = cls(*args) + with pytest.raises(ValueError, match='CZT defined for'): + myfunc(np.arange(5)) + + +def test_invalid_range(): + with pytest.raises(ValueError, match='2-length sequence'): + ZoomFFT(100, [1, 2, 3]) + + +@pytest.mark.parametrize('m', [0, -11, 5.5, 4.0]) +def test_czt_points_errors(m): + # Invalid number of points + with pytest.raises(ValueError, match='Invalid number of CZT'): + czt_points(m) + + +@pytest.mark.parametrize('size', [0, -5, 3.5, 4.0]) +def test_nonsense_size(size): + # Numpy and Scipy fft() give ValueError for 0 output size, so we do, too + with pytest.raises(ValueError, match='Invalid number of CZT'): + CZT(size, 3) + with pytest.raises(ValueError, match='Invalid number of CZT'): + ZoomFFT(size, 0.2, 3) + with pytest.raises(ValueError, match='Invalid number of CZT'): + CZT(3, size) + with pytest.raises(ValueError, match='Invalid number of CZT'): + ZoomFFT(3, 0.2, size) + with pytest.raises(ValueError, match='Invalid number of CZT'): + czt([1, 2, 3], size) + with pytest.raises(ValueError, match='Invalid number of CZT'): + zoom_fft([1, 2, 3], 0.2, size) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_dltisys.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_dltisys.py new file mode 100644 index 0000000000000000000000000000000000000000..e4f01efc545247e65ff586fd4123d7be9792e172 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_dltisys.py @@ -0,0 +1,598 @@ +# Author: Jeffrey Armstrong +# April 4, 2011 + +import numpy as np +from numpy.testing import (assert_equal, + assert_array_almost_equal, assert_array_equal, + assert_allclose, assert_, assert_almost_equal, + suppress_warnings) +from pytest import raises as assert_raises +from scipy.signal import (dlsim, dstep, dimpulse, tf2zpk, lti, dlti, + StateSpace, TransferFunction, ZerosPolesGain, + dfreqresp, dbode, BadCoefficients) + + +class TestDLTI: + + def test_dlsim(self): + + a = np.asarray([[0.9, 0.1], [-0.2, 0.9]]) + b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]]) + c = np.asarray([[0.1, 0.3]]) + d = np.asarray([[0.0, -0.1, 0.0]]) + dt = 0.5 + + # Create an input matrix with inputs down the columns (3 cols) and its + # respective time input vector + u = np.hstack((np.linspace(0, 4.0, num=5)[:, np.newaxis], + np.full((5, 1), 0.01), + np.full((5, 1), -0.002))) + t_in = np.linspace(0, 2.0, num=5) + + # Define the known result + yout_truth = np.array([[-0.001, + -0.00073, + 0.039446, + 0.0915387, + 0.13195948]]).T + xout_truth = np.asarray([[0, 0], + [0.0012, 0.0005], + [0.40233, 0.00071], + [1.163368, -0.079327], + [2.2402985, -0.3035679]]) + + tout, yout, xout = dlsim((a, b, c, d, dt), u, t_in) + + assert_array_almost_equal(yout_truth, yout) + assert_array_almost_equal(xout_truth, xout) + assert_array_almost_equal(t_in, tout) + + # Make sure input with single-dimension doesn't raise error + dlsim((1, 2, 3), 4) + + # Interpolated control - inputs should have different time steps + # than the discrete model uses internally + u_sparse = u[[0, 4], :] + t_sparse = np.asarray([0.0, 2.0]) + + tout, yout, xout = dlsim((a, b, c, d, dt), u_sparse, t_sparse) + + assert_array_almost_equal(yout_truth, yout) + assert_array_almost_equal(xout_truth, xout) + assert_equal(len(tout), yout.shape[0]) + + # Transfer functions (assume dt = 0.5) + num = np.asarray([1.0, -0.1]) + den = np.asarray([0.3, 1.0, 0.2]) + yout_truth = np.array([[0.0, + 0.0, + 3.33333333333333, + -4.77777777777778, + 23.0370370370370]]).T + + # Assume use of the first column of the control input built earlier + tout, yout = dlsim((num, den, 0.5), u[:, 0], t_in) + + assert_array_almost_equal(yout, yout_truth) + assert_array_almost_equal(t_in, tout) + + # Retest the same with a 1-D input vector + uflat = np.asarray(u[:, 0]) + uflat = uflat.reshape((5,)) + tout, yout = dlsim((num, den, 0.5), uflat, t_in) + + assert_array_almost_equal(yout, yout_truth) + assert_array_almost_equal(t_in, tout) + + # zeros-poles-gain representation + zd = np.array([0.5, -0.5]) + pd = np.array([1.j / np.sqrt(2), -1.j / np.sqrt(2)]) + k = 1.0 + yout_truth = np.array([[0.0, 1.0, 2.0, 2.25, 2.5]]).T + + tout, yout = dlsim((zd, pd, k, 0.5), u[:, 0], t_in) + + assert_array_almost_equal(yout, yout_truth) + assert_array_almost_equal(t_in, tout) + + # Raise an error for continuous-time systems + system = lti([1], [1, 1]) + assert_raises(AttributeError, dlsim, system, u) + + def test_dstep(self): + + a = np.asarray([[0.9, 0.1], [-0.2, 0.9]]) + b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]]) + c = np.asarray([[0.1, 0.3]]) + d = np.asarray([[0.0, -0.1, 0.0]]) + dt = 0.5 + + # Because b.shape[1] == 3, dstep should result in a tuple of three + # result vectors + yout_step_truth = (np.asarray([0.0, 0.04, 0.052, 0.0404, 0.00956, + -0.036324, -0.093318, -0.15782348, + -0.226628324, -0.2969374948]), + np.asarray([-0.1, -0.075, -0.058, -0.04815, + -0.04453, -0.0461895, -0.0521812, + -0.061588875, -0.073549579, + -0.08727047595]), + np.asarray([0.0, -0.01, -0.013, -0.0101, -0.00239, + 0.009081, 0.0233295, 0.03945587, + 0.056657081, 0.0742343737])) + + tout, yout = dstep((a, b, c, d, dt), n=10) + + assert_equal(len(yout), 3) + + for i in range(0, len(yout)): + assert_equal(yout[i].shape[0], 10) + assert_array_almost_equal(yout[i].flatten(), yout_step_truth[i]) + + # Check that the other two inputs (tf, zpk) will work as well + tfin = ([1.0], [1.0, 1.0], 0.5) + yout_tfstep = np.asarray([0.0, 1.0, 0.0]) + tout, yout = dstep(tfin, n=3) + assert_equal(len(yout), 1) + assert_array_almost_equal(yout[0].flatten(), yout_tfstep) + + zpkin = tf2zpk(tfin[0], tfin[1]) + (0.5,) + tout, yout = dstep(zpkin, n=3) + assert_equal(len(yout), 1) + assert_array_almost_equal(yout[0].flatten(), yout_tfstep) + + # Raise an error for continuous-time systems + system = lti([1], [1, 1]) + assert_raises(AttributeError, dstep, system) + + def test_dimpulse(self): + + a = np.asarray([[0.9, 0.1], [-0.2, 0.9]]) + b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]]) + c = np.asarray([[0.1, 0.3]]) + d = np.asarray([[0.0, -0.1, 0.0]]) + dt = 0.5 + + # Because b.shape[1] == 3, dimpulse should result in a tuple of three + # result vectors + yout_imp_truth = (np.asarray([0.0, 0.04, 0.012, -0.0116, -0.03084, + -0.045884, -0.056994, -0.06450548, + -0.068804844, -0.0703091708]), + np.asarray([-0.1, 0.025, 0.017, 0.00985, 0.00362, + -0.0016595, -0.0059917, -0.009407675, + -0.011960704, -0.01372089695]), + np.asarray([0.0, -0.01, -0.003, 0.0029, 0.00771, + 0.011471, 0.0142485, 0.01612637, + 0.017201211, 0.0175772927])) + + tout, yout = dimpulse((a, b, c, d, dt), n=10) + + assert_equal(len(yout), 3) + + for i in range(0, len(yout)): + assert_equal(yout[i].shape[0], 10) + assert_array_almost_equal(yout[i].flatten(), yout_imp_truth[i]) + + # Check that the other two inputs (tf, zpk) will work as well + tfin = ([1.0], [1.0, 1.0], 0.5) + yout_tfimpulse = np.asarray([0.0, 1.0, -1.0]) + tout, yout = dimpulse(tfin, n=3) + assert_equal(len(yout), 1) + assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse) + + zpkin = tf2zpk(tfin[0], tfin[1]) + (0.5,) + tout, yout = dimpulse(zpkin, n=3) + assert_equal(len(yout), 1) + assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse) + + # Raise an error for continuous-time systems + system = lti([1], [1, 1]) + assert_raises(AttributeError, dimpulse, system) + + def test_dlsim_trivial(self): + a = np.array([[0.0]]) + b = np.array([[0.0]]) + c = np.array([[0.0]]) + d = np.array([[0.0]]) + n = 5 + u = np.zeros(n).reshape(-1, 1) + tout, yout, xout = dlsim((a, b, c, d, 1), u) + assert_array_equal(tout, np.arange(float(n))) + assert_array_equal(yout, np.zeros((n, 1))) + assert_array_equal(xout, np.zeros((n, 1))) + + def test_dlsim_simple1d(self): + a = np.array([[0.5]]) + b = np.array([[0.0]]) + c = np.array([[1.0]]) + d = np.array([[0.0]]) + n = 5 + u = np.zeros(n).reshape(-1, 1) + tout, yout, xout = dlsim((a, b, c, d, 1), u, x0=1) + assert_array_equal(tout, np.arange(float(n))) + expected = (0.5 ** np.arange(float(n))).reshape(-1, 1) + assert_array_equal(yout, expected) + assert_array_equal(xout, expected) + + def test_dlsim_simple2d(self): + lambda1 = 0.5 + lambda2 = 0.25 + a = np.array([[lambda1, 0.0], + [0.0, lambda2]]) + b = np.array([[0.0], + [0.0]]) + c = np.array([[1.0, 0.0], + [0.0, 1.0]]) + d = np.array([[0.0], + [0.0]]) + n = 5 + u = np.zeros(n).reshape(-1, 1) + tout, yout, xout = dlsim((a, b, c, d, 1), u, x0=1) + assert_array_equal(tout, np.arange(float(n))) + # The analytical solution: + expected = (np.array([lambda1, lambda2]) ** + np.arange(float(n)).reshape(-1, 1)) + assert_array_equal(yout, expected) + assert_array_equal(xout, expected) + + def test_more_step_and_impulse(self): + lambda1 = 0.5 + lambda2 = 0.75 + a = np.array([[lambda1, 0.0], + [0.0, lambda2]]) + b = np.array([[1.0, 0.0], + [0.0, 1.0]]) + c = np.array([[1.0, 1.0]]) + d = np.array([[0.0, 0.0]]) + + n = 10 + + # Check a step response. + ts, ys = dstep((a, b, c, d, 1), n=n) + + # Create the exact step response. + stp0 = (1.0 / (1 - lambda1)) * (1.0 - lambda1 ** np.arange(n)) + stp1 = (1.0 / (1 - lambda2)) * (1.0 - lambda2 ** np.arange(n)) + + assert_allclose(ys[0][:, 0], stp0) + assert_allclose(ys[1][:, 0], stp1) + + # Check an impulse response with an initial condition. + x0 = np.array([1.0, 1.0]) + ti, yi = dimpulse((a, b, c, d, 1), n=n, x0=x0) + + # Create the exact impulse response. + imp = (np.array([lambda1, lambda2]) ** + np.arange(-1, n + 1).reshape(-1, 1)) + imp[0, :] = 0.0 + # Analytical solution to impulse response + y0 = imp[:n, 0] + np.dot(imp[1:n + 1, :], x0) + y1 = imp[:n, 1] + np.dot(imp[1:n + 1, :], x0) + + assert_allclose(yi[0][:, 0], y0) + assert_allclose(yi[1][:, 0], y1) + + # Check that dt=0.1, n=3 gives 3 time values. + system = ([1.0], [1.0, -0.5], 0.1) + t, (y,) = dstep(system, n=3) + assert_allclose(t, [0, 0.1, 0.2]) + assert_array_equal(y.T, [[0, 1.0, 1.5]]) + t, (y,) = dimpulse(system, n=3) + assert_allclose(t, [0, 0.1, 0.2]) + assert_array_equal(y.T, [[0, 1, 0.5]]) + + +class TestDlti: + def test_dlti_instantiation(self): + # Test that lti can be instantiated. + + dt = 0.05 + # TransferFunction + s = dlti([1], [-1], dt=dt) + assert_(isinstance(s, TransferFunction)) + assert_(isinstance(s, dlti)) + assert_(not isinstance(s, lti)) + assert_equal(s.dt, dt) + + # ZerosPolesGain + s = dlti(np.array([]), np.array([-1]), 1, dt=dt) + assert_(isinstance(s, ZerosPolesGain)) + assert_(isinstance(s, dlti)) + assert_(not isinstance(s, lti)) + assert_equal(s.dt, dt) + + # StateSpace + s = dlti([1], [-1], 1, 3, dt=dt) + assert_(isinstance(s, StateSpace)) + assert_(isinstance(s, dlti)) + assert_(not isinstance(s, lti)) + assert_equal(s.dt, dt) + + # Number of inputs + assert_raises(ValueError, dlti, 1) + assert_raises(ValueError, dlti, 1, 1, 1, 1, 1) + + +class TestStateSpaceDisc: + def test_initialization(self): + # Check that all initializations work + dt = 0.05 + StateSpace(1, 1, 1, 1, dt=dt) + StateSpace([1], [2], [3], [4], dt=dt) + StateSpace(np.array([[1, 2], [3, 4]]), np.array([[1], [2]]), + np.array([[1, 0]]), np.array([[0]]), dt=dt) + StateSpace(1, 1, 1, 1, dt=True) + + def test_conversion(self): + # Check the conversion functions + s = StateSpace(1, 2, 3, 4, dt=0.05) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(StateSpace(s) is not s) + assert_(s.to_ss() is not s) + + def test_properties(self): + # Test setters/getters for cross class properties. + # This implicitly tests to_tf() and to_zpk() + + # Getters + s = StateSpace(1, 1, 1, 1, dt=0.05) + assert_equal(s.poles, [1]) + assert_equal(s.zeros, [0]) + + +class TestTransferFunction: + def test_initialization(self): + # Check that all initializations work + dt = 0.05 + TransferFunction(1, 1, dt=dt) + TransferFunction([1], [2], dt=dt) + TransferFunction(np.array([1]), np.array([2]), dt=dt) + TransferFunction(1, 1, dt=True) + + def test_conversion(self): + # Check the conversion functions + s = TransferFunction([1, 0], [1, -1], dt=0.05) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(TransferFunction(s) is not s) + assert_(s.to_tf() is not s) + + def test_properties(self): + # Test setters/getters for cross class properties. + # This implicitly tests to_ss() and to_zpk() + + # Getters + s = TransferFunction([1, 0], [1, -1], dt=0.05) + assert_equal(s.poles, [1]) + assert_equal(s.zeros, [0]) + + +class TestZerosPolesGain: + def test_initialization(self): + # Check that all initializations work + dt = 0.05 + ZerosPolesGain(1, 1, 1, dt=dt) + ZerosPolesGain([1], [2], 1, dt=dt) + ZerosPolesGain(np.array([1]), np.array([2]), 1, dt=dt) + ZerosPolesGain(1, 1, 1, dt=True) + + def test_conversion(self): + # Check the conversion functions + s = ZerosPolesGain(1, 2, 3, dt=0.05) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(ZerosPolesGain(s) is not s) + assert_(s.to_zpk() is not s) + + +class Test_dfreqresp: + + def test_manual(self): + # Test dfreqresp() real part calculation (manual sanity check). + # 1st order low-pass filter: H(z) = 1 / (z - 0.2), + system = TransferFunction(1, [1, -0.2], dt=0.1) + w = [0.1, 1, 10] + w, H = dfreqresp(system, w=w) + + # test real + expected_re = [1.2383, 0.4130, -0.7553] + assert_almost_equal(H.real, expected_re, decimal=4) + + # test imag + expected_im = [-0.1555, -1.0214, 0.3955] + assert_almost_equal(H.imag, expected_im, decimal=4) + + def test_auto(self): + # Test dfreqresp() real part calculation. + # 1st order low-pass filter: H(z) = 1 / (z - 0.2), + system = TransferFunction(1, [1, -0.2], dt=0.1) + w = [0.1, 1, 10, 100] + w, H = dfreqresp(system, w=w) + jw = np.exp(w * 1j) + y = np.polyval(system.num, jw) / np.polyval(system.den, jw) + + # test real + expected_re = y.real + assert_almost_equal(H.real, expected_re) + + # test imag + expected_im = y.imag + assert_almost_equal(H.imag, expected_im) + + def test_freq_range(self): + # Test that freqresp() finds a reasonable frequency range. + # 1st order low-pass filter: H(z) = 1 / (z - 0.2), + # Expected range is from 0.01 to 10. + system = TransferFunction(1, [1, -0.2], dt=0.1) + n = 10 + expected_w = np.linspace(0, np.pi, 10, endpoint=False) + w, H = dfreqresp(system, n=n) + assert_almost_equal(w, expected_w) + + def test_pole_one(self): + # Test that freqresp() doesn't fail on a system with a pole at 0. + # integrator, pole at zero: H(s) = 1 / s + system = TransferFunction([1], [1, -1], dt=0.1) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, message="divide by zero") + sup.filter(RuntimeWarning, message="invalid value encountered") + w, H = dfreqresp(system, n=2) + assert_equal(w[0], 0.) # a fail would give not-a-number + + def test_error(self): + # Raise an error for continuous-time systems + system = lti([1], [1, 1]) + assert_raises(AttributeError, dfreqresp, system) + + def test_from_state_space(self): + # H(z) = 2 / z^3 - 0.5 * z^2 + + system_TF = dlti([2], [1, -0.5, 0, 0]) + + A = np.array([[0.5, 0, 0], + [1, 0, 0], + [0, 1, 0]]) + B = np.array([[1, 0, 0]]).T + C = np.array([[0, 0, 2]]) + D = 0 + + system_SS = dlti(A, B, C, D) + w = 10.0**np.arange(-3,0,.5) + with suppress_warnings() as sup: + sup.filter(BadCoefficients) + w1, H1 = dfreqresp(system_TF, w=w) + w2, H2 = dfreqresp(system_SS, w=w) + + assert_almost_equal(H1, H2) + + def test_from_zpk(self): + # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2), + system_ZPK = dlti([],[0.2],0.3) + system_TF = dlti(0.3, [1, -0.2]) + w = [0.1, 1, 10, 100] + w1, H1 = dfreqresp(system_ZPK, w=w) + w2, H2 = dfreqresp(system_TF, w=w) + assert_almost_equal(H1, H2) + + +class Test_bode: + + def test_manual(self): + # Test bode() magnitude calculation (manual sanity check). + # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2), + dt = 0.1 + system = TransferFunction(0.3, [1, -0.2], dt=dt) + w = [0.1, 0.5, 1, np.pi] + w2, mag, phase = dbode(system, w=w) + + # Test mag + expected_mag = [-8.5329, -8.8396, -9.6162, -12.0412] + assert_almost_equal(mag, expected_mag, decimal=4) + + # Test phase + expected_phase = [-7.1575, -35.2814, -67.9809, -180.0000] + assert_almost_equal(phase, expected_phase, decimal=4) + + # Test frequency + assert_equal(np.array(w) / dt, w2) + + def test_auto(self): + # Test bode() magnitude calculation. + # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2), + system = TransferFunction(0.3, [1, -0.2], dt=0.1) + w = np.array([0.1, 0.5, 1, np.pi]) + w2, mag, phase = dbode(system, w=w) + jw = np.exp(w * 1j) + y = np.polyval(system.num, jw) / np.polyval(system.den, jw) + + # Test mag + expected_mag = 20.0 * np.log10(abs(y)) + assert_almost_equal(mag, expected_mag) + + # Test phase + expected_phase = np.rad2deg(np.angle(y)) + assert_almost_equal(phase, expected_phase) + + def test_range(self): + # Test that bode() finds a reasonable frequency range. + # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2), + dt = 0.1 + system = TransferFunction(0.3, [1, -0.2], dt=0.1) + n = 10 + # Expected range is from 0.01 to 10. + expected_w = np.linspace(0, np.pi, n, endpoint=False) / dt + w, mag, phase = dbode(system, n=n) + assert_almost_equal(w, expected_w) + + def test_pole_one(self): + # Test that freqresp() doesn't fail on a system with a pole at 0. + # integrator, pole at zero: H(s) = 1 / s + system = TransferFunction([1], [1, -1], dt=0.1) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, message="divide by zero") + sup.filter(RuntimeWarning, message="invalid value encountered") + w, mag, phase = dbode(system, n=2) + assert_equal(w[0], 0.) # a fail would give not-a-number + + def test_imaginary(self): + # bode() should not fail on a system with pure imaginary poles. + # The test passes if bode doesn't raise an exception. + system = TransferFunction([1], [1, 0, 100], dt=0.1) + dbode(system, n=2) + + def test_error(self): + # Raise an error for continuous-time systems + system = lti([1], [1, 1]) + assert_raises(AttributeError, dbode, system) + + +class TestTransferFunctionZConversion: + """Test private conversions between 'z' and 'z**-1' polynomials.""" + + def test_full(self): + # Numerator and denominator same order + num = [2, 3, 4] + den = [5, 6, 7] + num2, den2 = TransferFunction._z_to_zinv(num, den) + assert_equal(num, num2) + assert_equal(den, den2) + + num2, den2 = TransferFunction._zinv_to_z(num, den) + assert_equal(num, num2) + assert_equal(den, den2) + + def test_numerator(self): + # Numerator lower order than denominator + num = [2, 3] + den = [5, 6, 7] + num2, den2 = TransferFunction._z_to_zinv(num, den) + assert_equal([0, 2, 3], num2) + assert_equal(den, den2) + + num2, den2 = TransferFunction._zinv_to_z(num, den) + assert_equal([2, 3, 0], num2) + assert_equal(den, den2) + + def test_denominator(self): + # Numerator higher order than denominator + num = [2, 3, 4] + den = [5, 6] + num2, den2 = TransferFunction._z_to_zinv(num, den) + assert_equal(num, num2) + assert_equal([0, 5, 6], den2) + + num2, den2 = TransferFunction._zinv_to_z(num, den) + assert_equal(num, num2) + assert_equal([5, 6, 0], den2) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..37c6110c21292e2a79bd8c677337e89e36844bc3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_filter_design.py @@ -0,0 +1,4398 @@ +import warnings + +from scipy._lib import _pep440 +import numpy as np +from numpy.testing import (assert_array_almost_equal, + assert_array_equal, assert_array_less, + assert_equal, assert_, + assert_allclose, assert_warns, suppress_warnings) +import pytest +from pytest import raises as assert_raises + +from numpy import array, spacing, sin, pi, sort, sqrt +from scipy.signal import (argrelextrema, BadCoefficients, bessel, besselap, bilinear, + buttap, butter, buttord, cheb1ap, cheb1ord, cheb2ap, + cheb2ord, cheby1, cheby2, ellip, ellipap, ellipord, + firwin, freqs_zpk, freqs, freqz, freqz_zpk, + gammatone, group_delay, iircomb, iirdesign, iirfilter, + iirnotch, iirpeak, lp2bp, lp2bs, lp2hp, lp2lp, normalize, + medfilt, order_filter, + sos2tf, sos2zpk, sosfreqz, tf2sos, tf2zpk, zpk2sos, + zpk2tf, bilinear_zpk, lp2lp_zpk, lp2hp_zpk, lp2bp_zpk, + lp2bs_zpk) +from scipy.signal._filter_design import (_cplxreal, _cplxpair, _norm_factor, + _bessel_poly, _bessel_zeros) + +try: + import mpmath +except ImportError: + mpmath = None + + +def mpmath_check(min_ver): + return pytest.mark.skipif( + mpmath is None + or _pep440.parse(mpmath.__version__) < _pep440.Version(min_ver), + reason=f"mpmath version >= {min_ver} required", + ) + + +class TestCplxPair: + + def test_trivial_input(self): + assert_equal(_cplxpair([]).size, 0) + assert_equal(_cplxpair(1), 1) + + def test_output_order(self): + assert_allclose(_cplxpair([1+1j, 1-1j]), [1-1j, 1+1j]) + + a = [1+1j, 1+1j, 1, 1-1j, 1-1j, 2] + b = [1-1j, 1+1j, 1-1j, 1+1j, 1, 2] + assert_allclose(_cplxpair(a), b) + + # points spaced around the unit circle + z = np.exp(2j*pi*array([4, 3, 5, 2, 6, 1, 0])/7) + z1 = np.copy(z) + np.random.shuffle(z) + assert_allclose(_cplxpair(z), z1) + np.random.shuffle(z) + assert_allclose(_cplxpair(z), z1) + np.random.shuffle(z) + assert_allclose(_cplxpair(z), z1) + + # Should be able to pair up all the conjugates + x = np.random.rand(10000) + 1j * np.random.rand(10000) + y = x.conj() + z = np.random.rand(10000) + x = np.concatenate((x, y, z)) + np.random.shuffle(x) + c = _cplxpair(x) + + # Every other element of head should be conjugates: + assert_allclose(c[0:20000:2], np.conj(c[1:20000:2])) + # Real parts of head should be in sorted order: + assert_allclose(c[0:20000:2].real, np.sort(c[0:20000:2].real)) + # Tail should be sorted real numbers: + assert_allclose(c[20000:], np.sort(c[20000:])) + + def test_real_integer_input(self): + assert_array_equal(_cplxpair([2, 0, 1]), [0, 1, 2]) + + def test_tolerances(self): + eps = spacing(1) + assert_allclose(_cplxpair([1j, -1j, 1+1j*eps], tol=2*eps), + [-1j, 1j, 1+1j*eps]) + + # sorting close to 0 + assert_allclose(_cplxpair([-eps+1j, +eps-1j]), [-1j, +1j]) + assert_allclose(_cplxpair([+eps+1j, -eps-1j]), [-1j, +1j]) + assert_allclose(_cplxpair([+1j, -1j]), [-1j, +1j]) + + def test_unmatched_conjugates(self): + # 1+2j is unmatched + assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+2j]) + + # 1+2j and 1-3j are unmatched + assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+2j, 1-3j]) + + # 1+3j is unmatched + assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+3j]) + + # Not conjugates + assert_raises(ValueError, _cplxpair, [4+5j, 4+5j]) + assert_raises(ValueError, _cplxpair, [1-7j, 1-7j]) + + # No pairs + assert_raises(ValueError, _cplxpair, [1+3j]) + assert_raises(ValueError, _cplxpair, [1-3j]) + + +class TestCplxReal: + + def test_trivial_input(self): + assert_equal(_cplxreal([]), ([], [])) + assert_equal(_cplxreal(1), ([], [1])) + + def test_output_order(self): + zc, zr = _cplxreal(np.roots(array([1, 0, 0, 1]))) + assert_allclose(np.append(zc, zr), [1/2 + 1j*sin(pi/3), -1]) + + eps = spacing(1) + + a = [0+1j, 0-1j, eps + 1j, eps - 1j, -eps + 1j, -eps - 1j, + 1, 4, 2, 3, 0, 0, + 2+3j, 2-3j, + 1-eps + 1j, 1+2j, 1-2j, 1+eps - 1j, # sorts out of order + 3+1j, 3+1j, 3+1j, 3-1j, 3-1j, 3-1j, + 2-3j, 2+3j] + zc, zr = _cplxreal(a) + assert_allclose(zc, [1j, 1j, 1j, 1+1j, 1+2j, 2+3j, 2+3j, 3+1j, 3+1j, + 3+1j]) + assert_allclose(zr, [0, 0, 1, 2, 3, 4]) + + z = array([1-eps + 1j, 1+2j, 1-2j, 1+eps - 1j, 1+eps+3j, 1-2*eps-3j, + 0+1j, 0-1j, 2+4j, 2-4j, 2+3j, 2-3j, 3+7j, 3-7j, 4-eps+1j, + 4+eps-2j, 4-1j, 4-eps+2j]) + + zc, zr = _cplxreal(z) + assert_allclose(zc, [1j, 1+1j, 1+2j, 1+3j, 2+3j, 2+4j, 3+7j, 4+1j, + 4+2j]) + assert_equal(zr, []) + + def test_unmatched_conjugates(self): + # 1+2j is unmatched + assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+2j]) + + # 1+2j and 1-3j are unmatched + assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+2j, 1-3j]) + + # 1+3j is unmatched + assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+3j]) + + # No pairs + assert_raises(ValueError, _cplxreal, [1+3j]) + assert_raises(ValueError, _cplxreal, [1-3j]) + + def test_real_integer_input(self): + zc, zr = _cplxreal([2, 0, 1, 4]) + assert_array_equal(zc, []) + assert_array_equal(zr, [0, 1, 2, 4]) + + +class TestTf2zpk: + + @pytest.mark.parametrize('dt', (np.float64, np.complex128)) + def test_simple(self, dt): + z_r = np.array([0.5, -0.5]) + p_r = np.array([1.j / np.sqrt(2), -1.j / np.sqrt(2)]) + # Sort the zeros/poles so that we don't fail the test if the order + # changes + z_r.sort() + p_r.sort() + b = np.poly(z_r).astype(dt) + a = np.poly(p_r).astype(dt) + + z, p, k = tf2zpk(b, a) + z.sort() + # The real part of `p` is ~0.0, so sort by imaginary part + p = p[np.argsort(p.imag)] + + assert_array_almost_equal(z, z_r) + assert_array_almost_equal(p, p_r) + assert_array_almost_equal(k, 1.) + assert k.dtype == dt + + def test_bad_filter(self): + # Regression test for #651: better handling of badly conditioned + # filter coefficients. + with suppress_warnings(): + warnings.simplefilter("error", BadCoefficients) + assert_raises(BadCoefficients, tf2zpk, [1e-15], [1.0, 1.0]) + + +class TestZpk2Tf: + + def test_identity(self): + """Test the identity transfer function.""" + z = [] + p = [] + k = 1. + b, a = zpk2tf(z, p, k) + b_r = np.array([1.]) # desired result + a_r = np.array([1.]) # desired result + # The test for the *type* of the return values is a regression + # test for ticket #1095. In the case p=[], zpk2tf used to + # return the scalar 1.0 instead of array([1.0]). + assert_array_equal(b, b_r) + assert_(isinstance(b, np.ndarray)) + assert_array_equal(a, a_r) + assert_(isinstance(a, np.ndarray)) + + +class TestSos2Zpk: + + def test_basic(self): + sos = [[1, 0, 1, 1, 0, -0.81], + [1, 0, 0, 1, 0, +0.49]] + z, p, k = sos2zpk(sos) + z2 = [1j, -1j, 0, 0] + p2 = [0.9, -0.9, 0.7j, -0.7j] + k2 = 1 + assert_array_almost_equal(sort(z), sort(z2), decimal=4) + assert_array_almost_equal(sort(p), sort(p2), decimal=4) + assert_array_almost_equal(k, k2) + + sos = [[1.00000, +0.61803, 1.0000, 1.00000, +0.60515, 0.95873], + [1.00000, -1.61803, 1.0000, 1.00000, -1.58430, 0.95873], + [1.00000, +1.00000, 0.0000, 1.00000, +0.97915, 0.00000]] + z, p, k = sos2zpk(sos) + z2 = [-0.3090 + 0.9511j, -0.3090 - 0.9511j, 0.8090 + 0.5878j, + 0.8090 - 0.5878j, -1.0000 + 0.0000j, 0] + p2 = [-0.3026 + 0.9312j, -0.3026 - 0.9312j, 0.7922 + 0.5755j, + 0.7922 - 0.5755j, -0.9791 + 0.0000j, 0] + k2 = 1 + assert_array_almost_equal(sort(z), sort(z2), decimal=4) + assert_array_almost_equal(sort(p), sort(p2), decimal=4) + + sos = array([[1, 2, 3, 1, 0.2, 0.3], + [4, 5, 6, 1, 0.4, 0.5]]) + z = array([-1 - 1.41421356237310j, -1 + 1.41421356237310j, + -0.625 - 1.05326872164704j, -0.625 + 1.05326872164704j]) + p = array([-0.2 - 0.678232998312527j, -0.2 + 0.678232998312527j, + -0.1 - 0.538516480713450j, -0.1 + 0.538516480713450j]) + k = 4 + z2, p2, k2 = sos2zpk(sos) + assert_allclose(_cplxpair(z2), z) + assert_allclose(_cplxpair(p2), p) + assert_allclose(k2, k) + + def test_fewer_zeros(self): + """Test not the expected number of p/z (effectively at origin).""" + sos = butter(3, 0.1, output='sos') + z, p, k = sos2zpk(sos) + assert len(z) == 4 + assert len(p) == 4 + + sos = butter(12, [5., 30.], 'bandpass', fs=1200., analog=False, + output='sos') + with pytest.warns(BadCoefficients, match='Badly conditioned'): + z, p, k = sos2zpk(sos) + assert len(z) == 24 + assert len(p) == 24 + + +class TestSos2Tf: + + def test_basic(self): + sos = [[1, 1, 1, 1, 0, -1], + [-2, 3, 1, 1, 10, 1]] + b, a = sos2tf(sos) + assert_array_almost_equal(b, [-2, 1, 2, 4, 1]) + assert_array_almost_equal(a, [1, 10, 0, -10, -1]) + + +class TestTf2Sos: + + def test_basic(self): + num = [2, 16, 44, 56, 32] + den = [3, 3, -15, 18, -12] + sos = tf2sos(num, den) + sos2 = [[0.6667, 4.0000, 5.3333, 1.0000, +2.0000, -4.0000], + [1.0000, 2.0000, 2.0000, 1.0000, -1.0000, +1.0000]] + assert_array_almost_equal(sos, sos2, decimal=4) + + b = [1, -3, 11, -27, 18] + a = [16, 12, 2, -4, -1] + sos = tf2sos(b, a) + sos2 = [[0.0625, -0.1875, 0.1250, 1.0000, -0.2500, -0.1250], + [1.0000, +0.0000, 9.0000, 1.0000, +1.0000, +0.5000]] + # assert_array_almost_equal(sos, sos2, decimal=4) + + @pytest.mark.parametrize('b, a, analog, sos', + [([1], [1], False, [[1., 0., 0., 1., 0., 0.]]), + ([1], [1], True, [[0., 0., 1., 0., 0., 1.]]), + ([1], [1., 0., -1.01, 0, 0.01], False, + [[1., 0., 0., 1., 0., -0.01], + [1., 0., 0., 1., 0., -1]]), + ([1], [1., 0., -1.01, 0, 0.01], True, + [[0., 0., 1., 1., 0., -1], + [0., 0., 1., 1., 0., -0.01]])]) + def test_analog(self, b, a, analog, sos): + sos2 = tf2sos(b, a, analog=analog) + assert_array_almost_equal(sos, sos2, decimal=4) + + +class TestZpk2Sos: + + @pytest.mark.parametrize('dt', 'fdgFDG') + @pytest.mark.parametrize('pairing, analog', + [('nearest', False), + ('keep_odd', False), + ('minimal', False), + ('minimal', True)]) + def test_dtypes(self, dt, pairing, analog): + z = np.array([-1, -1]).astype(dt) + ct = dt.upper() # the poles have to be complex + p = np.array([0.57149 + 0.29360j, 0.57149 - 0.29360j]).astype(ct) + k = np.array(1).astype(dt) + sos = zpk2sos(z, p, k, pairing=pairing, analog=analog) + sos2 = [[1, 2, 1, 1, -1.14298, 0.41280]] # octave & MATLAB + assert_array_almost_equal(sos, sos2, decimal=4) + + def test_basic(self): + for pairing in ('nearest', 'keep_odd'): + # + # Cases that match octave + # + + z = [-1, -1] + p = [0.57149 + 0.29360j, 0.57149 - 0.29360j] + k = 1 + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[1, 2, 1, 1, -1.14298, 0.41280]] # octave & MATLAB + assert_array_almost_equal(sos, sos2, decimal=4) + + z = [1j, -1j] + p = [0.9, -0.9, 0.7j, -0.7j] + k = 1 + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[1, 0, 1, 1, 0, +0.49], + [1, 0, 0, 1, 0, -0.81]] # octave + # sos2 = [[0, 0, 1, 1, -0.9, 0], + # [1, 0, 1, 1, 0.9, 0]] # MATLAB + assert_array_almost_equal(sos, sos2, decimal=4) + + z = [] + p = [0.8, -0.5+0.25j, -0.5-0.25j] + k = 1. + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[1., 0., 0., 1., 1., 0.3125], + [1., 0., 0., 1., -0.8, 0.]] # octave, MATLAB fails + assert_array_almost_equal(sos, sos2, decimal=4) + + z = [1., 1., 0.9j, -0.9j] + p = [0.99+0.01j, 0.99-0.01j, 0.1+0.9j, 0.1-0.9j] + k = 1 + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[1, 0, 0.81, 1, -0.2, 0.82], + [1, -2, 1, 1, -1.98, 0.9802]] # octave + # sos2 = [[1, -2, 1, 1, -0.2, 0.82], + # [1, 0, 0.81, 1, -1.98, 0.9802]] # MATLAB + assert_array_almost_equal(sos, sos2, decimal=4) + + z = [0.9+0.1j, 0.9-0.1j, -0.9] + p = [0.75+0.25j, 0.75-0.25j, 0.9] + k = 1 + sos = zpk2sos(z, p, k, pairing=pairing) + if pairing == 'keep_odd': + sos2 = [[1, -1.8, 0.82, 1, -1.5, 0.625], + [1, 0.9, 0, 1, -0.9, 0]] # octave; MATLAB fails + assert_array_almost_equal(sos, sos2, decimal=4) + else: # pairing == 'nearest' + sos2 = [[1, 0.9, 0, 1, -1.5, 0.625], + [1, -1.8, 0.82, 1, -0.9, 0]] # our algorithm + assert_array_almost_equal(sos, sos2, decimal=4) + + # + # Cases that differ from octave: + # + + z = [-0.3090 + 0.9511j, -0.3090 - 0.9511j, 0.8090 + 0.5878j, + +0.8090 - 0.5878j, -1.0000 + 0.0000j] + p = [-0.3026 + 0.9312j, -0.3026 - 0.9312j, 0.7922 + 0.5755j, + +0.7922 - 0.5755j, -0.9791 + 0.0000j] + k = 1 + sos = zpk2sos(z, p, k, pairing=pairing) + # sos2 = [[1, 0.618, 1, 1, 0.6052, 0.95870], + # [1, -1.618, 1, 1, -1.5844, 0.95878], + # [1, 1, 0, 1, 0.9791, 0]] # octave, MATLAB fails + sos2 = [[1, 1, 0, 1, +0.97915, 0], + [1, 0.61803, 1, 1, +0.60515, 0.95873], + [1, -1.61803, 1, 1, -1.58430, 0.95873]] + assert_array_almost_equal(sos, sos2, decimal=4) + + z = [-1 - 1.4142j, -1 + 1.4142j, + -0.625 - 1.0533j, -0.625 + 1.0533j] + p = [-0.2 - 0.6782j, -0.2 + 0.6782j, + -0.1 - 0.5385j, -0.1 + 0.5385j] + k = 4 + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[4, 8, 12, 1, 0.2, 0.3], + [1, 1.25, 1.5, 1, 0.4, 0.5]] # MATLAB + # sos2 = [[4, 8, 12, 1, 0.4, 0.5], + # [1, 1.25, 1.5, 1, 0.2, 0.3]] # octave + assert_allclose(sos, sos2, rtol=1e-4, atol=1e-4) + + z = [] + p = [0.2, -0.5+0.25j, -0.5-0.25j] + k = 1. + sos = zpk2sos(z, p, k, pairing=pairing) + sos2 = [[1., 0., 0., 1., -0.2, 0.], + [1., 0., 0., 1., 1., 0.3125]] + # sos2 = [[1., 0., 0., 1., 1., 0.3125], + # [1., 0., 0., 1., -0.2, 0]] # octave, MATLAB fails + assert_array_almost_equal(sos, sos2, decimal=4) + + # The next two examples are adapted from Leland B. Jackson, + # "Digital Filters and Signal Processing (1995) p.400: + # http://books.google.com/books?id=VZ8uabI1pNMC&lpg=PA400&ots=gRD9pi8Jua&dq=Pole%2Fzero%20pairing%20for%20minimum%20roundoff%20noise%20in%20BSF.&pg=PA400#v=onepage&q=Pole%2Fzero%20pairing%20for%20minimum%20roundoff%20noise%20in%20BSF.&f=false + + deg2rad = np.pi / 180. + k = 1. + + # first example + thetas = [22.5, 45, 77.5] + mags = [0.8, 0.6, 0.9] + z = np.array([np.exp(theta * deg2rad * 1j) for theta in thetas]) + z = np.concatenate((z, np.conj(z))) + p = np.array([mag * np.exp(theta * deg2rad * 1j) + for theta, mag in zip(thetas, mags)]) + p = np.concatenate((p, np.conj(p))) + sos = zpk2sos(z, p, k) + # sos2 = [[1, -0.43288, 1, 1, -0.38959, 0.81], # octave, + # [1, -1.41421, 1, 1, -0.84853, 0.36], # MATLAB fails + # [1, -1.84776, 1, 1, -1.47821, 0.64]] + # Note that pole-zero pairing matches, but ordering is different + sos2 = [[1, -1.41421, 1, 1, -0.84853, 0.36], + [1, -1.84776, 1, 1, -1.47821, 0.64], + [1, -0.43288, 1, 1, -0.38959, 0.81]] + assert_array_almost_equal(sos, sos2, decimal=4) + + # second example + z = np.array([np.exp(theta * deg2rad * 1j) + for theta in (85., 10.)]) + z = np.concatenate((z, np.conj(z), [1, -1])) + sos = zpk2sos(z, p, k) + + # sos2 = [[1, -0.17431, 1, 1, -0.38959, 0.81], # octave "wrong", + # [1, -1.96962, 1, 1, -0.84853, 0.36], # MATLAB fails + # [1, 0, -1, 1, -1.47821, 0.64000]] + # Our pole-zero pairing matches the text, Octave does not + sos2 = [[1, 0, -1, 1, -0.84853, 0.36], + [1, -1.96962, 1, 1, -1.47821, 0.64], + [1, -0.17431, 1, 1, -0.38959, 0.81]] + assert_array_almost_equal(sos, sos2, decimal=4) + + # these examples are taken from the doc string, and show the + # effect of the 'pairing' argument + @pytest.mark.parametrize('pairing, sos', + [('nearest', + np.array([[1., 1., 0.5, 1., -0.75, 0.], + [1., 1., 0., 1., -1.6, 0.65]])), + ('keep_odd', + np.array([[1., 1., 0, 1., -0.75, 0.], + [1., 1., 0.5, 1., -1.6, 0.65]])), + ('minimal', + np.array([[0., 1., 1., 0., 1., -0.75], + [1., 1., 0.5, 1., -1.6, 0.65]]))]) + def test_pairing(self, pairing, sos): + z1 = np.array([-1, -0.5-0.5j, -0.5+0.5j]) + p1 = np.array([0.75, 0.8+0.1j, 0.8-0.1j]) + sos2 = zpk2sos(z1, p1, 1, pairing=pairing) + assert_array_almost_equal(sos, sos2, decimal=4) + + @pytest.mark.parametrize('p, sos_dt', + [([-1, 1, -0.1, 0.1], + [[0., 0., 1., 1., 0., -0.01], + [0., 0., 1., 1., 0., -1]]), + ([-0.7071+0.7071j, -0.7071-0.7071j, -0.1j, 0.1j], + [[0., 0., 1., 1., 0., 0.01], + [0., 0., 1., 1., 1.4142, 1.]])]) + def test_analog(self, p, sos_dt): + # test `analog` argument + # for discrete time, poles closest to unit circle should appear last + # for cont. time, poles closest to imaginary axis should appear last + sos2_dt = zpk2sos([], p, 1, pairing='minimal', analog=False) + sos2_ct = zpk2sos([], p, 1, pairing='minimal', analog=True) + assert_array_almost_equal(sos_dt, sos2_dt, decimal=4) + assert_array_almost_equal(sos_dt[::-1], sos2_ct, decimal=4) + + def test_bad_args(self): + with pytest.raises(ValueError, match=r'pairing must be one of'): + zpk2sos([1], [2], 1, pairing='no_such_pairing') + + with pytest.raises(ValueError, match=r'.*pairing must be "minimal"'): + zpk2sos([1], [2], 1, pairing='keep_odd', analog=True) + + with pytest.raises(ValueError, + match=r'.*must have len\(p\)>=len\(z\)'): + zpk2sos([1, 1], [2], 1, analog=True) + + with pytest.raises(ValueError, match=r'k must be real'): + zpk2sos([1], [2], k=1j) + + +class TestFreqs: + + def test_basic(self): + _, h = freqs([1.0], [1.0], worN=8) + assert_array_almost_equal(h, np.ones(8)) + + def test_output(self): + # 1st order low-pass filter: H(s) = 1 / (s + 1) + w = [0.1, 1, 10, 100] + num = [1] + den = [1, 1] + w, H = freqs(num, den, worN=w) + s = w * 1j + expected = 1 / (s + 1) + assert_array_almost_equal(H.real, expected.real) + assert_array_almost_equal(H.imag, expected.imag) + + def test_freq_range(self): + # Test that freqresp() finds a reasonable frequency range. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + # Expected range is from 0.01 to 10. + num = [1] + den = [1, 1] + n = 10 + expected_w = np.logspace(-2, 1, n) + w, H = freqs(num, den, worN=n) + assert_array_almost_equal(w, expected_w) + + def test_plot(self): + + def plot(w, h): + assert_array_almost_equal(h, np.ones(8)) + + assert_raises(ZeroDivisionError, freqs, [1.0], [1.0], worN=8, + plot=lambda w, h: 1 / 0) + freqs([1.0], [1.0], worN=8, plot=plot) + + def test_backward_compat(self): + # For backward compatibility, test if None act as a wrapper for default + w1, h1 = freqs([1.0], [1.0]) + w2, h2 = freqs([1.0], [1.0], None) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(h1, h2) + + def test_w_or_N_types(self): + # Measure at 8 equally-spaced points + for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + w, h = freqs([1.0], [1.0], worN=N) + assert_equal(len(w), 8) + assert_array_almost_equal(h, np.ones(8)) + + # Measure at frequency 8 rad/sec + for w in (8.0, 8.0+0j): + w_out, h = freqs([1.0], [1.0], worN=w) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(h, [1]) + + +class TestFreqs_zpk: + + def test_basic(self): + _, h = freqs_zpk([1.0], [1.0], [1.0], worN=8) + assert_array_almost_equal(h, np.ones(8)) + + def test_output(self): + # 1st order low-pass filter: H(s) = 1 / (s + 1) + w = [0.1, 1, 10, 100] + z = [] + p = [-1] + k = 1 + w, H = freqs_zpk(z, p, k, worN=w) + s = w * 1j + expected = 1 / (s + 1) + assert_array_almost_equal(H.real, expected.real) + assert_array_almost_equal(H.imag, expected.imag) + + def test_freq_range(self): + # Test that freqresp() finds a reasonable frequency range. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + # Expected range is from 0.01 to 10. + z = [] + p = [-1] + k = 1 + n = 10 + expected_w = np.logspace(-2, 1, n) + w, H = freqs_zpk(z, p, k, worN=n) + assert_array_almost_equal(w, expected_w) + + def test_vs_freqs(self): + b, a = cheby1(4, 5, 100, analog=True, output='ba') + z, p, k = cheby1(4, 5, 100, analog=True, output='zpk') + + w1, h1 = freqs(b, a) + w2, h2 = freqs_zpk(z, p, k) + assert_allclose(w1, w2) + assert_allclose(h1, h2, rtol=1e-6) + + def test_backward_compat(self): + # For backward compatibility, test if None act as a wrapper for default + w1, h1 = freqs_zpk([1.0], [1.0], [1.0]) + w2, h2 = freqs_zpk([1.0], [1.0], [1.0], None) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(h1, h2) + + def test_w_or_N_types(self): + # Measure at 8 equally-spaced points + for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + w, h = freqs_zpk([], [], 1, worN=N) + assert_equal(len(w), 8) + assert_array_almost_equal(h, np.ones(8)) + + # Measure at frequency 8 rad/sec + for w in (8.0, 8.0+0j): + w_out, h = freqs_zpk([], [], 1, worN=w) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(h, [1]) + + +class TestFreqz: + + def test_ticket1441(self): + """Regression test for ticket 1441.""" + # Because freqz previously used arange instead of linspace, + # when N was large, it would return one more point than + # requested. + N = 100000 + w, h = freqz([1.0], worN=N) + assert_equal(w.shape, (N,)) + + def test_basic(self): + w, h = freqz([1.0], worN=8) + assert_array_almost_equal(w, np.pi * np.arange(8) / 8.) + assert_array_almost_equal(h, np.ones(8)) + w, h = freqz([1.0], worN=9) + assert_array_almost_equal(w, np.pi * np.arange(9) / 9.) + assert_array_almost_equal(h, np.ones(9)) + + for a in [1, np.ones(2)]: + w, h = freqz(np.ones(2), a, worN=0) + assert_equal(w.shape, (0,)) + assert_equal(h.shape, (0,)) + assert_equal(h.dtype, np.dtype('complex128')) + + t = np.linspace(0, 1, 4, endpoint=False) + for b, a, h_whole in zip( + ([1., 0, 0, 0], np.sin(2 * np.pi * t)), + ([1., 0, 0, 0], [0.5, 0, 0, 0]), + ([1., 1., 1., 1.], [0, -4j, 0, 4j])): + w, h = freqz(b, a, worN=4, whole=True) + expected_w = np.linspace(0, 2 * np.pi, 4, endpoint=False) + assert_array_almost_equal(w, expected_w) + assert_array_almost_equal(h, h_whole) + # simultaneously check int-like support + w, h = freqz(b, a, worN=np.int32(4), whole=True) + assert_array_almost_equal(w, expected_w) + assert_array_almost_equal(h, h_whole) + w, h = freqz(b, a, worN=w, whole=True) + assert_array_almost_equal(w, expected_w) + assert_array_almost_equal(h, h_whole) + + def test_basic_whole(self): + w, h = freqz([1.0], worN=8, whole=True) + assert_array_almost_equal(w, 2 * np.pi * np.arange(8.0) / 8) + assert_array_almost_equal(h, np.ones(8)) + + def test_plot(self): + + def plot(w, h): + assert_array_almost_equal(w, np.pi * np.arange(8.0) / 8) + assert_array_almost_equal(h, np.ones(8)) + + assert_raises(ZeroDivisionError, freqz, [1.0], worN=8, + plot=lambda w, h: 1 / 0) + freqz([1.0], worN=8, plot=plot) + + def test_fft_wrapping(self): + # Some simple real FIR filters + bs = list() # filters + as_ = list() + hs_whole = list() + hs_half = list() + # 3 taps + t = np.linspace(0, 1, 3, endpoint=False) + bs.append(np.sin(2 * np.pi * t)) + as_.append(3.) + hs_whole.append([0, -0.5j, 0.5j]) + hs_half.append([0, np.sqrt(1./12.), -0.5j]) + # 4 taps + t = np.linspace(0, 1, 4, endpoint=False) + bs.append(np.sin(2 * np.pi * t)) + as_.append(0.5) + hs_whole.append([0, -4j, 0, 4j]) + hs_half.append([0, np.sqrt(8), -4j, -np.sqrt(8)]) + del t + for ii, b in enumerate(bs): + # whole + a = as_[ii] + expected_w = np.linspace(0, 2 * np.pi, len(b), endpoint=False) + w, h = freqz(b, a, worN=expected_w, whole=True) # polyval + err_msg = f'b = {b}, a={a}' + assert_array_almost_equal(w, expected_w, err_msg=err_msg) + assert_array_almost_equal(h, hs_whole[ii], err_msg=err_msg) + w, h = freqz(b, a, worN=len(b), whole=True) # FFT + assert_array_almost_equal(w, expected_w, err_msg=err_msg) + assert_array_almost_equal(h, hs_whole[ii], err_msg=err_msg) + # non-whole + expected_w = np.linspace(0, np.pi, len(b), endpoint=False) + w, h = freqz(b, a, worN=expected_w, whole=False) # polyval + assert_array_almost_equal(w, expected_w, err_msg=err_msg) + assert_array_almost_equal(h, hs_half[ii], err_msg=err_msg) + w, h = freqz(b, a, worN=len(b), whole=False) # FFT + assert_array_almost_equal(w, expected_w, err_msg=err_msg) + assert_array_almost_equal(h, hs_half[ii], err_msg=err_msg) + + # some random FIR filters (real + complex) + # assume polyval is accurate + rng = np.random.RandomState(0) + for ii in range(2, 10): # number of taps + b = rng.randn(ii) + for kk in range(2): + a = rng.randn(1) if kk == 0 else rng.randn(3) + for jj in range(2): + if jj == 1: + b = b + rng.randn(ii) * 1j + # whole + expected_w = np.linspace(0, 2 * np.pi, ii, endpoint=False) + w, expected_h = freqz(b, a, worN=expected_w, whole=True) + assert_array_almost_equal(w, expected_w) + w, h = freqz(b, a, worN=ii, whole=True) + assert_array_almost_equal(w, expected_w) + assert_array_almost_equal(h, expected_h) + # half + expected_w = np.linspace(0, np.pi, ii, endpoint=False) + w, expected_h = freqz(b, a, worN=expected_w, whole=False) + assert_array_almost_equal(w, expected_w) + w, h = freqz(b, a, worN=ii, whole=False) + assert_array_almost_equal(w, expected_w) + assert_array_almost_equal(h, expected_h) + + def test_broadcasting1(self): + # Test broadcasting with worN an integer or a 1-D array, + # b and a are n-dimensional arrays. + np.random.seed(123) + b = np.random.rand(3, 5, 1) + a = np.random.rand(2, 1) + for whole in [False, True]: + # Test with worN being integers (one fast for FFT and one not), + # a 1-D array, and an empty array. + for worN in [16, 17, np.linspace(0, 1, 10), np.array([])]: + w, h = freqz(b, a, worN=worN, whole=whole) + for k in range(b.shape[1]): + bk = b[:, k, 0] + ak = a[:, 0] + ww, hh = freqz(bk, ak, worN=worN, whole=whole) + assert_allclose(ww, w) + assert_allclose(hh, h[k]) + + def test_broadcasting2(self): + # Test broadcasting with worN an integer or a 1-D array, + # b is an n-dimensional array, and a is left at the default value. + np.random.seed(123) + b = np.random.rand(3, 5, 1) + for whole in [False, True]: + for worN in [16, 17, np.linspace(0, 1, 10)]: + w, h = freqz(b, worN=worN, whole=whole) + for k in range(b.shape[1]): + bk = b[:, k, 0] + ww, hh = freqz(bk, worN=worN, whole=whole) + assert_allclose(ww, w) + assert_allclose(hh, h[k]) + + def test_broadcasting3(self): + # Test broadcasting where b.shape[-1] is the same length + # as worN, and a is left at the default value. + np.random.seed(123) + N = 16 + b = np.random.rand(3, N) + for whole in [False, True]: + for worN in [N, np.linspace(0, 1, N)]: + w, h = freqz(b, worN=worN, whole=whole) + assert_equal(w.size, N) + for k in range(N): + bk = b[:, k] + ww, hh = freqz(bk, worN=w[k], whole=whole) + assert_allclose(ww, w[k]) + assert_allclose(hh, h[k]) + + def test_broadcasting4(self): + # Test broadcasting with worN a 2-D array. + np.random.seed(123) + b = np.random.rand(4, 2, 1, 1) + a = np.random.rand(5, 2, 1, 1) + for whole in [False, True]: + for worN in [np.random.rand(6, 7), np.empty((6, 0))]: + w, h = freqz(b, a, worN=worN, whole=whole) + assert_allclose(w, worN, rtol=1e-14) + assert_equal(h.shape, (2,) + worN.shape) + for k in range(2): + ww, hh = freqz(b[:, k, 0, 0], a[:, k, 0, 0], + worN=worN.ravel(), + whole=whole) + assert_allclose(ww, worN.ravel(), rtol=1e-14) + assert_allclose(hh, h[k, :, :].ravel()) + + def test_backward_compat(self): + # For backward compatibility, test if None act as a wrapper for default + w1, h1 = freqz([1.0], 1) + w2, h2 = freqz([1.0], 1, None) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(h1, h2) + + def test_fs_param(self): + fs = 900 + b = [0.039479155677484369, 0.11843746703245311, 0.11843746703245311, + 0.039479155677484369] + a = [1.0, -1.3199152021838287, 0.80341991081938424, + -0.16767146321568049] + + # N = None, whole=False + w1, h1 = freqz(b, a, fs=fs) + w2, h2 = freqz(b, a) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 512, endpoint=False)) + + # N = None, whole=True + w1, h1 = freqz(b, a, whole=True, fs=fs) + w2, h2 = freqz(b, a, whole=True) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs, 512, endpoint=False)) + + # N = 5, whole=False + w1, h1 = freqz(b, a, 5, fs=fs) + w2, h2 = freqz(b, a, 5) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 5, endpoint=False)) + + # N = 5, whole=True + w1, h1 = freqz(b, a, 5, whole=True, fs=fs) + w2, h2 = freqz(b, a, 5, whole=True) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs, 5, endpoint=False)) + + # w is an array_like + for w in ([123], (123,), np.array([123]), (50, 123, 230), + np.array([50, 123, 230])): + w1, h1 = freqz(b, a, w, fs=fs) + w2, h2 = freqz(b, a, 2*pi*np.array(w)/fs) + assert_allclose(h1, h2) + assert_allclose(w, w1) + + def test_w_or_N_types(self): + # Measure at 7 (polyval) or 8 (fft) equally-spaced points + for N in (7, np.int8(7), np.int16(7), np.int32(7), np.int64(7), + np.array(7), + 8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + + w, h = freqz([1.0], worN=N) + assert_array_almost_equal(w, np.pi * np.arange(N) / N) + assert_array_almost_equal(h, np.ones(N)) + + w, h = freqz([1.0], worN=N, fs=100) + assert_array_almost_equal(w, np.linspace(0, 50, N, endpoint=False)) + assert_array_almost_equal(h, np.ones(N)) + + # Measure at frequency 8 Hz + for w in (8.0, 8.0+0j): + # Only makes sense when fs is specified + w_out, h = freqz([1.0], worN=w, fs=100) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(h, [1]) + + def test_nyquist(self): + w, h = freqz([1.0], worN=8, include_nyquist=True) + assert_array_almost_equal(w, np.pi * np.arange(8) / 7.) + assert_array_almost_equal(h, np.ones(8)) + w, h = freqz([1.0], worN=9, include_nyquist=True) + assert_array_almost_equal(w, np.pi * np.arange(9) / 8.) + assert_array_almost_equal(h, np.ones(9)) + + for a in [1, np.ones(2)]: + w, h = freqz(np.ones(2), a, worN=0, include_nyquist=True) + assert_equal(w.shape, (0,)) + assert_equal(h.shape, (0,)) + assert_equal(h.dtype, np.dtype('complex128')) + + w1, h1 = freqz([1.0], worN=8, whole = True, include_nyquist=True) + w2, h2 = freqz([1.0], worN=8, whole = True, include_nyquist=False) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(h1, h2) + + # https://github.com/scipy/scipy/issues/17289 + # https://github.com/scipy/scipy/issues/15273 + @pytest.mark.parametrize('whole,nyquist,worN', + [(False, False, 32), + (False, True, 32), + (True, False, 32), + (True, True, 32), + (False, False, 257), + (False, True, 257), + (True, False, 257), + (True, True, 257)]) + def test_17289(self, whole, nyquist, worN): + d = [0, 1] + w, Drfft = freqz(d, worN=32, whole=whole, include_nyquist=nyquist) + _, Dpoly = freqz(d, worN=w) + assert_allclose(Drfft, Dpoly) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + freqz([1.0], fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none."): + freqz([1.0], fs=None) + + +class TestSOSFreqz: + + def test_sosfreqz_basic(self): + # Compare the results of freqz and sosfreqz for a low order + # Butterworth filter. + + N = 500 + + b, a = butter(4, 0.2) + sos = butter(4, 0.2, output='sos') + w, h = freqz(b, a, worN=N) + w2, h2 = sosfreqz(sos, worN=N) + assert_equal(w2, w) + assert_allclose(h2, h, rtol=1e-10, atol=1e-14) + + b, a = ellip(3, 1, 30, (0.2, 0.3), btype='bandpass') + sos = ellip(3, 1, 30, (0.2, 0.3), btype='bandpass', output='sos') + w, h = freqz(b, a, worN=N) + w2, h2 = sosfreqz(sos, worN=N) + assert_equal(w2, w) + assert_allclose(h2, h, rtol=1e-10, atol=1e-14) + # must have at least one section + assert_raises(ValueError, sosfreqz, sos[:0]) + + def test_sosfrez_design(self): + # Compare sosfreqz output against expected values for different + # filter types + + # from cheb2ord + N, Wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60) + sos = cheby2(N, 60, Wn, 'stop', output='sos') + w, h = sosfreqz(sos) + h = np.abs(h) + w /= np.pi + assert_allclose(20 * np.log10(h[w <= 0.1]), 0, atol=3.01) + assert_allclose(20 * np.log10(h[w >= 0.6]), 0., atol=3.01) + assert_allclose(h[(w >= 0.2) & (w <= 0.5)], 0., atol=1e-3) # <= -60 dB + + N, Wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 150) + sos = cheby2(N, 150, Wn, 'stop', output='sos') + w, h = sosfreqz(sos) + dB = 20*np.log10(np.abs(h)) + w /= np.pi + assert_allclose(dB[w <= 0.1], 0, atol=3.01) + assert_allclose(dB[w >= 0.6], 0., atol=3.01) + assert_array_less(dB[(w >= 0.2) & (w <= 0.5)], -149.9) + + # from cheb1ord + N, Wn = cheb1ord(0.2, 0.3, 3, 40) + sos = cheby1(N, 3, Wn, 'low', output='sos') + w, h = sosfreqz(sos) + h = np.abs(h) + w /= np.pi + assert_allclose(20 * np.log10(h[w <= 0.2]), 0, atol=3.01) + assert_allclose(h[w >= 0.3], 0., atol=1e-2) # <= -40 dB + + N, Wn = cheb1ord(0.2, 0.3, 1, 150) + sos = cheby1(N, 1, Wn, 'low', output='sos') + w, h = sosfreqz(sos) + dB = 20*np.log10(np.abs(h)) + w /= np.pi + assert_allclose(dB[w <= 0.2], 0, atol=1.01) + assert_array_less(dB[w >= 0.3], -149.9) + + # adapted from ellipord + N, Wn = ellipord(0.3, 0.2, 3, 60) + sos = ellip(N, 0.3, 60, Wn, 'high', output='sos') + w, h = sosfreqz(sos) + h = np.abs(h) + w /= np.pi + assert_allclose(20 * np.log10(h[w >= 0.3]), 0, atol=3.01) + assert_allclose(h[w <= 0.1], 0., atol=1.5e-3) # <= -60 dB (approx) + + # adapted from buttord + N, Wn = buttord([0.2, 0.5], [0.14, 0.6], 3, 40) + sos = butter(N, Wn, 'band', output='sos') + w, h = sosfreqz(sos) + h = np.abs(h) + w /= np.pi + assert_allclose(h[w <= 0.14], 0., atol=1e-2) # <= -40 dB + assert_allclose(h[w >= 0.6], 0., atol=1e-2) # <= -40 dB + assert_allclose(20 * np.log10(h[(w >= 0.2) & (w <= 0.5)]), + 0, atol=3.01) + + N, Wn = buttord([0.2, 0.5], [0.14, 0.6], 3, 100) + sos = butter(N, Wn, 'band', output='sos') + w, h = sosfreqz(sos) + dB = 20*np.log10(np.maximum(np.abs(h), 1e-10)) + w /= np.pi + assert_array_less(dB[(w > 0) & (w <= 0.14)], -99.9) + assert_array_less(dB[w >= 0.6], -99.9) + assert_allclose(dB[(w >= 0.2) & (w <= 0.5)], 0, atol=3.01) + + def test_sosfreqz_design_ellip(self): + N, Wn = ellipord(0.3, 0.1, 3, 60) + sos = ellip(N, 0.3, 60, Wn, 'high', output='sos') + w, h = sosfreqz(sos) + h = np.abs(h) + w /= np.pi + assert_allclose(20 * np.log10(h[w >= 0.3]), 0, atol=3.01) + assert_allclose(h[w <= 0.1], 0., atol=1.5e-3) # <= -60 dB (approx) + + N, Wn = ellipord(0.3, 0.2, .5, 150) + sos = ellip(N, .5, 150, Wn, 'high', output='sos') + w, h = sosfreqz(sos) + dB = 20*np.log10(np.maximum(np.abs(h), 1e-10)) + w /= np.pi + assert_allclose(dB[w >= 0.3], 0, atol=.55) + # Allow some numerical slop in the upper bound -150, so this is + # a check that dB[w <= 0.2] is less than or almost equal to -150. + assert dB[w <= 0.2].max() < -150*(1 - 1e-12) + + @mpmath_check("0.10") + def test_sos_freqz_against_mp(self): + # Compare the result of sosfreqz applied to a high order Butterworth + # filter against the result computed using mpmath. (signal.freqz fails + # miserably with such high order filters.) + from . import mpsig + N = 500 + order = 25 + Wn = 0.15 + with mpmath.workdps(80): + z_mp, p_mp, k_mp = mpsig.butter_lp(order, Wn) + w_mp, h_mp = mpsig.zpkfreqz(z_mp, p_mp, k_mp, N) + w_mp = np.array([float(x) for x in w_mp]) + h_mp = np.array([complex(x) for x in h_mp]) + + sos = butter(order, Wn, output='sos') + w, h = sosfreqz(sos, worN=N) + assert_allclose(w, w_mp, rtol=1e-12, atol=1e-14) + assert_allclose(h, h_mp, rtol=1e-12, atol=1e-14) + + def test_fs_param(self): + fs = 900 + sos = [[0.03934683014103762, 0.07869366028207524, 0.03934683014103762, + 1.0, -0.37256600288916636, 0.0], + [1.0, 1.0, 0.0, 1.0, -0.9495739996946778, 0.45125966317124144]] + + # N = None, whole=False + w1, h1 = sosfreqz(sos, fs=fs) + w2, h2 = sosfreqz(sos) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 512, endpoint=False)) + + # N = None, whole=True + w1, h1 = sosfreqz(sos, whole=True, fs=fs) + w2, h2 = sosfreqz(sos, whole=True) + assert_allclose(h1, h2, atol=1e-27) + assert_allclose(w1, np.linspace(0, fs, 512, endpoint=False)) + + # N = 5, whole=False + w1, h1 = sosfreqz(sos, 5, fs=fs) + w2, h2 = sosfreqz(sos, 5) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 5, endpoint=False)) + + # N = 5, whole=True + w1, h1 = sosfreqz(sos, 5, whole=True, fs=fs) + w2, h2 = sosfreqz(sos, 5, whole=True) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs, 5, endpoint=False)) + + # w is an array_like + for w in ([123], (123,), np.array([123]), (50, 123, 230), + np.array([50, 123, 230])): + w1, h1 = sosfreqz(sos, w, fs=fs) + w2, h2 = sosfreqz(sos, 2*pi*np.array(w)/fs) + assert_allclose(h1, h2) + assert_allclose(w, w1) + + def test_w_or_N_types(self): + # Measure at 7 (polyval) or 8 (fft) equally-spaced points + for N in (7, np.int8(7), np.int16(7), np.int32(7), np.int64(7), + np.array(7), + 8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + + w, h = sosfreqz([1, 0, 0, 1, 0, 0], worN=N) + assert_array_almost_equal(w, np.pi * np.arange(N) / N) + assert_array_almost_equal(h, np.ones(N)) + + w, h = sosfreqz([1, 0, 0, 1, 0, 0], worN=N, fs=100) + assert_array_almost_equal(w, np.linspace(0, 50, N, endpoint=False)) + assert_array_almost_equal(h, np.ones(N)) + + # Measure at frequency 8 Hz + for w in (8.0, 8.0+0j): + # Only makes sense when fs is specified + w_out, h = sosfreqz([1, 0, 0, 1, 0, 0], worN=w, fs=100) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(h, [1]) + + def test_fs_validation(self): + sos = butter(4, 0.2, output='sos') + with pytest.raises(ValueError, match="Sampling.*single scalar"): + sosfreqz(sos, fs=np.array([10, 20])) + + +class TestFreqz_zpk: + + def test_ticket1441(self): + """Regression test for ticket 1441.""" + # Because freqz previously used arange instead of linspace, + # when N was large, it would return one more point than + # requested. + N = 100000 + w, h = freqz_zpk([0.5], [0.5], 1.0, worN=N) + assert_equal(w.shape, (N,)) + + def test_basic(self): + w, h = freqz_zpk([0.5], [0.5], 1.0, worN=8) + assert_array_almost_equal(w, np.pi * np.arange(8.0) / 8) + assert_array_almost_equal(h, np.ones(8)) + + def test_basic_whole(self): + w, h = freqz_zpk([0.5], [0.5], 1.0, worN=8, whole=True) + assert_array_almost_equal(w, 2 * np.pi * np.arange(8.0) / 8) + assert_array_almost_equal(h, np.ones(8)) + + def test_vs_freqz(self): + b, a = cheby1(4, 5, 0.5, analog=False, output='ba') + z, p, k = cheby1(4, 5, 0.5, analog=False, output='zpk') + + w1, h1 = freqz(b, a) + w2, h2 = freqz_zpk(z, p, k) + assert_allclose(w1, w2) + assert_allclose(h1, h2, rtol=1e-6) + + def test_backward_compat(self): + # For backward compatibility, test if None act as a wrapper for default + w1, h1 = freqz_zpk([0.5], [0.5], 1.0) + w2, h2 = freqz_zpk([0.5], [0.5], 1.0, None) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(h1, h2) + + def test_fs_param(self): + fs = 900 + z = [-1, -1, -1] + p = [0.4747869998473389+0.4752230717749344j, 0.37256600288916636, + 0.4747869998473389-0.4752230717749344j] + k = 0.03934683014103762 + + # N = None, whole=False + w1, h1 = freqz_zpk(z, p, k, whole=False, fs=fs) + w2, h2 = freqz_zpk(z, p, k, whole=False) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 512, endpoint=False)) + + # N = None, whole=True + w1, h1 = freqz_zpk(z, p, k, whole=True, fs=fs) + w2, h2 = freqz_zpk(z, p, k, whole=True) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs, 512, endpoint=False)) + + # N = 5, whole=False + w1, h1 = freqz_zpk(z, p, k, 5, fs=fs) + w2, h2 = freqz_zpk(z, p, k, 5) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs/2, 5, endpoint=False)) + + # N = 5, whole=True + w1, h1 = freqz_zpk(z, p, k, 5, whole=True, fs=fs) + w2, h2 = freqz_zpk(z, p, k, 5, whole=True) + assert_allclose(h1, h2) + assert_allclose(w1, np.linspace(0, fs, 5, endpoint=False)) + + # w is an array_like + for w in ([123], (123,), np.array([123]), (50, 123, 230), + np.array([50, 123, 230])): + w1, h1 = freqz_zpk(z, p, k, w, fs=fs) + w2, h2 = freqz_zpk(z, p, k, 2*pi*np.array(w)/fs) + assert_allclose(h1, h2) + assert_allclose(w, w1) + + def test_w_or_N_types(self): + # Measure at 8 equally-spaced points + for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + + w, h = freqz_zpk([], [], 1, worN=N) + assert_array_almost_equal(w, np.pi * np.arange(8) / 8.) + assert_array_almost_equal(h, np.ones(8)) + + w, h = freqz_zpk([], [], 1, worN=N, fs=100) + assert_array_almost_equal(w, np.linspace(0, 50, 8, endpoint=False)) + assert_array_almost_equal(h, np.ones(8)) + + # Measure at frequency 8 Hz + for w in (8.0, 8.0+0j): + # Only makes sense when fs is specified + w_out, h = freqz_zpk([], [], 1, worN=w, fs=100) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(h, [1]) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + freqz_zpk([1.0], [1.0], [1.0], fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none."): + freqz_zpk([1.0], [1.0], [1.0], fs=None) + + +class TestNormalize: + + def test_allclose(self): + """Test for false positive on allclose in normalize() in + filter_design.py""" + # Test to make sure the allclose call within signal.normalize does not + # choose false positives. Then check against a known output from MATLAB + # to make sure the fix doesn't break anything. + + # These are the coefficients returned from + # `[b,a] = cheby1(8, 0.5, 0.048)' + # in MATLAB. There are at least 15 significant figures in each + # coefficient, so it makes sense to test for errors on the order of + # 1e-13 (this can always be relaxed if different platforms have + # different rounding errors) + b_matlab = np.array([2.150733144728282e-11, 1.720586515782626e-10, + 6.022052805239190e-10, 1.204410561047838e-09, + 1.505513201309798e-09, 1.204410561047838e-09, + 6.022052805239190e-10, 1.720586515782626e-10, + 2.150733144728282e-11]) + a_matlab = np.array([1.000000000000000e+00, -7.782402035027959e+00, + 2.654354569747454e+01, -5.182182531666387e+01, + 6.334127355102684e+01, -4.963358186631157e+01, + 2.434862182949389e+01, -6.836925348604676e+00, + 8.412934944449140e-01]) + + # This is the input to signal.normalize after passing through the + # equivalent steps in signal.iirfilter as was done for MATLAB + b_norm_in = np.array([1.5543135865293012e-06, 1.2434508692234413e-05, + 4.3520780422820447e-05, 8.7041560845640893e-05, + 1.0880195105705122e-04, 8.7041560845640975e-05, + 4.3520780422820447e-05, 1.2434508692234413e-05, + 1.5543135865293012e-06]) + a_norm_in = np.array([7.2269025909127173e+04, -5.6242661430467968e+05, + 1.9182761917308895e+06, -3.7451128364682454e+06, + 4.5776121393762771e+06, -3.5869706138592605e+06, + 1.7596511818472347e+06, -4.9409793515707983e+05, + 6.0799461347219651e+04]) + + b_output, a_output = normalize(b_norm_in, a_norm_in) + + # The test on b works for decimal=14 but the one for a does not. For + # the sake of consistency, both of these are decimal=13. If something + # breaks on another platform, it is probably fine to relax this lower. + assert_array_almost_equal(b_matlab, b_output, decimal=13) + assert_array_almost_equal(a_matlab, a_output, decimal=13) + + def test_errors(self): + """Test the error cases.""" + # all zero denominator + assert_raises(ValueError, normalize, [1, 2], 0) + + # denominator not 1 dimensional + assert_raises(ValueError, normalize, [1, 2], [[1]]) + + # numerator too many dimensions + assert_raises(ValueError, normalize, [[[1, 2]]], 1) + + +class TestLp2lp: + + def test_basic(self): + b = [1] + a = [1, np.sqrt(2), 1] + b_lp, a_lp = lp2lp(b, a, 0.38574256627112119) + assert_array_almost_equal(b_lp, [0.1488], decimal=4) + assert_array_almost_equal(a_lp, [1, 0.5455, 0.1488], decimal=4) + + +class TestLp2hp: + + def test_basic(self): + b = [0.25059432325190018] + a = [1, 0.59724041654134863, 0.92834805757524175, 0.25059432325190018] + b_hp, a_hp = lp2hp(b, a, 2*np.pi*5000) + assert_allclose(b_hp, [1, 0, 0, 0]) + assert_allclose(a_hp, [1, 1.1638e5, 2.3522e9, 1.2373e14], rtol=1e-4) + + +class TestLp2bp: + + def test_basic(self): + b = [1] + a = [1, 2, 2, 1] + b_bp, a_bp = lp2bp(b, a, 2*np.pi*4000, 2*np.pi*2000) + assert_allclose(b_bp, [1.9844e12, 0, 0, 0], rtol=1e-6) + assert_allclose(a_bp, [1, 2.5133e4, 2.2108e9, 3.3735e13, + 1.3965e18, 1.0028e22, 2.5202e26], rtol=1e-4) + + +class TestLp2bs: + + def test_basic(self): + b = [1] + a = [1, 1] + b_bs, a_bs = lp2bs(b, a, 0.41722257286366754, 0.18460575326152251) + assert_array_almost_equal(b_bs, [1, 0, 0.17407], decimal=5) + assert_array_almost_equal(a_bs, [1, 0.18461, 0.17407], decimal=5) + + +class TestBilinear: + + def test_basic(self): + b = [0.14879732743343033] + a = [1, 0.54552236880522209, 0.14879732743343033] + b_z, a_z = bilinear(b, a, 0.5) + assert_array_almost_equal(b_z, [0.087821, 0.17564, 0.087821], + decimal=5) + assert_array_almost_equal(a_z, [1, -1.0048, 0.35606], decimal=4) + + b = [1, 0, 0.17407467530697837] + a = [1, 0.18460575326152251, 0.17407467530697837] + b_z, a_z = bilinear(b, a, 0.5) + assert_array_almost_equal(b_z, [0.86413, -1.2158, 0.86413], + decimal=4) + assert_array_almost_equal(a_z, [1, -1.2158, 0.72826], + decimal=4) + + def test_fs_validation(self): + b = [0.14879732743343033] + a = [1, 0.54552236880522209, 0.14879732743343033] + with pytest.raises(ValueError, match="Sampling.*single scalar"): + bilinear(b, a, fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none"): + bilinear(b, a, fs=None) + + +class TestLp2lp_zpk: + + def test_basic(self): + z = [] + p = [(-1+1j)/np.sqrt(2), (-1-1j)/np.sqrt(2)] + k = 1 + z_lp, p_lp, k_lp = lp2lp_zpk(z, p, k, 5) + assert_array_equal(z_lp, []) + assert_allclose(sort(p_lp), sort(p)*5) + assert_allclose(k_lp, 25) + + # Pseudo-Chebyshev with both poles and zeros + z = [-2j, +2j] + p = [-0.75, -0.5-0.5j, -0.5+0.5j] + k = 3 + z_lp, p_lp, k_lp = lp2lp_zpk(z, p, k, 20) + assert_allclose(sort(z_lp), sort([-40j, +40j])) + assert_allclose(sort(p_lp), sort([-15, -10-10j, -10+10j])) + assert_allclose(k_lp, 60) + + def test_fs_validation(self): + z = [-2j, +2j] + p = [-0.75, -0.5 - 0.5j, -0.5 + 0.5j] + k = 3 + + with pytest.raises(ValueError, match="Sampling.*single scalar"): + bilinear_zpk(z, p, k, fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none"): + bilinear_zpk(z, p, k, fs=None) + + +class TestLp2hp_zpk: + + def test_basic(self): + z = [] + p = [(-1+1j)/np.sqrt(2), (-1-1j)/np.sqrt(2)] + k = 1 + + z_hp, p_hp, k_hp = lp2hp_zpk(z, p, k, 5) + assert_array_equal(z_hp, [0, 0]) + assert_allclose(sort(p_hp), sort(p)*5) + assert_allclose(k_hp, 1) + + z = [-2j, +2j] + p = [-0.75, -0.5-0.5j, -0.5+0.5j] + k = 3 + z_hp, p_hp, k_hp = lp2hp_zpk(z, p, k, 6) + assert_allclose(sort(z_hp), sort([-3j, 0, +3j])) + assert_allclose(sort(p_hp), sort([-8, -6-6j, -6+6j])) + assert_allclose(k_hp, 32) + + +class TestLp2bp_zpk: + + def test_basic(self): + z = [-2j, +2j] + p = [-0.75, -0.5-0.5j, -0.5+0.5j] + k = 3 + z_bp, p_bp, k_bp = lp2bp_zpk(z, p, k, 15, 8) + assert_allclose(sort(z_bp), sort([-25j, -9j, 0, +9j, +25j])) + assert_allclose(sort(p_bp), sort([-3 + 6j*sqrt(6), + -3 - 6j*sqrt(6), + +2j+sqrt(-8j-225)-2, + -2j+sqrt(+8j-225)-2, + +2j-sqrt(-8j-225)-2, + -2j-sqrt(+8j-225)-2, ])) + assert_allclose(k_bp, 24) + + +class TestLp2bs_zpk: + + def test_basic(self): + z = [-2j, +2j] + p = [-0.75, -0.5-0.5j, -0.5+0.5j] + k = 3 + + z_bs, p_bs, k_bs = lp2bs_zpk(z, p, k, 35, 12) + + assert_allclose(sort(z_bs), sort([+35j, -35j, + +3j+sqrt(1234)*1j, + -3j+sqrt(1234)*1j, + +3j-sqrt(1234)*1j, + -3j-sqrt(1234)*1j])) + assert_allclose(sort(p_bs), sort([+3j*sqrt(129) - 8, + -3j*sqrt(129) - 8, + (-6 + 6j) - sqrt(-1225 - 72j), + (-6 - 6j) - sqrt(-1225 + 72j), + (-6 + 6j) + sqrt(-1225 - 72j), + (-6 - 6j) + sqrt(-1225 + 72j), ])) + assert_allclose(k_bs, 32) + + +class TestBilinear_zpk: + + def test_basic(self): + z = [-2j, +2j] + p = [-0.75, -0.5-0.5j, -0.5+0.5j] + k = 3 + + z_d, p_d, k_d = bilinear_zpk(z, p, k, 10) + + assert_allclose(sort(z_d), sort([(20-2j)/(20+2j), (20+2j)/(20-2j), + -1])) + assert_allclose(sort(p_d), sort([77/83, + (1j/2 + 39/2) / (41/2 - 1j/2), + (39/2 - 1j/2) / (1j/2 + 41/2), ])) + assert_allclose(k_d, 9696/69803) + + +class TestPrototypeType: + + def test_output_type(self): + # Prototypes should consistently output arrays, not lists + # https://github.com/scipy/scipy/pull/441 + for func in (buttap, + besselap, + lambda N: cheb1ap(N, 1), + lambda N: cheb2ap(N, 20), + lambda N: ellipap(N, 1, 20)): + for N in range(7): + z, p, k = func(N) + assert_(isinstance(z, np.ndarray)) + assert_(isinstance(p, np.ndarray)) + + +def dB(x): + # Return magnitude in decibels, avoiding divide-by-zero warnings + # (and deal with some "not less-ordered" errors when -inf shows up) + return 20 * np.log10(np.maximum(np.abs(x), np.finfo(np.float64).tiny)) + + +class TestButtord: + + def test_lowpass(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + N, Wn = buttord(wp, ws, rp, rs, False) + b, a = butter(N, Wn, 'lowpass', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs) + + assert_equal(N, 16) + assert_allclose(Wn, 2.0002776782743284e-01, rtol=1e-15) + + def test_highpass(self): + wp = 0.3 + ws = 0.2 + rp = 3 + rs = 70 + N, Wn = buttord(wp, ws, rp, rs, False) + b, a = butter(N, Wn, 'highpass', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs) + + assert_equal(N, 18) + assert_allclose(Wn, 2.9996603079132672e-01, rtol=1e-15) + + def test_bandpass(self): + wp = [0.2, 0.5] + ws = [0.1, 0.6] + rp = 3 + rs = 80 + N, Wn = buttord(wp, ws, rp, rs, False) + b, a = butter(N, Wn, 'bandpass', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 18) + assert_allclose(Wn, [1.9998742411409134e-01, 5.0002139595676276e-01], + rtol=1e-15) + + def test_bandstop(self): + wp = [0.1, 0.6] + ws = [0.2, 0.5] + rp = 3 + rs = 90 + N, Wn = buttord(wp, ws, rp, rs, False) + b, a = butter(N, Wn, 'bandstop', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs) + + assert_equal(N, 20) + assert_allclose(Wn, [1.4759432329294042e-01, 5.9997365985276407e-01], + rtol=1e-6) + + def test_analog(self): + wp = 200 + ws = 600 + rp = 3 + rs = 60 + N, Wn = buttord(wp, ws, rp, rs, True) + b, a = butter(N, Wn, 'lowpass', True) + w, h = freqs(b, a) + assert_array_less(-rp, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs) + + assert_equal(N, 7) + assert_allclose(Wn, 2.0006785355671877e+02, rtol=1e-15) + + n, Wn = buttord(1, 550/450, 1, 26, analog=True) + assert_equal(n, 19) + assert_allclose(Wn, 1.0361980524629517, rtol=1e-15) + + assert_equal(buttord(1, 1.2, 1, 80, analog=True)[0], 55) + + def test_fs_param(self): + wp = [4410, 11025] + ws = [2205, 13230] + rp = 3 + rs = 80 + fs = 44100 + N, Wn = buttord(wp, ws, rp, rs, False, fs=fs) + b, a = butter(N, Wn, 'bandpass', False, fs=fs) + w, h = freqz(b, a, fs=fs) + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 18) + assert_allclose(Wn, [4409.722701715714, 11025.47178084662], + rtol=1e-15) + + def test_invalid_input(self): + with pytest.raises(ValueError) as exc_info: + buttord([20, 50], [14, 60], 3, 2) + assert "gpass should be smaller than gstop" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + buttord([20, 50], [14, 60], -1, 2) + assert "gpass should be larger than 0.0" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + buttord([20, 50], [14, 60], 1, -2) + assert "gstop should be larger than 0.0" in str(exc_info.value) + + def test_runtime_warnings(self): + msg = "Order is zero.*|divide by zero encountered" + with pytest.warns(RuntimeWarning, match=msg): + buttord(0.0, 1.0, 3, 60) + + def test_ellip_butter(self): + # The purpose of the test is to compare to some known output from past + # scipy versions. The values to compare to are generated with scipy + # 1.9.1 (there is nothing special about this particular version though) + n, wn = buttord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert n == 14 + + def test_fs_validation(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + + with pytest.raises(ValueError, match="Sampling.*single scalar"): + buttord(wp, ws, rp, rs, False, fs=np.array([10, 20])) + + +class TestCheb1ord: + + def test_lowpass(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + N, Wn = cheb1ord(wp, ws, rp, rs, False) + b, a = cheby1(N, rp, Wn, 'low', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs + 0.1) + + assert_equal(N, 8) + assert_allclose(Wn, 0.2, rtol=1e-15) + + def test_highpass(self): + wp = 0.3 + ws = 0.2 + rp = 3 + rs = 70 + N, Wn = cheb1ord(wp, ws, rp, rs, False) + b, a = cheby1(N, rp, Wn, 'high', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs + 0.1) + + assert_equal(N, 9) + assert_allclose(Wn, 0.3, rtol=1e-15) + + def test_bandpass(self): + wp = [0.2, 0.5] + ws = [0.1, 0.6] + rp = 3 + rs = 80 + N, Wn = cheb1ord(wp, ws, rp, rs, False) + b, a = cheby1(N, rp, Wn, 'band', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 9) + assert_allclose(Wn, [0.2, 0.5], rtol=1e-15) + + def test_bandstop(self): + wp = [0.1, 0.6] + ws = [0.2, 0.5] + rp = 3 + rs = 90 + N, Wn = cheb1ord(wp, ws, rp, rs, False) + b, a = cheby1(N, rp, Wn, 'stop', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs + 0.1) + + assert_equal(N, 10) + assert_allclose(Wn, [0.14758232569947785, 0.6], rtol=1e-5) + + def test_analog(self): + wp = 700 + ws = 100 + rp = 3 + rs = 70 + N, Wn = cheb1ord(wp, ws, rp, rs, True) + b, a = cheby1(N, rp, Wn, 'high', True) + w, h = freqs(b, a) + assert_array_less(-rp - 0.1, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs + 0.1) + + assert_equal(N, 4) + assert_allclose(Wn, 700, rtol=1e-15) + + assert_equal(cheb1ord(1, 1.2, 1, 80, analog=True)[0], 17) + + def test_fs_param(self): + wp = 4800 + ws = 7200 + rp = 3 + rs = 60 + fs = 48000 + N, Wn = cheb1ord(wp, ws, rp, rs, False, fs=fs) + b, a = cheby1(N, rp, Wn, 'low', False, fs=fs) + w, h = freqz(b, a, fs=fs) + assert_array_less(-rp - 0.1, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs + 0.1) + + assert_equal(N, 8) + assert_allclose(Wn, 4800, rtol=1e-15) + + def test_invalid_input(self): + with pytest.raises(ValueError) as exc_info: + cheb1ord(0.2, 0.3, 3, 2) + assert "gpass should be smaller than gstop" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + cheb1ord(0.2, 0.3, -1, 2) + assert "gpass should be larger than 0.0" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + cheb1ord(0.2, 0.3, 1, -2) + assert "gstop should be larger than 0.0" in str(exc_info.value) + + def test_ellip_cheb1(self): + # The purpose of the test is to compare to some known output from past + # scipy versions. The values to compare to are generated with scipy + # 1.9.1 (there is nothing special about this particular version though) + n, wn = cheb1ord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert n == 7 + + n2, w2 = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert not (wn == w2).all() + + def test_fs_validation(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + + with pytest.raises(ValueError, match="Sampling.*single scalar"): + cheb1ord(wp, ws, rp, rs, False, fs=np.array([10, 20])) + + +class TestCheb2ord: + + def test_lowpass(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + N, Wn = cheb2ord(wp, ws, rp, rs, False) + b, a = cheby2(N, rs, Wn, 'lp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs + 0.1) + + assert_equal(N, 8) + assert_allclose(Wn, 0.28647639976553163, rtol=1e-15) + + def test_highpass(self): + wp = 0.3 + ws = 0.2 + rp = 3 + rs = 70 + N, Wn = cheb2ord(wp, ws, rp, rs, False) + b, a = cheby2(N, rs, Wn, 'hp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs + 0.1) + + assert_equal(N, 9) + assert_allclose(Wn, 0.20697492182903282, rtol=1e-15) + + def test_bandpass(self): + wp = [0.2, 0.5] + ws = [0.1, 0.6] + rp = 3 + rs = 80 + N, Wn = cheb2ord(wp, ws, rp, rs, False) + b, a = cheby2(N, rs, Wn, 'bp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 9) + assert_allclose(Wn, [0.14876937565923479, 0.59748447842351482], + rtol=1e-15) + + def test_bandstop(self): + wp = [0.1, 0.6] + ws = [0.2, 0.5] + rp = 3 + rs = 90 + N, Wn = cheb2ord(wp, ws, rp, rs, False) + b, a = cheby2(N, rs, Wn, 'bs', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs + 0.1) + + assert_equal(N, 10) + assert_allclose(Wn, [0.19926249974781743, 0.50125246585567362], + rtol=1e-6) + + def test_analog(self): + wp = [20, 50] + ws = [10, 60] + rp = 3 + rs = 80 + N, Wn = cheb2ord(wp, ws, rp, rs, True) + b, a = cheby2(N, rs, Wn, 'bp', True) + w, h = freqs(b, a) + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 11) + assert_allclose(Wn, [1.673740595370124e+01, 5.974641487254268e+01], + rtol=1e-15) + + def test_fs_param(self): + wp = 150 + ws = 100 + rp = 3 + rs = 70 + fs = 1000 + N, Wn = cheb2ord(wp, ws, rp, rs, False, fs=fs) + b, a = cheby2(N, rs, Wn, 'hp', False, fs=fs) + w, h = freqz(b, a, fs=fs) + assert_array_less(-rp - 0.1, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs + 0.1) + + assert_equal(N, 9) + assert_allclose(Wn, 103.4874609145164, rtol=1e-15) + + def test_invalid_input(self): + with pytest.raises(ValueError) as exc_info: + cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 2) + assert "gpass should be smaller than gstop" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + cheb2ord([0.1, 0.6], [0.2, 0.5], -1, 2) + assert "gpass should be larger than 0.0" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + cheb2ord([0.1, 0.6], [0.2, 0.5], 1, -2) + assert "gstop should be larger than 0.0" in str(exc_info.value) + + def test_ellip_cheb2(self): + # The purpose of the test is to compare to some known output from past + # scipy versions. The values to compare to are generated with scipy + # 1.9.1 (there is nothing special about this particular version though) + n, wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert n == 7 + + n1, w1 = cheb1ord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert not (wn == w1).all() + + def test_fs_validation(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + + with pytest.raises(ValueError, match="Sampling.*single scalar"): + cheb2ord(wp, ws, rp, rs, False, fs=np.array([10, 20])) + + +class TestEllipord: + + def test_lowpass(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + N, Wn = ellipord(wp, ws, rp, rs, False) + b, a = ellip(N, rp, rs, Wn, 'lp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs + 0.1) + + assert_equal(N, 5) + assert_allclose(Wn, 0.2, rtol=1e-15) + + def test_lowpass_1000dB(self): + # failed when ellipkm1 wasn't used in ellipord and ellipap + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 1000 + N, Wn = ellipord(wp, ws, rp, rs, False) + sos = ellip(N, rp, rs, Wn, 'lp', False, output='sos') + w, h = sosfreqz(sos) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[w <= wp])) + assert_array_less(dB(h[ws <= w]), -rs + 0.1) + + def test_highpass(self): + wp = 0.3 + ws = 0.2 + rp = 3 + rs = 70 + N, Wn = ellipord(wp, ws, rp, rs, False) + b, a = ellip(N, rp, rs, Wn, 'hp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, dB(h[wp <= w])) + assert_array_less(dB(h[w <= ws]), -rs + 0.1) + + assert_equal(N, 6) + assert_allclose(Wn, 0.3, rtol=1e-15) + + def test_bandpass(self): + wp = [0.2, 0.5] + ws = [0.1, 0.6] + rp = 3 + rs = 80 + N, Wn = ellipord(wp, ws, rp, rs, False) + b, a = ellip(N, rp, rs, Wn, 'bp', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_and(wp[0] <= w, w <= wp[1])])) + assert_array_less(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]), + -rs + 0.1) + + assert_equal(N, 6) + assert_allclose(Wn, [0.2, 0.5], rtol=1e-15) + + def test_bandstop(self): + wp = [0.1, 0.6] + ws = [0.2, 0.5] + rp = 3 + rs = 90 + N, Wn = ellipord(wp, ws, rp, rs, False) + b, a = ellip(N, rp, rs, Wn, 'bs', False) + w, h = freqz(b, a) + w /= np.pi + assert_array_less(-rp - 0.1, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs + 0.1) + + assert_equal(N, 7) + assert_allclose(Wn, [0.14758232794342988, 0.6], rtol=1e-5) + + def test_analog(self): + wp = [1000, 6000] + ws = [2000, 5000] + rp = 3 + rs = 90 + N, Wn = ellipord(wp, ws, rp, rs, True) + b, a = ellip(N, rp, rs, Wn, 'bs', True) + w, h = freqs(b, a) + assert_array_less(-rp - 0.1, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs + 0.1) + + assert_equal(N, 8) + assert_allclose(Wn, [1666.6666, 6000]) + + assert_equal(ellipord(1, 1.2, 1, 80, analog=True)[0], 9) + + def test_fs_param(self): + wp = [400, 2400] + ws = [800, 2000] + rp = 3 + rs = 90 + fs = 8000 + N, Wn = ellipord(wp, ws, rp, rs, False, fs=fs) + b, a = ellip(N, rp, rs, Wn, 'bs', False, fs=fs) + w, h = freqz(b, a, fs=fs) + assert_array_less(-rp - 0.1, + dB(h[np.logical_or(w <= wp[0], wp[1] <= w)])) + assert_array_less(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]), + -rs + 0.1) + + assert_equal(N, 7) + assert_allclose(Wn, [590.3293117737195, 2400], rtol=1e-5) + + def test_invalid_input(self): + with pytest.raises(ValueError) as exc_info: + ellipord(0.2, 0.5, 3, 2) + assert "gpass should be smaller than gstop" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + ellipord(0.2, 0.5, -1, 2) + assert "gpass should be larger than 0.0" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + ellipord(0.2, 0.5, 1, -2) + assert "gstop should be larger than 0.0" in str(exc_info.value) + + def test_ellip_butter(self): + # The purpose of the test is to compare to some known output from past + # scipy versions. The values to compare to are generated with scipy + # 1.9.1 (there is nothing special about this particular version though) + n, wn = ellipord([0.1, 0.6], [0.2, 0.5], 3, 60) + assert n == 5 + + def test_fs_validation(self): + wp = 0.2 + ws = 0.3 + rp = 3 + rs = 60 + + with pytest.raises(ValueError, match="Sampling.*single scalar"): + ellipord(wp, ws, rp, rs, False, fs=np.array([10, 20])) + + +class TestBessel: + + def test_degenerate(self): + for norm in ('delay', 'phase', 'mag'): + # 0-order filter is just a passthrough + b, a = bessel(0, 1, analog=True, norm=norm) + assert_array_equal(b, [1]) + assert_array_equal(a, [1]) + + # 1-order filter is same for all types + b, a = bessel(1, 1, analog=True, norm=norm) + assert_allclose(b, [1], rtol=1e-15) + assert_allclose(a, [1, 1], rtol=1e-15) + + z, p, k = bessel(1, 0.3, analog=True, output='zpk', norm=norm) + assert_array_equal(z, []) + assert_allclose(p, [-0.3], rtol=1e-14) + assert_allclose(k, 0.3, rtol=1e-14) + + def test_high_order(self): + # high even order, 'phase' + z, p, k = bessel(24, 100, analog=True, output='zpk') + z2 = [] + p2 = [ + -9.055312334014323e+01 + 4.844005815403969e+00j, + -8.983105162681878e+01 + 1.454056170018573e+01j, + -8.837357994162065e+01 + 2.426335240122282e+01j, + -8.615278316179575e+01 + 3.403202098404543e+01j, + -8.312326467067703e+01 + 4.386985940217900e+01j, + -7.921695461084202e+01 + 5.380628489700191e+01j, + -7.433392285433246e+01 + 6.388084216250878e+01j, + -6.832565803501586e+01 + 7.415032695116071e+01j, + -6.096221567378025e+01 + 8.470292433074425e+01j, + -5.185914574820616e+01 + 9.569048385258847e+01j, + -4.027853855197555e+01 + 1.074195196518679e+02j, + -2.433481337524861e+01 + 1.207298683731973e+02j, + ] + k2 = 9.999999999999989e+47 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(np.union1d(p2, np.conj(p2)), key=np.imag)) + assert_allclose(k, k2, rtol=1e-14) + + # high odd order, 'phase' + z, p, k = bessel(23, 1000, analog=True, output='zpk') + z2 = [] + p2 = [ + -2.497697202208956e+02 + 1.202813187870698e+03j, + -4.126986617510172e+02 + 1.065328794475509e+03j, + -5.304922463809596e+02 + 9.439760364018479e+02j, + -9.027564978975828e+02 + 1.010534334242318e+02j, + -8.909283244406079e+02 + 2.023024699647598e+02j, + -8.709469394347836e+02 + 3.039581994804637e+02j, + -8.423805948131370e+02 + 4.062657947488952e+02j, + -8.045561642249877e+02 + 5.095305912401127e+02j, + -7.564660146766259e+02 + 6.141594859516342e+02j, + -6.965966033906477e+02 + 7.207341374730186e+02j, + -6.225903228776276e+02 + 8.301558302815096e+02j, + -9.066732476324988e+02] + k2 = 9.999999999999983e+68 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(np.union1d(p2, np.conj(p2)), key=np.imag)) + assert_allclose(k, k2, rtol=1e-14) + + # high even order, 'delay' (Orchard 1965 "The Roots of the + # Maximally Flat-Delay Polynomials" Table 1) + z, p, k = bessel(31, 1, analog=True, output='zpk', norm='delay') + p2 = [-20.876706, + -20.826543 + 1.735732j, + -20.675502 + 3.473320j, + -20.421895 + 5.214702j, + -20.062802 + 6.961982j, + -19.593895 + 8.717546j, + -19.009148 + 10.484195j, + -18.300400 + 12.265351j, + -17.456663 + 14.065350j, + -16.463032 + 15.889910j, + -15.298849 + 17.746914j, + -13.934466 + 19.647827j, + -12.324914 + 21.610519j, + -10.395893 + 23.665701j, + - 8.005600 + 25.875019j, + - 4.792045 + 28.406037j, + ] + assert_allclose(sorted(p, key=np.imag), + sorted(np.union1d(p2, np.conj(p2)), key=np.imag)) + + # high odd order, 'delay' + z, p, k = bessel(30, 1, analog=True, output='zpk', norm='delay') + p2 = [-20.201029 + 0.867750j, + -20.097257 + 2.604235j, + -19.888485 + 4.343721j, + -19.572188 + 6.088363j, + -19.144380 + 7.840570j, + -18.599342 + 9.603147j, + -17.929195 + 11.379494j, + -17.123228 + 13.173901j, + -16.166808 + 14.992008j, + -15.039580 + 16.841580j, + -13.712245 + 18.733902j, + -12.140295 + 20.686563j, + -10.250119 + 22.729808j, + - 7.901170 + 24.924391j, + - 4.734679 + 27.435615j, + ] + assert_allclose(sorted(p, key=np.imag), + sorted(np.union1d(p2, np.conj(p2)), key=np.imag)) + + def test_refs(self): + # Compare to http://www.crbond.com/papers/bsf2.pdf + # "Delay Normalized Bessel Polynomial Coefficients" + bond_b = 10395 + bond_a = [1, 21, 210, 1260, 4725, 10395, 10395] + b, a = bessel(6, 1, norm='delay', analog=True) + assert_allclose(bond_b, b) + assert_allclose(bond_a, a) + + # "Delay Normalized Bessel Pole Locations" + bond_poles = { + 1: [-1.0000000000], + 2: [-1.5000000000 + 0.8660254038j], + 3: [-1.8389073227 + 1.7543809598j, -2.3221853546], + 4: [-2.1037893972 + 2.6574180419j, -2.8962106028 + 0.8672341289j], + 5: [-2.3246743032 + 3.5710229203j, -3.3519563992 + 1.7426614162j, + -3.6467385953], + 6: [-2.5159322478 + 4.4926729537j, -3.7357083563 + 2.6262723114j, + -4.2483593959 + 0.8675096732j], + 7: [-2.6856768789 + 5.4206941307j, -4.0701391636 + 3.5171740477j, + -4.7582905282 + 1.7392860611j, -4.9717868585], + 8: [-2.8389839489 + 6.3539112986j, -4.3682892172 + 4.4144425005j, + -5.2048407906 + 2.6161751526j, -5.5878860433 + 0.8676144454j], + 9: [-2.9792607982 + 7.2914636883j, -4.6384398872 + 5.3172716754j, + -5.6044218195 + 3.4981569179j, -6.1293679043 + 1.7378483835j, + -6.2970191817], + 10: [-3.1089162336 + 8.2326994591j, -4.8862195669 + 6.2249854825j, + -5.9675283286 + 4.3849471889j, -6.6152909655 + 2.6115679208j, + -6.9220449054 + 0.8676651955j] + } + + for N in range(1, 11): + p1 = np.sort(bond_poles[N]) + p2 = np.sort(np.concatenate(_cplxreal(besselap(N, 'delay')[1]))) + assert_array_almost_equal(p1, p2, decimal=10) + + # "Frequency Normalized Bessel Pole Locations" + bond_poles = { + 1: [-1.0000000000], + 2: [-1.1016013306 + 0.6360098248j], + 3: [-1.0474091610 + 0.9992644363j, -1.3226757999], + 4: [-0.9952087644 + 1.2571057395j, -1.3700678306 + 0.4102497175j], + 5: [-0.9576765486 + 1.4711243207j, -1.3808773259 + 0.7179095876j, + -1.5023162714], + 6: [-0.9306565229 + 1.6618632689j, -1.3818580976 + 0.9714718907j, + -1.5714904036 + 0.3208963742j], + 7: [-0.9098677806 + 1.8364513530j, -1.3789032168 + 1.1915667778j, + -1.6120387662 + 0.5892445069j, -1.6843681793], + 8: [-0.8928697188 + 1.9983258436j, -1.3738412176 + 1.3883565759j, + -1.6369394181 + 0.8227956251j, -1.7574084004 + 0.2728675751j], + 9: [-0.8783992762 + 2.1498005243j, -1.3675883098 + 1.5677337122j, + -1.6523964846 + 1.0313895670j, -1.8071705350 + 0.5123837306j, + -1.8566005012], + 10: [-0.8657569017 + 2.2926048310j, -1.3606922784 + 1.7335057427j, + -1.6618102414 + 1.2211002186j, -1.8421962445 + 0.7272575978j, + -1.9276196914 + 0.2416234710j] + } + + for N in range(1, 11): + p1 = np.sort(bond_poles[N]) + p2 = np.sort(np.concatenate(_cplxreal(besselap(N, 'mag')[1]))) + assert_array_almost_equal(p1, p2, decimal=10) + + # Compare to https://www.ranecommercial.com/legacy/note147.html + # "Table 1 - Bessel Crossovers of Second, Third, and Fourth-Order" + a = [1, 1, 1/3] + b2, a2 = bessel(2, 1, norm='delay', analog=True) + assert_allclose(a[::-1], a2/b2) + + a = [1, 1, 2/5, 1/15] + b2, a2 = bessel(3, 1, norm='delay', analog=True) + assert_allclose(a[::-1], a2/b2) + + a = [1, 1, 9/21, 2/21, 1/105] + b2, a2 = bessel(4, 1, norm='delay', analog=True) + assert_allclose(a[::-1], a2/b2) + + a = [1, np.sqrt(3), 1] + b2, a2 = bessel(2, 1, norm='phase', analog=True) + assert_allclose(a[::-1], a2/b2) + + # TODO: Why so inaccurate? Is reference flawed? + a = [1, 2.481, 2.463, 1.018] + b2, a2 = bessel(3, 1, norm='phase', analog=True) + assert_array_almost_equal(a[::-1], a2/b2, decimal=1) + + # TODO: Why so inaccurate? Is reference flawed? + a = [1, 3.240, 4.5, 3.240, 1.050] + b2, a2 = bessel(4, 1, norm='phase', analog=True) + assert_array_almost_equal(a[::-1], a2/b2, decimal=1) + + # Table of -3 dB factors: + N, scale = 2, 1.272 + scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1] + assert_array_almost_equal(scale, scale2, decimal=3) + + # TODO: Why so inaccurate? Is reference flawed? + N, scale = 3, 1.413 + scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1] + assert_array_almost_equal(scale, scale2, decimal=2) + + # TODO: Why so inaccurate? Is reference flawed? + N, scale = 4, 1.533 + scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1] + assert_array_almost_equal(scale, scale2, decimal=1) + + def test_hardcoded(self): + # Compare to values from original hardcoded implementation + originals = { + 0: [], + 1: [-1], + 2: [-.8660254037844386467637229 + .4999999999999999999999996j], + 3: [-.9416000265332067855971980, + -.7456403858480766441810907 + .7113666249728352680992154j], + 4: [-.6572111716718829545787788 + .8301614350048733772399715j, + -.9047587967882449459642624 + .2709187330038746636700926j], + 5: [-.9264420773877602247196260, + -.8515536193688395541722677 + .4427174639443327209850002j, + -.5905759446119191779319432 + .9072067564574549539291747j], + 6: [-.9093906830472271808050953 + .1856964396793046769246397j, + -.7996541858328288520243325 + .5621717346937317988594118j, + -.5385526816693109683073792 + .9616876881954277199245657j], + 7: [-.9194871556490290014311619, + -.8800029341523374639772340 + .3216652762307739398381830j, + -.7527355434093214462291616 + .6504696305522550699212995j, + -.4966917256672316755024763 + 1.002508508454420401230220j], + 8: [-.9096831546652910216327629 + .1412437976671422927888150j, + -.8473250802359334320103023 + .4259017538272934994996429j, + -.7111381808485399250796172 + .7186517314108401705762571j, + -.4621740412532122027072175 + 1.034388681126901058116589j], + 9: [-.9154957797499037686769223, + -.8911217017079759323183848 + .2526580934582164192308115j, + -.8148021112269012975514135 + .5085815689631499483745341j, + -.6743622686854761980403401 + .7730546212691183706919682j, + -.4331415561553618854685942 + 1.060073670135929666774323j], + 10: [-.9091347320900502436826431 + .1139583137335511169927714j, + -.8688459641284764527921864 + .3430008233766309973110589j, + -.7837694413101441082655890 + .5759147538499947070009852j, + -.6417513866988316136190854 + .8175836167191017226233947j, + -.4083220732868861566219785 + 1.081274842819124562037210j], + 11: [-.9129067244518981934637318, + -.8963656705721166099815744 + .2080480375071031919692341j, + -.8453044014712962954184557 + .4178696917801248292797448j, + -.7546938934722303128102142 + .6319150050721846494520941j, + -.6126871554915194054182909 + .8547813893314764631518509j, + -.3868149510055090879155425 + 1.099117466763120928733632j], + 12: [-.9084478234140682638817772 + 95506365213450398415258360e-27j, + -.8802534342016826507901575 + .2871779503524226723615457j, + -.8217296939939077285792834 + .4810212115100676440620548j, + -.7276681615395159454547013 + .6792961178764694160048987j, + -.5866369321861477207528215 + .8863772751320727026622149j, + -.3679640085526312839425808 + 1.114373575641546257595657j], + 13: [-.9110914665984182781070663, + -.8991314665475196220910718 + .1768342956161043620980863j, + -.8625094198260548711573628 + .3547413731172988997754038j, + -.7987460692470972510394686 + .5350752120696801938272504j, + -.7026234675721275653944062 + .7199611890171304131266374j, + -.5631559842430199266325818 + .9135900338325109684927731j, + -.3512792323389821669401925 + 1.127591548317705678613239j], + 14: [-.9077932138396487614720659 + 82196399419401501888968130e-27j, + -.8869506674916445312089167 + .2470079178765333183201435j, + -.8441199160909851197897667 + .4131653825102692595237260j, + -.7766591387063623897344648 + .5819170677377608590492434j, + -.6794256425119233117869491 + .7552857305042033418417492j, + -.5418766775112297376541293 + .9373043683516919569183099j, + -.3363868224902037330610040 + 1.139172297839859991370924j], + 15: [-.9097482363849064167228581, + -.9006981694176978324932918 + .1537681197278439351298882j, + -.8731264620834984978337843 + .3082352470564267657715883j, + -.8256631452587146506294553 + .4642348752734325631275134j, + -.7556027168970728127850416 + .6229396358758267198938604j, + -.6579196593110998676999362 + .7862895503722515897065645j, + -.5224954069658330616875186 + .9581787261092526478889345j, + -.3229963059766444287113517 + 1.149416154583629539665297j], + 16: [-.9072099595087001356491337 + 72142113041117326028823950e-27j, + -.8911723070323647674780132 + .2167089659900576449410059j, + -.8584264231521330481755780 + .3621697271802065647661080j, + -.8074790293236003885306146 + .5092933751171800179676218j, + -.7356166304713115980927279 + .6591950877860393745845254j, + -.6379502514039066715773828 + .8137453537108761895522580j, + -.5047606444424766743309967 + .9767137477799090692947061j, + -.3108782755645387813283867 + 1.158552841199330479412225j], + 17: [-.9087141161336397432860029, + -.9016273850787285964692844 + .1360267995173024591237303j, + -.8801100704438627158492165 + .2725347156478803885651973j, + -.8433414495836129204455491 + .4100759282910021624185986j, + -.7897644147799708220288138 + .5493724405281088674296232j, + -.7166893842372349049842743 + .6914936286393609433305754j, + -.6193710717342144521602448 + .8382497252826992979368621j, + -.4884629337672704194973683 + .9932971956316781632345466j, + -.2998489459990082015466971 + 1.166761272925668786676672j], + 18: [-.9067004324162775554189031 + 64279241063930693839360680e-27j, + -.8939764278132455733032155 + .1930374640894758606940586j, + -.8681095503628830078317207 + .3224204925163257604931634j, + -.8281885016242836608829018 + .4529385697815916950149364j, + -.7726285030739558780127746 + .5852778162086640620016316j, + -.6987821445005273020051878 + .7204696509726630531663123j, + -.6020482668090644386627299 + .8602708961893664447167418j, + -.4734268069916151511140032 + 1.008234300314801077034158j, + -.2897592029880489845789953 + 1.174183010600059128532230j], + 19: [-.9078934217899404528985092, + -.9021937639390660668922536 + .1219568381872026517578164j, + -.8849290585034385274001112 + .2442590757549818229026280j, + -.8555768765618421591093993 + .3672925896399872304734923j, + -.8131725551578197705476160 + .4915365035562459055630005j, + -.7561260971541629355231897 + .6176483917970178919174173j, + -.6818424412912442033411634 + .7466272357947761283262338j, + -.5858613321217832644813602 + .8801817131014566284786759j, + -.4595043449730988600785456 + 1.021768776912671221830298j, + -.2804866851439370027628724 + 1.180931628453291873626003j], + 20: [-.9062570115576771146523497 + 57961780277849516990208850e-27j, + -.8959150941925768608568248 + .1740317175918705058595844j, + -.8749560316673332850673214 + .2905559296567908031706902j, + -.8427907479956670633544106 + .4078917326291934082132821j, + -.7984251191290606875799876 + .5264942388817132427317659j, + -.7402780309646768991232610 + .6469975237605228320268752j, + -.6658120544829934193890626 + .7703721701100763015154510j, + -.5707026806915714094398061 + .8982829066468255593407161j, + -.4465700698205149555701841 + 1.034097702560842962315411j, + -.2719299580251652601727704 + 1.187099379810885886139638j], + 21: [-.9072262653142957028884077, + -.9025428073192696303995083 + .1105252572789856480992275j, + -.8883808106664449854431605 + .2213069215084350419975358j, + -.8643915813643204553970169 + .3326258512522187083009453j, + -.8299435470674444100273463 + .4448177739407956609694059j, + -.7840287980408341576100581 + .5583186348022854707564856j, + -.7250839687106612822281339 + .6737426063024382240549898j, + -.6506315378609463397807996 + .7920349342629491368548074j, + -.5564766488918562465935297 + .9148198405846724121600860j, + -.4345168906815271799687308 + 1.045382255856986531461592j, + -.2640041595834031147954813 + 1.192762031948052470183960j], + 22: [-.9058702269930872551848625 + 52774908289999045189007100e-27j, + -.8972983138153530955952835 + .1584351912289865608659759j, + -.8799661455640176154025352 + .2644363039201535049656450j, + -.8534754036851687233084587 + .3710389319482319823405321j, + -.8171682088462720394344996 + .4785619492202780899653575j, + -.7700332930556816872932937 + .5874255426351153211965601j, + -.7105305456418785989070935 + .6982266265924524000098548j, + -.6362427683267827226840153 + .8118875040246347267248508j, + -.5430983056306302779658129 + .9299947824439872998916657j, + -.4232528745642628461715044 + 1.055755605227545931204656j, + -.2566376987939318038016012 + 1.197982433555213008346532j], + 23: [-.9066732476324988168207439, + -.9027564979912504609412993 + .1010534335314045013252480j, + -.8909283242471251458653994 + .2023024699381223418195228j, + -.8709469395587416239596874 + .3039581993950041588888925j, + -.8423805948021127057054288 + .4062657948237602726779246j, + -.8045561642053176205623187 + .5095305912227258268309528j, + -.7564660146829880581478138 + .6141594859476032127216463j, + -.6965966033912705387505040 + .7207341374753046970247055j, + -.6225903228771341778273152 + .8301558302812980678845563j, + -.5304922463810191698502226 + .9439760364018300083750242j, + -.4126986617510148836149955 + 1.065328794475513585531053j, + -.2497697202208956030229911 + 1.202813187870697831365338j], + 24: [-.9055312363372773709269407 + 48440066540478700874836350e-27j, + -.8983105104397872954053307 + .1454056133873610120105857j, + -.8837358034555706623131950 + .2426335234401383076544239j, + -.8615278304016353651120610 + .3403202112618624773397257j, + -.8312326466813240652679563 + .4386985933597305434577492j, + -.7921695462343492518845446 + .5380628490968016700338001j, + -.7433392285088529449175873 + .6388084216222567930378296j, + -.6832565803536521302816011 + .7415032695091650806797753j, + -.6096221567378335562589532 + .8470292433077202380020454j, + -.5185914574820317343536707 + .9569048385259054576937721j, + -.4027853855197518014786978 + 1.074195196518674765143729j, + -.2433481337524869675825448 + 1.207298683731972524975429j], + 25: [-.9062073871811708652496104, + -.9028833390228020537142561 + 93077131185102967450643820e-27j, + -.8928551459883548836774529 + .1863068969804300712287138j, + -.8759497989677857803656239 + .2798521321771408719327250j, + -.8518616886554019782346493 + .3738977875907595009446142j, + -.8201226043936880253962552 + .4686668574656966589020580j, + -.7800496278186497225905443 + .5644441210349710332887354j, + -.7306549271849967721596735 + .6616149647357748681460822j, + -.6704827128029559528610523 + .7607348858167839877987008j, + -.5972898661335557242320528 + .8626676330388028512598538j, + -.5073362861078468845461362 + .9689006305344868494672405j, + -.3934529878191079606023847 + 1.082433927173831581956863j, + -.2373280669322028974199184 + 1.211476658382565356579418j], + } + for N in originals: + p1 = sorted(np.union1d(originals[N], + np.conj(originals[N])), key=np.imag) + p2 = sorted(besselap(N)[1], key=np.imag) + assert_allclose(p1, p2, rtol=1e-14) + + def test_norm_phase(self): + # Test some orders and frequencies and see that they have the right + # phase at w0 + for N in (1, 2, 3, 4, 5, 51, 72): + for w0 in (1, 100): + b, a = bessel(N, w0, analog=True, norm='phase') + w = np.linspace(0, w0, 100) + w, h = freqs(b, a, w) + phase = np.unwrap(np.angle(h)) + assert_allclose(phase[[0, -1]], (0, -N*pi/4), rtol=1e-1) + + def test_norm_mag(self): + # Test some orders and frequencies and see that they have the right + # mag at w0 + for N in (1, 2, 3, 4, 5, 51, 72): + for w0 in (1, 100): + b, a = bessel(N, w0, analog=True, norm='mag') + w = (0, w0) + w, h = freqs(b, a, w) + mag = abs(h) + assert_allclose(mag, (1, 1/np.sqrt(2))) + + def test_norm_delay(self): + # Test some orders and frequencies and see that they have the right + # delay at DC + for N in (1, 2, 3, 4, 5, 51, 72): + for w0 in (1, 100): + b, a = bessel(N, w0, analog=True, norm='delay') + w = np.linspace(0, 10*w0, 1000) + w, h = freqs(b, a, w) + delay = -np.diff(np.unwrap(np.angle(h)))/np.diff(w) + assert_allclose(delay[0], 1/w0, rtol=1e-4) + + def test_norm_factor(self): + mpmath_values = { + 1: 1, 2: 1.361654128716130520, 3: 1.755672368681210649, + 4: 2.113917674904215843, 5: 2.427410702152628137, + 6: 2.703395061202921876, 7: 2.951722147038722771, + 8: 3.179617237510651330, 9: 3.391693138911660101, + 10: 3.590980594569163482, 11: 3.779607416439620092, + 12: 3.959150821144285315, 13: 4.130825499383535980, + 14: 4.295593409533637564, 15: 4.454233021624377494, + 16: 4.607385465472647917, 17: 4.755586548961147727, + 18: 4.899289677284488007, 19: 5.038882681488207605, + 20: 5.174700441742707423, 21: 5.307034531360917274, + 22: 5.436140703250035999, 23: 5.562244783787878196, + 24: 5.685547371295963521, 25: 5.806227623775418541, + 50: 8.268963160013226298, 51: 8.352374541546012058, + } + for N in mpmath_values: + z, p, k = besselap(N, 'delay') + assert_allclose(mpmath_values[N], _norm_factor(p, k), rtol=1e-13) + + def test_bessel_poly(self): + assert_array_equal(_bessel_poly(5), [945, 945, 420, 105, 15, 1]) + assert_array_equal(_bessel_poly(4, True), [1, 10, 45, 105, 105]) + + def test_bessel_zeros(self): + assert_array_equal(_bessel_zeros(0), []) + + def test_invalid(self): + assert_raises(ValueError, besselap, 5, 'nonsense') + assert_raises(ValueError, besselap, -5) + assert_raises(ValueError, besselap, 3.2) + assert_raises(ValueError, _bessel_poly, -3) + assert_raises(ValueError, _bessel_poly, 3.3) + + def test_fs_param(self): + for norm in ('phase', 'mag', 'delay'): + for fs in (900, 900.1, 1234.567): + for N in (0, 1, 2, 3, 10): + for fc in (100, 100.1, 432.12345): + for btype in ('lp', 'hp'): + ba1 = bessel(N, fc, btype, norm=norm, fs=fs) + ba2 = bessel(N, fc/(fs/2), btype, norm=norm) + assert_allclose(ba1, ba2) + for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)): + for btype in ('bp', 'bs'): + ba1 = bessel(N, fc, btype, norm=norm, fs=fs) + for seq in (list, tuple, array): + fcnorm = seq([f/(fs/2) for f in fc]) + ba2 = bessel(N, fcnorm, btype, norm=norm) + assert_allclose(ba1, ba2) + + +class TestButter: + + def test_degenerate(self): + # 0-order filter is just a passthrough + b, a = butter(0, 1, analog=True) + assert_array_equal(b, [1]) + assert_array_equal(a, [1]) + + # 1-order filter is same for all types + b, a = butter(1, 1, analog=True) + assert_array_almost_equal(b, [1]) + assert_array_almost_equal(a, [1, 1]) + + z, p, k = butter(1, 0.3, output='zpk') + assert_array_equal(z, [-1]) + assert_allclose(p, [3.249196962329063e-01], rtol=1e-14) + assert_allclose(k, 3.375401518835469e-01, rtol=1e-14) + + def test_basic(self): + # analog s-plane + for N in range(25): + wn = 0.01 + z, p, k = butter(N, wn, 'low', analog=True, output='zpk') + assert_array_almost_equal([], z) + assert_(len(p) == N) + # All poles should be at distance wn from origin + assert_array_almost_equal(wn, abs(p)) + assert_(all(np.real(p) <= 0)) # No poles in right half of S-plane + assert_array_almost_equal(wn**N, k) + + # digital z-plane + for N in range(25): + wn = 0.01 + z, p, k = butter(N, wn, 'high', analog=False, output='zpk') + assert_array_equal(np.ones(N), z) # All zeros exactly at DC + assert_(all(np.abs(p) <= 1)) # No poles outside unit circle + + b1, a1 = butter(2, 1, analog=True) + assert_array_almost_equal(b1, [1]) + assert_array_almost_equal(a1, [1, np.sqrt(2), 1]) + + b2, a2 = butter(5, 1, analog=True) + assert_array_almost_equal(b2, [1]) + assert_array_almost_equal(a2, [1, 3.2361, 5.2361, + 5.2361, 3.2361, 1], decimal=4) + + b3, a3 = butter(10, 1, analog=True) + assert_array_almost_equal(b3, [1]) + assert_array_almost_equal(a3, [1, 6.3925, 20.4317, 42.8021, 64.8824, + 74.2334, 64.8824, 42.8021, 20.4317, + 6.3925, 1], decimal=4) + + b2, a2 = butter(19, 1.0441379169150726, analog=True) + assert_array_almost_equal(b2, [2.2720], decimal=4) + assert_array_almost_equal(a2, 1.0e+004 * np.array([ + 0.0001, 0.0013, 0.0080, 0.0335, 0.1045, 0.2570, + 0.5164, 0.8669, 1.2338, 1.5010, 1.5672, 1.4044, + 1.0759, 0.6986, 0.3791, 0.1681, 0.0588, 0.0153, + 0.0026, 0.0002]), decimal=0) + + b, a = butter(5, 0.4) + assert_array_almost_equal(b, [0.0219, 0.1097, 0.2194, + 0.2194, 0.1097, 0.0219], decimal=4) + assert_array_almost_equal(a, [1.0000, -0.9853, 0.9738, + -0.3864, 0.1112, -0.0113], decimal=4) + + def test_highpass(self): + # highpass, high even order + z, p, k = butter(28, 0.43, 'high', output='zpk') + z2 = np.ones(28) + p2 = [ + 2.068257195514592e-01 + 9.238294351481734e-01j, + 2.068257195514592e-01 - 9.238294351481734e-01j, + 1.874933103892023e-01 + 8.269455076775277e-01j, + 1.874933103892023e-01 - 8.269455076775277e-01j, + 1.717435567330153e-01 + 7.383078571194629e-01j, + 1.717435567330153e-01 - 7.383078571194629e-01j, + 1.588266870755982e-01 + 6.564623730651094e-01j, + 1.588266870755982e-01 - 6.564623730651094e-01j, + 1.481881532502603e-01 + 5.802343458081779e-01j, + 1.481881532502603e-01 - 5.802343458081779e-01j, + 1.394122576319697e-01 + 5.086609000582009e-01j, + 1.394122576319697e-01 - 5.086609000582009e-01j, + 1.321840881809715e-01 + 4.409411734716436e-01j, + 1.321840881809715e-01 - 4.409411734716436e-01j, + 1.262633413354405e-01 + 3.763990035551881e-01j, + 1.262633413354405e-01 - 3.763990035551881e-01j, + 1.214660449478046e-01 + 3.144545234797277e-01j, + 1.214660449478046e-01 - 3.144545234797277e-01j, + 1.104868766650320e-01 + 2.771505404367791e-02j, + 1.104868766650320e-01 - 2.771505404367791e-02j, + 1.111768629525075e-01 + 8.331369153155753e-02j, + 1.111768629525075e-01 - 8.331369153155753e-02j, + 1.125740630842972e-01 + 1.394219509611784e-01j, + 1.125740630842972e-01 - 1.394219509611784e-01j, + 1.147138487992747e-01 + 1.963932363793666e-01j, + 1.147138487992747e-01 - 1.963932363793666e-01j, + 1.176516491045901e-01 + 2.546021573417188e-01j, + 1.176516491045901e-01 - 2.546021573417188e-01j, + ] + k2 = 1.446671081817286e-06 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-7) + assert_allclose(k, k2, rtol=1e-10) + + # highpass, high odd order + z, p, k = butter(27, 0.56, 'high', output='zpk') + z2 = np.ones(27) + p2 = [ + -1.772572785680147e-01 + 9.276431102995948e-01j, + -1.772572785680147e-01 - 9.276431102995948e-01j, + -1.600766565322114e-01 + 8.264026279893268e-01j, + -1.600766565322114e-01 - 8.264026279893268e-01j, + -1.461948419016121e-01 + 7.341841939120078e-01j, + -1.461948419016121e-01 - 7.341841939120078e-01j, + -1.348975284762046e-01 + 6.493235066053785e-01j, + -1.348975284762046e-01 - 6.493235066053785e-01j, + -1.256628210712206e-01 + 5.704921366889227e-01j, + -1.256628210712206e-01 - 5.704921366889227e-01j, + -1.181038235962314e-01 + 4.966120551231630e-01j, + -1.181038235962314e-01 - 4.966120551231630e-01j, + -1.119304913239356e-01 + 4.267938916403775e-01j, + -1.119304913239356e-01 - 4.267938916403775e-01j, + -1.069237739782691e-01 + 3.602914879527338e-01j, + -1.069237739782691e-01 - 3.602914879527338e-01j, + -1.029178030691416e-01 + 2.964677964142126e-01j, + -1.029178030691416e-01 - 2.964677964142126e-01j, + -9.978747500816100e-02 + 2.347687643085738e-01j, + -9.978747500816100e-02 - 2.347687643085738e-01j, + -9.743974496324025e-02 + 1.747028739092479e-01j, + -9.743974496324025e-02 - 1.747028739092479e-01j, + -9.580754551625957e-02 + 1.158246860771989e-01j, + -9.580754551625957e-02 - 1.158246860771989e-01j, + -9.484562207782568e-02 + 5.772118357151691e-02j, + -9.484562207782568e-02 - 5.772118357151691e-02j, + -9.452783117928215e-02 + ] + k2 = 9.585686688851069e-09 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-8) + assert_allclose(k, k2) + + def test_bandpass(self): + z, p, k = butter(8, [0.25, 0.33], 'band', output='zpk') + z2 = [1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1] + p2 = [ + 4.979909925436156e-01 + 8.367609424799387e-01j, + 4.979909925436156e-01 - 8.367609424799387e-01j, + 4.913338722555539e-01 + 7.866774509868817e-01j, + 4.913338722555539e-01 - 7.866774509868817e-01j, + 5.035229361778706e-01 + 7.401147376726750e-01j, + 5.035229361778706e-01 - 7.401147376726750e-01j, + 5.307617160406101e-01 + 7.029184459442954e-01j, + 5.307617160406101e-01 - 7.029184459442954e-01j, + 5.680556159453138e-01 + 6.788228792952775e-01j, + 5.680556159453138e-01 - 6.788228792952775e-01j, + 6.100962560818854e-01 + 6.693849403338664e-01j, + 6.100962560818854e-01 - 6.693849403338664e-01j, + 6.904694312740631e-01 + 6.930501690145245e-01j, + 6.904694312740631e-01 - 6.930501690145245e-01j, + 6.521767004237027e-01 + 6.744414640183752e-01j, + 6.521767004237027e-01 - 6.744414640183752e-01j, + ] + k2 = 3.398854055800844e-08 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-13) + assert_allclose(k, k2, rtol=1e-13) + + # bandpass analog + z, p, k = butter(4, [90.5, 110.5], 'bp', analog=True, output='zpk') + z2 = np.zeros(4) + p2 = [ + -4.179137760733086e+00 + 1.095935899082837e+02j, + -4.179137760733086e+00 - 1.095935899082837e+02j, + -9.593598668443835e+00 + 1.034745398029734e+02j, + -9.593598668443835e+00 - 1.034745398029734e+02j, + -8.883991981781929e+00 + 9.582087115567160e+01j, + -8.883991981781929e+00 - 9.582087115567160e+01j, + -3.474530886568715e+00 + 9.111599925805801e+01j, + -3.474530886568715e+00 - 9.111599925805801e+01j, + ] + k2 = 1.600000000000001e+05 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), sorted(p2, key=np.imag)) + assert_allclose(k, k2, rtol=1e-15) + + def test_bandstop(self): + z, p, k = butter(7, [0.45, 0.56], 'stop', output='zpk') + z2 = [-1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j, + -1.594474531383421e-02 + 9.998728744679880e-01j, + -1.594474531383421e-02 - 9.998728744679880e-01j] + p2 = [-1.766850742887729e-01 + 9.466951258673900e-01j, + -1.766850742887729e-01 - 9.466951258673900e-01j, + 1.467897662432886e-01 + 9.515917126462422e-01j, + 1.467897662432886e-01 - 9.515917126462422e-01j, + -1.370083529426906e-01 + 8.880376681273993e-01j, + -1.370083529426906e-01 - 8.880376681273993e-01j, + 1.086774544701390e-01 + 8.915240810704319e-01j, + 1.086774544701390e-01 - 8.915240810704319e-01j, + -7.982704457700891e-02 + 8.506056315273435e-01j, + -7.982704457700891e-02 - 8.506056315273435e-01j, + 5.238812787110331e-02 + 8.524011102699969e-01j, + 5.238812787110331e-02 - 8.524011102699969e-01j, + -1.357545000491310e-02 + 8.382287744986582e-01j, + -1.357545000491310e-02 - 8.382287744986582e-01j] + k2 = 4.577122512960063e-01 + assert_allclose(sorted(z, key=np.imag), sorted(z2, key=np.imag)) + assert_allclose(sorted(p, key=np.imag), sorted(p2, key=np.imag)) + assert_allclose(k, k2, rtol=1e-14) + + def test_ba_output(self): + b, a = butter(4, [100, 300], 'bandpass', analog=True) + b2 = [1.6e+09, 0, 0, 0, 0] + a2 = [1.000000000000000e+00, 5.226251859505511e+02, + 2.565685424949238e+05, 6.794127417357160e+07, + 1.519411254969542e+10, 2.038238225207147e+12, + 2.309116882454312e+14, 1.411088002066486e+16, + 8.099999999999991e+17] + assert_allclose(b, b2, rtol=1e-14) + assert_allclose(a, a2, rtol=1e-14) + + def test_fs_param(self): + for fs in (900, 900.1, 1234.567): + for N in (0, 1, 2, 3, 10): + for fc in (100, 100.1, 432.12345): + for btype in ('lp', 'hp'): + ba1 = butter(N, fc, btype, fs=fs) + ba2 = butter(N, fc/(fs/2), btype) + assert_allclose(ba1, ba2) + for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)): + for btype in ('bp', 'bs'): + ba1 = butter(N, fc, btype, fs=fs) + for seq in (list, tuple, array): + fcnorm = seq([f/(fs/2) for f in fc]) + ba2 = butter(N, fcnorm, btype) + assert_allclose(ba1, ba2) + + +class TestCheby1: + + def test_degenerate(self): + # 0-order filter is just a passthrough + # Even-order filters have DC gain of -rp dB + b, a = cheby1(0, 10*np.log10(2), 1, analog=True) + assert_array_almost_equal(b, [1/np.sqrt(2)]) + assert_array_equal(a, [1]) + + # 1-order filter is same for all types + b, a = cheby1(1, 10*np.log10(2), 1, analog=True) + assert_array_almost_equal(b, [1]) + assert_array_almost_equal(a, [1, 1]) + + z, p, k = cheby1(1, 0.1, 0.3, output='zpk') + assert_array_equal(z, [-1]) + assert_allclose(p, [-5.390126972799615e-01], rtol=1e-14) + assert_allclose(k, 7.695063486399808e-01, rtol=1e-14) + + def test_basic(self): + for N in range(25): + wn = 0.01 + z, p, k = cheby1(N, 1, wn, 'low', analog=True, output='zpk') + assert_array_almost_equal([], z) + assert_(len(p) == N) + assert_(all(np.real(p) <= 0)) # No poles in right half of S-plane + + for N in range(25): + wn = 0.01 + z, p, k = cheby1(N, 1, wn, 'high', analog=False, output='zpk') + assert_array_equal(np.ones(N), z) # All zeros exactly at DC + assert_(all(np.abs(p) <= 1)) # No poles outside unit circle + + # Same test as TestNormalize + b, a = cheby1(8, 0.5, 0.048) + assert_array_almost_equal(b, [ + 2.150733144728282e-11, 1.720586515782626e-10, + 6.022052805239190e-10, 1.204410561047838e-09, + 1.505513201309798e-09, 1.204410561047838e-09, + 6.022052805239190e-10, 1.720586515782626e-10, + 2.150733144728282e-11], decimal=14) + assert_array_almost_equal(a, [ + 1.000000000000000e+00, -7.782402035027959e+00, + 2.654354569747454e+01, -5.182182531666387e+01, + 6.334127355102684e+01, -4.963358186631157e+01, + 2.434862182949389e+01, -6.836925348604676e+00, + 8.412934944449140e-01], decimal=14) + + b, a = cheby1(4, 1, [0.4, 0.7], btype='band') + assert_array_almost_equal(b, [0.0084, 0, -0.0335, 0, 0.0502, 0, + -0.0335, 0, 0.0084], decimal=4) + assert_array_almost_equal(a, [1.0, 1.1191, 2.862, 2.2986, 3.4137, + 1.8653, 1.8982, 0.5676, 0.4103], + decimal=4) + + b2, a2 = cheby1(5, 3, 1, analog=True) + assert_array_almost_equal(b2, [0.0626], decimal=4) + assert_array_almost_equal(a2, [1, 0.5745, 1.4150, 0.5489, 0.4080, + 0.0626], decimal=4) + + b, a = cheby1(8, 0.5, 0.1) + assert_array_almost_equal(b, 1.0e-006 * np.array([ + 0.00703924326028, 0.05631394608227, 0.19709881128793, + 0.39419762257586, 0.49274702821983, 0.39419762257586, + 0.19709881128793, 0.05631394608227, 0.00703924326028]), + decimal=13) + assert_array_almost_equal(a, [ + 1.00000000000000, -7.44912258934158, 24.46749067762108, + -46.27560200466141, 55.11160187999928, -42.31640010161038, + 20.45543300484147, -5.69110270561444, 0.69770374759022], + decimal=13) + + b, a = cheby1(8, 0.5, 0.25) + assert_array_almost_equal(b, 1.0e-003 * np.array([ + 0.00895261138923, 0.07162089111382, 0.25067311889837, + 0.50134623779673, 0.62668279724591, 0.50134623779673, + 0.25067311889837, 0.07162089111382, 0.00895261138923]), + decimal=13) + assert_array_almost_equal(a, [1.00000000000000, -5.97529229188545, + 16.58122329202101, -27.71423273542923, + 30.39509758355313, -22.34729670426879, + 10.74509800434910, -3.08924633697497, + 0.40707685889802], decimal=13) + + def test_highpass(self): + # high even order + z, p, k = cheby1(24, 0.7, 0.2, 'high', output='zpk') + z2 = np.ones(24) + p2 = [-6.136558509657073e-01 + 2.700091504942893e-01j, + -6.136558509657073e-01 - 2.700091504942893e-01j, + -3.303348340927516e-01 + 6.659400861114254e-01j, + -3.303348340927516e-01 - 6.659400861114254e-01j, + 8.779713780557169e-03 + 8.223108447483040e-01j, + 8.779713780557169e-03 - 8.223108447483040e-01j, + 2.742361123006911e-01 + 8.356666951611864e-01j, + 2.742361123006911e-01 - 8.356666951611864e-01j, + 4.562984557158206e-01 + 7.954276912303594e-01j, + 4.562984557158206e-01 - 7.954276912303594e-01j, + 5.777335494123628e-01 + 7.435821817961783e-01j, + 5.777335494123628e-01 - 7.435821817961783e-01j, + 6.593260977749194e-01 + 6.955390907990932e-01j, + 6.593260977749194e-01 - 6.955390907990932e-01j, + 7.149590948466562e-01 + 6.559437858502012e-01j, + 7.149590948466562e-01 - 6.559437858502012e-01j, + 7.532432388188739e-01 + 6.256158042292060e-01j, + 7.532432388188739e-01 - 6.256158042292060e-01j, + 7.794365244268271e-01 + 6.042099234813333e-01j, + 7.794365244268271e-01 - 6.042099234813333e-01j, + 7.967253874772997e-01 + 5.911966597313203e-01j, + 7.967253874772997e-01 - 5.911966597313203e-01j, + 8.069756417293870e-01 + 5.862214589217275e-01j, + 8.069756417293870e-01 - 5.862214589217275e-01j] + k2 = 6.190427617192018e-04 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-10) + assert_allclose(k, k2, rtol=1e-10) + + # high odd order + z, p, k = cheby1(23, 0.8, 0.3, 'high', output='zpk') + z2 = np.ones(23) + p2 = [-7.676400532011010e-01, + -6.754621070166477e-01 + 3.970502605619561e-01j, + -6.754621070166477e-01 - 3.970502605619561e-01j, + -4.528880018446727e-01 + 6.844061483786332e-01j, + -4.528880018446727e-01 - 6.844061483786332e-01j, + -1.986009130216447e-01 + 8.382285942941594e-01j, + -1.986009130216447e-01 - 8.382285942941594e-01j, + 2.504673931532608e-02 + 8.958137635794080e-01j, + 2.504673931532608e-02 - 8.958137635794080e-01j, + 2.001089429976469e-01 + 9.010678290791480e-01j, + 2.001089429976469e-01 - 9.010678290791480e-01j, + 3.302410157191755e-01 + 8.835444665962544e-01j, + 3.302410157191755e-01 - 8.835444665962544e-01j, + 4.246662537333661e-01 + 8.594054226449009e-01j, + 4.246662537333661e-01 - 8.594054226449009e-01j, + 4.919620928120296e-01 + 8.366772762965786e-01j, + 4.919620928120296e-01 - 8.366772762965786e-01j, + 5.385746917494749e-01 + 8.191616180796720e-01j, + 5.385746917494749e-01 - 8.191616180796720e-01j, + 5.855636993537203e-01 + 8.060680937701062e-01j, + 5.855636993537203e-01 - 8.060680937701062e-01j, + 5.688812849391721e-01 + 8.086497795114683e-01j, + 5.688812849391721e-01 - 8.086497795114683e-01j] + k2 = 1.941697029206324e-05 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-10) + assert_allclose(k, k2, rtol=1e-10) + + z, p, k = cheby1(10, 1, 1000, 'high', analog=True, output='zpk') + z2 = np.zeros(10) + p2 = [-3.144743169501551e+03 + 3.511680029092744e+03j, + -3.144743169501551e+03 - 3.511680029092744e+03j, + -5.633065604514602e+02 + 2.023615191183945e+03j, + -5.633065604514602e+02 - 2.023615191183945e+03j, + -1.946412183352025e+02 + 1.372309454274755e+03j, + -1.946412183352025e+02 - 1.372309454274755e+03j, + -7.987162953085479e+01 + 1.105207708045358e+03j, + -7.987162953085479e+01 - 1.105207708045358e+03j, + -2.250315039031946e+01 + 1.001723931471477e+03j, + -2.250315039031946e+01 - 1.001723931471477e+03j] + k2 = 8.912509381337453e-01 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-13) + assert_allclose(k, k2, rtol=1e-15) + + def test_bandpass(self): + z, p, k = cheby1(8, 1, [0.3, 0.4], 'bp', output='zpk') + z2 = [1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1] + p2 = [3.077784854851463e-01 + 9.453307017592942e-01j, + 3.077784854851463e-01 - 9.453307017592942e-01j, + 3.280567400654425e-01 + 9.272377218689016e-01j, + 3.280567400654425e-01 - 9.272377218689016e-01j, + 3.677912763284301e-01 + 9.038008865279966e-01j, + 3.677912763284301e-01 - 9.038008865279966e-01j, + 4.194425632520948e-01 + 8.769407159656157e-01j, + 4.194425632520948e-01 - 8.769407159656157e-01j, + 4.740921994669189e-01 + 8.496508528630974e-01j, + 4.740921994669189e-01 - 8.496508528630974e-01j, + 5.234866481897429e-01 + 8.259608422808477e-01j, + 5.234866481897429e-01 - 8.259608422808477e-01j, + 5.844717632289875e-01 + 8.052901363500210e-01j, + 5.844717632289875e-01 - 8.052901363500210e-01j, + 5.615189063336070e-01 + 8.100667803850766e-01j, + 5.615189063336070e-01 - 8.100667803850766e-01j] + k2 = 5.007028718074307e-09 + assert_array_equal(z, z2) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-13) + assert_allclose(k, k2, rtol=1e-13) + + def test_bandstop(self): + z, p, k = cheby1(7, 1, [0.5, 0.6], 'stop', output='zpk') + z2 = [-1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j, + -1.583844403245361e-01 + 9.873775210440450e-01j, + -1.583844403245361e-01 - 9.873775210440450e-01j] + p2 = [-8.942974551472813e-02 + 3.482480481185926e-01j, + -8.942974551472813e-02 - 3.482480481185926e-01j, + 1.293775154041798e-01 + 8.753499858081858e-01j, + 1.293775154041798e-01 - 8.753499858081858e-01j, + 3.399741945062013e-02 + 9.690316022705607e-01j, + 3.399741945062013e-02 - 9.690316022705607e-01j, + 4.167225522796539e-04 + 9.927338161087488e-01j, + 4.167225522796539e-04 - 9.927338161087488e-01j, + -3.912966549550960e-01 + 8.046122859255742e-01j, + -3.912966549550960e-01 - 8.046122859255742e-01j, + -3.307805547127368e-01 + 9.133455018206508e-01j, + -3.307805547127368e-01 - 9.133455018206508e-01j, + -3.072658345097743e-01 + 9.443589759799366e-01j, + -3.072658345097743e-01 - 9.443589759799366e-01j] + k2 = 3.619438310405028e-01 + assert_allclose(sorted(z, key=np.imag), + sorted(z2, key=np.imag), rtol=1e-13) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-13) + assert_allclose(k, k2, rtol=0, atol=5e-16) + + def test_ba_output(self): + # with transfer function conversion, without digital conversion + b, a = cheby1(5, 0.9, [210, 310], 'stop', analog=True) + b2 = [1.000000000000006e+00, 0, + 3.255000000000020e+05, 0, + 4.238010000000026e+10, 0, + 2.758944510000017e+15, 0, + 8.980364380050052e+19, 0, + 1.169243442282517e+24 + ] + a2 = [1.000000000000000e+00, 4.630555945694342e+02, + 4.039266454794788e+05, 1.338060988610237e+08, + 5.844333551294591e+10, 1.357346371637638e+13, + 3.804661141892782e+15, 5.670715850340080e+17, + 1.114411200988328e+20, 8.316815934908471e+21, + 1.169243442282517e+24 + ] + assert_allclose(b, b2, rtol=1e-14) + assert_allclose(a, a2, rtol=1e-14) + + def test_fs_param(self): + for fs in (900, 900.1, 1234.567): + for N in (0, 1, 2, 3, 10): + for fc in (100, 100.1, 432.12345): + for btype in ('lp', 'hp'): + ba1 = cheby1(N, 1, fc, btype, fs=fs) + ba2 = cheby1(N, 1, fc/(fs/2), btype) + assert_allclose(ba1, ba2) + for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)): + for btype in ('bp', 'bs'): + ba1 = cheby1(N, 1, fc, btype, fs=fs) + for seq in (list, tuple, array): + fcnorm = seq([f/(fs/2) for f in fc]) + ba2 = cheby1(N, 1, fcnorm, btype) + assert_allclose(ba1, ba2) + + +class TestCheby2: + + def test_degenerate(self): + # 0-order filter is just a passthrough + # Stopband ripple factor doesn't matter + b, a = cheby2(0, 123.456, 1, analog=True) + assert_array_equal(b, [1]) + assert_array_equal(a, [1]) + + # 1-order filter is same for all types + b, a = cheby2(1, 10*np.log10(2), 1, analog=True) + assert_array_almost_equal(b, [1]) + assert_array_almost_equal(a, [1, 1]) + + z, p, k = cheby2(1, 50, 0.3, output='zpk') + assert_array_equal(z, [-1]) + assert_allclose(p, [9.967826460175649e-01], rtol=1e-14) + assert_allclose(k, 1.608676991217512e-03, rtol=1e-14) + + def test_basic(self): + for N in range(25): + wn = 0.01 + z, p, k = cheby2(N, 40, wn, 'low', analog=True, output='zpk') + assert_(len(p) == N) + assert_(all(np.real(p) <= 0)) # No poles in right half of S-plane + + for N in range(25): + wn = 0.01 + z, p, k = cheby2(N, 40, wn, 'high', analog=False, output='zpk') + assert_(all(np.abs(p) <= 1)) # No poles outside unit circle + + B, A = cheby2(18, 100, 0.5) + assert_array_almost_equal(B, [ + 0.00167583914216, 0.01249479541868, 0.05282702120282, + 0.15939804265706, 0.37690207631117, 0.73227013789108, + 1.20191856962356, 1.69522872823393, 2.07598674519837, + 2.21972389625291, 2.07598674519838, 1.69522872823395, + 1.20191856962359, 0.73227013789110, 0.37690207631118, + 0.15939804265707, 0.05282702120282, 0.01249479541868, + 0.00167583914216], decimal=13) + assert_array_almost_equal(A, [ + 1.00000000000000, -0.27631970006174, 3.19751214254060, + -0.15685969461355, 4.13926117356269, 0.60689917820044, + 2.95082770636540, 0.89016501910416, 1.32135245849798, + 0.51502467236824, 0.38906643866660, 0.15367372690642, + 0.07255803834919, 0.02422454070134, 0.00756108751837, + 0.00179848550988, 0.00033713574499, 0.00004258794833, + 0.00000281030149], decimal=13) + + def test_highpass(self): + # high even order + z, p, k = cheby2(26, 60, 0.3, 'high', output='zpk') + z2 = [9.981088955489852e-01 + 6.147058341984388e-02j, + 9.981088955489852e-01 - 6.147058341984388e-02j, + 9.832702870387426e-01 + 1.821525257215483e-01j, + 9.832702870387426e-01 - 1.821525257215483e-01j, + 9.550760158089112e-01 + 2.963609353922882e-01j, + 9.550760158089112e-01 - 2.963609353922882e-01j, + 9.162054748821922e-01 + 4.007087817803773e-01j, + 9.162054748821922e-01 - 4.007087817803773e-01j, + 8.700619897368064e-01 + 4.929423232136168e-01j, + 8.700619897368064e-01 - 4.929423232136168e-01j, + 5.889791753434985e-01 + 8.081482110427953e-01j, + 5.889791753434985e-01 - 8.081482110427953e-01j, + 5.984900456570295e-01 + 8.011302423760501e-01j, + 5.984900456570295e-01 - 8.011302423760501e-01j, + 6.172880888914629e-01 + 7.867371958365343e-01j, + 6.172880888914629e-01 - 7.867371958365343e-01j, + 6.448899971038180e-01 + 7.642754030030161e-01j, + 6.448899971038180e-01 - 7.642754030030161e-01j, + 6.804845629637927e-01 + 7.327624168637228e-01j, + 6.804845629637927e-01 - 7.327624168637228e-01j, + 8.202619107108660e-01 + 5.719881098737678e-01j, + 8.202619107108660e-01 - 5.719881098737678e-01j, + 7.228410452536148e-01 + 6.910143437705678e-01j, + 7.228410452536148e-01 - 6.910143437705678e-01j, + 7.702121399578629e-01 + 6.377877856007792e-01j, + 7.702121399578629e-01 - 6.377877856007792e-01j] + p2 = [7.365546198286450e-01 + 4.842085129329526e-02j, + 7.365546198286450e-01 - 4.842085129329526e-02j, + 7.292038510962885e-01 + 1.442201672097581e-01j, + 7.292038510962885e-01 - 1.442201672097581e-01j, + 7.151293788040354e-01 + 2.369925800458584e-01j, + 7.151293788040354e-01 - 2.369925800458584e-01j, + 6.955051820787286e-01 + 3.250341363856910e-01j, + 6.955051820787286e-01 - 3.250341363856910e-01j, + 6.719122956045220e-01 + 4.070475750638047e-01j, + 6.719122956045220e-01 - 4.070475750638047e-01j, + 6.461722130611300e-01 + 4.821965916689270e-01j, + 6.461722130611300e-01 - 4.821965916689270e-01j, + 5.528045062872224e-01 + 8.162920513838372e-01j, + 5.528045062872224e-01 - 8.162920513838372e-01j, + 5.464847782492791e-01 + 7.869899955967304e-01j, + 5.464847782492791e-01 - 7.869899955967304e-01j, + 5.488033111260949e-01 + 7.520442354055579e-01j, + 5.488033111260949e-01 - 7.520442354055579e-01j, + 6.201874719022955e-01 + 5.500894392527353e-01j, + 6.201874719022955e-01 - 5.500894392527353e-01j, + 5.586478152536709e-01 + 7.112676877332921e-01j, + 5.586478152536709e-01 - 7.112676877332921e-01j, + 5.958145844148228e-01 + 6.107074340842115e-01j, + 5.958145844148228e-01 - 6.107074340842115e-01j, + 5.747812938519067e-01 + 6.643001536914696e-01j, + 5.747812938519067e-01 - 6.643001536914696e-01j] + k2 = 9.932997786497189e-02 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-13) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-12) + assert_allclose(k, k2, rtol=1e-11) + + # high odd order + z, p, k = cheby2(25, 80, 0.5, 'high', output='zpk') + z2 = [9.690690376586687e-01 + 2.467897896011971e-01j, + 9.690690376586687e-01 - 2.467897896011971e-01j, + 9.999999999999492e-01, + 8.835111277191199e-01 + 4.684101698261429e-01j, + 8.835111277191199e-01 - 4.684101698261429e-01j, + 7.613142857900539e-01 + 6.483830335935022e-01j, + 7.613142857900539e-01 - 6.483830335935022e-01j, + 6.232625173626231e-01 + 7.820126817709752e-01j, + 6.232625173626231e-01 - 7.820126817709752e-01j, + 4.864456563413621e-01 + 8.737108351316745e-01j, + 4.864456563413621e-01 - 8.737108351316745e-01j, + 3.618368136816749e-01 + 9.322414495530347e-01j, + 3.618368136816749e-01 - 9.322414495530347e-01j, + 2.549486883466794e-01 + 9.669545833752675e-01j, + 2.549486883466794e-01 - 9.669545833752675e-01j, + 1.676175432109457e-01 + 9.858520980390212e-01j, + 1.676175432109457e-01 - 9.858520980390212e-01j, + 1.975218468277521e-03 + 9.999980492540941e-01j, + 1.975218468277521e-03 - 9.999980492540941e-01j, + 1.786959496651858e-02 + 9.998403260399917e-01j, + 1.786959496651858e-02 - 9.998403260399917e-01j, + 9.967933660557139e-02 + 9.950196127985684e-01j, + 9.967933660557139e-02 - 9.950196127985684e-01j, + 5.013970951219547e-02 + 9.987422137518890e-01j, + 5.013970951219547e-02 - 9.987422137518890e-01j] + p2 = [4.218866331906864e-01, + 4.120110200127552e-01 + 1.361290593621978e-01j, + 4.120110200127552e-01 - 1.361290593621978e-01j, + 3.835890113632530e-01 + 2.664910809911026e-01j, + 3.835890113632530e-01 - 2.664910809911026e-01j, + 3.399195570456499e-01 + 3.863983538639875e-01j, + 3.399195570456499e-01 - 3.863983538639875e-01j, + 2.855977834508353e-01 + 4.929444399540688e-01j, + 2.855977834508353e-01 - 4.929444399540688e-01j, + 2.255765441339322e-01 + 5.851631870205766e-01j, + 2.255765441339322e-01 - 5.851631870205766e-01j, + 1.644087535815792e-01 + 6.637356937277153e-01j, + 1.644087535815792e-01 - 6.637356937277153e-01j, + -7.293633845273095e-02 + 9.739218252516307e-01j, + -7.293633845273095e-02 - 9.739218252516307e-01j, + 1.058259206358626e-01 + 7.304739464862978e-01j, + 1.058259206358626e-01 - 7.304739464862978e-01j, + -5.703971947785402e-02 + 9.291057542169088e-01j, + -5.703971947785402e-02 - 9.291057542169088e-01j, + 5.263875132656864e-02 + 7.877974334424453e-01j, + 5.263875132656864e-02 - 7.877974334424453e-01j, + -3.007943405982616e-02 + 8.846331716180016e-01j, + -3.007943405982616e-02 - 8.846331716180016e-01j, + 6.857277464483946e-03 + 8.383275456264492e-01j, + 6.857277464483946e-03 - 8.383275456264492e-01j] + k2 = 6.507068761705037e-03 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-13) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-12) + assert_allclose(k, k2, rtol=1e-11) + + def test_bandpass(self): + z, p, k = cheby2(9, 40, [0.07, 0.2], 'pass', output='zpk') + z2 = [-9.999999999999999e-01, + 3.676588029658514e-01 + 9.299607543341383e-01j, + 3.676588029658514e-01 - 9.299607543341383e-01j, + 7.009689684982283e-01 + 7.131917730894889e-01j, + 7.009689684982283e-01 - 7.131917730894889e-01j, + 7.815697973765858e-01 + 6.238178033919218e-01j, + 7.815697973765858e-01 - 6.238178033919218e-01j, + 8.063793628819866e-01 + 5.913986160941200e-01j, + 8.063793628819866e-01 - 5.913986160941200e-01j, + 1.000000000000001e+00, + 9.944493019920448e-01 + 1.052168511576739e-01j, + 9.944493019920448e-01 - 1.052168511576739e-01j, + 9.854674703367308e-01 + 1.698642543566085e-01j, + 9.854674703367308e-01 - 1.698642543566085e-01j, + 9.762751735919308e-01 + 2.165335665157851e-01j, + 9.762751735919308e-01 - 2.165335665157851e-01j, + 9.792277171575134e-01 + 2.027636011479496e-01j, + 9.792277171575134e-01 - 2.027636011479496e-01j] + p2 = [8.143803410489621e-01 + 5.411056063397541e-01j, + 8.143803410489621e-01 - 5.411056063397541e-01j, + 7.650769827887418e-01 + 5.195412242095543e-01j, + 7.650769827887418e-01 - 5.195412242095543e-01j, + 6.096241204063443e-01 + 3.568440484659796e-01j, + 6.096241204063443e-01 - 3.568440484659796e-01j, + 6.918192770246239e-01 + 4.770463577106911e-01j, + 6.918192770246239e-01 - 4.770463577106911e-01j, + 6.986241085779207e-01 + 1.146512226180060e-01j, + 6.986241085779207e-01 - 1.146512226180060e-01j, + 8.654645923909734e-01 + 1.604208797063147e-01j, + 8.654645923909734e-01 - 1.604208797063147e-01j, + 9.164831670444591e-01 + 1.969181049384918e-01j, + 9.164831670444591e-01 - 1.969181049384918e-01j, + 9.630425777594550e-01 + 2.317513360702271e-01j, + 9.630425777594550e-01 - 2.317513360702271e-01j, + 9.438104703725529e-01 + 2.193509900269860e-01j, + 9.438104703725529e-01 - 2.193509900269860e-01j] + k2 = 9.345352824659604e-03 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-13) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-13) + assert_allclose(k, k2, rtol=1e-11) + + def test_bandstop(self): + z, p, k = cheby2(6, 55, [0.1, 0.9], 'stop', output='zpk') + z2 = [6.230544895101009e-01 + 7.821784343111114e-01j, + 6.230544895101009e-01 - 7.821784343111114e-01j, + 9.086608545660115e-01 + 4.175349702471991e-01j, + 9.086608545660115e-01 - 4.175349702471991e-01j, + 9.478129721465802e-01 + 3.188268649763867e-01j, + 9.478129721465802e-01 - 3.188268649763867e-01j, + -6.230544895100982e-01 + 7.821784343111109e-01j, + -6.230544895100982e-01 - 7.821784343111109e-01j, + -9.086608545660116e-01 + 4.175349702472088e-01j, + -9.086608545660116e-01 - 4.175349702472088e-01j, + -9.478129721465784e-01 + 3.188268649763897e-01j, + -9.478129721465784e-01 - 3.188268649763897e-01j] + p2 = [-9.464094036167638e-01 + 1.720048695084344e-01j, + -9.464094036167638e-01 - 1.720048695084344e-01j, + -8.715844103386737e-01 + 1.370665039509297e-01j, + -8.715844103386737e-01 - 1.370665039509297e-01j, + -8.078751204586425e-01 + 5.729329866682983e-02j, + -8.078751204586425e-01 - 5.729329866682983e-02j, + 9.464094036167665e-01 + 1.720048695084332e-01j, + 9.464094036167665e-01 - 1.720048695084332e-01j, + 8.078751204586447e-01 + 5.729329866683007e-02j, + 8.078751204586447e-01 - 5.729329866683007e-02j, + 8.715844103386721e-01 + 1.370665039509331e-01j, + 8.715844103386721e-01 - 1.370665039509331e-01j] + k2 = 2.917823332763358e-03 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-13) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-13) + assert_allclose(k, k2, rtol=1e-11) + + def test_ba_output(self): + # with transfer function conversion, without digital conversion + b, a = cheby2(5, 20, [2010, 2100], 'stop', True) + b2 = [1.000000000000000e+00, 0, # Matlab: 6.683253076978249e-12, + 2.111512500000000e+07, 0, # Matlab: 1.134325604589552e-04, + 1.782966433781250e+14, 0, # Matlab: 7.216787944356781e+02, + 7.525901316990656e+20, 0, # Matlab: 2.039829265789886e+09, + 1.587960565565748e+27, 0, # Matlab: 2.161236218626134e+15, + 1.339913493808585e+33] + a2 = [1.000000000000000e+00, 1.849550755473371e+02, + 2.113222918998538e+07, 3.125114149732283e+09, + 1.785133457155609e+14, 1.979158697776348e+16, + 7.535048322653831e+20, 5.567966191263037e+22, + 1.589246884221346e+27, 5.871210648525566e+28, + 1.339913493808590e+33] + assert_allclose(b, b2, rtol=1e-14) + assert_allclose(a, a2, rtol=1e-14) + + def test_fs_param(self): + for fs in (900, 900.1, 1234.567): + for N in (0, 1, 2, 3, 10): + for fc in (100, 100.1, 432.12345): + for btype in ('lp', 'hp'): + ba1 = cheby2(N, 20, fc, btype, fs=fs) + ba2 = cheby2(N, 20, fc/(fs/2), btype) + assert_allclose(ba1, ba2) + for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)): + for btype in ('bp', 'bs'): + ba1 = cheby2(N, 20, fc, btype, fs=fs) + for seq in (list, tuple, array): + fcnorm = seq([f/(fs/2) for f in fc]) + ba2 = cheby2(N, 20, fcnorm, btype) + assert_allclose(ba1, ba2) + +class TestEllip: + + def test_degenerate(self): + # 0-order filter is just a passthrough + # Even-order filters have DC gain of -rp dB + # Stopband ripple factor doesn't matter + b, a = ellip(0, 10*np.log10(2), 123.456, 1, analog=True) + assert_array_almost_equal(b, [1/np.sqrt(2)]) + assert_array_equal(a, [1]) + + # 1-order filter is same for all types + b, a = ellip(1, 10*np.log10(2), 1, 1, analog=True) + assert_array_almost_equal(b, [1]) + assert_array_almost_equal(a, [1, 1]) + + z, p, k = ellip(1, 1, 55, 0.3, output='zpk') + assert_allclose(z, [-9.999999999999998e-01], rtol=1e-14) + assert_allclose(p, [-6.660721153525525e-04], rtol=1e-10) + assert_allclose(k, 5.003330360576763e-01, rtol=1e-14) + + def test_basic(self): + for N in range(25): + wn = 0.01 + z, p, k = ellip(N, 1, 40, wn, 'low', analog=True, output='zpk') + assert_(len(p) == N) + assert_(all(np.real(p) <= 0)) # No poles in right half of S-plane + + for N in range(25): + wn = 0.01 + z, p, k = ellip(N, 1, 40, wn, 'high', analog=False, output='zpk') + assert_(all(np.abs(p) <= 1)) # No poles outside unit circle + + b3, a3 = ellip(5, 3, 26, 1, analog=True) + assert_array_almost_equal(b3, [0.1420, 0, 0.3764, 0, + 0.2409], decimal=4) + assert_array_almost_equal(a3, [1, 0.5686, 1.8061, 0.8017, 0.8012, + 0.2409], decimal=4) + + b, a = ellip(3, 1, 60, [0.4, 0.7], 'stop') + assert_array_almost_equal(b, [0.3310, 0.3469, 1.1042, 0.7044, 1.1042, + 0.3469, 0.3310], decimal=4) + assert_array_almost_equal(a, [1.0000, 0.6973, 1.1441, 0.5878, 0.7323, + 0.1131, -0.0060], decimal=4) + + def test_highpass(self): + # high even order + z, p, k = ellip(24, 1, 80, 0.3, 'high', output='zpk') + z2 = [9.761875332501075e-01 + 2.169283290099910e-01j, + 9.761875332501075e-01 - 2.169283290099910e-01j, + 8.413503353963494e-01 + 5.404901600661900e-01j, + 8.413503353963494e-01 - 5.404901600661900e-01j, + 7.160082576305009e-01 + 6.980918098681732e-01j, + 7.160082576305009e-01 - 6.980918098681732e-01j, + 6.456533638965329e-01 + 7.636306264739803e-01j, + 6.456533638965329e-01 - 7.636306264739803e-01j, + 6.127321820971366e-01 + 7.902906256703928e-01j, + 6.127321820971366e-01 - 7.902906256703928e-01j, + 5.983607817490196e-01 + 8.012267936512676e-01j, + 5.983607817490196e-01 - 8.012267936512676e-01j, + 5.922577552594799e-01 + 8.057485658286990e-01j, + 5.922577552594799e-01 - 8.057485658286990e-01j, + 5.896952092563588e-01 + 8.076258788449631e-01j, + 5.896952092563588e-01 - 8.076258788449631e-01j, + 5.886248765538837e-01 + 8.084063054565607e-01j, + 5.886248765538837e-01 - 8.084063054565607e-01j, + 5.881802711123132e-01 + 8.087298490066037e-01j, + 5.881802711123132e-01 - 8.087298490066037e-01j, + 5.879995719101164e-01 + 8.088612386766461e-01j, + 5.879995719101164e-01 - 8.088612386766461e-01j, + 5.879354086709576e-01 + 8.089078780868164e-01j, + 5.879354086709576e-01 - 8.089078780868164e-01j] + p2 = [-3.184805259081650e-01 + 4.206951906775851e-01j, + -3.184805259081650e-01 - 4.206951906775851e-01j, + 1.417279173459985e-01 + 7.903955262836452e-01j, + 1.417279173459985e-01 - 7.903955262836452e-01j, + 4.042881216964651e-01 + 8.309042239116594e-01j, + 4.042881216964651e-01 - 8.309042239116594e-01j, + 5.128964442789670e-01 + 8.229563236799665e-01j, + 5.128964442789670e-01 - 8.229563236799665e-01j, + 5.569614712822724e-01 + 8.155957702908510e-01j, + 5.569614712822724e-01 - 8.155957702908510e-01j, + 5.750478870161392e-01 + 8.118633973883931e-01j, + 5.750478870161392e-01 - 8.118633973883931e-01j, + 5.825314018170804e-01 + 8.101960910679270e-01j, + 5.825314018170804e-01 - 8.101960910679270e-01j, + 5.856397379751872e-01 + 8.094825218722543e-01j, + 5.856397379751872e-01 - 8.094825218722543e-01j, + 5.869326035251949e-01 + 8.091827531557583e-01j, + 5.869326035251949e-01 - 8.091827531557583e-01j, + 5.874697218855733e-01 + 8.090593298213502e-01j, + 5.874697218855733e-01 - 8.090593298213502e-01j, + 5.876904783532237e-01 + 8.090127161018823e-01j, + 5.876904783532237e-01 - 8.090127161018823e-01j, + 5.877753105317594e-01 + 8.090050577978136e-01j, + 5.877753105317594e-01 - 8.090050577978136e-01j] + k2 = 4.918081266957108e-02 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-4) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-4) + assert_allclose(k, k2, rtol=1e-3) + + # high odd order + z, p, k = ellip(23, 1, 70, 0.5, 'high', output='zpk') + z2 = [9.999999999998661e-01, + 6.603717261750994e-01 + 7.509388678638675e-01j, + 6.603717261750994e-01 - 7.509388678638675e-01j, + 2.788635267510325e-01 + 9.603307416968041e-01j, + 2.788635267510325e-01 - 9.603307416968041e-01j, + 1.070215532544218e-01 + 9.942567008268131e-01j, + 1.070215532544218e-01 - 9.942567008268131e-01j, + 4.049427369978163e-02 + 9.991797705105507e-01j, + 4.049427369978163e-02 - 9.991797705105507e-01j, + 1.531059368627931e-02 + 9.998827859909265e-01j, + 1.531059368627931e-02 - 9.998827859909265e-01j, + 5.808061438534933e-03 + 9.999831330689181e-01j, + 5.808061438534933e-03 - 9.999831330689181e-01j, + 2.224277847754599e-03 + 9.999975262909676e-01j, + 2.224277847754599e-03 - 9.999975262909676e-01j, + 8.731857107534554e-04 + 9.999996187732845e-01j, + 8.731857107534554e-04 - 9.999996187732845e-01j, + 3.649057346914968e-04 + 9.999999334218996e-01j, + 3.649057346914968e-04 - 9.999999334218996e-01j, + 1.765538109802615e-04 + 9.999999844143768e-01j, + 1.765538109802615e-04 - 9.999999844143768e-01j, + 1.143655290967426e-04 + 9.999999934602630e-01j, + 1.143655290967426e-04 - 9.999999934602630e-01j] + p2 = [-6.322017026545028e-01, + -4.648423756662754e-01 + 5.852407464440732e-01j, + -4.648423756662754e-01 - 5.852407464440732e-01j, + -2.249233374627773e-01 + 8.577853017985717e-01j, + -2.249233374627773e-01 - 8.577853017985717e-01j, + -9.234137570557621e-02 + 9.506548198678851e-01j, + -9.234137570557621e-02 - 9.506548198678851e-01j, + -3.585663561241373e-02 + 9.821494736043981e-01j, + -3.585663561241373e-02 - 9.821494736043981e-01j, + -1.363917242312723e-02 + 9.933844128330656e-01j, + -1.363917242312723e-02 - 9.933844128330656e-01j, + -5.131505238923029e-03 + 9.975221173308673e-01j, + -5.131505238923029e-03 - 9.975221173308673e-01j, + -1.904937999259502e-03 + 9.990680819857982e-01j, + -1.904937999259502e-03 - 9.990680819857982e-01j, + -6.859439885466834e-04 + 9.996492201426826e-01j, + -6.859439885466834e-04 - 9.996492201426826e-01j, + -2.269936267937089e-04 + 9.998686250679161e-01j, + -2.269936267937089e-04 - 9.998686250679161e-01j, + -5.687071588789117e-05 + 9.999527573294513e-01j, + -5.687071588789117e-05 - 9.999527573294513e-01j, + -6.948417068525226e-07 + 9.999882737700173e-01j, + -6.948417068525226e-07 - 9.999882737700173e-01j] + k2 = 1.220910020289434e-02 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-4) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-4) + assert_allclose(k, k2, rtol=1e-3) + + def test_bandpass(self): + z, p, k = ellip(7, 1, 40, [0.07, 0.2], 'pass', output='zpk') + z2 = [-9.999999999999991e-01, + 6.856610961780020e-01 + 7.279209168501619e-01j, + 6.856610961780020e-01 - 7.279209168501619e-01j, + 7.850346167691289e-01 + 6.194518952058737e-01j, + 7.850346167691289e-01 - 6.194518952058737e-01j, + 7.999038743173071e-01 + 6.001281461922627e-01j, + 7.999038743173071e-01 - 6.001281461922627e-01j, + 9.999999999999999e-01, + 9.862938983554124e-01 + 1.649980183725925e-01j, + 9.862938983554124e-01 - 1.649980183725925e-01j, + 9.788558330548762e-01 + 2.045513580850601e-01j, + 9.788558330548762e-01 - 2.045513580850601e-01j, + 9.771155231720003e-01 + 2.127093189691258e-01j, + 9.771155231720003e-01 - 2.127093189691258e-01j] + p2 = [8.063992755498643e-01 + 5.858071374778874e-01j, + 8.063992755498643e-01 - 5.858071374778874e-01j, + 8.050395347071724e-01 + 5.639097428109795e-01j, + 8.050395347071724e-01 - 5.639097428109795e-01j, + 8.113124936559144e-01 + 4.855241143973142e-01j, + 8.113124936559144e-01 - 4.855241143973142e-01j, + 8.665595314082394e-01 + 3.334049560919331e-01j, + 8.665595314082394e-01 - 3.334049560919331e-01j, + 9.412369011968871e-01 + 2.457616651325908e-01j, + 9.412369011968871e-01 - 2.457616651325908e-01j, + 9.679465190411238e-01 + 2.228772501848216e-01j, + 9.679465190411238e-01 - 2.228772501848216e-01j, + 9.747235066273385e-01 + 2.178937926146544e-01j, + 9.747235066273385e-01 - 2.178937926146544e-01j] + k2 = 8.354782670263239e-03 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-4) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-4) + assert_allclose(k, k2, rtol=1e-3) + + z, p, k = ellip(5, 1, 75, [90.5, 110.5], 'pass', True, 'zpk') + z2 = [-5.583607317695175e-14 + 1.433755965989225e+02j, + -5.583607317695175e-14 - 1.433755965989225e+02j, + 5.740106416459296e-14 + 1.261678754570291e+02j, + 5.740106416459296e-14 - 1.261678754570291e+02j, + -2.199676239638652e-14 + 6.974861996895196e+01j, + -2.199676239638652e-14 - 6.974861996895196e+01j, + -3.372595657044283e-14 + 7.926145989044531e+01j, + -3.372595657044283e-14 - 7.926145989044531e+01j, + 0] + p2 = [-8.814960004852743e-01 + 1.104124501436066e+02j, + -8.814960004852743e-01 - 1.104124501436066e+02j, + -2.477372459140184e+00 + 1.065638954516534e+02j, + -2.477372459140184e+00 - 1.065638954516534e+02j, + -3.072156842945799e+00 + 9.995404870405324e+01j, + -3.072156842945799e+00 - 9.995404870405324e+01j, + -2.180456023925693e+00 + 9.379206865455268e+01j, + -2.180456023925693e+00 - 9.379206865455268e+01j, + -7.230484977485752e-01 + 9.056598800801140e+01j, + -7.230484977485752e-01 - 9.056598800801140e+01j] + k2 = 3.774571622827070e-02 + assert_allclose(sorted(z, key=np.imag), + sorted(z2, key=np.imag), rtol=1e-4) + assert_allclose(sorted(p, key=np.imag), + sorted(p2, key=np.imag), rtol=1e-6) + assert_allclose(k, k2, rtol=1e-3) + + def test_bandstop(self): + z, p, k = ellip(8, 1, 65, [0.2, 0.4], 'stop', output='zpk') + z2 = [3.528578094286510e-01 + 9.356769561794296e-01j, + 3.528578094286510e-01 - 9.356769561794296e-01j, + 3.769716042264783e-01 + 9.262248159096587e-01j, + 3.769716042264783e-01 - 9.262248159096587e-01j, + 4.406101783111199e-01 + 8.976985411420985e-01j, + 4.406101783111199e-01 - 8.976985411420985e-01j, + 5.539386470258847e-01 + 8.325574907062760e-01j, + 5.539386470258847e-01 - 8.325574907062760e-01j, + 6.748464963023645e-01 + 7.379581332490555e-01j, + 6.748464963023645e-01 - 7.379581332490555e-01j, + 7.489887970285254e-01 + 6.625826604475596e-01j, + 7.489887970285254e-01 - 6.625826604475596e-01j, + 7.913118471618432e-01 + 6.114127579150699e-01j, + 7.913118471618432e-01 - 6.114127579150699e-01j, + 7.806804740916381e-01 + 6.249303940216475e-01j, + 7.806804740916381e-01 - 6.249303940216475e-01j] + + p2 = [-1.025299146693730e-01 + 5.662682444754943e-01j, + -1.025299146693730e-01 - 5.662682444754943e-01j, + 1.698463595163031e-01 + 8.926678667070186e-01j, + 1.698463595163031e-01 - 8.926678667070186e-01j, + 2.750532687820631e-01 + 9.351020170094005e-01j, + 2.750532687820631e-01 - 9.351020170094005e-01j, + 3.070095178909486e-01 + 9.457373499553291e-01j, + 3.070095178909486e-01 - 9.457373499553291e-01j, + 7.695332312152288e-01 + 2.792567212705257e-01j, + 7.695332312152288e-01 - 2.792567212705257e-01j, + 8.083818999225620e-01 + 4.990723496863960e-01j, + 8.083818999225620e-01 - 4.990723496863960e-01j, + 8.066158014414928e-01 + 5.649811440393374e-01j, + 8.066158014414928e-01 - 5.649811440393374e-01j, + 8.062787978834571e-01 + 5.855780880424964e-01j, + 8.062787978834571e-01 - 5.855780880424964e-01j] + k2 = 2.068622545291259e-01 + assert_allclose(sorted(z, key=np.angle), + sorted(z2, key=np.angle), rtol=1e-6) + assert_allclose(sorted(p, key=np.angle), + sorted(p2, key=np.angle), rtol=1e-5) + assert_allclose(k, k2, rtol=1e-5) + + def test_ba_output(self): + # with transfer function conversion, without digital conversion + b, a = ellip(5, 1, 40, [201, 240], 'stop', True) + b2 = [ + 1.000000000000000e+00, 0, # Matlab: 1.743506051190569e-13, + 2.426561778314366e+05, 0, # Matlab: 3.459426536825722e-08, + 2.348218683400168e+10, 0, # Matlab: 2.559179747299313e-03, + 1.132780692872241e+15, 0, # Matlab: 8.363229375535731e+01, + 2.724038554089566e+19, 0, # Matlab: 1.018700994113120e+06, + 2.612380874940186e+23 + ] + a2 = [ + 1.000000000000000e+00, 1.337266601804649e+02, + 2.486725353510667e+05, 2.628059713728125e+07, + 2.436169536928770e+10, 1.913554568577315e+12, + 1.175208184614438e+15, 6.115751452473410e+16, + 2.791577695211466e+19, 7.241811142725384e+20, + 2.612380874940182e+23 + ] + assert_allclose(b, b2, rtol=1e-6) + assert_allclose(a, a2, rtol=1e-4) + + def test_fs_param(self): + for fs in (900, 900.1, 1234.567): + for N in (0, 1, 2, 3, 10): + for fc in (100, 100.1, 432.12345): + for btype in ('lp', 'hp'): + ba1 = ellip(N, 1, 20, fc, btype, fs=fs) + ba2 = ellip(N, 1, 20, fc/(fs/2), btype) + assert_allclose(ba1, ba2) + for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)): + for btype in ('bp', 'bs'): + ba1 = ellip(N, 1, 20, fc, btype, fs=fs) + for seq in (list, tuple, array): + fcnorm = seq([f/(fs/2) for f in fc]) + ba2 = ellip(N, 1, 20, fcnorm, btype) + assert_allclose(ba1, ba2) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + iirnotch(0.06, 30, fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none"): + iirnotch(0.06, 30, fs=None) + + +def test_sos_consistency(): + # Consistency checks of output='sos' for the specialized IIR filter + # design functions. + design_funcs = [(bessel, (0.1,)), + (butter, (0.1,)), + (cheby1, (45.0, 0.1)), + (cheby2, (0.087, 0.1)), + (ellip, (0.087, 45, 0.1))] + for func, args in design_funcs: + name = func.__name__ + + b, a = func(2, *args, output='ba') + sos = func(2, *args, output='sos') + assert_allclose(sos, [np.hstack((b, a))], err_msg="%s(2,...)" % name) + + zpk = func(3, *args, output='zpk') + sos = func(3, *args, output='sos') + assert_allclose(sos, zpk2sos(*zpk), err_msg="%s(3,...)" % name) + + zpk = func(4, *args, output='zpk') + sos = func(4, *args, output='sos') + assert_allclose(sos, zpk2sos(*zpk), err_msg="%s(4,...)" % name) + + +class TestIIRNotch: + + def test_ba_output(self): + # Compare coefficients with Matlab ones + # for the equivalent input: + b, a = iirnotch(0.06, 30) + b2 = [ + 9.9686824e-01, -1.9584219e+00, + 9.9686824e-01 + ] + a2 = [ + 1.0000000e+00, -1.9584219e+00, + 9.9373647e-01 + ] + + assert_allclose(b, b2, rtol=1e-8) + assert_allclose(a, a2, rtol=1e-8) + + def test_frequency_response(self): + # Get filter coefficients + b, a = iirnotch(0.3, 30) + + # Get frequency response + w, h = freqz(b, a, 1000) + + # Pick 5 point + p = [200, # w0 = 0.200 + 295, # w0 = 0.295 + 300, # w0 = 0.300 + 305, # w0 = 0.305 + 400] # w0 = 0.400 + + # Get frequency response correspondent to each of those points + hp = h[p] + + # Check if the frequency response fulfill the specifications: + # hp[0] and hp[4] correspond to frequencies distant from + # w0 = 0.3 and should be close to 1 + assert_allclose(abs(hp[0]), 1, rtol=1e-2) + assert_allclose(abs(hp[4]), 1, rtol=1e-2) + + # hp[1] and hp[3] correspond to frequencies approximately + # on the edges of the passband and should be close to -3dB + assert_allclose(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2) + assert_allclose(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2) + + # hp[2] correspond to the frequency that should be removed + # the frequency response should be very close to 0 + assert_allclose(abs(hp[2]), 0, atol=1e-10) + + def test_errors(self): + # Exception should be raised if w0 > 1 or w0 <0 + assert_raises(ValueError, iirnotch, w0=2, Q=30) + assert_raises(ValueError, iirnotch, w0=-1, Q=30) + + # Exception should be raised if any of the parameters + # are not float (or cannot be converted to one) + assert_raises(ValueError, iirnotch, w0="blabla", Q=30) + assert_raises(TypeError, iirnotch, w0=-1, Q=[1, 2, 3]) + + def test_fs_param(self): + # Get filter coefficients + b, a = iirnotch(1500, 30, fs=10000) + + # Get frequency response + w, h = freqz(b, a, 1000, fs=10000) + + # Pick 5 point + p = [200, # w0 = 1000 + 295, # w0 = 1475 + 300, # w0 = 1500 + 305, # w0 = 1525 + 400] # w0 = 2000 + + # Get frequency response correspondent to each of those points + hp = h[p] + + # Check if the frequency response fulfill the specifications: + # hp[0] and hp[4] correspond to frequencies distant from + # w0 = 1500 and should be close to 1 + assert_allclose(abs(hp[0]), 1, rtol=1e-2) + assert_allclose(abs(hp[4]), 1, rtol=1e-2) + + # hp[1] and hp[3] correspond to frequencies approximately + # on the edges of the passband and should be close to -3dB + assert_allclose(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2) + assert_allclose(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2) + + # hp[2] correspond to the frequency that should be removed + # the frequency response should be very close to 0 + assert_allclose(abs(hp[2]), 0, atol=1e-10) + + +class TestIIRPeak: + + def test_ba_output(self): + # Compare coefficients with Matlab ones + # for the equivalent input: + b, a = iirpeak(0.06, 30) + b2 = [ + 3.131764229e-03, 0, + -3.131764229e-03 + ] + a2 = [ + 1.0000000e+00, -1.958421917e+00, + 9.9373647e-01 + ] + assert_allclose(b, b2, rtol=1e-8) + assert_allclose(a, a2, rtol=1e-8) + + def test_frequency_response(self): + # Get filter coefficients + b, a = iirpeak(0.3, 30) + + # Get frequency response + w, h = freqz(b, a, 1000) + + # Pick 5 point + p = [30, # w0 = 0.030 + 295, # w0 = 0.295 + 300, # w0 = 0.300 + 305, # w0 = 0.305 + 800] # w0 = 0.800 + + # Get frequency response correspondent to each of those points + hp = h[p] + + # Check if the frequency response fulfill the specifications: + # hp[0] and hp[4] correspond to frequencies distant from + # w0 = 0.3 and should be close to 0 + assert_allclose(abs(hp[0]), 0, atol=1e-2) + assert_allclose(abs(hp[4]), 0, atol=1e-2) + + # hp[1] and hp[3] correspond to frequencies approximately + # on the edges of the passband and should be close to 10**(-3/20) + assert_allclose(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2) + assert_allclose(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2) + + # hp[2] correspond to the frequency that should be retained and + # the frequency response should be very close to 1 + assert_allclose(abs(hp[2]), 1, rtol=1e-10) + + def test_errors(self): + # Exception should be raised if w0 > 1 or w0 <0 + assert_raises(ValueError, iirpeak, w0=2, Q=30) + assert_raises(ValueError, iirpeak, w0=-1, Q=30) + + # Exception should be raised if any of the parameters + # are not float (or cannot be converted to one) + assert_raises(ValueError, iirpeak, w0="blabla", Q=30) + assert_raises(TypeError, iirpeak, w0=-1, Q=[1, 2, 3]) + + def test_fs_param(self): + # Get filter coefficients + b, a = iirpeak(1200, 30, fs=8000) + + # Get frequency response + w, h = freqz(b, a, 1000, fs=8000) + + # Pick 5 point + p = [30, # w0 = 120 + 295, # w0 = 1180 + 300, # w0 = 1200 + 305, # w0 = 1220 + 800] # w0 = 3200 + + # Get frequency response correspondent to each of those points + hp = h[p] + + # Check if the frequency response fulfill the specifications: + # hp[0] and hp[4] correspond to frequencies distant from + # w0 = 1200 and should be close to 0 + assert_allclose(abs(hp[0]), 0, atol=1e-2) + assert_allclose(abs(hp[4]), 0, atol=1e-2) + + # hp[1] and hp[3] correspond to frequencies approximately + # on the edges of the passband and should be close to 10**(-3/20) + assert_allclose(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2) + assert_allclose(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2) + + # hp[2] correspond to the frequency that should be retained and + # the frequency response should be very close to 1 + assert_allclose(abs(hp[2]), 1, rtol=1e-10) + + +class TestIIRComb: + # Test erroneous input cases + def test_invalid_input(self): + # w0 is <= 0 or >= fs / 2 + fs = 1000 + for args in [(-fs, 30), (0, 35), (fs / 2, 40), (fs, 35)]: + with pytest.raises(ValueError, match='w0 must be between '): + iircomb(*args, fs=fs) + + # fs is not divisible by w0 + for args in [(120, 30), (157, 35)]: + with pytest.raises(ValueError, match='fs must be divisible '): + iircomb(*args, fs=fs) + + # https://github.com/scipy/scipy/issues/14043#issuecomment-1107349140 + # Previously, fs=44100, w0=49.999 was rejected, but fs=2, + # w0=49.999/int(44100/2) was accepted. Now it is rejected, too. + with pytest.raises(ValueError, match='fs must be divisible '): + iircomb(w0=49.999/int(44100/2), Q=30) + + with pytest.raises(ValueError, match='fs must be divisible '): + iircomb(w0=49.999, Q=30, fs=44100) + + # Filter type is not notch or peak + for args in [(0.2, 30, 'natch'), (0.5, 35, 'comb')]: + with pytest.raises(ValueError, match='ftype must be '): + iircomb(*args) + + # Verify that the filter's frequency response contains a + # notch at the cutoff frequency + @pytest.mark.parametrize('ftype', ('notch', 'peak')) + def test_frequency_response(self, ftype): + # Create a notching or peaking comb filter at 1000 Hz + b, a = iircomb(1000, 30, ftype=ftype, fs=10000) + + # Compute the frequency response + freqs, response = freqz(b, a, 1000, fs=10000) + + # Find the notch using argrelextrema + comb_points = argrelextrema(abs(response), np.less)[0] + + # Verify that the first notch sits at 1000 Hz + comb1 = comb_points[0] + assert_allclose(freqs[comb1], 1000) + + # Verify pass_zero parameter + @pytest.mark.parametrize('ftype,pass_zero,peak,notch', + [('peak', True, 123.45, 61.725), + ('peak', False, 61.725, 123.45), + ('peak', None, 61.725, 123.45), + ('notch', None, 61.725, 123.45), + ('notch', True, 123.45, 61.725), + ('notch', False, 61.725, 123.45)]) + def test_pass_zero(self, ftype, pass_zero, peak, notch): + # Create a notching or peaking comb filter + b, a = iircomb(123.45, 30, ftype=ftype, fs=1234.5, pass_zero=pass_zero) + + # Compute the frequency response + freqs, response = freqz(b, a, [peak, notch], fs=1234.5) + + # Verify that expected notches are notches and peaks are peaks + assert abs(response[0]) > 0.99 + assert abs(response[1]) < 1e-10 + + # All built-in IIR filters are real, so should have perfectly + # symmetrical poles and zeros. Then ba representation (using + # numpy.poly) will be purely real instead of having negligible + # imaginary parts. + def test_iir_symmetry(self): + b, a = iircomb(400, 30, fs=24000) + z, p, k = tf2zpk(b, a) + assert_array_equal(sorted(z), sorted(z.conj())) + assert_array_equal(sorted(p), sorted(p.conj())) + assert_equal(k, np.real(k)) + + assert issubclass(b.dtype.type, np.floating) + assert issubclass(a.dtype.type, np.floating) + + # Verify filter coefficients with MATLAB's iircomb function + def test_ba_output(self): + b_notch, a_notch = iircomb(60, 35, ftype='notch', fs=600) + b_notch2 = [0.957020174408697, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -0.957020174408697] + a_notch2 = [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -0.914040348817395] + assert_allclose(b_notch, b_notch2) + assert_allclose(a_notch, a_notch2) + + b_peak, a_peak = iircomb(60, 35, ftype='peak', fs=600) + b_peak2 = [0.0429798255913026, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -0.0429798255913026] + a_peak2 = [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.914040348817395] + assert_allclose(b_peak, b_peak2) + assert_allclose(a_peak, a_peak2) + + # Verify that https://github.com/scipy/scipy/issues/14043 is fixed + def test_nearest_divisor(self): + # Create a notching comb filter + b, a = iircomb(50/int(44100/2), 50.0, ftype='notch') + + # Compute the frequency response at an upper harmonic of 50 + freqs, response = freqz(b, a, [22000], fs=44100) + + # Before bug fix, this would produce N = 881, so that 22 kHz was ~0 dB. + # Now N = 882 correctly and 22 kHz should be a notch <-220 dB + assert abs(response[0]) < 1e-10 + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + iircomb(1000, 30, fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none"): + iircomb(1000, 30, fs=None) + + +class TestIIRDesign: + + def test_exceptions(self): + with pytest.raises(ValueError, match="the same shape"): + iirdesign(0.2, [0.1, 0.3], 1, 40) + with pytest.raises(ValueError, match="the same shape"): + iirdesign(np.array([[0.3, 0.6], [0.3, 0.6]]), + np.array([[0.4, 0.5], [0.4, 0.5]]), 1, 40) + + # discrete filter with non-positive frequency + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(0, 0.5, 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(-0.1, 0.5, 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(0.1, 0, 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(0.1, -0.5, 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0, 0.3], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([-0.1, 0.3], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, -0.3], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [0, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [-0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [0.1, 0], 1, 40) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [0.1, -0.5], 1, 40) + + # analog filter with negative frequency + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(-0.1, 0.5, 1, 40, analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign(0.1, -0.5, 1, 40, analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([-0.1, 0.3], [0.1, 0.5], 1, 40, analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, -0.3], [0.1, 0.5], 1, 40, analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [-0.1, 0.5], 1, 40, analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirdesign([0.1, 0.3], [0.1, -0.5], 1, 40, analog=True) + + # discrete filter with fs=None, freq > 1 + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign(1, 0.5, 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign(1.1, 0.5, 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign(0.1, 1, 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign(0.1, 1.5, 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([1, 0.3], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([1.1, 0.3], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 1], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 1.1], [0.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 0.3], [1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 0.3], [1.1, 0.5], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 0.3], [0.1, 1], 1, 40) + with pytest.raises(ValueError, match="must be less than 1"): + iirdesign([0.1, 0.3], [0.1, 1.5], 1, 40) + + # discrete filter with fs>2, wp, ws < fs/2 must pass + iirdesign(100, 500, 1, 40, fs=2000) + iirdesign(500, 100, 1, 40, fs=2000) + iirdesign([200, 400], [100, 500], 1, 40, fs=2000) + iirdesign([100, 500], [200, 400], 1, 40, fs=2000) + + # discrete filter with fs>2, freq > fs/2: this must raise + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign(1000, 400, 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign(1100, 500, 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign(100, 1000, 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign(100, 1100, 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([1000, 400], [100, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([1100, 400], [100, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 1000], [100, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 1100], [100, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 400], [1000, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 400], [1100, 500], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 400], [100, 1000], 1, 40, fs=2000) + with pytest.raises(ValueError, match="must be less than fs/2"): + iirdesign([200, 400], [100, 1100], 1, 40, fs=2000) + + with pytest.raises(ValueError, match="strictly inside stopband"): + iirdesign([0.1, 0.4], [0.5, 0.6], 1, 40) + with pytest.raises(ValueError, match="strictly inside stopband"): + iirdesign([0.5, 0.6], [0.1, 0.4], 1, 40) + with pytest.raises(ValueError, match="strictly inside stopband"): + iirdesign([0.3, 0.6], [0.4, 0.7], 1, 40) + with pytest.raises(ValueError, match="strictly inside stopband"): + iirdesign([0.4, 0.7], [0.3, 0.6], 1, 40) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + iirfilter(1, 1, btype="low", fs=np.array([10, 20])) + + +class TestIIRFilter: + + def test_symmetry(self): + # All built-in IIR filters are real, so should have perfectly + # symmetrical poles and zeros. Then ba representation (using + # numpy.poly) will be purely real instead of having negligible + # imaginary parts. + for N in np.arange(1, 26): + for ftype in ('butter', 'bessel', 'cheby1', 'cheby2', 'ellip'): + z, p, k = iirfilter(N, 1.1, 1, 20, 'low', analog=True, + ftype=ftype, output='zpk') + assert_array_equal(sorted(z), sorted(z.conj())) + assert_array_equal(sorted(p), sorted(p.conj())) + assert_equal(k, np.real(k)) + + b, a = iirfilter(N, 1.1, 1, 20, 'low', analog=True, + ftype=ftype, output='ba') + assert_(issubclass(b.dtype.type, np.floating)) + assert_(issubclass(a.dtype.type, np.floating)) + + def test_int_inputs(self): + # Using integer frequency arguments and large N should not produce + # numpy integers that wraparound to negative numbers + k = iirfilter(24, 100, btype='low', analog=True, ftype='bessel', + output='zpk')[2] + k2 = 9.999999999999989e+47 + assert_allclose(k, k2) + + def test_invalid_wn_size(self): + # low and high have 1 Wn, band and stop have 2 Wn + assert_raises(ValueError, iirfilter, 1, [0.1, 0.9], btype='low') + assert_raises(ValueError, iirfilter, 1, [0.2, 0.5], btype='high') + assert_raises(ValueError, iirfilter, 1, 0.2, btype='bp') + assert_raises(ValueError, iirfilter, 1, 400, btype='bs', analog=True) + + def test_invalid_wn_range(self): + # For digital filters, 0 <= Wn <= 1 + assert_raises(ValueError, iirfilter, 1, 2, btype='low') + assert_raises(ValueError, iirfilter, 1, [0.5, 1], btype='band') + assert_raises(ValueError, iirfilter, 1, [0., 0.5], btype='band') + assert_raises(ValueError, iirfilter, 1, -1, btype='high') + assert_raises(ValueError, iirfilter, 1, [1, 2], btype='band') + assert_raises(ValueError, iirfilter, 1, [10, 20], btype='stop') + + # analog=True with non-positive critical frequencies + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, 0, btype='low', analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, -1, btype='low', analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, [0, 100], analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, [-1, 100], analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, [10, 0], analog=True) + with pytest.raises(ValueError, match="must be greater than 0"): + iirfilter(2, [10, -1], analog=True) + + def test_analog_sos(self): + # first order Butterworth filter with Wn = 1 has tf 1/(s+1) + sos = [[0., 0., 1., 0., 1., 1.]] + sos2 = iirfilter(N=1, Wn=1, btype='low', analog=True, output='sos') + assert_array_almost_equal(sos, sos2) + + def test_wn1_ge_wn0(self): + # gh-15773: should raise error if Wn[0] >= Wn[1] + with pytest.raises(ValueError, + match=r"Wn\[0\] must be less than Wn\[1\]"): + iirfilter(2, [0.5, 0.5]) + with pytest.raises(ValueError, + match=r"Wn\[0\] must be less than Wn\[1\]"): + iirfilter(2, [0.6, 0.5]) + + +class TestGroupDelay: + def test_identity_filter(self): + w, gd = group_delay((1, 1)) + assert_array_almost_equal(w, pi * np.arange(512) / 512) + assert_array_almost_equal(gd, np.zeros(512)) + w, gd = group_delay((1, 1), whole=True) + assert_array_almost_equal(w, 2 * pi * np.arange(512) / 512) + assert_array_almost_equal(gd, np.zeros(512)) + + def test_fir(self): + # Let's design linear phase FIR and check that the group delay + # is constant. + N = 100 + b = firwin(N + 1, 0.1) + w, gd = group_delay((b, 1)) + assert_allclose(gd, 0.5 * N) + + def test_iir(self): + # Let's design Butterworth filter and test the group delay at + # some points against MATLAB answer. + b, a = butter(4, 0.1) + w = np.linspace(0, pi, num=10, endpoint=False) + w, gd = group_delay((b, a), w=w) + matlab_gd = np.array([8.249313898506037, 11.958947880907104, + 2.452325615326005, 1.048918665702008, + 0.611382575635897, 0.418293269460578, + 0.317932917836572, 0.261371844762525, + 0.229038045801298, 0.212185774208521]) + assert_array_almost_equal(gd, matlab_gd) + + def test_singular(self): + # Let's create a filter with zeros and poles on the unit circle and + # check if warnings are raised at those frequencies. + z1 = np.exp(1j * 0.1 * pi) + z2 = np.exp(1j * 0.25 * pi) + p1 = np.exp(1j * 0.5 * pi) + p2 = np.exp(1j * 0.8 * pi) + b = np.convolve([1, -z1], [1, -z2]) + a = np.convolve([1, -p1], [1, -p2]) + w = np.array([0.1 * pi, 0.25 * pi, -0.5 * pi, -0.8 * pi]) + + w, gd = assert_warns(UserWarning, group_delay, (b, a), w=w) + + def test_backward_compat(self): + # For backward compatibility, test if None act as a wrapper for default + w1, gd1 = group_delay((1, 1)) + w2, gd2 = group_delay((1, 1), None) + assert_array_almost_equal(w1, w2) + assert_array_almost_equal(gd1, gd2) + + def test_fs_param(self): + # Let's design Butterworth filter and test the group delay at + # some points against the normalized frequency answer. + b, a = butter(4, 4800, fs=96000) + w = np.linspace(0, 96000/2, num=10, endpoint=False) + w, gd = group_delay((b, a), w=w, fs=96000) + norm_gd = np.array([8.249313898506037, 11.958947880907104, + 2.452325615326005, 1.048918665702008, + 0.611382575635897, 0.418293269460578, + 0.317932917836572, 0.261371844762525, + 0.229038045801298, 0.212185774208521]) + assert_array_almost_equal(gd, norm_gd) + + def test_w_or_N_types(self): + # Measure at 8 equally-spaced points + for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8), + np.array(8)): + w, gd = group_delay((1, 1), N) + assert_array_almost_equal(w, pi * np.arange(8) / 8) + assert_array_almost_equal(gd, np.zeros(8)) + + # Measure at frequency 8 rad/sec + for w in (8.0, 8.0+0j): + w_out, gd = group_delay((1, 1), w) + assert_array_almost_equal(w_out, [8]) + assert_array_almost_equal(gd, [0]) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + group_delay((1, 1), fs=np.array([10, 20])) + + with pytest.raises(ValueError, match="Sampling.*be none"): + group_delay((1, 1), fs=None) + + +class TestGammatone: + # Test erroneous input cases. + def test_invalid_input(self): + # Cutoff frequency is <= 0 or >= fs / 2. + fs = 16000 + for args in [(-fs, 'iir'), (0, 'fir'), (fs / 2, 'iir'), (fs, 'fir')]: + with pytest.raises(ValueError, match='The frequency must be ' + 'between '): + gammatone(*args, fs=fs) + + # Filter type is not fir or iir + for args in [(440, 'fie'), (220, 'it')]: + with pytest.raises(ValueError, match='ftype must be '): + gammatone(*args, fs=fs) + + # Order is <= 0 or > 24 for FIR filter. + for args in [(440, 'fir', -50), (220, 'fir', 0), (110, 'fir', 25), + (55, 'fir', 50)]: + with pytest.raises(ValueError, match='Invalid order: '): + gammatone(*args, numtaps=None, fs=fs) + + # Verify that the filter's frequency response is approximately + # 1 at the cutoff frequency. + def test_frequency_response(self): + fs = 16000 + ftypes = ['fir', 'iir'] + for ftype in ftypes: + # Create a gammatone filter centered at 1000 Hz. + b, a = gammatone(1000, ftype, fs=fs) + + # Calculate the frequency response. + freqs, response = freqz(b, a) + + # Determine peak magnitude of the response + # and corresponding frequency. + response_max = np.max(np.abs(response)) + freq_hz = freqs[np.argmax(np.abs(response))] / ((2 * np.pi) / fs) + + # Check that the peak magnitude is 1 and the frequency is 1000 Hz. + assert_allclose(response_max, 1, rtol=1e-2) + assert_allclose(freq_hz, 1000, rtol=1e-2) + + # All built-in IIR filters are real, so should have perfectly + # symmetrical poles and zeros. Then ba representation (using + # numpy.poly) will be purely real instead of having negligible + # imaginary parts. + def test_iir_symmetry(self): + b, a = gammatone(440, 'iir', fs=24000) + z, p, k = tf2zpk(b, a) + assert_array_equal(sorted(z), sorted(z.conj())) + assert_array_equal(sorted(p), sorted(p.conj())) + assert_equal(k, np.real(k)) + + assert_(issubclass(b.dtype.type, np.floating)) + assert_(issubclass(a.dtype.type, np.floating)) + + # Verify FIR filter coefficients with the paper's + # Mathematica implementation + def test_fir_ba_output(self): + b, _ = gammatone(15, 'fir', fs=1000) + b2 = [0.0, 2.2608075649884e-04, + 1.5077903981357e-03, 4.2033687753998e-03, + 8.1508962726503e-03, 1.2890059089154e-02, + 1.7833890391666e-02, 2.2392613558564e-02, + 2.6055195863104e-02, 2.8435872863284e-02, + 2.9293319149544e-02, 2.852976858014e-02, + 2.6176557156294e-02, 2.2371510270395e-02, + 1.7332485267759e-02] + assert_allclose(b, b2) + + # Verify IIR filter coefficients with the paper's MATLAB implementation + def test_iir_ba_output(self): + b, a = gammatone(440, 'iir', fs=16000) + b2 = [1.31494461367464e-06, -5.03391196645395e-06, + 7.00649426000897e-06, -4.18951968419854e-06, + 9.02614910412011e-07] + a2 = [1.0, -7.65646235454218, + 25.7584699322366, -49.7319214483238, + 60.2667361289181, -46.9399590980486, + 22.9474798808461, -6.43799381299034, + 0.793651554625368] + assert_allclose(b, b2) + assert_allclose(a, a2) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + gammatone(440, 'iir', fs=np.array([10, 20])) + + +class TestOrderFilter: + def test_doc_example(self): + x = np.arange(25).reshape(5, 5) + domain = np.identity(3) + + # minimum of elements 1,3,9 (zero-padded) on phone pad + # 7,5,3 on numpad + expected = np.array( + [[0., 0., 0., 0., 0.], + [0., 0., 1., 2., 0.], + [0., 5., 6., 7., 0.], + [0., 10., 11., 12., 0.], + [0., 0., 0., 0., 0.]], + ) + assert_allclose(order_filter(x, domain, 0), expected) + + # maximum of elements 1,3,9 (zero-padded) on phone pad + # 7,5,3 on numpad + expected = np.array( + [[6., 7., 8., 9., 4.], + [11., 12., 13., 14., 9.], + [16., 17., 18., 19., 14.], + [21., 22., 23., 24., 19.], + [20., 21., 22., 23., 24.]], + ) + assert_allclose(order_filter(x, domain, 2), expected) + + # and, just to complete the set, median of zero-padded elements + expected = np.array( + [[0, 1, 2, 3, 0], + [5, 6, 7, 8, 3], + [10, 11, 12, 13, 8], + [15, 16, 17, 18, 13], + [0, 15, 16, 17, 18]], + ) + assert_allclose(order_filter(x, domain, 1), expected) + + def test_medfilt_order_filter(self): + x = np.arange(25).reshape(5, 5) + + # median of zero-padded elements 1,5,9 on phone pad + # 7,5,3 on numpad + expected = np.array( + [[0, 1, 2, 3, 0], + [1, 6, 7, 8, 4], + [6, 11, 12, 13, 9], + [11, 16, 17, 18, 14], + [0, 16, 17, 18, 0]], + ) + assert_allclose(medfilt(x, 3), expected) + + assert_allclose( + order_filter(x, np.ones((3, 3)), 4), + expected + ) + + def test_order_filter_asymmetric(self): + x = np.arange(25).reshape(5, 5) + domain = np.array( + [[1, 1, 0], + [0, 1, 0], + [0, 0, 0]], + ) + + expected = np.array( + [[0, 0, 0, 0, 0], + [0, 0, 1, 2, 3], + [0, 5, 6, 7, 8], + [0, 10, 11, 12, 13], + [0, 15, 16, 17, 18]] + ) + assert_allclose(order_filter(x, domain, 0), expected) + + expected = np.array( + [[0, 0, 0, 0, 0], + [0, 1, 2, 3, 4], + [5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19]] + ) + assert_allclose(order_filter(x, domain, 1), expected) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_fir_filter_design.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_fir_filter_design.py new file mode 100644 index 0000000000000000000000000000000000000000..8d3b8ba4e9bc2ab1237abb042f919f8c7e4140a3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_fir_filter_design.py @@ -0,0 +1,697 @@ +import numpy as np +from numpy.testing import (assert_almost_equal, assert_array_almost_equal, + assert_equal, assert_, + assert_allclose, assert_warns) +from pytest import raises as assert_raises +import pytest + +from scipy.fft import fft +from scipy.special import sinc +from scipy.signal import kaiser_beta, kaiser_atten, kaiserord, \ + firwin, firwin2, freqz, remez, firls, minimum_phase + + +def test_kaiser_beta(): + b = kaiser_beta(58.7) + assert_almost_equal(b, 0.1102 * 50.0) + b = kaiser_beta(22.0) + assert_almost_equal(b, 0.5842 + 0.07886) + b = kaiser_beta(21.0) + assert_equal(b, 0.0) + b = kaiser_beta(10.0) + assert_equal(b, 0.0) + + +def test_kaiser_atten(): + a = kaiser_atten(1, 1.0) + assert_equal(a, 7.95) + a = kaiser_atten(2, 1/np.pi) + assert_equal(a, 2.285 + 7.95) + + +def test_kaiserord(): + assert_raises(ValueError, kaiserord, 1.0, 1.0) + numtaps, beta = kaiserord(2.285 + 7.95 - 0.001, 1/np.pi) + assert_equal((numtaps, beta), (2, 0.0)) + + +class TestFirwin: + + def check_response(self, h, expected_response, tol=.05): + N = len(h) + alpha = 0.5 * (N-1) + m = np.arange(0,N) - alpha # time indices of taps + for freq, expected in expected_response: + actual = abs(np.sum(h*np.exp(-1.j*np.pi*m*freq))) + mse = abs(actual-expected)**2 + assert_(mse < tol, f'response not as expected, mse={mse:g} > {tol:g}') + + def test_response(self): + N = 51 + f = .5 + # increase length just to try even/odd + h = firwin(N, f) # low-pass from 0 to f + self.check_response(h, [(.25,1), (.75,0)]) + + h = firwin(N+1, f, window='nuttall') # specific window + self.check_response(h, [(.25,1), (.75,0)]) + + h = firwin(N+2, f, pass_zero=False) # stop from 0 to f --> high-pass + self.check_response(h, [(.25,0), (.75,1)]) + + f1, f2, f3, f4 = .2, .4, .6, .8 + h = firwin(N+3, [f1, f2], pass_zero=False) # band-pass filter + self.check_response(h, [(.1,0), (.3,1), (.5,0)]) + + h = firwin(N+4, [f1, f2]) # band-stop filter + self.check_response(h, [(.1,1), (.3,0), (.5,1)]) + + h = firwin(N+5, [f1, f2, f3, f4], pass_zero=False, scale=False) + self.check_response(h, [(.1,0), (.3,1), (.5,0), (.7,1), (.9,0)]) + + h = firwin(N+6, [f1, f2, f3, f4]) # multiband filter + self.check_response(h, [(.1,1), (.3,0), (.5,1), (.7,0), (.9,1)]) + + h = firwin(N+7, 0.1, width=.03) # low-pass + self.check_response(h, [(.05,1), (.75,0)]) + + h = firwin(N+8, 0.1, pass_zero=False) # high-pass + self.check_response(h, [(.05,0), (.75,1)]) + + def mse(self, h, bands): + """Compute mean squared error versus ideal response across frequency + band. + h -- coefficients + bands -- list of (left, right) tuples relative to 1==Nyquist of + passbands + """ + w, H = freqz(h, worN=1024) + f = w/np.pi + passIndicator = np.zeros(len(w), bool) + for left, right in bands: + passIndicator |= (f >= left) & (f < right) + Hideal = np.where(passIndicator, 1, 0) + mse = np.mean(abs(abs(H)-Hideal)**2) + return mse + + def test_scaling(self): + """ + For one lowpass, bandpass, and highpass example filter, this test + checks two things: + - the mean squared error over the frequency domain of the unscaled + filter is smaller than the scaled filter (true for rectangular + window) + - the response of the scaled filter is exactly unity at the center + of the first passband + """ + N = 11 + cases = [ + ([.5], True, (0, 1)), + ([0.2, .6], False, (.4, 1)), + ([.5], False, (1, 1)), + ] + for cutoff, pass_zero, expected_response in cases: + h = firwin(N, cutoff, scale=False, pass_zero=pass_zero, window='ones') + hs = firwin(N, cutoff, scale=True, pass_zero=pass_zero, window='ones') + if len(cutoff) == 1: + if pass_zero: + cutoff = [0] + cutoff + else: + cutoff = cutoff + [1] + assert_(self.mse(h, [cutoff]) < self.mse(hs, [cutoff]), + 'least squares violation') + self.check_response(hs, [expected_response], 1e-12) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + firwin(51, .5, fs=np.array([10, 20])) + + +class TestFirWinMore: + """Different author, different style, different tests...""" + + def test_lowpass(self): + width = 0.04 + ntaps, beta = kaiserord(120, width) + kwargs = dict(cutoff=0.5, window=('kaiser', beta), scale=False) + taps = firwin(ntaps, **kwargs) + + # Check the symmetry of taps. + assert_array_almost_equal(taps[:ntaps//2], taps[ntaps:ntaps-ntaps//2-1:-1]) + + # Check the gain at a few samples where + # we know it should be approximately 0 or 1. + freq_samples = np.array([0.0, 0.25, 0.5-width/2, 0.5+width/2, 0.75, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [1.0, 1.0, 1.0, 0.0, 0.0, 0.0], decimal=5) + + taps_str = firwin(ntaps, pass_zero='lowpass', **kwargs) + assert_allclose(taps, taps_str) + + def test_highpass(self): + width = 0.04 + ntaps, beta = kaiserord(120, width) + + # Ensure that ntaps is odd. + ntaps |= 1 + + kwargs = dict(cutoff=0.5, window=('kaiser', beta), scale=False) + taps = firwin(ntaps, pass_zero=False, **kwargs) + + # Check the symmetry of taps. + assert_array_almost_equal(taps[:ntaps//2], taps[ntaps:ntaps-ntaps//2-1:-1]) + + # Check the gain at a few samples where + # we know it should be approximately 0 or 1. + freq_samples = np.array([0.0, 0.25, 0.5-width/2, 0.5+width/2, 0.75, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], decimal=5) + + taps_str = firwin(ntaps, pass_zero='highpass', **kwargs) + assert_allclose(taps, taps_str) + + def test_bandpass(self): + width = 0.04 + ntaps, beta = kaiserord(120, width) + kwargs = dict(cutoff=[0.3, 0.7], window=('kaiser', beta), scale=False) + taps = firwin(ntaps, pass_zero=False, **kwargs) + + # Check the symmetry of taps. + assert_array_almost_equal(taps[:ntaps//2], taps[ntaps:ntaps-ntaps//2-1:-1]) + + # Check the gain at a few samples where + # we know it should be approximately 0 or 1. + freq_samples = np.array([0.0, 0.2, 0.3-width/2, 0.3+width/2, 0.5, + 0.7-width/2, 0.7+width/2, 0.8, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], decimal=5) + + taps_str = firwin(ntaps, pass_zero='bandpass', **kwargs) + assert_allclose(taps, taps_str) + + def test_bandstop_multi(self): + width = 0.04 + ntaps, beta = kaiserord(120, width) + kwargs = dict(cutoff=[0.2, 0.5, 0.8], window=('kaiser', beta), + scale=False) + taps = firwin(ntaps, **kwargs) + + # Check the symmetry of taps. + assert_array_almost_equal(taps[:ntaps//2], taps[ntaps:ntaps-ntaps//2-1:-1]) + + # Check the gain at a few samples where + # we know it should be approximately 0 or 1. + freq_samples = np.array([0.0, 0.1, 0.2-width/2, 0.2+width/2, 0.35, + 0.5-width/2, 0.5+width/2, 0.65, + 0.8-width/2, 0.8+width/2, 0.9, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], + decimal=5) + + taps_str = firwin(ntaps, pass_zero='bandstop', **kwargs) + assert_allclose(taps, taps_str) + + def test_fs_nyq(self): + """Test the fs and nyq keywords.""" + nyquist = 1000 + width = 40.0 + relative_width = width/nyquist + ntaps, beta = kaiserord(120, relative_width) + taps = firwin(ntaps, cutoff=[300, 700], window=('kaiser', beta), + pass_zero=False, scale=False, fs=2*nyquist) + + # Check the symmetry of taps. + assert_array_almost_equal(taps[:ntaps//2], taps[ntaps:ntaps-ntaps//2-1:-1]) + + # Check the gain at a few samples where + # we know it should be approximately 0 or 1. + freq_samples = np.array([0.0, 200, 300-width/2, 300+width/2, 500, + 700-width/2, 700+width/2, 800, 1000]) + freqs, response = freqz(taps, worN=np.pi*freq_samples/nyquist) + assert_array_almost_equal(np.abs(response), + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], decimal=5) + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "Keyword argument 'nyq'") + taps2 = firwin(ntaps, cutoff=[300, 700], window=('kaiser', beta), + pass_zero=False, scale=False, nyq=nyquist) + assert_allclose(taps2, taps) + + def test_bad_cutoff(self): + """Test that invalid cutoff argument raises ValueError.""" + # cutoff values must be greater than 0 and less than 1. + assert_raises(ValueError, firwin, 99, -0.5) + assert_raises(ValueError, firwin, 99, 1.5) + # Don't allow 0 or 1 in cutoff. + assert_raises(ValueError, firwin, 99, [0, 0.5]) + assert_raises(ValueError, firwin, 99, [0.5, 1]) + # cutoff values must be strictly increasing. + assert_raises(ValueError, firwin, 99, [0.1, 0.5, 0.2]) + assert_raises(ValueError, firwin, 99, [0.1, 0.5, 0.5]) + # Must have at least one cutoff value. + assert_raises(ValueError, firwin, 99, []) + # 2D array not allowed. + assert_raises(ValueError, firwin, 99, [[0.1, 0.2],[0.3, 0.4]]) + # cutoff values must be less than nyq. + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "Keyword argument 'nyq'") + assert_raises(ValueError, firwin, 99, 50.0, nyq=40) + assert_raises(ValueError, firwin, 99, [10, 20, 30], nyq=25) + assert_raises(ValueError, firwin, 99, 50.0, fs=80) + assert_raises(ValueError, firwin, 99, [10, 20, 30], fs=50) + + def test_even_highpass_raises_value_error(self): + """Test that attempt to create a highpass filter with an even number + of taps raises a ValueError exception.""" + assert_raises(ValueError, firwin, 40, 0.5, pass_zero=False) + assert_raises(ValueError, firwin, 40, [.25, 0.5]) + + def test_bad_pass_zero(self): + """Test degenerate pass_zero cases.""" + with assert_raises(ValueError, match='pass_zero must be'): + firwin(41, 0.5, pass_zero='foo') + with assert_raises(TypeError, match='cannot be interpreted'): + firwin(41, 0.5, pass_zero=1.) + for pass_zero in ('lowpass', 'highpass'): + with assert_raises(ValueError, match='cutoff must have one'): + firwin(41, [0.5, 0.6], pass_zero=pass_zero) + for pass_zero in ('bandpass', 'bandstop'): + with assert_raises(ValueError, match='must have at least two'): + firwin(41, [0.5], pass_zero=pass_zero) + + def test_firwin_deprecations(self): + with pytest.deprecated_call(match="argument 'nyq' is deprecated"): + firwin(1, 1, nyq=10) + with pytest.deprecated_call(match="use keyword arguments"): + firwin(58, 0.1, 0.03) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + firwin2(51, .5, 1, fs=np.array([10, 20])) + + +class TestFirwin2: + + def test_invalid_args(self): + # `freq` and `gain` have different lengths. + with assert_raises(ValueError, match='must be of same length'): + firwin2(50, [0, 0.5, 1], [0.0, 1.0]) + # `nfreqs` is less than `ntaps`. + with assert_raises(ValueError, match='ntaps must be less than nfreqs'): + firwin2(50, [0, 0.5, 1], [0.0, 1.0, 1.0], nfreqs=33) + # Decreasing value in `freq` + with assert_raises(ValueError, match='must be nondecreasing'): + firwin2(50, [0, 0.5, 0.4, 1.0], [0, .25, .5, 1.0]) + # Value in `freq` repeated more than once. + with assert_raises(ValueError, match='must not occur more than twice'): + firwin2(50, [0, .1, .1, .1, 1.0], [0.0, 0.5, 0.75, 1.0, 1.0]) + # `freq` does not start at 0.0. + with assert_raises(ValueError, match='start with 0'): + firwin2(50, [0.5, 1.0], [0.0, 1.0]) + # `freq` does not end at fs/2. + with assert_raises(ValueError, match='end with fs/2'): + firwin2(50, [0.0, 0.5], [0.0, 1.0]) + # Value 0 is repeated in `freq` + with assert_raises(ValueError, match='0 must not be repeated'): + firwin2(50, [0.0, 0.0, 0.5, 1.0], [1.0, 1.0, 0.0, 0.0]) + # Value fs/2 is repeated in `freq` + with assert_raises(ValueError, match='fs/2 must not be repeated'): + firwin2(50, [0.0, 0.5, 1.0, 1.0], [1.0, 1.0, 0.0, 0.0]) + # Value in `freq` that is too close to a repeated number + with assert_raises(ValueError, match='cannot contain numbers ' + 'that are too close'): + firwin2(50, [0.0, 0.5 - np.finfo(float).eps * 0.5, 0.5, 0.5, 1.0], + [1.0, 1.0, 1.0, 0.0, 0.0]) + + # Type II filter, but the gain at nyquist frequency is not zero. + with assert_raises(ValueError, match='Type II filter'): + firwin2(16, [0.0, 0.5, 1.0], [0.0, 1.0, 1.0]) + + # Type III filter, but the gains at nyquist and zero rate are not zero. + with assert_raises(ValueError, match='Type III filter'): + firwin2(17, [0.0, 0.5, 1.0], [0.0, 1.0, 1.0], antisymmetric=True) + with assert_raises(ValueError, match='Type III filter'): + firwin2(17, [0.0, 0.5, 1.0], [1.0, 1.0, 0.0], antisymmetric=True) + with assert_raises(ValueError, match='Type III filter'): + firwin2(17, [0.0, 0.5, 1.0], [1.0, 1.0, 1.0], antisymmetric=True) + + # Type IV filter, but the gain at zero rate is not zero. + with assert_raises(ValueError, match='Type IV filter'): + firwin2(16, [0.0, 0.5, 1.0], [1.0, 1.0, 0.0], antisymmetric=True) + + def test01(self): + width = 0.04 + beta = 12.0 + ntaps = 400 + # Filter is 1 from w=0 to w=0.5, then decreases linearly from 1 to 0 as w + # increases from w=0.5 to w=1 (w=1 is the Nyquist frequency). + freq = [0.0, 0.5, 1.0] + gain = [1.0, 1.0, 0.0] + taps = firwin2(ntaps, freq, gain, window=('kaiser', beta)) + freq_samples = np.array([0.0, 0.25, 0.5-width/2, 0.5+width/2, + 0.75, 1.0-width/2]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [1.0, 1.0, 1.0, 1.0-width, 0.5, width], decimal=5) + + def test02(self): + width = 0.04 + beta = 12.0 + # ntaps must be odd for positive gain at Nyquist. + ntaps = 401 + # An ideal highpass filter. + freq = [0.0, 0.5, 0.5, 1.0] + gain = [0.0, 0.0, 1.0, 1.0] + taps = firwin2(ntaps, freq, gain, window=('kaiser', beta)) + freq_samples = np.array([0.0, 0.25, 0.5-width, 0.5+width, 0.75, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], decimal=5) + + def test03(self): + width = 0.02 + ntaps, beta = kaiserord(120, width) + # ntaps must be odd for positive gain at Nyquist. + ntaps = int(ntaps) | 1 + freq = [0.0, 0.4, 0.4, 0.5, 0.5, 1.0] + gain = [1.0, 1.0, 0.0, 0.0, 1.0, 1.0] + taps = firwin2(ntaps, freq, gain, window=('kaiser', beta)) + freq_samples = np.array([0.0, 0.4-width, 0.4+width, 0.45, + 0.5-width, 0.5+width, 0.75, 1.0]) + freqs, response = freqz(taps, worN=np.pi*freq_samples) + assert_array_almost_equal(np.abs(response), + [1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0], decimal=5) + + def test04(self): + """Test firwin2 when window=None.""" + ntaps = 5 + # Ideal lowpass: gain is 1 on [0,0.5], and 0 on [0.5, 1.0] + freq = [0.0, 0.5, 0.5, 1.0] + gain = [1.0, 1.0, 0.0, 0.0] + taps = firwin2(ntaps, freq, gain, window=None, nfreqs=8193) + alpha = 0.5 * (ntaps - 1) + m = np.arange(0, ntaps) - alpha + h = 0.5 * sinc(0.5 * m) + assert_array_almost_equal(h, taps) + + def test05(self): + """Test firwin2 for calculating Type IV filters""" + ntaps = 1500 + + freq = [0.0, 1.0] + gain = [0.0, 1.0] + taps = firwin2(ntaps, freq, gain, window=None, antisymmetric=True) + assert_array_almost_equal(taps[: ntaps // 2], -taps[ntaps // 2:][::-1]) + + freqs, response = freqz(taps, worN=2048) + assert_array_almost_equal(abs(response), freqs / np.pi, decimal=4) + + def test06(self): + """Test firwin2 for calculating Type III filters""" + ntaps = 1501 + + freq = [0.0, 0.5, 0.55, 1.0] + gain = [0.0, 0.5, 0.0, 0.0] + taps = firwin2(ntaps, freq, gain, window=None, antisymmetric=True) + assert_equal(taps[ntaps // 2], 0.0) + assert_array_almost_equal(taps[: ntaps // 2], -taps[ntaps // 2 + 1:][::-1]) + + freqs, response1 = freqz(taps, worN=2048) + response2 = np.interp(freqs / np.pi, freq, gain) + assert_array_almost_equal(abs(response1), response2, decimal=3) + + def test_fs_nyq(self): + taps1 = firwin2(80, [0.0, 0.5, 1.0], [1.0, 1.0, 0.0]) + taps2 = firwin2(80, [0.0, 30.0, 60.0], [1.0, 1.0, 0.0], fs=120.0) + assert_array_almost_equal(taps1, taps2) + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "Keyword argument 'nyq'") + taps2 = firwin2(80, [0.0, 30.0, 60.0], [1.0, 1.0, 0.0], nyq=60.0) + assert_array_almost_equal(taps1, taps2) + + def test_tuple(self): + taps1 = firwin2(150, (0.0, 0.5, 0.5, 1.0), (1.0, 1.0, 0.0, 0.0)) + taps2 = firwin2(150, [0.0, 0.5, 0.5, 1.0], [1.0, 1.0, 0.0, 0.0]) + assert_array_almost_equal(taps1, taps2) + + def test_input_modyfication(self): + freq1 = np.array([0.0, 0.5, 0.5, 1.0]) + freq2 = np.array(freq1) + firwin2(80, freq1, [1.0, 1.0, 0.0, 0.0]) + assert_equal(freq1, freq2) + + def test_firwin2_deprecations(self): + with pytest.deprecated_call(match="argument 'nyq' is deprecated"): + firwin2(1, [0, 10], [1, 1], nyq=10) + with pytest.deprecated_call(match="use keyword arguments"): + # from test04 + firwin2(5, [0.0, 0.5, 0.5, 1.0], [1.0, 1.0, 0.0, 0.0], 8193, None) + + +class TestRemez: + + def test_bad_args(self): + assert_raises(ValueError, remez, 11, [0.1, 0.4], [1], type='pooka') + + def test_hilbert(self): + N = 11 # number of taps in the filter + a = 0.1 # width of the transition band + + # design an unity gain hilbert bandpass filter from w to 0.5-w + h = remez(11, [a, 0.5-a], [1], type='hilbert') + + # make sure the filter has correct # of taps + assert_(len(h) == N, "Number of Taps") + + # make sure it is type III (anti-symmetric tap coefficients) + assert_array_almost_equal(h[:(N-1)//2], -h[:-(N-1)//2-1:-1]) + + # Since the requested response is symmetric, all even coefficients + # should be zero (or in this case really small) + assert_((abs(h[1::2]) < 1e-15).all(), "Even Coefficients Equal Zero") + + # now check the frequency response + w, H = freqz(h, 1) + f = w/2/np.pi + Hmag = abs(H) + + # should have a zero at 0 and pi (in this case close to zero) + assert_((Hmag[[0, -1]] < 0.02).all(), "Zero at zero and pi") + + # check that the pass band is close to unity + idx = np.logical_and(f > a, f < 0.5-a) + assert_((abs(Hmag[idx] - 1) < 0.015).all(), "Pass Band Close To Unity") + + def test_compare(self): + # test comparison to MATLAB + k = [0.024590270518440, -0.041314581814658, -0.075943803756711, + -0.003530911231040, 0.193140296954975, 0.373400753484939, + 0.373400753484939, 0.193140296954975, -0.003530911231040, + -0.075943803756711, -0.041314581814658, 0.024590270518440] + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "'remez'") + h = remez(12, [0, 0.3, 0.5, 1], [1, 0], Hz=2.) + assert_allclose(h, k) + h = remez(12, [0, 0.3, 0.5, 1], [1, 0], fs=2.) + assert_allclose(h, k) + + h = [-0.038976016082299, 0.018704846485491, -0.014644062687875, + 0.002879152556419, 0.016849978528150, -0.043276706138248, + 0.073641298245579, -0.103908158578635, 0.129770906801075, + -0.147163447297124, 0.153302248456347, -0.147163447297124, + 0.129770906801075, -0.103908158578635, 0.073641298245579, + -0.043276706138248, 0.016849978528150, 0.002879152556419, + -0.014644062687875, 0.018704846485491, -0.038976016082299] + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "'remez'") + assert_allclose(remez(21, [0, 0.8, 0.9, 1], [0, 1], Hz=2.), h) + assert_allclose(remez(21, [0, 0.8, 0.9, 1], [0, 1], fs=2.), h) + + def test_remez_deprecations(self): + with pytest.deprecated_call(match="'remez' keyword argument 'Hz'"): + remez(12, [0, 0.3, 0.5, 1], [1, 0], Hz=2.) + with pytest.deprecated_call(match="use keyword arguments"): + # from test_hilbert + remez(11, [0.1, 0.4], [1], None) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + remez(11, .1, 1, fs=np.array([10, 20])) + +class TestFirls: + + def test_bad_args(self): + # even numtaps + assert_raises(ValueError, firls, 10, [0.1, 0.2], [0, 0]) + # odd bands + assert_raises(ValueError, firls, 11, [0.1, 0.2, 0.4], [0, 0, 0]) + # len(bands) != len(desired) + assert_raises(ValueError, firls, 11, [0.1, 0.2, 0.3, 0.4], [0, 0, 0]) + # non-monotonic bands + assert_raises(ValueError, firls, 11, [0.2, 0.1], [0, 0]) + assert_raises(ValueError, firls, 11, [0.1, 0.2, 0.3, 0.3], [0] * 4) + assert_raises(ValueError, firls, 11, [0.3, 0.4, 0.1, 0.2], [0] * 4) + assert_raises(ValueError, firls, 11, [0.1, 0.3, 0.2, 0.4], [0] * 4) + # negative desired + assert_raises(ValueError, firls, 11, [0.1, 0.2], [-1, 1]) + # len(weight) != len(pairs) + assert_raises(ValueError, firls, 11, [0.1, 0.2], [0, 0], weight=[1, 2]) + # negative weight + assert_raises(ValueError, firls, 11, [0.1, 0.2], [0, 0], weight=[-1]) + + def test_firls(self): + N = 11 # number of taps in the filter + a = 0.1 # width of the transition band + + # design a halfband symmetric low-pass filter + h = firls(11, [0, a, 0.5-a, 0.5], [1, 1, 0, 0], fs=1.0) + + # make sure the filter has correct # of taps + assert_equal(len(h), N) + + # make sure it is symmetric + midx = (N-1) // 2 + assert_array_almost_equal(h[:midx], h[:-midx-1:-1]) + + # make sure the center tap is 0.5 + assert_almost_equal(h[midx], 0.5) + + # For halfband symmetric, odd coefficients (except the center) + # should be zero (really small) + hodd = np.hstack((h[1:midx:2], h[-midx+1::2])) + assert_array_almost_equal(hodd, 0) + + # now check the frequency response + w, H = freqz(h, 1) + f = w/2/np.pi + Hmag = np.abs(H) + + # check that the pass band is close to unity + idx = np.logical_and(f > 0, f < a) + assert_array_almost_equal(Hmag[idx], 1, decimal=3) + + # check that the stop band is close to zero + idx = np.logical_and(f > 0.5-a, f < 0.5) + assert_array_almost_equal(Hmag[idx], 0, decimal=3) + + def test_compare(self): + # compare to OCTAVE output + taps = firls(9, [0, 0.5, 0.55, 1], [1, 1, 0, 0], weight=[1, 2]) + # >> taps = firls(8, [0 0.5 0.55 1], [1 1 0 0], [1, 2]); + known_taps = [-6.26930101730182e-04, -1.03354450635036e-01, + -9.81576747564301e-03, 3.17271686090449e-01, + 5.11409425599933e-01, 3.17271686090449e-01, + -9.81576747564301e-03, -1.03354450635036e-01, + -6.26930101730182e-04] + assert_allclose(taps, known_taps) + + # compare to MATLAB output + taps = firls(11, [0, 0.5, 0.5, 1], [1, 1, 0, 0], weight=[1, 2]) + # >> taps = firls(10, [0 0.5 0.5 1], [1 1 0 0], [1, 2]); + known_taps = [ + 0.058545300496815, -0.014233383714318, -0.104688258464392, + 0.012403323025279, 0.317930861136062, 0.488047220029700, + 0.317930861136062, 0.012403323025279, -0.104688258464392, + -0.014233383714318, 0.058545300496815] + assert_allclose(taps, known_taps) + + # With linear changes: + taps = firls(7, (0, 1, 2, 3, 4, 5), [1, 0, 0, 1, 1, 0], fs=20) + # >> taps = firls(6, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [1, 0, 0, 1, 1, 0]) + known_taps = [ + 1.156090832768218, -4.1385894727395849, 7.5288619164321826, + -8.5530572592947856, 7.5288619164321826, -4.1385894727395849, + 1.156090832768218] + assert_allclose(taps, known_taps) + + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "Keyword argument 'nyq'") + taps = firls(7, (0, 1, 2, 3, 4, 5), [1, 0, 0, 1, 1, 0], nyq=10) + assert_allclose(taps, known_taps) + + with pytest.raises(ValueError, match='between 0 and 1'): + firls(7, [0, 1], [0, 1], nyq=0.5) + + def test_rank_deficient(self): + # solve() runs but warns (only sometimes, so here we don't use match) + x = firls(21, [0, 0.1, 0.9, 1], [1, 1, 0, 0]) + w, h = freqz(x, fs=2.) + assert_allclose(np.abs(h[:2]), 1., atol=1e-5) + assert_allclose(np.abs(h[-2:]), 0., atol=1e-6) + # switch to pinvh (tolerances could be higher with longer + # filters, but using shorter ones is faster computationally and + # the idea is the same) + x = firls(101, [0, 0.01, 0.99, 1], [1, 1, 0, 0]) + w, h = freqz(x, fs=2.) + mask = w < 0.01 + assert mask.sum() > 3 + assert_allclose(np.abs(h[mask]), 1., atol=1e-4) + mask = w > 0.99 + assert mask.sum() > 3 + assert_allclose(np.abs(h[mask]), 0., atol=1e-4) + + def test_firls_deprecations(self): + with pytest.deprecated_call(match="argument 'nyq' is deprecated"): + firls(1, (0, 1), (0, 0), nyq=10) + with pytest.deprecated_call(match="use keyword arguments"): + # from test_firls + firls(11, [0, 0.1, 0.4, 0.5], [1, 1, 0, 0], None) + + def test_fs_validation(self): + with pytest.raises(ValueError, match="Sampling.*single scalar"): + firls(11, .1, 1, fs=np.array([10, 20])) + +class TestMinimumPhase: + + def test_bad_args(self): + # not enough taps + assert_raises(ValueError, minimum_phase, [1.]) + assert_raises(ValueError, minimum_phase, [1., 1.]) + assert_raises(ValueError, minimum_phase, np.full(10, 1j)) + assert_raises(ValueError, minimum_phase, 'foo') + assert_raises(ValueError, minimum_phase, np.ones(10), n_fft=8) + assert_raises(ValueError, minimum_phase, np.ones(10), method='foo') + assert_warns(RuntimeWarning, minimum_phase, np.arange(3)) + + def test_homomorphic(self): + # check that it can recover frequency responses of arbitrary + # linear-phase filters + + # for some cases we can get the actual filter back + h = [1, -1] + h_new = minimum_phase(np.convolve(h, h[::-1])) + assert_allclose(h_new, h, rtol=0.05) + + # but in general we only guarantee we get the magnitude back + rng = np.random.RandomState(0) + for n in (2, 3, 10, 11, 15, 16, 17, 20, 21, 100, 101): + h = rng.randn(n) + h_new = minimum_phase(np.convolve(h, h[::-1])) + assert_allclose(np.abs(fft(h_new)), + np.abs(fft(h)), rtol=1e-4) + + def test_hilbert(self): + # compare to MATLAB output of reference implementation + + # f=[0 0.3 0.5 1]; + # a=[1 1 0 0]; + # h=remez(11,f,a); + h = remez(12, [0, 0.3, 0.5, 1], [1, 0], fs=2.) + k = [0.349585548646686, 0.373552164395447, 0.326082685363438, + 0.077152207480935, -0.129943946349364, -0.059355880509749] + m = minimum_phase(h, 'hilbert') + assert_allclose(m, k, rtol=5e-3) + + # f=[0 0.8 0.9 1]; + # a=[0 0 1 1]; + # h=remez(20,f,a); + h = remez(21, [0, 0.8, 0.9, 1], [0, 1], fs=2.) + k = [0.232486803906329, -0.133551833687071, 0.151871456867244, + -0.157957283165866, 0.151739294892963, -0.129293146705090, + 0.100787844523204, -0.065832656741252, 0.035361328741024, + -0.014977068692269, -0.158416139047557] + m = minimum_phase(h, 'hilbert', n_fft=2**19) + assert_allclose(m, k, rtol=2e-3) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_ltisys.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_ltisys.py new file mode 100644 index 0000000000000000000000000000000000000000..af69f109cd3e6e72858d69bcb338ddd61b18e3ba --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_ltisys.py @@ -0,0 +1,1221 @@ +import warnings + +import numpy as np +from numpy.testing import (assert_almost_equal, assert_equal, assert_allclose, + assert_, suppress_warnings) +from pytest import raises as assert_raises + +from scipy.signal import (ss2tf, tf2ss, lti, + dlti, bode, freqresp, lsim, impulse, step, + abcd_normalize, place_poles, + TransferFunction, StateSpace, ZerosPolesGain) +from scipy.signal._filter_design import BadCoefficients +import scipy.linalg as linalg + + +def _assert_poles_close(P1,P2, rtol=1e-8, atol=1e-8): + """ + Check each pole in P1 is close to a pole in P2 with a 1e-8 + relative tolerance or 1e-8 absolute tolerance (useful for zero poles). + These tolerances are very strict but the systems tested are known to + accept these poles so we should not be far from what is requested. + """ + P2 = P2.copy() + for p1 in P1: + found = False + for p2_idx in range(P2.shape[0]): + if np.allclose([np.real(p1), np.imag(p1)], + [np.real(P2[p2_idx]), np.imag(P2[p2_idx])], + rtol, atol): + found = True + np.delete(P2, p2_idx) + break + if not found: + raise ValueError("Can't find pole " + str(p1) + " in " + str(P2)) + + +class TestPlacePoles: + + def _check(self, A, B, P, **kwargs): + """ + Perform the most common tests on the poles computed by place_poles + and return the Bunch object for further specific tests + """ + fsf = place_poles(A, B, P, **kwargs) + expected, _ = np.linalg.eig(A - np.dot(B, fsf.gain_matrix)) + _assert_poles_close(expected, fsf.requested_poles) + _assert_poles_close(expected, fsf.computed_poles) + _assert_poles_close(P,fsf.requested_poles) + return fsf + + def test_real(self): + # Test real pole placement using KNV and YT0 algorithm and example 1 in + # section 4 of the reference publication (see place_poles docstring) + A = np.array([1.380, -0.2077, 6.715, -5.676, -0.5814, -4.290, 0, + 0.6750, 1.067, 4.273, -6.654, 5.893, 0.0480, 4.273, + 1.343, -2.104]).reshape(4, 4) + B = np.array([0, 5.679, 1.136, 1.136, 0, 0, -3.146,0]).reshape(4, 2) + P = np.array([-0.2, -0.5, -5.0566, -8.6659]) + + # Check that both KNV and YT compute correct K matrix + self._check(A, B, P, method='KNV0') + self._check(A, B, P, method='YT') + + # Try to reach the specific case in _YT_real where two singular + # values are almost equal. This is to improve code coverage but I + # have no way to be sure this code is really reached + + # on some architectures this can lead to a RuntimeWarning invalid + # value in divide (see gh-7590), so suppress it for now + with np.errstate(invalid='ignore'): + self._check(A, B, (2,2,3,3)) + + def test_complex(self): + # Test complex pole placement on a linearized car model, taken from L. + # Jaulin, Automatique pour la robotique, Cours et Exercices, iSTE + # editions p 184/185 + A = np.array([[0, 7, 0, 0], + [0, 0, 0, 7/3.], + [0, 0, 0, 0], + [0, 0, 0, 0]]) + B = np.array([[0, 0], + [0, 0], + [1, 0], + [0, 1]]) + # Test complex poles on YT + P = np.array([-3, -1, -2-1j, -2+1j]) + # on macOS arm64 this can lead to a RuntimeWarning invalid + # value in divide, so suppress it for now + with np.errstate(divide='ignore', invalid='ignore'): + self._check(A, B, P) + + # Try to reach the specific case in _YT_complex where two singular + # values are almost equal. This is to improve code coverage but I + # have no way to be sure this code is really reached + + P = [0-1e-6j,0+1e-6j,-10,10] + with np.errstate(divide='ignore', invalid='ignore'): + self._check(A, B, P, maxiter=1000) + + # Try to reach the specific case in _YT_complex where the rank two + # update yields two null vectors. This test was found via Monte Carlo. + + A = np.array( + [-2148,-2902, -2267, -598, -1722, -1829, -165, -283, -2546, + -167, -754, -2285, -543, -1700, -584, -2978, -925, -1300, + -1583, -984, -386, -2650, -764, -897, -517, -1598, 2, -1709, + -291, -338, -153, -1804, -1106, -1168, -867, -2297] + ).reshape(6,6) + + B = np.array( + [-108, -374, -524, -1285, -1232, -161, -1204, -672, -637, + -15, -483, -23, -931, -780, -1245, -1129, -1290, -1502, + -952, -1374, -62, -964, -930, -939, -792, -756, -1437, + -491, -1543, -686] + ).reshape(6,5) + P = [-25.-29.j, -25.+29.j, 31.-42.j, 31.+42.j, 33.-41.j, 33.+41.j] + self._check(A, B, P) + + # Use a lot of poles to go through all cases for update_order + # in _YT_loop + + big_A = np.ones((11,11))-np.eye(11) + big_B = np.ones((11,10))-np.diag([1]*10,1)[:,1:] + big_A[:6,:6] = A + big_B[:6,:5] = B + + P = [-10,-20,-30,40,50,60,70,-20-5j,-20+5j,5+3j,5-3j] + with np.errstate(divide='ignore', invalid='ignore'): + self._check(big_A, big_B, P) + + #check with only complex poles and only real poles + P = [-10,-20,-30,-40,-50,-60,-70,-80,-90,-100] + self._check(big_A[:-1,:-1], big_B[:-1,:-1], P) + P = [-10+10j,-20+20j,-30+30j,-40+40j,-50+50j, + -10-10j,-20-20j,-30-30j,-40-40j,-50-50j] + self._check(big_A[:-1,:-1], big_B[:-1,:-1], P) + + # need a 5x5 array to ensure YT handles properly when there + # is only one real pole and several complex + A = np.array([0,7,0,0,0,0,0,7/3.,0,0,0,0,0,0,0,0, + 0,0,0,5,0,0,0,0,9]).reshape(5,5) + B = np.array([0,0,0,0,1,0,0,1,2,3]).reshape(5,2) + P = np.array([-2, -3+1j, -3-1j, -1+1j, -1-1j]) + with np.errstate(divide='ignore', invalid='ignore'): + place_poles(A, B, P) + + # same test with an odd number of real poles > 1 + # this is another specific case of YT + P = np.array([-2, -3, -4, -1+1j, -1-1j]) + with np.errstate(divide='ignore', invalid='ignore'): + self._check(A, B, P) + + def test_tricky_B(self): + # check we handle as we should the 1 column B matrices and + # n column B matrices (with n such as shape(A)=(n, n)) + A = np.array([1.380, -0.2077, 6.715, -5.676, -0.5814, -4.290, 0, + 0.6750, 1.067, 4.273, -6.654, 5.893, 0.0480, 4.273, + 1.343, -2.104]).reshape(4, 4) + B = np.array([0, 5.679, 1.136, 1.136, 0, 0, -3.146, 0, 1, 2, 3, 4, + 5, 6, 7, 8]).reshape(4, 4) + + # KNV or YT are not called here, it's a specific case with only + # one unique solution + P = np.array([-0.2, -0.5, -5.0566, -8.6659]) + fsf = self._check(A, B, P) + # rtol and nb_iter should be set to np.nan as the identity can be + # used as transfer matrix + assert_equal(fsf.rtol, np.nan) + assert_equal(fsf.nb_iter, np.nan) + + # check with complex poles too as they trigger a specific case in + # the specific case :-) + P = np.array((-2+1j,-2-1j,-3,-2)) + fsf = self._check(A, B, P) + assert_equal(fsf.rtol, np.nan) + assert_equal(fsf.nb_iter, np.nan) + + #now test with a B matrix with only one column (no optimisation) + B = B[:,0].reshape(4,1) + P = np.array((-2+1j,-2-1j,-3,-2)) + fsf = self._check(A, B, P) + + # we can't optimize anything, check they are set to 0 as expected + assert_equal(fsf.rtol, 0) + assert_equal(fsf.nb_iter, 0) + + def test_errors(self): + # Test input mistakes from user + A = np.array([0,7,0,0,0,0,0,7/3.,0,0,0,0,0,0,0,0]).reshape(4,4) + B = np.array([0,0,0,0,1,0,0,1]).reshape(4,2) + + #should fail as the method keyword is invalid + assert_raises(ValueError, place_poles, A, B, (-2.1,-2.2,-2.3,-2.4), + method="foo") + + #should fail as poles are not 1D array + assert_raises(ValueError, place_poles, A, B, + np.array((-2.1,-2.2,-2.3,-2.4)).reshape(4,1)) + + #should fail as A is not a 2D array + assert_raises(ValueError, place_poles, A[:,:,np.newaxis], B, + (-2.1,-2.2,-2.3,-2.4)) + + #should fail as B is not a 2D array + assert_raises(ValueError, place_poles, A, B[:,:,np.newaxis], + (-2.1,-2.2,-2.3,-2.4)) + + #should fail as there are too many poles + assert_raises(ValueError, place_poles, A, B, (-2.1,-2.2,-2.3,-2.4,-3)) + + #should fail as there are not enough poles + assert_raises(ValueError, place_poles, A, B, (-2.1,-2.2,-2.3)) + + #should fail as the rtol is greater than 1 + assert_raises(ValueError, place_poles, A, B, (-2.1,-2.2,-2.3,-2.4), + rtol=42) + + #should fail as maxiter is smaller than 1 + assert_raises(ValueError, place_poles, A, B, (-2.1,-2.2,-2.3,-2.4), + maxiter=-42) + + # should fail as ndim(B) is two + assert_raises(ValueError, place_poles, A, B, (-2,-2,-2,-2)) + + #unctrollable system + assert_raises(ValueError, place_poles, np.ones((4,4)), + np.ones((4,2)), (1,2,3,4)) + + # Should not raise ValueError as the poles can be placed but should + # raise a warning as the convergence is not reached + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + fsf = place_poles(A, B, (-1,-2,-3,-4), rtol=1e-16, maxiter=42) + assert_(len(w) == 1) + assert_(issubclass(w[-1].category, UserWarning)) + assert_("Convergence was not reached after maxiter iterations" + in str(w[-1].message)) + assert_equal(fsf.nb_iter, 42) + + # should fail as a complex misses its conjugate + assert_raises(ValueError, place_poles, A, B, (-2+1j,-2-1j,-2+3j,-2)) + + # should fail as A is not square + assert_raises(ValueError, place_poles, A[:,:3], B, (-2,-3,-4,-5)) + + # should fail as B has not the same number of lines as A + assert_raises(ValueError, place_poles, A, B[:3,:], (-2,-3,-4,-5)) + + # should fail as KNV0 does not support complex poles + assert_raises(ValueError, place_poles, A, B, + (-2+1j,-2-1j,-2+3j,-2-3j), method="KNV0") + + +class TestSS2TF: + + def check_matrix_shapes(self, p, q, r): + ss2tf(np.zeros((p, p)), + np.zeros((p, q)), + np.zeros((r, p)), + np.zeros((r, q)), 0) + + def test_shapes(self): + # Each tuple holds: + # number of states, number of inputs, number of outputs + for p, q, r in [(3, 3, 3), (1, 3, 3), (1, 1, 1)]: + self.check_matrix_shapes(p, q, r) + + def test_basic(self): + # Test a round trip through tf2ss and ss2tf. + b = np.array([1.0, 3.0, 5.0]) + a = np.array([1.0, 2.0, 3.0]) + + A, B, C, D = tf2ss(b, a) + assert_allclose(A, [[-2, -3], [1, 0]], rtol=1e-13) + assert_allclose(B, [[1], [0]], rtol=1e-13) + assert_allclose(C, [[1, 2]], rtol=1e-13) + assert_allclose(D, [[1]], rtol=1e-14) + + bb, aa = ss2tf(A, B, C, D) + assert_allclose(bb[0], b, rtol=1e-13) + assert_allclose(aa, a, rtol=1e-13) + + def test_zero_order_round_trip(self): + # See gh-5760 + tf = (2, 1) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[0]], rtol=1e-13) + assert_allclose(B, [[0]], rtol=1e-13) + assert_allclose(C, [[0]], rtol=1e-13) + assert_allclose(D, [[2]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[2, 0]], rtol=1e-13) + assert_allclose(den, [1, 0], rtol=1e-13) + + tf = ([[5], [2]], 1) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[0]], rtol=1e-13) + assert_allclose(B, [[0]], rtol=1e-13) + assert_allclose(C, [[0], [0]], rtol=1e-13) + assert_allclose(D, [[5], [2]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[5, 0], [2, 0]], rtol=1e-13) + assert_allclose(den, [1, 0], rtol=1e-13) + + def test_simo_round_trip(self): + # See gh-5753 + tf = ([[1, 2], [1, 1]], [1, 2]) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[-2]], rtol=1e-13) + assert_allclose(B, [[1]], rtol=1e-13) + assert_allclose(C, [[0], [-1]], rtol=1e-13) + assert_allclose(D, [[1], [1]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[1, 2], [1, 1]], rtol=1e-13) + assert_allclose(den, [1, 2], rtol=1e-13) + + tf = ([[1, 0, 1], [1, 1, 1]], [1, 1, 1]) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[-1, -1], [1, 0]], rtol=1e-13) + assert_allclose(B, [[1], [0]], rtol=1e-13) + assert_allclose(C, [[-1, 0], [0, 0]], rtol=1e-13) + assert_allclose(D, [[1], [1]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[1, 0, 1], [1, 1, 1]], rtol=1e-13) + assert_allclose(den, [1, 1, 1], rtol=1e-13) + + tf = ([[1, 2, 3], [1, 2, 3]], [1, 2, 3, 4]) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[-2, -3, -4], [1, 0, 0], [0, 1, 0]], rtol=1e-13) + assert_allclose(B, [[1], [0], [0]], rtol=1e-13) + assert_allclose(C, [[1, 2, 3], [1, 2, 3]], rtol=1e-13) + assert_allclose(D, [[0], [0]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[0, 1, 2, 3], [0, 1, 2, 3]], rtol=1e-13) + assert_allclose(den, [1, 2, 3, 4], rtol=1e-13) + + tf = (np.array([1, [2, 3]], dtype=object), [1, 6]) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[-6]], rtol=1e-31) + assert_allclose(B, [[1]], rtol=1e-31) + assert_allclose(C, [[1], [-9]], rtol=1e-31) + assert_allclose(D, [[0], [2]], rtol=1e-31) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[0, 1], [2, 3]], rtol=1e-13) + assert_allclose(den, [1, 6], rtol=1e-13) + + tf = (np.array([[1, -3], [1, 2, 3]], dtype=object), [1, 6, 5]) + A, B, C, D = tf2ss(*tf) + assert_allclose(A, [[-6, -5], [1, 0]], rtol=1e-13) + assert_allclose(B, [[1], [0]], rtol=1e-13) + assert_allclose(C, [[1, -3], [-4, -2]], rtol=1e-13) + assert_allclose(D, [[0], [1]], rtol=1e-13) + + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[0, 1, -3], [1, 2, 3]], rtol=1e-13) + assert_allclose(den, [1, 6, 5], rtol=1e-13) + + def test_all_int_arrays(self): + A = [[0, 1, 0], [0, 0, 1], [-3, -4, -2]] + B = [[0], [0], [1]] + C = [[5, 1, 0]] + D = [[0]] + num, den = ss2tf(A, B, C, D) + assert_allclose(num, [[0.0, 0.0, 1.0, 5.0]], rtol=1e-13, atol=1e-14) + assert_allclose(den, [1.0, 2.0, 4.0, 3.0], rtol=1e-13) + + def test_multioutput(self): + # Regression test for gh-2669. + + # 4 states + A = np.array([[-1.0, 0.0, 1.0, 0.0], + [-1.0, 0.0, 2.0, 0.0], + [-4.0, 0.0, 3.0, 0.0], + [-8.0, 8.0, 0.0, 4.0]]) + + # 1 input + B = np.array([[0.3], + [0.0], + [7.0], + [0.0]]) + + # 3 outputs + C = np.array([[0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [8.0, 8.0, 0.0, 0.0]]) + + D = np.array([[0.0], + [0.0], + [1.0]]) + + # Get the transfer functions for all the outputs in one call. + b_all, a = ss2tf(A, B, C, D) + + # Get the transfer functions for each output separately. + b0, a0 = ss2tf(A, B, C[0], D[0]) + b1, a1 = ss2tf(A, B, C[1], D[1]) + b2, a2 = ss2tf(A, B, C[2], D[2]) + + # Check that we got the same results. + assert_allclose(a0, a, rtol=1e-13) + assert_allclose(a1, a, rtol=1e-13) + assert_allclose(a2, a, rtol=1e-13) + assert_allclose(b_all, np.vstack((b0, b1, b2)), rtol=1e-13, atol=1e-14) + + +class TestLsim: + digits_accuracy = 7 + + def lti_nowarn(self, *args): + with suppress_warnings() as sup: + sup.filter(BadCoefficients) + system = lti(*args) + return system + + def test_first_order(self): + # y' = -y + # exact solution is y(t) = exp(-t) + system = self.lti_nowarn(-1.,1.,1.,0.) + t = np.linspace(0,5) + u = np.zeros_like(t) + tout, y, x = lsim(system, u, t, X0=[1.0]) + expected_x = np.exp(-tout) + assert_almost_equal(x, expected_x) + assert_almost_equal(y, expected_x) + + def test_second_order(self): + t = np.linspace(0, 10, 1001) + u = np.zeros_like(t) + # Second order system with a repeated root: x''(t) + 2*x(t) + x(t) = 0. + # With initial conditions x(0)=1.0 and x'(t)=0.0, the exact solution + # is (1-t)*exp(-t). + system = self.lti_nowarn([1.0], [1.0, 2.0, 1.0]) + tout, y, x = lsim(system, u, t, X0=[1.0, 0.0]) + expected_x = (1.0 - tout) * np.exp(-tout) + assert_almost_equal(x[:, 0], expected_x) + + def test_integrator(self): + # integrator: y' = u + system = self.lti_nowarn(0., 1., 1., 0.) + t = np.linspace(0,5) + u = t + tout, y, x = lsim(system, u, t) + expected_x = 0.5 * tout**2 + assert_almost_equal(x, expected_x, decimal=self.digits_accuracy) + assert_almost_equal(y, expected_x, decimal=self.digits_accuracy) + + def test_two_states(self): + # A system with two state variables, two inputs, and one output. + A = np.array([[-1.0, 0.0], [0.0, -2.0]]) + B = np.array([[1.0, 0.0], [0.0, 1.0]]) + C = np.array([1.0, 0.0]) + D = np.zeros((1, 2)) + + system = self.lti_nowarn(A, B, C, D) + + t = np.linspace(0, 10.0, 21) + u = np.zeros((len(t), 2)) + tout, y, x = lsim(system, U=u, T=t, X0=[1.0, 1.0]) + expected_y = np.exp(-tout) + expected_x0 = np.exp(-tout) + expected_x1 = np.exp(-2.0 * tout) + assert_almost_equal(y, expected_y) + assert_almost_equal(x[:, 0], expected_x0) + assert_almost_equal(x[:, 1], expected_x1) + + def test_double_integrator(self): + # double integrator: y'' = 2u + A = np.array([[0., 1.], [0., 0.]]) + B = np.array([[0.], [1.]]) + C = np.array([[2., 0.]]) + system = self.lti_nowarn(A, B, C, 0.) + t = np.linspace(0,5) + u = np.ones_like(t) + tout, y, x = lsim(system, u, t) + expected_x = np.transpose(np.array([0.5 * tout**2, tout])) + expected_y = tout**2 + assert_almost_equal(x, expected_x, decimal=self.digits_accuracy) + assert_almost_equal(y, expected_y, decimal=self.digits_accuracy) + + def test_jordan_block(self): + # Non-diagonalizable A matrix + # x1' + x1 = x2 + # x2' + x2 = u + # y = x1 + # Exact solution with u = 0 is y(t) = t exp(-t) + A = np.array([[-1., 1.], [0., -1.]]) + B = np.array([[0.], [1.]]) + C = np.array([[1., 0.]]) + system = self.lti_nowarn(A, B, C, 0.) + t = np.linspace(0,5) + u = np.zeros_like(t) + tout, y, x = lsim(system, u, t, X0=[0.0, 1.0]) + expected_y = tout * np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_miso(self): + # A system with two state variables, two inputs, and one output. + A = np.array([[-1.0, 0.0], [0.0, -2.0]]) + B = np.array([[1.0, 0.0], [0.0, 1.0]]) + C = np.array([1.0, 0.0]) + D = np.zeros((1,2)) + system = self.lti_nowarn(A, B, C, D) + + t = np.linspace(0, 5.0, 101) + u = np.zeros((len(t), 2)) + tout, y, x = lsim(system, u, t, X0=[1.0, 1.0]) + expected_y = np.exp(-tout) + expected_x0 = np.exp(-tout) + expected_x1 = np.exp(-2.0*tout) + assert_almost_equal(y, expected_y) + assert_almost_equal(x[:,0], expected_x0) + assert_almost_equal(x[:,1], expected_x1) + + def test_nonzero_initial_time(self): + system = self.lti_nowarn(-1.,1.,1.,0.) + t = np.linspace(1,2) + u = np.zeros_like(t) + tout, y, x = lsim(system, u, t, X0=[1.0]) + expected_y = np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_nonequal_timesteps(self): + t = np.array([0.0, 1.0, 1.0, 3.0]) + u = np.array([0.0, 0.0, 1.0, 1.0]) + # Simple integrator: x'(t) = u(t) + system = ([1.0], [1.0, 0.0]) + with assert_raises(ValueError, + match="Time steps are not equally spaced."): + tout, y, x = lsim(system, u, t, X0=[1.0]) + + +class TestImpulse: + def test_first_order(self): + # First order system: x'(t) + x(t) = u(t) + # Exact impulse response is x(t) = exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = impulse(system) + expected_y = np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_first_order_fixed_time(self): + # Specify the desired time values for the output. + + # First order system: x'(t) + x(t) = u(t) + # Exact impulse response is x(t) = exp(-t). + system = ([1.0], [1.0,1.0]) + n = 21 + t = np.linspace(0, 2.0, n) + tout, y = impulse(system, T=t) + assert_equal(tout.shape, (n,)) + assert_almost_equal(tout, t) + expected_y = np.exp(-t) + assert_almost_equal(y, expected_y) + + def test_first_order_initial(self): + # Specify an initial condition as a scalar. + + # First order system: x'(t) + x(t) = u(t), x(0)=3.0 + # Exact impulse response is x(t) = 4*exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = impulse(system, X0=3.0) + expected_y = 4.0 * np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_first_order_initial_list(self): + # Specify an initial condition as a list. + + # First order system: x'(t) + x(t) = u(t), x(0)=3.0 + # Exact impulse response is x(t) = 4*exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = impulse(system, X0=[3.0]) + expected_y = 4.0 * np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_integrator(self): + # Simple integrator: x'(t) = u(t) + system = ([1.0], [1.0,0.0]) + tout, y = impulse(system) + expected_y = np.ones_like(tout) + assert_almost_equal(y, expected_y) + + def test_second_order(self): + # Second order system with a repeated root: + # x''(t) + 2*x(t) + x(t) = u(t) + # The exact impulse response is t*exp(-t). + system = ([1.0], [1.0, 2.0, 1.0]) + tout, y = impulse(system) + expected_y = tout * np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_array_like(self): + # Test that function can accept sequences, scalars. + system = ([1.0], [1.0, 2.0, 1.0]) + # TODO: add meaningful test where X0 is a list + tout, y = impulse(system, X0=[3], T=[5, 6]) + tout, y = impulse(system, X0=[3], T=[5]) + + def test_array_like2(self): + system = ([1.0], [1.0, 2.0, 1.0]) + tout, y = impulse(system, X0=3, T=5) + + +class TestStep: + def test_first_order(self): + # First order system: x'(t) + x(t) = u(t) + # Exact step response is x(t) = 1 - exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = step(system) + expected_y = 1.0 - np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_first_order_fixed_time(self): + # Specify the desired time values for the output. + + # First order system: x'(t) + x(t) = u(t) + # Exact step response is x(t) = 1 - exp(-t). + system = ([1.0], [1.0,1.0]) + n = 21 + t = np.linspace(0, 2.0, n) + tout, y = step(system, T=t) + assert_equal(tout.shape, (n,)) + assert_almost_equal(tout, t) + expected_y = 1 - np.exp(-t) + assert_almost_equal(y, expected_y) + + def test_first_order_initial(self): + # Specify an initial condition as a scalar. + + # First order system: x'(t) + x(t) = u(t), x(0)=3.0 + # Exact step response is x(t) = 1 + 2*exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = step(system, X0=3.0) + expected_y = 1 + 2.0*np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_first_order_initial_list(self): + # Specify an initial condition as a list. + + # First order system: x'(t) + x(t) = u(t), x(0)=3.0 + # Exact step response is x(t) = 1 + 2*exp(-t). + system = ([1.0], [1.0,1.0]) + tout, y = step(system, X0=[3.0]) + expected_y = 1 + 2.0*np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_integrator(self): + # Simple integrator: x'(t) = u(t) + # Exact step response is x(t) = t. + system = ([1.0],[1.0,0.0]) + tout, y = step(system) + expected_y = tout + assert_almost_equal(y, expected_y) + + def test_second_order(self): + # Second order system with a repeated root: + # x''(t) + 2*x(t) + x(t) = u(t) + # The exact step response is 1 - (1 + t)*exp(-t). + system = ([1.0], [1.0, 2.0, 1.0]) + tout, y = step(system) + expected_y = 1 - (1 + tout) * np.exp(-tout) + assert_almost_equal(y, expected_y) + + def test_array_like(self): + # Test that function can accept sequences, scalars. + system = ([1.0], [1.0, 2.0, 1.0]) + # TODO: add meaningful test where X0 is a list + tout, y = step(system, T=[5, 6]) + + def test_complex_input(self): + # Test that complex input doesn't raise an error. + # `step` doesn't seem to have been designed for complex input, but this + # works and may be used, so add regression test. See gh-2654. + step(([], [-1], 1+0j)) + + +class TestLti: + def test_lti_instantiation(self): + # Test that lti can be instantiated with sequences, scalars. + # See PR-225. + + # TransferFunction + s = lti([1], [-1]) + assert_(isinstance(s, TransferFunction)) + assert_(isinstance(s, lti)) + assert_(not isinstance(s, dlti)) + assert_(s.dt is None) + + # ZerosPolesGain + s = lti(np.array([]), np.array([-1]), 1) + assert_(isinstance(s, ZerosPolesGain)) + assert_(isinstance(s, lti)) + assert_(not isinstance(s, dlti)) + assert_(s.dt is None) + + # StateSpace + s = lti([], [-1], 1) + s = lti([1], [-1], 1, 3) + assert_(isinstance(s, StateSpace)) + assert_(isinstance(s, lti)) + assert_(not isinstance(s, dlti)) + assert_(s.dt is None) + + +class TestStateSpace: + def test_initialization(self): + # Check that all initializations work + StateSpace(1, 1, 1, 1) + StateSpace([1], [2], [3], [4]) + StateSpace(np.array([[1, 2], [3, 4]]), np.array([[1], [2]]), + np.array([[1, 0]]), np.array([[0]])) + + def test_conversion(self): + # Check the conversion functions + s = StateSpace(1, 2, 3, 4) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(StateSpace(s) is not s) + assert_(s.to_ss() is not s) + + def test_properties(self): + # Test setters/getters for cross class properties. + # This implicitly tests to_tf() and to_zpk() + + # Getters + s = StateSpace(1, 1, 1, 1) + assert_equal(s.poles, [1]) + assert_equal(s.zeros, [0]) + assert_(s.dt is None) + + def test_operators(self): + # Test +/-/* operators on systems + + class BadType: + pass + + s1 = StateSpace(np.array([[-0.5, 0.7], [0.3, -0.8]]), + np.array([[1], [0]]), + np.array([[1, 0]]), + np.array([[0]]), + ) + + s2 = StateSpace(np.array([[-0.2, -0.1], [0.4, -0.1]]), + np.array([[1], [0]]), + np.array([[1, 0]]), + np.array([[0]]) + ) + + s_discrete = s1.to_discrete(0.1) + s2_discrete = s2.to_discrete(0.2) + s3_discrete = s2.to_discrete(0.1) + + # Impulse response + t = np.linspace(0, 1, 100) + u = np.zeros_like(t) + u[0] = 1 + + # Test multiplication + for typ in (int, float, complex, np.float32, np.complex128, np.array): + assert_allclose(lsim(typ(2) * s1, U=u, T=t)[1], + typ(2) * lsim(s1, U=u, T=t)[1]) + + assert_allclose(lsim(s1 * typ(2), U=u, T=t)[1], + lsim(s1, U=u, T=t)[1] * typ(2)) + + assert_allclose(lsim(s1 / typ(2), U=u, T=t)[1], + lsim(s1, U=u, T=t)[1] / typ(2)) + + with assert_raises(TypeError): + typ(2) / s1 + + assert_allclose(lsim(s1 * 2, U=u, T=t)[1], + lsim(s1, U=2 * u, T=t)[1]) + + assert_allclose(lsim(s1 * s2, U=u, T=t)[1], + lsim(s1, U=lsim(s2, U=u, T=t)[1], T=t)[1], + atol=1e-5) + + with assert_raises(TypeError): + s1 / s1 + + with assert_raises(TypeError): + s1 * s_discrete + + with assert_raises(TypeError): + # Check different discretization constants + s_discrete * s2_discrete + + with assert_raises(TypeError): + s1 * BadType() + + with assert_raises(TypeError): + BadType() * s1 + + with assert_raises(TypeError): + s1 / BadType() + + with assert_raises(TypeError): + BadType() / s1 + + # Test addition + assert_allclose(lsim(s1 + 2, U=u, T=t)[1], + 2 * u + lsim(s1, U=u, T=t)[1]) + + # Check for dimension mismatch + with assert_raises(ValueError): + s1 + np.array([1, 2]) + + with assert_raises(ValueError): + np.array([1, 2]) + s1 + + with assert_raises(TypeError): + s1 + s_discrete + + with assert_raises(ValueError): + s1 / np.array([[1, 2], [3, 4]]) + + with assert_raises(TypeError): + # Check different discretization constants + s_discrete + s2_discrete + + with assert_raises(TypeError): + s1 + BadType() + + with assert_raises(TypeError): + BadType() + s1 + + assert_allclose(lsim(s1 + s2, U=u, T=t)[1], + lsim(s1, U=u, T=t)[1] + lsim(s2, U=u, T=t)[1]) + + # Test subtraction + assert_allclose(lsim(s1 - 2, U=u, T=t)[1], + -2 * u + lsim(s1, U=u, T=t)[1]) + + assert_allclose(lsim(2 - s1, U=u, T=t)[1], + 2 * u + lsim(-s1, U=u, T=t)[1]) + + assert_allclose(lsim(s1 - s2, U=u, T=t)[1], + lsim(s1, U=u, T=t)[1] - lsim(s2, U=u, T=t)[1]) + + with assert_raises(TypeError): + s1 - BadType() + + with assert_raises(TypeError): + BadType() - s1 + + s = s_discrete + s3_discrete + assert_(s.dt == 0.1) + + s = s_discrete * s3_discrete + assert_(s.dt == 0.1) + + s = 3 * s_discrete + assert_(s.dt == 0.1) + + s = -s_discrete + assert_(s.dt == 0.1) + +class TestTransferFunction: + def test_initialization(self): + # Check that all initializations work + TransferFunction(1, 1) + TransferFunction([1], [2]) + TransferFunction(np.array([1]), np.array([2])) + + def test_conversion(self): + # Check the conversion functions + s = TransferFunction([1, 0], [1, -1]) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(TransferFunction(s) is not s) + assert_(s.to_tf() is not s) + + def test_properties(self): + # Test setters/getters for cross class properties. + # This implicitly tests to_ss() and to_zpk() + + # Getters + s = TransferFunction([1, 0], [1, -1]) + assert_equal(s.poles, [1]) + assert_equal(s.zeros, [0]) + + +class TestZerosPolesGain: + def test_initialization(self): + # Check that all initializations work + ZerosPolesGain(1, 1, 1) + ZerosPolesGain([1], [2], 1) + ZerosPolesGain(np.array([1]), np.array([2]), 1) + + def test_conversion(self): + #Check the conversion functions + s = ZerosPolesGain(1, 2, 3) + assert_(isinstance(s.to_ss(), StateSpace)) + assert_(isinstance(s.to_tf(), TransferFunction)) + assert_(isinstance(s.to_zpk(), ZerosPolesGain)) + + # Make sure copies work + assert_(ZerosPolesGain(s) is not s) + assert_(s.to_zpk() is not s) + + +class Test_abcd_normalize: + def setup_method(self): + self.A = np.array([[1.0, 2.0], [3.0, 4.0]]) + self.B = np.array([[-1.0], [5.0]]) + self.C = np.array([[4.0, 5.0]]) + self.D = np.array([[2.5]]) + + def test_no_matrix_fails(self): + assert_raises(ValueError, abcd_normalize) + + def test_A_nosquare_fails(self): + assert_raises(ValueError, abcd_normalize, [1, -1], + self.B, self.C, self.D) + + def test_AB_mismatch_fails(self): + assert_raises(ValueError, abcd_normalize, self.A, [-1, 5], + self.C, self.D) + + def test_AC_mismatch_fails(self): + assert_raises(ValueError, abcd_normalize, self.A, self.B, + [[4.0], [5.0]], self.D) + + def test_CD_mismatch_fails(self): + assert_raises(ValueError, abcd_normalize, self.A, self.B, + self.C, [2.5, 0]) + + def test_BD_mismatch_fails(self): + assert_raises(ValueError, abcd_normalize, self.A, [-1, 5], + self.C, self.D) + + def test_normalized_matrices_unchanged(self): + A, B, C, D = abcd_normalize(self.A, self.B, self.C, self.D) + assert_equal(A, self.A) + assert_equal(B, self.B) + assert_equal(C, self.C) + assert_equal(D, self.D) + + def test_shapes(self): + A, B, C, D = abcd_normalize(self.A, self.B, [1, 0], 0) + assert_equal(A.shape[0], A.shape[1]) + assert_equal(A.shape[0], B.shape[0]) + assert_equal(A.shape[0], C.shape[1]) + assert_equal(C.shape[0], D.shape[0]) + assert_equal(B.shape[1], D.shape[1]) + + def test_zero_dimension_is_not_none1(self): + B_ = np.zeros((2, 0)) + D_ = np.zeros((0, 0)) + A, B, C, D = abcd_normalize(A=self.A, B=B_, D=D_) + assert_equal(A, self.A) + assert_equal(B, B_) + assert_equal(D, D_) + assert_equal(C.shape[0], D_.shape[0]) + assert_equal(C.shape[1], self.A.shape[0]) + + def test_zero_dimension_is_not_none2(self): + B_ = np.zeros((2, 0)) + C_ = np.zeros((0, 2)) + A, B, C, D = abcd_normalize(A=self.A, B=B_, C=C_) + assert_equal(A, self.A) + assert_equal(B, B_) + assert_equal(C, C_) + assert_equal(D.shape[0], C_.shape[0]) + assert_equal(D.shape[1], B_.shape[1]) + + def test_missing_A(self): + A, B, C, D = abcd_normalize(B=self.B, C=self.C, D=self.D) + assert_equal(A.shape[0], A.shape[1]) + assert_equal(A.shape[0], B.shape[0]) + assert_equal(A.shape, (self.B.shape[0], self.B.shape[0])) + + def test_missing_B(self): + A, B, C, D = abcd_normalize(A=self.A, C=self.C, D=self.D) + assert_equal(B.shape[0], A.shape[0]) + assert_equal(B.shape[1], D.shape[1]) + assert_equal(B.shape, (self.A.shape[0], self.D.shape[1])) + + def test_missing_C(self): + A, B, C, D = abcd_normalize(A=self.A, B=self.B, D=self.D) + assert_equal(C.shape[0], D.shape[0]) + assert_equal(C.shape[1], A.shape[0]) + assert_equal(C.shape, (self.D.shape[0], self.A.shape[0])) + + def test_missing_D(self): + A, B, C, D = abcd_normalize(A=self.A, B=self.B, C=self.C) + assert_equal(D.shape[0], C.shape[0]) + assert_equal(D.shape[1], B.shape[1]) + assert_equal(D.shape, (self.C.shape[0], self.B.shape[1])) + + def test_missing_AB(self): + A, B, C, D = abcd_normalize(C=self.C, D=self.D) + assert_equal(A.shape[0], A.shape[1]) + assert_equal(A.shape[0], B.shape[0]) + assert_equal(B.shape[1], D.shape[1]) + assert_equal(A.shape, (self.C.shape[1], self.C.shape[1])) + assert_equal(B.shape, (self.C.shape[1], self.D.shape[1])) + + def test_missing_AC(self): + A, B, C, D = abcd_normalize(B=self.B, D=self.D) + assert_equal(A.shape[0], A.shape[1]) + assert_equal(A.shape[0], B.shape[0]) + assert_equal(C.shape[0], D.shape[0]) + assert_equal(C.shape[1], A.shape[0]) + assert_equal(A.shape, (self.B.shape[0], self.B.shape[0])) + assert_equal(C.shape, (self.D.shape[0], self.B.shape[0])) + + def test_missing_AD(self): + A, B, C, D = abcd_normalize(B=self.B, C=self.C) + assert_equal(A.shape[0], A.shape[1]) + assert_equal(A.shape[0], B.shape[0]) + assert_equal(D.shape[0], C.shape[0]) + assert_equal(D.shape[1], B.shape[1]) + assert_equal(A.shape, (self.B.shape[0], self.B.shape[0])) + assert_equal(D.shape, (self.C.shape[0], self.B.shape[1])) + + def test_missing_BC(self): + A, B, C, D = abcd_normalize(A=self.A, D=self.D) + assert_equal(B.shape[0], A.shape[0]) + assert_equal(B.shape[1], D.shape[1]) + assert_equal(C.shape[0], D.shape[0]) + assert_equal(C.shape[1], A.shape[0]) + assert_equal(B.shape, (self.A.shape[0], self.D.shape[1])) + assert_equal(C.shape, (self.D.shape[0], self.A.shape[0])) + + def test_missing_ABC_fails(self): + assert_raises(ValueError, abcd_normalize, D=self.D) + + def test_missing_BD_fails(self): + assert_raises(ValueError, abcd_normalize, A=self.A, C=self.C) + + def test_missing_CD_fails(self): + assert_raises(ValueError, abcd_normalize, A=self.A, B=self.B) + + +class Test_bode: + + def test_01(self): + # Test bode() magnitude calculation (manual sanity check). + # 1st order low-pass filter: H(s) = 1 / (s + 1), + # cutoff: 1 rad/s, slope: -20 dB/decade + # H(s=0.1) ~= 0 dB + # H(s=1) ~= -3 dB + # H(s=10) ~= -20 dB + # H(s=100) ~= -40 dB + system = lti([1], [1, 1]) + w = [0.1, 1, 10, 100] + w, mag, phase = bode(system, w=w) + expected_mag = [0, -3, -20, -40] + assert_almost_equal(mag, expected_mag, decimal=1) + + def test_02(self): + # Test bode() phase calculation (manual sanity check). + # 1st order low-pass filter: H(s) = 1 / (s + 1), + # angle(H(s=0.1)) ~= -5.7 deg + # angle(H(s=1)) ~= -45 deg + # angle(H(s=10)) ~= -84.3 deg + system = lti([1], [1, 1]) + w = [0.1, 1, 10] + w, mag, phase = bode(system, w=w) + expected_phase = [-5.7, -45, -84.3] + assert_almost_equal(phase, expected_phase, decimal=1) + + def test_03(self): + # Test bode() magnitude calculation. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + system = lti([1], [1, 1]) + w = [0.1, 1, 10, 100] + w, mag, phase = bode(system, w=w) + jw = w * 1j + y = np.polyval(system.num, jw) / np.polyval(system.den, jw) + expected_mag = 20.0 * np.log10(abs(y)) + assert_almost_equal(mag, expected_mag) + + def test_04(self): + # Test bode() phase calculation. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + system = lti([1], [1, 1]) + w = [0.1, 1, 10, 100] + w, mag, phase = bode(system, w=w) + jw = w * 1j + y = np.polyval(system.num, jw) / np.polyval(system.den, jw) + expected_phase = np.arctan2(y.imag, y.real) * 180.0 / np.pi + assert_almost_equal(phase, expected_phase) + + def test_05(self): + # Test that bode() finds a reasonable frequency range. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + system = lti([1], [1, 1]) + n = 10 + # Expected range is from 0.01 to 10. + expected_w = np.logspace(-2, 1, n) + w, mag, phase = bode(system, n=n) + assert_almost_equal(w, expected_w) + + def test_06(self): + # Test that bode() doesn't fail on a system with a pole at 0. + # integrator, pole at zero: H(s) = 1 / s + system = lti([1], [1, 0]) + w, mag, phase = bode(system, n=2) + assert_equal(w[0], 0.01) # a fail would give not-a-number + + def test_07(self): + # bode() should not fail on a system with pure imaginary poles. + # The test passes if bode doesn't raise an exception. + system = lti([1], [1, 0, 100]) + w, mag, phase = bode(system, n=2) + + def test_08(self): + # Test that bode() return continuous phase, issues/2331. + system = lti([], [-10, -30, -40, -60, -70], 1) + w, mag, phase = system.bode(w=np.logspace(-3, 40, 100)) + assert_almost_equal(min(phase), -450, decimal=15) + + def test_from_state_space(self): + # Ensure that bode works with a system that was created from the + # state space representation matrices A, B, C, D. In this case, + # system.num will be a 2-D array with shape (1, n+1), where (n,n) + # is the shape of A. + # A Butterworth lowpass filter is used, so we know the exact + # frequency response. + a = np.array([1.0, 2.0, 2.0, 1.0]) + A = linalg.companion(a).T + B = np.array([[0.0], [0.0], [1.0]]) + C = np.array([[1.0, 0.0, 0.0]]) + D = np.array([[0.0]]) + with suppress_warnings() as sup: + sup.filter(BadCoefficients) + system = lti(A, B, C, D) + w, mag, phase = bode(system, n=100) + + expected_magnitude = 20 * np.log10(np.sqrt(1.0 / (1.0 + w**6))) + assert_almost_equal(mag, expected_magnitude) + + +class Test_freqresp: + + def test_output_manual(self): + # Test freqresp() output calculation (manual sanity check). + # 1st order low-pass filter: H(s) = 1 / (s + 1), + # re(H(s=0.1)) ~= 0.99 + # re(H(s=1)) ~= 0.5 + # re(H(s=10)) ~= 0.0099 + system = lti([1], [1, 1]) + w = [0.1, 1, 10] + w, H = freqresp(system, w=w) + expected_re = [0.99, 0.5, 0.0099] + expected_im = [-0.099, -0.5, -0.099] + assert_almost_equal(H.real, expected_re, decimal=1) + assert_almost_equal(H.imag, expected_im, decimal=1) + + def test_output(self): + # Test freqresp() output calculation. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + system = lti([1], [1, 1]) + w = [0.1, 1, 10, 100] + w, H = freqresp(system, w=w) + s = w * 1j + expected = np.polyval(system.num, s) / np.polyval(system.den, s) + assert_almost_equal(H.real, expected.real) + assert_almost_equal(H.imag, expected.imag) + + def test_freq_range(self): + # Test that freqresp() finds a reasonable frequency range. + # 1st order low-pass filter: H(s) = 1 / (s + 1) + # Expected range is from 0.01 to 10. + system = lti([1], [1, 1]) + n = 10 + expected_w = np.logspace(-2, 1, n) + w, H = freqresp(system, n=n) + assert_almost_equal(w, expected_w) + + def test_pole_zero(self): + # Test that freqresp() doesn't fail on a system with a pole at 0. + # integrator, pole at zero: H(s) = 1 / s + system = lti([1], [1, 0]) + w, H = freqresp(system, n=2) + assert_equal(w[0], 0.01) # a fail would give not-a-number + + def test_from_state_space(self): + # Ensure that freqresp works with a system that was created from the + # state space representation matrices A, B, C, D. In this case, + # system.num will be a 2-D array with shape (1, n+1), where (n,n) is + # the shape of A. + # A Butterworth lowpass filter is used, so we know the exact + # frequency response. + a = np.array([1.0, 2.0, 2.0, 1.0]) + A = linalg.companion(a).T + B = np.array([[0.0],[0.0],[1.0]]) + C = np.array([[1.0, 0.0, 0.0]]) + D = np.array([[0.0]]) + with suppress_warnings() as sup: + sup.filter(BadCoefficients) + system = lti(A, B, C, D) + w, H = freqresp(system, n=100) + s = w * 1j + expected = (1.0 / (1.0 + 2*s + 2*s**2 + s**3)) + assert_almost_equal(H.real, expected.real) + assert_almost_equal(H.imag, expected.imag) + + def test_from_zpk(self): + # 4th order low-pass filter: H(s) = 1 / (s + 1) + system = lti([],[-1]*4,[1]) + w = [0.1, 1, 10, 100] + w, H = freqresp(system, w=w) + s = w * 1j + expected = 1 / (s + 1)**4 + assert_almost_equal(H.real, expected.real) + assert_almost_equal(H.imag, expected.imag) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_max_len_seq.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_max_len_seq.py new file mode 100644 index 0000000000000000000000000000000000000000..c4e79969974fb4ba376bbf4d935a7a94e3064a5a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_max_len_seq.py @@ -0,0 +1,65 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_array_equal +from pytest import raises as assert_raises + +from numpy.fft import fft, ifft + +from scipy.signal import max_len_seq + + +class TestMLS: + + def test_mls_inputs(self): + # can't all be zero state + assert_raises(ValueError, max_len_seq, + 10, state=np.zeros(10)) + # wrong size state + assert_raises(ValueError, max_len_seq, 10, + state=np.ones(3)) + # wrong length + assert_raises(ValueError, max_len_seq, 10, length=-1) + assert_array_equal(max_len_seq(10, length=0)[0], []) + # unknown taps + assert_raises(ValueError, max_len_seq, 64) + # bad taps + assert_raises(ValueError, max_len_seq, 10, taps=[-1, 1]) + + def test_mls_output(self): + # define some alternate working taps + alt_taps = {2: [1], 3: [2], 4: [3], 5: [4, 3, 2], 6: [5, 4, 1], 7: [4], + 8: [7, 5, 3]} + # assume the other bit levels work, too slow to test higher orders... + for nbits in range(2, 8): + for state in [None, np.round(np.random.rand(nbits))]: + for taps in [None, alt_taps[nbits]]: + if state is not None and np.all(state == 0): + state[0] = 1 # they can't all be zero + orig_m = max_len_seq(nbits, state=state, + taps=taps)[0] + m = 2. * orig_m - 1. # convert to +/- 1 representation + # First, make sure we got all 1's or -1 + err_msg = "mls had non binary terms" + assert_array_equal(np.abs(m), np.ones_like(m), + err_msg=err_msg) + # Test via circular cross-correlation, which is just mult. + # in the frequency domain with one signal conjugated + tester = np.real(ifft(fft(m) * np.conj(fft(m)))) + out_len = 2**nbits - 1 + # impulse amplitude == test_len + err_msg = "mls impulse has incorrect value" + assert_allclose(tester[0], out_len, err_msg=err_msg) + # steady-state is -1 + err_msg = "mls steady-state has incorrect value" + assert_allclose(tester[1:], np.full(out_len - 1, -1), + err_msg=err_msg) + # let's do the split thing using a couple options + for n in (1, 2**(nbits - 1)): + m1, s1 = max_len_seq(nbits, state=state, taps=taps, + length=n) + m2, s2 = max_len_seq(nbits, state=s1, taps=taps, + length=1) + m3, s3 = max_len_seq(nbits, state=s2, taps=taps, + length=out_len - n - 1) + new_m = np.concatenate((m1, m2, m3)) + assert_array_equal(orig_m, new_m) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_peak_finding.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_peak_finding.py new file mode 100644 index 0000000000000000000000000000000000000000..77380c5496364745775b0a3b6fcf149e72722398 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_peak_finding.py @@ -0,0 +1,891 @@ +import copy + +import numpy as np +from numpy.testing import ( + assert_, + assert_equal, + assert_allclose, + assert_array_equal +) +import pytest +from pytest import raises, warns + +from scipy.signal._peak_finding import ( + argrelmax, + argrelmin, + peak_prominences, + peak_widths, + _unpack_condition_args, + find_peaks, + find_peaks_cwt, + _identify_ridge_lines +) +from scipy.signal.windows import gaussian +from scipy.signal._peak_finding_utils import _local_maxima_1d, PeakPropertyWarning + + +def _gen_gaussians(center_locs, sigmas, total_length): + xdata = np.arange(0, total_length).astype(float) + out_data = np.zeros(total_length, dtype=float) + for ind, sigma in enumerate(sigmas): + tmp = (xdata - center_locs[ind]) / sigma + out_data += np.exp(-(tmp**2)) + return out_data + + +def _gen_gaussians_even(sigmas, total_length): + num_peaks = len(sigmas) + delta = total_length / (num_peaks + 1) + center_locs = np.linspace(delta, total_length - delta, num=num_peaks).astype(int) + out_data = _gen_gaussians(center_locs, sigmas, total_length) + return out_data, center_locs + + +def _gen_ridge_line(start_locs, max_locs, length, distances, gaps): + """ + Generate coordinates for a ridge line. + + Will be a series of coordinates, starting a start_loc (length 2). + The maximum distance between any adjacent columns will be + `max_distance`, the max distance between adjacent rows + will be `map_gap'. + + `max_locs` should be the size of the intended matrix. The + ending coordinates are guaranteed to be less than `max_locs`, + although they may not approach `max_locs` at all. + """ + + def keep_bounds(num, max_val): + out = max(num, 0) + out = min(out, max_val) + return out + + gaps = copy.deepcopy(gaps) + distances = copy.deepcopy(distances) + + locs = np.zeros([length, 2], dtype=int) + locs[0, :] = start_locs + total_length = max_locs[0] - start_locs[0] - sum(gaps) + if total_length < length: + raise ValueError('Cannot generate ridge line according to constraints') + dist_int = length / len(distances) - 1 + gap_int = length / len(gaps) - 1 + for ind in range(1, length): + nextcol = locs[ind - 1, 1] + nextrow = locs[ind - 1, 0] + 1 + if (ind % dist_int == 0) and (len(distances) > 0): + nextcol += ((-1)**ind)*distances.pop() + if (ind % gap_int == 0) and (len(gaps) > 0): + nextrow += gaps.pop() + nextrow = keep_bounds(nextrow, max_locs[0]) + nextcol = keep_bounds(nextcol, max_locs[1]) + locs[ind, :] = [nextrow, nextcol] + + return [locs[:, 0], locs[:, 1]] + + +class TestLocalMaxima1d: + + def test_empty(self): + """Test with empty signal.""" + x = np.array([], dtype=np.float64) + for array in _local_maxima_1d(x): + assert_equal(array, np.array([])) + assert_(array.base is None) + + def test_linear(self): + """Test with linear signal.""" + x = np.linspace(0, 100) + for array in _local_maxima_1d(x): + assert_equal(array, np.array([])) + assert_(array.base is None) + + def test_simple(self): + """Test with simple signal.""" + x = np.linspace(-10, 10, 50) + x[2::3] += 1 + expected = np.arange(2, 50, 3) + for array in _local_maxima_1d(x): + # For plateaus of size 1, the edges are identical with the + # midpoints + assert_equal(array, expected) + assert_(array.base is None) + + def test_flat_maxima(self): + """Test if flat maxima are detected correctly.""" + x = np.array([-1.3, 0, 1, 0, 2, 2, 0, 3, 3, 3, 2.99, 4, 4, 4, 4, -10, + -5, -5, -5, -5, -5, -10]) + midpoints, left_edges, right_edges = _local_maxima_1d(x) + assert_equal(midpoints, np.array([2, 4, 8, 12, 18])) + assert_equal(left_edges, np.array([2, 4, 7, 11, 16])) + assert_equal(right_edges, np.array([2, 5, 9, 14, 20])) + + @pytest.mark.parametrize('x', [ + np.array([1., 0, 2]), + np.array([3., 3, 0, 4, 4]), + np.array([5., 5, 5, 0, 6, 6, 6]), + ]) + def test_signal_edges(self, x): + """Test if behavior on signal edges is correct.""" + for array in _local_maxima_1d(x): + assert_equal(array, np.array([])) + assert_(array.base is None) + + def test_exceptions(self): + """Test input validation and raised exceptions.""" + with raises(ValueError, match="wrong number of dimensions"): + _local_maxima_1d(np.ones((1, 1))) + with raises(ValueError, match="expected 'const float64_t'"): + _local_maxima_1d(np.ones(1, dtype=int)) + with raises(TypeError, match="list"): + _local_maxima_1d([1., 2.]) + with raises(TypeError, match="'x' must not be None"): + _local_maxima_1d(None) + + +class TestRidgeLines: + + def test_empty(self): + test_matr = np.zeros([20, 100]) + lines = _identify_ridge_lines(test_matr, np.full(20, 2), 1) + assert_(len(lines) == 0) + + def test_minimal(self): + test_matr = np.zeros([20, 100]) + test_matr[0, 10] = 1 + lines = _identify_ridge_lines(test_matr, np.full(20, 2), 1) + assert_(len(lines) == 1) + + test_matr = np.zeros([20, 100]) + test_matr[0:2, 10] = 1 + lines = _identify_ridge_lines(test_matr, np.full(20, 2), 1) + assert_(len(lines) == 1) + + def test_single_pass(self): + distances = [0, 1, 2, 5] + gaps = [0, 1, 2, 0, 1] + test_matr = np.zeros([20, 50]) + 1e-12 + length = 12 + line = _gen_ridge_line([0, 25], test_matr.shape, length, distances, gaps) + test_matr[line[0], line[1]] = 1 + max_distances = np.full(20, max(distances)) + identified_lines = _identify_ridge_lines(test_matr, + max_distances, + max(gaps) + 1) + assert_array_equal(identified_lines, [line]) + + def test_single_bigdist(self): + distances = [0, 1, 2, 5] + gaps = [0, 1, 2, 4] + test_matr = np.zeros([20, 50]) + length = 12 + line = _gen_ridge_line([0, 25], test_matr.shape, length, distances, gaps) + test_matr[line[0], line[1]] = 1 + max_dist = 3 + max_distances = np.full(20, max_dist) + #This should get 2 lines, since the distance is too large + identified_lines = _identify_ridge_lines(test_matr, + max_distances, + max(gaps) + 1) + assert_(len(identified_lines) == 2) + + for iline in identified_lines: + adists = np.diff(iline[1]) + np.testing.assert_array_less(np.abs(adists), max_dist) + + agaps = np.diff(iline[0]) + np.testing.assert_array_less(np.abs(agaps), max(gaps) + 0.1) + + def test_single_biggap(self): + distances = [0, 1, 2, 5] + max_gap = 3 + gaps = [0, 4, 2, 1] + test_matr = np.zeros([20, 50]) + length = 12 + line = _gen_ridge_line([0, 25], test_matr.shape, length, distances, gaps) + test_matr[line[0], line[1]] = 1 + max_dist = 6 + max_distances = np.full(20, max_dist) + #This should get 2 lines, since the gap is too large + identified_lines = _identify_ridge_lines(test_matr, max_distances, max_gap) + assert_(len(identified_lines) == 2) + + for iline in identified_lines: + adists = np.diff(iline[1]) + np.testing.assert_array_less(np.abs(adists), max_dist) + + agaps = np.diff(iline[0]) + np.testing.assert_array_less(np.abs(agaps), max(gaps) + 0.1) + + def test_single_biggaps(self): + distances = [0] + max_gap = 1 + gaps = [3, 6] + test_matr = np.zeros([50, 50]) + length = 30 + line = _gen_ridge_line([0, 25], test_matr.shape, length, distances, gaps) + test_matr[line[0], line[1]] = 1 + max_dist = 1 + max_distances = np.full(50, max_dist) + #This should get 3 lines, since the gaps are too large + identified_lines = _identify_ridge_lines(test_matr, max_distances, max_gap) + assert_(len(identified_lines) == 3) + + for iline in identified_lines: + adists = np.diff(iline[1]) + np.testing.assert_array_less(np.abs(adists), max_dist) + + agaps = np.diff(iline[0]) + np.testing.assert_array_less(np.abs(agaps), max(gaps) + 0.1) + + +class TestArgrel: + + def test_empty(self): + # Regression test for gh-2832. + # When there are no relative extrema, make sure that + # the number of empty arrays returned matches the + # dimension of the input. + + empty_array = np.array([], dtype=int) + + z1 = np.zeros(5) + + i = argrelmin(z1) + assert_equal(len(i), 1) + assert_array_equal(i[0], empty_array) + + z2 = np.zeros((3,5)) + + row, col = argrelmin(z2, axis=0) + assert_array_equal(row, empty_array) + assert_array_equal(col, empty_array) + + row, col = argrelmin(z2, axis=1) + assert_array_equal(row, empty_array) + assert_array_equal(col, empty_array) + + def test_basic(self): + # Note: the docstrings for the argrel{min,max,extrema} functions + # do not give a guarantee of the order of the indices, so we'll + # sort them before testing. + + x = np.array([[1, 2, 2, 3, 2], + [2, 1, 2, 2, 3], + [3, 2, 1, 2, 2], + [2, 3, 2, 1, 2], + [1, 2, 3, 2, 1]]) + + row, col = argrelmax(x, axis=0) + order = np.argsort(row) + assert_equal(row[order], [1, 2, 3]) + assert_equal(col[order], [4, 0, 1]) + + row, col = argrelmax(x, axis=1) + order = np.argsort(row) + assert_equal(row[order], [0, 3, 4]) + assert_equal(col[order], [3, 1, 2]) + + row, col = argrelmin(x, axis=0) + order = np.argsort(row) + assert_equal(row[order], [1, 2, 3]) + assert_equal(col[order], [1, 2, 3]) + + row, col = argrelmin(x, axis=1) + order = np.argsort(row) + assert_equal(row[order], [1, 2, 3]) + assert_equal(col[order], [1, 2, 3]) + + def test_highorder(self): + order = 2 + sigmas = [1.0, 2.0, 10.0, 5.0, 15.0] + test_data, act_locs = _gen_gaussians_even(sigmas, 500) + test_data[act_locs + order] = test_data[act_locs]*0.99999 + test_data[act_locs - order] = test_data[act_locs]*0.99999 + rel_max_locs = argrelmax(test_data, order=order, mode='clip')[0] + + assert_(len(rel_max_locs) == len(act_locs)) + assert_((rel_max_locs == act_locs).all()) + + def test_2d_gaussians(self): + sigmas = [1.0, 2.0, 10.0] + test_data, act_locs = _gen_gaussians_even(sigmas, 100) + rot_factor = 20 + rot_range = np.arange(0, len(test_data)) - rot_factor + test_data_2 = np.vstack([test_data, test_data[rot_range]]) + rel_max_rows, rel_max_cols = argrelmax(test_data_2, axis=1, order=1) + + for rw in range(0, test_data_2.shape[0]): + inds = (rel_max_rows == rw) + + assert_(len(rel_max_cols[inds]) == len(act_locs)) + assert_((act_locs == (rel_max_cols[inds] - rot_factor*rw)).all()) + + +class TestPeakProminences: + + def test_empty(self): + """ + Test if an empty array is returned if no peaks are provided. + """ + out = peak_prominences([1, 2, 3], []) + for arr, dtype in zip(out, [np.float64, np.intp, np.intp]): + assert_(arr.size == 0) + assert_(arr.dtype == dtype) + + out = peak_prominences([], []) + for arr, dtype in zip(out, [np.float64, np.intp, np.intp]): + assert_(arr.size == 0) + assert_(arr.dtype == dtype) + + def test_basic(self): + """ + Test if height of prominences is correctly calculated in signal with + rising baseline (peak widths are 1 sample). + """ + # Prepare basic signal + x = np.array([-1, 1.2, 1.2, 1, 3.2, 1.3, 2.88, 2.1]) + peaks = np.array([1, 2, 4, 6]) + lbases = np.array([0, 0, 0, 5]) + rbases = np.array([3, 3, 5, 7]) + proms = x[peaks] - np.max([x[lbases], x[rbases]], axis=0) + # Test if calculation matches handcrafted result + out = peak_prominences(x, peaks) + assert_equal(out[0], proms) + assert_equal(out[1], lbases) + assert_equal(out[2], rbases) + + def test_edge_cases(self): + """ + Test edge cases. + """ + # Peaks have same height, prominence and bases + x = [0, 2, 1, 2, 1, 2, 0] + peaks = [1, 3, 5] + proms, lbases, rbases = peak_prominences(x, peaks) + assert_equal(proms, [2, 2, 2]) + assert_equal(lbases, [0, 0, 0]) + assert_equal(rbases, [6, 6, 6]) + + # Peaks have same height & prominence but different bases + x = [0, 1, 0, 1, 0, 1, 0] + peaks = np.array([1, 3, 5]) + proms, lbases, rbases = peak_prominences(x, peaks) + assert_equal(proms, [1, 1, 1]) + assert_equal(lbases, peaks - 1) + assert_equal(rbases, peaks + 1) + + def test_non_contiguous(self): + """ + Test with non-C-contiguous input arrays. + """ + x = np.repeat([-9, 9, 9, 0, 3, 1], 2) + peaks = np.repeat([1, 2, 4], 2) + proms, lbases, rbases = peak_prominences(x[::2], peaks[::2]) + assert_equal(proms, [9, 9, 2]) + assert_equal(lbases, [0, 0, 3]) + assert_equal(rbases, [3, 3, 5]) + + def test_wlen(self): + """ + Test if wlen actually shrinks the evaluation range correctly. + """ + x = [0, 1, 2, 3, 1, 0, -1] + peak = [3] + # Test rounding behavior of wlen + assert_equal(peak_prominences(x, peak), [3., 0, 6]) + for wlen, i in [(8, 0), (7, 0), (6, 0), (5, 1), (3.2, 1), (3, 2), (1.1, 2)]: + assert_equal(peak_prominences(x, peak, wlen), [3. - i, 0 + i, 6 - i]) + + def test_exceptions(self): + """ + Verify that exceptions and warnings are raised. + """ + # x with dimension > 1 + with raises(ValueError, match='1-D array'): + peak_prominences([[0, 1, 1, 0]], [1, 2]) + # peaks with dimension > 1 + with raises(ValueError, match='1-D array'): + peak_prominences([0, 1, 1, 0], [[1, 2]]) + # x with dimension < 1 + with raises(ValueError, match='1-D array'): + peak_prominences(3, [0,]) + + # empty x with supplied + with raises(ValueError, match='not a valid index'): + peak_prominences([], [0]) + # invalid indices with non-empty x + for p in [-100, -1, 3, 1000]: + with raises(ValueError, match='not a valid index'): + peak_prominences([1, 0, 2], [p]) + + # peaks is not cast-able to np.intp + with raises(TypeError, match='cannot safely cast'): + peak_prominences([0, 1, 1, 0], [1.1, 2.3]) + + # wlen < 3 + with raises(ValueError, match='wlen'): + peak_prominences(np.arange(10), [3, 5], wlen=1) + + def test_warnings(self): + """ + Verify that appropriate warnings are raised. + """ + msg = "some peaks have a prominence of 0" + for p in [0, 1, 2]: + with warns(PeakPropertyWarning, match=msg): + peak_prominences([1, 0, 2], [p,]) + with warns(PeakPropertyWarning, match=msg): + peak_prominences([0, 1, 1, 1, 0], [2], wlen=2) + + +class TestPeakWidths: + + def test_empty(self): + """ + Test if an empty array is returned if no peaks are provided. + """ + widths = peak_widths([], [])[0] + assert_(isinstance(widths, np.ndarray)) + assert_equal(widths.size, 0) + widths = peak_widths([1, 2, 3], [])[0] + assert_(isinstance(widths, np.ndarray)) + assert_equal(widths.size, 0) + out = peak_widths([], []) + for arr in out: + assert_(isinstance(arr, np.ndarray)) + assert_equal(arr.size, 0) + + @pytest.mark.filterwarnings("ignore:some peaks have a width of 0") + def test_basic(self): + """ + Test a simple use case with easy to verify results at different relative + heights. + """ + x = np.array([1, 0, 1, 2, 1, 0, -1]) + prominence = 2 + for rel_height, width_true, lip_true, rip_true in [ + (0., 0., 3., 3.), # raises warning + (0.25, 1., 2.5, 3.5), + (0.5, 2., 2., 4.), + (0.75, 3., 1.5, 4.5), + (1., 4., 1., 5.), + (2., 5., 1., 6.), + (3., 5., 1., 6.) + ]: + width_calc, height, lip_calc, rip_calc = peak_widths( + x, [3], rel_height) + assert_allclose(width_calc, width_true) + assert_allclose(height, 2 - rel_height * prominence) + assert_allclose(lip_calc, lip_true) + assert_allclose(rip_calc, rip_true) + + def test_non_contiguous(self): + """ + Test with non-C-contiguous input arrays. + """ + x = np.repeat([0, 100, 50], 4) + peaks = np.repeat([1], 3) + result = peak_widths(x[::4], peaks[::3]) + assert_equal(result, [0.75, 75, 0.75, 1.5]) + + def test_exceptions(self): + """ + Verify that argument validation works as intended. + """ + with raises(ValueError, match='1-D array'): + # x with dimension > 1 + peak_widths(np.zeros((3, 4)), np.ones(3)) + with raises(ValueError, match='1-D array'): + # x with dimension < 1 + peak_widths(3, [0]) + with raises(ValueError, match='1-D array'): + # peaks with dimension > 1 + peak_widths(np.arange(10), np.ones((3, 2), dtype=np.intp)) + with raises(ValueError, match='1-D array'): + # peaks with dimension < 1 + peak_widths(np.arange(10), 3) + with raises(ValueError, match='not a valid index'): + # peak pos exceeds x.size + peak_widths(np.arange(10), [8, 11]) + with raises(ValueError, match='not a valid index'): + # empty x with peaks supplied + peak_widths([], [1, 2]) + with raises(TypeError, match='cannot safely cast'): + # peak cannot be safely casted to intp + peak_widths(np.arange(10), [1.1, 2.3]) + with raises(ValueError, match='rel_height'): + # rel_height is < 0 + peak_widths([0, 1, 0, 1, 0], [1, 3], rel_height=-1) + with raises(TypeError, match='None'): + # prominence data contains None + peak_widths([1, 2, 1], [1], prominence_data=(None, None, None)) + + def test_warnings(self): + """ + Verify that appropriate warnings are raised. + """ + msg = "some peaks have a width of 0" + with warns(PeakPropertyWarning, match=msg): + # Case: rel_height is 0 + peak_widths([0, 1, 0], [1], rel_height=0) + with warns(PeakPropertyWarning, match=msg): + # Case: prominence is 0 and bases are identical + peak_widths( + [0, 1, 1, 1, 0], [2], + prominence_data=(np.array([0.], np.float64), + np.array([2], np.intp), + np.array([2], np.intp)) + ) + + def test_mismatching_prominence_data(self): + """Test with mismatching peak and / or prominence data.""" + x = [0, 1, 0] + peak = [1] + for i, (prominences, left_bases, right_bases) in enumerate([ + ((1.,), (-1,), (2,)), # left base not in x + ((1.,), (0,), (3,)), # right base not in x + ((1.,), (2,), (0,)), # swapped bases same as peak + ((1., 1.), (0, 0), (2, 2)), # array shapes don't match peaks + ((1., 1.), (0,), (2,)), # arrays with different shapes + ((1.,), (0, 0), (2,)), # arrays with different shapes + ((1.,), (0,), (2, 2)) # arrays with different shapes + ]): + # Make sure input is matches output of signal.peak_prominences + prominence_data = (np.array(prominences, dtype=np.float64), + np.array(left_bases, dtype=np.intp), + np.array(right_bases, dtype=np.intp)) + # Test for correct exception + if i < 3: + match = "prominence data is invalid for peak" + else: + match = "arrays in `prominence_data` must have the same shape" + with raises(ValueError, match=match): + peak_widths(x, peak, prominence_data=prominence_data) + + @pytest.mark.filterwarnings("ignore:some peaks have a width of 0") + def test_intersection_rules(self): + """Test if x == eval_height counts as an intersection.""" + # Flatt peak with two possible intersection points if evaluated at 1 + x = [0, 1, 2, 1, 3, 3, 3, 1, 2, 1, 0] + # relative height is 0 -> width is 0 as well, raises warning + assert_allclose(peak_widths(x, peaks=[5], rel_height=0), + [(0.,), (3.,), (5.,), (5.,)]) + # width_height == x counts as intersection -> nearest 1 is chosen + assert_allclose(peak_widths(x, peaks=[5], rel_height=2/3), + [(4.,), (1.,), (3.,), (7.,)]) + + +def test_unpack_condition_args(): + """ + Verify parsing of condition arguments for `scipy.signal.find_peaks` function. + """ + x = np.arange(10) + amin_true = x + amax_true = amin_true + 10 + peaks = amin_true[1::2] + + # Test unpacking with None or interval + assert_((None, None) == _unpack_condition_args((None, None), x, peaks)) + assert_((1, None) == _unpack_condition_args(1, x, peaks)) + assert_((1, None) == _unpack_condition_args((1, None), x, peaks)) + assert_((None, 2) == _unpack_condition_args((None, 2), x, peaks)) + assert_((3., 4.5) == _unpack_condition_args((3., 4.5), x, peaks)) + + # Test if borders are correctly reduced with `peaks` + amin_calc, amax_calc = _unpack_condition_args((amin_true, amax_true), x, peaks) + assert_equal(amin_calc, amin_true[peaks]) + assert_equal(amax_calc, amax_true[peaks]) + + # Test raises if array borders don't match x + with raises(ValueError, match="array size of lower"): + _unpack_condition_args(amin_true, np.arange(11), peaks) + with raises(ValueError, match="array size of upper"): + _unpack_condition_args((None, amin_true), np.arange(11), peaks) + + +class TestFindPeaks: + + # Keys of optionally returned properties + property_keys = {'peak_heights', 'left_thresholds', 'right_thresholds', + 'prominences', 'left_bases', 'right_bases', 'widths', + 'width_heights', 'left_ips', 'right_ips'} + + def test_constant(self): + """ + Test behavior for signal without local maxima. + """ + open_interval = (None, None) + peaks, props = find_peaks(np.ones(10), + height=open_interval, threshold=open_interval, + prominence=open_interval, width=open_interval) + assert_(peaks.size == 0) + for key in self.property_keys: + assert_(props[key].size == 0) + + def test_plateau_size(self): + """ + Test plateau size condition for peaks. + """ + # Prepare signal with peaks with peak_height == plateau_size + plateau_sizes = np.array([1, 2, 3, 4, 8, 20, 111]) + x = np.zeros(plateau_sizes.size * 2 + 1) + x[1::2] = plateau_sizes + repeats = np.ones(x.size, dtype=int) + repeats[1::2] = x[1::2] + x = np.repeat(x, repeats) + + # Test full output + peaks, props = find_peaks(x, plateau_size=(None, None)) + assert_equal(peaks, [1, 3, 7, 11, 18, 33, 100]) + assert_equal(props["plateau_sizes"], plateau_sizes) + assert_equal(props["left_edges"], peaks - (plateau_sizes - 1) // 2) + assert_equal(props["right_edges"], peaks + plateau_sizes // 2) + + # Test conditions + assert_equal(find_peaks(x, plateau_size=4)[0], [11, 18, 33, 100]) + assert_equal(find_peaks(x, plateau_size=(None, 3.5))[0], [1, 3, 7]) + assert_equal(find_peaks(x, plateau_size=(5, 50))[0], [18, 33]) + + def test_height_condition(self): + """ + Test height condition for peaks. + """ + x = (0., 1/3, 0., 2.5, 0, 4., 0) + peaks, props = find_peaks(x, height=(None, None)) + assert_equal(peaks, np.array([1, 3, 5])) + assert_equal(props['peak_heights'], np.array([1/3, 2.5, 4.])) + assert_equal(find_peaks(x, height=0.5)[0], np.array([3, 5])) + assert_equal(find_peaks(x, height=(None, 3))[0], np.array([1, 3])) + assert_equal(find_peaks(x, height=(2, 3))[0], np.array([3])) + + def test_threshold_condition(self): + """ + Test threshold condition for peaks. + """ + x = (0, 2, 1, 4, -1) + peaks, props = find_peaks(x, threshold=(None, None)) + assert_equal(peaks, np.array([1, 3])) + assert_equal(props['left_thresholds'], np.array([2, 3])) + assert_equal(props['right_thresholds'], np.array([1, 5])) + assert_equal(find_peaks(x, threshold=2)[0], np.array([3])) + assert_equal(find_peaks(x, threshold=3.5)[0], np.array([])) + assert_equal(find_peaks(x, threshold=(None, 5))[0], np.array([1, 3])) + assert_equal(find_peaks(x, threshold=(None, 4))[0], np.array([1])) + assert_equal(find_peaks(x, threshold=(2, 4))[0], np.array([])) + + def test_distance_condition(self): + """ + Test distance condition for peaks. + """ + # Peaks of different height with constant distance 3 + peaks_all = np.arange(1, 21, 3) + x = np.zeros(21) + x[peaks_all] += np.linspace(1, 2, peaks_all.size) + + # Test if peaks with "minimal" distance are still selected (distance = 3) + assert_equal(find_peaks(x, distance=3)[0], peaks_all) + + # Select every second peak (distance > 3) + peaks_subset = find_peaks(x, distance=3.0001)[0] + # Test if peaks_subset is subset of peaks_all + assert_( + np.setdiff1d(peaks_subset, peaks_all, assume_unique=True).size == 0 + ) + # Test if every second peak was removed + assert_equal(np.diff(peaks_subset), 6) + + # Test priority of peak removal + x = [-2, 1, -1, 0, -3] + peaks_subset = find_peaks(x, distance=10)[0] # use distance > x size + assert_(peaks_subset.size == 1 and peaks_subset[0] == 1) + + def test_prominence_condition(self): + """ + Test prominence condition for peaks. + """ + x = np.linspace(0, 10, 100) + peaks_true = np.arange(1, 99, 2) + offset = np.linspace(1, 10, peaks_true.size) + x[peaks_true] += offset + prominences = x[peaks_true] - x[peaks_true + 1] + interval = (3, 9) + keep = np.nonzero( + (interval[0] <= prominences) & (prominences <= interval[1])) + + peaks_calc, properties = find_peaks(x, prominence=interval) + assert_equal(peaks_calc, peaks_true[keep]) + assert_equal(properties['prominences'], prominences[keep]) + assert_equal(properties['left_bases'], 0) + assert_equal(properties['right_bases'], peaks_true[keep] + 1) + + def test_width_condition(self): + """ + Test width condition for peaks. + """ + x = np.array([1, 0, 1, 2, 1, 0, -1, 4, 0]) + peaks, props = find_peaks(x, width=(None, 2), rel_height=0.75) + assert_equal(peaks.size, 1) + assert_equal(peaks, 7) + assert_allclose(props['widths'], 1.35) + assert_allclose(props['width_heights'], 1.) + assert_allclose(props['left_ips'], 6.4) + assert_allclose(props['right_ips'], 7.75) + + def test_properties(self): + """ + Test returned properties. + """ + open_interval = (None, None) + x = [0, 1, 0, 2, 1.5, 0, 3, 0, 5, 9] + peaks, props = find_peaks(x, + height=open_interval, threshold=open_interval, + prominence=open_interval, width=open_interval) + assert_(len(props) == len(self.property_keys)) + for key in self.property_keys: + assert_(peaks.size == props[key].size) + + def test_raises(self): + """ + Test exceptions raised by function. + """ + with raises(ValueError, match="1-D array"): + find_peaks(np.array(1)) + with raises(ValueError, match="1-D array"): + find_peaks(np.ones((2, 2))) + with raises(ValueError, match="distance"): + find_peaks(np.arange(10), distance=-1) + + @pytest.mark.filterwarnings("ignore:some peaks have a prominence of 0", + "ignore:some peaks have a width of 0") + def test_wlen_smaller_plateau(self): + """ + Test behavior of prominence and width calculation if the given window + length is smaller than a peak's plateau size. + + Regression test for gh-9110. + """ + peaks, props = find_peaks([0, 1, 1, 1, 0], prominence=(None, None), + width=(None, None), wlen=2) + assert_equal(peaks, 2) + assert_equal(props["prominences"], 0) + assert_equal(props["widths"], 0) + assert_equal(props["width_heights"], 1) + for key in ("left_bases", "right_bases", "left_ips", "right_ips"): + assert_equal(props[key], peaks) + + @pytest.mark.parametrize("kwargs", [ + {}, + {"distance": 3.0}, + {"prominence": (None, None)}, + {"width": (None, 2)}, + + ]) + def test_readonly_array(self, kwargs): + """ + Test readonly arrays are accepted. + """ + x = np.linspace(0, 10, 15) + x_readonly = x.copy() + x_readonly.flags.writeable = False + + peaks, _ = find_peaks(x) + peaks_readonly, _ = find_peaks(x_readonly, **kwargs) + + assert_allclose(peaks, peaks_readonly) + + +class TestFindPeaksCwt: + + def test_find_peaks_exact(self): + """ + Generate a series of gaussians and attempt to find the peak locations. + """ + sigmas = [5.0, 3.0, 10.0, 20.0, 10.0, 50.0] + num_points = 500 + test_data, act_locs = _gen_gaussians_even(sigmas, num_points) + widths = np.arange(0.1, max(sigmas)) + found_locs = find_peaks_cwt(test_data, widths, gap_thresh=2, min_snr=0, + min_length=None) + np.testing.assert_array_equal(found_locs, act_locs, + "Found maximum locations did not equal those expected") + + def test_find_peaks_withnoise(self): + """ + Verify that peak locations are (approximately) found + for a series of gaussians with added noise. + """ + sigmas = [5.0, 3.0, 10.0, 20.0, 10.0, 50.0] + num_points = 500 + test_data, act_locs = _gen_gaussians_even(sigmas, num_points) + widths = np.arange(0.1, max(sigmas)) + noise_amp = 0.07 + np.random.seed(18181911) + test_data += (np.random.rand(num_points) - 0.5)*(2*noise_amp) + found_locs = find_peaks_cwt(test_data, widths, min_length=15, + gap_thresh=1, min_snr=noise_amp / 5) + + np.testing.assert_equal(len(found_locs), len(act_locs), 'Different number' + + 'of peaks found than expected') + diffs = np.abs(found_locs - act_locs) + max_diffs = np.array(sigmas) / 5 + np.testing.assert_array_less(diffs, max_diffs, 'Maximum location differed' + + 'by more than %s' % (max_diffs)) + + def test_find_peaks_nopeak(self): + """ + Verify that no peak is found in + data that's just noise. + """ + noise_amp = 1.0 + num_points = 100 + np.random.seed(181819141) + test_data = (np.random.rand(num_points) - 0.5)*(2*noise_amp) + widths = np.arange(10, 50) + found_locs = find_peaks_cwt(test_data, widths, min_snr=5, noise_perc=30) + np.testing.assert_equal(len(found_locs), 0) + + def test_find_peaks_with_non_default_wavelets(self): + x = gaussian(200, 2) + widths = np.array([1, 2, 3, 4]) + a = find_peaks_cwt(x, widths, wavelet=gaussian) + + np.testing.assert_equal(np.array([100]), a) + + def test_find_peaks_window_size(self): + """ + Verify that window_size is passed correctly to private function and + affects the result. + """ + sigmas = [2.0, 2.0] + num_points = 1000 + test_data, act_locs = _gen_gaussians_even(sigmas, num_points) + widths = np.arange(0.1, max(sigmas), 0.2) + noise_amp = 0.05 + np.random.seed(18181911) + test_data += (np.random.rand(num_points) - 0.5)*(2*noise_amp) + + # Possibly contrived negative region to throw off peak finding + # when window_size is too large + test_data[250:320] -= 1 + + found_locs = find_peaks_cwt(test_data, widths, gap_thresh=2, min_snr=3, + min_length=None, window_size=None) + with pytest.raises(AssertionError): + assert found_locs.size == act_locs.size + + found_locs = find_peaks_cwt(test_data, widths, gap_thresh=2, min_snr=3, + min_length=None, window_size=20) + assert found_locs.size == act_locs.size + + def test_find_peaks_with_one_width(self): + """ + Verify that the `width` argument + in `find_peaks_cwt` can be a float + """ + xs = np.arange(0, np.pi, 0.05) + test_data = np.sin(xs) + widths = 1 + found_locs = find_peaks_cwt(test_data, widths) + + np.testing.assert_equal(found_locs, 32) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_result_type.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_result_type.py new file mode 100644 index 0000000000000000000000000000000000000000..58fdd458ef665051805c21ebce5340949fdca428 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_result_type.py @@ -0,0 +1,52 @@ +# Regressions tests on result types of some signal functions + +import numpy as np +from numpy.testing import assert_ + +from scipy.signal import (decimate, + lfilter_zi, + lfiltic, + sos2tf, + sosfilt_zi) + + +def test_decimate(): + ones_f32 = np.ones(32, dtype=np.float32) + assert_(decimate(ones_f32, 2).dtype == np.float32) + + ones_i64 = np.ones(32, dtype=np.int64) + assert_(decimate(ones_i64, 2).dtype == np.float64) + + +def test_lfilter_zi(): + b_f32 = np.array([1, 2, 3], dtype=np.float32) + a_f32 = np.array([4, 5, 6], dtype=np.float32) + assert_(lfilter_zi(b_f32, a_f32).dtype == np.float32) + + +def test_lfiltic(): + # this would return f32 when given a mix of f32 / f64 args + b_f32 = np.array([1, 2, 3], dtype=np.float32) + a_f32 = np.array([4, 5, 6], dtype=np.float32) + x_f32 = np.ones(32, dtype=np.float32) + + b_f64 = b_f32.astype(np.float64) + a_f64 = a_f32.astype(np.float64) + x_f64 = x_f32.astype(np.float64) + + assert_(lfiltic(b_f64, a_f32, x_f32).dtype == np.float64) + assert_(lfiltic(b_f32, a_f64, x_f32).dtype == np.float64) + assert_(lfiltic(b_f32, a_f32, x_f64).dtype == np.float64) + assert_(lfiltic(b_f32, a_f32, x_f32, x_f64).dtype == np.float64) + + +def test_sos2tf(): + sos_f32 = np.array([[4, 5, 6, 1, 2, 3]], dtype=np.float32) + b, a = sos2tf(sos_f32) + assert_(b.dtype == np.float32) + assert_(a.dtype == np.float32) + + +def test_sosfilt_zi(): + sos_f32 = np.array([[4, 5, 6, 1, 2, 3]], dtype=np.float32) + assert_(sosfilt_zi(sos_f32).dtype == np.float32) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_savitzky_golay.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_savitzky_golay.py new file mode 100644 index 0000000000000000000000000000000000000000..fbbf370bf558612b7f866b252303b2cfdcb58b06 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_savitzky_golay.py @@ -0,0 +1,358 @@ +import pytest +import numpy as np +from numpy.testing import (assert_allclose, assert_equal, + assert_almost_equal, assert_array_equal, + assert_array_almost_equal) + +from scipy.ndimage import convolve1d + +from scipy.signal import savgol_coeffs, savgol_filter +from scipy.signal._savitzky_golay import _polyder + + +def check_polyder(p, m, expected): + dp = _polyder(p, m) + assert_array_equal(dp, expected) + + +def test_polyder(): + cases = [ + ([5], 0, [5]), + ([5], 1, [0]), + ([3, 2, 1], 0, [3, 2, 1]), + ([3, 2, 1], 1, [6, 2]), + ([3, 2, 1], 2, [6]), + ([3, 2, 1], 3, [0]), + ([[3, 2, 1], [5, 6, 7]], 0, [[3, 2, 1], [5, 6, 7]]), + ([[3, 2, 1], [5, 6, 7]], 1, [[6, 2], [10, 6]]), + ([[3, 2, 1], [5, 6, 7]], 2, [[6], [10]]), + ([[3, 2, 1], [5, 6, 7]], 3, [[0], [0]]), + ] + for p, m, expected in cases: + check_polyder(np.array(p).T, m, np.array(expected).T) + + +#-------------------------------------------------------------------- +# savgol_coeffs tests +#-------------------------------------------------------------------- + +def alt_sg_coeffs(window_length, polyorder, pos): + """This is an alternative implementation of the SG coefficients. + + It uses numpy.polyfit and numpy.polyval. The results should be + equivalent to those of savgol_coeffs(), but this implementation + is slower. + + window_length should be odd. + + """ + if pos is None: + pos = window_length // 2 + t = np.arange(window_length) + unit = (t == pos).astype(int) + h = np.polyval(np.polyfit(t, unit, polyorder), t) + return h + + +def test_sg_coeffs_trivial(): + # Test a trivial case of savgol_coeffs: polyorder = window_length - 1 + h = savgol_coeffs(1, 0) + assert_allclose(h, [1]) + + h = savgol_coeffs(3, 2) + assert_allclose(h, [0, 1, 0], atol=1e-10) + + h = savgol_coeffs(5, 4) + assert_allclose(h, [0, 0, 1, 0, 0], atol=1e-10) + + h = savgol_coeffs(5, 4, pos=1) + assert_allclose(h, [0, 0, 0, 1, 0], atol=1e-10) + + h = savgol_coeffs(5, 4, pos=1, use='dot') + assert_allclose(h, [0, 1, 0, 0, 0], atol=1e-10) + + +def compare_coeffs_to_alt(window_length, order): + # For the given window_length and order, compare the results + # of savgol_coeffs and alt_sg_coeffs for pos from 0 to window_length - 1. + # Also include pos=None. + for pos in [None] + list(range(window_length)): + h1 = savgol_coeffs(window_length, order, pos=pos, use='dot') + h2 = alt_sg_coeffs(window_length, order, pos=pos) + assert_allclose(h1, h2, atol=1e-10, + err_msg=("window_length = %d, order = %d, pos = %s" % + (window_length, order, pos))) + + +def test_sg_coeffs_compare(): + # Compare savgol_coeffs() to alt_sg_coeffs(). + for window_length in range(1, 8, 2): + for order in range(window_length): + compare_coeffs_to_alt(window_length, order) + + +def test_sg_coeffs_exact(): + polyorder = 4 + window_length = 9 + halflen = window_length // 2 + + x = np.linspace(0, 21, 43) + delta = x[1] - x[0] + + # The data is a cubic polynomial. We'll use an order 4 + # SG filter, so the filtered values should equal the input data + # (except within half window_length of the edges). + y = 0.5 * x ** 3 - x + h = savgol_coeffs(window_length, polyorder) + y0 = convolve1d(y, h) + assert_allclose(y0[halflen:-halflen], y[halflen:-halflen]) + + # Check the same input, but use deriv=1. dy is the exact result. + dy = 1.5 * x ** 2 - 1 + h = savgol_coeffs(window_length, polyorder, deriv=1, delta=delta) + y1 = convolve1d(y, h) + assert_allclose(y1[halflen:-halflen], dy[halflen:-halflen]) + + # Check the same input, but use deriv=2. d2y is the exact result. + d2y = 3.0 * x + h = savgol_coeffs(window_length, polyorder, deriv=2, delta=delta) + y2 = convolve1d(y, h) + assert_allclose(y2[halflen:-halflen], d2y[halflen:-halflen]) + + +def test_sg_coeffs_deriv(): + # The data in `x` is a sampled parabola, so using savgol_coeffs with an + # order 2 or higher polynomial should give exact results. + i = np.array([-2.0, 0.0, 2.0, 4.0, 6.0]) + x = i ** 2 / 4 + dx = i / 2 + d2x = np.full_like(i, 0.5) + for pos in range(x.size): + coeffs0 = savgol_coeffs(5, 3, pos=pos, delta=2.0, use='dot') + assert_allclose(coeffs0.dot(x), x[pos], atol=1e-10) + coeffs1 = savgol_coeffs(5, 3, pos=pos, delta=2.0, use='dot', deriv=1) + assert_allclose(coeffs1.dot(x), dx[pos], atol=1e-10) + coeffs2 = savgol_coeffs(5, 3, pos=pos, delta=2.0, use='dot', deriv=2) + assert_allclose(coeffs2.dot(x), d2x[pos], atol=1e-10) + + +def test_sg_coeffs_deriv_gt_polyorder(): + """ + If deriv > polyorder, the coefficients should be all 0. + This is a regression test for a bug where, e.g., + savgol_coeffs(5, polyorder=1, deriv=2) + raised an error. + """ + coeffs = savgol_coeffs(5, polyorder=1, deriv=2) + assert_array_equal(coeffs, np.zeros(5)) + coeffs = savgol_coeffs(7, polyorder=4, deriv=6) + assert_array_equal(coeffs, np.zeros(7)) + + +def test_sg_coeffs_large(): + # Test that for large values of window_length and polyorder the array of + # coefficients returned is symmetric. The aim is to ensure that + # no potential numeric overflow occurs. + coeffs0 = savgol_coeffs(31, 9) + assert_array_almost_equal(coeffs0, coeffs0[::-1]) + coeffs1 = savgol_coeffs(31, 9, deriv=1) + assert_array_almost_equal(coeffs1, -coeffs1[::-1]) + +# -------------------------------------------------------------------- +# savgol_coeffs tests for even window length +# -------------------------------------------------------------------- + + +def test_sg_coeffs_even_window_length(): + # Simple case - deriv=0, polyorder=0, 1 + window_lengths = [4, 6, 8, 10, 12, 14, 16] + for length in window_lengths: + h_p_d = savgol_coeffs(length, 0, 0) + assert_allclose(h_p_d, 1/length) + + # Verify with closed forms + # deriv=1, polyorder=1, 2 + def h_p_d_closed_form_1(k, m): + return 6*(k - 0.5)/((2*m + 1)*m*(2*m - 1)) + + # deriv=2, polyorder=2 + def h_p_d_closed_form_2(k, m): + numer = 15*(-4*m**2 + 1 + 12*(k - 0.5)**2) + denom = 4*(2*m + 1)*(m + 1)*m*(m - 1)*(2*m - 1) + return numer/denom + + for length in window_lengths: + m = length//2 + expected_output = [h_p_d_closed_form_1(k, m) + for k in range(-m + 1, m + 1)][::-1] + actual_output = savgol_coeffs(length, 1, 1) + assert_allclose(expected_output, actual_output) + actual_output = savgol_coeffs(length, 2, 1) + assert_allclose(expected_output, actual_output) + + expected_output = [h_p_d_closed_form_2(k, m) + for k in range(-m + 1, m + 1)][::-1] + actual_output = savgol_coeffs(length, 2, 2) + assert_allclose(expected_output, actual_output) + actual_output = savgol_coeffs(length, 3, 2) + assert_allclose(expected_output, actual_output) + +#-------------------------------------------------------------------- +# savgol_filter tests +#-------------------------------------------------------------------- + + +def test_sg_filter_trivial(): + """ Test some trivial edge cases for savgol_filter().""" + x = np.array([1.0]) + y = savgol_filter(x, 1, 0) + assert_equal(y, [1.0]) + + # Input is a single value. With a window length of 3 and polyorder 1, + # the value in y is from the straight-line fit of (-1,0), (0,3) and + # (1, 0) at 0. This is just the average of the three values, hence 1.0. + x = np.array([3.0]) + y = savgol_filter(x, 3, 1, mode='constant') + assert_almost_equal(y, [1.0], decimal=15) + + x = np.array([3.0]) + y = savgol_filter(x, 3, 1, mode='nearest') + assert_almost_equal(y, [3.0], decimal=15) + + x = np.array([1.0] * 3) + y = savgol_filter(x, 3, 1, mode='wrap') + assert_almost_equal(y, [1.0, 1.0, 1.0], decimal=15) + + +def test_sg_filter_basic(): + # Some basic test cases for savgol_filter(). + x = np.array([1.0, 2.0, 1.0]) + y = savgol_filter(x, 3, 1, mode='constant') + assert_allclose(y, [1.0, 4.0 / 3, 1.0]) + + y = savgol_filter(x, 3, 1, mode='mirror') + assert_allclose(y, [5.0 / 3, 4.0 / 3, 5.0 / 3]) + + y = savgol_filter(x, 3, 1, mode='wrap') + assert_allclose(y, [4.0 / 3, 4.0 / 3, 4.0 / 3]) + + +def test_sg_filter_2d(): + x = np.array([[1.0, 2.0, 1.0], + [2.0, 4.0, 2.0]]) + expected = np.array([[1.0, 4.0 / 3, 1.0], + [2.0, 8.0 / 3, 2.0]]) + y = savgol_filter(x, 3, 1, mode='constant') + assert_allclose(y, expected) + + y = savgol_filter(x.T, 3, 1, mode='constant', axis=0) + assert_allclose(y, expected.T) + + +def test_sg_filter_interp_edges(): + # Another test with low degree polynomial data, for which we can easily + # give the exact results. In this test, we use mode='interp', so + # savgol_filter should match the exact solution for the entire data set, + # including the edges. + t = np.linspace(-5, 5, 21) + delta = t[1] - t[0] + # Polynomial test data. + x = np.array([t, + 3 * t ** 2, + t ** 3 - t]) + dx = np.array([np.ones_like(t), + 6 * t, + 3 * t ** 2 - 1.0]) + d2x = np.array([np.zeros_like(t), + np.full_like(t, 6), + 6 * t]) + + window_length = 7 + + y = savgol_filter(x, window_length, 3, axis=-1, mode='interp') + assert_allclose(y, x, atol=1e-12) + + y1 = savgol_filter(x, window_length, 3, axis=-1, mode='interp', + deriv=1, delta=delta) + assert_allclose(y1, dx, atol=1e-12) + + y2 = savgol_filter(x, window_length, 3, axis=-1, mode='interp', + deriv=2, delta=delta) + assert_allclose(y2, d2x, atol=1e-12) + + # Transpose everything, and test again with axis=0. + + x = x.T + dx = dx.T + d2x = d2x.T + + y = savgol_filter(x, window_length, 3, axis=0, mode='interp') + assert_allclose(y, x, atol=1e-12) + + y1 = savgol_filter(x, window_length, 3, axis=0, mode='interp', + deriv=1, delta=delta) + assert_allclose(y1, dx, atol=1e-12) + + y2 = savgol_filter(x, window_length, 3, axis=0, mode='interp', + deriv=2, delta=delta) + assert_allclose(y2, d2x, atol=1e-12) + + +def test_sg_filter_interp_edges_3d(): + # Test mode='interp' with a 3-D array. + t = np.linspace(-5, 5, 21) + delta = t[1] - t[0] + x1 = np.array([t, -t]) + x2 = np.array([t ** 2, 3 * t ** 2 + 5]) + x3 = np.array([t ** 3, 2 * t ** 3 + t ** 2 - 0.5 * t]) + dx1 = np.array([np.ones_like(t), -np.ones_like(t)]) + dx2 = np.array([2 * t, 6 * t]) + dx3 = np.array([3 * t ** 2, 6 * t ** 2 + 2 * t - 0.5]) + + # z has shape (3, 2, 21) + z = np.array([x1, x2, x3]) + dz = np.array([dx1, dx2, dx3]) + + y = savgol_filter(z, 7, 3, axis=-1, mode='interp', delta=delta) + assert_allclose(y, z, atol=1e-10) + + dy = savgol_filter(z, 7, 3, axis=-1, mode='interp', deriv=1, delta=delta) + assert_allclose(dy, dz, atol=1e-10) + + # z has shape (3, 21, 2) + z = np.array([x1.T, x2.T, x3.T]) + dz = np.array([dx1.T, dx2.T, dx3.T]) + + y = savgol_filter(z, 7, 3, axis=1, mode='interp', delta=delta) + assert_allclose(y, z, atol=1e-10) + + dy = savgol_filter(z, 7, 3, axis=1, mode='interp', deriv=1, delta=delta) + assert_allclose(dy, dz, atol=1e-10) + + # z has shape (21, 3, 2) + z = z.swapaxes(0, 1).copy() + dz = dz.swapaxes(0, 1).copy() + + y = savgol_filter(z, 7, 3, axis=0, mode='interp', delta=delta) + assert_allclose(y, z, atol=1e-10) + + dy = savgol_filter(z, 7, 3, axis=0, mode='interp', deriv=1, delta=delta) + assert_allclose(dy, dz, atol=1e-10) + + +def test_sg_filter_valid_window_length_3d(): + """Tests that the window_length check is using the correct axis.""" + + x = np.ones((10, 20, 30)) + + savgol_filter(x, window_length=29, polyorder=3, mode='interp') + + with pytest.raises(ValueError, match='window_length must be less than'): + # window_length is more than x.shape[-1]. + savgol_filter(x, window_length=31, polyorder=3, mode='interp') + + savgol_filter(x, window_length=9, polyorder=3, axis=0, mode='interp') + + with pytest.raises(ValueError, match='window_length must be less than'): + # window_length is more than x.shape[0]. + savgol_filter(x, window_length=11, polyorder=3, axis=0, mode='interp') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_short_time_fft.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_short_time_fft.py new file mode 100644 index 0000000000000000000000000000000000000000..2307f185698c8a8f60e6b46d423999b4fae8cbd3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_short_time_fft.py @@ -0,0 +1,840 @@ +"""Unit tests for module `_short_time_fft`. + +This file's structure loosely groups the tests into the following sequential +categories: + +1. Test function `_calc_dual_canonical_window`. +2. Test for invalid parameters and exceptions in `ShortTimeFFT` (until the + `test_from_window` function). +3. Test algorithmic properties of STFT/ISTFT. Some tests were ported from + ``test_spectral.py``. + +Notes +----- +* Mypy 0.990 does interpret the line:: + + from scipy.stats import norm as normal_distribution + + incorrectly (but the code works), hence a ``type: ignore`` was appended. +""" +import math +from itertools import product +from typing import cast, get_args, Literal + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_equal +from scipy.fft import fftshift +from scipy.stats import norm as normal_distribution # type: ignore +from scipy.signal import get_window, welch, stft, istft, spectrogram + +from scipy.signal._short_time_fft import FFT_MODE_TYPE, \ + _calc_dual_canonical_window, ShortTimeFFT, PAD_TYPE +from scipy.signal.windows import gaussian + + +def test__calc_dual_canonical_window_roundtrip(): + """Test dual window calculation with a round trip to verify duality. + + Note that this works only for canonical window pairs (having minimal + energy) like a Gaussian. + + The window is the same as in the example of `from ShortTimeFFT.from_dual`. + """ + win = gaussian(51, std=10, sym=True) + d_win = _calc_dual_canonical_window(win, 10) + win2 = _calc_dual_canonical_window(d_win, 10) + assert_allclose(win2, win) + + +def test__calc_dual_canonical_window_exceptions(): + """Raise all exceptions in `_calc_dual_canonical_window`.""" + # Verify that calculation can fail: + with pytest.raises(ValueError, match="hop=5 is larger than window len.*"): + _calc_dual_canonical_window(np.ones(4), 5) + with pytest.raises(ValueError, match=".* Transform not invertible!"): + _calc_dual_canonical_window(np.array([.1, .2, .3, 0]), 4) + + # Verify that parameter `win` may not be integers: + with pytest.raises(ValueError, match="Parameter 'win' cannot be of int.*"): + _calc_dual_canonical_window(np.ones(4, dtype=int), 1) + + +def test_invalid_initializer_parameters(): + """Verify that exceptions get raised on invalid parameters when + instantiating ShortTimeFFT. """ + with pytest.raises(ValueError, match=r"Parameter win must be 1d, " + + r"but win.shape=\(2, 2\)!"): + ShortTimeFFT(np.ones((2, 2)), hop=4, fs=1) + with pytest.raises(ValueError, match="Parameter win must have " + + "finite entries"): + ShortTimeFFT(np.array([1, np.inf, 2, 3]), hop=4, fs=1) + with pytest.raises(ValueError, match="Parameter hop=0 is not " + + "an integer >= 1!"): + ShortTimeFFT(np.ones(4), hop=0, fs=1) + with pytest.raises(ValueError, match="Parameter hop=2.0 is not " + + "an integer >= 1!"): + # noinspection PyTypeChecker + ShortTimeFFT(np.ones(4), hop=2.0, fs=1) + with pytest.raises(ValueError, match=r"dual_win.shape=\(5,\) must equal " + + r"win.shape=\(4,\)!"): + ShortTimeFFT(np.ones(4), hop=2, fs=1, dual_win=np.ones(5)) + with pytest.raises(ValueError, match="Parameter dual_win must be " + + "a finite array!"): + ShortTimeFFT(np.ones(3), hop=2, fs=1, + dual_win=np.array([np.nan, 2, 3])) + + +def test_exceptions_properties_methods(): + """Verify that exceptions get raised when setting properties or calling + method of ShortTimeFFT to/with invalid values.""" + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1) + with pytest.raises(ValueError, match="Sampling interval T=-1 must be " + + "positive!"): + SFT.T = -1 + with pytest.raises(ValueError, match="Sampling frequency fs=-1 must be " + + "positive!"): + SFT.fs = -1 + with pytest.raises(ValueError, match="fft_mode='invalid_typ' not in " + + r"\('twosided', 'centered', " + + r"'onesided', 'onesided2X'\)!"): + SFT.fft_mode = 'invalid_typ' + with pytest.raises(ValueError, match="For scaling is None, " + + "fft_mode='onesided2X' is invalid.*"): + SFT.fft_mode = 'onesided2X' + with pytest.raises(ValueError, match="Attribute mfft=7 needs to be " + + "at least the window length.*"): + SFT.mfft = 7 + with pytest.raises(ValueError, match="scaling='invalid' not in.*"): + # noinspection PyTypeChecker + SFT.scale_to('invalid') + with pytest.raises(ValueError, match="phase_shift=3.0 has the unit .*"): + SFT.phase_shift = 3.0 + with pytest.raises(ValueError, match="-mfft < phase_shift < mfft " + + "does not hold.*"): + SFT.phase_shift = 2*SFT.mfft + with pytest.raises(ValueError, match="Parameter padding='invalid' not.*"): + # noinspection PyTypeChecker + g = SFT._x_slices(np.zeros(16), k_off=0, p0=0, p1=1, padding='invalid') + next(g) # execute generator + with pytest.raises(ValueError, match="Trend type must be 'linear' " + + "or 'constant'"): + # noinspection PyTypeChecker + SFT.stft_detrend(np.zeros(16), detr='invalid') + with pytest.raises(ValueError, match="Parameter detr=nan is not a str, " + + "function or None!"): + # noinspection PyTypeChecker + SFT.stft_detrend(np.zeros(16), detr=np.nan) + with pytest.raises(ValueError, match="Invalid Parameter p0=0, p1=200.*"): + SFT.p_range(100, 0, 200) + + with pytest.raises(ValueError, match="f_axis=0 may not be equal to " + + "t_axis=0!"): + SFT.istft(np.zeros((SFT.f_pts, 2)), t_axis=0, f_axis=0) + with pytest.raises(ValueError, match=r"S.shape\[f_axis\]=2 must be equal" + + " to self.f_pts=5.*"): + SFT.istft(np.zeros((2, 2))) + with pytest.raises(ValueError, match=r"S.shape\[t_axis\]=1 needs to have" + + " at least 2 slices.*"): + SFT.istft(np.zeros((SFT.f_pts, 1))) + with pytest.raises(ValueError, match=r".*\(k1=100\) <= \(k_max=12\) " + + "is false!$"): + SFT.istft(np.zeros((SFT.f_pts, 3)), k1=100) + with pytest.raises(ValueError, match=r"\(k1=1\) - \(k0=0\) = 1 has to " + + "be at least.* length 4!"): + SFT.istft(np.zeros((SFT.f_pts, 3)), k0=0, k1=1) + + with pytest.raises(ValueError, match=r"Parameter axes_seq='invalid' " + + r"not in \['tf', 'ft'\]!"): + # noinspection PyTypeChecker + SFT.extent(n=100, axes_seq='invalid') + with pytest.raises(ValueError, match="Attribute fft_mode=twosided must.*"): + SFT.fft_mode = 'twosided' + SFT.extent(n=100) + + +@pytest.mark.parametrize('m', ('onesided', 'onesided2X')) +def test_exceptions_fft_mode_complex_win(m: FFT_MODE_TYPE): + """Verify that one-sided spectra are not allowed with complex-valued + windows or with complex-valued signals. + + The reason being, the `rfft` function only accepts real-valued input. + """ + with pytest.raises(ValueError, + match=f"One-sided spectra, i.e., fft_mode='{m}'.*"): + ShortTimeFFT(np.ones(8)*1j, hop=4, fs=1, fft_mode=m) + + SFT = ShortTimeFFT(np.ones(8)*1j, hop=4, fs=1, fft_mode='twosided') + with pytest.raises(ValueError, + match=f"One-sided spectra, i.e., fft_mode='{m}'.*"): + SFT.fft_mode = m + + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1, scale_to='psd', fft_mode='onesided') + with pytest.raises(ValueError, match="Complex-valued `x` not allowed for self.*"): + SFT.stft(np.ones(8)*1j) + SFT.fft_mode = 'onesided2X' + with pytest.raises(ValueError, match="Complex-valued `x` not allowed for self.*"): + SFT.stft(np.ones(8)*1j) + + +def test_invalid_fft_mode_RuntimeError(): + """Ensure exception gets raised when property `fft_mode` is invalid. """ + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1) + SFT._fft_mode = 'invalid_typ' + + with pytest.raises(RuntimeError): + _ = SFT.f + with pytest.raises(RuntimeError): + SFT._fft_func(np.ones(8)) + with pytest.raises(RuntimeError): + SFT._ifft_func(np.ones(8)) + + +@pytest.mark.parametrize('win_params, Nx', [(('gaussian', 2.), 9), # in docstr + ('triang', 7), + (('kaiser', 4.0), 9), + (('exponential', None, 1.), 9), + (4.0, 9)]) +def test_from_window(win_params, Nx: int): + """Verify that `from_window()` handles parameters correctly. + + The window parameterizations are documented in the `get_window` docstring. + """ + w_sym, fs = get_window(win_params, Nx, fftbins=False), 16. + w_per = get_window(win_params, Nx, fftbins=True) + SFT0 = ShortTimeFFT(w_sym, hop=3, fs=fs, fft_mode='twosided', + scale_to='psd', phase_shift=1) + nperseg = len(w_sym) + noverlap = nperseg - SFT0.hop + SFT1 = ShortTimeFFT.from_window(win_params, fs, nperseg, noverlap, + symmetric_win=True, fft_mode='twosided', + scale_to='psd', phase_shift=1) + # periodic window: + SFT2 = ShortTimeFFT.from_window(win_params, fs, nperseg, noverlap, + symmetric_win=False, fft_mode='twosided', + scale_to='psd', phase_shift=1) + # Be informative when comparing instances: + assert_equal(SFT1.win, SFT0.win) + assert_allclose(SFT2.win, w_per / np.sqrt(sum(w_per**2) * fs)) + for n_ in ('hop', 'T', 'fft_mode', 'mfft', 'scaling', 'phase_shift'): + v0, v1, v2 = (getattr(SFT_, n_) for SFT_ in (SFT0, SFT1, SFT2)) + assert v1 == v0, f"SFT1.{n_}={v1} does not equal SFT0.{n_}={v0}" + assert v2 == v0, f"SFT2.{n_}={v2} does not equal SFT0.{n_}={v0}" + + +def test_dual_win_roundtrip(): + """Verify the duality of `win` and `dual_win`. + + Note that this test does not work for arbitrary windows, since dual windows + are not unique. It always works for invertible STFTs if the windows do not + overlap. + """ + # Non-standard values for keyword arguments (except for `scale_to`): + kw = dict(hop=4, fs=1, fft_mode='twosided', mfft=8, scale_to=None, + phase_shift=2) + SFT0 = ShortTimeFFT(np.ones(4), **kw) + SFT1 = ShortTimeFFT.from_dual(SFT0.dual_win, **kw) + assert_allclose(SFT1.dual_win, SFT0.win) + + +@pytest.mark.parametrize('scale_to, fac_psd, fac_mag', + [(None, 0.25, 0.125), + ('magnitude', 2.0, 1), + ('psd', 1, 0.5)]) +def test_scaling(scale_to: Literal['magnitude', 'psd'], fac_psd, fac_mag): + """Verify scaling calculations. + + * Verify passing `scale_to`parameter to ``__init__(). + * Roundtrip while changing scaling factor. + """ + SFT = ShortTimeFFT(np.ones(4) * 2, hop=4, fs=1, scale_to=scale_to) + assert SFT.fac_psd == fac_psd + assert SFT.fac_magnitude == fac_mag + # increase coverage by accessing properties twice: + assert SFT.fac_psd == fac_psd + assert SFT.fac_magnitude == fac_mag + + x = np.fft.irfft([0, 0, 7, 0, 0, 0, 0]) # periodic signal + Sx = SFT.stft(x) + Sx_mag, Sx_psd = Sx * SFT.fac_magnitude, Sx * SFT.fac_psd + + SFT.scale_to('magnitude') + x_mag = SFT.istft(Sx_mag, k1=len(x)) + assert_allclose(x_mag, x) + + SFT.scale_to('psd') + x_psd = SFT.istft(Sx_psd, k1=len(x)) + assert_allclose(x_psd, x) + + +def test_scale_to(): + """Verify `scale_to()` method.""" + SFT = ShortTimeFFT(np.ones(4) * 2, hop=4, fs=1, scale_to=None) + + SFT.scale_to('magnitude') + assert SFT.scaling == 'magnitude' + assert SFT.fac_psd == 2.0 + assert SFT.fac_magnitude == 1 + + SFT.scale_to('psd') + assert SFT.scaling == 'psd' + assert SFT.fac_psd == 1 + assert SFT.fac_magnitude == 0.5 + + SFT.scale_to('psd') # needed for coverage + + for scale, s_fac in zip(('magnitude', 'psd'), (8, 4)): + SFT = ShortTimeFFT(np.ones(4) * 2, hop=4, fs=1, scale_to=None) + dual_win = SFT.dual_win.copy() + + SFT.scale_to(cast(Literal['magnitude', 'psd'], scale)) + assert_allclose(SFT.dual_win, dual_win * s_fac) + + +def test_x_slices_padding(): + """Verify padding. + + The reference arrays were taken from the docstrings of `zero_ext`, + `const_ext`, `odd_ext()`, and `even_ext()` from the _array_tools module. + """ + SFT = ShortTimeFFT(np.ones(5), hop=4, fs=1) + x = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]], dtype=float) + d = {'zeros': [[[0, 0, 1, 2, 3], [0, 0, 0, 1, 4]], + [[3, 4, 5, 0, 0], [4, 9, 16, 0, 0]]], + 'edge': [[[1, 1, 1, 2, 3], [0, 0, 0, 1, 4]], + [[3, 4, 5, 5, 5], [4, 9, 16, 16, 16]]], + 'even': [[[3, 2, 1, 2, 3], [4, 1, 0, 1, 4]], + [[3, 4, 5, 4, 3], [4, 9, 16, 9, 4]]], + 'odd': [[[-1, 0, 1, 2, 3], [-4, -1, 0, 1, 4]], + [[3, 4, 5, 6, 7], [4, 9, 16, 23, 28]]]} + for p_, xx in d.items(): + gen = SFT._x_slices(np.array(x), 0, 0, 2, padding=cast(PAD_TYPE, p_)) + yy = np.array([y_.copy() for y_ in gen]) # due to inplace copying + assert_equal(yy, xx, err_msg=f"Failed '{p_}' padding.") + + +def test_invertible(): + """Verify `invertible` property. """ + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1) + assert SFT.invertible + SFT = ShortTimeFFT(np.ones(8), hop=9, fs=1) + assert not SFT.invertible + + +def test_border_values(): + """Ensure that minimum and maximum values of slices are correct.""" + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1) + assert SFT.p_min == 0 + assert SFT.k_min == -4 + assert SFT.lower_border_end == (4, 1) + assert SFT.lower_border_end == (4, 1) # needed to test caching + assert SFT.p_max(10) == 4 + assert SFT.k_max(10) == 16 + assert SFT.upper_border_begin(10) == (4, 2) + + +def test_border_values_exotic(): + """Ensure that the border calculations are correct for windows with + zeros. """ + w = np.array([0, 0, 0, 0, 0, 0, 0, 1.]) + SFT = ShortTimeFFT(w, hop=1, fs=1) + assert SFT.lower_border_end == (0, 0) + + SFT = ShortTimeFFT(np.flip(w), hop=20, fs=1) + assert SFT.upper_border_begin(4) == (0, 0) + + SFT._hop = -1 # provoke unreachable line + with pytest.raises(RuntimeError): + _ = SFT.k_max(4) + with pytest.raises(RuntimeError): + _ = SFT.k_min + + +def test_t(): + """Verify that the times of the slices are correct. """ + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=2) + assert SFT.T == 1/2 + assert SFT.fs == 2. + assert SFT.delta_t == 4 * 1/2 + t_stft = np.arange(0, SFT.p_max(10)) * SFT.delta_t + assert_equal(SFT.t(10), t_stft) + assert_equal(SFT.t(10, 1, 3), t_stft[1:3]) + SFT.T = 1/4 + assert SFT.T == 1/4 + assert SFT.fs == 4 + SFT.fs = 1/8 + assert SFT.fs == 1/8 + assert SFT.T == 8 + + +@pytest.mark.parametrize('fft_mode, f', + [('onesided', [0., 1., 2.]), + ('onesided2X', [0., 1., 2.]), + ('twosided', [0., 1., 2., -2., -1.]), + ('centered', [-2., -1., 0., 1., 2.])]) +def test_f(fft_mode: FFT_MODE_TYPE, f): + """Verify the frequency values property `f`.""" + SFT = ShortTimeFFT(np.ones(5), hop=4, fs=5, fft_mode=fft_mode, + scale_to='psd') + assert_equal(SFT.f, f) + + +def test_extent(): + """Ensure that the `extent()` method is correct. """ + SFT = ShortTimeFFT(np.ones(32), hop=4, fs=32, fft_mode='onesided') + assert SFT.extent(100, 'tf', False) == (-0.375, 3.625, 0.0, 17.0) + assert SFT.extent(100, 'ft', False) == (0.0, 17.0, -0.375, 3.625) + assert SFT.extent(100, 'tf', True) == (-0.4375, 3.5625, -0.5, 16.5) + assert SFT.extent(100, 'ft', True) == (-0.5, 16.5, -0.4375, 3.5625) + + SFT = ShortTimeFFT(np.ones(32), hop=4, fs=32, fft_mode='centered') + assert SFT.extent(100, 'tf', False) == (-0.375, 3.625, -16.0, 15.0) + + +def test_spectrogram(): + """Verify spectrogram and cross-spectrogram methods. """ + SFT = ShortTimeFFT(np.ones(8), hop=4, fs=1) + x, y = np.ones(10), np.arange(10) + X, Y = SFT.stft(x), SFT.stft(y) + assert_allclose(SFT.spectrogram(x), X.real**2+X.imag**2) + assert_allclose(SFT.spectrogram(x, y), X * Y.conj()) + + +@pytest.mark.parametrize('n', [8, 9]) +def test_fft_func_roundtrip(n: int): + """Test roundtrip `ifft_func(fft_func(x)) == x` for all permutations of + relevant parameters. """ + np.random.seed(2394795) + x0 = np.random.rand(n) + w, h_n = np.ones(n), 4 + + pp = dict( + fft_mode=get_args(FFT_MODE_TYPE), + mfft=[None, n, n+1, n+2], + scaling=[None, 'magnitude', 'psd'], + phase_shift=[None, -n+1, 0, n // 2, n-1]) + for f_typ, mfft, scaling, phase_shift in product(*pp.values()): + if f_typ == 'onesided2X' and scaling is None: + continue # this combination is forbidden + SFT = ShortTimeFFT(w, h_n, fs=n, fft_mode=f_typ, mfft=mfft, + scale_to=scaling, phase_shift=phase_shift) + X0 = SFT._fft_func(x0) + x1 = SFT._ifft_func(X0) + assert_allclose(x0, x1, err_msg="_fft_func() roundtrip failed for " + + f"{f_typ=}, {mfft=}, {scaling=}, {phase_shift=}") + + SFT = ShortTimeFFT(w, h_n, fs=1) + SFT._fft_mode = 'invalid_fft' # type: ignore + with pytest.raises(RuntimeError): + SFT._fft_func(x0) + with pytest.raises(RuntimeError): + SFT._ifft_func(x0) + + +@pytest.mark.parametrize('i', range(19)) +def test_impulse_roundtrip(i): + """Roundtrip for an impulse being at different positions `i`.""" + n = 19 + w, h_n = np.ones(8), 3 + x = np.zeros(n) + x[i] = 1 + + SFT = ShortTimeFFT(w, hop=h_n, fs=1, scale_to=None, phase_shift=None) + Sx = SFT.stft(x) + # test slicing the input signal into two parts: + n_q = SFT.nearest_k_p(n // 2) + Sx0 = SFT.stft(x[:n_q], padding='zeros') + Sx1 = SFT.stft(x[n_q:], padding='zeros') + q0_ub = SFT.upper_border_begin(n_q)[1] - SFT.p_min + q1_le = SFT.lower_border_end[1] - SFT.p_min + assert_allclose(Sx0[:, :q0_ub], Sx[:, :q0_ub], err_msg=f"{i=}") + assert_allclose(Sx1[:, q1_le:], Sx[:, q1_le-Sx1.shape[1]:], + err_msg=f"{i=}") + + Sx01 = np.hstack((Sx0[:, :q0_ub], + Sx0[:, q0_ub:] + Sx1[:, :q1_le], + Sx1[:, q1_le:])) + assert_allclose(Sx, Sx01, atol=1e-8, err_msg=f"{i=}") + + y = SFT.istft(Sx, 0, n) + assert_allclose(y, x, atol=1e-8, err_msg=f"{i=}") + y0 = SFT.istft(Sx, 0, n//2) + assert_allclose(x[:n//2], y0, atol=1e-8, err_msg=f"{i=}") + y1 = SFT.istft(Sx, n // 2, n) + assert_allclose(x[n // 2:], y1, atol=1e-8, err_msg=f"{i=}") + + +@pytest.mark.parametrize('hop', [1, 7, 8]) +def test_asymmetric_window_roundtrip(hop: int): + """An asymmetric window could uncover indexing problems. """ + np.random.seed(23371) + + w = np.arange(16) / 8 # must be of type float + w[len(w)//2:] = 1 + SFT = ShortTimeFFT(w, hop, fs=1) + + x = 10 * np.random.randn(64) + Sx = SFT.stft(x) + x1 = SFT.istft(Sx, k1=len(x)) + assert_allclose(x1, x1, err_msg="Roundtrip for asymmetric window with " + + f" {hop=} failed!") + + +@pytest.mark.parametrize('m_num', [6, 7]) +def test_minimal_length_signal(m_num): + """Verify that the shortest allowed signal works. """ + SFT = ShortTimeFFT(np.ones(m_num), m_num//2, fs=1) + n = math.ceil(m_num/2) + x = np.ones(n) + Sx = SFT.stft(x) + x1 = SFT.istft(Sx, k1=n) + assert_allclose(x1, x, err_msg=f"Roundtrip minimal length signal ({n=})" + + f" for {m_num} sample window failed!") + with pytest.raises(ValueError, match=rf"len\(x\)={n-1} must be >= ceil.*"): + SFT.stft(x[:-1]) + with pytest.raises(ValueError, match=rf"S.shape\[t_axis\]={Sx.shape[1]-1}" + f" needs to have at least {Sx.shape[1]} slices"): + SFT.istft(Sx[:, :-1], k1=n) + + +def test_tutorial_stft_sliding_win(): + """Verify example in "Sliding Windows" subsection from the "User Guide". + + In :ref:`tutorial_stft_sliding_win` (file ``signal.rst``) of the + :ref:`user_guide` the behavior the border behavior of + ``ShortTimeFFT(np.ones(6), 2, fs=1)`` with a 50 sample signal is discussed. + This test verifies the presented indexes. + """ + SFT = ShortTimeFFT(np.ones(6), 2, fs=1) + + # Lower border: + assert SFT.m_num_mid == 3, f"Slice middle is not 3 but {SFT.m_num_mid=}" + assert SFT.p_min == -1, f"Lowest slice {SFT.p_min=} is not -1" + assert SFT.k_min == -5, f"Lowest slice sample {SFT.p_min=} is not -5" + k_lb, p_lb = SFT.lower_border_end + assert p_lb == 2, f"First unaffected slice {p_lb=} is not 2" + assert k_lb == 5, f"First unaffected sample {k_lb=} is not 5" + + n = 50 # upper signal border + assert (p_max := SFT.p_max(n)) == 27, f"Last slice {p_max=} must be 27" + assert (k_max := SFT.k_max(n)) == 55, f"Last sample {k_max=} must be 55" + k_ub, p_ub = SFT.upper_border_begin(n) + assert p_ub == 24, f"First upper border slice {p_ub=} must be 24" + assert k_ub == 45, f"First upper border slice {k_ub=} must be 45" + + +def test_tutorial_stft_legacy_stft(): + """Verify STFT example in "Comparison with Legacy Implementation" from the + "User Guide". + + In :ref:`tutorial_stft_legacy_stft` (file ``signal.rst``) of the + :ref:`user_guide` the legacy and the new implementation are compared. + """ + fs, N = 200, 1001 # # 200 Hz sampling rate for 5 s signal + t_z = np.arange(N) / fs # time indexes for signal + z = np.exp(2j*np.pi * 70 * (t_z - 0.2 * t_z ** 2)) # complex-valued chirp + + nperseg, noverlap = 50, 40 + win = ('gaussian', 1e-2 * fs) # Gaussian with 0.01 s standard deviation + + # Legacy STFT: + f0_u, t0, Sz0_u = stft(z, fs, win, nperseg, noverlap, + return_onesided=False, scaling='spectrum') + Sz0 = fftshift(Sz0_u, axes=0) + + # New STFT: + SFT = ShortTimeFFT.from_window(win, fs, nperseg, noverlap, + fft_mode='centered', + scale_to='magnitude', phase_shift=None) + Sz1 = SFT.stft(z) + + assert_allclose(Sz0, Sz1[:, 2:-1]) + + assert_allclose((abs(Sz1[:, 1]).min(), abs(Sz1[:, 1]).max()), + (6.925060911593139e-07, 8.00271269218721e-07)) + + t0_r, z0_r = istft(Sz0_u, fs, win, nperseg, noverlap, input_onesided=False, + scaling='spectrum') + z1_r = SFT.istft(Sz1, k1=N) + assert len(z0_r) == N + 9 + assert_allclose(z0_r[:N], z) + assert_allclose(z1_r, z) + + # Spectrogram is just the absolute square of th STFT: + assert_allclose(SFT.spectrogram(z), abs(Sz1) ** 2) + + +def test_tutorial_stft_legacy_spectrogram(): + """Verify spectrogram example in "Comparison with Legacy Implementation" + from the "User Guide". + + In :ref:`tutorial_stft_legacy_stft` (file ``signal.rst``) of the + :ref:`user_guide` the legacy and the new implementation are compared. + """ + fs, N = 200, 1001 # 200 Hz sampling rate for almost 5 s signal + t_z = np.arange(N) / fs # time indexes for signal + z = np.exp(2j*np.pi*70 * (t_z - 0.2*t_z**2)) # complex-valued sweep + + nperseg, noverlap = 50, 40 + win = ('gaussian', 1e-2 * fs) # Gaussian with 0.01 s standard dev. + + # Legacy spectrogram: + f2_u, t2, Sz2_u = spectrogram(z, fs, win, nperseg, noverlap, detrend=None, + return_onesided=False, scaling='spectrum', + mode='complex') + + f2, Sz2 = fftshift(f2_u), fftshift(Sz2_u, axes=0) + + # New STFT: + SFT = ShortTimeFFT.from_window(win, fs, nperseg, noverlap, + fft_mode='centered', scale_to='magnitude', + phase_shift=None) + Sz3 = SFT.stft(z, p0=0, p1=(N-noverlap) // SFT.hop, k_offset=nperseg // 2) + t3 = SFT.t(N, p0=0, p1=(N-noverlap) // SFT.hop, k_offset=nperseg // 2) + + assert_allclose(t2, t3) + assert_allclose(f2, SFT.f) + assert_allclose(Sz2, Sz3) + + +def test_permute_axes(): + """Verify correctness of four-dimensional signal by permuting its + shape. """ + n = 25 + SFT = ShortTimeFFT(np.ones(8)/8, hop=3, fs=n) + x0 = np.arange(n) + Sx0 = SFT.stft(x0) + Sx0 = Sx0.reshape((Sx0.shape[0], 1, 1, 1, Sx0.shape[-1])) + SxT = np.moveaxis(Sx0, (0, -1), (-1, 0)) + + atol = 2 * np.finfo(SFT.win.dtype).resolution + for i in range(4): + y = np.reshape(x0, np.roll((n, 1, 1, 1), i)) + Sy = SFT.stft(y, axis=i) + assert_allclose(Sy, np.moveaxis(Sx0, 0, i)) + + yb0 = SFT.istft(Sy, k1=n, f_axis=i) + assert_allclose(yb0, y, atol=atol) + # explicit t-axis parameter (for coverage): + yb1 = SFT.istft(Sy, k1=n, f_axis=i, t_axis=Sy.ndim-1) + assert_allclose(yb1, y, atol=atol) + + SyT = np.moveaxis(Sy, (i, -1), (-1, i)) + assert_allclose(SyT, np.moveaxis(SxT, 0, i)) + + ybT = SFT.istft(SyT, k1=n, t_axis=i, f_axis=-1) + assert_allclose(ybT, y, atol=atol) + + +@pytest.mark.parametrize("fft_mode", + ('twosided', 'centered', 'onesided', 'onesided2X')) +def test_roundtrip_multidimensional(fft_mode: FFT_MODE_TYPE): + """Test roundtrip of a multidimensional input signal versus its components. + + This test can uncover potential problems with `fftshift()`. + """ + n = 9 + x = np.arange(4*n*2).reshape(4, n, 2) + SFT = ShortTimeFFT(get_window('hann', 4), hop=2, fs=1, + scale_to='magnitude', fft_mode=fft_mode) + Sx = SFT.stft(x, axis=1) + y = SFT.istft(Sx, k1=n, f_axis=1, t_axis=-1) + assert_allclose(y, x, err_msg='Multidim. roundtrip failed!') + + for i, j in product(range(x.shape[0]), range(x.shape[2])): + y_ = SFT.istft(Sx[i, :, j, :], k1=n) + assert_allclose(y_, x[i, :, j], err_msg="Multidim. roundtrip for component " + + f"x[{i}, :, {j}] and {fft_mode=} failed!") + + +@pytest.mark.parametrize('window, n, nperseg, noverlap', + [('boxcar', 100, 10, 0), # Test no overlap + ('boxcar', 100, 10, 9), # Test high overlap + ('bartlett', 101, 51, 26), # Test odd nperseg + ('hann', 1024, 256, 128), # Test defaults + (('tukey', 0.5), 1152, 256, 64), # Test Tukey + ('hann', 1024, 256, 255), # Test overlapped hann + ('boxcar', 100, 10, 3), # NOLA True, COLA False + ('bartlett', 101, 51, 37), # NOLA True, COLA False + ('hann', 1024, 256, 127), # NOLA True, COLA False + # NOLA True, COLA False: + (('tukey', 0.5), 1152, 256, 14), + ('hann', 1024, 256, 5)]) # NOLA True, COLA False +def test_roundtrip_windows(window, n: int, nperseg: int, noverlap: int): + """Roundtrip test adapted from `test_spectral.TestSTFT`. + + The parameters are taken from the methods test_roundtrip_real(), + test_roundtrip_nola_not_cola(), test_roundtrip_float32(), + test_roundtrip_complex(). + """ + np.random.seed(2394655) + + w = get_window(window, nperseg) + SFT = ShortTimeFFT(w, nperseg - noverlap, fs=1, fft_mode='twosided', + phase_shift=None) + + z = 10 * np.random.randn(n) + 10j * np.random.randn(n) + Sz = SFT.stft(z) + z1 = SFT.istft(Sz, k1=len(z)) + assert_allclose(z, z1, err_msg="Roundtrip for complex values failed") + + x = 10 * np.random.randn(n) + Sx = SFT.stft(x) + x1 = SFT.istft(Sx, k1=len(z)) + assert_allclose(x, x1, err_msg="Roundtrip for float values failed") + + x32 = x.astype(np.float32) + Sx32 = SFT.stft(x32) + x32_1 = SFT.istft(Sx32, k1=len(x32)) + assert_allclose(x32, x32_1, + err_msg="Roundtrip for 32 Bit float values failed") + + +@pytest.mark.parametrize('signal_type', ('real', 'complex')) +def test_roundtrip_complex_window(signal_type): + """Test roundtrip for complex-valued window function + + The purpose of this test is to check if the dual window is calculated + correctly for complex-valued windows. + """ + np.random.seed(1354654) + win = np.exp(2j*np.linspace(0, np.pi, 8)) + SFT = ShortTimeFFT(win, 3, fs=1, fft_mode='twosided') + + z = 10 * np.random.randn(11) + if signal_type == 'complex': + z = z + 2j * z + Sz = SFT.stft(z) + z1 = SFT.istft(Sz, k1=len(z)) + assert_allclose(z, z1, + err_msg="Roundtrip for complex-valued window failed") + + +def test_average_all_segments(): + """Compare `welch` function with stft mean. + + Ported from `TestSpectrogram.test_average_all_segments` from file + ``test__spectral.py``. + """ + x = np.random.randn(1024) + + fs = 1.0 + window = ('tukey', 0.25) + nperseg, noverlap = 16, 2 + fw, Pw = welch(x, fs, window, nperseg, noverlap) + SFT = ShortTimeFFT.from_window(window, fs, nperseg, noverlap, + fft_mode='onesided2X', scale_to='psd', + phase_shift=None) + # `welch` positions the window differently than the STFT: + P = SFT.spectrogram(x, detr='constant', p0=0, + p1=(len(x)-noverlap)//SFT.hop, k_offset=nperseg//2) + + assert_allclose(SFT.f, fw) + assert_allclose(np.mean(P, axis=-1), Pw) + + +@pytest.mark.parametrize('window, N, nperseg, noverlap, mfft', + # from test_roundtrip_padded_FFT: + [('hann', 1024, 256, 128, 512), + ('hann', 1024, 256, 128, 501), + ('boxcar', 100, 10, 0, 33), + (('tukey', 0.5), 1152, 256, 64, 1024), + # from test_roundtrip_padded_signal: + ('boxcar', 101, 10, 0, None), + ('hann', 1000, 256, 128, None), + # from test_roundtrip_boundary_extension: + ('boxcar', 100, 10, 0, None), + ('boxcar', 100, 10, 9, None)]) +@pytest.mark.parametrize('padding', get_args(PAD_TYPE)) +def test_stft_padding_roundtrip(window, N: int, nperseg: int, noverlap: int, + mfft: int, padding): + """Test the parameter 'padding' of `stft` with roundtrips. + + The STFT parametrizations were taken from the methods + `test_roundtrip_padded_FFT`, `test_roundtrip_padded_signal` and + `test_roundtrip_boundary_extension` from class `TestSTFT` in file + ``test_spectral.py``. Note that the ShortTimeFFT does not need the + concept of "boundary extension". + """ + x = normal_distribution.rvs(size=N, random_state=2909) # real signal + z = x * np.exp(1j * np.pi / 4) # complex signal + + SFT = ShortTimeFFT.from_window(window, 1, nperseg, noverlap, + fft_mode='twosided', mfft=mfft) + Sx = SFT.stft(x, padding=padding) + x1 = SFT.istft(Sx, k1=N) + assert_allclose(x1, x, + err_msg=f"Failed real roundtrip with '{padding}' padding") + + Sz = SFT.stft(z, padding=padding) + z1 = SFT.istft(Sz, k1=N) + assert_allclose(z1, z, err_msg="Failed complex roundtrip with " + + f" '{padding}' padding") + + +@pytest.mark.parametrize('N_x', (128, 129, 255, 256, 1337)) # signal length +@pytest.mark.parametrize('w_size', (128, 256)) # window length +@pytest.mark.parametrize('t_step', (4, 64)) # SFT time hop +@pytest.mark.parametrize('f_c', (7., 23.)) # frequency of input sine +def test_energy_conservation(N_x: int, w_size: int, t_step: int, f_c: float): + """Test if a `psd`-scaled STFT conserves the L2 norm. + + This test is adapted from MNE-Python [1]_. Besides being battle-tested, + this test has the benefit of using non-standard window including + non-positive values and a 2d input signal. + + Since `ShortTimeFFT` requires the signal length `N_x` to be at least the + window length `w_size`, the parameter `N_x` was changed from + ``(127, 128, 255, 256, 1337)`` to ``(128, 129, 255, 256, 1337)`` to be + more useful. + + .. [1] File ``test_stft.py`` of MNE-Python + https://github.com/mne-tools/mne-python/blob/main/mne/time_frequency/tests/test_stft.py + """ + window = np.sin(np.arange(.5, w_size + .5) / w_size * np.pi) + SFT = ShortTimeFFT(window, t_step, fs=1000, fft_mode='onesided2X', + scale_to='psd') + atol = 2*np.finfo(window.dtype).resolution + N_x = max(N_x, w_size) # minimal sing + # Test with low frequency signal + t = np.arange(N_x).astype(np.float64) + x = np.sin(2 * np.pi * f_c * t * SFT.T) + x = np.array([x, x + 1.]) + X = SFT.stft(x) + xp = SFT.istft(X, k1=N_x) + + max_freq = SFT.f[np.argmax(np.sum(np.abs(X[0]) ** 2, axis=1))] + + assert X.shape[1] == SFT.f_pts + assert np.all(SFT.f >= 0.) + assert np.abs(max_freq - f_c) < 1. + assert_allclose(x, xp, atol=atol) + + # check L2-norm squared (i.e., energy) conservation: + E_x = np.sum(x**2, axis=-1) * SFT.T # numerical integration + aX2 = X.real**2 + X.imag.real**2 + E_X = np.sum(np.sum(aX2, axis=-1) * SFT.delta_t, axis=-1) * SFT.delta_f + assert_allclose(E_X, E_x, atol=atol) + + # Test with random signal + np.random.seed(2392795) + x = np.random.randn(2, N_x) + X = SFT.stft(x) + xp = SFT.istft(X, k1=N_x) + + assert X.shape[1] == SFT.f_pts + assert np.all(SFT.f >= 0.) + assert np.abs(max_freq - f_c) < 1. + assert_allclose(x, xp, atol=atol) + + # check L2-norm squared (i.e., energy) conservation: + E_x = np.sum(x**2, axis=-1) * SFT.T # numeric integration + aX2 = X.real ** 2 + X.imag.real ** 2 + E_X = np.sum(np.sum(aX2, axis=-1) * SFT.delta_t, axis=-1) * SFT.delta_f + assert_allclose(E_X, E_x, atol=atol) + + # Try with empty array + x = np.zeros((0, N_x)) + X = SFT.stft(x) + xp = SFT.istft(X, k1=N_x) + assert xp.shape == x.shape diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_signaltools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_signaltools.py new file mode 100644 index 0000000000000000000000000000000000000000..8be2308c47f92070b4c6e8f27212641efb9a4060 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_signaltools.py @@ -0,0 +1,3695 @@ +import sys + +from concurrent.futures import ThreadPoolExecutor, as_completed +from decimal import Decimal +from itertools import product +from math import gcd + +import pytest +from pytest import raises as assert_raises +from numpy.testing import ( + assert_equal, + assert_almost_equal, assert_array_equal, assert_array_almost_equal, + assert_allclose, assert_, assert_array_less, + suppress_warnings) +from numpy import array, arange +import numpy as np + +from scipy.fft import fft +from scipy.ndimage import correlate1d +from scipy.optimize import fmin, linear_sum_assignment +from scipy import signal +from scipy.signal import ( + correlate, correlate2d, correlation_lags, convolve, convolve2d, + fftconvolve, oaconvolve, choose_conv_method, + hilbert, hilbert2, lfilter, lfilter_zi, filtfilt, butter, zpk2tf, zpk2sos, + invres, invresz, vectorstrength, lfiltic, tf2sos, sosfilt, sosfiltfilt, + sosfilt_zi, tf2zpk, BadCoefficients, detrend, unique_roots, residue, + residuez) +from scipy.signal.windows import hann +from scipy.signal._signaltools import (_filtfilt_gust, _compute_factors, + _group_poles) +from scipy.signal._upfirdn import _upfirdn_modes +from scipy._lib import _testutils +from scipy._lib._util import ComplexWarning, np_long, np_ulong + + +class _TestConvolve: + + def test_basic(self): + a = [3, 4, 5, 6, 5, 4] + b = [1, 2, 3] + c = convolve(a, b) + assert_array_equal(c, array([3, 10, 22, 28, 32, 32, 23, 12])) + + def test_same(self): + a = [3, 4, 5] + b = [1, 2, 3, 4] + c = convolve(a, b, mode="same") + assert_array_equal(c, array([10, 22, 34])) + + def test_same_eq(self): + a = [3, 4, 5] + b = [1, 2, 3] + c = convolve(a, b, mode="same") + assert_array_equal(c, array([10, 22, 22])) + + def test_complex(self): + x = array([1 + 1j, 2 + 1j, 3 + 1j]) + y = array([1 + 1j, 2 + 1j]) + z = convolve(x, y) + assert_array_equal(z, array([2j, 2 + 6j, 5 + 8j, 5 + 5j])) + + def test_zero_rank(self): + a = 1289 + b = 4567 + c = convolve(a, b) + assert_equal(c, a * b) + + def test_broadcastable(self): + a = np.arange(27).reshape(3, 3, 3) + b = np.arange(3) + for i in range(3): + b_shape = [1]*3 + b_shape[i] = 3 + x = convolve(a, b.reshape(b_shape), method='direct') + y = convolve(a, b.reshape(b_shape), method='fft') + assert_allclose(x, y) + + def test_single_element(self): + a = array([4967]) + b = array([3920]) + c = convolve(a, b) + assert_equal(c, a * b) + + def test_2d_arrays(self): + a = [[1, 2, 3], [3, 4, 5]] + b = [[2, 3, 4], [4, 5, 6]] + c = convolve(a, b) + d = array([[2, 7, 16, 17, 12], + [10, 30, 62, 58, 38], + [12, 31, 58, 49, 30]]) + assert_array_equal(c, d) + + def test_input_swapping(self): + small = arange(8).reshape(2, 2, 2) + big = 1j * arange(27).reshape(3, 3, 3) + big += arange(27)[::-1].reshape(3, 3, 3) + + out_array = array( + [[[0 + 0j, 26 + 0j, 25 + 1j, 24 + 2j], + [52 + 0j, 151 + 5j, 145 + 11j, 93 + 11j], + [46 + 6j, 133 + 23j, 127 + 29j, 81 + 23j], + [40 + 12j, 98 + 32j, 93 + 37j, 54 + 24j]], + + [[104 + 0j, 247 + 13j, 237 + 23j, 135 + 21j], + [282 + 30j, 632 + 96j, 604 + 124j, 330 + 86j], + [246 + 66j, 548 + 180j, 520 + 208j, 282 + 134j], + [142 + 66j, 307 + 161j, 289 + 179j, 153 + 107j]], + + [[68 + 36j, 157 + 103j, 147 + 113j, 81 + 75j], + [174 + 138j, 380 + 348j, 352 + 376j, 186 + 230j], + [138 + 174j, 296 + 432j, 268 + 460j, 138 + 278j], + [70 + 138j, 145 + 323j, 127 + 341j, 63 + 197j]], + + [[32 + 72j, 68 + 166j, 59 + 175j, 30 + 100j], + [68 + 192j, 139 + 433j, 117 + 455j, 57 + 255j], + [38 + 222j, 73 + 499j, 51 + 521j, 21 + 291j], + [12 + 144j, 20 + 318j, 7 + 331j, 0 + 182j]]]) + + assert_array_equal(convolve(small, big, 'full'), out_array) + assert_array_equal(convolve(big, small, 'full'), out_array) + assert_array_equal(convolve(small, big, 'same'), + out_array[1:3, 1:3, 1:3]) + assert_array_equal(convolve(big, small, 'same'), + out_array[0:3, 0:3, 0:3]) + assert_array_equal(convolve(small, big, 'valid'), + out_array[1:3, 1:3, 1:3]) + assert_array_equal(convolve(big, small, 'valid'), + out_array[1:3, 1:3, 1:3]) + + def test_invalid_params(self): + a = [3, 4, 5] + b = [1, 2, 3] + assert_raises(ValueError, convolve, a, b, mode='spam') + assert_raises(ValueError, convolve, a, b, mode='eggs', method='fft') + assert_raises(ValueError, convolve, a, b, mode='ham', method='direct') + assert_raises(ValueError, convolve, a, b, mode='full', method='bacon') + assert_raises(ValueError, convolve, a, b, mode='same', method='bacon') + + +class TestConvolve(_TestConvolve): + + def test_valid_mode2(self): + # See gh-5897 + a = [1, 2, 3, 6, 5, 3] + b = [2, 3, 4, 5, 3, 4, 2, 2, 1] + expected = [70, 78, 73, 65] + + out = convolve(a, b, 'valid') + assert_array_equal(out, expected) + + out = convolve(b, a, 'valid') + assert_array_equal(out, expected) + + a = [1 + 5j, 2 - 1j, 3 + 0j] + b = [2 - 3j, 1 + 0j] + expected = [2 - 3j, 8 - 10j] + + out = convolve(a, b, 'valid') + assert_array_equal(out, expected) + + out = convolve(b, a, 'valid') + assert_array_equal(out, expected) + + def test_same_mode(self): + a = [1, 2, 3, 3, 1, 2] + b = [1, 4, 3, 4, 5, 6, 7, 4, 3, 2, 1, 1, 3] + c = convolve(a, b, 'same') + d = array([57, 61, 63, 57, 45, 36]) + assert_array_equal(c, d) + + def test_invalid_shapes(self): + # By "invalid," we mean that no one + # array has dimensions that are all at + # least as large as the corresponding + # dimensions of the other array. This + # setup should throw a ValueError. + a = np.arange(1, 7).reshape((2, 3)) + b = np.arange(-6, 0).reshape((3, 2)) + + assert_raises(ValueError, convolve, *(a, b), **{'mode': 'valid'}) + assert_raises(ValueError, convolve, *(b, a), **{'mode': 'valid'}) + + def test_convolve_method(self, n=100): + # this types data structure was manually encoded instead of + # using custom filters on the soon-to-be-removed np.sctypes + types = {'uint16', 'uint64', 'int64', 'int32', + 'complex128', 'float64', 'float16', + 'complex64', 'float32', 'int16', + 'uint8', 'uint32', 'int8', 'bool'} + args = [(t1, t2, mode) for t1 in types for t2 in types + for mode in ['valid', 'full', 'same']] + + # These are random arrays, which means test is much stronger than + # convolving testing by convolving two np.ones arrays + np.random.seed(42) + array_types = {'i': np.random.choice([0, 1], size=n), + 'f': np.random.randn(n)} + array_types['b'] = array_types['u'] = array_types['i'] + array_types['c'] = array_types['f'] + 0.5j*array_types['f'] + + for t1, t2, mode in args: + x1 = array_types[np.dtype(t1).kind].astype(t1) + x2 = array_types[np.dtype(t2).kind].astype(t2) + + results = {key: convolve(x1, x2, method=key, mode=mode) + for key in ['fft', 'direct']} + + assert_equal(results['fft'].dtype, results['direct'].dtype) + + if 'bool' in t1 and 'bool' in t2: + assert_equal(choose_conv_method(x1, x2), 'direct') + continue + + # Found by experiment. Found approx smallest value for (rtol, atol) + # threshold to have tests pass. + if any([t in {'complex64', 'float32'} for t in [t1, t2]]): + kwargs = {'rtol': 1.0e-4, 'atol': 1e-6} + elif 'float16' in [t1, t2]: + # atol is default for np.allclose + kwargs = {'rtol': 1e-3, 'atol': 1e-3} + else: + # defaults for np.allclose (different from assert_allclose) + kwargs = {'rtol': 1e-5, 'atol': 1e-8} + + assert_allclose(results['fft'], results['direct'], **kwargs) + + def test_convolve_method_large_input(self): + # This is really a test that convolving two large integers goes to the + # direct method even if they're in the fft method. + for n in [10, 20, 50, 51, 52, 53, 54, 60, 62]: + z = np.array([2**n], dtype=np.int64) + fft = convolve(z, z, method='fft') + direct = convolve(z, z, method='direct') + + # this is the case when integer precision gets to us + # issue #6076 has more detail, hopefully more tests after resolved + if n < 50: + assert_equal(fft, direct) + assert_equal(fft, 2**(2*n)) + assert_equal(direct, 2**(2*n)) + + def test_mismatched_dims(self): + # Input arrays should have the same number of dimensions + assert_raises(ValueError, convolve, [1], 2, method='direct') + assert_raises(ValueError, convolve, 1, [2], method='direct') + assert_raises(ValueError, convolve, [1], 2, method='fft') + assert_raises(ValueError, convolve, 1, [2], method='fft') + assert_raises(ValueError, convolve, [1], [[2]]) + assert_raises(ValueError, convolve, [3], 2) + + +class _TestConvolve2d: + + def test_2d_arrays(self): + a = [[1, 2, 3], [3, 4, 5]] + b = [[2, 3, 4], [4, 5, 6]] + d = array([[2, 7, 16, 17, 12], + [10, 30, 62, 58, 38], + [12, 31, 58, 49, 30]]) + e = convolve2d(a, b) + assert_array_equal(e, d) + + def test_valid_mode(self): + e = [[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]] + f = [[1, 2, 3], [3, 4, 5]] + h = array([[62, 80, 98, 116, 134]]) + + g = convolve2d(e, f, 'valid') + assert_array_equal(g, h) + + # See gh-5897 + g = convolve2d(f, e, 'valid') + assert_array_equal(g, h) + + def test_valid_mode_complx(self): + e = [[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]] + f = np.array([[1, 2, 3], [3, 4, 5]], dtype=complex) + 1j + h = array([[62.+24.j, 80.+30.j, 98.+36.j, 116.+42.j, 134.+48.j]]) + + g = convolve2d(e, f, 'valid') + assert_array_almost_equal(g, h) + + # See gh-5897 + g = convolve2d(f, e, 'valid') + assert_array_equal(g, h) + + def test_fillvalue(self): + a = [[1, 2, 3], [3, 4, 5]] + b = [[2, 3, 4], [4, 5, 6]] + fillval = 1 + c = convolve2d(a, b, 'full', 'fill', fillval) + d = array([[24, 26, 31, 34, 32], + [28, 40, 62, 64, 52], + [32, 46, 67, 62, 48]]) + assert_array_equal(c, d) + + def test_fillvalue_errors(self): + msg = "could not cast `fillvalue` directly to the output " + with np.testing.suppress_warnings() as sup: + sup.filter(ComplexWarning, "Casting complex values") + with assert_raises(ValueError, match=msg): + convolve2d([[1]], [[1, 2]], fillvalue=1j) + + msg = "`fillvalue` must be scalar or an array with " + with assert_raises(ValueError, match=msg): + convolve2d([[1]], [[1, 2]], fillvalue=[1, 2]) + + def test_fillvalue_empty(self): + # Check that fillvalue being empty raises an error: + assert_raises(ValueError, convolve2d, [[1]], [[1, 2]], + fillvalue=[]) + + def test_wrap_boundary(self): + a = [[1, 2, 3], [3, 4, 5]] + b = [[2, 3, 4], [4, 5, 6]] + c = convolve2d(a, b, 'full', 'wrap') + d = array([[80, 80, 74, 80, 80], + [68, 68, 62, 68, 68], + [80, 80, 74, 80, 80]]) + assert_array_equal(c, d) + + def test_sym_boundary(self): + a = [[1, 2, 3], [3, 4, 5]] + b = [[2, 3, 4], [4, 5, 6]] + c = convolve2d(a, b, 'full', 'symm') + d = array([[34, 30, 44, 62, 66], + [52, 48, 62, 80, 84], + [82, 78, 92, 110, 114]]) + assert_array_equal(c, d) + + @pytest.mark.parametrize('func', [convolve2d, correlate2d]) + @pytest.mark.parametrize('boundary, expected', + [('symm', [[37.0, 42.0, 44.0, 45.0]]), + ('wrap', [[43.0, 44.0, 42.0, 39.0]])]) + def test_same_with_boundary(self, func, boundary, expected): + # Test boundary='symm' and boundary='wrap' with a "long" kernel. + # The size of the kernel requires that the values in the "image" + # be extended more than once to handle the requested boundary method. + # This is a regression test for gh-8684 and gh-8814. + image = np.array([[2.0, -1.0, 3.0, 4.0]]) + kernel = np.ones((1, 21)) + result = func(image, kernel, mode='same', boundary=boundary) + # The expected results were calculated "by hand". Because the + # kernel is all ones, the same result is expected for convolve2d + # and correlate2d. + assert_array_equal(result, expected) + + def test_boundary_extension_same(self): + # Regression test for gh-12686. + # Use ndimage.convolve with appropriate arguments to create the + # expected result. + import scipy.ndimage as ndi + a = np.arange(1, 10*3+1, dtype=float).reshape(10, 3) + b = np.arange(1, 10*10+1, dtype=float).reshape(10, 10) + c = convolve2d(a, b, mode='same', boundary='wrap') + assert_array_equal(c, ndi.convolve(a, b, mode='wrap', origin=(-1, -1))) + + def test_boundary_extension_full(self): + # Regression test for gh-12686. + # Use ndimage.convolve with appropriate arguments to create the + # expected result. + import scipy.ndimage as ndi + a = np.arange(1, 3*3+1, dtype=float).reshape(3, 3) + b = np.arange(1, 6*6+1, dtype=float).reshape(6, 6) + c = convolve2d(a, b, mode='full', boundary='wrap') + apad = np.pad(a, ((3, 3), (3, 3)), 'wrap') + assert_array_equal(c, ndi.convolve(apad, b, mode='wrap')[:-1, :-1]) + + def test_invalid_shapes(self): + # By "invalid," we mean that no one + # array has dimensions that are all at + # least as large as the corresponding + # dimensions of the other array. This + # setup should throw a ValueError. + a = np.arange(1, 7).reshape((2, 3)) + b = np.arange(-6, 0).reshape((3, 2)) + + assert_raises(ValueError, convolve2d, *(a, b), **{'mode': 'valid'}) + assert_raises(ValueError, convolve2d, *(b, a), **{'mode': 'valid'}) + + +class TestConvolve2d(_TestConvolve2d): + + def test_same_mode(self): + e = [[1, 2, 3], [3, 4, 5]] + f = [[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]] + g = convolve2d(e, f, 'same') + h = array([[22, 28, 34], + [80, 98, 116]]) + assert_array_equal(g, h) + + def test_valid_mode2(self): + # See gh-5897 + e = [[1, 2, 3], [3, 4, 5]] + f = [[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]] + expected = [[62, 80, 98, 116, 134]] + + out = convolve2d(e, f, 'valid') + assert_array_equal(out, expected) + + out = convolve2d(f, e, 'valid') + assert_array_equal(out, expected) + + e = [[1 + 1j, 2 - 3j], [3 + 1j, 4 + 0j]] + f = [[2 - 1j, 3 + 2j, 4 + 0j], [4 - 0j, 5 + 1j, 6 - 3j]] + expected = [[27 - 1j, 46. + 2j]] + + out = convolve2d(e, f, 'valid') + assert_array_equal(out, expected) + + # See gh-5897 + out = convolve2d(f, e, 'valid') + assert_array_equal(out, expected) + + def test_consistency_convolve_funcs(self): + # Compare np.convolve, signal.convolve, signal.convolve2d + a = np.arange(5) + b = np.array([3.2, 1.4, 3]) + for mode in ['full', 'valid', 'same']: + assert_almost_equal(np.convolve(a, b, mode=mode), + signal.convolve(a, b, mode=mode)) + assert_almost_equal(np.squeeze( + signal.convolve2d([a], [b], mode=mode)), + signal.convolve(a, b, mode=mode)) + + def test_invalid_dims(self): + assert_raises(ValueError, convolve2d, 3, 4) + assert_raises(ValueError, convolve2d, [3], [4]) + assert_raises(ValueError, convolve2d, [[[3]]], [[[4]]]) + + @pytest.mark.slow + @pytest.mark.xfail_on_32bit("Can't create large array for test") + def test_large_array(self): + # Test indexing doesn't overflow an int (gh-10761) + n = 2**31 // (1000 * np.int64().itemsize) + _testutils.check_free_memory(2 * n * 1001 * np.int64().itemsize / 1e6) + + # Create a chequered pattern of 1s and 0s + a = np.zeros(1001 * n, dtype=np.int64) + a[::2] = 1 + a = np.lib.stride_tricks.as_strided(a, shape=(n, 1000), strides=(8008, 8)) + + count = signal.convolve2d(a, [[1, 1]]) + fails = np.where(count > 1) + assert fails[0].size == 0 + + +class TestFFTConvolve: + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_real(self, axes): + a = array([1, 2, 3]) + expected = array([1, 4, 10, 12, 9.]) + + if axes == '': + out = fftconvolve(a, a) + else: + out = fftconvolve(a, a, axes=axes) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [1, [1], -1, [-1]]) + def test_real_axes(self, axes): + a = array([1, 2, 3]) + expected = array([1, 4, 10, 12, 9.]) + + a = np.tile(a, [2, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_complex(self, axes): + a = array([1 + 1j, 2 + 2j, 3 + 3j]) + expected = array([0 + 2j, 0 + 8j, 0 + 20j, 0 + 24j, 0 + 18j]) + + if axes == '': + out = fftconvolve(a, a) + else: + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [1, [1], -1, [-1]]) + def test_complex_axes(self, axes): + a = array([1 + 1j, 2 + 2j, 3 + 3j]) + expected = array([0 + 2j, 0 + 8j, 0 + 20j, 0 + 24j, 0 + 18j]) + + a = np.tile(a, [2, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', ['', + None, + [0, 1], + [1, 0], + [0, -1], + [-1, 0], + [-2, 1], + [1, -2], + [-2, -1], + [-1, -2]]) + def test_2d_real_same(self, axes): + a = array([[1, 2, 3], + [4, 5, 6]]) + expected = array([[1, 4, 10, 12, 9], + [8, 26, 56, 54, 36], + [16, 40, 73, 60, 36]]) + + if axes == '': + out = fftconvolve(a, a) + else: + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [[1, 2], + [2, 1], + [1, -1], + [-1, 1], + [-2, 2], + [2, -2], + [-2, -1], + [-1, -2]]) + def test_2d_real_same_axes(self, axes): + a = array([[1, 2, 3], + [4, 5, 6]]) + expected = array([[1, 4, 10, 12, 9], + [8, 26, 56, 54, 36], + [16, 40, 73, 60, 36]]) + + a = np.tile(a, [2, 1, 1]) + expected = np.tile(expected, [2, 1, 1]) + + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', ['', + None, + [0, 1], + [1, 0], + [0, -1], + [-1, 0], + [-2, 1], + [1, -2], + [-2, -1], + [-1, -2]]) + def test_2d_complex_same(self, axes): + a = array([[1 + 2j, 3 + 4j, 5 + 6j], + [2 + 1j, 4 + 3j, 6 + 5j]]) + expected = array([ + [-3 + 4j, -10 + 20j, -21 + 56j, -18 + 76j, -11 + 60j], + [10j, 44j, 118j, 156j, 122j], + [3 + 4j, 10 + 20j, 21 + 56j, 18 + 76j, 11 + 60j] + ]) + + if axes == '': + out = fftconvolve(a, a) + else: + out = fftconvolve(a, a, axes=axes) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [[1, 2], + [2, 1], + [1, -1], + [-1, 1], + [-2, 2], + [2, -2], + [-2, -1], + [-1, -2]]) + def test_2d_complex_same_axes(self, axes): + a = array([[1 + 2j, 3 + 4j, 5 + 6j], + [2 + 1j, 4 + 3j, 6 + 5j]]) + expected = array([ + [-3 + 4j, -10 + 20j, -21 + 56j, -18 + 76j, -11 + 60j], + [10j, 44j, 118j, 156j, 122j], + [3 + 4j, 10 + 20j, 21 + 56j, 18 + 76j, 11 + 60j] + ]) + + a = np.tile(a, [2, 1, 1]) + expected = np.tile(expected, [2, 1, 1]) + + out = fftconvolve(a, a, axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_real_same_mode(self, axes): + a = array([1, 2, 3]) + b = array([3, 3, 5, 6, 8, 7, 9, 0, 1]) + expected_1 = array([35., 41., 47.]) + expected_2 = array([9., 20., 25., 35., 41., 47., 39., 28., 2.]) + + if axes == '': + out = fftconvolve(a, b, 'same') + else: + out = fftconvolve(a, b, 'same', axes=axes) + assert_array_almost_equal(out, expected_1) + + if axes == '': + out = fftconvolve(b, a, 'same') + else: + out = fftconvolve(b, a, 'same', axes=axes) + assert_array_almost_equal(out, expected_2) + + @pytest.mark.parametrize('axes', [1, -1, [1], [-1]]) + def test_real_same_mode_axes(self, axes): + a = array([1, 2, 3]) + b = array([3, 3, 5, 6, 8, 7, 9, 0, 1]) + expected_1 = array([35., 41., 47.]) + expected_2 = array([9., 20., 25., 35., 41., 47., 39., 28., 2.]) + + a = np.tile(a, [2, 1]) + b = np.tile(b, [2, 1]) + expected_1 = np.tile(expected_1, [2, 1]) + expected_2 = np.tile(expected_2, [2, 1]) + + out = fftconvolve(a, b, 'same', axes=axes) + assert_array_almost_equal(out, expected_1) + + out = fftconvolve(b, a, 'same', axes=axes) + assert_array_almost_equal(out, expected_2) + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_valid_mode_real(self, axes): + # See gh-5897 + a = array([3, 2, 1]) + b = array([3, 3, 5, 6, 8, 7, 9, 0, 1]) + expected = array([24., 31., 41., 43., 49., 25., 12.]) + + if axes == '': + out = fftconvolve(a, b, 'valid') + else: + out = fftconvolve(a, b, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + if axes == '': + out = fftconvolve(b, a, 'valid') + else: + out = fftconvolve(b, a, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [1, [1]]) + def test_valid_mode_real_axes(self, axes): + # See gh-5897 + a = array([3, 2, 1]) + b = array([3, 3, 5, 6, 8, 7, 9, 0, 1]) + expected = array([24., 31., 41., 43., 49., 25., 12.]) + + a = np.tile(a, [2, 1]) + b = np.tile(b, [2, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, b, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_valid_mode_complex(self, axes): + a = array([3 - 1j, 2 + 7j, 1 + 0j]) + b = array([3 + 2j, 3 - 3j, 5 + 0j, 6 - 1j, 8 + 0j]) + expected = array([45. + 12.j, 30. + 23.j, 48 + 32.j]) + + if axes == '': + out = fftconvolve(a, b, 'valid') + else: + out = fftconvolve(a, b, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + if axes == '': + out = fftconvolve(b, a, 'valid') + else: + out = fftconvolve(b, a, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [1, [1], -1, [-1]]) + def test_valid_mode_complex_axes(self, axes): + a = array([3 - 1j, 2 + 7j, 1 + 0j]) + b = array([3 + 2j, 3 - 3j, 5 + 0j, 6 - 1j, 8 + 0j]) + expected = array([45. + 12.j, 30. + 23.j, 48 + 32.j]) + + a = np.tile(a, [2, 1]) + b = np.tile(b, [2, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, b, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + out = fftconvolve(b, a, 'valid', axes=axes) + assert_array_almost_equal(out, expected) + + def test_valid_mode_ignore_nonaxes(self): + # See gh-5897 + a = array([3, 2, 1]) + b = array([3, 3, 5, 6, 8, 7, 9, 0, 1]) + expected = array([24., 31., 41., 43., 49., 25., 12.]) + + a = np.tile(a, [2, 1]) + b = np.tile(b, [1, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, b, 'valid', axes=1) + assert_array_almost_equal(out, expected) + + def test_empty(self): + # Regression test for #1745: crashes with 0-length input. + assert_(fftconvolve([], []).size == 0) + assert_(fftconvolve([5, 6], []).size == 0) + assert_(fftconvolve([], [7]).size == 0) + + def test_zero_rank(self): + a = array(4967) + b = array(3920) + out = fftconvolve(a, b) + assert_equal(out, a * b) + + def test_single_element(self): + a = array([4967]) + b = array([3920]) + out = fftconvolve(a, b) + assert_equal(out, a * b) + + @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]]) + def test_random_data(self, axes): + np.random.seed(1234) + a = np.random.rand(1233) + 1j * np.random.rand(1233) + b = np.random.rand(1321) + 1j * np.random.rand(1321) + expected = np.convolve(a, b, 'full') + + if axes == '': + out = fftconvolve(a, b, 'full') + else: + out = fftconvolve(a, b, 'full', axes=axes) + assert_(np.allclose(out, expected, rtol=1e-10)) + + @pytest.mark.parametrize('axes', [1, [1], -1, [-1]]) + def test_random_data_axes(self, axes): + np.random.seed(1234) + a = np.random.rand(1233) + 1j * np.random.rand(1233) + b = np.random.rand(1321) + 1j * np.random.rand(1321) + expected = np.convolve(a, b, 'full') + + a = np.tile(a, [2, 1]) + b = np.tile(b, [2, 1]) + expected = np.tile(expected, [2, 1]) + + out = fftconvolve(a, b, 'full', axes=axes) + assert_(np.allclose(out, expected, rtol=1e-10)) + + @pytest.mark.parametrize('axes', [[1, 4], + [4, 1], + [1, -1], + [-1, 1], + [-4, 4], + [4, -4], + [-4, -1], + [-1, -4]]) + def test_random_data_multidim_axes(self, axes): + a_shape, b_shape = (123, 22), (132, 11) + np.random.seed(1234) + a = np.random.rand(*a_shape) + 1j * np.random.rand(*a_shape) + b = np.random.rand(*b_shape) + 1j * np.random.rand(*b_shape) + expected = convolve2d(a, b, 'full') + + a = a[:, :, None, None, None] + b = b[:, :, None, None, None] + expected = expected[:, :, None, None, None] + + a = np.moveaxis(a.swapaxes(0, 2), 1, 4) + b = np.moveaxis(b.swapaxes(0, 2), 1, 4) + expected = np.moveaxis(expected.swapaxes(0, 2), 1, 4) + + # use 1 for dimension 2 in a and 3 in b to test broadcasting + a = np.tile(a, [2, 1, 3, 1, 1]) + b = np.tile(b, [2, 1, 1, 4, 1]) + expected = np.tile(expected, [2, 1, 3, 4, 1]) + + out = fftconvolve(a, b, 'full', axes=axes) + assert_allclose(out, expected, rtol=1e-10, atol=1e-10) + + @pytest.mark.slow + @pytest.mark.parametrize( + 'n', + list(range(1, 100)) + + list(range(1000, 1500)) + + np.random.RandomState(1234).randint(1001, 10000, 5).tolist()) + def test_many_sizes(self, n): + a = np.random.rand(n) + 1j * np.random.rand(n) + b = np.random.rand(n) + 1j * np.random.rand(n) + expected = np.convolve(a, b, 'full') + + out = fftconvolve(a, b, 'full') + assert_allclose(out, expected, atol=1e-10) + + out = fftconvolve(a, b, 'full', axes=[0]) + assert_allclose(out, expected, atol=1e-10) + + def test_fft_nan(self): + n = 1000 + rng = np.random.default_rng(43876432987) + sig_nan = rng.standard_normal(n) + + for val in [np.nan, np.inf]: + sig_nan[100] = val + coeffs = signal.firwin(200, 0.2) + + msg = "Use of fft convolution.*|invalid value encountered.*" + with pytest.warns(RuntimeWarning, match=msg): + signal.convolve(sig_nan, coeffs, mode='same', method='fft') + +def fftconvolve_err(*args, **kwargs): + raise RuntimeError('Fell back to fftconvolve') + + +def gen_oa_shapes(sizes): + return [(a, b) for a, b in product(sizes, repeat=2) + if abs(a - b) > 3] + + +def gen_oa_shapes_2d(sizes): + shapes0 = gen_oa_shapes(sizes) + shapes1 = gen_oa_shapes(sizes) + shapes = [ishapes0+ishapes1 for ishapes0, ishapes1 in + zip(shapes0, shapes1)] + + modes = ['full', 'valid', 'same'] + return [ishapes+(imode,) for ishapes, imode in product(shapes, modes) + if imode != 'valid' or + (ishapes[0] > ishapes[1] and ishapes[2] > ishapes[3]) or + (ishapes[0] < ishapes[1] and ishapes[2] < ishapes[3])] + + +def gen_oa_shapes_eq(sizes): + return [(a, b) for a, b in product(sizes, repeat=2) + if a >= b] + + +class TestOAConvolve: + @pytest.mark.slow() + @pytest.mark.parametrize('shape_a_0, shape_b_0', + gen_oa_shapes_eq(list(range(100)) + + list(range(100, 1000, 23))) + ) + def test_real_manylens(self, shape_a_0, shape_b_0): + a = np.random.rand(shape_a_0) + b = np.random.rand(shape_b_0) + + expected = fftconvolve(a, b) + out = oaconvolve(a, b) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('shape_a_0, shape_b_0', + gen_oa_shapes([50, 47, 6, 4, 1])) + @pytest.mark.parametrize('is_complex', [True, False]) + @pytest.mark.parametrize('mode', ['full', 'valid', 'same']) + def test_1d_noaxes(self, shape_a_0, shape_b_0, + is_complex, mode, monkeypatch): + a = np.random.rand(shape_a_0) + b = np.random.rand(shape_b_0) + if is_complex: + a = a + 1j*np.random.rand(shape_a_0) + b = b + 1j*np.random.rand(shape_b_0) + + expected = fftconvolve(a, b, mode=mode) + + monkeypatch.setattr(signal._signaltools, 'fftconvolve', + fftconvolve_err) + out = oaconvolve(a, b, mode=mode) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [0, 1]) + @pytest.mark.parametrize('shape_a_0, shape_b_0', + gen_oa_shapes([50, 47, 6, 4])) + @pytest.mark.parametrize('shape_a_extra', [1, 3]) + @pytest.mark.parametrize('shape_b_extra', [1, 3]) + @pytest.mark.parametrize('is_complex', [True, False]) + @pytest.mark.parametrize('mode', ['full', 'valid', 'same']) + def test_1d_axes(self, axes, shape_a_0, shape_b_0, + shape_a_extra, shape_b_extra, + is_complex, mode, monkeypatch): + ax_a = [shape_a_extra]*2 + ax_b = [shape_b_extra]*2 + ax_a[axes] = shape_a_0 + ax_b[axes] = shape_b_0 + + a = np.random.rand(*ax_a) + b = np.random.rand(*ax_b) + if is_complex: + a = a + 1j*np.random.rand(*ax_a) + b = b + 1j*np.random.rand(*ax_b) + + expected = fftconvolve(a, b, mode=mode, axes=axes) + + monkeypatch.setattr(signal._signaltools, 'fftconvolve', + fftconvolve_err) + out = oaconvolve(a, b, mode=mode, axes=axes) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('shape_a_0, shape_b_0, ' + 'shape_a_1, shape_b_1, mode', + gen_oa_shapes_2d([50, 47, 6, 4])) + @pytest.mark.parametrize('is_complex', [True, False]) + def test_2d_noaxes(self, shape_a_0, shape_b_0, + shape_a_1, shape_b_1, mode, + is_complex, monkeypatch): + a = np.random.rand(shape_a_0, shape_a_1) + b = np.random.rand(shape_b_0, shape_b_1) + if is_complex: + a = a + 1j*np.random.rand(shape_a_0, shape_a_1) + b = b + 1j*np.random.rand(shape_b_0, shape_b_1) + + expected = fftconvolve(a, b, mode=mode) + + monkeypatch.setattr(signal._signaltools, 'fftconvolve', + fftconvolve_err) + out = oaconvolve(a, b, mode=mode) + + assert_array_almost_equal(out, expected) + + @pytest.mark.parametrize('axes', [[0, 1], [0, 2], [1, 2]]) + @pytest.mark.parametrize('shape_a_0, shape_b_0, ' + 'shape_a_1, shape_b_1, mode', + gen_oa_shapes_2d([50, 47, 6, 4])) + @pytest.mark.parametrize('shape_a_extra', [1, 3]) + @pytest.mark.parametrize('shape_b_extra', [1, 3]) + @pytest.mark.parametrize('is_complex', [True, False]) + def test_2d_axes(self, axes, shape_a_0, shape_b_0, + shape_a_1, shape_b_1, mode, + shape_a_extra, shape_b_extra, + is_complex, monkeypatch): + ax_a = [shape_a_extra]*3 + ax_b = [shape_b_extra]*3 + ax_a[axes[0]] = shape_a_0 + ax_b[axes[0]] = shape_b_0 + ax_a[axes[1]] = shape_a_1 + ax_b[axes[1]] = shape_b_1 + + a = np.random.rand(*ax_a) + b = np.random.rand(*ax_b) + if is_complex: + a = a + 1j*np.random.rand(*ax_a) + b = b + 1j*np.random.rand(*ax_b) + + expected = fftconvolve(a, b, mode=mode, axes=axes) + + monkeypatch.setattr(signal._signaltools, 'fftconvolve', + fftconvolve_err) + out = oaconvolve(a, b, mode=mode, axes=axes) + + assert_array_almost_equal(out, expected) + + def test_empty(self): + # Regression test for #1745: crashes with 0-length input. + assert_(oaconvolve([], []).size == 0) + assert_(oaconvolve([5, 6], []).size == 0) + assert_(oaconvolve([], [7]).size == 0) + + def test_zero_rank(self): + a = array(4967) + b = array(3920) + out = oaconvolve(a, b) + assert_equal(out, a * b) + + def test_single_element(self): + a = array([4967]) + b = array([3920]) + out = oaconvolve(a, b) + assert_equal(out, a * b) + + +class TestAllFreqConvolves: + + @pytest.mark.parametrize('convapproach', + [fftconvolve, oaconvolve]) + def test_invalid_shapes(self, convapproach): + a = np.arange(1, 7).reshape((2, 3)) + b = np.arange(-6, 0).reshape((3, 2)) + with assert_raises(ValueError, + match="For 'valid' mode, one must be at least " + "as large as the other in every dimension"): + convapproach(a, b, mode='valid') + + @pytest.mark.parametrize('convapproach', + [fftconvolve, oaconvolve]) + def test_invalid_shapes_axes(self, convapproach): + a = np.zeros([5, 6, 2, 1]) + b = np.zeros([5, 6, 3, 1]) + with assert_raises(ValueError, + match=r"incompatible shapes for in1 and in2:" + r" \(5L?, 6L?, 2L?, 1L?\) and" + r" \(5L?, 6L?, 3L?, 1L?\)"): + convapproach(a, b, axes=[0, 1]) + + @pytest.mark.parametrize('a,b', + [([1], 2), + (1, [2]), + ([3], [[2]])]) + @pytest.mark.parametrize('convapproach', + [fftconvolve, oaconvolve]) + def test_mismatched_dims(self, a, b, convapproach): + with assert_raises(ValueError, + match="in1 and in2 should have the same" + " dimensionality"): + convapproach(a, b) + + @pytest.mark.parametrize('convapproach', + [fftconvolve, oaconvolve]) + def test_invalid_flags(self, convapproach): + with assert_raises(ValueError, + match="acceptable mode flags are 'valid'," + " 'same', or 'full'"): + convapproach([1], [2], mode='chips') + + with assert_raises(ValueError, + match="when provided, axes cannot be empty"): + convapproach([1], [2], axes=[]) + + with assert_raises(ValueError, match="axes must be a scalar or " + "iterable of integers"): + convapproach([1], [2], axes=[[1, 2], [3, 4]]) + + with assert_raises(ValueError, match="axes must be a scalar or " + "iterable of integers"): + convapproach([1], [2], axes=[1., 2., 3., 4.]) + + with assert_raises(ValueError, + match="axes exceeds dimensionality of input"): + convapproach([1], [2], axes=[1]) + + with assert_raises(ValueError, + match="axes exceeds dimensionality of input"): + convapproach([1], [2], axes=[-2]) + + with assert_raises(ValueError, + match="all axes must be unique"): + convapproach([1], [2], axes=[0, 0]) + + @pytest.mark.parametrize('dtype', [np.longdouble, np.clongdouble]) + def test_longdtype_input(self, dtype): + x = np.random.random((27, 27)).astype(dtype) + y = np.random.random((4, 4)).astype(dtype) + if np.iscomplexobj(dtype()): + x += .1j + y -= .1j + + res = fftconvolve(x, y) + assert_allclose(res, convolve(x, y, method='direct')) + assert res.dtype == dtype + + +class TestMedFilt: + + IN = [[50, 50, 50, 50, 50, 92, 18, 27, 65, 46], + [50, 50, 50, 50, 50, 0, 72, 77, 68, 66], + [50, 50, 50, 50, 50, 46, 47, 19, 64, 77], + [50, 50, 50, 50, 50, 42, 15, 29, 95, 35], + [50, 50, 50, 50, 50, 46, 34, 9, 21, 66], + [70, 97, 28, 68, 78, 77, 61, 58, 71, 42], + [64, 53, 44, 29, 68, 32, 19, 68, 24, 84], + [3, 33, 53, 67, 1, 78, 74, 55, 12, 83], + [7, 11, 46, 70, 60, 47, 24, 43, 61, 26], + [32, 61, 88, 7, 39, 4, 92, 64, 45, 61]] + + OUT = [[0, 50, 50, 50, 42, 15, 15, 18, 27, 0], + [0, 50, 50, 50, 50, 42, 19, 21, 29, 0], + [50, 50, 50, 50, 50, 47, 34, 34, 46, 35], + [50, 50, 50, 50, 50, 50, 42, 47, 64, 42], + [50, 50, 50, 50, 50, 50, 46, 55, 64, 35], + [33, 50, 50, 50, 50, 47, 46, 43, 55, 26], + [32, 50, 50, 50, 50, 47, 46, 45, 55, 26], + [7, 46, 50, 50, 47, 46, 46, 43, 45, 21], + [0, 32, 33, 39, 32, 32, 43, 43, 43, 0], + [0, 7, 11, 7, 4, 4, 19, 19, 24, 0]] + + KERNEL_SIZE = [7,3] + + def test_basic(self): + d = signal.medfilt(self.IN, self.KERNEL_SIZE) + e = signal.medfilt2d(np.array(self.IN, float), self.KERNEL_SIZE) + assert_array_equal(d, self.OUT) + assert_array_equal(d, e) + + @pytest.mark.parametrize('dtype', [np.ubyte, np.byte, np.ushort, np.short, + np_ulong, np_long, np.ulonglong, np.ulonglong, + np.float32, np.float64]) + def test_types(self, dtype): + # volume input and output types match + in_typed = np.array(self.IN, dtype=dtype) + assert_equal(signal.medfilt(in_typed).dtype, dtype) + assert_equal(signal.medfilt2d(in_typed).dtype, dtype) + + def test_types_deprecated(self): + dtype = np.longdouble + in_typed = np.array(self.IN, dtype=dtype) + msg = "Using medfilt with arrays of dtype" + with pytest.deprecated_call(match=msg): + assert_equal(signal.medfilt(in_typed).dtype, dtype) + with pytest.deprecated_call(match=msg): + assert_equal(signal.medfilt2d(in_typed).dtype, dtype) + + + @pytest.mark.parametrize('dtype', [np.bool_, np.complex64, np.complex128, + np.clongdouble, np.float16,]) + def test_invalid_dtypes(self, dtype): + in_typed = np.array(self.IN, dtype=dtype) + with pytest.raises(ValueError, match="not supported"): + signal.medfilt(in_typed) + + with pytest.raises(ValueError, match="not supported"): + signal.medfilt2d(in_typed) + + def test_none(self): + # gh-1651, trac #1124. Ensure this does not segfault. + msg = "kernel_size exceeds volume.*|Using medfilt with arrays of dtype.*" + with pytest.warns((UserWarning, DeprecationWarning), match=msg): + assert_raises(TypeError, signal.medfilt, None) + + def test_odd_strides(self): + # Avoid a regression with possible contiguous + # numpy arrays that have odd strides. The stride value below gets + # us into wrong memory if used (but it does not need to be used) + dummy = np.arange(10, dtype=np.float64) + a = dummy[5:6] + a.strides = 16 + assert_(signal.medfilt(a, 1) == 5.) + + def test_refcounting(self): + # Check a refcounting-related crash + a = Decimal(123) + x = np.array([a, a], dtype=object) + if hasattr(sys, 'getrefcount'): + n = 2 * sys.getrefcount(a) + else: + n = 10 + # Shouldn't segfault: + msg = "kernel_size exceeds volume.*|Using medfilt with arrays of dtype.*" + with pytest.warns((UserWarning, DeprecationWarning), match=msg): + for j in range(n): + signal.medfilt(x) + if hasattr(sys, 'getrefcount'): + assert_(sys.getrefcount(a) < n) + assert_equal(x, [a, a]) + + def test_object(self,): + msg = "Using medfilt with arrays of dtype" + with pytest.deprecated_call(match=msg): + in_object = np.array(self.IN, dtype=object) + out_object = np.array(self.OUT, dtype=object) + assert_array_equal(signal.medfilt(in_object, self.KERNEL_SIZE), + out_object) + + @pytest.mark.parametrize("dtype", [np.ubyte, np.float32, np.float64]) + def test_medfilt2d_parallel(self, dtype): + in_typed = np.array(self.IN, dtype=dtype) + expected = np.array(self.OUT, dtype=dtype) + + # This is used to simplify the indexing calculations. + assert in_typed.shape == expected.shape + + # We'll do the calculation in four chunks. M1 and N1 are the dimensions + # of the first output chunk. We have to extend the input by half the + # kernel size to be able to calculate the full output chunk. + M1 = expected.shape[0] // 2 + N1 = expected.shape[1] // 2 + offM = self.KERNEL_SIZE[0] // 2 + 1 + offN = self.KERNEL_SIZE[1] // 2 + 1 + + def apply(chunk): + # in = slice of in_typed to use. + # sel = slice of output to crop it to the correct region. + # out = slice of output array to store in. + M, N = chunk + if M == 0: + Min = slice(0, M1 + offM) + Msel = slice(0, -offM) + Mout = slice(0, M1) + else: + Min = slice(M1 - offM, None) + Msel = slice(offM, None) + Mout = slice(M1, None) + if N == 0: + Nin = slice(0, N1 + offN) + Nsel = slice(0, -offN) + Nout = slice(0, N1) + else: + Nin = slice(N1 - offN, None) + Nsel = slice(offN, None) + Nout = slice(N1, None) + + # Do the calculation, but do not write to the output in the threads. + chunk_data = in_typed[Min, Nin] + med = signal.medfilt2d(chunk_data, self.KERNEL_SIZE) + return med[Msel, Nsel], Mout, Nout + + # Give each chunk to a different thread. + output = np.zeros_like(expected) + with ThreadPoolExecutor(max_workers=4) as pool: + chunks = {(0, 0), (0, 1), (1, 0), (1, 1)} + futures = {pool.submit(apply, chunk) for chunk in chunks} + + # Store each result in the output as it arrives. + for future in as_completed(futures): + data, Mslice, Nslice = future.result() + output[Mslice, Nslice] = data + + assert_array_equal(output, expected) + + +class TestWiener: + + def test_basic(self): + g = array([[5, 6, 4, 3], + [3, 5, 6, 2], + [2, 3, 5, 6], + [1, 6, 9, 7]], 'd') + h = array([[2.16374269, 3.2222222222, 2.8888888889, 1.6666666667], + [2.666666667, 4.33333333333, 4.44444444444, 2.8888888888], + [2.222222222, 4.4444444444, 5.4444444444, 4.801066874837], + [1.33333333333, 3.92735042735, 6.0712560386, 5.0404040404]]) + assert_array_almost_equal(signal.wiener(g), h, decimal=6) + assert_array_almost_equal(signal.wiener(g, mysize=3), h, decimal=6) + + +padtype_options = ["mean", "median", "minimum", "maximum", "line"] +padtype_options += _upfirdn_modes + + +class TestResample: + def test_basic(self): + # Some basic tests + + # Regression test for issue #3603. + # window.shape must equal to sig.shape[0] + sig = np.arange(128) + num = 256 + win = signal.get_window(('kaiser', 8.0), 160) + assert_raises(ValueError, signal.resample, sig, num, window=win) + + # Other degenerate conditions + assert_raises(ValueError, signal.resample_poly, sig, 'yo', 1) + assert_raises(ValueError, signal.resample_poly, sig, 1, 0) + assert_raises(ValueError, signal.resample_poly, sig, 2, 1, padtype='') + assert_raises(ValueError, signal.resample_poly, sig, 2, 1, + padtype='mean', cval=10) + + # test for issue #6505 - should not modify window.shape when axis ≠ 0 + sig2 = np.tile(np.arange(160), (2, 1)) + signal.resample(sig2, num, axis=-1, window=win) + assert_(win.shape == (160,)) + + @pytest.mark.parametrize('window', (None, 'hamming')) + @pytest.mark.parametrize('N', (20, 19)) + @pytest.mark.parametrize('num', (100, 101, 10, 11)) + def test_rfft(self, N, num, window): + # Make sure the speed up using rfft gives the same result as the normal + # way using fft + x = np.linspace(0, 10, N, endpoint=False) + y = np.cos(-x**2/6.0) + assert_allclose(signal.resample(y, num, window=window), + signal.resample(y + 0j, num, window=window).real) + + y = np.array([np.cos(-x**2/6.0), np.sin(-x**2/6.0)]) + y_complex = y + 0j + assert_allclose( + signal.resample(y, num, axis=1, window=window), + signal.resample(y_complex, num, axis=1, window=window).real, + atol=1e-9) + + def test_input_domain(self): + # Test if both input domain modes produce the same results. + tsig = np.arange(256) + 0j + fsig = fft(tsig) + num = 256 + assert_allclose( + signal.resample(fsig, num, domain='freq'), + signal.resample(tsig, num, domain='time'), + atol=1e-9) + + @pytest.mark.parametrize('nx', (1, 2, 3, 5, 8)) + @pytest.mark.parametrize('ny', (1, 2, 3, 5, 8)) + @pytest.mark.parametrize('dtype', ('float', 'complex')) + def test_dc(self, nx, ny, dtype): + x = np.array([1] * nx, dtype) + y = signal.resample(x, ny) + assert_allclose(y, [1] * ny) + + @pytest.mark.parametrize('padtype', padtype_options) + def test_mutable_window(self, padtype): + # Test that a mutable window is not modified + impulse = np.zeros(3) + window = np.random.RandomState(0).randn(2) + window_orig = window.copy() + signal.resample_poly(impulse, 5, 1, window=window, padtype=padtype) + assert_array_equal(window, window_orig) + + @pytest.mark.parametrize('padtype', padtype_options) + def test_output_float32(self, padtype): + # Test that float32 inputs yield a float32 output + x = np.arange(10, dtype=np.float32) + h = np.array([1, 1, 1], dtype=np.float32) + y = signal.resample_poly(x, 1, 2, window=h, padtype=padtype) + assert y.dtype == np.float32 + + @pytest.mark.parametrize('padtype', padtype_options) + @pytest.mark.parametrize('dtype', [np.float32, np.float64]) + def test_output_match_dtype(self, padtype, dtype): + # Test that the dtype of x is preserved per issue #14733 + x = np.arange(10, dtype=dtype) + y = signal.resample_poly(x, 1, 2, padtype=padtype) + assert y.dtype == x.dtype + + @pytest.mark.parametrize( + "method, ext, padtype", + [("fft", False, None)] + + list( + product( + ["polyphase"], [False, True], padtype_options, + ) + ), + ) + def test_resample_methods(self, method, ext, padtype): + # Test resampling of sinusoids and random noise (1-sec) + rate = 100 + rates_to = [49, 50, 51, 99, 100, 101, 199, 200, 201] + + # Sinusoids, windowed to avoid edge artifacts + t = np.arange(rate) / float(rate) + freqs = np.array((1., 10., 40.))[:, np.newaxis] + x = np.sin(2 * np.pi * freqs * t) * hann(rate) + + for rate_to in rates_to: + t_to = np.arange(rate_to) / float(rate_to) + y_tos = np.sin(2 * np.pi * freqs * t_to) * hann(rate_to) + if method == 'fft': + y_resamps = signal.resample(x, rate_to, axis=-1) + else: + if ext and rate_to != rate: + # Match default window design + g = gcd(rate_to, rate) + up = rate_to // g + down = rate // g + max_rate = max(up, down) + f_c = 1. / max_rate + half_len = 10 * max_rate + window = signal.firwin(2 * half_len + 1, f_c, + window=('kaiser', 5.0)) + polyargs = {'window': window, 'padtype': padtype} + else: + polyargs = {'padtype': padtype} + + y_resamps = signal.resample_poly(x, rate_to, rate, axis=-1, + **polyargs) + + for y_to, y_resamp, freq in zip(y_tos, y_resamps, freqs): + if freq >= 0.5 * rate_to: + y_to.fill(0.) # mostly low-passed away + if padtype in ['minimum', 'maximum']: + assert_allclose(y_resamp, y_to, atol=3e-1) + else: + assert_allclose(y_resamp, y_to, atol=1e-3) + else: + assert_array_equal(y_to.shape, y_resamp.shape) + corr = np.corrcoef(y_to, y_resamp)[0, 1] + assert_(corr > 0.99, msg=(corr, rate, rate_to)) + + # Random data + rng = np.random.RandomState(0) + x = hann(rate) * np.cumsum(rng.randn(rate)) # low-pass, wind + for rate_to in rates_to: + # random data + t_to = np.arange(rate_to) / float(rate_to) + y_to = np.interp(t_to, t, x) + if method == 'fft': + y_resamp = signal.resample(x, rate_to) + else: + y_resamp = signal.resample_poly(x, rate_to, rate, + padtype=padtype) + assert_array_equal(y_to.shape, y_resamp.shape) + corr = np.corrcoef(y_to, y_resamp)[0, 1] + assert_(corr > 0.99, msg=corr) + + # More tests of fft method (Master 0.18.1 fails these) + if method == 'fft': + x1 = np.array([1.+0.j, 0.+0.j]) + y1_test = signal.resample(x1, 4) + # upsampling a complex array + y1_true = np.array([1.+0.j, 0.5+0.j, 0.+0.j, 0.5+0.j]) + assert_allclose(y1_test, y1_true, atol=1e-12) + x2 = np.array([1., 0.5, 0., 0.5]) + y2_test = signal.resample(x2, 2) # downsampling a real array + y2_true = np.array([1., 0.]) + assert_allclose(y2_test, y2_true, atol=1e-12) + + def test_poly_vs_filtfilt(self): + # Check that up=1.0 gives same answer as filtfilt + slicing + random_state = np.random.RandomState(17) + try_types = (int, np.float32, np.complex64, float, complex) + size = 10000 + down_factors = [2, 11, 79] + + for dtype in try_types: + x = random_state.randn(size).astype(dtype) + if dtype in (np.complex64, np.complex128): + x += 1j * random_state.randn(size) + + # resample_poly assumes zeros outside of signl, whereas filtfilt + # can only constant-pad. Make them equivalent: + x[0] = 0 + x[-1] = 0 + + for down in down_factors: + h = signal.firwin(31, 1. / down, window='hamming') + yf = filtfilt(h, 1.0, x, padtype='constant')[::down] + + # Need to pass convolved version of filter to resample_poly, + # since filtfilt does forward and backward, but resample_poly + # only goes forward + hc = convolve(h, h[::-1]) + y = signal.resample_poly(x, 1, down, window=hc) + assert_allclose(yf, y, atol=1e-7, rtol=1e-7) + + def test_correlate1d(self): + for down in [2, 4]: + for nx in range(1, 40, down): + for nweights in (32, 33): + x = np.random.random((nx,)) + weights = np.random.random((nweights,)) + y_g = correlate1d(x, weights[::-1], mode='constant') + y_s = signal.resample_poly( + x, up=1, down=down, window=weights) + assert_allclose(y_g[::down], y_s) + + +class TestCSpline1DEval: + + def test_basic(self): + y = array([1, 2, 3, 4, 3, 2, 1, 2, 3.0]) + x = arange(len(y)) + dx = x[1] - x[0] + cj = signal.cspline1d(y) + + x2 = arange(len(y) * 10.0) / 10.0 + y2 = signal.cspline1d_eval(cj, x2, dx=dx, x0=x[0]) + + # make sure interpolated values are on knot points + assert_array_almost_equal(y2[::10], y, decimal=5) + + def test_complex(self): + # create some smoothly varying complex signal to interpolate + x = np.arange(2) + y = np.zeros(x.shape, dtype=np.complex64) + T = 10.0 + f = 1.0 / T + y = np.exp(2.0J * np.pi * f * x) + + # get the cspline transform + cy = signal.cspline1d(y) + + # determine new test x value and interpolate + xnew = np.array([0.5]) + ynew = signal.cspline1d_eval(cy, xnew) + + assert_equal(ynew.dtype, y.dtype) + +class TestOrderFilt: + + def test_basic(self): + assert_array_equal(signal.order_filter([1, 2, 3], [1, 0, 1], 1), + [2, 3, 2]) + + +class _TestLinearFilter: + + def generate(self, shape): + x = np.linspace(0, np.prod(shape) - 1, np.prod(shape)).reshape(shape) + return self.convert_dtype(x) + + def convert_dtype(self, arr): + if self.dtype == np.dtype('O'): + arr = np.asarray(arr) + out = np.empty(arr.shape, self.dtype) + iter = np.nditer([arr, out], ['refs_ok','zerosize_ok'], + [['readonly'],['writeonly']]) + for x, y in iter: + y[...] = self.type(x[()]) + return out + else: + return np.asarray(arr, dtype=self.dtype) + + def test_rank_1_IIR(self): + x = self.generate((6,)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, -0.5]) + y_r = self.convert_dtype([0, 2, 4, 6, 8, 10.]) + assert_array_almost_equal(lfilter(b, a, x), y_r) + + def test_rank_1_FIR(self): + x = self.generate((6,)) + b = self.convert_dtype([1, 1]) + a = self.convert_dtype([1]) + y_r = self.convert_dtype([0, 1, 3, 5, 7, 9.]) + assert_array_almost_equal(lfilter(b, a, x), y_r) + + def test_rank_1_IIR_init_cond(self): + x = self.generate((6,)) + b = self.convert_dtype([1, 0, -1]) + a = self.convert_dtype([0.5, -0.5]) + zi = self.convert_dtype([1, 2]) + y_r = self.convert_dtype([1, 5, 9, 13, 17, 21]) + zf_r = self.convert_dtype([13, -10]) + y, zf = lfilter(b, a, x, zi=zi) + assert_array_almost_equal(y, y_r) + assert_array_almost_equal(zf, zf_r) + + def test_rank_1_FIR_init_cond(self): + x = self.generate((6,)) + b = self.convert_dtype([1, 1, 1]) + a = self.convert_dtype([1]) + zi = self.convert_dtype([1, 1]) + y_r = self.convert_dtype([1, 2, 3, 6, 9, 12.]) + zf_r = self.convert_dtype([9, 5]) + y, zf = lfilter(b, a, x, zi=zi) + assert_array_almost_equal(y, y_r) + assert_array_almost_equal(zf, zf_r) + + def test_rank_2_IIR_axis_0(self): + x = self.generate((4, 3)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + y_r2_a0 = self.convert_dtype([[0, 2, 4], [6, 4, 2], [0, 2, 4], + [6, 4, 2]]) + y = lfilter(b, a, x, axis=0) + assert_array_almost_equal(y_r2_a0, y) + + def test_rank_2_IIR_axis_1(self): + x = self.generate((4, 3)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + y_r2_a1 = self.convert_dtype([[0, 2, 0], [6, -4, 6], [12, -10, 12], + [18, -16, 18]]) + y = lfilter(b, a, x, axis=1) + assert_array_almost_equal(y_r2_a1, y) + + def test_rank_2_IIR_axis_0_init_cond(self): + x = self.generate((4, 3)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + zi = self.convert_dtype(np.ones((4,1))) + + y_r2_a0_1 = self.convert_dtype([[1, 1, 1], [7, -5, 7], [13, -11, 13], + [19, -17, 19]]) + zf_r = self.convert_dtype([-5, -17, -29, -41])[:, np.newaxis] + y, zf = lfilter(b, a, x, axis=1, zi=zi) + assert_array_almost_equal(y_r2_a0_1, y) + assert_array_almost_equal(zf, zf_r) + + def test_rank_2_IIR_axis_1_init_cond(self): + x = self.generate((4,3)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + zi = self.convert_dtype(np.ones((1,3))) + + y_r2_a0_0 = self.convert_dtype([[1, 3, 5], [5, 3, 1], + [1, 3, 5], [5, 3, 1]]) + zf_r = self.convert_dtype([[-23, -23, -23]]) + y, zf = lfilter(b, a, x, axis=0, zi=zi) + assert_array_almost_equal(y_r2_a0_0, y) + assert_array_almost_equal(zf, zf_r) + + def test_rank_3_IIR(self): + x = self.generate((4, 3, 2)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + + for axis in range(x.ndim): + y = lfilter(b, a, x, axis) + y_r = np.apply_along_axis(lambda w: lfilter(b, a, w), axis, x) + assert_array_almost_equal(y, y_r) + + def test_rank_3_IIR_init_cond(self): + x = self.generate((4, 3, 2)) + b = self.convert_dtype([1, -1]) + a = self.convert_dtype([0.5, 0.5]) + + for axis in range(x.ndim): + zi_shape = list(x.shape) + zi_shape[axis] = 1 + zi = self.convert_dtype(np.ones(zi_shape)) + zi1 = self.convert_dtype([1]) + y, zf = lfilter(b, a, x, axis, zi) + def lf0(w): + return lfilter(b, a, w, zi=zi1)[0] + def lf1(w): + return lfilter(b, a, w, zi=zi1)[1] + y_r = np.apply_along_axis(lf0, axis, x) + zf_r = np.apply_along_axis(lf1, axis, x) + assert_array_almost_equal(y, y_r) + assert_array_almost_equal(zf, zf_r) + + def test_rank_3_FIR(self): + x = self.generate((4, 3, 2)) + b = self.convert_dtype([1, 0, -1]) + a = self.convert_dtype([1]) + + for axis in range(x.ndim): + y = lfilter(b, a, x, axis) + y_r = np.apply_along_axis(lambda w: lfilter(b, a, w), axis, x) + assert_array_almost_equal(y, y_r) + + def test_rank_3_FIR_init_cond(self): + x = self.generate((4, 3, 2)) + b = self.convert_dtype([1, 0, -1]) + a = self.convert_dtype([1]) + + for axis in range(x.ndim): + zi_shape = list(x.shape) + zi_shape[axis] = 2 + zi = self.convert_dtype(np.ones(zi_shape)) + zi1 = self.convert_dtype([1, 1]) + y, zf = lfilter(b, a, x, axis, zi) + def lf0(w): + return lfilter(b, a, w, zi=zi1)[0] + def lf1(w): + return lfilter(b, a, w, zi=zi1)[1] + y_r = np.apply_along_axis(lf0, axis, x) + zf_r = np.apply_along_axis(lf1, axis, x) + assert_array_almost_equal(y, y_r) + assert_array_almost_equal(zf, zf_r) + + def test_zi_pseudobroadcast(self): + x = self.generate((4, 5, 20)) + b,a = signal.butter(8, 0.2, output='ba') + b = self.convert_dtype(b) + a = self.convert_dtype(a) + zi_size = b.shape[0] - 1 + + # lfilter requires x.ndim == zi.ndim exactly. However, zi can have + # length 1 dimensions. + zi_full = self.convert_dtype(np.ones((4, 5, zi_size))) + zi_sing = self.convert_dtype(np.ones((1, 1, zi_size))) + + y_full, zf_full = lfilter(b, a, x, zi=zi_full) + y_sing, zf_sing = lfilter(b, a, x, zi=zi_sing) + + assert_array_almost_equal(y_sing, y_full) + assert_array_almost_equal(zf_full, zf_sing) + + # lfilter does not prepend ones + assert_raises(ValueError, lfilter, b, a, x, -1, np.ones(zi_size)) + + def test_scalar_a(self): + # a can be a scalar. + x = self.generate(6) + b = self.convert_dtype([1, 0, -1]) + a = self.convert_dtype([1]) + y_r = self.convert_dtype([0, 1, 2, 2, 2, 2]) + + y = lfilter(b, a[0], x) + assert_array_almost_equal(y, y_r) + + def test_zi_some_singleton_dims(self): + # lfilter doesn't really broadcast (no prepending of 1's). But does + # do singleton expansion if x and zi have the same ndim. This was + # broken only if a subset of the axes were singletons (gh-4681). + x = self.convert_dtype(np.zeros((3,2,5), 'l')) + b = self.convert_dtype(np.ones(5, 'l')) + a = self.convert_dtype(np.array([1,0,0])) + zi = np.ones((3,1,4), 'l') + zi[1,:,:] *= 2 + zi[2,:,:] *= 3 + zi = self.convert_dtype(zi) + + zf_expected = self.convert_dtype(np.zeros((3,2,4), 'l')) + y_expected = np.zeros((3,2,5), 'l') + y_expected[:,:,:4] = [[[1]], [[2]], [[3]]] + y_expected = self.convert_dtype(y_expected) + + # IIR + y_iir, zf_iir = lfilter(b, a, x, -1, zi) + assert_array_almost_equal(y_iir, y_expected) + assert_array_almost_equal(zf_iir, zf_expected) + + # FIR + y_fir, zf_fir = lfilter(b, a[0], x, -1, zi) + assert_array_almost_equal(y_fir, y_expected) + assert_array_almost_equal(zf_fir, zf_expected) + + def base_bad_size_zi(self, b, a, x, axis, zi): + b = self.convert_dtype(b) + a = self.convert_dtype(a) + x = self.convert_dtype(x) + zi = self.convert_dtype(zi) + assert_raises(ValueError, lfilter, b, a, x, axis, zi) + + def test_bad_size_zi(self): + # rank 1 + x1 = np.arange(6) + self.base_bad_size_zi([1], [1], x1, -1, [1]) + self.base_bad_size_zi([1, 1], [1], x1, -1, [0, 1]) + self.base_bad_size_zi([1, 1], [1], x1, -1, [[0]]) + self.base_bad_size_zi([1, 1], [1], x1, -1, [0, 1, 2]) + self.base_bad_size_zi([1, 1, 1], [1], x1, -1, [[0]]) + self.base_bad_size_zi([1, 1, 1], [1], x1, -1, [0, 1, 2]) + self.base_bad_size_zi([1], [1, 1], x1, -1, [0, 1]) + self.base_bad_size_zi([1], [1, 1], x1, -1, [[0]]) + self.base_bad_size_zi([1], [1, 1], x1, -1, [0, 1, 2]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [[0], [1]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0, 1, 2]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0, 1, 2, 3]) + self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0]) + self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [[0], [1]]) + self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0, 1, 2]) + self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0, 1, 2, 3]) + + # rank 2 + x2 = np.arange(12).reshape((4,3)) + # for axis=0 zi.shape should == (max(len(a),len(b))-1, 3) + self.base_bad_size_zi([1], [1], x2, 0, [0]) + + # for each of these there are 5 cases tested (in this order): + # 1. not deep enough, right # elements + # 2. too deep, right # elements + # 3. right depth, right # elements, transposed + # 4. right depth, too few elements + # 5. right depth, too many elements + + self.base_bad_size_zi([1, 1], [1], x2, 0, [0,1,2]) + self.base_bad_size_zi([1, 1], [1], x2, 0, [[[0,1,2]]]) + self.base_bad_size_zi([1, 1], [1], x2, 0, [[0], [1], [2]]) + self.base_bad_size_zi([1, 1], [1], x2, 0, [[0,1]]) + self.base_bad_size_zi([1, 1], [1], x2, 0, [[0,1,2,3]]) + + self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [0,1,2,3,4,5]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[[0,1,2],[3,4,5]]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0,1],[2,3]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0,1,2,3],[4,5,6,7]]) + + self.base_bad_size_zi([1], [1, 1], x2, 0, [0,1,2]) + self.base_bad_size_zi([1], [1, 1], x2, 0, [[[0,1,2]]]) + self.base_bad_size_zi([1], [1, 1], x2, 0, [[0], [1], [2]]) + self.base_bad_size_zi([1], [1, 1], x2, 0, [[0,1]]) + self.base_bad_size_zi([1], [1, 1], x2, 0, [[0,1,2,3]]) + + self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [0,1,2,3,4,5]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[[0,1,2],[3,4,5]]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0,1],[2,3]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0,1,2,3],[4,5,6,7]]) + + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [0,1,2,3,4,5]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[[0,1,2],[3,4,5]]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[0,1],[2,3]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[0,1,2,3],[4,5,6,7]]) + + # for axis=1 zi.shape should == (4, max(len(a),len(b))-1) + self.base_bad_size_zi([1], [1], x2, 1, [0]) + + self.base_bad_size_zi([1, 1], [1], x2, 1, [0,1,2,3]) + self.base_bad_size_zi([1, 1], [1], x2, 1, [[[0],[1],[2],[3]]]) + self.base_bad_size_zi([1, 1], [1], x2, 1, [[0, 1, 2, 3]]) + self.base_bad_size_zi([1, 1], [1], x2, 1, [[0],[1],[2]]) + self.base_bad_size_zi([1, 1], [1], x2, 1, [[0],[1],[2],[3],[4]]) + + self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [0,1,2,3,4,5,6,7]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[[0,1],[2,3],[4,5],[6,7]]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[0,1,2,3],[4,5,6,7]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[0,1],[2,3],[4,5],[6,7],[8,9]]) + + self.base_bad_size_zi([1], [1, 1], x2, 1, [0,1,2,3]) + self.base_bad_size_zi([1], [1, 1], x2, 1, [[[0],[1],[2],[3]]]) + self.base_bad_size_zi([1], [1, 1], x2, 1, [[0, 1, 2, 3]]) + self.base_bad_size_zi([1], [1, 1], x2, 1, [[0],[1],[2]]) + self.base_bad_size_zi([1], [1, 1], x2, 1, [[0],[1],[2],[3],[4]]) + + self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [0,1,2,3,4,5,6,7]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[[0,1],[2,3],[4,5],[6,7]]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0,1,2,3],[4,5,6,7]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0,1],[2,3],[4,5],[6,7],[8,9]]) + + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [0,1,2,3,4,5,6,7]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [[[0,1],[2,3],[4,5],[6,7]]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [[0,1,2,3],[4,5,6,7]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [[0,1],[2,3],[4,5]]) + self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [[0,1],[2,3],[4,5],[6,7],[8,9]]) + + def test_empty_zi(self): + # Regression test for #880: empty array for zi crashes. + x = self.generate((5,)) + a = self.convert_dtype([1]) + b = self.convert_dtype([1]) + zi = self.convert_dtype([]) + y, zf = lfilter(b, a, x, zi=zi) + assert_array_almost_equal(y, x) + assert_equal(zf.dtype, self.dtype) + assert_equal(zf.size, 0) + + def test_lfiltic_bad_zi(self): + # Regression test for #3699: bad initial conditions + a = self.convert_dtype([1]) + b = self.convert_dtype([1]) + # "y" sets the datatype of zi, so it truncates if int + zi = lfiltic(b, a, [1., 0]) + zi_1 = lfiltic(b, a, [1, 0]) + zi_2 = lfiltic(b, a, [True, False]) + assert_array_equal(zi, zi_1) + assert_array_equal(zi, zi_2) + + def test_short_x_FIR(self): + # regression test for #5116 + # x shorter than b, with non None zi fails + a = self.convert_dtype([1]) + b = self.convert_dtype([1, 0, -1]) + zi = self.convert_dtype([2, 7]) + x = self.convert_dtype([72]) + ye = self.convert_dtype([74]) + zfe = self.convert_dtype([7, -72]) + y, zf = lfilter(b, a, x, zi=zi) + assert_array_almost_equal(y, ye) + assert_array_almost_equal(zf, zfe) + + def test_short_x_IIR(self): + # regression test for #5116 + # x shorter than b, with non None zi fails + a = self.convert_dtype([1, 1]) + b = self.convert_dtype([1, 0, -1]) + zi = self.convert_dtype([2, 7]) + x = self.convert_dtype([72]) + ye = self.convert_dtype([74]) + zfe = self.convert_dtype([-67, -72]) + y, zf = lfilter(b, a, x, zi=zi) + assert_array_almost_equal(y, ye) + assert_array_almost_equal(zf, zfe) + + def test_do_not_modify_a_b_IIR(self): + x = self.generate((6,)) + b = self.convert_dtype([1, -1]) + b0 = b.copy() + a = self.convert_dtype([0.5, -0.5]) + a0 = a.copy() + y_r = self.convert_dtype([0, 2, 4, 6, 8, 10.]) + y_f = lfilter(b, a, x) + assert_array_almost_equal(y_f, y_r) + assert_equal(b, b0) + assert_equal(a, a0) + + def test_do_not_modify_a_b_FIR(self): + x = self.generate((6,)) + b = self.convert_dtype([1, 0, 1]) + b0 = b.copy() + a = self.convert_dtype([2]) + a0 = a.copy() + y_r = self.convert_dtype([0, 0.5, 1, 2, 3, 4.]) + y_f = lfilter(b, a, x) + assert_array_almost_equal(y_f, y_r) + assert_equal(b, b0) + assert_equal(a, a0) + + @pytest.mark.parametrize("a", [1.0, [1.0], np.array(1.0)]) + @pytest.mark.parametrize("b", [1.0, [1.0], np.array(1.0)]) + def test_scalar_input(self, a, b): + data = np.random.randn(10) + assert_allclose( + lfilter(np.array([1.0]), np.array([1.0]), data), + lfilter(b, a, data)) + + +class TestLinearFilterFloat32(_TestLinearFilter): + dtype = np.dtype('f') + + +class TestLinearFilterFloat64(_TestLinearFilter): + dtype = np.dtype('d') + + +class TestLinearFilterFloatExtended(_TestLinearFilter): + dtype = np.dtype('g') + + +class TestLinearFilterComplex64(_TestLinearFilter): + dtype = np.dtype('F') + + +class TestLinearFilterComplex128(_TestLinearFilter): + dtype = np.dtype('D') + + +class TestLinearFilterComplexExtended(_TestLinearFilter): + dtype = np.dtype('G') + +class TestLinearFilterDecimal(_TestLinearFilter): + dtype = np.dtype('O') + + def type(self, x): + return Decimal(str(x)) + + +class TestLinearFilterObject(_TestLinearFilter): + dtype = np.dtype('O') + type = float + + +def test_lfilter_bad_object(): + # lfilter: object arrays with non-numeric objects raise TypeError. + # Regression test for ticket #1452. + if hasattr(sys, 'abiflags') and 'd' in sys.abiflags: + pytest.skip('test is flaky when run with python3-dbg') + assert_raises(TypeError, lfilter, [1.0], [1.0], [1.0, None, 2.0]) + assert_raises(TypeError, lfilter, [1.0], [None], [1.0, 2.0, 3.0]) + assert_raises(TypeError, lfilter, [None], [1.0], [1.0, 2.0, 3.0]) + + +def test_lfilter_notimplemented_input(): + # Should not crash, gh-7991 + assert_raises(NotImplementedError, lfilter, [2,3], [4,5], [1,2,3,4,5]) + + +@pytest.mark.parametrize('dt', [np.ubyte, np.byte, np.ushort, np.short, + np_ulong, np_long, np.ulonglong, np.ulonglong, + np.float32, np.float64, np.longdouble, + Decimal]) +class TestCorrelateReal: + def _setup_rank1(self, dt): + a = np.linspace(0, 3, 4).astype(dt) + b = np.linspace(1, 2, 2).astype(dt) + + y_r = np.array([0, 2, 5, 8, 3]).astype(dt) + return a, b, y_r + + def equal_tolerance(self, res_dt): + # default value of keyword + decimal = 6 + try: + dt_info = np.finfo(res_dt) + if hasattr(dt_info, 'resolution'): + decimal = int(-0.5*np.log10(dt_info.resolution)) + except Exception: + pass + return decimal + + def equal_tolerance_fft(self, res_dt): + # FFT implementations convert longdouble arguments down to + # double so don't expect better precision, see gh-9520 + if res_dt == np.longdouble: + return self.equal_tolerance(np.float64) + else: + return self.equal_tolerance(res_dt) + + def test_method(self, dt): + if dt == Decimal: + method = choose_conv_method([Decimal(4)], [Decimal(3)]) + assert_equal(method, 'direct') + else: + a, b, y_r = self._setup_rank3(dt) + y_fft = correlate(a, b, method='fft') + y_direct = correlate(a, b, method='direct') + + assert_array_almost_equal(y_r, + y_fft, + decimal=self.equal_tolerance_fft(y_fft.dtype),) + assert_array_almost_equal(y_r, + y_direct, + decimal=self.equal_tolerance(y_direct.dtype),) + assert_equal(y_fft.dtype, dt) + assert_equal(y_direct.dtype, dt) + + def test_rank1_valid(self, dt): + a, b, y_r = self._setup_rank1(dt) + y = correlate(a, b, 'valid') + assert_array_almost_equal(y, y_r[1:4]) + assert_equal(y.dtype, dt) + + # See gh-5897 + y = correlate(b, a, 'valid') + assert_array_almost_equal(y, y_r[1:4][::-1]) + assert_equal(y.dtype, dt) + + def test_rank1_same(self, dt): + a, b, y_r = self._setup_rank1(dt) + y = correlate(a, b, 'same') + assert_array_almost_equal(y, y_r[:-1]) + assert_equal(y.dtype, dt) + + def test_rank1_full(self, dt): + a, b, y_r = self._setup_rank1(dt) + y = correlate(a, b, 'full') + assert_array_almost_equal(y, y_r) + assert_equal(y.dtype, dt) + + def _setup_rank3(self, dt): + a = np.linspace(0, 39, 40).reshape((2, 4, 5), order='F').astype( + dt) + b = np.linspace(0, 23, 24).reshape((2, 3, 4), order='F').astype( + dt) + + y_r = array([[[0., 184., 504., 912., 1360., 888., 472., 160.], + [46., 432., 1062., 1840., 2672., 1698., 864., 266.], + [134., 736., 1662., 2768., 3920., 2418., 1168., 314.], + [260., 952., 1932., 3056., 4208., 2580., 1240., 332.], + [202., 664., 1290., 1984., 2688., 1590., 712., 150.], + [114., 344., 642., 960., 1280., 726., 296., 38.]], + + [[23., 400., 1035., 1832., 2696., 1737., 904., 293.], + [134., 920., 2166., 3680., 5280., 3306., 1640., 474.], + [325., 1544., 3369., 5512., 7720., 4683., 2192., 535.], + [571., 1964., 3891., 6064., 8272., 4989., 2324., 565.], + [434., 1360., 2586., 3920., 5264., 3054., 1312., 230.], + [241., 700., 1281., 1888., 2496., 1383., 532., 39.]], + + [[22., 214., 528., 916., 1332., 846., 430., 132.], + [86., 484., 1098., 1832., 2600., 1602., 772., 206.], + [188., 802., 1698., 2732., 3788., 2256., 1018., 218.], + [308., 1006., 1950., 2996., 4052., 2400., 1078., 230.], + [230., 692., 1290., 1928., 2568., 1458., 596., 78.], + [126., 354., 636., 924., 1212., 654., 234., 0.]]], + dtype=np.float64).astype(dt) + + return a, b, y_r + + def test_rank3_valid(self, dt): + a, b, y_r = self._setup_rank3(dt) + y = correlate(a, b, "valid") + assert_array_almost_equal(y, y_r[1:2, 2:4, 3:5]) + assert_equal(y.dtype, dt) + + # See gh-5897 + y = correlate(b, a, "valid") + assert_array_almost_equal(y, y_r[1:2, 2:4, 3:5][::-1, ::-1, ::-1]) + assert_equal(y.dtype, dt) + + def test_rank3_same(self, dt): + a, b, y_r = self._setup_rank3(dt) + y = correlate(a, b, "same") + assert_array_almost_equal(y, y_r[0:-1, 1:-1, 1:-2]) + assert_equal(y.dtype, dt) + + def test_rank3_all(self, dt): + a, b, y_r = self._setup_rank3(dt) + y = correlate(a, b) + assert_array_almost_equal(y, y_r) + assert_equal(y.dtype, dt) + + +class TestCorrelate: + # Tests that don't depend on dtype + + def test_invalid_shapes(self): + # By "invalid," we mean that no one + # array has dimensions that are all at + # least as large as the corresponding + # dimensions of the other array. This + # setup should throw a ValueError. + a = np.arange(1, 7).reshape((2, 3)) + b = np.arange(-6, 0).reshape((3, 2)) + + assert_raises(ValueError, correlate, *(a, b), **{'mode': 'valid'}) + assert_raises(ValueError, correlate, *(b, a), **{'mode': 'valid'}) + + def test_invalid_params(self): + a = [3, 4, 5] + b = [1, 2, 3] + assert_raises(ValueError, correlate, a, b, mode='spam') + assert_raises(ValueError, correlate, a, b, mode='eggs', method='fft') + assert_raises(ValueError, correlate, a, b, mode='ham', method='direct') + assert_raises(ValueError, correlate, a, b, mode='full', method='bacon') + assert_raises(ValueError, correlate, a, b, mode='same', method='bacon') + + def test_mismatched_dims(self): + # Input arrays should have the same number of dimensions + assert_raises(ValueError, correlate, [1], 2, method='direct') + assert_raises(ValueError, correlate, 1, [2], method='direct') + assert_raises(ValueError, correlate, [1], 2, method='fft') + assert_raises(ValueError, correlate, 1, [2], method='fft') + assert_raises(ValueError, correlate, [1], [[2]]) + assert_raises(ValueError, correlate, [3], 2) + + def test_numpy_fastpath(self): + a = [1, 2, 3] + b = [4, 5] + assert_allclose(correlate(a, b, mode='same'), [5, 14, 23]) + + a = [1, 2, 3] + b = [4, 5, 6] + assert_allclose(correlate(a, b, mode='same'), [17, 32, 23]) + assert_allclose(correlate(a, b, mode='full'), [6, 17, 32, 23, 12]) + assert_allclose(correlate(a, b, mode='valid'), [32]) + + +@pytest.mark.parametrize("mode", ["valid", "same", "full"]) +@pytest.mark.parametrize("behind", [True, False]) +@pytest.mark.parametrize("input_size", [100, 101, 1000, 1001, 10000, 10001]) +def test_correlation_lags(mode, behind, input_size): + # generate random data + rng = np.random.RandomState(0) + in1 = rng.standard_normal(input_size) + offset = int(input_size/10) + # generate offset version of array to correlate with + if behind: + # y is behind x + in2 = np.concatenate([rng.standard_normal(offset), in1]) + expected = -offset + else: + # y is ahead of x + in2 = in1[offset:] + expected = offset + # cross correlate, returning lag information + correlation = correlate(in1, in2, mode=mode) + lags = correlation_lags(in1.size, in2.size, mode=mode) + # identify the peak + lag_index = np.argmax(correlation) + # Check as expected + assert_equal(lags[lag_index], expected) + # Correlation and lags shape should match + assert_equal(lags.shape, correlation.shape) + + +@pytest.mark.parametrize('dt', [np.csingle, np.cdouble, np.clongdouble]) +class TestCorrelateComplex: + # The decimal precision to be used for comparing results. + # This value will be passed as the 'decimal' keyword argument of + # assert_array_almost_equal(). + # Since correlate may chose to use FFT method which converts + # longdoubles to doubles internally don't expect better precision + # for longdouble than for double (see gh-9520). + + def decimal(self, dt): + if dt == np.clongdouble: + dt = np.cdouble + return int(2 * np.finfo(dt).precision / 3) + + def _setup_rank1(self, dt, mode): + np.random.seed(9) + a = np.random.randn(10).astype(dt) + a += 1j * np.random.randn(10).astype(dt) + b = np.random.randn(8).astype(dt) + b += 1j * np.random.randn(8).astype(dt) + + y_r = (correlate(a.real, b.real, mode=mode) + + correlate(a.imag, b.imag, mode=mode)).astype(dt) + y_r += 1j * (-correlate(a.real, b.imag, mode=mode) + + correlate(a.imag, b.real, mode=mode)) + return a, b, y_r + + def test_rank1_valid(self, dt): + a, b, y_r = self._setup_rank1(dt, 'valid') + y = correlate(a, b, 'valid') + assert_array_almost_equal(y, y_r, decimal=self.decimal(dt)) + assert_equal(y.dtype, dt) + + # See gh-5897 + y = correlate(b, a, 'valid') + assert_array_almost_equal(y, y_r[::-1].conj(), decimal=self.decimal(dt)) + assert_equal(y.dtype, dt) + + def test_rank1_same(self, dt): + a, b, y_r = self._setup_rank1(dt, 'same') + y = correlate(a, b, 'same') + assert_array_almost_equal(y, y_r, decimal=self.decimal(dt)) + assert_equal(y.dtype, dt) + + def test_rank1_full(self, dt): + a, b, y_r = self._setup_rank1(dt, 'full') + y = correlate(a, b, 'full') + assert_array_almost_equal(y, y_r, decimal=self.decimal(dt)) + assert_equal(y.dtype, dt) + + def test_swap_full(self, dt): + d = np.array([0.+0.j, 1.+1.j, 2.+2.j], dtype=dt) + k = np.array([1.+3.j, 2.+4.j, 3.+5.j, 4.+6.j], dtype=dt) + y = correlate(d, k) + assert_equal(y, [0.+0.j, 10.-2.j, 28.-6.j, 22.-6.j, 16.-6.j, 8.-4.j]) + + def test_swap_same(self, dt): + d = [0.+0.j, 1.+1.j, 2.+2.j] + k = [1.+3.j, 2.+4.j, 3.+5.j, 4.+6.j] + y = correlate(d, k, mode="same") + assert_equal(y, [10.-2.j, 28.-6.j, 22.-6.j]) + + def test_rank3(self, dt): + a = np.random.randn(10, 8, 6).astype(dt) + a += 1j * np.random.randn(10, 8, 6).astype(dt) + b = np.random.randn(8, 6, 4).astype(dt) + b += 1j * np.random.randn(8, 6, 4).astype(dt) + + y_r = (correlate(a.real, b.real) + + correlate(a.imag, b.imag)).astype(dt) + y_r += 1j * (-correlate(a.real, b.imag) + correlate(a.imag, b.real)) + + y = correlate(a, b, 'full') + assert_array_almost_equal(y, y_r, decimal=self.decimal(dt) - 1) + assert_equal(y.dtype, dt) + + def test_rank0(self, dt): + a = np.array(np.random.randn()).astype(dt) + a += 1j * np.array(np.random.randn()).astype(dt) + b = np.array(np.random.randn()).astype(dt) + b += 1j * np.array(np.random.randn()).astype(dt) + + y_r = (correlate(a.real, b.real) + + correlate(a.imag, b.imag)).astype(dt) + y_r += 1j * np.array(-correlate(a.real, b.imag) + + correlate(a.imag, b.real)) + + y = correlate(a, b, 'full') + assert_array_almost_equal(y, y_r, decimal=self.decimal(dt) - 1) + assert_equal(y.dtype, dt) + + assert_equal(correlate([1], [2j]), correlate(1, 2j)) + assert_equal(correlate([2j], [3j]), correlate(2j, 3j)) + assert_equal(correlate([3j], [4]), correlate(3j, 4)) + + +class TestCorrelate2d: + + def test_consistency_correlate_funcs(self): + # Compare np.correlate, signal.correlate, signal.correlate2d + a = np.arange(5) + b = np.array([3.2, 1.4, 3]) + for mode in ['full', 'valid', 'same']: + assert_almost_equal(np.correlate(a, b, mode=mode), + signal.correlate(a, b, mode=mode)) + assert_almost_equal(np.squeeze(signal.correlate2d([a], [b], + mode=mode)), + signal.correlate(a, b, mode=mode)) + + # See gh-5897 + if mode == 'valid': + assert_almost_equal(np.correlate(b, a, mode=mode), + signal.correlate(b, a, mode=mode)) + assert_almost_equal(np.squeeze(signal.correlate2d([b], [a], + mode=mode)), + signal.correlate(b, a, mode=mode)) + + def test_invalid_shapes(self): + # By "invalid," we mean that no one + # array has dimensions that are all at + # least as large as the corresponding + # dimensions of the other array. This + # setup should throw a ValueError. + a = np.arange(1, 7).reshape((2, 3)) + b = np.arange(-6, 0).reshape((3, 2)) + + assert_raises(ValueError, signal.correlate2d, *(a, b), **{'mode': 'valid'}) + assert_raises(ValueError, signal.correlate2d, *(b, a), **{'mode': 'valid'}) + + def test_complex_input(self): + assert_equal(signal.correlate2d([[1]], [[2j]]), -2j) + assert_equal(signal.correlate2d([[2j]], [[3j]]), 6) + assert_equal(signal.correlate2d([[3j]], [[4]]), 12j) + + +class TestLFilterZI: + + def test_basic(self): + a = np.array([1.0, -1.0, 0.5]) + b = np.array([1.0, 0.0, 2.0]) + zi_expected = np.array([5.0, -1.0]) + zi = lfilter_zi(b, a) + assert_array_almost_equal(zi, zi_expected) + + def test_scale_invariance(self): + # Regression test. There was a bug in which b was not correctly + # rescaled when a[0] was nonzero. + b = np.array([2, 8, 5]) + a = np.array([1, 1, 8]) + zi1 = lfilter_zi(b, a) + zi2 = lfilter_zi(2*b, 2*a) + assert_allclose(zi2, zi1, rtol=1e-12) + + @pytest.mark.parametrize('dtype', [np.float32, np.float64]) + def test_types(self, dtype): + b = np.zeros((8), dtype=dtype) + a = np.array([1], dtype=dtype) + assert_equal(np.real(signal.lfilter_zi(b, a)).dtype, dtype) + + +class TestFiltFilt: + filtfilt_kind = 'tf' + + def filtfilt(self, zpk, x, axis=-1, padtype='odd', padlen=None, + method='pad', irlen=None): + if self.filtfilt_kind == 'tf': + b, a = zpk2tf(*zpk) + return filtfilt(b, a, x, axis, padtype, padlen, method, irlen) + elif self.filtfilt_kind == 'sos': + sos = zpk2sos(*zpk) + return sosfiltfilt(sos, x, axis, padtype, padlen) + + def test_basic(self): + zpk = tf2zpk([1, 2, 3], [1, 2, 3]) + out = self.filtfilt(zpk, np.arange(12)) + assert_allclose(out, arange(12), atol=5.28e-11) + + def test_sine(self): + rate = 2000 + t = np.linspace(0, 1.0, rate + 1) + # A signal with low frequency and a high frequency. + xlow = np.sin(5 * 2 * np.pi * t) + xhigh = np.sin(250 * 2 * np.pi * t) + x = xlow + xhigh + + zpk = butter(8, 0.125, output='zpk') + # r is the magnitude of the largest pole. + r = np.abs(zpk[1]).max() + eps = 1e-5 + # n estimates the number of steps for the + # transient to decay by a factor of eps. + n = int(np.ceil(np.log(eps) / np.log(r))) + + # High order lowpass filter... + y = self.filtfilt(zpk, x, padlen=n) + # Result should be just xlow. + err = np.abs(y - xlow).max() + assert_(err < 1e-4) + + # A 2D case. + x2d = np.vstack([xlow, xlow + xhigh]) + y2d = self.filtfilt(zpk, x2d, padlen=n, axis=1) + assert_equal(y2d.shape, x2d.shape) + err = np.abs(y2d - xlow).max() + assert_(err < 1e-4) + + # Use the previous result to check the use of the axis keyword. + # (Regression test for ticket #1620) + y2dt = self.filtfilt(zpk, x2d.T, padlen=n, axis=0) + assert_equal(y2d, y2dt.T) + + def test_axis(self): + # Test the 'axis' keyword on a 3D array. + x = np.arange(10.0 * 11.0 * 12.0).reshape(10, 11, 12) + zpk = butter(3, 0.125, output='zpk') + y0 = self.filtfilt(zpk, x, padlen=0, axis=0) + y1 = self.filtfilt(zpk, np.swapaxes(x, 0, 1), padlen=0, axis=1) + assert_array_equal(y0, np.swapaxes(y1, 0, 1)) + y2 = self.filtfilt(zpk, np.swapaxes(x, 0, 2), padlen=0, axis=2) + assert_array_equal(y0, np.swapaxes(y2, 0, 2)) + + def test_acoeff(self): + if self.filtfilt_kind != 'tf': + return # only necessary for TF + # test for 'a' coefficient as single number + out = signal.filtfilt([.5, .5], 1, np.arange(10)) + assert_allclose(out, np.arange(10), rtol=1e-14, atol=1e-14) + + def test_gust_simple(self): + if self.filtfilt_kind != 'tf': + pytest.skip('gust only implemented for TF systems') + # The input array has length 2. The exact solution for this case + # was computed "by hand". + x = np.array([1.0, 2.0]) + b = np.array([0.5]) + a = np.array([1.0, -0.5]) + y, z1, z2 = _filtfilt_gust(b, a, x) + assert_allclose([z1[0], z2[0]], + [0.3*x[0] + 0.2*x[1], 0.2*x[0] + 0.3*x[1]]) + assert_allclose(y, [z1[0] + 0.25*z2[0] + 0.25*x[0] + 0.125*x[1], + 0.25*z1[0] + z2[0] + 0.125*x[0] + 0.25*x[1]]) + + def test_gust_scalars(self): + if self.filtfilt_kind != 'tf': + pytest.skip('gust only implemented for TF systems') + # The filter coefficients are both scalars, so the filter simply + # multiplies its input by b/a. When it is used in filtfilt, the + # factor is (b/a)**2. + x = np.arange(12) + b = 3.0 + a = 2.0 + y = filtfilt(b, a, x, method="gust") + expected = (b/a)**2 * x + assert_allclose(y, expected) + + +class TestSOSFiltFilt(TestFiltFilt): + filtfilt_kind = 'sos' + + def test_equivalence(self): + """Test equivalence between sosfiltfilt and filtfilt""" + x = np.random.RandomState(0).randn(1000) + for order in range(1, 6): + zpk = signal.butter(order, 0.35, output='zpk') + b, a = zpk2tf(*zpk) + sos = zpk2sos(*zpk) + y = filtfilt(b, a, x) + y_sos = sosfiltfilt(sos, x) + assert_allclose(y, y_sos, atol=1e-12, err_msg='order=%s' % order) + + +def filtfilt_gust_opt(b, a, x): + """ + An alternative implementation of filtfilt with Gustafsson edges. + + This function computes the same result as + `scipy.signal._signaltools._filtfilt_gust`, but only 1-d arrays + are accepted. The problem is solved using `fmin` from `scipy.optimize`. + `_filtfilt_gust` is significantly faster than this implementation. + """ + def filtfilt_gust_opt_func(ics, b, a, x): + """Objective function used in filtfilt_gust_opt.""" + m = max(len(a), len(b)) - 1 + z0f = ics[:m] + z0b = ics[m:] + y_f = lfilter(b, a, x, zi=z0f)[0] + y_fb = lfilter(b, a, y_f[::-1], zi=z0b)[0][::-1] + + y_b = lfilter(b, a, x[::-1], zi=z0b)[0][::-1] + y_bf = lfilter(b, a, y_b, zi=z0f)[0] + value = np.sum((y_fb - y_bf)**2) + return value + + m = max(len(a), len(b)) - 1 + zi = lfilter_zi(b, a) + ics = np.concatenate((x[:m].mean()*zi, x[-m:].mean()*zi)) + result = fmin(filtfilt_gust_opt_func, ics, args=(b, a, x), + xtol=1e-10, ftol=1e-12, + maxfun=10000, maxiter=10000, + full_output=True, disp=False) + opt, fopt, niter, funcalls, warnflag = result + if warnflag > 0: + raise RuntimeError("minimization failed in filtfilt_gust_opt: " + "warnflag=%d" % warnflag) + z0f = opt[:m] + z0b = opt[m:] + + # Apply the forward-backward filter using the computed initial + # conditions. + y_b = lfilter(b, a, x[::-1], zi=z0b)[0][::-1] + y = lfilter(b, a, y_b, zi=z0f)[0] + + return y, z0f, z0b + + +def check_filtfilt_gust(b, a, shape, axis, irlen=None): + # Generate x, the data to be filtered. + np.random.seed(123) + x = np.random.randn(*shape) + + # Apply filtfilt to x. This is the main calculation to be checked. + y = filtfilt(b, a, x, axis=axis, method="gust", irlen=irlen) + + # Also call the private function so we can test the ICs. + yg, zg1, zg2 = _filtfilt_gust(b, a, x, axis=axis, irlen=irlen) + + # filtfilt_gust_opt is an independent implementation that gives the + # expected result, but it only handles 1-D arrays, so use some looping + # and reshaping shenanigans to create the expected output arrays. + xx = np.swapaxes(x, axis, -1) + out_shape = xx.shape[:-1] + yo = np.empty_like(xx) + m = max(len(a), len(b)) - 1 + zo1 = np.empty(out_shape + (m,)) + zo2 = np.empty(out_shape + (m,)) + for indx in product(*[range(d) for d in out_shape]): + yo[indx], zo1[indx], zo2[indx] = filtfilt_gust_opt(b, a, xx[indx]) + yo = np.swapaxes(yo, -1, axis) + zo1 = np.swapaxes(zo1, -1, axis) + zo2 = np.swapaxes(zo2, -1, axis) + + assert_allclose(y, yo, rtol=1e-8, atol=1e-9) + assert_allclose(yg, yo, rtol=1e-8, atol=1e-9) + assert_allclose(zg1, zo1, rtol=1e-8, atol=1e-9) + assert_allclose(zg2, zo2, rtol=1e-8, atol=1e-9) + + +def test_choose_conv_method(): + for mode in ['valid', 'same', 'full']: + for ndim in [1, 2]: + n, k, true_method = 8, 6, 'direct' + x = np.random.randn(*((n,) * ndim)) + h = np.random.randn(*((k,) * ndim)) + + method = choose_conv_method(x, h, mode=mode) + assert_equal(method, true_method) + + method_try, times = choose_conv_method(x, h, mode=mode, measure=True) + assert_(method_try in {'fft', 'direct'}) + assert_(isinstance(times, dict)) + assert_('fft' in times.keys() and 'direct' in times.keys()) + + n = 10 + for not_fft_conv_supp in ["complex256", "complex192"]: + if hasattr(np, not_fft_conv_supp): + x = np.ones(n, dtype=not_fft_conv_supp) + h = x.copy() + assert_equal(choose_conv_method(x, h, mode=mode), 'direct') + + x = np.array([2**51], dtype=np.int64) + h = x.copy() + assert_equal(choose_conv_method(x, h, mode=mode), 'direct') + + x = [Decimal(3), Decimal(2)] + h = [Decimal(1), Decimal(4)] + assert_equal(choose_conv_method(x, h, mode=mode), 'direct') + + +def test_filtfilt_gust(): + # Design a filter. + z, p, k = signal.ellip(3, 0.01, 120, 0.0875, output='zpk') + + # Find the approximate impulse response length of the filter. + eps = 1e-10 + r = np.max(np.abs(p)) + approx_impulse_len = int(np.ceil(np.log(eps) / np.log(r))) + + np.random.seed(123) + + b, a = zpk2tf(z, p, k) + for irlen in [None, approx_impulse_len]: + signal_len = 5 * approx_impulse_len + + # 1-d test case + check_filtfilt_gust(b, a, (signal_len,), 0, irlen) + + # 3-d test case; test each axis. + for axis in range(3): + shape = [2, 2, 2] + shape[axis] = signal_len + check_filtfilt_gust(b, a, shape, axis, irlen) + + # Test case with length less than 2*approx_impulse_len. + # In this case, `filtfilt_gust` should behave the same as if + # `irlen=None` was given. + length = 2*approx_impulse_len - 50 + check_filtfilt_gust(b, a, (length,), 0, approx_impulse_len) + + +class TestDecimate: + def test_bad_args(self): + x = np.arange(12) + assert_raises(TypeError, signal.decimate, x, q=0.5, n=1) + assert_raises(TypeError, signal.decimate, x, q=2, n=0.5) + + def test_basic_IIR(self): + x = np.arange(12) + y = signal.decimate(x, 2, n=1, ftype='iir', zero_phase=False).round() + assert_array_equal(y, x[::2]) + + def test_basic_FIR(self): + x = np.arange(12) + y = signal.decimate(x, 2, n=1, ftype='fir', zero_phase=False).round() + assert_array_equal(y, x[::2]) + + def test_shape(self): + # Regression test for ticket #1480. + z = np.zeros((30, 30)) + d0 = signal.decimate(z, 2, axis=0, zero_phase=False) + assert_equal(d0.shape, (15, 30)) + d1 = signal.decimate(z, 2, axis=1, zero_phase=False) + assert_equal(d1.shape, (30, 15)) + + def test_phaseshift_FIR(self): + with suppress_warnings() as sup: + sup.filter(BadCoefficients, "Badly conditioned filter") + self._test_phaseshift(method='fir', zero_phase=False) + + def test_zero_phase_FIR(self): + with suppress_warnings() as sup: + sup.filter(BadCoefficients, "Badly conditioned filter") + self._test_phaseshift(method='fir', zero_phase=True) + + def test_phaseshift_IIR(self): + self._test_phaseshift(method='iir', zero_phase=False) + + def test_zero_phase_IIR(self): + self._test_phaseshift(method='iir', zero_phase=True) + + def _test_phaseshift(self, method, zero_phase): + rate = 120 + rates_to = [15, 20, 30, 40] # q = 8, 6, 4, 3 + + t_tot = 100 # Need to let antialiasing filters settle + t = np.arange(rate*t_tot+1) / float(rate) + + # Sinusoids at 0.8*nyquist, windowed to avoid edge artifacts + freqs = np.array(rates_to) * 0.8 / 2 + d = (np.exp(1j * 2 * np.pi * freqs[:, np.newaxis] * t) + * signal.windows.tukey(t.size, 0.1)) + + for rate_to in rates_to: + q = rate // rate_to + t_to = np.arange(rate_to*t_tot+1) / float(rate_to) + d_tos = (np.exp(1j * 2 * np.pi * freqs[:, np.newaxis] * t_to) + * signal.windows.tukey(t_to.size, 0.1)) + + # Set up downsampling filters, match v0.17 defaults + if method == 'fir': + n = 30 + system = signal.dlti(signal.firwin(n + 1, 1. / q, + window='hamming'), 1.) + elif method == 'iir': + n = 8 + wc = 0.8*np.pi/q + system = signal.dlti(*signal.cheby1(n, 0.05, wc/np.pi)) + + # Calculate expected phase response, as unit complex vector + if zero_phase is False: + _, h_resps = signal.freqz(system.num, system.den, + freqs/rate*2*np.pi) + h_resps /= np.abs(h_resps) + else: + h_resps = np.ones_like(freqs) + + y_resamps = signal.decimate(d.real, q, n, ftype=system, + zero_phase=zero_phase) + + # Get phase from complex inner product, like CSD + h_resamps = np.sum(d_tos.conj() * y_resamps, axis=-1) + h_resamps /= np.abs(h_resamps) + subnyq = freqs < 0.5*rate_to + + # Complex vectors should be aligned, only compare below nyquist + assert_allclose(np.angle(h_resps.conj()*h_resamps)[subnyq], 0, + atol=1e-3, rtol=1e-3) + + def test_auto_n(self): + # Test that our value of n is a reasonable choice (depends on + # the downsampling factor) + sfreq = 100. + n = 1000 + t = np.arange(n) / sfreq + # will alias for decimations (>= 15) + x = np.sqrt(2. / n) * np.sin(2 * np.pi * (sfreq / 30.) * t) + assert_allclose(np.linalg.norm(x), 1., rtol=1e-3) + x_out = signal.decimate(x, 30, ftype='fir') + assert_array_less(np.linalg.norm(x_out), 0.01) + + def test_long_float32(self): + # regression: gh-15072. With 32-bit float and either lfilter + # or filtfilt, this is numerically unstable + x = signal.decimate(np.ones(10_000, dtype=np.float32), 10) + assert not any(np.isnan(x)) + + def test_float16_upcast(self): + # float16 must be upcast to float64 + x = signal.decimate(np.ones(100, dtype=np.float16), 10) + assert x.dtype.type == np.float64 + + def test_complex_iir_dlti(self): + # regression: gh-17845 + # centre frequency for filter [Hz] + fcentre = 50 + # filter passband width [Hz] + fwidth = 5 + # sample rate [Hz] + fs = 1e3 + + z, p, k = signal.butter(2, 2*np.pi*fwidth/2, output='zpk', fs=fs) + z = z.astype(complex) * np.exp(2j * np.pi * fcentre/fs) + p = p.astype(complex) * np.exp(2j * np.pi * fcentre/fs) + system = signal.dlti(z, p, k) + + t = np.arange(200) / fs + + # input + u = (np.exp(2j * np.pi * fcentre * t) + + 0.5 * np.exp(-2j * np.pi * fcentre * t)) + + ynzp = signal.decimate(u, 2, ftype=system, zero_phase=False) + ynzpref = signal.lfilter(*signal.zpk2tf(z, p, k), + u)[::2] + + assert_equal(ynzp, ynzpref) + + yzp = signal.decimate(u, 2, ftype=system, zero_phase=True) + yzpref = signal.filtfilt(*signal.zpk2tf(z, p, k), + u)[::2] + + assert_allclose(yzp, yzpref, rtol=1e-10, atol=1e-13) + + def test_complex_fir_dlti(self): + # centre frequency for filter [Hz] + fcentre = 50 + # filter passband width [Hz] + fwidth = 5 + # sample rate [Hz] + fs = 1e3 + numtaps = 20 + + # FIR filter about 0Hz + bbase = signal.firwin(numtaps, fwidth/2, fs=fs) + + # rotate these to desired frequency + zbase = np.roots(bbase) + zrot = zbase * np.exp(2j * np.pi * fcentre/fs) + # FIR filter about 50Hz, maintaining passband gain of 0dB + bz = bbase[0] * np.poly(zrot) + + system = signal.dlti(bz, 1) + + t = np.arange(200) / fs + + # input + u = (np.exp(2j * np.pi * fcentre * t) + + 0.5 * np.exp(-2j * np.pi * fcentre * t)) + + ynzp = signal.decimate(u, 2, ftype=system, zero_phase=False) + ynzpref = signal.upfirdn(bz, u, up=1, down=2)[:100] + + assert_equal(ynzp, ynzpref) + + yzp = signal.decimate(u, 2, ftype=system, zero_phase=True) + yzpref = signal.resample_poly(u, 1, 2, window=bz) + + assert_equal(yzp, yzpref) + + +class TestHilbert: + + def test_bad_args(self): + x = np.array([1.0 + 0.0j]) + assert_raises(ValueError, hilbert, x) + x = np.arange(8.0) + assert_raises(ValueError, hilbert, x, N=0) + + def test_hilbert_theoretical(self): + # test cases by Ariel Rokem + decimal = 14 + + pi = np.pi + t = np.arange(0, 2 * pi, pi / 256) + a0 = np.sin(t) + a1 = np.cos(t) + a2 = np.sin(2 * t) + a3 = np.cos(2 * t) + a = np.vstack([a0, a1, a2, a3]) + + h = hilbert(a) + h_abs = np.abs(h) + h_angle = np.angle(h) + h_real = np.real(h) + + # The real part should be equal to the original signals: + assert_almost_equal(h_real, a, decimal) + # The absolute value should be one everywhere, for this input: + assert_almost_equal(h_abs, np.ones(a.shape), decimal) + # For the 'slow' sine - the phase should go from -pi/2 to pi/2 in + # the first 256 bins: + assert_almost_equal(h_angle[0, :256], + np.arange(-pi / 2, pi / 2, pi / 256), + decimal) + # For the 'slow' cosine - the phase should go from 0 to pi in the + # same interval: + assert_almost_equal( + h_angle[1, :256], np.arange(0, pi, pi / 256), decimal) + # The 'fast' sine should make this phase transition in half the time: + assert_almost_equal(h_angle[2, :128], + np.arange(-pi / 2, pi / 2, pi / 128), + decimal) + # Ditto for the 'fast' cosine: + assert_almost_equal( + h_angle[3, :128], np.arange(0, pi, pi / 128), decimal) + + # The imaginary part of hilbert(cos(t)) = sin(t) Wikipedia + assert_almost_equal(h[1].imag, a0, decimal) + + def test_hilbert_axisN(self): + # tests for axis and N arguments + a = np.arange(18).reshape(3, 6) + # test axis + aa = hilbert(a, axis=-1) + assert_equal(hilbert(a.T, axis=0), aa.T) + # test 1d + assert_almost_equal(hilbert(a[0]), aa[0], 14) + + # test N + aan = hilbert(a, N=20, axis=-1) + assert_equal(aan.shape, [3, 20]) + assert_equal(hilbert(a.T, N=20, axis=0).shape, [20, 3]) + # the next test is just a regression test, + # no idea whether numbers make sense + a0hilb = np.array([0.000000000000000e+00 - 1.72015830311905j, + 1.000000000000000e+00 - 2.047794505137069j, + 1.999999999999999e+00 - 2.244055555687583j, + 3.000000000000000e+00 - 1.262750302935009j, + 4.000000000000000e+00 - 1.066489252384493j, + 5.000000000000000e+00 + 2.918022706971047j, + 8.881784197001253e-17 + 3.845658908989067j, + -9.444121133484362e-17 + 0.985044202202061j, + -1.776356839400251e-16 + 1.332257797702019j, + -3.996802888650564e-16 + 0.501905089898885j, + 1.332267629550188e-16 + 0.668696078880782j, + -1.192678053963799e-16 + 0.235487067862679j, + -1.776356839400251e-16 + 0.286439612812121j, + 3.108624468950438e-16 + 0.031676888064907j, + 1.332267629550188e-16 - 0.019275656884536j, + -2.360035624836702e-16 - 0.1652588660287j, + 0.000000000000000e+00 - 0.332049855010597j, + 3.552713678800501e-16 - 0.403810179797771j, + 8.881784197001253e-17 - 0.751023775297729j, + 9.444121133484362e-17 - 0.79252210110103j]) + assert_almost_equal(aan[0], a0hilb, 14, 'N regression') + + @pytest.mark.parametrize('dtype', [np.float32, np.float64]) + def test_hilbert_types(self, dtype): + in_typed = np.zeros(8, dtype=dtype) + assert_equal(np.real(signal.hilbert(in_typed)).dtype, dtype) + + +class TestHilbert2: + + def test_bad_args(self): + # x must be real. + x = np.array([[1.0 + 0.0j]]) + assert_raises(ValueError, hilbert2, x) + + # x must be rank 2. + x = np.arange(24).reshape(2, 3, 4) + assert_raises(ValueError, hilbert2, x) + + # Bad value for N. + x = np.arange(16).reshape(4, 4) + assert_raises(ValueError, hilbert2, x, N=0) + assert_raises(ValueError, hilbert2, x, N=(2, 0)) + assert_raises(ValueError, hilbert2, x, N=(2,)) + + @pytest.mark.parametrize('dtype', [np.float32, np.float64]) + def test_hilbert2_types(self, dtype): + in_typed = np.zeros((2, 32), dtype=dtype) + assert_equal(np.real(signal.hilbert2(in_typed)).dtype, dtype) + + +class TestPartialFractionExpansion: + @staticmethod + def assert_rp_almost_equal(r, p, r_true, p_true, decimal=7): + r_true = np.asarray(r_true) + p_true = np.asarray(p_true) + + distance = np.hypot(abs(p[:, None] - p_true), + abs(r[:, None] - r_true)) + + rows, cols = linear_sum_assignment(distance) + assert_almost_equal(p[rows], p_true[cols], decimal=decimal) + assert_almost_equal(r[rows], r_true[cols], decimal=decimal) + + def test_compute_factors(self): + factors, poly = _compute_factors([1, 2, 3], [3, 2, 1]) + assert_equal(len(factors), 3) + assert_almost_equal(factors[0], np.poly([2, 2, 3])) + assert_almost_equal(factors[1], np.poly([1, 1, 1, 3])) + assert_almost_equal(factors[2], np.poly([1, 1, 1, 2, 2])) + assert_almost_equal(poly, np.poly([1, 1, 1, 2, 2, 3])) + + factors, poly = _compute_factors([1, 2, 3], [3, 2, 1], + include_powers=True) + assert_equal(len(factors), 6) + assert_almost_equal(factors[0], np.poly([1, 1, 2, 2, 3])) + assert_almost_equal(factors[1], np.poly([1, 2, 2, 3])) + assert_almost_equal(factors[2], np.poly([2, 2, 3])) + assert_almost_equal(factors[3], np.poly([1, 1, 1, 2, 3])) + assert_almost_equal(factors[4], np.poly([1, 1, 1, 3])) + assert_almost_equal(factors[5], np.poly([1, 1, 1, 2, 2])) + assert_almost_equal(poly, np.poly([1, 1, 1, 2, 2, 3])) + + def test_group_poles(self): + unique, multiplicity = _group_poles( + [1.0, 1.001, 1.003, 2.0, 2.003, 3.0], 0.1, 'min') + assert_equal(unique, [1.0, 2.0, 3.0]) + assert_equal(multiplicity, [3, 2, 1]) + + def test_residue_general(self): + # Test are taken from issue #4464, note that poles in scipy are + # in increasing by absolute value order, opposite to MATLAB. + r, p, k = residue([5, 3, -2, 7], [-4, 0, 8, 3]) + assert_almost_equal(r, [1.3320, -0.6653, -1.4167], decimal=4) + assert_almost_equal(p, [-0.4093, -1.1644, 1.5737], decimal=4) + assert_almost_equal(k, [-1.2500], decimal=4) + + r, p, k = residue([-4, 8], [1, 6, 8]) + assert_almost_equal(r, [8, -12]) + assert_almost_equal(p, [-2, -4]) + assert_equal(k.size, 0) + + r, p, k = residue([4, 1], [1, -1, -2]) + assert_almost_equal(r, [1, 3]) + assert_almost_equal(p, [-1, 2]) + assert_equal(k.size, 0) + + r, p, k = residue([4, 3], [2, -3.4, 1.98, -0.406]) + self.assert_rp_almost_equal( + r, p, [-18.125 - 13.125j, -18.125 + 13.125j, 36.25], + [0.5 - 0.2j, 0.5 + 0.2j, 0.7]) + assert_equal(k.size, 0) + + r, p, k = residue([2, 1], [1, 5, 8, 4]) + self.assert_rp_almost_equal(r, p, [-1, 1, 3], [-1, -2, -2]) + assert_equal(k.size, 0) + + r, p, k = residue([3, -1.1, 0.88, -2.396, 1.348], + [1, -0.7, -0.14, 0.048]) + assert_almost_equal(r, [-3, 4, 1]) + assert_almost_equal(p, [0.2, -0.3, 0.8]) + assert_almost_equal(k, [3, 1]) + + r, p, k = residue([1], [1, 2, -3]) + assert_almost_equal(r, [0.25, -0.25]) + assert_almost_equal(p, [1, -3]) + assert_equal(k.size, 0) + + r, p, k = residue([1, 0, -5], [1, 0, 0, 0, -1]) + self.assert_rp_almost_equal(r, p, + [1, 1.5j, -1.5j, -1], [-1, -1j, 1j, 1]) + assert_equal(k.size, 0) + + r, p, k = residue([3, 8, 6], [1, 3, 3, 1]) + self.assert_rp_almost_equal(r, p, [1, 2, 3], [-1, -1, -1]) + assert_equal(k.size, 0) + + r, p, k = residue([3, -1], [1, -3, 2]) + assert_almost_equal(r, [-2, 5]) + assert_almost_equal(p, [1, 2]) + assert_equal(k.size, 0) + + r, p, k = residue([2, 3, -1], [1, -3, 2]) + assert_almost_equal(r, [-4, 13]) + assert_almost_equal(p, [1, 2]) + assert_almost_equal(k, [2]) + + r, p, k = residue([7, 2, 3, -1], [1, -3, 2]) + assert_almost_equal(r, [-11, 69]) + assert_almost_equal(p, [1, 2]) + assert_almost_equal(k, [7, 23]) + + r, p, k = residue([2, 3, -1], [1, -3, 4, -2]) + self.assert_rp_almost_equal(r, p, [4, -1 + 3.5j, -1 - 3.5j], + [1, 1 - 1j, 1 + 1j]) + assert_almost_equal(k.size, 0) + + def test_residue_leading_zeros(self): + # Leading zeros in numerator or denominator must not affect the answer. + r0, p0, k0 = residue([5, 3, -2, 7], [-4, 0, 8, 3]) + r1, p1, k1 = residue([0, 5, 3, -2, 7], [-4, 0, 8, 3]) + r2, p2, k2 = residue([5, 3, -2, 7], [0, -4, 0, 8, 3]) + r3, p3, k3 = residue([0, 0, 5, 3, -2, 7], [0, 0, 0, -4, 0, 8, 3]) + assert_almost_equal(r0, r1) + assert_almost_equal(r0, r2) + assert_almost_equal(r0, r3) + assert_almost_equal(p0, p1) + assert_almost_equal(p0, p2) + assert_almost_equal(p0, p3) + assert_almost_equal(k0, k1) + assert_almost_equal(k0, k2) + assert_almost_equal(k0, k3) + + def test_resiude_degenerate(self): + # Several tests for zero numerator and denominator. + r, p, k = residue([0, 0], [1, 6, 8]) + assert_almost_equal(r, [0, 0]) + assert_almost_equal(p, [-2, -4]) + assert_equal(k.size, 0) + + r, p, k = residue(0, 1) + assert_equal(r.size, 0) + assert_equal(p.size, 0) + assert_equal(k.size, 0) + + with pytest.raises(ValueError, match="Denominator `a` is zero."): + residue(1, 0) + + def test_residuez_general(self): + r, p, k = residuez([1, 6, 6, 2], [1, -(2 + 1j), (1 + 2j), -1j]) + self.assert_rp_almost_equal(r, p, [-2+2.5j, 7.5+7.5j, -4.5-12j], + [1j, 1, 1]) + assert_almost_equal(k, [2j]) + + r, p, k = residuez([1, 2, 1], [1, -1, 0.3561]) + self.assert_rp_almost_equal(r, p, + [-0.9041 - 5.9928j, -0.9041 + 5.9928j], + [0.5 + 0.3257j, 0.5 - 0.3257j], + decimal=4) + assert_almost_equal(k, [2.8082], decimal=4) + + r, p, k = residuez([1, -1], [1, -5, 6]) + assert_almost_equal(r, [-1, 2]) + assert_almost_equal(p, [2, 3]) + assert_equal(k.size, 0) + + r, p, k = residuez([2, 3, 4], [1, 3, 3, 1]) + self.assert_rp_almost_equal(r, p, [4, -5, 3], [-1, -1, -1]) + assert_equal(k.size, 0) + + r, p, k = residuez([1, -10, -4, 4], [2, -2, -4]) + assert_almost_equal(r, [0.5, -1.5]) + assert_almost_equal(p, [-1, 2]) + assert_almost_equal(k, [1.5, -1]) + + r, p, k = residuez([18], [18, 3, -4, -1]) + self.assert_rp_almost_equal(r, p, + [0.36, 0.24, 0.4], [0.5, -1/3, -1/3]) + assert_equal(k.size, 0) + + r, p, k = residuez([2, 3], np.polymul([1, -1/2], [1, 1/4])) + assert_almost_equal(r, [-10/3, 16/3]) + assert_almost_equal(p, [-0.25, 0.5]) + assert_equal(k.size, 0) + + r, p, k = residuez([1, -2, 1], [1, -1]) + assert_almost_equal(r, [0]) + assert_almost_equal(p, [1]) + assert_almost_equal(k, [1, -1]) + + r, p, k = residuez(1, [1, -1j]) + assert_almost_equal(r, [1]) + assert_almost_equal(p, [1j]) + assert_equal(k.size, 0) + + r, p, k = residuez(1, [1, -1, 0.25]) + assert_almost_equal(r, [0, 1]) + assert_almost_equal(p, [0.5, 0.5]) + assert_equal(k.size, 0) + + r, p, k = residuez(1, [1, -0.75, .125]) + assert_almost_equal(r, [-1, 2]) + assert_almost_equal(p, [0.25, 0.5]) + assert_equal(k.size, 0) + + r, p, k = residuez([1, 6, 2], [1, -2, 1]) + assert_almost_equal(r, [-10, 9]) + assert_almost_equal(p, [1, 1]) + assert_almost_equal(k, [2]) + + r, p, k = residuez([6, 2], [1, -2, 1]) + assert_almost_equal(r, [-2, 8]) + assert_almost_equal(p, [1, 1]) + assert_equal(k.size, 0) + + r, p, k = residuez([1, 6, 6, 2], [1, -2, 1]) + assert_almost_equal(r, [-24, 15]) + assert_almost_equal(p, [1, 1]) + assert_almost_equal(k, [10, 2]) + + r, p, k = residuez([1, 0, 1], [1, 0, 0, 0, 0, -1]) + self.assert_rp_almost_equal(r, p, + [0.2618 + 0.1902j, 0.2618 - 0.1902j, + 0.4, 0.0382 - 0.1176j, 0.0382 + 0.1176j], + [-0.8090 + 0.5878j, -0.8090 - 0.5878j, + 1.0, 0.3090 + 0.9511j, 0.3090 - 0.9511j], + decimal=4) + assert_equal(k.size, 0) + + def test_residuez_trailing_zeros(self): + # Trailing zeros in numerator or denominator must not affect the + # answer. + r0, p0, k0 = residuez([5, 3, -2, 7], [-4, 0, 8, 3]) + r1, p1, k1 = residuez([5, 3, -2, 7, 0], [-4, 0, 8, 3]) + r2, p2, k2 = residuez([5, 3, -2, 7], [-4, 0, 8, 3, 0]) + r3, p3, k3 = residuez([5, 3, -2, 7, 0, 0], [-4, 0, 8, 3, 0, 0, 0]) + assert_almost_equal(r0, r1) + assert_almost_equal(r0, r2) + assert_almost_equal(r0, r3) + assert_almost_equal(p0, p1) + assert_almost_equal(p0, p2) + assert_almost_equal(p0, p3) + assert_almost_equal(k0, k1) + assert_almost_equal(k0, k2) + assert_almost_equal(k0, k3) + + def test_residuez_degenerate(self): + r, p, k = residuez([0, 0], [1, 6, 8]) + assert_almost_equal(r, [0, 0]) + assert_almost_equal(p, [-2, -4]) + assert_equal(k.size, 0) + + r, p, k = residuez(0, 1) + assert_equal(r.size, 0) + assert_equal(p.size, 0) + assert_equal(k.size, 0) + + with pytest.raises(ValueError, match="Denominator `a` is zero."): + residuez(1, 0) + + with pytest.raises(ValueError, + match="First coefficient of determinant `a` must " + "be non-zero."): + residuez(1, [0, 1, 2, 3]) + + def test_inverse_unique_roots_different_rtypes(self): + # This test was inspired by github issue 2496. + r = [3 / 10, -1 / 6, -2 / 15] + p = [0, -2, -5] + k = [] + b_expected = [0, 1, 3] + a_expected = [1, 7, 10, 0] + + # With the default tolerance, the rtype does not matter + # for this example. + for rtype in ('avg', 'mean', 'min', 'minimum', 'max', 'maximum'): + b, a = invres(r, p, k, rtype=rtype) + assert_allclose(b, b_expected) + assert_allclose(a, a_expected) + + b, a = invresz(r, p, k, rtype=rtype) + assert_allclose(b, b_expected) + assert_allclose(a, a_expected) + + def test_inverse_repeated_roots_different_rtypes(self): + r = [3 / 20, -7 / 36, -1 / 6, 2 / 45] + p = [0, -2, -2, -5] + k = [] + b_expected = [0, 0, 1, 3] + b_expected_z = [-1/6, -2/3, 11/6, 3] + a_expected = [1, 9, 24, 20, 0] + + for rtype in ('avg', 'mean', 'min', 'minimum', 'max', 'maximum'): + b, a = invres(r, p, k, rtype=rtype) + assert_allclose(b, b_expected, atol=1e-14) + assert_allclose(a, a_expected) + + b, a = invresz(r, p, k, rtype=rtype) + assert_allclose(b, b_expected_z, atol=1e-14) + assert_allclose(a, a_expected) + + def test_inverse_bad_rtype(self): + r = [3 / 20, -7 / 36, -1 / 6, 2 / 45] + p = [0, -2, -2, -5] + k = [] + with pytest.raises(ValueError, match="`rtype` must be one of"): + invres(r, p, k, rtype='median') + with pytest.raises(ValueError, match="`rtype` must be one of"): + invresz(r, p, k, rtype='median') + + def test_invresz_one_coefficient_bug(self): + # Regression test for issue in gh-4646. + r = [1] + p = [2] + k = [0] + b, a = invresz(r, p, k) + assert_allclose(b, [1.0]) + assert_allclose(a, [1.0, -2.0]) + + def test_invres(self): + b, a = invres([1], [1], []) + assert_almost_equal(b, [1]) + assert_almost_equal(a, [1, -1]) + + b, a = invres([1 - 1j, 2, 0.5 - 3j], [1, 0.5j, 1 + 1j], []) + assert_almost_equal(b, [3.5 - 4j, -8.5 + 0.25j, 3.5 + 3.25j]) + assert_almost_equal(a, [1, -2 - 1.5j, 0.5 + 2j, 0.5 - 0.5j]) + + b, a = invres([0.5, 1], [1 - 1j, 2 + 2j], [1, 2, 3]) + assert_almost_equal(b, [1, -1 - 1j, 1 - 2j, 0.5 - 3j, 10]) + assert_almost_equal(a, [1, -3 - 1j, 4]) + + b, a = invres([-1, 2, 1j, 3 - 1j, 4, -2], + [-1, 2 - 1j, 2 - 1j, 3, 3, 3], []) + assert_almost_equal(b, [4 - 1j, -28 + 16j, 40 - 62j, 100 + 24j, + -292 + 219j, 192 - 268j]) + assert_almost_equal(a, [1, -12 + 2j, 53 - 20j, -96 + 68j, 27 - 72j, + 108 - 54j, -81 + 108j]) + + b, a = invres([-1, 1j], [1, 1], [1, 2]) + assert_almost_equal(b, [1, 0, -4, 3 + 1j]) + assert_almost_equal(a, [1, -2, 1]) + + def test_invresz(self): + b, a = invresz([1], [1], []) + assert_almost_equal(b, [1]) + assert_almost_equal(a, [1, -1]) + + b, a = invresz([1 - 1j, 2, 0.5 - 3j], [1, 0.5j, 1 + 1j], []) + assert_almost_equal(b, [3.5 - 4j, -8.5 + 0.25j, 3.5 + 3.25j]) + assert_almost_equal(a, [1, -2 - 1.5j, 0.5 + 2j, 0.5 - 0.5j]) + + b, a = invresz([0.5, 1], [1 - 1j, 2 + 2j], [1, 2, 3]) + assert_almost_equal(b, [2.5, -3 - 1j, 1 - 2j, -1 - 3j, 12]) + assert_almost_equal(a, [1, -3 - 1j, 4]) + + b, a = invresz([-1, 2, 1j, 3 - 1j, 4, -2], + [-1, 2 - 1j, 2 - 1j, 3, 3, 3], []) + assert_almost_equal(b, [6, -50 + 11j, 100 - 72j, 80 + 58j, + -354 + 228j, 234 - 297j]) + assert_almost_equal(a, [1, -12 + 2j, 53 - 20j, -96 + 68j, 27 - 72j, + 108 - 54j, -81 + 108j]) + + b, a = invresz([-1, 1j], [1, 1], [1, 2]) + assert_almost_equal(b, [1j, 1, -3, 2]) + assert_almost_equal(a, [1, -2, 1]) + + def test_inverse_scalar_arguments(self): + b, a = invres(1, 1, 1) + assert_almost_equal(b, [1, 0]) + assert_almost_equal(a, [1, -1]) + + b, a = invresz(1, 1, 1) + assert_almost_equal(b, [2, -1]) + assert_almost_equal(a, [1, -1]) + + +class TestVectorstrength: + + def test_single_1dperiod(self): + events = np.array([.5]) + period = 5. + targ_strength = 1. + targ_phase = .1 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 0) + assert_equal(phase.ndim, 0) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_single_2dperiod(self): + events = np.array([.5]) + period = [1, 2, 5.] + targ_strength = [1.] * 3 + targ_phase = np.array([.5, .25, .1]) + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 1) + assert_equal(phase.ndim, 1) + assert_array_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_equal_1dperiod(self): + events = np.array([.25, .25, .25, .25, .25, .25]) + period = 2 + targ_strength = 1. + targ_phase = .125 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 0) + assert_equal(phase.ndim, 0) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_equal_2dperiod(self): + events = np.array([.25, .25, .25, .25, .25, .25]) + period = [1, 2, ] + targ_strength = [1.] * 2 + targ_phase = np.array([.25, .125]) + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 1) + assert_equal(phase.ndim, 1) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_spaced_1dperiod(self): + events = np.array([.1, 1.1, 2.1, 4.1, 10.1]) + period = 1 + targ_strength = 1. + targ_phase = .1 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 0) + assert_equal(phase.ndim, 0) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_spaced_2dperiod(self): + events = np.array([.1, 1.1, 2.1, 4.1, 10.1]) + period = [1, .5] + targ_strength = [1.] * 2 + targ_phase = np.array([.1, .2]) + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 1) + assert_equal(phase.ndim, 1) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_partial_1dperiod(self): + events = np.array([.25, .5, .75]) + period = 1 + targ_strength = 1. / 3. + targ_phase = .5 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 0) + assert_equal(phase.ndim, 0) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_partial_2dperiod(self): + events = np.array([.25, .5, .75]) + period = [1., 1., 1., 1.] + targ_strength = [1. / 3.] * 4 + targ_phase = np.array([.5, .5, .5, .5]) + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 1) + assert_equal(phase.ndim, 1) + assert_almost_equal(strength, targ_strength) + assert_almost_equal(phase, 2 * np.pi * targ_phase) + + def test_opposite_1dperiod(self): + events = np.array([0, .25, .5, .75]) + period = 1. + targ_strength = 0 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 0) + assert_equal(phase.ndim, 0) + assert_almost_equal(strength, targ_strength) + + def test_opposite_2dperiod(self): + events = np.array([0, .25, .5, .75]) + period = [1.] * 10 + targ_strength = [0.] * 10 + + strength, phase = vectorstrength(events, period) + + assert_equal(strength.ndim, 1) + assert_equal(phase.ndim, 1) + assert_almost_equal(strength, targ_strength) + + def test_2d_events_ValueError(self): + events = np.array([[1, 2]]) + period = 1. + assert_raises(ValueError, vectorstrength, events, period) + + def test_2d_period_ValueError(self): + events = 1. + period = np.array([[1]]) + assert_raises(ValueError, vectorstrength, events, period) + + def test_zero_period_ValueError(self): + events = 1. + period = 0 + assert_raises(ValueError, vectorstrength, events, period) + + def test_negative_period_ValueError(self): + events = 1. + period = -1 + assert_raises(ValueError, vectorstrength, events, period) + + +def assert_allclose_cast(actual, desired, rtol=1e-7, atol=0): + """Wrap assert_allclose while casting object arrays.""" + if actual.dtype.kind == 'O': + dtype = np.array(actual.flat[0]).dtype + actual, desired = actual.astype(dtype), desired.astype(dtype) + assert_allclose(actual, desired, rtol, atol) + + +@pytest.mark.parametrize('func', (sosfilt, lfilter)) +def test_nonnumeric_dtypes(func): + x = [Decimal(1), Decimal(2), Decimal(3)] + b = [Decimal(1), Decimal(2), Decimal(3)] + a = [Decimal(1), Decimal(2), Decimal(3)] + x = np.array(x) + assert x.dtype.kind == 'O' + desired = lfilter(np.array(b, float), np.array(a, float), x.astype(float)) + if func is sosfilt: + actual = sosfilt([b + a], x) + else: + actual = lfilter(b, a, x) + assert all(isinstance(x, Decimal) for x in actual) + assert_allclose(actual.astype(float), desired.astype(float)) + # Degenerate cases + if func is lfilter: + args = [1., 1.] + else: + args = [tf2sos(1., 1.)] + + with pytest.raises(ValueError, match='must be at least 1-D'): + func(*args, x=1.) + + +@pytest.mark.parametrize('dt', 'fdFD') +class TestSOSFilt: + + # The test_rank* tests are pulled from _TestLinearFilter + def test_rank1(self, dt): + x = np.linspace(0, 5, 6).astype(dt) + b = np.array([1, -1]).astype(dt) + a = np.array([0.5, -0.5]).astype(dt) + + # Test simple IIR + y_r = np.array([0, 2, 4, 6, 8, 10.]).astype(dt) + sos = tf2sos(b, a) + assert_array_almost_equal(sosfilt(tf2sos(b, a), x), y_r) + + # Test simple FIR + b = np.array([1, 1]).astype(dt) + # NOTE: This was changed (rel. to TestLinear...) to add a pole @zero: + a = np.array([1, 0]).astype(dt) + y_r = np.array([0, 1, 3, 5, 7, 9.]).astype(dt) + assert_array_almost_equal(sosfilt(tf2sos(b, a), x), y_r) + + b = [1, 1, 0] + a = [1, 0, 0] + x = np.ones(8) + sos = np.concatenate((b, a)) + sos.shape = (1, 6) + y = sosfilt(sos, x) + assert_allclose(y, [1, 2, 2, 2, 2, 2, 2, 2]) + + def test_rank2(self, dt): + shape = (4, 3) + x = np.linspace(0, np.prod(shape) - 1, np.prod(shape)).reshape(shape) + x = x.astype(dt) + + b = np.array([1, -1]).astype(dt) + a = np.array([0.5, 0.5]).astype(dt) + + y_r2_a0 = np.array([[0, 2, 4], [6, 4, 2], [0, 2, 4], [6, 4, 2]], + dtype=dt) + + y_r2_a1 = np.array([[0, 2, 0], [6, -4, 6], [12, -10, 12], + [18, -16, 18]], dtype=dt) + + y = sosfilt(tf2sos(b, a), x, axis=0) + assert_array_almost_equal(y_r2_a0, y) + + y = sosfilt(tf2sos(b, a), x, axis=1) + assert_array_almost_equal(y_r2_a1, y) + + def test_rank3(self, dt): + shape = (4, 3, 2) + x = np.linspace(0, np.prod(shape) - 1, np.prod(shape)).reshape(shape) + + b = np.array([1, -1]).astype(dt) + a = np.array([0.5, 0.5]).astype(dt) + + # Test last axis + y = sosfilt(tf2sos(b, a), x) + for i in range(x.shape[0]): + for j in range(x.shape[1]): + assert_array_almost_equal(y[i, j], lfilter(b, a, x[i, j])) + + def test_initial_conditions(self, dt): + b1, a1 = signal.butter(2, 0.25, 'low') + b2, a2 = signal.butter(2, 0.75, 'low') + b3, a3 = signal.butter(2, 0.75, 'low') + b = np.convolve(np.convolve(b1, b2), b3) + a = np.convolve(np.convolve(a1, a2), a3) + sos = np.array((np.r_[b1, a1], np.r_[b2, a2], np.r_[b3, a3])) + + x = np.random.rand(50).astype(dt) + + # Stopping filtering and continuing + y_true, zi = lfilter(b, a, x[:20], zi=np.zeros(6)) + y_true = np.r_[y_true, lfilter(b, a, x[20:], zi=zi)[0]] + assert_allclose_cast(y_true, lfilter(b, a, x)) + + y_sos, zi = sosfilt(sos, x[:20], zi=np.zeros((3, 2))) + y_sos = np.r_[y_sos, sosfilt(sos, x[20:], zi=zi)[0]] + assert_allclose_cast(y_true, y_sos) + + # Use a step function + zi = sosfilt_zi(sos) + x = np.ones(8, dt) + y, zf = sosfilt(sos, x, zi=zi) + + assert_allclose_cast(y, np.ones(8)) + assert_allclose_cast(zf, zi) + + # Initial condition shape matching + x.shape = (1, 1) + x.shape # 3D + assert_raises(ValueError, sosfilt, sos, x, zi=zi) + zi_nd = zi.copy() + zi_nd.shape = (zi.shape[0], 1, 1, zi.shape[-1]) + assert_raises(ValueError, sosfilt, sos, x, + zi=zi_nd[:, :, :, [0, 1, 1]]) + y, zf = sosfilt(sos, x, zi=zi_nd) + assert_allclose_cast(y[0, 0], np.ones(8)) + assert_allclose_cast(zf[:, 0, 0, :], zi) + + def test_initial_conditions_3d_axis1(self, dt): + # Test the use of zi when sosfilt is applied to axis 1 of a 3-d input. + + # Input array is x. + x = np.random.RandomState(159).randint(0, 5, size=(2, 15, 3)) + x = x.astype(dt) + + # Design a filter in ZPK format and convert to SOS + zpk = signal.butter(6, 0.35, output='zpk') + sos = zpk2sos(*zpk) + nsections = sos.shape[0] + + # Filter along this axis. + axis = 1 + + # Initial conditions, all zeros. + shp = list(x.shape) + shp[axis] = 2 + shp = [nsections] + shp + z0 = np.zeros(shp) + + # Apply the filter to x. + yf, zf = sosfilt(sos, x, axis=axis, zi=z0) + + # Apply the filter to x in two stages. + y1, z1 = sosfilt(sos, x[:, :5, :], axis=axis, zi=z0) + y2, z2 = sosfilt(sos, x[:, 5:, :], axis=axis, zi=z1) + + # y should equal yf, and z2 should equal zf. + y = np.concatenate((y1, y2), axis=axis) + assert_allclose_cast(y, yf, rtol=1e-10, atol=1e-13) + assert_allclose_cast(z2, zf, rtol=1e-10, atol=1e-13) + + # let's try the "step" initial condition + zi = sosfilt_zi(sos) + zi.shape = [nsections, 1, 2, 1] + zi = zi * x[:, 0:1, :] + y = sosfilt(sos, x, axis=axis, zi=zi)[0] + # check it against the TF form + b, a = zpk2tf(*zpk) + zi = lfilter_zi(b, a) + zi.shape = [1, zi.size, 1] + zi = zi * x[:, 0:1, :] + y_tf = lfilter(b, a, x, axis=axis, zi=zi)[0] + assert_allclose_cast(y, y_tf, rtol=1e-10, atol=1e-13) + + def test_bad_zi_shape(self, dt): + # The shape of zi is checked before using any values in the + # arguments, so np.empty is fine for creating the arguments. + x = np.empty((3, 15, 3), dt) + sos = np.zeros((4, 6)) + zi = np.empty((4, 3, 3, 2)) # Correct shape is (4, 3, 2, 3) + with pytest.raises(ValueError, match='should be all ones'): + sosfilt(sos, x, zi=zi, axis=1) + sos[:, 3] = 1. + with pytest.raises(ValueError, match='Invalid zi shape'): + sosfilt(sos, x, zi=zi, axis=1) + + def test_sosfilt_zi(self, dt): + sos = signal.butter(6, 0.2, output='sos') + zi = sosfilt_zi(sos) + + y, zf = sosfilt(sos, np.ones(40, dt), zi=zi) + assert_allclose_cast(zf, zi, rtol=1e-13) + + # Expected steady state value of the step response of this filter: + ss = np.prod(sos[:, :3].sum(axis=-1) / sos[:, 3:].sum(axis=-1)) + assert_allclose_cast(y, ss, rtol=1e-13) + + # zi as array-like + _, zf = sosfilt(sos, np.ones(40, dt), zi=zi.tolist()) + assert_allclose_cast(zf, zi, rtol=1e-13) + + +class TestDeconvolve: + + def test_basic(self): + # From docstring example + original = [0, 1, 0, 0, 1, 1, 0, 0] + impulse_response = [2, 1] + recorded = [0, 2, 1, 0, 2, 3, 1, 0, 0] + recovered, remainder = signal.deconvolve(recorded, impulse_response) + assert_allclose(recovered, original) + + def test_n_dimensional_signal(self): + recorded = [[0, 0], [0, 0]] + impulse_response = [0, 0] + with pytest.raises(ValueError, match="signal must be 1-D."): + quotient, remainder = signal.deconvolve(recorded, impulse_response) + + def test_n_dimensional_divisor(self): + recorded = [0, 0] + impulse_response = [[0, 0], [0, 0]] + with pytest.raises(ValueError, match="divisor must be 1-D."): + quotient, remainder = signal.deconvolve(recorded, impulse_response) + + +class TestDetrend: + + def test_basic(self): + detrended = detrend(array([1, 2, 3])) + detrended_exact = array([0, 0, 0]) + assert_array_almost_equal(detrended, detrended_exact) + + def test_copy(self): + x = array([1, 1.2, 1.5, 1.6, 2.4]) + copy_array = detrend(x, overwrite_data=False) + inplace = detrend(x, overwrite_data=True) + assert_array_almost_equal(copy_array, inplace) + + @pytest.mark.parametrize('kind', ['linear', 'constant']) + @pytest.mark.parametrize('axis', [0, 1, 2]) + def test_axis(self, axis, kind): + data = np.arange(5*6*7).reshape(5, 6, 7) + detrended = detrend(data, type=kind, axis=axis) + assert detrended.shape == data.shape + + def test_bp(self): + data = [0, 1, 2] + [5, 0, -5, -10] + detrended = detrend(data, type='linear', bp=3) + assert_allclose(detrended, 0, atol=1e-14) + + # repeat with ndim > 1 and axis + data = np.asarray(data)[None, :, None] + + detrended = detrend(data, type="linear", bp=3, axis=1) + assert_allclose(detrended, 0, atol=1e-14) + + # breakpoint index > shape[axis]: raises + with assert_raises(ValueError): + detrend(data, type="linear", bp=3) + + @pytest.mark.parametrize('bp', [np.array([0, 2]), [0, 2]]) + def test_detrend_array_bp(self, bp): + # regression test for https://github.com/scipy/scipy/issues/18675 + rng = np.random.RandomState(12345) + x = rng.rand(10) + # bp = np.array([0, 2]) + + res = detrend(x, bp=bp) + res_scipy_191 = np.array([-4.44089210e-16, -2.22044605e-16, + -1.11128506e-01, -1.69470553e-01, 1.14710683e-01, 6.35468419e-02, + 3.53533144e-01, -3.67877935e-02, -2.00417675e-02, -1.94362049e-01]) + + assert_allclose(res, res_scipy_191, atol=1e-14) + + +class TestUniqueRoots: + def test_real_no_repeat(self): + p = [-1.0, -0.5, 0.3, 1.2, 10.0] + unique, multiplicity = unique_roots(p) + assert_almost_equal(unique, p, decimal=15) + assert_equal(multiplicity, np.ones(len(p))) + + def test_real_repeat(self): + p = [-1.0, -0.95, -0.89, -0.8, 0.5, 1.0, 1.05] + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='min') + assert_almost_equal(unique, [-1.0, -0.89, 0.5, 1.0], decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='max') + assert_almost_equal(unique, [-0.95, -0.8, 0.5, 1.05], decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='avg') + assert_almost_equal(unique, [-0.975, -0.845, 0.5, 1.025], decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + def test_complex_no_repeat(self): + p = [-1.0, 1.0j, 0.5 + 0.5j, -1.0 - 1.0j, 3.0 + 2.0j] + unique, multiplicity = unique_roots(p) + assert_almost_equal(unique, p, decimal=15) + assert_equal(multiplicity, np.ones(len(p))) + + def test_complex_repeat(self): + p = [-1.0, -1.0 + 0.05j, -0.95 + 0.15j, -0.90 + 0.15j, 0.0, + 0.5 + 0.5j, 0.45 + 0.55j] + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='min') + assert_almost_equal(unique, [-1.0, -0.95 + 0.15j, 0.0, 0.45 + 0.55j], + decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='max') + assert_almost_equal(unique, + [-1.0 + 0.05j, -0.90 + 0.15j, 0.0, 0.5 + 0.5j], + decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + unique, multiplicity = unique_roots(p, tol=1e-1, rtype='avg') + assert_almost_equal( + unique, [-1.0 + 0.025j, -0.925 + 0.15j, 0.0, 0.475 + 0.525j], + decimal=15) + assert_equal(multiplicity, [2, 2, 1, 2]) + + def test_gh_4915(self): + p = np.roots(np.convolve(np.ones(5), np.ones(5))) + true_roots = [-(-1)**(1/5), (-1)**(4/5), -(-1)**(3/5), (-1)**(2/5)] + + unique, multiplicity = unique_roots(p) + unique = np.sort(unique) + + assert_almost_equal(np.sort(unique), true_roots, decimal=7) + assert_equal(multiplicity, [2, 2, 2, 2]) + + def test_complex_roots_extra(self): + unique, multiplicity = unique_roots([1.0, 1.0j, 1.0]) + assert_almost_equal(unique, [1.0, 1.0j], decimal=15) + assert_equal(multiplicity, [2, 1]) + + unique, multiplicity = unique_roots([1, 1 + 2e-9, 1e-9 + 1j], tol=0.1) + assert_almost_equal(unique, [1.0, 1e-9 + 1.0j], decimal=15) + assert_equal(multiplicity, [2, 1]) + + def test_single_unique_root(self): + p = np.random.rand(100) + 1j * np.random.rand(100) + unique, multiplicity = unique_roots(p, 2) + assert_almost_equal(unique, [np.min(p)], decimal=15) + assert_equal(multiplicity, [100]) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_spectral.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_spectral.py new file mode 100644 index 0000000000000000000000000000000000000000..ed0af49b2ef8901f3c8b073f4d19def5578d0dbe --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_spectral.py @@ -0,0 +1,1713 @@ +import sys + +import numpy as np +from numpy.testing import (assert_, assert_approx_equal, + assert_allclose, assert_array_equal, assert_equal, + assert_array_almost_equal_nulp, suppress_warnings) +import pytest +from pytest import raises as assert_raises + +from scipy import signal +from scipy.fft import fftfreq, rfftfreq, fft, irfft +from scipy.integrate import trapezoid +from scipy.signal import (periodogram, welch, lombscargle, coherence, + spectrogram, check_COLA, check_NOLA) +from scipy.signal.windows import hann +from scipy.signal._spectral_py import _spectral_helper + +# Compare ShortTimeFFT.stft() / ShortTimeFFT.istft() with stft() / istft(): +from scipy.signal.tests._scipy_spectral_test_shim import stft_compare as stft +from scipy.signal.tests._scipy_spectral_test_shim import istft_compare as istft +from scipy.signal.tests._scipy_spectral_test_shim import csd_compare as csd + + +class TestPeriodogram: + def test_real_onesided_even(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.linspace(0, 0.5, 9)) + q = np.ones(9) + q[0] = 0 + q[-1] /= 2.0 + q /= 8 + assert_allclose(p, q) + + def test_real_onesided_odd(self): + x = np.zeros(15) + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.arange(8.0)/15.0) + q = np.ones(8) + q[0] = 0 + q *= 2.0/15.0 + assert_allclose(p, q, atol=1e-15) + + def test_real_twosided(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x, return_onesided=False) + assert_allclose(f, fftfreq(16, 1.0)) + q = np.full(16, 1/16.0) + q[0] = 0 + assert_allclose(p, q) + + def test_real_spectrum(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x, scaling='spectrum') + g, q = periodogram(x, scaling='density') + assert_allclose(f, np.linspace(0, 0.5, 9)) + assert_allclose(p, q/16.0) + + def test_integer_even(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.linspace(0, 0.5, 9)) + q = np.ones(9) + q[0] = 0 + q[-1] /= 2.0 + q /= 8 + assert_allclose(p, q) + + def test_integer_odd(self): + x = np.zeros(15, dtype=int) + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.arange(8.0)/15.0) + q = np.ones(8) + q[0] = 0 + q *= 2.0/15.0 + assert_allclose(p, q, atol=1e-15) + + def test_integer_twosided(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + f, p = periodogram(x, return_onesided=False) + assert_allclose(f, fftfreq(16, 1.0)) + q = np.full(16, 1/16.0) + q[0] = 0 + assert_allclose(p, q) + + def test_complex(self): + x = np.zeros(16, np.complex128) + x[0] = 1.0 + 2.0j + f, p = periodogram(x, return_onesided=False) + assert_allclose(f, fftfreq(16, 1.0)) + q = np.full(16, 5.0/16.0) + q[0] = 0 + assert_allclose(p, q) + + def test_unk_scaling(self): + assert_raises(ValueError, periodogram, np.zeros(4, np.complex128), + scaling='foo') + + @pytest.mark.skipif( + sys.maxsize <= 2**32, + reason="On some 32-bit tolerance issue" + ) + def test_nd_axis_m1(self): + x = np.zeros(20, dtype=np.float64) + x = x.reshape((2,1,10)) + x[:,:,0] = 1.0 + f, p = periodogram(x) + assert_array_equal(p.shape, (2, 1, 6)) + assert_array_almost_equal_nulp(p[0,0,:], p[1,0,:], 60) + f0, p0 = periodogram(x[0,0,:]) + assert_array_almost_equal_nulp(p0[np.newaxis,:], p[1,:], 60) + + @pytest.mark.skipif( + sys.maxsize <= 2**32, + reason="On some 32-bit tolerance issue" + ) + def test_nd_axis_0(self): + x = np.zeros(20, dtype=np.float64) + x = x.reshape((10,2,1)) + x[0,:,:] = 1.0 + f, p = periodogram(x, axis=0) + assert_array_equal(p.shape, (6,2,1)) + assert_array_almost_equal_nulp(p[:,0,0], p[:,1,0], 60) + f0, p0 = periodogram(x[:,0,0]) + assert_array_almost_equal_nulp(p0, p[:,1,0]) + + def test_window_external(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x, 10, 'hann') + win = signal.get_window('hann', 16) + fe, pe = periodogram(x, 10, win) + assert_array_almost_equal_nulp(p, pe) + assert_array_almost_equal_nulp(f, fe) + win_err = signal.get_window('hann', 32) + assert_raises(ValueError, periodogram, x, + 10, win_err) # win longer than signal + + def test_padded_fft(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x) + fp, pp = periodogram(x, nfft=32) + assert_allclose(f, fp[::2]) + assert_allclose(p, pp[::2]) + assert_array_equal(pp.shape, (17,)) + + def test_empty_input(self): + f, p = periodogram([]) + assert_array_equal(f.shape, (0,)) + assert_array_equal(p.shape, (0,)) + for shape in [(0,), (3,0), (0,5,2)]: + f, p = periodogram(np.empty(shape)) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + def test_empty_input_other_axis(self): + for shape in [(3,0), (0,5,2)]: + f, p = periodogram(np.empty(shape), axis=1) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + def test_short_nfft(self): + x = np.zeros(18) + x[0] = 1 + f, p = periodogram(x, nfft=16) + assert_allclose(f, np.linspace(0, 0.5, 9)) + q = np.ones(9) + q[0] = 0 + q[-1] /= 2.0 + q /= 8 + assert_allclose(p, q) + + def test_nfft_is_xshape(self): + x = np.zeros(16) + x[0] = 1 + f, p = periodogram(x, nfft=16) + assert_allclose(f, np.linspace(0, 0.5, 9)) + q = np.ones(9) + q[0] = 0 + q[-1] /= 2.0 + q /= 8 + assert_allclose(p, q) + + def test_real_onesided_even_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.linspace(0, 0.5, 9)) + q = np.ones(9, 'f') + q[0] = 0 + q[-1] /= 2.0 + q /= 8 + assert_allclose(p, q) + assert_(p.dtype == q.dtype) + + def test_real_onesided_odd_32(self): + x = np.zeros(15, 'f') + x[0] = 1 + f, p = periodogram(x) + assert_allclose(f, np.arange(8.0)/15.0) + q = np.ones(8, 'f') + q[0] = 0 + q *= 2.0/15.0 + assert_allclose(p, q, atol=1e-7) + assert_(p.dtype == q.dtype) + + def test_real_twosided_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + f, p = periodogram(x, return_onesided=False) + assert_allclose(f, fftfreq(16, 1.0)) + q = np.full(16, 1/16.0, 'f') + q[0] = 0 + assert_allclose(p, q) + assert_(p.dtype == q.dtype) + + def test_complex_32(self): + x = np.zeros(16, 'F') + x[0] = 1.0 + 2.0j + f, p = periodogram(x, return_onesided=False) + assert_allclose(f, fftfreq(16, 1.0)) + q = np.full(16, 5.0/16.0, 'f') + q[0] = 0 + assert_allclose(p, q) + assert_(p.dtype == q.dtype) + + def test_shorter_window_error(self): + x = np.zeros(16) + x[0] = 1 + win = signal.get_window('hann', 10) + expected_msg = ('the size of the window must be the same size ' + 'of the input on the specified axis') + with assert_raises(ValueError, match=expected_msg): + periodogram(x, window=win) + + +class TestWelch: + def test_real_onesided_even(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_onesided_odd(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113, + 0.17072113]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_twosided(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.07638889]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_spectrum(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8, scaling='spectrum') + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667, + 0.02083333]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_onesided_even(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_onesided_odd(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113, + 0.17072113]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_twosided(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.07638889]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_complex(self): + x = np.zeros(16, np.complex128) + x[0] = 1.0 + 2.0j + x[8] = 1.0 + 2.0j + f, p = welch(x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556, + 0.55555556, 0.55555556, 0.55555556, 0.38194444]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_unk_scaling(self): + assert_raises(ValueError, welch, np.zeros(4, np.complex128), + scaling='foo', nperseg=4) + + def test_detrend_linear(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f, p = welch(x, nperseg=10, detrend='linear') + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_no_detrending(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f1, p1 = welch(x, nperseg=10, detrend=False) + f2, p2 = welch(x, nperseg=10, detrend=lambda x: x) + assert_allclose(f1, f2, atol=1e-15) + assert_allclose(p1, p2, atol=1e-15) + + def test_detrend_external(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f, p = welch(x, nperseg=10, + detrend=lambda seg: signal.detrend(seg, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_detrend_external_nd_m1(self): + x = np.arange(40, dtype=np.float64) + 0.04 + x = x.reshape((2,2,10)) + f, p = welch(x, nperseg=10, + detrend=lambda seg: signal.detrend(seg, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_detrend_external_nd_0(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((2,1,10)) + x = np.moveaxis(x, 2, 0) + f, p = welch(x, nperseg=10, axis=0, + detrend=lambda seg: signal.detrend(seg, axis=0, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_nd_axis_m1(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((2,1,10)) + f, p = welch(x, nperseg=10) + assert_array_equal(p.shape, (2, 1, 6)) + assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13) + f0, p0 = welch(x[0,0,:], nperseg=10) + assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13) + + def test_nd_axis_0(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((10,2,1)) + f, p = welch(x, nperseg=10, axis=0) + assert_array_equal(p.shape, (6,2,1)) + assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13) + f0, p0 = welch(x[:,0,0], nperseg=10) + assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13) + + def test_window_external(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, 10, 'hann', nperseg=8) + win = signal.get_window('hann', 8) + fe, pe = welch(x, 10, win, nperseg=None) + assert_array_almost_equal_nulp(p, pe) + assert_array_almost_equal_nulp(f, fe) + assert_array_equal(fe.shape, (5,)) # because win length used as nperseg + assert_array_equal(pe.shape, (5,)) + assert_raises(ValueError, welch, x, + 10, win, nperseg=4) # because nperseg != win.shape[-1] + win_err = signal.get_window('hann', 32) + assert_raises(ValueError, welch, x, + 10, win_err, nperseg=None) # win longer than signal + + def test_empty_input(self): + f, p = welch([]) + assert_array_equal(f.shape, (0,)) + assert_array_equal(p.shape, (0,)) + for shape in [(0,), (3,0), (0,5,2)]: + f, p = welch(np.empty(shape)) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + def test_empty_input_other_axis(self): + for shape in [(3,0), (0,5,2)]: + f, p = welch(np.empty(shape), axis=1) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + def test_short_data(self): + x = np.zeros(8) + x[0] = 1 + #for string-like window, input signal length < nperseg value gives + #UserWarning, sets nperseg to x.shape[-1] + with suppress_warnings() as sup: + msg = "nperseg = 256 is greater than input length = 8, using nperseg = 8" + sup.filter(UserWarning, msg) + f, p = welch(x,window='hann') # default nperseg + f1, p1 = welch(x,window='hann', nperseg=256) # user-specified nperseg + f2, p2 = welch(x, nperseg=8) # valid nperseg, doesn't give warning + assert_allclose(f, f2) + assert_allclose(p, p2) + assert_allclose(f1, f2) + assert_allclose(p1, p2) + + def test_window_long_or_nd(self): + assert_raises(ValueError, welch, np.zeros(4), 1, np.array([1,1,1,1,1])) + assert_raises(ValueError, welch, np.zeros(4), 1, + np.arange(6).reshape((2,3))) + + def test_nondefault_noverlap(self): + x = np.zeros(64) + x[::8] = 1 + f, p = welch(x, nperseg=16, noverlap=4) + q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5., + 1./6.]) + assert_allclose(p, q, atol=1e-12) + + def test_bad_noverlap(self): + assert_raises(ValueError, welch, np.zeros(4), 1, 'hann', 2, 7) + + def test_nfft_too_short(self): + assert_raises(ValueError, welch, np.ones(12), nfft=3, nperseg=4) + + def test_real_onesided_even_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_real_onesided_odd_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116, + 0.17072113], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_real_twosided_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.11111111, + 0.07638889], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_complex_32(self): + x = np.zeros(16, 'F') + x[0] = 1.0 + 2.0j + x[8] = 1.0 + 2.0j + f, p = welch(x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552, + 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype, + f'dtype mismatch, {p.dtype}, {q.dtype}') + + def test_padded_freqs(self): + x = np.zeros(12) + + nfft = 24 + f = fftfreq(nfft, 1.0)[:nfft//2+1] + f[-1] *= -1 + fodd, _ = welch(x, nperseg=5, nfft=nfft) + feven, _ = welch(x, nperseg=6, nfft=nfft) + assert_allclose(f, fodd) + assert_allclose(f, feven) + + nfft = 25 + f = fftfreq(nfft, 1.0)[:(nfft + 1)//2] + fodd, _ = welch(x, nperseg=5, nfft=nfft) + feven, _ = welch(x, nperseg=6, nfft=nfft) + assert_allclose(f, fodd) + assert_allclose(f, feven) + + def test_window_correction(self): + A = 20 + fs = 1e4 + nperseg = int(fs//10) + fsig = 300 + ii = int(fsig*nperseg//fs) # Freq index of fsig + + tt = np.arange(fs)/fs + x = A*np.sin(2*np.pi*fsig*tt) + + for window in ['hann', 'bartlett', ('tukey', 0.1), 'flattop']: + _, p_spec = welch(x, fs=fs, nperseg=nperseg, window=window, + scaling='spectrum') + freq, p_dens = welch(x, fs=fs, nperseg=nperseg, window=window, + scaling='density') + + # Check peak height at signal frequency for 'spectrum' + assert_allclose(p_spec[ii], A**2/2.0) + # Check integrated spectrum RMS for 'density' + assert_allclose(np.sqrt(trapezoid(p_dens, freq)), A*np.sqrt(2)/2, + rtol=1e-3) + + def test_axis_rolling(self): + np.random.seed(1234) + + x_flat = np.random.randn(1024) + _, p_flat = welch(x_flat) + + for a in range(3): + newshape = [1,]*3 + newshape[a] = -1 + x = x_flat.reshape(newshape) + + _, p_plus = welch(x, axis=a) # Positive axis index + _, p_minus = welch(x, axis=a-x.ndim) # Negative axis index + + assert_equal(p_flat, p_plus.squeeze(), err_msg=a) + assert_equal(p_flat, p_minus.squeeze(), err_msg=a-x.ndim) + + def test_average(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = welch(x, nperseg=8, average='median') + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([.1, .05, 0., 1.54074396e-33, 0.]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + assert_raises(ValueError, welch, x, nperseg=8, + average='unrecognised-average') + + +class TestCSD: + def test_pad_shorter_x(self): + x = np.zeros(8) + y = np.zeros(12) + + f = np.linspace(0, 0.5, 7) + c = np.zeros(7,dtype=np.complex128) + f1, c1 = csd(x, y, nperseg=12) + + assert_allclose(f, f1) + assert_allclose(c, c1) + + def test_pad_shorter_y(self): + x = np.zeros(12) + y = np.zeros(8) + + f = np.linspace(0, 0.5, 7) + c = np.zeros(7,dtype=np.complex128) + f1, c1 = csd(x, y, nperseg=12) + + assert_allclose(f, f1) + assert_allclose(c, c1) + + def test_real_onesided_even(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_onesided_odd(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113, + 0.17072113]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_twosided(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.07638889]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_real_spectrum(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8, scaling='spectrum') + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667, + 0.02083333]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_onesided_even(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_onesided_odd(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113, + 0.17072113]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_integer_twosided(self): + x = np.zeros(16, dtype=int) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.07638889]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_complex(self): + x = np.zeros(16, np.complex128) + x[0] = 1.0 + 2.0j + x[8] = 1.0 + 2.0j + f, p = csd(x, x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556, + 0.55555556, 0.55555556, 0.55555556, 0.38194444]) + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + + def test_unk_scaling(self): + assert_raises(ValueError, csd, np.zeros(4, np.complex128), + np.ones(4, np.complex128), scaling='foo', nperseg=4) + + def test_detrend_linear(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f, p = csd(x, x, nperseg=10, detrend='linear') + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_no_detrending(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f1, p1 = csd(x, x, nperseg=10, detrend=False) + f2, p2 = csd(x, x, nperseg=10, detrend=lambda x: x) + assert_allclose(f1, f2, atol=1e-15) + assert_allclose(p1, p2, atol=1e-15) + + def test_detrend_external(self): + x = np.arange(10, dtype=np.float64) + 0.04 + f, p = csd(x, x, nperseg=10, + detrend=lambda seg: signal.detrend(seg, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_detrend_external_nd_m1(self): + x = np.arange(40, dtype=np.float64) + 0.04 + x = x.reshape((2,2,10)) + f, p = csd(x, x, nperseg=10, + detrend=lambda seg: signal.detrend(seg, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_detrend_external_nd_0(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((2,1,10)) + x = np.moveaxis(x, 2, 0) + f, p = csd(x, x, nperseg=10, axis=0, + detrend=lambda seg: signal.detrend(seg, axis=0, type='l')) + assert_allclose(p, np.zeros_like(p), atol=1e-15) + + def test_nd_axis_m1(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((2,1,10)) + f, p = csd(x, x, nperseg=10) + assert_array_equal(p.shape, (2, 1, 6)) + assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13) + f0, p0 = csd(x[0,0,:], x[0,0,:], nperseg=10) + assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13) + + def test_nd_axis_0(self): + x = np.arange(20, dtype=np.float64) + 0.04 + x = x.reshape((10,2,1)) + f, p = csd(x, x, nperseg=10, axis=0) + assert_array_equal(p.shape, (6,2,1)) + assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13) + f0, p0 = csd(x[:,0,0], x[:,0,0], nperseg=10) + assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13) + + def test_window_external(self): + x = np.zeros(16) + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, 10, 'hann', 8) + win = signal.get_window('hann', 8) + fe, pe = csd(x, x, 10, win, nperseg=None) + assert_array_almost_equal_nulp(p, pe) + assert_array_almost_equal_nulp(f, fe) + assert_array_equal(fe.shape, (5,)) # because win length used as nperseg + assert_array_equal(pe.shape, (5,)) + assert_raises(ValueError, csd, x, x, + 10, win, nperseg=256) # because nperseg != win.shape[-1] + win_err = signal.get_window('hann', 32) + assert_raises(ValueError, csd, x, x, + 10, win_err, nperseg=None) # because win longer than signal + + def test_empty_input(self): + f, p = csd([],np.zeros(10)) + assert_array_equal(f.shape, (0,)) + assert_array_equal(p.shape, (0,)) + + f, p = csd(np.zeros(10),[]) + assert_array_equal(f.shape, (0,)) + assert_array_equal(p.shape, (0,)) + + for shape in [(0,), (3,0), (0,5,2)]: + f, p = csd(np.empty(shape), np.empty(shape)) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + f, p = csd(np.ones(10), np.empty((5,0))) + assert_array_equal(f.shape, (5,0)) + assert_array_equal(p.shape, (5,0)) + + f, p = csd(np.empty((5,0)), np.ones(10)) + assert_array_equal(f.shape, (5,0)) + assert_array_equal(p.shape, (5,0)) + + def test_empty_input_other_axis(self): + for shape in [(3,0), (0,5,2)]: + f, p = csd(np.empty(shape), np.empty(shape), axis=1) + assert_array_equal(f.shape, shape) + assert_array_equal(p.shape, shape) + + f, p = csd(np.empty((10,10,3)), np.zeros((10,0,1)), axis=1) + assert_array_equal(f.shape, (10,0,3)) + assert_array_equal(p.shape, (10,0,3)) + + f, p = csd(np.empty((10,0,1)), np.zeros((10,10,3)), axis=1) + assert_array_equal(f.shape, (10,0,3)) + assert_array_equal(p.shape, (10,0,3)) + + def test_short_data(self): + x = np.zeros(8) + x[0] = 1 + + #for string-like window, input signal length < nperseg value gives + #UserWarning, sets nperseg to x.shape[-1] + with suppress_warnings() as sup: + msg = "nperseg = 256 is greater than input length = 8, using nperseg = 8" + sup.filter(UserWarning, msg) + f, p = csd(x, x, window='hann') # default nperseg + f1, p1 = csd(x, x, window='hann', nperseg=256) # user-specified nperseg + f2, p2 = csd(x, x, nperseg=8) # valid nperseg, doesn't give warning + assert_allclose(f, f2) + assert_allclose(p, p2) + assert_allclose(f1, f2) + assert_allclose(p1, p2) + + def test_window_long_or_nd(self): + assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1, + np.array([1,1,1,1,1])) + assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1, + np.arange(6).reshape((2,3))) + + def test_nondefault_noverlap(self): + x = np.zeros(64) + x[::8] = 1 + f, p = csd(x, x, nperseg=16, noverlap=4) + q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5., + 1./6.]) + assert_allclose(p, q, atol=1e-12) + + def test_bad_noverlap(self): + assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1, 'hann', + 2, 7) + + def test_nfft_too_short(self): + assert_raises(ValueError, csd, np.ones(12), np.zeros(12), nfft=3, + nperseg=4) + + def test_real_onesided_even_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8) + assert_allclose(f, np.linspace(0, 0.5, 5)) + q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222, + 0.11111111], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_real_onesided_odd_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=9) + assert_allclose(f, np.arange(5.0)/9.0) + q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116, + 0.17072113], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_real_twosided_32(self): + x = np.zeros(16, 'f') + x[0] = 1 + x[8] = 1 + f, p = csd(x, x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.08333333, 0.07638889, 0.11111111, + 0.11111111, 0.11111111, 0.11111111, 0.11111111, + 0.07638889], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype) + + def test_complex_32(self): + x = np.zeros(16, 'F') + x[0] = 1.0 + 2.0j + x[8] = 1.0 + 2.0j + f, p = csd(x, x, nperseg=8, return_onesided=False) + assert_allclose(f, fftfreq(8, 1.0)) + q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552, + 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f') + assert_allclose(p, q, atol=1e-7, rtol=1e-7) + assert_(p.dtype == q.dtype, + f'dtype mismatch, {p.dtype}, {q.dtype}') + + def test_padded_freqs(self): + x = np.zeros(12) + y = np.ones(12) + + nfft = 24 + f = fftfreq(nfft, 1.0)[:nfft//2+1] + f[-1] *= -1 + fodd, _ = csd(x, y, nperseg=5, nfft=nfft) + feven, _ = csd(x, y, nperseg=6, nfft=nfft) + assert_allclose(f, fodd) + assert_allclose(f, feven) + + nfft = 25 + f = fftfreq(nfft, 1.0)[:(nfft + 1)//2] + fodd, _ = csd(x, y, nperseg=5, nfft=nfft) + feven, _ = csd(x, y, nperseg=6, nfft=nfft) + assert_allclose(f, fodd) + assert_allclose(f, feven) + + def test_copied_data(self): + x = np.random.randn(64) + y = x.copy() + + _, p_same = csd(x, x, nperseg=8, average='mean', + return_onesided=False) + _, p_copied = csd(x, y, nperseg=8, average='mean', + return_onesided=False) + assert_allclose(p_same, p_copied) + + _, p_same = csd(x, x, nperseg=8, average='median', + return_onesided=False) + _, p_copied = csd(x, y, nperseg=8, average='median', + return_onesided=False) + assert_allclose(p_same, p_copied) + + +class TestCoherence: + def test_identical_input(self): + x = np.random.randn(20) + y = np.copy(x) # So `y is x` -> False + + f = np.linspace(0, 0.5, 6) + C = np.ones(6) + f1, C1 = coherence(x, y, nperseg=10) + + assert_allclose(f, f1) + assert_allclose(C, C1) + + def test_phase_shifted_input(self): + x = np.random.randn(20) + y = -x + + f = np.linspace(0, 0.5, 6) + C = np.ones(6) + f1, C1 = coherence(x, y, nperseg=10) + + assert_allclose(f, f1) + assert_allclose(C, C1) + + +class TestSpectrogram: + def test_average_all_segments(self): + x = np.random.randn(1024) + + fs = 1.0 + window = ('tukey', 0.25) + nperseg = 16 + noverlap = 2 + + f, _, P = spectrogram(x, fs, window, nperseg, noverlap) + fw, Pw = welch(x, fs, window, nperseg, noverlap) + assert_allclose(f, fw) + assert_allclose(np.mean(P, axis=-1), Pw) + + def test_window_external(self): + x = np.random.randn(1024) + + fs = 1.0 + window = ('tukey', 0.25) + nperseg = 16 + noverlap = 2 + f, _, P = spectrogram(x, fs, window, nperseg, noverlap) + + win = signal.get_window(('tukey', 0.25), 16) + fe, _, Pe = spectrogram(x, fs, win, nperseg=None, noverlap=2) + assert_array_equal(fe.shape, (9,)) # because win length used as nperseg + assert_array_equal(Pe.shape, (9,73)) + assert_raises(ValueError, spectrogram, x, + fs, win, nperseg=8) # because nperseg != win.shape[-1] + win_err = signal.get_window(('tukey', 0.25), 2048) + assert_raises(ValueError, spectrogram, x, + fs, win_err, nperseg=None) # win longer than signal + + def test_short_data(self): + x = np.random.randn(1024) + fs = 1.0 + + #for string-like window, input signal length < nperseg value gives + #UserWarning, sets nperseg to x.shape[-1] + f, _, p = spectrogram(x, fs, window=('tukey',0.25)) # default nperseg + with suppress_warnings() as sup: + sup.filter(UserWarning, + "nperseg = 1025 is greater than input length = 1024, " + "using nperseg = 1024",) + f1, _, p1 = spectrogram(x, fs, window=('tukey',0.25), + nperseg=1025) # user-specified nperseg + f2, _, p2 = spectrogram(x, fs, nperseg=256) # to compare w/default + f3, _, p3 = spectrogram(x, fs, nperseg=1024) # compare w/user-spec'd + assert_allclose(f, f2) + assert_allclose(p, p2) + assert_allclose(f1, f3) + assert_allclose(p1, p3) + +class TestLombscargle: + def test_frequency(self): + """Test if frequency location of peak corresponds to frequency of + generated input signal. + """ + + # Input parameters + ampl = 2. + w = 1. + phi = 0.5 * np.pi + nin = 100 + nout = 1000 + p = 0.7 # Fraction of points to select + + # Randomly select a fraction of an array with timesteps + np.random.seed(2353425) + r = np.random.rand(nin) + t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p] + + # Plot a sine wave for the selected times + x = ampl * np.sin(w*t + phi) + + # Define the array of frequencies for which to compute the periodogram + f = np.linspace(0.01, 10., nout) + + # Calculate Lomb-Scargle periodogram + P = lombscargle(t, x, f) + + # Check if difference between found frequency maximum and input + # frequency is less than accuracy + delta = f[1] - f[0] + assert_(w - f[np.argmax(P)] < (delta/2.)) + + def test_amplitude(self): + # Test if height of peak in normalized Lomb-Scargle periodogram + # corresponds to amplitude of the generated input signal. + + # Input parameters + ampl = 2. + w = 1. + phi = 0.5 * np.pi + nin = 100 + nout = 1000 + p = 0.7 # Fraction of points to select + + # Randomly select a fraction of an array with timesteps + np.random.seed(2353425) + r = np.random.rand(nin) + t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p] + + # Plot a sine wave for the selected times + x = ampl * np.sin(w*t + phi) + + # Define the array of frequencies for which to compute the periodogram + f = np.linspace(0.01, 10., nout) + + # Calculate Lomb-Scargle periodogram + pgram = lombscargle(t, x, f) + + # Normalize + pgram = np.sqrt(4 * pgram / t.shape[0]) + + # Check if difference between found frequency maximum and input + # frequency is less than accuracy + assert_approx_equal(np.max(pgram), ampl, significant=2) + + def test_precenter(self): + # Test if precenter gives the same result as manually precentering. + + # Input parameters + ampl = 2. + w = 1. + phi = 0.5 * np.pi + nin = 100 + nout = 1000 + p = 0.7 # Fraction of points to select + offset = 0.15 # Offset to be subtracted in pre-centering + + # Randomly select a fraction of an array with timesteps + np.random.seed(2353425) + r = np.random.rand(nin) + t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p] + + # Plot a sine wave for the selected times + x = ampl * np.sin(w*t + phi) + offset + + # Define the array of frequencies for which to compute the periodogram + f = np.linspace(0.01, 10., nout) + + # Calculate Lomb-Scargle periodogram + pgram = lombscargle(t, x, f, precenter=True) + pgram2 = lombscargle(t, x - x.mean(), f, precenter=False) + + # check if centering worked + assert_allclose(pgram, pgram2) + + def test_normalize(self): + # Test normalize option of Lomb-Scarge. + + # Input parameters + ampl = 2. + w = 1. + phi = 0.5 * np.pi + nin = 100 + nout = 1000 + p = 0.7 # Fraction of points to select + + # Randomly select a fraction of an array with timesteps + np.random.seed(2353425) + r = np.random.rand(nin) + t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p] + + # Plot a sine wave for the selected times + x = ampl * np.sin(w*t + phi) + + # Define the array of frequencies for which to compute the periodogram + f = np.linspace(0.01, 10., nout) + + # Calculate Lomb-Scargle periodogram + pgram = lombscargle(t, x, f) + pgram2 = lombscargle(t, x, f, normalize=True) + + # check if normalization works as expected + assert_allclose(pgram * 2 / np.dot(x, x), pgram2) + assert_approx_equal(np.max(pgram2), 1.0, significant=2) + + def test_wrong_shape(self): + t = np.linspace(0, 1, 1) + x = np.linspace(0, 1, 2) + f = np.linspace(0, 1, 3) + assert_raises(ValueError, lombscargle, t, x, f) + + def test_zero_division(self): + t = np.zeros(1) + x = np.zeros(1) + f = np.zeros(1) + assert_raises(ZeroDivisionError, lombscargle, t, x, f) + + def test_lombscargle_atan_vs_atan2(self): + # https://github.com/scipy/scipy/issues/3787 + # This raised a ZeroDivisionError. + t = np.linspace(0, 10, 1000, endpoint=False) + x = np.sin(4*t) + f = np.linspace(0, 50, 500, endpoint=False) + 0.1 + lombscargle(t, x, f*2*np.pi) + + +class TestSTFT: + def test_input_validation(self): + + def chk_VE(match): + """Assert for a ValueError matching regexp `match`. + + This little wrapper allows a more concise code layout. + """ + return pytest.raises(ValueError, match=match) + + # Checks for check_COLA(): + with chk_VE('nperseg must be a positive integer'): + check_COLA('hann', -10, 0) + with chk_VE('noverlap must be less than nperseg.'): + check_COLA('hann', 10, 20) + with chk_VE('window must be 1-D'): + check_COLA(np.ones((2, 2)), 10, 0) + with chk_VE('window must have length of nperseg'): + check_COLA(np.ones(20), 10, 0) + + # Checks for check_NOLA(): + with chk_VE('nperseg must be a positive integer'): + check_NOLA('hann', -10, 0) + with chk_VE('noverlap must be less than nperseg'): + check_NOLA('hann', 10, 20) + with chk_VE('window must be 1-D'): + check_NOLA(np.ones((2, 2)), 10, 0) + with chk_VE('window must have length of nperseg'): + check_NOLA(np.ones(20), 10, 0) + with chk_VE('noverlap must be a nonnegative integer'): + check_NOLA('hann', 64, -32) + + x = np.zeros(1024) + z = stft(x)[2] + + # Checks for stft(): + with chk_VE('window must be 1-D'): + stft(x, window=np.ones((2, 2))) + with chk_VE('value specified for nperseg is different ' + + 'from length of window'): + stft(x, window=np.ones(10), nperseg=256) + with chk_VE('nperseg must be a positive integer'): + stft(x, nperseg=-256) + with chk_VE('noverlap must be less than nperseg.'): + stft(x, nperseg=256, noverlap=1024) + with chk_VE('nfft must be greater than or equal to nperseg.'): + stft(x, nperseg=256, nfft=8) + + # Checks for istft(): + with chk_VE('Input stft must be at least 2d!'): + istft(x) + with chk_VE('window must be 1-D'): + istft(z, window=np.ones((2, 2))) + with chk_VE('window must have length of 256'): + istft(z, window=np.ones(10), nperseg=256) + with chk_VE('nperseg must be a positive integer'): + istft(z, nperseg=-256) + with chk_VE('noverlap must be less than nperseg.'): + istft(z, nperseg=256, noverlap=1024) + with chk_VE('nfft must be greater than or equal to nperseg.'): + istft(z, nperseg=256, nfft=8) + with pytest.warns(UserWarning, match="NOLA condition failed, " + + "STFT may not be invertible"): + istft(z, nperseg=256, noverlap=0, window='hann') + with chk_VE('Must specify differing time and frequency axes!'): + istft(z, time_axis=0, freq_axis=0) + + # Checks for _spectral_helper(): + with chk_VE("Unknown value for mode foo, must be one of: " + + r"\{'psd', 'stft'\}"): + _spectral_helper(x, x, mode='foo') + with chk_VE("x and y must be equal if mode is 'stft'"): + _spectral_helper(x[:512], x[512:], mode='stft') + with chk_VE("Unknown boundary option 'foo', must be one of: " + + r"\['even', 'odd', 'constant', 'zeros', None\]"): + _spectral_helper(x, x, boundary='foo') + + scaling = "not_valid" + with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"): + stft(x, scaling=scaling) + with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"): + istft(z, scaling=scaling) + + def test_check_COLA(self): + settings = [ + ('boxcar', 10, 0), + ('boxcar', 10, 9), + ('bartlett', 51, 26), + ('hann', 256, 128), + ('hann', 256, 192), + ('blackman', 300, 200), + (('tukey', 0.5), 256, 64), + ('hann', 256, 255), + ] + + for setting in settings: + msg = '{}, {}, {}'.format(*setting) + assert_equal(True, check_COLA(*setting), err_msg=msg) + + def test_check_NOLA(self): + settings_pass = [ + ('boxcar', 10, 0), + ('boxcar', 10, 9), + ('boxcar', 10, 7), + ('bartlett', 51, 26), + ('bartlett', 51, 10), + ('hann', 256, 128), + ('hann', 256, 192), + ('hann', 256, 37), + ('blackman', 300, 200), + ('blackman', 300, 123), + (('tukey', 0.5), 256, 64), + (('tukey', 0.5), 256, 38), + ('hann', 256, 255), + ('hann', 256, 39), + ] + for setting in settings_pass: + msg = '{}, {}, {}'.format(*setting) + assert_equal(True, check_NOLA(*setting), err_msg=msg) + + w_fail = np.ones(16) + w_fail[::2] = 0 + settings_fail = [ + (w_fail, len(w_fail), len(w_fail) // 2), + ('hann', 64, 0), + ] + for setting in settings_fail: + msg = '{}, {}, {}'.format(*setting) + assert_equal(False, check_NOLA(*setting), err_msg=msg) + + def test_average_all_segments(self): + np.random.seed(1234) + x = np.random.randn(1024) + + fs = 1.0 + window = 'hann' + nperseg = 16 + noverlap = 8 + + # Compare twosided, because onesided welch doubles non-DC terms to + # account for power at negative frequencies. stft doesn't do this, + # because it breaks invertibility. + f, _, Z = stft(x, fs, window, nperseg, noverlap, padded=False, + return_onesided=False, boundary=None) + fw, Pw = welch(x, fs, window, nperseg, noverlap, return_onesided=False, + scaling='spectrum', detrend=False) + + assert_allclose(f, fw) + assert_allclose(np.mean(np.abs(Z)**2, axis=-1), Pw) + + def test_permute_axes(self): + np.random.seed(1234) + x = np.random.randn(1024) + + fs = 1.0 + window = 'hann' + nperseg = 16 + noverlap = 8 + + f1, t1, Z1 = stft(x, fs, window, nperseg, noverlap) + f2, t2, Z2 = stft(x.reshape((-1, 1, 1)), fs, window, nperseg, noverlap, + axis=0) + + t3, x1 = istft(Z1, fs, window, nperseg, noverlap) + t4, x2 = istft(Z2.T, fs, window, nperseg, noverlap, time_axis=0, + freq_axis=-1) + + assert_allclose(f1, f2) + assert_allclose(t1, t2) + assert_allclose(t3, t4) + assert_allclose(Z1, Z2[:, 0, 0, :]) + assert_allclose(x1, x2[:, 0, 0]) + + @pytest.mark.parametrize('scaling', ['spectrum', 'psd']) + def test_roundtrip_real(self, scaling): + np.random.seed(1234) + + settings = [ + ('boxcar', 100, 10, 0), # Test no overlap + ('boxcar', 100, 10, 9), # Test high overlap + ('bartlett', 101, 51, 26), # Test odd nperseg + ('hann', 1024, 256, 128), # Test defaults + (('tukey', 0.5), 1152, 256, 64), # Test Tukey + ('hann', 1024, 256, 255), # Test overlapped hann + ] + + for window, N, nperseg, noverlap in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=False, + scaling=scaling) + + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window, scaling=scaling) + + msg = f'{window}, {noverlap}' + assert_allclose(t, tr, err_msg=msg) + assert_allclose(x, xr, err_msg=msg) + + def test_roundtrip_not_nola(self): + np.random.seed(1234) + + w_fail = np.ones(16) + w_fail[::2] = 0 + settings = [ + (w_fail, 256, len(w_fail), len(w_fail) // 2), + ('hann', 256, 64, 0), + ] + + for window, N, nperseg, noverlap in settings: + msg = f'{window}, {N}, {nperseg}, {noverlap}' + assert not check_NOLA(window, nperseg, noverlap), msg + + t = np.arange(N) + x = 10 * np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=True, + boundary='zeros') + with pytest.warns(UserWarning, match='NOLA'): + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window, boundary=True) + + assert np.allclose(t, tr[:len(t)]), msg + assert not np.allclose(x, xr[:len(x)]), msg + + def test_roundtrip_nola_not_cola(self): + np.random.seed(1234) + + settings = [ + ('boxcar', 100, 10, 3), # NOLA True, COLA False + ('bartlett', 101, 51, 37), # NOLA True, COLA False + ('hann', 1024, 256, 127), # NOLA True, COLA False + (('tukey', 0.5), 1152, 256, 14), # NOLA True, COLA False + ('hann', 1024, 256, 5), # NOLA True, COLA False + ] + + for window, N, nperseg, noverlap in settings: + msg = f'{window}, {nperseg}, {noverlap}' + assert check_NOLA(window, nperseg, noverlap), msg + assert not check_COLA(window, nperseg, noverlap), msg + + t = np.arange(N) + x = 10 * np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=True, + boundary='zeros') + + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window, boundary=True) + + msg = f'{window}, {noverlap}' + assert_allclose(t, tr[:len(t)], err_msg=msg) + assert_allclose(x, xr[:len(x)], err_msg=msg) + + def test_roundtrip_float32(self): + np.random.seed(1234) + + settings = [('hann', 1024, 256, 128)] + + for window, N, nperseg, noverlap in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + x = x.astype(np.float32) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=False) + + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window) + + msg = f'{window}, {noverlap}' + assert_allclose(t, t, err_msg=msg) + assert_allclose(x, xr, err_msg=msg, rtol=1e-4, atol=1e-5) + assert_(x.dtype == xr.dtype) + + @pytest.mark.parametrize('scaling', ['spectrum', 'psd']) + def test_roundtrip_complex(self, scaling): + np.random.seed(1234) + + settings = [ + ('boxcar', 100, 10, 0), # Test no overlap + ('boxcar', 100, 10, 9), # Test high overlap + ('bartlett', 101, 51, 26), # Test odd nperseg + ('hann', 1024, 256, 128), # Test defaults + (('tukey', 0.5), 1152, 256, 64), # Test Tukey + ('hann', 1024, 256, 255), # Test overlapped hann + ] + + for window, N, nperseg, noverlap in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + 10j*np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=False, + return_onesided=False, scaling=scaling) + + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window, input_onesided=False, + scaling=scaling) + + msg = f'{window}, {nperseg}, {noverlap}' + assert_allclose(t, tr, err_msg=msg) + assert_allclose(x, xr, err_msg=msg) + + # Check that asking for onesided switches to twosided + with suppress_warnings() as sup: + sup.filter(UserWarning, + "Input data is complex, switching to return_onesided=False") + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=False, + return_onesided=True, scaling=scaling) + + tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap, + window=window, input_onesided=False, scaling=scaling) + + msg = f'{window}, {nperseg}, {noverlap}' + assert_allclose(t, tr, err_msg=msg) + assert_allclose(x, xr, err_msg=msg) + + def test_roundtrip_boundary_extension(self): + np.random.seed(1234) + + # Test against boxcar, since window is all ones, and thus can be fully + # recovered with no boundary extension + + settings = [ + ('boxcar', 100, 10, 0), # Test no overlap + ('boxcar', 100, 10, 9), # Test high overlap + ] + + for window, N, nperseg, noverlap in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=True, + boundary=None) + + _, xr = istft(zz, noverlap=noverlap, window=window, boundary=False) + + for boundary in ['even', 'odd', 'constant', 'zeros']: + _, _, zz_ext = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=True, + boundary=boundary) + + _, xr_ext = istft(zz_ext, noverlap=noverlap, window=window, + boundary=True) + + msg = f'{window}, {noverlap}, {boundary}' + assert_allclose(x, xr, err_msg=msg) + assert_allclose(x, xr_ext, err_msg=msg) + + def test_roundtrip_padded_signal(self): + np.random.seed(1234) + + settings = [ + ('boxcar', 101, 10, 0), + ('hann', 1000, 256, 128), + ] + + for window, N, nperseg, noverlap in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + + _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap, + window=window, detrend=None, padded=True) + + tr, xr = istft(zz, noverlap=noverlap, window=window) + + msg = f'{window}, {noverlap}' + # Account for possible zero-padding at the end + assert_allclose(t, tr[:t.size], err_msg=msg) + assert_allclose(x, xr[:x.size], err_msg=msg) + + def test_roundtrip_padded_FFT(self): + np.random.seed(1234) + + settings = [ + ('hann', 1024, 256, 128, 512), + ('hann', 1024, 256, 128, 501), + ('boxcar', 100, 10, 0, 33), + (('tukey', 0.5), 1152, 256, 64, 1024), + ] + + for window, N, nperseg, noverlap, nfft in settings: + t = np.arange(N) + x = 10*np.random.randn(t.size) + xc = x*np.exp(1j*np.pi/4) + + # real signal + _, _, z = stft(x, nperseg=nperseg, noverlap=noverlap, nfft=nfft, + window=window, detrend=None, padded=True) + + # complex signal + _, _, zc = stft(xc, nperseg=nperseg, noverlap=noverlap, nfft=nfft, + window=window, detrend=None, padded=True, + return_onesided=False) + + tr, xr = istft(z, nperseg=nperseg, noverlap=noverlap, nfft=nfft, + window=window) + + tr, xcr = istft(zc, nperseg=nperseg, noverlap=noverlap, nfft=nfft, + window=window, input_onesided=False) + + msg = f'{window}, {noverlap}' + assert_allclose(t, tr, err_msg=msg) + assert_allclose(x, xr, err_msg=msg) + assert_allclose(xc, xcr, err_msg=msg) + + def test_axis_rolling(self): + np.random.seed(1234) + + x_flat = np.random.randn(1024) + _, _, z_flat = stft(x_flat) + + for a in range(3): + newshape = [1,]*3 + newshape[a] = -1 + x = x_flat.reshape(newshape) + + _, _, z_plus = stft(x, axis=a) # Positive axis index + _, _, z_minus = stft(x, axis=a-x.ndim) # Negative axis index + + assert_equal(z_flat, z_plus.squeeze(), err_msg=a) + assert_equal(z_flat, z_minus.squeeze(), err_msg=a-x.ndim) + + # z_flat has shape [n_freq, n_time] + + # Test vs. transpose + _, x_transpose_m = istft(z_flat.T, time_axis=-2, freq_axis=-1) + _, x_transpose_p = istft(z_flat.T, time_axis=0, freq_axis=1) + + assert_allclose(x_flat, x_transpose_m, err_msg='istft transpose minus') + assert_allclose(x_flat, x_transpose_p, err_msg='istft transpose plus') + + def test_roundtrip_scaling(self): + """Verify behavior of scaling parameter. """ + # Create 1024 sample cosine signal with amplitude 2: + X = np.zeros(513, dtype=complex) + X[256] = 1024 + x = np.fft.irfft(X) + power_x = sum(x**2) / len(x) # power of signal x is 2 + + # Calculate magnitude-scaled STFT: + Zs = stft(x, boundary='even', scaling='spectrum')[2] + + # Test round trip: + x1 = istft(Zs, boundary=True, scaling='spectrum')[1] + assert_allclose(x1, x) + + # For a Hann-windowed 256 sample length FFT, we expect a peak at + # frequency 64 (since it is 1/4 the length of X) with a height of 1 + # (half the amplitude). A Hann window of a perfectly centered sine has + # the magnitude [..., 0, 0, 0.5, 1, 0.5, 0, 0, ...]. + # Note that in this case the 'even' padding works for the beginning + # but not for the end of the STFT. + assert_allclose(abs(Zs[63, :-1]), 0.5) + assert_allclose(abs(Zs[64, :-1]), 1) + assert_allclose(abs(Zs[65, :-1]), 0.5) + # All other values should be zero: + Zs[63:66, :-1] = 0 + # Note since 'rtol' does not have influence here, atol needs to be set: + assert_allclose(Zs[:, :-1], 0, atol=np.finfo(Zs.dtype).resolution) + + # Calculate two-sided psd-scaled STFT: + # - using 'even' padding since signal is axis symmetric - this ensures + # stationary behavior on the boundaries + # - using the two-sided transform allows determining the spectral + # power by `sum(abs(Zp[:, k])**2) / len(f)` for the k-th time slot. + Zp = stft(x, return_onesided=False, boundary='even', scaling='psd')[2] + + # Calculate spectral power of Zd by summing over the frequency axis: + psd_Zp = np.sum(Zp.real**2 + Zp.imag**2, axis=0) / Zp.shape[0] + # Spectral power of Zp should be equal to the signal's power: + assert_allclose(psd_Zp, power_x) + + # Test round trip: + x1 = istft(Zp, input_onesided=False, boundary=True, scaling='psd')[1] + assert_allclose(x1, x) + + # The power of the one-sided psd-scaled STFT can be determined + # analogously (note that the two sides are not of equal shape): + Zp0 = stft(x, return_onesided=True, boundary='even', scaling='psd')[2] + + # Since x is real, its Fourier transform is conjugate symmetric, i.e., + # the missing 'second side' can be expressed through the 'first side': + Zp1 = np.conj(Zp0[-2:0:-1, :]) # 'second side' is conjugate reversed + assert_allclose(Zp[:129, :], Zp0) + assert_allclose(Zp[129:, :], Zp1) + + # Calculate the spectral power: + s2 = (np.sum(Zp0.real ** 2 + Zp0.imag ** 2, axis=0) + + np.sum(Zp1.real ** 2 + Zp1.imag ** 2, axis=0)) + psd_Zp01 = s2 / (Zp0.shape[0] + Zp1.shape[0]) + assert_allclose(psd_Zp01, power_x) + + # Test round trip: + x1 = istft(Zp0, input_onesided=True, boundary=True, scaling='psd')[1] + assert_allclose(x1, x) + + +class TestSampledSpectralRepresentations: + """Check energy/power relations from `Spectral Analysis` section in the user guide. + + A 32 sample cosine signal is used to compare the numerical to the expected results + stated in :ref:`tutorial_SpectralAnalysis` in + file ``doc/source/tutorial/signal.rst`` + """ + n: int = 32 #: number of samples + T: float = 1/16 #: sampling interval + a_ref: float = 3 #: amplitude of reference + l_a: int = 3 #: index in fft for defining frequency of test signal + + x_ref: np.ndarray #: reference signal + X_ref: np.ndarray #: two-sided FFT of x_ref + E_ref: float #: energy of signal + P_ref: float #: power of signal + + def setup_method(self): + """Create Cosine signal with amplitude a from spectrum. """ + f = rfftfreq(self.n, self.T) + X_ref = np.zeros_like(f) + self.l_a = 3 + X_ref[self.l_a] = self.a_ref/2 * self.n # set amplitude + self.x_ref = irfft(X_ref) + self.X_ref = fft(self.x_ref) + + # Closed form expression for continuous-time signal: + self.E_ref = self.tau * self.a_ref**2 / 2 # energy of signal + self.P_ref = self.a_ref**2 / 2 # power of signal + + @property + def tau(self) -> float: + """Duration of signal. """ + return self.n * self.T + + @property + def delta_f(self) -> float: + """Bin width """ + return 1 / (self.n * self.T) + + def test_reference_signal(self): + """Test energy and power formulas. """ + # Verify that amplitude is a: + assert_allclose(2*self.a_ref, np.ptp(self.x_ref), rtol=0.1) + # Verify that energy expression for sampled signal: + assert_allclose(self.T * sum(self.x_ref ** 2), self.E_ref) + + # Verify that spectral energy and power formulas are correct: + sum_X_ref_squared = sum(self.X_ref.real**2 + self.X_ref.imag**2) + assert_allclose(self.T/self.n * sum_X_ref_squared, self.E_ref) + assert_allclose(1/self.n**2 * sum_X_ref_squared, self.P_ref) + + def test_windowed_DFT(self): + """Verify spectral representations of windowed DFT. + + Furthermore, the scalings of `periodogram` and `welch` are verified. + """ + w = hann(self.n, sym=False) + c_amp, c_rms = abs(sum(w)), np.sqrt(sum(w.real**2 + w.imag**2)) + Xw = fft(self.x_ref*w) # unnormalized windowed DFT + + # Verify that the *spectrum* peak is consistent: + assert_allclose(self.tau * Xw[self.l_a] / c_amp, self.a_ref * self.tau / 2) + # Verify that the *amplitude spectrum* peak is consistent: + assert_allclose(Xw[self.l_a] / c_amp, self.a_ref/2) + + # Verify spectral power/energy equals signal's power/energy: + X_ESD = self.tau * self.T * abs(Xw / c_rms)**2 # Energy Spectral Density + X_PSD = self.T * abs(Xw / c_rms)**2 # Power Spectral Density + assert_allclose(self.delta_f * sum(X_ESD), self.E_ref) + assert_allclose(self.delta_f * sum(X_PSD), self.P_ref) + + # Verify scalings of periodogram: + kw = dict(fs=1/self.T, window=w, detrend=False, return_onesided=False) + _, P_mag = periodogram(self.x_ref, scaling='spectrum', **kw) + _, P_psd = periodogram(self.x_ref, scaling='density', **kw) + + # Verify that periodogram calculates a squared magnitude spectrum: + float_res = np.finfo(P_mag.dtype).resolution + assert_allclose(P_mag, abs(Xw/c_amp)**2, atol=float_res*max(P_mag)) + # Verify that periodogram calculates a PSD: + assert_allclose(P_psd, X_PSD, atol=float_res*max(P_psd)) + + # Ensure that scaling of welch is the same as of periodogram: + kw = dict(nperseg=len(self.x_ref), noverlap=0, **kw) + assert_allclose(welch(self.x_ref, scaling='spectrum', **kw)[1], P_mag, + atol=float_res*max(P_mag)) + assert_allclose(welch(self.x_ref, scaling='density', **kw)[1], P_psd, + atol=float_res*max(P_psd)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_upfirdn.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_upfirdn.py new file mode 100644 index 0000000000000000000000000000000000000000..af23fd41c0f66bff6ed5ffc493ac70f7c0608e10 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_upfirdn.py @@ -0,0 +1,287 @@ +# Code adapted from "upfirdn" python library with permission: +# +# Copyright (c) 2009, Motorola, Inc +# +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Motorola nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import numpy as np +from itertools import product + +from numpy.testing import assert_equal, assert_allclose +from pytest import raises as assert_raises +import pytest + +from scipy.signal import upfirdn, firwin +from scipy.signal._upfirdn import _output_len, _upfirdn_modes +from scipy.signal._upfirdn_apply import _pad_test + + +def upfirdn_naive(x, h, up=1, down=1): + """Naive upfirdn processing in Python. + + Note: arg order (x, h) differs to facilitate apply_along_axis use. + """ + h = np.asarray(h) + out = np.zeros(len(x) * up, x.dtype) + out[::up] = x + out = np.convolve(h, out)[::down][:_output_len(len(h), len(x), up, down)] + return out + + +class UpFIRDnCase: + """Test _UpFIRDn object""" + def __init__(self, up, down, h, x_dtype): + self.up = up + self.down = down + self.h = np.atleast_1d(h) + self.x_dtype = x_dtype + self.rng = np.random.RandomState(17) + + def __call__(self): + # tiny signal + self.scrub(np.ones(1, self.x_dtype)) + # ones + self.scrub(np.ones(10, self.x_dtype)) # ones + # randn + x = self.rng.randn(10).astype(self.x_dtype) + if self.x_dtype in (np.complex64, np.complex128): + x += 1j * self.rng.randn(10) + self.scrub(x) + # ramp + self.scrub(np.arange(10).astype(self.x_dtype)) + # 3D, random + size = (2, 3, 5) + x = self.rng.randn(*size).astype(self.x_dtype) + if self.x_dtype in (np.complex64, np.complex128): + x += 1j * self.rng.randn(*size) + for axis in range(len(size)): + self.scrub(x, axis=axis) + x = x[:, ::2, 1::3].T + for axis in range(len(size)): + self.scrub(x, axis=axis) + + def scrub(self, x, axis=-1): + yr = np.apply_along_axis(upfirdn_naive, axis, x, + self.h, self.up, self.down) + want_len = _output_len(len(self.h), x.shape[axis], self.up, self.down) + assert yr.shape[axis] == want_len + y = upfirdn(self.h, x, self.up, self.down, axis=axis) + assert y.shape[axis] == want_len + assert y.shape == yr.shape + dtypes = (self.h.dtype, x.dtype) + if all(d == np.complex64 for d in dtypes): + assert_equal(y.dtype, np.complex64) + elif np.complex64 in dtypes and np.float32 in dtypes: + assert_equal(y.dtype, np.complex64) + elif all(d == np.float32 for d in dtypes): + assert_equal(y.dtype, np.float32) + elif np.complex128 in dtypes or np.complex64 in dtypes: + assert_equal(y.dtype, np.complex128) + else: + assert_equal(y.dtype, np.float64) + assert_allclose(yr, y) + + +_UPFIRDN_TYPES = (int, np.float32, np.complex64, float, complex) + + +class TestUpfirdn: + + def test_valid_input(self): + assert_raises(ValueError, upfirdn, [1], [1], 1, 0) # up or down < 1 + assert_raises(ValueError, upfirdn, [], [1], 1, 1) # h.ndim != 1 + assert_raises(ValueError, upfirdn, [[1]], [1], 1, 1) + + @pytest.mark.parametrize('len_h', [1, 2, 3, 4, 5]) + @pytest.mark.parametrize('len_x', [1, 2, 3, 4, 5]) + def test_singleton(self, len_h, len_x): + # gh-9844: lengths producing expected outputs + h = np.zeros(len_h) + h[len_h // 2] = 1. # make h a delta + x = np.ones(len_x) + y = upfirdn(h, x, 1, 1) + want = np.pad(x, (len_h // 2, (len_h - 1) // 2), 'constant') + assert_allclose(y, want) + + def test_shift_x(self): + # gh-9844: shifted x can change values? + y = upfirdn([1, 1], [1.], 1, 1) + assert_allclose(y, [1, 1]) # was [0, 1] in the issue + y = upfirdn([1, 1], [0., 1.], 1, 1) + assert_allclose(y, [0, 1, 1]) + + # A bunch of lengths/factors chosen because they exposed differences + # between the "old way" and new way of computing length, and then + # got `expected` from MATLAB + @pytest.mark.parametrize('len_h, len_x, up, down, expected', [ + (2, 2, 5, 2, [1, 0, 0, 0]), + (2, 3, 6, 3, [1, 0, 1, 0, 1]), + (2, 4, 4, 3, [1, 0, 0, 0, 1]), + (3, 2, 6, 2, [1, 0, 0, 1, 0]), + (4, 11, 3, 5, [1, 0, 0, 1, 0, 0, 1]), + ]) + def test_length_factors(self, len_h, len_x, up, down, expected): + # gh-9844: weird factors + h = np.zeros(len_h) + h[0] = 1. + x = np.ones(len_x) + y = upfirdn(h, x, up, down) + assert_allclose(y, expected) + + @pytest.mark.parametrize('down, want_len', [ # lengths from MATLAB + (2, 5015), + (11, 912), + (79, 127), + ]) + def test_vs_convolve(self, down, want_len): + # Check that up=1.0 gives same answer as convolve + slicing + random_state = np.random.RandomState(17) + try_types = (int, np.float32, np.complex64, float, complex) + size = 10000 + + for dtype in try_types: + x = random_state.randn(size).astype(dtype) + if dtype in (np.complex64, np.complex128): + x += 1j * random_state.randn(size) + + h = firwin(31, 1. / down, window='hamming') + yl = upfirdn_naive(x, h, 1, down) + y = upfirdn(h, x, up=1, down=down) + assert y.shape == (want_len,) + assert yl.shape[0] == y.shape[0] + assert_allclose(yl, y, atol=1e-7, rtol=1e-7) + + @pytest.mark.parametrize('x_dtype', _UPFIRDN_TYPES) + @pytest.mark.parametrize('h', (1., 1j)) + @pytest.mark.parametrize('up, down', [(1, 1), (2, 2), (3, 2), (2, 3)]) + def test_vs_naive_delta(self, x_dtype, h, up, down): + UpFIRDnCase(up, down, h, x_dtype)() + + @pytest.mark.parametrize('x_dtype', _UPFIRDN_TYPES) + @pytest.mark.parametrize('h_dtype', _UPFIRDN_TYPES) + @pytest.mark.parametrize('p_max, q_max', + list(product((10, 100), (10, 100)))) + def test_vs_naive(self, x_dtype, h_dtype, p_max, q_max): + tests = self._random_factors(p_max, q_max, h_dtype, x_dtype) + for test in tests: + test() + + def _random_factors(self, p_max, q_max, h_dtype, x_dtype): + n_rep = 3 + longest_h = 25 + random_state = np.random.RandomState(17) + tests = [] + + for _ in range(n_rep): + # Randomize the up/down factors somewhat + p_add = q_max if p_max > q_max else 1 + q_add = p_max if q_max > p_max else 1 + p = random_state.randint(p_max) + p_add + q = random_state.randint(q_max) + q_add + + # Generate random FIR coefficients + len_h = random_state.randint(longest_h) + 1 + h = np.atleast_1d(random_state.randint(len_h)) + h = h.astype(h_dtype) + if h_dtype == complex: + h += 1j * random_state.randint(len_h) + + tests.append(UpFIRDnCase(p, q, h, x_dtype)) + + return tests + + @pytest.mark.parametrize('mode', _upfirdn_modes) + def test_extensions(self, mode): + """Test vs. manually computed results for modes not in numpy's pad.""" + x = np.array([1, 2, 3, 1], dtype=float) + npre, npost = 6, 6 + y = _pad_test(x, npre=npre, npost=npost, mode=mode) + if mode == 'antisymmetric': + y_expected = np.asarray( + [3, 1, -1, -3, -2, -1, 1, 2, 3, 1, -1, -3, -2, -1, 1, 2]) + elif mode == 'antireflect': + y_expected = np.asarray( + [1, 2, 3, 1, -1, 0, 1, 2, 3, 1, -1, 0, 1, 2, 3, 1]) + elif mode == 'smooth': + y_expected = np.asarray( + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 1, -1, -3, -5, -7, -9, -11]) + elif mode == "line": + lin_slope = (x[-1] - x[0]) / (len(x) - 1) + left = x[0] + np.arange(-npre, 0, 1) * lin_slope + right = x[-1] + np.arange(1, npost + 1) * lin_slope + y_expected = np.concatenate((left, x, right)) + else: + y_expected = np.pad(x, (npre, npost), mode=mode) + assert_allclose(y, y_expected) + + @pytest.mark.parametrize( + 'size, h_len, mode, dtype', + product( + [8], + [4, 5, 26], # include cases with h_len > 2*size + _upfirdn_modes, + [np.float32, np.float64, np.complex64, np.complex128], + ) + ) + def test_modes(self, size, h_len, mode, dtype): + random_state = np.random.RandomState(5) + x = random_state.randn(size).astype(dtype) + if dtype in (np.complex64, np.complex128): + x += 1j * random_state.randn(size) + h = np.arange(1, 1 + h_len, dtype=x.real.dtype) + + y = upfirdn(h, x, up=1, down=1, mode=mode) + # expected result: pad the input, filter with zero padding, then crop + npad = h_len - 1 + if mode in ['antisymmetric', 'antireflect', 'smooth', 'line']: + # use _pad_test test function for modes not supported by np.pad. + xpad = _pad_test(x, npre=npad, npost=npad, mode=mode) + else: + xpad = np.pad(x, npad, mode=mode) + ypad = upfirdn(h, xpad, up=1, down=1, mode='constant') + y_expected = ypad[npad:-npad] + + atol = rtol = np.finfo(dtype).eps * 1e2 + assert_allclose(y, y_expected, atol=atol, rtol=rtol) + + +def test_output_len_long_input(): + # Regression test for gh-17375. On Windows, a large enough input + # that should have been well within the capabilities of 64 bit integers + # would result in a 32 bit overflow because of a bug in Cython 0.29.32. + len_h = 1001 + in_len = 10**8 + up = 320 + down = 441 + out_len = _output_len(len_h, in_len, up, down) + # The expected value was computed "by hand" from the formula + # (((in_len - 1) * up + len_h) - 1) // down + 1 + assert out_len == 72562360 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_waveforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_waveforms.py new file mode 100644 index 0000000000000000000000000000000000000000..7f84a804b52fc70b19bfe8e9d731c086f34179b2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_waveforms.py @@ -0,0 +1,351 @@ +import numpy as np +from numpy.testing import (assert_almost_equal, assert_equal, + assert_, assert_allclose, assert_array_equal) +from pytest import raises as assert_raises + +import scipy.signal._waveforms as waveforms + + +# These chirp_* functions are the instantaneous frequencies of the signals +# returned by chirp(). + +def chirp_linear(t, f0, f1, t1): + f = f0 + (f1 - f0) * t / t1 + return f + + +def chirp_quadratic(t, f0, f1, t1, vertex_zero=True): + if vertex_zero: + f = f0 + (f1 - f0) * t**2 / t1**2 + else: + f = f1 - (f1 - f0) * (t1 - t)**2 / t1**2 + return f + + +def chirp_geometric(t, f0, f1, t1): + f = f0 * (f1/f0)**(t/t1) + return f + + +def chirp_hyperbolic(t, f0, f1, t1): + f = f0*f1*t1 / ((f0 - f1)*t + f1*t1) + return f + + +def compute_frequency(t, theta): + """ + Compute theta'(t)/(2*pi), where theta'(t) is the derivative of theta(t). + """ + # Assume theta and t are 1-D NumPy arrays. + # Assume that t is uniformly spaced. + dt = t[1] - t[0] + f = np.diff(theta)/(2*np.pi) / dt + tf = 0.5*(t[1:] + t[:-1]) + return tf, f + + +class TestChirp: + + def test_linear_at_zero(self): + w = waveforms.chirp(t=0, f0=1.0, f1=2.0, t1=1.0, method='linear') + assert_almost_equal(w, 1.0) + + def test_linear_freq_01(self): + method = 'linear' + f0 = 1.0 + f1 = 2.0 + t1 = 1.0 + t = np.linspace(0, t1, 100) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_linear(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_linear_freq_02(self): + method = 'linear' + f0 = 200.0 + f1 = 100.0 + t1 = 10.0 + t = np.linspace(0, t1, 100) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_linear(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_quadratic_at_zero(self): + w = waveforms.chirp(t=0, f0=1.0, f1=2.0, t1=1.0, method='quadratic') + assert_almost_equal(w, 1.0) + + def test_quadratic_at_zero2(self): + w = waveforms.chirp(t=0, f0=1.0, f1=2.0, t1=1.0, method='quadratic', + vertex_zero=False) + assert_almost_equal(w, 1.0) + + def test_quadratic_freq_01(self): + method = 'quadratic' + f0 = 1.0 + f1 = 2.0 + t1 = 1.0 + t = np.linspace(0, t1, 2000) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_quadratic(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_quadratic_freq_02(self): + method = 'quadratic' + f0 = 20.0 + f1 = 10.0 + t1 = 10.0 + t = np.linspace(0, t1, 2000) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_quadratic(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_logarithmic_at_zero(self): + w = waveforms.chirp(t=0, f0=1.0, f1=2.0, t1=1.0, method='logarithmic') + assert_almost_equal(w, 1.0) + + def test_logarithmic_freq_01(self): + method = 'logarithmic' + f0 = 1.0 + f1 = 2.0 + t1 = 1.0 + t = np.linspace(0, t1, 10000) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_geometric(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_logarithmic_freq_02(self): + method = 'logarithmic' + f0 = 200.0 + f1 = 100.0 + t1 = 10.0 + t = np.linspace(0, t1, 10000) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_geometric(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_logarithmic_freq_03(self): + method = 'logarithmic' + f0 = 100.0 + f1 = 100.0 + t1 = 10.0 + t = np.linspace(0, t1, 10000) + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + abserr = np.max(np.abs(f - chirp_geometric(tf, f0, f1, t1))) + assert_(abserr < 1e-6) + + def test_hyperbolic_at_zero(self): + w = waveforms.chirp(t=0, f0=10.0, f1=1.0, t1=1.0, method='hyperbolic') + assert_almost_equal(w, 1.0) + + def test_hyperbolic_freq_01(self): + method = 'hyperbolic' + t1 = 1.0 + t = np.linspace(0, t1, 10000) + # f0 f1 + cases = [[10.0, 1.0], + [1.0, 10.0], + [-10.0, -1.0], + [-1.0, -10.0]] + for f0, f1 in cases: + phase = waveforms._chirp_phase(t, f0, t1, f1, method) + tf, f = compute_frequency(t, phase) + expected = chirp_hyperbolic(tf, f0, f1, t1) + assert_allclose(f, expected) + + def test_hyperbolic_zero_freq(self): + # f0=0 or f1=0 must raise a ValueError. + method = 'hyperbolic' + t1 = 1.0 + t = np.linspace(0, t1, 5) + assert_raises(ValueError, waveforms.chirp, t, 0, t1, 1, method) + assert_raises(ValueError, waveforms.chirp, t, 1, t1, 0, method) + + def test_unknown_method(self): + method = "foo" + f0 = 10.0 + f1 = 20.0 + t1 = 1.0 + t = np.linspace(0, t1, 10) + assert_raises(ValueError, waveforms.chirp, t, f0, t1, f1, method) + + def test_integer_t1(self): + f0 = 10.0 + f1 = 20.0 + t = np.linspace(-1, 1, 11) + t1 = 3.0 + float_result = waveforms.chirp(t, f0, t1, f1) + t1 = 3 + int_result = waveforms.chirp(t, f0, t1, f1) + err_msg = "Integer input 't1=3' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_f0(self): + f1 = 20.0 + t1 = 3.0 + t = np.linspace(-1, 1, 11) + f0 = 10.0 + float_result = waveforms.chirp(t, f0, t1, f1) + f0 = 10 + int_result = waveforms.chirp(t, f0, t1, f1) + err_msg = "Integer input 'f0=10' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_f1(self): + f0 = 10.0 + t1 = 3.0 + t = np.linspace(-1, 1, 11) + f1 = 20.0 + float_result = waveforms.chirp(t, f0, t1, f1) + f1 = 20 + int_result = waveforms.chirp(t, f0, t1, f1) + err_msg = "Integer input 'f1=20' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_all(self): + f0 = 10 + t1 = 3 + f1 = 20 + t = np.linspace(-1, 1, 11) + float_result = waveforms.chirp(t, float(f0), float(t1), float(f1)) + int_result = waveforms.chirp(t, f0, t1, f1) + err_msg = "Integer input 'f0=10, t1=3, f1=20' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + +class TestSweepPoly: + + def test_sweep_poly_quad1(self): + p = np.poly1d([1.0, 0.0, 1.0]) + t = np.linspace(0, 3.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = p(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_const(self): + p = np.poly1d(2.0) + t = np.linspace(0, 3.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = p(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_linear(self): + p = np.poly1d([-1.0, 10.0]) + t = np.linspace(0, 3.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = p(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_quad2(self): + p = np.poly1d([1.0, 0.0, -2.0]) + t = np.linspace(0, 3.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = p(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_cubic(self): + p = np.poly1d([2.0, 1.0, 0.0, -2.0]) + t = np.linspace(0, 2.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = p(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_cubic2(self): + """Use an array of coefficients instead of a poly1d.""" + p = np.array([2.0, 1.0, 0.0, -2.0]) + t = np.linspace(0, 2.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = np.poly1d(p)(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + def test_sweep_poly_cubic3(self): + """Use a list of coefficients instead of a poly1d.""" + p = [2.0, 1.0, 0.0, -2.0] + t = np.linspace(0, 2.0, 10000) + phase = waveforms._sweep_poly_phase(t, p) + tf, f = compute_frequency(t, phase) + expected = np.poly1d(p)(tf) + abserr = np.max(np.abs(f - expected)) + assert_(abserr < 1e-6) + + +class TestGaussPulse: + + def test_integer_fc(self): + float_result = waveforms.gausspulse('cutoff', fc=1000.0) + int_result = waveforms.gausspulse('cutoff', fc=1000) + err_msg = "Integer input 'fc=1000' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_bw(self): + float_result = waveforms.gausspulse('cutoff', bw=1.0) + int_result = waveforms.gausspulse('cutoff', bw=1) + err_msg = "Integer input 'bw=1' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_bwr(self): + float_result = waveforms.gausspulse('cutoff', bwr=-6.0) + int_result = waveforms.gausspulse('cutoff', bwr=-6) + err_msg = "Integer input 'bwr=-6' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + def test_integer_tpr(self): + float_result = waveforms.gausspulse('cutoff', tpr=-60.0) + int_result = waveforms.gausspulse('cutoff', tpr=-60) + err_msg = "Integer input 'tpr=-60' gives wrong result" + assert_equal(int_result, float_result, err_msg=err_msg) + + +class TestUnitImpulse: + + def test_no_index(self): + assert_array_equal(waveforms.unit_impulse(7), [1, 0, 0, 0, 0, 0, 0]) + assert_array_equal(waveforms.unit_impulse((3, 3)), + [[1, 0, 0], [0, 0, 0], [0, 0, 0]]) + + def test_index(self): + assert_array_equal(waveforms.unit_impulse(10, 3), + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]) + assert_array_equal(waveforms.unit_impulse((3, 3), (1, 1)), + [[0, 0, 0], [0, 1, 0], [0, 0, 0]]) + + # Broadcasting + imp = waveforms.unit_impulse((4, 4), 2) + assert_array_equal(imp, np.array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 0]])) + + def test_mid(self): + assert_array_equal(waveforms.unit_impulse((3, 3), 'mid'), + [[0, 0, 0], [0, 1, 0], [0, 0, 0]]) + assert_array_equal(waveforms.unit_impulse(9, 'mid'), + [0, 0, 0, 0, 1, 0, 0, 0, 0]) + + def test_dtype(self): + imp = waveforms.unit_impulse(7) + assert_(np.issubdtype(imp.dtype, np.floating)) + + imp = waveforms.unit_impulse(5, 3, dtype=int) + assert_(np.issubdtype(imp.dtype, np.integer)) + + imp = waveforms.unit_impulse((5, 2), (3, 1), dtype=complex) + assert_(np.issubdtype(imp.dtype, np.complexfloating)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_wavelets.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_wavelets.py new file mode 100644 index 0000000000000000000000000000000000000000..e83e6918429bfc539a44fc9a627deabafe2852a6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_wavelets.py @@ -0,0 +1,161 @@ +import numpy as np +from numpy.testing import (assert_equal, + assert_array_equal, assert_array_almost_equal, assert_array_less, assert_,) +import pytest + +import scipy.signal._wavelets as wavelets + + +class TestWavelets: + def test_qmf(self): + with pytest.deprecated_call(): + assert_array_equal(wavelets.qmf([1, 1]), [1, -1]) + + def test_daub(self): + with pytest.deprecated_call(): + for i in range(1, 15): + assert_equal(len(wavelets.daub(i)), i * 2) + + def test_cascade(self): + with pytest.deprecated_call(): + for J in range(1, 7): + for i in range(1, 5): + lpcoef = wavelets.daub(i) + k = len(lpcoef) + x, phi, psi = wavelets.cascade(lpcoef, J) + assert_(len(x) == len(phi) == len(psi)) + assert_equal(len(x), (k - 1) * 2 ** J) + + def test_morlet(self): + with pytest.deprecated_call(): + x = wavelets.morlet(50, 4.1, complete=True) + y = wavelets.morlet(50, 4.1, complete=False) + # Test if complete and incomplete wavelet have same lengths: + assert_equal(len(x), len(y)) + # Test if complete wavelet is less than incomplete wavelet: + assert_array_less(x, y) + + x = wavelets.morlet(10, 50, complete=False) + y = wavelets.morlet(10, 50, complete=True) + # For large widths complete and incomplete wavelets should be + # identical within numerical precision: + assert_equal(x, y) + + # miscellaneous tests: + x = np.array([1.73752399e-09 + 9.84327394e-25j, + 6.49471756e-01 + 0.00000000e+00j, + 1.73752399e-09 - 9.84327394e-25j]) + y = wavelets.morlet(3, w=2, complete=True) + assert_array_almost_equal(x, y) + + x = np.array([2.00947715e-09 + 9.84327394e-25j, + 7.51125544e-01 + 0.00000000e+00j, + 2.00947715e-09 - 9.84327394e-25j]) + y = wavelets.morlet(3, w=2, complete=False) + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, s=4, complete=True) + y = wavelets.morlet(20000, s=8, complete=True)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, s=4, complete=False) + assert_array_almost_equal(y, x, decimal=2) + y = wavelets.morlet(20000, s=8, complete=False)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, w=3, s=5, complete=True) + y = wavelets.morlet(20000, w=3, s=10, complete=True)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, w=3, s=5, complete=False) + assert_array_almost_equal(y, x, decimal=2) + y = wavelets.morlet(20000, w=3, s=10, complete=False)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, w=7, s=10, complete=True) + y = wavelets.morlet(20000, w=7, s=20, complete=True)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + x = wavelets.morlet(10000, w=7, s=10, complete=False) + assert_array_almost_equal(x, y, decimal=2) + y = wavelets.morlet(20000, w=7, s=20, complete=False)[5000:15000] + assert_array_almost_equal(x, y, decimal=2) + + def test_morlet2(self): + with pytest.deprecated_call(): + w = wavelets.morlet2(1.0, 0.5) + expected = (np.pi**(-0.25) * np.sqrt(1/0.5)).astype(complex) + assert_array_equal(w, expected) + + lengths = [5, 11, 15, 51, 101] + for length in lengths: + w = wavelets.morlet2(length, 1.0) + assert_(len(w) == length) + max_loc = np.argmax(w) + assert_(max_loc == (length // 2)) + + points = 100 + w = abs(wavelets.morlet2(points, 2.0)) + half_vec = np.arange(0, points // 2) + assert_array_almost_equal(w[half_vec], w[-(half_vec + 1)]) + + x = np.array([5.03701224e-09 + 2.46742437e-24j, + 1.88279253e+00 + 0.00000000e+00j, + 5.03701224e-09 - 2.46742437e-24j]) + y = wavelets.morlet2(3, s=1/(2*np.pi), w=2) + assert_array_almost_equal(x, y) + + def test_ricker(self): + with pytest.deprecated_call(): + w = wavelets.ricker(1.0, 1) + expected = 2 / (np.sqrt(3 * 1.0) * (np.pi ** 0.25)) + assert_array_equal(w, expected) + + lengths = [5, 11, 15, 51, 101] + for length in lengths: + w = wavelets.ricker(length, 1.0) + assert_(len(w) == length) + max_loc = np.argmax(w) + assert_(max_loc == (length // 2)) + + points = 100 + w = wavelets.ricker(points, 2.0) + half_vec = np.arange(0, points // 2) + #Wavelet should be symmetric + assert_array_almost_equal(w[half_vec], w[-(half_vec + 1)]) + + #Check zeros + aas = [5, 10, 15, 20, 30] + points = 99 + for a in aas: + w = wavelets.ricker(points, a) + vec = np.arange(0, points) - (points - 1.0) / 2 + exp_zero1 = np.argmin(np.abs(vec - a)) + exp_zero2 = np.argmin(np.abs(vec + a)) + assert_array_almost_equal(w[exp_zero1], 0) + assert_array_almost_equal(w[exp_zero2], 0) + + def test_cwt(self): + with pytest.deprecated_call(): + widths = [1.0] + def delta_wavelet(s, t): + return np.array([1]) + len_data = 100 + test_data = np.sin(np.pi * np.arange(0, len_data) / 10.0) + + #Test delta function input gives same data as output + cwt_dat = wavelets.cwt(test_data, delta_wavelet, widths) + assert_(cwt_dat.shape == (len(widths), len_data)) + assert_array_almost_equal(test_data, cwt_dat.flatten()) + + #Check proper shape on output + widths = [1, 3, 4, 5, 10] + cwt_dat = wavelets.cwt(test_data, wavelets.ricker, widths) + assert_(cwt_dat.shape == (len(widths), len_data)) + + widths = [len_data * 10] + #Note: this wavelet isn't defined quite right, but is fine for this test + def flat_wavelet(l, w): + return np.full(w, 1 / w) + cwt_dat = wavelets.cwt(test_data, flat_wavelet, widths) + assert_array_almost_equal(cwt_dat, np.mean(test_data)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_windows.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_windows.py new file mode 100644 index 0000000000000000000000000000000000000000..cdf1a46c924f9f2594d944e885b23c37fe9bf4f2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/tests/test_windows.py @@ -0,0 +1,846 @@ +import numpy as np +from numpy import array +from numpy.testing import (assert_array_almost_equal, assert_array_equal, + assert_allclose, + assert_equal, assert_, assert_array_less, + suppress_warnings) +from pytest import raises as assert_raises + +from scipy.fft import fft +from scipy.signal import windows, get_window, resample + + +window_funcs = [ + ('boxcar', ()), + ('triang', ()), + ('parzen', ()), + ('bohman', ()), + ('blackman', ()), + ('nuttall', ()), + ('blackmanharris', ()), + ('flattop', ()), + ('bartlett', ()), + ('barthann', ()), + ('hamming', ()), + ('kaiser', (1,)), + ('dpss', (2,)), + ('gaussian', (0.5,)), + ('general_gaussian', (1.5, 2)), + ('chebwin', (1,)), + ('cosine', ()), + ('hann', ()), + ('exponential', ()), + ('taylor', ()), + ('tukey', (0.5,)), + ('lanczos', ()), + ] + + +class TestBartHann: + + def test_basic(self): + assert_allclose(windows.barthann(6, sym=True), + [0, 0.35857354213752, 0.8794264578624801, + 0.8794264578624801, 0.3585735421375199, 0], + rtol=1e-15, atol=1e-15) + assert_allclose(windows.barthann(7), + [0, 0.27, 0.73, 1.0, 0.73, 0.27, 0], + rtol=1e-15, atol=1e-15) + assert_allclose(windows.barthann(6, False), + [0, 0.27, 0.73, 1.0, 0.73, 0.27], + rtol=1e-15, atol=1e-15) + + +class TestBartlett: + + def test_basic(self): + assert_allclose(windows.bartlett(6), [0, 0.4, 0.8, 0.8, 0.4, 0]) + assert_allclose(windows.bartlett(7), [0, 1/3, 2/3, 1.0, 2/3, 1/3, 0]) + assert_allclose(windows.bartlett(6, False), + [0, 1/3, 2/3, 1.0, 2/3, 1/3]) + + +class TestBlackman: + + def test_basic(self): + assert_allclose(windows.blackman(6, sym=False), + [0, 0.13, 0.63, 1.0, 0.63, 0.13], atol=1e-14) + assert_allclose(windows.blackman(7, sym=False), + [0, 0.09045342435412804, 0.4591829575459636, + 0.9203636180999081, 0.9203636180999081, + 0.4591829575459636, 0.09045342435412804], atol=1e-8) + assert_allclose(windows.blackman(6), + [0, 0.2007701432625305, 0.8492298567374694, + 0.8492298567374694, 0.2007701432625305, 0], + atol=1e-14) + assert_allclose(windows.blackman(7, True), + [0, 0.13, 0.63, 1.0, 0.63, 0.13, 0], atol=1e-14) + + +class TestBlackmanHarris: + + def test_basic(self): + assert_allclose(windows.blackmanharris(6, False), + [6.0e-05, 0.055645, 0.520575, 1.0, 0.520575, 0.055645]) + assert_allclose(windows.blackmanharris(7, sym=False), + [6.0e-05, 0.03339172347815117, 0.332833504298565, + 0.8893697722232837, 0.8893697722232838, + 0.3328335042985652, 0.03339172347815122]) + assert_allclose(windows.blackmanharris(6), + [6.0e-05, 0.1030114893456638, 0.7938335106543362, + 0.7938335106543364, 0.1030114893456638, 6.0e-05]) + assert_allclose(windows.blackmanharris(7, sym=True), + [6.0e-05, 0.055645, 0.520575, 1.0, 0.520575, 0.055645, + 6.0e-05]) + + +class TestTaylor: + + def test_normalized(self): + """Tests windows of small length that are normalized to 1. See the + documentation for the Taylor window for more information on + normalization. + """ + assert_allclose(windows.taylor(1, 2, 15), 1.0) + assert_allclose( + windows.taylor(5, 2, 15), + np.array([0.75803341, 0.90757699, 1.0, 0.90757699, 0.75803341]) + ) + assert_allclose( + windows.taylor(6, 2, 15), + np.array([ + 0.7504082, 0.86624416, 0.98208011, 0.98208011, 0.86624416, + 0.7504082 + ]) + ) + + def test_non_normalized(self): + """Test windows of small length that are not normalized to 1. See + the documentation for the Taylor window for more information on + normalization. + """ + assert_allclose( + windows.taylor(5, 2, 15, norm=False), + np.array([ + 0.87508054, 1.04771499, 1.15440894, 1.04771499, 0.87508054 + ]) + ) + assert_allclose( + windows.taylor(6, 2, 15, norm=False), + np.array([ + 0.86627793, 1.0, 1.13372207, 1.13372207, 1.0, 0.86627793 + ]) + ) + + def test_correctness(self): + """This test ensures the correctness of the implemented Taylor + Windowing function. A Taylor Window of 1024 points is created, its FFT + is taken, and the Peak Sidelobe Level (PSLL) and 3dB and 18dB bandwidth + are found and checked. + + A publication from Sandia National Laboratories was used as reference + for the correctness values [1]_. + + References + ----- + .. [1] Armin Doerry, "Catalog of Window Taper Functions for + Sidelobe Control", 2017. + https://www.researchgate.net/profile/Armin_Doerry/publication/316281181_Catalog_of_Window_Taper_Functions_for_Sidelobe_Control/links/58f92cb2a6fdccb121c9d54d/Catalog-of-Window-Taper-Functions-for-Sidelobe-Control.pdf + """ + M_win = 1024 + N_fft = 131072 + # Set norm=False for correctness as the values obtained from the + # scientific publication do not normalize the values. Normalizing + # changes the sidelobe level from the desired value. + w = windows.taylor(M_win, nbar=4, sll=35, norm=False, sym=False) + f = fft(w, N_fft) + spec = 20 * np.log10(np.abs(f / np.amax(f))) + + first_zero = np.argmax(np.diff(spec) > 0) + + PSLL = np.amax(spec[first_zero:-first_zero]) + + BW_3dB = 2*np.argmax(spec <= -3.0102999566398121) / N_fft * M_win + BW_18dB = 2*np.argmax(spec <= -18.061799739838872) / N_fft * M_win + + assert_allclose(PSLL, -35.1672, atol=1) + assert_allclose(BW_3dB, 1.1822, atol=0.1) + assert_allclose(BW_18dB, 2.6112, atol=0.1) + + +class TestBohman: + + def test_basic(self): + assert_allclose(windows.bohman(6), + [0, 0.1791238937062839, 0.8343114522576858, + 0.8343114522576858, 0.1791238937062838, 0]) + assert_allclose(windows.bohman(7, sym=True), + [0, 0.1089977810442293, 0.6089977810442293, 1.0, + 0.6089977810442295, 0.1089977810442293, 0]) + assert_allclose(windows.bohman(6, False), + [0, 0.1089977810442293, 0.6089977810442293, 1.0, + 0.6089977810442295, 0.1089977810442293]) + + +class TestBoxcar: + + def test_basic(self): + assert_allclose(windows.boxcar(6), [1, 1, 1, 1, 1, 1]) + assert_allclose(windows.boxcar(7), [1, 1, 1, 1, 1, 1, 1]) + assert_allclose(windows.boxcar(6, False), [1, 1, 1, 1, 1, 1]) + + +cheb_odd_true = array([0.200938, 0.107729, 0.134941, 0.165348, + 0.198891, 0.235450, 0.274846, 0.316836, + 0.361119, 0.407338, 0.455079, 0.503883, + 0.553248, 0.602637, 0.651489, 0.699227, + 0.745266, 0.789028, 0.829947, 0.867485, + 0.901138, 0.930448, 0.955010, 0.974482, + 0.988591, 0.997138, 1.000000, 0.997138, + 0.988591, 0.974482, 0.955010, 0.930448, + 0.901138, 0.867485, 0.829947, 0.789028, + 0.745266, 0.699227, 0.651489, 0.602637, + 0.553248, 0.503883, 0.455079, 0.407338, + 0.361119, 0.316836, 0.274846, 0.235450, + 0.198891, 0.165348, 0.134941, 0.107729, + 0.200938]) + +cheb_even_true = array([0.203894, 0.107279, 0.133904, + 0.163608, 0.196338, 0.231986, + 0.270385, 0.311313, 0.354493, + 0.399594, 0.446233, 0.493983, + 0.542378, 0.590916, 0.639071, + 0.686302, 0.732055, 0.775783, + 0.816944, 0.855021, 0.889525, + 0.920006, 0.946060, 0.967339, + 0.983557, 0.994494, 1.000000, + 1.000000, 0.994494, 0.983557, + 0.967339, 0.946060, 0.920006, + 0.889525, 0.855021, 0.816944, + 0.775783, 0.732055, 0.686302, + 0.639071, 0.590916, 0.542378, + 0.493983, 0.446233, 0.399594, + 0.354493, 0.311313, 0.270385, + 0.231986, 0.196338, 0.163608, + 0.133904, 0.107279, 0.203894]) + + +class TestChebWin: + + def test_basic(self): + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + assert_allclose(windows.chebwin(6, 100), + [0.1046401879356917, 0.5075781475823447, 1.0, 1.0, + 0.5075781475823447, 0.1046401879356917]) + assert_allclose(windows.chebwin(7, 100), + [0.05650405062850233, 0.316608530648474, + 0.7601208123539079, 1.0, 0.7601208123539079, + 0.316608530648474, 0.05650405062850233]) + assert_allclose(windows.chebwin(6, 10), + [1.0, 0.6071201674458373, 0.6808391469897297, + 0.6808391469897297, 0.6071201674458373, 1.0]) + assert_allclose(windows.chebwin(7, 10), + [1.0, 0.5190521247588651, 0.5864059018130382, + 0.6101519801307441, 0.5864059018130382, + 0.5190521247588651, 1.0]) + assert_allclose(windows.chebwin(6, 10, False), + [1.0, 0.5190521247588651, 0.5864059018130382, + 0.6101519801307441, 0.5864059018130382, + 0.5190521247588651]) + + def test_cheb_odd_high_attenuation(self): + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + cheb_odd = windows.chebwin(53, at=-40) + assert_array_almost_equal(cheb_odd, cheb_odd_true, decimal=4) + + def test_cheb_even_high_attenuation(self): + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + cheb_even = windows.chebwin(54, at=40) + assert_array_almost_equal(cheb_even, cheb_even_true, decimal=4) + + def test_cheb_odd_low_attenuation(self): + cheb_odd_low_at_true = array([1.000000, 0.519052, 0.586405, + 0.610151, 0.586405, 0.519052, + 1.000000]) + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + cheb_odd = windows.chebwin(7, at=10) + assert_array_almost_equal(cheb_odd, cheb_odd_low_at_true, decimal=4) + + def test_cheb_even_low_attenuation(self): + cheb_even_low_at_true = array([1.000000, 0.451924, 0.51027, + 0.541338, 0.541338, 0.51027, + 0.451924, 1.000000]) + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + cheb_even = windows.chebwin(8, at=-10) + assert_array_almost_equal(cheb_even, cheb_even_low_at_true, decimal=4) + + +exponential_data = { + (4, None, 0.2, False): + array([4.53999297624848542e-05, + 6.73794699908546700e-03, 1.00000000000000000e+00, + 6.73794699908546700e-03]), + (4, None, 0.2, True): array([0.00055308437014783, 0.0820849986238988, + 0.0820849986238988, 0.00055308437014783]), + (4, None, 1.0, False): array([0.1353352832366127, 0.36787944117144233, 1., + 0.36787944117144233]), + (4, None, 1.0, True): array([0.22313016014842982, 0.60653065971263342, + 0.60653065971263342, 0.22313016014842982]), + (4, 2, 0.2, False): + array([4.53999297624848542e-05, 6.73794699908546700e-03, + 1.00000000000000000e+00, 6.73794699908546700e-03]), + (4, 2, 0.2, True): None, + (4, 2, 1.0, False): array([0.1353352832366127, 0.36787944117144233, 1., + 0.36787944117144233]), + (4, 2, 1.0, True): None, + (5, None, 0.2, True): + array([4.53999297624848542e-05, + 6.73794699908546700e-03, 1.00000000000000000e+00, + 6.73794699908546700e-03, 4.53999297624848542e-05]), + (5, None, 1.0, True): array([0.1353352832366127, 0.36787944117144233, 1., + 0.36787944117144233, 0.1353352832366127]), + (5, 2, 0.2, True): None, + (5, 2, 1.0, True): None +} + + +def test_exponential(): + for k, v in exponential_data.items(): + if v is None: + assert_raises(ValueError, windows.exponential, *k) + else: + win = windows.exponential(*k) + assert_allclose(win, v, rtol=1e-14) + + +class TestFlatTop: + + def test_basic(self): + assert_allclose(windows.flattop(6, sym=False), + [-0.000421051, -0.051263156, 0.19821053, 1.0, + 0.19821053, -0.051263156]) + assert_allclose(windows.flattop(7, sym=False), + [-0.000421051, -0.03684078115492348, + 0.01070371671615342, 0.7808739149387698, + 0.7808739149387698, 0.01070371671615342, + -0.03684078115492348]) + assert_allclose(windows.flattop(6), + [-0.000421051, -0.0677142520762119, 0.6068721525762117, + 0.6068721525762117, -0.0677142520762119, + -0.000421051]) + assert_allclose(windows.flattop(7, True), + [-0.000421051, -0.051263156, 0.19821053, 1.0, + 0.19821053, -0.051263156, -0.000421051]) + + +class TestGaussian: + + def test_basic(self): + assert_allclose(windows.gaussian(6, 1.0), + [0.04393693362340742, 0.3246524673583497, + 0.8824969025845955, 0.8824969025845955, + 0.3246524673583497, 0.04393693362340742]) + assert_allclose(windows.gaussian(7, 1.2), + [0.04393693362340742, 0.2493522087772962, + 0.7066482778577162, 1.0, 0.7066482778577162, + 0.2493522087772962, 0.04393693362340742]) + assert_allclose(windows.gaussian(7, 3), + [0.6065306597126334, 0.8007374029168081, + 0.9459594689067654, 1.0, 0.9459594689067654, + 0.8007374029168081, 0.6065306597126334]) + assert_allclose(windows.gaussian(6, 3, False), + [0.6065306597126334, 0.8007374029168081, + 0.9459594689067654, 1.0, 0.9459594689067654, + 0.8007374029168081]) + + +class TestGeneralCosine: + + def test_basic(self): + assert_allclose(windows.general_cosine(5, [0.5, 0.3, 0.2]), + [0.4, 0.3, 1, 0.3, 0.4]) + assert_allclose(windows.general_cosine(4, [0.5, 0.3, 0.2], sym=False), + [0.4, 0.3, 1, 0.3]) + + +class TestGeneralHamming: + + def test_basic(self): + assert_allclose(windows.general_hamming(5, 0.7), + [0.4, 0.7, 1.0, 0.7, 0.4]) + assert_allclose(windows.general_hamming(5, 0.75, sym=False), + [0.5, 0.6727457514, 0.9522542486, + 0.9522542486, 0.6727457514]) + assert_allclose(windows.general_hamming(6, 0.75, sym=True), + [0.5, 0.6727457514, 0.9522542486, + 0.9522542486, 0.6727457514, 0.5]) + + +class TestHamming: + + def test_basic(self): + assert_allclose(windows.hamming(6, False), + [0.08, 0.31, 0.77, 1.0, 0.77, 0.31]) + assert_allclose(windows.hamming(7, sym=False), + [0.08, 0.2531946911449826, 0.6423596296199047, + 0.9544456792351128, 0.9544456792351128, + 0.6423596296199047, 0.2531946911449826]) + assert_allclose(windows.hamming(6), + [0.08, 0.3978521825875242, 0.9121478174124757, + 0.9121478174124757, 0.3978521825875242, 0.08]) + assert_allclose(windows.hamming(7, sym=True), + [0.08, 0.31, 0.77, 1.0, 0.77, 0.31, 0.08]) + + +class TestHann: + + def test_basic(self): + assert_allclose(windows.hann(6, sym=False), + [0, 0.25, 0.75, 1.0, 0.75, 0.25], + rtol=1e-15, atol=1e-15) + assert_allclose(windows.hann(7, sym=False), + [0, 0.1882550990706332, 0.6112604669781572, + 0.9504844339512095, 0.9504844339512095, + 0.6112604669781572, 0.1882550990706332], + rtol=1e-15, atol=1e-15) + assert_allclose(windows.hann(6, True), + [0, 0.3454915028125263, 0.9045084971874737, + 0.9045084971874737, 0.3454915028125263, 0], + rtol=1e-15, atol=1e-15) + assert_allclose(windows.hann(7), + [0, 0.25, 0.75, 1.0, 0.75, 0.25, 0], + rtol=1e-15, atol=1e-15) + + +class TestKaiser: + + def test_basic(self): + assert_allclose(windows.kaiser(6, 0.5), + [0.9403061933191572, 0.9782962393705389, + 0.9975765035372042, 0.9975765035372042, + 0.9782962393705389, 0.9403061933191572]) + assert_allclose(windows.kaiser(7, 0.5), + [0.9403061933191572, 0.9732402256999829, + 0.9932754654413773, 1.0, 0.9932754654413773, + 0.9732402256999829, 0.9403061933191572]) + assert_allclose(windows.kaiser(6, 2.7), + [0.2603047507678832, 0.6648106293528054, + 0.9582099802511439, 0.9582099802511439, + 0.6648106293528054, 0.2603047507678832]) + assert_allclose(windows.kaiser(7, 2.7), + [0.2603047507678832, 0.5985765418119844, + 0.8868495172060835, 1.0, 0.8868495172060835, + 0.5985765418119844, 0.2603047507678832]) + assert_allclose(windows.kaiser(6, 2.7, False), + [0.2603047507678832, 0.5985765418119844, + 0.8868495172060835, 1.0, 0.8868495172060835, + 0.5985765418119844]) + + +class TestKaiserBesselDerived: + + def test_basic(self): + M = 100 + w = windows.kaiser_bessel_derived(M, beta=4.0) + w2 = windows.get_window(('kaiser bessel derived', 4.0), + M, fftbins=False) + assert_allclose(w, w2) + + # Test for Princen-Bradley condition + assert_allclose(w[:M // 2] ** 2 + w[-M // 2:] ** 2, 1.) + + # Test actual values from other implementations + # M = 2: sqrt(2) / 2 + # M = 4: 0.518562710536, 0.855039598640 + # M = 6: 0.436168993154, 0.707106781187, 0.899864772847 + # Ref:https://github.com/scipy/scipy/pull/4747#issuecomment-172849418 + assert_allclose(windows.kaiser_bessel_derived(2, beta=np.pi / 2)[:1], + np.sqrt(2) / 2) + + assert_allclose(windows.kaiser_bessel_derived(4, beta=np.pi / 2)[:2], + [0.518562710536, 0.855039598640]) + + assert_allclose(windows.kaiser_bessel_derived(6, beta=np.pi / 2)[:3], + [0.436168993154, 0.707106781187, 0.899864772847]) + + def test_exceptions(self): + M = 100 + # Assert ValueError for odd window length + msg = ("Kaiser-Bessel Derived windows are only defined for even " + "number of points") + with assert_raises(ValueError, match=msg): + windows.kaiser_bessel_derived(M + 1, beta=4.) + + # Assert ValueError for non-symmetric setting + msg = ("Kaiser-Bessel Derived windows are only defined for " + "symmetric shapes") + with assert_raises(ValueError, match=msg): + windows.kaiser_bessel_derived(M + 1, beta=4., sym=False) + + +class TestNuttall: + + def test_basic(self): + assert_allclose(windows.nuttall(6, sym=False), + [0.0003628, 0.0613345, 0.5292298, 1.0, 0.5292298, + 0.0613345]) + assert_allclose(windows.nuttall(7, sym=False), + [0.0003628, 0.03777576895352025, 0.3427276199688195, + 0.8918518610776603, 0.8918518610776603, + 0.3427276199688196, 0.0377757689535203]) + assert_allclose(windows.nuttall(6), + [0.0003628, 0.1105152530498718, 0.7982580969501282, + 0.7982580969501283, 0.1105152530498719, 0.0003628]) + assert_allclose(windows.nuttall(7, True), + [0.0003628, 0.0613345, 0.5292298, 1.0, 0.5292298, + 0.0613345, 0.0003628]) + + +class TestParzen: + + def test_basic(self): + assert_allclose(windows.parzen(6), + [0.009259259259259254, 0.25, 0.8611111111111112, + 0.8611111111111112, 0.25, 0.009259259259259254]) + assert_allclose(windows.parzen(7, sym=True), + [0.00583090379008747, 0.1574344023323616, + 0.6501457725947521, 1.0, 0.6501457725947521, + 0.1574344023323616, 0.00583090379008747]) + assert_allclose(windows.parzen(6, False), + [0.00583090379008747, 0.1574344023323616, + 0.6501457725947521, 1.0, 0.6501457725947521, + 0.1574344023323616]) + + +class TestTriang: + + def test_basic(self): + + assert_allclose(windows.triang(6, True), + [1/6, 1/2, 5/6, 5/6, 1/2, 1/6]) + assert_allclose(windows.triang(7), + [1/4, 1/2, 3/4, 1, 3/4, 1/2, 1/4]) + assert_allclose(windows.triang(6, sym=False), + [1/4, 1/2, 3/4, 1, 3/4, 1/2]) + + +tukey_data = { + (4, 0.5, True): array([0.0, 1.0, 1.0, 0.0]), + (4, 0.9, True): array([0.0, 0.84312081893436686, + 0.84312081893436686, 0.0]), + (4, 1.0, True): array([0.0, 0.75, 0.75, 0.0]), + (4, 0.5, False): array([0.0, 1.0, 1.0, 1.0]), + (4, 0.9, False): array([0.0, 0.58682408883346526, + 1.0, 0.58682408883346526]), + (4, 1.0, False): array([0.0, 0.5, 1.0, 0.5]), + (5, 0.0, True): array([1.0, 1.0, 1.0, 1.0, 1.0]), + (5, 0.8, True): array([0.0, 0.69134171618254492, + 1.0, 0.69134171618254492, 0.0]), + (5, 1.0, True): array([0.0, 0.5, 1.0, 0.5, 0.0]), + + (6, 0): [1, 1, 1, 1, 1, 1], + (7, 0): [1, 1, 1, 1, 1, 1, 1], + (6, .25): [0, 1, 1, 1, 1, 0], + (7, .25): [0, 1, 1, 1, 1, 1, 0], + (6,): [0, 0.9045084971874737, 1.0, 1.0, 0.9045084971874735, 0], + (7,): [0, 0.75, 1.0, 1.0, 1.0, 0.75, 0], + (6, .75): [0, 0.5522642316338269, 1.0, 1.0, 0.5522642316338267, 0], + (7, .75): [0, 0.4131759111665348, 0.9698463103929542, 1.0, + 0.9698463103929542, 0.4131759111665347, 0], + (6, 1): [0, 0.3454915028125263, 0.9045084971874737, 0.9045084971874737, + 0.3454915028125263, 0], + (7, 1): [0, 0.25, 0.75, 1.0, 0.75, 0.25, 0], +} + + +class TestTukey: + + def test_basic(self): + # Test against hardcoded data + for k, v in tukey_data.items(): + if v is None: + assert_raises(ValueError, windows.tukey, *k) + else: + win = windows.tukey(*k) + assert_allclose(win, v, rtol=1e-15, atol=1e-15) + + def test_extremes(self): + # Test extremes of alpha correspond to boxcar and hann + tuk0 = windows.tukey(100, 0) + box0 = windows.boxcar(100) + assert_array_almost_equal(tuk0, box0) + + tuk1 = windows.tukey(100, 1) + han1 = windows.hann(100) + assert_array_almost_equal(tuk1, han1) + + +dpss_data = { + # All values from MATLAB: + # * taper[1] of (3, 1.4, 3) sign-flipped + # * taper[3] of (5, 1.5, 5) sign-flipped + (4, 0.1, 2): ([[0.497943898, 0.502047681, 0.502047681, 0.497943898], [0.670487993, 0.224601537, -0.224601537, -0.670487993]], [0.197961815, 0.002035474]), # noqa: E501 + (3, 1.4, 3): ([[0.410233151, 0.814504464, 0.410233151], [0.707106781, 0.0, -0.707106781], [0.575941629, -0.580157287, 0.575941629]], [0.999998093, 0.998067480, 0.801934426]), # noqa: E501 + (5, 1.5, 5): ([[0.1745071052, 0.4956749177, 0.669109327, 0.495674917, 0.174507105], [0.4399493348, 0.553574369, 0.0, -0.553574369, -0.439949334], [0.631452756, 0.073280238, -0.437943884, 0.073280238, 0.631452756], [0.553574369, -0.439949334, 0.0, 0.439949334, -0.553574369], [0.266110290, -0.498935248, 0.600414741, -0.498935248, 0.266110290147157]], [0.999728571, 0.983706916, 0.768457889, 0.234159338, 0.013947282907567]), # noqa: E501 + (100, 2, 4): ([[0.0030914414, 0.0041266922, 0.005315076, 0.006665149, 0.008184854, 0.0098814158, 0.011761239, 0.013829809, 0.016091597, 0.018549973, 0.02120712, 0.02406396, 0.027120092, 0.030373728, 0.033821651, 0.037459181, 0.041280145, 0.045276872, 0.049440192, 0.053759447, 0.058222524, 0.062815894, 0.067524661, 0.072332638, 0.077222418, 0.082175473, 0.087172252, 0.092192299, 0.097214376, 0.1022166, 0.10717657, 0.11207154, 0.11687856, 0.12157463, 0.12613686, 0.13054266, 0.13476986, 0.13879691, 0.14260302, 0.14616832, 0.14947401, 0.1525025, 0.15523755, 0.15766438, 0.15976981, 0.16154233, 0.16297223, 0.16405162, 0.16477455, 0.16513702, 0.16513702, 0.16477455, 0.16405162, 0.16297223, 0.16154233, 0.15976981, 0.15766438, 0.15523755, 0.1525025, 0.14947401, 0.14616832, 0.14260302, 0.13879691, 0.13476986, 0.13054266, 0.12613686, 0.12157463, 0.11687856, 0.11207154, 0.10717657, 0.1022166, 0.097214376, 0.092192299, 0.087172252, 0.082175473, 0.077222418, 0.072332638, 0.067524661, 0.062815894, 0.058222524, 0.053759447, 0.049440192, 0.045276872, 0.041280145, 0.037459181, 0.033821651, 0.030373728, 0.027120092, 0.02406396, 0.02120712, 0.018549973, 0.016091597, 0.013829809, 0.011761239, 0.0098814158, 0.008184854, 0.006665149, 0.005315076, 0.0041266922, 0.0030914414], [0.018064449, 0.022040342, 0.026325013, 0.030905288, 0.035764398, 0.040881982, 0.046234148, 0.051793558, 0.057529559, 0.063408356, 0.069393216, 0.075444716, 0.081521022, 0.087578202, 0.093570567, 0.099451049, 0.10517159, 0.11068356, 0.11593818, 0.12088699, 0.12548227, 0.12967752, 0.1334279, 0.13669069, 0.13942569, 0.1415957, 0.14316686, 0.14410905, 0.14439626, 0.14400686, 0.14292389, 0.1411353, 0.13863416, 0.13541876, 0.13149274, 0.12686516, 0.12155045, 0.1155684, 0.10894403, 0.10170748, 0.093893752, 0.08554251, 0.076697768, 0.067407559, 0.057723559, 0.04770068, 0.037396627, 0.026871428, 0.016186944, 0.0054063557, -0.0054063557, -0.016186944, -0.026871428, -0.037396627, -0.04770068, -0.057723559, -0.067407559, -0.076697768, -0.08554251, -0.093893752, -0.10170748, -0.10894403, -0.1155684, -0.12155045, -0.12686516, -0.13149274, -0.13541876, -0.13863416, -0.1411353, -0.14292389, -0.14400686, -0.14439626, -0.14410905, -0.14316686, -0.1415957, -0.13942569, -0.13669069, -0.1334279, -0.12967752, -0.12548227, -0.12088699, -0.11593818, -0.11068356, -0.10517159, -0.099451049, -0.093570567, -0.087578202, -0.081521022, -0.075444716, -0.069393216, -0.063408356, -0.057529559, -0.051793558, -0.046234148, -0.040881982, -0.035764398, -0.030905288, -0.026325013, -0.022040342, -0.018064449], [0.064817553, 0.072567801, 0.080292992, 0.087918235, 0.095367076, 0.10256232, 0.10942687, 0.1158846, 0.12186124, 0.12728523, 0.13208858, 0.13620771, 0.13958427, 0.14216587, 0.14390678, 0.14476863, 0.1447209, 0.14374148, 0.14181704, 0.13894336, 0.13512554, 0.13037812, 0.1247251, 0.11819984, 0.11084487, 0.10271159, 0.093859853, 0.084357497, 0.074279719, 0.063708406, 0.052731374, 0.041441525, 0.029935953, 0.018314987, 0.0066811877, -0.0048616765, -0.016209689, -0.027259848, -0.037911124, -0.048065512, -0.05762905, -0.066512804, -0.0746338, -0.081915903, -0.088290621, -0.09369783, -0.098086416, -0.10141482, -0.10365146, -0.10477512, -0.10477512, -0.10365146, -0.10141482, -0.098086416, -0.09369783, -0.088290621, -0.081915903, -0.0746338, -0.066512804, -0.05762905, -0.048065512, -0.037911124, -0.027259848, -0.016209689, -0.0048616765, 0.0066811877, 0.018314987, 0.029935953, 0.041441525, 0.052731374, 0.063708406, 0.074279719, 0.084357497, 0.093859853, 0.10271159, 0.11084487, 0.11819984, 0.1247251, 0.13037812, 0.13512554, 0.13894336, 0.14181704, 0.14374148, 0.1447209, 0.14476863, 0.14390678, 0.14216587, 0.13958427, 0.13620771, 0.13208858, 0.12728523, 0.12186124, 0.1158846, 0.10942687, 0.10256232, 0.095367076, 0.087918235, 0.080292992, 0.072567801, 0.064817553], [0.14985551, 0.15512305, 0.15931467, 0.16236806, 0.16423291, 0.16487165, 0.16426009, 0.1623879, 0.1592589, 0.15489114, 0.14931693, 0.14258255, 0.13474785, 0.1258857, 0.11608124, 0.10543095, 0.094041635, 0.082029213, 0.069517411, 0.056636348, 0.043521028, 0.030309756, 0.017142511, 0.0041592774, -0.0085016282, -0.020705223, -0.032321494, -0.043226982, -0.053306291, -0.062453515, -0.070573544, -0.077583253, -0.083412547, -0.088005244, -0.091319802, -0.093329861, -0.094024602, -0.093408915, -0.091503383, -0.08834406, -0.08398207, -0.078483012, -0.071926192, -0.064403681, -0.056019215, -0.046886954, -0.037130106, -0.026879442, -0.016271713, -0.005448, 0.005448, 0.016271713, 0.026879442, 0.037130106, 0.046886954, 0.056019215, 0.064403681, 0.071926192, 0.078483012, 0.08398207, 0.08834406, 0.091503383, 0.093408915, 0.094024602, 0.093329861, 0.091319802, 0.088005244, 0.083412547, 0.077583253, 0.070573544, 0.062453515, 0.053306291, 0.043226982, 0.032321494, 0.020705223, 0.0085016282, -0.0041592774, -0.017142511, -0.030309756, -0.043521028, -0.056636348, -0.069517411, -0.082029213, -0.094041635, -0.10543095, -0.11608124, -0.1258857, -0.13474785, -0.14258255, -0.14931693, -0.15489114, -0.1592589, -0.1623879, -0.16426009, -0.16487165, -0.16423291, -0.16236806, -0.15931467, -0.15512305, -0.14985551]], [0.999943140, 0.997571533, 0.959465463, 0.721862496]), # noqa: E501 +} + + +class TestDPSS: + + def test_basic(self): + # Test against hardcoded data + for k, v in dpss_data.items(): + win, ratios = windows.dpss(*k, return_ratios=True) + assert_allclose(win, v[0], atol=1e-7, err_msg=k) + assert_allclose(ratios, v[1], rtol=1e-5, atol=1e-7, err_msg=k) + + def test_unity(self): + # Test unity value handling (gh-2221) + for M in range(1, 21): + # corrected w/approximation (default) + win = windows.dpss(M, M / 2.1) + expected = M % 2 # one for odd, none for even + assert_equal(np.isclose(win, 1.).sum(), expected, + err_msg=f'{win}') + # corrected w/subsample delay (slower) + win_sub = windows.dpss(M, M / 2.1, norm='subsample') + if M > 2: + # @M=2 the subsample doesn't do anything + assert_equal(np.isclose(win_sub, 1.).sum(), expected, + err_msg=f'{win_sub}') + assert_allclose(win, win_sub, rtol=0.03) # within 3% + # not the same, l2-norm + win_2 = windows.dpss(M, M / 2.1, norm=2) + expected = 1 if M == 1 else 0 + assert_equal(np.isclose(win_2, 1.).sum(), expected, + err_msg=f'{win_2}') + + def test_extremes(self): + # Test extremes of alpha + lam = windows.dpss(31, 6, 4, return_ratios=True)[1] + assert_array_almost_equal(lam, 1.) + lam = windows.dpss(31, 7, 4, return_ratios=True)[1] + assert_array_almost_equal(lam, 1.) + lam = windows.dpss(31, 8, 4, return_ratios=True)[1] + assert_array_almost_equal(lam, 1.) + + def test_degenerate(self): + # Test failures + assert_raises(ValueError, windows.dpss, 4, 1.5, -1) # Bad Kmax + assert_raises(ValueError, windows.dpss, 4, 1.5, -5) + assert_raises(TypeError, windows.dpss, 4, 1.5, 1.1) + assert_raises(ValueError, windows.dpss, 3, 1.5, 3) # NW must be < N/2. + assert_raises(ValueError, windows.dpss, 3, -1, 3) # NW must be pos + assert_raises(ValueError, windows.dpss, 3, 0, 3) + assert_raises(ValueError, windows.dpss, -1, 1, 3) # negative M + + +class TestLanczos: + + def test_basic(self): + # Analytical results: + # sinc(x) = sinc(-x) + # sinc(pi) = 0, sinc(0) = 1 + # Hand computation on WolframAlpha: + # sinc(2 pi / 3) = 0.413496672 + # sinc(pi / 3) = 0.826993343 + # sinc(3 pi / 5) = 0.504551152 + # sinc(pi / 5) = 0.935489284 + assert_allclose(windows.lanczos(6, sym=False), + [0., 0.413496672, + 0.826993343, 1., 0.826993343, + 0.413496672], + atol=1e-9) + assert_allclose(windows.lanczos(6), + [0., 0.504551152, + 0.935489284, 0.935489284, + 0.504551152, 0.], + atol=1e-9) + assert_allclose(windows.lanczos(7, sym=True), + [0., 0.413496672, + 0.826993343, 1., 0.826993343, + 0.413496672, 0.], + atol=1e-9) + + def test_array_size(self): + for n in [0, 10, 11]: + assert_equal(len(windows.lanczos(n, sym=False)), n) + assert_equal(len(windows.lanczos(n, sym=True)), n) + + +class TestGetWindow: + + def test_boxcar(self): + w = windows.get_window('boxcar', 12) + assert_array_equal(w, np.ones_like(w)) + + # window is a tuple of len 1 + w = windows.get_window(('boxcar',), 16) + assert_array_equal(w, np.ones_like(w)) + + def test_cheb_odd(self): + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + w = windows.get_window(('chebwin', -40), 53, fftbins=False) + assert_array_almost_equal(w, cheb_odd_true, decimal=4) + + def test_cheb_even(self): + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + w = windows.get_window(('chebwin', 40), 54, fftbins=False) + assert_array_almost_equal(w, cheb_even_true, decimal=4) + + def test_dpss(self): + win1 = windows.get_window(('dpss', 3), 64, fftbins=False) + win2 = windows.dpss(64, 3) + assert_array_almost_equal(win1, win2, decimal=4) + + def test_kaiser_float(self): + win1 = windows.get_window(7.2, 64) + win2 = windows.kaiser(64, 7.2, False) + assert_allclose(win1, win2) + + def test_invalid_inputs(self): + # Window is not a float, tuple, or string + assert_raises(ValueError, windows.get_window, set('hann'), 8) + + # Unknown window type error + assert_raises(ValueError, windows.get_window, 'broken', 4) + + def test_array_as_window(self): + # github issue 3603 + osfactor = 128 + sig = np.arange(128) + + win = windows.get_window(('kaiser', 8.0), osfactor // 2) + with assert_raises(ValueError, match='must have the same length'): + resample(sig, len(sig) * osfactor, window=win) + + def test_general_cosine(self): + assert_allclose(get_window(('general_cosine', [0.5, 0.3, 0.2]), 4), + [0.4, 0.3, 1, 0.3]) + assert_allclose(get_window(('general_cosine', [0.5, 0.3, 0.2]), 4, + fftbins=False), + [0.4, 0.55, 0.55, 0.4]) + + def test_general_hamming(self): + assert_allclose(get_window(('general_hamming', 0.7), 5), + [0.4, 0.6072949, 0.9427051, 0.9427051, 0.6072949]) + assert_allclose(get_window(('general_hamming', 0.7), 5, fftbins=False), + [0.4, 0.7, 1.0, 0.7, 0.4]) + + def test_lanczos(self): + assert_allclose(get_window('lanczos', 6), + [0., 0.413496672, 0.826993343, 1., 0.826993343, + 0.413496672], atol=1e-9) + assert_allclose(get_window('lanczos', 6, fftbins=False), + [0., 0.504551152, 0.935489284, 0.935489284, + 0.504551152, 0.], atol=1e-9) + assert_allclose(get_window('lanczos', 6), get_window('sinc', 6)) + + +def test_windowfunc_basics(): + for window_name, params in window_funcs: + window = getattr(windows, window_name) + with suppress_warnings() as sup: + sup.filter(UserWarning, "This window is not suitable") + # Check symmetry for odd and even lengths + w1 = window(8, *params, sym=True) + w2 = window(7, *params, sym=False) + assert_array_almost_equal(w1[:-1], w2) + + w1 = window(9, *params, sym=True) + w2 = window(8, *params, sym=False) + assert_array_almost_equal(w1[:-1], w2) + + # Check that functions run and output lengths are correct + assert_equal(len(window(6, *params, sym=True)), 6) + assert_equal(len(window(6, *params, sym=False)), 6) + assert_equal(len(window(7, *params, sym=True)), 7) + assert_equal(len(window(7, *params, sym=False)), 7) + + # Check invalid lengths + assert_raises(ValueError, window, 5.5, *params) + assert_raises(ValueError, window, -7, *params) + + # Check degenerate cases + assert_array_equal(window(0, *params, sym=True), []) + assert_array_equal(window(0, *params, sym=False), []) + assert_array_equal(window(1, *params, sym=True), [1]) + assert_array_equal(window(1, *params, sym=False), [1]) + + # Check dtype + assert_(window(0, *params, sym=True).dtype == 'float') + assert_(window(0, *params, sym=False).dtype == 'float') + assert_(window(1, *params, sym=True).dtype == 'float') + assert_(window(1, *params, sym=False).dtype == 'float') + assert_(window(6, *params, sym=True).dtype == 'float') + assert_(window(6, *params, sym=False).dtype == 'float') + + # Check normalization + assert_array_less(window(10, *params, sym=True), 1.01) + assert_array_less(window(10, *params, sym=False), 1.01) + assert_array_less(window(9, *params, sym=True), 1.01) + assert_array_less(window(9, *params, sym=False), 1.01) + + # Check that DFT-even spectrum is purely real for odd and even + assert_allclose(fft(window(10, *params, sym=False)).imag, + 0, atol=1e-14) + assert_allclose(fft(window(11, *params, sym=False)).imag, + 0, atol=1e-14) + + +def test_needs_params(): + for winstr in ['kaiser', 'ksr', 'kaiser_bessel_derived', 'kbd', + 'gaussian', 'gauss', 'gss', + 'general gaussian', 'general_gaussian', + 'general gauss', 'general_gauss', 'ggs', + 'dss', 'dpss', 'general cosine', 'general_cosine', + 'chebwin', 'cheb', 'general hamming', 'general_hamming', + ]: + assert_raises(ValueError, get_window, winstr, 7) + + +def test_not_needs_params(): + for winstr in ['barthann', + 'bartlett', + 'blackman', + 'blackmanharris', + 'bohman', + 'boxcar', + 'cosine', + 'flattop', + 'hamming', + 'nuttall', + 'parzen', + 'taylor', + 'exponential', + 'poisson', + 'tukey', + 'tuk', + 'triangle', + 'lanczos', + 'sinc', + ]: + win = get_window(winstr, 7) + assert_equal(len(win), 7) + + +def test_symmetric(): + + for win in [windows.lanczos]: + # Even sampling points + w = win(4096) + error = np.max(np.abs(w-np.flip(w))) + assert_equal(error, 0.0) + + # Odd sampling points + w = win(4097) + error = np.max(np.abs(w-np.flip(w))) + assert_equal(error, 0.0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/waveforms.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/waveforms.py new file mode 100644 index 0000000000000000000000000000000000000000..2fa191bef7febde46310e25a8886037539370881 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/waveforms.py @@ -0,0 +1,21 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'sawtooth', 'square', 'gausspulse', 'chirp', 'sweep_poly', + 'unit_impulse', 'place', 'nan', 'mod', 'extract', 'log', 'exp', + 'polyval', 'polyint' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="waveforms", + private_modules=["_waveforms"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/wavelets.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/wavelets.py new file mode 100644 index 0000000000000000000000000000000000000000..0391ec2ab35dd9787e7f254765c012808d40996c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/wavelets.py @@ -0,0 +1,20 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'daub', 'qmf', 'cascade', 'morlet', 'ricker', 'morlet2', 'cwt', + 'eig', 'comb', 'convolve' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal", module="wavelets", + private_modules=["_wavelets"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..967a7c758f69c1c8002d886d78832904c402d2b3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/__init__.py @@ -0,0 +1,52 @@ +""" +Window functions (:mod:`scipy.signal.windows`) +============================================== + +The suite of window functions for filtering and spectral estimation. + +.. currentmodule:: scipy.signal.windows + +.. autosummary:: + :toctree: generated/ + + get_window -- Return a window of a given length and type. + + barthann -- Bartlett-Hann window + bartlett -- Bartlett window + blackman -- Blackman window + blackmanharris -- Minimum 4-term Blackman-Harris window + bohman -- Bohman window + boxcar -- Boxcar window + chebwin -- Dolph-Chebyshev window + cosine -- Cosine window + dpss -- Discrete prolate spheroidal sequences + exponential -- Exponential window + flattop -- Flat top window + gaussian -- Gaussian window + general_cosine -- Generalized Cosine window + general_gaussian -- Generalized Gaussian window + general_hamming -- Generalized Hamming window + hamming -- Hamming window + hann -- Hann window + kaiser -- Kaiser window + kaiser_bessel_derived -- Kaiser-Bessel derived window + lanczos -- Lanczos window also known as a sinc window + nuttall -- Nuttall's minimum 4-term Blackman-Harris window + parzen -- Parzen window + taylor -- Taylor window + triang -- Triangular window + tukey -- Tukey window + +""" + +from ._windows import * + +# Deprecated namespaces, to be removed in v2.0.0 +from . import windows + +__all__ = ['boxcar', 'triang', 'parzen', 'bohman', 'blackman', 'nuttall', + 'blackmanharris', 'flattop', 'bartlett', 'barthann', + 'hamming', 'kaiser', 'kaiser_bessel_derived', 'gaussian', + 'general_gaussian', 'general_cosine', 'general_hamming', + 'chebwin', 'cosine', 'hann', 'exponential', 'tukey', 'taylor', + 'get_window', 'dpss', 'lanczos'] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/_windows.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/_windows.py new file mode 100644 index 0000000000000000000000000000000000000000..bafd48b2457633c569dae640ca83158163820513 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/_windows.py @@ -0,0 +1,2374 @@ +"""The suite of window functions.""" + +import operator +import warnings + +import numpy as np +from scipy import linalg, special, fft as sp_fft + +__all__ = ['boxcar', 'triang', 'parzen', 'bohman', 'blackman', 'nuttall', + 'blackmanharris', 'flattop', 'bartlett', 'barthann', + 'hamming', 'kaiser', 'kaiser_bessel_derived', 'gaussian', + 'general_cosine', 'general_gaussian', 'general_hamming', + 'chebwin', 'cosine', 'hann', 'exponential', 'tukey', 'taylor', + 'dpss', 'get_window', 'lanczos'] + + +def _len_guards(M): + """Handle small or incorrect window lengths""" + if int(M) != M or M < 0: + raise ValueError('Window length M must be a non-negative integer') + return M <= 1 + + +def _extend(M, sym): + """Extend window by 1 sample if needed for DFT-even symmetry""" + if not sym: + return M + 1, True + else: + return M, False + + +def _truncate(w, needed): + """Truncate window by 1 sample if needed for DFT-even symmetry""" + if needed: + return w[:-1] + else: + return w + + +def general_cosine(M, a, sym=True): + r""" + Generic weighted sum of cosine terms window + + Parameters + ---------- + M : int + Number of points in the output window + a : array_like + Sequence of weighting coefficients. This uses the convention of being + centered on the origin, so these will typically all be positive + numbers, not alternating sign. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The array of window values. + + References + ---------- + .. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE + Transactions on Acoustics, Speech, and Signal Processing, vol. 29, + no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`. + .. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the + Discrete Fourier transform (DFT), including a comprehensive list of + window functions and some new flat-top windows", February 15, 2002 + https://holometer.fnal.gov/GH_FFT.pdf + + Examples + -------- + Heinzel describes a flat-top window named "HFT90D" with formula: [2]_ + + .. math:: w_j = 1 - 1.942604 \cos(z) + 1.340318 \cos(2z) + - 0.440811 \cos(3z) + 0.043097 \cos(4z) + + where + + .. math:: z = \frac{2 \pi j}{N}, j = 0...N - 1 + + Since this uses the convention of starting at the origin, to reproduce the + window, we need to convert every other coefficient to a positive number: + + >>> HFT90D = [1, 1.942604, 1.340318, 0.440811, 0.043097] + + The paper states that the highest sidelobe is at -90.2 dB. Reproduce + Figure 42 by plotting the window and its frequency response, and confirm + the sidelobe level in red: + + >>> import numpy as np + >>> from scipy.signal.windows import general_cosine + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = general_cosine(1000, HFT90D, sym=False) + >>> plt.plot(window) + >>> plt.title("HFT90D window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 10000) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = np.abs(fftshift(A / abs(A).max())) + >>> response = 20 * np.log10(np.maximum(response, 1e-10)) + >>> plt.plot(freq, response) + >>> plt.axis([-50/1000, 50/1000, -140, 0]) + >>> plt.title("Frequency response of the HFT90D window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + >>> plt.axhline(-90.2, color='red') + >>> plt.show() + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + fac = np.linspace(-np.pi, np.pi, M) + w = np.zeros(M) + for k in range(len(a)): + w += a[k] * np.cos(k * fac) + + return _truncate(w, needs_trunc) + + +def boxcar(M, sym=True): + """Return a boxcar or rectangular window. + + Also known as a rectangular window or Dirichlet window, this is equivalent + to no window at all. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + Whether the window is symmetric. (Has no effect for boxcar.) + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.boxcar(51) + >>> plt.plot(window) + >>> plt.title("Boxcar window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the boxcar window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + w = np.ones(M, float) + + return _truncate(w, needs_trunc) + + +def triang(M, sym=True): + """Return a triangular window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + See Also + -------- + bartlett : A triangular window that touches zero + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.triang(51) + >>> plt.plot(window) + >>> plt.title("Triangular window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = np.abs(fftshift(A / abs(A).max())) + >>> response = 20 * np.log10(np.maximum(response, 1e-10)) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the triangular window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(1, (M + 1) // 2 + 1) + if M % 2 == 0: + w = (2 * n - 1.0) / M + w = np.r_[w, w[::-1]] + else: + w = 2 * n / (M + 1.0) + w = np.r_[w, w[-2::-1]] + + return _truncate(w, needs_trunc) + + +def parzen(M, sym=True): + """Return a Parzen window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + References + ---------- + .. [1] E. Parzen, "Mathematical Considerations in the Estimation of + Spectra", Technometrics, Vol. 3, No. 2 (May, 1961), pp. 167-190 + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.parzen(51) + >>> plt.plot(window) + >>> plt.title("Parzen window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Parzen window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(-(M - 1) / 2.0, (M - 1) / 2.0 + 0.5, 1.0) + na = np.extract(n < -(M - 1) / 4.0, n) + nb = np.extract(abs(n) <= (M - 1) / 4.0, n) + wa = 2 * (1 - np.abs(na) / (M / 2.0)) ** 3.0 + wb = (1 - 6 * (np.abs(nb) / (M / 2.0)) ** 2.0 + + 6 * (np.abs(nb) / (M / 2.0)) ** 3.0) + w = np.r_[wa, wb, wa[::-1]] + + return _truncate(w, needs_trunc) + + +def bohman(M, sym=True): + """Return a Bohman window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.bohman(51) + >>> plt.plot(window) + >>> plt.title("Bohman window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2047) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Bohman window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + fac = np.abs(np.linspace(-1, 1, M)[1:-1]) + w = (1 - fac) * np.cos(np.pi * fac) + 1.0 / np.pi * np.sin(np.pi * fac) + w = np.r_[0, w, 0] + + return _truncate(w, needs_trunc) + + +def blackman(M, sym=True): + r""" + Return a Blackman window. + + The Blackman window is a taper formed by using the first three terms of + a summation of cosines. It was designed to have close to the minimal + leakage possible. It is close to optimal, only slightly worse than a + Kaiser window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Blackman window is defined as + + .. math:: w(n) = 0.42 - 0.5 \cos(2\pi n/M) + 0.08 \cos(4\pi n/M) + + The "exact Blackman" window was designed to null out the third and fourth + sidelobes, but has discontinuities at the boundaries, resulting in a + 6 dB/oct fall-off. This window is an approximation of the "exact" window, + which does not null the sidelobes as well, but is smooth at the edges, + improving the fall-off rate to 18 dB/oct. [3]_ + + Most references to the Blackman window come from the signal processing + literature, where it is used as one of many windowing functions for + smoothing values. It is also known as an apodization (which means + "removing the foot", i.e. smoothing discontinuities at the beginning + and end of the sampled signal) or tapering function. It is known as a + "near optimal" tapering function, almost as good (by some measures) + as the Kaiser window. + + References + ---------- + .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power + spectra, Dover Publications, New York. + .. [2] Oppenheim, A.V., and R.W. Schafer. Discrete-Time Signal Processing. + Upper Saddle River, NJ: Prentice-Hall, 1999, pp. 468-471. + .. [3] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic + Analysis with the Discrete Fourier Transform". Proceedings of the + IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837`. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.blackman(51) + >>> plt.plot(window) + >>> plt.title("Blackman window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = np.abs(fftshift(A / abs(A).max())) + >>> response = 20 * np.log10(np.maximum(response, 1e-10)) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Blackman window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + # Docstring adapted from NumPy's blackman function + return general_cosine(M, [0.42, 0.50, 0.08], sym) + + +def nuttall(M, sym=True): + """Return a minimum 4-term Blackman-Harris window according to Nuttall. + + This variation is called "Nuttall4c" by Heinzel. [2]_ + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + References + ---------- + .. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE + Transactions on Acoustics, Speech, and Signal Processing, vol. 29, + no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`. + .. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the + Discrete Fourier transform (DFT), including a comprehensive list of + window functions and some new flat-top windows", February 15, 2002 + https://holometer.fnal.gov/GH_FFT.pdf + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.nuttall(51) + >>> plt.plot(window) + >>> plt.title("Nuttall window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Nuttall window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + return general_cosine(M, [0.3635819, 0.4891775, 0.1365995, 0.0106411], sym) + + +def blackmanharris(M, sym=True): + """Return a minimum 4-term Blackman-Harris window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.blackmanharris(51) + >>> plt.plot(window) + >>> plt.title("Blackman-Harris window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Blackman-Harris window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + return general_cosine(M, [0.35875, 0.48829, 0.14128, 0.01168], sym) + + +def flattop(M, sym=True): + """Return a flat top window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + Flat top windows are used for taking accurate measurements of signal + amplitude in the frequency domain, with minimal scalloping error from the + center of a frequency bin to its edges, compared to others. This is a + 5th-order cosine window, with the 5 terms optimized to make the main lobe + maximally flat. [1]_ + + References + ---------- + .. [1] D'Antona, Gabriele, and A. Ferrero, "Digital Signal Processing for + Measurement Systems", Springer Media, 2006, p. 70 + :doi:`10.1007/0-387-28666-7`. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.flattop(51) + >>> plt.plot(window) + >>> plt.title("Flat top window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the flat top window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + a = [0.21557895, 0.41663158, 0.277263158, 0.083578947, 0.006947368] + return general_cosine(M, a, sym) + + +def bartlett(M, sym=True): + r""" + Return a Bartlett window. + + The Bartlett window is very similar to a triangular window, except + that the end points are at zero. It is often used in signal + processing for tapering a signal, without generating too much + ripple in the frequency domain. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The triangular window, with the first and last samples equal to zero + and the maximum value normalized to 1 (though the value 1 does not + appear if `M` is even and `sym` is True). + + See Also + -------- + triang : A triangular window that does not touch zero at the ends + + Notes + ----- + The Bartlett window is defined as + + .. math:: w(n) = \frac{2}{M-1} \left( + \frac{M-1}{2} - \left|n - \frac{M-1}{2}\right| + \right) + + Most references to the Bartlett window come from the signal + processing literature, where it is used as one of many windowing + functions for smoothing values. Note that convolution with this + window produces linear interpolation. It is also known as an + apodization (which means"removing the foot", i.e. smoothing + discontinuities at the beginning and end of the sampled signal) or + tapering function. The Fourier transform of the Bartlett is the product + of two sinc functions. + Note the excellent discussion in Kanasewich. [2]_ + + References + ---------- + .. [1] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra", + Biometrika 37, 1-16, 1950. + .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", + The University of Alberta Press, 1975, pp. 109-110. + .. [3] A.V. Oppenheim and R.W. Schafer, "Discrete-Time Signal + Processing", Prentice-Hall, 1999, pp. 468-471. + .. [4] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function + .. [5] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, + "Numerical Recipes", Cambridge University Press, 1986, page 429. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.bartlett(51) + >>> plt.plot(window) + >>> plt.title("Bartlett window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Bartlett window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + # Docstring adapted from NumPy's bartlett function + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) + w = np.where(np.less_equal(n, (M - 1) / 2.0), + 2.0 * n / (M - 1), 2.0 - 2.0 * n / (M - 1)) + + return _truncate(w, needs_trunc) + + +def hann(M, sym=True): + r""" + Return a Hann window. + + The Hann window is a taper formed by using a raised cosine or sine-squared + with ends that touch zero. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Hann window is defined as + + .. math:: w(n) = 0.5 - 0.5 \cos\left(\frac{2\pi{n}}{M-1}\right) + \qquad 0 \leq n \leq M-1 + + The window was named for Julius von Hann, an Austrian meteorologist. It is + also known as the Cosine Bell. It is sometimes erroneously referred to as + the "Hanning" window, from the use of "hann" as a verb in the original + paper and confusion with the very similar Hamming window. + + Most references to the Hann window come from the signal processing + literature, where it is used as one of many windowing functions for + smoothing values. It is also known as an apodization (which means + "removing the foot", i.e. smoothing discontinuities at the beginning + and end of the sampled signal) or tapering function. + + References + ---------- + .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power + spectra, Dover Publications, New York. + .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", + The University of Alberta Press, 1975, pp. 106-108. + .. [3] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function + .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, + "Numerical Recipes", Cambridge University Press, 1986, page 425. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.hann(51) + >>> plt.plot(window) + >>> plt.title("Hann window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = np.abs(fftshift(A / abs(A).max())) + >>> response = 20 * np.log10(np.maximum(response, 1e-10)) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Hann window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + # Docstring adapted from NumPy's hanning function + return general_hamming(M, 0.5, sym) + + +def tukey(M, alpha=0.5, sym=True): + r"""Return a Tukey window, also known as a tapered cosine window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + alpha : float, optional + Shape parameter of the Tukey window, representing the fraction of the + window inside the cosine tapered region. + If zero, the Tukey window is equivalent to a rectangular window. + If one, the Tukey window is equivalent to a Hann window. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + References + ---------- + .. [1] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic + Analysis with the Discrete Fourier Transform". Proceedings of the + IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837` + .. [2] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function#Tukey_window + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.tukey(51) + >>> plt.plot(window) + >>> plt.title("Tukey window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + >>> plt.ylim([0, 1.1]) + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Tukey window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + + if alpha <= 0: + return np.ones(M, 'd') + elif alpha >= 1.0: + return hann(M, sym=sym) + + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) + width = int(np.floor(alpha*(M-1)/2.0)) + n1 = n[0:width+1] + n2 = n[width+1:M-width-1] + n3 = n[M-width-1:] + + w1 = 0.5 * (1 + np.cos(np.pi * (-1 + 2.0*n1/alpha/(M-1)))) + w2 = np.ones(n2.shape) + w3 = 0.5 * (1 + np.cos(np.pi * (-2.0/alpha + 1 + 2.0*n3/alpha/(M-1)))) + + w = np.concatenate((w1, w2, w3)) + + return _truncate(w, needs_trunc) + + +def barthann(M, sym=True): + """Return a modified Bartlett-Hann window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.barthann(51) + >>> plt.plot(window) + >>> plt.title("Bartlett-Hann window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Bartlett-Hann window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) + fac = np.abs(n / (M - 1.0) - 0.5) + w = 0.62 - 0.48 * fac + 0.38 * np.cos(2 * np.pi * fac) + + return _truncate(w, needs_trunc) + + +def general_hamming(M, alpha, sym=True): + r"""Return a generalized Hamming window. + + The generalized Hamming window is constructed by multiplying a rectangular + window by one period of a cosine function [1]_. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + alpha : float + The window coefficient, :math:`\alpha` + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + See Also + -------- + hamming, hann + + Notes + ----- + The generalized Hamming window is defined as + + .. math:: w(n) = \alpha - \left(1 - \alpha\right) + \cos\left(\frac{2\pi{n}}{M-1}\right) \qquad 0 \leq n \leq M-1 + + Both the common Hamming window and Hann window are special cases of the + generalized Hamming window with :math:`\alpha` = 0.54 and :math:`\alpha` = + 0.5, respectively [2]_. + + References + ---------- + .. [1] DSPRelated, "Generalized Hamming Window Family", + https://www.dsprelated.com/freebooks/sasp/Generalized_Hamming_Window_Family.html + .. [2] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function + .. [3] Riccardo Piantanida ESA, "Sentinel-1 Level 1 Detailed Algorithm + Definition", + https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Level-1-Detailed-Algorithm-Definition + .. [4] Matthieu Bourbigot ESA, "Sentinel-1 Product Definition", + https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Product-Definition + + Examples + -------- + The Sentinel-1A/B Instrument Processing Facility uses generalized Hamming + windows in the processing of spaceborne Synthetic Aperture Radar (SAR) + data [3]_. The facility uses various values for the :math:`\alpha` + parameter based on operating mode of the SAR instrument. Some common + :math:`\alpha` values include 0.75, 0.7 and 0.52 [4]_. As an example, we + plot these different windows. + + >>> import numpy as np + >>> from scipy.signal.windows import general_hamming + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> fig1, spatial_plot = plt.subplots() + >>> spatial_plot.set_title("Generalized Hamming Windows") + >>> spatial_plot.set_ylabel("Amplitude") + >>> spatial_plot.set_xlabel("Sample") + + >>> fig2, freq_plot = plt.subplots() + >>> freq_plot.set_title("Frequency Responses") + >>> freq_plot.set_ylabel("Normalized magnitude [dB]") + >>> freq_plot.set_xlabel("Normalized frequency [cycles per sample]") + + >>> for alpha in [0.75, 0.7, 0.52]: + ... window = general_hamming(41, alpha) + ... spatial_plot.plot(window, label="{:.2f}".format(alpha)) + ... A = fft(window, 2048) / (len(window)/2.0) + ... freq = np.linspace(-0.5, 0.5, len(A)) + ... response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + ... freq_plot.plot(freq, response, label="{:.2f}".format(alpha)) + >>> freq_plot.legend(loc="upper right") + >>> spatial_plot.legend(loc="upper right") + + """ + return general_cosine(M, [alpha, 1. - alpha], sym) + + +def hamming(M, sym=True): + r"""Return a Hamming window. + + The Hamming window is a taper formed by using a raised cosine with + non-zero endpoints, optimized to minimize the nearest side lobe. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Hamming window is defined as + + .. math:: w(n) = 0.54 - 0.46 \cos\left(\frac{2\pi{n}}{M-1}\right) + \qquad 0 \leq n \leq M-1 + + The Hamming was named for R. W. Hamming, an associate of J. W. Tukey and + is described in Blackman and Tukey. It was recommended for smoothing the + truncated autocovariance function in the time domain. + Most references to the Hamming window come from the signal processing + literature, where it is used as one of many windowing functions for + smoothing values. It is also known as an apodization (which means + "removing the foot", i.e. smoothing discontinuities at the beginning + and end of the sampled signal) or tapering function. + + References + ---------- + .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power + spectra, Dover Publications, New York. + .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The + University of Alberta Press, 1975, pp. 109-110. + .. [3] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function + .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, + "Numerical Recipes", Cambridge University Press, 1986, page 425. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.hamming(51) + >>> plt.plot(window) + >>> plt.title("Hamming window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Hamming window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + # Docstring adapted from NumPy's hamming function + return general_hamming(M, 0.54, sym) + + +def kaiser(M, beta, sym=True): + r"""Return a Kaiser window. + + The Kaiser window is a taper formed by using a Bessel function. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + beta : float + Shape parameter, determines trade-off between main-lobe width and + side lobe level. As beta gets large, the window narrows. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Kaiser window is defined as + + .. math:: w(n) = I_0\left( \beta \sqrt{1-\frac{4n^2}{(M-1)^2}} + \right)/I_0(\beta) + + with + + .. math:: \quad -\frac{M-1}{2} \leq n \leq \frac{M-1}{2}, + + where :math:`I_0` is the modified zeroth-order Bessel function. + + The Kaiser was named for Jim Kaiser, who discovered a simple approximation + to the DPSS window based on Bessel functions. + The Kaiser window is a very good approximation to the Digital Prolate + Spheroidal Sequence, or Slepian window, which is the transform which + maximizes the energy in the main lobe of the window relative to total + energy. + + The Kaiser can approximate other windows by varying the beta parameter. + (Some literature uses alpha = beta/pi.) [4]_ + + ==== ======================= + beta Window shape + ==== ======================= + 0 Rectangular + 5 Similar to a Hamming + 6 Similar to a Hann + 8.6 Similar to a Blackman + ==== ======================= + + A beta value of 14 is probably a good starting point. Note that as beta + gets large, the window narrows, and so the number of samples needs to be + large enough to sample the increasingly narrow spike, otherwise NaNs will + be returned. + + Most references to the Kaiser window come from the signal processing + literature, where it is used as one of many windowing functions for + smoothing values. It is also known as an apodization (which means + "removing the foot", i.e. smoothing discontinuities at the beginning + and end of the sampled signal) or tapering function. + + References + ---------- + .. [1] J. F. Kaiser, "Digital Filters" - Ch 7 in "Systems analysis by + digital computer", Editors: F.F. Kuo and J.F. Kaiser, p 218-285. + John Wiley and Sons, New York, (1966). + .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The + University of Alberta Press, 1975, pp. 177-178. + .. [3] Wikipedia, "Window function", + https://en.wikipedia.org/wiki/Window_function + .. [4] F. J. Harris, "On the use of windows for harmonic analysis with the + discrete Fourier transform," Proceedings of the IEEE, vol. 66, + no. 1, pp. 51-83, Jan. 1978. :doi:`10.1109/PROC.1978.10837`. + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.kaiser(51, beta=14) + >>> plt.plot(window) + >>> plt.title(r"Kaiser window ($\beta$=14)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title(r"Frequency response of the Kaiser window ($\beta$=14)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + # Docstring adapted from NumPy's kaiser function + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) + alpha = (M - 1) / 2.0 + w = (special.i0(beta * np.sqrt(1 - ((n - alpha) / alpha) ** 2.0)) / + special.i0(beta)) + + return _truncate(w, needs_trunc) + + +def kaiser_bessel_derived(M, beta, *, sym=True): + """Return a Kaiser-Bessel derived window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + Note that this window is only defined for an even + number of points. + beta : float + Kaiser window shape parameter. + sym : bool, optional + This parameter only exists to comply with the interface offered by + the other window functions and to be callable by `get_window`. + When True (default), generates a symmetric window, for use in filter + design. + + Returns + ------- + w : ndarray + The window, normalized to fulfil the Princen-Bradley condition. + + See Also + -------- + kaiser + + Notes + ----- + It is designed to be suitable for use with the modified discrete cosine + transform (MDCT) and is mainly used in audio signal processing and + audio coding. + + .. versionadded:: 1.9.0 + + References + ---------- + .. [1] Bosi, Marina, and Richard E. Goldberg. Introduction to Digital + Audio Coding and Standards. Dordrecht: Kluwer, 2003. + .. [2] Wikipedia, "Kaiser window", + https://en.wikipedia.org/wiki/Kaiser_window + + Examples + -------- + Plot the Kaiser-Bessel derived window based on the wikipedia + reference [2]_: + + >>> import numpy as np + >>> from scipy import signal + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> N = 50 + >>> for alpha in [0.64, 2.55, 7.64, 31.83]: + ... ax.plot(signal.windows.kaiser_bessel_derived(2*N, np.pi*alpha), + ... label=f"{alpha=}") + >>> ax.grid(True) + >>> ax.set_title("Kaiser-Bessel derived window") + >>> ax.set_ylabel("Amplitude") + >>> ax.set_xlabel("Sample") + >>> ax.set_xticks([0, N, 2*N-1]) + >>> ax.set_xticklabels(["0", "N", "2N+1"]) # doctest: +SKIP + >>> ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.707, 0.8, 1.0]) + >>> fig.legend(loc="center") + >>> fig.tight_layout() + >>> fig.show() + """ + if not sym: + raise ValueError( + "Kaiser-Bessel Derived windows are only defined for symmetric " + "shapes" + ) + elif M < 1: + return np.array([]) + elif M % 2: + raise ValueError( + "Kaiser-Bessel Derived windows are only defined for even number " + "of points" + ) + + kaiser_window = kaiser(M // 2 + 1, beta) + csum = np.cumsum(kaiser_window) + half_window = np.sqrt(csum[:-1] / csum[-1]) + w = np.concatenate((half_window, half_window[::-1]), axis=0) + return w + + +def gaussian(M, std, sym=True): + r"""Return a Gaussian window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + std : float + The standard deviation, sigma. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Gaussian window is defined as + + .. math:: w(n) = e^{ -\frac{1}{2}\left(\frac{n}{\sigma}\right)^2 } + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.gaussian(51, std=7) + >>> plt.plot(window) + >>> plt.title(r"Gaussian window ($\sigma$=7)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title(r"Frequency response of the Gaussian window ($\sigma$=7)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) - (M - 1.0) / 2.0 + sig2 = 2 * std * std + w = np.exp(-n ** 2 / sig2) + + return _truncate(w, needs_trunc) + + +def general_gaussian(M, p, sig, sym=True): + r"""Return a window with a generalized Gaussian shape. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + p : float + Shape parameter. p = 1 is identical to `gaussian`, p = 0.5 is + the same shape as the Laplace distribution. + sig : float + The standard deviation, sigma. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The generalized Gaussian window is defined as + + .. math:: w(n) = e^{ -\frac{1}{2}\left|\frac{n}{\sigma}\right|^{2p} } + + the half-power point is at + + .. math:: (2 \log(2))^{1/(2 p)} \sigma + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.general_gaussian(51, p=1.5, sig=7) + >>> plt.plot(window) + >>> plt.title(r"Generalized Gaussian window (p=1.5, $\sigma$=7)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title(r"Freq. resp. of the gen. Gaussian " + ... r"window (p=1.5, $\sigma$=7)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + n = np.arange(0, M) - (M - 1.0) / 2.0 + w = np.exp(-0.5 * np.abs(n / sig) ** (2 * p)) + + return _truncate(w, needs_trunc) + + +# `chebwin` contributed by Kumar Appaiah. +def chebwin(M, at, sym=True): + r"""Return a Dolph-Chebyshev window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + at : float + Attenuation (in dB). + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value always normalized to 1 + + Notes + ----- + This window optimizes for the narrowest main lobe width for a given order + `M` and sidelobe equiripple attenuation `at`, using Chebyshev + polynomials. It was originally developed by Dolph to optimize the + directionality of radio antenna arrays. + + Unlike most windows, the Dolph-Chebyshev is defined in terms of its + frequency response: + + .. math:: W(k) = \frac + {\cos\{M \cos^{-1}[\beta \cos(\frac{\pi k}{M})]\}} + {\cosh[M \cosh^{-1}(\beta)]} + + where + + .. math:: \beta = \cosh \left [\frac{1}{M} + \cosh^{-1}(10^\frac{A}{20}) \right ] + + and 0 <= abs(k) <= M-1. A is the attenuation in decibels (`at`). + + The time domain window is then generated using the IFFT, so + power-of-two `M` are the fastest to generate, and prime number `M` are + the slowest. + + The equiripple condition in the frequency domain creates impulses in the + time domain, which appear at the ends of the window. + + References + ---------- + .. [1] C. Dolph, "A current distribution for broadside arrays which + optimizes the relationship between beam width and side-lobe level", + Proceedings of the IEEE, Vol. 34, Issue 6 + .. [2] Peter Lynch, "The Dolph-Chebyshev Window: A Simple Optimal Filter", + American Meteorological Society (April 1997) + http://mathsci.ucd.ie/~plynch/Publications/Dolph.pdf + .. [3] F. J. Harris, "On the use of windows for harmonic analysis with the + discrete Fourier transforms", Proceedings of the IEEE, Vol. 66, + No. 1, January 1978 + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.chebwin(51, at=100) + >>> plt.plot(window) + >>> plt.title("Dolph-Chebyshev window (100 dB)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Dolph-Chebyshev window (100 dB)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ + if np.abs(at) < 45: + warnings.warn("This window is not suitable for spectral analysis " + "for attenuation values lower than about 45dB because " + "the equivalent noise bandwidth of a Chebyshev window " + "does not grow monotonically with increasing sidelobe " + "attenuation when the attenuation is smaller than " + "about 45 dB.", + stacklevel=2) + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + # compute the parameter beta + order = M - 1.0 + beta = np.cosh(1.0 / order * np.arccosh(10 ** (np.abs(at) / 20.))) + k = np.r_[0:M] * 1.0 + x = beta * np.cos(np.pi * k / M) + # Find the window's DFT coefficients + # Use analytic definition of Chebyshev polynomial instead of expansion + # from scipy.special. Using the expansion in scipy.special leads to errors. + p = np.zeros(x.shape) + p[x > 1] = np.cosh(order * np.arccosh(x[x > 1])) + p[x < -1] = (2 * (M % 2) - 1) * np.cosh(order * np.arccosh(-x[x < -1])) + p[np.abs(x) <= 1] = np.cos(order * np.arccos(x[np.abs(x) <= 1])) + + # Appropriate IDFT and filling up + # depending on even/odd M + if M % 2: + w = np.real(sp_fft.fft(p)) + n = (M + 1) // 2 + w = w[:n] + w = np.concatenate((w[n - 1:0:-1], w)) + else: + p = p * np.exp(1.j * np.pi / M * np.r_[0:M]) + w = np.real(sp_fft.fft(p)) + n = M // 2 + 1 + w = np.concatenate((w[n - 1:0:-1], w[1:n])) + w = w / max(w) + + return _truncate(w, needs_trunc) + + +def cosine(M, sym=True): + """Return a window with a simple cosine shape. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + + .. versionadded:: 0.13.0 + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.cosine(51) + >>> plt.plot(window) + >>> plt.title("Cosine window") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2047) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the cosine window") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + >>> plt.show() + + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + w = np.sin(np.pi / M * (np.arange(0, M) + .5)) + + return _truncate(w, needs_trunc) + + +def exponential(M, center=None, tau=1., sym=True): + r"""Return an exponential (or Poisson) window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + center : float, optional + Parameter defining the center location of the window function. + The default value if not given is ``center = (M-1) / 2``. This + parameter must take its default value for symmetric windows. + tau : float, optional + Parameter defining the decay. For ``center = 0`` use + ``tau = -(M-1) / ln(x)`` if ``x`` is the fraction of the window + remaining at the end. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Exponential window is defined as + + .. math:: w(n) = e^{-|n-center| / \tau} + + References + ---------- + .. [1] S. Gade and H. Herlufsen, "Windows to FFT analysis (Part I)", + Technical Review 3, Bruel & Kjaer, 1987. + + Examples + -------- + Plot the symmetric window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> M = 51 + >>> tau = 3.0 + >>> window = signal.windows.exponential(M, tau=tau) + >>> plt.plot(window) + >>> plt.title("Exponential Window (tau=3.0)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -35, 0]) + >>> plt.title("Frequency response of the Exponential window (tau=3.0)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + This function can also generate non-symmetric windows: + + >>> tau2 = -(M-1) / np.log(0.01) + >>> window2 = signal.windows.exponential(M, 0, tau2, False) + >>> plt.figure() + >>> plt.plot(window2) + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + """ + if sym and center is not None: + raise ValueError("If sym==True, center must be None.") + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + if center is None: + center = (M-1) / 2 + + n = np.arange(0, M) + w = np.exp(-np.abs(n-center) / tau) + + return _truncate(w, needs_trunc) + + +def taylor(M, nbar=4, sll=30, norm=True, sym=True): + """ + Return a Taylor window. + + The Taylor window taper function approximates the Dolph-Chebyshev window's + constant sidelobe level for a parameterized number of near-in sidelobes, + but then allows a taper beyond [2]_. + + The SAR (synthetic aperture radar) community commonly uses Taylor + weighting for image formation processing because it provides strong, + selectable sidelobe suppression with minimum broadening of the + mainlobe [1]_. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + nbar : int, optional + Number of nearly constant level sidelobes adjacent to the mainlobe. + sll : float, optional + Desired suppression of sidelobe level in decibels (dB) relative to the + DC gain of the mainlobe. This should be a positive number. + norm : bool, optional + When True (default), divides the window by the largest (middle) value + for odd-length windows or the value that would occur between the two + repeated middle values for even-length windows such that all values + are less than or equal to 1. When False the DC gain will remain at 1 + (0 dB) and the sidelobes will be `sll` dB down. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + out : array + The window. When `norm` is True (default), the maximum value is + normalized to 1 (though the value 1 does not appear if `M` is + even and `sym` is True). + + See Also + -------- + chebwin, kaiser, bartlett, blackman, hamming, hann + + References + ---------- + .. [1] W. Carrara, R. Goodman, and R. Majewski, "Spotlight Synthetic + Aperture Radar: Signal Processing Algorithms" Pages 512-513, + July 1995. + .. [2] Armin Doerry, "Catalog of Window Taper Functions for + Sidelobe Control", 2017. + https://www.researchgate.net/profile/Armin_Doerry/publication/316281181_Catalog_of_Window_Taper_Functions_for_Sidelobe_Control/links/58f92cb2a6fdccb121c9d54d/Catalog-of-Window-Taper-Functions-for-Sidelobe-Control.pdf + + Examples + -------- + Plot the window and its frequency response: + + >>> import numpy as np + >>> from scipy import signal + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + + >>> window = signal.windows.taylor(51, nbar=20, sll=100, norm=False) + >>> plt.plot(window) + >>> plt.title("Taylor window (100 dB)") + >>> plt.ylabel("Amplitude") + >>> plt.xlabel("Sample") + + >>> plt.figure() + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> plt.plot(freq, response) + >>> plt.axis([-0.5, 0.5, -120, 0]) + >>> plt.title("Frequency response of the Taylor window (100 dB)") + >>> plt.ylabel("Normalized magnitude [dB]") + >>> plt.xlabel("Normalized frequency [cycles per sample]") + + """ # noqa: E501 + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + # Original text uses a negative sidelobe level parameter and then negates + # it in the calculation of B. To keep consistent with other methods we + # assume the sidelobe level parameter to be positive. + B = 10**(sll / 20) + A = np.arccosh(B) / np.pi + s2 = nbar**2 / (A**2 + (nbar - 0.5)**2) + ma = np.arange(1, nbar) + + Fm = np.empty(nbar-1) + signs = np.empty_like(ma) + signs[::2] = 1 + signs[1::2] = -1 + m2 = ma*ma + for mi, m in enumerate(ma): + numer = signs[mi] * np.prod(1 - m2[mi]/s2/(A**2 + (ma - 0.5)**2)) + denom = 2 * np.prod(1 - m2[mi]/m2[:mi]) * np.prod(1 - m2[mi]/m2[mi+1:]) + Fm[mi] = numer / denom + + def W(n): + return 1 + 2*np.dot(Fm, np.cos( + 2*np.pi*ma[:, np.newaxis]*(n-M/2.+0.5)/M)) + + w = W(np.arange(M)) + + # normalize (Note that this is not described in the original text [1]) + if norm: + scale = 1.0 / W((M - 1) / 2) + w *= scale + + return _truncate(w, needs_trunc) + + +def dpss(M, NW, Kmax=None, sym=True, norm=None, return_ratios=False): + """ + Compute the Discrete Prolate Spheroidal Sequences (DPSS). + + DPSS (or Slepian sequences) are often used in multitaper power spectral + density estimation (see [1]_). The first window in the sequence can be + used to maximize the energy concentration in the main lobe, and is also + called the Slepian window. + + Parameters + ---------- + M : int + Window length. + NW : float + Standardized half bandwidth corresponding to ``2*NW = BW/f0 = BW*M*dt`` + where ``dt`` is taken as 1. + Kmax : int | None, optional + Number of DPSS windows to return (orders ``0`` through ``Kmax-1``). + If None (default), return only a single window of shape ``(M,)`` + instead of an array of windows of shape ``(Kmax, M)``. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + norm : {2, 'approximate', 'subsample'} | None, optional + If 'approximate' or 'subsample', then the windows are normalized by the + maximum, and a correction scale-factor for even-length windows + is applied either using ``M**2/(M**2+NW)`` ("approximate") or + a FFT-based subsample shift ("subsample"), see Notes for details. + If None, then "approximate" is used when ``Kmax=None`` and 2 otherwise + (which uses the l2 norm). + return_ratios : bool, optional + If True, also return the concentration ratios in addition to the + windows. + + Returns + ------- + v : ndarray, shape (Kmax, M) or (M,) + The DPSS windows. Will be 1D if `Kmax` is None. + r : ndarray, shape (Kmax,) or float, optional + The concentration ratios for the windows. Only returned if + `return_ratios` evaluates to True. Will be 0D if `Kmax` is None. + + Notes + ----- + This computation uses the tridiagonal eigenvector formulation given + in [2]_. + + The default normalization for ``Kmax=None``, i.e. window-generation mode, + simply using the l-infinity norm would create a window with two unity + values, which creates slight normalization differences between even and odd + orders. The approximate correction of ``M**2/float(M**2+NW)`` for even + sample numbers is used to counteract this effect (see Examples below). + + For very long signals (e.g., 1e6 elements), it can be useful to compute + windows orders of magnitude shorter and use interpolation (e.g., + `scipy.interpolate.interp1d`) to obtain tapers of length `M`, + but this in general will not preserve orthogonality between the tapers. + + .. versionadded:: 1.1 + + References + ---------- + .. [1] Percival DB, Walden WT. Spectral Analysis for Physical Applications: + Multitaper and Conventional Univariate Techniques. + Cambridge University Press; 1993. + .. [2] Slepian, D. Prolate spheroidal wave functions, Fourier analysis, and + uncertainty V: The discrete case. Bell System Technical Journal, + Volume 57 (1978), 1371430. + .. [3] Kaiser, JF, Schafer RW. On the Use of the I0-Sinh Window for + Spectrum Analysis. IEEE Transactions on Acoustics, Speech and + Signal Processing. ASSP-28 (1): 105-107; 1980. + + Examples + -------- + We can compare the window to `kaiser`, which was invented as an alternative + that was easier to calculate [3]_ (example adapted from + `here `_): + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.signal import windows, freqz + >>> M = 51 + >>> fig, axes = plt.subplots(3, 2, figsize=(5, 7)) + >>> for ai, alpha in enumerate((1, 3, 5)): + ... win_dpss = windows.dpss(M, alpha) + ... beta = alpha*np.pi + ... win_kaiser = windows.kaiser(M, beta) + ... for win, c in ((win_dpss, 'k'), (win_kaiser, 'r')): + ... win /= win.sum() + ... axes[ai, 0].plot(win, color=c, lw=1.) + ... axes[ai, 0].set(xlim=[0, M-1], title=r'$\\alpha$ = %s' % alpha, + ... ylabel='Amplitude') + ... w, h = freqz(win) + ... axes[ai, 1].plot(w, 20 * np.log10(np.abs(h)), color=c, lw=1.) + ... axes[ai, 1].set(xlim=[0, np.pi], + ... title=r'$\\beta$ = %0.2f' % beta, + ... ylabel='Magnitude (dB)') + >>> for ax in axes.ravel(): + ... ax.grid(True) + >>> axes[2, 1].legend(['DPSS', 'Kaiser']) + >>> fig.tight_layout() + >>> plt.show() + + And here are examples of the first four windows, along with their + concentration ratios: + + >>> M = 512 + >>> NW = 2.5 + >>> win, eigvals = windows.dpss(M, NW, 4, return_ratios=True) + >>> fig, ax = plt.subplots(1) + >>> ax.plot(win.T, linewidth=1.) + >>> ax.set(xlim=[0, M-1], ylim=[-0.1, 0.1], xlabel='Samples', + ... title='DPSS, M=%d, NW=%0.1f' % (M, NW)) + >>> ax.legend(['win[%d] (%0.4f)' % (ii, ratio) + ... for ii, ratio in enumerate(eigvals)]) + >>> fig.tight_layout() + >>> plt.show() + + Using a standard :math:`l_{\\infty}` norm would produce two unity values + for even `M`, but only one unity value for odd `M`. This produces uneven + window power that can be counteracted by the approximate correction + ``M**2/float(M**2+NW)``, which can be selected by using + ``norm='approximate'`` (which is the same as ``norm=None`` when + ``Kmax=None``, as is the case here). Alternatively, the slower + ``norm='subsample'`` can be used, which uses subsample shifting in the + frequency domain (FFT) to compute the correction: + + >>> Ms = np.arange(1, 41) + >>> factors = (50, 20, 10, 5, 2.0001) + >>> energy = np.empty((3, len(Ms), len(factors))) + >>> for mi, M in enumerate(Ms): + ... for fi, factor in enumerate(factors): + ... NW = M / float(factor) + ... # Corrected using empirical approximation (default) + ... win = windows.dpss(M, NW) + ... energy[0, mi, fi] = np.sum(win ** 2) / np.sqrt(M) + ... # Corrected using subsample shifting + ... win = windows.dpss(M, NW, norm='subsample') + ... energy[1, mi, fi] = np.sum(win ** 2) / np.sqrt(M) + ... # Uncorrected (using l-infinity norm) + ... win /= win.max() + ... energy[2, mi, fi] = np.sum(win ** 2) / np.sqrt(M) + >>> fig, ax = plt.subplots(1) + >>> hs = ax.plot(Ms, energy[2], '-o', markersize=4, + ... markeredgecolor='none') + >>> leg = [hs[-1]] + >>> for hi, hh in enumerate(hs): + ... h1 = ax.plot(Ms, energy[0, :, hi], '-o', markersize=4, + ... color=hh.get_color(), markeredgecolor='none', + ... alpha=0.66) + ... h2 = ax.plot(Ms, energy[1, :, hi], '-o', markersize=4, + ... color=hh.get_color(), markeredgecolor='none', + ... alpha=0.33) + ... if hi == len(hs) - 1: + ... leg.insert(0, h1[0]) + ... leg.insert(0, h2[0]) + >>> ax.set(xlabel='M (samples)', ylabel=r'Power / $\\sqrt{M}$') + >>> ax.legend(leg, ['Uncorrected', r'Corrected: $\\frac{M^2}{M^2+NW}$', + ... 'Corrected (subsample)']) + >>> fig.tight_layout() + + """ + if _len_guards(M): + return np.ones(M) + if norm is None: + norm = 'approximate' if Kmax is None else 2 + known_norms = (2, 'approximate', 'subsample') + if norm not in known_norms: + raise ValueError(f'norm must be one of {known_norms}, got {norm}') + if Kmax is None: + singleton = True + Kmax = 1 + else: + singleton = False + Kmax = operator.index(Kmax) + if not 0 < Kmax <= M: + raise ValueError('Kmax must be greater than 0 and less than M') + if NW >= M/2.: + raise ValueError('NW must be less than M/2.') + if NW <= 0: + raise ValueError('NW must be positive') + M, needs_trunc = _extend(M, sym) + W = float(NW) / M + nidx = np.arange(M) + + # Here we want to set up an optimization problem to find a sequence + # whose energy is maximally concentrated within band [-W,W]. + # Thus, the measure lambda(T,W) is the ratio between the energy within + # that band, and the total energy. This leads to the eigen-system + # (A - (l1)I)v = 0, where the eigenvector corresponding to the largest + # eigenvalue is the sequence with maximally concentrated energy. The + # collection of eigenvectors of this system are called Slepian + # sequences, or discrete prolate spheroidal sequences (DPSS). Only the + # first K, K = 2NW/dt orders of DPSS will exhibit good spectral + # concentration + # [see https://en.wikipedia.org/wiki/Spectral_concentration_problem] + + # Here we set up an alternative symmetric tri-diagonal eigenvalue + # problem such that + # (B - (l2)I)v = 0, and v are our DPSS (but eigenvalues l2 != l1) + # the main diagonal = ([M-1-2*t]/2)**2 cos(2PIW), t=[0,1,2,...,M-1] + # and the first off-diagonal = t(M-t)/2, t=[1,2,...,M-1] + # [see Percival and Walden, 1993] + d = ((M - 1 - 2 * nidx) / 2.) ** 2 * np.cos(2 * np.pi * W) + e = nidx[1:] * (M - nidx[1:]) / 2. + + # only calculate the highest Kmax eigenvalues + w, windows = linalg.eigh_tridiagonal( + d, e, select='i', select_range=(M - Kmax, M - 1)) + w = w[::-1] + windows = windows[:, ::-1].T + + # By convention (Percival and Walden, 1993 pg 379) + # * symmetric tapers (k=0,2,4,...) should have a positive average. + fix_even = (windows[::2].sum(axis=1) < 0) + for i, f in enumerate(fix_even): + if f: + windows[2 * i] *= -1 + # * antisymmetric tapers should begin with a positive lobe + # (this depends on the definition of "lobe", here we'll take the first + # point above the numerical noise, which should be good enough for + # sufficiently smooth functions, and more robust than relying on an + # algorithm that uses max(abs(w)), which is susceptible to numerical + # noise problems) + thresh = max(1e-7, 1. / M) + for i, w in enumerate(windows[1::2]): + if w[w * w > thresh][0] < 0: + windows[2 * i + 1] *= -1 + + # Now find the eigenvalues of the original spectral concentration problem + # Use the autocorr sequence technique from Percival and Walden, 1993 pg 390 + if return_ratios: + dpss_rxx = _fftautocorr(windows) + r = 4 * W * np.sinc(2 * W * nidx) + r[0] = 2 * W + ratios = np.dot(dpss_rxx, r) + if singleton: + ratios = ratios[0] + # Deal with sym and Kmax=None + if norm != 2: + windows /= windows.max() + if M % 2 == 0: + if norm == 'approximate': + correction = M**2 / float(M**2 + NW) + else: + s = sp_fft.rfft(windows[0]) + shift = -(1 - 1./M) * np.arange(1, M//2 + 1) + s[1:] *= 2 * np.exp(-1j * np.pi * shift) + correction = M / s.real.sum() + windows *= correction + # else we're already l2 normed, so do nothing + if needs_trunc: + windows = windows[:, :-1] + if singleton: + windows = windows[0] + return (windows, ratios) if return_ratios else windows + + +def lanczos(M, *, sym=True): + r"""Return a Lanczos window also known as a sinc window. + + Parameters + ---------- + M : int + Number of points in the output window. If zero, an empty array + is returned. An exception is thrown when it is negative. + sym : bool, optional + When True (default), generates a symmetric window, for use in filter + design. + When False, generates a periodic window, for use in spectral analysis. + + Returns + ------- + w : ndarray + The window, with the maximum value normalized to 1 (though the value 1 + does not appear if `M` is even and `sym` is True). + + Notes + ----- + The Lanczos window is defined as + + .. math:: w(n) = sinc \left( \frac{2n}{M - 1} - 1 \right) + + where + + .. math:: sinc(x) = \frac{\sin(\pi x)}{\pi x} + + The Lanczos window has reduced Gibbs oscillations and is widely used for + filtering climate timeseries with good properties in the physical and + spectral domains. + + .. versionadded:: 1.10 + + References + ---------- + .. [1] Lanczos, C., and Teichmann, T. (1957). Applied analysis. + Physics Today, 10, 44. + .. [2] Duchon C. E. (1979) Lanczos Filtering in One and Two Dimensions. + Journal of Applied Meteorology, Vol 18, pp 1016-1022. + .. [3] Thomson, R. E. and Emery, W. J. (2014) Data Analysis Methods in + Physical Oceanography (Third Edition), Elsevier, pp 593-637. + .. [4] Wikipedia, "Window function", + http://en.wikipedia.org/wiki/Window_function + + Examples + -------- + Plot the window + + >>> import numpy as np + >>> from scipy.signal.windows import lanczos + >>> from scipy.fft import fft, fftshift + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1) + >>> window = lanczos(51) + >>> ax.plot(window) + >>> ax.set_title("Lanczos window") + >>> ax.set_ylabel("Amplitude") + >>> ax.set_xlabel("Sample") + >>> fig.tight_layout() + >>> plt.show() + + and its frequency response: + + >>> fig, ax = plt.subplots(1) + >>> A = fft(window, 2048) / (len(window)/2.0) + >>> freq = np.linspace(-0.5, 0.5, len(A)) + >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) + >>> ax.plot(freq, response) + >>> ax.set_xlim(-0.5, 0.5) + >>> ax.set_ylim(-120, 0) + >>> ax.set_title("Frequency response of the lanczos window") + >>> ax.set_ylabel("Normalized magnitude [dB]") + >>> ax.set_xlabel("Normalized frequency [cycles per sample]") + >>> fig.tight_layout() + >>> plt.show() + """ + if _len_guards(M): + return np.ones(M) + M, needs_trunc = _extend(M, sym) + + # To make sure that the window is symmetric, we concatenate the right hand + # half of the window and the flipped one which is the left hand half of + # the window. + def _calc_right_side_lanczos(n, m): + return np.sinc(2. * np.arange(n, m) / (m - 1) - 1.0) + + if M % 2 == 0: + wh = _calc_right_side_lanczos(M/2, M) + w = np.r_[np.flip(wh), wh] + else: + wh = _calc_right_side_lanczos((M+1)/2, M) + w = np.r_[np.flip(wh), 1.0, wh] + + return _truncate(w, needs_trunc) + + +def _fftautocorr(x): + """Compute the autocorrelation of a real array and crop the result.""" + N = x.shape[-1] + use_N = sp_fft.next_fast_len(2*N-1) + x_fft = sp_fft.rfft(x, use_N, axis=-1) + cxy = sp_fft.irfft(x_fft * x_fft.conj(), n=use_N)[:, :N] + # Or equivalently (but in most cases slower): + # cxy = np.array([np.convolve(xx, yy[::-1], mode='full') + # for xx, yy in zip(x, x)])[:, N-1:2*N-1] + return cxy + + +_win_equiv_raw = { + ('barthann', 'brthan', 'bth'): (barthann, False), + ('bartlett', 'bart', 'brt'): (bartlett, False), + ('blackman', 'black', 'blk'): (blackman, False), + ('blackmanharris', 'blackharr', 'bkh'): (blackmanharris, False), + ('bohman', 'bman', 'bmn'): (bohman, False), + ('boxcar', 'box', 'ones', + 'rect', 'rectangular'): (boxcar, False), + ('chebwin', 'cheb'): (chebwin, True), + ('cosine', 'halfcosine'): (cosine, False), + ('dpss',): (dpss, True), + ('exponential', 'poisson'): (exponential, False), + ('flattop', 'flat', 'flt'): (flattop, False), + ('gaussian', 'gauss', 'gss'): (gaussian, True), + ('general cosine', 'general_cosine'): (general_cosine, True), + ('general gaussian', 'general_gaussian', + 'general gauss', 'general_gauss', 'ggs'): (general_gaussian, True), + ('general hamming', 'general_hamming'): (general_hamming, True), + ('hamming', 'hamm', 'ham'): (hamming, False), + ('hann', 'han'): (hann, False), + ('kaiser', 'ksr'): (kaiser, True), + ('kaiser bessel derived', 'kbd'): (kaiser_bessel_derived, True), + ('lanczos', 'sinc'): (lanczos, False), + ('nuttall', 'nutl', 'nut'): (nuttall, False), + ('parzen', 'parz', 'par'): (parzen, False), + ('taylor', 'taylorwin'): (taylor, False), + ('triangle', 'triang', 'tri'): (triang, False), + ('tukey', 'tuk'): (tukey, False), +} + +# Fill dict with all valid window name strings +_win_equiv = {} +for k, v in _win_equiv_raw.items(): + for key in k: + _win_equiv[key] = v[0] + +# Keep track of which windows need additional parameters +_needs_param = set() +for k, v in _win_equiv_raw.items(): + if v[1]: + _needs_param.update(k) + + +def get_window(window, Nx, fftbins=True): + """ + Return a window of a given length and type. + + Parameters + ---------- + window : string, float, or tuple + The type of window to create. See below for more details. + Nx : int + The number of samples in the window. + fftbins : bool, optional + If True (default), create a "periodic" window, ready to use with + `ifftshift` and be multiplied by the result of an FFT (see also + :func:`~scipy.fft.fftfreq`). + If False, create a "symmetric" window, for use in filter design. + + Returns + ------- + get_window : ndarray + Returns a window of length `Nx` and type `window` + + Notes + ----- + Window types: + + - `~scipy.signal.windows.boxcar` + - `~scipy.signal.windows.triang` + - `~scipy.signal.windows.blackman` + - `~scipy.signal.windows.hamming` + - `~scipy.signal.windows.hann` + - `~scipy.signal.windows.bartlett` + - `~scipy.signal.windows.flattop` + - `~scipy.signal.windows.parzen` + - `~scipy.signal.windows.bohman` + - `~scipy.signal.windows.blackmanharris` + - `~scipy.signal.windows.nuttall` + - `~scipy.signal.windows.barthann` + - `~scipy.signal.windows.cosine` + - `~scipy.signal.windows.exponential` + - `~scipy.signal.windows.tukey` + - `~scipy.signal.windows.taylor` + - `~scipy.signal.windows.lanczos` + - `~scipy.signal.windows.kaiser` (needs beta) + - `~scipy.signal.windows.kaiser_bessel_derived` (needs beta) + - `~scipy.signal.windows.gaussian` (needs standard deviation) + - `~scipy.signal.windows.general_cosine` (needs weighting coefficients) + - `~scipy.signal.windows.general_gaussian` (needs power, width) + - `~scipy.signal.windows.general_hamming` (needs window coefficient) + - `~scipy.signal.windows.dpss` (needs normalized half-bandwidth) + - `~scipy.signal.windows.chebwin` (needs attenuation) + + + If the window requires no parameters, then `window` can be a string. + + If the window requires parameters, then `window` must be a tuple + with the first argument the string name of the window, and the next + arguments the needed parameters. + + If `window` is a floating point number, it is interpreted as the beta + parameter of the `~scipy.signal.windows.kaiser` window. + + Each of the window types listed above is also the name of + a function that can be called directly to create a window of + that type. + + Examples + -------- + >>> from scipy import signal + >>> signal.get_window('triang', 7) + array([ 0.125, 0.375, 0.625, 0.875, 0.875, 0.625, 0.375]) + >>> signal.get_window(('kaiser', 4.0), 9) + array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093, + 0.97885093, 0.82160913, 0.56437221, 0.29425961]) + >>> signal.get_window(('exponential', None, 1.), 9) + array([ 0.011109 , 0.03019738, 0.082085 , 0.22313016, 0.60653066, + 0.60653066, 0.22313016, 0.082085 , 0.03019738]) + >>> signal.get_window(4.0, 9) + array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093, + 0.97885093, 0.82160913, 0.56437221, 0.29425961]) + + """ + sym = not fftbins + try: + beta = float(window) + except (TypeError, ValueError) as e: + args = () + if isinstance(window, tuple): + winstr = window[0] + if len(window) > 1: + args = window[1:] + elif isinstance(window, str): + if window in _needs_param: + raise ValueError("The '" + window + "' window needs one or " + "more parameters -- pass a tuple.") from e + else: + winstr = window + else: + raise ValueError("%s as window type is not supported." % + str(type(window))) from e + + try: + winfunc = _win_equiv[winstr] + except KeyError as e: + raise ValueError("Unknown window type.") from e + + if winfunc is dpss: + params = (Nx,) + args + (None,) + else: + params = (Nx,) + args + else: + winfunc = kaiser + params = (Nx, beta) + + return winfunc(*params, sym=sym) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/windows.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/windows.py new file mode 100644 index 0000000000000000000000000000000000000000..a449bc1aaafc777538db27c699dfbb07b48a473b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/signal/windows/windows.py @@ -0,0 +1,24 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.signal.windows` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'boxcar', 'triang', 'parzen', 'bohman', 'blackman', 'nuttall', + 'blackmanharris', 'flattop', 'bartlett', 'barthann', + 'hamming', 'kaiser', 'gaussian', 'general_cosine', + 'general_gaussian', 'general_hamming', 'chebwin', 'cosine', + 'hann', 'exponential', 'tukey', 'taylor', 'dpss', 'get_window', + 'linalg', 'sp_fft', 'k', 'v', 'key' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="signal.windows", module="windows", + private_modules=["_windows"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ec3dd05726c906ec0d887b72e1de8dd28de00299 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/__init__.py @@ -0,0 +1,324 @@ +""" +===================================== +Sparse matrices (:mod:`scipy.sparse`) +===================================== + +.. currentmodule:: scipy.sparse + +.. toctree:: + :hidden: + + sparse.csgraph + sparse.linalg + +SciPy 2-D sparse array package for numeric data. + +.. note:: + + This package is switching to an array interface, compatible with + NumPy arrays, from the older matrix interface. We recommend that + you use the array objects (`bsr_array`, `coo_array`, etc.) for + all new work. + + When using the array interface, please note that: + + - ``x * y`` no longer performs matrix multiplication, but + element-wise multiplication (just like with NumPy arrays). To + make code work with both arrays and matrices, use ``x @ y`` for + matrix multiplication. + - Operations such as `sum`, that used to produce dense matrices, now + produce arrays, whose multiplication behavior differs similarly. + - Sparse arrays currently must be two-dimensional. This also means + that all *slicing* operations on these objects must produce + two-dimensional results, or they will result in an error. This + will be addressed in a future version. + + The construction utilities (`eye`, `kron`, `random`, `diags`, etc.) + have not yet been ported, but their results can be wrapped into arrays:: + + A = csr_array(eye(3)) + +Contents +======== + +Sparse array classes +-------------------- + +.. autosummary:: + :toctree: generated/ + + bsr_array - Block Sparse Row array + coo_array - A sparse array in COOrdinate format + csc_array - Compressed Sparse Column array + csr_array - Compressed Sparse Row array + dia_array - Sparse array with DIAgonal storage + dok_array - Dictionary Of Keys based sparse array + lil_array - Row-based list of lists sparse array + sparray - Sparse array base class + +Sparse matrix classes +--------------------- + +.. autosummary:: + :toctree: generated/ + + bsr_matrix - Block Sparse Row matrix + coo_matrix - A sparse matrix in COOrdinate format + csc_matrix - Compressed Sparse Column matrix + csr_matrix - Compressed Sparse Row matrix + dia_matrix - Sparse matrix with DIAgonal storage + dok_matrix - Dictionary Of Keys based sparse matrix + lil_matrix - Row-based list of lists sparse matrix + spmatrix - Sparse matrix base class + +Functions +--------- + +Building sparse arrays: + +.. autosummary:: + :toctree: generated/ + + diags_array - Return a sparse array from diagonals + eye_array - Sparse MxN array whose k-th diagonal is all ones + random_array - Random values in a given shape array + block_array - Build a sparse array from sub-blocks + +Building sparse matrices: + +.. autosummary:: + :toctree: generated/ + + eye - Sparse MxN matrix whose k-th diagonal is all ones + identity - Identity matrix in sparse matrix format + diags - Return a sparse matrix from diagonals + spdiags - Return a sparse matrix from diagonals + bmat - Build a sparse matrix from sparse sub-blocks + random - Random values in a given shape matrix + rand - Random values in a given shape matrix (old interface) + +Building larger structures from smaller (array or matrix) + +.. autosummary:: + :toctree: generated/ + + kron - kronecker product of two sparse matrices + kronsum - kronecker sum of sparse matrices + block_diag - Build a block diagonal sparse matrix + tril - Lower triangular portion of a matrix in sparse format + triu - Upper triangular portion of a matrix in sparse format + hstack - Stack sparse matrices horizontally (column wise) + vstack - Stack sparse matrices vertically (row wise) + +Save and load sparse matrices: + +.. autosummary:: + :toctree: generated/ + + save_npz - Save a sparse matrix/array to a file using ``.npz`` format. + load_npz - Load a sparse matrix/array from a file using ``.npz`` format. + +Sparse tools: + +.. autosummary:: + :toctree: generated/ + + find + +Identifying sparse arrays: + +- use `isinstance(A, sp.sparse.sparray)` to check whether an array or matrix. +- use `A.format == 'csr'` to check the sparse format + +Identifying sparse matrices: + +.. autosummary:: + :toctree: generated/ + + issparse + isspmatrix + isspmatrix_csc + isspmatrix_csr + isspmatrix_bsr + isspmatrix_lil + isspmatrix_dok + isspmatrix_coo + isspmatrix_dia + +Submodules +---------- + +.. autosummary:: + + csgraph - Compressed sparse graph routines + linalg - sparse linear algebra routines + +Exceptions +---------- + +.. autosummary:: + :toctree: generated/ + + SparseEfficiencyWarning + SparseWarning + + +Usage information +================= + +There are seven available sparse array types: + + 1. `csc_array`: Compressed Sparse Column format + 2. `csr_array`: Compressed Sparse Row format + 3. `bsr_array`: Block Sparse Row format + 4. `lil_array`: List of Lists format + 5. `dok_array`: Dictionary of Keys format + 6. `coo_array`: COOrdinate format (aka IJV, triplet format) + 7. `dia_array`: DIAgonal format + +To construct an array efficiently, use either `dok_array` or `lil_array`. +The `lil_array` class supports basic slicing and fancy indexing with a +similar syntax to NumPy arrays. As illustrated below, the COO format +may also be used to efficiently construct arrays. Despite their +similarity to NumPy arrays, it is **strongly discouraged** to use NumPy +functions directly on these arrays because NumPy may not properly convert +them for computations, leading to unexpected (and incorrect) results. If you +do want to apply a NumPy function to these arrays, first check if SciPy has +its own implementation for the given sparse array class, or **convert the +sparse array to a NumPy array** (e.g., using the ``toarray`` method of the +class) first before applying the method. + +To perform manipulations such as multiplication or inversion, first +convert the array to either CSC or CSR format. The `lil_array` format is +row-based, so conversion to CSR is efficient, whereas conversion to CSC +is less so. + +All conversions among the CSR, CSC, and COO formats are efficient, +linear-time operations. + +Matrix vector product +--------------------- +To do a vector product between a sparse array and a vector simply use +the array ``dot`` method, as described in its docstring: + +>>> import numpy as np +>>> from scipy.sparse import csr_array +>>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) +>>> v = np.array([1, 0, -1]) +>>> A.dot(v) +array([ 1, -3, -1], dtype=int64) + +.. warning:: As of NumPy 1.7, ``np.dot`` is not aware of sparse arrays, + therefore using it will result on unexpected results or errors. + The corresponding dense array should be obtained first instead: + + >>> np.dot(A.toarray(), v) + array([ 1, -3, -1], dtype=int64) + + but then all the performance advantages would be lost. + +The CSR format is especially suitable for fast matrix vector products. + +Example 1 +--------- +Construct a 1000x1000 `lil_array` and add some values to it: + +>>> from scipy.sparse import lil_array +>>> from scipy.sparse.linalg import spsolve +>>> from numpy.linalg import solve, norm +>>> from numpy.random import rand + +>>> A = lil_array((1000, 1000)) +>>> A[0, :100] = rand(100) +>>> A[1, 100:200] = A[0, :100] +>>> A.setdiag(rand(1000)) + +Now convert it to CSR format and solve A x = b for x: + +>>> A = A.tocsr() +>>> b = rand(1000) +>>> x = spsolve(A, b) + +Convert it to a dense array and solve, and check that the result +is the same: + +>>> x_ = solve(A.toarray(), b) + +Now we can compute norm of the error with: + +>>> err = norm(x-x_) +>>> err < 1e-10 +True + +It should be small :) + + +Example 2 +--------- + +Construct an array in COO format: + +>>> from scipy import sparse +>>> from numpy import array +>>> I = array([0,3,1,0]) +>>> J = array([0,3,1,2]) +>>> V = array([4,5,7,9]) +>>> A = sparse.coo_array((V,(I,J)),shape=(4,4)) + +Notice that the indices do not need to be sorted. + +Duplicate (i,j) entries are summed when converting to CSR or CSC. + +>>> I = array([0,0,1,3,1,0,0]) +>>> J = array([0,2,1,3,1,0,0]) +>>> V = array([1,1,1,1,1,1,1]) +>>> B = sparse.coo_array((V,(I,J)),shape=(4,4)).tocsr() + +This is useful for constructing finite-element stiffness and mass matrices. + +Further details +--------------- + +CSR column indices are not necessarily sorted. Likewise for CSC row +indices. Use the ``.sorted_indices()`` and ``.sort_indices()`` methods when +sorted indices are required (e.g., when passing data to other libraries). + +""" + +# Original code by Travis Oliphant. +# Modified and extended by Ed Schofield, Robert Cimrman, +# Nathan Bell, and Jake Vanderplas. + +import warnings as _warnings + +from ._base import * +from ._csr import * +from ._csc import * +from ._lil import * +from ._dok import * +from ._coo import * +from ._dia import * +from ._bsr import * +from ._construct import * +from ._extract import * +from ._matrix import spmatrix +from ._matrix_io import * + +# For backward compatibility with v0.19. +from . import csgraph + +# Deprecated namespaces, to be removed in v2.0.0 +from . import ( + base, bsr, compressed, construct, coo, csc, csr, data, dia, dok, extract, + lil, sparsetools, sputils +) + +__all__ = [s for s in dir() if not s.startswith('_')] + +# Filter PendingDeprecationWarning for np.matrix introduced with numpy 1.15 +msg = 'the matrix subclass is not the recommended way' +_warnings.filterwarnings('ignore', message=msg) + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_base.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_base.py new file mode 100644 index 0000000000000000000000000000000000000000..80a25aa002d9866900e3a68504813c4e7c6cd680 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_base.py @@ -0,0 +1,1568 @@ +"""Base class for sparse matrices""" +from warnings import warn + +import numpy as np +from scipy._lib._util import VisibleDeprecationWarning + +from ._sputils import (asmatrix, check_reshape_kwargs, check_shape, + get_sum_dtype, isdense, isscalarlike, + matrix, validateaxis,) + +from ._matrix import spmatrix + +__all__ = ['isspmatrix', 'issparse', 'sparray', + 'SparseWarning', 'SparseEfficiencyWarning'] + + +class SparseWarning(Warning): + pass + + +class SparseFormatWarning(SparseWarning): + pass + + +class SparseEfficiencyWarning(SparseWarning): + pass + + +# The formats that we might potentially understand. +_formats = {'csc': [0, "Compressed Sparse Column"], + 'csr': [1, "Compressed Sparse Row"], + 'dok': [2, "Dictionary Of Keys"], + 'lil': [3, "List of Lists"], + 'dod': [4, "Dictionary of Dictionaries"], + 'sss': [5, "Symmetric Sparse Skyline"], + 'coo': [6, "COOrdinate"], + 'lba': [7, "Linpack BAnded"], + 'egd': [8, "Ellpack-itpack Generalized Diagonal"], + 'dia': [9, "DIAgonal"], + 'bsr': [10, "Block Sparse Row"], + 'msr': [11, "Modified compressed Sparse Row"], + 'bsc': [12, "Block Sparse Column"], + 'msc': [13, "Modified compressed Sparse Column"], + 'ssk': [14, "Symmetric SKyline"], + 'nsk': [15, "Nonsymmetric SKyline"], + 'jad': [16, "JAgged Diagonal"], + 'uss': [17, "Unsymmetric Sparse Skyline"], + 'vbr': [18, "Variable Block Row"], + 'und': [19, "Undefined"] + } + + +# These univariate ufuncs preserve zeros. +_ufuncs_with_fixed_point_at_zero = frozenset([ + np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh, + np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad, + np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt]) + + +MAXPRINT = 50 + + +class _spbase: + """ This class provides a base class for all sparse arrays. It + cannot be instantiated. Most of the work is provided by subclasses. + """ + + __array_priority__ = 10.1 + _format = 'und' # undefined + + @property + def ndim(self) -> int: + return len(self._shape) + + @property + def _shape_as_2d(self): + s = self._shape + return (1, s[-1]) if len(s) == 1 else s + + @property + def _bsr_container(self): + from ._bsr import bsr_array + return bsr_array + + @property + def _coo_container(self): + from ._coo import coo_array + return coo_array + + @property + def _csc_container(self): + from ._csc import csc_array + return csc_array + + @property + def _csr_container(self): + from ._csr import csr_array + return csr_array + + @property + def _dia_container(self): + from ._dia import dia_array + return dia_array + + @property + def _dok_container(self): + from ._dok import dok_array + return dok_array + + @property + def _lil_container(self): + from ._lil import lil_array + return lil_array + + def __init__(self, maxprint=MAXPRINT): + self._shape = None + if self.__class__.__name__ == '_spbase': + raise ValueError("This class is not intended" + " to be instantiated directly.") + self.maxprint = maxprint + + # Use this in 1.14.0 and later: + # + # @property + # def shape(self): + # return self._shape + + def reshape(self, *args, **kwargs): + """reshape(self, shape, order='C', copy=False) + + Gives a new shape to a sparse array/matrix without changing its data. + + Parameters + ---------- + shape : length-2 tuple of ints + The new shape should be compatible with the original shape. + order : {'C', 'F'}, optional + Read the elements using this index order. 'C' means to read and + write the elements using C-like index order; e.g., read entire first + row, then second row, etc. 'F' means to read and write the elements + using Fortran-like index order; e.g., read entire first column, then + second column, etc. + copy : bool, optional + Indicates whether or not attributes of self should be copied + whenever possible. The degree to which attributes are copied varies + depending on the type of sparse array being used. + + Returns + ------- + reshaped : sparse array/matrix + A sparse array/matrix with the given `shape`, not necessarily of the same + format as the current object. + + See Also + -------- + numpy.reshape : NumPy's implementation of 'reshape' for ndarrays + """ + # If the shape already matches, don't bother doing an actual reshape + # Otherwise, the default is to convert to COO and use its reshape + is_array = isinstance(self, sparray) + shape = check_shape(args, self.shape, allow_1d=is_array) + order, copy = check_reshape_kwargs(kwargs) + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + return self.tocoo(copy=copy).reshape(shape, order=order, copy=False) + + def resize(self, shape): + """Resize the array/matrix in-place to dimensions given by ``shape`` + + Any elements that lie within the new shape will remain at the same + indices, while non-zero elements lying outside the new shape are + removed. + + Parameters + ---------- + shape : (int, int) + number of rows and columns in the new array/matrix + + Notes + ----- + The semantics are not identical to `numpy.ndarray.resize` or + `numpy.resize`. Here, the same data will be maintained at each index + before and after reshape, if that index is within the new bounds. In + numpy, resizing maintains contiguity of the array, moving elements + around in the logical array but not within a flattened representation. + + We give no guarantees about whether the underlying data attributes + (arrays, etc.) will be modified in place or replaced with new objects. + """ + # As an inplace operation, this requires implementation in each format. + raise NotImplementedError( + f'{type(self).__name__}.resize is not implemented') + + def astype(self, dtype, casting='unsafe', copy=True): + """Cast the array/matrix elements to a specified type. + + Parameters + ---------- + dtype : string or numpy dtype + Typecode or data-type to which to cast the data. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur. + Defaults to 'unsafe' for backwards compatibility. + 'no' means the data types should not be cast at all. + 'equiv' means only byte-order changes are allowed. + 'safe' means only casts which can preserve values are allowed. + 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + 'unsafe' means any data conversions may be done. + copy : bool, optional + If `copy` is `False`, the result might share some memory with this + array/matrix. If `copy` is `True`, it is guaranteed that the result and + this array/matrix do not share any memory. + """ + + dtype = np.dtype(dtype) + if self.dtype != dtype: + return self.tocsr().astype( + dtype, casting=casting, copy=copy).asformat(self.format) + elif copy: + return self.copy() + else: + return self + + @classmethod + def _ascontainer(cls, X, **kwargs): + if issubclass(cls, sparray): + return np.asarray(X, **kwargs) + else: + return asmatrix(X, **kwargs) + + @classmethod + def _container(cls, X, **kwargs): + if issubclass(cls, sparray): + return np.array(X, **kwargs) + else: + return matrix(X, **kwargs) + + def _asfptype(self): + """Upcast array to a floating point format (if necessary)""" + + fp_types = ['f', 'd', 'F', 'D'] + + if self.dtype.char in fp_types: + return self + else: + for fp_type in fp_types: + if self.dtype <= np.dtype(fp_type): + return self.astype(fp_type) + + raise TypeError('cannot upcast [%s] to a floating ' + 'point format' % self.dtype.name) + + def __iter__(self): + for r in range(self.shape[0]): + yield self[r] + + def _getmaxprint(self): + """Maximum number of elements to display when printed.""" + return self.maxprint + + def count_nonzero(self): + """Number of non-zero entries, equivalent to + + np.count_nonzero(a.toarray()) + + Unlike the nnz property, which return the number of stored + entries (the length of the data attribute), this method counts the + actual number of non-zero entries in data. + """ + raise NotImplementedError("count_nonzero not implemented for %s." % + self.__class__.__name__) + + def _getnnz(self, axis=None): + """Number of stored values, including explicit zeros. + + Parameters + ---------- + axis : None, 0, or 1 + Select between the number of values across the whole array, in + each column, or in each row. + + See also + -------- + count_nonzero : Number of non-zero entries + """ + raise NotImplementedError("getnnz not implemented for %s." % + self.__class__.__name__) + + @property + def nnz(self) -> int: + """Number of stored values, including explicit zeros. + + See also + -------- + count_nonzero : Number of non-zero entries + """ + return self._getnnz() + + @property + def size(self) -> int: + """Number of stored values. + + See also + -------- + count_nonzero : Number of non-zero values. + """ + return self._getnnz() + + @property + def format(self) -> str: + """Format string for matrix.""" + return self._format + + @property + def A(self) -> np.ndarray: + """DEPRECATED: Return a dense array. + + .. deprecated:: 1.11.0 + + `.A` is deprecated and will be removed in v1.14.0. + Use `.toarray()` instead. + """ + if isinstance(self, sparray): + message = ("`.A` is deprecated and will be removed in v1.14.0. " + "Use `.toarray()` instead.") + warn(VisibleDeprecationWarning(message), stacklevel=2) + return self.toarray() + + @property + def T(self): + """Transpose.""" + return self.transpose() + + @property + def H(self): + """DEPRECATED: Returns the (complex) conjugate transpose. + + .. deprecated:: 1.11.0 + + `.H` is deprecated and will be removed in v1.14.0. + Please use `.T.conjugate()` instead. + """ + if isinstance(self, sparray): + message = ("`.H` is deprecated and will be removed in v1.14.0. " + "Please use `.T.conjugate()` instead.") + warn(VisibleDeprecationWarning(message), stacklevel=2) + return self.T.conjugate() + + @property + def real(self): + return self._real() + + @property + def imag(self): + return self._imag() + + def __repr__(self): + _, format_name = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + shape_str = 'x'.join(str(x) for x in self.shape) + return ( + f"<{shape_str} sparse {sparse_cls} of type '{self.dtype.type}'\n" + f"\twith {self.nnz} stored elements in {format_name} format>" + ) + + def __str__(self): + maxprint = self._getmaxprint() + + A = self.tocoo() + + # helper function, outputs "(i,j) v" + def tostr(row, col, data): + triples = zip(list(zip(row, col)), data) + return '\n'.join([(' {}\t{}'.format(*t)) for t in triples]) + + if self.nnz > maxprint: + half = maxprint // 2 + out = tostr(A.row[:half], A.col[:half], A.data[:half]) + out += "\n :\t:\n" + half = maxprint - maxprint//2 + out += tostr(A.row[-half:], A.col[-half:], A.data[-half:]) + else: + out = tostr(A.row, A.col, A.data) + + return out + + def __bool__(self): # Simple -- other ideas? + if self.shape == (1, 1): + return self.nnz != 0 + else: + raise ValueError("The truth value of an array with more than one " + "element is ambiguous. Use a.any() or a.all().") + __nonzero__ = __bool__ + + # What should len(sparse) return? For consistency with dense matrices, + # perhaps it should be the number of rows? But for some uses the number of + # non-zeros is more important. For now, raise an exception! + def __len__(self): + raise TypeError("sparse array length is ambiguous; use getnnz()" + " or shape[0]") + + def asformat(self, format, copy=False): + """Return this array/matrix in the passed format. + + Parameters + ---------- + format : {str, None} + The desired sparse format ("csr", "csc", "lil", "dok", "array", ...) + or None for no conversion. + copy : bool, optional + If True, the result is guaranteed to not share data with self. + + Returns + ------- + A : This array/matrix in the passed format. + """ + if format is None or format == self.format: + if copy: + return self.copy() + else: + return self + else: + try: + convert_method = getattr(self, 'to' + format) + except AttributeError as e: + raise ValueError(f'Format {format} is unknown.') from e + + # Forward the copy kwarg, if it's accepted. + try: + return convert_method(copy=copy) + except TypeError: + return convert_method() + + ################################################################### + # NOTE: All arithmetic operations use csr_matrix by default. + # Therefore a new sparse array format just needs to define a + # .tocsr() method to provide arithmetic support. Any of these + # methods can be overridden for efficiency. + #################################################################### + + def multiply(self, other): + """Point-wise multiplication by another array/matrix.""" + return self.tocsr().multiply(other) + + def maximum(self, other): + """Element-wise maximum between this and another array/matrix.""" + return self.tocsr().maximum(other) + + def minimum(self, other): + """Element-wise minimum between this and another array/matrix.""" + return self.tocsr().minimum(other) + + def dot(self, other): + """Ordinary dot product + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) + >>> v = np.array([1, 0, -1]) + >>> A.dot(v) + array([ 1, -3, -1], dtype=int64) + + """ + if np.isscalar(other): + return self * other + else: + return self @ other + + def power(self, n, dtype=None): + """Element-wise power.""" + return self.tocsr().power(n, dtype=dtype) + + def __eq__(self, other): + return self.tocsr().__eq__(other) + + def __ne__(self, other): + return self.tocsr().__ne__(other) + + def __lt__(self, other): + return self.tocsr().__lt__(other) + + def __gt__(self, other): + return self.tocsr().__gt__(other) + + def __le__(self, other): + return self.tocsr().__le__(other) + + def __ge__(self, other): + return self.tocsr().__ge__(other) + + def __abs__(self): + return abs(self.tocsr()) + + def __round__(self, ndigits=0): + return round(self.tocsr(), ndigits=ndigits) + + def _add_sparse(self, other): + return self.tocsr()._add_sparse(other) + + def _add_dense(self, other): + return self.tocoo()._add_dense(other) + + def _sub_sparse(self, other): + return self.tocsr()._sub_sparse(other) + + def _sub_dense(self, other): + return self.todense() - other + + def _rsub_dense(self, other): + # note: this can't be replaced by other + (-self) for unsigned types + return other - self.todense() + + def __add__(self, other): # self + other + if isscalarlike(other): + if other == 0: + return self.copy() + # Now we would add this scalar to every element. + raise NotImplementedError('adding a nonzero scalar to a ' + 'sparse array is not supported') + elif issparse(other): + if other.shape != self.shape: + raise ValueError("inconsistent shapes") + return self._add_sparse(other) + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._add_dense(other) + else: + return NotImplemented + + def __radd__(self,other): # other + self + return self.__add__(other) + + def __sub__(self, other): # self - other + if isscalarlike(other): + if other == 0: + return self.copy() + raise NotImplementedError('subtracting a nonzero scalar from a ' + 'sparse array is not supported') + elif issparse(other): + if other.shape != self.shape: + raise ValueError("inconsistent shapes") + return self._sub_sparse(other) + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._sub_dense(other) + else: + return NotImplemented + + def __rsub__(self,other): # other - self + if isscalarlike(other): + if other == 0: + return -self.copy() + raise NotImplementedError('subtracting a sparse array from a ' + 'nonzero scalar is not supported') + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._rsub_dense(other) + else: + return NotImplemented + + def _matmul_dispatch(self, other): + """np.array-like matmul & `np.matrix`-like mul, i.e. `dot` or `NotImplemented` + + interpret other and call one of the following + self._mul_scalar() + self._matmul_vector() + self._matmul_multivector() + self._matmul_sparse() + """ + # This method has to be different from `__matmul__` because it is also + # called by sparse matrix classes. + + # Currently matrix multiplication is only supported + # for 2D arrays. Hence we unpacked and use only the + # two last axes' lengths. + M, N = self._shape_as_2d + + if other.__class__ is np.ndarray: + # Fast path for the most common case + if other.shape == (N,): + return self._matmul_vector(other) + elif other.shape == (N, 1): + result = self._matmul_vector(other.ravel()) + if self.ndim == 1: + return result + return result.reshape(M, 1) + elif other.ndim == 2 and other.shape[0] == N: + return self._matmul_multivector(other) + + if isscalarlike(other): + # scalar value + return self._mul_scalar(other) + + if issparse(other): + if self.shape[-1] != other.shape[0]: + raise ValueError('dimension mismatch') + if other.ndim == 1: + raise ValueError('Cannot yet multiply a 1d sparse array') + return self._matmul_sparse(other) + + # If it's a list or whatever, treat it like an array + other_a = np.asanyarray(other) + + if other_a.ndim == 0 and other_a.dtype == np.object_: + # Not interpretable as an array; return NotImplemented so that + # other's __rmatmul__ can kick in if that's implemented. + return NotImplemented + + try: + other.shape + except AttributeError: + other = other_a + + if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1: + # dense row or column vector + if other.shape != (N,) and other.shape != (N, 1): + raise ValueError('dimension mismatch') + + result = self._matmul_vector(np.ravel(other)) + + if isinstance(other, np.matrix): + result = self._ascontainer(result) + + if other.ndim == 2 and other.shape[1] == 1: + # If 'other' was an (nx1) column vector, reshape the result + result = result.reshape(-1, 1) + + return result + + elif other.ndim == 2: + ## + # dense 2D array or matrix ("multivector") + + if other.shape[0] != N: + raise ValueError('dimension mismatch') + + result = self._matmul_multivector(np.asarray(other)) + + if isinstance(other, np.matrix): + result = self._ascontainer(result) + + return result + + else: + raise ValueError('could not interpret dimensions') + + def __mul__(self, *args, **kwargs): + return self.multiply(*args, **kwargs) + + def __rmul__(self, *args, **kwargs): # other * self + return self.multiply(*args, **kwargs) + + # by default, use CSR for __mul__ handlers + def _mul_scalar(self, other): + return self.tocsr()._mul_scalar(other) + + def _matmul_vector(self, other): + return self.tocsr()._matmul_vector(other) + + def _matmul_multivector(self, other): + return self.tocsr()._matmul_multivector(other) + + def _matmul_sparse(self, other): + return self.tocsr()._matmul_sparse(other) + + def _rmatmul_dispatch(self, other): + if isscalarlike(other): + return self._mul_scalar(other) + else: + # Don't use asarray unless we have to + try: + tr = other.transpose() + except AttributeError: + tr = np.asarray(other).transpose() + ret = self.transpose()._matmul_dispatch(tr) + if ret is NotImplemented: + return NotImplemented + return ret.transpose() + + ####################### + # matmul (@) operator # + ####################### + + def __matmul__(self, other): + if isscalarlike(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self._matmul_dispatch(other) + + def __rmatmul__(self, other): + if isscalarlike(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self._rmatmul_dispatch(other) + + #################### + # Other Arithmetic # + #################### + + def _divide(self, other, true_divide=False, rdivide=False): + if isscalarlike(other): + if rdivide: + if true_divide: + return np.true_divide(other, self.todense()) + else: + return np.divide(other, self.todense()) + + if true_divide and np.can_cast(self.dtype, np.float64): + return self.astype(np.float64)._mul_scalar(1./other) + else: + r = self._mul_scalar(1./other) + + scalar_dtype = np.asarray(other).dtype + if (np.issubdtype(self.dtype, np.integer) and + np.issubdtype(scalar_dtype, np.integer)): + return r.astype(self.dtype) + else: + return r + + elif isdense(other): + if not rdivide: + if true_divide: + recip = np.true_divide(1., other) + else: + recip = np.divide(1., other) + return self.multiply(recip) + else: + if true_divide: + return np.true_divide(other, self.todense()) + else: + return np.divide(other, self.todense()) + elif issparse(other): + if rdivide: + return other._divide(self, true_divide, rdivide=False) + + self_csr = self.tocsr() + if true_divide and np.can_cast(self.dtype, np.float64): + return self_csr.astype(np.float64)._divide_sparse(other) + else: + return self_csr._divide_sparse(other) + else: + return NotImplemented + + def __truediv__(self, other): + return self._divide(other, true_divide=True) + + def __div__(self, other): + # Always do true division + return self._divide(other, true_divide=True) + + def __rtruediv__(self, other): + # Implementing this as the inverse would be too magical -- bail out + return NotImplemented + + def __rdiv__(self, other): + # Implementing this as the inverse would be too magical -- bail out + return NotImplemented + + def __neg__(self): + return -self.tocsr() + + def __iadd__(self, other): + return NotImplemented + + def __isub__(self, other): + return NotImplemented + + def __imul__(self, other): + return NotImplemented + + def __idiv__(self, other): + return self.__itruediv__(other) + + def __itruediv__(self, other): + return NotImplemented + + def __pow__(self, *args, **kwargs): + return self.power(*args, **kwargs) + + def transpose(self, axes=None, copy=False): + """ + Reverses the dimensions of the sparse array/matrix. + + Parameters + ---------- + axes : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value. + copy : bool, optional + Indicates whether or not attributes of `self` should be + copied whenever possible. The degree to which attributes + are copied varies depending on the type of sparse array/matrix + being used. + + Returns + ------- + p : `self` with the dimensions reversed. + + Notes + ----- + If `self` is a `csr_array` or a `csc_array`, then this will return a + `csc_array` or a `csr_array`, respectively. + + See Also + -------- + numpy.transpose : NumPy's implementation of 'transpose' for ndarrays + """ + return self.tocsr(copy=copy).transpose(axes=axes, copy=False) + + def conjugate(self, copy=True): + """Element-wise complex conjugation. + + If the array/matrix is of non-complex data type and `copy` is False, + this method does nothing and the data is not copied. + + Parameters + ---------- + copy : bool, optional + If True, the result is guaranteed to not share data with self. + + Returns + ------- + A : The element-wise complex conjugate. + + """ + if np.issubdtype(self.dtype, np.complexfloating): + return self.tocsr(copy=copy).conjugate(copy=False) + elif copy: + return self.copy() + else: + return self + + def conj(self, copy=True): + return self.conjugate(copy=copy) + + conj.__doc__ = conjugate.__doc__ + + def _real(self): + return self.tocsr()._real() + + def _imag(self): + return self.tocsr()._imag() + + def nonzero(self): + """Nonzero indices of the array/matrix. + + Returns a tuple of arrays (row,col) containing the indices + of the non-zero elements of the array. + + Examples + -------- + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1,2,0],[0,0,3],[4,0,5]]) + >>> A.nonzero() + (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2])) + + """ + + # convert to COOrdinate format + A = self.tocoo() + nz_mask = A.data != 0 + return (A.row[nz_mask], A.col[nz_mask]) + + def _getcol(self, j): + """Returns a copy of column j of the array, as an (m x 1) sparse + array (column vector). + """ + if self.ndim == 1: + raise ValueError("getcol not provided for 1d arrays. Use indexing A[j]") + # Subclasses should override this method for efficiency. + # Post-multiply by a (n x 1) column vector 'a' containing all zeros + # except for a_j = 1 + N = self.shape[-1] + if j < 0: + j += N + if j < 0 or j >= N: + raise IndexError("index out of bounds") + col_selector = self._csc_container(([1], [[j], [0]]), + shape=(N, 1), dtype=self.dtype) + result = self @ col_selector + return result + + def _getrow(self, i): + """Returns a copy of row i of the array, as a (1 x n) sparse + array (row vector). + """ + if self.ndim == 1: + raise ValueError("getrow not meaningful for a 1d array") + # Subclasses should override this method for efficiency. + # Pre-multiply by a (1 x m) row vector 'a' containing all zeros + # except for a_i = 1 + M = self.shape[0] + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError("index out of bounds") + row_selector = self._csr_container(([1], [[0], [i]]), + shape=(1, M), dtype=self.dtype) + return row_selector @ self + + # The following dunder methods cannot be implemented. + # + # def __array__(self): + # # Sparse matrices rely on NumPy wrapping them in object arrays under + # # the hood to make unary ufuncs work on them. So we cannot raise + # # TypeError here - which would be handy to not give users object + # # arrays they probably don't want (they're looking for `.toarray()`). + # # + # # Conversion with `toarray()` would also break things because of the + # # behavior discussed above, plus we want to avoid densification by + # # accident because that can too easily blow up memory. + # + # def __array_ufunc__(self): + # # We cannot implement __array_ufunc__ due to mismatching semantics. + # # See gh-7707 and gh-7349 for details. + # + # def __array_function__(self): + # # We cannot implement __array_function__ due to mismatching semantics. + # # See gh-10362 for details. + + def todense(self, order=None, out=None): + """ + Return a dense representation of this sparse array/matrix. + + Parameters + ---------- + order : {'C', 'F'}, optional + Whether to store multi-dimensional data in C (row-major) + or Fortran (column-major) order in memory. The default + is 'None', which provides no ordering guarantees. + Cannot be specified in conjunction with the `out` + argument. + + out : ndarray, 2-D, optional + If specified, uses this array (or `numpy.matrix`) as the + output buffer instead of allocating a new array to + return. The provided array must have the same shape and + dtype as the sparse array/matrix on which you are calling the + method. + + Returns + ------- + arr : numpy.matrix, 2-D + A NumPy matrix object with the same shape and containing + the same data represented by the sparse array/matrix, with the + requested memory order. If `out` was passed and was an + array (rather than a `numpy.matrix`), it will be filled + with the appropriate values and returned wrapped in a + `numpy.matrix` object that shares the same memory. + """ + return self._ascontainer(self.toarray(order=order, out=out)) + + def toarray(self, order=None, out=None): + """ + Return a dense ndarray representation of this sparse array/matrix. + + Parameters + ---------- + order : {'C', 'F'}, optional + Whether to store multidimensional data in C (row-major) + or Fortran (column-major) order in memory. The default + is 'None', which provides no ordering guarantees. + Cannot be specified in conjunction with the `out` + argument. + + out : ndarray, 2-D, optional + If specified, uses this array as the output buffer + instead of allocating a new array to return. The provided + array must have the same shape and dtype as the sparse + array/matrix on which you are calling the method. For most + sparse types, `out` is required to be memory contiguous + (either C or Fortran ordered). + + Returns + ------- + arr : ndarray, 2-D + An array with the same shape and containing the same + data represented by the sparse array/matrix, with the requested + memory order. If `out` was passed, the same object is + returned after being modified in-place to contain the + appropriate values. + """ + return self.tocoo(copy=False).toarray(order=order, out=out) + + # Any sparse array format deriving from _spbase must define one of + # tocsr or tocoo. The other conversion methods may be implemented for + # efficiency, but are not required. + def tocsr(self, copy=False): + """Convert this array/matrix to Compressed Sparse Row format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant csr_array/matrix. + """ + return self.tocoo(copy=copy).tocsr(copy=False) + + def todok(self, copy=False): + """Convert this array/matrix to Dictionary Of Keys format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant dok_array/matrix. + """ + return self.tocoo(copy=copy).todok(copy=False) + + def tocoo(self, copy=False): + """Convert this array/matrix to COOrdinate format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant coo_array/matrix. + """ + return self.tocsr(copy=False).tocoo(copy=copy) + + def tolil(self, copy=False): + """Convert this array/matrix to List of Lists format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant lil_array/matrix. + """ + return self.tocsr(copy=False).tolil(copy=copy) + + def todia(self, copy=False): + """Convert this array/matrix to sparse DIAgonal format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant dia_array/matrix. + """ + return self.tocoo(copy=copy).todia(copy=False) + + def tobsr(self, blocksize=None, copy=False): + """Convert this array/matrix to Block Sparse Row format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant bsr_array/matrix. + + When blocksize=(R, C) is provided, it will be used for construction of + the bsr_array/matrix. + """ + return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy) + + def tocsc(self, copy=False): + """Convert this array/matrix to Compressed Sparse Column format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant csc_array/matrix. + """ + return self.tocsr(copy=copy).tocsc(copy=False) + + def copy(self): + """Returns a copy of this array/matrix. + + No data/indices will be shared between the returned value and current + array/matrix. + """ + return self.__class__(self, copy=True) + + def sum(self, axis=None, dtype=None, out=None): + """ + Sum the array/matrix elements over a given axis. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the sum of all the array/matrix elements, returning a scalar + (i.e., `axis` = `None`). + dtype : dtype, optional + The type of the returned array/matrix and of the accumulator in which + the elements are summed. The dtype of `a` is used by default + unless `a` has an integer dtype of less precision than the default + platform integer. In that case, if `a` is signed then the platform + integer is used while if `a` is unsigned then an unsigned integer + of the same precision as the platform integer is used. + + .. versionadded:: 0.18.0 + + out : np.matrix, optional + Alternative output matrix in which to place the result. It must + have the same shape as the expected output, but the type of the + output values will be cast if necessary. + + .. versionadded:: 0.18.0 + + Returns + ------- + sum_along_axis : np.matrix + A matrix with the same shape as `self`, with the specified + axis removed. + + See Also + -------- + numpy.matrix.sum : NumPy's implementation of 'sum' for matrices + + """ + validateaxis(axis) + + # Mimic numpy's casting. + res_dtype = get_sum_dtype(self.dtype) + + if self.ndim == 1: + if axis not in (None, -1, 0): + raise ValueError("axis must be None, -1 or 0") + ret = (self @ np.ones(self.shape, dtype=res_dtype)).astype(dtype) + + if out is not None: + if any(dim != 1 for dim in out.shape): + raise ValueError("dimensions do not match") + out[...] = ret + return ret + + # We use multiplication by a matrix of ones to achieve this. + # For some sparse array formats more efficient methods are + # possible -- these should override this function. + M, N = self.shape + + if axis is None: + # sum over rows and columns + return ( + self @ self._ascontainer(np.ones((N, 1), dtype=res_dtype)) + ).sum(dtype=dtype, out=out) + + if axis < 0: + axis += 2 + + # axis = 0 or 1 now + if axis == 0: + # sum over columns + ret = self._ascontainer( + np.ones((1, M), dtype=res_dtype) + ) @ self + else: + # sum over rows + ret = self @ self._ascontainer( + np.ones((N, 1), dtype=res_dtype) + ) + + if out is not None and out.shape != ret.shape: + raise ValueError("dimensions do not match") + + return ret.sum(axis=axis, dtype=dtype, out=out) + + def mean(self, axis=None, dtype=None, out=None): + """ + Compute the arithmetic mean along the specified axis. + + Returns the average of the array/matrix elements. The average is taken + over all elements in the array/matrix by default, otherwise over the + specified axis. `float64` intermediate and return values are used + for integer inputs. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the mean is computed. The default is to compute + the mean of all elements in the array/matrix (i.e., `axis` = `None`). + dtype : data-type, optional + Type to use in computing the mean. For integer inputs, the default + is `float64`; for floating point inputs, it is the same as the + input dtype. + + .. versionadded:: 0.18.0 + + out : np.matrix, optional + Alternative output matrix in which to place the result. It must + have the same shape as the expected output, but the type of the + output values will be cast if necessary. + + .. versionadded:: 0.18.0 + + Returns + ------- + m : np.matrix + + See Also + -------- + numpy.matrix.mean : NumPy's implementation of 'mean' for matrices + + """ + validateaxis(axis) + + res_dtype = self.dtype.type + integral = (np.issubdtype(self.dtype, np.integer) or + np.issubdtype(self.dtype, np.bool_)) + + # output dtype + if dtype is None: + if integral: + res_dtype = np.float64 + else: + res_dtype = np.dtype(dtype).type + + # intermediate dtype for summation + inter_dtype = np.float64 if integral else res_dtype + inter_self = self.astype(inter_dtype) + + if self.ndim == 1: + if axis not in (None, -1, 0): + raise ValueError("axis must be None, -1 or 0") + res = inter_self / self.shape[-1] + return res.sum(dtype=res_dtype, out=out) + + if axis is None: + return (inter_self / (self.shape[0] * self.shape[1]))\ + .sum(dtype=res_dtype, out=out) + + if axis < 0: + axis += 2 + + # axis = 0 or 1 now + if axis == 0: + return (inter_self * (1.0 / self.shape[0])).sum( + axis=0, dtype=res_dtype, out=out) + else: + return (inter_self * (1.0 / self.shape[1])).sum( + axis=1, dtype=res_dtype, out=out) + + def diagonal(self, k=0): + """Returns the kth diagonal of the array/matrix. + + Parameters + ---------- + k : int, optional + Which diagonal to get, corresponding to elements a[i, i+k]. + Default: 0 (the main diagonal). + + .. versionadded:: 1.0 + + See also + -------- + numpy.diagonal : Equivalent numpy function. + + Examples + -------- + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) + >>> A.diagonal() + array([1, 0, 5]) + >>> A.diagonal(k=1) + array([2, 3]) + """ + return self.tocsr().diagonal(k=k) + + def trace(self, offset=0): + """Returns the sum along diagonals of the sparse array/matrix. + + Parameters + ---------- + offset : int, optional + Which diagonal to get, corresponding to elements a[i, i+offset]. + Default: 0 (the main diagonal). + + """ + return self.diagonal(k=offset).sum() + + def setdiag(self, values, k=0): + """ + Set diagonal or off-diagonal elements of the array/matrix. + + Parameters + ---------- + values : array_like + New values of the diagonal elements. + + Values may have any length. If the diagonal is longer than values, + then the remaining diagonal entries will not be set. If values are + longer than the diagonal, then the remaining values are ignored. + + If a scalar value is given, all of the diagonal is set to it. + + k : int, optional + Which off-diagonal to set, corresponding to elements a[i,i+k]. + Default: 0 (the main diagonal). + + """ + M, N = self.shape + if (k > 0 and k >= N) or (k < 0 and -k >= M): + raise ValueError("k exceeds array dimensions") + self._setdiag(np.asarray(values), k) + + def _setdiag(self, values, k): + """This part of the implementation gets overridden by the + different formats. + """ + M, N = self.shape + if k < 0: + if values.ndim == 0: + # broadcast + max_index = min(M+k, N) + for i in range(max_index): + self[i - k, i] = values + else: + max_index = min(M+k, N, len(values)) + if max_index <= 0: + return + for i, v in enumerate(values[:max_index]): + self[i - k, i] = v + else: + if values.ndim == 0: + # broadcast + max_index = min(M, N-k) + for i in range(max_index): + self[i, i + k] = values + else: + max_index = min(M, N-k, len(values)) + if max_index <= 0: + return + for i, v in enumerate(values[:max_index]): + self[i, i + k] = v + + def _process_toarray_args(self, order, out): + if out is not None: + if order is not None: + raise ValueError('order cannot be specified if out ' + 'is not None') + if out.shape != self.shape or out.dtype != self.dtype: + raise ValueError('out array must be same dtype and shape as ' + 'sparse array') + out[...] = 0. + return out + else: + return np.zeros(self.shape, dtype=self.dtype, order=order) + + def _get_index_dtype(self, arrays=(), maxval=None, check_contents=False): + """ + Determine index dtype for array. + + This wraps _sputils.get_index_dtype, providing compatibility for both + array and matrix API sparse matrices. Matrix API sparse matrices would + attempt to downcast the indices - which can be computationally + expensive and undesirable for users. The array API changes this + behaviour. + + See discussion: https://github.com/scipy/scipy/issues/16774 + + The get_index_dtype import is due to implementation details of the test + suite. It allows the decorator ``with_64bit_maxval_limit`` to mock a + lower int32 max value for checks on the matrix API's downcasting + behaviour. + """ + from ._sputils import get_index_dtype + + # Don't check contents for array API + return get_index_dtype(arrays, + maxval, + (check_contents and not isinstance(self, sparray))) + + + ## All methods below are deprecated and should be removed in + ## scipy 1.14.0 + ## + ## Also uncomment the definition of shape above. + + def get_shape(self): + """Get shape of a sparse array/matrix. + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use `X.shape` instead. + """ + msg = ( + "`get_shape` is deprecated and will be removed in v1.14.0; " + "use `X.shape` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + + return self._shape + + def set_shape(self, shape): + """See `reshape`. + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use `X.reshape` instead. + """ + msg = ( + "Shape assignment is deprecated and will be removed in v1.14.0; " + "use `reshape` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + + # Make sure copy is False since this is in place + # Make sure format is unchanged because we are doing a __dict__ swap + new_self = self.reshape(shape, copy=False).asformat(self.format) + self.__dict__ = new_self.__dict__ + + shape = property( + fget=lambda self: self._shape, + fset=set_shape, + doc="""The shape of the array. + +Note that, starting in SciPy 1.14.0, this property will no longer be +settable. To change the array shape, use `X.reshape` instead. +""" + ) + + def asfptype(self): + """Upcast array/matrix to a floating point format (if necessary) + + .. deprecated:: 1.11.0 + This method is for internal use only, and will be removed from the + public API in SciPy 1.14.0. + """ + msg = ( + "`asfptype` is an internal function, and is deprecated " + "as part of the public API. It will be removed in v1.14.0." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self._asfptype() + + def getmaxprint(self): + """Maximum number of elements to display when printed. + + .. deprecated:: 1.11.0 + This method is for internal use only, and will be removed from the + public API in SciPy 1.14.0. + """ + msg = ( + "`getmaxprint` is an internal function, and is deprecated " + "as part of the public API. It will be removed in v1.14.0." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self._getmaxprint() + + def getformat(self): + """Sparse array/matrix storage format. + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use `X.format` instead. + """ + msg = ( + "`getformat` is deprecated and will be removed in v1.14.0; " + "use `X.format` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self.format + + def getnnz(self, axis=None): + """Number of stored values, including explicit zeros. + + Parameters + ---------- + axis : None, 0, or 1 + Select between the number of values across the whole array/matrix, in + each column, or in each row. + + See also + -------- + count_nonzero : Number of non-zero entries + """ + return self._getnnz(axis=axis) + + def getH(self): + """Return the Hermitian transpose of this array/matrix. + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use `X.conj().T` instead. + """ + msg = ( + "`getH` is deprecated and will be removed in v1.14.0; " + "use `X.conj().T` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self.conjugate().transpose() + + def getcol(self, j): + """Returns a copy of column j of the array/matrix, as an (m x 1) sparse + array/matrix (column vector). + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use array/matrix indexing instead. + """ + msg = ( + "`getcol` is deprecated and will be removed in v1.14.0; " + f"use `X[:, [{j}]]` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self._getcol(j) + + def getrow(self, i): + """Returns a copy of row i of the array/matrix, as a (1 x n) sparse + array/matrix (row vector). + + .. deprecated:: 1.11.0 + This method will be removed in SciPy 1.14.0. + Use array/matrix indexing instead. + """ + msg = ( + "`getrow` is deprecated and will be removed in v1.14.0; " + f"use `X[[{i}]]` instead." + ) + warn(msg, DeprecationWarning, stacklevel=2) + return self._getrow(i) + + ## End 1.14.0 deprecated methods + + +class sparray: + """A namespace class to separate sparray from spmatrix""" + pass + +sparray.__doc__ = _spbase.__doc__ + + +def issparse(x): + """Is `x` of a sparse array or sparse matrix type? + + Parameters + ---------- + x + object to check for being a sparse array or sparse matrix + + Returns + ------- + bool + True if `x` is a sparse array or a sparse matrix, False otherwise + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array, csr_matrix, issparse + >>> issparse(csr_matrix([[5]])) + True + >>> issparse(csr_array([[5]])) + True + >>> issparse(np.array([[5]])) + False + >>> issparse(5) + False + """ + return isinstance(x, _spbase) + + +def isspmatrix(x): + """Is `x` of a sparse matrix type? + + Parameters + ---------- + x + object to check for being a sparse matrix + + Returns + ------- + bool + True if `x` is a sparse matrix, False otherwise + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array, csr_matrix, isspmatrix + >>> isspmatrix(csr_matrix([[5]])) + True + >>> isspmatrix(csr_array([[5]])) + False + >>> isspmatrix(np.array([[5]])) + False + >>> isspmatrix(5) + False + """ + return isinstance(x, spmatrix) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_bsr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_bsr.py new file mode 100644 index 0000000000000000000000000000000000000000..97de7149baca132ca059ff9ae0c8eb976c0599c3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_bsr.py @@ -0,0 +1,855 @@ +"""Compressed Block Sparse Row format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['bsr_array', 'bsr_matrix', 'isspmatrix_bsr'] + +from warnings import warn + +import numpy as np + +from scipy._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._data import _data_matrix, _minmax_mixin +from ._compressed import _cs_matrix +from ._base import issparse, _formats, _spbase, sparray +from ._sputils import (isshape, getdtype, getdata, to_native, upcast, + check_shape) +from . import _sparsetools +from ._sparsetools import (bsr_matvec, bsr_matvecs, csr_matmat_maxnnz, + bsr_matmat, bsr_transpose, bsr_sort_indices, + bsr_tocsr) + + +class _bsr_base(_cs_matrix, _minmax_mixin): + _format = 'bsr' + + def __init__(self, arg1, shape=None, dtype=None, copy=False, blocksize=None): + _data_matrix.__init__(self) + + if issparse(arg1): + if arg1.format == self.format and copy: + arg1 = arg1.copy() + else: + arg1 = arg1.tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + elif isinstance(arg1,tuple): + if isshape(arg1): + # it's a tuple of matrix dimensions (M,N) + self._shape = check_shape(arg1) + M,N = self.shape + # process blocksize + if blocksize is None: + blocksize = (1,1) + else: + if not isshape(blocksize): + raise ValueError('invalid blocksize=%s' % blocksize) + blocksize = tuple(blocksize) + self.data = np.zeros((0,) + blocksize, getdtype(dtype, default=float)) + + R,C = blocksize + if (M % R) != 0 or (N % C) != 0: + raise ValueError('shape must be multiple of blocksize') + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + idx_dtype = self._get_index_dtype(maxval=max(M//R, N//C, R, C)) + self.indices = np.zeros(0, dtype=idx_dtype) + self.indptr = np.zeros(M//R + 1, dtype=idx_dtype) + + elif len(arg1) == 2: + # (data,(row,col)) format + coo = self._coo_container(arg1, dtype=dtype, shape=shape) + bsr = coo.tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + bsr.indptr, bsr.indices, bsr.data, bsr._shape + ) + + elif len(arg1) == 3: + # (data,indices,indptr) format + (data, indices, indptr) = arg1 + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + maxval = 1 + if shape is not None: + maxval = max(shape) + if blocksize is not None: + maxval = max(maxval, max(blocksize)) + idx_dtype = self._get_index_dtype((indices, indptr), maxval=maxval, + check_contents=True) + if not copy: + copy = copy_if_needed + self.indices = np.array(indices, copy=copy, dtype=idx_dtype) + self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype) + self.data = getdata(data, copy=copy, dtype=dtype) + if self.data.ndim != 3: + raise ValueError( + f'BSR data must be 3-dimensional, got shape={self.data.shape}' + ) + if blocksize is not None: + if not isshape(blocksize): + raise ValueError(f'invalid blocksize={blocksize}') + if tuple(blocksize) != self.data.shape[1:]: + raise ValueError('mismatching blocksize={} vs {}'.format( + blocksize, self.data.shape[1:])) + else: + raise ValueError('unrecognized bsr_array constructor usage') + else: + # must be dense + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise ValueError("unrecognized form for" + " %s_matrix constructor" % self.format) from e + if isinstance(self, sparray) and arg1.ndim != 2: + raise ValueError(f"BSR arrays don't support {arg1.ndim}D input. Use 2D") + arg1 = self._coo_container(arg1, dtype=dtype).tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + if shape is not None: + self._shape = check_shape(shape) + else: + if self.shape is None: + # shape not already set, try to infer dimensions + try: + M = len(self.indptr) - 1 + N = self.indices.max() + 1 + except Exception as e: + raise ValueError('unable to infer matrix dimensions') from e + else: + R,C = self.blocksize + self._shape = check_shape((M*R,N*C)) + + if self.shape is None: + if shape is None: + # TODO infer shape here + raise ValueError('need to infer shape') + else: + self._shape = check_shape(shape) + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self.check_format(full_check=False) + + def check_format(self, full_check=True): + """Check whether the array/matrix respects the BSR format. + + Parameters + ---------- + full_check : bool, optional + If `True`, run rigorous check, scanning arrays for valid values. + Note that activating those check might copy arrays for casting, + modifying indices and index pointers' inplace. + If `False`, run basic checks on attributes. O(1) operations. + Default is `True`. + """ + M,N = self.shape + R,C = self.blocksize + + # index arrays should have integer data types + if self.indptr.dtype.kind != 'i': + warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})", + stacklevel=2) + if self.indices.dtype.kind != 'i': + warn(f"indices array has non-integer dtype ({self.indices.dtype.name})", + stacklevel=2) + + # check array shapes + if self.indices.ndim != 1 or self.indptr.ndim != 1: + raise ValueError("indices, and indptr should be 1-D") + if self.data.ndim != 3: + raise ValueError("data should be 3-D") + + # check index pointer + if (len(self.indptr) != M//R + 1): + raise ValueError("index pointer size (%d) should be (%d)" % + (len(self.indptr), M//R + 1)) + if (self.indptr[0] != 0): + raise ValueError("index pointer should start with 0") + + # check index and data arrays + if (len(self.indices) != len(self.data)): + raise ValueError("indices and data should have the same size") + if (self.indptr[-1] > len(self.indices)): + raise ValueError("Last value of index pointer should be less than " + "the size of index and data arrays") + + self.prune() + + if full_check: + # check format validity (more expensive) + if self.nnz > 0: + if self.indices.max() >= N//C: + raise ValueError("column index values must be < %d (now max %d)" + % (N//C, self.indices.max())) + if self.indices.min() < 0: + raise ValueError("column index values must be >= 0") + if np.diff(self.indptr).min() < 0: + raise ValueError("index pointer values must form a " + "non-decreasing sequence") + + idx_dtype = self._get_index_dtype((self.indices, self.indptr)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + self.data = to_native(self.data) + # if not self.has_sorted_indices(): + # warn('Indices were not in sorted order. Sorting indices.') + # self.sort_indices(check_first=False) + + @property + def blocksize(self) -> tuple: + """Block size of the matrix.""" + return self.data.shape[1:] + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError("_getnnz over an axis is not implemented " + "for BSR format") + R,C = self.blocksize + return int(self.indptr[-1] * R * C) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def __repr__(self): + _, fmt = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + shape_str = 'x'.join(str(x) for x in self.shape) + blksz = 'x'.join(str(x) for x in self.blocksize) + return ( + f"<{shape_str} sparse {sparse_cls} of type '{self.dtype.type}'\n" + f"\twith {self.nnz} stored elements (blocksize = {blksz}) in {fmt} format>" + ) + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + R, C = self.blocksize + y = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), + dtype=upcast(self.dtype)) + _sparsetools.bsr_diagonal(k, rows // R, cols // C, R, C, + self.indptr, self.indices, + np.ravel(self.data), y) + return y + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + ########################## + # NotImplemented methods # + ########################## + + def __getitem__(self,key): + raise NotImplementedError + + def __setitem__(self,key,val): + raise NotImplementedError + + ###################### + # Arithmetic methods # + ###################### + + def _add_dense(self, other): + return self.tocoo(copy=False)._add_dense(other) + + def _matmul_vector(self, other): + M,N = self.shape + R,C = self.blocksize + + result = np.zeros(self.shape[0], dtype=upcast(self.dtype, other.dtype)) + + bsr_matvec(M//R, N//C, R, C, + self.indptr, self.indices, self.data.ravel(), + other, result) + + return result + + def _matmul_multivector(self,other): + R,C = self.blocksize + M,N = self.shape + n_vecs = other.shape[1] # number of column vectors + + result = np.zeros((M,n_vecs), dtype=upcast(self.dtype,other.dtype)) + + bsr_matvecs(M//R, N//C, n_vecs, R, C, + self.indptr, self.indices, self.data.ravel(), + other.ravel(), result.ravel()) + + return result + + def _matmul_sparse(self, other): + M, K1 = self.shape + K2, N = other.shape + + R,n = self.blocksize + + # convert to this format + if other.format == "bsr": + C = other.blocksize[1] + else: + C = 1 + + if other.format == "csr" and n == 1: + other = other.tobsr(blocksize=(n,C), copy=False) # lightweight conversion + else: + other = other.tobsr(blocksize=(n,C)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices)) + + bnnz = csr_matmat_maxnnz(M//R, N//C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=bnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(bnnz, dtype=idx_dtype) + data = np.empty(R*C*bnnz, dtype=upcast(self.dtype,other.dtype)) + + bsr_matmat(bnnz, M//R, N//C, R, C, n, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + np.ravel(self.data), + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype), + np.ravel(other.data), + indptr, + indices, + data) + + data = data.reshape(-1,R,C) + + # TODO eliminate zeros + + return self._bsr_container( + (data, indices, indptr), shape=(M, N), blocksize=(R, C) + ) + + ###################### + # Conversion methods # + ###################### + + def tobsr(self, blocksize=None, copy=False): + """Convert this array/matrix into Block Sparse Row Format. + + With copy=False, the data/indices may be shared between this + array/matrix and the resultant bsr_array/bsr_matrix. + + If blocksize=(R, C) is provided, it will be used for determining + block size of the bsr_array/bsr_matrix. + """ + if blocksize not in [None, self.blocksize]: + return self.tocsr().tobsr(blocksize=blocksize) + if copy: + return self.copy() + else: + return self + + def tocsr(self, copy=False): + M, N = self.shape + R, C = self.blocksize + nnz = self.nnz + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(nnz, N)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=upcast(self.dtype)) + + bsr_tocsr(M // R, # n_brow + N // C, # n_bcol + R, C, + self.indptr.astype(idx_dtype, copy=False), + self.indices.astype(idx_dtype, copy=False), + self.data, + indptr, + indices, + data) + return self._csr_container((data, indices, indptr), shape=self.shape) + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def tocsc(self, copy=False): + return self.tocsr(copy=False).tocsc(copy=copy) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocoo(self, copy=True): + """Convert this array/matrix to COOrdinate format. + + When copy=False the data array will be shared between + this array/matrix and the resultant coo_array/coo_matrix. + """ + + M,N = self.shape + R,C = self.blocksize + + indptr_diff = np.diff(self.indptr) + if indptr_diff.dtype.itemsize > np.dtype(np.intp).itemsize: + # Check for potential overflow + indptr_diff_limited = indptr_diff.astype(np.intp) + if np.any(indptr_diff_limited != indptr_diff): + raise ValueError("Matrix too big to convert") + indptr_diff = indptr_diff_limited + + idx_dtype = self._get_index_dtype(maxval=max(M, N)) + row = (R * np.arange(M//R, dtype=idx_dtype)).repeat(indptr_diff) + row = row.repeat(R*C).reshape(-1,R,C) + row += np.tile(np.arange(R, dtype=idx_dtype).reshape(-1,1), (1,C)) + row = row.reshape(-1) + + col = ((C * self.indices).astype(idx_dtype, copy=False) + .repeat(R*C).reshape(-1,R,C)) + col += np.tile(np.arange(C, dtype=idx_dtype), (R,1)) + col = col.reshape(-1) + + data = self.data.reshape(-1) + + if copy: + data = data.copy() + + return self._coo_container( + (data, (row, col)), shape=self.shape + ) + + def toarray(self, order=None, out=None): + return self.tocoo(copy=False).toarray(order=order, out=out) + + toarray.__doc__ = _spbase.toarray.__doc__ + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + R, C = self.blocksize + M, N = self.shape + NBLK = self.nnz//(R*C) + + if self.nnz == 0: + return self._bsr_container((N, M), blocksize=(C, R), + dtype=self.dtype, copy=copy) + + indptr = np.empty(N//C + 1, dtype=self.indptr.dtype) + indices = np.empty(NBLK, dtype=self.indices.dtype) + data = np.empty((NBLK, C, R), dtype=self.data.dtype) + + bsr_transpose(M//R, N//C, R, C, + self.indptr, self.indices, self.data.ravel(), + indptr, indices, data.ravel()) + + return self._bsr_container((data, indices, indptr), + shape=(N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + ############################################################## + # methods that examine or modify the internal data structure # + ############################################################## + + def eliminate_zeros(self): + """Remove zero elements in-place.""" + + if not self.nnz: + return # nothing to do + + R,C = self.blocksize + M,N = self.shape + + mask = (self.data != 0).reshape(-1,R*C).sum(axis=1) # nonzero blocks + + nonzero_blocks = mask.nonzero()[0] + + self.data[:len(nonzero_blocks)] = self.data[nonzero_blocks] + + # modifies self.indptr and self.indices *in place* + _sparsetools.csr_eliminate_zeros(M//R, N//C, self.indptr, + self.indices, mask) + self.prune() + + def sum_duplicates(self): + """Eliminate duplicate array/matrix entries by adding them together + + The is an *in place* operation + """ + if self.has_canonical_format: + return + self.sort_indices() + R, C = self.blocksize + M, N = self.shape + + # port of _sparsetools.csr_sum_duplicates + n_row = M // R + nnz = 0 + row_end = 0 + for i in range(n_row): + jj = row_end + row_end = self.indptr[i+1] + while jj < row_end: + j = self.indices[jj] + x = self.data[jj] + jj += 1 + while jj < row_end and self.indices[jj] == j: + x += self.data[jj] + jj += 1 + self.indices[nnz] = j + self.data[nnz] = x + nnz += 1 + self.indptr[i+1] = nnz + + self.prune() # nnz may have changed + self.has_canonical_format = True + + def sort_indices(self): + """Sort the indices of this array/matrix *in place* + """ + if self.has_sorted_indices: + return + + R,C = self.blocksize + M,N = self.shape + + bsr_sort_indices(M//R, N//C, R, C, self.indptr, self.indices, self.data.ravel()) + + self.has_sorted_indices = True + + def prune(self): + """Remove empty space after all non-zero elements. + """ + + R,C = self.blocksize + M,N = self.shape + + if len(self.indptr) != M//R + 1: + raise ValueError("index pointer has invalid length") + + bnnz = self.indptr[-1] + + if len(self.indices) < bnnz: + raise ValueError("indices array has too few elements") + if len(self.data) < bnnz: + raise ValueError("data array has too few elements") + + self.data = self.data[:bnnz] + self.indices = self.indices[:bnnz] + + # utility functions + def _binopt(self, other, op, in_shape=None, out_shape=None): + """Apply the binary operation fn to two sparse matrices.""" + + # Ideally we'd take the GCDs of the blocksize dimensions + # and explode self and other to match. + other = self.__class__(other, blocksize=self.blocksize) + + # e.g. bsr_plus_bsr, etc. + fn = getattr(_sparsetools, self.format + op + self.format) + + R,C = self.blocksize + + max_bnnz = len(self.data) + len(other.data) + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=max_bnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(max_bnnz, dtype=idx_dtype) + + bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] + if op in bool_ops: + data = np.empty(R*C*max_bnnz, dtype=np.bool_) + else: + data = np.empty(R*C*max_bnnz, dtype=upcast(self.dtype,other.dtype)) + + fn(self.shape[0]//R, self.shape[1]//C, R, C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype), + np.ravel(other.data), + indptr, + indices, + data) + + actual_bnnz = indptr[-1] + indices = indices[:actual_bnnz] + data = data[:R*C*actual_bnnz] + + if actual_bnnz < max_bnnz/2: + indices = indices.copy() + data = data.copy() + + data = data.reshape(-1,R,C) + + return self.__class__((data, indices, indptr), shape=self.shape) + + # needed by _data_matrix + def _with_data(self,data,copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays + (i.e. .indptr and .indices) are copied. + """ + if copy: + return self.__class__((data,self.indices.copy(),self.indptr.copy()), + shape=self.shape,dtype=data.dtype) + else: + return self.__class__((data,self.indices,self.indptr), + shape=self.shape,dtype=data.dtype) + +# # these functions are used by the parent class +# # to remove redundancy between bsc_matrix and bsr_matrix +# def _swap(self,x): +# """swap the members of x if this is a column-oriented matrix +# """ +# return (x[0],x[1]) + + +def isspmatrix_bsr(x): + """Is `x` of a bsr_matrix type? + + Parameters + ---------- + x + object to check for being a bsr matrix + + Returns + ------- + bool + True if `x` is a bsr matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import bsr_array, bsr_matrix, csr_matrix, isspmatrix_bsr + >>> isspmatrix_bsr(bsr_matrix([[5]])) + True + >>> isspmatrix_bsr(bsr_array([[5]])) + False + >>> isspmatrix_bsr(csr_matrix([[5]])) + False + """ + return isinstance(x, bsr_matrix) + + +# This namespace class separates array from matrix with isinstance +class bsr_array(_bsr_base, sparray): + """ + Block Sparse Row format sparse array. + + This can be instantiated in several ways: + bsr_array(D, [blocksize=(R,C)]) + where D is a 2-D ndarray. + + bsr_array(S, [blocksize=(R,C)]) + with another sparse array or matrix S (equivalent to S.tobsr()) + + bsr_array((M, N), [blocksize=(R,C), dtype]) + to construct an empty sparse array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + bsr_array((data, ij), [blocksize=(R,C), shape=(M, N)]) + where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]`` + + bsr_array((data, indices, indptr), [shape=(M, N)]) + is the standard BSR representation where the block column + indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding block values are stored in + ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not + supplied, the array dimensions are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + BSR format data array of the array + indices + BSR format index array of the array + indptr + BSR format index pointer array of the array + blocksize + Block size + has_sorted_indices : bool + Whether indices are sorted + has_canonical_format : bool + T + + Notes + ----- + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + **Summary of BSR format** + + The Block Sparse Row (BSR) format is very similar to the Compressed + Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense + sub matrices like the last example below. Such sparse block matrices often + arise in vector-valued finite element discretizations. In such cases, BSR is + considerably more efficient than CSR and CSC for many sparse arithmetic + operations. + + **Blocksize** + + The blocksize (R,C) must evenly divide the shape of the sparse array (M,N). + That is, R and C must satisfy the relationship ``M % R = 0`` and + ``N % C = 0``. + + If no blocksize is specified, a simple heuristic is applied to determine + an appropriate blocksize. + + **Canonical Format** + + In canonical format, there are no duplicate blocks and indices are sorted + per row. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import bsr_array + >>> bsr_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3 ,4, 5, 6]) + >>> bsr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2) + >>> bsr_array((data,indices,indptr), shape=(6, 6)).toarray() + array([[1, 1, 0, 0, 2, 2], + [1, 1, 0, 0, 2, 2], + [0, 0, 0, 0, 3, 3], + [0, 0, 0, 0, 3, 3], + [4, 4, 5, 5, 6, 6], + [4, 4, 5, 5, 6, 6]]) + + """ + + +class bsr_matrix(spmatrix, _bsr_base): + """ + Block Sparse Row format sparse matrix. + + This can be instantiated in several ways: + bsr_matrix(D, [blocksize=(R,C)]) + where D is a 2-D ndarray. + + bsr_matrix(S, [blocksize=(R,C)]) + with another sparse array or matrix S (equivalent to S.tobsr()) + + bsr_matrix((M, N), [blocksize=(R,C), dtype]) + to construct an empty sparse matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + bsr_matrix((data, ij), [blocksize=(R,C), shape=(M, N)]) + where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]`` + + bsr_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard BSR representation where the block column + indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding block values are stored in + ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not + supplied, the matrix dimensions are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + BSR format data array of the matrix + indices + BSR format index array of the matrix + indptr + BSR format index pointer array of the matrix + blocksize + Block size + has_sorted_indices : bool + Whether indices are sorted + has_canonical_format : bool + T + + Notes + ----- + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + **Summary of BSR format** + + The Block Sparse Row (BSR) format is very similar to the Compressed + Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense + sub matrices like the last example below. Such sparse block matrices often + arise in vector-valued finite element discretizations. In such cases, BSR is + considerably more efficient than CSR and CSC for many sparse arithmetic + operations. + + **Blocksize** + + The blocksize (R,C) must evenly divide the shape of the sparse matrix (M,N). + That is, R and C must satisfy the relationship ``M % R = 0`` and + ``N % C = 0``. + + If no blocksize is specified, a simple heuristic is applied to determine + an appropriate blocksize. + + **Canonical Format** + + In canonical format, there are no duplicate blocks and indices are sorted + per row. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import bsr_matrix + >>> bsr_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3 ,4, 5, 6]) + >>> bsr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2) + >>> bsr_matrix((data,indices,indptr), shape=(6, 6)).toarray() + array([[1, 1, 0, 0, 2, 2], + [1, 1, 0, 0, 2, 2], + [0, 0, 0, 0, 3, 3], + [0, 0, 0, 0, 3, 3], + [4, 4, 5, 5, 6, 6], + [4, 4, 5, 5, 6, 6]]) + + """ + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_compressed.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_compressed.py new file mode 100644 index 0000000000000000000000000000000000000000..d8ae861c204001859644681e1d8fa5ce5bbaf4bd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_compressed.py @@ -0,0 +1,1372 @@ +"""Base class for sparse matrix formats using compressed storage.""" +__all__ = [] + +from warnings import warn +import operator + +import numpy as np +from scipy._lib._util import _prune_array, copy_if_needed + +from ._base import _spbase, issparse, SparseEfficiencyWarning, sparray +from ._data import _data_matrix, _minmax_mixin +from . import _sparsetools +from ._sparsetools import (get_csr_submatrix, csr_sample_offsets, csr_todense, + csr_sample_values, csr_row_index, csr_row_slice, + csr_column_index1, csr_column_index2) +from ._index import IndexMixin +from ._sputils import (upcast, upcast_char, to_native, isdense, isshape, + getdtype, isscalarlike, isintlike, downcast_intp_index, + get_sum_dtype, check_shape, is_pydata_spmatrix) + + +class _cs_matrix(_data_matrix, _minmax_mixin, IndexMixin): + """ + base array/matrix class for compressed row- and column-oriented arrays/matrices + """ + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self) + + if issparse(arg1): + if arg1.format == self.format and copy: + arg1 = arg1.copy() + else: + arg1 = arg1.asformat(self.format) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + elif isinstance(arg1, tuple): + if isshape(arg1): + # It's a tuple of matrix dimensions (M, N) + # create empty matrix + self._shape = check_shape(arg1) + M, N = self.shape + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + idx_dtype = self._get_index_dtype(maxval=max(M, N)) + self.data = np.zeros(0, getdtype(dtype, default=float)) + self.indices = np.zeros(0, idx_dtype) + self.indptr = np.zeros(self._swap((M, N))[0] + 1, + dtype=idx_dtype) + else: + if len(arg1) == 2: + # (data, ij) format + coo = self._coo_container(arg1, shape=shape, dtype=dtype) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, self._shape = arrays + self.sum_duplicates() + elif len(arg1) == 3: + # (data, indices, indptr) format + (data, indices, indptr) = arg1 + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + maxval = None + if shape is not None: + maxval = max(shape) + idx_dtype = self._get_index_dtype((indices, indptr), + maxval=maxval, + check_contents=True) + + if not copy: + copy = copy_if_needed + self.indices = np.array(indices, copy=copy, dtype=idx_dtype) + self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype) + self.data = np.array(data, copy=copy, dtype=dtype) + else: + raise ValueError(f"unrecognized {self.format}_matrix " + "constructor usage") + + else: + # must be dense + try: + arg1 = np.asarray(arg1) + except Exception as e: + msg = f"unrecognized {self.format}_matrix constructor usage" + raise ValueError(msg) from e + if isinstance(self, sparray) and arg1.ndim < 2 and self.format == "csc": + raise ValueError( + f"CSC arrays don't support {arg1.ndim}D input. Use 2D" + ) + coo = self._coo_container(arg1, dtype=dtype) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, self._shape = arrays + + # Read matrix dimensions given, if any + if shape is not None: + self._shape = check_shape(shape) + else: + if self.shape is None: + # shape not already set, try to infer dimensions + try: + major_dim = len(self.indptr) - 1 + minor_dim = self.indices.max() + 1 + except Exception as e: + raise ValueError('unable to infer matrix dimensions') from e + else: + self._shape = check_shape(self._swap((major_dim, minor_dim))) + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self.check_format(full_check=False) + + def _getnnz(self, axis=None): + if axis is None: + return int(self.indptr[-1]) + else: + if axis < 0: + axis += 2 + axis, _ = self._swap((axis, 1 - axis)) + _, N = self._swap(self.shape) + if axis == 0: + return np.bincount(downcast_intp_index(self.indices), + minlength=N) + elif axis == 1: + return np.diff(self.indptr) + raise ValueError('axis out of bounds') + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def check_format(self, full_check=True): + """Check whether the array/matrix respects the CSR or CSC format. + + Parameters + ---------- + full_check : bool, optional + If `True`, run rigorous check, scanning arrays for valid values. + Note that activating those check might copy arrays for casting, + modifying indices and index pointers' inplace. + If `False`, run basic checks on attributes. O(1) operations. + Default is `True`. + """ + # use _swap to determine proper bounds + major_name, minor_name = self._swap(('row', 'column')) + major_dim, minor_dim = self._swap(self.shape) + + # index arrays should have integer data types + if self.indptr.dtype.kind != 'i': + warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})", + stacklevel=3) + if self.indices.dtype.kind != 'i': + warn(f"indices array has non-integer dtype ({self.indices.dtype.name})", + stacklevel=3) + + # check array shapes + for x in [self.data.ndim, self.indices.ndim, self.indptr.ndim]: + if x != 1: + raise ValueError('data, indices, and indptr should be 1-D') + + # check index pointer + if (len(self.indptr) != major_dim + 1): + raise ValueError("index pointer size ({}) should be ({})" + "".format(len(self.indptr), major_dim + 1)) + if (self.indptr[0] != 0): + raise ValueError("index pointer should start with 0") + + # check index and data arrays + if (len(self.indices) != len(self.data)): + raise ValueError("indices and data should have the same size") + if (self.indptr[-1] > len(self.indices)): + raise ValueError("Last value of index pointer should be less than " + "the size of index and data arrays") + + self.prune() + + if full_check: + # check format validity (more expensive) + if self.nnz > 0: + if self.indices.max() >= minor_dim: + raise ValueError(f"{minor_name} index values must be < {minor_dim}") + if self.indices.min() < 0: + raise ValueError(f"{minor_name} index values must be >= 0") + if np.diff(self.indptr).min() < 0: + raise ValueError("index pointer values must form a " + "non-decreasing sequence") + + idx_dtype = self._get_index_dtype((self.indptr, self.indices)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + self.data = to_native(self.data) + + # if not self.has_sorted_indices(): + # warn('Indices were not in sorted order. Sorting indices.') + # self.sort_indices() + # assert(self.has_sorted_indices()) + # TODO check for duplicates? + + ####################### + # Boolean comparisons # + ####################### + + def _scalar_binopt(self, other, op): + """Scalar version of self._binopt, for cases in which no new nonzeros + are added. Produces a new sparse array in canonical form. + """ + self.sum_duplicates() + res = self._with_data(op(self.data, other), copy=True) + res.eliminate_zeros() + return res + + def __eq__(self, other): + # Scalar other. + if isscalarlike(other): + if np.isnan(other): + return self.__class__(self.shape, dtype=np.bool_) + + if other == 0: + warn("Comparing a sparse matrix with 0 using == is inefficient" + ", try using != instead.", SparseEfficiencyWarning, + stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + inv = self._scalar_binopt(other, operator.ne) + return all_true - inv + else: + return self._scalar_binopt(other, operator.eq) + # Dense other. + elif isdense(other): + return self.todense() == other + # Pydata sparse other. + elif is_pydata_spmatrix(other): + return NotImplemented + # Sparse other. + elif issparse(other): + warn("Comparing sparse matrices using == is inefficient, try using" + " != instead.", SparseEfficiencyWarning, stacklevel=3) + # TODO sparse broadcasting + if self.shape != other.shape: + return False + elif self.format != other.format: + other = other.asformat(self.format) + res = self._binopt(other, '_ne_') + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + return all_true - res + else: + return NotImplemented + + def __ne__(self, other): + # Scalar other. + if isscalarlike(other): + if np.isnan(other): + warn("Comparing a sparse matrix with nan using != is" + " inefficient", SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + return all_true + elif other != 0: + warn("Comparing a sparse matrix with a nonzero scalar using !=" + " is inefficient, try using == instead.", + SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape), dtype=np.bool_) + inv = self._scalar_binopt(other, operator.eq) + return all_true - inv + else: + return self._scalar_binopt(other, operator.ne) + # Dense other. + elif isdense(other): + return self.todense() != other + # Pydata sparse other. + elif is_pydata_spmatrix(other): + return NotImplemented + # Sparse other. + elif issparse(other): + # TODO sparse broadcasting + if self.shape != other.shape: + return True + elif self.format != other.format: + other = other.asformat(self.format) + return self._binopt(other, '_ne_') + else: + return NotImplemented + + def _inequality(self, other, op, op_name, bad_scalar_msg): + # Scalar other. + if isscalarlike(other): + if 0 == other and op_name in ('_le_', '_ge_'): + raise NotImplementedError(" >= and <= don't work with 0.") + elif op(0, other): + warn(bad_scalar_msg, SparseEfficiencyWarning, stacklevel=3) + other_arr = np.empty(self.shape, dtype=np.result_type(other)) + other_arr.fill(other) + other_arr = self.__class__(other_arr) + return self._binopt(other_arr, op_name) + else: + return self._scalar_binopt(other, op) + # Dense other. + elif isdense(other): + return op(self.todense(), other) + # Sparse other. + elif issparse(other): + # TODO sparse broadcasting + if self.shape != other.shape: + raise ValueError("inconsistent shapes") + elif self.format != other.format: + other = other.asformat(self.format) + if op_name not in ('_ge_', '_le_'): + return self._binopt(other, op_name) + + warn("Comparing sparse matrices using >= and <= is inefficient, " + "using <, >, or !=, instead.", + SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + res = self._binopt(other, '_gt_' if op_name == '_le_' else '_lt_') + return all_true - res + else: + return NotImplemented + + def __lt__(self, other): + return self._inequality(other, operator.lt, '_lt_', + "Comparing a sparse matrix with a scalar " + "greater than zero using < is inefficient, " + "try using >= instead.") + + def __gt__(self, other): + return self._inequality(other, operator.gt, '_gt_', + "Comparing a sparse matrix with a scalar " + "less than zero using > is inefficient, " + "try using <= instead.") + + def __le__(self, other): + return self._inequality(other, operator.le, '_le_', + "Comparing a sparse matrix with a scalar " + "greater than zero using <= is inefficient, " + "try using > instead.") + + def __ge__(self, other): + return self._inequality(other, operator.ge, '_ge_', + "Comparing a sparse matrix with a scalar " + "less than zero using >= is inefficient, " + "try using < instead.") + + ################################# + # Arithmetic operator overrides # + ################################# + + def _add_dense(self, other): + if other.shape != self.shape: + raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})') + dtype = upcast_char(self.dtype.char, other.dtype.char) + order = self._swap('CF')[0] + result = np.array(other, dtype=dtype, order=order, copy=True) + M, N = self._swap(self.shape) + y = result if result.flags.c_contiguous else result.T + csr_todense(M, N, self.indptr, self.indices, self.data, y) + return self._container(result, copy=False) + + def _add_sparse(self, other): + return self._binopt(other, '_plus_') + + def _sub_sparse(self, other): + return self._binopt(other, '_minus_') + + def multiply(self, other): + """Point-wise multiplication by another array/matrix, vector, or + scalar. + """ + # Scalar multiplication. + if isscalarlike(other): + return self._mul_scalar(other) + # Sparse matrix or vector. + if issparse(other): + if self.shape == other.shape: + other = self.__class__(other) + return self._binopt(other, '_elmul_') + if other.ndim == 1: + raise TypeError("broadcast from a 1d array not yet supported") + # Single element. + elif other.shape == (1, 1): + return self._mul_scalar(other.toarray()[0, 0]) + elif self.shape == (1, 1): + return other._mul_scalar(self.toarray()[0, 0]) + # A row times a column. + elif self.shape[1] == 1 and other.shape[0] == 1: + return self._matmul_sparse(other.tocsc()) + elif self.shape[0] == 1 and other.shape[1] == 1: + return other._matmul_sparse(self.tocsc()) + # Row vector times matrix. other is a row. + elif other.shape[0] == 1 and self.shape[1] == other.shape[1]: + other = self._dia_container( + (other.toarray().ravel(), [0]), + shape=(other.shape[1], other.shape[1]) + ) + return self._matmul_sparse(other) + # self is a row. + elif self.shape[0] == 1 and self.shape[1] == other.shape[1]: + copy = self._dia_container( + (self.toarray().ravel(), [0]), + shape=(self.shape[1], self.shape[1]) + ) + return other._matmul_sparse(copy) + # Column vector times matrix. other is a column. + elif other.shape[1] == 1 and self.shape[0] == other.shape[0]: + other = self._dia_container( + (other.toarray().ravel(), [0]), + shape=(other.shape[0], other.shape[0]) + ) + return other._matmul_sparse(self) + # self is a column. + elif self.shape[1] == 1 and self.shape[0] == other.shape[0]: + copy = self._dia_container( + (self.toarray().ravel(), [0]), + shape=(self.shape[0], self.shape[0]) + ) + return copy._matmul_sparse(other) + else: + raise ValueError("inconsistent shapes") + + # Assume other is a dense matrix/array, which produces a single-item + # object array if other isn't convertible to ndarray. + other = np.atleast_2d(other) + + if other.ndim != 2: + return np.multiply(self.toarray(), other) + # Single element / wrapped object. + if other.size == 1: + if other.dtype == np.object_: + # 'other' not convertible to ndarray. + return NotImplemented + return self._mul_scalar(other.flat[0]) + # Fast case for trivial sparse matrix. + elif self.shape == (1, 1): + return np.multiply(self.toarray()[0, 0], other) + + ret = self.tocoo() + # Matching shapes. + if self.shape == other.shape: + data = np.multiply(ret.data, other[ret.row, ret.col]) + # Sparse row vector times... + elif self.shape[0] == 1: + if other.shape[1] == 1: # Dense column vector. + data = np.multiply(ret.data, other) + elif other.shape[1] == self.shape[1]: # Dense matrix. + data = np.multiply(ret.data, other[:, ret.col]) + else: + raise ValueError("inconsistent shapes") + row = np.repeat(np.arange(other.shape[0]), len(ret.row)) + col = np.tile(ret.col, other.shape[0]) + return self._coo_container( + (data.view(np.ndarray).ravel(), (row, col)), + shape=(other.shape[0], self.shape[1]), + copy=False + ) + # Sparse column vector times... + elif self.shape[1] == 1: + if other.shape[0] == 1: # Dense row vector. + data = np.multiply(ret.data[:, None], other) + elif other.shape[0] == self.shape[0]: # Dense matrix. + data = np.multiply(ret.data[:, None], other[ret.row]) + else: + raise ValueError("inconsistent shapes") + row = np.repeat(ret.row, other.shape[1]) + col = np.tile(np.arange(other.shape[1]), len(ret.col)) + return self._coo_container( + (data.view(np.ndarray).ravel(), (row, col)), + shape=(self.shape[0], other.shape[1]), + copy=False + ) + # Sparse matrix times dense row vector. + elif other.shape[0] == 1 and self.shape[1] == other.shape[1]: + data = np.multiply(ret.data, other[:, ret.col].ravel()) + # Sparse matrix times dense column vector. + elif other.shape[1] == 1 and self.shape[0] == other.shape[0]: + data = np.multiply(ret.data, other[ret.row].ravel()) + else: + raise ValueError("inconsistent shapes") + ret.data = data.view(np.ndarray).ravel() + return ret + + ########################### + # Multiplication handlers # + ########################### + + def _matmul_vector(self, other): + M, N = self.shape + + # output array + result = np.zeros(M, dtype=upcast_char(self.dtype.char, + other.dtype.char)) + + # csr_matvec or csc_matvec + fn = getattr(_sparsetools, self.format + '_matvec') + fn(M, N, self.indptr, self.indices, self.data, other, result) + + return result + + def _matmul_multivector(self, other): + M, N = self.shape + n_vecs = other.shape[1] # number of column vectors + + result = np.zeros((M, n_vecs), + dtype=upcast_char(self.dtype.char, other.dtype.char)) + + # csr_matvecs or csc_matvecs + fn = getattr(_sparsetools, self.format + '_matvecs') + fn(M, N, n_vecs, self.indptr, self.indices, self.data, + other.ravel(), result.ravel()) + + return result + + def _matmul_sparse(self, other): + M, K1 = self.shape + K2, N = other.shape + + major_axis = self._swap((M, N))[0] + other = self.__class__(other) # convert to this format + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices)) + + fn = getattr(_sparsetools, self.format + '_matmat_maxnnz') + nnz = fn(M, N, + np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=nnz) + + indptr = np.empty(major_axis + 1, dtype=idx_dtype) + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=upcast(self.dtype, other.dtype)) + + fn = getattr(_sparsetools, self.format + '_matmat') + fn(M, N, np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + self.data, + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype), + other.data, + indptr, indices, data) + + return self.__class__((data, indices, indptr), shape=(M, N)) + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + fn = getattr(_sparsetools, self.format + "_diagonal") + y = np.empty(min(rows + min(k, 0), cols - max(k, 0)), + dtype=upcast(self.dtype)) + fn(k, self.shape[0], self.shape[1], self.indptr, self.indices, + self.data, y) + return y + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + ##################### + # Other binary ops # + ##################### + + def _maximum_minimum(self, other, npop, op_name, dense_check): + if isscalarlike(other): + if dense_check(other): + warn("Taking maximum (minimum) with > 0 (< 0) number results" + " to a dense matrix.", SparseEfficiencyWarning, + stacklevel=3) + other_arr = np.empty(self.shape, dtype=np.asarray(other).dtype) + other_arr.fill(other) + other_arr = self.__class__(other_arr) + return self._binopt(other_arr, op_name) + else: + self.sum_duplicates() + new_data = npop(self.data, np.asarray(other)) + mat = self.__class__((new_data, self.indices, self.indptr), + dtype=new_data.dtype, shape=self.shape) + return mat + elif isdense(other): + return npop(self.todense(), other) + elif issparse(other): + return self._binopt(other, op_name) + else: + raise ValueError("Operands not compatible.") + + def maximum(self, other): + return self._maximum_minimum(other, np.maximum, + '_maximum_', lambda x: np.asarray(x) > 0) + + maximum.__doc__ = _spbase.maximum.__doc__ + + def minimum(self, other): + return self._maximum_minimum(other, np.minimum, + '_minimum_', lambda x: np.asarray(x) < 0) + + minimum.__doc__ = _spbase.minimum.__doc__ + + ##################### + # Reduce operations # + ##################### + + def sum(self, axis=None, dtype=None, out=None): + """Sum the array/matrix over the given axis. If the axis is None, sum + over both rows and columns, returning a scalar. + """ + # The _spbase base class already does axis=0 and axis=1 efficiently + # so we only do the case axis=None here + if (not hasattr(self, 'blocksize') and + axis in self._swap(((1, -1), (0, 2)))[0]): + # faster than multiplication for large minor axis in CSC/CSR + res_dtype = get_sum_dtype(self.dtype) + ret = np.zeros(len(self.indptr) - 1, dtype=res_dtype) + + major_index, value = self._minor_reduce(np.add) + ret[major_index] = value + ret = self._ascontainer(ret) + if axis % 2 == 1: + ret = ret.T + + if out is not None and out.shape != ret.shape: + raise ValueError('dimensions do not match') + + return ret.sum(axis=(), dtype=dtype, out=out) + # _spbase will handle the remaining situations when axis + # is in {None, -1, 0, 1} + else: + return _spbase.sum(self, axis=axis, dtype=dtype, out=out) + + sum.__doc__ = _spbase.sum.__doc__ + + def _minor_reduce(self, ufunc, data=None): + """Reduce nonzeros with a ufunc over the minor axis when non-empty + + Can be applied to a function of self.data by supplying data parameter. + + Warning: this does not call sum_duplicates() + + Returns + ------- + major_index : array of ints + Major indices where nonzero + + value : array of self.dtype + Reduce result for nonzeros in each major_index + """ + if data is None: + data = self.data + major_index = np.flatnonzero(np.diff(self.indptr)) + value = ufunc.reduceat(data, + downcast_intp_index(self.indptr[major_index])) + return major_index, value + + ####################### + # Getting and Setting # + ####################### + + def _get_intXint(self, row, col): + M, N = self._swap(self.shape) + major, minor = self._swap((row, col)) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, + major, major + 1, minor, minor + 1) + return data.sum(dtype=self.dtype) + + def _get_sliceXslice(self, row, col): + major, minor = self._swap((row, col)) + if major.step in (1, None) and minor.step in (1, None): + return self._get_submatrix(major, minor, copy=True) + return self._major_slice(major)._minor_slice(minor) + + def _get_arrayXarray(self, row, col): + # inner indexing + idx_dtype = self.indices.dtype + M, N = self._swap(self.shape) + major, minor = self._swap((row, col)) + major = np.asarray(major, dtype=idx_dtype) + minor = np.asarray(minor, dtype=idx_dtype) + + val = np.empty(major.size, dtype=self.dtype) + csr_sample_values(M, N, self.indptr, self.indices, self.data, + major.size, major.ravel(), minor.ravel(), val) + if major.ndim == 1: + return self._ascontainer(val) + return self.__class__(val.reshape(major.shape)) + + def _get_columnXarray(self, row, col): + # outer indexing + major, minor = self._swap((row, col)) + return self._major_index_fancy(major)._minor_index_fancy(minor) + + def _major_index_fancy(self, idx): + """Index along the major axis where idx is an array of ints. + """ + idx_dtype = self._get_index_dtype((self.indptr, self.indices)) + indices = np.asarray(idx, dtype=idx_dtype).ravel() + + _, N = self._swap(self.shape) + M = len(indices) + new_shape = self._swap((M, N)) + if M == 0: + return self.__class__(new_shape, dtype=self.dtype) + + row_nnz = (self.indptr[indices + 1] - self.indptr[indices]).astype(idx_dtype) + + res_indptr = np.zeros(M+1, dtype=idx_dtype) + np.cumsum(row_nnz, out=res_indptr[1:]) + + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_row_index( + M, + indices, + self.indptr.astype(idx_dtype, copy=False), + self.indices.astype(idx_dtype, copy=False), + self.data, + res_indices, + res_data + ) + + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _major_slice(self, idx, copy=False): + """Index along the major axis where idx is a slice object. + """ + if idx == slice(None): + return self.copy() if copy else self + + M, N = self._swap(self.shape) + start, stop, step = idx.indices(M) + M = len(range(start, stop, step)) + new_shape = self._swap((M, N)) + if M == 0: + return self.__class__(new_shape, dtype=self.dtype) + + # Work out what slices are needed for `row_nnz` + # start,stop can be -1, only if step is negative + start0, stop0 = start, stop + if stop == -1 and start >= 0: + stop0 = None + start1, stop1 = start + 1, stop + 1 + + row_nnz = self.indptr[start1:stop1:step] - \ + self.indptr[start0:stop0:step] + idx_dtype = self.indices.dtype + res_indptr = np.zeros(M+1, dtype=idx_dtype) + np.cumsum(row_nnz, out=res_indptr[1:]) + + if step == 1: + all_idx = slice(self.indptr[start], self.indptr[stop]) + res_indices = np.array(self.indices[all_idx], copy=copy) + res_data = np.array(self.data[all_idx], copy=copy) + else: + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_row_slice(start, stop, step, self.indptr, self.indices, + self.data, res_indices, res_data) + + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _minor_index_fancy(self, idx): + """Index along the minor axis where idx is an array of ints. + """ + idx_dtype = self._get_index_dtype((self.indices, self.indptr)) + indices = self.indices.astype(idx_dtype, copy=False) + indptr = self.indptr.astype(idx_dtype, copy=False) + + idx = np.asarray(idx, dtype=idx_dtype).ravel() + + M, N = self._swap(self.shape) + k = len(idx) + new_shape = self._swap((M, k)) + if k == 0: + return self.__class__(new_shape, dtype=self.dtype) + + # pass 1: count idx entries and compute new indptr + col_offsets = np.zeros(N, dtype=idx_dtype) + res_indptr = np.empty_like(self.indptr, dtype=idx_dtype) + csr_column_index1( + k, + idx, + M, + N, + indptr, + indices, + col_offsets, + res_indptr, + ) + + # pass 2: copy indices/data for selected idxs + col_order = np.argsort(idx).astype(idx_dtype, copy=False) + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_column_index2(col_order, col_offsets, len(self.indices), + indices, self.data, res_indices, res_data) + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _minor_slice(self, idx, copy=False): + """Index along the minor axis where idx is a slice object. + """ + if idx == slice(None): + return self.copy() if copy else self + + M, N = self._swap(self.shape) + start, stop, step = idx.indices(N) + N = len(range(start, stop, step)) + if N == 0: + return self.__class__(self._swap((M, N)), dtype=self.dtype) + if step == 1: + return self._get_submatrix(minor=idx, copy=copy) + # TODO: don't fall back to fancy indexing here + return self._minor_index_fancy(np.arange(start, stop, step)) + + def _get_submatrix(self, major=None, minor=None, copy=False): + """Return a submatrix of this matrix. + + major, minor: None, int, or slice with step 1 + """ + M, N = self._swap(self.shape) + i0, i1 = _process_slice(major, M) + j0, j1 = _process_slice(minor, N) + + if i0 == 0 and j0 == 0 and i1 == M and j1 == N: + return self.copy() if copy else self + + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, i0, i1, j0, j1) + + shape = self._swap((i1 - i0, j1 - j0)) + return self.__class__((data, indices, indptr), shape=shape, + dtype=self.dtype, copy=False) + + def _set_intXint(self, row, col, x): + i, j = self._swap((row, col)) + self._set_many(i, j, x) + + def _set_arrayXarray(self, row, col, x): + i, j = self._swap((row, col)) + self._set_many(i, j, x) + + def _set_arrayXarray_sparse(self, row, col, x): + # clear entries that will be overwritten + self._zero_many(*self._swap((row, col))) + + M, N = row.shape # matches col.shape + broadcast_row = M != 1 and x.shape[0] == 1 + broadcast_col = N != 1 and x.shape[1] == 1 + r, c = x.row, x.col + + x = np.asarray(x.data, dtype=self.dtype) + if x.size == 0: + return + + if broadcast_row: + r = np.repeat(np.arange(M), len(r)) + c = np.tile(c, M) + x = np.tile(x, M) + if broadcast_col: + r = np.repeat(r, N) + c = np.tile(np.arange(N), len(c)) + x = np.repeat(x, N) + # only assign entries in the new sparsity structure + i, j = self._swap((row[r, c], col[r, c])) + self._set_many(i, j, x) + + def _setdiag(self, values, k): + if 0 in self.shape: + return + + M, N = self.shape + broadcast = (values.ndim == 0) + + if k < 0: + if broadcast: + max_index = min(M + k, N) + else: + max_index = min(M + k, N, len(values)) + i = np.arange(-k, max_index - k, dtype=self.indices.dtype) + j = np.arange(max_index, dtype=self.indices.dtype) + + else: + if broadcast: + max_index = min(M, N - k) + else: + max_index = min(M, N - k, len(values)) + i = np.arange(max_index, dtype=self.indices.dtype) + j = np.arange(k, k + max_index, dtype=self.indices.dtype) + + if not broadcast: + values = values[:len(i)] + + x = np.atleast_1d(np.asarray(values, dtype=self.dtype)).ravel() + if x.squeeze().shape != i.squeeze().shape: + x = np.broadcast_to(x, i.shape) + if x.size == 0: + return + + M, N = self._swap((M, N)) + i, j = self._swap((i, j)) + n_samples = x.size + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if -1 not in offsets: + # only affects existing non-zero cells + self.data[offsets] = x + return + + mask = (offsets <= -1) + # Boundary between csc and convert to coo + # The value 0.001 is justified in gh-19962#issuecomment-1920499678 + if mask.sum() < self.nnz * 0.001: + # create new entries + i = i[mask] + j = j[mask] + self._insert_many(i, j, x[mask]) + # replace existing entries + mask = ~mask + self.data[offsets[mask]] = x[mask] + else: + # convert to coo for _set_diag + coo = self.tocoo() + coo._setdiag(values, k) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, _ = arrays + + def _prepare_indices(self, i, j): + M, N = self._swap(self.shape) + + def check_bounds(indices, bound): + idx = indices.max() + if idx >= bound: + raise IndexError('index (%d) out of range (>= %d)' % + (idx, bound)) + idx = indices.min() + if idx < -bound: + raise IndexError('index (%d) out of range (< -%d)' % + (idx, bound)) + + i = np.atleast_1d(np.asarray(i, dtype=self.indices.dtype)).ravel() + j = np.atleast_1d(np.asarray(j, dtype=self.indices.dtype)).ravel() + check_bounds(i, M) + check_bounds(j, N) + return i, j, M, N + + def _set_many(self, i, j, x): + """Sets value at each (i, j) to x + + Here (i,j) index major and minor respectively, and must not contain + duplicate entries. + """ + i, j, M, N = self._prepare_indices(i, j) + x = np.atleast_1d(np.asarray(x, dtype=self.dtype)).ravel() + + n_samples = x.size + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + + if -1 not in offsets: + # only affects existing non-zero cells + self.data[offsets] = x + return + + else: + warn("Changing the sparsity structure of a {}_matrix is expensive." + " lil_matrix is more efficient.".format(self.format), + SparseEfficiencyWarning, stacklevel=3) + # replace where possible + mask = offsets > -1 + self.data[offsets[mask]] = x[mask] + # only insertions remain + mask = ~mask + i = i[mask] + i[i < 0] += M + j = j[mask] + j[j < 0] += N + self._insert_many(i, j, x[mask]) + + def _zero_many(self, i, j): + """Sets value at each (i, j) to zero, preserving sparsity structure. + + Here (i,j) index major and minor respectively. + """ + i, j, M, N = self._prepare_indices(i, j) + + n_samples = len(i) + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + + # only assign zeros to the existing sparsity structure + self.data[offsets[offsets > -1]] = 0 + + def _insert_many(self, i, j, x): + """Inserts new nonzero at each (i, j) with value x + + Here (i,j) index major and minor respectively. + i, j and x must be non-empty, 1d arrays. + Inserts each major group (e.g. all entries per row) at a time. + Maintains has_sorted_indices property. + Modifies i, j, x in place. + """ + order = np.argsort(i, kind='mergesort') # stable for duplicates + i = i.take(order, mode='clip') + j = j.take(order, mode='clip') + x = x.take(order, mode='clip') + + do_sort = self.has_sorted_indices + + # Update index data type + idx_dtype = self._get_index_dtype((self.indices, self.indptr), + maxval=(self.indptr[-1] + x.size)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + i = np.asarray(i, dtype=idx_dtype) + j = np.asarray(j, dtype=idx_dtype) + + # Collate old and new in chunks by major index + indices_parts = [] + data_parts = [] + ui, ui_indptr = np.unique(i, return_index=True) + ui_indptr = np.append(ui_indptr, len(j)) + new_nnzs = np.diff(ui_indptr) + prev = 0 + for c, (ii, js, je) in enumerate(zip(ui, ui_indptr, ui_indptr[1:])): + # old entries + start = self.indptr[prev] + stop = self.indptr[ii] + indices_parts.append(self.indices[start:stop]) + data_parts.append(self.data[start:stop]) + + # handle duplicate j: keep last setting + uj, uj_indptr = np.unique(j[js:je][::-1], return_index=True) + if len(uj) == je - js: + indices_parts.append(j[js:je]) + data_parts.append(x[js:je]) + else: + indices_parts.append(j[js:je][::-1][uj_indptr]) + data_parts.append(x[js:je][::-1][uj_indptr]) + new_nnzs[c] = len(uj) + + prev = ii + + # remaining old entries + start = self.indptr[ii] + indices_parts.append(self.indices[start:]) + data_parts.append(self.data[start:]) + + # update attributes + self.indices = np.concatenate(indices_parts) + self.data = np.concatenate(data_parts) + nnzs = np.empty(self.indptr.shape, dtype=idx_dtype) + nnzs[0] = idx_dtype(0) + indptr_diff = np.diff(self.indptr) + indptr_diff[ui] += new_nnzs + nnzs[1:] = indptr_diff + self.indptr = np.cumsum(nnzs, out=nnzs) + + if do_sort: + # TODO: only sort where necessary + self.has_sorted_indices = False + self.sort_indices() + + self.check_format(full_check=False) + + ###################### + # Conversion methods # + ###################### + + def tocoo(self, copy=True): + major_dim, minor_dim = self._swap(self.shape) + minor_indices = self.indices + major_indices = np.empty(len(minor_indices), dtype=self.indices.dtype) + _sparsetools.expandptr(major_dim, self.indptr, major_indices) + coords = self._swap((major_indices, minor_indices)) + + return self._coo_container( + (self.data, coords), self.shape, copy=copy, dtype=self.dtype + ) + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def toarray(self, order=None, out=None): + if out is None and order is None: + order = self._swap('cf')[0] + out = self._process_toarray_args(order, out) + if not (out.flags.c_contiguous or out.flags.f_contiguous): + raise ValueError('Output array must be C or F contiguous') + # align ideal order with output array order + if out.flags.c_contiguous: + x = self.tocsr() + y = out + else: + x = self.tocsc() + y = out.T + M, N = x._swap(x.shape) + csr_todense(M, N, x.indptr, x.indices, x.data, y) + return out + + toarray.__doc__ = _spbase.toarray.__doc__ + + ############################################################## + # methods that examine or modify the internal data structure # + ############################################################## + + def eliminate_zeros(self): + """Remove zero entries from the array/matrix + + This is an *in place* operation. + """ + M, N = self._swap(self.shape) + _sparsetools.csr_eliminate_zeros(M, N, self.indptr, self.indices, + self.data) + self.prune() # nnz may have changed + + @property + def has_canonical_format(self) -> bool: + """Whether the array/matrix has sorted indices and no duplicates + + Returns + - True: if the above applies + - False: otherwise + + has_canonical_format implies has_sorted_indices, so if the latter flag + is False, so will the former be; if the former is found True, the + latter flag is also set. + """ + # first check to see if result was cached + if not getattr(self, '_has_sorted_indices', True): + # not sorted => not canonical + self._has_canonical_format = False + elif not hasattr(self, '_has_canonical_format'): + self.has_canonical_format = bool( + _sparsetools.csr_has_canonical_format( + len(self.indptr) - 1, self.indptr, self.indices) + ) + return self._has_canonical_format + + @has_canonical_format.setter + def has_canonical_format(self, val: bool): + self._has_canonical_format = bool(val) + if val: + self.has_sorted_indices = True + + def sum_duplicates(self): + """Eliminate duplicate entries by adding them together + + This is an *in place* operation. + """ + if self.has_canonical_format: + return + self.sort_indices() + + M, N = self._swap(self.shape) + _sparsetools.csr_sum_duplicates(M, N, self.indptr, self.indices, + self.data) + + self.prune() # nnz may have changed + self.has_canonical_format = True + + @property + def has_sorted_indices(self) -> bool: + """Whether the indices are sorted + + Returns + - True: if the indices of the array/matrix are in sorted order + - False: otherwise + """ + # first check to see if result was cached + if not hasattr(self, '_has_sorted_indices'): + self._has_sorted_indices = bool( + _sparsetools.csr_has_sorted_indices( + len(self.indptr) - 1, self.indptr, self.indices) + ) + return self._has_sorted_indices + + @has_sorted_indices.setter + def has_sorted_indices(self, val: bool): + self._has_sorted_indices = bool(val) + + + def sorted_indices(self): + """Return a copy of this array/matrix with sorted indices + """ + A = self.copy() + A.sort_indices() + return A + + # an alternative that has linear complexity is the following + # although the previous option is typically faster + # return self.toother().toother() + + def sort_indices(self): + """Sort the indices of this array/matrix *in place* + """ + + if not self.has_sorted_indices: + _sparsetools.csr_sort_indices(len(self.indptr) - 1, self.indptr, + self.indices, self.data) + self.has_sorted_indices = True + + def prune(self): + """Remove empty space after all non-zero elements. + """ + major_dim = self._swap(self.shape)[0] + + if len(self.indptr) != major_dim + 1: + raise ValueError('index pointer has invalid length') + if len(self.indices) < self.nnz: + raise ValueError('indices array has fewer than nnz elements') + if len(self.data) < self.nnz: + raise ValueError('data array has fewer than nnz elements') + + self.indices = _prune_array(self.indices[:self.nnz]) + self.data = _prune_array(self.data[:self.nnz]) + + def resize(self, *shape): + shape = check_shape(shape) + if hasattr(self, 'blocksize'): + bm, bn = self.blocksize + new_M, rm = divmod(shape[0], bm) + new_N, rn = divmod(shape[1], bn) + if rm or rn: + raise ValueError("shape must be divisible into {} blocks. " + "Got {}".format(self.blocksize, shape)) + M, N = self.shape[0] // bm, self.shape[1] // bn + else: + new_M, new_N = self._swap(shape) + M, N = self._swap(self.shape) + + if new_M < M: + self.indices = self.indices[:self.indptr[new_M]] + self.data = self.data[:self.indptr[new_M]] + self.indptr = self.indptr[:new_M + 1] + elif new_M > M: + self.indptr = np.resize(self.indptr, new_M + 1) + self.indptr[M + 1:].fill(self.indptr[M]) + + if new_N < N: + mask = self.indices < new_N + if not np.all(mask): + self.indices = self.indices[mask] + self.data = self.data[mask] + major_index, val = self._minor_reduce(np.add, mask) + self.indptr.fill(0) + self.indptr[1:][major_index] = val + np.cumsum(self.indptr, out=self.indptr) + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + ################### + # utility methods # + ################### + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays + (i.e. .indptr and .indices) are copied. + """ + if copy: + return self.__class__((data, self.indices.copy(), + self.indptr.copy()), + shape=self.shape, + dtype=data.dtype) + else: + return self.__class__((data, self.indices, self.indptr), + shape=self.shape, dtype=data.dtype) + + def _binopt(self, other, op): + """apply the binary operation fn to two sparse matrices.""" + other = self.__class__(other) + + # e.g. csr_plus_csr, csr_minus_csr, etc. + fn = getattr(_sparsetools, self.format + op + self.format) + + maxnnz = self.nnz + other.nnz + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=maxnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(maxnnz, dtype=idx_dtype) + + bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] + if op in bool_ops: + data = np.empty(maxnnz, dtype=np.bool_) + else: + data = np.empty(maxnnz, dtype=upcast(self.dtype, other.dtype)) + + fn(self.shape[0], self.shape[1], + np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + self.data, + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype), + other.data, + indptr, indices, data) + + A = self.__class__((data, indices, indptr), shape=self.shape) + A.prune() + + return A + + def _divide_sparse(self, other): + """ + Divide this matrix by a second sparse matrix. + """ + if other.shape != self.shape: + raise ValueError('inconsistent shapes') + + r = self._binopt(other, '_eldiv_') + + if np.issubdtype(r.dtype, np.inexact): + # Eldiv leaves entries outside the combined sparsity + # pattern empty, so they must be filled manually. + # Everything outside of other's sparsity is NaN, and everything + # inside it is either zero or defined by eldiv. + out = np.empty(self.shape, dtype=self.dtype) + out.fill(np.nan) + row, col = other.nonzero() + out[row, col] = 0 + r = r.tocoo() + out[r.row, r.col] = r.data + out = self._container(out) + else: + # integers types go with nan <-> 0 + out = r + + return out + + +def _process_slice(sl, num): + if sl is None: + i0, i1 = 0, num + elif isinstance(sl, slice): + i0, i1, stride = sl.indices(num) + if stride != 1: + raise ValueError('slicing with step != 1 not supported') + i0 = min(i0, i1) # give an empty slice when i0 > i1 + elif isintlike(sl): + if sl < 0: + sl += num + i0, i1 = sl, sl + 1 + if i0 < 0 or i1 > num: + raise IndexError('index out of bounds: 0 <= %d < %d <= %d' % + (i0, i1, num)) + else: + raise TypeError('expected slice or scalar') + + return i0, i1 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_construct.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_construct.py new file mode 100644 index 0000000000000000000000000000000000000000..6f5d3dd514d2684bfc6a53ae485c183e299f780f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_construct.py @@ -0,0 +1,1401 @@ +"""Functions to construct sparse matrices and arrays +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['spdiags', 'eye', 'identity', 'kron', 'kronsum', + 'hstack', 'vstack', 'bmat', 'rand', 'random', 'diags', 'block_diag', + 'diags_array', 'block_array', 'eye_array', 'random_array'] + +import numbers +import math +import numpy as np + +from scipy._lib._util import check_random_state, rng_integers +from ._sputils import upcast, get_index_dtype, isscalarlike + +from ._sparsetools import csr_hstack +from ._bsr import bsr_matrix, bsr_array +from ._coo import coo_matrix, coo_array +from ._csc import csc_matrix, csc_array +from ._csr import csr_matrix, csr_array +from ._dia import dia_matrix, dia_array + +from ._base import issparse, sparray + + +def spdiags(data, diags, m=None, n=None, format=None): + """ + Return a sparse matrix from diagonals. + + Parameters + ---------- + data : array_like + Matrix diagonals stored row-wise + diags : sequence of int or an int + Diagonals to set: + + * k = 0 the main diagonal + * k > 0 the kth upper diagonal + * k < 0 the kth lower diagonal + m, n : int, tuple, optional + Shape of the result. If `n` is None and `m` is a given tuple, + the shape is this tuple. If omitted, the matrix is square and + its shape is len(data[0]). + format : str, optional + Format of the result. By default (format=None) an appropriate sparse + matrix format is returned. This choice is subject to change. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``diags_array`` to take advantage + of the sparse array functionality. + + See Also + -------- + diags_array : more convenient form of this function + diags : matrix version of diags_array + dia_matrix : the sparse DIAgonal format. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import spdiags + >>> data = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) + >>> diags = np.array([0, -1, 2]) + >>> spdiags(data, diags, 4, 4).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + """ + if m is None and n is None: + m = n = len(data[0]) + elif n is None: + m, n = m + return dia_matrix((data, diags), shape=(m, n)).asformat(format) + + +def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None): + """ + Construct a sparse array from diagonals. + + Parameters + ---------- + diagonals : sequence of array_like + Sequence of arrays containing the array diagonals, + corresponding to `offsets`. + offsets : sequence of int or an int, optional + Diagonals to set: + - k = 0 the main diagonal (default) + - k > 0 the kth upper diagonal + - k < 0 the kth lower diagonal + shape : tuple of int, optional + Shape of the result. If omitted, a square array large enough + to contain the diagonals is returned. + format : {"dia", "csr", "csc", "lil", ...}, optional + Matrix format of the result. By default (format=None) an + appropriate sparse array format is returned. This choice is + subject to change. + dtype : dtype, optional + Data type of the array. + + Notes + ----- + The result from `diags_array` is the sparse equivalent of:: + + np.diag(diagonals[0], offsets[0]) + + ... + + np.diag(diagonals[k], offsets[k]) + + Repeated diagonal offsets are disallowed. + + .. versionadded:: 1.11 + + Examples + -------- + >>> from scipy.sparse import diags_array + >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]] + >>> diags_array(diagonals, offsets=[0, -1, 2]).toarray() + array([[1, 0, 1, 0], + [1, 2, 0, 2], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + Broadcasting of scalars is supported (but shape needs to be + specified): + + >>> diags_array([1, -2, 1], offsets=[-1, 0, 1], shape=(4, 4)).toarray() + array([[-2., 1., 0., 0.], + [ 1., -2., 1., 0.], + [ 0., 1., -2., 1.], + [ 0., 0., 1., -2.]]) + + + If only one diagonal is wanted (as in `numpy.diag`), the following + works as well: + + >>> diags_array([1, 2, 3], offsets=1).toarray() + array([[ 0., 1., 0., 0.], + [ 0., 0., 2., 0.], + [ 0., 0., 0., 3.], + [ 0., 0., 0., 0.]]) + """ + # if offsets is not a sequence, assume that there's only one diagonal + if isscalarlike(offsets): + # now check that there's actually only one diagonal + if len(diagonals) == 0 or isscalarlike(diagonals[0]): + diagonals = [np.atleast_1d(diagonals)] + else: + raise ValueError("Different number of diagonals and offsets.") + else: + diagonals = list(map(np.atleast_1d, diagonals)) + + offsets = np.atleast_1d(offsets) + + # Basic check + if len(diagonals) != len(offsets): + raise ValueError("Different number of diagonals and offsets.") + + # Determine shape, if omitted + if shape is None: + m = len(diagonals[0]) + abs(int(offsets[0])) + shape = (m, m) + + # Determine data type, if omitted + if dtype is None: + dtype = np.common_type(*diagonals) + + # Construct data array + m, n = shape + + M = max([min(m + offset, n - offset) + max(0, offset) + for offset in offsets]) + M = max(0, M) + data_arr = np.zeros((len(offsets), M), dtype=dtype) + + K = min(m, n) + + for j, diagonal in enumerate(diagonals): + offset = offsets[j] + k = max(0, offset) + length = min(m + offset, n - offset, K) + if length < 0: + raise ValueError("Offset %d (index %d) out of bounds" % (offset, j)) + try: + data_arr[j, k:k+length] = diagonal[...,:length] + except ValueError as e: + if len(diagonal) != length and len(diagonal) != 1: + raise ValueError( + "Diagonal length (index %d: %d at offset %d) does not " + "agree with array size (%d, %d)." % ( + j, len(diagonal), offset, m, n)) from e + raise + + return dia_array((data_arr, offsets), shape=(m, n)).asformat(format) + + +def diags(diagonals, offsets=0, shape=None, format=None, dtype=None): + """ + Construct a sparse matrix from diagonals. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``diags_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + diagonals : sequence of array_like + Sequence of arrays containing the matrix diagonals, + corresponding to `offsets`. + offsets : sequence of int or an int, optional + Diagonals to set: + - k = 0 the main diagonal (default) + - k > 0 the kth upper diagonal + - k < 0 the kth lower diagonal + shape : tuple of int, optional + Shape of the result. If omitted, a square matrix large enough + to contain the diagonals is returned. + format : {"dia", "csr", "csc", "lil", ...}, optional + Matrix format of the result. By default (format=None) an + appropriate sparse matrix format is returned. This choice is + subject to change. + dtype : dtype, optional + Data type of the matrix. + + See Also + -------- + spdiags : construct matrix from diagonals + diags_array : construct sparse array instead of sparse matrix + + Notes + ----- + This function differs from `spdiags` in the way it handles + off-diagonals. + + The result from `diags` is the sparse equivalent of:: + + np.diag(diagonals[0], offsets[0]) + + ... + + np.diag(diagonals[k], offsets[k]) + + Repeated diagonal offsets are disallowed. + + .. versionadded:: 0.11 + + Examples + -------- + >>> from scipy.sparse import diags + >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]] + >>> diags(diagonals, [0, -1, 2]).toarray() + array([[1, 0, 1, 0], + [1, 2, 0, 2], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + Broadcasting of scalars is supported (but shape needs to be + specified): + + >>> diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)).toarray() + array([[-2., 1., 0., 0.], + [ 1., -2., 1., 0.], + [ 0., 1., -2., 1.], + [ 0., 0., 1., -2.]]) + + + If only one diagonal is wanted (as in `numpy.diag`), the following + works as well: + + >>> diags([1, 2, 3], 1).toarray() + array([[ 0., 1., 0., 0.], + [ 0., 0., 2., 0.], + [ 0., 0., 0., 3.], + [ 0., 0., 0., 0.]]) + """ + A = diags_array(diagonals, offsets=offsets, shape=shape, dtype=dtype) + return dia_matrix(A).asformat(format) + + +def identity(n, dtype='d', format=None): + """Identity matrix in sparse format + + Returns an identity matrix with shape (n,n) using a given + sparse format and dtype. This differs from `eye_array` in + that it has a square shape with ones only on the main diagonal. + It is thus the multiplicative identity. `eye_array` allows + rectangular shapes and the diagonal can be offset from the main one. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``eye_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + n : int + Shape of the identity matrix. + dtype : dtype, optional + Data type of the matrix + format : str, optional + Sparse format of the result, e.g., format="csr", etc. + + Examples + -------- + >>> import scipy as sp + >>> sp.sparse.identity(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.identity(3, dtype='int8', format='dia') + <3x3 sparse matrix of type '' + with 3 stored elements (1 diagonals) in DIAgonal format> + >>> sp.sparse.eye_array(3, dtype='int8', format='dia') + <3x3 sparse array of type '' + with 3 stored elements (1 diagonals) in DIAgonal format> + + """ + return eye(n, n, dtype=dtype, format=format) + + +def eye_array(m, n=None, *, k=0, dtype=float, format=None): + """Identity matrix in sparse array format + + Return a sparse array with ones on diagonal. + Specifically a sparse array (m x n) where the kth diagonal + is all ones and everything else is zeros. + + Parameters + ---------- + m : int or tuple of ints + Number of rows requested. + n : int, optional + Number of columns. Default: `m`. + k : int, optional + Diagonal to place ones on. Default: 0 (main diagonal). + dtype : dtype, optional + Data type of the array + format : str, optional (default: "dia") + Sparse format of the result, e.g., format="csr", etc. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> sp.sparse.eye_array(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.eye_array(3, dtype=np.int8) + <3x3 sparse array of type '' + with 3 stored elements (1 diagonals) in DIAgonal format> + + """ + # TODO: delete next 15 lines [combine with _eye()] once spmatrix removed + return _eye(m, n, k, dtype, format) + + +def _eye(m, n, k, dtype, format, as_sparray=True): + if as_sparray: + csr_sparse = csr_array + csc_sparse = csc_array + coo_sparse = coo_array + diags_sparse = diags_array + else: + csr_sparse = csr_matrix + csc_sparse = csc_matrix + coo_sparse = coo_matrix + diags_sparse = diags + + if n is None: + n = m + m, n = int(m), int(n) + + if m == n and k == 0: + # fast branch for special formats + if format in ['csr', 'csc']: + idx_dtype = get_index_dtype(maxval=n) + indptr = np.arange(n+1, dtype=idx_dtype) + indices = np.arange(n, dtype=idx_dtype) + data = np.ones(n, dtype=dtype) + cls = {'csr': csr_sparse, 'csc': csc_sparse}[format] + return cls((data, indices, indptr), (n, n)) + + elif format == 'coo': + idx_dtype = get_index_dtype(maxval=n) + row = np.arange(n, dtype=idx_dtype) + col = np.arange(n, dtype=idx_dtype) + data = np.ones(n, dtype=dtype) + return coo_sparse((data, (row, col)), (n, n)) + + data = np.ones((1, max(0, min(m + k, n))), dtype=dtype) + return diags_sparse(data, offsets=[k], shape=(m, n), dtype=dtype).asformat(format) + + +def eye(m, n=None, k=0, dtype=float, format=None): + """Sparse matrix with ones on diagonal + + Returns a sparse matrix (m x n) where the kth diagonal + is all ones and everything else is zeros. + + Parameters + ---------- + m : int + Number of rows in the matrix. + n : int, optional + Number of columns. Default: `m`. + k : int, optional + Diagonal to place ones on. Default: 0 (main diagonal). + dtype : dtype, optional + Data type of the matrix. + format : str, optional + Sparse format of the result, e.g., format="csr", etc. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``eye_array`` to take advantage + of the sparse array functionality. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> sp.sparse.eye(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.eye(3, dtype=np.int8) + <3x3 sparse matrix of type '' + with 3 stored elements (1 diagonals) in DIAgonal format> + + """ + return _eye(m, n, k, dtype, format, False) + + +def kron(A, B, format=None): + """kronecker product of sparse matrices A and B + + Parameters + ---------- + A : sparse or dense matrix + first matrix of the product + B : sparse or dense matrix + second matrix of the product + format : str, optional (default: 'bsr' or 'coo') + format of the result (e.g. "csr") + If None, choose 'bsr' for relatively dense array and 'coo' for others + + Returns + ------- + kronecker product in a sparse format. + Returns a sparse matrix unless either A or B is a + sparse array in which case returns a sparse array. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> A = sp.sparse.csr_array(np.array([[0, 2], [5, 0]])) + >>> B = sp.sparse.csr_array(np.array([[1, 2], [3, 4]])) + >>> sp.sparse.kron(A, B).toarray() + array([[ 0, 0, 2, 4], + [ 0, 0, 6, 8], + [ 5, 10, 0, 0], + [15, 20, 0, 0]]) + + >>> sp.sparse.kron(A, [[1, 2], [3, 4]]).toarray() + array([[ 0, 0, 2, 4], + [ 0, 0, 6, 8], + [ 5, 10, 0, 0], + [15, 20, 0, 0]]) + + """ + # TODO: delete next 10 lines and replace _sparse with _array when spmatrix removed + if isinstance(A, sparray) or isinstance(B, sparray): + # convert to local variables + bsr_sparse = bsr_array + csr_sparse = csr_array + coo_sparse = coo_array + else: # use spmatrix + bsr_sparse = bsr_matrix + csr_sparse = csr_matrix + coo_sparse = coo_matrix + + B = coo_sparse(B) + + # B is fairly dense, use BSR + if (format is None or format == "bsr") and 2*B.nnz >= B.shape[0] * B.shape[1]: + A = csr_sparse(A,copy=True) + output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) + + if A.nnz == 0 or B.nnz == 0: + # kronecker product is the zero matrix + return coo_sparse(output_shape).asformat(format) + + B = B.toarray() + data = A.data.repeat(B.size).reshape(-1,B.shape[0],B.shape[1]) + data = data * B + + return bsr_sparse((data,A.indices,A.indptr), shape=output_shape) + else: + # use COO + A = coo_sparse(A) + output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) + + if A.nnz == 0 or B.nnz == 0: + # kronecker product is the zero matrix + return coo_sparse(output_shape).asformat(format) + + # expand entries of a into blocks + row = A.row.repeat(B.nnz) + col = A.col.repeat(B.nnz) + data = A.data.repeat(B.nnz) + + if max(A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) > np.iinfo('int32').max: + row = row.astype(np.int64) + col = col.astype(np.int64) + + row *= B.shape[0] + col *= B.shape[1] + + # increment block indices + row,col = row.reshape(-1,B.nnz),col.reshape(-1,B.nnz) + row += B.row + col += B.col + row,col = row.reshape(-1),col.reshape(-1) + + # compute block entries + data = data.reshape(-1,B.nnz) * B.data + data = data.reshape(-1) + + return coo_sparse((data,(row,col)), shape=output_shape).asformat(format) + + +def kronsum(A, B, format=None): + """kronecker sum of square sparse matrices A and B + + Kronecker sum of two sparse matrices is a sum of two Kronecker + products kron(I_n,A) + kron(B,I_m) where A has shape (m,m) + and B has shape (n,n) and I_m and I_n are identity matrices + of shape (m,m) and (n,n), respectively. + + Parameters + ---------- + A + square matrix + B + square matrix + format : str + format of the result (e.g. "csr") + + Returns + ------- + kronecker sum in a sparse matrix format + + """ + # TODO: delete next 8 lines and replace _sparse with _array when spmatrix removed + if isinstance(A, sparray) or isinstance(B, sparray): + # convert to local variables + coo_sparse = coo_array + identity_sparse = eye_array + else: + coo_sparse = coo_matrix + identity_sparse = identity + + A = coo_sparse(A) + B = coo_sparse(B) + + if A.shape[0] != A.shape[1]: + raise ValueError('A is not square') + + if B.shape[0] != B.shape[1]: + raise ValueError('B is not square') + + dtype = upcast(A.dtype, B.dtype) + + I_n = identity_sparse(A.shape[0], dtype=dtype) + I_m = identity_sparse(B.shape[0], dtype=dtype) + L = kron(I_m, A, format='coo') + R = kron(B, I_n, format='coo') + + return (L + R).asformat(format) + + +def _compressed_sparse_stack(blocks, axis, return_spmatrix): + """ + Stacking fast path for CSR/CSC matrices or arrays + (i) vstack for CSR, (ii) hstack for CSC. + """ + other_axis = 1 if axis == 0 else 0 + data = np.concatenate([b.data for b in blocks]) + constant_dim = blocks[0].shape[other_axis] + idx_dtype = get_index_dtype(arrays=[b.indptr for b in blocks], + maxval=max(data.size, constant_dim)) + indices = np.empty(data.size, dtype=idx_dtype) + indptr = np.empty(sum(b.shape[axis] for b in blocks) + 1, dtype=idx_dtype) + last_indptr = idx_dtype(0) + sum_dim = 0 + sum_indices = 0 + for b in blocks: + if b.shape[other_axis] != constant_dim: + raise ValueError(f'incompatible dimensions for axis {other_axis}') + indices[sum_indices:sum_indices+b.indices.size] = b.indices + sum_indices += b.indices.size + idxs = slice(sum_dim, sum_dim + b.shape[axis]) + indptr[idxs] = b.indptr[:-1] + indptr[idxs] += last_indptr + sum_dim += b.shape[axis] + last_indptr += b.indptr[-1] + indptr[-1] = last_indptr + # TODO remove this if-structure when sparse matrices removed + if return_spmatrix: + if axis == 0: + return csr_matrix((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return csc_matrix((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + if axis == 0: + return csr_array((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return csc_array((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + +def _stack_along_minor_axis(blocks, axis): + """ + Stacking fast path for CSR/CSC matrices along the minor axis + (i) hstack for CSR, (ii) vstack for CSC. + """ + n_blocks = len(blocks) + if n_blocks == 0: + raise ValueError('Missing block matrices') + + if n_blocks == 1: + return blocks[0] + + # check for incompatible dimensions + other_axis = 1 if axis == 0 else 0 + other_axis_dims = {b.shape[other_axis] for b in blocks} + if len(other_axis_dims) > 1: + raise ValueError(f'Mismatching dimensions along axis {other_axis}: ' + f'{other_axis_dims}') + constant_dim, = other_axis_dims + + # Do the stacking + indptr_list = [b.indptr for b in blocks] + data_cat = np.concatenate([b.data for b in blocks]) + + # Need to check if any indices/indptr, would be too large post- + # concatenation for np.int32: + # - The max value of indices is the output array's stacking-axis length - 1 + # - The max value in indptr is the number of non-zero entries. This is + # exceedingly unlikely to require int64, but is checked out of an + # abundance of caution. + sum_dim = sum(b.shape[axis] for b in blocks) + nnz = sum(len(b.indices) for b in blocks) + idx_dtype = get_index_dtype(maxval=max(sum_dim - 1, nnz)) + stack_dim_cat = np.array([b.shape[axis] for b in blocks], dtype=idx_dtype) + if data_cat.size > 0: + indptr_cat = np.concatenate(indptr_list).astype(idx_dtype) + indices_cat = (np.concatenate([b.indices for b in blocks]) + .astype(idx_dtype)) + indptr = np.empty(constant_dim + 1, dtype=idx_dtype) + indices = np.empty_like(indices_cat) + data = np.empty_like(data_cat) + csr_hstack(n_blocks, constant_dim, stack_dim_cat, + indptr_cat, indices_cat, data_cat, + indptr, indices, data) + else: + indptr = np.zeros(constant_dim + 1, dtype=idx_dtype) + indices = np.empty(0, dtype=idx_dtype) + data = np.empty(0, dtype=data_cat.dtype) + + if axis == 0: + return blocks[0]._csc_container((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return blocks[0]._csr_container((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + +def hstack(blocks, format=None, dtype=None): + """ + Stack sparse matrices horizontally (column wise) + + Parameters + ---------- + blocks + sequence of sparse matrices with compatible shapes + format : str + sparse format of the result (e.g., "csr") + by default an appropriate sparse matrix format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output matrix. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + new_array : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block(hstack(blocks))` or convert one block + e.g. `blocks[0] = csr_array(blocks[0])`. + + See Also + -------- + vstack : stack sparse matrices vertically (row wise) + + Examples + -------- + >>> from scipy.sparse import coo_matrix, hstack + >>> A = coo_matrix([[1, 2], [3, 4]]) + >>> B = coo_matrix([[5], [6]]) + >>> hstack([A,B]).toarray() + array([[1, 2, 5], + [3, 4, 6]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block([blocks], format, dtype) + else: + return _block([blocks], format, dtype, return_spmatrix=True) + + +def vstack(blocks, format=None, dtype=None): + """ + Stack sparse arrays vertically (row wise) + + Parameters + ---------- + blocks + sequence of sparse arrays with compatible shapes + format : str, optional + sparse format of the result (e.g., "csr") + by default an appropriate sparse array format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output array. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + new_array : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block(vstack(blocks))` or convert one block + e.g. `blocks[0] = csr_array(blocks[0])`. + + See Also + -------- + hstack : stack sparse matrices horizontally (column wise) + + Examples + -------- + >>> from scipy.sparse import coo_array, vstack + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5, 6]]) + >>> vstack([A, B]).toarray() + array([[1, 2], + [3, 4], + [5, 6]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block([[b] for b in blocks], format, dtype) + else: + return _block([[b] for b in blocks], format, dtype, return_spmatrix=True) + + +def bmat(blocks, format=None, dtype=None): + """ + Build a sparse array or matrix from sparse sub-blocks + + Note: `block_array` is preferred over `bmat`. They are the same function + except that `bmat` can return a deprecated sparse matrix. + `bmat` returns a coo_matrix if none of the inputs are a sparse array. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``block_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + blocks : array_like + Grid of sparse matrices with compatible shapes. + An entry of None implies an all-zero matrix. + format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional + The sparse format of the result (e.g. "csr"). By default an + appropriate sparse matrix format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output matrix. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + bmat : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block_array()`. + + See Also + -------- + block_array + + Examples + -------- + >>> from scipy.sparse import coo_array, bmat + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> bmat([[A, B], [None, C]]).toarray() + array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + + >>> bmat([[A, None], [None, C]]).toarray() + array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block(blocks, format, dtype) + else: + return _block(blocks, format, dtype, return_spmatrix=True) + + +def block_array(blocks, *, format=None, dtype=None): + """ + Build a sparse array from sparse sub-blocks + + Parameters + ---------- + blocks : array_like + Grid of sparse arrays with compatible shapes. + An entry of None implies an all-zero array. + format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional + The sparse format of the result (e.g. "csr"). By default an + appropriate sparse array format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output array. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + block : sparse array + + See Also + -------- + block_diag : specify blocks along the main diagonals + diags : specify (possibly offset) diagonals + + Examples + -------- + >>> from scipy.sparse import coo_array, block_array + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> block_array([[A, B], [None, C]]).toarray() + array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + + >>> block_array([[A, None], [None, C]]).toarray() + array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + + """ + return _block(blocks, format, dtype) + + +def _block(blocks, format, dtype, return_spmatrix=False): + blocks = np.asarray(blocks, dtype='object') + + if blocks.ndim != 2: + raise ValueError('blocks must be 2-D') + + M,N = blocks.shape + + # check for fast path cases + if (format in (None, 'csr') and + all(issparse(b) and b.format == 'csr' for b in blocks.flat) + ): + if N > 1: + # stack along columns (axis 1): must have shape (M, 1) + blocks = [[_stack_along_minor_axis(blocks[b, :], 1)] for b in range(M)] + blocks = np.asarray(blocks, dtype='object') + + # stack along rows (axis 0): + A = _compressed_sparse_stack(blocks[:, 0], 0, return_spmatrix) + if dtype is not None: + A = A.astype(dtype) + return A + elif (format in (None, 'csc') and + all(issparse(b) and b.format == 'csc' for b in blocks.flat) + ): + if M > 1: + # stack along rows (axis 0): must have shape (1, N) + blocks = [[_stack_along_minor_axis(blocks[:, b], 0) for b in range(N)]] + blocks = np.asarray(blocks, dtype='object') + + # stack along columns (axis 1): + A = _compressed_sparse_stack(blocks[0, :], 1, return_spmatrix) + if dtype is not None: + A = A.astype(dtype) + return A + + block_mask = np.zeros(blocks.shape, dtype=bool) + brow_lengths = np.zeros(M, dtype=np.int64) + bcol_lengths = np.zeros(N, dtype=np.int64) + + # convert everything to COO format + for i in range(M): + for j in range(N): + if blocks[i,j] is not None: + A = coo_array(blocks[i,j]) + blocks[i,j] = A + block_mask[i,j] = True + + if brow_lengths[i] == 0: + brow_lengths[i] = A.shape[0] + elif brow_lengths[i] != A.shape[0]: + msg = (f'blocks[{i},:] has incompatible row dimensions. ' + f'Got blocks[{i},{j}].shape[0] == {A.shape[0]}, ' + f'expected {brow_lengths[i]}.') + raise ValueError(msg) + + if bcol_lengths[j] == 0: + bcol_lengths[j] = A.shape[1] + elif bcol_lengths[j] != A.shape[1]: + msg = (f'blocks[:,{j}] has incompatible column ' + f'dimensions. ' + f'Got blocks[{i},{j}].shape[1] == {A.shape[1]}, ' + f'expected {bcol_lengths[j]}.') + raise ValueError(msg) + + nnz = sum(block.nnz for block in blocks[block_mask]) + if dtype is None: + all_dtypes = [blk.dtype for blk in blocks[block_mask]] + dtype = upcast(*all_dtypes) if all_dtypes else None + + row_offsets = np.append(0, np.cumsum(brow_lengths)) + col_offsets = np.append(0, np.cumsum(bcol_lengths)) + + shape = (row_offsets[-1], col_offsets[-1]) + + data = np.empty(nnz, dtype=dtype) + idx_dtype = get_index_dtype(maxval=max(shape)) + row = np.empty(nnz, dtype=idx_dtype) + col = np.empty(nnz, dtype=idx_dtype) + + nnz = 0 + ii, jj = np.nonzero(block_mask) + for i, j in zip(ii, jj): + B = blocks[i, j] + idx = slice(nnz, nnz + B.nnz) + data[idx] = B.data + np.add(B.row, row_offsets[i], out=row[idx], dtype=idx_dtype) + np.add(B.col, col_offsets[j], out=col[idx], dtype=idx_dtype) + nnz += B.nnz + + if return_spmatrix: + return coo_matrix((data, (row, col)), shape=shape).asformat(format) + return coo_array((data, (row, col)), shape=shape).asformat(format) + + +def block_diag(mats, format=None, dtype=None): + """ + Build a block diagonal sparse matrix or array from provided matrices. + + Parameters + ---------- + mats : sequence of matrices or arrays + Input matrices or arrays. + format : str, optional + The sparse format of the result (e.g., "csr"). If not given, the result + is returned in "coo" format. + dtype : dtype specifier, optional + The data-type of the output. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + res : sparse matrix or array + If at least one input is a sparse array, the output is a sparse array. + Otherwise the output is a sparse matrix. + + Notes + ----- + + .. versionadded:: 0.11.0 + + See Also + -------- + block_array + diags_array + + Examples + -------- + >>> from scipy.sparse import coo_array, block_diag + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> block_diag((A, B, C)).toarray() + array([[1, 2, 0, 0], + [3, 4, 0, 0], + [0, 0, 5, 0], + [0, 0, 6, 0], + [0, 0, 0, 7]]) + + """ + if any(isinstance(a, sparray) for a in mats): + container = coo_array + else: + container = coo_matrix + + row = [] + col = [] + data = [] + r_idx = 0 + c_idx = 0 + for a in mats: + if isinstance(a, (list, numbers.Number)): + a = coo_array(np.atleast_2d(a)) + if issparse(a): + a = a.tocoo() + nrows, ncols = a._shape_as_2d + row.append(a.row + r_idx) + col.append(a.col + c_idx) + data.append(a.data) + else: + nrows, ncols = a.shape + a_row, a_col = np.divmod(np.arange(nrows*ncols), ncols) + row.append(a_row + r_idx) + col.append(a_col + c_idx) + data.append(a.ravel()) + r_idx += nrows + c_idx += ncols + row = np.concatenate(row) + col = np.concatenate(col) + data = np.concatenate(data) + return container((data, (row, col)), + shape=(r_idx, c_idx), + dtype=dtype).asformat(format) + + +def random_array(shape, *, density=0.01, format='coo', dtype=None, + random_state=None, data_sampler=None): + """Return a sparse array of uniformly random numbers in [0, 1) + + Returns a sparse array with the given shape and density + where values are generated uniformly randomly in the range [0, 1). + + .. warning:: + + Since numpy 1.17, passing a ``np.random.Generator`` (e.g. + ``np.random.default_rng``) for ``random_state`` will lead to much + faster execution times. + + A much slower implementation is used by default for backwards + compatibility. + + Parameters + ---------- + shape : int or tuple of ints + shape of the array + density : real, optional (default: 0.01) + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional (default: 'coo') + sparse matrix format. + dtype : dtype, optional (default: np.float64) + type of the returned matrix values. + random_state : {None, int, `Generator`, `RandomState`}, optional + A random number generator to determine nonzero structure. We recommend using + a `numpy.random.Generator` manually provided for every call as it is much + faster than RandomState. + + - If `None` (or `np.random`), the `numpy.random.RandomState` + singleton is used. + - If an int, a new ``Generator`` instance is used, + seeded with the int. + - If a ``Generator`` or ``RandomState`` instance then + that instance is used. + + This random state will be used for sampling `indices` (the sparsity + structure), and by default for the data values too (see `data_sampler`). + + data_sampler : callable, optional (default depends on dtype) + Sampler of random data values with keyword arg `size`. + This function should take a single keyword argument `size` specifying + the length of its returned ndarray. It is used to generate the nonzero + values in the matrix after the locations of those values are chosen. + By default, uniform [0, 1) random values are used unless `dtype` is + an integer (default uniform integers from that dtype) or + complex (default uniform over the unit square in the complex plane). + For these, the `random_state` rng is used e.g. `rng.uniform(size=size)`. + + Returns + ------- + res : sparse array + + Examples + -------- + + Passing a ``np.random.Generator`` instance for better performance: + + >>> import numpy as np + >>> import scipy as sp + >>> rng = np.random.default_rng() + + Default sampling uniformly from [0, 1): + + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng) + + Providing a sampler for the values: + + >>> rvs = sp.stats.poisson(25, loc=10).rvs + >>> S = sp.sparse.random_array((3, 4), density=0.25, + ... random_state=rng, data_sampler=rvs) + >>> S.toarray() + array([[ 36., 0., 33., 0.], # random + [ 0., 0., 0., 0.], + [ 0., 0., 36., 0.]]) + + Building a custom distribution. + This example builds a squared normal from np.random: + + >>> def np_normal_squared(size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng, + ... data_sampler=np_normal_squared) + + Or we can build it from sp.stats style rvs functions: + + >>> def sp_stats_normal_squared(size=None, random_state=rng): + ... std_normal = sp.stats.distributions.norm_gen().rvs + ... return std_normal(size=size, random_state=random_state) ** 2 + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng, + ... data_sampler=sp_stats_normal_squared) + + Or we can subclass sp.stats rv_continous or rv_discrete: + + >>> class NormalSquared(sp.stats.rv_continuous): + ... def _rvs(self, size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> X = NormalSquared() + >>> Y = X().rvs + >>> S = sp.sparse.random_array((3, 4), density=0.25, + ... random_state=rng, data_sampler=Y) + """ + # Use the more efficient RNG by default. + if random_state is None: + random_state = np.random.default_rng() + data, ind = _random(shape, density, format, dtype, random_state, data_sampler) + return coo_array((data, ind), shape=shape).asformat(format) + + +def _random(shape, density=0.01, format=None, dtype=None, + random_state=None, data_sampler=None): + if density < 0 or density > 1: + raise ValueError("density expected to be 0 <= density <= 1") + + tot_prod = math.prod(shape) # use `math` for when prod is >= 2**64 + + # Number of non zero values + size = int(round(density * tot_prod)) + + rng = check_random_state(random_state) + + if data_sampler is None: + if np.issubdtype(dtype, np.integer): + def data_sampler(size): + return rng_integers(rng, + np.iinfo(dtype).min, + np.iinfo(dtype).max, + size, + dtype=dtype) + elif np.issubdtype(dtype, np.complexfloating): + def data_sampler(size): + return (rng.uniform(size=size) + + rng.uniform(size=size) * 1j) + else: + data_sampler = rng.uniform + + # rng.choice uses int64 if first arg is an int + if tot_prod < np.iinfo(np.int64).max: + raveled_ind = rng.choice(tot_prod, size=size, replace=False) + ind = np.unravel_index(raveled_ind, shape=shape, order='F') + else: + # for ravel indices bigger than dtype max, use sets to remove duplicates + ndim = len(shape) + seen = set() + while len(seen) < size: + dsize = size - len(seen) + seen.update(map(tuple, rng_integers(rng, shape, size=(dsize, ndim)))) + ind = tuple(np.array(list(seen)).T) + + # size kwarg allows eg data_sampler=partial(np.random.poisson, lam=5) + vals = data_sampler(size=size).astype(dtype, copy=False) + return vals, ind + + +def random(m, n, density=0.01, format='coo', dtype=None, + random_state=None, data_rvs=None): + """Generate a sparse matrix of the given shape and density with randomly + distributed values. + + .. warning:: + + Since numpy 1.17, passing a ``np.random.Generator`` (e.g. + ``np.random.default_rng``) for ``random_state`` will lead to much + faster execution times. + + A much slower implementation is used by default for backwards + compatibility. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``random_array`` to take advantage of the + sparse array functionality. + + Parameters + ---------- + m, n : int + shape of the matrix + density : real, optional + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional + sparse matrix format. + dtype : dtype, optional + type of the returned matrix values. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + - If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + - If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + - If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + This random state will be used for sampling the sparsity structure, but + not necessarily for sampling the values of the structurally nonzero + entries of the matrix. + data_rvs : callable, optional + Samples a requested number of random values. + This function should take a single argument specifying the length + of the ndarray that it will return. The structurally nonzero entries + of the sparse random matrix will be taken from the array sampled + by this function. By default, uniform [0, 1) random values will be + sampled using the same random state as is used for sampling + the sparsity structure. + + Returns + ------- + res : sparse matrix + + See Also + -------- + random_array : constructs sparse arrays instead of sparse matrices + + Examples + -------- + + Passing a ``np.random.Generator`` instance for better performance: + + >>> import scipy as sp + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng) + + Providing a sampler for the values: + + >>> rvs = sp.stats.poisson(25, loc=10).rvs + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, data_rvs=rvs) + >>> S.toarray() + array([[ 36., 0., 33., 0.], # random + [ 0., 0., 0., 0.], + [ 0., 0., 36., 0.]]) + + Building a custom distribution. + This example builds a squared normal from np.random: + + >>> def np_normal_squared(size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, + ... data_rvs=np_normal_squared) + + Or we can build it from sp.stats style rvs functions: + + >>> def sp_stats_normal_squared(size=None, random_state=rng): + ... std_normal = sp.stats.distributions.norm_gen().rvs + ... return std_normal(size=size, random_state=random_state) ** 2 + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, + ... data_rvs=sp_stats_normal_squared) + + Or we can subclass sp.stats rv_continous or rv_discrete: + + >>> class NormalSquared(sp.stats.rv_continuous): + ... def _rvs(self, size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> X = NormalSquared() + >>> Y = X() # get a frozen version of the distribution + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, data_rvs=Y.rvs) + """ + if n is None: + n = m + m, n = int(m), int(n) + # make keyword syntax work for data_rvs e.g. data_rvs(size=7) + if data_rvs is not None: + def data_rvs_kw(size): + return data_rvs(size) + else: + data_rvs_kw = None + vals, ind = _random((m, n), density, format, dtype, random_state, data_rvs_kw) + return coo_matrix((vals, ind), shape=(m, n)).asformat(format) + + +def rand(m, n, density=0.01, format="coo", dtype=None, random_state=None): + """Generate a sparse matrix of the given shape and density with uniformly + distributed values. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``random_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + m, n : int + shape of the matrix + density : real, optional + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional + sparse matrix format. + dtype : dtype, optional + type of the returned matrix values. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + res : sparse matrix + + Notes + ----- + Only float types are supported for now. + + See Also + -------- + random : Similar function allowing a custom random data sampler + random_array : Similar to random() but returns a sparse array + + Examples + -------- + >>> from scipy.sparse import rand + >>> matrix = rand(3, 4, density=0.25, format="csr", random_state=42) + >>> matrix + <3x4 sparse matrix of type '' + with 3 stored elements in Compressed Sparse Row format> + >>> matrix.toarray() + array([[0.05641158, 0. , 0. , 0.65088847], # random + [0. , 0. , 0. , 0.14286682], + [0. , 0. , 0. , 0. ]]) + + """ + return random(m, n, density, format, dtype, random_state) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_coo.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_coo.py new file mode 100644 index 0000000000000000000000000000000000000000..ccf536b2b17883bcc726dd2b2c05f60f4bb796ec --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_coo.py @@ -0,0 +1,858 @@ +""" A sparse matrix in COOrdinate or 'triplet' format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['coo_array', 'coo_matrix', 'isspmatrix_coo'] + +import math +from warnings import warn + +import numpy as np + +from .._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._sparsetools import coo_tocsr, coo_todense, coo_matvec +from ._base import issparse, SparseEfficiencyWarning, _spbase, sparray +from ._data import _data_matrix, _minmax_mixin +from ._sputils import (upcast_char, to_native, isshape, getdtype, + getdata, downcast_intp_index, get_index_dtype, + check_shape, check_reshape_kwargs) + +import operator + + +class _coo_base(_data_matrix, _minmax_mixin): + _format = 'coo' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self) + is_array = isinstance(self, sparray) + if not copy: + copy = copy_if_needed + + if isinstance(arg1, tuple): + if isshape(arg1, allow_1d=is_array): + self._shape = check_shape(arg1, allow_1d=is_array) + idx_dtype = self._get_index_dtype(maxval=max(self._shape)) + data_dtype = getdtype(dtype, default=float) + self.coords = tuple(np.array([], dtype=idx_dtype) + for _ in range(len(self._shape))) + self.data = np.array([], dtype=data_dtype) + self.has_canonical_format = True + else: + try: + obj, coords = arg1 + except (TypeError, ValueError) as e: + raise TypeError('invalid input format') from e + + if shape is None: + if any(len(idx) == 0 for idx in coords): + raise ValueError('cannot infer dimensions from zero ' + 'sized index arrays') + shape = tuple(operator.index(np.max(idx)) + 1 + for idx in coords) + self._shape = check_shape(shape, allow_1d=is_array) + + idx_dtype = self._get_index_dtype(coords, + maxval=max(self.shape), + check_contents=True) + self.coords = tuple(np.array(idx, copy=copy, dtype=idx_dtype) + for idx in coords) + self.data = getdata(obj, copy=copy, dtype=dtype) + self.has_canonical_format = False + else: + if issparse(arg1): + if arg1.format == self.format and copy: + self.coords = tuple(idx.copy() for idx in arg1.coords) + self.data = arg1.data.copy() + self._shape = check_shape(arg1.shape, allow_1d=is_array) + self.has_canonical_format = arg1.has_canonical_format + else: + coo = arg1.tocoo() + self.coords = tuple(coo.coords) + self.data = coo.data + self._shape = check_shape(coo.shape, allow_1d=is_array) + self.has_canonical_format = False + else: + # dense argument + M = np.asarray(arg1) + if not is_array: + M = np.atleast_2d(M) + if M.ndim != 2: + raise TypeError(f'expected 2D array or matrix, not {M.ndim}D') + + self._shape = check_shape(M.shape, allow_1d=is_array) + if shape is not None: + if check_shape(shape, allow_1d=is_array) != self._shape: + message = f'inconsistent shapes: {shape} != {self._shape}' + raise ValueError(message) + index_dtype = self._get_index_dtype(maxval=max(self._shape)) + coords = M.nonzero() + self.coords = tuple(idx.astype(index_dtype, copy=False) + for idx in coords) + self.data = M[coords] + self.has_canonical_format = True + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self._check() + + @property + def row(self): + if self.ndim > 1: + return self.coords[-2] + result = np.zeros_like(self.col) + result.setflags(write=False) + return result + + + @row.setter + def row(self, new_row): + if self.ndim < 2: + raise ValueError('cannot set row attribute of a 1-dimensional sparse array') + new_row = np.asarray(new_row, dtype=self.coords[-2].dtype) + self.coords = self.coords[:-2] + (new_row,) + self.coords[-1:] + + @property + def col(self): + return self.coords[-1] + + @col.setter + def col(self, new_col): + new_col = np.asarray(new_col, dtype=self.coords[-1].dtype) + self.coords = self.coords[:-1] + (new_col,) + + def reshape(self, *args, **kwargs): + is_array = isinstance(self, sparray) + shape = check_shape(args, self.shape, allow_1d=is_array) + order, copy = check_reshape_kwargs(kwargs) + + # Return early if reshape is not required + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + # When reducing the number of dimensions, we need to be careful about + # index overflow. This is why we can't simply call + # `np.ravel_multi_index()` followed by `np.unravel_index()` here. + flat_coords = _ravel_coords(self.coords, self.shape, order=order) + if len(shape) == 2: + if order == 'C': + new_coords = divmod(flat_coords, shape[1]) + else: + new_coords = divmod(flat_coords, shape[0])[::-1] + else: + new_coords = np.unravel_index(flat_coords, shape, order=order) + + # Handle copy here rather than passing on to the constructor so that no + # copy will be made of `new_coords` regardless. + if copy: + new_data = self.data.copy() + else: + new_data = self.data + + return self.__class__((new_data, new_coords), shape=shape, copy=False) + + reshape.__doc__ = _spbase.reshape.__doc__ + + def _getnnz(self, axis=None): + if axis is None or (axis == 0 and self.ndim == 1): + nnz = len(self.data) + if any(len(idx) != nnz for idx in self.coords): + raise ValueError('all index and data arrays must have the ' + 'same length') + + if self.data.ndim != 1 or any(idx.ndim != 1 for idx in self.coords): + raise ValueError('row, column, and data arrays must be 1-D') + + return int(nnz) + + if axis < 0: + axis += self.ndim + if axis >= self.ndim: + raise ValueError('axis out of bounds') + if self.ndim > 2: + raise NotImplementedError('per-axis nnz for COO arrays with >2 ' + 'dimensions is not supported') + return np.bincount(downcast_intp_index(self.coords[1 - axis]), + minlength=self.shape[1 - axis]) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def _check(self): + """ Checks data structure for consistency """ + if self.ndim != len(self.coords): + raise ValueError('mismatching number of index arrays for shape; ' + f'got {len(self.coords)}, expected {self.ndim}') + + # index arrays should have integer data types + for i, idx in enumerate(self.coords): + if idx.dtype.kind != 'i': + warn(f'index array {i} has non-integer dtype ({idx.dtype.name})', + stacklevel=3) + + idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.shape)) + self.coords = tuple(np.asarray(idx, dtype=idx_dtype) + for idx in self.coords) + self.data = to_native(self.data) + + if self.nnz > 0: + for i, idx in enumerate(self.coords): + if idx.max() >= self.shape[i]: + raise ValueError(f'axis {i} index {idx.max()} exceeds ' + f'matrix dimension {self.shape[i]}') + if idx.min() < 0: + raise ValueError(f'negative axis {i} index: {idx.min()}') + + def transpose(self, axes=None, copy=False): + if axes is None: + axes = range(self.ndim)[::-1] + elif isinstance(self, sparray): + if len(axes) != self.ndim: + raise ValueError("axes don't match matrix dimensions") + if len(set(axes)) != self.ndim: + raise ValueError("repeated axis in transpose") + elif axes != (1, 0): + raise ValueError("Sparse matrices do not support an 'axes' " + "parameter because swapping dimensions is the " + "only logical permutation.") + + permuted_shape = tuple(self._shape[i] for i in axes) + permuted_coords = tuple(self.coords[i] for i in axes) + return self.__class__((self.data, permuted_coords), + shape=permuted_shape, copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def resize(self, *shape) -> None: + is_array = isinstance(self, sparray) + shape = check_shape(shape, allow_1d=is_array) + + # Check for added dimensions. + if len(shape) > self.ndim: + flat_coords = _ravel_coords(self.coords, self.shape) + max_size = math.prod(shape) + self.coords = np.unravel_index(flat_coords[:max_size], shape) + self.data = self.data[:max_size] + self._shape = shape + return + + # Check for removed dimensions. + if len(shape) < self.ndim: + tmp_shape = ( + self._shape[:len(shape) - 1] # Original shape without last axis + + (-1,) # Last axis is used to flatten the array + + (1,) * (self.ndim - len(shape)) # Pad with ones + ) + tmp = self.reshape(tmp_shape) + self.coords = tmp.coords[:len(shape)] + self._shape = tmp.shape[:len(shape)] + + # Handle truncation of existing dimensions. + is_truncating = any(old > new for old, new in zip(self.shape, shape)) + if is_truncating: + mask = np.logical_and.reduce([ + idx < size for idx, size in zip(self.coords, shape) + ]) + if not mask.all(): + self.coords = tuple(idx[mask] for idx in self.coords) + self.data = self.data[mask] + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + def toarray(self, order=None, out=None): + B = self._process_toarray_args(order, out) + fortran = int(B.flags.f_contiguous) + if not fortran and not B.flags.c_contiguous: + raise ValueError("Output array must be C or F contiguous") + if self.ndim > 2: + raise ValueError("Cannot densify higher-rank sparse array") + # This handles both 0D and 1D cases correctly regardless of the + # original shape. + M, N = self._shape_as_2d + coo_todense(M, N, self.nnz, self.row, self.col, self.data, + B.ravel('A'), fortran) + # Note: reshape() doesn't copy here, but does return a new array (view). + return B.reshape(self.shape) + + toarray.__doc__ = _spbase.toarray.__doc__ + + def tocsc(self, copy=False): + """Convert this array/matrix to Compressed Sparse Column format + + Duplicate entries will be summed together. + + Examples + -------- + >>> from numpy import array + >>> from scipy.sparse import coo_array + >>> row = array([0, 0, 1, 3, 1, 0, 0]) + >>> col = array([0, 2, 1, 3, 1, 0, 0]) + >>> data = array([1, 1, 1, 1, 1, 1, 1]) + >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsc() + >>> A.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to csc format") + if self.nnz == 0: + return self._csc_container(self.shape, dtype=self.dtype) + else: + from ._csc import csc_array + indptr, indices, data, shape = self._coo_to_compressed(csc_array._swap) + + x = self._csc_container((data, indices, indptr), shape=shape) + if not self.has_canonical_format: + x.sum_duplicates() + return x + + def tocsr(self, copy=False): + """Convert this array/matrix to Compressed Sparse Row format + + Duplicate entries will be summed together. + + Examples + -------- + >>> from numpy import array + >>> from scipy.sparse import coo_array + >>> row = array([0, 0, 1, 3, 1, 0, 0]) + >>> col = array([0, 2, 1, 3, 1, 0, 0]) + >>> data = array([1, 1, 1, 1, 1, 1, 1]) + >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsr() + >>> A.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to csr format") + if self.nnz == 0: + return self._csr_container(self.shape, dtype=self.dtype) + else: + from ._csr import csr_array + indptr, indices, data, shape = self._coo_to_compressed(csr_array._swap) + + x = self._csr_container((data, indices, indptr), shape=self.shape) + if not self.has_canonical_format: + x.sum_duplicates() + return x + + def _coo_to_compressed(self, swap): + """convert (shape, coords, data) to (indptr, indices, data, shape)""" + M, N = swap(self.shape) + major, minor = swap(self.coords) + nnz = len(major) + # convert idx_dtype intc to int32 for pythran. + # tested in scipy/optimize/tests/test__numdiff.py::test_group_columns + idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.nnz, N)) + major = major.astype(idx_dtype, copy=False) + minor = minor.astype(idx_dtype, copy=False) + + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty_like(minor, dtype=idx_dtype) + data = np.empty_like(self.data, dtype=self.dtype) + + coo_tocsr(M, N, nnz, major, minor, self.data, indptr, indices, data) + return indptr, indices, data, self.shape + + def tocoo(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def todia(self, copy=False): + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to dia format") + self.sum_duplicates() + ks = self.col - self.row # the diagonal for each nonzero + diags, diag_idx = np.unique(ks, return_inverse=True) + + if len(diags) > 100: + # probably undesired, should todia() have a maxdiags parameter? + warn("Constructing a DIA matrix with %d diagonals " + "is inefficient" % len(diags), + SparseEfficiencyWarning, stacklevel=2) + + #initialize and fill in data array + if self.data.size == 0: + data = np.zeros((0, 0), dtype=self.dtype) + else: + data = np.zeros((len(diags), self.col.max()+1), dtype=self.dtype) + data[diag_idx, self.col] = self.data + + return self._dia_container((data, diags), shape=self.shape) + + todia.__doc__ = _spbase.todia.__doc__ + + def todok(self, copy=False): + self.sum_duplicates() + dok = self._dok_container(self.shape, dtype=self.dtype) + # ensure that 1d coordinates are not tuples + if self.ndim == 1: + coords = self.coords[0] + else: + coords = zip(*self.coords) + + dok._dict = dict(zip(coords, self.data)) + return dok + + todok.__doc__ = _spbase.todok.__doc__ + + def diagonal(self, k=0): + if self.ndim != 2: + raise ValueError("diagonal requires two dimensions") + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + diag = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), + dtype=self.dtype) + diag_mask = (self.row + k) == self.col + + if self.has_canonical_format: + row = self.row[diag_mask] + data = self.data[diag_mask] + else: + inds = tuple(idx[diag_mask] for idx in self.coords) + (row, _), data = self._sum_duplicates(inds, self.data[diag_mask]) + diag[row + min(k, 0)] = data + + return diag + + diagonal.__doc__ = _data_matrix.diagonal.__doc__ + + def _setdiag(self, values, k): + if self.ndim != 2: + raise ValueError("setting a diagonal requires two dimensions") + M, N = self.shape + if values.ndim and not len(values): + return + idx_dtype = self.row.dtype + + # Determine which triples to keep and where to put the new ones. + full_keep = self.col - self.row != k + if k < 0: + max_index = min(M+k, N) + if values.ndim: + max_index = min(max_index, len(values)) + keep = np.logical_or(full_keep, self.col >= max_index) + new_row = np.arange(-k, -k + max_index, dtype=idx_dtype) + new_col = np.arange(max_index, dtype=idx_dtype) + else: + max_index = min(M, N-k) + if values.ndim: + max_index = min(max_index, len(values)) + keep = np.logical_or(full_keep, self.row >= max_index) + new_row = np.arange(max_index, dtype=idx_dtype) + new_col = np.arange(k, k + max_index, dtype=idx_dtype) + + # Define the array of data consisting of the entries to be added. + if values.ndim: + new_data = values[:max_index] + else: + new_data = np.empty(max_index, dtype=self.dtype) + new_data[:] = values + + # Update the internal structure. + self.coords = (np.concatenate((self.row[keep], new_row)), + np.concatenate((self.col[keep], new_col))) + self.data = np.concatenate((self.data[keep], new_data)) + self.has_canonical_format = False + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the index arrays are copied. + """ + if copy: + coords = tuple(idx.copy() for idx in self.coords) + else: + coords = self.coords + return self.__class__((data, coords), shape=self.shape, dtype=data.dtype) + + def sum_duplicates(self) -> None: + """Eliminate duplicate entries by adding them together + + This is an *in place* operation + """ + if self.has_canonical_format: + return + summed = self._sum_duplicates(self.coords, self.data) + self.coords, self.data = summed + self.has_canonical_format = True + + def _sum_duplicates(self, coords, data): + # Assumes coords not in canonical format. + if len(data) == 0: + return coords, data + # Sort coords w.r.t. rows, then cols. This corresponds to C-order, + # which we rely on for argmin/argmax to return the first index in the + # same way that numpy does (in the case of ties). + order = np.lexsort(coords[::-1]) + coords = tuple(idx[order] for idx in coords) + data = data[order] + unique_mask = np.logical_or.reduce([ + idx[1:] != idx[:-1] for idx in coords + ]) + unique_mask = np.append(True, unique_mask) + coords = tuple(idx[unique_mask] for idx in coords) + unique_inds, = np.nonzero(unique_mask) + data = np.add.reduceat(data, unique_inds, dtype=self.dtype) + return coords, data + + def eliminate_zeros(self): + """Remove zero entries from the array/matrix + + This is an *in place* operation + """ + mask = self.data != 0 + self.data = self.data[mask] + self.coords = tuple(idx[mask] for idx in self.coords) + + ####################### + # Arithmetic handlers # + ####################### + + def _add_dense(self, other): + if other.shape != self.shape: + raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})') + dtype = upcast_char(self.dtype.char, other.dtype.char) + result = np.array(other, dtype=dtype, copy=True) + fortran = int(result.flags.f_contiguous) + M, N = self._shape_as_2d + coo_todense(M, N, self.nnz, self.row, self.col, self.data, + result.ravel('A'), fortran) + return self._container(result, copy=False) + + def _matmul_vector(self, other): + result_shape = self.shape[0] if self.ndim > 1 else 1 + result = np.zeros(result_shape, + dtype=upcast_char(self.dtype.char, other.dtype.char)) + + if self.ndim == 2: + col = self.col + row = self.row + elif self.ndim == 1: + col = self.coords[0] + row = np.zeros_like(col) + else: + raise NotImplementedError( + f"coo_matvec not implemented for ndim={self.ndim}") + + coo_matvec(self.nnz, row, col, self.data, other, result) + # Array semantics return a scalar here, not a single-element array. + if isinstance(self, sparray) and result_shape == 1: + return result[0] + return result + + def _matmul_multivector(self, other): + result_dtype = upcast_char(self.dtype.char, other.dtype.char) + if self.ndim == 2: + result_shape = (other.shape[1], self.shape[0]) + col = self.col + row = self.row + elif self.ndim == 1: + result_shape = (other.shape[1],) + col = self.coords[0] + row = np.zeros_like(col) + else: + raise NotImplementedError( + f"coo_matvec not implemented for ndim={self.ndim}") + + result = np.zeros(result_shape, dtype=result_dtype) + for i, other_col in enumerate(other.T): + coo_matvec(self.nnz, row, col, self.data, other_col, result[i:i + 1]) + return result.T.view(type=type(other)) + + +def _ravel_coords(coords, shape, order='C'): + """Like np.ravel_multi_index, but avoids some overflow issues.""" + if len(coords) == 1: + return coords[0] + # Handle overflow as in https://github.com/scipy/scipy/pull/9132 + if len(coords) == 2: + nrows, ncols = shape + row, col = coords + if order == 'C': + maxval = (ncols * max(0, nrows - 1) + max(0, ncols - 1)) + idx_dtype = get_index_dtype(maxval=maxval) + return np.multiply(ncols, row, dtype=idx_dtype) + col + elif order == 'F': + maxval = (nrows * max(0, ncols - 1) + max(0, nrows - 1)) + idx_dtype = get_index_dtype(maxval=maxval) + return np.multiply(nrows, col, dtype=idx_dtype) + row + else: + raise ValueError("'order' must be 'C' or 'F'") + return np.ravel_multi_index(coords, shape, order=order) + + +def isspmatrix_coo(x): + """Is `x` of coo_matrix type? + + Parameters + ---------- + x + object to check for being a coo matrix + + Returns + ------- + bool + True if `x` is a coo matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import coo_array, coo_matrix, csr_matrix, isspmatrix_coo + >>> isspmatrix_coo(coo_matrix([[5]])) + True + >>> isspmatrix_coo(coo_array([[5]])) + False + >>> isspmatrix_coo(csr_matrix([[5]])) + False + """ + return isinstance(x, coo_matrix) + + +# This namespace class separates array from matrix with isinstance +class coo_array(_coo_base, sparray): + """ + A sparse array in COOrdinate format. + + Also known as the 'ijv' or 'triplet' format. + + This can be instantiated in several ways: + coo_array(D) + where D is an ndarray + + coo_array(S) + with another sparse array or matrix S (equivalent to S.tocoo()) + + coo_array(shape, [dtype]) + to construct an empty sparse array with shape `shape` + dtype is optional, defaulting to dtype='d'. + + coo_array((data, coords), [shape]) + to construct from existing data and index arrays: + 1. data[:] the entries of the sparse array, in any order + 2. coords[i][:] the axis-i coordinates of the data entries + + Where ``A[coords] = data``, and coords is a tuple of index arrays. + When shape is not specified, it is inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the sparse array + shape : tuple of integers + Shape of the sparse array + ndim : int + Number of dimensions of the sparse array + nnz + size + data + COO format data array of the sparse array + coords + COO format tuple of index arrays + has_canonical_format : bool + Whether the matrix has sorted coordinates and no duplicates + format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the COO format + - facilitates fast conversion among sparse formats + - permits duplicate entries (see example) + - very fast conversion to and from CSR/CSC formats + + Disadvantages of the COO format + - does not directly support: + + arithmetic operations + + slicing + + Intended Usage + - COO is a fast format for constructing sparse arrays + - Once a COO array has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - By default when converting to CSR or CSC format, duplicate (i,j) + entries will be summed together. This facilitates efficient + construction of finite element matrices and the like. (see example) + + Canonical format + - Entries and coordinates sorted by row, then column. + - There are no duplicate entries (i.e. duplicate (i,j) locations) + - Data arrays MAY have explicit zeros. + + Examples + -------- + + >>> # Constructing an empty sparse array + >>> import numpy as np + >>> from scipy.sparse import coo_array + >>> coo_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> # Constructing a sparse array using ijv format + >>> row = np.array([0, 3, 1, 0]) + >>> col = np.array([0, 3, 1, 2]) + >>> data = np.array([4, 5, 7, 9]) + >>> coo_array((data, (row, col)), shape=(4, 4)).toarray() + array([[4, 0, 9, 0], + [0, 7, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 5]]) + + >>> # Constructing a sparse array with duplicate coordinates + >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) + >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) + >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) + >>> coo = coo_array((data, (row, col)), shape=(4, 4)) + >>> # Duplicate coordinates are maintained until implicitly or explicitly summed + >>> np.max(coo.data) + 1 + >>> coo.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + + +class coo_matrix(spmatrix, _coo_base): + """ + A sparse matrix in COOrdinate format. + + Also known as the 'ijv' or 'triplet' format. + + This can be instantiated in several ways: + coo_matrix(D) + where D is a 2-D ndarray + + coo_matrix(S) + with another sparse array or matrix S (equivalent to S.tocoo()) + + coo_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + coo_matrix((data, (i, j)), [shape=(M, N)]) + to construct from three arrays: + 1. data[:] the entries of the matrix, in any order + 2. i[:] the row indices of the matrix entries + 3. j[:] the column indices of the matrix entries + + Where ``A[i[k], j[k]] = data[k]``. When shape is not + specified, it is inferred from the index arrays + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + COO format data array of the matrix + row + COO format row index array of the matrix + col + COO format column index array of the matrix + has_canonical_format : bool + Whether the matrix has sorted indices and no duplicates + format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the COO format + - facilitates fast conversion among sparse formats + - permits duplicate entries (see example) + - very fast conversion to and from CSR/CSC formats + + Disadvantages of the COO format + - does not directly support: + + arithmetic operations + + slicing + + Intended Usage + - COO is a fast format for constructing sparse matrices + - Once a COO matrix has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - By default when converting to CSR or CSC format, duplicate (i,j) + entries will be summed together. This facilitates efficient + construction of finite element matrices and the like. (see example) + + Canonical format + - Entries and coordinates sorted by row, then column. + - There are no duplicate entries (i.e. duplicate (i,j) locations) + - Data arrays MAY have explicit zeros. + + Examples + -------- + + >>> # Constructing an empty matrix + >>> import numpy as np + >>> from scipy.sparse import coo_matrix + >>> coo_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> # Constructing a matrix using ijv format + >>> row = np.array([0, 3, 1, 0]) + >>> col = np.array([0, 3, 1, 2]) + >>> data = np.array([4, 5, 7, 9]) + >>> coo_matrix((data, (row, col)), shape=(4, 4)).toarray() + array([[4, 0, 9, 0], + [0, 7, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 5]]) + + >>> # Constructing a matrix with duplicate coordinates + >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) + >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) + >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) + >>> coo = coo_matrix((data, (row, col)), shape=(4, 4)) + >>> # Duplicate coordinates are maintained until implicitly or explicitly summed + >>> np.max(coo.data) + 1 + >>> coo.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + + def __setstate__(self, state): + if 'coords' not in state: + # For retro-compatibility with the previous attributes + # storing nnz coordinates for 2D COO matrix. + state['coords'] = (state.pop('row'), state.pop('col')) + self.__dict__.update(state) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csc.py new file mode 100644 index 0000000000000000000000000000000000000000..3fcdeb49cc0a951b9a2df955b971d5148916f289 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csc.py @@ -0,0 +1,364 @@ +"""Compressed Sparse Column matrix format""" +__docformat__ = "restructuredtext en" + +__all__ = ['csc_array', 'csc_matrix', 'isspmatrix_csc'] + + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray +from ._sparsetools import csc_tocsr, expandptr +from ._sputils import upcast + +from ._compressed import _cs_matrix + + +class _csc_base(_cs_matrix): + _format = 'csc' + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + M, N = self.shape + + return self._csr_container((self.data, self.indices, + self.indptr), (N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def __iter__(self): + yield from self.tocsr() + + def tocsc(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocsr(self, copy=False): + M,N = self.shape + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(self.nnz, N)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty(self.nnz, dtype=idx_dtype) + data = np.empty(self.nnz, dtype=upcast(self.dtype)) + + csc_tocsr(M, N, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, + indices, + data) + + A = self._csr_container( + (data, indices, indptr), + shape=self.shape, copy=False + ) + A.has_sorted_indices = True + return A + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def nonzero(self): + # CSC can't use _cs_matrix's .nonzero method because it + # returns the indices sorted for self transposed. + + # Get row and col indices, from _cs_matrix.tocoo + major_dim, minor_dim = self._swap(self.shape) + minor_indices = self.indices + major_indices = np.empty(len(minor_indices), dtype=self.indices.dtype) + expandptr(major_dim, self.indptr, major_indices) + row, col = self._swap((major_indices, minor_indices)) + + # Remove explicit zeros + nz_mask = self.data != 0 + row = row[nz_mask] + col = col[nz_mask] + + # Sort them to be in C-style order + ind = np.argsort(row, kind='mergesort') + row = row[ind] + col = col[ind] + + return row, col + + nonzero.__doc__ = _cs_matrix.nonzero.__doc__ + + def _getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) + CSR matrix (row vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('index (%d) out of range' % i) + return self._get_submatrix(minor=i).tocsr() + + def _getcol(self, i): + """Returns a copy of column i of the matrix, as a (m x 1) + CSC matrix (column vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += N + if i < 0 or i >= N: + raise IndexError('index (%d) out of range' % i) + return self._get_submatrix(major=i, copy=True) + + def _get_intXarray(self, row, col): + return self._major_index_fancy(col)._get_submatrix(minor=row) + + def _get_intXslice(self, row, col): + if col.step in (1, None): + return self._get_submatrix(major=col, minor=row, copy=True) + return self._major_slice(col)._get_submatrix(minor=row) + + def _get_sliceXint(self, row, col): + if row.step in (1, None): + return self._get_submatrix(major=col, minor=row, copy=True) + return self._get_submatrix(major=col)._minor_slice(row) + + def _get_sliceXarray(self, row, col): + return self._major_index_fancy(col)._minor_slice(row) + + def _get_arrayXint(self, row, col): + return self._get_submatrix(major=col)._minor_index_fancy(row) + + def _get_arrayXslice(self, row, col): + return self._major_slice(col)._minor_index_fancy(row) + + # these functions are used by the parent class (_cs_matrix) + # to remove redundancy between csc_array and csr_matrix + @staticmethod + def _swap(x): + """swap the members of x if this is a column-oriented matrix + """ + return x[1], x[0] + + +def isspmatrix_csc(x): + """Is `x` of csc_matrix type? + + Parameters + ---------- + x + object to check for being a csc matrix + + Returns + ------- + bool + True if `x` is a csc matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import csc_array, csc_matrix, coo_matrix, isspmatrix_csc + >>> isspmatrix_csc(csc_matrix([[5]])) + True + >>> isspmatrix_csc(csc_array([[5]])) + False + >>> isspmatrix_csc(coo_matrix([[5]])) + False + """ + return isinstance(x, csc_matrix) + + +# This namespace class separates array from matrix with isinstance +class csc_array(_csc_base, sparray): + """ + Compressed Sparse Column array. + + This can be instantiated in several ways: + csc_array(D) + where D is a 2-D ndarray + + csc_array(S) + with another sparse array or matrix S (equivalent to S.tocsc()) + + csc_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csc_array((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csc_array((data, indices, indptr), [shape=(M, N)]) + is the standard CSC representation where the row indices for + column i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding values are stored in + ``data[indptr[i]:indptr[i+1]]``. If the shape parameter is + not supplied, the array dimensions are inferred from + the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSC format data array of the array + indices + CSC format index array of the array + indptr + CSC format index pointer array of the array + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSC format + - efficient arithmetic operations CSC + CSC, CSC * CSC, etc. + - efficient column slicing + - fast matrix vector products (CSR, BSR may be faster) + + Disadvantages of the CSC format + - slow row slicing operations (consider CSR) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical format + - Within each column, indices are sorted by row. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csc_array + >>> csc_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 2, 2, 0, 1, 2]) + >>> col = np.array([0, 0, 1, 2, 2, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_array((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + """ + + +class csc_matrix(spmatrix, _csc_base): + """ + Compressed Sparse Column matrix. + + This can be instantiated in several ways: + csc_matrix(D) + where D is a 2-D ndarray + + csc_matrix(S) + with another sparse array or matrix S (equivalent to S.tocsc()) + + csc_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csc_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csc_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard CSC representation where the row indices for + column i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding values are stored in + ``data[indptr[i]:indptr[i+1]]``. If the shape parameter is + not supplied, the matrix dimensions are inferred from + the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSC format data array of the matrix + indices + CSC format index array of the matrix + indptr + CSC format index pointer array of the matrix + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSC format + - efficient arithmetic operations CSC + CSC, CSC * CSC, etc. + - efficient column slicing + - fast matrix vector products (CSR, BSR may be faster) + + Disadvantages of the CSC format + - slow row slicing operations (consider CSR) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical format + - Within each column, indices are sorted by row. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> csc_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 2, 2, 0, 1, 2]) + >>> col = np.array([0, 0, 1, 2, 2, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_matrix((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + """ + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csr.py new file mode 100644 index 0000000000000000000000000000000000000000..37c6ffacd8145a42ee74b5d71c1c736b7bb508e0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_csr.py @@ -0,0 +1,491 @@ +"""Compressed Sparse Row matrix format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['csr_array', 'csr_matrix', 'isspmatrix_csr'] + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray +from ._sparsetools import (csr_tocsc, csr_tobsr, csr_count_blocks, + get_csr_submatrix) +from ._sputils import upcast + +from ._compressed import _cs_matrix + + +class _csr_base(_cs_matrix): + _format = 'csr' + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + M, N = self.shape + return self._csc_container((self.data, self.indices, + self.indptr), shape=(N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def tolil(self, copy=False): + lil = self._lil_container(self.shape, dtype=self.dtype) + + self.sum_duplicates() + ptr,ind,dat = self.indptr,self.indices,self.data + rows, data = lil.rows, lil.data + + for n in range(self.shape[0]): + start = ptr[n] + end = ptr[n+1] + rows[n] = ind[start:end].tolist() + data[n] = dat[start:end].tolist() + + return lil + + tolil.__doc__ = _spbase.tolil.__doc__ + + def tocsr(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def tocsc(self, copy=False): + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(self.nnz, self.shape[0])) + indptr = np.empty(self.shape[1] + 1, dtype=idx_dtype) + indices = np.empty(self.nnz, dtype=idx_dtype) + data = np.empty(self.nnz, dtype=upcast(self.dtype)) + + csr_tocsc(self.shape[0], self.shape[1], + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, + indices, + data) + + A = self._csc_container((data, indices, indptr), shape=self.shape) + A.has_sorted_indices = True + return A + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tobsr(self, blocksize=None, copy=True): + if blocksize is None: + from ._spfuncs import estimate_blocksize + return self.tobsr(blocksize=estimate_blocksize(self)) + + elif blocksize == (1,1): + arg1 = (self.data.reshape(-1,1,1),self.indices,self.indptr) + return self._bsr_container(arg1, shape=self.shape, copy=copy) + + else: + R,C = blocksize + M,N = self.shape + + if R < 1 or C < 1 or M % R != 0 or N % C != 0: + raise ValueError('invalid blocksize %s' % blocksize) + + blks = csr_count_blocks(M,N,R,C,self.indptr,self.indices) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(N//C, blks)) + indptr = np.empty(M//R+1, dtype=idx_dtype) + indices = np.empty(blks, dtype=idx_dtype) + data = np.zeros((blks,R,C), dtype=self.dtype) + + csr_tobsr(M, N, R, C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, indices, data.ravel()) + + return self._bsr_container( + (data, indices, indptr), shape=self.shape + ) + + tobsr.__doc__ = _spbase.tobsr.__doc__ + + # these functions are used by the parent class (_cs_matrix) + # to remove redundancy between csc_matrix and csr_array + @staticmethod + def _swap(x): + """swap the members of x if this is a column-oriented matrix + """ + return x + + def __iter__(self): + indptr = np.zeros(2, dtype=self.indptr.dtype) + shape = (1, self.shape[1]) + i0 = 0 + for i1 in self.indptr[1:]: + indptr[1] = i1 - i0 + indices = self.indices[i0:i1] + data = self.data[i0:i1] + yield self.__class__( + (data, indices, indptr), shape=shape, copy=True + ) + i0 = i1 + + def _getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) + CSR matrix (row vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('index (%d) out of range' % i) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, i, i + 1, 0, N) + return self.__class__((data, indices, indptr), shape=(1, N), + dtype=self.dtype, copy=False) + + def _getcol(self, i): + """Returns a copy of column i of the matrix, as a (m x 1) + CSR matrix (column vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += N + if i < 0 or i >= N: + raise IndexError('index (%d) out of range' % i) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, 0, M, i, i + 1) + return self.__class__((data, indices, indptr), shape=(M, 1), + dtype=self.dtype, copy=False) + + def _get_intXarray(self, row, col): + return self._getrow(row)._minor_index_fancy(col) + + def _get_intXslice(self, row, col): + if col.step in (1, None): + return self._get_submatrix(row, col, copy=True) + # TODO: uncomment this once it's faster: + # return self._getrow(row)._minor_slice(col) + + M, N = self.shape + start, stop, stride = col.indices(N) + + ii, jj = self.indptr[row:row+2] + row_indices = self.indices[ii:jj] + row_data = self.data[ii:jj] + + if stride > 0: + ind = (row_indices >= start) & (row_indices < stop) + else: + ind = (row_indices <= start) & (row_indices > stop) + + if abs(stride) > 1: + ind &= (row_indices - start) % stride == 0 + + row_indices = (row_indices[ind] - start) // stride + row_data = row_data[ind] + row_indptr = np.array([0, len(row_indices)]) + + if stride < 0: + row_data = row_data[::-1] + row_indices = abs(row_indices[::-1]) + + shape = (1, max(0, int(np.ceil(float(stop - start) / stride)))) + return self.__class__((row_data, row_indices, row_indptr), shape=shape, + dtype=self.dtype, copy=False) + + def _get_sliceXint(self, row, col): + if row.step in (1, None): + return self._get_submatrix(row, col, copy=True) + return self._major_slice(row)._get_submatrix(minor=col) + + def _get_sliceXarray(self, row, col): + return self._major_slice(row)._minor_index_fancy(col) + + def _get_arrayXint(self, row, col): + return self._major_index_fancy(row)._get_submatrix(minor=col) + + def _get_arrayXslice(self, row, col): + if col.step not in (1, None): + col = np.arange(*col.indices(self.shape[1])) + return self._get_arrayXarray(row, col) + return self._major_index_fancy(row)._get_submatrix(minor=col) + + +def isspmatrix_csr(x): + """Is `x` of csr_matrix type? + + Parameters + ---------- + x + object to check for being a csr matrix + + Returns + ------- + bool + True if `x` is a csr matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import csr_array, csr_matrix, coo_matrix, isspmatrix_csr + >>> isspmatrix_csr(csr_matrix([[5]])) + True + >>> isspmatrix_csr(csr_array([[5]])) + False + >>> isspmatrix_csr(coo_matrix([[5]])) + False + """ + return isinstance(x, csr_matrix) + + +# This namespace class separates array from matrix with isinstance +class csr_array(_csr_base, sparray): + """ + Compressed Sparse Row array. + + This can be instantiated in several ways: + csr_array(D) + where D is a 2-D ndarray + + csr_array(S) + with another sparse array or matrix S (equivalent to S.tocsr()) + + csr_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csr_array((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csr_array((data, indices, indptr), [shape=(M, N)]) + is the standard CSR representation where the column indices for + row i are stored in ``indices[indptr[i]:indptr[i+1]]`` and their + corresponding values are stored in ``data[indptr[i]:indptr[i+1]]``. + If the shape parameter is not supplied, the array dimensions + are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSR format data array of the array + indices + CSR format index array of the array + indptr + CSR format index pointer array of the array + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSR format + - efficient arithmetic operations CSR + CSR, CSR * CSR, etc. + - efficient row slicing + - fast matrix vector products + + Disadvantages of the CSR format + - slow column slicing operations (consider CSC) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical Format + - Within each row, indices are sorted by column. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csr_array + >>> csr_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_array((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + Duplicate entries are summed together: + + >>> row = np.array([0, 1, 2, 0]) + >>> col = np.array([0, 1, 1, 0]) + >>> data = np.array([1, 2, 4, 8]) + >>> csr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[9, 0, 0], + [0, 2, 0], + [0, 4, 0]]) + + As an example of how to construct a CSR array incrementally, + the following snippet builds a term-document array from texts: + + >>> docs = [["hello", "world", "hello"], ["goodbye", "cruel", "world"]] + >>> indptr = [0] + >>> indices = [] + >>> data = [] + >>> vocabulary = {} + >>> for d in docs: + ... for term in d: + ... index = vocabulary.setdefault(term, len(vocabulary)) + ... indices.append(index) + ... data.append(1) + ... indptr.append(len(indices)) + ... + >>> csr_array((data, indices, indptr), dtype=int).toarray() + array([[2, 1, 0, 0], + [0, 1, 1, 1]]) + + """ + + +class csr_matrix(spmatrix, _csr_base): + """ + Compressed Sparse Row matrix. + + This can be instantiated in several ways: + csr_matrix(D) + where D is a 2-D ndarray + + csr_matrix(S) + with another sparse array or matrix S (equivalent to S.tocsr()) + + csr_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csr_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard CSR representation where the column indices for + row i are stored in ``indices[indptr[i]:indptr[i+1]]`` and their + corresponding values are stored in ``data[indptr[i]:indptr[i+1]]``. + If the shape parameter is not supplied, the matrix dimensions + are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSR format data array of the matrix + indices + CSR format index array of the matrix + indptr + CSR format index pointer array of the matrix + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSR format + - efficient arithmetic operations CSR + CSR, CSR * CSR, etc. + - efficient row slicing + - fast matrix vector products + + Disadvantages of the CSR format + - slow column slicing operations (consider CSC) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical Format + - Within each row, indices are sorted by column. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csr_matrix + >>> csr_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_matrix((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + Duplicate entries are summed together: + + >>> row = np.array([0, 1, 2, 0]) + >>> col = np.array([0, 1, 1, 0]) + >>> data = np.array([1, 2, 4, 8]) + >>> csr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[9, 0, 0], + [0, 2, 0], + [0, 4, 0]]) + + As an example of how to construct a CSR matrix incrementally, + the following snippet builds a term-document matrix from texts: + + >>> docs = [["hello", "world", "hello"], ["goodbye", "cruel", "world"]] + >>> indptr = [0] + >>> indices = [] + >>> data = [] + >>> vocabulary = {} + >>> for d in docs: + ... for term in d: + ... index = vocabulary.setdefault(term, len(vocabulary)) + ... indices.append(index) + ... data.append(1) + ... indptr.append(len(indices)) + ... + >>> csr_matrix((data, indices, indptr), dtype=int).toarray() + array([[2, 1, 0, 0], + [0, 1, 1, 1]]) + + """ + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_data.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_data.py new file mode 100644 index 0000000000000000000000000000000000000000..408661ccb4a37aaecbdfc6ae6557ebff468000ed --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_data.py @@ -0,0 +1,506 @@ +"""Base class for sparse matrice with a .data attribute + + subclasses must provide a _with_data() method that + creates a new matrix with the same sparsity pattern + as self but with a different data array + +""" + +import numpy as np + +from ._base import _spbase, _ufuncs_with_fixed_point_at_zero +from ._sputils import isscalarlike, validateaxis + +__all__ = [] + + +# TODO implement all relevant operations +# use .data.__methods__() instead of /=, *=, etc. +class _data_matrix(_spbase): + def __init__(self): + _spbase.__init__(self) + + @property + def dtype(self): + return self.data.dtype + + @dtype.setter + def dtype(self, newtype): + self.data.dtype = newtype + + def _deduped_data(self): + if hasattr(self, 'sum_duplicates'): + self.sum_duplicates() + return self.data + + def __abs__(self): + return self._with_data(abs(self._deduped_data())) + + def __round__(self, ndigits=0): + return self._with_data(np.around(self._deduped_data(), decimals=ndigits)) + + def _real(self): + return self._with_data(self.data.real) + + def _imag(self): + return self._with_data(self.data.imag) + + def __neg__(self): + if self.dtype.kind == 'b': + raise NotImplementedError('negating a boolean sparse array is not ' + 'supported') + return self._with_data(-self.data) + + def __imul__(self, other): # self *= other + if isscalarlike(other): + self.data *= other + return self + else: + return NotImplemented + + def __itruediv__(self, other): # self /= other + if isscalarlike(other): + recip = 1.0 / other + self.data *= recip + return self + else: + return NotImplemented + + def astype(self, dtype, casting='unsafe', copy=True): + dtype = np.dtype(dtype) + if self.dtype != dtype: + matrix = self._with_data( + self.data.astype(dtype, casting=casting, copy=True), + copy=True + ) + return matrix._with_data(matrix._deduped_data(), copy=False) + elif copy: + return self.copy() + else: + return self + + astype.__doc__ = _spbase.astype.__doc__ + + def conjugate(self, copy=True): + if np.issubdtype(self.dtype, np.complexfloating): + return self._with_data(self.data.conjugate(), copy=copy) + elif copy: + return self.copy() + else: + return self + + conjugate.__doc__ = _spbase.conjugate.__doc__ + + def copy(self): + return self._with_data(self.data.copy(), copy=True) + + copy.__doc__ = _spbase.copy.__doc__ + + def count_nonzero(self): + return np.count_nonzero(self._deduped_data()) + + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def power(self, n, dtype=None): + """ + This function performs element-wise power. + + Parameters + ---------- + n : scalar + n is a non-zero scalar (nonzero avoids dense ones creation) + If zero power is desired, special case it to use `np.ones` + + dtype : If dtype is not specified, the current dtype will be preserved. + + Raises + ------ + NotImplementedError : if n is a zero scalar + If zero power is desired, special case it to use + `np.ones(A.shape, dtype=A.dtype)` + """ + if not isscalarlike(n): + raise NotImplementedError("input is not scalar") + if not n: + raise NotImplementedError( + "zero power is not supported as it would densify the matrix.\n" + "Use `np.ones(A.shape, dtype=A.dtype)` for this case." + ) + + data = self._deduped_data() + if dtype is not None: + data = data.astype(dtype) + return self._with_data(data ** n) + + ########################### + # Multiplication handlers # + ########################### + + def _mul_scalar(self, other): + return self._with_data(self.data * other) + + +# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix. +for npfunc in _ufuncs_with_fixed_point_at_zero: + name = npfunc.__name__ + + def _create_method(op): + def method(self): + result = op(self._deduped_data()) + return self._with_data(result, copy=True) + + method.__doc__ = (f"Element-wise {name}.\n\n" + f"See `numpy.{name}` for more information.") + method.__name__ = name + + return method + + setattr(_data_matrix, name, _create_method(npfunc)) + + +def _find_missing_index(ind, n): + for k, a in enumerate(ind): + if k != a: + return k + + k += 1 + if k < n: + return k + else: + return -1 + + +class _minmax_mixin: + """Mixin for min and max methods. + + These are not implemented for dia_matrix, hence the separate class. + """ + + def _min_or_max_axis(self, axis, min_or_max): + N = self.shape[axis] + if N == 0: + raise ValueError("zero-size array to reduction operation") + M = self.shape[1 - axis] + idx_dtype = self._get_index_dtype(maxval=M) + + mat = self.tocsc() if axis == 0 else self.tocsr() + mat.sum_duplicates() + + major_index, value = mat._minor_reduce(min_or_max) + not_full = np.diff(mat.indptr)[major_index] < N + value[not_full] = min_or_max(value[not_full], 0) + + mask = value != 0 + major_index = np.compress(mask, major_index) + value = np.compress(mask, value) + + if axis == 0: + return self._coo_container( + (value, (np.zeros(len(value), dtype=idx_dtype), major_index)), + dtype=self.dtype, shape=(1, M) + ) + else: + return self._coo_container( + (value, (major_index, np.zeros(len(value), dtype=idx_dtype))), + dtype=self.dtype, shape=(M, 1) + ) + + def _min_or_max(self, axis, out, min_or_max): + if out is not None: + raise ValueError("Sparse arrays do not support an 'out' parameter.") + + validateaxis(axis) + if self.ndim == 1: + if axis not in (None, 0, -1): + raise ValueError("axis out of range") + axis = None # avoid calling special axis case. no impact on 1d + + if axis is None: + if 0 in self.shape: + raise ValueError("zero-size array to reduction operation") + + zero = self.dtype.type(0) + if self.nnz == 0: + return zero + m = min_or_max.reduce(self._deduped_data().ravel()) + if self.nnz != np.prod(self.shape): + m = min_or_max(zero, m) + return m + + if axis < 0: + axis += 2 + + if (axis == 0) or (axis == 1): + return self._min_or_max_axis(axis, min_or_max) + else: + raise ValueError("axis out of range") + + def _arg_min_or_max_axis(self, axis, argmin_or_argmax, compare): + if self.shape[axis] == 0: + raise ValueError("Cannot apply the operation along a zero-sized dimension.") + + if axis < 0: + axis += 2 + + zero = self.dtype.type(0) + + mat = self.tocsc() if axis == 0 else self.tocsr() + mat.sum_duplicates() + + ret_size, line_size = mat._swap(mat.shape) + ret = np.zeros(ret_size, dtype=int) + + nz_lines, = np.nonzero(np.diff(mat.indptr)) + for i in nz_lines: + p, q = mat.indptr[i:i + 2] + data = mat.data[p:q] + indices = mat.indices[p:q] + extreme_index = argmin_or_argmax(data) + extreme_value = data[extreme_index] + if compare(extreme_value, zero) or q - p == line_size: + ret[i] = indices[extreme_index] + else: + zero_ind = _find_missing_index(indices, line_size) + if extreme_value == zero: + ret[i] = min(extreme_index, zero_ind) + else: + ret[i] = zero_ind + + if axis == 1: + ret = ret.reshape(-1, 1) + + return self._ascontainer(ret) + + def _arg_min_or_max(self, axis, out, argmin_or_argmax, compare): + if out is not None: + raise ValueError("Sparse types do not support an 'out' parameter.") + + validateaxis(axis) + + if self.ndim == 1: + if axis not in (None, 0, -1): + raise ValueError("axis out of range") + axis = None # avoid calling special axis case. no impact on 1d + + if axis is not None: + return self._arg_min_or_max_axis(axis, argmin_or_argmax, compare) + + if 0 in self.shape: + raise ValueError("Cannot apply the operation to an empty matrix.") + + if self.nnz == 0: + return 0 + + zero = self.dtype.type(0) + mat = self.tocoo() + # Convert to canonical form: no duplicates, sorted indices. + mat.sum_duplicates() + extreme_index = argmin_or_argmax(mat.data) + extreme_value = mat.data[extreme_index] + num_col = mat.shape[-1] + + # If the min value is less than zero, or max is greater than zero, + # then we do not need to worry about implicit zeros. + if compare(extreme_value, zero): + # cast to Python int to avoid overflow and RuntimeError + return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index]) + + # Cheap test for the rare case where we have no implicit zeros. + size = np.prod(self.shape) + if size == mat.nnz: + return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index]) + + # At this stage, any implicit zero could be the min or max value. + # After sum_duplicates(), the `row` and `col` arrays are guaranteed to + # be sorted in C-order, which means the linearized indices are sorted. + linear_indices = mat.row * num_col + mat.col + first_implicit_zero_index = _find_missing_index(linear_indices, size) + if extreme_value == zero: + return min(first_implicit_zero_index, extreme_index) + return first_implicit_zero_index + + def max(self, axis=None, out=None): + """ + Return the maximum of the array/matrix or maximum along an axis. + This takes all elements into account, not just the non-zero ones. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the maximum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value, as this argument is not used. + + Returns + ------- + amax : coo_matrix or scalar + Maximum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + min : The minimum value of a sparse array/matrix along a given axis. + numpy.matrix.max : NumPy's implementation of 'max' for matrices + + """ + return self._min_or_max(axis, out, np.maximum) + + def min(self, axis=None, out=None): + """ + Return the minimum of the array/matrix or maximum along an axis. + This takes all elements into account, not just the non-zero ones. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the minimum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + amin : coo_matrix or scalar + Minimum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + max : The maximum value of a sparse array/matrix along a given axis. + numpy.matrix.min : NumPy's implementation of 'min' for matrices + + """ + return self._min_or_max(axis, out, np.minimum) + + def nanmax(self, axis=None, out=None): + """ + Return the maximum of the array/matrix or maximum along an axis, ignoring any + NaNs. This takes all elements into account, not just the non-zero + ones. + + .. versionadded:: 1.11.0 + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the maximum is computed. The default is to + compute the maximum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value, as this argument is not used. + + Returns + ------- + amax : coo_matrix or scalar + Maximum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + nanmin : The minimum value of a sparse array/matrix along a given axis, + ignoring NaNs. + max : The maximum value of a sparse array/matrix along a given axis, + propagating NaNs. + numpy.nanmax : NumPy's implementation of 'nanmax'. + + """ + return self._min_or_max(axis, out, np.fmax) + + def nanmin(self, axis=None, out=None): + """ + Return the minimum of the array/matrix or minimum along an axis, ignoring any + NaNs. This takes all elements into account, not just the non-zero + ones. + + .. versionadded:: 1.11.0 + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the minimum is computed. The default is to + compute the minimum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + amin : coo_matrix or scalar + Minimum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + nanmax : The maximum value of a sparse array/matrix along a given axis, + ignoring NaNs. + min : The minimum value of a sparse array/matrix along a given axis, + propagating NaNs. + numpy.nanmin : NumPy's implementation of 'nanmin'. + + """ + return self._min_or_max(axis, out, np.fmin) + + def argmax(self, axis=None, out=None): + """Return indices of maximum elements along an axis. + + Implicit zero elements are also taken into account. If there are + several maximum values, the index of the first occurrence is returned. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None}, optional + Axis along which the argmax is computed. If None (default), index + of the maximum element in the flatten data is returned. + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + ind : numpy.matrix or int + Indices of maximum elements. If matrix, its size along `axis` is 1. + """ + return self._arg_min_or_max(axis, out, np.argmax, np.greater) + + def argmin(self, axis=None, out=None): + """Return indices of minimum elements along an axis. + + Implicit zero elements are also taken into account. If there are + several minimum values, the index of the first occurrence is returned. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None}, optional + Axis along which the argmin is computed. If None (default), index + of the minimum element in the flatten data is returned. + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + ind : numpy.matrix or int + Indices of minimum elements. If matrix, its size along `axis` is 1. + """ + return self._arg_min_or_max(axis, out, np.argmin, np.less) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dia.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dia.py new file mode 100644 index 0000000000000000000000000000000000000000..3ef22f4596d881c06be9b00e8457b88272d5f193 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dia.py @@ -0,0 +1,565 @@ +"""Sparse DIAgonal format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['dia_array', 'dia_matrix', 'isspmatrix_dia'] + +import numpy as np + +from .._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._base import issparse, _formats, _spbase, sparray +from ._data import _data_matrix +from ._sputils import ( + isshape, upcast_char, getdtype, get_sum_dtype, validateaxis, check_shape +) +from ._sparsetools import dia_matvec + + +class _dia_base(_data_matrix): + _format = 'dia' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self) + + if issparse(arg1): + if arg1.format == "dia": + if copy: + arg1 = arg1.copy() + self.data = arg1.data + self.offsets = arg1.offsets + self._shape = check_shape(arg1.shape) + else: + if arg1.format == self.format and copy: + A = arg1.copy() + else: + A = arg1.todia() + self.data = A.data + self.offsets = A.offsets + self._shape = check_shape(A.shape) + elif isinstance(arg1, tuple): + if isshape(arg1): + # It's a tuple of matrix dimensions (M, N) + # create empty matrix + self._shape = check_shape(arg1) + self.data = np.zeros((0,0), getdtype(dtype, default=float)) + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + self.offsets = np.zeros((0), dtype=idx_dtype) + else: + try: + # Try interpreting it as (data, offsets) + data, offsets = arg1 + except Exception as e: + message = 'unrecognized form for dia_array constructor' + raise ValueError(message) from e + else: + if shape is None: + raise ValueError('expected a shape argument') + if not copy: + copy = copy_if_needed + self.data = np.atleast_2d(np.array(arg1[0], dtype=dtype, copy=copy)) + offsets = np.array(arg1[1], + dtype=self._get_index_dtype(maxval=max(shape)), + copy=copy) + self.offsets = np.atleast_1d(offsets) + self._shape = check_shape(shape) + else: + #must be dense, convert to COO first, then to DIA + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise ValueError("unrecognized form for" + " %s_matrix constructor" % self.format) from e + if isinstance(self, sparray) and arg1.ndim != 2: + raise ValueError(f"DIA arrays don't support {arg1.ndim}D input. Use 2D") + A = self._coo_container(arg1, dtype=dtype, shape=shape).todia() + self.data = A.data + self.offsets = A.offsets + self._shape = check_shape(A.shape) + + if dtype is not None: + self.data = self.data.astype(dtype) + + #check format + if self.offsets.ndim != 1: + raise ValueError('offsets array must have rank 1') + + if self.data.ndim != 2: + raise ValueError('data array must have rank 2') + + if self.data.shape[0] != len(self.offsets): + raise ValueError('number of diagonals (%d) ' + 'does not match the number of offsets (%d)' + % (self.data.shape[0], len(self.offsets))) + + if len(np.unique(self.offsets)) != len(self.offsets): + raise ValueError('offset array contains duplicate values') + + def __repr__(self): + _, fmt = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + shape_str = 'x'.join(str(x) for x in self.shape) + ndiag = self.data.shape[0] + return ( + f"<{shape_str} sparse {sparse_cls} of type '{self.dtype.type}'\n" + f"\twith {self.nnz} stored elements ({ndiag} diagonals) in {fmt} format>" + ) + + def _data_mask(self): + """Returns a mask of the same shape as self.data, where + mask[i,j] is True when data[i,j] corresponds to a stored element.""" + num_rows, num_cols = self.shape + offset_inds = np.arange(self.data.shape[1]) + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + return mask + + def count_nonzero(self): + mask = self._data_mask() + return np.count_nonzero(self.data[mask]) + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError("_getnnz over an axis is not implemented " + "for DIA format") + M,N = self.shape + nnz = 0 + for k in self.offsets: + if k > 0: + nnz += min(M,N-k) + else: + nnz += min(M+k,N) + return int(nnz) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def sum(self, axis=None, dtype=None, out=None): + validateaxis(axis) + + if axis is not None and axis < 0: + axis += 2 + + res_dtype = get_sum_dtype(self.dtype) + num_rows, num_cols = self.shape + ret = None + + if axis == 0: + mask = self._data_mask() + x = (self.data * mask).sum(axis=0) + if x.shape[0] == num_cols: + res = x + else: + res = np.zeros(num_cols, dtype=x.dtype) + res[:x.shape[0]] = x + ret = self._ascontainer(res, dtype=res_dtype) + + else: + row_sums = np.zeros((num_rows, 1), dtype=res_dtype) + one = np.ones(num_cols, dtype=res_dtype) + dia_matvec(num_rows, num_cols, len(self.offsets), + self.data.shape[1], self.offsets, self.data, one, row_sums) + + row_sums = self._ascontainer(row_sums) + + if axis is None: + return row_sums.sum(dtype=dtype, out=out) + + ret = self._ascontainer(row_sums.sum(axis=axis)) + + if out is not None and out.shape != ret.shape: + raise ValueError("dimensions do not match") + + return ret.sum(axis=(), dtype=dtype, out=out) + + sum.__doc__ = _spbase.sum.__doc__ + + def _add_sparse(self, other): + + # Check if other is also of type dia_array + if not isinstance(other, type(self)): + # If other is not of type dia_array, default to + # converting to csr_matrix, as is done in the _add_sparse + # method of parent class _spbase + return self.tocsr()._add_sparse(other) + + # The task is to compute m = self + other + # Start by making a copy of self, of the datatype + # that should result from adding self and other + dtype = np.promote_types(self.dtype, other.dtype) + m = self.astype(dtype, copy=True) + + # Then, add all the stored diagonals of other. + for d in other.offsets: + # Check if the diagonal has already been added. + if d in m.offsets: + # If the diagonal is already there, we need to take + # the sum of the existing and the new + m.setdiag(m.diagonal(d) + other.diagonal(d), d) + else: + m.setdiag(other.diagonal(d), d) + return m + + def _matmul_vector(self, other): + x = other + + y = np.zeros(self.shape[0], dtype=upcast_char(self.dtype.char, + x.dtype.char)) + + L = self.data.shape[1] + + M,N = self.shape + + dia_matvec(M,N, len(self.offsets), L, self.offsets, self.data, + x.ravel(), y.ravel()) + + return y + + def _setdiag(self, values, k=0): + M, N = self.shape + + if values.ndim == 0: + # broadcast + values_n = np.inf + else: + values_n = len(values) + + if k < 0: + n = min(M + k, N, values_n) + min_index = 0 + max_index = n + else: + n = min(M, N - k, values_n) + min_index = k + max_index = k + n + + if values.ndim != 0: + # allow also longer sequences + values = values[:n] + + data_rows, data_cols = self.data.shape + if k in self.offsets: + if max_index > data_cols: + data = np.zeros((data_rows, max_index), dtype=self.data.dtype) + data[:, :data_cols] = self.data + self.data = data + self.data[self.offsets == k, min_index:max_index] = values + else: + self.offsets = np.append(self.offsets, self.offsets.dtype.type(k)) + m = max(max_index, data_cols) + data = np.zeros((data_rows + 1, m), dtype=self.data.dtype) + data[:-1, :data_cols] = self.data + data[-1, min_index:max_index] = values + self.data = data + + def todia(self, copy=False): + if copy: + return self.copy() + else: + return self + + todia.__doc__ = _spbase.todia.__doc__ + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + num_rows, num_cols = self.shape + max_dim = max(self.shape) + + # flip diagonal offsets + offsets = -self.offsets + + # re-align the data matrix + r = np.arange(len(offsets), dtype=np.intc)[:, None] + c = np.arange(num_rows, dtype=np.intc) - (offsets % max_dim)[:, None] + pad_amount = max(0, max_dim-self.data.shape[1]) + data = np.hstack((self.data, np.zeros((self.data.shape[0], pad_amount), + dtype=self.data.dtype))) + data = data[r, c] + return self._dia_container((data, offsets), shape=( + num_cols, num_rows), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + idx, = np.nonzero(self.offsets == k) + first_col = max(0, k) + last_col = min(rows + k, cols) + result_size = last_col - first_col + if idx.size == 0: + return np.zeros(result_size, dtype=self.data.dtype) + result = self.data[idx[0], first_col:last_col] + padding = result_size - len(result) + if padding > 0: + result = np.pad(result, (0, padding), mode='constant') + return result + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + def tocsc(self, copy=False): + if self.nnz == 0: + return self._csc_container(self.shape, dtype=self.dtype) + + num_rows, num_cols = self.shape + num_offsets, offset_len = self.data.shape + offset_inds = np.arange(offset_len) + + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + mask &= (self.data != 0) + + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + indptr = np.zeros(num_cols + 1, dtype=idx_dtype) + indptr[1:offset_len+1] = np.cumsum(mask.sum(axis=0)[:num_cols]) + if offset_len < num_cols: + indptr[offset_len+1:] = indptr[offset_len] + indices = row.T[mask.T].astype(idx_dtype, copy=False) + data = self.data.T[mask.T] + return self._csc_container((data, indices, indptr), shape=self.shape, + dtype=self.dtype) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocoo(self, copy=False): + num_rows, num_cols = self.shape + num_offsets, offset_len = self.data.shape + offset_inds = np.arange(offset_len) + + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + mask &= (self.data != 0) + row = row[mask] + col = np.tile(offset_inds, num_offsets)[mask.ravel()] + idx_dtype = self._get_index_dtype( + arrays=(self.offsets,), maxval=max(self.shape) + ) + row = row.astype(idx_dtype, copy=False) + col = col.astype(idx_dtype, copy=False) + data = self.data[mask] + # Note: this cannot set has_canonical_format=True, because despite the + # lack of duplicates, we do not generate sorted indices. + return self._coo_container( + (data, (row, col)), shape=self.shape, dtype=self.dtype, copy=False + ) + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays are copied. + """ + if copy: + return self._dia_container( + (data, self.offsets.copy()), shape=self.shape + ) + else: + return self._dia_container( + (data, self.offsets), shape=self.shape + ) + + def resize(self, *shape): + shape = check_shape(shape) + M, N = shape + # we do not need to handle the case of expanding N + self.data = self.data[:, :N] + + if (M > self.shape[0] and + np.any(self.offsets + self.shape[0] < self.data.shape[1])): + # explicitly clear values that were previously hidden + mask = (self.offsets[:, None] + self.shape[0] <= + np.arange(self.data.shape[1])) + self.data[mask] = 0 + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + +def isspmatrix_dia(x): + """Is `x` of dia_matrix type? + + Parameters + ---------- + x + object to check for being a dia matrix + + Returns + ------- + bool + True if `x` is a dia matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import dia_array, dia_matrix, coo_matrix, isspmatrix_dia + >>> isspmatrix_dia(dia_matrix([[5]])) + True + >>> isspmatrix_dia(dia_array([[5]])) + False + >>> isspmatrix_dia(coo_matrix([[5]])) + False + """ + return isinstance(x, dia_matrix) + + +# This namespace class separates array from matrix with isinstance +class dia_array(_dia_base, sparray): + """ + Sparse array with DIAgonal storage. + + This can be instantiated in several ways: + dia_array(D) + where D is a 2-D ndarray + + dia_array(S) + with another sparse array or matrix S (equivalent to S.todia()) + + dia_array((M, N), [dtype]) + to construct an empty array with shape (M, N), + dtype is optional, defaulting to dtype='d'. + + dia_array((data, offsets), shape=(M, N)) + where the ``data[k,:]`` stores the diagonal entries for + diagonal ``offsets[k]`` (See example below) + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + DIA format data array of the array + offsets + DIA format offset array of the array + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import dia_array + >>> dia_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + >>> offsets = np.array([0, -1, 2]) + >>> dia_array((data, offsets), shape=(4, 4)).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + >>> from scipy.sparse import dia_array + >>> n = 10 + >>> ex = np.ones(n) + >>> data = np.array([ex, 2 * ex, ex]) + >>> offsets = np.array([-1, 0, 1]) + >>> dia_array((data, offsets), shape=(n, n)).toarray() + array([[2., 1., 0., ..., 0., 0., 0.], + [1., 2., 1., ..., 0., 0., 0.], + [0., 1., 2., ..., 0., 0., 0.], + ..., + [0., 0., 0., ..., 2., 1., 0.], + [0., 0., 0., ..., 1., 2., 1.], + [0., 0., 0., ..., 0., 1., 2.]]) + """ + + +class dia_matrix(spmatrix, _dia_base): + """ + Sparse matrix with DIAgonal storage. + + This can be instantiated in several ways: + dia_matrix(D) + where D is a 2-D ndarray + + dia_matrix(S) + with another sparse array or matrix S (equivalent to S.todia()) + + dia_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N), + dtype is optional, defaulting to dtype='d'. + + dia_matrix((data, offsets), shape=(M, N)) + where the ``data[k,:]`` stores the diagonal entries for + diagonal ``offsets[k]`` (See example below) + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + DIA format data array of the matrix + offsets + DIA format offset array of the matrix + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import dia_matrix + >>> dia_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + >>> offsets = np.array([0, -1, 2]) + >>> dia_matrix((data, offsets), shape=(4, 4)).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + >>> from scipy.sparse import dia_matrix + >>> n = 10 + >>> ex = np.ones(n) + >>> data = np.array([ex, 2 * ex, ex]) + >>> offsets = np.array([-1, 0, 1]) + >>> dia_matrix((data, offsets), shape=(n, n)).toarray() + array([[2., 1., 0., ..., 0., 0., 0.], + [1., 2., 1., ..., 0., 0., 0.], + [0., 1., 2., ..., 0., 0., 0.], + ..., + [0., 0., 0., ..., 2., 1., 0.], + [0., 0., 0., ..., 1., 2., 1.], + [0., 0., 0., ..., 0., 1., 2.]]) + """ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dok.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dok.py new file mode 100644 index 0000000000000000000000000000000000000000..5e59920074654b5ab41b3a8b015ae68aa00886f9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_dok.py @@ -0,0 +1,672 @@ +"""Dictionary Of Keys based matrix""" + +__docformat__ = "restructuredtext en" + +__all__ = ['dok_array', 'dok_matrix', 'isspmatrix_dok'] + +import itertools +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray, issparse +from ._index import IndexMixin +from ._sputils import (isdense, getdtype, isshape, isintlike, isscalarlike, + upcast, upcast_scalar, check_shape) + + +class _dok_base(_spbase, IndexMixin, dict): + _format = 'dok' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _spbase.__init__(self) + + is_array = isinstance(self, sparray) + if isinstance(arg1, tuple) and isshape(arg1, allow_1d=is_array): + self._shape = check_shape(arg1, allow_1d=is_array) + self._dict = {} + self.dtype = getdtype(dtype, default=float) + elif issparse(arg1): # Sparse ctor + if arg1.format == self.format: + arg1 = arg1.copy() if copy else arg1 + else: + arg1 = arg1.todok() + + if dtype is not None: + arg1 = arg1.astype(dtype, copy=False) + + self._dict = arg1._dict + self._shape = check_shape(arg1.shape, allow_1d=is_array) + self.dtype = arg1.dtype + else: # Dense ctor + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise TypeError('Invalid input format.') from e + + if arg1.ndim > 2: + raise TypeError('Expected rank <=2 dense array or matrix.') + + if arg1.ndim == 1: + if dtype is not None: + arg1 = arg1.astype(dtype) + self._dict = {i: v for i, v in enumerate(arg1) if v != 0} + self.dtype = arg1.dtype + else: + d = self._coo_container(arg1, dtype=dtype).todok() + self._dict = d._dict + self.dtype = d.dtype + self._shape = check_shape(arg1.shape, allow_1d=is_array) + + def update(self, val): + # Prevent direct usage of update + raise NotImplementedError("Direct update to DOK sparse format is not allowed.") + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError( + "_getnnz over an axis is not implemented for DOK format." + ) + return len(self._dict) + + def count_nonzero(self): + return sum(x != 0 for x in self.values()) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def __len__(self): + return len(self._dict) + + def __contains__(self, key): + return key in self._dict + + def setdefault(self, key, default=None, /): + return self._dict.setdefault(key, default) + + def __delitem__(self, key, /): + del self._dict[key] + + def clear(self): + return self._dict.clear() + + def pop(self, /, *args): + return self._dict.pop(*args) + + def __reversed__(self): + raise TypeError("reversed is not defined for dok_array type") + + def __or__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def __ror__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def __ior__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def popitem(self): + return self._dict.popitem() + + def items(self): + return self._dict.items() + + def keys(self): + return self._dict.keys() + + def values(self): + return self._dict.values() + + def get(self, key, default=0.0): + """This provides dict.get method functionality with type checking""" + if key in self._dict: + return self._dict[key] + if isintlike(key) and self.ndim == 1: + key = (key,) + if self.ndim != len(key): + raise IndexError(f'Index {key} length needs to match self.shape') + try: + for i in key: + assert isintlike(i) + except (AssertionError, TypeError, ValueError) as e: + raise IndexError('Index must be or consist of integers.') from e + key = tuple(i + M if i < 0 else i for i, M in zip(key, self.shape)) + if any(i < 0 or i >= M for i, M in zip(key, self.shape)): + raise IndexError('Index out of bounds.') + if self.ndim == 1: + key = key[0] + return self._dict.get(key, default) + + # override IndexMixin.__getitem__ for 1d case until fully implemented + def __getitem__(self, key): + if self.ndim == 2: + return super().__getitem__(key) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._get_int(key) + else: + raise IndexError('array/slice index for 1d dok_array not yet supported') + + # 1D get methods + def _get_int(self, idx): + return self._dict.get(idx, self.dtype.type(0)) + + # 2D get methods + def _get_intXint(self, row, col): + return self._dict.get((row, col), self.dtype.type(0)) + + def _get_intXslice(self, row, col): + return self._get_sliceXslice(slice(row, row + 1), col) + + def _get_sliceXint(self, row, col): + return self._get_sliceXslice(row, slice(col, col + 1)) + + def _get_sliceXslice(self, row, col): + row_start, row_stop, row_step = row.indices(self.shape[0]) + col_start, col_stop, col_step = col.indices(self.shape[1]) + row_range = range(row_start, row_stop, row_step) + col_range = range(col_start, col_stop, col_step) + shape = (len(row_range), len(col_range)) + # Switch paths only when advantageous + # (count the iterations in the loops, adjust for complexity) + if len(self) >= 2 * shape[0] * shape[1]: + # O(nr*nc) path: loop over + return self._get_columnXarray(row_range, col_range) + # O(nnz) path: loop over entries of self + newdok = self._dok_container(shape, dtype=self.dtype) + for key in self.keys(): + i, ri = divmod(int(key[0]) - row_start, row_step) + if ri != 0 or i < 0 or i >= shape[0]: + continue + j, rj = divmod(int(key[1]) - col_start, col_step) + if rj != 0 or j < 0 or j >= shape[1]: + continue + newdok._dict[i, j] = self._dict[key] + return newdok + + def _get_intXarray(self, row, col): + col = col.squeeze() + return self._get_columnXarray([row], col) + + def _get_arrayXint(self, row, col): + row = row.squeeze() + return self._get_columnXarray(row, [col]) + + def _get_sliceXarray(self, row, col): + row = list(range(*row.indices(self.shape[0]))) + return self._get_columnXarray(row, col) + + def _get_arrayXslice(self, row, col): + col = list(range(*col.indices(self.shape[1]))) + return self._get_columnXarray(row, col) + + def _get_columnXarray(self, row, col): + # outer indexing + newdok = self._dok_container((len(row), len(col)), dtype=self.dtype) + + for i, r in enumerate(row): + for j, c in enumerate(col): + v = self._dict.get((r, c), 0) + if v: + newdok._dict[i, j] = v + return newdok + + def _get_arrayXarray(self, row, col): + # inner indexing + i, j = map(np.atleast_2d, np.broadcast_arrays(row, col)) + newdok = self._dok_container(i.shape, dtype=self.dtype) + + for key in itertools.product(range(i.shape[0]), range(i.shape[1])): + v = self._dict.get((i[key], j[key]), 0) + if v: + newdok._dict[key] = v + return newdok + + # override IndexMixin.__setitem__ for 1d case until fully implemented + def __setitem__(self, key, value): + if self.ndim == 2: + return super().__setitem__(key, value) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._set_int(key, value) + else: + raise IndexError('array index for 1d dok_array not yet provided') + + # 1D set methods + def _set_int(self, idx, x): + if x: + self._dict[idx] = x + elif idx in self._dict: + del self._dict[idx] + + # 2D set methods + def _set_intXint(self, row, col, x): + key = (row, col) + if x: + self._dict[key] = x + elif key in self._dict: + del self._dict[key] + + def _set_arrayXarray(self, row, col, x): + row = list(map(int, row.ravel())) + col = list(map(int, col.ravel())) + x = x.ravel() + self._dict.update(zip(zip(row, col), x)) + + for i in np.nonzero(x == 0)[0]: + key = (row[i], col[i]) + if self._dict[key] == 0: + # may have been superseded by later update + del self._dict[key] + + def __add__(self, other): + if isscalarlike(other): + res_dtype = upcast_scalar(self.dtype, other) + new = self._dok_container(self.shape, dtype=res_dtype) + # Add this scalar to each element. + for key in itertools.product(*[range(d) for d in self.shape]): + aij = self._dict.get(key, 0) + other + if aij: + new[key] = aij + elif issparse(other): + if other.shape != self.shape: + raise ValueError("Matrix dimensions are not equal.") + res_dtype = upcast(self.dtype, other.dtype) + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict = self._dict.copy() + if other.format == "dok": + o_items = other.items() + else: + other = other.tocoo() + if self.ndim == 1: + o_items = zip(other.coords[0], other.data) + else: + o_items = zip(zip(*other.coords), other.data) + with np.errstate(over='ignore'): + new._dict.update((k, new[k] + v) for k, v in o_items) + elif isdense(other): + new = self.todense() + other + else: + return NotImplemented + return new + + def __radd__(self, other): + return self + other # addition is comutative + + def __neg__(self): + if self.dtype.kind == 'b': + raise NotImplementedError( + 'Negating a sparse boolean matrix is not supported.' + ) + new = self._dok_container(self.shape, dtype=self.dtype) + new._dict.update((k, -v) for k, v in self.items()) + return new + + def _mul_scalar(self, other): + res_dtype = upcast_scalar(self.dtype, other) + # Multiply this scalar by every element. + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict.update(((k, v * other) for k, v in self.items())) + return new + + def _matmul_vector(self, other): + res_dtype = upcast(self.dtype, other.dtype) + + # vector @ vector + if self.ndim == 1: + if issparse(other): + if other.format == "dok": + keys = self.keys() & other.keys() + else: + keys = self.keys() & other.tocoo().coords[0] + return res_dtype(sum(self._dict[k] * other._dict[k] for k in keys)) + elif isdense(other): + return res_dtype(sum(other[k] * v for k, v in self.items())) + else: + return NotImplemented + + # matrix @ vector + result = np.zeros(self.shape[0], dtype=res_dtype) + for (i, j), v in self.items(): + result[i] += v * other[j] + return result + + def _matmul_multivector(self, other): + result_dtype = upcast(self.dtype, other.dtype) + # vector @ multivector + if self.ndim == 1: + # works for other 1d or 2d + return sum(v * other[j] for j, v in self._dict.items()) + + # matrix @ multivector + M = self.shape[0] + new_shape = (M,) if other.ndim == 1 else (M, other.shape[1]) + result = np.zeros(new_shape, dtype=result_dtype) + for (i, j), v in self.items(): + result[i] += v * other[j] + return result + + def __imul__(self, other): + if isscalarlike(other): + self._dict.update((k, v * other) for k, v in self.items()) + return self + return NotImplemented + + def __truediv__(self, other): + if isscalarlike(other): + res_dtype = upcast_scalar(self.dtype, other) + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict.update(((k, v / other) for k, v in self.items())) + return new + return self.tocsr() / other + + def __itruediv__(self, other): + if isscalarlike(other): + self._dict.update((k, v / other) for k, v in self.items()) + return self + return NotImplemented + + def __reduce__(self): + # this approach is necessary because __setstate__ is called after + # __setitem__ upon unpickling and since __init__ is not called there + # is no shape attribute hence it is not possible to unpickle it. + return dict.__reduce__(self) + + def diagonal(self, k=0): + if self.ndim == 2: + return super().diagonal(k) + raise ValueError("diagonal requires two dimensions") + + def transpose(self, axes=None, copy=False): + if self.ndim == 1: + return self.copy() + + if axes is not None and axes != (1, 0): + raise ValueError( + "Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation." + ) + + M, N = self.shape + new = self._dok_container((N, M), dtype=self.dtype, copy=copy) + new._dict.update((((right, left), val) for (left, right), val in self.items())) + return new + + transpose.__doc__ = _spbase.transpose.__doc__ + + def conjtransp(self): + """Return the conjugate transpose.""" + if self.ndim == 1: + new = self.tocoo() + new.data = new.data.conjugate() + return new + M, N = self.shape + new = self._dok_container((N, M), dtype=self.dtype) + new._dict = {(right, left): np.conj(val) for (left, right), val in self.items()} + return new + + def copy(self): + new = self._dok_container(self.shape, dtype=self.dtype) + new._dict.update(self._dict) + return new + + copy.__doc__ = _spbase.copy.__doc__ + + @classmethod + def fromkeys(cls, iterable, value=1, /): + tmp = dict.fromkeys(iterable, value) + if isinstance(next(iter(tmp)), tuple): + shape = tuple(max(idx) + 1 for idx in zip(*tmp)) + else: + shape = (max(tmp) + 1,) + result = cls(shape, dtype=type(value)) + result._dict = tmp + return result + + def tocoo(self, copy=False): + nnz = self.nnz + if nnz == 0: + return self._coo_container(self.shape, dtype=self.dtype) + + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + data = np.fromiter(self.values(), dtype=self.dtype, count=nnz) + # handle 1d keys specially b/c not a tuple + inds = zip(*self.keys()) if self.ndim > 1 else (self.keys(),) + coords = tuple(np.fromiter(ix, dtype=idx_dtype, count=nnz) for ix in inds) + A = self._coo_container((data, coords), shape=self.shape, dtype=self.dtype) + A.has_canonical_format = True + return A + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def todok(self, copy=False): + if copy: + return self.copy() + return self + + todok.__doc__ = _spbase.todok.__doc__ + + def tocsc(self, copy=False): + if self.ndim == 1: + raise NotImplementedError("tocsr() not valid for 1d sparse array") + return self.tocoo(copy=False).tocsc(copy=copy) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def resize(self, *shape): + is_array = isinstance(self, sparray) + shape = check_shape(shape, allow_1d=is_array) + if len(shape) != len(self.shape): + # TODO implement resize across dimensions + raise NotImplementedError + + if self.ndim == 1: + newN = shape[-1] + for i in list(self._dict): + if i >= newN: + del self._dict[i] + self._shape = shape + return + + newM, newN = shape + M, N = self.shape + if newM < M or newN < N: + # Remove all elements outside new dimensions + for i, j in list(self.keys()): + if i >= newM or j >= newN: + del self._dict[i, j] + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + # Added for 1d to avoid `tocsr` from _base.py + def astype(self, dtype, casting='unsafe', copy=True): + dtype = np.dtype(dtype) + if self.dtype != dtype: + result = self._dok_container(self.shape, dtype=dtype) + data = np.array(list(self._dict.values()), dtype=dtype) + result._dict = dict(zip(self._dict, data)) + return result + elif copy: + return self.copy() + return self + + +def isspmatrix_dok(x): + """Is `x` of dok_array type? + + Parameters + ---------- + x + object to check for being a dok matrix + + Returns + ------- + bool + True if `x` is a dok matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import dok_array, dok_matrix, coo_matrix, isspmatrix_dok + >>> isspmatrix_dok(dok_matrix([[5]])) + True + >>> isspmatrix_dok(dok_array([[5]])) + False + >>> isspmatrix_dok(coo_matrix([[5]])) + False + """ + return isinstance(x, dok_matrix) + + +# This namespace class separates array from matrix with isinstance +class dok_array(_dok_base, sparray): + """ + Dictionary Of Keys based sparse array. + + This is an efficient structure for constructing sparse + arrays incrementally. + + This can be instantiated in several ways: + dok_array(D) + where D is a 2-D ndarray + + dok_array(S) + with another sparse array or matrix S (equivalent to S.todok()) + + dok_array((M,N), [dtype]) + create the array with initial shape (M,N) + dtype is optional, defaulting to dtype='d' + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + Number of nonzero elements + size + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + - Allows for efficient O(1) access of individual elements. + - Duplicates are not allowed. + - Can be efficiently converted to a coo_array once constructed. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import dok_array + >>> S = dok_array((5, 5), dtype=np.float32) + >>> for i in range(5): + ... for j in range(5): + ... S[i, j] = i + j # Update element + + """ + + +class dok_matrix(spmatrix, _dok_base): + """ + Dictionary Of Keys based sparse matrix. + + This is an efficient structure for constructing sparse + matrices incrementally. + + This can be instantiated in several ways: + dok_matrix(D) + where D is a 2-D ndarray + + dok_matrix(S) + with another sparse array or matrix S (equivalent to S.todok()) + + dok_matrix((M,N), [dtype]) + create the matrix with initial shape (M,N) + dtype is optional, defaulting to dtype='d' + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + Number of nonzero elements + size + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + - Allows for efficient O(1) access of individual elements. + - Duplicates are not allowed. + - Can be efficiently converted to a coo_matrix once constructed. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import dok_matrix + >>> S = dok_matrix((5, 5), dtype=np.float32) + >>> for i in range(5): + ... for j in range(5): + ... S[i, j] = i + j # Update element + + """ + + def set_shape(self, shape): + new_matrix = self.reshape(shape, copy=False).asformat(self.format) + self.__dict__ = new_matrix.__dict__ + + def get_shape(self): + """Get shape of a sparse matrix.""" + return self._shape + + shape = property(fget=get_shape, fset=set_shape) + + def __reversed__(self): + return self._dict.__reversed__() + + def __or__(self, other): + if isinstance(other, _dok_base): + return self._dict | other._dict + return self._dict | other + + def __ror__(self, other): + if isinstance(other, _dok_base): + return self._dict | other._dict + return self._dict | other + + def __ior__(self, other): + if isinstance(other, _dok_base): + self._dict |= other._dict + else: + self._dict |= other + return self diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_extract.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_extract.py new file mode 100644 index 0000000000000000000000000000000000000000..349a056bd2f673b0a0b5379fa8b18d720eb5a86d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_extract.py @@ -0,0 +1,178 @@ +"""Functions to extract parts of sparse matrices +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['find', 'tril', 'triu'] + + +from ._coo import coo_matrix, coo_array +from ._base import sparray + + +def find(A): + """Return the indices and values of the nonzero elements of a matrix + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose nonzero elements are desired. + + Returns + ------- + (I,J,V) : tuple of arrays + I,J, and V contain the row indices, column indices, and values + of the nonzero entries. + + + Examples + -------- + >>> from scipy.sparse import csr_array, find + >>> A = csr_array([[7.0, 8.0, 0],[0, 0, 9.0]]) + >>> find(A) + (array([0, 0, 1], dtype=int32), + array([0, 1, 2], dtype=int32), + array([ 7., 8., 9.])) + + """ + + A = coo_array(A, copy=True) + A.sum_duplicates() + # remove explicit zeros + nz_mask = A.data != 0 + return A.row[nz_mask], A.col[nz_mask], A.data[nz_mask] + + +def tril(A, k=0, format=None): + """Return the lower triangular portion of a sparse array or matrix + + Returns the elements on or below the k-th diagonal of A. + - k = 0 corresponds to the main diagonal + - k > 0 is above the main diagonal + - k < 0 is below the main diagonal + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose lower trianglar portion is desired. + k : integer : optional + The top-most diagonal of the lower triangle. + format : string + Sparse format of the result, e.g. format="csr", etc. + + Returns + ------- + L : sparse matrix + Lower triangular portion of A in sparse format. + + See Also + -------- + triu : upper triangle in sparse format + + Examples + -------- + >>> from scipy.sparse import csr_array, tril + >>> A = csr_array([[1, 2, 0, 0, 3], [4, 5, 0, 6, 7], [0, 0, 8, 9, 0]], + ... dtype='int32') + >>> A.toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> tril(A).toarray() + array([[1, 0, 0, 0, 0], + [4, 5, 0, 0, 0], + [0, 0, 8, 0, 0]]) + >>> tril(A).nnz + 4 + >>> tril(A, k=1).toarray() + array([[1, 2, 0, 0, 0], + [4, 5, 0, 0, 0], + [0, 0, 8, 9, 0]]) + >>> tril(A, k=-1).toarray() + array([[0, 0, 0, 0, 0], + [4, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + >>> tril(A, format='csc') + <3x5 sparse array of type '' + with 4 stored elements in Compressed Sparse Column format> + + """ + coo_sparse = coo_array if isinstance(A, sparray) else coo_matrix + + # convert to COOrdinate format where things are easy + A = coo_sparse(A, copy=False) + mask = A.row + k >= A.col + + row = A.row[mask] + col = A.col[mask] + data = A.data[mask] + new_coo = coo_sparse((data, (row, col)), shape=A.shape, dtype=A.dtype) + return new_coo.asformat(format) + + +def triu(A, k=0, format=None): + """Return the upper triangular portion of a sparse array or matrix + + Returns the elements on or above the k-th diagonal of A. + - k = 0 corresponds to the main diagonal + - k > 0 is above the main diagonal + - k < 0 is below the main diagonal + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose upper trianglar portion is desired. + k : integer : optional + The bottom-most diagonal of the upper triangle. + format : string + Sparse format of the result, e.g. format="csr", etc. + + Returns + ------- + L : sparse array or matrix + Upper triangular portion of A in sparse format. + Sparse array if A is a sparse array, otherwise matrix. + + See Also + -------- + tril : lower triangle in sparse format + + Examples + -------- + >>> from scipy.sparse import csr_array, triu + >>> A = csr_array([[1, 2, 0, 0, 3], [4, 5, 0, 6, 7], [0, 0, 8, 9, 0]], + ... dtype='int32') + >>> A.toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A).toarray() + array([[1, 2, 0, 0, 3], + [0, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A).nnz + 8 + >>> triu(A, k=1).toarray() + array([[0, 2, 0, 0, 3], + [0, 0, 0, 6, 7], + [0, 0, 0, 9, 0]]) + >>> triu(A, k=-1).toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A, format='csc') + <3x5 sparse array of type '' + with 8 stored elements in Compressed Sparse Column format> + + """ + coo_sparse = coo_array if isinstance(A, sparray) else coo_matrix + + # convert to COOrdinate format where things are easy + A = coo_sparse(A, copy=False) + mask = A.row + k <= A.col + + row = A.row[mask] + col = A.col[mask] + data = A.data[mask] + new_coo = coo_sparse((data, (row, col)), shape=A.shape, dtype=A.dtype) + return new_coo.asformat(format) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_index.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_index.py new file mode 100644 index 0000000000000000000000000000000000000000..c0fc3d01b0ebd153703a76af431626d958b7de64 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_index.py @@ -0,0 +1,392 @@ +"""Indexing mixin for sparse array/matrix classes. +""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +from ._sputils import isintlike + +if TYPE_CHECKING: + import numpy.typing as npt + +INT_TYPES = (int, np.integer) + + +def _broadcast_arrays(a, b): + """ + Same as np.broadcast_arrays(a, b) but old writeability rules. + + NumPy >= 1.17.0 transitions broadcast_arrays to return + read-only arrays. Set writeability explicitly to avoid warnings. + Retain the old writeability rules, as our Cython code assumes + the old behavior. + """ + x, y = np.broadcast_arrays(a, b) + x.flags.writeable = a.flags.writeable + y.flags.writeable = b.flags.writeable + return x, y + + +class IndexMixin: + """ + This class provides common dispatching and validation logic for indexing. + """ + def _raise_on_1d_array_slice(self): + """We do not currently support 1D sparse arrays. + + This function is called each time that a 1D array would + result, raising an error instead. + + Once 1D sparse arrays are implemented, it should be removed. + """ + from scipy.sparse import sparray + + if isinstance(self, sparray): + raise NotImplementedError( + 'We have not yet implemented 1D sparse slices; ' + 'please index using explicit indices, e.g. `x[:, [0]]`' + ) + + def __getitem__(self, key): + row, col = self._validate_indices(key) + + # Dispatch to specialized methods. + if isinstance(row, INT_TYPES): + if isinstance(col, INT_TYPES): + return self._get_intXint(row, col) + elif isinstance(col, slice): + self._raise_on_1d_array_slice() + return self._get_intXslice(row, col) + elif col.ndim == 1: + self._raise_on_1d_array_slice() + return self._get_intXarray(row, col) + elif col.ndim == 2: + return self._get_intXarray(row, col) + raise IndexError('index results in >2 dimensions') + elif isinstance(row, slice): + if isinstance(col, INT_TYPES): + self._raise_on_1d_array_slice() + return self._get_sliceXint(row, col) + elif isinstance(col, slice): + if row == slice(None) and row == col: + return self.copy() + return self._get_sliceXslice(row, col) + elif col.ndim == 1: + return self._get_sliceXarray(row, col) + raise IndexError('index results in >2 dimensions') + elif row.ndim == 1: + if isinstance(col, INT_TYPES): + self._raise_on_1d_array_slice() + return self._get_arrayXint(row, col) + elif isinstance(col, slice): + return self._get_arrayXslice(row, col) + else: # row.ndim == 2 + if isinstance(col, INT_TYPES): + return self._get_arrayXint(row, col) + elif isinstance(col, slice): + raise IndexError('index results in >2 dimensions') + elif row.shape[1] == 1 and (col.ndim == 1 or col.shape[0] == 1): + # special case for outer indexing + return self._get_columnXarray(row[:,0], col.ravel()) + + # The only remaining case is inner (fancy) indexing + row, col = _broadcast_arrays(row, col) + if row.shape != col.shape: + raise IndexError('number of row and column indices differ') + if row.size == 0: + return self.__class__(np.atleast_2d(row).shape, dtype=self.dtype) + return self._get_arrayXarray(row, col) + + def __setitem__(self, key, x): + row, col = self._validate_indices(key) + + if isinstance(row, INT_TYPES) and isinstance(col, INT_TYPES): + x = np.asarray(x, dtype=self.dtype) + if x.size != 1: + raise ValueError('Trying to assign a sequence to an item') + self._set_intXint(row, col, x.flat[0]) + return + + if isinstance(row, slice): + row = np.arange(*row.indices(self.shape[0]))[:, None] + else: + row = np.atleast_1d(row) + + if isinstance(col, slice): + col = np.arange(*col.indices(self.shape[1]))[None, :] + if row.ndim == 1: + row = row[:, None] + else: + col = np.atleast_1d(col) + + i, j = _broadcast_arrays(row, col) + if i.shape != j.shape: + raise IndexError('number of row and column indices differ') + + from ._base import issparse + if issparse(x): + if i.ndim == 1: + # Inner indexing, so treat them like row vectors. + i = i[None] + j = j[None] + broadcast_row = x.shape[0] == 1 and i.shape[0] != 1 + broadcast_col = x.shape[1] == 1 and i.shape[1] != 1 + if not ((broadcast_row or x.shape[0] == i.shape[0]) and + (broadcast_col or x.shape[1] == i.shape[1])): + raise ValueError('shape mismatch in assignment') + if x.shape[0] == 0 or x.shape[1] == 0: + return + x = x.tocoo(copy=True) + x.sum_duplicates() + self._set_arrayXarray_sparse(i, j, x) + else: + # Make x and i into the same shape + x = np.asarray(x, dtype=self.dtype) + if x.squeeze().shape != i.squeeze().shape: + x = np.broadcast_to(x, i.shape) + if x.size == 0: + return + x = x.reshape(i.shape) + self._set_arrayXarray(i, j, x) + + def _validate_indices(self, key): + # First, check if indexing with single boolean matrix. + from ._base import _spbase + if (isinstance(key, (_spbase, np.ndarray)) and + key.ndim == 2 and key.dtype.kind == 'b'): + if key.shape != self.shape: + raise IndexError('boolean index shape does not match array shape') + row, col = key.nonzero() + else: + row, col = _unpack_index(key) + M, N = self.shape + + def _validate_bool_idx( + idx: npt.NDArray[np.bool_], + axis_size: int, + axis_name: str + ) -> npt.NDArray[np.int_]: + if len(idx) != axis_size: + raise IndexError( + f"boolean {axis_name} index has incorrect length: {len(idx)} " + f"instead of {axis_size}" + ) + return _boolean_index_to_array(idx) + + if isintlike(row): + row = int(row) + if row < -M or row >= M: + raise IndexError('row index (%d) out of range' % row) + if row < 0: + row += M + elif (bool_row := _compatible_boolean_index(row)) is not None: + row = _validate_bool_idx(bool_row, M, "row") + elif not isinstance(row, slice): + row = self._asindices(row, M) + + if isintlike(col): + col = int(col) + if col < -N or col >= N: + raise IndexError('column index (%d) out of range' % col) + if col < 0: + col += N + elif (bool_col := _compatible_boolean_index(col)) is not None: + col = _validate_bool_idx(bool_col, N, "column") + elif not isinstance(col, slice): + col = self._asindices(col, N) + + return row, col + + def _asindices(self, idx, length): + """Convert `idx` to a valid index for an axis with a given length. + + Subclasses that need special validation can override this method. + """ + try: + x = np.asarray(idx) + except (ValueError, TypeError, MemoryError) as e: + raise IndexError('invalid index') from e + + if x.ndim not in (1, 2): + raise IndexError('Index dimension must be 1 or 2') + + if x.size == 0: + return x + + # Check bounds + max_indx = x.max() + if max_indx >= length: + raise IndexError('index (%d) out of range' % max_indx) + + min_indx = x.min() + if min_indx < 0: + if min_indx < -length: + raise IndexError('index (%d) out of range' % min_indx) + if x is idx or not x.flags.owndata: + x = x.copy() + x[x < 0] += length + return x + + def _getrow(self, i): + """Return a copy of row i of the matrix, as a (1 x n) row vector. + """ + M, N = self.shape + i = int(i) + if i < -M or i >= M: + raise IndexError('index (%d) out of range' % i) + if i < 0: + i += M + return self._get_intXslice(i, slice(None)) + + def _getcol(self, i): + """Return a copy of column i of the matrix, as a (m x 1) column vector. + """ + M, N = self.shape + i = int(i) + if i < -N or i >= N: + raise IndexError('index (%d) out of range' % i) + if i < 0: + i += N + return self._get_sliceXint(slice(None), i) + + def _get_intXint(self, row, col): + raise NotImplementedError() + + def _get_intXarray(self, row, col): + raise NotImplementedError() + + def _get_intXslice(self, row, col): + raise NotImplementedError() + + def _get_sliceXint(self, row, col): + raise NotImplementedError() + + def _get_sliceXslice(self, row, col): + raise NotImplementedError() + + def _get_sliceXarray(self, row, col): + raise NotImplementedError() + + def _get_arrayXint(self, row, col): + raise NotImplementedError() + + def _get_arrayXslice(self, row, col): + raise NotImplementedError() + + def _get_columnXarray(self, row, col): + raise NotImplementedError() + + def _get_arrayXarray(self, row, col): + raise NotImplementedError() + + def _set_intXint(self, row, col, x): + raise NotImplementedError() + + def _set_arrayXarray(self, row, col, x): + raise NotImplementedError() + + def _set_arrayXarray_sparse(self, row, col, x): + # Fall back to densifying x + x = np.asarray(x.toarray(), dtype=self.dtype) + x, _ = _broadcast_arrays(x, row) + self._set_arrayXarray(row, col, x) + + +def _unpack_index(index) -> tuple[ + int | slice | npt.NDArray[np.bool_ | np.int_], + int | slice | npt.NDArray[np.bool_ | np.int_] +]: + """ Parse index. Always return a tuple of the form (row, col). + Valid type for row/col is integer, slice, array of bool, or array of integers. + """ + # Parse any ellipses. + index = _check_ellipsis(index) + + # Next, parse the tuple or object + if isinstance(index, tuple): + if len(index) == 2: + row, col = index + elif len(index) == 1: + row, col = index[0], slice(None) + else: + raise IndexError('invalid number of indices') + else: + idx = _compatible_boolean_index(index) + if idx is None: + row, col = index, slice(None) + elif idx.ndim < 2: + return idx, slice(None) + elif idx.ndim == 2: + return idx.nonzero() + # Next, check for validity and transform the index as needed. + from ._base import issparse + if issparse(row) or issparse(col): + # Supporting sparse boolean indexing with both row and col does + # not work because spmatrix.ndim is always 2. + raise IndexError( + 'Indexing with sparse matrices is not supported ' + 'except boolean indexing where matrix and index ' + 'are equal shapes.') + return row, col + + +def _check_ellipsis(index): + """Process indices with Ellipsis. Returns modified index.""" + if index is Ellipsis: + return (slice(None), slice(None)) + + if not isinstance(index, tuple): + return index + + # Find any Ellipsis objects. + ellipsis_indices = [i for i, v in enumerate(index) if v is Ellipsis] + if not ellipsis_indices: + return index + if len(ellipsis_indices) > 1: + raise IndexError("an index can only have a single ellipsis ('...')") + + # Replace the Ellipsis object with 0, 1, or 2 null-slices as needed. + i, = ellipsis_indices + num_slices = max(0, 3 - len(index)) + return index[:i] + (slice(None),) * num_slices + index[i + 1:] + + +def _maybe_bool_ndarray(idx): + """Returns a compatible array if elements are boolean. + """ + idx = np.asanyarray(idx) + if idx.dtype.kind == 'b': + return idx + return None + + +def _first_element_bool(idx, max_dim=2): + """Returns True if first element of the incompatible + array type is boolean. + """ + if max_dim < 1: + return None + try: + first = next(iter(idx), None) + except TypeError: + return None + if isinstance(first, bool): + return True + return _first_element_bool(first, max_dim-1) + + +def _compatible_boolean_index(idx): + """Returns a boolean index array that can be converted to + integer array. Returns None if no such array exists. + """ + # Presence of attribute `ndim` indicates a compatible array type. + if hasattr(idx, 'ndim') or _first_element_bool(idx): + return _maybe_bool_ndarray(idx) + return None + + +def _boolean_index_to_array(idx): + if idx.ndim > 1: + raise IndexError('invalid index shape') + return np.where(idx)[0] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_lil.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_lil.py new file mode 100644 index 0000000000000000000000000000000000000000..e267935fc980fd2a228165628f595af0865dda21 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_lil.py @@ -0,0 +1,619 @@ +"""List of Lists sparse matrix class +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['lil_array', 'lil_matrix', 'isspmatrix_lil'] + +from bisect import bisect_left + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray, issparse +from ._index import IndexMixin, INT_TYPES, _broadcast_arrays +from ._sputils import (getdtype, isshape, isscalarlike, upcast_scalar, + check_shape, check_reshape_kwargs) +from . import _csparsetools + + +class _lil_base(_spbase, IndexMixin): + _format = 'lil' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _spbase.__init__(self) + self.dtype = getdtype(dtype, arg1, default=float) + + # First get the shape + if issparse(arg1): + if arg1.format == "lil" and copy: + A = arg1.copy() + else: + A = arg1.tolil() + + if dtype is not None: + A = A.astype(dtype, copy=False) + + self._shape = check_shape(A.shape) + self.dtype = A.dtype + self.rows = A.rows + self.data = A.data + elif isinstance(arg1,tuple): + if isshape(arg1): + if shape is not None: + raise ValueError('invalid use of shape parameter') + M, N = arg1 + self._shape = check_shape((M, N)) + self.rows = np.empty((M,), dtype=object) + self.data = np.empty((M,), dtype=object) + for i in range(M): + self.rows[i] = [] + self.data[i] = [] + else: + raise TypeError('unrecognized lil_array constructor usage') + else: + # assume A is dense + try: + A = self._ascontainer(arg1) + except TypeError as e: + raise TypeError('unsupported matrix type') from e + if isinstance(self, sparray) and A.ndim != 2: + raise ValueError(f"LIL arrays don't support {A.ndim}D input. Use 2D") + A = self._csr_container(A, dtype=dtype).tolil() + + self._shape = check_shape(A.shape) + self.dtype = A.dtype + self.rows = A.rows + self.data = A.data + + def __iadd__(self,other): + self[:,:] = self + other + return self + + def __isub__(self,other): + self[:,:] = self - other + return self + + def __imul__(self,other): + if isscalarlike(other): + self[:,:] = self * other + return self + else: + return NotImplemented + + def __itruediv__(self,other): + if isscalarlike(other): + self[:,:] = self / other + return self + else: + return NotImplemented + + # Whenever the dimensions change, empty lists should be created for each + # row + + def _getnnz(self, axis=None): + if axis is None: + return sum([len(rowvals) for rowvals in self.data]) + if axis < 0: + axis += 2 + if axis == 0: + out = np.zeros(self.shape[1], dtype=np.intp) + for row in self.rows: + out[row] += 1 + return out + elif axis == 1: + return np.array([len(rowvals) for rowvals in self.data], dtype=np.intp) + else: + raise ValueError('axis out of bounds') + + def count_nonzero(self): + return sum(np.count_nonzero(rowvals) for rowvals in self.data) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def __str__(self): + val = '' + for i, row in enumerate(self.rows): + for pos, j in enumerate(row): + val += f" {str((i, j))}\t{str(self.data[i][pos])}\n" + return val[:-1] + + def getrowview(self, i): + """Returns a view of the 'i'th row (without copying). + """ + new = self._lil_container((1, self.shape[1]), dtype=self.dtype) + new.rows[0] = self.rows[i] + new.data[0] = self.data[i] + return new + + def getrow(self, i): + """Returns a copy of the 'i'th row. + """ + M, N = self.shape + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('row index out of bounds') + new = self._lil_container((1, N), dtype=self.dtype) + new.rows[0] = self.rows[i][:] + new.data[0] = self.data[i][:] + return new + + def __getitem__(self, key): + # Fast path for simple (int, int) indexing. + if (isinstance(key, tuple) and len(key) == 2 and + isinstance(key[0], INT_TYPES) and + isinstance(key[1], INT_TYPES)): + # lil_get1 handles validation for us. + return self._get_intXint(*key) + # Everything else takes the normal path. + return IndexMixin.__getitem__(self, key) + + def _asindices(self, idx, N): + # LIL routines handle bounds-checking for us, so don't do it here. + try: + x = np.asarray(idx) + except (ValueError, TypeError, MemoryError) as e: + raise IndexError('invalid index') from e + if x.ndim not in (1, 2): + raise IndexError('Index dimension must be <= 2') + return x + + def _get_intXint(self, row, col): + v = _csparsetools.lil_get1(self.shape[0], self.shape[1], self.rows, + self.data, row, col) + return self.dtype.type(v) + + def _get_sliceXint(self, row, col): + row = range(*row.indices(self.shape[0])) + return self._get_row_ranges(row, slice(col, col+1)) + + def _get_arrayXint(self, row, col): + row = row.squeeze() + return self._get_row_ranges(row, slice(col, col+1)) + + def _get_intXslice(self, row, col): + return self._get_row_ranges((row,), col) + + def _get_sliceXslice(self, row, col): + row = range(*row.indices(self.shape[0])) + return self._get_row_ranges(row, col) + + def _get_arrayXslice(self, row, col): + return self._get_row_ranges(row, col) + + def _get_intXarray(self, row, col): + row = np.array(row, dtype=col.dtype, ndmin=1) + return self._get_columnXarray(row, col) + + def _get_sliceXarray(self, row, col): + row = np.arange(*row.indices(self.shape[0])) + return self._get_columnXarray(row, col) + + def _get_columnXarray(self, row, col): + # outer indexing + row, col = _broadcast_arrays(row[:,None], col) + return self._get_arrayXarray(row, col) + + def _get_arrayXarray(self, row, col): + # inner indexing + i, j = map(np.atleast_2d, _prepare_index_for_memoryview(row, col)) + new = self._lil_container(i.shape, dtype=self.dtype) + _csparsetools.lil_fancy_get(self.shape[0], self.shape[1], + self.rows, self.data, + new.rows, new.data, + i, j) + return new + + def _get_row_ranges(self, rows, col_slice): + """ + Fast path for indexing in the case where column index is slice. + + This gains performance improvement over brute force by more + efficient skipping of zeros, by accessing the elements + column-wise in order. + + Parameters + ---------- + rows : sequence or range + Rows indexed. If range, must be within valid bounds. + col_slice : slice + Columns indexed + + """ + j_start, j_stop, j_stride = col_slice.indices(self.shape[1]) + col_range = range(j_start, j_stop, j_stride) + nj = len(col_range) + new = self._lil_container((len(rows), nj), dtype=self.dtype) + + _csparsetools.lil_get_row_ranges(self.shape[0], self.shape[1], + self.rows, self.data, + new.rows, new.data, + rows, + j_start, j_stop, j_stride, nj) + + return new + + def _set_intXint(self, row, col, x): + _csparsetools.lil_insert(self.shape[0], self.shape[1], self.rows, + self.data, row, col, x) + + def _set_arrayXarray(self, row, col, x): + i, j, x = map(np.atleast_2d, _prepare_index_for_memoryview(row, col, x)) + _csparsetools.lil_fancy_set(self.shape[0], self.shape[1], + self.rows, self.data, + i, j, x) + + def _set_arrayXarray_sparse(self, row, col, x): + # Fall back to densifying x + x = np.asarray(x.toarray(), dtype=self.dtype) + x, _ = _broadcast_arrays(x, row) + self._set_arrayXarray(row, col, x) + + def __setitem__(self, key, x): + if isinstance(key, tuple) and len(key) == 2: + row, col = key + # Fast path for simple (int, int) indexing. + if isinstance(row, INT_TYPES) and isinstance(col, INT_TYPES): + x = self.dtype.type(x) + if x.size > 1: + raise ValueError("Trying to assign a sequence to an item") + return self._set_intXint(row, col, x) + # Fast path for full-matrix sparse assignment. + if (isinstance(row, slice) and isinstance(col, slice) and + row == slice(None) and col == slice(None) and + issparse(x) and x.shape == self.shape): + x = self._lil_container(x, dtype=self.dtype) + self.rows = x.rows + self.data = x.data + return + # Everything else takes the normal path. + IndexMixin.__setitem__(self, key, x) + + def _mul_scalar(self, other): + if other == 0: + # Multiply by zero: return the zero matrix + new = self._lil_container(self.shape, dtype=self.dtype) + else: + res_dtype = upcast_scalar(self.dtype, other) + + new = self.copy() + new = new.astype(res_dtype) + # Multiply this scalar by every element. + for j, rowvals in enumerate(new.data): + new.data[j] = [val*other for val in rowvals] + return new + + def __truediv__(self, other): # self / other + if isscalarlike(other): + new = self.copy() + new.dtype = np.result_type(self, other) + # Divide every element by this scalar + for j, rowvals in enumerate(new.data): + new.data[j] = [val/other for val in rowvals] + return new + else: + return self.tocsr() / other + + def copy(self): + M, N = self.shape + new = self._lil_container(self.shape, dtype=self.dtype) + # This is ~14x faster than calling deepcopy() on rows and data. + _csparsetools.lil_get_row_ranges(M, N, self.rows, self.data, + new.rows, new.data, range(M), + 0, N, 1, N) + return new + + copy.__doc__ = _spbase.copy.__doc__ + + def reshape(self, *args, **kwargs): + shape = check_shape(args, self.shape) + order, copy = check_reshape_kwargs(kwargs) + + # Return early if reshape is not required + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + new = self._lil_container(shape, dtype=self.dtype) + + if order == 'C': + ncols = self.shape[1] + for i, row in enumerate(self.rows): + for col, j in enumerate(row): + new_r, new_c = np.unravel_index(i * ncols + j, shape) + new[new_r, new_c] = self[i, j] + elif order == 'F': + nrows = self.shape[0] + for i, row in enumerate(self.rows): + for col, j in enumerate(row): + new_r, new_c = np.unravel_index(i + j * nrows, shape, order) + new[new_r, new_c] = self[i, j] + else: + raise ValueError("'order' must be 'C' or 'F'") + + return new + + reshape.__doc__ = _spbase.reshape.__doc__ + + def resize(self, *shape): + shape = check_shape(shape) + new_M, new_N = shape + M, N = self.shape + + if new_M < M: + self.rows = self.rows[:new_M] + self.data = self.data[:new_M] + elif new_M > M: + self.rows = np.resize(self.rows, new_M) + self.data = np.resize(self.data, new_M) + for i in range(M, new_M): + self.rows[i] = [] + self.data[i] = [] + + if new_N < N: + for row, data in zip(self.rows, self.data): + trunc = bisect_left(row, new_N) + del row[trunc:] + del data[trunc:] + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + def toarray(self, order=None, out=None): + d = self._process_toarray_args(order, out) + for i, row in enumerate(self.rows): + for pos, j in enumerate(row): + d[i, j] = self.data[i][pos] + return d + + toarray.__doc__ = _spbase.toarray.__doc__ + + def transpose(self, axes=None, copy=False): + return self.tocsr(copy=copy).transpose(axes=axes, copy=False).tolil(copy=False) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def tolil(self, copy=False): + if copy: + return self.copy() + else: + return self + + tolil.__doc__ = _spbase.tolil.__doc__ + + def tocsr(self, copy=False): + M, N = self.shape + if M == 0 or N == 0: + return self._csr_container((M, N), dtype=self.dtype) + + # construct indptr array + if M*N <= np.iinfo(np.int32).max: + # fast path: it is known that 64-bit indexing will not be needed. + idx_dtype = np.int32 + indptr = np.empty(M + 1, dtype=idx_dtype) + indptr[0] = 0 + _csparsetools.lil_get_lengths(self.rows, indptr[1:]) + np.cumsum(indptr, out=indptr) + nnz = indptr[-1] + else: + idx_dtype = self._get_index_dtype(maxval=N) + lengths = np.empty(M, dtype=idx_dtype) + _csparsetools.lil_get_lengths(self.rows, lengths) + nnz = lengths.sum(dtype=np.int64) + idx_dtype = self._get_index_dtype(maxval=max(N, nnz)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indptr[0] = 0 + np.cumsum(lengths, dtype=idx_dtype, out=indptr[1:]) + + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=self.dtype) + _csparsetools.lil_flatten_to_array(self.rows, indices) + _csparsetools.lil_flatten_to_array(self.data, data) + + # init csr matrix + return self._csr_container((data, indices, indptr), shape=self.shape) + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + +def _prepare_index_for_memoryview(i, j, x=None): + """ + Convert index and data arrays to form suitable for passing to the + Cython fancy getset routines. + + The conversions are necessary since to (i) ensure the integer + index arrays are in one of the accepted types, and (ii) to ensure + the arrays are writable so that Cython memoryview support doesn't + choke on them. + + Parameters + ---------- + i, j + Index arrays + x : optional + Data arrays + + Returns + ------- + i, j, x + Re-formatted arrays (x is omitted, if input was None) + + """ + if i.dtype > j.dtype: + j = j.astype(i.dtype) + elif i.dtype < j.dtype: + i = i.astype(j.dtype) + + if not i.flags.writeable or i.dtype not in (np.int32, np.int64): + i = i.astype(np.intp) + if not j.flags.writeable or j.dtype not in (np.int32, np.int64): + j = j.astype(np.intp) + + if x is not None: + if not x.flags.writeable: + x = x.copy() + return i, j, x + else: + return i, j + + +def isspmatrix_lil(x): + """Is `x` of lil_matrix type? + + Parameters + ---------- + x + object to check for being a lil matrix + + Returns + ------- + bool + True if `x` is a lil matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import lil_array, lil_matrix, coo_matrix, isspmatrix_lil + >>> isspmatrix_lil(lil_matrix([[5]])) + True + >>> isspmatrix_lil(lil_array([[5]])) + False + >>> isspmatrix_lil(coo_matrix([[5]])) + False + """ + return isinstance(x, lil_matrix) + + +# This namespace class separates array from matrix with isinstance +class lil_array(_lil_base, sparray): + """ + Row-based LIst of Lists sparse array. + + This is a structure for constructing sparse arrays incrementally. + Note that inserting a single item can take linear time in the worst case; + to construct the array efficiently, make sure the items are pre-sorted by + index, per row. + + This can be instantiated in several ways: + lil_array(D) + where D is a 2-D ndarray + + lil_array(S) + with another sparse array or matrix S (equivalent to S.tolil()) + + lil_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + LIL format data array of the array + rows + LIL format row index array of the array + T + + Notes + ----- + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the LIL format + - supports flexible slicing + - changes to the array sparsity structure are efficient + + Disadvantages of the LIL format + - arithmetic operations LIL + LIL are slow (consider CSR or CSC) + - slow column slicing (consider CSC) + - slow matrix vector products (consider CSR or CSC) + + Intended Usage + - LIL is a convenient format for constructing sparse arrays + - once an array has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - consider using the COO format when constructing large arrays + + Data Structure + - An array (``self.rows``) of rows, each of which is a sorted + list of column indices of non-zero elements. + - The corresponding nonzero values are stored in similar + fashion in ``self.data``. + + """ + + +class lil_matrix(spmatrix, _lil_base): + """ + Row-based LIst of Lists sparse matrix. + + This is a structure for constructing sparse matrices incrementally. + Note that inserting a single item can take linear time in the worst case; + to construct the matrix efficiently, make sure the items are pre-sorted by + index, per row. + + This can be instantiated in several ways: + lil_matrix(D) + where D is a 2-D ndarray + + lil_matrix(S) + with another sparse array or matrix S (equivalent to S.tolil()) + + lil_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + LIL format data array of the matrix + rows + LIL format row index array of the matrix + T + + Notes + ----- + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the LIL format + - supports flexible slicing + - changes to the matrix sparsity structure are efficient + + Disadvantages of the LIL format + - arithmetic operations LIL + LIL are slow (consider CSR or CSC) + - slow column slicing (consider CSC) + - slow matrix vector products (consider CSR or CSC) + + Intended Usage + - LIL is a convenient format for constructing sparse matrices + - once a matrix has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - consider using the COO format when constructing large matrices + + Data Structure + - An array (``self.rows``) of rows, each of which is a sorted + list of column indices of non-zero elements. + - The corresponding nonzero values are stored in similar + fashion in ``self.data``. + + """ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix.py new file mode 100644 index 0000000000000000000000000000000000000000..1ab8749423833b78f7efc17feb6e1a8e6405408a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix.py @@ -0,0 +1,113 @@ +class spmatrix: + """This class provides a base class for all sparse matrix classes. + + It cannot be instantiated. Most of the work is provided by subclasses. + """ + + @property + def _bsr_container(self): + from ._bsr import bsr_matrix + return bsr_matrix + + @property + def _coo_container(self): + from ._coo import coo_matrix + return coo_matrix + + @property + def _csc_container(self): + from ._csc import csc_matrix + return csc_matrix + + @property + def _csr_container(self): + from ._csr import csr_matrix + return csr_matrix + + @property + def _dia_container(self): + from ._dia import dia_matrix + return dia_matrix + + @property + def _dok_container(self): + from ._dok import dok_matrix + return dok_matrix + + @property + def _lil_container(self): + from ._lil import lil_matrix + return lil_matrix + + # Restore matrix multiplication + def __mul__(self, other): + return self._matmul_dispatch(other) + + def __rmul__(self, other): + return self._rmatmul_dispatch(other) + + # Restore matrix power + def __pow__(self, power): + from .linalg import matrix_power + + return matrix_power(self, power) + + ## Backward compatibility + + def set_shape(self, shape): + """Set the shape of the matrix in-place""" + # Make sure copy is False since this is in place + # Make sure format is unchanged because we are doing a __dict__ swap + new_self = self.reshape(shape, copy=False).asformat(self.format) + self.__dict__ = new_self.__dict__ + + def get_shape(self): + """Get the shape of the matrix""" + return self._shape + + shape = property(fget=get_shape, fset=set_shape, + doc="Shape of the matrix") + + def asfptype(self): + """Upcast matrix to a floating point format (if necessary)""" + return self._asfptype() + + def getmaxprint(self): + """Maximum number of elements to display when printed.""" + return self._getmaxprint() + + def getformat(self): + """Matrix storage format""" + return self.format + + def getnnz(self, axis=None): + """Number of stored values, including explicit zeros. + + Parameters + ---------- + axis : None, 0, or 1 + Select between the number of values across the whole array, in + each column, or in each row. + """ + return self._getnnz(axis=axis) + + def getH(self): + """Return the Hermitian transpose of this matrix. + + See Also + -------- + numpy.matrix.getH : NumPy's implementation of `getH` for matrices + """ + return self.conjugate().transpose() + + def getcol(self, j): + """Returns a copy of column j of the matrix, as an (m x 1) sparse + matrix (column vector). + """ + return self._getcol(j) + + def getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) sparse + matrix (row vector). + """ + return self._getrow(i) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix_io.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix_io.py new file mode 100644 index 0000000000000000000000000000000000000000..e115260afb9f625a68f6b14d8750d3650603bd11 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_matrix_io.py @@ -0,0 +1,167 @@ +import numpy as np +import scipy as sp + +__all__ = ['save_npz', 'load_npz'] + + +# Make loading safe vs. malicious input +PICKLE_KWARGS = dict(allow_pickle=False) + + +def save_npz(file, matrix, compressed=True): + """ Save a sparse matrix or array to a file using ``.npz`` format. + + Parameters + ---------- + file : str or file-like object + Either the file name (string) or an open file (file-like object) + where the data will be saved. If file is a string, the ``.npz`` + extension will be appended to the file name if it is not already + there. + matrix: spmatrix or sparray + The sparse matrix or array to save. + Supported formats: ``csc``, ``csr``, ``bsr``, ``dia`` or ``coo``. + compressed : bool, optional + Allow compressing the file. Default: True + + See Also + -------- + scipy.sparse.load_npz: Load a sparse matrix from a file using ``.npz`` format. + numpy.savez: Save several arrays into a ``.npz`` archive. + numpy.savez_compressed : Save several arrays into a compressed ``.npz`` archive. + + Examples + -------- + Store sparse matrix to disk, and load it again: + + >>> import numpy as np + >>> import scipy as sp + >>> sparse_matrix = sp.sparse.csc_matrix([[0, 0, 3], [4, 0, 0]]) + >>> sparse_matrix + <2x3 sparse matrix of type '' + with 2 stored elements in Compressed Sparse Column format> + >>> sparse_matrix.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + >>> sp.sparse.save_npz('/tmp/sparse_matrix.npz', sparse_matrix) + >>> sparse_matrix = sp.sparse.load_npz('/tmp/sparse_matrix.npz') + + >>> sparse_matrix + <2x3 sparse matrix of type '' + with 2 stored elements in Compressed Sparse Column format> + >>> sparse_matrix.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + """ + arrays_dict = {} + if matrix.format in ('csc', 'csr', 'bsr'): + arrays_dict.update(indices=matrix.indices, indptr=matrix.indptr) + elif matrix.format == 'dia': + arrays_dict.update(offsets=matrix.offsets) + elif matrix.format == 'coo': + arrays_dict.update(row=matrix.row, col=matrix.col) + else: + msg = f'Save is not implemented for sparse matrix of format {matrix.format}.' + raise NotImplementedError(msg) + arrays_dict.update( + format=matrix.format.encode('ascii'), + shape=matrix.shape, + data=matrix.data + ) + if isinstance(matrix, sp.sparse.sparray): + arrays_dict.update(_is_array=True) + if compressed: + np.savez_compressed(file, **arrays_dict) + else: + np.savez(file, **arrays_dict) + + +def load_npz(file): + """ Load a sparse array/matrix from a file using ``.npz`` format. + + Parameters + ---------- + file : str or file-like object + Either the file name (string) or an open file (file-like object) + where the data will be loaded. + + Returns + ------- + result : csc_array, csr_array, bsr_array, dia_array or coo_array + A sparse array/matrix containing the loaded data. + + Raises + ------ + OSError + If the input file does not exist or cannot be read. + + See Also + -------- + scipy.sparse.save_npz: Save a sparse array/matrix to a file using ``.npz`` format. + numpy.load: Load several arrays from a ``.npz`` archive. + + Examples + -------- + Store sparse array/matrix to disk, and load it again: + + >>> import numpy as np + >>> import scipy as sp + >>> sparse_array = sp.sparse.csc_array([[0, 0, 3], [4, 0, 0]]) + >>> sparse_array + <2x3 sparse array of type '' + with 2 stored elements in Compressed Sparse Column format> + >>> sparse_array.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + >>> sp.sparse.save_npz('/tmp/sparse_array.npz', sparse_array) + >>> sparse_array = sp.sparse.load_npz('/tmp/sparse_array.npz') + + >>> sparse_array + <2x3 sparse array of type '' + with 2 stored elements in Compressed Sparse Column format> + >>> sparse_array.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + In this example we force the result to be csr_array from csr_matrix + >>> sparse_matrix = sp.sparse.csc_matrix([[0, 0, 3], [4, 0, 0]]) + >>> sp.sparse.save_npz('/tmp/sparse_matrix.npz', sparse_matrix) + >>> tmp = sp.sparse.load_npz('/tmp/sparse_matrix.npz') + >>> sparse_array = sp.sparse.csr_array(tmp) + """ + with np.load(file, **PICKLE_KWARGS) as loaded: + sparse_format = loaded.get('format') + if sparse_format is None: + raise ValueError(f'The file {file} does not contain ' + f'a sparse array or matrix.') + sparse_format = sparse_format.item() + + if not isinstance(sparse_format, str): + # Play safe with Python 2 vs 3 backward compatibility; + # files saved with SciPy < 1.0.0 may contain unicode or bytes. + sparse_format = sparse_format.decode('ascii') + + if loaded.get('_is_array'): + sparse_type = sparse_format + '_array' + else: + sparse_type = sparse_format + '_matrix' + + try: + cls = getattr(sp.sparse, f'{sparse_type}') + except AttributeError as e: + raise ValueError(f'Unknown format "{sparse_type}"') from e + + if sparse_format in ('csc', 'csr', 'bsr'): + return cls((loaded['data'], loaded['indices'], loaded['indptr']), + shape=loaded['shape']) + elif sparse_format == 'dia': + return cls((loaded['data'], loaded['offsets']), + shape=loaded['shape']) + elif sparse_format == 'coo': + return cls((loaded['data'], (loaded['row'], loaded['col'])), + shape=loaded['shape']) + else: + raise NotImplementedError(f'Load is not implemented for ' + f'sparse matrix of format {sparse_format}.') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_spfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..8e9b0abcede6387e74538baf839a303c6cc1b6be --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_spfuncs.py @@ -0,0 +1,76 @@ +""" Functions that operate on sparse matrices +""" + +__all__ = ['count_blocks','estimate_blocksize'] + +from ._base import issparse +from ._csr import csr_array +from ._sparsetools import csr_count_blocks + + +def estimate_blocksize(A,efficiency=0.7): + """Attempt to determine the blocksize of a sparse matrix + + Returns a blocksize=(r,c) such that + - A.nnz / A.tobsr( (r,c) ).nnz > efficiency + """ + if not (issparse(A) and A.format in ("csc", "csr")): + A = csr_array(A) + + if A.nnz == 0: + return (1,1) + + if not 0 < efficiency < 1.0: + raise ValueError('efficiency must satisfy 0.0 < efficiency < 1.0') + + high_efficiency = (1.0 + efficiency) / 2.0 + nnz = float(A.nnz) + M,N = A.shape + + if M % 2 == 0 and N % 2 == 0: + e22 = nnz / (4 * count_blocks(A,(2,2))) + else: + e22 = 0.0 + + if M % 3 == 0 and N % 3 == 0: + e33 = nnz / (9 * count_blocks(A,(3,3))) + else: + e33 = 0.0 + + if e22 > high_efficiency and e33 > high_efficiency: + e66 = nnz / (36 * count_blocks(A,(6,6))) + if e66 > efficiency: + return (6,6) + else: + return (3,3) + else: + if M % 4 == 0 and N % 4 == 0: + e44 = nnz / (16 * count_blocks(A,(4,4))) + else: + e44 = 0.0 + + if e44 > efficiency: + return (4,4) + elif e33 > efficiency: + return (3,3) + elif e22 > efficiency: + return (2,2) + else: + return (1,1) + + +def count_blocks(A,blocksize): + """For a given blocksize=(r,c) count the number of occupied + blocks in a sparse matrix A + """ + r,c = blocksize + if r < 1 or c < 1: + raise ValueError('r and c must be positive') + + if issparse(A): + if A.format == "csr": + M,N = A.shape + return csr_count_blocks(M,N,r,c,A.indptr,A.indices) + elif A.format == "csc": + return count_blocks(A.T,(c,r)) + return count_blocks(csr_array(A),blocksize) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_sputils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..fa515606006d5084799cd6ac8578e1f88ed51bb9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/_sputils.py @@ -0,0 +1,451 @@ +""" Utility functions for sparse matrix module +""" + +import sys +from typing import Any, Literal, Optional, Union +import operator +import numpy as np +from math import prod +import scipy.sparse as sp +from scipy._lib._util import np_long, np_ulong + + +__all__ = ['upcast', 'getdtype', 'getdata', 'isscalarlike', 'isintlike', + 'isshape', 'issequence', 'isdense', 'ismatrix', 'get_sum_dtype'] + +supported_dtypes = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc, + np.uintc, np_long, np_ulong, np.longlong, np.ulonglong, + np.float32, np.float64, np.longdouble, + np.complex64, np.complex128, np.clongdouble] + +_upcast_memo = {} + + +def upcast(*args): + """Returns the nearest supported sparse dtype for the + combination of one or more types. + + upcast(t0, t1, ..., tn) -> T where T is a supported dtype + + Examples + -------- + >>> from scipy.sparse._sputils import upcast + >>> upcast('int32') + + >>> upcast('bool') + + >>> upcast('int32','float32') + + >>> upcast('bool',complex,float) + + + """ + + t = _upcast_memo.get(hash(args)) + if t is not None: + return t + + upcast = np.result_type(*args) + + for t in supported_dtypes: + if np.can_cast(upcast, t): + _upcast_memo[hash(args)] = t + return t + + raise TypeError(f'no supported conversion for types: {args!r}') + + +def upcast_char(*args): + """Same as `upcast` but taking dtype.char as input (faster).""" + t = _upcast_memo.get(args) + if t is not None: + return t + t = upcast(*map(np.dtype, args)) + _upcast_memo[args] = t + return t + + +def upcast_scalar(dtype, scalar): + """Determine data type for binary operation between an array of + type `dtype` and a scalar. + """ + return (np.array([0], dtype=dtype) * scalar).dtype + + +def downcast_intp_index(arr): + """ + Down-cast index array to np.intp dtype if it is of a larger dtype. + + Raise an error if the array contains a value that is too large for + intp. + """ + if arr.dtype.itemsize > np.dtype(np.intp).itemsize: + if arr.size == 0: + return arr.astype(np.intp) + maxval = arr.max() + minval = arr.min() + if maxval > np.iinfo(np.intp).max or minval < np.iinfo(np.intp).min: + raise ValueError("Cannot deal with arrays with indices larger " + "than the machine maximum address size " + "(e.g. 64-bit indices on 32-bit machine).") + return arr.astype(np.intp) + return arr + + +def to_native(A): + """ + Ensure that the data type of the NumPy array `A` has native byte order. + + `A` must be a NumPy array. If the data type of `A` does not have native + byte order, a copy of `A` with a native byte order is returned. Otherwise + `A` is returned. + """ + dt = A.dtype + if dt.isnative: + # Don't call `asarray()` if A is already native, to avoid unnecessarily + # creating a view of the input array. + return A + return np.asarray(A, dtype=dt.newbyteorder('native')) + + +def getdtype(dtype, a=None, default=None): + """Function used to simplify argument processing. If 'dtype' is not + specified (is None), returns a.dtype; otherwise returns a np.dtype + object created from the specified dtype argument. If 'dtype' and 'a' + are both None, construct a data type out of the 'default' parameter. + Furthermore, 'dtype' must be in 'allowed' set. + """ + # TODO is this really what we want? + if dtype is None: + try: + newdtype = a.dtype + except AttributeError as e: + if default is not None: + newdtype = np.dtype(default) + else: + raise TypeError("could not interpret data type") from e + else: + newdtype = np.dtype(dtype) + if newdtype == np.object_: + raise ValueError( + "object dtype is not supported by sparse matrices" + ) + + return newdtype + + +def getdata(obj, dtype=None, copy=False) -> np.ndarray: + """ + This is a wrapper of `np.array(obj, dtype=dtype, copy=copy)` + that will generate a warning if the result is an object array. + """ + data = np.array(obj, dtype=dtype, copy=copy) + # Defer to getdtype for checking that the dtype is OK. + # This is called for the validation only; we don't need the return value. + getdtype(data.dtype) + return data + + +def get_index_dtype(arrays=(), maxval=None, check_contents=False): + """ + Based on input (integer) arrays `a`, determine a suitable index data + type that can hold the data in the arrays. + + Parameters + ---------- + arrays : tuple of array_like + Input arrays whose types/contents to check + maxval : float, optional + Maximum value needed + check_contents : bool, optional + Whether to check the values in the arrays and not just their types. + Default: False (check only the types) + + Returns + ------- + dtype : dtype + Suitable index data type (int32 or int64) + + """ + + int32min = np.int32(np.iinfo(np.int32).min) + int32max = np.int32(np.iinfo(np.int32).max) + + # not using intc directly due to misinteractions with pythran + dtype = np.int32 if np.intc().itemsize == 4 else np.int64 + if maxval is not None: + maxval = np.int64(maxval) + if maxval > int32max: + dtype = np.int64 + + if isinstance(arrays, np.ndarray): + arrays = (arrays,) + + for arr in arrays: + arr = np.asarray(arr) + if not np.can_cast(arr.dtype, np.int32): + if check_contents: + if arr.size == 0: + # a bigger type not needed + continue + elif np.issubdtype(arr.dtype, np.integer): + maxval = arr.max() + minval = arr.min() + if minval >= int32min and maxval <= int32max: + # a bigger type not needed + continue + + dtype = np.int64 + break + + return dtype + + +def get_sum_dtype(dtype: np.dtype) -> np.dtype: + """Mimic numpy's casting for np.sum""" + if dtype.kind == 'u' and np.can_cast(dtype, np.uint): + return np.uint + if np.can_cast(dtype, np.int_): + return np.int_ + return dtype + + +def isscalarlike(x) -> bool: + """Is x either a scalar, an array scalar, or a 0-dim array?""" + return np.isscalar(x) or (isdense(x) and x.ndim == 0) + + +def isintlike(x) -> bool: + """Is x appropriate as an index into a sparse matrix? Returns True + if it can be cast safely to a machine int. + """ + # Fast-path check to eliminate non-scalar values. operator.index would + # catch this case too, but the exception catching is slow. + if np.ndim(x) != 0: + return False + try: + operator.index(x) + except (TypeError, ValueError): + try: + loose_int = bool(int(x) == x) + except (TypeError, ValueError): + return False + if loose_int: + msg = "Inexact indices into sparse matrices are not allowed" + raise ValueError(msg) + return loose_int + return True + + +def isshape(x, nonneg=False, *, allow_1d=False) -> bool: + """Is x a valid tuple of dimensions? + + If nonneg, also checks that the dimensions are non-negative. + If allow_1d, shapes of length 1 or 2 are allowed. + """ + ndim = len(x) + if ndim != 2 and not (allow_1d and ndim == 1): + return False + for d in x: + if not isintlike(d): + return False + if nonneg and d < 0: + return False + return True + + +def issequence(t) -> bool: + return ((isinstance(t, (list, tuple)) and + (len(t) == 0 or np.isscalar(t[0]))) or + (isinstance(t, np.ndarray) and (t.ndim == 1))) + + +def ismatrix(t) -> bool: + return ((isinstance(t, (list, tuple)) and + len(t) > 0 and issequence(t[0])) or + (isinstance(t, np.ndarray) and t.ndim == 2)) + + +def isdense(x) -> bool: + return isinstance(x, np.ndarray) + + +def validateaxis(axis) -> None: + if axis is None: + return + axis_type = type(axis) + + # In NumPy, you can pass in tuples for 'axis', but they are + # not very useful for sparse matrices given their limited + # dimensions, so let's make it explicit that they are not + # allowed to be passed in + if axis_type == tuple: + raise TypeError("Tuples are not accepted for the 'axis' parameter. " + "Please pass in one of the following: " + "{-2, -1, 0, 1, None}.") + + # If not a tuple, check that the provided axis is actually + # an integer and raise a TypeError similar to NumPy's + if not np.issubdtype(np.dtype(axis_type), np.integer): + raise TypeError(f"axis must be an integer, not {axis_type.__name__}") + + if not (-2 <= axis <= 1): + raise ValueError("axis out of range") + + +def check_shape(args, current_shape=None, *, allow_1d=False) -> tuple[int, ...]: + """Imitate numpy.matrix handling of shape arguments + + Parameters + ---------- + args : array_like + Data structures providing information about the shape of the sparse array. + current_shape : tuple, optional + The current shape of the sparse array or matrix. + If None (default), the current shape will be inferred from args. + allow_1d : bool, optional + If True, then 1-D or 2-D arrays are accepted. + If False (default), then only 2-D arrays are accepted and an error is + raised otherwise. + + Returns + ------- + new_shape: tuple + The new shape after validation. + """ + if len(args) == 0: + raise TypeError("function missing 1 required positional argument: " + "'shape'") + if len(args) == 1: + try: + shape_iter = iter(args[0]) + except TypeError: + new_shape = (operator.index(args[0]), ) + else: + new_shape = tuple(operator.index(arg) for arg in shape_iter) + else: + new_shape = tuple(operator.index(arg) for arg in args) + + if current_shape is None: + if allow_1d: + if len(new_shape) not in (1, 2): + raise ValueError('shape must be a 1- or 2-tuple of positive ' + 'integers') + elif len(new_shape) != 2: + raise ValueError('shape must be a 2-tuple of positive integers') + if any(d < 0 for d in new_shape): + raise ValueError("'shape' elements cannot be negative") + else: + # Check the current size only if needed + current_size = prod(current_shape) + + # Check for negatives + negative_indexes = [i for i, x in enumerate(new_shape) if x < 0] + if not negative_indexes: + new_size = prod(new_shape) + if new_size != current_size: + raise ValueError('cannot reshape array of size {} into shape {}' + .format(current_size, new_shape)) + elif len(negative_indexes) == 1: + skip = negative_indexes[0] + specified = prod(new_shape[:skip] + new_shape[skip+1:]) + unspecified, remainder = divmod(current_size, specified) + if remainder != 0: + err_shape = tuple('newshape' if x < 0 else x for x in new_shape) + raise ValueError('cannot reshape array of size {} into shape {}' + ''.format(current_size, err_shape)) + new_shape = new_shape[:skip] + (unspecified,) + new_shape[skip+1:] + else: + raise ValueError('can only specify one unknown dimension') + + if len(new_shape) != 2 and not (allow_1d and len(new_shape) == 1): + raise ValueError('matrix shape must be two-dimensional') + + return new_shape + + +def check_reshape_kwargs(kwargs): + """Unpack keyword arguments for reshape function. + + This is useful because keyword arguments after star arguments are not + allowed in Python 2, but star keyword arguments are. This function unpacks + 'order' and 'copy' from the star keyword arguments (with defaults) and + throws an error for any remaining. + """ + + order = kwargs.pop('order', 'C') + copy = kwargs.pop('copy', False) + if kwargs: # Some unused kwargs remain + raise TypeError('reshape() got unexpected keywords arguments: {}' + .format(', '.join(kwargs.keys()))) + return order, copy + + +def is_pydata_spmatrix(m) -> bool: + """ + Check whether object is pydata/sparse matrix, avoiding importing the module. + """ + base_cls = getattr(sys.modules.get('sparse'), 'SparseArray', None) + return base_cls is not None and isinstance(m, base_cls) + + +def convert_pydata_sparse_to_scipy( + arg: Any, target_format: Optional[Literal["csc", "csr"]] = None +) -> Union[Any, "sp.spmatrix"]: + """ + Convert a pydata/sparse array to scipy sparse matrix, + pass through anything else. + """ + if is_pydata_spmatrix(arg): + arg = arg.to_scipy_sparse() + if target_format is not None: + arg = arg.asformat(target_format) + elif arg.format not in ("csc", "csr"): + arg = arg.tocsc() + return arg + + +############################################################################### +# Wrappers for NumPy types that are deprecated + +# Numpy versions of these functions raise deprecation warnings, the +# ones below do not. + +def matrix(*args, **kwargs): + return np.array(*args, **kwargs).view(np.matrix) + + +def asmatrix(data, dtype=None): + if isinstance(data, np.matrix) and (dtype is None or data.dtype == dtype): + return data + return np.asarray(data, dtype=dtype).view(np.matrix) + +############################################################################### + + +def _todata(s) -> np.ndarray: + """Access nonzero values, possibly after summing duplicates. + + Parameters + ---------- + s : sparse array + Input sparse array. + + Returns + ------- + data: ndarray + Nonzero values of the array, with shape (s.nnz,) + + """ + if isinstance(s, sp._data._data_matrix): + return s._deduped_data() + + if isinstance(s, sp.dok_array): + return np.fromiter(s.values(), dtype=s.dtype, count=s.nnz) + + if isinstance(s, sp.lil_array): + data = np.empty(s.nnz, dtype=s.dtype) + sp._csparsetools.lil_flatten_to_array(s.data, data) + return data + + return s.tocoo()._deduped_data() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/base.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/base.py new file mode 100644 index 0000000000000000000000000000000000000000..d0a427e4570e07cc71e9e45bf98c7cf61798125b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/base.py @@ -0,0 +1,33 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'MAXPRINT', + 'SparseEfficiencyWarning', + 'SparseFormatWarning', + 'SparseWarning', + 'asmatrix', + 'check_reshape_kwargs', + 'check_shape', + 'get_sum_dtype', + 'isdense', + 'isscalarlike', + 'issparse', + 'isspmatrix', + 'spmatrix', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="base", + private_modules=["_base"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/bsr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/bsr.py new file mode 100644 index 0000000000000000000000000000000000000000..c686301a78fc3e2221600eb06035a5cb12898cdb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/bsr.py @@ -0,0 +1,36 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'bsr_matmat', + 'bsr_matrix', + 'bsr_matvec', + 'bsr_matvecs', + 'bsr_sort_indices', + 'bsr_tocsr', + 'bsr_transpose', + 'check_shape', + 'csr_matmat_maxnnz', + 'getdata', + 'getdtype', + 'isshape', + 'isspmatrix_bsr', + 'spmatrix', + 'to_native', + 'upcast', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="bsr", + private_modules=["_bsr"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/compressed.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/compressed.py new file mode 100644 index 0000000000000000000000000000000000000000..e6dc8a73e5ab527cfe0b73d558dae25047cfb98b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/compressed.py @@ -0,0 +1,43 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'IndexMixin', + 'SparseEfficiencyWarning', + 'check_shape', + 'csr_column_index1', + 'csr_column_index2', + 'csr_row_index', + 'csr_row_slice', + 'csr_sample_offsets', + 'csr_sample_values', + 'csr_todense', + 'downcast_intp_index', + 'get_csr_submatrix', + 'get_sum_dtype', + 'getdtype', + 'is_pydata_spmatrix', + 'isdense', + 'isintlike', + 'isscalarlike', + 'isshape', + 'operator', + 'to_native', + 'upcast', + 'upcast_char', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="compressed", + private_modules=["_compressed"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/construct.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/construct.py new file mode 100644 index 0000000000000000000000000000000000000000..c3d34d2fd38887877980727bceaaa215129bf283 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/construct.py @@ -0,0 +1,44 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'block_diag', + 'bmat', + 'bsr_matrix', + 'check_random_state', + 'coo_matrix', + 'csc_matrix', + 'csr_hstack', + 'csr_matrix', + 'dia_matrix', + 'diags', + 'eye', + 'get_index_dtype', + 'hstack', + 'identity', + 'isscalarlike', + 'issparse', + 'kron', + 'kronsum', + 'numbers', + 'rand', + 'random', + 'rng_integers', + 'spdiags', + 'upcast', + 'vstack', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="construct", + private_modules=["_construct"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/coo.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/coo.py new file mode 100644 index 0000000000000000000000000000000000000000..bda2da3d09a676ab79739331a21ba26102bb90ae --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/coo.py @@ -0,0 +1,37 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'SparseEfficiencyWarning', + 'check_reshape_kwargs', + 'check_shape', + 'coo_matrix', + 'coo_matvec', + 'coo_tocsr', + 'coo_todense', + 'downcast_intp_index', + 'getdata', + 'getdtype', + 'isshape', + 'isspmatrix_coo', + 'operator', + 'spmatrix', + 'to_native', + 'upcast', + 'upcast_char', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="coo", + private_modules=["_coo"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csc.py new file mode 100644 index 0000000000000000000000000000000000000000..d140b841e0724155f8602a4215836e2c8a7fad72 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csc.py @@ -0,0 +1,25 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'csc_matrix', + 'csc_tocsr', + 'expandptr', + 'isspmatrix_csc', + 'spmatrix', + 'upcast', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="csc", + private_modules=["_csc"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dd64891ea2c94c91823762ebb24d387fb54a3dd0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/__init__.py @@ -0,0 +1,208 @@ +r""" +Compressed sparse graph routines (:mod:`scipy.sparse.csgraph`) +============================================================== + +.. currentmodule:: scipy.sparse.csgraph + +Fast graph algorithms based on sparse matrix representations. + +Contents +-------- + +.. autosummary:: + :toctree: generated/ + + connected_components -- determine connected components of a graph + laplacian -- compute the laplacian of a graph + shortest_path -- compute the shortest path between points on a positive graph + dijkstra -- use Dijkstra's algorithm for shortest path + floyd_warshall -- use the Floyd-Warshall algorithm for shortest path + bellman_ford -- use the Bellman-Ford algorithm for shortest path + johnson -- use Johnson's algorithm for shortest path + breadth_first_order -- compute a breadth-first order of nodes + depth_first_order -- compute a depth-first order of nodes + breadth_first_tree -- construct the breadth-first tree from a given node + depth_first_tree -- construct a depth-first tree from a given node + minimum_spanning_tree -- construct the minimum spanning tree of a graph + reverse_cuthill_mckee -- compute permutation for reverse Cuthill-McKee ordering + maximum_flow -- solve the maximum flow problem for a graph + maximum_bipartite_matching -- compute a maximum matching of a bipartite graph + min_weight_full_bipartite_matching - compute a minimum weight full matching of a bipartite graph + structural_rank -- compute the structural rank of a graph + NegativeCycleError + +.. autosummary:: + :toctree: generated/ + + construct_dist_matrix + csgraph_from_dense + csgraph_from_masked + csgraph_masked_from_dense + csgraph_to_dense + csgraph_to_masked + reconstruct_path + +Graph Representations +--------------------- +This module uses graphs which are stored in a matrix format. A +graph with N nodes can be represented by an (N x N) adjacency matrix G. +If there is a connection from node i to node j, then G[i, j] = w, where +w is the weight of the connection. For nodes i and j which are +not connected, the value depends on the representation: + +- for dense array representations, non-edges are represented by + G[i, j] = 0, infinity, or NaN. + +- for dense masked representations (of type np.ma.MaskedArray), non-edges + are represented by masked values. This can be useful when graphs with + zero-weight edges are desired. + +- for sparse array representations, non-edges are represented by + non-entries in the matrix. This sort of sparse representation also + allows for edges with zero weights. + +As a concrete example, imagine that you would like to represent the following +undirected graph:: + + G + + (0) + / \ + 1 2 + / \ + (2) (1) + +This graph has three nodes, where node 0 and 1 are connected by an edge of +weight 2, and nodes 0 and 2 are connected by an edge of weight 1. +We can construct the dense, masked, and sparse representations as follows, +keeping in mind that an undirected graph is represented by a symmetric matrix:: + + >>> import numpy as np + >>> G_dense = np.array([[0, 2, 1], + ... [2, 0, 0], + ... [1, 0, 0]]) + >>> G_masked = np.ma.masked_values(G_dense, 0) + >>> from scipy.sparse import csr_matrix + >>> G_sparse = csr_matrix(G_dense) + +This becomes more difficult when zero edges are significant. For example, +consider the situation when we slightly modify the above graph:: + + G2 + + (0) + / \ + 0 2 + / \ + (2) (1) + +This is identical to the previous graph, except nodes 0 and 2 are connected +by an edge of zero weight. In this case, the dense representation above +leads to ambiguities: how can non-edges be represented if zero is a meaningful +value? In this case, either a masked or sparse representation must be used +to eliminate the ambiguity:: + + >>> import numpy as np + >>> G2_data = np.array([[np.inf, 2, 0 ], + ... [2, np.inf, np.inf], + ... [0, np.inf, np.inf]]) + >>> G2_masked = np.ma.masked_invalid(G2_data) + >>> from scipy.sparse.csgraph import csgraph_from_dense + >>> # G2_sparse = csr_matrix(G2_data) would give the wrong result + >>> G2_sparse = csgraph_from_dense(G2_data, null_value=np.inf) + >>> G2_sparse.data + array([ 2., 0., 2., 0.]) + +Here we have used a utility routine from the csgraph submodule in order to +convert the dense representation to a sparse representation which can be +understood by the algorithms in submodule. By viewing the data array, we +can see that the zero values are explicitly encoded in the graph. + +Directed vs. undirected +^^^^^^^^^^^^^^^^^^^^^^^ +Matrices may represent either directed or undirected graphs. This is +specified throughout the csgraph module by a boolean keyword. Graphs are +assumed to be directed by default. In a directed graph, traversal from node +i to node j can be accomplished over the edge G[i, j], but not the edge +G[j, i]. Consider the following dense graph:: + + >>> import numpy as np + >>> G_dense = np.array([[0, 1, 0], + ... [2, 0, 3], + ... [0, 4, 0]]) + +When ``directed=True`` we get the graph:: + + ---1--> ---3--> + (0) (1) (2) + <--2--- <--4--- + +In a non-directed graph, traversal from node i to node j can be +accomplished over either G[i, j] or G[j, i]. If both edges are not null, +and the two have unequal weights, then the smaller of the two is used. + +So for the same graph, when ``directed=False`` we get the graph:: + + (0)--1--(1)--3--(2) + +Note that a symmetric matrix will represent an undirected graph, regardless +of whether the 'directed' keyword is set to True or False. In this case, +using ``directed=True`` generally leads to more efficient computation. + +The routines in this module accept as input either scipy.sparse representations +(csr, csc, or lil format), masked representations, or dense representations +with non-edges indicated by zeros, infinities, and NaN entries. +""" # noqa: E501 + +__docformat__ = "restructuredtext en" + +__all__ = ['connected_components', + 'laplacian', + 'shortest_path', + 'floyd_warshall', + 'dijkstra', + 'bellman_ford', + 'johnson', + 'breadth_first_order', + 'depth_first_order', + 'breadth_first_tree', + 'depth_first_tree', + 'minimum_spanning_tree', + 'reverse_cuthill_mckee', + 'maximum_flow', + 'maximum_bipartite_matching', + 'min_weight_full_bipartite_matching', + 'structural_rank', + 'construct_dist_matrix', + 'reconstruct_path', + 'csgraph_masked_from_dense', + 'csgraph_from_dense', + 'csgraph_from_masked', + 'csgraph_to_dense', + 'csgraph_to_masked', + 'NegativeCycleError'] + +from ._laplacian import laplacian +from ._shortest_path import ( + shortest_path, floyd_warshall, dijkstra, bellman_ford, johnson, + NegativeCycleError +) +from ._traversal import ( + breadth_first_order, depth_first_order, breadth_first_tree, + depth_first_tree, connected_components +) +from ._min_spanning_tree import minimum_spanning_tree +from ._flow import maximum_flow +from ._matching import ( + maximum_bipartite_matching, min_weight_full_bipartite_matching +) +from ._reordering import reverse_cuthill_mckee, structural_rank +from ._tools import ( + construct_dist_matrix, reconstruct_path, csgraph_from_dense, + csgraph_to_dense, csgraph_masked_from_dense, csgraph_from_masked, + csgraph_to_masked +) + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_laplacian.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_laplacian.py new file mode 100644 index 0000000000000000000000000000000000000000..9c3d1f7972d7e16b9d600bcbc40f57fb776b657c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_laplacian.py @@ -0,0 +1,562 @@ +""" +Laplacian of a compressed-sparse graph +""" + +import numpy as np +from scipy.sparse import issparse +from scipy.sparse.linalg import LinearOperator +from scipy.sparse._sputils import convert_pydata_sparse_to_scipy, is_pydata_spmatrix + + +############################################################################### +# Graph laplacian +def laplacian( + csgraph, + normed=False, + return_diag=False, + use_out_degree=False, + *, + copy=True, + form="array", + dtype=None, + symmetrized=False, +): + """ + Return the Laplacian of a directed graph. + + Parameters + ---------- + csgraph : array_like or sparse matrix, 2 dimensions + compressed-sparse graph, with shape (N, N). + normed : bool, optional + If True, then compute symmetrically normalized Laplacian. + Default: False. + return_diag : bool, optional + If True, then also return an array related to vertex degrees. + Default: False. + use_out_degree : bool, optional + If True, then use out-degree instead of in-degree. + This distinction matters only if the graph is asymmetric. + Default: False. + copy: bool, optional + If False, then change `csgraph` in place if possible, + avoiding doubling the memory use. + Default: True, for backward compatibility. + form: 'array', or 'function', or 'lo' + Determines the format of the output Laplacian: + + * 'array' is a numpy array; + * 'function' is a pointer to evaluating the Laplacian-vector + or Laplacian-matrix product; + * 'lo' results in the format of the `LinearOperator`. + + Choosing 'function' or 'lo' always avoids doubling + the memory use, ignoring `copy` value. + Default: 'array', for backward compatibility. + dtype: None or one of numeric numpy dtypes, optional + The dtype of the output. If ``dtype=None``, the dtype of the + output matches the dtype of the input csgraph, except for + the case ``normed=True`` and integer-like csgraph, where + the output dtype is 'float' allowing accurate normalization, + but dramatically increasing the memory use. + Default: None, for backward compatibility. + symmetrized: bool, optional + If True, then the output Laplacian is symmetric/Hermitian. + The symmetrization is done by ``csgraph + csgraph.T.conj`` + without dividing by 2 to preserve integer dtypes if possible + prior to the construction of the Laplacian. + The symmetrization will increase the memory footprint of + sparse matrices unless the sparsity pattern is symmetric or + `form` is 'function' or 'lo'. + Default: False, for backward compatibility. + + Returns + ------- + lap : ndarray, or sparse matrix, or `LinearOperator` + The N x N Laplacian of csgraph. It will be a NumPy array (dense) + if the input was dense, or a sparse matrix otherwise, or + the format of a function or `LinearOperator` if + `form` equals 'function' or 'lo', respectively. + diag : ndarray, optional + The length-N main diagonal of the Laplacian matrix. + For the normalized Laplacian, this is the array of square roots + of vertex degrees or 1 if the degree is zero. + + Notes + ----- + The Laplacian matrix of a graph is sometimes referred to as the + "Kirchhoff matrix" or just the "Laplacian", and is useful in many + parts of spectral graph theory. + In particular, the eigen-decomposition of the Laplacian can give + insight into many properties of the graph, e.g., + is commonly used for spectral data embedding and clustering. + + The constructed Laplacian doubles the memory use if ``copy=True`` and + ``form="array"`` which is the default. + Choosing ``copy=False`` has no effect unless ``form="array"`` + or the matrix is sparse in the ``coo`` format, or dense array, except + for the integer input with ``normed=True`` that forces the float output. + + Sparse input is reformatted into ``coo`` if ``form="array"``, + which is the default. + + If the input adjacency matrix is not symmetric, the Laplacian is + also non-symmetric unless ``symmetrized=True`` is used. + + Diagonal entries of the input adjacency matrix are ignored and + replaced with zeros for the purpose of normalization where ``normed=True``. + The normalization uses the inverse square roots of row-sums of the input + adjacency matrix, and thus may fail if the row-sums contain + negative or complex with a non-zero imaginary part values. + + The normalization is symmetric, making the normalized Laplacian also + symmetric if the input csgraph was symmetric. + + References + ---------- + .. [1] Laplacian matrix. https://en.wikipedia.org/wiki/Laplacian_matrix + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csgraph + + Our first illustration is the symmetric graph + + >>> G = np.arange(4) * np.arange(4)[:, np.newaxis] + >>> G + array([[0, 0, 0, 0], + [0, 1, 2, 3], + [0, 2, 4, 6], + [0, 3, 6, 9]]) + + and its symmetric Laplacian matrix + + >>> csgraph.laplacian(G) + array([[ 0, 0, 0, 0], + [ 0, 5, -2, -3], + [ 0, -2, 8, -6], + [ 0, -3, -6, 9]]) + + The non-symmetric graph + + >>> G = np.arange(9).reshape(3, 3) + >>> G + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + + has different row- and column sums, resulting in two varieties + of the Laplacian matrix, using an in-degree, which is the default + + >>> L_in_degree = csgraph.laplacian(G) + >>> L_in_degree + array([[ 9, -1, -2], + [-3, 8, -5], + [-6, -7, 7]]) + + or alternatively an out-degree + + >>> L_out_degree = csgraph.laplacian(G, use_out_degree=True) + >>> L_out_degree + array([[ 3, -1, -2], + [-3, 8, -5], + [-6, -7, 13]]) + + Constructing a symmetric Laplacian matrix, one can add the two as + + >>> L_in_degree + L_out_degree.T + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + or use the ``symmetrized=True`` option + + >>> csgraph.laplacian(G, symmetrized=True) + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + that is equivalent to symmetrizing the original graph + + >>> csgraph.laplacian(G + G.T) + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + The goal of normalization is to make the non-zero diagonal entries + of the Laplacian matrix to be all unit, also scaling off-diagonal + entries correspondingly. The normalization can be done manually, e.g., + + >>> G = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]) + >>> L, d = csgraph.laplacian(G, return_diag=True) + >>> L + array([[ 2, -1, -1], + [-1, 2, -1], + [-1, -1, 2]]) + >>> d + array([2, 2, 2]) + >>> scaling = np.sqrt(d) + >>> scaling + array([1.41421356, 1.41421356, 1.41421356]) + >>> (1/scaling)*L*(1/scaling) + array([[ 1. , -0.5, -0.5], + [-0.5, 1. , -0.5], + [-0.5, -0.5, 1. ]]) + + Or using ``normed=True`` option + + >>> L, d = csgraph.laplacian(G, return_diag=True, normed=True) + >>> L + array([[ 1. , -0.5, -0.5], + [-0.5, 1. , -0.5], + [-0.5, -0.5, 1. ]]) + + which now instead of the diagonal returns the scaling coefficients + + >>> d + array([1.41421356, 1.41421356, 1.41421356]) + + Zero scaling coefficients are substituted with 1s, where scaling + has thus no effect, e.g., + + >>> G = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0]]) + >>> G + array([[0, 0, 0], + [0, 0, 1], + [0, 1, 0]]) + >>> L, d = csgraph.laplacian(G, return_diag=True, normed=True) + >>> L + array([[ 0., -0., -0.], + [-0., 1., -1.], + [-0., -1., 1.]]) + >>> d + array([1., 1., 1.]) + + Only the symmetric normalization is implemented, resulting + in a symmetric Laplacian matrix if and only if its graph is symmetric + and has all non-negative degrees, like in the examples above. + + The output Laplacian matrix is by default a dense array or a sparse matrix + inferring its shape, format, and dtype from the input graph matrix: + + >>> G = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]).astype(np.float32) + >>> G + array([[0., 1., 1.], + [1., 0., 1.], + [1., 1., 0.]], dtype=float32) + >>> csgraph.laplacian(G) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]], dtype=float32) + + but can alternatively be generated matrix-free as a LinearOperator: + + >>> L = csgraph.laplacian(G, form="lo") + >>> L + <3x3 _CustomLinearOperator with dtype=float32> + >>> L(np.eye(3)) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]]) + + or as a lambda-function: + + >>> L = csgraph.laplacian(G, form="function") + >>> L + . at 0x0000012AE6F5A598> + >>> L(np.eye(3)) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]]) + + The Laplacian matrix is used for + spectral data clustering and embedding + as well as for spectral graph partitioning. + Our final example illustrates the latter + for a noisy directed linear graph. + + >>> from scipy.sparse import diags, random + >>> from scipy.sparse.linalg import lobpcg + + Create a directed linear graph with ``N=35`` vertices + using a sparse adjacency matrix ``G``: + + >>> N = 35 + >>> G = diags(np.ones(N-1), 1, format="csr") + + Fix a random seed ``rng`` and add a random sparse noise to the graph ``G``: + + >>> rng = np.random.default_rng() + >>> G += 1e-2 * random(N, N, density=0.1, random_state=rng) + + Set initial approximations for eigenvectors: + + >>> X = rng.random((N, 2)) + + The constant vector of ones is always a trivial eigenvector + of the non-normalized Laplacian to be filtered out: + + >>> Y = np.ones((N, 1)) + + Alternating (1) the sign of the graph weights allows determining + labels for spectral max- and min- cuts in a single loop. + Since the graph is undirected, the option ``symmetrized=True`` + must be used in the construction of the Laplacian. + The option ``normed=True`` cannot be used in (2) for the negative weights + here as the symmetric normalization evaluates square roots. + The option ``form="lo"`` in (2) is matrix-free, i.e., guarantees + a fixed memory footprint and read-only access to the graph. + Calling the eigenvalue solver ``lobpcg`` (3) computes the Fiedler vector + that determines the labels as the signs of its components in (5). + Since the sign in an eigenvector is not deterministic and can flip, + we fix the sign of the first component to be always +1 in (4). + + >>> for cut in ["max", "min"]: + ... G = -G # 1. + ... L = csgraph.laplacian(G, symmetrized=True, form="lo") # 2. + ... _, eves = lobpcg(L, X, Y=Y, largest=False, tol=1e-3) # 3. + ... eves *= np.sign(eves[0, 0]) # 4. + ... print(cut + "-cut labels:\\n", 1 * (eves[:, 0]>0)) # 5. + max-cut labels: + [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1] + min-cut labels: + [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + + As anticipated for a (slightly noisy) linear graph, + the max-cut strips all the edges of the graph coloring all + odd vertices into one color and all even vertices into another one, + while the balanced min-cut partitions the graph + in the middle by deleting a single edge. + Both determined partitions are optimal. + """ + is_pydata_sparse = is_pydata_spmatrix(csgraph) + if is_pydata_sparse: + pydata_sparse_cls = csgraph.__class__ + csgraph = convert_pydata_sparse_to_scipy(csgraph) + if csgraph.ndim != 2 or csgraph.shape[0] != csgraph.shape[1]: + raise ValueError('csgraph must be a square matrix or array') + + if normed and ( + np.issubdtype(csgraph.dtype, np.signedinteger) + or np.issubdtype(csgraph.dtype, np.uint) + ): + csgraph = csgraph.astype(np.float64) + + if form == "array": + create_lap = ( + _laplacian_sparse if issparse(csgraph) else _laplacian_dense + ) + else: + create_lap = ( + _laplacian_sparse_flo + if issparse(csgraph) + else _laplacian_dense_flo + ) + + degree_axis = 1 if use_out_degree else 0 + + lap, d = create_lap( + csgraph, + normed=normed, + axis=degree_axis, + copy=copy, + form=form, + dtype=dtype, + symmetrized=symmetrized, + ) + if is_pydata_sparse: + lap = pydata_sparse_cls.from_scipy_sparse(lap) + if return_diag: + return lap, d + return lap + + +def _setdiag_dense(m, d): + step = len(d) + 1 + m.flat[::step] = d + + +def _laplace(m, d): + return lambda v: v * d[:, np.newaxis] - m @ v + + +def _laplace_normed(m, d, nd): + laplace = _laplace(m, d) + return lambda v: nd[:, np.newaxis] * laplace(v * nd[:, np.newaxis]) + + +def _laplace_sym(m, d): + return ( + lambda v: v * d[:, np.newaxis] + - m @ v + - np.transpose(np.conjugate(np.transpose(np.conjugate(v)) @ m)) + ) + + +def _laplace_normed_sym(m, d, nd): + laplace_sym = _laplace_sym(m, d) + return lambda v: nd[:, np.newaxis] * laplace_sym(v * nd[:, np.newaxis]) + + +def _linearoperator(mv, shape, dtype): + return LinearOperator(matvec=mv, matmat=mv, shape=shape, dtype=dtype) + + +def _laplacian_sparse_flo(graph, normed, axis, copy, form, dtype, symmetrized): + # The keyword argument `copy` is unused and has no effect here. + del copy + + if dtype is None: + dtype = graph.dtype + + graph_sum = np.asarray(graph.sum(axis=axis)).ravel() + graph_diagonal = graph.diagonal() + diag = graph_sum - graph_diagonal + if symmetrized: + graph_sum += np.asarray(graph.sum(axis=1 - axis)).ravel() + diag = graph_sum - graph_diagonal - graph_diagonal + + if normed: + isolated_node_mask = diag == 0 + w = np.where(isolated_node_mask, 1, np.sqrt(diag)) + if symmetrized: + md = _laplace_normed_sym(graph, graph_sum, 1.0 / w) + else: + md = _laplace_normed(graph, graph_sum, 1.0 / w) + if form == "function": + return md, w.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, w.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + else: + if symmetrized: + md = _laplace_sym(graph, graph_sum) + else: + md = _laplace(graph, graph_sum) + if form == "function": + return md, diag.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, diag.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + + +def _laplacian_sparse(graph, normed, axis, copy, form, dtype, symmetrized): + # The keyword argument `form` is unused and has no effect here. + del form + + if dtype is None: + dtype = graph.dtype + + needs_copy = False + if graph.format in ('lil', 'dok'): + m = graph.tocoo() + else: + m = graph + if copy: + needs_copy = True + + if symmetrized: + m += m.T.conj() + + w = np.asarray(m.sum(axis=axis)).ravel() - m.diagonal() + if normed: + m = m.tocoo(copy=needs_copy) + isolated_node_mask = (w == 0) + w = np.where(isolated_node_mask, 1, np.sqrt(w)) + m.data /= w[m.row] + m.data /= w[m.col] + m.data *= -1 + m.setdiag(1 - isolated_node_mask) + else: + if m.format == 'dia': + m = m.copy() + else: + m = m.tocoo(copy=needs_copy) + m.data *= -1 + m.setdiag(w) + + return m.astype(dtype, copy=False), w.astype(dtype) + + +def _laplacian_dense_flo(graph, normed, axis, copy, form, dtype, symmetrized): + + if copy: + m = np.array(graph) + else: + m = np.asarray(graph) + + if dtype is None: + dtype = m.dtype + + graph_sum = m.sum(axis=axis) + graph_diagonal = m.diagonal() + diag = graph_sum - graph_diagonal + if symmetrized: + graph_sum += m.sum(axis=1 - axis) + diag = graph_sum - graph_diagonal - graph_diagonal + + if normed: + isolated_node_mask = diag == 0 + w = np.where(isolated_node_mask, 1, np.sqrt(diag)) + if symmetrized: + md = _laplace_normed_sym(m, graph_sum, 1.0 / w) + else: + md = _laplace_normed(m, graph_sum, 1.0 / w) + if form == "function": + return md, w.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, w.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + else: + if symmetrized: + md = _laplace_sym(m, graph_sum) + else: + md = _laplace(m, graph_sum) + if form == "function": + return md, diag.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, diag.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + + +def _laplacian_dense(graph, normed, axis, copy, form, dtype, symmetrized): + + if form != "array": + raise ValueError(f'{form!r} must be "array"') + + if dtype is None: + dtype = graph.dtype + + if copy: + m = np.array(graph) + else: + m = np.asarray(graph) + + if dtype is None: + dtype = m.dtype + + if symmetrized: + m += m.T.conj() + np.fill_diagonal(m, 0) + w = m.sum(axis=axis) + if normed: + isolated_node_mask = (w == 0) + w = np.where(isolated_node_mask, 1, np.sqrt(w)) + m /= w + m /= w[:, np.newaxis] + m *= -1 + _setdiag_dense(m, 1 - isolated_node_mask) + else: + m *= -1 + _setdiag_dense(m, w) + + return m.astype(dtype, copy=False), w.astype(dtype, copy=False) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_validation.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..e160cf5e7b0e9fd94772e5e150e32c8b0d0be7c6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/_validation.py @@ -0,0 +1,61 @@ +import numpy as np +from scipy.sparse import csr_matrix, issparse +from scipy.sparse._sputils import convert_pydata_sparse_to_scipy +from scipy.sparse.csgraph._tools import ( + csgraph_to_dense, csgraph_from_dense, + csgraph_masked_from_dense, csgraph_from_masked +) + +DTYPE = np.float64 + + +def validate_graph(csgraph, directed, dtype=DTYPE, + csr_output=True, dense_output=True, + copy_if_dense=False, copy_if_sparse=False, + null_value_in=0, null_value_out=np.inf, + infinity_null=True, nan_null=True): + """Routine for validation and conversion of csgraph inputs""" + if not (csr_output or dense_output): + raise ValueError("Internal: dense or csr output must be true") + + csgraph = convert_pydata_sparse_to_scipy(csgraph) + + # if undirected and csc storage, then transposing in-place + # is quicker than later converting to csr. + if (not directed) and issparse(csgraph) and csgraph.format == "csc": + csgraph = csgraph.T + + if issparse(csgraph): + if csr_output: + csgraph = csr_matrix(csgraph, dtype=DTYPE, copy=copy_if_sparse) + else: + csgraph = csgraph_to_dense(csgraph, null_value=null_value_out) + elif np.ma.isMaskedArray(csgraph): + if dense_output: + mask = csgraph.mask + csgraph = np.array(csgraph.data, dtype=DTYPE, copy=copy_if_dense) + csgraph[mask] = null_value_out + else: + csgraph = csgraph_from_masked(csgraph) + else: + if dense_output: + csgraph = csgraph_masked_from_dense(csgraph, + copy=copy_if_dense, + null_value=null_value_in, + nan_null=nan_null, + infinity_null=infinity_null) + mask = csgraph.mask + csgraph = np.asarray(csgraph.data, dtype=DTYPE) + csgraph[mask] = null_value_out + else: + csgraph = csgraph_from_dense(csgraph, null_value=null_value_in, + infinity_null=infinity_null, + nan_null=nan_null) + + if csgraph.ndim != 2: + raise ValueError("compressed-sparse graph must be 2-D") + + if csgraph.shape[0] != csgraph.shape[1]: + raise ValueError("compressed-sparse graph must be shape (N, N)") + + return csgraph diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py new file mode 100644 index 0000000000000000000000000000000000000000..0b190a24deb9f2818893a120f8ea376fbfb8d6fe --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py @@ -0,0 +1,119 @@ +import numpy as np +from numpy.testing import assert_equal, assert_array_almost_equal +from scipy.sparse import csgraph, csr_array + + +def test_weak_connections(): + Xde = np.array([[0, 1, 0], + [0, 0, 0], + [0, 0, 0]]) + + Xsp = csgraph.csgraph_from_dense(Xde, null_value=0) + + for X in Xsp, Xde: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='weak') + + assert_equal(n_components, 2) + assert_array_almost_equal(labels, [0, 0, 1]) + + +def test_strong_connections(): + X1de = np.array([[0, 1, 0], + [0, 0, 0], + [0, 0, 0]]) + X2de = X1de + X1de.T + + X1sp = csgraph.csgraph_from_dense(X1de, null_value=0) + X2sp = csgraph.csgraph_from_dense(X2de, null_value=0) + + for X in X1sp, X1de: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + + assert_equal(n_components, 3) + labels.sort() + assert_array_almost_equal(labels, [0, 1, 2]) + + for X in X2sp, X2de: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + + assert_equal(n_components, 2) + labels.sort() + assert_array_almost_equal(labels, [0, 0, 1]) + + +def test_strong_connections2(): + X = np.array([[0, 0, 0, 0, 0, 0], + [1, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0]]) + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + assert_equal(n_components, 5) + labels.sort() + assert_array_almost_equal(labels, [0, 1, 2, 2, 3, 4]) + + +def test_weak_connections2(): + X = np.array([[0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0]]) + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='weak') + assert_equal(n_components, 2) + labels.sort() + assert_array_almost_equal(labels, [0, 0, 1, 1, 1, 1]) + + +def test_ticket1876(): + # Regression test: this failed in the original implementation + # There should be two strongly-connected components; previously gave one + g = np.array([[0, 1, 1, 0], + [1, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 1, 0]]) + n_components, labels = csgraph.connected_components(g, connection='strong') + + assert_equal(n_components, 2) + assert_equal(labels[0], labels[1]) + assert_equal(labels[2], labels[3]) + + +def test_fully_connected_graph(): + # Fully connected dense matrices raised an exception. + # https://github.com/scipy/scipy/issues/3818 + g = np.ones((4, 4)) + n_components, labels = csgraph.connected_components(g) + assert_equal(n_components, 1) + + +def test_int64_indices_undirected(): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + n, labels = csgraph.connected_components(g, directed=False) + assert n == 1 + assert_array_almost_equal(labels, [0, 0]) + + +def test_int64_indices_directed(): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + n, labels = csgraph.connected_components(g, directed=True, + connection='strong') + assert n == 2 + assert_array_almost_equal(labels, [1, 0]) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_conversions.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_conversions.py new file mode 100644 index 0000000000000000000000000000000000000000..e7900d67b543187e6a34b76ee5c9511cfcccae9e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_conversions.py @@ -0,0 +1,61 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import csgraph_from_dense, csgraph_to_dense + + +def test_csgraph_from_dense(): + np.random.seed(1234) + G = np.random.random((10, 10)) + some_nulls = (G < 0.4) + all_nulls = (G < 0.8) + + for null_value in [0, np.nan, np.inf]: + G[all_nulls] = null_value + with np.errstate(invalid="ignore"): + G_csr = csgraph_from_dense(G, null_value=0) + + G[all_nulls] = 0 + assert_array_almost_equal(G, G_csr.toarray()) + + for null_value in [np.nan, np.inf]: + G[all_nulls] = 0 + G[some_nulls] = null_value + with np.errstate(invalid="ignore"): + G_csr = csgraph_from_dense(G, null_value=0) + + G[all_nulls] = 0 + assert_array_almost_equal(G, G_csr.toarray()) + + +def test_csgraph_to_dense(): + np.random.seed(1234) + G = np.random.random((10, 10)) + nulls = (G < 0.8) + G[nulls] = np.inf + + G_csr = csgraph_from_dense(G) + + for null_value in [0, 10, -np.inf, np.inf]: + G[nulls] = null_value + assert_array_almost_equal(G, csgraph_to_dense(G_csr, null_value)) + + +def test_multiple_edges(): + # create a random square matrix with an even number of elements + np.random.seed(1234) + X = np.random.random((10, 10)) + Xcsr = csr_matrix(X) + + # now double-up every other column + Xcsr.indices[::2] = Xcsr.indices[1::2] + + # normal sparse toarray() will sum the duplicated edges + Xdense = Xcsr.toarray() + assert_array_almost_equal(Xdense[:, 1::2], + X[:, ::2] + X[:, 1::2]) + + # csgraph_to_dense chooses the minimum of each duplicated edge + Xdense = csgraph_to_dense(Xcsr) + assert_array_almost_equal(Xdense[:, 1::2], + np.minimum(X[:, ::2], X[:, 1::2])) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_flow.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..8bb129a572dd3abedb2afd896d04fa53e8c096bc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_flow.py @@ -0,0 +1,201 @@ +import numpy as np +from numpy.testing import assert_array_equal +import pytest + +from scipy.sparse import csr_matrix, csc_matrix +from scipy.sparse.csgraph import maximum_flow +from scipy.sparse.csgraph._flow import ( + _add_reverse_edges, _make_edge_pointers, _make_tails +) + +methods = ['edmonds_karp', 'dinic'] + +def test_raises_on_dense_input(): + with pytest.raises(TypeError): + graph = np.array([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_csc_input(): + with pytest.raises(TypeError): + graph = csc_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_floating_point_input(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1.5], [0, 0]], dtype=np.float64) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_non_square_input(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1, 2], [2, 1, 0]]) + maximum_flow(graph, 0, 1) + + +def test_raises_when_source_is_sink(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 0) + maximum_flow(graph, 0, 0, method='edmonds_karp') + + +@pytest.mark.parametrize('method', methods) +@pytest.mark.parametrize('source', [-1, 2, 3]) +def test_raises_when_source_is_out_of_bounds(source, method): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, source, 1, method=method) + + +@pytest.mark.parametrize('method', methods) +@pytest.mark.parametrize('sink', [-1, 2, 3]) +def test_raises_when_sink_is_out_of_bounds(sink, method): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, sink, method=method) + + +@pytest.mark.parametrize('method', methods) +def test_simple_graph(method): + # This graph looks as follows: + # (0) --5--> (1) + graph = csr_matrix([[0, 5], [0, 0]]) + res = maximum_flow(graph, 0, 1, method=method) + assert res.flow_value == 5 + expected_flow = np.array([[0, 5], [-5, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_bottle_neck_graph(method): + # This graph cannot use the full capacity between 0 and 1: + # (0) --5--> (1) --3--> (2) + graph = csr_matrix([[0, 5, 0], [0, 0, 3], [0, 0, 0]]) + res = maximum_flow(graph, 0, 2, method=method) + assert res.flow_value == 3 + expected_flow = np.array([[0, 3, 0], [-3, 0, 3], [0, -3, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_backwards_flow(method): + # This example causes backwards flow between vertices 3 and 4, + # and so this test ensures that we handle that accordingly. See + # https://stackoverflow.com/q/38843963/5085211 + # for more information. + graph = csr_matrix([[0, 10, 0, 0, 10, 0, 0, 0], + [0, 0, 10, 0, 0, 0, 0, 0], + [0, 0, 0, 10, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 10], + [0, 0, 0, 10, 0, 10, 0, 0], + [0, 0, 0, 0, 0, 0, 10, 0], + [0, 0, 0, 0, 0, 0, 0, 10], + [0, 0, 0, 0, 0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 7, method=method) + assert res.flow_value == 20 + expected_flow = np.array([[0, 10, 0, 0, 10, 0, 0, 0], + [-10, 0, 10, 0, 0, 0, 0, 0], + [0, -10, 0, 10, 0, 0, 0, 0], + [0, 0, -10, 0, 0, 0, 0, 10], + [-10, 0, 0, 0, 0, 10, 0, 0], + [0, 0, 0, 0, -10, 0, 10, 0], + [0, 0, 0, 0, 0, -10, 0, 10], + [0, 0, 0, -10, 0, 0, -10, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_example_from_clrs_chapter_26_1(method): + # See page 659 in CLRS second edition, but note that the maximum flow + # we find is slightly different than the one in CLRS; we push a flow of + # 12 to v_1 instead of v_2. + graph = csr_matrix([[0, 16, 13, 0, 0, 0], + [0, 0, 10, 12, 0, 0], + [0, 4, 0, 0, 14, 0], + [0, 0, 9, 0, 0, 20], + [0, 0, 0, 7, 0, 4], + [0, 0, 0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 5, method=method) + assert res.flow_value == 23 + expected_flow = np.array([[0, 12, 11, 0, 0, 0], + [-12, 0, 0, 12, 0, 0], + [-11, 0, 0, 0, 11, 0], + [0, -12, 0, 0, -7, 19], + [0, 0, -11, 7, 0, 4], + [0, 0, 0, -19, -4, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_disconnected_graph(method): + # This tests the following disconnected graph: + # (0) --5--> (1) (2) --3--> (3) + graph = csr_matrix([[0, 5, 0, 0], + [0, 0, 0, 0], + [0, 0, 9, 3], + [0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 3, method=method) + assert res.flow_value == 0 + expected_flow = np.zeros((4, 4), dtype=np.int32) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_add_reverse_edges_large_graph(method): + # Regression test for https://github.com/scipy/scipy/issues/14385 + n = 100_000 + indices = np.arange(1, n) + indptr = np.array(list(range(n)) + [n - 1]) + data = np.ones(n - 1, dtype=np.int32) + graph = csr_matrix((data, indices, indptr), shape=(n, n)) + res = maximum_flow(graph, 0, n - 1, method=method) + assert res.flow_value == 1 + expected_flow = graph - graph.transpose() + assert_array_equal(res.flow.data, expected_flow.data) + assert_array_equal(res.flow.indices, expected_flow.indices) + assert_array_equal(res.flow.indptr, expected_flow.indptr) + + +@pytest.mark.parametrize("a,b_data_expected", [ + ([[]], []), + ([[0], [0]], []), + ([[1, 0, 2], [0, 0, 0], [0, 3, 0]], [1, 2, 0, 0, 3]), + ([[9, 8, 7], [4, 5, 6], [0, 0, 0]], [9, 8, 7, 4, 5, 6, 0, 0])]) +def test_add_reverse_edges(a, b_data_expected): + """Test that the reversal of the edges of the input graph works + as expected. + """ + a = csr_matrix(a, dtype=np.int32, shape=(len(a), len(a))) + b = _add_reverse_edges(a) + assert_array_equal(b.data, b_data_expected) + + +@pytest.mark.parametrize("a,expected", [ + ([[]], []), + ([[0]], []), + ([[1]], [0]), + ([[0, 1], [10, 0]], [1, 0]), + ([[1, 0, 2], [0, 0, 3], [4, 5, 0]], [0, 3, 4, 1, 2]) +]) +def test_make_edge_pointers(a, expected): + a = csr_matrix(a, dtype=np.int32) + rev_edge_ptr = _make_edge_pointers(a) + assert_array_equal(rev_edge_ptr, expected) + + +@pytest.mark.parametrize("a,expected", [ + ([[]], []), + ([[0]], []), + ([[1]], [0]), + ([[0, 1], [10, 0]], [0, 1]), + ([[1, 0, 2], [0, 0, 3], [4, 5, 0]], [0, 0, 1, 2, 2]) +]) +def test_make_tails(a, expected): + a = csr_matrix(a, dtype=np.int32) + tails = _make_tails(a) + assert_array_equal(tails, expected) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py new file mode 100644 index 0000000000000000000000000000000000000000..4a4213dcbec63530466078437d01ca7765494514 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py @@ -0,0 +1,369 @@ +import pytest +import numpy as np +from numpy.testing import assert_allclose +from pytest import raises as assert_raises +from scipy import sparse + +from scipy.sparse import csgraph +from scipy._lib._util import np_long, np_ulong + + +def check_int_type(mat): + return np.issubdtype(mat.dtype, np.signedinteger) or np.issubdtype( + mat.dtype, np_ulong + ) + + +def test_laplacian_value_error(): + for t in int, float, complex: + for m in ([1, 1], + [[[1]]], + [[1, 2, 3], [4, 5, 6]], + [[1, 2], [3, 4], [5, 5]]): + A = np.array(m, dtype=t) + assert_raises(ValueError, csgraph.laplacian, A) + + +def _explicit_laplacian(x, normed=False): + if sparse.issparse(x): + x = x.toarray() + x = np.asarray(x) + y = -1.0 * x + for j in range(y.shape[0]): + y[j,j] = x[j,j+1:].sum() + x[j,:j].sum() + if normed: + d = np.diag(y).copy() + d[d == 0] = 1.0 + y /= d[:,None]**.5 + y /= d[None,:]**.5 + return y + + +def _check_symmetric_graph_laplacian(mat, normed, copy=True): + if not hasattr(mat, 'shape'): + mat = eval(mat, dict(np=np, sparse=sparse)) + + if sparse.issparse(mat): + sp_mat = mat + mat = sp_mat.toarray() + else: + sp_mat = sparse.csr_matrix(mat) + + mat_copy = np.copy(mat) + sp_mat_copy = sparse.csr_matrix(sp_mat, copy=True) + + n_nodes = mat.shape[0] + explicit_laplacian = _explicit_laplacian(mat, normed=normed) + laplacian = csgraph.laplacian(mat, normed=normed, copy=copy) + sp_laplacian = csgraph.laplacian(sp_mat, normed=normed, + copy=copy) + + if copy: + assert_allclose(mat, mat_copy) + _assert_allclose_sparse(sp_mat, sp_mat_copy) + else: + if not (normed and check_int_type(mat)): + assert_allclose(laplacian, mat) + if sp_mat.format == 'coo': + _assert_allclose_sparse(sp_laplacian, sp_mat) + + assert_allclose(laplacian, sp_laplacian.toarray()) + + for tested in [laplacian, sp_laplacian.toarray()]: + if not normed: + assert_allclose(tested.sum(axis=0), np.zeros(n_nodes)) + assert_allclose(tested.T, tested) + assert_allclose(tested, explicit_laplacian) + + +def test_symmetric_graph_laplacian(): + symmetric_mats = ( + 'np.arange(10) * np.arange(10)[:, np.newaxis]', + 'np.ones((7, 7))', + 'np.eye(19)', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4))', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4)).toarray()', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4)).todense()', + 'np.vander(np.arange(4)) + np.vander(np.arange(4)).T' + ) + for mat in symmetric_mats: + for normed in True, False: + for copy in True, False: + _check_symmetric_graph_laplacian(mat, normed, copy) + + +def _assert_allclose_sparse(a, b, **kwargs): + # helper function that can deal with sparse matrices + if sparse.issparse(a): + a = a.toarray() + if sparse.issparse(b): + b = b.toarray() + assert_allclose(a, b, **kwargs) + + +def _check_laplacian_dtype_none( + A, desired_L, desired_d, normed, use_out_degree, copy, dtype, arr_type +): + mat = arr_type(A, dtype=dtype) + L, d = csgraph.laplacian( + mat, + normed=normed, + return_diag=True, + use_out_degree=use_out_degree, + copy=copy, + dtype=None, + ) + if normed and check_int_type(mat): + assert L.dtype == np.float64 + assert d.dtype == np.float64 + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + else: + assert L.dtype == dtype + assert d.dtype == dtype + desired_L = np.asarray(desired_L).astype(dtype) + desired_d = np.asarray(desired_d).astype(dtype) + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + + if not copy: + if not (normed and check_int_type(mat)): + if type(mat) is np.ndarray: + assert_allclose(L, mat) + elif mat.format == "coo": + _assert_allclose_sparse(L, mat) + + +def _check_laplacian_dtype( + A, desired_L, desired_d, normed, use_out_degree, copy, dtype, arr_type +): + mat = arr_type(A, dtype=dtype) + L, d = csgraph.laplacian( + mat, + normed=normed, + return_diag=True, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + ) + assert L.dtype == dtype + assert d.dtype == dtype + desired_L = np.asarray(desired_L).astype(dtype) + desired_d = np.asarray(desired_d).astype(dtype) + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + + if not copy: + if not (normed and check_int_type(mat)): + if type(mat) is np.ndarray: + assert_allclose(L, mat) + elif mat.format == 'coo': + _assert_allclose_sparse(L, mat) + + +INT_DTYPES = {np.intc, np_long, np.longlong} +REAL_DTYPES = {np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +DTYPES = sorted(INT_DTYPES ^ REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +@pytest.mark.parametrize("dtype", DTYPES) +@pytest.mark.parametrize("arr_type", [np.array, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array]) +@pytest.mark.parametrize("copy", [True, False]) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("use_out_degree", [True, False]) +def test_asymmetric_laplacian(use_out_degree, normed, + copy, dtype, arr_type): + # adjacency matrix + A = [[0, 1, 0], + [4, 2, 0], + [0, 0, 0]] + A = arr_type(np.array(A), dtype=dtype) + A_copy = A.copy() + + if not normed and use_out_degree: + # Laplacian matrix using out-degree + L = [[1, -1, 0], + [-4, 4, 0], + [0, 0, 0]] + d = [1, 4, 0] + + if normed and use_out_degree: + # normalized Laplacian matrix using out-degree + L = [[1, -0.5, 0], + [-2, 1, 0], + [0, 0, 0]] + d = [1, 2, 1] + + if not normed and not use_out_degree: + # Laplacian matrix using in-degree + L = [[4, -1, 0], + [-4, 1, 0], + [0, 0, 0]] + d = [4, 1, 0] + + if normed and not use_out_degree: + # normalized Laplacian matrix using in-degree + L = [[1, -0.5, 0], + [-2, 1, 0], + [0, 0, 0]] + d = [2, 1, 1] + + _check_laplacian_dtype_none( + A, + L, + d, + normed=normed, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + arr_type=arr_type, + ) + + _check_laplacian_dtype( + A_copy, + L, + d, + normed=normed, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + arr_type=arr_type, + ) + + +@pytest.mark.parametrize("fmt", ['csr', 'csc', 'coo', 'lil', + 'dok', 'dia', 'bsr']) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("copy", [True, False]) +def test_sparse_formats(fmt, normed, copy): + mat = sparse.diags([1, 1], [-1, 1], shape=(4, 4), format=fmt) + _check_symmetric_graph_laplacian(mat, normed, copy) + + +@pytest.mark.parametrize( + "arr_type", [np.asarray, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array] +) +@pytest.mark.parametrize("form", ["array", "function", "lo"]) +def test_laplacian_symmetrized(arr_type, form): + # adjacency matrix + n = 3 + mat = arr_type(np.arange(n * n).reshape(n, n)) + L_in, d_in = csgraph.laplacian( + mat, + return_diag=True, + form=form, + ) + L_out, d_out = csgraph.laplacian( + mat, + return_diag=True, + use_out_degree=True, + form=form, + ) + Ls, ds = csgraph.laplacian( + mat, + return_diag=True, + symmetrized=True, + form=form, + ) + Ls_normed, ds_normed = csgraph.laplacian( + mat, + return_diag=True, + symmetrized=True, + normed=True, + form=form, + ) + mat += mat.T + Lss, dss = csgraph.laplacian(mat, return_diag=True, form=form) + Lss_normed, dss_normed = csgraph.laplacian( + mat, + return_diag=True, + normed=True, + form=form, + ) + + assert_allclose(ds, d_in + d_out) + assert_allclose(ds, dss) + assert_allclose(ds_normed, dss_normed) + + d = {} + for L in ["L_in", "L_out", "Ls", "Ls_normed", "Lss", "Lss_normed"]: + if form == "array": + d[L] = eval(L) + else: + d[L] = eval(L)(np.eye(n, dtype=mat.dtype)) + + _assert_allclose_sparse(d["Ls"], d["L_in"] + d["L_out"].T) + _assert_allclose_sparse(d["Ls"], d["Lss"]) + _assert_allclose_sparse(d["Ls_normed"], d["Lss_normed"]) + + +@pytest.mark.parametrize( + "arr_type", [np.asarray, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array] +) +@pytest.mark.parametrize("dtype", DTYPES) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("symmetrized", [True, False]) +@pytest.mark.parametrize("use_out_degree", [True, False]) +@pytest.mark.parametrize("form", ["function", "lo"]) +def test_format(dtype, arr_type, normed, symmetrized, use_out_degree, form): + n = 3 + mat = [[0, 1, 0], [4, 2, 0], [0, 0, 0]] + mat = arr_type(np.array(mat), dtype=dtype) + Lo, do = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + ) + La, da = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + form="array", + ) + assert_allclose(do, da) + _assert_allclose_sparse(Lo, La) + + L, d = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + form=form, + ) + assert_allclose(d, do) + assert d.dtype == dtype + Lm = L(np.eye(n, dtype=mat.dtype)).astype(dtype) + _assert_allclose_sparse(Lm, Lo, rtol=2e-7, atol=2e-7) + x = np.arange(6).reshape(3, 2) + if not (normed and dtype in INT_DTYPES): + assert_allclose(L(x), Lo @ x) + else: + # Normalized Lo is casted to integer, but L() is not + pass + + +def test_format_error_message(): + with pytest.raises(ValueError, match="Invalid form: 'toto'"): + _ = csgraph.laplacian(np.eye(1), form='toto') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_matching.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_matching.py new file mode 100644 index 0000000000000000000000000000000000000000..87e2920fe971d22a16b473d543e2ad26ac8e777d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_matching.py @@ -0,0 +1,294 @@ +from itertools import product + +import numpy as np +from numpy.testing import assert_array_equal, assert_equal +import pytest + +from scipy.sparse import csr_matrix, coo_matrix, diags +from scipy.sparse.csgraph import ( + maximum_bipartite_matching, min_weight_full_bipartite_matching +) + + +def test_maximum_bipartite_matching_raises_on_dense_input(): + with pytest.raises(TypeError): + graph = np.array([[0, 1], [0, 0]]) + maximum_bipartite_matching(graph) + + +def test_maximum_bipartite_matching_empty_graph(): + graph = csr_matrix((0, 0)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + expected_matching = np.array([]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_empty_left_partition(): + graph = csr_matrix((2, 0)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([]), x) + assert_array_equal(np.array([-1, -1]), y) + + +def test_maximum_bipartite_matching_empty_right_partition(): + graph = csr_matrix((0, 3)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([-1, -1, -1]), x) + assert_array_equal(np.array([]), y) + + +def test_maximum_bipartite_matching_graph_with_no_edges(): + graph = csr_matrix((2, 2)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([-1, -1]), x) + assert_array_equal(np.array([-1, -1]), y) + + +def test_maximum_bipartite_matching_graph_that_causes_augmentation(): + # In this graph, column 1 is initially assigned to row 1, but it should be + # reassigned to make room for row 2. + graph = csr_matrix([[1, 1], [1, 0]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + expected_matching = np.array([1, 0]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_graph_with_more_rows_than_columns(): + graph = csr_matrix([[1, 1], [1, 0], [0, 1]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + assert_array_equal(np.array([0, -1, 1]), x) + assert_array_equal(np.array([0, 2]), y) + + +def test_maximum_bipartite_matching_graph_with_more_columns_than_rows(): + graph = csr_matrix([[1, 1, 0], [0, 0, 1]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + assert_array_equal(np.array([0, 2]), x) + assert_array_equal(np.array([0, -1, 1]), y) + + +def test_maximum_bipartite_matching_explicit_zeros_count_as_edges(): + data = [0, 0] + indices = [1, 0] + indptr = [0, 1, 2] + graph = csr_matrix((data, indices, indptr), shape=(2, 2)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + expected_matching = np.array([1, 0]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_feasibility_of_result(): + # This is a regression test for GitHub issue #11458 + data = np.ones(50, dtype=int) + indices = [11, 12, 19, 22, 23, 5, 22, 3, 8, 10, 5, 6, 11, 12, 13, 5, 13, + 14, 20, 22, 3, 15, 3, 13, 14, 11, 12, 19, 22, 23, 5, 22, 3, 8, + 10, 5, 6, 11, 12, 13, 5, 13, 14, 20, 22, 3, 15, 3, 13, 14] + indptr = [0, 5, 7, 10, 10, 15, 20, 22, 22, 23, 25, 30, 32, 35, 35, 40, 45, + 47, 47, 48, 50] + graph = csr_matrix((data, indices, indptr), shape=(20, 25)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert (x != -1).sum() == 13 + assert (y != -1).sum() == 13 + # Ensure that each element of the matching is in fact an edge in the graph. + for u, v in zip(range(graph.shape[0]), y): + if v != -1: + assert graph[u, v] + for u, v in zip(x, range(graph.shape[1])): + if u != -1: + assert graph[u, v] + + +def test_matching_large_random_graph_with_one_edge_incident_to_each_vertex(): + np.random.seed(42) + A = diags(np.ones(25), offsets=0, format='csr') + rand_perm = np.random.permutation(25) + rand_perm2 = np.random.permutation(25) + + Rrow = np.arange(25) + Rcol = rand_perm + Rdata = np.ones(25, dtype=int) + Rmat = coo_matrix((Rdata, (Rrow, Rcol))).tocsr() + + Crow = rand_perm2 + Ccol = np.arange(25) + Cdata = np.ones(25, dtype=int) + Cmat = coo_matrix((Cdata, (Crow, Ccol))).tocsr() + # Randomly permute identity matrix + B = Rmat * A * Cmat + + # Row permute + perm = maximum_bipartite_matching(B, perm_type='row') + Rrow = np.arange(25) + Rcol = perm + Rdata = np.ones(25, dtype=int) + Rmat = coo_matrix((Rdata, (Rrow, Rcol))).tocsr() + C1 = Rmat * B + + # Column permute + perm2 = maximum_bipartite_matching(B, perm_type='column') + Crow = perm2 + Ccol = np.arange(25) + Cdata = np.ones(25, dtype=int) + Cmat = coo_matrix((Cdata, (Crow, Ccol))).tocsr() + C2 = B * Cmat + + # Should get identity matrix back + assert_equal(any(C1.diagonal() == 0), False) + assert_equal(any(C2.diagonal() == 0), False) + + +@pytest.mark.parametrize('num_rows,num_cols', [(0, 0), (2, 0), (0, 3)]) +def test_min_weight_full_matching_trivial_graph(num_rows, num_cols): + biadjacency_matrix = csr_matrix((num_cols, num_rows)) + row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency_matrix) + assert len(row_ind) == 0 + assert len(col_ind) == 0 + + +@pytest.mark.parametrize('biadjacency_matrix', + [ + [[1, 1, 1], [1, 0, 0], [1, 0, 0]], + [[1, 1, 1], [0, 0, 1], [0, 0, 1]], + [[1, 0, 0, 1], [1, 1, 0, 1], [0, 0, 0, 0]], + [[1, 0, 0], [2, 0, 0]], + [[0, 1, 0], [0, 2, 0]], + [[1, 0], [2, 0], [5, 0]] + ]) +def test_min_weight_full_matching_infeasible_problems(biadjacency_matrix): + with pytest.raises(ValueError): + min_weight_full_bipartite_matching(csr_matrix(biadjacency_matrix)) + + +def test_min_weight_full_matching_large_infeasible(): + # Regression test for GitHub issue #17269 + a = np.asarray([ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], + [0.0, 0.11687445, 0.0, 0.0, 0.01319788, 0.07509257, 0.0, + 0.0, 0.0, 0.74228317, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.81087935, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.8408466, 0.0, 0.0, 0.0, 0.0, 0.01194389, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.82994211, 0.0, 0.0, 0.0, 0.11468516, 0.0, 0.0, 0.0, + 0.11173505, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0], + [0.18796507, 0.0, 0.04002318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75883335, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.71545464, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02748488, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.78470564, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.14829198, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.10870609, 0.0, 0.0, 0.0, 0.8918677, 0.0, 0.0, 0.0, 0.06306644, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.63844085, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7442354, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09850549, 0.0, 0.0, 0.18638258, + 0.2769244, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.73182464, 0.0, 0.0, 0.46443561, + 0.38589284, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.29510278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09666032, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + ]) + with pytest.raises(ValueError, match='no full matching exists'): + min_weight_full_bipartite_matching(csr_matrix(a)) + + +def test_explicit_zero_causes_warning(): + with pytest.warns(UserWarning): + biadjacency_matrix = csr_matrix(((2, 0, 3), (0, 1, 1), (0, 2, 3))) + min_weight_full_bipartite_matching(biadjacency_matrix) + + +# General test for linear sum assignment solvers to make it possible to rely +# on the same tests for scipy.optimize.linear_sum_assignment. +def linear_sum_assignment_assertions( + solver, array_type, sign, test_case +): + cost_matrix, expected_cost = test_case + maximize = sign == -1 + cost_matrix = sign * array_type(cost_matrix) + expected_cost = sign * np.array(expected_cost) + + row_ind, col_ind = solver(cost_matrix, maximize=maximize) + assert_array_equal(row_ind, np.sort(row_ind)) + assert_array_equal(expected_cost, + np.array(cost_matrix[row_ind, col_ind]).flatten()) + + cost_matrix = cost_matrix.T + row_ind, col_ind = solver(cost_matrix, maximize=maximize) + assert_array_equal(row_ind, np.sort(row_ind)) + assert_array_equal(np.sort(expected_cost), + np.sort(np.array( + cost_matrix[row_ind, col_ind])).flatten()) + + +linear_sum_assignment_test_cases = product( + [-1, 1], + [ + # Square + ([[400, 150, 400], + [400, 450, 600], + [300, 225, 300]], + [150, 400, 300]), + + # Rectangular variant + ([[400, 150, 400, 1], + [400, 450, 600, 2], + [300, 225, 300, 3]], + [150, 2, 300]), + + ([[10, 10, 8], + [9, 8, 1], + [9, 7, 4]], + [10, 1, 7]), + + # Square + ([[10, 10, 8, 11], + [9, 8, 1, 1], + [9, 7, 4, 10]], + [10, 1, 4]), + + # Rectangular variant + ([[10, float("inf"), float("inf")], + [float("inf"), float("inf"), 1], + [float("inf"), 7, float("inf")]], + [10, 1, 7]) + ]) + + +@pytest.mark.parametrize('sign,test_case', linear_sum_assignment_test_cases) +def test_min_weight_full_matching_small_inputs(sign, test_case): + linear_sum_assignment_assertions( + min_weight_full_bipartite_matching, csr_matrix, sign, test_case) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py new file mode 100644 index 0000000000000000000000000000000000000000..63ed5f61a430e4291c40284f1bbfff3165421013 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py @@ -0,0 +1,149 @@ +import pytest + +import numpy as np +import scipy.sparse as sp +import scipy.sparse.csgraph as spgraph + +from numpy.testing import assert_equal + +try: + import sparse +except Exception: + sparse = None + +pytestmark = pytest.mark.skipif(sparse is None, + reason="pydata/sparse not installed") + + +msg = "pydata/sparse (0.15.1) does not implement necessary operations" + + +sparse_params = (pytest.param("COO"), + pytest.param("DOK", marks=[pytest.mark.xfail(reason=msg)])) + + +@pytest.fixture(params=sparse_params) +def sparse_cls(request): + return getattr(sparse, request.param) + + +@pytest.fixture +def graphs(sparse_cls): + graph = [ + [0, 1, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 0], + ] + A_dense = np.array(graph) + A_sparse = sparse_cls(A_dense) + return A_dense, A_sparse + + +@pytest.mark.parametrize( + "func", + [ + spgraph.shortest_path, + spgraph.dijkstra, + spgraph.floyd_warshall, + spgraph.bellman_ford, + spgraph.johnson, + spgraph.reverse_cuthill_mckee, + spgraph.maximum_bipartite_matching, + spgraph.structural_rank, + ] +) +def test_csgraph_equiv(func, graphs): + A_dense, A_sparse = graphs + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + assert_equal(actual, desired) + + +def test_connected_components(graphs): + A_dense, A_sparse = graphs + func = spgraph.connected_components + + actual_comp, actual_labels = func(A_sparse) + desired_comp, desired_labels, = func(sp.csc_matrix(A_dense)) + + assert actual_comp == desired_comp + assert_equal(actual_labels, desired_labels) + + +def test_laplacian(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.laplacian + + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +@pytest.mark.parametrize( + "func", [spgraph.breadth_first_order, spgraph.depth_first_order] +) +def test_order_search(graphs, func): + A_dense, A_sparse = graphs + + actual = func(A_sparse, 0) + desired = func(sp.csc_matrix(A_dense), 0) + + assert_equal(actual, desired) + + +@pytest.mark.parametrize( + "func", [spgraph.breadth_first_tree, spgraph.depth_first_tree] +) +def test_tree_search(graphs, func): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + + actual = func(A_sparse, 0) + desired = func(sp.csc_matrix(A_dense), 0) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +def test_minimum_spanning_tree(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.minimum_spanning_tree + + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +def test_maximum_flow(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.maximum_flow + + actual = func(A_sparse, 0, 2) + desired = func(sp.csr_matrix(A_dense), 0, 2) + + assert actual.flow_value == desired.flow_value + assert isinstance(actual.flow, sparse_cls) + + assert_equal(actual.flow.todense(), desired.flow.todense()) + + +def test_min_weight_full_bipartite_matching(graphs): + A_dense, A_sparse = graphs + func = spgraph.min_weight_full_bipartite_matching + + actual = func(A_sparse[0:2, 1:3]) + desired = func(sp.csc_matrix(A_dense)[0:2, 1:3]) + + assert_equal(actual, desired) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_reordering.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_reordering.py new file mode 100644 index 0000000000000000000000000000000000000000..cb4c002fa303e7196278367afd316d47b3473cbb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_reordering.py @@ -0,0 +1,70 @@ +import numpy as np +from numpy.testing import assert_equal +from scipy.sparse.csgraph import reverse_cuthill_mckee, structural_rank +from scipy.sparse import csc_matrix, csr_matrix, coo_matrix + + +def test_graph_reverse_cuthill_mckee(): + A = np.array([[1, 0, 0, 0, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 1, 0, 1], + [0, 1, 1, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 1, 0], + [1, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 1, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 1, 0, 1]], dtype=int) + + graph = csr_matrix(A) + perm = reverse_cuthill_mckee(graph) + correct_perm = np.array([6, 3, 7, 5, 1, 2, 4, 0]) + assert_equal(perm, correct_perm) + + # Test int64 indices input + graph.indices = graph.indices.astype('int64') + graph.indptr = graph.indptr.astype('int64') + perm = reverse_cuthill_mckee(graph, True) + assert_equal(perm, correct_perm) + + +def test_graph_reverse_cuthill_mckee_ordering(): + data = np.ones(63,dtype=int) + rows = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, + 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, + 14, 15, 15, 15, 15, 15]) + cols = np.array([0, 2, 5, 8, 10, 1, 3, 9, 11, 0, 2, + 7, 10, 1, 3, 11, 4, 6, 12, 14, 0, 7, 13, + 15, 4, 6, 14, 2, 5, 7, 15, 0, 8, 10, 13, + 1, 9, 11, 0, 2, 8, 10, 15, 1, 3, 9, 11, + 4, 12, 14, 5, 8, 13, 15, 4, 6, 12, 14, + 5, 7, 10, 13, 15]) + graph = coo_matrix((data, (rows,cols))).tocsr() + perm = reverse_cuthill_mckee(graph) + correct_perm = np.array([12, 14, 4, 6, 10, 8, 2, 15, + 0, 13, 7, 5, 9, 11, 1, 3]) + assert_equal(perm, correct_perm) + + +def test_graph_structural_rank(): + # Test square matrix #1 + A = csc_matrix([[1, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + assert_equal(structural_rank(A), 3) + + # Test square matrix #2 + rows = np.array([0,0,0,0,0,1,1,2,2,3,3,3,3,3,3,4,4,5,5,6,6,7,7]) + cols = np.array([0,1,2,3,4,2,5,2,6,0,1,3,5,6,7,4,5,5,6,2,6,2,4]) + data = np.ones_like(rows) + B = coo_matrix((data,(rows,cols)), shape=(8,8)) + assert_equal(structural_rank(B), 6) + + #Test non-square matrix + C = csc_matrix([[1, 0, 2, 0], + [2, 0, 4, 0]]) + assert_equal(structural_rank(C), 2) + + #Test tall matrix + assert_equal(structural_rank(C.T), 2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py new file mode 100644 index 0000000000000000000000000000000000000000..f745e0fbba31f12e7ae17d5de05942578762d0af --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py @@ -0,0 +1,395 @@ +from io import StringIO +import warnings +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_array_equal, assert_allclose +from pytest import raises as assert_raises +from scipy.sparse.csgraph import (shortest_path, dijkstra, johnson, + bellman_ford, construct_dist_matrix, + NegativeCycleError) +import scipy.sparse +from scipy.io import mmread +import pytest + +directed_G = np.array([[0, 3, 3, 0, 0], + [0, 0, 0, 2, 4], + [0, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [2, 0, 0, 2, 0]], dtype=float) + +undirected_G = np.array([[0, 3, 3, 1, 2], + [3, 0, 0, 2, 4], + [3, 0, 0, 0, 0], + [1, 2, 0, 0, 2], + [2, 4, 0, 2, 0]], dtype=float) + +unweighted_G = (directed_G > 0).astype(float) + +directed_SP = [[0, 3, 3, 5, 7], + [3, 0, 6, 2, 4], + [np.inf, np.inf, 0, np.inf, np.inf], + [1, 4, 4, 0, 8], + [2, 5, 5, 2, 0]] + +directed_sparse_zero_G = scipy.sparse.csr_matrix(([0, 1, 2, 3, 1], + ([0, 1, 2, 3, 4], + [1, 2, 0, 4, 3])), + shape = (5, 5)) + +directed_sparse_zero_SP = [[0, 0, 1, np.inf, np.inf], + [3, 0, 1, np.inf, np.inf], + [2, 2, 0, np.inf, np.inf], + [np.inf, np.inf, np.inf, 0, 3], + [np.inf, np.inf, np.inf, 1, 0]] + +undirected_sparse_zero_G = scipy.sparse.csr_matrix(([0, 0, 1, 1, 2, 2, 1, 1], + ([0, 1, 1, 2, 2, 0, 3, 4], + [1, 0, 2, 1, 0, 2, 4, 3])), + shape = (5, 5)) + +undirected_sparse_zero_SP = [[0, 0, 1, np.inf, np.inf], + [0, 0, 1, np.inf, np.inf], + [1, 1, 0, np.inf, np.inf], + [np.inf, np.inf, np.inf, 0, 1], + [np.inf, np.inf, np.inf, 1, 0]] + +directed_pred = np.array([[-9999, 0, 0, 1, 1], + [3, -9999, 0, 1, 1], + [-9999, -9999, -9999, -9999, -9999], + [3, 0, 0, -9999, 1], + [4, 0, 0, 4, -9999]], dtype=float) + +undirected_SP = np.array([[0, 3, 3, 1, 2], + [3, 0, 6, 2, 4], + [3, 6, 0, 4, 5], + [1, 2, 4, 0, 2], + [2, 4, 5, 2, 0]], dtype=float) + +undirected_SP_limit_2 = np.array([[0, np.inf, np.inf, 1, 2], + [np.inf, 0, np.inf, 2, np.inf], + [np.inf, np.inf, 0, np.inf, np.inf], + [1, 2, np.inf, 0, 2], + [2, np.inf, np.inf, 2, 0]], dtype=float) + +undirected_SP_limit_0 = np.ones((5, 5), dtype=float) - np.eye(5) +undirected_SP_limit_0[undirected_SP_limit_0 > 0] = np.inf + +undirected_pred = np.array([[-9999, 0, 0, 0, 0], + [1, -9999, 0, 1, 1], + [2, 0, -9999, 0, 0], + [3, 3, 0, -9999, 3], + [4, 4, 0, 4, -9999]], dtype=float) + +directed_negative_weighted_G = np.array([[0, 0, 0], + [-1, 0, 0], + [0, -1, 0]], dtype=float) + +directed_negative_weighted_SP = np.array([[0, np.inf, np.inf], + [-1, 0, np.inf], + [-2, -1, 0]], dtype=float) + +methods = ['auto', 'FW', 'D', 'BF', 'J'] + + +def test_dijkstra_limit(): + limits = [0, 2, np.inf] + results = [undirected_SP_limit_0, + undirected_SP_limit_2, + undirected_SP] + + def check(limit, result): + SP = dijkstra(undirected_G, directed=False, limit=limit) + assert_array_almost_equal(SP, result) + + for limit, result in zip(limits, results): + check(limit, result) + + +def test_directed(): + def check(method): + SP = shortest_path(directed_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_SP) + + for method in methods: + check(method) + + +def test_undirected(): + def check(method, directed_in): + if directed_in: + SP1 = shortest_path(directed_G, method=method, directed=False, + overwrite=False) + assert_array_almost_equal(SP1, undirected_SP) + else: + SP2 = shortest_path(undirected_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP2, undirected_SP) + + for method in methods: + for directed_in in (True, False): + check(method, directed_in) + + +def test_directed_sparse_zero(): + # test directed sparse graph with zero-weight edge and two connected components + def check(method): + SP = shortest_path(directed_sparse_zero_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_sparse_zero_SP) + + for method in methods: + check(method) + + +def test_undirected_sparse_zero(): + def check(method, directed_in): + if directed_in: + SP1 = shortest_path(directed_sparse_zero_G, method=method, directed=False, + overwrite=False) + assert_array_almost_equal(SP1, undirected_sparse_zero_SP) + else: + SP2 = shortest_path(undirected_sparse_zero_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP2, undirected_sparse_zero_SP) + + for method in methods: + for directed_in in (True, False): + check(method, directed_in) + + +@pytest.mark.parametrize('directed, SP_ans', + ((True, directed_SP), + (False, undirected_SP))) +@pytest.mark.parametrize('indices', ([0, 2, 4], [0, 4], [3, 4], [0, 0])) +def test_dijkstra_indices_min_only(directed, SP_ans, indices): + SP_ans = np.array(SP_ans) + indices = np.array(indices, dtype=np.int64) + min_ind_ans = indices[np.argmin(SP_ans[indices, :], axis=0)] + min_d_ans = np.zeros(SP_ans.shape[0], SP_ans.dtype) + for k in range(SP_ans.shape[0]): + min_d_ans[k] = SP_ans[min_ind_ans[k], k] + min_ind_ans[np.isinf(min_d_ans)] = -9999 + + SP, pred, sources = dijkstra(directed_G, + directed=directed, + indices=indices, + min_only=True, + return_predecessors=True) + assert_array_almost_equal(SP, min_d_ans) + assert_array_equal(min_ind_ans, sources) + SP = dijkstra(directed_G, + directed=directed, + indices=indices, + min_only=True, + return_predecessors=False) + assert_array_almost_equal(SP, min_d_ans) + + +@pytest.mark.parametrize('n', (10, 100, 1000)) +def test_dijkstra_min_only_random(n): + np.random.seed(1234) + data = scipy.sparse.rand(n, n, density=0.5, format='lil', + random_state=42, dtype=np.float64) + data.setdiag(np.zeros(n, dtype=np.bool_)) + # choose some random vertices + v = np.arange(n) + np.random.shuffle(v) + indices = v[:int(n*.1)] + ds, pred, sources = dijkstra(data, + directed=True, + indices=indices, + min_only=True, + return_predecessors=True) + for k in range(n): + p = pred[k] + s = sources[k] + while p != -9999: + assert sources[p] == s + p = pred[p] + + +def test_dijkstra_random(): + # reproduces the hang observed in gh-17782 + n = 10 + indices = [0, 4, 4, 5, 7, 9, 0, 6, 2, 3, 7, 9, 1, 2, 9, 2, 5, 6] + indptr = [0, 0, 2, 5, 6, 7, 8, 12, 15, 18, 18] + data = [0.33629, 0.40458, 0.47493, 0.42757, 0.11497, 0.91653, 0.69084, + 0.64979, 0.62555, 0.743, 0.01724, 0.99945, 0.31095, 0.15557, + 0.02439, 0.65814, 0.23478, 0.24072] + graph = scipy.sparse.csr_matrix((data, indices, indptr), shape=(n, n)) + dijkstra(graph, directed=True, return_predecessors=True) + + +def test_gh_17782_segfault(): + text = """%%MatrixMarket matrix coordinate real general + 84 84 22 + 2 1 4.699999809265137e+00 + 6 14 1.199999973177910e-01 + 9 6 1.199999973177910e-01 + 10 16 2.012000083923340e+01 + 11 10 1.422000026702881e+01 + 12 1 9.645999908447266e+01 + 13 18 2.012000083923340e+01 + 14 13 4.679999828338623e+00 + 15 11 1.199999973177910e-01 + 16 12 1.199999973177910e-01 + 18 15 1.199999973177910e-01 + 32 2 2.299999952316284e+00 + 33 20 6.000000000000000e+00 + 33 32 5.000000000000000e+00 + 36 9 3.720000028610229e+00 + 36 37 3.720000028610229e+00 + 36 38 3.720000028610229e+00 + 37 44 8.159999847412109e+00 + 38 32 7.903999328613281e+01 + 43 20 2.400000000000000e+01 + 43 33 4.000000000000000e+00 + 44 43 6.028000259399414e+01 + """ + data = mmread(StringIO(text)) + dijkstra(data, directed=True, return_predecessors=True) + + +def test_shortest_path_indices(): + indices = np.arange(4) + + def check(func, indshape): + outshape = indshape + (5,) + SP = func(directed_G, directed=False, + indices=indices.reshape(indshape)) + assert_array_almost_equal(SP, undirected_SP[indices].reshape(outshape)) + + for indshape in [(4,), (4, 1), (2, 2)]: + for func in (dijkstra, bellman_ford, johnson, shortest_path): + check(func, indshape) + + assert_raises(ValueError, shortest_path, directed_G, method='FW', + indices=indices) + + +def test_predecessors(): + SP_res = {True: directed_SP, + False: undirected_SP} + pred_res = {True: directed_pred, + False: undirected_pred} + + def check(method, directed): + SP, pred = shortest_path(directed_G, method, directed=directed, + overwrite=False, + return_predecessors=True) + assert_array_almost_equal(SP, SP_res[directed]) + assert_array_almost_equal(pred, pred_res[directed]) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_construct_shortest_path(): + def check(method, directed): + SP1, pred = shortest_path(directed_G, + directed=directed, + overwrite=False, + return_predecessors=True) + SP2 = construct_dist_matrix(directed_G, pred, directed=directed) + assert_array_almost_equal(SP1, SP2) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_unweighted_path(): + def check(method, directed): + SP1 = shortest_path(directed_G, + directed=directed, + overwrite=False, + unweighted=True) + SP2 = shortest_path(unweighted_G, + directed=directed, + overwrite=False, + unweighted=False) + assert_array_almost_equal(SP1, SP2) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_negative_cycles(): + # create a small graph with a negative cycle + graph = np.ones([5, 5]) + graph.flat[::6] = 0 + graph[1, 2] = -2 + + def check(method, directed): + assert_raises(NegativeCycleError, shortest_path, graph, method, + directed) + + for method in ['FW', 'J', 'BF']: + for directed in (True, False): + check(method, directed) + + +@pytest.mark.parametrize("method", ['FW', 'J', 'BF']) +def test_negative_weights(method): + SP = shortest_path(directed_negative_weighted_G, method, directed=True) + assert_allclose(SP, directed_negative_weighted_SP, atol=1e-10) + + +def test_masked_input(): + np.ma.masked_equal(directed_G, 0) + + def check(method): + SP = shortest_path(directed_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_SP) + + for method in methods: + check(method) + + +def test_overwrite(): + G = np.array([[0, 3, 3, 1, 2], + [3, 0, 0, 2, 4], + [3, 0, 0, 0, 0], + [1, 2, 0, 0, 2], + [2, 4, 0, 2, 0]], dtype=float) + foo = G.copy() + shortest_path(foo, overwrite=False) + assert_array_equal(foo, G) + + +@pytest.mark.parametrize('method', methods) +def test_buffer(method): + # Smoke test that sparse matrices with read-only buffers (e.g., those from + # joblib workers) do not cause:: + # + # ValueError: buffer source array is read-only + # + G = scipy.sparse.csr_matrix([[1.]]) + G.data.flags['WRITEABLE'] = False + shortest_path(G, method=method) + + +def test_NaN_warnings(): + with warnings.catch_warnings(record=True) as record: + shortest_path(np.array([[0, 1], [np.nan, 0]])) + for r in record: + assert r.category is not RuntimeWarning + + +def test_sparse_matrices(): + # Test that using lil,csr and csc sparse matrix do not cause error + G_dense = np.array([[0, 3, 0, 0, 0], + [0, 0, -1, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 4], + [0, 0, 0, 0, 0]], dtype=float) + SP = shortest_path(G_dense) + G_csr = scipy.sparse.csr_matrix(G_dense) + G_csc = scipy.sparse.csc_matrix(G_dense) + G_lil = scipy.sparse.lil_matrix(G_dense) + assert_array_almost_equal(SP, shortest_path(G_csr)) + assert_array_almost_equal(SP, shortest_path(G_csc)) + assert_array_almost_equal(SP, shortest_path(G_lil)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py new file mode 100644 index 0000000000000000000000000000000000000000..90ef6d1b1ba86170b0264f76e6a63e21749acdc8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py @@ -0,0 +1,66 @@ +"""Test the minimum spanning tree function""" +import numpy as np +from numpy.testing import assert_ +import numpy.testing as npt +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import minimum_spanning_tree + + +def test_minimum_spanning_tree(): + + # Create a graph with two connected components. + graph = [[0,1,0,0,0], + [1,0,0,0,0], + [0,0,0,8,5], + [0,0,8,0,1], + [0,0,5,1,0]] + graph = np.asarray(graph) + + # Create the expected spanning tree. + expected = [[0,1,0,0,0], + [0,0,0,0,0], + [0,0,0,0,5], + [0,0,0,0,1], + [0,0,0,0,0]] + expected = np.asarray(expected) + + # Ensure minimum spanning tree code gives this expected output. + csgraph = csr_matrix(graph) + mintree = minimum_spanning_tree(csgraph) + mintree_array = mintree.toarray() + npt.assert_array_equal(mintree_array, expected, + 'Incorrect spanning tree found.') + + # Ensure that the original graph was not modified. + npt.assert_array_equal(csgraph.toarray(), graph, + 'Original graph was modified.') + + # Now let the algorithm modify the csgraph in place. + mintree = minimum_spanning_tree(csgraph, overwrite=True) + npt.assert_array_equal(mintree.toarray(), expected, + 'Graph was not properly modified to contain MST.') + + np.random.seed(1234) + for N in (5, 10, 15, 20): + + # Create a random graph. + graph = 3 + np.random.random((N, N)) + csgraph = csr_matrix(graph) + + # The spanning tree has at most N - 1 edges. + mintree = minimum_spanning_tree(csgraph) + assert_(mintree.nnz < N) + + # Set the sub diagonal to 1 to create a known spanning tree. + idx = np.arange(N-1) + graph[idx,idx+1] = 1 + csgraph = csr_matrix(graph) + mintree = minimum_spanning_tree(csgraph) + + # We expect to see this pattern in the spanning tree and otherwise + # have this zero. + expected = np.zeros((N, N)) + expected[idx, idx+1] = 1 + + npt.assert_array_equal(mintree.toarray(), expected, + 'Incorrect spanning tree found.') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_traversal.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_traversal.py new file mode 100644 index 0000000000000000000000000000000000000000..414e2d14864da8613eaf85f41a0b391ce1ae916d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csgraph/tests/test_traversal.py @@ -0,0 +1,81 @@ +import numpy as np +import pytest +from numpy.testing import assert_array_almost_equal +from scipy.sparse import csr_array +from scipy.sparse.csgraph import (breadth_first_tree, depth_first_tree, + csgraph_to_dense, csgraph_from_dense) + + +def test_graph_breadth_first(): + csgraph = np.array([[0, 1, 2, 0, 0], + [1, 0, 0, 0, 3], + [2, 0, 0, 7, 0], + [0, 0, 7, 0, 1], + [0, 3, 0, 1, 0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0, 1, 2, 0, 0], + [0, 0, 0, 0, 3], + [0, 0, 0, 7, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + + for directed in [True, False]: + bfirst_test = breadth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +def test_graph_depth_first(): + csgraph = np.array([[0, 1, 2, 0, 0], + [1, 0, 0, 0, 3], + [2, 0, 0, 7, 0], + [0, 0, 7, 0, 1], + [0, 3, 0, 1, 0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + dfirst = np.array([[0, 1, 0, 0, 0], + [0, 0, 0, 0, 3], + [0, 0, 0, 0, 0], + [0, 0, 7, 0, 0], + [0, 0, 0, 1, 0]]) + + for directed in [True, False]: + dfirst_test = depth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(dfirst_test), + dfirst) + + +def test_graph_breadth_first_trivial_graph(): + csgraph = np.array([[0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0]]) + + for directed in [True, False]: + bfirst_test = breadth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +def test_graph_depth_first_trivial_graph(): + csgraph = np.array([[0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0]]) + + for directed in [True, False]: + bfirst_test = depth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +@pytest.mark.parametrize('directed', [True, False]) +@pytest.mark.parametrize('tree_func', [breadth_first_tree, depth_first_tree]) +def test_int64_indices(tree_func, directed): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + tree = tree_func(g, 0, directed=directed) + assert_array_almost_equal(csgraph_to_dense(tree), [[0, 1], [0, 0]]) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csr.py new file mode 100644 index 0000000000000000000000000000000000000000..86bb1e072ebe4480e9dcb01f2d36f7387872b898 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/csr.py @@ -0,0 +1,27 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'csr_count_blocks', + 'csr_matrix', + 'csr_tobsr', + 'csr_tocsc', + 'get_csr_submatrix', + 'isspmatrix_csr', + 'spmatrix', + 'upcast', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="csr", + private_modules=["_csr"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/data.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/data.py new file mode 100644 index 0000000000000000000000000000000000000000..a9958bcda6dd35ac0779514d79b7f1c494c1b01a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/data.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'isscalarlike', + 'name', + 'npfunc', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="data", + private_modules=["_data"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dia.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dia.py new file mode 100644 index 0000000000000000000000000000000000000000..f79abd39f114b23df8ceb6eafb7fcc1c07218dcb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dia.py @@ -0,0 +1,29 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'check_shape', + 'dia_matrix', + 'dia_matvec', + 'get_sum_dtype', + 'getdtype', + 'isshape', + 'isspmatrix_dia', + 'spmatrix', + 'upcast_char', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="dia", + private_modules=["_dia"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dok.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dok.py new file mode 100644 index 0000000000000000000000000000000000000000..847824456eaa3145d5ecb078e30251875168775b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/dok.py @@ -0,0 +1,32 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'IndexMixin', + 'check_shape', + 'dok_matrix', + 'getdtype', + 'isdense', + 'isintlike', + 'isscalarlike', + 'isshape', + 'isspmatrix_dok', + 'itertools', + 'spmatrix', + 'upcast', + 'upcast_scalar', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="dok", + private_modules=["_dok"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/extract.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/extract.py new file mode 100644 index 0000000000000000000000000000000000000000..be5e161b6f99e57e2b2a6b3d4f1ef6427c07658d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/extract.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'coo_matrix', + 'find', + 'tril', + 'triu', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="extract", + private_modules=["_extract"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/lil.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/lil.py new file mode 100644 index 0000000000000000000000000000000000000000..31e5f20e4887c4e163aa2d807c89fe0768e3afb0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/lil.py @@ -0,0 +1,31 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'INT_TYPES', + 'IndexMixin', + 'bisect_left', + 'check_reshape_kwargs', + 'check_shape', + 'getdtype', + 'isscalarlike', + 'isshape', + 'isspmatrix_lil', + 'lil_matrix', + 'spmatrix', + 'upcast_scalar', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="lil", + private_modules=["_lil"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0d20b194dcbac5c2f48947d37e6b233edc2baf2b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/__init__.py @@ -0,0 +1,146 @@ +""" +Sparse linear algebra (:mod:`scipy.sparse.linalg`) +================================================== + +.. currentmodule:: scipy.sparse.linalg + +Abstract linear operators +------------------------- + +.. autosummary:: + :toctree: generated/ + + LinearOperator -- abstract representation of a linear operator + aslinearoperator -- convert an object to an abstract linear operator + +Matrix Operations +----------------- + +.. autosummary:: + :toctree: generated/ + + inv -- compute the sparse matrix inverse + expm -- compute the sparse matrix exponential + expm_multiply -- compute the product of a matrix exponential and a matrix + matrix_power -- compute the matrix power by raising a matrix to an exponent + +Matrix norms +------------ + +.. autosummary:: + :toctree: generated/ + + norm -- Norm of a sparse matrix + onenormest -- Estimate the 1-norm of a sparse matrix + +Solving linear problems +----------------------- + +Direct methods for linear equation systems: + +.. autosummary:: + :toctree: generated/ + + spsolve -- Solve the sparse linear system Ax=b + spsolve_triangular -- Solve sparse linear system Ax=b for a triangular A. + factorized -- Pre-factorize matrix to a function solving a linear system + MatrixRankWarning -- Warning on exactly singular matrices + use_solver -- Select direct solver to use + +Iterative methods for linear equation systems: + +.. autosummary:: + :toctree: generated/ + + bicg -- Use BIConjugate Gradient iteration to solve Ax = b + bicgstab -- Use BIConjugate Gradient STABilized iteration to solve Ax = b + cg -- Use Conjugate Gradient iteration to solve Ax = b + cgs -- Use Conjugate Gradient Squared iteration to solve Ax = b + gmres -- Use Generalized Minimal RESidual iteration to solve Ax = b + lgmres -- Solve a matrix equation using the LGMRES algorithm + minres -- Use MINimum RESidual iteration to solve Ax = b + qmr -- Use Quasi-Minimal Residual iteration to solve Ax = b + gcrotmk -- Solve a matrix equation using the GCROT(m,k) algorithm + tfqmr -- Use Transpose-Free Quasi-Minimal Residual iteration to solve Ax = b + +Iterative methods for least-squares problems: + +.. autosummary:: + :toctree: generated/ + + lsqr -- Find the least-squares solution to a sparse linear equation system + lsmr -- Find the least-squares solution to a sparse linear equation system + +Matrix factorizations +--------------------- + +Eigenvalue problems: + +.. autosummary:: + :toctree: generated/ + + eigs -- Find k eigenvalues and eigenvectors of the square matrix A + eigsh -- Find k eigenvalues and eigenvectors of a symmetric matrix + lobpcg -- Solve symmetric partial eigenproblems with optional preconditioning + +Singular values problems: + +.. autosummary:: + :toctree: generated/ + + svds -- Compute k singular values/vectors for a sparse matrix + +The `svds` function supports the following solvers: + +.. toctree:: + + sparse.linalg.svds-arpack + sparse.linalg.svds-lobpcg + sparse.linalg.svds-propack + +Complete or incomplete LU factorizations + +.. autosummary:: + :toctree: generated/ + + splu -- Compute a LU decomposition for a sparse matrix + spilu -- Compute an incomplete LU decomposition for a sparse matrix + SuperLU -- Object representing an LU factorization + +Sparse arrays with structure +---------------------------- + +.. autosummary:: + :toctree: generated/ + + LaplacianNd -- Laplacian on a uniform rectangular grid in ``N`` dimensions + +Exceptions +---------- + +.. autosummary:: + :toctree: generated/ + + ArpackNoConvergence + ArpackError + +""" + +from ._isolve import * +from ._dsolve import * +from ._interface import * +from ._eigen import * +from ._matfuncs import * +from ._onenormest import * +from ._norm import * +from ._expm_multiply import * +from ._special_sparse_arrays import * + +# Deprecated namespaces, to be removed in v2.0.0 +from . import isolve, dsolve, interface, eigen, matfuncs + +__all__ = [s for s in dir() if not s.startswith('_')] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ca1a0f435498acbeb4dc2fbdadf0baac86efcaa1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/__init__.py @@ -0,0 +1,71 @@ +""" +Linear Solvers +============== + +The default solver is SuperLU (included in the scipy distribution), +which can solve real or complex linear systems in both single and +double precisions. It is automatically replaced by UMFPACK, if +available. Note that UMFPACK works in double precision only, so +switch it off by:: + + >>> from scipy.sparse.linalg import spsolve, use_solver + >>> use_solver(useUmfpack=False) + +to solve in the single precision. See also use_solver documentation. + +Example session:: + + >>> from scipy.sparse import csc_matrix, spdiags + >>> from numpy import array + >>> + >>> print("Inverting a sparse linear system:") + >>> print("The sparse matrix (constructed from diagonals):") + >>> a = spdiags([[1, 2, 3, 4, 5], [6, 5, 8, 9, 10]], [0, 1], 5, 5) + >>> b = array([1, 2, 3, 4, 5]) + >>> print("Solve: single precision complex:") + >>> use_solver( useUmfpack = False ) + >>> a = a.astype('F') + >>> x = spsolve(a, b) + >>> print(x) + >>> print("Error: ", a@x-b) + >>> + >>> print("Solve: double precision complex:") + >>> use_solver( useUmfpack = True ) + >>> a = a.astype('D') + >>> x = spsolve(a, b) + >>> print(x) + >>> print("Error: ", a@x-b) + >>> + >>> print("Solve: double precision:") + >>> a = a.astype('d') + >>> x = spsolve(a, b) + >>> print(x) + >>> print("Error: ", a@x-b) + >>> + >>> print("Solve: single precision:") + >>> use_solver( useUmfpack = False ) + >>> a = a.astype('f') + >>> x = spsolve(a, b.astype('f')) + >>> print(x) + >>> print("Error: ", a@x-b) + +""" + +#import umfpack +#__doc__ = '\n\n'.join( (__doc__, umfpack.__doc__) ) +#del umfpack + +from .linsolve import * +from ._superlu import SuperLU +from . import _add_newdocs +from . import linsolve + +__all__ = [ + 'MatrixRankWarning', 'SuperLU', 'factorized', + 'spilu', 'splu', 'spsolve', + 'spsolve_triangular', 'use_solver' +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/_add_newdocs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/_add_newdocs.py new file mode 100644 index 0000000000000000000000000000000000000000..e7f7b1d16fd33b4beb5620e0a931a4c800d11cd4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/_add_newdocs.py @@ -0,0 +1,153 @@ +from numpy.lib import add_newdoc + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', + """ + LU factorization of a sparse matrix. + + Factorization is represented as:: + + Pr @ A @ Pc = L @ U + + To construct these `SuperLU` objects, call the `splu` and `spilu` + functions. + + Attributes + ---------- + shape + nnz + perm_c + perm_r + L + U + + Methods + ------- + solve + + Notes + ----- + + .. versionadded:: 0.14.0 + + Examples + -------- + The LU decomposition can be used to solve matrix equations. Consider: + + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import splu + >>> A = csc_matrix([[1,2,0,4], [1,0,0,1], [1,0,2,1], [2,2,1,0.]]) + + This can be solved for a given right-hand side: + + >>> lu = splu(A) + >>> b = np.array([1, 2, 3, 4]) + >>> x = lu.solve(b) + >>> A.dot(x) + array([ 1., 2., 3., 4.]) + + The ``lu`` object also contains an explicit representation of the + decomposition. The permutations are represented as mappings of + indices: + + >>> lu.perm_r + array([2, 1, 3, 0], dtype=int32) # may vary + >>> lu.perm_c + array([0, 1, 3, 2], dtype=int32) # may vary + + The L and U factors are sparse matrices in CSC format: + + >>> lu.L.toarray() + array([[ 1. , 0. , 0. , 0. ], # may vary + [ 0.5, 1. , 0. , 0. ], + [ 0.5, -1. , 1. , 0. ], + [ 0.5, 1. , 0. , 1. ]]) + >>> lu.U.toarray() + array([[ 2. , 2. , 0. , 1. ], # may vary + [ 0. , -1. , 1. , -0.5], + [ 0. , 0. , 5. , -1. ], + [ 0. , 0. , 0. , 2. ]]) + + The permutation matrices can be constructed: + + >>> Pr = csc_matrix((np.ones(4), (lu.perm_r, np.arange(4)))) + >>> Pc = csc_matrix((np.ones(4), (np.arange(4), lu.perm_c))) + + We can reassemble the original matrix: + + >>> (Pr.T @ (lu.L @ lu.U) @ Pc.T).toarray() + array([[ 1., 2., 0., 4.], + [ 1., 0., 0., 1.], + [ 1., 0., 2., 1.], + [ 2., 2., 1., 0.]]) + """) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('solve', + """ + solve(rhs[, trans]) + + Solves linear system of equations with one or several right-hand sides. + + Parameters + ---------- + rhs : ndarray, shape (n,) or (n, k) + Right hand side(s) of equation + trans : {'N', 'T', 'H'}, optional + Type of system to solve:: + + 'N': A @ x == rhs (default) + 'T': A^T @ x == rhs + 'H': A^H @ x == rhs + + i.e., normal, transposed, and hermitian conjugate. + + Returns + ------- + x : ndarray, shape ``rhs.shape`` + Solution vector(s) + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('L', + """ + Lower triangular factor with unit diagonal as a + `scipy.sparse.csc_matrix`. + + .. versionadded:: 0.14.0 + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('U', + """ + Upper triangular factor as a `scipy.sparse.csc_matrix`. + + .. versionadded:: 0.14.0 + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('shape', + """ + Shape of the original matrix as a tuple of ints. + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('nnz', + """ + Number of nonzero elements in the matrix. + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('perm_c', + """ + Permutation Pc represented as an array of indices. + + The column permutation matrix can be reconstructed via: + + >>> Pc = np.zeros((n, n)) + >>> Pc[np.arange(n), perm_c] = 1 + """)) + +add_newdoc('scipy.sparse.linalg._dsolve._superlu', 'SuperLU', ('perm_r', + """ + Permutation Pr represented as an array of indices. + + The row permutation matrix can be reconstructed via: + + >>> Pr = np.zeros((n, n)) + >>> Pr[perm_r, np.arange(n)] = 1 + """)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/linsolve.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/linsolve.py new file mode 100644 index 0000000000000000000000000000000000000000..e37721c76c133ee3ddbd44a6df03dfd99ee41e06 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/linsolve.py @@ -0,0 +1,746 @@ +from warnings import warn + +import numpy as np +from numpy import asarray +from scipy.sparse import (issparse, + SparseEfficiencyWarning, csc_matrix, csr_matrix) +from scipy.sparse._sputils import is_pydata_spmatrix, convert_pydata_sparse_to_scipy +from scipy.linalg import LinAlgError +import copy + +from . import _superlu + +noScikit = False +try: + import scikits.umfpack as umfpack +except ImportError: + noScikit = True + +useUmfpack = not noScikit + +__all__ = ['use_solver', 'spsolve', 'splu', 'spilu', 'factorized', + 'MatrixRankWarning', 'spsolve_triangular'] + + +class MatrixRankWarning(UserWarning): + pass + + +def use_solver(**kwargs): + """ + Select default sparse direct solver to be used. + + Parameters + ---------- + useUmfpack : bool, optional + Use UMFPACK [1]_, [2]_, [3]_, [4]_. over SuperLU. Has effect only + if ``scikits.umfpack`` is installed. Default: True + assumeSortedIndices : bool, optional + Allow UMFPACK to skip the step of sorting indices for a CSR/CSC matrix. + Has effect only if useUmfpack is True and ``scikits.umfpack`` is + installed. Default: False + + Notes + ----- + The default sparse solver is UMFPACK when available + (``scikits.umfpack`` is installed). This can be changed by passing + useUmfpack = False, which then causes the always present SuperLU + based solver to be used. + + UMFPACK requires a CSR/CSC matrix to have sorted column/row indices. If + sure that the matrix fulfills this, pass ``assumeSortedIndices=True`` + to gain some speed. + + References + ---------- + .. [1] T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern + multifrontal method with a column pre-ordering strategy, ACM + Trans. on Mathematical Software, 30(2), 2004, pp. 196--199. + https://dl.acm.org/doi/abs/10.1145/992200.992206 + + .. [2] T. A. Davis, A column pre-ordering strategy for the + unsymmetric-pattern multifrontal method, ACM Trans. + on Mathematical Software, 30(2), 2004, pp. 165--195. + https://dl.acm.org/doi/abs/10.1145/992200.992205 + + .. [3] T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal + method for unsymmetric sparse matrices, ACM Trans. on + Mathematical Software, 25(1), 1999, pp. 1--19. + https://doi.org/10.1145/305658.287640 + + .. [4] T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal + method for sparse LU factorization, SIAM J. Matrix Analysis and + Computations, 18(1), 1997, pp. 140--158. + https://doi.org/10.1137/S0895479894246905T. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import use_solver, spsolve + >>> from scipy.sparse import csc_matrix + >>> R = np.random.randn(5, 5) + >>> A = csc_matrix(R) + >>> b = np.random.randn(5) + >>> use_solver(useUmfpack=False) # enforce superLU over UMFPACK + >>> x = spsolve(A, b) + >>> np.allclose(A.dot(x), b) + True + >>> use_solver(useUmfpack=True) # reset umfPack usage to default + """ + if 'useUmfpack' in kwargs: + globals()['useUmfpack'] = kwargs['useUmfpack'] + if useUmfpack and 'assumeSortedIndices' in kwargs: + umfpack.configure(assumeSortedIndices=kwargs['assumeSortedIndices']) + +def _get_umf_family(A): + """Get umfpack family string given the sparse matrix dtype.""" + _families = { + (np.float64, np.int32): 'di', + (np.complex128, np.int32): 'zi', + (np.float64, np.int64): 'dl', + (np.complex128, np.int64): 'zl' + } + + # A.dtype.name can only be "float64" or + # "complex128" in control flow + f_type = getattr(np, A.dtype.name) + # control flow may allow for more index + # types to get through here + i_type = getattr(np, A.indices.dtype.name) + + try: + family = _families[(f_type, i_type)] + + except KeyError as e: + msg = ('only float64 or complex128 matrices with int32 or int64 ' + f'indices are supported! (got: matrix: {f_type}, indices: {i_type})') + raise ValueError(msg) from e + + # See gh-8278. Considered converting only if + # A.shape[0]*A.shape[1] > np.iinfo(np.int32).max, + # but that didn't always fix the issue. + family = family[0] + "l" + A_new = copy.copy(A) + A_new.indptr = np.asarray(A.indptr, dtype=np.int64) + A_new.indices = np.asarray(A.indices, dtype=np.int64) + + return family, A_new + +def _safe_downcast_indices(A): + # check for safe downcasting + max_value = np.iinfo(np.intc).max + + if A.indptr[-1] > max_value: # indptr[-1] is max b/c indptr always sorted + raise ValueError("indptr values too large for SuperLU") + + if max(*A.shape) > max_value: # only check large enough arrays + if np.any(A.indices > max_value): + raise ValueError("indices values too large for SuperLU") + + indices = A.indices.astype(np.intc, copy=False) + indptr = A.indptr.astype(np.intc, copy=False) + return indices, indptr + +def spsolve(A, b, permc_spec=None, use_umfpack=True): + """Solve the sparse linear system Ax=b, where b may be a vector or a matrix. + + Parameters + ---------- + A : ndarray or sparse matrix + The square matrix A will be converted into CSC or CSR form + b : ndarray or sparse matrix + The matrix or vector representing the right hand side of the equation. + If a vector, b.shape must be (n,) or (n, 1). + permc_spec : str, optional + How to permute the columns of the matrix for sparsity preservation. + (default: 'COLAMD') + + - ``NATURAL``: natural ordering. + - ``MMD_ATA``: minimum degree ordering on the structure of A^T A. + - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A. + - ``COLAMD``: approximate minimum degree column ordering [1]_, [2]_. + + use_umfpack : bool, optional + if True (default) then use UMFPACK for the solution [3]_, [4]_, [5]_, + [6]_ . This is only referenced if b is a vector and + ``scikits.umfpack`` is installed. + + Returns + ------- + x : ndarray or sparse matrix + the solution of the sparse linear equation. + If b is a vector, then x is a vector of size A.shape[1] + If b is a matrix, then x is a matrix of size (A.shape[1], b.shape[1]) + + Notes + ----- + For solving the matrix expression AX = B, this solver assumes the resulting + matrix X is sparse, as is often the case for very sparse inputs. If the + resulting X is dense, the construction of this sparse result will be + relatively expensive. In that case, consider converting A to a dense + matrix and using scipy.linalg.solve or its variants. + + References + ---------- + .. [1] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: + COLAMD, an approximate column minimum degree ordering algorithm, + ACM Trans. on Mathematical Software, 30(3), 2004, pp. 377--380. + :doi:`10.1145/1024074.1024080` + + .. [2] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate + minimum degree ordering algorithm, ACM Trans. on Mathematical + Software, 30(3), 2004, pp. 353--376. :doi:`10.1145/1024074.1024079` + + .. [3] T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern + multifrontal method with a column pre-ordering strategy, ACM + Trans. on Mathematical Software, 30(2), 2004, pp. 196--199. + https://dl.acm.org/doi/abs/10.1145/992200.992206 + + .. [4] T. A. Davis, A column pre-ordering strategy for the + unsymmetric-pattern multifrontal method, ACM Trans. + on Mathematical Software, 30(2), 2004, pp. 165--195. + https://dl.acm.org/doi/abs/10.1145/992200.992205 + + .. [5] T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal + method for unsymmetric sparse matrices, ACM Trans. on + Mathematical Software, 25(1), 1999, pp. 1--19. + https://doi.org/10.1145/305658.287640 + + .. [6] T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal + method for sparse LU factorization, SIAM J. Matrix Analysis and + Computations, 18(1), 1997, pp. 140--158. + https://doi.org/10.1137/S0895479894246905T. + + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import spsolve + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> B = csc_matrix([[2, 0], [-1, 0], [2, 0]], dtype=float) + >>> x = spsolve(A, B) + >>> np.allclose(A.dot(x).toarray(), B.toarray()) + True + """ + is_pydata_sparse = is_pydata_spmatrix(b) + pydata_sparse_cls = b.__class__ if is_pydata_sparse else None + A = convert_pydata_sparse_to_scipy(A) + b = convert_pydata_sparse_to_scipy(b) + + if not (issparse(A) and A.format in ("csc", "csr")): + A = csc_matrix(A) + warn('spsolve requires A be CSC or CSR matrix format', + SparseEfficiencyWarning, stacklevel=2) + + # b is a vector only if b have shape (n,) or (n, 1) + b_is_sparse = issparse(b) + if not b_is_sparse: + b = asarray(b) + b_is_vector = ((b.ndim == 1) or (b.ndim == 2 and b.shape[1] == 1)) + + # sum duplicates for non-canonical format + A.sum_duplicates() + A = A._asfptype() # upcast to a floating point format + result_dtype = np.promote_types(A.dtype, b.dtype) + if A.dtype != result_dtype: + A = A.astype(result_dtype) + if b.dtype != result_dtype: + b = b.astype(result_dtype) + + # validate input shapes + M, N = A.shape + if (M != N): + raise ValueError(f"matrix must be square (has shape {(M, N)})") + + if M != b.shape[0]: + raise ValueError(f"matrix - rhs dimension mismatch ({A.shape} - {b.shape[0]})") + + use_umfpack = use_umfpack and useUmfpack + + if b_is_vector and use_umfpack: + if b_is_sparse: + b_vec = b.toarray() + else: + b_vec = b + b_vec = asarray(b_vec, dtype=A.dtype).ravel() + + if noScikit: + raise RuntimeError('Scikits.umfpack not installed.') + + if A.dtype.char not in 'dD': + raise ValueError("convert matrix data to double, please, using" + " .astype(), or set linsolve.useUmfpack = False") + + umf_family, A = _get_umf_family(A) + umf = umfpack.UmfpackContext(umf_family) + x = umf.linsolve(umfpack.UMFPACK_A, A, b_vec, + autoTranspose=True) + else: + if b_is_vector and b_is_sparse: + b = b.toarray() + b_is_sparse = False + + if not b_is_sparse: + if A.format == "csc": + flag = 1 # CSC format + else: + flag = 0 # CSR format + + indices = A.indices.astype(np.intc, copy=False) + indptr = A.indptr.astype(np.intc, copy=False) + options = dict(ColPerm=permc_spec) + x, info = _superlu.gssv(N, A.nnz, A.data, indices, indptr, + b, flag, options=options) + if info != 0: + warn("Matrix is exactly singular", MatrixRankWarning, stacklevel=2) + x.fill(np.nan) + if b_is_vector: + x = x.ravel() + else: + # b is sparse + Afactsolve = factorized(A) + + if not (b.format == "csc" or is_pydata_spmatrix(b)): + warn('spsolve is more efficient when sparse b ' + 'is in the CSC matrix format', + SparseEfficiencyWarning, stacklevel=2) + b = csc_matrix(b) + + # Create a sparse output matrix by repeatedly applying + # the sparse factorization to solve columns of b. + data_segs = [] + row_segs = [] + col_segs = [] + for j in range(b.shape[1]): + # TODO: replace this with + # bj = b[:, j].toarray().ravel() + # once 1D sparse arrays are supported. + # That is a slightly faster code path. + bj = b[:, [j]].toarray().ravel() + xj = Afactsolve(bj) + w = np.flatnonzero(xj) + segment_length = w.shape[0] + row_segs.append(w) + col_segs.append(np.full(segment_length, j, dtype=int)) + data_segs.append(np.asarray(xj[w], dtype=A.dtype)) + sparse_data = np.concatenate(data_segs) + sparse_row = np.concatenate(row_segs) + sparse_col = np.concatenate(col_segs) + x = A.__class__((sparse_data, (sparse_row, sparse_col)), + shape=b.shape, dtype=A.dtype) + + if is_pydata_sparse: + x = pydata_sparse_cls.from_scipy_sparse(x) + + return x + + +def splu(A, permc_spec=None, diag_pivot_thresh=None, + relax=None, panel_size=None, options=dict()): + """ + Compute the LU decomposition of a sparse, square matrix. + + Parameters + ---------- + A : sparse matrix + Sparse matrix to factorize. Most efficient when provided in CSC + format. Other formats will be converted to CSC before factorization. + permc_spec : str, optional + How to permute the columns of the matrix for sparsity preservation. + (default: 'COLAMD') + + - ``NATURAL``: natural ordering. + - ``MMD_ATA``: minimum degree ordering on the structure of A^T A. + - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A. + - ``COLAMD``: approximate minimum degree column ordering + + diag_pivot_thresh : float, optional + Threshold used for a diagonal entry to be an acceptable pivot. + See SuperLU user's guide for details [1]_ + relax : int, optional + Expert option for customizing the degree of relaxing supernodes. + See SuperLU user's guide for details [1]_ + panel_size : int, optional + Expert option for customizing the panel size. + See SuperLU user's guide for details [1]_ + options : dict, optional + Dictionary containing additional expert options to SuperLU. + See SuperLU user guide [1]_ (section 2.4 on the 'Options' argument) + for more details. For example, you can specify + ``options=dict(Equil=False, IterRefine='SINGLE'))`` + to turn equilibration off and perform a single iterative refinement. + + Returns + ------- + invA : scipy.sparse.linalg.SuperLU + Object, which has a ``solve`` method. + + See also + -------- + spilu : incomplete LU decomposition + + Notes + ----- + This function uses the SuperLU library. + + References + ---------- + .. [1] SuperLU https://portal.nersc.gov/project/sparse/superlu/ + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import splu + >>> A = csc_matrix([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float) + >>> B = splu(A) + >>> x = np.array([1., 2., 3.], dtype=float) + >>> B.solve(x) + array([ 1. , -3. , -1.5]) + >>> A.dot(B.solve(x)) + array([ 1., 2., 3.]) + >>> B.solve(A.dot(x)) + array([ 1., 2., 3.]) + """ + + if is_pydata_spmatrix(A): + def csc_construct_func(*a, cls=type(A)): + return cls.from_scipy_sparse(csc_matrix(*a)) + A = A.to_scipy_sparse().tocsc() + else: + csc_construct_func = csc_matrix + + if not (issparse(A) and A.format == "csc"): + A = csc_matrix(A) + warn('splu converted its input to CSC format', + SparseEfficiencyWarning, stacklevel=2) + + # sum duplicates for non-canonical format + A.sum_duplicates() + A = A._asfptype() # upcast to a floating point format + + M, N = A.shape + if (M != N): + raise ValueError("can only factor square matrices") # is this true? + + indices, indptr = _safe_downcast_indices(A) + + _options = dict(DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec, + PanelSize=panel_size, Relax=relax) + if options is not None: + _options.update(options) + + # Ensure that no column permutations are applied + if (_options["ColPerm"] == "NATURAL"): + _options["SymmetricMode"] = True + + return _superlu.gstrf(N, A.nnz, A.data, indices, indptr, + csc_construct_func=csc_construct_func, + ilu=False, options=_options) + + +def spilu(A, drop_tol=None, fill_factor=None, drop_rule=None, permc_spec=None, + diag_pivot_thresh=None, relax=None, panel_size=None, options=None): + """ + Compute an incomplete LU decomposition for a sparse, square matrix. + + The resulting object is an approximation to the inverse of `A`. + + Parameters + ---------- + A : (N, N) array_like + Sparse matrix to factorize. Most efficient when provided in CSC format. + Other formats will be converted to CSC before factorization. + drop_tol : float, optional + Drop tolerance (0 <= tol <= 1) for an incomplete LU decomposition. + (default: 1e-4) + fill_factor : float, optional + Specifies the fill ratio upper bound (>= 1.0) for ILU. (default: 10) + drop_rule : str, optional + Comma-separated string of drop rules to use. + Available rules: ``basic``, ``prows``, ``column``, ``area``, + ``secondary``, ``dynamic``, ``interp``. (Default: ``basic,area``) + + See SuperLU documentation for details. + + Remaining other options + Same as for `splu` + + Returns + ------- + invA_approx : scipy.sparse.linalg.SuperLU + Object, which has a ``solve`` method. + + See also + -------- + splu : complete LU decomposition + + Notes + ----- + To improve the better approximation to the inverse, you may need to + increase `fill_factor` AND decrease `drop_tol`. + + This function uses the SuperLU library. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import spilu + >>> A = csc_matrix([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float) + >>> B = spilu(A) + >>> x = np.array([1., 2., 3.], dtype=float) + >>> B.solve(x) + array([ 1. , -3. , -1.5]) + >>> A.dot(B.solve(x)) + array([ 1., 2., 3.]) + >>> B.solve(A.dot(x)) + array([ 1., 2., 3.]) + """ + + if is_pydata_spmatrix(A): + def csc_construct_func(*a, cls=type(A)): + return cls.from_scipy_sparse(csc_matrix(*a)) + A = A.to_scipy_sparse().tocsc() + else: + csc_construct_func = csc_matrix + + if not (issparse(A) and A.format == "csc"): + A = csc_matrix(A) + warn('spilu converted its input to CSC format', + SparseEfficiencyWarning, stacklevel=2) + + # sum duplicates for non-canonical format + A.sum_duplicates() + A = A._asfptype() # upcast to a floating point format + + M, N = A.shape + if (M != N): + raise ValueError("can only factor square matrices") # is this true? + + indices, indptr = _safe_downcast_indices(A) + + _options = dict(ILU_DropRule=drop_rule, ILU_DropTol=drop_tol, + ILU_FillFactor=fill_factor, + DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec, + PanelSize=panel_size, Relax=relax) + if options is not None: + _options.update(options) + + # Ensure that no column permutations are applied + if (_options["ColPerm"] == "NATURAL"): + _options["SymmetricMode"] = True + + return _superlu.gstrf(N, A.nnz, A.data, indices, indptr, + csc_construct_func=csc_construct_func, + ilu=True, options=_options) + + +def factorized(A): + """ + Return a function for solving a sparse linear system, with A pre-factorized. + + Parameters + ---------- + A : (N, N) array_like + Input. A in CSC format is most efficient. A CSR format matrix will + be converted to CSC before factorization. + + Returns + ------- + solve : callable + To solve the linear system of equations given in `A`, the `solve` + callable should be passed an ndarray of shape (N,). + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import factorized + >>> from scipy.sparse import csc_matrix + >>> A = np.array([[ 3. , 2. , -1. ], + ... [ 2. , -2. , 4. ], + ... [-1. , 0.5, -1. ]]) + >>> solve = factorized(csc_matrix(A)) # Makes LU decomposition. + >>> rhs1 = np.array([1, -2, 0]) + >>> solve(rhs1) # Uses the LU factors. + array([ 1., -2., -2.]) + + """ + if is_pydata_spmatrix(A): + A = A.to_scipy_sparse().tocsc() + + if useUmfpack: + if noScikit: + raise RuntimeError('Scikits.umfpack not installed.') + + if not (issparse(A) and A.format == "csc"): + A = csc_matrix(A) + warn('splu converted its input to CSC format', + SparseEfficiencyWarning, stacklevel=2) + + A = A._asfptype() # upcast to a floating point format + + if A.dtype.char not in 'dD': + raise ValueError("convert matrix data to double, please, using" + " .astype(), or set linsolve.useUmfpack = False") + + umf_family, A = _get_umf_family(A) + umf = umfpack.UmfpackContext(umf_family) + + # Make LU decomposition. + umf.numeric(A) + + def solve(b): + with np.errstate(divide="ignore", invalid="ignore"): + # Ignoring warnings with numpy >= 1.23.0, see gh-16523 + result = umf.solve(umfpack.UMFPACK_A, A, b, autoTranspose=True) + + return result + + return solve + else: + return splu(A).solve + + +def spsolve_triangular(A, b, lower=True, overwrite_A=False, overwrite_b=False, + unit_diagonal=False): + """ + Solve the equation ``A x = b`` for `x`, assuming A is a triangular matrix. + + Parameters + ---------- + A : (M, M) sparse matrix + A sparse square triangular matrix. Should be in CSR format. + b : (M,) or (M, N) array_like + Right-hand side matrix in ``A x = b`` + lower : bool, optional + Whether `A` is a lower or upper triangular matrix. + Default is lower triangular matrix. + overwrite_A : bool, optional + Allow changing `A`. The indices of `A` are going to be sorted and zero + entries are going to be removed. + Enabling gives a performance gain. Default is False. + overwrite_b : bool, optional + Allow overwriting data in `b`. + Enabling gives a performance gain. Default is False. + If `overwrite_b` is True, it should be ensured that + `b` has an appropriate dtype to be able to store the result. + unit_diagonal : bool, optional + If True, diagonal elements of `a` are assumed to be 1 and will not be + referenced. + + .. versionadded:: 1.4.0 + + Returns + ------- + x : (M,) or (M, N) ndarray + Solution to the system ``A x = b``. Shape of return matches shape + of `b`. + + Raises + ------ + LinAlgError + If `A` is singular or not triangular. + ValueError + If shape of `A` or shape of `b` do not match the requirements. + + Notes + ----- + .. versionadded:: 0.19.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_matrix + >>> from scipy.sparse.linalg import spsolve_triangular + >>> A = csr_matrix([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float) + >>> B = np.array([[2, 0], [-1, 0], [2, 0]], dtype=float) + >>> x = spsolve_triangular(A, B) + >>> np.allclose(A.dot(x), B) + True + """ + + if is_pydata_spmatrix(A): + A = A.to_scipy_sparse().tocsr() + + # Check the input for correct type and format. + if not (issparse(A) and A.format == "csr"): + warn('CSR matrix format is required. Converting to CSR matrix.', + SparseEfficiencyWarning, stacklevel=2) + A = csr_matrix(A) + elif not overwrite_A: + A = A.copy() + + if A.shape[0] != A.shape[1]: + raise ValueError( + f'A must be a square matrix but its shape is {A.shape}.') + + # sum duplicates for non-canonical format + A.sum_duplicates() + + b = np.asanyarray(b) + + if b.ndim not in [1, 2]: + raise ValueError( + f'b must have 1 or 2 dims but its shape is {b.shape}.') + if A.shape[0] != b.shape[0]: + raise ValueError( + 'The size of the dimensions of A must be equal to ' + 'the size of the first dimension of b but the shape of A is ' + f'{A.shape} and the shape of b is {b.shape}.' + ) + + # Init x as (a copy of) b. + x_dtype = np.result_type(A.data, b, np.float64) + if overwrite_b: + if np.can_cast(b.dtype, x_dtype, casting='same_kind'): + x = b + else: + raise ValueError( + f'Cannot overwrite b (dtype {b.dtype}) with result ' + f'of type {x_dtype}.' + ) + else: + x = b.astype(x_dtype, copy=True) + + # Choose forward or backward order. + if lower: + row_indices = range(len(b)) + else: + row_indices = range(len(b) - 1, -1, -1) + + # Fill x iteratively. + for i in row_indices: + + # Get indices for i-th row. + indptr_start = A.indptr[i] + indptr_stop = A.indptr[i + 1] + + if lower: + A_diagonal_index_row_i = indptr_stop - 1 + A_off_diagonal_indices_row_i = slice(indptr_start, indptr_stop - 1) + else: + A_diagonal_index_row_i = indptr_start + A_off_diagonal_indices_row_i = slice(indptr_start + 1, indptr_stop) + + # Check regularity and triangularity of A. + if not unit_diagonal and (indptr_stop <= indptr_start + or A.indices[A_diagonal_index_row_i] < i): + raise LinAlgError( + f'A is singular: diagonal {i} is zero.') + if not unit_diagonal and A.indices[A_diagonal_index_row_i] > i: + raise LinAlgError( + 'A is not triangular: A[{}, {}] is nonzero.' + ''.format(i, A.indices[A_diagonal_index_row_i])) + + # Incorporate off-diagonal entries. + A_column_indices_in_row_i = A.indices[A_off_diagonal_indices_row_i] + A_values_in_row_i = A.data[A_off_diagonal_indices_row_i] + x[i] -= np.dot(x[A_column_indices_in_row_i].T, A_values_in_row_i) + + # Compute i-th entry of x. + if not unit_diagonal: + x[i] /= A.data[A_diagonal_index_row_i] + + return x diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/tests/test_linsolve.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/tests/test_linsolve.py new file mode 100644 index 0000000000000000000000000000000000000000..f1684b562ff20812a4280e3bdbac2f56086c5b1d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_dsolve/tests/test_linsolve.py @@ -0,0 +1,805 @@ +import sys +import threading + +import numpy as np +from numpy import array, finfo, arange, eye, all, unique, ones, dot +import numpy.random as random +from numpy.testing import ( + assert_array_almost_equal, assert_almost_equal, + assert_equal, assert_array_equal, assert_, assert_allclose, + assert_warns, suppress_warnings) +import pytest +from pytest import raises as assert_raises + +import scipy.linalg +from scipy.linalg import norm, inv +from scipy.sparse import (spdiags, SparseEfficiencyWarning, csc_matrix, + csr_matrix, identity, issparse, dok_matrix, lil_matrix, bsr_matrix) +from scipy.sparse.linalg import SuperLU +from scipy.sparse.linalg._dsolve import (spsolve, use_solver, splu, spilu, + MatrixRankWarning, _superlu, spsolve_triangular, factorized) +import scipy.sparse + +from scipy._lib._testutils import check_free_memory +from scipy._lib._util import ComplexWarning + + +sup_sparse_efficiency = suppress_warnings() +sup_sparse_efficiency.filter(SparseEfficiencyWarning) + +# scikits.umfpack is not a SciPy dependency but it is optionally used in +# dsolve, so check whether it's available +try: + import scikits.umfpack as umfpack + has_umfpack = True +except ImportError: + has_umfpack = False + +def toarray(a): + if issparse(a): + return a.toarray() + else: + return a + + +def setup_bug_8278(): + N = 2 ** 6 + h = 1/N + Ah1D = scipy.sparse.diags([-1, 2, -1], [-1, 0, 1], + shape=(N-1, N-1))/(h**2) + eyeN = scipy.sparse.eye(N - 1) + A = (scipy.sparse.kron(eyeN, scipy.sparse.kron(eyeN, Ah1D)) + + scipy.sparse.kron(eyeN, scipy.sparse.kron(Ah1D, eyeN)) + + scipy.sparse.kron(Ah1D, scipy.sparse.kron(eyeN, eyeN))) + b = np.random.rand((N-1)**3) + return A, b + + +class TestFactorized: + def setup_method(self): + n = 5 + d = arange(n) + 1 + self.n = n + self.A = spdiags((d, 2*d, d[::-1]), (-3, 0, 5), n, n).tocsc() + random.seed(1234) + + def _check_singular(self): + A = csc_matrix((5,5), dtype='d') + b = ones(5) + assert_array_almost_equal(0. * b, factorized(A)(b)) + + def _check_non_singular(self): + # Make a diagonal dominant, to make sure it is not singular + n = 5 + a = csc_matrix(random.rand(n, n)) + b = ones(n) + + expected = splu(a).solve(b) + assert_array_almost_equal(factorized(a)(b), expected) + + def test_singular_without_umfpack(self): + use_solver(useUmfpack=False) + with assert_raises(RuntimeError, match="Factor is exactly singular"): + self._check_singular() + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_singular_with_umfpack(self): + use_solver(useUmfpack=True) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "divide by zero encountered in double_scalars") + assert_warns(umfpack.UmfpackWarning, self._check_singular) + + def test_non_singular_without_umfpack(self): + use_solver(useUmfpack=False) + self._check_non_singular() + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_non_singular_with_umfpack(self): + use_solver(useUmfpack=True) + self._check_non_singular() + + def test_cannot_factorize_nonsquare_matrix_without_umfpack(self): + use_solver(useUmfpack=False) + msg = "can only factor square matrices" + with assert_raises(ValueError, match=msg): + factorized(self.A[:, :4]) + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_factorizes_nonsquare_matrix_with_umfpack(self): + use_solver(useUmfpack=True) + # does not raise + factorized(self.A[:,:4]) + + def test_call_with_incorrectly_sized_matrix_without_umfpack(self): + use_solver(useUmfpack=False) + solve = factorized(self.A) + b = random.rand(4) + B = random.rand(4, 3) + BB = random.rand(self.n, 3, 9) + + with assert_raises(ValueError, match="is of incompatible size"): + solve(b) + with assert_raises(ValueError, match="is of incompatible size"): + solve(B) + with assert_raises(ValueError, + match="object too deep for desired array"): + solve(BB) + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_call_with_incorrectly_sized_matrix_with_umfpack(self): + use_solver(useUmfpack=True) + solve = factorized(self.A) + b = random.rand(4) + B = random.rand(4, 3) + BB = random.rand(self.n, 3, 9) + + # does not raise + solve(b) + msg = "object too deep for desired array" + with assert_raises(ValueError, match=msg): + solve(B) + with assert_raises(ValueError, match=msg): + solve(BB) + + def test_call_with_cast_to_complex_without_umfpack(self): + use_solver(useUmfpack=False) + solve = factorized(self.A) + b = random.rand(4) + for t in [np.complex64, np.complex128]: + with assert_raises(TypeError, match="Cannot cast array data"): + solve(b.astype(t)) + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_call_with_cast_to_complex_with_umfpack(self): + use_solver(useUmfpack=True) + solve = factorized(self.A) + b = random.rand(4) + for t in [np.complex64, np.complex128]: + assert_warns(ComplexWarning, solve, b.astype(t)) + + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_assume_sorted_indices_flag(self): + # a sparse matrix with unsorted indices + unsorted_inds = np.array([2, 0, 1, 0]) + data = np.array([10, 16, 5, 0.4]) + indptr = np.array([0, 1, 2, 4]) + A = csc_matrix((data, unsorted_inds, indptr), (3, 3)) + b = ones(3) + + # should raise when incorrectly assuming indices are sorted + use_solver(useUmfpack=True, assumeSortedIndices=True) + with assert_raises(RuntimeError, + match="UMFPACK_ERROR_invalid_matrix"): + factorized(A) + + # should sort indices and succeed when not assuming indices are sorted + use_solver(useUmfpack=True, assumeSortedIndices=False) + expected = splu(A.copy()).solve(b) + + assert_equal(A.has_sorted_indices, 0) + assert_array_almost_equal(factorized(A)(b), expected) + + @pytest.mark.slow + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_bug_8278(self): + check_free_memory(8000) + use_solver(useUmfpack=True) + A, b = setup_bug_8278() + A = A.tocsc() + f = factorized(A) + x = f(b) + assert_array_almost_equal(A @ x, b) + + +class TestLinsolve: + def setup_method(self): + use_solver(useUmfpack=False) + + def test_singular(self): + A = csc_matrix((5,5), dtype='d') + b = array([1, 2, 3, 4, 5],dtype='d') + with suppress_warnings() as sup: + sup.filter(MatrixRankWarning, "Matrix is exactly singular") + x = spsolve(A, b) + assert_(not np.isfinite(x).any()) + + def test_singular_gh_3312(self): + # "Bad" test case that leads SuperLU to call LAPACK with invalid + # arguments. Check that it fails moderately gracefully. + ij = np.array([(17, 0), (17, 6), (17, 12), (10, 13)], dtype=np.int32) + v = np.array([0.284213, 0.94933781, 0.15767017, 0.38797296]) + A = csc_matrix((v, ij.T), shape=(20, 20)) + b = np.arange(20) + + try: + # should either raise a runtime error or return value + # appropriate for singular input (which yields the warning) + with suppress_warnings() as sup: + sup.filter(MatrixRankWarning, "Matrix is exactly singular") + x = spsolve(A, b) + assert not np.isfinite(x).any() + except RuntimeError: + pass + + @pytest.mark.parametrize('format', ['csc', 'csr']) + @pytest.mark.parametrize('idx_dtype', [np.int32, np.int64]) + def test_twodiags(self, format: str, idx_dtype: np.dtype): + A = spdiags([[1, 2, 3, 4, 5], [6, 5, 8, 9, 10]], [0, 1], 5, 5, + format=format) + b = array([1, 2, 3, 4, 5]) + + # condition number of A + cond_A = norm(A.toarray(), 2) * norm(inv(A.toarray()), 2) + + for t in ['f','d','F','D']: + eps = finfo(t).eps # floating point epsilon + b = b.astype(t) + Asp = A.astype(t) + Asp.indices = Asp.indices.astype(idx_dtype, copy=False) + Asp.indptr = Asp.indptr.astype(idx_dtype, copy=False) + + x = spsolve(Asp, b) + assert_(norm(b - Asp@x) < 10 * cond_A * eps) + + def test_bvector_smoketest(self): + Adense = array([[0., 1., 1.], + [1., 0., 1.], + [0., 0., 1.]]) + As = csc_matrix(Adense) + random.seed(1234) + x = random.randn(3) + b = As@x + x2 = spsolve(As, b) + + assert_array_almost_equal(x, x2) + + def test_bmatrix_smoketest(self): + Adense = array([[0., 1., 1.], + [1., 0., 1.], + [0., 0., 1.]]) + As = csc_matrix(Adense) + random.seed(1234) + x = random.randn(3, 4) + Bdense = As.dot(x) + Bs = csc_matrix(Bdense) + x2 = spsolve(As, Bs) + assert_array_almost_equal(x, x2.toarray()) + + @sup_sparse_efficiency + def test_non_square(self): + # A is not square. + A = ones((3, 4)) + b = ones((4, 1)) + assert_raises(ValueError, spsolve, A, b) + # A2 and b2 have incompatible shapes. + A2 = csc_matrix(eye(3)) + b2 = array([1.0, 2.0]) + assert_raises(ValueError, spsolve, A2, b2) + + @sup_sparse_efficiency + def test_example_comparison(self): + row = array([0,0,1,2,2,2]) + col = array([0,2,2,0,1,2]) + data = array([1,2,3,-4,5,6]) + sM = csr_matrix((data,(row,col)), shape=(3,3), dtype=float) + M = sM.toarray() + + row = array([0,0,1,1,0,0]) + col = array([0,2,1,1,0,0]) + data = array([1,1,1,1,1,1]) + sN = csr_matrix((data, (row,col)), shape=(3,3), dtype=float) + N = sN.toarray() + + sX = spsolve(sM, sN) + X = scipy.linalg.solve(M, N) + + assert_array_almost_equal(X, sX.toarray()) + + @sup_sparse_efficiency + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_shape_compatibility(self): + use_solver(useUmfpack=True) + A = csc_matrix([[1., 0], [0, 2]]) + bs = [ + [1, 6], + array([1, 6]), + [[1], [6]], + array([[1], [6]]), + csc_matrix([[1], [6]]), + csr_matrix([[1], [6]]), + dok_matrix([[1], [6]]), + bsr_matrix([[1], [6]]), + array([[1., 2., 3.], [6., 8., 10.]]), + csc_matrix([[1., 2., 3.], [6., 8., 10.]]), + csr_matrix([[1., 2., 3.], [6., 8., 10.]]), + dok_matrix([[1., 2., 3.], [6., 8., 10.]]), + bsr_matrix([[1., 2., 3.], [6., 8., 10.]]), + ] + + for b in bs: + x = np.linalg.solve(A.toarray(), toarray(b)) + for spmattype in [csc_matrix, csr_matrix, dok_matrix, lil_matrix]: + x1 = spsolve(spmattype(A), b, use_umfpack=True) + x2 = spsolve(spmattype(A), b, use_umfpack=False) + + # check solution + if x.ndim == 2 and x.shape[1] == 1: + # interprets also these as "vectors" + x = x.ravel() + + assert_array_almost_equal(toarray(x1), x, + err_msg=repr((b, spmattype, 1))) + assert_array_almost_equal(toarray(x2), x, + err_msg=repr((b, spmattype, 2))) + + # dense vs. sparse output ("vectors" are always dense) + if issparse(b) and x.ndim > 1: + assert_(issparse(x1), repr((b, spmattype, 1))) + assert_(issparse(x2), repr((b, spmattype, 2))) + else: + assert_(isinstance(x1, np.ndarray), repr((b, spmattype, 1))) + assert_(isinstance(x2, np.ndarray), repr((b, spmattype, 2))) + + # check output shape + if x.ndim == 1: + # "vector" + assert_equal(x1.shape, (A.shape[1],)) + assert_equal(x2.shape, (A.shape[1],)) + else: + # "matrix" + assert_equal(x1.shape, x.shape) + assert_equal(x2.shape, x.shape) + + A = csc_matrix((3, 3)) + b = csc_matrix((1, 3)) + assert_raises(ValueError, spsolve, A, b) + + @sup_sparse_efficiency + def test_ndarray_support(self): + A = array([[1., 2.], [2., 0.]]) + x = array([[1., 1.], [0.5, -0.5]]) + b = array([[2., 0.], [2., 2.]]) + + assert_array_almost_equal(x, spsolve(A, b)) + + def test_gssv_badinput(self): + N = 10 + d = arange(N) + 1.0 + A = spdiags((d, 2*d, d[::-1]), (-3, 0, 5), N, N) + + for spmatrix in (csc_matrix, csr_matrix): + A = spmatrix(A) + b = np.arange(N) + + def not_c_contig(x): + return x.repeat(2)[::2] + + def not_1dim(x): + return x[:,None] + + def bad_type(x): + return x.astype(bool) + + def too_short(x): + return x[:-1] + + badops = [not_c_contig, not_1dim, bad_type, too_short] + + for badop in badops: + msg = f"{spmatrix!r} {badop!r}" + # Not C-contiguous + assert_raises((ValueError, TypeError), _superlu.gssv, + N, A.nnz, badop(A.data), A.indices, A.indptr, + b, int(spmatrix == csc_matrix), err_msg=msg) + assert_raises((ValueError, TypeError), _superlu.gssv, + N, A.nnz, A.data, badop(A.indices), A.indptr, + b, int(spmatrix == csc_matrix), err_msg=msg) + assert_raises((ValueError, TypeError), _superlu.gssv, + N, A.nnz, A.data, A.indices, badop(A.indptr), + b, int(spmatrix == csc_matrix), err_msg=msg) + + def test_sparsity_preservation(self): + ident = csc_matrix([ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) + b = csc_matrix([ + [0, 1], + [1, 0], + [0, 0]]) + x = spsolve(ident, b) + assert_equal(ident.nnz, 3) + assert_equal(b.nnz, 2) + assert_equal(x.nnz, 2) + assert_allclose(x.A, b.A, atol=1e-12, rtol=1e-12) + + def test_dtype_cast(self): + A_real = scipy.sparse.csr_matrix([[1, 2, 0], + [0, 0, 3], + [4, 0, 5]]) + A_complex = scipy.sparse.csr_matrix([[1, 2, 0], + [0, 0, 3], + [4, 0, 5 + 1j]]) + b_real = np.array([1,1,1]) + b_complex = np.array([1,1,1]) + 1j*np.array([1,1,1]) + x = spsolve(A_real, b_real) + assert_(np.issubdtype(x.dtype, np.floating)) + x = spsolve(A_real, b_complex) + assert_(np.issubdtype(x.dtype, np.complexfloating)) + x = spsolve(A_complex, b_real) + assert_(np.issubdtype(x.dtype, np.complexfloating)) + x = spsolve(A_complex, b_complex) + assert_(np.issubdtype(x.dtype, np.complexfloating)) + + @pytest.mark.slow + @pytest.mark.skipif(not has_umfpack, reason="umfpack not available") + def test_bug_8278(self): + check_free_memory(8000) + use_solver(useUmfpack=True) + A, b = setup_bug_8278() + x = spsolve(A, b) + assert_array_almost_equal(A @ x, b) + + +class TestSplu: + def setup_method(self): + use_solver(useUmfpack=False) + n = 40 + d = arange(n) + 1 + self.n = n + self.A = spdiags((d, 2*d, d[::-1]), (-3, 0, 5), n, n, format='csc') + random.seed(1234) + + def _smoketest(self, spxlu, check, dtype, idx_dtype): + if np.issubdtype(dtype, np.complexfloating): + A = self.A + 1j*self.A.T + else: + A = self.A + + A = A.astype(dtype) + A.indices = A.indices.astype(idx_dtype, copy=False) + A.indptr = A.indptr.astype(idx_dtype, copy=False) + lu = spxlu(A) + + rng = random.RandomState(1234) + + # Input shapes + for k in [None, 1, 2, self.n, self.n+2]: + msg = f"k={k!r}" + + if k is None: + b = rng.rand(self.n) + else: + b = rng.rand(self.n, k) + + if np.issubdtype(dtype, np.complexfloating): + b = b + 1j*rng.rand(*b.shape) + b = b.astype(dtype) + + x = lu.solve(b) + check(A, b, x, msg) + + x = lu.solve(b, 'T') + check(A.T, b, x, msg) + + x = lu.solve(b, 'H') + check(A.T.conj(), b, x, msg) + + @sup_sparse_efficiency + def test_splu_smoketest(self): + self._internal_test_splu_smoketest() + + def _internal_test_splu_smoketest(self): + # Check that splu works at all + def check(A, b, x, msg=""): + eps = np.finfo(A.dtype).eps + r = A @ x + assert_(abs(r - b).max() < 1e3*eps, msg) + + for dtype in [np.float32, np.float64, np.complex64, np.complex128]: + for idx_dtype in [np.int32, np.int64]: + self._smoketest(splu, check, dtype, idx_dtype) + + @sup_sparse_efficiency + def test_spilu_smoketest(self): + self._internal_test_spilu_smoketest() + + def _internal_test_spilu_smoketest(self): + errors = [] + + def check(A, b, x, msg=""): + r = A @ x + err = abs(r - b).max() + assert_(err < 1e-2, msg) + if b.dtype in (np.float64, np.complex128): + errors.append(err) + + for dtype in [np.float32, np.float64, np.complex64, np.complex128]: + for idx_dtype in [np.int32, np.int64]: + self._smoketest(spilu, check, dtype, idx_dtype) + + assert_(max(errors) > 1e-5) + + @sup_sparse_efficiency + def test_spilu_drop_rule(self): + # Test passing in the drop_rule argument to spilu. + A = identity(2) + + rules = [ + b'basic,area'.decode('ascii'), # unicode + b'basic,area', # ascii + [b'basic', b'area'.decode('ascii')] + ] + for rule in rules: + # Argument should be accepted + assert_(isinstance(spilu(A, drop_rule=rule), SuperLU)) + + def test_splu_nnz0(self): + A = csc_matrix((5,5), dtype='d') + assert_raises(RuntimeError, splu, A) + + def test_spilu_nnz0(self): + A = csc_matrix((5,5), dtype='d') + assert_raises(RuntimeError, spilu, A) + + def test_splu_basic(self): + # Test basic splu functionality. + n = 30 + rng = random.RandomState(12) + a = rng.rand(n, n) + a[a < 0.95] = 0 + # First test with a singular matrix + a[:, 0] = 0 + a_ = csc_matrix(a) + # Matrix is exactly singular + assert_raises(RuntimeError, splu, a_) + + # Make a diagonal dominant, to make sure it is not singular + a += 4*eye(n) + a_ = csc_matrix(a) + lu = splu(a_) + b = ones(n) + x = lu.solve(b) + assert_almost_equal(dot(a, x), b) + + def test_splu_perm(self): + # Test the permutation vectors exposed by splu. + n = 30 + a = random.random((n, n)) + a[a < 0.95] = 0 + # Make a diagonal dominant, to make sure it is not singular + a += 4*eye(n) + a_ = csc_matrix(a) + lu = splu(a_) + # Check that the permutation indices do belong to [0, n-1]. + for perm in (lu.perm_r, lu.perm_c): + assert_(all(perm > -1)) + assert_(all(perm < n)) + assert_equal(len(unique(perm)), len(perm)) + + # Now make a symmetric, and test that the two permutation vectors are + # the same + # Note: a += a.T relies on undefined behavior. + a = a + a.T + a_ = csc_matrix(a) + lu = splu(a_) + assert_array_equal(lu.perm_r, lu.perm_c) + + @pytest.mark.parametrize("splu_fun, rtol", [(splu, 1e-7), (spilu, 1e-1)]) + def test_natural_permc(self, splu_fun, rtol): + # Test that the "NATURAL" permc_spec does not permute the matrix + np.random.seed(42) + n = 500 + p = 0.01 + A = scipy.sparse.random(n, n, p) + x = np.random.rand(n) + # Make A diagonal dominant to make sure it is not singular + A += (n+1)*scipy.sparse.identity(n) + A_ = csc_matrix(A) + b = A_ @ x + + # without permc_spec, permutation is not identity + lu = splu_fun(A_) + assert_(np.any(lu.perm_c != np.arange(n))) + + # with permc_spec="NATURAL", permutation is identity + lu = splu_fun(A_, permc_spec="NATURAL") + assert_array_equal(lu.perm_c, np.arange(n)) + + # Also, lu decomposition is valid + x2 = lu.solve(b) + assert_allclose(x, x2, rtol=rtol) + + @pytest.mark.skipif(not hasattr(sys, 'getrefcount'), reason="no sys.getrefcount") + def test_lu_refcount(self): + # Test that we are keeping track of the reference count with splu. + n = 30 + a = random.random((n, n)) + a[a < 0.95] = 0 + # Make a diagonal dominant, to make sure it is not singular + a += 4*eye(n) + a_ = csc_matrix(a) + lu = splu(a_) + + # And now test that we don't have a refcount bug + rc = sys.getrefcount(lu) + for attr in ('perm_r', 'perm_c'): + perm = getattr(lu, attr) + assert_equal(sys.getrefcount(lu), rc + 1) + del perm + assert_equal(sys.getrefcount(lu), rc) + + def test_bad_inputs(self): + A = self.A.tocsc() + + assert_raises(ValueError, splu, A[:,:4]) + assert_raises(ValueError, spilu, A[:,:4]) + + for lu in [splu(A), spilu(A)]: + b = random.rand(42) + B = random.rand(42, 3) + BB = random.rand(self.n, 3, 9) + assert_raises(ValueError, lu.solve, b) + assert_raises(ValueError, lu.solve, B) + assert_raises(ValueError, lu.solve, BB) + assert_raises(TypeError, lu.solve, + b.astype(np.complex64)) + assert_raises(TypeError, lu.solve, + b.astype(np.complex128)) + + @sup_sparse_efficiency + def test_superlu_dlamch_i386_nan(self): + # SuperLU 4.3 calls some functions returning floats without + # declaring them. On i386@linux call convention, this fails to + # clear floating point registers after call. As a result, NaN + # can appear in the next floating point operation made. + # + # Here's a test case that triggered the issue. + n = 8 + d = np.arange(n) + 1 + A = spdiags((d, 2*d, d[::-1]), (-3, 0, 5), n, n) + A = A.astype(np.float32) + spilu(A) + A = A + 1j*A + B = A.A + assert_(not np.isnan(B).any()) + + @sup_sparse_efficiency + def test_lu_attr(self): + + def check(dtype, complex_2=False): + A = self.A.astype(dtype) + + if complex_2: + A = A + 1j*A.T + + n = A.shape[0] + lu = splu(A) + + # Check that the decomposition is as advertised + + Pc = np.zeros((n, n)) + Pc[np.arange(n), lu.perm_c] = 1 + + Pr = np.zeros((n, n)) + Pr[lu.perm_r, np.arange(n)] = 1 + + Ad = A.toarray() + lhs = Pr.dot(Ad).dot(Pc) + rhs = (lu.L @ lu.U).toarray() + + eps = np.finfo(dtype).eps + + assert_allclose(lhs, rhs, atol=100*eps) + + check(np.float32) + check(np.float64) + check(np.complex64) + check(np.complex128) + check(np.complex64, True) + check(np.complex128, True) + + @pytest.mark.slow + @sup_sparse_efficiency + def test_threads_parallel(self): + oks = [] + + def worker(): + try: + self.test_splu_basic() + self._internal_test_splu_smoketest() + self._internal_test_spilu_smoketest() + oks.append(True) + except Exception: + pass + + threads = [threading.Thread(target=worker) + for k in range(20)] + for t in threads: + t.start() + for t in threads: + t.join() + + assert_equal(len(oks), 20) + + +class TestSpsolveTriangular: + def setup_method(self): + use_solver(useUmfpack=False) + + def test_zero_diagonal(self): + n = 5 + rng = np.random.default_rng(43876432987) + A = rng.standard_normal((n, n)) + b = np.arange(n) + A = scipy.sparse.tril(A, k=0, format='csr') + + x = spsolve_triangular(A, b, unit_diagonal=True, lower=True) + + A.setdiag(1) + assert_allclose(A.dot(x), b) + + # Regression test from gh-15199 + A = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0]], dtype=np.float64) + b = np.array([1., 2., 3.]) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "CSR matrix format is") + spsolve_triangular(A, b, unit_diagonal=True) + + def test_singular(self): + n = 5 + A = csr_matrix((n, n)) + b = np.arange(n) + for lower in (True, False): + assert_raises(scipy.linalg.LinAlgError, + spsolve_triangular, A, b, lower=lower) + + @sup_sparse_efficiency + def test_bad_shape(self): + # A is not square. + A = np.zeros((3, 4)) + b = ones((4, 1)) + assert_raises(ValueError, spsolve_triangular, A, b) + # A2 and b2 have incompatible shapes. + A2 = csr_matrix(eye(3)) + b2 = array([1.0, 2.0]) + assert_raises(ValueError, spsolve_triangular, A2, b2) + + @sup_sparse_efficiency + def test_input_types(self): + A = array([[1., 0.], [1., 2.]]) + b = array([[2., 0.], [2., 2.]]) + for matrix_type in (array, csc_matrix, csr_matrix): + x = spsolve_triangular(matrix_type(A), b, lower=True) + assert_array_almost_equal(A.dot(x), b) + + @pytest.mark.slow + @pytest.mark.timeout(120) # prerelease_deps_coverage_64bit_blas job + @sup_sparse_efficiency + def test_random(self): + def random_triangle_matrix(n, lower=True): + A = scipy.sparse.random(n, n, density=0.1, format='coo') + if lower: + A = scipy.sparse.tril(A) + else: + A = scipy.sparse.triu(A) + A = A.tocsr(copy=False) + for i in range(n): + A[i, i] = np.random.rand() + 1 + return A + + np.random.seed(1234) + for lower in (True, False): + for n in (10, 10**2, 10**3): + A = random_triangle_matrix(n, lower=lower) + for m in (1, 10): + for b in (np.random.rand(n, m), + np.random.randint(-9, 9, (n, m)), + np.random.randint(-9, 9, (n, m)) + + np.random.randint(-9, 9, (n, m)) * 1j): + x = spsolve_triangular(A, b, lower=lower) + assert_array_almost_equal(A.dot(x), b) + x = spsolve_triangular(A, b, lower=lower, + unit_diagonal=True) + A.setdiag(1) + assert_array_almost_equal(A.dot(x), b) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..25278d34ecd3353d409a25f7a94797902fe6ef93 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/__init__.py @@ -0,0 +1,22 @@ +""" +Sparse Eigenvalue Solvers +------------------------- + +The submodules of sparse.linalg._eigen: + 1. lobpcg: Locally Optimal Block Preconditioned Conjugate Gradient Method + +""" +from .arpack import * +from .lobpcg import * +from ._svds import svds + +from . import arpack + +__all__ = [ + 'ArpackError', 'ArpackNoConvergence', + 'eigs', 'eigsh', 'lobpcg', 'svds' +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds.py new file mode 100644 index 0000000000000000000000000000000000000000..f70b8f922ed63a976568d97b0045f589d592512e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds.py @@ -0,0 +1,545 @@ +import numpy as np + +from .arpack import _arpack # type: ignore[attr-defined] +from . import eigsh + +from scipy._lib._util import check_random_state +from scipy.sparse.linalg._interface import LinearOperator, aslinearoperator +from scipy.sparse.linalg._eigen.lobpcg import lobpcg # type: ignore[no-redef] +from scipy.sparse.linalg._svdp import _svdp +from scipy.linalg import svd + +arpack_int = _arpack.timing.nbx.dtype +__all__ = ['svds'] + + +def _herm(x): + return x.T.conj() + + +def _iv(A, k, ncv, tol, which, v0, maxiter, + return_singular, solver, random_state): + + # input validation/standardization for `solver` + # out of order because it's needed for other parameters + solver = str(solver).lower() + solvers = {"arpack", "lobpcg", "propack"} + if solver not in solvers: + raise ValueError(f"solver must be one of {solvers}.") + + # input validation/standardization for `A` + A = aslinearoperator(A) # this takes care of some input validation + if not (np.issubdtype(A.dtype, np.complexfloating) + or np.issubdtype(A.dtype, np.floating)): + message = "`A` must be of floating or complex floating data type." + raise ValueError(message) + if np.prod(A.shape) == 0: + message = "`A` must not be empty." + raise ValueError(message) + + # input validation/standardization for `k` + kmax = min(A.shape) if solver == 'propack' else min(A.shape) - 1 + if int(k) != k or not (0 < k <= kmax): + message = "`k` must be an integer satisfying `0 < k < min(A.shape)`." + raise ValueError(message) + k = int(k) + + # input validation/standardization for `ncv` + if solver == "arpack" and ncv is not None: + if int(ncv) != ncv or not (k < ncv < min(A.shape)): + message = ("`ncv` must be an integer satisfying " + "`k < ncv < min(A.shape)`.") + raise ValueError(message) + ncv = int(ncv) + + # input validation/standardization for `tol` + if tol < 0 or not np.isfinite(tol): + message = "`tol` must be a non-negative floating point value." + raise ValueError(message) + tol = float(tol) + + # input validation/standardization for `which` + which = str(which).upper() + whichs = {'LM', 'SM'} + if which not in whichs: + raise ValueError(f"`which` must be in {whichs}.") + + # input validation/standardization for `v0` + if v0 is not None: + v0 = np.atleast_1d(v0) + if not (np.issubdtype(v0.dtype, np.complexfloating) + or np.issubdtype(v0.dtype, np.floating)): + message = ("`v0` must be of floating or complex floating " + "data type.") + raise ValueError(message) + + shape = (A.shape[0],) if solver == 'propack' else (min(A.shape),) + if v0.shape != shape: + message = f"`v0` must have shape {shape}." + raise ValueError(message) + + # input validation/standardization for `maxiter` + if maxiter is not None and (int(maxiter) != maxiter or maxiter <= 0): + message = "`maxiter` must be a positive integer." + raise ValueError(message) + maxiter = int(maxiter) if maxiter is not None else maxiter + + # input validation/standardization for `return_singular_vectors` + # not going to be flexible with this; too complicated for little gain + rs_options = {True, False, "vh", "u"} + if return_singular not in rs_options: + raise ValueError(f"`return_singular_vectors` must be in {rs_options}.") + + random_state = check_random_state(random_state) + + return (A, k, ncv, tol, which, v0, maxiter, + return_singular, solver, random_state) + + +def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='arpack', random_state=None, options=None): + """ + Partial singular value decomposition of a sparse matrix. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : ndarray, sparse matrix, or LinearOperator + Matrix to decompose of a floating point numeric dtype. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= kmax``, where ``kmax=min(M, N)`` for + ``solver='propack'`` and ``kmax=min(M, N) - 1`` otherwise. + ncv : int, optional + When ``solver='arpack'``, this is the number of Lanczos vectors + generated. See :ref:`'arpack' ` for details. + When ``solver='lobpcg'`` or ``solver='propack'``, this parameter is + ignored. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + The starting vector for iteration; see method-specific + documentation (:ref:`'arpack' `, + :ref:`'lobpcg' `), or + :ref:`'propack' ` for details. + maxiter : int, optional + Maximum number of iterations; see method-specific + documentation (:ref:`'arpack' `, + :ref:`'lobpcg' `), or + :ref:`'propack' ` for details. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + If ``solver='propack'``, the option is respected regardless of the + matrix shape. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + The solver used. + :ref:`'arpack' `, + :ref:`'lobpcg' `, and + :ref:`'propack' ` are supported. + Default: `'arpack'`. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using ARPACK or LOBPCG as an eigensolver + on the matrix ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on + which one is smaller size, followed by the Rayleigh-Ritz method + as postprocessing; see + Using the normal matrix, in Rayleigh-Ritz method, (2022, Nov. 19), + Wikipedia, https://w.wiki/4zms. + + Alternatively, the PROPACK solver can be called. + + Choices of the input matrix `A` numeric dtype may be limited. + Only ``solver="lobpcg"`` supports all floating point dtypes + real: 'np.float32', 'np.float64', 'np.longdouble' and + complex: 'np.complex64', 'np.complex128', 'np.clongdouble'. + The ``solver="arpack"`` supports only + 'np.float32', 'np.float64', and 'np.complex128'. + + Examples + -------- + Construct a matrix `A` from singular values and vectors. + + >>> import numpy as np + >>> from scipy import sparse, linalg, stats + >>> from scipy.sparse.linalg import svds, aslinearoperator, LinearOperator + + Construct a dense matrix `A` from singular values and vectors. + + >>> rng = np.random.default_rng(258265244568965474821194062361901728911) + >>> orthogonal = stats.ortho_group.rvs(10, random_state=rng) + >>> s = [1e-3, 1, 2, 3, 4] # non-zero singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ np.diag(s) @ vT + + With only four singular values/vectors, the SVD approximates the original + matrix. + + >>> u4, s4, vT4 = svds(A, k=4) + >>> A4 = u4 @ np.diag(s4) @ vT4 + >>> np.allclose(A4, A, atol=1e-3) + True + + With all five non-zero singular values/vectors, we can reproduce + the original matrix more accurately. + + >>> u5, s5, vT5 = svds(A, k=5) + >>> A5 = u5 @ np.diag(s5) @ vT5 + >>> np.allclose(A5, A) + True + + The singular values match the expected singular values. + + >>> np.allclose(s5, s) + True + + Since the singular values are not close to each other in this example, + every singular vector matches as expected up to a difference in sign. + + >>> (np.allclose(np.abs(u5), np.abs(u)) and + ... np.allclose(np.abs(vT5), np.abs(vT))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u5.T @ u5, np.eye(5)) and + ... np.allclose(vT5 @ vT5.T, np.eye(5))) + True + + If there are (nearly) multiple singular values, the corresponding + individual singular vectors may be unstable, but the whole invariant + subspace containing all such singular vectors is computed accurately + as can be measured by angles between subspaces via 'subspace_angles'. + + >>> rng = np.random.default_rng(178686584221410808734965903901790843963) + >>> s = [1, 1 + 1e-6] # non-zero singular values + >>> u, _ = np.linalg.qr(rng.standard_normal((99, 2))) + >>> v, _ = np.linalg.qr(rng.standard_normal((99, 2))) + >>> vT = v.T + >>> A = u @ np.diag(s) @ vT + >>> A = A.astype(np.float32) + >>> u2, s2, vT2 = svds(A, k=2, random_state=rng) + >>> np.allclose(s2, s) + True + + The angles between the individual exact and computed singular vectors + may not be so small. To check use: + + >>> (linalg.subspace_angles(u2[:, :1], u[:, :1]) + + ... linalg.subspace_angles(u2[:, 1:], u[:, 1:])) + array([0.06562513]) # may vary + >>> (linalg.subspace_angles(vT2[:1, :].T, vT[:1, :].T) + + ... linalg.subspace_angles(vT2[1:, :].T, vT[1:, :].T)) + array([0.06562507]) # may vary + + As opposed to the angles between the 2-dimensional invariant subspaces + that these vectors span, which are small for rights singular vectors + + >>> linalg.subspace_angles(u2, u).sum() < 1e-6 + True + + as well as for left singular vectors. + + >>> linalg.subspace_angles(vT2.T, vT.T).sum() < 1e-6 + True + + The next example follows that of 'sklearn.decomposition.TruncatedSVD'. + + >>> rng = np.random.RandomState(0) + >>> X_dense = rng.random(size=(100, 100)) + >>> X_dense[:, 2 * np.arange(50)] = 0 + >>> X = sparse.csr_matrix(X_dense) + >>> _, singular_values, _ = svds(X, k=5, random_state=rng) + >>> print(singular_values) + [ 4.3293... 4.4491... 4.5420... 4.5987... 35.2410...] + + The function can be called without the transpose of the input matrix + ever explicitly constructed. + + >>> rng = np.random.default_rng(102524723947864966825913730119128190974) + >>> G = sparse.rand(8, 9, density=0.5, random_state=rng) + >>> Glo = aslinearoperator(G) + >>> _, singular_values_svds, _ = svds(Glo, k=5, random_state=rng) + >>> _, singular_values_svd, _ = linalg.svd(G.toarray()) + >>> np.allclose(singular_values_svds, singular_values_svd[-4::-1]) + True + + The most memory efficient scenario is where neither + the original matrix, nor its transpose, is explicitly constructed. + Our example computes the smallest singular values and vectors + of 'LinearOperator' constructed from the numpy function 'np.diff' used + column-wise to be consistent with 'LinearOperator' operating on columns. + + >>> diff0 = lambda a: np.diff(a, axis=0) + + Let us create the matrix from 'diff0' to be used for validation only. + + >>> n = 5 # The dimension of the space. + >>> M_from_diff0 = diff0(np.eye(n)) + >>> print(M_from_diff0.astype(int)) + [[-1 1 0 0 0] + [ 0 -1 1 0 0] + [ 0 0 -1 1 0] + [ 0 0 0 -1 1]] + + The matrix 'M_from_diff0' is bi-diagonal and could be alternatively + created directly by + + >>> M = - np.eye(n - 1, n, dtype=int) + >>> np.fill_diagonal(M[:,1:], 1) + >>> np.allclose(M, M_from_diff0) + True + + Its transpose + + >>> print(M.T) + [[-1 0 0 0] + [ 1 -1 0 0] + [ 0 1 -1 0] + [ 0 0 1 -1] + [ 0 0 0 1]] + + can be viewed as the incidence matrix; see + Incidence matrix, (2022, Nov. 19), Wikipedia, https://w.wiki/5YXU, + of a linear graph with 5 vertices and 4 edges. The 5x5 normal matrix + ``M.T @ M`` thus is + + >>> print(M.T @ M) + [[ 1 -1 0 0 0] + [-1 2 -1 0 0] + [ 0 -1 2 -1 0] + [ 0 0 -1 2 -1] + [ 0 0 0 -1 1]] + + the graph Laplacian, while the actually used in 'svds' smaller size + 4x4 normal matrix ``M @ M.T`` + + >>> print(M @ M.T) + [[ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -1 2]] + + is the so-called edge-based Laplacian; see + Symmetric Laplacian via the incidence matrix, in Laplacian matrix, + (2022, Nov. 19), Wikipedia, https://w.wiki/5YXW. + + The 'LinearOperator' setup needs the options 'rmatvec' and 'rmatmat' + of multiplication by the matrix transpose ``M.T``, but we want to be + matrix-free to save memory, so knowing how ``M.T`` looks like, we + manually construct the following function to be + used in ``rmatmat=diff0t``. + + >>> def diff0t(a): + ... if a.ndim == 1: + ... a = a[:,np.newaxis] # Turn 1D into 2D array + ... d = np.zeros((a.shape[0] + 1, a.shape[1]), dtype=a.dtype) + ... d[0, :] = - a[0, :] + ... d[1:-1, :] = a[0:-1, :] - a[1:, :] + ... d[-1, :] = a[-1, :] + ... return d + + We check that our function 'diff0t' for the matrix transpose is valid. + + >>> np.allclose(M.T, diff0t(np.eye(n-1))) + True + + Now we setup our matrix-free 'LinearOperator' called 'diff0_func_aslo' + and for validation the matrix-based 'diff0_matrix_aslo'. + + >>> def diff0_func_aslo_def(n): + ... return LinearOperator(matvec=diff0, + ... matmat=diff0, + ... rmatvec=diff0t, + ... rmatmat=diff0t, + ... shape=(n - 1, n)) + >>> diff0_func_aslo = diff0_func_aslo_def(n) + >>> diff0_matrix_aslo = aslinearoperator(M_from_diff0) + + And validate both the matrix and its transpose in 'LinearOperator'. + + >>> np.allclose(diff0_func_aslo(np.eye(n)), + ... diff0_matrix_aslo(np.eye(n))) + True + >>> np.allclose(diff0_func_aslo.T(np.eye(n-1)), + ... diff0_matrix_aslo.T(np.eye(n-1))) + True + + Having the 'LinearOperator' setup validated, we run the solver. + + >>> n = 100 + >>> diff0_func_aslo = diff0_func_aslo_def(n) + >>> u, s, vT = svds(diff0_func_aslo, k=3, which='SM') + + The singular values squared and the singular vectors are known + explicitly; see + Pure Dirichlet boundary conditions, in + Eigenvalues and eigenvectors of the second derivative, + (2022, Nov. 19), Wikipedia, https://w.wiki/5YX6, + since 'diff' corresponds to first + derivative, and its smaller size n-1 x n-1 normal matrix + ``M @ M.T`` represent the discrete second derivative with the Dirichlet + boundary conditions. We use these analytic expressions for validation. + + >>> se = 2. * np.sin(np.pi * np.arange(1, 4) / (2. * n)) + >>> ue = np.sqrt(2 / n) * np.sin(np.pi * np.outer(np.arange(1, n), + ... np.arange(1, 4)) / n) + >>> np.allclose(s, se, atol=1e-3) + True + >>> print(np.allclose(np.abs(u), np.abs(ue), atol=1e-6)) + True + + """ + args = _iv(A, k, ncv, tol, which, v0, maxiter, return_singular_vectors, + solver, random_state) + (A, k, ncv, tol, which, v0, maxiter, + return_singular_vectors, solver, random_state) = args + + largest = (which == 'LM') + n, m = A.shape + + if n >= m: + X_dot = A.matvec + X_matmat = A.matmat + XH_dot = A.rmatvec + XH_mat = A.rmatmat + transpose = False + else: + X_dot = A.rmatvec + X_matmat = A.rmatmat + XH_dot = A.matvec + XH_mat = A.matmat + transpose = True + + dtype = getattr(A, 'dtype', None) + if dtype is None: + dtype = A.dot(np.zeros([m, 1])).dtype + + def matvec_XH_X(x): + return XH_dot(X_dot(x)) + + def matmat_XH_X(x): + return XH_mat(X_matmat(x)) + + XH_X = LinearOperator(matvec=matvec_XH_X, dtype=A.dtype, + matmat=matmat_XH_X, + shape=(min(A.shape), min(A.shape))) + + # Get a low rank approximation of the implicitly defined gramian matrix. + # This is not a stable way to approach the problem. + if solver == 'lobpcg': + + if k == 1 and v0 is not None: + X = np.reshape(v0, (-1, 1)) + else: + X = random_state.standard_normal(size=(min(A.shape), k)) + + _, eigvec = lobpcg(XH_X, X, tol=tol ** 2, maxiter=maxiter, + largest=largest) + + elif solver == 'propack': + jobu = return_singular_vectors in {True, 'u'} + jobv = return_singular_vectors in {True, 'vh'} + irl_mode = (which == 'SM') + res = _svdp(A, k=k, tol=tol**2, which=which, maxiter=None, + compute_u=jobu, compute_v=jobv, irl_mode=irl_mode, + kmax=maxiter, v0=v0, random_state=random_state) + + u, s, vh, _ = res # but we'll ignore bnd, the last output + + # PROPACK order appears to be largest first. `svds` output order is not + # guaranteed, according to documentation, but for ARPACK and LOBPCG + # they actually are ordered smallest to largest, so reverse for + # consistency. + s = s[::-1] + u = u[:, ::-1] + vh = vh[::-1] + + u = u if jobu else None + vh = vh if jobv else None + + if return_singular_vectors: + return u, s, vh + else: + return s + + elif solver == 'arpack' or solver is None: + if v0 is None: + v0 = random_state.standard_normal(size=(min(A.shape),)) + _, eigvec = eigsh(XH_X, k=k, tol=tol ** 2, maxiter=maxiter, + ncv=ncv, which=which, v0=v0) + # arpack do not guarantee exactly orthonormal eigenvectors + # for clustered eigenvalues, especially in complex arithmetic + eigvec, _ = np.linalg.qr(eigvec) + + # the eigenvectors eigvec must be orthonomal here; see gh-16712 + Av = X_matmat(eigvec) + if not return_singular_vectors: + s = svd(Av, compute_uv=False, overwrite_a=True) + return s[::-1] + + # compute the left singular vectors of X and update the right ones + # accordingly + u, s, vh = svd(Av, full_matrices=False, overwrite_a=True) + u = u[:, ::-1] + s = s[::-1] + vh = vh[::-1] + + jobu = return_singular_vectors in {True, 'u'} + jobv = return_singular_vectors in {True, 'vh'} + + if transpose: + u_tmp = eigvec @ _herm(vh) if jobu else None + vh = _herm(u) if jobv else None + u = u_tmp + else: + if not jobu: + u = None + vh = vh @ _herm(eigvec) if jobv else None + + return u, s, vh diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py new file mode 100644 index 0000000000000000000000000000000000000000..3de1b76d6d4dc437bfbee8faf8171f2dfa2377fa --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py @@ -0,0 +1,400 @@ +def _svds_arpack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='arpack', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using ARPACK. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. + k : int, optional + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N) - 1``. + Default is 6. + ncv : int, optional + The number of Lanczos vectors generated. + The default is ``min(n, max(2*k + 1, 20))``. + If specified, must satistify ``k + 1 < ncv < min(M, N)``; ``ncv > 2*k`` + is recommended. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + The starting vector for iteration: + an (approximate) left singular vector if ``N > M`` and a right singular + vector otherwise. Must be of length ``min(M, N)``. + Default: random + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed; + default is ``min(M, N) * 10``. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='arpack'``. + :ref:`'lobpcg' ` and + :ref:`'propack' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using ARPACK as an eigensolver + on ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on which one is more + efficient. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='arpack') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.toarray(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='arpack') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.toarray()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.toarray())) and + ... np.allclose(np.abs(vT3), np.abs(vT.toarray()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + """ + pass + + +def _svds_lobpcg_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='lobpcg', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using LOBPCG. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N) - 1``. + ncv : int, optional + Ignored. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + If `k` is 1, the starting vector for iteration: + an (approximate) left singular vector if ``N > M`` and a right singular + vector otherwise. Must be of length ``min(M, N)``. + Ignored otherwise. + Default: random + maxiter : int, default: 20 + Maximum number of iterations. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='lobpcg'``. + :ref:`'arpack' ` and + :ref:`'propack' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using LOBPCG as an eigensolver + on ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on which one is more + efficient. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='lobpcg') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.toarray(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='lobpcg') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.toarray()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.todense())) and + ... np.allclose(np.abs(vT3), np.abs(vT.todense()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + + """ + pass + + +def _svds_propack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='propack', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using PROPACK. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. If `A` is a ``LinearOperator`` + object, it must define both ``matvec`` and ``rmatvec`` methods. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N)``. + ncv : int, optional + Ignored. + tol : float, optional + The desired relative accuracy for computed singular values. + Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. Note that choosing + ``which='SM'`` will force the ``irl`` option to be set ``True``. + v0 : ndarray, optional + Starting vector for iterations: must be of length ``A.shape[0]``. + If not specified, PROPACK will generate a starting vector. + maxiter : int, optional + Maximum number of iterations / maximal dimension of the Krylov + subspace. Default is ``10 * k``. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: compute only the left singular vectors; return ``None`` for + the right singular vectors. + - ``"vh"``: compute only the right singular vectors; return ``None`` + for the left singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='propack'``. + :ref:`'arpack' ` and + :ref:`'lobpcg' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is an interface to the Fortran library PROPACK [1]_. + The current default is to run with IRL mode disabled unless seeking the + smallest singular values/vectors (``which='SM'``). + + References + ---------- + + .. [1] Larsen, Rasmus Munk. "PROPACK-Software for large and sparse SVD + calculations." Available online. URL + http://sun.stanford.edu/~rmunk/PROPACK (2004): 2008-2009. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='propack') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.todense(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='propack') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.todense()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.toarray())) and + ... np.allclose(np.abs(vT3), np.abs(vT.toarray()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + + """ + pass diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..e87667e1b8c178e53c6a7c6268ebc09ab4b0476c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING @@ -0,0 +1,45 @@ + +BSD Software License + +Pertains to ARPACK and P_ARPACK + +Copyright (c) 1996-2008 Rice University. +Developed by D.C. Sorensen, R.B. Lehoucq, C. Yang, and K. Maschhoff. +All rights reserved. + +Arpack has been renamed to arpack-ng. + +Copyright (c) 2001-2011 - Scilab Enterprises +Updated by Allan Cornet, Sylvestre Ledru. + +Copyright (c) 2010 - Jordi Gutiérrez Hermoso (Octave patch) + +Copyright (c) 2007 - Sébastien Fabbro (gentoo patch) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + +- Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..679b94480d7ff5a11e037ffb758f2214c6e5097f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py @@ -0,0 +1,20 @@ +""" +Eigenvalue solver using iterative methods. + +Find k eigenvectors and eigenvalues of a matrix A using the +Arnoldi/Lanczos iterative methods from ARPACK [1]_,[2]_. + +These methods are most useful for large sparse matrices. + + - eigs(A,k) + - eigsh(A,k) + +References +---------- +.. [1] ARPACK Software, http://www.caam.rice.edu/software/ARPACK/ +.. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + +""" +from .arpack import * diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py new file mode 100644 index 0000000000000000000000000000000000000000..f7a6fa218ca462212f1ed8a2fc0bd0037cf9d44b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py @@ -0,0 +1,1702 @@ +""" +Find a few eigenvectors and eigenvalues of a matrix. + + +Uses ARPACK: https://github.com/opencollab/arpack-ng + +""" +# Wrapper implementation notes +# +# ARPACK Entry Points +# ------------------- +# The entry points to ARPACK are +# - (s,d)seupd : single and double precision symmetric matrix +# - (s,d,c,z)neupd: single,double,complex,double complex general matrix +# This wrapper puts the *neupd (general matrix) interfaces in eigs() +# and the *seupd (symmetric matrix) in eigsh(). +# There is no specialized interface for complex Hermitian matrices. +# To find eigenvalues of a complex Hermitian matrix you +# may use eigsh(), but eigsh() will simply call eigs() +# and return the real part of the eigenvalues thus obtained. + +# Number of eigenvalues returned and complex eigenvalues +# ------------------------------------------------------ +# The ARPACK nonsymmetric real and double interface (s,d)naupd return +# eigenvalues and eigenvectors in real (float,double) arrays. +# Since the eigenvalues and eigenvectors are, in general, complex +# ARPACK puts the real and imaginary parts in consecutive entries +# in real-valued arrays. This wrapper puts the real entries +# into complex data types and attempts to return the requested eigenvalues +# and eigenvectors. + + +# Solver modes +# ------------ +# ARPACK and handle shifted and shift-inverse computations +# for eigenvalues by providing a shift (sigma) and a solver. + +import numpy as np +import warnings +from scipy.sparse.linalg._interface import aslinearoperator, LinearOperator +from scipy.sparse import eye, issparse +from scipy.linalg import eig, eigh, lu_factor, lu_solve +from scipy.sparse._sputils import isdense, is_pydata_spmatrix +from scipy.sparse.linalg import gmres, splu +from scipy._lib._util import _aligned_zeros +from scipy._lib._threadsafety import ReentrancyLock + +from . import _arpack +arpack_int = _arpack.timing.nbx.dtype + +__docformat__ = "restructuredtext en" + +__all__ = ['eigs', 'eigsh', 'ArpackError', 'ArpackNoConvergence'] + + +_type_conv = {'f': 's', 'd': 'd', 'F': 'c', 'D': 'z'} +_ndigits = {'f': 5, 'd': 12, 'F': 5, 'D': 12} + +DNAUPD_ERRORS = { + 0: "Normal exit.", + 1: "Maximum number of iterations taken. " + "All possible eigenvalues of OP has been found. IPARAM(5) " + "returns the number of wanted converged Ritz values.", + 2: "No longer an informational error. Deprecated starting " + "with release 2 of ARPACK.", + 3: "No shifts could be applied during a cycle of the " + "Implicitly restarted Arnoldi iteration. One possibility " + "is to increase the size of NCV relative to NEV. ", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 2 and less than or equal to N.", + -4: "The maximum number of Arnoldi update iterations allowed " + "must be greater than zero.", + -5: " WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work array WORKL is not sufficient.", + -8: "Error return from LAPACK eigenvalue calculation;", + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "IPARAM(1) must be equal to 0 or 1.", + -13: "NEV and WHICH = 'BE' are incompatible.", + -9999: "Could not build an Arnoldi factorization. " + "IPARAM(5) returns the size of the current Arnoldi " + "factorization. The user is advised to check that " + "enough workspace and array storage has been allocated." +} + +SNAUPD_ERRORS = DNAUPD_ERRORS + +ZNAUPD_ERRORS = DNAUPD_ERRORS.copy() +ZNAUPD_ERRORS[-10] = "IPARAM(7) must be 1,2,3." + +CNAUPD_ERRORS = ZNAUPD_ERRORS + +DSAUPD_ERRORS = { + 0: "Normal exit.", + 1: "Maximum number of iterations taken. " + "All possible eigenvalues of OP has been found.", + 2: "No longer an informational error. Deprecated starting with " + "release 2 of ARPACK.", + 3: "No shifts could be applied during a cycle of the Implicitly " + "restarted Arnoldi iteration. One possibility is to increase " + "the size of NCV relative to NEV. ", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV must be greater than NEV and less than or equal to N.", + -4: "The maximum number of Arnoldi update iterations allowed " + "must be greater than zero.", + -5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work array WORKL is not sufficient.", + -8: "Error return from trid. eigenvalue calculation; " + "Informational error from LAPACK routine dsteqr .", + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4,5.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "IPARAM(1) must be equal to 0 or 1.", + -13: "NEV and WHICH = 'BE' are incompatible. ", + -9999: "Could not build an Arnoldi factorization. " + "IPARAM(5) returns the size of the current Arnoldi " + "factorization. The user is advised to check that " + "enough workspace and array storage has been allocated.", +} + +SSAUPD_ERRORS = DSAUPD_ERRORS + +DNEUPD_ERRORS = { + 0: "Normal exit.", + 1: "The Schur form computed by LAPACK routine dlahqr " + "could not be reordered by LAPACK routine dtrsen. " + "Re-enter subroutine dneupd with IPARAM(5)NCV and " + "increase the size of the arrays DR and DI to have " + "dimension at least dimension NCV and allocate at least NCV " + "columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error" + "occurs.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 2 and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: "Error return from calculation of a real Schur form. " + "Informational error from LAPACK routine dlahqr .", + -9: "Error return from calculation of eigenvectors. " + "Informational error from LAPACK routine dtrevc.", + -10: "IPARAM(7) must be 1,2,3,4.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "HOWMNY = 'S' not yet implemented", + -13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", + -14: "DNAUPD did not find any eigenvalues to sufficient " + "accuracy.", + -15: "DNEUPD got a different count of the number of converged " + "Ritz values than DNAUPD got. This indicates the user " + "probably made an error in passing data from DNAUPD to " + "DNEUPD or that the data was modified before entering " + "DNEUPD", +} + +SNEUPD_ERRORS = DNEUPD_ERRORS.copy() +SNEUPD_ERRORS[1] = ("The Schur form computed by LAPACK routine slahqr " + "could not be reordered by LAPACK routine strsen . " + "Re-enter subroutine dneupd with IPARAM(5)=NCV and " + "increase the size of the arrays DR and DI to have " + "dimension at least dimension NCV and allocate at least " + "NCV columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error " + "occurs.") +SNEUPD_ERRORS[-14] = ("SNAUPD did not find any eigenvalues to sufficient " + "accuracy.") +SNEUPD_ERRORS[-15] = ("SNEUPD got a different count of the number of " + "converged Ritz values than SNAUPD got. This indicates " + "the user probably made an error in passing data from " + "SNAUPD to SNEUPD or that the data was modified before " + "entering SNEUPD") + +ZNEUPD_ERRORS = {0: "Normal exit.", + 1: "The Schur form computed by LAPACK routine csheqr " + "could not be reordered by LAPACK routine ztrsen. " + "Re-enter subroutine zneupd with IPARAM(5)=NCV and " + "increase the size of the array D to have " + "dimension at least dimension NCV and allocate at least " + "NCV columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error " + "occurs.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 1 and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: "Error return from LAPACK eigenvalue calculation. " + "This should never happened.", + -9: "Error return from calculation of eigenvectors. " + "Informational error from LAPACK routine ztrevc.", + -10: "IPARAM(7) must be 1,2,3", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "HOWMNY = 'S' not yet implemented", + -13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", + -14: "ZNAUPD did not find any eigenvalues to sufficient " + "accuracy.", + -15: "ZNEUPD got a different count of the number of " + "converged Ritz values than ZNAUPD got. This " + "indicates the user probably made an error in passing " + "data from ZNAUPD to ZNEUPD or that the data was " + "modified before entering ZNEUPD" + } + +CNEUPD_ERRORS = ZNEUPD_ERRORS.copy() +CNEUPD_ERRORS[-14] = ("CNAUPD did not find any eigenvalues to sufficient " + "accuracy.") +CNEUPD_ERRORS[-15] = ("CNEUPD got a different count of the number of " + "converged Ritz values than CNAUPD got. This indicates " + "the user probably made an error in passing data from " + "CNAUPD to CNEUPD or that the data was modified before " + "entering CNEUPD") + +DSEUPD_ERRORS = { + 0: "Normal exit.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV must be greater than NEV and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: ("Error return from trid. eigenvalue calculation; " + "Information error from LAPACK routine dsteqr."), + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4,5.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "NEV and WHICH = 'BE' are incompatible.", + -14: "DSAUPD did not find any eigenvalues to sufficient accuracy.", + -15: "HOWMNY must be one of 'A' or 'S' if RVEC = .true.", + -16: "HOWMNY = 'S' not yet implemented", + -17: ("DSEUPD got a different count of the number of converged " + "Ritz values than DSAUPD got. This indicates the user " + "probably made an error in passing data from DSAUPD to " + "DSEUPD or that the data was modified before entering " + "DSEUPD.") +} + +SSEUPD_ERRORS = DSEUPD_ERRORS.copy() +SSEUPD_ERRORS[-14] = ("SSAUPD did not find any eigenvalues " + "to sufficient accuracy.") +SSEUPD_ERRORS[-17] = ("SSEUPD got a different count of the number of " + "converged " + "Ritz values than SSAUPD got. This indicates the user " + "probably made an error in passing data from SSAUPD to " + "SSEUPD or that the data was modified before entering " + "SSEUPD.") + +_SAUPD_ERRORS = {'d': DSAUPD_ERRORS, + 's': SSAUPD_ERRORS} +_NAUPD_ERRORS = {'d': DNAUPD_ERRORS, + 's': SNAUPD_ERRORS, + 'z': ZNAUPD_ERRORS, + 'c': CNAUPD_ERRORS} +_SEUPD_ERRORS = {'d': DSEUPD_ERRORS, + 's': SSEUPD_ERRORS} +_NEUPD_ERRORS = {'d': DNEUPD_ERRORS, + 's': SNEUPD_ERRORS, + 'z': ZNEUPD_ERRORS, + 'c': CNEUPD_ERRORS} + +# accepted values of parameter WHICH in _SEUPD +_SEUPD_WHICH = ['LM', 'SM', 'LA', 'SA', 'BE'] + +# accepted values of parameter WHICH in _NAUPD +_NEUPD_WHICH = ['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] + + +class ArpackError(RuntimeError): + """ + ARPACK error + """ + + def __init__(self, info, infodict=_NAUPD_ERRORS): + msg = infodict.get(info, "Unknown error") + RuntimeError.__init__(self, "ARPACK error %d: %s" % (info, msg)) + + +class ArpackNoConvergence(ArpackError): + """ + ARPACK iteration did not converge + + Attributes + ---------- + eigenvalues : ndarray + Partial result. Converged eigenvalues. + eigenvectors : ndarray + Partial result. Converged eigenvectors. + + """ + + def __init__(self, msg, eigenvalues, eigenvectors): + ArpackError.__init__(self, -1, {-1: msg}) + self.eigenvalues = eigenvalues + self.eigenvectors = eigenvectors + + +def choose_ncv(k): + """ + Choose number of lanczos vectors based on target number + of singular/eigen values and vectors to compute, k. + """ + return max(2 * k + 1, 20) + + +class _ArpackParams: + def __init__(self, n, k, tp, mode=1, sigma=None, + ncv=None, v0=None, maxiter=None, which="LM", tol=0): + if k <= 0: + raise ValueError("k must be positive, k=%d" % k) + + if maxiter is None: + maxiter = n * 10 + if maxiter <= 0: + raise ValueError("maxiter must be positive, maxiter=%d" % maxiter) + + if tp not in 'fdFD': + raise ValueError("matrix type must be 'f', 'd', 'F', or 'D'") + + if v0 is not None: + # ARPACK overwrites its initial resid, make a copy + self.resid = np.array(v0, copy=True) + info = 1 + else: + # ARPACK will use a random initial vector. + self.resid = np.zeros(n, tp) + info = 0 + + if sigma is None: + #sigma not used + self.sigma = 0 + else: + self.sigma = sigma + + if ncv is None: + ncv = choose_ncv(k) + ncv = min(ncv, n) + + self.v = np.zeros((n, ncv), tp) # holds Ritz vectors + self.iparam = np.zeros(11, arpack_int) + + # set solver mode and parameters + ishfts = 1 + self.mode = mode + self.iparam[0] = ishfts + self.iparam[2] = maxiter + self.iparam[3] = 1 + self.iparam[6] = mode + + self.n = n + self.tol = tol + self.k = k + self.maxiter = maxiter + self.ncv = ncv + self.which = which + self.tp = tp + self.info = info + + self.converged = False + self.ido = 0 + + def _raise_no_convergence(self): + msg = "No convergence (%d iterations, %d/%d eigenvectors converged)" + k_ok = self.iparam[4] + num_iter = self.iparam[2] + try: + ev, vec = self.extract(True) + except ArpackError as err: + msg = f"{msg} [{err}]" + ev = np.zeros((0,)) + vec = np.zeros((self.n, 0)) + k_ok = 0 + raise ArpackNoConvergence(msg % (num_iter, k_ok, self.k), ev, vec) + + +class _SymmetricArpackParams(_ArpackParams): + def __init__(self, n, k, tp, matvec, mode=1, M_matvec=None, + Minv_matvec=None, sigma=None, + ncv=None, v0=None, maxiter=None, which="LM", tol=0): + # The following modes are supported: + # mode = 1: + # Solve the standard eigenvalue problem: + # A*x = lambda*x : + # A - symmetric + # Arguments should be + # matvec = left multiplication by A + # M_matvec = None [not used] + # Minv_matvec = None [not used] + # + # mode = 2: + # Solve the general eigenvalue problem: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive definite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = left multiplication by M + # Minv_matvec = left multiplication by M^-1 + # + # mode = 3: + # Solve the general eigenvalue problem in shift-invert mode: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive semi-definite + # Arguments should be + # matvec = None [not used] + # M_matvec = left multiplication by M + # or None, if M is the identity + # Minv_matvec = left multiplication by [A-sigma*M]^-1 + # + # mode = 4: + # Solve the general eigenvalue problem in Buckling mode: + # A*x = lambda*AG*x + # A - symmetric positive semi-definite + # AG - symmetric indefinite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = None [not used] + # Minv_matvec = left multiplication by [A-sigma*AG]^-1 + # + # mode = 5: + # Solve the general eigenvalue problem in Cayley-transformed mode: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive semi-definite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = left multiplication by M + # or None, if M is the identity + # Minv_matvec = left multiplication by [A-sigma*M]^-1 + if mode == 1: + if matvec is None: + raise ValueError("matvec must be specified for mode=1") + if M_matvec is not None: + raise ValueError("M_matvec cannot be specified for mode=1") + if Minv_matvec is not None: + raise ValueError("Minv_matvec cannot be specified for mode=1") + + self.OP = matvec + self.B = lambda x: x + self.bmat = 'I' + elif mode == 2: + if matvec is None: + raise ValueError("matvec must be specified for mode=2") + if M_matvec is None: + raise ValueError("M_matvec must be specified for mode=2") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=2") + + self.OP = lambda x: Minv_matvec(matvec(x)) + self.OPa = Minv_matvec + self.OPb = matvec + self.B = M_matvec + self.bmat = 'G' + elif mode == 3: + if matvec is not None: + raise ValueError("matvec must not be specified for mode=3") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=3") + + if M_matvec is None: + self.OP = Minv_matvec + self.OPa = Minv_matvec + self.B = lambda x: x + self.bmat = 'I' + else: + self.OP = lambda x: Minv_matvec(M_matvec(x)) + self.OPa = Minv_matvec + self.B = M_matvec + self.bmat = 'G' + elif mode == 4: + if matvec is None: + raise ValueError("matvec must be specified for mode=4") + if M_matvec is not None: + raise ValueError("M_matvec must not be specified for mode=4") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=4") + self.OPa = Minv_matvec + self.OP = lambda x: self.OPa(matvec(x)) + self.B = matvec + self.bmat = 'G' + elif mode == 5: + if matvec is None: + raise ValueError("matvec must be specified for mode=5") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=5") + + self.OPa = Minv_matvec + self.A_matvec = matvec + + if M_matvec is None: + self.OP = lambda x: Minv_matvec(matvec(x) + sigma * x) + self.B = lambda x: x + self.bmat = 'I' + else: + self.OP = lambda x: Minv_matvec(matvec(x) + + sigma * M_matvec(x)) + self.B = M_matvec + self.bmat = 'G' + else: + raise ValueError("mode=%i not implemented" % mode) + + if which not in _SEUPD_WHICH: + raise ValueError("which must be one of %s" + % ' '.join(_SEUPD_WHICH)) + if k >= n: + raise ValueError("k must be less than ndim(A), k=%d" % k) + + _ArpackParams.__init__(self, n, k, tp, mode, sigma, + ncv, v0, maxiter, which, tol) + + if self.ncv > n or self.ncv <= k: + raise ValueError("ncv must be k= n - 1: + raise ValueError("k must be less than ndim(A)-1, k=%d" % k) + + _ArpackParams.__init__(self, n, k, tp, mode, sigma, + ncv, v0, maxiter, which, tol) + + if self.ncv > n or self.ncv <= k + 1: + raise ValueError("ncv must be k+1 k, so we'll + # throw out this case. + nreturned -= 1 + i += 1 + + else: + # real matrix, mode 3 or 4, imag(sigma) is nonzero: + # see remark 3 in neupd.f + # Build complex eigenvalues from real and imaginary parts + i = 0 + while i <= k: + if abs(d[i].imag) == 0: + d[i] = np.dot(zr[:, i], self.matvec(zr[:, i])) + else: + if i < k: + z[:, i] = zr[:, i] + 1.0j * zr[:, i + 1] + z[:, i + 1] = z[:, i].conjugate() + d[i] = ((np.dot(zr[:, i], + self.matvec(zr[:, i])) + + np.dot(zr[:, i + 1], + self.matvec(zr[:, i + 1]))) + + 1j * (np.dot(zr[:, i], + self.matvec(zr[:, i + 1])) + - np.dot(zr[:, i + 1], + self.matvec(zr[:, i])))) + d[i + 1] = d[i].conj() + i += 1 + else: + #last eigenvalue is complex: the imaginary part of + # the eigenvector has not been returned + #this can only happen if nreturned > k, so we'll + # throw out this case. + nreturned -= 1 + i += 1 + + # Now we have k+1 possible eigenvalues and eigenvectors + # Return the ones specified by the keyword "which" + + if nreturned <= k: + # we got less or equal as many eigenvalues we wanted + d = d[:nreturned] + z = z[:, :nreturned] + else: + # we got one extra eigenvalue (likely a cc pair, but which?) + if self.mode in (1, 2): + rd = d + elif self.mode in (3, 4): + rd = 1 / (d - self.sigma) + + if self.which in ['LR', 'SR']: + ind = np.argsort(rd.real) + elif self.which in ['LI', 'SI']: + # for LI,SI ARPACK returns largest,smallest + # abs(imaginary) (complex pairs come together) + ind = np.argsort(abs(rd.imag)) + else: + ind = np.argsort(abs(rd)) + + if self.which in ['LR', 'LM', 'LI']: + ind = ind[-k:][::-1] + elif self.which in ['SR', 'SM', 'SI']: + ind = ind[:k] + + d = d[ind] + z = z[:, ind] + else: + # complex is so much simpler... + d, z, ierr =\ + self._arpack_extract(return_eigenvectors, + howmny, sselect, self.sigma, workev, + self.bmat, self.which, k, self.tol, self.resid, + self.v, self.iparam, self.ipntr, + self.workd, self.workl, self.rwork, ierr) + + if ierr != 0: + raise ArpackError(ierr, infodict=self.extract_infodict) + + k_ok = self.iparam[4] + d = d[:k_ok] + z = z[:, :k_ok] + + if return_eigenvectors: + return d, z + else: + return d + + +def _aslinearoperator_with_dtype(m): + m = aslinearoperator(m) + if not hasattr(m, 'dtype'): + x = np.zeros(m.shape[1]) + m.dtype = (m * x).dtype + return m + + +class SpLuInv(LinearOperator): + """ + SpLuInv: + helper class to repeatedly solve M*x=b + using a sparse LU-decomposition of M + """ + + def __init__(self, M): + self.M_lu = splu(M) + self.shape = M.shape + self.dtype = M.dtype + self.isreal = not np.issubdtype(self.dtype, np.complexfloating) + + def _matvec(self, x): + # careful here: splu.solve will throw away imaginary + # part of x if M is real + x = np.asarray(x) + if self.isreal and np.issubdtype(x.dtype, np.complexfloating): + return (self.M_lu.solve(np.real(x).astype(self.dtype)) + + 1j * self.M_lu.solve(np.imag(x).astype(self.dtype))) + else: + return self.M_lu.solve(x.astype(self.dtype)) + + +class LuInv(LinearOperator): + """ + LuInv: + helper class to repeatedly solve M*x=b + using an LU-decomposition of M + """ + + def __init__(self, M): + self.M_lu = lu_factor(M) + self.shape = M.shape + self.dtype = M.dtype + + def _matvec(self, x): + return lu_solve(self.M_lu, x) + + +def gmres_loose(A, b, tol): + """ + gmres with looser termination condition. + """ + b = np.asarray(b) + min_tol = 1000 * np.sqrt(b.size) * np.finfo(b.dtype).eps + return gmres(A, b, rtol=max(tol, min_tol), atol=0) + + +class IterInv(LinearOperator): + """ + IterInv: + helper class to repeatedly solve M*x=b + using an iterative method. + """ + + def __init__(self, M, ifunc=gmres_loose, tol=0): + self.M = M + if hasattr(M, 'dtype'): + self.dtype = M.dtype + else: + x = np.zeros(M.shape[1]) + self.dtype = (M * x).dtype + self.shape = M.shape + + if tol <= 0: + # when tol=0, ARPACK uses machine tolerance as calculated + # by LAPACK's _LAMCH function. We should match this + tol = 2 * np.finfo(self.dtype).eps + self.ifunc = ifunc + self.tol = tol + + def _matvec(self, x): + b, info = self.ifunc(self.M, x, tol=self.tol) + if info != 0: + raise ValueError("Error in inverting M: function " + "%s did not converge (info = %i)." + % (self.ifunc.__name__, info)) + return b + + +class IterOpInv(LinearOperator): + """ + IterOpInv: + helper class to repeatedly solve [A-sigma*M]*x = b + using an iterative method + """ + + def __init__(self, A, M, sigma, ifunc=gmres_loose, tol=0): + self.A = A + self.M = M + self.sigma = sigma + + def mult_func(x): + return A.matvec(x) - sigma * M.matvec(x) + + def mult_func_M_None(x): + return A.matvec(x) - sigma * x + + x = np.zeros(A.shape[1]) + if M is None: + dtype = mult_func_M_None(x).dtype + self.OP = LinearOperator(self.A.shape, + mult_func_M_None, + dtype=dtype) + else: + dtype = mult_func(x).dtype + self.OP = LinearOperator(self.A.shape, + mult_func, + dtype=dtype) + self.shape = A.shape + + if tol <= 0: + # when tol=0, ARPACK uses machine tolerance as calculated + # by LAPACK's _LAMCH function. We should match this + tol = 2 * np.finfo(self.OP.dtype).eps + self.ifunc = ifunc + self.tol = tol + + def _matvec(self, x): + b, info = self.ifunc(self.OP, x, tol=self.tol) + if info != 0: + raise ValueError("Error in inverting [A-sigma*M]: function " + "%s did not converge (info = %i)." + % (self.ifunc.__name__, info)) + return b + + @property + def dtype(self): + return self.OP.dtype + + +def _fast_spmatrix_to_csc(A, hermitian=False): + """Convert sparse matrix to CSC (by transposing, if possible)""" + if (A.format == "csr" and hermitian + and not np.issubdtype(A.dtype, np.complexfloating)): + return A.T + elif is_pydata_spmatrix(A): + # No need to convert + return A + else: + return A.tocsc() + + +def get_inv_matvec(M, hermitian=False, tol=0): + if isdense(M): + return LuInv(M).matvec + elif issparse(M) or is_pydata_spmatrix(M): + M = _fast_spmatrix_to_csc(M, hermitian=hermitian) + return SpLuInv(M).matvec + else: + return IterInv(M, tol=tol).matvec + + +def get_OPinv_matvec(A, M, sigma, hermitian=False, tol=0): + if sigma == 0: + return get_inv_matvec(A, hermitian=hermitian, tol=tol) + + if M is None: + #M is the identity matrix + if isdense(A): + if (np.issubdtype(A.dtype, np.complexfloating) + or np.imag(sigma) == 0): + A = np.copy(A) + else: + A = A + 0j + A.flat[::A.shape[1] + 1] -= sigma + return LuInv(A).matvec + elif issparse(A) or is_pydata_spmatrix(A): + A = A - sigma * eye(A.shape[0]) + A = _fast_spmatrix_to_csc(A, hermitian=hermitian) + return SpLuInv(A).matvec + else: + return IterOpInv(_aslinearoperator_with_dtype(A), + M, sigma, tol=tol).matvec + else: + if ((not isdense(A) and not issparse(A) and not is_pydata_spmatrix(A)) or + (not isdense(M) and not issparse(M) and not is_pydata_spmatrix(A))): + return IterOpInv(_aslinearoperator_with_dtype(A), + _aslinearoperator_with_dtype(M), + sigma, tol=tol).matvec + elif isdense(A) or isdense(M): + return LuInv(A - sigma * M).matvec + else: + OP = A - sigma * M + OP = _fast_spmatrix_to_csc(OP, hermitian=hermitian) + return SpLuInv(OP).matvec + + +# ARPACK is not threadsafe or reentrant (SAVE variables), so we need a +# lock and a re-entering check. +_ARPACK_LOCK = ReentrancyLock("Nested calls to eigs/eighs not allowed: " + "ARPACK is not re-entrant") + + +def eigs(A, k=6, M=None, sigma=None, which='LM', v0=None, + ncv=None, maxiter=None, tol=0, return_eigenvectors=True, + Minv=None, OPinv=None, OPpart=None): + """ + Find k eigenvalues and eigenvectors of the square matrix A. + + Solves ``A @ x[i] = w[i] * x[i]``, the standard eigenvalue problem + for w[i] eigenvalues with corresponding eigenvectors x[i]. + + If M is specified, solves ``A @ x[i] = w[i] * M @ x[i]``, the + generalized eigenvalue problem for w[i] eigenvalues + with corresponding eigenvectors x[i] + + Parameters + ---------- + A : ndarray, sparse matrix or LinearOperator + An array, sparse matrix, or LinearOperator representing + the operation ``A @ x``, where A is a real or complex square matrix. + k : int, optional + The number of eigenvalues and eigenvectors desired. + `k` must be smaller than N-1. It is not possible to compute all + eigenvectors of a matrix. + M : ndarray, sparse matrix or LinearOperator, optional + An array, sparse matrix, or LinearOperator representing + the operation M@x for the generalized eigenvalue problem + + A @ x = w * M @ x. + + M must represent a real symmetric matrix if A is real, and must + represent a complex Hermitian matrix if A is complex. For best + results, the data type of M should be the same as that of A. + Additionally: + + If `sigma` is None, M is positive definite + + If sigma is specified, M is positive semi-definite + + If sigma is None, eigs requires an operator to compute the solution + of the linear equation ``M @ x = b``. This is done internally via a + (sparse) LU decomposition for an explicit matrix M, or via an + iterative solver for a general linear operator. Alternatively, + the user can supply the matrix or operator Minv, which gives + ``x = Minv @ b = M^-1 @ b``. + sigma : real or complex, optional + Find eigenvalues near sigma using shift-invert mode. This requires + an operator to compute the solution of the linear system + ``[A - sigma * M] @ x = b``, where M is the identity matrix if + unspecified. This is computed internally via a (sparse) LU + decomposition for explicit matrices A & M, or via an iterative + solver if either A or M is a general linear operator. + Alternatively, the user can supply the matrix or operator OPinv, + which gives ``x = OPinv @ b = [A - sigma * M]^-1 @ b``. + For a real matrix A, shift-invert can either be done in imaginary + mode or real mode, specified by the parameter OPpart ('r' or 'i'). + Note that when sigma is specified, the keyword 'which' (below) + refers to the shifted eigenvalues ``w'[i]`` where: + + If A is real and OPpart == 'r' (default), + ``w'[i] = 1/2 * [1/(w[i]-sigma) + 1/(w[i]-conj(sigma))]``. + + If A is real and OPpart == 'i', + ``w'[i] = 1/2i * [1/(w[i]-sigma) - 1/(w[i]-conj(sigma))]``. + + If A is complex, ``w'[i] = 1/(w[i]-sigma)``. + + v0 : ndarray, optional + Starting vector for iteration. + Default: random + ncv : int, optional + The number of Lanczos vectors generated + `ncv` must be greater than `k`; it is recommended that ``ncv > 2*k``. + Default: ``min(n, max(2*k + 1, 20))`` + which : str, ['LM' | 'SM' | 'LR' | 'SR' | 'LI' | 'SI'], optional + Which `k` eigenvectors and eigenvalues to find: + + 'LM' : largest magnitude + + 'SM' : smallest magnitude + + 'LR' : largest real part + + 'SR' : smallest real part + + 'LI' : largest imaginary part + + 'SI' : smallest imaginary part + + When sigma != None, 'which' refers to the shifted eigenvalues w'[i] + (see discussion in 'sigma', above). ARPACK is generally better + at finding large values than small values. If small eigenvalues are + desired, consider using shift-invert mode for better performance. + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed + Default: ``n*10`` + tol : float, optional + Relative accuracy for eigenvalues (stopping criterion) + The default value of 0 implies machine precision. + return_eigenvectors : bool, optional + Return eigenvectors (True) in addition to eigenvalues + Minv : ndarray, sparse matrix or LinearOperator, optional + See notes in M, above. + OPinv : ndarray, sparse matrix or LinearOperator, optional + See notes in sigma, above. + OPpart : {'r' or 'i'}, optional + See notes in sigma, above + + Returns + ------- + w : ndarray + Array of k eigenvalues. + v : ndarray + An array of `k` eigenvectors. + ``v[:, i]`` is the eigenvector corresponding to the eigenvalue w[i]. + + Raises + ------ + ArpackNoConvergence + When the requested convergence is not obtained. + The currently converged eigenvalues and eigenvectors can be found + as ``eigenvalues`` and ``eigenvectors`` attributes of the exception + object. + + See Also + -------- + eigsh : eigenvalues and eigenvectors for symmetric matrix A + svds : singular value decomposition for a matrix A + + Notes + ----- + This function is a wrapper to the ARPACK [1]_ SNEUPD, DNEUPD, CNEUPD, + ZNEUPD, functions which use the Implicitly Restarted Arnoldi Method to + find the eigenvalues and eigenvectors [2]_. + + References + ---------- + .. [1] ARPACK Software, https://github.com/opencollab/arpack-ng + .. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + + Examples + -------- + Find 6 eigenvectors of the identity matrix: + + >>> import numpy as np + >>> from scipy.sparse.linalg import eigs + >>> id = np.eye(13) + >>> vals, vecs = eigs(id, k=6) + >>> vals + array([ 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]) + >>> vecs.shape + (13, 6) + + """ + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix (shape={A.shape})') + if M is not None: + if M.shape != A.shape: + raise ValueError(f'wrong M dimensions {M.shape}, should be {A.shape}') + if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): + warnings.warn('M does not have the same type precision as A. ' + 'This may adversely affect ARPACK convergence', + stacklevel=2) + + n = A.shape[0] + + if k <= 0: + raise ValueError("k=%d must be greater than 0." % k) + + if k >= n - 1: + warnings.warn("k >= N - 1 for N * N square matrix. " + "Attempting to use scipy.linalg.eig instead.", + RuntimeWarning, stacklevel=2) + + if issparse(A): + raise TypeError("Cannot use scipy.linalg.eig for sparse A with " + "k >= N - 1. Use scipy.linalg.eig(A.toarray()) or" + " reduce k.") + if isinstance(A, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " + "A with k >= N - 1.") + if isinstance(M, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " + "M with k >= N - 1.") + + return eig(A, b=M, right=return_eigenvectors) + + if sigma is None: + matvec = _aslinearoperator_with_dtype(A).matvec + + if OPinv is not None: + raise ValueError("OPinv should not be specified " + "with sigma = None.") + if OPpart is not None: + raise ValueError("OPpart should not be specified with " + "sigma = None or complex A") + + if M is None: + #standard eigenvalue problem + mode = 1 + M_matvec = None + Minv_matvec = None + if Minv is not None: + raise ValueError("Minv should not be " + "specified with M = None.") + else: + #general eigenvalue problem + mode = 2 + if Minv is None: + Minv_matvec = get_inv_matvec(M, hermitian=True, tol=tol) + else: + Minv = _aslinearoperator_with_dtype(Minv) + Minv_matvec = Minv.matvec + M_matvec = _aslinearoperator_with_dtype(M).matvec + else: + #sigma is not None: shift-invert mode + if np.issubdtype(A.dtype, np.complexfloating): + if OPpart is not None: + raise ValueError("OPpart should not be specified " + "with sigma=None or complex A") + mode = 3 + elif OPpart is None or OPpart.lower() == 'r': + mode = 3 + elif OPpart.lower() == 'i': + if np.imag(sigma) == 0: + raise ValueError("OPpart cannot be 'i' if sigma is real") + mode = 4 + else: + raise ValueError("OPpart must be one of ('r','i')") + + matvec = _aslinearoperator_with_dtype(A).matvec + if Minv is not None: + raise ValueError("Minv should not be specified when sigma is") + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=False, tol=tol) + else: + OPinv = _aslinearoperator_with_dtype(OPinv) + Minv_matvec = OPinv.matvec + if M is None: + M_matvec = None + else: + M_matvec = _aslinearoperator_with_dtype(M).matvec + + params = _UnsymmetricArpackParams(n, k, A.dtype.char, matvec, mode, + M_matvec, Minv_matvec, sigma, + ncv, v0, maxiter, which, tol) + + with _ARPACK_LOCK: + while not params.converged: + params.iterate() + + return params.extract(return_eigenvectors) + + +def eigsh(A, k=6, M=None, sigma=None, which='LM', v0=None, + ncv=None, maxiter=None, tol=0, return_eigenvectors=True, + Minv=None, OPinv=None, mode='normal'): + """ + Find k eigenvalues and eigenvectors of the real symmetric square matrix + or complex Hermitian matrix A. + + Solves ``A @ x[i] = w[i] * x[i]``, the standard eigenvalue problem for + w[i] eigenvalues with corresponding eigenvectors x[i]. + + If M is specified, solves ``A @ x[i] = w[i] * M @ x[i]``, the + generalized eigenvalue problem for w[i] eigenvalues + with corresponding eigenvectors x[i]. + + Note that there is no specialized routine for the case when A is a complex + Hermitian matrix. In this case, ``eigsh()`` will call ``eigs()`` and return the + real parts of the eigenvalues thus obtained. + + Parameters + ---------- + A : ndarray, sparse matrix or LinearOperator + A square operator representing the operation ``A @ x``, where ``A`` is + real symmetric or complex Hermitian. For buckling mode (see below) + ``A`` must additionally be positive-definite. + k : int, optional + The number of eigenvalues and eigenvectors desired. + `k` must be smaller than N. It is not possible to compute all + eigenvectors of a matrix. + + Returns + ------- + w : array + Array of k eigenvalues. + v : array + An array representing the `k` eigenvectors. The column ``v[:, i]`` is + the eigenvector corresponding to the eigenvalue ``w[i]``. + + Other Parameters + ---------------- + M : An N x N matrix, array, sparse matrix, or linear operator representing + the operation ``M @ x`` for the generalized eigenvalue problem + + A @ x = w * M @ x. + + M must represent a real symmetric matrix if A is real, and must + represent a complex Hermitian matrix if A is complex. For best + results, the data type of M should be the same as that of A. + Additionally: + + If sigma is None, M is symmetric positive definite. + + If sigma is specified, M is symmetric positive semi-definite. + + In buckling mode, M is symmetric indefinite. + + If sigma is None, eigsh requires an operator to compute the solution + of the linear equation ``M @ x = b``. This is done internally via a + (sparse) LU decomposition for an explicit matrix M, or via an + iterative solver for a general linear operator. Alternatively, + the user can supply the matrix or operator Minv, which gives + ``x = Minv @ b = M^-1 @ b``. + sigma : real + Find eigenvalues near sigma using shift-invert mode. This requires + an operator to compute the solution of the linear system + ``[A - sigma * M] x = b``, where M is the identity matrix if + unspecified. This is computed internally via a (sparse) LU + decomposition for explicit matrices A & M, or via an iterative + solver if either A or M is a general linear operator. + Alternatively, the user can supply the matrix or operator OPinv, + which gives ``x = OPinv @ b = [A - sigma * M]^-1 @ b``. + Note that when sigma is specified, the keyword 'which' refers to + the shifted eigenvalues ``w'[i]`` where: + + if mode == 'normal', ``w'[i] = 1 / (w[i] - sigma)``. + + if mode == 'cayley', ``w'[i] = (w[i] + sigma) / (w[i] - sigma)``. + + if mode == 'buckling', ``w'[i] = w[i] / (w[i] - sigma)``. + + (see further discussion in 'mode' below) + v0 : ndarray, optional + Starting vector for iteration. + Default: random + ncv : int, optional + The number of Lanczos vectors generated ncv must be greater than k and + smaller than n; it is recommended that ``ncv > 2*k``. + Default: ``min(n, max(2*k + 1, 20))`` + which : str ['LM' | 'SM' | 'LA' | 'SA' | 'BE'] + If A is a complex Hermitian matrix, 'BE' is invalid. + Which `k` eigenvectors and eigenvalues to find: + + 'LM' : Largest (in magnitude) eigenvalues. + + 'SM' : Smallest (in magnitude) eigenvalues. + + 'LA' : Largest (algebraic) eigenvalues. + + 'SA' : Smallest (algebraic) eigenvalues. + + 'BE' : Half (k/2) from each end of the spectrum. + + When k is odd, return one more (k/2+1) from the high end. + When sigma != None, 'which' refers to the shifted eigenvalues ``w'[i]`` + (see discussion in 'sigma', above). ARPACK is generally better + at finding large values than small values. If small eigenvalues are + desired, consider using shift-invert mode for better performance. + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed. + Default: ``n*10`` + tol : float + Relative accuracy for eigenvalues (stopping criterion). + The default value of 0 implies machine precision. + Minv : N x N matrix, array, sparse matrix, or LinearOperator + See notes in M, above. + OPinv : N x N matrix, array, sparse matrix, or LinearOperator + See notes in sigma, above. + return_eigenvectors : bool + Return eigenvectors (True) in addition to eigenvalues. + This value determines the order in which eigenvalues are sorted. + The sort order is also dependent on the `which` variable. + + For which = 'LM' or 'SA': + If `return_eigenvectors` is True, eigenvalues are sorted by + algebraic value. + + If `return_eigenvectors` is False, eigenvalues are sorted by + absolute value. + + For which = 'BE' or 'LA': + eigenvalues are always sorted by algebraic value. + + For which = 'SM': + If `return_eigenvectors` is True, eigenvalues are sorted by + algebraic value. + + If `return_eigenvectors` is False, eigenvalues are sorted by + decreasing absolute value. + + mode : string ['normal' | 'buckling' | 'cayley'] + Specify strategy to use for shift-invert mode. This argument applies + only for real-valued A and sigma != None. For shift-invert mode, + ARPACK internally solves the eigenvalue problem + ``OP @ x'[i] = w'[i] * B @ x'[i]`` + and transforms the resulting Ritz vectors x'[i] and Ritz values w'[i] + into the desired eigenvectors and eigenvalues of the problem + ``A @ x[i] = w[i] * M @ x[i]``. + The modes are as follows: + + 'normal' : + OP = [A - sigma * M]^-1 @ M, + B = M, + w'[i] = 1 / (w[i] - sigma) + + 'buckling' : + OP = [A - sigma * M]^-1 @ A, + B = A, + w'[i] = w[i] / (w[i] - sigma) + + 'cayley' : + OP = [A - sigma * M]^-1 @ [A + sigma * M], + B = M, + w'[i] = (w[i] + sigma) / (w[i] - sigma) + + The choice of mode will affect which eigenvalues are selected by + the keyword 'which', and can also impact the stability of + convergence (see [2] for a discussion). + + Raises + ------ + ArpackNoConvergence + When the requested convergence is not obtained. + + The currently converged eigenvalues and eigenvectors can be found + as ``eigenvalues`` and ``eigenvectors`` attributes of the exception + object. + + See Also + -------- + eigs : eigenvalues and eigenvectors for a general (nonsymmetric) matrix A + svds : singular value decomposition for a matrix A + + Notes + ----- + This function is a wrapper to the ARPACK [1]_ SSEUPD and DSEUPD + functions which use the Implicitly Restarted Lanczos Method to + find the eigenvalues and eigenvectors [2]_. + + References + ---------- + .. [1] ARPACK Software, https://github.com/opencollab/arpack-ng + .. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import eigsh + >>> identity = np.eye(13) + >>> eigenvalues, eigenvectors = eigsh(identity, k=6) + >>> eigenvalues + array([1., 1., 1., 1., 1., 1.]) + >>> eigenvectors.shape + (13, 6) + + """ + # complex Hermitian matrices should be solved with eigs + if np.issubdtype(A.dtype, np.complexfloating): + if mode != 'normal': + raise ValueError("mode=%s cannot be used with " + "complex matrix A" % mode) + if which == 'BE': + raise ValueError("which='BE' cannot be used with complex matrix A") + elif which == 'LA': + which = 'LR' + elif which == 'SA': + which = 'SR' + ret = eigs(A, k, M=M, sigma=sigma, which=which, v0=v0, + ncv=ncv, maxiter=maxiter, tol=tol, + return_eigenvectors=return_eigenvectors, Minv=Minv, + OPinv=OPinv) + + if return_eigenvectors: + return ret[0].real, ret[1] + else: + return ret.real + + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix (shape={A.shape})') + if M is not None: + if M.shape != A.shape: + raise ValueError(f'wrong M dimensions {M.shape}, should be {A.shape}') + if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): + warnings.warn('M does not have the same type precision as A. ' + 'This may adversely affect ARPACK convergence', + stacklevel=2) + + n = A.shape[0] + + if k <= 0: + raise ValueError("k must be greater than 0.") + + if k >= n: + warnings.warn("k >= N for N * N square matrix. " + "Attempting to use scipy.linalg.eigh instead.", + RuntimeWarning, stacklevel=2) + + if issparse(A): + raise TypeError("Cannot use scipy.linalg.eigh for sparse A with " + "k >= N. Use scipy.linalg.eigh(A.toarray()) or" + " reduce k.") + if isinstance(A, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " + "A with k >= N.") + if isinstance(M, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " + "M with k >= N.") + + return eigh(A, b=M, eigvals_only=not return_eigenvectors) + + if sigma is None: + A = _aslinearoperator_with_dtype(A) + matvec = A.matvec + + if OPinv is not None: + raise ValueError("OPinv should not be specified " + "with sigma = None.") + if M is None: + #standard eigenvalue problem + mode = 1 + M_matvec = None + Minv_matvec = None + if Minv is not None: + raise ValueError("Minv should not be " + "specified with M = None.") + else: + #general eigenvalue problem + mode = 2 + if Minv is None: + Minv_matvec = get_inv_matvec(M, hermitian=True, tol=tol) + else: + Minv = _aslinearoperator_with_dtype(Minv) + Minv_matvec = Minv.matvec + M_matvec = _aslinearoperator_with_dtype(M).matvec + else: + # sigma is not None: shift-invert mode + if Minv is not None: + raise ValueError("Minv should not be specified when sigma is") + + # normal mode + if mode == 'normal': + mode = 3 + matvec = None + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + OPinv = _aslinearoperator_with_dtype(OPinv) + Minv_matvec = OPinv.matvec + if M is None: + M_matvec = None + else: + M = _aslinearoperator_with_dtype(M) + M_matvec = M.matvec + + # buckling mode + elif mode == 'buckling': + mode = 4 + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec + matvec = _aslinearoperator_with_dtype(A).matvec + M_matvec = None + + # cayley-transform mode + elif mode == 'cayley': + mode = 5 + matvec = _aslinearoperator_with_dtype(A).matvec + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec + if M is None: + M_matvec = None + else: + M_matvec = _aslinearoperator_with_dtype(M).matvec + + # unrecognized mode + else: + raise ValueError("unrecognized mode '%s'" % mode) + + params = _SymmetricArpackParams(n, k, A.dtype.char, matvec, mode, + M_matvec, Minv_matvec, sigma, + ncv, v0, maxiter, which, tol) + + with _ARPACK_LOCK: + while not params.converged: + params.iterate() + + return params.extract(return_eigenvectors) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py new file mode 100644 index 0000000000000000000000000000000000000000..1cf73f28e21d4f29d067ae84bf63e5291acbe810 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py @@ -0,0 +1,718 @@ +__usage__ = """ +To run tests locally: + python tests/test_arpack.py [-l] [-v] + +""" + +import threading +import itertools + +import numpy as np + +from numpy.testing import assert_allclose, assert_equal, suppress_warnings +from pytest import raises as assert_raises +import pytest + +from numpy import dot, conj, random +from scipy.linalg import eig, eigh +from scipy.sparse import csc_matrix, csr_matrix, diags, rand +from scipy.sparse.linalg import LinearOperator, aslinearoperator +from scipy.sparse.linalg._eigen.arpack import (eigs, eigsh, arpack, + ArpackNoConvergence) + + +from scipy._lib._gcutils import assert_deallocated, IS_PYPY + + +# precision for tests +_ndigits = {'f': 3, 'd': 11, 'F': 3, 'D': 11} + + +def _get_test_tolerance(type_char, mattype=None, D_type=None, which=None): + """ + Return tolerance values suitable for a given test: + + Parameters + ---------- + type_char : {'f', 'd', 'F', 'D'} + Data type in ARPACK eigenvalue problem + mattype : {csr_matrix, aslinearoperator, asarray}, optional + Linear operator type + + Returns + ------- + tol + Tolerance to pass to the ARPACK routine + rtol + Relative tolerance for outputs + atol + Absolute tolerance for outputs + + """ + + rtol = {'f': 3000 * np.finfo(np.float32).eps, + 'F': 3000 * np.finfo(np.float32).eps, + 'd': 2000 * np.finfo(np.float64).eps, + 'D': 2000 * np.finfo(np.float64).eps}[type_char] + atol = rtol + tol = 0 + + if mattype is aslinearoperator and type_char in ('f', 'F'): + # iterative methods in single precision: worse errors + # also: bump ARPACK tolerance so that the iterative method converges + tol = 30 * np.finfo(np.float32).eps + rtol *= 5 + + if mattype is csr_matrix and type_char in ('f', 'F'): + # sparse in single precision: worse errors + rtol *= 5 + + if ( + which in ('LM', 'SM', 'LA') + and D_type.name == "gen-hermitian-Mc" + ): + if type_char == 'F': + # missing case 1, 2, and more, from PR 14798 + rtol *= 5 + + if type_char == 'D': + # missing more cases, from PR 14798 + rtol *= 10 + atol *= 10 + + return tol, rtol, atol + + +def generate_matrix(N, complex_=False, hermitian=False, + pos_definite=False, sparse=False): + M = np.random.random((N, N)) + if complex_: + M = M + 1j * np.random.random((N, N)) + + if hermitian: + if pos_definite: + if sparse: + i = np.arange(N) + j = np.random.randint(N, size=N-2) + i, j = np.meshgrid(i, j) + M[i, j] = 0 + M = np.dot(M.conj(), M.T) + else: + M = np.dot(M.conj(), M.T) + if sparse: + i = np.random.randint(N, size=N * N // 4) + j = np.random.randint(N, size=N * N // 4) + ind = np.nonzero(i == j) + j[ind] = (j[ind] + 1) % N + M[i, j] = 0 + M[j, i] = 0 + else: + if sparse: + i = np.random.randint(N, size=N * N // 2) + j = np.random.randint(N, size=N * N // 2) + M[i, j] = 0 + return M + + +def generate_matrix_symmetric(N, pos_definite=False, sparse=False): + M = np.random.random((N, N)) + + M = 0.5 * (M + M.T) # Make M symmetric + + if pos_definite: + Id = N * np.eye(N) + if sparse: + M = csr_matrix(M) + M += Id + else: + if sparse: + M = csr_matrix(M) + + return M + + +def assert_allclose_cc(actual, desired, **kw): + """Almost equal or complex conjugates almost equal""" + try: + assert_allclose(actual, desired, **kw) + except AssertionError: + assert_allclose(actual, conj(desired), **kw) + + +def argsort_which(eigenvalues, typ, k, which, + sigma=None, OPpart=None, mode=None): + """Return sorted indices of eigenvalues using the "which" keyword + from eigs and eigsh""" + if sigma is None: + reval = np.round(eigenvalues, decimals=_ndigits[typ]) + else: + if mode is None or mode == 'normal': + if OPpart is None: + reval = 1. / (eigenvalues - sigma) + elif OPpart == 'r': + reval = 0.5 * (1. / (eigenvalues - sigma) + + 1. / (eigenvalues - np.conj(sigma))) + elif OPpart == 'i': + reval = -0.5j * (1. / (eigenvalues - sigma) + - 1. / (eigenvalues - np.conj(sigma))) + elif mode == 'cayley': + reval = (eigenvalues + sigma) / (eigenvalues - sigma) + elif mode == 'buckling': + reval = eigenvalues / (eigenvalues - sigma) + else: + raise ValueError("mode='%s' not recognized" % mode) + + reval = np.round(reval, decimals=_ndigits[typ]) + + if which in ['LM', 'SM']: + ind = np.argsort(abs(reval)) + elif which in ['LR', 'SR', 'LA', 'SA', 'BE']: + ind = np.argsort(np.real(reval)) + elif which in ['LI', 'SI']: + # for LI,SI ARPACK returns largest,smallest abs(imaginary) why? + if typ.islower(): + ind = np.argsort(abs(np.imag(reval))) + else: + ind = np.argsort(np.imag(reval)) + else: + raise ValueError("which='%s' is unrecognized" % which) + + if which in ['LM', 'LA', 'LR', 'LI']: + return ind[-k:] + elif which in ['SM', 'SA', 'SR', 'SI']: + return ind[:k] + elif which == 'BE': + return np.concatenate((ind[:k//2], ind[k//2-k:])) + + +def eval_evec(symmetric, d, typ, k, which, v0=None, sigma=None, + mattype=np.asarray, OPpart=None, mode='normal'): + general = ('bmat' in d) + + if symmetric: + eigs_func = eigsh + else: + eigs_func = eigs + + if general: + err = ("error for {}:general, typ={}, which={}, sigma={}, " + "mattype={}, OPpart={}, mode={}".format(eigs_func.__name__, + typ, which, sigma, + mattype.__name__, + OPpart, mode)) + else: + err = ("error for {}:standard, typ={}, which={}, sigma={}, " + "mattype={}, OPpart={}, mode={}".format(eigs_func.__name__, + typ, which, sigma, + mattype.__name__, + OPpart, mode)) + + a = d['mat'].astype(typ) + ac = mattype(a) + + if general: + b = d['bmat'].astype(typ) + bc = mattype(b) + + # get exact eigenvalues + exact_eval = d['eval'].astype(typ.upper()) + ind = argsort_which(exact_eval, typ, k, which, + sigma, OPpart, mode) + exact_eval = exact_eval[ind] + + # compute arpack eigenvalues + kwargs = dict(which=which, v0=v0, sigma=sigma) + if eigs_func is eigsh: + kwargs['mode'] = mode + else: + kwargs['OPpart'] = OPpart + + # compute suitable tolerances + kwargs['tol'], rtol, atol = _get_test_tolerance(typ, mattype, d, which) + # on rare occasions, ARPACK routines return results that are proper + # eigenvalues and -vectors, but not necessarily the ones requested in + # the parameter which. This is inherent to the Krylov methods, and + # should not be treated as a failure. If such a rare situation + # occurs, the calculation is tried again (but at most a few times). + ntries = 0 + while ntries < 5: + # solve + if general: + try: + eigenvalues, evec = eigs_func(ac, k, bc, **kwargs) + except ArpackNoConvergence: + kwargs['maxiter'] = 20*a.shape[0] + eigenvalues, evec = eigs_func(ac, k, bc, **kwargs) + else: + try: + eigenvalues, evec = eigs_func(ac, k, **kwargs) + except ArpackNoConvergence: + kwargs['maxiter'] = 20*a.shape[0] + eigenvalues, evec = eigs_func(ac, k, **kwargs) + + ind = argsort_which(eigenvalues, typ, k, which, + sigma, OPpart, mode) + eigenvalues = eigenvalues[ind] + evec = evec[:, ind] + + try: + # check eigenvalues + assert_allclose_cc(eigenvalues, exact_eval, rtol=rtol, atol=atol, + err_msg=err) + check_evecs = True + except AssertionError: + check_evecs = False + ntries += 1 + + if check_evecs: + # check eigenvectors + LHS = np.dot(a, evec) + if general: + RHS = eigenvalues * np.dot(b, evec) + else: + RHS = eigenvalues * evec + + assert_allclose(LHS, RHS, rtol=rtol, atol=atol, err_msg=err) + break + + # check eigenvalues + assert_allclose_cc(eigenvalues, exact_eval, rtol=rtol, atol=atol, err_msg=err) + + +class DictWithRepr(dict): + def __init__(self, name): + self.name = name + + def __repr__(self): + return "<%s>" % self.name + + +class SymmetricParams: + def __init__(self): + self.eigs = eigsh + self.which = ['LM', 'SM', 'LA', 'SA', 'BE'] + self.mattypes = [csr_matrix, aslinearoperator, np.asarray] + self.sigmas_modes = {None: ['normal'], + 0.5: ['normal', 'buckling', 'cayley']} + + # generate matrices + # these should all be float32 so that the eigenvalues + # are the same in float32 and float64 + N = 6 + np.random.seed(2300) + Ar = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + M = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + Ac = generate_matrix(N, hermitian=True, pos_definite=True, + complex_=True).astype('F').astype('D') + Mc = generate_matrix(N, hermitian=True, pos_definite=True, + complex_=True).astype('F').astype('D') + v0 = np.random.random(N) + + # standard symmetric problem + SS = DictWithRepr("std-symmetric") + SS['mat'] = Ar + SS['v0'] = v0 + SS['eval'] = eigh(SS['mat'], eigvals_only=True) + + # general symmetric problem + GS = DictWithRepr("gen-symmetric") + GS['mat'] = Ar + GS['bmat'] = M + GS['v0'] = v0 + GS['eval'] = eigh(GS['mat'], GS['bmat'], eigvals_only=True) + + # standard hermitian problem + SH = DictWithRepr("std-hermitian") + SH['mat'] = Ac + SH['v0'] = v0 + SH['eval'] = eigh(SH['mat'], eigvals_only=True) + + # general hermitian problem + GH = DictWithRepr("gen-hermitian") + GH['mat'] = Ac + GH['bmat'] = M + GH['v0'] = v0 + GH['eval'] = eigh(GH['mat'], GH['bmat'], eigvals_only=True) + + # general hermitian problem with hermitian M + GHc = DictWithRepr("gen-hermitian-Mc") + GHc['mat'] = Ac + GHc['bmat'] = Mc + GHc['v0'] = v0 + GHc['eval'] = eigh(GHc['mat'], GHc['bmat'], eigvals_only=True) + + self.real_test_cases = [SS, GS] + self.complex_test_cases = [SH, GH, GHc] + + +class NonSymmetricParams: + def __init__(self): + self.eigs = eigs + self.which = ['LM', 'LR', 'LI'] # , 'SM', 'LR', 'SR', 'LI', 'SI'] + self.mattypes = [csr_matrix, aslinearoperator, np.asarray] + self.sigmas_OPparts = {None: [None], + 0.1: ['r'], + 0.1 + 0.1j: ['r', 'i']} + + # generate matrices + # these should all be float32 so that the eigenvalues + # are the same in float32 and float64 + N = 6 + np.random.seed(2300) + Ar = generate_matrix(N).astype('f').astype('d') + M = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + Ac = generate_matrix(N, complex_=True).astype('F').astype('D') + v0 = np.random.random(N) + + # standard real nonsymmetric problem + SNR = DictWithRepr("std-real-nonsym") + SNR['mat'] = Ar + SNR['v0'] = v0 + SNR['eval'] = eig(SNR['mat'], left=False, right=False) + + # general real nonsymmetric problem + GNR = DictWithRepr("gen-real-nonsym") + GNR['mat'] = Ar + GNR['bmat'] = M + GNR['v0'] = v0 + GNR['eval'] = eig(GNR['mat'], GNR['bmat'], left=False, right=False) + + # standard complex nonsymmetric problem + SNC = DictWithRepr("std-cmplx-nonsym") + SNC['mat'] = Ac + SNC['v0'] = v0 + SNC['eval'] = eig(SNC['mat'], left=False, right=False) + + # general complex nonsymmetric problem + GNC = DictWithRepr("gen-cmplx-nonsym") + GNC['mat'] = Ac + GNC['bmat'] = M + GNC['v0'] = v0 + GNC['eval'] = eig(GNC['mat'], GNC['bmat'], left=False, right=False) + + self.real_test_cases = [SNR, GNR] + self.complex_test_cases = [SNC, GNC] + + +def test_symmetric_modes(): + params = SymmetricParams() + k = 2 + symmetric = True + for D in params.real_test_cases: + for typ in 'fd': + for which in params.which: + for mattype in params.mattypes: + for (sigma, modes) in params.sigmas_modes.items(): + for mode in modes: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype, None, mode) + + +def test_hermitian_modes(): + params = SymmetricParams() + k = 2 + symmetric = True + for D in params.complex_test_cases: + for typ in 'FD': + for which in params.which: + if which == 'BE': + continue # BE invalid for complex + for mattype in params.mattypes: + for sigma in params.sigmas_modes: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype) + + +def test_symmetric_starting_vector(): + params = SymmetricParams() + symmetric = True + for k in [1, 2, 3, 4, 5]: + for D in params.real_test_cases: + for typ in 'fd': + v0 = random.rand(len(D['v0'])).astype(typ) + eval_evec(symmetric, D, typ, k, 'LM', v0) + + +def test_symmetric_no_convergence(): + np.random.seed(1234) + m = generate_matrix(30, hermitian=True, pos_definite=True) + tol, rtol, atol = _get_test_tolerance('d') + try: + w, v = eigsh(m, 4, which='LM', v0=m[:, 0], maxiter=5, tol=tol, ncv=9) + raise AssertionError("Spurious no-error exit") + except ArpackNoConvergence as err: + k = len(err.eigenvalues) + if k <= 0: + raise AssertionError("Spurious no-eigenvalues-found case") from err + w, v = err.eigenvalues, err.eigenvectors + assert_allclose(dot(m, v), w * v, rtol=rtol, atol=atol) + + +def test_real_nonsymmetric_modes(): + params = NonSymmetricParams() + k = 2 + symmetric = False + for D in params.real_test_cases: + for typ in 'fd': + for which in params.which: + for mattype in params.mattypes: + for sigma, OPparts in params.sigmas_OPparts.items(): + for OPpart in OPparts: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype, OPpart) + + +def test_complex_nonsymmetric_modes(): + params = NonSymmetricParams() + k = 2 + symmetric = False + for D in params.complex_test_cases: + for typ in 'DF': + for which in params.which: + for mattype in params.mattypes: + for sigma in params.sigmas_OPparts: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype) + + +def test_standard_nonsymmetric_starting_vector(): + params = NonSymmetricParams() + sigma = None + symmetric = False + for k in [1, 2, 3, 4]: + for d in params.complex_test_cases: + for typ in 'FD': + A = d['mat'] + n = A.shape[0] + v0 = random.rand(n).astype(typ) + eval_evec(symmetric, d, typ, k, "LM", v0, sigma) + + +def test_general_nonsymmetric_starting_vector(): + params = NonSymmetricParams() + sigma = None + symmetric = False + for k in [1, 2, 3, 4]: + for d in params.complex_test_cases: + for typ in 'FD': + A = d['mat'] + n = A.shape[0] + v0 = random.rand(n).astype(typ) + eval_evec(symmetric, d, typ, k, "LM", v0, sigma) + + +def test_standard_nonsymmetric_no_convergence(): + np.random.seed(1234) + m = generate_matrix(30, complex_=True) + tol, rtol, atol = _get_test_tolerance('d') + try: + w, v = eigs(m, 4, which='LM', v0=m[:, 0], maxiter=5, tol=tol) + raise AssertionError("Spurious no-error exit") + except ArpackNoConvergence as err: + k = len(err.eigenvalues) + if k <= 0: + raise AssertionError("Spurious no-eigenvalues-found case") from err + w, v = err.eigenvalues, err.eigenvectors + for ww, vv in zip(w, v.T): + assert_allclose(dot(m, vv), ww * vv, rtol=rtol, atol=atol) + + +def test_eigen_bad_shapes(): + # A is not square. + A = csc_matrix(np.zeros((2, 3))) + assert_raises(ValueError, eigs, A) + + +def test_eigen_bad_kwargs(): + # Test eigen on wrong keyword argument + A = csc_matrix(np.zeros((8, 8))) + assert_raises(ValueError, eigs, A, which='XX') + + +def test_ticket_1459_arpack_crash(): + for dtype in [np.float32, np.float64]: + # This test does not seem to catch the issue for float32, + # but we made the same fix there, just to be sure + + N = 6 + k = 2 + + np.random.seed(2301) + A = np.random.random((N, N)).astype(dtype) + v0 = np.array([-0.71063568258907849895, -0.83185111795729227424, + -0.34365925382227402451, 0.46122533684552280420, + -0.58001341115969040629, -0.78844877570084292984e-01], + dtype=dtype) + + # Should not crash: + evals, evecs = eigs(A, k, v0=v0) + + +@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy") +def test_linearoperator_deallocation(): + # Check that the linear operators used by the Arpack wrappers are + # deallocatable by reference counting -- they are big objects, so + # Python's cyclic GC may not collect them fast enough before + # running out of memory if eigs/eigsh are called in a tight loop. + + M_d = np.eye(10) + M_s = csc_matrix(M_d) + M_o = aslinearoperator(M_d) + + with assert_deallocated(lambda: arpack.SpLuInv(M_s)): + pass + with assert_deallocated(lambda: arpack.LuInv(M_d)): + pass + with assert_deallocated(lambda: arpack.IterInv(M_s)): + pass + with assert_deallocated(lambda: arpack.IterOpInv(M_o, None, 0.3)): + pass + with assert_deallocated(lambda: arpack.IterOpInv(M_o, M_o, 0.3)): + pass + +def test_parallel_threads(): + results = [] + v0 = np.random.rand(50) + + def worker(): + x = diags([1, -2, 1], [-1, 0, 1], shape=(50, 50)) + w, v = eigs(x, k=3, v0=v0) + results.append(w) + + w, v = eigsh(x, k=3, v0=v0) + results.append(w) + + threads = [threading.Thread(target=worker) for k in range(10)] + for t in threads: + t.start() + for t in threads: + t.join() + + worker() + + for r in results: + assert_allclose(r, results[-1]) + + +def test_reentering(): + # Just some linear operator that calls eigs recursively + def A_matvec(x): + x = diags([1, -2, 1], [-1, 0, 1], shape=(50, 50)) + w, v = eigs(x, k=1) + return v / w[0] + A = LinearOperator(matvec=A_matvec, dtype=float, shape=(50, 50)) + + # The Fortran code is not reentrant, so this fails (gracefully, not crashing) + assert_raises(RuntimeError, eigs, A, k=1) + assert_raises(RuntimeError, eigsh, A, k=1) + + +def test_regression_arpackng_1315(): + # Check that issue arpack-ng/#1315 is not present. + # Adapted from arpack-ng/TESTS/bug_1315_single.c + # If this fails, then the installed ARPACK library is faulty. + + for dtype in [np.float32, np.float64]: + np.random.seed(1234) + + w0 = np.arange(1, 1000+1).astype(dtype) + A = diags([w0], [0], shape=(1000, 1000)) + + v0 = np.random.rand(1000).astype(dtype) + w, v = eigs(A, k=9, ncv=2*9+1, which="LM", v0=v0) + + assert_allclose(np.sort(w), np.sort(w0[-9:]), + rtol=1e-4) + + +def test_eigs_for_k_greater(): + # Test eigs() for k beyond limits. + A_sparse = diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)) # sparse + A = generate_matrix(4, sparse=False) + M_dense = np.random.random((4, 4)) + M_sparse = generate_matrix(4, sparse=True) + M_linop = aslinearoperator(M_dense) + eig_tuple1 = eig(A, b=M_dense) + eig_tuple2 = eig(A, b=M_sparse) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + + assert_equal(eigs(A, M=M_dense, k=3), eig_tuple1) + assert_equal(eigs(A, M=M_dense, k=4), eig_tuple1) + assert_equal(eigs(A, M=M_dense, k=5), eig_tuple1) + assert_equal(eigs(A, M=M_sparse, k=5), eig_tuple2) + + # M as LinearOperator + assert_raises(TypeError, eigs, A, M=M_linop, k=3) + + # Test 'A' for different types + assert_raises(TypeError, eigs, aslinearoperator(A), k=3) + assert_raises(TypeError, eigs, A_sparse, k=3) + + +def test_eigsh_for_k_greater(): + # Test eigsh() for k beyond limits. + A_sparse = diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)) # sparse + A = generate_matrix(4, sparse=False) + M_dense = generate_matrix_symmetric(4, pos_definite=True) + M_sparse = generate_matrix_symmetric(4, pos_definite=True, sparse=True) + M_linop = aslinearoperator(M_dense) + eig_tuple1 = eigh(A, b=M_dense) + eig_tuple2 = eigh(A, b=M_sparse) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + + assert_equal(eigsh(A, M=M_dense, k=4), eig_tuple1) + assert_equal(eigsh(A, M=M_dense, k=5), eig_tuple1) + assert_equal(eigsh(A, M=M_sparse, k=5), eig_tuple2) + + # M as LinearOperator + assert_raises(TypeError, eigsh, A, M=M_linop, k=4) + + # Test 'A' for different types + assert_raises(TypeError, eigsh, aslinearoperator(A), k=4) + assert_raises(TypeError, eigsh, A_sparse, M=M_dense, k=4) + + +def test_real_eigs_real_k_subset(): + np.random.seed(1) + + n = 10 + A = rand(n, n, density=0.5) + A.data *= 2 + A.data -= 1 + + v0 = np.ones(n) + + whichs = ['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] + dtypes = [np.float32, np.float64] + + for which, sigma, dtype in itertools.product(whichs, [None, 0, 5], dtypes): + prev_w = np.array([], dtype=dtype) + eps = np.finfo(dtype).eps + for k in range(1, 9): + w, z = eigs(A.astype(dtype), k=k, which=which, sigma=sigma, + v0=v0.astype(dtype), tol=0) + assert_allclose(np.linalg.norm(A.dot(z) - z * w), 0, atol=np.sqrt(eps)) + + # Check that the set of eigenvalues for `k` is a subset of that for `k+1` + dist = abs(prev_w[:,None] - w).min(axis=1) + assert_allclose(dist, 0, atol=np.sqrt(eps)) + + prev_w = w + + # Check sort order + if sigma is None: + d = w + else: + d = 1 / (w - sigma) + + if which == 'LM': + # ARPACK is systematic for 'LM', but sort order + # appears not well defined for other modes + assert np.all(np.diff(abs(d)) <= 1e-6) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab5330361a6bcc2a8403f9b3788aedae750d57f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py @@ -0,0 +1,16 @@ +""" +Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG) + +LOBPCG is a preconditioned eigensolver for large symmetric positive definite +(SPD) generalized eigenproblems. + +Call the function lobpcg - see help for lobpcg.lobpcg. + +""" +from .lobpcg import * + +__all__ = [s for s in dir() if not s.startswith('_')] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py new file mode 100644 index 0000000000000000000000000000000000000000..2e4cfc155675fff7b36bda24b7c63cf70456e0db --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py @@ -0,0 +1,1112 @@ +""" +Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG). + +References +---------- +.. [1] A. V. Knyazev (2001), + Toward the Optimal Preconditioned Eigensolver: Locally Optimal + Block Preconditioned Conjugate Gradient Method. + SIAM Journal on Scientific Computing 23, no. 2, + pp. 517-541. :doi:`10.1137/S1064827500366124` + +.. [2] A. V. Knyazev, I. Lashuk, M. E. Argentati, and E. Ovchinnikov (2007), + Block Locally Optimal Preconditioned Eigenvalue Xolvers (BLOPEX) + in hypre and PETSc. :arxiv:`0705.2626` + +.. [3] A. V. Knyazev's C and MATLAB implementations: + https://github.com/lobpcg/blopex +""" + +import warnings +import numpy as np +from scipy.linalg import (inv, eigh, cho_factor, cho_solve, + cholesky, LinAlgError) +from scipy.sparse.linalg import LinearOperator +from scipy.sparse import issparse + +__all__ = ["lobpcg"] + + +def _report_nonhermitian(M, name): + """ + Report if `M` is not a Hermitian matrix given its type. + """ + from scipy.linalg import norm + + md = M - M.T.conj() + nmd = norm(md, 1) + tol = 10 * np.finfo(M.dtype).eps + tol = max(tol, tol * norm(M, 1)) + if nmd > tol: + warnings.warn( + f"Matrix {name} of the type {M.dtype} is not Hermitian: " + f"condition: {nmd} < {tol} fails.", + UserWarning, stacklevel=4 + ) + +def _as2d(ar): + """ + If the input array is 2D return it, if it is 1D, append a dimension, + making it a column vector. + """ + if ar.ndim == 2: + return ar + else: # Assume 1! + aux = np.asarray(ar) + aux.shape = (ar.shape[0], 1) + return aux + + +def _makeMatMat(m): + if m is None: + return None + elif callable(m): + return lambda v: m(v) + else: + return lambda v: m @ v + + +def _matmul_inplace(x, y, verbosityLevel=0): + """Perform 'np.matmul' in-place if possible. + + If some sufficient conditions for inplace matmul are met, do so. + Otherwise try inplace update and fall back to overwrite if that fails. + """ + if x.flags["CARRAY"] and x.shape[1] == y.shape[1] and x.dtype == y.dtype: + # conditions where we can guarantee that inplace updates will work; + # i.e. x is not a view/slice, x & y have compatible dtypes, and the + # shape of the result of x @ y matches the shape of x. + np.matmul(x, y, out=x) + else: + # ideally, we'd have an exhaustive list of conditions above when + # inplace updates are possible; since we don't, we opportunistically + # try if it works, and fall back to overwriting if necessary + try: + np.matmul(x, y, out=x) + except Exception: + if verbosityLevel: + warnings.warn( + "Inplace update of x = x @ y failed, " + "x needs to be overwritten.", + UserWarning, stacklevel=3 + ) + x = x @ y + return x + + +def _applyConstraints(blockVectorV, factYBY, blockVectorBY, blockVectorY): + """Changes blockVectorV in-place.""" + YBV = blockVectorBY.T.conj() @ blockVectorV + tmp = cho_solve(factYBY, YBV) + blockVectorV -= blockVectorY @ tmp + + +def _b_orthonormalize(B, blockVectorV, blockVectorBV=None, + verbosityLevel=0): + """in-place B-orthonormalize the given block vector using Cholesky.""" + if blockVectorBV is None: + if B is None: + blockVectorBV = blockVectorV + else: + try: + blockVectorBV = B(blockVectorV) + except Exception as e: + if verbosityLevel: + warnings.warn( + f"Secondary MatMul call failed with error\n" + f"{e}\n", + UserWarning, stacklevel=3 + ) + return None, None, None + if blockVectorBV.shape != blockVectorV.shape: + raise ValueError( + f"The shape {blockVectorV.shape} " + f"of the orthogonalized matrix not preserved\n" + f"and changed to {blockVectorBV.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + VBV = blockVectorV.T.conj() @ blockVectorBV + try: + # VBV is a Cholesky factor from now on... + VBV = cholesky(VBV, overwrite_a=True) + VBV = inv(VBV, overwrite_a=True) + blockVectorV = _matmul_inplace( + blockVectorV, VBV, + verbosityLevel=verbosityLevel + ) + if B is not None: + blockVectorBV = _matmul_inplace( + blockVectorBV, VBV, + verbosityLevel=verbosityLevel + ) + return blockVectorV, blockVectorBV, VBV + except LinAlgError: + if verbosityLevel: + warnings.warn( + "Cholesky has failed.", + UserWarning, stacklevel=3 + ) + return None, None, None + + +def _get_indx(_lambda, num, largest): + """Get `num` indices into `_lambda` depending on `largest` option.""" + ii = np.argsort(_lambda) + if largest: + ii = ii[:-num - 1:-1] + else: + ii = ii[:num] + + return ii + + +def _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel): + if verbosityLevel: + _report_nonhermitian(gramA, "gramA") + _report_nonhermitian(gramB, "gramB") + + +def lobpcg( + A, + X, + B=None, + M=None, + Y=None, + tol=None, + maxiter=None, + largest=True, + verbosityLevel=0, + retLambdaHistory=False, + retResidualNormsHistory=False, + restartControl=20, +): + """Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG). + + LOBPCG is a preconditioned eigensolver for large real symmetric and complex + Hermitian definite generalized eigenproblems. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator, callable object} + The Hermitian linear operator of the problem, usually given by a + sparse matrix. Often called the "stiffness matrix". + X : ndarray, float32 or float64 + Initial approximation to the ``k`` eigenvectors (non-sparse). + If `A` has ``shape=(n,n)`` then `X` must have ``shape=(n,k)``. + B : {sparse matrix, ndarray, LinearOperator, callable object} + Optional. By default ``B = None``, which is equivalent to identity. + The right hand side operator in a generalized eigenproblem if present. + Often called the "mass matrix". Must be Hermitian positive definite. + M : {sparse matrix, ndarray, LinearOperator, callable object} + Optional. By default ``M = None``, which is equivalent to identity. + Preconditioner aiming to accelerate convergence. + Y : ndarray, float32 or float64, default: None + An ``n-by-sizeY`` ndarray of constraints with ``sizeY < n``. + The iterations will be performed in the ``B``-orthogonal complement + of the column-space of `Y`. `Y` must be full rank if present. + tol : scalar, optional + The default is ``tol=n*sqrt(eps)``. + Solver tolerance for the stopping criterion. + maxiter : int, default: 20 + Maximum number of iterations. + largest : bool, default: True + When True, solve for the largest eigenvalues, otherwise the smallest. + verbosityLevel : int, optional + By default ``verbosityLevel=0`` no output. + Controls the solver standard/screen output. + retLambdaHistory : bool, default: False + Whether to return iterative eigenvalue history. + retResidualNormsHistory : bool, default: False + Whether to return iterative history of residual norms. + restartControl : int, optional. + Iterations restart if the residuals jump ``2**restartControl`` times + compared to the smallest recorded in ``retResidualNormsHistory``. + The default is ``restartControl=20``, making the restarts rare for + backward compatibility. + + Returns + ------- + lambda : ndarray of the shape ``(k, )``. + Array of ``k`` approximate eigenvalues. + v : ndarray of the same shape as ``X.shape``. + An array of ``k`` approximate eigenvectors. + lambdaHistory : ndarray, optional. + The eigenvalue history, if `retLambdaHistory` is ``True``. + ResidualNormsHistory : ndarray, optional. + The history of residual norms, if `retResidualNormsHistory` + is ``True``. + + Notes + ----- + The iterative loop runs ``maxit=maxiter`` (20 if ``maxit=None``) + iterations at most and finishes earlier if the tolerance is met. + Breaking backward compatibility with the previous version, LOBPCG + now returns the block of iterative vectors with the best accuracy rather + than the last one iterated, as a cure for possible divergence. + + If ``X.dtype == np.float32`` and user-provided operations/multiplications + by `A`, `B`, and `M` all preserve the ``np.float32`` data type, + all the calculations and the output are in ``np.float32``. + + The size of the iteration history output equals to the number of the best + (limited by `maxit`) iterations plus 3: initial, final, and postprocessing. + + If both `retLambdaHistory` and `retResidualNormsHistory` are ``True``, + the return tuple has the following format + ``(lambda, V, lambda history, residual norms history)``. + + In the following ``n`` denotes the matrix size and ``k`` the number + of required eigenvalues (smallest or largest). + + The LOBPCG code internally solves eigenproblems of the size ``3k`` on every + iteration by calling the dense eigensolver `eigh`, so if ``k`` is not + small enough compared to ``n``, it makes no sense to call the LOBPCG code. + Moreover, if one calls the LOBPCG algorithm for ``5k > n``, it would likely + break internally, so the code calls the standard function `eigh` instead. + It is not that ``n`` should be large for the LOBPCG to work, but rather the + ratio ``n / k`` should be large. It you call LOBPCG with ``k=1`` + and ``n=10``, it works though ``n`` is small. The method is intended + for extremely large ``n / k``. + + The convergence speed depends basically on three factors: + + 1. Quality of the initial approximations `X` to the seeking eigenvectors. + Randomly distributed around the origin vectors work well if no better + choice is known. + + 2. Relative separation of the desired eigenvalues from the rest + of the eigenvalues. One can vary ``k`` to improve the separation. + + 3. Proper preconditioning to shrink the spectral spread. + For example, a rod vibration test problem (under tests + directory) is ill-conditioned for large ``n``, so convergence will be + slow, unless efficient preconditioning is used. For this specific + problem, a good simple preconditioner function would be a linear solve + for `A`, which is easy to code since `A` is tridiagonal. + + References + ---------- + .. [1] A. V. Knyazev (2001), + Toward the Optimal Preconditioned Eigensolver: Locally Optimal + Block Preconditioned Conjugate Gradient Method. + SIAM Journal on Scientific Computing 23, no. 2, + pp. 517-541. :doi:`10.1137/S1064827500366124` + + .. [2] A. V. Knyazev, I. Lashuk, M. E. Argentati, and E. Ovchinnikov + (2007), Block Locally Optimal Preconditioned Eigenvalue Xolvers + (BLOPEX) in hypre and PETSc. :arxiv:`0705.2626` + + .. [3] A. V. Knyazev's C and MATLAB implementations: + https://github.com/lobpcg/blopex + + Examples + -------- + Our first example is minimalistic - find the largest eigenvalue of + a diagonal matrix by solving the non-generalized eigenvalue problem + ``A x = lambda x`` without constraints or preconditioning. + + >>> import numpy as np + >>> from scipy.sparse import spdiags + >>> from scipy.sparse.linalg import LinearOperator, aslinearoperator + >>> from scipy.sparse.linalg import lobpcg + + The square matrix size is + + >>> n = 100 + + and its diagonal entries are 1, ..., 100 defined by + + >>> vals = np.arange(1, n + 1).astype(np.int16) + + The first mandatory input parameter in this test is + the sparse diagonal matrix `A` + of the eigenvalue problem ``A x = lambda x`` to solve. + + >>> A = spdiags(vals, 0, n, n) + >>> A = A.astype(np.int16) + >>> A.toarray() + array([[ 1, 0, 0, ..., 0, 0, 0], + [ 0, 2, 0, ..., 0, 0, 0], + [ 0, 0, 3, ..., 0, 0, 0], + ..., + [ 0, 0, 0, ..., 98, 0, 0], + [ 0, 0, 0, ..., 0, 99, 0], + [ 0, 0, 0, ..., 0, 0, 100]], dtype=int16) + + The second mandatory input parameter `X` is a 2D array with the + row dimension determining the number of requested eigenvalues. + `X` is an initial guess for targeted eigenvectors. + `X` must have linearly independent columns. + If no initial approximations available, randomly oriented vectors + commonly work best, e.g., with components normally distributed + around zero or uniformly distributed on the interval [-1 1]. + Setting the initial approximations to dtype ``np.float32`` + forces all iterative values to dtype ``np.float32`` speeding up + the run while still allowing accurate eigenvalue computations. + + >>> k = 1 + >>> rng = np.random.default_rng() + >>> X = rng.normal(size=(n, k)) + >>> X = X.astype(np.float32) + + >>> eigenvalues, _ = lobpcg(A, X, maxiter=60) + >>> eigenvalues + array([100.]) + >>> eigenvalues.dtype + dtype('float32') + + `lobpcg` needs only access the matrix product with `A` rather + then the matrix itself. Since the matrix `A` is diagonal in + this example, one can write a function of the matrix product + ``A @ X`` using the diagonal values ``vals`` only, e.g., by + element-wise multiplication with broadcasting in the lambda-function + + >>> A_lambda = lambda X: vals[:, np.newaxis] * X + + or the regular function + + >>> def A_matmat(X): + ... return vals[:, np.newaxis] * X + + and use the handle to one of these callables as an input + + >>> eigenvalues, _ = lobpcg(A_lambda, X, maxiter=60) + >>> eigenvalues + array([100.]) + >>> eigenvalues, _ = lobpcg(A_matmat, X, maxiter=60) + >>> eigenvalues + array([100.]) + + The traditional callable `LinearOperator` is no longer + necessary but still supported as the input to `lobpcg`. + Specifying ``matmat=A_matmat`` explicitly improves performance. + + >>> A_lo = LinearOperator((n, n), matvec=A_matmat, matmat=A_matmat, dtype=np.int16) + >>> eigenvalues, _ = lobpcg(A_lo, X, maxiter=80) + >>> eigenvalues + array([100.]) + + The least efficient callable option is `aslinearoperator`: + + >>> eigenvalues, _ = lobpcg(aslinearoperator(A), X, maxiter=80) + >>> eigenvalues + array([100.]) + + We now switch to computing the three smallest eigenvalues specifying + + >>> k = 3 + >>> X = np.random.default_rng().normal(size=(n, k)) + + and ``largest=False`` parameter + + >>> eigenvalues, _ = lobpcg(A, X, largest=False, maxiter=80) + >>> print(eigenvalues) + [1. 2. 3.] + + The next example illustrates computing 3 smallest eigenvalues of + the same matrix `A` given by the function handle ``A_matmat`` but + with constraints and preconditioning. + + Constraints - an optional input parameter is a 2D array comprising + of column vectors that the eigenvectors must be orthogonal to + + >>> Y = np.eye(n, 3) + + The preconditioner acts as the inverse of `A` in this example, but + in the reduced precision ``np.float32`` even though the initial `X` + and thus all iterates and the output are in full ``np.float64``. + + >>> inv_vals = 1./vals + >>> inv_vals = inv_vals.astype(np.float32) + >>> M = lambda X: inv_vals[:, np.newaxis] * X + + Let us now solve the eigenvalue problem for the matrix `A` first + without preconditioning requesting 80 iterations + + >>> eigenvalues, _ = lobpcg(A_matmat, X, Y=Y, largest=False, maxiter=80) + >>> eigenvalues + array([4., 5., 6.]) + >>> eigenvalues.dtype + dtype('float64') + + With preconditioning we need only 20 iterations from the same `X` + + >>> eigenvalues, _ = lobpcg(A_matmat, X, Y=Y, M=M, largest=False, maxiter=20) + >>> eigenvalues + array([4., 5., 6.]) + + Note that the vectors passed in `Y` are the eigenvectors of the 3 + smallest eigenvalues. The results returned above are orthogonal to those. + + The primary matrix `A` may be indefinite, e.g., after shifting + ``vals`` by 50 from 1, ..., 100 to -49, ..., 50, we still can compute + the 3 smallest or largest eigenvalues. + + >>> vals = vals - 50 + >>> X = rng.normal(size=(n, k)) + >>> eigenvalues, _ = lobpcg(A_matmat, X, largest=False, maxiter=99) + >>> eigenvalues + array([-49., -48., -47.]) + >>> eigenvalues, _ = lobpcg(A_matmat, X, largest=True, maxiter=99) + >>> eigenvalues + array([50., 49., 48.]) + + """ + blockVectorX = X + bestblockVectorX = blockVectorX + blockVectorY = Y + residualTolerance = tol + if maxiter is None: + maxiter = 20 + + bestIterationNumber = maxiter + + sizeY = 0 + if blockVectorY is not None: + if len(blockVectorY.shape) != 2: + warnings.warn( + f"Expected rank-2 array for argument Y, instead got " + f"{len(blockVectorY.shape)}, " + f"so ignore it and use no constraints.", + UserWarning, stacklevel=2 + ) + blockVectorY = None + else: + sizeY = blockVectorY.shape[1] + + # Block size. + if blockVectorX is None: + raise ValueError("The mandatory initial matrix X cannot be None") + if len(blockVectorX.shape) != 2: + raise ValueError("expected rank-2 array for argument X") + + n, sizeX = blockVectorX.shape + + # Data type of iterates, determined by X, must be inexact + if not np.issubdtype(blockVectorX.dtype, np.inexact): + warnings.warn( + f"Data type for argument X is {blockVectorX.dtype}, " + f"which is not inexact, so casted to np.float32.", + UserWarning, stacklevel=2 + ) + blockVectorX = np.asarray(blockVectorX, dtype=np.float32) + + if retLambdaHistory: + lambdaHistory = np.zeros((maxiter + 3, sizeX), + dtype=blockVectorX.dtype) + if retResidualNormsHistory: + residualNormsHistory = np.zeros((maxiter + 3, sizeX), + dtype=blockVectorX.dtype) + + if verbosityLevel: + aux = "Solving " + if B is None: + aux += "standard" + else: + aux += "generalized" + aux += " eigenvalue problem with" + if M is None: + aux += "out" + aux += " preconditioning\n\n" + aux += "matrix size %d\n" % n + aux += "block size %d\n\n" % sizeX + if blockVectorY is None: + aux += "No constraints\n\n" + else: + if sizeY > 1: + aux += "%d constraints\n\n" % sizeY + else: + aux += "%d constraint\n\n" % sizeY + print(aux) + + if (n - sizeY) < (5 * sizeX): + warnings.warn( + f"The problem size {n} minus the constraints size {sizeY} " + f"is too small relative to the block size {sizeX}. " + f"Using a dense eigensolver instead of LOBPCG iterations." + f"No output of the history of the iterations.", + UserWarning, stacklevel=2 + ) + + sizeX = min(sizeX, n) + + if blockVectorY is not None: + raise NotImplementedError( + "The dense eigensolver does not support constraints." + ) + + # Define the closed range of indices of eigenvalues to return. + if largest: + eigvals = (n - sizeX, n - 1) + else: + eigvals = (0, sizeX - 1) + + try: + if isinstance(A, LinearOperator): + A = A(np.eye(n, dtype=int)) + elif callable(A): + A = A(np.eye(n, dtype=int)) + if A.shape != (n, n): + raise ValueError( + f"The shape {A.shape} of the primary matrix\n" + f"defined by a callable object is wrong.\n" + ) + elif issparse(A): + A = A.toarray() + else: + A = np.asarray(A) + except Exception as e: + raise Exception( + f"Primary MatMul call failed with error\n" + f"{e}\n") + + if B is not None: + try: + if isinstance(B, LinearOperator): + B = B(np.eye(n, dtype=int)) + elif callable(B): + B = B(np.eye(n, dtype=int)) + if B.shape != (n, n): + raise ValueError( + f"The shape {B.shape} of the secondary matrix\n" + f"defined by a callable object is wrong.\n" + ) + elif issparse(B): + B = B.toarray() + else: + B = np.asarray(B) + except Exception as e: + raise Exception( + f"Secondary MatMul call failed with error\n" + f"{e}\n") + + try: + vals, vecs = eigh(A, + B, + subset_by_index=eigvals, + check_finite=False) + if largest: + # Reverse order to be compatible with eigs() in 'LM' mode. + vals = vals[::-1] + vecs = vecs[:, ::-1] + + return vals, vecs + except Exception as e: + raise Exception( + f"Dense eigensolver failed with error\n" + f"{e}\n" + ) + + if (residualTolerance is None) or (residualTolerance <= 0.0): + residualTolerance = np.sqrt(np.finfo(blockVectorX.dtype).eps) * n + + A = _makeMatMat(A) + B = _makeMatMat(B) + M = _makeMatMat(M) + + # Apply constraints to X. + if blockVectorY is not None: + + if B is not None: + blockVectorBY = B(blockVectorY) + if blockVectorBY.shape != blockVectorY.shape: + raise ValueError( + f"The shape {blockVectorY.shape} " + f"of the constraint not preserved\n" + f"and changed to {blockVectorBY.shape} " + f"after multiplying by the secondary matrix.\n" + ) + else: + blockVectorBY = blockVectorY + + # gramYBY is a dense array. + gramYBY = blockVectorY.T.conj() @ blockVectorBY + try: + # gramYBY is a Cholesky factor from now on... + gramYBY = cho_factor(gramYBY, overwrite_a=True) + except LinAlgError as e: + raise ValueError("Linearly dependent constraints") from e + + _applyConstraints(blockVectorX, gramYBY, blockVectorBY, blockVectorY) + + ## + # B-orthonormalize X. + blockVectorX, blockVectorBX, _ = _b_orthonormalize( + B, blockVectorX, verbosityLevel=verbosityLevel) + if blockVectorX is None: + raise ValueError("Linearly dependent initial approximations") + + ## + # Compute the initial Ritz vectors: solve the eigenproblem. + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the initial approximations not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + + gramXAX = blockVectorX.T.conj() @ blockVectorAX + + _lambda, eigBlockVector = eigh(gramXAX, check_finite=False) + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + if retLambdaHistory: + lambdaHistory[0, :] = _lambda + + eigBlockVector = np.asarray(eigBlockVector[:, ii]) + blockVectorX = _matmul_inplace( + blockVectorX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + blockVectorAX = _matmul_inplace( + blockVectorAX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + if B is not None: + blockVectorBX = _matmul_inplace( + blockVectorBX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + + ## + # Active index set. + activeMask = np.ones((sizeX,), dtype=bool) + + ## + # Main iteration loop. + + blockVectorP = None # set during iteration + blockVectorAP = None + blockVectorBP = None + + smallestResidualNorm = np.abs(np.finfo(blockVectorX.dtype).max) + + iterationNumber = -1 + restart = True + forcedRestart = False + explicitGramFlag = False + while iterationNumber < maxiter: + iterationNumber += 1 + + if B is not None: + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + if retResidualNormsHistory: + residualNormsHistory[iterationNumber, :] = residualNorms + residualNorm = np.sum(np.abs(residualNorms)) / sizeX + + if residualNorm < smallestResidualNorm: + smallestResidualNorm = residualNorm + bestIterationNumber = iterationNumber + bestblockVectorX = blockVectorX + elif residualNorm > 2**restartControl * smallestResidualNorm: + forcedRestart = True + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the restarted iterate not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + if B is not None: + blockVectorBX = B(blockVectorX) + if blockVectorBX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the restarted iterate not preserved\n" + f"and changed to {blockVectorBX.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + ii = np.where(residualNorms > residualTolerance, True, False) + activeMask = activeMask & ii + currentBlockSize = activeMask.sum() + + if verbosityLevel: + print(f"iteration {iterationNumber}") + print(f"current block size: {currentBlockSize}") + print(f"eigenvalue(s):\n{_lambda}") + print(f"residual norm(s):\n{residualNorms}") + + if currentBlockSize == 0: + break + + activeBlockVectorR = _as2d(blockVectorR[:, activeMask]) + + if iterationNumber > 0: + activeBlockVectorP = _as2d(blockVectorP[:, activeMask]) + activeBlockVectorAP = _as2d(blockVectorAP[:, activeMask]) + if B is not None: + activeBlockVectorBP = _as2d(blockVectorBP[:, activeMask]) + + if M is not None: + # Apply preconditioner T to the active residuals. + activeBlockVectorR = M(activeBlockVectorR) + + ## + # Apply constraints to the preconditioned residuals. + if blockVectorY is not None: + _applyConstraints(activeBlockVectorR, + gramYBY, + blockVectorBY, + blockVectorY) + + ## + # B-orthogonalize the preconditioned residuals to X. + if B is not None: + activeBlockVectorR = activeBlockVectorR - ( + blockVectorX @ + (blockVectorBX.T.conj() @ activeBlockVectorR) + ) + else: + activeBlockVectorR = activeBlockVectorR - ( + blockVectorX @ + (blockVectorX.T.conj() @ activeBlockVectorR) + ) + + ## + # B-orthonormalize the preconditioned residuals. + aux = _b_orthonormalize( + B, activeBlockVectorR, verbosityLevel=verbosityLevel) + activeBlockVectorR, activeBlockVectorBR, _ = aux + + if activeBlockVectorR is None: + warnings.warn( + f"Failed at iteration {iterationNumber} with accuracies " + f"{residualNorms}\n not reaching the requested " + f"tolerance {residualTolerance}.", + UserWarning, stacklevel=2 + ) + break + activeBlockVectorAR = A(activeBlockVectorR) + + if iterationNumber > 0: + if B is not None: + aux = _b_orthonormalize( + B, activeBlockVectorP, activeBlockVectorBP, + verbosityLevel=verbosityLevel + ) + activeBlockVectorP, activeBlockVectorBP, invR = aux + else: + aux = _b_orthonormalize(B, activeBlockVectorP, + verbosityLevel=verbosityLevel) + activeBlockVectorP, _, invR = aux + # Function _b_orthonormalize returns None if Cholesky fails + if activeBlockVectorP is not None: + activeBlockVectorAP = _matmul_inplace( + activeBlockVectorAP, invR, + verbosityLevel=verbosityLevel + ) + restart = forcedRestart + else: + restart = True + + ## + # Perform the Rayleigh Ritz Procedure: + # Compute symmetric Gram matrices: + + if activeBlockVectorAR.dtype == "float32": + myeps = 1 + else: + myeps = np.sqrt(np.finfo(activeBlockVectorR.dtype).eps) + + if residualNorms.max() > myeps and not explicitGramFlag: + explicitGramFlag = False + else: + # Once explicitGramFlag, forever explicitGramFlag. + explicitGramFlag = True + + # Shared memory assignments to simplify the code + if B is None: + blockVectorBX = blockVectorX + activeBlockVectorBR = activeBlockVectorR + if not restart: + activeBlockVectorBP = activeBlockVectorP + + # Common submatrices: + gramXAR = np.dot(blockVectorX.T.conj(), activeBlockVectorAR) + gramRAR = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorAR) + + gramDtype = activeBlockVectorAR.dtype + if explicitGramFlag: + gramRAR = (gramRAR + gramRAR.T.conj()) / 2 + gramXAX = np.dot(blockVectorX.T.conj(), blockVectorAX) + gramXAX = (gramXAX + gramXAX.T.conj()) / 2 + gramXBX = np.dot(blockVectorX.T.conj(), blockVectorBX) + gramRBR = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorBR) + gramXBR = np.dot(blockVectorX.T.conj(), activeBlockVectorBR) + else: + gramXAX = np.diag(_lambda).astype(gramDtype) + gramXBX = np.eye(sizeX, dtype=gramDtype) + gramRBR = np.eye(currentBlockSize, dtype=gramDtype) + gramXBR = np.zeros((sizeX, currentBlockSize), dtype=gramDtype) + + if not restart: + gramXAP = np.dot(blockVectorX.T.conj(), activeBlockVectorAP) + gramRAP = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorAP) + gramPAP = np.dot(activeBlockVectorP.T.conj(), activeBlockVectorAP) + gramXBP = np.dot(blockVectorX.T.conj(), activeBlockVectorBP) + gramRBP = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorBP) + if explicitGramFlag: + gramPAP = (gramPAP + gramPAP.T.conj()) / 2 + gramPBP = np.dot(activeBlockVectorP.T.conj(), + activeBlockVectorBP) + else: + gramPBP = np.eye(currentBlockSize, dtype=gramDtype) + + gramA = np.block( + [ + [gramXAX, gramXAR, gramXAP], + [gramXAR.T.conj(), gramRAR, gramRAP], + [gramXAP.T.conj(), gramRAP.T.conj(), gramPAP], + ] + ) + gramB = np.block( + [ + [gramXBX, gramXBR, gramXBP], + [gramXBR.T.conj(), gramRBR, gramRBP], + [gramXBP.T.conj(), gramRBP.T.conj(), gramPBP], + ] + ) + + _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel) + + try: + _lambda, eigBlockVector = eigh(gramA, + gramB, + check_finite=False) + except LinAlgError as e: + # raise ValueError("eigh failed in lobpcg iterations") from e + if verbosityLevel: + warnings.warn( + f"eigh failed at iteration {iterationNumber} \n" + f"with error {e} causing a restart.\n", + UserWarning, stacklevel=2 + ) + # try again after dropping the direction vectors P from RR + restart = True + + if restart: + gramA = np.block([[gramXAX, gramXAR], [gramXAR.T.conj(), gramRAR]]) + gramB = np.block([[gramXBX, gramXBR], [gramXBR.T.conj(), gramRBR]]) + + _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel) + + try: + _lambda, eigBlockVector = eigh(gramA, + gramB, + check_finite=False) + except LinAlgError as e: + # raise ValueError("eigh failed in lobpcg iterations") from e + warnings.warn( + f"eigh failed at iteration {iterationNumber} with error\n" + f"{e}\n", + UserWarning, stacklevel=2 + ) + break + + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + eigBlockVector = eigBlockVector[:, ii] + if retLambdaHistory: + lambdaHistory[iterationNumber + 1, :] = _lambda + + # Compute Ritz vectors. + if B is not None: + if not restart: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX: + sizeX + currentBlockSize] + eigBlockVectorP = eigBlockVector[sizeX + currentBlockSize:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + pp += np.dot(activeBlockVectorP, eigBlockVectorP) + + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + app += np.dot(activeBlockVectorAP, eigBlockVectorP) + + bpp = np.dot(activeBlockVectorBR, eigBlockVectorR) + bpp += np.dot(activeBlockVectorBP, eigBlockVectorP) + else: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + bpp = np.dot(activeBlockVectorBR, eigBlockVectorR) + + blockVectorX = np.dot(blockVectorX, eigBlockVectorX) + pp + blockVectorAX = np.dot(blockVectorAX, eigBlockVectorX) + app + blockVectorBX = np.dot(blockVectorBX, eigBlockVectorX) + bpp + + blockVectorP, blockVectorAP, blockVectorBP = pp, app, bpp + + else: + if not restart: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX: + sizeX + currentBlockSize] + eigBlockVectorP = eigBlockVector[sizeX + currentBlockSize:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + pp += np.dot(activeBlockVectorP, eigBlockVectorP) + + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + app += np.dot(activeBlockVectorAP, eigBlockVectorP) + else: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + + blockVectorX = np.dot(blockVectorX, eigBlockVectorX) + pp + blockVectorAX = np.dot(blockVectorAX, eigBlockVectorX) + app + + blockVectorP, blockVectorAP = pp, app + + if B is not None: + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + # Use old lambda in case of early loop exit. + if retLambdaHistory: + lambdaHistory[iterationNumber + 1, :] = _lambda + if retResidualNormsHistory: + residualNormsHistory[iterationNumber + 1, :] = residualNorms + residualNorm = np.sum(np.abs(residualNorms)) / sizeX + if residualNorm < smallestResidualNorm: + smallestResidualNorm = residualNorm + bestIterationNumber = iterationNumber + 1 + bestblockVectorX = blockVectorX + + if np.max(np.abs(residualNorms)) > residualTolerance: + warnings.warn( + f"Exited at iteration {iterationNumber} with accuracies \n" + f"{residualNorms}\n" + f"not reaching the requested tolerance {residualTolerance}.\n" + f"Use iteration {bestIterationNumber} instead with accuracy \n" + f"{smallestResidualNorm}.\n", + UserWarning, stacklevel=2 + ) + + if verbosityLevel: + print(f"Final iterative eigenvalue(s):\n{_lambda}") + print(f"Final iterative residual norm(s):\n{residualNorms}") + + blockVectorX = bestblockVectorX + # Making eigenvectors "exactly" satisfy the blockVectorY constrains + if blockVectorY is not None: + _applyConstraints(blockVectorX, + gramYBY, + blockVectorBY, + blockVectorY) + + # Making eigenvectors "exactly" othonormalized by final "exact" RR + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the postprocessing iterate not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + gramXAX = np.dot(blockVectorX.T.conj(), blockVectorAX) + + blockVectorBX = blockVectorX + if B is not None: + blockVectorBX = B(blockVectorX) + if blockVectorBX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the postprocessing iterate not preserved\n" + f"and changed to {blockVectorBX.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + gramXBX = np.dot(blockVectorX.T.conj(), blockVectorBX) + _handle_gramA_gramB_verbosity(gramXAX, gramXBX, verbosityLevel) + gramXAX = (gramXAX + gramXAX.T.conj()) / 2 + gramXBX = (gramXBX + gramXBX.T.conj()) / 2 + try: + _lambda, eigBlockVector = eigh(gramXAX, + gramXBX, + check_finite=False) + except LinAlgError as e: + raise ValueError("eigh has failed in lobpcg postprocessing") from e + + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + eigBlockVector = np.asarray(eigBlockVector[:, ii]) + + blockVectorX = np.dot(blockVectorX, eigBlockVector) + blockVectorAX = np.dot(blockVectorAX, eigBlockVector) + + if B is not None: + blockVectorBX = np.dot(blockVectorBX, eigBlockVector) + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + + if retLambdaHistory: + lambdaHistory[bestIterationNumber + 1, :] = _lambda + if retResidualNormsHistory: + residualNormsHistory[bestIterationNumber + 1, :] = residualNorms + + if retLambdaHistory: + lambdaHistory = lambdaHistory[ + : bestIterationNumber + 2, :] + if retResidualNormsHistory: + residualNormsHistory = residualNormsHistory[ + : bestIterationNumber + 2, :] + + if np.max(np.abs(residualNorms)) > residualTolerance: + warnings.warn( + f"Exited postprocessing with accuracies \n" + f"{residualNorms}\n" + f"not reaching the requested tolerance {residualTolerance}.", + UserWarning, stacklevel=2 + ) + + if verbosityLevel: + print(f"Final postprocessing eigenvalue(s):\n{_lambda}") + print(f"Final residual norm(s):\n{residualNorms}") + + if retLambdaHistory: + lambdaHistory = np.vsplit(lambdaHistory, np.shape(lambdaHistory)[0]) + lambdaHistory = [np.squeeze(i) for i in lambdaHistory] + if retResidualNormsHistory: + residualNormsHistory = np.vsplit(residualNormsHistory, + np.shape(residualNormsHistory)[0]) + residualNormsHistory = [np.squeeze(i) for i in residualNormsHistory] + + if retLambdaHistory: + if retResidualNormsHistory: + return _lambda, blockVectorX, lambdaHistory, residualNormsHistory + else: + return _lambda, blockVectorX, lambdaHistory + else: + if retResidualNormsHistory: + return _lambda, blockVectorX, residualNormsHistory + else: + return _lambda, blockVectorX diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py new file mode 100644 index 0000000000000000000000000000000000000000..4b060d77d1f7531b50b86e79a9132ef2ef7d506d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py @@ -0,0 +1,645 @@ +""" Test functions for the sparse.linalg._eigen.lobpcg module +""" + +import itertools +import platform +import sys +import pytest +import numpy as np +from numpy import ones, r_, diag +from numpy.testing import (assert_almost_equal, assert_equal, + assert_allclose, assert_array_less) + +from scipy import sparse +from scipy.linalg import eig, eigh, toeplitz, orth +from scipy.sparse import spdiags, diags, eye, csr_matrix +from scipy.sparse.linalg import eigs, LinearOperator +from scipy.sparse.linalg._eigen.lobpcg import lobpcg +from scipy.sparse.linalg._eigen.lobpcg.lobpcg import _b_orthonormalize +from scipy._lib._util import np_long, np_ulong + +_IS_32BIT = (sys.maxsize < 2**32) + +INT_DTYPES = {np.intc, np_long, np.longlong, np.uintc, np_ulong, np.ulonglong} +# np.half is unsupported on many test systems so excluded +REAL_DTYPES = {np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +VDTYPES = sorted(REAL_DTYPES ^ COMPLEX_DTYPES, key=str) +MDTYPES = sorted(INT_DTYPES ^ REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +def sign_align(A, B): + """Align signs of columns of A match those of B: column-wise remove + sign of A by multiplying with its sign then multiply in sign of B. + """ + return np.array([col_A * np.sign(col_A[0]) * np.sign(col_B[0]) + for col_A, col_B in zip(A.T, B.T)]).T + +def ElasticRod(n): + """Build the matrices for the generalized eigenvalue problem of the + fixed-free elastic rod vibration model. + """ + L = 1.0 + le = L/n + rho = 7.85e3 + S = 1.e-4 + E = 2.1e11 + mass = rho*S*le/6. + k = E*S/le + A = k*(diag(r_[2.*ones(n-1), 1])-diag(ones(n-1), 1)-diag(ones(n-1), -1)) + B = mass*(diag(r_[4.*ones(n-1), 2])+diag(ones(n-1), 1)+diag(ones(n-1), -1)) + return A, B + + +def MikotaPair(n): + """Build a pair of full diagonal matrices for the generalized eigenvalue + problem. The Mikota pair acts as a nice test since the eigenvalues are the + squares of the integers n, n=1,2,... + """ + x = np.arange(1, n+1) + B = diag(1./x) + y = np.arange(n-1, 0, -1) + z = np.arange(2*n-1, 0, -2) + A = diag(z)-diag(y, -1)-diag(y, 1) + return A, B + + +def compare_solutions(A, B, m): + """Check eig vs. lobpcg consistency. + """ + n = A.shape[0] + rnd = np.random.RandomState(0) + V = rnd.random((n, m)) + X = orth(V) + eigvals, _ = lobpcg(A, X, B=B, tol=1e-2, maxiter=50, largest=False) + eigvals.sort() + w, _ = eig(A, b=B) + w.sort() + assert_almost_equal(w[:int(m/2)], eigvals[:int(m/2)], decimal=2) + + +def test_Small(): + A, B = ElasticRod(10) + with pytest.warns(UserWarning, match="The problem size"): + compare_solutions(A, B, 10) + A, B = MikotaPair(10) + with pytest.warns(UserWarning, match="The problem size"): + compare_solutions(A, B, 10) + + +def test_ElasticRod(): + A, B = ElasticRod(20) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + compare_solutions(A, B, 2) + + +def test_MikotaPair(): + A, B = MikotaPair(20) + compare_solutions(A, B, 2) + + +@pytest.mark.parametrize("n", [50]) +@pytest.mark.parametrize("m", [1, 2, 10]) +@pytest.mark.parametrize("Vdtype", sorted(REAL_DTYPES, key=str)) +@pytest.mark.parametrize("Bdtype", sorted(REAL_DTYPES, key=str)) +@pytest.mark.parametrize("BVdtype", sorted(REAL_DTYPES, key=str)) +def test_b_orthonormalize(n, m, Vdtype, Bdtype, BVdtype): + """Test B-orthonormalization by Cholesky with callable 'B'. + The function '_b_orthonormalize' is key in LOBPCG but may + lead to numerical instabilities. The input vectors are often + badly scaled, so the function needs scale-invariant Cholesky; + see https://netlib.org/lapack/lawnspdf/lawn14.pdf. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((n, m)).astype(Vdtype) + Xcopy = np.copy(X) + vals = np.arange(1, n+1, dtype=float) + B = diags([vals], [0], (n, n)).astype(Bdtype) + BX = B @ X + BX = BX.astype(BVdtype) + dtype = min(X.dtype, B.dtype, BX.dtype) + # np.longdouble tol cannot be achieved on most systems + atol = m * n * max(np.finfo(dtype).eps, np.finfo(np.float64).eps) + + Xo, BXo, _ = _b_orthonormalize(lambda v: B @ v, X, BX) + # Check in-place. + assert_equal(X, Xo) + assert_equal(id(X), id(Xo)) + assert_equal(BX, BXo) + assert_equal(id(BX), id(BXo)) + # Check BXo. + assert_allclose(B @ Xo, BXo, atol=atol, rtol=atol) + # Check B-orthonormality + assert_allclose(Xo.T.conj() @ B @ Xo, np.identity(m), + atol=atol, rtol=atol) + # Repeat without BX in outputs + X = np.copy(Xcopy) + Xo1, BXo1, _ = _b_orthonormalize(lambda v: B @ v, X) + assert_allclose(Xo, Xo1, atol=atol, rtol=atol) + assert_allclose(BXo, BXo1, atol=atol, rtol=atol) + # Check in-place. + assert_equal(X, Xo1) + assert_equal(id(X), id(Xo1)) + # Check BXo1. + assert_allclose(B @ Xo1, BXo1, atol=atol, rtol=atol) + + # Introduce column-scaling in X. + scaling = 1.0 / np.geomspace(10, 1e10, num=m) + X = Xcopy * scaling + X = X.astype(Vdtype) + BX = B @ X + BX = BX.astype(BVdtype) + # Check scaling-invariance of Cholesky-based orthonormalization + Xo1, BXo1, _ = _b_orthonormalize(lambda v: B @ v, X, BX) + # The output should be the same, up the signs of the columns. + Xo1 = sign_align(Xo1, Xo) + assert_allclose(Xo, Xo1, atol=atol, rtol=atol) + BXo1 = sign_align(BXo1, BXo) + assert_allclose(BXo, BXo1, atol=atol, rtol=atol) + + +@pytest.mark.filterwarnings("ignore:Exited at iteration 0") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_nonhermitian_warning(capsys): + """Check the warning of a Ritz matrix being not Hermitian + by feeding a non-Hermitian input matrix. + Also check stdout since verbosityLevel=1 and lack of stderr. + """ + n = 10 + X = np.arange(n * 2).reshape(n, 2).astype(np.float32) + A = np.arange(n * n).reshape(n, n).astype(np.float32) + with pytest.warns(UserWarning, match="Matrix gramA"): + _, _ = lobpcg(A, X, verbosityLevel=1, maxiter=0) + out, err = capsys.readouterr() # Capture output + assert out.startswith("Solving standard eigenvalue") # Test stdout + assert err == '' # Test empty stderr + # Make the matrix symmetric and the UserWarning disappears. + A += A.T + _, _ = lobpcg(A, X, verbosityLevel=1, maxiter=0) + out, err = capsys.readouterr() # Capture output + assert out.startswith("Solving standard eigenvalue") # Test stdout + assert err == '' # Test empty stderr + + +def test_regression(): + """Check the eigenvalue of the identity matrix is one. + """ + # https://mail.python.org/pipermail/scipy-user/2010-October/026944.html + n = 10 + X = np.ones((n, 1)) + A = np.identity(n) + w, _ = lobpcg(A, X) + assert_allclose(w, [1]) + + +@pytest.mark.filterwarnings("ignore:The problem size") +@pytest.mark.parametrize('n, m, m_excluded', [(30, 4, 3), (4, 2, 0)]) +def test_diagonal(n, m, m_excluded): + """Test ``m - m_excluded`` eigenvalues and eigenvectors of + diagonal matrices of the size ``n`` varying matrix formats: + dense array, spare matrix, and ``LinearOperator`` for both + matrixes in the generalized eigenvalue problem ``Av = cBv`` + and for the preconditioner. + """ + rnd = np.random.RandomState(0) + + # Define the generalized eigenvalue problem Av = cBv + # where (c, v) is a generalized eigenpair, + # A is the diagonal matrix whose entries are 1,...n, + # B is the identity matrix. + vals = np.arange(1, n+1, dtype=float) + A_s = diags([vals], [0], (n, n)) + A_a = A_s.toarray() + + def A_f(x): + return A_s @ x + + A_lo = LinearOperator(matvec=A_f, + matmat=A_f, + shape=(n, n), dtype=float) + + B_a = eye(n) + B_s = csr_matrix(B_a) + + def B_f(x): + return B_a @ x + + B_lo = LinearOperator(matvec=B_f, + matmat=B_f, + shape=(n, n), dtype=float) + + # Let the preconditioner M be the inverse of A. + M_s = diags([1./vals], [0], (n, n)) + M_a = M_s.toarray() + + def M_f(x): + return M_s @ x + + M_lo = LinearOperator(matvec=M_f, + matmat=M_f, + shape=(n, n), dtype=float) + + # Pick random initial vectors. + X = rnd.normal(size=(n, m)) + + # Require that the returned eigenvectors be in the orthogonal complement + # of the first few standard basis vectors. + if m_excluded > 0: + Y = np.eye(n, m_excluded) + else: + Y = None + + for A in [A_a, A_s, A_lo]: + for B in [B_a, B_s, B_lo]: + for M in [M_a, M_s, M_lo]: + eigvals, vecs = lobpcg(A, X, B, M=M, Y=Y, + maxiter=40, largest=False) + + assert_allclose(eigvals, np.arange(1+m_excluded, + 1+m_excluded+m)) + _check_eigen(A, eigvals, vecs, rtol=1e-3, atol=1e-3) + + +def _check_eigen(M, w, V, rtol=1e-8, atol=1e-14): + """Check if the eigenvalue residual is small. + """ + mult_wV = np.multiply(w, V) + dot_MV = M.dot(V) + assert_allclose(mult_wV, dot_MV, rtol=rtol, atol=atol) + + +def _check_fiedler(n, p): + """Check the Fiedler vector computation. + """ + # This is not necessarily the recommended way to find the Fiedler vector. + col = np.zeros(n) + col[1] = 1 + A = toeplitz(col) + D = np.diag(A.sum(axis=1)) + L = D - A + # Compute the full eigendecomposition using tricks, e.g. + # http://www.cs.yale.edu/homes/spielman/561/2009/lect02-09.pdf + tmp = np.pi * np.arange(n) / n + analytic_w = 2 * (1 - np.cos(tmp)) + analytic_V = np.cos(np.outer(np.arange(n) + 1/2, tmp)) + _check_eigen(L, analytic_w, analytic_V) + # Compute the full eigendecomposition using eigh. + eigh_w, eigh_V = eigh(L) + _check_eigen(L, eigh_w, eigh_V) + # Check that the first eigenvalue is near zero and that the rest agree. + assert_array_less(np.abs([eigh_w[0], analytic_w[0]]), 1e-14) + assert_allclose(eigh_w[1:], analytic_w[1:]) + + # Check small lobpcg eigenvalues. + X = analytic_V[:, :p] + lobpcg_w, lobpcg_V = lobpcg(L, X, largest=False) + assert_equal(lobpcg_w.shape, (p,)) + assert_equal(lobpcg_V.shape, (n, p)) + _check_eigen(L, lobpcg_w, lobpcg_V) + assert_array_less(np.abs(np.min(lobpcg_w)), 1e-14) + assert_allclose(np.sort(lobpcg_w)[1:], analytic_w[1:p]) + + # Check large lobpcg eigenvalues. + X = analytic_V[:, -p:] + lobpcg_w, lobpcg_V = lobpcg(L, X, largest=True) + assert_equal(lobpcg_w.shape, (p,)) + assert_equal(lobpcg_V.shape, (n, p)) + _check_eigen(L, lobpcg_w, lobpcg_V) + assert_allclose(np.sort(lobpcg_w), analytic_w[-p:]) + + # Look for the Fiedler vector using good but not exactly correct guesses. + fiedler_guess = np.concatenate((np.ones(n//2), -np.ones(n-n//2))) + X = np.vstack((np.ones(n), fiedler_guess)).T + lobpcg_w, _ = lobpcg(L, X, largest=False) + # Mathematically, the smaller eigenvalue should be zero + # and the larger should be the algebraic connectivity. + lobpcg_w = np.sort(lobpcg_w) + assert_allclose(lobpcg_w, analytic_w[:2], atol=1e-14) + + +def test_fiedler_small_8(): + """Check the dense workaround path for small matrices. + """ + # This triggers the dense path because 8 < 2*5. + with pytest.warns(UserWarning, match="The problem size"): + _check_fiedler(8, 2) + + +def test_fiedler_large_12(): + """Check the dense workaround path avoided for non-small matrices. + """ + # This does not trigger the dense path, because 2*5 <= 12. + _check_fiedler(12, 2) + + +@pytest.mark.filterwarnings("ignore:Failed at iteration") +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_failure_to_run_iterations(): + """Check that the code exits gracefully without breaking. Issue #10974. + The code may or not issue a warning, filtered out. Issue #15935, #17954. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((100, 10)) + A = X @ X.T + Q = rnd.standard_normal((X.shape[0], 4)) + eigenvalues, _ = lobpcg(A, Q, maxiter=40, tol=1e-12) + assert np.max(eigenvalues) > 0 + + +def test_failure_to_run_iterations_nonsymmetric(): + """Check that the code exists gracefully without breaking + if the matrix in not symmetric. + """ + A = np.zeros((10, 10)) + A[0, 1] = 1 + Q = np.ones((10, 1)) + msg = "Exited at iteration 2|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + eigenvalues, _ = lobpcg(A, Q, maxiter=20) + assert np.max(eigenvalues) > 0 + + +@pytest.mark.filterwarnings("ignore:The problem size") +def test_hermitian(): + """Check complex-value Hermitian cases. + """ + rnd = np.random.RandomState(0) + + sizes = [3, 12] + ks = [1, 2] + gens = [True, False] + + for s, k, gen, dh, dx, db in ( + itertools.product(sizes, ks, gens, gens, gens, gens) + ): + H = rnd.random((s, s)) + 1.j * rnd.random((s, s)) + H = 10 * np.eye(s) + H + H.T.conj() + H = H.astype(np.complex128) if dh else H.astype(np.complex64) + + X = rnd.standard_normal((s, k)) + X = X + 1.j * rnd.standard_normal((s, k)) + X = X.astype(np.complex128) if dx else X.astype(np.complex64) + + if not gen: + B = np.eye(s) + w, v = lobpcg(H, X, maxiter=99, verbosityLevel=0) + # Also test mixing complex H with real B. + wb, _ = lobpcg(H, X, B, maxiter=99, verbosityLevel=0) + assert_allclose(w, wb, rtol=1e-6) + w0, _ = eigh(H) + else: + B = rnd.random((s, s)) + 1.j * rnd.random((s, s)) + B = 10 * np.eye(s) + B.dot(B.T.conj()) + B = B.astype(np.complex128) if db else B.astype(np.complex64) + w, v = lobpcg(H, X, B, maxiter=99, verbosityLevel=0) + w0, _ = eigh(H, B) + + for wx, vx in zip(w, v.T): + # Check eigenvector + assert_allclose(np.linalg.norm(H.dot(vx) - B.dot(vx) * wx) + / np.linalg.norm(H.dot(vx)), + 0, atol=5e-2, rtol=0) + + # Compare eigenvalues + j = np.argmin(abs(w0 - wx)) + assert_allclose(wx, w0[j], rtol=1e-4) + + +# The n=5 case tests the alternative small matrix code path that uses eigh(). +@pytest.mark.filterwarnings("ignore:The problem size") +@pytest.mark.parametrize('n, atol', [(20, 1e-3), (5, 1e-8)]) +def test_eigs_consistency(n, atol): + """Check eigs vs. lobpcg consistency. + """ + vals = np.arange(1, n+1, dtype=np.float64) + A = spdiags(vals, 0, n, n) + rnd = np.random.RandomState(0) + X = rnd.standard_normal((n, 2)) + lvals, lvecs = lobpcg(A, X, largest=True, maxiter=100) + vals, _ = eigs(A, k=2) + + _check_eigen(A, lvals, lvecs, atol=atol, rtol=0) + assert_allclose(np.sort(vals), np.sort(lvals), atol=1e-14) + + +def test_verbosity(): + """Check that nonzero verbosity level code runs. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((10, 10)) + A = X @ X.T + Q = rnd.standard_normal((X.shape[0], 1)) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + _, _ = lobpcg(A, Q, maxiter=3, verbosityLevel=9) + + +@pytest.mark.xfail(_IS_32BIT and sys.platform == 'win32', + reason="tolerance violation on windows") +@pytest.mark.xfail(platform.machine() == 'ppc64le', + reason="fails on ppc64le") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_tolerance_float32(): + """Check lobpcg for attainable tolerance in float32. + """ + rnd = np.random.RandomState(0) + n = 50 + m = 3 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.float32) + X = rnd.standard_normal((n, m)) + X = X.astype(np.float32) + eigvals, _ = lobpcg(A, X, tol=1.25e-5, maxiter=50, verbosityLevel=0) + assert_allclose(eigvals, -np.arange(1, 1 + m), atol=2e-5, rtol=1e-5) + + +@pytest.mark.parametrize("vdtype", VDTYPES) +@pytest.mark.parametrize("mdtype", MDTYPES) +@pytest.mark.parametrize("arr_type", [np.array, + sparse.csr_matrix, + sparse.coo_matrix]) +def test_dtypes(vdtype, mdtype, arr_type): + """Test lobpcg in various dtypes. + """ + rnd = np.random.RandomState(0) + n = 12 + m = 2 + A = arr_type(np.diag(np.arange(1, n + 1)).astype(mdtype)) + X = rnd.random((n, m)) + X = X.astype(vdtype) + eigvals, eigvecs = lobpcg(A, X, tol=1e-2, largest=False) + assert_allclose(eigvals, np.arange(1, 1 + m), atol=1e-1) + # eigenvectors must be nearly real in any case + assert_allclose(np.sum(np.abs(eigvecs - eigvecs.conj())), 0, atol=1e-2) + + +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_inplace_warning(): + """Check lobpcg gives a warning in '_b_orthonormalize' + that in-place orthogonalization is impossible due to dtype mismatch. + """ + rnd = np.random.RandomState(0) + n = 6 + m = 1 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.cdouble) + X = rnd.standard_normal((n, m)) + with pytest.warns(UserWarning, match="Inplace update"): + eigvals, _ = lobpcg(A, X, maxiter=2, verbosityLevel=1) + + +def test_maxit(): + """Check lobpcg if maxit=maxiter runs maxiter iterations and + if maxit=None runs 20 iterations (the default) + by checking the size of the iteration history output, which should + be the number of iterations plus 3 (initial, final, and postprocessing) + typically when maxiter is small and the choice of the best is passive. + """ + rnd = np.random.RandomState(0) + n = 50 + m = 4 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.float32) + X = rnd.standard_normal((n, m)) + X = X.astype(np.float64) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + for maxiter in range(1, 4): + with pytest.warns(UserWarning, match=msg): + _, _, l_h, r_h = lobpcg(A, X, tol=1e-8, maxiter=maxiter, + retLambdaHistory=True, + retResidualNormsHistory=True) + assert_allclose(np.shape(l_h)[0], maxiter+3) + assert_allclose(np.shape(r_h)[0], maxiter+3) + with pytest.warns(UserWarning, match=msg): + l, _, l_h, r_h = lobpcg(A, X, tol=1e-8, + retLambdaHistory=True, + retResidualNormsHistory=True) + assert_allclose(np.shape(l_h)[0], 20+3) + assert_allclose(np.shape(r_h)[0], 20+3) + # Check that eigenvalue output is the last one in history + assert_allclose(l, l_h[-1]) + # Make sure that both history outputs are lists + assert isinstance(l_h, list) + assert isinstance(r_h, list) + # Make sure that both history lists are arrays-like + assert_allclose(np.shape(l_h), np.shape(np.asarray(l_h))) + assert_allclose(np.shape(r_h), np.shape(np.asarray(r_h))) + + +@pytest.mark.slow +@pytest.mark.parametrize("n", [15]) +@pytest.mark.parametrize("m", [1, 2]) +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_diagonal_data_types(n, m): + """Check lobpcg for diagonal matrices for all matrix types. + Constraints are imposed, so a dense eigensolver eig cannot run. + """ + rnd = np.random.RandomState(0) + # Define the generalized eigenvalue problem Av = cBv + # where (c, v) is a generalized eigenpair, + # and where we choose A and B to be diagonal. + vals = np.arange(1, n + 1) + + # list_sparse_format = ['bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'] + list_sparse_format = ['coo'] + sparse_formats = len(list_sparse_format) + for s_f_i, s_f in enumerate(list_sparse_format): + + As64 = diags([vals * vals], [0], (n, n), format=s_f) + As32 = As64.astype(np.float32) + Af64 = As64.toarray() + Af32 = Af64.astype(np.float32) + + def As32f(x): + return As32 @ x + As32LO = LinearOperator(matvec=As32f, + matmat=As32f, + shape=(n, n), + dtype=As32.dtype) + + listA = [Af64, As64, Af32, As32, As32f, As32LO, lambda v: As32 @ v] + + Bs64 = diags([vals], [0], (n, n), format=s_f) + Bf64 = Bs64.toarray() + Bs32 = Bs64.astype(np.float32) + + def Bs32f(x): + return Bs32 @ x + Bs32LO = LinearOperator(matvec=Bs32f, + matmat=Bs32f, + shape=(n, n), + dtype=Bs32.dtype) + listB = [Bf64, Bs64, Bs32, Bs32f, Bs32LO, lambda v: Bs32 @ v] + + # Define the preconditioner function as LinearOperator. + Ms64 = diags([1./vals], [0], (n, n), format=s_f) + + def Ms64precond(x): + return Ms64 @ x + Ms64precondLO = LinearOperator(matvec=Ms64precond, + matmat=Ms64precond, + shape=(n, n), + dtype=Ms64.dtype) + Mf64 = Ms64.toarray() + + def Mf64precond(x): + return Mf64 @ x + Mf64precondLO = LinearOperator(matvec=Mf64precond, + matmat=Mf64precond, + shape=(n, n), + dtype=Mf64.dtype) + Ms32 = Ms64.astype(np.float32) + + def Ms32precond(x): + return Ms32 @ x + Ms32precondLO = LinearOperator(matvec=Ms32precond, + matmat=Ms32precond, + shape=(n, n), + dtype=Ms32.dtype) + Mf32 = Ms32.toarray() + + def Mf32precond(x): + return Mf32 @ x + Mf32precondLO = LinearOperator(matvec=Mf32precond, + matmat=Mf32precond, + shape=(n, n), + dtype=Mf32.dtype) + listM = [None, Ms64, Ms64precondLO, Mf64precondLO, Ms64precond, + Ms32, Ms32precondLO, Mf32precondLO, Ms32precond] + + # Setup matrix of the initial approximation to the eigenvectors + # (cannot be sparse array). + Xf64 = rnd.random((n, m)) + Xf32 = Xf64.astype(np.float32) + listX = [Xf64, Xf32] + + # Require that the returned eigenvectors be in the orthogonal complement + # of the first few standard basis vectors (cannot be sparse array). + m_excluded = 3 + Yf64 = np.eye(n, m_excluded, dtype=float) + Yf32 = np.eye(n, m_excluded, dtype=np.float32) + listY = [Yf64, Yf32] + + tests = list(itertools.product(listA, listB, listM, listX, listY)) + # This is one of the slower tests because there are >1,000 configs + # to test here, instead of checking product of all input, output types + # test each configuration for the first sparse format, and then + # for one additional sparse format. this takes 2/7=30% as long as + # testing all configurations for all sparse formats. + if s_f_i > 0: + tests = tests[s_f_i - 1::sparse_formats-1] + + for A, B, M, X, Y in tests: + eigvals, _ = lobpcg(A, X, B=B, M=M, Y=Y, tol=1e-4, + maxiter=100, largest=False) + assert_allclose(eigvals, + np.arange(1 + m_excluded, 1 + m_excluded + m), + atol=1e-5) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py new file mode 100644 index 0000000000000000000000000000000000000000..587d0eb6ede989935ab1d210e8b142613565401f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py @@ -0,0 +1,862 @@ +import re +import copy +import numpy as np + +from numpy.testing import assert_allclose, assert_equal, assert_array_equal +import pytest + +from scipy.linalg import svd, null_space +from scipy.sparse import csc_matrix, issparse, spdiags, random +from scipy.sparse.linalg import LinearOperator, aslinearoperator +from scipy.sparse.linalg import svds +from scipy.sparse.linalg._eigen.arpack import ArpackNoConvergence + + +# --- Helper Functions / Classes --- + + +def sorted_svd(m, k, which='LM'): + # Compute svd of a dense matrix m, and return singular vectors/values + # sorted. + if issparse(m): + m = m.toarray() + u, s, vh = svd(m) + if which == 'LM': + ii = np.argsort(s)[-k:] + elif which == 'SM': + ii = np.argsort(s)[:k] + else: + raise ValueError(f"unknown which={which!r}") + + return u[:, ii], s[ii], vh[ii] + + +def _check_svds(A, k, u, s, vh, which="LM", check_usvh_A=False, + check_svd=True, atol=1e-10, rtol=1e-7): + n, m = A.shape + + # Check shapes. + assert_equal(u.shape, (n, k)) + assert_equal(s.shape, (k,)) + assert_equal(vh.shape, (k, m)) + + # Check that the original matrix can be reconstituted. + A_rebuilt = (u*s).dot(vh) + assert_equal(A_rebuilt.shape, A.shape) + if check_usvh_A: + assert_allclose(A_rebuilt, A, atol=atol, rtol=rtol) + + # Check that u is a semi-orthogonal matrix. + uh_u = np.dot(u.T.conj(), u) + assert_equal(uh_u.shape, (k, k)) + assert_allclose(uh_u, np.identity(k), atol=atol, rtol=rtol) + + # Check that vh is a semi-orthogonal matrix. + vh_v = np.dot(vh, vh.T.conj()) + assert_equal(vh_v.shape, (k, k)) + assert_allclose(vh_v, np.identity(k), atol=atol, rtol=rtol) + + # Check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + if check_svd: + u2, s2, vh2 = sorted_svd(A, k, which) + assert_allclose(np.abs(u), np.abs(u2), atol=atol, rtol=rtol) + assert_allclose(s, s2, atol=atol, rtol=rtol) + assert_allclose(np.abs(vh), np.abs(vh2), atol=atol, rtol=rtol) + + +def _check_svds_n(A, k, u, s, vh, which="LM", check_res=True, + check_svd=True, atol=1e-10, rtol=1e-7): + n, m = A.shape + + # Check shapes. + assert_equal(u.shape, (n, k)) + assert_equal(s.shape, (k,)) + assert_equal(vh.shape, (k, m)) + + # Check that u is a semi-orthogonal matrix. + uh_u = np.dot(u.T.conj(), u) + assert_equal(uh_u.shape, (k, k)) + error = np.sum(np.abs(uh_u - np.identity(k))) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + # Check that vh is a semi-orthogonal matrix. + vh_v = np.dot(vh, vh.T.conj()) + assert_equal(vh_v.shape, (k, k)) + error = np.sum(np.abs(vh_v - np.identity(k))) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + # Check residuals + if check_res: + ru = A.T.conj() @ u - vh.T.conj() * s + rus = np.sum(np.abs(ru)) / (n * k) + rvh = A @ vh.T.conj() - u * s + rvhs = np.sum(np.abs(rvh)) / (m * k) + assert_allclose(rus, 0.0, atol=atol, rtol=rtol) + assert_allclose(rvhs, 0.0, atol=atol, rtol=rtol) + + # Check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + if check_svd: + u2, s2, vh2 = sorted_svd(A, k, which) + assert_allclose(s, s2, atol=atol, rtol=rtol) + A_rebuilt_svd = (u2*s2).dot(vh2) + A_rebuilt = (u*s).dot(vh) + assert_equal(A_rebuilt.shape, A.shape) + error = np.sum(np.abs(A_rebuilt_svd - A_rebuilt)) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + +class CheckingLinearOperator(LinearOperator): + def __init__(self, A): + self.A = A + self.dtype = A.dtype + self.shape = A.shape + + def _matvec(self, x): + assert_equal(max(x.shape), np.size(x)) + return self.A.dot(x) + + def _rmatvec(self, x): + assert_equal(max(x.shape), np.size(x)) + return self.A.T.conjugate().dot(x) + + +# --- Test Input Validation --- +# Tests input validation on parameters `k` and `which`. +# Needs better input validation checks for all other parameters. + +class SVDSCommonTests: + + solver = None + + # some of these IV tests could run only once, say with solver=None + + _A_empty_msg = "`A` must not be empty." + _A_dtype_msg = "`A` must be of floating or complex floating data type" + _A_type_msg = "type not understood" + _A_ndim_msg = "array must have ndim <= 2" + _A_validation_inputs = [ + (np.asarray([[]]), ValueError, _A_empty_msg), + (np.asarray([[1, 2], [3, 4]]), ValueError, _A_dtype_msg), + ("hi", TypeError, _A_type_msg), + (np.asarray([[[1., 2.], [3., 4.]]]), ValueError, _A_ndim_msg)] + + @pytest.mark.parametrize("args", _A_validation_inputs) + def test_svds_input_validation_A(self, args): + A, error_type, message = args + with pytest.raises(error_type, match=message): + svds(A, k=1, solver=self.solver) + + @pytest.mark.parametrize("k", [-1, 0, 3, 4, 5, 1.5, "1"]) + def test_svds_input_validation_k_1(self, k): + rng = np.random.default_rng(0) + A = rng.random((4, 3)) + + # propack can do complete SVD + if self.solver == 'propack' and k == 3: + res = svds(A, k=k, solver=self.solver, random_state=0) + _check_svds(A, k, *res, check_usvh_A=True, check_svd=True) + return + + message = ("`k` must be an integer satisfying") + with pytest.raises(ValueError, match=message): + svds(A, k=k, solver=self.solver) + + def test_svds_input_validation_k_2(self): + # I think the stack trace is reasonable when `k` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), k=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), k="hi", solver=self.solver) + + @pytest.mark.parametrize("tol", (-1, np.inf, np.nan)) + def test_svds_input_validation_tol_1(self, tol): + message = "`tol` must be a non-negative floating point value." + with pytest.raises(ValueError, match=message): + svds(np.eye(10), tol=tol, solver=self.solver) + + @pytest.mark.parametrize("tol", ([], 'hi')) + def test_svds_input_validation_tol_2(self, tol): + # I think the stack trace is reasonable here + message = "'<' not supported between instances" + with pytest.raises(TypeError, match=message): + svds(np.eye(10), tol=tol, solver=self.solver) + + @pytest.mark.parametrize("which", ('LA', 'SA', 'ekki', 0)) + def test_svds_input_validation_which(self, which): + # Regression test for a github issue. + # https://github.com/scipy/scipy/issues/4590 + # Function was not checking for eigenvalue type and unintended + # values could be returned. + with pytest.raises(ValueError, match="`which` must be in"): + svds(np.eye(10), which=which, solver=self.solver) + + @pytest.mark.parametrize("transpose", (True, False)) + @pytest.mark.parametrize("n", range(4, 9)) + def test_svds_input_validation_v0_1(self, transpose, n): + rng = np.random.default_rng(0) + A = rng.random((5, 7)) + v0 = rng.random(n) + if transpose: + A = A.T + k = 2 + message = "`v0` must have shape" + + required_length = (A.shape[0] if self.solver == 'propack' + else min(A.shape)) + if n != required_length: + with pytest.raises(ValueError, match=message): + svds(A, k=k, v0=v0, solver=self.solver) + + def test_svds_input_validation_v0_2(self): + A = np.ones((10, 10)) + v0 = np.ones((1, 10)) + message = "`v0` must have shape" + with pytest.raises(ValueError, match=message): + svds(A, k=1, v0=v0, solver=self.solver) + + @pytest.mark.parametrize("v0", ("hi", 1, np.ones(10, dtype=int))) + def test_svds_input_validation_v0_3(self, v0): + A = np.ones((10, 10)) + message = "`v0` must be of floating or complex floating data type." + with pytest.raises(ValueError, match=message): + svds(A, k=1, v0=v0, solver=self.solver) + + @pytest.mark.parametrize("maxiter", (-1, 0, 5.5)) + def test_svds_input_validation_maxiter_1(self, maxiter): + message = ("`maxiter` must be a positive integer.") + with pytest.raises(ValueError, match=message): + svds(np.eye(10), maxiter=maxiter, solver=self.solver) + + def test_svds_input_validation_maxiter_2(self): + # I think the stack trace is reasonable when `k` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), maxiter=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), maxiter="hi", solver=self.solver) + + @pytest.mark.parametrize("rsv", ('ekki', 10)) + def test_svds_input_validation_return_singular_vectors(self, rsv): + message = "`return_singular_vectors` must be in" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), return_singular_vectors=rsv, solver=self.solver) + + # --- Test Parameters --- + + @pytest.mark.parametrize("k", [3, 5]) + @pytest.mark.parametrize("which", ["LM", "SM"]) + def test_svds_parameter_k_which(self, k, which): + # check that the `k` parameter sets the number of eigenvalues/ + # eigenvectors returned. + # Also check that the `which` parameter sets whether the largest or + # smallest eigenvalues are returned + rng = np.random.default_rng(0) + A = rng.random((10, 10)) + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + res = svds(A, k=k, which=which, solver=self.solver, + random_state=0) + else: + res = svds(A, k=k, which=which, solver=self.solver, + random_state=0) + _check_svds(A, k, *res, which=which, atol=1e-9, rtol=2e-13) + + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + # loop instead of parametrize for simplicity + def test_svds_parameter_tol(self): + # check the effect of the `tol` parameter on solver accuracy by solving + # the same problem with varying `tol` and comparing the eigenvalues + # against ground truth computed + n = 100 # matrix size + k = 3 # number of eigenvalues to check + + # generate a random, sparse-ish matrix + # effect isn't apparent for matrices that are too small + rng = np.random.default_rng(0) + A = rng.random((n, n)) + A[A > .1] = 0 + A = A @ A.T + + _, s, _ = svd(A) # calculate ground truth + + # calculate the error as a function of `tol` + A = csc_matrix(A) + + def err(tol): + _, s2, _ = svds(A, k=k, v0=np.ones(n), maxiter=1000, + solver=self.solver, tol=tol, random_state=0) + return np.linalg.norm((s2 - s[k-1::-1])/s[k-1::-1]) + + tols = [1e-4, 1e-2, 1e0] # tolerance levels to check + # for 'arpack' and 'propack', accuracies make discrete steps + accuracies = {'propack': [1e-12, 1e-6, 1e-4], + 'arpack': [2.5e-15, 1e-10, 1e-10], + 'lobpcg': [2e-12, 4e-2, 2]} + + for tol, accuracy in zip(tols, accuracies[self.solver]): + error = err(tol) + assert error < accuracy + + def test_svd_v0(self): + # check that the `v0` parameter affects the solution + n = 100 + k = 1 + # If k != 1, LOBPCG needs more initial vectors, which are generated + # with random_state, so it does not pass w/ k >= 2. + # For some other values of `n`, the AssertionErrors are not raised + # with different v0s, which is reasonable. + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + # with the same v0, solutions are the same, and they are accurate + # v0 takes precedence over random_state + v0a = rng.random(n) + res1a = svds(A, k, v0=v0a, solver=self.solver, random_state=0) + res2a = svds(A, k, v0=v0a, solver=self.solver, random_state=1) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + # with the same v0, solutions are the same, and they are accurate + v0b = rng.random(n) + res1b = svds(A, k, v0=v0b, solver=self.solver, random_state=2) + res2b = svds(A, k, v0=v0b, solver=self.solver, random_state=3) + for idx in range(3): + assert_allclose(res1b[idx], res2b[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1b) + + # with different v0, solutions can be numerically different + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res1b) + + def test_svd_random_state(self): + # check that the `random_state` parameter affects the solution + # Admittedly, `n` and `k` are chosen so that all solver pass all + # these checks. That's a tall order, since LOBPCG doesn't want to + # achieve the desired accuracy and ARPACK often returns the same + # singular values/vectors for different v0. + n = 100 + k = 1 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + # with the same random_state, solutions are the same and accurate + res1a = svds(A, k, solver=self.solver, random_state=0) + res2a = svds(A, k, solver=self.solver, random_state=0) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + # with the same random_state, solutions are the same and accurate + res1b = svds(A, k, solver=self.solver, random_state=1) + res2b = svds(A, k, solver=self.solver, random_state=1) + for idx in range(3): + assert_allclose(res1b[idx], res2b[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1b) + + # with different random_state, solutions can be numerically different + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res1b) + + @pytest.mark.parametrize("random_state", (0, 1, + np.random.RandomState(0), + np.random.default_rng(0))) + def test_svd_random_state_2(self, random_state): + n = 100 + k = 1 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + random_state_2 = copy.deepcopy(random_state) + + # with the same random_state, solutions are the same and accurate + res1a = svds(A, k, solver=self.solver, random_state=random_state) + res2a = svds(A, k, solver=self.solver, random_state=random_state_2) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + @pytest.mark.parametrize("random_state", (None, + np.random.RandomState(0), + np.random.default_rng(0))) + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + def test_svd_random_state_3(self, random_state): + n = 100 + k = 5 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + random_state = copy.deepcopy(random_state) + + # random_state in different state produces accurate - but not + # not necessarily identical - results + res1a = svds(A, k, solver=self.solver, random_state=random_state, maxiter=1000) + res2a = svds(A, k, solver=self.solver, random_state=random_state, maxiter=1000) + _check_svds(A, k, *res1a, atol=2e-7) + _check_svds(A, k, *res2a, atol=2e-7) + + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res2a) + + @pytest.mark.filterwarnings("ignore:Exited postprocessing") + def test_svd_maxiter(self): + # check that maxiter works as expected: should not return accurate + # solution after 1 iteration, but should with default `maxiter` + A = np.diag(np.arange(9)).astype(np.float64) + k = 1 + u, s, vh = sorted_svd(A, k) + # Use default maxiter by default + maxiter = None + + if self.solver == 'arpack': + message = "ARPACK error -1: No convergence" + with pytest.raises(ArpackNoConvergence, match=message): + svds(A, k, ncv=3, maxiter=1, solver=self.solver) + elif self.solver == 'lobpcg': + # Set maxiter higher so test passes without changing + # default and breaking backward compatibility (gh-20221) + maxiter = 30 + with pytest.warns(UserWarning, match="Exited at iteration"): + svds(A, k, maxiter=1, solver=self.solver) + elif self.solver == 'propack': + message = "k=1 singular triplets did not converge within" + with pytest.raises(np.linalg.LinAlgError, match=message): + svds(A, k, maxiter=1, solver=self.solver) + + ud, sd, vhd = svds(A, k, solver=self.solver, maxiter=maxiter, + random_state=0) + _check_svds(A, k, ud, sd, vhd, atol=1e-8) + assert_allclose(np.abs(ud), np.abs(u), atol=1e-8) + assert_allclose(np.abs(vhd), np.abs(vh), atol=1e-8) + assert_allclose(np.abs(sd), np.abs(s), atol=1e-9) + + @pytest.mark.parametrize("rsv", (True, False, 'u', 'vh')) + @pytest.mark.parametrize("shape", ((5, 7), (6, 6), (7, 5))) + def test_svd_return_singular_vectors(self, rsv, shape): + # check that the return_singular_vectors parameter works as expected + rng = np.random.default_rng(0) + A = rng.random(shape) + k = 2 + M, N = shape + u, s, vh = sorted_svd(A, k) + + respect_u = True if self.solver == 'propack' else M <= N + respect_vh = True if self.solver == 'propack' else M > N + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + if rsv is False: + s2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(s2, s) + elif rsv == 'u' and respect_u: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + assert vh2 is None + elif rsv == 'vh' and respect_vh: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert u2 is None + assert_allclose(s2, s) + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + if u2 is not None: + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + if vh2 is not None: + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + if rsv is False: + s2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(s2, s) + elif rsv == 'u' and respect_u: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + assert vh2 is None + elif rsv == 'vh' and respect_vh: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert u2 is None + assert_allclose(s2, s) + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + if u2 is not None: + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + if vh2 is not None: + assert_allclose(np.abs(vh2), np.abs(vh)) + + # --- Test Basic Functionality --- + # Tests the accuracy of each solver for real and complex matrices provided + # as list, dense array, sparse matrix, and LinearOperator. + + A1 = [[1, 2, 3], [3, 4, 3], [1 + 1j, 0, 2], [0, 0, 1]] + A2 = [[1, 2, 3, 8 + 5j], [3 - 2j, 4, 3, 5], [1, 0, 2, 3], [0, 0, 1, 0]] + + @pytest.mark.filterwarnings("ignore:k >= N - 1", + reason="needed to demonstrate #16725") + @pytest.mark.parametrize('A', (A1, A2)) + @pytest.mark.parametrize('k', range(1, 5)) + # PROPACK fails a lot if @pytest.mark.parametrize('which', ("SM", "LM")) + @pytest.mark.parametrize('real', (True, False)) + @pytest.mark.parametrize('transpose', (False, True)) + # In gh-14299, it was suggested the `svds` should _not_ work with lists + @pytest.mark.parametrize('lo_type', (np.asarray, csc_matrix, + aslinearoperator)) + def test_svd_simple(self, A, k, real, transpose, lo_type): + + A = np.asarray(A) + A = np.real(A) if real else A + A = A.T if transpose else A + A2 = lo_type(A) + + # could check for the appropriate errors, but that is tested above + if k > min(A.shape): + pytest.skip("`k` cannot be greater than `min(A.shape)`") + if self.solver != 'propack' and k >= min(A.shape): + pytest.skip("Only PROPACK supports complete SVD") + if self.solver == 'arpack' and not real and k == min(A.shape) - 1: + pytest.skip("#16725") + + atol = 3e-10 + if self.solver == 'propack': + atol = 3e-9 # otherwise test fails on Linux aarch64 (see gh-19855) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + u, s, vh = svds(A2, k, solver=self.solver, random_state=0) + else: + u, s, vh = svds(A2, k, solver=self.solver, random_state=0) + _check_svds(A, k, u, s, vh, atol=atol) + + def test_svd_linop(self): + solver = self.solver + + nmks = [(6, 7, 3), + (9, 5, 4), + (10, 8, 5)] + + def reorder(args): + U, s, VH = args + j = np.argsort(s) + return U[:, j], s[j], VH[j, :] + + for n, m, k in nmks: + # Test svds on a LinearOperator. + A = np.random.RandomState(52).randn(n, m) + L = CheckingLinearOperator(A) + + if solver == 'propack': + v0 = np.ones(n) + else: + v0 = np.ones(min(A.shape)) + if solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, v0=v0, solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, v0=v0, solver=solver, + random_state=0)) + else: + U1, s1, VH1 = reorder(svds(A, k, v0=v0, solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, v0=v0, solver=solver, + random_state=0)) + + assert_allclose(np.abs(U1), np.abs(U2)) + assert_allclose(s1, s2) + assert_allclose(np.abs(VH1), np.abs(VH2)) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2))) + + # Try again with which="SM". + A = np.random.RandomState(1909).randn(n, m) + L = CheckingLinearOperator(A) + + # TODO: arpack crashes when v0=v0, which="SM" + kwargs = {'v0': v0} if solver not in {None, 'arpack'} else {} + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, which="SM", solver=solver, + random_state=0, **kwargs)) + U2, s2, VH2 = reorder(svds(L, k, which="SM", solver=solver, + random_state=0, **kwargs)) + else: + U1, s1, VH1 = reorder(svds(A, k, which="SM", solver=solver, + random_state=0, **kwargs)) + U2, s2, VH2 = reorder(svds(L, k, which="SM", solver=solver, + random_state=0, **kwargs)) + + assert_allclose(np.abs(U1), np.abs(U2)) + assert_allclose(s1 + 1, s2 + 1) + assert_allclose(np.abs(VH1), np.abs(VH2)) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2))) + + if k < min(n, m) - 1: + # Complex input and explicit which="LM". + for (dt, eps) in [(complex, 1e-7), (np.complex64, 3e-3)]: + rng = np.random.RandomState(1648) + A = (rng.randn(n, m) + 1j * rng.randn(n, m)).astype(dt) + L = CheckingLinearOperator(A) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, + match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, which="LM", + solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, which="LM", + solver=solver, + random_state=0)) + else: + U1, s1, VH1 = reorder(svds(A, k, which="LM", + solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, which="LM", + solver=solver, + random_state=0)) + + assert_allclose(np.abs(U1), np.abs(U2), rtol=eps) + assert_allclose(s1, s2, rtol=eps) + assert_allclose(np.abs(VH1), np.abs(VH2), rtol=eps) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2)), + rtol=eps) + + SHAPES = ((100, 100), (100, 101), (101, 100)) + + @pytest.mark.filterwarnings("ignore:Exited at iteration") + @pytest.mark.filterwarnings("ignore:Exited postprocessing") + @pytest.mark.parametrize("shape", SHAPES) + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + def test_small_sigma_sparse(self, shape, dtype): + # https://github.com/scipy/scipy/pull/11829 + solver = self.solver + # 2do: PROPACK fails orthogonality of singular vectors + # if dtype == complex and self.solver == 'propack': + # pytest.skip("PROPACK unsupported for complex dtype") + rng = np.random.default_rng(0) + k = 5 + (m, n) = shape + S = random(m, n, density=0.1, random_state=rng) + if dtype == complex: + S = + 1j * random(m, n, density=0.1, random_state=rng) + e = np.ones(m) + e[0:5] *= 1e1 ** np.arange(-5, 0, 1) + S = spdiags(e, 0, m, m) @ S + S = S.astype(dtype) + u, s, vh = svds(S, k, which='SM', solver=solver, maxiter=1000, + random_state=0) + c_svd = False # partial SVD can be different from full SVD + _check_svds_n(S, k, u, s, vh, which="SM", check_svd=c_svd, atol=2e-1) + + # --- Test Edge Cases --- + # Checks a few edge cases. + + @pytest.mark.parametrize("shape", ((6, 5), (5, 5), (5, 6))) + @pytest.mark.parametrize("dtype", (float, complex)) + def test_svd_LM_ones_matrix(self, shape, dtype): + # Check that svds can deal with matrix_rank less than k in LM mode. + k = 3 + n, m = shape + A = np.ones((n, m), dtype=dtype) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + else: + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + + _check_svds(A, k, U, s, VH, check_usvh_A=True, check_svd=False) + + # Check that the largest singular value is near sqrt(n*m) + # and the other singular values have been forced to zero. + assert_allclose(np.max(s), np.sqrt(n*m)) + s = np.array(sorted(s)[:-1]) + 1 + z = np.ones_like(s) + assert_allclose(s, z) + + @pytest.mark.filterwarnings("ignore:k >= N - 1", + reason="needed to demonstrate #16725") + @pytest.mark.parametrize("shape", ((3, 4), (4, 4), (4, 3), (4, 2))) + @pytest.mark.parametrize("dtype", (float, complex)) + def test_zero_matrix(self, shape, dtype): + # Check that svds can deal with matrices containing only zeros; + # see https://github.com/scipy/scipy/issues/3452/ + # shape = (4, 2) is included because it is the particular case + # reported in the issue + k = 1 + n, m = shape + A = np.zeros((n, m), dtype=dtype) + + if (self.solver == 'arpack' and dtype is complex + and k == min(A.shape) - 1): + pytest.skip("#16725") + + if self.solver == 'propack': + pytest.skip("PROPACK failures unrelated to PR #16712") + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + else: + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + + # Check some generic properties of svd. + _check_svds(A, k, U, s, VH, check_usvh_A=True, check_svd=False) + + # Check that the singular values are zero. + assert_array_equal(s, 0) + + @pytest.mark.parametrize("shape", ((20, 20), (20, 21), (21, 20))) + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + def test_small_sigma(self, shape, dtype): + rng = np.random.default_rng(179847540) + A = rng.random(shape).astype(dtype) + u, _, vh = svd(A, full_matrices=False) + if dtype == np.float32: + e = 10.0 + else: + e = 100.0 + t = e**(-np.arange(len(vh))).astype(dtype) + A = (u*t).dot(vh) + k = 4 + u, s, vh = svds(A, k, solver=self.solver, maxiter=100, random_state=0) + t = np.sum(s > 0) + assert_equal(t, k) + # LOBPCG needs larger atol and rtol to pass + _check_svds_n(A, k, u, s, vh, atol=1e-3, rtol=1e0, check_svd=False) + + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.filterwarnings("ignore:The problem size") + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + def test_small_sigma2(self, dtype): + rng = np.random.default_rng(179847540) + # create a 10x10 singular matrix with a 4-dim null space + dim = 4 + size = 10 + x = rng.random((size, size-dim)) + y = x[:, :dim] * rng.random(dim) + mat = np.hstack((x, y)) + mat = mat.astype(dtype) + + nz = null_space(mat) + assert_equal(nz.shape[1], dim) + + # Tolerances atol and rtol adjusted to pass np.float32 + # Use non-sparse svd + u, s, vh = svd(mat) + # Singular values are 0: + assert_allclose(s[-dim:], 0, atol=1e-6, rtol=1e0) + # Smallest right singular vectors in null space: + assert_allclose(mat @ vh[-dim:, :].T, 0, atol=1e-6, rtol=1e0) + + # Smallest singular values should be 0 + sp_mat = csc_matrix(mat) + su, ss, svh = svds(sp_mat, k=dim, which='SM', solver=self.solver, + random_state=0) + # Smallest dim singular values are 0: + assert_allclose(ss, 0, atol=1e-5, rtol=1e0) + # Smallest singular vectors via svds in null space: + n, m = mat.shape + if n < m: # else the assert fails with some libraries unclear why + assert_allclose(sp_mat.transpose() @ su, 0, atol=1e-5, rtol=1e0) + assert_allclose(sp_mat @ svh.T, 0, atol=1e-5, rtol=1e0) + +# --- Perform tests with each solver --- + + +class Test_SVDS_once: + @pytest.mark.parametrize("solver", ['ekki', object]) + def test_svds_input_validation_solver(self, solver): + message = "solver must be one of" + with pytest.raises(ValueError, match=message): + svds(np.ones((3, 4)), k=2, solver=solver) + + +class Test_SVDS_ARPACK(SVDSCommonTests): + + def setup_method(self): + self.solver = 'arpack' + + @pytest.mark.parametrize("ncv", list(range(-1, 8)) + [4.5, "5"]) + def test_svds_input_validation_ncv_1(self, ncv): + rng = np.random.default_rng(0) + A = rng.random((6, 7)) + k = 3 + if ncv in {4, 5}: + u, s, vh = svds(A, k=k, ncv=ncv, solver=self.solver, random_state=0) + # partial decomposition, so don't check that u@diag(s)@vh=A; + # do check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + _check_svds(A, k, u, s, vh) + else: + message = ("`ncv` must be an integer satisfying") + with pytest.raises(ValueError, match=message): + svds(A, k=k, ncv=ncv, solver=self.solver) + + def test_svds_input_validation_ncv_2(self): + # I think the stack trace is reasonable when `ncv` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), ncv=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), ncv="hi", solver=self.solver) + + # I can't see a robust relationship between `ncv` and relevant outputs + # (e.g. accuracy, time), so no test of the parameter. + + +class Test_SVDS_LOBPCG(SVDSCommonTests): + + def setup_method(self): + self.solver = 'lobpcg' + + +class Test_SVDS_PROPACK(SVDSCommonTests): + + def setup_method(self): + self.solver = 'propack' + + def test_svd_LM_ones_matrix(self): + message = ("PROPACK does not return orthonormal singular vectors " + "associated with zero singular values.") + # There are some other issues with this matrix of all ones, e.g. + # `which='sm'` and `k=1` returns the largest singular value + pytest.xfail(message) + + def test_svd_LM_zeros_matrix(self): + message = ("PROPACK does not return orthonormal singular vectors " + "associated with zero singular values.") + pytest.xfail(message) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_expm_multiply.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_expm_multiply.py new file mode 100644 index 0000000000000000000000000000000000000000..6bc8d83b75a7944193e8060aeb0f841f3a402b16 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_expm_multiply.py @@ -0,0 +1,810 @@ +"""Compute the action of the matrix exponential.""" +from warnings import warn + +import numpy as np + +import scipy.linalg +import scipy.sparse.linalg +from scipy.linalg._decomp_qr import qr +from scipy.sparse._sputils import is_pydata_spmatrix +from scipy.sparse.linalg import aslinearoperator +from scipy.sparse.linalg._interface import IdentityOperator +from scipy.sparse.linalg._onenormest import onenormest + +__all__ = ['expm_multiply'] + + +def _exact_inf_norm(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + return max(abs(A).sum(axis=1).flat) + elif is_pydata_spmatrix(A): + return max(abs(A).sum(axis=1)) + else: + return np.linalg.norm(A, np.inf) + + +def _exact_1_norm(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + return max(abs(A).sum(axis=0).flat) + elif is_pydata_spmatrix(A): + return max(abs(A).sum(axis=0)) + else: + return np.linalg.norm(A, 1) + + +def _trace(A): + # A compatibility function which should eventually disappear. + if is_pydata_spmatrix(A): + return A.to_scipy_sparse().trace() + else: + return A.trace() + + +def traceest(A, m3, seed=None): + """Estimate `np.trace(A)` using `3*m3` matrix-vector products. + + The result is not deterministic. + + Parameters + ---------- + A : LinearOperator + Linear operator whose trace will be estimated. Has to be square. + m3 : int + Number of matrix-vector products divided by 3 used to estimate the + trace. + seed : optional + Seed for `numpy.random.default_rng`. + Can be provided to obtain deterministic results. + + Returns + ------- + trace : LinearOperator.dtype + Estimate of the trace + + Notes + ----- + This is the Hutch++ algorithm given in [1]_. + + References + ---------- + .. [1] Meyer, Raphael A., Cameron Musco, Christopher Musco, and David P. + Woodruff. "Hutch++: Optimal Stochastic Trace Estimation." In Symposium + on Simplicity in Algorithms (SOSA), pp. 142-155. Society for Industrial + and Applied Mathematics, 2021 + https://doi.org/10.1137/1.9781611976496.16 + + """ + rng = np.random.default_rng(seed) + if len(A.shape) != 2 or A.shape[-1] != A.shape[-2]: + raise ValueError("Expected A to be like a square matrix.") + n = A.shape[-1] + S = rng.choice([-1.0, +1.0], [n, m3]) + Q, _ = qr(A.matmat(S), overwrite_a=True, mode='economic') + trQAQ = np.trace(Q.conj().T @ A.matmat(Q)) + G = rng.choice([-1, +1], [n, m3]) + right = G - Q@(Q.conj().T @ G) + trGAG = np.trace(right.conj().T @ A.matmat(right)) + return trQAQ + trGAG/m3 + + +def _ident_like(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + # Creates a sparse matrix in dia format + out = scipy.sparse.eye(A.shape[0], A.shape[1], dtype=A.dtype) + if isinstance(A, scipy.sparse.spmatrix): + return out.asformat(A.format) + return scipy.sparse.dia_array(out).asformat(A.format) + elif is_pydata_spmatrix(A): + import sparse + return sparse.eye(A.shape[0], A.shape[1], dtype=A.dtype) + elif isinstance(A, scipy.sparse.linalg.LinearOperator): + return IdentityOperator(A.shape, dtype=A.dtype) + else: + return np.eye(A.shape[0], A.shape[1], dtype=A.dtype) + + +def expm_multiply(A, B, start=None, stop=None, num=None, + endpoint=None, traceA=None): + """ + Compute the action of the matrix exponential of A on B. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix or vector to be multiplied by the matrix exponential of A. + start : scalar, optional + The starting time point of the sequence. + stop : scalar, optional + The end time point of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced time points, so that `stop` is excluded. + Note that the step size changes when `endpoint` is False. + num : int, optional + Number of time points to use. + endpoint : bool, optional + If True, `stop` is the last time point. Otherwise, it is not included. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable. + For linear operators, `traceA` should be provided to ensure performance + as the estimation is not guaranteed to be reliable for all cases. + + .. versionadded:: 1.9.0 + + Returns + ------- + expm_A_B : ndarray + The result of the action :math:`e^{t_k A} B`. + + Warns + ----- + UserWarning + If `A` is a linear operator and ``traceA=None`` (default). + + Notes + ----- + The optional arguments defining the sequence of evenly spaced time points + are compatible with the arguments of `numpy.linspace`. + + The output ndarray shape is somewhat complicated so I explain it here. + The ndim of the output could be either 1, 2, or 3. + It would be 1 if you are computing the expm action on a single vector + at a single time point. + It would be 2 if you are computing the expm action on a vector + at multiple time points, or if you are computing the expm action + on a matrix at a single time point. + It would be 3 if you want the action on a matrix with multiple + columns at multiple time points. + If multiple time points are requested, expm_A_B[0] will always + be the action of the expm at the first time point, + regardless of whether the action is on a vector or a matrix. + + References + ---------- + .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2011) + "Computing the Action of the Matrix Exponential, + with an Application to Exponential Integrators." + SIAM Journal on Scientific Computing, + 33 (2). pp. 488-511. ISSN 1064-8275 + http://eprints.ma.man.ac.uk/1591/ + + .. [2] Nicholas J. Higham and Awad H. Al-Mohy (2010) + "Computing Matrix Functions." + Acta Numerica, + 19. 159-208. ISSN 0962-4929 + http://eprints.ma.man.ac.uk/1451/ + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import expm, expm_multiply + >>> A = csc_matrix([[1, 0], [0, 1]]) + >>> A.toarray() + array([[1, 0], + [0, 1]], dtype=int64) + >>> B = np.array([np.exp(-1.), np.exp(-2.)]) + >>> B + array([ 0.36787944, 0.13533528]) + >>> expm_multiply(A, B, start=1, stop=2, num=3, endpoint=True) + array([[ 1. , 0.36787944], + [ 1.64872127, 0.60653066], + [ 2.71828183, 1. ]]) + >>> expm(A).dot(B) # Verify 1st timestep + array([ 1. , 0.36787944]) + >>> expm(1.5*A).dot(B) # Verify 2nd timestep + array([ 1.64872127, 0.60653066]) + >>> expm(2*A).dot(B) # Verify 3rd timestep + array([ 2.71828183, 1. ]) + """ + if all(arg is None for arg in (start, stop, num, endpoint)): + X = _expm_multiply_simple(A, B, traceA=traceA) + else: + X, status = _expm_multiply_interval(A, B, start, stop, num, + endpoint, traceA=traceA) + return X + + +def _expm_multiply_simple(A, B, t=1.0, traceA=None, balance=False): + """ + Compute the action of the matrix exponential at a single time point. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix to be multiplied by the matrix exponential of A. + t : float + A time point. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable + balance : bool + Indicates whether or not to apply balancing. + + Returns + ------- + F : ndarray + :math:`e^{t A} B` + + Notes + ----- + This is algorithm (3.2) in Al-Mohy and Higham (2011). + + """ + if balance: + raise NotImplementedError + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if A.shape[1] != B.shape[0]: + raise ValueError('shapes of matrices A {} and B {} are incompatible' + .format(A.shape, B.shape)) + ident = _ident_like(A) + is_linear_operator = isinstance(A, scipy.sparse.linalg.LinearOperator) + n = A.shape[0] + if len(B.shape) == 1: + n0 = 1 + elif len(B.shape) == 2: + n0 = B.shape[1] + else: + raise ValueError('expected B to be like a matrix or a vector') + u_d = 2**-53 + tol = u_d + if traceA is None: + if is_linear_operator: + warn("Trace of LinearOperator not available, it will be estimated." + " Provide `traceA` to ensure performance.", stacklevel=3) + # m3=1 is bit arbitrary choice, a more accurate trace (larger m3) might + # speed up exponential calculation, but trace estimation is more costly + traceA = traceest(A, m3=1) if is_linear_operator else _trace(A) + mu = traceA / float(n) + A = A - mu * ident + A_1_norm = onenormest(A) if is_linear_operator else _exact_1_norm(A) + if t*A_1_norm == 0: + m_star, s = 0, 1 + else: + ell = 2 + norm_info = LazyOperatorNormInfo(t*A, A_1_norm=t*A_1_norm, ell=ell) + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + return _expm_multiply_simple_core(A, B, t, mu, m_star, s, tol, balance) + + +def _expm_multiply_simple_core(A, B, t, mu, m_star, s, tol=None, balance=False): + """ + A helper function. + """ + if balance: + raise NotImplementedError + if tol is None: + u_d = 2 ** -53 + tol = u_d + F = B + eta = np.exp(t*mu / float(s)) + for i in range(s): + c1 = _exact_inf_norm(B) + for j in range(m_star): + coeff = t / float(s*(j+1)) + B = coeff * A.dot(B) + c2 = _exact_inf_norm(B) + F = F + B + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + F = eta * F + B = F + return F + + +# This table helps to compute bounds. +# They seem to have been difficult to calculate, involving symbolic +# manipulation of equations, followed by numerical root finding. +_theta = { + # The first 30 values are from table A.3 of Computing Matrix Functions. + 1: 2.29e-16, + 2: 2.58e-8, + 3: 1.39e-5, + 4: 3.40e-4, + 5: 2.40e-3, + 6: 9.07e-3, + 7: 2.38e-2, + 8: 5.00e-2, + 9: 8.96e-2, + 10: 1.44e-1, + # 11 + 11: 2.14e-1, + 12: 3.00e-1, + 13: 4.00e-1, + 14: 5.14e-1, + 15: 6.41e-1, + 16: 7.81e-1, + 17: 9.31e-1, + 18: 1.09, + 19: 1.26, + 20: 1.44, + # 21 + 21: 1.62, + 22: 1.82, + 23: 2.01, + 24: 2.22, + 25: 2.43, + 26: 2.64, + 27: 2.86, + 28: 3.08, + 29: 3.31, + 30: 3.54, + # The rest are from table 3.1 of + # Computing the Action of the Matrix Exponential. + 35: 4.7, + 40: 6.0, + 45: 7.2, + 50: 8.5, + 55: 9.9, + } + + +def _onenormest_matrix_power(A, p, + t=2, itmax=5, compute_v=False, compute_w=False): + """ + Efficiently estimate the 1-norm of A^p. + + Parameters + ---------- + A : ndarray + Matrix whose 1-norm of a power is to be computed. + p : int + Non-negative integer power. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + #XXX Eventually turn this into an API function in the _onenormest module, + #XXX and remove its underscore, + #XXX but wait until expm_multiply goes into scipy. + from scipy.sparse.linalg._onenormest import onenormest + return onenormest(aslinearoperator(A) ** p) + +class LazyOperatorNormInfo: + """ + Information about an operator is lazily computed. + + The information includes the exact 1-norm of the operator, + in addition to estimates of 1-norms of powers of the operator. + This uses the notation of Computing the Action (2011). + This class is specialized enough to probably not be of general interest + outside of this module. + + """ + + def __init__(self, A, A_1_norm=None, ell=2, scale=1): + """ + Provide the operator and some norm-related information. + + Parameters + ---------- + A : linear operator + The operator of interest. + A_1_norm : float, optional + The exact 1-norm of A. + ell : int, optional + A technical parameter controlling norm estimation quality. + scale : int, optional + If specified, return the norms of scale*A instead of A. + + """ + self._A = A + self._A_1_norm = A_1_norm + self._ell = ell + self._d = {} + self._scale = scale + + def set_scale(self,scale): + """ + Set the scale parameter. + """ + self._scale = scale + + def onenorm(self): + """ + Compute the exact 1-norm. + """ + if self._A_1_norm is None: + self._A_1_norm = _exact_1_norm(self._A) + return self._scale*self._A_1_norm + + def d(self, p): + """ + Lazily estimate :math:`d_p(A) ~= || A^p ||^(1/p)` where :math:`||.||` is the 1-norm. + """ + if p not in self._d: + est = _onenormest_matrix_power(self._A, p, self._ell) + self._d[p] = est ** (1.0 / p) + return self._scale*self._d[p] + + def alpha(self, p): + """ + Lazily compute max(d(p), d(p+1)). + """ + return max(self.d(p), self.d(p+1)) + +def _compute_cost_div_m(m, p, norm_info): + """ + A helper function for computing bounds. + + This is equation (3.10). + It measures cost in terms of the number of required matrix products. + + Parameters + ---------- + m : int + A valid key of _theta. + p : int + A matrix power. + norm_info : LazyOperatorNormInfo + Information about 1-norms of related operators. + + Returns + ------- + cost_div_m : int + Required number of matrix products divided by m. + + """ + return int(np.ceil(norm_info.alpha(p) / _theta[m])) + + +def _compute_p_max(m_max): + """ + Compute the largest positive integer p such that p*(p-1) <= m_max + 1. + + Do this in a slightly dumb way, but safe and not too slow. + + Parameters + ---------- + m_max : int + A count related to bounds. + + """ + sqrt_m_max = np.sqrt(m_max) + p_low = int(np.floor(sqrt_m_max)) + p_high = int(np.ceil(sqrt_m_max + 1)) + return max(p for p in range(p_low, p_high+1) if p*(p-1) <= m_max + 1) + + +def _fragment_3_1(norm_info, n0, tol, m_max=55, ell=2): + """ + A helper function for the _expm_multiply_* functions. + + Parameters + ---------- + norm_info : LazyOperatorNormInfo + Information about norms of certain linear operators of interest. + n0 : int + Number of columns in the _expm_multiply_* B matrix. + tol : float + Expected to be + :math:`2^{-24}` for single precision or + :math:`2^{-53}` for double precision. + m_max : int + A value related to a bound. + ell : int + The number of columns used in the 1-norm approximation. + This is usually taken to be small, maybe between 1 and 5. + + Returns + ------- + best_m : int + Related to bounds for error control. + best_s : int + Amount of scaling. + + Notes + ----- + This is code fragment (3.1) in Al-Mohy and Higham (2011). + The discussion of default values for m_max and ell + is given between the definitions of equation (3.11) + and the definition of equation (3.12). + + """ + if ell < 1: + raise ValueError('expected ell to be a positive integer') + best_m = None + best_s = None + if _condition_3_13(norm_info.onenorm(), n0, m_max, ell): + for m, theta in _theta.items(): + s = int(np.ceil(norm_info.onenorm() / theta)) + if best_m is None or m * s < best_m * best_s: + best_m = m + best_s = s + else: + # Equation (3.11). + for p in range(2, _compute_p_max(m_max) + 1): + for m in range(p*(p-1)-1, m_max+1): + if m in _theta: + s = _compute_cost_div_m(m, p, norm_info) + if best_m is None or m * s < best_m * best_s: + best_m = m + best_s = s + best_s = max(best_s, 1) + return best_m, best_s + + +def _condition_3_13(A_1_norm, n0, m_max, ell): + """ + A helper function for the _expm_multiply_* functions. + + Parameters + ---------- + A_1_norm : float + The precomputed 1-norm of A. + n0 : int + Number of columns in the _expm_multiply_* B matrix. + m_max : int + A value related to a bound. + ell : int + The number of columns used in the 1-norm approximation. + This is usually taken to be small, maybe between 1 and 5. + + Returns + ------- + value : bool + Indicates whether or not the condition has been met. + + Notes + ----- + This is condition (3.13) in Al-Mohy and Higham (2011). + + """ + + # This is the rhs of equation (3.12). + p_max = _compute_p_max(m_max) + a = 2 * ell * p_max * (p_max + 3) + + # Evaluate the condition (3.13). + b = _theta[m_max] / float(n0 * m_max) + return A_1_norm <= a * b + + +def _expm_multiply_interval(A, B, start=None, stop=None, num=None, + endpoint=None, traceA=None, balance=False, + status_only=False): + """ + Compute the action of the matrix exponential at multiple time points. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix to be multiplied by the matrix exponential of A. + start : scalar, optional + The starting time point of the sequence. + stop : scalar, optional + The end time point of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced time points, so that `stop` is excluded. + Note that the step size changes when `endpoint` is False. + num : int, optional + Number of time points to use. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable + endpoint : bool, optional + If True, `stop` is the last time point. Otherwise, it is not included. + balance : bool + Indicates whether or not to apply balancing. + status_only : bool + A flag that is set to True for some debugging and testing operations. + + Returns + ------- + F : ndarray + :math:`e^{t_k A} B` + status : int + An integer status for testing and debugging. + + Notes + ----- + This is algorithm (5.2) in Al-Mohy and Higham (2011). + + There seems to be a typo, where line 15 of the algorithm should be + moved to line 6.5 (between lines 6 and 7). + + """ + if balance: + raise NotImplementedError + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if A.shape[1] != B.shape[0]: + raise ValueError('shapes of matrices A {} and B {} are incompatible' + .format(A.shape, B.shape)) + ident = _ident_like(A) + is_linear_operator = isinstance(A, scipy.sparse.linalg.LinearOperator) + n = A.shape[0] + if len(B.shape) == 1: + n0 = 1 + elif len(B.shape) == 2: + n0 = B.shape[1] + else: + raise ValueError('expected B to be like a matrix or a vector') + u_d = 2**-53 + tol = u_d + if traceA is None: + if is_linear_operator: + warn("Trace of LinearOperator not available, it will be estimated." + " Provide `traceA` to ensure performance.", stacklevel=3) + # m3=5 is bit arbitrary choice, a more accurate trace (larger m3) might + # speed up exponential calculation, but trace estimation is also costly + # an educated guess would need to consider the number of time points + traceA = traceest(A, m3=5) if is_linear_operator else _trace(A) + mu = traceA / float(n) + + # Get the linspace samples, attempting to preserve the linspace defaults. + linspace_kwargs = {'retstep': True} + if num is not None: + linspace_kwargs['num'] = num + if endpoint is not None: + linspace_kwargs['endpoint'] = endpoint + samples, step = np.linspace(start, stop, **linspace_kwargs) + + # Convert the linspace output to the notation used by the publication. + nsamples = len(samples) + if nsamples < 2: + raise ValueError('at least two time points are required') + q = nsamples - 1 + h = step + t_0 = samples[0] + t_q = samples[q] + + # Define the output ndarray. + # Use an ndim=3 shape, such that the last two indices + # are the ones that may be involved in level 3 BLAS operations. + X_shape = (nsamples,) + B.shape + X = np.empty(X_shape, dtype=np.result_type(A.dtype, B.dtype, float)) + t = t_q - t_0 + A = A - mu * ident + A_1_norm = onenormest(A) if is_linear_operator else _exact_1_norm(A) + ell = 2 + norm_info = LazyOperatorNormInfo(t*A, A_1_norm=t*A_1_norm, ell=ell) + if t*A_1_norm == 0: + m_star, s = 0, 1 + else: + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + + # Compute the expm action up to the initial time point. + X[0] = _expm_multiply_simple_core(A, B, t_0, mu, m_star, s) + + # Compute the expm action at the rest of the time points. + if q <= s: + if status_only: + return 0 + else: + return _expm_multiply_interval_core_0(A, X, + h, mu, q, norm_info, tol, ell,n0) + elif not (q % s): + if status_only: + return 1 + else: + return _expm_multiply_interval_core_1(A, X, + h, mu, m_star, s, q, tol) + elif (q % s): + if status_only: + return 2 + else: + return _expm_multiply_interval_core_2(A, X, + h, mu, m_star, s, q, tol) + else: + raise Exception('internal error') + + +def _expm_multiply_interval_core_0(A, X, h, mu, q, norm_info, tol, ell, n0): + """ + A helper function, for the case q <= s. + """ + + # Compute the new values of m_star and s which should be applied + # over intervals of size t/q + if norm_info.onenorm() == 0: + m_star, s = 0, 1 + else: + norm_info.set_scale(1./q) + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + norm_info.set_scale(1) + + for k in range(q): + X[k+1] = _expm_multiply_simple_core(A, X[k], h, mu, m_star, s) + return X, 0 + + +def _expm_multiply_interval_core_1(A, X, h, mu, m_star, s, q, tol): + """ + A helper function, for the case q > s and q % s == 0. + """ + d = q // s + input_shape = X.shape[1:] + K_shape = (m_star + 1, ) + input_shape + K = np.empty(K_shape, dtype=X.dtype) + for i in range(s): + Z = X[i*d] + K[0] = Z + high_p = 0 + for k in range(1, d+1): + F = K[0] + c1 = _exact_inf_norm(F) + for p in range(1, m_star+1): + if p > high_p: + K[p] = h * A.dot(K[p-1]) / float(p) + coeff = float(pow(k, p)) + F = F + coeff * K[p] + inf_norm_K_p_1 = _exact_inf_norm(K[p]) + c2 = coeff * inf_norm_K_p_1 + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + X[k + i*d] = np.exp(k*h*mu) * F + return X, 1 + + +def _expm_multiply_interval_core_2(A, X, h, mu, m_star, s, q, tol): + """ + A helper function, for the case q > s and q % s > 0. + """ + d = q // s + j = q // d + r = q - d * j + input_shape = X.shape[1:] + K_shape = (m_star + 1, ) + input_shape + K = np.empty(K_shape, dtype=X.dtype) + for i in range(j + 1): + Z = X[i*d] + K[0] = Z + high_p = 0 + if i < j: + effective_d = d + else: + effective_d = r + for k in range(1, effective_d+1): + F = K[0] + c1 = _exact_inf_norm(F) + for p in range(1, m_star+1): + if p == high_p + 1: + K[p] = h * A.dot(K[p-1]) / float(p) + high_p = p + coeff = float(pow(k, p)) + F = F + coeff * K[p] + inf_norm_K_p_1 = _exact_inf_norm(K[p]) + c2 = coeff * inf_norm_K_p_1 + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + X[k + i*d] = np.exp(k*h*mu) * F + return X, 2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_interface.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..7c515167c326e27f4dce21cbfa5c052995afc7da --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_interface.py @@ -0,0 +1,896 @@ +"""Abstract linear algebra library. + +This module defines a class hierarchy that implements a kind of "lazy" +matrix representation, called the ``LinearOperator``. It can be used to do +linear algebra with extremely large sparse or structured matrices, without +representing those explicitly in memory. Such matrices can be added, +multiplied, transposed, etc. + +As a motivating example, suppose you want have a matrix where almost all of +the elements have the value one. The standard sparse matrix representation +skips the storage of zeros, but not ones. By contrast, a LinearOperator is +able to represent such matrices efficiently. First, we need a compact way to +represent an all-ones matrix:: + + >>> import numpy as np + >>> from scipy.sparse.linalg._interface import LinearOperator + >>> class Ones(LinearOperator): + ... def __init__(self, shape): + ... super().__init__(dtype=None, shape=shape) + ... def _matvec(self, x): + ... return np.repeat(x.sum(), self.shape[0]) + +Instances of this class emulate ``np.ones(shape)``, but using a constant +amount of storage, independent of ``shape``. The ``_matvec`` method specifies +how this linear operator multiplies with (operates on) a vector. We can now +add this operator to a sparse matrix that stores only offsets from one:: + + >>> from scipy.sparse.linalg._interface import aslinearoperator + >>> from scipy.sparse import csr_matrix + >>> offsets = csr_matrix([[1, 0, 2], [0, -1, 0], [0, 0, 3]]) + >>> A = aslinearoperator(offsets) + Ones(offsets.shape) + >>> A.dot([1, 2, 3]) + array([13, 4, 15]) + +The result is the same as that given by its dense, explicitly-stored +counterpart:: + + >>> (np.ones(A.shape, A.dtype) + offsets.toarray()).dot([1, 2, 3]) + array([13, 4, 15]) + +Several algorithms in the ``scipy.sparse`` library are able to operate on +``LinearOperator`` instances. +""" + +import warnings + +import numpy as np + +from scipy.sparse import issparse +from scipy.sparse._sputils import isshape, isintlike, asmatrix, is_pydata_spmatrix + +__all__ = ['LinearOperator', 'aslinearoperator'] + + +class LinearOperator: + """Common interface for performing matrix vector products + + Many iterative methods (e.g. cg, gmres) do not need to know the + individual entries of a matrix to solve a linear system A*x=b. + Such solvers only require the computation of matrix vector + products, A*v where v is a dense vector. This class serves as + an abstract interface between iterative solvers and matrix-like + objects. + + To construct a concrete LinearOperator, either pass appropriate + callables to the constructor of this class, or subclass it. + + A subclass must implement either one of the methods ``_matvec`` + and ``_matmat``, and the attributes/properties ``shape`` (pair of + integers) and ``dtype`` (may be None). It may call the ``__init__`` + on this class to have these attributes validated. Implementing + ``_matvec`` automatically implements ``_matmat`` (using a naive + algorithm) and vice-versa. + + Optionally, a subclass may implement ``_rmatvec`` or ``_adjoint`` + to implement the Hermitian adjoint (conjugate transpose). As with + ``_matvec`` and ``_matmat``, implementing either ``_rmatvec`` or + ``_adjoint`` implements the other automatically. Implementing + ``_adjoint`` is preferable; ``_rmatvec`` is mostly there for + backwards compatibility. + + Parameters + ---------- + shape : tuple + Matrix dimensions (M, N). + matvec : callable f(v) + Returns returns A * v. + rmatvec : callable f(v) + Returns A^H * v, where A^H is the conjugate transpose of A. + matmat : callable f(V) + Returns A * V, where V is a dense matrix with dimensions (N, K). + dtype : dtype + Data type of the matrix. + rmatmat : callable f(V) + Returns A^H * V, where V is a dense matrix with dimensions (M, K). + + Attributes + ---------- + args : tuple + For linear operators describing products etc. of other linear + operators, the operands of the binary operation. + ndim : int + Number of dimensions (this is always 2) + + See Also + -------- + aslinearoperator : Construct LinearOperators + + Notes + ----- + The user-defined matvec() function must properly handle the case + where v has shape (N,) as well as the (N,1) case. The shape of + the return type is handled internally by LinearOperator. + + LinearOperator instances can also be multiplied, added with each + other and exponentiated, all lazily: the result of these operations + is always a new, composite LinearOperator, that defers linear + operations to the original operators and combines the results. + + More details regarding how to subclass a LinearOperator and several + examples of concrete LinearOperator instances can be found in the + external project `PyLops `_. + + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import LinearOperator + >>> def mv(v): + ... return np.array([2*v[0], 3*v[1]]) + ... + >>> A = LinearOperator((2,2), matvec=mv) + >>> A + <2x2 _CustomLinearOperator with dtype=float64> + >>> A.matvec(np.ones(2)) + array([ 2., 3.]) + >>> A * np.ones(2) + array([ 2., 3.]) + + """ + + ndim = 2 + # Necessary for right matmul with numpy arrays. + __array_ufunc__ = None + + def __new__(cls, *args, **kwargs): + if cls is LinearOperator: + # Operate as _CustomLinearOperator factory. + return super().__new__(_CustomLinearOperator) + else: + obj = super().__new__(cls) + + if (type(obj)._matvec == LinearOperator._matvec + and type(obj)._matmat == LinearOperator._matmat): + warnings.warn("LinearOperator subclass should implement" + " at least one of _matvec and _matmat.", + category=RuntimeWarning, stacklevel=2) + + return obj + + def __init__(self, dtype, shape): + """Initialize this LinearOperator. + + To be called by subclasses. ``dtype`` may be None; ``shape`` should + be convertible to a length-2 tuple. + """ + if dtype is not None: + dtype = np.dtype(dtype) + + shape = tuple(shape) + if not isshape(shape): + raise ValueError(f"invalid shape {shape!r} (must be 2-d)") + + self.dtype = dtype + self.shape = shape + + def _init_dtype(self): + """Called from subclasses at the end of the __init__ routine. + """ + if self.dtype is None: + v = np.zeros(self.shape[-1]) + self.dtype = np.asarray(self.matvec(v)).dtype + + def _matmat(self, X): + """Default matrix-matrix multiplication handler. + + Falls back on the user-defined _matvec method, so defining that will + define matrix multiplication (though in a very suboptimal way). + """ + + return np.hstack([self.matvec(col.reshape(-1,1)) for col in X.T]) + + def _matvec(self, x): + """Default matrix-vector multiplication handler. + + If self is a linear operator of shape (M, N), then this method will + be called on a shape (N,) or (N, 1) ndarray, and should return a + shape (M,) or (M, 1) ndarray. + + This default implementation falls back on _matmat, so defining that + will define matrix-vector multiplication as well. + """ + return self.matmat(x.reshape(-1, 1)) + + def matvec(self, x): + """Matrix-vector multiplication. + + Performs the operation y=A*x where A is an MxN linear + operator and x is a column vector or 1-d array. + + Parameters + ---------- + x : {matrix, ndarray} + An array with shape (N,) or (N,1). + + Returns + ------- + y : {matrix, ndarray} + A matrix or ndarray with shape (M,) or (M,1) depending + on the type and shape of the x argument. + + Notes + ----- + This matvec wraps the user-specified matvec routine or overridden + _matvec method to ensure that y has the correct shape and type. + + """ + + x = np.asanyarray(x) + + M,N = self.shape + + if x.shape != (N,) and x.shape != (N,1): + raise ValueError('dimension mismatch') + + y = self._matvec(x) + + if isinstance(x, np.matrix): + y = asmatrix(y) + else: + y = np.asarray(y) + + if x.ndim == 1: + y = y.reshape(M) + elif x.ndim == 2: + y = y.reshape(M,1) + else: + raise ValueError('invalid shape returned by user-defined matvec()') + + return y + + def rmatvec(self, x): + """Adjoint matrix-vector multiplication. + + Performs the operation y = A^H * x where A is an MxN linear + operator and x is a column vector or 1-d array. + + Parameters + ---------- + x : {matrix, ndarray} + An array with shape (M,) or (M,1). + + Returns + ------- + y : {matrix, ndarray} + A matrix or ndarray with shape (N,) or (N,1) depending + on the type and shape of the x argument. + + Notes + ----- + This rmatvec wraps the user-specified rmatvec routine or overridden + _rmatvec method to ensure that y has the correct shape and type. + + """ + + x = np.asanyarray(x) + + M,N = self.shape + + if x.shape != (M,) and x.shape != (M,1): + raise ValueError('dimension mismatch') + + y = self._rmatvec(x) + + if isinstance(x, np.matrix): + y = asmatrix(y) + else: + y = np.asarray(y) + + if x.ndim == 1: + y = y.reshape(N) + elif x.ndim == 2: + y = y.reshape(N,1) + else: + raise ValueError('invalid shape returned by user-defined rmatvec()') + + return y + + def _rmatvec(self, x): + """Default implementation of _rmatvec; defers to adjoint.""" + if type(self)._adjoint == LinearOperator._adjoint: + # _adjoint not overridden, prevent infinite recursion + raise NotImplementedError + else: + return self.H.matvec(x) + + def matmat(self, X): + """Matrix-matrix multiplication. + + Performs the operation y=A*X where A is an MxN linear + operator and X dense N*K matrix or ndarray. + + Parameters + ---------- + X : {matrix, ndarray} + An array with shape (N,K). + + Returns + ------- + Y : {matrix, ndarray} + A matrix or ndarray with shape (M,K) depending on + the type of the X argument. + + Notes + ----- + This matmat wraps any user-specified matmat routine or overridden + _matmat method to ensure that y has the correct type. + + """ + if not (issparse(X) or is_pydata_spmatrix(X)): + X = np.asanyarray(X) + + if X.ndim != 2: + raise ValueError(f'expected 2-d ndarray or matrix, not {X.ndim}-d') + + if X.shape[0] != self.shape[1]: + raise ValueError(f'dimension mismatch: {self.shape}, {X.shape}') + + try: + Y = self._matmat(X) + except Exception as e: + if issparse(X) or is_pydata_spmatrix(X): + raise TypeError( + "Unable to multiply a LinearOperator with a sparse matrix." + " Wrap the matrix in aslinearoperator first." + ) from e + raise + + if isinstance(Y, np.matrix): + Y = asmatrix(Y) + + return Y + + def rmatmat(self, X): + """Adjoint matrix-matrix multiplication. + + Performs the operation y = A^H * x where A is an MxN linear + operator and x is a column vector or 1-d array, or 2-d array. + The default implementation defers to the adjoint. + + Parameters + ---------- + X : {matrix, ndarray} + A matrix or 2D array. + + Returns + ------- + Y : {matrix, ndarray} + A matrix or 2D array depending on the type of the input. + + Notes + ----- + This rmatmat wraps the user-specified rmatmat routine. + + """ + if not (issparse(X) or is_pydata_spmatrix(X)): + X = np.asanyarray(X) + + if X.ndim != 2: + raise ValueError('expected 2-d ndarray or matrix, not %d-d' + % X.ndim) + + if X.shape[0] != self.shape[0]: + raise ValueError(f'dimension mismatch: {self.shape}, {X.shape}') + + try: + Y = self._rmatmat(X) + except Exception as e: + if issparse(X) or is_pydata_spmatrix(X): + raise TypeError( + "Unable to multiply a LinearOperator with a sparse matrix." + " Wrap the matrix in aslinearoperator() first." + ) from e + raise + + if isinstance(Y, np.matrix): + Y = asmatrix(Y) + return Y + + def _rmatmat(self, X): + """Default implementation of _rmatmat defers to rmatvec or adjoint.""" + if type(self)._adjoint == LinearOperator._adjoint: + return np.hstack([self.rmatvec(col.reshape(-1, 1)) for col in X.T]) + else: + return self.H.matmat(X) + + def __call__(self, x): + return self*x + + def __mul__(self, x): + return self.dot(x) + + def __truediv__(self, other): + if not np.isscalar(other): + raise ValueError("Can only divide a linear operator by a scalar.") + + return _ScaledLinearOperator(self, 1.0/other) + + def dot(self, x): + """Matrix-matrix or matrix-vector multiplication. + + Parameters + ---------- + x : array_like + 1-d or 2-d array, representing a vector or matrix. + + Returns + ------- + Ax : array + 1-d or 2-d array (depending on the shape of x) that represents + the result of applying this linear operator on x. + + """ + if isinstance(x, LinearOperator): + return _ProductLinearOperator(self, x) + elif np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + if not issparse(x) and not is_pydata_spmatrix(x): + # Sparse matrices shouldn't be converted to numpy arrays. + x = np.asarray(x) + + if x.ndim == 1 or x.ndim == 2 and x.shape[1] == 1: + return self.matvec(x) + elif x.ndim == 2: + return self.matmat(x) + else: + raise ValueError('expected 1-d or 2-d array or matrix, got %r' + % x) + + def __matmul__(self, other): + if np.isscalar(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self.__mul__(other) + + def __rmatmul__(self, other): + if np.isscalar(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self.__rmul__(other) + + def __rmul__(self, x): + if np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + return self._rdot(x) + + def _rdot(self, x): + """Matrix-matrix or matrix-vector multiplication from the right. + + Parameters + ---------- + x : array_like + 1-d or 2-d array, representing a vector or matrix. + + Returns + ------- + xA : array + 1-d or 2-d array (depending on the shape of x) that represents + the result of applying this linear operator on x from the right. + + Notes + ----- + This is copied from dot to implement right multiplication. + """ + if isinstance(x, LinearOperator): + return _ProductLinearOperator(x, self) + elif np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + if not issparse(x) and not is_pydata_spmatrix(x): + # Sparse matrices shouldn't be converted to numpy arrays. + x = np.asarray(x) + + # We use transpose instead of rmatvec/rmatmat to avoid + # unnecessary complex conjugation if possible. + if x.ndim == 1 or x.ndim == 2 and x.shape[0] == 1: + return self.T.matvec(x.T).T + elif x.ndim == 2: + return self.T.matmat(x.T).T + else: + raise ValueError('expected 1-d or 2-d array or matrix, got %r' + % x) + + def __pow__(self, p): + if np.isscalar(p): + return _PowerLinearOperator(self, p) + else: + return NotImplemented + + def __add__(self, x): + if isinstance(x, LinearOperator): + return _SumLinearOperator(self, x) + else: + return NotImplemented + + def __neg__(self): + return _ScaledLinearOperator(self, -1) + + def __sub__(self, x): + return self.__add__(-x) + + def __repr__(self): + M,N = self.shape + if self.dtype is None: + dt = 'unspecified dtype' + else: + dt = 'dtype=' + str(self.dtype) + + return '<%dx%d %s with %s>' % (M, N, self.__class__.__name__, dt) + + def adjoint(self): + """Hermitian adjoint. + + Returns the Hermitian adjoint of self, aka the Hermitian + conjugate or Hermitian transpose. For a complex matrix, the + Hermitian adjoint is equal to the conjugate transpose. + + Can be abbreviated self.H instead of self.adjoint(). + + Returns + ------- + A_H : LinearOperator + Hermitian adjoint of self. + """ + return self._adjoint() + + H = property(adjoint) + + def transpose(self): + """Transpose this linear operator. + + Returns a LinearOperator that represents the transpose of this one. + Can be abbreviated self.T instead of self.transpose(). + """ + return self._transpose() + + T = property(transpose) + + def _adjoint(self): + """Default implementation of _adjoint; defers to rmatvec.""" + return _AdjointLinearOperator(self) + + def _transpose(self): + """ Default implementation of _transpose; defers to rmatvec + conj""" + return _TransposedLinearOperator(self) + + +class _CustomLinearOperator(LinearOperator): + """Linear operator defined in terms of user-specified operations.""" + + def __init__(self, shape, matvec, rmatvec=None, matmat=None, + dtype=None, rmatmat=None): + super().__init__(dtype, shape) + + self.args = () + + self.__matvec_impl = matvec + self.__rmatvec_impl = rmatvec + self.__rmatmat_impl = rmatmat + self.__matmat_impl = matmat + + self._init_dtype() + + def _matmat(self, X): + if self.__matmat_impl is not None: + return self.__matmat_impl(X) + else: + return super()._matmat(X) + + def _matvec(self, x): + return self.__matvec_impl(x) + + def _rmatvec(self, x): + func = self.__rmatvec_impl + if func is None: + raise NotImplementedError("rmatvec is not defined") + return self.__rmatvec_impl(x) + + def _rmatmat(self, X): + if self.__rmatmat_impl is not None: + return self.__rmatmat_impl(X) + else: + return super()._rmatmat(X) + + def _adjoint(self): + return _CustomLinearOperator(shape=(self.shape[1], self.shape[0]), + matvec=self.__rmatvec_impl, + rmatvec=self.__matvec_impl, + matmat=self.__rmatmat_impl, + rmatmat=self.__matmat_impl, + dtype=self.dtype) + + +class _AdjointLinearOperator(LinearOperator): + """Adjoint of arbitrary Linear Operator""" + + def __init__(self, A): + shape = (A.shape[1], A.shape[0]) + super().__init__(dtype=A.dtype, shape=shape) + self.A = A + self.args = (A,) + + def _matvec(self, x): + return self.A._rmatvec(x) + + def _rmatvec(self, x): + return self.A._matvec(x) + + def _matmat(self, x): + return self.A._rmatmat(x) + + def _rmatmat(self, x): + return self.A._matmat(x) + +class _TransposedLinearOperator(LinearOperator): + """Transposition of arbitrary Linear Operator""" + + def __init__(self, A): + shape = (A.shape[1], A.shape[0]) + super().__init__(dtype=A.dtype, shape=shape) + self.A = A + self.args = (A,) + + def _matvec(self, x): + # NB. np.conj works also on sparse matrices + return np.conj(self.A._rmatvec(np.conj(x))) + + def _rmatvec(self, x): + return np.conj(self.A._matvec(np.conj(x))) + + def _matmat(self, x): + # NB. np.conj works also on sparse matrices + return np.conj(self.A._rmatmat(np.conj(x))) + + def _rmatmat(self, x): + return np.conj(self.A._matmat(np.conj(x))) + +def _get_dtype(operators, dtypes=None): + if dtypes is None: + dtypes = [] + for obj in operators: + if obj is not None and hasattr(obj, 'dtype'): + dtypes.append(obj.dtype) + return np.result_type(*dtypes) + + +class _SumLinearOperator(LinearOperator): + def __init__(self, A, B): + if not isinstance(A, LinearOperator) or \ + not isinstance(B, LinearOperator): + raise ValueError('both operands have to be a LinearOperator') + if A.shape != B.shape: + raise ValueError(f'cannot add {A} and {B}: shape mismatch') + self.args = (A, B) + super().__init__(_get_dtype([A, B]), A.shape) + + def _matvec(self, x): + return self.args[0].matvec(x) + self.args[1].matvec(x) + + def _rmatvec(self, x): + return self.args[0].rmatvec(x) + self.args[1].rmatvec(x) + + def _rmatmat(self, x): + return self.args[0].rmatmat(x) + self.args[1].rmatmat(x) + + def _matmat(self, x): + return self.args[0].matmat(x) + self.args[1].matmat(x) + + def _adjoint(self): + A, B = self.args + return A.H + B.H + + +class _ProductLinearOperator(LinearOperator): + def __init__(self, A, B): + if not isinstance(A, LinearOperator) or \ + not isinstance(B, LinearOperator): + raise ValueError('both operands have to be a LinearOperator') + if A.shape[1] != B.shape[0]: + raise ValueError(f'cannot multiply {A} and {B}: shape mismatch') + super().__init__(_get_dtype([A, B]), + (A.shape[0], B.shape[1])) + self.args = (A, B) + + def _matvec(self, x): + return self.args[0].matvec(self.args[1].matvec(x)) + + def _rmatvec(self, x): + return self.args[1].rmatvec(self.args[0].rmatvec(x)) + + def _rmatmat(self, x): + return self.args[1].rmatmat(self.args[0].rmatmat(x)) + + def _matmat(self, x): + return self.args[0].matmat(self.args[1].matmat(x)) + + def _adjoint(self): + A, B = self.args + return B.H * A.H + + +class _ScaledLinearOperator(LinearOperator): + def __init__(self, A, alpha): + if not isinstance(A, LinearOperator): + raise ValueError('LinearOperator expected as A') + if not np.isscalar(alpha): + raise ValueError('scalar expected as alpha') + if isinstance(A, _ScaledLinearOperator): + A, alpha_original = A.args + # Avoid in-place multiplication so that we don't accidentally mutate + # the original prefactor. + alpha = alpha * alpha_original + + dtype = _get_dtype([A], [type(alpha)]) + super().__init__(dtype, A.shape) + self.args = (A, alpha) + + def _matvec(self, x): + return self.args[1] * self.args[0].matvec(x) + + def _rmatvec(self, x): + return np.conj(self.args[1]) * self.args[0].rmatvec(x) + + def _rmatmat(self, x): + return np.conj(self.args[1]) * self.args[0].rmatmat(x) + + def _matmat(self, x): + return self.args[1] * self.args[0].matmat(x) + + def _adjoint(self): + A, alpha = self.args + return A.H * np.conj(alpha) + + +class _PowerLinearOperator(LinearOperator): + def __init__(self, A, p): + if not isinstance(A, LinearOperator): + raise ValueError('LinearOperator expected as A') + if A.shape[0] != A.shape[1]: + raise ValueError('square LinearOperator expected, got %r' % A) + if not isintlike(p) or p < 0: + raise ValueError('non-negative integer expected as p') + + super().__init__(_get_dtype([A]), A.shape) + self.args = (A, p) + + def _power(self, fun, x): + res = np.array(x, copy=True) + for i in range(self.args[1]): + res = fun(res) + return res + + def _matvec(self, x): + return self._power(self.args[0].matvec, x) + + def _rmatvec(self, x): + return self._power(self.args[0].rmatvec, x) + + def _rmatmat(self, x): + return self._power(self.args[0].rmatmat, x) + + def _matmat(self, x): + return self._power(self.args[0].matmat, x) + + def _adjoint(self): + A, p = self.args + return A.H ** p + + +class MatrixLinearOperator(LinearOperator): + def __init__(self, A): + super().__init__(A.dtype, A.shape) + self.A = A + self.__adj = None + self.args = (A,) + + def _matmat(self, X): + return self.A.dot(X) + + def _adjoint(self): + if self.__adj is None: + self.__adj = _AdjointMatrixOperator(self) + return self.__adj + +class _AdjointMatrixOperator(MatrixLinearOperator): + def __init__(self, adjoint): + self.A = adjoint.A.T.conj() + self.__adjoint = adjoint + self.args = (adjoint,) + self.shape = adjoint.shape[1], adjoint.shape[0] + + @property + def dtype(self): + return self.__adjoint.dtype + + def _adjoint(self): + return self.__adjoint + + +class IdentityOperator(LinearOperator): + def __init__(self, shape, dtype=None): + super().__init__(dtype, shape) + + def _matvec(self, x): + return x + + def _rmatvec(self, x): + return x + + def _rmatmat(self, x): + return x + + def _matmat(self, x): + return x + + def _adjoint(self): + return self + + +def aslinearoperator(A): + """Return A as a LinearOperator. + + 'A' may be any of the following types: + - ndarray + - matrix + - sparse matrix (e.g. csr_matrix, lil_matrix, etc.) + - LinearOperator + - An object with .shape and .matvec attributes + + See the LinearOperator documentation for additional information. + + Notes + ----- + If 'A' has no .dtype attribute, the data type is determined by calling + :func:`LinearOperator.matvec()` - set the .dtype attribute to prevent this + call upon the linear operator creation. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import aslinearoperator + >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32) + >>> aslinearoperator(M) + <2x3 MatrixLinearOperator with dtype=int32> + """ + if isinstance(A, LinearOperator): + return A + + elif isinstance(A, np.ndarray) or isinstance(A, np.matrix): + if A.ndim > 2: + raise ValueError('array must have ndim <= 2') + A = np.atleast_2d(np.asarray(A)) + return MatrixLinearOperator(A) + + elif issparse(A) or is_pydata_spmatrix(A): + return MatrixLinearOperator(A) + + else: + if hasattr(A, 'shape') and hasattr(A, 'matvec'): + rmatvec = None + rmatmat = None + dtype = None + + if hasattr(A, 'rmatvec'): + rmatvec = A.rmatvec + if hasattr(A, 'rmatmat'): + rmatmat = A.rmatmat + if hasattr(A, 'dtype'): + dtype = A.dtype + return LinearOperator(A.shape, A.matvec, rmatvec=rmatvec, + rmatmat=rmatmat, dtype=dtype) + + else: + raise TypeError('type not understood') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3b57274542928e79c234bb6955849a90be21990e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/__init__.py @@ -0,0 +1,20 @@ +"Iterative Solvers for Sparse Linear Systems" + +#from info import __doc__ +from .iterative import * +from .minres import minres +from .lgmres import lgmres +from .lsqr import lsqr +from .lsmr import lsmr +from ._gcrotmk import gcrotmk +from .tfqmr import tfqmr + +__all__ = [ + 'bicg', 'bicgstab', 'cg', 'cgs', 'gcrotmk', 'gmres', + 'lgmres', 'lsmr', 'lsqr', + 'minres', 'qmr', 'tfqmr' +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/_gcrotmk.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/_gcrotmk.py new file mode 100644 index 0000000000000000000000000000000000000000..56a398508cbdad2b4541be49a8a7847de29af3bd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/_gcrotmk.py @@ -0,0 +1,514 @@ +# Copyright (C) 2015, Pauli Virtanen +# Distributed under the same license as SciPy. + +import numpy as np +from numpy.linalg import LinAlgError +from scipy.linalg import (get_blas_funcs, qr, solve, svd, qr_insert, lstsq) +from .iterative import _get_atol_rtol +from scipy.sparse.linalg._isolve.utils import make_system +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args + + +__all__ = ['gcrotmk'] + + +def _fgmres(matvec, v0, m, atol, lpsolve=None, rpsolve=None, cs=(), outer_v=(), + prepend_outer_v=False): + """ + FGMRES Arnoldi process, with optional projection or augmentation + + Parameters + ---------- + matvec : callable + Operation A*x + v0 : ndarray + Initial vector, normalized to nrm2(v0) == 1 + m : int + Number of GMRES rounds + atol : float + Absolute tolerance for early exit + lpsolve : callable + Left preconditioner L + rpsolve : callable + Right preconditioner R + cs : list of (ndarray, ndarray) + Columns of matrices C and U in GCROT + outer_v : list of ndarrays + Augmentation vectors in LGMRES + prepend_outer_v : bool, optional + Whether augmentation vectors come before or after + Krylov iterates + + Raises + ------ + LinAlgError + If nans encountered + + Returns + ------- + Q, R : ndarray + QR decomposition of the upper Hessenberg H=QR + B : ndarray + Projections corresponding to matrix C + vs : list of ndarray + Columns of matrix V + zs : list of ndarray + Columns of matrix Z + y : ndarray + Solution to ||H y - e_1||_2 = min! + res : float + The final (preconditioned) residual norm + + """ + + if lpsolve is None: + def lpsolve(x): + return x + if rpsolve is None: + def rpsolve(x): + return x + + axpy, dot, scal, nrm2 = get_blas_funcs(['axpy', 'dot', 'scal', 'nrm2'], (v0,)) + + vs = [v0] + zs = [] + y = None + res = np.nan + + m = m + len(outer_v) + + # Orthogonal projection coefficients + B = np.zeros((len(cs), m), dtype=v0.dtype) + + # H is stored in QR factorized form + Q = np.ones((1, 1), dtype=v0.dtype) + R = np.zeros((1, 0), dtype=v0.dtype) + + eps = np.finfo(v0.dtype).eps + + breakdown = False + + # FGMRES Arnoldi process + for j in range(m): + # L A Z = C B + V H + + if prepend_outer_v and j < len(outer_v): + z, w = outer_v[j] + elif prepend_outer_v and j == len(outer_v): + z = rpsolve(v0) + w = None + elif not prepend_outer_v and j >= m - len(outer_v): + z, w = outer_v[j - (m - len(outer_v))] + else: + z = rpsolve(vs[-1]) + w = None + + if w is None: + w = lpsolve(matvec(z)) + else: + # w is clobbered below + w = w.copy() + + w_norm = nrm2(w) + + # GCROT projection: L A -> (1 - C C^H) L A + # i.e. orthogonalize against C + for i, c in enumerate(cs): + alpha = dot(c, w) + B[i,j] = alpha + w = axpy(c, w, c.shape[0], -alpha) # w -= alpha*c + + # Orthogonalize against V + hcur = np.zeros(j+2, dtype=Q.dtype) + for i, v in enumerate(vs): + alpha = dot(v, w) + hcur[i] = alpha + w = axpy(v, w, v.shape[0], -alpha) # w -= alpha*v + hcur[i+1] = nrm2(w) + + with np.errstate(over='ignore', divide='ignore'): + # Careful with denormals + alpha = 1/hcur[-1] + + if np.isfinite(alpha): + w = scal(alpha, w) + + if not (hcur[-1] > eps * w_norm): + # w essentially in the span of previous vectors, + # or we have nans. Bail out after updating the QR + # solution. + breakdown = True + + vs.append(w) + zs.append(z) + + # Arnoldi LSQ problem + + # Add new column to H=Q@R, padding other columns with zeros + Q2 = np.zeros((j+2, j+2), dtype=Q.dtype, order='F') + Q2[:j+1,:j+1] = Q + Q2[j+1,j+1] = 1 + + R2 = np.zeros((j+2, j), dtype=R.dtype, order='F') + R2[:j+1,:] = R + + Q, R = qr_insert(Q2, R2, hcur, j, which='col', + overwrite_qru=True, check_finite=False) + + # Transformed least squares problem + # || Q R y - inner_res_0 * e_1 ||_2 = min! + # Since R = [R'; 0], solution is y = inner_res_0 (R')^{-1} (Q^H)[:j,0] + + # Residual is immediately known + res = abs(Q[0,-1]) + + # Check for termination + if res < atol or breakdown: + break + + if not np.isfinite(R[j,j]): + # nans encountered, bail out + raise LinAlgError() + + # -- Get the LSQ problem solution + + # The problem is triangular, but the condition number may be + # bad (or in case of breakdown the last diagonal entry may be + # zero), so use lstsq instead of trtrs. + y, _, _, _, = lstsq(R[:j+1,:j+1], Q[0,:j+1].conj()) + + B = B[:,:j+1] + + return Q, R, B, vs, zs, y, res + + +@_deprecate_positional_args(version="1.14.0") +def gcrotmk(A, b, x0=None, *, tol=_NoValue, maxiter=1000, M=None, callback=None, + m=20, k=None, CU=None, discard_C=False, truncate='oldest', + atol=None, rtol=1e-5): + """ + Solve a matrix equation using flexible GCROT(m,k) algorithm. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``rtol=1e-5``, the default for ``atol`` is ``rtol``. + + .. warning:: + + The default value for ``atol`` will be changed to ``0.0`` in + SciPy 1.14.0. + maxiter : int, optional + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator}, optional + Preconditioner for A. The preconditioner should approximate the + inverse of A. gcrotmk is a 'flexible' algorithm and the preconditioner + can vary from iteration to iteration. Effective preconditioning + dramatically improves the rate of convergence, which implies that + fewer iterations are needed to reach a given error tolerance. + callback : function, optional + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + m : int, optional + Number of inner FGMRES iterations per each outer iteration. + Default: 20 + k : int, optional + Number of vectors to carry between inner FGMRES iterations. + According to [2]_, good values are around m. + Default: m + CU : list of tuples, optional + List of tuples ``(c, u)`` which contain the columns of the matrices + C and U in the GCROT(m,k) algorithm. For details, see [2]_. + The list given and vectors contained in it are modified in-place. + If not given, start from empty matrices. The ``c`` elements in the + tuples can be ``None``, in which case the vectors are recomputed + via ``c = A u`` on start and orthogonalized as described in [3]_. + discard_C : bool, optional + Discard the C-vectors at the end. Useful if recycling Krylov subspaces + for different linear systems. + truncate : {'oldest', 'smallest'}, optional + Truncation scheme to use. Drop: oldest vectors, or vectors with + smallest singular values using the scheme discussed in [1,2]. + See [2]_ for detailed comparison. + Default: 'oldest' + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `gcrotmk` keyword argument ``tol`` is deprecated in favor of + ``rtol`` and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The solution found. + info : int + Provides convergence information: + + * 0 : successful exit + * >0 : convergence to tolerance not achieved, number of iterations + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import gcrotmk + >>> R = np.random.randn(5, 5) + >>> A = csc_matrix(R) + >>> b = np.random.randn(5) + >>> x, exit_code = gcrotmk(A, b, atol=1e-5) + >>> print(exit_code) + 0 + >>> np.allclose(A.dot(x), b) + True + + References + ---------- + .. [1] E. de Sturler, ''Truncation strategies for optimal Krylov subspace + methods'', SIAM J. Numer. Anal. 36, 864 (1999). + .. [2] J.E. Hicken and D.W. Zingg, ''A simplified and flexible variant + of GCROT for solving nonsymmetric linear systems'', + SIAM J. Sci. Comput. 32, 172 (2010). + .. [3] M.L. Parks, E. de Sturler, G. Mackey, D.D. Johnson, S. Maiti, + ''Recycling Krylov subspaces for sequences of linear systems'', + SIAM J. Sci. Comput. 28, 1651 (2006). + + """ + A,M,x,b,postprocess = make_system(A,M,x0,b) + + if not np.isfinite(b).all(): + raise ValueError("RHS must contain only finite numbers") + + if truncate not in ('oldest', 'smallest'): + raise ValueError(f"Invalid value for 'truncate': {truncate!r}") + + matvec = A.matvec + psolve = M.matvec + + if CU is None: + CU = [] + + if k is None: + k = m + + axpy, dot, scal = None, None, None + + if x0 is None: + r = b.copy() + else: + r = b - matvec(x) + + axpy, dot, scal, nrm2 = get_blas_funcs(['axpy', 'dot', 'scal', 'nrm2'], (x, r)) + + b_norm = nrm2(b) + + # we call this to get the right atol/rtol and raise warnings as necessary + atol, rtol = _get_atol_rtol('gcrotmk', b_norm, tol, atol, rtol) + + if b_norm == 0: + x = b + return (postprocess(x), 0) + + if discard_C: + CU[:] = [(None, u) for c, u in CU] + + # Reorthogonalize old vectors + if CU: + # Sort already existing vectors to the front + CU.sort(key=lambda cu: cu[0] is not None) + + # Fill-in missing ones + C = np.empty((A.shape[0], len(CU)), dtype=r.dtype, order='F') + us = [] + j = 0 + while CU: + # More memory-efficient: throw away old vectors as we go + c, u = CU.pop(0) + if c is None: + c = matvec(u) + C[:,j] = c + j += 1 + us.append(u) + + # Orthogonalize + Q, R, P = qr(C, overwrite_a=True, mode='economic', pivoting=True) + del C + + # C := Q + cs = list(Q.T) + + # U := U P R^-1, back-substitution + new_us = [] + for j in range(len(cs)): + u = us[P[j]] + for i in range(j): + u = axpy(us[P[i]], u, u.shape[0], -R[i,j]) + if abs(R[j,j]) < 1e-12 * abs(R[0,0]): + # discard rest of the vectors + break + u = scal(1.0/R[j,j], u) + new_us.append(u) + + # Form the new CU lists + CU[:] = list(zip(cs, new_us))[::-1] + + if CU: + axpy, dot = get_blas_funcs(['axpy', 'dot'], (r,)) + + # Solve first the projection operation with respect to the CU + # vectors. This corresponds to modifying the initial guess to + # be + # + # x' = x + U y + # y = argmin_y || b - A (x + U y) ||^2 + # + # The solution is y = C^H (b - A x) + for c, u in CU: + yc = dot(c, r) + x = axpy(u, x, x.shape[0], yc) + r = axpy(c, r, r.shape[0], -yc) + + # GCROT main iteration + for j_outer in range(maxiter): + # -- callback + if callback is not None: + callback(x) + + beta = nrm2(r) + + # -- check stopping condition + beta_tol = max(atol, rtol * b_norm) + + if beta <= beta_tol and (j_outer > 0 or CU): + # recompute residual to avoid rounding error + r = b - matvec(x) + beta = nrm2(r) + + if beta <= beta_tol: + j_outer = -1 + break + + ml = m + max(k - len(CU), 0) + + cs = [c for c, u in CU] + + try: + Q, R, B, vs, zs, y, pres = _fgmres(matvec, + r/beta, + ml, + rpsolve=psolve, + atol=max(atol, rtol*b_norm)/beta, + cs=cs) + y *= beta + except LinAlgError: + # Floating point over/underflow, non-finite result from + # matmul etc. -- report failure. + break + + # + # At this point, + # + # [A U, A Z] = [C, V] G; G = [ I B ] + # [ 0 H ] + # + # where [C, V] has orthonormal columns, and r = beta v_0. Moreover, + # + # || b - A (x + Z y + U q) ||_2 = || r - C B y - V H y - C q ||_2 = min! + # + # from which y = argmin_y || beta e_1 - H y ||_2, and q = -B y + # + + # + # GCROT(m,k) update + # + + # Define new outer vectors + + # ux := (Z - U B) y + ux = zs[0]*y[0] + for z, yc in zip(zs[1:], y[1:]): + ux = axpy(z, ux, ux.shape[0], yc) # ux += z*yc + by = B.dot(y) + for cu, byc in zip(CU, by): + c, u = cu + ux = axpy(u, ux, ux.shape[0], -byc) # ux -= u*byc + + # cx := V H y + hy = Q.dot(R.dot(y)) + cx = vs[0] * hy[0] + for v, hyc in zip(vs[1:], hy[1:]): + cx = axpy(v, cx, cx.shape[0], hyc) # cx += v*hyc + + # Normalize cx, maintaining cx = A ux + # This new cx is orthogonal to the previous C, by construction + try: + alpha = 1/nrm2(cx) + if not np.isfinite(alpha): + raise FloatingPointError() + except (FloatingPointError, ZeroDivisionError): + # Cannot update, so skip it + continue + + cx = scal(alpha, cx) + ux = scal(alpha, ux) + + # Update residual and solution + gamma = dot(cx, r) + r = axpy(cx, r, r.shape[0], -gamma) # r -= gamma*cx + x = axpy(ux, x, x.shape[0], gamma) # x += gamma*ux + + # Truncate CU + if truncate == 'oldest': + while len(CU) >= k and CU: + del CU[0] + elif truncate == 'smallest': + if len(CU) >= k and CU: + # cf. [1,2] + D = solve(R[:-1,:].T, B.T).T + W, sigma, V = svd(D) + + # C := C W[:,:k-1], U := U W[:,:k-1] + new_CU = [] + for j, w in enumerate(W[:,:k-1].T): + c, u = CU[0] + c = c * w[0] + u = u * w[0] + for cup, wp in zip(CU[1:], w[1:]): + cp, up = cup + c = axpy(cp, c, c.shape[0], wp) + u = axpy(up, u, u.shape[0], wp) + + # Reorthogonalize at the same time; not necessary + # in exact arithmetic, but floating point error + # tends to accumulate here + for cp, up in new_CU: + alpha = dot(cp, c) + c = axpy(cp, c, c.shape[0], -alpha) + u = axpy(up, u, u.shape[0], -alpha) + alpha = nrm2(c) + c = scal(1.0/alpha, c) + u = scal(1.0/alpha, u) + + new_CU.append((c, u)) + CU[:] = new_CU + + # Add new vector to CU + CU.append((cx, ux)) + + # Include the solution vector to the span + CU.append((None, x.copy())) + if discard_C: + CU[:] = [(None, uz) for cz, uz in CU] + + return postprocess(x), j_outer + 1 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/iterative.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/iterative.py new file mode 100644 index 0000000000000000000000000000000000000000..11f71914320de60e7f8b3e3b5cd0574d15ec438e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/iterative.py @@ -0,0 +1,1079 @@ +import warnings +import numpy as np +from scipy.sparse.linalg._interface import LinearOperator +from .utils import make_system +from scipy.linalg import get_lapack_funcs +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args + +__all__ = ['bicg', 'bicgstab', 'cg', 'cgs', 'gmres', 'qmr'] + + +def _get_atol_rtol(name, b_norm, tol=_NoValue, atol=0., rtol=1e-5): + """ + A helper function to handle tolerance deprecations and normalization + """ + if tol is not _NoValue: + msg = (f"'scipy.sparse.linalg.{name}' keyword argument `tol` is " + "deprecated in favor of `rtol` and will be removed in SciPy " + "v1.14.0. Until then, if set, it will override `rtol`.") + warnings.warn(msg, category=DeprecationWarning, stacklevel=4) + rtol = float(tol) if tol is not None else rtol + + if atol == 'legacy': + msg = (f"'scipy.sparse.linalg.{name}' called with `atol='legacy'`. " + "This behavior is deprecated and will result in an error in " + "SciPy v1.14.0. To preserve current behaviour, set `atol=0.0`.") + warnings.warn(msg, category=DeprecationWarning, stacklevel=4) + atol = 0 + + # this branch is only hit from gcrotmk/lgmres/tfqmr + if atol is None: + msg = (f"'scipy.sparse.linalg.{name}' called without specifying " + "`atol`. This behavior is deprecated and will result in an " + "error in SciPy v1.14.0. To preserve current behaviour, set " + "`atol=rtol`, or, to adopt the future default, set `atol=0.0`.") + warnings.warn(msg, category=DeprecationWarning, stacklevel=4) + atol = rtol + + atol = max(float(atol), float(rtol) * float(b_norm)) + + return atol, rtol + + +@_deprecate_positional_args(version="1.14") +def bicg(A, b, x0=None, *, tol=_NoValue, maxiter=None, M=None, callback=None, + atol=0., rtol=1e-5): + """Use BIConjugate Gradient iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `bicg` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import bicg + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1.]]) + >>> b = np.array([2., 4., -1.]) + >>> x, exitCode = bicg(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('bicg', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec, rmatvec = A.matvec, A.rmatvec + psolve, rpsolve = M.matvec, M.rmatvec + + rhotol = np.finfo(x.dtype.char).eps**2 + + # Dummy values to initialize vars, silence linter warnings + rho_prev, p, ptilde = None, None, None + + r = b - matvec(x) if x.any() else b.copy() + rtilde = r.copy() + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + z = psolve(r) + ztilde = rpsolve(rtilde) + # order matters in this dot product + rho_cur = dotprod(rtilde, z) + + if np.abs(rho_cur) < rhotol: # Breakdown case + return postprocess, -10 + + if iteration > 0: + beta = rho_cur / rho_prev + p *= beta + p += z + ptilde *= beta.conj() + ptilde += ztilde + else: # First spin + p = z.copy() + ptilde = ztilde.copy() + + q = matvec(p) + qtilde = rmatvec(ptilde) + rv = dotprod(ptilde, q) + + if rv == 0: + return postprocess(x), -11 + + alpha = rho_cur / rv + x += alpha*p + r -= alpha*q + rtilde -= alpha.conj()*qtilde + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +@_deprecate_positional_args(version="1.14") +def bicgstab(A, b, *, x0=None, tol=_NoValue, maxiter=None, M=None, + callback=None, atol=0., rtol=1e-5): + """Use BIConjugate Gradient STABilized iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `bicgstab` keyword argument ``tol`` is deprecated in favor of + ``rtol`` and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import bicgstab + >>> R = np.array([[4, 2, 0, 1], + ... [3, 0, 0, 2], + ... [0, 1, 1, 1], + ... [0, 2, 1, 0]]) + >>> A = csc_matrix(R) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = bicgstab(A, b, atol=1e-5) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('bicgstab', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec = A.matvec + psolve = M.matvec + + # These values make no sense but coming from original Fortran code + # sqrt might have been meant instead. + rhotol = np.finfo(x.dtype.char).eps**2 + omegatol = rhotol + + # Dummy values to initialize vars, silence linter warnings + rho_prev, omega, alpha, p, v = None, None, None, None, None + + r = b - matvec(x) if x.any() else b.copy() + rtilde = r.copy() + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + rho = dotprod(rtilde, r) + if np.abs(rho) < rhotol: # rho breakdown + return postprocess(x), -10 + + if iteration > 0: + if np.abs(omega) < omegatol: # omega breakdown + return postprocess(x), -11 + + beta = (rho / rho_prev) * (alpha / omega) + p -= omega*v + p *= beta + p += r + else: # First spin + s = np.empty_like(r) + p = r.copy() + + phat = psolve(p) + v = matvec(phat) + rv = dotprod(rtilde, v) + if rv == 0: + return postprocess(x), -11 + alpha = rho / rv + r -= alpha*v + s[:] = r[:] + + if np.linalg.norm(s) < atol: + x += alpha*phat + return postprocess(x), 0 + + shat = psolve(s) + t = matvec(shat) + omega = dotprod(t, s) / dotprod(t, t) + x += alpha*phat + x += omega*shat + r -= omega*t + rho_prev = rho + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +@_deprecate_positional_args(version="1.14") +def cg(A, b, x0=None, *, tol=_NoValue, maxiter=None, M=None, callback=None, + atol=0., rtol=1e-5): + """Use Conjugate Gradient iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + ``A`` must represent a hermitian, positive definite matrix. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `cg` keyword argument ``tol`` is deprecated in favor of ``rtol`` and + will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import cg + >>> P = np.array([[4, 0, 1, 0], + ... [0, 5, 0, 0], + ... [1, 0, 3, 2], + ... [0, 0, 2, 4]]) + >>> A = csc_matrix(P) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = cg(A, b, atol=1e-5) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('cg', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + if maxiter is None: + maxiter = n*10 + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + matvec = A.matvec + psolve = M.matvec + r = b - matvec(x) if x.any() else b.copy() + + # Dummy value to initialize var, silences warnings + rho_prev, p = None, None + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + z = psolve(r) + rho_cur = dotprod(r, z) + if iteration > 0: + beta = rho_cur / rho_prev + p *= beta + p += z + else: # First spin + p = np.empty_like(r) + p[:] = z[:] + + q = matvec(p) + alpha = rho_cur / dotprod(p, q) + x += alpha*p + r -= alpha*q + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +@_deprecate_positional_args(version="1.14") +def cgs(A, b, x0=None, *, tol=_NoValue, maxiter=None, M=None, callback=None, + atol=0., rtol=1e-5): + """Use Conjugate Gradient Squared iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real-valued N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `cgs` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import cgs + >>> R = np.array([[4, 2, 0, 1], + ... [3, 0, 0, 2], + ... [0, 1, 1, 1], + ... [0, 2, 1, 0]]) + >>> A = csc_matrix(R) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = cgs(A, b) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('cgs', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec = A.matvec + psolve = M.matvec + + rhotol = np.finfo(x.dtype.char).eps**2 + + r = b - matvec(x) if x.any() else b.copy() + + rtilde = r.copy() + bnorm = np.linalg.norm(b) + if bnorm == 0: + bnorm = 1 + + # Dummy values to initialize vars, silence linter warnings + rho_prev, p, u, q = None, None, None, None + + for iteration in range(maxiter): + rnorm = np.linalg.norm(r) + if rnorm < atol: # Are we done? + return postprocess(x), 0 + + rho_cur = dotprod(rtilde, r) + if np.abs(rho_cur) < rhotol: # Breakdown case + return postprocess, -10 + + if iteration > 0: + beta = rho_cur / rho_prev + + # u = r + beta * q + # p = u + beta * (q + beta * p); + u[:] = r[:] + u += beta*q + + p *= beta + p += q + p *= beta + p += u + + else: # First spin + p = r.copy() + u = r.copy() + q = np.empty_like(r) + + phat = psolve(p) + vhat = matvec(phat) + rv = dotprod(rtilde, vhat) + + if rv == 0: # Dot product breakdown + return postprocess(x), -11 + + alpha = rho_cur / rv + q[:] = u[:] + q -= alpha*vhat + uhat = psolve(u + q) + x += alpha*uhat + + # Due to numerical error build-up the actual residual is computed + # instead of the following two lines that were in the original + # FORTRAN templates, still using a single matvec. + + # qhat = matvec(uhat) + # r -= alpha*qhat + r = b - matvec(x) + + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +@_deprecate_positional_args(version="1.14") +def gmres(A, b, x0=None, *, tol=_NoValue, restart=None, maxiter=None, M=None, + callback=None, restrt=_NoValue, atol=0., callback_type=None, + rtol=1e-5): + """ + Use Generalized Minimal RESidual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution (a vector of zeros by default). + atol, rtol : float + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + restart : int, optional + Number of iterations between restarts. Larger values increase + iteration cost, but may be necessary for convergence. + If omitted, ``min(20, n)`` is used. + maxiter : int, optional + Maximum number of iterations (restart cycles). Iteration will stop + after maxiter steps even if the specified tolerance has not been + achieved. See `callback_type`. + M : {sparse matrix, ndarray, LinearOperator} + Inverse of the preconditioner of A. M should approximate the + inverse of A and be easy to solve for (see Notes). Effective + preconditioning dramatically improves the rate of convergence, + which implies that fewer iterations are needed to reach a given + error tolerance. By default, no preconditioner is used. + In this implementation, left preconditioning is used, + and the preconditioned residual is minimized. However, the final + convergence is tested with respect to the ``b - A @ x`` residual. + callback : function + User-supplied function to call after each iteration. It is called + as `callback(args)`, where `args` are selected by `callback_type`. + callback_type : {'x', 'pr_norm', 'legacy'}, optional + Callback function argument requested: + - ``x``: current iterate (ndarray), called on every restart + - ``pr_norm``: relative (preconditioned) residual norm (float), + called on every inner iteration + - ``legacy`` (default): same as ``pr_norm``, but also changes the + meaning of `maxiter` to count inner iterations instead of restart + cycles. + + This keyword has no effect if `callback` is not set. + restrt : int, optional, deprecated + + .. deprecated:: 0.11.0 + `gmres` keyword argument ``restrt`` is deprecated in favor of + ``restart`` and will be removed in SciPy 1.14.0. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `gmres` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0 + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + + See Also + -------- + LinearOperator + + Notes + ----- + A preconditioner, P, is chosen such that P is close to A but easy to solve + for. The preconditioner parameter required by this routine is + ``M = P^-1``. The inverse should preferably not be calculated + explicitly. Rather, use the following template to produce M:: + + # Construct a linear operator that computes P^-1 @ x. + import scipy.sparse.linalg as spla + M_x = lambda x: spla.spsolve(P, x) + M = spla.LinearOperator((n, n), M_x) + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import gmres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = gmres(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + + # Handle the deprecation frenzy + if restrt not in (None, _NoValue) and restart: + raise ValueError("Cannot specify both 'restart' and 'restrt'" + " keywords. Also 'rstrt' is deprecated." + " and will be removed in SciPy 1.14.0. Use " + "'restart' instead.") + if restrt is not _NoValue: + msg = ("'gmres' keyword argument 'restrt' is deprecated " + "in favor of 'restart' and will be removed in SciPy" + " 1.14.0. Until then, if set, 'rstrt' will override 'restart'." + ) + warnings.warn(msg, DeprecationWarning, stacklevel=3) + restart = restrt + + if callback is not None and callback_type is None: + # Warn about 'callback_type' semantic changes. + # Probably should be removed only in far future, Scipy 2.0 or so. + msg = ("scipy.sparse.linalg.gmres called without specifying " + "`callback_type`. The default value will be changed in" + " a future release. For compatibility, specify a value " + "for `callback_type` explicitly, e.g., " + "``gmres(..., callback_type='pr_norm')``, or to retain the " + "old behavior ``gmres(..., callback_type='legacy')``" + ) + warnings.warn(msg, category=DeprecationWarning, stacklevel=3) + + if callback_type is None: + callback_type = 'legacy' + + if callback_type not in ('x', 'pr_norm', 'legacy'): + raise ValueError(f"Unknown callback_type: {callback_type!r}") + + if callback is None: + callback_type = None + + A, M, x, b, postprocess = make_system(A, M, x0, b) + matvec = A.matvec + psolve = M.matvec + n = len(b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('gmres', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + eps = np.finfo(x.dtype.char).eps + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + if restart is None: + restart = 20 + restart = min(restart, n) + + Mb_nrm2 = np.linalg.norm(psolve(b)) + + # ==================================================== + # =========== Tolerance control from gh-8400 ========= + # ==================================================== + # Tolerance passed to GMRESREVCOM applies to the inner + # iteration and deals with the left-preconditioned + # residual. + ptol_max_factor = 1. + ptol = Mb_nrm2 * min(ptol_max_factor, atol / bnrm2) + presid = 0. + # ==================================================== + lartg = get_lapack_funcs('lartg', dtype=x.dtype) + + # allocate internal variables + v = np.empty([restart+1, n], dtype=x.dtype) + h = np.zeros([restart, restart+1], dtype=x.dtype) + givens = np.zeros([restart, 2], dtype=x.dtype) + + # legacy iteration count + inner_iter = 0 + + for iteration in range(maxiter): + if iteration == 0: + r = b - matvec(x) if x.any() else b.copy() + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + v[0, :] = psolve(r) + tmp = np.linalg.norm(v[0, :]) + v[0, :] *= (1 / tmp) + # RHS of the Hessenberg problem + S = np.zeros(restart+1, dtype=x.dtype) + S[0] = tmp + + breakdown = False + for col in range(restart): + av = matvec(v[col, :]) + w = psolve(av) + + # Modified Gram-Schmidt + h0 = np.linalg.norm(w) + for k in range(col+1): + tmp = dotprod(v[k, :], w) + h[col, k] = tmp + w -= tmp*v[k, :] + + h1 = np.linalg.norm(w) + h[col, col + 1] = h1 + v[col + 1, :] = w[:] + + # Exact solution indicator + if h1 <= eps*h0: + h[col, col + 1] = 0 + breakdown = True + else: + v[col + 1, :] *= (1 / h1) + + # apply past Givens rotations to current h column + for k in range(col): + c, s = givens[k, 0], givens[k, 1] + n0, n1 = h[col, [k, k+1]] + h[col, [k, k + 1]] = [c*n0 + s*n1, -s.conj()*n0 + c*n1] + + # get and apply current rotation to h and S + c, s, mag = lartg(h[col, col], h[col, col+1]) + givens[col, :] = [c, s] + h[col, [col, col+1]] = mag, 0 + + # S[col+1] component is always 0 + tmp = -np.conjugate(s)*S[col] + S[[col, col + 1]] = [c*S[col], tmp] + presid = np.abs(tmp) + inner_iter += 1 + + if callback_type in ('legacy', 'pr_norm'): + callback(presid / bnrm2) + # Legacy behavior + if callback_type == 'legacy' and inner_iter == maxiter: + break + if presid <= ptol or breakdown: + break + + # Solve h(col, col) upper triangular system and allow pseudo-solve + # singular cases as in (but without the f2py copies): + # y = trsv(h[:col+1, :col+1].T, S[:col+1]) + + if h[col, col] == 0: + S[col] = 0 + + y = np.zeros([col+1], dtype=x.dtype) + y[:] = S[:col+1] + for k in range(col, 0, -1): + if y[k] != 0: + y[k] /= h[k, k] + tmp = y[k] + y[:k] -= tmp*h[k, :k] + if y[0] != 0: + y[0] /= h[0, 0] + + x += y @ v[:col+1, :] + + r = b - matvec(x) + rnorm = np.linalg.norm(r) + + # Legacy exit + if callback_type == 'legacy' and inner_iter == maxiter: + return postprocess(x), 0 if rnorm <= atol else maxiter + + if callback_type == 'x': + callback(x) + + if rnorm <= atol: + break + elif breakdown: + # Reached breakdown (= exact solution), but the external + # tolerance check failed. Bail out with failure. + break + elif presid <= ptol: + # Inner loop passed but outer didn't + ptol_max_factor = max(eps, 0.25 * ptol_max_factor) + else: + ptol_max_factor = min(1.0, 1.5 * ptol_max_factor) + + ptol = presid * min(ptol_max_factor, atol / rnorm) + + info = 0 if (rnorm <= atol) else maxiter + return postprocess(x), info + + +@_deprecate_positional_args(version="1.14") +def qmr(A, b, x0=None, *, tol=_NoValue, maxiter=None, M1=None, M2=None, + callback=None, atol=0., rtol=1e-5): + """Use Quasi-Minimal Residual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real-valued N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + atol, rtol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M1 : {sparse matrix, ndarray, LinearOperator} + Left preconditioner for A. + M2 : {sparse matrix, ndarray, LinearOperator} + Right preconditioner for A. Used together with the left + preconditioner M1. The matrix M1@A@M2 should have better + conditioned than A alone. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `qmr` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + See Also + -------- + LinearOperator + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import qmr + >>> A = csc_matrix([[3., 2., 0.], [1., -1., 0.], [0., 5., 1.]]) + >>> b = np.array([2., 4., -1.]) + >>> x, exitCode = qmr(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + A_ = A + A, M, x, b, postprocess = make_system(A, None, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('qmr', bnrm2, tol, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + if M1 is None and M2 is None: + if hasattr(A_, 'psolve'): + def left_psolve(b): + return A_.psolve(b, 'left') + + def right_psolve(b): + return A_.psolve(b, 'right') + + def left_rpsolve(b): + return A_.rpsolve(b, 'left') + + def right_rpsolve(b): + return A_.rpsolve(b, 'right') + M1 = LinearOperator(A.shape, + matvec=left_psolve, + rmatvec=left_rpsolve) + M2 = LinearOperator(A.shape, + matvec=right_psolve, + rmatvec=right_rpsolve) + else: + def id(b): + return b + M1 = LinearOperator(A.shape, matvec=id, rmatvec=id) + M2 = LinearOperator(A.shape, matvec=id, rmatvec=id) + + n = len(b) + if maxiter is None: + maxiter = n*10 + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + rhotol = np.finfo(x.dtype.char).eps + betatol = rhotol + gammatol = rhotol + deltatol = rhotol + epsilontol = rhotol + xitol = rhotol + + r = b - A.matvec(x) if x.any() else b.copy() + + vtilde = r.copy() + y = M1.matvec(vtilde) + rho = np.linalg.norm(y) + wtilde = r.copy() + z = M2.rmatvec(wtilde) + xi = np.linalg.norm(z) + gamma, eta, theta = 1, -1, 0 + v = np.empty_like(vtilde) + w = np.empty_like(wtilde) + + # Dummy values to initialize vars, silence linter warnings + epsilon, q, d, p, s = None, None, None, None, None + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + if np.abs(rho) < rhotol: # rho breakdown + return postprocess(x), -10 + if np.abs(xi) < xitol: # xi breakdown + return postprocess(x), -15 + + v[:] = vtilde[:] + v *= (1 / rho) + y *= (1 / rho) + w[:] = wtilde[:] + w *= (1 / xi) + z *= (1 / xi) + delta = dotprod(z, y) + + if np.abs(delta) < deltatol: # delta breakdown + return postprocess(x), -13 + + ytilde = M2.matvec(y) + ztilde = M1.rmatvec(z) + + if iteration > 0: + ytilde -= (xi * delta / epsilon) * p + p[:] = ytilde[:] + ztilde -= (rho * (delta / epsilon).conj()) * q + q[:] = ztilde[:] + else: # First spin + p = ytilde.copy() + q = ztilde.copy() + + ptilde = A.matvec(p) + epsilon = dotprod(q, ptilde) + if np.abs(epsilon) < epsilontol: # epsilon breakdown + return postprocess(x), -14 + + beta = epsilon / delta + if np.abs(beta) < betatol: # beta breakdown + return postprocess(x), -11 + + vtilde[:] = ptilde[:] + vtilde -= beta*v + y = M1.matvec(vtilde) + + rho_prev = rho + rho = np.linalg.norm(y) + wtilde[:] = w[:] + wtilde *= - beta.conj() + wtilde += A.rmatvec(q) + z = M2.rmatvec(wtilde) + xi = np.linalg.norm(z) + gamma_prev = gamma + theta_prev = theta + theta = rho / (gamma_prev * np.abs(beta)) + gamma = 1 / np.sqrt(1 + theta**2) + + if np.abs(gamma) < gammatol: # gamma breakdown + return postprocess(x), -12 + + eta *= -(rho_prev / beta) * (gamma / gamma_prev)**2 + + if iteration > 0: + d *= (theta_prev * gamma) ** 2 + d += eta*p + s *= (theta_prev * gamma) ** 2 + s += eta*ptilde + else: + d = p.copy() + d *= eta + s = ptilde.copy() + s *= eta + + x += d + r -= s + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lgmres.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lgmres.py new file mode 100644 index 0000000000000000000000000000000000000000..012479576def42c2daf3670270717c039da0d39c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lgmres.py @@ -0,0 +1,242 @@ +# Copyright (C) 2009, Pauli Virtanen +# Distributed under the same license as SciPy. + +import numpy as np +from numpy.linalg import LinAlgError +from scipy.linalg import get_blas_funcs +from .iterative import _get_atol_rtol +from .utils import make_system +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args + +from ._gcrotmk import _fgmres + +__all__ = ['lgmres'] + + +@_deprecate_positional_args(version="1.14.0") +def lgmres(A, b, x0=None, *, tol=_NoValue, maxiter=1000, M=None, callback=None, + inner_m=30, outer_k=3, outer_v=None, store_outer_Av=True, + prepend_outer_v=False, atol=None, rtol=1e-5): + """ + Solve a matrix equation using the LGMRES algorithm. + + The LGMRES algorithm [1]_ [2]_ is designed to avoid some problems + in the convergence in restarted GMRES, and often converges in fewer + iterations. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``rtol=1e-5``, the default for ``atol`` is ``rtol``. + + .. warning:: + + The default value for ``atol`` will be changed to ``0.0`` in + SciPy 1.14.0. + maxiter : int, optional + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator}, optional + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function, optional + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + inner_m : int, optional + Number of inner GMRES iterations per each outer iteration. + outer_k : int, optional + Number of vectors to carry between inner GMRES iterations. + According to [1]_, good values are in the range of 1...3. + However, note that if you want to use the additional vectors to + accelerate solving multiple similar problems, larger values may + be beneficial. + outer_v : list of tuples, optional + List containing tuples ``(v, Av)`` of vectors and corresponding + matrix-vector products, used to augment the Krylov subspace, and + carried between inner GMRES iterations. The element ``Av`` can + be `None` if the matrix-vector product should be re-evaluated. + This parameter is modified in-place by `lgmres`, and can be used + to pass "guess" vectors in and out of the algorithm when solving + similar problems. + store_outer_Av : bool, optional + Whether LGMRES should store also A@v in addition to vectors `v` + in the `outer_v` list. Default is True. + prepend_outer_v : bool, optional + Whether to put outer_v augmentation vectors before Krylov iterates. + In standard LGMRES, prepend_outer_v=False. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `lgmres` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + + - 0 : successful exit + - >0 : convergence to tolerance not achieved, number of iterations + - <0 : illegal input or breakdown + + Notes + ----- + The LGMRES algorithm [1]_ [2]_ is designed to avoid the + slowing of convergence in restarted GMRES, due to alternating + residual vectors. Typically, it often outperforms GMRES(m) of + comparable memory requirements by some measure, or at least is not + much worse. + + Another advantage in this algorithm is that you can supply it with + 'guess' vectors in the `outer_v` argument that augment the Krylov + subspace. If the solution lies close to the span of these vectors, + the algorithm converges faster. This can be useful if several very + similar matrices need to be inverted one after another, such as in + Newton-Krylov iteration where the Jacobian matrix often changes + little in the nonlinear steps. + + References + ---------- + .. [1] A.H. Baker and E.R. Jessup and T. Manteuffel, "A Technique for + Accelerating the Convergence of Restarted GMRES", SIAM J. Matrix + Anal. Appl. 26, 962 (2005). + .. [2] A.H. Baker, "On Improving the Performance of the Linear Solver + restarted GMRES", PhD thesis, University of Colorado (2003). + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lgmres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = lgmres(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + A,M,x,b,postprocess = make_system(A,M,x0,b) + + if not np.isfinite(b).all(): + raise ValueError("RHS must contain only finite numbers") + + matvec = A.matvec + psolve = M.matvec + + if outer_v is None: + outer_v = [] + + axpy, dot, scal = None, None, None + nrm2 = get_blas_funcs('nrm2', [b]) + + b_norm = nrm2(b) + + # we call this to get the right atol/rtol and raise warnings as necessary + atol, rtol = _get_atol_rtol('lgmres', b_norm, tol, atol, rtol) + + if b_norm == 0: + x = b + return (postprocess(x), 0) + + ptol_max_factor = 1.0 + + for k_outer in range(maxiter): + r_outer = matvec(x) - b + + # -- callback + if callback is not None: + callback(x) + + # -- determine input type routines + if axpy is None: + if np.iscomplexobj(r_outer) and not np.iscomplexobj(x): + x = x.astype(r_outer.dtype) + axpy, dot, scal, nrm2 = get_blas_funcs(['axpy', 'dot', 'scal', 'nrm2'], + (x, r_outer)) + + # -- check stopping condition + r_norm = nrm2(r_outer) + if r_norm <= max(atol, rtol * b_norm): + break + + # -- inner LGMRES iteration + v0 = -psolve(r_outer) + inner_res_0 = nrm2(v0) + + if inner_res_0 == 0: + rnorm = nrm2(r_outer) + raise RuntimeError("Preconditioner returned a zero vector; " + "|v| ~ %.1g, |M v| = 0" % rnorm) + + v0 = scal(1.0/inner_res_0, v0) + + ptol = min(ptol_max_factor, max(atol, rtol*b_norm)/r_norm) + + try: + Q, R, B, vs, zs, y, pres = _fgmres(matvec, + v0, + inner_m, + lpsolve=psolve, + atol=ptol, + outer_v=outer_v, + prepend_outer_v=prepend_outer_v) + y *= inner_res_0 + if not np.isfinite(y).all(): + # Overflow etc. in computation. There's no way to + # recover from this, so we have to bail out. + raise LinAlgError() + except LinAlgError: + # Floating point over/underflow, non-finite result from + # matmul etc. -- report failure. + return postprocess(x), k_outer + 1 + + # Inner loop tolerance control + if pres > ptol: + ptol_max_factor = min(1.0, 1.5 * ptol_max_factor) + else: + ptol_max_factor = max(1e-16, 0.25 * ptol_max_factor) + + # -- GMRES terminated: eval solution + dx = zs[0]*y[0] + for w, yc in zip(zs[1:], y[1:]): + dx = axpy(w, dx, dx.shape[0], yc) # dx += w*yc + + # -- Store LGMRES augmentation vectors + nx = nrm2(dx) + if nx > 0: + if store_outer_Av: + q = Q.dot(R.dot(y)) + ax = vs[0]*q[0] + for v, qc in zip(vs[1:], q[1:]): + ax = axpy(v, ax, ax.shape[0], qc) + outer_v.append((dx/nx, ax/nx)) + else: + outer_v.append((dx/nx, None)) + + # -- Retain only a finite number of augmentation vectors + while len(outer_v) > outer_k: + del outer_v[0] + + # -- Apply step + x += dx + else: + # didn't converge ... + return postprocess(x), maxiter + + return postprocess(x), 0 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsmr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsmr.py new file mode 100644 index 0000000000000000000000000000000000000000..e9dd114a78b558742a4cea0ec8847378607f940d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsmr.py @@ -0,0 +1,486 @@ +""" +Copyright (C) 2010 David Fong and Michael Saunders + +LSMR uses an iterative method. + +07 Jun 2010: Documentation updated +03 Jun 2010: First release version in Python + +David Chin-lung Fong clfong@stanford.edu +Institute for Computational and Mathematical Engineering +Stanford University + +Michael Saunders saunders@stanford.edu +Systems Optimization Laboratory +Dept of MS&E, Stanford University. + +""" + +__all__ = ['lsmr'] + +from numpy import zeros, inf, atleast_1d, result_type +from numpy.linalg import norm +from math import sqrt +from scipy.sparse.linalg._interface import aslinearoperator + +from scipy.sparse.linalg._isolve.lsqr import _sym_ortho + + +def lsmr(A, b, damp=0.0, atol=1e-6, btol=1e-6, conlim=1e8, + maxiter=None, show=False, x0=None): + """Iterative solver for least-squares problems. + + lsmr solves the system of linear equations ``Ax = b``. If the system + is inconsistent, it solves the least-squares problem ``min ||b - Ax||_2``. + ``A`` is a rectangular matrix of dimension m-by-n, where all cases are + allowed: m = n, m > n, or m < n. ``b`` is a vector of length m. + The matrix A may be dense or sparse (usually sparse). + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + Matrix A in the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^H x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : array_like, shape (m,) + Vector ``b`` in the linear system. + damp : float + Damping factor for regularized least-squares. `lsmr` solves + the regularized least-squares problem:: + + min ||(b) - ( A )x|| + ||(0) (damp*I) ||_2 + + where damp is a scalar. If damp is None or 0, the system + is solved without regularization. Default is 0. + atol, btol : float, optional + Stopping tolerances. `lsmr` continues iterations until a + certain backward error estimate is smaller than some quantity + depending on atol and btol. Let ``r = b - Ax`` be the + residual vector for the current approximate solution ``x``. + If ``Ax = b`` seems to be consistent, `lsmr` terminates + when ``norm(r) <= atol * norm(A) * norm(x) + btol * norm(b)``. + Otherwise, `lsmr` terminates when ``norm(A^H r) <= + atol * norm(A) * norm(r)``. If both tolerances are 1.0e-6 (default), + the final ``norm(r)`` should be accurate to about 6 + digits. (The final ``x`` will usually have fewer correct digits, + depending on ``cond(A)`` and the size of LAMBDA.) If `atol` + or `btol` is None, a default value of 1.0e-6 will be used. + Ideally, they should be estimates of the relative error in the + entries of ``A`` and ``b`` respectively. For example, if the entries + of ``A`` have 7 correct digits, set ``atol = 1e-7``. This prevents + the algorithm from doing unnecessary work beyond the + uncertainty of the input data. + conlim : float, optional + `lsmr` terminates if an estimate of ``cond(A)`` exceeds + `conlim`. For compatible systems ``Ax = b``, conlim could be + as large as 1.0e+12 (say). For least-squares problems, + `conlim` should be less than 1.0e+8. If `conlim` is None, the + default value is 1e+8. Maximum precision can be obtained by + setting ``atol = btol = conlim = 0``, but the number of + iterations may then be excessive. Default is 1e8. + maxiter : int, optional + `lsmr` terminates if the number of iterations reaches + `maxiter`. The default is ``maxiter = min(m, n)``. For + ill-conditioned systems, a larger value of `maxiter` may be + needed. Default is False. + show : bool, optional + Print iterations logs if ``show=True``. Default is False. + x0 : array_like, shape (n,), optional + Initial guess of ``x``, if None zeros are used. Default is None. + + .. versionadded:: 1.0.0 + + Returns + ------- + x : ndarray of float + Least-square solution returned. + istop : int + istop gives the reason for stopping:: + + istop = 0 means x=0 is a solution. If x0 was given, then x=x0 is a + solution. + = 1 means x is an approximate solution to A@x = B, + according to atol and btol. + = 2 means x approximately solves the least-squares problem + according to atol. + = 3 means COND(A) seems to be greater than CONLIM. + = 4 is the same as 1 with atol = btol = eps (machine + precision) + = 5 is the same as 2 with atol = eps. + = 6 is the same as 3 with CONLIM = 1/eps. + = 7 means ITN reached maxiter before the other stopping + conditions were satisfied. + + itn : int + Number of iterations used. + normr : float + ``norm(b-Ax)`` + normar : float + ``norm(A^H (b - Ax))`` + norma : float + ``norm(A)`` + conda : float + Condition number of A. + normx : float + ``norm(x)`` + + Notes + ----- + + .. versionadded:: 0.11.0 + + References + ---------- + .. [1] D. C.-L. Fong and M. A. Saunders, + "LSMR: An iterative algorithm for sparse least-squares problems", + SIAM J. Sci. Comput., vol. 33, pp. 2950-2971, 2011. + :arxiv:`1006.0758` + .. [2] LSMR Software, https://web.stanford.edu/group/SOL/software/lsmr/ + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lsmr + >>> A = csc_matrix([[1., 0.], [1., 1.], [0., 1.]], dtype=float) + + The first example has the trivial solution ``[0, 0]`` + + >>> b = np.array([0., 0., 0.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 0 + >>> x + array([0., 0.]) + + The stopping code `istop=0` returned indicates that a vector of zeros was + found as a solution. The returned solution `x` indeed contains + ``[0., 0.]``. The next example has a non-trivial solution: + + >>> b = np.array([1., 0., -1.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 1 + >>> x + array([ 1., -1.]) + >>> itn + 1 + >>> normr + 4.440892098500627e-16 + + As indicated by `istop=1`, `lsmr` found a solution obeying the tolerance + limits. The given solution ``[1., -1.]`` obviously solves the equation. The + remaining return values include information about the number of iterations + (`itn=1`) and the remaining difference of left and right side of the solved + equation. + The final example demonstrates the behavior in the case where there is no + solution for the equation: + + >>> b = np.array([1., 0.01, -1.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 2 + >>> x + array([ 1.00333333, -0.99666667]) + >>> A.dot(x)-b + array([ 0.00333333, -0.00333333, 0.00333333]) + >>> normr + 0.005773502691896255 + + `istop` indicates that the system is inconsistent and thus `x` is rather an + approximate solution to the corresponding least-squares problem. `normr` + contains the minimal distance that was found. + """ + + A = aslinearoperator(A) + b = atleast_1d(b) + if b.ndim > 1: + b = b.squeeze() + + msg = ('The exact solution is x = 0, or x = x0, if x0 was given ', + 'Ax - b is small enough, given atol, btol ', + 'The least-squares solution is good enough, given atol ', + 'The estimate of cond(Abar) has exceeded conlim ', + 'Ax - b is small enough for this machine ', + 'The least-squares solution is good enough for this machine', + 'Cond(Abar) seems to be too large for this machine ', + 'The iteration limit has been reached ') + + hdg1 = ' itn x(1) norm r norm Ar' + hdg2 = ' compatible LS norm A cond A' + pfreq = 20 # print frequency (for repeating the heading) + pcount = 0 # print counter + + m, n = A.shape + + # stores the num of singular values + minDim = min([m, n]) + + if maxiter is None: + maxiter = minDim + + if x0 is None: + dtype = result_type(A, b, float) + else: + dtype = result_type(A, b, x0, float) + + if show: + print(' ') + print('LSMR Least-squares solution of Ax = b\n') + print(f'The matrix A has {m} rows and {n} columns') + print('damp = %20.14e\n' % (damp)) + print(f'atol = {atol:8.2e} conlim = {conlim:8.2e}\n') + print(f'btol = {btol:8.2e} maxiter = {maxiter:8g}\n') + + u = b + normb = norm(b) + if x0 is None: + x = zeros(n, dtype) + beta = normb.copy() + else: + x = atleast_1d(x0.copy()) + u = u - A.matvec(x) + beta = norm(u) + + if beta > 0: + u = (1 / beta) * u + v = A.rmatvec(u) + alpha = norm(v) + else: + v = zeros(n, dtype) + alpha = 0 + + if alpha > 0: + v = (1 / alpha) * v + + # Initialize variables for 1st iteration. + + itn = 0 + zetabar = alpha * beta + alphabar = alpha + rho = 1 + rhobar = 1 + cbar = 1 + sbar = 0 + + h = v.copy() + hbar = zeros(n, dtype) + + # Initialize variables for estimation of ||r||. + + betadd = beta + betad = 0 + rhodold = 1 + tautildeold = 0 + thetatilde = 0 + zeta = 0 + d = 0 + + # Initialize variables for estimation of ||A|| and cond(A) + + normA2 = alpha * alpha + maxrbar = 0 + minrbar = 1e+100 + normA = sqrt(normA2) + condA = 1 + normx = 0 + + # Items for use in stopping rules, normb set earlier + istop = 0 + ctol = 0 + if conlim > 0: + ctol = 1 / conlim + normr = beta + + # Reverse the order here from the original matlab code because + # there was an error on return when arnorm==0 + normar = alpha * beta + if normar == 0: + if show: + print(msg[0]) + return x, istop, itn, normr, normar, normA, condA, normx + + if normb == 0: + x[()] = 0 + return x, istop, itn, normr, normar, normA, condA, normx + + if show: + print(' ') + print(hdg1, hdg2) + test1 = 1 + test2 = alpha / beta + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {normr:10.3e} {normar:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + print(''.join([str1, str2, str3])) + + # Main iteration loop. + while itn < maxiter: + itn = itn + 1 + + # Perform the next step of the bidiagonalization to obtain the + # next beta, u, alpha, v. These satisfy the relations + # beta*u = A@v - alpha*u, + # alpha*v = A'@u - beta*v. + + u *= -alpha + u += A.matvec(v) + beta = norm(u) + + if beta > 0: + u *= (1 / beta) + v *= -beta + v += A.rmatvec(u) + alpha = norm(v) + if alpha > 0: + v *= (1 / alpha) + + # At this point, beta = beta_{k+1}, alpha = alpha_{k+1}. + + # Construct rotation Qhat_{k,2k+1}. + + chat, shat, alphahat = _sym_ortho(alphabar, damp) + + # Use a plane rotation (Q_i) to turn B_i to R_i + + rhoold = rho + c, s, rho = _sym_ortho(alphahat, beta) + thetanew = s*alpha + alphabar = c*alpha + + # Use a plane rotation (Qbar_i) to turn R_i^T to R_i^bar + + rhobarold = rhobar + zetaold = zeta + thetabar = sbar * rho + rhotemp = cbar * rho + cbar, sbar, rhobar = _sym_ortho(cbar * rho, thetanew) + zeta = cbar * zetabar + zetabar = - sbar * zetabar + + # Update h, h_hat, x. + + hbar *= - (thetabar * rho / (rhoold * rhobarold)) + hbar += h + x += (zeta / (rho * rhobar)) * hbar + h *= - (thetanew / rho) + h += v + + # Estimate of ||r||. + + # Apply rotation Qhat_{k,2k+1}. + betaacute = chat * betadd + betacheck = -shat * betadd + + # Apply rotation Q_{k,k+1}. + betahat = c * betaacute + betadd = -s * betaacute + + # Apply rotation Qtilde_{k-1}. + # betad = betad_{k-1} here. + + thetatildeold = thetatilde + ctildeold, stildeold, rhotildeold = _sym_ortho(rhodold, thetabar) + thetatilde = stildeold * rhobar + rhodold = ctildeold * rhobar + betad = - stildeold * betad + ctildeold * betahat + + # betad = betad_k here. + # rhodold = rhod_k here. + + tautildeold = (zetaold - thetatildeold * tautildeold) / rhotildeold + taud = (zeta - thetatilde * tautildeold) / rhodold + d = d + betacheck * betacheck + normr = sqrt(d + (betad - taud)**2 + betadd * betadd) + + # Estimate ||A||. + normA2 = normA2 + beta * beta + normA = sqrt(normA2) + normA2 = normA2 + alpha * alpha + + # Estimate cond(A). + maxrbar = max(maxrbar, rhobarold) + if itn > 1: + minrbar = min(minrbar, rhobarold) + condA = max(maxrbar, rhotemp) / min(minrbar, rhotemp) + + # Test for convergence. + + # Compute norms for convergence testing. + normar = abs(zetabar) + normx = norm(x) + + # Now use these norms to estimate certain other quantities, + # some of which will be small near a solution. + + test1 = normr / normb + if (normA * normr) != 0: + test2 = normar / (normA * normr) + else: + test2 = inf + test3 = 1 / condA + t1 = test1 / (1 + normA * normx / normb) + rtol = btol + atol * normA * normx / normb + + # The following tests guard against extremely small values of + # atol, btol or ctol. (The user may have set any or all of + # the parameters atol, btol, conlim to 0.) + # The effect is equivalent to the normAl tests using + # atol = eps, btol = eps, conlim = 1/eps. + + if itn >= maxiter: + istop = 7 + if 1 + test3 <= 1: + istop = 6 + if 1 + test2 <= 1: + istop = 5 + if 1 + t1 <= 1: + istop = 4 + + # Allow for tolerances set by the user. + + if test3 <= ctol: + istop = 3 + if test2 <= atol: + istop = 2 + if test1 <= rtol: + istop = 1 + + # See if it is time to print something. + + if show: + if (n <= 40) or (itn <= 10) or (itn >= maxiter - 10) or \ + (itn % 10 == 0) or (test3 <= 1.1 * ctol) or \ + (test2 <= 1.1 * atol) or (test1 <= 1.1 * rtol) or \ + (istop != 0): + + if pcount >= pfreq: + pcount = 0 + print(' ') + print(hdg1, hdg2) + pcount = pcount + 1 + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {normr:10.3e} {normar:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + str4 = f' {normA:8.1e} {condA:8.1e}' + print(''.join([str1, str2, str3, str4])) + + if istop > 0: + break + + # Print the stopping condition. + + if show: + print(' ') + print('LSMR finished') + print(msg[istop]) + print(f'istop ={istop:8g} normr ={normr:8.1e}') + print(f' normA ={normA:8.1e} normAr ={normar:8.1e}') + print(f'itn ={itn:8g} condA ={condA:8.1e}') + print(' normx =%8.1e' % (normx)) + print(str1, str2) + print(str3, str4) + + return x, istop, itn, normr, normar, normA, condA, normx diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsqr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsqr.py new file mode 100644 index 0000000000000000000000000000000000000000..010f61bc5412f96a31ef5303f09b5167556e80ce --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/lsqr.py @@ -0,0 +1,587 @@ +"""Sparse Equations and Least Squares. + +The original Fortran code was written by C. C. Paige and M. A. Saunders as +described in + +C. C. Paige and M. A. Saunders, LSQR: An algorithm for sparse linear +equations and sparse least squares, TOMS 8(1), 43--71 (1982). + +C. C. Paige and M. A. Saunders, Algorithm 583; LSQR: Sparse linear +equations and least-squares problems, TOMS 8(2), 195--209 (1982). + +It is licensed under the following BSD license: + +Copyright (c) 2006, Systems Optimization Laboratory +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Stanford University nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The Fortran code was translated to Python for use in CVXOPT by Jeffery +Kline with contributions by Mridul Aanjaneya and Bob Myhill. + +Adapted for SciPy by Stefan van der Walt. + +""" + +__all__ = ['lsqr'] + +import numpy as np +from math import sqrt +from scipy.sparse.linalg._interface import aslinearoperator + +eps = np.finfo(np.float64).eps + + +def _sym_ortho(a, b): + """ + Stable implementation of Givens rotation. + + Notes + ----- + The routine 'SymOrtho' was added for numerical stability. This is + recommended by S.-C. Choi in [1]_. It removes the unpleasant potential of + ``1/eps`` in some important places (see, for example text following + "Compute the next plane rotation Qk" in minres.py). + + References + ---------- + .. [1] S.-C. Choi, "Iterative Methods for Singular Linear Equations + and Least-Squares Problems", Dissertation, + http://www.stanford.edu/group/SOL/dissertations/sou-cheng-choi-thesis.pdf + + """ + if b == 0: + return np.sign(a), 0, abs(a) + elif a == 0: + return 0, np.sign(b), abs(b) + elif abs(b) > abs(a): + tau = a / b + s = np.sign(b) / sqrt(1 + tau * tau) + c = s * tau + r = b / s + else: + tau = b / a + c = np.sign(a) / sqrt(1+tau*tau) + s = c * tau + r = a / c + return c, s, r + + +def lsqr(A, b, damp=0.0, atol=1e-6, btol=1e-6, conlim=1e8, + iter_lim=None, show=False, calc_var=False, x0=None): + """Find the least-squares solution to a large, sparse, linear system + of equations. + + The function solves ``Ax = b`` or ``min ||Ax - b||^2`` or + ``min ||Ax - b||^2 + d^2 ||x - x0||^2``. + + The matrix A may be square or rectangular (over-determined or + under-determined), and may have any rank. + + :: + + 1. Unsymmetric equations -- solve Ax = b + + 2. Linear least squares -- solve Ax = b + in the least-squares sense + + 3. Damped least squares -- solve ( A )*x = ( b ) + ( damp*I ) ( damp*x0 ) + in the least-squares sense + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + Representation of an m-by-n matrix. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : array_like, shape (m,) + Right-hand side vector ``b``. + damp : float + Damping coefficient. Default is 0. + atol, btol : float, optional + Stopping tolerances. `lsqr` continues iterations until a + certain backward error estimate is smaller than some quantity + depending on atol and btol. Let ``r = b - Ax`` be the + residual vector for the current approximate solution ``x``. + If ``Ax = b`` seems to be consistent, `lsqr` terminates + when ``norm(r) <= atol * norm(A) * norm(x) + btol * norm(b)``. + Otherwise, `lsqr` terminates when ``norm(A^H r) <= + atol * norm(A) * norm(r)``. If both tolerances are 1.0e-6 (default), + the final ``norm(r)`` should be accurate to about 6 + digits. (The final ``x`` will usually have fewer correct digits, + depending on ``cond(A)`` and the size of LAMBDA.) If `atol` + or `btol` is None, a default value of 1.0e-6 will be used. + Ideally, they should be estimates of the relative error in the + entries of ``A`` and ``b`` respectively. For example, if the entries + of ``A`` have 7 correct digits, set ``atol = 1e-7``. This prevents + the algorithm from doing unnecessary work beyond the + uncertainty of the input data. + conlim : float, optional + Another stopping tolerance. lsqr terminates if an estimate of + ``cond(A)`` exceeds `conlim`. For compatible systems ``Ax = + b``, `conlim` could be as large as 1.0e+12 (say). For + least-squares problems, conlim should be less than 1.0e+8. + Maximum precision can be obtained by setting ``atol = btol = + conlim = zero``, but the number of iterations may then be + excessive. Default is 1e8. + iter_lim : int, optional + Explicit limitation on number of iterations (for safety). + show : bool, optional + Display an iteration log. Default is False. + calc_var : bool, optional + Whether to estimate diagonals of ``(A'A + damp^2*I)^{-1}``. + x0 : array_like, shape (n,), optional + Initial guess of x, if None zeros are used. Default is None. + + .. versionadded:: 1.0.0 + + Returns + ------- + x : ndarray of float + The final solution. + istop : int + Gives the reason for termination. + 1 means x is an approximate solution to Ax = b. + 2 means x approximately solves the least-squares problem. + itn : int + Iteration number upon termination. + r1norm : float + ``norm(r)``, where ``r = b - Ax``. + r2norm : float + ``sqrt( norm(r)^2 + damp^2 * norm(x - x0)^2 )``. Equal to `r1norm` + if ``damp == 0``. + anorm : float + Estimate of Frobenius norm of ``Abar = [[A]; [damp*I]]``. + acond : float + Estimate of ``cond(Abar)``. + arnorm : float + Estimate of ``norm(A'@r - damp^2*(x - x0))``. + xnorm : float + ``norm(x)`` + var : ndarray of float + If ``calc_var`` is True, estimates all diagonals of + ``(A'A)^{-1}`` (if ``damp == 0``) or more generally ``(A'A + + damp^2*I)^{-1}``. This is well defined if A has full column + rank or ``damp > 0``. (Not sure what var means if ``rank(A) + < n`` and ``damp = 0.``) + + Notes + ----- + LSQR uses an iterative method to approximate the solution. The + number of iterations required to reach a certain accuracy depends + strongly on the scaling of the problem. Poor scaling of the rows + or columns of A should therefore be avoided where possible. + + For example, in problem 1 the solution is unaltered by + row-scaling. If a row of A is very small or large compared to + the other rows of A, the corresponding row of ( A b ) should be + scaled up or down. + + In problems 1 and 2, the solution x is easily recovered + following column-scaling. Unless better information is known, + the nonzero columns of A should be scaled so that they all have + the same Euclidean norm (e.g., 1.0). + + In problem 3, there is no freedom to re-scale if damp is + nonzero. However, the value of damp should be assigned only + after attention has been paid to the scaling of A. + + The parameter damp is intended to help regularize + ill-conditioned systems, by preventing the true solution from + being very large. Another aid to regularization is provided by + the parameter acond, which may be used to terminate iterations + before the computed solution becomes very large. + + If some initial estimate ``x0`` is known and if ``damp == 0``, + one could proceed as follows: + + 1. Compute a residual vector ``r0 = b - A@x0``. + 2. Use LSQR to solve the system ``A@dx = r0``. + 3. Add the correction dx to obtain a final solution ``x = x0 + dx``. + + This requires that ``x0`` be available before and after the call + to LSQR. To judge the benefits, suppose LSQR takes k1 iterations + to solve A@x = b and k2 iterations to solve A@dx = r0. + If x0 is "good", norm(r0) will be smaller than norm(b). + If the same stopping tolerances atol and btol are used for each + system, k1 and k2 will be similar, but the final solution x0 + dx + should be more accurate. The only way to reduce the total work + is to use a larger stopping tolerance for the second system. + If some value btol is suitable for A@x = b, the larger value + btol*norm(b)/norm(r0) should be suitable for A@dx = r0. + + Preconditioning is another way to reduce the number of iterations. + If it is possible to solve a related system ``M@x = b`` + efficiently, where M approximates A in some helpful way (e.g. M - + A has low rank or its elements are small relative to those of A), + LSQR may converge more rapidly on the system ``A@M(inverse)@z = + b``, after which x can be recovered by solving M@x = z. + + If A is symmetric, LSQR should not be used! + + Alternatives are the symmetric conjugate-gradient method (cg) + and/or SYMMLQ. SYMMLQ is an implementation of symmetric cg that + applies to any symmetric A and will converge more rapidly than + LSQR. If A is positive definite, there are other implementations + of symmetric cg that require slightly less work per iteration than + SYMMLQ (but will take the same number of iterations). + + References + ---------- + .. [1] C. C. Paige and M. A. Saunders (1982a). + "LSQR: An algorithm for sparse linear equations and + sparse least squares", ACM TOMS 8(1), 43-71. + .. [2] C. C. Paige and M. A. Saunders (1982b). + "Algorithm 583. LSQR: Sparse linear equations and least + squares problems", ACM TOMS 8(2), 195-209. + .. [3] M. A. Saunders (1995). "Solution of sparse rectangular + systems using LSQR and CRAIG", BIT 35, 588-604. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lsqr + >>> A = csc_matrix([[1., 0.], [1., 1.], [0., 1.]], dtype=float) + + The first example has the trivial solution ``[0, 0]`` + + >>> b = np.array([0., 0., 0.], dtype=float) + >>> x, istop, itn, normr = lsqr(A, b)[:4] + >>> istop + 0 + >>> x + array([ 0., 0.]) + + The stopping code `istop=0` returned indicates that a vector of zeros was + found as a solution. The returned solution `x` indeed contains + ``[0., 0.]``. The next example has a non-trivial solution: + + >>> b = np.array([1., 0., -1.], dtype=float) + >>> x, istop, itn, r1norm = lsqr(A, b)[:4] + >>> istop + 1 + >>> x + array([ 1., -1.]) + >>> itn + 1 + >>> r1norm + 4.440892098500627e-16 + + As indicated by `istop=1`, `lsqr` found a solution obeying the tolerance + limits. The given solution ``[1., -1.]`` obviously solves the equation. The + remaining return values include information about the number of iterations + (`itn=1`) and the remaining difference of left and right side of the solved + equation. + The final example demonstrates the behavior in the case where there is no + solution for the equation: + + >>> b = np.array([1., 0.01, -1.], dtype=float) + >>> x, istop, itn, r1norm = lsqr(A, b)[:4] + >>> istop + 2 + >>> x + array([ 1.00333333, -0.99666667]) + >>> A.dot(x)-b + array([ 0.00333333, -0.00333333, 0.00333333]) + >>> r1norm + 0.005773502691896255 + + `istop` indicates that the system is inconsistent and thus `x` is rather an + approximate solution to the corresponding least-squares problem. `r1norm` + contains the norm of the minimal residual that was found. + """ + A = aslinearoperator(A) + b = np.atleast_1d(b) + if b.ndim > 1: + b = b.squeeze() + + m, n = A.shape + if iter_lim is None: + iter_lim = 2 * n + var = np.zeros(n) + + msg = ('The exact solution is x = 0 ', + 'Ax - b is small enough, given atol, btol ', + 'The least-squares solution is good enough, given atol ', + 'The estimate of cond(Abar) has exceeded conlim ', + 'Ax - b is small enough for this machine ', + 'The least-squares solution is good enough for this machine', + 'Cond(Abar) seems to be too large for this machine ', + 'The iteration limit has been reached ') + + if show: + print(' ') + print('LSQR Least-squares solution of Ax = b') + str1 = f'The matrix A has {m} rows and {n} columns' + str2 = f'damp = {damp:20.14e} calc_var = {calc_var:8g}' + str3 = f'atol = {atol:8.2e} conlim = {conlim:8.2e}' + str4 = f'btol = {btol:8.2e} iter_lim = {iter_lim:8g}' + print(str1) + print(str2) + print(str3) + print(str4) + + itn = 0 + istop = 0 + ctol = 0 + if conlim > 0: + ctol = 1/conlim + anorm = 0 + acond = 0 + dampsq = damp**2 + ddnorm = 0 + res2 = 0 + xnorm = 0 + xxnorm = 0 + z = 0 + cs2 = -1 + sn2 = 0 + + # Set up the first vectors u and v for the bidiagonalization. + # These satisfy beta*u = b - A@x, alfa*v = A'@u. + u = b + bnorm = np.linalg.norm(b) + + if x0 is None: + x = np.zeros(n) + beta = bnorm.copy() + else: + x = np.asarray(x0) + u = u - A.matvec(x) + beta = np.linalg.norm(u) + + if beta > 0: + u = (1/beta) * u + v = A.rmatvec(u) + alfa = np.linalg.norm(v) + else: + v = x.copy() + alfa = 0 + + if alfa > 0: + v = (1/alfa) * v + w = v.copy() + + rhobar = alfa + phibar = beta + rnorm = beta + r1norm = rnorm + r2norm = rnorm + + # Reverse the order here from the original matlab code because + # there was an error on return when arnorm==0 + arnorm = alfa * beta + if arnorm == 0: + if show: + print(msg[0]) + return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var + + head1 = ' Itn x[0] r1norm r2norm ' + head2 = ' Compatible LS Norm A Cond A' + + if show: + print(' ') + print(head1, head2) + test1 = 1 + test2 = alfa / beta + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {r1norm:10.3e} {r2norm:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + print(str1, str2, str3) + + # Main iteration loop. + while itn < iter_lim: + itn = itn + 1 + # Perform the next step of the bidiagonalization to obtain the + # next beta, u, alfa, v. These satisfy the relations + # beta*u = a@v - alfa*u, + # alfa*v = A'@u - beta*v. + u = A.matvec(v) - alfa * u + beta = np.linalg.norm(u) + + if beta > 0: + u = (1/beta) * u + anorm = sqrt(anorm**2 + alfa**2 + beta**2 + dampsq) + v = A.rmatvec(u) - beta * v + alfa = np.linalg.norm(v) + if alfa > 0: + v = (1 / alfa) * v + + # Use a plane rotation to eliminate the damping parameter. + # This alters the diagonal (rhobar) of the lower-bidiagonal matrix. + if damp > 0: + rhobar1 = sqrt(rhobar**2 + dampsq) + cs1 = rhobar / rhobar1 + sn1 = damp / rhobar1 + psi = sn1 * phibar + phibar = cs1 * phibar + else: + # cs1 = 1 and sn1 = 0 + rhobar1 = rhobar + psi = 0. + + # Use a plane rotation to eliminate the subdiagonal element (beta) + # of the lower-bidiagonal matrix, giving an upper-bidiagonal matrix. + cs, sn, rho = _sym_ortho(rhobar1, beta) + + theta = sn * alfa + rhobar = -cs * alfa + phi = cs * phibar + phibar = sn * phibar + tau = sn * phi + + # Update x and w. + t1 = phi / rho + t2 = -theta / rho + dk = (1 / rho) * w + + x = x + t1 * w + w = v + t2 * w + ddnorm = ddnorm + np.linalg.norm(dk)**2 + + if calc_var: + var = var + dk**2 + + # Use a plane rotation on the right to eliminate the + # super-diagonal element (theta) of the upper-bidiagonal matrix. + # Then use the result to estimate norm(x). + delta = sn2 * rho + gambar = -cs2 * rho + rhs = phi - delta * z + zbar = rhs / gambar + xnorm = sqrt(xxnorm + zbar**2) + gamma = sqrt(gambar**2 + theta**2) + cs2 = gambar / gamma + sn2 = theta / gamma + z = rhs / gamma + xxnorm = xxnorm + z**2 + + # Test for convergence. + # First, estimate the condition of the matrix Abar, + # and the norms of rbar and Abar'rbar. + acond = anorm * sqrt(ddnorm) + res1 = phibar**2 + res2 = res2 + psi**2 + rnorm = sqrt(res1 + res2) + arnorm = alfa * abs(tau) + + # Distinguish between + # r1norm = ||b - Ax|| and + # r2norm = rnorm in current code + # = sqrt(r1norm^2 + damp^2*||x - x0||^2). + # Estimate r1norm from + # r1norm = sqrt(r2norm^2 - damp^2*||x - x0||^2). + # Although there is cancellation, it might be accurate enough. + if damp > 0: + r1sq = rnorm**2 - dampsq * xxnorm + r1norm = sqrt(abs(r1sq)) + if r1sq < 0: + r1norm = -r1norm + else: + r1norm = rnorm + r2norm = rnorm + + # Now use these norms to estimate certain other quantities, + # some of which will be small near a solution. + test1 = rnorm / bnorm + test2 = arnorm / (anorm * rnorm + eps) + test3 = 1 / (acond + eps) + t1 = test1 / (1 + anorm * xnorm / bnorm) + rtol = btol + atol * anorm * xnorm / bnorm + + # The following tests guard against extremely small values of + # atol, btol or ctol. (The user may have set any or all of + # the parameters atol, btol, conlim to 0.) + # The effect is equivalent to the normal tests using + # atol = eps, btol = eps, conlim = 1/eps. + if itn >= iter_lim: + istop = 7 + if 1 + test3 <= 1: + istop = 6 + if 1 + test2 <= 1: + istop = 5 + if 1 + t1 <= 1: + istop = 4 + + # Allow for tolerances set by the user. + if test3 <= ctol: + istop = 3 + if test2 <= atol: + istop = 2 + if test1 <= rtol: + istop = 1 + + if show: + # See if it is time to print something. + prnt = False + if n <= 40: + prnt = True + if itn <= 10: + prnt = True + if itn >= iter_lim-10: + prnt = True + # if itn%10 == 0: prnt = True + if test3 <= 2*ctol: + prnt = True + if test2 <= 10*atol: + prnt = True + if test1 <= 10*rtol: + prnt = True + if istop != 0: + prnt = True + + if prnt: + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {r1norm:10.3e} {r2norm:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + str4 = f' {anorm:8.1e} {acond:8.1e}' + print(str1, str2, str3, str4) + + if istop != 0: + break + + # End of iteration loop. + # Print the stopping condition. + if show: + print(' ') + print('LSQR finished') + print(msg[istop]) + print(' ') + str1 = f'istop ={istop:8g} r1norm ={r1norm:8.1e}' + str2 = f'anorm ={anorm:8.1e} arnorm ={arnorm:8.1e}' + str3 = f'itn ={itn:8g} r2norm ={r2norm:8.1e}' + str4 = f'acond ={acond:8.1e} xnorm ={xnorm:8.1e}' + print(str1 + ' ' + str2) + print(str3 + ' ' + str4) + print(' ') + + return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/minres.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/minres.py new file mode 100644 index 0000000000000000000000000000000000000000..daea59e920ff8dabb28cab98a058c049c764f959 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/minres.py @@ -0,0 +1,387 @@ +import warnings +from numpy import inner, zeros, inf, finfo +from numpy.linalg import norm +from math import sqrt + +from .utils import make_system +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args + +__all__ = ['minres'] + + +@_deprecate_positional_args(version="1.14.0") +def minres(A, b, x0=None, *, shift=0.0, tol=_NoValue, maxiter=None, + M=None, callback=None, show=False, check=False, rtol=1e-5): + """ + Use MINimum RESidual iteration to solve Ax=b + + MINRES minimizes norm(Ax - b) for a real symmetric matrix A. Unlike + the Conjugate Gradient method, A can be indefinite or singular. + + If shift != 0 then the method solves (A - shift*I)x = b + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real symmetric N-by-N matrix of the linear system + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : illegal input or breakdown + + Other Parameters + ---------------- + x0 : ndarray + Starting guess for the solution. + shift : float + Value to apply to the system ``(A - shift * I)x = b``. Default is 0. + rtol : float + Tolerance to achieve. The algorithm terminates when the relative + residual is below ``rtol``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + show : bool + If ``True``, print out a summary and metrics related to the solution + during iterations. Default is ``False``. + check : bool + If ``True``, run additional input validation to check that `A` and + `M` (if specified) are symmetric. Default is ``False``. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `minres` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import minres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> A = A + A.T + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = minres(A, b) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + References + ---------- + Solution of sparse indefinite systems of linear equations, + C. C. Paige and M. A. Saunders (1975), + SIAM J. Numer. Anal. 12(4), pp. 617-629. + https://web.stanford.edu/group/SOL/software/minres/ + + This file is a translation of the following MATLAB implementation: + https://web.stanford.edu/group/SOL/software/minres/minres-matlab.zip + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + + if tol is not _NoValue: + msg = ("'scipy.sparse.linalg.minres' keyword argument `tol` is " + "deprecated in favor of `rtol` and will be removed in SciPy " + "v1.14. Until then, if set, it will override `rtol`.") + warnings.warn(msg, category=DeprecationWarning, stacklevel=4) + rtol = float(tol) if tol is not None else rtol + + matvec = A.matvec + psolve = M.matvec + + first = 'Enter minres. ' + last = 'Exit minres. ' + + n = A.shape[0] + + if maxiter is None: + maxiter = 5 * n + + msg = [' beta2 = 0. If M = I, b and x are eigenvectors ', # -1 + ' beta1 = 0. The exact solution is x0 ', # 0 + ' A solution to Ax = b was found, given rtol ', # 1 + ' A least-squares solution was found, given rtol ', # 2 + ' Reasonable accuracy achieved, given eps ', # 3 + ' x has converged to an eigenvector ', # 4 + ' acond has exceeded 0.1/eps ', # 5 + ' The iteration limit was reached ', # 6 + ' A does not define a symmetric matrix ', # 7 + ' M does not define a symmetric matrix ', # 8 + ' M does not define a pos-def preconditioner '] # 9 + + if show: + print(first + 'Solution of symmetric Ax = b') + print(first + f'n = {n:3g} shift = {shift:23.14e}') + print(first + f'itnlim = {maxiter:3g} rtol = {rtol:11.2e}') + print() + + istop = 0 + itn = 0 + Anorm = 0 + Acond = 0 + rnorm = 0 + ynorm = 0 + + xtype = x.dtype + + eps = finfo(xtype).eps + + # Set up y and v for the first Lanczos vector v1. + # y = beta1 P' v1, where P = C**(-1). + # v is really P' v1. + + if x0 is None: + r1 = b.copy() + else: + r1 = b - A@x + y = psolve(r1) + + beta1 = inner(r1, y) + + if beta1 < 0: + raise ValueError('indefinite preconditioner') + elif beta1 == 0: + return (postprocess(x), 0) + + bnorm = norm(b) + if bnorm == 0: + x = b + return (postprocess(x), 0) + + beta1 = sqrt(beta1) + + if check: + # are these too strict? + + # see if A is symmetric + w = matvec(y) + r2 = matvec(w) + s = inner(w,w) + t = inner(y,r2) + z = abs(s - t) + epsa = (s + eps) * eps**(1.0/3.0) + if z > epsa: + raise ValueError('non-symmetric matrix') + + # see if M is symmetric + r2 = psolve(y) + s = inner(y,y) + t = inner(r1,r2) + z = abs(s - t) + epsa = (s + eps) * eps**(1.0/3.0) + if z > epsa: + raise ValueError('non-symmetric preconditioner') + + # Initialize other quantities + oldb = 0 + beta = beta1 + dbar = 0 + epsln = 0 + qrnorm = beta1 + phibar = beta1 + rhs1 = beta1 + rhs2 = 0 + tnorm2 = 0 + gmax = 0 + gmin = finfo(xtype).max + cs = -1 + sn = 0 + w = zeros(n, dtype=xtype) + w2 = zeros(n, dtype=xtype) + r2 = r1 + + if show: + print() + print() + print(' Itn x(1) Compatible LS norm(A) cond(A) gbar/|A|') + + while itn < maxiter: + itn += 1 + + s = 1.0/beta + v = s*y + + y = matvec(v) + y = y - shift * v + + if itn >= 2: + y = y - (beta/oldb)*r1 + + alfa = inner(v,y) + y = y - (alfa/beta)*r2 + r1 = r2 + r2 = y + y = psolve(r2) + oldb = beta + beta = inner(r2,y) + if beta < 0: + raise ValueError('non-symmetric matrix') + beta = sqrt(beta) + tnorm2 += alfa**2 + oldb**2 + beta**2 + + if itn == 1: + if beta/beta1 <= 10*eps: + istop = -1 # Terminate later + + # Apply previous rotation Qk-1 to get + # [deltak epslnk+1] = [cs sn][dbark 0 ] + # [gbar k dbar k+1] [sn -cs][alfak betak+1]. + + oldeps = epsln + delta = cs * dbar + sn * alfa # delta1 = 0 deltak + gbar = sn * dbar - cs * alfa # gbar 1 = alfa1 gbar k + epsln = sn * beta # epsln2 = 0 epslnk+1 + dbar = - cs * beta # dbar 2 = beta2 dbar k+1 + root = norm([gbar, dbar]) + Arnorm = phibar * root + + # Compute the next plane rotation Qk + + gamma = norm([gbar, beta]) # gammak + gamma = max(gamma, eps) + cs = gbar / gamma # ck + sn = beta / gamma # sk + phi = cs * phibar # phik + phibar = sn * phibar # phibark+1 + + # Update x. + + denom = 1.0/gamma + w1 = w2 + w2 = w + w = (v - oldeps*w1 - delta*w2) * denom + x = x + phi*w + + # Go round again. + + gmax = max(gmax, gamma) + gmin = min(gmin, gamma) + z = rhs1 / gamma + rhs1 = rhs2 - delta*z + rhs2 = - epsln*z + + # Estimate various norms and test for convergence. + + Anorm = sqrt(tnorm2) + ynorm = norm(x) + epsa = Anorm * eps + epsx = Anorm * ynorm * eps + epsr = Anorm * ynorm * rtol + diag = gbar + + if diag == 0: + diag = epsa + + qrnorm = phibar + rnorm = qrnorm + if ynorm == 0 or Anorm == 0: + test1 = inf + else: + test1 = rnorm / (Anorm*ynorm) # ||r|| / (||A|| ||x||) + if Anorm == 0: + test2 = inf + else: + test2 = root / Anorm # ||Ar|| / (||A|| ||r||) + + # Estimate cond(A). + # In this version we look at the diagonals of R in the + # factorization of the lower Hessenberg matrix, Q @ H = R, + # where H is the tridiagonal matrix from Lanczos with one + # extra row, beta(k+1) e_k^T. + + Acond = gmax/gmin + + # See if any of the stopping criteria are satisfied. + # In rare cases, istop is already -1 from above (Abar = const*I). + + if istop == 0: + t1 = 1 + test1 # These tests work if rtol < eps + t2 = 1 + test2 + if t2 <= 1: + istop = 2 + if t1 <= 1: + istop = 1 + + if itn >= maxiter: + istop = 6 + if Acond >= 0.1/eps: + istop = 4 + if epsx >= beta1: + istop = 3 + # if rnorm <= epsx : istop = 2 + # if rnorm <= epsr : istop = 1 + if test2 <= rtol: + istop = 2 + if test1 <= rtol: + istop = 1 + + # See if it is time to print something. + + prnt = False + if n <= 40: + prnt = True + if itn <= 10: + prnt = True + if itn >= maxiter-10: + prnt = True + if itn % 10 == 0: + prnt = True + if qrnorm <= 10*epsx: + prnt = True + if qrnorm <= 10*epsr: + prnt = True + if Acond <= 1e-2/eps: + prnt = True + if istop != 0: + prnt = True + + if show and prnt: + str1 = f'{itn:6g} {x[0]:12.5e} {test1:10.3e}' + str2 = f' {test2:10.3e}' + str3 = f' {Anorm:8.1e} {Acond:8.1e} {gbar/Anorm:8.1e}' + + print(str1 + str2 + str3) + + if itn % 10 == 0: + print() + + if callback is not None: + callback(x) + + if istop != 0: + break # TODO check this + + if show: + print() + print(last + f' istop = {istop:3g} itn ={itn:5g}') + print(last + f' Anorm = {Anorm:12.4e} Acond = {Acond:12.4e}') + print(last + f' rnorm = {rnorm:12.4e} ynorm = {ynorm:12.4e}') + print(last + f' Arnorm = {Arnorm:12.4e}') + print(last + msg[istop+1]) + + if istop == 6: + info = maxiter + else: + info = 0 + + return (postprocess(x),info) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py new file mode 100644 index 0000000000000000000000000000000000000000..0bb8a92c5582a29aa28378051841ed3546d1cf69 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +"""Tests for the linalg._isolve.gcrotmk module +""" + +from numpy.testing import (assert_, assert_allclose, assert_equal, + suppress_warnings) + +import numpy as np +from numpy import zeros, array, allclose +from scipy.linalg import norm +from scipy.sparse import csr_matrix, eye, rand + +from scipy.sparse.linalg._interface import LinearOperator +from scipy.sparse.linalg import splu +from scipy.sparse.linalg._isolve import gcrotmk, gmres + + +Am = csr_matrix(array([[-2,1,0,0,0,9], + [1,-2,1,0,5,0], + [0,1,-2,1,0,0], + [0,0,1,-2,1,0], + [0,3,0,1,-2,1], + [1,0,0,0,1,-2]])) +b = array([1,2,3,4,5,6]) +count = [0] + + +def matvec(v): + count[0] += 1 + return Am@v + + +A = LinearOperator(matvec=matvec, shape=Am.shape, dtype=Am.dtype) + + +def do_solve(**kw): + count[0] = 0 + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x0, flag = gcrotmk(A, b, x0=zeros(A.shape[0]), rtol=1e-14, **kw) + count_0 = count[0] + assert_(allclose(A@x0, b, rtol=1e-12, atol=1e-12), norm(A@x0-b)) + return x0, count_0 + + +class TestGCROTMK: + def test_preconditioner(self): + # Check that preconditioning works + pc = splu(Am.tocsc()) + M = LinearOperator(matvec=pc.solve, shape=A.shape, dtype=A.dtype) + + x0, count_0 = do_solve() + x1, count_1 = do_solve(M=M) + + assert_equal(count_1, 3) + assert_(count_1 < count_0/2) + assert_(allclose(x1, x0, rtol=1e-14)) + + def test_arnoldi(self): + np.random.seed(1) + + A = eye(2000) + rand(2000, 2000, density=5e-4) + b = np.random.rand(2000) + + # The inner arnoldi should be equivalent to gmres + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x0, flag0 = gcrotmk(A, b, x0=zeros(A.shape[0]), m=15, k=0, maxiter=1) + x1, flag1 = gmres(A, b, x0=zeros(A.shape[0]), restart=15, maxiter=1) + + assert_equal(flag0, 1) + assert_equal(flag1, 1) + assert np.linalg.norm(A.dot(x0) - b) > 1e-3 + + assert_allclose(x0, x1) + + def test_cornercase(self): + np.random.seed(1234) + + # Rounding error may prevent convergence with tol=0 --- ensure + # that the return values in this case are correct, and no + # exceptions are raised + + for n in [3, 5, 10, 100]: + A = 2*eye(n) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + b = np.ones(n) + x, info = gcrotmk(A, b, maxiter=10) + assert_equal(info, 0) + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + x, info = gcrotmk(A, b, rtol=0, maxiter=10) + if info == 0: + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + b = np.random.rand(n) + x, info = gcrotmk(A, b, maxiter=10) + assert_equal(info, 0) + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + x, info = gcrotmk(A, b, rtol=0, maxiter=10) + if info == 0: + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + def test_nans(self): + A = eye(3, format='lil') + A[1,1] = np.nan + b = np.ones(3) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x, info = gcrotmk(A, b, rtol=0, maxiter=10) + assert_equal(info, 1) + + def test_truncate(self): + np.random.seed(1234) + A = np.random.rand(30, 30) + np.eye(30) + b = np.random.rand(30) + + for truncate in ['oldest', 'smallest']: + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x, info = gcrotmk(A, b, m=10, k=10, truncate=truncate, + rtol=1e-4, maxiter=200) + assert_equal(info, 0) + assert_allclose(A.dot(x) - b, 0, atol=1e-3) + + def test_CU(self): + for discard_C in (True, False): + # Check that C,U behave as expected + CU = [] + x0, count_0 = do_solve(CU=CU, discard_C=discard_C) + assert_(len(CU) > 0) + assert_(len(CU) <= 6) + + if discard_C: + for c, u in CU: + assert_(c is None) + + # should converge immediately + x1, count_1 = do_solve(CU=CU, discard_C=discard_C) + if discard_C: + assert_equal(count_1, 2 + len(CU)) + else: + assert_equal(count_1, 3) + assert_(count_1 <= count_0/2) + assert_allclose(x1, x0, atol=1e-14) + + def test_denormals(self): + # Check that no warnings are emitted if the matrix contains + # numbers for which 1/x has no float representation, and that + # the solver behaves properly. + A = np.array([[1, 2], [3, 4]], dtype=float) + A *= 100 * np.nextafter(0, 1) + + b = np.array([1, 1]) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + xp, info = gcrotmk(A, b) + + if info == 0: + assert_allclose(A.dot(xp), b) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_iterative.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_iterative.py new file mode 100644 index 0000000000000000000000000000000000000000..01540ff10c3e5f814fce9890fd5e4c88ff09581c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_iterative.py @@ -0,0 +1,796 @@ +""" Test functions for the sparse.linalg._isolve module +""" + +import itertools +import platform +import sys +import pytest + +import numpy as np +from numpy.testing import assert_array_equal, assert_allclose +from numpy import zeros, arange, array, ones, eye, iscomplexobj +from numpy.linalg import norm + +from scipy.sparse import spdiags, csr_matrix, kronsum + +from scipy.sparse.linalg import LinearOperator, aslinearoperator +from scipy.sparse.linalg._isolve import (bicg, bicgstab, cg, cgs, + gcrotmk, gmres, lgmres, + minres, qmr, tfqmr) + +# TODO check that method preserve shape and type +# TODO test both preconditioner methods + + +# list of all solvers under test +_SOLVERS = [bicg, bicgstab, cg, cgs, gcrotmk, gmres, lgmres, + minres, qmr, tfqmr] + +pytestmark = [ + # remove this once atol defaults to 0.0 for all methods + pytest.mark.filterwarnings("ignore:.*called without specifying.*"), +] + + +# create parametrized fixture for easy reuse in tests +@pytest.fixture(params=_SOLVERS, scope="session") +def solver(request): + """ + Fixture for all solvers in scipy.sparse.linalg._isolve + """ + return request.param + + +class Case: + def __init__(self, name, A, b=None, skip=None, nonconvergence=None): + self.name = name + self.A = A + if b is None: + self.b = arange(A.shape[0], dtype=float) + else: + self.b = b + if skip is None: + self.skip = [] + else: + self.skip = skip + if nonconvergence is None: + self.nonconvergence = [] + else: + self.nonconvergence = nonconvergence + + +class SingleTest: + def __init__(self, A, b, solver, casename, convergence=True): + self.A = A + self.b = b + self.solver = solver + self.name = casename + '-' + solver.__name__ + self.convergence = convergence + + def __repr__(self): + return f"<{self.name}>" + + +class IterativeParams: + def __init__(self): + sym_solvers = [minres, cg] + posdef_solvers = [cg] + real_solvers = [minres] + + # list of Cases + self.cases = [] + + # Symmetric and Positive Definite + N = 40 + data = ones((3, N)) + data[0, :] = 2 + data[1, :] = -1 + data[2, :] = -1 + Poisson1D = spdiags(data, [0, -1, 1], N, N, format='csr') + self.cases.append(Case("poisson1d", Poisson1D)) + # note: minres fails for single precision + self.cases.append(Case("poisson1d-F", Poisson1D.astype('f'), + skip=[minres])) + + # Symmetric and Negative Definite + self.cases.append(Case("neg-poisson1d", -Poisson1D, + skip=posdef_solvers)) + # note: minres fails for single precision + self.cases.append(Case("neg-poisson1d-F", (-Poisson1D).astype('f'), + skip=posdef_solvers + [minres])) + + # 2-dimensional Poisson equations + Poisson2D = kronsum(Poisson1D, Poisson1D) + # note: minres fails for 2-d poisson problem, + # it will be fixed in the future PR + self.cases.append(Case("poisson2d", Poisson2D, skip=[minres])) + # note: minres fails for single precision + self.cases.append(Case("poisson2d-F", Poisson2D.astype('f'), + skip=[minres])) + + # Symmetric and Indefinite + data = array([[6, -5, 2, 7, -1, 10, 4, -3, -8, 9]], dtype='d') + RandDiag = spdiags(data, [0], 10, 10, format='csr') + self.cases.append(Case("rand-diag", RandDiag, skip=posdef_solvers)) + self.cases.append(Case("rand-diag-F", RandDiag.astype('f'), + skip=posdef_solvers)) + + # Random real-valued + np.random.seed(1234) + data = np.random.rand(4, 4) + self.cases.append(Case("rand", data, + skip=posdef_solvers + sym_solvers)) + self.cases.append(Case("rand-F", data.astype('f'), + skip=posdef_solvers + sym_solvers)) + + # Random symmetric real-valued + np.random.seed(1234) + data = np.random.rand(4, 4) + data = data + data.T + self.cases.append(Case("rand-sym", data, skip=posdef_solvers)) + self.cases.append(Case("rand-sym-F", data.astype('f'), + skip=posdef_solvers)) + + # Random pos-def symmetric real + np.random.seed(1234) + data = np.random.rand(9, 9) + data = np.dot(data.conj(), data.T) + self.cases.append(Case("rand-sym-pd", data)) + # note: minres fails for single precision + self.cases.append(Case("rand-sym-pd-F", data.astype('f'), + skip=[minres])) + + # Random complex-valued + np.random.seed(1234) + data = np.random.rand(4, 4) + 1j * np.random.rand(4, 4) + skip_cmplx = posdef_solvers + sym_solvers + real_solvers + self.cases.append(Case("rand-cmplx", data, skip=skip_cmplx)) + self.cases.append(Case("rand-cmplx-F", data.astype('F'), + skip=skip_cmplx)) + + # Random hermitian complex-valued + np.random.seed(1234) + data = np.random.rand(4, 4) + 1j * np.random.rand(4, 4) + data = data + data.T.conj() + self.cases.append(Case("rand-cmplx-herm", data, + skip=posdef_solvers + real_solvers)) + self.cases.append(Case("rand-cmplx-herm-F", data.astype('F'), + skip=posdef_solvers + real_solvers)) + + # Random pos-def hermitian complex-valued + np.random.seed(1234) + data = np.random.rand(9, 9) + 1j * np.random.rand(9, 9) + data = np.dot(data.conj(), data.T) + self.cases.append(Case("rand-cmplx-sym-pd", data, skip=real_solvers)) + self.cases.append(Case("rand-cmplx-sym-pd-F", data.astype('F'), + skip=real_solvers)) + + # Non-symmetric and Positive Definite + # + # cgs, qmr, bicg and tfqmr fail to converge on this one + # -- algorithmic limitation apparently + data = ones((2, 10)) + data[0, :] = 2 + data[1, :] = -1 + A = spdiags(data, [0, -1], 10, 10, format='csr') + self.cases.append(Case("nonsymposdef", A, + skip=sym_solvers + [cgs, qmr, bicg, tfqmr])) + self.cases.append(Case("nonsymposdef-F", A.astype('F'), + skip=sym_solvers + [cgs, qmr, bicg, tfqmr])) + + # Symmetric, non-pd, hitting cgs/bicg/bicgstab/qmr/tfqmr breakdown + A = np.array([[0, 0, 0, 0, 0, 1, -1, -0, -0, -0, -0], + [0, 0, 0, 0, 0, 2, -0, -1, -0, -0, -0], + [0, 0, 0, 0, 0, 2, -0, -0, -1, -0, -0], + [0, 0, 0, 0, 0, 2, -0, -0, -0, -1, -0], + [0, 0, 0, 0, 0, 1, -0, -0, -0, -0, -1], + [1, 2, 2, 2, 1, 0, -0, -0, -0, -0, -0], + [-1, 0, 0, 0, 0, 0, -1, -0, -0, -0, -0], + [0, -1, 0, 0, 0, 0, -0, -1, -0, -0, -0], + [0, 0, -1, 0, 0, 0, -0, -0, -1, -0, -0], + [0, 0, 0, -1, 0, 0, -0, -0, -0, -1, -0], + [0, 0, 0, 0, -1, 0, -0, -0, -0, -0, -1]], dtype=float) + b = np.array([0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], dtype=float) + assert (A == A.T).all() + self.cases.append(Case("sym-nonpd", A, b, + skip=posdef_solvers, + nonconvergence=[cgs, bicg, bicgstab, qmr, tfqmr] + ) + ) + + def generate_tests(self): + # generate test cases with skips applied + tests = [] + for case in self.cases: + for solver in _SOLVERS: + if (solver in case.skip): + continue + if solver in case.nonconvergence: + tests += [SingleTest(case.A, case.b, solver, case.name, + convergence=False)] + else: + tests += [SingleTest(case.A, case.b, solver, case.name)] + return tests + + +cases = IterativeParams().generate_tests() + + +@pytest.fixture(params=cases, ids=[x.name for x in cases], scope="module") +def case(request): + """ + Fixture for all cases in IterativeParams + """ + return request.param + + +def test_maxiter(case): + if not case.convergence: + pytest.skip("Solver - Breakdown case, see gh-8829") + A = case.A + rtol = 1e-12 + + b = case.b + x0 = 0 * b + + residuals = [] + + def callback(x): + residuals.append(norm(b - case.A * x)) + + x, info = case.solver(A, b, x0=x0, rtol=rtol, maxiter=1, callback=callback) + + assert len(residuals) == 1 + assert info == 1 + + +def test_convergence(case): + A = case.A + + if A.dtype.char in "dD": + rtol = 1e-8 + else: + rtol = 1e-2 + + b = case.b + x0 = 0 * b + + x, info = case.solver(A, b, x0=x0, rtol=rtol) + + assert_array_equal(x0, 0 * b) # ensure that x0 is not overwritten + if case.convergence: + assert info == 0 + assert norm(A @ x - b) <= norm(b) * rtol + else: + assert info != 0 + assert norm(A @ x - b) <= norm(b) + + +def test_precond_dummy(case): + if not case.convergence: + pytest.skip("Solver - Breakdown case, see gh-8829") + + rtol = 1e-8 + + def identity(b, which=None): + """trivial preconditioner""" + return b + + A = case.A + + M, N = A.shape + # Ensure the diagonal elements of A are non-zero before calculating + # 1.0/A.diagonal() + diagOfA = A.diagonal() + if np.count_nonzero(diagOfA) == len(diagOfA): + spdiags([1.0 / diagOfA], [0], M, N) + + b = case.b + x0 = 0 * b + + precond = LinearOperator(A.shape, identity, rmatvec=identity) + + if case.solver is qmr: + x, info = case.solver(A, b, M1=precond, M2=precond, x0=x0, rtol=rtol) + else: + x, info = case.solver(A, b, M=precond, x0=x0, rtol=rtol) + assert info == 0 + assert norm(A @ x - b) <= norm(b) * rtol + + A = aslinearoperator(A) + A.psolve = identity + A.rpsolve = identity + + x, info = case.solver(A, b, x0=x0, rtol=rtol) + assert info == 0 + assert norm(A @ x - b) <= norm(b) * rtol + + +# Specific test for poisson1d and poisson2d cases +@pytest.mark.parametrize('case', [x for x in IterativeParams().cases + if x.name in ('poisson1d', 'poisson2d')], + ids=['poisson1d', 'poisson2d']) +def test_precond_inverse(case): + for solver in _SOLVERS: + if solver in case.skip or solver is qmr: + continue + + rtol = 1e-8 + + def inverse(b, which=None): + """inverse preconditioner""" + A = case.A + if not isinstance(A, np.ndarray): + A = A.toarray() + return np.linalg.solve(A, b) + + def rinverse(b, which=None): + """inverse preconditioner""" + A = case.A + if not isinstance(A, np.ndarray): + A = A.toarray() + return np.linalg.solve(A.T, b) + + matvec_count = [0] + + def matvec(b): + matvec_count[0] += 1 + return case.A @ b + + def rmatvec(b): + matvec_count[0] += 1 + return case.A.T @ b + + b = case.b + x0 = 0 * b + + A = LinearOperator(case.A.shape, matvec, rmatvec=rmatvec) + precond = LinearOperator(case.A.shape, inverse, rmatvec=rinverse) + + # Solve with preconditioner + matvec_count = [0] + x, info = solver(A, b, M=precond, x0=x0, rtol=rtol) + + assert info == 0 + assert norm(case.A @ x - b) <= norm(b) * rtol + + # Solution should be nearly instant + assert matvec_count[0] <= 3 + + +def test_atol(solver): + # TODO: minres / tfqmr. It didn't historically use absolute tolerances, so + # fixing it is less urgent. + if solver in (minres, tfqmr): + pytest.skip("TODO: Add atol to minres/tfqmr") + + # Historically this is tested as below, all pass but for some reason + # gcrotmk is over-sensitive to difference between random.seed/rng.random + # Hence tol lower bound is changed from -10 to -9 + # np.random.seed(1234) + # A = np.random.rand(10, 10) + # A = A @ A.T + 10 * np.eye(10) + # b = 1e3*np.random.rand(10) + + rng = np.random.default_rng(168441431005389) + A = rng.uniform(size=[10, 10]) + A = A @ A.T + 10*np.eye(10) + b = 1e3 * rng.uniform(size=10) + + b_norm = np.linalg.norm(b) + + tols = np.r_[0, np.logspace(-9, 2, 7), np.inf] + + # Check effect of badly scaled preconditioners + M0 = rng.standard_normal(size=(10, 10)) + M0 = M0 @ M0.T + Ms = [None, 1e-6 * M0, 1e6 * M0] + + for M, rtol, atol in itertools.product(Ms, tols, tols): + if rtol == 0 and atol == 0: + continue + + if solver is qmr: + if M is not None: + M = aslinearoperator(M) + M2 = aslinearoperator(np.eye(10)) + else: + M2 = None + x, info = solver(A, b, M1=M, M2=M2, rtol=rtol, atol=atol) + else: + x, info = solver(A, b, M=M, rtol=rtol, atol=atol) + + assert info == 0 + residual = A @ x - b + err = np.linalg.norm(residual) + atol2 = rtol * b_norm + # Added 1.00025 fudge factor because of `err` exceeding `atol` just + # very slightly on s390x (see gh-17839) + assert err <= 1.00025 * max(atol, atol2) + + +def test_zero_rhs(solver): + rng = np.random.default_rng(1684414984100503) + A = rng.random(size=[10, 10]) + A = A @ A.T + 10 * np.eye(10) + + b = np.zeros(10) + tols = np.r_[np.logspace(-10, 2, 7)] + + for tol in tols: + x, info = solver(A, b, rtol=tol) + assert info == 0 + assert_allclose(x, 0., atol=1e-15) + + x, info = solver(A, b, rtol=tol, x0=ones(10)) + assert info == 0 + assert_allclose(x, 0., atol=tol) + + if solver is not minres: + x, info = solver(A, b, rtol=tol, atol=0, x0=ones(10)) + if info == 0: + assert_allclose(x, 0) + + x, info = solver(A, b, rtol=tol, atol=tol) + assert info == 0 + assert_allclose(x, 0, atol=1e-300) + + x, info = solver(A, b, rtol=tol, atol=0) + assert info == 0 + assert_allclose(x, 0, atol=1e-300) + + +@pytest.mark.xfail(reason="see gh-18697") +def test_maxiter_worsening(solver): + if solver not in (gmres, lgmres, qmr): + # these were skipped from the very beginning, see gh-9201; gh-14160 + pytest.skip("Solver breakdown case") + # Check error does not grow (boundlessly) with increasing maxiter. + # This can occur due to the solvers hitting close to breakdown, + # which they should detect and halt as necessary. + # cf. gh-9100 + if (solver is gmres and platform.machine() == 'aarch64' + and sys.version_info[1] == 9): + pytest.xfail(reason="gh-13019") + if (solver is lgmres and + platform.machine() not in ['x86_64' 'x86', 'aarch64', 'arm64']): + # see gh-17839 + pytest.xfail(reason="fails on at least ppc64le, ppc64 and riscv64") + + # Singular matrix, rhs numerically not in range + A = np.array([[-0.1112795288033378, 0, 0, 0.16127952880333685], + [0, -0.13627952880333782 + 6.283185307179586j, 0, 0], + [0, 0, -0.13627952880333782 - 6.283185307179586j, 0], + [0.1112795288033368, 0j, 0j, -0.16127952880333785]]) + v = np.ones(4) + best_error = np.inf + + # Unable to match the Fortran code tolerance levels with this example + # Original tolerance values + + # slack_tol = 7 if platform.machine() == 'aarch64' else 5 + slack_tol = 9 + + for maxiter in range(1, 20): + x, info = solver(A, v, maxiter=maxiter, rtol=1e-8, atol=0) + + if info == 0: + assert norm(A @ x - v) <= 1e-8 * norm(v) + + error = np.linalg.norm(A @ x - v) + best_error = min(best_error, error) + + # Check with slack + assert error <= slack_tol * best_error + + +def test_x0_working(solver): + # Easy problem + rng = np.random.default_rng(1685363802304750) + n = 10 + A = rng.random(size=[n, n]) + A = A @ A.T + b = rng.random(n) + x0 = rng.random(n) + + if solver is minres: + kw = dict(rtol=1e-6) + else: + kw = dict(atol=0, rtol=1e-6) + + x, info = solver(A, b, **kw) + assert info == 0 + assert norm(A @ x - b) <= 1e-6 * norm(b) + + x, info = solver(A, b, x0=x0, **kw) + assert info == 0 + assert norm(A @ x - b) <= 2e-6*norm(b) + + +def test_x0_equals_Mb(case): + if case.solver is tfqmr: + pytest.skip("Solver does not support x0='Mb'") + A = case.A + b = case.b + x0 = 'Mb' + rtol = 1e-8 + x, info = case.solver(A, b, x0=x0, rtol=rtol) + + assert_array_equal(x0, 'Mb') # ensure that x0 is not overwritten + assert info == 0 + assert norm(A @ x - b) <= rtol * norm(b) + + +@pytest.mark.parametrize('solver', _SOLVERS) +def test_x0_solves_problem_exactly(solver): + # See gh-19948 + mat = np.eye(2) + rhs = np.array([-1., -1.]) + + sol, info = solver(mat, rhs, x0=rhs) + assert_allclose(sol, rhs) + assert info == 0 + + +# Specific tfqmr test +@pytest.mark.parametrize('case', IterativeParams().cases) +def test_show(case, capsys): + def cb(x): + pass + + x, info = tfqmr(case.A, case.b, callback=cb, show=True) + out, err = capsys.readouterr() + + if case.name == "sym-nonpd": + # no logs for some reason + exp = "" + elif case.name in ("nonsymposdef", "nonsymposdef-F"): + # Asymmetric and Positive Definite + exp = "TFQMR: Linear solve not converged due to reach MAXIT iterations" + else: # all other cases + exp = "TFQMR: Linear solve converged due to reach TOL iterations" + + assert out.startswith(exp) + assert err == "" + + +def test_positional_deprecation(solver): + # from test_x0_working + rng = np.random.default_rng(1685363802304750) + n = 10 + A = rng.random(size=[n, n]) + A = A @ A.T + b = rng.random(n) + x0 = rng.random(n) + with pytest.deprecated_call( + # due to the use of the _deprecate_positional_args decorator, it's not possible + # to separate the two warnings (1 for positional use, 1 for `tol` deprecation). + match="use keyword arguments.*|argument `tol` is deprecated.*" + ): + solver(A, b, x0, 1e-5) + + +class TestQMR: + @pytest.mark.filterwarnings('ignore::scipy.sparse.SparseEfficiencyWarning') + def test_leftright_precond(self): + """Check that QMR works with left and right preconditioners""" + + from scipy.sparse.linalg._dsolve import splu + from scipy.sparse.linalg._interface import LinearOperator + + n = 100 + + dat = ones(n) + A = spdiags([-2 * dat, 4 * dat, -dat], [-1, 0, 1], n, n) + b = arange(n, dtype='d') + + L = spdiags([-dat / 2, dat], [-1, 0], n, n) + U = spdiags([4 * dat, -dat], [0, 1], n, n) + L_solver = splu(L) + U_solver = splu(U) + + def L_solve(b): + return L_solver.solve(b) + + def U_solve(b): + return U_solver.solve(b) + + def LT_solve(b): + return L_solver.solve(b, 'T') + + def UT_solve(b): + return U_solver.solve(b, 'T') + + M1 = LinearOperator((n, n), matvec=L_solve, rmatvec=LT_solve) + M2 = LinearOperator((n, n), matvec=U_solve, rmatvec=UT_solve) + + rtol = 1e-8 + x, info = qmr(A, b, rtol=rtol, maxiter=15, M1=M1, M2=M2) + + assert info == 0 + assert norm(A @ x - b) <= rtol * norm(b) + + +class TestGMRES: + def test_basic(self): + A = np.vander(np.arange(10) + 1)[:, ::-1] + b = np.zeros(10) + b[0] = 1 + + x_gm, err = gmres(A, b, restart=5, maxiter=1) + + assert_allclose(x_gm[0], 0.359, rtol=1e-2) + + def test_callback(self): + + def store_residual(r, rvec): + rvec[rvec.nonzero()[0].max() + 1] = r + + # Define, A,b + A = csr_matrix(array([[-2, 1, 0, 0, 0, 0], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [0, 0, 0, 0, 1, -2]])) + b = ones((A.shape[0],)) + maxiter = 1 + rvec = zeros(maxiter + 1) + rvec[0] = 1.0 + + def callback(r): + return store_residual(r, rvec) + + x, flag = gmres(A, b, x0=zeros(A.shape[0]), rtol=1e-16, + maxiter=maxiter, callback=callback) + + # Expected output from SciPy 1.0.0 + assert_allclose(rvec, array([1.0, 0.81649658092772603]), rtol=1e-10) + + # Test preconditioned callback + M = 1e-3 * np.eye(A.shape[0]) + rvec = zeros(maxiter + 1) + rvec[0] = 1.0 + x, flag = gmres(A, b, M=M, rtol=1e-16, maxiter=maxiter, + callback=callback) + + # Expected output from SciPy 1.0.0 + # (callback has preconditioned residual!) + assert_allclose(rvec, array([1.0, 1e-3 * 0.81649658092772603]), + rtol=1e-10) + + def test_abi(self): + # Check we don't segfault on gmres with complex argument + A = eye(2) + b = ones(2) + r_x, r_info = gmres(A, b) + r_x = r_x.astype(complex) + x, info = gmres(A.astype(complex), b.astype(complex)) + + assert iscomplexobj(x) + assert_allclose(r_x, x) + assert r_info == info + + def test_atol_legacy(self): + + A = eye(2) + b = ones(2) + x, info = gmres(A, b, rtol=1e-5) + assert np.linalg.norm(A @ x - b) <= 1e-5 * np.linalg.norm(b) + assert_allclose(x, b, atol=0, rtol=1e-8) + + rndm = np.random.RandomState(12345) + A = rndm.rand(30, 30) + b = 1e-6 * ones(30) + x, info = gmres(A, b, rtol=1e-7, restart=20) + assert np.linalg.norm(A @ x - b) > 1e-7 + + A = eye(2) + b = 1e-10 * ones(2) + x, info = gmres(A, b, rtol=1e-8, atol=0) + assert np.linalg.norm(A @ x - b) <= 1e-8 * np.linalg.norm(b) + + def test_defective_precond_breakdown(self): + # Breakdown due to defective preconditioner + M = np.eye(3) + M[2, 2] = 0 + + b = np.array([0, 1, 1]) + x = np.array([1, 0, 0]) + A = np.diag([2, 3, 4]) + + x, info = gmres(A, b, x0=x, M=M, rtol=1e-15, atol=0) + + # Should not return nans, nor terminate with false success + assert not np.isnan(x).any() + if info == 0: + assert np.linalg.norm(A @ x - b) <= 1e-15 * np.linalg.norm(b) + + # The solution should be OK outside null space of M + assert_allclose(M @ (A @ x), M @ b) + + def test_defective_matrix_breakdown(self): + # Breakdown due to defective matrix + A = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 0]]) + b = np.array([1, 0, 1]) + rtol = 1e-8 + x, info = gmres(A, b, rtol=rtol, atol=0) + + # Should not return nans, nor terminate with false success + assert not np.isnan(x).any() + if info == 0: + assert np.linalg.norm(A @ x - b) <= rtol * np.linalg.norm(b) + + # The solution should be OK outside null space of A + assert_allclose(A @ (A @ x), A @ b) + + def test_callback_type(self): + # The legacy callback type changes meaning of 'maxiter' + np.random.seed(1) + A = np.random.rand(20, 20) + b = np.random.rand(20) + + cb_count = [0] + + def pr_norm_cb(r): + cb_count[0] += 1 + assert isinstance(r, float) + + def x_cb(x): + cb_count[0] += 1 + assert isinstance(x, np.ndarray) + + # 2 iterations is not enough to solve the problem + cb_count = [0] + x, info = gmres(A, b, rtol=1e-6, atol=0, callback=pr_norm_cb, + maxiter=2, restart=50) + assert info == 2 + assert cb_count[0] == 2 + + # With `callback_type` specified, no warning should be raised + cb_count = [0] + x, info = gmres(A, b, rtol=1e-6, atol=0, callback=pr_norm_cb, + maxiter=2, restart=50, callback_type='legacy') + assert info == 2 + assert cb_count[0] == 2 + + # 2 restart cycles is enough to solve the problem + cb_count = [0] + x, info = gmres(A, b, rtol=1e-6, atol=0, callback=pr_norm_cb, + maxiter=2, restart=50, callback_type='pr_norm') + assert info == 0 + assert cb_count[0] > 2 + + # 2 restart cycles is enough to solve the problem + cb_count = [0] + x, info = gmres(A, b, rtol=1e-6, atol=0, callback=x_cb, maxiter=2, + restart=50, callback_type='x') + assert info == 0 + assert cb_count[0] == 1 + + def test_callback_x_monotonic(self): + # Check that callback_type='x' gives monotonic norm decrease + np.random.seed(1) + A = np.random.rand(20, 20) + np.eye(20) + b = np.random.rand(20) + + prev_r = [np.inf] + count = [0] + + def x_cb(x): + r = np.linalg.norm(A @ x - b) + assert r <= prev_r[0] + prev_r[0] = r + count[0] += 1 + + x, info = gmres(A, b, rtol=1e-6, atol=0, callback=x_cb, maxiter=20, + restart=10, callback_type='x') + assert info == 20 + assert count[0] == 20 + + def test_restrt_dep(self): + with pytest.warns( + DeprecationWarning, + match="'gmres' keyword argument 'restrt'" + ): + gmres(np.array([1]), np.array([1]), restrt=10) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lgmres.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lgmres.py new file mode 100644 index 0000000000000000000000000000000000000000..22b030fede8efcbb86e22b98880984995d5ef6fe --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lgmres.py @@ -0,0 +1,211 @@ +"""Tests for the linalg._isolve.lgmres module +""" + +from numpy.testing import (assert_, assert_allclose, assert_equal, + suppress_warnings) + +import pytest +from platform import python_implementation + +import numpy as np +from numpy import zeros, array, allclose +from scipy.linalg import norm +from scipy.sparse import csr_matrix, eye, rand + +from scipy.sparse.linalg._interface import LinearOperator +from scipy.sparse.linalg import splu +from scipy.sparse.linalg._isolve import lgmres, gmres + + +Am = csr_matrix(array([[-2, 1, 0, 0, 0, 9], + [1, -2, 1, 0, 5, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 3, 0, 1, -2, 1], + [1, 0, 0, 0, 1, -2]])) +b = array([1, 2, 3, 4, 5, 6]) +count = [0] + + +def matvec(v): + count[0] += 1 + return Am@v + + +A = LinearOperator(matvec=matvec, shape=Am.shape, dtype=Am.dtype) + + +def do_solve(**kw): + count[0] = 0 + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x0, flag = lgmres(A, b, x0=zeros(A.shape[0]), + inner_m=6, rtol=1e-14, **kw) + count_0 = count[0] + assert_(allclose(A@x0, b, rtol=1e-12, atol=1e-12), norm(A@x0-b)) + return x0, count_0 + + +class TestLGMRES: + def test_preconditioner(self): + # Check that preconditioning works + pc = splu(Am.tocsc()) + M = LinearOperator(matvec=pc.solve, shape=A.shape, dtype=A.dtype) + + x0, count_0 = do_solve() + x1, count_1 = do_solve(M=M) + + assert_(count_1 == 3) + assert_(count_1 < count_0/2) + assert_(allclose(x1, x0, rtol=1e-14)) + + def test_outer_v(self): + # Check that the augmentation vectors behave as expected + + outer_v = [] + x0, count_0 = do_solve(outer_k=6, outer_v=outer_v) + assert_(len(outer_v) > 0) + assert_(len(outer_v) <= 6) + + x1, count_1 = do_solve(outer_k=6, outer_v=outer_v, + prepend_outer_v=True) + assert_(count_1 == 2, count_1) + assert_(count_1 < count_0/2) + assert_(allclose(x1, x0, rtol=1e-14)) + + # --- + + outer_v = [] + x0, count_0 = do_solve(outer_k=6, outer_v=outer_v, + store_outer_Av=False) + assert_(array([v[1] is None for v in outer_v]).all()) + assert_(len(outer_v) > 0) + assert_(len(outer_v) <= 6) + + x1, count_1 = do_solve(outer_k=6, outer_v=outer_v, + prepend_outer_v=True) + assert_(count_1 == 3, count_1) + assert_(count_1 < count_0/2) + assert_(allclose(x1, x0, rtol=1e-14)) + + @pytest.mark.skipif(python_implementation() == 'PyPy', + reason="Fails on PyPy CI runs. See #9507") + def test_arnoldi(self): + np.random.seed(1234) + + A = eye(2000) + rand(2000, 2000, density=5e-4) + b = np.random.rand(2000) + + # The inner arnoldi should be equivalent to gmres + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x0, flag0 = lgmres(A, b, x0=zeros(A.shape[0]), + inner_m=15, maxiter=1) + x1, flag1 = gmres(A, b, x0=zeros(A.shape[0]), + restart=15, maxiter=1) + + assert_equal(flag0, 1) + assert_equal(flag1, 1) + norm = np.linalg.norm(A.dot(x0) - b) + assert_(norm > 1e-4) + assert_allclose(x0, x1) + + def test_cornercase(self): + np.random.seed(1234) + + # Rounding error may prevent convergence with tol=0 --- ensure + # that the return values in this case are correct, and no + # exceptions are raised + + for n in [3, 5, 10, 100]: + A = 2*eye(n) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + + b = np.ones(n) + x, info = lgmres(A, b, maxiter=10) + assert_equal(info, 0) + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + x, info = lgmres(A, b, rtol=0, maxiter=10) + if info == 0: + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + b = np.random.rand(n) + x, info = lgmres(A, b, maxiter=10) + assert_equal(info, 0) + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + x, info = lgmres(A, b, rtol=0, maxiter=10) + if info == 0: + assert_allclose(A.dot(x) - b, 0, atol=1e-14) + + def test_nans(self): + A = eye(3, format='lil') + A[1, 1] = np.nan + b = np.ones(3) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + x, info = lgmres(A, b, rtol=0, maxiter=10) + assert_equal(info, 1) + + def test_breakdown_with_outer_v(self): + A = np.array([[1, 2], [3, 4]], dtype=float) + b = np.array([1, 2]) + + x = np.linalg.solve(A, b) + v0 = np.array([1, 0]) + + # The inner iteration should converge to the correct solution, + # since it's in the outer vector list + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + xp, info = lgmres(A, b, outer_v=[(v0, None), (x, None)], maxiter=1) + + assert_allclose(xp, x, atol=1e-12) + + def test_breakdown_underdetermined(self): + # Should find LSQ solution in the Krylov span in one inner + # iteration, despite solver breakdown from nilpotent A. + A = np.array([[0, 1, 1, 1], + [0, 0, 1, 1], + [0, 0, 0, 1], + [0, 0, 0, 0]], dtype=float) + + bs = [ + np.array([1, 1, 1, 1]), + np.array([1, 1, 1, 0]), + np.array([1, 1, 0, 0]), + np.array([1, 0, 0, 0]), + ] + + for b in bs: + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + xp, info = lgmres(A, b, maxiter=1) + resp = np.linalg.norm(A.dot(xp) - b) + + K = np.c_[b, A.dot(b), A.dot(A.dot(b)), A.dot(A.dot(A.dot(b)))] + y, _, _, _ = np.linalg.lstsq(A.dot(K), b, rcond=-1) + x = K.dot(y) + res = np.linalg.norm(A.dot(x) - b) + + assert_allclose(resp, res, err_msg=repr(b)) + + def test_denormals(self): + # Check that no warnings are emitted if the matrix contains + # numbers for which 1/x has no float representation, and that + # the solver behaves properly. + A = np.array([[1, 2], [3, 4]], dtype=float) + A *= 100 * np.nextafter(0, 1) + + b = np.array([1, 1]) + + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, ".*called without specifying.*") + xp, info = lgmres(A, b) + + if info == 0: + assert_allclose(A.dot(xp), b) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsmr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsmr.py new file mode 100644 index 0000000000000000000000000000000000000000..623b398fe97d2c2c8c208ba0eba8e47432b6ac14 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsmr.py @@ -0,0 +1,185 @@ +""" +Copyright (C) 2010 David Fong and Michael Saunders +Distributed under the same license as SciPy + +Testing Code for LSMR. + +03 Jun 2010: First version release with lsmr.py + +David Chin-lung Fong clfong@stanford.edu +Institute for Computational and Mathematical Engineering +Stanford University + +Michael Saunders saunders@stanford.edu +Systems Optimization Laboratory +Dept of MS&E, Stanford University. + +""" + +from numpy import array, arange, eye, zeros, ones, transpose, hstack +from numpy.linalg import norm +from numpy.testing import assert_allclose +import pytest +from scipy.sparse import coo_matrix +from scipy.sparse.linalg._interface import aslinearoperator +from scipy.sparse.linalg import lsmr +from .test_lsqr import G, b + + +class TestLSMR: + def setup_method(self): + self.n = 10 + self.m = 10 + + def assertCompatibleSystem(self, A, xtrue): + Afun = aslinearoperator(A) + b = Afun.matvec(xtrue) + x = lsmr(A, b)[0] + assert norm(x - xtrue) == pytest.approx(0, abs=1e-5) + + def testIdentityACase1(self): + A = eye(self.n) + xtrue = zeros((self.n, 1)) + self.assertCompatibleSystem(A, xtrue) + + def testIdentityACase2(self): + A = eye(self.n) + xtrue = ones((self.n,1)) + self.assertCompatibleSystem(A, xtrue) + + def testIdentityACase3(self): + A = eye(self.n) + xtrue = transpose(arange(self.n,0,-1)) + self.assertCompatibleSystem(A, xtrue) + + def testBidiagonalA(self): + A = lowerBidiagonalMatrix(20,self.n) + xtrue = transpose(arange(self.n,0,-1)) + self.assertCompatibleSystem(A,xtrue) + + def testScalarB(self): + A = array([[1.0, 2.0]]) + b = 3.0 + x = lsmr(A, b)[0] + assert norm(A.dot(x) - b) == pytest.approx(0) + + def testComplexX(self): + A = eye(self.n) + xtrue = transpose(arange(self.n, 0, -1) * (1 + 1j)) + self.assertCompatibleSystem(A, xtrue) + + def testComplexX0(self): + A = 4 * eye(self.n) + ones((self.n, self.n)) + xtrue = transpose(arange(self.n, 0, -1)) + b = aslinearoperator(A).matvec(xtrue) + x0 = zeros(self.n, dtype=complex) + x = lsmr(A, b, x0=x0)[0] + assert norm(x - xtrue) == pytest.approx(0, abs=1e-5) + + def testComplexA(self): + A = 4 * eye(self.n) + 1j * ones((self.n, self.n)) + xtrue = transpose(arange(self.n, 0, -1).astype(complex)) + self.assertCompatibleSystem(A, xtrue) + + def testComplexB(self): + A = 4 * eye(self.n) + ones((self.n, self.n)) + xtrue = transpose(arange(self.n, 0, -1) * (1 + 1j)) + b = aslinearoperator(A).matvec(xtrue) + x = lsmr(A, b)[0] + assert norm(x - xtrue) == pytest.approx(0, abs=1e-5) + + def testColumnB(self): + A = eye(self.n) + b = ones((self.n, 1)) + x = lsmr(A, b)[0] + assert norm(A.dot(x) - b.ravel()) == pytest.approx(0) + + def testInitialization(self): + # Test that the default setting is not modified + x_ref, _, itn_ref, normr_ref, *_ = lsmr(G, b) + assert_allclose(norm(b - G@x_ref), normr_ref, atol=1e-6) + + # Test passing zeros yields similar result + x0 = zeros(b.shape) + x = lsmr(G, b, x0=x0)[0] + assert_allclose(x, x_ref) + + # Test warm-start with single iteration + x0 = lsmr(G, b, maxiter=1)[0] + + x, _, itn, normr, *_ = lsmr(G, b, x0=x0) + assert_allclose(norm(b - G@x), normr, atol=1e-6) + + # NOTE(gh-12139): This doesn't always converge to the same value as + # ref because error estimates will be slightly different when calculated + # from zeros vs x0 as a result only compare norm and itn (not x). + + # x generally converges 1 iteration faster because it started at x0. + # itn == itn_ref means that lsmr(x0) took an extra iteration see above. + # -1 is technically possible but is rare (1 in 100000) so it's more + # likely to be an error elsewhere. + assert itn - itn_ref in (0, 1) + + # If an extra iteration is performed normr may be 0, while normr_ref + # may be much larger. + assert normr < normr_ref * (1 + 1e-6) + + +class TestLSMRReturns: + def setup_method(self): + self.n = 10 + self.A = lowerBidiagonalMatrix(20, self.n) + self.xtrue = transpose(arange(self.n, 0, -1)) + self.Afun = aslinearoperator(self.A) + self.b = self.Afun.matvec(self.xtrue) + self.x0 = ones(self.n) + self.x00 = self.x0.copy() + self.returnValues = lsmr(self.A, self.b) + self.returnValuesX0 = lsmr(self.A, self.b, x0=self.x0) + + def test_unchanged_x0(self): + x, istop, itn, normr, normar, normA, condA, normx = self.returnValuesX0 + assert_allclose(self.x00, self.x0) + + def testNormr(self): + x, istop, itn, normr, normar, normA, condA, normx = self.returnValues + assert norm(self.b - self.Afun.matvec(x)) == pytest.approx(normr) + + def testNormar(self): + x, istop, itn, normr, normar, normA, condA, normx = self.returnValues + assert (norm(self.Afun.rmatvec(self.b - self.Afun.matvec(x))) + == pytest.approx(normar)) + + def testNormx(self): + x, istop, itn, normr, normar, normA, condA, normx = self.returnValues + assert norm(x) == pytest.approx(normx) + + +def lowerBidiagonalMatrix(m, n): + # This is a simple example for testing LSMR. + # It uses the leading m*n submatrix from + # A = [ 1 + # 1 2 + # 2 3 + # 3 4 + # ... + # n ] + # suitably padded by zeros. + # + # 04 Jun 2010: First version for distribution with lsmr.py + if m <= n: + row = hstack((arange(m, dtype=int), + arange(1, m, dtype=int))) + col = hstack((arange(m, dtype=int), + arange(m-1, dtype=int))) + data = hstack((arange(1, m+1, dtype=float), + arange(1,m, dtype=float))) + return coo_matrix((data, (row, col)), shape=(m,n)) + else: + row = hstack((arange(n, dtype=int), + arange(1, n+1, dtype=int))) + col = hstack((arange(n, dtype=int), + arange(n, dtype=int))) + data = hstack((arange(1, n+1, dtype=float), + arange(1,n+1, dtype=float))) + return coo_matrix((data,(row, col)), shape=(m,n)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py new file mode 100644 index 0000000000000000000000000000000000000000..c46290ac3d7af3b7571659d6219157dcd25e29c3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py @@ -0,0 +1,120 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_array_equal, assert_equal +import pytest +import scipy.sparse +import scipy.sparse.linalg +from scipy.sparse.linalg import lsqr + +# Set up a test problem +n = 35 +G = np.eye(n) +normal = np.random.normal +norm = np.linalg.norm + +for jj in range(5): + gg = normal(size=n) + hh = gg * gg.T + G += (hh + hh.T) * 0.5 + G += normal(size=n) * normal(size=n) + +b = normal(size=n) + +# tolerance for atol/btol keywords of lsqr() +tol = 2e-10 +# tolerances for testing the results of the lsqr() call with assert_allclose +# These tolerances are a bit fragile - see discussion in gh-15301. +atol_test = 4e-10 +rtol_test = 2e-8 +show = False +maxit = None + + +def test_lsqr_basic(): + b_copy = b.copy() + xo, *_ = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit) + assert_array_equal(b_copy, b) + + svx = np.linalg.solve(G, b) + assert_allclose(xo, svx, atol=atol_test, rtol=rtol_test) + + # Now the same but with damp > 0. + # This is equivalent to solving the extended system: + # ( G ) @ x = ( b ) + # ( damp*I ) ( 0 ) + damp = 1.5 + xo, *_ = lsqr( + G, b, damp=damp, show=show, atol=tol, btol=tol, iter_lim=maxit) + + Gext = np.r_[G, damp * np.eye(G.shape[1])] + bext = np.r_[b, np.zeros(G.shape[1])] + svx, *_ = np.linalg.lstsq(Gext, bext, rcond=None) + assert_allclose(xo, svx, atol=atol_test, rtol=rtol_test) + + +def test_gh_2466(): + row = np.array([0, 0]) + col = np.array([0, 1]) + val = np.array([1, -1]) + A = scipy.sparse.coo_matrix((val, (row, col)), shape=(1, 2)) + b = np.asarray([4]) + lsqr(A, b) + + +def test_well_conditioned_problems(): + # Test that sparse the lsqr solver returns the right solution + # on various problems with different random seeds. + # This is a non-regression test for a potential ZeroDivisionError + # raised when computing the `test2` & `test3` convergence conditions. + n = 10 + A_sparse = scipy.sparse.eye(n, n) + A_dense = A_sparse.toarray() + + with np.errstate(invalid='raise'): + for seed in range(30): + rng = np.random.RandomState(seed + 10) + beta = rng.rand(n) + beta[beta == 0] = 0.00001 # ensure that all the betas are not null + b = A_sparse @ beta[:, np.newaxis] + output = lsqr(A_sparse, b, show=show) + + # Check that the termination condition corresponds to an approximate + # solution to Ax = b + assert_equal(output[1], 1) + solution = output[0] + + # Check that we recover the ground truth solution + assert_allclose(solution, beta) + + # Sanity check: compare to the dense array solver + reference_solution = np.linalg.solve(A_dense, b).ravel() + assert_allclose(solution, reference_solution) + + +def test_b_shapes(): + # Test b being a scalar. + A = np.array([[1.0, 2.0]]) + b = 3.0 + x = lsqr(A, b)[0] + assert norm(A.dot(x) - b) == pytest.approx(0) + + # Test b being a column vector. + A = np.eye(10) + b = np.ones((10, 1)) + x = lsqr(A, b)[0] + assert norm(A.dot(x) - b.ravel()) == pytest.approx(0) + + +def test_initialization(): + # Test the default setting is the same as zeros + b_copy = b.copy() + x_ref = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit) + x0 = np.zeros(x_ref[0].shape) + x = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit, x0=x0) + assert_array_equal(b_copy, b) + assert_allclose(x_ref[0], x[0]) + + # Test warm-start with single iteration + x0 = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=1)[0] + x = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit, x0=x0) + assert_allclose(x_ref[0], x[0]) + assert_array_equal(b_copy, b) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_minres.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_minres.py new file mode 100644 index 0000000000000000000000000000000000000000..6d51ba274cacb64223c81bf385a6df0e1a426a7d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_minres.py @@ -0,0 +1,97 @@ +import numpy as np +from numpy.linalg import norm +from numpy.testing import assert_equal, assert_allclose, assert_ +from scipy.sparse.linalg._isolve import minres + +from pytest import raises as assert_raises + + +def get_sample_problem(): + # A random 10 x 10 symmetric matrix + np.random.seed(1234) + matrix = np.random.rand(10, 10) + matrix = matrix + matrix.T + # A random vector of length 10 + vector = np.random.rand(10) + return matrix, vector + + +def test_singular(): + A, b = get_sample_problem() + A[0, ] = 0 + b[0] = 0 + xp, info = minres(A, b) + assert_equal(info, 0) + assert norm(A @ xp - b) <= 1e-5 * norm(b) + + +def test_x0_is_used_by(): + A, b = get_sample_problem() + # Random x0 to feed minres + np.random.seed(12345) + x0 = np.random.rand(10) + trace = [] + + def trace_iterates(xk): + trace.append(xk) + minres(A, b, x0=x0, callback=trace_iterates) + trace_with_x0 = trace + + trace = [] + minres(A, b, callback=trace_iterates) + assert_(not np.array_equal(trace_with_x0[0], trace[0])) + + +def test_shift(): + A, b = get_sample_problem() + shift = 0.5 + shifted_A = A - shift * np.eye(10) + x1, info1 = minres(A, b, shift=shift) + x2, info2 = minres(shifted_A, b) + assert_equal(info1, 0) + assert_allclose(x1, x2, rtol=1e-5) + + +def test_asymmetric_fail(): + """Asymmetric matrix should raise `ValueError` when check=True""" + A, b = get_sample_problem() + A[1, 2] = 1 + A[2, 1] = 2 + with assert_raises(ValueError): + xp, info = minres(A, b, check=True) + + +def test_minres_non_default_x0(): + np.random.seed(1234) + rtol = 1e-6 + a = np.random.randn(5, 5) + a = np.dot(a, a.T) + b = np.random.randn(5) + c = np.random.randn(5) + x = minres(a, b, x0=c, rtol=rtol)[0] + assert norm(a @ x - b) <= rtol * norm(b) + + +def test_minres_precond_non_default_x0(): + np.random.seed(12345) + rtol = 1e-6 + a = np.random.randn(5, 5) + a = np.dot(a, a.T) + b = np.random.randn(5) + c = np.random.randn(5) + m = np.random.randn(5, 5) + m = np.dot(m, m.T) + x = minres(a, b, M=m, x0=c, rtol=rtol)[0] + assert norm(a @ x - b) <= rtol * norm(b) + + +def test_minres_precond_exact_x0(): + np.random.seed(1234) + rtol = 1e-6 + a = np.eye(10) + b = np.ones(10) + c = np.ones(10) + m = np.random.randn(10, 10) + m = np.dot(m, m.T) + x = minres(a, b, M=m, x0=c, rtol=rtol)[0] + assert norm(a @ x - b) <= rtol * norm(b) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_utils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..bb62e2223d515288bd5aa6c5a70028279c4f6d30 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tests/test_utils.py @@ -0,0 +1,9 @@ +import numpy as np +from pytest import raises as assert_raises + +import scipy.sparse.linalg._isolve.utils as utils + + +def test_make_system_bad_shape(): + assert_raises(ValueError, + utils.make_system, np.zeros((5,3)), None, np.zeros(4), np.zeros(4)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py new file mode 100644 index 0000000000000000000000000000000000000000..c8d0231c51bb41f971ac2f6f1c569df3e230c884 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py @@ -0,0 +1,191 @@ +import numpy as np +from .iterative import _get_atol_rtol +from .utils import make_system +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args + + +__all__ = ['tfqmr'] + + +@_deprecate_positional_args(version="1.14.0") +def tfqmr(A, b, x0=None, *, tol=_NoValue, maxiter=None, M=None, + callback=None, atol=None, rtol=1e-5, show=False): + """ + Use Transpose-Free Quasi-Minimal Residual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, `A` can be a linear operator which can + produce ``Ax`` using, e.g., + `scipy.sparse.linalg.LinearOperator`. + b : {ndarray} + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : {ndarray} + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``rtol=1e-5``, the default for ``atol`` is ``rtol``. + + .. warning:: + + The default value for ``atol`` will be changed to ``0.0`` in + SciPy 1.14.0. + maxiter : int, optional + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + Default is ``min(10000, ndofs * 10)``, where ``ndofs = A.shape[0]``. + M : {sparse matrix, ndarray, LinearOperator} + Inverse of the preconditioner of A. M should approximate the + inverse of A and be easy to solve for (see Notes). Effective + preconditioning dramatically improves the rate of convergence, + which implies that fewer iterations are needed to reach a given + error tolerance. By default, no preconditioner is used. + callback : function, optional + User-supplied function to call after each iteration. It is called + as `callback(xk)`, where `xk` is the current solution vector. + show : bool, optional + Specify ``show = True`` to show the convergence, ``show = False`` is + to close the output of the convergence. + Default is `False`. + tol : float, optional, deprecated + + .. deprecated:: 1.12.0 + `tfqmr` keyword argument ``tol`` is deprecated in favor of ``rtol`` + and will be removed in SciPy 1.14.0. + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + + - 0 : successful exit + - >0 : convergence to tolerance not achieved, number of iterations + - <0 : illegal input or breakdown + + Notes + ----- + The Transpose-Free QMR algorithm is derived from the CGS algorithm. + However, unlike CGS, the convergence curves for the TFQMR method is + smoothed by computing a quasi minimization of the residual norm. The + implementation supports left preconditioner, and the "residual norm" + to compute in convergence criterion is actually an upper bound on the + actual residual norm ``||b - Axk||``. + + References + ---------- + .. [1] R. W. Freund, A Transpose-Free Quasi-Minimal Residual Algorithm for + Non-Hermitian Linear Systems, SIAM J. Sci. Comput., 14(2), 470-482, + 1993. + .. [2] Y. Saad, Iterative Methods for Sparse Linear Systems, 2nd edition, + SIAM, Philadelphia, 2003. + .. [3] C. T. Kelley, Iterative Methods for Linear and Nonlinear Equations, + number 16 in Frontiers in Applied Mathematics, SIAM, Philadelphia, + 1995. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import tfqmr + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = tfqmr(A, b) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + + # Check data type + dtype = A.dtype + if np.issubdtype(dtype, np.int64): + dtype = float + A = A.astype(dtype) + if np.issubdtype(b.dtype, np.int64): + b = b.astype(dtype) + + A, M, x, b, postprocess = make_system(A, M, x0, b) + + # Check if the R.H.S is a zero vector + if np.linalg.norm(b) == 0.: + x = b.copy() + return (postprocess(x), 0) + + ndofs = A.shape[0] + if maxiter is None: + maxiter = min(10000, ndofs * 10) + + if x0 is None: + r = b.copy() + else: + r = b - A.matvec(x) + u = r + w = r.copy() + # Take rstar as b - Ax0, that is rstar := r = b - Ax0 mathematically + rstar = r + v = M.matvec(A.matvec(r)) + uhat = v + d = theta = eta = 0. + # at this point we know rstar == r, so rho is always real + rho = np.inner(rstar.conjugate(), r).real + rhoLast = rho + r0norm = np.sqrt(rho) + tau = r0norm + if r0norm == 0: + return (postprocess(x), 0) + + # we call this to get the right atol and raise warnings as necessary + atol, _ = _get_atol_rtol('tfqmr', r0norm, tol, atol, rtol) + + for iter in range(maxiter): + even = iter % 2 == 0 + if (even): + vtrstar = np.inner(rstar.conjugate(), v) + # Check breakdown + if vtrstar == 0.: + return (postprocess(x), -1) + alpha = rho / vtrstar + uNext = u - alpha * v # [1]-(5.6) + w -= alpha * uhat # [1]-(5.8) + d = u + (theta**2 / alpha) * eta * d # [1]-(5.5) + # [1]-(5.2) + theta = np.linalg.norm(w) / tau + c = np.sqrt(1. / (1 + theta**2)) + tau *= theta * c + # Calculate step and direction [1]-(5.4) + eta = (c**2) * alpha + z = M.matvec(d) + x += eta * z + + if callback is not None: + callback(x) + + # Convergence criterion + if tau * np.sqrt(iter+1) < atol: + if (show): + print("TFQMR: Linear solve converged due to reach TOL " + f"iterations {iter+1}") + return (postprocess(x), 0) + + if (not even): + # [1]-(5.7) + rho = np.inner(rstar.conjugate(), w) + beta = rho / rhoLast + u = w + beta * u + v = beta * uhat + (beta**2) * v + uhat = M.matvec(A.matvec(u)) + v += uhat + else: + uhat = M.matvec(A.matvec(uNext)) + u = uNext + rhoLast = rho + + if (show): + print("TFQMR: Linear solve not converged due to reach MAXIT " + f"iterations {iter+1}") + return (postprocess(x), maxiter) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/utils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..80f37fc1cf63fa0352fd93d62be758f87c065db5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_isolve/utils.py @@ -0,0 +1,127 @@ +__docformat__ = "restructuredtext en" + +__all__ = [] + + +from numpy import asanyarray, asarray, array, zeros + +from scipy.sparse.linalg._interface import aslinearoperator, LinearOperator, \ + IdentityOperator + +_coerce_rules = {('f','f'):'f', ('f','d'):'d', ('f','F'):'F', + ('f','D'):'D', ('d','f'):'d', ('d','d'):'d', + ('d','F'):'D', ('d','D'):'D', ('F','f'):'F', + ('F','d'):'D', ('F','F'):'F', ('F','D'):'D', + ('D','f'):'D', ('D','d'):'D', ('D','F'):'D', + ('D','D'):'D'} + + +def coerce(x,y): + if x not in 'fdFD': + x = 'd' + if y not in 'fdFD': + y = 'd' + return _coerce_rules[x,y] + + +def id(x): + return x + + +def make_system(A, M, x0, b): + """Make a linear system Ax=b + + Parameters + ---------- + A : LinearOperator + sparse or dense matrix (or any valid input to aslinearoperator) + M : {LinearOperator, Nones} + preconditioner + sparse or dense matrix (or any valid input to aslinearoperator) + x0 : {array_like, str, None} + initial guess to iterative method. + ``x0 = 'Mb'`` means using the nonzero initial guess ``M @ b``. + Default is `None`, which means using the zero initial guess. + b : array_like + right hand side + + Returns + ------- + (A, M, x, b, postprocess) + A : LinearOperator + matrix of the linear system + M : LinearOperator + preconditioner + x : rank 1 ndarray + initial guess + b : rank 1 ndarray + right hand side + postprocess : function + converts the solution vector to the appropriate + type and dimensions (e.g. (N,1) matrix) + + """ + A_ = A + A = aslinearoperator(A) + + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix, but got shape={(A.shape,)}') + + N = A.shape[0] + + b = asanyarray(b) + + if not (b.shape == (N,1) or b.shape == (N,)): + raise ValueError(f'shapes of A {A.shape} and b {b.shape} are ' + 'incompatible') + + if b.dtype.char not in 'fdFD': + b = b.astype('d') # upcast non-FP types to double + + def postprocess(x): + return x + + if hasattr(A,'dtype'): + xtype = A.dtype.char + else: + xtype = A.matvec(b).dtype.char + xtype = coerce(xtype, b.dtype.char) + + b = asarray(b,dtype=xtype) # make b the same type as x + b = b.ravel() + + # process preconditioner + if M is None: + if hasattr(A_,'psolve'): + psolve = A_.psolve + else: + psolve = id + if hasattr(A_,'rpsolve'): + rpsolve = A_.rpsolve + else: + rpsolve = id + if psolve is id and rpsolve is id: + M = IdentityOperator(shape=A.shape, dtype=A.dtype) + else: + M = LinearOperator(A.shape, matvec=psolve, rmatvec=rpsolve, + dtype=A.dtype) + else: + M = aslinearoperator(M) + if A.shape != M.shape: + raise ValueError('matrix and preconditioner have different shapes') + + # set initial guess + if x0 is None: + x = zeros(N, dtype=xtype) + elif isinstance(x0, str): + if x0 == 'Mb': # use nonzero initial guess ``M @ b`` + bCopy = b.copy() + x = M.matvec(bCopy) + else: + x = array(x0, dtype=xtype) + if not (x.shape == (N, 1) or x.shape == (N,)): + raise ValueError(f'shapes of A {A.shape} and ' + f'x0 {x.shape} are incompatible') + x = x.ravel() + + return A, M, x, b, postprocess diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_matfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..525c97b2cca5555a917cc3bdd7617a50436538a5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_matfuncs.py @@ -0,0 +1,940 @@ +""" +Sparse matrix functions +""" + +# +# Authors: Travis Oliphant, March 2002 +# Anthony Scopatz, August 2012 (Sparse Updates) +# Jake Vanderplas, August 2012 (Sparse Updates) +# + +__all__ = ['expm', 'inv', 'matrix_power'] + +import numpy as np +from scipy.linalg._basic import solve, solve_triangular + +from scipy.sparse._base import issparse +from scipy.sparse.linalg import spsolve +from scipy.sparse._sputils import is_pydata_spmatrix, isintlike + +import scipy.sparse +import scipy.sparse.linalg +from scipy.sparse.linalg._interface import LinearOperator +from scipy.sparse._construct import eye + +from ._expm_multiply import _ident_like, _exact_1_norm as _onenorm + + +UPPER_TRIANGULAR = 'upper_triangular' + + +def inv(A): + """ + Compute the inverse of a sparse matrix + + Parameters + ---------- + A : (M, M) sparse matrix + square matrix to be inverted + + Returns + ------- + Ainv : (M, M) sparse matrix + inverse of `A` + + Notes + ----- + This computes the sparse inverse of `A`. If the inverse of `A` is expected + to be non-sparse, it will likely be faster to convert `A` to dense and use + `scipy.linalg.inv`. + + Examples + -------- + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import inv + >>> A = csc_matrix([[1., 0.], [1., 2.]]) + >>> Ainv = inv(A) + >>> Ainv + <2x2 sparse matrix of type '' + with 3 stored elements in Compressed Sparse Column format> + >>> A.dot(Ainv) + <2x2 sparse matrix of type '' + with 2 stored elements in Compressed Sparse Column format> + >>> A.dot(Ainv).toarray() + array([[ 1., 0.], + [ 0., 1.]]) + + .. versionadded:: 0.12.0 + + """ + # Check input + if not (scipy.sparse.issparse(A) or is_pydata_spmatrix(A)): + raise TypeError('Input must be a sparse matrix') + + # Use sparse direct solver to solve "AX = I" accurately + I = _ident_like(A) + Ainv = spsolve(A, I) + return Ainv + + +def _onenorm_matrix_power_nnm(A, p): + """ + Compute the 1-norm of a non-negative integer power of a non-negative matrix. + + Parameters + ---------- + A : a square ndarray or matrix or sparse matrix + Input matrix with non-negative entries. + p : non-negative integer + The power to which the matrix is to be raised. + + Returns + ------- + out : float + The 1-norm of the matrix power p of A. + + """ + # Check input + if int(p) != p or p < 0: + raise ValueError('expected non-negative integer p') + p = int(p) + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + + # Explicitly make a column vector so that this works when A is a + # numpy matrix (in addition to ndarray and sparse matrix). + v = np.ones((A.shape[0], 1), dtype=float) + M = A.T + for i in range(p): + v = M.dot(v) + return np.max(v) + + +def _is_upper_triangular(A): + # This function could possibly be of wider interest. + if issparse(A): + lower_part = scipy.sparse.tril(A, -1) + # Check structural upper triangularity, + # then coincidental upper triangularity if needed. + return lower_part.nnz == 0 or lower_part.count_nonzero() == 0 + elif is_pydata_spmatrix(A): + import sparse + lower_part = sparse.tril(A, -1) + return lower_part.nnz == 0 + else: + return not np.tril(A, -1).any() + + +def _smart_matrix_product(A, B, alpha=None, structure=None): + """ + A matrix product that knows about sparse and structured matrices. + + Parameters + ---------- + A : 2d ndarray + First matrix. + B : 2d ndarray + Second matrix. + alpha : float + The matrix product will be scaled by this constant. + structure : str, optional + A string describing the structure of both matrices `A` and `B`. + Only `upper_triangular` is currently supported. + + Returns + ------- + M : 2d ndarray + Matrix product of A and B. + + """ + if len(A.shape) != 2: + raise ValueError('expected A to be a rectangular matrix') + if len(B.shape) != 2: + raise ValueError('expected B to be a rectangular matrix') + f = None + if structure == UPPER_TRIANGULAR: + if (not issparse(A) and not issparse(B) + and not is_pydata_spmatrix(A) and not is_pydata_spmatrix(B)): + f, = scipy.linalg.get_blas_funcs(('trmm',), (A, B)) + if f is not None: + if alpha is None: + alpha = 1. + out = f(alpha, A, B) + else: + if alpha is None: + out = A.dot(B) + else: + out = alpha * A.dot(B) + return out + + +class MatrixPowerOperator(LinearOperator): + + def __init__(self, A, p, structure=None): + if A.ndim != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if p < 0: + raise ValueError('expected p to be a non-negative integer') + self._A = A + self._p = p + self._structure = structure + self.dtype = A.dtype + self.ndim = A.ndim + self.shape = A.shape + + def _matvec(self, x): + for i in range(self._p): + x = self._A.dot(x) + return x + + def _rmatvec(self, x): + A_T = self._A.T + x = x.ravel() + for i in range(self._p): + x = A_T.dot(x) + return x + + def _matmat(self, X): + for i in range(self._p): + X = _smart_matrix_product(self._A, X, structure=self._structure) + return X + + @property + def T(self): + return MatrixPowerOperator(self._A.T, self._p) + + +class ProductOperator(LinearOperator): + """ + For now, this is limited to products of multiple square matrices. + """ + + def __init__(self, *args, **kwargs): + self._structure = kwargs.get('structure', None) + for A in args: + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError( + 'For now, the ProductOperator implementation is ' + 'limited to the product of multiple square matrices.') + if args: + n = args[0].shape[0] + for A in args: + for d in A.shape: + if d != n: + raise ValueError( + 'The square matrices of the ProductOperator ' + 'must all have the same shape.') + self.shape = (n, n) + self.ndim = len(self.shape) + self.dtype = np.result_type(*[x.dtype for x in args]) + self._operator_sequence = args + + def _matvec(self, x): + for A in reversed(self._operator_sequence): + x = A.dot(x) + return x + + def _rmatvec(self, x): + x = x.ravel() + for A in self._operator_sequence: + x = A.T.dot(x) + return x + + def _matmat(self, X): + for A in reversed(self._operator_sequence): + X = _smart_matrix_product(A, X, structure=self._structure) + return X + + @property + def T(self): + T_args = [A.T for A in reversed(self._operator_sequence)] + return ProductOperator(*T_args) + + +def _onenormest_matrix_power(A, p, + t=2, itmax=5, compute_v=False, compute_w=False, structure=None): + """ + Efficiently estimate the 1-norm of A^p. + + Parameters + ---------- + A : ndarray + Matrix whose 1-norm of a power is to be computed. + p : int + Non-negative integer power. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + return scipy.sparse.linalg.onenormest( + MatrixPowerOperator(A, p, structure=structure)) + + +def _onenormest_product(operator_seq, + t=2, itmax=5, compute_v=False, compute_w=False, structure=None): + """ + Efficiently estimate the 1-norm of the matrix product of the args. + + Parameters + ---------- + operator_seq : linear operator sequence + Matrices whose 1-norm of product is to be computed. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + structure : str, optional + A string describing the structure of all operators. + Only `upper_triangular` is currently supported. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + return scipy.sparse.linalg.onenormest( + ProductOperator(*operator_seq, structure=structure)) + + +class _ExpmPadeHelper: + """ + Help lazily evaluate a matrix exponential. + + The idea is to not do more work than we need for high expm precision, + so we lazily compute matrix powers and store or precompute + other properties of the matrix. + + """ + + def __init__(self, A, structure=None, use_exact_onenorm=False): + """ + Initialize the object. + + Parameters + ---------- + A : a dense or sparse square numpy matrix or ndarray + The matrix to be exponentiated. + structure : str, optional + A string describing the structure of matrix `A`. + Only `upper_triangular` is currently supported. + use_exact_onenorm : bool, optional + If True then only the exact one-norm of matrix powers and products + will be used. Otherwise, the one-norm of powers and products + may initially be estimated. + """ + self.A = A + self._A2 = None + self._A4 = None + self._A6 = None + self._A8 = None + self._A10 = None + self._d4_exact = None + self._d6_exact = None + self._d8_exact = None + self._d10_exact = None + self._d4_approx = None + self._d6_approx = None + self._d8_approx = None + self._d10_approx = None + self.ident = _ident_like(A) + self.structure = structure + self.use_exact_onenorm = use_exact_onenorm + + @property + def A2(self): + if self._A2 is None: + self._A2 = _smart_matrix_product( + self.A, self.A, structure=self.structure) + return self._A2 + + @property + def A4(self): + if self._A4 is None: + self._A4 = _smart_matrix_product( + self.A2, self.A2, structure=self.structure) + return self._A4 + + @property + def A6(self): + if self._A6 is None: + self._A6 = _smart_matrix_product( + self.A4, self.A2, structure=self.structure) + return self._A6 + + @property + def A8(self): + if self._A8 is None: + self._A8 = _smart_matrix_product( + self.A6, self.A2, structure=self.structure) + return self._A8 + + @property + def A10(self): + if self._A10 is None: + self._A10 = _smart_matrix_product( + self.A4, self.A6, structure=self.structure) + return self._A10 + + @property + def d4_tight(self): + if self._d4_exact is None: + self._d4_exact = _onenorm(self.A4)**(1/4.) + return self._d4_exact + + @property + def d6_tight(self): + if self._d6_exact is None: + self._d6_exact = _onenorm(self.A6)**(1/6.) + return self._d6_exact + + @property + def d8_tight(self): + if self._d8_exact is None: + self._d8_exact = _onenorm(self.A8)**(1/8.) + return self._d8_exact + + @property + def d10_tight(self): + if self._d10_exact is None: + self._d10_exact = _onenorm(self.A10)**(1/10.) + return self._d10_exact + + @property + def d4_loose(self): + if self.use_exact_onenorm: + return self.d4_tight + if self._d4_exact is not None: + return self._d4_exact + else: + if self._d4_approx is None: + self._d4_approx = _onenormest_matrix_power(self.A2, 2, + structure=self.structure)**(1/4.) + return self._d4_approx + + @property + def d6_loose(self): + if self.use_exact_onenorm: + return self.d6_tight + if self._d6_exact is not None: + return self._d6_exact + else: + if self._d6_approx is None: + self._d6_approx = _onenormest_matrix_power(self.A2, 3, + structure=self.structure)**(1/6.) + return self._d6_approx + + @property + def d8_loose(self): + if self.use_exact_onenorm: + return self.d8_tight + if self._d8_exact is not None: + return self._d8_exact + else: + if self._d8_approx is None: + self._d8_approx = _onenormest_matrix_power(self.A4, 2, + structure=self.structure)**(1/8.) + return self._d8_approx + + @property + def d10_loose(self): + if self.use_exact_onenorm: + return self.d10_tight + if self._d10_exact is not None: + return self._d10_exact + else: + if self._d10_approx is None: + self._d10_approx = _onenormest_product((self.A4, self.A6), + structure=self.structure)**(1/10.) + return self._d10_approx + + def pade3(self): + b = (120., 60., 12., 1.) + U = _smart_matrix_product(self.A, + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade5(self): + b = (30240., 15120., 3360., 420., 30., 1.) + U = _smart_matrix_product(self.A, + b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade7(self): + b = (17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.) + U = _smart_matrix_product(self.A, + b[7]*self.A6 + b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[6]*self.A6 + b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade9(self): + b = (17643225600., 8821612800., 2075673600., 302702400., 30270240., + 2162160., 110880., 3960., 90., 1.) + U = _smart_matrix_product(self.A, + (b[9]*self.A8 + b[7]*self.A6 + b[5]*self.A4 + + b[3]*self.A2 + b[1]*self.ident), + structure=self.structure) + V = (b[8]*self.A8 + b[6]*self.A6 + b[4]*self.A4 + + b[2]*self.A2 + b[0]*self.ident) + return U, V + + def pade13_scaled(self, s): + b = (64764752532480000., 32382376266240000., 7771770303897600., + 1187353796428800., 129060195264000., 10559470521600., + 670442572800., 33522128640., 1323241920., 40840800., 960960., + 16380., 182., 1.) + B = self.A * 2**-s + B2 = self.A2 * 2**(-2*s) + B4 = self.A4 * 2**(-4*s) + B6 = self.A6 * 2**(-6*s) + U2 = _smart_matrix_product(B6, + b[13]*B6 + b[11]*B4 + b[9]*B2, + structure=self.structure) + U = _smart_matrix_product(B, + (U2 + b[7]*B6 + b[5]*B4 + + b[3]*B2 + b[1]*self.ident), + structure=self.structure) + V2 = _smart_matrix_product(B6, + b[12]*B6 + b[10]*B4 + b[8]*B2, + structure=self.structure) + V = V2 + b[6]*B6 + b[4]*B4 + b[2]*B2 + b[0]*self.ident + return U, V + + +def expm(A): + """ + Compute the matrix exponential using Pade approximation. + + Parameters + ---------- + A : (M,M) array_like or sparse matrix + 2D Array or Matrix (sparse or dense) to be exponentiated + + Returns + ------- + expA : (M,M) ndarray + Matrix exponential of `A` + + Notes + ----- + This is algorithm (6.1) which is a simplification of algorithm (5.1). + + .. versionadded:: 0.12.0 + + References + ---------- + .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2009) + "A New Scaling and Squaring Algorithm for the Matrix Exponential." + SIAM Journal on Matrix Analysis and Applications. + 31 (3). pp. 970-989. ISSN 1095-7162 + + Examples + -------- + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import expm + >>> A = csc_matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) + >>> A.toarray() + array([[1, 0, 0], + [0, 2, 0], + [0, 0, 3]], dtype=int64) + >>> Aexp = expm(A) + >>> Aexp + <3x3 sparse matrix of type '' + with 3 stored elements in Compressed Sparse Column format> + >>> Aexp.toarray() + array([[ 2.71828183, 0. , 0. ], + [ 0. , 7.3890561 , 0. ], + [ 0. , 0. , 20.08553692]]) + """ + return _expm(A, use_exact_onenorm='auto') + + +def _expm(A, use_exact_onenorm): + # Core of expm, separated to allow testing exact and approximate + # algorithms. + + # Avoid indiscriminate asarray() to allow sparse or other strange arrays. + if isinstance(A, (list, tuple, np.matrix)): + A = np.asarray(A) + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected a square matrix') + + # gracefully handle size-0 input, + # carefully handling sparse scenario + if A.shape == (0, 0): + out = np.zeros([0, 0], dtype=A.dtype) + if issparse(A) or is_pydata_spmatrix(A): + return A.__class__(out) + return out + + # Trivial case + if A.shape == (1, 1): + out = [[np.exp(A[0, 0])]] + + # Avoid indiscriminate casting to ndarray to + # allow for sparse or other strange arrays + if issparse(A) or is_pydata_spmatrix(A): + return A.__class__(out) + + return np.array(out) + + # Ensure input is of float type, to avoid integer overflows etc. + if ((isinstance(A, np.ndarray) or issparse(A) or is_pydata_spmatrix(A)) + and not np.issubdtype(A.dtype, np.inexact)): + A = A.astype(float) + + # Detect upper triangularity. + structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None + + if use_exact_onenorm == "auto": + # Hardcode a matrix order threshold for exact vs. estimated one-norms. + use_exact_onenorm = A.shape[0] < 200 + + # Track functions of A to help compute the matrix exponential. + h = _ExpmPadeHelper( + A, structure=structure, use_exact_onenorm=use_exact_onenorm) + + # Try Pade order 3. + eta_1 = max(h.d4_loose, h.d6_loose) + if eta_1 < 1.495585217958292e-002 and _ell(h.A, 3) == 0: + U, V = h.pade3() + return _solve_P_Q(U, V, structure=structure) + + # Try Pade order 5. + eta_2 = max(h.d4_tight, h.d6_loose) + if eta_2 < 2.539398330063230e-001 and _ell(h.A, 5) == 0: + U, V = h.pade5() + return _solve_P_Q(U, V, structure=structure) + + # Try Pade orders 7 and 9. + eta_3 = max(h.d6_tight, h.d8_loose) + if eta_3 < 9.504178996162932e-001 and _ell(h.A, 7) == 0: + U, V = h.pade7() + return _solve_P_Q(U, V, structure=structure) + if eta_3 < 2.097847961257068e+000 and _ell(h.A, 9) == 0: + U, V = h.pade9() + return _solve_P_Q(U, V, structure=structure) + + # Use Pade order 13. + eta_4 = max(h.d8_loose, h.d10_loose) + eta_5 = min(eta_3, eta_4) + theta_13 = 4.25 + + # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13 + if eta_5 == 0: + # Nilpotent special case + s = 0 + else: + s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0) + s = s + _ell(2**-s * h.A, 13) + U, V = h.pade13_scaled(s) + X = _solve_P_Q(U, V, structure=structure) + if structure == UPPER_TRIANGULAR: + # Invoke Code Fragment 2.1. + X = _fragment_2_1(X, h.A, s) + else: + # X = r_13(A)^(2^s) by repeated squaring. + for i in range(s): + X = X.dot(X) + return X + + +def _solve_P_Q(U, V, structure=None): + """ + A helper function for expm_2009. + + Parameters + ---------- + U : ndarray + Pade numerator. + V : ndarray + Pade denominator. + structure : str, optional + A string describing the structure of both matrices `U` and `V`. + Only `upper_triangular` is currently supported. + + Notes + ----- + The `structure` argument is inspired by similar args + for theano and cvxopt functions. + + """ + P = U + V + Q = -U + V + if issparse(U) or is_pydata_spmatrix(U): + return spsolve(Q, P) + elif structure is None: + return solve(Q, P) + elif structure == UPPER_TRIANGULAR: + return solve_triangular(Q, P) + else: + raise ValueError('unsupported matrix structure: ' + str(structure)) + + +def _exp_sinch(a, x): + """ + Stably evaluate exp(a)*sinh(x)/x + + Notes + ----- + The strategy of falling back to a sixth order Taylor expansion + was suggested by the Spallation Neutron Source docs + which was found on the internet by google search. + http://www.ornl.gov/~t6p/resources/xal/javadoc/gov/sns/tools/math/ElementaryFunction.html + The details of the cutoff point and the Horner-like evaluation + was picked without reference to anything in particular. + + Note that sinch is not currently implemented in scipy.special, + whereas the "engineer's" definition of sinc is implemented. + The implementation of sinc involves a scaling factor of pi + that distinguishes it from the "mathematician's" version of sinc. + + """ + + # If x is small then use sixth order Taylor expansion. + # How small is small? I am using the point where the relative error + # of the approximation is less than 1e-14. + # If x is large then directly evaluate sinh(x) / x. + if abs(x) < 0.0135: + x2 = x*x + return np.exp(a) * (1 + (x2/6.)*(1 + (x2/20.)*(1 + (x2/42.)))) + else: + return (np.exp(a + x) - np.exp(a - x)) / (2*x) + + +def _eq_10_42(lam_1, lam_2, t_12): + """ + Equation (10.42) of Functions of Matrices: Theory and Computation. + + Notes + ----- + This is a helper function for _fragment_2_1 of expm_2009. + Equation (10.42) is on page 251 in the section on Schur algorithms. + In particular, section 10.4.3 explains the Schur-Parlett algorithm. + expm([[lam_1, t_12], [0, lam_1]) + = + [[exp(lam_1), t_12*exp((lam_1 + lam_2)/2)*sinch((lam_1 - lam_2)/2)], + [0, exp(lam_2)] + """ + + # The plain formula t_12 * (exp(lam_2) - exp(lam_2)) / (lam_2 - lam_1) + # apparently suffers from cancellation, according to Higham's textbook. + # A nice implementation of sinch, defined as sinh(x)/x, + # will apparently work around the cancellation. + a = 0.5 * (lam_1 + lam_2) + b = 0.5 * (lam_1 - lam_2) + return t_12 * _exp_sinch(a, b) + + +def _fragment_2_1(X, T, s): + """ + A helper function for expm_2009. + + Notes + ----- + The argument X is modified in-place, but this modification is not the same + as the returned value of the function. + This function also takes pains to do things in ways that are compatible + with sparse matrices, for example by avoiding fancy indexing + and by using methods of the matrices whenever possible instead of + using functions of the numpy or scipy libraries themselves. + + """ + # Form X = r_m(2^-s T) + # Replace diag(X) by exp(2^-s diag(T)). + n = X.shape[0] + diag_T = np.ravel(T.diagonal().copy()) + + # Replace diag(X) by exp(2^-s diag(T)). + scale = 2 ** -s + exp_diag = np.exp(scale * diag_T) + for k in range(n): + X[k, k] = exp_diag[k] + + for i in range(s-1, -1, -1): + X = X.dot(X) + + # Replace diag(X) by exp(2^-i diag(T)). + scale = 2 ** -i + exp_diag = np.exp(scale * diag_T) + for k in range(n): + X[k, k] = exp_diag[k] + + # Replace (first) superdiagonal of X by explicit formula + # for superdiagonal of exp(2^-i T) from Eq (10.42) of + # the author's 2008 textbook + # Functions of Matrices: Theory and Computation. + for k in range(n-1): + lam_1 = scale * diag_T[k] + lam_2 = scale * diag_T[k+1] + t_12 = scale * T[k, k+1] + value = _eq_10_42(lam_1, lam_2, t_12) + X[k, k+1] = value + + # Return the updated X matrix. + return X + + +def _ell(A, m): + """ + A helper function for expm_2009. + + Parameters + ---------- + A : linear operator + A linear operator whose norm of power we care about. + m : int + The power of the linear operator + + Returns + ------- + value : int + A value related to a bound. + + """ + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + + # The c_i are explained in (2.2) and (2.6) of the 2005 expm paper. + # They are coefficients of terms of a generating function series expansion. + c_i = {3: 100800., + 5: 10059033600., + 7: 4487938430976000., + 9: 5914384781877411840000., + 13: 113250775606021113483283660800000000. + } + abs_c_recip = c_i[m] + + # This is explained after Eq. (1.2) of the 2009 expm paper. + # It is the "unit roundoff" of IEEE double precision arithmetic. + u = 2**-53 + + # Compute the one-norm of matrix power p of abs(A). + A_abs_onenorm = _onenorm_matrix_power_nnm(abs(A), 2*m + 1) + + # Treat zero norm as a special case. + if not A_abs_onenorm: + return 0 + + alpha = A_abs_onenorm / (_onenorm(A) * abs_c_recip) + log2_alpha_div_u = np.log2(alpha/u) + value = int(np.ceil(log2_alpha_div_u / (2 * m))) + return max(value, 0) + +def matrix_power(A, power): + """ + Raise a square matrix to the integer power, `power`. + + For non-negative integers, ``A**power`` is computed using repeated + matrix multiplications. Negative integers are not supported. + + Parameters + ---------- + A : (M, M) square sparse array or matrix + sparse array that will be raised to power `power` + power : int + Exponent used to raise sparse array `A` + + Returns + ------- + A**power : (M, M) sparse array or matrix + The output matrix will be the same shape as A, and will preserve + the class of A, but the format of the output may be changed. + + Notes + ----- + This uses a recursive implementation of the matrix power. For computing + the matrix power using a reasonably large `power`, this may be less efficient + than computing the product directly, using A @ A @ ... @ A. + This is contingent upon the number of nonzero entries in the matrix. + + .. versionadded:: 1.12.0 + + Examples + -------- + >>> from scipy import sparse + >>> A = sparse.csc_array([[0,1,0],[1,0,1],[0,1,0]]) + >>> A.todense() + array([[0, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + >>> (A @ A).todense() + array([[1, 0, 1], + [0, 2, 0], + [1, 0, 1]]) + >>> A2 = sparse.linalg.matrix_power(A, 2) + >>> A2.todense() + array([[1, 0, 1], + [0, 2, 0], + [1, 0, 1]]) + >>> A4 = sparse.linalg.matrix_power(A, 4) + >>> A4.todense() + array([[2, 0, 2], + [0, 4, 0], + [2, 0, 2]]) + + """ + M, N = A.shape + if M != N: + raise TypeError('sparse matrix is not square') + + if isintlike(power): + power = int(power) + if power < 0: + raise ValueError('exponent must be >= 0') + + if power == 0: + return eye(M, dtype=A.dtype) + + if power == 1: + return A.copy() + + tmp = matrix_power(A, power // 2) + if power % 2: + return A @ tmp @ tmp + else: + return tmp @ tmp + else: + raise ValueError("exponent must be an integer") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_norm.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..38f3a6d7a6f84ec315b3177b384eef5c5d93311a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_norm.py @@ -0,0 +1,193 @@ +"""Sparse matrix norms. + +""" +import numpy as np +from scipy.sparse import issparse +from scipy.sparse.linalg import svds +import scipy.sparse as sp + +from numpy import sqrt, abs + +__all__ = ['norm'] + + +def _sparse_frobenius_norm(x): + data = sp._sputils._todata(x) + return np.linalg.norm(data) + + +def norm(x, ord=None, axis=None): + """ + Norm of a sparse matrix + + This function is able to return one of seven different matrix norms, + depending on the value of the ``ord`` parameter. + + Parameters + ---------- + x : a sparse matrix + Input sparse matrix. + ord : {non-zero int, inf, -inf, 'fro'}, optional + Order of the norm (see table under ``Notes``). inf means numpy's + `inf` object. + axis : {int, 2-tuple of ints, None}, optional + If `axis` is an integer, it specifies the axis of `x` along which to + compute the vector norms. If `axis` is a 2-tuple, it specifies the + axes that hold 2-D matrices, and the matrix norms of these matrices + are computed. If `axis` is None then either a vector norm (when `x` + is 1-D) or a matrix norm (when `x` is 2-D) is returned. + + Returns + ------- + n : float or ndarray + + Notes + ----- + Some of the ord are not implemented because some associated functions like, + _multi_svd_norm, are not yet available for sparse matrix. + + This docstring is modified based on numpy.linalg.norm. + https://github.com/numpy/numpy/blob/main/numpy/linalg/linalg.py + + The following norms can be calculated: + + ===== ============================ + ord norm for sparse matrices + ===== ============================ + None Frobenius norm + 'fro' Frobenius norm + inf max(sum(abs(x), axis=1)) + -inf min(sum(abs(x), axis=1)) + 0 abs(x).sum(axis=axis) + 1 max(sum(abs(x), axis=0)) + -1 min(sum(abs(x), axis=0)) + 2 Spectral norm (the largest singular value) + -2 Not implemented + other Not implemented + ===== ============================ + + The Frobenius norm is given by [1]_: + + :math:`||A||_F = [\\sum_{i,j} abs(a_{i,j})^2]^{1/2}` + + References + ---------- + .. [1] G. H. Golub and C. F. Van Loan, *Matrix Computations*, + Baltimore, MD, Johns Hopkins University Press, 1985, pg. 15 + + Examples + -------- + >>> from scipy.sparse import * + >>> import numpy as np + >>> from scipy.sparse.linalg import norm + >>> a = np.arange(9) - 4 + >>> a + array([-4, -3, -2, -1, 0, 1, 2, 3, 4]) + >>> b = a.reshape((3, 3)) + >>> b + array([[-4, -3, -2], + [-1, 0, 1], + [ 2, 3, 4]]) + + >>> b = csr_matrix(b) + >>> norm(b) + 7.745966692414834 + >>> norm(b, 'fro') + 7.745966692414834 + >>> norm(b, np.inf) + 9 + >>> norm(b, -np.inf) + 2 + >>> norm(b, 1) + 7 + >>> norm(b, -1) + 6 + + The matrix 2-norm or the spectral norm is the largest singular + value, computed approximately and with limitations. + + >>> b = diags([-1, 1], [0, 1], shape=(9, 10)) + >>> norm(b, 2) + 1.9753... + """ + if not issparse(x): + raise TypeError("input is not sparse. use numpy.linalg.norm") + + # Check the default case first and handle it immediately. + if axis is None and ord in (None, 'fro', 'f'): + return _sparse_frobenius_norm(x) + + # Some norms require functions that are not implemented for all types. + x = x.tocsr() + + if axis is None: + axis = (0, 1) + elif not isinstance(axis, tuple): + msg = "'axis' must be None, an integer or a tuple of integers" + try: + int_axis = int(axis) + except TypeError as e: + raise TypeError(msg) from e + if axis != int_axis: + raise TypeError(msg) + axis = (int_axis,) + + nd = 2 + if len(axis) == 2: + row_axis, col_axis = axis + if not (-nd <= row_axis < nd and -nd <= col_axis < nd): + message = f'Invalid axis {axis!r} for an array with shape {x.shape!r}' + raise ValueError(message) + if row_axis % nd == col_axis % nd: + raise ValueError('Duplicate axes given.') + if ord == 2: + # Only solver="lobpcg" supports all numpy dtypes + _, s, _ = svds(x, k=1, solver="lobpcg") + return s[0] + elif ord == -2: + raise NotImplementedError + #return _multi_svd_norm(x, row_axis, col_axis, amin) + elif ord == 1: + return abs(x).sum(axis=row_axis).max(axis=col_axis)[0,0] + elif ord == np.inf: + return abs(x).sum(axis=col_axis).max(axis=row_axis)[0,0] + elif ord == -1: + return abs(x).sum(axis=row_axis).min(axis=col_axis)[0,0] + elif ord == -np.inf: + return abs(x).sum(axis=col_axis).min(axis=row_axis)[0,0] + elif ord in (None, 'f', 'fro'): + # The axis order does not matter for this norm. + return _sparse_frobenius_norm(x) + else: + raise ValueError("Invalid norm order for matrices.") + elif len(axis) == 1: + a, = axis + if not (-nd <= a < nd): + message = f'Invalid axis {axis!r} for an array with shape {x.shape!r}' + raise ValueError(message) + if ord == np.inf: + M = abs(x).max(axis=a) + elif ord == -np.inf: + M = abs(x).min(axis=a) + elif ord == 0: + # Zero norm + M = (x != 0).sum(axis=a) + elif ord == 1: + # special case for speedup + M = abs(x).sum(axis=a) + elif ord in (2, None): + M = sqrt(abs(x).power(2).sum(axis=a)) + else: + try: + ord + 1 + except TypeError as e: + raise ValueError('Invalid norm order for vectors.') from e + M = np.power(abs(x).power(ord).sum(axis=a), 1 / ord) + if hasattr(M, 'toarray'): + return M.toarray().ravel() + elif hasattr(M, 'A'): + return M.A.ravel() + else: + return M.ravel() + else: + raise ValueError("Improper number of dimensions to norm.") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_onenormest.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_onenormest.py new file mode 100644 index 0000000000000000000000000000000000000000..c3e383aa6370b3ef11b8c2cf57d2cf85da66d02d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_onenormest.py @@ -0,0 +1,467 @@ +"""Sparse block 1-norm estimator. +""" + +import numpy as np +from scipy.sparse.linalg import aslinearoperator + + +__all__ = ['onenormest'] + + +def onenormest(A, t=2, itmax=5, compute_v=False, compute_w=False): + """ + Compute a lower bound of the 1-norm of a sparse matrix. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can be transposed and that can + produce matrix products. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + Notes + ----- + This is algorithm 2.4 of [1]. + + In [2] it is described as follows. + "This algorithm typically requires the evaluation of + about 4t matrix-vector products and almost invariably + produces a norm estimate (which is, in fact, a lower + bound on the norm) correct to within a factor 3." + + .. versionadded:: 0.13.0 + + References + ---------- + .. [1] Nicholas J. Higham and Francoise Tisseur (2000), + "A Block Algorithm for Matrix 1-Norm Estimation, + with an Application to 1-Norm Pseudospectra." + SIAM J. Matrix Anal. Appl. Vol. 21, No. 4, pp. 1185-1201. + + .. [2] Awad H. Al-Mohy and Nicholas J. Higham (2009), + "A new scaling and squaring algorithm for the matrix exponential." + SIAM J. Matrix Anal. Appl. Vol. 31, No. 3, pp. 970-989. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import onenormest + >>> A = csc_matrix([[1., 0., 0.], [5., 8., 2.], [0., -1., 0.]], dtype=float) + >>> A.toarray() + array([[ 1., 0., 0.], + [ 5., 8., 2.], + [ 0., -1., 0.]]) + >>> onenormest(A) + 9.0 + >>> np.linalg.norm(A.toarray(), ord=1) + 9.0 + """ + + # Check the input. + A = aslinearoperator(A) + if A.shape[0] != A.shape[1]: + raise ValueError('expected the operator to act like a square matrix') + + # If the operator size is small compared to t, + # then it is easier to compute the exact norm. + # Otherwise estimate the norm. + n = A.shape[1] + if t >= n: + A_explicit = np.asarray(aslinearoperator(A).matmat(np.identity(n))) + if A_explicit.shape != (n, n): + raise Exception('internal error: ', + 'unexpected shape ' + str(A_explicit.shape)) + col_abs_sums = abs(A_explicit).sum(axis=0) + if col_abs_sums.shape != (n, ): + raise Exception('internal error: ', + 'unexpected shape ' + str(col_abs_sums.shape)) + argmax_j = np.argmax(col_abs_sums) + v = elementary_vector(n, argmax_j) + w = A_explicit[:, argmax_j] + est = col_abs_sums[argmax_j] + else: + est, v, w, nmults, nresamples = _onenormest_core(A, A.H, t, itmax) + + # Report the norm estimate along with some certificates of the estimate. + if compute_v or compute_w: + result = (est,) + if compute_v: + result += (v,) + if compute_w: + result += (w,) + return result + else: + return est + + +def _blocked_elementwise(func): + """ + Decorator for an elementwise function, to apply it blockwise along + first dimension, to avoid excessive memory usage in temporaries. + """ + block_size = 2**20 + + def wrapper(x): + if x.shape[0] < block_size: + return func(x) + else: + y0 = func(x[:block_size]) + y = np.zeros((x.shape[0],) + y0.shape[1:], dtype=y0.dtype) + y[:block_size] = y0 + del y0 + for j in range(block_size, x.shape[0], block_size): + y[j:j+block_size] = func(x[j:j+block_size]) + return y + return wrapper + + +@_blocked_elementwise +def sign_round_up(X): + """ + This should do the right thing for both real and complex matrices. + + From Higham and Tisseur: + "Everything in this section remains valid for complex matrices + provided that sign(A) is redefined as the matrix (aij / |aij|) + (and sign(0) = 1) transposes are replaced by conjugate transposes." + + """ + Y = X.copy() + Y[Y == 0] = 1 + Y /= np.abs(Y) + return Y + + +@_blocked_elementwise +def _max_abs_axis1(X): + return np.max(np.abs(X), axis=1) + + +def _sum_abs_axis0(X): + block_size = 2**20 + r = None + for j in range(0, X.shape[0], block_size): + y = np.sum(np.abs(X[j:j+block_size]), axis=0) + if r is None: + r = y + else: + r += y + return r + + +def elementary_vector(n, i): + v = np.zeros(n, dtype=float) + v[i] = 1 + return v + + +def vectors_are_parallel(v, w): + # Columns are considered parallel when they are equal or negative. + # Entries are required to be in {-1, 1}, + # which guarantees that the magnitudes of the vectors are identical. + if v.ndim != 1 or v.shape != w.shape: + raise ValueError('expected conformant vectors with entries in {-1,1}') + n = v.shape[0] + return np.dot(v, w) == n + + +def every_col_of_X_is_parallel_to_a_col_of_Y(X, Y): + for v in X.T: + if not any(vectors_are_parallel(v, w) for w in Y.T): + return False + return True + + +def column_needs_resampling(i, X, Y=None): + # column i of X needs resampling if either + # it is parallel to a previous column of X or + # it is parallel to a column of Y + n, t = X.shape + v = X[:, i] + if any(vectors_are_parallel(v, X[:, j]) for j in range(i)): + return True + if Y is not None: + if any(vectors_are_parallel(v, w) for w in Y.T): + return True + return False + + +def resample_column(i, X): + X[:, i] = np.random.randint(0, 2, size=X.shape[0])*2 - 1 + + +def less_than_or_close(a, b): + return np.allclose(a, b) or (a < b) + + +def _algorithm_2_2(A, AT, t): + """ + This is Algorithm 2.2. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can produce matrix products. + AT : ndarray or other linear operator + The transpose of A. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + + Returns + ------- + g : sequence + A non-negative decreasing vector + such that g[j] is a lower bound for the 1-norm + of the column of A of jth largest 1-norm. + The first entry of this vector is therefore a lower bound + on the 1-norm of the linear operator A. + This sequence has length t. + ind : sequence + The ith entry of ind is the index of the column A whose 1-norm + is given by g[i]. + This sequence of indices has length t, and its entries are + chosen from range(n), possibly with repetition, + where n is the order of the operator A. + + Notes + ----- + This algorithm is mainly for testing. + It uses the 'ind' array in a way that is similar to + its usage in algorithm 2.4. This algorithm 2.2 may be easier to test, + so it gives a chance of uncovering bugs related to indexing + which could have propagated less noticeably to algorithm 2.4. + + """ + A_linear_operator = aslinearoperator(A) + AT_linear_operator = aslinearoperator(AT) + n = A_linear_operator.shape[0] + + # Initialize the X block with columns of unit 1-norm. + X = np.ones((n, t)) + if t > 1: + X[:, 1:] = np.random.randint(0, 2, size=(n, t-1))*2 - 1 + X /= float(n) + + # Iteratively improve the lower bounds. + # Track extra things, to assert invariants for debugging. + g_prev = None + h_prev = None + k = 1 + ind = range(t) + while True: + Y = np.asarray(A_linear_operator.matmat(X)) + g = _sum_abs_axis0(Y) + best_j = np.argmax(g) + g.sort() + g = g[::-1] + S = sign_round_up(Y) + Z = np.asarray(AT_linear_operator.matmat(S)) + h = _max_abs_axis1(Z) + + # If this algorithm runs for fewer than two iterations, + # then its return values do not have the properties indicated + # in the description of the algorithm. + # In particular, the entries of g are not 1-norms of any + # column of A until the second iteration. + # Therefore we will require the algorithm to run for at least + # two iterations, even though this requirement is not stated + # in the description of the algorithm. + if k >= 2: + if less_than_or_close(max(h), np.dot(Z[:, best_j], X[:, best_j])): + break + ind = np.argsort(h)[::-1][:t] + h = h[ind] + for j in range(t): + X[:, j] = elementary_vector(n, ind[j]) + + # Check invariant (2.2). + if k >= 2: + if not less_than_or_close(g_prev[0], h_prev[0]): + raise Exception('invariant (2.2) is violated') + if not less_than_or_close(h_prev[0], g[0]): + raise Exception('invariant (2.2) is violated') + + # Check invariant (2.3). + if k >= 3: + for j in range(t): + if not less_than_or_close(g[j], g_prev[j]): + raise Exception('invariant (2.3) is violated') + + # Update for the next iteration. + g_prev = g + h_prev = h + k += 1 + + # Return the lower bounds and the corresponding column indices. + return g, ind + + +def _onenormest_core(A, AT, t, itmax): + """ + Compute a lower bound of the 1-norm of a sparse matrix. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can produce matrix products. + AT : ndarray or other linear operator + The transpose of A. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + itmax : int, optional + Use at most this many iterations. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + nmults : int, optional + The number of matrix products that were computed. + nresamples : int, optional + The number of times a parallel column was observed, + necessitating a re-randomization of the column. + + Notes + ----- + This is algorithm 2.4. + + """ + # This function is a more or less direct translation + # of Algorithm 2.4 from the Higham and Tisseur (2000) paper. + A_linear_operator = aslinearoperator(A) + AT_linear_operator = aslinearoperator(AT) + if itmax < 2: + raise ValueError('at least two iterations are required') + if t < 1: + raise ValueError('at least one column is required') + n = A.shape[0] + if t >= n: + raise ValueError('t should be smaller than the order of A') + # Track the number of big*small matrix multiplications + # and the number of resamplings. + nmults = 0 + nresamples = 0 + # "We now explain our choice of starting matrix. We take the first + # column of X to be the vector of 1s [...] This has the advantage that + # for a matrix with nonnegative elements the algorithm converges + # with an exact estimate on the second iteration, and such matrices + # arise in applications [...]" + X = np.ones((n, t), dtype=float) + # "The remaining columns are chosen as rand{-1,1}, + # with a check for and correction of parallel columns, + # exactly as for S in the body of the algorithm." + if t > 1: + for i in range(1, t): + # These are technically initial samples, not resamples, + # so the resampling count is not incremented. + resample_column(i, X) + for i in range(t): + while column_needs_resampling(i, X): + resample_column(i, X) + nresamples += 1 + # "Choose starting matrix X with columns of unit 1-norm." + X /= float(n) + # "indices of used unit vectors e_j" + ind_hist = np.zeros(0, dtype=np.intp) + est_old = 0 + S = np.zeros((n, t), dtype=float) + k = 1 + ind = None + while True: + Y = np.asarray(A_linear_operator.matmat(X)) + nmults += 1 + mags = _sum_abs_axis0(Y) + est = np.max(mags) + best_j = np.argmax(mags) + if est > est_old or k == 2: + if k >= 2: + ind_best = ind[best_j] + w = Y[:, best_j] + # (1) + if k >= 2 and est <= est_old: + est = est_old + break + est_old = est + S_old = S + if k > itmax: + break + S = sign_round_up(Y) + del Y + # (2) + if every_col_of_X_is_parallel_to_a_col_of_Y(S, S_old): + break + if t > 1: + # "Ensure that no column of S is parallel to another column of S + # or to a column of S_old by replacing columns of S by rand{-1,1}." + for i in range(t): + while column_needs_resampling(i, S, S_old): + resample_column(i, S) + nresamples += 1 + del S_old + # (3) + Z = np.asarray(AT_linear_operator.matmat(S)) + nmults += 1 + h = _max_abs_axis1(Z) + del Z + # (4) + if k >= 2 and max(h) == h[ind_best]: + break + # "Sort h so that h_first >= ... >= h_last + # and re-order ind correspondingly." + # + # Later on, we will need at most t+len(ind_hist) largest + # entries, so drop the rest + ind = np.argsort(h)[::-1][:t+len(ind_hist)].copy() + del h + if t > 1: + # (5) + # Break if the most promising t vectors have been visited already. + if np.isin(ind[:t], ind_hist).all(): + break + # Put the most promising unvisited vectors at the front of the list + # and put the visited vectors at the end of the list. + # Preserve the order of the indices induced by the ordering of h. + seen = np.isin(ind, ind_hist) + ind = np.concatenate((ind[~seen], ind[seen])) + for j in range(t): + X[:, j] = elementary_vector(n, ind[j]) + + new_ind = ind[:t][~np.isin(ind[:t], ind_hist)] + ind_hist = np.concatenate((ind_hist, new_ind)) + k += 1 + v = elementary_vector(n, ind_best) + return est, v, w, nmults, nresamples diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py new file mode 100644 index 0000000000000000000000000000000000000000..e80ae3c288114e124d34efd7c7f62c43e9d02bea --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py @@ -0,0 +1,948 @@ +import numpy as np +from scipy.sparse.linalg import LinearOperator +from scipy.sparse import kron, eye, dia_array + +__all__ = ['LaplacianNd'] +# Sakurai and Mikota classes are intended for tests and benchmarks +# and explicitly not included in the public API of this module. + + +class LaplacianNd(LinearOperator): + """ + The grid Laplacian in ``N`` dimensions and its eigenvalues/eigenvectors. + + Construct Laplacian on a uniform rectangular grid in `N` dimensions + and output its eigenvalues and eigenvectors. + The Laplacian ``L`` is square, negative definite, real symmetric array + with signed integer entries and zeros otherwise. + + Parameters + ---------- + grid_shape : tuple + A tuple of integers of length ``N`` (corresponding to the dimension of + the Lapacian), where each entry gives the size of that dimension. The + Laplacian matrix is square of the size ``np.prod(grid_shape)``. + boundary_conditions : {'neumann', 'dirichlet', 'periodic'}, optional + The type of the boundary conditions on the boundaries of the grid. + Valid values are ``'dirichlet'`` or ``'neumann'``(default) or + ``'periodic'``. + dtype : dtype + Numerical type of the array. Default is ``np.int8``. + + Methods + ------- + toarray() + Construct a dense array from Laplacian data + tosparse() + Construct a sparse array from Laplacian data + eigenvalues(m=None) + Construct a 1D array of `m` largest (smallest in absolute value) + eigenvalues of the Laplacian matrix in ascending order. + eigenvectors(m=None): + Construct the array with columns made of `m` eigenvectors (``float``) + of the ``Nd`` Laplacian corresponding to the `m` ordered eigenvalues. + + .. versionadded:: 1.12.0 + + Notes + ----- + Compared to the MATLAB/Octave implementation [1] of 1-, 2-, and 3-D + Laplacian, this code allows the arbitrary N-D case and the matrix-free + callable option, but is currently limited to pure Dirichlet, Neumann or + Periodic boundary conditions only. + + The Laplacian matrix of a graph (`scipy.sparse.csgraph.laplacian`) of a + rectangular grid corresponds to the negative Laplacian with the Neumann + conditions, i.e., ``boundary_conditions = 'neumann'``. + + All eigenvalues and eigenvectors of the discrete Laplacian operator for + an ``N``-dimensional regular grid of shape `grid_shape` with the grid + step size ``h=1`` are analytically known [2]. + + References + ---------- + .. [1] https://github.com/lobpcg/blopex/blob/master/blopex_\ +tools/matlab/laplacian/laplacian.m + .. [2] "Eigenvalues and eigenvectors of the second derivative", Wikipedia + https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors_\ +of_the_second_derivative + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import LaplacianNd + >>> from scipy.sparse import diags, csgraph + >>> from scipy.linalg import eigvalsh + + The one-dimensional Laplacian demonstrated below for pure Neumann boundary + conditions on a regular grid with ``n=6`` grid points is exactly the + negative graph Laplacian for the undirected linear graph with ``n`` + vertices using the sparse adjacency matrix ``G`` represented by the + famous tri-diagonal matrix: + + >>> n = 6 + >>> G = diags(np.ones(n - 1), 1, format='csr') + >>> Lf = csgraph.laplacian(G, symmetrized=True, form='function') + >>> grid_shape = (n, ) + >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann') + >>> np.array_equal(lap.matmat(np.eye(n)), -Lf(np.eye(n))) + True + + Since all matrix entries of the Laplacian are integers, ``'int8'`` is + the default dtype for storing matrix representations. + + >>> lap.tosparse() + <6x6 sparse array of type '' + with 16 stored elements (3 diagonals) in DIAgonal format> + >>> lap.toarray() + array([[-1, 1, 0, 0, 0, 0], + [ 1, -2, 1, 0, 0, 0], + [ 0, 1, -2, 1, 0, 0], + [ 0, 0, 1, -2, 1, 0], + [ 0, 0, 0, 1, -2, 1], + [ 0, 0, 0, 0, 1, -1]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + + Any number of extreme eigenvalues and/or eigenvectors can be computed. + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic') + >>> lap.eigenvalues() + array([-4., -3., -3., -1., -1., 0.]) + >>> lap.eigenvalues()[-2:] + array([-1., 0.]) + >>> lap.eigenvalues(2) + array([-1., 0.]) + >>> lap.eigenvectors(1) + array([[0.40824829], + [0.40824829], + [0.40824829], + [0.40824829], + [0.40824829], + [0.40824829]]) + >>> lap.eigenvectors(2) + array([[ 0.5 , 0.40824829], + [ 0. , 0.40824829], + [-0.5 , 0.40824829], + [-0.5 , 0.40824829], + [ 0. , 0.40824829], + [ 0.5 , 0.40824829]]) + >>> lap.eigenvectors() + array([[ 0.40824829, 0.28867513, 0.28867513, 0.5 , 0.5 , + 0.40824829], + [-0.40824829, -0.57735027, -0.57735027, 0. , 0. , + 0.40824829], + [ 0.40824829, 0.28867513, 0.28867513, -0.5 , -0.5 , + 0.40824829], + [-0.40824829, 0.28867513, 0.28867513, -0.5 , -0.5 , + 0.40824829], + [ 0.40824829, -0.57735027, -0.57735027, 0. , 0. , + 0.40824829], + [-0.40824829, 0.28867513, 0.28867513, 0.5 , 0.5 , + 0.40824829]]) + + The two-dimensional Laplacian is illustrated on a regular grid with + ``grid_shape = (2, 3)`` points in each dimension. + + >>> grid_shape = (2, 3) + >>> n = np.prod(grid_shape) + + Numeration of grid points is as follows: + + >>> np.arange(n).reshape(grid_shape + (-1,)) + array([[[0], + [1], + [2]], + + [[3], + [4], + [5]]]) + + Each of the boundary conditions ``'dirichlet'``, ``'periodic'``, and + ``'neumann'`` is illustrated separately; with ``'dirichlet'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='dirichlet') + >>> lap.tosparse() + <6x6 sparse array of type '' + with 20 stored elements in Compressed Sparse Row format> + >>> lap.toarray() + array([[-4, 1, 0, 1, 0, 0], + [ 1, -4, 1, 0, 1, 0], + [ 0, 1, -4, 0, 0, 1], + [ 1, 0, 0, -4, 1, 0], + [ 0, 1, 0, 1, -4, 1], + [ 0, 0, 1, 0, 1, -4]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-6.41421356, -5. , -4.41421356, -3.58578644, -3. , + -1.58578644]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + with ``'periodic'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic') + >>> lap.tosparse() + <6x6 sparse array of type '' + with 24 stored elements in Compressed Sparse Row format> + >>> lap.toarray() + array([[-4, 1, 1, 2, 0, 0], + [ 1, -4, 1, 0, 2, 0], + [ 1, 1, -4, 0, 0, 2], + [ 2, 0, 0, -4, 1, 1], + [ 0, 2, 0, 1, -4, 1], + [ 0, 0, 2, 1, 1, -4]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-7., -7., -4., -3., -3., 0.]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + and with ``'neumann'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann') + >>> lap.tosparse() + <6x6 sparse array of type '' + with 20 stored elements in Compressed Sparse Row format> + >>> lap.toarray() + array([[-2, 1, 0, 1, 0, 0], + [ 1, -3, 1, 0, 1, 0], + [ 0, 1, -2, 0, 0, 1], + [ 1, 0, 0, -2, 1, 0], + [ 0, 1, 0, 1, -3, 1], + [ 0, 0, 1, 0, 1, -2]]) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-5., -3., -3., -2., -1., 0.]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + """ + + def __init__(self, grid_shape, *, + boundary_conditions='neumann', + dtype=np.int8): + + if boundary_conditions not in ('dirichlet', 'neumann', 'periodic'): + raise ValueError( + f"Unknown value {boundary_conditions!r} is given for " + "'boundary_conditions' parameter. The valid options are " + "'dirichlet', 'periodic', and 'neumann' (default)." + ) + + self.grid_shape = grid_shape + self.boundary_conditions = boundary_conditions + # LaplacianNd folds all dimensions in `grid_shape` into a single one + N = np.prod(grid_shape) + super().__init__(dtype=dtype, shape=(N, N)) + + def _eigenvalue_ordering(self, m): + """Compute `m` largest eigenvalues in each of the ``N`` directions, + i.e., up to ``m * N`` total, order them and return `m` largest. + """ + grid_shape = self.grid_shape + if m is None: + indices = np.indices(grid_shape) + Leig = np.zeros(grid_shape) + else: + grid_shape_min = min(grid_shape, + tuple(np.ones_like(grid_shape) * m)) + indices = np.indices(grid_shape_min) + Leig = np.zeros(grid_shape_min) + + for j, n in zip(indices, grid_shape): + if self.boundary_conditions == 'dirichlet': + Leig += -4 * np.sin(np.pi * (j + 1) / (2 * (n + 1))) ** 2 + elif self.boundary_conditions == 'neumann': + Leig += -4 * np.sin(np.pi * j / (2 * n)) ** 2 + else: # boundary_conditions == 'periodic' + Leig += -4 * np.sin(np.pi * np.floor((j + 1) / 2) / n) ** 2 + + Leig_ravel = Leig.ravel() + ind = np.argsort(Leig_ravel) + eigenvalues = Leig_ravel[ind] + if m is not None: + eigenvalues = eigenvalues[-m:] + ind = ind[-m:] + + return eigenvalues, ind + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : float array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + eigenvalues, _ = self._eigenvalue_ordering(m) + return eigenvalues + + def _ev1d(self, j, n): + """Return 1 eigenvector in 1d with index `j` + and number of grid points `n` where ``j < n``. + """ + if self.boundary_conditions == 'dirichlet': + i = np.pi * (np.arange(n) + 1) / (n + 1) + ev = np.sqrt(2. / (n + 1.)) * np.sin(i * (j + 1)) + elif self.boundary_conditions == 'neumann': + i = np.pi * (np.arange(n) + 0.5) / n + ev = np.sqrt((1. if j == 0 else 2.) / n) * np.cos(i * j) + else: # boundary_conditions == 'periodic' + if j == 0: + ev = np.sqrt(1. / n) * np.ones(n) + elif j + 1 == n and n % 2 == 0: + ev = np.sqrt(1. / n) * np.tile([1, -1], n//2) + else: + i = 2. * np.pi * (np.arange(n) + 0.5) / n + ev = np.sqrt(2. / n) * np.cos(i * np.floor((j + 1) / 2)) + # make small values exact zeros correcting round-off errors + # due to symmetry of eigenvectors the exact 0. is correct + ev[np.abs(ev) < np.finfo(np.float64).eps] = 0. + return ev + + def _one_eve(self, k): + """Return 1 eigenvector in Nd with multi-index `j` + as a tensor product of the corresponding 1d eigenvectors. + """ + phi = [self._ev1d(j, n) for j, n in zip(k, self.grid_shape)] + result = phi[0] + for phi in phi[1:]: + result = np.tensordot(result, phi, axes=0) + return np.asarray(result).ravel() + + def eigenvectors(self, m=None): + """Return the requested number of eigenvectors for ordered eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of eigenvectors to return. If not provided, + then all eigenvectors will be returned. + + Returns + ------- + eigenvectors : float array + An array with columns made of the requested `m` or all eigenvectors. + The columns are ordered according to the `m` ordered eigenvalues. + """ + _, ind = self._eigenvalue_ordering(m) + if m is None: + grid_shape_min = self.grid_shape + else: + grid_shape_min = min(self.grid_shape, + tuple(np.ones_like(self.grid_shape) * m)) + + N_indices = np.unravel_index(ind, grid_shape_min) + N_indices = [tuple(x) for x in zip(*N_indices)] + eigenvectors_list = [self._one_eve(k) for k in N_indices] + return np.column_stack(eigenvectors_list) + + def toarray(self): + """ + Converts the Laplacian data to a dense array. + + Returns + ------- + L : ndarray + The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``. + + """ + grid_shape = self.grid_shape + n = np.prod(grid_shape) + L = np.zeros([n, n], dtype=np.int8) + # Scratch arrays + L_i = np.empty_like(L) + Ltemp = np.empty_like(L) + + for ind, dim in enumerate(grid_shape): + # Start zeroing out L_i + L_i[:] = 0 + # Allocate the top left corner with the kernel of L_i + # Einsum returns writable view of arrays + np.einsum("ii->i", L_i[:dim, :dim])[:] = -2 + np.einsum("ii->i", L_i[: dim - 1, 1:dim])[:] = 1 + np.einsum("ii->i", L_i[1:dim, : dim - 1])[:] = 1 + + if self.boundary_conditions == 'neumann': + L_i[0, 0] = -1 + L_i[dim - 1, dim - 1] = -1 + elif self.boundary_conditions == 'periodic': + if dim > 1: + L_i[0, dim - 1] += 1 + L_i[dim - 1, 0] += 1 + else: + L_i[0, 0] += 1 + + # kron is too slow for large matrices hence the next two tricks + # 1- kron(eye, mat) is block_diag(mat, mat, ...) + # 2- kron(mat, eye) can be performed by 4d stride trick + + # 1- + new_dim = dim + # for block_diag we tile the top left portion on the diagonal + if ind > 0: + tiles = np.prod(grid_shape[:ind]) + for j in range(1, tiles): + L_i[j*dim:(j+1)*dim, j*dim:(j+1)*dim] = L_i[:dim, :dim] + new_dim += dim + # 2- + # we need the keep L_i, but reset the array + Ltemp[:new_dim, :new_dim] = L_i[:new_dim, :new_dim] + tiles = int(np.prod(grid_shape[ind+1:])) + # Zero out the top left, the rest is already 0 + L_i[:new_dim, :new_dim] = 0 + idx = [x for x in range(tiles)] + L_i.reshape( + (new_dim, tiles, + new_dim, tiles) + )[:, idx, :, idx] = Ltemp[:new_dim, :new_dim] + + L += L_i + + return L.astype(self.dtype) + + def tosparse(self): + """ + Constructs a sparse array from the Laplacian data. The returned sparse + array format is dependent on the selected boundary conditions. + + Returns + ------- + L : scipy.sparse.sparray + The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``. + + """ + N = len(self.grid_shape) + p = np.prod(self.grid_shape) + L = dia_array((p, p), dtype=np.int8) + + for i in range(N): + dim = self.grid_shape[i] + data = np.ones([3, dim], dtype=np.int8) + data[1, :] *= -2 + + if self.boundary_conditions == 'neumann': + data[1, 0] = -1 + data[1, -1] = -1 + + L_i = dia_array((data, [-1, 0, 1]), shape=(dim, dim), + dtype=np.int8 + ) + + if self.boundary_conditions == 'periodic': + t = dia_array((dim, dim), dtype=np.int8) + t.setdiag([1], k=-dim+1) + t.setdiag([1], k=dim-1) + L_i += t + + for j in range(i): + L_i = kron(eye(self.grid_shape[j], dtype=np.int8), L_i) + for j in range(i + 1, N): + L_i = kron(L_i, eye(self.grid_shape[j], dtype=np.int8)) + L += L_i + return L.astype(self.dtype) + + def _matvec(self, x): + grid_shape = self.grid_shape + N = len(grid_shape) + X = x.reshape(grid_shape + (-1,)) + Y = -2 * N * X + for i in range(N): + Y += np.roll(X, 1, axis=i) + Y += np.roll(X, -1, axis=i) + if self.boundary_conditions in ('neumann', 'dirichlet'): + Y[(slice(None),)*i + (0,) + (slice(None),)*(N-i-1) + ] -= np.roll(X, 1, axis=i)[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] + Y[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] -= np.roll(X, -1, axis=i)[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] + + if self.boundary_conditions == 'neumann': + Y[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] += np.roll(X, 0, axis=i)[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] + Y[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] += np.roll(X, 0, axis=i)[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] + + return Y.reshape(-1, X.shape[-1]) + + def _matmat(self, x): + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class Sakurai(LinearOperator): + """ + Construct a Sakurai matrix in various formats and its eigenvalues. + + Constructs the "Sakurai" matrix motivated by reference [1]_: + square real symmetric positive definite and 5-diagonal + with the main digonal ``[5, 6, 6, ..., 6, 6, 5], the ``+1`` and ``-1`` + diagonals filled with ``-4``, and the ``+2`` and ``-2`` diagonals + made of ``1``. Its eigenvalues are analytically known to be + ``16. * np.power(np.cos(0.5 * k * np.pi / (n + 1)), 4)``. + The matrix gets ill-conditioned with its size growing. + It is useful for testing and benchmarking sparse eigenvalue solvers + especially those taking advantage of its banded 5-diagonal structure. + See the notes below for details. + + Parameters + ---------- + n : int + The size of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.int8``. + + Methods + ------- + toarray() + Construct a dense array from Laplacian data + tosparse() + Construct a sparse array from Laplacian data + tobanded() + The Sakurai matrix in the format for banded symmetric matrices, + i.e., (3, n) ndarray with 3 upper diagonals + placing the main diagonal at the bottom. + eigenvalues + All eigenvalues of the Sakurai matrix ordered ascending. + + Notes + ----- + Reference [1]_ introduces a generalized eigenproblem for the matrix pair + `A` and `B` where `A` is the identity so we turn it into an eigenproblem + just for the matrix `B` that this function outputs in various formats + together with its eigenvalues. + + .. versionadded:: 1.12.0 + + References + ---------- + .. [1] T. Sakurai, H. Tadano, Y. Inadomi, and U. Nagashima, + "A moment-based method for large-scale generalized + eigenvalue problems", + Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004). + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg._special_sparse_arrays import Sakurai + >>> from scipy.linalg import eig_banded + >>> n = 6 + >>> sak = Sakurai(n) + + Since all matrix entries are small integers, ``'int8'`` is + the default dtype for storing matrix representations. + + >>> sak.toarray() + array([[ 5, -4, 1, 0, 0, 0], + [-4, 6, -4, 1, 0, 0], + [ 1, -4, 6, -4, 1, 0], + [ 0, 1, -4, 6, -4, 1], + [ 0, 0, 1, -4, 6, -4], + [ 0, 0, 0, 1, -4, 5]], dtype=int8) + >>> sak.tobanded() + array([[ 1, 1, 1, 1, 1, 1], + [-4, -4, -4, -4, -4, -4], + [ 5, 6, 6, 6, 6, 5]], dtype=int8) + >>> sak.tosparse() + <6x6 sparse matrix of type '' + with 24 stored elements (5 diagonals) in DIAgonal format> + >>> np.array_equal(sak.dot(np.eye(n)), sak.tosparse().toarray()) + True + >>> sak.eigenvalues() + array([0.03922866, 0.56703972, 2.41789479, 5.97822974, + 10.54287655, 14.45473055]) + >>> sak.eigenvalues(2) + array([0.03922866, 0.56703972]) + + The banded form can be used in scipy functions for banded matrices, e.g., + + >>> e = eig_banded(sak.tobanded(), eigvals_only=True) + >>> np.allclose(sak.eigenvalues, e, atol= n * n * n * np.finfo(float).eps) + True + + """ + def __init__(self, n, dtype=np.int8): + self.n = n + self.dtype = dtype + shape = (n, n) + super().__init__(dtype, shape) + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : `np.float64` array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + if m is None: + m = self.n + k = np.arange(self.n + 1 -m, self.n + 1) + return np.flip(16. * np.power(np.cos(0.5 * k * np.pi / (self.n + 1)), 4)) + + def tobanded(self): + """ + Construct the Sakurai matrix as a banded array. + """ + d0 = np.r_[5, 6 * np.ones(self.n - 2, dtype=self.dtype), 5] + d1 = -4 * np.ones(self.n, dtype=self.dtype) + d2 = np.ones(self.n, dtype=self.dtype) + return np.array([d2, d1, d0]).astype(self.dtype) + + def tosparse(self): + """ + Construct the Sakurai matrix is a sparse format. + """ + from scipy.sparse import spdiags + d = self.tobanded() + # the banded format has the main diagonal at the bottom + # `spdiags` has no `dtype` parameter so inherits dtype from banded + return spdiags([d[0], d[1], d[2], d[1], d[0]], [-2, -1, 0, 1, 2], + self.n, self.n) + + def toarray(self): + return self.tosparse().toarray() + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Sakurai matrix without constructing or storing the matrix itself + using the knowledge of its entries and the 5-diagonal format. + """ + x = x.reshape(self.n, -1) + result_dtype = np.promote_types(x.dtype, self.dtype) + sx = np.zeros_like(x, dtype=result_dtype) + sx[0, :] = 5 * x[0, :] - 4 * x[1, :] + x[2, :] + sx[-1, :] = 5 * x[-1, :] - 4 * x[-2, :] + x[-3, :] + sx[1: -1, :] = (6 * x[1: -1, :] - 4 * (x[:-2, :] + x[2:, :]) + + np.pad(x[:-3, :], ((1, 0), (0, 0))) + + np.pad(x[3:, :], ((0, 1), (0, 0)))) + return sx + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Sakurai matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaM(LinearOperator): + """ + Construct a mass matrix in various formats of Mikota pair. + + The mass matrix `M` is square real diagonal + positive definite with entries that are reciprocal to integers. + + Parameters + ---------- + shape : tuple of int + The shape of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.float64``. + + Methods + ------- + toarray() + Construct a dense array from Mikota data + tosparse() + Construct a sparse array from Mikota data + tobanded() + The format for banded symmetric matrices, + i.e., (1, n) ndarray with the main diagonal. + """ + def __init__(self, shape, dtype=np.float64): + self.shape = shape + self.dtype = dtype + super().__init__(dtype, shape) + + def _diag(self): + # The matrix is constructed from its diagonal 1 / [1, ..., N+1]; + # compute in a function to avoid duplicated code & storage footprint + return (1. / np.arange(1, self.shape[0] + 1)).astype(self.dtype) + + def tobanded(self): + return self._diag() + + def tosparse(self): + from scipy.sparse import diags + return diags([self._diag()], [0], shape=self.shape, dtype=self.dtype) + + def toarray(self): + return np.diag(self._diag()).astype(self.dtype) + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Mikota mass matrix without constructing or storing the matrix itself + using the knowledge of its entries and the diagonal format. + """ + x = x.reshape(self.shape[0], -1) + return self._diag()[:, np.newaxis] * x + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Mikota mass matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaK(LinearOperator): + """ + Construct a stiffness matrix in various formats of Mikota pair. + + The stiffness matrix `K` is square real tri-diagonal symmetric + positive definite with integer entries. + + Parameters + ---------- + shape : tuple of int + The shape of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.int32``. + + Methods + ------- + toarray() + Construct a dense array from Mikota data + tosparse() + Construct a sparse array from Mikota data + tobanded() + The format for banded symmetric matrices, + i.e., (2, n) ndarray with 2 upper diagonals + placing the main diagonal at the bottom. + """ + def __init__(self, shape, dtype=np.int32): + self.shape = shape + self.dtype = dtype + super().__init__(dtype, shape) + # The matrix is constructed from its diagonals; + # we precompute these to avoid duplicating the computation + n = shape[0] + self._diag0 = np.arange(2 * n - 1, 0, -2, dtype=self.dtype) + self._diag1 = - np.arange(n - 1, 0, -1, dtype=self.dtype) + + def tobanded(self): + return np.array([np.pad(self._diag1, (1, 0), 'constant'), self._diag0]) + + def tosparse(self): + from scipy.sparse import diags + return diags([self._diag1, self._diag0, self._diag1], [-1, 0, 1], + shape=self.shape, dtype=self.dtype) + + def toarray(self): + return self.tosparse().toarray() + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Mikota stiffness matrix without constructing or storing the matrix + itself using the knowledge of its entries and the 3-diagonal format. + """ + x = x.reshape(self.shape[0], -1) + result_dtype = np.promote_types(x.dtype, self.dtype) + kx = np.zeros_like(x, dtype=result_dtype) + d1 = self._diag1 + d0 = self._diag0 + kx[0, :] = d0[0] * x[0, :] + d1[0] * x[1, :] + kx[-1, :] = d1[-1] * x[-2, :] + d0[-1] * x[-1, :] + kx[1: -1, :] = (d1[:-1, None] * x[: -2, :] + + d0[1: -1, None] * x[1: -1, :] + + d1[1:, None] * x[2:, :]) + return kx + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Stiffness mass matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaPair: + """ + Construct the Mikota pair of matrices in various formats and + eigenvalues of the generalized eigenproblem with them. + + The Mikota pair of matrices [1, 2]_ models a vibration problem + of a linear mass-spring system with the ends attached where + the stiffness of the springs and the masses increase along + the system length such that vibration frequencies are subsequent + integers 1, 2, ..., `n` where `n` is the number of the masses. Thus, + eigenvalues of the generalized eigenvalue problem for + the matrix pair `K` and `M` where `K` is he system stiffness matrix + and `M` is the system mass matrix are the squares of the integers, + i.e., 1, 4, 9, ..., ``n * n``. + + The stiffness matrix `K` is square real tri-diagonal symmetric + positive definite. The mass matrix `M` is diagonal with diagonal + entries 1, 1/2, 1/3, ...., ``1/n``. Both matrices get + ill-conditioned with `n` growing. + + Parameters + ---------- + n : int + The size of the matrices of the Mikota pair. + dtype : dtype + Numerical type of the array. Default is ``np.float64``. + + Attributes + ---------- + eigenvalues : 1D ndarray, ``np.uint64`` + All eigenvalues of the Mikota pair ordered ascending. + + Methods + ------- + MikotaK() + A `LinearOperator` custom object for the stiffness matrix. + MikotaM() + A `LinearOperator` custom object for the mass matrix. + + .. versionadded:: 1.12.0 + + References + ---------- + .. [1] J. Mikota, "Frequency tuning of chain structure multibody oscillators + to place the natural frequencies at omega1 and N-1 integer multiples + omega2,..., omegaN", Z. Angew. Math. Mech. 81 (2001), S2, S201-S202. + Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004). + .. [2] Peter C. Muller and Metin Gurgoze, + "Natural frequencies of a multi-degree-of-freedom vibration system", + Proc. Appl. Math. Mech. 6, 319-320 (2006). + http://dx.doi.org/10.1002/pamm.200610141. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg._special_sparse_arrays import MikotaPair + >>> n = 6 + >>> mik = MikotaPair(n) + >>> mik_k = mik.k + >>> mik_m = mik.m + >>> mik_k.toarray() + array([[11., -5., 0., 0., 0., 0.], + [-5., 9., -4., 0., 0., 0.], + [ 0., -4., 7., -3., 0., 0.], + [ 0., 0., -3., 5., -2., 0.], + [ 0., 0., 0., -2., 3., -1.], + [ 0., 0., 0., 0., -1., 1.]]) + >>> mik_k.tobanded() + array([[ 0., -5., -4., -3., -2., -1.], + [11., 9., 7., 5., 3., 1.]]) + >>> mik_m.tobanded() + array([1. , 0.5 , 0.33333333, 0.25 , 0.2 , + 0.16666667]) + >>> mik_k.tosparse() + <6x6 sparse matrix of type '' + with 16 stored elements (3 diagonals) in DIAgonal format> + >>> mik_m.tosparse() + <6x6 sparse matrix of type '' + with 6 stored elements (1 diagonals) in DIAgonal format> + >>> np.array_equal(mik_k(np.eye(n)), mik_k.toarray()) + True + >>> np.array_equal(mik_m(np.eye(n)), mik_m.toarray()) + True + >>> mik.eigenvalues() + array([ 1, 4, 9, 16, 25, 36]) + >>> mik.eigenvalues(2) + array([ 1, 4]) + + """ + def __init__(self, n, dtype=np.float64): + self.n = n + self.dtype = dtype + self.shape = (n, n) + self.m = MikotaM(self.shape, self.dtype) + self.k = MikotaK(self.shape, self.dtype) + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : `np.uint64` array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + if m is None: + m = self.n + arange_plus1 = np.arange(1, m + 1, dtype=np.uint64) + return arange_plus1 * arange_plus1 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_svdp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_svdp.py new file mode 100644 index 0000000000000000000000000000000000000000..9b85d6c7eefe59c7049f42e0c6ff00331085afa0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/_svdp.py @@ -0,0 +1,315 @@ +""" +Python wrapper for PROPACK +-------------------------- + +PROPACK is a collection of Fortran routines for iterative computation +of partial SVDs of large matrices or linear operators. + +Based on BSD licensed pypropack project: + http://github.com/jakevdp/pypropack + Author: Jake Vanderplas + +PROPACK source is BSD licensed, and available at + http://soi.stanford.edu/~rmunk/PROPACK/ +""" + +__all__ = ['_svdp'] + +import numpy as np + +from scipy._lib._util import check_random_state +from scipy.sparse.linalg import aslinearoperator +from scipy.linalg import LinAlgError + +from ._propack import _spropack # type: ignore[attr-defined] +from ._propack import _dpropack # type: ignore[attr-defined] +from ._propack import _cpropack # type: ignore[attr-defined] +from ._propack import _zpropack # type: ignore[attr-defined] + + +_lansvd_dict = { + 'f': _spropack.slansvd, + 'd': _dpropack.dlansvd, + 'F': _cpropack.clansvd, + 'D': _zpropack.zlansvd, +} + + +_lansvd_irl_dict = { + 'f': _spropack.slansvd_irl, + 'd': _dpropack.dlansvd_irl, + 'F': _cpropack.clansvd_irl, + 'D': _zpropack.zlansvd_irl, +} + +_which_converter = { + 'LM': 'L', + 'SM': 'S', +} + + +class _AProd: + """ + Wrapper class for linear operator + + The call signature of the __call__ method matches the callback of + the PROPACK routines. + """ + def __init__(self, A): + try: + self.A = aslinearoperator(A) + except TypeError: + self.A = aslinearoperator(np.asarray(A)) + + def __call__(self, transa, m, n, x, y, sparm, iparm): + if transa == 'n': + y[:] = self.A.matvec(x) + else: + y[:] = self.A.rmatvec(x) + + @property + def shape(self): + return self.A.shape + + @property + def dtype(self): + try: + return self.A.dtype + except AttributeError: + return self.A.matvec(np.zeros(self.A.shape[1])).dtype + + +def _svdp(A, k, which='LM', irl_mode=True, kmax=None, + compute_u=True, compute_v=True, v0=None, full_output=False, tol=0, + delta=None, eta=None, anorm=0, cgs=False, elr=True, + min_relgap=0.002, shifts=None, maxiter=None, random_state=None): + """ + Compute the singular value decomposition of a linear operator using PROPACK + + Parameters + ---------- + A : array_like, sparse matrix, or LinearOperator + Operator for which SVD will be computed. If `A` is a LinearOperator + object, it must define both ``matvec`` and ``rmatvec`` methods. + k : int + Number of singular values/vectors to compute + which : {"LM", "SM"} + Which singular triplets to compute: + - 'LM': compute triplets corresponding to the `k` largest singular + values + - 'SM': compute triplets corresponding to the `k` smallest singular + values + `which='SM'` requires `irl_mode=True`. Computes largest singular + values by default. + irl_mode : bool, optional + If `True`, then compute SVD using IRL (implicitly restarted Lanczos) + mode. Default is `True`. + kmax : int, optional + Maximal number of iterations / maximal dimension of the Krylov + subspace. Default is ``10 * k``. + compute_u : bool, optional + If `True` (default) then compute left singular vectors, `u`. + compute_v : bool, optional + If `True` (default) then compute right singular vectors, `v`. + tol : float, optional + The desired relative accuracy for computed singular values. + If not specified, it will be set based on machine precision. + v0 : array_like, optional + Starting vector for iterations: must be of length ``A.shape[0]``. + If not specified, PROPACK will generate a starting vector. + full_output : bool, optional + If `True`, then return sigma_bound. Default is `False`. + delta : float, optional + Level of orthogonality to maintain between Lanczos vectors. + Default is set based on machine precision. + eta : float, optional + Orthogonality cutoff. During reorthogonalization, vectors with + component larger than `eta` along the Lanczos vector will be purged. + Default is set based on machine precision. + anorm : float, optional + Estimate of ``||A||``. Default is `0`. + cgs : bool, optional + If `True`, reorthogonalization is done using classical Gram-Schmidt. + If `False` (default), it is done using modified Gram-Schmidt. + elr : bool, optional + If `True` (default), then extended local orthogonality is enforced + when obtaining singular vectors. + min_relgap : float, optional + The smallest relative gap allowed between any shift in IRL mode. + Default is `0.001`. Accessed only if ``irl_mode=True``. + shifts : int, optional + Number of shifts per restart in IRL mode. Default is determined + to satisfy ``k <= min(kmax-shifts, m, n)``. Must be + >= 0, but choosing 0 might lead to performance degradation. + Accessed only if ``irl_mode=True``. + maxiter : int, optional + Maximum number of restarts in IRL mode. Default is `1000`. + Accessed only if ``irl_mode=True``. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + u : ndarray + The `k` largest (``which="LM"``) or smallest (``which="SM"``) left + singular vectors, ``shape == (A.shape[0], 3)``, returned only if + ``compute_u=True``. + sigma : ndarray + The top `k` singular values, ``shape == (k,)`` + vt : ndarray + The `k` largest (``which="LM"``) or smallest (``which="SM"``) right + singular vectors, ``shape == (3, A.shape[1])``, returned only if + ``compute_v=True``. + sigma_bound : ndarray + the error bounds on the singular values sigma, returned only if + ``full_output=True``. + + """ + random_state = check_random_state(random_state) + + which = which.upper() + if which not in {'LM', 'SM'}: + raise ValueError("`which` must be either 'LM' or 'SM'") + if not irl_mode and which == 'SM': + raise ValueError("`which`='SM' requires irl_mode=True") + + aprod = _AProd(A) + typ = aprod.dtype.char + + try: + lansvd_irl = _lansvd_irl_dict[typ] + lansvd = _lansvd_dict[typ] + except KeyError: + # work with non-supported types using native system precision + if np.iscomplexobj(np.empty(0, dtype=typ)): + typ = np.dtype(complex).char + else: + typ = np.dtype(float).char + lansvd_irl = _lansvd_irl_dict[typ] + lansvd = _lansvd_dict[typ] + + m, n = aprod.shape + if (k < 1) or (k > min(m, n)): + raise ValueError("k must be positive and not greater than m or n") + + if kmax is None: + kmax = 10*k + if maxiter is None: + maxiter = 1000 + + # guard against unnecessarily large kmax + kmax = min(m + 1, n + 1, kmax) + if kmax < k: + raise ValueError( + "kmax must be greater than or equal to k, " + f"but kmax ({kmax}) < k ({k})") + + # convert python args to fortran args + jobu = 'y' if compute_u else 'n' + jobv = 'y' if compute_v else 'n' + + # these will be the output arrays + u = np.zeros((m, kmax + 1), order='F', dtype=typ) + v = np.zeros((n, kmax), order='F', dtype=typ) + + # Specify the starting vector. if v0 is all zero, PROPACK will generate + # a random starting vector: the random seed cannot be controlled in that + # case, so we'll instead use numpy to generate a random vector + if v0 is None: + u[:, 0] = random_state.uniform(size=m) + if np.iscomplexobj(np.empty(0, dtype=typ)): # complex type + u[:, 0] += 1j * random_state.uniform(size=m) + else: + try: + u[:, 0] = v0 + except ValueError: + raise ValueError(f"v0 must be of length {m}") + + # process options for the fit + if delta is None: + delta = np.sqrt(np.finfo(typ).eps) + if eta is None: + eta = np.finfo(typ).eps ** 0.75 + + if irl_mode: + doption = np.array((delta, eta, anorm, min_relgap), dtype=typ.lower()) + + # validate or find default shifts + if shifts is None: + shifts = kmax - k + if k > min(kmax - shifts, m, n): + raise ValueError('shifts must satisfy ' + 'k <= min(kmax-shifts, m, n)!') + elif shifts < 0: + raise ValueError('shifts must be >= 0!') + + else: + doption = np.array((delta, eta, anorm), dtype=typ.lower()) + + ioption = np.array((int(bool(cgs)), int(bool(elr))), dtype='i') + + # If computing `u` or `v` (left and right singular vectors, + # respectively), `blocksize` controls how large a fraction of the + # work is done via fast BLAS level 3 operations. A larger blocksize + # may lead to faster computation at the expense of greater memory + # consumption. `blocksize` must be ``>= 1``. Choosing blocksize + # of 16, but docs don't specify; it's almost surely a + # power of 2. + blocksize = 16 + + # Determine lwork & liwork: + # the required lengths are specified in the PROPACK documentation + if compute_u or compute_v: + lwork = m + n + 9*kmax + 5*kmax*kmax + 4 + max( + 3*kmax*kmax + 4*kmax + 4, + blocksize*max(m, n)) + liwork = 8*kmax + else: + lwork = m + n + 9*kmax + 2*kmax*kmax + 4 + max(m + n, 4*kmax + 4) + liwork = 2*kmax + 1 + work = np.empty(lwork, dtype=typ.lower()) + iwork = np.empty(liwork, dtype=np.int32) + + # dummy arguments: these are passed to aprod, and not used in this wrapper + dparm = np.empty(1, dtype=typ.lower()) + iparm = np.empty(1, dtype=np.int32) + + if typ.isupper(): + # PROPACK documentation is unclear on the required length of zwork. + # Use the same length Julia's wrapper uses + # see https://github.com/JuliaSmoothOptimizers/PROPACK.jl/ + zwork = np.empty(m + n + 32*m, dtype=typ) + works = work, zwork, iwork + else: + works = work, iwork + + if irl_mode: + u, sigma, bnd, v, info = lansvd_irl(_which_converter[which], jobu, + jobv, m, n, shifts, k, maxiter, + aprod, u, v, tol, *works, doption, + ioption, dparm, iparm) + else: + u, sigma, bnd, v, info = lansvd(jobu, jobv, m, n, k, aprod, u, v, tol, + *works, doption, ioption, dparm, iparm) + + if info > 0: + raise LinAlgError( + f"An invariant subspace of dimension {info} was found.") + elif info < 0: + raise LinAlgError( + f"k={k} singular triplets did not converge within " + f"kmax={kmax} iterations") + + # info == 0: The K largest (or smallest) singular triplets were computed + # successfully! + + return u[:, :k], sigma, v[:, :k].conj().T, bnd diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/dsolve.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/dsolve.py new file mode 100644 index 0000000000000000000000000000000000000000..e4d0a33200cdf82eb36f9ff8c66678897e70a3a6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/dsolve.py @@ -0,0 +1,24 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'MatrixRankWarning', 'SuperLU', 'factorized', + 'spilu', 'splu', 'spsolve', + 'spsolve_triangular', 'use_solver', 'linsolve', 'test' +] + +dsolve_modules = ['linsolve'] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="dsolve", + private_modules=["_dsolve"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/eigen.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/eigen.py new file mode 100644 index 0000000000000000000000000000000000000000..0022daecea9917fd53b17724aae98226d6bb5d57 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/eigen.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'ArpackError', 'ArpackNoConvergence', 'ArpackError', + 'eigs', 'eigsh', 'lobpcg', 'svds', 'arpack', 'test' +] + +eigen_modules = ['arpack'] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="eigen", + private_modules=["_eigen"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/interface.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..9f4c635911066ef50cd7601ea23cd2a862543dfd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/interface.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'LinearOperator', 'aslinearoperator', + 'isshape', 'isintlike', 'asmatrix', + 'is_pydata_spmatrix', 'MatrixLinearOperator', 'IdentityOperator' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="interface", + private_modules=["_interface"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/isolve.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/isolve.py new file mode 100644 index 0000000000000000000000000000000000000000..61e5655caf4b7701ddbad814560b98a58e5992f4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/isolve.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'bicg', 'bicgstab', 'cg', 'cgs', 'gcrotmk', 'gmres', + 'lgmres', 'lsmr', 'lsqr', + 'minres', 'qmr', 'tfqmr', 'utils', 'iterative', 'test' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="isolve", + private_modules=["_isolve"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/matfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..3535a83aaf9854daa23827b314d93551f00ebe36 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/matfuncs.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'expm', 'inv', 'solve', 'solve_triangular', + 'spsolve', 'is_pydata_spmatrix', 'LinearOperator', + 'UPPER_TRIANGULAR', 'MatrixPowerOperator', 'ProductOperator' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="matfuncs", + private_modules=["_matfuncs"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py new file mode 100644 index 0000000000000000000000000000000000000000..03677bea159c0440dc80f5beff3c285c26be6087 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py @@ -0,0 +1,346 @@ +"""Test functions for the sparse.linalg._expm_multiply module.""" +from functools import partial +from itertools import product + +import numpy as np +import pytest +from numpy.testing import (assert_allclose, assert_, assert_equal, + suppress_warnings) +from scipy.sparse import SparseEfficiencyWarning +from scipy.sparse.linalg import aslinearoperator +import scipy.linalg +from scipy.sparse.linalg import expm as sp_expm +from scipy.sparse.linalg._expm_multiply import (_theta, _compute_p_max, + _onenormest_matrix_power, expm_multiply, _expm_multiply_simple, + _expm_multiply_interval) +from scipy._lib._util import np_long + + +IMPRECISE = {np.single, np.csingle} +REAL_DTYPES = {np.intc, np_long, np.longlong, + np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +DTYPES = sorted(REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +def estimated(func): + """If trace is estimated, it should warn. + + We warn that estimation of trace might impact performance. + All result have to be correct nevertheless! + + """ + def wrapped(*args, **kwds): + with pytest.warns(UserWarning, + match="Trace of LinearOperator not available"): + return func(*args, **kwds) + return wrapped + + +def less_than_or_close(a, b): + return np.allclose(a, b) or (a < b) + + +class TestExpmActionSimple: + """ + These tests do not consider the case of multiple time steps in one call. + """ + + def test_theta_monotonicity(self): + pairs = sorted(_theta.items()) + for (m_a, theta_a), (m_b, theta_b) in zip(pairs[:-1], pairs[1:]): + assert_(theta_a < theta_b) + + def test_p_max_default(self): + m_max = 55 + expected_p_max = 8 + observed_p_max = _compute_p_max(m_max) + assert_equal(observed_p_max, expected_p_max) + + def test_p_max_range(self): + for m_max in range(1, 55+1): + p_max = _compute_p_max(m_max) + assert_(p_max*(p_max - 1) <= m_max + 1) + p_too_big = p_max + 1 + assert_(p_too_big*(p_too_big - 1) > m_max + 1) + + def test_onenormest_matrix_power(self): + np.random.seed(1234) + n = 40 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + for p in range(4): + if not p: + M = np.identity(n) + else: + M = np.dot(M, A) + estimated = _onenormest_matrix_power(A, p) + exact = np.linalg.norm(M, 1) + assert_(less_than_or_close(estimated, exact)) + assert_(less_than_or_close(exact, 3*estimated)) + + def test_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + observed = expm_multiply(A, B) + expected = np.dot(sp_expm(A), B) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + traceA = np.trace(A) + observed = expm_multiply(aslinearoperator(A), B, traceA=traceA) + assert_allclose(observed, expected) + + def test_matrix_vector_multiply(self): + np.random.seed(1234) + n = 40 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + v = np.random.randn(n) + observed = expm_multiply(A, v) + expected = np.dot(sp_expm(A), v) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), v) + assert_allclose(observed, expected) + + def test_scaled_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i, t in product(range(nsamples), [0.2, 1.0, 1.5]): + with np.errstate(invalid='ignore'): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + observed = _expm_multiply_simple(A, B, t=t) + expected = np.dot(sp_expm(t*A), B) + assert_allclose(observed, expected) + observed = estimated(_expm_multiply_simple)( + aslinearoperator(A), B, t=t + ) + assert_allclose(observed, expected) + + def test_scaled_expm_multiply_single_timepoint(self): + np.random.seed(1234) + t = 0.1 + n = 5 + k = 2 + A = np.random.randn(n, n) + B = np.random.randn(n, k) + observed = _expm_multiply_simple(A, B, t=t) + expected = sp_expm(t*A).dot(B) + assert_allclose(observed, expected) + observed = estimated(_expm_multiply_simple)( + aslinearoperator(A), B, t=t + ) + assert_allclose(observed, expected) + + def test_sparse_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i in range(nsamples): + A = scipy.sparse.rand(n, n, density=0.05) + B = np.random.randn(n, k) + observed = expm_multiply(A, B) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in the" + " CSC matrix format") + expected = sp_expm(A).dot(B) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + + def test_complex(self): + A = np.array([ + [1j, 1j], + [0, 1j]], dtype=complex) + B = np.array([1j, 1j]) + observed = expm_multiply(A, B) + expected = np.array([ + 1j * np.exp(1j) + 1j * (1j*np.cos(1) - np.sin(1)), + 1j * np.exp(1j)], dtype=complex) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + + +class TestExpmActionInterval: + + def test_sparse_expm_multiply_interval(self): + np.random.seed(1234) + start = 0.1 + stop = 3.2 + n = 40 + k = 3 + endpoint = True + for num in (14, 13, 2): + A = scipy.sparse.rand(n, n, density=0.05) + B = np.random.randn(n, k) + v = np.random.randn(n) + for target in (B, v): + X = expm_multiply(A, target, start=start, stop=stop, + num=num, endpoint=endpoint) + samples = np.linspace(start=start, stop=stop, + num=num, endpoint=endpoint) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in" + " the CSC matrix format") + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(target)) + + def test_expm_multiply_interval_vector(self): + np.random.seed(1234) + interval = {'start': 0.1, 'stop': 3.2, 'endpoint': True} + for num, n in product([14, 13, 2], [1, 2, 5, 20, 40]): + A = scipy.linalg.inv(np.random.randn(n, n)) + v = np.random.randn(n) + samples = np.linspace(num=num, **interval) + X = expm_multiply(A, v, num=num, **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(v)) + # test for linear operator with unknown trace -> estimate trace + Xguess = estimated(expm_multiply)(aslinearoperator(A), v, + num=num, **interval) + # test for linear operator with given trace + Xgiven = expm_multiply(aslinearoperator(A), v, num=num, **interval, + traceA=np.trace(A)) + # test robustness for linear operator with wrong trace + Xwrong = expm_multiply(aslinearoperator(A), v, num=num, **interval, + traceA=np.trace(A)*5) + for sol_guess, sol_given, sol_wrong, t in zip(Xguess, Xgiven, + Xwrong, samples): + correct = sp_expm(t*A).dot(v) + assert_allclose(sol_guess, correct) + assert_allclose(sol_given, correct) + assert_allclose(sol_wrong, correct) + + def test_expm_multiply_interval_matrix(self): + np.random.seed(1234) + interval = {'start': 0.1, 'stop': 3.2, 'endpoint': True} + for num, n, k in product([14, 13, 2], [1, 2, 5, 20, 40], [1, 2]): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + samples = np.linspace(num=num, **interval) + X = expm_multiply(A, B, num=num, **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + X = estimated(expm_multiply)(aslinearoperator(A), B, num=num, + **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + + def test_sparse_expm_multiply_interval_dtypes(self): + # Test A & B int + A = scipy.sparse.diags(np.arange(5),format='csr', dtype=int) + B = np.ones(5, dtype=int) + Aexpm = scipy.sparse.diags(np.exp(np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + # Test A complex, B int + A = scipy.sparse.diags(-1j*np.arange(5),format='csr', dtype=complex) + B = np.ones(5, dtype=int) + Aexpm = scipy.sparse.diags(np.exp(-1j*np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + # Test A int, B complex + A = scipy.sparse.diags(np.arange(5),format='csr', dtype=int) + B = np.full(5, 1j, dtype=complex) + Aexpm = scipy.sparse.diags(np.exp(np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + def test_expm_multiply_interval_status_0(self): + self._help_test_specific_expm_interval_status(0) + + def test_expm_multiply_interval_status_1(self): + self._help_test_specific_expm_interval_status(1) + + def test_expm_multiply_interval_status_2(self): + self._help_test_specific_expm_interval_status(2) + + def _help_test_specific_expm_interval_status(self, target_status): + np.random.seed(1234) + start = 0.1 + stop = 3.2 + num = 13 + endpoint = True + n = 5 + k = 2 + nrepeats = 10 + nsuccesses = 0 + for num in [14, 13, 2] * nrepeats: + A = np.random.randn(n, n) + B = np.random.randn(n, k) + status = _expm_multiply_interval(A, B, + start=start, stop=stop, num=num, endpoint=endpoint, + status_only=True) + if status == target_status: + X, status = _expm_multiply_interval(A, B, + start=start, stop=stop, num=num, endpoint=endpoint, + status_only=False) + assert_equal(X.shape, (num, n, k)) + samples = np.linspace(start=start, stop=stop, + num=num, endpoint=endpoint) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + nsuccesses += 1 + if not nsuccesses: + msg = 'failed to find a status-' + str(target_status) + ' interval' + raise Exception(msg) + + +@pytest.mark.parametrize("dtype_a", DTYPES) +@pytest.mark.parametrize("dtype_b", DTYPES) +@pytest.mark.parametrize("b_is_matrix", [False, True]) +def test_expm_multiply_dtype(dtype_a, dtype_b, b_is_matrix): + """Make sure `expm_multiply` handles all numerical dtypes correctly.""" + assert_allclose_ = (partial(assert_allclose, rtol=1.2e-3, atol=1e-5) + if {dtype_a, dtype_b} & IMPRECISE else assert_allclose) + rng = np.random.default_rng(1234) + # test data + n = 7 + b_shape = (n, 3) if b_is_matrix else (n, ) + if dtype_a in REAL_DTYPES: + A = scipy.linalg.inv(rng.random([n, n])).astype(dtype_a) + else: + A = scipy.linalg.inv( + rng.random([n, n]) + 1j*rng.random([n, n]) + ).astype(dtype_a) + if dtype_b in REAL_DTYPES: + B = (2*rng.random(b_shape)).astype(dtype_b) + else: + B = (rng.random(b_shape) + 1j*rng.random(b_shape)).astype(dtype_b) + + # single application + sol_mat = expm_multiply(A, B) + sol_op = estimated(expm_multiply)(aslinearoperator(A), B) + direct_sol = np.dot(sp_expm(A), B) + assert_allclose_(sol_mat, direct_sol) + assert_allclose_(sol_op, direct_sol) + sol_op = expm_multiply(aslinearoperator(A), B, traceA=np.trace(A)) + assert_allclose_(sol_op, direct_sol) + + # for time points + interval = {'start': 0.1, 'stop': 3.2, 'num': 13, 'endpoint': True} + samples = np.linspace(**interval) + X_mat = expm_multiply(A, B, **interval) + X_op = estimated(expm_multiply)(aslinearoperator(A), B, **interval) + for sol_mat, sol_op, t in zip(X_mat, X_op, samples): + direct_sol = sp_expm(t*A).dot(B) + assert_allclose_(sol_mat, direct_sol) + assert_allclose_(sol_op, direct_sol) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_interface.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..217946e23358ec6250a03c6bb10e39615638b493 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_interface.py @@ -0,0 +1,481 @@ +"""Test functions for the sparse.linalg._interface module +""" + +from functools import partial +from itertools import product +import operator +from pytest import raises as assert_raises, warns +from numpy.testing import assert_, assert_equal + +import numpy as np +import scipy.sparse as sparse + +import scipy.sparse.linalg._interface as interface +from scipy.sparse._sputils import matrix + + +class TestLinearOperator: + def setup_method(self): + self.A = np.array([[1,2,3], + [4,5,6]]) + self.B = np.array([[1,2], + [3,4], + [5,6]]) + self.C = np.array([[1,2], + [3,4]]) + + def test_matvec(self): + def get_matvecs(A): + return [{ + 'shape': A.shape, + 'matvec': lambda x: np.dot(A, x).reshape(A.shape[0]), + 'rmatvec': lambda x: np.dot(A.T.conj(), + x).reshape(A.shape[1]) + }, + { + 'shape': A.shape, + 'matvec': lambda x: np.dot(A, x), + 'rmatvec': lambda x: np.dot(A.T.conj(), x), + 'rmatmat': lambda x: np.dot(A.T.conj(), x), + 'matmat': lambda x: np.dot(A, x) + }] + + for matvecs in get_matvecs(self.A): + A = interface.LinearOperator(**matvecs) + + assert_(A.args == ()) + + assert_equal(A.matvec(np.array([1,2,3])), [14,32]) + assert_equal(A.matvec(np.array([[1],[2],[3]])), [[14],[32]]) + assert_equal(A * np.array([1,2,3]), [14,32]) + assert_equal(A * np.array([[1],[2],[3]]), [[14],[32]]) + assert_equal(A.dot(np.array([1,2,3])), [14,32]) + assert_equal(A.dot(np.array([[1],[2],[3]])), [[14],[32]]) + + assert_equal(A.matvec(matrix([[1],[2],[3]])), [[14],[32]]) + assert_equal(A * matrix([[1],[2],[3]]), [[14],[32]]) + assert_equal(A.dot(matrix([[1],[2],[3]])), [[14],[32]]) + + assert_equal((2*A)*[1,1,1], [12,30]) + assert_equal((2 * A).rmatvec([1, 1]), [10, 14, 18]) + assert_equal((2*A).H.matvec([1,1]), [10, 14, 18]) + assert_equal((2*A)*[[1],[1],[1]], [[12],[30]]) + assert_equal((2 * A).matmat([[1], [1], [1]]), [[12], [30]]) + assert_equal((A*2)*[1,1,1], [12,30]) + assert_equal((A*2)*[[1],[1],[1]], [[12],[30]]) + assert_equal((2j*A)*[1,1,1], [12j,30j]) + assert_equal((A+A)*[1,1,1], [12, 30]) + assert_equal((A + A).rmatvec([1, 1]), [10, 14, 18]) + assert_equal((A+A).H.matvec([1,1]), [10, 14, 18]) + assert_equal((A+A)*[[1],[1],[1]], [[12], [30]]) + assert_equal((A+A).matmat([[1],[1],[1]]), [[12], [30]]) + assert_equal((-A)*[1,1,1], [-6,-15]) + assert_equal((-A)*[[1],[1],[1]], [[-6],[-15]]) + assert_equal((A-A)*[1,1,1], [0,0]) + assert_equal((A - A) * [[1], [1], [1]], [[0], [0]]) + + X = np.array([[1, 2], [3, 4]]) + # A_asarray = np.array([[1, 2, 3], [4, 5, 6]]) + assert_equal((2 * A).rmatmat(X), np.dot((2 * self.A).T, X)) + assert_equal((A * 2).rmatmat(X), np.dot((self.A * 2).T, X)) + assert_equal((2j * A).rmatmat(X), + np.dot((2j * self.A).T.conj(), X)) + assert_equal((A * 2j).rmatmat(X), + np.dot((self.A * 2j).T.conj(), X)) + assert_equal((A + A).rmatmat(X), + np.dot((self.A + self.A).T, X)) + assert_equal((A + 2j * A).rmatmat(X), + np.dot((self.A + 2j * self.A).T.conj(), X)) + assert_equal((-A).rmatmat(X), np.dot((-self.A).T, X)) + assert_equal((A - A).rmatmat(X), + np.dot((self.A - self.A).T, X)) + assert_equal((2j * A).rmatmat(2j * X), + np.dot((2j * self.A).T.conj(), 2j * X)) + + z = A+A + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] is A) + z = 2*A + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] == 2) + + assert_(isinstance(A.matvec([1, 2, 3]), np.ndarray)) + assert_(isinstance(A.matvec(np.array([[1],[2],[3]])), np.ndarray)) + assert_(isinstance(A * np.array([1,2,3]), np.ndarray)) + assert_(isinstance(A * np.array([[1],[2],[3]]), np.ndarray)) + assert_(isinstance(A.dot(np.array([1,2,3])), np.ndarray)) + assert_(isinstance(A.dot(np.array([[1],[2],[3]])), np.ndarray)) + + assert_(isinstance(A.matvec(matrix([[1],[2],[3]])), np.ndarray)) + assert_(isinstance(A * matrix([[1],[2],[3]]), np.ndarray)) + assert_(isinstance(A.dot(matrix([[1],[2],[3]])), np.ndarray)) + + assert_(isinstance(2*A, interface._ScaledLinearOperator)) + assert_(isinstance(2j*A, interface._ScaledLinearOperator)) + assert_(isinstance(A+A, interface._SumLinearOperator)) + assert_(isinstance(-A, interface._ScaledLinearOperator)) + assert_(isinstance(A-A, interface._SumLinearOperator)) + assert_(isinstance(A/2, interface._ScaledLinearOperator)) + assert_(isinstance(A/2j, interface._ScaledLinearOperator)) + assert_(((A * 3) / 3).args[0] is A) # check for simplification + + # Test that prefactor is of _ScaledLinearOperator is not mutated + # when the operator is multiplied by a number + result = A @ np.array([1, 2, 3]) + B = A * 3 + C = A / 5 + assert_equal(A @ np.array([1, 2, 3]), result) + + assert_((2j*A).dtype == np.complex128) + + # Test division by non-scalar + msg = "Can only divide a linear operator by a scalar." + with assert_raises(ValueError, match=msg): + A / np.array([1, 2]) + + assert_raises(ValueError, A.matvec, np.array([1,2])) + assert_raises(ValueError, A.matvec, np.array([1,2,3,4])) + assert_raises(ValueError, A.matvec, np.array([[1],[2]])) + assert_raises(ValueError, A.matvec, np.array([[1],[2],[3],[4]])) + + assert_raises(ValueError, lambda: A*A) + assert_raises(ValueError, lambda: A**2) + + for matvecsA, matvecsB in product(get_matvecs(self.A), + get_matvecs(self.B)): + A = interface.LinearOperator(**matvecsA) + B = interface.LinearOperator(**matvecsB) + # AtimesB = np.array([[22, 28], [49, 64]]) + AtimesB = self.A.dot(self.B) + X = np.array([[1, 2], [3, 4]]) + + assert_equal((A * B).rmatmat(X), np.dot((AtimesB).T, X)) + assert_equal((2j * A * B).rmatmat(X), + np.dot((2j * AtimesB).T.conj(), X)) + + assert_equal((A*B)*[1,1], [50,113]) + assert_equal((A*B)*[[1],[1]], [[50],[113]]) + assert_equal((A*B).matmat([[1],[1]]), [[50],[113]]) + + assert_equal((A * B).rmatvec([1, 1]), [71, 92]) + assert_equal((A * B).H.matvec([1, 1]), [71, 92]) + + assert_(isinstance(A*B, interface._ProductLinearOperator)) + + assert_raises(ValueError, lambda: A+B) + assert_raises(ValueError, lambda: A**2) + + z = A*B + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] is B) + + for matvecsC in get_matvecs(self.C): + C = interface.LinearOperator(**matvecsC) + X = np.array([[1, 2], [3, 4]]) + + assert_equal(C.rmatmat(X), np.dot((self.C).T, X)) + assert_equal((C**2).rmatmat(X), + np.dot((np.dot(self.C, self.C)).T, X)) + + assert_equal((C**2)*[1,1], [17,37]) + assert_equal((C**2).rmatvec([1, 1]), [22, 32]) + assert_equal((C**2).H.matvec([1, 1]), [22, 32]) + assert_equal((C**2).matmat([[1],[1]]), [[17],[37]]) + + assert_(isinstance(C**2, interface._PowerLinearOperator)) + + def test_matmul(self): + D = {'shape': self.A.shape, + 'matvec': lambda x: np.dot(self.A, x).reshape(self.A.shape[0]), + 'rmatvec': lambda x: np.dot(self.A.T.conj(), + x).reshape(self.A.shape[1]), + 'rmatmat': lambda x: np.dot(self.A.T.conj(), x), + 'matmat': lambda x: np.dot(self.A, x)} + A = interface.LinearOperator(**D) + B = np.array([[1 + 1j, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + b = B[0] + + assert_equal(operator.matmul(A, b), A * b) + assert_equal(operator.matmul(A, b.reshape(-1, 1)), A * b.reshape(-1, 1)) + assert_equal(operator.matmul(A, B), A * B) + assert_equal(operator.matmul(b, A.H), b * A.H) + assert_equal(operator.matmul(b.reshape(1, -1), A.H), b.reshape(1, -1) * A.H) + assert_equal(operator.matmul(B, A.H), B * A.H) + assert_raises(ValueError, operator.matmul, A, 2) + assert_raises(ValueError, operator.matmul, 2, A) + + +class TestAsLinearOperator: + def setup_method(self): + self.cases = [] + + def make_cases(original, dtype): + cases = [] + + cases.append((matrix(original, dtype=dtype), original)) + cases.append((np.array(original, dtype=dtype), original)) + cases.append((sparse.csr_matrix(original, dtype=dtype), original)) + + # Test default implementations of _adjoint and _rmatvec, which + # refer to each other. + def mv(x, dtype): + y = original.dot(x) + if len(x.shape) == 2: + y = y.reshape(-1, 1) + return y + + def rmv(x, dtype): + return original.T.conj().dot(x) + + class BaseMatlike(interface.LinearOperator): + args = () + + def __init__(self, dtype): + self.dtype = np.dtype(dtype) + self.shape = original.shape + + def _matvec(self, x): + return mv(x, self.dtype) + + class HasRmatvec(BaseMatlike): + args = () + + def _rmatvec(self,x): + return rmv(x, self.dtype) + + class HasAdjoint(BaseMatlike): + args = () + + def _adjoint(self): + shape = self.shape[1], self.shape[0] + matvec = partial(rmv, dtype=self.dtype) + rmatvec = partial(mv, dtype=self.dtype) + return interface.LinearOperator(matvec=matvec, + rmatvec=rmatvec, + dtype=self.dtype, + shape=shape) + + class HasRmatmat(HasRmatvec): + def _matmat(self, x): + return original.dot(x) + + def _rmatmat(self, x): + return original.T.conj().dot(x) + + cases.append((HasRmatvec(dtype), original)) + cases.append((HasAdjoint(dtype), original)) + cases.append((HasRmatmat(dtype), original)) + return cases + + original = np.array([[1,2,3], [4,5,6]]) + self.cases += make_cases(original, np.int32) + self.cases += make_cases(original, np.float32) + self.cases += make_cases(original, np.float64) + self.cases += [(interface.aslinearoperator(M).T, A.T) + for M, A in make_cases(original.T, np.float64)] + self.cases += [(interface.aslinearoperator(M).H, A.T.conj()) + for M, A in make_cases(original.T, np.float64)] + + original = np.array([[1, 2j, 3j], [4j, 5j, 6]]) + self.cases += make_cases(original, np.complex128) + self.cases += [(interface.aslinearoperator(M).T, A.T) + for M, A in make_cases(original.T, np.complex128)] + self.cases += [(interface.aslinearoperator(M).H, A.T.conj()) + for M, A in make_cases(original.T, np.complex128)] + + def test_basic(self): + + for M, A_array in self.cases: + A = interface.aslinearoperator(M) + M,N = A.shape + + xs = [np.array([1, 2, 3]), + np.array([[1], [2], [3]])] + ys = [np.array([1, 2]), np.array([[1], [2]])] + + if A.dtype == np.complex128: + xs += [np.array([1, 2j, 3j]), + np.array([[1], [2j], [3j]])] + ys += [np.array([1, 2j]), np.array([[1], [2j]])] + + x2 = np.array([[1, 4], [2, 5], [3, 6]]) + + for x in xs: + assert_equal(A.matvec(x), A_array.dot(x)) + assert_equal(A * x, A_array.dot(x)) + + assert_equal(A.matmat(x2), A_array.dot(x2)) + assert_equal(A * x2, A_array.dot(x2)) + + for y in ys: + assert_equal(A.rmatvec(y), A_array.T.conj().dot(y)) + assert_equal(A.T.matvec(y), A_array.T.dot(y)) + assert_equal(A.H.matvec(y), A_array.T.conj().dot(y)) + + for y in ys: + if y.ndim < 2: + continue + assert_equal(A.rmatmat(y), A_array.T.conj().dot(y)) + assert_equal(A.T.matmat(y), A_array.T.dot(y)) + assert_equal(A.H.matmat(y), A_array.T.conj().dot(y)) + + if hasattr(M,'dtype'): + assert_equal(A.dtype, M.dtype) + + assert_(hasattr(A, 'args')) + + def test_dot(self): + + for M, A_array in self.cases: + A = interface.aslinearoperator(M) + M,N = A.shape + + x0 = np.array([1, 2, 3]) + x1 = np.array([[1], [2], [3]]) + x2 = np.array([[1, 4], [2, 5], [3, 6]]) + + assert_equal(A.dot(x0), A_array.dot(x0)) + assert_equal(A.dot(x1), A_array.dot(x1)) + assert_equal(A.dot(x2), A_array.dot(x2)) + + +def test_repr(): + A = interface.LinearOperator(shape=(1, 1), matvec=lambda x: 1) + repr_A = repr(A) + assert_('unspecified dtype' not in repr_A, repr_A) + + +def test_identity(): + ident = interface.IdentityOperator((3, 3)) + assert_equal(ident * [1, 2, 3], [1, 2, 3]) + assert_equal(ident.dot(np.arange(9).reshape(3, 3)).ravel(), np.arange(9)) + + assert_raises(ValueError, ident.matvec, [1, 2, 3, 4]) + + +def test_attributes(): + A = interface.aslinearoperator(np.arange(16).reshape(4, 4)) + + def always_four_ones(x): + x = np.asarray(x) + assert_(x.shape == (3,) or x.shape == (3, 1)) + return np.ones(4) + + B = interface.LinearOperator(shape=(4, 3), matvec=always_four_ones) + + for op in [A, B, A * B, A.H, A + A, B + B, A**4]: + assert_(hasattr(op, "dtype")) + assert_(hasattr(op, "shape")) + assert_(hasattr(op, "_matvec")) + +def matvec(x): + """ Needed for test_pickle as local functions are not pickleable """ + return np.zeros(3) + +def test_pickle(): + import pickle + + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + A = interface.LinearOperator((3, 3), matvec) + s = pickle.dumps(A, protocol=protocol) + B = pickle.loads(s) + + for k in A.__dict__: + assert_equal(getattr(A, k), getattr(B, k)) + +def test_inheritance(): + class Empty(interface.LinearOperator): + pass + + with warns(RuntimeWarning, match="should implement at least"): + assert_raises(TypeError, Empty) + + class Identity(interface.LinearOperator): + def __init__(self, n): + super().__init__(dtype=None, shape=(n, n)) + + def _matvec(self, x): + return x + + id3 = Identity(3) + assert_equal(id3.matvec([1, 2, 3]), [1, 2, 3]) + assert_raises(NotImplementedError, id3.rmatvec, [4, 5, 6]) + + class MatmatOnly(interface.LinearOperator): + def __init__(self, A): + super().__init__(A.dtype, A.shape) + self.A = A + + def _matmat(self, x): + return self.A.dot(x) + + mm = MatmatOnly(np.random.randn(5, 3)) + assert_equal(mm.matvec(np.random.randn(3)).shape, (5,)) + +def test_dtypes_of_operator_sum(): + # gh-6078 + + mat_complex = np.random.rand(2,2) + 1j * np.random.rand(2,2) + mat_real = np.random.rand(2,2) + + complex_operator = interface.aslinearoperator(mat_complex) + real_operator = interface.aslinearoperator(mat_real) + + sum_complex = complex_operator + complex_operator + sum_real = real_operator + real_operator + + assert_equal(sum_real.dtype, np.float64) + assert_equal(sum_complex.dtype, np.complex128) + +def test_no_double_init(): + call_count = [0] + + def matvec(v): + call_count[0] += 1 + return v + + # It should call matvec exactly once (in order to determine the + # operator dtype) + interface.LinearOperator((2, 2), matvec=matvec) + assert_equal(call_count[0], 1) + +def test_adjoint_conjugate(): + X = np.array([[1j]]) + A = interface.aslinearoperator(X) + + B = 1j * A + Y = 1j * X + + v = np.array([1]) + + assert_equal(B.dot(v), Y.dot(v)) + assert_equal(B.H.dot(v), Y.T.conj().dot(v)) + +def test_ndim(): + X = np.array([[1]]) + A = interface.aslinearoperator(X) + assert_equal(A.ndim, 2) + +def test_transpose_noconjugate(): + X = np.array([[1j]]) + A = interface.aslinearoperator(X) + + B = 1j * A + Y = 1j * X + + v = np.array([1]) + + assert_equal(B.dot(v), Y.dot(v)) + assert_equal(B.T.dot(v), Y.T.dot(v)) + +def test_sparse_matmat_exception(): + A = interface.LinearOperator((2, 2), matvec=lambda x: x) + B = sparse.identity(2) + msg = "Unable to multiply a LinearOperator with a sparse matrix." + with assert_raises(TypeError, match=msg): + A @ B + with assert_raises(TypeError, match=msg): + B @ A + with assert_raises(ValueError): + A @ np.identity(4) + with assert_raises(ValueError): + np.identity(4) @ A diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..c7e6c11adf9fe9821456826c278c58a835948407 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py @@ -0,0 +1,598 @@ +# +# Created by: Pearu Peterson, March 2002 +# +""" Test functions for scipy.linalg._matfuncs module + +""" +import math + +import numpy as np +from numpy import array, eye, exp, random +from numpy.testing import ( + assert_allclose, assert_, assert_array_almost_equal, assert_equal, + assert_array_almost_equal_nulp, suppress_warnings) + +from scipy.sparse import csc_matrix, csc_array, SparseEfficiencyWarning +from scipy.sparse._construct import eye as speye +from scipy.sparse.linalg._matfuncs import (expm, _expm, + ProductOperator, MatrixPowerOperator, + _onenorm_matrix_power_nnm, matrix_power) +from scipy.sparse._sputils import matrix +from scipy.linalg import logm +from scipy.special import factorial, binom +import scipy.sparse +import scipy.sparse.linalg + + +def _burkardt_13_power(n, p): + """ + A helper function for testing matrix functions. + + Parameters + ---------- + n : integer greater than 1 + Order of the square matrix to be returned. + p : non-negative integer + Power of the matrix. + + Returns + ------- + out : ndarray representing a square matrix + A Forsythe matrix of order n, raised to the power p. + + """ + # Input validation. + if n != int(n) or n < 2: + raise ValueError('n must be an integer greater than 1') + n = int(n) + if p != int(p) or p < 0: + raise ValueError('p must be a non-negative integer') + p = int(p) + + # Construct the matrix explicitly. + a, b = divmod(p, n) + large = np.power(10.0, -n*a) + small = large * np.power(10.0, -n) + return np.diag([large]*(n-b), b) + np.diag([small]*b, b-n) + + +def test_onenorm_matrix_power_nnm(): + np.random.seed(1234) + for n in range(1, 5): + for p in range(5): + M = np.random.random((n, n)) + Mp = np.linalg.matrix_power(M, p) + observed = _onenorm_matrix_power_nnm(M, p) + expected = np.linalg.norm(Mp, 1) + assert_allclose(observed, expected) + +def test_matrix_power(): + np.random.seed(1234) + row, col = np.random.randint(0, 4, size=(2, 6)) + data = np.random.random(size=(6,)) + Amat = csc_matrix((data, (row, col)), shape=(4, 4)) + A = csc_array((data, (row, col)), shape=(4, 4)) + Adense = A.toarray() + for power in (2, 5, 6): + Apow = matrix_power(A, power).toarray() + Amat_pow = (Amat**power).toarray() + Adense_pow = np.linalg.matrix_power(Adense, power) + assert_allclose(Apow, Adense_pow) + assert_allclose(Apow, Amat_pow) + + +class TestExpM: + def test_zero_ndarray(self): + a = array([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a),[[1,0],[0,1]]) + + def test_zero_sparse(self): + a = csc_matrix([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a).toarray(),[[1,0],[0,1]]) + + def test_zero_matrix(self): + a = matrix([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a),[[1,0],[0,1]]) + + def test_misc_types(self): + A = expm(np.array([[1]])) + assert_allclose(expm(((1,),)), A) + assert_allclose(expm([[1]]), A) + assert_allclose(expm(matrix([[1]])), A) + assert_allclose(expm(np.array([[1]])), A) + assert_allclose(expm(csc_matrix([[1]])).A, A) + B = expm(np.array([[1j]])) + assert_allclose(expm(((1j,),)), B) + assert_allclose(expm([[1j]]), B) + assert_allclose(expm(matrix([[1j]])), B) + assert_allclose(expm(csc_matrix([[1j]])).A, B) + + def test_bidiagonal_sparse(self): + A = csc_matrix([ + [1, 3, 0], + [0, 1, 5], + [0, 0, 2]], dtype=float) + e1 = math.exp(1) + e2 = math.exp(2) + expected = np.array([ + [e1, 3*e1, 15*(e2 - 2*e1)], + [0, e1, 5*(e2 - e1)], + [0, 0, e2]], dtype=float) + observed = expm(A).toarray() + assert_array_almost_equal(observed, expected) + + def test_padecases_dtype_float(self): + for dtype in [np.float32, np.float64]: + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + A = scale * eye(3, dtype=dtype) + observed = expm(A) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + assert_array_almost_equal_nulp(observed, expected, nulp=100) + + def test_padecases_dtype_complex(self): + for dtype in [np.complex64, np.complex128]: + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + A = scale * eye(3, dtype=dtype) + observed = expm(A) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + assert_array_almost_equal_nulp(observed, expected, nulp=100) + + def test_padecases_dtype_sparse_float(self): + # float32 and complex64 lead to errors in spsolve/UMFpack + dtype = np.float64 + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + a = scale * speye(3, 3, dtype=dtype, format='csc') + e = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a csc_matrix is expensive." + ) + exact_onenorm = _expm(a, use_exact_onenorm=True).toarray() + inexact_onenorm = _expm(a, use_exact_onenorm=False).toarray() + assert_array_almost_equal_nulp(exact_onenorm, e, nulp=100) + assert_array_almost_equal_nulp(inexact_onenorm, e, nulp=100) + + def test_padecases_dtype_sparse_complex(self): + # float32 and complex64 lead to errors in spsolve/UMFpack + dtype = np.complex128 + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + a = scale * speye(3, 3, dtype=dtype, format='csc') + e = exp(scale) * eye(3, dtype=dtype) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a csc_matrix is expensive." + ) + assert_array_almost_equal_nulp(expm(a).toarray(), e, nulp=100) + + def test_logm_consistency(self): + random.seed(1234) + for dtype in [np.float64, np.complex128]: + for n in range(1, 10): + for scale in [1e-4, 1e-3, 1e-2, 1e-1, 1, 1e1, 1e2]: + # make logm(A) be of a given scale + A = (eye(n) + random.rand(n, n) * scale).astype(dtype) + if np.iscomplexobj(A): + A = A + 1j * random.rand(n, n) * scale + assert_array_almost_equal(expm(logm(A)), A) + + def test_integer_matrix(self): + Q = np.array([ + [-3, 1, 1, 1], + [1, -3, 1, 1], + [1, 1, -3, 1], + [1, 1, 1, -3]]) + assert_allclose(expm(Q), expm(1.0 * Q)) + + def test_integer_matrix_2(self): + # Check for integer overflows + Q = np.array([[-500, 500, 0, 0], + [0, -550, 360, 190], + [0, 630, -630, 0], + [0, 0, 0, 0]], dtype=np.int16) + assert_allclose(expm(Q), expm(1.0 * Q)) + + Q = csc_matrix(Q) + assert_allclose(expm(Q).A, expm(1.0 * Q).A) + + def test_triangularity_perturbation(self): + # Experiment (1) of + # Awad H. Al-Mohy and Nicholas J. Higham (2012) + # Improved Inverse Scaling and Squaring Algorithms + # for the Matrix Logarithm. + A = np.array([ + [3.2346e-1, 3e4, 3e4, 3e4], + [0, 3.0089e-1, 3e4, 3e4], + [0, 0, 3.221e-1, 3e4], + [0, 0, 0, 3.0744e-1]], + dtype=float) + A_logm = np.array([ + [-1.12867982029050462e+00, 9.61418377142025565e+04, + -4.52485573953179264e+09, 2.92496941103871812e+14], + [0.00000000000000000e+00, -1.20101052953082288e+00, + 9.63469687211303099e+04, -4.68104828911105442e+09], + [0.00000000000000000e+00, 0.00000000000000000e+00, + -1.13289322264498393e+00, 9.53249183094775653e+04], + [0.00000000000000000e+00, 0.00000000000000000e+00, + 0.00000000000000000e+00, -1.17947533272554850e+00]], + dtype=float) + assert_allclose(expm(A_logm), A, rtol=1e-4) + + # Perturb the upper triangular matrix by tiny amounts, + # so that it becomes technically not upper triangular. + random.seed(1234) + tiny = 1e-17 + A_logm_perturbed = A_logm.copy() + A_logm_perturbed[1, 0] = tiny + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Ill-conditioned.*") + A_expm_logm_perturbed = expm(A_logm_perturbed) + rtol = 1e-4 + atol = 100 * tiny + assert_(not np.allclose(A_expm_logm_perturbed, A, rtol=rtol, atol=atol)) + + def test_burkardt_1(self): + # This matrix is diagonal. + # The calculation of the matrix exponential is simple. + # + # This is the first of a series of matrix exponential tests + # collected by John Burkardt from the following sources. + # + # Alan Laub, + # Review of "Linear System Theory" by Joao Hespanha, + # SIAM Review, + # Volume 52, Number 4, December 2010, pages 779--781. + # + # Cleve Moler and Charles Van Loan, + # Nineteen Dubious Ways to Compute the Exponential of a Matrix, + # Twenty-Five Years Later, + # SIAM Review, + # Volume 45, Number 1, March 2003, pages 3--49. + # + # Cleve Moler, + # Cleve's Corner: A Balancing Act for the Matrix Exponential, + # 23 July 2012. + # + # Robert Ward, + # Numerical computation of the matrix exponential + # with accuracy estimate, + # SIAM Journal on Numerical Analysis, + # Volume 14, Number 4, September 1977, pages 600--610. + exp1 = np.exp(1) + exp2 = np.exp(2) + A = np.array([ + [1, 0], + [0, 2], + ], dtype=float) + desired = np.array([ + [exp1, 0], + [0, exp2], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_2(self): + # This matrix is symmetric. + # The calculation of the matrix exponential is straightforward. + A = np.array([ + [1, 3], + [3, 2], + ], dtype=float) + desired = np.array([ + [39.322809708033859, 46.166301438885753], + [46.166301438885768, 54.711576854329110], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_3(self): + # This example is due to Laub. + # This matrix is ill-suited for the Taylor series approach. + # As powers of A are computed, the entries blow up too quickly. + exp1 = np.exp(1) + exp39 = np.exp(39) + A = np.array([ + [0, 1], + [-39, -40], + ], dtype=float) + desired = np.array([ + [ + 39/(38*exp1) - 1/(38*exp39), + -np.expm1(-38) / (38*exp1)], + [ + 39*np.expm1(-38) / (38*exp1), + -1/(38*exp1) + 39/(38*exp39)], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_4(self): + # This example is due to Moler and Van Loan. + # The example will cause problems for the series summation approach, + # as well as for diagonal Pade approximations. + A = np.array([ + [-49, 24], + [-64, 31], + ], dtype=float) + U = np.array([[3, 1], [4, 2]], dtype=float) + V = np.array([[1, -1/2], [-2, 3/2]], dtype=float) + w = np.array([-17, -1], dtype=float) + desired = np.dot(U * np.exp(w), V) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_5(self): + # This example is due to Moler and Van Loan. + # This matrix is strictly upper triangular + # All powers of A are zero beyond some (low) limit. + # This example will cause problems for Pade approximations. + A = np.array([ + [0, 6, 0, 0], + [0, 0, 6, 0], + [0, 0, 0, 6], + [0, 0, 0, 0], + ], dtype=float) + desired = np.array([ + [1, 6, 18, 36], + [0, 1, 6, 18], + [0, 0, 1, 6], + [0, 0, 0, 1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_6(self): + # This example is due to Moler and Van Loan. + # This matrix does not have a complete set of eigenvectors. + # That means the eigenvector approach will fail. + exp1 = np.exp(1) + A = np.array([ + [1, 1], + [0, 1], + ], dtype=float) + desired = np.array([ + [exp1, exp1], + [0, exp1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_7(self): + # This example is due to Moler and Van Loan. + # This matrix is very close to example 5. + # Mathematically, it has a complete set of eigenvectors. + # Numerically, however, the calculation will be suspect. + exp1 = np.exp(1) + eps = np.spacing(1) + A = np.array([ + [1 + eps, 1], + [0, 1 - eps], + ], dtype=float) + desired = np.array([ + [exp1, exp1], + [0, exp1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_8(self): + # This matrix was an example in Wikipedia. + exp4 = np.exp(4) + exp16 = np.exp(16) + A = np.array([ + [21, 17, 6], + [-5, -1, -6], + [4, 4, 16], + ], dtype=float) + desired = np.array([ + [13*exp16 - exp4, 13*exp16 - 5*exp4, 2*exp16 - 2*exp4], + [-9*exp16 + exp4, -9*exp16 + 5*exp4, -2*exp16 + 2*exp4], + [16*exp16, 16*exp16, 4*exp16], + ], dtype=float) * 0.25 + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_9(self): + # This matrix is due to the NAG Library. + # It is an example for function F01ECF. + A = np.array([ + [1, 2, 2, 2], + [3, 1, 1, 2], + [3, 2, 1, 2], + [3, 3, 3, 1], + ], dtype=float) + desired = np.array([ + [740.7038, 610.8500, 542.2743, 549.1753], + [731.2510, 603.5524, 535.0884, 542.2743], + [823.7630, 679.4257, 603.5524, 610.8500], + [998.4355, 823.7630, 731.2510, 740.7038], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_10(self): + # This is Ward's example #1. + # It is defective and nonderogatory. + A = np.array([ + [4, 2, 0], + [1, 4, 1], + [1, 1, 4], + ], dtype=float) + assert_allclose(sorted(scipy.linalg.eigvals(A)), (3, 3, 6)) + desired = np.array([ + [147.8666224463699, 183.7651386463682, 71.79703239999647], + [127.7810855231823, 183.7651386463682, 91.88256932318415], + [127.7810855231824, 163.6796017231806, 111.9681062463718], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_11(self): + # This is Ward's example #2. + # It is a symmetric matrix. + A = np.array([ + [29.87942128909879, 0.7815750847907159, -2.289519314033932], + [0.7815750847907159, 25.72656945571064, 8.680737820540137], + [-2.289519314033932, 8.680737820540137, 34.39400925519054], + ], dtype=float) + assert_allclose(scipy.linalg.eigvalsh(A), (20, 30, 40)) + desired = np.array([ + [ + 5.496313853692378E+15, + -1.823188097200898E+16, + -3.047577080858001E+16], + [ + -1.823188097200899E+16, + 6.060522870222108E+16, + 1.012918429302482E+17], + [ + -3.047577080858001E+16, + 1.012918429302482E+17, + 1.692944112408493E+17], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_12(self): + # This is Ward's example #3. + # Ward's algorithm has difficulty estimating the accuracy + # of its results. + A = np.array([ + [-131, 19, 18], + [-390, 56, 54], + [-387, 57, 52], + ], dtype=float) + assert_allclose(sorted(scipy.linalg.eigvals(A)), (-20, -2, -1)) + desired = np.array([ + [-1.509644158793135, 0.3678794391096522, 0.1353352811751005], + [-5.632570799891469, 1.471517758499875, 0.4060058435250609], + [-4.934938326088363, 1.103638317328798, 0.5413411267617766], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_13(self): + # This is Ward's example #4. + # This is a version of the Forsythe matrix. + # The eigenvector problem is badly conditioned. + # Ward's algorithm has difficulty estimating the accuracy + # of its results for this problem. + # + # Check the construction of one instance of this family of matrices. + A4_actual = _burkardt_13_power(4, 1) + A4_desired = [[0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [1e-4, 0, 0, 0]] + assert_allclose(A4_actual, A4_desired) + # Check the expm for a few instances. + for n in (2, 3, 4, 10): + # Approximate expm using Taylor series. + # This works well for this matrix family + # because each matrix in the summation, + # even before dividing by the factorial, + # is entrywise positive with max entry 10**(-floor(p/n)*n). + k = max(1, int(np.ceil(16/n))) + desired = np.zeros((n, n), dtype=float) + for p in range(n*k): + Ap = _burkardt_13_power(n, p) + assert_equal(np.min(Ap), 0) + assert_allclose(np.max(Ap), np.power(10, -np.floor(p/n)*n)) + desired += Ap / factorial(p) + actual = expm(_burkardt_13_power(n, 1)) + assert_allclose(actual, desired) + + def test_burkardt_14(self): + # This is Moler's example. + # This badly scaled matrix caused problems for MATLAB's expm(). + A = np.array([ + [0, 1e-8, 0], + [-(2e10 + 4e8/6.), -3, 2e10], + [200./3., 0, -200./3.], + ], dtype=float) + desired = np.array([ + [0.446849468283175, 1.54044157383952e-09, 0.462811453558774], + [-5743067.77947947, -0.0152830038686819, -4526542.71278401], + [0.447722977849494, 1.54270484519591e-09, 0.463480648837651], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_pascal(self): + # Test pascal triangle. + # Nilpotent exponential, used to trigger a failure (gh-8029) + + for scale in [1.0, 1e-3, 1e-6]: + for n in range(0, 80, 3): + sc = scale ** np.arange(n, -1, -1) + if np.any(sc < 1e-300): + break + + A = np.diag(np.arange(1, n + 1), -1) * scale + B = expm(A) + + got = B + expected = binom(np.arange(n + 1)[:,None], + np.arange(n + 1)[None,:]) * sc[None,:] / sc[:,None] + atol = 1e-13 * abs(expected).max() + assert_allclose(got, expected, atol=atol) + + def test_matrix_input(self): + # Large np.matrix inputs should work, gh-5546 + A = np.zeros((200, 200)) + A[-1,0] = 1 + B0 = expm(A) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, "the matrix subclass.*") + sup.filter(PendingDeprecationWarning, "the matrix subclass.*") + B = expm(np.matrix(A)) + assert_allclose(B, B0) + + def test_exp_sinch_overflow(self): + # Check overflow in intermediate steps is fixed (gh-11839) + L = np.array([[1.0, -0.5, -0.5, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, -0.5, -0.5, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, -0.5, -0.5], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) + + E0 = expm(-L) + E1 = expm(-2**11 * L) + E2 = E0 + for j in range(11): + E2 = E2 @ E2 + + assert_allclose(E1, E2) + + +class TestOperators: + + def test_product_operator(self): + random.seed(1234) + n = 5 + k = 2 + nsamples = 10 + for i in range(nsamples): + A = np.random.randn(n, n) + B = np.random.randn(n, n) + C = np.random.randn(n, n) + D = np.random.randn(n, k) + op = ProductOperator(A, B, C) + assert_allclose(op.matmat(D), A.dot(B).dot(C).dot(D)) + assert_allclose(op.T.matmat(D), (A.dot(B).dot(C)).T.dot(D)) + + def test_matrix_power_operator(self): + random.seed(1234) + n = 5 + k = 2 + p = 3 + nsamples = 10 + for i in range(nsamples): + A = np.random.randn(n, n) + B = np.random.randn(n, k) + op = MatrixPowerOperator(A, p) + assert_allclose(op.matmat(B), np.linalg.matrix_power(A, p).dot(B)) + assert_allclose(op.T.matmat(B), np.linalg.matrix_power(A, p).T.dot(B)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_norm.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..96c2f65da75b5e30e34dc2d4e695d1bb369b79b9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_norm.py @@ -0,0 +1,141 @@ +"""Test functions for the sparse.linalg.norm module +""" + +import pytest +import numpy as np +from numpy.linalg import norm as npnorm +from numpy.testing import assert_allclose, assert_equal +from pytest import raises as assert_raises + +import scipy.sparse +from scipy.sparse.linalg import norm as spnorm + + +# https://github.com/scipy/scipy/issues/16031 +def test_sparray_norm(): + row = np.array([0, 0, 1, 1]) + col = np.array([0, 1, 2, 3]) + data = np.array([4, 5, 7, 9]) + test_arr = scipy.sparse.coo_array((data, (row, col)), shape=(2, 4)) + test_mat = scipy.sparse.coo_matrix((data, (row, col)), shape=(2, 4)) + assert_equal(spnorm(test_arr, ord=1, axis=0), np.array([4, 5, 7, 9])) + assert_equal(spnorm(test_mat, ord=1, axis=0), np.array([4, 5, 7, 9])) + assert_equal(spnorm(test_arr, ord=1, axis=1), np.array([9, 16])) + assert_equal(spnorm(test_mat, ord=1, axis=1), np.array([9, 16])) + + +class TestNorm: + def setup_method(self): + a = np.arange(9) - 4 + b = a.reshape((3, 3)) + self.b = scipy.sparse.csr_matrix(b) + + def test_matrix_norm(self): + + # Frobenius norm is the default + assert_allclose(spnorm(self.b), 7.745966692414834) + assert_allclose(spnorm(self.b, 'fro'), 7.745966692414834) + + assert_allclose(spnorm(self.b, np.inf), 9) + assert_allclose(spnorm(self.b, -np.inf), 2) + assert_allclose(spnorm(self.b, 1), 7) + assert_allclose(spnorm(self.b, -1), 6) + # Only floating or complex floating dtype supported by svds. + with pytest.warns(UserWarning, match="The problem size"): + assert_allclose(spnorm(self.b.astype(np.float64), 2), + 7.348469228349534) + + # _multi_svd_norm is not implemented for sparse matrix + assert_raises(NotImplementedError, spnorm, self.b, -2) + + def test_matrix_norm_axis(self): + for m, axis in ((self.b, None), (self.b, (0, 1)), (self.b.T, (1, 0))): + assert_allclose(spnorm(m, axis=axis), 7.745966692414834) + assert_allclose(spnorm(m, 'fro', axis=axis), 7.745966692414834) + assert_allclose(spnorm(m, np.inf, axis=axis), 9) + assert_allclose(spnorm(m, -np.inf, axis=axis), 2) + assert_allclose(spnorm(m, 1, axis=axis), 7) + assert_allclose(spnorm(m, -1, axis=axis), 6) + + def test_vector_norm(self): + v = [4.5825756949558398, 4.2426406871192848, 4.5825756949558398] + for m, a in (self.b, 0), (self.b.T, 1): + for axis in a, (a, ), a-2, (a-2, ): + assert_allclose(spnorm(m, 1, axis=axis), [7, 6, 7]) + assert_allclose(spnorm(m, np.inf, axis=axis), [4, 3, 4]) + assert_allclose(spnorm(m, axis=axis), v) + assert_allclose(spnorm(m, ord=2, axis=axis), v) + assert_allclose(spnorm(m, ord=None, axis=axis), v) + + def test_norm_exceptions(self): + m = self.b + assert_raises(TypeError, spnorm, m, None, 1.5) + assert_raises(TypeError, spnorm, m, None, [2]) + assert_raises(ValueError, spnorm, m, None, ()) + assert_raises(ValueError, spnorm, m, None, (0, 1, 2)) + assert_raises(ValueError, spnorm, m, None, (0, 0)) + assert_raises(ValueError, spnorm, m, None, (0, 2)) + assert_raises(ValueError, spnorm, m, None, (-3, 0)) + assert_raises(ValueError, spnorm, m, None, 2) + assert_raises(ValueError, spnorm, m, None, -3) + assert_raises(ValueError, spnorm, m, 'plate_of_shrimp', 0) + assert_raises(ValueError, spnorm, m, 'plate_of_shrimp', (0, 1)) + + +class TestVsNumpyNorm: + _sparse_types = ( + scipy.sparse.bsr_matrix, + scipy.sparse.coo_matrix, + scipy.sparse.csc_matrix, + scipy.sparse.csr_matrix, + scipy.sparse.dia_matrix, + scipy.sparse.dok_matrix, + scipy.sparse.lil_matrix, + ) + _test_matrices = ( + (np.arange(9) - 4).reshape((3, 3)), + [ + [1, 2, 3], + [-1, 1, 4]], + [ + [1, 0, 3], + [-1, 1, 4j]], + ) + + def test_sparse_matrix_norms(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + assert_allclose(spnorm(S), npnorm(M)) + assert_allclose(spnorm(S, 'fro'), npnorm(M, 'fro')) + assert_allclose(spnorm(S, np.inf), npnorm(M, np.inf)) + assert_allclose(spnorm(S, -np.inf), npnorm(M, -np.inf)) + assert_allclose(spnorm(S, 1), npnorm(M, 1)) + assert_allclose(spnorm(S, -1), npnorm(M, -1)) + + def test_sparse_matrix_norms_with_axis(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + for axis in None, (0, 1), (1, 0): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + for ord in 'fro', np.inf, -np.inf, 1, -1: + assert_allclose(spnorm(S, ord, axis=axis), + npnorm(M, ord, axis=axis)) + # Some numpy matrix norms are allergic to negative axes. + for axis in (-2, -1), (-1, -2), (1, -2): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + assert_allclose(spnorm(S, 'f', axis=axis), + npnorm(M, 'f', axis=axis)) + assert_allclose(spnorm(S, 'fro', axis=axis), + npnorm(M, 'fro', axis=axis)) + + def test_sparse_vector_norms(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + for axis in (0, 1, -1, -2, (0, ), (1, ), (-1, ), (-2, )): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + for ord in None, 2, np.inf, -np.inf, 1, 0.5, 0.42: + assert_allclose(spnorm(S, ord, axis=axis), + npnorm(M, ord, axis=axis)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_onenormest.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_onenormest.py new file mode 100644 index 0000000000000000000000000000000000000000..907a456f0358e3a9cca4f6293e3806aef813ef4d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_onenormest.py @@ -0,0 +1,252 @@ +"""Test functions for the sparse.linalg._onenormest module +""" + +import numpy as np +from numpy.testing import assert_allclose, assert_equal, assert_ +import pytest +import scipy.linalg +import scipy.sparse.linalg +from scipy.sparse.linalg._onenormest import _onenormest_core, _algorithm_2_2 + + +class MatrixProductOperator(scipy.sparse.linalg.LinearOperator): + """ + This is purely for onenormest testing. + """ + + def __init__(self, A, B): + if A.ndim != 2 or B.ndim != 2: + raise ValueError('expected ndarrays representing matrices') + if A.shape[1] != B.shape[0]: + raise ValueError('incompatible shapes') + self.A = A + self.B = B + self.ndim = 2 + self.shape = (A.shape[0], B.shape[1]) + + def _matvec(self, x): + return np.dot(self.A, np.dot(self.B, x)) + + def _rmatvec(self, x): + return np.dot(np.dot(x, self.A), self.B) + + def _matmat(self, X): + return np.dot(self.A, np.dot(self.B, X)) + + @property + def T(self): + return MatrixProductOperator(self.B.T, self.A.T) + + +class TestOnenormest: + + @pytest.mark.xslow + def test_onenormest_table_3_t_2(self): + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 2 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + assert_(0.99 < np.mean(underestimation_ratio) < 1.0) + + # check the max and mean required column resamples + assert_equal(np.max(nresample_list), 2) + assert_(0.05 < np.mean(nresample_list) < 0.2) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.9 < proportion_exact < 0.95) + + # check the average number of matrix*vector multiplications + assert_(3.5 < np.mean(nmult_list) < 4.5) + + @pytest.mark.xslow + def test_onenormest_table_4_t_7(self): + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 7 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A = np.random.randint(-1, 2, size=(n, n)) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + assert_(0.90 < np.mean(underestimation_ratio) < 0.99) + + # check the required column resamples + assert_equal(np.max(nresample_list), 0) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.15 < proportion_exact < 0.25) + + # check the average number of matrix*vector multiplications + assert_(3.5 < np.mean(nmult_list) < 4.5) + + def test_onenormest_table_5_t_1(self): + # "note that there is no randomness and hence only one estimate for t=1" + t = 1 + n = 100 + itmax = 5 + alpha = 1 - 1e-6 + A = -scipy.linalg.inv(np.identity(n) + alpha*np.eye(n, k=1)) + first_col = np.array([1] + [0]*(n-1)) + first_row = np.array([(-alpha)**i for i in range(n)]) + B = -scipy.linalg.toeplitz(first_col, first_row) + assert_allclose(A, B) + est, v, w, nmults, nresamples = _onenormest_core(B, B.T, t, itmax) + exact_value = scipy.linalg.norm(B, 1) + underest_ratio = est / exact_value + assert_allclose(underest_ratio, 0.05, rtol=1e-4) + assert_equal(nmults, 11) + assert_equal(nresamples, 0) + # check the non-underscored version of onenormest + est_plain = scipy.sparse.linalg.onenormest(B, t=t, itmax=itmax) + assert_allclose(est, est_plain) + + @pytest.mark.xslow + def test_onenormest_table_6_t_1(self): + #TODO this test seems to give estimates that match the table, + #TODO even though no attempt has been made to deal with + #TODO complex numbers in the one-norm estimation. + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 1 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A_inv = np.random.rand(n, n) + 1j * np.random.rand(n, n) + A = scipy.linalg.inv(A_inv) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + underestimation_ratio_mean = np.mean(underestimation_ratio) + assert_(0.90 < underestimation_ratio_mean < 0.99) + + # check the required column resamples + max_nresamples = np.max(nresample_list) + assert_equal(max_nresamples, 0) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.7 < proportion_exact < 0.8) + + # check the average number of matrix*vector multiplications + mean_nmult = np.mean(nmult_list) + assert_(4 < mean_nmult < 5) + + def _help_product_norm_slow(self, A, B): + # for profiling + C = np.dot(A, B) + return scipy.linalg.norm(C, 1) + + def _help_product_norm_fast(self, A, B): + # for profiling + t = 2 + itmax = 5 + D = MatrixProductOperator(A, B) + est, v, w, nmults, nresamples = _onenormest_core(D, D.T, t, itmax) + return est + + @pytest.mark.slow + def test_onenormest_linear_operator(self): + # Define a matrix through its product A B. + # Depending on the shapes of A and B, + # it could be easy to multiply this product by a small matrix, + # but it could be annoying to look at all of + # the entries of the product explicitly. + np.random.seed(1234) + n = 6000 + k = 3 + A = np.random.randn(n, k) + B = np.random.randn(k, n) + fast_estimate = self._help_product_norm_fast(A, B) + exact_value = self._help_product_norm_slow(A, B) + assert_(fast_estimate <= exact_value <= 3*fast_estimate, + f'fast: {fast_estimate:g}\nexact:{exact_value:g}') + + def test_returns(self): + np.random.seed(1234) + A = scipy.sparse.rand(50, 50, 0.1) + + s0 = scipy.linalg.norm(A.toarray(), 1) + s1, v = scipy.sparse.linalg.onenormest(A, compute_v=True) + s2, w = scipy.sparse.linalg.onenormest(A, compute_w=True) + s3, v2, w2 = scipy.sparse.linalg.onenormest(A, compute_w=True, compute_v=True) + + assert_allclose(s1, s0, rtol=1e-9) + assert_allclose(np.linalg.norm(A.dot(v), 1), s0*np.linalg.norm(v, 1), rtol=1e-9) + assert_allclose(A.dot(v), w, rtol=1e-9) + + +class TestAlgorithm_2_2: + + def test_randn_inv(self): + np.random.seed(1234) + n = 20 + nsamples = 100 + for i in range(nsamples): + + # Choose integer t uniformly between 1 and 3 inclusive. + t = np.random.randint(1, 4) + + # Choose n uniformly between 10 and 40 inclusive. + n = np.random.randint(10, 41) + + # Sample the inverse of a matrix with random normal entries. + A = scipy.linalg.inv(np.random.randn(n, n)) + + # Compute the 1-norm bounds. + g, ind = _algorithm_2_2(A, A.T, t) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_propack.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_propack.py new file mode 100644 index 0000000000000000000000000000000000000000..64eb888fd99433cc2dba241878d1db9316f88736 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_propack.py @@ -0,0 +1,165 @@ +import os +import pytest + +import numpy as np +from numpy.testing import assert_allclose +from pytest import raises as assert_raises +from scipy.sparse.linalg._svdp import _svdp +from scipy.sparse import csr_matrix, csc_matrix + + +# dtype_flavour to tolerance +TOLS = { + np.float32: 1e-4, + np.float64: 1e-8, + np.complex64: 1e-4, + np.complex128: 1e-8, +} + + +def is_complex_type(dtype): + return np.dtype(dtype).kind == "c" + + +_dtypes = [] +for dtype_flavour in TOLS.keys(): + marks = [] + if is_complex_type(dtype_flavour): + marks = [pytest.mark.slow] + _dtypes.append(pytest.param(dtype_flavour, marks=marks, + id=dtype_flavour.__name__)) +_dtypes = tuple(_dtypes) # type: ignore[assignment] + + +def generate_matrix(constructor, n, m, f, + dtype=float, rseed=0, **kwargs): + """Generate a random sparse matrix""" + rng = np.random.RandomState(rseed) + if is_complex_type(dtype): + M = (- 5 + 10 * rng.rand(n, m) + - 5j + 10j * rng.rand(n, m)).astype(dtype) + else: + M = (-5 + 10 * rng.rand(n, m)).astype(dtype) + M[M.real > 10 * f - 5] = 0 + return constructor(M, **kwargs) + + +def assert_orthogonal(u1, u2, rtol, atol): + """Check that the first k rows of u1 and u2 are orthogonal""" + A = abs(np.dot(u1.conj().T, u2)) + assert_allclose(A, np.eye(u1.shape[1], u2.shape[1]), rtol=rtol, atol=atol) + + +def check_svdp(n, m, constructor, dtype, k, irl_mode, which, f=0.8): + tol = TOLS[dtype] + + M = generate_matrix(np.asarray, n, m, f, dtype) + Msp = constructor(M) + + u1, sigma1, vt1 = np.linalg.svd(M, full_matrices=False) + u2, sigma2, vt2, _ = _svdp(Msp, k=k, which=which, irl_mode=irl_mode, + tol=tol) + + # check the which + if which.upper() == 'SM': + u1 = np.roll(u1, k, 1) + vt1 = np.roll(vt1, k, 0) + sigma1 = np.roll(sigma1, k) + + # check that singular values agree + assert_allclose(sigma1[:k], sigma2, rtol=tol, atol=tol) + + # check that singular vectors are orthogonal + assert_orthogonal(u1, u2, rtol=tol, atol=tol) + assert_orthogonal(vt1.T, vt2.T, rtol=tol, atol=tol) + + +@pytest.mark.parametrize('ctor', (np.array, csr_matrix, csc_matrix)) +@pytest.mark.parametrize('dtype', _dtypes) +@pytest.mark.parametrize('irl', (True, False)) +@pytest.mark.parametrize('which', ('LM', 'SM')) +def test_svdp(ctor, dtype, irl, which): + np.random.seed(0) + n, m, k = 10, 20, 3 + if which == 'SM' and not irl: + message = "`which`='SM' requires irl_mode=True" + with assert_raises(ValueError, match=message): + check_svdp(n, m, ctor, dtype, k, irl, which) + else: + check_svdp(n, m, ctor, dtype, k, irl, which) + + +@pytest.mark.parametrize('dtype', _dtypes) +@pytest.mark.parametrize('irl', (False, True)) +@pytest.mark.timeout(120) # True, complex64 > 60 s: prerel deps cov 64bit blas +def test_examples(dtype, irl): + # Note: atol for complex64 bumped from 1e-4 to 1e-3 due to test failures + # with BLIS, Netlib, and MKL+AVX512 - see + # https://github.com/conda-forge/scipy-feedstock/pull/198#issuecomment-999180432 + atol = { + np.float32: 1.3e-4, + np.float64: 1e-9, + np.complex64: 1e-3, + np.complex128: 1e-9, + }[dtype] + + path_prefix = os.path.dirname(__file__) + # Test matrices from `illc1850.coord` and `mhd1280b.cua` distributed with + # PROPACK 2.1: http://sun.stanford.edu/~rmunk/PROPACK/ + relative_path = "propack_test_data.npz" + filename = os.path.join(path_prefix, relative_path) + with np.load(filename, allow_pickle=True) as data: + if is_complex_type(dtype): + A = data['A_complex'].item().astype(dtype) + else: + A = data['A_real'].item().astype(dtype) + + k = 200 + u, s, vh, _ = _svdp(A, k, irl_mode=irl, random_state=0) + + # complex example matrix has many repeated singular values, so check only + # beginning non-repeated singular vectors to avoid permutations + sv_check = 27 if is_complex_type(dtype) else k + u = u[:, :sv_check] + vh = vh[:sv_check, :] + s = s[:sv_check] + + # Check orthogonality of singular vectors + assert_allclose(np.eye(u.shape[1]), u.conj().T @ u, atol=atol) + assert_allclose(np.eye(vh.shape[0]), vh @ vh.conj().T, atol=atol) + + # Ensure the norm of the difference between the np.linalg.svd and + # PROPACK reconstructed matrices is small + u3, s3, vh3 = np.linalg.svd(A.todense()) + u3 = u3[:, :sv_check] + s3 = s3[:sv_check] + vh3 = vh3[:sv_check, :] + A3 = u3 @ np.diag(s3) @ vh3 + recon = u @ np.diag(s) @ vh + assert_allclose(np.linalg.norm(A3 - recon), 0, atol=atol) + + +@pytest.mark.parametrize('shifts', (None, -10, 0, 1, 10, 70)) +@pytest.mark.parametrize('dtype', _dtypes[:2]) +def test_shifts(shifts, dtype): + np.random.seed(0) + n, k = 70, 10 + A = np.random.random((n, n)) + if shifts is not None and ((shifts < 0) or (k > min(n-1-shifts, n))): + with pytest.raises(ValueError): + _svdp(A, k, shifts=shifts, kmax=5*k, irl_mode=True) + else: + _svdp(A, k, shifts=shifts, kmax=5*k, irl_mode=True) + + +@pytest.mark.slow +@pytest.mark.xfail() +def test_shifts_accuracy(): + np.random.seed(0) + n, k = 70, 10 + A = np.random.random((n, n)).astype(np.float64) + u1, s1, vt1, _ = _svdp(A, k, shifts=None, which='SM', irl_mode=True) + u2, s2, vt2, _ = _svdp(A, k, shifts=32, which='SM', irl_mode=True) + # shifts <= 32 doesn't agree with shifts > 32 + # Does agree when which='LM' instead of 'SM' + assert_allclose(s1, s2) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py new file mode 100644 index 0000000000000000000000000000000000000000..b42448d0ef1ab92f9745b0be22b65bb3f280fcb4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py @@ -0,0 +1,243 @@ +import pytest + +import numpy as np +import scipy.sparse as sp +import scipy.sparse.linalg as splin + +from numpy.testing import assert_allclose, assert_equal + +try: + import sparse +except Exception: + sparse = None + +pytestmark = pytest.mark.skipif(sparse is None, + reason="pydata/sparse not installed") + + +msg = "pydata/sparse (0.15.1) does not implement necessary operations" + + +sparse_params = (pytest.param("COO"), + pytest.param("DOK", marks=[pytest.mark.xfail(reason=msg)])) + +scipy_sparse_classes = [ + sp.bsr_matrix, + sp.csr_matrix, + sp.coo_matrix, + sp.csc_matrix, + sp.dia_matrix, + sp.dok_matrix +] + + +@pytest.fixture(params=sparse_params) +def sparse_cls(request): + return getattr(sparse, request.param) + + +@pytest.fixture(params=scipy_sparse_classes) +def sp_sparse_cls(request): + return request.param + + +@pytest.fixture +def same_matrix(sparse_cls, sp_sparse_cls): + np.random.seed(1234) + A_dense = np.random.rand(9, 9) + return sp_sparse_cls(A_dense), sparse_cls(A_dense) + + +@pytest.fixture +def matrices(sparse_cls): + np.random.seed(1234) + A_dense = np.random.rand(9, 9) + A_dense = A_dense @ A_dense.T + A_sparse = sparse_cls(A_dense) + b = np.random.rand(9) + return A_dense, A_sparse, b + + +def test_isolve_gmres(matrices): + # Several of the iterative solvers use the same + # isolve.utils.make_system wrapper code, so test just one of them. + A_dense, A_sparse, b = matrices + x, info = splin.gmres(A_sparse, b, atol=1e-15) + assert info == 0 + assert isinstance(x, np.ndarray) + assert_allclose(A_sparse @ x, b) + + +def test_lsmr(matrices): + A_dense, A_sparse, b = matrices + res0 = splin.lsmr(A_dense, b) + res = splin.lsmr(A_sparse, b) + assert_allclose(res[0], res0[0], atol=1e-3) + + +# test issue 17012 +def test_lsmr_output_shape(): + x = splin.lsmr(A=np.ones((10, 1)), b=np.zeros(10), x0=np.ones(1))[0] + assert_equal(x.shape, (1,)) + + +def test_lsqr(matrices): + A_dense, A_sparse, b = matrices + res0 = splin.lsqr(A_dense, b) + res = splin.lsqr(A_sparse, b) + assert_allclose(res[0], res0[0], atol=1e-5) + + +def test_eigs(matrices): + A_dense, A_sparse, v0 = matrices + + M_dense = np.diag(v0**2) + M_sparse = A_sparse.__class__(M_dense) + + w_dense, v_dense = splin.eigs(A_dense, k=3, v0=v0) + w, v = splin.eigs(A_sparse, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + for M in [M_sparse, M_dense]: + w_dense, v_dense = splin.eigs(A_dense, M=M_dense, k=3, v0=v0) + w, v = splin.eigs(A_sparse, M=M, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + w_dense, v_dense = splin.eigsh(A_dense, M=M_dense, k=3, v0=v0) + w, v = splin.eigsh(A_sparse, M=M, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + +def test_svds(matrices): + A_dense, A_sparse, v0 = matrices + + u0, s0, vt0 = splin.svds(A_dense, k=2, v0=v0) + u, s, vt = splin.svds(A_sparse, k=2, v0=v0) + + assert_allclose(s, s0) + assert_allclose(np.abs(u), np.abs(u0)) + assert_allclose(np.abs(vt), np.abs(vt0)) + + +def test_lobpcg(matrices): + A_dense, A_sparse, x = matrices + X = x[:,None] + + w_dense, v_dense = splin.lobpcg(A_dense, X) + w, v = splin.lobpcg(A_sparse, X) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + +def test_spsolve(matrices): + A_dense, A_sparse, b = matrices + b2 = np.random.rand(len(b), 3) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b) + x = splin.spsolve(A_sparse, b) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b) + x = splin.spsolve(A_sparse, b, use_umfpack=True) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b2) + x = splin.spsolve(A_sparse, b2) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), + sp.csc_matrix(A_dense)) + x = splin.spsolve(A_sparse, A_sparse) + assert isinstance(x, type(A_sparse)) + assert_allclose(x.todense(), x0.todense()) + + +def test_splu(matrices): + A_dense, A_sparse, b = matrices + n = len(b) + sparse_cls = type(A_sparse) + + lu = splin.splu(A_sparse) + + assert isinstance(lu.L, sparse_cls) + assert isinstance(lu.U, sparse_cls) + + _Pr_scipy = sp.csc_matrix((np.ones(n), (lu.perm_r, np.arange(n)))) + _Pc_scipy = sp.csc_matrix((np.ones(n), (np.arange(n), lu.perm_c))) + Pr = sparse_cls.from_scipy_sparse(_Pr_scipy) + Pc = sparse_cls.from_scipy_sparse(_Pc_scipy) + A2 = Pr.T @ lu.L @ lu.U @ Pc.T + + assert_allclose(A2.todense(), A_sparse.todense()) + + z = lu.solve(A_sparse.todense()) + assert_allclose(z, np.eye(n), atol=1e-10) + + +def test_spilu(matrices): + A_dense, A_sparse, b = matrices + sparse_cls = type(A_sparse) + + lu = splin.spilu(A_sparse) + + assert isinstance(lu.L, sparse_cls) + assert isinstance(lu.U, sparse_cls) + + z = lu.solve(A_sparse.todense()) + assert_allclose(z, np.eye(len(b)), atol=1e-3) + + +def test_spsolve_triangular(matrices): + A_dense, A_sparse, b = matrices + A_sparse = sparse.tril(A_sparse) + + x = splin.spsolve_triangular(A_sparse, b) + assert_allclose(A_sparse @ x, b) + + +def test_onenormest(matrices): + A_dense, A_sparse, b = matrices + est0 = splin.onenormest(A_dense) + est = splin.onenormest(A_sparse) + assert_allclose(est, est0) + + +def test_inv(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.inv(sp.csc_matrix(A_dense)) + x = splin.inv(A_sparse) + assert_allclose(x.todense(), x0.todense()) + + +def test_expm(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.expm(sp.csc_matrix(A_dense)) + x = splin.expm(A_sparse) + assert_allclose(x.todense(), x0.todense()) + + +def test_expm_multiply(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.expm_multiply(A_dense, b) + x = splin.expm_multiply(A_sparse, b) + assert_allclose(x, x0) + + +def test_eq(same_matrix): + sp_sparse, pd_sparse = same_matrix + assert (sp_sparse == pd_sparse).all() + + +def test_ne(same_matrix): + sp_sparse, pd_sparse = same_matrix + assert not (sp_sparse != pd_sparse).any() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py new file mode 100644 index 0000000000000000000000000000000000000000..d9d1c4001af6697233380edf0047409a41847834 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py @@ -0,0 +1,337 @@ +import pytest +import numpy as np +from numpy.testing import assert_array_equal, assert_allclose + +from scipy.sparse import diags, csgraph +from scipy.linalg import eigh + +from scipy.sparse.linalg import LaplacianNd +from scipy.sparse.linalg._special_sparse_arrays import Sakurai +from scipy.sparse.linalg._special_sparse_arrays import MikotaPair + +INT_DTYPES = [np.int8, np.int16, np.int32, np.int64] +REAL_DTYPES = [np.float32, np.float64] +COMPLEX_DTYPES = [np.complex64, np.complex128] +ALLDTYPES = INT_DTYPES + REAL_DTYPES + COMPLEX_DTYPES + + +class TestLaplacianNd: + """ + LaplacianNd tests + """ + + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_1d_specific_shape(self, bc): + lap = LaplacianNd(grid_shape=(6, ), boundary_conditions=bc) + lapa = lap.toarray() + if bc == 'neumann': + a = np.array( + [ + [-1, 1, 0, 0, 0, 0], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [0, 0, 0, 0, 1, -1], + ] + ) + elif bc == 'dirichlet': + a = np.array( + [ + [-2, 1, 0, 0, 0, 0], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [0, 0, 0, 0, 1, -2], + ] + ) + else: + a = np.array( + [ + [-2, 1, 0, 0, 0, 1], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [1, 0, 0, 0, 1, -2], + ] + ) + assert_array_equal(a, lapa) + + def test_1d_with_graph_laplacian(self): + n = 6 + G = diags(np.ones(n - 1), 1, format='dia') + Lf = csgraph.laplacian(G, symmetrized=True, form='function') + La = csgraph.laplacian(G, symmetrized=True, form='array') + grid_shape = (n,) + bc = 'neumann' + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + assert_array_equal(lap(np.eye(n)), -Lf(np.eye(n))) + assert_array_equal(lap.toarray(), -La.toarray()) + # https://github.com/numpy/numpy/issues/24351 + assert_array_equal(lap.tosparse().toarray(), -La.toarray()) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_eigenvalues(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=np.float64) + L = lap.toarray() + eigvals = eigh(L, eigvals_only=True) + n = np.prod(grid_shape) + eigenvalues = lap.eigenvalues() + dtype = eigenvalues.dtype + atol = n * n * np.finfo(dtype).eps + # test the default ``m = None`` + assert_allclose(eigenvalues, eigvals, atol=atol) + # test every ``m > 0`` + for m in np.arange(1, n + 1): + assert_array_equal(lap.eigenvalues(m), eigenvalues[-m:]) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_eigenvectors(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=np.float64) + n = np.prod(grid_shape) + eigenvalues = lap.eigenvalues() + eigenvectors = lap.eigenvectors() + dtype = eigenvectors.dtype + atol = n * n * max(np.finfo(dtype).eps, np.finfo(np.double).eps) + # test the default ``m = None`` every individual eigenvector + for i in np.arange(n): + r = lap.toarray() @ eigenvectors[:, i] - eigenvectors[:, i] * eigenvalues[i] + assert_allclose(r, np.zeros_like(r), atol=atol) + # test every ``m > 0`` + for m in np.arange(1, n + 1): + e = lap.eigenvalues(m) + ev = lap.eigenvectors(m) + r = lap.toarray() @ ev - ev @ np.diag(e) + assert_allclose(r, np.zeros_like(r), atol=atol) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_toarray_tosparse_consistency(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + n = np.prod(grid_shape) + assert_array_equal(lap.toarray(), lap(np.eye(n))) + assert_array_equal(lap.tosparse().toarray(), lap.toarray()) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_linearoperator_shape_dtype(self, grid_shape, bc, dtype): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=dtype) + n = np.prod(grid_shape) + assert lap.shape == (n, n) + assert lap.dtype == dtype + assert_array_equal( + LaplacianNd( + grid_shape, boundary_conditions=bc, dtype=dtype + ).toarray(), + LaplacianNd(grid_shape, boundary_conditions=bc) + .toarray() + .astype(dtype), + ) + assert_array_equal( + LaplacianNd(grid_shape, boundary_conditions=bc, dtype=dtype) + .tosparse() + .toarray(), + LaplacianNd(grid_shape, boundary_conditions=bc) + .tosparse() + .toarray() + .astype(dtype), + ) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_dot(self, grid_shape, bc, dtype): + """ Test the dot-product for type preservation and consistency. + """ + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + n = np.prod(grid_shape) + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + input_set = [x0, x1, x2] + for x in input_set: + y = lap.dot(x.astype(dtype)) + assert x.shape == y.shape + assert y.dtype == dtype + if x.ndim == 2: + yy = lap.toarray() @ x.astype(dtype) + assert yy.dtype == dtype + np.array_equal(y, yy) + + def test_boundary_conditions_value_error(self): + with pytest.raises(ValueError, match="Unknown value 'robin'"): + LaplacianNd(grid_shape=(6, ), boundary_conditions='robin') + + +class TestSakurai: + """ + Sakurai tests + """ + + def test_specific_shape(self): + sak = Sakurai(6) + assert_array_equal(sak.toarray(), sak(np.eye(6))) + a = np.array( + [ + [ 5, -4, 1, 0, 0, 0], + [-4, 6, -4, 1, 0, 0], + [ 1, -4, 6, -4, 1, 0], + [ 0, 1, -4, 6, -4, 1], + [ 0, 0, 1, -4, 6, -4], + [ 0, 0, 0, 1, -4, 5] + ] + ) + + np.array_equal(a, sak.toarray()) + np.array_equal(sak.tosparse().toarray(), sak.toarray()) + ab = np.array( + [ + [ 1, 1, 1, 1, 1, 1], + [-4, -4, -4, -4, -4, -4], + [ 5, 6, 6, 6, 6, 5] + ] + ) + np.array_equal(ab, sak.tobanded()) + e = np.array( + [0.03922866, 0.56703972, 2.41789479, 5.97822974, + 10.54287655, 14.45473055] + ) + np.array_equal(e, sak.eigenvalues()) + np.array_equal(e[:2], sak.eigenvalues(2)) + + # `Sakurai` default `dtype` is `np.int8` as its entries are small integers + @pytest.mark.parametrize('dtype', ALLDTYPES) + def test_linearoperator_shape_dtype(self, dtype): + n = 7 + sak = Sakurai(n, dtype=dtype) + assert sak.shape == (n, n) + assert sak.dtype == dtype + assert_array_equal(sak.toarray(), Sakurai(n).toarray().astype(dtype)) + assert_array_equal(sak.tosparse().toarray(), + Sakurai(n).tosparse().toarray().astype(dtype)) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('argument_dtype', ALLDTYPES) + def test_dot(self, dtype, argument_dtype): + """ Test the dot-product for type preservation and consistency. + """ + result_dtype = np.promote_types(argument_dtype, dtype) + n = 5 + sak = Sakurai(n) + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + input_set = [x0, x1, x2] + for x in input_set: + y = sak.dot(x.astype(argument_dtype)) + assert x.shape == y.shape + assert np.can_cast(y.dtype, result_dtype) + if x.ndim == 2: + ya = sak.toarray() @ x.astype(argument_dtype) + np.array_equal(y, ya) + assert np.can_cast(ya.dtype, result_dtype) + ys = sak.tosparse() @ x.astype(argument_dtype) + np.array_equal(y, ys) + assert np.can_cast(ys.dtype, result_dtype) + +class TestMikotaPair: + """ + MikotaPair tests + """ + # both MikotaPair `LinearOperator`s share the same dtype + # while `MikotaK` `dtype` can be as small as its default `np.int32` + # since its entries are integers, the `MikotaM` involves inverses + # so its smallest still accurate `dtype` is `np.float32` + tested_types = REAL_DTYPES + COMPLEX_DTYPES + + def test_specific_shape(self): + n = 6 + mik = MikotaPair(n) + mik_k = mik.k + mik_m = mik.m + assert_array_equal(mik_k.toarray(), mik_k(np.eye(n))) + assert_array_equal(mik_m.toarray(), mik_m(np.eye(n))) + + k = np.array( + [ + [11, -5, 0, 0, 0, 0], + [-5, 9, -4, 0, 0, 0], + [ 0, -4, 7, -3, 0, 0], + [ 0, 0, -3, 5, -2, 0], + [ 0, 0, 0, -2, 3, -1], + [ 0, 0, 0, 0, -1, 1] + ] + ) + np.array_equal(k, mik_k.toarray()) + np.array_equal(mik_k.tosparse().toarray(), k) + kb = np.array( + [ + [ 0, -5, -4, -3, -2, -1], + [11, 9, 7, 5, 3, 1] + ] + ) + np.array_equal(kb, mik_k.tobanded()) + + minv = np.arange(1, n + 1) + np.array_equal(np.diag(1. / minv), mik_m.toarray()) + np.array_equal(mik_m.tosparse().toarray(), mik_m.toarray()) + np.array_equal(1. / minv, mik_m.tobanded()) + + e = np.array([ 1, 4, 9, 16, 25, 36]) + np.array_equal(e, mik.eigenvalues()) + np.array_equal(e[:2], mik.eigenvalues(2)) + + @pytest.mark.parametrize('dtype', tested_types) + def test_linearoperator_shape_dtype(self, dtype): + n = 7 + mik = MikotaPair(n, dtype=dtype) + mik_k = mik.k + mik_m = mik.m + assert mik_k.shape == (n, n) + assert mik_k.dtype == dtype + assert mik_m.shape == (n, n) + assert mik_m.dtype == dtype + mik_default_dtype = MikotaPair(n) + mikd_k = mik_default_dtype.k + mikd_m = mik_default_dtype.m + assert mikd_k.shape == (n, n) + assert mikd_k.dtype == np.float64 + assert mikd_m.shape == (n, n) + assert mikd_m.dtype == np.float64 + assert_array_equal(mik_k.toarray(), + mikd_k.toarray().astype(dtype)) + assert_array_equal(mik_k.tosparse().toarray(), + mikd_k.tosparse().toarray().astype(dtype)) + + @pytest.mark.parametrize('dtype', tested_types) + @pytest.mark.parametrize('argument_dtype', ALLDTYPES) + def test_dot(self, dtype, argument_dtype): + """ Test the dot-product for type preservation and consistency. + """ + result_dtype = np.promote_types(argument_dtype, dtype) + n = 5 + mik = MikotaPair(n, dtype=dtype) + mik_k = mik.k + mik_m = mik.m + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + lo_set = [mik_k, mik_m] + input_set = [x0, x1, x2] + for lo in lo_set: + for x in input_set: + y = lo.dot(x.astype(argument_dtype)) + assert x.shape == y.shape + assert np.can_cast(y.dtype, result_dtype) + if x.ndim == 2: + ya = lo.toarray() @ x.astype(argument_dtype) + np.array_equal(y, ya) + assert np.can_cast(ya.dtype, result_dtype) + ys = lo.tosparse() @ x.astype(argument_dtype) + np.array_equal(y, ys) + assert np.can_cast(ys.dtype, result_dtype) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sparsetools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sparsetools.py new file mode 100644 index 0000000000000000000000000000000000000000..47ac80adae7145a6192f9fb9b225a1762ff830ce --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sparsetools.py @@ -0,0 +1,98 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'bsr_diagonal', + 'bsr_eldiv_bsr', + 'bsr_elmul_bsr', + 'bsr_ge_bsr', + 'bsr_gt_bsr', + 'bsr_le_bsr', + 'bsr_lt_bsr', + 'bsr_matmat', + 'bsr_matvec', + 'bsr_matvecs', + 'bsr_maximum_bsr', + 'bsr_minimum_bsr', + 'bsr_minus_bsr', + 'bsr_ne_bsr', + 'bsr_plus_bsr', + 'bsr_scale_columns', + 'bsr_scale_rows', + 'bsr_sort_indices', + 'bsr_tocsr', + 'bsr_transpose', + 'coo_matvec', + 'coo_tocsr', + 'coo_todense', + 'cs_graph_components', + 'csc_diagonal', + 'csc_eldiv_csc', + 'csc_elmul_csc', + 'csc_ge_csc', + 'csc_gt_csc', + 'csc_le_csc', + 'csc_lt_csc', + 'csc_matmat', + 'csc_matmat_maxnnz', + 'csc_matvec', + 'csc_matvecs', + 'csc_maximum_csc', + 'csc_minimum_csc', + 'csc_minus_csc', + 'csc_ne_csc', + 'csc_plus_csc', + 'csc_tocsr', + 'csr_column_index1', + 'csr_column_index2', + 'csr_count_blocks', + 'csr_diagonal', + 'csr_eldiv_csr', + 'csr_eliminate_zeros', + 'csr_elmul_csr', + 'csr_ge_csr', + 'csr_gt_csr', + 'csr_has_canonical_format', + 'csr_has_sorted_indices', + 'csr_hstack', + 'csr_le_csr', + 'csr_lt_csr', + 'csr_matmat', + 'csr_matmat_maxnnz', + 'csr_matvec', + 'csr_matvecs', + 'csr_maximum_csr', + 'csr_minimum_csr', + 'csr_minus_csr', + 'csr_ne_csr', + 'csr_plus_csr', + 'csr_row_index', + 'csr_row_slice', + 'csr_sample_offsets', + 'csr_sample_values', + 'csr_scale_columns', + 'csr_scale_rows', + 'csr_sort_indices', + 'csr_sum_duplicates', + 'csr_tobsr', + 'csr_tocsc', + 'csr_todense', + 'dia_matvec', + 'expandptr', + 'get_csr_submatrix', + 'test_throw_error', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="sparsetools", + private_modules=["_sparsetools"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/spfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..b005a9b7c56b82f0c902c26664607b237d808f68 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/spfuncs.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'csr_count_blocks', + 'estimate_blocksize', + 'count_blocks' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="spfuncs", + private_modules=["_spfuncs"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sputils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..bdacb42dd0fb23b956fb95f2eea913cfb933d029 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/sputils.py @@ -0,0 +1,44 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'asmatrix', + 'check_reshape_kwargs', + 'check_shape', + 'downcast_intp_index', + 'get_index_dtype', + 'get_sum_dtype', + 'getdata', + 'getdtype', + 'is_pydata_spmatrix', + 'isdense', + 'isintlike', + 'ismatrix', + 'isscalarlike', + 'issequence', + 'isshape', + 'matrix', + 'operator', + 'prod', + 'supported_dtypes', + 'sys', + 'to_native', + 'upcast', + 'upcast_char', + 'upcast_scalar', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="sputils", + private_modules=["_sputils"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_array_api.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_array_api.py new file mode 100644 index 0000000000000000000000000000000000000000..4385a4e3ab3e50f9076bcbbc539c448131e11c71 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_array_api.py @@ -0,0 +1,561 @@ +import pytest +import numpy as np +import numpy.testing as npt +import scipy.sparse +import scipy.sparse.linalg as spla +from scipy._lib._util import VisibleDeprecationWarning + + +sparray_types = ('bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil') + +sparray_classes = [ + getattr(scipy.sparse, f'{T}_array') for T in sparray_types +] + +A = np.array([ + [0, 1, 2, 0], + [2, 0, 0, 3], + [1, 4, 0, 0] +]) + +B = np.array([ + [0, 1], + [2, 0] +]) + +X = np.array([ + [1, 0, 0, 1], + [2, 1, 2, 0], + [0, 2, 1, 0], + [0, 0, 1, 2] +], dtype=float) + + +sparrays = [sparray(A) for sparray in sparray_classes] +square_sparrays = [sparray(B) for sparray in sparray_classes] +eig_sparrays = [sparray(X) for sparray in sparray_classes] + +parametrize_sparrays = pytest.mark.parametrize( + "A", sparrays, ids=sparray_types +) +parametrize_square_sparrays = pytest.mark.parametrize( + "B", square_sparrays, ids=sparray_types +) +parametrize_eig_sparrays = pytest.mark.parametrize( + "X", eig_sparrays, ids=sparray_types +) + + +@parametrize_sparrays +def test_sum(A): + assert not isinstance(A.sum(axis=0), np.matrix), \ + "Expected array, got matrix" + assert A.sum(axis=0).shape == (4,) + assert A.sum(axis=1).shape == (3,) + + +@parametrize_sparrays +def test_mean(A): + assert not isinstance(A.mean(axis=1), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_min_max(A): + # Some formats don't support min/max operations, so we skip those here. + if hasattr(A, 'min'): + assert not isinstance(A.min(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'max'): + assert not isinstance(A.max(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'argmin'): + assert not isinstance(A.argmin(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'argmax'): + assert not isinstance(A.argmax(axis=1), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_todense(A): + assert not isinstance(A.todense(), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_indexing(A): + if A.__class__.__name__[:3] in ('dia', 'coo', 'bsr'): + return + + with pytest.raises(NotImplementedError): + A[1, :] + + with pytest.raises(NotImplementedError): + A[:, 1] + + with pytest.raises(NotImplementedError): + A[1, [1, 2]] + + with pytest.raises(NotImplementedError): + A[[1, 2], 1] + + assert isinstance(A[[0]], scipy.sparse.sparray), \ + "Expected sparse array, got sparse matrix" + assert isinstance(A[1, [[1, 2]]], scipy.sparse.sparray), \ + "Expected ndarray, got sparse array" + assert isinstance(A[[[1, 2]], 1], scipy.sparse.sparray), \ + "Expected ndarray, got sparse array" + assert isinstance(A[:, [1, 2]], scipy.sparse.sparray), \ + "Expected sparse array, got something else" + + +@parametrize_sparrays +def test_dense_addition(A): + X = np.random.random(A.shape) + assert not isinstance(A + X, np.matrix), "Expected array, got matrix" + + +@parametrize_sparrays +def test_sparse_addition(A): + assert isinstance((A + A), scipy.sparse.sparray), "Expected array, got matrix" + + +@parametrize_sparrays +def test_elementwise_mul(A): + assert np.all((A * A).todense() == A.power(2).todense()) + + +@parametrize_sparrays +def test_elementwise_rmul(A): + with pytest.raises(TypeError): + None * A + + with pytest.raises(ValueError): + np.eye(3) * scipy.sparse.csr_array(np.arange(6).reshape(2, 3)) + + assert np.all((2 * A) == (A.todense() * 2)) + + assert np.all((A.todense() * A) == (A.todense() ** 2)) + + +@parametrize_sparrays +def test_matmul(A): + assert np.all((A @ A.T).todense() == A.dot(A.T).todense()) + + +@parametrize_sparrays +def test_power_operator(A): + assert isinstance((A**2), scipy.sparse.sparray), "Expected array, got matrix" + + # https://github.com/scipy/scipy/issues/15948 + npt.assert_equal((A**2).todense(), (A.todense())**2) + + # power of zero is all ones (dense) so helpful msg exception + with pytest.raises(NotImplementedError, match="zero power"): + A**0 + + +@parametrize_sparrays +def test_sparse_divide(A): + assert isinstance(A / A, np.ndarray) + +@parametrize_sparrays +def test_sparse_dense_divide(A): + with pytest.warns(RuntimeWarning): + assert isinstance((A / A.todense()), scipy.sparse.sparray) + +@parametrize_sparrays +def test_dense_divide(A): + assert isinstance((A / 2), scipy.sparse.sparray), "Expected array, got matrix" + + +@parametrize_sparrays +def test_no_A_attr(A): + with pytest.warns(VisibleDeprecationWarning): + A.A + + +@parametrize_sparrays +def test_no_H_attr(A): + with pytest.warns(VisibleDeprecationWarning): + A.H + + +@parametrize_sparrays +def test_getrow_getcol(A): + assert isinstance(A._getcol(0), scipy.sparse.sparray) + assert isinstance(A._getrow(0), scipy.sparse.sparray) + + +# -- linalg -- + +@parametrize_sparrays +def test_as_linearoperator(A): + L = spla.aslinearoperator(A) + npt.assert_allclose(L * [1, 2, 3, 4], A @ [1, 2, 3, 4]) + + +@parametrize_square_sparrays +def test_inv(B): + if B.__class__.__name__[:3] != 'csc': + return + + C = spla.inv(B) + + assert isinstance(C, scipy.sparse.sparray) + npt.assert_allclose(C.todense(), np.linalg.inv(B.todense())) + + +@parametrize_square_sparrays +def test_expm(B): + if B.__class__.__name__[:3] != 'csc': + return + + Bmat = scipy.sparse.csc_matrix(B) + + C = spla.expm(B) + + assert isinstance(C, scipy.sparse.sparray) + npt.assert_allclose( + C.todense(), + spla.expm(Bmat).todense() + ) + + +@parametrize_square_sparrays +def test_expm_multiply(B): + if B.__class__.__name__[:3] != 'csc': + return + + npt.assert_allclose( + spla.expm_multiply(B, np.array([1, 2])), + spla.expm(B) @ [1, 2] + ) + + +@parametrize_sparrays +def test_norm(A): + C = spla.norm(A) + npt.assert_allclose(C, np.linalg.norm(A.todense())) + + +@parametrize_square_sparrays +def test_onenormest(B): + C = spla.onenormest(B) + npt.assert_allclose(C, np.linalg.norm(B.todense(), 1)) + + +@parametrize_square_sparrays +def test_spsolve(B): + if B.__class__.__name__[:3] not in ('csc', 'csr'): + return + + npt.assert_allclose( + spla.spsolve(B, [1, 2]), + np.linalg.solve(B.todense(), [1, 2]) + ) + + +def test_spsolve_triangular(): + X = scipy.sparse.csr_array([ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ]) + spla.spsolve_triangular(X, [1, 2, 3, 4]) + + +@parametrize_square_sparrays +def test_factorized(B): + if B.__class__.__name__[:3] != 'csc': + return + + LU = spla.factorized(B) + npt.assert_allclose( + LU(np.array([1, 2])), + np.linalg.solve(B.todense(), [1, 2]) + ) + + +@parametrize_square_sparrays +@pytest.mark.parametrize( + "solver", + ["bicg", "bicgstab", "cg", "cgs", "gmres", "lgmres", "minres", "qmr", + "gcrotmk", "tfqmr"] +) +def test_solvers(B, solver): + if solver == "minres": + kwargs = {} + else: + kwargs = {'atol': 1e-5} + + x, info = getattr(spla, solver)(B, np.array([1, 2]), **kwargs) + assert info >= 0 # no errors, even if perhaps did not converge fully + npt.assert_allclose(x, [1, 1], atol=1e-1) + + +@parametrize_sparrays +@pytest.mark.parametrize( + "solver", + ["lsqr", "lsmr"] +) +def test_lstsqr(A, solver): + x, *_ = getattr(spla, solver)(A, [1, 2, 3]) + npt.assert_allclose(A @ x, [1, 2, 3]) + + +@parametrize_eig_sparrays +def test_eigs(X): + e, v = spla.eigs(X, k=1) + npt.assert_allclose( + X @ v, + e[0] * v + ) + + +@parametrize_eig_sparrays +def test_eigsh(X): + X = X + X.T + e, v = spla.eigsh(X, k=1) + npt.assert_allclose( + X @ v, + e[0] * v + ) + + +@parametrize_eig_sparrays +def test_svds(X): + u, s, vh = spla.svds(X, k=3) + u2, s2, vh2 = np.linalg.svd(X.todense()) + s = np.sort(s) + s2 = np.sort(s2[:3]) + npt.assert_allclose(s, s2, atol=1e-3) + + +def test_splu(): + X = scipy.sparse.csc_array([ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ]) + LU = spla.splu(X) + npt.assert_allclose( + LU.solve(np.array([1, 2, 3, 4])), + np.asarray([1, 0, 0, 0], dtype=np.float64), + rtol=1e-14, atol=3e-16 + ) + + +def test_spilu(): + X = scipy.sparse.csc_array([ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ]) + LU = spla.spilu(X) + npt.assert_allclose( + LU.solve(np.array([1, 2, 3, 4])), + np.asarray([1, 0, 0, 0], dtype=np.float64), + rtol=1e-14, atol=3e-16 + ) + + +@pytest.mark.parametrize( + "cls,indices_attrs", + [ + ( + scipy.sparse.csr_array, + ["indices", "indptr"], + ), + ( + scipy.sparse.csc_array, + ["indices", "indptr"], + ), + ( + scipy.sparse.coo_array, + ["row", "col"], + ), + ] +) +@pytest.mark.parametrize("expected_dtype", [np.int64, np.int32]) +def test_index_dtype_compressed(cls, indices_attrs, expected_dtype): + input_array = scipy.sparse.coo_array(np.arange(9).reshape(3, 3)) + coo_tuple = ( + input_array.data, + ( + input_array.row.astype(expected_dtype), + input_array.col.astype(expected_dtype), + ) + ) + + result = cls(coo_tuple) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + result = cls(coo_tuple, shape=(3, 3)) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + if issubclass(cls, scipy.sparse._compressed._cs_matrix): + input_array_csr = input_array.tocsr() + csr_tuple = ( + input_array_csr.data, + input_array_csr.indices.astype(expected_dtype), + input_array_csr.indptr.astype(expected_dtype), + ) + + result = cls(csr_tuple) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + result = cls(csr_tuple, shape=(3, 3)) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + +def test_default_is_matrix_diags(): + m = scipy.sparse.diags([0, 1, 2]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_eye(): + m = scipy.sparse.eye(3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_spdiags(): + m = scipy.sparse.spdiags([1, 2, 3], 0, 3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_identity(): + m = scipy.sparse.identity(3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kron_dense(): + m = scipy.sparse.kron( + np.array([[1, 2], [3, 4]]), np.array([[4, 3], [2, 1]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kron_sparse(): + m = scipy.sparse.kron( + np.array([[1, 2], [3, 4]]), np.array([[1, 0], [0, 0]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kronsum(): + m = scipy.sparse.kronsum( + np.array([[1, 0], [0, 1]]), np.array([[0, 1], [1, 0]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_random(): + m = scipy.sparse.random(3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_rand(): + m = scipy.sparse.rand(3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +@pytest.mark.parametrize("fn", (scipy.sparse.hstack, scipy.sparse.vstack)) +def test_default_is_matrix_stacks(fn): + """Same idea as `test_default_construction_fn_matrices`, but for the + stacking creation functions.""" + A = scipy.sparse.coo_matrix(np.eye(2)) + B = scipy.sparse.coo_matrix([[0, 1], [1, 0]]) + m = fn([A, B]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_blocks_default_construction_fn_matrices(): + """Same idea as `test_default_construction_fn_matrices`, but for the block + creation function""" + A = scipy.sparse.coo_matrix(np.eye(2)) + B = scipy.sparse.coo_matrix([[2], [0]]) + C = scipy.sparse.coo_matrix([[3]]) + + # block diag + m = scipy.sparse.block_diag((A, B, C)) + assert not isinstance(m, scipy.sparse.sparray) + + # bmat + m = scipy.sparse.bmat([[A, None], [None, C]]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_format_property(): + for fmt in sparray_types: + arr_cls = getattr(scipy.sparse, f"{fmt}_array") + M = arr_cls([[1, 2]]) + assert M.format == fmt + assert M._format == fmt + with pytest.raises(AttributeError): + M.format = "qqq" + + +def test_issparse(): + m = scipy.sparse.eye(3) + a = scipy.sparse.csr_array(m) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Both sparse arrays and sparse matrices should be sparse + assert scipy.sparse.issparse(a) + assert scipy.sparse.issparse(m) + + # ndarray and array_likes are not sparse + assert not scipy.sparse.issparse(a.todense()) + assert not scipy.sparse.issparse(m.todense()) + + +def test_isspmatrix(): + m = scipy.sparse.eye(3) + a = scipy.sparse.csr_array(m) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Should only be true for sparse matrices, not sparse arrays + assert not scipy.sparse.isspmatrix(a) + assert scipy.sparse.isspmatrix(m) + + # ndarray and array_likes are not sparse + assert not scipy.sparse.isspmatrix(a.todense()) + assert not scipy.sparse.isspmatrix(m.todense()) + + +@pytest.mark.parametrize( + ("fmt", "fn"), + ( + ("bsr", scipy.sparse.isspmatrix_bsr), + ("coo", scipy.sparse.isspmatrix_coo), + ("csc", scipy.sparse.isspmatrix_csc), + ("csr", scipy.sparse.isspmatrix_csr), + ("dia", scipy.sparse.isspmatrix_dia), + ("dok", scipy.sparse.isspmatrix_dok), + ("lil", scipy.sparse.isspmatrix_lil), + ), +) +def test_isspmatrix_format(fmt, fn): + m = scipy.sparse.eye(3, format=fmt) + a = scipy.sparse.csr_array(m).asformat(fmt) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Should only be true for sparse matrices, not sparse arrays + assert not fn(a) + assert fn(m) + + # ndarray and array_likes are not sparse + assert not fn(a.todense()) + assert not fn(m.todense()) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_base.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_base.py new file mode 100644 index 0000000000000000000000000000000000000000..eb5efa9e10403b5a97b386ef5dead54956e560bb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_base.py @@ -0,0 +1,5235 @@ +# +# Authors: Travis Oliphant, Ed Schofield, Robert Cimrman, Nathan Bell, and others + +""" Test functions for sparse matrices. Each class in the "Matrix class +based tests" section become subclasses of the classes in the "Generic +tests" section. This is done by the functions in the "Tailored base +class for generic tests" section. + +""" + + +import contextlib +import functools +import operator +import platform +import itertools +import sys +from scipy._lib import _pep440 + +import numpy as np +from numpy import (arange, zeros, array, dot, asarray, + vstack, ndarray, transpose, diag, kron, inf, conjugate, + int8) + +import random +from numpy.testing import (assert_equal, assert_array_equal, + assert_array_almost_equal, assert_almost_equal, assert_, + assert_allclose,suppress_warnings) +from pytest import raises as assert_raises + +import scipy.linalg + +import scipy.sparse as sparse +from scipy.sparse import (csc_matrix, csr_matrix, dok_matrix, + coo_matrix, lil_matrix, dia_matrix, bsr_matrix, + eye, issparse, SparseEfficiencyWarning, sparray) +from scipy.sparse._sputils import (supported_dtypes, isscalarlike, + get_index_dtype, asmatrix, matrix) +from scipy.sparse.linalg import splu, expm, inv + +from scipy._lib.decorator import decorator +from scipy._lib._util import ComplexWarning + +import pytest + + +IS_COLAB = ('google.colab' in sys.modules) + + +def assert_in(member, collection, msg=None): + message = msg if msg is not None else f"{member!r} not found in {collection!r}" + assert_(member in collection, msg=message) + + +def assert_array_equal_dtype(x, y, **kwargs): + assert_(x.dtype == y.dtype) + assert_array_equal(x, y, **kwargs) + + +NON_ARRAY_BACKED_FORMATS = frozenset(['dok']) + +def sparse_may_share_memory(A, B): + # Checks if A and B have any numpy array sharing memory. + + def _underlying_arrays(x): + # Given any object (e.g. a sparse array), returns all numpy arrays + # stored in any attribute. + + arrays = [] + for a in x.__dict__.values(): + if isinstance(a, (np.ndarray, np.generic)): + arrays.append(a) + return arrays + + for a in _underlying_arrays(A): + for b in _underlying_arrays(B): + if np.may_share_memory(a, b): + return True + return False + + +sup_complex = suppress_warnings() +sup_complex.filter(ComplexWarning) + + +def with_64bit_maxval_limit(maxval_limit=None, random=False, fixed_dtype=None, + downcast_maxval=None, assert_32bit=False): + """ + Monkeypatch the maxval threshold at which scipy.sparse switches to + 64-bit index arrays, or make it (pseudo-)random. + + """ + if maxval_limit is None: + maxval_limit = np.int64(10) + else: + # Ensure we use numpy scalars rather than Python scalars (matters for + # NEP 50 casting rule changes) + maxval_limit = np.int64(maxval_limit) + + if assert_32bit: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + tp = get_index_dtype(arrays, maxval, check_contents) + assert_equal(np.iinfo(tp).max, np.iinfo(np.int32).max) + assert_(tp == np.int32 or tp == np.intc) + return tp + elif fixed_dtype is not None: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + return fixed_dtype + elif random: + counter = np.random.RandomState(seed=1234) + + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + return (np.int32, np.int64)[counter.randint(2)] + else: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + dtype = np.int32 + if maxval is not None: + if maxval > maxval_limit: + dtype = np.int64 + for arr in arrays: + arr = np.asarray(arr) + if arr.dtype > np.int32: + if check_contents: + if arr.size == 0: + # a bigger type not needed + continue + elif np.issubdtype(arr.dtype, np.integer): + maxval = arr.max() + minval = arr.min() + if minval >= -maxval_limit and maxval <= maxval_limit: + # a bigger type not needed + continue + dtype = np.int64 + return dtype + + if downcast_maxval is not None: + def new_downcast_intp_index(arr): + if arr.max() > downcast_maxval: + raise AssertionError("downcast limited") + return arr.astype(np.intp) + + @decorator + def deco(func, *a, **kw): + backup = [] + modules = [scipy.sparse._bsr, scipy.sparse._coo, scipy.sparse._csc, + scipy.sparse._csr, scipy.sparse._dia, scipy.sparse._dok, + scipy.sparse._lil, scipy.sparse._sputils, + scipy.sparse._compressed, scipy.sparse._construct] + try: + for mod in modules: + backup.append((mod, 'get_index_dtype', + getattr(mod, 'get_index_dtype', None))) + setattr(mod, 'get_index_dtype', new_get_index_dtype) + if downcast_maxval is not None: + backup.append((mod, 'downcast_intp_index', + getattr(mod, 'downcast_intp_index', None))) + setattr(mod, 'downcast_intp_index', new_downcast_intp_index) + return func(*a, **kw) + finally: + for mod, name, oldfunc in backup: + if oldfunc is not None: + setattr(mod, name, oldfunc) + + return deco + + +def toarray(a): + if isinstance(a, np.ndarray) or isscalarlike(a): + return a + return a.toarray() + + +class BinopTester: + # Custom type to test binary operations on sparse matrices. + + def __add__(self, mat): + return "matrix on the right" + + def __mul__(self, mat): + return "matrix on the right" + + def __sub__(self, mat): + return "matrix on the right" + + def __radd__(self, mat): + return "matrix on the left" + + def __rmul__(self, mat): + return "matrix on the left" + + def __rsub__(self, mat): + return "matrix on the left" + + def __matmul__(self, mat): + return "matrix on the right" + + def __rmatmul__(self, mat): + return "matrix on the left" + +class BinopTester_with_shape: + # Custom type to test binary operations on sparse matrices + # with object which has shape attribute. + def __init__(self,shape): + self._shape = shape + + def shape(self): + return self._shape + + def ndim(self): + return len(self._shape) + + def __add__(self, mat): + return "matrix on the right" + + def __mul__(self, mat): + return "matrix on the right" + + def __sub__(self, mat): + return "matrix on the right" + + def __radd__(self, mat): + return "matrix on the left" + + def __rmul__(self, mat): + return "matrix on the left" + + def __rsub__(self, mat): + return "matrix on the left" + + def __matmul__(self, mat): + return "matrix on the right" + + def __rmatmul__(self, mat): + return "matrix on the left" + +class ComparisonTester: + # Custom type to test comparison operations on sparse matrices. + def __eq__(self, other): + return "eq" + + def __ne__(self, other): + return "ne" + + def __lt__(self, other): + return "lt" + + def __le__(self, other): + return "le" + + def __gt__(self, other): + return "gt" + + def __ge__(self, other): + return "ge" + + +#------------------------------------------------------------------------------ +# Generic tests +#------------------------------------------------------------------------------ + + +# TODO test prune +# TODO test has_sorted_indices +class _TestCommon: + """test common functionality shared by all sparse formats""" + math_dtypes = supported_dtypes + + @classmethod + def init_class(cls): + # Canonical data. + cls.dat = array([[1, 0, 0, 2], [3, 0, 1, 0], [0, 2, 0, 0]], 'd') + cls.datsp = cls.spcreator(cls.dat) + + # Some sparse and dense matrices with data for every supported dtype. + # This set union is a workaround for numpy#6295, which means that + # two np.int64 dtypes don't hash to the same value. + cls.checked_dtypes = set(supported_dtypes).union(cls.math_dtypes) + cls.dat_dtypes = {} + cls.datsp_dtypes = {} + for dtype in cls.checked_dtypes: + cls.dat_dtypes[dtype] = cls.dat.astype(dtype) + cls.datsp_dtypes[dtype] = cls.spcreator(cls.dat.astype(dtype)) + + # Check that the original data is equivalent to the + # corresponding dat_dtypes & datsp_dtypes. + assert_equal(cls.dat, cls.dat_dtypes[np.float64]) + assert_equal(cls.datsp.toarray(), + cls.datsp_dtypes[np.float64].toarray()) + + def test_bool(self): + def check(dtype): + datsp = self.datsp_dtypes[dtype] + + assert_raises(ValueError, bool, datsp) + assert_(self.spcreator([[1]])) + assert_(not self.spcreator([[0]])) + + if isinstance(self, TestDOK): + pytest.skip("Cannot create a rank <= 2 DOK matrix.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_bool_rollover(self): + # bool's underlying dtype is 1 byte, check that it does not + # rollover True -> False at 256. + dat = array([[True, False]]) + datsp = self.spcreator(dat) + + for _ in range(10): + datsp = datsp + datsp + dat = dat + dat + assert_array_equal(dat, datsp.toarray()) + + def test_eq(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datbsr = bsr_matrix(dat) + datcsr = csr_matrix(dat) + datcsc = csc_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat == dat2, (datsp == datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat == dat2, (datbsr == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datcsr == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datcsc == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datlil == datsp2).toarray()) + # sparse/dense + assert_array_equal_dtype(dat == datsp2, datsp2 == dat) + # sparse/scalar + assert_array_equal_dtype(dat == 0, (datsp == 0).toarray()) + assert_array_equal_dtype(dat == 1, (datsp == 1).toarray()) + assert_array_equal_dtype(dat == np.nan, + (datsp == np.nan).toarray()) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_ne(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat != dat2, (datsp != datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat != dat2, (datbsr != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datcsc != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datcsr != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datlil != datsp2).toarray()) + # sparse/dense + assert_array_equal_dtype(dat != datsp2, datsp2 != dat) + # sparse/scalar + assert_array_equal_dtype(dat != 0, (datsp != 0).toarray()) + assert_array_equal_dtype(dat != 1, (datsp != 1).toarray()) + assert_array_equal_dtype(0 != dat, (0 != datsp).toarray()) + assert_array_equal_dtype(1 != dat, (1 != datsp).toarray()) + assert_array_equal_dtype(dat != np.nan, + (datsp != np.nan).toarray()) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_lt(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat < dat2, (datsp < datsp2).toarray()) + assert_array_equal_dtype(datcomplex < dat2, + (datspcomplex < datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat < dat2, (datbsr < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datcsc < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datcsr < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datlil < datsp2).toarray()) + + assert_array_equal_dtype(dat2 < dat, (datsp2 < datbsr).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datcsc).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datcsr).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datlil).toarray()) + # sparse/dense + assert_array_equal_dtype(dat < dat2, datsp < dat2) + assert_array_equal_dtype(datcomplex < dat2, datspcomplex < dat2) + # sparse/scalar + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp < val).toarray(), dat < val) + assert_array_equal_dtype((val < datsp).toarray(), val < dat) + + with np.errstate(invalid='ignore'): + assert_array_equal_dtype((datsp < np.nan).toarray(), + dat < np.nan) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat < datsp2, datsp < dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_gt(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat > dat2, (datsp > datsp2).toarray()) + assert_array_equal_dtype(datcomplex > dat2, + (datspcomplex > datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat > dat2, (datbsr > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datcsc > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datcsr > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datlil > datsp2).toarray()) + + assert_array_equal_dtype(dat2 > dat, (datsp2 > datbsr).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datcsc).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datcsr).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datlil).toarray()) + # sparse/dense + assert_array_equal_dtype(dat > dat2, datsp > dat2) + assert_array_equal_dtype(datcomplex > dat2, datspcomplex > dat2) + # sparse/scalar + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp > val).toarray(), dat > val) + assert_array_equal_dtype((val > datsp).toarray(), val > dat) + + with np.errstate(invalid='ignore'): + assert_array_equal_dtype((datsp > np.nan).toarray(), + dat > np.nan) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat > datsp2, datsp > dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_le(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat <= dat2, (datsp <= datsp2).toarray()) + assert_array_equal_dtype(datcomplex <= dat2, + (datspcomplex <= datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype((datbsr <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datcsc <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datcsr <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datlil <= datsp2).toarray(), dat <= dat2) + + assert_array_equal_dtype((datsp2 <= datbsr).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datcsc).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datcsr).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datlil).toarray(), dat2 <= dat) + # sparse/dense + assert_array_equal_dtype(datsp <= dat2, dat <= dat2) + assert_array_equal_dtype(datspcomplex <= dat2, datcomplex <= dat2) + # sparse/scalar + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp <= val).toarray(), dat <= val) + assert_array_equal_dtype((val <= datsp).toarray(), val <= dat) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat <= datsp2, datsp <= dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_ge(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat >= dat2, (datsp >= datsp2).toarray()) + assert_array_equal_dtype(datcomplex >= dat2, + (datspcomplex >= datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype((datbsr >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datcsc >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datcsr >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datlil >= datsp2).toarray(), dat >= dat2) + + assert_array_equal_dtype((datsp2 >= datbsr).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datcsc).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datcsr).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datlil).toarray(), dat2 >= dat) + # sparse/dense + assert_array_equal_dtype(datsp >= dat2, dat >= dat2) + assert_array_equal_dtype(datspcomplex >= dat2, datcomplex >= dat2) + # sparse/scalar + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp >= val).toarray(), dat >= val) + assert_array_equal_dtype((val >= datsp).toarray(), val >= dat) + + # dense data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat >= datsp2, datsp >= dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_empty(self): + # create empty matrices + assert_equal(self.spcreator((3, 3)).toarray(), zeros((3, 3))) + assert_equal(self.spcreator((3, 3)).nnz, 0) + assert_equal(self.spcreator((3, 3)).count_nonzero(), 0) + + def test_count_nonzero(self): + expected = np.count_nonzero(self.datsp.toarray()) + assert_equal(self.datsp.count_nonzero(), expected) + assert_equal(self.datsp.T.count_nonzero(), expected) + + def test_invalid_shapes(self): + assert_raises(ValueError, self.spcreator, (-1,3)) + assert_raises(ValueError, self.spcreator, (3,-1)) + assert_raises(ValueError, self.spcreator, (-1,-1)) + + def test_repr(self): + repr(self.datsp) + + def test_str(self): + str(self.datsp) + + def test_empty_arithmetic(self): + # Test manipulating empty matrices. Fails in SciPy SVN <= r1768 + shape = (5, 5) + for mytype in [np.dtype('int32'), np.dtype('float32'), + np.dtype('float64'), np.dtype('complex64'), + np.dtype('complex128')]: + a = self.spcreator(shape, dtype=mytype) + b = a + a + c = 2 * a + d = a @ a.tocsc() + e = a @ a.tocsr() + f = a @ a.tocoo() + for m in [a,b,c,d,e,f]: + assert_equal(m.toarray(), a.toarray()@a.toarray()) + # These fail in all revisions <= r1768: + assert_equal(m.dtype,mytype) + assert_equal(m.toarray().dtype,mytype) + + def test_abs(self): + A = array([[-1, 0, 17], [0, -5, 0], [1, -4, 0], [0, 0, 0]], 'd') + assert_equal(abs(A), abs(self.spcreator(A)).toarray()) + + def test_round(self): + decimal = 1 + A = array([[-1.35, 0.56], [17.25, -5.98]], 'd') + assert_equal(np.around(A, decimals=decimal), + round(self.spcreator(A), ndigits=decimal).toarray()) + + def test_elementwise_power(self): + A = array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]], 'd') + assert_equal(np.power(A, 2), self.spcreator(A).power(2).toarray()) + + #it's element-wise power function, input has to be a scalar + assert_raises(NotImplementedError, self.spcreator(A).power, A) + + def test_neg(self): + A = array([[-1, 0, 17], [0, -5, 0], [1, -4, 0], [0, 0, 0]], 'd') + assert_equal(-A, (-self.spcreator(A)).toarray()) + + # see gh-5843 + A = array([[True, False, False], [False, False, True]]) + assert_raises(NotImplementedError, self.spcreator(A).__neg__) + + def test_real(self): + D = array([[1 + 3j, 2 - 4j]]) + A = self.spcreator(D) + assert_equal(A.real.toarray(), D.real) + + def test_imag(self): + D = array([[1 + 3j, 2 - 4j]]) + A = self.spcreator(D) + assert_equal(A.imag.toarray(), D.imag) + + def test_diagonal(self): + # Does the matrix's .diagonal() method work? + mats = [] + mats.append([[1,0,2]]) + mats.append([[1],[0],[2]]) + mats.append([[0,1],[0,2],[0,3]]) + mats.append([[0,0,1],[0,0,2],[0,3,0]]) + mats.append([[1,0],[0,0]]) + + mats.append(kron(mats[0],[[1,2]])) + mats.append(kron(mats[0],[[1],[2]])) + mats.append(kron(mats[1],[[1,2],[3,4]])) + mats.append(kron(mats[2],[[1,2],[3,4]])) + mats.append(kron(mats[3],[[1,2],[3,4]])) + mats.append(kron(mats[3],[[1,2,3,4]])) + + for m in mats: + rows, cols = array(m).shape + sparse_mat = self.spcreator(m) + for k in range(-rows-1, cols+2): + assert_equal(sparse_mat.diagonal(k=k), diag(m, k=k)) + # Test for k beyond boundaries(issue #11949) + assert_equal(sparse_mat.diagonal(k=10), diag(m, k=10)) + assert_equal(sparse_mat.diagonal(k=-99), diag(m, k=-99)) + + # Test all-zero matrix. + assert_equal(self.spcreator((40, 16130)).diagonal(), np.zeros(40)) + # Test empty matrix + # https://github.com/scipy/scipy/issues/11949 + assert_equal(self.spcreator((0, 0)).diagonal(), np.empty(0)) + assert_equal(self.spcreator((15, 0)).diagonal(), np.empty(0)) + assert_equal(self.spcreator((0, 5)).diagonal(10), np.empty(0)) + + def test_trace(self): + # For square matrix + A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + B = self.spcreator(A) + for k in range(-2, 3): + assert_equal(A.trace(offset=k), B.trace(offset=k)) + + # For rectangular matrix + A = np.array([[1, 2, 3], [4, 5, 6]]) + B = self.spcreator(A) + for k in range(-1, 3): + assert_equal(A.trace(offset=k), B.trace(offset=k)) + + def test_reshape(self): + # This first example is taken from the lil_matrix reshaping test. + x = self.spcreator([[1, 0, 7], [0, 0, 0], [0, 3, 0], [0, 0, 5]]) + for order in ['C', 'F']: + for s in [(12, 1), (1, 12)]: + assert_array_equal(x.reshape(s, order=order).toarray(), + x.toarray().reshape(s, order=order)) + + # This example is taken from the stackoverflow answer at + # https://stackoverflow.com/q/16511879 + x = self.spcreator([[0, 10, 0, 0], [0, 0, 0, 0], [0, 20, 30, 40]]) + y = x.reshape((2, 6)) # Default order is 'C' + desired = [[0, 10, 0, 0, 0, 0], [0, 0, 0, 20, 30, 40]] + assert_array_equal(y.toarray(), desired) + + # Reshape with negative indexes + y = x.reshape((2, -1)) + assert_array_equal(y.toarray(), desired) + y = x.reshape((-1, 6)) + assert_array_equal(y.toarray(), desired) + assert_raises(ValueError, x.reshape, (-1, -1)) + + # Reshape with star args + y = x.reshape(2, 6) + assert_array_equal(y.toarray(), desired) + assert_raises(TypeError, x.reshape, 2, 6, not_an_arg=1) + + # Reshape with same size is noop unless copy=True + y = x.reshape((3, 4)) + assert_(y is x) + y = x.reshape((3, 4), copy=True) + assert_(y is not x) + + # Ensure reshape did not alter original size + assert_array_equal(x.shape, (3, 4)) + + # Reshape in place + x.shape = (2, 6) + assert_array_equal(x.toarray(), desired) + + # Reshape to bad ndim + assert_raises(ValueError, x.reshape, (x.size,)) + assert_raises(ValueError, x.reshape, (1, x.size, 1)) + + @pytest.mark.slow + def test_setdiag_comprehensive(self): + def dense_setdiag(a, v, k): + v = np.asarray(v) + if k >= 0: + n = min(a.shape[0], a.shape[1] - k) + if v.ndim != 0: + n = min(n, len(v)) + v = v[:n] + i = np.arange(0, n) + j = np.arange(k, k + n) + a[i,j] = v + elif k < 0: + dense_setdiag(a.T, v, -k) + + def check_setdiag(a, b, k): + # Check setting diagonal using a scalar, a vector of + # correct length, and too short or too long vectors + for r in [-1, len(np.diag(a, k)), 2, 30]: + if r < 0: + v = np.random.choice(range(1, 20)) + else: + v = np.random.randint(1, 20, size=r) + + dense_setdiag(a, v, k) + with suppress_warnings() as sup: + message = ("Changing the sparsity structure of " + "a cs[cr]_matrix is expensive") + sup.filter(SparseEfficiencyWarning, message) + b.setdiag(v, k) + + # check that dense_setdiag worked + d = np.diag(a, k) + if np.asarray(v).ndim == 0: + assert_array_equal(d, v, err_msg="%s %d" % (msg, r)) + else: + n = min(len(d), len(v)) + assert_array_equal(d[:n], v[:n], err_msg="%s %d" % (msg, r)) + # check that sparse setdiag worked + assert_array_equal(b.toarray(), a, err_msg="%s %d" % (msg, r)) + + # comprehensive test + np.random.seed(1234) + shapes = [(0,5), (5,0), (1,5), (5,1), (5,5)] + for dtype in [np.int8, np.float64]: + for m,n in shapes: + ks = np.arange(-m+1, n-1) + for k in ks: + msg = repr((dtype, m, n, k)) + a = np.zeros((m, n), dtype=dtype) + b = self.spcreator((m, n), dtype=dtype) + + check_setdiag(a, b, k) + + # check overwriting etc + for k2 in np.random.choice(ks, size=min(len(ks), 5)): + check_setdiag(a, b, k2) + + def test_setdiag(self): + # simple test cases + m = self.spcreator(np.eye(3)) + m2 = self.spcreator((4, 4)) + values = [3, 2, 1] + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive", + ) + assert_raises(ValueError, m.setdiag, values, k=4) + m.setdiag(values) + assert_array_equal(m.diagonal(), values) + m.setdiag(values, k=1) + assert_array_equal(m.toarray(), np.array([[3, 3, 0], + [0, 2, 2], + [0, 0, 1]])) + m.setdiag(values, k=-2) + assert_array_equal(m.toarray(), np.array([[3, 3, 0], + [0, 2, 2], + [3, 0, 1]])) + m.setdiag((9,), k=2) + assert_array_equal(m.toarray()[0,2], 9) + m.setdiag((9,), k=-2) + assert_array_equal(m.toarray()[2,0], 9) + # test short values on an empty matrix + m2.setdiag([1], k=2) + assert_array_equal(m2.toarray()[0], [0, 0, 1, 0]) + # test overwriting that same diagonal + m2.setdiag([1, 1], k=2) + assert_array_equal(m2.toarray()[:2], [[0, 0, 1, 0], + [0, 0, 0, 1]]) + + def test_nonzero(self): + A = array([[1, 0, 1],[0, 1, 1],[0, 0, 1]]) + Asp = self.spcreator(A) + + A_nz = {tuple(ij) for ij in transpose(A.nonzero())} + Asp_nz = {tuple(ij) for ij in transpose(Asp.nonzero())} + + assert_equal(A_nz, Asp_nz) + + def test_numpy_nonzero(self): + # See gh-5987 + A = array([[1, 0, 1], [0, 1, 1], [0, 0, 1]]) + Asp = self.spcreator(A) + + A_nz = {tuple(ij) for ij in transpose(np.nonzero(A))} + Asp_nz = {tuple(ij) for ij in transpose(np.nonzero(Asp))} + + assert_equal(A_nz, Asp_nz) + + def test_getrow(self): + assert_array_equal(self.datsp.getrow(1).toarray(), self.dat[[1], :]) + assert_array_equal(self.datsp.getrow(-1).toarray(), self.dat[[-1], :]) + + def test_getcol(self): + assert_array_equal(self.datsp.getcol(1).toarray(), self.dat[:, [1]]) + assert_array_equal(self.datsp.getcol(-1).toarray(), self.dat[:, [-1]]) + + def test_sum(self): + np.random.seed(1234) + dat_1 = matrix([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + dat_2 = np.random.rand(5, 5) + dat_3 = np.array([[]]) + dat_4 = np.zeros((40, 40)) + dat_5 = sparse.rand(5, 5, density=1e-2).toarray() + matrices = [dat_1, dat_2, dat_3, dat_4, dat_5] + + def check(dtype, j): + dat = matrix(matrices[j], dtype=dtype) + datsp = self.spcreator(dat, dtype=dtype) + with np.errstate(over='ignore'): + assert_array_almost_equal(dat.sum(), datsp.sum()) + assert_equal(dat.sum().dtype, datsp.sum().dtype) + assert_(np.isscalar(datsp.sum(axis=None))) + assert_array_almost_equal(dat.sum(axis=None), + datsp.sum(axis=None)) + assert_equal(dat.sum(axis=None).dtype, + datsp.sum(axis=None).dtype) + assert_array_almost_equal(dat.sum(axis=0), datsp.sum(axis=0)) + assert_equal(dat.sum(axis=0).dtype, datsp.sum(axis=0).dtype) + assert_array_almost_equal(dat.sum(axis=1), datsp.sum(axis=1)) + assert_equal(dat.sum(axis=1).dtype, datsp.sum(axis=1).dtype) + assert_array_almost_equal(dat.sum(axis=-2), datsp.sum(axis=-2)) + assert_equal(dat.sum(axis=-2).dtype, datsp.sum(axis=-2).dtype) + assert_array_almost_equal(dat.sum(axis=-1), datsp.sum(axis=-1)) + assert_equal(dat.sum(axis=-1).dtype, datsp.sum(axis=-1).dtype) + + for dtype in self.checked_dtypes: + for j in range(len(matrices)): + check(dtype, j) + + def test_sum_invalid_params(self): + out = np.zeros((1, 3)) + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + assert_raises(ValueError, datsp.sum, axis=3) + assert_raises(TypeError, datsp.sum, axis=(0, 1)) + assert_raises(TypeError, datsp.sum, axis=1.5) + assert_raises(ValueError, datsp.sum, axis=1, out=out) + + def test_sum_dtype(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + def check(dtype): + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_sum_out(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_out = array([[0]]) + datsp_out = matrix([[0]]) + + dat.sum(out=dat_out, keepdims=True) + datsp.sum(out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + dat_out = np.zeros((3, 1)) + datsp_out = asmatrix(np.zeros((3, 1))) + + dat.sum(axis=1, out=dat_out, keepdims=True) + datsp.sum(axis=1, out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + def test_numpy_sum(self): + # See gh-5987 + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_mean = np.sum(dat) + datsp_mean = np.sum(datsp) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_mean(self): + def check(dtype): + dat = array([[0, 1, 2], + [3, 4, 5], + [6, 7, 9]], dtype=dtype) + datsp = self.spcreator(dat, dtype=dtype) + + assert_array_almost_equal(dat.mean(), datsp.mean()) + assert_equal(dat.mean().dtype, datsp.mean().dtype) + assert_(np.isscalar(datsp.mean(axis=None))) + assert_array_almost_equal( + dat.mean(axis=None, keepdims=True), datsp.mean(axis=None) + ) + assert_equal(dat.mean(axis=None).dtype, datsp.mean(axis=None).dtype) + assert_array_almost_equal( + dat.mean(axis=0, keepdims=True), datsp.mean(axis=0) + ) + assert_equal(dat.mean(axis=0).dtype, datsp.mean(axis=0).dtype) + assert_array_almost_equal( + dat.mean(axis=1, keepdims=True), datsp.mean(axis=1) + ) + assert_equal(dat.mean(axis=1).dtype, datsp.mean(axis=1).dtype) + assert_array_almost_equal( + dat.mean(axis=-2, keepdims=True), datsp.mean(axis=-2) + ) + assert_equal(dat.mean(axis=-2).dtype, datsp.mean(axis=-2).dtype) + assert_array_almost_equal( + dat.mean(axis=-1, keepdims=True), datsp.mean(axis=-1) + ) + assert_equal(dat.mean(axis=-1).dtype, datsp.mean(axis=-1).dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_mean_invalid_params(self): + out = asmatrix(np.zeros((1, 3))) + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + assert_raises(ValueError, datsp.mean, axis=3) + assert_raises(TypeError, datsp.mean, axis=(0, 1)) + assert_raises(TypeError, datsp.mean, axis=1.5) + assert_raises(ValueError, datsp.mean, axis=1, out=out) + + def test_mean_dtype(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + def check(dtype): + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_mean_out(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_out = array([[0]]) + datsp_out = matrix([[0]]) + + dat.mean(out=dat_out, keepdims=True) + datsp.mean(out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + dat_out = np.zeros((3, 1)) + datsp_out = matrix(np.zeros((3, 1))) + + dat.mean(axis=1, out=dat_out, keepdims=True) + datsp.mean(axis=1, out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + def test_numpy_mean(self): + # See gh-5987 + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_mean = np.mean(dat) + datsp_mean = np.mean(datsp) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_expm(self): + M = array([[1, 0, 2], [0, 0, 3], [-4, 5, 6]], float) + sM = self.spcreator(M, shape=(3,3), dtype=float) + Mexp = scipy.linalg.expm(M) + + N = array([[3., 0., 1.], [0., 2., 0.], [0., 0., 0.]]) + sN = self.spcreator(N, shape=(3,3), dtype=float) + Nexp = scipy.linalg.expm(N) + + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "splu converted its input to CSC format", + ) + sup.filter( + SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in the CSC matrix format", + ) + sup.filter( + SparseEfficiencyWarning, + "spsolve requires A be CSC or CSR matrix format", + ) + sMexp = expm(sM).toarray() + sNexp = expm(sN).toarray() + + assert_array_almost_equal((sMexp - Mexp), zeros((3, 3))) + assert_array_almost_equal((sNexp - Nexp), zeros((3, 3))) + + def test_inv(self): + def check(dtype): + M = array([[1, 0, 2], [0, 0, 3], [-4, 5, 6]], dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "spsolve requires A be CSC or CSR matrix format",) + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b " + "is in the CSC matrix format",) + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format",) + sM = self.spcreator(M, shape=(3,3), dtype=dtype) + sMinv = inv(sM) + assert_array_almost_equal(sMinv.dot(sM).toarray(), np.eye(3)) + assert_raises(TypeError, inv, M) + for dtype in [float]: + check(dtype) + + @sup_complex + def test_from_array(self): + A = array([[1,0,0],[2,3,4],[0,5,0],[0,0,0]]) + assert_array_equal(self.spcreator(A).toarray(), A) + + A = array([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + assert_array_equal(self.spcreator(A).toarray(), A) + assert_array_equal(self.spcreator(A, dtype='int16').toarray(),A.astype('int16')) + + @sup_complex + def test_from_matrix(self): + A = matrix([[1, 0, 0], [2, 3, 4], [0, 5, 0], [0, 0, 0]]) + assert_array_equal(self.spcreator(A).todense(), A) + + A = matrix([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + assert_array_equal(self.spcreator(A).todense(), A) + assert_array_equal( + self.spcreator(A, dtype='int16').todense(), A.astype('int16') + ) + + @sup_complex + def test_from_list(self): + A = [[1,0,0],[2,3,4],[0,5,0],[0,0,0]] + assert_array_equal(self.spcreator(A).toarray(), A) + + A = [[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]] + assert_array_equal(self.spcreator(A).toarray(), array(A)) + assert_array_equal( + self.spcreator(A, dtype='int16').toarray(), array(A).astype('int16') + ) + + @sup_complex + def test_from_sparse(self): + D = array([[1,0,0],[2,3,4],[0,5,0],[0,0,0]]) + S = csr_matrix(D) + assert_array_equal(self.spcreator(S).toarray(), D) + S = self.spcreator(D) + assert_array_equal(self.spcreator(S).toarray(), D) + + D = array([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + S = csr_matrix(D) + assert_array_equal(self.spcreator(S).toarray(), D) + assert_array_equal(self.spcreator(S, dtype='int16').toarray(), + D.astype('int16')) + S = self.spcreator(D) + assert_array_equal(self.spcreator(S).toarray(), D) + assert_array_equal(self.spcreator(S, dtype='int16').toarray(), + D.astype('int16')) + + # def test_array(self): + # """test array(A) where A is in sparse format""" + # assert_equal( array(self.datsp), self.dat ) + + def test_todense(self): + # Check C- or F-contiguous (default). + chk = self.datsp.todense() + assert isinstance(chk, np.matrix) + assert_array_equal(chk, self.dat) + assert_(chk.flags.c_contiguous != chk.flags.f_contiguous) + # Check C-contiguous (with arg). + chk = self.datsp.todense(order='C') + assert_array_equal(chk, self.dat) + assert_(chk.flags.c_contiguous) + assert_(not chk.flags.f_contiguous) + # Check F-contiguous (with arg). + chk = self.datsp.todense(order='F') + assert_array_equal(chk, self.dat) + assert_(not chk.flags.c_contiguous) + assert_(chk.flags.f_contiguous) + # Check with out argument (array). + out = np.zeros(self.datsp.shape, dtype=self.datsp.dtype) + chk = self.datsp.todense(out=out) + assert_array_equal(self.dat, out) + assert_array_equal(self.dat, chk) + assert np.may_share_memory(chk, out) + # Check with out array (matrix). + out = asmatrix(np.zeros(self.datsp.shape, dtype=self.datsp.dtype)) + chk = self.datsp.todense(out=out) + assert_array_equal(self.dat, out) + assert_array_equal(self.dat, chk) + assert np.may_share_memory(chk, out) + a = array([[1.,2.,3.]]) + dense_dot_dense = a @ self.dat + check = a @ self.datsp.todense() + assert_array_equal(dense_dot_dense, check) + b = array([[1.,2.,3.,4.]]).T + dense_dot_dense = self.dat @ b + check2 = self.datsp.todense() @ b + assert_array_equal(dense_dot_dense, check2) + # Check bool data works. + spbool = self.spcreator(self.dat, dtype=bool) + matbool = self.dat.astype(bool) + assert_array_equal(spbool.todense(), matbool) + + def test_toarray(self): + # Check C- or F-contiguous (default). + dat = asarray(self.dat) + chk = self.datsp.toarray() + assert_array_equal(chk, dat) + assert_(chk.flags.c_contiguous != chk.flags.f_contiguous) + # Check C-contiguous (with arg). + chk = self.datsp.toarray(order='C') + assert_array_equal(chk, dat) + assert_(chk.flags.c_contiguous) + assert_(not chk.flags.f_contiguous) + # Check F-contiguous (with arg). + chk = self.datsp.toarray(order='F') + assert_array_equal(chk, dat) + assert_(not chk.flags.c_contiguous) + assert_(chk.flags.f_contiguous) + # Check with output arg. + out = np.zeros(self.datsp.shape, dtype=self.datsp.dtype) + self.datsp.toarray(out=out) + assert_array_equal(chk, dat) + # Check that things are fine when we don't initialize with zeros. + out[...] = 1. + self.datsp.toarray(out=out) + assert_array_equal(chk, dat) + a = array([1.,2.,3.]) + dense_dot_dense = dot(a, dat) + check = dot(a, self.datsp.toarray()) + assert_array_equal(dense_dot_dense, check) + b = array([1.,2.,3.,4.]) + dense_dot_dense = dot(dat, b) + check2 = dot(self.datsp.toarray(), b) + assert_array_equal(dense_dot_dense, check2) + # Check bool data works. + spbool = self.spcreator(self.dat, dtype=bool) + arrbool = dat.astype(bool) + assert_array_equal(spbool.toarray(), arrbool) + + @sup_complex + def test_astype(self): + D = array([[2.0 + 3j, 0, 0], + [0, 4.0 + 5j, 0], + [0, 0, 0]]) + S = self.spcreator(D) + + for x in supported_dtypes: + # Check correctly casted + D_casted = D.astype(x) + for copy in (True, False): + S_casted = S.astype(x, copy=copy) + assert_equal(S_casted.dtype, D_casted.dtype) # correct type + assert_equal(S_casted.toarray(), D_casted) # correct values + assert_equal(S_casted.format, S.format) # format preserved + # Check correctly copied + assert_(S_casted.astype(x, copy=False) is S_casted) + S_copied = S_casted.astype(x, copy=True) + assert_(S_copied is not S_casted) + + def check_equal_but_not_same_array_attribute(attribute): + a = getattr(S_casted, attribute) + b = getattr(S_copied, attribute) + assert_array_equal(a, b) + assert_(a is not b) + i = (0,) * b.ndim + b_i = b[i] + b[i] = not b[i] + assert_(a[i] != b[i]) + b[i] = b_i + + if S_casted.format in ('csr', 'csc', 'bsr'): + for attribute in ('indices', 'indptr', 'data'): + check_equal_but_not_same_array_attribute(attribute) + elif S_casted.format == 'coo': + for attribute in ('row', 'col', 'data'): + check_equal_but_not_same_array_attribute(attribute) + elif S_casted.format == 'dia': + for attribute in ('offsets', 'data'): + check_equal_but_not_same_array_attribute(attribute) + + @sup_complex + def test_astype_immutable(self): + D = array([[2.0 + 3j, 0, 0], + [0, 4.0 + 5j, 0], + [0, 0, 0]]) + S = self.spcreator(D) + if hasattr(S, 'data'): + S.data.flags.writeable = False + if S.format in ('csr', 'csc', 'bsr'): + S.indptr.flags.writeable = False + S.indices.flags.writeable = False + for x in supported_dtypes: + D_casted = D.astype(x) + S_casted = S.astype(x) + assert_equal(S_casted.dtype, D_casted.dtype) + + + def test_asfptype(self): + A = self.spcreator(arange(6,dtype='int32').reshape(2,3)) + + assert_equal(A.dtype, np.dtype('int32')) + assert_equal(A.asfptype().dtype, np.dtype('float64')) + assert_equal(A.asfptype().format, A.format) + assert_equal(A.astype('int16').asfptype().dtype, np.dtype('float32')) + assert_equal(A.astype('complex128').asfptype().dtype, np.dtype('complex128')) + + B = A.asfptype() + C = B.asfptype() + assert_(B is C) + + def test_mul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal(dat*2, (datsp*2).toarray()) + assert_array_equal(dat*17.3, (datsp*17.3).toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_rmul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal(2*dat, (2*datsp).toarray()) + assert_array_equal(17.3*dat, (17.3*datsp).toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + # github issue #15210 + def test_rmul_scalar_type_error(self): + datsp = self.datsp_dtypes[np.float64] + with assert_raises(TypeError): + None * datsp + + def test_add(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + a = dat.copy() + a[0,2] = 2.0 + b = datsp + c = b + a + assert_array_equal(c, b.toarray() + a) + + c = b + b.tocsr() + assert_array_equal(c.toarray(), + b.toarray() + b.toarray()) + + # test broadcasting + c = b + a[0] + assert_array_equal(c, b.toarray() + a[0]) + + for dtype in self.math_dtypes: + check(dtype) + + def test_radd(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + a = dat.copy() + a[0,2] = 2.0 + b = datsp + c = a + b + assert_array_equal(c, a + b.toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_sub(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal((datsp - datsp).toarray(), np.zeros((3, 4))) + assert_array_equal((datsp - 0).toarray(), dat) + + A = self.spcreator( + np.array([[1, 0, 0, 4], [-1, 0, 0, 0], [0, 8, 0, -5]], 'd') + ) + assert_array_equal((datsp - A).toarray(), dat - A.toarray()) + assert_array_equal((A - datsp).toarray(), A.toarray() - dat) + + # test broadcasting + assert_array_equal(datsp - dat[0], dat - dat[0]) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_rsub(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal((dat - datsp),[[0,0,0,0],[0,0,0,0],[0,0,0,0]]) + assert_array_equal((datsp - dat),[[0,0,0,0],[0,0,0,0],[0,0,0,0]]) + assert_array_equal((0 - datsp).toarray(), -dat) + + A = self.spcreator(matrix([[1,0,0,4],[-1,0,0,0],[0,8,0,-5]],'d')) + assert_array_equal((dat - A), dat - A.toarray()) + assert_array_equal((A - dat), A.toarray() - dat) + assert_array_equal(A.toarray() - datsp, A.toarray() - dat) + assert_array_equal(datsp - A.toarray(), dat - A.toarray()) + + # test broadcasting + assert_array_equal(dat[0] - datsp, dat[0] - dat) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_add0(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Adding 0 to a sparse matrix + assert_array_equal((datsp + 0).toarray(), dat) + # use sum (which takes 0 as a starting value) + sumS = sum([k * datsp for k in range(1, 3)]) + sumD = sum([k * dat for k in range(1, 3)]) + assert_almost_equal(sumS.toarray(), sumD) + + for dtype in self.math_dtypes: + check(dtype) + + def test_elementwise_multiply(self): + # real/real + A = array([[4,0,9],[2,-3,5]]) + B = array([[0,7,0],[0,-4,0]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + assert_almost_equal(Asp.multiply(Bsp).toarray(), A*B) # sparse/sparse + assert_almost_equal(Asp.multiply(B).toarray(), A*B) # sparse/dense + + # complex/complex + C = array([[1-2j,0+5j,-1+0j],[4-3j,-3+6j,5]]) + D = array([[5+2j,7-3j,-2+1j],[0-1j,-4+2j,9]]) + Csp = self.spcreator(C) + Dsp = self.spcreator(D) + assert_almost_equal(Csp.multiply(Dsp).toarray(), C*D) # sparse/sparse + assert_almost_equal(Csp.multiply(D).toarray(), C*D) # sparse/dense + + # real/complex + assert_almost_equal(Asp.multiply(Dsp).toarray(), A*D) # sparse/sparse + assert_almost_equal(Asp.multiply(D).toarray(), A*D) # sparse/dense + + def test_elementwise_multiply_broadcast(self): + A = array([4]) + B = array([[-9]]) + C = array([1,-1,0]) + D = array([[7,9,-9]]) + E = array([[3],[2],[1]]) + F = array([[8,6,3],[-4,3,2],[6,6,6]]) + G = [1, 2, 3] + H = np.ones((3, 4)) + J = H.T + K = array([[0]]) + L = array([[[1,2],[0,1]]]) + + # Some arrays can't be cast as spmatrices (A,C,L) so leave + # them out. + Bsp = self.spcreator(B) + Dsp = self.spcreator(D) + Esp = self.spcreator(E) + Fsp = self.spcreator(F) + Hsp = self.spcreator(H) + Hspp = self.spcreator(H[0,None]) + Jsp = self.spcreator(J) + Jspp = self.spcreator(J[:,0,None]) + Ksp = self.spcreator(K) + + matrices = [A, B, C, D, E, F, G, H, J, K, L] + spmatrices = [Bsp, Dsp, Esp, Fsp, Hsp, Hspp, Jsp, Jspp, Ksp] + + # sparse/sparse + for i in spmatrices: + for j in spmatrices: + try: + dense_mult = i.toarray() * j.toarray() + except ValueError: + assert_raises(ValueError, i.multiply, j) + continue + sp_mult = i.multiply(j) + assert_almost_equal(sp_mult.toarray(), dense_mult) + + # sparse/dense + for i in spmatrices: + for j in matrices: + try: + dense_mult = i.toarray() * j + except TypeError: + continue + except ValueError: + assert_raises(ValueError, i.multiply, j) + continue + sp_mult = i.multiply(j) + if issparse(sp_mult): + assert_almost_equal(sp_mult.toarray(), dense_mult) + else: + assert_almost_equal(sp_mult, dense_mult) + + def test_elementwise_divide(self): + expected = [[1,np.nan,np.nan,1], + [1,np.nan,1,np.nan], + [np.nan,1,np.nan,np.nan]] + assert_array_equal(toarray(self.datsp / self.datsp), expected) + + denom = self.spcreator(matrix([[1,0,0,4],[-1,0,0,0],[0,8,0,-5]],'d')) + expected = [[1,np.nan,np.nan,0.5], + [-3,np.nan,inf,np.nan], + [np.nan,0.25,np.nan,0]] + assert_array_equal(toarray(self.datsp / denom), expected) + + # complex + A = array([[1-2j,0+5j,-1+0j],[4-3j,-3+6j,5]]) + B = array([[5+2j,7-3j,-2+1j],[0-1j,-4+2j,9]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + assert_almost_equal(toarray(Asp / Bsp), A/B) + + # integer + A = array([[1,2,3],[-3,2,1]]) + B = array([[0,1,2],[0,-2,3]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + with np.errstate(divide='ignore'): + assert_array_equal(toarray(Asp / Bsp), A / B) + + # mismatching sparsity patterns + A = array([[0,1],[1,0]]) + B = array([[1,0],[1,0]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + with np.errstate(divide='ignore', invalid='ignore'): + assert_array_equal(np.array(toarray(Asp / Bsp)), A / B) + + def test_pow(self): + A = array([[1, 0, 2, 0], [0, 3, 4, 0], [0, 5, 0, 0], [0, 6, 7, 8]]) + B = self.spcreator(A) + + for exponent in [0,1,2,3]: + ret_sp = B**exponent + ret_np = np.linalg.matrix_power(A, exponent) + assert_array_equal(ret_sp.toarray(), ret_np) + assert_equal(ret_sp.dtype, ret_np.dtype) + + # invalid exponents + for exponent in [-1, 2.2, 1 + 3j]: + assert_raises(ValueError, B.__pow__, exponent) + + # nonsquare matrix + B = self.spcreator(A[:3,:]) + assert_raises(TypeError, B.__pow__, 1) + + def test_rmatvec(self): + M = self.spcreator(matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + assert_array_almost_equal([1,2,3,4] @ M, dot([1,2,3,4], M.toarray())) + row = array([[1,2,3,4]]) + assert_array_almost_equal(row @ M, row @ M.toarray()) + + def test_small_multiplication(self): + # test that A*x works for x with shape () (1,) (1,1) and (1,0) + A = self.spcreator([[1],[2],[3]]) + + assert_(issparse(A * array(1))) + assert_equal((A * array(1)).toarray(), [[1], [2], [3]]) + + assert_equal(A @ array([1]), array([1, 2, 3])) + assert_equal(A @ array([[1]]), array([[1], [2], [3]])) + assert_equal(A @ np.ones((1, 1)), array([[1], [2], [3]])) + assert_equal(A @ np.ones((1, 0)), np.ones((3, 0))) + + def test_start_vs_at_sign_for_sparray_and_spmatrix(self): + # test that * is matmul for spmatrix and mul for sparray + A = self.spcreator([[1],[2],[3]]) + + if isinstance(A, sparray): + assert_array_almost_equal(A * np.ones((3,1)), A) + assert_array_almost_equal(A * array([[1]]), A) + assert_array_almost_equal(A * np.ones((3,1)), A) + else: + assert_equal(A * array([1]), array([1, 2, 3])) + assert_equal(A * array([[1]]), array([[1], [2], [3]])) + assert_equal(A * np.ones((1, 0)), np.ones((3, 0))) + + def test_binop_custom_type(self): + # Non-regression test: previously, binary operations would raise + # NotImplementedError instead of returning NotImplemented + # (https://docs.python.org/library/constants.html#NotImplemented) + # so overloading Custom + matrix etc. didn't work. + A = self.spcreator([[1], [2], [3]]) + B = BinopTester() + assert_equal(A + B, "matrix on the left") + assert_equal(A - B, "matrix on the left") + assert_equal(A * B, "matrix on the left") + assert_equal(B + A, "matrix on the right") + assert_equal(B - A, "matrix on the right") + assert_equal(B * A, "matrix on the right") + + assert_equal(A @ B, "matrix on the left") + assert_equal(B @ A, "matrix on the right") + + def test_binop_custom_type_with_shape(self): + A = self.spcreator([[1], [2], [3]]) + B = BinopTester_with_shape((3,1)) + assert_equal(A + B, "matrix on the left") + assert_equal(A - B, "matrix on the left") + assert_equal(A * B, "matrix on the left") + assert_equal(B + A, "matrix on the right") + assert_equal(B - A, "matrix on the right") + assert_equal(B * A, "matrix on the right") + + assert_equal(A @ B, "matrix on the left") + assert_equal(B @ A, "matrix on the right") + + def test_mul_custom_type(self): + class Custom: + def __init__(self, scalar): + self.scalar = scalar + + def __rmul__(self, other): + return other * self.scalar + + scalar = 2 + A = self.spcreator([[1],[2],[3]]) + c = Custom(scalar) + A_scalar = A * scalar + A_c = A * c + assert_array_equal_dtype(A_scalar.toarray(), A_c.toarray()) + assert_equal(A_scalar.format, A_c.format) + + def test_comparisons_custom_type(self): + A = self.spcreator([[1], [2], [3]]) + B = ComparisonTester() + assert_equal(A == B, "eq") + assert_equal(A != B, "ne") + assert_equal(A > B, "lt") + assert_equal(A >= B, "le") + assert_equal(A < B, "gt") + assert_equal(A <= B, "ge") + + def test_dot_scalar(self): + M = self.spcreator(array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + scalar = 10 + actual = M.dot(scalar) + expected = M * scalar + + assert_allclose(actual.toarray(), expected.toarray()) + + def test_matmul(self): + M = self.spcreator(array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + B = self.spcreator(array([[0,1],[1,0],[0,2]],'d')) + col = array([[1,2,3]]).T + + matmul = operator.matmul + # check matrix-vector + assert_array_almost_equal(matmul(M, col), M.toarray() @ col) + + # check matrix-matrix + assert_array_almost_equal(matmul(M, B).toarray(), (M @ B).toarray()) + assert_array_almost_equal(matmul(M.toarray(), B), (M @ B).toarray()) + assert_array_almost_equal(matmul(M, B.toarray()), (M @ B).toarray()) + if not isinstance(M, sparray): + assert_array_almost_equal(matmul(M, B).toarray(), (M * B).toarray()) + assert_array_almost_equal(matmul(M.toarray(), B), (M * B).toarray()) + assert_array_almost_equal(matmul(M, B.toarray()), (M * B).toarray()) + + # check error on matrix-scalar + assert_raises(ValueError, matmul, M, 1) + assert_raises(ValueError, matmul, 1, M) + + def test_matvec(self): + M = self.spcreator(matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + col = array([[1,2,3]]).T + + assert_array_almost_equal(M @ col, M.toarray() @ col) + + # check result dimensions (ticket #514) + assert_equal((M @ array([1,2,3])).shape,(4,)) + assert_equal((M @ array([[1],[2],[3]])).shape,(4,1)) + assert_equal((M @ matrix([[1],[2],[3]])).shape,(4,1)) + + # check result type + assert_(isinstance(M @ array([1,2,3]), ndarray)) + assert_(isinstance(M @ matrix([1,2,3]).T, np.matrix)) + + # ensure exception is raised for improper dimensions + bad_vecs = [array([1,2]), array([1,2,3,4]), array([[1],[2]]), + matrix([1,2,3]), matrix([[1],[2]])] + for x in bad_vecs: + assert_raises(ValueError, M.__mul__, x) + + # The current relationship between sparse matrix products and array + # products is as follows: + assert_array_almost_equal(M@array([1,2,3]), dot(M.toarray(),[1,2,3])) + assert_array_almost_equal(M@[[1],[2],[3]], asmatrix(dot(M.toarray(),[1,2,3])).T) + # Note that the result of M * x is dense if x has a singleton dimension. + + # Currently M.matvec(asarray(col)) is rank-1, whereas M.matvec(col) + # is rank-2. Is this desirable? + + def test_matmat_sparse(self): + a = matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + a2 = array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + b = matrix([[0,1],[1,0],[0,2]],'d') + asp = self.spcreator(a) + bsp = self.spcreator(b) + assert_array_almost_equal((asp @ bsp).toarray(), a @ b) + assert_array_almost_equal(asp @ b, a @ b) + assert_array_almost_equal(a @ bsp, a @ b) + assert_array_almost_equal(a2 @ bsp, a @ b) + + # Now try performing cross-type multplication: + csp = bsp.tocsc() + c = b + want = a @ c + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + csp = bsp.tocsr() + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + csp = bsp.tocoo() + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + + # Test provided by Andy Fraser, 2006-03-26 + L = 30 + frac = .3 + random.seed(0) # make runs repeatable + A = zeros((L,2)) + for i in range(L): + for j in range(2): + r = random.random() + if r < frac: + A[i,j] = r/frac + + A = self.spcreator(A) + B = A @ A.T + assert_array_almost_equal(B.toarray(), A.toarray() @ A.T.toarray()) + assert_array_almost_equal(B.toarray(), A.toarray() @ A.toarray().T) + + # check dimension mismatch 2x2 times 3x2 + A = self.spcreator([[1,2],[3,4]]) + B = self.spcreator([[1,2],[3,4],[5,6]]) + assert_raises(ValueError, A.__matmul__, B) + if isinstance(A, sparray): + assert_raises(ValueError, A.__mul__, B) + + def test_matmat_dense(self): + a = matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + asp = self.spcreator(a) + + # check both array and matrix types + bs = [array([[1,2],[3,4],[5,6]]), matrix([[1,2],[3,4],[5,6]])] + + for b in bs: + result = asp @ b + assert_(isinstance(result, type(b))) + assert_equal(result.shape, (4,2)) + assert_equal(result, dot(a,b)) + + def test_sparse_format_conversions(self): + A = sparse.kron([[1,0,2],[0,3,4],[5,0,0]], [[1,2],[0,3]]) + D = A.toarray() + A = self.spcreator(A) + + for format in ['bsr','coo','csc','csr','dia','dok','lil']: + a = A.asformat(format) + assert_equal(a.format,format) + assert_array_equal(a.toarray(), D) + + b = self.spcreator(D+3j).asformat(format) + assert_equal(b.format,format) + assert_array_equal(b.toarray(), D+3j) + + c = eval(format + '_matrix')(A) + assert_equal(c.format,format) + assert_array_equal(c.toarray(), D) + + for format in ['array', 'dense']: + a = A.asformat(format) + assert_array_equal(a, D) + + b = self.spcreator(D+3j).asformat(format) + assert_array_equal(b, D+3j) + + def test_tobsr(self): + x = array([[1,0,2,0],[0,0,0,0],[0,0,4,5]]) + y = array([[0,1,2],[3,0,5]]) + A = kron(x,y) + Asp = self.spcreator(A) + for format in ['bsr']: + fn = getattr(Asp, 'to' + format) + + for X in [1, 2, 3, 6]: + for Y in [1, 2, 3, 4, 6, 12]: + assert_equal(fn(blocksize=(X, Y)).toarray(), A) + + def test_transpose(self): + dat_1 = self.dat + dat_2 = np.array([[]]) + matrices = [dat_1, dat_2] + + def check(dtype, j): + dat = array(matrices[j], dtype=dtype) + datsp = self.spcreator(dat) + + a = datsp.transpose() + b = dat.transpose() + + assert_array_equal(a.toarray(), b) + assert_array_equal(a.transpose().toarray(), dat) + assert_array_equal(datsp.transpose(axes=(1, 0)).toarray(), b) + assert_equal(a.dtype, b.dtype) + + # See gh-5987 + empty = self.spcreator((3, 4)) + assert_array_equal(np.transpose(empty).toarray(), + np.transpose(zeros((3, 4)))) + assert_array_equal(empty.T.toarray(), zeros((4, 3))) + assert_raises(ValueError, empty.transpose, axes=0) + + for dtype in self.checked_dtypes: + for j in range(len(matrices)): + check(dtype, j) + + def test_add_dense(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # adding a dense matrix to a sparse matrix + sum1 = dat + datsp + assert_array_equal(sum1, dat + dat) + sum2 = datsp + dat + assert_array_equal(sum2, dat + dat) + + for dtype in self.math_dtypes: + check(dtype) + + def test_sub_dense(self): + # subtracting a dense matrix to/from a sparse matrix + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Behavior is different for bool. + if dat.dtype == bool: + sum1 = dat - datsp + assert_array_equal(sum1, dat - dat) + sum2 = datsp - dat + assert_array_equal(sum2, dat - dat) + else: + # Manually add to avoid upcasting from scalar + # multiplication. + sum1 = (dat + dat + dat) - datsp + assert_array_equal(sum1, dat + dat) + sum2 = (datsp + datsp + datsp) - dat + assert_array_equal(sum2, dat + dat) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_maximum_minimum(self): + A_dense = np.array([[1, 0, 3], [0, 4, 5], [0, 0, 0]]) + B_dense = np.array([[1, 1, 2], [0, 3, 6], [1, -1, 0]]) + + A_dense_cpx = np.array([[1, 0, 3], [0, 4+2j, 5], [0, 1j, -1j]]) + + def check(dtype, dtype2, btype): + if np.issubdtype(dtype, np.complexfloating): + A = self.spcreator(A_dense_cpx.astype(dtype)) + else: + A = self.spcreator(A_dense.astype(dtype)) + if btype == 'scalar': + B = dtype2.type(1) + elif btype == 'scalar2': + B = dtype2.type(-1) + elif btype == 'dense': + B = B_dense.astype(dtype2) + elif btype == 'sparse': + B = self.spcreator(B_dense.astype(dtype2)) + else: + raise ValueError() + + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "Taking maximum .minimum. with > 0 .< 0. number " + "results to a dense matrix") + + max_s = A.maximum(B) + min_s = A.minimum(B) + + max_d = np.maximum(toarray(A), toarray(B)) + assert_array_equal(toarray(max_s), max_d) + assert_equal(max_s.dtype, max_d.dtype) + + min_d = np.minimum(toarray(A), toarray(B)) + assert_array_equal(toarray(min_s), min_d) + assert_equal(min_s.dtype, min_d.dtype) + + for dtype in self.math_dtypes: + for dtype2 in [np.int8, np.float64, np.complex128]: + for btype in ['scalar', 'scalar2', 'dense', 'sparse']: + check(np.dtype(dtype), np.dtype(dtype2), btype) + + def test_copy(self): + # Check whether the copy=True and copy=False keywords work + A = self.datsp + + # check that copy preserves format + assert_equal(A.copy().format, A.format) + assert_equal(A.__class__(A,copy=True).format, A.format) + assert_equal(A.__class__(A,copy=False).format, A.format) + + assert_equal(A.copy().toarray(), A.toarray()) + assert_equal(A.__class__(A, copy=True).toarray(), A.toarray()) + assert_equal(A.__class__(A, copy=False).toarray(), A.toarray()) + + # check that XXX_matrix.toXXX() works + toself = getattr(A,'to' + A.format) + assert_(toself() is A) + assert_(toself(copy=False) is A) + assert_equal(toself(copy=True).format, A.format) + assert_equal(toself(copy=True).toarray(), A.toarray()) + + # check whether the data is copied? + assert_(not sparse_may_share_memory(A.copy(), A)) + + # test that __iter__ is compatible with NumPy matrix + def test_iterator(self): + B = matrix(np.arange(50).reshape(5, 10)) + A = self.spcreator(B) + + for x, y in zip(A, B): + assert_equal(x.toarray(), y) + + def test_size_zero_matrix_arithmetic(self): + # Test basic matrix arithmetic with shapes like (0,0), (10,0), + # (0, 3), etc. + mat = array([]) + a = mat.reshape((0, 0)) + b = mat.reshape((0, 1)) + c = mat.reshape((0, 5)) + d = mat.reshape((1, 0)) + e = mat.reshape((5, 0)) + f = np.ones([5, 5]) + + asp = self.spcreator(a) + bsp = self.spcreator(b) + csp = self.spcreator(c) + dsp = self.spcreator(d) + esp = self.spcreator(e) + fsp = self.spcreator(f) + + # matrix product. + assert_array_equal(asp.dot(asp).toarray(), np.dot(a, a)) + assert_array_equal(bsp.dot(dsp).toarray(), np.dot(b, d)) + assert_array_equal(dsp.dot(bsp).toarray(), np.dot(d, b)) + assert_array_equal(csp.dot(esp).toarray(), np.dot(c, e)) + assert_array_equal(csp.dot(fsp).toarray(), np.dot(c, f)) + assert_array_equal(esp.dot(csp).toarray(), np.dot(e, c)) + assert_array_equal(dsp.dot(csp).toarray(), np.dot(d, c)) + assert_array_equal(fsp.dot(esp).toarray(), np.dot(f, e)) + + # bad matrix products + assert_raises(ValueError, dsp.dot, e) + assert_raises(ValueError, asp.dot, d) + + # elemente-wise multiplication + assert_array_equal(asp.multiply(asp).toarray(), np.multiply(a, a)) + assert_array_equal(bsp.multiply(bsp).toarray(), np.multiply(b, b)) + assert_array_equal(dsp.multiply(dsp).toarray(), np.multiply(d, d)) + + assert_array_equal(asp.multiply(a).toarray(), np.multiply(a, a)) + assert_array_equal(bsp.multiply(b).toarray(), np.multiply(b, b)) + assert_array_equal(dsp.multiply(d).toarray(), np.multiply(d, d)) + + assert_array_equal(asp.multiply(6).toarray(), np.multiply(a, 6)) + assert_array_equal(bsp.multiply(6).toarray(), np.multiply(b, 6)) + assert_array_equal(dsp.multiply(6).toarray(), np.multiply(d, 6)) + + # bad element-wise multiplication + assert_raises(ValueError, asp.multiply, c) + assert_raises(ValueError, esp.multiply, c) + + # Addition + assert_array_equal(asp.__add__(asp).toarray(), a.__add__(a)) + assert_array_equal(bsp.__add__(bsp).toarray(), b.__add__(b)) + assert_array_equal(dsp.__add__(dsp).toarray(), d.__add__(d)) + + # bad addition + assert_raises(ValueError, asp.__add__, dsp) + assert_raises(ValueError, bsp.__add__, asp) + + def test_size_zero_conversions(self): + mat = array([]) + a = mat.reshape((0, 0)) + b = mat.reshape((0, 5)) + c = mat.reshape((5, 0)) + + for m in [a, b, c]: + spm = self.spcreator(m) + assert_array_equal(spm.tocoo().toarray(), m) + assert_array_equal(spm.tocsr().toarray(), m) + assert_array_equal(spm.tocsc().toarray(), m) + assert_array_equal(spm.tolil().toarray(), m) + assert_array_equal(spm.todok().toarray(), m) + assert_array_equal(spm.tobsr().toarray(), m) + + def test_pickle(self): + import pickle + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + def check(): + datsp = self.datsp.copy() + for protocol in range(pickle.HIGHEST_PROTOCOL): + sploaded = pickle.loads(pickle.dumps(datsp, protocol=protocol)) + assert_equal(datsp.shape, sploaded.shape) + assert_array_equal(datsp.toarray(), sploaded.toarray()) + assert_equal(datsp.format, sploaded.format) + # Hacky check for class member equality. This assumes that + # all instance variables are one of: + # 1. Plain numpy ndarrays + # 2. Tuples of ndarrays + # 3. Types that support equality comparison with == + for key, val in datsp.__dict__.items(): + if isinstance(val, np.ndarray): + assert_array_equal(val, sploaded.__dict__[key]) + elif (isinstance(val, tuple) and val + and isinstance(val[0], np.ndarray)): + assert_array_equal(val, sploaded.__dict__[key]) + else: + assert_(val == sploaded.__dict__[key]) + check() + + def test_unary_ufunc_overrides(self): + def check(name): + if name == "sign": + pytest.skip("sign conflicts with comparison op " + "support on Numpy") + if self.spcreator in (dok_matrix, lil_matrix): + pytest.skip("Unary ops not implemented for dok/lil") + ufunc = getattr(np, name) + + X = self.spcreator(np.arange(20).reshape(4, 5) / 20.) + X0 = ufunc(X.toarray()) + + X2 = ufunc(X) + assert_array_equal(X2.toarray(), X0) + + for name in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt", + "abs"]: + check(name) + + def test_resize(self): + # resize(shape) resizes the matrix in-place + D = np.array([[1, 0, 3, 4], + [2, 0, 0, 0], + [3, 0, 0, 0]]) + S = self.spcreator(D) + assert_(S.resize((3, 2)) is None) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [3, 0]]) + S.resize((2, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0]]) + S.resize((3, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + S.resize((3, 3)) + assert_array_equal(S.toarray(), [[1, 0, 0], + [2, 0, 0], + [0, 0, 0]]) + # test no-op + S.resize((3, 3)) + assert_array_equal(S.toarray(), [[1, 0, 0], + [2, 0, 0], + [0, 0, 0]]) + + # test *args + S.resize(3, 2) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + + for bad_shape in [1, (-1, 2), (2, -1), (1, 2, 3)]: + assert_raises(ValueError, S.resize, bad_shape) + + def test_constructor1_base(self): + A = self.datsp + + self_format = A.format + + C = A.__class__(A, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + if self_format not in NON_ARRAY_BACKED_FORMATS: + assert_(sparse_may_share_memory(A, C)) + + C = A.__class__(A, dtype=A.dtype, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + if self_format not in NON_ARRAY_BACKED_FORMATS: + assert_(sparse_may_share_memory(A, C)) + + C = A.__class__(A, dtype=np.float32, copy=False) + assert_array_equal(A.toarray(), C.toarray()) + + C = A.__class__(A, copy=True) + assert_array_equal_dtype(A.toarray(), C.toarray()) + assert_(not sparse_may_share_memory(A, C)) + + for other_format in ['csr', 'csc', 'coo', 'dia', 'dok', 'lil']: + if other_format == self_format: + continue + B = A.asformat(other_format) + C = A.__class__(B, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + + C = A.__class__(B, copy=True) + assert_array_equal_dtype(A.toarray(), C.toarray()) + assert_(not sparse_may_share_memory(B, C)) + + +class _TestInplaceArithmetic: + def test_inplace_dense(self): + a = np.ones((3, 4)) + b = self.spcreator(a) + + x = a.copy() + y = a.copy() + x += a + y += b + assert_array_equal(x, y) + + x = a.copy() + y = a.copy() + x -= a + y -= b + assert_array_equal(x, y) + + x = a.copy() + y = a.copy() + if isinstance(b, sparray): + assert_raises(ValueError, operator.imul, x, b.T) + x = x * a + y *= b + else: + # This is matrix product, from __rmul__ + assert_raises(ValueError, operator.imul, x, b) + x = x.dot(a.T) + y *= b.T + assert_array_equal(x, y) + + # Matrix (non-elementwise) floor division is not defined + assert_raises(TypeError, operator.ifloordiv, x, b) + + def test_imul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Avoid implicit casting. + if np.can_cast(int, dtype, casting='same_kind'): + a = datsp.copy() + a *= 2 + b = dat.copy() + b *= 2 + assert_array_equal(b, a.toarray()) + + if np.can_cast(float, dtype, casting='same_kind'): + a = datsp.copy() + a *= 17.3 + b = dat.copy() + b *= 17.3 + assert_array_equal(b, a.toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_idiv_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + if np.can_cast(int, dtype, casting='same_kind'): + a = datsp.copy() + a /= 2 + b = dat.copy() + b /= 2 + assert_array_equal(b, a.toarray()) + + if np.can_cast(float, dtype, casting='same_kind'): + a = datsp.copy() + a /= 17.3 + b = dat.copy() + b /= 17.3 + assert_array_equal(b, a.toarray()) + + for dtype in self.math_dtypes: + # /= should only be used with float dtypes to avoid implicit + # casting. + if not np.can_cast(dtype, np.dtype(int)): + check(dtype) + + def test_inplace_success(self): + # Inplace ops should work even if a specialized version is not + # implemented, falling back to x = x y + a = self.spcreator(np.eye(5)) + b = self.spcreator(np.eye(5)) + bp = self.spcreator(np.eye(5)) + + b += a + bp = bp + a + assert_allclose(b.toarray(), bp.toarray()) + + b *= a + bp = bp * a + assert_allclose(b.toarray(), bp.toarray()) + + b -= a + bp = bp - a + assert_allclose(b.toarray(), bp.toarray()) + + assert_raises(TypeError, operator.ifloordiv, a, b) + + +class _TestGetSet: + def test_getelement(self): + def check(dtype): + D = array([[1,0,0], + [4,3,0], + [0,2,0], + [0,0,0]], dtype=dtype) + A = self.spcreator(D) + + M,N = D.shape + + for i in range(-M, M): + for j in range(-N, N): + assert_equal(A[i,j], D[i,j]) + + assert_equal(type(A[1,1]), dtype) + + for ij in [(0,3),(-1,3),(4,0),(4,3),(4,-1), (1, 2, 3)]: + assert_raises((IndexError, TypeError), A.__getitem__, ij) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_setelement(self): + def check(dtype): + A = self.spcreator((3,4), dtype=dtype) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[0, 0] = dtype.type(0) # bug 870 + A[1, 2] = dtype.type(4.0) + A[0, 1] = dtype.type(3) + A[2, 0] = dtype.type(2.0) + A[0,-1] = dtype.type(8) + A[-1,-2] = dtype.type(7) + A[0, 1] = dtype.type(5) + + if dtype != np.bool_: + assert_array_equal( + A.toarray(), + [ + [0, 5, 0, 8], + [0, 0, 4, 0], + [2, 0, 7, 0] + ] + ) + + for ij in [(0,4),(-1,4),(3,0),(3,4),(3,-1)]: + assert_raises(IndexError, A.__setitem__, ij, 123.0) + + for v in [[1,2,3], array([1,2,3])]: + assert_raises(ValueError, A.__setitem__, (0,0), v) + + if (not np.issubdtype(dtype, np.complexfloating) and + dtype != np.bool_): + for v in [3j]: + assert_raises(TypeError, A.__setitem__, (0,0), v) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_negative_index_assignment(self): + # Regression test for github issue 4428. + + def check(dtype): + A = self.spcreator((3, 10), dtype=dtype) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[0, -4] = 1 + assert_equal(A[0, -4], 1) + + for dtype in self.math_dtypes: + check(np.dtype(dtype)) + + def test_scalar_assign_2(self): + n, m = (5, 10) + + def _test_set(i, j, nitems): + msg = f"{i!r} ; {j!r} ; {nitems!r}" + A = self.spcreator((n, m)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[i, j] = 1 + assert_almost_equal(A.sum(), nitems, err_msg=msg) + assert_almost_equal(A[i, j], 1, err_msg=msg) + + # [i,j] + for i, j in [(2, 3), (-1, 8), (-1, -2), (array(-1), -2), (-1, array(-2)), + (array(-1), array(-2))]: + _test_set(i, j, 1) + + def test_index_scalar_assign(self): + A = self.spcreator((5, 5)) + B = np.zeros((5, 5)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + for C in [A, B]: + C[0,1] = 1 + C[3,0] = 4 + C[3,0] = 9 + assert_array_equal(A.toarray(), B) + + +class _TestSolve: + def test_solve(self): + # Test whether the lu_solve command segfaults, as reported by Nils + # Wagner for a 64-bit machine, 02 March 2005 (EJS) + n = 20 + np.random.seed(0) # make tests repeatable + A = zeros((n,n), dtype=complex) + x = np.random.rand(n) + y = np.random.rand(n-1)+1j*np.random.rand(n-1) + r = np.random.rand(n) + for i in range(len(x)): + A[i,i] = x[i] + for i in range(len(y)): + A[i,i+1] = y[i] + A[i+1,i] = conjugate(y[i]) + A = self.spcreator(A) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + x = splu(A).solve(r) + assert_almost_equal(A @ x,r) + + +class _TestSlicing: + def test_dtype_preservation(self): + assert_equal(self.spcreator((1,10), dtype=np.int16)[0,1:5].dtype, np.int16) + assert_equal(self.spcreator((1,10), dtype=np.int32)[0,1:5].dtype, np.int32) + assert_equal(self.spcreator((1,10), dtype=np.float32)[0,1:5].dtype, np.float32) + assert_equal(self.spcreator((1,10), dtype=np.float64)[0,1:5].dtype, np.float64) + + def test_dtype_preservation_empty_slice(self): + # This should be parametrized with pytest, but something in the parent + # class creation used in this file breaks pytest.mark.parametrize. + for dt in [np.int16, np.int32, np.float32, np.float64]: + A = self.spcreator((3, 2), dtype=dt) + assert_equal(A[:, 0:0:2].dtype, dt) + assert_equal(A[0:0:2, :].dtype, dt) + assert_equal(A[0, 0:0:2].dtype, dt) + assert_equal(A[0:0:2, 0].dtype, dt) + + def test_get_horiz_slice(self): + B = asmatrix(arange(50.).reshape(5,10)) + A = self.spcreator(B) + assert_array_equal(B[1, :], A[1, :].toarray()) + assert_array_equal(B[1, 2:5], A[1, 2:5].toarray()) + + C = matrix([[1, 2, 1], [4, 0, 6], [0, 0, 0], [0, 0, 1]]) + D = self.spcreator(C) + assert_array_equal(C[1, 1:3], D[1, 1:3].toarray()) + + # Now test slicing when a row contains only zeros + E = matrix([[1, 2, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[1, 1:3], F[1, 1:3].toarray()) + assert_array_equal(E[2, -2:], F[2, -2:].toarray()) + + # The following should raise exceptions: + assert_raises(IndexError, A.__getitem__, (slice(None), 11)) + assert_raises(IndexError, A.__getitem__, (6, slice(3, 7))) + + def test_get_vert_slice(self): + B = arange(50.).reshape(5, 10) + A = self.spcreator(B) + assert_array_equal(B[2:5, [0]], A[2:5, 0].toarray()) + assert_array_equal(B[:, [1]], A[:, 1].toarray()) + + C = array([[1, 2, 1], [4, 0, 6], [0, 0, 0], [0, 0, 1]]) + D = self.spcreator(C) + assert_array_equal(C[1:3, [1]], D[1:3, 1].toarray()) + assert_array_equal(C[:, [2]], D[:, 2].toarray()) + + # Now test slicing when a column contains only zeros + E = array([[1, 0, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[:, [1]], F[:, 1].toarray()) + assert_array_equal(E[-2:, [2]], F[-2:, 2].toarray()) + + # The following should raise exceptions: + assert_raises(IndexError, A.__getitem__, (slice(None), 11)) + assert_raises(IndexError, A.__getitem__, (6, slice(3, 7))) + + def test_get_slices(self): + B = arange(50.).reshape(5, 10) + A = self.spcreator(B) + assert_array_equal(A[2:5, 0:3].toarray(), B[2:5, 0:3]) + assert_array_equal(A[1:, :-1].toarray(), B[1:, :-1]) + assert_array_equal(A[:-1, 1:].toarray(), B[:-1, 1:]) + + # Now test slicing when a column contains only zeros + E = array([[1, 0, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[1:2, 1:2], F[1:2, 1:2].toarray()) + assert_array_equal(E[:, 1:], F[:, 1:].toarray()) + + def test_non_unit_stride_2d_indexing(self): + # Regression test -- used to silently ignore the stride. + v0 = np.random.rand(50, 50) + try: + v = self.spcreator(v0)[0:25:2, 2:30:3] + except ValueError: + # if unsupported + raise pytest.skip("feature not implemented") + + assert_array_equal(v.toarray(), v0[0:25:2, 2:30:3]) + + def test_slicing_2(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + # [i,j] + assert_equal(A[2,3], B[2,3]) + assert_equal(A[-1,8], B[-1,8]) + assert_equal(A[-1,-2],B[-1,-2]) + assert_equal(A[array(-1),-2],B[-1,-2]) + assert_equal(A[-1,array(-2)],B[-1,-2]) + assert_equal(A[array(-1),array(-2)],B[-1,-2]) + + # [i,1:2] + assert_equal(A[2, :].toarray(), B[2, :]) + assert_equal(A[2, 5:-2].toarray(), B[2, 5:-2]) + assert_equal(A[array(2), 5:-2].toarray(), B[2, 5:-2]) + + # [1:2,j] + assert_equal(A[:, 2].toarray(), B[:, 2]) + assert_equal(A[3:4, 9].toarray(), B[3:4, 9]) + assert_equal(A[1:4, -5].toarray(), B[1:4, -5]) + assert_equal(A[2:-1, 3].toarray(), B[2:-1, 3]) + assert_equal(A[2:-1, array(3)].toarray(), B[2:-1, 3]) + + # [1:2,1:2] + assert_equal(A[1:2, 1:2].toarray(), B[1:2, 1:2]) + assert_equal(A[4:, 3:].toarray(), B[4:, 3:]) + assert_equal(A[:4, :5].toarray(), B[:4, :5]) + assert_equal(A[2:-1, :5].toarray(), B[2:-1, :5]) + + # [i] + assert_equal(A[1, :].toarray(), B[1, :]) + assert_equal(A[-2, :].toarray(), B[-2, :]) + assert_equal(A[array(-2), :].toarray(), B[-2, :]) + + # [1:2] + assert_equal(A[1:4].toarray(), B[1:4]) + assert_equal(A[1:-2].toarray(), B[1:-2]) + + # Check bug reported by Robert Cimrman: + # http://thread.gmane.org/gmane.comp.python.scientific.devel/7986 (dead link) + s = slice(int8(2),int8(4),None) + assert_equal(A[s, :].toarray(), B[2:4, :]) + assert_equal(A[:, s].toarray(), B[:, 2:4]) + + def test_slicing_3(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + s_ = np.s_ + slices = [s_[:2], s_[1:2], s_[3:], s_[3::2], + s_[15:20], s_[3:2], + s_[8:3:-1], s_[4::-2], s_[:5:-1], + 0, 1, s_[:], s_[1:5], -1, -2, -5, + array(-1), np.int8(-3)] + + def check_1(a): + x = A[a] + y = B[a] + if y.shape == (): + assert_equal(x, y, repr(a)) + else: + if x.size == 0 and y.size == 0: + pass + else: + assert_array_equal(x.toarray(), y, repr(a)) + + for j, a in enumerate(slices): + check_1(a) + + def check_2(a, b): + # Indexing np.matrix with 0-d arrays seems to be broken, + # as they seem not to be treated as scalars. + # https://github.com/numpy/numpy/issues/3110 + if isinstance(a, np.ndarray): + ai = int(a) + else: + ai = a + if isinstance(b, np.ndarray): + bi = int(b) + else: + bi = b + + x = A[a, b] + y = B[ai, bi] + + if y.shape == (): + assert_equal(x, y, repr((a, b))) + else: + if x.size == 0 and y.size == 0: + pass + else: + assert_array_equal(x.toarray(), y, repr((a, b))) + + for i, a in enumerate(slices): + for j, b in enumerate(slices): + check_2(a, b) + + # Check out of bounds etc. systematically + extra_slices = [] + for a, b, c in itertools.product(*([(None, 0, 1, 2, 5, 15, + -1, -2, 5, -15)]*3)): + if c == 0: + continue + extra_slices.append(slice(a, b, c)) + + for a in extra_slices: + check_2(a, a) + check_2(a, -2) + check_2(-2, a) + + def test_ellipsis_slicing(self): + b = asmatrix(arange(50).reshape(5,10)) + a = self.spcreator(b) + + assert_array_equal(a[...].toarray(), b[...].A) + assert_array_equal(a[...,].toarray(), b[...,].A) + + assert_array_equal(a[1, ...].toarray(), b[1, ...].A) + assert_array_equal(a[..., 1].toarray(), b[..., 1].A) + assert_array_equal(a[1:, ...].toarray(), b[1:, ...].A) + assert_array_equal(a[..., 1:].toarray(), b[..., 1:].A) + + assert_array_equal(a[1:, 1, ...].toarray(), b[1:, 1, ...].A) + assert_array_equal(a[1, ..., 1:].toarray(), b[1, ..., 1:].A) + # These return ints + assert_equal(a[1, 1, ...], b[1, 1, ...]) + assert_equal(a[1, ..., 1], b[1, ..., 1]) + + def test_multiple_ellipsis_slicing(self): + a = self.spcreator(arange(6).reshape(3, 2)) + + with pytest.raises(IndexError, + match='an index can only have a single ellipsis'): + a[..., ...] + with pytest.raises(IndexError, + match='an index can only have a single ellipsis'): + a[..., 1, ...] + + +class _TestSlicingAssign: + def test_slice_scalar_assign(self): + A = self.spcreator((5, 5)) + B = np.zeros((5, 5)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + for C in [A, B]: + C[0:1,1] = 1 + C[3:0,0] = 4 + C[3:4,0] = 9 + C[0,4:] = 1 + C[3::-1,4:] = 9 + assert_array_equal(A.toarray(), B) + + def test_slice_assign_2(self): + n, m = (5, 10) + + def _test_set(i, j): + msg = f"i={i!r}; j={j!r}" + A = self.spcreator((n, m)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[i, j] = 1 + B = np.zeros((n, m)) + B[i, j] = 1 + assert_array_almost_equal(A.toarray(), B, err_msg=msg) + # [i,1:2] + for i, j in [(2, slice(3)), (2, slice(None, 10, 4)), (2, slice(5, -2)), + (array(2), slice(5, -2))]: + _test_set(i, j) + + def test_self_self_assignment(self): + # Tests whether a row of one lil_matrix can be assigned to + # another. + B = self.spcreator((4,3)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + B[0,0] = 2 + B[1,2] = 7 + B[2,1] = 3 + B[3,0] = 10 + + A = B / 10 + B[0,:] = A[0,:] + assert_array_equal(A[0,:].toarray(), B[0,:].toarray()) + + A = B / 10 + B[:,:] = A[:1,:1] + assert_array_equal(np.zeros((4,3)) + A[0,0], B.toarray()) + + A = B / 10 + B[:-1,0] = A[0,:].T + assert_array_equal(A[0,:].toarray().T, B[:-1,0].toarray()) + + def test_slice_assignment(self): + B = self.spcreator((4,3)) + expected = array([[10,0,0], + [0,0,6], + [0,14,0], + [0,0,0]]) + block = [[1,0],[0,4]] + + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + B[0,0] = 5 + B[1,2] = 3 + B[2,1] = 7 + B[:,:] = B+B + assert_array_equal(B.toarray(), expected) + + B[:2,:2] = csc_matrix(array(block)) + assert_array_equal(B.toarray()[:2, :2], block) + + def test_sparsity_modifying_assignment(self): + B = self.spcreator((4,3)) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + B[0,0] = 5 + B[1,2] = 3 + B[2,1] = 7 + B[3,0] = 10 + B[:3] = csr_matrix(np.eye(3)) + + expected = array([[1,0,0],[0,1,0],[0,0,1],[10,0,0]]) + assert_array_equal(B.toarray(), expected) + + def test_set_slice(self): + A = self.spcreator((5,10)) + B = array(zeros((5, 10), float)) + s_ = np.s_ + slices = [s_[:2], s_[1:2], s_[3:], s_[3::2], + s_[8:3:-1], s_[4::-2], s_[:5:-1], + 0, 1, s_[:], s_[1:5], -1, -2, -5, + array(-1), np.int8(-3)] + + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + for j, a in enumerate(slices): + A[a] = j + B[a] = j + assert_array_equal(A.toarray(), B, repr(a)) + + for i, a in enumerate(slices): + for j, b in enumerate(slices): + A[a,b] = 10*i + 1000*(j+1) + B[a,b] = 10*i + 1000*(j+1) + assert_array_equal(A.toarray(), B, repr((a, b))) + + A[0, 1:10:2] = range(1, 10, 2) + B[0, 1:10:2] = range(1, 10, 2) + assert_array_equal(A.toarray(), B) + A[1:5:2, 0] = np.arange(1, 5, 2)[:, None] + B[1:5:2, 0] = np.arange(1, 5, 2)[:] + assert_array_equal(A.toarray(), B) + + # The next commands should raise exceptions + assert_raises(ValueError, A.__setitem__, (0, 0), list(range(100))) + assert_raises(ValueError, A.__setitem__, (0, 0), arange(100)) + assert_raises(ValueError, A.__setitem__, (0, slice(None)), + list(range(100))) + assert_raises(ValueError, A.__setitem__, (slice(None), 1), + list(range(100))) + assert_raises(ValueError, A.__setitem__, (slice(None), 1), A.copy()) + assert_raises(ValueError, A.__setitem__, + ([[1, 2, 3], [0, 3, 4]], [1, 2, 3]), [1, 2, 3, 4]) + assert_raises(ValueError, A.__setitem__, + ([[1, 2, 3], [0, 3, 4], [4, 1, 3]], + [[1, 2, 4], [0, 1, 3]]), [2, 3, 4]) + assert_raises(ValueError, A.__setitem__, (slice(4), 0), + [[1, 2], [3, 4]]) + + def test_assign_empty(self): + A = self.spcreator(np.ones((2, 3))) + B = self.spcreator((1, 2)) + A[1, :2] = B + assert_array_equal(A.toarray(), [[1, 1, 1], [0, 0, 1]]) + + def test_assign_1d_slice(self): + A = self.spcreator(np.ones((3, 3))) + x = np.zeros(3) + A[:, 0] = x + A[1, :] = x + assert_array_equal(A.toarray(), [[0, 1, 1], [0, 0, 0], [0, 1, 1]]) + + +class _TestFancyIndexing: + """Tests fancy indexing features. The tests for any matrix formats + that implement these features should derive from this class. + """ + + def test_dtype_preservation_empty_index(self): + # This should be parametrized with pytest, but something in the parent + # class creation used in this file breaks pytest.mark.parametrize. + for dt in [np.int16, np.int32, np.float32, np.float64]: + A = self.spcreator((3, 2), dtype=dt) + assert_equal(A[:, [False, False]].dtype, dt) + assert_equal(A[[False, False, False], :].dtype, dt) + assert_equal(A[:, []].dtype, dt) + assert_equal(A[[], :].dtype, dt) + + def test_bad_index(self): + A = self.spcreator(np.zeros([5, 5])) + assert_raises((IndexError, ValueError, TypeError), A.__getitem__, "foo") + assert_raises((IndexError, ValueError, TypeError), A.__getitem__, (2, "foo")) + assert_raises((IndexError, ValueError), A.__getitem__, + ([1, 2, 3], [1, 2, 3, 4])) + + def test_fancy_indexing(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + # [i] + assert_equal(A[[1, 3]].toarray(), B[[1, 3]]) + + # [i,[1,2]] + assert_equal(A[3, [1, 3]].toarray(), B[3, [1, 3]]) + assert_equal(A[-1, [2, -5]].toarray(), B[-1, [2, -5]]) + assert_equal(A[array(-1), [2, -5]].toarray(), B[-1, [2, -5]]) + assert_equal(A[-1, array([2, -5])].toarray(), B[-1, [2, -5]]) + assert_equal(A[array(-1), array([2, -5])].toarray(), B[-1, [2, -5]]) + + # [1:2,[1,2]] + assert_equal(A[:, [2, 8, 3, -1]].toarray(), B[:, [2, 8, 3, -1]]) + assert_equal(A[3:4, [9]].toarray(), B[3:4, [9]]) + assert_equal(A[1:4, [-1, -5]].toarray(), B[1:4, [-1, -5]]) + assert_equal(A[1:4, array([-1, -5])].toarray(), B[1:4, [-1, -5]]) + + # [[1,2],j] + assert_equal(A[[1, 3], 3].toarray(), B[[1, 3], 3]) + assert_equal(A[[2, -5], -4].toarray(), B[[2, -5], -4]) + assert_equal(A[array([2, -5]), -4].toarray(), B[[2, -5], -4]) + assert_equal(A[[2, -5], array(-4)].toarray(), B[[2, -5], -4]) + assert_equal(A[array([2, -5]), array(-4)].toarray(), B[[2, -5], -4]) + + # [[1,2],1:2] + assert_equal(A[[1, 3], :].toarray(), B[[1, 3], :]) + assert_equal(A[[2, -5], 8:-1].toarray(), B[[2, -5], 8:-1]) + assert_equal(A[array([2, -5]), 8:-1].toarray(), B[[2, -5], 8:-1]) + + # [[1,2],[1,2]] + assert_equal(toarray(A[[1, 3], [2, 4]]), B[[1, 3], [2, 4]]) + assert_equal(toarray(A[[-1, -3], [2, -4]]), B[[-1, -3], [2, -4]]) + assert_equal( + toarray(A[array([-1, -3]), [2, -4]]), B[[-1, -3], [2, -4]] + ) + assert_equal( + toarray(A[[-1, -3], array([2, -4])]), B[[-1, -3], [2, -4]] + ) + assert_equal( + toarray(A[array([-1, -3]), array([2, -4])]), B[[-1, -3], [2, -4]] + ) + + # [[[1],[2]],[1,2]] + assert_equal(A[[[1], [3]], [2, 4]].toarray(), B[[[1], [3]], [2, 4]]) + assert_equal( + A[[[-1], [-3], [-2]], [2, -4]].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[array([[-1], [-3], [-2]]), [2, -4]].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[[[-1], [-3], [-2]], array([2, -4])].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[array([[-1], [-3], [-2]]), array([2, -4])].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + + # [[1,2]] + assert_equal(A[[1, 3]].toarray(), B[[1, 3]]) + assert_equal(A[[-1, -3]].toarray(), B[[-1, -3]]) + assert_equal(A[array([-1, -3])].toarray(), B[[-1, -3]]) + + # [[1,2],:][:,[1,2]] + assert_equal( + A[[1, 3], :][:, [2, 4]].toarray(), B[[1, 3], :][:, [2, 4]] + ) + assert_equal( + A[[-1, -3], :][:, [2, -4]].toarray(), B[[-1, -3], :][:, [2, -4]] + ) + assert_equal( + A[array([-1, -3]), :][:, array([2, -4])].toarray(), + B[[-1, -3], :][:, [2, -4]] + ) + + # [:,[1,2]][[1,2],:] + assert_equal( + A[:, [1, 3]][[2, 4], :].toarray(), B[:, [1, 3]][[2, 4], :] + ) + assert_equal( + A[:, [-1, -3]][[2, -4], :].toarray(), B[:, [-1, -3]][[2, -4], :] + ) + assert_equal( + A[:, array([-1, -3])][array([2, -4]), :].toarray(), + B[:, [-1, -3]][[2, -4], :] + ) + + # Check bug reported by Robert Cimrman: + # http://thread.gmane.org/gmane.comp.python.scientific.devel/7986 (dead link) + s = slice(int8(2),int8(4),None) + assert_equal(A[s, :].toarray(), B[2:4, :]) + assert_equal(A[:, s].toarray(), B[:, 2:4]) + + # Regression for gh-4917: index with tuple of 2D arrays + i = np.array([[1]], dtype=int) + assert_equal(A[i, i].toarray(), B[i, i]) + + # Regression for gh-4917: index with tuple of empty nested lists + assert_equal(A[[[]], [[]]].toarray(), B[[[]], [[]]]) + + def test_fancy_indexing_randomized(self): + np.random.seed(1234) # make runs repeatable + + NUM_SAMPLES = 50 + M = 6 + N = 4 + + D = asmatrix(np.random.rand(M,N)) + D = np.multiply(D, D > 0.5) + + I = np.random.randint(-M + 1, M, size=NUM_SAMPLES) + J = np.random.randint(-N + 1, N, size=NUM_SAMPLES) + + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + I_bad = I + M + J_bad = J - N + + assert_raises(IndexError, S.__getitem__, (I_bad,J)) + assert_raises(IndexError, S.__getitem__, (I,J_bad)) + + def test_missized_masking(self): + M, N = 5, 10 + + B = asmatrix(arange(M * N).reshape(M, N)) + A = self.spcreator(B) + + # Content of mask shouldn't matter, only its size + row_long = np.ones(M + 1, dtype=bool) + row_short = np.ones(M - 1, dtype=bool) + col_long = np.ones(N + 2, dtype=bool) + col_short = np.ones(N - 2, dtype=bool) + + with pytest.raises( + IndexError, + match=rf"boolean row index has incorrect length: {M + 1} instead of {M}" + ): + _ = A[row_long, :] + with pytest.raises( + IndexError, + match=rf"boolean row index has incorrect length: {M - 1} instead of {M}" + ): + _ = A[row_short, :] + + for i, j in itertools.product( + (row_long, row_short, slice(None)), + (col_long, col_short, slice(None)), + ): + if isinstance(i, slice) and isinstance(j, slice): + continue + with pytest.raises( + IndexError, + match=r"boolean \w+ index has incorrect length" + ): + _ = A[i, j] + + def test_fancy_indexing_boolean(self): + np.random.seed(1234) # make runs repeatable + + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + I = np.array(np.random.randint(0, 2, size=5), dtype=bool) + J = np.array(np.random.randint(0, 2, size=10), dtype=bool) + X = np.array(np.random.randint(0, 2, size=(5, 10)), dtype=bool) + + assert_equal(toarray(A[I]), B[I]) + assert_equal(toarray(A[:, J]), B[:, J]) + assert_equal(toarray(A[X]), B[X]) + assert_equal(toarray(A[B > 9]), B[B > 9]) + + I = np.array([True, False, True, True, False]) + J = np.array([False, True, True, False, True, + False, False, False, False, False]) + + assert_equal(toarray(A[I, J]), B[I, J]) + + Z1 = np.zeros((6, 11), dtype=bool) + Z2 = np.zeros((6, 11), dtype=bool) + Z2[0,-1] = True + Z3 = np.zeros((6, 11), dtype=bool) + Z3[-1,0] = True + + assert_raises(IndexError, A.__getitem__, Z1) + assert_raises(IndexError, A.__getitem__, Z2) + assert_raises(IndexError, A.__getitem__, Z3) + assert_raises((IndexError, ValueError), A.__getitem__, (X, 1)) + + def test_fancy_indexing_sparse_boolean(self): + np.random.seed(1234) # make runs repeatable + + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + X = np.array(np.random.randint(0, 2, size=(5, 10)), dtype=bool) + + Xsp = csr_matrix(X) + + assert_equal(toarray(A[Xsp]), B[X]) + assert_equal(toarray(A[A > 9]), B[B > 9]) + + Z = np.array(np.random.randint(0, 2, size=(5, 11)), dtype=bool) + Y = np.array(np.random.randint(0, 2, size=(6, 10)), dtype=bool) + + Zsp = csr_matrix(Z) + Ysp = csr_matrix(Y) + + assert_raises(IndexError, A.__getitem__, Zsp) + assert_raises(IndexError, A.__getitem__, Ysp) + assert_raises((IndexError, ValueError), A.__getitem__, (Xsp, 1)) + + def test_fancy_indexing_regression_3087(self): + mat = self.spcreator(array([[1, 0, 0], [0,1,0], [1,0,0]])) + desired_cols = np.ravel(mat.sum(0)) > 0 + assert_equal(mat[:, desired_cols].toarray(), [[1, 0], [0, 1], [1, 0]]) + + def test_fancy_indexing_seq_assign(self): + mat = self.spcreator(array([[1, 0], [0, 1]])) + assert_raises(ValueError, mat.__setitem__, (0, 0), np.array([1,2])) + + def test_fancy_indexing_2d_assign(self): + # regression test for gh-10695 + mat = self.spcreator(array([[1, 0], [2, 3]])) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "Changing the sparsity structure") + mat[[0, 1], [1, 1]] = mat[[1, 0], [0, 0]] + assert_equal(toarray(mat), array([[1, 2], [2, 1]])) + + def test_fancy_indexing_empty(self): + B = asmatrix(arange(50).reshape(5,10)) + B[1,:] = 0 + B[:,2] = 0 + B[3,6] = 0 + A = self.spcreator(B) + + K = np.array([False, False, False, False, False]) + assert_equal(toarray(A[K]), B[K]) + K = np.array([], dtype=int) + assert_equal(toarray(A[K]), B[K]) + assert_equal(toarray(A[K, K]), B[K, K]) + J = np.array([0, 1, 2, 3, 4], dtype=int)[:,None] + assert_equal(toarray(A[K, J]), B[K, J]) + assert_equal(toarray(A[J, K]), B[J, K]) + + +@contextlib.contextmanager +def check_remains_sorted(X): + """Checks that sorted indices property is retained through an operation + """ + if not hasattr(X, 'has_sorted_indices') or not X.has_sorted_indices: + yield + return + yield + indices = X.indices.copy() + X.has_sorted_indices = False + X.sort_indices() + assert_array_equal(indices, X.indices, + 'Expected sorted indices, found unsorted') + + +class _TestFancyIndexingAssign: + def test_bad_index_assign(self): + A = self.spcreator(np.zeros([5, 5])) + assert_raises((IndexError, ValueError, TypeError), A.__setitem__, "foo", 2) + assert_raises((IndexError, ValueError, TypeError), A.__setitem__, (2, "foo"), 5) + + def test_fancy_indexing_set(self): + n, m = (5, 10) + + def _test_set_slice(i, j): + A = self.spcreator((n, m)) + B = asmatrix(np.zeros((n, m))) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + B[i, j] = 1 + with check_remains_sorted(A): + A[i, j] = 1 + assert_array_almost_equal(A.toarray(), B) + # [1:2,1:2] + for i, j in [((2, 3, 4), slice(None, 10, 4)), + (np.arange(3), slice(5, -2)), + (slice(2, 5), slice(5, -2))]: + _test_set_slice(i, j) + for i, j in [(np.arange(3), np.arange(3)), ((0, 3, 4), (1, 2, 4))]: + _test_set_slice(i, j) + + def test_fancy_assignment_dtypes(self): + def check(dtype): + A = self.spcreator((5, 5), dtype=dtype) + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[[0,1],[0,1]] = dtype.type(1) + assert_equal(A.sum(), dtype.type(1)*2) + A[0:2,0:2] = dtype.type(1.0) + assert_equal(A.sum(), dtype.type(1)*4) + A[2,2] = dtype.type(1.0) + assert_equal(A.sum(), dtype.type(1)*4 + dtype.type(1)) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_sequence_assignment(self): + A = self.spcreator((4,3)) + B = self.spcreator(eye(3,4)) + + i0 = [0,1,2] + i1 = (0,1,2) + i2 = array(i0) + + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + with check_remains_sorted(A): + A[0,i0] = B[i0,0].T + A[1,i1] = B[i1,1].T + A[2,i2] = B[i2,2].T + assert_array_equal(A.toarray(), B.T.toarray()) + + # column slice + A = self.spcreator((2,3)) + with check_remains_sorted(A): + A[1,1:3] = [10,20] + assert_array_equal(A.toarray(), [[0, 0, 0], [0, 10, 20]]) + + # row slice + A = self.spcreator((3,2)) + with check_remains_sorted(A): + A[1:3,1] = [[10],[20]] + assert_array_equal(A.toarray(), [[0, 0], [0, 10], [0, 20]]) + + # both slices + A = self.spcreator((3,3)) + B = asmatrix(np.zeros((3,3))) + with check_remains_sorted(A): + for C in [A, B]: + C[[0,1,2], [0,1,2]] = [4,5,6] + assert_array_equal(A.toarray(), B) + + # both slices (2) + A = self.spcreator((4, 3)) + with check_remains_sorted(A): + A[(1, 2, 3), (0, 1, 2)] = [1, 2, 3] + assert_almost_equal(A.sum(), 6) + B = asmatrix(np.zeros((4, 3))) + B[(1, 2, 3), (0, 1, 2)] = [1, 2, 3] + assert_array_equal(A.toarray(), B) + + def test_fancy_assign_empty(self): + B = asmatrix(arange(50).reshape(5,10)) + B[1,:] = 0 + B[:,2] = 0 + B[3,6] = 0 + A = self.spcreator(B) + + K = np.array([False, False, False, False, False]) + A[K] = 42 + assert_equal(toarray(A), B) + + K = np.array([], dtype=int) + A[K] = 42 + assert_equal(toarray(A), B) + A[K,K] = 42 + assert_equal(toarray(A), B) + + J = np.array([0, 1, 2, 3, 4], dtype=int)[:,None] + A[K,J] = 42 + assert_equal(toarray(A), B) + A[J,K] = 42 + assert_equal(toarray(A), B) + + +class _TestFancyMultidim: + def test_fancy_indexing_ndarray(self): + sets = [ + (np.array([[1], [2], [3]]), np.array([3, 4, 2])), + (np.array([[1], [2], [3]]), np.array([[3, 4, 2]])), + (np.array([[1, 2, 3]]), np.array([[3], [4], [2]])), + (np.array([1, 2, 3]), np.array([[3], [4], [2]])), + (np.array([[1, 2, 3], [3, 4, 2]]), + np.array([[5, 6, 3], [2, 3, 1]])) + ] + # These inputs generate 3-D outputs + # (np.array([[[1], [2], [3]], [[3], [4], [2]]]), + # np.array([[[5], [6], [3]], [[2], [3], [1]]])), + + for I, J in sets: + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + I_bad = I + 5 + J_bad = J + 7 + + assert_raises(IndexError, S.__getitem__, (I_bad,J)) + assert_raises(IndexError, S.__getitem__, (I,J_bad)) + + # This would generate 3-D arrays -- not supported + assert_raises(IndexError, S.__getitem__, ([I, I], slice(None))) + assert_raises(IndexError, S.__getitem__, (slice(None), [J, J])) + + +class _TestFancyMultidimAssign: + def test_fancy_assign_ndarray(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + X = np.random.rand(2, 3) + + I = np.array([[1, 2, 3], [3, 4, 2]]) + J = np.array([[5, 6, 3], [2, 3, 1]]) + + with check_remains_sorted(S): + S[I,J] = X + D[I,J] = X + assert_equal(S.toarray(), D) + + I_bad = I + 5 + J_bad = J + 7 + + C = [1, 2, 3] + + with check_remains_sorted(S): + S[I,J] = C + D[I,J] = C + assert_equal(S.toarray(), D) + + with check_remains_sorted(S): + S[I,J] = 3 + D[I,J] = 3 + assert_equal(S.toarray(), D) + + assert_raises(IndexError, S.__setitem__, (I_bad,J), C) + assert_raises(IndexError, S.__setitem__, (I,J_bad), C) + + def test_fancy_indexing_multidim_set(self): + n, m = (5, 10) + + def _test_set_slice(i, j): + A = self.spcreator((n, m)) + with check_remains_sorted(A), suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[i, j] = 1 + B = asmatrix(np.zeros((n, m))) + B[i, j] = 1 + assert_array_almost_equal(A.toarray(), B) + # [[[1, 2], [1, 2]], [1, 2]] + for i, j in [(np.array([[1, 2], [1, 3]]), [1, 3]), + (np.array([0, 4]), [[0, 3], [1, 2]]), + ([[1, 2, 3], [0, 2, 4]], [[0, 4, 3], [4, 1, 2]])]: + _test_set_slice(i, j) + + def test_fancy_assign_list(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + X = np.random.rand(2, 3) + + I = [[1, 2, 3], [3, 4, 2]] + J = [[5, 6, 3], [2, 3, 1]] + + S[I,J] = X + D[I,J] = X + assert_equal(S.toarray(), D) + + I_bad = [[ii + 5 for ii in i] for i in I] + J_bad = [[jj + 7 for jj in j] for j in J] + C = [1, 2, 3] + + S[I,J] = C + D[I,J] = C + assert_equal(S.toarray(), D) + + S[I,J] = 3 + D[I,J] = 3 + assert_equal(S.toarray(), D) + + assert_raises(IndexError, S.__setitem__, (I_bad,J), C) + assert_raises(IndexError, S.__setitem__, (I,J_bad), C) + + def test_fancy_assign_slice(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + I = [1, 2, 3, 3, 4, 2] + J = [5, 6, 3, 2, 3, 1] + + I_bad = [ii + 5 for ii in I] + J_bad = [jj + 7 for jj in J] + + C1 = [1, 2, 3, 4, 5, 6, 7] + C2 = np.arange(5)[:, None] + assert_raises(IndexError, S.__setitem__, (I_bad, slice(None)), C1) + assert_raises(IndexError, S.__setitem__, (slice(None), J_bad), C2) + + +class _TestArithmetic: + """ + Test real/complex arithmetic + """ + def __arith_init(self): + # these can be represented exactly in FP (so arithmetic should be exact) + self.__A = array([[-1.5, 6.5, 0, 2.25, 0, 0], + [3.125, -7.875, 0.625, 0, 0, 0], + [0, 0, -0.125, 1.0, 0, 0], + [0, 0, 8.375, 0, 0, 0]], 'float64') + self.__B = array([[0.375, 0, 0, 0, -5, 2.5], + [14.25, -3.75, 0, 0, -0.125, 0], + [0, 7.25, 0, 0, 0, 0], + [18.5, -0.0625, 0, 0, 0, 0]], 'complex128') + self.__B.imag = array([[1.25, 0, 0, 0, 6, -3.875], + [2.25, 4.125, 0, 0, 0, 2.75], + [0, 4.125, 0, 0, 0, 0], + [-0.0625, 0, 0, 0, 0, 0]], 'float64') + + # fractions are all x/16ths + assert_array_equal((self.__A*16).astype('int32'),16*self.__A) + assert_array_equal((self.__B.real*16).astype('int32'),16*self.__B.real) + assert_array_equal((self.__B.imag*16).astype('int32'),16*self.__B.imag) + + self.__Asp = self.spcreator(self.__A) + self.__Bsp = self.spcreator(self.__B) + + def test_add_sub(self): + self.__arith_init() + + # basic tests + assert_array_equal( + (self.__Asp + self.__Bsp).toarray(), self.__A + self.__B + ) + + # check conversions + for x in supported_dtypes: + with np.errstate(invalid="ignore"): + A = self.__A.astype(x) + Asp = self.spcreator(A) + for y in supported_dtypes: + if not np.issubdtype(y, np.complexfloating): + with np.errstate(invalid="ignore"): + B = self.__B.real.astype(y) + else: + B = self.__B.astype(y) + Bsp = self.spcreator(B) + + # addition + D1 = A + B + S1 = Asp + Bsp + + assert_equal(S1.dtype,D1.dtype) + assert_array_equal(S1.toarray(), D1) + assert_array_equal(Asp + B,D1) # check sparse + dense + assert_array_equal(A + Bsp,D1) # check dense + sparse + + # subtraction + if np.dtype('bool') in [x, y]: + # boolean array subtraction deprecated in 1.9.0 + continue + + D1 = A - B + S1 = Asp - Bsp + + assert_equal(S1.dtype,D1.dtype) + assert_array_equal(S1.toarray(), D1) + assert_array_equal(Asp - B,D1) # check sparse - dense + assert_array_equal(A - Bsp,D1) # check dense - sparse + + def test_mu(self): + self.__arith_init() + + # basic tests + assert_array_equal((self.__Asp @ self.__Bsp.T).toarray(), + self.__A @ self.__B.T) + + for x in supported_dtypes: + with np.errstate(invalid="ignore"): + A = self.__A.astype(x) + Asp = self.spcreator(A) + for y in supported_dtypes: + if np.issubdtype(y, np.complexfloating): + B = self.__B.astype(y) + else: + with np.errstate(invalid="ignore"): + B = self.__B.real.astype(y) + Bsp = self.spcreator(B) + + D1 = A @ B.T + S1 = Asp @ Bsp.T + + assert_allclose(S1.toarray(), D1, + atol=1e-14*abs(D1).max()) + assert_equal(S1.dtype,D1.dtype) + + +class _TestMinMax: + def test_minmax(self): + for dtype in [np.float32, np.float64, np.int32, np.int64, np.complex128]: + D = np.arange(20, dtype=dtype).reshape(5,4) + + X = self.spcreator(D) + assert_equal(X.min(), 0) + assert_equal(X.max(), 19) + assert_equal(X.min().dtype, dtype) + assert_equal(X.max().dtype, dtype) + + D *= -1 + X = self.spcreator(D) + assert_equal(X.min(), -19) + assert_equal(X.max(), 0) + + D += 5 + X = self.spcreator(D) + assert_equal(X.min(), -14) + assert_equal(X.max(), 5) + + # try a fully dense matrix + X = self.spcreator(np.arange(1, 10).reshape(3, 3)) + assert_equal(X.min(), 1) + assert_equal(X.min().dtype, X.dtype) + + X = -X + assert_equal(X.max(), -1) + + # and a fully sparse matrix + Z = self.spcreator(np.zeros((1, 1))) + assert_equal(Z.min(), 0) + assert_equal(Z.max(), 0) + assert_equal(Z.max().dtype, Z.dtype) + + # another test + D = np.arange(20, dtype=float).reshape(5,4) + D[0:2, :] = 0 + X = self.spcreator(D) + assert_equal(X.min(), 0) + assert_equal(X.max(), 19) + + # zero-size matrices + for D in [np.zeros((0, 0)), np.zeros((0, 10)), np.zeros((10, 0))]: + X = self.spcreator(D) + assert_raises(ValueError, X.min) + assert_raises(ValueError, X.max) + + def test_minmax_axis(self): + D = np.arange(50).reshape(5, 10) + # completely empty rows, leaving some completely full: + D[1, :] = 0 + # empty at end for reduceat: + D[:, 9] = 0 + # partial rows/cols: + D[3, 3] = 0 + # entries on either side of 0: + D[2, 2] = -1 + X = self.spcreator(D) + + axes = [-2, -1, 0, 1] + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + # full matrix + D = np.arange(1, 51).reshape(10, 5) + X = self.spcreator(D) + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + # empty matrix + D = np.zeros((10, 5)) + X = self.spcreator(D) + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + axes_even = [0, -2] + axes_odd = [1, -1] + + # zero-size matrices + D = np.zeros((0, 10)) + X = self.spcreator(D) + for axis in axes_even: + assert_raises(ValueError, X.min, axis=axis) + assert_raises(ValueError, X.max, axis=axis) + for axis in axes_odd: + assert_array_equal(np.zeros((0, 1)), X.min(axis=axis).toarray()) + assert_array_equal(np.zeros((0, 1)), X.max(axis=axis).toarray()) + + D = np.zeros((10, 0)) + X = self.spcreator(D) + for axis in axes_odd: + assert_raises(ValueError, X.min, axis=axis) + assert_raises(ValueError, X.max, axis=axis) + for axis in axes_even: + assert_array_equal(np.zeros((1, 0)), X.min(axis=axis).toarray()) + assert_array_equal(np.zeros((1, 0)), X.max(axis=axis).toarray()) + + def test_nanminmax(self): + D = matrix(np.arange(50).reshape(5,10), dtype=float) + D[1, :] = 0 + D[:, 9] = 0 + D[3, 3] = 0 + D[2, 2] = -1 + D[4, 2] = np.nan + D[1, 4] = np.nan + X = self.spcreator(D) + + X_nan_maximum = X.nanmax() + assert np.isscalar(X_nan_maximum) + assert X_nan_maximum == np.nanmax(D) + + X_nan_minimum = X.nanmin() + assert np.isscalar(X_nan_minimum) + assert X_nan_minimum == np.nanmin(D) + + axes = [-2, -1, 0, 1] + for axis in axes: + X_nan_maxima = X.nanmax(axis=axis) + assert isinstance(X_nan_maxima, coo_matrix) + assert_allclose(X_nan_maxima.toarray(), + np.nanmax(D, axis=axis)) + + X_nan_minima = X.nanmin(axis=axis) + assert isinstance(X_nan_minima, coo_matrix) + assert_allclose(X_nan_minima.toarray(), + np.nanmin(D, axis=axis)) + + def test_minmax_invalid_params(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + for fname in ('min', 'max'): + func = getattr(datsp, fname) + assert_raises(ValueError, func, axis=3) + assert_raises(TypeError, func, axis=(0, 1)) + assert_raises(TypeError, func, axis=1.5) + assert_raises(ValueError, func, axis=1, out=1) + + def test_numpy_minmax(self): + # See gh-5987 + # xref gh-7460 in 'numpy' + from scipy.sparse import _data + + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + # We are only testing sparse matrices who have + # implemented 'min' and 'max' because they are + # the ones with the compatibility issues with + # the 'numpy' implementation. + if isinstance(datsp, _data._minmax_mixin): + assert_array_equal(np.min(datsp), np.min(dat)) + assert_array_equal(np.max(datsp), np.max(dat)) + + def test_argmax(self): + from scipy.sparse import _data + D1 = np.array([ + [-1, 5, 2, 3], + [0, 0, -1, -2], + [-1, -2, -3, -4], + [1, 2, 3, 4], + [1, 2, 0, 0], + ]) + D2 = D1.transpose() + # Non-regression test cases for gh-16929. + D3 = np.array([[4, 3], [7, 5]]) + D4 = np.array([[4, 3], [7, 0]]) + D5 = np.array([[5, 5, 3], [4, 9, 10], [3, 4, 9]]) + + for D in [D1, D2, D3, D4, D5]: + mat = self.spcreator(D) + if not isinstance(mat, _data._minmax_mixin): + continue + + assert_equal(mat.argmax(), np.argmax(D)) + assert_equal(mat.argmin(), np.argmin(D)) + + assert_equal(mat.argmax(axis=0), + asmatrix(np.argmax(D, axis=0))) + assert_equal(mat.argmin(axis=0), + asmatrix(np.argmin(D, axis=0))) + + assert_equal(mat.argmax(axis=1), + asmatrix(np.argmax(D, axis=1).reshape(-1, 1))) + assert_equal(mat.argmin(axis=1), + asmatrix(np.argmin(D, axis=1).reshape(-1, 1))) + + D1 = np.empty((0, 5)) + D2 = np.empty((5, 0)) + + for axis in [None, 0]: + mat = self.spcreator(D1) + assert_raises(ValueError, mat.argmax, axis=axis) + assert_raises(ValueError, mat.argmin, axis=axis) + + for axis in [None, 1]: + mat = self.spcreator(D2) + assert_raises(ValueError, mat.argmax, axis=axis) + assert_raises(ValueError, mat.argmin, axis=axis) + + +class _TestGetNnzAxis: + def test_getnnz_axis(self): + dat = array([[0, 2], + [3, 5], + [-6, 9]]) + bool_dat = dat.astype(bool) + datsp = self.spcreator(dat) + + accepted_return_dtypes = (np.int32, np.int64) + + assert_array_equal(bool_dat.sum(axis=None), datsp.getnnz(axis=None)) + assert_array_equal(bool_dat.sum(), datsp.getnnz()) + assert_array_equal(bool_dat.sum(axis=0), datsp.getnnz(axis=0)) + assert_in(datsp.getnnz(axis=0).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=1), datsp.getnnz(axis=1)) + assert_in(datsp.getnnz(axis=1).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=-2), datsp.getnnz(axis=-2)) + assert_in(datsp.getnnz(axis=-2).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=-1), datsp.getnnz(axis=-1)) + assert_in(datsp.getnnz(axis=-1).dtype, accepted_return_dtypes) + + assert_raises(ValueError, datsp.getnnz, axis=2) + + +#------------------------------------------------------------------------------ +# Tailored base class for generic tests +#------------------------------------------------------------------------------ + +def _possibly_unimplemented(cls, require=True): + """ + Construct a class that either runs tests as usual (require=True), + or each method skips if it encounters a common error. + """ + if require: + return cls + else: + def wrap(fc): + @functools.wraps(fc) + def wrapper(*a, **kw): + try: + return fc(*a, **kw) + except (NotImplementedError, TypeError, ValueError, + IndexError, AttributeError): + raise pytest.skip("feature not implemented") + + return wrapper + + new_dict = dict(cls.__dict__) + for name, func in cls.__dict__.items(): + if name.startswith('test_'): + new_dict[name] = wrap(func) + return type(cls.__name__ + "NotImplemented", + cls.__bases__, + new_dict) + + +def sparse_test_class(getset=True, slicing=True, slicing_assign=True, + fancy_indexing=True, fancy_assign=True, + fancy_multidim_indexing=True, fancy_multidim_assign=True, + minmax=True, nnz_axis=True): + """ + Construct a base class, optionally converting some of the tests in + the suite to check that the feature is not implemented. + """ + bases = (_TestCommon, + _possibly_unimplemented(_TestGetSet, getset), + _TestSolve, + _TestInplaceArithmetic, + _TestArithmetic, + _possibly_unimplemented(_TestSlicing, slicing), + _possibly_unimplemented(_TestSlicingAssign, slicing_assign), + _possibly_unimplemented(_TestFancyIndexing, fancy_indexing), + _possibly_unimplemented(_TestFancyIndexingAssign, + fancy_assign), + _possibly_unimplemented(_TestFancyMultidim, + fancy_indexing and fancy_multidim_indexing), + _possibly_unimplemented(_TestFancyMultidimAssign, + fancy_multidim_assign and fancy_assign), + _possibly_unimplemented(_TestMinMax, minmax), + _possibly_unimplemented(_TestGetNnzAxis, nnz_axis)) + + # check that test names do not clash + names = {} + for cls in bases: + for name in cls.__dict__: + if not name.startswith('test_'): + continue + old_cls = names.get(name) + if old_cls is not None: + raise ValueError(f"Test class {cls.__name__} overloads test " + f"{name} defined in {old_cls.__name__}") + names[name] = cls + + return type("TestBase", bases, {}) + + +#------------------------------------------------------------------------------ +# Matrix class based tests +#------------------------------------------------------------------------------ + +class TestCSR(sparse_test_class()): + @classmethod + def spcreator(cls, *args, **kwargs): + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "Changing the sparsity structure of a csr_matrix is expensive") + return csr_matrix(*args, **kwargs) + math_dtypes = [np.bool_, np.int_, np.float64, np.complex128] + + def test_constructor1(self): + b = array([[0, 4, 0], + [3, 0, 0], + [0, 2, 0]], 'd') + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[4,3,2]) + assert_array_equal(bsp.indices,[1,0,1]) + assert_array_equal(bsp.indptr,[0,1,2,3]) + assert_equal(bsp.getnnz(),3) + assert_equal(bsp.format,'csr') + assert_array_equal(bsp.toarray(), b) + + def test_constructor2(self): + b = zeros((6,6),'d') + b[3,4] = 5 + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[5]) + assert_array_equal(bsp.indices,[4]) + assert_array_equal(bsp.indptr,[0,0,0,0,1,1,1]) + assert_array_almost_equal(bsp.toarray(), b) + + def test_constructor3(self): + b = array([[1, 0], + [0, 2], + [3, 0]], 'd') + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[1,2,3]) + assert_array_equal(bsp.indices,[0,1,0]) + assert_array_equal(bsp.indptr,[0,1,2,3]) + assert_array_almost_equal(bsp.toarray(), b) + + def test_constructor4(self): + # using (data, ij) format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., + 11., 2., 8., 5., 7.]) + + ij = vstack((row,col)) + csr = csr_matrix((data,ij),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), csr.toarray()) + + # using Python lists and a specified dtype + csr = csr_matrix(([2**63 + 1, 1], ([0, 1], [0, 1])), dtype=np.uint64) + dense = array([[2**63 + 1, 0], [0, 1]], dtype=np.uint64) + assert_array_equal(dense, csr.toarray()) + + # with duplicates (should sum the duplicates) + csr = csr_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + assert csr.nnz == 2 + + def test_constructor5(self): + # infer dimensions from arrays + indptr = array([0,1,3,3]) + indices = array([0,5,1,2]) + data = array([1,2,3,4]) + csr = csr_matrix((data, indices, indptr)) + assert_array_equal(csr.shape,(3,6)) + + def test_constructor6(self): + # infer dimensions and dtype from lists + indptr = [0, 1, 3, 3] + indices = [0, 5, 1, 2] + data = [1, 2, 3, 4] + csr = csr_matrix((data, indices, indptr)) + assert_array_equal(csr.shape, (3,6)) + assert_(np.issubdtype(csr.dtype, np.signedinteger)) + + def test_constructor_smallcol(self): + # int64 indices not required + data = arange(6) + 1 + col = array([1, 2, 1, 0, 0, 2], dtype=np.int64) + ptr = array([0, 2, 4, 6], dtype=np.int64) + + a = csr_matrix((data, col, ptr), shape=(3, 3)) + + b = array([[0, 1, 2], + [4, 3, 0], + [5, 0, 6]], 'd') + + assert_equal(a.indptr.dtype, np.dtype(np.int32)) + assert_equal(a.indices.dtype, np.dtype(np.int32)) + assert_array_equal(a.toarray(), b) + + def test_constructor_largecol(self): + # int64 indices required + data = arange(6) + 1 + large = np.iinfo(np.int32).max + 100 + col = array([0, 1, 2, large, large+1, large+2], dtype=np.int64) + ptr = array([0, 2, 4, 6], dtype=np.int64) + + a = csr_matrix((data, col, ptr)) + + assert_equal(a.indptr.dtype, np.dtype(np.int64)) + assert_equal(a.indices.dtype, np.dtype(np.int64)) + assert_array_equal(a.shape, (3, max(col)+1)) + + def test_sort_indices(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + bsp = asp.copy() + asp.sort_indices() + assert_array_equal(asp.indices,[1, 2, 7, 4, 5]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3) + assert_array_equal(asp.data,[1, 2, 3]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_ufuncs(self): + X = csr_matrix(np.arange(20).reshape(4, 5) / 20.) + for f in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt"]: + assert_equal(hasattr(csr_matrix, f), True) + X2 = getattr(X, f)() + assert_equal(X.shape, X2.shape) + assert_array_equal(X.indices, X2.indices) + assert_array_equal(X.indptr, X2.indptr) + assert_array_equal(X2.toarray(), getattr(np, f)(X.toarray())) + + def test_unsorted_arithmetic(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + data = arange(6) + indices = array([8, 1, 5, 7, 2, 4]) + indptr = array([0, 2, 6]) + bsp = csr_matrix((data, indices, indptr), shape=(2,10)) + assert_equal((asp + bsp).toarray(), asp.toarray() + bsp.toarray()) + + def test_fancy_indexing_broadcast(self): + # broadcasting indexing mode is supported + I = np.array([[1], [2], [3]]) + J = np.array([3, 4, 2]) + + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + def test_has_sorted_indices(self): + "Ensure has_sorted_indices memoizes sorted state for sort_indices" + sorted_inds = np.array([0, 1]) + unsorted_inds = np.array([1, 0]) + data = np.array([1, 1]) + indptr = np.array([0, 2]) + M = csr_matrix((data, sorted_inds, indptr)).copy() + assert_equal(True, M.has_sorted_indices) + assert isinstance(M.has_sorted_indices, bool) + + M = csr_matrix((data, unsorted_inds, indptr)).copy() + assert_equal(False, M.has_sorted_indices) + + # set by sorting + M.sort_indices() + assert_equal(True, M.has_sorted_indices) + assert_array_equal(M.indices, sorted_inds) + + M = csr_matrix((data, unsorted_inds, indptr)).copy() + # set manually (although underlyingly unsorted) + M.has_sorted_indices = True + assert_equal(True, M.has_sorted_indices) + assert_array_equal(M.indices, unsorted_inds) + + # ensure sort bypassed when has_sorted_indices == True + M.sort_indices() + assert_array_equal(M.indices, unsorted_inds) + + def test_has_canonical_format(self): + "Ensure has_canonical_format memoizes state for sum_duplicates" + + M = csr_matrix((np.array([2]), np.array([0]), np.array([0, 1]))) + assert_equal(True, M.has_canonical_format) + + indices = np.array([0, 0]) # contains duplicate + data = np.array([1, 1]) + indptr = np.array([0, 2]) + + M = csr_matrix((data, indices, indptr)).copy() + assert_equal(False, M.has_canonical_format) + assert isinstance(M.has_canonical_format, bool) + + # set by deduplicating + M.sum_duplicates() + assert_equal(True, M.has_canonical_format) + assert_equal(1, len(M.indices)) + + M = csr_matrix((data, indices, indptr)).copy() + # set manually (although underlyingly duplicated) + M.has_canonical_format = True + assert_equal(True, M.has_canonical_format) + assert_equal(2, len(M.indices)) # unaffected content + + # ensure deduplication bypassed when has_canonical_format == True + M.sum_duplicates() + assert_equal(2, len(M.indices)) # unaffected content + + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros(0) + a = csr_matrix((vals, indices, indptr), shape=(1, 2**31-1)) + b = csr_matrix((vals, indices, indptr), shape=(1, 2**31)) + ij = np.zeros((2, 0), dtype=np.int32) + c = csr_matrix((vals, ij), shape=(1, 2**31-1)) + d = csr_matrix((vals, ij), shape=(1, 2**31)) + e = csr_matrix((1, 2**31-1)) + f = csr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + def test_binop_explicit_zeros(self): + # Check that binary ops don't introduce spurious explicit zeros. + # See gh-9619 for context. + a = csr_matrix([[0, 1, 0]]) + b = csr_matrix([[1, 1, 0]]) + assert (a + b).nnz == 2 + assert a.multiply(b).nnz == 1 + + +TestCSR.init_class() + + +class TestCSC(sparse_test_class()): + @classmethod + def spcreator(cls, *args, **kwargs): + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "Changing the sparsity structure of a csc_matrix is expensive") + return csc_matrix(*args, **kwargs) + math_dtypes = [np.bool_, np.int_, np.float64, np.complex128] + + def test_constructor1(self): + b = array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 2, 0, 3]], 'd') + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[1,2,1,3]) + assert_array_equal(bsp.indices,[0,2,1,2]) + assert_array_equal(bsp.indptr,[0,1,2,3,4]) + assert_equal(bsp.getnnz(),4) + assert_equal(bsp.shape,b.shape) + assert_equal(bsp.format,'csc') + + def test_constructor2(self): + b = zeros((6,6),'d') + b[2,4] = 5 + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[5]) + assert_array_equal(bsp.indices,[2]) + assert_array_equal(bsp.indptr,[0,0,0,0,0,1,1]) + + def test_constructor3(self): + b = array([[1, 0], [0, 0], [0, 2]], 'd') + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[1,2]) + assert_array_equal(bsp.indices,[0,2]) + assert_array_equal(bsp.indptr,[0,1,2]) + + def test_constructor4(self): + # using (data, ij) format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., 11., 2., 8., 5., 7.]) + + ij = vstack((row,col)) + csc = csc_matrix((data,ij),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), csc.toarray()) + + # with duplicates (should sum the duplicates) + csc = csc_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + assert csc.nnz == 2 + + def test_constructor5(self): + # infer dimensions from arrays + indptr = array([0,1,3,3]) + indices = array([0,5,1,2]) + data = array([1,2,3,4]) + csc = csc_matrix((data, indices, indptr)) + assert_array_equal(csc.shape,(6,3)) + + def test_constructor6(self): + # infer dimensions and dtype from lists + indptr = [0, 1, 3, 3] + indices = [0, 5, 1, 2] + data = [1, 2, 3, 4] + csc = csc_matrix((data, indices, indptr)) + assert_array_equal(csc.shape,(6,3)) + assert_(np.issubdtype(csc.dtype, np.signedinteger)) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = csc_matrix((data, indices, indptr), shape=(10,2)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3) + assert_array_equal(asp.data,[1, 2, 3]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_sort_indices(self): + data = arange(5) + row = array([7, 2, 1, 5, 4]) + ptr = [0, 3, 5] + asp = csc_matrix((data, row, ptr), shape=(10,2)) + bsp = asp.copy() + asp.sort_indices() + assert_array_equal(asp.indices,[1, 2, 7, 4, 5]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_ufuncs(self): + X = csc_matrix(np.arange(21).reshape(7, 3) / 21.) + for f in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt"]: + assert_equal(hasattr(csr_matrix, f), True) + X2 = getattr(X, f)() + assert_equal(X.shape, X2.shape) + assert_array_equal(X.indices, X2.indices) + assert_array_equal(X.indptr, X2.indptr) + assert_array_equal(X2.toarray(), getattr(np, f)(X.toarray())) + + def test_unsorted_arithmetic(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csc_matrix((data, indices, indptr), shape=(10,2)) + data = arange(6) + indices = array([8, 1, 5, 7, 2, 4]) + indptr = array([0, 2, 6]) + bsp = csc_matrix((data, indices, indptr), shape=(10,2)) + assert_equal((asp + bsp).toarray(), asp.toarray() + bsp.toarray()) + + def test_fancy_indexing_broadcast(self): + # broadcasting indexing mode is supported + I = np.array([[1], [2], [3]]) + J = np.array([3, 4, 2]) + + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros(0) + a = csc_matrix((vals, indices, indptr), shape=(2**31-1, 1)) + b = csc_matrix((vals, indices, indptr), shape=(2**31, 1)) + ij = np.zeros((2, 0), dtype=np.int32) + c = csc_matrix((vals, ij), shape=(2**31-1, 1)) + d = csc_matrix((vals, ij), shape=(2**31, 1)) + e = csr_matrix((1, 2**31-1)) + f = csr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + +TestCSC.init_class() + + +class TestDOK(sparse_test_class(minmax=False, nnz_axis=False)): + spcreator = dok_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_mult(self): + A = dok_matrix((10,10)) + A[0,3] = 10 + A[5,6] = 20 + D = A*A.T + E = A*A.H + assert_array_equal(D.toarray(), E.toarray()) + + def test_add_nonzero(self): + A = self.spcreator((3,2)) + A[0,1] = -10 + A[2,0] = 20 + A = A + 10 + B = array([[10, 0], [10, 10], [30, 10]]) + assert_array_equal(A.toarray(), B) + + A = A + 1j + B = B + 1j + assert_array_equal(A.toarray(), B) + + def test_dok_divide_scalar(self): + A = self.spcreator((3,2)) + A[0,1] = -10 + A[2,0] = 20 + + assert_array_equal((A/1j).toarray(), A.toarray()/1j) + assert_array_equal((A/9).toarray(), A.toarray()/9) + + def test_convert(self): + # Test provided by Andrew Straw. Fails in SciPy <= r1477. + (m, n) = (6, 7) + a = dok_matrix((m, n)) + + # set a few elements, but none in the last column + a[2,1] = 1 + a[0,2] = 2 + a[3,1] = 3 + a[1,5] = 4 + a[4,3] = 5 + a[4,2] = 6 + + # assert that the last column is all zeros + assert_array_equal(a.toarray()[:,n-1], zeros(m,)) + + # make sure it still works for CSC format + csc = a.tocsc() + assert_array_equal(csc.toarray()[:,n-1], zeros(m,)) + + # now test CSR + (m, n) = (n, m) + b = a.transpose() + assert_equal(b.shape, (m, n)) + # assert that the last row is all zeros + assert_array_equal(b.toarray()[m-1,:], zeros(n,)) + + # make sure it still works for CSR format + csr = b.tocsr() + assert_array_equal(csr.toarray()[m-1,:], zeros(n,)) + + def test_ctor(self): + # Empty ctor + assert_raises(TypeError, dok_matrix) + + # Dense ctor + b = array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 2, 0, 3]], 'd') + A = dok_matrix(b) + assert_equal(b.dtype, A.dtype) + assert_equal(A.toarray(), b) + + # Sparse ctor + c = csr_matrix(b) + assert_equal(A.toarray(), c.toarray()) + + data = [[0, 1, 2], [3, 0, 0]] + d = dok_matrix(data, dtype=np.float32) + assert_equal(d.dtype, np.float32) + da = d.toarray() + assert_equal(da.dtype, np.float32) + assert_array_equal(da, data) + + def test_ticket1160(self): + # Regression test for ticket #1160. + a = dok_matrix((3,3)) + a[0,0] = 0 + # This assert would fail, because the above assignment would + # incorrectly call __set_item__ even though the value was 0. + assert_((0,0) not in a.keys(), "Unexpected entry (0,0) in keys") + + # Slice assignments were also affected. + b = dok_matrix((3,3)) + b[:,0] = 0 + assert_(len(b.keys()) == 0, "Unexpected entries in keys") + + +TestDOK.init_class() + + +class TestLIL(sparse_test_class(minmax=False)): + spcreator = lil_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_dot(self): + A = zeros((10, 10), np.complex128) + A[0, 3] = 10 + A[5, 6] = 20j + + B = lil_matrix((10, 10), dtype=np.complex128) + B[0, 3] = 10 + B[5, 6] = 20j + + # TODO: properly handle this assertion on ppc64le + if platform.machine() != 'ppc64le': + assert_array_equal(A @ A.T, (B * B.T).toarray()) + + assert_array_equal(A @ A.conjugate().T, (B * B.conjugate().T).toarray()) + + def test_scalar_mul(self): + x = lil_matrix((3, 3)) + x[0, 0] = 2 + + x = x*2 + assert_equal(x[0, 0], 4) + + x = x*0 + assert_equal(x[0, 0], 0) + + def test_truediv_scalar(self): + A = self.spcreator((3, 2)) + A[0, 1] = -10 + A[2, 0] = 20 + + assert_array_equal((A / 1j).toarray(), A.toarray() / 1j) + assert_array_equal((A / 9).toarray(), A.toarray() / 9) + + def test_inplace_ops(self): + A = lil_matrix([[0, 2, 3], [4, 0, 6]]) + B = lil_matrix([[0, 1, 0], [0, 2, 3]]) + + data = {'add': (B, A + B), + 'sub': (B, A - B), + 'mul': (3, A * 3)} + + for op, (other, expected) in data.items(): + result = A.copy() + getattr(result, '__i%s__' % op)(other) + + assert_array_equal(result.toarray(), expected.toarray()) + + # Ticket 1604. + A = lil_matrix((1, 3), dtype=np.dtype('float64')) + B = array([0.1, 0.1, 0.1]) + A[0, :] += B + assert_array_equal(A[0, :].toarray().squeeze(), B) + + def test_lil_iteration(self): + row_data = [[1, 2, 3], [4, 5, 6]] + B = lil_matrix(array(row_data)) + for r, row in enumerate(B): + assert_array_equal(row.toarray(), array(row_data[r], ndmin=2)) + + def test_lil_from_csr(self): + # Tests whether a lil_matrix can be constructed from a + # csr_matrix. + B = lil_matrix((10, 10)) + B[0, 3] = 10 + B[5, 6] = 20 + B[8, 3] = 30 + B[3, 8] = 40 + B[8, 9] = 50 + C = B.tocsr() + D = lil_matrix(C) + assert_array_equal(C.toarray(), D.toarray()) + + def test_fancy_indexing_lil(self): + M = asmatrix(arange(25).reshape(5, 5)) + A = lil_matrix(M) + + assert_equal(A[array([1, 2, 3]), 2:3].toarray(), + M[array([1, 2, 3]), 2:3]) + + def test_point_wise_multiply(self): + l = lil_matrix((4, 3)) + l[0, 0] = 1 + l[1, 1] = 2 + l[2, 2] = 3 + l[3, 1] = 4 + + m = lil_matrix((4, 3)) + m[0, 0] = 1 + m[0, 1] = 2 + m[2, 2] = 3 + m[3, 1] = 4 + m[3, 2] = 4 + + assert_array_equal(l.multiply(m).toarray(), + m.multiply(l).toarray()) + + assert_array_equal(l.multiply(m).toarray(), + [[1, 0, 0], + [0, 0, 0], + [0, 0, 9], + [0, 16, 0]]) + + def test_lil_multiply_removal(self): + # Ticket #1427. + a = lil_matrix(np.ones((3, 3))) + a *= 2. + a[0, :] = 0 + + +TestLIL.init_class() + + +class TestCOO(sparse_test_class(getset=False, + slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False)): + spcreator = coo_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + # unsorted triplet format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., 11., 2., 8., 5., 7.]) + + coo = coo_matrix((data,(row,col)),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), coo.toarray()) + + # using Python lists and a specified dtype + coo = coo_matrix(([2**63 + 1, 1], ([0, 1], [0, 1])), dtype=np.uint64) + dense = array([[2**63 + 1, 0], [0, 1]], dtype=np.uint64) + assert_array_equal(dense, coo.toarray()) + + def test_constructor2(self): + # unsorted triplet format with duplicates (which are summed) + row = array([0,1,2,2,2,2,0,0,2,2]) + col = array([0,2,0,2,1,1,1,0,0,2]) + data = array([2,9,-4,5,7,0,-1,2,1,-5]) + coo = coo_matrix((data,(row,col)),(3,3)) + + mat = array([[4, -1, 0], [0, 0, 9], [-3, 7, 0]]) + + assert_array_equal(mat, coo.toarray()) + + def test_constructor3(self): + # empty matrix + coo = coo_matrix((4,3)) + + assert_array_equal(coo.shape,(4,3)) + assert_array_equal(coo.row,[]) + assert_array_equal(coo.col,[]) + assert_array_equal(coo.data,[]) + assert_array_equal(coo.toarray(), zeros((4, 3))) + + def test_constructor4(self): + # from dense matrix + mat = array([[0,1,0,0], + [7,0,3,0], + [0,4,0,0]]) + coo = coo_matrix(mat) + assert_array_equal(coo.toarray(), mat) + + # upgrade rank 1 arrays to row matrix + mat = array([0,1,0,0]) + coo = coo_matrix(mat) + assert_array_equal(coo.toarray(), mat.reshape(1, -1)) + + # error if second arg interpreted as shape (gh-9919) + with pytest.raises(TypeError, match=r'object cannot be interpreted'): + coo_matrix([0, 11, 22, 33], ([0, 1, 2, 3], [0, 0, 0, 0])) + + # error if explicit shape arg doesn't match the dense matrix + with pytest.raises(ValueError, match=r'inconsistent shapes'): + coo_matrix([0, 11, 22, 33], shape=(4, 4)) + + def test_constructor_data_ij_dtypeNone(self): + data = [1] + coo = coo_matrix((data, ([0], [0])), dtype=None) + assert coo.dtype == np.array(data).dtype + + @pytest.mark.xfail(run=False, reason='COO does not have a __getitem__') + def test_iterator(self): + pass + + def test_todia_all_zeros(self): + zeros = [[0, 0]] + dia = coo_matrix(zeros).todia() + assert_array_equal(dia.toarray(), zeros) + + def test_sum_duplicates(self): + coo = coo_matrix((4,3)) + coo.sum_duplicates() + coo = coo_matrix(([1,2], ([1,0], [1,0]))) + coo.sum_duplicates() + assert_array_equal(coo.toarray(), [[2,0],[0,1]]) + coo = coo_matrix(([1,2], ([1,1], [1,1]))) + coo.sum_duplicates() + assert_array_equal(coo.toarray(), [[0,0],[0,3]]) + assert_array_equal(coo.row, [1]) + assert_array_equal(coo.col, [1]) + assert_array_equal(coo.data, [3]) + + def test_todok_duplicates(self): + coo = coo_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + dok = coo.todok() + assert_array_equal(dok.toarray(), coo.toarray()) + + def test_tocompressed_duplicates(self): + coo = coo_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + csr = coo.tocsr() + assert_equal(csr.nnz + 2, coo.nnz) + csc = coo.tocsc() + assert_equal(csc.nnz + 2, coo.nnz) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + row = array([0, 0, 0, 1, 1, 1, 1, 1]) + col = array([1, 2, 3, 4, 5, 6, 7, 8]) + asp = coo_matrix((data, (row, col)), shape=(2,10)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_((asp.data != 0).all()) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_reshape_copy(self): + arr = [[0, 10, 0, 0], [0, 0, 0, 0], [0, 20, 30, 40]] + new_shape = (2, 6) + x = coo_matrix(arr) + + y = x.reshape(new_shape) + assert_(y.data is x.data) + + y = x.reshape(new_shape, copy=False) + assert_(y.data is x.data) + + y = x.reshape(new_shape, copy=True) + assert_(not np.may_share_memory(y.data, x.data)) + + def test_large_dimensions_reshape(self): + # Test that reshape is immune to integer overflow when number of elements + # exceeds 2^31-1 + mat1 = coo_matrix(([1], ([3000000], [1000])), (3000001, 1001)) + mat2 = coo_matrix(([1], ([1000], [3000000])), (1001, 3000001)) + + # assert_array_equal is slow for big matrices because it expects dense + # Using __ne__ and nnz instead + assert_((mat1.reshape((1001, 3000001), order='C') != mat2).nnz == 0) + assert_((mat2.reshape((3000001, 1001), order='F') != mat1).nnz == 0) + + +TestCOO.init_class() + + +class TestDIA(sparse_test_class(getset=False, slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False, + minmax=False, nnz_axis=False)): + spcreator = dia_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + D = array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + data = np.array([[1,2,3,4]]).repeat(3,axis=0) + offsets = np.array([0,-1,2]) + assert_equal(dia_matrix((data, offsets), shape=(4, 4)).toarray(), D) + + @pytest.mark.xfail(run=False, reason='DIA does not have a __getitem__') + def test_iterator(self): + pass + + @with_64bit_maxval_limit(3) + def test_setdiag_dtype(self): + m = dia_matrix(np.eye(3)) + assert_equal(m.offsets.dtype, np.int32) + m.setdiag((3,), k=2) + assert_equal(m.offsets.dtype, np.int32) + + m = dia_matrix(np.eye(4)) + assert_equal(m.offsets.dtype, np.int64) + m.setdiag((3,), k=3) + assert_equal(m.offsets.dtype, np.int64) + + @pytest.mark.skip(reason='DIA stores extra zeros') + def test_getnnz_axis(self): + pass + + def test_convert_gh14555(self): + # regression test for gh-14555 + m = dia_matrix(([[1, 1, 0]], [-1]), shape=(4, 2)) + expected = m.toarray() + assert_array_equal(m.tocsc().toarray(), expected) + assert_array_equal(m.tocsr().toarray(), expected) + + def test_tocoo_gh10050(self): + # regression test for gh-10050 + m = dia_matrix([[1, 2], [3, 4]]).tocoo() + flat_inds = np.ravel_multi_index((m.row, m.col), m.shape) + inds_are_sorted = np.all(np.diff(flat_inds) > 0) + assert m.has_canonical_format == inds_are_sorted + + def test_tocoo_tocsr_tocsc_gh19245(self): + # test index_dtype with tocoo, tocsr, tocsc + data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + offsets = np.array([0, -1, 2], dtype=np.int32) + dia = sparse.dia_array((data, offsets), shape=(4, 4)) + + coo = dia.tocoo() + assert coo.col.dtype == np.int32 + csr = dia.tocsr() + assert csr.indices.dtype == np.int32 + csc = dia.tocsc() + assert csc.indices.dtype == np.int32 + + +TestDIA.init_class() + + +class TestBSR(sparse_test_class(getset=False, + slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False, + nnz_axis=False)): + spcreator = bsr_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + # check native BSR format constructor + indptr = array([0,2,2,4]) + indices = array([0,2,2,3]) + data = zeros((4,2,3)) + + data[0] = array([[0, 1, 2], + [3, 0, 5]]) + data[1] = array([[0, 2, 4], + [6, 0, 10]]) + data[2] = array([[0, 4, 8], + [12, 0, 20]]) + data[3] = array([[0, 5, 10], + [15, 0, 25]]) + + A = kron([[1,0,2,0],[0,0,0,0],[0,0,4,5]], [[0,1,2],[3,0,5]]) + Asp = bsr_matrix((data,indices,indptr),shape=(6,12)) + assert_equal(Asp.toarray(), A) + + # infer shape from arrays + Asp = bsr_matrix((data,indices,indptr)) + assert_equal(Asp.toarray(), A) + + def test_constructor2(self): + # construct from dense + + # test zero mats + for shape in [(1,1), (5,1), (1,10), (10,4), (3,7), (2,1)]: + A = zeros(shape) + assert_equal(bsr_matrix(A).toarray(), A) + A = zeros((4,6)) + assert_equal(bsr_matrix(A, blocksize=(2, 2)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + + A = kron([[1,0,2,0],[0,0,0,0],[0,0,4,5]], [[0,1,2],[3,0,5]]) + assert_equal(bsr_matrix(A).toarray(), A) + assert_equal(bsr_matrix(A, shape=(6, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(1, 1)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 6)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(3, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(6, 12)).toarray(), A) + + A = kron([[1,0,2,0],[0,1,0,0],[0,0,0,0]], [[0,1,2],[3,0,5]]) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + + def test_constructor3(self): + # construct from coo-like (data,(row,col)) format + arg = ([1,2,3], ([0,1,1], [0,0,1])) + A = array([[1,0],[2,3]]) + assert_equal(bsr_matrix(arg, blocksize=(2, 2)).toarray(), A) + + def test_constructor4(self): + # regression test for gh-6292: bsr_matrix((data, indices, indptr)) was + # trying to compare an int to a None + n = 8 + data = np.ones((n, n, 1), dtype=np.int8) + indptr = np.array([0, n], dtype=np.int32) + indices = np.arange(n, dtype=np.int32) + bsr_matrix((data, indices, indptr), blocksize=(n, 1), copy=False) + + def test_constructor5(self): + # check for validations introduced in gh-13400 + n = 8 + data_1dim = np.ones(n) + data = np.ones((n, n, n)) + indptr = np.array([0, n]) + indices = np.arange(n) + + with assert_raises(ValueError): + # data ndim check + bsr_matrix((data_1dim, indices, indptr)) + + with assert_raises(ValueError): + # invalid blocksize + bsr_matrix((data, indices, indptr), blocksize=(1, 1, 1)) + + with assert_raises(ValueError): + # mismatching blocksize + bsr_matrix((data, indices, indptr), blocksize=(1, 1)) + + def test_default_dtype(self): + # As a numpy array, `values` has shape (2, 2, 1). + values = [[[1], [1]], [[1], [1]]] + indptr = np.array([0, 2], dtype=np.int32) + indices = np.array([0, 1], dtype=np.int32) + b = bsr_matrix((values, indices, indptr), blocksize=(2, 1)) + assert b.dtype == np.array(values).dtype + + def test_bsr_tocsr(self): + # check native conversion from BSR to CSR + indptr = array([0, 2, 2, 4]) + indices = array([0, 2, 2, 3]) + data = zeros((4, 2, 3)) + + data[0] = array([[0, 1, 2], + [3, 0, 5]]) + data[1] = array([[0, 2, 4], + [6, 0, 10]]) + data[2] = array([[0, 4, 8], + [12, 0, 20]]) + data[3] = array([[0, 5, 10], + [15, 0, 25]]) + + A = kron([[1, 0, 2, 0], [0, 0, 0, 0], [0, 0, 4, 5]], + [[0, 1, 2], [3, 0, 5]]) + Absr = bsr_matrix((data, indices, indptr), shape=(6, 12)) + Acsr = Absr.tocsr() + Acsr_via_coo = Absr.tocoo().tocsr() + assert_equal(Acsr.toarray(), A) + assert_equal(Acsr.toarray(), Acsr_via_coo.toarray()) + + def test_eliminate_zeros(self): + data = kron([1, 0, 0, 0, 2, 0, 3, 0], [[1,1],[1,1]]).T + data = data.reshape(-1,2,2) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = bsr_matrix((data, indices, indptr), shape=(4,20)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3*4) + assert_array_equal(asp.toarray(), bsp.toarray()) + + # github issue #9687 + def test_eliminate_zeros_all_zero(self): + np.random.seed(0) + m = bsr_matrix(np.random.random((12, 12)), blocksize=(2, 3)) + + # eliminate some blocks, but not all + m.data[m.data <= 0.9] = 0 + m.eliminate_zeros() + assert_equal(m.nnz, 66) + assert_array_equal(m.data.shape, (11, 2, 3)) + + # eliminate all remaining blocks + m.data[m.data <= 1.0] = 0 + m.eliminate_zeros() + assert_equal(m.nnz, 0) + assert_array_equal(m.data.shape, (0, 2, 3)) + assert_array_equal(m.toarray(), np.zeros((12, 12))) + + # test fast path + m.eliminate_zeros() + assert_equal(m.nnz, 0) + assert_array_equal(m.data.shape, (0, 2, 3)) + assert_array_equal(m.toarray(), np.zeros((12, 12))) + + def test_bsr_matvec(self): + A = bsr_matrix(arange(2*3*4*5).reshape(2*4,3*5), blocksize=(4,5)) + x = arange(A.shape[1]).reshape(-1,1) + assert_equal(A*x, A.toarray() @ x) + + def test_bsr_matvecs(self): + A = bsr_matrix(arange(2*3*4*5).reshape(2*4,3*5), blocksize=(4,5)) + x = arange(A.shape[1]*6).reshape(-1,6) + assert_equal(A*x, A.toarray() @ x) + + @pytest.mark.xfail(run=False, reason='BSR does not have a __getitem__') + def test_iterator(self): + pass + + @pytest.mark.xfail(run=False, reason='BSR does not have a __setitem__') + def test_setdiag(self): + pass + + def test_resize_blocked(self): + # test resize() with non-(1,1) blocksize + D = np.array([[1, 0, 3, 4], + [2, 0, 0, 0], + [3, 0, 0, 0]]) + S = self.spcreator(D, blocksize=(1, 2)) + assert_(S.resize((3, 2)) is None) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [3, 0]]) + S.resize((2, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0]]) + S.resize((3, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + S.resize((3, 4)) + assert_array_equal(S.toarray(), [[1, 0, 0, 0], + [2, 0, 0, 0], + [0, 0, 0, 0]]) + assert_raises(ValueError, S.resize, (2, 3)) + + @pytest.mark.xfail(run=False, reason='BSR does not have a __setitem__') + def test_setdiag_comprehensive(self): + pass + + @pytest.mark.skipif(IS_COLAB, reason="exceeds memory limit") + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros((0, 1, 1)) + a = bsr_matrix((vals, indices, indptr), shape=(1, 2**31-1)) + b = bsr_matrix((vals, indices, indptr), shape=(1, 2**31)) + c = bsr_matrix((1, 2**31-1)) + d = bsr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + + try: + vals2 = np.zeros((0, 1, 2**31-1)) + vals3 = np.zeros((0, 1, 2**31)) + e = bsr_matrix((vals2, indices, indptr), shape=(1, 2**31-1)) + f = bsr_matrix((vals3, indices, indptr), shape=(1, 2**31)) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + except (MemoryError, ValueError): + # May fail on 32-bit Python + e = 0 + f = 0 + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + +TestBSR.init_class() + + +#------------------------------------------------------------------------------ +# Tests for non-canonical representations (with duplicates, unsorted indices) +#------------------------------------------------------------------------------ + +def _same_sum_duplicate(data, *inds, **kwargs): + """Duplicates entries to produce the same matrix""" + indptr = kwargs.pop('indptr', None) + if np.issubdtype(data.dtype, np.bool_) or \ + np.issubdtype(data.dtype, np.unsignedinteger): + if indptr is None: + return (data,) + inds + else: + return (data,) + inds + (indptr,) + + zeros_pos = (data == 0).nonzero() + + # duplicate data + data = data.repeat(2, axis=0) + data[::2] -= 1 + data[1::2] = 1 + + # don't spoil all explicit zeros + if zeros_pos[0].size > 0: + pos = tuple(p[0] for p in zeros_pos) + pos1 = (2*pos[0],) + pos[1:] + pos2 = (2*pos[0]+1,) + pos[1:] + data[pos1] = 0 + data[pos2] = 0 + + inds = tuple(indices.repeat(2) for indices in inds) + + if indptr is None: + return (data,) + inds + else: + return (data,) + inds + (indptr * 2,) + + +class _NonCanonicalMixin: + def spcreator(self, D, sorted_indices=False, **kwargs): + """Replace D with a non-canonical equivalent: containing + duplicate elements and explicit zeros""" + construct = super().spcreator + M = construct(D, **kwargs) + + zero_pos = (M.toarray() == 0).nonzero() + has_zeros = (zero_pos[0].size > 0) + if has_zeros: + k = zero_pos[0].size//2 + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + M = self._insert_explicit_zero(M, zero_pos[0][k], zero_pos[1][k]) + + arg1 = self._arg1_for_noncanonical(M, sorted_indices) + if 'shape' not in kwargs: + kwargs['shape'] = M.shape + NC = construct(arg1, **kwargs) + + # check that result is valid + if NC.dtype in [np.float32, np.complex64]: + # For single-precision floats, the differences between M and NC + # that are introduced by the extra operations involved in the + # construction of NC necessitate a more lenient tolerance level + # than the default. + rtol = 1e-05 + else: + rtol = 1e-07 + assert_allclose(NC.toarray(), M.toarray(), rtol=rtol) + + # check that at least one explicit zero + if has_zeros: + assert_((NC.data == 0).any()) + # TODO check that NC has duplicates (which are not explicit zeros) + + return NC + + @pytest.mark.skip(reason='bool(matrix) counts explicit zeros') + def test_bool(self): + pass + + @pytest.mark.skip(reason='getnnz-axis counts explicit zeros') + def test_getnnz_axis(self): + pass + + @pytest.mark.skip(reason='nnz counts explicit zeros') + def test_empty(self): + pass + + +class _NonCanonicalCompressedMixin(_NonCanonicalMixin): + def _arg1_for_noncanonical(self, M, sorted_indices=False): + """Return non-canonical constructor arg1 equivalent to M""" + data, indices, indptr = _same_sum_duplicate(M.data, M.indices, + indptr=M.indptr) + if not sorted_indices: + for start, stop in zip(indptr, indptr[1:]): + indices[start:stop] = indices[start:stop][::-1].copy() + data[start:stop] = data[start:stop][::-1].copy() + return data, indices, indptr + + def _insert_explicit_zero(self, M, i, j): + M[i,j] = 0 + return M + + +class _NonCanonicalCSMixin(_NonCanonicalCompressedMixin): + def test_getelement(self): + def check(dtype, sorted_indices): + D = array([[1,0,0], + [4,3,0], + [0,2,0], + [0,0,0]], dtype=dtype) + A = self.spcreator(D, sorted_indices=sorted_indices) + + M,N = D.shape + + for i in range(-M, M): + for j in range(-N, N): + assert_equal(A[i,j], D[i,j]) + + for ij in [(0,3),(-1,3),(4,0),(4,3),(4,-1), (1, 2, 3)]: + assert_raises((IndexError, TypeError), A.__getitem__, ij) + + for dtype in supported_dtypes: + for sorted_indices in [False, True]: + check(np.dtype(dtype), sorted_indices) + + def test_setitem_sparse(self): + D = np.eye(3) + A = self.spcreator(D) + B = self.spcreator([[1,2,3]]) + + D[1,:] = B.toarray() + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[1,:] = B + assert_array_equal(A.toarray(), D) + + D[:,2] = B.toarray().ravel() + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive" + ) + A[:,2] = B.T + assert_array_equal(A.toarray(), D) + + @pytest.mark.xfail(run=False, reason='inverse broken with non-canonical matrix') + def test_inv(self): + pass + + @pytest.mark.xfail(run=False, reason='solve broken with non-canonical matrix') + def test_solve(self): + pass + + +class TestCSRNonCanonical(_NonCanonicalCSMixin, TestCSR): + pass + + +class TestCSCNonCanonical(_NonCanonicalCSMixin, TestCSC): + pass + + +class TestBSRNonCanonical(_NonCanonicalCompressedMixin, TestBSR): + def _insert_explicit_zero(self, M, i, j): + x = M.tocsr() + x[i,j] = 0 + return x.tobsr(blocksize=M.blocksize) + + @pytest.mark.xfail(run=False, reason='diagonal broken with non-canonical BSR') + def test_diagonal(self): + pass + + @pytest.mark.xfail(run=False, reason='expm broken with non-canonical BSR') + def test_expm(self): + pass + + +class TestCOONonCanonical(_NonCanonicalMixin, TestCOO): + def _arg1_for_noncanonical(self, M, sorted_indices=None): + """Return non-canonical constructor arg1 equivalent to M""" + data, row, col = _same_sum_duplicate(M.data, M.row, M.col) + return data, (row, col) + + def _insert_explicit_zero(self, M, i, j): + M.data = np.r_[M.data.dtype.type(0), M.data] + M.row = np.r_[M.row.dtype.type(i), M.row] + M.col = np.r_[M.col.dtype.type(j), M.col] + return M + + def test_setdiag_noncanonical(self): + m = self.spcreator(np.eye(3)) + m.sum_duplicates() + m.setdiag([3, 2], k=1) + m.sum_duplicates() + assert_(np.all(np.diff(m.col) >= 0)) + + +def cases_64bit(): + TEST_CLASSES = [TestBSR, TestCOO, TestCSC, TestCSR, TestDIA, + # lil/dok->other conversion operations have get_index_dtype + TestDOK, TestLIL + ] + + # The following features are missing, so skip the tests: + SKIP_TESTS = { + 'test_expm': 'expm for 64-bit indices not available', + 'test_inv': 'linsolve for 64-bit indices not available', + 'test_solve': 'linsolve for 64-bit indices not available', + 'test_scalar_idx_dtype': 'test implemented in base class', + 'test_large_dimensions_reshape': 'test actually requires 64-bit to work', + 'test_constructor_smallcol': 'test verifies int32 indexes', + 'test_constructor_largecol': 'test verifies int64 indexes', + 'test_tocoo_tocsr_tocsc_gh19245': 'test verifies int32 indexes', + } + + for cls in TEST_CLASSES: + for method_name in sorted(dir(cls)): + method = getattr(cls, method_name) + if (method_name.startswith('test_') and + not getattr(method, 'slow', False)): + marks = [] + + msg = SKIP_TESTS.get(method_name) + if bool(msg): + marks += [pytest.mark.skip(reason=msg)] + + if _pep440.parse(pytest.__version__) >= _pep440.Version("3.6.0"): + markers = getattr(method, 'pytestmark', []) + for mark in markers: + if mark.name in ('skipif', 'skip', 'xfail', 'xslow'): + marks.append(mark) + else: + for mname in ['skipif', 'skip', 'xfail', 'xslow']: + if hasattr(method, mname): + marks += [getattr(method, mname)] + + yield pytest.param(cls, method_name, marks=marks) + + +class Test64Bit: + MAT_CLASSES = [bsr_matrix, coo_matrix, csc_matrix, csr_matrix, dia_matrix] + + def _create_some_matrix(self, mat_cls, m, n): + return mat_cls(np.random.rand(m, n)) + + def _compare_index_dtype(self, m, dtype): + dtype = np.dtype(dtype) + if isinstance(m, (csc_matrix, csr_matrix, bsr_matrix)): + return (m.indices.dtype == dtype) and (m.indptr.dtype == dtype) + elif isinstance(m, coo_matrix): + return (m.row.dtype == dtype) and (m.col.dtype == dtype) + elif isinstance(m, dia_matrix): + return (m.offsets.dtype == dtype) + else: + raise ValueError(f"matrix {m!r} has no integer indices") + + def test_decorator_maxval_limit(self): + # Test that the with_64bit_maxval_limit decorator works + + @with_64bit_maxval_limit(maxval_limit=10) + def check(mat_cls): + m = mat_cls(np.random.rand(10, 1)) + assert_(self._compare_index_dtype(m, np.int32)) + m = mat_cls(np.random.rand(11, 1)) + assert_(self._compare_index_dtype(m, np.int64)) + + for mat_cls in self.MAT_CLASSES: + check(mat_cls) + + def test_decorator_maxval_random(self): + # Test that the with_64bit_maxval_limit decorator works (2) + + @with_64bit_maxval_limit(random=True) + def check(mat_cls): + seen_32 = False + seen_64 = False + for k in range(100): + m = self._create_some_matrix(mat_cls, 9, 9) + seen_32 = seen_32 or self._compare_index_dtype(m, np.int32) + seen_64 = seen_64 or self._compare_index_dtype(m, np.int64) + if seen_32 and seen_64: + break + else: + raise AssertionError("both 32 and 64 bit indices not seen") + + for mat_cls in self.MAT_CLASSES: + check(mat_cls) + + def _check_resiliency(self, cls, method_name, **kw): + # Resiliency test, to check that sparse matrices deal reasonably + # with varying index data types. + + @with_64bit_maxval_limit(**kw) + def check(cls, method_name): + instance = cls() + if hasattr(instance, 'setup_method'): + instance.setup_method() + try: + getattr(instance, method_name)() + finally: + if hasattr(instance, 'teardown_method'): + instance.teardown_method() + + check(cls, method_name) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_limit_10(self, cls, method_name): + self._check_resiliency(cls, method_name, maxval_limit=10) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_random(self, cls, method_name): + # bsr_matrix.eliminate_zeros relies on csr_matrix constructor + # not making copies of index arrays --- this is not + # necessarily true when we pick the index data type randomly + self._check_resiliency(cls, method_name, random=True) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_all_32(self, cls, method_name): + self._check_resiliency(cls, method_name, fixed_dtype=np.int32) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_all_64(self, cls, method_name): + self._check_resiliency(cls, method_name, fixed_dtype=np.int64) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_no_64(self, cls, method_name): + self._check_resiliency(cls, method_name, assert_32bit=True) + + def test_downcast_intp(self): + # Check that bincount and ufunc.reduceat intp downcasts are + # dealt with. The point here is to trigger points in the code + # that can fail on 32-bit systems when using 64-bit indices, + # due to use of functions that only work with intp-size + # indices. + + @with_64bit_maxval_limit(fixed_dtype=np.int64, + downcast_maxval=1) + def check_limited(): + # These involve indices larger than `downcast_maxval` + a = csc_matrix([[1, 2], [3, 4], [5, 6]]) + assert_raises(AssertionError, a.getnnz, axis=1) + assert_raises(AssertionError, a.sum, axis=0) + + a = csr_matrix([[1, 2, 3], [3, 4, 6]]) + assert_raises(AssertionError, a.getnnz, axis=0) + + a = coo_matrix([[1, 2, 3], [3, 4, 5]]) + assert_raises(AssertionError, a.getnnz, axis=0) + + @with_64bit_maxval_limit(fixed_dtype=np.int64) + def check_unlimited(): + # These involve indices larger than `downcast_maxval` + a = csc_matrix([[1, 2], [3, 4], [5, 6]]) + a.getnnz(axis=1) + a.sum(axis=0) + + a = csr_matrix([[1, 2, 3], [3, 4, 6]]) + a.getnnz(axis=0) + + a = coo_matrix([[1, 2, 3], [3, 4, 5]]) + a.getnnz(axis=0) + + check_limited() + check_unlimited() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_common1d.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_common1d.py new file mode 100644 index 0000000000000000000000000000000000000000..555314517f9c30599ec7714d4d735063bbc6bb49 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_common1d.py @@ -0,0 +1,457 @@ +"""Test of 1D aspects of sparse array classes""" + +import pytest + +import numpy as np + +import scipy as sp +from scipy.sparse import ( + bsr_array, csc_array, dia_array, lil_array, + ) +from scipy.sparse._sputils import supported_dtypes, matrix +from scipy._lib._util import ComplexWarning + + +sup_complex = np.testing.suppress_warnings() +sup_complex.filter(ComplexWarning) + + +spcreators = [sp.sparse.coo_array, sp.sparse.dok_array] +math_dtypes = [np.int64, np.float64, np.complex128] + + +@pytest.fixture +def dat1d(): + return np.array([3, 0, 1, 0], 'd') + + +@pytest.fixture +def datsp_math_dtypes(dat1d): + dat_dtypes = {dtype: dat1d.astype(dtype) for dtype in math_dtypes} + return { + sp: [(dtype, dat, sp(dat)) for dtype, dat in dat_dtypes.items()] + for sp in spcreators + } + + +# Test init with 1D dense input +# sparrays which do not plan to support 1D +@pytest.mark.parametrize("spcreator", [bsr_array, csc_array, dia_array, lil_array]) +def test_no_1d_support_in_init(spcreator): + with pytest.raises(ValueError, match="arrays don't support 1D input"): + spcreator([0, 1, 2, 3]) + + +# Main tests class +@pytest.mark.parametrize("spcreator", spcreators) +class TestCommon1D: + """test common functionality shared by 1D sparse formats""" + + def test_create_empty(self, spcreator): + assert np.array_equal(spcreator((3,)).toarray(), np.zeros(3)) + assert np.array_equal(spcreator((3,)).nnz, 0) + assert np.array_equal(spcreator((3,)).count_nonzero(), 0) + + def test_invalid_shapes(self, spcreator): + with pytest.raises(ValueError, match='elements cannot be negative'): + spcreator((-3,)) + + def test_repr(self, spcreator, dat1d): + repr(spcreator(dat1d)) + + def test_str(self, spcreator, dat1d): + str(spcreator(dat1d)) + + def test_neg(self, spcreator): + A = np.array([-1, 0, 17, 0, -5, 0, 1, -4, 0, 0, 0, 0], 'd') + assert np.array_equal(-A, (-spcreator(A)).toarray()) + + def test_1d_supported_init(self, spcreator): + A = spcreator([0, 1, 2, 3]) + assert A.ndim == 1 + + def test_reshape_1d_tofrom_row_or_column(self, spcreator): + # add a dimension 1d->2d + x = spcreator([1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]) + y = x.reshape(1, 12) + desired = [[1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]] + assert np.array_equal(y.toarray(), desired) + + # remove a size-1 dimension 2d->1d + x = spcreator(desired) + y = x.reshape(12) + assert np.array_equal(y.toarray(), desired[0]) + y2 = x.reshape((12,)) + assert y.shape == y2.shape + + # make a 2d column into 1d. 2d->1d + y = x.T.reshape(12) + assert np.array_equal(y.toarray(), desired[0]) + + def test_reshape(self, spcreator): + x = spcreator([1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]) + y = x.reshape((4, 3)) + desired = [[1, 0, 7], [0, 0, 0], [0, -3, 0], [0, 0, 5]] + assert np.array_equal(y.toarray(), desired) + + y = x.reshape((12,)) + assert y is x + + y = x.reshape(12) + assert np.array_equal(y.toarray(), x.toarray()) + + def test_sum(self, spcreator): + np.random.seed(1234) + dat_1 = np.array([0, 1, 2, 3, -4, 5, -6, 7, 9]) + dat_2 = np.random.rand(5) + dat_3 = np.array([]) + dat_4 = np.zeros((40,)) + arrays = [dat_1, dat_2, dat_3, dat_4] + + for dat in arrays: + datsp = spcreator(dat) + with np.errstate(over='ignore'): + assert np.isscalar(datsp.sum()) + assert np.allclose(dat.sum(), datsp.sum()) + assert np.allclose(dat.sum(axis=None), datsp.sum(axis=None)) + assert np.allclose(dat.sum(axis=0), datsp.sum(axis=0)) + assert np.allclose(dat.sum(axis=-1), datsp.sum(axis=-1)) + + # test `out` parameter + datsp.sum(axis=0, out=np.zeros(())) + + def test_sum_invalid_params(self, spcreator): + out = np.zeros((3,)) # wrong size for out + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + with pytest.raises(ValueError, match='axis must be None, -1 or 0'): + datsp.sum(axis=1) + with pytest.raises(TypeError, match='Tuples are not accepted'): + datsp.sum(axis=(0, 1)) + with pytest.raises(TypeError, match='axis must be an integer'): + datsp.sum(axis=1.5) + with pytest.raises(ValueError, match='dimensions do not match'): + datsp.sum(axis=0, out=out) + + def test_numpy_sum(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_sum = np.sum(dat) + datsp_sum = np.sum(datsp) + + assert np.allclose(dat_sum, datsp_sum) + + def test_mean(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + assert np.allclose(dat.mean(), datsp.mean()) + assert np.isscalar(datsp.mean(axis=None)) + assert np.allclose(dat.mean(axis=None), datsp.mean(axis=None)) + assert np.allclose(dat.mean(axis=0), datsp.mean(axis=0)) + assert np.allclose(dat.mean(axis=-1), datsp.mean(axis=-1)) + + with pytest.raises(ValueError, match='axis'): + datsp.mean(axis=1) + with pytest.raises(ValueError, match='axis'): + datsp.mean(axis=-2) + + def test_mean_invalid_params(self, spcreator): + out = np.asarray(np.zeros((1, 3))) + dat = np.array([[0, 1, 2], [3, -4, 5], [-6, 7, 9]]) + + if spcreator._format == 'uni': + with pytest.raises(ValueError, match='zq'): + spcreator(dat) + return + + datsp = spcreator(dat) + with pytest.raises(ValueError, match='axis out of range'): + datsp.mean(axis=3) + with pytest.raises(TypeError, match='Tuples are not accepted'): + datsp.mean(axis=(0, 1)) + with pytest.raises(TypeError, match='axis must be an integer'): + datsp.mean(axis=1.5) + with pytest.raises(ValueError, match='dimensions do not match'): + datsp.mean(axis=1, out=out) + + def test_sum_dtype(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + for dtype in supported_dtypes: + dat_sum = dat.sum(dtype=dtype) + datsp_sum = datsp.sum(dtype=dtype) + + assert np.allclose(dat_sum, datsp_sum) + assert np.array_equal(dat_sum.dtype, datsp_sum.dtype) + + def test_mean_dtype(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + for dtype in supported_dtypes: + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert np.allclose(dat_mean, datsp_mean) + assert np.array_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_mean_out(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_out = np.array([0]) + datsp_out = np.array([0]) + + dat.mean(out=dat_out, keepdims=True) + datsp.mean(out=datsp_out) + assert np.allclose(dat_out, datsp_out) + + dat.mean(axis=0, out=dat_out, keepdims=True) + datsp.mean(axis=0, out=datsp_out) + assert np.allclose(dat_out, datsp_out) + + def test_numpy_mean(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_mean = np.mean(dat) + datsp_mean = np.mean(datsp) + + assert np.allclose(dat_mean, datsp_mean) + assert np.array_equal(dat_mean.dtype, datsp_mean.dtype) + + @sup_complex + def test_from_array(self, spcreator): + A = np.array([2, 3, 4]) + assert np.array_equal(spcreator(A).toarray(), A) + + A = np.array([1.0 + 3j, 0, -1]) + assert np.array_equal(spcreator(A).toarray(), A) + assert np.array_equal(spcreator(A, dtype='int16').toarray(), A.astype('int16')) + + @sup_complex + def test_from_list(self, spcreator): + A = [2, 3, 4] + assert np.array_equal(spcreator(A).toarray(), A) + + A = [1.0 + 3j, 0, -1] + assert np.array_equal(spcreator(A).toarray(), np.array(A)) + assert np.array_equal( + spcreator(A, dtype='int16').toarray(), np.array(A).astype('int16') + ) + + @sup_complex + def test_from_sparse(self, spcreator): + D = np.array([1, 0, 0]) + S = sp.sparse.coo_array(D) + assert np.array_equal(spcreator(S).toarray(), D) + S = spcreator(D) + assert np.array_equal(spcreator(S).toarray(), D) + + D = np.array([1.0 + 3j, 0, -1]) + S = sp.sparse.coo_array(D) + assert np.array_equal(spcreator(S).toarray(), D) + assert np.array_equal(spcreator(S, dtype='int16').toarray(), D.astype('int16')) + S = spcreator(D) + assert np.array_equal(spcreator(S).toarray(), D) + assert np.array_equal(spcreator(S, dtype='int16').toarray(), D.astype('int16')) + + def test_toarray(self, spcreator, dat1d): + datsp = spcreator(dat1d) + # Check C- or F-contiguous (default). + chk = datsp.toarray() + assert np.array_equal(chk, dat1d) + assert chk.flags.c_contiguous == chk.flags.f_contiguous + + # Check C-contiguous (with arg). + chk = datsp.toarray(order='C') + assert np.array_equal(chk, dat1d) + assert chk.flags.c_contiguous + assert chk.flags.f_contiguous + + # Check F-contiguous (with arg). + chk = datsp.toarray(order='F') + assert np.array_equal(chk, dat1d) + assert chk.flags.c_contiguous + assert chk.flags.f_contiguous + + # Check with output arg. + out = np.zeros(datsp.shape, dtype=datsp.dtype) + datsp.toarray(out=out) + assert np.array_equal(out, dat1d) + + # Check that things are fine when we don't initialize with zeros. + out[...] = 1.0 + datsp.toarray(out=out) + assert np.array_equal(out, dat1d) + + # np.dot does not work with sparse matrices (unless scalars) + # so this is testing whether dat1d matches datsp.toarray() + a = np.array([1.0, 2.0, 3.0, 4.0]) + dense_dot_dense = np.dot(a, dat1d) + check = np.dot(a, datsp.toarray()) + assert np.array_equal(dense_dot_dense, check) + + b = np.array([1.0, 2.0, 3.0, 4.0]) + dense_dot_dense = np.dot(dat1d, b) + check = np.dot(datsp.toarray(), b) + assert np.array_equal(dense_dot_dense, check) + + # Check bool data works. + spbool = spcreator(dat1d, dtype=bool) + arrbool = dat1d.astype(bool) + assert np.array_equal(spbool.toarray(), arrbool) + + def test_add(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + a = dat.copy() + a[0] = 2.0 + b = datsp + c = b + a + assert np.array_equal(c, b.toarray() + a) + + # test broadcasting + # Note: cant add nonzero scalar to sparray. Can add len 1 array + c = b + a[0:1] + assert np.array_equal(c, b.toarray() + a[0]) + + def test_radd(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + a = dat.copy() + a[0] = 2.0 + b = datsp + c = a + b + assert np.array_equal(c, a + b.toarray()) + + def test_rsub(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + assert np.array_equal((dat - datsp), [0, 0, 0, 0]) + assert np.array_equal((datsp - dat), [0, 0, 0, 0]) + assert np.array_equal((0 - datsp).toarray(), -dat) + + A = spcreator([1, -4, 0, 2], dtype='d') + assert np.array_equal((dat - A), dat - A.toarray()) + assert np.array_equal((A - dat), A.toarray() - dat) + assert np.array_equal(A.toarray() - datsp, A.toarray() - dat) + assert np.array_equal(datsp - A.toarray(), dat - A.toarray()) + + # test broadcasting + assert np.array_equal(dat[:1] - datsp, dat[:1] - dat) + + def test_matvec(self, spcreator): + A = np.array([2, 0, 3.0]) + Asp = spcreator(A) + col = np.array([[1, 2, 3]]).T + + assert np.allclose(Asp @ col, Asp.toarray() @ col) + + assert (A @ np.array([1, 2, 3])).shape == () + assert Asp @ np.array([1, 2, 3]) == 11 + assert (Asp @ np.array([1, 2, 3])).shape == () + assert (Asp @ np.array([[1], [2], [3]])).shape == () + # check result type + assert isinstance(Asp @ matrix([[1, 2, 3]]).T, np.ndarray) + assert (Asp @ np.array([[1, 2, 3]]).T).shape == () + + # ensure exception is raised for improper dimensions + bad_vecs = [np.array([1, 2]), np.array([1, 2, 3, 4]), np.array([[1], [2]])] + for x in bad_vecs: + with pytest.raises(ValueError, match='dimension mismatch'): + Asp.__matmul__(x) + + # The current relationship between sparse matrix products and array + # products is as follows: + dot_result = np.dot(Asp.toarray(), [1, 2, 3]) + assert np.allclose(Asp @ np.array([1, 2, 3]), dot_result) + assert np.allclose(Asp @ [[1], [2], [3]], dot_result.T) + # Note that the result of Asp @ x is dense if x has a singleton dimension. + + def test_rmatvec(self, spcreator, dat1d): + M = spcreator(dat1d) + assert np.allclose([1, 2, 3, 4] @ M, np.dot([1, 2, 3, 4], M.toarray())) + row = np.array([[1, 2, 3, 4]]) + assert np.allclose(row @ M, row @ M.toarray()) + + def test_transpose(self, spcreator, dat1d): + for A in [dat1d, np.array([])]: + B = spcreator(A) + assert np.array_equal(B.toarray(), A) + assert np.array_equal(B.transpose().toarray(), A) + assert np.array_equal(B.dtype, A.dtype) + + def test_add_dense_to_sparse(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + sum1 = dat + datsp + assert np.array_equal(sum1, dat + dat) + sum2 = datsp + dat + assert np.array_equal(sum2, dat + dat) + + def test_iterator(self, spcreator): + # test that __iter__ is compatible with NumPy + B = np.arange(5) + A = spcreator(B) + + if A.format not in ['coo', 'dia', 'bsr']: + for x, y in zip(A, B): + assert np.array_equal(x, y) + + def test_resize(self, spcreator): + # resize(shape) resizes the matrix in-place + D = np.array([1, 0, 3, 4]) + S = spcreator(D) + assert S.resize((3,)) is None + assert np.array_equal(S.toarray(), [1, 0, 3]) + S.resize((5,)) + assert np.array_equal(S.toarray(), [1, 0, 3, 0, 0]) + + +@pytest.mark.parametrize("spcreator", [sp.sparse.dok_array]) +class TestGetSet1D: + def test_getelement(self, spcreator): + D = np.array([4, 3, 0]) + A = spcreator(D) + + N = D.shape[0] + for j in range(-N, N): + assert np.array_equal(A[j], D[j]) + + for ij in [3, -4]: + with pytest.raises( + (IndexError, TypeError), match='index value out of bounds' + ): + A.__getitem__(ij) + + # single element tuples unwrapped + assert A[(0,)] == 4 + + with pytest.raises(IndexError, match='index value out of bounds'): + A.__getitem__((4,)) + + def test_setelement(self, spcreator): + dtype = np.float64 + A = spcreator((12,), dtype=dtype) + with np.testing.suppress_warnings() as sup: + sup.filter( + sp.sparse.SparseEfficiencyWarning, + "Changing the sparsity structure of a cs[cr]_matrix is expensive", + ) + A[0] = dtype(0) + A[1] = dtype(3) + A[8] = dtype(9.0) + A[-2] = dtype(7) + A[5] = 9 + + A[-9,] = dtype(8) + A[1,] = dtype(5) # overwrite using 1-tuple index + + for ij in [13, -14, (13,), (14,)]: + with pytest.raises(IndexError, match='index value out of bounds'): + A.__setitem__(ij, 123.0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_construct.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_construct.py new file mode 100644 index 0000000000000000000000000000000000000000..ff174158cb99356733a0535cf38a43216a995895 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_construct.py @@ -0,0 +1,764 @@ +"""test sparse matrix construction functions""" + +import numpy as np +from numpy import array +from numpy.testing import (assert_equal, assert_, + assert_array_equal, assert_array_almost_equal_nulp) +import pytest +from pytest import raises as assert_raises +from scipy._lib._testutils import check_free_memory +from scipy._lib._util import check_random_state + +from scipy.sparse import (csr_matrix, coo_matrix, + csr_array, coo_array, + sparray, spmatrix, + _construct as construct) +from scipy.sparse._construct import rand as sprand + +sparse_formats = ['csr','csc','coo','bsr','dia','lil','dok'] + +#TODO check whether format=XXX is respected + + +def _sprandn(m, n, density=0.01, format="coo", dtype=None, random_state=None): + # Helper function for testing. + random_state = check_random_state(random_state) + data_rvs = random_state.standard_normal + return construct.random(m, n, density, format, dtype, + random_state, data_rvs) + + +def _sprandn_array(m, n, density=0.01, format="coo", dtype=None, random_state=None): + # Helper function for testing. + random_state = check_random_state(random_state) + data_sampler = random_state.standard_normal + return construct.random_array((m, n), density=density, format=format, dtype=dtype, + random_state=random_state, data_sampler=data_sampler) + + +class TestConstructUtils: + def test_spdiags(self): + diags1 = array([[1, 2, 3, 4, 5]]) + diags2 = array([[1, 2, 3, 4, 5], + [6, 7, 8, 9,10]]) + diags3 = array([[1, 2, 3, 4, 5], + [6, 7, 8, 9,10], + [11,12,13,14,15]]) + + cases = [] + cases.append((diags1, 0, 1, 1, [[1]])) + cases.append((diags1, [0], 1, 1, [[1]])) + cases.append((diags1, [0], 2, 1, [[1],[0]])) + cases.append((diags1, [0], 1, 2, [[1,0]])) + cases.append((diags1, [1], 1, 2, [[0,2]])) + cases.append((diags1,[-1], 1, 2, [[0,0]])) + cases.append((diags1, [0], 2, 2, [[1,0],[0,2]])) + cases.append((diags1,[-1], 2, 2, [[0,0],[1,0]])) + cases.append((diags1, [3], 2, 2, [[0,0],[0,0]])) + cases.append((diags1, [0], 3, 4, [[1,0,0,0],[0,2,0,0],[0,0,3,0]])) + cases.append((diags1, [1], 3, 4, [[0,2,0,0],[0,0,3,0],[0,0,0,4]])) + cases.append((diags1, [2], 3, 5, [[0,0,3,0,0],[0,0,0,4,0],[0,0,0,0,5]])) + + cases.append((diags2, [0,2], 3, 3, [[1,0,8],[0,2,0],[0,0,3]])) + cases.append((diags2, [-1,0], 3, 4, [[6,0,0,0],[1,7,0,0],[0,2,8,0]])) + cases.append((diags2, [2,-3], 6, 6, [[0,0,3,0,0,0], + [0,0,0,4,0,0], + [0,0,0,0,5,0], + [6,0,0,0,0,0], + [0,7,0,0,0,0], + [0,0,8,0,0,0]])) + + cases.append((diags3, [-1,0,1], 6, 6, [[6,12, 0, 0, 0, 0], + [1, 7,13, 0, 0, 0], + [0, 2, 8,14, 0, 0], + [0, 0, 3, 9,15, 0], + [0, 0, 0, 4,10, 0], + [0, 0, 0, 0, 5, 0]])) + cases.append((diags3, [-4,2,-1], 6, 5, [[0, 0, 8, 0, 0], + [11, 0, 0, 9, 0], + [0,12, 0, 0,10], + [0, 0,13, 0, 0], + [1, 0, 0,14, 0], + [0, 2, 0, 0,15]])) + cases.append((diags3, [-1, 1, 2], len(diags3[0]), len(diags3[0]), + [[0, 7, 13, 0, 0], + [1, 0, 8, 14, 0], + [0, 2, 0, 9, 15], + [0, 0, 3, 0, 10], + [0, 0, 0, 4, 0]])) + + for d, o, m, n, result in cases: + if len(d[0]) == m and m == n: + assert_equal(construct.spdiags(d, o).toarray(), result) + assert_equal(construct.spdiags(d, o, m, n).toarray(), result) + assert_equal(construct.spdiags(d, o, (m, n)).toarray(), result) + + def test_diags(self): + a = array([1, 2, 3, 4, 5]) + b = array([6, 7, 8, 9, 10]) + c = array([11, 12, 13, 14, 15]) + + cases = [] + cases.append((a[:1], 0, (1, 1), [[1]])) + cases.append(([a[:1]], [0], (1, 1), [[1]])) + cases.append(([a[:1]], [0], (2, 1), [[1],[0]])) + cases.append(([a[:1]], [0], (1, 2), [[1,0]])) + cases.append(([a[:1]], [1], (1, 2), [[0,1]])) + cases.append(([a[:2]], [0], (2, 2), [[1,0],[0,2]])) + cases.append(([a[:1]],[-1], (2, 2), [[0,0],[1,0]])) + cases.append(([a[:3]], [0], (3, 4), [[1,0,0,0],[0,2,0,0],[0,0,3,0]])) + cases.append(([a[:3]], [1], (3, 4), [[0,1,0,0],[0,0,2,0],[0,0,0,3]])) + cases.append(([a[:1]], [-2], (3, 5), [[0,0,0,0,0],[0,0,0,0,0],[1,0,0,0,0]])) + cases.append(([a[:2]], [-1], (3, 5), [[0,0,0,0,0],[1,0,0,0,0],[0,2,0,0,0]])) + cases.append(([a[:3]], [0], (3, 5), [[1,0,0,0,0],[0,2,0,0,0],[0,0,3,0,0]])) + cases.append(([a[:3]], [1], (3, 5), [[0,1,0,0,0],[0,0,2,0,0],[0,0,0,3,0]])) + cases.append(([a[:3]], [2], (3, 5), [[0,0,1,0,0],[0,0,0,2,0],[0,0,0,0,3]])) + cases.append(([a[:2]], [3], (3, 5), [[0,0,0,1,0],[0,0,0,0,2],[0,0,0,0,0]])) + cases.append(([a[:1]], [4], (3, 5), [[0,0,0,0,1],[0,0,0,0,0],[0,0,0,0,0]])) + cases.append(([a[:1]], [-4], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[1,0,0]])) + cases.append(([a[:2]], [-3], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[1,0,0],[0,2,0]])) + cases.append(([a[:3]], [-2], (5, 3), [[0,0,0],[0,0,0],[1,0,0],[0,2,0],[0,0,3]])) + cases.append(([a[:3]], [-1], (5, 3), [[0,0,0],[1,0,0],[0,2,0],[0,0,3],[0,0,0]])) + cases.append(([a[:3]], [0], (5, 3), [[1,0,0],[0,2,0],[0,0,3],[0,0,0],[0,0,0]])) + cases.append(([a[:2]], [1], (5, 3), [[0,1,0],[0,0,2],[0,0,0],[0,0,0],[0,0,0]])) + cases.append(([a[:1]], [2], (5, 3), [[0,0,1],[0,0,0],[0,0,0],[0,0,0],[0,0,0]])) + + cases.append(([a[:3],b[:1]], [0,2], (3, 3), [[1,0,6],[0,2,0],[0,0,3]])) + cases.append(([a[:2],b[:3]], [-1,0], (3, 4), [[6,0,0,0],[1,7,0,0],[0,2,8,0]])) + cases.append(([a[:4],b[:3]], [2,-3], (6, 6), [[0,0,1,0,0,0], + [0,0,0,2,0,0], + [0,0,0,0,3,0], + [6,0,0,0,0,4], + [0,7,0,0,0,0], + [0,0,8,0,0,0]])) + + cases.append(([a[:4],b,c[:4]], [-1,0,1], (5, 5), [[6,11, 0, 0, 0], + [1, 7,12, 0, 0], + [0, 2, 8,13, 0], + [0, 0, 3, 9,14], + [0, 0, 0, 4,10]])) + cases.append(([a[:2],b[:3],c], [-4,2,-1], (6, 5), [[0, 0, 6, 0, 0], + [11, 0, 0, 7, 0], + [0,12, 0, 0, 8], + [0, 0,13, 0, 0], + [1, 0, 0,14, 0], + [0, 2, 0, 0,15]])) + + # too long arrays are OK + cases.append(([a], [0], (1, 1), [[1]])) + cases.append(([a[:3],b], [0,2], (3, 3), [[1, 0, 6], [0, 2, 0], [0, 0, 3]])) + cases.append(( + np.array([[1, 2, 3], [4, 5, 6]]), + [0,-1], + (3, 3), + [[1, 0, 0], [4, 2, 0], [0, 5, 3]] + )) + + # scalar case: broadcasting + cases.append(([1,-2,1], [1,0,-1], (3, 3), [[-2, 1, 0], + [1, -2, 1], + [0, 1, -2]])) + + for d, o, shape, result in cases: + err_msg = f"{d!r} {o!r} {shape!r} {result!r}" + assert_equal(construct.diags(d, offsets=o, shape=shape).toarray(), + result, err_msg=err_msg) + + if (shape[0] == shape[1] + and hasattr(d[0], '__len__') + and len(d[0]) <= max(shape)): + # should be able to find the shape automatically + assert_equal(construct.diags(d, offsets=o).toarray(), result, + err_msg=err_msg) + + def test_diags_default(self): + a = array([1, 2, 3, 4, 5]) + assert_equal(construct.diags(a).toarray(), np.diag(a)) + + def test_diags_default_bad(self): + a = array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]) + assert_raises(ValueError, construct.diags, a) + + def test_diags_bad(self): + a = array([1, 2, 3, 4, 5]) + b = array([6, 7, 8, 9, 10]) + c = array([11, 12, 13, 14, 15]) + + cases = [] + cases.append(([a[:0]], 0, (1, 1))) + cases.append(([a[:4],b,c[:3]], [-1,0,1], (5, 5))) + cases.append(([a[:2],c,b[:3]], [-4,2,-1], (6, 5))) + cases.append(([a[:2],c,b[:3]], [-4,2,-1], None)) + cases.append(([], [-4,2,-1], None)) + cases.append(([1], [-5], (4, 4))) + cases.append(([a], 0, None)) + + for d, o, shape in cases: + assert_raises(ValueError, construct.diags, d, offsets=o, shape=shape) + + assert_raises(TypeError, construct.diags, [[None]], offsets=[0]) + + def test_diags_vs_diag(self): + # Check that + # + # diags([a, b, ...], [i, j, ...]) == diag(a, i) + diag(b, j) + ... + # + + np.random.seed(1234) + + for n_diags in [1, 2, 3, 4, 5, 10]: + n = 1 + n_diags//2 + np.random.randint(0, 10) + + offsets = np.arange(-n+1, n-1) + np.random.shuffle(offsets) + offsets = offsets[:n_diags] + + diagonals = [np.random.rand(n - abs(q)) for q in offsets] + + mat = construct.diags(diagonals, offsets=offsets) + dense_mat = sum([np.diag(x, j) for x, j in zip(diagonals, offsets)]) + + assert_array_almost_equal_nulp(mat.toarray(), dense_mat) + + if len(offsets) == 1: + mat = construct.diags(diagonals[0], offsets=offsets[0]) + dense_mat = np.diag(diagonals[0], offsets[0]) + assert_array_almost_equal_nulp(mat.toarray(), dense_mat) + + def test_diags_dtype(self): + x = construct.diags([2.2], offsets=[0], shape=(2, 2), dtype=int) + assert_equal(x.dtype, int) + assert_equal(x.toarray(), [[2, 0], [0, 2]]) + + def test_diags_one_diagonal(self): + d = list(range(5)) + for k in range(-5, 6): + assert_equal(construct.diags(d, offsets=k).toarray(), + construct.diags([d], offsets=[k]).toarray()) + + def test_diags_empty(self): + x = construct.diags([]) + assert_equal(x.shape, (0, 0)) + + @pytest.mark.parametrize("identity", [construct.identity, construct.eye_array]) + def test_identity(self, identity): + assert_equal(identity(1).toarray(), [[1]]) + assert_equal(identity(2).toarray(), [[1,0],[0,1]]) + + I = identity(3, dtype='int8', format='dia') + assert_equal(I.dtype, np.dtype('int8')) + assert_equal(I.format, 'dia') + + for fmt in sparse_formats: + I = identity(3, format=fmt) + assert_equal(I.format, fmt) + assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array]) + def test_eye(self, eye): + assert_equal(eye(1,1).toarray(), [[1]]) + assert_equal(eye(2,3).toarray(), [[1,0,0],[0,1,0]]) + assert_equal(eye(3,2).toarray(), [[1,0],[0,1],[0,0]]) + assert_equal(eye(3,3).toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + assert_equal(eye(3,3,dtype='int16').dtype, np.dtype('int16')) + + for m in [3, 5]: + for n in [3, 5]: + for k in range(-5,6): + # scipy.sparse.eye deviates from np.eye here. np.eye will + # create arrays of all 0's when the diagonal offset is + # greater than the size of the array. For sparse arrays + # this makes less sense, especially as it results in dia + # arrays with negative diagonals. Therefore sp.sparse.eye + # validates that diagonal offsets fall within the shape of + # the array. See gh-18555. + if (k > 0 and k > n) or (k < 0 and abs(k) > m): + with pytest.raises( + ValueError, match="Offset.*out of bounds" + ): + eye(m, n, k=k) + + else: + assert_equal( + eye(m, n, k=k).toarray(), + np.eye(m, n, k=k) + ) + if m == n: + assert_equal( + eye(m, k=k).toarray(), + np.eye(m, n, k=k) + ) + + @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array]) + def test_eye_one(self, eye): + assert_equal(eye(1).toarray(), [[1]]) + assert_equal(eye(2).toarray(), [[1,0],[0,1]]) + + I = eye(3, dtype='int8', format='dia') + assert_equal(I.dtype, np.dtype('int8')) + assert_equal(I.format, 'dia') + + for fmt in sparse_formats: + I = eye(3, format=fmt) + assert_equal(I.format, fmt) + assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + def test_eye_array_vs_matrix(self): + assert isinstance(construct.eye_array(3), sparray) + assert not isinstance(construct.eye(3), sparray) + + def test_kron(self): + cases = [] + + cases.append(array([[0]])) + cases.append(array([[-1]])) + cases.append(array([[4]])) + cases.append(array([[10]])) + cases.append(array([[0],[0]])) + cases.append(array([[0,0]])) + cases.append(array([[1,2],[3,4]])) + cases.append(array([[0,2],[5,0]])) + cases.append(array([[0,2,-6],[8,0,14]])) + cases.append(array([[5,4],[0,0],[6,0]])) + cases.append(array([[5,4,4],[1,0,0],[6,0,8]])) + cases.append(array([[0,1,0,2,0,5,8]])) + cases.append(array([[0.5,0.125,0,3.25],[0,2.5,0,0]])) + + # test all cases with some formats + for a in cases: + ca = csr_array(a) + for b in cases: + cb = csr_array(b) + expected = np.kron(a, b) + for fmt in sparse_formats[1:4]: + result = construct.kron(ca, cb, format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, sparray) + + # test one case with all formats + a = cases[-1] + b = cases[-3] + ca = csr_array(a) + cb = csr_array(b) + + expected = np.kron(a, b) + for fmt in sparse_formats: + result = construct.kron(ca, cb, format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, sparray) + + # check that spmatrix returned when both inputs are spmatrix + result = construct.kron(csr_matrix(a), csr_matrix(b), format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, spmatrix) + + def test_kron_large(self): + n = 2**16 + a = construct.diags_array([1], shape=(1, n), offsets=n-1) + b = construct.diags_array([1], shape=(n, 1), offsets=1-n) + + construct.kron(a, a) + construct.kron(b, b) + + def test_kronsum(self): + cases = [] + + cases.append(array([[0]])) + cases.append(array([[-1]])) + cases.append(array([[4]])) + cases.append(array([[10]])) + cases.append(array([[1,2],[3,4]])) + cases.append(array([[0,2],[5,0]])) + cases.append(array([[0,2,-6],[8,0,14],[0,3,0]])) + cases.append(array([[1,0,0],[0,5,-1],[4,-2,8]])) + + # test all cases with default format + for a in cases: + for b in cases: + result = construct.kronsum(csr_array(a), csr_array(b)).toarray() + expected = (np.kron(np.eye(b.shape[0]), a) + + np.kron(b, np.eye(a.shape[0]))) + assert_array_equal(result, expected) + + # check that spmatrix returned when both inputs are spmatrix + result = construct.kronsum(csr_matrix(a), csr_matrix(b)).toarray() + assert_array_equal(result, expected) + + @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array]) + def test_vstack(self, coo_cls): + A = coo_cls([[1,2],[3,4]]) + B = coo_cls([[5,6]]) + + expected = array([[1, 2], + [3, 4], + [5, 6]]) + assert_equal(construct.vstack([A, B]).toarray(), expected) + assert_equal(construct.vstack([A, B], dtype=np.float32).dtype, + np.float32) + + assert_equal(construct.vstack([A.todok(), B.todok()]).toarray(), expected) + + assert_equal(construct.vstack([A.tocsr(), B.tocsr()]).toarray(), + expected) + result = construct.vstack([A.tocsr(), B.tocsr()], + format="csr", dtype=np.float32) + assert_equal(result.dtype, np.float32) + assert_equal(result.indices.dtype, np.int32) + assert_equal(result.indptr.dtype, np.int32) + + assert_equal(construct.vstack([A.tocsc(), B.tocsc()]).toarray(), + expected) + result = construct.vstack([A.tocsc(), B.tocsc()], + format="csc", dtype=np.float32) + assert_equal(result.dtype, np.float32) + assert_equal(result.indices.dtype, np.int32) + assert_equal(result.indptr.dtype, np.int32) + + def test_vstack_matrix_or_array(self): + A = [[1,2],[3,4]] + B = [[5,6]] + assert isinstance(construct.vstack([coo_array(A), coo_array(B)]), sparray) + assert isinstance(construct.vstack([coo_array(A), coo_matrix(B)]), sparray) + assert isinstance(construct.vstack([coo_matrix(A), coo_array(B)]), sparray) + assert isinstance(construct.vstack([coo_matrix(A), coo_matrix(B)]), spmatrix) + + @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array]) + def test_hstack(self, coo_cls): + A = coo_cls([[1,2],[3,4]]) + B = coo_cls([[5],[6]]) + + expected = array([[1, 2, 5], + [3, 4, 6]]) + assert_equal(construct.hstack([A, B]).toarray(), expected) + assert_equal(construct.hstack([A, B], dtype=np.float32).dtype, + np.float32) + + assert_equal(construct.hstack([A.todok(), B.todok()]).toarray(), expected) + + assert_equal(construct.hstack([A.tocsc(), B.tocsc()]).toarray(), + expected) + assert_equal(construct.hstack([A.tocsc(), B.tocsc()], + dtype=np.float32).dtype, + np.float32) + assert_equal(construct.hstack([A.tocsr(), B.tocsr()]).toarray(), + expected) + assert_equal(construct.hstack([A.tocsr(), B.tocsr()], + dtype=np.float32).dtype, + np.float32) + + def test_hstack_matrix_or_array(self): + A = [[1,2],[3,4]] + B = [[5],[6]] + assert isinstance(construct.hstack([coo_array(A), coo_array(B)]), sparray) + assert isinstance(construct.hstack([coo_array(A), coo_matrix(B)]), sparray) + assert isinstance(construct.hstack([coo_matrix(A), coo_array(B)]), sparray) + assert isinstance(construct.hstack([coo_matrix(A), coo_matrix(B)]), spmatrix) + + @pytest.mark.parametrize("block_array", (construct.bmat, construct.block_array)) + def test_block_creation(self, block_array): + + A = coo_array([[1, 2], [3, 4]]) + B = coo_array([[5],[6]]) + C = coo_array([[7]]) + D = coo_array((0, 0)) + + expected = array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + assert_equal(block_array([[A, B], [None, C]]).toarray(), expected) + E = csr_array((1, 2), dtype=np.int32) + assert_equal(block_array([[A.tocsr(), B.tocsr()], + [E, C.tocsr()]]).toarray(), + expected) + assert_equal(block_array([[A.tocsc(), B.tocsc()], + [E.tocsc(), C.tocsc()]]).toarray(), + expected) + + expected = array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + assert_equal(block_array([[A, None], [None, C]]).toarray(), expected) + assert_equal(block_array([[A.tocsr(), E.T.tocsr()], + [E, C.tocsr()]]).toarray(), + expected) + assert_equal(block_array([[A.tocsc(), E.T.tocsc()], + [E.tocsc(), C.tocsc()]]).toarray(), + expected) + + Z = csr_array((1, 1), dtype=np.int32) + expected = array([[0, 5], + [0, 6], + [7, 0]]) + assert_equal(block_array([[None, B], [C, None]]).toarray(), expected) + assert_equal(block_array([[E.T.tocsr(), B.tocsr()], + [C.tocsr(), Z]]).toarray(), + expected) + assert_equal(block_array([[E.T.tocsc(), B.tocsc()], + [C.tocsc(), Z.tocsc()]]).toarray(), + expected) + + expected = np.empty((0, 0)) + assert_equal(block_array([[None, None]]).toarray(), expected) + assert_equal(block_array([[None, D], [D, None]]).toarray(), + expected) + + # test bug reported in gh-5976 + expected = array([[7]]) + assert_equal(block_array([[None, D], [C, None]]).toarray(), + expected) + + # test failure cases + with assert_raises(ValueError) as excinfo: + block_array([[A], [B]]) + excinfo.match(r'Got blocks\[1,0\]\.shape\[1\] == 1, expected 2') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsr()], [B.tocsr()]]) + excinfo.match(r'incompatible dimensions for axis 1') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsc()], [B.tocsc()]]) + excinfo.match(r'Mismatching dimensions along axis 1: ({1, 2}|{2, 1})') + + with assert_raises(ValueError) as excinfo: + block_array([[A, C]]) + excinfo.match(r'Got blocks\[0,1\]\.shape\[0\] == 1, expected 2') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsr(), C.tocsr()]]) + excinfo.match(r'Mismatching dimensions along axis 0: ({1, 2}|{2, 1})') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsc(), C.tocsc()]]) + excinfo.match(r'incompatible dimensions for axis 0') + + def test_block_return_type(self): + block = construct.block_array + + # csr format ensures we hit _compressed_sparse_stack + # shape of F,G ensure we hit _stack_along_minor_axis + # list version ensure we hit the path with neither helper function + Fl, Gl = [[1, 2],[3, 4]], [[7], [5]] + Fm, Gm = csr_matrix(Fl), csr_matrix(Gl) + assert isinstance(block([[None, Fl], [Gl, None]], format="csr"), sparray) + assert isinstance(block([[None, Fm], [Gm, None]], format="csr"), sparray) + assert isinstance(block([[Fm, Gm]], format="csr"), sparray) + + def test_bmat_return_type(self): + """This can be removed after sparse matrix is removed""" + bmat = construct.bmat + # check return type. if any input _is_array output array, else matrix + Fl, Gl = [[1, 2],[3, 4]], [[7], [5]] + Fm, Gm = csr_matrix(Fl), csr_matrix(Gl) + Fa, Ga = csr_array(Fl), csr_array(Gl) + assert isinstance(bmat([[Fa, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Fm, Gm]], format="csr"), spmatrix) + assert isinstance(bmat([[None, Fa], [Ga, None]], format="csr"), sparray) + assert isinstance(bmat([[None, Fm], [Ga, None]], format="csr"), sparray) + assert isinstance(bmat([[None, Fm], [Gm, None]], format="csr"), spmatrix) + assert isinstance(bmat([[None, Fl], [Gl, None]], format="csr"), spmatrix) + + # type returned by _compressed_sparse_stack (all csr) + assert isinstance(bmat([[Ga, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Ga, Gm]], format="csr"), sparray) + assert isinstance(bmat([[Gm, Gm]], format="csr"), spmatrix) + # shape is 2x2 so no _stack_along_minor_axis + assert isinstance(bmat([[Fa, Fm]], format="csr"), sparray) + assert isinstance(bmat([[Fm, Fm]], format="csr"), spmatrix) + + # type returned by _compressed_sparse_stack (all csc) + assert isinstance(bmat([[Gm.tocsc(), Ga.tocsc()]], format="csc"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Gm.tocsc()]], format="csc"), spmatrix) + # shape is 2x2 so no _stack_along_minor_axis + assert isinstance(bmat([[Fa.tocsc(), Fm.tocsc()]], format="csr"), sparray) + assert isinstance(bmat([[Fm.tocsc(), Fm.tocsc()]], format="csr"), spmatrix) + + # type returned when mixed input + assert isinstance(bmat([[Gl, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Gm]], format="csr"), spmatrix) + assert isinstance(bmat([[Gm, Gm]], format="csc"), spmatrix) + + @pytest.mark.slow + @pytest.mark.xfail_on_32bit("Can't create large array for test") + def test_concatenate_int32_overflow(self): + """ test for indptr overflow when concatenating matrices """ + check_free_memory(30000) + + n = 33000 + A = csr_array(np.ones((n, n), dtype=bool)) + B = A.copy() + C = construct._compressed_sparse_stack((A, B), axis=0, + return_spmatrix=False) + + assert_(np.all(np.equal(np.diff(C.indptr), n))) + assert_equal(C.indices.dtype, np.int64) + assert_equal(C.indptr.dtype, np.int64) + + def test_block_diag_basic(self): + """ basic test for block_diag """ + A = coo_array([[1,2],[3,4]]) + B = coo_array([[5],[6]]) + C = coo_array([[7]]) + + expected = array([[1, 2, 0, 0], + [3, 4, 0, 0], + [0, 0, 5, 0], + [0, 0, 6, 0], + [0, 0, 0, 7]]) + + assert_equal(construct.block_diag((A, B, C)).toarray(), expected) + + def test_block_diag_scalar_1d_args(self): + """ block_diag with scalar and 1d arguments """ + # one 1d matrix and a scalar + assert_array_equal(construct.block_diag([[2,3], 4]).toarray(), + [[2, 3, 0], [0, 0, 4]]) + # 1d sparse arrays + A = coo_array([1,0,3]) + B = coo_array([0,4]) + assert_array_equal(construct.block_diag([A, B]).toarray(), + [[1, 0, 3, 0, 0], [0, 0, 0, 0, 4]]) + + + def test_block_diag_1(self): + """ block_diag with one matrix """ + assert_equal(construct.block_diag([[1, 0]]).toarray(), + array([[1, 0]])) + assert_equal(construct.block_diag([[[1, 0]]]).toarray(), + array([[1, 0]])) + assert_equal(construct.block_diag([[[1], [0]]]).toarray(), + array([[1], [0]])) + # just on scalar + assert_equal(construct.block_diag([1]).toarray(), + array([[1]])) + + def test_block_diag_sparse_arrays(self): + """ block_diag with sparse arrays """ + + A = coo_array([[1, 2, 3]], shape=(1, 3)) + B = coo_array([[4, 5]], shape=(1, 2)) + assert_equal(construct.block_diag([A, B]).toarray(), + array([[1, 2, 3, 0, 0], [0, 0, 0, 4, 5]])) + + A = coo_array([[1], [2], [3]], shape=(3, 1)) + B = coo_array([[4], [5]], shape=(2, 1)) + assert_equal(construct.block_diag([A, B]).toarray(), + array([[1, 0], [2, 0], [3, 0], [0, 4], [0, 5]])) + + def test_block_diag_return_type(self): + A, B = coo_array([[1, 2, 3]]), coo_matrix([[2, 3, 4]]) + assert isinstance(construct.block_diag([A, A]), sparray) + assert isinstance(construct.block_diag([A, B]), sparray) + assert isinstance(construct.block_diag([B, A]), sparray) + assert isinstance(construct.block_diag([B, B]), spmatrix) + + def test_random_sampling(self): + # Simple sanity checks for sparse random sampling. + for f in sprand, _sprandn: + for t in [np.float32, np.float64, np.longdouble, + np.int32, np.int64, np.complex64, np.complex128]: + x = f(5, 10, density=0.1, dtype=t) + assert_equal(x.dtype, t) + assert_equal(x.shape, (5, 10)) + assert_equal(x.nnz, 5) + + x1 = f(5, 10, density=0.1, random_state=4321) + assert_equal(x1.dtype, np.float64) + + x2 = f(5, 10, density=0.1, + random_state=np.random.RandomState(4321)) + + assert_array_equal(x1.data, x2.data) + assert_array_equal(x1.row, x2.row) + assert_array_equal(x1.col, x2.col) + + for density in [0.0, 0.1, 0.5, 1.0]: + x = f(5, 10, density=density) + assert_equal(x.nnz, int(density * np.prod(x.shape))) + + for fmt in ['coo', 'csc', 'csr', 'lil']: + x = f(5, 10, format=fmt) + assert_equal(x.format, fmt) + + assert_raises(ValueError, lambda: f(5, 10, 1.1)) + assert_raises(ValueError, lambda: f(5, 10, -0.1)) + + def test_rand(self): + # Simple distributional checks for sparse.rand. + random_states = [None, 4321, np.random.RandomState()] + try: + gen = np.random.default_rng() + random_states.append(gen) + except AttributeError: + pass + + for random_state in random_states: + x = sprand(10, 20, density=0.5, dtype=np.float64, + random_state=random_state) + assert_(np.all(np.less_equal(0, x.data))) + assert_(np.all(np.less_equal(x.data, 1))) + + def test_randn(self): + # Simple distributional checks for sparse.randn. + # Statistically, some of these should be negative + # and some should be greater than 1. + random_states = [None, 4321, np.random.RandomState()] + try: + gen = np.random.default_rng() + random_states.append(gen) + except AttributeError: + pass + + for rs in random_states: + x = _sprandn(10, 20, density=0.5, dtype=np.float64, random_state=rs) + assert_(np.any(np.less(x.data, 0))) + assert_(np.any(np.less(1, x.data))) + x = _sprandn_array(10, 20, density=0.5, dtype=np.float64, random_state=rs) + assert_(np.any(np.less(x.data, 0))) + assert_(np.any(np.less(1, x.data))) + + def test_random_accept_str_dtype(self): + # anything that np.dtype can convert to a dtype should be accepted + # for the dtype + construct.random(10, 10, dtype='d') + construct.random_array((10, 10), dtype='d') + + def test_random_sparse_matrix_returns_correct_number_of_non_zero_elements(self): + # A 10 x 10 matrix, with density of 12.65%, should have 13 nonzero elements. + # 10 x 10 x 0.1265 = 12.65, which should be rounded up to 13, not 12. + sparse_matrix = construct.random(10, 10, density=0.1265) + assert_equal(sparse_matrix.count_nonzero(),13) + # check random_array + sparse_array = construct.random_array((10, 10), density=0.1265) + assert_equal(sparse_array.count_nonzero(),13) + assert isinstance(sparse_array, sparray) + # check big size + shape = (2**33, 2**33) + sparse_array = construct.random_array(shape, density=2.7105e-17) + assert_equal(sparse_array.count_nonzero(),2000) + + +def test_diags_array(): + """Tests of diags_array that do not rely on diags wrapper.""" + diag = np.arange(1, 5) + + assert_array_equal(construct.diags_array(diag).toarray(), np.diag(diag)) + + assert_array_equal( + construct.diags_array(diag, offsets=2).toarray(), np.diag(diag, k=2) + ) + + assert_array_equal( + construct.diags_array(diag, offsets=2, shape=(4, 4)).toarray(), + np.diag(diag, k=2)[:4, :4] + ) + + # Offset outside bounds when shape specified + with pytest.raises(ValueError, match=".*out of bounds"): + construct.diags(np.arange(1, 5), 5, shape=(4, 4)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_coo.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_coo.py new file mode 100644 index 0000000000000000000000000000000000000000..b9c80e9e16d61fdd4519b53af77bde8571c7d975 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_coo.py @@ -0,0 +1,274 @@ +import numpy as np +import pytest +from scipy.sparse import coo_array + + +def test_shape_constructor(): + empty1d = coo_array((3,)) + assert empty1d.shape == (3,) + assert np.array_equal(empty1d.toarray(), np.zeros((3,))) + + empty2d = coo_array((3, 2)) + assert empty2d.shape == (3, 2) + assert np.array_equal(empty2d.toarray(), np.zeros((3, 2))) + + with pytest.raises(TypeError, match='invalid input format'): + coo_array((3, 2, 2)) + + +def test_dense_constructor(): + res1d = coo_array([1, 2, 3]) + assert res1d.shape == (3,) + assert np.array_equal(res1d.toarray(), np.array([1, 2, 3])) + + res2d = coo_array([[1, 2, 3], [4, 5, 6]]) + assert res2d.shape == (2, 3) + assert np.array_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]])) + + with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'): + coo_array([[[3]], [[4]]]) + + +def test_dense_constructor_with_shape(): + res1d = coo_array([1, 2, 3], shape=(3,)) + assert res1d.shape == (3,) + assert np.array_equal(res1d.toarray(), np.array([1, 2, 3])) + + res2d = coo_array([[1, 2, 3], [4, 5, 6]], shape=(2, 3)) + assert res2d.shape == (2, 3) + assert np.array_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]])) + + with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'): + coo_array([[[3]], [[4]]], shape=(2, 1, 1)) + + +def test_dense_constructor_with_inconsistent_shape(): + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([1, 2, 3], shape=(4,)) + + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([1, 2, 3], shape=(3, 1)) + + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([[1, 2, 3]], shape=(3,)) + + with pytest.raises(ValueError, + match='axis 0 index 2 exceeds matrix dimension 2'): + coo_array(([1], ([2],)), shape=(2,)) + + with pytest.raises(ValueError, match='negative axis 0 index: -1'): + coo_array(([1], ([-1],))) + + +def test_1d_sparse_constructor(): + empty1d = coo_array((3,)) + res = coo_array(empty1d) + assert res.shape == (3,) + assert np.array_equal(res.toarray(), np.zeros((3,))) + + +def test_1d_tuple_constructor(): + res = coo_array(([9,8], ([1,2],))) + assert res.shape == (3,) + assert np.array_equal(res.toarray(), np.array([0, 9, 8])) + + +def test_1d_tuple_constructor_with_shape(): + res = coo_array(([9,8], ([1,2],)), shape=(4,)) + assert res.shape == (4,) + assert np.array_equal(res.toarray(), np.array([0, 9, 8, 0])) + +def test_non_subscriptability(): + coo_2d = coo_array((2, 2)) + + with pytest.raises(TypeError, + match="'coo_array' object does not support item assignment"): + coo_2d[0, 0] = 1 + + with pytest.raises(TypeError, + match="'coo_array' object is not subscriptable"): + coo_2d[0, :] + +def test_reshape(): + arr1d = coo_array([1, 0, 3]) + assert arr1d.shape == (3,) + + col_vec = arr1d.reshape((3, 1)) + assert col_vec.shape == (3, 1) + assert np.array_equal(col_vec.toarray(), np.array([[1], [0], [3]])) + + row_vec = arr1d.reshape((1, 3)) + assert row_vec.shape == (1, 3) + assert np.array_equal(row_vec.toarray(), np.array([[1, 0, 3]])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]) + assert arr2d.shape == (2, 3) + + flat = arr2d.reshape((6,)) + assert flat.shape == (6,) + assert np.array_equal(flat.toarray(), np.array([1, 2, 0, 0, 0, 3])) + + +def test_nnz(): + arr1d = coo_array([1, 0, 3]) + assert arr1d.shape == (3,) + assert arr1d.nnz == 2 + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]) + assert arr2d.shape == (2, 3) + assert arr2d.nnz == 3 + + +def test_transpose(): + arr1d = coo_array([1, 0, 3]).T + assert arr1d.shape == (3,) + assert np.array_equal(arr1d.toarray(), np.array([1, 0, 3])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).T + assert arr2d.shape == (3, 2) + assert np.array_equal(arr2d.toarray(), np.array([[1, 0], [2, 0], [0, 3]])) + + +def test_transpose_with_axis(): + arr1d = coo_array([1, 0, 3]).transpose(axes=(0,)) + assert arr1d.shape == (3,) + assert np.array_equal(arr1d.toarray(), np.array([1, 0, 3])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(0, 1)) + assert arr2d.shape == (2, 3) + assert np.array_equal(arr2d.toarray(), np.array([[1, 2, 0], [0, 0, 3]])) + + with pytest.raises(ValueError, match="axes don't match matrix dimensions"): + coo_array([1, 0, 3]).transpose(axes=(0, 1)) + + with pytest.raises(ValueError, match="repeated axis in transpose"): + coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(1, 1)) + + +def test_1d_row_and_col(): + res = coo_array([1, -2, -3]) + assert np.array_equal(res.col, np.array([0, 1, 2])) + assert np.array_equal(res.row, np.zeros_like(res.col)) + assert res.row.dtype == res.col.dtype + assert res.row.flags.writeable is False + + res.col = [1, 2, 3] + assert len(res.coords) == 1 + assert np.array_equal(res.col, np.array([1, 2, 3])) + assert res.row.dtype == res.col.dtype + + with pytest.raises(ValueError, match="cannot set row attribute"): + res.row = [1, 2, 3] + + +def test_1d_toformats(): + res = coo_array([1, -2, -3]) + for f in [res.tocsc, res.tocsr, res.todia, res.tolil, res.tobsr]: + with pytest.raises(ValueError, match='Cannot convert'): + f() + for f in [res.tocoo, res.todok]: + assert np.array_equal(f().toarray(), res.toarray()) + + +@pytest.mark.parametrize('arg', [1, 2, 4, 5, 8]) +def test_1d_resize(arg: int): + den = np.array([1, -2, -3]) + res = coo_array(den) + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert np.array_equal(res.toarray(), den) + + +@pytest.mark.parametrize('arg', zip([1, 2, 3, 4], [1, 2, 3, 4])) +def test_1d_to_2d_resize(arg: tuple[int, int]): + den = np.array([1, 0, 3]) + res = coo_array(den) + + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert np.array_equal(res.toarray(), den) + + +@pytest.mark.parametrize('arg', [1, 4, 6, 8]) +def test_2d_to_1d_resize(arg: int): + den = np.array([[1, 0, 3], [4, 0, 0]]) + res = coo_array(den) + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert np.array_equal(res.toarray(), den) + + +def test_sum_duplicates(): + arr1d = coo_array(([2, 2, 2], ([1, 0, 1],))) + assert arr1d.nnz == 3 + assert np.array_equal(arr1d.toarray(), np.array([2, 4])) + arr1d.sum_duplicates() + assert arr1d.nnz == 2 + assert np.array_equal(arr1d.toarray(), np.array([2, 4])) + + +def test_eliminate_zeros(): + arr1d = coo_array(([0, 0, 1], ([1, 0, 1],))) + assert arr1d.nnz == 3 + assert arr1d.count_nonzero() == 1 + assert np.array_equal(arr1d.toarray(), np.array([0, 1])) + arr1d.eliminate_zeros() + assert arr1d.nnz == 1 + assert arr1d.count_nonzero() == 1 + assert np.array_equal(arr1d.toarray(), np.array([0, 1])) + assert np.array_equal(arr1d.col, np.array([1])) + assert np.array_equal(arr1d.row, np.array([0])) + + +def test_1d_add_dense(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + exp = den_a + den_b + res = coo_array(den_a) + den_b + assert type(res) == type(exp) + assert np.array_equal(res, exp) + + +def test_1d_add_sparse(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + # Currently this routes through CSR format, so 1d sparse addition + # isn't supported. + with pytest.raises(ValueError, + match='Cannot convert a 1d sparse array'): + coo_array(den_a) + coo_array(den_b) + + +def test_1d_matmul_vector(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + exp = den_a @ den_b + res = coo_array(den_a) @ den_b + assert np.ndim(res) == 0 + assert np.array_equal(res, exp) + + +def test_1d_matmul_multivector(): + den = np.array([0, -2, -3, 0]) + other = np.array([[0, 1, 2, 3], [3, 2, 1, 0]]).T + exp = den @ other + res = coo_array(den) @ other + assert type(res) == type(exp) + assert np.array_equal(res, exp) + + +def test_2d_matmul_multivector(): + den = np.array([[0, 1, 2, 3], [3, 2, 1, 0]]) + arr2d = coo_array(den) + exp = den @ den.T + res = arr2d @ arr2d.T + assert np.array_equal(res.toarray(), exp) + + +def test_1d_diagonal(): + den = np.array([0, -2, -3, 0]) + with pytest.raises(ValueError, match='diagonal requires two dimensions'): + coo_array(den).diagonal() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csc.py new file mode 100644 index 0000000000000000000000000000000000000000..6313751e41899ae7c5daf01fbdbbacdc1f303fa1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csc.py @@ -0,0 +1,98 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_ +from scipy.sparse import csr_matrix, csc_matrix, lil_matrix + +import pytest + + +def test_csc_getrow(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsc = csc_matrix(X) + + for i in range(N): + arr_row = X[i:i + 1, :] + csc_row = Xcsc.getrow(i) + + assert_array_almost_equal(arr_row, csc_row.toarray()) + assert_(type(csc_row) is csr_matrix) + + +def test_csc_getcol(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsc = csc_matrix(X) + + for i in range(N): + arr_col = X[:, i:i + 1] + csc_col = Xcsc.getcol(i) + + assert_array_almost_equal(arr_col, csc_col.toarray()) + assert_(type(csc_col) is csc_matrix) + +@pytest.mark.parametrize("matrix_input, axis, expected_shape", + [(csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 0, (0, 2)), + (csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 1, (3, 0)), + (csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 'both', (0, 0)), + (csc_matrix([[0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 2, 3, 0, 1]]), + 0, (0, 6))]) +def test_csc_empty_slices(matrix_input, axis, expected_shape): + # see gh-11127 for related discussion + slice_1 = matrix_input.toarray().shape[0] - 1 + slice_2 = slice_1 + slice_3 = slice_2 - 1 + + if axis == 0: + actual_shape_1 = matrix_input[slice_1:slice_2, :].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, :].toarray().shape + elif axis == 1: + actual_shape_1 = matrix_input[:, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[:, slice_1:slice_3].toarray().shape + elif axis == 'both': + actual_shape_1 = matrix_input[slice_1:slice_2, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, slice_1:slice_3].toarray().shape + + assert actual_shape_1 == expected_shape + assert actual_shape_1 == actual_shape_2 + + +@pytest.mark.parametrize('ax', (-2, -1, 0, 1, None)) +def test_argmax_overflow(ax): + # See gh-13646: Windows integer overflow for large sparse matrices. + dim = (100000, 100000) + A = lil_matrix(dim) + A[-2, -2] = 42 + A[-3, -3] = 0.1234 + A = csc_matrix(A) + idx = A.argmax(axis=ax) + + if ax is None: + # idx is a single flattened index + # that we need to convert to a 2d index pair; + # can't do this with np.unravel_index because + # the dimensions are too large + ii = idx % dim[0] + jj = idx // dim[0] + else: + # idx is an array of size of A.shape[ax]; + # check the max index to make sure no overflows + # we encountered + assert np.count_nonzero(idx) == A.nnz + ii, jj = np.max(idx), np.argmax(idx) + + assert A[ii, jj] == A[-2, -2] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csr.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csr.py new file mode 100644 index 0000000000000000000000000000000000000000..57101c5d89f342149d56ab295aefa26ff5b497d0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_csr.py @@ -0,0 +1,184 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_ +from scipy.sparse import csr_matrix, csc_matrix, csr_array, csc_array, hstack +from scipy import sparse +import pytest + + +def _check_csr_rowslice(i, sl, X, Xcsr): + np_slice = X[i, sl] + csr_slice = Xcsr[i, sl] + assert_array_almost_equal(np_slice, csr_slice.toarray()[0]) + assert_(type(csr_slice) is csr_matrix) + + +def test_csr_rowslice(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + slices = [slice(None, None, None), + slice(None, None, -1), + slice(1, -2, 2), + slice(-2, 1, -2)] + + for i in range(N): + for sl in slices: + _check_csr_rowslice(i, sl, X, Xcsr) + + +def test_csr_getrow(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + for i in range(N): + arr_row = X[i:i + 1, :] + csr_row = Xcsr.getrow(i) + + assert_array_almost_equal(arr_row, csr_row.toarray()) + assert_(type(csr_row) is csr_matrix) + + +def test_csr_getcol(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + for i in range(N): + arr_col = X[:, i:i + 1] + csr_col = Xcsr.getcol(i) + + assert_array_almost_equal(arr_col, csr_col.toarray()) + assert_(type(csr_col) is csr_matrix) + +@pytest.mark.parametrize("matrix_input, axis, expected_shape", + [(csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 0, (0, 4)), + (csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 1, (3, 0)), + (csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 'both', (0, 0)), + (csr_matrix([[0, 1, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 2, 3, 0]]), + 0, (0, 5))]) +def test_csr_empty_slices(matrix_input, axis, expected_shape): + # see gh-11127 for related discussion + slice_1 = matrix_input.toarray().shape[0] - 1 + slice_2 = slice_1 + slice_3 = slice_2 - 1 + + if axis == 0: + actual_shape_1 = matrix_input[slice_1:slice_2, :].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, :].toarray().shape + elif axis == 1: + actual_shape_1 = matrix_input[:, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[:, slice_1:slice_3].toarray().shape + elif axis == 'both': + actual_shape_1 = matrix_input[slice_1:slice_2, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, slice_1:slice_3].toarray().shape + + assert actual_shape_1 == expected_shape + assert actual_shape_1 == actual_shape_2 + + +def test_csr_bool_indexing(): + data = csr_matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) + list_indices1 = [False, True, False] + array_indices1 = np.array(list_indices1) + list_indices2 = [[False, True, False], [False, True, False], [False, True, False]] + array_indices2 = np.array(list_indices2) + list_indices3 = ([False, True, False], [False, True, False]) + array_indices3 = (np.array(list_indices3[0]), np.array(list_indices3[1])) + slice_list1 = data[list_indices1].toarray() + slice_array1 = data[array_indices1].toarray() + slice_list2 = data[list_indices2] + slice_array2 = data[array_indices2] + slice_list3 = data[list_indices3] + slice_array3 = data[array_indices3] + assert (slice_list1 == slice_array1).all() + assert (slice_list2 == slice_array2).all() + assert (slice_list3 == slice_array3).all() + + +def test_csr_hstack_int64(): + """ + Tests if hstack properly promotes to indices and indptr arrays to np.int64 + when using np.int32 during concatenation would result in either array + overflowing. + """ + max_int32 = np.iinfo(np.int32).max + + # First case: indices would overflow with int32 + data = [1.0] + row = [0] + + max_indices_1 = max_int32 - 1 + max_indices_2 = 3 + + # Individual indices arrays are representable with int32 + col_1 = [max_indices_1 - 1] + col_2 = [max_indices_2 - 1] + + X_1 = csr_matrix((data, (row, col_1))) + X_2 = csr_matrix((data, (row, col_2))) + + assert max(max_indices_1 - 1, max_indices_2 - 1) < max_int32 + assert X_1.indices.dtype == X_1.indptr.dtype == np.int32 + assert X_2.indices.dtype == X_2.indptr.dtype == np.int32 + + # ... but when concatenating their CSR matrices, the resulting indices + # array can't be represented with int32 and must be promoted to int64. + X_hs = hstack([X_1, X_2], format="csr") + + assert X_hs.indices.max() == max_indices_1 + max_indices_2 - 1 + assert max_indices_1 + max_indices_2 - 1 > max_int32 + assert X_hs.indices.dtype == X_hs.indptr.dtype == np.int64 + + # Even if the matrices are empty, we must account for their size + # contribution so that we may safely set the final elements. + X_1_empty = csr_matrix(X_1.shape) + X_2_empty = csr_matrix(X_2.shape) + X_hs_empty = hstack([X_1_empty, X_2_empty], format="csr") + + assert X_hs_empty.shape == X_hs.shape + assert X_hs_empty.indices.dtype == np.int64 + + # Should be just small enough to stay in int32 after stack. Note that + # we theoretically could support indices.max() == max_int32, but due to an + # edge-case in the underlying sparsetools code + # (namely the `coo_tocsr` routine), + # we require that max(X_hs_32.shape) < max_int32 as well. + # Hence we can only support max_int32 - 1. + col_3 = [max_int32 - max_indices_1 - 1] + X_3 = csr_matrix((data, (row, col_3))) + X_hs_32 = hstack([X_1, X_3], format="csr") + assert X_hs_32.indices.dtype == np.int32 + assert X_hs_32.indices.max() == max_int32 - 1 + +@pytest.mark.parametrize("cls", [csr_matrix, csr_array, csc_matrix, csc_array]) +def test_mixed_index_dtype_int_indexing(cls): + # https://github.com/scipy/scipy/issues/20182 + rng = np.random.default_rng(0) + base_mtx = cls(sparse.random(50, 50, random_state=rng, density=0.1)) + indptr_64bit = base_mtx.copy() + indices_64bit = base_mtx.copy() + indptr_64bit.indptr = base_mtx.indptr.astype(np.int64) + indices_64bit.indices = base_mtx.indices.astype(np.int64) + + for mtx in [base_mtx, indptr_64bit, indices_64bit]: + np.testing.assert_array_equal(mtx[[1,2], :].toarray(), base_mtx[[1, 2], :].toarray()) + np.testing.assert_array_equal(mtx[:, [1, 2]].toarray(), base_mtx[:, [1, 2]].toarray()) \ No newline at end of file diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_deprecations.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_deprecations.py new file mode 100644 index 0000000000000000000000000000000000000000..c73b9bc4bb4783d5b2cf4331dadeb038f9deae54 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_deprecations.py @@ -0,0 +1,31 @@ +import scipy as sp +import pytest + + +def test_array_api_deprecations(): + X = sp.sparse.csr_array([ + [1,2,3], + [4,0,6] + ]) + msg = "1.14.0" + + with pytest.deprecated_call(match=msg): + X.get_shape() + + with pytest.deprecated_call(match=msg): + X.set_shape((2,3)) + + with pytest.deprecated_call(match=msg): + X.asfptype() + + with pytest.deprecated_call(match=msg): + X.getmaxprint() + + with pytest.deprecated_call(match=msg): + X.getH() + + with pytest.deprecated_call(match=msg): + X.getcol(1).todense() + + with pytest.deprecated_call(match=msg): + X.getrow(1).todense() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_dok.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_dok.py new file mode 100644 index 0000000000000000000000000000000000000000..8823ce8a2dbcf2ff3609f4e5df266458226744c2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_dok.py @@ -0,0 +1,210 @@ +import pytest +import numpy as np +from numpy.testing import assert_equal +import scipy as sp +from scipy.sparse import dok_array, dok_matrix + + +@pytest.fixture +def d(): + return {(0, 1): 1, (0, 2): 2} + +@pytest.fixture +def A(): + return np.array([[0, 1, 2], [0, 0, 0], [0, 0, 0]]) + +@pytest.fixture(params=[dok_array, dok_matrix]) +def Asp(request): + A = request.param((3, 3)) + A[(0, 1)] = 1 + A[(0, 2)] = 2 + yield A + +# Note: __iter__ and comparison dunders act like ndarrays for DOK, not dict. +# Dunders reversed, or, ror, ior work as dict for dok_matrix, raise for dok_array +# All other dict methods on DOK format act like dict methods (with extra checks). + +# Start of tests +################ +def test_dict_methods_covered(d, Asp): + d_methods = set(dir(d)) - {"__class_getitem__"} + asp_methods = set(dir(Asp)) + assert d_methods < asp_methods + +def test_clear(d, Asp): + assert d.items() == Asp.items() + d.clear() + Asp.clear() + assert d.items() == Asp.items() + +def test_copy(d, Asp): + assert d.items() == Asp.items() + dd = d.copy() + asp = Asp.copy() + assert dd.items() == asp.items() + assert asp.items() == Asp.items() + asp[(0, 1)] = 3 + assert Asp[(0, 1)] == 1 + +def test_fromkeys_default(): + # test with default value + edges = [(0, 2), (1, 0), (2, 1)] + Xdok = dok_array.fromkeys(edges) + X = [[0, 0, 1], [1, 0, 0], [0, 1, 0]] + assert_equal(Xdok.toarray(), X) + +def test_fromkeys_positional(): + # test with positional value + edges = [(0, 2), (1, 0), (2, 1)] + Xdok = dok_array.fromkeys(edges, -1) + X = [[0, 0, -1], [-1, 0, 0], [0, -1, 0]] + assert_equal(Xdok.toarray(), X) + +def test_fromkeys_iterator(): + it = ((a, a % 2) for a in range(4)) + Xdok = dok_array.fromkeys(it) + X = [[1, 0], [0, 1], [1, 0], [0, 1]] + assert_equal(Xdok.toarray(), X) + +def test_get(d, Asp): + assert Asp.get((0, 1)) == d.get((0, 1)) + assert Asp.get((0, 0), 99) == d.get((0, 0), 99) + with pytest.raises(IndexError, match="out of bounds"): + Asp.get((0, 4), 99) + +def test_items(d, Asp): + assert Asp.items() == d.items() + +def test_keys(d, Asp): + assert Asp.keys() == d.keys() + +def test_pop(d, Asp): + assert d.pop((0, 1)) == 1 + assert Asp.pop((0, 1)) == 1 + assert d.items() == Asp.items() + + assert Asp.pop((22, 21), None) is None + assert Asp.pop((22, 21), "other") == "other" + with pytest.raises(KeyError, match="(22, 21)"): + Asp.pop((22, 21)) + with pytest.raises(TypeError, match="got an unexpected keyword argument"): + Asp.pop((22, 21), default=5) + +def test_popitem(d, Asp): + assert d.popitem() == Asp.popitem() + assert d.items() == Asp.items() + +def test_setdefault(d, Asp): + assert Asp.setdefault((0, 1), 4) == 1 + assert Asp.setdefault((2, 2), 4) == 4 + d.setdefault((0, 1), 4) + d.setdefault((2, 2), 4) + assert d.items() == Asp.items() + +def test_update(d, Asp): + with pytest.raises(NotImplementedError): + Asp.update(Asp) + +def test_values(d, Asp): + # Note: dict.values are strange: d={1: 1}; d.values() == d.values() is False + # Using list(d.values()) makes them comparable. + assert list(Asp.values()) == list(d.values()) + +def test_dunder_getitem(d, Asp): + assert Asp[(0, 1)] == d[(0, 1)] + +def test_dunder_setitem(d, Asp): + Asp[(1, 1)] = 5 + d[(1, 1)] = 5 + assert d.items() == Asp.items() + +def test_dunder_delitem(d, Asp): + del Asp[(0, 1)] + del d[(0, 1)] + assert d.items() == Asp.items() + +def test_dunder_contains(d, Asp): + assert ((0, 1) in d) == ((0, 1) in Asp) + assert ((0, 0) in d) == ((0, 0) in Asp) + +def test_dunder_len(d, Asp): + assert len(d) == len(Asp) + +# Note: dunders reversed, or, ror, ior work as dict for dok_matrix, raise for dok_array +def test_dunder_reversed(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + list(reversed(Asp)) + else: + list(reversed(Asp)) == list(reversed(d)) + +def test_dunder_ior(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp |= Asp + else: + dd = {(0, 0): 5} + Asp |= dd + assert Asp[(0, 0)] == 5 + d |= dd + assert d.items() == Asp.items() + dd |= Asp + assert dd.items() == Asp.items() + +def test_dunder_or(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp | Asp + else: + assert d | d == Asp | d + assert d | d == Asp | Asp + +def test_dunder_ror(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp | Asp + with pytest.raises(TypeError): + d | Asp + else: + assert Asp.__ror__(d) == Asp.__ror__(Asp) + assert d.__ror__(d) == Asp.__ror__(d) + assert d | Asp + +# Note: comparison dunders, e.g. ==, >=, etc follow np.array not dict +def test_dunder_eq(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp == Asp).toarray().all() + assert (A == Asp).all() + +def test_dunder_ne(A, Asp): + assert not (Asp != Asp).toarray().any() + assert not (A != Asp).any() + +def test_dunder_lt(A, Asp): + assert not (Asp < Asp).toarray().any() + assert not (A < Asp).any() + +def test_dunder_gt(A, Asp): + assert not (Asp > Asp).toarray().any() + assert not (A > Asp).any() + +def test_dunder_le(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp <= Asp).toarray().all() + assert (A <= Asp).all() + +def test_dunder_ge(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp >= Asp).toarray().all() + assert (A >= Asp).all() + +# Note: iter dunder follows np.array not dict +def test_dunder_iter(A, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(NotImplementedError): + [a.toarray() for a in Asp] + else: + assert all((a == asp).all() for a, asp in zip(A, Asp)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_extract.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_extract.py new file mode 100644 index 0000000000000000000000000000000000000000..a7c9f68bb2bde76d74ca767abba3c99b89d6e771 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_extract.py @@ -0,0 +1,51 @@ +"""test sparse matrix construction functions""" + +from numpy.testing import assert_equal +from scipy.sparse import csr_matrix, csr_array, sparray + +import numpy as np +from scipy.sparse import _extract + + +class TestExtract: + def setup_method(self): + self.cases = [ + csr_array([[1,2]]), + csr_array([[1,0]]), + csr_array([[0,0]]), + csr_array([[1],[2]]), + csr_array([[1],[0]]), + csr_array([[0],[0]]), + csr_array([[1,2],[3,4]]), + csr_array([[0,1],[0,0]]), + csr_array([[0,0],[1,0]]), + csr_array([[0,0],[0,0]]), + csr_array([[1,2,0,0,3],[4,5,0,6,7],[0,0,8,9,0]]), + csr_array([[1,2,0,0,3],[4,5,0,6,7],[0,0,8,9,0]]).T, + ] + + def test_find(self): + for A in self.cases: + I,J,V = _extract.find(A) + B = csr_array((V,(I,J)), shape=A.shape) + assert_equal(A.toarray(), B.toarray()) + + def test_tril(self): + for A in self.cases: + B = A.toarray() + for k in [-3,-2,-1,0,1,2,3]: + assert_equal(_extract.tril(A,k=k).toarray(), np.tril(B,k=k)) + + def test_triu(self): + for A in self.cases: + B = A.toarray() + for k in [-3,-2,-1,0,1,2,3]: + assert_equal(_extract.triu(A,k=k).toarray(), np.triu(B,k=k)) + + def test_array_vs_matrix(self): + for A in self.cases: + assert isinstance(_extract.tril(A), sparray) + assert isinstance(_extract.triu(A), sparray) + M = csr_matrix(A) + assert not isinstance(_extract.tril(M), sparray) + assert not isinstance(_extract.triu(M), sparray) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_matrix_io.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_matrix_io.py new file mode 100644 index 0000000000000000000000000000000000000000..90b4ea64a8928073eb5dd3f1b2752379f57327d9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_matrix_io.py @@ -0,0 +1,109 @@ +import os +import numpy as np +import tempfile + +from pytest import raises as assert_raises +from numpy.testing import assert_equal, assert_ + +from scipy.sparse import (sparray, csc_matrix, csr_matrix, bsr_matrix, dia_matrix, + coo_matrix, dok_matrix, csr_array, save_npz, load_npz) + + +DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') + + +def _save_and_load(matrix): + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, matrix) + loaded_matrix = load_npz(tmpfile) + finally: + os.remove(tmpfile) + return loaded_matrix + +def _check_save_and_load(dense_matrix): + for matrix_class in [csc_matrix, csr_matrix, bsr_matrix, dia_matrix, coo_matrix]: + matrix = matrix_class(dense_matrix) + loaded_matrix = _save_and_load(matrix) + assert_(type(loaded_matrix) is matrix_class) + assert_(loaded_matrix.shape == dense_matrix.shape) + assert_(loaded_matrix.dtype == dense_matrix.dtype) + assert_equal(loaded_matrix.toarray(), dense_matrix) + +def test_save_and_load_random(): + N = 10 + np.random.seed(0) + dense_matrix = np.random.random((N, N)) + dense_matrix[dense_matrix > 0.7] = 0 + _check_save_and_load(dense_matrix) + +def test_save_and_load_empty(): + dense_matrix = np.zeros((4,6)) + _check_save_and_load(dense_matrix) + +def test_save_and_load_one_entry(): + dense_matrix = np.zeros((4,6)) + dense_matrix[1,2] = 1 + _check_save_and_load(dense_matrix) + +def test_sparray_vs_spmatrix(): + #save/load matrix + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, csr_matrix([[1.2, 0, 0.9], [0, 0.3, 0]])) + loaded_matrix = load_npz(tmpfile) + finally: + os.remove(tmpfile) + + #save/load array + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, csr_array([[1.2, 0, 0.9], [0, 0.3, 0]])) + loaded_array = load_npz(tmpfile) + finally: + os.remove(tmpfile) + + assert not isinstance(loaded_matrix, sparray) + assert isinstance(loaded_array, sparray) + assert_(loaded_matrix.dtype == loaded_array.dtype) + assert_equal(loaded_matrix.toarray(), loaded_array.toarray()) + +def test_malicious_load(): + class Executor: + def __reduce__(self): + return (assert_, (False, 'unexpected code execution')) + + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + np.savez(tmpfile, format=Executor()) + + # Should raise a ValueError, not execute code + assert_raises(ValueError, load_npz, tmpfile) + finally: + os.remove(tmpfile) + + +def test_py23_compatibility(): + # Try loading files saved on Python 2 and Python 3. They are not + # the same, since files saved with SciPy versions < 1.0.0 may + # contain unicode. + + a = load_npz(os.path.join(DATA_DIR, 'csc_py2.npz')) + b = load_npz(os.path.join(DATA_DIR, 'csc_py3.npz')) + c = csc_matrix([[0]]) + + assert_equal(a.toarray(), c.toarray()) + assert_equal(b.toarray(), c.toarray()) + +def test_implemented_error(): + # Attempts to save an unsupported type and checks that an + # NotImplementedError is raised. + + x = dok_matrix((2,3)) + x[0,1] = 1 + + assert_raises(NotImplementedError, save_npz, 'x.npz', x) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_minmax1d.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_minmax1d.py new file mode 100644 index 0000000000000000000000000000000000000000..53e9619314920195ba7083a0c499c3d8ebc32c37 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_minmax1d.py @@ -0,0 +1,82 @@ +"""Test of min-max 1D features of sparse array classes""" + +import pytest + +import numpy as np + +from numpy.testing import assert_equal, assert_array_equal + +from scipy.sparse import coo_array +from scipy.sparse._sputils import isscalarlike + + +def toarray(a): + if isinstance(a, np.ndarray) or isscalarlike(a): + return a + return a.toarray() + + +formats_for_minmax = [coo_array] + + +@pytest.mark.parametrize("spcreator", formats_for_minmax) +class Test_MinMaxMixin1D: + def test_minmax(self, spcreator): + D = np.arange(5) + X = spcreator(D) + + assert_equal(X.min(), 0) + assert_equal(X.max(), 4) + assert_equal((-X).min(), -4) + assert_equal((-X).max(), 0) + + + def test_minmax_axis(self, spcreator): + D = np.arange(50) + X = spcreator(D) + + for axis in [0, -1]: + assert_array_equal( + toarray(X.max(axis=axis)), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + toarray(X.min(axis=axis)), D.min(axis=axis, keepdims=True) + ) + for axis in [-2, 1]: + with pytest.raises(ValueError, match="axis out of range"): + X.min(axis=axis) + with pytest.raises(ValueError, match="axis out of range"): + X.max(axis=axis) + + + def test_numpy_minmax(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + assert_array_equal(np.min(datsp), np.min(dat)) + assert_array_equal(np.max(datsp), np.max(dat)) + + + def test_argmax(self, spcreator): + D1 = np.array([-1, 5, 2, 3]) + D2 = np.array([0, 0, -1, -2]) + D3 = np.array([-1, -2, -3, -4]) + D4 = np.array([1, 2, 3, 4]) + D5 = np.array([1, 2, 0, 0]) + + for D in [D1, D2, D3, D4, D5]: + mat = spcreator(D) + + assert_equal(mat.argmax(), np.argmax(D)) + assert_equal(mat.argmin(), np.argmin(D)) + + assert_equal(mat.argmax(axis=0), np.argmax(D, axis=0)) + assert_equal(mat.argmin(axis=0), np.argmin(D, axis=0)) + + D6 = np.empty((0,)) + + for axis in [None, 0]: + mat = spcreator(D6) + with pytest.raises(ValueError, match="to an empty matrix"): + mat.argmin(axis=axis) + with pytest.raises(ValueError, match="to an empty matrix"): + mat.argmax(axis=axis) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sparsetools.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sparsetools.py new file mode 100644 index 0000000000000000000000000000000000000000..6a8b94796116a22c210104fc446c5a17045ed21c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sparsetools.py @@ -0,0 +1,339 @@ +import sys +import os +import gc +import threading + +import numpy as np +from numpy.testing import assert_equal, assert_, assert_allclose +from scipy.sparse import (_sparsetools, coo_matrix, csr_matrix, csc_matrix, + bsr_matrix, dia_matrix) +from scipy.sparse._sputils import supported_dtypes +from scipy._lib._testutils import check_free_memory + +import pytest +from pytest import raises as assert_raises + + +def int_to_int8(n): + """ + Wrap an integer to the interval [-128, 127]. + """ + return (n + 128) % 256 - 128 + + +def test_exception(): + assert_raises(MemoryError, _sparsetools.test_throw_error) + + +def test_threads(): + # Smoke test for parallel threaded execution; doesn't actually + # check that code runs in parallel, but just that it produces + # expected results. + nthreads = 10 + niter = 100 + + n = 20 + a = csr_matrix(np.ones([n, n])) + bres = [] + + class Worker(threading.Thread): + def run(self): + b = a.copy() + for j in range(niter): + _sparsetools.csr_plus_csr(n, n, + a.indptr, a.indices, a.data, + a.indptr, a.indices, a.data, + b.indptr, b.indices, b.data) + bres.append(b) + + threads = [Worker() for _ in range(nthreads)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + for b in bres: + assert_(np.all(b.toarray() == 2)) + + +def test_regression_std_vector_dtypes(): + # Regression test for gh-3780, checking the std::vector typemaps + # in sparsetools.cxx are complete. + for dtype in supported_dtypes: + ad = np.array([[1, 2], [3, 4]]).astype(dtype) + a = csr_matrix(ad, dtype=dtype) + + # getcol is one function using std::vector typemaps, and should not fail + assert_equal(a.getcol(0).toarray(), ad[:, :1]) + + +@pytest.mark.slow +@pytest.mark.xfail_on_32bit("Can't create large array for test") +def test_nnz_overflow(): + # Regression test for gh-7230 / gh-7871, checking that coo_toarray + # with nnz > int32max doesn't overflow. + nnz = np.iinfo(np.int32).max + 1 + # Ensure ~20 GB of RAM is free to run this test. + check_free_memory((4 + 4 + 1) * nnz / 1e6 + 0.5) + + # Use nnz duplicate entries to keep the dense version small. + row = np.zeros(nnz, dtype=np.int32) + col = np.zeros(nnz, dtype=np.int32) + data = np.zeros(nnz, dtype=np.int8) + data[-1] = 4 + s = coo_matrix((data, (row, col)), shape=(1, 1), copy=False) + # Sums nnz duplicates to produce a 1x1 array containing 4. + d = s.toarray() + + assert_allclose(d, [[4]]) + + +@pytest.mark.skipif( + not (sys.platform.startswith('linux') and np.dtype(np.intp).itemsize >= 8), + reason="test requires 64-bit Linux" +) +class TestInt32Overflow: + """ + Some of the sparsetools routines use dense 2D matrices whose + total size is not bounded by the nnz of the sparse matrix. These + routines used to suffer from int32 wraparounds; here, we try to + check that the wraparounds don't occur any more. + """ + # choose n large enough + n = 50000 + + def setup_method(self): + assert self.n**2 > np.iinfo(np.int32).max + + # check there's enough memory even if everything is run at the + # same time + try: + parallel_count = int(os.environ.get('PYTEST_XDIST_WORKER_COUNT', '1')) + except ValueError: + parallel_count = np.inf + + check_free_memory(3000 * parallel_count) + + def teardown_method(self): + gc.collect() + + def test_coo_todense(self): + # Check *_todense routines (cf. gh-2179) + # + # All of them in the end call coo_matrix.todense + + n = self.n + + i = np.array([0, n-1]) + j = np.array([0, n-1]) + data = np.array([1, 2], dtype=np.int8) + m = coo_matrix((data, (i, j))) + + r = m.todense() + assert_equal(r[0,0], 1) + assert_equal(r[-1,-1], 2) + del r + gc.collect() + + @pytest.mark.slow + def test_matvecs(self): + # Check *_matvecs routines + n = self.n + + i = np.array([0, n-1]) + j = np.array([0, n-1]) + data = np.array([1, 2], dtype=np.int8) + m = coo_matrix((data, (i, j))) + + b = np.ones((n, n), dtype=np.int8) + for sptype in (csr_matrix, csc_matrix, bsr_matrix): + m2 = sptype(m) + r = m2.dot(b) + assert_equal(r[0,0], 1) + assert_equal(r[-1,-1], 2) + del r + gc.collect() + + del b + gc.collect() + + @pytest.mark.slow + def test_dia_matvec(self): + # Check: huge dia_matrix _matvec + n = self.n + data = np.ones((n, n), dtype=np.int8) + offsets = np.arange(n) + m = dia_matrix((data, offsets), shape=(n, n)) + v = np.ones(m.shape[1], dtype=np.int8) + r = m.dot(v) + assert_equal(r[0], int_to_int8(n)) + del data, offsets, m, v, r + gc.collect() + + _bsr_ops = [pytest.param("matmat", marks=pytest.mark.xslow), + pytest.param("matvecs", marks=pytest.mark.xslow), + "matvec", + "diagonal", + "sort_indices", + pytest.param("transpose", marks=pytest.mark.xslow)] + + @pytest.mark.slow + @pytest.mark.parametrize("op", _bsr_ops) + def test_bsr_1_block(self, op): + # Check: huge bsr_matrix (1-block) + # + # The point here is that indices inside a block may overflow. + + def get_matrix(): + n = self.n + data = np.ones((1, n, n), dtype=np.int8) + indptr = np.array([0, 1], dtype=np.int32) + indices = np.array([0], dtype=np.int32) + m = bsr_matrix((data, indices, indptr), blocksize=(n, n), copy=False) + del data, indptr, indices + return m + + gc.collect() + try: + getattr(self, "_check_bsr_" + op)(get_matrix) + finally: + gc.collect() + + @pytest.mark.slow + @pytest.mark.parametrize("op", _bsr_ops) + def test_bsr_n_block(self, op): + # Check: huge bsr_matrix (n-block) + # + # The point here is that while indices within a block don't + # overflow, accumulators across many block may. + + def get_matrix(): + n = self.n + data = np.ones((n, n, 1), dtype=np.int8) + indptr = np.array([0, n], dtype=np.int32) + indices = np.arange(n, dtype=np.int32) + m = bsr_matrix((data, indices, indptr), blocksize=(n, 1), copy=False) + del data, indptr, indices + return m + + gc.collect() + try: + getattr(self, "_check_bsr_" + op)(get_matrix) + finally: + gc.collect() + + def _check_bsr_matvecs(self, m): # skip name check + m = m() + n = self.n + + # _matvecs + r = m.dot(np.ones((n, 2), dtype=np.int8)) + assert_equal(r[0, 0], int_to_int8(n)) + + def _check_bsr_matvec(self, m): # skip name check + m = m() + n = self.n + + # _matvec + r = m.dot(np.ones((n,), dtype=np.int8)) + assert_equal(r[0], int_to_int8(n)) + + def _check_bsr_diagonal(self, m): # skip name check + m = m() + n = self.n + + # _diagonal + r = m.diagonal() + assert_equal(r, np.ones(n)) + + def _check_bsr_sort_indices(self, m): # skip name check + # _sort_indices + m = m() + m.sort_indices() + + def _check_bsr_transpose(self, m): # skip name check + # _transpose + m = m() + m.transpose() + + def _check_bsr_matmat(self, m): # skip name check + m = m() + n = self.n + + # _bsr_matmat + m2 = bsr_matrix(np.ones((n, 2), dtype=np.int8), blocksize=(m.blocksize[1], 2)) + m.dot(m2) # shouldn't SIGSEGV + del m2 + + # _bsr_matmat + m2 = bsr_matrix(np.ones((2, n), dtype=np.int8), blocksize=(2, m.blocksize[0])) + m2.dot(m) # shouldn't SIGSEGV + + +@pytest.mark.skip(reason="64-bit indices in sparse matrices not available") +def test_csr_matmat_int64_overflow(): + n = 3037000500 + assert n**2 > np.iinfo(np.int64).max + + # the test would take crazy amounts of memory + check_free_memory(n * (8*2 + 1) * 3 / 1e6) + + # int64 overflow + data = np.ones((n,), dtype=np.int8) + indptr = np.arange(n+1, dtype=np.int64) + indices = np.zeros(n, dtype=np.int64) + a = csr_matrix((data, indices, indptr)) + b = a.T + + assert_raises(RuntimeError, a.dot, b) + + +def test_upcast(): + a0 = csr_matrix([[np.pi, np.pi*1j], [3, 4]], dtype=complex) + b0 = np.array([256+1j, 2**32], dtype=complex) + + for a_dtype in supported_dtypes: + for b_dtype in supported_dtypes: + msg = f"({a_dtype!r}, {b_dtype!r})" + + if np.issubdtype(a_dtype, np.complexfloating): + a = a0.copy().astype(a_dtype) + else: + a = a0.real.copy().astype(a_dtype) + + if np.issubdtype(b_dtype, np.complexfloating): + b = b0.copy().astype(b_dtype) + else: + with np.errstate(invalid="ignore"): + # Casting a large value (2**32) to int8 causes a warning in + # numpy >1.23 + b = b0.real.copy().astype(b_dtype) + + if not (a_dtype == np.bool_ and b_dtype == np.bool_): + c = np.zeros((2,), dtype=np.bool_) + assert_raises(ValueError, _sparsetools.csr_matvec, + 2, 2, a.indptr, a.indices, a.data, b, c) + + if ((np.issubdtype(a_dtype, np.complexfloating) and + not np.issubdtype(b_dtype, np.complexfloating)) or + (not np.issubdtype(a_dtype, np.complexfloating) and + np.issubdtype(b_dtype, np.complexfloating))): + c = np.zeros((2,), dtype=np.float64) + assert_raises(ValueError, _sparsetools.csr_matvec, + 2, 2, a.indptr, a.indices, a.data, b, c) + + c = np.zeros((2,), dtype=np.result_type(a_dtype, b_dtype)) + _sparsetools.csr_matvec(2, 2, a.indptr, a.indices, a.data, b, c) + assert_allclose(c, np.dot(a.toarray(), b), err_msg=msg) + + +def test_endianness(): + d = np.ones((3,4)) + offsets = [-1,0,1] + + a = dia_matrix((d.astype('f8'), offsets), (4, 4)) + v = np.arange(4) + + assert_allclose(a.dot(v), [1, 3, 6, 5]) + assert_allclose(b.dot(v), [1, 3, 6, 5]) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_spfuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..75bc2d92c369be5799a904bc0938617f30321f12 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_spfuncs.py @@ -0,0 +1,97 @@ +from numpy import array, kron, diag +from numpy.testing import assert_, assert_equal + +from scipy.sparse import _spfuncs as spfuncs +from scipy.sparse import csr_matrix, csc_matrix, bsr_matrix +from scipy.sparse._sparsetools import (csr_scale_rows, csr_scale_columns, + bsr_scale_rows, bsr_scale_columns) + + +class TestSparseFunctions: + def test_scale_rows_and_cols(self): + D = array([[1, 0, 0, 2, 3], + [0, 4, 0, 5, 0], + [0, 0, 6, 7, 0]]) + + #TODO expose through function + S = csr_matrix(D) + v = array([1,2,3]) + csr_scale_rows(3,5,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@D) + + S = csr_matrix(D) + v = array([1,2,3,4,5]) + csr_scale_columns(3,5,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), D@diag(v)) + + # blocks + E = kron(D,[[1,2],[3,4]]) + S = bsr_matrix(E,blocksize=(2,2)) + v = array([1,2,3,4,5,6]) + bsr_scale_rows(3,5,2,2,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@E) + + S = bsr_matrix(E,blocksize=(2,2)) + v = array([1,2,3,4,5,6,7,8,9,10]) + bsr_scale_columns(3,5,2,2,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), E@diag(v)) + + E = kron(D,[[1,2,3],[4,5,6]]) + S = bsr_matrix(E,blocksize=(2,3)) + v = array([1,2,3,4,5,6]) + bsr_scale_rows(3,5,2,3,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@E) + + S = bsr_matrix(E,blocksize=(2,3)) + v = array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]) + bsr_scale_columns(3,5,2,3,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), E@diag(v)) + + def test_estimate_blocksize(self): + mats = [] + mats.append([[0,1],[1,0]]) + mats.append([[1,1,0],[0,0,1],[1,0,1]]) + mats.append([[0],[0],[1]]) + mats = [array(x) for x in mats] + + blks = [] + blks.append([[1]]) + blks.append([[1,1],[1,1]]) + blks.append([[1,1],[0,1]]) + blks.append([[1,1,0],[1,0,1],[1,1,1]]) + blks = [array(x) for x in blks] + + for A in mats: + for B in blks: + X = kron(A,B) + r,c = spfuncs.estimate_blocksize(X) + assert_(r >= B.shape[0]) + assert_(c >= B.shape[1]) + + def test_count_blocks(self): + def gold(A,bs): + R,C = bs + I,J = A.nonzero() + return len(set(zip(I//R,J//C))) + + mats = [] + mats.append([[0]]) + mats.append([[1]]) + mats.append([[1,0]]) + mats.append([[1,1]]) + mats.append([[0,1],[1,0]]) + mats.append([[1,1,0],[0,0,1],[1,0,1]]) + mats.append([[0],[0],[1]]) + + for A in mats: + for B in mats: + X = kron(A,B) + Y = csr_matrix(X) + for R in range(1,6): + for C in range(1,6): + assert_equal(spfuncs.count_blocks(Y, (R, C)), gold(X, (R, C))) + + X = kron([[1,1,0],[0,0,1],[1,0,1]],[[1,1]]) + Y = csc_matrix(X) + assert_equal(spfuncs.count_blocks(X, (1, 2)), gold(X, (1, 2))) + assert_equal(spfuncs.count_blocks(Y, (1, 2)), gold(X, (1, 2))) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sputils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..4545b49bea2cce465ae039ea7fc0f5a48b3da140 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/sparse/tests/test_sputils.py @@ -0,0 +1,196 @@ +"""unit tests for sparse utility functions""" + +import numpy as np +from numpy.testing import assert_equal +from pytest import raises as assert_raises +from scipy.sparse import _sputils as sputils +from scipy.sparse._sputils import matrix + + +class TestSparseUtils: + + def test_upcast(self): + assert_equal(sputils.upcast('intc'), np.intc) + assert_equal(sputils.upcast('int32', 'float32'), np.float64) + assert_equal(sputils.upcast('bool', complex, float), np.complex128) + assert_equal(sputils.upcast('i', 'd'), np.float64) + + def test_getdtype(self): + A = np.array([1], dtype='int8') + + assert_equal(sputils.getdtype(None, default=float), float) + assert_equal(sputils.getdtype(None, a=A), np.int8) + + with assert_raises( + ValueError, + match="object dtype is not supported by sparse matrices", + ): + sputils.getdtype("O") + + def test_isscalarlike(self): + assert_equal(sputils.isscalarlike(3.0), True) + assert_equal(sputils.isscalarlike(-4), True) + assert_equal(sputils.isscalarlike(2.5), True) + assert_equal(sputils.isscalarlike(1 + 3j), True) + assert_equal(sputils.isscalarlike(np.array(3)), True) + assert_equal(sputils.isscalarlike("16"), True) + + assert_equal(sputils.isscalarlike(np.array([3])), False) + assert_equal(sputils.isscalarlike([[3]]), False) + assert_equal(sputils.isscalarlike((1,)), False) + assert_equal(sputils.isscalarlike((1, 2)), False) + + def test_isintlike(self): + assert_equal(sputils.isintlike(-4), True) + assert_equal(sputils.isintlike(np.array(3)), True) + assert_equal(sputils.isintlike(np.array([3])), False) + with assert_raises( + ValueError, + match="Inexact indices into sparse matrices are not allowed" + ): + sputils.isintlike(3.0) + + assert_equal(sputils.isintlike(2.5), False) + assert_equal(sputils.isintlike(1 + 3j), False) + assert_equal(sputils.isintlike((1,)), False) + assert_equal(sputils.isintlike((1, 2)), False) + + def test_isshape(self): + assert_equal(sputils.isshape((1, 2)), True) + assert_equal(sputils.isshape((5, 2)), True) + + assert_equal(sputils.isshape((1.5, 2)), False) + assert_equal(sputils.isshape((2, 2, 2)), False) + assert_equal(sputils.isshape(([2], 2)), False) + assert_equal(sputils.isshape((-1, 2), nonneg=False),True) + assert_equal(sputils.isshape((2, -1), nonneg=False),True) + assert_equal(sputils.isshape((-1, 2), nonneg=True),False) + assert_equal(sputils.isshape((2, -1), nonneg=True),False) + + assert_equal(sputils.isshape((1.5, 2), allow_1d=True), False) + assert_equal(sputils.isshape(([2], 2), allow_1d=True), False) + assert_equal(sputils.isshape((2, 2, -2), nonneg=True, allow_1d=True), + False) + assert_equal(sputils.isshape((2,), allow_1d=True), True) + assert_equal(sputils.isshape((2, 2,), allow_1d=True), True) + assert_equal(sputils.isshape((2, 2, 2), allow_1d=True), False) + + def test_issequence(self): + assert_equal(sputils.issequence((1,)), True) + assert_equal(sputils.issequence((1, 2, 3)), True) + assert_equal(sputils.issequence([1]), True) + assert_equal(sputils.issequence([1, 2, 3]), True) + assert_equal(sputils.issequence(np.array([1, 2, 3])), True) + + assert_equal(sputils.issequence(np.array([[1], [2], [3]])), False) + assert_equal(sputils.issequence(3), False) + + def test_ismatrix(self): + assert_equal(sputils.ismatrix(((),)), True) + assert_equal(sputils.ismatrix([[1], [2]]), True) + assert_equal(sputils.ismatrix(np.arange(3)[None]), True) + + assert_equal(sputils.ismatrix([1, 2]), False) + assert_equal(sputils.ismatrix(np.arange(3)), False) + assert_equal(sputils.ismatrix([[[1]]]), False) + assert_equal(sputils.ismatrix(3), False) + + def test_isdense(self): + assert_equal(sputils.isdense(np.array([1])), True) + assert_equal(sputils.isdense(matrix([1])), True) + + def test_validateaxis(self): + assert_raises(TypeError, sputils.validateaxis, (0, 1)) + assert_raises(TypeError, sputils.validateaxis, 1.5) + assert_raises(ValueError, sputils.validateaxis, 3) + + # These function calls should not raise errors + for axis in (-2, -1, 0, 1, None): + sputils.validateaxis(axis) + + def test_get_index_dtype(self): + imax = np.int64(np.iinfo(np.int32).max) + too_big = imax + 1 + + # Check that uint32's with no values too large doesn't return + # int64 + a1 = np.ones(90, dtype='uint32') + a2 = np.ones(90, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int32') + ) + + # Check that if we can not convert but all values are less than or + # equal to max that we can just convert to int32 + a1[-1] = imax + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int32') + ) + + # Check that if it can not convert directly and the contents are + # too large that we return int64 + a1[-1] = too_big + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int64') + ) + + # test that if can not convert and didn't specify to check_contents + # we return int64 + a1 = np.ones(89, dtype='uint32') + a2 = np.ones(89, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2))), + np.dtype('int64') + ) + + # Check that even if we have arrays that can be converted directly + # that if we specify a maxval directly it takes precedence + a1 = np.ones(12, dtype='uint32') + a2 = np.ones(12, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype( + (a1, a2), maxval=too_big, check_contents=True + )), + np.dtype('int64') + ) + + # Check that an array with a too max size and maxval set + # still returns int64 + a1[-1] = too_big + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), maxval=too_big)), + np.dtype('int64') + ) + + def test_check_shape_overflow(self): + new_shape = sputils.check_shape([(10, -1)], (65535, 131070)) + assert_equal(new_shape, (10, 858967245)) + + def test_matrix(self): + a = [[1, 2, 3]] + b = np.array(a) + + assert isinstance(sputils.matrix(a), np.matrix) + assert isinstance(sputils.matrix(b), np.matrix) + + c = sputils.matrix(b) + c[:, :] = 123 + assert_equal(b, a) + + c = sputils.matrix(b, copy=False) + c[:, :] = 123 + assert_equal(b, [[123, 123, 123]]) + + def test_asmatrix(self): + a = [[1, 2, 3]] + b = np.array(a) + + assert isinstance(sputils.asmatrix(a), np.matrix) + assert isinstance(sputils.asmatrix(b), np.matrix) + + c = sputils.asmatrix(b) + c[:, :] = 123 + assert_equal(b, [[123, 123, 123]]) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9e9bc7aa18fc4a8d1b4452a71ecae6b2395dde0f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/__init__.py @@ -0,0 +1,129 @@ +""" +============================================================= +Spatial algorithms and data structures (:mod:`scipy.spatial`) +============================================================= + +.. currentmodule:: scipy.spatial + +.. toctree:: + :hidden: + + spatial.distance + +Spatial transformations +======================= + +These are contained in the `scipy.spatial.transform` submodule. + +Nearest-neighbor queries +======================== +.. autosummary:: + :toctree: generated/ + + KDTree -- class for efficient nearest-neighbor queries + cKDTree -- class for efficient nearest-neighbor queries (faster implementation) + Rectangle + +Distance metrics +================ + +Distance metrics are contained in the :mod:`scipy.spatial.distance` submodule. + +Delaunay triangulation, convex hulls, and Voronoi diagrams +========================================================== + +.. autosummary:: + :toctree: generated/ + + Delaunay -- compute Delaunay triangulation of input points + ConvexHull -- compute a convex hull for input points + Voronoi -- compute a Voronoi diagram hull from input points + SphericalVoronoi -- compute a Voronoi diagram from input points on the surface of a sphere + HalfspaceIntersection -- compute the intersection points of input halfspaces + +Plotting helpers +================ + +.. autosummary:: + :toctree: generated/ + + delaunay_plot_2d -- plot 2-D triangulation + convex_hull_plot_2d -- plot 2-D convex hull + voronoi_plot_2d -- plot 2-D Voronoi diagram + +.. seealso:: :ref:`Tutorial ` + + +Simplex representation +====================== +The simplices (triangles, tetrahedra, etc.) appearing in the Delaunay +tessellation (N-D simplices), convex hull facets, and Voronoi ridges +(N-1-D simplices) are represented in the following scheme:: + + tess = Delaunay(points) + hull = ConvexHull(points) + voro = Voronoi(points) + + # coordinates of the jth vertex of the ith simplex + tess.points[tess.simplices[i, j], :] # tessellation element + hull.points[hull.simplices[i, j], :] # convex hull facet + voro.vertices[voro.ridge_vertices[i, j], :] # ridge between Voronoi cells + +For Delaunay triangulations and convex hulls, the neighborhood +structure of the simplices satisfies the condition: +``tess.neighbors[i,j]`` is the neighboring simplex of the ith +simplex, opposite to the ``j``-vertex. It is -1 in case of no neighbor. + +Convex hull facets also define a hyperplane equation:: + + (hull.equations[i,:-1] * coord).sum() + hull.equations[i,-1] == 0 + +Similar hyperplane equations for the Delaunay triangulation correspond +to the convex hull facets on the corresponding N+1-D +paraboloid. + +The Delaunay triangulation objects offer a method for locating the +simplex containing a given point, and barycentric coordinate +computations. + +Functions +--------- + +.. autosummary:: + :toctree: generated/ + + tsearch + distance_matrix + minkowski_distance + minkowski_distance_p + procrustes + geometric_slerp + +Warnings / Errors used in :mod:`scipy.spatial` +---------------------------------------------- +.. autosummary:: + :toctree: generated/ + + QhullError +""" # noqa: E501 + +from ._kdtree import * +from ._ckdtree import * +from ._qhull import * +from ._spherical_voronoi import SphericalVoronoi +from ._plotutils import * +from ._procrustes import procrustes +from ._geometric_slerp import geometric_slerp + +# Deprecated namespaces, to be removed in v2.0.0 +from . import ckdtree, kdtree, qhull + +__all__ = [s for s in dir() if not s.startswith('_')] + +from . import distance, transform + +__all__ += ['distance', 'transform'] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_ckdtree.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_ckdtree.pyi new file mode 100644 index 0000000000000000000000000000000000000000..42670067a38c3021d20aeb39bb04920ab9ccde24 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_ckdtree.pyi @@ -0,0 +1,214 @@ +from __future__ import annotations +from typing import ( + Any, + Generic, + overload, + TypeVar, +) + +import numpy as np +import numpy.typing as npt +from scipy.sparse import coo_matrix, dok_matrix + +from typing import Literal + +# TODO: Replace `ndarray` with a 1D float64 array when possible +_BoxType = TypeVar("_BoxType", None, npt.NDArray[np.float64]) + +# Copied from `numpy.typing._scalar_like._ScalarLike` +# TODO: Expand with 0D arrays once we have shape support +_ArrayLike0D = bool | int | float | complex | str | bytes | np.generic + +_WeightType = npt.ArrayLike | tuple[npt.ArrayLike | None, npt.ArrayLike | None] + +class cKDTreeNode: + @property + def data_points(self) -> npt.NDArray[np.float64]: ... + @property + def indices(self) -> npt.NDArray[np.intp]: ... + + # These are read-only attributes in cython, which behave like properties + @property + def level(self) -> int: ... + @property + def split_dim(self) -> int: ... + @property + def children(self) -> int: ... + @property + def start_idx(self) -> int: ... + @property + def end_idx(self) -> int: ... + @property + def split(self) -> float: ... + @property + def lesser(self) -> cKDTreeNode | None: ... + @property + def greater(self) -> cKDTreeNode | None: ... + +class cKDTree(Generic[_BoxType]): + @property + def n(self) -> int: ... + @property + def m(self) -> int: ... + @property + def leafsize(self) -> int: ... + @property + def size(self) -> int: ... + @property + def tree(self) -> cKDTreeNode: ... + + # These are read-only attributes in cython, which behave like properties + @property + def data(self) -> npt.NDArray[np.float64]: ... + @property + def maxes(self) -> npt.NDArray[np.float64]: ... + @property + def mins(self) -> npt.NDArray[np.float64]: ... + @property + def indices(self) -> npt.NDArray[np.float64]: ... + @property + def boxsize(self) -> _BoxType: ... + + # NOTE: In practice `__init__` is used as constructor, not `__new__`. + # The latter gives us more flexibility in setting the generic parameter + # though. + @overload + def __new__( # type: ignore[misc] + cls, + data: npt.ArrayLike, + leafsize: int = ..., + compact_nodes: bool = ..., + copy_data: bool = ..., + balanced_tree: bool = ..., + boxsize: None = ..., + ) -> cKDTree[None]: ... + @overload + def __new__( + cls, + data: npt.ArrayLike, + leafsize: int = ..., + compact_nodes: bool = ..., + copy_data: bool = ..., + balanced_tree: bool = ..., + boxsize: npt.ArrayLike = ..., + ) -> cKDTree[npt.NDArray[np.float64]]: ... + + # TODO: returns a 2-tuple of scalars if `x.ndim == 1` and `k == 1`, + # returns a 2-tuple of arrays otherwise + def query( + self, + x: npt.ArrayLike, + k: npt.ArrayLike = ..., + eps: float = ..., + p: float = ..., + distance_upper_bound: float = ..., + workers: int | None = ..., + ) -> tuple[Any, Any]: ... + + # TODO: returns a list scalars if `x.ndim <= 1`, + # returns an object array of lists otherwise + def query_ball_point( + self, + x: npt.ArrayLike, + r: npt.ArrayLike, + p: float, + eps: float = ..., + workers: int | None = ..., + return_sorted: bool | None = ..., + return_length: bool = ... + ) -> Any: ... + + def query_ball_tree( + self, + other: cKDTree, + r: float, + p: float, + eps: float = ..., + ) -> list[list[int]]: ... + + @overload + def query_pairs( # type: ignore[misc] + self, + r: float, + p: float = ..., + eps: float = ..., + output_type: Literal["set"] = ..., + ) -> set[tuple[int, int]]: ... + @overload + def query_pairs( + self, + r: float, + p: float = ..., + eps: float = ..., + output_type: Literal["ndarray"] = ..., + ) -> npt.NDArray[np.intp]: ... + + @overload + def count_neighbors( # type: ignore[misc] + self, + other: cKDTree, + r: _ArrayLike0D, + p: float = ..., + weights: None | tuple[None, None] = ..., + cumulative: bool = ..., + ) -> int: ... + @overload + def count_neighbors( # type: ignore[misc] + self, + other: cKDTree, + r: _ArrayLike0D, + p: float = ..., + weights: _WeightType = ..., + cumulative: bool = ..., + ) -> np.float64: ... + @overload + def count_neighbors( # type: ignore[misc] + self, + other: cKDTree, + r: npt.ArrayLike, + p: float = ..., + weights: None | tuple[None, None] = ..., + cumulative: bool = ..., + ) -> npt.NDArray[np.intp]: ... + @overload + def count_neighbors( + self, + other: cKDTree, + r: npt.ArrayLike, + p: float = ..., + weights: _WeightType = ..., + cumulative: bool = ..., + ) -> npt.NDArray[np.float64]: ... + + @overload + def sparse_distance_matrix( # type: ignore[misc] + self, + other: cKDTree, + max_distance: float, + p: float = ..., + output_type: Literal["dok_matrix"] = ..., + ) -> dok_matrix: ... + @overload + def sparse_distance_matrix( # type: ignore[misc] + self, + other: cKDTree, + max_distance: float, + p: float = ..., + output_type: Literal["coo_matrix"] = ..., + ) -> coo_matrix: ... + @overload + def sparse_distance_matrix( # type: ignore[misc] + self, + other: cKDTree, + max_distance: float, + p: float = ..., + output_type: Literal["dict"] = ..., + ) -> dict[tuple[int, int], float]: ... + @overload + def sparse_distance_matrix( + self, + other: cKDTree, + max_distance: float, + p: float = ..., + output_type: Literal["ndarray"] = ..., + ) -> npt.NDArray[np.void]: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_geometric_slerp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_geometric_slerp.py new file mode 100644 index 0000000000000000000000000000000000000000..c3a7a81a1209bff2b1758aa2d6a5248a3d4360fd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_geometric_slerp.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +__all__ = ['geometric_slerp'] + +import warnings +from typing import TYPE_CHECKING + +import numpy as np +from scipy.spatial.distance import euclidean + +if TYPE_CHECKING: + import numpy.typing as npt + + +def _geometric_slerp(start, end, t): + # create an orthogonal basis using QR decomposition + basis = np.vstack([start, end]) + Q, R = np.linalg.qr(basis.T) + signs = 2 * (np.diag(R) >= 0) - 1 + Q = Q.T * signs.T[:, np.newaxis] + R = R.T * signs.T[:, np.newaxis] + + # calculate the angle between `start` and `end` + c = np.dot(start, end) + s = np.linalg.det(R) + omega = np.arctan2(s, c) + + # interpolate + start, end = Q + s = np.sin(t * omega) + c = np.cos(t * omega) + return start * c[:, np.newaxis] + end * s[:, np.newaxis] + + +def geometric_slerp( + start: npt.ArrayLike, + end: npt.ArrayLike, + t: npt.ArrayLike, + tol: float = 1e-7, +) -> np.ndarray: + """ + Geometric spherical linear interpolation. + + The interpolation occurs along a unit-radius + great circle arc in arbitrary dimensional space. + + Parameters + ---------- + start : (n_dimensions, ) array-like + Single n-dimensional input coordinate in a 1-D array-like + object. `n` must be greater than 1. + end : (n_dimensions, ) array-like + Single n-dimensional input coordinate in a 1-D array-like + object. `n` must be greater than 1. + t : float or (n_points,) 1D array-like + A float or 1D array-like of doubles representing interpolation + parameters, with values required in the inclusive interval + between 0 and 1. A common approach is to generate the array + with ``np.linspace(0, 1, n_pts)`` for linearly spaced points. + Ascending, descending, and scrambled orders are permitted. + tol : float + The absolute tolerance for determining if the start and end + coordinates are antipodes. + + Returns + ------- + result : (t.size, D) + An array of doubles containing the interpolated + spherical path and including start and + end when 0 and 1 t are used. The + interpolated values should correspond to the + same sort order provided in the t array. The result + may be 1-dimensional if ``t`` is a float. + + Raises + ------ + ValueError + If ``start`` and ``end`` are antipodes, not on the + unit n-sphere, or for a variety of degenerate conditions. + + See Also + -------- + scipy.spatial.transform.Slerp : 3-D Slerp that works with quaternions + + Notes + ----- + The implementation is based on the mathematical formula provided in [1]_, + and the first known presentation of this algorithm, derived from study of + 4-D geometry, is credited to Glenn Davis in a footnote of the original + quaternion Slerp publication by Ken Shoemake [2]_. + + .. versionadded:: 1.5.0 + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Slerp#Geometric_Slerp + .. [2] Ken Shoemake (1985) Animating rotation with quaternion curves. + ACM SIGGRAPH Computer Graphics, 19(3): 245-254. + + Examples + -------- + Interpolate four linearly-spaced values on the circumference of + a circle spanning 90 degrees: + + >>> import numpy as np + >>> from scipy.spatial import geometric_slerp + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> start = np.array([1, 0]) + >>> end = np.array([0, 1]) + >>> t_vals = np.linspace(0, 1, 4) + >>> result = geometric_slerp(start, + ... end, + ... t_vals) + + The interpolated results should be at 30 degree intervals + recognizable on the unit circle: + + >>> ax.scatter(result[...,0], result[...,1], c='k') + >>> circle = plt.Circle((0, 0), 1, color='grey') + >>> ax.add_artist(circle) + >>> ax.set_aspect('equal') + >>> plt.show() + + Attempting to interpolate between antipodes on a circle is + ambiguous because there are two possible paths, and on a + sphere there are infinite possible paths on the geodesic surface. + Nonetheless, one of the ambiguous paths is returned along + with a warning: + + >>> opposite_pole = np.array([-1, 0]) + >>> with np.testing.suppress_warnings() as sup: + ... sup.filter(UserWarning) + ... geometric_slerp(start, + ... opposite_pole, + ... t_vals) + array([[ 1.00000000e+00, 0.00000000e+00], + [ 5.00000000e-01, 8.66025404e-01], + [-5.00000000e-01, 8.66025404e-01], + [-1.00000000e+00, 1.22464680e-16]]) + + Extend the original example to a sphere and plot interpolation + points in 3D: + + >>> from mpl_toolkits.mplot3d import proj3d + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111, projection='3d') + + Plot the unit sphere for reference (optional): + + >>> u = np.linspace(0, 2 * np.pi, 100) + >>> v = np.linspace(0, np.pi, 100) + >>> x = np.outer(np.cos(u), np.sin(v)) + >>> y = np.outer(np.sin(u), np.sin(v)) + >>> z = np.outer(np.ones(np.size(u)), np.cos(v)) + >>> ax.plot_surface(x, y, z, color='y', alpha=0.1) + + Interpolating over a larger number of points + may provide the appearance of a smooth curve on + the surface of the sphere, which is also useful + for discretized integration calculations on a + sphere surface: + + >>> start = np.array([1, 0, 0]) + >>> end = np.array([0, 0, 1]) + >>> t_vals = np.linspace(0, 1, 200) + >>> result = geometric_slerp(start, + ... end, + ... t_vals) + >>> ax.plot(result[...,0], + ... result[...,1], + ... result[...,2], + ... c='k') + >>> plt.show() + """ + + start = np.asarray(start, dtype=np.float64) + end = np.asarray(end, dtype=np.float64) + t = np.asarray(t) + + if t.ndim > 1: + raise ValueError("The interpolation parameter " + "value must be one dimensional.") + + if start.ndim != 1 or end.ndim != 1: + raise ValueError("Start and end coordinates " + "must be one-dimensional") + + if start.size != end.size: + raise ValueError("The dimensions of start and " + "end must match (have same size)") + + if start.size < 2 or end.size < 2: + raise ValueError("The start and end coordinates must " + "both be in at least two-dimensional " + "space") + + if np.array_equal(start, end): + return np.linspace(start, start, t.size) + + # for points that violate equation for n-sphere + for coord in [start, end]: + if not np.allclose(np.linalg.norm(coord), 1.0, + rtol=1e-9, + atol=0): + raise ValueError("start and end are not" + " on a unit n-sphere") + + if not isinstance(tol, float): + raise ValueError("tol must be a float") + else: + tol = np.fabs(tol) + + coord_dist = euclidean(start, end) + + # diameter of 2 within tolerance means antipodes, which is a problem + # for all unit n-spheres (even the 0-sphere would have an ambiguous path) + if np.allclose(coord_dist, 2.0, rtol=0, atol=tol): + warnings.warn("start and end are antipodes " + "using the specified tolerance; " + "this may cause ambiguous slerp paths", + stacklevel=2) + + t = np.asarray(t, dtype=np.float64) + + if t.size == 0: + return np.empty((0, start.size)) + + if t.min() < 0 or t.max() > 1: + raise ValueError("interpolation parameter must be in [0, 1]") + + if t.ndim == 0: + return _geometric_slerp(start, + end, + np.atleast_1d(t)).ravel() + else: + return _geometric_slerp(start, + end, + t) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_kdtree.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_kdtree.py new file mode 100644 index 0000000000000000000000000000000000000000..f65412e9e4ad3a0e0da3876c861430d678fe858c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_kdtree.py @@ -0,0 +1,920 @@ +# Copyright Anne M. Archibald 2008 +# Released under the scipy license +import numpy as np +from ._ckdtree import cKDTree, cKDTreeNode + +__all__ = ['minkowski_distance_p', 'minkowski_distance', + 'distance_matrix', + 'Rectangle', 'KDTree'] + + +def minkowski_distance_p(x, y, p=2): + """Compute the pth power of the L**p distance between two arrays. + + For efficiency, this function computes the L**p distance but does + not extract the pth root. If `p` is 1 or infinity, this is equal to + the actual L**p distance. + + The last dimensions of `x` and `y` must be the same length. Any + other dimensions must be compatible for broadcasting. + + Parameters + ---------- + x : (..., K) array_like + Input array. + y : (..., K) array_like + Input array. + p : float, 1 <= p <= infinity + Which Minkowski p-norm to use. + + Returns + ------- + dist : ndarray + pth power of the distance between the input arrays. + + Examples + -------- + >>> from scipy.spatial import minkowski_distance_p + >>> minkowski_distance_p([[0, 0], [0, 0]], [[1, 1], [0, 1]]) + array([2, 1]) + + """ + x = np.asarray(x) + y = np.asarray(y) + + # Find smallest common datatype with float64 (return type of this + # function) - addresses #10262. + # Don't just cast to float64 for complex input case. + common_datatype = np.promote_types(np.promote_types(x.dtype, y.dtype), + 'float64') + + # Make sure x and y are NumPy arrays of correct datatype. + x = x.astype(common_datatype) + y = y.astype(common_datatype) + + if p == np.inf: + return np.amax(np.abs(y-x), axis=-1) + elif p == 1: + return np.sum(np.abs(y-x), axis=-1) + else: + return np.sum(np.abs(y-x)**p, axis=-1) + + +def minkowski_distance(x, y, p=2): + """Compute the L**p distance between two arrays. + + The last dimensions of `x` and `y` must be the same length. Any + other dimensions must be compatible for broadcasting. + + Parameters + ---------- + x : (..., K) array_like + Input array. + y : (..., K) array_like + Input array. + p : float, 1 <= p <= infinity + Which Minkowski p-norm to use. + + Returns + ------- + dist : ndarray + Distance between the input arrays. + + Examples + -------- + >>> from scipy.spatial import minkowski_distance + >>> minkowski_distance([[0, 0], [0, 0]], [[1, 1], [0, 1]]) + array([ 1.41421356, 1. ]) + + """ + x = np.asarray(x) + y = np.asarray(y) + if p == np.inf or p == 1: + return minkowski_distance_p(x, y, p) + else: + return minkowski_distance_p(x, y, p)**(1./p) + + +class Rectangle: + """Hyperrectangle class. + + Represents a Cartesian product of intervals. + """ + def __init__(self, maxes, mins): + """Construct a hyperrectangle.""" + self.maxes = np.maximum(maxes,mins).astype(float) + self.mins = np.minimum(maxes,mins).astype(float) + self.m, = self.maxes.shape + + def __repr__(self): + return "" % list(zip(self.mins, self.maxes)) + + def volume(self): + """Total volume.""" + return np.prod(self.maxes-self.mins) + + def split(self, d, split): + """Produce two hyperrectangles by splitting. + + In general, if you need to compute maximum and minimum + distances to the children, it can be done more efficiently + by updating the maximum and minimum distances to the parent. + + Parameters + ---------- + d : int + Axis to split hyperrectangle along. + split : float + Position along axis `d` to split at. + + """ + mid = np.copy(self.maxes) + mid[d] = split + less = Rectangle(self.mins, mid) + mid = np.copy(self.mins) + mid[d] = split + greater = Rectangle(mid, self.maxes) + return less, greater + + def min_distance_point(self, x, p=2.): + """ + Return the minimum distance between input and points in the + hyperrectangle. + + Parameters + ---------- + x : array_like + Input. + p : float, optional + Input. + + """ + return minkowski_distance( + 0, np.maximum(0, np.maximum(self.mins-x, x-self.maxes)), + p + ) + + def max_distance_point(self, x, p=2.): + """ + Return the maximum distance between input and points in the hyperrectangle. + + Parameters + ---------- + x : array_like + Input array. + p : float, optional + Input. + + """ + return minkowski_distance(0, np.maximum(self.maxes-x, x-self.mins), p) + + def min_distance_rectangle(self, other, p=2.): + """ + Compute the minimum distance between points in the two hyperrectangles. + + Parameters + ---------- + other : hyperrectangle + Input. + p : float + Input. + + """ + return minkowski_distance( + 0, + np.maximum(0, np.maximum(self.mins-other.maxes, + other.mins-self.maxes)), + p + ) + + def max_distance_rectangle(self, other, p=2.): + """ + Compute the maximum distance between points in the two hyperrectangles. + + Parameters + ---------- + other : hyperrectangle + Input. + p : float, optional + Input. + + """ + return minkowski_distance( + 0, np.maximum(self.maxes-other.mins, other.maxes-self.mins), p) + + +class KDTree(cKDTree): + """kd-tree for quick nearest-neighbor lookup. + + This class provides an index into a set of k-dimensional points + which can be used to rapidly look up the nearest neighbors of any + point. + + Parameters + ---------- + data : array_like, shape (n,m) + The n data points of dimension m to be indexed. This array is + not copied unless this is necessary to produce a contiguous + array of doubles, and so modifying this data will result in + bogus results. The data are also copied if the kd-tree is built + with copy_data=True. + leafsize : positive int, optional + The number of points at which the algorithm switches over to + brute-force. Default: 10. + compact_nodes : bool, optional + If True, the kd-tree is built to shrink the hyperrectangles to + the actual data range. This usually gives a more compact tree that + is robust against degenerated input data and gives faster queries + at the expense of longer build time. Default: True. + copy_data : bool, optional + If True the data is always copied to protect the kd-tree against + data corruption. Default: False. + balanced_tree : bool, optional + If True, the median is used to split the hyperrectangles instead of + the midpoint. This usually gives a more compact tree and + faster queries at the expense of longer build time. Default: True. + boxsize : array_like or scalar, optional + Apply a m-d toroidal topology to the KDTree.. The topology is generated + by :math:`x_i + n_i L_i` where :math:`n_i` are integers and :math:`L_i` + is the boxsize along i-th dimension. The input data shall be wrapped + into :math:`[0, L_i)`. A ValueError is raised if any of the data is + outside of this bound. + + Notes + ----- + The algorithm used is described in Maneewongvatana and Mount 1999. + The general idea is that the kd-tree is a binary tree, each of whose + nodes represents an axis-aligned hyperrectangle. Each node specifies + an axis and splits the set of points based on whether their coordinate + along that axis is greater than or less than a particular value. + + During construction, the axis and splitting point are chosen by the + "sliding midpoint" rule, which ensures that the cells do not all + become long and thin. + + The tree can be queried for the r closest neighbors of any given point + (optionally returning only those within some maximum distance of the + point). It can also be queried, with a substantial gain in efficiency, + for the r approximate closest neighbors. + + For large dimensions (20 is already large) do not expect this to run + significantly faster than brute force. High-dimensional nearest-neighbor + queries are a substantial open problem in computer science. + + Attributes + ---------- + data : ndarray, shape (n,m) + The n data points of dimension m to be indexed. This array is + not copied unless this is necessary to produce a contiguous + array of doubles. The data are also copied if the kd-tree is built + with `copy_data=True`. + leafsize : positive int + The number of points at which the algorithm switches over to + brute-force. + m : int + The dimension of a single data-point. + n : int + The number of data points. + maxes : ndarray, shape (m,) + The maximum value in each dimension of the n data points. + mins : ndarray, shape (m,) + The minimum value in each dimension of the n data points. + size : int + The number of nodes in the tree. + + """ + + class node: + @staticmethod + def _create(ckdtree_node=None): + """Create either an inner or leaf node, wrapping a cKDTreeNode instance""" + if ckdtree_node is None: + return KDTree.node(ckdtree_node) + elif ckdtree_node.split_dim == -1: + return KDTree.leafnode(ckdtree_node) + else: + return KDTree.innernode(ckdtree_node) + + def __init__(self, ckdtree_node=None): + if ckdtree_node is None: + ckdtree_node = cKDTreeNode() + self._node = ckdtree_node + + def __lt__(self, other): + return id(self) < id(other) + + def __gt__(self, other): + return id(self) > id(other) + + def __le__(self, other): + return id(self) <= id(other) + + def __ge__(self, other): + return id(self) >= id(other) + + def __eq__(self, other): + return id(self) == id(other) + + class leafnode(node): + @property + def idx(self): + return self._node.indices + + @property + def children(self): + return self._node.children + + class innernode(node): + def __init__(self, ckdtreenode): + assert isinstance(ckdtreenode, cKDTreeNode) + super().__init__(ckdtreenode) + self.less = KDTree.node._create(ckdtreenode.lesser) + self.greater = KDTree.node._create(ckdtreenode.greater) + + @property + def split_dim(self): + return self._node.split_dim + + @property + def split(self): + return self._node.split + + @property + def children(self): + return self._node.children + + @property + def tree(self): + if not hasattr(self, "_tree"): + self._tree = KDTree.node._create(super().tree) + + return self._tree + + def __init__(self, data, leafsize=10, compact_nodes=True, copy_data=False, + balanced_tree=True, boxsize=None): + data = np.asarray(data) + if data.dtype.kind == 'c': + raise TypeError("KDTree does not work with complex data") + + # Note KDTree has different default leafsize from cKDTree + super().__init__(data, leafsize, compact_nodes, copy_data, + balanced_tree, boxsize) + + def query( + self, x, k=1, eps=0, p=2, distance_upper_bound=np.inf, workers=1): + r"""Query the kd-tree for nearest neighbors. + + Parameters + ---------- + x : array_like, last dimension self.m + An array of points to query. + k : int or Sequence[int], optional + Either the number of nearest neighbors to return, or a list of the + k-th nearest neighbors to return, starting from 1. + eps : nonnegative float, optional + Return approximate nearest neighbors; the kth returned value + is guaranteed to be no further than (1+eps) times the + distance to the real kth nearest neighbor. + p : float, 1<=p<=infinity, optional + Which Minkowski p-norm to use. + 1 is the sum-of-absolute-values distance ("Manhattan" distance). + 2 is the usual Euclidean distance. + infinity is the maximum-coordinate-difference distance. + A large, finite p may cause a ValueError if overflow can occur. + distance_upper_bound : nonnegative float, optional + Return only neighbors within this distance. This is used to prune + tree searches, so if you are doing a series of nearest-neighbor + queries, it may help to supply the distance to the nearest neighbor + of the most recent point. + workers : int, optional + Number of workers to use for parallel processing. If -1 is given + all CPU threads are used. Default: 1. + + .. versionadded:: 1.6.0 + + Returns + ------- + d : float or array of floats + The distances to the nearest neighbors. + If ``x`` has shape ``tuple+(self.m,)``, then ``d`` has shape + ``tuple+(k,)``. + When k == 1, the last dimension of the output is squeezed. + Missing neighbors are indicated with infinite distances. + Hits are sorted by distance (nearest first). + + .. versionchanged:: 1.9.0 + Previously if ``k=None``, then `d` was an object array of + shape ``tuple``, containing lists of distances. This behavior + has been removed, use `query_ball_point` instead. + + i : integer or array of integers + The index of each neighbor in ``self.data``. + ``i`` is the same shape as d. + Missing neighbors are indicated with ``self.n``. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.spatial import KDTree + >>> x, y = np.mgrid[0:5, 2:8] + >>> tree = KDTree(np.c_[x.ravel(), y.ravel()]) + + To query the nearest neighbours and return squeezed result, use + + >>> dd, ii = tree.query([[0, 0], [2.2, 2.9]], k=1) + >>> print(dd, ii, sep='\n') + [2. 0.2236068] + [ 0 13] + + To query the nearest neighbours and return unsqueezed result, use + + >>> dd, ii = tree.query([[0, 0], [2.2, 2.9]], k=[1]) + >>> print(dd, ii, sep='\n') + [[2. ] + [0.2236068]] + [[ 0] + [13]] + + To query the second nearest neighbours and return unsqueezed result, + use + + >>> dd, ii = tree.query([[0, 0], [2.2, 2.9]], k=[2]) + >>> print(dd, ii, sep='\n') + [[2.23606798] + [0.80622577]] + [[ 6] + [19]] + + To query the first and second nearest neighbours, use + + >>> dd, ii = tree.query([[0, 0], [2.2, 2.9]], k=2) + >>> print(dd, ii, sep='\n') + [[2. 2.23606798] + [0.2236068 0.80622577]] + [[ 0 6] + [13 19]] + + or, be more specific + + >>> dd, ii = tree.query([[0, 0], [2.2, 2.9]], k=[1, 2]) + >>> print(dd, ii, sep='\n') + [[2. 2.23606798] + [0.2236068 0.80622577]] + [[ 0 6] + [13 19]] + + """ + x = np.asarray(x) + if x.dtype.kind == 'c': + raise TypeError("KDTree does not work with complex data") + + if k is None: + raise ValueError("k must be an integer or a sequence of integers") + + d, i = super().query(x, k, eps, p, distance_upper_bound, workers) + if isinstance(i, int): + i = np.intp(i) + return d, i + + def query_ball_point(self, x, r, p=2., eps=0, workers=1, + return_sorted=None, return_length=False): + """Find all points within distance r of point(s) x. + + Parameters + ---------- + x : array_like, shape tuple + (self.m,) + The point or points to search for neighbors of. + r : array_like, float + The radius of points to return, must broadcast to the length of x. + p : float, optional + Which Minkowski p-norm to use. Should be in the range [1, inf]. + A finite large p may cause a ValueError if overflow can occur. + eps : nonnegative float, optional + Approximate search. Branches of the tree are not explored if their + nearest points are further than ``r / (1 + eps)``, and branches are + added in bulk if their furthest points are nearer than + ``r * (1 + eps)``. + workers : int, optional + Number of jobs to schedule for parallel processing. If -1 is given + all processors are used. Default: 1. + + .. versionadded:: 1.6.0 + return_sorted : bool, optional + Sorts returned indices if True and does not sort them if False. If + None, does not sort single point queries, but does sort + multi-point queries which was the behavior before this option + was added. + + .. versionadded:: 1.6.0 + return_length : bool, optional + Return the number of points inside the radius instead of a list + of the indices. + + .. versionadded:: 1.6.0 + + Returns + ------- + results : list or array of lists + If `x` is a single point, returns a list of the indices of the + neighbors of `x`. If `x` is an array of points, returns an object + array of shape tuple containing lists of neighbors. + + Notes + ----- + If you have many points whose neighbors you want to find, you may save + substantial amounts of time by putting them in a KDTree and using + query_ball_tree. + + Examples + -------- + >>> import numpy as np + >>> from scipy import spatial + >>> x, y = np.mgrid[0:5, 0:5] + >>> points = np.c_[x.ravel(), y.ravel()] + >>> tree = spatial.KDTree(points) + >>> sorted(tree.query_ball_point([2, 0], 1)) + [5, 10, 11, 15] + + Query multiple points and plot the results: + + >>> import matplotlib.pyplot as plt + >>> points = np.asarray(points) + >>> plt.plot(points[:,0], points[:,1], '.') + >>> for results in tree.query_ball_point(([2, 0], [3, 3]), 1): + ... nearby_points = points[results] + ... plt.plot(nearby_points[:,0], nearby_points[:,1], 'o') + >>> plt.margins(0.1, 0.1) + >>> plt.show() + + """ + x = np.asarray(x) + if x.dtype.kind == 'c': + raise TypeError("KDTree does not work with complex data") + return super().query_ball_point( + x, r, p, eps, workers, return_sorted, return_length) + + def query_ball_tree(self, other, r, p=2., eps=0): + """ + Find all pairs of points between `self` and `other` whose distance is + at most r. + + Parameters + ---------- + other : KDTree instance + The tree containing points to search against. + r : float + The maximum distance, has to be positive. + p : float, optional + Which Minkowski norm to use. `p` has to meet the condition + ``1 <= p <= infinity``. + eps : float, optional + Approximate search. Branches of the tree are not explored + if their nearest points are further than ``r/(1+eps)``, and + branches are added in bulk if their furthest points are nearer + than ``r * (1+eps)``. `eps` has to be non-negative. + + Returns + ------- + results : list of lists + For each element ``self.data[i]`` of this tree, ``results[i]`` is a + list of the indices of its neighbors in ``other.data``. + + Examples + -------- + You can search all pairs of points between two kd-trees within a distance: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> from scipy.spatial import KDTree + >>> rng = np.random.default_rng() + >>> points1 = rng.random((15, 2)) + >>> points2 = rng.random((15, 2)) + >>> plt.figure(figsize=(6, 6)) + >>> plt.plot(points1[:, 0], points1[:, 1], "xk", markersize=14) + >>> plt.plot(points2[:, 0], points2[:, 1], "og", markersize=14) + >>> kd_tree1 = KDTree(points1) + >>> kd_tree2 = KDTree(points2) + >>> indexes = kd_tree1.query_ball_tree(kd_tree2, r=0.2) + >>> for i in range(len(indexes)): + ... for j in indexes[i]: + ... plt.plot([points1[i, 0], points2[j, 0]], + ... [points1[i, 1], points2[j, 1]], "-r") + >>> plt.show() + + """ + return super().query_ball_tree(other, r, p, eps) + + def query_pairs(self, r, p=2., eps=0, output_type='set'): + """Find all pairs of points in `self` whose distance is at most r. + + Parameters + ---------- + r : positive float + The maximum distance. + p : float, optional + Which Minkowski norm to use. `p` has to meet the condition + ``1 <= p <= infinity``. + eps : float, optional + Approximate search. Branches of the tree are not explored + if their nearest points are further than ``r/(1+eps)``, and + branches are added in bulk if their furthest points are nearer + than ``r * (1+eps)``. `eps` has to be non-negative. + output_type : string, optional + Choose the output container, 'set' or 'ndarray'. Default: 'set' + + .. versionadded:: 1.6.0 + + Returns + ------- + results : set or ndarray + Set of pairs ``(i,j)``, with ``i < j``, for which the corresponding + positions are close. If output_type is 'ndarray', an ndarry is + returned instead of a set. + + Examples + -------- + You can search all pairs of points in a kd-tree within a distance: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> from scipy.spatial import KDTree + >>> rng = np.random.default_rng() + >>> points = rng.random((20, 2)) + >>> plt.figure(figsize=(6, 6)) + >>> plt.plot(points[:, 0], points[:, 1], "xk", markersize=14) + >>> kd_tree = KDTree(points) + >>> pairs = kd_tree.query_pairs(r=0.2) + >>> for (i, j) in pairs: + ... plt.plot([points[i, 0], points[j, 0]], + ... [points[i, 1], points[j, 1]], "-r") + >>> plt.show() + + """ + return super().query_pairs(r, p, eps, output_type) + + def count_neighbors(self, other, r, p=2., weights=None, cumulative=True): + """Count how many nearby pairs can be formed. + + Count the number of pairs ``(x1,x2)`` can be formed, with ``x1`` drawn + from ``self`` and ``x2`` drawn from ``other``, and where + ``distance(x1, x2, p) <= r``. + + Data points on ``self`` and ``other`` are optionally weighted by the + ``weights`` argument. (See below) + + This is adapted from the "two-point correlation" algorithm described by + Gray and Moore [1]_. See notes for further discussion. + + Parameters + ---------- + other : KDTree + The other tree to draw points from, can be the same tree as self. + r : float or one-dimensional array of floats + The radius to produce a count for. Multiple radii are searched with + a single tree traversal. + If the count is non-cumulative(``cumulative=False``), ``r`` defines + the edges of the bins, and must be non-decreasing. + p : float, optional + 1<=p<=infinity. + Which Minkowski p-norm to use. + Default 2.0. + A finite large p may cause a ValueError if overflow can occur. + weights : tuple, array_like, or None, optional + If None, the pair-counting is unweighted. + If given as a tuple, weights[0] is the weights of points in + ``self``, and weights[1] is the weights of points in ``other``; + either can be None to indicate the points are unweighted. + If given as an array_like, weights is the weights of points in + ``self`` and ``other``. For this to make sense, ``self`` and + ``other`` must be the same tree. If ``self`` and ``other`` are two + different trees, a ``ValueError`` is raised. + Default: None + + .. versionadded:: 1.6.0 + cumulative : bool, optional + Whether the returned counts are cumulative. When cumulative is set + to ``False`` the algorithm is optimized to work with a large number + of bins (>10) specified by ``r``. When ``cumulative`` is set to + True, the algorithm is optimized to work with a small number of + ``r``. Default: True + + .. versionadded:: 1.6.0 + + Returns + ------- + result : scalar or 1-D array + The number of pairs. For unweighted counts, the result is integer. + For weighted counts, the result is float. + If cumulative is False, ``result[i]`` contains the counts with + ``(-inf if i == 0 else r[i-1]) < R <= r[i]`` + + Notes + ----- + Pair-counting is the basic operation used to calculate the two point + correlation functions from a data set composed of position of objects. + + Two point correlation function measures the clustering of objects and + is widely used in cosmology to quantify the large scale structure + in our Universe, but it may be useful for data analysis in other fields + where self-similar assembly of objects also occur. + + The Landy-Szalay estimator for the two point correlation function of + ``D`` measures the clustering signal in ``D``. [2]_ + + For example, given the position of two sets of objects, + + - objects ``D`` (data) contains the clustering signal, and + + - objects ``R`` (random) that contains no signal, + + .. math:: + + \\xi(r) = \\frac{ - 2 f + f^2}{f^2}, + + where the brackets represents counting pairs between two data sets + in a finite bin around ``r`` (distance), corresponding to setting + `cumulative=False`, and ``f = float(len(D)) / float(len(R))`` is the + ratio between number of objects from data and random. + + The algorithm implemented here is loosely based on the dual-tree + algorithm described in [1]_. We switch between two different + pair-cumulation scheme depending on the setting of ``cumulative``. + The computing time of the method we use when for + ``cumulative == False`` does not scale with the total number of bins. + The algorithm for ``cumulative == True`` scales linearly with the + number of bins, though it is slightly faster when only + 1 or 2 bins are used. [5]_. + + As an extension to the naive pair-counting, + weighted pair-counting counts the product of weights instead + of number of pairs. + Weighted pair-counting is used to estimate marked correlation functions + ([3]_, section 2.2), + or to properly calculate the average of data per distance bin + (e.g. [4]_, section 2.1 on redshift). + + .. [1] Gray and Moore, + "N-body problems in statistical learning", + Mining the sky, 2000, + https://arxiv.org/abs/astro-ph/0012333 + + .. [2] Landy and Szalay, + "Bias and variance of angular correlation functions", + The Astrophysical Journal, 1993, + http://adsabs.harvard.edu/abs/1993ApJ...412...64L + + .. [3] Sheth, Connolly and Skibba, + "Marked correlations in galaxy formation models", + Arxiv e-print, 2005, + https://arxiv.org/abs/astro-ph/0511773 + + .. [4] Hawkins, et al., + "The 2dF Galaxy Redshift Survey: correlation functions, + peculiar velocities and the matter density of the Universe", + Monthly Notices of the Royal Astronomical Society, 2002, + http://adsabs.harvard.edu/abs/2003MNRAS.346...78H + + .. [5] https://github.com/scipy/scipy/pull/5647#issuecomment-168474926 + + Examples + -------- + You can count neighbors number between two kd-trees within a distance: + + >>> import numpy as np + >>> from scipy.spatial import KDTree + >>> rng = np.random.default_rng() + >>> points1 = rng.random((5, 2)) + >>> points2 = rng.random((5, 2)) + >>> kd_tree1 = KDTree(points1) + >>> kd_tree2 = KDTree(points2) + >>> kd_tree1.count_neighbors(kd_tree2, 0.2) + 1 + + This number is same as the total pair number calculated by + `query_ball_tree`: + + >>> indexes = kd_tree1.query_ball_tree(kd_tree2, r=0.2) + >>> sum([len(i) for i in indexes]) + 1 + + """ + return super().count_neighbors(other, r, p, weights, cumulative) + + def sparse_distance_matrix( + self, other, max_distance, p=2., output_type='dok_matrix'): + """Compute a sparse distance matrix. + + Computes a distance matrix between two KDTrees, leaving as zero + any distance greater than max_distance. + + Parameters + ---------- + other : KDTree + + max_distance : positive float + + p : float, 1<=p<=infinity + Which Minkowski p-norm to use. + A finite large p may cause a ValueError if overflow can occur. + + output_type : string, optional + Which container to use for output data. Options: 'dok_matrix', + 'coo_matrix', 'dict', or 'ndarray'. Default: 'dok_matrix'. + + .. versionadded:: 1.6.0 + + Returns + ------- + result : dok_matrix, coo_matrix, dict or ndarray + Sparse matrix representing the results in "dictionary of keys" + format. If a dict is returned the keys are (i,j) tuples of indices. + If output_type is 'ndarray' a record array with fields 'i', 'j', + and 'v' is returned, + + Examples + -------- + You can compute a sparse distance matrix between two kd-trees: + + >>> import numpy as np + >>> from scipy.spatial import KDTree + >>> rng = np.random.default_rng() + >>> points1 = rng.random((5, 2)) + >>> points2 = rng.random((5, 2)) + >>> kd_tree1 = KDTree(points1) + >>> kd_tree2 = KDTree(points2) + >>> sdm = kd_tree1.sparse_distance_matrix(kd_tree2, 0.3) + >>> sdm.toarray() + array([[0. , 0. , 0.12295571, 0. , 0. ], + [0. , 0. , 0. , 0. , 0. ], + [0.28942611, 0. , 0. , 0.2333084 , 0. ], + [0. , 0. , 0. , 0. , 0. ], + [0.24617575, 0.29571802, 0.26836782, 0. , 0. ]]) + + You can check distances above the `max_distance` are zeros: + + >>> from scipy.spatial import distance_matrix + >>> distance_matrix(points1, points2) + array([[0.56906522, 0.39923701, 0.12295571, 0.8658745 , 0.79428925], + [0.37327919, 0.7225693 , 0.87665969, 0.32580855, 0.75679479], + [0.28942611, 0.30088013, 0.6395831 , 0.2333084 , 0.33630734], + [0.31994999, 0.72658602, 0.71124834, 0.55396483, 0.90785663], + [0.24617575, 0.29571802, 0.26836782, 0.57714465, 0.6473269 ]]) + + """ + return super().sparse_distance_matrix( + other, max_distance, p, output_type) + + +def distance_matrix(x, y, p=2, threshold=1000000): + """Compute the distance matrix. + + Returns the matrix of all pair-wise distances. + + Parameters + ---------- + x : (M, K) array_like + Matrix of M vectors in K dimensions. + y : (N, K) array_like + Matrix of N vectors in K dimensions. + p : float, 1 <= p <= infinity + Which Minkowski p-norm to use. + threshold : positive int + If ``M * N * K`` > `threshold`, algorithm uses a Python loop instead + of large temporary arrays. + + Returns + ------- + result : (M, N) ndarray + Matrix containing the distance from every vector in `x` to every vector + in `y`. + + Examples + -------- + >>> from scipy.spatial import distance_matrix + >>> distance_matrix([[0,0],[0,1]], [[1,0],[1,1]]) + array([[ 1. , 1.41421356], + [ 1.41421356, 1. ]]) + + """ + + x = np.asarray(x) + m, k = x.shape + y = np.asarray(y) + n, kk = y.shape + + if k != kk: + raise ValueError(f"x contains {k}-dimensional vectors but y contains " + f"{kk}-dimensional vectors") + + if m*n*k <= threshold: + return minkowski_distance(x[:,np.newaxis,:],y[np.newaxis,:,:],p) + else: + result = np.empty((m,n),dtype=float) # FIXME: figure out the best dtype + if m < n: + for i in range(m): + result[i,:] = minkowski_distance(x[i],y,p) + else: + for j in range(n): + result[:,j] = minkowski_distance(x,y[j],p) + return result diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_plotutils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_plotutils.py new file mode 100644 index 0000000000000000000000000000000000000000..9e7e0998a5a75d0cab18effc1ca05ede708c0f0a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_plotutils.py @@ -0,0 +1,270 @@ +import numpy as np +from scipy._lib.decorator import decorator as _decorator + +__all__ = ['delaunay_plot_2d', 'convex_hull_plot_2d', 'voronoi_plot_2d'] + + +@_decorator +def _held_figure(func, obj, ax=None, **kw): + import matplotlib.pyplot as plt + + if ax is None: + fig = plt.figure() + ax = fig.gca() + return func(obj, ax=ax, **kw) + + # As of matplotlib 2.0, the "hold" mechanism is deprecated. + # When matplotlib 1.x is no longer supported, this check can be removed. + was_held = getattr(ax, 'ishold', lambda: True)() + if was_held: + return func(obj, ax=ax, **kw) + try: + ax.hold(True) + return func(obj, ax=ax, **kw) + finally: + ax.hold(was_held) + + +def _adjust_bounds(ax, points): + margin = 0.1 * np.ptp(points, axis=0) + xy_min = points.min(axis=0) - margin + xy_max = points.max(axis=0) + margin + ax.set_xlim(xy_min[0], xy_max[0]) + ax.set_ylim(xy_min[1], xy_max[1]) + + +@_held_figure +def delaunay_plot_2d(tri, ax=None): + """ + Plot the given Delaunay triangulation in 2-D + + Parameters + ---------- + tri : scipy.spatial.Delaunay instance + Triangulation to plot + ax : matplotlib.axes.Axes instance, optional + Axes to plot on + + Returns + ------- + fig : matplotlib.figure.Figure instance + Figure for the plot + + See Also + -------- + Delaunay + matplotlib.pyplot.triplot + + Notes + ----- + Requires Matplotlib. + + Examples + -------- + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.spatial import Delaunay, delaunay_plot_2d + + The Delaunay triangulation of a set of random points: + + >>> rng = np.random.default_rng() + >>> points = rng.random((30, 2)) + >>> tri = Delaunay(points) + + Plot it: + + >>> _ = delaunay_plot_2d(tri) + >>> plt.show() + + """ + if tri.points.shape[1] != 2: + raise ValueError("Delaunay triangulation is not 2-D") + + x, y = tri.points.T + ax.plot(x, y, 'o') + ax.triplot(x, y, tri.simplices.copy()) + + _adjust_bounds(ax, tri.points) + + return ax.figure + + +@_held_figure +def convex_hull_plot_2d(hull, ax=None): + """ + Plot the given convex hull diagram in 2-D + + Parameters + ---------- + hull : scipy.spatial.ConvexHull instance + Convex hull to plot + ax : matplotlib.axes.Axes instance, optional + Axes to plot on + + Returns + ------- + fig : matplotlib.figure.Figure instance + Figure for the plot + + See Also + -------- + ConvexHull + + Notes + ----- + Requires Matplotlib. + + + Examples + -------- + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.spatial import ConvexHull, convex_hull_plot_2d + + The convex hull of a random set of points: + + >>> rng = np.random.default_rng() + >>> points = rng.random((30, 2)) + >>> hull = ConvexHull(points) + + Plot it: + + >>> _ = convex_hull_plot_2d(hull) + >>> plt.show() + + """ + from matplotlib.collections import LineCollection + + if hull.points.shape[1] != 2: + raise ValueError("Convex hull is not 2-D") + + ax.plot(hull.points[:, 0], hull.points[:, 1], 'o') + line_segments = [hull.points[simplex] for simplex in hull.simplices] + ax.add_collection(LineCollection(line_segments, + colors='k', + linestyle='solid')) + _adjust_bounds(ax, hull.points) + + return ax.figure + + +@_held_figure +def voronoi_plot_2d(vor, ax=None, **kw): + """ + Plot the given Voronoi diagram in 2-D + + Parameters + ---------- + vor : scipy.spatial.Voronoi instance + Diagram to plot + ax : matplotlib.axes.Axes instance, optional + Axes to plot on + show_points : bool, optional + Add the Voronoi points to the plot. + show_vertices : bool, optional + Add the Voronoi vertices to the plot. + line_colors : string, optional + Specifies the line color for polygon boundaries + line_width : float, optional + Specifies the line width for polygon boundaries + line_alpha : float, optional + Specifies the line alpha for polygon boundaries + point_size : float, optional + Specifies the size of points + + Returns + ------- + fig : matplotlib.figure.Figure instance + Figure for the plot + + See Also + -------- + Voronoi + + Notes + ----- + Requires Matplotlib. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.spatial import Voronoi, voronoi_plot_2d + + Create a set of points for the example: + + >>> rng = np.random.default_rng() + >>> points = rng.random((10,2)) + + Generate the Voronoi diagram for the points: + + >>> vor = Voronoi(points) + + Use `voronoi_plot_2d` to plot the diagram: + + >>> fig = voronoi_plot_2d(vor) + + Use `voronoi_plot_2d` to plot the diagram again, with some settings + customized: + + >>> fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange', + ... line_width=2, line_alpha=0.6, point_size=2) + >>> plt.show() + + """ + from matplotlib.collections import LineCollection + + if vor.points.shape[1] != 2: + raise ValueError("Voronoi diagram is not 2-D") + + if kw.get('show_points', True): + point_size = kw.get('point_size', None) + ax.plot(vor.points[:, 0], vor.points[:, 1], '.', markersize=point_size) + if kw.get('show_vertices', True): + ax.plot(vor.vertices[:, 0], vor.vertices[:, 1], 'o') + + line_colors = kw.get('line_colors', 'k') + line_width = kw.get('line_width', 1.0) + line_alpha = kw.get('line_alpha', 1.0) + + center = vor.points.mean(axis=0) + ptp_bound = np.ptp(vor.points, axis=0) + + finite_segments = [] + infinite_segments = [] + for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices): + simplex = np.asarray(simplex) + if np.all(simplex >= 0): + finite_segments.append(vor.vertices[simplex]) + else: + i = simplex[simplex >= 0][0] # finite end Voronoi vertex + + t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent + t /= np.linalg.norm(t) + n = np.array([-t[1], t[0]]) # normal + + midpoint = vor.points[pointidx].mean(axis=0) + direction = np.sign(np.dot(midpoint - center, n)) * n + if (vor.furthest_site): + direction = -direction + aspect_factor = abs(ptp_bound.max() / ptp_bound.min()) + far_point = vor.vertices[i] + direction * ptp_bound.max() * aspect_factor + + infinite_segments.append([vor.vertices[i], far_point]) + + ax.add_collection(LineCollection(finite_segments, + colors=line_colors, + lw=line_width, + alpha=line_alpha, + linestyle='solid')) + ax.add_collection(LineCollection(infinite_segments, + colors=line_colors, + lw=line_width, + alpha=line_alpha, + linestyle='dashed')) + + _adjust_bounds(ax, vor.points) + + return ax.figure diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_procrustes.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_procrustes.py new file mode 100644 index 0000000000000000000000000000000000000000..ec460056ca06ebdff007423f91577cbd8f22e24c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_procrustes.py @@ -0,0 +1,132 @@ +""" +This module provides functions to perform full Procrustes analysis. + +This code was originally written by Justin Kucynski and ported over from +scikit-bio by Yoshiki Vazquez-Baeza. +""" + +import numpy as np +from scipy.linalg import orthogonal_procrustes + + +__all__ = ['procrustes'] + + +def procrustes(data1, data2): + r"""Procrustes analysis, a similarity test for two data sets. + + Each input matrix is a set of points or vectors (the rows of the matrix). + The dimension of the space is the number of columns of each matrix. Given + two identically sized matrices, procrustes standardizes both such that: + + - :math:`tr(AA^{T}) = 1`. + + - Both sets of points are centered around the origin. + + Procrustes ([1]_, [2]_) then applies the optimal transform to the second + matrix (including scaling/dilation, rotations, and reflections) to minimize + :math:`M^{2}=\sum(data1-data2)^{2}`, or the sum of the squares of the + pointwise differences between the two input datasets. + + This function was not designed to handle datasets with different numbers of + datapoints (rows). If two data sets have different dimensionality + (different number of columns), simply add columns of zeros to the smaller + of the two. + + Parameters + ---------- + data1 : array_like + Matrix, n rows represent points in k (columns) space `data1` is the + reference data, after it is standardised, the data from `data2` will be + transformed to fit the pattern in `data1` (must have >1 unique points). + data2 : array_like + n rows of data in k space to be fit to `data1`. Must be the same + shape ``(numrows, numcols)`` as data1 (must have >1 unique points). + + Returns + ------- + mtx1 : array_like + A standardized version of `data1`. + mtx2 : array_like + The orientation of `data2` that best fits `data1`. Centered, but not + necessarily :math:`tr(AA^{T}) = 1`. + disparity : float + :math:`M^{2}` as defined above. + + Raises + ------ + ValueError + If the input arrays are not two-dimensional. + If the shape of the input arrays is different. + If the input arrays have zero columns or zero rows. + + See Also + -------- + scipy.linalg.orthogonal_procrustes + scipy.spatial.distance.directed_hausdorff : Another similarity test + for two data sets + + Notes + ----- + - The disparity should not depend on the order of the input matrices, but + the output matrices will, as only the first output matrix is guaranteed + to be scaled such that :math:`tr(AA^{T}) = 1`. + + - Duplicate data points are generally ok, duplicating a data point will + increase its effect on the procrustes fit. + + - The disparity scales as the number of points per input matrix. + + References + ---------- + .. [1] Krzanowski, W. J. (2000). "Principles of Multivariate analysis". + .. [2] Gower, J. C. (1975). "Generalized procrustes analysis". + + Examples + -------- + >>> import numpy as np + >>> from scipy.spatial import procrustes + + The matrix ``b`` is a rotated, shifted, scaled and mirrored version of + ``a`` here: + + >>> a = np.array([[1, 3], [1, 2], [1, 1], [2, 1]], 'd') + >>> b = np.array([[4, -2], [4, -4], [4, -6], [2, -6]], 'd') + >>> mtx1, mtx2, disparity = procrustes(a, b) + >>> round(disparity) + 0.0 + + """ + mtx1 = np.array(data1, dtype=np.float64, copy=True) + mtx2 = np.array(data2, dtype=np.float64, copy=True) + + if mtx1.ndim != 2 or mtx2.ndim != 2: + raise ValueError("Input matrices must be two-dimensional") + if mtx1.shape != mtx2.shape: + raise ValueError("Input matrices must be of same shape") + if mtx1.size == 0: + raise ValueError("Input matrices must be >0 rows and >0 cols") + + # translate all the data to the origin + mtx1 -= np.mean(mtx1, 0) + mtx2 -= np.mean(mtx2, 0) + + norm1 = np.linalg.norm(mtx1) + norm2 = np.linalg.norm(mtx2) + + if norm1 == 0 or norm2 == 0: + raise ValueError("Input matrices must contain >1 unique points") + + # change scaling of data (in rows) such that trace(mtx*mtx') = 1 + mtx1 /= norm1 + mtx2 /= norm2 + + # transform mtx2 to minimize disparity + R, s = orthogonal_procrustes(mtx1, mtx2) + mtx2 = np.dot(mtx2, R.T) * s + + # measure the dissimilarity between the two datasets + disparity = np.sum(np.square(mtx1 - mtx2)) + + return mtx1, mtx2, disparity + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_qhull.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_qhull.pyi new file mode 100644 index 0000000000000000000000000000000000000000..416128eab5f53cdc8f45c422bcc140451223bd3f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_qhull.pyi @@ -0,0 +1,213 @@ +''' +Static type checking stub file for scipy/spatial/qhull.pyx +''' + + +import numpy as np +from numpy.typing import ArrayLike, NDArray +from typing_extensions import final + +class QhullError(RuntimeError): + ... + +@final +class _Qhull: + # Read-only cython attribute that behaves, more or less, like a property + @property + def ndim(self) -> int: ... + mode_option: bytes + options: bytes + furthest_site: bool + + def __init__( + self, + mode_option: bytes, + points: NDArray[np.float64], + options: None | bytes = ..., + required_options: None | bytes = ..., + furthest_site: bool = ..., + incremental: bool = ..., + interior_point: None | NDArray[np.float64] = ..., + ) -> None: ... + def check_active(self) -> None: ... + def close(self) -> None: ... + def get_points(self) -> NDArray[np.float64]: ... + def add_points( + self, + points: ArrayLike, + interior_point: ArrayLike = ... + ) -> None: ... + def get_paraboloid_shift_scale(self) -> tuple[float, float]: ... + def volume_area(self) -> tuple[float, float]: ... + def triangulate(self) -> None: ... + def get_simplex_facet_array(self) -> tuple[ + NDArray[np.intc], + NDArray[np.intc], + NDArray[np.float64], + NDArray[np.intc], + NDArray[np.intc], + ]: ... + def get_hull_points(self) -> NDArray[np.float64]: ... + def get_hull_facets(self) -> tuple[ + list[list[int]], + NDArray[np.float64], + ]: ... + def get_voronoi_diagram(self) -> tuple[ + NDArray[np.float64], + NDArray[np.intc], + list[list[int]], + list[list[int]], + NDArray[np.intp], + ]: ... + def get_extremes_2d(self) -> NDArray[np.intc]: ... + +def _get_barycentric_transforms( + points: NDArray[np.float64], + simplices: NDArray[np.intc], + eps: float +) -> NDArray[np.float64]: ... + +class _QhullUser: + ndim: int + npoints: int + min_bound: NDArray[np.float64] + max_bound: NDArray[np.float64] + + def __init__(self, qhull: _Qhull, incremental: bool = ...) -> None: ... + def close(self) -> None: ... + def _update(self, qhull: _Qhull) -> None: ... + def _add_points( + self, + points: ArrayLike, + restart: bool = ..., + interior_point: ArrayLike = ... + ) -> None: ... + +class Delaunay(_QhullUser): + furthest_site: bool + paraboloid_scale: float + paraboloid_shift: float + simplices: NDArray[np.intc] + neighbors: NDArray[np.intc] + equations: NDArray[np.float64] + coplanar: NDArray[np.intc] + good: NDArray[np.intc] + nsimplex: int + vertices: NDArray[np.intc] + + def __init__( + self, + points: ArrayLike, + furthest_site: bool = ..., + incremental: bool = ..., + qhull_options: None | str = ... + ) -> None: ... + def _update(self, qhull: _Qhull) -> None: ... + def add_points( + self, + points: ArrayLike, + restart: bool = ... + ) -> None: ... + @property + def points(self) -> NDArray[np.float64]: ... + @property + def transform(self) -> NDArray[np.float64]: ... + @property + def vertex_to_simplex(self) -> NDArray[np.intc]: ... + @property + def vertex_neighbor_vertices(self) -> tuple[ + NDArray[np.intc], + NDArray[np.intc], + ]: ... + @property + def convex_hull(self) -> NDArray[np.intc]: ... + def find_simplex( + self, + xi: ArrayLike, + bruteforce: bool = ..., + tol: float = ... + ) -> NDArray[np.intc]: ... + def plane_distance(self, xi: ArrayLike) -> NDArray[np.float64]: ... + def lift_points(self, x: ArrayLike) -> NDArray[np.float64]: ... + +def tsearch(tri: Delaunay, xi: ArrayLike) -> NDArray[np.intc]: ... +def _copy_docstr(dst: object, src: object) -> None: ... + +class ConvexHull(_QhullUser): + simplices: NDArray[np.intc] + neighbors: NDArray[np.intc] + equations: NDArray[np.float64] + coplanar: NDArray[np.intc] + good: None | NDArray[np.bool_] + volume: float + area: float + nsimplex: int + + def __init__( + self, + points: ArrayLike, + incremental: bool = ..., + qhull_options: None | str = ... + ) -> None: ... + def _update(self, qhull: _Qhull) -> None: ... + def add_points(self, points: ArrayLike, + restart: bool = ...) -> None: ... + @property + def points(self) -> NDArray[np.float64]: ... + @property + def vertices(self) -> NDArray[np.intc]: ... + +class Voronoi(_QhullUser): + vertices: NDArray[np.float64] + ridge_points: NDArray[np.intc] + ridge_vertices: list[list[int]] + regions: list[list[int]] + point_region: NDArray[np.intp] + furthest_site: bool + + def __init__( + self, + points: ArrayLike, + furthest_site: bool = ..., + incremental: bool = ..., + qhull_options: None | str = ... + ) -> None: ... + def _update(self, qhull: _Qhull) -> None: ... + def add_points( + self, + points: ArrayLike, + restart: bool = ... + ) -> None: ... + @property + def points(self) -> NDArray[np.float64]: ... + @property + def ridge_dict(self) -> dict[tuple[int, int], list[int]]: ... + +class HalfspaceIntersection(_QhullUser): + interior_point: NDArray[np.float64] + dual_facets: list[list[int]] + dual_equations: NDArray[np.float64] + dual_points: NDArray[np.float64] + dual_volume: float + dual_area: float + intersections: NDArray[np.float64] + ndim: int + nineq: int + + def __init__( + self, + halfspaces: ArrayLike, + interior_point: ArrayLike, + incremental: bool = ..., + qhull_options: None | str = ... + ) -> None: ... + def _update(self, qhull: _Qhull) -> None: ... + def add_halfspaces( + self, + halfspaces: ArrayLike, + restart: bool = ... + ) -> None: ... + @property + def halfspaces(self) -> NDArray[np.float64]: ... + @property + def dual_vertices(self) -> NDArray[np.integer]: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_spherical_voronoi.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_spherical_voronoi.py new file mode 100644 index 0000000000000000000000000000000000000000..6b9ba82429235469ada83de917d8f9d65d1a4088 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_spherical_voronoi.py @@ -0,0 +1,341 @@ +""" +Spherical Voronoi Code + +.. versionadded:: 0.18.0 + +""" +# +# Copyright (C) Tyler Reddy, Ross Hemsley, Edd Edmondson, +# Nikolai Nowaczyk, Joe Pitt-Francis, 2015. +# +# Distributed under the same BSD license as SciPy. +# + +import numpy as np +import scipy +from . import _voronoi +from scipy.spatial import cKDTree + +__all__ = ['SphericalVoronoi'] + + +def calculate_solid_angles(R): + """Calculates the solid angles of plane triangles. Implements the method of + Van Oosterom and Strackee [VanOosterom]_ with some modifications. Assumes + that input points have unit norm.""" + # Original method uses a triple product `R1 . (R2 x R3)` for the numerator. + # This is equal to the determinant of the matrix [R1 R2 R3], which can be + # computed with better stability. + numerator = np.linalg.det(R) + denominator = 1 + (np.einsum('ij,ij->i', R[:, 0], R[:, 1]) + + np.einsum('ij,ij->i', R[:, 1], R[:, 2]) + + np.einsum('ij,ij->i', R[:, 2], R[:, 0])) + return np.abs(2 * np.arctan2(numerator, denominator)) + + +class SphericalVoronoi: + """ Voronoi diagrams on the surface of a sphere. + + .. versionadded:: 0.18.0 + + Parameters + ---------- + points : ndarray of floats, shape (npoints, ndim) + Coordinates of points from which to construct a spherical + Voronoi diagram. + radius : float, optional + Radius of the sphere (Default: 1) + center : ndarray of floats, shape (ndim,) + Center of sphere (Default: origin) + threshold : float + Threshold for detecting duplicate points and + mismatches between points and sphere parameters. + (Default: 1e-06) + + Attributes + ---------- + points : double array of shape (npoints, ndim) + the points in `ndim` dimensions to generate the Voronoi diagram from + radius : double + radius of the sphere + center : double array of shape (ndim,) + center of the sphere + vertices : double array of shape (nvertices, ndim) + Voronoi vertices corresponding to points + regions : list of list of integers of shape (npoints, _ ) + the n-th entry is a list consisting of the indices + of the vertices belonging to the n-th point in points + + Methods + ------- + calculate_areas + Calculates the areas of the Voronoi regions. For 2D point sets, the + regions are circular arcs. The sum of the areas is `2 * pi * radius`. + For 3D point sets, the regions are spherical polygons. The sum of the + areas is `4 * pi * radius**2`. + + Raises + ------ + ValueError + If there are duplicates in `points`. + If the provided `radius` is not consistent with `points`. + + Notes + ----- + The spherical Voronoi diagram algorithm proceeds as follows. The Convex + Hull of the input points (generators) is calculated, and is equivalent to + their Delaunay triangulation on the surface of the sphere [Caroli]_. + The Convex Hull neighbour information is then used to + order the Voronoi region vertices around each generator. The latter + approach is substantially less sensitive to floating point issues than + angle-based methods of Voronoi region vertex sorting. + + Empirical assessment of spherical Voronoi algorithm performance suggests + quadratic time complexity (loglinear is optimal, but algorithms are more + challenging to implement). + + References + ---------- + .. [Caroli] Caroli et al. Robust and Efficient Delaunay triangulations of + points on or close to a sphere. Research Report RR-7004, 2009. + + .. [VanOosterom] Van Oosterom and Strackee. The solid angle of a plane + triangle. IEEE Transactions on Biomedical Engineering, + 2, 1983, pp 125--126. + + See Also + -------- + Voronoi : Conventional Voronoi diagrams in N dimensions. + + Examples + -------- + Do some imports and take some points on a cube: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.spatial import SphericalVoronoi, geometric_slerp + >>> from mpl_toolkits.mplot3d import proj3d + >>> # set input data + >>> points = np.array([[0, 0, 1], [0, 0, -1], [1, 0, 0], + ... [0, 1, 0], [0, -1, 0], [-1, 0, 0], ]) + + Calculate the spherical Voronoi diagram: + + >>> radius = 1 + >>> center = np.array([0, 0, 0]) + >>> sv = SphericalVoronoi(points, radius, center) + + Generate plot: + + >>> # sort vertices (optional, helpful for plotting) + >>> sv.sort_vertices_of_regions() + >>> t_vals = np.linspace(0, 1, 2000) + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111, projection='3d') + >>> # plot the unit sphere for reference (optional) + >>> u = np.linspace(0, 2 * np.pi, 100) + >>> v = np.linspace(0, np.pi, 100) + >>> x = np.outer(np.cos(u), np.sin(v)) + >>> y = np.outer(np.sin(u), np.sin(v)) + >>> z = np.outer(np.ones(np.size(u)), np.cos(v)) + >>> ax.plot_surface(x, y, z, color='y', alpha=0.1) + >>> # plot generator points + >>> ax.scatter(points[:, 0], points[:, 1], points[:, 2], c='b') + >>> # plot Voronoi vertices + >>> ax.scatter(sv.vertices[:, 0], sv.vertices[:, 1], sv.vertices[:, 2], + ... c='g') + >>> # indicate Voronoi regions (as Euclidean polygons) + >>> for region in sv.regions: + ... n = len(region) + ... for i in range(n): + ... start = sv.vertices[region][i] + ... end = sv.vertices[region][(i + 1) % n] + ... result = geometric_slerp(start, end, t_vals) + ... ax.plot(result[..., 0], + ... result[..., 1], + ... result[..., 2], + ... c='k') + >>> ax.azim = 10 + >>> ax.elev = 40 + >>> _ = ax.set_xticks([]) + >>> _ = ax.set_yticks([]) + >>> _ = ax.set_zticks([]) + >>> fig.set_size_inches(4, 4) + >>> plt.show() + + """ + def __init__(self, points, radius=1, center=None, threshold=1e-06): + + if radius is None: + raise ValueError('`radius` is `None`. ' + 'Please provide a floating point number ' + '(i.e. `radius=1`).') + + self.radius = float(radius) + self.points = np.array(points).astype(np.float64) + self._dim = self.points.shape[1] + if center is None: + self.center = np.zeros(self._dim) + else: + self.center = np.array(center, dtype=float) + + # test degenerate input + self._rank = np.linalg.matrix_rank(self.points - self.points[0], + tol=threshold * self.radius) + if self._rank < self._dim: + raise ValueError(f"Rank of input points must be at least {self._dim}") + + if cKDTree(self.points).query_pairs(threshold * self.radius): + raise ValueError("Duplicate generators present.") + + radii = np.linalg.norm(self.points - self.center, axis=1) + max_discrepancy = np.abs(radii - self.radius).max() + if max_discrepancy >= threshold * self.radius: + raise ValueError("Radius inconsistent with generators.") + + self._calc_vertices_regions() + + def _calc_vertices_regions(self): + """ + Calculates the Voronoi vertices and regions of the generators stored + in self.points. The vertices will be stored in self.vertices and the + regions in self.regions. + + This algorithm was discussed at PyData London 2015 by + Tyler Reddy, Ross Hemsley and Nikolai Nowaczyk + """ + # get Convex Hull + conv = scipy.spatial.ConvexHull(self.points) + # get circumcenters of Convex Hull triangles from facet equations + # for 3D input circumcenters will have shape: (2N-4, 3) + self.vertices = self.radius * conv.equations[:, :-1] + self.center + self._simplices = conv.simplices + # calculate regions from triangulation + # for 3D input simplex_indices will have shape: (2N-4,) + simplex_indices = np.arange(len(self._simplices)) + # for 3D input tri_indices will have shape: (6N-12,) + tri_indices = np.column_stack([simplex_indices] * self._dim).ravel() + # for 3D input point_indices will have shape: (6N-12,) + point_indices = self._simplices.ravel() + # for 3D input indices will have shape: (6N-12,) + indices = np.argsort(point_indices, kind='mergesort') + # for 3D input flattened_groups will have shape: (6N-12,) + flattened_groups = tri_indices[indices].astype(np.intp) + # intervals will have shape: (N+1,) + intervals = np.cumsum(np.bincount(point_indices + 1)) + # split flattened groups to get nested list of unsorted regions + groups = [list(flattened_groups[intervals[i]:intervals[i + 1]]) + for i in range(len(intervals) - 1)] + self.regions = groups + + def sort_vertices_of_regions(self): + """Sort indices of the vertices to be (counter-)clockwise ordered. + + Raises + ------ + TypeError + If the points are not three-dimensional. + + Notes + ----- + For each region in regions, it sorts the indices of the Voronoi + vertices such that the resulting points are in a clockwise or + counterclockwise order around the generator point. + + This is done as follows: Recall that the n-th region in regions + surrounds the n-th generator in points and that the k-th + Voronoi vertex in vertices is the circumcenter of the k-th triangle + in self._simplices. For each region n, we choose the first triangle + (=Voronoi vertex) in self._simplices and a vertex of that triangle + not equal to the center n. These determine a unique neighbor of that + triangle, which is then chosen as the second triangle. The second + triangle will have a unique vertex not equal to the current vertex or + the center. This determines a unique neighbor of the second triangle, + which is then chosen as the third triangle and so forth. We proceed + through all the triangles (=Voronoi vertices) belonging to the + generator in points and obtain a sorted version of the vertices + of its surrounding region. + """ + if self._dim != 3: + raise TypeError("Only supported for three-dimensional point sets") + _voronoi.sort_vertices_of_regions(self._simplices, self.regions) + + def _calculate_areas_3d(self): + self.sort_vertices_of_regions() + sizes = [len(region) for region in self.regions] + csizes = np.cumsum(sizes) + num_regions = csizes[-1] + + # We create a set of triangles consisting of one point and two Voronoi + # vertices. The vertices of each triangle are adjacent in the sorted + # regions list. + point_indices = [i for i, size in enumerate(sizes) + for j in range(size)] + + nbrs1 = np.array([r for region in self.regions for r in region]) + + # The calculation of nbrs2 is a vectorized version of: + # np.array([r for region in self.regions for r in np.roll(region, 1)]) + nbrs2 = np.roll(nbrs1, 1) + indices = np.roll(csizes, 1) + indices[0] = 0 + nbrs2[indices] = nbrs1[csizes - 1] + + # Normalize points and vertices. + pnormalized = (self.points - self.center) / self.radius + vnormalized = (self.vertices - self.center) / self.radius + + # Create the complete set of triangles and calculate their solid angles + triangles = np.hstack([pnormalized[point_indices], + vnormalized[nbrs1], + vnormalized[nbrs2] + ]).reshape((num_regions, 3, 3)) + triangle_solid_angles = calculate_solid_angles(triangles) + + # Sum the solid angles of the triangles in each region + solid_angles = np.cumsum(triangle_solid_angles)[csizes - 1] + solid_angles[1:] -= solid_angles[:-1] + + # Get polygon areas using A = omega * r**2 + return solid_angles * self.radius**2 + + def _calculate_areas_2d(self): + # Find start and end points of arcs + arcs = self.points[self._simplices] - self.center + + # Calculate the angle subtended by arcs + d = np.sum((arcs[:, 1] - arcs[:, 0]) ** 2, axis=1) + theta = np.arccos(1 - (d / (2 * (self.radius ** 2)))) + + # Get areas using A = r * theta + areas = self.radius * theta + + # Correct arcs which go the wrong way (single-hemisphere inputs) + signs = np.sign(np.einsum('ij,ij->i', arcs[:, 0], + self.vertices - self.center)) + indices = np.where(signs < 0) + areas[indices] = 2 * np.pi * self.radius - areas[indices] + return areas + + def calculate_areas(self): + """Calculates the areas of the Voronoi regions. + + For 2D point sets, the regions are circular arcs. The sum of the areas + is `2 * pi * radius`. + + For 3D point sets, the regions are spherical polygons. The sum of the + areas is `4 * pi * radius**2`. + + .. versionadded:: 1.5.0 + + Returns + ------- + areas : double array of shape (npoints,) + The areas of the Voronoi regions. + """ + if self._dim == 2: + return self._calculate_areas_2d() + elif self._dim == 3: + return self._calculate_areas_3d() + else: + raise TypeError("Only supported for 2D and 3D point sets") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_voronoi.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_voronoi.pyi new file mode 100644 index 0000000000000000000000000000000000000000..c7c361ff69414d50a6ebcfe2c837025b60083940 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/_voronoi.pyi @@ -0,0 +1,4 @@ + +import numpy as np + +def sort_vertices_of_regions(simplices: np.ndarray, regions: list[list[int]]) -> None: ... # noqa: E501 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/ckdtree.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/ckdtree.py new file mode 100644 index 0000000000000000000000000000000000000000..b838f29cc0d9af6dfd672a15a0337ca680f9eeaf --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/ckdtree.py @@ -0,0 +1,27 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.spatial` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'cKDTree', + 'cKDTreeNode', + 'coo_entries', + 'operator', + 'ordered_pairs', + 'os', + 'scipy', + 'threading', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="spatial", module="ckdtree", + private_modules=["_ckdtree"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.py new file mode 100644 index 0000000000000000000000000000000000000000..d57a0472953b4d91888bf0def7b6a3f007c4c3b7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.py @@ -0,0 +1,2993 @@ +""" +Distance computations (:mod:`scipy.spatial.distance`) +===================================================== + +.. sectionauthor:: Damian Eads + +Function reference +------------------ + +Distance matrix computation from a collection of raw observation vectors +stored in a rectangular array. + +.. autosummary:: + :toctree: generated/ + + pdist -- pairwise distances between observation vectors. + cdist -- distances between two collections of observation vectors + squareform -- convert distance matrix to a condensed one and vice versa + directed_hausdorff -- directed Hausdorff distance between arrays + +Predicates for checking the validity of distance matrices, both +condensed and redundant. Also contained in this module are functions +for computing the number of observations in a distance matrix. + +.. autosummary:: + :toctree: generated/ + + is_valid_dm -- checks for a valid distance matrix + is_valid_y -- checks for a valid condensed distance matrix + num_obs_dm -- # of observations in a distance matrix + num_obs_y -- # of observations in a condensed distance matrix + +Distance functions between two numeric vectors ``u`` and ``v``. Computing +distances over a large collection of vectors is inefficient for these +functions. Use ``pdist`` for this purpose. + +.. autosummary:: + :toctree: generated/ + + braycurtis -- the Bray-Curtis distance. + canberra -- the Canberra distance. + chebyshev -- the Chebyshev distance. + cityblock -- the Manhattan distance. + correlation -- the Correlation distance. + cosine -- the Cosine distance. + euclidean -- the Euclidean distance. + jensenshannon -- the Jensen-Shannon distance. + mahalanobis -- the Mahalanobis distance. + minkowski -- the Minkowski distance. + seuclidean -- the normalized Euclidean distance. + sqeuclidean -- the squared Euclidean distance. + +Distance functions between two boolean vectors (representing sets) ``u`` and +``v``. As in the case of numerical vectors, ``pdist`` is more efficient for +computing the distances between all pairs. + +.. autosummary:: + :toctree: generated/ + + dice -- the Dice dissimilarity. + hamming -- the Hamming distance. + jaccard -- the Jaccard distance. + kulczynski1 -- the Kulczynski 1 distance. + rogerstanimoto -- the Rogers-Tanimoto dissimilarity. + russellrao -- the Russell-Rao dissimilarity. + sokalmichener -- the Sokal-Michener dissimilarity. + sokalsneath -- the Sokal-Sneath dissimilarity. + yule -- the Yule dissimilarity. + +:func:`hamming` also operates over discrete numerical vectors. +""" + +# Copyright (C) Damian Eads, 2007-2008. New BSD License. + +__all__ = [ + 'braycurtis', + 'canberra', + 'cdist', + 'chebyshev', + 'cityblock', + 'correlation', + 'cosine', + 'dice', + 'directed_hausdorff', + 'euclidean', + 'hamming', + 'is_valid_dm', + 'is_valid_y', + 'jaccard', + 'jensenshannon', + 'kulczynski1', + 'mahalanobis', + 'minkowski', + 'num_obs_dm', + 'num_obs_y', + 'pdist', + 'rogerstanimoto', + 'russellrao', + 'seuclidean', + 'sokalmichener', + 'sokalsneath', + 'sqeuclidean', + 'squareform', + 'yule' +] + + +import math +import warnings +import numpy as np +import dataclasses + +from typing import Optional, Callable + +from functools import partial +from scipy._lib._util import _asarray_validated + +from . import _distance_wrap +from . import _hausdorff +from ..linalg import norm +from ..special import rel_entr + +from . import _distance_pybind + + +def _copy_array_if_base_present(a): + """Copy the array if its base points to a parent array.""" + if a.base is not None: + return a.copy() + return a + + +def _correlation_cdist_wrap(XA, XB, dm, **kwargs): + XA = XA - XA.mean(axis=1, keepdims=True) + XB = XB - XB.mean(axis=1, keepdims=True) + _distance_wrap.cdist_cosine_double_wrap(XA, XB, dm, **kwargs) + + +def _correlation_pdist_wrap(X, dm, **kwargs): + X2 = X - X.mean(axis=1, keepdims=True) + _distance_wrap.pdist_cosine_double_wrap(X2, dm, **kwargs) + + +def _convert_to_type(X, out_type): + return np.ascontiguousarray(X, dtype=out_type) + + +def _nbool_correspond_all(u, v, w=None): + if u.dtype == v.dtype == bool and w is None: + not_u = ~u + not_v = ~v + nff = (not_u & not_v).sum() + nft = (not_u & v).sum() + ntf = (u & not_v).sum() + ntt = (u & v).sum() + else: + dtype = np.result_type(int, u.dtype, v.dtype) + u = u.astype(dtype) + v = v.astype(dtype) + not_u = 1.0 - u + not_v = 1.0 - v + if w is not None: + not_u = w * not_u + u = w * u + nff = (not_u * not_v).sum() + nft = (not_u * v).sum() + ntf = (u * not_v).sum() + ntt = (u * v).sum() + return (nff, nft, ntf, ntt) + + +def _nbool_correspond_ft_tf(u, v, w=None): + if u.dtype == v.dtype == bool and w is None: + not_u = ~u + not_v = ~v + nft = (not_u & v).sum() + ntf = (u & not_v).sum() + else: + dtype = np.result_type(int, u.dtype, v.dtype) + u = u.astype(dtype) + v = v.astype(dtype) + not_u = 1.0 - u + not_v = 1.0 - v + if w is not None: + not_u = w * not_u + u = w * u + nft = (not_u * v).sum() + ntf = (u * not_v).sum() + return (nft, ntf) + + +def _validate_cdist_input(XA, XB, mA, mB, n, metric_info, **kwargs): + # get supported types + types = metric_info.types + # choose best type + typ = types[types.index(XA.dtype)] if XA.dtype in types else types[0] + # validate data + XA = _convert_to_type(XA, out_type=typ) + XB = _convert_to_type(XB, out_type=typ) + + # validate kwargs + _validate_kwargs = metric_info.validator + if _validate_kwargs: + kwargs = _validate_kwargs((XA, XB), mA + mB, n, **kwargs) + return XA, XB, typ, kwargs + + +def _validate_weight_with_size(X, m, n, **kwargs): + w = kwargs.pop('w', None) + if w is None: + return kwargs + + if w.ndim != 1 or w.shape[0] != n: + raise ValueError("Weights must have same size as input vector. " + f"{w.shape[0]} vs. {n}") + + kwargs['w'] = _validate_weights(w) + return kwargs + + +def _validate_hamming_kwargs(X, m, n, **kwargs): + w = kwargs.get('w', np.ones((n,), dtype='double')) + + if w.ndim != 1 or w.shape[0] != n: + raise ValueError( + "Weights must have same size as input vector. %d vs. %d" % (w.shape[0], n) + ) + + kwargs['w'] = _validate_weights(w) + return kwargs + + +def _validate_mahalanobis_kwargs(X, m, n, **kwargs): + VI = kwargs.pop('VI', None) + if VI is None: + if m <= n: + # There are fewer observations than the dimension of + # the observations. + raise ValueError("The number of observations (%d) is too " + "small; the covariance matrix is " + "singular. For observations with %d " + "dimensions, at least %d observations " + "are required." % (m, n, n + 1)) + if isinstance(X, tuple): + X = np.vstack(X) + CV = np.atleast_2d(np.cov(X.astype(np.float64, copy=False).T)) + VI = np.linalg.inv(CV).T.copy() + kwargs["VI"] = _convert_to_double(VI) + return kwargs + + +def _validate_minkowski_kwargs(X, m, n, **kwargs): + kwargs = _validate_weight_with_size(X, m, n, **kwargs) + if 'p' not in kwargs: + kwargs['p'] = 2. + else: + if kwargs['p'] <= 0: + raise ValueError("p must be greater than 0") + + return kwargs + + +def _validate_pdist_input(X, m, n, metric_info, **kwargs): + # get supported types + types = metric_info.types + # choose best type + typ = types[types.index(X.dtype)] if X.dtype in types else types[0] + # validate data + X = _convert_to_type(X, out_type=typ) + + # validate kwargs + _validate_kwargs = metric_info.validator + if _validate_kwargs: + kwargs = _validate_kwargs(X, m, n, **kwargs) + return X, typ, kwargs + + +def _validate_seuclidean_kwargs(X, m, n, **kwargs): + V = kwargs.pop('V', None) + if V is None: + if isinstance(X, tuple): + X = np.vstack(X) + V = np.var(X.astype(np.float64, copy=False), axis=0, ddof=1) + else: + V = np.asarray(V, order='c') + if len(V.shape) != 1: + raise ValueError('Variance vector V must ' + 'be one-dimensional.') + if V.shape[0] != n: + raise ValueError('Variance vector V must be of the same ' + 'dimension as the vectors on which the distances ' + 'are computed.') + kwargs['V'] = _convert_to_double(V) + return kwargs + + +def _validate_vector(u, dtype=None): + # XXX Is order='c' really necessary? + u = np.asarray(u, dtype=dtype, order='c') + if u.ndim == 1: + return u + raise ValueError("Input vector should be 1-D.") + + +def _validate_weights(w, dtype=np.float64): + w = _validate_vector(w, dtype=dtype) + if np.any(w < 0): + raise ValueError("Input weights should be all non-negative") + return w + + +def directed_hausdorff(u, v, seed=0): + """ + Compute the directed Hausdorff distance between two 2-D arrays. + + Distances between pairs are calculated using a Euclidean metric. + + Parameters + ---------- + u : (M,N) array_like + Input array with M points in N dimensions. + v : (O,N) array_like + Input array with O points in N dimensions. + seed : int or None, optional + Local `numpy.random.RandomState` seed. Default is 0, a random + shuffling of u and v that guarantees reproducibility. + + Returns + ------- + d : double + The directed Hausdorff distance between arrays `u` and `v`, + + index_1 : int + index of point contributing to Hausdorff pair in `u` + + index_2 : int + index of point contributing to Hausdorff pair in `v` + + Raises + ------ + ValueError + An exception is thrown if `u` and `v` do not have + the same number of columns. + + See Also + -------- + scipy.spatial.procrustes : Another similarity test for two data sets + + Notes + ----- + Uses the early break technique and the random sampling approach + described by [1]_. Although worst-case performance is ``O(m * o)`` + (as with the brute force algorithm), this is unlikely in practice + as the input data would have to require the algorithm to explore + every single point interaction, and after the algorithm shuffles + the input points at that. The best case performance is O(m), which + is satisfied by selecting an inner loop distance that is less than + cmax and leads to an early break as often as possible. The authors + have formally shown that the average runtime is closer to O(m). + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] A. A. Taha and A. Hanbury, "An efficient algorithm for + calculating the exact Hausdorff distance." IEEE Transactions On + Pattern Analysis And Machine Intelligence, vol. 37 pp. 2153-63, + 2015. + + Examples + -------- + Find the directed Hausdorff distance between two 2-D arrays of + coordinates: + + >>> from scipy.spatial.distance import directed_hausdorff + >>> import numpy as np + >>> u = np.array([(1.0, 0.0), + ... (0.0, 1.0), + ... (-1.0, 0.0), + ... (0.0, -1.0)]) + >>> v = np.array([(2.0, 0.0), + ... (0.0, 2.0), + ... (-2.0, 0.0), + ... (0.0, -4.0)]) + + >>> directed_hausdorff(u, v)[0] + 2.23606797749979 + >>> directed_hausdorff(v, u)[0] + 3.0 + + Find the general (symmetric) Hausdorff distance between two 2-D + arrays of coordinates: + + >>> max(directed_hausdorff(u, v)[0], directed_hausdorff(v, u)[0]) + 3.0 + + Find the indices of the points that generate the Hausdorff distance + (the Hausdorff pair): + + >>> directed_hausdorff(v, u)[1:] + (3, 3) + + """ + u = np.asarray(u, dtype=np.float64, order='c') + v = np.asarray(v, dtype=np.float64, order='c') + if u.shape[1] != v.shape[1]: + raise ValueError('u and v need to have the same ' + 'number of columns') + result = _hausdorff.directed_hausdorff(u, v, seed) + return result + + +def minkowski(u, v, p=2, w=None): + """ + Compute the Minkowski distance between two 1-D arrays. + + The Minkowski distance between 1-D arrays `u` and `v`, + is defined as + + .. math:: + + {\\|u-v\\|}_p = (\\sum{|u_i - v_i|^p})^{1/p}. + + + \\left(\\sum{w_i(|(u_i - v_i)|^p)}\\right)^{1/p}. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + p : scalar + The order of the norm of the difference :math:`{\\|u-v\\|}_p`. Note + that for :math:`0 < p < 1`, the triangle inequality only holds with + an additional multiplicative factor, i.e. it is only a quasi-metric. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + minkowski : double + The Minkowski distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.minkowski([1, 0, 0], [0, 1, 0], 1) + 2.0 + >>> distance.minkowski([1, 0, 0], [0, 1, 0], 2) + 1.4142135623730951 + >>> distance.minkowski([1, 0, 0], [0, 1, 0], 3) + 1.2599210498948732 + >>> distance.minkowski([1, 1, 0], [0, 1, 0], 1) + 1.0 + >>> distance.minkowski([1, 1, 0], [0, 1, 0], 2) + 1.0 + >>> distance.minkowski([1, 1, 0], [0, 1, 0], 3) + 1.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if p <= 0: + raise ValueError("p must be greater than 0") + u_v = u - v + if w is not None: + w = _validate_weights(w) + if p == 1: + root_w = w + elif p == 2: + # better precision and speed + root_w = np.sqrt(w) + elif p == np.inf: + root_w = (w != 0) + else: + root_w = np.power(w, 1/p) + u_v = root_w * u_v + dist = norm(u_v, ord=p) + return dist + + +def euclidean(u, v, w=None): + """ + Computes the Euclidean distance between two 1-D arrays. + + The Euclidean distance between 1-D arrays `u` and `v`, is defined as + + .. math:: + + {\\|u-v\\|}_2 + + \\left(\\sum{(w_i |(u_i - v_i)|^2)}\\right)^{1/2} + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + euclidean : double + The Euclidean distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.euclidean([1, 0, 0], [0, 1, 0]) + 1.4142135623730951 + >>> distance.euclidean([1, 1, 0], [0, 1, 0]) + 1.0 + + """ + return minkowski(u, v, p=2, w=w) + + +def sqeuclidean(u, v, w=None): + """ + Compute the squared Euclidean distance between two 1-D arrays. + + The squared Euclidean distance between `u` and `v` is defined as + + .. math:: + + \\sum_i{w_i |u_i - v_i|^2} + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + sqeuclidean : double + The squared Euclidean distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.sqeuclidean([1, 0, 0], [0, 1, 0]) + 2.0 + >>> distance.sqeuclidean([1, 1, 0], [0, 1, 0]) + 1.0 + + """ + # Preserve float dtypes, but convert everything else to np.float64 + # for stability. + utype, vtype = None, None + if not (hasattr(u, "dtype") and np.issubdtype(u.dtype, np.inexact)): + utype = np.float64 + if not (hasattr(v, "dtype") and np.issubdtype(v.dtype, np.inexact)): + vtype = np.float64 + + u = _validate_vector(u, dtype=utype) + v = _validate_vector(v, dtype=vtype) + u_v = u - v + u_v_w = u_v # only want weights applied once + if w is not None: + w = _validate_weights(w) + u_v_w = w * u_v + return np.dot(u_v, u_v_w) + + +def correlation(u, v, w=None, centered=True): + """ + Compute the correlation distance between two 1-D arrays. + + The correlation distance between `u` and `v`, is + defined as + + .. math:: + + 1 - \\frac{(u - \\bar{u}) \\cdot (v - \\bar{v})} + {{\\|(u - \\bar{u})\\|}_2 {\\|(v - \\bar{v})\\|}_2} + + where :math:`\\bar{u}` is the mean of the elements of `u` + and :math:`x \\cdot y` is the dot product of :math:`x` and :math:`y`. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + centered : bool, optional + If True, `u` and `v` will be centered. Default is True. + + Returns + ------- + correlation : double + The correlation distance between 1-D array `u` and `v`. + + Examples + -------- + Find the correlation between two arrays. + + >>> from scipy.spatial.distance import correlation + >>> correlation([1, 0, 1], [1, 1, 0]) + 1.5 + + Using a weighting array, the correlation can be calculated as: + + >>> correlation([1, 0, 1], [1, 1, 0], w=[0.9, 0.1, 0.1]) + 1.1 + + If centering is not needed, the correlation can be calculated as: + + >>> correlation([1, 0, 1], [1, 1, 0], centered=False) + 0.5 + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + w = w / w.sum() + if centered: + if w is not None: + umu = np.dot(u, w) + vmu = np.dot(v, w) + else: + umu = np.mean(u) + vmu = np.mean(v) + u = u - umu + v = v - vmu + if w is not None: + vw = v * w + uw = u * w + else: + vw, uw = v, u + uv = np.dot(u, vw) + uu = np.dot(u, uw) + vv = np.dot(v, vw) + dist = 1.0 - uv / math.sqrt(uu * vv) + # Clip the result to avoid rounding error + return np.clip(dist, 0.0, 2.0) + + +def cosine(u, v, w=None): + """ + Compute the Cosine distance between 1-D arrays. + + The Cosine distance between `u` and `v`, is defined as + + .. math:: + + 1 - \\frac{u \\cdot v} + {\\|u\\|_2 \\|v\\|_2}. + + where :math:`u \\cdot v` is the dot product of :math:`u` and + :math:`v`. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + cosine : double + The Cosine distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.cosine([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.cosine([100, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.cosine([1, 1, 0], [0, 1, 0]) + 0.29289321881345254 + + """ + # cosine distance is also referred to as 'uncentered correlation', + # or 'reflective correlation' + return correlation(u, v, w=w, centered=False) + + +def hamming(u, v, w=None): + """ + Compute the Hamming distance between two 1-D arrays. + + The Hamming distance between 1-D arrays `u` and `v`, is simply the + proportion of disagreeing components in `u` and `v`. If `u` and `v` are + boolean vectors, the Hamming distance is + + .. math:: + + \\frac{c_{01} + c_{10}}{n} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n`. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + hamming : double + The Hamming distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.hamming([1, 0, 0], [0, 1, 0]) + 0.66666666666666663 + >>> distance.hamming([1, 0, 0], [1, 1, 0]) + 0.33333333333333331 + >>> distance.hamming([1, 0, 0], [2, 0, 0]) + 0.33333333333333331 + >>> distance.hamming([1, 0, 0], [3, 0, 0]) + 0.33333333333333331 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if u.shape != v.shape: + raise ValueError('The 1d arrays must have equal lengths.') + u_ne_v = u != v + if w is not None: + w = _validate_weights(w) + if w.shape != u.shape: + raise ValueError("'w' should have the same length as 'u' and 'v'.") + w = w / w.sum() + return np.dot(u_ne_v, w) + return np.mean(u_ne_v) + + +def jaccard(u, v, w=None): + """ + Compute the Jaccard-Needham dissimilarity between two boolean 1-D arrays. + + The Jaccard-Needham dissimilarity between 1-D boolean arrays `u` and `v`, + is defined as + + .. math:: + + \\frac{c_{TF} + c_{FT}} + {c_{TT} + c_{FT} + c_{TF}} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + jaccard : double + The Jaccard distance between vectors `u` and `v`. + + Notes + ----- + When both `u` and `v` lead to a `0/0` division i.e. there is no overlap + between the items in the vectors the returned distance is 0. See the + Wikipedia page on the Jaccard index [1]_, and this paper [2]_. + + .. versionchanged:: 1.2.0 + Previously, when `u` and `v` lead to a `0/0` division, the function + would return NaN. This was changed to return 0 instead. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Jaccard_index + .. [2] S. Kosub, "A note on the triangle inequality for the Jaccard + distance", 2016, :arxiv:`1612.02696` + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.jaccard([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.jaccard([1, 0, 0], [1, 1, 0]) + 0.5 + >>> distance.jaccard([1, 0, 0], [1, 2, 0]) + 0.5 + >>> distance.jaccard([1, 0, 0], [1, 1, 1]) + 0.66666666666666663 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + + nonzero = np.bitwise_or(u != 0, v != 0) + unequal_nonzero = np.bitwise_and((u != v), nonzero) + if w is not None: + w = _validate_weights(w) + nonzero = w * nonzero + unequal_nonzero = w * unequal_nonzero + a = np.float64(unequal_nonzero.sum()) + b = np.float64(nonzero.sum()) + return (a / b) if b != 0 else 0 + + +def kulczynski1(u, v, *, w=None): + """ + Compute the Kulczynski 1 dissimilarity between two boolean 1-D arrays. + + The Kulczynski 1 dissimilarity between two boolean 1-D arrays `u` and `v` + of length ``n``, is defined as + + .. math:: + + \\frac{c_{11}} + {c_{01} + c_{10}} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k \\in {0, 1, ..., n-1}`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + kulczynski1 : float + The Kulczynski 1 distance between vectors `u` and `v`. + + Notes + ----- + This measure has a minimum value of 0 and no upper limit. + It is un-defined when there are no non-matches. + + .. versionadded:: 1.8.0 + + References + ---------- + .. [1] Kulczynski S. et al. Bulletin + International de l'Academie Polonaise des Sciences + et des Lettres, Classe des Sciences Mathematiques + et Naturelles, Serie B (Sciences Naturelles). 1927; + Supplement II: 57-203. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.kulczynski1([1, 0, 0], [0, 1, 0]) + 0.0 + >>> distance.kulczynski1([True, False, False], [True, True, False]) + 1.0 + >>> distance.kulczynski1([True, False, False], [True]) + 0.5 + >>> distance.kulczynski1([1, 0, 0], [3, 1, 0]) + -3.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + (_, nft, ntf, ntt) = _nbool_correspond_all(u, v, w=w) + + return ntt / (ntf + nft) + + +def seuclidean(u, v, V): + """ + Return the standardized Euclidean distance between two 1-D arrays. + + The standardized Euclidean distance between two n-vectors `u` and `v` is + + .. math:: + + \\sqrt{\\sum\\limits_i \\frac{1}{V_i} \\left(u_i-v_i \\right)^2} + + ``V`` is the variance vector; ``V[I]`` is the variance computed over all the i-th + components of the points. If not passed, it is automatically computed. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + V : (N,) array_like + `V` is an 1-D array of component variances. It is usually computed + among a larger collection vectors. + + Returns + ------- + seuclidean : double + The standardized Euclidean distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.seuclidean([1, 0, 0], [0, 1, 0], [0.1, 0.1, 0.1]) + 4.4721359549995796 + >>> distance.seuclidean([1, 0, 0], [0, 1, 0], [1, 0.1, 0.1]) + 3.3166247903553998 + >>> distance.seuclidean([1, 0, 0], [0, 1, 0], [10, 0.1, 0.1]) + 3.1780497164141406 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + V = _validate_vector(V, dtype=np.float64) + if V.shape[0] != u.shape[0] or u.shape[0] != v.shape[0]: + raise TypeError('V must be a 1-D array of the same dimension ' + 'as u and v.') + return euclidean(u, v, w=1/V) + + +def cityblock(u, v, w=None): + """ + Compute the City Block (Manhattan) distance. + + Computes the Manhattan distance between two 1-D arrays `u` and `v`, + which is defined as + + .. math:: + + \\sum_i {\\left| u_i - v_i \\right|}. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + cityblock : double + The City Block (Manhattan) distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.cityblock([1, 0, 0], [0, 1, 0]) + 2 + >>> distance.cityblock([1, 0, 0], [0, 2, 0]) + 3 + >>> distance.cityblock([1, 0, 0], [1, 1, 0]) + 1 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + l1_diff = abs(u - v) + if w is not None: + w = _validate_weights(w) + l1_diff = w * l1_diff + return l1_diff.sum() + + +def mahalanobis(u, v, VI): + """ + Compute the Mahalanobis distance between two 1-D arrays. + + The Mahalanobis distance between 1-D arrays `u` and `v`, is defined as + + .. math:: + + \\sqrt{ (u-v) V^{-1} (u-v)^T } + + where ``V`` is the covariance matrix. Note that the argument `VI` + is the inverse of ``V``. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + VI : array_like + The inverse of the covariance matrix. + + Returns + ------- + mahalanobis : double + The Mahalanobis distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> iv = [[1, 0.5, 0.5], [0.5, 1, 0.5], [0.5, 0.5, 1]] + >>> distance.mahalanobis([1, 0, 0], [0, 1, 0], iv) + 1.0 + >>> distance.mahalanobis([0, 2, 0], [0, 1, 0], iv) + 1.0 + >>> distance.mahalanobis([2, 0, 0], [0, 1, 0], iv) + 1.7320508075688772 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + VI = np.atleast_2d(VI) + delta = u - v + m = np.dot(np.dot(delta, VI), delta) + return np.sqrt(m) + + +def chebyshev(u, v, w=None): + """ + Compute the Chebyshev distance. + + Computes the Chebyshev distance between two 1-D arrays `u` and `v`, + which is defined as + + .. math:: + + \\max_i {|u_i-v_i|}. + + Parameters + ---------- + u : (N,) array_like + Input vector. + v : (N,) array_like + Input vector. + w : (N,) array_like, optional + Unused, as 'max' is a weightless operation. Here for API consistency. + + Returns + ------- + chebyshev : double + The Chebyshev distance between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.chebyshev([1, 0, 0], [0, 1, 0]) + 1 + >>> distance.chebyshev([1, 1, 0], [0, 1, 0]) + 1 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + has_weight = w > 0 + if has_weight.sum() < w.size: + u = u[has_weight] + v = v[has_weight] + return max(abs(u - v)) + + +def braycurtis(u, v, w=None): + """ + Compute the Bray-Curtis distance between two 1-D arrays. + + Bray-Curtis distance is defined as + + .. math:: + + \\sum{|u_i-v_i|} / \\sum{|u_i+v_i|} + + The Bray-Curtis distance is in the range [0, 1] if all coordinates are + positive, and is undefined if the inputs are of length zero. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + braycurtis : double + The Bray-Curtis distance between 1-D arrays `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.braycurtis([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.braycurtis([1, 1, 0], [0, 1, 0]) + 0.33333333333333331 + + """ + u = _validate_vector(u) + v = _validate_vector(v, dtype=np.float64) + l1_diff = abs(u - v) + l1_sum = abs(u + v) + if w is not None: + w = _validate_weights(w) + l1_diff = w * l1_diff + l1_sum = w * l1_sum + return l1_diff.sum() / l1_sum.sum() + + +def canberra(u, v, w=None): + """ + Compute the Canberra distance between two 1-D arrays. + + The Canberra distance is defined as + + .. math:: + + d(u,v) = \\sum_i \\frac{|u_i-v_i|} + {|u_i|+|v_i|}. + + Parameters + ---------- + u : (N,) array_like + Input array. + v : (N,) array_like + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + canberra : double + The Canberra distance between vectors `u` and `v`. + + Notes + ----- + When `u[i]` and `v[i]` are 0 for given i, then the fraction 0/0 = 0 is + used in the calculation. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.canberra([1, 0, 0], [0, 1, 0]) + 2.0 + >>> distance.canberra([1, 1, 0], [0, 1, 0]) + 1.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v, dtype=np.float64) + if w is not None: + w = _validate_weights(w) + with np.errstate(invalid='ignore'): + abs_uv = abs(u - v) + abs_u = abs(u) + abs_v = abs(v) + d = abs_uv / (abs_u + abs_v) + if w is not None: + d = w * d + d = np.nansum(d) + return d + + +def jensenshannon(p, q, base=None, *, axis=0, keepdims=False): + """ + Compute the Jensen-Shannon distance (metric) between + two probability arrays. This is the square root + of the Jensen-Shannon divergence. + + The Jensen-Shannon distance between two probability + vectors `p` and `q` is defined as, + + .. math:: + + \\sqrt{\\frac{D(p \\parallel m) + D(q \\parallel m)}{2}} + + where :math:`m` is the pointwise mean of :math:`p` and :math:`q` + and :math:`D` is the Kullback-Leibler divergence. + + This routine will normalize `p` and `q` if they don't sum to 1.0. + + Parameters + ---------- + p : (N,) array_like + left probability vector + q : (N,) array_like + right probability vector + base : double, optional + the base of the logarithm used to compute the output + if not given, then the routine uses the default base of + scipy.stats.entropy. + axis : int, optional + Axis along which the Jensen-Shannon distances are computed. The default + is 0. + + .. versionadded:: 1.7.0 + keepdims : bool, optional + If this is set to `True`, the reduced axes are left in the + result as dimensions with size one. With this option, + the result will broadcast correctly against the input array. + Default is False. + + .. versionadded:: 1.7.0 + + Returns + ------- + js : double or ndarray + The Jensen-Shannon distances between `p` and `q` along the `axis`. + + Notes + ----- + + .. versionadded:: 1.2.0 + + Examples + -------- + >>> from scipy.spatial import distance + >>> import numpy as np + >>> distance.jensenshannon([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], 2.0) + 1.0 + >>> distance.jensenshannon([1.0, 0.0], [0.5, 0.5]) + 0.46450140402245893 + >>> distance.jensenshannon([1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) + 0.0 + >>> a = np.array([[1, 2, 3, 4], + ... [5, 6, 7, 8], + ... [9, 10, 11, 12]]) + >>> b = np.array([[13, 14, 15, 16], + ... [17, 18, 19, 20], + ... [21, 22, 23, 24]]) + >>> distance.jensenshannon(a, b, axis=0) + array([0.1954288, 0.1447697, 0.1138377, 0.0927636]) + >>> distance.jensenshannon(a, b, axis=1) + array([0.1402339, 0.0399106, 0.0201815]) + + """ + p = np.asarray(p) + q = np.asarray(q) + p = p / np.sum(p, axis=axis, keepdims=True) + q = q / np.sum(q, axis=axis, keepdims=True) + m = (p + q) / 2.0 + left = rel_entr(p, m) + right = rel_entr(q, m) + left_sum = np.sum(left, axis=axis, keepdims=keepdims) + right_sum = np.sum(right, axis=axis, keepdims=keepdims) + js = left_sum + right_sum + if base is not None: + js /= np.log(base) + return np.sqrt(js / 2.0) + + +def yule(u, v, w=None): + """ + Compute the Yule dissimilarity between two boolean 1-D arrays. + + The Yule dissimilarity is defined as + + .. math:: + + \\frac{R}{c_{TT} * c_{FF} + \\frac{R}{2}} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n` and :math:`R = 2.0 * c_{TF} * c_{FT}`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + yule : double + The Yule dissimilarity between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.yule([1, 0, 0], [0, 1, 0]) + 2.0 + >>> distance.yule([1, 1, 0], [0, 1, 0]) + 0.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + (nff, nft, ntf, ntt) = _nbool_correspond_all(u, v, w=w) + half_R = ntf * nft + if half_R == 0: + return 0.0 + else: + return float(2.0 * half_R / (ntt * nff + half_R)) + + +def dice(u, v, w=None): + """ + Compute the Dice dissimilarity between two boolean 1-D arrays. + + The Dice dissimilarity between `u` and `v`, is + + .. math:: + + \\frac{c_{TF} + c_{FT}} + {2c_{TT} + c_{FT} + c_{TF}} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n`. + + Parameters + ---------- + u : (N,) array_like, bool + Input 1-D array. + v : (N,) array_like, bool + Input 1-D array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + dice : double + The Dice dissimilarity between 1-D arrays `u` and `v`. + + Notes + ----- + This function computes the Dice dissimilarity index. To compute the + Dice similarity index, convert one to the other with similarity = + 1 - dissimilarity. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.dice([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.dice([1, 0, 0], [1, 1, 0]) + 0.3333333333333333 + >>> distance.dice([1, 0, 0], [2, 0, 0]) + -0.3333333333333333 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + if u.dtype == v.dtype == bool and w is None: + ntt = (u & v).sum() + else: + dtype = np.result_type(int, u.dtype, v.dtype) + u = u.astype(dtype) + v = v.astype(dtype) + if w is None: + ntt = (u * v).sum() + else: + ntt = (u * v * w).sum() + (nft, ntf) = _nbool_correspond_ft_tf(u, v, w=w) + return float((ntf + nft) / np.array(2.0 * ntt + ntf + nft)) + + +def rogerstanimoto(u, v, w=None): + """ + Compute the Rogers-Tanimoto dissimilarity between two boolean 1-D arrays. + + The Rogers-Tanimoto dissimilarity between two boolean 1-D arrays + `u` and `v`, is defined as + + .. math:: + \\frac{R} + {c_{TT} + c_{FF} + R} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n` and :math:`R = 2(c_{TF} + c_{FT})`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + rogerstanimoto : double + The Rogers-Tanimoto dissimilarity between vectors + `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.rogerstanimoto([1, 0, 0], [0, 1, 0]) + 0.8 + >>> distance.rogerstanimoto([1, 0, 0], [1, 1, 0]) + 0.5 + >>> distance.rogerstanimoto([1, 0, 0], [2, 0, 0]) + -1.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + (nff, nft, ntf, ntt) = _nbool_correspond_all(u, v, w=w) + return float(2.0 * (ntf + nft)) / float(ntt + nff + (2.0 * (ntf + nft))) + + +def russellrao(u, v, w=None): + """ + Compute the Russell-Rao dissimilarity between two boolean 1-D arrays. + + The Russell-Rao dissimilarity between two boolean 1-D arrays, `u` and + `v`, is defined as + + .. math:: + + \\frac{n - c_{TT}} + {n} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + russellrao : double + The Russell-Rao dissimilarity between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.russellrao([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.russellrao([1, 0, 0], [1, 1, 0]) + 0.6666666666666666 + >>> distance.russellrao([1, 0, 0], [2, 0, 0]) + 0.3333333333333333 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if u.dtype == v.dtype == bool and w is None: + ntt = (u & v).sum() + n = float(len(u)) + elif w is None: + ntt = (u * v).sum() + n = float(len(u)) + else: + w = _validate_weights(w) + ntt = (u * v * w).sum() + n = w.sum() + return float(n - ntt) / n + + +def sokalmichener(u, v, w=None): + """ + Compute the Sokal-Michener dissimilarity between two boolean 1-D arrays. + + The Sokal-Michener dissimilarity between boolean 1-D arrays `u` and `v`, + is defined as + + .. math:: + + \\frac{R} + {S + R} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n`, :math:`R = 2 * (c_{TF} + c_{FT})` and + :math:`S = c_{FF} + c_{TT}`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + sokalmichener : double + The Sokal-Michener dissimilarity between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.sokalmichener([1, 0, 0], [0, 1, 0]) + 0.8 + >>> distance.sokalmichener([1, 0, 0], [1, 1, 0]) + 0.5 + >>> distance.sokalmichener([1, 0, 0], [2, 0, 0]) + -1.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if w is not None: + w = _validate_weights(w) + nff, nft, ntf, ntt = _nbool_correspond_all(u, v, w=w) + return float(2.0 * (ntf + nft)) / float(ntt + nff + 2.0 * (ntf + nft)) + + +def sokalsneath(u, v, w=None): + """ + Compute the Sokal-Sneath dissimilarity between two boolean 1-D arrays. + + The Sokal-Sneath dissimilarity between `u` and `v`, + + .. math:: + + \\frac{R} + {c_{TT} + R} + + where :math:`c_{ij}` is the number of occurrences of + :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for + :math:`k < n` and :math:`R = 2(c_{TF} + c_{FT})`. + + Parameters + ---------- + u : (N,) array_like, bool + Input array. + v : (N,) array_like, bool + Input array. + w : (N,) array_like, optional + The weights for each value in `u` and `v`. Default is None, + which gives each value a weight of 1.0 + + Returns + ------- + sokalsneath : double + The Sokal-Sneath dissimilarity between vectors `u` and `v`. + + Examples + -------- + >>> from scipy.spatial import distance + >>> distance.sokalsneath([1, 0, 0], [0, 1, 0]) + 1.0 + >>> distance.sokalsneath([1, 0, 0], [1, 1, 0]) + 0.66666666666666663 + >>> distance.sokalsneath([1, 0, 0], [2, 1, 0]) + 0.0 + >>> distance.sokalsneath([1, 0, 0], [3, 1, 0]) + -2.0 + + """ + u = _validate_vector(u) + v = _validate_vector(v) + if u.dtype == v.dtype == bool and w is None: + ntt = (u & v).sum() + elif w is None: + ntt = (u * v).sum() + else: + w = _validate_weights(w) + ntt = (u * v * w).sum() + (nft, ntf) = _nbool_correspond_ft_tf(u, v, w=w) + denom = np.array(ntt + 2.0 * (ntf + nft)) + if not denom.any(): + raise ValueError('Sokal-Sneath dissimilarity is not defined for ' + 'vectors that are entirely false.') + return float(2.0 * (ntf + nft)) / denom + + +_convert_to_double = partial(_convert_to_type, out_type=np.float64) +_convert_to_bool = partial(_convert_to_type, out_type=bool) + +# adding python-only wrappers to _distance_wrap module +_distance_wrap.pdist_correlation_double_wrap = _correlation_pdist_wrap +_distance_wrap.cdist_correlation_double_wrap = _correlation_cdist_wrap + + +@dataclasses.dataclass(frozen=True) +class CDistMetricWrapper: + metric_name: str + + def __call__(self, XA, XB, *, out=None, **kwargs): + XA = np.ascontiguousarray(XA) + XB = np.ascontiguousarray(XB) + mA, n = XA.shape + mB, _ = XB.shape + metric_name = self.metric_name + metric_info = _METRICS[metric_name] + XA, XB, typ, kwargs = _validate_cdist_input( + XA, XB, mA, mB, n, metric_info, **kwargs) + + w = kwargs.pop('w', None) + if w is not None: + metric = metric_info.dist_func + return _cdist_callable( + XA, XB, metric=metric, out=out, w=w, **kwargs) + + dm = _prepare_out_argument(out, np.float64, (mA, mB)) + # get cdist wrapper + cdist_fn = getattr(_distance_wrap, f'cdist_{metric_name}_{typ}_wrap') + cdist_fn(XA, XB, dm, **kwargs) + return dm + + +@dataclasses.dataclass(frozen=True) +class PDistMetricWrapper: + metric_name: str + + def __call__(self, X, *, out=None, **kwargs): + X = np.ascontiguousarray(X) + m, n = X.shape + metric_name = self.metric_name + metric_info = _METRICS[metric_name] + X, typ, kwargs = _validate_pdist_input( + X, m, n, metric_info, **kwargs) + out_size = (m * (m - 1)) // 2 + w = kwargs.pop('w', None) + if w is not None: + metric = metric_info.dist_func + return _pdist_callable( + X, metric=metric, out=out, w=w, **kwargs) + + dm = _prepare_out_argument(out, np.float64, (out_size,)) + # get pdist wrapper + pdist_fn = getattr(_distance_wrap, f'pdist_{metric_name}_{typ}_wrap') + pdist_fn(X, dm, **kwargs) + return dm + + +@dataclasses.dataclass(frozen=True) +class MetricInfo: + # Name of python distance function + canonical_name: str + # All aliases, including canonical_name + aka: set[str] + # unvectorized distance function + dist_func: Callable + # Optimized cdist function + cdist_func: Callable + # Optimized pdist function + pdist_func: Callable + # function that checks kwargs and computes default values: + # f(X, m, n, **kwargs) + validator: Optional[Callable] = None + # list of supported types: + # X (pdist) and XA (cdist) are used to choose the type. if there is no + # match the first type is used. Default double + types: list[str] = dataclasses.field(default_factory=lambda: ['double']) + # true if out array must be C-contiguous + requires_contiguous_out: bool = True + + +# Registry of implemented metrics: +_METRIC_INFOS = [ + MetricInfo( + canonical_name='braycurtis', + aka={'braycurtis'}, + dist_func=braycurtis, + cdist_func=_distance_pybind.cdist_braycurtis, + pdist_func=_distance_pybind.pdist_braycurtis, + ), + MetricInfo( + canonical_name='canberra', + aka={'canberra'}, + dist_func=canberra, + cdist_func=_distance_pybind.cdist_canberra, + pdist_func=_distance_pybind.pdist_canberra, + ), + MetricInfo( + canonical_name='chebyshev', + aka={'chebychev', 'chebyshev', 'cheby', 'cheb', 'ch'}, + dist_func=chebyshev, + cdist_func=_distance_pybind.cdist_chebyshev, + pdist_func=_distance_pybind.pdist_chebyshev, + ), + MetricInfo( + canonical_name='cityblock', + aka={'cityblock', 'cblock', 'cb', 'c'}, + dist_func=cityblock, + cdist_func=_distance_pybind.cdist_cityblock, + pdist_func=_distance_pybind.pdist_cityblock, + ), + MetricInfo( + canonical_name='correlation', + aka={'correlation', 'co'}, + dist_func=correlation, + cdist_func=CDistMetricWrapper('correlation'), + pdist_func=PDistMetricWrapper('correlation'), + ), + MetricInfo( + canonical_name='cosine', + aka={'cosine', 'cos'}, + dist_func=cosine, + cdist_func=CDistMetricWrapper('cosine'), + pdist_func=PDistMetricWrapper('cosine'), + ), + MetricInfo( + canonical_name='dice', + aka={'dice'}, + types=['bool'], + dist_func=dice, + cdist_func=_distance_pybind.cdist_dice, + pdist_func=_distance_pybind.pdist_dice, + ), + MetricInfo( + canonical_name='euclidean', + aka={'euclidean', 'euclid', 'eu', 'e'}, + dist_func=euclidean, + cdist_func=_distance_pybind.cdist_euclidean, + pdist_func=_distance_pybind.pdist_euclidean, + ), + MetricInfo( + canonical_name='hamming', + aka={'matching', 'hamming', 'hamm', 'ha', 'h'}, + types=['double', 'bool'], + validator=_validate_hamming_kwargs, + dist_func=hamming, + cdist_func=_distance_pybind.cdist_hamming, + pdist_func=_distance_pybind.pdist_hamming, + ), + MetricInfo( + canonical_name='jaccard', + aka={'jaccard', 'jacc', 'ja', 'j'}, + types=['double', 'bool'], + dist_func=jaccard, + cdist_func=_distance_pybind.cdist_jaccard, + pdist_func=_distance_pybind.pdist_jaccard, + ), + MetricInfo( + canonical_name='jensenshannon', + aka={'jensenshannon', 'js'}, + dist_func=jensenshannon, + cdist_func=CDistMetricWrapper('jensenshannon'), + pdist_func=PDistMetricWrapper('jensenshannon'), + ), + MetricInfo( + canonical_name='kulczynski1', + aka={'kulczynski1'}, + types=['bool'], + dist_func=kulczynski1, + cdist_func=_distance_pybind.cdist_kulczynski1, + pdist_func=_distance_pybind.pdist_kulczynski1, + ), + MetricInfo( + canonical_name='mahalanobis', + aka={'mahalanobis', 'mahal', 'mah'}, + validator=_validate_mahalanobis_kwargs, + dist_func=mahalanobis, + cdist_func=CDistMetricWrapper('mahalanobis'), + pdist_func=PDistMetricWrapper('mahalanobis'), + ), + MetricInfo( + canonical_name='minkowski', + aka={'minkowski', 'mi', 'm', 'pnorm'}, + validator=_validate_minkowski_kwargs, + dist_func=minkowski, + cdist_func=_distance_pybind.cdist_minkowski, + pdist_func=_distance_pybind.pdist_minkowski, + ), + MetricInfo( + canonical_name='rogerstanimoto', + aka={'rogerstanimoto'}, + types=['bool'], + dist_func=rogerstanimoto, + cdist_func=_distance_pybind.cdist_rogerstanimoto, + pdist_func=_distance_pybind.pdist_rogerstanimoto, + ), + MetricInfo( + canonical_name='russellrao', + aka={'russellrao'}, + types=['bool'], + dist_func=russellrao, + cdist_func=_distance_pybind.cdist_russellrao, + pdist_func=_distance_pybind.pdist_russellrao, + ), + MetricInfo( + canonical_name='seuclidean', + aka={'seuclidean', 'se', 's'}, + validator=_validate_seuclidean_kwargs, + dist_func=seuclidean, + cdist_func=CDistMetricWrapper('seuclidean'), + pdist_func=PDistMetricWrapper('seuclidean'), + ), + MetricInfo( + canonical_name='sokalmichener', + aka={'sokalmichener'}, + types=['bool'], + dist_func=sokalmichener, + cdist_func=_distance_pybind.cdist_sokalmichener, + pdist_func=_distance_pybind.pdist_sokalmichener, + ), + MetricInfo( + canonical_name='sokalsneath', + aka={'sokalsneath'}, + types=['bool'], + dist_func=sokalsneath, + cdist_func=_distance_pybind.cdist_sokalsneath, + pdist_func=_distance_pybind.pdist_sokalsneath, + ), + MetricInfo( + canonical_name='sqeuclidean', + aka={'sqeuclidean', 'sqe', 'sqeuclid'}, + dist_func=sqeuclidean, + cdist_func=_distance_pybind.cdist_sqeuclidean, + pdist_func=_distance_pybind.pdist_sqeuclidean, + ), + MetricInfo( + canonical_name='yule', + aka={'yule'}, + types=['bool'], + dist_func=yule, + cdist_func=_distance_pybind.cdist_yule, + pdist_func=_distance_pybind.pdist_yule, + ), +] + +_METRICS = {info.canonical_name: info for info in _METRIC_INFOS} +_METRIC_ALIAS = {alias: info + for info in _METRIC_INFOS + for alias in info.aka} + +_METRICS_NAMES = list(_METRICS.keys()) + +_TEST_METRICS = {'test_' + info.canonical_name: info for info in _METRIC_INFOS} + + +def pdist(X, metric='euclidean', *, out=None, **kwargs): + """ + Pairwise distances between observations in n-dimensional space. + + See Notes for common calling conventions. + + Parameters + ---------- + X : array_like + An m by n array of m original observations in an + n-dimensional space. + metric : str or function, optional + The distance metric to use. The distance function can + be 'braycurtis', 'canberra', 'chebyshev', 'cityblock', + 'correlation', 'cosine', 'dice', 'euclidean', 'hamming', + 'jaccard', 'jensenshannon', 'kulczynski1', + 'mahalanobis', 'matching', 'minkowski', 'rogerstanimoto', + 'russellrao', 'seuclidean', 'sokalmichener', 'sokalsneath', + 'sqeuclidean', 'yule'. + out : ndarray, optional + The output array. + If not None, condensed distance matrix Y is stored in this array. + **kwargs : dict, optional + Extra arguments to `metric`: refer to each metric documentation for a + list of all possible arguments. + + Some possible arguments: + + p : scalar + The p-norm to apply for Minkowski, weighted and unweighted. + Default: 2. + + w : ndarray + The weight vector for metrics that support weights (e.g., Minkowski). + + V : ndarray + The variance vector for standardized Euclidean. + Default: var(X, axis=0, ddof=1) + + VI : ndarray + The inverse of the covariance matrix for Mahalanobis. + Default: inv(cov(X.T)).T + + Returns + ------- + Y : ndarray + Returns a condensed distance matrix Y. For each :math:`i` and :math:`j` + (where :math:`i 0` (note + that this is only a quasi-metric if :math:`0 < p < 1`). + + 3. ``Y = pdist(X, 'cityblock')`` + + Computes the city block or Manhattan distance between the + points. + + 4. ``Y = pdist(X, 'seuclidean', V=None)`` + + Computes the standardized Euclidean distance. The standardized + Euclidean distance between two n-vectors ``u`` and ``v`` is + + .. math:: + + \\sqrt{\\sum {(u_i-v_i)^2 / V[x_i]}} + + + V is the variance vector; V[i] is the variance computed over all + the i'th components of the points. If not passed, it is + automatically computed. + + 5. ``Y = pdist(X, 'sqeuclidean')`` + + Computes the squared Euclidean distance :math:`\\|u-v\\|_2^2` between + the vectors. + + 6. ``Y = pdist(X, 'cosine')`` + + Computes the cosine distance between vectors u and v, + + .. math:: + + 1 - \\frac{u \\cdot v} + {{\\|u\\|}_2 {\\|v\\|}_2} + + where :math:`\\|*\\|_2` is the 2-norm of its argument ``*``, and + :math:`u \\cdot v` is the dot product of ``u`` and ``v``. + + 7. ``Y = pdist(X, 'correlation')`` + + Computes the correlation distance between vectors u and v. This is + + .. math:: + + 1 - \\frac{(u - \\bar{u}) \\cdot (v - \\bar{v})} + {{\\|(u - \\bar{u})\\|}_2 {\\|(v - \\bar{v})\\|}_2} + + where :math:`\\bar{v}` is the mean of the elements of vector v, + and :math:`x \\cdot y` is the dot product of :math:`x` and :math:`y`. + + 8. ``Y = pdist(X, 'hamming')`` + + Computes the normalized Hamming distance, or the proportion of + those vector elements between two n-vectors ``u`` and ``v`` + which disagree. To save memory, the matrix ``X`` can be of type + boolean. + + 9. ``Y = pdist(X, 'jaccard')`` + + Computes the Jaccard distance between the points. Given two + vectors, ``u`` and ``v``, the Jaccard distance is the + proportion of those elements ``u[i]`` and ``v[i]`` that + disagree. + + 10. ``Y = pdist(X, 'jensenshannon')`` + + Computes the Jensen-Shannon distance between two probability arrays. + Given two probability vectors, :math:`p` and :math:`q`, the + Jensen-Shannon distance is + + .. math:: + + \\sqrt{\\frac{D(p \\parallel m) + D(q \\parallel m)}{2}} + + where :math:`m` is the pointwise mean of :math:`p` and :math:`q` + and :math:`D` is the Kullback-Leibler divergence. + + 11. ``Y = pdist(X, 'chebyshev')`` + + Computes the Chebyshev distance between the points. The + Chebyshev distance between two n-vectors ``u`` and ``v`` is the + maximum norm-1 distance between their respective elements. More + precisely, the distance is given by + + .. math:: + + d(u,v) = \\max_i {|u_i-v_i|} + + 12. ``Y = pdist(X, 'canberra')`` + + Computes the Canberra distance between the points. The + Canberra distance between two points ``u`` and ``v`` is + + .. math:: + + d(u,v) = \\sum_i \\frac{|u_i-v_i|} + {|u_i|+|v_i|} + + + 13. ``Y = pdist(X, 'braycurtis')`` + + Computes the Bray-Curtis distance between the points. The + Bray-Curtis distance between two points ``u`` and ``v`` is + + + .. math:: + + d(u,v) = \\frac{\\sum_i {|u_i-v_i|}} + {\\sum_i {|u_i+v_i|}} + + 14. ``Y = pdist(X, 'mahalanobis', VI=None)`` + + Computes the Mahalanobis distance between the points. The + Mahalanobis distance between two points ``u`` and ``v`` is + :math:`\\sqrt{(u-v)(1/V)(u-v)^T}` where :math:`(1/V)` (the ``VI`` + variable) is the inverse covariance. If ``VI`` is not None, + ``VI`` will be used as the inverse covariance matrix. + + 15. ``Y = pdist(X, 'yule')`` + + Computes the Yule distance between each pair of boolean + vectors. (see yule function documentation) + + 16. ``Y = pdist(X, 'matching')`` + + Synonym for 'hamming'. + + 17. ``Y = pdist(X, 'dice')`` + + Computes the Dice distance between each pair of boolean + vectors. (see dice function documentation) + + 18. ``Y = pdist(X, 'kulczynski1')`` + + Computes the kulczynski1 distance between each pair of + boolean vectors. (see kulczynski1 function documentation) + + 19. ``Y = pdist(X, 'rogerstanimoto')`` + + Computes the Rogers-Tanimoto distance between each pair of + boolean vectors. (see rogerstanimoto function documentation) + + 20. ``Y = pdist(X, 'russellrao')`` + + Computes the Russell-Rao distance between each pair of + boolean vectors. (see russellrao function documentation) + + 21. ``Y = pdist(X, 'sokalmichener')`` + + Computes the Sokal-Michener distance between each pair of + boolean vectors. (see sokalmichener function documentation) + + 22. ``Y = pdist(X, 'sokalsneath')`` + + Computes the Sokal-Sneath distance between each pair of + boolean vectors. (see sokalsneath function documentation) + + 23. ``Y = pdist(X, 'kulczynski1')`` + + Computes the Kulczynski 1 distance between each pair of + boolean vectors. (see kulczynski1 function documentation) + + 24. ``Y = pdist(X, f)`` + + Computes the distance between all pairs of vectors in X + using the user supplied 2-arity function f. For example, + Euclidean distance between the vectors could be computed + as follows:: + + dm = pdist(X, lambda u, v: np.sqrt(((u-v)**2).sum())) + + Note that you should avoid passing a reference to one of + the distance functions defined in this library. For example,:: + + dm = pdist(X, sokalsneath) + + would calculate the pair-wise distances between the vectors in + X using the Python function sokalsneath. This would result in + sokalsneath being called :math:`{n \\choose 2}` times, which + is inefficient. Instead, the optimized C version is more + efficient, and we call it using the following syntax.:: + + dm = pdist(X, 'sokalsneath') + + Examples + -------- + >>> import numpy as np + >>> from scipy.spatial.distance import pdist + + ``x`` is an array of five points in three-dimensional space. + + >>> x = np.array([[2, 0, 2], [2, 2, 3], [-2, 4, 5], [0, 1, 9], [2, 2, 4]]) + + ``pdist(x)`` with no additional arguments computes the 10 pairwise + Euclidean distances: + + >>> pdist(x) + array([2.23606798, 6.40312424, 7.34846923, 2.82842712, 4.89897949, + 6.40312424, 1. , 5.38516481, 4.58257569, 5.47722558]) + + The following computes the pairwise Minkowski distances with ``p = 3.5``: + + >>> pdist(x, metric='minkowski', p=3.5) + array([2.04898923, 5.1154929 , 7.02700737, 2.43802731, 4.19042714, + 6.03956994, 1. , 4.45128103, 4.10636143, 5.0619695 ]) + + The pairwise city block or Manhattan distances: + + >>> pdist(x, metric='cityblock') + array([ 3., 11., 10., 4., 8., 9., 1., 9., 7., 8.]) + + """ + # You can also call this as: + # Y = pdist(X, 'test_abc') + # where 'abc' is the metric being tested. This computes the distance + # between all pairs of vectors in X using the distance metric 'abc' but + # with a more succinct, verifiable, but less efficient implementation. + + X = _asarray_validated(X, sparse_ok=False, objects_ok=True, mask_ok=True, + check_finite=False) + + s = X.shape + if len(s) != 2: + raise ValueError('A 2-dimensional array must be passed.') + + m, n = s + + if callable(metric): + mstr = getattr(metric, '__name__', 'UnknownCustomMetric') + metric_info = _METRIC_ALIAS.get(mstr, None) + + if metric_info is not None: + X, typ, kwargs = _validate_pdist_input( + X, m, n, metric_info, **kwargs) + + return _pdist_callable(X, metric=metric, out=out, **kwargs) + elif isinstance(metric, str): + mstr = metric.lower() + metric_info = _METRIC_ALIAS.get(mstr, None) + + if metric_info is not None: + pdist_fn = metric_info.pdist_func + return pdist_fn(X, out=out, **kwargs) + elif mstr.startswith("test_"): + metric_info = _TEST_METRICS.get(mstr, None) + if metric_info is None: + raise ValueError(f'Unknown "Test" Distance Metric: {mstr[5:]}') + X, typ, kwargs = _validate_pdist_input( + X, m, n, metric_info, **kwargs) + return _pdist_callable( + X, metric=metric_info.dist_func, out=out, **kwargs) + else: + raise ValueError('Unknown Distance Metric: %s' % mstr) + else: + raise TypeError('2nd argument metric must be a string identifier ' + 'or a function.') + + +def squareform(X, force="no", checks=True): + """ + Convert a vector-form distance vector to a square-form distance + matrix, and vice-versa. + + Parameters + ---------- + X : array_like + Either a condensed or redundant distance matrix. + force : str, optional + As with MATLAB(TM), if force is equal to ``'tovector'`` or + ``'tomatrix'``, the input will be treated as a distance matrix or + distance vector respectively. + checks : bool, optional + If set to False, no checks will be made for matrix + symmetry nor zero diagonals. This is useful if it is known that + ``X - X.T1`` is small and ``diag(X)`` is close to zero. + These values are ignored any way so they do not disrupt the + squareform transformation. + + Returns + ------- + Y : ndarray + If a condensed distance matrix is passed, a redundant one is + returned, or if a redundant one is passed, a condensed distance + matrix is returned. + + Notes + ----- + 1. ``v = squareform(X)`` + + Given a square n-by-n symmetric distance matrix ``X``, + ``v = squareform(X)`` returns a ``n * (n-1) / 2`` + (i.e. binomial coefficient n choose 2) sized vector `v` + where :math:`v[{n \\choose 2} - {n-i \\choose 2} + (j-i-1)]` + is the distance between distinct points ``i`` and ``j``. + If ``X`` is non-square or asymmetric, an error is raised. + + 2. ``X = squareform(v)`` + + Given a ``n * (n-1) / 2`` sized vector ``v`` + for some integer ``n >= 1`` encoding distances as described, + ``X = squareform(v)`` returns a n-by-n distance matrix ``X``. + The ``X[i, j]`` and ``X[j, i]`` values are set to + :math:`v[{n \\choose 2} - {n-i \\choose 2} + (j-i-1)]` + and all diagonal elements are zero. + + In SciPy 0.19.0, ``squareform`` stopped casting all input types to + float64, and started returning arrays of the same dtype as the input. + + Examples + -------- + >>> import numpy as np + >>> from scipy.spatial.distance import pdist, squareform + + ``x`` is an array of five points in three-dimensional space. + + >>> x = np.array([[2, 0, 2], [2, 2, 3], [-2, 4, 5], [0, 1, 9], [2, 2, 4]]) + + ``pdist(x)`` computes the Euclidean distances between each pair of + points in ``x``. The distances are returned in a one-dimensional + array with length ``5*(5 - 1)/2 = 10``. + + >>> distvec = pdist(x) + >>> distvec + array([2.23606798, 6.40312424, 7.34846923, 2.82842712, 4.89897949, + 6.40312424, 1. , 5.38516481, 4.58257569, 5.47722558]) + + ``squareform(distvec)`` returns the 5x5 distance matrix. + + >>> m = squareform(distvec) + >>> m + array([[0. , 2.23606798, 6.40312424, 7.34846923, 2.82842712], + [2.23606798, 0. , 4.89897949, 6.40312424, 1. ], + [6.40312424, 4.89897949, 0. , 5.38516481, 4.58257569], + [7.34846923, 6.40312424, 5.38516481, 0. , 5.47722558], + [2.82842712, 1. , 4.58257569, 5.47722558, 0. ]]) + + When given a square distance matrix ``m``, ``squareform(m)`` returns + the one-dimensional condensed distance vector associated with the + matrix. In this case, we recover ``distvec``. + + >>> squareform(m) + array([2.23606798, 6.40312424, 7.34846923, 2.82842712, 4.89897949, + 6.40312424, 1. , 5.38516481, 4.58257569, 5.47722558]) + """ + X = np.ascontiguousarray(X) + + s = X.shape + + if force.lower() == 'tomatrix': + if len(s) != 1: + raise ValueError("Forcing 'tomatrix' but input X is not a " + "distance vector.") + elif force.lower() == 'tovector': + if len(s) != 2: + raise ValueError("Forcing 'tovector' but input X is not a " + "distance matrix.") + + # X = squareform(v) + if len(s) == 1: + if s[0] == 0: + return np.zeros((1, 1), dtype=X.dtype) + + # Grab the closest value to the square root of the number + # of elements times 2 to see if the number of elements + # is indeed a binomial coefficient. + d = int(np.ceil(np.sqrt(s[0] * 2))) + + # Check that v is of valid dimensions. + if d * (d - 1) != s[0] * 2: + raise ValueError('Incompatible vector size. It must be a binomial ' + 'coefficient n choose 2 for some integer n >= 2.') + + # Allocate memory for the distance matrix. + M = np.zeros((d, d), dtype=X.dtype) + + # Since the C code does not support striding using strides. + # The dimensions are used instead. + X = _copy_array_if_base_present(X) + + # Fill in the values of the distance matrix. + _distance_wrap.to_squareform_from_vector_wrap(M, X) + + # Return the distance matrix. + return M + elif len(s) == 2: + if s[0] != s[1]: + raise ValueError('The matrix argument must be square.') + if checks: + is_valid_dm(X, throw=True, name='X') + + # One-side of the dimensions is set here. + d = s[0] + + if d <= 1: + return np.array([], dtype=X.dtype) + + # Create a vector. + v = np.zeros((d * (d - 1)) // 2, dtype=X.dtype) + + # Since the C code does not support striding using strides. + # The dimensions are used instead. + X = _copy_array_if_base_present(X) + + # Convert the vector to squareform. + _distance_wrap.to_vector_from_squareform_wrap(X, v) + return v + else: + raise ValueError(('The first argument must be one or two dimensional ' + 'array. A %d-dimensional array is not ' + 'permitted') % len(s)) + + +def is_valid_dm(D, tol=0.0, throw=False, name="D", warning=False): + """ + Return True if input array is a valid distance matrix. + + Distance matrices must be 2-dimensional numpy arrays. + They must have a zero-diagonal, and they must be symmetric. + + Parameters + ---------- + D : array_like + The candidate object to test for validity. + tol : float, optional + The distance matrix should be symmetric. `tol` is the maximum + difference between entries ``ij`` and ``ji`` for the distance + metric to be considered symmetric. + throw : bool, optional + An exception is thrown if the distance matrix passed is not valid. + name : str, optional + The name of the variable to checked. This is useful if + throw is set to True so the offending variable can be identified + in the exception message when an exception is thrown. + warning : bool, optional + Instead of throwing an exception, a warning message is + raised. + + Returns + ------- + valid : bool + True if the variable `D` passed is a valid distance matrix. + + Notes + ----- + Small numerical differences in `D` and `D.T` and non-zeroness of + the diagonal are ignored if they are within the tolerance specified + by `tol`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.spatial.distance import is_valid_dm + + This matrix is a valid distance matrix. + + >>> d = np.array([[0.0, 1.1, 1.2, 1.3], + ... [1.1, 0.0, 1.0, 1.4], + ... [1.2, 1.0, 0.0, 1.5], + ... [1.3, 1.4, 1.5, 0.0]]) + >>> is_valid_dm(d) + True + + In the following examples, the input is not a valid distance matrix. + + Not square: + + >>> is_valid_dm([[0, 2, 2], [2, 0, 2]]) + False + + Nonzero diagonal element: + + >>> is_valid_dm([[0, 1, 1], [1, 2, 3], [1, 3, 0]]) + False + + Not symmetric: + + >>> is_valid_dm([[0, 1, 3], [2, 0, 1], [3, 1, 0]]) + False + + """ + D = np.asarray(D, order='c') + valid = True + try: + s = D.shape + if len(D.shape) != 2: + if name: + raise ValueError(('Distance matrix \'%s\' must have shape=2 ' + '(i.e. be two-dimensional).') % name) + else: + raise ValueError('Distance matrix must have shape=2 (i.e. ' + 'be two-dimensional).') + if tol == 0.0: + if not (D == D.T).all(): + if name: + raise ValueError(('Distance matrix \'%s\' must be ' + 'symmetric.') % name) + else: + raise ValueError('Distance matrix must be symmetric.') + if not (D[range(0, s[0]), range(0, s[0])] == 0).all(): + if name: + raise ValueError(('Distance matrix \'%s\' diagonal must ' + 'be zero.') % name) + else: + raise ValueError('Distance matrix diagonal must be zero.') + else: + if not (D - D.T <= tol).all(): + if name: + raise ValueError(f'Distance matrix \'{name}\' must be ' + f'symmetric within tolerance {tol:5.5f}.') + else: + raise ValueError('Distance matrix must be symmetric within ' + 'tolerance %5.5f.' % tol) + if not (D[range(0, s[0]), range(0, s[0])] <= tol).all(): + if name: + raise ValueError(f'Distance matrix \'{name}\' diagonal must be ' + f'close to zero within tolerance {tol:5.5f}.') + else: + raise ValueError(('Distance matrix \'{}\' diagonal must be close ' + 'to zero within tolerance {:5.5f}.').format(*tol)) + except Exception as e: + if throw: + raise + if warning: + warnings.warn(str(e), stacklevel=2) + valid = False + return valid + + +def is_valid_y(y, warning=False, throw=False, name=None): + """ + Return True if the input array is a valid condensed distance matrix. + + Condensed distance matrices must be 1-dimensional numpy arrays. + Their length must be a binomial coefficient :math:`{n \\choose 2}` + for some positive integer n. + + Parameters + ---------- + y : array_like + The condensed distance matrix. + warning : bool, optional + Invokes a warning if the variable passed is not a valid + condensed distance matrix. The warning message explains why + the distance matrix is not valid. `name` is used when + referencing the offending variable. + throw : bool, optional + Throws an exception if the variable passed is not a valid + condensed distance matrix. + name : bool, optional + Used when referencing the offending variable in the + warning or exception message. + + Returns + ------- + bool + True if the input array is a valid condensed distance matrix, + False otherwise. + + Examples + -------- + >>> from scipy.spatial.distance import is_valid_y + + This vector is a valid condensed distance matrix. The length is 6, + which corresponds to ``n = 4``, since ``4*(4 - 1)/2`` is 6. + + >>> v = [1.0, 1.2, 1.0, 0.5, 1.3, 0.9] + >>> is_valid_y(v) + True + + An input vector with length, say, 7, is not a valid condensed distance + matrix. + + >>> is_valid_y([1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]) + False + + """ + y = np.asarray(y, order='c') + valid = True + try: + if len(y.shape) != 1: + if name: + raise ValueError(('Condensed distance matrix \'%s\' must ' + 'have shape=1 (i.e. be one-dimensional).') + % name) + else: + raise ValueError('Condensed distance matrix must have shape=1 ' + '(i.e. be one-dimensional).') + n = y.shape[0] + d = int(np.ceil(np.sqrt(n * 2))) + if (d * (d - 1) / 2) != n: + if name: + raise ValueError(('Length n of condensed distance matrix ' + '\'%s\' must be a binomial coefficient, i.e.' + 'there must be a k such that ' + '(k \\choose 2)=n)!') % name) + else: + raise ValueError('Length n of condensed distance matrix must ' + 'be a binomial coefficient, i.e. there must ' + 'be a k such that (k \\choose 2)=n)!') + except Exception as e: + if throw: + raise + if warning: + warnings.warn(str(e), stacklevel=2) + valid = False + return valid + + +def num_obs_dm(d): + """ + Return the number of original observations that correspond to a + square, redundant distance matrix. + + Parameters + ---------- + d : array_like + The target distance matrix. + + Returns + ------- + num_obs_dm : int + The number of observations in the redundant distance matrix. + + Examples + -------- + Find the number of original observations corresponding + to a square redundant distance matrix d. + + >>> from scipy.spatial.distance import num_obs_dm + >>> d = [[0, 100, 200], [100, 0, 150], [200, 150, 0]] + >>> num_obs_dm(d) + 3 + """ + d = np.asarray(d, order='c') + is_valid_dm(d, tol=np.inf, throw=True, name='d') + return d.shape[0] + + +def num_obs_y(Y): + """ + Return the number of original observations that correspond to a + condensed distance matrix. + + Parameters + ---------- + Y : array_like + Condensed distance matrix. + + Returns + ------- + n : int + The number of observations in the condensed distance matrix `Y`. + + Examples + -------- + Find the number of original observations corresponding to a + condensed distance matrix Y. + + >>> from scipy.spatial.distance import num_obs_y + >>> Y = [1, 2, 3.5, 7, 10, 4] + >>> num_obs_y(Y) + 4 + """ + Y = np.asarray(Y, order='c') + is_valid_y(Y, throw=True, name='Y') + k = Y.shape[0] + if k == 0: + raise ValueError("The number of observations cannot be determined on " + "an empty distance matrix.") + d = int(np.ceil(np.sqrt(k * 2))) + if (d * (d - 1) / 2) != k: + raise ValueError("Invalid condensed distance matrix passed. Must be " + "some k where k=(n choose 2) for some n >= 2.") + return d + + +def _prepare_out_argument(out, dtype, expected_shape): + if out is None: + return np.empty(expected_shape, dtype=dtype) + + if out.shape != expected_shape: + raise ValueError("Output array has incorrect shape.") + if not out.flags.c_contiguous: + raise ValueError("Output array must be C-contiguous.") + if out.dtype != np.float64: + raise ValueError("Output array must be double type.") + return out + + +def _pdist_callable(X, *, out, metric, **kwargs): + n = X.shape[0] + out_size = (n * (n - 1)) // 2 + dm = _prepare_out_argument(out, np.float64, (out_size,)) + k = 0 + for i in range(X.shape[0] - 1): + for j in range(i + 1, X.shape[0]): + dm[k] = metric(X[i], X[j], **kwargs) + k += 1 + return dm + + +def _cdist_callable(XA, XB, *, out, metric, **kwargs): + mA = XA.shape[0] + mB = XB.shape[0] + dm = _prepare_out_argument(out, np.float64, (mA, mB)) + for i in range(mA): + for j in range(mB): + dm[i, j] = metric(XA[i], XB[j], **kwargs) + return dm + + +def cdist(XA, XB, metric='euclidean', *, out=None, **kwargs): + """ + Compute distance between each pair of the two collections of inputs. + + See Notes for common calling conventions. + + Parameters + ---------- + XA : array_like + An :math:`m_A` by :math:`n` array of :math:`m_A` + original observations in an :math:`n`-dimensional space. + Inputs are converted to float type. + XB : array_like + An :math:`m_B` by :math:`n` array of :math:`m_B` + original observations in an :math:`n`-dimensional space. + Inputs are converted to float type. + metric : str or callable, optional + The distance metric to use. If a string, the distance function can be + 'braycurtis', 'canberra', 'chebyshev', 'cityblock', 'correlation', + 'cosine', 'dice', 'euclidean', 'hamming', 'jaccard', 'jensenshannon', + 'kulczynski1', 'mahalanobis', 'matching', 'minkowski', + 'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener', + 'sokalsneath', 'sqeuclidean', 'yule'. + **kwargs : dict, optional + Extra arguments to `metric`: refer to each metric documentation for a + list of all possible arguments. + + Some possible arguments: + + p : scalar + The p-norm to apply for Minkowski, weighted and unweighted. + Default: 2. + + w : array_like + The weight vector for metrics that support weights (e.g., Minkowski). + + V : array_like + The variance vector for standardized Euclidean. + Default: var(vstack([XA, XB]), axis=0, ddof=1) + + VI : array_like + The inverse of the covariance matrix for Mahalanobis. + Default: inv(cov(vstack([XA, XB].T))).T + + out : ndarray + The output array + If not None, the distance matrix Y is stored in this array. + + Returns + ------- + Y : ndarray + A :math:`m_A` by :math:`m_B` distance matrix is returned. + For each :math:`i` and :math:`j`, the metric + ``dist(u=XA[i], v=XB[j])`` is computed and stored in the + :math:`ij` th entry. + + Raises + ------ + ValueError + An exception is thrown if `XA` and `XB` do not have + the same number of columns. + + Notes + ----- + The following are common calling conventions: + + 1. ``Y = cdist(XA, XB, 'euclidean')`` + + Computes the distance between :math:`m` points using + Euclidean distance (2-norm) as the distance metric between the + points. The points are arranged as :math:`m` + :math:`n`-dimensional row vectors in the matrix X. + + 2. ``Y = cdist(XA, XB, 'minkowski', p=2.)`` + + Computes the distances using the Minkowski distance + :math:`\\|u-v\\|_p` (:math:`p`-norm) where :math:`p > 0` (note + that this is only a quasi-metric if :math:`0 < p < 1`). + + 3. ``Y = cdist(XA, XB, 'cityblock')`` + + Computes the city block or Manhattan distance between the + points. + + 4. ``Y = cdist(XA, XB, 'seuclidean', V=None)`` + + Computes the standardized Euclidean distance. The standardized + Euclidean distance between two n-vectors ``u`` and ``v`` is + + .. math:: + + \\sqrt{\\sum {(u_i-v_i)^2 / V[x_i]}}. + + V is the variance vector; V[i] is the variance computed over all + the i'th components of the points. If not passed, it is + automatically computed. + + 5. ``Y = cdist(XA, XB, 'sqeuclidean')`` + + Computes the squared Euclidean distance :math:`\\|u-v\\|_2^2` between + the vectors. + + 6. ``Y = cdist(XA, XB, 'cosine')`` + + Computes the cosine distance between vectors u and v, + + .. math:: + + 1 - \\frac{u \\cdot v} + {{\\|u\\|}_2 {\\|v\\|}_2} + + where :math:`\\|*\\|_2` is the 2-norm of its argument ``*``, and + :math:`u \\cdot v` is the dot product of :math:`u` and :math:`v`. + + 7. ``Y = cdist(XA, XB, 'correlation')`` + + Computes the correlation distance between vectors u and v. This is + + .. math:: + + 1 - \\frac{(u - \\bar{u}) \\cdot (v - \\bar{v})} + {{\\|(u - \\bar{u})\\|}_2 {\\|(v - \\bar{v})\\|}_2} + + where :math:`\\bar{v}` is the mean of the elements of vector v, + and :math:`x \\cdot y` is the dot product of :math:`x` and :math:`y`. + + + 8. ``Y = cdist(XA, XB, 'hamming')`` + + Computes the normalized Hamming distance, or the proportion of + those vector elements between two n-vectors ``u`` and ``v`` + which disagree. To save memory, the matrix ``X`` can be of type + boolean. + + 9. ``Y = cdist(XA, XB, 'jaccard')`` + + Computes the Jaccard distance between the points. Given two + vectors, ``u`` and ``v``, the Jaccard distance is the + proportion of those elements ``u[i]`` and ``v[i]`` that + disagree where at least one of them is non-zero. + + 10. ``Y = cdist(XA, XB, 'jensenshannon')`` + + Computes the Jensen-Shannon distance between two probability arrays. + Given two probability vectors, :math:`p` and :math:`q`, the + Jensen-Shannon distance is + + .. math:: + + \\sqrt{\\frac{D(p \\parallel m) + D(q \\parallel m)}{2}} + + where :math:`m` is the pointwise mean of :math:`p` and :math:`q` + and :math:`D` is the Kullback-Leibler divergence. + + 11. ``Y = cdist(XA, XB, 'chebyshev')`` + + Computes the Chebyshev distance between the points. The + Chebyshev distance between two n-vectors ``u`` and ``v`` is the + maximum norm-1 distance between their respective elements. More + precisely, the distance is given by + + .. math:: + + d(u,v) = \\max_i {|u_i-v_i|}. + + 12. ``Y = cdist(XA, XB, 'canberra')`` + + Computes the Canberra distance between the points. The + Canberra distance between two points ``u`` and ``v`` is + + .. math:: + + d(u,v) = \\sum_i \\frac{|u_i-v_i|} + {|u_i|+|v_i|}. + + 13. ``Y = cdist(XA, XB, 'braycurtis')`` + + Computes the Bray-Curtis distance between the points. The + Bray-Curtis distance between two points ``u`` and ``v`` is + + + .. math:: + + d(u,v) = \\frac{\\sum_i (|u_i-v_i|)} + {\\sum_i (|u_i+v_i|)} + + 14. ``Y = cdist(XA, XB, 'mahalanobis', VI=None)`` + + Computes the Mahalanobis distance between the points. The + Mahalanobis distance between two points ``u`` and ``v`` is + :math:`\\sqrt{(u-v)(1/V)(u-v)^T}` where :math:`(1/V)` (the ``VI`` + variable) is the inverse covariance. If ``VI`` is not None, + ``VI`` will be used as the inverse covariance matrix. + + 15. ``Y = cdist(XA, XB, 'yule')`` + + Computes the Yule distance between the boolean + vectors. (see `yule` function documentation) + + 16. ``Y = cdist(XA, XB, 'matching')`` + + Synonym for 'hamming'. + + 17. ``Y = cdist(XA, XB, 'dice')`` + + Computes the Dice distance between the boolean vectors. (see + `dice` function documentation) + + 18. ``Y = cdist(XA, XB, 'kulczynski1')`` + + Computes the kulczynski distance between the boolean + vectors. (see `kulczynski1` function documentation) + + 19. ``Y = cdist(XA, XB, 'rogerstanimoto')`` + + Computes the Rogers-Tanimoto distance between the boolean + vectors. (see `rogerstanimoto` function documentation) + + 20. ``Y = cdist(XA, XB, 'russellrao')`` + + Computes the Russell-Rao distance between the boolean + vectors. (see `russellrao` function documentation) + + 21. ``Y = cdist(XA, XB, 'sokalmichener')`` + + Computes the Sokal-Michener distance between the boolean + vectors. (see `sokalmichener` function documentation) + + 22. ``Y = cdist(XA, XB, 'sokalsneath')`` + + Computes the Sokal-Sneath distance between the vectors. (see + `sokalsneath` function documentation) + + 23. ``Y = cdist(XA, XB, f)`` + + Computes the distance between all pairs of vectors in X + using the user supplied 2-arity function f. For example, + Euclidean distance between the vectors could be computed + as follows:: + + dm = cdist(XA, XB, lambda u, v: np.sqrt(((u-v)**2).sum())) + + Note that you should avoid passing a reference to one of + the distance functions defined in this library. For example,:: + + dm = cdist(XA, XB, sokalsneath) + + would calculate the pair-wise distances between the vectors in + X using the Python function `sokalsneath`. This would result in + sokalsneath being called :math:`{n \\choose 2}` times, which + is inefficient. Instead, the optimized C version is more + efficient, and we call it using the following syntax:: + + dm = cdist(XA, XB, 'sokalsneath') + + Examples + -------- + Find the Euclidean distances between four 2-D coordinates: + + >>> from scipy.spatial import distance + >>> import numpy as np + >>> coords = [(35.0456, -85.2672), + ... (35.1174, -89.9711), + ... (35.9728, -83.9422), + ... (36.1667, -86.7833)] + >>> distance.cdist(coords, coords, 'euclidean') + array([[ 0. , 4.7044, 1.6172, 1.8856], + [ 4.7044, 0. , 6.0893, 3.3561], + [ 1.6172, 6.0893, 0. , 2.8477], + [ 1.8856, 3.3561, 2.8477, 0. ]]) + + + Find the Manhattan distance from a 3-D point to the corners of the unit + cube: + + >>> a = np.array([[0, 0, 0], + ... [0, 0, 1], + ... [0, 1, 0], + ... [0, 1, 1], + ... [1, 0, 0], + ... [1, 0, 1], + ... [1, 1, 0], + ... [1, 1, 1]]) + >>> b = np.array([[ 0.1, 0.2, 0.4]]) + >>> distance.cdist(a, b, 'cityblock') + array([[ 0.7], + [ 0.9], + [ 1.3], + [ 1.5], + [ 1.5], + [ 1.7], + [ 2.1], + [ 2.3]]) + + """ + # You can also call this as: + # Y = cdist(XA, XB, 'test_abc') + # where 'abc' is the metric being tested. This computes the distance + # between all pairs of vectors in XA and XB using the distance metric 'abc' + # but with a more succinct, verifiable, but less efficient implementation. + + XA = np.asarray(XA) + XB = np.asarray(XB) + + s = XA.shape + sB = XB.shape + + if len(s) != 2: + raise ValueError('XA must be a 2-dimensional array.') + if len(sB) != 2: + raise ValueError('XB must be a 2-dimensional array.') + if s[1] != sB[1]: + raise ValueError('XA and XB must have the same number of columns ' + '(i.e. feature dimension.)') + + mA = s[0] + mB = sB[0] + n = s[1] + + if callable(metric): + mstr = getattr(metric, '__name__', 'Unknown') + metric_info = _METRIC_ALIAS.get(mstr, None) + if metric_info is not None: + XA, XB, typ, kwargs = _validate_cdist_input( + XA, XB, mA, mB, n, metric_info, **kwargs) + return _cdist_callable(XA, XB, metric=metric, out=out, **kwargs) + elif isinstance(metric, str): + mstr = metric.lower() + metric_info = _METRIC_ALIAS.get(mstr, None) + if metric_info is not None: + cdist_fn = metric_info.cdist_func + return cdist_fn(XA, XB, out=out, **kwargs) + elif mstr.startswith("test_"): + metric_info = _TEST_METRICS.get(mstr, None) + if metric_info is None: + raise ValueError(f'Unknown "Test" Distance Metric: {mstr[5:]}') + XA, XB, typ, kwargs = _validate_cdist_input( + XA, XB, mA, mB, n, metric_info, **kwargs) + return _cdist_callable( + XA, XB, metric=metric_info.dist_func, out=out, **kwargs) + else: + raise ValueError('Unknown Distance Metric: %s' % mstr) + else: + raise TypeError('2nd argument metric must be a string identifier ' + 'or a function.') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.pyi new file mode 100644 index 0000000000000000000000000000000000000000..0e231b46dd3b2d248e54d1259b384b9e48facd9a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/distance.pyi @@ -0,0 +1,211 @@ +from __future__ import annotations +from typing import (overload, Any, SupportsFloat, Literal, Protocol, SupportsIndex) + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +# Anything that can be parsed by `np.float64.__init__` and is thus +# compatible with `ndarray.__setitem__` (for a float64 array) +_FloatValue = None | str | bytes | SupportsFloat | SupportsIndex + +class _MetricCallback1(Protocol): + def __call__( + self, __XA: NDArray[Any], __XB: NDArray[Any] + ) -> _FloatValue: ... + +class _MetricCallback2(Protocol): + def __call__( + self, __XA: NDArray[Any], __XB: NDArray[Any], **kwargs: Any + ) -> _FloatValue: ... + +# TODO: Use a single protocol with a parameter specification variable +# once available (PEP 612) +_MetricCallback = _MetricCallback1 | _MetricCallback2 + +_MetricKind = Literal[ + 'braycurtis', + 'canberra', + 'chebychev', 'chebyshev', 'cheby', 'cheb', 'ch', + 'cityblock', 'cblock', 'cb', 'c', + 'correlation', 'co', + 'cosine', 'cos', + 'dice', + 'euclidean', 'euclid', 'eu', 'e', + 'hamming', 'hamm', 'ha', 'h', + 'minkowski', 'mi', 'm', 'pnorm', + 'jaccard', 'jacc', 'ja', 'j', + 'jensenshannon', 'js', + 'kulczynski1', + 'mahalanobis', 'mahal', 'mah', + 'rogerstanimoto', + 'russellrao', + 'seuclidean', 'se', 's', + 'sokalmichener', + 'sokalsneath', + 'sqeuclidean', 'sqe', 'sqeuclid', + 'yule', +] + +# Function annotations + +def braycurtis( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def canberra( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +# TODO: Add `metric`-specific overloads +# Returns a float64 or float128 array, depending on the input dtype +@overload +def cdist( + XA: ArrayLike, + XB: ArrayLike, + metric: _MetricKind = ..., + *, + out: None | NDArray[np.floating[Any]] = ..., + p: float = ..., + w: ArrayLike | None = ..., + V: ArrayLike | None = ..., + VI: ArrayLike | None = ..., +) -> NDArray[np.floating[Any]]: ... +@overload +def cdist( + XA: ArrayLike, + XB: ArrayLike, + metric: _MetricCallback, + *, + out: None | NDArray[np.floating[Any]] = ..., + **kwargs: Any, +) -> NDArray[np.floating[Any]]: ... + +# TODO: Wait for dtype support; the return type is +# dependent on the input arrays dtype +def chebyshev( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> Any: ... + +# TODO: Wait for dtype support; the return type is +# dependent on the input arrays dtype +def cityblock( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> Any: ... + +def correlation( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ..., centered: bool = ... +) -> np.float64: ... + +def cosine( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def dice( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... + +def directed_hausdorff( + u: ArrayLike, v: ArrayLike, seed: int | None = ... +) -> tuple[float, int, int]: ... + +def euclidean( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... + +def hamming( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def is_valid_dm( + D: ArrayLike, + tol: float = ..., + throw: bool = ..., + name: str | None = ..., + warning: bool = ..., +) -> bool: ... + +def is_valid_y( + y: ArrayLike, + warning: bool = ..., + throw: bool = ..., + name: str | None = ..., +) -> bool: ... + +def jaccard( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def jensenshannon( + p: ArrayLike, q: ArrayLike, base: float | None = ... +) -> np.float64: ... + +def kulczynski1( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def mahalanobis( + u: ArrayLike, v: ArrayLike, VI: ArrayLike +) -> np.float64: ... + +def minkowski( + u: ArrayLike, v: ArrayLike, p: float = ..., w: ArrayLike | None = ... +) -> float: ... + +def num_obs_dm(d: ArrayLike) -> int: ... + +def num_obs_y(Y: ArrayLike) -> int: ... + +# TODO: Add `metric`-specific overloads +@overload +def pdist( + X: ArrayLike, + metric: _MetricKind = ..., + *, + out: None | NDArray[np.floating[Any]] = ..., + p: float = ..., + w: ArrayLike | None = ..., + V: ArrayLike | None = ..., + VI: ArrayLike | None = ..., +) -> NDArray[np.floating[Any]]: ... +@overload +def pdist( + X: ArrayLike, + metric: _MetricCallback, + *, + out: None | NDArray[np.floating[Any]] = ..., + **kwargs: Any, +) -> NDArray[np.floating[Any]]: ... + +def seuclidean( + u: ArrayLike, v: ArrayLike, V: ArrayLike +) -> float: ... + +def sokalmichener( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... + +def sokalsneath( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def sqeuclidean( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> np.float64: ... + +def squareform( + X: ArrayLike, + force: Literal["no", "tomatrix", "tovector"] = ..., + checks: bool = ..., +) -> NDArray[Any]: ... + +def rogerstanimoto( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... + +def russellrao( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... + +def yule( + u: ArrayLike, v: ArrayLike, w: ArrayLike | None = ... +) -> float: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/kdtree.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/kdtree.py new file mode 100644 index 0000000000000000000000000000000000000000..c0a6bab41e94733fccbb96313db6fcd868d284ea --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/kdtree.py @@ -0,0 +1,26 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.spatial` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'KDTree', + 'Rectangle', + 'cKDTree', + 'cKDTreeNode', + 'distance_matrix', + 'minkowski_distance', + 'minkowski_distance_p', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="spatial", module="kdtree", + private_modules=["_kdtree"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull.py new file mode 100644 index 0000000000000000000000000000000000000000..a8d51bf239bfe48077e66a36bcbf59f6dbadaf95 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull.py @@ -0,0 +1,25 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.spatial` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'ConvexHull', + 'Delaunay', + 'HalfspaceIntersection', + 'QhullError', + 'Voronoi', + 'tsearch', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="spatial", module="qhull", + private_modules=["_qhull"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull_src/COPYING.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull_src/COPYING.txt new file mode 100644 index 0000000000000000000000000000000000000000..4ac02a07f45d562410025f05305c31d1ec39a28c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/qhull_src/COPYING.txt @@ -0,0 +1,38 @@ + Qhull, Copyright (c) 1993-2019 + + C.B. Barber + Arlington, MA + + and + + The National Science and Technology Research Center for + Computation and Visualization of Geometric Structures + (The Geometry Center) + University of Minnesota + + email: qhull@qhull.org + +This software includes Qhull from C.B. Barber and The Geometry Center. +Qhull is copyrighted as noted above. Qhull is free software and may +be obtained via http from www.qhull.org. It may be freely copied, modified, +and redistributed under the following conditions: + +1. All copyright notices must remain intact in all files. + +2. A copy of this text file must be distributed along with any copies + of Qhull that you redistribute; this includes copies that you have + modified, or copies of programs or other software products that + include Qhull. + +3. If you modify Qhull, you must include a notice giving the + name of the person performing the modification, the date of + modification, and the reason for such modification. + +4. When distributing modified versions of Qhull, or other software + products that include Qhull, you must provide notice that the original + source code may be obtained as noted above. + +5. There is no warranty or other guarantee of fitness for Qhull, it is + provided solely "as is". Bug reports or fixes may be sent to + qhull_bug@qhull.org; the authors may or may not act on them as + they desire. diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X1.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X1.txt new file mode 100644 index 0000000000000000000000000000000000000000..833d5bdf2a344f585c5f34faa3e22716b1aa363c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X1.txt @@ -0,0 +1,10 @@ +1.147593763490969421e-01 8.926156143344999849e-01 1.437758624645746330e-02 1.803435962879929022e-02 5.533046214065578949e-01 5.554315640747428118e-01 4.497546637814608950e-02 4.438089247948049376e-01 7.984582810220538507e-01 2.752880789161644692e-01 1.344667112315823809e-01 9.230479561452992199e-01 6.040471462941819913e-01 3.797251652770228247e-01 4.316042735592399149e-01 5.312356915348823705e-01 4.348143005129563310e-01 3.111531488508799681e-01 9.531194313908697424e-04 8.212995023500069269e-02 6.689953269869852726e-01 9.914864535288493430e-01 8.037556036341153565e-01 +9.608925123801395074e-01 2.974451233678974127e-01 9.001110330654185088e-01 5.824163330415995654e-01 7.308574928293812834e-01 2.276154562412870952e-01 7.306791076039623745e-01 8.677244866905511333e-01 9.160806456176984192e-01 6.157216959991280714e-01 5.149053524695440531e-01 3.056427344890983999e-01 9.790557366933895223e-01 4.484995861076724877e-01 4.776550391081165747e-01 7.210436977670631187e-01 9.136399501661039979e-01 4.260275733550000776e-02 5.943900041968954717e-01 3.864571606342745991e-01 9.442027665110838131e-01 4.779949058608601309e-02 6.107551944250865228e-01 +3.297286578103622023e-01 5.980207401936733502e-01 3.673301293561567205e-01 2.585830520887681949e-01 4.660558746104259686e-01 6.083795956610364986e-01 4.535206368070313632e-01 6.873989778785424276e-01 5.130152688495458468e-01 7.665877846542720198e-01 3.444402973525138023e-01 3.583658123644906102e-02 7.924818220986856732e-01 8.746685720522412444e-01 3.010105569182431884e-01 6.012239357385538163e-01 6.233737362204671006e-01 4.830438698668915176e-01 2.317286885842551047e-02 7.585989958123050547e-01 7.108257632278830451e-01 1.551024884178199281e-01 2.665485998155288083e-01 +2.456278068903017253e-02 4.148739837711815648e-01 1.986372227934196655e-01 6.920408530298168825e-01 1.003067576685774398e-01 7.421560456480125190e-01 1.808453980608998313e-01 4.251297882537475870e-01 6.773002683522370004e-01 4.084108792570182445e-01 7.462888013191590897e-01 8.069930220529277776e-01 9.211110587681808903e-01 4.141491046181076108e-01 7.486318689260342829e-01 9.515405507589296263e-01 4.634288892577109742e-03 8.027593488166355762e-01 3.010346805217798405e-01 8.663248877242523127e-01 2.479968181181605447e-01 5.619851096054278017e-01 3.903886764590250857e-01 +7.122019976035700584e-01 6.188878051047785878e-01 7.290897087051201320e-01 6.334802157757637442e-01 5.523084734954342156e-01 5.614937129563645213e-01 2.496741051791574462e-01 5.972227939599233926e-01 1.786590597761109622e-01 2.609525984850900038e-01 7.210438943286010538e-01 2.211429064605652250e-01 9.140497572472672250e-02 1.430242193668443962e-01 7.856446942916397447e-01 4.635256358156553125e-01 5.278744289813760426e-01 3.702808015407184072e-01 5.527073830480792038e-01 6.370732917599846168e-01 9.953487928925482953e-01 3.021789770611936765e-01 3.354901923998221402e-02 +6.509638560895427695e-01 8.387598220902757751e-01 7.761375971745763103e-01 1.481627639227802717e-01 3.529474982902305324e-01 4.883093646287851586e-01 9.652923033658690199e-01 9.500680513565308294e-01 3.061885005078281985e-01 7.271902818906019750e-01 2.358962978196710303e-03 7.359889703223099211e-01 8.988893768074724955e-01 4.135279653937307121e-02 8.516441856688283796e-01 4.889597623270667270e-01 5.575909822114655245e-01 9.010853652261575641e-01 2.912844516556202246e-01 9.088759383368658629e-01 8.104351227460024898e-01 8.080695436776826890e-01 1.430530913253185155e-01 +8.048001196608134400e-01 3.066089444418462762e-02 9.021887554292090661e-01 6.154331491807940591e-02 1.378912575206647784e-02 5.775720193142440673e-01 1.219298963069791464e-01 1.883270243412101808e-01 5.569262398688379356e-02 8.964817777510125651e-02 7.977092785346929782e-01 4.878149375226197293e-01 4.511973131518809410e-02 1.858690046801604323e-01 6.947686471083162063e-01 5.884058794291086025e-01 8.638884676612634816e-01 3.855470871341656336e-01 3.495049047300468059e-01 2.767740932353948136e-01 4.731087031714035218e-01 6.679001673437914288e-01 7.502944200696660682e-01 +6.527328264244687261e-01 8.289483383553154505e-01 9.179741348282299818e-01 1.065639864466713105e-01 6.253616929058514184e-01 5.927750325266062381e-01 3.039157425463192563e-01 2.452766763359194302e-01 6.514027700704632107e-01 5.529218485487964463e-01 4.941158239308394151e-01 6.605306467722642516e-01 2.273688037050677346e-01 4.282616592244774534e-01 2.956128257930247250e-01 1.154803628237965896e-01 9.228220410235263849e-01 6.663525307676617659e-01 1.908852615936970087e-01 9.921383408926374159e-01 4.988716450388516188e-01 1.014900352736023414e-01 3.363930180244284474e-01 +2.914369076275757919e-01 5.196673601143533272e-01 7.420144907858341465e-01 1.768984185504740569e-01 5.296766993228564369e-01 5.922023566159900776e-01 5.965161262020234334e-01 3.810272333046110793e-01 8.368797246118340194e-01 7.896422363801189892e-01 9.655797561098209414e-01 4.430034032346981121e-01 2.780869795706976122e-01 3.047310845416009162e-01 8.051138863500326703e-01 6.731468634690835895e-01 4.743383036815584930e-01 9.530709614322225853e-01 7.753587619850917934e-01 2.801137109357491051e-01 6.182543660889736614e-01 5.005218857766725593e-01 9.071447804755052857e-01 +2.075071644012620453e-01 4.834950086973934802e-01 3.037011473860764532e-01 6.476084284887700937e-01 8.107195771564194020e-01 7.869075869075803364e-01 6.851234019375299633e-01 3.544187468104398331e-02 4.847673235908021017e-01 5.690262846164507726e-01 1.663354142616256803e-01 9.692796809752548537e-01 4.133441725866372485e-01 6.729167604487583665e-01 3.998813427407297283e-01 8.272617414104491695e-01 2.129248316324727774e-01 6.517004761357130249e-01 7.363013506605019520e-01 4.072375306356985636e-01 4.463336683526665238e-01 5.485059309728204102e-01 1.981745754527846071e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X2.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X2.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc3ea19674ee36856446c75df98b8c17c53ca51f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/cdist-X2.txt @@ -0,0 +1,20 @@ +7.680465556300619667e-02 4.675022344069014180e-01 8.955498989131543963e-01 3.816236071436276411e-01 1.109030077070989329e-01 2.318928815459808668e-02 7.477394240984251983e-01 1.202289789304434864e-01 8.007290497575981769e-01 6.795195698871731027e-01 6.568225762396605605e-01 2.231475263228478445e-01 7.064624077661341151e-02 1.081656666815267176e-02 1.592069359090128033e-01 1.363392203645097389e-01 9.277020735447568667e-01 8.103136564528209407e-01 5.229467676276455812e-02 7.708020259874025504e-01 6.527954747473352359e-02 5.516397414886525796e-01 3.653371861367954443e-01 +8.144399106025798085e-01 7.731852525462976633e-01 6.909477620673205589e-01 9.696063817000286633e-01 4.297887511677249694e-01 6.989600553425188156e-01 7.310201335033380543e-01 3.135256147868910048e-01 5.715578037275241829e-01 3.935000744675094531e-01 2.057715781268398825e-01 5.892508589665171881e-01 8.512951599236765476e-01 9.569808799061578775e-01 6.164885878024699561e-01 4.714185430004367294e-01 6.128831737628155363e-01 6.641799309623502845e-01 6.001985185338730711e-01 4.231922889723856995e-01 7.605249308075449077e-01 1.064530958018087281e-01 6.306470691957204444e-01 +4.265470127256254518e-01 5.933766716280767239e-01 3.698589270536845053e-02 2.173799740537294412e-01 3.032679325475639009e-01 4.271831790058847611e-01 1.828944535901013690e-01 4.772333422710156592e-01 2.564773455194128138e-01 7.120329875362141347e-01 8.952243430110462530e-01 1.808777012183288013e-01 3.612151871458374464e-01 3.960999167923041631e-01 1.821669970670747318e-02 8.835474857189200559e-01 1.353104648821573663e-01 3.457291739160937016e-01 1.126467375304566199e-01 4.107293162402323450e-01 4.051719311053743056e-01 4.007382985250427243e-01 1.286905671428811848e-01 +2.910657003883979632e-01 9.616259180685315933e-03 2.033032441536681834e-01 1.096599110293863255e-01 4.191101704605176836e-01 5.462131536027151624e-01 8.393047907010142694e-01 9.046805198676335369e-01 7.009863472176891541e-01 2.508215985039629059e-01 6.754410796667598138e-01 6.740895474032024826e-01 1.358993708621679675e-01 8.219861775211464439e-01 6.322220445623235596e-01 2.766813559002430090e-01 6.575983861590951607e-01 9.515869708336625044e-01 8.654526462353933081e-01 3.450245117834797037e-01 5.649032890631299209e-01 4.717687914789682191e-01 3.296483580510030098e-01 +9.172477457635394016e-01 3.057396583041891436e-01 7.335332344225760082e-01 8.370236206345178509e-01 3.765464253115927695e-01 5.089680319287778199e-01 1.202325719268168003e-01 9.717771065272349240e-01 5.907820104019682050e-01 9.809211614977710880e-01 9.064285003671219698e-01 8.848841466121748489e-01 2.043407730734815297e-01 9.157600394927275511e-01 4.532260315147775831e-01 4.241077335005828397e-01 1.751730149568804240e-01 4.090412146081819911e-01 3.632197861847064058e-02 5.832539334970230360e-01 4.041848151536805434e-01 3.603643989086504629e-01 1.838411383882069261e-01 +2.508806403290032572e-01 4.381403985282813496e-01 4.694787405018008286e-02 6.353900562024634713e-01 1.200813444244532846e-01 6.072397042913001419e-01 9.937255904754030977e-01 4.916670237677555066e-01 3.473845913923001572e-01 3.526875922864345370e-01 5.448595548197197047e-01 2.245096010156972799e-01 9.003258279804994269e-01 3.534560469735994470e-01 2.989266066346342177e-01 4.621024982808636938e-01 9.626538866576676012e-01 9.791401720716153001e-01 7.138514287330390840e-01 9.832862333928654719e-01 3.233999591031431198e-01 5.406467224926423398e-01 9.581890295057201579e-01 +5.210583601680578436e-01 4.598159993059653949e-01 2.111497132057748027e-01 5.949977700916546652e-01 6.342618461422359077e-01 9.888228769705599275e-01 6.096770711536318998e-01 7.548431368960863974e-01 7.490858664860100546e-01 3.186213496546415058e-01 7.895687083231245351e-01 4.178326793268141159e-01 8.095818334534051752e-01 7.886271673523481684e-01 4.038905626506847923e-01 3.652649247094948981e-01 8.267205959224892542e-01 6.433617243328785262e-01 3.117681563249452559e-01 9.675995575054980868e-01 3.675673836358472890e-01 5.863757289184046151e-01 9.099029857959717305e-02 +4.024573981231733821e-01 3.578997554002771864e-01 3.519299868071553705e-01 7.417747693762357653e-01 2.963713903285800644e-01 9.602967989298948348e-01 3.811392331739601458e-01 5.493237898295448840e-01 6.835113342793640578e-01 2.304506220807415184e-01 3.727299857731285471e-01 5.450263991912108752e-01 6.951521210987908761e-01 6.474582745861203747e-01 6.316089475403589004e-01 5.672043967425510758e-02 9.034937506977609445e-01 2.332567550780038079e-01 1.096955741449157085e-02 8.870663813493575578e-01 4.384385452180562526e-01 7.100898998169548060e-01 3.245358176196319056e-01 +9.162009194452818139e-01 5.572224742426723498e-02 3.445910686865658601e-01 9.683564008127462097e-01 9.375063149031520604e-01 9.128188852869822956e-02 9.613605414326487075e-01 5.298598697556915482e-01 6.724799695520149445e-01 1.269103938571825019e-02 1.008406153387807480e-01 8.951105272379104028e-01 1.585460318853607609e-01 6.739986455059543413e-01 5.345419321702655768e-01 6.248843899572337213e-01 3.050288488994817859e-01 1.423645553465189284e-01 1.802121190541096096e-01 9.474646822694763326e-01 2.345716438587298613e-01 9.688281784764296578e-01 1.845165243240991515e-01 +2.548297646910531178e-01 2.580877375379494465e-01 1.355482532666937301e-01 6.478812986505504412e-01 9.971695982152032345e-01 2.606721082477282403e-01 5.483439686378906996e-01 4.409612606704470528e-01 4.396442074915688503e-01 7.414262832597111608e-01 7.308840725375539416e-01 8.072095530497225280e-02 6.829509968656330976e-01 5.700030854230387911e-01 3.801845336730320657e-01 2.481059916867158766e-01 3.977295094395927322e-03 5.749480512407895150e-01 4.112033136603401307e-01 8.676159710377848722e-01 9.062646588480167686e-01 3.326691167317923359e-01 8.498307982774666591e-01 +4.464338109330643345e-01 8.546516760817471914e-01 7.384800352329814466e-01 3.692485164984804502e-02 2.915662689505471583e-02 9.010049994217171898e-01 8.622900253010918892e-01 9.786230638032608065e-01 6.546824077297251909e-01 6.342297560006789903e-01 2.230339826582647955e-01 7.658846744185553446e-01 4.603043831539479491e-01 2.017100469861691225e-01 4.891590639893540482e-01 1.937140918314912419e-01 8.161582138652878626e-01 5.597293607114051106e-02 8.423261093326828153e-02 5.105392204475533990e-02 8.234193902673621057e-01 1.784268309975372002e-01 9.118997881986501408e-02 +8.588746913421980711e-01 1.479641118621310980e-02 1.375875301146138874e-01 7.533888774725254756e-01 5.782592791549248101e-01 9.128573037619659436e-01 1.831275762880391067e-01 3.471382864827737835e-01 4.859524740929310749e-02 8.955146541561730400e-01 4.787220791101074457e-01 4.222803577759057791e-01 8.469923964908064873e-01 6.300290047587608910e-02 1.020873237837905956e-01 3.585612487182909813e-02 6.320107119904569970e-01 5.891245970008752719e-01 1.104698053665007507e-01 4.233226558073774903e-01 4.432217054386708988e-01 2.864765416628194394e-01 2.489777211814803159e-02 +5.343810659756068615e-01 4.829076396403546578e-01 8.364480888953172988e-01 8.931374995414760321e-01 6.034161442354715188e-01 3.578336000768178593e-03 4.100579775972763574e-01 3.968667908067096128e-01 5.897163653686778861e-01 3.003241263928478899e-01 2.520935203143799264e-01 3.112129371563532310e-02 9.052865295974613646e-01 1.172285124002711010e-01 4.840001666149388315e-01 3.424620676348436588e-01 5.526057133826853818e-01 6.346139530261846184e-01 5.747945930485597321e-01 1.389915612177697879e-01 2.413801217666421417e-01 7.829900796662081497e-01 7.213528084845653998e-01 +9.384509283406079483e-01 6.303019601671526750e-01 1.787921522728125323e-01 1.556003868047917127e-02 5.662397078816850948e-01 3.437473614806091371e-01 8.615844972800188462e-01 7.624380237306396246e-01 1.096468347898514883e-01 1.276566836610887323e-01 8.479188493443535757e-01 3.634713454428405432e-01 7.478112314318967613e-01 9.856395696968375253e-01 6.250293654177319080e-02 1.919327272501809567e-01 1.415594476031050153e-01 7.224057351041784925e-01 8.452145259310355208e-01 5.434318833772002755e-01 5.177620959731277228e-02 3.358977598185840518e-01 2.542654881527960375e-01 +4.800909104006243489e-01 3.651345393613150137e-01 3.657093052788148446e-01 8.579662326651369408e-01 5.787694361240260932e-01 6.491966196891312268e-01 3.252508517294879775e-01 8.639694334693422961e-01 3.028097078756678551e-01 6.295814666338699350e-01 7.305627351548695803e-01 6.975931849120264872e-03 8.321205159004851915e-01 2.681809305821257761e-01 3.628869474597150591e-01 9.598981434716586936e-01 5.947913523332928332e-01 7.794864238003402779e-01 2.819511239444029149e-01 5.134200958476284882e-01 7.284684743064278045e-01 3.099571109539331903e-01 1.502222882866774967e-01 +2.463382654375219083e-01 4.465700737264240994e-01 7.180855317941433613e-01 5.056099420785193921e-01 6.182117344332578313e-01 2.370453793561340117e-01 9.831748018047525850e-01 6.397098184531551102e-01 8.260469782208745837e-02 7.474671691560941245e-01 9.963429983418570224e-02 5.450078811081275898e-01 5.370188678062637333e-02 2.774024442708808991e-01 2.082643088545442778e-01 2.704155352788065736e-01 7.225035580445194894e-01 4.866791976239246420e-01 1.357043111201584606e-01 7.911335827987711067e-01 7.278977102006007893e-01 6.880892094410231419e-01 1.029231496520791600e-01 +6.901796117735281566e-01 1.558248977395644275e-01 4.241818789360329855e-01 5.055658246392458199e-01 1.756288758075611467e-01 4.215083703818177652e-01 7.809231602323289945e-01 1.170053878686481141e-01 6.497026323614403243e-01 5.733120641440232479e-01 4.407703406152092551e-01 5.608677124532297498e-01 7.471045703286000039e-01 3.334604336022076732e-01 8.927208811415126011e-01 9.794565286182396191e-01 9.621542824973521313e-01 3.945825239405253981e-01 8.338963875792834157e-01 9.310552325082104286e-01 7.688283033784242271e-01 3.798823731047119567e-01 1.459993613028365278e-02 +7.848623555505630511e-01 2.681039365355797344e-03 7.833208051794043891e-01 8.184381915171493604e-01 4.682581645582317709e-01 2.391069309436419932e-01 1.765377537168698607e-01 9.863494676539893424e-01 4.378412300863872009e-01 7.494505491149090481e-01 1.942180356195394308e-01 9.981402467222395547e-01 7.992190944052800505e-01 1.350875702852057936e-01 4.950149186748543650e-01 7.243422481248201761e-01 3.544596746353472216e-01 8.320192561472177228e-01 9.776840296475269865e-01 7.733852731914863110e-01 2.305732998099923048e-01 9.746878189802981041e-01 7.747723331200035979e-01 +6.521099013127149568e-01 5.452399443648201505e-01 8.146707517183656710e-01 3.827256063695345656e-01 7.954832091744263867e-01 7.834427643148527132e-01 9.661317930643520402e-02 9.215673965718058636e-01 4.914305728788055383e-01 4.105628408027649501e-01 9.844647830893304974e-02 3.974831165301851987e-01 3.857608898053827007e-01 5.520210781401946321e-01 3.445787541654143915e-03 4.552922057017416702e-01 7.456544561760444223e-01 4.753985092154335845e-01 2.821385239833401615e-01 7.560136035104459973e-01 8.453142510471420845e-01 6.679627143276523071e-01 6.910882868284401459e-01 +8.526493480446283302e-01 1.183917973068240315e-01 6.163988861865119517e-01 5.751899460059114455e-01 1.638797964925038375e-01 8.214597298784013235e-01 5.424670654187370156e-01 1.806631819658732763e-01 9.268107278221827672e-01 4.127397378597359445e-01 7.529877485901653733e-01 1.714251090083847018e-01 2.601487784245806179e-01 2.028326156742237263e-01 5.299879450122358948e-01 7.587877062981395193e-01 4.070738595375062996e-01 3.546903049793261875e-01 8.695365138547607176e-01 1.447085661525142619e-01 3.193366245820845606e-01 8.797841086211429795e-01 2.666562188639977071e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..4d78390c2596beb41b1abff651a729e4e964c36e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/iris.txt @@ -0,0 +1,150 @@ +5.099999999999999645e+00 3.500000000000000000e+00 1.399999999999999911e+00 2.000000000000000111e-01 +4.900000000000000355e+00 3.000000000000000000e+00 1.399999999999999911e+00 2.000000000000000111e-01 +4.700000000000000178e+00 3.200000000000000178e+00 1.300000000000000044e+00 2.000000000000000111e-01 +4.599999999999999645e+00 3.100000000000000089e+00 1.500000000000000000e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.600000000000000089e+00 1.399999999999999911e+00 2.000000000000000111e-01 +5.400000000000000355e+00 3.899999999999999911e+00 1.699999999999999956e+00 4.000000000000000222e-01 +4.599999999999999645e+00 3.399999999999999911e+00 1.399999999999999911e+00 2.999999999999999889e-01 +5.000000000000000000e+00 3.399999999999999911e+00 1.500000000000000000e+00 2.000000000000000111e-01 +4.400000000000000355e+00 2.899999999999999911e+00 1.399999999999999911e+00 2.000000000000000111e-01 +4.900000000000000355e+00 3.100000000000000089e+00 1.500000000000000000e+00 1.000000000000000056e-01 +5.400000000000000355e+00 3.700000000000000178e+00 1.500000000000000000e+00 2.000000000000000111e-01 +4.799999999999999822e+00 3.399999999999999911e+00 1.600000000000000089e+00 2.000000000000000111e-01 +4.799999999999999822e+00 3.000000000000000000e+00 1.399999999999999911e+00 1.000000000000000056e-01 +4.299999999999999822e+00 3.000000000000000000e+00 1.100000000000000089e+00 1.000000000000000056e-01 +5.799999999999999822e+00 4.000000000000000000e+00 1.199999999999999956e+00 2.000000000000000111e-01 +5.700000000000000178e+00 4.400000000000000355e+00 1.500000000000000000e+00 4.000000000000000222e-01 +5.400000000000000355e+00 3.899999999999999911e+00 1.300000000000000044e+00 4.000000000000000222e-01 +5.099999999999999645e+00 3.500000000000000000e+00 1.399999999999999911e+00 2.999999999999999889e-01 +5.700000000000000178e+00 3.799999999999999822e+00 1.699999999999999956e+00 2.999999999999999889e-01 +5.099999999999999645e+00 3.799999999999999822e+00 1.500000000000000000e+00 2.999999999999999889e-01 +5.400000000000000355e+00 3.399999999999999911e+00 1.699999999999999956e+00 2.000000000000000111e-01 +5.099999999999999645e+00 3.700000000000000178e+00 1.500000000000000000e+00 4.000000000000000222e-01 +4.599999999999999645e+00 3.600000000000000089e+00 1.000000000000000000e+00 2.000000000000000111e-01 +5.099999999999999645e+00 3.299999999999999822e+00 1.699999999999999956e+00 5.000000000000000000e-01 +4.799999999999999822e+00 3.399999999999999911e+00 1.899999999999999911e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.000000000000000000e+00 1.600000000000000089e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.399999999999999911e+00 1.600000000000000089e+00 4.000000000000000222e-01 +5.200000000000000178e+00 3.500000000000000000e+00 1.500000000000000000e+00 2.000000000000000111e-01 +5.200000000000000178e+00 3.399999999999999911e+00 1.399999999999999911e+00 2.000000000000000111e-01 +4.700000000000000178e+00 3.200000000000000178e+00 1.600000000000000089e+00 2.000000000000000111e-01 +4.799999999999999822e+00 3.100000000000000089e+00 1.600000000000000089e+00 2.000000000000000111e-01 +5.400000000000000355e+00 3.399999999999999911e+00 1.500000000000000000e+00 4.000000000000000222e-01 +5.200000000000000178e+00 4.099999999999999645e+00 1.500000000000000000e+00 1.000000000000000056e-01 +5.500000000000000000e+00 4.200000000000000178e+00 1.399999999999999911e+00 2.000000000000000111e-01 +4.900000000000000355e+00 3.100000000000000089e+00 1.500000000000000000e+00 1.000000000000000056e-01 +5.000000000000000000e+00 3.200000000000000178e+00 1.199999999999999956e+00 2.000000000000000111e-01 +5.500000000000000000e+00 3.500000000000000000e+00 1.300000000000000044e+00 2.000000000000000111e-01 +4.900000000000000355e+00 3.100000000000000089e+00 1.500000000000000000e+00 1.000000000000000056e-01 +4.400000000000000355e+00 3.000000000000000000e+00 1.300000000000000044e+00 2.000000000000000111e-01 +5.099999999999999645e+00 3.399999999999999911e+00 1.500000000000000000e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.500000000000000000e+00 1.300000000000000044e+00 2.999999999999999889e-01 +4.500000000000000000e+00 2.299999999999999822e+00 1.300000000000000044e+00 2.999999999999999889e-01 +4.400000000000000355e+00 3.200000000000000178e+00 1.300000000000000044e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.500000000000000000e+00 1.600000000000000089e+00 5.999999999999999778e-01 +5.099999999999999645e+00 3.799999999999999822e+00 1.899999999999999911e+00 4.000000000000000222e-01 +4.799999999999999822e+00 3.000000000000000000e+00 1.399999999999999911e+00 2.999999999999999889e-01 +5.099999999999999645e+00 3.799999999999999822e+00 1.600000000000000089e+00 2.000000000000000111e-01 +4.599999999999999645e+00 3.200000000000000178e+00 1.399999999999999911e+00 2.000000000000000111e-01 +5.299999999999999822e+00 3.700000000000000178e+00 1.500000000000000000e+00 2.000000000000000111e-01 +5.000000000000000000e+00 3.299999999999999822e+00 1.399999999999999911e+00 2.000000000000000111e-01 +7.000000000000000000e+00 3.200000000000000178e+00 4.700000000000000178e+00 1.399999999999999911e+00 +6.400000000000000355e+00 3.200000000000000178e+00 4.500000000000000000e+00 1.500000000000000000e+00 +6.900000000000000355e+00 3.100000000000000089e+00 4.900000000000000355e+00 1.500000000000000000e+00 +5.500000000000000000e+00 2.299999999999999822e+00 4.000000000000000000e+00 1.300000000000000044e+00 +6.500000000000000000e+00 2.799999999999999822e+00 4.599999999999999645e+00 1.500000000000000000e+00 +5.700000000000000178e+00 2.799999999999999822e+00 4.500000000000000000e+00 1.300000000000000044e+00 +6.299999999999999822e+00 3.299999999999999822e+00 4.700000000000000178e+00 1.600000000000000089e+00 +4.900000000000000355e+00 2.399999999999999911e+00 3.299999999999999822e+00 1.000000000000000000e+00 +6.599999999999999645e+00 2.899999999999999911e+00 4.599999999999999645e+00 1.300000000000000044e+00 +5.200000000000000178e+00 2.700000000000000178e+00 3.899999999999999911e+00 1.399999999999999911e+00 +5.000000000000000000e+00 2.000000000000000000e+00 3.500000000000000000e+00 1.000000000000000000e+00 +5.900000000000000355e+00 3.000000000000000000e+00 4.200000000000000178e+00 1.500000000000000000e+00 +6.000000000000000000e+00 2.200000000000000178e+00 4.000000000000000000e+00 1.000000000000000000e+00 +6.099999999999999645e+00 2.899999999999999911e+00 4.700000000000000178e+00 1.399999999999999911e+00 +5.599999999999999645e+00 2.899999999999999911e+00 3.600000000000000089e+00 1.300000000000000044e+00 +6.700000000000000178e+00 3.100000000000000089e+00 4.400000000000000355e+00 1.399999999999999911e+00 +5.599999999999999645e+00 3.000000000000000000e+00 4.500000000000000000e+00 1.500000000000000000e+00 +5.799999999999999822e+00 2.700000000000000178e+00 4.099999999999999645e+00 1.000000000000000000e+00 +6.200000000000000178e+00 2.200000000000000178e+00 4.500000000000000000e+00 1.500000000000000000e+00 +5.599999999999999645e+00 2.500000000000000000e+00 3.899999999999999911e+00 1.100000000000000089e+00 +5.900000000000000355e+00 3.200000000000000178e+00 4.799999999999999822e+00 1.800000000000000044e+00 +6.099999999999999645e+00 2.799999999999999822e+00 4.000000000000000000e+00 1.300000000000000044e+00 +6.299999999999999822e+00 2.500000000000000000e+00 4.900000000000000355e+00 1.500000000000000000e+00 +6.099999999999999645e+00 2.799999999999999822e+00 4.700000000000000178e+00 1.199999999999999956e+00 +6.400000000000000355e+00 2.899999999999999911e+00 4.299999999999999822e+00 1.300000000000000044e+00 +6.599999999999999645e+00 3.000000000000000000e+00 4.400000000000000355e+00 1.399999999999999911e+00 +6.799999999999999822e+00 2.799999999999999822e+00 4.799999999999999822e+00 1.399999999999999911e+00 +6.700000000000000178e+00 3.000000000000000000e+00 5.000000000000000000e+00 1.699999999999999956e+00 +6.000000000000000000e+00 2.899999999999999911e+00 4.500000000000000000e+00 1.500000000000000000e+00 +5.700000000000000178e+00 2.600000000000000089e+00 3.500000000000000000e+00 1.000000000000000000e+00 +5.500000000000000000e+00 2.399999999999999911e+00 3.799999999999999822e+00 1.100000000000000089e+00 +5.500000000000000000e+00 2.399999999999999911e+00 3.700000000000000178e+00 1.000000000000000000e+00 +5.799999999999999822e+00 2.700000000000000178e+00 3.899999999999999911e+00 1.199999999999999956e+00 +6.000000000000000000e+00 2.700000000000000178e+00 5.099999999999999645e+00 1.600000000000000089e+00 +5.400000000000000355e+00 3.000000000000000000e+00 4.500000000000000000e+00 1.500000000000000000e+00 +6.000000000000000000e+00 3.399999999999999911e+00 4.500000000000000000e+00 1.600000000000000089e+00 +6.700000000000000178e+00 3.100000000000000089e+00 4.700000000000000178e+00 1.500000000000000000e+00 +6.299999999999999822e+00 2.299999999999999822e+00 4.400000000000000355e+00 1.300000000000000044e+00 +5.599999999999999645e+00 3.000000000000000000e+00 4.099999999999999645e+00 1.300000000000000044e+00 +5.500000000000000000e+00 2.500000000000000000e+00 4.000000000000000000e+00 1.300000000000000044e+00 +5.500000000000000000e+00 2.600000000000000089e+00 4.400000000000000355e+00 1.199999999999999956e+00 +6.099999999999999645e+00 3.000000000000000000e+00 4.599999999999999645e+00 1.399999999999999911e+00 +5.799999999999999822e+00 2.600000000000000089e+00 4.000000000000000000e+00 1.199999999999999956e+00 +5.000000000000000000e+00 2.299999999999999822e+00 3.299999999999999822e+00 1.000000000000000000e+00 +5.599999999999999645e+00 2.700000000000000178e+00 4.200000000000000178e+00 1.300000000000000044e+00 +5.700000000000000178e+00 3.000000000000000000e+00 4.200000000000000178e+00 1.199999999999999956e+00 +5.700000000000000178e+00 2.899999999999999911e+00 4.200000000000000178e+00 1.300000000000000044e+00 +6.200000000000000178e+00 2.899999999999999911e+00 4.299999999999999822e+00 1.300000000000000044e+00 +5.099999999999999645e+00 2.500000000000000000e+00 3.000000000000000000e+00 1.100000000000000089e+00 +5.700000000000000178e+00 2.799999999999999822e+00 4.099999999999999645e+00 1.300000000000000044e+00 +6.299999999999999822e+00 3.299999999999999822e+00 6.000000000000000000e+00 2.500000000000000000e+00 +5.799999999999999822e+00 2.700000000000000178e+00 5.099999999999999645e+00 1.899999999999999911e+00 +7.099999999999999645e+00 3.000000000000000000e+00 5.900000000000000355e+00 2.100000000000000089e+00 +6.299999999999999822e+00 2.899999999999999911e+00 5.599999999999999645e+00 1.800000000000000044e+00 +6.500000000000000000e+00 3.000000000000000000e+00 5.799999999999999822e+00 2.200000000000000178e+00 +7.599999999999999645e+00 3.000000000000000000e+00 6.599999999999999645e+00 2.100000000000000089e+00 +4.900000000000000355e+00 2.500000000000000000e+00 4.500000000000000000e+00 1.699999999999999956e+00 +7.299999999999999822e+00 2.899999999999999911e+00 6.299999999999999822e+00 1.800000000000000044e+00 +6.700000000000000178e+00 2.500000000000000000e+00 5.799999999999999822e+00 1.800000000000000044e+00 +7.200000000000000178e+00 3.600000000000000089e+00 6.099999999999999645e+00 2.500000000000000000e+00 +6.500000000000000000e+00 3.200000000000000178e+00 5.099999999999999645e+00 2.000000000000000000e+00 +6.400000000000000355e+00 2.700000000000000178e+00 5.299999999999999822e+00 1.899999999999999911e+00 +6.799999999999999822e+00 3.000000000000000000e+00 5.500000000000000000e+00 2.100000000000000089e+00 +5.700000000000000178e+00 2.500000000000000000e+00 5.000000000000000000e+00 2.000000000000000000e+00 +5.799999999999999822e+00 2.799999999999999822e+00 5.099999999999999645e+00 2.399999999999999911e+00 +6.400000000000000355e+00 3.200000000000000178e+00 5.299999999999999822e+00 2.299999999999999822e+00 +6.500000000000000000e+00 3.000000000000000000e+00 5.500000000000000000e+00 1.800000000000000044e+00 +7.700000000000000178e+00 3.799999999999999822e+00 6.700000000000000178e+00 2.200000000000000178e+00 +7.700000000000000178e+00 2.600000000000000089e+00 6.900000000000000355e+00 2.299999999999999822e+00 +6.000000000000000000e+00 2.200000000000000178e+00 5.000000000000000000e+00 1.500000000000000000e+00 +6.900000000000000355e+00 3.200000000000000178e+00 5.700000000000000178e+00 2.299999999999999822e+00 +5.599999999999999645e+00 2.799999999999999822e+00 4.900000000000000355e+00 2.000000000000000000e+00 +7.700000000000000178e+00 2.799999999999999822e+00 6.700000000000000178e+00 2.000000000000000000e+00 +6.299999999999999822e+00 2.700000000000000178e+00 4.900000000000000355e+00 1.800000000000000044e+00 +6.700000000000000178e+00 3.299999999999999822e+00 5.700000000000000178e+00 2.100000000000000089e+00 +7.200000000000000178e+00 3.200000000000000178e+00 6.000000000000000000e+00 1.800000000000000044e+00 +6.200000000000000178e+00 2.799999999999999822e+00 4.799999999999999822e+00 1.800000000000000044e+00 +6.099999999999999645e+00 3.000000000000000000e+00 4.900000000000000355e+00 1.800000000000000044e+00 +6.400000000000000355e+00 2.799999999999999822e+00 5.599999999999999645e+00 2.100000000000000089e+00 +7.200000000000000178e+00 3.000000000000000000e+00 5.799999999999999822e+00 1.600000000000000089e+00 +7.400000000000000355e+00 2.799999999999999822e+00 6.099999999999999645e+00 1.899999999999999911e+00 +7.900000000000000355e+00 3.799999999999999822e+00 6.400000000000000355e+00 2.000000000000000000e+00 +6.400000000000000355e+00 2.799999999999999822e+00 5.599999999999999645e+00 2.200000000000000178e+00 +6.299999999999999822e+00 2.799999999999999822e+00 5.099999999999999645e+00 1.500000000000000000e+00 +6.099999999999999645e+00 2.600000000000000089e+00 5.599999999999999645e+00 1.399999999999999911e+00 +7.700000000000000178e+00 3.000000000000000000e+00 6.099999999999999645e+00 2.299999999999999822e+00 +6.299999999999999822e+00 3.399999999999999911e+00 5.599999999999999645e+00 2.399999999999999911e+00 +6.400000000000000355e+00 3.100000000000000089e+00 5.500000000000000000e+00 1.800000000000000044e+00 +6.000000000000000000e+00 3.000000000000000000e+00 4.799999999999999822e+00 1.800000000000000044e+00 +6.900000000000000355e+00 3.100000000000000089e+00 5.400000000000000355e+00 2.100000000000000089e+00 +6.700000000000000178e+00 3.100000000000000089e+00 5.599999999999999645e+00 2.399999999999999911e+00 +6.900000000000000355e+00 3.100000000000000089e+00 5.099999999999999645e+00 2.299999999999999822e+00 +5.799999999999999822e+00 2.700000000000000178e+00 5.099999999999999645e+00 1.899999999999999911e+00 +6.799999999999999822e+00 3.200000000000000178e+00 5.900000000000000355e+00 2.299999999999999822e+00 +6.700000000000000178e+00 3.299999999999999822e+00 5.700000000000000178e+00 2.500000000000000000e+00 +6.700000000000000178e+00 3.000000000000000000e+00 5.200000000000000178e+00 2.299999999999999822e+00 +6.299999999999999822e+00 2.500000000000000000e+00 5.000000000000000000e+00 1.899999999999999911e+00 +6.500000000000000000e+00 3.000000000000000000e+00 5.200000000000000178e+00 2.000000000000000000e+00 +6.200000000000000178e+00 3.399999999999999911e+00 5.400000000000000355e+00 2.299999999999999822e+00 +5.900000000000000355e+00 3.000000000000000000e+00 5.099999999999999645e+00 1.800000000000000044e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-boolean-inp.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-boolean-inp.txt new file mode 100644 index 0000000000000000000000000000000000000000..0636cc9f4590f2e960f7136d83a5d2cb07c56536 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-boolean-inp.txt @@ -0,0 +1,20 @@ +1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 +0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 +1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..0aff1267ca7fd6f41d38c2273540bb771e9cbd0c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt @@ -0,0 +1 @@ + 5.0000000e-01 4.0000000e-01 5.0000000e-01 1.0000000e-01 4.0000000e-01 5.0000000e-01 1.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 8.0000000e-01 7.0000000e-01 9.0000000e-01 4.0000000e-01 1.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 2.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 2.0000000e-01 1.0000000e-01 1.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e-01 1.0000000e-01 1.2000000e+00 7.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 2.0000000e-01 2.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 2.0000000e-01 3.0000000e-01 6.0000000e-01 9.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 1.0000000e-01 7.0000000e-01 4.0000000e-01 1.0000000e-01 6.0000000e-01 1.0000000e+00 1.4000000e+00 9.0000000e-01 5.0000000e-01 8.0000000e-01 8.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 3.0000000e-01 5.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 2.0000000e-01 2.0000000e-01 5.0000000e-01 1.1000000e+00 1.2000000e+00 1.0000000e-01 2.0000000e-01 6.0000000e-01 1.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e-01 8.0000000e-01 3.0000000e-01 7.0000000e-01 3.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 2.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 3.0000000e-01 3.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 1.1000000e+00 1.2000000e+00 7.0000000e-01 4.0000000e-01 1.0000000e+00 6.0000000e-01 7.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 7.0000000e-01 9.0000000e-01 1.0000000e+00 2.0000000e-01 3.0000000e-01 8.0000000e-01 2.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 9.0000000e-01 3.0000000e-01 4.0000000e-01 6.0000000e-01 2.0000000e-01 6.0000000e-01 1.0000000e-01 6.0000000e-01 3.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 5.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 2.0000000e-01 3.0000000e-01 8.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 1.2000000e+00 1.3000000e+00 8.0000000e-01 5.0000000e-01 1.1000000e+00 7.0000000e-01 8.0000000e-01 6.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 1.0000000e-01 2.0000000e-01 8.0000000e-01 1.0000000e+00 1.1000000e+00 3.0000000e-01 4.0000000e-01 9.0000000e-01 3.0000000e-01 2.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 2.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 1.0000000e-01 7.0000000e-01 4.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 4.0000000e-01 4.0000000e-01 2.0000000e-01 7.0000000e-01 5.0000000e-01 4.0000000e-01 2.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 8.0000000e-01 4.0000000e-01 1.0000000e-01 7.0000000e-01 2.0000000e-01 4.0000000e-01 2.0000000e-01 4.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 2.0000000e-01 2.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 2.0000000e-01 1.0000000e-01 1.3000000e+00 6.0000000e-01 4.0000000e-01 5.0000000e-01 6.0000000e-01 2.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 8.0000000e-01 5.0000000e-01 1.0000000e+00 8.0000000e-01 2.0000000e-01 6.0000000e-01 9.0000000e-01 1.1000000e+00 5.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 7.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 4.0000000e-01 1.6000000e+00 1.0000000e+00 4.0000000e-01 3.0000000e-01 9.0000000e-01 3.0000000e-01 8.0000000e-01 2.0000000e-01 6.0000000e-01 3.0000000e+00 2.8000000e+00 3.2000000e+00 2.3000000e+00 2.9000000e+00 2.8000000e+00 3.0000000e+00 1.6000000e+00 2.9000000e+00 2.2000000e+00 1.9000000e+00 2.5000000e+00 2.3000000e+00 3.0000000e+00 1.9000000e+00 2.7000000e+00 2.8000000e+00 2.4000000e+00 2.8000000e+00 2.2000000e+00 3.1000000e+00 2.3000000e+00 3.2000000e+00 3.0000000e+00 2.6000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.8000000e+00 1.8000000e+00 2.1000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.7000000e+00 2.4000000e+00 2.3000000e+00 2.7000000e+00 2.9000000e+00 2.3000000e+00 1.6000000e+00 2.5000000e+00 2.5000000e+00 2.5000000e+00 2.6000000e+00 1.4000000e+00 2.4000000e+00 4.3000000e+00 3.4000000e+00 4.2000000e+00 3.9000000e+00 4.1000000e+00 4.9000000e+00 2.8000000e+00 4.6000000e+00 4.1000000e+00 4.4000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 5.0000000e+00 5.2000000e+00 3.3000000e+00 4.0000000e+00 3.2000000e+00 5.0000000e+00 3.2000000e+00 4.0000000e+00 4.3000000e+00 3.1000000e+00 3.2000000e+00 3.9000000e+00 4.1000000e+00 4.4000000e+00 4.7000000e+00 3.9000000e+00 3.4000000e+00 3.9000000e+00 4.4000000e+00 3.9000000e+00 3.8000000e+00 3.1000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.4000000e+00 4.2000000e+00 4.0000000e+00 3.5000000e+00 3.3000000e+00 3.5000000e+00 3.7000000e+00 3.4000000e+00 4.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 2.0000000e-01 4.0000000e-01 4.0000000e-01 1.2000000e+00 1.1000000e+00 8.0000000e-01 5.0000000e-01 1.1000000e+00 5.0000000e-01 8.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 2.0000000e-01 3.0000000e-01 8.0000000e-01 7.0000000e-01 9.0000000e-01 3.0000000e-01 4.0000000e-01 9.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 1.1000000e+00 2.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 2.0000000e-01 7.0000000e-01 4.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 6.0000000e-01 3.0000000e-01 4.0000000e-01 2.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 1.0000000e-01 7.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 5.0000000e-01 3.0000000e-01 4.0000000e-01 4.0000000e-01 2.0000000e-01 2.0000000e-01 2.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 3.0000000e-01 6.0000000e-01 1.0000000e-01 2.0000000e-01 1.1000000e+00 6.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 1.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 5.0000000e-01 1.0000000e+00 5.0000000e-01 4.0000000e-01 3.0000000e-01 1.4000000e+00 1.5000000e+00 1.0000000e+00 7.0000000e-01 1.3000000e+00 9.0000000e-01 1.0000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 8.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 1.0000000e+00 1.2000000e+00 1.3000000e+00 5.0000000e-01 6.0000000e-01 1.1000000e+00 5.0000000e-01 1.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 6.0000000e-01 9.0000000e-01 4.0000000e-01 9.0000000e-01 3.0000000e-01 9.0000000e-01 6.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 6.0000000e-01 3.0000000e-01 1.0000000e-01 6.0000000e-01 9.0000000e-01 1.3000000e+00 8.0000000e-01 4.0000000e-01 8.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 1.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 1.0000000e-01 5.0000000e-01 1.0000000e+00 1.1000000e+00 0.0000000e+00 3.0000000e-01 6.0000000e-01 0.0000000e+00 5.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 6.0000000e-01 2.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 6.0000000e-01 7.0000000e-01 1.1000000e+00 4.0000000e-01 7.0000000e-01 2.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 2.0000000e-01 3.0000000e-01 7.0000000e-01 6.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 2.0000000e-01 6.0000000e-01 1.0000000e+00 3.0000000e-01 4.0000000e-01 1.4000000e+00 1.0000000e+00 4.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 8.0000000e-01 1.0000000e-01 4.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 4.0000000e-01 5.0000000e-01 1.0000000e+00 1.0000000e+00 6.0000000e-01 3.0000000e-01 9.0000000e-01 4.0000000e-01 6.0000000e-01 3.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 2.0000000e-01 4.0000000e-01 4.0000000e-01 2.0000000e-01 3.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 1.1000000e+00 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 2.0000000e-01 5.0000000e-01 2.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 5.0000000e-01 1.0000000e+00 1.4000000e+00 9.0000000e-01 5.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 2.0000000e-01 2.0000000e-01 6.0000000e-01 1.1000000e+00 1.2000000e+00 1.0000000e-01 2.0000000e-01 7.0000000e-01 1.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 4.0000000e-01 5.0000000e-01 8.0000000e-01 2.0000000e-01 8.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 1.5000000e+00 1.4000000e+00 1.1000000e+00 8.0000000e-01 1.4000000e+00 8.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 8.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 9.0000000e-01 9.0000000e-01 5.0000000e-01 5.0000000e-01 1.1000000e+00 1.1000000e+00 1.2000000e+00 6.0000000e-01 7.0000000e-01 1.2000000e+00 6.0000000e-01 2.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 8.0000000e-01 3.0000000e-01 1.0000000e+00 7.0000000e-01 3.6000000e+00 3.4000000e+00 3.8000000e+00 2.9000000e+00 3.5000000e+00 3.4000000e+00 3.6000000e+00 2.2000000e+00 3.5000000e+00 2.8000000e+00 2.4000000e+00 3.1000000e+00 2.9000000e+00 3.6000000e+00 2.5000000e+00 3.3000000e+00 3.4000000e+00 3.0000000e+00 3.4000000e+00 2.8000000e+00 3.7000000e+00 2.9000000e+00 3.8000000e+00 3.6000000e+00 3.2000000e+00 3.3000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 2.4000000e+00 2.7000000e+00 2.6000000e+00 2.8000000e+00 4.0000000e+00 3.4000000e+00 3.4000000e+00 3.6000000e+00 3.3000000e+00 3.0000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 2.9000000e+00 2.2000000e+00 3.1000000e+00 3.1000000e+00 3.1000000e+00 3.2000000e+00 1.9000000e+00 3.0000000e+00 4.9000000e+00 4.0000000e+00 4.8000000e+00 4.5000000e+00 4.7000000e+00 5.5000000e+00 3.4000000e+00 5.2000000e+00 4.7000000e+00 5.0000000e+00 4.0000000e+00 4.2000000e+00 4.4000000e+00 3.9000000e+00 4.0000000e+00 4.2000000e+00 4.4000000e+00 5.6000000e+00 5.8000000e+00 3.9000000e+00 4.6000000e+00 3.8000000e+00 5.6000000e+00 3.8000000e+00 4.6000000e+00 4.9000000e+00 3.7000000e+00 3.8000000e+00 4.5000000e+00 4.7000000e+00 5.0000000e+00 5.3000000e+00 4.5000000e+00 4.0000000e+00 4.5000000e+00 5.0000000e+00 4.5000000e+00 4.4000000e+00 3.7000000e+00 4.3000000e+00 4.5000000e+00 4.0000000e+00 4.0000000e+00 4.8000000e+00 4.6000000e+00 4.1000000e+00 3.9000000e+00 4.1000000e+00 4.3000000e+00 4.0000000e+00 4.0000000e-01 4.0000000e-01 7.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 7.0000000e-01 1.2000000e+00 7.0000000e-01 1.0000000e+00 1.0000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 1.1000000e+00 1.0000000e+00 6.0000000e-01 6.0000000e-01 3.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 9.0000000e-01 1.4000000e+00 7.0000000e-01 8.0000000e-01 1.7000000e+00 1.4000000e+00 8.0000000e-01 7.0000000e-01 1.0000000e+00 7.0000000e-01 1.2000000e+00 5.0000000e-01 8.0000000e-01 3.5000000e+00 3.3000000e+00 3.7000000e+00 2.8000000e+00 3.4000000e+00 3.3000000e+00 3.5000000e+00 2.1000000e+00 3.4000000e+00 2.7000000e+00 2.3000000e+00 3.0000000e+00 2.8000000e+00 3.5000000e+00 2.4000000e+00 3.2000000e+00 3.3000000e+00 2.9000000e+00 3.3000000e+00 2.7000000e+00 3.6000000e+00 2.8000000e+00 3.7000000e+00 3.5000000e+00 3.1000000e+00 3.2000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 2.3000000e+00 2.6000000e+00 2.5000000e+00 2.7000000e+00 3.9000000e+00 3.3000000e+00 3.3000000e+00 3.5000000e+00 3.2000000e+00 2.9000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.8000000e+00 2.1000000e+00 3.0000000e+00 3.0000000e+00 3.0000000e+00 3.1000000e+00 1.8000000e+00 2.9000000e+00 4.8000000e+00 3.9000000e+00 4.7000000e+00 4.4000000e+00 4.6000000e+00 5.4000000e+00 3.3000000e+00 5.1000000e+00 4.6000000e+00 4.9000000e+00 3.9000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.9000000e+00 4.1000000e+00 4.3000000e+00 5.5000000e+00 5.7000000e+00 3.8000000e+00 4.5000000e+00 3.7000000e+00 5.5000000e+00 3.7000000e+00 4.5000000e+00 4.8000000e+00 3.6000000e+00 3.7000000e+00 4.4000000e+00 4.6000000e+00 4.9000000e+00 5.2000000e+00 4.4000000e+00 3.9000000e+00 4.4000000e+00 4.9000000e+00 4.4000000e+00 4.3000000e+00 3.6000000e+00 4.2000000e+00 4.4000000e+00 3.9000000e+00 3.9000000e+00 4.7000000e+00 4.5000000e+00 4.0000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.9000000e+00 5.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 1.0000000e+00 7.0000000e-01 1.1000000e+00 1.1000000e+00 1.0000000e+00 1.4000000e+00 1.0000000e+00 9.0000000e-01 1.0000000e+00 1.2000000e+00 1.3000000e+00 1.0000000e+00 5.0000000e-01 2.0000000e-01 1.3000000e+00 1.2000000e+00 9.0000000e-01 1.3000000e+00 1.4000000e+00 1.0000000e+00 9.0000000e-01 2.1000000e+00 1.3000000e+00 9.0000000e-01 6.0000000e-01 1.4000000e+00 6.0000000e-01 1.2000000e+00 7.0000000e-01 1.1000000e+00 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 2.0000000e+00 3.1000000e+00 2.4000000e+00 2.4000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 2.1000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.9000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 4.0000000e-01 4.0000000e-01 3.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 7.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 4.0000000e-01 1.6000000e+00 1.0000000e+00 4.0000000e-01 6.0000000e-01 9.0000000e-01 3.0000000e-01 8.0000000e-01 2.0000000e-01 6.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 6.0000000e-01 3.0000000e-01 3.0000000e-01 2.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 2.0000000e-01 1.0000000e-01 1.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e-01 1.0000000e-01 1.2000000e+00 7.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 2.0000000e-01 2.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 6.0000000e-01 4.0000000e-01 6.0000000e-01 1.1000000e+00 6.0000000e-01 9.0000000e-01 8.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 1.0000000e+00 9.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 7.0000000e-01 4.0000000e-01 8.0000000e-01 1.3000000e+00 6.0000000e-01 7.0000000e-01 1.5000000e+00 1.3000000e+00 7.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 1.1000000e+00 4.0000000e-01 7.0000000e-01 3.0000000e+00 2.8000000e+00 3.2000000e+00 2.3000000e+00 2.9000000e+00 2.8000000e+00 3.0000000e+00 1.6000000e+00 2.9000000e+00 2.2000000e+00 1.8000000e+00 2.5000000e+00 2.3000000e+00 3.0000000e+00 1.9000000e+00 2.7000000e+00 2.8000000e+00 2.4000000e+00 2.8000000e+00 2.2000000e+00 3.1000000e+00 2.3000000e+00 3.2000000e+00 3.0000000e+00 2.6000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.8000000e+00 1.8000000e+00 2.1000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.7000000e+00 2.4000000e+00 2.3000000e+00 2.7000000e+00 2.9000000e+00 2.3000000e+00 1.6000000e+00 2.5000000e+00 2.5000000e+00 2.5000000e+00 2.6000000e+00 1.3000000e+00 2.4000000e+00 4.3000000e+00 3.4000000e+00 4.2000000e+00 3.9000000e+00 4.1000000e+00 4.9000000e+00 2.8000000e+00 4.6000000e+00 4.1000000e+00 4.4000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 5.0000000e+00 5.2000000e+00 3.3000000e+00 4.0000000e+00 3.2000000e+00 5.0000000e+00 3.2000000e+00 4.0000000e+00 4.3000000e+00 3.1000000e+00 3.2000000e+00 3.9000000e+00 4.1000000e+00 4.4000000e+00 4.7000000e+00 3.9000000e+00 3.4000000e+00 3.9000000e+00 4.4000000e+00 3.9000000e+00 3.8000000e+00 3.1000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.4000000e+00 4.2000000e+00 4.0000000e+00 3.5000000e+00 3.3000000e+00 3.5000000e+00 3.7000000e+00 3.4000000e+00 4.0000000e-01 1.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 7.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 4.0000000e-01 3.0000000e-01 1.5000000e+00 7.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e-01 6.0000000e-01 2.0000000e-01 5.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 3.0000000e-01 8.0000000e-01 3.0000000e-01 6.0000000e-01 4.0000000e-01 4.0000000e-01 2.0000000e-01 3.0000000e-01 7.0000000e-01 6.0000000e-01 2.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 1.0000000e+00 3.0000000e-01 4.0000000e-01 1.1000000e+00 1.0000000e+00 4.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e+00 2.8000000e+00 3.2000000e+00 2.3000000e+00 2.9000000e+00 2.8000000e+00 3.0000000e+00 1.6000000e+00 2.9000000e+00 2.2000000e+00 1.8000000e+00 2.5000000e+00 2.3000000e+00 3.0000000e+00 1.9000000e+00 2.7000000e+00 2.8000000e+00 2.4000000e+00 2.8000000e+00 2.2000000e+00 3.1000000e+00 2.3000000e+00 3.2000000e+00 3.0000000e+00 2.6000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.8000000e+00 1.8000000e+00 2.1000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.7000000e+00 2.4000000e+00 2.3000000e+00 2.7000000e+00 2.9000000e+00 2.3000000e+00 1.6000000e+00 2.5000000e+00 2.5000000e+00 2.5000000e+00 2.6000000e+00 1.3000000e+00 2.4000000e+00 4.3000000e+00 3.4000000e+00 4.2000000e+00 3.9000000e+00 4.1000000e+00 4.9000000e+00 2.8000000e+00 4.6000000e+00 4.1000000e+00 4.4000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 5.0000000e+00 5.2000000e+00 3.3000000e+00 4.0000000e+00 3.2000000e+00 5.0000000e+00 3.2000000e+00 4.0000000e+00 4.3000000e+00 3.1000000e+00 3.2000000e+00 3.9000000e+00 4.1000000e+00 4.4000000e+00 4.7000000e+00 3.9000000e+00 3.4000000e+00 3.9000000e+00 4.4000000e+00 3.9000000e+00 3.8000000e+00 3.1000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.4000000e+00 4.2000000e+00 4.0000000e+00 3.5000000e+00 3.3000000e+00 3.5000000e+00 3.7000000e+00 3.4000000e+00 5.0000000e-01 4.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 2.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 3.0000000e-01 2.0000000e-01 1.4000000e+00 7.0000000e-01 2.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 5.0000000e-01 2.0000000e-01 4.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 7.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 9.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 4.0000000e-01 1.3000000e+00 4.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 4.0000000e-01 3.7000000e+00 3.5000000e+00 3.9000000e+00 3.0000000e+00 3.6000000e+00 3.5000000e+00 3.7000000e+00 2.3000000e+00 3.6000000e+00 2.9000000e+00 2.5000000e+00 3.2000000e+00 3.0000000e+00 3.7000000e+00 2.6000000e+00 3.4000000e+00 3.5000000e+00 3.1000000e+00 3.5000000e+00 2.9000000e+00 3.8000000e+00 3.0000000e+00 3.9000000e+00 3.7000000e+00 3.3000000e+00 3.4000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 2.5000000e+00 2.8000000e+00 2.7000000e+00 2.9000000e+00 4.1000000e+00 3.5000000e+00 3.5000000e+00 3.7000000e+00 3.4000000e+00 3.1000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.0000000e+00 2.3000000e+00 3.2000000e+00 3.2000000e+00 3.2000000e+00 3.3000000e+00 2.0000000e+00 3.1000000e+00 5.0000000e+00 4.1000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.6000000e+00 3.5000000e+00 5.3000000e+00 4.8000000e+00 5.1000000e+00 4.1000000e+00 4.3000000e+00 4.5000000e+00 4.0000000e+00 4.1000000e+00 4.3000000e+00 4.5000000e+00 5.7000000e+00 5.9000000e+00 4.0000000e+00 4.7000000e+00 3.9000000e+00 5.7000000e+00 3.9000000e+00 4.7000000e+00 5.0000000e+00 3.8000000e+00 3.9000000e+00 4.6000000e+00 4.8000000e+00 5.1000000e+00 5.4000000e+00 4.6000000e+00 4.1000000e+00 4.6000000e+00 5.1000000e+00 4.6000000e+00 4.5000000e+00 3.8000000e+00 4.4000000e+00 4.6000000e+00 4.1000000e+00 4.1000000e+00 4.9000000e+00 4.7000000e+00 4.2000000e+00 4.0000000e+00 4.2000000e+00 4.4000000e+00 4.1000000e+00 3.0000000e-01 3.0000000e-01 1.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 9.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 4.0000000e-01 1.0000000e+00 7.0000000e-01 2.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e+00 2.8000000e+00 3.2000000e+00 2.3000000e+00 2.9000000e+00 2.8000000e+00 3.0000000e+00 1.6000000e+00 2.9000000e+00 2.2000000e+00 1.8000000e+00 2.5000000e+00 2.3000000e+00 3.0000000e+00 1.9000000e+00 2.7000000e+00 2.8000000e+00 2.4000000e+00 2.8000000e+00 2.2000000e+00 3.1000000e+00 2.3000000e+00 3.2000000e+00 3.0000000e+00 2.6000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.8000000e+00 1.8000000e+00 2.1000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.7000000e+00 2.4000000e+00 2.3000000e+00 2.7000000e+00 2.9000000e+00 2.3000000e+00 1.6000000e+00 2.5000000e+00 2.5000000e+00 2.5000000e+00 2.6000000e+00 1.3000000e+00 2.4000000e+00 4.3000000e+00 3.4000000e+00 4.2000000e+00 3.9000000e+00 4.1000000e+00 4.9000000e+00 2.8000000e+00 4.6000000e+00 4.1000000e+00 4.4000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 5.0000000e+00 5.2000000e+00 3.3000000e+00 4.0000000e+00 3.2000000e+00 5.0000000e+00 3.2000000e+00 4.0000000e+00 4.3000000e+00 3.1000000e+00 3.2000000e+00 3.9000000e+00 4.1000000e+00 4.4000000e+00 4.7000000e+00 3.9000000e+00 3.4000000e+00 3.9000000e+00 4.4000000e+00 3.9000000e+00 3.8000000e+00 3.1000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.4000000e+00 4.2000000e+00 4.0000000e+00 3.5000000e+00 3.3000000e+00 3.5000000e+00 3.7000000e+00 3.4000000e+00 4.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 4.0000000e-01 7.0000000e-01 7.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 6.0000000e-01 1.1000000e+00 6.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 2.8000000e+00 2.6000000e+00 3.0000000e+00 2.1000000e+00 2.7000000e+00 2.6000000e+00 2.8000000e+00 1.4000000e+00 2.7000000e+00 2.0000000e+00 1.6000000e+00 2.3000000e+00 2.1000000e+00 2.8000000e+00 1.7000000e+00 2.5000000e+00 2.6000000e+00 2.2000000e+00 2.6000000e+00 2.0000000e+00 2.9000000e+00 2.1000000e+00 3.0000000e+00 2.8000000e+00 2.4000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.6000000e+00 1.6000000e+00 1.9000000e+00 1.8000000e+00 2.0000000e+00 3.2000000e+00 2.6000000e+00 2.6000000e+00 2.8000000e+00 2.5000000e+00 2.2000000e+00 2.1000000e+00 2.5000000e+00 2.7000000e+00 2.1000000e+00 1.4000000e+00 2.3000000e+00 2.3000000e+00 2.3000000e+00 2.4000000e+00 1.1000000e+00 2.2000000e+00 4.1000000e+00 3.2000000e+00 4.0000000e+00 3.7000000e+00 3.9000000e+00 4.7000000e+00 2.6000000e+00 4.4000000e+00 3.9000000e+00 4.2000000e+00 3.2000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 3.2000000e+00 3.4000000e+00 3.6000000e+00 4.8000000e+00 5.0000000e+00 3.1000000e+00 3.8000000e+00 3.0000000e+00 4.8000000e+00 3.0000000e+00 3.8000000e+00 4.1000000e+00 2.9000000e+00 3.0000000e+00 3.7000000e+00 3.9000000e+00 4.2000000e+00 4.5000000e+00 3.7000000e+00 3.2000000e+00 3.7000000e+00 4.2000000e+00 3.7000000e+00 3.6000000e+00 2.9000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 3.2000000e+00 4.0000000e+00 3.8000000e+00 3.3000000e+00 3.1000000e+00 3.3000000e+00 3.5000000e+00 3.2000000e+00 4.0000000e-01 5.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 1.1000000e+00 1.2000000e+00 1.0000000e-01 4.0000000e-01 5.0000000e-01 1.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 2.0000000e-01 8.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 2.0000000e-01 2.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 3.0000000e-01 6.0000000e-01 2.0000000e-01 3.0000000e-01 1.1000000e+00 6.0000000e-01 2.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 1.0000000e-01 5.0000000e-01 4.0000000e-01 2.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e-01 2.0000000e-01 1.2000000e+00 8.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 3.0000000e-01 6.0000000e-01 2.0000000e-01 2.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 5.0000000e-01 4.0000000e-01 2.0000000e-01 7.0000000e-01 8.0000000e-01 3.0000000e-01 2.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 1.0000000e-01 2.0000000e-01 1.1000000e+00 8.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 6.0000000e-01 3.0000000e-01 2.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 1.0000000e-01 7.0000000e-01 9.0000000e-01 1.0000000e+00 2.0000000e-01 4.0000000e-01 8.0000000e-01 2.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 9.0000000e-01 3.0000000e-01 4.0000000e-01 6.0000000e-01 2.0000000e-01 6.0000000e-01 2.0000000e-01 6.0000000e-01 3.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 6.0000000e-01 1.0000000e+00 1.1000000e+00 1.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 4.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 2.0000000e-01 6.0000000e-01 2.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 7.0000000e-01 8.0000000e-01 5.0000000e-01 4.0000000e-01 2.0000000e-01 5.0000000e-01 1.0000000e+00 3.0000000e-01 4.0000000e-01 1.1000000e+00 1.0000000e+00 4.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 8.0000000e-01 3.0000000e-01 4.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 3.0000000e-01 1.0000000e+00 9.0000000e-01 6.0000000e-01 1.0000000e+00 1.1000000e+00 7.0000000e-01 6.0000000e-01 1.8000000e+00 9.0000000e-01 6.0000000e-01 4.0000000e-01 1.1000000e+00 3.0000000e-01 9.0000000e-01 4.0000000e-01 8.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.1000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.6000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 1.1000000e+00 1.0000000e+00 7.0000000e-01 1.1000000e+00 1.2000000e+00 8.0000000e-01 7.0000000e-01 1.9000000e+00 1.1000000e+00 7.0000000e-01 5.0000000e-01 1.2000000e+00 4.0000000e-01 1.0000000e+00 5.0000000e-01 9.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.2000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.7000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 3.0000000e-01 6.0000000e-01 0.0000000e+00 5.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 6.0000000e-01 2.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 5.0000000e-01 3.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 9.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 2.0000000e-01 3.5000000e+00 3.3000000e+00 3.7000000e+00 2.8000000e+00 3.4000000e+00 3.3000000e+00 3.5000000e+00 2.1000000e+00 3.4000000e+00 2.7000000e+00 2.3000000e+00 3.0000000e+00 2.8000000e+00 3.5000000e+00 2.4000000e+00 3.2000000e+00 3.3000000e+00 2.9000000e+00 3.3000000e+00 2.7000000e+00 3.6000000e+00 2.8000000e+00 3.7000000e+00 3.5000000e+00 3.1000000e+00 3.2000000e+00 3.6000000e+00 3.8000000e+00 3.3000000e+00 2.3000000e+00 2.6000000e+00 2.5000000e+00 2.7000000e+00 3.9000000e+00 3.3000000e+00 3.3000000e+00 3.5000000e+00 3.2000000e+00 2.9000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.8000000e+00 2.1000000e+00 3.0000000e+00 3.0000000e+00 3.0000000e+00 3.1000000e+00 1.8000000e+00 2.9000000e+00 4.8000000e+00 3.9000000e+00 4.7000000e+00 4.4000000e+00 4.6000000e+00 5.4000000e+00 3.3000000e+00 5.1000000e+00 4.6000000e+00 4.9000000e+00 3.9000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.9000000e+00 4.1000000e+00 4.3000000e+00 5.5000000e+00 5.7000000e+00 3.8000000e+00 4.5000000e+00 3.7000000e+00 5.5000000e+00 3.7000000e+00 4.5000000e+00 4.8000000e+00 3.6000000e+00 3.7000000e+00 4.4000000e+00 4.6000000e+00 4.9000000e+00 5.2000000e+00 4.4000000e+00 3.9000000e+00 4.4000000e+00 4.9000000e+00 4.4000000e+00 4.3000000e+00 3.6000000e+00 4.2000000e+00 4.4000000e+00 3.9000000e+00 3.9000000e+00 4.7000000e+00 4.5000000e+00 4.0000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.9000000e+00 6.0000000e-01 1.1000000e+00 4.0000000e-01 5.0000000e-01 1.2000000e+00 1.1000000e+00 5.0000000e-01 6.0000000e-01 7.0000000e-01 4.0000000e-01 9.0000000e-01 2.0000000e-01 5.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 5.0000000e-01 3.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 6.0000000e-01 2.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 7.0000000e-01 6.0000000e-01 7.0000000e-01 2.0000000e-01 6.0000000e-01 8.0000000e-01 4.0000000e-01 8.0000000e-01 2.0000000e-01 9.0000000e-01 6.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 2.0000000e-01 1.1000000e+00 7.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 3.0000000e-01 1.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 1.2000000e+00 6.0000000e-01 3.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 9.0000000e-01 1.2000000e+00 1.5000000e+00 7.0000000e-01 1.5000000e+00 9.0000000e-01 1.4000000e+00 1.0000000e+00 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 6.0000000e-01 7.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 9.0000000e-01 6.0000000e-01 3.4000000e+00 3.2000000e+00 3.6000000e+00 2.7000000e+00 3.3000000e+00 3.2000000e+00 3.4000000e+00 2.0000000e+00 3.3000000e+00 2.6000000e+00 2.2000000e+00 2.9000000e+00 2.7000000e+00 3.4000000e+00 2.3000000e+00 3.1000000e+00 3.2000000e+00 2.8000000e+00 3.2000000e+00 2.6000000e+00 3.5000000e+00 2.7000000e+00 3.6000000e+00 3.4000000e+00 3.0000000e+00 3.1000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 2.2000000e+00 2.5000000e+00 2.4000000e+00 2.6000000e+00 3.8000000e+00 3.2000000e+00 3.2000000e+00 3.4000000e+00 3.1000000e+00 2.8000000e+00 2.7000000e+00 3.1000000e+00 3.3000000e+00 2.7000000e+00 2.0000000e+00 2.9000000e+00 2.9000000e+00 2.9000000e+00 3.0000000e+00 1.7000000e+00 2.8000000e+00 4.7000000e+00 3.8000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.3000000e+00 3.2000000e+00 5.0000000e+00 4.5000000e+00 4.8000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.8000000e+00 4.0000000e+00 4.2000000e+00 5.4000000e+00 5.6000000e+00 3.7000000e+00 4.4000000e+00 3.6000000e+00 5.4000000e+00 3.6000000e+00 4.4000000e+00 4.7000000e+00 3.5000000e+00 3.6000000e+00 4.3000000e+00 4.5000000e+00 4.8000000e+00 5.1000000e+00 4.3000000e+00 3.8000000e+00 4.3000000e+00 4.8000000e+00 4.3000000e+00 4.2000000e+00 3.5000000e+00 4.1000000e+00 4.3000000e+00 3.8000000e+00 3.8000000e+00 4.6000000e+00 4.4000000e+00 3.9000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.8000000e+00 3.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 8.0000000e-01 3.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 2.8000000e+00 2.6000000e+00 3.0000000e+00 2.1000000e+00 2.7000000e+00 2.6000000e+00 2.8000000e+00 1.4000000e+00 2.7000000e+00 2.0000000e+00 1.8000000e+00 2.3000000e+00 2.1000000e+00 2.8000000e+00 1.7000000e+00 2.5000000e+00 2.6000000e+00 2.2000000e+00 2.6000000e+00 2.0000000e+00 2.9000000e+00 2.1000000e+00 3.0000000e+00 2.8000000e+00 2.4000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.6000000e+00 1.6000000e+00 1.9000000e+00 1.8000000e+00 2.0000000e+00 3.2000000e+00 2.6000000e+00 2.6000000e+00 2.8000000e+00 2.5000000e+00 2.2000000e+00 2.1000000e+00 2.5000000e+00 2.7000000e+00 2.1000000e+00 1.5000000e+00 2.3000000e+00 2.3000000e+00 2.3000000e+00 2.4000000e+00 1.3000000e+00 2.2000000e+00 4.1000000e+00 3.2000000e+00 4.0000000e+00 3.7000000e+00 3.9000000e+00 4.7000000e+00 2.6000000e+00 4.4000000e+00 3.9000000e+00 4.2000000e+00 3.2000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 3.2000000e+00 3.4000000e+00 3.6000000e+00 4.8000000e+00 5.0000000e+00 3.1000000e+00 3.8000000e+00 3.0000000e+00 4.8000000e+00 3.0000000e+00 3.8000000e+00 4.1000000e+00 2.9000000e+00 3.0000000e+00 3.7000000e+00 3.9000000e+00 4.2000000e+00 4.5000000e+00 3.7000000e+00 3.2000000e+00 3.7000000e+00 4.2000000e+00 3.7000000e+00 3.6000000e+00 2.9000000e+00 3.5000000e+00 3.7000000e+00 3.2000000e+00 3.2000000e+00 4.0000000e+00 3.8000000e+00 3.3000000e+00 3.1000000e+00 3.3000000e+00 3.5000000e+00 3.2000000e+00 8.0000000e-01 2.0000000e-01 7.0000000e-01 3.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 6.0000000e-01 2.0000000e-01 5.0000000e-01 3.1000000e+00 2.9000000e+00 3.3000000e+00 2.4000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 1.7000000e+00 3.0000000e+00 2.3000000e+00 1.9000000e+00 2.6000000e+00 2.4000000e+00 3.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 2.9000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 3.3000000e+00 3.1000000e+00 2.7000000e+00 2.8000000e+00 3.2000000e+00 3.4000000e+00 2.9000000e+00 1.9000000e+00 2.2000000e+00 2.1000000e+00 2.3000000e+00 3.5000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 2.8000000e+00 3.0000000e+00 2.4000000e+00 1.7000000e+00 2.6000000e+00 2.6000000e+00 2.6000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 4.4000000e+00 3.5000000e+00 4.3000000e+00 4.0000000e+00 4.2000000e+00 5.0000000e+00 2.9000000e+00 4.7000000e+00 4.2000000e+00 4.5000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.4000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 5.1000000e+00 5.3000000e+00 3.4000000e+00 4.1000000e+00 3.3000000e+00 5.1000000e+00 3.3000000e+00 4.1000000e+00 4.4000000e+00 3.2000000e+00 3.3000000e+00 4.0000000e+00 4.2000000e+00 4.5000000e+00 4.8000000e+00 4.0000000e+00 3.5000000e+00 4.0000000e+00 4.5000000e+00 4.0000000e+00 3.9000000e+00 3.2000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.5000000e+00 4.3000000e+00 4.1000000e+00 3.6000000e+00 3.4000000e+00 3.6000000e+00 3.8000000e+00 3.5000000e+00 7.0000000e-01 4.0000000e-01 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 4.0000000e-01 3.2000000e+00 3.0000000e+00 3.4000000e+00 2.5000000e+00 3.1000000e+00 3.0000000e+00 3.2000000e+00 1.8000000e+00 3.1000000e+00 2.4000000e+00 2.0000000e+00 2.7000000e+00 2.5000000e+00 3.2000000e+00 2.1000000e+00 2.9000000e+00 3.0000000e+00 2.6000000e+00 3.0000000e+00 2.4000000e+00 3.3000000e+00 2.5000000e+00 3.4000000e+00 3.2000000e+00 2.8000000e+00 2.9000000e+00 3.3000000e+00 3.5000000e+00 3.0000000e+00 2.0000000e+00 2.3000000e+00 2.2000000e+00 2.4000000e+00 3.6000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.9000000e+00 2.6000000e+00 2.5000000e+00 2.9000000e+00 3.1000000e+00 2.5000000e+00 1.8000000e+00 2.7000000e+00 2.7000000e+00 2.7000000e+00 2.8000000e+00 1.5000000e+00 2.6000000e+00 4.5000000e+00 3.6000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 5.1000000e+00 3.0000000e+00 4.8000000e+00 4.3000000e+00 4.6000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.5000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 5.2000000e+00 5.4000000e+00 3.5000000e+00 4.2000000e+00 3.4000000e+00 5.2000000e+00 3.4000000e+00 4.2000000e+00 4.5000000e+00 3.3000000e+00 3.4000000e+00 4.1000000e+00 4.3000000e+00 4.6000000e+00 4.9000000e+00 4.1000000e+00 3.6000000e+00 4.1000000e+00 4.6000000e+00 4.1000000e+00 4.0000000e+00 3.3000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.6000000e+00 4.4000000e+00 4.2000000e+00 3.7000000e+00 3.5000000e+00 3.7000000e+00 3.9000000e+00 3.6000000e+00 3.3000000e+00 3.1000000e+00 3.5000000e+00 2.6000000e+00 3.2000000e+00 3.1000000e+00 3.3000000e+00 1.9000000e+00 3.2000000e+00 2.5000000e+00 2.1000000e+00 2.8000000e+00 2.6000000e+00 3.3000000e+00 2.2000000e+00 3.0000000e+00 3.1000000e+00 2.7000000e+00 3.1000000e+00 2.5000000e+00 3.4000000e+00 2.6000000e+00 3.5000000e+00 3.3000000e+00 2.9000000e+00 3.0000000e+00 3.4000000e+00 3.6000000e+00 3.1000000e+00 2.1000000e+00 2.4000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 3.0000000e+00 3.2000000e+00 2.6000000e+00 1.9000000e+00 2.8000000e+00 2.8000000e+00 2.8000000e+00 2.9000000e+00 1.6000000e+00 2.7000000e+00 4.6000000e+00 3.7000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.2000000e+00 3.1000000e+00 4.9000000e+00 4.4000000e+00 4.7000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 3.6000000e+00 3.7000000e+00 3.9000000e+00 4.1000000e+00 5.3000000e+00 5.5000000e+00 3.6000000e+00 4.3000000e+00 3.5000000e+00 5.3000000e+00 3.5000000e+00 4.3000000e+00 4.6000000e+00 3.4000000e+00 3.5000000e+00 4.2000000e+00 4.4000000e+00 4.7000000e+00 5.0000000e+00 4.2000000e+00 3.7000000e+00 4.2000000e+00 4.7000000e+00 4.2000000e+00 4.1000000e+00 3.4000000e+00 4.0000000e+00 4.2000000e+00 3.7000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.0000000e+00 3.7000000e+00 6.0000000e-01 2.0000000e-01 1.5000000e+00 5.0000000e-01 1.3000000e+00 7.0000000e-01 2.1000000e+00 4.0000000e-01 1.8000000e+00 2.0000000e+00 1.1000000e+00 1.0000000e+00 9.0000000e-01 1.4000000e+00 3.0000000e-01 1.4000000e+00 1.2000000e+00 1.0000000e+00 1.4000000e+00 1.1000000e+00 9.0000000e-01 7.0000000e-01 9.0000000e-01 6.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 1.0000000e+00 1.3000000e+00 1.5000000e+00 1.5000000e+00 1.2000000e+00 1.0000000e+00 1.6000000e+00 1.0000000e+00 3.0000000e-01 9.0000000e-01 1.4000000e+00 1.5000000e+00 1.5000000e+00 9.0000000e-01 1.2000000e+00 2.0000000e+00 1.4000000e+00 1.3000000e+00 1.3000000e+00 8.0000000e-01 1.9000000e+00 1.3000000e+00 1.3000000e+00 1.2000000e+00 1.2000000e+00 9.0000000e-01 1.1000000e+00 1.9000000e+00 2.1000000e+00 1.6000000e+00 1.1000000e+00 1.4000000e+00 6.0000000e-01 6.0000000e-01 8.0000000e-01 1.3000000e+00 1.2000000e+00 9.0000000e-01 8.0000000e-01 2.0000000e+00 2.2000000e+00 1.0000000e+00 1.0000000e+00 1.4000000e+00 2.0000000e+00 7.0000000e-01 1.0000000e+00 1.3000000e+00 8.0000000e-01 9.0000000e-01 9.0000000e-01 1.1000000e+00 1.4000000e+00 1.7000000e+00 9.0000000e-01 7.0000000e-01 9.0000000e-01 1.4000000e+00 1.0000000e+00 8.0000000e-01 1.0000000e+00 7.0000000e-01 1.0000000e+00 9.0000000e-01 1.2000000e+00 1.2000000e+00 1.1000000e+00 9.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 1.1000000e+00 5.0000000e-01 9.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 1.5000000e+00 3.0000000e-01 1.2000000e+00 1.4000000e+00 5.0000000e-01 1.0000000e+00 3.0000000e-01 9.0000000e-01 3.0000000e-01 8.0000000e-01 6.0000000e-01 1.0000000e+00 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 1.0000000e+00 9.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 1.0000000e+00 4.0000000e-01 3.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 3.0000000e-01 6.0000000e-01 1.4000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 3.0000000e-01 1.5000000e+00 7.0000000e-01 1.5000000e+00 6.0000000e-01 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 1.5000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 6.0000000e-01 8.0000000e-01 1.0000000e+00 7.0000000e-01 9.0000000e-01 8.0000000e-01 1.0000000e+00 2.2000000e+00 2.4000000e+00 1.0000000e+00 1.2000000e+00 8.0000000e-01 2.2000000e+00 5.0000000e-01 1.2000000e+00 1.5000000e+00 4.0000000e-01 4.0000000e-01 1.1000000e+00 1.3000000e+00 1.6000000e+00 1.9000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.6000000e+00 1.1000000e+00 1.0000000e+00 4.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 1.4000000e+00 1.2000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 9.0000000e-01 6.0000000e-01 1.4000000e+00 4.0000000e-01 1.2000000e+00 6.0000000e-01 2.0000000e+00 3.0000000e-01 1.7000000e+00 1.9000000e+00 1.0000000e+00 9.0000000e-01 8.0000000e-01 1.3000000e+00 5.0000000e-01 1.3000000e+00 1.1000000e+00 9.0000000e-01 1.3000000e+00 1.0000000e+00 9.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 2.0000000e-01 9.0000000e-01 1.4000000e+00 1.4000000e+00 1.4000000e+00 1.1000000e+00 9.0000000e-01 1.5000000e+00 9.0000000e-01 2.0000000e-01 8.0000000e-01 1.3000000e+00 1.4000000e+00 1.4000000e+00 8.0000000e-01 1.1000000e+00 1.9000000e+00 1.3000000e+00 1.2000000e+00 1.2000000e+00 7.0000000e-01 1.9000000e+00 1.2000000e+00 1.1000000e+00 1.1000000e+00 1.0000000e+00 7.0000000e-01 9.0000000e-01 1.7000000e+00 2.0000000e+00 1.4000000e+00 9.0000000e-01 1.2000000e+00 5.0000000e-01 5.0000000e-01 6.0000000e-01 1.2000000e+00 1.1000000e+00 8.0000000e-01 6.0000000e-01 1.8000000e+00 2.0000000e+00 9.0000000e-01 8.0000000e-01 1.3000000e+00 1.8000000e+00 6.0000000e-01 8.0000000e-01 1.1000000e+00 7.0000000e-01 8.0000000e-01 7.0000000e-01 9.0000000e-01 1.2000000e+00 1.5000000e+00 7.0000000e-01 6.0000000e-01 8.0000000e-01 1.2000000e+00 9.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 9.0000000e-01 8.0000000e-01 1.1000000e+00 1.0000000e+00 1.0000000e+00 8.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 1.0000000e+00 5.0000000e-01 1.0000000e+00 7.0000000e-01 1.1000000e+00 4.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 1.2000000e+00 7.0000000e-01 4.0000000e-01 7.0000000e-01 2.0000000e-01 9.0000000e-01 6.0000000e-01 9.0000000e-01 7.0000000e-01 9.0000000e-01 1.1000000e+00 1.3000000e+00 1.2000000e+00 6.0000000e-01 5.0000000e-01 2.0000000e-01 3.0000000e-01 4.0000000e-01 1.1000000e+00 7.0000000e-01 1.1000000e+00 1.2000000e+00 8.0000000e-01 7.0000000e-01 2.0000000e-01 4.0000000e-01 7.0000000e-01 3.0000000e-01 7.0000000e-01 4.0000000e-01 7.0000000e-01 6.0000000e-01 7.0000000e-01 1.0000000e+00 5.0000000e-01 2.0000000e+00 1.1000000e+00 1.9000000e+00 1.6000000e+00 1.8000000e+00 2.6000000e+00 6.0000000e-01 2.3000000e+00 1.8000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.0000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 2.7000000e+00 2.9000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 2.7000000e+00 9.0000000e-01 1.7000000e+00 2.0000000e+00 8.0000000e-01 9.0000000e-01 1.6000000e+00 1.8000000e+00 2.1000000e+00 2.4000000e+00 1.6000000e+00 1.1000000e+00 1.6000000e+00 2.2000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 1.4000000e+00 1.1000000e+00 1.9000000e+00 1.7000000e+00 1.2000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.1000000e+00 8.0000000e-01 5.0000000e-01 1.6000000e+00 2.0000000e-01 1.3000000e+00 1.5000000e+00 6.0000000e-01 6.0000000e-01 4.0000000e-01 1.0000000e+00 3.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 4.0000000e-01 3.0000000e-01 2.0000000e-01 3.0000000e-01 4.0000000e-01 5.0000000e-01 1.1000000e+00 1.0000000e+00 1.0000000e+00 7.0000000e-01 5.0000000e-01 1.1000000e+00 6.0000000e-01 3.0000000e-01 5.0000000e-01 9.0000000e-01 1.0000000e+00 1.0000000e+00 4.0000000e-01 7.0000000e-01 1.5000000e+00 9.0000000e-01 8.0000000e-01 8.0000000e-01 3.0000000e-01 1.6000000e+00 8.0000000e-01 1.4000000e+00 7.0000000e-01 1.3000000e+00 1.0000000e+00 1.2000000e+00 2.0000000e+00 1.6000000e+00 1.7000000e+00 1.2000000e+00 1.5000000e+00 5.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 2.1000000e+00 2.3000000e+00 6.0000000e-01 1.1000000e+00 9.0000000e-01 2.1000000e+00 3.0000000e-01 1.1000000e+00 1.4000000e+00 3.0000000e-01 4.0000000e-01 1.0000000e+00 1.2000000e+00 1.5000000e+00 1.8000000e+00 1.0000000e+00 5.0000000e-01 1.0000000e+00 1.5000000e+00 1.0000000e+00 9.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 8.0000000e-01 7.0000000e-01 1.3000000e+00 1.1000000e+00 8.0000000e-01 4.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 1.2000000e+00 9.0000000e-01 6.0000000e-01 1.0000000e+00 3.0000000e-01 6.0000000e-01 4.0000000e-01 9.0000000e-01 1.0000000e+00 2.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 9.0000000e-01 1.1000000e+00 1.0000000e+00 3.0000000e-01 1.0000000e+00 7.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 6.0000000e-01 1.0000000e+00 6.0000000e-01 4.0000000e-01 5.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 1.2000000e+00 3.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 1.5000000e+00 4.0000000e-01 1.5000000e+00 6.0000000e-01 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 8.0000000e-01 1.8000000e+00 1.3000000e+00 1.6000000e+00 8.0000000e-01 8.0000000e-01 1.1000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 1.0000000e+00 2.2000000e+00 2.4000000e+00 6.0000000e-01 1.2000000e+00 7.0000000e-01 2.2000000e+00 6.0000000e-01 1.2000000e+00 1.5000000e+00 5.0000000e-01 5.0000000e-01 1.1000000e+00 1.5000000e+00 1.7000000e+00 2.2000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 2.0000000e+00 1.1000000e+00 1.0000000e+00 5.0000000e-01 1.2000000e+00 1.1000000e+00 1.2000000e+00 6.0000000e-01 1.4000000e+00 1.2000000e+00 1.0000000e+00 6.0000000e-01 8.0000000e-01 1.0000000e+00 6.0000000e-01 1.4000000e+00 4.0000000e-01 1.1000000e+00 1.3000000e+00 5.0000000e-01 1.1000000e+00 4.0000000e-01 1.1000000e+00 4.0000000e-01 7.0000000e-01 6.0000000e-01 1.1000000e+00 8.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 4.0000000e-01 3.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 1.2000000e+00 9.0000000e-01 1.0000000e+00 8.0000000e-01 6.0000000e-01 9.0000000e-01 3.0000000e-01 4.0000000e-01 1.0000000e+00 7.0000000e-01 8.0000000e-01 8.0000000e-01 3.0000000e-01 7.0000000e-01 1.4000000e+00 7.0000000e-01 6.0000000e-01 6.0000000e-01 4.0000000e-01 1.7000000e+00 6.0000000e-01 1.3000000e+00 6.0000000e-01 1.2000000e+00 9.0000000e-01 1.1000000e+00 1.9000000e+00 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.4000000e+00 4.0000000e-01 6.0000000e-01 8.0000000e-01 8.0000000e-01 8.0000000e-01 7.0000000e-01 8.0000000e-01 2.0000000e+00 2.2000000e+00 1.1000000e+00 1.0000000e+00 7.0000000e-01 2.0000000e+00 6.0000000e-01 1.0000000e+00 1.3000000e+00 5.0000000e-01 3.0000000e-01 9.0000000e-01 1.1000000e+00 1.4000000e+00 1.7000000e+00 9.0000000e-01 5.0000000e-01 9.0000000e-01 1.4000000e+00 9.0000000e-01 8.0000000e-01 3.0000000e-01 7.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 1.2000000e+00 1.0000000e+00 7.0000000e-01 8.0000000e-01 5.0000000e-01 7.0000000e-01 4.0000000e-01 1.7000000e+00 6.0000000e-01 4.0000000e-01 1.0000000e+00 1.1000000e+00 1.4000000e+00 7.0000000e-01 1.8000000e+00 1.2000000e+00 9.0000000e-01 1.3000000e+00 7.0000000e-01 1.5000000e+00 1.2000000e+00 1.6000000e+00 1.4000000e+00 1.5000000e+00 1.7000000e+00 1.9000000e+00 1.8000000e+00 1.2000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.8000000e+00 1.2000000e+00 1.2000000e+00 1.8000000e+00 1.4000000e+00 8.0000000e-01 7.0000000e-01 1.1000000e+00 1.3000000e+00 9.0000000e-01 1.0000000e-01 9.0000000e-01 9.0000000e-01 9.0000000e-01 1.3000000e+00 3.0000000e-01 8.0000000e-01 2.7000000e+00 1.8000000e+00 2.6000000e+00 2.3000000e+00 2.5000000e+00 3.3000000e+00 1.2000000e+00 3.0000000e+00 2.5000000e+00 2.8000000e+00 1.8000000e+00 2.0000000e+00 2.2000000e+00 1.7000000e+00 1.8000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 3.6000000e+00 1.7000000e+00 2.4000000e+00 1.6000000e+00 3.4000000e+00 1.6000000e+00 2.4000000e+00 2.7000000e+00 1.5000000e+00 1.6000000e+00 2.3000000e+00 2.5000000e+00 2.8000000e+00 3.1000000e+00 2.3000000e+00 1.8000000e+00 2.3000000e+00 2.8000000e+00 2.3000000e+00 2.2000000e+00 1.5000000e+00 2.1000000e+00 2.3000000e+00 2.0000000e+00 1.8000000e+00 2.6000000e+00 2.4000000e+00 1.9000000e+00 1.7000000e+00 1.9000000e+00 2.1000000e+00 1.8000000e+00 1.4000000e+00 1.6000000e+00 7.0000000e-01 7.0000000e-01 5.0000000e-01 1.0000000e+00 2.0000000e-01 1.0000000e+00 8.0000000e-01 7.0000000e-01 1.0000000e+00 7.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 3.0000000e-01 2.0000000e-01 2.0000000e-01 4.0000000e-01 6.0000000e-01 1.1000000e+00 1.1000000e+00 1.1000000e+00 8.0000000e-01 6.0000000e-01 1.2000000e+00 6.0000000e-01 2.0000000e-01 6.0000000e-01 1.0000000e+00 1.1000000e+00 1.1000000e+00 5.0000000e-01 8.0000000e-01 1.6000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 4.0000000e-01 1.6000000e+00 9.0000000e-01 1.4000000e+00 8.0000000e-01 1.3000000e+00 1.0000000e+00 1.2000000e+00 2.0000000e+00 1.7000000e+00 1.7000000e+00 1.2000000e+00 1.5000000e+00 7.0000000e-01 7.0000000e-01 9.0000000e-01 9.0000000e-01 1.1000000e+00 1.0000000e+00 9.0000000e-01 2.1000000e+00 2.3000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 2.1000000e+00 5.0000000e-01 1.1000000e+00 1.4000000e+00 5.0000000e-01 5.0000000e-01 1.0000000e+00 1.2000000e+00 1.5000000e+00 1.8000000e+00 1.0000000e+00 5.0000000e-01 1.0000000e+00 1.5000000e+00 1.1000000e+00 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.1000000e+00 1.0000000e+00 8.0000000e-01 1.3000000e+00 1.2000000e+00 1.0000000e+00 6.0000000e-01 7.0000000e-01 1.0000000e+00 7.0000000e-01 7.0000000e-01 7.0000000e-01 8.0000000e-01 9.0000000e-01 4.0000000e-01 1.5000000e+00 6.0000000e-01 6.0000000e-01 1.0000000e+00 4.0000000e-01 9.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 1.2000000e+00 1.4000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 5.0000000e-01 3.0000000e-01 4.0000000e-01 6.0000000e-01 1.2000000e+00 6.0000000e-01 8.0000000e-01 1.5000000e+00 1.1000000e+00 4.0000000e-01 3.0000000e-01 5.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 2.1000000e+00 1.2000000e+00 2.0000000e+00 1.7000000e+00 1.9000000e+00 2.7000000e+00 6.0000000e-01 2.4000000e+00 1.9000000e+00 2.2000000e+00 1.3000000e+00 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 2.8000000e+00 3.0000000e+00 1.1000000e+00 1.8000000e+00 1.0000000e+00 2.8000000e+00 1.1000000e+00 1.8000000e+00 2.1000000e+00 1.0000000e+00 1.0000000e+00 1.7000000e+00 2.0000000e+00 2.2000000e+00 2.7000000e+00 1.7000000e+00 1.2000000e+00 1.7000000e+00 2.5000000e+00 1.7000000e+00 1.6000000e+00 9.0000000e-01 1.7000000e+00 1.7000000e+00 1.7000000e+00 1.2000000e+00 2.0000000e+00 1.8000000e+00 1.5000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.2000000e+00 1.0000000e+00 1.0000000e+00 1.2000000e+00 9.0000000e-01 1.7000000e+00 1.0000000e+00 8.0000000e-01 1.2000000e+00 6.0000000e-01 1.3000000e+00 1.1000000e+00 1.4000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 1.8000000e+00 1.7000000e+00 1.0000000e+00 7.0000000e-01 5.0000000e-01 5.0000000e-01 8.0000000e-01 1.6000000e+00 1.0000000e+00 1.4000000e+00 1.7000000e+00 1.3000000e+00 1.0000000e+00 5.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 3.0000000e-01 7.0000000e-01 1.0000000e+00 9.0000000e-01 1.2000000e+00 5.0000000e-01 8.0000000e-01 2.5000000e+00 1.6000000e+00 2.4000000e+00 2.1000000e+00 2.3000000e+00 3.1000000e+00 1.0000000e+00 2.8000000e+00 2.3000000e+00 2.6000000e+00 1.6000000e+00 1.8000000e+00 2.0000000e+00 1.5000000e+00 1.6000000e+00 1.8000000e+00 2.0000000e+00 3.2000000e+00 3.4000000e+00 1.5000000e+00 2.2000000e+00 1.4000000e+00 3.2000000e+00 1.4000000e+00 2.2000000e+00 2.5000000e+00 1.3000000e+00 1.4000000e+00 2.1000000e+00 2.3000000e+00 2.6000000e+00 2.9000000e+00 2.1000000e+00 1.6000000e+00 2.1000000e+00 2.7000000e+00 2.1000000e+00 2.0000000e+00 1.3000000e+00 1.9000000e+00 2.1000000e+00 1.9000000e+00 1.6000000e+00 2.4000000e+00 2.2000000e+00 1.7000000e+00 1.5000000e+00 1.7000000e+00 1.9000000e+00 1.6000000e+00 8.0000000e-01 5.0000000e-01 6.0000000e-01 8.0000000e-01 3.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 6.0000000e-01 2.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 3.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 7.0000000e-01 3.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 4.0000000e-01 9.0000000e-01 3.0000000e-01 3.0000000e-01 2.0000000e-01 3.0000000e-01 1.2000000e+00 2.0000000e-01 1.8000000e+00 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.6000000e+00 2.4000000e+00 1.0000000e+00 2.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 8.0000000e-01 9.0000000e-01 1.1000000e+00 1.3000000e+00 2.5000000e+00 2.7000000e+00 8.0000000e-01 1.5000000e+00 7.0000000e-01 2.5000000e+00 7.0000000e-01 1.5000000e+00 1.8000000e+00 6.0000000e-01 7.0000000e-01 1.4000000e+00 1.6000000e+00 1.9000000e+00 2.2000000e+00 1.4000000e+00 9.0000000e-01 1.4000000e+00 1.9000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.2000000e+00 1.4000000e+00 1.0000000e+00 9.0000000e-01 1.7000000e+00 1.5000000e+00 1.0000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 7.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 1.0000000e+00 6.0000000e-01 9.0000000e-01 7.0000000e-01 7.0000000e-01 8.0000000e-01 8.0000000e-01 1.0000000e+00 7.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 1.1000000e+00 8.0000000e-01 1.2000000e+00 9.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 8.0000000e-01 4.0000000e-01 1.0000000e+00 5.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 1.0000000e+00 6.0000000e-01 2.0000000e+00 1.1000000e+00 1.9000000e+00 1.6000000e+00 1.8000000e+00 2.6000000e+00 1.1000000e+00 2.3000000e+00 1.8000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.0000000e+00 1.4000000e+00 1.3000000e+00 1.5000000e+00 2.7000000e+00 2.9000000e+00 1.0000000e+00 1.7000000e+00 1.0000000e+00 2.7000000e+00 9.0000000e-01 1.7000000e+00 2.0000000e+00 8.0000000e-01 9.0000000e-01 1.6000000e+00 1.8000000e+00 2.1000000e+00 2.4000000e+00 1.6000000e+00 1.1000000e+00 1.6000000e+00 2.1000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 1.3000000e+00 1.1000000e+00 1.9000000e+00 1.7000000e+00 1.3000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.1000000e+00 1.1000000e+00 6.0000000e-01 5.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 4.0000000e-01 7.0000000e-01 4.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 2.0000000e-01 1.2000000e+00 9.0000000e-01 1.0000000e+00 8.0000000e-01 4.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 1.0000000e-01 7.0000000e-01 1.4000000e+00 5.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 1.7000000e+00 6.0000000e-01 1.3000000e+00 5.0000000e-01 1.2000000e+00 9.0000000e-01 1.1000000e+00 1.9000000e+00 1.2000000e+00 1.6000000e+00 1.1000000e+00 1.4000000e+00 6.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 1.0000000e+00 9.0000000e-01 8.0000000e-01 2.0000000e+00 2.2000000e+00 7.0000000e-01 1.0000000e+00 6.0000000e-01 2.0000000e+00 4.0000000e-01 1.0000000e+00 1.3000000e+00 4.0000000e-01 4.0000000e-01 9.0000000e-01 1.1000000e+00 1.4000000e+00 1.8000000e+00 9.0000000e-01 4.0000000e-01 9.0000000e-01 1.6000000e+00 1.0000000e+00 8.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 1.2000000e+00 1.1000000e+00 9.0000000e-01 5.0000000e-01 6.0000000e-01 9.0000000e-01 4.0000000e-01 1.1000000e+00 9.0000000e-01 5.0000000e-01 9.0000000e-01 4.0000000e-01 1.2000000e+00 5.0000000e-01 1.3000000e+00 1.1000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 1.4000000e+00 9.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 1.5000000e+00 9.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 4.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 2.4000000e+00 1.5000000e+00 2.3000000e+00 2.0000000e+00 2.2000000e+00 3.0000000e+00 9.0000000e-01 2.7000000e+00 2.2000000e+00 2.5000000e+00 1.5000000e+00 1.7000000e+00 1.9000000e+00 1.4000000e+00 1.5000000e+00 1.7000000e+00 1.9000000e+00 3.1000000e+00 3.3000000e+00 1.4000000e+00 2.1000000e+00 1.3000000e+00 3.1000000e+00 1.3000000e+00 2.1000000e+00 2.4000000e+00 1.2000000e+00 1.3000000e+00 2.0000000e+00 2.2000000e+00 2.5000000e+00 2.8000000e+00 2.0000000e+00 1.5000000e+00 2.0000000e+00 2.5000000e+00 2.0000000e+00 1.9000000e+00 1.2000000e+00 1.8000000e+00 2.0000000e+00 1.5000000e+00 1.5000000e+00 2.3000000e+00 2.1000000e+00 1.6000000e+00 1.4000000e+00 1.6000000e+00 1.8000000e+00 1.5000000e+00 1.1000000e+00 9.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 1.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 1.0000000e+00 1.2000000e+00 1.2000000e+00 9.0000000e-01 7.0000000e-01 1.3000000e+00 7.0000000e-01 3.0000000e-01 8.0000000e-01 1.1000000e+00 1.2000000e+00 1.2000000e+00 6.0000000e-01 9.0000000e-01 1.7000000e+00 1.1000000e+00 1.0000000e+00 1.0000000e+00 5.0000000e-01 1.6000000e+00 1.0000000e+00 1.6000000e+00 9.0000000e-01 1.5000000e+00 1.2000000e+00 1.4000000e+00 2.2000000e+00 1.8000000e+00 1.9000000e+00 1.4000000e+00 1.7000000e+00 7.0000000e-01 9.0000000e-01 1.1000000e+00 1.0000000e+00 1.0000000e+00 9.0000000e-01 1.1000000e+00 2.3000000e+00 2.5000000e+00 9.0000000e-01 1.3000000e+00 1.1000000e+00 2.3000000e+00 5.0000000e-01 1.3000000e+00 1.6000000e+00 5.0000000e-01 6.0000000e-01 1.2000000e+00 1.4000000e+00 1.7000000e+00 2.0000000e+00 1.2000000e+00 7.0000000e-01 1.2000000e+00 1.7000000e+00 1.2000000e+00 1.1000000e+00 7.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 9.0000000e-01 1.5000000e+00 1.3000000e+00 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 8.0000000e-01 5.0000000e-01 8.0000000e-01 6.0000000e-01 3.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 1.2000000e+00 1.1000000e+00 4.0000000e-01 1.0000000e+00 7.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 2.0000000e-01 4.0000000e-01 1.1000000e+00 7.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 1.2000000e+00 3.0000000e-01 3.0000000e-01 3.0000000e-01 6.0000000e-01 1.5000000e+00 4.0000000e-01 1.5000000e+00 6.0000000e-01 1.5000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 7.0000000e-01 1.8000000e+00 1.3000000e+00 1.6000000e+00 9.0000000e-01 8.0000000e-01 1.2000000e+00 5.0000000e-01 9.0000000e-01 8.0000000e-01 1.0000000e+00 2.2000000e+00 2.4000000e+00 8.0000000e-01 1.3000000e+00 5.0000000e-01 2.2000000e+00 7.0000000e-01 1.2000000e+00 1.6000000e+00 6.0000000e-01 5.0000000e-01 1.1000000e+00 1.6000000e+00 1.8000000e+00 2.3000000e+00 1.1000000e+00 7.0000000e-01 1.1000000e+00 2.1000000e+00 1.1000000e+00 1.0000000e+00 4.0000000e-01 1.3000000e+00 1.1000000e+00 1.3000000e+00 6.0000000e-01 1.4000000e+00 1.2000000e+00 1.1000000e+00 7.0000000e-01 9.0000000e-01 9.0000000e-01 6.0000000e-01 5.0000000e-01 2.0000000e-01 8.0000000e-01 3.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 6.0000000e-01 3.0000000e-01 4.0000000e-01 2.0000000e-01 1.0000000e+00 5.0000000e-01 7.0000000e-01 9.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 2.0000000e-01 8.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 1.1000000e+00 3.0000000e-01 1.9000000e+00 1.0000000e+00 1.8000000e+00 1.5000000e+00 1.7000000e+00 2.5000000e+00 9.0000000e-01 2.2000000e+00 1.7000000e+00 2.0000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.0000000e+00 1.4000000e+00 1.3000000e+00 1.4000000e+00 2.6000000e+00 2.8000000e+00 9.0000000e-01 1.6000000e+00 1.0000000e+00 2.6000000e+00 8.0000000e-01 1.6000000e+00 1.9000000e+00 8.0000000e-01 8.0000000e-01 1.5000000e+00 1.7000000e+00 2.0000000e+00 2.3000000e+00 1.5000000e+00 1.0000000e+00 1.5000000e+00 2.0000000e+00 1.5000000e+00 1.4000000e+00 8.0000000e-01 1.3000000e+00 1.5000000e+00 1.3000000e+00 1.0000000e+00 1.8000000e+00 1.6000000e+00 1.3000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 1.0000000e+00 6.0000000e-01 1.0000000e+00 6.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 6.0000000e-01 8.0000000e-01 7.0000000e-01 1.0000000e+00 7.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 8.0000000e-01 1.2000000e+00 9.0000000e-01 2.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 1.2000000e+00 6.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 1.5000000e+00 6.0000000e-01 1.5000000e+00 6.0000000e-01 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 1.3000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 1.0000000e+00 8.0000000e-01 1.0000000e+00 5.0000000e-01 9.0000000e-01 1.0000000e+00 1.0000000e+00 2.2000000e+00 2.4000000e+00 5.0000000e-01 1.2000000e+00 6.0000000e-01 2.2000000e+00 5.0000000e-01 1.2000000e+00 1.5000000e+00 6.0000000e-01 8.0000000e-01 1.1000000e+00 1.3000000e+00 1.6000000e+00 1.9000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.6000000e+00 1.2000000e+00 1.0000000e+00 8.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 6.0000000e-01 1.4000000e+00 1.2000000e+00 8.0000000e-01 5.0000000e-01 8.0000000e-01 1.2000000e+00 8.0000000e-01 9.0000000e-01 5.0000000e-01 1.0000000e+00 8.0000000e-01 8.0000000e-01 1.0000000e+00 1.2000000e+00 1.1000000e+00 6.0000000e-01 4.0000000e-01 1.0000000e-01 2.0000000e-01 2.0000000e-01 1.2000000e+00 6.0000000e-01 9.0000000e-01 1.1000000e+00 7.0000000e-01 5.0000000e-01 2.0000000e-01 5.0000000e-01 7.0000000e-01 2.0000000e-01 6.0000000e-01 3.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 9.0000000e-01 3.0000000e-01 2.1000000e+00 1.2000000e+00 2.0000000e+00 1.7000000e+00 1.9000000e+00 2.7000000e+00 7.0000000e-01 2.4000000e+00 1.9000000e+00 2.2000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.3000000e+00 1.4000000e+00 1.6000000e+00 2.8000000e+00 3.0000000e+00 1.1000000e+00 1.8000000e+00 1.0000000e+00 2.8000000e+00 1.0000000e+00 1.8000000e+00 2.1000000e+00 9.0000000e-01 1.0000000e+00 1.7000000e+00 1.9000000e+00 2.2000000e+00 2.5000000e+00 1.7000000e+00 1.2000000e+00 1.7000000e+00 2.2000000e+00 1.7000000e+00 1.6000000e+00 9.0000000e-01 1.5000000e+00 1.7000000e+00 1.3000000e+00 1.2000000e+00 2.0000000e+00 1.8000000e+00 1.3000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.2000000e+00 8.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 3.0000000e-01 1.3000000e+00 1.0000000e+00 1.1000000e+00 9.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 9.0000000e-01 7.0000000e-01 8.0000000e-01 6.0000000e-01 4.0000000e-01 8.0000000e-01 1.5000000e+00 6.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 1.8000000e+00 7.0000000e-01 1.2000000e+00 5.0000000e-01 1.2000000e+00 8.0000000e-01 1.0000000e+00 1.8000000e+00 1.0000000e+00 1.5000000e+00 1.0000000e+00 1.3000000e+00 6.0000000e-01 5.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 1.9000000e+00 2.1000000e+00 1.0000000e+00 1.0000000e+00 4.0000000e-01 1.9000000e+00 5.0000000e-01 9.0000000e-01 1.3000000e+00 4.0000000e-01 2.0000000e-01 8.0000000e-01 1.3000000e+00 1.5000000e+00 2.0000000e+00 8.0000000e-01 4.0000000e-01 8.0000000e-01 1.8000000e+00 8.0000000e-01 7.0000000e-01 2.0000000e-01 1.0000000e+00 8.0000000e-01 1.0000000e+00 5.0000000e-01 1.1000000e+00 9.0000000e-01 8.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 9.0000000e-01 7.0000000e-01 3.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 1.1000000e+00 7.0000000e-01 6.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 1.1000000e+00 5.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 1.0000000e+00 4.0000000e-01 2.0000000e+00 1.1000000e+00 1.9000000e+00 1.6000000e+00 1.8000000e+00 2.6000000e+00 1.2000000e+00 2.3000000e+00 1.8000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.0000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 2.7000000e+00 2.9000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 2.7000000e+00 9.0000000e-01 1.7000000e+00 2.0000000e+00 8.0000000e-01 9.0000000e-01 1.6000000e+00 1.8000000e+00 2.1000000e+00 2.4000000e+00 1.6000000e+00 1.1000000e+00 1.6000000e+00 2.1000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.1000000e+00 1.9000000e+00 1.7000000e+00 1.2000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.1000000e+00 3.0000000e-01 6.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 1.4000000e+00 1.1000000e+00 1.2000000e+00 1.0000000e+00 3.0000000e-01 9.0000000e-01 9.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 9.0000000e-01 1.6000000e+00 7.0000000e-01 7.0000000e-01 7.0000000e-01 6.0000000e-01 1.9000000e+00 8.0000000e-01 1.1000000e+00 5.0000000e-01 1.0000000e+00 7.0000000e-01 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.4000000e+00 9.0000000e-01 1.2000000e+00 7.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 1.8000000e+00 2.0000000e+00 3.0000000e-01 8.0000000e-01 7.0000000e-01 1.8000000e+00 3.0000000e-01 8.0000000e-01 1.1000000e+00 3.0000000e-01 5.0000000e-01 7.0000000e-01 9.0000000e-01 1.2000000e+00 1.6000000e+00 7.0000000e-01 3.0000000e-01 7.0000000e-01 1.4000000e+00 9.0000000e-01 6.0000000e-01 5.0000000e-01 6.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 1.0000000e+00 1.0000000e+00 8.0000000e-01 4.0000000e-01 5.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 3.0000000e-01 1.2000000e+00 9.0000000e-01 1.0000000e+00 8.0000000e-01 4.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 2.0000000e-01 7.0000000e-01 1.4000000e+00 5.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 1.7000000e+00 6.0000000e-01 1.3000000e+00 7.0000000e-01 1.2000000e+00 9.0000000e-01 1.1000000e+00 1.9000000e+00 1.2000000e+00 1.6000000e+00 1.1000000e+00 1.4000000e+00 8.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 1.2000000e+00 1.1000000e+00 8.0000000e-01 2.0000000e+00 2.2000000e+00 6.0000000e-01 1.1000000e+00 8.0000000e-01 2.0000000e+00 6.0000000e-01 1.0000000e+00 1.3000000e+00 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.1000000e+00 1.4000000e+00 1.8000000e+00 1.0000000e+00 4.0000000e-01 9.0000000e-01 1.6000000e+00 1.2000000e+00 8.0000000e-01 6.0000000e-01 9.0000000e-01 1.2000000e+00 1.1000000e+00 7.0000000e-01 1.2000000e+00 1.3000000e+00 1.1000000e+00 7.0000000e-01 8.0000000e-01 1.1000000e+00 6.0000000e-01 2.0000000e-01 5.0000000e-01 7.0000000e-01 4.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 4.0000000e-01 6.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 3.0000000e-01 6.0000000e-01 1.4000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 2.0000000e-01 1.3000000e+00 7.0000000e-01 1.7000000e+00 8.0000000e-01 1.6000000e+00 1.3000000e+00 1.5000000e+00 2.3000000e+00 1.5000000e+00 2.0000000e+00 1.5000000e+00 1.8000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 1.2000000e+00 2.4000000e+00 2.6000000e+00 7.0000000e-01 1.4000000e+00 8.0000000e-01 2.4000000e+00 6.0000000e-01 1.4000000e+00 1.7000000e+00 5.0000000e-01 6.0000000e-01 1.3000000e+00 1.5000000e+00 1.8000000e+00 2.1000000e+00 1.3000000e+00 8.0000000e-01 1.3000000e+00 1.8000000e+00 1.3000000e+00 1.2000000e+00 5.0000000e-01 1.1000000e+00 1.3000000e+00 1.0000000e+00 8.0000000e-01 1.6000000e+00 1.4000000e+00 1.0000000e+00 7.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.1000000e+00 1.1000000e+00 8.0000000e-01 7.0000000e-01 1.2000000e+00 6.0000000e-01 3.0000000e-01 7.0000000e-01 1.0000000e+00 1.1000000e+00 1.1000000e+00 5.0000000e-01 8.0000000e-01 1.6000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 4.0000000e-01 1.5000000e+00 9.0000000e-01 1.6000000e+00 8.0000000e-01 1.5000000e+00 1.2000000e+00 1.4000000e+00 2.2000000e+00 1.7000000e+00 1.9000000e+00 1.4000000e+00 1.7000000e+00 7.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 1.0000000e+00 9.0000000e-01 1.1000000e+00 2.3000000e+00 2.5000000e+00 8.0000000e-01 1.3000000e+00 1.0000000e+00 2.3000000e+00 5.0000000e-01 1.3000000e+00 1.6000000e+00 4.0000000e-01 5.0000000e-01 1.2000000e+00 1.4000000e+00 1.7000000e+00 2.0000000e+00 1.2000000e+00 7.0000000e-01 1.2000000e+00 1.7000000e+00 1.2000000e+00 1.1000000e+00 6.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 8.0000000e-01 1.5000000e+00 1.3000000e+00 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 7.0000000e-01 3.0000000e-01 8.0000000e-01 1.3000000e+00 1.3000000e+00 1.3000000e+00 1.0000000e+00 8.0000000e-01 1.4000000e+00 8.0000000e-01 3.0000000e-01 5.0000000e-01 1.2000000e+00 1.3000000e+00 1.3000000e+00 7.0000000e-01 1.0000000e+00 1.8000000e+00 1.2000000e+00 1.1000000e+00 1.1000000e+00 6.0000000e-01 1.8000000e+00 1.1000000e+00 1.2000000e+00 1.0000000e+00 1.1000000e+00 8.0000000e-01 1.0000000e+00 1.8000000e+00 1.9000000e+00 1.5000000e+00 1.0000000e+00 1.3000000e+00 6.0000000e-01 5.0000000e-01 7.0000000e-01 1.1000000e+00 1.0000000e+00 9.0000000e-01 7.0000000e-01 1.9000000e+00 2.1000000e+00 8.0000000e-01 9.0000000e-01 1.2000000e+00 1.9000000e+00 5.0000000e-01 9.0000000e-01 1.2000000e+00 6.0000000e-01 7.0000000e-01 8.0000000e-01 1.0000000e+00 1.3000000e+00 1.6000000e+00 8.0000000e-01 5.0000000e-01 8.0000000e-01 1.3000000e+00 1.0000000e+00 7.0000000e-01 8.0000000e-01 7.0000000e-01 1.0000000e+00 9.0000000e-01 1.0000000e+00 1.1000000e+00 1.1000000e+00 9.0000000e-01 5.0000000e-01 6.0000000e-01 9.0000000e-01 9.0000000e-01 7.0000000e-01 1.5000000e+00 1.2000000e+00 1.3000000e+00 1.1000000e+00 7.0000000e-01 1.3000000e+00 7.0000000e-01 3.0000000e-01 7.0000000e-01 1.1000000e+00 1.2000000e+00 1.2000000e+00 6.0000000e-01 1.0000000e+00 1.7000000e+00 1.1000000e+00 1.0000000e+00 1.0000000e+00 7.0000000e-01 2.0000000e+00 1.0000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.6000000e+00 1.8000000e+00 1.3000000e+00 8.0000000e-01 1.1000000e+00 3.0000000e-01 3.0000000e-01 5.0000000e-01 1.0000000e+00 9.0000000e-01 6.0000000e-01 5.0000000e-01 1.7000000e+00 1.9000000e+00 8.0000000e-01 7.0000000e-01 1.1000000e+00 1.7000000e+00 4.0000000e-01 7.0000000e-01 1.0000000e+00 5.0000000e-01 6.0000000e-01 6.0000000e-01 8.0000000e-01 1.1000000e+00 1.4000000e+00 6.0000000e-01 4.0000000e-01 6.0000000e-01 1.1000000e+00 7.0000000e-01 5.0000000e-01 7.0000000e-01 4.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 7.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 1.0000000e-01 5.0000000e-01 1.2000000e+00 4.0000000e-01 3.0000000e-01 3.0000000e-01 2.0000000e-01 1.5000000e+00 4.0000000e-01 1.5000000e+00 6.0000000e-01 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 1.1000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 6.0000000e-01 8.0000000e-01 1.0000000e+00 5.0000000e-01 9.0000000e-01 8.0000000e-01 1.0000000e+00 2.2000000e+00 2.4000000e+00 7.0000000e-01 1.2000000e+00 5.0000000e-01 2.2000000e+00 4.0000000e-01 1.2000000e+00 1.5000000e+00 3.0000000e-01 4.0000000e-01 1.1000000e+00 1.3000000e+00 1.6000000e+00 1.9000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.7000000e+00 1.1000000e+00 1.0000000e+00 3.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 6.0000000e-01 1.4000000e+00 1.2000000e+00 8.0000000e-01 5.0000000e-01 7.0000000e-01 9.0000000e-01 6.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 1.6000000e+00 1.0000000e+00 1.0000000e+00 1.2000000e+00 9.0000000e-01 6.0000000e-01 5.0000000e-01 9.0000000e-01 1.1000000e+00 5.0000000e-01 7.0000000e-01 7.0000000e-01 7.0000000e-01 7.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 2.5000000e+00 1.6000000e+00 2.4000000e+00 2.1000000e+00 2.3000000e+00 3.1000000e+00 1.0000000e+00 2.8000000e+00 2.3000000e+00 2.6000000e+00 1.6000000e+00 1.8000000e+00 2.0000000e+00 1.5000000e+00 1.6000000e+00 1.8000000e+00 2.0000000e+00 3.2000000e+00 3.4000000e+00 1.5000000e+00 2.2000000e+00 1.4000000e+00 3.2000000e+00 1.4000000e+00 2.2000000e+00 2.5000000e+00 1.3000000e+00 1.4000000e+00 2.1000000e+00 2.3000000e+00 2.6000000e+00 2.9000000e+00 2.1000000e+00 1.6000000e+00 2.1000000e+00 2.6000000e+00 2.1000000e+00 2.0000000e+00 1.3000000e+00 1.9000000e+00 2.1000000e+00 1.6000000e+00 1.6000000e+00 2.4000000e+00 2.2000000e+00 1.7000000e+00 1.5000000e+00 1.7000000e+00 1.9000000e+00 1.6000000e+00 1.0000000e-01 3.0000000e-01 1.3000000e+00 7.0000000e-01 1.0000000e+00 1.2000000e+00 8.0000000e-01 6.0000000e-01 2.0000000e-01 6.0000000e-01 8.0000000e-01 3.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 8.0000000e-01 4.0000000e-01 2.2000000e+00 1.3000000e+00 2.1000000e+00 1.8000000e+00 2.0000000e+00 2.8000000e+00 7.0000000e-01 2.5000000e+00 2.0000000e+00 2.3000000e+00 1.3000000e+00 1.5000000e+00 1.7000000e+00 1.2000000e+00 1.3000000e+00 1.5000000e+00 1.7000000e+00 2.9000000e+00 3.1000000e+00 1.2000000e+00 1.9000000e+00 1.1000000e+00 2.9000000e+00 1.1000000e+00 1.9000000e+00 2.2000000e+00 1.0000000e+00 1.1000000e+00 1.8000000e+00 2.0000000e+00 2.3000000e+00 2.6000000e+00 1.8000000e+00 1.3000000e+00 1.8000000e+00 2.3000000e+00 1.8000000e+00 1.7000000e+00 1.0000000e+00 1.6000000e+00 1.8000000e+00 1.4000000e+00 1.3000000e+00 2.1000000e+00 1.9000000e+00 1.4000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 1.3000000e+00 3.0000000e-01 1.4000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 8.0000000e-01 6.0000000e-01 3.0000000e-01 7.0000000e-01 9.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 7.0000000e-01 4.0000000e-01 2.3000000e+00 1.4000000e+00 2.2000000e+00 1.9000000e+00 2.1000000e+00 2.9000000e+00 8.0000000e-01 2.6000000e+00 2.1000000e+00 2.4000000e+00 1.4000000e+00 1.6000000e+00 1.8000000e+00 1.3000000e+00 1.4000000e+00 1.6000000e+00 1.8000000e+00 3.0000000e+00 3.2000000e+00 1.3000000e+00 2.0000000e+00 1.2000000e+00 3.0000000e+00 1.2000000e+00 2.0000000e+00 2.3000000e+00 1.1000000e+00 1.2000000e+00 1.9000000e+00 2.1000000e+00 2.4000000e+00 2.7000000e+00 1.9000000e+00 1.4000000e+00 1.9000000e+00 2.4000000e+00 1.9000000e+00 1.8000000e+00 1.1000000e+00 1.7000000e+00 1.9000000e+00 1.4000000e+00 1.4000000e+00 2.2000000e+00 2.0000000e+00 1.5000000e+00 1.3000000e+00 1.5000000e+00 1.7000000e+00 1.4000000e+00 1.2000000e+00 6.0000000e-01 7.0000000e-01 9.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 5.0000000e-01 7.0000000e-01 1.0000000e-01 8.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 4.0000000e-01 9.0000000e-01 2.0000000e-01 2.1000000e+00 1.2000000e+00 2.0000000e+00 1.7000000e+00 1.9000000e+00 2.7000000e+00 9.0000000e-01 2.4000000e+00 1.9000000e+00 2.2000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.2000000e+00 1.4000000e+00 1.6000000e+00 2.8000000e+00 3.0000000e+00 1.1000000e+00 1.8000000e+00 1.0000000e+00 2.8000000e+00 1.0000000e+00 1.8000000e+00 2.1000000e+00 9.0000000e-01 1.0000000e+00 1.7000000e+00 1.9000000e+00 2.2000000e+00 2.5000000e+00 1.7000000e+00 1.2000000e+00 1.7000000e+00 2.2000000e+00 1.7000000e+00 1.6000000e+00 9.0000000e-01 1.5000000e+00 1.7000000e+00 1.2000000e+00 1.2000000e+00 2.0000000e+00 1.8000000e+00 1.3000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.2000000e+00 6.0000000e-01 7.0000000e-01 7.0000000e-01 7.0000000e-01 1.0000000e+00 1.1000000e+00 7.0000000e-01 5.0000000e-01 1.1000000e+00 1.8000000e+00 9.0000000e-01 9.0000000e-01 9.0000000e-01 8.0000000e-01 2.1000000e+00 1.0000000e+00 9.0000000e-01 3.0000000e-01 1.1000000e+00 5.0000000e-01 7.0000000e-01 1.6000000e+00 1.1000000e+00 1.3000000e+00 7.0000000e-01 1.2000000e+00 5.0000000e-01 4.0000000e-01 8.0000000e-01 4.0000000e-01 8.0000000e-01 7.0000000e-01 5.0000000e-01 1.7000000e+00 1.8000000e+00 5.0000000e-01 9.0000000e-01 4.0000000e-01 1.7000000e+00 3.0000000e-01 7.0000000e-01 1.2000000e+00 3.0000000e-01 3.0000000e-01 5.0000000e-01 1.2000000e+00 1.4000000e+00 1.9000000e+00 6.0000000e-01 3.0000000e-01 5.0000000e-01 1.7000000e+00 8.0000000e-01 4.0000000e-01 3.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 3.0000000e-01 8.0000000e-01 9.0000000e-01 7.0000000e-01 3.0000000e-01 5.0000000e-01 7.0000000e-01 3.0000000e-01 6.0000000e-01 1.3000000e+00 9.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 7.0000000e-01 5.0000000e-01 1.2000000e+00 3.0000000e-01 3.0000000e-01 3.0000000e-01 8.0000000e-01 1.5000000e+00 4.0000000e-01 1.5000000e+00 6.0000000e-01 1.7000000e+00 1.1000000e+00 1.3000000e+00 2.2000000e+00 5.0000000e-01 1.9000000e+00 1.3000000e+00 1.8000000e+00 1.1000000e+00 1.0000000e+00 1.4000000e+00 5.0000000e-01 9.0000000e-01 1.0000000e+00 1.1000000e+00 2.3000000e+00 2.4000000e+00 8.0000000e-01 1.5000000e+00 5.0000000e-01 2.3000000e+00 9.0000000e-01 1.3000000e+00 1.8000000e+00 8.0000000e-01 7.0000000e-01 1.1000000e+00 1.8000000e+00 2.0000000e+00 2.5000000e+00 1.1000000e+00 9.0000000e-01 1.1000000e+00 2.3000000e+00 1.1000000e+00 1.0000000e+00 6.0000000e-01 1.5000000e+00 1.3000000e+00 1.5000000e+00 6.0000000e-01 1.4000000e+00 1.3000000e+00 1.3000000e+00 9.0000000e-01 1.1000000e+00 9.0000000e-01 6.0000000e-01 7.0000000e-01 1.1000000e+00 4.0000000e-01 9.0000000e-01 8.0000000e-01 4.0000000e-01 8.0000000e-01 1.2000000e+00 7.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 1.5000000e+00 6.0000000e-01 1.5000000e+00 7.0000000e-01 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.1000000e+00 1.1000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 6.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 8.0000000e-01 8.0000000e-01 1.0000000e+00 2.2000000e+00 2.4000000e+00 1.2000000e+00 1.2000000e+00 6.0000000e-01 2.2000000e+00 7.0000000e-01 1.2000000e+00 1.5000000e+00 6.0000000e-01 4.0000000e-01 1.1000000e+00 1.3000000e+00 1.6000000e+00 1.9000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.7000000e+00 1.1000000e+00 1.0000000e+00 4.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 7.0000000e-01 1.4000000e+00 1.2000000e+00 7.0000000e-01 9.0000000e-01 7.0000000e-01 9.0000000e-01 6.0000000e-01 8.0000000e-01 1.1000000e+00 1.2000000e+00 1.2000000e+00 6.0000000e-01 9.0000000e-01 1.7000000e+00 1.1000000e+00 1.0000000e+00 1.0000000e+00 5.0000000e-01 1.7000000e+00 1.0000000e+00 1.3000000e+00 9.0000000e-01 1.2000000e+00 9.0000000e-01 1.1000000e+00 1.9000000e+00 1.8000000e+00 1.6000000e+00 1.1000000e+00 1.4000000e+00 5.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 8.0000000e-01 8.0000000e-01 2.0000000e+00 2.2000000e+00 9.0000000e-01 1.0000000e+00 1.1000000e+00 2.0000000e+00 4.0000000e-01 1.0000000e+00 1.3000000e+00 5.0000000e-01 6.0000000e-01 9.0000000e-01 1.1000000e+00 1.4000000e+00 1.7000000e+00 9.0000000e-01 4.0000000e-01 9.0000000e-01 1.4000000e+00 9.0000000e-01 8.0000000e-01 7.0000000e-01 7.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 1.2000000e+00 1.0000000e+00 8.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 8.0000000e-01 7.0000000e-01 8.0000000e-01 8.0000000e-01 7.0000000e-01 5.0000000e-01 1.3000000e+00 7.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 1.4000000e+00 6.0000000e-01 1.6000000e+00 7.0000000e-01 1.5000000e+00 1.2000000e+00 1.4000000e+00 2.2000000e+00 1.4000000e+00 1.9000000e+00 1.4000000e+00 1.7000000e+00 9.0000000e-01 9.0000000e-01 1.1000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 1.1000000e+00 2.3000000e+00 2.5000000e+00 6.0000000e-01 1.3000000e+00 7.0000000e-01 2.3000000e+00 5.0000000e-01 1.3000000e+00 1.6000000e+00 5.0000000e-01 7.0000000e-01 1.2000000e+00 1.4000000e+00 1.7000000e+00 2.0000000e+00 1.2000000e+00 7.0000000e-01 1.2000000e+00 1.7000000e+00 1.2000000e+00 1.1000000e+00 7.0000000e-01 1.0000000e+00 1.2000000e+00 1.0000000e+00 7.0000000e-01 1.5000000e+00 1.3000000e+00 1.0000000e+00 6.0000000e-01 8.0000000e-01 1.1000000e+00 7.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 3.0000000e-01 1.0000000e-01 1.0000000e-01 6.0000000e-01 1.1000000e+00 2.0000000e-01 1.9000000e+00 1.0000000e+00 1.8000000e+00 1.5000000e+00 1.7000000e+00 2.5000000e+00 7.0000000e-01 2.2000000e+00 1.7000000e+00 2.0000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 9.0000000e-01 1.1000000e+00 1.2000000e+00 1.4000000e+00 2.6000000e+00 2.8000000e+00 9.0000000e-01 1.6000000e+00 8.0000000e-01 2.6000000e+00 8.0000000e-01 1.6000000e+00 1.9000000e+00 7.0000000e-01 8.0000000e-01 1.5000000e+00 1.7000000e+00 2.0000000e+00 2.3000000e+00 1.5000000e+00 1.0000000e+00 1.5000000e+00 2.1000000e+00 1.5000000e+00 1.4000000e+00 7.0000000e-01 1.3000000e+00 1.5000000e+00 1.3000000e+00 1.0000000e+00 1.8000000e+00 1.6000000e+00 1.1000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 1.0000000e+00 4.0000000e-01 6.0000000e-01 3.0000000e-01 7.0000000e-01 2.0000000e-01 5.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e+00 3.0000000e-01 2.0000000e+00 1.1000000e+00 1.9000000e+00 1.6000000e+00 1.8000000e+00 2.6000000e+00 6.0000000e-01 2.3000000e+00 1.8000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.0000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 2.7000000e+00 2.9000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 2.7000000e+00 9.0000000e-01 1.7000000e+00 2.0000000e+00 8.0000000e-01 9.0000000e-01 1.6000000e+00 1.8000000e+00 2.1000000e+00 2.4000000e+00 1.6000000e+00 1.1000000e+00 1.6000000e+00 2.2000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 1.4000000e+00 1.1000000e+00 1.9000000e+00 1.7000000e+00 1.2000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.1000000e+00 6.0000000e-01 4.0000000e-01 1.1000000e+00 2.0000000e-01 4.0000000e-01 3.0000000e-01 7.0000000e-01 1.4000000e+00 3.0000000e-01 1.6000000e+00 7.0000000e-01 1.6000000e+00 1.2000000e+00 1.4000000e+00 2.2000000e+00 6.0000000e-01 1.9000000e+00 1.4000000e+00 1.7000000e+00 1.0000000e+00 9.0000000e-01 1.3000000e+00 8.0000000e-01 1.2000000e+00 1.1000000e+00 1.1000000e+00 2.3000000e+00 2.5000000e+00 6.0000000e-01 1.4000000e+00 8.0000000e-01 2.3000000e+00 8.0000000e-01 1.3000000e+00 1.7000000e+00 7.0000000e-01 6.0000000e-01 1.2000000e+00 1.7000000e+00 1.9000000e+00 2.4000000e+00 1.2000000e+00 8.0000000e-01 1.2000000e+00 2.2000000e+00 1.2000000e+00 1.1000000e+00 6.0000000e-01 1.4000000e+00 1.2000000e+00 1.4000000e+00 7.0000000e-01 1.5000000e+00 1.3000000e+00 1.2000000e+00 8.0000000e-01 1.0000000e+00 1.1000000e+00 7.0000000e-01 6.0000000e-01 1.3000000e+00 5.0000000e-01 4.0000000e-01 4.0000000e-01 3.0000000e-01 1.6000000e+00 5.0000000e-01 1.4000000e+00 5.0000000e-01 1.3000000e+00 1.0000000e+00 1.2000000e+00 2.0000000e+00 1.2000000e+00 1.7000000e+00 1.2000000e+00 1.5000000e+00 6.0000000e-01 7.0000000e-01 9.0000000e-01 6.0000000e-01 1.0000000e+00 9.0000000e-01 9.0000000e-01 2.1000000e+00 2.3000000e+00 8.0000000e-01 1.1000000e+00 6.0000000e-01 2.1000000e+00 4.0000000e-01 1.1000000e+00 1.4000000e+00 4.0000000e-01 4.0000000e-01 1.0000000e+00 1.2000000e+00 1.5000000e+00 1.8000000e+00 1.0000000e+00 5.0000000e-01 1.0000000e+00 1.6000000e+00 1.0000000e+00 9.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 1.3000000e+00 1.1000000e+00 9.0000000e-01 5.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 8.0000000e-01 2.0000000e-01 4.0000000e-01 3.0000000e-01 4.0000000e-01 1.0000000e+00 2.0000000e-01 2.0000000e+00 1.1000000e+00 1.9000000e+00 1.6000000e+00 1.8000000e+00 2.6000000e+00 9.0000000e-01 2.3000000e+00 1.8000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 1.5000000e+00 1.0000000e+00 1.2000000e+00 1.3000000e+00 1.5000000e+00 2.7000000e+00 2.9000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 2.7000000e+00 9.0000000e-01 1.7000000e+00 2.0000000e+00 8.0000000e-01 9.0000000e-01 1.6000000e+00 1.8000000e+00 2.1000000e+00 2.4000000e+00 1.6000000e+00 1.1000000e+00 1.6000000e+00 2.1000000e+00 1.6000000e+00 1.5000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 1.1000000e+00 1.1000000e+00 1.9000000e+00 1.7000000e+00 1.2000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 1.1000000e+00 9.0000000e-01 9.0000000e-01 9.0000000e-01 1.2000000e+00 3.0000000e-01 8.0000000e-01 2.7000000e+00 1.8000000e+00 2.6000000e+00 2.3000000e+00 2.5000000e+00 3.3000000e+00 1.2000000e+00 3.0000000e+00 2.5000000e+00 2.8000000e+00 1.8000000e+00 2.0000000e+00 2.2000000e+00 1.7000000e+00 1.8000000e+00 2.0000000e+00 2.2000000e+00 3.4000000e+00 3.6000000e+00 1.7000000e+00 2.4000000e+00 1.6000000e+00 3.4000000e+00 1.6000000e+00 2.4000000e+00 2.7000000e+00 1.5000000e+00 1.6000000e+00 2.3000000e+00 2.5000000e+00 2.8000000e+00 3.1000000e+00 2.3000000e+00 1.8000000e+00 2.3000000e+00 2.8000000e+00 2.3000000e+00 2.2000000e+00 1.5000000e+00 2.1000000e+00 2.3000000e+00 1.9000000e+00 1.8000000e+00 2.6000000e+00 2.4000000e+00 1.9000000e+00 1.7000000e+00 1.9000000e+00 2.1000000e+00 1.8000000e+00 3.0000000e-01 2.0000000e-01 6.0000000e-01 1.2000000e+00 1.0000000e-01 1.8000000e+00 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.6000000e+00 2.4000000e+00 7.0000000e-01 2.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 8.0000000e-01 1.1000000e+00 1.1000000e+00 1.3000000e+00 2.5000000e+00 2.7000000e+00 8.0000000e-01 1.5000000e+00 7.0000000e-01 2.5000000e+00 7.0000000e-01 1.5000000e+00 1.8000000e+00 6.0000000e-01 7.0000000e-01 1.4000000e+00 1.6000000e+00 1.9000000e+00 2.3000000e+00 1.4000000e+00 9.0000000e-01 1.4000000e+00 2.1000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.3000000e+00 1.4000000e+00 1.3000000e+00 9.0000000e-01 1.7000000e+00 1.5000000e+00 1.1000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 1.0000000e-01 5.0000000e-01 1.2000000e+00 2.0000000e-01 1.8000000e+00 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.6000000e+00 2.4000000e+00 8.0000000e-01 2.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 8.0000000e-01 1.2000000e+00 1.1000000e+00 1.3000000e+00 2.5000000e+00 2.7000000e+00 8.0000000e-01 1.5000000e+00 8.0000000e-01 2.5000000e+00 7.0000000e-01 1.5000000e+00 1.8000000e+00 6.0000000e-01 7.0000000e-01 1.4000000e+00 1.6000000e+00 1.9000000e+00 2.2000000e+00 1.4000000e+00 9.0000000e-01 1.4000000e+00 2.0000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.2000000e+00 1.4000000e+00 1.2000000e+00 9.0000000e-01 1.7000000e+00 1.5000000e+00 1.1000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 5.0000000e-01 1.2000000e+00 1.0000000e-01 1.8000000e+00 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.6000000e+00 2.4000000e+00 8.0000000e-01 2.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 8.0000000e-01 1.1000000e+00 1.1000000e+00 1.3000000e+00 2.5000000e+00 2.7000000e+00 8.0000000e-01 1.5000000e+00 7.0000000e-01 2.5000000e+00 7.0000000e-01 1.5000000e+00 1.8000000e+00 6.0000000e-01 7.0000000e-01 1.4000000e+00 1.6000000e+00 1.9000000e+00 2.2000000e+00 1.4000000e+00 9.0000000e-01 1.4000000e+00 2.0000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.2000000e+00 1.4000000e+00 1.2000000e+00 9.0000000e-01 1.7000000e+00 1.5000000e+00 1.0000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 1.3000000e+00 5.0000000e-01 1.7000000e+00 8.0000000e-01 1.6000000e+00 1.3000000e+00 1.5000000e+00 2.3000000e+00 1.3000000e+00 2.0000000e+00 1.5000000e+00 1.8000000e+00 8.0000000e-01 1.0000000e+00 1.2000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 1.2000000e+00 2.4000000e+00 2.6000000e+00 7.0000000e-01 1.4000000e+00 7.0000000e-01 2.4000000e+00 6.0000000e-01 1.4000000e+00 1.7000000e+00 5.0000000e-01 6.0000000e-01 1.3000000e+00 1.5000000e+00 1.8000000e+00 2.1000000e+00 1.3000000e+00 8.0000000e-01 1.3000000e+00 1.8000000e+00 1.3000000e+00 1.2000000e+00 5.0000000e-01 1.1000000e+00 1.3000000e+00 1.0000000e+00 8.0000000e-01 1.6000000e+00 1.4000000e+00 1.0000000e+00 7.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 1.1000000e+00 3.0000000e+00 2.1000000e+00 2.9000000e+00 2.6000000e+00 2.8000000e+00 3.6000000e+00 1.5000000e+00 3.3000000e+00 2.8000000e+00 3.1000000e+00 2.1000000e+00 2.3000000e+00 2.5000000e+00 2.0000000e+00 2.1000000e+00 2.3000000e+00 2.5000000e+00 3.7000000e+00 3.9000000e+00 2.0000000e+00 2.7000000e+00 1.9000000e+00 3.7000000e+00 1.9000000e+00 2.7000000e+00 3.0000000e+00 1.8000000e+00 1.9000000e+00 2.6000000e+00 2.8000000e+00 3.1000000e+00 3.4000000e+00 2.6000000e+00 2.1000000e+00 2.6000000e+00 3.1000000e+00 2.6000000e+00 2.5000000e+00 1.8000000e+00 2.4000000e+00 2.6000000e+00 2.1000000e+00 2.1000000e+00 2.9000000e+00 2.7000000e+00 2.2000000e+00 2.0000000e+00 2.2000000e+00 2.4000000e+00 2.1000000e+00 1.9000000e+00 1.0000000e+00 1.8000000e+00 1.5000000e+00 1.7000000e+00 2.5000000e+00 8.0000000e-01 2.2000000e+00 1.7000000e+00 2.0000000e+00 1.0000000e+00 1.2000000e+00 1.4000000e+00 9.0000000e-01 1.1000000e+00 1.2000000e+00 1.4000000e+00 2.6000000e+00 2.8000000e+00 9.0000000e-01 1.6000000e+00 8.0000000e-01 2.6000000e+00 8.0000000e-01 1.6000000e+00 1.9000000e+00 7.0000000e-01 8.0000000e-01 1.5000000e+00 1.7000000e+00 2.0000000e+00 2.3000000e+00 1.5000000e+00 1.0000000e+00 1.5000000e+00 2.0000000e+00 1.5000000e+00 1.4000000e+00 7.0000000e-01 1.3000000e+00 1.5000000e+00 1.2000000e+00 1.0000000e+00 1.8000000e+00 1.6000000e+00 1.1000000e+00 9.0000000e-01 1.1000000e+00 1.3000000e+00 1.0000000e+00 9.0000000e-01 8.0000000e-01 7.0000000e-01 3.0000000e-01 1.3000000e+00 1.5000000e+00 1.0000000e+00 8.0000000e-01 9.0000000e-01 9.0000000e-01 7.0000000e-01 5.0000000e-01 1.0000000e+00 9.0000000e-01 7.0000000e-01 7.0000000e-01 1.4000000e+00 1.4000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.4000000e+00 1.1000000e+00 4.0000000e-01 9.0000000e-01 1.2000000e+00 1.1000000e+00 5.0000000e-01 9.0000000e-01 1.1000000e+00 1.6000000e+00 5.0000000e-01 1.0000000e+00 1.1000000e+00 1.4000000e+00 4.0000000e-01 7.0000000e-01 1.2000000e+00 6.0000000e-01 4.0000000e-01 9.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 8.0000000e-01 6.0000000e-01 9.0000000e-01 1.3000000e+00 5.0000000e-01 7.0000000e-01 1.8000000e+00 9.0000000e-01 1.5000000e+00 9.0000000e-01 1.4000000e+00 7.0000000e-01 6.0000000e-01 1.0000000e+00 2.0000000e-01 5.0000000e-01 6.0000000e-01 7.0000000e-01 1.9000000e+00 1.9000000e+00 5.0000000e-01 1.1000000e+00 2.0000000e-01 1.9000000e+00 5.0000000e-01 9.0000000e-01 1.4000000e+00 4.0000000e-01 3.0000000e-01 6.0000000e-01 1.4000000e+00 1.6000000e+00 2.1000000e+00 6.0000000e-01 5.0000000e-01 5.0000000e-01 1.9000000e+00 7.0000000e-01 6.0000000e-01 3.0000000e-01 1.1000000e+00 9.0000000e-01 1.1000000e+00 0.0000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 5.0000000e-01 7.0000000e-01 7.0000000e-01 3.0000000e-01 8.0000000e-01 6.0000000e-01 7.0000000e-01 2.2000000e+00 4.0000000e-01 5.0000000e-01 6.0000000e-01 8.0000000e-01 7.0000000e-01 4.0000000e-01 1.4000000e+00 1.3000000e+00 7.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 1.1000000e+00 2.0000000e-01 1.5000000e+00 8.0000000e-01 1.0000000e+00 4.0000000e-01 3.0000000e-01 1.1000000e+00 1.0000000e+00 7.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 7.0000000e-01 8.0000000e-01 1.0000000e+00 6.0000000e-01 8.0000000e-01 7.0000000e-01 1.1000000e+00 5.0000000e-01 4.0000000e-01 8.0000000e-01 1.3000000e+00 3.0000000e-01 4.0000000e-01 7.0000000e-01 9.0000000e-01 7.0000000e-01 9.0000000e-01 1.2000000e+00 4.0000000e-01 1.3000000e+00 1.4000000e+00 1.0000000e+00 4.0000000e-01 9.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 2.0000000e-01 1.4000000e+00 1.4000000e+00 7.0000000e-01 6.0000000e-01 7.0000000e-01 1.4000000e+00 7.0000000e-01 4.0000000e-01 9.0000000e-01 8.0000000e-01 7.0000000e-01 3.0000000e-01 9.0000000e-01 1.1000000e+00 1.6000000e+00 4.0000000e-01 5.0000000e-01 4.0000000e-01 1.4000000e+00 6.0000000e-01 2.0000000e-01 8.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 1.1000000e+00 1.6000000e+00 8.0000000e-01 5.0000000e-01 7.0000000e-01 7.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 7.0000000e-01 5.0000000e-01 4.0000000e-01 1.2000000e+00 1.2000000e+00 8.0000000e-01 4.0000000e-01 9.0000000e-01 1.2000000e+00 9.0000000e-01 3.0000000e-01 7.0000000e-01 1.0000000e+00 9.0000000e-01 2.0000000e-01 7.0000000e-01 9.0000000e-01 1.4000000e+00 2.0000000e-01 7.0000000e-01 8.0000000e-01 1.2000000e+00 4.0000000e-01 4.0000000e-01 1.0000000e+00 4.0000000e-01 2.0000000e-01 7.0000000e-01 7.0000000e-01 3.0000000e-01 3.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 2.7000000e+00 3.0000000e-01 9.0000000e-01 6.0000000e-01 1.5000000e+00 1.3000000e+00 1.1000000e+00 1.9000000e+00 1.8000000e+00 1.3000000e+00 1.1000000e+00 8.0000000e-01 4.0000000e-01 1.6000000e+00 9.0000000e-01 2.0000000e+00 2.0000000e-01 1.7000000e+00 9.0000000e-01 6.0000000e-01 1.8000000e+00 1.7000000e+00 1.2000000e+00 8.0000000e-01 5.0000000e-01 8.0000000e-01 1.2000000e+00 1.5000000e+00 1.5000000e+00 5.0000000e-01 1.3000000e+00 1.2000000e+00 1.8000000e+00 1.2000000e+00 1.0000000e+00 1.5000000e+00 1.8000000e+00 8.0000000e-01 9.0000000e-01 1.4000000e+00 1.6000000e+00 1.4000000e+00 1.4000000e+00 1.7000000e+00 2.4000000e+00 1.8000000e+00 2.3000000e+00 1.6000000e+00 1.5000000e+00 1.9000000e+00 8.0000000e-01 9.0000000e-01 1.5000000e+00 1.6000000e+00 2.8000000e+00 2.8000000e+00 1.1000000e+00 2.0000000e+00 7.0000000e-01 2.8000000e+00 1.4000000e+00 1.8000000e+00 2.3000000e+00 1.3000000e+00 1.2000000e+00 1.5000000e+00 2.3000000e+00 2.5000000e+00 3.0000000e+00 1.5000000e+00 1.4000000e+00 1.2000000e+00 2.8000000e+00 1.4000000e+00 1.5000000e+00 1.1000000e+00 2.0000000e+00 1.8000000e+00 2.0000000e+00 9.0000000e-01 1.9000000e+00 1.8000000e+00 1.8000000e+00 1.4000000e+00 1.6000000e+00 1.3000000e+00 1.0000000e+00 6.0000000e-01 7.0000000e-01 1.2000000e+00 1.0000000e+00 8.0000000e-01 1.6000000e+00 1.5000000e+00 1.0000000e+00 8.0000000e-01 9.0000000e-01 6.0000000e-01 1.3000000e+00 6.0000000e-01 1.7000000e+00 4.0000000e-01 1.4000000e+00 6.0000000e-01 3.0000000e-01 1.5000000e+00 1.4000000e+00 9.0000000e-01 5.0000000e-01 2.0000000e-01 9.0000000e-01 9.0000000e-01 1.2000000e+00 1.2000000e+00 5.0000000e-01 1.0000000e+00 9.0000000e-01 1.5000000e+00 9.0000000e-01 7.0000000e-01 1.2000000e+00 1.5000000e+00 5.0000000e-01 7.0000000e-01 1.1000000e+00 1.3000000e+00 1.1000000e+00 1.1000000e+00 1.4000000e+00 1.1000000e+00 7.0000000e-01 5.0000000e-01 5.0000000e-01 1.0000000e+00 9.0000000e-01 7.0000000e-01 5.0000000e-01 1.3000000e+00 1.1000000e+00 8.0000000e-01 7.0000000e-01 1.1000000e+00 1.0000000e+00 9.0000000e-01 8.0000000e-01 7.0000000e-01 1.0000000e+00 9.0000000e-01 3.0000000e-01 5.0000000e-01 7.0000000e-01 1.3000000e+00 4.0000000e-01 7.0000000e-01 6.0000000e-01 1.0000000e+00 9.0000000e-01 6.0000000e-01 1.0000000e+00 6.0000000e-01 6.0000000e-01 7.0000000e-01 9.0000000e-01 7.0000000e-01 8.0000000e-01 6.0000000e-01 8.0000000e-01 6.0000000e-01 9.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 6.0000000e-01 1.5000000e+00 1.4000000e+00 8.0000000e-01 7.0000000e-01 6.0000000e-01 1.0000000e+00 1.4000000e+00 4.0000000e-01 1.6000000e+00 8.0000000e-01 1.2000000e+00 5.0000000e-01 7.0000000e-01 1.3000000e+00 1.2000000e+00 8.0000000e-01 9.0000000e-01 8.0000000e-01 7.0000000e-01 8.0000000e-01 1.0000000e+00 1.1000000e+00 6.0000000e-01 9.0000000e-01 8.0000000e-01 1.3000000e+00 7.0000000e-01 5.0000000e-01 1.0000000e+00 1.4000000e+00 4.0000000e-01 5.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 1.0000000e+00 1.3000000e+00 5.0000000e-01 4.0000000e-01 8.0000000e-01 7.0000000e-01 3.0000000e-01 4.0000000e-01 1.6000000e+00 1.8000000e+00 1.0000000e+00 6.0000000e-01 9.0000000e-01 1.6000000e+00 5.0000000e-01 6.0000000e-01 9.0000000e-01 4.0000000e-01 4.0000000e-01 5.0000000e-01 7.0000000e-01 1.0000000e+00 1.4000000e+00 5.0000000e-01 5.0000000e-01 6.0000000e-01 1.2000000e+00 5.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 6.0000000e-01 3.0000000e-01 7.0000000e-01 2.0000000e-01 3.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 1.4000000e+00 1.6000000e+00 5.0000000e-01 5.0000000e-01 8.0000000e-01 1.4000000e+00 4.0000000e-01 6.0000000e-01 8.0000000e-01 5.0000000e-01 4.0000000e-01 3.0000000e-01 8.0000000e-01 1.0000000e+00 1.5000000e+00 3.0000000e-01 4.0000000e-01 5.0000000e-01 1.3000000e+00 7.0000000e-01 4.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 4.0000000e-01 3.0000000e-01 3.0000000e-01 7.0000000e-01 5.0000000e-01 1.1000000e+00 1.0000000e+00 4.0000000e-01 3.0000000e-01 1.2000000e+00 1.4000000e+00 8.0000000e-01 2.0000000e-01 1.2000000e+00 1.2000000e+00 6.0000000e-01 3.0000000e-01 5.0000000e-01 7.0000000e-01 7.0000000e-01 4.0000000e-01 5.0000000e-01 6.0000000e-01 1.1000000e+00 4.0000000e-01 6.0000000e-01 7.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e-01 3.0000000e-01 4.0000000e-01 1.0000000e+00 4.0000000e-01 4.0000000e-01 3.0000000e-01 5.0000000e-01 3.0000000e-01 6.0000000e-01 9.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 2.0000000e+00 2.0000000e+00 5.0000000e-01 1.2000000e+00 3.0000000e-01 2.0000000e+00 6.0000000e-01 1.0000000e+00 1.5000000e+00 5.0000000e-01 5.0000000e-01 7.0000000e-01 1.5000000e+00 1.7000000e+00 2.2000000e+00 7.0000000e-01 6.0000000e-01 6.0000000e-01 2.0000000e+00 9.0000000e-01 7.0000000e-01 5.0000000e-01 1.2000000e+00 1.0000000e+00 1.2000000e+00 2.0000000e-01 1.1000000e+00 1.0000000e+00 1.0000000e+00 6.0000000e-01 8.0000000e-01 9.0000000e-01 5.0000000e-01 6.0000000e-01 7.0000000e-01 1.9000000e+00 1.9000000e+00 9.0000000e-01 1.1000000e+00 4.0000000e-01 1.9000000e+00 6.0000000e-01 9.0000000e-01 1.4000000e+00 6.0000000e-01 6.0000000e-01 6.0000000e-01 1.4000000e+00 1.6000000e+00 2.1000000e+00 6.0000000e-01 9.0000000e-01 1.0000000e+00 1.9000000e+00 6.0000000e-01 6.0000000e-01 6.0000000e-01 1.1000000e+00 9.0000000e-01 1.1000000e+00 5.0000000e-01 1.0000000e+00 9.0000000e-01 9.0000000e-01 5.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 5.0000000e-01 1.4000000e+00 1.6000000e+00 1.0000000e+00 5.0000000e-01 8.0000000e-01 1.4000000e+00 5.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 4.0000000e-01 8.0000000e-01 1.0000000e+00 1.5000000e+00 4.0000000e-01 8.0000000e-01 9.0000000e-01 1.3000000e+00 3.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 6.0000000e-01 4.0000000e-01 3.0000000e-01 7.0000000e-01 3.0000000e-01 2.0000000e-01 5.0000000e-01 1.2000000e+00 1.4000000e+00 8.0000000e-01 5.0000000e-01 9.0000000e-01 1.2000000e+00 6.0000000e-01 3.0000000e-01 7.0000000e-01 7.0000000e-01 6.0000000e-01 3.0000000e-01 7.0000000e-01 9.0000000e-01 1.4000000e+00 4.0000000e-01 4.0000000e-01 4.0000000e-01 1.2000000e+00 6.0000000e-01 1.0000000e-01 7.0000000e-01 4.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 1.2000000e+00 1.7000000e+00 1.0000000e+00 2.1000000e+00 1.0000000e+00 1.8000000e+00 1.0000000e+00 7.0000000e-01 1.9000000e+00 1.8000000e+00 1.3000000e+00 9.0000000e-01 1.0000000e+00 3.0000000e-01 1.3000000e+00 1.6000000e+00 1.6000000e+00 8.0000000e-01 1.4000000e+00 1.3000000e+00 1.9000000e+00 1.3000000e+00 1.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.0000000e+00 1.5000000e+00 1.7000000e+00 1.5000000e+00 1.5000000e+00 1.8000000e+00 1.9000000e+00 1.2000000e+00 2.1000000e+00 3.0000000e-01 2.0000000e+00 1.2000000e+00 9.0000000e-01 2.1000000e+00 2.0000000e+00 1.3000000e+00 1.1000000e+00 8.0000000e-01 1.2000000e+00 1.3000000e+00 1.8000000e+00 1.6000000e+00 8.0000000e-01 1.4000000e+00 1.4000000e+00 2.1000000e+00 1.5000000e+00 1.3000000e+00 1.8000000e+00 1.9000000e+00 1.0000000e+00 1.2000000e+00 1.7000000e+00 1.9000000e+00 1.7000000e+00 1.5000000e+00 1.8000000e+00 1.0000000e+00 6.0000000e-01 1.7000000e+00 5.0000000e-01 1.1000000e+00 1.2000000e+00 6.0000000e-01 8.0000000e-01 6.0000000e-01 1.2000000e+00 1.4000000e+00 1.9000000e+00 7.0000000e-01 6.0000000e-01 6.0000000e-01 1.7000000e+00 1.2000000e+00 9.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 9.0000000e-01 5.0000000e-01 1.0000000e+00 1.1000000e+00 8.0000000e-01 4.0000000e-01 8.0000000e-01 1.2000000e+00 8.0000000e-01 1.3000000e+00 1.0000000e+00 8.0000000e-01 2.0000000e-01 5.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 1.0000000e+00 5.0000000e-01 8.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 5.0000000e-01 9.0000000e-01 3.0000000e-01 2.0000000e-01 6.0000000e-01 1.1000000e+00 2.0000000e-01 2.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 7.0000000e-01 1.0000000e+00 2.1000000e+00 7.0000000e-01 1.1000000e+00 1.6000000e+00 6.0000000e-01 5.0000000e-01 8.0000000e-01 1.6000000e+00 1.8000000e+00 2.3000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 2.1000000e+00 7.0000000e-01 8.0000000e-01 4.0000000e-01 1.3000000e+00 1.1000000e+00 1.3000000e+00 2.0000000e-01 1.2000000e+00 1.1000000e+00 1.1000000e+00 7.0000000e-01 9.0000000e-01 6.0000000e-01 3.0000000e-01 1.8000000e+00 1.0000000e+00 7.0000000e-01 1.9000000e+00 1.8000000e+00 1.3000000e+00 9.0000000e-01 6.0000000e-01 1.0000000e+00 1.3000000e+00 1.6000000e+00 1.6000000e+00 6.0000000e-01 1.4000000e+00 1.3000000e+00 1.9000000e+00 1.3000000e+00 1.1000000e+00 1.6000000e+00 1.9000000e+00 9.0000000e-01 1.0000000e+00 1.5000000e+00 1.7000000e+00 1.5000000e+00 1.5000000e+00 1.8000000e+00 8.0000000e-01 1.1000000e+00 1.0000000e-01 3.0000000e-01 7.0000000e-01 9.0000000e-01 1.2000000e+00 1.6000000e+00 7.0000000e-01 3.0000000e-01 7.0000000e-01 1.4000000e+00 7.0000000e-01 6.0000000e-01 3.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 5.0000000e-01 1.0000000e+00 8.0000000e-01 5.0000000e-01 2.0000000e-01 3.0000000e-01 7.0000000e-01 4.0000000e-01 5.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 1.2000000e+00 5.0000000e-01 6.0000000e-01 7.0000000e-01 1.0000000e+00 4.0000000e-01 3.0000000e-01 9.0000000e-01 3.0000000e-01 3.0000000e-01 6.0000000e-01 9.0000000e-01 2.0000000e-01 4.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 8.0000000e-01 1.2000000e+00 1.1000000e+00 8.0000000e-01 2.0000000e-01 4.0000000e-01 7.0000000e-01 8.0000000e-01 9.0000000e-01 1.1000000e+00 5.0000000e-01 9.0000000e-01 8.0000000e-01 1.2000000e+00 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.4000000e+00 5.0000000e-01 7.0000000e-01 8.0000000e-01 1.0000000e+00 8.0000000e-01 1.0000000e+00 1.3000000e+00 2.0000000e-01 8.0000000e-01 1.0000000e+00 1.3000000e+00 1.7000000e+00 8.0000000e-01 3.0000000e-01 8.0000000e-01 1.5000000e+00 8.0000000e-01 7.0000000e-01 2.0000000e-01 7.0000000e-01 8.0000000e-01 7.0000000e-01 4.0000000e-01 1.1000000e+00 9.0000000e-01 5.0000000e-01 3.0000000e-01 4.0000000e-01 6.0000000e-01 3.0000000e-01 7.0000000e-01 1.1000000e+00 1.3000000e+00 1.8000000e+00 7.0000000e-01 3.0000000e-01 7.0000000e-01 1.6000000e+00 7.0000000e-01 6.0000000e-01 1.0000000e-01 8.0000000e-01 7.0000000e-01 8.0000000e-01 3.0000000e-01 1.0000000e+00 8.0000000e-01 6.0000000e-01 5.0000000e-01 4.0000000e-01 5.0000000e-01 2.0000000e-01 8.0000000e-01 1.0000000e+00 1.5000000e+00 1.0000000e-01 6.0000000e-01 7.0000000e-01 1.3000000e+00 6.0000000e-01 3.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 8.0000000e-01 8.0000000e-01 9.0000000e-01 1.1000000e+00 7.0000000e-01 9.0000000e-01 8.0000000e-01 1.2000000e+00 5.0000000e-01 8.0000000e-01 7.0000000e-01 1.4000000e+00 7.0000000e-01 9.0000000e-01 7.0000000e-01 9.0000000e-01 7.0000000e-01 1.0000000e+00 1.3000000e+00 1.0000000e+00 1.0000000e+00 1.1000000e+00 1.3000000e+00 4.0000000e-01 1.1000000e+00 1.0000000e+00 1.4000000e+00 7.0000000e-01 7.0000000e-01 1.0000000e+00 1.6000000e+00 6.0000000e-01 7.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 1.2000000e+00 1.5000000e+00 1.5000000e+00 1.6000000e+00 1.8000000e+00 8.0000000e-01 1.6000000e+00 1.5000000e+00 1.9000000e+00 1.0000000e+00 1.2000000e+00 1.3000000e+00 2.1000000e+00 1.1000000e+00 1.2000000e+00 1.2000000e+00 1.6000000e+00 1.4000000e+00 1.7000000e+00 2.0000000e+00 7.0000000e-01 8.0000000e-01 1.3000000e+00 6.0000000e-01 4.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 6.0000000e-01 5.0000000e-01 5.0000000e-01 1.4000000e+00 9.0000000e-01 4.0000000e-01 3.0000000e-01 6.0000000e-01 9.0000000e-01 8.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 8.0000000e-01 4.0000000e-01 5.0000000e-01 8.0000000e-01 4.0000000e-01 1.6000000e+00 1.0000000e+00 5.0000000e-01 8.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 9.0000000e-01 1.1000000e+00 9.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 1.4000000e+00 1.3000000e+00 1.7000000e+00 8.0000000e-01 1.0000000e+00 1.0000000e+00 1.9000000e+00 9.0000000e-01 1.0000000e+00 1.0000000e+00 1.4000000e+00 1.2000000e+00 1.5000000e+00 1.8000000e+00 6.0000000e-01 8.0000000e-01 6.0000000e-01 4.0000000e-01 6.0000000e-01 7.0000000e-01 5.0000000e-01 4.0000000e-01 4.0000000e-01 9.0000000e-01 4.0000000e-01 2.0000000e-01 6.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 6.0000000e-01 5.0000000e-01 7.0000000e-01 5.0000000e-01 6.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 9.0000000e-01 8.0000000e-01 9.0000000e-01 3.0000000e-01 1.1000000e+00 9.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 1.1000000e+00 5.0000000e-01 4.0000000e-01 2.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e+00 5.0000000e-01 9.0000000e-01 3.0000000e-01 2.0000000e-01 4.0000000e-01 6.0000000e-01 4.0000000e-01 5.0000000e-01 8.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 2.0000000e-01 6.0000000e-01 4.0000000e-01 7.0000000e-01 1.0000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 5.0000000e-01 7.0000000e-01 7.0000000e-01 3.0000000e-01 2.0000000e-01 7.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 9.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 5.0000000e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..786486295935319c03a60a349f03328c127935b9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-chebyshev-ml.txt @@ -0,0 +1 @@ + 8.9084734e-01 9.3573853e-01 9.3507398e-01 9.6040691e-01 9.2918157e-01 9.6617342e-01 9.0430930e-01 9.5753424e-01 8.7106898e-01 9.2169905e-01 9.7401159e-01 8.9013416e-01 9.3956689e-01 9.0041896e-01 9.2588355e-01 9.3849417e-01 8.9713468e-01 9.1481804e-01 9.7500539e-01 9.0012586e-01 9.0962559e-01 8.5860091e-01 8.6981095e-01 8.9995771e-01 8.8070172e-01 9.1456657e-01 8.6711474e-01 9.2593917e-01 8.7560376e-01 8.5193121e-01 9.0898542e-01 8.7765302e-01 8.6555584e-01 8.6093485e-01 9.0447028e-01 8.7614405e-01 9.4803522e-01 8.4998062e-01 7.8398996e-01 8.9538612e-01 8.3902291e-01 9.9039470e-01 9.5480519e-01 8.9152195e-01 9.1623329e-01 7.9094921e-01 9.1777100e-01 9.8972335e-01 9.0429093e-01 8.7646362e-01 9.2136649e-01 9.7178177e-01 8.9610979e-01 9.4710327e-01 9.3612450e-01 9.0241499e-01 7.7992538e-01 8.7262126e-01 9.3325183e-01 8.5796531e-01 9.4267977e-01 6.7224167e-01 7.9568368e-01 8.6411267e-01 9.3311642e-01 9.0160114e-01 9.0698887e-01 8.5833256e-01 9.6902830e-01 9.5072298e-01 8.6808495e-01 9.7879599e-01 8.8060729e-01 8.2818573e-01 8.4366706e-01 8.4506700e-01 9.4532981e-01 9.1792306e-01 7.8917825e-01 9.8337805e-01 8.1751613e-01 9.3037855e-01 9.1618832e-01 8.6568874e-01 8.9751397e-01 8.7923710e-01 8.6814329e-01 9.0330164e-01 8.2426213e-01 9.4644643e-01 8.8431293e-01 8.8497426e-01 9.0633818e-01 9.5537161e-01 8.2167575e-01 8.7771053e-01 9.0681167e-01 8.7626143e-01 8.7463464e-01 9.8033940e-01 9.2920881e-01 9.5108549e-01 9.1287466e-01 8.0052218e-01 9.2409517e-01 8.8252650e-01 8.7873923e-01 9.2989402e-01 9.1985043e-01 9.6172646e-01 8.8223856e-01 9.4477822e-01 8.8310948e-01 9.4461306e-01 9.1875210e-01 9.1233363e-01 9.2124013e-01 9.5460897e-01 8.4640982e-01 9.0882657e-01 9.8169468e-01 9.7828355e-01 8.4150533e-01 8.6888923e-01 9.7138825e-01 8.7988144e-01 9.6720910e-01 8.9450147e-01 9.5331584e-01 8.8871809e-01 8.9736685e-01 8.6258146e-01 9.1331565e-01 9.0968870e-01 9.4833654e-01 9.0536967e-01 9.5099871e-01 8.0251958e-01 9.2526150e-01 9.8971957e-01 9.0340947e-01 9.4955892e-01 9.6838162e-01 8.7534901e-01 9.1178797e-01 9.2649154e-01 9.5260993e-01 9.3178143e-01 9.4943000e-01 8.7816171e-01 9.6506542e-01 8.3422958e-01 9.3443585e-01 9.3220084e-01 8.5706573e-01 8.4666325e-01 9.0474744e-01 9.1080644e-01 9.2406899e-01 8.7901768e-01 9.3265263e-01 9.5992829e-01 9.5696271e-01 9.1932272e-01 8.0937044e-01 9.0904917e-01 8.9516756e-01 9.4797906e-01 8.4159421e-01 9.6773901e-01 9.7099825e-01 9.6941820e-01 9.8174088e-01 9.7569951e-01 9.3655362e-01 8.4130333e-01 9.5994549e-01 8.4235414e-01 9.1429418e-01 9.3418117e-01 8.4600977e-01 8.8166496e-01 8.7594776e-01 8.8571112e-01 9.6308174e-01 9.5315927e-01 8.6997519e-01 8.9383032e-01 9.4686804e-01 9.4399596e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..6722928a4a4491c74d3fef5205276b532b15dcbf --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt @@ -0,0 +1 @@ + 7.0000000e-01 8.0000000e-01 1.0000000e+00 2.0000000e-01 1.2000000e+00 7.0000000e-01 3.0000000e-01 1.3000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.8000000e+00 1.0000000e+00 1.0000000e-01 1.3000000e+00 5.0000000e-01 7.0000000e-01 5.0000000e-01 1.0000000e+00 8.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 2.0000000e-01 2.0000000e-01 9.0000000e-01 9.0000000e-01 7.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 1.3000000e+00 2.0000000e-01 3.0000000e-01 2.0000000e+00 1.1000000e+00 7.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.3000000e+00 6.6000000e+00 5.5000000e+00 6.1000000e+00 4.0000000e+00 6.4000000e+00 4.6000000e+00 4.5000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.4000000e+00 6.2000000e+00 5.4000000e+00 5.0000000e+00 6.8000000e+00 4.9000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 4.8000000e+00 4.6000000e+00 5.0000000e+00 6.8000000e+00 5.2000000e+00 5.5000000e+00 6.6000000e+00 6.5000000e+00 4.8000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 5.2000000e+00 4.0000000e+00 5.2000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 3.5000000e+00 5.1000000e+00 8.3000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 5.8000000e+00 9.3000000e+00 8.6000000e+00 9.2000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 1.0200000e+01 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.5000000e+00 1.0400000e+01 7.1000000e+00 8.0000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 9.9000000e+00 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 7.7000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.4000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.3000000e+00 6.6000000e+00 5.0000000e-01 5.0000000e-01 7.0000000e-01 1.9000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 1.3000000e+00 7.0000000e-01 2.0000000e-01 1.0000000e+00 2.1000000e+00 2.5000000e+00 1.7000000e+00 8.0000000e-01 2.0000000e+00 1.2000000e+00 1.2000000e+00 1.2000000e+00 1.3000000e+00 1.1000000e+00 1.0000000e+00 3.0000000e-01 9.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 4.0000000e-01 1.2000000e+00 1.6000000e+00 1.8000000e+00 3.0000000e-01 5.0000000e-01 1.2000000e+00 3.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 1.3000000e+00 8.0000000e-01 1.2000000e+00 1.7000000e+00 2.0000000e-01 1.2000000e+00 5.0000000e-01 1.2000000e+00 4.0000000e-01 6.8000000e+00 6.1000000e+00 6.9000000e+00 5.0000000e+00 6.3000000e+00 5.2000000e+00 6.4000000e+00 3.3000000e+00 6.1000000e+00 4.3000000e+00 4.0000000e+00 5.1000000e+00 5.3000000e+00 5.8000000e+00 4.1000000e+00 6.1000000e+00 5.1000000e+00 4.7000000e+00 6.5000000e+00 4.6000000e+00 6.2000000e+00 5.1000000e+00 6.7000000e+00 5.7000000e+00 5.6000000e+00 5.9000000e+00 6.7000000e+00 6.9000000e+00 5.6000000e+00 4.1000000e+00 4.5000000e+00 4.3000000e+00 4.7000000e+00 6.5000000e+00 4.9000000e+00 6.0000000e+00 6.5000000e+00 6.2000000e+00 4.5000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 4.9000000e+00 3.5000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 3.2000000e+00 4.8000000e+00 8.6000000e+00 6.6000000e+00 8.6000000e+00 7.3000000e+00 8.0000000e+00 9.8000000e+00 5.1000000e+00 9.0000000e+00 8.3000000e+00 9.9000000e+00 7.3000000e+00 7.4000000e+00 7.9000000e+00 6.7000000e+00 7.0000000e+00 7.7000000e+00 7.3000000e+00 1.0900000e+01 1.0800000e+01 6.8000000e+00 8.6000000e+00 6.2000000e+00 1.0100000e+01 6.8000000e+00 8.3000000e+00 8.7000000e+00 6.5000000e+00 6.3000000e+00 7.8000000e+00 8.1000000e+00 9.1000000e+00 1.0600000e+01 7.9000000e+00 6.6000000e+00 7.0000000e+00 9.6000000e+00 8.2000000e+00 7.3000000e+00 6.1000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.6000000e+00 8.7000000e+00 8.7000000e+00 7.7000000e+00 7.2000000e+00 7.2000000e+00 7.8000000e+00 6.3000000e+00 4.0000000e-01 8.0000000e-01 2.0000000e+00 5.0000000e-01 7.0000000e-01 7.0000000e-01 6.0000000e-01 1.4000000e+00 6.0000000e-01 5.0000000e-01 9.0000000e-01 2.0000000e+00 2.6000000e+00 1.6000000e+00 9.0000000e-01 2.1000000e+00 1.3000000e+00 1.3000000e+00 1.3000000e+00 8.0000000e-01 1.2000000e+00 9.0000000e-01 8.0000000e-01 1.0000000e+00 1.0000000e+00 8.0000000e-01 3.0000000e-01 5.0000000e-01 1.3000000e+00 1.7000000e+00 1.9000000e+00 6.0000000e-01 4.0000000e-01 1.1000000e+00 6.0000000e-01 5.0000000e-01 8.0000000e-01 7.0000000e-01 1.2000000e+00 3.0000000e-01 1.3000000e+00 1.8000000e+00 5.0000000e-01 1.3000000e+00 2.0000000e-01 1.3000000e+00 5.0000000e-01 6.9000000e+00 6.2000000e+00 7.2000000e+00 5.5000000e+00 6.8000000e+00 5.7000000e+00 6.5000000e+00 3.8000000e+00 6.6000000e+00 4.8000000e+00 4.5000000e+00 5.6000000e+00 5.8000000e+00 6.3000000e+00 4.6000000e+00 6.4000000e+00 5.6000000e+00 5.2000000e+00 7.0000000e+00 5.1000000e+00 6.3000000e+00 5.6000000e+00 7.2000000e+00 6.2000000e+00 6.1000000e+00 6.4000000e+00 7.2000000e+00 7.4000000e+00 6.1000000e+00 4.6000000e+00 5.0000000e+00 4.8000000e+00 5.2000000e+00 7.0000000e+00 5.4000000e+00 6.1000000e+00 6.8000000e+00 6.7000000e+00 5.0000000e+00 5.3000000e+00 5.5000000e+00 6.1000000e+00 5.4000000e+00 4.0000000e+00 5.4000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 3.7000000e+00 5.3000000e+00 8.7000000e+00 7.1000000e+00 9.1000000e+00 7.8000000e+00 8.5000000e+00 1.0300000e+01 5.6000000e+00 9.5000000e+00 8.8000000e+00 1.0000000e+01 7.4000000e+00 7.9000000e+00 8.4000000e+00 7.2000000e+00 7.5000000e+00 7.8000000e+00 7.8000000e+00 1.1000000e+01 1.1300000e+01 7.3000000e+00 8.7000000e+00 6.7000000e+00 1.0600000e+01 7.3000000e+00 8.4000000e+00 8.8000000e+00 7.0000000e+00 6.8000000e+00 8.3000000e+00 8.6000000e+00 9.6000000e+00 1.0700000e+01 8.4000000e+00 7.1000000e+00 7.5000000e+00 1.0100000e+01 8.3000000e+00 7.6000000e+00 6.6000000e+00 8.3000000e+00 8.6000000e+00 8.2000000e+00 7.1000000e+00 8.8000000e+00 8.8000000e+00 8.2000000e+00 7.7000000e+00 7.7000000e+00 7.9000000e+00 6.8000000e+00 1.0000000e+00 2.0000000e+00 5.0000000e-01 7.0000000e-01 5.0000000e-01 4.0000000e-01 1.4000000e+00 6.0000000e-01 5.0000000e-01 9.0000000e-01 2.4000000e+00 2.6000000e+00 2.0000000e+00 1.1000000e+00 2.1000000e+00 1.3000000e+00 1.3000000e+00 1.3000000e+00 1.0000000e+00 1.2000000e+00 9.0000000e-01 6.0000000e-01 1.0000000e+00 1.0000000e+00 1.0000000e+00 3.0000000e-01 3.0000000e-01 1.3000000e+00 1.7000000e+00 2.1000000e+00 4.0000000e-01 8.0000000e-01 1.5000000e+00 4.0000000e-01 5.0000000e-01 8.0000000e-01 1.1000000e+00 1.2000000e+00 5.0000000e-01 1.3000000e+00 1.8000000e+00 5.0000000e-01 1.3000000e+00 2.0000000e-01 1.3000000e+00 7.0000000e-01 6.9000000e+00 6.2000000e+00 7.0000000e+00 5.3000000e+00 6.6000000e+00 5.5000000e+00 6.5000000e+00 3.6000000e+00 6.4000000e+00 4.6000000e+00 4.3000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.4000000e+00 6.2000000e+00 5.4000000e+00 5.0000000e+00 6.8000000e+00 4.9000000e+00 6.3000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 4.8000000e+00 4.6000000e+00 5.0000000e+00 6.8000000e+00 5.2000000e+00 6.1000000e+00 6.6000000e+00 6.5000000e+00 4.8000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 5.2000000e+00 3.8000000e+00 5.2000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 3.5000000e+00 5.1000000e+00 8.7000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 5.4000000e+00 9.3000000e+00 8.6000000e+00 1.0000000e+01 7.4000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.8000000e+00 7.6000000e+00 1.1000000e+01 1.1100000e+01 7.1000000e+00 8.7000000e+00 6.5000000e+00 1.0400000e+01 7.1000000e+00 8.4000000e+00 8.8000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 1.0700000e+01 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 8.3000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.8000000e+00 8.8000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.9000000e+00 6.6000000e+00 1.2000000e+00 7.0000000e-01 3.0000000e-01 1.3000000e+00 8.0000000e-01 6.0000000e-01 6.0000000e-01 9.0000000e-01 1.7000000e+00 1.4000000e+00 1.8000000e+00 1.0000000e+00 3.0000000e-01 1.3000000e+00 5.0000000e-01 9.0000000e-01 5.0000000e-01 8.0000000e-01 1.0000000e+00 9.0000000e-01 8.0000000e-01 6.0000000e-01 4.0000000e-01 4.0000000e-01 9.0000000e-01 9.0000000e-01 9.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 7.0000000e-01 8.0000000e-01 1.3000000e+00 4.0000000e-01 3.0000000e-01 2.0000000e+00 1.1000000e+00 7.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 6.9000000e+00 6.2000000e+00 7.2000000e+00 5.5000000e+00 6.8000000e+00 5.7000000e+00 6.3000000e+00 4.0000000e+00 6.6000000e+00 4.8000000e+00 4.5000000e+00 5.6000000e+00 5.8000000e+00 6.3000000e+00 4.6000000e+00 6.4000000e+00 5.6000000e+00 5.2000000e+00 7.0000000e+00 5.1000000e+00 6.3000000e+00 5.6000000e+00 7.2000000e+00 6.2000000e+00 6.1000000e+00 6.4000000e+00 7.2000000e+00 7.4000000e+00 6.1000000e+00 4.6000000e+00 5.0000000e+00 4.8000000e+00 5.2000000e+00 7.0000000e+00 5.4000000e+00 5.7000000e+00 6.8000000e+00 6.7000000e+00 5.0000000e+00 5.3000000e+00 5.5000000e+00 6.1000000e+00 5.4000000e+00 4.0000000e+00 5.4000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 3.7000000e+00 5.3000000e+00 8.5000000e+00 7.1000000e+00 9.1000000e+00 7.8000000e+00 8.5000000e+00 1.0300000e+01 5.8000000e+00 9.5000000e+00 8.8000000e+00 9.2000000e+00 7.4000000e+00 7.9000000e+00 8.4000000e+00 7.2000000e+00 7.5000000e+00 7.8000000e+00 7.8000000e+00 1.0200000e+01 1.1300000e+01 7.3000000e+00 8.7000000e+00 6.7000000e+00 1.0600000e+01 7.3000000e+00 8.2000000e+00 8.8000000e+00 7.0000000e+00 6.8000000e+00 8.3000000e+00 8.6000000e+00 9.6000000e+00 9.9000000e+00 8.4000000e+00 7.1000000e+00 7.5000000e+00 1.0100000e+01 7.9000000e+00 7.6000000e+00 6.6000000e+00 8.3000000e+00 8.6000000e+00 8.2000000e+00 7.1000000e+00 8.8000000e+00 8.6000000e+00 8.2000000e+00 7.7000000e+00 7.7000000e+00 7.5000000e+00 6.8000000e+00 1.7000000e+00 1.3000000e+00 2.5000000e+00 1.8000000e+00 6.0000000e-01 1.4000000e+00 2.1000000e+00 2.9000000e+00 1.2000000e+00 1.0000000e+00 4.0000000e-01 1.1000000e+00 5.0000000e-01 7.0000000e-01 7.0000000e-01 7.0000000e-01 2.0000000e+00 1.0000000e+00 1.5000000e+00 1.6000000e+00 1.0000000e+00 1.0000000e+00 1.2000000e+00 1.7000000e+00 1.7000000e+00 7.0000000e-01 9.0000000e-01 9.0000000e-01 1.8000000e+00 1.8000000e+00 1.1000000e+00 1.8000000e+00 2.5000000e+00 1.2000000e+00 1.3000000e+00 3.0000000e+00 2.3000000e+00 1.1000000e+00 6.0000000e-01 1.9000000e+00 7.0000000e-01 2.0000000e+00 7.0000000e-01 1.5000000e+00 6.3000000e+00 5.6000000e+00 6.6000000e+00 4.9000000e+00 6.2000000e+00 5.1000000e+00 5.7000000e+00 4.2000000e+00 6.0000000e+00 4.6000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.7000000e+00 4.0000000e+00 5.8000000e+00 5.0000000e+00 4.6000000e+00 6.4000000e+00 4.5000000e+00 5.7000000e+00 5.0000000e+00 6.6000000e+00 5.6000000e+00 5.5000000e+00 5.8000000e+00 6.6000000e+00 6.8000000e+00 5.5000000e+00 4.0000000e+00 4.4000000e+00 4.2000000e+00 4.6000000e+00 6.4000000e+00 4.8000000e+00 5.1000000e+00 6.2000000e+00 6.1000000e+00 4.4000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 4.8000000e+00 4.2000000e+00 4.8000000e+00 4.5000000e+00 4.7000000e+00 5.3000000e+00 3.7000000e+00 4.7000000e+00 7.9000000e+00 6.5000000e+00 8.5000000e+00 7.2000000e+00 7.9000000e+00 9.7000000e+00 6.0000000e+00 8.9000000e+00 8.2000000e+00 8.6000000e+00 6.8000000e+00 7.3000000e+00 7.8000000e+00 6.6000000e+00 6.9000000e+00 7.2000000e+00 7.2000000e+00 9.2000000e+00 1.0700000e+01 6.7000000e+00 8.1000000e+00 6.1000000e+00 1.0000000e+01 6.7000000e+00 7.6000000e+00 8.2000000e+00 6.4000000e+00 6.2000000e+00 7.7000000e+00 8.0000000e+00 9.0000000e+00 8.9000000e+00 7.8000000e+00 6.5000000e+00 6.9000000e+00 9.5000000e+00 7.3000000e+00 7.0000000e+00 6.0000000e+00 7.7000000e+00 8.0000000e+00 7.6000000e+00 6.5000000e+00 8.2000000e+00 8.0000000e+00 7.6000000e+00 7.1000000e+00 7.1000000e+00 6.9000000e+00 6.2000000e+00 6.0000000e-01 8.0000000e-01 9.0000000e-01 1.3000000e+00 5.0000000e-01 8.0000000e-01 1.2000000e+00 2.1000000e+00 2.3000000e+00 1.5000000e+00 6.0000000e-01 1.8000000e+00 1.0000000e+00 1.2000000e+00 1.0000000e+00 7.0000000e-01 1.1000000e+00 8.0000000e-01 1.1000000e+00 7.0000000e-01 9.0000000e-01 7.0000000e-01 6.0000000e-01 8.0000000e-01 1.0000000e+00 1.6000000e+00 1.8000000e+00 9.0000000e-01 9.0000000e-01 1.2000000e+00 9.0000000e-01 8.0000000e-01 7.0000000e-01 6.0000000e-01 1.3000000e+00 6.0000000e-01 1.0000000e+00 1.5000000e+00 6.0000000e-01 1.2000000e+00 3.0000000e-01 1.2000000e+00 6.0000000e-01 7.0000000e+00 6.3000000e+00 7.3000000e+00 5.6000000e+00 6.9000000e+00 5.8000000e+00 6.4000000e+00 3.9000000e+00 6.7000000e+00 4.9000000e+00 4.6000000e+00 5.7000000e+00 5.9000000e+00 6.4000000e+00 4.7000000e+00 6.5000000e+00 5.7000000e+00 5.3000000e+00 7.1000000e+00 5.2000000e+00 6.4000000e+00 5.7000000e+00 7.3000000e+00 6.3000000e+00 6.2000000e+00 6.5000000e+00 7.3000000e+00 7.5000000e+00 6.2000000e+00 4.7000000e+00 5.1000000e+00 4.9000000e+00 5.3000000e+00 7.1000000e+00 5.5000000e+00 5.8000000e+00 6.9000000e+00 6.8000000e+00 5.1000000e+00 5.4000000e+00 5.6000000e+00 6.2000000e+00 5.5000000e+00 4.1000000e+00 5.5000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 3.8000000e+00 5.4000000e+00 8.6000000e+00 7.2000000e+00 9.2000000e+00 7.9000000e+00 8.6000000e+00 1.0400000e+01 5.7000000e+00 9.6000000e+00 8.9000000e+00 9.7000000e+00 7.5000000e+00 8.0000000e+00 8.5000000e+00 7.3000000e+00 7.6000000e+00 7.9000000e+00 7.9000000e+00 1.0700000e+01 1.1400000e+01 7.4000000e+00 8.8000000e+00 6.8000000e+00 1.0700000e+01 7.4000000e+00 8.3000000e+00 8.9000000e+00 7.1000000e+00 6.9000000e+00 8.4000000e+00 8.7000000e+00 9.7000000e+00 1.0400000e+01 8.5000000e+00 7.2000000e+00 7.6000000e+00 1.0200000e+01 8.0000000e+00 7.7000000e+00 6.7000000e+00 8.4000000e+00 8.7000000e+00 8.3000000e+00 7.2000000e+00 8.9000000e+00 8.7000000e+00 8.3000000e+00 7.8000000e+00 7.8000000e+00 7.6000000e+00 6.9000000e+00 1.2000000e+00 5.0000000e-01 7.0000000e-01 3.0000000e-01 8.0000000e-01 1.6000000e+00 1.7000000e+00 1.9000000e+00 1.3000000e+00 4.0000000e-01 1.4000000e+00 6.0000000e-01 6.0000000e-01 6.0000000e-01 1.1000000e+00 7.0000000e-01 6.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 3.0000000e-01 6.0000000e-01 6.0000000e-01 6.0000000e-01 1.0000000e+00 1.4000000e+00 5.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 1.2000000e+00 1.0000000e-01 4.0000000e-01 1.9000000e+00 1.0000000e+00 6.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 7.0000000e-01 6.0000000e-01 2.0000000e-01 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.0000000e+00 3.7000000e+00 6.3000000e+00 4.5000000e+00 4.2000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.4000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.7000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.2000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.5000000e+00 9.2000000e+00 8.5000000e+00 9.3000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 1.0300000e+01 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 7.9000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 1.0000000e+01 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 7.6000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.3000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.2000000e+00 6.5000000e+00 9.0000000e-01 1.9000000e+00 1.1000000e+00 6.0000000e-01 6.0000000e-01 2.7000000e+00 3.1000000e+00 2.3000000e+00 1.4000000e+00 2.6000000e+00 1.8000000e+00 1.8000000e+00 1.8000000e+00 1.3000000e+00 1.7000000e+00 1.4000000e+00 9.0000000e-01 1.5000000e+00 1.5000000e+00 1.3000000e+00 8.0000000e-01 8.0000000e-01 1.8000000e+00 2.2000000e+00 2.4000000e+00 9.0000000e-01 1.1000000e+00 1.8000000e+00 9.0000000e-01 2.0000000e-01 1.3000000e+00 1.4000000e+00 9.0000000e-01 4.0000000e-01 1.8000000e+00 2.3000000e+00 6.0000000e-01 1.8000000e+00 5.0000000e-01 1.8000000e+00 1.0000000e+00 7.4000000e+00 6.7000000e+00 7.5000000e+00 5.4000000e+00 6.7000000e+00 5.6000000e+00 7.0000000e+00 3.7000000e+00 6.5000000e+00 4.7000000e+00 4.4000000e+00 5.7000000e+00 5.7000000e+00 6.2000000e+00 4.5000000e+00 6.7000000e+00 5.7000000e+00 5.1000000e+00 6.9000000e+00 5.0000000e+00 6.8000000e+00 5.5000000e+00 7.1000000e+00 6.1000000e+00 6.0000000e+00 6.5000000e+00 7.1000000e+00 7.5000000e+00 6.0000000e+00 4.5000000e+00 4.9000000e+00 4.7000000e+00 5.1000000e+00 6.9000000e+00 5.5000000e+00 6.6000000e+00 7.1000000e+00 6.6000000e+00 5.1000000e+00 5.2000000e+00 5.4000000e+00 6.2000000e+00 5.3000000e+00 3.9000000e+00 5.3000000e+00 5.2000000e+00 5.2000000e+00 5.8000000e+00 3.6000000e+00 5.2000000e+00 9.2000000e+00 7.0000000e+00 9.2000000e+00 7.7000000e+00 8.6000000e+00 1.0400000e+01 5.5000000e+00 9.4000000e+00 8.7000000e+00 1.0500000e+01 7.9000000e+00 7.8000000e+00 8.5000000e+00 7.1000000e+00 7.4000000e+00 8.3000000e+00 7.9000000e+00 1.1500000e+01 1.1200000e+01 7.2000000e+00 9.2000000e+00 6.6000000e+00 1.0500000e+01 7.2000000e+00 8.9000000e+00 9.3000000e+00 6.9000000e+00 6.9000000e+00 8.2000000e+00 8.7000000e+00 9.5000000e+00 1.1200000e+01 8.3000000e+00 7.0000000e+00 7.4000000e+00 1.0200000e+01 8.8000000e+00 7.9000000e+00 6.7000000e+00 8.6000000e+00 8.9000000e+00 8.5000000e+00 7.0000000e+00 9.3000000e+00 9.3000000e+00 8.3000000e+00 7.6000000e+00 7.8000000e+00 8.4000000e+00 6.9000000e+00 1.2000000e+00 6.0000000e-01 3.0000000e-01 1.1000000e+00 2.2000000e+00 2.4000000e+00 1.8000000e+00 9.0000000e-01 1.9000000e+00 1.1000000e+00 1.1000000e+00 1.1000000e+00 1.4000000e+00 1.0000000e+00 9.0000000e-01 4.0000000e-01 8.0000000e-01 8.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 1.1000000e+00 1.3000000e+00 1.9000000e+00 0.0000000e+00 6.0000000e-01 1.3000000e+00 0.0000000e+00 9.0000000e-01 6.0000000e-01 9.0000000e-01 1.6000000e+00 9.0000000e-01 1.1000000e+00 1.6000000e+00 5.0000000e-01 1.1000000e+00 6.0000000e-01 1.1000000e+00 5.0000000e-01 6.7000000e+00 6.0000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 6.3000000e+00 3.4000000e+00 6.2000000e+00 4.4000000e+00 4.1000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 6.1000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.9000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 3.6000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.3000000e+00 4.9000000e+00 8.5000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.2000000e+00 9.1000000e+00 8.4000000e+00 9.8000000e+00 7.2000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.6000000e+00 7.4000000e+00 1.0800000e+01 1.0900000e+01 6.9000000e+00 8.5000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 8.2000000e+00 8.6000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 1.0500000e+01 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 8.1000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.6000000e+00 8.6000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.7000000e+00 6.4000000e+00 1.0000000e+00 1.5000000e+00 2.3000000e+00 1.0000000e+00 1.2000000e+00 6.0000000e-01 7.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 5.0000000e-01 1.4000000e+00 1.2000000e+00 1.3000000e+00 1.2000000e+00 1.0000000e+00 4.0000000e-01 6.0000000e-01 1.3000000e+00 1.3000000e+00 5.0000000e-01 7.0000000e-01 7.0000000e-01 1.2000000e+00 1.2000000e+00 5.0000000e-01 1.2000000e+00 1.9000000e+00 6.0000000e-01 9.0000000e-01 2.6000000e+00 1.7000000e+00 1.1000000e+00 1.0000000e+00 1.5000000e+00 5.0000000e-01 1.4000000e+00 1.0000000e-01 9.0000000e-01 6.5000000e+00 5.8000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 5.9000000e+00 4.4000000e+00 6.2000000e+00 4.8000000e+00 4.9000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 5.9000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.3000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 4.4000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.9000000e+00 4.9000000e+00 8.1000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 6.2000000e+00 9.1000000e+00 8.4000000e+00 8.8000000e+00 7.0000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.4000000e+00 7.4000000e+00 9.6000000e+00 1.0900000e+01 6.9000000e+00 8.3000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 7.8000000e+00 8.4000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 9.3000000e+00 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 7.5000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.4000000e+00 8.2000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.1000000e+00 6.4000000e+00 7.0000000e-01 1.5000000e+00 2.0000000e+00 2.2000000e+00 1.6000000e+00 7.0000000e-01 1.5000000e+00 9.0000000e-01 7.0000000e-01 9.0000000e-01 1.0000000e+00 8.0000000e-01 3.0000000e-01 6.0000000e-01 4.0000000e-01 6.0000000e-01 6.0000000e-01 3.0000000e-01 3.0000000e-01 9.0000000e-01 1.3000000e+00 1.7000000e+00 6.0000000e-01 8.0000000e-01 1.1000000e+00 6.0000000e-01 1.1000000e+00 4.0000000e-01 7.0000000e-01 1.8000000e+00 9.0000000e-01 7.0000000e-01 1.2000000e+00 7.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 5.0000000e-01 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.3000000e+00 6.6000000e+00 5.5000000e+00 6.1000000e+00 3.6000000e+00 6.4000000e+00 4.6000000e+00 4.3000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.4000000e+00 6.2000000e+00 5.4000000e+00 5.0000000e+00 6.8000000e+00 4.9000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 4.8000000e+00 4.6000000e+00 5.0000000e+00 6.8000000e+00 5.2000000e+00 5.5000000e+00 6.6000000e+00 6.5000000e+00 4.8000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 5.2000000e+00 3.8000000e+00 5.2000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 3.5000000e+00 5.1000000e+00 8.3000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 5.4000000e+00 9.3000000e+00 8.6000000e+00 9.4000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 1.0400000e+01 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.5000000e+00 1.0400000e+01 7.1000000e+00 8.0000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 1.0100000e+01 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 7.7000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.4000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.3000000e+00 6.6000000e+00 8.0000000e-01 2.3000000e+00 2.7000000e+00 1.9000000e+00 1.0000000e+00 2.2000000e+00 1.4000000e+00 1.4000000e+00 1.4000000e+00 1.3000000e+00 1.3000000e+00 1.0000000e+00 5.0000000e-01 1.1000000e+00 1.1000000e+00 9.0000000e-01 6.0000000e-01 4.0000000e-01 1.4000000e+00 1.6000000e+00 2.0000000e+00 3.0000000e-01 7.0000000e-01 1.4000000e+00 3.0000000e-01 6.0000000e-01 9.0000000e-01 1.0000000e+00 1.3000000e+00 8.0000000e-01 1.4000000e+00 1.9000000e+00 2.0000000e-01 1.4000000e+00 5.0000000e-01 1.4000000e+00 6.0000000e-01 7.0000000e+00 6.3000000e+00 7.1000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.6000000e+00 3.5000000e+00 6.3000000e+00 4.5000000e+00 4.2000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.3000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.4000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 6.2000000e+00 6.7000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.7000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.8000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.3000000e+00 9.2000000e+00 8.5000000e+00 1.0100000e+01 7.5000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.9000000e+00 7.5000000e+00 1.1100000e+01 1.1000000e+01 7.0000000e+00 8.8000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 8.5000000e+00 8.9000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 1.0800000e+01 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 8.4000000e+00 7.5000000e+00 6.3000000e+00 8.2000000e+00 8.5000000e+00 8.1000000e+00 6.8000000e+00 8.9000000e+00 8.9000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 8.0000000e+00 6.5000000e+00 2.7000000e+00 3.5000000e+00 2.5000000e+00 1.8000000e+00 3.0000000e+00 2.2000000e+00 2.2000000e+00 2.2000000e+00 1.1000000e+00 2.1000000e+00 1.8000000e+00 1.3000000e+00 1.9000000e+00 1.9000000e+00 1.7000000e+00 1.2000000e+00 1.2000000e+00 2.2000000e+00 2.4000000e+00 2.8000000e+00 1.1000000e+00 1.1000000e+00 2.0000000e+00 1.1000000e+00 4.0000000e-01 1.7000000e+00 1.6000000e+00 1.3000000e+00 6.0000000e-01 2.2000000e+00 2.7000000e+00 1.0000000e+00 2.2000000e+00 9.0000000e-01 2.2000000e+00 1.4000000e+00 7.8000000e+00 7.1000000e+00 7.9000000e+00 6.0000000e+00 7.3000000e+00 6.2000000e+00 7.4000000e+00 4.3000000e+00 7.1000000e+00 5.3000000e+00 5.0000000e+00 6.1000000e+00 6.3000000e+00 6.8000000e+00 5.1000000e+00 7.1000000e+00 6.1000000e+00 5.7000000e+00 7.5000000e+00 5.6000000e+00 7.2000000e+00 6.1000000e+00 7.7000000e+00 6.7000000e+00 6.6000000e+00 6.9000000e+00 7.7000000e+00 7.9000000e+00 6.6000000e+00 5.1000000e+00 5.5000000e+00 5.3000000e+00 5.7000000e+00 7.5000000e+00 5.9000000e+00 7.0000000e+00 7.5000000e+00 7.2000000e+00 5.5000000e+00 5.8000000e+00 6.0000000e+00 6.6000000e+00 5.9000000e+00 4.5000000e+00 5.9000000e+00 5.6000000e+00 5.8000000e+00 6.4000000e+00 4.2000000e+00 5.8000000e+00 9.6000000e+00 7.6000000e+00 9.6000000e+00 8.3000000e+00 9.0000000e+00 1.0800000e+01 6.1000000e+00 1.0000000e+01 9.3000000e+00 1.0900000e+01 8.3000000e+00 8.4000000e+00 8.9000000e+00 7.7000000e+00 8.0000000e+00 8.7000000e+00 8.3000000e+00 1.1900000e+01 1.1800000e+01 7.8000000e+00 9.6000000e+00 7.2000000e+00 1.1100000e+01 7.8000000e+00 9.3000000e+00 9.7000000e+00 7.5000000e+00 7.3000000e+00 8.8000000e+00 9.1000000e+00 1.0100000e+01 1.1600000e+01 8.9000000e+00 7.6000000e+00 8.0000000e+00 1.0600000e+01 9.2000000e+00 8.3000000e+00 7.1000000e+00 9.0000000e+00 9.3000000e+00 8.9000000e+00 7.6000000e+00 9.7000000e+00 9.7000000e+00 8.7000000e+00 8.2000000e+00 8.2000000e+00 8.8000000e+00 7.3000000e+00 1.0000000e+00 8.0000000e-01 1.5000000e+00 9.0000000e-01 1.3000000e+00 1.5000000e+00 1.5000000e+00 1.8000000e+00 2.2000000e+00 2.3000000e+00 2.2000000e+00 2.0000000e+00 1.4000000e+00 1.4000000e+00 2.3000000e+00 2.3000000e+00 1.5000000e+00 1.1000000e+00 7.0000000e-01 2.2000000e+00 1.6000000e+00 9.0000000e-01 2.2000000e+00 2.5000000e+00 1.6000000e+00 1.5000000e+00 3.2000000e+00 2.3000000e+00 2.1000000e+00 1.8000000e+00 2.3000000e+00 1.3000000e+00 2.2000000e+00 1.1000000e+00 1.7000000e+00 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.9000000e+00 6.6000000e+00 5.7000000e+00 6.1000000e+00 5.4000000e+00 6.4000000e+00 5.8000000e+00 5.9000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.8000000e+00 6.2000000e+00 5.8000000e+00 5.0000000e+00 6.8000000e+00 5.3000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.6000000e+00 5.4000000e+00 5.2000000e+00 5.0000000e+00 6.8000000e+00 6.0000000e+00 5.5000000e+00 6.6000000e+00 6.5000000e+00 5.2000000e+00 5.7000000e+00 5.9000000e+00 5.9000000e+00 5.2000000e+00 5.4000000e+00 5.6000000e+00 5.1000000e+00 5.3000000e+00 5.7000000e+00 4.9000000e+00 5.3000000e+00 8.3000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 7.2000000e+00 9.3000000e+00 8.6000000e+00 9.0000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.2000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 9.6000000e+00 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.9000000e+00 1.0400000e+01 7.1000000e+00 8.0000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 9.3000000e+00 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 7.7000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.4000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.3000000e+00 6.6000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 1.3000000e+00 1.7000000e+00 1.3000000e+00 2.6000000e+00 2.0000000e+00 2.5000000e+00 2.4000000e+00 1.8000000e+00 1.6000000e+00 1.8000000e+00 2.5000000e+00 2.5000000e+00 1.3000000e+00 1.1000000e+00 7.0000000e-01 2.4000000e+00 2.4000000e+00 1.5000000e+00 2.4000000e+00 3.1000000e+00 1.8000000e+00 1.9000000e+00 3.6000000e+00 2.9000000e+00 1.9000000e+00 1.6000000e+00 2.5000000e+00 1.5000000e+00 2.6000000e+00 1.3000000e+00 2.1000000e+00 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.7000000e+00 6.6000000e+00 5.5000000e+00 6.1000000e+00 5.2000000e+00 6.4000000e+00 5.6000000e+00 5.7000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.6000000e+00 6.2000000e+00 5.6000000e+00 5.0000000e+00 6.8000000e+00 5.1000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 5.2000000e+00 5.0000000e+00 5.0000000e+00 6.8000000e+00 5.8000000e+00 5.5000000e+00 6.6000000e+00 6.5000000e+00 5.0000000e+00 5.5000000e+00 5.7000000e+00 5.9000000e+00 5.2000000e+00 5.2000000e+00 5.4000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 4.7000000e+00 5.1000000e+00 8.3000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 7.0000000e+00 9.3000000e+00 8.6000000e+00 9.0000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 9.6000000e+00 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.7000000e+00 1.0400000e+01 7.1000000e+00 8.0000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 9.3000000e+00 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 7.7000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.4000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.3000000e+00 6.6000000e+00 9.0000000e-01 9.0000000e-01 7.0000000e-01 1.1000000e+00 7.0000000e-01 1.6000000e+00 1.4000000e+00 1.9000000e+00 1.8000000e+00 1.2000000e+00 1.0000000e+00 1.0000000e+00 1.9000000e+00 1.9000000e+00 7.0000000e-01 9.0000000e-01 7.0000000e-01 1.8000000e+00 1.4000000e+00 7.0000000e-01 1.8000000e+00 2.1000000e+00 1.2000000e+00 9.0000000e-01 2.6000000e+00 1.9000000e+00 1.3000000e+00 1.0000000e+00 1.7000000e+00 9.0000000e-01 1.8000000e+00 7.0000000e-01 1.3000000e+00 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.3000000e+00 6.6000000e+00 5.5000000e+00 6.1000000e+00 4.6000000e+00 6.4000000e+00 5.0000000e+00 5.1000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.4000000e+00 6.2000000e+00 5.4000000e+00 5.0000000e+00 6.8000000e+00 4.9000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 4.8000000e+00 4.6000000e+00 5.0000000e+00 6.8000000e+00 5.2000000e+00 5.5000000e+00 6.6000000e+00 6.5000000e+00 4.8000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 5.2000000e+00 4.6000000e+00 5.2000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 4.1000000e+00 5.1000000e+00 8.3000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 6.4000000e+00 9.3000000e+00 8.6000000e+00 9.0000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 9.6000000e+00 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.5000000e+00 1.0400000e+01 7.1000000e+00 8.0000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 9.3000000e+00 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 7.7000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.4000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.3000000e+00 6.6000000e+00 1.2000000e+00 4.0000000e-01 8.0000000e-01 4.0000000e-01 1.1000000e+00 7.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 3.0000000e-01 3.0000000e-01 1.0000000e+00 1.0000000e+00 6.0000000e-01 1.0000000e+00 1.2000000e+00 9.0000000e-01 7.0000000e-01 6.0000000e-01 9.0000000e-01 1.4000000e+00 3.0000000e-01 2.0000000e-01 1.9000000e+00 1.2000000e+00 6.0000000e-01 9.0000000e-01 8.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 4.0000000e-01 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.0000000e+00 3.9000000e+00 6.3000000e+00 4.5000000e+00 4.4000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.4000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.9000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.2000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.7000000e+00 9.2000000e+00 8.5000000e+00 9.1000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 1.0100000e+01 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 7.9000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 9.8000000e+00 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 7.6000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.3000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.2000000e+00 6.5000000e+00 8.0000000e-01 8.0000000e-01 1.0000000e+00 2.1000000e+00 1.3000000e+00 1.6000000e+00 1.7000000e+00 1.3000000e+00 1.1000000e+00 1.3000000e+00 1.8000000e+00 1.8000000e+00 1.0000000e+00 1.2000000e+00 1.0000000e+00 1.9000000e+00 1.9000000e+00 1.0000000e+00 1.9000000e+00 2.6000000e+00 1.3000000e+00 1.4000000e+00 3.1000000e+00 2.4000000e+00 1.4000000e+00 9.0000000e-01 2.0000000e+00 8.0000000e-01 2.1000000e+00 8.0000000e-01 1.6000000e+00 6.0000000e+00 5.3000000e+00 6.3000000e+00 5.0000000e+00 5.9000000e+00 4.8000000e+00 5.4000000e+00 4.5000000e+00 5.7000000e+00 4.9000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.4000000e+00 3.9000000e+00 5.5000000e+00 4.9000000e+00 4.3000000e+00 6.1000000e+00 4.4000000e+00 5.4000000e+00 4.7000000e+00 6.3000000e+00 5.3000000e+00 5.2000000e+00 5.5000000e+00 6.3000000e+00 6.5000000e+00 5.2000000e+00 3.7000000e+00 4.5000000e+00 4.3000000e+00 4.3000000e+00 6.1000000e+00 5.1000000e+00 4.8000000e+00 5.9000000e+00 5.8000000e+00 4.3000000e+00 4.8000000e+00 5.0000000e+00 5.2000000e+00 4.5000000e+00 4.5000000e+00 4.7000000e+00 4.2000000e+00 4.4000000e+00 5.0000000e+00 4.0000000e+00 4.4000000e+00 7.6000000e+00 6.2000000e+00 8.2000000e+00 6.9000000e+00 7.6000000e+00 9.4000000e+00 6.3000000e+00 8.6000000e+00 7.9000000e+00 8.3000000e+00 6.5000000e+00 7.0000000e+00 7.5000000e+00 6.3000000e+00 6.6000000e+00 6.9000000e+00 6.9000000e+00 8.9000000e+00 1.0400000e+01 6.4000000e+00 7.8000000e+00 6.0000000e+00 9.7000000e+00 6.4000000e+00 7.3000000e+00 7.9000000e+00 6.1000000e+00 5.9000000e+00 7.4000000e+00 7.7000000e+00 8.7000000e+00 8.6000000e+00 7.5000000e+00 6.2000000e+00 6.6000000e+00 9.2000000e+00 7.0000000e+00 6.7000000e+00 5.7000000e+00 7.4000000e+00 7.7000000e+00 7.3000000e+00 6.2000000e+00 7.9000000e+00 7.7000000e+00 7.3000000e+00 6.8000000e+00 6.8000000e+00 6.6000000e+00 5.9000000e+00 1.0000000e+00 2.0000000e-01 1.3000000e+00 9.0000000e-01 1.2000000e+00 1.1000000e+00 7.0000000e-01 5.0000000e-01 7.0000000e-01 1.2000000e+00 1.2000000e+00 8.0000000e-01 6.0000000e-01 1.0000000e+00 1.1000000e+00 1.1000000e+00 1.0000000e+00 1.1000000e+00 1.8000000e+00 5.0000000e-01 6.0000000e-01 2.3000000e+00 1.6000000e+00 8.0000000e-01 5.0000000e-01 1.2000000e+00 2.0000000e-01 1.3000000e+00 4.0000000e-01 8.0000000e-01 6.8000000e+00 6.1000000e+00 7.1000000e+00 5.4000000e+00 6.7000000e+00 5.6000000e+00 6.2000000e+00 4.1000000e+00 6.5000000e+00 4.7000000e+00 4.6000000e+00 5.5000000e+00 5.7000000e+00 6.2000000e+00 4.5000000e+00 6.3000000e+00 5.5000000e+00 5.1000000e+00 6.9000000e+00 5.0000000e+00 6.2000000e+00 5.5000000e+00 7.1000000e+00 6.1000000e+00 6.0000000e+00 6.3000000e+00 7.1000000e+00 7.3000000e+00 6.0000000e+00 4.5000000e+00 4.9000000e+00 4.7000000e+00 5.1000000e+00 6.9000000e+00 5.3000000e+00 5.6000000e+00 6.7000000e+00 6.6000000e+00 4.9000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 5.3000000e+00 4.1000000e+00 5.3000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 3.6000000e+00 5.2000000e+00 8.4000000e+00 7.0000000e+00 9.0000000e+00 7.7000000e+00 8.4000000e+00 1.0200000e+01 5.9000000e+00 9.4000000e+00 8.7000000e+00 9.1000000e+00 7.3000000e+00 7.8000000e+00 8.3000000e+00 7.1000000e+00 7.4000000e+00 7.7000000e+00 7.7000000e+00 9.7000000e+00 1.1200000e+01 7.2000000e+00 8.6000000e+00 6.6000000e+00 1.0500000e+01 7.2000000e+00 8.1000000e+00 8.7000000e+00 6.9000000e+00 6.7000000e+00 8.2000000e+00 8.5000000e+00 9.5000000e+00 9.4000000e+00 8.3000000e+00 7.0000000e+00 7.4000000e+00 1.0000000e+01 7.8000000e+00 7.5000000e+00 6.5000000e+00 8.2000000e+00 8.5000000e+00 8.1000000e+00 7.0000000e+00 8.7000000e+00 8.5000000e+00 8.1000000e+00 7.6000000e+00 7.6000000e+00 7.4000000e+00 6.7000000e+00 1.0000000e+00 1.7000000e+00 7.0000000e-01 8.0000000e-01 9.0000000e-01 7.0000000e-01 5.0000000e-01 5.0000000e-01 1.0000000e+00 1.0000000e+00 4.0000000e-01 1.2000000e+00 1.2000000e+00 1.1000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.8000000e+00 5.0000000e-01 1.0000000e+00 2.5000000e+00 1.6000000e+00 1.0000000e+00 1.1000000e+00 1.4000000e+00 8.0000000e-01 1.3000000e+00 6.0000000e-01 8.0000000e-01 6.0000000e+00 5.3000000e+00 6.3000000e+00 4.6000000e+00 5.9000000e+00 4.8000000e+00 5.4000000e+00 3.9000000e+00 5.7000000e+00 4.3000000e+00 4.4000000e+00 4.7000000e+00 4.9000000e+00 5.4000000e+00 3.7000000e+00 5.5000000e+00 4.7000000e+00 4.3000000e+00 6.1000000e+00 4.2000000e+00 5.4000000e+00 4.7000000e+00 6.3000000e+00 5.3000000e+00 5.2000000e+00 5.5000000e+00 6.3000000e+00 6.5000000e+00 5.2000000e+00 3.7000000e+00 4.1000000e+00 3.9000000e+00 4.3000000e+00 6.1000000e+00 4.5000000e+00 4.8000000e+00 5.9000000e+00 5.8000000e+00 4.1000000e+00 4.4000000e+00 4.6000000e+00 5.2000000e+00 4.5000000e+00 3.9000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.0000000e+00 3.4000000e+00 4.4000000e+00 7.6000000e+00 6.2000000e+00 8.2000000e+00 6.9000000e+00 7.6000000e+00 9.4000000e+00 5.7000000e+00 8.6000000e+00 7.9000000e+00 8.7000000e+00 6.5000000e+00 7.0000000e+00 7.5000000e+00 6.3000000e+00 6.6000000e+00 6.9000000e+00 6.9000000e+00 9.7000000e+00 1.0400000e+01 6.4000000e+00 7.8000000e+00 5.8000000e+00 9.7000000e+00 6.4000000e+00 7.3000000e+00 7.9000000e+00 6.1000000e+00 5.9000000e+00 7.4000000e+00 7.7000000e+00 8.7000000e+00 9.4000000e+00 7.5000000e+00 6.2000000e+00 6.6000000e+00 9.2000000e+00 7.0000000e+00 6.7000000e+00 5.7000000e+00 7.4000000e+00 7.7000000e+00 7.3000000e+00 6.2000000e+00 7.9000000e+00 7.7000000e+00 7.3000000e+00 6.8000000e+00 6.8000000e+00 6.6000000e+00 5.9000000e+00 1.3000000e+00 7.0000000e-01 1.2000000e+00 1.1000000e+00 5.0000000e-01 5.0000000e-01 7.0000000e-01 1.2000000e+00 1.2000000e+00 6.0000000e-01 8.0000000e-01 1.2000000e+00 1.1000000e+00 1.1000000e+00 1.0000000e+00 1.1000000e+00 1.8000000e+00 5.0000000e-01 6.0000000e-01 2.3000000e+00 1.6000000e+00 6.0000000e-01 5.0000000e-01 1.2000000e+00 4.0000000e-01 1.3000000e+00 4.0000000e-01 8.0000000e-01 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.0000000e+00 3.9000000e+00 6.3000000e+00 4.5000000e+00 4.4000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.4000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.9000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.2000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.7000000e+00 9.2000000e+00 8.5000000e+00 8.9000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 9.7000000e+00 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 7.9000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 9.4000000e+00 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 7.6000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.3000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.2000000e+00 6.5000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 1.4000000e+00 1.2000000e+00 1.2000000e+00 1.1000000e+00 1.3000000e+00 1.7000000e+00 1.7000000e+00 1.9000000e+00 1.4000000e+00 1.0000000e+00 1.3000000e+00 1.4000000e+00 1.1000000e+00 1.2000000e+00 9.0000000e-01 1.8000000e+00 9.0000000e-01 1.5000000e+00 1.8000000e+00 1.3000000e+00 1.3000000e+00 8.0000000e-01 1.3000000e+00 1.1000000e+00 7.7000000e+00 7.0000000e+00 8.0000000e+00 6.3000000e+00 7.6000000e+00 6.5000000e+00 7.1000000e+00 4.6000000e+00 7.4000000e+00 5.6000000e+00 5.3000000e+00 6.4000000e+00 6.6000000e+00 7.1000000e+00 5.4000000e+00 7.2000000e+00 6.4000000e+00 6.0000000e+00 7.8000000e+00 5.9000000e+00 7.1000000e+00 6.4000000e+00 8.0000000e+00 7.0000000e+00 6.9000000e+00 7.2000000e+00 8.0000000e+00 8.2000000e+00 6.9000000e+00 5.4000000e+00 5.8000000e+00 5.6000000e+00 6.0000000e+00 7.8000000e+00 6.2000000e+00 6.5000000e+00 7.6000000e+00 7.5000000e+00 5.8000000e+00 6.1000000e+00 6.3000000e+00 6.9000000e+00 6.2000000e+00 4.8000000e+00 6.2000000e+00 5.9000000e+00 6.1000000e+00 6.7000000e+00 4.5000000e+00 6.1000000e+00 9.3000000e+00 7.9000000e+00 9.9000000e+00 8.6000000e+00 9.3000000e+00 1.1100000e+01 6.4000000e+00 1.0300000e+01 9.6000000e+00 1.0000000e+01 8.2000000e+00 8.7000000e+00 9.2000000e+00 8.0000000e+00 8.3000000e+00 8.6000000e+00 8.6000000e+00 1.1000000e+01 1.2100000e+01 8.1000000e+00 9.5000000e+00 7.5000000e+00 1.1400000e+01 8.1000000e+00 9.0000000e+00 9.6000000e+00 7.8000000e+00 7.6000000e+00 9.1000000e+00 9.4000000e+00 1.0400000e+01 1.0700000e+01 9.2000000e+00 7.9000000e+00 8.3000000e+00 1.0900000e+01 8.7000000e+00 8.4000000e+00 7.4000000e+00 9.1000000e+00 9.4000000e+00 9.0000000e+00 7.9000000e+00 9.6000000e+00 9.4000000e+00 9.0000000e+00 8.5000000e+00 8.5000000e+00 8.3000000e+00 7.6000000e+00 9.0000000e-01 8.0000000e-01 4.0000000e-01 8.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 7.0000000e-01 1.5000000e+00 1.9000000e+00 1.0000000e+00 1.0000000e+00 1.3000000e+00 1.0000000e+00 1.7000000e+00 6.0000000e-01 9.0000000e-01 2.2000000e+00 1.5000000e+00 5.0000000e-01 8.0000000e-01 1.1000000e+00 9.0000000e-01 1.2000000e+00 1.1000000e+00 7.0000000e-01 5.9000000e+00 5.2000000e+00 6.2000000e+00 4.5000000e+00 5.8000000e+00 4.7000000e+00 5.3000000e+00 3.2000000e+00 5.6000000e+00 3.8000000e+00 3.7000000e+00 4.6000000e+00 4.8000000e+00 5.3000000e+00 3.6000000e+00 5.4000000e+00 4.6000000e+00 4.2000000e+00 6.0000000e+00 4.1000000e+00 5.3000000e+00 4.6000000e+00 6.2000000e+00 5.2000000e+00 5.1000000e+00 5.4000000e+00 6.2000000e+00 6.4000000e+00 5.1000000e+00 3.6000000e+00 4.0000000e+00 3.8000000e+00 4.2000000e+00 6.0000000e+00 4.4000000e+00 4.9000000e+00 5.8000000e+00 5.7000000e+00 4.0000000e+00 4.3000000e+00 4.5000000e+00 5.1000000e+00 4.4000000e+00 3.2000000e+00 4.4000000e+00 4.1000000e+00 4.3000000e+00 4.9000000e+00 2.7000000e+00 4.3000000e+00 7.5000000e+00 6.1000000e+00 8.1000000e+00 6.8000000e+00 7.5000000e+00 9.3000000e+00 5.0000000e+00 8.5000000e+00 7.8000000e+00 8.8000000e+00 6.4000000e+00 6.9000000e+00 7.4000000e+00 6.2000000e+00 6.5000000e+00 6.8000000e+00 6.8000000e+00 9.8000000e+00 1.0300000e+01 6.3000000e+00 7.7000000e+00 5.7000000e+00 9.6000000e+00 6.3000000e+00 7.2000000e+00 7.8000000e+00 6.0000000e+00 5.8000000e+00 7.3000000e+00 7.6000000e+00 8.6000000e+00 9.5000000e+00 7.4000000e+00 6.1000000e+00 6.5000000e+00 9.1000000e+00 7.1000000e+00 6.6000000e+00 5.6000000e+00 7.3000000e+00 7.6000000e+00 7.2000000e+00 6.1000000e+00 7.8000000e+00 7.6000000e+00 7.2000000e+00 6.7000000e+00 6.7000000e+00 6.7000000e+00 5.8000000e+00 9.0000000e-01 7.0000000e-01 9.0000000e-01 9.0000000e-01 6.0000000e-01 6.0000000e-01 1.2000000e+00 1.6000000e+00 2.0000000e+00 9.0000000e-01 1.1000000e+00 1.4000000e+00 9.0000000e-01 1.4000000e+00 7.0000000e-01 1.0000000e+00 2.1000000e+00 1.2000000e+00 1.0000000e+00 9.0000000e-01 1.0000000e+00 1.0000000e+00 9.0000000e-01 1.2000000e+00 8.0000000e-01 6.4000000e+00 5.7000000e+00 6.7000000e+00 5.0000000e+00 6.3000000e+00 5.2000000e+00 5.8000000e+00 3.3000000e+00 6.1000000e+00 4.3000000e+00 4.0000000e+00 5.1000000e+00 5.3000000e+00 5.8000000e+00 4.1000000e+00 5.9000000e+00 5.1000000e+00 4.7000000e+00 6.5000000e+00 4.6000000e+00 5.8000000e+00 5.1000000e+00 6.7000000e+00 5.7000000e+00 5.6000000e+00 5.9000000e+00 6.7000000e+00 6.9000000e+00 5.6000000e+00 4.1000000e+00 4.5000000e+00 4.3000000e+00 4.7000000e+00 6.5000000e+00 4.9000000e+00 5.2000000e+00 6.3000000e+00 6.2000000e+00 4.5000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 4.9000000e+00 3.5000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 3.2000000e+00 4.8000000e+00 8.0000000e+00 6.6000000e+00 8.6000000e+00 7.3000000e+00 8.0000000e+00 9.8000000e+00 5.1000000e+00 9.0000000e+00 8.3000000e+00 9.1000000e+00 6.9000000e+00 7.4000000e+00 7.9000000e+00 6.7000000e+00 7.0000000e+00 7.3000000e+00 7.3000000e+00 1.0100000e+01 1.0800000e+01 6.8000000e+00 8.2000000e+00 6.2000000e+00 1.0100000e+01 6.8000000e+00 7.7000000e+00 8.3000000e+00 6.5000000e+00 6.3000000e+00 7.8000000e+00 8.1000000e+00 9.1000000e+00 9.8000000e+00 7.9000000e+00 6.6000000e+00 7.0000000e+00 9.6000000e+00 7.4000000e+00 7.1000000e+00 6.1000000e+00 7.8000000e+00 8.1000000e+00 7.7000000e+00 6.6000000e+00 8.3000000e+00 8.1000000e+00 7.7000000e+00 7.2000000e+00 7.2000000e+00 7.0000000e+00 6.3000000e+00 6.0000000e-01 8.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 1.1000000e+00 1.5000000e+00 1.9000000e+00 4.0000000e-01 6.0000000e-01 1.3000000e+00 4.0000000e-01 9.0000000e-01 6.0000000e-01 9.0000000e-01 1.6000000e+00 1.1000000e+00 9.0000000e-01 1.4000000e+00 5.0000000e-01 9.0000000e-01 8.0000000e-01 1.1000000e+00 5.0000000e-01 6.5000000e+00 5.8000000e+00 6.6000000e+00 4.7000000e+00 6.0000000e+00 4.9000000e+00 6.1000000e+00 3.2000000e+00 5.8000000e+00 4.0000000e+00 3.7000000e+00 4.8000000e+00 5.0000000e+00 5.5000000e+00 3.8000000e+00 5.8000000e+00 4.8000000e+00 4.4000000e+00 6.2000000e+00 4.3000000e+00 5.9000000e+00 4.8000000e+00 6.4000000e+00 5.4000000e+00 5.3000000e+00 5.6000000e+00 6.4000000e+00 6.6000000e+00 5.3000000e+00 3.8000000e+00 4.2000000e+00 4.0000000e+00 4.4000000e+00 6.2000000e+00 4.6000000e+00 5.7000000e+00 6.2000000e+00 5.9000000e+00 4.2000000e+00 4.5000000e+00 4.7000000e+00 5.3000000e+00 4.6000000e+00 3.2000000e+00 4.6000000e+00 4.3000000e+00 4.5000000e+00 5.1000000e+00 2.9000000e+00 4.5000000e+00 8.3000000e+00 6.3000000e+00 8.3000000e+00 7.0000000e+00 7.7000000e+00 9.5000000e+00 5.0000000e+00 8.7000000e+00 8.0000000e+00 9.6000000e+00 7.0000000e+00 7.1000000e+00 7.6000000e+00 6.4000000e+00 6.7000000e+00 7.4000000e+00 7.0000000e+00 1.0600000e+01 1.0500000e+01 6.5000000e+00 8.3000000e+00 5.9000000e+00 9.8000000e+00 6.5000000e+00 8.0000000e+00 8.4000000e+00 6.2000000e+00 6.0000000e+00 7.5000000e+00 7.8000000e+00 8.8000000e+00 1.0300000e+01 7.6000000e+00 6.3000000e+00 6.7000000e+00 9.3000000e+00 7.9000000e+00 7.0000000e+00 5.8000000e+00 7.7000000e+00 8.0000000e+00 7.6000000e+00 6.3000000e+00 8.4000000e+00 8.4000000e+00 7.4000000e+00 6.9000000e+00 6.9000000e+00 7.5000000e+00 6.0000000e+00 6.0000000e-01 6.0000000e-01 7.0000000e-01 7.0000000e-01 5.0000000e-01 1.3000000e+00 1.7000000e+00 8.0000000e-01 8.0000000e-01 1.1000000e+00 8.0000000e-01 1.5000000e+00 4.0000000e-01 5.0000000e-01 2.0000000e+00 1.3000000e+00 3.0000000e-01 8.0000000e-01 9.0000000e-01 7.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 6.3000000e+00 5.6000000e+00 6.6000000e+00 4.9000000e+00 6.2000000e+00 5.1000000e+00 5.7000000e+00 3.4000000e+00 6.0000000e+00 4.2000000e+00 3.9000000e+00 5.0000000e+00 5.2000000e+00 5.7000000e+00 4.0000000e+00 5.8000000e+00 5.0000000e+00 4.6000000e+00 6.4000000e+00 4.5000000e+00 5.7000000e+00 5.0000000e+00 6.6000000e+00 5.6000000e+00 5.5000000e+00 5.8000000e+00 6.6000000e+00 6.8000000e+00 5.5000000e+00 4.0000000e+00 4.4000000e+00 4.2000000e+00 4.6000000e+00 6.4000000e+00 4.8000000e+00 5.1000000e+00 6.2000000e+00 6.1000000e+00 4.4000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 4.8000000e+00 3.4000000e+00 4.8000000e+00 4.5000000e+00 4.7000000e+00 5.3000000e+00 3.1000000e+00 4.7000000e+00 7.9000000e+00 6.5000000e+00 8.5000000e+00 7.2000000e+00 7.9000000e+00 9.7000000e+00 5.2000000e+00 8.9000000e+00 8.2000000e+00 9.0000000e+00 6.8000000e+00 7.3000000e+00 7.8000000e+00 6.6000000e+00 6.9000000e+00 7.2000000e+00 7.2000000e+00 1.0000000e+01 1.0700000e+01 6.7000000e+00 8.1000000e+00 6.1000000e+00 1.0000000e+01 6.7000000e+00 7.6000000e+00 8.2000000e+00 6.4000000e+00 6.2000000e+00 7.7000000e+00 8.0000000e+00 9.0000000e+00 9.7000000e+00 7.8000000e+00 6.5000000e+00 6.9000000e+00 9.5000000e+00 7.3000000e+00 7.0000000e+00 6.0000000e+00 7.7000000e+00 8.0000000e+00 7.6000000e+00 6.5000000e+00 8.2000000e+00 8.0000000e+00 7.6000000e+00 7.1000000e+00 7.1000000e+00 6.9000000e+00 6.2000000e+00 2.0000000e-01 9.0000000e-01 9.0000000e-01 5.0000000e-01 7.0000000e-01 1.1000000e+00 8.0000000e-01 8.0000000e-01 5.0000000e-01 8.0000000e-01 1.5000000e+00 2.0000000e-01 5.0000000e-01 2.2000000e+00 1.3000000e+00 7.0000000e-01 1.0000000e+00 1.1000000e+00 5.0000000e-01 1.0000000e+00 3.0000000e-01 5.0000000e-01 6.5000000e+00 5.8000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 5.9000000e+00 4.0000000e+00 6.2000000e+00 4.4000000e+00 4.5000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 5.9000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.3000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 4.0000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.5000000e+00 4.9000000e+00 8.1000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.8000000e+00 9.1000000e+00 8.4000000e+00 9.0000000e+00 7.0000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.4000000e+00 7.4000000e+00 1.0000000e+01 1.0900000e+01 6.9000000e+00 8.3000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 7.8000000e+00 8.4000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 9.7000000e+00 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 7.5000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.4000000e+00 8.2000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.1000000e+00 6.4000000e+00 9.0000000e-01 9.0000000e-01 5.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 6.0000000e-01 5.0000000e-01 8.0000000e-01 1.3000000e+00 2.0000000e-01 5.0000000e-01 2.0000000e+00 1.1000000e+00 9.0000000e-01 1.2000000e+00 9.0000000e-01 7.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 6.5000000e+00 5.8000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 5.9000000e+00 4.0000000e+00 6.2000000e+00 4.4000000e+00 4.5000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 5.9000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.3000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 4.0000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.5000000e+00 4.9000000e+00 8.1000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.8000000e+00 9.1000000e+00 8.4000000e+00 9.2000000e+00 7.0000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.4000000e+00 7.4000000e+00 1.0200000e+01 1.0900000e+01 6.9000000e+00 8.3000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 7.8000000e+00 8.4000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 9.9000000e+00 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 7.5000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.4000000e+00 8.2000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.1000000e+00 6.4000000e+00 2.0000000e-01 1.2000000e+00 1.6000000e+00 2.0000000e+00 5.0000000e-01 7.0000000e-01 1.4000000e+00 5.0000000e-01 8.0000000e-01 7.0000000e-01 1.0000000e+00 1.5000000e+00 6.0000000e-01 1.0000000e+00 1.5000000e+00 6.0000000e-01 1.0000000e+00 3.0000000e-01 1.2000000e+00 6.0000000e-01 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.2000000e+00 3.5000000e+00 6.3000000e+00 4.5000000e+00 4.2000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.8000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.7000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.4000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.3000000e+00 9.2000000e+00 8.5000000e+00 9.7000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 1.0700000e+01 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 8.1000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 1.0400000e+01 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 8.0000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.5000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.6000000e+00 6.5000000e+00 1.2000000e+00 1.6000000e+00 2.0000000e+00 3.0000000e-01 7.0000000e-01 1.4000000e+00 3.0000000e-01 8.0000000e-01 7.0000000e-01 1.0000000e+00 1.5000000e+00 8.0000000e-01 1.0000000e+00 1.5000000e+00 4.0000000e-01 1.0000000e+00 5.0000000e-01 1.2000000e+00 6.0000000e-01 6.6000000e+00 5.9000000e+00 6.7000000e+00 5.0000000e+00 6.3000000e+00 5.2000000e+00 6.2000000e+00 3.3000000e+00 6.1000000e+00 4.3000000e+00 4.0000000e+00 5.1000000e+00 5.3000000e+00 5.8000000e+00 4.1000000e+00 5.9000000e+00 5.1000000e+00 4.7000000e+00 6.5000000e+00 4.6000000e+00 6.0000000e+00 5.1000000e+00 6.7000000e+00 5.7000000e+00 5.6000000e+00 5.9000000e+00 6.7000000e+00 6.9000000e+00 5.6000000e+00 4.1000000e+00 4.5000000e+00 4.3000000e+00 4.7000000e+00 6.5000000e+00 4.9000000e+00 5.8000000e+00 6.3000000e+00 6.2000000e+00 4.5000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 4.9000000e+00 3.5000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 3.2000000e+00 4.8000000e+00 8.4000000e+00 6.6000000e+00 8.6000000e+00 7.3000000e+00 8.0000000e+00 9.8000000e+00 5.1000000e+00 9.0000000e+00 8.3000000e+00 9.7000000e+00 7.1000000e+00 7.4000000e+00 7.9000000e+00 6.7000000e+00 7.0000000e+00 7.5000000e+00 7.3000000e+00 1.0700000e+01 1.0800000e+01 6.8000000e+00 8.4000000e+00 6.2000000e+00 1.0100000e+01 6.8000000e+00 8.1000000e+00 8.5000000e+00 6.5000000e+00 6.3000000e+00 7.8000000e+00 8.1000000e+00 9.1000000e+00 1.0400000e+01 7.9000000e+00 6.6000000e+00 7.0000000e+00 9.6000000e+00 8.0000000e+00 7.1000000e+00 6.1000000e+00 7.8000000e+00 8.1000000e+00 7.7000000e+00 6.6000000e+00 8.5000000e+00 8.5000000e+00 7.7000000e+00 7.2000000e+00 7.2000000e+00 7.6000000e+00 6.3000000e+00 1.2000000e+00 1.2000000e+00 1.1000000e+00 1.1000000e+00 6.0000000e-01 1.1000000e+00 1.8000000e+00 5.0000000e-01 8.0000000e-01 2.3000000e+00 1.6000000e+00 8.0000000e-01 1.1000000e+00 1.2000000e+00 1.0000000e+00 1.3000000e+00 6.0000000e-01 8.0000000e-01 6.0000000e+00 5.3000000e+00 6.3000000e+00 4.6000000e+00 5.9000000e+00 4.8000000e+00 5.4000000e+00 3.9000000e+00 5.7000000e+00 4.3000000e+00 4.4000000e+00 4.7000000e+00 4.9000000e+00 5.4000000e+00 3.7000000e+00 5.5000000e+00 4.7000000e+00 4.3000000e+00 6.1000000e+00 4.2000000e+00 5.4000000e+00 4.7000000e+00 6.3000000e+00 5.3000000e+00 5.2000000e+00 5.5000000e+00 6.3000000e+00 6.5000000e+00 5.2000000e+00 3.7000000e+00 4.1000000e+00 3.9000000e+00 4.3000000e+00 6.1000000e+00 4.5000000e+00 4.8000000e+00 5.9000000e+00 5.8000000e+00 4.1000000e+00 4.4000000e+00 4.6000000e+00 5.2000000e+00 4.5000000e+00 3.9000000e+00 4.5000000e+00 4.2000000e+00 4.4000000e+00 5.0000000e+00 3.4000000e+00 4.4000000e+00 7.6000000e+00 6.2000000e+00 8.2000000e+00 6.9000000e+00 7.6000000e+00 9.4000000e+00 5.7000000e+00 8.6000000e+00 7.9000000e+00 8.7000000e+00 6.5000000e+00 7.0000000e+00 7.5000000e+00 6.3000000e+00 6.6000000e+00 6.9000000e+00 6.9000000e+00 9.7000000e+00 1.0400000e+01 6.4000000e+00 7.8000000e+00 5.8000000e+00 9.7000000e+00 6.4000000e+00 7.3000000e+00 7.9000000e+00 6.1000000e+00 5.9000000e+00 7.4000000e+00 7.7000000e+00 8.7000000e+00 9.4000000e+00 7.5000000e+00 6.2000000e+00 6.6000000e+00 9.2000000e+00 7.0000000e+00 6.7000000e+00 5.7000000e+00 7.4000000e+00 7.7000000e+00 7.3000000e+00 6.2000000e+00 7.9000000e+00 7.7000000e+00 7.3000000e+00 6.8000000e+00 6.8000000e+00 6.6000000e+00 5.9000000e+00 6.0000000e-01 1.3000000e+00 1.5000000e+00 1.2000000e+00 1.3000000e+00 2.2000000e+00 9.0000000e-01 1.2000000e+00 2.9000000e+00 2.0000000e+00 1.4000000e+00 1.1000000e+00 1.8000000e+00 6.0000000e-01 1.7000000e+00 6.0000000e-01 1.2000000e+00 7.2000000e+00 6.5000000e+00 7.5000000e+00 5.8000000e+00 7.1000000e+00 6.0000000e+00 6.6000000e+00 4.7000000e+00 6.9000000e+00 5.1000000e+00 5.2000000e+00 5.9000000e+00 6.1000000e+00 6.6000000e+00 4.9000000e+00 6.7000000e+00 5.9000000e+00 5.5000000e+00 7.3000000e+00 5.4000000e+00 6.6000000e+00 5.9000000e+00 7.5000000e+00 6.5000000e+00 6.4000000e+00 6.7000000e+00 7.5000000e+00 7.7000000e+00 6.4000000e+00 4.9000000e+00 5.3000000e+00 5.1000000e+00 5.5000000e+00 7.3000000e+00 5.7000000e+00 6.0000000e+00 7.1000000e+00 7.0000000e+00 5.3000000e+00 5.6000000e+00 5.8000000e+00 6.4000000e+00 5.7000000e+00 4.7000000e+00 5.7000000e+00 5.4000000e+00 5.6000000e+00 6.2000000e+00 4.2000000e+00 5.6000000e+00 8.8000000e+00 7.4000000e+00 9.4000000e+00 8.1000000e+00 8.8000000e+00 1.0600000e+01 6.5000000e+00 9.8000000e+00 9.1000000e+00 9.5000000e+00 7.7000000e+00 8.2000000e+00 8.7000000e+00 7.5000000e+00 7.8000000e+00 8.1000000e+00 8.1000000e+00 1.0100000e+01 1.1600000e+01 7.6000000e+00 9.0000000e+00 7.0000000e+00 1.0900000e+01 7.6000000e+00 8.5000000e+00 9.1000000e+00 7.3000000e+00 7.1000000e+00 8.6000000e+00 8.9000000e+00 9.9000000e+00 9.8000000e+00 8.7000000e+00 7.4000000e+00 7.8000000e+00 1.0400000e+01 8.2000000e+00 7.9000000e+00 6.9000000e+00 8.6000000e+00 8.9000000e+00 8.5000000e+00 7.4000000e+00 9.1000000e+00 8.9000000e+00 8.5000000e+00 8.0000000e+00 8.0000000e+00 7.8000000e+00 7.1000000e+00 1.9000000e+00 1.7000000e+00 8.0000000e-01 1.9000000e+00 2.4000000e+00 1.3000000e+00 1.4000000e+00 3.1000000e+00 2.2000000e+00 1.8000000e+00 1.5000000e+00 2.0000000e+00 1.0000000e+00 1.9000000e+00 8.0000000e-01 1.4000000e+00 7.0000000e+00 6.3000000e+00 7.3000000e+00 5.6000000e+00 6.9000000e+00 5.8000000e+00 6.4000000e+00 5.1000000e+00 6.7000000e+00 5.5000000e+00 5.6000000e+00 5.7000000e+00 5.9000000e+00 6.4000000e+00 4.7000000e+00 6.5000000e+00 5.7000000e+00 5.3000000e+00 7.1000000e+00 5.2000000e+00 6.4000000e+00 5.7000000e+00 7.3000000e+00 6.3000000e+00 6.2000000e+00 6.5000000e+00 7.3000000e+00 7.5000000e+00 6.2000000e+00 4.7000000e+00 5.1000000e+00 4.9000000e+00 5.3000000e+00 7.1000000e+00 5.7000000e+00 5.8000000e+00 6.9000000e+00 6.8000000e+00 5.1000000e+00 5.4000000e+00 5.6000000e+00 6.2000000e+00 5.5000000e+00 5.1000000e+00 5.5000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 4.6000000e+00 5.4000000e+00 8.6000000e+00 7.2000000e+00 9.2000000e+00 7.9000000e+00 8.6000000e+00 1.0400000e+01 6.9000000e+00 9.6000000e+00 8.9000000e+00 9.3000000e+00 7.5000000e+00 8.0000000e+00 8.5000000e+00 7.3000000e+00 7.6000000e+00 7.9000000e+00 7.9000000e+00 9.9000000e+00 1.1400000e+01 7.4000000e+00 8.8000000e+00 6.8000000e+00 1.0700000e+01 7.4000000e+00 8.3000000e+00 8.9000000e+00 7.1000000e+00 6.9000000e+00 8.4000000e+00 8.7000000e+00 9.7000000e+00 9.6000000e+00 8.5000000e+00 7.2000000e+00 7.6000000e+00 1.0200000e+01 8.0000000e+00 7.7000000e+00 6.7000000e+00 8.4000000e+00 8.7000000e+00 8.3000000e+00 7.2000000e+00 8.9000000e+00 8.7000000e+00 8.3000000e+00 7.8000000e+00 7.8000000e+00 7.6000000e+00 6.9000000e+00 6.0000000e-01 1.3000000e+00 0.0000000e+00 9.0000000e-01 6.0000000e-01 9.0000000e-01 1.6000000e+00 9.0000000e-01 1.1000000e+00 1.6000000e+00 5.0000000e-01 1.1000000e+00 6.0000000e-01 1.1000000e+00 5.0000000e-01 6.7000000e+00 6.0000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 6.3000000e+00 3.4000000e+00 6.2000000e+00 4.4000000e+00 4.1000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 6.1000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.9000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 3.6000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.3000000e+00 4.9000000e+00 8.5000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.2000000e+00 9.1000000e+00 8.4000000e+00 9.8000000e+00 7.2000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.6000000e+00 7.4000000e+00 1.0800000e+01 1.0900000e+01 6.9000000e+00 8.5000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 8.2000000e+00 8.6000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 1.0500000e+01 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 8.1000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.6000000e+00 8.6000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.7000000e+00 6.4000000e+00 9.0000000e-01 6.0000000e-01 9.0000000e-01 6.0000000e-01 5.0000000e-01 1.6000000e+00 7.0000000e-01 1.1000000e+00 1.6000000e+00 7.0000000e-01 1.1000000e+00 6.0000000e-01 1.1000000e+00 3.0000000e-01 6.7000000e+00 6.0000000e+00 7.0000000e+00 5.3000000e+00 6.6000000e+00 5.5000000e+00 6.3000000e+00 3.8000000e+00 6.4000000e+00 4.6000000e+00 4.3000000e+00 5.4000000e+00 5.6000000e+00 6.1000000e+00 4.4000000e+00 6.2000000e+00 5.4000000e+00 5.0000000e+00 6.8000000e+00 4.9000000e+00 6.1000000e+00 5.4000000e+00 7.0000000e+00 6.0000000e+00 5.9000000e+00 6.2000000e+00 7.0000000e+00 7.2000000e+00 5.9000000e+00 4.4000000e+00 4.8000000e+00 4.6000000e+00 5.0000000e+00 6.8000000e+00 5.2000000e+00 5.9000000e+00 6.6000000e+00 6.5000000e+00 4.8000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 5.2000000e+00 3.8000000e+00 5.2000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 3.5000000e+00 5.1000000e+00 8.5000000e+00 6.9000000e+00 8.9000000e+00 7.6000000e+00 8.3000000e+00 1.0100000e+01 5.6000000e+00 9.3000000e+00 8.6000000e+00 9.8000000e+00 7.2000000e+00 7.7000000e+00 8.2000000e+00 7.0000000e+00 7.3000000e+00 7.6000000e+00 7.6000000e+00 1.0800000e+01 1.1100000e+01 7.1000000e+00 8.5000000e+00 6.5000000e+00 1.0400000e+01 7.1000000e+00 8.2000000e+00 8.6000000e+00 6.8000000e+00 6.6000000e+00 8.1000000e+00 8.4000000e+00 9.4000000e+00 1.0500000e+01 8.2000000e+00 6.9000000e+00 7.3000000e+00 9.9000000e+00 8.1000000e+00 7.4000000e+00 6.4000000e+00 8.1000000e+00 8.4000000e+00 8.0000000e+00 6.9000000e+00 8.6000000e+00 8.6000000e+00 8.0000000e+00 7.5000000e+00 7.5000000e+00 7.7000000e+00 6.6000000e+00 1.3000000e+00 1.6000000e+00 7.0000000e-01 6.0000000e-01 2.3000000e+00 1.4000000e+00 1.2000000e+00 1.5000000e+00 1.4000000e+00 1.0000000e+00 1.3000000e+00 6.0000000e-01 8.0000000e-01 6.4000000e+00 5.7000000e+00 6.7000000e+00 5.0000000e+00 6.3000000e+00 5.2000000e+00 5.8000000e+00 4.5000000e+00 6.1000000e+00 4.9000000e+00 5.0000000e+00 5.1000000e+00 5.3000000e+00 5.8000000e+00 4.1000000e+00 5.9000000e+00 5.1000000e+00 4.7000000e+00 6.5000000e+00 4.6000000e+00 5.8000000e+00 5.1000000e+00 6.7000000e+00 5.7000000e+00 5.6000000e+00 5.9000000e+00 6.7000000e+00 6.9000000e+00 5.6000000e+00 4.1000000e+00 4.5000000e+00 4.3000000e+00 4.7000000e+00 6.5000000e+00 5.1000000e+00 5.2000000e+00 6.3000000e+00 6.2000000e+00 4.5000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 4.9000000e+00 4.5000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 4.0000000e+00 4.8000000e+00 8.0000000e+00 6.6000000e+00 8.6000000e+00 7.3000000e+00 8.0000000e+00 9.8000000e+00 6.3000000e+00 9.0000000e+00 8.3000000e+00 8.9000000e+00 6.9000000e+00 7.4000000e+00 7.9000000e+00 6.7000000e+00 7.0000000e+00 7.3000000e+00 7.3000000e+00 9.9000000e+00 1.0800000e+01 6.8000000e+00 8.2000000e+00 6.2000000e+00 1.0100000e+01 6.8000000e+00 7.7000000e+00 8.3000000e+00 6.5000000e+00 6.3000000e+00 7.8000000e+00 8.1000000e+00 9.1000000e+00 9.6000000e+00 7.9000000e+00 6.6000000e+00 7.0000000e+00 9.6000000e+00 7.4000000e+00 7.1000000e+00 6.1000000e+00 7.8000000e+00 8.1000000e+00 7.7000000e+00 6.6000000e+00 8.3000000e+00 8.1000000e+00 7.7000000e+00 7.2000000e+00 7.2000000e+00 7.0000000e+00 6.3000000e+00 9.0000000e-01 6.0000000e-01 9.0000000e-01 1.6000000e+00 9.0000000e-01 1.1000000e+00 1.6000000e+00 5.0000000e-01 1.1000000e+00 6.0000000e-01 1.1000000e+00 5.0000000e-01 6.7000000e+00 6.0000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 6.3000000e+00 3.4000000e+00 6.2000000e+00 4.4000000e+00 4.1000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 6.1000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.9000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 3.6000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.3000000e+00 4.9000000e+00 8.5000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.2000000e+00 9.1000000e+00 8.4000000e+00 9.8000000e+00 7.2000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.6000000e+00 7.4000000e+00 1.0800000e+01 1.0900000e+01 6.9000000e+00 8.5000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 8.2000000e+00 8.6000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 1.0500000e+01 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 8.1000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.6000000e+00 8.6000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.7000000e+00 6.4000000e+00 1.3000000e+00 1.2000000e+00 9.0000000e-01 2.0000000e-01 1.8000000e+00 2.3000000e+00 6.0000000e-01 1.8000000e+00 5.0000000e-01 1.8000000e+00 1.0000000e+00 7.4000000e+00 6.7000000e+00 7.5000000e+00 5.6000000e+00 6.9000000e+00 5.8000000e+00 7.0000000e+00 3.9000000e+00 6.7000000e+00 4.9000000e+00 4.6000000e+00 5.7000000e+00 5.9000000e+00 6.4000000e+00 4.7000000e+00 6.7000000e+00 5.7000000e+00 5.3000000e+00 7.1000000e+00 5.2000000e+00 6.8000000e+00 5.7000000e+00 7.3000000e+00 6.3000000e+00 6.2000000e+00 6.5000000e+00 7.3000000e+00 7.5000000e+00 6.2000000e+00 4.7000000e+00 5.1000000e+00 4.9000000e+00 5.3000000e+00 7.1000000e+00 5.5000000e+00 6.6000000e+00 7.1000000e+00 6.8000000e+00 5.1000000e+00 5.4000000e+00 5.6000000e+00 6.2000000e+00 5.5000000e+00 4.1000000e+00 5.5000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 3.8000000e+00 5.4000000e+00 9.2000000e+00 7.2000000e+00 9.2000000e+00 7.9000000e+00 8.6000000e+00 1.0400000e+01 5.7000000e+00 9.6000000e+00 8.9000000e+00 1.0500000e+01 7.9000000e+00 8.0000000e+00 8.5000000e+00 7.3000000e+00 7.6000000e+00 8.3000000e+00 7.9000000e+00 1.1500000e+01 1.1400000e+01 7.4000000e+00 9.2000000e+00 6.8000000e+00 1.0700000e+01 7.4000000e+00 8.9000000e+00 9.3000000e+00 7.1000000e+00 6.9000000e+00 8.4000000e+00 8.7000000e+00 9.7000000e+00 1.1200000e+01 8.5000000e+00 7.2000000e+00 7.6000000e+00 1.0200000e+01 8.8000000e+00 7.9000000e+00 6.7000000e+00 8.6000000e+00 8.9000000e+00 8.5000000e+00 7.2000000e+00 9.3000000e+00 9.3000000e+00 8.3000000e+00 7.8000000e+00 7.8000000e+00 8.4000000e+00 6.9000000e+00 5.0000000e-01 2.0000000e+00 1.1000000e+00 7.0000000e-01 1.0000000e+00 9.0000000e-01 5.0000000e-01 8.0000000e-01 5.0000000e-01 3.0000000e-01 6.5000000e+00 5.8000000e+00 6.8000000e+00 5.1000000e+00 6.4000000e+00 5.3000000e+00 5.9000000e+00 3.8000000e+00 6.2000000e+00 4.4000000e+00 4.3000000e+00 5.2000000e+00 5.4000000e+00 5.9000000e+00 4.2000000e+00 6.0000000e+00 5.2000000e+00 4.8000000e+00 6.6000000e+00 4.7000000e+00 5.9000000e+00 5.2000000e+00 6.8000000e+00 5.8000000e+00 5.7000000e+00 6.0000000e+00 6.8000000e+00 7.0000000e+00 5.7000000e+00 4.2000000e+00 4.6000000e+00 4.4000000e+00 4.8000000e+00 6.6000000e+00 5.0000000e+00 5.3000000e+00 6.4000000e+00 6.3000000e+00 4.6000000e+00 4.9000000e+00 5.1000000e+00 5.7000000e+00 5.0000000e+00 3.8000000e+00 5.0000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 3.3000000e+00 4.9000000e+00 8.1000000e+00 6.7000000e+00 8.7000000e+00 7.4000000e+00 8.1000000e+00 9.9000000e+00 5.6000000e+00 9.1000000e+00 8.4000000e+00 9.2000000e+00 7.0000000e+00 7.5000000e+00 8.0000000e+00 6.8000000e+00 7.1000000e+00 7.4000000e+00 7.4000000e+00 1.0200000e+01 1.0900000e+01 6.9000000e+00 8.3000000e+00 6.3000000e+00 1.0200000e+01 6.9000000e+00 7.8000000e+00 8.4000000e+00 6.6000000e+00 6.4000000e+00 7.9000000e+00 8.2000000e+00 9.2000000e+00 9.9000000e+00 8.0000000e+00 6.7000000e+00 7.1000000e+00 9.7000000e+00 7.5000000e+00 7.2000000e+00 6.2000000e+00 7.9000000e+00 8.2000000e+00 7.8000000e+00 6.7000000e+00 8.4000000e+00 8.2000000e+00 7.8000000e+00 7.3000000e+00 7.3000000e+00 7.1000000e+00 6.4000000e+00 1.7000000e+00 1.0000000e+00 6.0000000e-01 1.1000000e+00 8.0000000e-01 8.0000000e-01 9.0000000e-01 8.0000000e-01 4.0000000e-01 6.8000000e+00 6.1000000e+00 7.1000000e+00 5.4000000e+00 6.7000000e+00 5.6000000e+00 6.2000000e+00 3.9000000e+00 6.5000000e+00 4.7000000e+00 4.4000000e+00 5.5000000e+00 5.7000000e+00 6.2000000e+00 4.5000000e+00 6.3000000e+00 5.5000000e+00 5.1000000e+00 6.9000000e+00 5.0000000e+00 6.2000000e+00 5.5000000e+00 7.1000000e+00 6.1000000e+00 6.0000000e+00 6.3000000e+00 7.1000000e+00 7.3000000e+00 6.0000000e+00 4.5000000e+00 4.9000000e+00 4.7000000e+00 5.1000000e+00 6.9000000e+00 5.3000000e+00 5.6000000e+00 6.7000000e+00 6.6000000e+00 4.9000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 5.3000000e+00 3.9000000e+00 5.3000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 3.6000000e+00 5.2000000e+00 8.4000000e+00 7.0000000e+00 9.0000000e+00 7.7000000e+00 8.4000000e+00 1.0200000e+01 5.7000000e+00 9.4000000e+00 8.7000000e+00 9.3000000e+00 7.3000000e+00 7.8000000e+00 8.3000000e+00 7.1000000e+00 7.4000000e+00 7.7000000e+00 7.7000000e+00 1.0300000e+01 1.1200000e+01 7.2000000e+00 8.6000000e+00 6.6000000e+00 1.0500000e+01 7.2000000e+00 8.1000000e+00 8.7000000e+00 6.9000000e+00 6.7000000e+00 8.2000000e+00 8.5000000e+00 9.5000000e+00 1.0000000e+01 8.3000000e+00 7.0000000e+00 7.4000000e+00 1.0000000e+01 7.8000000e+00 7.5000000e+00 6.5000000e+00 8.2000000e+00 8.5000000e+00 8.1000000e+00 7.0000000e+00 8.7000000e+00 8.5000000e+00 8.1000000e+00 7.6000000e+00 7.6000000e+00 7.4000000e+00 6.7000000e+00 1.1000000e+00 2.3000000e+00 2.8000000e+00 1.1000000e+00 2.5000000e+00 1.2000000e+00 2.5000000e+00 1.7000000e+00 7.9000000e+00 7.2000000e+00 8.0000000e+00 4.7000000e+00 7.0000000e+00 5.9000000e+00 7.5000000e+00 3.2000000e+00 7.0000000e+00 4.8000000e+00 3.7000000e+00 6.2000000e+00 5.0000000e+00 6.7000000e+00 5.0000000e+00 7.2000000e+00 6.2000000e+00 5.2000000e+00 6.2000000e+00 4.7000000e+00 7.3000000e+00 5.8000000e+00 6.8000000e+00 6.4000000e+00 6.5000000e+00 7.0000000e+00 7.4000000e+00 8.0000000e+00 6.5000000e+00 4.4000000e+00 4.4000000e+00 4.2000000e+00 5.2000000e+00 7.0000000e+00 6.0000000e+00 7.1000000e+00 7.6000000e+00 5.9000000e+00 5.6000000e+00 4.9000000e+00 5.3000000e+00 6.7000000e+00 5.2000000e+00 3.2000000e+00 5.4000000e+00 5.7000000e+00 5.7000000e+00 6.3000000e+00 3.3000000e+00 5.5000000e+00 9.7000000e+00 7.1000000e+00 9.7000000e+00 8.2000000e+00 9.1000000e+00 1.0900000e+01 5.2000000e+00 9.9000000e+00 8.4000000e+00 1.1000000e+01 8.4000000e+00 7.9000000e+00 9.0000000e+00 6.8000000e+00 7.7000000e+00 8.8000000e+00 8.4000000e+00 1.2000000e+01 1.1100000e+01 6.5000000e+00 9.7000000e+00 6.9000000e+00 1.0800000e+01 7.3000000e+00 9.4000000e+00 9.8000000e+00 7.2000000e+00 7.4000000e+00 8.5000000e+00 9.2000000e+00 9.8000000e+00 1.1700000e+01 8.6000000e+00 7.3000000e+00 7.3000000e+00 1.0700000e+01 9.3000000e+00 8.4000000e+00 7.2000000e+00 9.1000000e+00 9.4000000e+00 9.0000000e+00 7.1000000e+00 9.8000000e+00 9.8000000e+00 8.8000000e+00 7.3000000e+00 8.3000000e+00 8.9000000e+00 7.4000000e+00 1.6000000e+00 2.1000000e+00 8.0000000e-01 1.6000000e+00 3.0000000e-01 1.6000000e+00 8.0000000e-01 7.2000000e+00 6.5000000e+00 7.5000000e+00 5.8000000e+00 7.1000000e+00 6.0000000e+00 6.8000000e+00 4.1000000e+00 6.9000000e+00 5.1000000e+00 4.8000000e+00 5.9000000e+00 6.1000000e+00 6.6000000e+00 4.9000000e+00 6.7000000e+00 5.9000000e+00 5.5000000e+00 7.3000000e+00 5.4000000e+00 6.6000000e+00 5.9000000e+00 7.5000000e+00 6.5000000e+00 6.4000000e+00 6.7000000e+00 7.5000000e+00 7.7000000e+00 6.4000000e+00 4.9000000e+00 5.3000000e+00 5.1000000e+00 5.5000000e+00 7.3000000e+00 5.7000000e+00 6.4000000e+00 7.1000000e+00 7.0000000e+00 5.3000000e+00 5.6000000e+00 5.8000000e+00 6.4000000e+00 5.7000000e+00 4.3000000e+00 5.7000000e+00 5.4000000e+00 5.6000000e+00 6.2000000e+00 4.0000000e+00 5.6000000e+00 9.0000000e+00 7.4000000e+00 9.4000000e+00 8.1000000e+00 8.8000000e+00 1.0600000e+01 5.9000000e+00 9.8000000e+00 9.1000000e+00 1.0300000e+01 7.7000000e+00 8.2000000e+00 8.7000000e+00 7.5000000e+00 7.8000000e+00 8.1000000e+00 8.1000000e+00 1.1300000e+01 1.1600000e+01 7.6000000e+00 9.0000000e+00 7.0000000e+00 1.0900000e+01 7.6000000e+00 8.7000000e+00 9.1000000e+00 7.3000000e+00 7.1000000e+00 8.6000000e+00 8.9000000e+00 9.9000000e+00 1.1000000e+01 8.7000000e+00 7.4000000e+00 7.8000000e+00 1.0400000e+01 8.6000000e+00 7.9000000e+00 6.9000000e+00 8.6000000e+00 8.9000000e+00 8.5000000e+00 7.4000000e+00 9.1000000e+00 9.1000000e+00 8.5000000e+00 8.0000000e+00 8.0000000e+00 8.2000000e+00 7.1000000e+00 9.0000000e-01 1.2000000e+00 8.0000000e-01 1.3000000e+00 1.0000000e+00 8.0000000e-01 6.2000000e+00 5.5000000e+00 6.5000000e+00 4.8000000e+00 6.1000000e+00 5.0000000e+00 5.6000000e+00 3.3000000e+00 5.9000000e+00 4.1000000e+00 3.8000000e+00 4.9000000e+00 5.1000000e+00 5.6000000e+00 3.9000000e+00 5.7000000e+00 4.9000000e+00 4.5000000e+00 6.3000000e+00 4.4000000e+00 5.6000000e+00 4.9000000e+00 6.5000000e+00 5.5000000e+00 5.4000000e+00 5.7000000e+00 6.5000000e+00 6.7000000e+00 5.4000000e+00 3.9000000e+00 4.3000000e+00 4.1000000e+00 4.5000000e+00 6.3000000e+00 4.7000000e+00 5.0000000e+00 6.1000000e+00 6.0000000e+00 4.3000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 4.7000000e+00 3.3000000e+00 4.7000000e+00 4.4000000e+00 4.6000000e+00 5.2000000e+00 3.0000000e+00 4.6000000e+00 7.8000000e+00 6.4000000e+00 8.4000000e+00 7.1000000e+00 7.8000000e+00 9.6000000e+00 5.1000000e+00 8.8000000e+00 8.1000000e+00 8.7000000e+00 6.7000000e+00 7.2000000e+00 7.7000000e+00 6.5000000e+00 6.8000000e+00 7.1000000e+00 7.1000000e+00 9.7000000e+00 1.0600000e+01 6.6000000e+00 8.0000000e+00 6.0000000e+00 9.9000000e+00 6.6000000e+00 7.5000000e+00 8.1000000e+00 6.3000000e+00 6.1000000e+00 7.6000000e+00 7.9000000e+00 8.9000000e+00 9.4000000e+00 7.7000000e+00 6.4000000e+00 6.8000000e+00 9.4000000e+00 7.2000000e+00 6.9000000e+00 5.9000000e+00 7.6000000e+00 7.9000000e+00 7.5000000e+00 6.4000000e+00 8.1000000e+00 7.9000000e+00 7.5000000e+00 7.0000000e+00 7.0000000e+00 6.8000000e+00 6.1000000e+00 1.7000000e+00 5.0000000e-01 1.8000000e+00 9.0000000e-01 1.3000000e+00 6.3000000e+00 5.6000000e+00 6.6000000e+00 4.9000000e+00 6.2000000e+00 5.1000000e+00 5.7000000e+00 3.6000000e+00 6.0000000e+00 4.2000000e+00 4.1000000e+00 5.0000000e+00 5.2000000e+00 5.7000000e+00 4.0000000e+00 5.8000000e+00 5.0000000e+00 4.6000000e+00 6.4000000e+00 4.5000000e+00 5.7000000e+00 5.0000000e+00 6.6000000e+00 5.6000000e+00 5.5000000e+00 5.8000000e+00 6.6000000e+00 6.8000000e+00 5.5000000e+00 4.0000000e+00 4.4000000e+00 4.2000000e+00 4.6000000e+00 6.4000000e+00 4.8000000e+00 5.1000000e+00 6.2000000e+00 6.1000000e+00 4.4000000e+00 4.7000000e+00 4.9000000e+00 5.5000000e+00 4.8000000e+00 3.6000000e+00 4.8000000e+00 4.5000000e+00 4.7000000e+00 5.3000000e+00 3.1000000e+00 4.7000000e+00 7.9000000e+00 6.5000000e+00 8.5000000e+00 7.2000000e+00 7.9000000e+00 9.7000000e+00 5.4000000e+00 8.9000000e+00 8.2000000e+00 8.6000000e+00 6.8000000e+00 7.3000000e+00 7.8000000e+00 6.6000000e+00 6.9000000e+00 7.2000000e+00 7.2000000e+00 9.2000000e+00 1.0700000e+01 6.7000000e+00 8.1000000e+00 6.1000000e+00 1.0000000e+01 6.7000000e+00 7.6000000e+00 8.2000000e+00 6.4000000e+00 6.2000000e+00 7.7000000e+00 8.0000000e+00 9.0000000e+00 8.9000000e+00 7.8000000e+00 6.5000000e+00 6.9000000e+00 9.5000000e+00 7.3000000e+00 7.0000000e+00 6.0000000e+00 7.7000000e+00 8.0000000e+00 7.6000000e+00 6.5000000e+00 8.2000000e+00 8.0000000e+00 7.6000000e+00 7.1000000e+00 7.1000000e+00 6.9000000e+00 6.2000000e+00 1.4000000e+00 5.0000000e-01 1.4000000e+00 6.0000000e-01 6.8000000e+00 6.1000000e+00 6.9000000e+00 5.0000000e+00 6.3000000e+00 5.2000000e+00 6.4000000e+00 3.3000000e+00 6.1000000e+00 4.3000000e+00 4.0000000e+00 5.1000000e+00 5.3000000e+00 5.8000000e+00 4.1000000e+00 6.1000000e+00 5.1000000e+00 4.7000000e+00 6.5000000e+00 4.6000000e+00 6.2000000e+00 5.1000000e+00 6.7000000e+00 5.7000000e+00 5.6000000e+00 5.9000000e+00 6.7000000e+00 6.9000000e+00 5.6000000e+00 4.1000000e+00 4.5000000e+00 4.3000000e+00 4.7000000e+00 6.5000000e+00 4.9000000e+00 6.0000000e+00 6.5000000e+00 6.2000000e+00 4.5000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 4.9000000e+00 3.5000000e+00 4.9000000e+00 4.6000000e+00 4.8000000e+00 5.4000000e+00 3.2000000e+00 4.8000000e+00 8.6000000e+00 6.6000000e+00 8.6000000e+00 7.3000000e+00 8.0000000e+00 9.8000000e+00 5.1000000e+00 9.0000000e+00 8.3000000e+00 9.9000000e+00 7.3000000e+00 7.4000000e+00 7.9000000e+00 6.7000000e+00 7.0000000e+00 7.7000000e+00 7.3000000e+00 1.0900000e+01 1.0800000e+01 6.8000000e+00 8.6000000e+00 6.2000000e+00 1.0100000e+01 6.8000000e+00 8.3000000e+00 8.7000000e+00 6.5000000e+00 6.3000000e+00 7.8000000e+00 8.1000000e+00 9.1000000e+00 1.0600000e+01 7.9000000e+00 6.6000000e+00 7.0000000e+00 9.6000000e+00 8.2000000e+00 7.3000000e+00 6.1000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.6000000e+00 8.7000000e+00 8.7000000e+00 7.7000000e+00 7.2000000e+00 7.2000000e+00 7.8000000e+00 6.3000000e+00 1.3000000e+00 4.0000000e-01 8.0000000e-01 6.8000000e+00 6.1000000e+00 7.1000000e+00 5.4000000e+00 6.7000000e+00 5.6000000e+00 6.2000000e+00 4.1000000e+00 6.5000000e+00 4.7000000e+00 4.6000000e+00 5.5000000e+00 5.7000000e+00 6.2000000e+00 4.5000000e+00 6.3000000e+00 5.5000000e+00 5.1000000e+00 6.9000000e+00 5.0000000e+00 6.2000000e+00 5.5000000e+00 7.1000000e+00 6.1000000e+00 6.0000000e+00 6.3000000e+00 7.1000000e+00 7.3000000e+00 6.0000000e+00 4.5000000e+00 4.9000000e+00 4.7000000e+00 5.1000000e+00 6.9000000e+00 5.3000000e+00 5.6000000e+00 6.7000000e+00 6.6000000e+00 4.9000000e+00 5.2000000e+00 5.4000000e+00 6.0000000e+00 5.3000000e+00 4.1000000e+00 5.3000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 3.6000000e+00 5.2000000e+00 8.4000000e+00 7.0000000e+00 9.0000000e+00 7.7000000e+00 8.4000000e+00 1.0200000e+01 5.9000000e+00 9.4000000e+00 8.7000000e+00 9.1000000e+00 7.3000000e+00 7.8000000e+00 8.3000000e+00 7.1000000e+00 7.4000000e+00 7.7000000e+00 7.7000000e+00 9.7000000e+00 1.1200000e+01 7.2000000e+00 8.6000000e+00 6.6000000e+00 1.0500000e+01 7.2000000e+00 8.1000000e+00 8.7000000e+00 6.9000000e+00 6.7000000e+00 8.2000000e+00 8.5000000e+00 9.5000000e+00 9.4000000e+00 8.3000000e+00 7.0000000e+00 7.4000000e+00 1.0000000e+01 7.8000000e+00 7.5000000e+00 6.5000000e+00 8.2000000e+00 8.5000000e+00 8.1000000e+00 7.0000000e+00 8.7000000e+00 8.5000000e+00 8.1000000e+00 7.6000000e+00 7.6000000e+00 7.4000000e+00 6.7000000e+00 1.3000000e+00 5.0000000e-01 6.9000000e+00 6.2000000e+00 7.2000000e+00 5.5000000e+00 6.8000000e+00 5.7000000e+00 6.5000000e+00 3.8000000e+00 6.6000000e+00 4.8000000e+00 4.5000000e+00 5.6000000e+00 5.8000000e+00 6.3000000e+00 4.6000000e+00 6.4000000e+00 5.6000000e+00 5.2000000e+00 7.0000000e+00 5.1000000e+00 6.3000000e+00 5.6000000e+00 7.2000000e+00 6.2000000e+00 6.1000000e+00 6.4000000e+00 7.2000000e+00 7.4000000e+00 6.1000000e+00 4.6000000e+00 5.0000000e+00 4.8000000e+00 5.2000000e+00 7.0000000e+00 5.4000000e+00 6.1000000e+00 6.8000000e+00 6.7000000e+00 5.0000000e+00 5.3000000e+00 5.5000000e+00 6.1000000e+00 5.4000000e+00 4.0000000e+00 5.4000000e+00 5.1000000e+00 5.3000000e+00 5.9000000e+00 3.7000000e+00 5.3000000e+00 8.7000000e+00 7.1000000e+00 9.1000000e+00 7.8000000e+00 8.5000000e+00 1.0300000e+01 5.6000000e+00 9.5000000e+00 8.8000000e+00 1.0000000e+01 7.4000000e+00 7.9000000e+00 8.4000000e+00 7.2000000e+00 7.5000000e+00 7.8000000e+00 7.8000000e+00 1.1000000e+01 1.1300000e+01 7.3000000e+00 8.7000000e+00 6.7000000e+00 1.0600000e+01 7.3000000e+00 8.4000000e+00 8.8000000e+00 7.0000000e+00 6.8000000e+00 8.3000000e+00 8.6000000e+00 9.6000000e+00 1.0700000e+01 8.4000000e+00 7.1000000e+00 7.5000000e+00 1.0100000e+01 8.3000000e+00 7.6000000e+00 6.6000000e+00 8.3000000e+00 8.6000000e+00 8.2000000e+00 7.1000000e+00 8.8000000e+00 8.8000000e+00 8.2000000e+00 7.7000000e+00 7.7000000e+00 7.9000000e+00 6.8000000e+00 8.0000000e-01 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.0000000e+00 4.3000000e+00 6.3000000e+00 4.7000000e+00 4.8000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.4000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 4.3000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.8000000e+00 5.0000000e+00 8.2000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 6.1000000e+00 9.2000000e+00 8.5000000e+00 8.9000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 9.7000000e+00 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 7.9000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 9.4000000e+00 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 7.6000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.3000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.2000000e+00 6.5000000e+00 6.6000000e+00 5.9000000e+00 6.9000000e+00 5.2000000e+00 6.5000000e+00 5.4000000e+00 6.0000000e+00 3.7000000e+00 6.3000000e+00 4.5000000e+00 4.2000000e+00 5.3000000e+00 5.5000000e+00 6.0000000e+00 4.3000000e+00 6.1000000e+00 5.3000000e+00 4.9000000e+00 6.7000000e+00 4.8000000e+00 6.0000000e+00 5.3000000e+00 6.9000000e+00 5.9000000e+00 5.8000000e+00 6.1000000e+00 6.9000000e+00 7.1000000e+00 5.8000000e+00 4.3000000e+00 4.7000000e+00 4.5000000e+00 4.9000000e+00 6.7000000e+00 5.1000000e+00 5.6000000e+00 6.5000000e+00 6.4000000e+00 4.7000000e+00 5.0000000e+00 5.2000000e+00 5.8000000e+00 5.1000000e+00 3.7000000e+00 5.1000000e+00 4.8000000e+00 5.0000000e+00 5.6000000e+00 3.4000000e+00 5.0000000e+00 8.2000000e+00 6.8000000e+00 8.8000000e+00 7.5000000e+00 8.2000000e+00 1.0000000e+01 5.5000000e+00 9.2000000e+00 8.5000000e+00 9.5000000e+00 7.1000000e+00 7.6000000e+00 8.1000000e+00 6.9000000e+00 7.2000000e+00 7.5000000e+00 7.5000000e+00 1.0500000e+01 1.1000000e+01 7.0000000e+00 8.4000000e+00 6.4000000e+00 1.0300000e+01 7.0000000e+00 7.9000000e+00 8.5000000e+00 6.7000000e+00 6.5000000e+00 8.0000000e+00 8.3000000e+00 9.3000000e+00 1.0200000e+01 8.1000000e+00 6.8000000e+00 7.2000000e+00 9.8000000e+00 7.8000000e+00 7.3000000e+00 6.3000000e+00 8.0000000e+00 8.3000000e+00 7.9000000e+00 6.8000000e+00 8.5000000e+00 8.3000000e+00 7.9000000e+00 7.4000000e+00 7.4000000e+00 7.4000000e+00 6.5000000e+00 9.0000000e-01 5.0000000e-01 3.2000000e+00 1.1000000e+00 2.0000000e+00 1.0000000e+00 4.7000000e+00 9.0000000e-01 3.1000000e+00 4.8000000e+00 1.9000000e+00 3.1000000e+00 1.2000000e+00 2.9000000e+00 7.0000000e-01 1.9000000e+00 2.7000000e+00 2.1000000e+00 3.2000000e+00 1.6000000e+00 2.1000000e+00 1.7000000e+00 1.5000000e+00 1.4000000e+00 9.0000000e-01 7.0000000e-01 1.1000000e+00 1.6000000e+00 3.5000000e+00 3.5000000e+00 3.7000000e+00 2.7000000e+00 2.1000000e+00 2.1000000e+00 1.6000000e+00 5.0000000e-01 2.0000000e+00 2.3000000e+00 3.0000000e+00 2.6000000e+00 1.2000000e+00 2.7000000e+00 4.7000000e+00 2.5000000e+00 2.2000000e+00 2.2000000e+00 1.6000000e+00 4.6000000e+00 2.4000000e+00 3.2000000e+00 2.6000000e+00 2.2000000e+00 2.3000000e+00 2.6000000e+00 3.4000000e+00 3.3000000e+00 2.6000000e+00 2.5000000e+00 3.1000000e+00 1.5000000e+00 2.2000000e+00 1.9000000e+00 2.9000000e+00 3.0000000e+00 2.1000000e+00 1.9000000e+00 4.1000000e+00 4.4000000e+00 2.4000000e+00 2.0000000e+00 2.6000000e+00 3.7000000e+00 1.8000000e+00 2.1000000e+00 1.9000000e+00 1.7000000e+00 1.7000000e+00 2.6000000e+00 1.7000000e+00 2.7000000e+00 3.8000000e+00 2.7000000e+00 1.6000000e+00 2.4000000e+00 3.2000000e+00 2.8000000e+00 1.9000000e+00 1.7000000e+00 1.6000000e+00 2.3000000e+00 1.5000000e+00 2.6000000e+00 2.3000000e+00 2.5000000e+00 1.9000000e+00 2.2000000e+00 1.8000000e+00 2.6000000e+00 2.1000000e+00 1.0000000e+00 2.5000000e+00 6.0000000e-01 1.3000000e+00 5.0000000e-01 4.0000000e+00 8.0000000e-01 2.4000000e+00 4.1000000e+00 1.0000000e+00 2.4000000e+00 9.0000000e-01 2.2000000e+00 6.0000000e-01 1.0000000e+00 2.0000000e+00 1.2000000e+00 2.5000000e+00 1.1000000e+00 1.4000000e+00 1.2000000e+00 1.2000000e+00 7.0000000e-01 6.0000000e-01 1.2000000e+00 1.2000000e+00 7.0000000e-01 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.0000000e+00 1.6000000e+00 1.2000000e+00 7.0000000e-01 6.0000000e-01 1.3000000e+00 1.6000000e+00 2.3000000e+00 1.9000000e+00 7.0000000e-01 2.0000000e+00 4.0000000e+00 1.8000000e+00 1.5000000e+00 1.5000000e+00 9.0000000e-01 3.9000000e+00 1.7000000e+00 2.7000000e+00 2.1000000e+00 2.9000000e+00 1.8000000e+00 2.3000000e+00 4.1000000e+00 2.4000000e+00 3.3000000e+00 2.6000000e+00 3.8000000e+00 1.2000000e+00 1.7000000e+00 2.2000000e+00 2.4000000e+00 2.5000000e+00 1.6000000e+00 1.6000000e+00 4.8000000e+00 5.1000000e+00 1.9000000e+00 2.5000000e+00 2.1000000e+00 4.4000000e+00 1.3000000e+00 2.2000000e+00 2.6000000e+00 1.2000000e+00 1.2000000e+00 2.1000000e+00 2.4000000e+00 3.4000000e+00 4.5000000e+00 2.2000000e+00 1.1000000e+00 2.1000000e+00 3.9000000e+00 2.3000000e+00 1.4000000e+00 1.2000000e+00 2.1000000e+00 2.4000000e+00 2.0000000e+00 2.1000000e+00 2.6000000e+00 2.6000000e+00 2.0000000e+00 1.7000000e+00 1.5000000e+00 2.1000000e+00 1.6000000e+00 3.3000000e+00 1.0000000e+00 2.1000000e+00 1.1000000e+00 4.8000000e+00 1.0000000e+00 3.2000000e+00 4.9000000e+00 1.8000000e+00 3.2000000e+00 1.3000000e+00 3.0000000e+00 8.0000000e-01 1.8000000e+00 2.8000000e+00 2.0000000e+00 3.3000000e+00 1.5000000e+00 2.2000000e+00 1.2000000e+00 1.6000000e+00 1.5000000e+00 1.0000000e+00 6.0000000e-01 6.0000000e-01 1.5000000e+00 3.6000000e+00 3.6000000e+00 3.8000000e+00 2.8000000e+00 1.6000000e+00 2.0000000e+00 1.7000000e+00 4.0000000e-01 2.1000000e+00 2.4000000e+00 3.1000000e+00 2.7000000e+00 1.3000000e+00 2.8000000e+00 4.8000000e+00 2.6000000e+00 2.3000000e+00 2.3000000e+00 1.7000000e+00 4.7000000e+00 2.5000000e+00 2.9000000e+00 2.1000000e+00 1.9000000e+00 1.8000000e+00 2.1000000e+00 3.1000000e+00 3.2000000e+00 2.3000000e+00 2.0000000e+00 3.0000000e+00 1.2000000e+00 1.7000000e+00 1.4000000e+00 2.4000000e+00 2.5000000e+00 1.8000000e+00 1.4000000e+00 4.0000000e+00 4.1000000e+00 1.9000000e+00 1.7000000e+00 2.1000000e+00 3.4000000e+00 1.3000000e+00 1.8000000e+00 1.8000000e+00 1.4000000e+00 1.2000000e+00 2.1000000e+00 1.4000000e+00 2.4000000e+00 3.7000000e+00 2.2000000e+00 1.1000000e+00 2.1000000e+00 2.9000000e+00 2.5000000e+00 1.4000000e+00 1.4000000e+00 1.1000000e+00 1.8000000e+00 1.0000000e+00 2.1000000e+00 2.0000000e+00 2.2000000e+00 1.4000000e+00 1.7000000e+00 1.3000000e+00 2.3000000e+00 1.6000000e+00 2.3000000e+00 1.2000000e+00 2.8000000e+00 1.7000000e+00 2.3000000e+00 9.0000000e-01 1.6000000e+00 1.5000000e+00 9.0000000e-01 2.0000000e+00 1.1000000e+00 2.5000000e+00 1.5000000e+00 1.1000000e+00 1.5000000e+00 6.0000000e-01 2.6000000e+00 1.1000000e+00 2.1000000e+00 1.9000000e+00 1.8000000e+00 2.3000000e+00 2.7000000e+00 3.3000000e+00 1.8000000e+00 1.3000000e+00 5.0000000e-01 7.0000000e-01 9.0000000e-01 2.3000000e+00 1.5000000e+00 2.4000000e+00 2.9000000e+00 1.2000000e+00 9.0000000e-01 2.0000000e-01 8.0000000e-01 2.0000000e+00 7.0000000e-01 1.5000000e+00 7.0000000e-01 1.2000000e+00 1.0000000e+00 1.6000000e+00 1.8000000e+00 8.0000000e-01 5.0000000e+00 2.4000000e+00 5.0000000e+00 3.5000000e+00 4.4000000e+00 6.2000000e+00 1.7000000e+00 5.2000000e+00 3.7000000e+00 6.3000000e+00 3.7000000e+00 3.2000000e+00 4.3000000e+00 2.1000000e+00 3.0000000e+00 4.1000000e+00 3.7000000e+00 7.3000000e+00 6.4000000e+00 1.8000000e+00 5.0000000e+00 2.2000000e+00 6.1000000e+00 2.6000000e+00 4.7000000e+00 5.1000000e+00 2.5000000e+00 2.7000000e+00 3.8000000e+00 4.5000000e+00 5.1000000e+00 7.0000000e+00 3.9000000e+00 2.6000000e+00 2.6000000e+00 6.0000000e+00 4.6000000e+00 3.7000000e+00 2.5000000e+00 4.4000000e+00 4.7000000e+00 4.3000000e+00 2.4000000e+00 5.1000000e+00 5.1000000e+00 4.1000000e+00 2.6000000e+00 3.6000000e+00 4.2000000e+00 2.7000000e+00 1.1000000e+00 9.0000000e-01 3.8000000e+00 4.0000000e-01 2.2000000e+00 3.9000000e+00 1.2000000e+00 2.2000000e+00 7.0000000e-01 2.2000000e+00 8.0000000e-01 1.2000000e+00 1.8000000e+00 1.0000000e+00 2.3000000e+00 1.5000000e+00 1.2000000e+00 8.0000000e-01 8.0000000e-01 7.0000000e-01 6.0000000e-01 6.0000000e-01 1.0000000e+00 7.0000000e-01 2.6000000e+00 2.6000000e+00 2.8000000e+00 1.8000000e+00 1.2000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.1000000e+00 1.8000000e+00 2.1000000e+00 1.7000000e+00 7.0000000e-01 1.8000000e+00 3.8000000e+00 1.6000000e+00 1.7000000e+00 1.5000000e+00 9.0000000e-01 3.7000000e+00 1.5000000e+00 3.1000000e+00 1.7000000e+00 2.7000000e+00 1.6000000e+00 2.1000000e+00 3.9000000e+00 2.2000000e+00 2.9000000e+00 2.0000000e+00 4.0000000e+00 1.4000000e+00 1.3000000e+00 2.0000000e+00 2.0000000e+00 2.1000000e+00 2.0000000e+00 1.4000000e+00 5.0000000e+00 4.5000000e+00 1.5000000e+00 2.7000000e+00 1.7000000e+00 3.8000000e+00 9.0000000e-01 2.4000000e+00 2.8000000e+00 8.0000000e-01 1.2000000e+00 1.7000000e+00 2.2000000e+00 2.8000000e+00 4.7000000e+00 1.8000000e+00 7.0000000e-01 1.7000000e+00 3.7000000e+00 2.7000000e+00 1.6000000e+00 1.2000000e+00 2.1000000e+00 2.4000000e+00 2.0000000e+00 1.7000000e+00 2.8000000e+00 2.8000000e+00 1.8000000e+00 1.3000000e+00 1.3000000e+00 2.5000000e+00 1.6000000e+00 1.6000000e+00 2.7000000e+00 1.1000000e+00 1.3000000e+00 2.8000000e+00 9.0000000e-01 1.7000000e+00 8.0000000e-01 1.1000000e+00 1.5000000e+00 5.0000000e-01 9.0000000e-01 1.3000000e+00 1.2000000e+00 1.4000000e+00 9.0000000e-01 1.5000000e+00 7.0000000e-01 1.0000000e+00 1.3000000e+00 1.5000000e+00 2.1000000e+00 6.0000000e-01 1.5000000e+00 1.5000000e+00 1.7000000e+00 9.0000000e-01 1.3000000e+00 7.0000000e-01 1.2000000e+00 1.7000000e+00 1.2000000e+00 7.0000000e-01 1.0000000e+00 6.0000000e-01 8.0000000e-01 9.0000000e-01 2.7000000e+00 5.0000000e-01 6.0000000e-01 4.0000000e-01 8.0000000e-01 2.6000000e+00 4.0000000e-01 3.8000000e+00 1.4000000e+00 3.8000000e+00 2.3000000e+00 3.2000000e+00 5.0000000e+00 1.5000000e+00 4.0000000e+00 3.1000000e+00 5.1000000e+00 2.5000000e+00 2.2000000e+00 3.1000000e+00 1.5000000e+00 1.8000000e+00 2.9000000e+00 2.5000000e+00 6.1000000e+00 5.6000000e+00 1.6000000e+00 3.8000000e+00 1.2000000e+00 4.9000000e+00 1.6000000e+00 3.5000000e+00 3.9000000e+00 1.3000000e+00 1.5000000e+00 2.6000000e+00 3.3000000e+00 3.9000000e+00 5.8000000e+00 2.7000000e+00 1.4000000e+00 1.8000000e+00 4.8000000e+00 3.4000000e+00 2.5000000e+00 1.3000000e+00 3.2000000e+00 3.5000000e+00 3.1000000e+00 1.4000000e+00 3.9000000e+00 3.9000000e+00 2.9000000e+00 2.0000000e+00 2.4000000e+00 3.0000000e+00 1.5000000e+00 4.3000000e+00 1.1000000e+00 2.7000000e+00 4.4000000e+00 1.3000000e+00 2.7000000e+00 8.0000000e-01 2.5000000e+00 1.1000000e+00 1.3000000e+00 2.3000000e+00 1.5000000e+00 2.8000000e+00 8.0000000e-01 1.7000000e+00 1.1000000e+00 1.1000000e+00 1.2000000e+00 1.1000000e+00 1.3000000e+00 1.1000000e+00 1.0000000e+00 3.1000000e+00 3.1000000e+00 3.3000000e+00 2.3000000e+00 1.3000000e+00 1.5000000e+00 6.0000000e-01 7.0000000e-01 1.6000000e+00 1.9000000e+00 2.6000000e+00 2.2000000e+00 8.0000000e-01 2.3000000e+00 4.3000000e+00 2.1000000e+00 1.8000000e+00 1.8000000e+00 1.2000000e+00 4.2000000e+00 2.0000000e+00 2.2000000e+00 1.8000000e+00 2.8000000e+00 1.5000000e+00 2.2000000e+00 4.0000000e+00 2.5000000e+00 3.2000000e+00 2.5000000e+00 3.5000000e+00 1.1000000e+00 1.6000000e+00 2.1000000e+00 2.1000000e+00 2.2000000e+00 1.5000000e+00 1.5000000e+00 4.5000000e+00 5.0000000e+00 1.8000000e+00 2.4000000e+00 1.8000000e+00 4.3000000e+00 1.0000000e+00 1.9000000e+00 2.5000000e+00 9.0000000e-01 9.0000000e-01 2.0000000e+00 2.3000000e+00 3.3000000e+00 4.2000000e+00 2.1000000e+00 1.0000000e+00 2.0000000e+00 3.8000000e+00 1.8000000e+00 1.3000000e+00 9.0000000e-01 2.0000000e+00 2.3000000e+00 1.9000000e+00 1.8000000e+00 2.5000000e+00 2.3000000e+00 1.9000000e+00 1.4000000e+00 1.4000000e+00 1.6000000e+00 1.3000000e+00 3.8000000e+00 1.6000000e+00 7.0000000e-01 3.0000000e+00 2.0000000e+00 3.5000000e+00 1.8000000e+00 4.0000000e+00 3.0000000e+00 2.0000000e+00 3.2000000e+00 1.5000000e+00 4.1000000e+00 2.6000000e+00 3.6000000e+00 3.2000000e+00 3.3000000e+00 3.8000000e+00 4.2000000e+00 4.8000000e+00 3.3000000e+00 1.2000000e+00 1.2000000e+00 1.0000000e+00 2.0000000e+00 3.8000000e+00 2.8000000e+00 3.9000000e+00 4.4000000e+00 2.9000000e+00 2.4000000e+00 1.7000000e+00 2.1000000e+00 3.5000000e+00 2.0000000e+00 2.0000000e-01 2.2000000e+00 2.5000000e+00 2.5000000e+00 3.1000000e+00 7.0000000e-01 2.3000000e+00 6.5000000e+00 3.9000000e+00 6.5000000e+00 5.0000000e+00 5.9000000e+00 7.7000000e+00 2.0000000e+00 6.7000000e+00 5.2000000e+00 7.8000000e+00 5.2000000e+00 4.7000000e+00 5.8000000e+00 3.6000000e+00 4.5000000e+00 5.6000000e+00 5.2000000e+00 8.8000000e+00 7.9000000e+00 3.5000000e+00 6.5000000e+00 3.7000000e+00 7.6000000e+00 4.1000000e+00 6.2000000e+00 6.6000000e+00 4.0000000e+00 4.2000000e+00 5.3000000e+00 6.0000000e+00 6.6000000e+00 8.5000000e+00 5.4000000e+00 4.1000000e+00 4.1000000e+00 7.5000000e+00 6.1000000e+00 5.2000000e+00 4.0000000e+00 5.9000000e+00 6.2000000e+00 5.8000000e+00 3.9000000e+00 6.6000000e+00 6.6000000e+00 5.6000000e+00 4.1000000e+00 5.1000000e+00 5.7000000e+00 4.2000000e+00 2.4000000e+00 3.9000000e+00 1.4000000e+00 2.2000000e+00 7.0000000e-01 2.0000000e+00 6.0000000e-01 1.4000000e+00 1.8000000e+00 1.4000000e+00 2.3000000e+00 1.7000000e+00 1.2000000e+00 1.2000000e+00 8.0000000e-01 5.0000000e-01 4.0000000e-01 6.0000000e-01 1.0000000e+00 9.0000000e-01 2.6000000e+00 2.6000000e+00 2.8000000e+00 1.8000000e+00 1.6000000e+00 1.6000000e+00 1.5000000e+00 6.0000000e-01 1.1000000e+00 1.6000000e+00 2.1000000e+00 1.7000000e+00 7.0000000e-01 1.8000000e+00 3.8000000e+00 1.6000000e+00 1.5000000e+00 1.3000000e+00 7.0000000e-01 3.7000000e+00 1.5000000e+00 3.3000000e+00 2.1000000e+00 2.7000000e+00 1.8000000e+00 2.3000000e+00 3.9000000e+00 2.6000000e+00 2.9000000e+00 2.2000000e+00 4.0000000e+00 1.6000000e+00 1.7000000e+00 2.0000000e+00 2.4000000e+00 2.5000000e+00 2.2000000e+00 1.6000000e+00 5.0000000e+00 4.7000000e+00 1.9000000e+00 2.7000000e+00 2.1000000e+00 4.0000000e+00 1.3000000e+00 2.4000000e+00 2.8000000e+00 1.2000000e+00 1.4000000e+00 2.1000000e+00 2.2000000e+00 3.0000000e+00 4.7000000e+00 2.2000000e+00 1.1000000e+00 1.9000000e+00 3.7000000e+00 2.9000000e+00 1.8000000e+00 1.4000000e+00 2.1000000e+00 2.4000000e+00 2.0000000e+00 2.1000000e+00 2.8000000e+00 2.8000000e+00 1.8000000e+00 1.7000000e+00 1.5000000e+00 2.7000000e+00 1.8000000e+00 1.7000000e+00 1.4000000e+00 1.8000000e+00 1.9000000e+00 1.0000000e+00 2.4000000e+00 1.4000000e+00 1.2000000e+00 2.2000000e+00 9.0000000e-01 2.5000000e+00 1.2000000e+00 2.4000000e+00 2.0000000e+00 1.9000000e+00 2.2000000e+00 2.6000000e+00 3.2000000e+00 1.7000000e+00 1.4000000e+00 1.0000000e+00 1.2000000e+00 8.0000000e-01 2.2000000e+00 1.2000000e+00 2.3000000e+00 2.8000000e+00 2.1000000e+00 1.0000000e+00 7.0000000e-01 1.1000000e+00 1.9000000e+00 1.0000000e+00 1.6000000e+00 8.0000000e-01 1.3000000e+00 1.1000000e+00 1.7000000e+00 1.5000000e+00 9.0000000e-01 4.9000000e+00 2.3000000e+00 4.9000000e+00 3.4000000e+00 4.3000000e+00 6.1000000e+00 1.4000000e+00 5.1000000e+00 4.0000000e+00 6.2000000e+00 3.6000000e+00 3.1000000e+00 4.2000000e+00 2.4000000e+00 2.9000000e+00 4.0000000e+00 3.6000000e+00 7.2000000e+00 6.5000000e+00 2.5000000e+00 4.9000000e+00 2.1000000e+00 6.0000000e+00 2.5000000e+00 4.6000000e+00 5.0000000e+00 2.4000000e+00 2.6000000e+00 3.7000000e+00 4.4000000e+00 5.0000000e+00 6.9000000e+00 3.8000000e+00 2.5000000e+00 2.7000000e+00 5.9000000e+00 4.5000000e+00 3.6000000e+00 2.4000000e+00 4.3000000e+00 4.6000000e+00 4.2000000e+00 2.3000000e+00 5.0000000e+00 5.0000000e+00 4.0000000e+00 2.9000000e+00 3.5000000e+00 4.1000000e+00 2.6000000e+00 3.1000000e+00 1.7000000e+00 3.6000000e+00 1.9000000e+00 4.1000000e+00 3.1000000e+00 2.1000000e+00 2.9000000e+00 1.6000000e+00 4.2000000e+00 2.7000000e+00 3.7000000e+00 3.3000000e+00 3.4000000e+00 3.9000000e+00 4.3000000e+00 4.9000000e+00 3.4000000e+00 1.3000000e+00 1.3000000e+00 1.1000000e+00 2.1000000e+00 3.9000000e+00 2.9000000e+00 4.0000000e+00 4.5000000e+00 2.8000000e+00 2.5000000e+00 1.8000000e+00 2.2000000e+00 3.6000000e+00 2.1000000e+00 5.0000000e-01 2.3000000e+00 2.6000000e+00 2.6000000e+00 3.2000000e+00 1.2000000e+00 2.4000000e+00 6.6000000e+00 4.0000000e+00 6.6000000e+00 5.1000000e+00 6.0000000e+00 7.8000000e+00 2.3000000e+00 6.8000000e+00 5.3000000e+00 7.9000000e+00 5.3000000e+00 4.8000000e+00 5.9000000e+00 3.7000000e+00 4.6000000e+00 5.7000000e+00 5.3000000e+00 8.9000000e+00 8.0000000e+00 3.2000000e+00 6.6000000e+00 3.8000000e+00 7.7000000e+00 4.2000000e+00 6.3000000e+00 6.7000000e+00 4.1000000e+00 4.3000000e+00 5.4000000e+00 6.1000000e+00 6.7000000e+00 8.6000000e+00 5.5000000e+00 4.2000000e+00 4.2000000e+00 7.6000000e+00 6.2000000e+00 5.3000000e+00 4.1000000e+00 6.0000000e+00 6.3000000e+00 5.9000000e+00 4.0000000e+00 6.7000000e+00 6.7000000e+00 5.7000000e+00 4.2000000e+00 5.2000000e+00 5.8000000e+00 4.3000000e+00 1.6000000e+00 9.0000000e-01 1.2000000e+00 1.2000000e+00 6.0000000e-01 1.0000000e+00 1.4000000e+00 1.5000000e+00 1.1000000e+00 8.0000000e-01 1.6000000e+00 1.2000000e+00 9.0000000e-01 1.0000000e+00 1.8000000e+00 1.8000000e+00 5.0000000e-01 1.8000000e+00 1.8000000e+00 2.0000000e+00 1.0000000e+00 1.4000000e+00 8.0000000e-01 9.0000000e-01 1.4000000e+00 1.5000000e+00 6.0000000e-01 1.3000000e+00 1.3000000e+00 7.0000000e-01 1.0000000e+00 3.0000000e+00 8.0000000e-01 5.0000000e-01 5.0000000e-01 7.0000000e-01 2.9000000e+00 7.0000000e-01 3.5000000e+00 1.7000000e+00 3.5000000e+00 2.2000000e+00 2.9000000e+00 4.7000000e+00 2.0000000e+00 3.9000000e+00 3.2000000e+00 4.8000000e+00 2.2000000e+00 2.3000000e+00 2.8000000e+00 2.0000000e+00 2.1000000e+00 2.6000000e+00 2.2000000e+00 5.8000000e+00 5.7000000e+00 1.7000000e+00 3.5000000e+00 1.7000000e+00 5.0000000e+00 1.7000000e+00 3.2000000e+00 3.6000000e+00 1.4000000e+00 1.2000000e+00 2.7000000e+00 3.0000000e+00 4.0000000e+00 5.5000000e+00 2.8000000e+00 1.5000000e+00 2.1000000e+00 4.5000000e+00 3.1000000e+00 2.2000000e+00 1.0000000e+00 2.9000000e+00 3.2000000e+00 2.8000000e+00 1.7000000e+00 3.6000000e+00 3.6000000e+00 2.6000000e+00 2.1000000e+00 2.1000000e+00 2.7000000e+00 1.2000000e+00 1.9000000e+00 1.8000000e+00 2.4000000e+00 2.2000000e+00 8.0000000e-01 1.2000000e+00 9.0000000e-01 2.7000000e+00 1.0000000e+00 2.0000000e+00 1.6000000e+00 1.7000000e+00 2.2000000e+00 2.6000000e+00 3.2000000e+00 1.7000000e+00 1.2000000e+00 1.0000000e+00 1.0000000e+00 1.0000000e+00 2.2000000e+00 2.4000000e+00 2.3000000e+00 2.8000000e+00 1.1000000e+00 1.6000000e+00 1.1000000e+00 1.5000000e+00 1.9000000e+00 8.0000000e-01 1.8000000e+00 1.4000000e+00 1.5000000e+00 1.5000000e+00 1.5000000e+00 2.3000000e+00 1.3000000e+00 4.9000000e+00 2.7000000e+00 4.9000000e+00 3.4000000e+00 4.3000000e+00 6.1000000e+00 2.6000000e+00 5.1000000e+00 3.6000000e+00 6.2000000e+00 3.6000000e+00 3.1000000e+00 4.2000000e+00 2.6000000e+00 3.3000000e+00 4.0000000e+00 3.6000000e+00 7.2000000e+00 6.3000000e+00 1.5000000e+00 4.9000000e+00 2.9000000e+00 6.0000000e+00 2.5000000e+00 4.6000000e+00 5.0000000e+00 2.4000000e+00 2.6000000e+00 3.7000000e+00 4.4000000e+00 5.0000000e+00 6.9000000e+00 3.8000000e+00 2.5000000e+00 2.5000000e+00 5.9000000e+00 4.5000000e+00 3.6000000e+00 2.4000000e+00 4.3000000e+00 4.6000000e+00 4.2000000e+00 2.7000000e+00 5.0000000e+00 5.0000000e+00 4.0000000e+00 2.5000000e+00 3.5000000e+00 4.1000000e+00 2.8000000e+00 1.7000000e+00 1.1000000e+00 9.0000000e-01 1.5000000e+00 1.1000000e+00 2.0000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 3.0000000e-01 8.0000000e-01 9.0000000e-01 9.0000000e-01 1.3000000e+00 4.0000000e-01 2.3000000e+00 2.3000000e+00 2.5000000e+00 1.5000000e+00 9.0000000e-01 1.1000000e+00 1.0000000e+00 9.0000000e-01 1.2000000e+00 1.3000000e+00 1.8000000e+00 1.4000000e+00 2.0000000e-01 1.5000000e+00 3.5000000e+00 1.3000000e+00 1.2000000e+00 1.0000000e+00 6.0000000e-01 3.4000000e+00 1.2000000e+00 3.0000000e+00 1.4000000e+00 3.0000000e+00 1.5000000e+00 2.4000000e+00 4.2000000e+00 2.1000000e+00 3.2000000e+00 2.5000000e+00 4.3000000e+00 1.7000000e+00 1.6000000e+00 2.3000000e+00 1.7000000e+00 1.8000000e+00 2.1000000e+00 1.7000000e+00 5.3000000e+00 5.0000000e+00 1.2000000e+00 3.0000000e+00 1.4000000e+00 4.3000000e+00 1.0000000e+00 2.7000000e+00 3.1000000e+00 7.0000000e-01 7.0000000e-01 2.0000000e+00 2.5000000e+00 3.3000000e+00 5.0000000e+00 2.1000000e+00 8.0000000e-01 1.2000000e+00 4.0000000e+00 2.6000000e+00 1.7000000e+00 7.0000000e-01 2.4000000e+00 2.7000000e+00 2.3000000e+00 1.4000000e+00 3.1000000e+00 3.1000000e+00 2.1000000e+00 1.4000000e+00 1.6000000e+00 2.2000000e+00 1.1000000e+00 2.2000000e+00 1.2000000e+00 1.2000000e+00 2.4000000e+00 9.0000000e-01 2.3000000e+00 1.0000000e+00 2.6000000e+00 1.8000000e+00 1.5000000e+00 2.0000000e+00 2.6000000e+00 3.0000000e+00 1.5000000e+00 8.0000000e-01 1.0000000e+00 1.0000000e+00 8.0000000e-01 2.4000000e+00 1.4000000e+00 2.1000000e+00 2.6000000e+00 2.1000000e+00 6.0000000e-01 9.0000000e-01 1.3000000e+00 1.7000000e+00 1.0000000e+00 1.8000000e+00 8.0000000e-01 9.0000000e-01 7.0000000e-01 1.3000000e+00 1.7000000e+00 7.0000000e-01 4.7000000e+00 2.5000000e+00 4.7000000e+00 3.2000000e+00 4.1000000e+00 5.9000000e+00 2.4000000e+00 4.9000000e+00 4.2000000e+00 6.0000000e+00 3.4000000e+00 3.3000000e+00 4.0000000e+00 2.6000000e+00 2.9000000e+00 3.8000000e+00 3.4000000e+00 7.0000000e+00 6.7000000e+00 2.7000000e+00 4.7000000e+00 2.1000000e+00 6.0000000e+00 2.7000000e+00 4.4000000e+00 4.8000000e+00 2.4000000e+00 2.4000000e+00 3.7000000e+00 4.2000000e+00 5.0000000e+00 6.7000000e+00 3.8000000e+00 2.5000000e+00 2.9000000e+00 5.7000000e+00 4.3000000e+00 3.4000000e+00 2.2000000e+00 4.1000000e+00 4.4000000e+00 4.0000000e+00 2.5000000e+00 4.8000000e+00 4.8000000e+00 3.8000000e+00 3.1000000e+00 3.3000000e+00 3.9000000e+00 2.4000000e+00 1.4000000e+00 2.0000000e+00 1.6000000e+00 2.5000000e+00 1.7000000e+00 1.4000000e+00 1.6000000e+00 1.4000000e+00 7.0000000e-01 2.0000000e-01 8.0000000e-01 1.0000000e+00 1.1000000e+00 2.8000000e+00 2.8000000e+00 3.0000000e+00 2.0000000e+00 2.0000000e+00 1.6000000e+00 1.3000000e+00 4.0000000e-01 1.3000000e+00 1.6000000e+00 2.3000000e+00 1.9000000e+00 9.0000000e-01 2.0000000e+00 4.0000000e+00 1.8000000e+00 1.5000000e+00 1.5000000e+00 9.0000000e-01 3.9000000e+00 1.7000000e+00 3.3000000e+00 2.5000000e+00 2.7000000e+00 2.2000000e+00 2.5000000e+00 3.9000000e+00 2.8000000e+00 3.1000000e+00 2.4000000e+00 3.8000000e+00 1.6000000e+00 2.1000000e+00 2.0000000e+00 2.8000000e+00 2.9000000e+00 2.2000000e+00 1.8000000e+00 4.8000000e+00 4.9000000e+00 2.3000000e+00 2.5000000e+00 2.5000000e+00 4.2000000e+00 1.7000000e+00 2.2000000e+00 2.6000000e+00 1.6000000e+00 1.6000000e+00 2.5000000e+00 2.2000000e+00 3.2000000e+00 4.5000000e+00 2.6000000e+00 1.5000000e+00 2.3000000e+00 3.7000000e+00 2.9000000e+00 1.8000000e+00 1.6000000e+00 1.9000000e+00 2.2000000e+00 1.8000000e+00 2.5000000e+00 2.6000000e+00 2.6000000e+00 1.8000000e+00 2.1000000e+00 1.7000000e+00 2.7000000e+00 2.0000000e+00 1.4000000e+00 1.4000000e+00 1.5000000e+00 1.1000000e+00 1.4000000e+00 1.6000000e+00 1.2000000e+00 1.3000000e+00 1.2000000e+00 1.8000000e+00 1.8000000e+00 5.0000000e-01 2.0000000e+00 1.8000000e+00 2.0000000e+00 1.4000000e+00 1.4000000e+00 2.0000000e-01 9.0000000e-01 1.4000000e+00 1.7000000e+00 6.0000000e-01 1.3000000e+00 9.0000000e-01 7.0000000e-01 1.4000000e+00 3.0000000e+00 8.0000000e-01 7.0000000e-01 7.0000000e-01 1.1000000e+00 2.9000000e+00 9.0000000e-01 3.5000000e+00 1.5000000e+00 3.5000000e+00 2.2000000e+00 2.9000000e+00 4.7000000e+00 1.4000000e+00 3.9000000e+00 3.2000000e+00 4.8000000e+00 2.2000000e+00 2.3000000e+00 2.8000000e+00 1.6000000e+00 1.9000000e+00 2.6000000e+00 2.2000000e+00 5.8000000e+00 5.7000000e+00 1.7000000e+00 3.5000000e+00 1.1000000e+00 5.0000000e+00 1.7000000e+00 3.2000000e+00 3.6000000e+00 1.4000000e+00 1.2000000e+00 2.7000000e+00 3.0000000e+00 4.0000000e+00 5.5000000e+00 2.8000000e+00 1.5000000e+00 2.1000000e+00 4.5000000e+00 3.1000000e+00 2.2000000e+00 1.0000000e+00 2.9000000e+00 3.2000000e+00 2.8000000e+00 1.5000000e+00 3.6000000e+00 3.6000000e+00 2.6000000e+00 2.1000000e+00 2.1000000e+00 2.7000000e+00 1.2000000e+00 1.8000000e+00 7.0000000e-01 2.1000000e+00 8.0000000e-01 2.0000000e+00 1.2000000e+00 1.3000000e+00 1.8000000e+00 2.2000000e+00 2.8000000e+00 1.3000000e+00 8.0000000e-01 1.0000000e+00 1.0000000e+00 4.0000000e-01 1.8000000e+00 1.6000000e+00 1.9000000e+00 2.4000000e+00 1.5000000e+00 8.0000000e-01 9.0000000e-01 9.0000000e-01 1.5000000e+00 4.0000000e-01 2.0000000e+00 6.0000000e-01 7.0000000e-01 7.0000000e-01 1.1000000e+00 2.1000000e+00 5.0000000e-01 4.5000000e+00 1.9000000e+00 4.5000000e+00 3.0000000e+00 3.9000000e+00 5.7000000e+00 2.2000000e+00 4.7000000e+00 3.6000000e+00 5.8000000e+00 3.2000000e+00 2.7000000e+00 3.8000000e+00 2.2000000e+00 2.5000000e+00 3.6000000e+00 3.2000000e+00 6.8000000e+00 6.1000000e+00 2.1000000e+00 4.5000000e+00 2.1000000e+00 5.6000000e+00 2.1000000e+00 4.2000000e+00 4.6000000e+00 2.0000000e+00 2.2000000e+00 3.3000000e+00 4.0000000e+00 4.6000000e+00 6.5000000e+00 3.4000000e+00 2.1000000e+00 2.3000000e+00 5.5000000e+00 4.1000000e+00 3.2000000e+00 2.0000000e+00 3.9000000e+00 4.2000000e+00 3.8000000e+00 1.9000000e+00 4.6000000e+00 4.6000000e+00 3.6000000e+00 2.5000000e+00 3.1000000e+00 3.7000000e+00 2.2000000e+00 1.9000000e+00 1.9000000e+00 1.4000000e+00 8.0000000e-01 1.2000000e+00 1.3000000e+00 1.4000000e+00 1.6000000e+00 2.0000000e+00 9.0000000e-01 2.4000000e+00 2.0000000e+00 2.2000000e+00 1.8000000e+00 1.4000000e+00 1.6000000e+00 1.5000000e+00 1.6000000e+00 5.0000000e-01 2.0000000e+00 1.7000000e+00 1.5000000e+00 1.1000000e+00 1.6000000e+00 3.0000000e+00 1.6000000e+00 1.9000000e+00 1.7000000e+00 1.1000000e+00 3.3000000e+00 1.7000000e+00 3.7000000e+00 1.9000000e+00 3.7000000e+00 2.2000000e+00 3.1000000e+00 4.9000000e+00 1.8000000e+00 3.9000000e+00 2.4000000e+00 5.0000000e+00 2.4000000e+00 1.9000000e+00 3.0000000e+00 1.8000000e+00 2.5000000e+00 2.8000000e+00 2.4000000e+00 6.0000000e+00 5.1000000e+00 7.0000000e-01 3.7000000e+00 2.1000000e+00 4.8000000e+00 1.3000000e+00 3.4000000e+00 3.8000000e+00 1.2000000e+00 1.6000000e+00 2.5000000e+00 3.2000000e+00 3.8000000e+00 5.7000000e+00 2.6000000e+00 1.3000000e+00 1.7000000e+00 4.7000000e+00 3.3000000e+00 2.4000000e+00 1.6000000e+00 3.1000000e+00 3.4000000e+00 3.0000000e+00 1.9000000e+00 3.8000000e+00 3.8000000e+00 2.8000000e+00 1.3000000e+00 2.3000000e+00 2.9000000e+00 2.0000000e+00 2.6000000e+00 1.1000000e+00 2.1000000e+00 1.7000000e+00 1.8000000e+00 2.3000000e+00 2.7000000e+00 3.3000000e+00 1.8000000e+00 7.0000000e-01 3.0000000e-01 5.0000000e-01 5.0000000e-01 2.3000000e+00 1.7000000e+00 2.4000000e+00 2.9000000e+00 1.6000000e+00 9.0000000e-01 4.0000000e-01 8.0000000e-01 2.0000000e+00 5.0000000e-01 1.5000000e+00 7.0000000e-01 1.0000000e+00 1.0000000e+00 1.6000000e+00 1.4000000e+00 8.0000000e-01 5.0000000e+00 2.4000000e+00 5.0000000e+00 3.5000000e+00 4.4000000e+00 6.2000000e+00 1.9000000e+00 5.2000000e+00 3.7000000e+00 6.3000000e+00 3.7000000e+00 3.2000000e+00 4.3000000e+00 2.1000000e+00 3.0000000e+00 4.1000000e+00 3.7000000e+00 7.3000000e+00 6.4000000e+00 2.2000000e+00 5.0000000e+00 2.2000000e+00 6.1000000e+00 2.6000000e+00 4.7000000e+00 5.1000000e+00 2.5000000e+00 2.7000000e+00 3.8000000e+00 4.5000000e+00 5.1000000e+00 7.0000000e+00 3.9000000e+00 2.6000000e+00 2.6000000e+00 6.0000000e+00 4.6000000e+00 3.7000000e+00 2.5000000e+00 4.4000000e+00 4.7000000e+00 4.3000000e+00 2.4000000e+00 5.1000000e+00 5.1000000e+00 4.1000000e+00 2.6000000e+00 3.6000000e+00 4.2000000e+00 2.7000000e+00 1.9000000e+00 1.5000000e+00 1.3000000e+00 1.8000000e+00 1.7000000e+00 1.7000000e+00 1.3000000e+00 1.0000000e+00 2.9000000e+00 2.9000000e+00 3.1000000e+00 2.1000000e+00 1.1000000e+00 1.3000000e+00 8.0000000e-01 1.3000000e+00 2.2000000e+00 1.7000000e+00 2.4000000e+00 2.0000000e+00 1.0000000e+00 2.1000000e+00 4.1000000e+00 1.9000000e+00 1.6000000e+00 1.6000000e+00 1.6000000e+00 4.0000000e+00 1.8000000e+00 2.4000000e+00 1.0000000e+00 2.8000000e+00 1.5000000e+00 2.2000000e+00 4.0000000e+00 2.1000000e+00 3.2000000e+00 2.5000000e+00 3.7000000e+00 1.1000000e+00 1.6000000e+00 2.1000000e+00 1.3000000e+00 1.4000000e+00 1.5000000e+00 1.5000000e+00 4.7000000e+00 5.0000000e+00 1.6000000e+00 2.4000000e+00 1.0000000e+00 4.3000000e+00 1.0000000e+00 2.1000000e+00 2.5000000e+00 7.0000000e-01 5.0000000e-01 2.0000000e+00 2.7000000e+00 3.3000000e+00 4.4000000e+00 2.1000000e+00 1.4000000e+00 2.0000000e+00 3.8000000e+00 2.0000000e+00 1.3000000e+00 3.0000000e-01 2.0000000e+00 2.3000000e+00 1.9000000e+00 1.0000000e+00 2.5000000e+00 2.5000000e+00 1.9000000e+00 1.4000000e+00 1.4000000e+00 1.6000000e+00 5.0000000e-01 1.6000000e+00 8.0000000e-01 7.0000000e-01 1.2000000e+00 1.6000000e+00 2.2000000e+00 9.0000000e-01 1.4000000e+00 1.4000000e+00 1.6000000e+00 6.0000000e-01 1.6000000e+00 1.6000000e+00 1.5000000e+00 1.8000000e+00 1.1000000e+00 8.0000000e-01 9.0000000e-01 1.3000000e+00 9.0000000e-01 6.0000000e-01 2.6000000e+00 8.0000000e-01 9.0000000e-01 7.0000000e-01 5.0000000e-01 2.5000000e+00 5.0000000e-01 3.9000000e+00 2.1000000e+00 3.9000000e+00 2.4000000e+00 3.3000000e+00 5.1000000e+00 2.4000000e+00 4.1000000e+00 3.2000000e+00 5.2000000e+00 2.6000000e+00 2.3000000e+00 3.2000000e+00 2.4000000e+00 2.5000000e+00 3.0000000e+00 2.6000000e+00 6.2000000e+00 5.7000000e+00 1.9000000e+00 3.9000000e+00 2.1000000e+00 5.0000000e+00 1.7000000e+00 3.6000000e+00 4.0000000e+00 1.4000000e+00 1.6000000e+00 2.7000000e+00 3.4000000e+00 4.0000000e+00 5.9000000e+00 2.8000000e+00 1.5000000e+00 1.9000000e+00 4.9000000e+00 3.5000000e+00 2.6000000e+00 1.6000000e+00 3.3000000e+00 3.6000000e+00 3.2000000e+00 2.1000000e+00 4.0000000e+00 4.0000000e+00 3.0000000e+00 2.1000000e+00 2.5000000e+00 3.1000000e+00 2.0000000e+00 1.0000000e+00 1.3000000e+00 1.4000000e+00 1.0000000e+00 1.2000000e+00 1.1000000e+00 2.6000000e+00 2.4000000e+00 2.6000000e+00 2.0000000e+00 8.0000000e-01 1.8000000e+00 1.7000000e+00 1.2000000e+00 9.0000000e-01 2.2000000e+00 1.9000000e+00 1.7000000e+00 1.1000000e+00 1.8000000e+00 3.6000000e+00 1.8000000e+00 2.1000000e+00 1.9000000e+00 1.3000000e+00 3.5000000e+00 1.9000000e+00 2.9000000e+00 1.3000000e+00 2.9000000e+00 1.4000000e+00 2.3000000e+00 4.1000000e+00 2.0000000e+00 3.1000000e+00 1.6000000e+00 4.2000000e+00 1.6000000e+00 1.1000000e+00 2.2000000e+00 1.2000000e+00 1.9000000e+00 2.0000000e+00 1.6000000e+00 5.2000000e+00 4.3000000e+00 7.0000000e-01 2.9000000e+00 1.5000000e+00 4.0000000e+00 5.0000000e-01 2.6000000e+00 3.0000000e+00 8.0000000e-01 1.0000000e+00 1.7000000e+00 2.4000000e+00 3.0000000e+00 4.9000000e+00 1.8000000e+00 5.0000000e-01 1.1000000e+00 3.9000000e+00 2.5000000e+00 1.6000000e+00 1.2000000e+00 2.3000000e+00 2.6000000e+00 2.2000000e+00 1.3000000e+00 3.0000000e+00 3.0000000e+00 2.0000000e+00 5.0000000e-01 1.5000000e+00 2.3000000e+00 1.4000000e+00 9.0000000e-01 1.2000000e+00 1.0000000e+00 1.6000000e+00 7.0000000e-01 2.0000000e+00 2.0000000e+00 2.2000000e+00 1.2000000e+00 1.0000000e+00 1.4000000e+00 1.3000000e+00 1.2000000e+00 1.1000000e+00 1.4000000e+00 1.7000000e+00 1.1000000e+00 5.0000000e-01 1.2000000e+00 3.2000000e+00 1.2000000e+00 1.1000000e+00 1.1000000e+00 7.0000000e-01 3.1000000e+00 1.1000000e+00 3.3000000e+00 1.5000000e+00 3.3000000e+00 1.8000000e+00 2.7000000e+00 4.5000000e+00 2.2000000e+00 3.5000000e+00 2.6000000e+00 4.6000000e+00 2.0000000e+00 1.7000000e+00 2.6000000e+00 1.8000000e+00 1.9000000e+00 2.4000000e+00 2.0000000e+00 5.6000000e+00 5.1000000e+00 1.3000000e+00 3.3000000e+00 1.5000000e+00 4.4000000e+00 1.1000000e+00 3.0000000e+00 3.4000000e+00 8.0000000e-01 1.0000000e+00 2.1000000e+00 2.8000000e+00 3.4000000e+00 5.3000000e+00 2.2000000e+00 9.0000000e-01 1.3000000e+00 4.3000000e+00 2.9000000e+00 2.0000000e+00 1.0000000e+00 2.7000000e+00 3.0000000e+00 2.6000000e+00 1.5000000e+00 3.4000000e+00 3.4000000e+00 2.4000000e+00 1.5000000e+00 1.9000000e+00 2.5000000e+00 1.4000000e+00 5.0000000e-01 1.1000000e+00 1.5000000e+00 8.0000000e-01 2.1000000e+00 2.1000000e+00 2.3000000e+00 1.3000000e+00 1.7000000e+00 1.5000000e+00 1.4000000e+00 1.1000000e+00 8.0000000e-01 1.1000000e+00 1.6000000e+00 1.4000000e+00 8.0000000e-01 1.3000000e+00 3.3000000e+00 1.1000000e+00 1.0000000e+00 8.0000000e-01 2.0000000e-01 3.2000000e+00 1.0000000e+00 3.4000000e+00 2.2000000e+00 3.2000000e+00 1.9000000e+00 2.6000000e+00 4.4000000e+00 2.5000000e+00 3.4000000e+00 2.7000000e+00 4.5000000e+00 1.9000000e+00 1.8000000e+00 2.5000000e+00 2.5000000e+00 2.6000000e+00 2.3000000e+00 1.9000000e+00 5.5000000e+00 5.2000000e+00 2.0000000e+00 3.2000000e+00 2.2000000e+00 4.5000000e+00 1.4000000e+00 2.9000000e+00 3.3000000e+00 1.3000000e+00 1.5000000e+00 2.2000000e+00 2.7000000e+00 3.5000000e+00 5.2000000e+00 2.3000000e+00 1.2000000e+00 2.0000000e+00 4.2000000e+00 3.0000000e+00 1.9000000e+00 1.5000000e+00 2.6000000e+00 2.9000000e+00 2.5000000e+00 2.2000000e+00 3.3000000e+00 3.3000000e+00 2.3000000e+00 1.8000000e+00 1.8000000e+00 2.8000000e+00 1.9000000e+00 8.0000000e-01 1.0000000e+00 9.0000000e-01 2.6000000e+00 2.6000000e+00 2.8000000e+00 1.8000000e+00 1.8000000e+00 1.4000000e+00 1.3000000e+00 6.0000000e-01 1.1000000e+00 1.4000000e+00 2.1000000e+00 1.7000000e+00 7.0000000e-01 1.8000000e+00 3.8000000e+00 1.6000000e+00 1.3000000e+00 1.3000000e+00 7.0000000e-01 3.7000000e+00 1.5000000e+00 3.3000000e+00 2.3000000e+00 2.7000000e+00 2.0000000e+00 2.3000000e+00 3.9000000e+00 2.6000000e+00 3.1000000e+00 2.4000000e+00 4.0000000e+00 1.6000000e+00 1.9000000e+00 2.0000000e+00 2.6000000e+00 2.7000000e+00 2.2000000e+00 1.6000000e+00 5.0000000e+00 4.9000000e+00 2.1000000e+00 2.7000000e+00 2.3000000e+00 4.2000000e+00 1.5000000e+00 2.4000000e+00 2.8000000e+00 1.4000000e+00 1.4000000e+00 2.3000000e+00 2.2000000e+00 3.2000000e+00 4.7000000e+00 2.4000000e+00 1.3000000e+00 2.1000000e+00 3.7000000e+00 2.9000000e+00 1.8000000e+00 1.4000000e+00 2.1000000e+00 2.4000000e+00 2.0000000e+00 2.3000000e+00 2.8000000e+00 2.8000000e+00 1.8000000e+00 1.9000000e+00 1.5000000e+00 2.7000000e+00 1.8000000e+00 8.0000000e-01 1.3000000e+00 3.0000000e+00 3.0000000e+00 3.2000000e+00 2.2000000e+00 1.4000000e+00 2.0000000e+00 1.9000000e+00 6.0000000e-01 1.5000000e+00 2.2000000e+00 2.5000000e+00 2.1000000e+00 1.1000000e+00 2.2000000e+00 4.2000000e+00 2.0000000e+00 2.1000000e+00 1.9000000e+00 1.3000000e+00 4.1000000e+00 1.9000000e+00 3.3000000e+00 1.9000000e+00 2.3000000e+00 1.8000000e+00 2.3000000e+00 3.5000000e+00 2.8000000e+00 2.5000000e+00 1.8000000e+00 3.6000000e+00 1.6000000e+00 1.5000000e+00 1.6000000e+00 2.2000000e+00 2.3000000e+00 2.2000000e+00 1.6000000e+00 4.6000000e+00 4.1000000e+00 1.7000000e+00 2.3000000e+00 1.9000000e+00 3.4000000e+00 1.1000000e+00 2.2000000e+00 2.4000000e+00 1.0000000e+00 1.4000000e+00 1.9000000e+00 1.8000000e+00 2.4000000e+00 4.3000000e+00 2.0000000e+00 9.0000000e-01 1.7000000e+00 3.3000000e+00 2.9000000e+00 1.8000000e+00 1.4000000e+00 1.7000000e+00 2.2000000e+00 1.6000000e+00 1.9000000e+00 2.4000000e+00 2.6000000e+00 1.6000000e+00 1.5000000e+00 1.5000000e+00 2.7000000e+00 1.8000000e+00 1.5000000e+00 3.6000000e+00 3.6000000e+00 3.8000000e+00 2.8000000e+00 1.2000000e+00 2.0000000e+00 1.7000000e+00 6.0000000e-01 2.1000000e+00 2.4000000e+00 3.1000000e+00 2.7000000e+00 1.3000000e+00 2.8000000e+00 4.8000000e+00 2.6000000e+00 2.3000000e+00 2.3000000e+00 1.7000000e+00 4.7000000e+00 2.5000000e+00 2.5000000e+00 1.5000000e+00 1.7000000e+00 1.2000000e+00 1.5000000e+00 2.9000000e+00 2.8000000e+00 2.1000000e+00 1.4000000e+00 3.0000000e+00 8.0000000e-01 1.1000000e+00 1.0000000e+00 1.8000000e+00 1.9000000e+00 1.4000000e+00 8.0000000e-01 4.0000000e+00 3.9000000e+00 1.7000000e+00 1.7000000e+00 1.7000000e+00 3.2000000e+00 9.0000000e-01 1.4000000e+00 1.8000000e+00 1.0000000e+00 8.0000000e-01 1.5000000e+00 1.4000000e+00 2.2000000e+00 3.7000000e+00 1.6000000e+00 9.0000000e-01 1.9000000e+00 2.7000000e+00 2.1000000e+00 1.0000000e+00 1.0000000e+00 1.1000000e+00 1.4000000e+00 1.0000000e+00 1.5000000e+00 1.8000000e+00 1.8000000e+00 8.0000000e-01 1.1000000e+00 7.0000000e-01 1.9000000e+00 1.0000000e+00 2.1000000e+00 2.1000000e+00 2.3000000e+00 1.3000000e+00 9.0000000e-01 7.0000000e-01 6.0000000e-01 1.1000000e+00 1.2000000e+00 1.1000000e+00 1.6000000e+00 1.2000000e+00 4.0000000e-01 1.3000000e+00 3.3000000e+00 1.1000000e+00 1.0000000e+00 8.0000000e-01 6.0000000e-01 3.2000000e+00 1.0000000e+00 3.2000000e+00 1.4000000e+00 3.2000000e+00 1.7000000e+00 2.6000000e+00 4.4000000e+00 1.7000000e+00 3.4000000e+00 2.7000000e+00 4.5000000e+00 1.9000000e+00 1.8000000e+00 2.5000000e+00 1.7000000e+00 1.8000000e+00 2.3000000e+00 1.9000000e+00 5.5000000e+00 5.2000000e+00 1.2000000e+00 3.2000000e+00 1.4000000e+00 4.5000000e+00 1.2000000e+00 2.9000000e+00 3.3000000e+00 9.0000000e-01 9.0000000e-01 2.2000000e+00 2.7000000e+00 3.5000000e+00 5.2000000e+00 2.3000000e+00 1.0000000e+00 1.6000000e+00 4.2000000e+00 2.8000000e+00 1.9000000e+00 7.0000000e-01 2.6000000e+00 2.9000000e+00 2.5000000e+00 1.4000000e+00 3.3000000e+00 3.3000000e+00 2.3000000e+00 1.6000000e+00 1.8000000e+00 2.4000000e+00 1.1000000e+00 8.0000000e-01 6.0000000e-01 8.0000000e-01 2.6000000e+00 2.2000000e+00 2.7000000e+00 3.2000000e+00 2.1000000e+00 1.4000000e+00 1.1000000e+00 1.3000000e+00 2.3000000e+00 8.0000000e-01 1.2000000e+00 1.2000000e+00 1.3000000e+00 1.3000000e+00 1.9000000e+00 1.3000000e+00 1.1000000e+00 5.3000000e+00 2.7000000e+00 5.3000000e+00 3.8000000e+00 4.7000000e+00 6.5000000e+00 2.6000000e+00 5.5000000e+00 4.2000000e+00 6.6000000e+00 4.0000000e+00 3.5000000e+00 4.6000000e+00 2.6000000e+00 3.3000000e+00 4.4000000e+00 4.0000000e+00 7.6000000e+00 6.7000000e+00 2.7000000e+00 5.3000000e+00 2.7000000e+00 6.4000000e+00 2.9000000e+00 5.0000000e+00 5.4000000e+00 2.8000000e+00 3.0000000e+00 4.1000000e+00 4.8000000e+00 5.4000000e+00 7.3000000e+00 4.2000000e+00 2.9000000e+00 2.9000000e+00 6.3000000e+00 4.9000000e+00 4.0000000e+00 2.8000000e+00 4.7000000e+00 5.0000000e+00 4.6000000e+00 2.7000000e+00 5.4000000e+00 5.4000000e+00 4.4000000e+00 3.1000000e+00 3.9000000e+00 4.5000000e+00 3.0000000e+00 2.0000000e-01 8.0000000e-01 2.6000000e+00 1.8000000e+00 2.7000000e+00 3.2000000e+00 1.7000000e+00 1.2000000e+00 5.0000000e-01 9.0000000e-01 2.3000000e+00 8.0000000e-01 1.2000000e+00 1.0000000e+00 1.3000000e+00 1.3000000e+00 1.9000000e+00 1.3000000e+00 1.1000000e+00 5.3000000e+00 2.7000000e+00 5.3000000e+00 3.8000000e+00 4.7000000e+00 6.5000000e+00 2.0000000e+00 5.5000000e+00 4.0000000e+00 6.6000000e+00 4.0000000e+00 3.5000000e+00 4.6000000e+00 2.4000000e+00 3.3000000e+00 4.4000000e+00 4.0000000e+00 7.6000000e+00 6.7000000e+00 2.3000000e+00 5.3000000e+00 2.5000000e+00 6.4000000e+00 2.9000000e+00 5.0000000e+00 5.4000000e+00 2.8000000e+00 3.0000000e+00 4.1000000e+00 4.8000000e+00 5.4000000e+00 7.3000000e+00 4.2000000e+00 2.9000000e+00 2.9000000e+00 6.3000000e+00 4.9000000e+00 4.0000000e+00 2.8000000e+00 4.7000000e+00 5.0000000e+00 4.6000000e+00 2.7000000e+00 5.4000000e+00 5.4000000e+00 4.4000000e+00 2.9000000e+00 3.9000000e+00 4.5000000e+00 3.0000000e+00 1.0000000e+00 2.8000000e+00 2.0000000e+00 2.9000000e+00 3.4000000e+00 1.9000000e+00 1.4000000e+00 7.0000000e-01 1.1000000e+00 2.5000000e+00 1.0000000e+00 1.0000000e+00 1.2000000e+00 1.5000000e+00 1.5000000e+00 2.1000000e+00 1.3000000e+00 1.3000000e+00 5.5000000e+00 2.9000000e+00 5.5000000e+00 4.0000000e+00 4.9000000e+00 6.7000000e+00 2.2000000e+00 5.7000000e+00 4.2000000e+00 6.8000000e+00 4.2000000e+00 3.7000000e+00 4.8000000e+00 2.6000000e+00 3.5000000e+00 4.6000000e+00 4.2000000e+00 7.8000000e+00 6.9000000e+00 2.5000000e+00 5.5000000e+00 2.7000000e+00 6.6000000e+00 3.1000000e+00 5.2000000e+00 5.6000000e+00 3.0000000e+00 3.2000000e+00 4.3000000e+00 5.0000000e+00 5.6000000e+00 7.5000000e+00 4.4000000e+00 3.1000000e+00 3.1000000e+00 6.5000000e+00 5.1000000e+00 4.2000000e+00 3.0000000e+00 4.9000000e+00 5.2000000e+00 4.8000000e+00 2.9000000e+00 5.6000000e+00 5.6000000e+00 4.6000000e+00 3.1000000e+00 4.1000000e+00 4.7000000e+00 3.2000000e+00 1.8000000e+00 1.6000000e+00 1.9000000e+00 2.4000000e+00 1.5000000e+00 8.0000000e-01 7.0000000e-01 9.0000000e-01 1.5000000e+00 2.0000000e-01 2.0000000e+00 6.0000000e-01 7.0000000e-01 7.0000000e-01 1.1000000e+00 1.9000000e+00 5.0000000e-01 4.5000000e+00 1.9000000e+00 4.5000000e+00 3.0000000e+00 3.9000000e+00 5.7000000e+00 2.2000000e+00 4.7000000e+00 3.6000000e+00 5.8000000e+00 3.2000000e+00 2.7000000e+00 3.8000000e+00 2.2000000e+00 2.5000000e+00 3.6000000e+00 3.2000000e+00 6.8000000e+00 6.1000000e+00 2.1000000e+00 4.5000000e+00 2.1000000e+00 5.6000000e+00 2.1000000e+00 4.2000000e+00 4.6000000e+00 2.0000000e+00 2.2000000e+00 3.3000000e+00 4.0000000e+00 4.6000000e+00 6.5000000e+00 3.4000000e+00 2.1000000e+00 2.3000000e+00 5.5000000e+00 4.1000000e+00 3.2000000e+00 2.0000000e+00 3.9000000e+00 4.2000000e+00 3.8000000e+00 1.9000000e+00 4.6000000e+00 4.6000000e+00 3.6000000e+00 2.5000000e+00 3.1000000e+00 3.7000000e+00 2.2000000e+00 1.6000000e+00 1.3000000e+00 1.6000000e+00 1.7000000e+00 2.0000000e+00 2.1000000e+00 1.7000000e+00 1.1000000e+00 1.8000000e+00 3.8000000e+00 1.6000000e+00 1.9000000e+00 1.7000000e+00 1.5000000e+00 3.7000000e+00 1.7000000e+00 2.7000000e+00 5.0000000e-01 2.7000000e+00 1.2000000e+00 2.1000000e+00 3.9000000e+00 2.0000000e+00 2.9000000e+00 1.8000000e+00 4.0000000e+00 1.4000000e+00 9.0000000e-01 2.0000000e+00 1.0000000e+00 1.1000000e+00 1.8000000e+00 1.4000000e+00 5.0000000e+00 4.3000000e+00 7.0000000e-01 2.7000000e+00 1.1000000e+00 3.8000000e+00 7.0000000e-01 2.4000000e+00 2.8000000e+00 8.0000000e-01 8.0000000e-01 1.5000000e+00 2.2000000e+00 2.8000000e+00 4.7000000e+00 1.6000000e+00 5.0000000e-01 9.0000000e-01 3.7000000e+00 2.3000000e+00 1.4000000e+00 8.0000000e-01 2.1000000e+00 2.4000000e+00 2.0000000e+00 5.0000000e-01 2.8000000e+00 2.8000000e+00 1.8000000e+00 9.0000000e-01 1.3000000e+00 1.9000000e+00 6.0000000e-01 1.1000000e+00 1.6000000e+00 1.9000000e+00 8.0000000e-01 1.3000000e+00 9.0000000e-01 9.0000000e-01 1.6000000e+00 2.8000000e+00 1.0000000e+00 9.0000000e-01 9.0000000e-01 1.3000000e+00 2.7000000e+00 1.1000000e+00 3.7000000e+00 1.7000000e+00 3.7000000e+00 2.4000000e+00 3.1000000e+00 4.9000000e+00 1.2000000e+00 4.1000000e+00 3.4000000e+00 5.0000000e+00 2.4000000e+00 2.5000000e+00 3.0000000e+00 1.8000000e+00 2.1000000e+00 2.8000000e+00 2.4000000e+00 6.0000000e+00 5.9000000e+00 1.9000000e+00 3.7000000e+00 1.3000000e+00 5.2000000e+00 1.9000000e+00 3.4000000e+00 3.8000000e+00 1.6000000e+00 1.4000000e+00 2.9000000e+00 3.2000000e+00 4.2000000e+00 5.7000000e+00 3.0000000e+00 1.7000000e+00 2.3000000e+00 4.7000000e+00 3.3000000e+00 2.4000000e+00 1.2000000e+00 3.1000000e+00 3.4000000e+00 3.0000000e+00 1.7000000e+00 3.8000000e+00 3.8000000e+00 2.8000000e+00 2.3000000e+00 2.3000000e+00 2.9000000e+00 1.4000000e+00 1.3000000e+00 1.8000000e+00 1.5000000e+00 2.2000000e+00 1.8000000e+00 8.0000000e-01 1.9000000e+00 3.9000000e+00 1.7000000e+00 1.4000000e+00 1.4000000e+00 1.2000000e+00 3.8000000e+00 1.6000000e+00 2.8000000e+00 1.8000000e+00 3.4000000e+00 2.1000000e+00 2.8000000e+00 4.6000000e+00 2.1000000e+00 3.8000000e+00 3.1000000e+00 3.9000000e+00 1.7000000e+00 2.2000000e+00 2.7000000e+00 2.1000000e+00 2.2000000e+00 2.1000000e+00 2.1000000e+00 4.9000000e+00 5.6000000e+00 1.8000000e+00 3.0000000e+00 1.8000000e+00 4.9000000e+00 1.6000000e+00 2.5000000e+00 3.1000000e+00 1.3000000e+00 1.1000000e+00 2.6000000e+00 2.9000000e+00 3.9000000e+00 4.6000000e+00 2.7000000e+00 1.6000000e+00 2.2000000e+00 4.4000000e+00 2.2000000e+00 1.9000000e+00 9.0000000e-01 2.6000000e+00 2.9000000e+00 2.5000000e+00 1.8000000e+00 3.1000000e+00 2.9000000e+00 2.5000000e+00 2.0000000e+00 2.0000000e+00 1.8000000e+00 1.3000000e+00 1.7000000e+00 2.0000000e+00 2.7000000e+00 2.3000000e+00 9.0000000e-01 2.4000000e+00 4.4000000e+00 2.2000000e+00 1.9000000e+00 1.9000000e+00 1.3000000e+00 4.3000000e+00 2.1000000e+00 2.9000000e+00 2.1000000e+00 2.3000000e+00 1.8000000e+00 2.1000000e+00 3.5000000e+00 2.8000000e+00 2.7000000e+00 2.0000000e+00 3.4000000e+00 1.2000000e+00 1.7000000e+00 1.6000000e+00 2.4000000e+00 2.5000000e+00 1.8000000e+00 1.4000000e+00 4.4000000e+00 4.5000000e+00 1.9000000e+00 2.1000000e+00 2.1000000e+00 3.8000000e+00 1.3000000e+00 1.8000000e+00 2.2000000e+00 1.2000000e+00 1.2000000e+00 2.1000000e+00 1.8000000e+00 2.8000000e+00 4.1000000e+00 2.2000000e+00 1.1000000e+00 2.1000000e+00 3.3000000e+00 2.5000000e+00 1.4000000e+00 1.2000000e+00 1.5000000e+00 1.8000000e+00 1.4000000e+00 2.1000000e+00 2.2000000e+00 2.2000000e+00 1.4000000e+00 1.7000000e+00 1.3000000e+00 2.3000000e+00 1.6000000e+00 1.7000000e+00 1.4000000e+00 1.2000000e+00 1.2000000e+00 1.3000000e+00 2.7000000e+00 1.3000000e+00 1.6000000e+00 1.4000000e+00 8.0000000e-01 3.0000000e+00 1.4000000e+00 3.8000000e+00 2.2000000e+00 3.8000000e+00 2.3000000e+00 3.2000000e+00 5.0000000e+00 2.1000000e+00 4.0000000e+00 2.5000000e+00 5.1000000e+00 2.5000000e+00 2.0000000e+00 3.1000000e+00 2.1000000e+00 2.8000000e+00 2.9000000e+00 2.5000000e+00 6.1000000e+00 5.2000000e+00 1.2000000e+00 3.8000000e+00 2.4000000e+00 4.9000000e+00 1.4000000e+00 3.5000000e+00 3.9000000e+00 1.5000000e+00 1.9000000e+00 2.6000000e+00 3.3000000e+00 3.9000000e+00 5.8000000e+00 2.7000000e+00 1.4000000e+00 1.8000000e+00 4.8000000e+00 3.4000000e+00 2.5000000e+00 1.9000000e+00 3.2000000e+00 3.5000000e+00 3.1000000e+00 2.2000000e+00 3.9000000e+00 3.9000000e+00 2.9000000e+00 1.4000000e+00 2.4000000e+00 3.2000000e+00 2.3000000e+00 7.0000000e-01 9.0000000e-01 1.1000000e+00 8.0000000e-01 2.4000000e+00 4.0000000e-01 3.0000000e-01 3.0000000e-01 9.0000000e-01 2.3000000e+00 3.0000000e-01 4.1000000e+00 2.1000000e+00 4.1000000e+00 2.8000000e+00 3.5000000e+00 5.3000000e+00 2.0000000e+00 4.5000000e+00 3.8000000e+00 5.4000000e+00 2.8000000e+00 2.9000000e+00 3.4000000e+00 2.2000000e+00 2.5000000e+00 3.2000000e+00 2.8000000e+00 6.4000000e+00 6.3000000e+00 2.3000000e+00 4.1000000e+00 1.7000000e+00 5.6000000e+00 2.3000000e+00 3.8000000e+00 4.2000000e+00 2.0000000e+00 1.8000000e+00 3.3000000e+00 3.6000000e+00 4.6000000e+00 6.1000000e+00 3.4000000e+00 2.1000000e+00 2.5000000e+00 5.1000000e+00 3.7000000e+00 2.8000000e+00 1.6000000e+00 3.5000000e+00 3.8000000e+00 3.4000000e+00 2.1000000e+00 4.2000000e+00 4.2000000e+00 3.2000000e+00 2.7000000e+00 2.7000000e+00 3.3000000e+00 1.8000000e+00 6.0000000e-01 1.8000000e+00 5.0000000e-01 1.7000000e+00 5.0000000e-01 1.0000000e+00 8.0000000e-01 1.4000000e+00 1.6000000e+00 6.0000000e-01 4.8000000e+00 2.2000000e+00 4.8000000e+00 3.3000000e+00 4.2000000e+00 6.0000000e+00 1.5000000e+00 5.0000000e+00 3.5000000e+00 6.1000000e+00 3.5000000e+00 3.0000000e+00 4.1000000e+00 1.9000000e+00 2.8000000e+00 3.9000000e+00 3.5000000e+00 7.1000000e+00 6.2000000e+00 2.0000000e+00 4.8000000e+00 2.0000000e+00 5.9000000e+00 2.4000000e+00 4.5000000e+00 4.9000000e+00 2.3000000e+00 2.5000000e+00 3.6000000e+00 4.3000000e+00 4.9000000e+00 6.8000000e+00 3.7000000e+00 2.4000000e+00 2.4000000e+00 5.8000000e+00 4.4000000e+00 3.5000000e+00 2.3000000e+00 4.2000000e+00 4.5000000e+00 4.1000000e+00 2.2000000e+00 4.9000000e+00 4.9000000e+00 3.9000000e+00 2.4000000e+00 3.4000000e+00 4.0000000e+00 2.5000000e+00 1.4000000e+00 7.0000000e-01 2.1000000e+00 5.0000000e-01 8.0000000e-01 8.0000000e-01 1.2000000e+00 2.0000000e+00 8.0000000e-01 4.4000000e+00 1.8000000e+00 4.4000000e+00 2.9000000e+00 3.8000000e+00 5.6000000e+00 1.3000000e+00 4.6000000e+00 3.3000000e+00 5.7000000e+00 3.1000000e+00 2.6000000e+00 3.7000000e+00 1.7000000e+00 2.4000000e+00 3.5000000e+00 3.1000000e+00 6.7000000e+00 5.8000000e+00 1.8000000e+00 4.4000000e+00 1.6000000e+00 5.5000000e+00 2.0000000e+00 4.1000000e+00 4.5000000e+00 1.9000000e+00 2.1000000e+00 3.2000000e+00 3.9000000e+00 4.5000000e+00 6.4000000e+00 3.3000000e+00 2.0000000e+00 2.0000000e+00 5.4000000e+00 4.0000000e+00 3.1000000e+00 1.9000000e+00 3.8000000e+00 4.1000000e+00 3.7000000e+00 1.8000000e+00 4.5000000e+00 4.5000000e+00 3.5000000e+00 2.2000000e+00 3.0000000e+00 3.6000000e+00 2.1000000e+00 1.5000000e+00 3.5000000e+00 1.3000000e+00 1.0000000e+00 1.0000000e+00 6.0000000e-01 3.4000000e+00 1.2000000e+00 3.0000000e+00 1.6000000e+00 3.0000000e+00 1.7000000e+00 2.4000000e+00 4.2000000e+00 2.1000000e+00 3.4000000e+00 2.7000000e+00 4.3000000e+00 1.7000000e+00 1.8000000e+00 2.3000000e+00 1.9000000e+00 2.0000000e+00 2.1000000e+00 1.7000000e+00 5.3000000e+00 5.2000000e+00 1.4000000e+00 3.0000000e+00 1.6000000e+00 4.5000000e+00 1.2000000e+00 2.7000000e+00 3.1000000e+00 9.0000000e-01 7.0000000e-01 2.2000000e+00 2.5000000e+00 3.5000000e+00 5.0000000e+00 2.3000000e+00 1.0000000e+00 1.4000000e+00 4.0000000e+00 2.6000000e+00 1.7000000e+00 7.0000000e-01 2.4000000e+00 2.7000000e+00 2.3000000e+00 1.6000000e+00 3.1000000e+00 3.1000000e+00 2.1000000e+00 1.6000000e+00 1.6000000e+00 2.2000000e+00 1.1000000e+00 2.0000000e+00 6.0000000e-01 7.0000000e-01 7.0000000e-01 1.1000000e+00 1.9000000e+00 5.0000000e-01 4.5000000e+00 1.9000000e+00 4.5000000e+00 3.0000000e+00 3.9000000e+00 5.7000000e+00 2.0000000e+00 4.7000000e+00 3.4000000e+00 5.8000000e+00 3.2000000e+00 2.7000000e+00 3.8000000e+00 2.0000000e+00 2.5000000e+00 3.6000000e+00 3.2000000e+00 6.8000000e+00 5.9000000e+00 1.9000000e+00 4.5000000e+00 2.1000000e+00 5.6000000e+00 2.1000000e+00 4.2000000e+00 4.6000000e+00 2.0000000e+00 2.2000000e+00 3.3000000e+00 4.0000000e+00 4.6000000e+00 6.5000000e+00 3.4000000e+00 2.1000000e+00 2.1000000e+00 5.5000000e+00 4.1000000e+00 3.2000000e+00 2.0000000e+00 3.9000000e+00 4.2000000e+00 3.8000000e+00 1.9000000e+00 4.6000000e+00 4.6000000e+00 3.6000000e+00 2.3000000e+00 3.1000000e+00 3.7000000e+00 2.2000000e+00 2.2000000e+00 2.5000000e+00 2.5000000e+00 3.1000000e+00 7.0000000e-01 2.3000000e+00 6.5000000e+00 3.9000000e+00 6.5000000e+00 5.0000000e+00 5.9000000e+00 7.7000000e+00 2.2000000e+00 6.7000000e+00 5.2000000e+00 7.8000000e+00 5.2000000e+00 4.7000000e+00 5.8000000e+00 3.6000000e+00 4.5000000e+00 5.6000000e+00 5.2000000e+00 8.8000000e+00 7.9000000e+00 3.3000000e+00 6.5000000e+00 3.7000000e+00 7.6000000e+00 4.1000000e+00 6.2000000e+00 6.6000000e+00 4.0000000e+00 4.2000000e+00 5.3000000e+00 6.0000000e+00 6.6000000e+00 8.5000000e+00 5.4000000e+00 4.1000000e+00 4.1000000e+00 7.5000000e+00 6.1000000e+00 5.2000000e+00 4.0000000e+00 5.9000000e+00 6.2000000e+00 5.8000000e+00 3.9000000e+00 6.6000000e+00 6.6000000e+00 5.6000000e+00 4.1000000e+00 5.1000000e+00 5.7000000e+00 4.2000000e+00 5.0000000e-01 3.0000000e-01 9.0000000e-01 2.1000000e+00 3.0000000e-01 4.3000000e+00 1.7000000e+00 4.3000000e+00 2.8000000e+00 3.7000000e+00 5.5000000e+00 1.6000000e+00 4.5000000e+00 3.4000000e+00 5.6000000e+00 3.0000000e+00 2.5000000e+00 3.6000000e+00 1.8000000e+00 2.3000000e+00 3.4000000e+00 3.0000000e+00 6.6000000e+00 5.9000000e+00 1.9000000e+00 4.3000000e+00 1.5000000e+00 5.4000000e+00 1.9000000e+00 4.0000000e+00 4.4000000e+00 1.8000000e+00 2.0000000e+00 3.1000000e+00 3.8000000e+00 4.4000000e+00 6.3000000e+00 3.2000000e+00 1.9000000e+00 2.1000000e+00 5.3000000e+00 3.9000000e+00 3.0000000e+00 1.8000000e+00 3.7000000e+00 4.0000000e+00 3.6000000e+00 1.7000000e+00 4.4000000e+00 4.4000000e+00 3.4000000e+00 2.3000000e+00 2.9000000e+00 3.5000000e+00 2.0000000e+00 2.0000000e-01 8.0000000e-01 2.4000000e+00 4.0000000e-01 4.0000000e+00 2.0000000e+00 4.0000000e+00 2.7000000e+00 3.4000000e+00 5.2000000e+00 2.1000000e+00 4.4000000e+00 3.7000000e+00 5.3000000e+00 2.7000000e+00 2.8000000e+00 3.3000000e+00 2.1000000e+00 2.4000000e+00 3.1000000e+00 2.7000000e+00 6.3000000e+00 6.2000000e+00 2.2000000e+00 4.0000000e+00 1.8000000e+00 5.5000000e+00 2.2000000e+00 3.7000000e+00 4.1000000e+00 1.9000000e+00 1.7000000e+00 3.2000000e+00 3.5000000e+00 4.5000000e+00 6.0000000e+00 3.3000000e+00 2.0000000e+00 2.4000000e+00 5.0000000e+00 3.6000000e+00 2.7000000e+00 1.5000000e+00 3.4000000e+00 3.7000000e+00 3.3000000e+00 2.0000000e+00 4.1000000e+00 4.1000000e+00 3.1000000e+00 2.6000000e+00 2.6000000e+00 3.2000000e+00 1.7000000e+00 6.0000000e-01 2.4000000e+00 2.0000000e-01 4.0000000e+00 1.8000000e+00 4.0000000e+00 2.5000000e+00 3.4000000e+00 5.2000000e+00 1.9000000e+00 4.2000000e+00 3.5000000e+00 5.3000000e+00 2.7000000e+00 2.6000000e+00 3.3000000e+00 1.9000000e+00 2.2000000e+00 3.1000000e+00 2.7000000e+00 6.3000000e+00 6.0000000e+00 2.0000000e+00 4.0000000e+00 1.6000000e+00 5.3000000e+00 2.0000000e+00 3.7000000e+00 4.1000000e+00 1.7000000e+00 1.7000000e+00 3.0000000e+00 3.5000000e+00 4.3000000e+00 6.0000000e+00 3.1000000e+00 1.8000000e+00 2.2000000e+00 5.0000000e+00 3.6000000e+00 2.7000000e+00 1.5000000e+00 3.4000000e+00 3.7000000e+00 3.3000000e+00 1.8000000e+00 4.1000000e+00 4.1000000e+00 3.1000000e+00 2.4000000e+00 2.6000000e+00 3.2000000e+00 1.7000000e+00 3.0000000e+00 8.0000000e-01 3.4000000e+00 2.0000000e+00 3.4000000e+00 1.9000000e+00 2.8000000e+00 4.6000000e+00 2.3000000e+00 3.6000000e+00 2.9000000e+00 4.7000000e+00 2.1000000e+00 2.0000000e+00 2.7000000e+00 2.3000000e+00 2.4000000e+00 2.5000000e+00 2.1000000e+00 5.7000000e+00 5.4000000e+00 1.8000000e+00 3.4000000e+00 2.0000000e+00 4.7000000e+00 1.4000000e+00 3.1000000e+00 3.5000000e+00 1.1000000e+00 1.3000000e+00 2.4000000e+00 2.9000000e+00 3.7000000e+00 5.4000000e+00 2.5000000e+00 1.2000000e+00 1.8000000e+00 4.4000000e+00 3.0000000e+00 2.1000000e+00 1.3000000e+00 2.8000000e+00 3.1000000e+00 2.7000000e+00 2.0000000e+00 3.5000000e+00 3.5000000e+00 2.5000000e+00 1.8000000e+00 2.0000000e+00 2.6000000e+00 1.7000000e+00 2.2000000e+00 6.4000000e+00 3.8000000e+00 6.4000000e+00 4.9000000e+00 5.8000000e+00 7.6000000e+00 2.3000000e+00 6.6000000e+00 5.1000000e+00 7.7000000e+00 5.1000000e+00 4.6000000e+00 5.7000000e+00 3.5000000e+00 4.4000000e+00 5.5000000e+00 5.1000000e+00 8.7000000e+00 7.8000000e+00 3.6000000e+00 6.4000000e+00 3.6000000e+00 7.5000000e+00 4.0000000e+00 6.1000000e+00 6.5000000e+00 3.9000000e+00 4.1000000e+00 5.2000000e+00 5.9000000e+00 6.5000000e+00 8.4000000e+00 5.3000000e+00 4.0000000e+00 4.0000000e+00 7.4000000e+00 6.0000000e+00 5.1000000e+00 3.9000000e+00 5.8000000e+00 6.1000000e+00 5.7000000e+00 3.8000000e+00 6.5000000e+00 6.5000000e+00 5.5000000e+00 4.0000000e+00 5.0000000e+00 5.6000000e+00 4.1000000e+00 4.2000000e+00 1.8000000e+00 4.2000000e+00 2.7000000e+00 3.6000000e+00 5.4000000e+00 1.9000000e+00 4.4000000e+00 3.5000000e+00 5.5000000e+00 2.9000000e+00 2.6000000e+00 3.5000000e+00 1.9000000e+00 2.2000000e+00 3.3000000e+00 2.9000000e+00 6.5000000e+00 6.0000000e+00 2.0000000e+00 4.2000000e+00 1.6000000e+00 5.3000000e+00 2.0000000e+00 3.9000000e+00 4.3000000e+00 1.7000000e+00 1.9000000e+00 3.0000000e+00 3.7000000e+00 4.3000000e+00 6.2000000e+00 3.1000000e+00 1.8000000e+00 2.2000000e+00 5.2000000e+00 3.8000000e+00 2.9000000e+00 1.7000000e+00 3.6000000e+00 3.9000000e+00 3.5000000e+00 1.8000000e+00 4.3000000e+00 4.3000000e+00 3.3000000e+00 2.4000000e+00 2.8000000e+00 3.4000000e+00 1.9000000e+00 2.6000000e+00 1.6000000e+00 1.5000000e+00 1.0000000e+00 2.6000000e+00 4.5000000e+00 2.4000000e+00 2.1000000e+00 1.3000000e+00 1.7000000e+00 2.0000000e+00 1.7000000e+00 2.9000000e+00 2.0000000e+00 1.1000000e+00 1.7000000e+00 2.9000000e+00 3.2000000e+00 3.4000000e+00 1.2000000e+00 2.8000000e+00 3.1000000e+00 2.4000000e+00 1.1000000e+00 1.7000000e+00 2.5000000e+00 2.3000000e+00 1.4000000e+00 2.3000000e+00 2.3000000e+00 3.0000000e+00 1.3000000e+00 2.4000000e+00 2.4000000e+00 2.0000000e+00 6.0000000e-01 1.5000000e+00 2.5000000e+00 1.8000000e+00 1.1000000e+00 1.9000000e+00 2.6000000e+00 9.0000000e-01 7.0000000e-01 1.7000000e+00 2.4000000e+00 1.8000000e+00 1.0000000e+00 2.3000000e+00 2.6000000e+00 1.3000000e+00 2.0000000e+00 3.8000000e+00 1.9000000e+00 3.0000000e+00 1.9000000e+00 3.9000000e+00 1.3000000e+00 8.0000000e-01 1.9000000e+00 5.0000000e-01 6.0000000e-01 1.7000000e+00 1.5000000e+00 4.9000000e+00 4.2000000e+00 1.2000000e+00 2.6000000e+00 6.0000000e-01 3.7000000e+00 8.0000000e-01 2.3000000e+00 2.9000000e+00 9.0000000e-01 9.0000000e-01 1.4000000e+00 2.7000000e+00 2.7000000e+00 4.6000000e+00 1.5000000e+00 1.0000000e+00 1.4000000e+00 3.6000000e+00 2.2000000e+00 1.5000000e+00 9.0000000e-01 2.0000000e+00 2.3000000e+00 1.9000000e+00 0.0000000e+00 2.7000000e+00 2.7000000e+00 1.7000000e+00 8.0000000e-01 1.2000000e+00 1.8000000e+00 5.0000000e-01 1.5000000e+00 8.0000000e-01 1.2000000e+00 4.5000000e+00 1.0000000e+00 1.3000000e+00 1.3000000e+00 1.7000000e+00 1.8000000e+00 7.0000000e-01 2.9000000e+00 2.6000000e+00 1.7000000e+00 1.3000000e+00 2.3000000e+00 2.2000000e+00 3.4000000e+00 8.0000000e-01 2.8000000e+00 1.7000000e+00 2.4000000e+00 9.0000000e-01 7.0000000e-01 2.5000000e+00 2.3000000e+00 1.2000000e+00 7.0000000e-01 9.0000000e-01 2.2000000e+00 1.3000000e+00 2.4000000e+00 2.4000000e+00 1.0000000e+00 1.8000000e+00 1.5000000e+00 2.5000000e+00 8.0000000e-01 1.1000000e+00 1.3000000e+00 2.6000000e+00 7.0000000e-01 1.3000000e+00 1.3000000e+00 2.4000000e+00 1.4000000e+00 2.0000000e+00 2.3000000e+00 9.0000000e-01 2.7000000e+00 3.0000000e+00 1.7000000e+00 1.0000000e+00 2.8000000e+00 1.2000000e+00 7.0000000e-01 1.0000000e+00 1.8000000e+00 1.7000000e+00 1.2000000e+00 4.0000000e-01 3.8000000e+00 3.5000000e+00 1.9000000e+00 1.5000000e+00 1.7000000e+00 2.8000000e+00 9.0000000e-01 1.2000000e+00 1.6000000e+00 1.0000000e+00 1.0000000e+00 5.0000000e-01 1.4000000e+00 1.8000000e+00 3.5000000e+00 6.0000000e-01 9.0000000e-01 9.0000000e-01 2.5000000e+00 1.1000000e+00 4.0000000e-01 1.2000000e+00 1.3000000e+00 1.2000000e+00 1.8000000e+00 1.3000000e+00 1.6000000e+00 1.6000000e+00 1.4000000e+00 1.1000000e+00 9.0000000e-01 1.3000000e+00 1.0000000e+00 2.0000000e+00 3.9000000e+00 1.8000000e+00 1.1000000e+00 1.9000000e+00 1.1000000e+00 1.2000000e+00 7.0000000e-01 2.3000000e+00 1.8000000e+00 9.0000000e-01 7.0000000e-01 2.9000000e+00 2.8000000e+00 2.8000000e+00 8.0000000e-01 2.2000000e+00 2.5000000e+00 1.8000000e+00 7.0000000e-01 1.5000000e+00 1.9000000e+00 1.7000000e+00 6.0000000e-01 1.3000000e+00 1.7000000e+00 3.0000000e+00 5.0000000e-01 1.8000000e+00 1.8000000e+00 1.6000000e+00 1.0000000e+00 9.0000000e-01 1.9000000e+00 1.0000000e+00 7.0000000e-01 1.3000000e+00 2.0000000e+00 7.0000000e-01 9.0000000e-01 9.0000000e-01 1.8000000e+00 8.0000000e-01 1.2000000e+00 1.7000000e+00 5.7000000e+00 1.0000000e+00 2.5000000e+00 1.9000000e+00 2.9000000e+00 3.0000000e+00 1.9000000e+00 4.1000000e+00 3.8000000e+00 2.9000000e+00 2.5000000e+00 1.1000000e+00 1.0000000e+00 4.6000000e+00 2.0000000e+00 4.0000000e+00 5.0000000e-01 3.6000000e+00 2.1000000e+00 1.5000000e+00 3.7000000e+00 3.5000000e+00 2.4000000e+00 1.7000000e+00 1.1000000e+00 1.4000000e+00 2.5000000e+00 3.6000000e+00 3.6000000e+00 8.0000000e-01 3.0000000e+00 2.7000000e+00 3.7000000e+00 2.0000000e+00 2.3000000e+00 2.5000000e+00 3.8000000e+00 1.9000000e+00 2.5000000e+00 2.5000000e+00 3.6000000e+00 2.6000000e+00 3.2000000e+00 3.5000000e+00 4.7000000e+00 3.2000000e+00 5.8000000e+00 3.2000000e+00 2.7000000e+00 3.8000000e+00 1.6000000e+00 2.5000000e+00 3.6000000e+00 3.2000000e+00 6.8000000e+00 5.9000000e+00 2.1000000e+00 4.5000000e+00 1.7000000e+00 5.6000000e+00 2.1000000e+00 4.2000000e+00 4.6000000e+00 2.0000000e+00 2.2000000e+00 3.3000000e+00 4.2000000e+00 4.6000000e+00 6.5000000e+00 3.4000000e+00 2.5000000e+00 2.7000000e+00 5.5000000e+00 4.1000000e+00 3.2000000e+00 2.0000000e+00 3.9000000e+00 4.2000000e+00 3.8000000e+00 1.9000000e+00 4.6000000e+00 4.6000000e+00 3.6000000e+00 2.1000000e+00 3.1000000e+00 3.7000000e+00 2.2000000e+00 1.5000000e+00 1.7000000e+00 2.5000000e+00 2.2000000e+00 1.7000000e+00 3.5000000e+00 3.4000000e+00 2.7000000e+00 1.7000000e+00 2.1000000e+00 1.8000000e+00 3.6000000e+00 1.8000000e+00 3.4000000e+00 1.1000000e+00 2.6000000e+00 1.9000000e+00 7.0000000e-01 2.7000000e+00 2.7000000e+00 2.0000000e+00 9.0000000e-01 5.0000000e-01 1.8000000e+00 2.1000000e+00 2.6000000e+00 2.6000000e+00 1.2000000e+00 2.8000000e+00 1.9000000e+00 2.9000000e+00 1.8000000e+00 2.1000000e+00 2.3000000e+00 3.0000000e+00 1.7000000e+00 2.3000000e+00 2.3000000e+00 2.8000000e+00 2.2000000e+00 3.0000000e+00 2.7000000e+00 2.6000000e+00 1.8000000e+00 1.1000000e+00 1.2000000e+00 2.0000000e+00 2.5000000e+00 2.0000000e+00 1.0000000e+00 3.6000000e+00 2.7000000e+00 2.1000000e+00 1.5000000e+00 2.5000000e+00 2.4000000e+00 1.5000000e+00 1.2000000e+00 1.4000000e+00 1.8000000e+00 2.0000000e+00 1.1000000e+00 1.2000000e+00 1.4000000e+00 3.3000000e+00 1.2000000e+00 1.7000000e+00 1.3000000e+00 2.3000000e+00 2.1000000e+00 1.2000000e+00 2.2000000e+00 1.5000000e+00 1.4000000e+00 2.0000000e+00 1.9000000e+00 1.4000000e+00 1.6000000e+00 1.6000000e+00 1.3000000e+00 1.5000000e+00 2.3000000e+00 2.0000000e+00 2.6000000e+00 3.1000000e+00 2.0000000e+00 4.2000000e+00 3.3000000e+00 2.2000000e+00 2.6000000e+00 1.6000000e+00 2.5000000e+00 4.7000000e+00 1.3000000e+00 4.1000000e+00 2.4000000e+00 3.7000000e+00 1.6000000e+00 1.2000000e+00 3.8000000e+00 3.6000000e+00 2.5000000e+00 1.8000000e+00 1.6000000e+00 1.7000000e+00 2.4000000e+00 3.7000000e+00 3.7000000e+00 1.3000000e+00 1.7000000e+00 2.6000000e+00 3.8000000e+00 1.9000000e+00 1.6000000e+00 2.0000000e+00 3.9000000e+00 1.2000000e+00 1.2000000e+00 2.2000000e+00 3.7000000e+00 2.7000000e+00 2.1000000e+00 3.6000000e+00 9.0000000e-01 1.0000000e+00 1.6000000e+00 1.5000000e+00 6.0000000e-01 8.0000000e-01 3.6000000e+00 3.9000000e+00 2.1000000e+00 1.3000000e+00 1.5000000e+00 3.2000000e+00 1.1000000e+00 1.0000000e+00 1.8000000e+00 1.2000000e+00 1.0000000e+00 1.1000000e+00 2.0000000e+00 2.4000000e+00 3.3000000e+00 1.2000000e+00 1.1000000e+00 2.1000000e+00 2.7000000e+00 1.3000000e+00 8.0000000e-01 1.2000000e+00 9.0000000e-01 1.2000000e+00 8.0000000e-01 1.3000000e+00 1.4000000e+00 1.4000000e+00 8.0000000e-01 1.1000000e+00 3.0000000e-01 1.1000000e+00 1.0000000e+00 1.1000000e+00 1.3000000e+00 1.4000000e+00 9.0000000e-01 7.0000000e-01 4.1000000e+00 3.4000000e+00 1.6000000e+00 1.8000000e+00 1.4000000e+00 2.9000000e+00 6.0000000e-01 1.5000000e+00 2.1000000e+00 9.0000000e-01 1.1000000e+00 6.0000000e-01 1.9000000e+00 1.9000000e+00 3.8000000e+00 7.0000000e-01 8.0000000e-01 1.2000000e+00 2.8000000e+00 1.6000000e+00 7.0000000e-01 1.3000000e+00 1.2000000e+00 1.5000000e+00 1.5000000e+00 8.0000000e-01 1.9000000e+00 1.9000000e+00 1.1000000e+00 6.0000000e-01 6.0000000e-01 1.4000000e+00 1.1000000e+00 2.2000000e+00 1.9000000e+00 1.0000000e+00 6.0000000e-01 3.0000000e+00 2.9000000e+00 2.7000000e+00 7.0000000e-01 2.1000000e+00 2.4000000e+00 1.7000000e+00 6.0000000e-01 1.4000000e+00 1.8000000e+00 1.6000000e+00 7.0000000e-01 1.2000000e+00 1.6000000e+00 2.9000000e+00 8.0000000e-01 1.7000000e+00 1.9000000e+00 1.7000000e+00 1.3000000e+00 8.0000000e-01 1.8000000e+00 3.0000000e-01 6.0000000e-01 8.0000000e-01 1.9000000e+00 8.0000000e-01 1.0000000e+00 6.0000000e-01 1.7000000e+00 7.0000000e-01 1.3000000e+00 1.6000000e+00 9.0000000e-01 2.0000000e+00 2.0000000e+00 5.2000000e+00 4.3000000e+00 1.1000000e+00 2.9000000e+00 5.0000000e-01 4.0000000e+00 1.1000000e+00 2.6000000e+00 3.4000000e+00 1.2000000e+00 1.2000000e+00 1.7000000e+00 3.2000000e+00 3.2000000e+00 4.9000000e+00 1.8000000e+00 1.5000000e+00 1.7000000e+00 3.9000000e+00 2.5000000e+00 2.0000000e+00 1.2000000e+00 2.3000000e+00 2.6000000e+00 2.2000000e+00 5.0000000e-01 3.0000000e+00 3.0000000e+00 2.0000000e+00 7.0000000e-01 1.5000000e+00 2.1000000e+00 1.0000000e+00 1.3000000e+00 1.9000000e+00 4.7000000e+00 4.0000000e+00 1.8000000e+00 2.2000000e+00 8.0000000e-01 3.9000000e+00 1.4000000e+00 2.3000000e+00 3.3000000e+00 1.3000000e+00 1.3000000e+00 1.4000000e+00 3.1000000e+00 3.1000000e+00 4.8000000e+00 1.3000000e+00 1.4000000e+00 2.0000000e+00 3.2000000e+00 1.6000000e+00 1.9000000e+00 1.3000000e+00 2.0000000e+00 1.7000000e+00 1.5000000e+00 6.0000000e-01 2.3000000e+00 2.1000000e+00 1.3000000e+00 1.4000000e+00 1.4000000e+00 1.4000000e+00 9.0000000e-01 1.0000000e+00 3.4000000e+00 3.5000000e+00 2.5000000e+00 9.0000000e-01 1.9000000e+00 3.4000000e+00 1.5000000e+00 1.0000000e+00 2.0000000e+00 1.6000000e+00 1.4000000e+00 9.0000000e-01 2.2000000e+00 2.6000000e+00 3.5000000e+00 8.0000000e-01 1.5000000e+00 2.1000000e+00 2.3000000e+00 7.0000000e-01 8.0000000e-01 1.6000000e+00 9.0000000e-01 8.0000000e-01 8.0000000e-01 1.7000000e+00 1.0000000e+00 1.0000000e+00 6.0000000e-01 1.5000000e+00 7.0000000e-01 5.0000000e-01 1.4000000e+00 3.6000000e+00 3.5000000e+00 2.1000000e+00 1.3000000e+00 1.9000000e+00 2.8000000e+00 1.1000000e+00 1.0000000e+00 1.4000000e+00 1.2000000e+00 1.0000000e+00 7.0000000e-01 1.2000000e+00 1.8000000e+00 3.3000000e+00 8.0000000e-01 1.1000000e+00 1.3000000e+00 2.3000000e+00 1.3000000e+00 2.0000000e-01 1.2000000e+00 9.0000000e-01 1.0000000e+00 1.4000000e+00 1.5000000e+00 1.4000000e+00 1.4000000e+00 1.0000000e+00 1.3000000e+00 5.0000000e-01 1.3000000e+00 1.0000000e+00 1.5000000e+00 5.7000000e+00 2.5000000e+00 5.1000000e+00 1.2000000e+00 4.7000000e+00 2.6000000e+00 2.2000000e+00 4.8000000e+00 4.6000000e+00 3.5000000e+00 2.8000000e+00 2.2000000e+00 7.0000000e-01 3.4000000e+00 4.7000000e+00 4.7000000e+00 1.5000000e+00 3.1000000e+00 3.6000000e+00 4.8000000e+00 2.9000000e+00 3.0000000e+00 3.2000000e+00 4.9000000e+00 2.4000000e+00 2.8000000e+00 3.4000000e+00 4.7000000e+00 3.7000000e+00 3.3000000e+00 4.6000000e+00 4.8000000e+00 2.6000000e+00 4.6000000e+00 7.0000000e-01 4.0000000e+00 3.1000000e+00 2.5000000e+00 4.3000000e+00 4.5000000e+00 3.0000000e+00 2.7000000e+00 1.7000000e+00 2.2000000e+00 2.9000000e+00 4.2000000e+00 3.8000000e+00 1.2000000e+00 3.6000000e+00 3.7000000e+00 4.7000000e+00 3.0000000e+00 2.9000000e+00 3.1000000e+00 4.2000000e+00 2.5000000e+00 3.1000000e+00 3.1000000e+00 3.8000000e+00 3.6000000e+00 3.8000000e+00 4.5000000e+00 3.4000000e+00 1.6000000e+00 4.5000000e+00 1.2000000e+00 3.1000000e+00 3.5000000e+00 1.3000000e+00 1.3000000e+00 2.2000000e+00 2.9000000e+00 3.5000000e+00 5.4000000e+00 2.3000000e+00 1.0000000e+00 1.2000000e+00 4.4000000e+00 3.0000000e+00 2.1000000e+00 1.3000000e+00 2.8000000e+00 3.1000000e+00 2.7000000e+00 1.2000000e+00 3.5000000e+00 3.5000000e+00 2.5000000e+00 1.0000000e+00 2.0000000e+00 2.6000000e+00 1.3000000e+00 2.8000000e+00 2.5000000e+00 2.4000000e+00 5.0000000e-01 1.1000000e+00 2.5000000e+00 2.3000000e+00 1.2000000e+00 1.3000000e+00 1.7000000e+00 2.6000000e+00 1.1000000e+00 2.4000000e+00 2.4000000e+00 1.4000000e+00 1.0000000e+00 1.3000000e+00 2.5000000e+00 6.0000000e-01 5.0000000e-01 7.0000000e-01 2.6000000e+00 3.0000000e-01 5.0000000e-01 9.0000000e-01 2.4000000e+00 1.4000000e+00 1.2000000e+00 2.3000000e+00 3.9000000e+00 1.0000000e+00 2.5000000e+00 3.3000000e+00 9.0000000e-01 9.0000000e-01 1.6000000e+00 3.1000000e+00 3.1000000e+00 4.8000000e+00 1.7000000e+00 1.4000000e+00 2.0000000e+00 3.8000000e+00 2.4000000e+00 1.9000000e+00 9.0000000e-01 2.2000000e+00 2.5000000e+00 2.1000000e+00 6.0000000e-01 2.9000000e+00 2.9000000e+00 1.9000000e+00 1.2000000e+00 1.4000000e+00 2.0000000e+00 9.0000000e-01 3.5000000e+00 2.6000000e+00 1.8000000e+00 3.6000000e+00 3.8000000e+00 2.5000000e+00 2.0000000e+00 1.0000000e+00 1.5000000e+00 2.6000000e+00 3.5000000e+00 3.5000000e+00 1.1000000e+00 3.5000000e+00 3.0000000e+00 4.0000000e+00 2.5000000e+00 2.8000000e+00 3.0000000e+00 3.7000000e+00 2.4000000e+00 3.0000000e+00 3.0000000e+00 3.5000000e+00 2.9000000e+00 3.7000000e+00 3.8000000e+00 2.1000000e+00 2.5000000e+00 3.0000000e-01 5.0000000e-01 1.2000000e+00 2.3000000e+00 2.5000000e+00 4.4000000e+00 1.3000000e+00 6.0000000e-01 1.4000000e+00 3.4000000e+00 2.0000000e+00 1.1000000e+00 7.0000000e-01 1.8000000e+00 2.1000000e+00 1.7000000e+00 8.0000000e-01 2.5000000e+00 2.5000000e+00 1.5000000e+00 4.0000000e-01 1.0000000e+00 1.8000000e+00 9.0000000e-01 1.2000000e+00 2.2000000e+00 2.0000000e+00 9.0000000e-01 1.4000000e+00 1.8000000e+00 2.5000000e+00 1.0000000e+00 2.1000000e+00 2.1000000e+00 1.9000000e+00 9.0000000e-01 1.0000000e+00 2.2000000e+00 7.0000000e-01 6.0000000e-01 1.2000000e+00 2.3000000e+00 6.0000000e-01 4.0000000e-01 1.0000000e+00 2.1000000e+00 1.1000000e+00 1.1000000e+00 2.0000000e+00 2.6000000e+00 2.4000000e+00 1.9000000e+00 6.0000000e-01 8.0000000e-01 1.9000000e+00 2.0000000e+00 2.5000000e+00 2.5000000e+00 1.3000000e+00 2.1000000e+00 1.4000000e+00 2.6000000e+00 1.3000000e+00 1.6000000e+00 1.8000000e+00 2.9000000e+00 1.0000000e+00 1.6000000e+00 2.0000000e+00 2.7000000e+00 1.9000000e+00 2.3000000e+00 2.4000000e+00 4.0000000e-01 1.3000000e+00 2.4000000e+00 2.6000000e+00 4.5000000e+00 1.4000000e+00 7.0000000e-01 1.5000000e+00 3.5000000e+00 2.1000000e+00 1.2000000e+00 4.0000000e-01 1.9000000e+00 2.2000000e+00 1.8000000e+00 9.0000000e-01 2.6000000e+00 2.6000000e+00 1.6000000e+00 7.0000000e-01 1.1000000e+00 1.7000000e+00 8.0000000e-01 1.5000000e+00 2.2000000e+00 2.8000000e+00 4.3000000e+00 1.6000000e+00 9.0000000e-01 1.5000000e+00 3.3000000e+00 1.9000000e+00 1.0000000e+00 2.0000000e-01 1.7000000e+00 2.0000000e+00 1.6000000e+00 9.0000000e-01 2.4000000e+00 2.4000000e+00 1.4000000e+00 9.0000000e-01 9.0000000e-01 1.5000000e+00 4.0000000e-01 1.7000000e+00 1.7000000e+00 3.4000000e+00 1.0000000e-01 1.2000000e+00 1.2000000e+00 2.2000000e+00 1.0000000e+00 7.0000000e-01 1.7000000e+00 1.0000000e+00 9.0000000e-01 1.5000000e+00 1.4000000e+00 1.3000000e+00 1.3000000e+00 1.1000000e+00 1.2000000e+00 8.0000000e-01 1.2000000e+00 1.5000000e+00 1.0000000e+00 2.5000000e+00 1.8000000e+00 1.9000000e+00 1.9000000e+00 1.5000000e+00 2.3000000e+00 1.4000000e+00 2.4000000e+00 1.3000000e+00 1.6000000e+00 1.8000000e+00 2.7000000e+00 1.4000000e+00 1.8000000e+00 1.8000000e+00 2.5000000e+00 1.7000000e+00 2.5000000e+00 2.2000000e+00 1.9000000e+00 1.8000000e+00 2.5000000e+00 2.5000000e+00 9.0000000e-01 2.7000000e+00 2.0000000e+00 3.0000000e+00 1.7000000e+00 2.0000000e+00 2.2000000e+00 2.7000000e+00 1.6000000e+00 2.2000000e+00 2.2000000e+00 2.5000000e+00 2.1000000e+00 2.9000000e+00 2.8000000e+00 3.5000000e+00 4.4000000e+00 4.4000000e+00 1.6000000e+00 3.2000000e+00 3.3000000e+00 4.5000000e+00 2.8000000e+00 3.1000000e+00 3.3000000e+00 4.6000000e+00 2.5000000e+00 2.9000000e+00 3.5000000e+00 4.4000000e+00 3.4000000e+00 3.4000000e+00 4.3000000e+00 1.3000000e+00 1.3000000e+00 2.1000000e+00 9.0000000e-01 8.0000000e-01 1.8000000e+00 1.1000000e+00 8.0000000e-01 1.4000000e+00 1.5000000e+00 1.2000000e+00 1.2000000e+00 1.0000000e+00 1.3000000e+00 9.0000000e-01 1.1000000e+00 1.6000000e+00 1.0000000e+00 3.4000000e+00 2.0000000e+00 1.1000000e+00 1.1000000e+00 1.8000000e+00 2.1000000e+00 1.7000000e+00 1.0000000e+00 2.5000000e+00 2.5000000e+00 1.5000000e+00 8.0000000e-01 1.0000000e+00 1.8000000e+00 9.0000000e-01 3.4000000e+00 2.0000000e+00 1.3000000e+00 1.7000000e+00 2.2000000e+00 2.1000000e+00 2.7000000e+00 1.4000000e+00 2.5000000e+00 2.5000000e+00 2.3000000e+00 1.4000000e+00 1.8000000e+00 2.0000000e+00 1.5000000e+00 2.4000000e+00 2.5000000e+00 3.5000000e+00 1.8000000e+00 1.7000000e+00 1.9000000e+00 3.6000000e+00 1.3000000e+00 1.9000000e+00 1.9000000e+00 3.4000000e+00 2.4000000e+00 2.6000000e+00 3.3000000e+00 1.1000000e+00 2.1000000e+00 1.4000000e+00 7.0000000e-01 1.5000000e+00 2.2000000e+00 1.1000000e+00 7.0000000e-01 1.3000000e+00 2.0000000e+00 1.4000000e+00 4.0000000e-01 1.9000000e+00 1.2000000e+00 9.0000000e-01 1.0000000e+00 1.4000000e+00 1.5000000e+00 1.4000000e+00 1.4000000e+00 1.2000000e+00 1.3000000e+00 7.0000000e-01 1.1000000e+00 1.0000000e+00 1.9000000e+00 2.2000000e+00 1.8000000e+00 9.0000000e-01 2.6000000e+00 2.6000000e+00 1.6000000e+00 1.1000000e+00 1.1000000e+00 1.7000000e+00 4.0000000e-01 7.0000000e-01 5.0000000e-01 2.0000000e+00 9.0000000e-01 1.1000000e+00 7.0000000e-01 1.8000000e+00 8.0000000e-01 1.2000000e+00 1.7000000e+00 8.0000000e-01 2.3000000e+00 6.0000000e-01 4.0000000e-01 6.0000000e-01 2.1000000e+00 1.1000000e+00 1.1000000e+00 2.0000000e+00 1.9000000e+00 1.0000000e+00 1.2000000e+00 4.0000000e-01 1.7000000e+00 9.0000000e-01 1.3000000e+00 1.6000000e+00 2.7000000e+00 2.7000000e+00 1.7000000e+00 8.0000000e-01 1.2000000e+00 1.8000000e+00 5.0000000e-01 6.0000000e-01 1.0000000e+00 2.5000000e+00 1.5000000e+00 1.3000000e+00 2.4000000e+00 1.0000000e+00 2.5000000e+00 1.5000000e+00 1.1000000e+00 2.4000000e+00 1.5000000e+00 5.0000000e-01 1.1000000e+00 1.4000000e+00 1.0000000e+00 1.8000000e+00 1.1000000e+00 1.2000000e+00 9.0000000e-01 1.5000000e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..8fb22e62200894f9693010ebf45fb23751f4e3a6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cityblock-ml.txt @@ -0,0 +1 @@ + 3.2420590e+01 3.3246607e+01 3.0526910e+01 3.5166573e+01 3.1868301e+01 3.6025002e+01 3.2513623e+01 3.6557796e+01 3.3752212e+01 3.4422130e+01 3.2526018e+01 3.2581161e+01 3.3743555e+01 3.6960777e+01 3.4225270e+01 3.2965308e+01 3.4591031e+01 3.4204203e+01 3.4678123e+01 3.5728720e+01 3.0830047e+01 3.1550681e+01 3.3304790e+01 3.2676753e+01 3.2742330e+01 3.1684556e+01 3.2830915e+01 3.2956614e+01 2.7365639e+01 3.3207307e+01 3.3420925e+01 3.4357941e+01 2.8280126e+01 3.4523458e+01 3.2705274e+01 3.2455891e+01 3.1636060e+01 3.1594957e+01 3.1805202e+01 3.3886574e+01 3.3438829e+01 3.3330030e+01 3.4168514e+01 3.0637353e+01 4.2149167e+01 3.6340559e+01 2.9315308e+01 3.5778314e+01 3.7693050e+01 3.2598714e+01 3.2990836e+01 3.4967659e+01 3.9748920e+01 3.6745043e+01 2.7117550e+01 3.6014760e+01 2.9367558e+01 3.3845350e+01 3.5477339e+01 3.1513372e+01 3.2517953e+01 2.4755097e+01 3.0229897e+01 3.4799343e+01 3.3371710e+01 2.9600910e+01 3.3275088e+01 3.3567110e+01 3.4527016e+01 3.4942320e+01 3.2359383e+01 3.2607100e+01 3.1467914e+01 2.9032039e+01 3.3122878e+01 2.8496709e+01 2.9908448e+01 2.9962886e+01 3.0345299e+01 3.1737613e+01 2.8551485e+01 3.2610551e+01 3.3082660e+01 3.3719298e+01 3.6434018e+01 3.6589278e+01 3.3889586e+01 3.8036774e+01 3.1483497e+01 3.4196794e+01 3.5154035e+01 3.5488608e+01 3.6143183e+01 3.3473491e+01 3.4686446e+01 2.8687495e+01 3.5725742e+01 3.0188298e+01 3.3084534e+01 3.3538519e+01 3.6226849e+01 2.9052099e+01 3.6032733e+01 3.0811503e+01 3.2616190e+01 3.3888566e+01 3.3074570e+01 2.9683515e+01 3.0600771e+01 3.4345247e+01 3.6983843e+01 3.3692824e+01 3.3762461e+01 3.4024582e+01 3.3698854e+01 3.1238613e+01 3.4978833e+01 3.4991078e+01 3.4577741e+01 3.3749227e+01 3.4982272e+01 3.0487868e+01 3.2317632e+01 3.1125588e+01 3.4413791e+01 3.1881871e+01 3.1373821e+01 3.0416864e+01 3.2066187e+01 3.1128313e+01 3.0240249e+01 3.0125198e+01 3.1343454e+01 3.5479092e+01 3.4450767e+01 3.2953507e+01 3.4456795e+01 3.0136375e+01 3.3462150e+01 2.9894274e+01 3.1367432e+01 3.2839320e+01 3.1440398e+01 2.9400374e+01 3.1106338e+01 3.1242624e+01 3.5537892e+01 3.3056459e+01 2.8610281e+01 3.4296217e+01 3.5819772e+01 3.2503922e+01 3.0963029e+01 3.4762112e+01 3.4796284e+01 2.9645345e+01 3.4468088e+01 2.6975590e+01 3.3738555e+01 2.8825009e+01 3.2663999e+01 3.2547878e+01 3.2308091e+01 3.2489966e+01 3.0868597e+01 3.2974220e+01 3.0866111e+01 3.8197342e+01 3.0609568e+01 3.5478978e+01 2.9249184e+01 3.6185622e+01 3.1948258e+01 3.2649719e+01 3.3305650e+01 3.4643955e+01 3.6566241e+01 3.4968484e+01 3.2632218e+01 3.6741383e+01 3.5700008e+01 3.1962468e+01 3.1410623e+01 3.0412061e+01 3.3749077e+01 3.5649661e+01 3.7649263e+01 3.2832574e+01 3.1783914e+01 2.8264292e+01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..f297500381fe33428e6725ea3b10ab6191a46fa3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml-iris.txt @@ -0,0 +1 @@ + 4.0013388e-03 2.6088954e-05 1.8315482e-03 6.5266850e-04 4.1394685e-04 1.1888069e-03 4.6185289e-04 1.9233577e-03 3.4480388e-03 1.5150632e-05 1.9126718e-03 3.0974734e-03 2.2295833e-04 2.4043394e-03 5.0134320e-03 3.0165570e-03 1.3145239e-04 6.0759419e-04 1.6672981e-03 4.0036132e-03 6.1375191e-04 8.5916540e-03 3.0212269e-03 8.6923503e-03 7.7875235e-03 5.1612907e-04 2.9662451e-04 6.2402983e-04 2.7278440e-03 4.0510347e-03 3.0027154e-03 6.2616145e-03 4.1342211e-03 3.4480388e-03 1.5822510e-03 1.7143312e-03 3.4480388e-03 2.2462074e-04 6.1048465e-04 6.5190641e-04 2.4247873e-02 9.0785596e-04 2.1652052e-04 3.4845573e-03 3.2507646e-03 2.3346511e-03 4.0773355e-04 1.1278223e-04 5.0819669e-04 2.1340893e-01 2.1253858e-01 2.5193073e-01 2.9479565e-01 2.6774348e-01 2.8869785e-01 2.3348217e-01 1.9273490e-01 2.4443270e-01 2.4320510e-01 2.7679421e-01 2.1672263e-01 2.6813840e-01 2.8435705e-01 1.5561363e-01 2.0057173e-01 2.7812139e-01 2.2900256e-01 3.4724680e-01 2.3882260e-01 2.9132931e-01 2.0333645e-01 3.5307051e-01 2.8812452e-01 2.1722530e-01 2.1423111e-01 2.7396952e-01 2.9207940e-01 2.6626182e-01 1.7106032e-01 2.4279706e-01 2.2559055e-01 2.0940857e-01 3.8432412e-01 2.9354670e-01 2.0829958e-01 2.3669414e-01 3.0463326e-01 2.1035851e-01 2.6623117e-01 3.0835417e-01 2.5871089e-01 2.3465249e-01 2.0319416e-01 2.6292582e-01 2.1771735e-01 2.3212816e-01 2.2399387e-01 1.3799316e-01 2.3049526e-01 4.8512087e-01 4.2535066e-01 4.0184471e-01 4.1903049e-01 4.4627199e-01 4.4692268e-01 4.3569888e-01 4.2673251e-01 4.5731950e-01 3.7438176e-01 3.0619251e-01 4.0039114e-01 3.7245195e-01 4.5829878e-01 4.5814844e-01 3.6107062e-01 3.7600936e-01 3.7662883e-01 5.2492832e-01 4.2684428e-01 3.7975064e-01 4.0636707e-01 4.6364339e-01 3.4607190e-01 3.6988036e-01 3.6764668e-01 3.2524634e-01 3.1943549e-01 4.4481193e-01 3.5496498e-01 4.1356534e-01 3.2082320e-01 4.5322964e-01 3.4300770e-01 4.4485158e-01 3.9755578e-01 3.9702418e-01 3.7202285e-01 3.1131344e-01 3.4018064e-01 4.0217537e-01 3.1441868e-01 4.2535066e-01 4.1533176e-01 3.9695242e-01 3.5313531e-01 3.9400199e-01 3.4652657e-01 3.6608320e-01 3.6684161e-01 3.3929143e-03 2.6033698e-03 7.7673212e-03 6.4081099e-03 9.2794464e-03 2.8819447e-03 1.4536586e-03 9.6714455e-04 3.7992387e-03 5.9342609e-03 3.9974031e-04 6.0694735e-03 9.1304628e-03 1.7655983e-02 1.1643899e-02 4.0363794e-03 1.6463709e-03 1.0706739e-02 6.7984475e-04 7.6845878e-03 2.3516587e-02 9.9502337e-05 1.0315881e-02 1.0821735e-03 1.8887942e-03 2.4624674e-03 1.5760536e-03 3.6638868e-03 1.6253664e-03 7.8762517e-04 1.9487010e-02 1.6211862e-02 9.6714455e-04 2.2382105e-03 2.1712385e-03 9.6714455e-04 2.9674185e-03 1.9068589e-03 6.4555509e-03 8.8254342e-03 8.1777355e-03 3.4663084e-03 9.6481454e-03 9.7747764e-05 1.0706793e-02 4.2246850e-03 4.9836128e-03 1.6613867e-03 1.6856078e-01 1.6930583e-01 2.0381801e-01 2.4171317e-01 2.1689289e-01 2.4212069e-01 1.9027913e-01 1.5127382e-01 1.9696970e-01 1.9830901e-01 2.2503195e-01 1.7290786e-01 2.1618942e-01 2.3648275e-01 1.1720113e-01 1.5636322e-01 2.3357633e-01 1.8548772e-01 2.8791738e-01 1.9215793e-01 2.4470933e-01 1.5850128e-01 2.9662484e-01 2.4061109e-01 1.7172858e-01 1.6853658e-01 2.2283143e-01 2.4032537e-01 2.1849821e-01 1.2975740e-01 1.9502432e-01 1.7953012e-01 1.6504306e-01 3.2966120e-01 2.4985160e-01 1.6946778e-01 1.8991741e-01 2.4919698e-01 1.7046257e-01 2.1691882e-01 2.6018338e-01 2.1310883e-01 1.8776864e-01 1.5909082e-01 2.1620321e-01 1.7782256e-01 1.8911127e-01 1.7894094e-01 9.9649433e-02 1.8605378e-01 4.2702260e-01 3.6742642e-01 3.4284735e-01 3.6351462e-01 3.8703088e-01 3.8643219e-01 3.8033312e-01 3.6849688e-01 3.9561383e-01 3.1894438e-01 2.5437467e-01 3.4129756e-01 3.1464030e-01 3.9617829e-01 3.9513061e-01 3.0506617e-01 3.2181902e-01 3.2465822e-01 4.5849285e-01 3.6615509e-01 3.2201598e-01 3.4969244e-01 4.0175045e-01 2.8934891e-01 3.1614551e-01 3.1385643e-01 2.7059114e-01 2.6798818e-01 3.8429703e-01 3.0087709e-01 3.5349775e-01 2.7097983e-01 3.9156246e-01 2.9045741e-01 3.8972428e-01 3.3603762e-01 3.4254663e-01 3.1960575e-01 2.6050327e-01 2.8406500e-01 3.4225008e-01 2.5735736e-01 3.6742642e-01 3.5724408e-01 3.3861338e-01 2.9412113e-01 3.3288556e-01 2.9101723e-01 3.1374321e-01 3.1516519e-01 1.6665208e-03 9.3886805e-04 6.2270349e-04 1.5623211e-03 3.9549141e-04 1.6439076e-03 3.0144044e-03 3.0583810e-05 2.0234943e-03 2.6246966e-03 3.8983492e-04 2.5645160e-03 5.6944285e-03 3.3339055e-03 1.2831328e-04 4.1302346e-04 2.1101667e-03 3.4972086e-03 8.7482704e-04 9.4271115e-03 2.5080125e-03 8.7042936e-03 7.0125369e-03 3.5088415e-04 1.9451019e-04 3.9574419e-04 2.5986219e-03 3.6402032e-03 2.4900748e-03 7.0784673e-03 4.7935149e-03 3.0144044e-03 1.2869381e-03 1.4017897e-03 3.0144044e-03 1.7197161e-04 4.4525534e-04 7.8138074e-04 2.2693053e-02 1.2229281e-03 1.5754704e-04 3.7899670e-03 2.6957902e-03 2.7721274e-03 4.5733371e-04 2.2324575e-04 3.1003560e-04 2.1002397e-01 2.0931874e-01 2.4834042e-01 2.9081912e-01 2.6391644e-01 2.8536997e-01 2.3033428e-01 1.8962461e-01 2.4088505e-01 2.3991724e-01 2.7290008e-01 2.1345771e-01 2.6419304e-01 2.8088883e-01 1.5266518e-01 1.9720348e-01 2.7496549e-01 2.2580939e-01 3.4275207e-01 2.3533918e-01 2.8800391e-01 1.9991219e-01 3.4890690e-01 2.8470246e-01 2.1378625e-01 2.1075909e-01 2.7013290e-01 2.8823552e-01 2.6275308e-01 1.6787442e-01 2.3921107e-01 2.2212337e-01 2.0605918e-01 3.8041788e-01 2.9051019e-01 2.0550537e-01 2.3319159e-01 3.0043218e-01 2.0746652e-01 2.6256228e-01 3.0491843e-01 2.5539836e-01 2.3113139e-01 1.9984797e-01 2.5951290e-01 2.1484809e-01 2.2899468e-01 2.2062653e-01 1.3495710e-01 2.2721304e-01 4.8106467e-01 4.2120214e-01 3.9753504e-01 4.1511036e-01 4.4203186e-01 4.4255637e-01 4.3182495e-01 4.2255527e-01 4.5284888e-01 3.7037516e-01 3.0238339e-01 3.9606802e-01 3.6819494e-01 4.5378706e-01 4.5354234e-01 3.5697379e-01 3.7213234e-01 3.7297305e-01 5.2009409e-01 4.2241474e-01 3.7552000e-01 4.0230555e-01 4.5916611e-01 3.4185974e-01 3.6603540e-01 3.6379117e-01 3.2119479e-01 3.1570003e-01 4.4043880e-01 3.5104995e-01 4.0917086e-01 3.1725229e-01 4.4875464e-01 3.3921954e-01 4.4101743e-01 3.9296628e-01 3.9316311e-01 3.6831351e-01 3.0762140e-01 3.3601664e-01 3.9776902e-01 3.1006892e-01 4.2120214e-01 4.1114584e-01 3.9269988e-01 3.4869458e-01 3.8944692e-01 3.4244384e-01 3.6236872e-01 3.6319420e-01 3.2811792e-03 2.1674206e-03 3.8606330e-03 4.5444049e-04 1.6669051e-04 6.9315236e-04 1.5191179e-03 7.4896915e-04 1.0486334e-03 3.0115188e-03 8.3553530e-03 1.1528814e-02 9.5172421e-03 2.7099731e-03 7.1677618e-04 4.9455548e-03 1.1260396e-03 4.0113810e-03 1.7041109e-02 1.7048436e-03 3.2998306e-03 3.5839458e-03 6.5708756e-04 7.5073414e-04 1.6739794e-03 1.4404874e-04 6.5489426e-04 3.9918560e-03 9.7678136e-03 9.5698494e-03 6.9315236e-04 4.1051921e-03 4.2098821e-03 6.9315236e-04 7.7852178e-04 5.0066998e-04 4.6641147e-03 1.9877450e-02 2.9999880e-03 2.6696154e-03 2.3124511e-03 2.6940762e-03 3.6188953e-03 7.8131154e-04 1.7395433e-03 1.1236329e-03 1.8068407e-01 1.7911784e-01 2.1632614e-01 2.5732132e-01 2.3189913e-01 2.4898678e-01 1.9775994e-01 1.6095697e-01 2.0933838e-01 2.0710048e-01 2.4048237e-01 1.8306865e-01 2.3298507e-01 2.4543409e-01 1.2760114e-01 1.6921167e-01 2.3873595e-01 1.9385405e-01 3.0859675e-01 2.0396213e-01 2.5141439e-01 1.7197884e-01 3.1193260e-01 2.4875015e-01 1.8437187e-01 1.8189435e-01 2.3759181e-01 2.5412614e-01 2.2898904e-01 1.4232827e-01 2.0806035e-01 1.9199138e-01 1.7693745e-01 3.4002004e-01 2.5277786e-01 1.7384453e-01 2.0213849e-01 2.6767804e-01 1.7597754e-01 2.2968366e-01 2.6752309e-01 2.2134041e-01 2.0039734e-01 1.7140536e-01 2.2556075e-01 1.8258974e-01 1.9648053e-01 1.9006393e-01 1.1321036e-01 1.9554999e-01 4.3600761e-01 3.7954719e-01 3.5812457e-01 3.7278397e-01 3.9968201e-01 4.0081518e-01 3.8843945e-01 3.8096257e-01 4.1111272e-01 3.3107441e-01 2.6691137e-01 3.5682832e-01 3.3041468e-01 4.1223120e-01 4.1255773e-01 3.1903974e-01 3.3211211e-01 3.3199965e-01 4.7707633e-01 3.8217339e-01 3.3708398e-01 3.6131644e-01 4.1712805e-01 3.0571403e-01 3.2625308e-01 3.2419755e-01 2.8563873e-01 2.7883266e-01 3.9884997e-01 3.1256889e-01 3.6952768e-01 2.7953122e-01 4.0726631e-01 3.0093515e-01 3.9702583e-01 3.5566883e-01 3.5181973e-01 3.2782384e-01 2.7114516e-01 3.0000941e-01 3.5891976e-01 2.7762255e-01 3.7954719e-01 3.7024637e-01 3.5327118e-01 3.1362016e-01 3.5214869e-01 3.0546169e-01 3.2226324e-01 3.2277503e-01 1.1668032e-04 8.6044327e-05 1.4968429e-03 3.9691382e-03 6.2388252e-03 7.0588028e-04 1.9691519e-03 6.1279520e-03 1.9660913e-04 2.5761274e-03 2.5168387e-03 2.4029967e-03 9.7429318e-04 2.3122381e-03 2.3682626e-04 7.1643085e-03 1.3128642e-04 5.3939078e-03 6.2992904e-03 9.0353935e-03 1.2266741e-02 2.0706893e-03 1.5408774e-03 2.5522607e-03 3.9522692e-03 6.6899152e-03 6.3980861e-03 2.9039306e-03 1.6942568e-03 6.2388252e-03 3.9336207e-03 4.1533642e-03 6.2388252e-03 1.2180733e-03 2.1518176e-03 8.8270219e-04 3.2743785e-02 7.7572782e-05 1.3417853e-03 2.5322339e-03 6.7844678e-03 8.2761566e-04 8.6236157e-04 3.1792685e-04 2.2579028e-03 2.2976852e-01 2.2800773e-01 2.6917639e-01 3.1391346e-01 2.8619117e-01 3.0434544e-01 2.4844431e-01 2.0773635e-01 2.6148944e-01 2.5886513e-01 2.9555853e-01 2.3241008e-01 2.8723433e-01 3.0077518e-01 1.6999944e-01 2.1692490e-01 2.9290302e-01 2.4423212e-01 3.6894940e-01 2.5556289e-01 3.0695160e-01 2.1997775e-01 3.7292526e-01 3.0427758e-01 2.3385552e-01 2.3106102e-01 2.9243468e-01 3.1048744e-01 2.8298832e-01 1.8662712e-01 2.6007204e-01 2.4232174e-01 2.2560003e-01 4.0265980e-01 3.0762748e-01 2.2151447e-01 2.5355085e-01 3.2493368e-01 2.2408199e-01 2.8382455e-01 3.2448802e-01 2.7442187e-01 2.5162228e-01 2.1940897e-01 2.7915380e-01 2.3127910e-01 2.4702051e-01 2.4019206e-01 1.5301315e-01 2.4619533e-01 5.0391216e-01 4.4483392e-01 4.2228707e-01 4.3731273e-01 4.6617394e-01 4.6750253e-01 4.5367757e-01 4.4636521e-01 4.7842690e-01 3.9329580e-01 3.2434154e-01 4.2091234e-01 3.9272980e-01 4.7962551e-01 4.7998875e-01 3.8052757e-01 3.9422077e-01 3.9365636e-01 5.4778535e-01 4.4784024e-01 3.9985415e-01 4.2545240e-01 4.8476479e-01 3.6622351e-01 3.8794414e-01 3.8577592e-01 3.4462011e-01 3.3712507e-01 4.6543611e-01 3.7346604e-01 4.3442200e-01 3.3762412e-01 4.7437523e-01 3.6087712e-01 4.6258851e-01 4.1954525e-01 4.1507031e-01 3.8935328e-01 3.2880662e-01 3.6009673e-01 4.2314218e-01 3.3549029e-01 4.4483392e-01 4.3505302e-01 4.1710447e-01 3.7450914e-01 4.1581746e-01 3.6597096e-01 3.8346411e-01 3.8386195e-01 2.7739415e-04 8.2117467e-04 2.7843462e-03 4.7394226e-03 3.9365385e-04 1.1964598e-03 4.7400628e-03 2.5527396e-04 3.2634446e-03 3.7103657e-03 3.3188195e-03 8.6302611e-04 1.5635411e-03 6.0189508e-04 5.5859876e-03 3.8282951e-04 7.0925635e-03 5.0273924e-03 7.3470160e-03 1.0223636e-02 1.3503463e-03 9.3049535e-04 1.9663208e-03 2.7155903e-03 5.0798223e-03 5.5875952e-03 3.5987384e-03 2.6550151e-03 4.7394226e-03 3.5923878e-03 3.7937786e-03 4.7394226e-03 6.6480476e-04 1.3814035e-03 1.1581699e-03 3.0091048e-02 1.0888067e-04 1.1634967e-03 1.9052023e-03 5.6332034e-03 7.9034466e-04 3.5005887e-04 1.0179107e-04 1.6076683e-03 2.2018795e-01 2.1841167e-01 2.5888963e-01 3.0298911e-01 2.7568827e-01 2.9345793e-01 2.3845526e-01 1.9853879e-01 2.5133209e-01 2.4870042e-01 2.8491736e-01 2.2273608e-01 2.7678086e-01 2.8993699e-01 1.6165333e-01 2.0762176e-01 2.8220337e-01 2.3432093e-01 3.5743296e-01 2.4549712e-01 2.9602684e-01 2.1063470e-01 3.6116406e-01 2.9338620e-01 2.2421094e-01 2.2149308e-01 2.8182182e-01 2.9956540e-01 2.7243830e-01 1.7796817e-01 2.4995634e-01 2.3251210e-01 2.1609445e-01 3.9047864e-01 2.9675065e-01 2.1202908e-01 2.4353030e-01 3.1395037e-01 2.1453916e-01 2.7329907e-01 3.1330772e-01 2.6399518e-01 2.4164659e-01 2.1003834e-01 2.6865531e-01 2.2160867e-01 2.3705638e-01 2.3039039e-01 1.4523591e-01 2.3625874e-01 4.9071957e-01 4.3219633e-01 4.0993016e-01 4.2475440e-01 4.5332327e-01 4.5465382e-01 4.4096188e-01 4.3371359e-01 4.6548638e-01 3.8123224e-01 3.1318896e-01 4.0857587e-01 3.8073064e-01 4.6668253e-01 4.6707002e-01 3.6864305e-01 3.8213773e-01 3.8159377e-01 5.3428461e-01 4.3521869e-01 3.8775353e-01 4.1301893e-01 4.7176152e-01 3.5457739e-01 3.7593537e-01 3.7379344e-01 3.3323181e-01 3.2577190e-01 4.5261005e-01 3.6164142e-01 4.2194514e-01 3.2625778e-01 4.6147754e-01 3.4920516e-01 4.4979521e-01 4.0734174e-01 4.0275102e-01 3.7733353e-01 3.1756791e-01 3.4852048e-01 4.1080565e-01 3.2443308e-01 4.3219633e-01 4.2252463e-01 4.0479526e-01 3.6286403e-01 4.0364435e-01 3.5428112e-01 3.7151258e-01 3.7191203e-01 2.0478784e-03 4.7860280e-03 7.2727783e-03 1.2329906e-03 2.0550268e-03 7.3066158e-03 5.3810576e-04 3.2245377e-03 2.1674888e-03 2.7856851e-03 1.6387773e-03 3.1323423e-03 6.7307381e-05 8.3332066e-03 3.3954200e-04 4.9119026e-03 7.6276175e-03 8.9240504e-03 1.3851706e-02 2.8347122e-03 2.2069601e-03 3.5278340e-03 4.3892066e-03 7.6179982e-03 7.9398397e-03 2.0003115e-03 1.2885024e-03 7.2727783e-03 5.1809110e-03 5.4327573e-03 7.2727783e-03 1.7938257e-03 2.8924302e-03 1.4132511e-03 3.5877316e-02 5.5425651e-05 2.1070391e-03 2.2246722e-03 8.2675293e-03 4.8309816e-04 1.2152730e-03 6.6498554e-04 3.1323983e-03 2.3387083e-01 2.3171300e-01 2.7340823e-01 3.1873143e-01 2.9087099e-01 3.0765106e-01 2.5178166e-01 2.1138210e-01 2.6568600e-01 2.6244481e-01 3.0032621e-01 2.3618270e-01 2.9221666e-01 3.0443867e-01 1.7368514e-01 2.2112593e-01 2.9589691e-01 2.4771596e-01 3.7467992e-01 2.5965416e-01 3.1023215e-01 2.2429067e-01 3.7775475e-01 3.0780450e-01 2.3805322e-01 2.3537447e-01 2.9708156e-01 3.1499475e-01 2.8689460e-01 1.9071775e-01 2.6437980e-01 2.4650321e-01 2.2965596e-01 4.0666307e-01 3.1024566e-01 2.2426781e-01 2.5770992e-01 3.3024819e-01 2.2703835e-01 2.8812097e-01 3.2789845e-01 2.7792695e-01 2.5584753e-01 2.2352457e-01 2.8285984e-01 2.3411709e-01 2.5033634e-01 2.4414228e-01 1.5718330e-01 2.4987685e-01 5.0772777e-01 4.4916856e-01 4.2715006e-01 4.4114868e-01 4.7061118e-01 4.7223859e-01 4.5731921e-01 4.5076024e-01 4.8335950e-01 3.9759816e-01 3.2864932e-01 4.2581769e-01 3.9765561e-01 4.8465345e-01 4.8525127e-01 3.8513607e-01 3.9820652e-01 3.9712803e-01 5.5326906e-01 4.5284577e-01 4.0466523e-01 4.2968979e-01 4.8967945e-01 3.7122633e-01 3.9189300e-01 3.8976354e-01 3.4937792e-01 3.4115590e-01 4.7020033e-01 3.7767688e-01 4.3942138e-01 3.4125876e-01 4.7934102e-01 3.6486747e-01 4.6609349e-01 4.2514437e-01 4.1889348e-01 3.9297481e-01 3.3279384e-01 3.6502235e-01 4.2824304e-01 3.4111505e-01 4.4916856e-01 4.3953404e-01 4.2185806e-01 3.8004789e-01 4.2135185e-01 3.7064704e-01 3.8713375e-01 3.8737322e-01 5.9378937e-04 1.6263483e-03 3.1194349e-04 8.5089275e-04 1.6365846e-03 1.1579874e-03 4.9430863e-03 7.7957878e-03 5.8209267e-03 9.7423596e-04 2.1559031e-04 2.8280232e-03 2.1261057e-03 1.8496545e-03 1.2342594e-02 1.9347552e-03 5.4995961e-03 5.2624400e-03 1.3773080e-04 7.1496401e-05 7.1145768e-04 9.6706058e-04 1.9028496e-03 3.0842001e-03 7.5087003e-03 6.3709632e-03 1.6263483e-03 2.4219636e-03 2.5416684e-03 1.6263483e-03 4.5881830e-05 1.0508341e-04 2.2101780e-03 2.1711060e-02 1.4779987e-03 1.0004664e-03 2.4029906e-03 2.5527616e-03 2.4859397e-03 1.2918144e-04 4.6388898e-04 3.7292268e-04 1.9674800e-01 1.9551090e-01 2.3384591e-01 2.7581575e-01 2.4956432e-01 2.6852776e-01 2.1529223e-01 1.7652116e-01 2.2659936e-01 2.2483764e-01 2.5838801e-01 1.9958250e-01 2.5031938e-01 2.6459593e-01 1.4127677e-01 1.8459488e-01 2.5809699e-01 2.1110498e-01 3.2773890e-01 2.2109976e-01 2.7105695e-01 1.8736680e-01 3.3227725e-01 2.6813261e-01 2.0050552e-01 1.9777445e-01 2.5552383e-01 2.7284453e-01 2.4733170e-01 1.5638593e-01 2.2514733e-01 2.0849921e-01 1.9287136e-01 3.6191982e-01 2.7281863e-01 1.9071038e-01 2.1912633e-01 2.8593968e-01 1.9281596e-01 2.4768186e-01 2.8763479e-01 2.3971138e-01 2.1723753e-01 1.8699948e-01 2.4393940e-01 1.9979806e-01 2.1397418e-01 2.0672587e-01 1.2529166e-01 2.1270878e-01 4.6033946e-01 4.0222602e-01 3.7977808e-01 3.9565939e-01 4.2276626e-01 4.2367223e-01 4.1181849e-01 4.0362790e-01 4.3403229e-01 3.5248619e-01 2.8628855e-01 3.7840466e-01 3.5121985e-01 4.3508597e-01 4.3518504e-01 3.3982310e-01 3.5380487e-01 3.5403534e-01 5.0086936e-01 4.0431760e-01 3.5820087e-01 3.8360776e-01 4.4020334e-01 3.2567463e-01 3.4780712e-01 3.4566354e-01 3.0520609e-01 2.9886193e-01 4.2163480e-01 3.3351442e-01 3.9135037e-01 2.9988740e-01 4.3006423e-01 3.2170490e-01 4.2068520e-01 3.7643926e-01 3.7416586e-01 3.4964963e-01 2.9095264e-01 3.1987117e-01 3.8035231e-01 2.9582266e-01 4.0222602e-01 3.9256940e-01 3.7489846e-01 3.3318735e-01 3.7289799e-01 3.2576051e-01 3.4389968e-01 3.4452790e-01 2.6022528e-04 1.6357171e-03 1.5776794e-03 3.8821876e-04 3.3383101e-03 8.1348232e-03 1.2673676e-02 9.6454978e-03 2.5951087e-03 4.7710550e-04 5.9640558e-03 5.0935416e-04 4.5005321e-03 1.8295131e-02 8.0881241e-04 4.5548809e-03 2.4342161e-03 4.9437559e-04 7.1285200e-04 1.1827453e-03 5.3640347e-04 3.9274104e-04 2.7126384e-03 1.1795471e-02 1.0834355e-02 2.6022528e-04 3.1798475e-03 3.2407554e-03 2.6022528e-04 8.6271302e-04 3.7982619e-04 4.6816553e-03 1.6563494e-02 3.8583498e-03 2.4190920e-03 3.6876972e-03 1.5540748e-03 4.9548680e-03 1.1822578e-03 2.1020911e-03 7.8943086e-04 1.7690932e-01 1.7592254e-01 2.1244374e-01 2.5265403e-01 2.2738778e-01 2.4651370e-01 1.9514374e-01 1.5780110e-01 2.0549282e-01 2.0415245e-01 2.3585798e-01 1.7978451e-01 2.2802648e-01 2.4243736e-01 1.2428265e-01 1.6526016e-01 2.3669421e-01 1.9101658e-01 3.0265561e-01 2.0025949e-01 2.4898144e-01 1.6786898e-01 3.0733331e-01 2.4595698e-01 1.8046568e-01 1.7781045e-01 2.3314059e-01 2.4991164e-01 2.2560913e-01 1.3845901e-01 2.0404813e-01 1.8812805e-01 1.7322140e-01 3.3666075e-01 2.5129679e-01 1.7201674e-01 1.9833201e-01 2.6229128e-01 1.7386253e-01 2.2573363e-01 2.6492814e-01 2.1852944e-01 1.9648937e-01 1.6758641e-01 2.2246603e-01 1.8066071e-01 1.9389274e-01 1.8653629e-01 1.0911288e-01 1.9242842e-01 4.3299781e-01 3.7574545e-01 3.5353088e-01 3.6969868e-01 3.9574789e-01 3.9644649e-01 3.8564737e-01 3.7707427e-01 4.0646475e-01 3.2727058e-01 2.6301141e-01 3.5217112e-01 3.2569733e-01 4.0744443e-01 4.0742674e-01 3.1477415e-01 3.2876954e-01 3.2939944e-01 4.7166141e-01 3.7739415e-01 3.3254323e-01 3.5763917e-01 4.1251084e-01 3.0085067e-01 3.2295735e-01 3.2084322e-01 2.8110727e-01 2.7535557e-01 3.9443856e-01 3.0887601e-01 3.6474538e-01 2.7663010e-01 4.0256676e-01 2.9754793e-01 3.9443683e-01 3.4998590e-01 3.4873324e-01 3.2500314e-01 2.6771990e-01 2.9525187e-01 3.5397733e-01 2.7178914e-01 3.7574545e-01 3.6622316e-01 3.4883278e-01 3.0797294e-01 3.4655783e-01 3.0107914e-01 3.1936679e-01 3.2010756e-01 3.0868881e-03 2.8691382e-03 1.3643967e-04 5.3429196e-03 1.0581034e-02 1.6459895e-02 1.2551534e-02 4.1576220e-03 1.2160579e-03 8.7064401e-03 5.4418849e-05 6.8214146e-03 2.2732626e-02 5.6586090e-04 4.9831593e-03 1.1314391e-03 1.3090644e-03 1.7262411e-03 1.9770194e-03 1.0389613e-03 9.3641634e-05 2.8067647e-03 1.5471006e-02 1.4411876e-02 0.0000000e+00 4.0251529e-03 4.0402030e-03 0.0000000e+00 2.0104773e-03 1.1579294e-03 6.7733512e-03 1.3328703e-02 6.1195429e-03 3.8280621e-03 5.4471697e-03 1.3203495e-03 7.3930866e-03 2.5511055e-03 3.8011014e-03 1.5935161e-03 1.6488937e-01 1.6420208e-01 1.9946134e-01 2.3841307e-01 2.1377841e-01 2.3352484e-01 1.8323739e-01 1.4660873e-01 1.9269692e-01 1.9183992e-01 2.2200730e-01 1.6791595e-01 2.1423078e-01 2.2922867e-01 1.1407468e-01 1.5349471e-01 2.2418179e-01 1.7908948e-01 2.8692621e-01 1.8765985e-01 2.3596576e-01 1.5596498e-01 2.9203641e-01 2.3278981e-01 1.6829150e-01 1.6563525e-01 2.1942293e-01 2.3592536e-01 2.1256421e-01 1.2755291e-01 1.9121342e-01 1.7576718e-01 1.6132929e-01 3.2148865e-01 2.3885357e-01 1.6118125e-01 1.8573297e-01 2.4757051e-01 1.6279862e-01 2.1240660e-01 2.5149161e-01 2.0595435e-01 1.8389163e-01 1.5580881e-01 2.0964365e-01 1.6953436e-01 1.8203376e-01 1.7437090e-01 9.9184065e-02 1.8031354e-01 4.1664204e-01 3.5971944e-01 3.3744612e-01 3.5416868e-01 3.7936058e-01 3.7982315e-01 3.7006184e-01 3.6098196e-01 3.8956200e-01 3.1201251e-01 2.4889912e-01 3.3607844e-01 3.1002022e-01 3.9046127e-01 3.9028467e-01 2.9949936e-01 3.1373736e-01 3.1479561e-01 4.5355800e-01 3.6085089e-01 3.1682959e-01 3.4195608e-01 3.9553949e-01 2.8555816e-01 3.0804941e-01 3.0593834e-01 2.6633772e-01 2.6121338e-01 3.7782244e-01 2.9399549e-01 3.4839508e-01 2.6278429e-01 3.8569374e-01 2.8303600e-01 3.7885421e-01 3.3349568e-01 3.3352424e-01 3.1033775e-01 2.5375578e-01 2.8011018e-01 3.3772574e-01 2.5671985e-01 3.5971944e-01 3.5022311e-01 3.3289784e-01 2.9224130e-01 3.3016010e-01 2.8599652e-01 3.0475125e-01 3.0561759e-01 1.6232219e-03 2.8110674e-03 3.0992704e-04 2.8009412e-03 5.3859157e-03 3.4428307e-03 2.2389965e-04 4.8865483e-04 1.7600776e-03 3.6417064e-03 7.4576509e-04 9.1296419e-03 2.8123816e-03 8.0068765e-03 7.3379880e-03 3.9579356e-04 1.9713653e-04 6.0194434e-04 2.3367622e-03 3.6239908e-03 3.0185118e-03 6.3202257e-03 4.4046298e-03 3.0868881e-03 1.7141363e-03 1.8461990e-03 3.0868881e-03 1.2729358e-04 4.6571753e-04 8.6393185e-04 2.3903803e-02 9.0441478e-04 3.0528309e-04 3.1648190e-03 3.1223857e-03 2.2339929e-03 2.7724170e-04 9.5102356e-05 4.3913729e-04 2.1061066e-01 2.0964712e-01 2.4888926e-01 2.9163776e-01 2.6471732e-01 2.8522189e-01 2.3035873e-01 1.8998369e-01 2.4143327e-01 2.4006731e-01 2.7373417e-01 2.1381600e-01 2.6519773e-01 2.8097864e-01 1.5319882e-01 1.9790190e-01 2.7464921e-01 2.2594124e-01 3.4406181e-01 2.3583526e-01 2.8783351e-01 2.0067586e-01 3.4959337e-01 2.8469567e-01 2.1442337e-01 2.1148410e-01 2.7089383e-01 2.8885413e-01 2.6304757e-01 1.6861411e-01 2.3983808e-01 2.2272157e-01 2.0662897e-01 3.8050402e-01 2.8992390e-01 2.0524163e-01 2.3373909e-01 3.0156183e-01 2.0732304e-01 2.6311208e-01 3.0478863e-01 2.5545595e-01 2.3172924e-01 2.0047959e-01 2.5968741e-01 2.1460680e-01 2.2900957e-01 2.2107714e-01 1.3590375e-01 2.2746759e-01 4.8087096e-01 4.2142796e-01 3.9814594e-01 4.1502871e-01 4.4228995e-01 4.4300705e-01 4.3159396e-01 4.2281768e-01 4.5341178e-01 3.7067256e-01 3.0283474e-01 3.9670954e-01 3.6890449e-01 4.5441105e-01 4.5432159e-01 3.5749751e-01 3.7222314e-01 3.7273757e-01 5.2092654e-01 4.2307513e-01 3.7613897e-01 4.0250144e-01 4.5970761e-01 3.4267709e-01 3.6611458e-01 3.6389952e-01 3.2189682e-01 3.1593975e-01 4.4091119e-01 3.5132753e-01 4.0985044e-01 3.1723537e-01 4.4934566e-01 3.3938050e-01 4.4068685e-01 3.9407776e-01 3.9311145e-01 3.6818097e-01 3.0785214e-01 3.3679573e-01 3.9853670e-01 3.1138703e-01 4.2142796e-01 4.1148317e-01 3.9324794e-01 3.4985868e-01 3.9052142e-01 3.4304315e-01 3.6227810e-01 3.6300235e-01 3.5297445e-03 2.3993465e-03 8.1469449e-03 8.4116551e-03 8.4748907e-03 3.0443320e-03 1.8587915e-03 2.8140158e-03 3.7033592e-03 2.9317197e-03 1.3265454e-02 4.5015236e-03 2.6265400e-03 7.5415562e-03 1.6353241e-03 1.4046892e-03 3.1172532e-03 6.0159720e-04 2.6265400e-03 7.0517547e-03 5.5270478e-03 6.4518235e-03 2.8691382e-03 6.1014438e-03 6.3013427e-03 2.8691382e-03 1.1615614e-03 1.4498289e-03 4.4069463e-03 2.8268404e-02 1.4610903e-03 3.3082950e-03 4.5136771e-04 5.8170191e-03 1.2876475e-03 5.5964987e-04 1.3152056e-03 2.3446691e-03 1.9624436e-01 1.9365486e-01 2.3272539e-01 2.7570910e-01 2.4962218e-01 2.6341222e-01 2.1161968e-01 1.7505868e-01 2.2555325e-01 2.2171510e-01 2.5853430e-01 1.9783603e-01 2.5146360e-01 2.6075203e-01 1.4121466e-01 1.8483453e-01 2.5221719e-01 2.0803875e-01 3.2980663e-01 2.1983733e-01 2.6580206e-01 1.8792174e-01 3.3096405e-01 2.6375229e-01 2.0022607e-01 1.9799659e-01 2.5530781e-01 2.7170116e-01 2.4472945e-01 1.5721426e-01 2.2453551e-01 2.0791989e-01 1.9232403e-01 3.5721533e-01 2.6541719e-01 1.8583170e-01 2.1815822e-01 2.8740780e-01 1.8853156e-01 2.4642239e-01 2.8243457e-01 2.3594931e-01 2.1655773e-01 1.8685267e-01 2.4074340e-01 1.9492750e-01 2.1026662e-01 2.0538411e-01 1.2769778e-01 2.1026662e-01 4.5350142e-01 3.9795187e-01 3.7769065e-01 3.8983341e-01 4.1851849e-01 4.2042793e-01 4.0510768e-01 3.9953508e-01 4.3130939e-01 3.4895812e-01 2.8413412e-01 3.7648942e-01 3.4987508e-01 4.3267862e-01 4.3359924e-01 3.3758650e-01 3.4918773e-01 3.4772733e-01 4.9915788e-01 4.0231358e-01 3.5632097e-01 3.7931174e-01 4.3732697e-01 3.2511301e-01 3.4317696e-01 3.4120231e-01 3.0420772e-01 2.9548175e-01 4.1851849e-01 3.3003548e-01 3.8954108e-01 2.9516106e-01 4.2751620e-01 3.1771485e-01 4.1340131e-01 3.7704473e-01 3.6865281e-01 3.4390717e-01 2.8759676e-01 3.1915503e-01 3.7909158e-01 2.9821954e-01 3.9795187e-01 3.8894782e-01 3.7251565e-01 3.3442155e-01 3.7333196e-01 3.2403985e-01 3.3841983e-01 3.3852014e-01 4.9713816e-03 9.2903476e-03 1.5944722e-02 1.1386125e-02 3.5402300e-03 9.6201776e-04 8.6911918e-03 9.4953207e-05 6.4447745e-03 2.1940505e-02 1.4660203e-04 6.6774582e-03 1.0681613e-03 1.0987220e-03 1.5397922e-03 1.3925202e-03 1.6744918e-03 4.5493239e-04 1.7274513e-03 1.6063124e-02 1.4167352e-02 1.3643967e-04 2.8974888e-03 2.8893579e-03 1.3643967e-04 1.8844794e-03 1.0195101e-03 5.9835494e-03 1.1907169e-02 6.2144210e-03 3.1461009e-03 6.4603159e-03 6.0804116e-04 7.9398115e-03 2.6608779e-03 3.6611489e-03 1.1878605e-03 1.6827914e-01 1.6808878e-01 2.0330224e-01 2.4208927e-01 2.1725982e-01 2.3901101e-01 1.8791090e-01 1.5022413e-01 1.9646923e-01 1.9636982e-01 2.2550082e-01 1.7178784e-01 2.1730098e-01 2.3423675e-01 1.1690649e-01 1.5652486e-01 2.2988556e-01 1.8351752e-01 2.8999988e-01 1.9148148e-01 2.4151451e-01 1.5889324e-01 2.9641885e-01 2.3800995e-01 1.7162027e-01 1.6875690e-01 2.2303923e-01 2.3997805e-01 2.1702847e-01 1.3016155e-01 1.9481438e-01 1.7925806e-01 1.6471086e-01 3.2723835e-01 2.4516700e-01 1.6613136e-01 1.8943302e-01 2.5069233e-01 1.6755119e-01 2.1637456e-01 2.5710224e-01 2.1080083e-01 1.8747239e-01 1.5900127e-01 2.1430731e-01 1.7454101e-01 1.8671199e-01 1.7813660e-01 1.0093430e-01 1.8452279e-01 4.2348788e-01 3.6545764e-01 3.4229982e-01 3.6044592e-01 3.8515670e-01 3.8525367e-01 3.7671058e-01 3.6665866e-01 3.9483186e-01 3.1729700e-01 2.5339335e-01 3.4086300e-01 3.1448994e-01 3.9561710e-01 3.9513385e-01 3.0425707e-01 3.1942407e-01 3.2109044e-01 4.5863634e-01 3.6575820e-01 3.2152631e-01 3.4763745e-01 4.0088503e-01 2.8963041e-01 3.1371721e-01 3.1153634e-01 2.7048666e-01 2.6621754e-01 3.8319922e-01 2.9918600e-01 3.5318564e-01 2.6828212e-01 3.9088644e-01 2.8836369e-01 3.8573578e-01 3.3732047e-01 3.3961180e-01 3.1641339e-01 2.5871452e-01 2.8421639e-01 3.4227214e-01 2.5952711e-01 3.6545764e-01 3.5568934e-01 3.3784386e-01 2.9565951e-01 3.3403769e-01 2.9050448e-01 3.1070970e-01 3.1176741e-01 1.7225353e-03 3.1252650e-03 1.9141697e-03 3.0572974e-04 1.5621195e-03 7.7657730e-04 6.0730841e-03 9.6969946e-05 6.0804497e-03 4.8728559e-03 1.0019276e-02 1.0630759e-02 1.4028596e-03 1.0008907e-03 1.5106647e-03 3.9425625e-03 5.9922357e-03 4.5089760e-03 4.5296071e-03 2.4544132e-03 5.3429196e-03 2.3927105e-03 2.5658780e-03 5.3429196e-03 8.0759380e-04 1.5345623e-03 3.2653860e-04 2.8784931e-02 4.6959359e-04 5.2961514e-04 3.5529133e-03 5.0787251e-03 1.7528094e-03 7.9750642e-04 1.8469304e-04 1.3949343e-03 2.2601194e-01 2.2489065e-01 2.6538416e-01 3.0933559e-01 2.8173337e-01 3.0217927e-01 2.4601138e-01 2.0461695e-01 2.5772236e-01 2.5608721e-01 2.9099324e-01 2.2920421e-01 2.8227951e-01 2.9802519e-01 1.6660589e-01 2.1294215e-01 2.9118999e-01 2.4154604e-01 3.6304829e-01 2.5194222e-01 3.0483437e-01 2.1582549e-01 3.6852765e-01 3.0175865e-01 2.2996178e-01 2.2696306e-01 2.8805620e-01 3.0640689e-01 2.7978083e-01 1.8266242e-01 2.5611673e-01 2.3849427e-01 2.2189938e-01 3.9968280e-01 3.0655638e-01 2.1989254e-01 2.4981061e-01 3.1957344e-01 2.2214967e-01 2.7998655e-01 3.2222399e-01 2.7182647e-01 2.4776514e-01 2.1557967e-01 2.7625427e-01 2.2956697e-01 2.4461597e-01 2.3673300e-01 1.4870158e-01 2.4319918e-01 5.0146439e-01 4.4143183e-01 4.1797463e-01 4.3468983e-01 4.6265612e-01 4.6350594e-01 4.5140079e-01 4.4286970e-01 4.7413628e-01 3.8981309e-01 3.2063561e-01 4.1652733e-01 3.8823357e-01 4.7518267e-01 4.7516433e-01 3.7651294e-01 3.9124904e-01 3.9150135e-01 5.4273573e-01 4.4336023e-01 3.9556546e-01 4.2215915e-01 4.8051706e-01 3.6152094e-01 3.8501414e-01 3.8277781e-01 3.4024930e-01 3.3391004e-01 4.6138930e-01 3.7007400e-01 4.2991865e-01 3.3504580e-01 4.7002175e-01 3.5780202e-01 4.6054739e-01 4.1401655e-01 4.1241306e-01 3.8694896e-01 3.2563422e-01 3.5550143e-01 4.1844379e-01 3.2964964e-01 4.4143183e-01 4.3139176e-01 4.1295631e-01 3.6894646e-01 4.1038562e-01 3.6180237e-01 3.8096707e-01 3.8161756e-01 2.9059655e-03 2.3706161e-04 1.5622923e-03 4.6985391e-03 3.0899699e-03 1.1144685e-02 1.5452086e-03 4.3912180e-03 8.2378168e-03 1.9894938e-02 1.6022015e-02 4.6248887e-03 4.1434850e-03 3.4955495e-03 1.0239539e-02 1.2012056e-02 5.3911313e-03 8.1025404e-03 3.3141536e-03 1.0581034e-02 2.5128050e-03 2.6447621e-03 1.0581034e-02 4.0444636e-03 5.0265914e-03 5.7591609e-04 3.1111838e-02 3.5439088e-03 1.6935296e-03 1.0096079e-02 7.4536930e-03 6.1909555e-03 4.6446420e-03 2.9301624e-03 3.9714148e-03 2.5068010e-01 2.5098525e-01 2.9216380e-01 3.3638336e-01 3.0774280e-01 3.3430586e-01 2.7479523e-01 2.2946729e-01 2.8415608e-01 2.8467354e-01 3.1719659e-01 2.5534962e-01 3.0706036e-01 3.2874800e-01 1.8820706e-01 2.3625921e-01 3.2370915e-01 2.6954509e-01 3.8903955e-01 2.7840834e-01 3.3718014e-01 2.3890051e-01 3.9852630e-01 3.3314087e-01 2.5453269e-01 2.5085481e-01 3.1457631e-01 3.3452242e-01 3.0863300e-01 2.0400912e-01 2.8201538e-01 2.6372052e-01 2.4645516e-01 4.3401653e-01 3.4099078e-01 2.4885351e-01 2.7587880e-01 3.4510687e-01 2.5062404e-01 3.0740449e-01 3.5503881e-01 3.0161400e-01 2.7344211e-01 2.3943815e-01 3.0560987e-01 2.5890901e-01 2.7338361e-01 2.6272911e-01 1.6653571e-01 2.7061027e-01 5.3996294e-01 4.7622847e-01 4.4996413e-01 4.7092027e-01 4.9784258e-01 4.9765553e-01 4.8884384e-01 4.7750758e-01 5.0792579e-01 4.2270376e-01 3.5026703e-01 4.4829833e-01 4.1874266e-01 5.0865069e-01 5.0773433e-01 4.0771387e-01 4.2529697e-01 4.2723724e-01 5.7654330e-01 4.7578493e-01 4.2683086e-01 4.5657562e-01 5.1458642e-01 3.9050926e-01 4.1892650e-01 4.1646419e-01 3.6916765e-01 3.6521270e-01 4.9536279e-01 4.0243409e-01 4.6185742e-01 3.6775616e-01 5.0354755e-01 3.9037876e-01 4.9872590e-01 4.4290716e-01 4.4784995e-01 4.2202156e-01 3.5667822e-01 3.8450868e-01 4.4953729e-01 3.5436611e-01 4.7622847e-01 4.6530251e-01 4.4515647e-01 3.9606646e-01 4.3939461e-01 3.9207838e-01 4.1563418e-01 4.1682072e-01 1.5609953e-03 4.8490070e-03 9.0958370e-03 1.4989091e-03 1.7813868e-02 2.1261488e-03 5.5450301e-04 1.5694148e-02 1.9384410e-02 2.5209737e-02 8.6954304e-03 7.6213095e-03 8.7105486e-03 1.2654300e-02 1.7334342e-02 1.3883194e-02 2.2522789e-03 1.7443565e-04 1.6459895e-02 9.1718824e-03 9.4916987e-03 1.6459895e-02 6.9918464e-03 8.9833757e-03 2.9662983e-03 4.9266036e-02 2.9055422e-03 5.5731976e-03 8.0889759e-03 1.5739049e-02 3.7597322e-03 6.3200695e-03 4.4644236e-03 8.6395939e-03 2.7692079e-01 2.7499135e-01 3.1941212e-01 3.6719225e-01 3.3760508e-01 3.5656667e-01 2.9687968e-01 2.5298236e-01 3.1115442e-01 3.0819394e-01 3.4760947e-01 2.7976620e-01 3.3861131e-01 3.5300749e-01 2.1161836e-01 2.6294922e-01 3.4413759e-01 2.9242527e-01 4.2523864e-01 3.0477646e-01 3.5931443e-01 2.6623770e-01 4.2973462e-01 3.5665121e-01 2.8133678e-01 2.7828246e-01 3.4429599e-01 3.6357656e-01 3.3414792e-01 2.2982660e-01 3.0962503e-01 2.9049749e-01 2.7240490e-01 4.6073277e-01 3.5937207e-01 2.6744708e-01 3.0261187e-01 3.7874332e-01 2.7038484e-01 3.3511133e-01 3.7800754e-01 3.2481692e-01 3.0053112e-01 2.6567213e-01 3.2997477e-01 2.7805229e-01 2.9533686e-01 2.8819780e-01 1.9246360e-01 2.9461561e-01 5.6604002e-01 5.0500114e-01 4.8160322e-01 4.9684759e-01 5.2728223e-01 5.2878056e-01 5.1374073e-01 5.0662675e-01 5.4019822e-01 4.5105935e-01 3.7828674e-01 4.8016813e-01 4.5059090e-01 5.4146236e-01 5.4186046e-01 4.3772456e-01 4.5187440e-01 4.5090747e-01 6.1216296e-01 5.0833718e-01 4.5807252e-01 4.8471571e-01 5.4678369e-01 4.2264992e-01 4.4526564e-01 4.4301133e-01 3.9982165e-01 3.9174986e-01 5.2663733e-01 4.3017652e-01 4.9431552e-01 3.9206379e-01 5.3598953e-01 4.1681568e-01 5.2288475e-01 4.7864082e-01 4.7360696e-01 4.4651927e-01 3.8292357e-01 4.1618542e-01 4.8251093e-01 3.8978460e-01 5.0500114e-01 4.9485605e-01 4.7615846e-01 4.3123359e-01 4.7475023e-01 4.2239197e-01 4.4037545e-01 4.4066833e-01 2.2696323e-03 5.9674345e-03 2.4448133e-03 1.3335241e-02 1.4550127e-03 2.5899751e-03 1.0462849e-02 2.0483763e-02 1.9016265e-02 5.8048658e-03 5.1341121e-03 4.8749246e-03 1.1285550e-02 1.3907377e-02 7.6337585e-03 6.3180508e-03 2.0267734e-03 1.2551534e-02 4.1078635e-03 4.2919330e-03 1.2551534e-02 4.8854397e-03 6.2043549e-03 8.9134425e-04 3.6466805e-02 3.2782414e-03 2.5696799e-03 9.8307089e-03 9.8120264e-03 5.5450409e-03 5.1728098e-03 3.2838118e-03 5.2555481e-03 2.6123905e-01 2.6103645e-01 3.0329747e-01 3.4864780e-01 3.1958423e-01 3.4458162e-01 2.8459400e-01 2.3921225e-01 2.9516933e-01 2.9488851e-01 3.2923215e-01 2.6553322e-01 3.1926739e-01 3.3946038e-01 1.9751618e-01 2.4678743e-01 3.3347141e-01 2.7948950e-01 4.0283567e-01 2.8923185e-01 3.4744486e-01 2.4959732e-01 4.1128993e-01 3.4370645e-01 2.6525258e-01 2.6168140e-01 3.2643822e-01 3.4638099e-01 3.1949478e-01 2.1402441e-01 2.9314941e-01 2.7451414e-01 2.5691069e-01 4.4594923e-01 3.5036767e-01 2.5760360e-01 2.8676453e-01 3.5805235e-01 2.5967094e-01 3.1875975e-01 3.6562012e-01 3.1188296e-01 2.8438818e-01 2.4989387e-01 3.1618152e-01 2.6787548e-01 2.8314006e-01 2.7321717e-01 1.7615044e-01 2.8082637e-01 5.5224352e-01 4.8885819e-01 4.6311762e-01 4.8285549e-01 5.1073035e-01 5.1093091e-01 5.0061869e-01 4.9022404e-01 5.2151073e-01 4.3495694e-01 3.6199911e-01 4.6149506e-01 4.3176986e-01 5.2236122e-01 5.2173621e-01 4.2026138e-01 4.3714978e-01 4.3841019e-01 5.9117531e-01 4.8927834e-01 4.3976762e-01 4.6895974e-01 5.2818477e-01 4.0343191e-01 4.3068816e-01 4.2826096e-01 3.8162190e-01 3.7670056e-01 5.0866155e-01 4.1442940e-01 4.7525840e-01 3.7873655e-01 5.1715063e-01 4.0199874e-01 5.1036877e-01 4.5693051e-01 4.5962906e-01 4.3336423e-01 3.6804311e-01 3.9729031e-01 4.6298867e-01 3.6775880e-01 4.8885819e-01 4.7805886e-01 4.5813974e-01 4.0968699e-01 4.5331588e-01 4.0460089e-01 4.2699969e-01 4.2797954e-01 8.7894099e-04 2.0541035e-03 4.6305984e-03 6.7282118e-04 8.1166178e-03 3.1872717e-03 1.0880321e-02 8.3605717e-03 8.2577841e-04 6.1811314e-04 5.6936180e-04 3.8806955e-03 4.9821714e-03 2.5043588e-03 7.1844876e-03 4.2468498e-03 4.1576220e-03 9.9353221e-04 1.1055786e-03 4.1576220e-03 5.9733473e-04 9.9247040e-04 3.2081104e-04 2.3598005e-02 1.4307976e-03 3.0934240e-05 4.9322953e-03 3.1299098e-03 3.2877683e-03 9.9769768e-04 4.3968696e-04 6.2770216e-04 2.1786575e-01 2.1746374e-01 2.5686278e-01 2.9961632e-01 2.7235218e-01 2.9521657e-01 2.3917491e-01 1.9737534e-01 2.4929109e-01 2.4877587e-01 2.8142376e-01 2.2163964e-01 2.7235613e-01 2.9042411e-01 1.5945203e-01 2.0466964e-01 2.8483074e-01 2.3445929e-01 3.5153649e-01 2.4372083e-01 2.9790924e-01 2.0734139e-01 3.5860995e-01 2.9439415e-01 2.2162942e-01 2.1843466e-01 2.7871280e-01 2.9725443e-01 2.7179832e-01 1.7471289e-01 2.4749127e-01 2.3015922e-01 2.1385216e-01 3.9117409e-01 3.0083879e-01 2.1421404e-01 2.4147478e-01 3.0893498e-01 2.1609222e-01 2.7129944e-01 3.1500957e-01 2.6459749e-01 2.3931769e-01 2.0744985e-01 2.6864595e-01 2.2369874e-01 2.3782351e-01 2.2882042e-01 1.4076875e-01 2.3574843e-01 4.9305396e-01 4.3221625e-01 4.0786305e-01 4.2639713e-01 4.5320351e-01 4.5351054e-01 4.4342223e-01 4.3354113e-01 4.6376094e-01 3.8078797e-01 3.1179824e-01 4.0634323e-01 3.7808795e-01 4.6463298e-01 4.6419251e-01 3.6697079e-01 3.8279432e-01 3.8398235e-01 5.3121485e-01 4.3292773e-01 3.8560639e-01 4.1316779e-01 4.7015919e-01 3.5131205e-01 3.7664303e-01 3.7434225e-01 3.3054253e-01 3.2553357e-01 4.5134808e-01 3.6126826e-01 4.1953095e-01 3.2738535e-01 4.5959627e-01 3.4943014e-01 4.5279702e-01 4.0259529e-01 4.0419917e-01 3.7916847e-01 3.1736132e-01 3.4544845e-01 4.0790313e-01 3.1842804e-01 4.3221625e-01 4.2193584e-01 4.0305492e-01 3.5775833e-01 3.9908964e-01 3.5217975e-01 3.7311479e-01 3.7405234e-01 4.0128787e-03 1.5082918e-03 2.4327589e-03 1.3749582e-02 9.8644164e-04 7.0275865e-03 4.0509182e-03 9.2137987e-06 8.1954627e-05 2.0221322e-04 1.4919720e-03 1.6918938e-03 1.6810350e-03 9.8366461e-03 7.8161988e-03 1.2160579e-03 1.4072237e-03 1.4774681e-03 1.2160579e-03 1.9402564e-04 2.6249834e-05 2.2527118e-03 1.8043780e-02 2.4914194e-03 7.5228825e-04 4.0311549e-03 1.3209671e-03 4.0483306e-03 6.3837247e-04 9.1866746e-04 4.5668021e-05 1.9279238e-01 1.9221899e-01 2.2979083e-01 2.7088361e-01 2.4479658e-01 2.6610561e-01 2.1267629e-01 1.7326091e-01 2.2258210e-01 2.2184784e-01 2.5349552e-01 1.9619155e-01 2.4503806e-01 2.6157291e-01 1.3780150e-01 1.8042756e-01 2.5616217e-01 2.0823320e-01 3.2136818e-01 2.1724300e-01 2.6868196e-01 1.8301959e-01 3.2745065e-01 2.6534429e-01 1.9640162e-01 1.9346456e-01 2.5082861e-01 2.6843057e-01 2.4386055e-01 1.5229091e-01 2.2093793e-01 2.0444992e-01 1.8898039e-01 3.5852616e-01 2.7153123e-01 1.8897970e-01 2.1514921e-01 2.8018592e-01 1.9075855e-01 2.4355737e-01 2.8508289e-01 2.3688721e-01 2.1314274e-01 1.8298617e-01 2.4079289e-01 1.9795777e-01 2.1138990e-01 2.0306053e-01 1.2090533e-01 2.0951271e-01 4.5737829e-01 3.9834085e-01 3.7497739e-01 3.9259152e-01 4.1873643e-01 4.1914498e-01 4.0909121e-01 3.9964410e-01 4.2918834e-01 3.4857995e-01 2.8224226e-01 3.7353062e-01 3.4626594e-01 4.3008323e-01 4.2978752e-01 3.3538250e-01 3.5042715e-01 3.5150644e-01 4.9516533e-01 3.9931312e-01 3.5345241e-01 3.7985840e-01 4.3539662e-01 3.2054198e-01 3.4448056e-01 3.4226906e-01 3.0044482e-01 2.9530625e-01 4.1705793e-01 3.2972732e-01 3.8633770e-01 2.9699130e-01 4.2515986e-01 3.1826259e-01 4.1819279e-01 3.7038673e-01 3.7108888e-01 3.4686702e-01 2.8745249e-01 3.1485734e-01 3.7515178e-01 2.8956004e-01 3.9834085e-01 3.8842721e-01 3.7027395e-01 3.2715736e-01 3.6694987e-01 3.2117964e-01 3.4102816e-01 3.4191835e-01 9.8460504e-03 4.2097646e-04 3.8720695e-03 8.9609139e-03 1.0271114e-02 1.5759765e-02 3.6853519e-03 2.9640455e-03 4.3261623e-03 5.5313131e-03 9.1074119e-03 8.9974672e-03 1.5687499e-03 7.6879483e-04 8.7064401e-03 5.8625582e-03 6.1329529e-03 8.7064401e-03 2.4949105e-03 3.7729184e-03 1.5387455e-03 3.8489345e-02 2.4427633e-04 2.5870999e-03 2.8098846e-03 9.5472550e-03 6.5811256e-04 1.8465019e-03 1.0925425e-03 3.9557876e-03 2.4136396e-01 2.3914246e-01 2.8140056e-01 3.2726072e-01 2.9909180e-01 3.1587284e-01 2.5940651e-01 2.1853199e-01 2.7358477e-01 2.7023320e-01 3.0865618e-01 2.4367721e-01 3.0046030e-01 3.1269475e-01 1.8026794e-01 2.2845050e-01 3.0393397e-01 2.5531598e-01 3.8377418e-01 2.6747174e-01 3.1847415e-01 2.3166294e-01 3.8684625e-01 3.1607055e-01 2.4560721e-01 2.4289838e-01 3.0537010e-01 3.2346477e-01 2.9500292e-01 1.9758720e-01 2.7227129e-01 2.5416529e-01 2.3708995e-01 4.1588028e-01 3.1833018e-01 2.3144395e-01 2.6550973e-01 3.3890880e-01 2.3429142e-01 2.9628566e-01 3.3633600e-01 2.8587728e-01 2.6362949e-01 2.3087874e-01 2.9089607e-01 2.4143368e-01 2.5794041e-01 2.5175590e-01 1.6347149e-01 2.5753099e-01 5.1758017e-01 4.5875138e-01 4.3664497e-01 4.5058511e-01 4.8035455e-01 4.8203651e-01 4.6682330e-01 4.6036369e-01 4.9325486e-01 4.0679312e-01 3.3723664e-01 4.3530687e-01 4.0692383e-01 4.9456817e-01 4.9519341e-01 3.9426880e-01 4.0735509e-01 4.0616607e-01 5.6363656e-01 4.6254311e-01 4.1397979e-01 4.3911880e-01 4.9961302e-01 3.8027310e-01 4.0098514e-01 3.9884545e-01 3.5820683e-01 3.4982569e-01 4.7998848e-01 3.8669536e-01 4.4902142e-01 3.4986296e-01 4.8921417e-01 3.7374272e-01 4.7562308e-01 4.3467328e-01 4.2816870e-01 4.0201263e-01 3.4137888e-01 3.7400811e-01 4.3776854e-01 3.4988231e-01 4.5875138e-01 4.4907175e-01 4.3130086e-01 3.8919296e-01 4.3084858e-01 3.7966236e-01 3.9613519e-01 3.9634370e-01 7.6697767e-03 2.4232895e-02 4.2497383e-04 5.8645021e-03 7.1697323e-04 1.6459430e-03 2.1472725e-03 2.1701877e-03 1.5644328e-03 2.1493500e-04 2.5540450e-03 1.7185148e-02 1.5785034e-02 5.4418849e-05 4.0402433e-03 4.0294443e-03 5.4418849e-05 2.5045043e-03 1.5070390e-03 7.3845654e-03 1.1685406e-02 7.1165327e-03 4.2192668e-03 6.5870297e-03 1.0764642e-03 8.6213220e-03 3.2147266e-03 4.4993834e-03 1.8554035e-03 1.6163874e-01 1.6124582e-01 1.9600517e-01 2.3441471e-01 2.0995191e-01 2.3066019e-01 1.8049424e-01 1.4375647e-01 1.8928742e-01 1.8888935e-01 2.1809263e-01 1.6489764e-01 2.1018407e-01 2.2610729e-01 1.1131536e-01 1.5021138e-01 2.2157821e-01 1.7624974e-01 2.8213828e-01 1.8434338e-01 2.3311339e-01 1.5259174e-01 2.8788764e-01 2.2976085e-01 1.6495975e-01 1.6222817e-01 2.1560367e-01 2.3216078e-01 2.0930930e-01 1.2444580e-01 1.8772709e-01 1.7242257e-01 1.5812464e-01 3.1788081e-01 2.3649618e-01 1.5894784e-01 1.8237277e-01 2.4318751e-01 1.6040650e-01 2.0886424e-01 2.4850015e-01 2.0301750e-01 1.8048879e-01 1.5257957e-01 2.0654604e-01 1.6721007e-01 1.7931046e-01 1.7120054e-01 9.6139593e-02 1.7732284e-01 4.1297254e-01 3.5577891e-01 3.3316436e-01 3.5061301e-01 3.7530173e-01 3.7554177e-01 3.6661833e-01 3.5699377e-01 3.8511507e-01 3.0820403e-01 2.4524565e-01 3.3176869e-01 3.0575678e-01 3.8594227e-01 3.8559393e-01 2.9549451e-01 3.1016061e-01 3.1160241e-01 4.4857854e-01 3.5641190e-01 3.1263400e-01 3.3812708e-01 3.9109335e-01 2.8129908e-01 3.0451362e-01 3.0237858e-01 2.6230725e-01 2.5773246e-01 3.7352449e-01 2.9029824e-01 3.4398832e-01 2.5959514e-01 3.8123049e-01 2.7952979e-01 3.7549545e-01 3.2868655e-01 3.3002382e-01 3.0704202e-01 2.5032682e-01 2.7592132e-01 3.3326893e-01 2.5208920e-01 3.5577891e-01 3.4619466e-01 3.2870702e-01 2.8757710e-01 3.2540565e-01 2.8197541e-01 3.0143248e-01 3.0241595e-01 4.6779054e-03 6.3405358e-03 1.1072067e-02 1.2740784e-02 2.2273681e-03 1.7042107e-03 2.3469652e-03 4.9562680e-03 7.4940361e-03 5.7695726e-03 3.5981535e-03 1.5844867e-03 6.8214146e-03 3.1957366e-03 3.3967393e-03 6.8214146e-03 1.4289215e-03 2.3814252e-03 3.6906895e-04 3.2052685e-02 4.0942979e-04 9.9039775e-04 3.7975123e-03 6.5400285e-03 1.5831617e-03 1.2837357e-03 4.6903663e-04 2.2256509e-03 2.3440889e-01 2.3310632e-01 2.7431935e-01 3.1898347e-01 2.9103053e-01 3.1107217e-01 2.5431417e-01 2.1253549e-01 2.6655333e-01 2.6462531e-01 3.0042601e-01 2.3750489e-01 2.9168949e-01 3.0705757e-01 1.7397041e-01 2.2119992e-01 2.9980584e-01 2.4986709e-01 3.7353610e-01 2.6066067e-01 3.1374027e-01 2.2416418e-01 3.7874518e-01 3.1076095e-01 2.3844790e-01 2.3545346e-01 2.9741154e-01 3.1590317e-01 2.8873621e-01 1.9043311e-01 2.6497335e-01 2.4708373e-01 2.3022342e-01 4.0978464e-01 3.1511039e-01 2.2757419e-01 2.5853132e-01 3.2950090e-01 2.2996799e-01 2.8911377e-01 3.3136481e-01 2.8050509e-01 2.5648758e-01 2.2384164e-01 2.8507826e-01 2.3741411e-01 2.5289158e-01 2.4520543e-01 1.5591712e-01 2.5163277e-01 5.1215829e-01 4.5200156e-01 4.2861009e-01 4.4496538e-01 4.7341919e-01 4.7441196e-01 4.6168878e-01 4.5347805e-01 4.8519916e-01 3.9997999e-01 3.3019092e-01 4.2716946e-01 3.9865809e-01 4.8629326e-01 4.8637186e-01 3.8670691e-01 4.0127624e-01 4.0126667e-01 5.5444310e-01 4.5424309e-01 4.0600299e-01 4.3254422e-01 4.9161364e-01 3.7174460e-01 3.9497344e-01 3.9273809e-01 3.5018083e-01 3.4346685e-01 4.7229362e-01 3.8003558e-01 4.4070091e-01 3.4442025e-01 4.8107176e-01 3.6755422e-01 4.7082182e-01 4.2490414e-01 4.2252116e-01 3.9675769e-01 3.3509282e-01 3.6564070e-01 4.2918040e-01 3.3977682e-01 4.5200156e-01 4.4195847e-01 4.2350677e-01 3.7942836e-01 4.2122143e-01 3.7189771e-01 3.9075300e-01 3.9132598e-01 2.1401129e-02 2.6296569e-02 3.2566498e-02 1.3297832e-02 1.1999623e-02 1.2969856e-02 1.8466773e-02 2.3873417e-02 1.8518842e-02 4.0273028e-03 1.2616800e-03 2.2732626e-02 1.2827409e-02 1.3181090e-02 2.2732626e-02 1.1261737e-02 1.3701796e-02 5.3984869e-03 5.7610599e-02 5.9941455e-03 8.9691119e-03 1.2694193e-02 2.1144882e-02 7.0627421e-03 1.0543898e-02 8.0308076e-03 1.3063205e-02 2.9989796e-01 2.9808728e-01 3.4381381e-01 3.9275825e-01 3.6234621e-01 3.8238034e-01 3.2086021e-01 2.7526551e-01 3.3529881e-01 3.3248525e-01 3.7261196e-01 3.0300865e-01 3.6317903e-01 3.7865155e-01 2.3211430e-01 2.8532877e-01 3.6963026e-01 3.1621648e-01 4.5177033e-01 3.2875625e-01 3.8520789e-01 2.8867443e-01 4.5697798e-01 3.8242988e-01 3.0442487e-01 3.0118555e-01 3.6926913e-01 3.8920975e-01 3.5915749e-01 2.5088735e-01 3.3365900e-01 3.1393007e-01 2.9523721e-01 4.8898239e-01 3.8528778e-01 2.9050533e-01 3.2648373e-01 4.0430761e-01 2.9352285e-01 3.5998625e-01 4.0438969e-01 3.4965804e-01 3.2429593e-01 2.8821126e-01 3.5492121e-01 3.0147553e-01 3.1927061e-01 3.1166517e-01 2.1164881e-01 3.1841810e-01 5.9626248e-01 5.3406968e-01 5.1001241e-01 5.2586196e-01 5.5673824e-01 5.5816878e-01 5.4309284e-01 5.3570978e-01 5.6971774e-01 4.7901704e-01 4.0442977e-01 5.0852772e-01 4.7828326e-01 5.7096471e-01 5.7126109e-01 4.6526268e-01 4.7993533e-01 4.7902644e-01 6.4260357e-01 5.3722757e-01 4.8599135e-01 5.1340958e-01 5.7642669e-01 4.4962350e-01 4.7318809e-01 4.7087433e-01 4.2633712e-01 4.1835467e-01 5.5597597e-01 4.5768215e-01 5.2292685e-01 4.1877489e-01 5.6541976e-01 4.4406435e-01 5.5240885e-01 5.0657081e-01 5.0216108e-01 4.7452659e-01 4.0930806e-01 4.4303784e-01 5.1082488e-01 4.1541892e-01 5.3406968e-01 5.2368299e-01 5.0449934e-01 4.5807263e-01 5.0263701e-01 4.4952858e-01 4.6823972e-01 4.6855914e-01 8.7198254e-03 1.2902931e-03 1.1684021e-03 1.6341127e-03 1.0742763e-03 2.6208124e-03 1.1173357e-03 8.9620141e-04 1.6960301e-02 1.4197837e-02 5.6586090e-04 2.0157829e-03 1.9836256e-03 5.6586090e-04 2.0413623e-03 1.1637665e-03 5.4511725e-03 1.0718252e-02 6.5990100e-03 2.7263257e-03 7.7966832e-03 1.5754002e-04 8.7922744e-03 3.0619296e-03 3.8030551e-03 1.0550593e-03 1.7203244e-01 1.7235723e-01 2.0751307e-01 2.4611820e-01 2.2109400e-01 2.4491699e-01 1.9298922e-01 1.5421652e-01 2.0061094e-01 2.0129686e-01 2.2934517e-01 1.7603988e-01 2.2070889e-01 2.3964854e-01 1.2009621e-01 1.5990877e-01 2.3601974e-01 1.8834267e-01 2.9339046e-01 1.9567575e-01 2.4748464e-01 1.6217086e-01 3.0116422e-01 2.4364046e-01 1.7530938e-01 1.7223213e-01 2.2701156e-01 2.4439785e-01 2.2188150e-01 1.3311613e-01 1.9877894e-01 1.8311301e-01 1.6845668e-01 3.3339209e-01 2.5192873e-01 1.7150268e-01 1.9350184e-01 2.5414415e-01 1.7271733e-01 2.2071334e-01 2.6313203e-01 2.1605186e-01 1.9141813e-01 1.6255272e-01 2.1936783e-01 1.7996851e-01 1.9179556e-01 1.8227672e-01 1.0300751e-01 1.8912086e-01 4.3075013e-01 3.7158844e-01 3.4752045e-01 3.6713693e-01 3.9134187e-01 3.9105984e-01 3.8378198e-01 3.7272538e-01 4.0046876e-01 3.2297138e-01 2.5826699e-01 3.4601238e-01 3.1932057e-01 4.0113556e-01 4.0033459e-01 3.0938917e-01 3.2551476e-01 3.2781102e-01 4.6405632e-01 3.7102765e-01 3.2659021e-01 3.5371449e-01 4.0659862e-01 2.9405620e-01 3.1978995e-01 3.1753736e-01 2.7499746e-01 2.7161605e-01 3.8895029e-01 3.0476818e-01 3.5833770e-01 2.7419176e-01 3.9644448e-01 2.9409120e-01 3.9304592e-01 3.4147602e-01 3.4611220e-01 3.2290811e-01 2.6406812e-01 2.8867894e-01 3.4717424e-01 2.6265032e-01 3.7158844e-01 3.6154105e-01 3.4316147e-01 2.9940560e-01 3.3824889e-01 2.9538166e-01 3.1708459e-01 3.1834036e-01 8.7316989e-03 6.7615313e-03 6.7580543e-03 9.6081666e-03 2.0657605e-03 3.8371349e-03 1.4271025e-02 1.2184393e-02 1.6084765e-02 4.9831593e-03 1.4699244e-02 1.4912454e-02 4.9831593e-03 6.5028816e-03 6.2489819e-03 1.3726162e-02 3.1411524e-02 7.7445772e-03 1.1141212e-02 2.5000477e-03 1.1143820e-02 6.1604304e-03 5.3711085e-03 7.5849080e-03 8.1835455e-03 1.6688130e-01 1.6291433e-01 2.0003066e-01 2.4149909e-01 2.1722570e-01 2.2445012e-01 1.7758122e-01 1.4623090e-01 1.9343386e-01 1.8753794e-01 2.2568158e-01 1.6690274e-01 2.2030573e-01 2.2323949e-01 1.1699096e-01 1.5725818e-01 2.1323159e-01 1.7489777e-01 2.9532205e-01 1.8786273e-01 2.2655558e-01 1.6049736e-01 2.9226070e-01 2.2550223e-01 1.7083413e-01 1.6938578e-01 2.2221647e-01 2.3649790e-01 2.0956346e-01 1.3266766e-01 1.9299947e-01 1.7760033e-01 1.6320695e-01 3.1350837e-01 2.2421748e-01 1.5253844e-01 1.8664517e-01 2.5447595e-01 1.5560691e-01 2.1266187e-01 2.4223633e-01 2.0012528e-01 1.8549850e-01 1.5864276e-01 2.0519641e-01 1.6093217e-01 1.7628990e-01 1.7435363e-01 1.0801101e-01 1.7763987e-01 4.0351234e-01 3.5280598e-01 3.3582666e-01 3.4348161e-01 3.7251839e-01 3.7541634e-01 3.5722931e-01 3.5450932e-01 3.8642215e-01 3.0688243e-01 2.4703841e-01 3.3487550e-01 3.1018257e-01 3.8808655e-01 3.8990045e-01 2.9749231e-01 3.0596368e-01 3.0291072e-01 4.5287211e-01 3.5944458e-01 3.1570031e-01 3.3498082e-01 3.9202879e-01 2.8759651e-01 3.0023966e-01 2.9853735e-01 2.6729925e-01 2.5639363e-01 3.7372548e-01 2.8896987e-01 3.4745520e-01 2.5469579e-01 3.8297689e-01 2.7675896e-01 3.6463006e-01 3.3839246e-01 3.2358656e-01 2.9982548e-01 2.4898596e-01 2.8176933e-01 3.3810925e-01 2.6587897e-01 3.5280598e-01 3.4488951e-01 3.3055981e-01 2.9862862e-01 3.3464036e-01 2.8522813e-01 2.9487313e-01 2.9445589e-01 4.3312037e-03 5.1674385e-03 4.6600432e-03 4.1032398e-03 1.3249619e-03 3.6837745e-03 2.4877830e-02 2.2958753e-02 1.1314391e-03 6.3769043e-03 6.2810489e-03 1.1314391e-03 5.7702159e-03 4.1635898e-03 1.1902706e-02 7.2040189e-03 1.2293784e-02 7.6706139e-03 1.1339253e-02 1.8296747e-03 1.4280945e-02 6.9429868e-03 8.6587607e-03 4.4388406e-03 1.4462991e-01 1.4494206e-01 1.7756526e-01 2.1378771e-01 1.9031042e-01 2.1286860e-01 1.6419734e-01 1.2822571e-01 1.7113301e-01 1.7185341e-01 1.9804950e-01 1.4833973e-01 1.9005998e-01 2.0775634e-01 9.7105565e-02 1.3348080e-01 2.0467358e-01 1.5981923e-01 2.5874568e-01 1.6653873e-01 2.1530133e-01 1.3558698e-01 2.6575883e-01 2.1156399e-01 1.4766732e-01 1.4484877e-01 1.9583483e-01 2.1211626e-01 1.9101641e-01 1.0897783e-01 1.6943757e-01 1.5488355e-01 1.4132668e-01 2.9650216e-01 2.1994601e-01 1.4455458e-01 1.6451956e-01 2.2152336e-01 1.4555202e-01 1.8989624e-01 2.3000345e-01 1.8566766e-01 1.6258902e-01 1.3589561e-01 1.8870744e-01 1.5235300e-01 1.6309487e-01 1.5410176e-01 8.1942750e-02 1.6048904e-01 3.8999083e-01 3.3294245e-01 3.0988325e-01 3.2882841e-01 3.5187047e-01 3.5156617e-01 3.4487977e-01 3.3402051e-01 3.6059894e-01 2.8649814e-01 2.2516617e-01 3.0844683e-01 2.8301726e-01 3.6124416e-01 3.6050400e-01 2.7355262e-01 2.8899505e-01 2.9139815e-01 4.2192237e-01 3.3236960e-01 2.8992253e-01 3.1585310e-01 3.6648886e-01 2.5905330e-01 2.8355149e-01 2.8139447e-01 2.4097820e-01 2.3780572e-01 3.4954046e-01 2.6919588e-01 3.2022959e-01 2.4036369e-01 3.5673540e-01 2.5910558e-01 3.5384915e-01 3.0430469e-01 3.0871278e-01 2.8664912e-01 2.3068275e-01 2.5394489e-01 3.0958210e-01 2.2970603e-01 3.3294245e-01 3.2329736e-01 3.0571743e-01 2.6432145e-01 3.0120271e-01 2.6026006e-01 2.8107880e-01 2.8234890e-01 3.9333334e-05 2.5761851e-04 1.3896575e-03 1.7536063e-03 1.9216331e-03 9.2702772e-03 7.4022776e-03 1.3090644e-03 1.5444025e-03 1.6252591e-03 1.3090644e-03 1.2151407e-04 1.0618022e-05 2.1493524e-03 1.8833153e-02 2.2172388e-03 7.3253783e-04 3.6800655e-03 1.5497854e-03 3.6733784e-03 4.9421897e-04 7.6664670e-04 7.0183041e-05 1.9407897e-01 1.9337938e-01 2.3114302e-01 2.7244640e-01 2.4629979e-01 2.6718506e-01 2.1373312e-01 1.7438911e-01 2.2391848e-01 2.2298785e-01 2.5503185e-01 1.9737603e-01 2.4663659e-01 2.6276403e-01 1.3891547e-01 1.8173656e-01 2.5713590e-01 2.0933386e-01 3.2324703e-01 2.1854256e-01 2.6975489e-01 1.8436571e-01 3.2904939e-01 2.6649404e-01 1.9772084e-01 1.9481826e-01 2.5232520e-01 2.6989379e-01 2.4511828e-01 1.5354404e-01 2.2230840e-01 2.0576951e-01 1.9024966e-01 3.5987888e-01 2.7239312e-01 1.8983400e-01 2.1646886e-01 2.8191043e-01 1.9167912e-01 2.4493869e-01 2.8620678e-01 2.3801287e-01 2.1448199e-01 1.8427005e-01 2.4198503e-01 1.9884520e-01 2.1243903e-01 2.0430599e-01 1.2215728e-01 2.1067715e-01 4.5871846e-01 3.9981904e-01 3.7661173e-01 3.9390784e-01 4.2025710e-01 4.2076082e-01 4.1035332e-01 4.0114215e-01 4.3087129e-01 3.5002279e-01 2.8365040e-01 3.7517763e-01 3.4790525e-01 4.3179681e-01 4.3157518e-01 3.3691557e-01 3.5177012e-01 3.5268567e-01 4.9705280e-01 4.0100475e-01 3.5505921e-01 3.8129652e-01 4.3707681e-01 3.2219168e-01 3.4580868e-01 3.4360838e-01 3.0200588e-01 2.9663386e-01 4.1868171e-01 3.3113109e-01 3.8802094e-01 2.9819354e-01 4.2685142e-01 3.1959000e-01 4.1941543e-01 3.7225097e-01 3.7239048e-01 3.4809183e-01 2.8876177e-01 3.1647952e-01 3.7686145e-01 2.9138730e-01 3.9981904e-01 3.8994709e-01 3.7187123e-01 3.2897937e-01 3.6879196e-01 3.2272643e-01 3.4226532e-01 3.4310531e-01 3.4181612e-04 1.4644608e-03 2.1632573e-03 2.3231555e-03 8.1286280e-03 6.3818616e-03 1.7262411e-03 1.6624580e-03 1.7631926e-03 1.7262411e-03 2.4569053e-05 5.6859581e-05 1.7551896e-03 2.0541034e-02 1.6702311e-03 5.9025530e-04 3.2341087e-03 2.0408948e-03 3.0131078e-03 2.8888789e-04 4.5924146e-04 1.3293684e-04 1.9862431e-01 1.9776069e-01 2.3600084e-01 2.7779005e-01 2.5142895e-01 2.7187023e-01 2.1810101e-01 1.7860088e-01 2.2871390e-01 2.2752286e-01 2.6024955e-01 2.0181434e-01 2.5187795e-01 2.6758248e-01 1.4283878e-01 1.8622396e-01 2.6161990e-01 2.1373552e-01 3.2920505e-01 2.2325952e-01 2.7444329e-01 1.8891553e-01 3.3470963e-01 2.7127428e-01 2.0233024e-01 1.9944956e-01 2.5748056e-01 2.7510316e-01 2.4993365e-01 1.5775267e-01 2.2713624e-01 2.1043184e-01 1.9474801e-01 3.6534538e-01 2.7678503e-01 1.9377126e-01 2.2119834e-01 2.8749014e-01 1.9572304e-01 2.4991164e-01 2.9104517e-01 2.4261230e-01 2.1922346e-01 1.8874518e-01 2.4669621e-01 2.0288553e-01 2.1678869e-01 2.0886650e-01 1.2608625e-01 2.1517216e-01 4.6449792e-01 4.0560127e-01 3.8251194e-01 3.9944814e-01 4.2616553e-01 4.2679596e-01 4.1587264e-01 4.0695550e-01 4.3702628e-01 3.5557170e-01 2.8885981e-01 3.8108822e-01 3.5369655e-01 4.3799369e-01 4.3786355e-01 3.4252750e-01 3.5719184e-01 3.5788232e-01 5.0366583e-01 4.0706759e-01 3.6083966e-01 3.8695887e-01 4.4324858e-01 3.2788328e-01 3.5118655e-01 3.4899273e-01 3.0749578e-01 3.0179987e-01 4.2472010e-01 3.3655063e-01 3.9402558e-01 3.0319627e-01 4.3300399e-01 3.2485825e-01 4.2490754e-01 3.7841034e-01 3.7783349e-01 3.5333537e-01 2.9386653e-01 3.2211174e-01 3.8285611e-01 2.9712888e-01 4.0560127e-01 3.9574961e-01 3.7770634e-01 3.3490218e-01 3.7491174e-01 3.2829405e-01 3.4750328e-01 3.4827580e-01 2.7893549e-03 2.7277911e-03 9.4108270e-04 1.0799149e-02 7.7770747e-03 1.9770194e-03 5.4325768e-04 5.8945201e-04 1.9770194e-03 5.1514113e-04 3.6478892e-04 1.6876295e-03 1.7172006e-02 2.9808015e-03 3.7816236e-04 5.6735840e-03 1.0535544e-03 5.0675384e-03 1.2248969e-03 1.1742680e-03 5.8878380e-05 1.9840502e-01 1.9840646e-01 2.3601178e-01 2.7699198e-01 2.5062921e-01 2.7429513e-01 2.1981939e-01 1.7908511e-01 2.2870797e-01 2.2884763e-01 2.5935890e-01 2.0237179e-01 2.5038976e-01 2.6920634e-01 1.4261267e-01 1.8559031e-01 2.6457877e-01 2.1508394e-01 3.2684946e-01 2.2341437e-01 2.7694800e-01 1.8806980e-01 3.3447269e-01 2.7322826e-01 2.0195195e-01 1.9876628e-01 2.5683031e-01 2.7497319e-01 2.5083242e-01 1.5688509e-01 2.2686098e-01 2.1020792e-01 1.9457412e-01 3.6717222e-01 2.8066992e-01 1.9637709e-01 2.2117475e-01 2.8566080e-01 1.9793610e-01 2.4995417e-01 2.9345115e-01 2.4428403e-01 2.1902512e-01 1.8834472e-01 2.4798811e-01 2.0544380e-01 2.1853532e-01 2.0913443e-01 1.2439434e-01 2.1611468e-01 4.6739623e-01 4.0702486e-01 3.8261075e-01 4.0188705e-01 4.2751037e-01 4.2749738e-01 4.1883120e-01 4.0825884e-01 4.3736523e-01 3.5667688e-01 2.8932174e-01 3.8108192e-01 3.5341594e-01 4.3812879e-01 4.3747647e-01 3.4285168e-01 3.5898948e-01 3.6076578e-01 5.0317788e-01 4.0703744e-01 3.6087589e-01 3.8845434e-01 4.4366743e-01 3.2719263e-01 3.5301257e-01 3.5071770e-01 3.0715395e-01 3.0299726e-01 4.2534595e-01 3.3768843e-01 3.9391146e-01 3.0525217e-01 4.3324318e-01 3.2636388e-01 4.2820876e-01 3.7681443e-01 3.8013938e-01 3.5586945e-01 2.9507753e-01 3.2153910e-01 3.8242816e-01 2.9467977e-01 4.0702486e-01 3.9678588e-01 3.7800850e-01 3.3305200e-01 3.7343525e-01 3.2833826e-01 3.4988792e-01 3.5099795e-01 7.6889988e-04 5.5936308e-03 9.7548717e-03 1.0412603e-02 1.0389613e-03 5.7781385e-03 5.9052415e-03 1.0389613e-03 1.4340775e-03 1.1584691e-03 6.0259991e-03 2.1726707e-02 3.4598658e-03 3.9021886e-03 1.8160607e-03 3.9104990e-03 3.6199548e-03 1.2013216e-03 2.4248068e-03 2.0560641e-03 1.7726458e-01 1.7519789e-01 2.1239850e-01 2.5350621e-01 2.2831460e-01 2.4335474e-01 1.9301815e-01 1.5733953e-01 2.0548992e-01 2.0248973e-01 2.3687424e-01 1.7915768e-01 2.2981656e-01 2.4028800e-01 1.2483249e-01 1.6617415e-01 2.3289941e-01 1.8936545e-01 3.0533170e-01 2.0007138e-01 2.4571617e-01 1.6904051e-01 3.0733551e-01 2.4338605e-01 1.8100871e-01 1.7874491e-01 2.3386295e-01 2.4993365e-01 2.2441376e-01 1.3976123e-01 2.0438723e-01 1.8845276e-01 1.7353191e-01 3.3400984e-01 2.4631085e-01 1.6886156e-01 1.9837340e-01 2.6441281e-01 1.7118963e-01 2.2560913e-01 2.6174131e-01 2.1639026e-01 1.9675554e-01 1.6819987e-01 2.2078897e-01 1.7753606e-01 1.9173589e-01 1.8624992e-01 1.1156657e-01 1.9127963e-01 4.2879129e-01 3.7350470e-01 3.5299849e-01 3.6620603e-01 3.9355975e-01 3.9506070e-01 3.8147101e-01 3.7498057e-01 4.0550993e-01 3.2554347e-01 2.6225589e-01 3.5177359e-01 3.2570798e-01 4.0674255e-01 4.0737898e-01 3.1405475e-01 3.2617446e-01 3.2545044e-01 4.7160294e-01 3.7696723e-01 3.3214002e-01 3.5535112e-01 4.1144267e-01 3.0143840e-01 3.2034131e-01 3.1835849e-01 2.8130938e-01 2.7364725e-01 3.9315179e-01 3.0715437e-01 3.6445309e-01 2.7384746e-01 4.0174546e-01 2.9539622e-01 3.8981435e-01 3.5158797e-01 3.4545598e-01 3.2149982e-01 2.6601526e-01 2.9570609e-01 3.5410378e-01 2.7466513e-01 3.7350470e-01 3.6448827e-01 3.4805708e-01 3.0999906e-01 3.4801807e-01 3.0074518e-01 3.1606363e-01 3.1638243e-01 3.9211584e-03 1.5428451e-02 1.5051422e-02 9.3641634e-05 5.2251987e-03 5.2564865e-03 9.3641634e-05 2.4059863e-03 1.5411207e-03 7.7716003e-03 1.4517538e-02 6.4009679e-03 4.6963365e-03 4.9429047e-03 2.1131701e-03 7.3135995e-03 2.7662314e-03 4.2415669e-03 2.1979302e-03 1.6154877e-01 1.6047147e-01 1.9568300e-01 2.3468417e-01 2.1025394e-01 2.2838581e-01 1.7884422e-01 1.4313754e-01 1.8898793e-01 1.8753794e-01 2.1845622e-01 1.6418759e-01 2.1103286e-01 2.2447017e-01 1.1131548e-01 1.5046796e-01 2.1889519e-01 1.7489777e-01 2.8359045e-01 1.8392350e-01 2.3077309e-01 1.5301294e-01 2.8765002e-01 2.2785871e-01 1.6499082e-01 1.6250540e-01 2.1577964e-01 2.3190311e-01 2.0826660e-01 1.2493129e-01 1.8764327e-01 1.7232008e-01 1.5800409e-01 3.1597925e-01 2.3306671e-01 1.5663136e-01 1.8209705e-01 2.4426589e-01 1.5839676e-01 2.0849973e-01 2.4621874e-01 2.0137281e-01 1.8035428e-01 1.5264734e-01 2.0519641e-01 1.6491824e-01 1.7763987e-01 1.7071018e-01 9.7334595e-02 1.7628990e-01 4.1013753e-01 3.5415853e-01 3.3261378e-01 3.4819629e-01 3.7372548e-01 3.7447405e-01 3.6377706e-01 3.5546726e-01 3.8432564e-01 3.0688243e-01 2.4450319e-01 3.3130276e-01 3.0553473e-01 3.8531353e-01 3.8537934e-01 2.9480414e-01 3.0829006e-01 3.0887441e-01 4.4839112e-01 3.5594109e-01 3.1215369e-01 3.3646690e-01 3.9023537e-01 2.8142851e-01 3.0262656e-01 3.0057353e-01 2.6218143e-01 2.5639363e-01 3.7251839e-01 2.8896987e-01 3.4359589e-01 2.5757650e-01 3.8052352e-01 2.7792266e-01 3.7237623e-01 3.2948525e-01 3.2773210e-01 3.0459398e-01 2.4898596e-01 2.7596310e-01 3.3313575e-01 2.5365041e-01 3.5415853e-01 3.4488951e-01 3.2799981e-01 2.8862094e-01 3.2611271e-01 2.8152144e-01 2.9910812e-01 2.9982464e-01 1.7874931e-02 1.3166667e-02 2.8067647e-03 5.4483543e-04 4.8491153e-04 2.8067647e-03 2.7932574e-03 2.1120566e-03 4.0283644e-03 1.1006031e-02 7.1968081e-03 1.9824114e-03 1.0903705e-02 3.4373671e-04 1.0364284e-02 4.2441401e-03 4.1832512e-03 1.3456492e-03 1.8893734e-01 1.9027350e-01 2.2602463e-01 2.6493124e-01 2.3909907e-01 2.6731994e-01 2.1287273e-01 1.7119179e-01 2.1885648e-01 2.2106766e-01 2.4751153e-01 1.9400411e-01 2.3783986e-01 2.6104566e-01 1.3465024e-01 1.7581619e-01 2.5867158e-01 2.0765719e-01 3.1157756e-01 2.1392913e-01 2.7004976e-01 1.7791371e-01 3.2221942e-01 2.6551484e-01 1.9215572e-01 1.8858682e-01 2.4539617e-01 2.6396374e-01 2.4190361e-01 1.4753547e-01 2.1663955e-01 2.0044864e-01 1.8527044e-01 3.5759997e-01 2.7597540e-01 1.9143761e-01 2.1146655e-01 2.7190276e-01 1.9231433e-01 2.3971651e-01 2.8607417e-01 2.3671699e-01 2.0909909e-01 1.7885289e-01 2.3973819e-01 2.0020124e-01 2.1166375e-01 2.0015528e-01 1.1482314e-01 2.0802247e-01 4.5814031e-01 3.9622068e-01 3.7010544e-01 3.9277065e-01 4.1629822e-01 4.1528170e-01 4.1031284e-01 3.9724817e-01 4.2445794e-01 3.4613203e-01 2.7894605e-01 3.6843870e-01 3.4078468e-01 4.2489846e-01 4.2345869e-01 3.3131814e-01 3.4950009e-01 3.5299308e-01 4.8820349e-01 3.9397890e-01 3.4860920e-01 3.7803314e-01 4.3080058e-01 3.1437592e-01 3.4366436e-01 3.4124618e-01 2.9521701e-01 2.9351505e-01 4.1304236e-01 3.2750551e-01 3.8091343e-01 2.9709224e-01 4.2023724e-01 3.1695286e-01 4.2011817e-01 3.6184591e-01 3.7113199e-01 3.4760954e-01 2.8576301e-01 3.0899799e-01 3.6912287e-01 2.7985004e-01 3.9622068e-01 3.8552168e-01 3.6588486e-01 3.1841134e-01 3.5869504e-01 3.1661834e-01 3.4148439e-01 3.4312143e-01 1.2983249e-03 1.5471006e-02 1.3470267e-02 1.3877317e-02 1.5471006e-02 7.2739782e-03 9.2612860e-03 6.0792360e-03 5.4160667e-02 2.4574168e-03 8.1477262e-03 3.8451807e-03 1.8194605e-02 1.5067164e-03 5.6792549e-03 4.8846913e-03 1.0004616e-02 2.6108642e-01 2.5718656e-01 3.0162187e-01 3.4990044e-01 3.2118558e-01 3.3212064e-01 2.7586622e-01 2.3634761e-01 2.9366681e-01 2.8770633e-01 3.3110877e-01 2.6200119e-01 3.2383134e-01 3.3046521e-01 1.9829106e-01 2.4860521e-01 3.1887574e-01 2.7240198e-01 4.1018982e-01 2.8711577e-01 3.3461178e-01 2.5228731e-01 4.0944673e-01 3.3325697e-01 2.6572836e-01 2.6351234e-01 3.2731613e-01 3.4479515e-01 3.1382719e-01 2.1730674e-01 2.9282566e-01 2.7420761e-01 2.5662111e-01 4.3493335e-01 3.3165524e-01 2.4546083e-01 2.8544328e-01 3.6363165e-01 2.4916639e-01 3.1676067e-01 3.5299851e-01 3.0301330e-01 2.8384547e-01 2.5066938e-01 3.0887732e-01 2.5579752e-01 2.7431070e-01 2.7082223e-01 1.8355500e-01 2.7545202e-01 5.3566205e-01 4.7913827e-01 4.5930782e-01 4.6887050e-01 5.0113940e-01 5.0408511e-01 4.8425611e-01 4.8100390e-01 5.1611676e-01 4.2712908e-01 3.5770237e-01 4.5815125e-01 4.2990067e-01 5.1783166e-01 5.1944925e-01 4.1592191e-01 4.2634771e-01 4.2297928e-01 5.8870710e-01 4.8576650e-01 4.3645958e-01 4.5912638e-01 5.2238783e-01 4.0361731e-01 4.1983156e-01 4.1785974e-01 3.8054730e-01 3.6909926e-01 5.0215992e-01 4.0667536e-01 4.7224138e-01 3.6745640e-01 5.1222367e-01 3.9280819e-01 4.9246039e-01 4.6045456e-01 4.4643751e-01 4.1946868e-01 3.6048019e-01 3.9703515e-01 4.6143668e-01 3.7590038e-01 4.7913827e-01 4.7009550e-01 4.5350904e-01 4.1479012e-01 4.5636227e-01 4.0162438e-01 4.1380491e-01 4.1334093e-01 1.4411876e-02 8.7935053e-03 9.1184252e-03 1.4411876e-02 5.7453578e-03 7.6054776e-03 2.7409931e-03 4.7624677e-02 1.8780439e-03 4.9746662e-03 5.9684857e-03 1.4534556e-02 2.3601867e-03 4.9286760e-03 3.4466344e-03 7.5341006e-03 2.6746877e-01 2.6516664e-01 3.0921139e-01 3.5673992e-01 3.2754140e-01 3.4481450e-01 2.8624779e-01 2.4359422e-01 3.0108022e-01 2.9756219e-01 3.3745616e-01 2.6990583e-01 3.2887946e-01 3.4163113e-01 2.0331657e-01 2.5392638e-01 3.3233847e-01 2.8201820e-01 4.1489235e-01 2.9472025e-01 3.4749512e-01 2.5726587e-01 4.1828245e-01 3.4508966e-01 2.7189140e-01 2.6903465e-01 3.3406888e-01 3.5284896e-01 3.2333666e-01 2.2149953e-01 2.9970099e-01 2.8083524e-01 2.6300357e-01 4.4804341e-01 3.4702929e-01 2.5691578e-01 2.9267142e-01 3.6866600e-01 2.5996213e-01 3.2467728e-01 3.6599412e-01 3.1379996e-01 2.9070461e-01 2.5648991e-01 3.1904849e-01 2.6736905e-01 2.8471636e-01 2.7833907e-01 1.8530093e-01 2.8435281e-01 5.5199539e-01 4.9206433e-01 4.6946939e-01 4.8356355e-01 5.1418554e-01 5.1595196e-01 5.0010048e-01 4.9372697e-01 5.2743722e-01 4.3877522e-01 3.6710197e-01 4.6809646e-01 4.3893984e-01 5.2878375e-01 5.2942069e-01 4.2592703e-01 4.3928640e-01 4.3789059e-01 5.9924430e-01 4.9601715e-01 4.4619754e-01 4.7193405e-01 5.3393309e-01 4.1149136e-01 4.3273356e-01 4.3054493e-01 3.8875406e-01 3.8007672e-01 5.1386048e-01 4.1809676e-01 4.8216133e-01 3.8002405e-01 5.2330849e-01 4.0472333e-01 5.0903363e-01 4.6735136e-01 4.6059448e-01 4.3368525e-01 3.7134977e-01 4.0504167e-01 4.7061436e-01 3.7989795e-01 4.9206433e-01 4.8217825e-01 4.6398354e-01 4.2055878e-01 4.6343857e-01 4.1088828e-01 4.2766517e-01 4.2782051e-01 4.0251529e-03 4.0402030e-03 0.0000000e+00 2.0104773e-03 1.1579294e-03 6.7733512e-03 1.3328703e-02 6.1195429e-03 3.8280621e-03 5.4471697e-03 1.3203495e-03 7.3930866e-03 2.5511055e-03 3.8011014e-03 1.5935161e-03 1.6488937e-01 1.6420208e-01 1.9946134e-01 2.3841307e-01 2.1377841e-01 2.3352484e-01 1.8323739e-01 1.4660873e-01 1.9269692e-01 1.9183992e-01 2.2200730e-01 1.6791595e-01 2.1423078e-01 2.2922867e-01 1.1407468e-01 1.5349471e-01 2.2418179e-01 1.7908948e-01 2.8692621e-01 1.8765985e-01 2.3596576e-01 1.5596498e-01 2.9203641e-01 2.3278981e-01 1.6829150e-01 1.6563525e-01 2.1942293e-01 2.3592536e-01 2.1256421e-01 1.2755291e-01 1.9121342e-01 1.7576718e-01 1.6132929e-01 3.2148865e-01 2.3885357e-01 1.6118125e-01 1.8573297e-01 2.4757051e-01 1.6279862e-01 2.1240660e-01 2.5149161e-01 2.0595435e-01 1.8389163e-01 1.5580881e-01 2.0964365e-01 1.6953436e-01 1.8203376e-01 1.7437090e-01 9.9184065e-02 1.8031354e-01 4.1664204e-01 3.5971944e-01 3.3744612e-01 3.5416868e-01 3.7936058e-01 3.7982315e-01 3.7006184e-01 3.6098196e-01 3.8956200e-01 3.1201251e-01 2.4889912e-01 3.3607844e-01 3.1002022e-01 3.9046127e-01 3.9028467e-01 2.9949936e-01 3.1373736e-01 3.1479561e-01 4.5355800e-01 3.6085089e-01 3.1682959e-01 3.4195608e-01 3.9553949e-01 2.8555816e-01 3.0804941e-01 3.0593834e-01 2.6633772e-01 2.6121338e-01 3.7782244e-01 2.9399549e-01 3.4839508e-01 2.6278429e-01 3.8569374e-01 2.8303600e-01 3.7885421e-01 3.3349568e-01 3.3352424e-01 3.1033775e-01 2.5375578e-01 2.8011018e-01 3.3772574e-01 2.5671985e-01 3.5971944e-01 3.5022311e-01 3.3289784e-01 2.9224130e-01 3.3016010e-01 2.8599652e-01 3.0475125e-01 3.0561759e-01 3.0700267e-06 4.0251529e-03 1.9467123e-03 1.7971688e-03 1.7301608e-03 1.6293141e-02 4.7718752e-03 6.8454231e-04 9.3235659e-03 1.4106675e-03 7.7429721e-03 3.1097831e-03 2.5391665e-03 9.5764453e-04 2.0739268e-01 2.0836358e-01 2.4589773e-01 2.8656370e-01 2.5981614e-01 2.8739169e-01 2.3131893e-01 1.8849968e-01 2.3845740e-01 2.4007299e-01 2.6857108e-01 2.1230255e-01 2.5877066e-01 2.8137449e-01 1.5046312e-01 1.9386671e-01 2.7809337e-01 2.2610426e-01 3.3523485e-01 2.3325794e-01 2.9016640e-01 1.9614834e-01 3.4542234e-01 2.8580893e-01 2.1082239e-01 2.0722978e-01 2.6628244e-01 2.8528108e-01 2.6193836e-01 1.6431358e-01 2.3627495e-01 2.1939954e-01 2.0354344e-01 3.8077948e-01 2.9536596e-01 2.0839572e-01 2.3078002e-01 2.9414568e-01 2.0957408e-01 2.6009226e-01 3.0679254e-01 2.5613081e-01 2.2839644e-01 1.9694047e-01 2.5948108e-01 2.1757899e-01 2.3004331e-01 2.1886202e-01 1.3010315e-01 2.2671016e-01 4.8309156e-01 4.2059741e-01 3.9446827e-01 4.1650857e-01 4.4118694e-01 4.4046177e-01 4.3416675e-01 4.2171124e-01 4.5001131e-01 3.6938174e-01 3.0049261e-01 3.9280494e-01 3.6453208e-01 4.5055186e-01 4.4930066e-01 3.5453263e-01 3.7247610e-01 3.7543144e-01 5.1540859e-01 4.1899010e-01 3.7243361e-01 4.0192295e-01 4.5645786e-01 3.3753686e-01 3.6646355e-01 3.6403348e-01 3.1765048e-01 3.1516192e-01 4.3820660e-01 3.5021371e-01 4.0564147e-01 3.1837605e-01 4.4574076e-01 3.3915719e-01 4.4399141e-01 3.8666018e-01 3.9439941e-01 3.7011128e-01 3.0715348e-01 3.3195062e-01 3.9368522e-01 3.0254596e-01 4.2059741e-01 4.0983289e-01 3.9004780e-01 3.4211299e-01 3.8338579e-01 3.3953411e-01 3.6390344e-01 3.6538415e-01 4.0402030e-03 2.0647746e-03 1.8810615e-03 1.8744038e-03 1.5904825e-02 5.0089073e-03 7.7718458e-04 9.5962272e-03 1.3536136e-03 8.0354537e-03 3.2683077e-03 2.7053207e-03 1.0204136e-03 2.0663566e-01 2.0767698e-01 2.4509318e-01 2.8563097e-01 2.5892354e-01 2.8672996e-01 2.3068420e-01 1.8783705e-01 2.3766371e-01 2.3938906e-01 2.6765771e-01 2.1160129e-01 2.5782529e-01 2.8065126e-01 1.4982077e-01 1.9310124e-01 2.7749364e-01 2.2544624e-01 3.3411471e-01 2.3248629e-01 2.8950769e-01 1.9536146e-01 3.4445558e-01 2.8510792e-01 2.1004612e-01 2.0643532e-01 2.6539182e-01 2.8440424e-01 2.6118254e-01 1.6358877e-01 2.3546280e-01 2.1862058e-01 2.0279724e-01 3.7994304e-01 2.9482551e-01 2.0788172e-01 2.2999781e-01 2.9312113e-01 2.0902225e-01 2.5926765e-01 3.0610117e-01 2.5545076e-01 2.2760393e-01 1.9618808e-01 2.5876275e-01 2.1704368e-01 2.2941326e-01 2.1812470e-01 1.2939132e-01 2.2601611e-01 4.8224276e-01 4.1968249e-01 3.9347110e-01 4.1568549e-01 4.4024422e-01 4.3946585e-01 4.3337086e-01 4.2078495e-01 4.4897602e-01 3.6849705e-01 2.9964288e-01 3.9180103e-01 3.6353845e-01 4.4949934e-01 4.4820701e-01 3.5360058e-01 3.7164688e-01 3.7469388e-01 5.1424729e-01 4.1795596e-01 3.7145645e-01 4.0103422e-01 4.5542290e-01 3.3654347e-01 3.6564394e-01 3.6320808e-01 3.1671115e-01 3.1435391e-01 4.3720666e-01 3.4935506e-01 4.0461470e-01 3.1763784e-01 4.4470146e-01 3.3834387e-01 4.4321597e-01 3.8553656e-01 3.9358898e-01 3.6934910e-01 3.0635763e-01 3.3097385e-01 3.9264615e-01 3.0146236e-01 4.1968249e-01 4.0889669e-01 3.8907226e-01 3.4102273e-01 3.8227517e-01 3.3859771e-01 3.6313559e-01 3.6464431e-01 2.0104773e-03 1.1579294e-03 6.7733512e-03 1.3328703e-02 6.1195429e-03 3.8280621e-03 5.4471697e-03 1.3203495e-03 7.3930866e-03 2.5511055e-03 3.8011014e-03 1.5935161e-03 1.6488937e-01 1.6420208e-01 1.9946134e-01 2.3841307e-01 2.1377841e-01 2.3352484e-01 1.8323739e-01 1.4660873e-01 1.9269692e-01 1.9183992e-01 2.2200730e-01 1.6791595e-01 2.1423078e-01 2.2922867e-01 1.1407468e-01 1.5349471e-01 2.2418179e-01 1.7908948e-01 2.8692621e-01 1.8765985e-01 2.3596576e-01 1.5596498e-01 2.9203641e-01 2.3278981e-01 1.6829150e-01 1.6563525e-01 2.1942293e-01 2.3592536e-01 2.1256421e-01 1.2755291e-01 1.9121342e-01 1.7576718e-01 1.6132929e-01 3.2148865e-01 2.3885357e-01 1.6118125e-01 1.8573297e-01 2.4757051e-01 1.6279862e-01 2.1240660e-01 2.5149161e-01 2.0595435e-01 1.8389163e-01 1.5580881e-01 2.0964365e-01 1.6953436e-01 1.8203376e-01 1.7437090e-01 9.9184065e-02 1.8031354e-01 4.1664204e-01 3.5971944e-01 3.3744612e-01 3.5416868e-01 3.7936058e-01 3.7982315e-01 3.7006184e-01 3.6098196e-01 3.8956200e-01 3.1201251e-01 2.4889912e-01 3.3607844e-01 3.1002022e-01 3.9046127e-01 3.9028467e-01 2.9949936e-01 3.1373736e-01 3.1479561e-01 4.5355800e-01 3.6085089e-01 3.1682959e-01 3.4195608e-01 3.9553949e-01 2.8555816e-01 3.0804941e-01 3.0593834e-01 2.6633772e-01 2.6121338e-01 3.7782244e-01 2.9399549e-01 3.4839508e-01 2.6278429e-01 3.8569374e-01 2.8303600e-01 3.7885421e-01 3.3349568e-01 3.3352424e-01 3.1033775e-01 2.5375578e-01 2.8011018e-01 3.3772574e-01 2.5671985e-01 3.5971944e-01 3.5022311e-01 3.3289784e-01 2.9224130e-01 3.3016010e-01 2.8599652e-01 3.0475125e-01 3.0561759e-01 1.3213080e-04 1.6348389e-03 2.1958653e-02 1.3024074e-03 6.2459052e-04 2.7868209e-03 2.5128724e-03 2.4948096e-03 1.5213665e-04 2.9312203e-04 2.6269769e-04 2.0119891e-01 2.0014287e-01 2.3871519e-01 2.8086210e-01 2.5438649e-01 2.7419983e-01 2.2034638e-01 1.8091150e-01 2.3139686e-01 2.2990542e-01 2.6326545e-01 2.0423778e-01 2.5497864e-01 2.7008054e-01 1.4507978e-01 1.8881738e-01 2.6377709e-01 2.1604374e-01 3.3279319e-01 2.2588101e-01 2.7676493e-01 1.9156801e-01 3.3788258e-01 2.7371187e-01 2.0495838e-01 2.0212622e-01 2.6043406e-01 2.7802264e-01 2.5251791e-01 1.6022437e-01 2.2986910e-01 2.1306976e-01 1.9729190e-01 3.6816423e-01 2.7878040e-01 1.9567818e-01 2.2384809e-01 2.9081290e-01 1.9773475e-01 2.5268382e-01 2.9345893e-01 2.4498738e-01 2.2190073e-01 1.9130390e-01 2.4917712e-01 2.0485354e-01 2.1902087e-01 2.1139045e-01 1.2850635e-01 2.1757950e-01 4.6735126e-01 4.0863686e-01 3.8577163e-01 4.0223139e-01 4.2927752e-01 4.3005363e-01 4.1858115e-01 4.1002225e-01 4.4039161e-01 3.5852229e-01 2.9170762e-01 3.8436621e-01 3.5694453e-01 4.4140604e-01 4.4138801e-01 3.4560659e-01 3.5999045e-01 3.6042881e-01 5.0737364e-01 4.1042921e-01 3.6404336e-01 3.8992213e-01 4.4661300e-01 3.3112795e-01 3.5395838e-01 3.5178032e-01 3.1059068e-01 3.0453530e-01 4.2798873e-01 3.3942843e-01 3.9736659e-01 3.0574030e-01 4.3638021e-01 3.2761075e-01 4.2755962e-01 3.8201356e-01 3.8058140e-01 3.5594943e-01 2.9656762e-01 3.2531047e-01 3.8623001e-01 3.0061288e-01 4.0863686e-01 3.9884323e-01 3.8090672e-01 3.3841042e-01 3.7847943e-01 3.3138355e-01 3.5013260e-01 3.5082807e-01 2.3963109e-03 1.8894374e-02 2.2439950e-03 9.0594652e-04 3.4335324e-03 1.6224592e-03 3.5937956e-03 4.5783206e-04 8.1747242e-04 1.3119947e-04 1.9231717e-01 1.9150735e-01 2.2920314e-01 2.7047035e-01 2.4441394e-01 2.6481790e-01 2.1164496e-01 1.7262115e-01 2.2200818e-01 2.2091128e-01 2.5312771e-01 1.9549847e-01 2.4484600e-01 2.6050695e-01 1.3741417e-01 1.8008449e-01 2.5474237e-01 2.0730811e-01 3.2133505e-01 2.1663183e-01 2.6736983e-01 1.8273137e-01 3.2682425e-01 2.6418461e-01 1.9596515e-01 1.9311641e-01 2.5039748e-01 2.6783585e-01 2.4301257e-01 1.5205635e-01 2.2044020e-01 2.0395985e-01 1.8849810e-01 3.5730736e-01 2.6984619e-01 1.8774203e-01 2.1458977e-01 2.8004857e-01 1.8962388e-01 2.4294411e-01 2.8377929e-01 2.3583758e-01 2.1263399e-01 1.8257496e-01 2.3984158e-01 1.9672007e-01 2.1035264e-01 2.0243724e-01 1.2095575e-01 2.0869633e-01 4.5579154e-01 3.9719792e-01 3.7421582e-01 3.9117796e-01 4.1759891e-01 4.1818270e-01 4.0752420e-01 3.9853286e-01 4.2831769e-01 3.4756630e-01 2.8146997e-01 3.7279915e-01 3.4563396e-01 4.2926782e-01 4.2911586e-01 3.3459449e-01 3.4922354e-01 3.5000846e-01 4.9447633e-01 3.9856686e-01 3.5272682e-01 3.7871114e-01 4.3449948e-01 3.2004729e-01 3.4327468e-01 3.4109263e-01 2.9987407e-01 2.9431934e-01 4.1611792e-01 3.2872232e-01 3.8562509e-01 2.9576964e-01 4.2431958e-01 3.1716861e-01 4.1652649e-01 3.7009695e-01 3.6972936e-01 3.4546743e-01 2.8647014e-01 3.1433567e-01 3.7453491e-01 2.8958559e-01 3.9719792e-01 3.8739786e-01 3.6946063e-01 3.2697767e-01 3.6662992e-01 3.2048201e-01 3.3967430e-01 3.4047263e-01 2.8241524e-02 1.4713791e-03 4.6823304e-04 6.0296352e-03 5.2071190e-03 3.4182869e-03 1.9529381e-03 9.1025104e-04 1.8441969e-03 2.3388660e-01 2.3348281e-01 2.7404368e-01 3.1787596e-01 2.8991570e-01 3.1331298e-01 2.5580945e-01 2.1274052e-01 2.6625865e-01 2.6571335e-01 2.9921857e-01 2.3779036e-01 2.8985951e-01 3.0846478e-01 1.7343111e-01 2.2024476e-01 3.0259960e-01 2.5097696e-01 3.7082192e-01 2.6053197e-01 3.1606460e-01 2.2298918e-01 3.7823337e-01 3.1251210e-01 2.3776110e-01 2.3444923e-01 2.9645330e-01 3.1548927e-01 2.8938607e-01 1.8922982e-01 2.6439849e-01 2.4655891e-01 2.2974757e-01 4.1142478e-01 3.1887022e-01 2.2998734e-01 2.5821680e-01 3.2732410e-01 2.3197317e-01 2.8887378e-01 3.3359010e-01 2.8195681e-01 2.5599107e-01 2.2312925e-01 2.8613603e-01 2.3977955e-01 2.5441596e-01 2.4519140e-01 1.5385241e-01 2.5232104e-01 5.1493631e-01 4.5323214e-01 4.2845834e-01 4.4724986e-01 4.7457294e-01 4.7489714e-01 4.6451744e-01 4.5458397e-01 4.8531016e-01 4.0086775e-01 3.3039510e-01 4.2690815e-01 3.9810528e-01 4.8619268e-01 4.8572861e-01 3.8678000e-01 4.0288412e-01 4.0400345e-01 5.5371159e-01 4.5396321e-01 4.0578110e-01 4.3384503e-01 4.9180921e-01 3.7075981e-01 3.9660943e-01 3.9426853e-01 3.4955388e-01 3.4443823e-01 4.7269976e-01 3.8095285e-01 4.4033063e-01 3.4628429e-01 4.8107810e-01 3.6885113e-01 4.7400892e-01 4.2299055e-01 4.2466221e-01 3.9913020e-01 3.3607659e-01 3.6477722e-01 4.2848136e-01 3.3695835e-01 4.5323214e-01 4.4278413e-01 4.2356519e-01 3.7724050e-01 4.1943108e-01 3.7167660e-01 3.9296882e-01 3.9389283e-01 3.3787081e-02 2.1999955e-02 3.5606869e-02 9.7919779e-03 3.8730614e-02 2.5164827e-02 2.6849118e-02 1.7903003e-02 1.2662420e-01 1.3061214e-01 1.5776511e-01 1.8787898e-01 1.6611889e-01 2.0099100e-01 1.5292701e-01 1.1474225e-01 1.5177722e-01 1.5820095e-01 1.7289957e-01 1.3332512e-01 1.6306194e-01 1.9266388e-01 8.3782583e-02 1.1475472e-01 1.9583103e-01 1.4727273e-01 2.2350713e-01 1.4824286e-01 2.0360977e-01 1.1578494e-01 2.3876064e-01 1.9766401e-01 1.2872248e-01 1.2487918e-01 1.7184761e-01 1.8912261e-01 1.7398714e-01 9.1697206e-02 1.4908080e-01 1.3596066e-01 1.2380963e-01 2.7656824e-01 2.1428571e-01 1.3903777e-01 1.4563130e-01 1.9085533e-01 1.3814153e-01 1.6927103e-01 2.1683317e-01 1.7245190e-01 1.4312164e-01 1.1792045e-01 1.7357532e-01 1.4591400e-01 1.5204372e-01 1.3733781e-01 6.4362895e-02 1.4610010e-01 3.6994724e-01 3.0922665e-01 2.8172128e-01 3.0981046e-01 3.2683078e-01 3.2376302e-01 3.2734273e-01 3.0972403e-01 3.3079457e-01 2.6398840e-01 2.0377846e-01 2.7991998e-01 2.5479336e-01 3.3054260e-01 3.2764011e-01 2.4845492e-01 2.6936921e-01 2.7648193e-01 3.8578015e-01 3.0244531e-01 2.6262990e-01 2.9337350e-01 3.3673416e-01 2.3057418e-01 2.6437275e-01 2.6189855e-01 2.1511723e-01 2.1874548e-01 3.2151629e-01 2.4786804e-01 2.9060022e-01 2.2492263e-01 3.2669967e-01 2.4001378e-01 3.3739817e-01 2.6941492e-01 2.9020759e-01 2.7043921e-01 2.1216140e-01 2.2627991e-01 2.7921868e-01 1.9599823e-01 3.0922665e-01 2.9841488e-01 2.7865157e-01 2.3076134e-01 2.6697481e-01 2.3479056e-01 2.6453179e-01 2.6724220e-01 1.8463349e-03 1.7603525e-03 7.3051568e-03 4.1015204e-04 7.6986614e-04 4.1505661e-04 2.5516535e-03 2.2715123e-01 2.2501703e-01 2.6621562e-01 3.1107232e-01 2.8349906e-01 3.0014485e-01 2.4486000e-01 2.0495206e-01 2.5858102e-01 2.5538755e-01 2.9285644e-01 2.2943000e-01 2.8485147e-01 2.9693404e-01 1.6780939e-01 2.1457958e-01 2.8854018e-01 2.4083271e-01 3.6655716e-01 2.5261802e-01 3.0270375e-01 2.1770871e-01 3.6954821e-01 3.0027680e-01 2.3128364e-01 2.2864524e-01 2.8964053e-01 3.0736347e-01 2.7955757e-01 1.8460266e-01 2.5729263e-01 2.3962699e-01 2.2298939e-01 3.9825299e-01 3.0280555e-01 2.1772566e-01 2.5069761e-01 3.2250864e-01 2.2044120e-01 2.8076678e-01 3.2019205e-01 2.7070478e-01 2.4885895e-01 2.1694127e-01 2.7557358e-01 2.2744661e-01 2.4343281e-01 2.3729093e-01 1.5163937e-01 2.4296011e-01 4.9867564e-01 4.4043288e-01 4.1855224e-01 4.3250726e-01 4.6172807e-01 4.6333132e-01 4.4859207e-01 4.4201028e-01 4.7437581e-01 3.8923920e-01 3.2088439e-01 4.1722959e-01 3.8928568e-01 4.7566007e-01 4.7625383e-01 3.7686783e-01 3.8986190e-01 3.8884081e-01 5.4387473e-01 4.4406349e-01 3.9623886e-01 4.2109532e-01 4.8065646e-01 3.6308022e-01 3.8359994e-01 3.8148432e-01 3.4142085e-01 3.3328357e-01 4.6130540e-01 3.6948155e-01 4.3073385e-01 3.3341169e-01 4.7038237e-01 3.5679054e-01 4.5732763e-01 4.1658437e-01 4.1040883e-01 3.8470265e-01 3.2499997e-01 3.5692838e-01 4.1963835e-01 3.3330111e-01 4.4043288e-01 4.3085698e-01 4.1330060e-01 3.7185723e-01 4.1281711e-01 3.6250007e-01 3.7890193e-01 3.7915604e-01 5.4235993e-03 2.5998420e-03 3.8677378e-03 1.1491859e-03 6.2877149e-04 4.8332372e-04 2.1486898e-01 2.1467854e-01 2.5369922e-01 2.9604720e-01 2.6891688e-01 2.9246555e-01 2.3653315e-01 1.9467620e-01 2.4616446e-01 2.4597983e-01 2.7792166e-01 2.1880759e-01 2.6876410e-01 2.8748855e-01 1.5684504e-01 2.0165721e-01 2.8227299e-01 2.3174975e-01 3.4738814e-01 2.4066356e-01 2.9516630e-01 2.0426320e-01 3.5491429e-01 2.9152419e-01 2.1857252e-01 2.1532575e-01 2.7527993e-01 2.9385335e-01 2.6877316e-01 1.7184619e-01 2.4431046e-01 2.2708854e-01 2.1089056e-01 3.8786051e-01 2.9845589e-01 2.1195408e-01 2.3838722e-01 3.0508869e-01 2.1371586e-01 2.6806783e-01 3.1216051e-01 2.6180490e-01 2.3620065e-01 2.0447438e-01 2.6573552e-01 2.2136814e-01 2.3519672e-01 2.2587537e-01 1.3797486e-01 2.3292930e-01 4.8968343e-01 4.2865553e-01 4.0406082e-01 4.2311568e-01 4.4955467e-01 4.4970134e-01 4.4022012e-01 4.2994584e-01 4.5983149e-01 3.7733154e-01 3.0847182e-01 4.0252124e-01 3.7430526e-01 4.6065171e-01 4.6008825e-01 3.6337620e-01 3.7950484e-01 3.8097080e-01 5.2689771e-01 4.2900834e-01 3.8187058e-01 4.0969052e-01 4.6622956e-01 3.4753954e-01 3.7338441e-01 3.7106677e-01 3.2693971e-01 3.2232718e-01 4.4752724e-01 3.5789640e-01 4.1563695e-01 3.2438944e-01 4.5565562e-01 3.4619907e-01 4.4965557e-01 3.9841763e-01 4.0096111e-01 3.7608333e-01 3.1419485e-01 3.4172782e-01 4.0397551e-01 3.1440281e-01 4.2865553e-01 4.1831326e-01 3.9931889e-01 3.5369559e-01 3.9495166e-01 3.4857729e-01 3.7001412e-01 3.7103653e-01 9.4891023e-03 8.2375707e-04 1.6686708e-03 2.4475618e-03 4.6565778e-03 2.0491959e-01 2.0139969e-01 2.4160221e-01 2.8586265e-01 2.5951451e-01 2.7003471e-01 2.1844761e-01 1.8272084e-01 2.3436629e-01 2.2908460e-01 2.6861177e-01 2.0572725e-01 2.6206958e-01 2.6821933e-01 1.4908933e-01 1.9377976e-01 2.5813756e-01 2.1522061e-01 3.4200281e-01 2.2841742e-01 2.7236034e-01 1.9712279e-01 3.4101997e-01 2.7089218e-01 2.0911514e-01 2.0716704e-01 2.6510721e-01 2.8112922e-01 2.5280689e-01 1.6599180e-01 2.3361443e-01 2.1674754e-01 2.0090153e-01 3.6527528e-01 2.7043122e-01 1.9136502e-01 2.2690330e-01 2.9871211e-01 1.9453190e-01 2.5541554e-01 2.8925890e-01 2.4310626e-01 2.2546391e-01 1.9558871e-01 2.4836004e-01 2.0063248e-01 2.1704777e-01 2.1367234e-01 1.3678274e-01 2.1790721e-01 4.6096128e-01 4.0671584e-01 3.8773373e-01 3.9744288e-01 4.2748862e-01 4.3009711e-01 4.1223718e-01 4.0843810e-01 4.4142325e-01 3.5773584e-01 2.9306179e-01 3.8663461e-01 3.6011993e-01 4.4301487e-01 4.4448732e-01 3.4710733e-01 3.5722182e-01 3.5456119e-01 5.1046759e-01 4.1264841e-01 3.6628224e-01 3.8788026e-01 4.4738884e-01 3.3559118e-01 3.5113614e-01 3.4925623e-01 3.1415212e-01 3.0373195e-01 4.2825709e-01 3.3863565e-01 3.9988417e-01 3.0248234e-01 4.3771445e-01 3.2582243e-01 4.2019828e-01 3.8883040e-01 3.7626984e-01 3.5109855e-01 2.9576205e-01 3.2946337e-01 3.8969349e-01 3.1023698e-01 4.0671584e-01 3.9807129e-01 3.8231042e-01 3.4615104e-01 3.8497123e-01 3.3374807e-01 3.4573433e-01 3.4546409e-01 9.9827550e-03 3.7862368e-03 4.2584946e-03 1.2265791e-03 1.7617949e-01 1.7703821e-01 2.1212343e-01 2.5052805e-01 2.2530971e-01 2.5127302e-01 1.9850334e-01 1.5861692e-01 2.0515179e-01 2.0665158e-01 2.3356888e-01 1.8070269e-01 2.2448284e-01 2.4549391e-01 1.2367511e-01 1.6367665e-01 2.4261509e-01 1.9359579e-01 2.9712394e-01 2.0027260e-01 2.5390638e-01 1.6582785e-01 3.0629971e-01 2.4971132e-01 1.7938888e-01 1.7609087e-01 2.3136866e-01 2.4921343e-01 2.2715314e-01 1.3644726e-01 2.0313672e-01 1.8736201e-01 1.7259709e-01 3.3997757e-01 2.5916969e-01 1.7732718e-01 1.9796934e-01 2.5795325e-01 1.7832877e-01 2.2545225e-01 2.6961076e-01 2.2173788e-01 1.9575867e-01 1.6649347e-01 2.2485533e-01 1.8584864e-01 1.9731550e-01 1.8682160e-01 1.0543428e-01 1.9413835e-01 4.3845459e-01 3.7813833e-01 3.5313427e-01 3.7426892e-01 3.9794201e-01 3.9726718e-01 3.9130315e-01 3.7920848e-01 4.0649770e-01 3.2906316e-01 2.6354873e-01 3.5155280e-01 3.2453882e-01 4.0704153e-01 4.0591144e-01 3.1492301e-01 3.3203733e-01 3.3498583e-01 4.6984073e-01 3.7668481e-01 3.3204804e-01 3.6021418e-01 4.1270518e-01 2.9886261e-01 3.2629567e-01 3.2396944e-01 2.7989788e-01 2.7743776e-01 3.9510115e-01 3.1077003e-01 3.6387709e-01 2.8054253e-01 4.0239292e-01 3.0024699e-01 4.0081171e-01 3.4598760e-01 3.5305310e-01 3.2985030e-01 2.6984561e-01 2.9352511e-01 3.5245796e-01 2.6611615e-01 3.7813833e-01 3.6780473e-01 3.4887718e-01 3.0350575e-01 3.4281917e-01 3.0065558e-01 3.2390437e-01 3.2536506e-01 1.4872402e-03 1.4261828e-03 4.3370414e-03 2.2707675e-01 2.2400493e-01 2.6569285e-01 3.1123078e-01 2.8378554e-01 2.9683242e-01 2.4257696e-01 2.0422363e-01 2.5811380e-01 2.5348079e-01 2.9319960e-01 2.2849166e-01 2.8589147e-01 2.9450563e-01 1.6807288e-01 2.1502765e-01 2.8470332e-01 2.3897250e-01 3.6819788e-01 2.5200370e-01 2.9929335e-01 2.1837402e-01 3.6884033e-01 2.9747447e-01 2.3136263e-01 2.2907381e-01 2.8973378e-01 3.0680413e-01 2.7799960e-01 1.8547528e-01 2.5712752e-01 2.3949352e-01 2.2288675e-01 3.9517754e-01 2.9790586e-01 2.1461980e-01 2.5028856e-01 3.2375470e-01 2.1774627e-01 2.8014020e-01 3.1681085e-01 2.6834640e-01 2.4865097e-01 2.1711982e-01 2.7360556e-01 2.2434130e-01 2.4112622e-01 2.3662943e-01 1.5362542e-01 2.4153192e-01 4.9407199e-01 4.3763583e-01 4.1728329e-01 4.2863716e-01 4.5893922e-01 4.6123811e-01 4.4410036e-01 4.3933832e-01 4.7263926e-01 3.8698450e-01 3.1961653e-01 4.1608078e-01 3.8854631e-01 4.7414193e-01 4.7530563e-01 3.7551546e-01 3.8685112e-01 3.8465728e-01 5.4280771e-01 4.4284025e-01 3.9512469e-01 4.1829234e-01 4.7881043e-01 3.6289720e-01 3.8058317e-01 3.7858579e-01 3.4094528e-01 3.3115887e-01 4.5930198e-01 3.6727055e-01 4.2965213e-01 3.3035276e-01 4.6876907e-01 3.5423104e-01 4.5243416e-01 4.1718566e-01 4.0676646e-01 3.8091352e-01 3.2289623e-01 3.5664111e-01 4.1894270e-01 3.3518670e-01 4.3763583e-01 4.2851737e-01 4.1182914e-01 3.7291664e-01 4.1330116e-01 3.6151280e-01 3.7529785e-01 3.7518553e-01 2.1466294e-04 8.1135795e-04 2.0395325e-01 2.0232995e-01 2.4147692e-01 2.8429935e-01 2.5773107e-01 2.7550146e-01 2.2191392e-01 1.8311464e-01 2.3414066e-01 2.3177254e-01 2.6670079e-01 2.0650082e-01 2.5876572e-01 2.7190067e-01 1.4755667e-01 1.9178198e-01 2.6468375e-01 2.1783813e-01 3.3738596e-01 2.2849816e-01 2.7802206e-01 1.9468150e-01 3.4113386e-01 2.7532568e-01 2.0783209e-01 2.0518365e-01 2.6370466e-01 2.8101900e-01 2.5475268e-01 1.6319612e-01 2.3278117e-01 2.1587899e-01 1.9999930e-01 3.7014440e-01 2.7911349e-01 1.9654509e-01 2.2657275e-01 2.9494234e-01 1.9887441e-01 2.5547815e-01 2.9483790e-01 2.4668408e-01 2.2473055e-01 1.9413008e-01 2.5114079e-01 2.0579387e-01 2.2056413e-01 2.1387343e-01 1.3181835e-01 2.1964768e-01 4.6883654e-01 4.1098091e-01 3.8893009e-01 4.0388763e-01 4.3171852e-01 4.3291890e-01 4.1992695e-01 4.1244869e-01 4.4351517e-01 3.6095375e-01 2.9436456e-01 3.8758959e-01 3.6027619e-01 4.4466450e-01 4.4498549e-01 3.4851465e-01 3.6196876e-01 3.6168475e-01 5.1115719e-01 4.1372900e-01 3.6718805e-01 3.9217531e-01 4.4969696e-01 3.3465314e-01 3.5590188e-01 3.5378490e-01 3.1382959e-01 3.0675099e-01 4.3089908e-01 3.4178657e-01 4.0069101e-01 3.0739171e-01 4.3956059e-01 3.2969272e-01 4.2869495e-01 3.8625531e-01 3.8227459e-01 3.5742723e-01 2.9874912e-01 3.2874265e-01 3.8973129e-01 3.0516637e-01 4.1098091e-01 4.0141982e-01 3.8392089e-01 3.4269880e-01 3.8263609e-01 3.3443392e-01 3.5169446e-01 3.5216636e-01 9.1517643e-04 2.1524074e-01 2.1387759e-01 2.5373107e-01 2.9714785e-01 2.7003778e-01 2.8920001e-01 2.3426287e-01 1.9410928e-01 2.4622443e-01 2.4423617e-01 2.7915984e-01 2.1812334e-01 2.7081916e-01 2.8531140e-01 1.5727134e-01 2.0260081e-01 2.7829111e-01 2.2998414e-01 3.5056990e-01 2.4050939e-01 2.9179089e-01 2.0549251e-01 3.5521699e-01 2.8889714e-01 2.1915718e-01 2.1632646e-01 2.7619378e-01 2.9404932e-01 2.6759110e-01 1.7313032e-01 2.4473695e-01 2.2745766e-01 2.1120369e-01 3.8534612e-01 2.9320974e-01 2.0849982e-01 2.3847660e-01 3.0758274e-01 2.1079215e-01 2.6804702e-01 3.0890781e-01 2.5958017e-01 2.3652855e-01 2.0509955e-01 2.6402277e-01 2.1797131e-01 2.3288914e-01 2.2557846e-01 1.4040598e-01 2.3171289e-01 4.8566100e-01 4.2666426e-01 4.0387929e-01 4.1975185e-01 4.4765854e-01 4.4867725e-01 4.3614287e-01 4.2811681e-01 4.5929331e-01 3.7580128e-01 3.0785579e-01 4.0248311e-01 3.7465557e-01 4.6038945e-01 4.6053443e-01 3.6291292e-01 3.7703492e-01 3.7702914e-01 5.2744561e-01 4.2898757e-01 3.8178676e-01 4.0761255e-01 4.6557959e-01 3.4846205e-01 3.7087965e-01 3.6870032e-01 3.2740185e-01 3.2070546e-01 4.4660679e-01 3.5633353e-01 4.1573756e-01 3.2160319e-01 4.5525495e-01 3.4414484e-01 4.4510906e-01 4.0053809e-01 3.9779009e-01 3.7261777e-01 3.1255985e-01 3.4249381e-01 4.0450836e-01 3.1773210e-01 4.2666426e-01 4.1685273e-01 3.9886436e-01 3.5618753e-01 3.9691207e-01 3.4850257e-01 3.6675459e-01 3.6731925e-01 1.9628524e-01 1.9595152e-01 2.3363495e-01 2.7477230e-01 2.4851118e-01 2.7084921e-01 2.1685944e-01 1.7678722e-01 2.2636857e-01 2.2599655e-01 2.5724256e-01 1.9993208e-01 2.4854721e-01 2.6607935e-01 1.4078995e-01 1.8369551e-01 2.6097312e-01 2.1228442e-01 3.2508389e-01 2.2103528e-01 2.7346255e-01 1.8624671e-01 3.3183707e-01 2.6996197e-01 1.9987779e-01 1.9682726e-01 2.5462868e-01 2.7249816e-01 2.4805238e-01 1.5523356e-01 2.2463473e-01 2.0803519e-01 1.9245485e-01 3.6364465e-01 2.7666661e-01 1.9319723e-01 2.1887484e-01 2.8382186e-01 1.9489313e-01 2.4750809e-01 2.8994226e-01 2.4123726e-01 2.1680288e-01 1.8634448e-01 2.4506974e-01 2.0223799e-01 2.1557050e-01 2.0677515e-01 1.2326853e-01 2.1346465e-01 4.6321322e-01 4.0354170e-01 3.7970298e-01 3.9803381e-01 4.2400584e-01 4.2423988e-01 4.1474064e-01 4.0481837e-01 4.3422436e-01 3.5345426e-01 2.8656464e-01 3.7821934e-01 3.5073976e-01 4.3506541e-01 4.3461895e-01 3.3997143e-01 3.5549675e-01 3.5686166e-01 5.0021759e-01 4.0411514e-01 3.5805390e-01 3.8499293e-01 4.4048074e-01 3.2475847e-01 3.4952686e-01 3.4727706e-01 3.0464816e-01 2.9991017e-01 4.2212289e-01 3.3451084e-01 3.9105629e-01 3.0183284e-01 4.3015090e-01 3.2308204e-01 4.2396798e-01 3.7459262e-01 3.7639485e-01 3.5210962e-01 2.9201270e-01 3.1907654e-01 3.7972706e-01 2.9306033e-01 4.0354170e-01 3.9347753e-01 3.7503434e-01 3.3106203e-01 3.7117506e-01 3.2561211e-01 3.4620183e-01 3.4718282e-01 5.4794269e-04 1.8595150e-03 7.6486933e-03 3.6709934e-03 1.1297771e-02 3.2211024e-03 9.3536855e-04 1.2319946e-03 2.8277178e-03 4.9162548e-03 4.5756064e-04 4.5051431e-03 8.2041162e-03 5.0174936e-03 3.6228961e-04 1.1821527e-02 2.0977093e-03 2.1132418e-02 9.2067605e-04 1.2006919e-02 4.4257629e-04 2.0287770e-02 9.6871594e-03 3.6190304e-05 1.5767395e-04 4.3807979e-03 7.0604016e-03 4.2468409e-03 2.9914879e-03 1.0695136e-03 1.8996353e-04 2.2349983e-05 3.2007300e-02 1.8626112e-02 5.9280137e-03 7.1455899e-04 1.0704644e-02 4.3625889e-03 3.3835317e-03 1.4689427e-02 5.0222514e-03 5.6899707e-04 1.6818673e-04 4.5805421e-03 5.6585758e-03 3.2371570e-03 3.2354233e-04 1.1207565e-02 1.3610741e-03 7.1737216e-02 4.4570224e-02 3.4818230e-02 4.5037397e-02 5.2043753e-02 5.1252208e-02 5.2740741e-02 4.4819381e-02 5.4844272e-02 2.7492078e-02 9.9689819e-03 3.4289978e-02 2.5632216e-02 5.5069559e-02 5.4817042e-02 2.2717491e-02 2.9451557e-02 3.3085492e-02 8.2968826e-02 4.3268668e-02 2.7878625e-02 3.8225012e-02 5.7392372e-02 1.8533113e-02 2.7752880e-02 2.6863838e-02 1.3522246e-02 1.3720834e-02 5.0366251e-02 2.2136217e-02 3.8620607e-02 1.6104597e-02 5.3184340e-02 1.9805362e-02 5.7449954e-02 3.4065200e-02 3.7316903e-02 3.0490470e-02 1.2067932e-02 1.7032969e-02 3.4772493e-02 1.3995163e-02 4.4570224e-02 4.0370599e-02 3.3379869e-02 2.1584097e-02 3.2788169e-02 1.8689520e-02 2.8313400e-02 2.9690702e-02 2.1810130e-03 9.0584977e-03 5.0419708e-03 8.6169391e-03 1.3497185e-03 5.5621011e-04 1.5626898e-03 1.4715788e-03 6.4159866e-03 2.8759099e-05 6.7456588e-03 6.5333041e-03 5.4642837e-03 1.4108585e-03 8.4463171e-03 6.8795048e-04 2.4580787e-02 1.0388021e-03 9.2286561e-03 1.7639378e-03 2.1069750e-02 7.6032385e-03 7.9175727e-04 1.2932915e-03 5.5725803e-03 7.6219263e-03 3.4673161e-03 4.2795834e-03 1.7380404e-03 7.6483144e-04 5.0584546e-04 3.0216137e-02 1.4096242e-02 2.8861882e-03 1.0548294e-03 1.3430225e-02 1.8234348e-03 3.6876972e-03 1.2061426e-02 3.2511669e-03 1.1268214e-03 9.2690858e-04 3.2976984e-03 2.6938518e-03 1.3276664e-03 2.7368625e-04 1.3465991e-02 4.6822375e-04 6.8703048e-02 4.3340061e-02 3.5237451e-02 4.2511976e-02 5.0937508e-02 5.0961712e-02 4.9575019e-02 4.3743233e-02 5.5024281e-02 2.6603107e-02 9.8022959e-03 3.4840370e-02 2.6494597e-02 5.5508568e-02 5.5916268e-02 2.2800218e-02 2.7686924e-02 2.9943438e-02 8.4266289e-02 4.3880838e-02 2.8349604e-02 3.6881146e-02 5.7478275e-02 1.9879185e-02 2.5944410e-02 2.5180647e-02 1.4399125e-02 1.2641568e-02 5.0169037e-02 2.1180767e-02 3.9323276e-02 1.3938661e-02 5.3485998e-02 1.8367828e-02 5.3859405e-02 3.6620288e-02 3.4933732e-02 2.7786245e-02 1.0959937e-02 1.8222006e-02 3.5858197e-02 1.7515874e-02 4.3340061e-02 3.9619354e-02 3.3535832e-02 2.4403195e-02 3.5188163e-02 1.9107255e-02 2.5791231e-02 2.6740983e-02 2.3644893e-03 7.3259797e-04 5.5696730e-03 2.5707992e-03 4.5988577e-03 6.5028014e-05 1.3580837e-03 1.2238983e-03 1.7109321e-03 1.8714646e-03 2.9944669e-03 1.2905109e-02 3.6725511e-03 6.9487479e-03 1.9516582e-03 1.2369689e-02 2.1307052e-04 6.0423668e-03 3.5751225e-03 1.0028727e-02 4.0861108e-03 1.5902610e-03 2.1378325e-03 8.2628456e-04 1.7251178e-03 8.0406810e-04 9.4788056e-03 1.5999623e-04 8.7509151e-04 2.2560004e-03 1.8732619e-02 1.2379328e-02 7.5803848e-03 2.7223442e-04 5.1089250e-03 5.8562851e-03 2.2798360e-04 7.3027135e-03 2.0605789e-03 3.8651941e-04 3.1125030e-03 1.3286042e-03 6.3896783e-03 2.7088758e-03 9.7424018e-04 2.1879745e-02 1.1792881e-03 5.1158538e-02 2.8393852e-02 2.0746802e-02 2.9099880e-02 3.4416089e-02 3.3763806e-02 3.5540899e-02 2.8575694e-02 3.6757319e-02 1.5144171e-02 3.2315336e-03 2.0363783e-02 1.3898176e-02 3.6986917e-02 3.6943012e-02 1.1630303e-02 1.6809451e-02 2.0213112e-02 6.0723696e-02 2.7441217e-02 1.5470812e-02 2.3398093e-02 3.8846172e-02 9.0112370e-03 1.5570624e-02 1.4874310e-02 5.5171614e-03 5.6710746e-03 3.3048406e-02 1.1270408e-02 2.3776077e-02 7.7482583e-03 3.5413096e-02 9.7831561e-03 3.9569593e-02 2.1047269e-02 2.3004495e-02 1.8015778e-02 4.6822715e-03 7.9260822e-03 2.0880056e-02 7.9428824e-03 2.8393852e-02 2.5015882e-02 1.9595645e-02 1.2073748e-02 1.9972960e-02 8.8353740e-03 1.6316616e-02 1.7585650e-02 7.5921246e-04 7.6581519e-03 8.7602132e-03 1.3285408e-02 3.1081233e-03 6.1183864e-03 3.4258252e-04 8.0693053e-03 1.1527108e-03 4.3989827e-03 2.4770294e-02 1.0297780e-02 1.0716436e-02 8.0921947e-03 4.0468338e-03 3.9682629e-03 8.0100500e-03 9.6963228e-03 3.4263963e-03 5.5915243e-03 6.8271736e-03 7.3405400e-03 4.5375223e-04 3.3086446e-04 2.9090971e-03 1.8954919e-02 3.0536401e-03 5.4665114e-03 8.4934379e-03 1.2174442e-02 1.6139108e-02 1.7445625e-02 4.0248005e-03 8.4214697e-04 1.4968032e-02 1.3210674e-03 7.6674238e-03 5.8292743e-03 4.1363018e-03 9.7236406e-03 4.2254644e-03 1.5250051e-02 9.0649565e-03 6.2807525e-03 3.4303230e-02 6.6673397e-03 3.8666536e-02 1.8227060e-02 1.0732501e-02 2.0722480e-02 2.2627004e-02 2.1137328e-02 2.6644819e-02 1.8156140e-02 2.2956019e-02 8.4429075e-03 1.3075172e-03 1.0338488e-02 5.7120788e-03 2.2877982e-02 2.2249653e-02 5.0348740e-03 1.0858478e-02 1.5670368e-02 4.1328272e-02 1.5415087e-02 7.0760309e-03 1.4735153e-02 2.4690692e-02 2.4709117e-03 1.0136092e-02 9.4846153e-03 1.0671740e-03 3.5238125e-03 2.0483172e-02 6.1432951e-03 1.2637418e-02 6.5903261e-03 2.1786492e-02 6.0819601e-03 3.0486925e-02 9.5174641e-03 1.6103409e-02 1.3353819e-02 3.2356456e-03 1.9926682e-03 1.0243487e-02 2.2538296e-03 1.8227060e-02 1.5108364e-02 1.0188754e-02 3.7627514e-03 8.8362152e-03 3.0791096e-03 1.1920070e-02 1.3588585e-02 8.0203401e-03 5.9881494e-03 7.9998519e-03 1.0330180e-03 3.9563353e-03 9.0962406e-05 4.3155161e-03 3.4798297e-04 4.5913809e-03 1.6934319e-02 5.4778348e-03 1.0415690e-02 5.0724002e-03 7.4143773e-03 1.5625174e-03 8.5090504e-03 5.0363885e-03 7.2978538e-03 5.9687085e-03 3.0800386e-03 3.3854665e-03 6.0369763e-05 9.7766526e-04 2.1106407e-03 1.2150960e-02 8.7050457e-04 2.2450596e-03 4.2656078e-03 1.7573546e-02 1.6541615e-02 1.3010703e-02 1.4863348e-03 2.0318224e-03 1.0715058e-02 5.1959240e-04 9.1091800e-03 4.5577281e-03 1.4679901e-03 5.0882044e-03 3.2208315e-03 1.1416637e-02 6.2084512e-03 2.9761078e-03 2.5010280e-02 3.7556543e-03 4.8641699e-02 2.5613136e-02 1.7022941e-02 2.7738185e-02 3.0983818e-02 2.9540677e-02 3.4381880e-02 2.5612023e-02 3.1832087e-02 1.3388731e-02 2.7364434e-03 1.6556686e-02 1.0555947e-02 3.1795721e-02 3.1144834e-02 9.3248236e-03 1.5850102e-02 2.0586917e-02 5.3133864e-02 2.2885657e-02 1.2291162e-02 2.1220606e-02 3.3851933e-02 5.9611654e-03 1.4821288e-02 1.4065156e-02 3.4935277e-03 5.6486577e-03 2.8790647e-02 1.0114487e-02 1.9481208e-02 8.7344459e-03 3.0476411e-02 9.4253713e-03 3.8601009e-02 1.5492941e-02 2.2081481e-02 1.8082534e-02 4.9261084e-03 5.1866248e-03 1.6531969e-02 3.8794099e-03 2.5613136e-02 2.2037756e-02 1.6246851e-02 7.5034139e-03 1.4652134e-02 6.6254167e-03 1.6367612e-02 1.8037074e-02 3.5963937e-03 1.3253217e-02 6.2726815e-03 2.9800542e-03 8.1679414e-03 8.1170586e-03 1.1643531e-02 4.8148791e-04 2.7366574e-02 1.5666050e-02 3.5602925e-04 4.6080605e-03 1.9717886e-02 6.2846724e-03 1.0858138e-05 1.6108248e-02 9.2526017e-03 1.6342536e-04 1.1289374e-02 1.3182521e-02 7.1086671e-03 4.8107615e-03 2.1505038e-03 2.4867835e-02 7.4585494e-03 9.3457001e-03 1.1859292e-02 8.6506765e-03 1.5825531e-03 7.6287797e-03 7.1464196e-03 1.3469318e-02 7.0587993e-03 4.6011946e-03 3.8423475e-04 1.2933991e-03 8.0463849e-03 1.4044313e-02 1.5554975e-03 5.8246512e-03 3.7445923e-03 7.8179143e-03 4.3383609e-02 5.1292419e-03 3.1208966e-02 1.7106674e-02 1.5590525e-02 1.4711786e-02 2.2118312e-02 2.3677515e-02 1.8355265e-02 1.7620984e-02 2.7322237e-02 8.2125300e-03 3.2025084e-03 1.5673958e-02 1.1993026e-02 2.8215754e-02 3.0043744e-02 8.3225318e-03 7.3692394e-03 7.0283195e-03 5.0297559e-02 2.1175797e-02 1.1957606e-02 1.3050611e-02 2.8749586e-02 1.0337000e-02 6.4346633e-03 6.2339245e-03 7.0135662e-03 2.1302546e-03 2.3356399e-02 5.5096838e-03 1.8637999e-02 1.1462623e-03 2.6577654e-02 3.5495923e-03 2.0668821e-02 2.2280093e-02 1.0461671e-02 6.3164945e-03 1.6640428e-03 9.2261377e-03 1.7584245e-02 1.7910761e-02 1.7106674e-02 1.5703153e-02 1.4029303e-02 1.7152691e-02 2.0996496e-02 7.4959989e-03 5.5175599e-03 5.5902559e-03 3.2004692e-03 2.3419030e-03 2.3799055e-04 7.0894399e-03 1.3013007e-03 8.8089668e-03 2.9046172e-03 1.1317768e-02 5.4246845e-03 3.1331888e-03 1.2023171e-04 2.4596565e-02 1.8369557e-03 3.9714617e-03 5.9789699e-03 1.7350836e-02 3.3069650e-03 3.5250221e-03 4.6752456e-03 5.9850395e-03 6.3682068e-03 1.7841601e-03 1.0289989e-02 3.0278347e-03 2.8250687e-03 3.3130538e-03 2.2020473e-02 6.7612416e-03 1.5121522e-03 2.2413165e-03 1.4397994e-02 9.1427197e-04 3.3482085e-03 6.1881321e-03 7.4537620e-04 2.6935839e-03 4.4237033e-03 1.1678481e-03 9.0068892e-04 2.9616334e-06 1.6314282e-03 2.2817755e-02 4.4730602e-04 5.5111404e-02 3.4158218e-02 2.9021577e-02 3.2043073e-02 4.1089749e-02 4.2083473e-02 3.7707137e-02 3.4692829e-02 4.6329738e-02 1.9906109e-02 6.9820129e-03 2.8847990e-02 2.2051442e-02 4.7099521e-02 4.8343924e-02 1.7860817e-02 1.9868896e-02 2.0488578e-02 7.4490873e-02 3.6906910e-02 2.3107397e-02 2.8319511e-02 4.8426947e-02 1.7182411e-02 1.8331671e-02 1.7811306e-02 1.2023707e-02 8.1012404e-03 4.1487060e-02 1.5227136e-02 3.2990763e-02 8.0392167e-03 4.5098943e-02 1.2350504e-02 4.1149609e-02 3.3318195e-02 2.5517275e-02 1.9030633e-02 6.7530681e-03 1.5581926e-02 3.0469465e-02 1.9100479e-02 3.4158218e-02 3.1415243e-02 2.7190491e-02 2.3321177e-02 3.1828581e-02 1.5212907e-02 1.7498443e-02 1.7919743e-02 3.6042298e-03 3.7468828e-03 9.7768723e-03 7.8664808e-04 9.4997776e-03 1.0849175e-02 2.5690262e-03 9.8725606e-04 1.2645853e-02 2.2464135e-03 3.0594913e-02 2.8398433e-03 1.3996699e-02 1.4253747e-03 2.8011999e-02 1.2147431e-02 1.3365379e-03 1.5899927e-03 8.8722552e-03 1.1913349e-02 6.7897739e-03 2.1135064e-03 3.6254453e-03 1.7898196e-03 6.9240260e-04 3.8891340e-02 1.9082524e-02 3.5297395e-03 2.7024302e-03 1.7766622e-02 2.5342822e-03 6.8092652e-03 1.7593080e-02 6.3527044e-03 2.6240465e-03 7.1495011e-04 6.5371413e-03 3.8357773e-03 3.1136154e-03 1.3436442e-03 8.9702416e-03 2.0253115e-03 8.1348350e-02 5.3552444e-02 4.4248656e-02 5.2648527e-02 6.1923652e-02 6.1838703e-02 6.0414517e-02 5.3985372e-02 6.6201925e-02 3.4742321e-02 1.4883289e-02 4.3767758e-02 3.4228373e-02 6.6672589e-02 6.6939218e-02 3.0219175e-02 3.6020463e-02 3.8455365e-02 9.7619080e-02 5.3827294e-02 3.6463335e-02 4.6373140e-02 6.8906148e-02 2.6398982e-02 3.4032020e-02 3.3159361e-02 2.0120628e-02 1.8469842e-02 6.0950543e-02 2.8524017e-02 4.8737718e-02 1.9976319e-02 6.4489083e-02 2.5275791e-02 6.5080438e-02 4.4969016e-02 4.4191042e-02 3.6066964e-02 1.6430448e-02 2.4524789e-02 4.4743887e-02 2.2062365e-02 5.3552444e-02 4.9370398e-02 4.2403953e-02 3.0850071e-02 4.3434971e-02 2.5846885e-02 3.3807321e-02 3.4832107e-02 1.2988107e-03 1.6703175e-03 1.1675843e-03 2.1414615e-03 3.6300242e-03 1.1143098e-02 2.7878082e-03 7.4699291e-03 1.6417856e-03 1.3833381e-02 5.5119833e-05 6.7870415e-03 2.7321459e-03 1.1691818e-02 4.7830259e-03 1.0265050e-03 1.5056614e-03 1.2372212e-03 2.4522309e-03 1.1336677e-03 8.0050923e-03 5.6870746e-05 4.6915622e-04 1.5562032e-03 2.0887194e-02 1.3125417e-02 6.8837382e-03 7.1162950e-05 5.9522504e-03 5.2043728e-03 5.3649750e-04 8.3200456e-03 2.2696632e-03 1.4322584e-04 2.2876100e-03 1.6070427e-03 5.8684470e-03 2.4558869e-03 5.5570590e-04 1.9651663e-02 8.9172519e-04 5.4659464e-02 3.1114613e-02 2.3119551e-02 3.1736271e-02 3.7415588e-02 3.6761852e-02 3.8397207e-02 3.1313890e-02 3.9883303e-02 1.7150840e-02 4.2007421e-03 2.2713095e-02 1.5842502e-02 4.0120080e-02 4.0057089e-02 1.3428356e-02 1.8845203e-02 2.2251079e-02 6.4673609e-02 3.0152373e-02 1.7530451e-02 2.5859899e-02 4.2056336e-02 1.0552527e-02 1.7518114e-02 1.6789150e-02 6.7575680e-03 6.8601014e-03 3.6016991e-02 1.2994930e-02 2.6303908e-02 8.9623653e-03 3.8482980e-02 1.1333705e-02 4.2542187e-02 2.3287141e-02 2.5337550e-02 1.9991634e-02 5.7451264e-03 9.3841364e-03 2.3239540e-02 8.8973019e-03 3.1114613e-02 2.7595638e-02 2.1905943e-02 1.3658827e-02 2.2169613e-02 1.0410758e-02 1.8207584e-02 1.9484294e-02 4.8008453e-03 1.2650854e-03 6.4006179e-03 1.8900850e-03 1.2501460e-02 5.1363105e-03 3.0955001e-03 2.4385845e-04 2.0020418e-02 1.0205842e-03 3.3467158e-03 5.5033857e-03 1.3788482e-02 2.4101593e-03 2.9461849e-03 4.0056193e-03 3.8916824e-03 4.1559722e-03 7.4799809e-04 1.0679921e-02 1.8732675e-03 2.1213449e-03 3.0477499e-03 1.9047032e-02 6.9834653e-03 2.9438547e-03 1.3758132e-03 1.0992441e-02 2.0426183e-03 1.8183256e-03 5.1275661e-03 3.4870074e-04 1.7800086e-03 4.1941671e-03 4.4278656e-04 2.0594764e-03 2.9165015e-04 1.2452133e-03 2.3762069e-02 2.9165015e-04 5.0966362e-02 3.0166832e-02 2.4694798e-02 2.8788397e-02 3.6671707e-02 3.7274280e-02 3.4472569e-02 3.0604199e-02 4.1125938e-02 1.6692997e-02 4.7977197e-03 2.4489085e-02 1.8077937e-02 4.1761735e-02 4.2713992e-02 1.4455884e-02 1.7039782e-02 1.8322490e-02 6.7670508e-02 3.2041093e-02 1.9169462e-02 2.4724400e-02 4.3157837e-02 1.3510623e-02 1.5640518e-02 1.5102883e-02 8.9767687e-03 6.0642978e-03 3.6671707e-02 1.2419694e-02 2.8323093e-02 6.4935421e-03 3.9910361e-02 1.0006286e-02 3.7967777e-02 2.8141888e-02 2.2594035e-02 1.6734859e-02 4.8977754e-03 1.2095984e-02 2.5839912e-02 1.5166518e-02 3.0166832e-02 2.7378800e-02 2.3072794e-02 1.8885101e-02 2.6783663e-02 1.1941964e-02 1.5226991e-02 1.5844953e-02 5.5898027e-03 3.6211515e-04 4.6884489e-03 1.9469863e-02 6.9494812e-03 1.0856564e-02 6.2070186e-03 5.9174289e-03 2.3319364e-03 8.6215938e-03 6.4261295e-03 5.9157019e-03 6.0481987e-03 4.2268867e-03 4.5610911e-03 4.8970997e-05 6.8799458e-04 2.4420024e-03 1.4255275e-02 1.5079078e-03 3.2366850e-03 5.6006336e-03 1.5957034e-02 1.6871464e-02 1.4787237e-02 2.2762765e-03 1.3333689e-03 1.2378081e-02 7.3736127e-04 8.8932355e-03 5.1508306e-03 2.2779875e-03 6.5293055e-03 3.6737889e-03 1.2985539e-02 7.3417742e-03 4.0613774e-03 2.7837547e-02 4.7997557e-03 4.5639701e-02 2.3278576e-02 1.4877176e-02 2.5663407e-02 2.8314352e-02 2.6769516e-02 3.2135703e-02 2.3238033e-02 2.8854470e-02 1.1827077e-02 2.2644401e-03 1.4421001e-02 8.8429015e-03 2.8776903e-02 2.8063687e-02 7.8778602e-03 1.4365876e-02 1.9273282e-02 4.9067035e-02 2.0328263e-02 1.0496416e-02 1.9196637e-02 3.0789735e-02 4.6457607e-03 1.3443289e-02 1.2708604e-02 2.5804695e-03 5.0424172e-03 2.6041403e-02 8.8829912e-03 1.7122028e-02 8.2367466e-03 2.7547637e-02 8.4529986e-03 3.6274794e-02 1.3225786e-02 2.0335647e-02 1.6784857e-02 4.4669319e-03 3.9860599e-03 1.4316384e-02 2.9523808e-03 2.3278576e-02 1.9803941e-02 1.4205949e-02 5.9492702e-03 1.2454271e-02 5.3895801e-03 1.5143895e-02 1.6858271e-02 5.9387347e-03 5.9571758e-03 6.0526515e-03 1.4146691e-03 8.1320415e-03 6.3420687e-04 2.2943426e-02 7.2217865e-04 8.7156753e-03 1.7123420e-03 1.9617600e-02 7.0385795e-03 6.4044399e-04 1.1390332e-03 4.8019256e-03 6.7384180e-03 2.9679315e-03 4.6026472e-03 1.3268878e-03 5.4152853e-04 4.6516773e-04 2.8772311e-02 1.3783757e-02 3.2459159e-03 7.3821107e-04 1.2234521e-02 2.0992860e-03 3.0774015e-03 1.1372571e-02 2.9352447e-03 8.1022588e-04 9.2842537e-04 2.8855444e-03 2.9418595e-03 1.2977062e-03 1.2869337e-04 1.4124612e-02 3.4246729e-04 6.6626445e-02 4.1505551e-02 3.3416051e-02 4.0874061e-02 4.8928444e-02 4.8860648e-02 4.7883828e-02 4.1881126e-02 5.2796241e-02 2.5141882e-02 8.8605790e-03 3.3017625e-02 2.4870522e-02 5.3247026e-02 5.3590923e-02 2.1349209e-02 2.6308756e-02 2.8721666e-02 8.1439460e-02 4.1842449e-02 2.6703197e-02 3.5208876e-02 5.5212555e-02 1.8433774e-02 2.4622301e-02 2.3863121e-02 1.3181398e-02 1.1675955e-02 4.8074316e-02 1.9890418e-02 3.7380285e-02 1.3082557e-02 5.1276398e-02 1.7233265e-02 5.2150081e-02 3.4634218e-02 3.3454504e-02 2.6550201e-02 1.0070918e-02 1.6842626e-02 3.3968952e-02 1.6166125e-02 4.1505551e-02 3.7811539e-02 3.1780349e-02 2.2766214e-02 3.3244526e-02 1.7747224e-02 2.4583452e-02 2.5577750e-02 7.4058632e-03 1.7632325e-02 5.8672157e-03 1.4554963e-02 7.5139440e-03 6.3063306e-03 2.8227254e-03 1.2216172e-02 5.2171506e-03 8.3411571e-03 9.1198919e-03 3.7613430e-03 3.7527482e-03 5.6979631e-04 2.0412760e-03 4.1677011e-03 1.2238157e-02 1.7043766e-03 3.0984992e-03 5.1374687e-03 2.0730806e-02 2.1628161e-02 1.6669142e-02 2.5627983e-03 1.3966031e-03 1.3985502e-02 1.7150229e-03 1.2743810e-02 7.3773848e-03 2.3644296e-03 5.7186323e-03 5.6669191e-03 1.5046711e-02 9.0550035e-03 4.3221940e-03 2.4177800e-02 5.7926331e-03 5.2977640e-02 2.8412846e-02 1.8464622e-02 3.1548301e-02 3.3726407e-02 3.1637531e-02 3.8729422e-02 2.8292534e-02 3.3574657e-02 1.5809867e-02 4.4178873e-03 1.7890807e-02 1.1580028e-02 3.3334020e-02 3.2156672e-02 1.0943899e-02 1.8967943e-02 2.4791881e-02 5.4103864e-02 2.4225163e-02 1.3643641e-02 2.4071431e-02 3.5681100e-02 6.5373199e-03 1.7959936e-02 1.7099713e-02 4.3929724e-03 8.0887873e-03 3.0809355e-02 1.2543658e-02 2.0721585e-02 1.2037199e-02 3.2114506e-02 1.2207760e-02 4.3300211e-02 1.5218252e-02 2.5733556e-02 2.1932604e-02 7.3680112e-03 5.8739609e-03 1.7443010e-02 2.6299180e-03 2.8412846e-02 2.4437667e-02 1.7887656e-02 6.9159049e-03 1.4494165e-02 7.9434468e-03 2.0060731e-02 2.2068579e-02 2.3875094e-02 1.2000415e-02 1.3948342e-03 3.4546351e-03 1.4939730e-02 3.7950422e-03 6.0175173e-04 1.2211196e-02 7.0052335e-03 9.1256063e-05 8.0336825e-03 9.5484059e-03 3.8945506e-03 2.3206010e-03 7.0732153e-04 2.0727990e-02 4.4889370e-03 6.3258821e-03 8.7927798e-03 9.0069682e-03 3.7952179e-03 7.8092546e-03 4.4335690e-03 9.0550736e-03 6.7946167e-03 2.1531805e-03 9.6426638e-04 7.0770423e-04 5.1150602e-03 1.0670446e-02 5.5187206e-04 6.0114634e-03 3.0771240e-03 5.3655604e-03 3.8039285e-02 3.5100982e-03 3.3392676e-02 1.7228333e-02 1.4023464e-02 1.5984642e-02 2.2267037e-02 2.3075907e-02 2.0330300e-02 1.7609739e-02 2.6354220e-02 7.6657981e-03 1.4938436e-03 1.3975046e-02 9.8087979e-03 2.7013992e-02 2.8227679e-02 6.7815824e-03 7.6426994e-03 8.5739542e-03 4.8734857e-02 1.9601188e-02 1.0188446e-02 1.3147209e-02 2.7911001e-02 7.5145131e-03 6.7053137e-03 6.3732135e-03 4.4764490e-03 1.3642012e-03 2.2654181e-02 4.8866188e-03 1.6888522e-02 1.3930577e-03 2.5469441e-02 3.2860894e-03 2.3095694e-02 1.8694362e-02 1.1451986e-02 7.4272413e-03 8.6465452e-04 6.5073035e-03 1.5400191e-02 1.2824348e-02 1.7228333e-02 1.5313622e-02 1.2672619e-02 1.3006211e-02 1.7530867e-02 5.5400420e-03 6.4187909e-03 6.8900377e-03 3.1968437e-03 2.6175875e-02 9.5689725e-03 4.4766639e-02 9.9845360e-03 2.8418681e-02 3.6870693e-03 4.5253718e-02 2.5815239e-02 5.6468536e-03 5.2271680e-03 1.8578768e-02 2.3906603e-02 1.7414675e-02 6.6309054e-04 1.0701959e-02 7.1563943e-03 4.3943049e-03 6.0754711e-02 3.4699507e-02 9.6990814e-03 9.4374842e-03 2.8881695e-02 8.6238198e-03 1.6549411e-02 3.3492594e-02 1.6963829e-02 8.9575119e-03 3.4839312e-03 1.7214511e-02 1.0985852e-02 1.1115876e-02 7.0774378e-03 2.3223923e-03 9.1191389e-03 1.1169736e-01 7.8250518e-02 6.5808362e-02 7.7763348e-02 8.8114611e-02 8.7449743e-02 8.7215232e-02 7.8675005e-02 9.2203874e-02 5.5132383e-02 2.8794481e-02 6.5100824e-02 5.3025891e-02 9.2531519e-02 9.2220151e-02 4.8731252e-02 5.7186075e-02 6.0580024e-02 1.2745253e-01 7.7174441e-02 5.6198920e-02 6.9678402e-02 9.5449745e-02 4.2538093e-02 5.4722713e-02 5.3576276e-02 3.4860888e-02 3.4291475e-02 8.6329226e-02 4.7343142e-02 7.0978985e-02 3.6695928e-02 9.0088005e-02 4.3430772e-02 9.2828505e-02 6.4239353e-02 6.7481588e-02 5.7536867e-02 3.1542812e-02 4.0297150e-02 6.5753742e-02 3.3241661e-02 7.8250518e-02 7.2933055e-02 6.3794057e-02 4.6178781e-02 6.2571051e-02 4.2843100e-02 5.4666081e-02 5.6027673e-02 1.6139749e-02 3.9412782e-03 2.4265145e-02 2.3891707e-03 1.6498373e-02 4.1418612e-05 2.4988702e-02 1.3783039e-02 4.3146851e-04 2.5122323e-04 6.4815288e-03 1.0052630e-02 7.0587637e-03 1.3669961e-03 2.4020339e-03 9.7331181e-04 2.7171014e-04 3.8783427e-02 2.3881244e-02 7.7285447e-03 2.0010737e-03 1.2962382e-02 6.0316172e-03 5.6775409e-03 1.9642810e-02 8.0347010e-03 1.6772041e-03 5.1201506e-05 7.5139419e-03 7.7685816e-03 5.4104161e-03 1.3577183e-03 7.6646931e-03 3.0214900e-03 8.1621829e-02 5.2182884e-02 4.1040324e-02 5.3055858e-02 6.0133087e-02 5.8980137e-02 6.1457708e-02 5.2397706e-02 6.2608146e-02 3.3589093e-02 1.3750660e-02 4.0409320e-02 3.0837807e-02 6.2732754e-02 6.2152008e-02 2.7986481e-02 3.5988306e-02 4.0227814e-02 9.1799153e-02 5.0051858e-02 3.3478326e-02 4.5393111e-02 6.5357720e-02 2.2750138e-02 3.4139497e-02 3.3132950e-02 1.7367166e-02 1.8328224e-02 5.7997026e-02 2.7720666e-02 4.5013892e-02 2.1236242e-02 6.0787869e-02 2.5266646e-02 6.6573172e-02 3.9086224e-02 4.4693577e-02 3.7322864e-02 1.6444309e-02 2.1159286e-02 4.0674593e-02 1.6113197e-02 5.2182884e-02 4.7503868e-02 3.9598661e-02 2.5231075e-02 3.7795470e-02 2.3382629e-02 3.4903444e-02 3.6481922e-02 4.3266011e-03 2.5120283e-02 7.2104610e-03 3.6362605e-04 1.6818393e-02 1.3224351e-02 8.9751703e-04 1.2039448e-02 1.4078561e-02 9.5477546e-03 7.2983976e-03 3.1999143e-03 2.4639885e-02 8.8187561e-03 1.0207680e-02 1.2236717e-02 1.1661588e-02 8.0001293e-04 5.7297070e-03 8.1532898e-03 1.7517743e-02 5.5505771e-03 6.3202290e-03 1.2261175e-03 1.5275601e-03 9.1326114e-03 1.4417917e-02 2.2056571e-03 4.2297788e-03 3.2218738e-03 8.2583968e-03 4.2679879e-02 5.2144338e-03 3.5325690e-02 2.1286357e-02 2.0340751e-02 1.7932964e-02 2.6773752e-02 2.8858059e-02 2.1473619e-02 2.1925037e-02 3.2987864e-02 1.1596147e-02 5.5679058e-03 2.0475433e-02 1.6418368e-02 3.4058657e-02 3.6285268e-02 1.2036020e-02 1.0227865e-02 8.9974606e-03 5.7847652e-02 2.6531721e-02 1.6295053e-02 1.6788576e-02 3.4473710e-02 1.4498141e-02 9.1389529e-03 8.9727022e-03 1.0441422e-02 4.2212624e-03 2.8555402e-02 8.4473824e-03 2.3791713e-02 2.5698702e-03 3.2242281e-02 5.9266749e-03 2.3676462e-02 2.8216759e-02 1.3343464e-02 8.4809495e-03 3.5554866e-03 1.3166972e-02 2.2745344e-02 2.2629651e-02 2.1286357e-02 1.9992085e-02 1.8520730e-02 2.2324446e-02 2.6771848e-02 1.1107369e-02 7.6788410e-03 7.4852977e-03 2.3512869e-02 1.1702196e-03 5.0486445e-03 4.4067902e-03 1.7493736e-02 4.0658633e-03 2.3529372e-03 3.3047066e-03 5.2094902e-03 6.0430183e-03 1.7805903e-03 8.3604250e-03 2.1645857e-03 1.8214590e-03 2.1737913e-03 2.3530298e-02 8.5714286e-03 1.8444368e-03 1.4565864e-03 1.3243081e-02 1.0630397e-03 2.8808992e-03 7.3652636e-03 1.0835317e-03 1.7933322e-03 3.0961279e-03 1.3436220e-03 1.3043704e-03 1.2482375e-04 8.8034137e-04 2.0043242e-02 1.2482375e-04 5.7954445e-02 3.5775683e-02 2.9742194e-02 3.4149795e-02 4.2824229e-02 4.3477315e-02 4.0214177e-02 3.6256651e-02 4.7605297e-02 2.0950874e-02 7.1042975e-03 2.9499916e-02 2.2329817e-02 4.8271691e-02 4.9230028e-02 1.8375611e-02 2.1296157e-02 2.2492082e-02 7.5802989e-02 3.7751582e-02 2.3617894e-02 2.9827991e-02 4.9792717e-02 1.7017775e-02 1.9724142e-02 1.9131907e-02 1.1873908e-02 8.7349825e-03 4.2824229e-02 1.6130090e-02 3.3687060e-02 9.1410950e-03 4.6289887e-02 1.3350026e-02 4.3904542e-02 3.3071376e-02 2.7381594e-02 2.0813943e-02 7.3233676e-03 1.5432666e-02 3.0904955e-02 1.7682128e-02 3.5775683e-02 3.2747980e-02 2.7982984e-02 2.2571206e-02 3.1620085e-02 1.5454693e-02 1.9154732e-02 1.9759156e-02 1.5625666e-02 2.0014241e-02 2.2906319e-02 3.3689213e-03 1.6561417e-02 1.9555072e-02 1.9724522e-02 6.8229205e-03 6.0996359e-03 1.3507575e-02 3.5744409e-02 1.3348521e-02 1.7651623e-02 2.2506296e-02 1.4148535e-02 3.1413076e-02 3.8119216e-02 1.5501824e-02 1.7783197e-03 3.4445966e-02 9.9745771e-03 1.7822684e-02 1.8993068e-02 1.5436010e-02 2.3911299e-02 1.6070505e-02 3.4793519e-02 2.5109394e-02 1.9730191e-02 5.3384757e-02 2.0981999e-02 3.5016323e-02 1.5808551e-02 7.2216559e-03 2.1250521e-02 1.8323057e-02 1.5289849e-02 2.7077232e-02 1.5342317e-02 1.5450153e-02 9.5719689e-03 7.0769801e-03 6.7142336e-03 3.8618160e-03 1.4833874e-02 1.3052654e-02 5.5299114e-03 1.3477582e-02 2.0871841e-02 2.6745939e-02 9.5407500e-03 5.3018257e-03 1.4058965e-02 1.6863810e-02 2.0380823e-03 1.3353665e-02 1.2655783e-02 3.2775616e-03 9.7625272e-03 1.4632542e-02 9.0437643e-03 7.6711708e-03 1.4740530e-02 1.4381411e-02 1.0907741e-02 3.1026769e-02 2.6758954e-03 1.8148668e-02 1.8095332e-02 1.0256426e-02 2.3444102e-03 5.5327937e-03 1.0086149e-03 1.5808551e-02 1.2612237e-02 7.5482105e-03 2.3261323e-04 2.5482034e-03 4.2287654e-03 1.6814254e-02 1.9242327e-02 6.8098804e-03 2.4185222e-03 1.3095995e-02 4.9100577e-03 8.0319599e-04 1.3085229e-03 1.8113887e-03 3.1306373e-03 1.2541646e-03 7.2250359e-03 1.5182032e-04 3.2471009e-04 1.1793141e-03 2.2134336e-02 1.2798604e-02 5.8036245e-03 2.9068060e-05 7.1481800e-03 4.2574873e-03 8.5847073e-04 8.5715637e-03 2.1039195e-03 1.1389023e-04 1.8746094e-03 1.5895426e-03 4.9316123e-03 1.9257560e-03 2.7708506e-04 1.8515651e-02 5.3986259e-04 5.6637702e-02 3.2886175e-02 2.4967826e-02 3.3187187e-02 3.9414817e-02 3.8923256e-02 3.9880016e-02 3.3128964e-02 4.2231952e-02 1.8470852e-02 4.9097912e-03 2.4570551e-02 1.7467535e-02 4.2525770e-02 4.2580968e-02 1.4790908e-02 2.0011146e-02 2.3142926e-02 6.7845285e-02 3.2285576e-02 1.9161137e-02 2.7420216e-02 4.4447416e-02 1.1980855e-02 1.8611023e-02 1.7883360e-02 7.8729229e-03 7.5466400e-03 3.8175619e-02 1.4096206e-02 2.8321778e-02 9.4496873e-03 4.0815141e-02 1.2211823e-02 4.4024579e-02 2.5465922e-02 2.6596134e-02 2.0925904e-02 6.3340227e-03 1.0720439e-02 2.5211490e-02 1.0333158e-02 3.2886175e-02 2.9360405e-02 2.3656085e-02 1.5413665e-02 2.4285913e-02 1.1678199e-02 1.9116491e-02 2.0313205e-02 1.6950975e-02 9.2576768e-03 2.2830227e-04 1.1996475e-02 1.3943618e-02 7.5471408e-03 5.0950912e-03 2.4447813e-03 2.5903324e-02 8.0139448e-03 9.9879335e-03 1.2585436e-02 8.2754975e-03 1.4174352e-03 8.0478199e-03 7.7042954e-03 1.3901912e-02 7.5078674e-03 4.9957099e-03 2.9997761e-04 1.5395926e-03 8.6362248e-03 1.4833108e-02 1.8222710e-03 6.1957159e-03 4.1238914e-03 8.4102568e-03 4.4734160e-02 5.6076899e-03 3.0288189e-02 1.6597244e-02 1.5336169e-02 1.4117002e-02 2.1529085e-02 2.3159309e-02 1.7633168e-02 1.7119251e-02 2.6802924e-02 7.9632454e-03 3.3472740e-03 1.5438450e-02 1.1914773e-02 2.7716384e-02 2.9607261e-02 8.2241873e-03 7.0346123e-03 6.5727745e-03 4.9604200e-02 2.0828511e-02 1.1808642e-02 1.2615158e-02 2.8194719e-02 1.0429441e-02 6.1245969e-03 5.9452412e-03 7.1618546e-03 2.1261487e-03 2.2855826e-02 5.3452154e-03 1.8357871e-02 1.0397070e-03 2.6087088e-02 3.3990115e-03 1.9871685e-02 2.2251351e-02 9.9773797e-03 5.9192173e-03 1.6976055e-03 9.3317377e-03 1.7391404e-02 1.8375579e-02 1.6597244e-02 1.5280892e-02 1.3772451e-02 1.7356806e-02 2.0968489e-02 7.4978418e-03 5.1634523e-03 5.1963190e-03 2.4233623e-02 1.4077025e-02 4.3787020e-04 1.8512542e-04 6.0477386e-03 9.6703484e-03 7.1488601e-03 1.5477068e-03 2.2742281e-03 9.7171923e-04 3.9271863e-04 3.8509554e-02 2.4774335e-02 8.7360281e-03 1.9891111e-03 1.1995846e-02 6.8973897e-03 5.5019459e-03 1.9965157e-02 8.4152844e-03 1.6247783e-03 1.4531219e-04 7.7480972e-03 8.6972032e-03 5.9824611e-03 1.5176827e-03 7.8567259e-03 3.3493021e-03 8.1204385e-02 5.1575806e-02 4.0135980e-02 5.2803481e-02 5.9400299e-02 5.8042463e-02 6.1291964e-02 5.1745941e-02 6.1508632e-02 3.3138226e-02 1.3469654e-02 3.9479896e-02 2.9967140e-02 6.1565597e-02 6.0825097e-02 2.7361698e-02 3.5748038e-02 4.0333571e-02 9.0138895e-02 4.8984196e-02 3.2667108e-02 4.4902460e-02 6.4253355e-02 2.1894327e-02 3.3940189e-02 3.2913172e-02 1.6731486e-02 1.8202458e-02 5.7045856e-02 2.7382191e-02 4.3985602e-02 2.1375556e-02 5.9676750e-02 2.5108522e-02 6.6474949e-02 3.7689168e-02 4.4508235e-02 3.7339453e-02 1.6368548e-02 2.0371230e-02 3.9606943e-02 1.4908799e-02 5.1575806e-02 4.6824025e-02 3.8781771e-02 2.3989539e-02 3.6452117e-02 2.2748990e-02 3.4902979e-02 3.6586492e-02 7.5459794e-03 1.9125850e-02 2.0310725e-02 6.1222293e-03 3.4447226e-03 8.1418452e-03 3.7867035e-02 1.2077489e-02 1.6567789e-02 2.1605044e-02 3.9038067e-03 1.6430417e-02 2.8689503e-02 1.3569150e-02 4.2181405e-03 2.6167927e-02 7.2610935e-03 7.0630535e-03 1.1392972e-02 1.4088158e-02 2.3849569e-02 9.5566395e-03 2.5308356e-02 1.7804542e-02 1.7150362e-02 5.8921861e-02 1.6182419e-02 2.0027139e-02 6.1819655e-03 2.0410396e-03 8.6030014e-03 8.6357480e-03 7.5787415e-03 1.2598904e-02 6.0656768e-03 8.7139108e-03 1.7031846e-03 2.3331644e-03 1.8821103e-03 3.1682605e-04 8.7160052e-03 8.5600328e-03 2.8826583e-04 3.4529372e-03 7.5176091e-03 2.1668627e-02 4.3875084e-03 6.5654007e-04 4.4940457e-03 9.7914071e-03 1.9831433e-04 3.3391345e-03 2.9870521e-03 6.9985633e-04 2.5162956e-03 7.1853248e-03 1.3860021e-03 2.9754449e-03 4.8339512e-03 8.0121089e-03 2.3142577e-03 1.5373296e-02 2.8512223e-03 6.2399212e-03 5.8796015e-03 3.1092372e-03 2.3415322e-04 1.9852251e-03 5.2321529e-03 6.1819655e-03 4.3274341e-03 1.8113019e-03 1.9110595e-03 2.4033410e-03 9.3130265e-05 5.1498910e-03 6.5356041e-03 9.5659910e-03 1.1252405e-02 5.1556774e-03 3.2048021e-03 1.2695843e-03 2.2856756e-02 5.7848184e-03 7.7211451e-03 1.0283621e-02 8.3602499e-03 2.7615733e-03 7.9786726e-03 5.6505970e-03 1.0691105e-02 7.1332727e-03 3.1295602e-03 5.2401241e-04 9.5266815e-04 6.4319329e-03 1.2318850e-02 9.4872145e-04 6.1326072e-03 3.4758657e-03 6.5333653e-03 4.0853883e-02 4.3049208e-03 3.1649609e-02 1.6528693e-02 1.4101758e-02 1.4838879e-02 2.1483291e-02 2.2597676e-02 1.8841242e-02 1.6959579e-02 2.5991410e-02 7.4250762e-03 1.9970150e-03 1.4113092e-02 1.0250250e-02 2.6744340e-02 2.8212793e-02 6.9997737e-03 7.0607843e-03 7.4739664e-03 4.8397396e-02 1.9593840e-02 1.0430940e-02 1.2525634e-02 2.7473469e-02 8.3209543e-03 6.1476473e-03 5.8775226e-03 5.2455782e-03 1.4265861e-03 2.2224591e-02 4.7507930e-03 1.7000338e-02 1.0369313e-03 2.5178358e-02 3.0495461e-03 2.1392322e-02 1.9628131e-02 1.0500631e-02 6.5204272e-03 9.7357638e-04 7.2933833e-03 1.5732817e-02 1.4704532e-02 1.6528693e-02 1.4858839e-02 1.2681737e-02 1.4339393e-02 1.8427144e-02 5.9852538e-03 5.6194230e-03 5.9265324e-03 8.2461319e-05 3.7688699e-03 6.4113525e-03 4.0576095e-03 3.3120738e-03 8.1782115e-04 1.0911416e-04 1.0750826e-04 3.1097496e-02 1.8955233e-02 6.6951403e-03 5.7580225e-04 9.5785875e-03 5.0095389e-03 2.9949836e-03 1.4512717e-02 5.0859668e-03 4.0940931e-04 2.6323499e-04 4.5146422e-03 6.3221754e-03 3.5606197e-03 3.7002765e-04 1.1722539e-02 1.5048339e-03 7.0395338e-02 4.3268177e-02 3.3370831e-02 4.4028795e-02 5.0578307e-02 4.9629423e-02 5.1746432e-02 4.3478758e-02 5.3066435e-02 2.6481479e-02 9.3392135e-03 3.2828923e-02 2.4313691e-02 5.3237993e-02 5.2867243e-02 2.1623243e-02 2.8596153e-02 3.2496284e-02 8.0563616e-02 4.1614554e-02 2.6578058e-02 3.7074597e-02 5.5590704e-02 1.7323162e-02 2.6950223e-02 2.6053254e-02 1.2554151e-02 1.3157434e-02 4.8740123e-02 2.1279987e-02 3.7040705e-02 1.5739340e-02 5.1410982e-02 1.9129885e-02 5.6480014e-02 3.2271228e-02 3.6432542e-02 2.9847396e-02 1.1573499e-02 1.5895178e-02 3.3202746e-02 1.2659655e-02 4.3268177e-02 3.9046279e-02 3.2014714e-02 2.0067384e-02 3.1045819e-02 1.7642117e-02 2.7677552e-02 2.9133996e-02 4.1910853e-03 7.2076003e-03 5.1147587e-03 2.7507325e-03 1.1620134e-03 3.3310681e-04 2.1458858e-04 3.3405406e-02 2.1499526e-02 8.0088547e-03 9.8332257e-04 9.6892205e-03 6.1744631e-03 3.6704596e-03 1.6544301e-02 6.4217725e-03 7.2111274e-04 2.1270559e-04 5.7183304e-03 7.6981750e-03 4.7117366e-03 8.0177817e-04 1.0442534e-02 2.2910923e-03 7.3784227e-02 4.5661689e-02 3.5029236e-02 4.6823533e-02 5.3065363e-02 5.1836541e-02 5.4866908e-02 4.5828107e-02 5.5174565e-02 2.8408213e-02 1.0505539e-02 3.4430852e-02 2.5609623e-02 5.5260802e-02 5.4654205e-02 2.3114484e-02 3.0836583e-02 3.5207839e-02 8.2686924e-02 4.3372344e-02 2.8064998e-02 3.9380344e-02 5.7772002e-02 1.8254235e-02 2.9162191e-02 2.8206341e-02 1.3497345e-02 1.4745279e-02 5.0900941e-02 2.3090436e-02 3.8677830e-02 1.7710943e-02 5.3450122e-02 2.1019262e-02 5.9801906e-02 3.3130692e-02 3.9030212e-02 3.2379101e-02 1.3108535e-02 1.6839016e-02 3.4625964e-02 1.2551681e-02 4.5661689e-02 4.1207031e-02 3.3732757e-02 2.0513590e-02 3.1937443e-02 1.8908607e-02 3.0105345e-02 3.1711397e-02 5.5341249e-04 1.8100865e-03 1.3685556e-02 1.1595059e-03 2.7733190e-03 5.0259130e-03 1.5582273e-02 1.5303863e-02 1.3216313e-02 1.8061219e-03 1.8483286e-03 1.0961959e-02 4.0632184e-04 7.9419645e-03 4.2030049e-03 1.8572156e-03 5.9964081e-03 2.8843938e-03 1.1487297e-02 6.2195738e-03 3.4171878e-03 2.7328194e-02 3.9549773e-03 4.5323537e-02 2.3233798e-02 1.5194289e-02 2.5226902e-02 2.8389486e-02 2.7075250e-02 3.1585337e-02 2.3242139e-02 2.9332670e-02 1.1663012e-02 1.9950589e-03 1.4768251e-02 9.1566367e-03 2.9330780e-02 2.8799195e-02 7.9264454e-03 1.3962831e-02 1.8481959e-02 5.0115855e-02 2.0802698e-02 1.0733745e-02 1.9041166e-02 3.1266982e-02 4.9760517e-03 1.3001310e-02 1.2292231e-02 2.6877167e-03 4.5798906e-03 2.6364594e-02 8.6153969e-03 1.7566530e-02 7.4744318e-03 2.8043210e-02 7.9969256e-03 3.5640074e-02 1.4109863e-02 1.9847472e-02 1.6098385e-02 3.9587699e-03 4.2468630e-03 1.4818700e-02 3.7268555e-03 2.3233798e-02 1.9851942e-02 1.4429507e-02 6.7117241e-03 1.3281147e-02 5.4664867e-03 1.4483311e-02 1.6079322e-02 1.4644608e-03 1.8793440e-02 2.6529941e-03 4.9386280e-03 7.8423223e-03 1.0293845e-02 1.1871124e-02 1.4064748e-02 3.3517168e-03 2.2145112e-03 1.1983758e-02 7.1285200e-04 4.9198989e-03 3.5944894e-03 3.6244119e-03 9.2640212e-03 2.3814700e-03 1.1959906e-02 6.6391160e-03 5.2659669e-03 3.4768199e-02 5.0225808e-03 3.6096256e-02 1.6893661e-02 1.0608435e-02 1.8369401e-02 2.1445998e-02 2.0588370e-02 2.3840862e-02 1.6945577e-02 2.2798129e-02 7.2320779e-03 4.5716975e-04 1.0311574e-02 5.8475513e-03 2.2922553e-02 2.2802937e-02 4.5367353e-03 8.9750660e-03 1.2721749e-02 4.2209187e-02 1.5508457e-02 6.9302761e-03 1.3261817e-02 2.4477020e-02 2.9037640e-03 8.2014592e-03 7.6405998e-03 1.0779902e-03 2.0077167e-03 2.0000571e-02 4.8259503e-03 1.2764273e-02 4.2375783e-03 2.1711543e-02 4.3575270e-03 2.7378197e-02 1.1026559e-02 1.3799894e-02 1.0731586e-02 1.6733633e-03 2.2875292e-03 1.0634216e-02 4.2659077e-03 1.6893661e-02 1.4117780e-02 9.8480141e-03 5.3491991e-03 1.0216132e-02 2.7977678e-03 9.4220767e-03 1.0761419e-02 1.4255275e-02 1.6372127e-03 2.8542706e-03 4.7214122e-03 1.3332443e-02 7.0444510e-03 6.5532524e-03 1.6174955e-03 6.7559932e-03 5.2389698e-03 5.3640347e-04 3.3171183e-03 5.1283913e-04 2.0296126e-03 6.0950963e-03 1.2744293e-04 5.0948120e-03 1.9318373e-03 2.3608728e-03 2.9089432e-02 1.4755776e-03 4.1865124e-02 2.2429622e-02 1.7100637e-02 2.2021240e-02 2.8022948e-02 2.8179896e-02 2.7393595e-02 2.2729266e-02 3.1385076e-02 1.0928177e-02 1.7748140e-03 1.6893502e-02 1.1535806e-02 3.1856326e-02 3.2502251e-02 8.7863970e-03 1.1670124e-02 1.3706425e-02 5.4822004e-02 2.3316700e-02 1.2494202e-02 1.7833364e-02 3.3214276e-02 7.9216177e-03 1.0562159e-02 1.0054198e-02 4.5572702e-03 2.8793276e-03 2.7614983e-02 7.5454514e-03 2.0103411e-02 3.8323098e-03 3.0272091e-02 5.9246169e-03 3.0771609e-02 1.9772265e-02 1.6666117e-02 1.2065056e-02 2.1106407e-03 6.8455106e-03 1.7918371e-02 1.0155544e-02 2.2429622e-02 1.9803941e-02 1.5812785e-02 1.2351590e-02 1.8630292e-02 6.7616623e-03 1.0713593e-02 1.1544341e-02 7.3852619e-03 4.6030790e-03 2.5842708e-03 5.4300453e-02 3.3576641e-02 1.0668428e-02 6.6015056e-03 2.1814225e-02 9.0913366e-03 1.2584515e-02 3.0243821e-02 1.4861127e-02 6.0467884e-03 1.7442631e-03 1.4568415e-02 1.1492675e-02 1.0173171e-02 5.0439803e-03 2.6472861e-03 7.4513739e-03 1.0326079e-01 7.0098941e-02 5.7127711e-02 7.0823391e-02 7.9250920e-02 7.7946311e-02 8.0298570e-02 7.0363352e-02 8.2049228e-02 4.8294128e-02 2.3708406e-02 5.6369153e-02 4.4949110e-02 8.2157728e-02 8.1370723e-02 4.1592461e-02 5.0974970e-02 5.5456655e-02 1.1462128e-01 6.7593968e-02 4.8172633e-02 6.2189094e-02 8.5184220e-02 3.4981615e-02 4.8742735e-02 4.7569879e-02 2.8345798e-02 2.9430788e-02 7.6817222e-02 4.1186102e-02 6.1732592e-02 3.2638549e-02 7.9961084e-02 3.8043139e-02 8.5993831e-02 5.4182844e-02 6.1100206e-02 5.2203816e-02 2.6988218e-02 3.3049806e-02 5.6579852e-02 2.5305632e-02 7.0098941e-02 6.4715083e-02 5.5458622e-02 3.7374533e-02 5.2731313e-02 3.5938214e-02 4.9377578e-02 5.1061578e-02 3.5870177e-04 1.3937749e-03 2.2192133e-02 1.4887667e-02 7.8001469e-03 8.8171759e-05 5.5216302e-03 5.9793748e-03 7.0276376e-04 9.5746848e-03 3.0437686e-03 8.2181914e-05 2.0068727e-03 2.2506604e-03 6.8090409e-03 3.1469229e-03 6.3301284e-04 1.8528100e-02 1.2625125e-03 5.6704255e-02 3.2399710e-02 2.3806821e-02 3.3394775e-02 3.8737297e-02 3.7833623e-02 4.0310184e-02 3.2556548e-02 4.0845247e-02 1.8140183e-02 4.6897646e-03 2.3353931e-02 1.6279158e-02 4.1004664e-02 4.0734447e-02 1.4064354e-02 2.0110135e-02 2.3937572e-02 6.5512797e-02 3.0871191e-02 1.8117892e-02 2.7110445e-02 4.3069544e-02 1.0732705e-02 1.8772743e-02 1.7996705e-02 7.0003565e-03 7.6876035e-03 3.7052349e-02 1.3928781e-02 2.6945955e-02 1.0128877e-02 3.9392715e-02 1.2368176e-02 4.4616010e-02 2.3284644e-02 2.6871325e-02 2.1527670e-02 6.5473767e-03 9.5899926e-03 2.3719212e-02 8.2529644e-03 3.2399710e-02 2.8696517e-02 2.2657048e-02 1.3399527e-02 2.2203050e-02 1.0884330e-02 1.9665180e-02 2.1074599e-02 3.3845415e-04 2.7555720e-02 1.6677843e-02 6.3795274e-03 1.8362843e-04 8.3039101e-03 4.7168779e-03 1.9845708e-03 1.2200549e-02 3.8746792e-03 1.0179352e-04 6.8781671e-04 3.2951534e-03 5.8068152e-03 2.8838183e-03 1.7929502e-04 1.4079350e-02 1.0261263e-03 6.5064614e-02 3.9139337e-02 2.9910766e-02 3.9801101e-02 4.6137717e-02 4.5316574e-02 4.7159535e-02 3.9352955e-02 4.8674767e-02 2.3254889e-02 7.4636086e-03 2.9417103e-02 2.1432655e-02 4.8877998e-02 4.8632496e-02 1.8795647e-02 2.5200016e-02 2.8886320e-02 7.5371106e-02 3.7782169e-02 2.3498806e-02 3.3236011e-02 5.1084684e-02 1.4998262e-02 2.3652548e-02 2.2813769e-02 1.0515841e-02 1.0877794e-02 4.4476659e-02 1.8379623e-02 3.3440220e-02 1.3262540e-02 4.7104689e-02 1.6362923e-02 5.1690239e-02 2.9302173e-02 3.2587374e-02 2.6378456e-02 9.4404077e-03 1.3645256e-02 2.9862026e-02 1.1376615e-02 3.9139337e-02 3.5158865e-02 2.8588131e-02 1.7920264e-02 2.8103943e-02 1.5132216e-02 2.4337714e-02 2.5719532e-02 3.3433714e-02 1.9092201e-02 5.6820829e-03 9.6311300e-04 1.1687721e-02 4.1741711e-03 3.9152052e-03 1.5429746e-02 5.3660274e-03 8.0766059e-04 9.2937667e-05 4.9943675e-03 5.5161799e-03 3.3131521e-03 4.2533013e-04 1.0435178e-02 1.4826896e-03 7.3827823e-02 4.6346021e-02 3.6506307e-02 4.6683572e-02 5.3981470e-02 5.3241058e-02 5.4467293e-02 4.6614647e-02 5.6933591e-02 2.8894083e-02 1.0839802e-02 3.5973376e-02 2.7110780e-02 5.7179243e-02 5.6957917e-02 2.4065468e-02 3.0813759e-02 3.4360438e-02 8.5582303e-02 4.5156230e-02 2.9396418e-02 3.9850269e-02 5.9522160e-02 1.9818286e-02 2.9063291e-02 2.8164144e-02 1.4610346e-02 1.4656069e-02 5.2344759e-02 2.3377306e-02 4.0412481e-02 1.6984268e-02 5.5250411e-02 2.0918953e-02 5.9213325e-02 3.5814189e-02 3.8804285e-02 3.1759139e-02 1.2931018e-02 1.8261938e-02 3.6495356e-02 1.5089913e-02 4.6346021e-02 4.2098942e-02 3.5016667e-02 2.2986158e-02 3.4503485e-02 1.9934983e-02 2.9546649e-02 3.0900707e-02 1.1335824e-02 3.2301226e-02 2.3277392e-02 1.5629192e-02 3.0795664e-02 1.5041716e-02 5.4315810e-03 1.4713782e-02 2.4451968e-02 3.6761202e-02 1.3762814e-02 2.8473446e-02 2.2472023e-02 2.6736705e-02 8.0230549e-02 2.3341323e-02 8.1067950e-03 1.4485807e-03 2.4486060e-03 1.1703837e-03 3.1310445e-03 3.9732842e-03 2.8350922e-03 1.6242765e-03 5.7101906e-03 4.5289508e-04 6.6077306e-03 2.6813364e-03 3.3571693e-03 6.2840633e-03 7.7260784e-03 2.0715363e-03 5.6385417e-05 8.9610646e-04 1.8073537e-02 3.9621048e-03 2.3604444e-03 4.7560860e-04 6.2480089e-03 5.8573026e-03 1.7069942e-04 2.3254450e-04 6.3902073e-03 3.8624324e-03 3.9083302e-03 1.0347438e-03 3.4531305e-03 3.5564063e-03 5.5040957e-03 1.4887365e-03 4.1720167e-03 8.6822743e-03 2.8448886e-04 5.1458480e-04 4.8536300e-03 5.8397245e-03 3.9827712e-03 1.8058791e-02 1.4485807e-03 1.2408841e-03 1.8448387e-03 1.0796845e-02 8.0405443e-03 3.4808504e-03 5.2754145e-04 8.8520211e-04 8.8923137e-03 1.4043268e-02 2.4126814e-02 9.1698320e-03 1.1327807e-02 1.9771649e-03 4.5360079e-03 1.5317882e-02 2.1762150e-02 5.6097757e-03 7.1877025e-03 6.8496039e-03 1.4100359e-02 5.3810596e-02 9.9629280e-03 3.1307769e-02 2.0553715e-02 2.1937453e-02 1.5991855e-02 2.5676129e-02 2.8613140e-02 1.8469501e-02 2.1322428e-02 3.3003652e-02 1.2471005e-02 8.9752587e-03 2.2249180e-02 1.9221338e-02 3.4330234e-02 3.7274155e-02 1.4283960e-02 1.0136722e-02 7.4549090e-03 5.7532344e-02 2.7732649e-02 1.8492242e-02 1.6393364e-02 3.4241314e-02 1.8497141e-02 9.1551303e-03 9.1767161e-03 1.4470512e-02 6.3039218e-03 2.8459911e-02 9.7413243e-03 2.5409718e-02 3.4719577e-03 3.2484401e-02 7.0164005e-03 2.0017467e-02 3.2249701e-02 1.2084843e-02 7.5073944e-03 5.8227226e-03 1.7146634e-02 2.5055287e-02 2.9865216e-02 2.0553715e-02 2.0008085e-02 1.9953689e-02 2.7764157e-02 3.0721044e-02 1.4086953e-02 7.0439235e-03 6.3871700e-03 6.2932024e-03 2.4881477e-02 1.2570656e-04 9.2318283e-03 1.1425023e-02 3.9781027e-03 6.8287051e-03 6.6316807e-03 5.1943433e-03 1.2253382e-04 1.3842125e-03 4.4535183e-03 2.1436401e-02 2.8241776e-03 6.8828353e-02 4.7091014e-02 4.2520568e-02 4.3241334e-02 5.5155458e-02 5.7042053e-02 4.8981701e-02 4.7857882e-02 6.2261278e-02 3.0649818e-02 1.4761608e-02 4.2406580e-02 3.4493935e-02 6.3340671e-02 6.5239814e-02 2.8920518e-02 2.9750953e-02 2.8851937e-02 9.4568410e-02 5.1830706e-02 3.5536784e-02 4.0198717e-02 6.4555077e-02 2.8697450e-02 2.7846993e-02 2.7347316e-02 2.1934395e-02 1.5675611e-02 5.6443004e-02 2.4878970e-02 4.7356921e-02 1.4503825e-02 6.0963940e-02 2.0840976e-02 5.2367097e-02 4.8672170e-02 3.5784762e-02 2.7656172e-02 1.3836981e-02 2.6623769e-02 4.4650658e-02 3.0852069e-02 4.7091014e-02 4.4370840e-02 4.0176310e-02 3.6600204e-02 4.6858384e-02 2.5835018e-02 2.6019603e-02 2.5994222e-02 6.9728196e-03 4.6608676e-03 9.9835054e-04 9.5223148e-03 2.6272609e-03 2.7884021e-05 1.5635547e-03 2.0372846e-03 5.4615556e-03 2.3300808e-03 2.5698617e-04 1.7457304e-02 7.2996870e-04 5.8453388e-02 3.4095678e-02 2.5737672e-02 3.4628989e-02 4.0687367e-02 4.0032201e-02 4.1519056e-02 3.4313166e-02 4.3287054e-02 1.9386832e-02 5.3532424e-03 2.5306754e-02 1.8013458e-02 4.3531307e-02 4.3448361e-02 1.5446633e-02 2.1111383e-02 2.4519102e-02 6.8935827e-02 3.3123525e-02 1.9821803e-02 2.8570801e-02 4.5547846e-02 1.2302601e-02 1.9692146e-02 1.8929129e-02 8.1934144e-03 8.2421848e-03 3.9256624e-02 1.4936345e-02 2.9083818e-02 1.0370393e-02 4.1828239e-02 1.3093356e-02 4.5785106e-02 2.5766235e-02 2.7913838e-02 2.2194996e-02 6.9953757e-03 1.1047300e-02 2.5843766e-02 1.0033677e-02 3.4095678e-02 3.0429687e-02 2.4458901e-02 1.5454278e-02 2.4603652e-02 1.2196519e-02 2.0322394e-02 2.1606954e-02 2.1763592e-02 3.8778874e-03 1.3140898e-02 1.0973585e-02 6.8570288e-03 1.2689294e-02 8.7227464e-03 2.2446768e-02 1.4768924e-02 9.8837253e-03 3.6594036e-02 1.1176832e-02 4.2371132e-02 2.0489598e-02 1.1322791e-02 2.4620431e-02 2.4482623e-02 2.2010230e-02 3.1108475e-02 2.0217050e-02 2.3141445e-02 1.0944826e-02 3.8599335e-03 1.0791452e-02 6.1542383e-03 2.2743482e-02 2.1333545e-02 6.5515291e-03 1.4388718e-02 2.0852825e-02 3.9383251e-02 1.5461941e-02 7.8878514e-03 1.7372104e-02 2.4911352e-02 2.6880230e-03 1.3798611e-02 1.3025794e-02 2.1193973e-03 6.9838956e-03 2.1270749e-02 8.9787629e-03 1.2743052e-02 1.1335890e-02 2.1877049e-02 9.6533919e-03 3.5351983e-02 7.6384883e-03 2.0107777e-02 1.8067074e-02 6.8413076e-03 2.4741061e-03 1.0039063e-02 3.4670280e-04 2.0489598e-02 1.6923143e-02 1.1154236e-02 2.1521967e-03 7.1850256e-03 4.3898059e-03 1.6491142e-02 1.8665860e-02 7.4245740e-03 1.0715228e-02 3.1685550e-03 5.1120383e-03 5.0451240e-03 4.1323270e-03 1.4489604e-04 8.1318290e-04 3.0839979e-03 1.9714350e-02 1.7947024e-03 6.7440625e-02 4.5128820e-02 3.9916292e-02 4.1909560e-02 5.3040678e-02 5.4550431e-02 4.7872699e-02 4.5818357e-02 5.9509133e-02 2.8778013e-02 1.2927960e-02 3.9749997e-02 3.1835574e-02 6.0466685e-02 6.2068316e-02 2.6673332e-02 2.8274822e-02 2.8015780e-02 9.1028220e-02 4.9020613e-02 3.3018114e-02 3.8378554e-02 6.1817956e-02 2.5959804e-02 2.6422336e-02 2.5876047e-02 1.9520884e-02 1.4184191e-02 5.3918305e-02 2.3137863e-02 4.4568283e-02 1.3507580e-02 5.8174666e-02 1.9385371e-02 5.1416653e-02 4.5132833e-02 3.4491897e-02 2.6637939e-02 1.2404699e-02 2.3988175e-02 4.1744067e-02 2.7329966e-02 4.5128820e-02 4.2235431e-02 3.7715182e-02 3.3171215e-02 4.3405180e-02 2.3512609e-02 2.4946638e-02 2.5117607e-02 5.7451096e-03 2.0181202e-03 1.1916495e-03 5.0056293e-03 1.1565469e-03 7.7131020e-03 3.5313465e-03 2.1072743e-03 2.6393551e-02 2.0670282e-03 4.4935473e-02 2.3639481e-02 1.6646699e-02 2.4503230e-02 2.9137719e-02 2.8488385e-02 3.0530237e-02 2.3788614e-02 3.1241308e-02 1.1731452e-02 1.7654569e-03 1.6307697e-02 1.0602623e-02 3.1457386e-02 3.1449229e-02 8.6094127e-03 1.3341698e-02 1.6740022e-02 5.3656632e-02 2.2705161e-02 1.1958802e-02 1.9130319e-02 3.3171817e-02 6.4720106e-03 1.2268562e-02 1.1633623e-02 3.5425115e-03 3.7925712e-03 2.7828397e-02 8.3873180e-03 1.9384549e-02 5.8220699e-03 3.0002594e-02 7.2263237e-03 3.4339797e-02 1.7195852e-02 1.8977924e-02 1.4659749e-02 3.0403500e-03 5.5430802e-03 1.6804744e-02 6.5028482e-03 2.3639481e-02 2.0526700e-02 1.5612876e-02 9.4514606e-03 1.6202837e-02 6.2323724e-03 1.3120120e-02 1.4374340e-02 2.8180986e-03 1.0509318e-02 1.7897176e-02 2.8798358e-03 9.1928702e-03 6.3970688e-03 1.0746267e-02 5.0496414e-02 7.7981540e-03 2.4751970e-02 1.2458649e-02 1.1686269e-02 1.0356316e-02 1.6774912e-02 1.8282513e-02 1.3498932e-02 1.2919728e-02 2.1579044e-02 5.2962835e-03 2.7725666e-03 1.1818152e-02 9.1091800e-03 2.2436834e-02 2.4265521e-02 5.8512682e-03 4.4398598e-03 4.1822455e-03 4.2435194e-02 1.6425553e-02 8.8184300e-03 9.0473617e-03 2.2804959e-02 8.4055012e-03 3.7245140e-03 3.5955501e-03 5.8501155e-03 1.2261597e-03 1.8027694e-02 3.2889908e-03 1.4332649e-02 2.5252408e-04 2.0964539e-02 1.7865385e-03 1.5552416e-02 1.8549588e-02 6.8429145e-03 3.5948443e-03 1.0694978e-03 7.5092190e-03 1.3665818e-02 1.7252830e-02 1.2458649e-02 1.1370103e-02 1.0296741e-02 1.5007456e-02 1.7383467e-02 5.5463863e-03 2.9922345e-03 3.0734531e-03 3.1960387e-03 6.8662451e-03 1.2966654e-04 2.7649067e-03 8.3427366e-04 2.7984284e-03 2.9822521e-02 1.2731780e-03 4.3416537e-02 2.4875338e-02 2.0714079e-02 2.3200351e-02 3.0852531e-02 3.1753284e-02 2.8200300e-02 2.5332686e-02 3.5512031e-02 1.2989773e-02 3.4785945e-03 2.0603808e-02 1.5135483e-02 3.6226615e-02 3.7456354e-02 1.1531484e-02 1.2959687e-02 1.3737739e-02 6.0763799e-02 2.7435536e-02 1.5849420e-02 1.9923216e-02 3.7336849e-02 1.1567919e-02 1.1725372e-02 1.1300051e-02 7.4803077e-03 3.9656480e-03 3.1244978e-02 9.2703065e-03 2.4126524e-02 3.9496417e-03 3.4455719e-02 7.0383738e-03 3.1302098e-02 2.5203523e-02 1.7680030e-02 1.2436482e-02 3.0466203e-03 1.0266971e-02 2.2126952e-02 1.5206492e-02 2.4875338e-02 2.2550174e-02 1.9125072e-02 1.7383181e-02 2.3878119e-02 9.5944653e-03 1.1168635e-02 1.1615557e-02 1.3151667e-03 2.5320521e-03 6.0366399e-03 2.7823080e-03 2.9364031e-04 1.6476106e-02 9.7247557e-04 6.0284770e-02 3.5334723e-02 2.6546579e-02 3.6095524e-02 4.1987790e-02 4.1172424e-02 4.3178382e-02 3.5527420e-02 4.4374359e-02 2.0339225e-02 5.8436205e-03 2.6082735e-02 1.8603485e-02 4.4570054e-02 4.4351810e-02 1.6144347e-02 2.2244142e-02 2.5921932e-02 7.0056396e-02 3.3998717e-02 2.0524111e-02 2.9752453e-02 4.6679420e-02 1.2673386e-02 2.0806267e-02 2.0008665e-02 8.5632030e-03 8.9793060e-03 4.0369526e-02 1.5814708e-02 2.9884891e-02 1.1327921e-02 4.2874486e-02 1.4012365e-02 4.7563178e-02 2.6115270e-02 2.9259206e-02 2.3493076e-02 7.6990791e-03 1.1423166e-02 2.6517929e-02 9.7961479e-03 3.5334723e-02 3.1531604e-02 2.5300172e-02 1.5549538e-02 2.4969819e-02 1.2759840e-02 2.1558690e-02 2.2929217e-02 6.4418336e-03 6.6136731e-03 4.4102106e-03 9.1481615e-04 8.6358065e-03 2.3009102e-03 7.8700864e-02 5.0063075e-02 3.9499858e-02 5.0635713e-02 5.7926279e-02 5.6986449e-02 5.8777354e-02 5.0310505e-02 6.0682610e-02 3.1865387e-02 1.2666075e-02 3.8913195e-02 2.9597893e-02 6.0871779e-02 6.0471723e-02 2.6606279e-02 3.4024273e-02 3.7905442e-02 8.9816272e-02 4.8416482e-02 3.2086335e-02 4.3354864e-02 6.3371751e-02 2.1811352e-02 3.2202223e-02 3.1242353e-02 1.6436605e-02 1.6909958e-02 5.6040246e-02 2.6101775e-02 4.3480802e-02 1.9524603e-02 5.8918015e-02 2.3597831e-02 6.3733101e-02 3.8161980e-02 4.2440703e-02 3.5140814e-02 1.5073633e-02 2.0215594e-02 3.9313992e-02 1.6000311e-02 5.0063075e-02 4.5569203e-02 3.8016354e-02 2.4659247e-02 3.6849168e-02 2.2186423e-02 3.2805330e-02 3.4269626e-02 3.8467105e-03 1.2875815e-03 2.5072619e-03 2.9525973e-02 1.2875815e-03 4.2282453e-02 2.3357390e-02 1.8646096e-02 2.2313442e-02 2.9124686e-02 2.9658732e-02 2.7480923e-02 2.3735668e-02 3.3135090e-02 1.1714292e-02 2.4574323e-03 1.8489812e-02 1.3106245e-02 3.3729170e-02 3.4672500e-02 9.9345371e-03 1.2062833e-02 1.3461486e-02 5.7431716e-02 2.5093254e-02 1.3932087e-02 1.8599958e-02 3.4955869e-02 9.5459229e-03 1.0897419e-02 1.0433767e-02 5.8375168e-03 3.2324242e-03 2.9124686e-02 8.1819777e-03 2.1845036e-02 3.6925062e-03 3.2054971e-02 6.2644754e-03 3.0710474e-02 2.2249402e-02 1.6897068e-02 1.1997033e-02 2.3970471e-03 8.3620725e-03 1.9766550e-02 1.2536577e-02 2.3357390e-02 2.0894448e-02 1.7210222e-02 1.4675128e-02 2.1017625e-02 7.9709601e-03 1.0694253e-02 1.1329979e-02 8.0862109e-04 3.9488260e-03 2.3216413e-02 2.2109950e-03 6.3313725e-02 4.2461361e-02 3.8219621e-02 3.8832114e-02 5.0144792e-02 5.1966249e-02 4.4333772e-02 4.3192281e-02 5.6977170e-02 2.6944997e-02 1.2389873e-02 3.8126276e-02 3.0729621e-02 5.8026007e-02 5.9897200e-02 2.5415278e-02 2.6079828e-02 2.5293603e-02 8.8103550e-02 4.7063061e-02 3.1649890e-02 3.5923563e-02 5.9166899e-02 2.5434205e-02 2.4297609e-02 2.3831192e-02 1.9112973e-02 1.3083158e-02 5.1398726e-02 2.1557413e-02 4.2826183e-02 1.1968109e-02 5.5744441e-02 1.7792619e-02 4.7600638e-02 4.4377594e-02 3.1768113e-02 2.4143930e-02 1.1416637e-02 2.3484839e-02 4.0313562e-02 2.8205265e-02 4.2461361e-02 3.9889484e-02 3.5980112e-02 3.3161057e-02 4.2633793e-02 2.2600989e-02 2.2605649e-02 2.2606045e-02 1.6651142e-03 2.2593118e-02 4.7494657e-04 5.5766903e-02 3.4735344e-02 2.9594132e-02 3.2557240e-02 4.1723024e-02 4.2746173e-02 3.8238608e-02 3.5278801e-02 4.7033204e-02 2.0363061e-02 7.2717611e-03 2.9420951e-02 2.2560431e-02 4.7813714e-02 4.9078515e-02 1.8314288e-02 2.0299319e-02 2.0869007e-02 7.5386883e-02 3.7549246e-02 2.3623644e-02 2.8844458e-02 4.9142563e-02 1.7633849e-02 1.8744071e-02 1.8222205e-02 1.2402012e-02 8.3988430e-03 4.2147698e-02 1.5629140e-02 3.3602383e-02 8.3018288e-03 4.5796447e-02 1.2702505e-02 4.1687026e-02 3.3943329e-02 2.5980350e-02 1.9415550e-02 7.0260502e-03 1.6012157e-02 3.1063717e-02 1.9520820e-02 3.4735344e-02 3.1983672e-02 2.7741891e-02 2.3833980e-02 3.2440091e-02 1.5637054e-02 1.7874227e-02 1.8281297e-02 1.4972117e-02 3.8328039e-04 6.3723448e-02 3.8719440e-02 3.0384566e-02 3.8629777e-02 4.5827446e-02 4.5472032e-02 4.5653244e-02 3.9023449e-02 4.9116846e-02 2.2930601e-02 7.4083466e-03 2.9962579e-02 2.2100137e-02 4.9469036e-02 4.9598054e-02 1.8997460e-02 2.4379661e-02 2.7279745e-02 7.6566902e-02 3.8413283e-02 2.3958684e-02 3.2715136e-02 5.1484761e-02 1.5886249e-02 2.2794916e-02 2.2023153e-02 1.1094162e-02 1.0331639e-02 4.4681292e-02 1.7978253e-02 3.4097516e-02 1.2099271e-02 4.7608429e-02 1.5662138e-02 4.9957613e-02 3.0977854e-02 3.1455087e-02 2.5013254e-02 8.8604244e-03 1.4432007e-02 3.0714741e-02 1.3426163e-02 3.8719440e-02 3.4992868e-02 2.8902863e-02 1.9656006e-02 2.9684992e-02 1.5494562e-02 2.3064694e-02 2.4203450e-02 1.8836956e-02 1.3755831e-01 9.8685385e-02 8.2425470e-02 1.0000651e-01 1.0925759e-01 1.0726669e-01 1.1120208e-01 9.8918033e-02 1.1167083e-01 7.2661493e-02 4.1665349e-02 8.1426911e-02 6.7501542e-02 1.1159988e-01 1.1014878e-01 6.3989575e-02 7.6224987e-02 8.1835804e-02 1.4773346e-01 9.4587815e-02 7.1696153e-02 8.9447560e-02 1.1534202e-01 5.4876111e-02 7.3538690e-02 7.2081812e-02 4.6952332e-02 4.9385723e-02 1.0589901e-01 6.4038977e-02 8.7644093e-02 5.3625558e-02 1.0917520e-01 6.0322002e-02 1.1787190e-01 7.6918821e-02 8.8499122e-02 7.7885622e-02 4.6255911e-02 5.2612368e-02 8.1239445e-02 4.0019052e-02 9.8685385e-02 9.2131722e-02 8.0633770e-02 5.6243805e-02 7.5363077e-02 5.6811044e-02 7.4440381e-02 7.6512320e-02 5.8097767e-02 3.5211195e-02 2.8463781e-02 3.4215950e-02 4.2147698e-02 4.2414656e-02 4.0542228e-02 3.5615822e-02 4.6295613e-02 2.0363061e-02 6.3798220e-03 2.8163995e-02 2.0925233e-02 4.6838114e-02 4.7487902e-02 1.7368541e-02 2.1117782e-02 2.2967145e-02 7.3810390e-02 3.6316652e-02 2.2375879e-02 2.9367287e-02 4.8511603e-02 1.5463833e-02 1.9583828e-02 1.8938570e-02 1.0601468e-02 8.3988430e-03 4.1723024e-02 1.5629140e-02 3.2244606e-02 9.3153170e-03 4.4933305e-02 1.3111918e-02 4.4412271e-02 3.0809608e-02 2.7438827e-02 2.1093197e-02 7.0260502e-03 1.3969417e-02 2.9313944e-02 1.5218585e-02 3.5211195e-02 3.1983672e-02 2.6841235e-02 2.0313092e-02 2.9439905e-02 1.4332974e-02 1.9364178e-02 2.0170126e-02 3.9924060e-03 1.0538198e-02 3.2148883e-03 2.7366019e-03 4.4621109e-03 1.7397347e-03 4.1425525e-03 5.2723146e-03 1.1136640e-02 2.9112354e-02 1.1160954e-02 1.6541883e-02 6.0336652e-03 8.3590571e-03 1.6026960e-02 9.4770245e-03 8.7523451e-03 9.5282841e-03 8.5487892e-03 1.3900105e-02 5.7309995e-03 4.7773544e-03 2.3853571e-02 1.0495078e-02 1.1045936e-02 2.7111420e-02 2.3076623e-02 4.8207470e-03 1.4681091e-02 1.0141190e-02 2.1316125e-02 5.8141126e-03 1.6474359e-02 1.3064397e-03 2.0347529e-02 5.7992324e-03 9.5368199e-03 2.5369333e-02 2.4565418e-02 1.2896533e-02 4.4389699e-02 3.9924060e-03 5.8442541e-03 1.0341765e-02 2.9771308e-02 2.0024906e-02 2.0168043e-02 1.0668382e-02 1.0461554e-02 1.6977050e-03 8.0038314e-04 3.2736649e-04 7.3247771e-04 1.7861084e-03 1.2199790e-05 1.6168506e-03 2.0903986e-03 1.2529506e-02 1.9650001e-03 4.3150624e-03 2.0139182e-03 3.1759995e-03 4.1258242e-03 2.0323215e-03 3.5878910e-03 9.4977361e-03 1.5488203e-03 3.0112961e-03 2.7909675e-04 1.8152664e-03 8.3701176e-03 2.5714996e-03 2.7148441e-03 1.0520423e-02 9.2964166e-03 7.5284963e-04 3.9888853e-03 1.8331853e-03 9.4586856e-03 1.6039412e-03 5.4889906e-03 2.8052973e-03 7.3341015e-03 1.2316222e-03 3.2601592e-03 1.0868316e-02 8.8162037e-03 2.9210959e-03 2.1963225e-02 0.0000000e+00 1.8230646e-04 1.5254865e-03 1.2273284e-02 6.9788794e-03 6.3577637e-03 3.5631537e-03 4.1101326e-03 4.2047124e-03 2.6033919e-03 1.7916004e-03 6.7524244e-03 1.5279872e-03 2.3303877e-03 1.3468412e-03 7.9573793e-03 9.8329441e-06 8.0754860e-04 2.3639655e-03 2.4986002e-03 1.3799583e-03 2.6729899e-03 6.2996248e-03 1.0853281e-02 4.8574547e-04 3.9201723e-04 1.4418237e-03 2.9002781e-03 3.0725233e-03 3.0407572e-03 2.9093497e-03 5.1175009e-03 6.6599859e-03 1.5926877e-03 2.5889139e-03 1.3713626e-04 8.5117654e-03 1.9829707e-03 4.3442759e-03 8.7298845e-03 2.1325966e-03 3.5994846e-03 5.1472858e-03 7.9423211e-03 3.5289513e-03 2.0294631e-04 1.1925423e-02 1.6977050e-03 7.8274456e-04 4.8500499e-05 4.9289073e-03 1.8879551e-03 2.5218165e-03 4.9495642e-03 6.1589498e-03 1.4775011e-03 2.8508338e-03 3.8437964e-04 1.0082748e-03 4.3870644e-03 2.7766403e-03 1.3317076e-02 4.6107743e-03 7.0365754e-03 5.0970174e-03 7.0294656e-03 5.8145812e-03 1.6991258e-03 1.6190058e-03 1.4192459e-02 4.5311136e-03 5.3518685e-03 7.5607221e-04 4.5347742e-03 1.1372343e-02 2.1219078e-03 2.3903563e-03 1.2745790e-02 9.1602079e-03 2.9606388e-03 4.3905218e-03 4.7897027e-03 8.0493172e-03 4.4779450e-03 5.1908739e-03 9.8295198e-04 1.2281083e-02 3.7969538e-04 1.7665186e-03 1.0605818e-02 1.1571194e-02 6.2101277e-03 2.7119271e-02 8.0038314e-04 1.4492761e-03 3.6743113e-03 1.7056692e-02 1.1710246e-02 8.2821554e-03 2.2240766e-03 2.2684005e-03 3.0380682e-04 2.0343287e-03 2.8037036e-04 8.3123377e-04 3.9939935e-03 1.6647447e-02 2.9061444e-03 6.1278767e-03 1.1769582e-03 2.2926522e-03 6.3717175e-03 3.9793414e-03 5.6921961e-03 6.6386310e-03 1.6577942e-03 4.6178179e-03 1.2108195e-03 8.4773320e-04 1.1017765e-02 4.7223063e-03 4.9236953e-03 1.3900771e-02 1.3046714e-02 3.7972280e-04 6.5335900e-03 2.3480655e-03 1.3286542e-02 9.3142474e-04 8.4820747e-03 2.8343361e-03 8.2403259e-03 2.5815546e-03 5.4537681e-03 1.4900562e-02 1.1670902e-02 3.7659184e-03 2.5593433e-02 3.2736649e-04 7.7091107e-04 2.6079554e-03 1.4611634e-02 8.0055625e-03 9.0564615e-03 5.9260895e-03 6.5055071e-03 3.8779943e-03 5.7015696e-04 1.7303569e-04 4.1690936e-03 1.6322670e-02 1.9974930e-03 5.0009825e-03 3.2570724e-04 9.5057293e-04 5.8304604e-03 4.8059418e-03 7.5457291e-03 5.1241997e-03 6.9111123e-04 3.7782470e-03 1.7092344e-03 2.5698914e-04 9.5453465e-03 5.5846827e-03 5.6938512e-03 1.2788699e-02 1.3393372e-02 7.3809633e-06 6.7682476e-03 1.3200598e-03 1.4442150e-02 1.8441195e-04 9.0822620e-03 4.9911804e-03 5.8437761e-03 3.8589879e-03 6.9829557e-03 1.5273413e-02 1.0318402e-02 2.4584659e-03 2.2525564e-02 7.3247771e-04 8.2385517e-04 1.9942911e-03 1.2041849e-02 5.7258386e-03 8.2738022e-03 7.3225251e-03 8.2204997e-03 2.0515682e-03 5.4510656e-03 5.2217407e-03 1.7964531e-02 7.2742632e-03 1.0588415e-02 6.2949332e-03 8.6345788e-03 9.1795241e-03 3.5876856e-03 2.6991302e-03 1.4376485e-02 6.5862079e-03 8.4804528e-03 2.1372463e-03 5.3927480e-03 1.5896874e-02 4.1394106e-03 4.5375167e-03 1.7523452e-02 1.2909742e-02 4.0941134e-03 7.2891356e-03 7.2227860e-03 1.1126344e-02 5.7124492e-03 8.1126160e-03 1.3907276e-04 1.6260954e-02 1.3854449e-03 3.2122771e-03 1.4553993e-02 1.6151019e-02 9.1578470e-03 3.3857588e-02 1.7861084e-03 2.9619943e-03 6.1829720e-03 2.2326762e-02 1.5680260e-02 1.2223630e-02 3.9245535e-03 3.7050303e-03 1.3686449e-03 2.1603250e-03 1.2649428e-02 1.7801502e-03 4.1458992e-03 1.7278919e-03 2.7991042e-03 4.0888611e-03 2.2218452e-03 3.9716339e-03 8.9665631e-03 1.3013529e-03 2.8806398e-03 3.4594790e-04 1.5677728e-03 8.1916354e-03 2.7802616e-03 2.9107124e-03 1.0452698e-02 9.5134829e-03 5.8171764e-04 4.1098444e-03 1.6001879e-03 9.8257537e-03 1.3468918e-03 5.7037585e-03 3.1132824e-03 6.8805675e-03 1.4645264e-03 3.5912866e-03 1.1107885e-02 8.6722521e-03 2.6587395e-03 2.1561220e-02 1.2199790e-05 1.4772036e-04 1.4023821e-03 1.1878859e-02 6.5538274e-03 6.3060622e-03 3.8811059e-03 4.4873121e-03 5.6719205e-03 1.8601099e-02 2.5037679e-03 5.8026549e-03 3.0990295e-05 3.8568498e-04 7.1301713e-03 6.6581271e-03 9.9891912e-03 3.5467136e-03 7.7986655e-04 4.6294369e-03 2.9226786e-03 3.3006174e-05 1.0560037e-02 7.5497742e-03 7.6393818e-03 1.4326258e-02 1.5841028e-02 1.7171643e-04 8.5994033e-03 1.5671556e-03 1.7334506e-02 1.9316046e-05 1.1306536e-02 6.6195881e-03 5.5794173e-03 5.6606012e-03 9.3044548e-03 1.7865075e-02 1.1490070e-02 2.7178516e-03 2.3185889e-02 1.6168506e-03 1.6521970e-03 2.7157547e-03 1.2355858e-02 5.5768992e-03 9.6617637e-03 9.6558503e-03 1.0729574e-02 4.4050618e-03 1.4462579e-03 1.4084514e-03 6.0437228e-03 6.9280724e-03 5.9092958e-04 3.5267150e-04 2.3264461e-03 1.8099026e-02 3.0921484e-03 8.6005594e-04 9.1287231e-04 6.3843974e-03 3.0627943e-03 4.0297343e-04 3.2526954e-04 3.6399798e-03 2.6478661e-03 3.9939935e-03 3.1762278e-04 2.2988741e-03 3.2596799e-03 5.3016678e-03 9.7044163e-04 7.0435695e-03 5.7009823e-03 1.4263218e-03 1.5142545e-03 3.5226184e-03 3.0792135e-03 2.3251410e-03 1.2857609e-02 2.0903986e-03 1.3292907e-03 8.8570158e-04 6.8622586e-03 5.1349839e-03 1.4775225e-03 1.2815928e-03 1.9995407e-03 7.7887386e-03 4.2718762e-03 1.8880208e-02 1.9223691e-02 2.7240761e-03 5.5247078e-03 8.3688264e-03 3.7206770e-02 1.2358468e-02 4.8889738e-03 9.3012979e-03 2.0063076e-02 2.3808756e-03 4.8847228e-03 4.4654367e-03 8.0211221e-04 5.5572260e-04 1.5849918e-02 2.4737465e-03 1.0011166e-02 2.0320189e-03 1.7697241e-02 2.0106232e-03 2.1009310e-02 1.0002251e-02 9.3994441e-03 6.7786672e-03 4.3329436e-04 1.8154672e-03 8.4395042e-03 6.2214471e-03 1.2529506e-02 1.0325253e-02 7.1412079e-03 5.6981926e-03 9.1712120e-03 1.6108637e-03 5.7438691e-03 6.7900644e-03 6.8888062e-04 2.5080626e-03 2.5613963e-03 1.3274318e-03 2.8814590e-03 6.6724246e-03 1.1044919e-02 5.3338455e-04 3.3999007e-04 1.6762239e-03 3.1000026e-03 2.8256325e-03 3.2387801e-03 3.0867064e-03 4.8730365e-03 6.6378483e-03 1.7822362e-03 2.6462661e-03 1.4123935e-04 8.6230682e-03 2.1318487e-03 4.4281251e-03 9.3233552e-03 1.8673327e-03 3.9343847e-03 5.4568897e-03 7.9015195e-03 3.2859077e-03 1.3180217e-04 1.1320265e-02 1.9650001e-03 9.6799653e-04 7.8282421e-05 4.5166083e-03 1.6331972e-03 2.3904078e-03 5.2224500e-03 6.4841213e-03 5.7642096e-03 5.5885978e-03 3.7048759e-04 3.1695738e-03 7.3434699e-03 1.6768125e-02 2.3743894e-03 1.1932462e-04 3.2200650e-03 6.6976868e-03 7.3134819e-04 3.2564607e-03 2.9664521e-03 1.9437509e-03 4.0525881e-03 4.6657625e-03 1.7256268e-03 1.3667365e-03 6.4082141e-03 5.2167922e-03 3.0823107e-03 1.3133486e-02 1.7068449e-03 5.3434665e-03 5.8085657e-03 4.9261084e-03 9.6684380e-04 7.1816432e-04 6.8418503e-03 4.3150624e-03 2.7358698e-03 7.4246714e-04 2.1999970e-03 1.3715192e-03 6.3897855e-04 5.2531215e-03 6.6567087e-03 1.9959041e-04 7.2877827e-03 7.2348769e-03 1.0891220e-02 3.2459725e-03 7.4122702e-04 4.6749634e-03 3.3669230e-03 7.9583955e-05 1.0437521e-02 8.1436642e-03 8.2061472e-03 1.4337740e-02 1.6319841e-02 3.0462366e-04 9.0085125e-03 1.5214225e-03 1.8068443e-02 2.5934558e-05 1.1842923e-02 7.5545176e-03 5.1162105e-03 6.3606028e-03 1.0107865e-02 1.8360102e-02 1.1411116e-02 2.5943653e-03 2.2590562e-02 2.0139182e-03 1.9469619e-03 2.8165944e-03 1.1880076e-02 5.1539049e-03 9.7568917e-03 1.0422756e-02 1.1596876e-02 7.5663186e-03 8.6488322e-03 1.3133604e-02 3.0107568e-03 7.9406152e-04 4.7466095e-03 4.5711602e-03 4.9936224e-04 9.9296431e-03 9.5749791e-03 9.5587917e-03 1.4081306e-02 1.7234890e-02 8.7040364e-04 9.9040316e-03 1.5012448e-03 1.9639569e-02 3.0179394e-04 1.3010570e-02 1.0161281e-02 3.9519870e-03 8.1767554e-03 1.2080688e-02 1.9279439e-02 1.0989202e-02 2.3266303e-03 2.0713423e-02 3.1759995e-03 2.8067172e-03 3.1032715e-03 1.0490119e-02 4.0786420e-03 9.8110939e-03 1.2277454e-02 1.3709414e-02 1.7582839e-03 4.9489607e-03 1.9934064e-02 3.5016865e-03 3.4697267e-04 2.6310541e-03 8.0617166e-03 9.6411709e-04 1.7064375e-03 1.4666650e-03 1.4745772e-03 2.1108592e-03 5.5260764e-03 5.1802495e-04 2.3341578e-03 3.7167048e-03 6.5626516e-03 1.3180061e-03 1.1565092e-02 3.6658759e-03 3.8514458e-03 3.6514318e-03 2.8125792e-03 9.8545506e-04 1.7565517e-03 7.9513446e-03 4.1258242e-03 2.7177731e-03 1.0483123e-03 3.5558284e-03 3.1649242e-03 2.4110016e-04 3.1231514e-03 4.2303212e-03 8.7046320e-04 1.9803680e-02 4.5057787e-03 2.2875453e-03 8.1473256e-04 7.2784764e-03 5.2841523e-03 3.1936914e-05 6.0579771e-05 5.5427829e-03 2.9996038e-03 4.7111715e-03 6.7018226e-04 3.8206460e-03 2.7379425e-03 6.3961850e-03 9.7000606e-04 5.0455840e-03 8.7517746e-03 5.2181192e-04 4.2086901e-04 3.8740476e-03 5.1909001e-03 4.1846614e-03 1.6912712e-02 2.0323215e-03 1.6814934e-03 2.0140995e-03 1.0246238e-02 8.0709165e-03 2.9533168e-03 3.5001524e-04 7.2402168e-04 2.4525121e-02 8.3786268e-03 5.9730003e-03 2.2241688e-03 1.0490949e-02 1.0052150e-02 8.4481649e-04 1.0306755e-03 9.7104895e-03 4.7051149e-03 7.5682486e-03 2.5521754e-03 7.8053009e-03 3.0734717e-03 9.8801938e-03 2.2384251e-03 3.6192976e-03 1.5052125e-02 6.1736712e-04 1.0280118e-04 5.5375454e-03 9.7589612e-03 8.6474768e-03 2.4375316e-02 3.5878910e-03 3.8390770e-03 5.3089764e-03 1.6859170e-02 1.4176552e-02 6.5576789e-03 2.5984028e-04 9.3320862e-05 6.7547274e-03 1.5238284e-02 1.2713534e-02 3.0890016e-03 2.3712664e-02 2.1340343e-02 2.1521507e-02 3.0005294e-02 3.3960619e-02 5.2290080e-03 2.3006673e-02 8.7125215e-03 3.6416136e-02 3.8281789e-03 2.7389250e-02 1.5157530e-02 1.2639929e-02 1.7411617e-02 2.3820503e-02 3.6850697e-02 2.5373970e-02 1.0602298e-02 3.7784525e-02 9.4977361e-03 1.0004799e-02 1.1952848e-02 2.3559319e-02 1.3109247e-02 2.3603108e-02 2.4562463e-02 2.6017612e-02 1.7241949e-03 2.0286689e-03 1.1335916e-03 5.6322483e-03 5.1221157e-03 5.0559971e-03 8.5835151e-03 1.0650923e-02 5.5566853e-04 5.1134826e-03 1.3892193e-04 1.2640034e-02 5.6438567e-04 7.4313522e-03 8.3161300e-03 2.5369950e-03 4.7342848e-03 7.3166190e-03 1.2276381e-02 6.3456614e-03 5.8916640e-04 1.5594819e-02 1.5488203e-03 9.3082136e-04 7.5849611e-04 7.0579696e-03 2.4440449e-03 5.1791902e-03 7.3166180e-03 8.5826500e-03 2.1075364e-03 5.4157920e-03 1.3748154e-03 2.4371345e-03 2.2201300e-03 2.6881175e-03 4.1574302e-03 3.5021271e-03 1.4032793e-03 9.1188034e-04 6.1085103e-03 4.1365224e-03 2.7438304e-03 1.0770631e-02 2.1519140e-03 3.9794011e-03 4.6480198e-03 5.1312578e-03 1.6288841e-03 5.4679156e-04 8.7532006e-03 3.0112961e-03 1.7262374e-03 2.9055719e-04 3.3158230e-03 1.8013008e-03 9.2887256e-04 4.2320076e-03 5.4776329e-03 3.2889511e-03 6.5019207e-03 1.1654311e-03 1.2550970e-03 7.9744383e-03 6.4095512e-03 1.6783080e-03 2.2182877e-03 1.9380758e-03 6.5001122e-03 2.8053456e-03 3.3035106e-03 3.3582234e-03 7.0744470e-03 5.6581852e-04 1.8141689e-03 7.7202001e-03 6.7565678e-03 2.7207024e-03 1.9179880e-02 2.7909675e-04 2.2008347e-04 1.1064738e-03 1.0688407e-02 6.6082062e-03 4.4435997e-03 1.9606397e-03 2.4773484e-03 1.1763001e-02 8.2259536e-03 8.3480149e-03 1.5692475e-02 1.7083720e-02 2.8355914e-04 9.4960515e-03 2.0536401e-03 1.8469924e-02 1.0092056e-04 1.2279622e-02 6.4441301e-03 6.4046448e-03 6.0274458e-03 9.8857207e-03 1.9188640e-02 1.2736270e-02 3.3492132e-03 2.4941935e-02 1.8152664e-03 1.9865894e-03 3.3071796e-03 1.3641872e-02 6.4197769e-03 1.0769048e-02 1.0309787e-02 1.1341762e-02 5.1119032e-03 4.6662587e-03 4.6505160e-04 3.2269116e-03 9.0774431e-03 2.5215213e-03 4.0057628e-03 6.1199030e-03 9.7447895e-03 3.5360157e-03 1.8995080e-02 2.6805580e-03 8.6582433e-03 8.1342278e-03 3.7198152e-03 3.9443681e-05 2.6670326e-03 3.4016785e-03 8.3701176e-03 6.1426721e-03 2.9189037e-03 1.0083175e-03 2.2713681e-03 3.9595770e-04 7.2341855e-03 8.8447009e-03 1.1230173e-05 5.1389929e-03 2.4707449e-03 5.4731760e-03 5.1980845e-04 4.3112681e-03 2.1787917e-03 7.2534046e-03 6.7672528e-04 5.6623211e-03 9.1277214e-03 7.3619628e-04 3.7016392e-04 3.2564607e-03 4.9531882e-03 4.5742792e-03 1.6438776e-02 2.5714996e-03 2.1421258e-03 2.3256067e-03 1.0175775e-02 8.4088183e-03 2.7613050e-03 2.4755840e-04 6.0873636e-04 4.6703189e-03 2.2089026e-03 5.5642336e-03 3.7824549e-04 4.1878969e-03 2.0462491e-03 7.3148116e-03 5.4583143e-04 6.1390745e-03 8.7247882e-03 9.1073145e-04 4.9034572e-04 2.9664521e-03 4.5041455e-03 4.3650355e-03 1.5605807e-02 2.7148441e-03 2.1951828e-03 2.2077213e-03 9.5777719e-03 8.0120192e-03 2.4278861e-03 3.2552080e-04 7.3734328e-04 1.7636357e-03 1.2290813e-02 2.3919392e-03 6.5534766e-03 4.2355076e-03 1.3428854e-02 2.7760250e-03 2.0719142e-02 5.3736931e-03 9.3604766e-03 7.8350168e-03 1.9437509e-03 2.3451546e-04 4.9805974e-03 3.4615227e-03 1.0520423e-02 8.1642247e-03 4.6947450e-03 2.2490758e-03 4.7893620e-03 5.2359029e-04 6.8110246e-03 8.2524171e-03 1.3046714e-02 1.1318623e-03 8.6855001e-03 5.4130623e-04 1.5131426e-02 5.6179632e-04 1.5407719e-02 1.0669118e-02 5.8683852e-03 3.5797271e-03 6.2414181e-05 2.6714308e-03 7.7088950e-03 9.8081718e-03 9.2964166e-03 7.7221233e-03 5.7330158e-03 7.6711675e-03 9.7842038e-03 1.6419007e-03 2.8364539e-03 3.5173281e-03 6.5335900e-03 1.1340078e-03 1.4192754e-02 1.5964523e-04 8.8481511e-03 5.2711173e-03 5.4383051e-03 3.8856537e-03 6.9538378e-03 1.4900562e-02 9.8443835e-03 2.2013577e-03 2.1746585e-02 7.5284963e-04 7.7091107e-04 1.8026328e-03 1.1463429e-02 5.3229428e-03 7.8904169e-03 7.2583306e-03 8.1944055e-03 3.9135288e-03 1.7196090e-03 8.1113282e-03 2.2942459e-04 9.3463740e-03 6.8261058e-03 2.3665724e-03 1.6309596e-03 1.7256268e-03 2.3397606e-03 3.5922701e-03 1.1208568e-02 3.9888853e-03 2.9455145e-03 1.9660659e-03 6.5467152e-03 6.1492489e-03 9.3538874e-04 1.2212079e-03 1.9267388e-03 1.0793365e-02 1.2592720e-03 6.0147390e-03 9.1609131e-03 1.8537817e-03 4.5335508e-03 6.6172179e-03 1.0131803e-02 4.6089934e-03 1.7595473e-04 1.2987364e-02 1.8331853e-03 9.7087402e-04 3.3609260e-04 5.4018686e-03 1.7068395e-03 3.6646912e-03 6.4787894e-03 7.7901480e-03 1.6754655e-02 6.9641544e-04 1.3182224e-02 1.4651385e-02 4.9466239e-03 2.3931163e-03 5.9263439e-04 5.4211666e-03 1.0221921e-02 1.4928620e-02 9.4586856e-03 8.3819492e-03 7.3339215e-03 1.2011676e-02 1.3620169e-02 3.6119007e-03 1.8435886e-03 2.0979826e-03 1.0794235e-02 6.9811950e-03 4.9473584e-03 5.5993500e-03 9.1192858e-03 1.7103671e-02 1.0654852e-02 2.2936354e-03 2.1887320e-02 1.6039412e-03 1.5236521e-03 2.3684718e-03 1.1405606e-02 4.9416620e-03 8.9601309e-03 9.4128890e-03 1.0537337e-02 1.0144176e-02 9.3650956e-03 2.8049928e-03 1.4261619e-03 9.6795720e-04 3.1673467e-03 5.6308712e-03 1.2399657e-02 5.4889906e-03 4.4629844e-03 3.5148873e-03 8.2921709e-03 8.5570702e-03 1.5767832e-03 9.7941910e-04 1.5044401e-03 1.9162648e-02 2.3258011e-03 4.3611697e-03 1.7154674e-02 1.9266334e-02 1.1399040e-02 3.8294804e-02 2.8052973e-03 4.2987486e-03 8.1194560e-03 2.5933333e-02 1.8564970e-02 1.4940871e-02 5.2230072e-03 4.8296564e-03 1.1136786e-02 1.2996602e-02 1.1896568e-02 3.3692870e-03 1.0295085e-03 6.7928621e-03 7.3341015e-03 5.3674586e-03 2.6380919e-03 1.7126363e-03 1.9252416e-05 3.9628066e-03 1.2371798e-02 1.4417093e-02 5.5916770e-04 7.0172680e-03 8.6736288e-03 5.5025710e-03 2.2796596e-02 1.2316222e-03 1.4793666e-03 2.9451077e-03 1.4303806e-02 1.0475492e-02 5.7470026e-03 7.8882742e-04 9.0526661e-04 4.3645694e-03 7.8632956e-03 7.2260963e-03 2.1331409e-02 3.2601592e-03 3.2514734e-03 4.2300937e-03 1.4385386e-02 1.2160577e-02 5.0215597e-03 4.3279739e-05 5.1818418e-05 3.0767343e-03 9.0015857e-03 9.8042907e-03 1.0868316e-02 9.1698320e-03 6.9442206e-03 8.2610449e-03 1.0963075e-02 2.1355881e-03 3.5431999e-03 4.2180590e-03 3.2143555e-03 3.3819870e-03 8.8162037e-03 6.5643841e-03 3.2940615e-03 1.2995092e-03 2.9075684e-03 3.1897532e-04 6.9331287e-03 8.4863836e-03 1.0164058e-02 2.9210959e-03 1.7078622e-03 4.0814516e-04 3.6297378e-03 8.7693352e-04 2.6826046e-03 6.9132822e-03 8.3825591e-03 2.1963225e-02 1.8183756e-02 1.2010413e-02 1.7351817e-03 6.4958425e-03 5.7545512e-03 1.9704198e-02 2.2181841e-02 1.8230646e-04 1.5254865e-03 1.2273284e-02 6.9788794e-03 6.3577637e-03 3.5631537e-03 4.1101326e-03 6.5324999e-04 9.4729463e-03 5.0293123e-03 4.5622977e-03 3.3814637e-03 4.1233865e-03 5.1615257e-03 2.3326030e-03 2.1577255e-03 4.0325048e-03 5.1426622e-03 1.5351757e-03 2.6422594e-03 1.3282600e-02 1.5461999e-02 3.4307479e-03 1.1531055e-02 1.3518551e-02 4.2918744e-03 5.5398788e-03 8.4122900e-05 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..2a17a2a8fb002493fff38c7ed059668867768a7e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-correlation-ml.txt @@ -0,0 +1 @@ + 9.2507465e-01 9.6528566e-01 8.7255441e-01 1.1287379e+00 8.7318727e-01 1.0767102e+00 9.1419676e-01 1.1503304e+00 9.8074509e-01 1.0135025e+00 1.0495025e+00 9.4794536e-01 9.6829273e-01 1.1345767e+00 1.1048008e+00 9.2407796e-01 1.0228634e+00 9.3853195e-01 9.9377619e-01 1.0407662e+00 9.5048989e-01 9.0465688e-01 9.8056930e-01 8.9777156e-01 9.6357127e-01 9.3864452e-01 9.9754613e-01 9.7271356e-01 8.4383151e-01 9.6981983e-01 9.7510267e-01 1.0112663e+00 7.8730400e-01 1.0299498e+00 9.9307979e-01 9.0239520e-01 8.5428231e-01 8.8972742e-01 8.5933162e-01 9.6625934e-01 9.4175449e-01 9.9120729e-01 1.0503963e+00 8.8223053e-01 1.3261434e+00 1.1063209e+00 8.4058398e-01 1.0844267e+00 1.1153093e+00 1.0092643e+00 8.9585237e-01 1.0599818e+00 1.2321707e+00 1.1359624e+00 8.3503556e-01 1.1792243e+00 7.9159781e-01 1.0830419e+00 1.2181870e+00 9.9888500e-01 1.0227144e+00 6.8557277e-01 9.6836193e-01 1.1061227e+00 1.0883453e+00 9.5681974e-01 9.9436299e-01 1.0304323e+00 1.1273949e+00 1.0735563e+00 1.0582583e+00 9.6040272e-01 1.0032137e+00 8.4900547e-01 1.1035351e+00 8.7867480e-01 9.6433176e-01 9.1850122e-01 8.9337435e-01 1.0449390e+00 8.9639384e-01 9.6704971e-01 1.0084258e+00 1.0528587e+00 1.1764481e+00 1.0913280e+00 1.0136672e+00 1.2737156e+00 9.5130359e-01 1.0367909e+00 1.1983402e+00 1.1319901e+00 1.1117462e+00 1.0343695e+00 1.0838628e+00 7.5266057e-01 1.0763316e+00 8.8067924e-01 9.6734383e-01 9.8800551e-01 1.2265742e+00 7.8833055e-01 1.0338670e+00 8.6666625e-01 9.9039950e-01 9.7142684e-01 9.3138616e-01 8.5849977e-01 8.5486301e-01 1.0516028e+00 1.1105313e+00 9.5943505e-01 9.8845171e-01 1.0566288e+00 9.9712198e-01 9.5545756e-01 1.1817974e+00 9.9128482e-01 1.0117892e+00 1.0979115e+00 1.0493943e+00 9.1318848e-01 9.3157311e-01 8.7073304e-01 1.2459441e+00 9.3412689e-01 1.0482297e+00 9.4224032e-01 9.5134153e-01 9.0857493e-01 9.7264161e-01 8.2900820e-01 9.3140549e-01 1.1330242e+00 1.0333002e+00 1.0117861e+00 1.2053255e+00 8.5291396e-01 1.0148928e+00 8.6641379e-01 9.7080819e-01 9.5457159e-01 9.5207457e-01 9.3539674e-01 9.0769069e-01 9.5322590e-01 1.1181803e+00 9.9765614e-01 7.5370610e-01 1.0807114e+00 1.0804601e+00 9.0214124e-01 8.7101998e-01 1.0167435e+00 1.2045936e+00 8.7300539e-01 1.1054300e+00 7.9145574e-01 1.0279340e+00 8.7623462e-01 1.0034756e+00 1.0386933e+00 9.3910970e-01 1.0028455e+00 9.9868824e-01 9.8752945e-01 9.8319327e-01 1.3110209e+00 8.6180633e-01 1.0993856e+00 8.5912563e-01 1.1303979e+00 9.8690459e-01 9.6910090e-01 9.1456819e-01 1.1525339e+00 1.1064552e+00 1.1062255e+00 9.7226683e-01 1.1091447e+00 1.1072238e+00 9.6544444e-01 9.6681036e-01 9.3247685e-01 9.6854634e-01 1.1035119e+00 1.1317148e+00 9.5557793e-01 9.8908485e-01 7.4873648e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b705b348fc3de5041ad9e3d6a1686af61046b2a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml-iris.txt @@ -0,0 +1 @@ + 1.4208365e-03 1.2652718e-05 8.9939315e-04 2.4232332e-04 9.9747033e-04 9.2045721e-04 2.2040648e-04 8.6480051e-04 1.2354911e-03 5.3650090e-06 1.0275886e-03 1.1695784e-03 2.3556571e-04 1.4590172e-03 1.8981327e-03 1.0939621e-03 1.2392314e-04 3.5850877e-04 8.6078038e-04 1.4490833e-03 8.4059347e-04 3.2873982e-03 2.7359832e-03 4.1316044e-03 2.7719149e-03 1.1814143e-03 1.1431285e-04 2.3850299e-04 1.3446247e-03 1.6406549e-03 1.2070654e-03 2.2241257e-03 1.4969348e-03 1.2354911e-03 7.6154552e-04 9.0853884e-04 1.2354911e-03 1.5825612e-04 2.3716586e-04 2.5806020e-04 8.5870759e-03 4.3447170e-04 2.6103416e-03 3.4026094e-03 1.2625429e-03 1.0000714e-03 2.7088099e-04 4.6161202e-05 1.7993015e-04 7.1619641e-02 7.4013940e-02 8.2336355e-02 9.3599031e-02 8.6542298e-02 9.2667602e-02 8.0934616e-02 6.7002415e-02 7.9695318e-02 8.3991107e-02 8.8330128e-02 7.6449243e-02 8.6123390e-02 9.1414445e-02 5.9767596e-02 6.8589764e-02 9.2363748e-02 7.5261304e-02 1.0768528e-01 7.8250149e-02 9.7383870e-02 6.9410330e-02 1.0895936e-01 9.1644587e-02 7.2677910e-02 7.2208930e-02 8.7635618e-02 9.3586395e-02 8.7700193e-02 5.8825053e-02 7.9271072e-02 7.4136423e-02 7.0977606e-02 1.1670751e-01 9.6691498e-02 7.7157266e-02 7.8793137e-02 9.6187418e-02 7.4355610e-02 8.6677009e-02 9.7286808e-02 8.5214421e-02 7.7419803e-02 6.8888638e-02 8.6192502e-02 7.4757686e-02 7.8851331e-02 7.5042247e-02 5.2484298e-02 7.8023694e-02 1.3991867e-01 1.2655756e-01 1.2099780e-01 1.2515784e-01 1.3134370e-01 1.3306336e-01 1.2911903e-01 1.2854613e-01 1.3655327e-01 1.1601604e-01 9.9632498e-02 1.2063863e-01 1.1404742e-01 1.3409335e-01 1.3451976e-01 1.1368563e-01 1.1469397e-01 1.1505768e-01 1.5479411e-01 1.2906390e-01 1.1634186e-01 1.2299625e-01 1.3892070e-01 1.0732534e-01 1.1401190e-01 1.1254699e-01 1.0266168e-01 1.0210743e-01 1.3111378e-01 1.0950615e-01 1.2501276e-01 1.0108759e-01 1.3297245e-01 1.0624129e-01 1.3360037e-01 1.2002867e-01 1.2233784e-01 1.1387071e-01 1.0061412e-01 1.0649150e-01 1.2174429e-01 1.0147290e-01 1.2655756e-01 1.2438709e-01 1.2138109e-01 1.1044406e-01 1.1910000e-01 1.0821359e-01 1.1609070e-01 1.1329724e-01 1.2085473e-03 1.2060695e-03 2.7592041e-03 3.0736184e-03 3.7201033e-03 1.0861043e-03 7.3910902e-04 3.4790667e-04 1.3491546e-03 2.4493052e-03 1.8482587e-04 2.3308566e-03 3.8997403e-03 6.3069928e-03 4.1362617e-03 1.5079538e-03 7.4890015e-04 4.0049414e-03 3.0763412e-04 3.2877725e-03 8.6909088e-03 1.8863199e-03 4.7592122e-03 4.5180751e-04 1.7148301e-03 8.8703626e-04 5.7128783e-04 1.7151033e-03 8.4814176e-04 4.7551630e-04 6.9313334e-03 5.8126778e-03 3.4790667e-04 9.7078221e-04 1.0390338e-03 3.4790667e-04 1.1371495e-03 7.0598263e-04 2.3100870e-03 3.1332241e-03 2.9870115e-03 3.7693564e-03 5.5008337e-03 2.0081767e-04 3.9261497e-03 1.6237803e-03 1.7731168e-03 5.9153033e-04 5.9997244e-02 6.3706418e-02 7.0131342e-02 8.0131815e-02 7.3670020e-02 8.1412444e-02 7.1132932e-02 5.6572408e-02 6.7223691e-02 7.3993918e-02 7.4363256e-02 6.6371013e-02 7.1106157e-02 7.9730716e-02 5.0610503e-02 5.7285563e-02 8.2536028e-02 6.3695818e-02 9.1877918e-02 6.6044079e-02 8.7700525e-02 5.7975072e-02 9.4407127e-02 7.9385033e-02 6.0900938e-02 6.0521931e-02 7.4070557e-02 8.1073873e-02 7.6438218e-02 4.7634460e-02 6.6728846e-02 6.1732271e-02 5.9656897e-02 1.0363139e-01 8.7312695e-02 6.8806126e-02 6.7142432e-02 8.0911573e-02 6.5091322e-02 7.4541034e-02 8.5313436e-02 7.4229332e-02 6.5328348e-02 5.7461491e-02 7.4891760e-02 6.5136264e-02 6.8598864e-02 6.3641018e-02 4.2790811e-02 6.7276779e-02 1.2872765e-01 1.1385917e-01 1.0708423e-01 1.1221780e-01 1.1844388e-01 1.1798239e-01 1.1767648e-01 1.1356773e-01 1.2073038e-01 1.0467824e-01 8.8441784e-02 1.0671832e-01 1.0091826e-01 1.2051300e-01 1.2244533e-01 1.0247664e-01 1.0203920e-01 1.0334656e-01 1.3764340e-01 1.1314999e-01 1.0390175e-01 1.1148602e-01 1.2274267e-01 9.3929112e-02 1.0239198e-01 9.9372667e-02 9.0109024e-02 9.0770318e-02 1.1749345e-01 9.5509620e-02 1.0956056e-01 8.9331297e-02 1.1936188e-01 9.3207628e-02 1.1935153e-01 1.0516553e-01 1.1204585e-01 1.0191688e-01 8.9582588e-02 9.3806716e-02 1.0922100e-01 8.9087100e-02 1.1385917e-01 1.1193127e-01 1.0978099e-01 9.7766696e-02 1.0448839e-01 9.5849546e-02 1.0619992e-01 1.0212555e-01 7.8301662e-04 3.3186074e-04 9.6097551e-04 9.6384587e-04 1.7160230e-04 7.1714495e-04 1.0915291e-03 1.4406904e-05 9.9431295e-04 1.0280837e-03 3.4520010e-04 1.6070142e-03 2.0814960e-03 1.1810349e-03 9.3270090e-05 2.4892291e-04 9.5000112e-04 1.2447556e-03 8.3736374e-04 3.6303226e-03 2.4141846e-03 3.9965261e-03 2.4688022e-03 1.0115165e-03 6.9871786e-05 1.7487334e-04 1.2251185e-03 1.4398826e-03 9.8199498e-04 2.5137187e-03 1.7466742e-03 1.0915291e-03 7.0690363e-04 8.5846505e-04 1.0915291e-03 1.0992291e-04 1.6427013e-04 2.8562896e-04 8.0123750e-03 5.0490687e-04 2.4076078e-03 3.3222239e-03 1.0270492e-03 1.0987887e-03 2.4862356e-04 7.8815959e-05 1.1120052e-04 7.0071463e-02 7.2494258e-02 8.0694698e-02 9.1816479e-02 8.4823937e-02 9.1055284e-02 7.9406161e-02 6.5540015e-02 7.8075821e-02 8.2418924e-02 8.6586217e-02 7.4908999e-02 8.4375857e-02 8.9771433e-02 5.8365951e-02 6.7055640e-02 9.0792516e-02 7.3755504e-02 1.0570869e-01 7.6652799e-02 9.5758989e-02 6.7858347e-02 1.0707149e-01 9.0015148e-02 7.1111432e-02 7.0634591e-02 8.5909852e-02 9.1841705e-02 8.6060650e-02 5.7382885e-02 7.7642663e-02 7.2560884e-02 6.9439824e-02 1.1486601e-01 9.5132094e-02 7.5722276e-02 7.7186494e-02 9.4329550e-02 7.2913445e-02 8.4999890e-02 9.5631654e-02 8.3632299e-02 7.5814411e-02 6.7360493e-02 8.4581854e-02 7.3324210e-02 7.7335911e-02 7.3484711e-02 5.1093482e-02 7.6474851e-02 1.3800148e-01 1.2463801e-01 1.1904450e-01 1.2328593e-01 1.2938789e-01 1.3104169e-01 1.2726294e-01 1.2658511e-01 1.3448678e-01 1.1418055e-01 9.7888383e-02 1.1868360e-01 1.1213978e-01 1.3206545e-01 1.3251384e-01 1.1184454e-01 1.1286955e-01 1.1328841e-01 1.5256500e-01 1.2703121e-01 1.1444439e-01 1.2112577e-01 1.3684054e-01 1.0544428e-01 1.1220824e-01 1.1073079e-01 1.0084086e-01 1.0036834e-01 1.2912019e-01 1.0768201e-01 1.2300696e-01 9.9385216e-02 1.3095409e-01 1.0446385e-01 1.3171213e-01 1.1800444e-01 1.2052688e-01 1.1209190e-01 9.8892088e-02 1.0463359e-01 1.1979721e-01 9.9600101e-02 1.2463801e-01 1.2247195e-01 1.1948197e-01 1.0852184e-01 1.1709036e-01 1.0637133e-01 1.1433097e-01 1.1154058e-01 1.2829581e-03 8.6520525e-04 1.3042912e-03 2.3052671e-04 6.0609671e-05 6.1408538e-04 7.9384016e-04 2.5551469e-04 9.4346154e-04 1.8930050e-03 4.6203036e-03 3.8649853e-03 3.3273220e-03 9.7135787e-04 2.5836286e-04 1.6395377e-03 4.6720392e-04 1.3833444e-03 6.8585778e-03 1.1817616e-03 1.4184724e-03 1.2935682e-03 4.4534899e-04 4.3337262e-04 9.9734142e-04 6.2957380e-05 2.1802414e-04 1.3452346e-03 3.6759458e-03 3.7514511e-03 6.1408538e-04 2.3527566e-03 2.5967147e-03 6.1408538e-04 3.1896708e-04 3.0643540e-04 1.7034162e-03 7.0964884e-03 1.0371098e-03 1.9760564e-03 1.6993217e-03 9.2490489e-04 1.2129757e-03 2.8785057e-04 7.8777499e-04 6.4144968e-04 5.7636535e-02 5.9786679e-02 6.7275391e-02 7.7706661e-02 7.1288776e-02 7.6308806e-02 6.5987844e-02 5.3398709e-02 6.4839697e-02 6.8887148e-02 7.2874646e-02 6.2111692e-02 7.1088473e-02 7.5274214e-02 4.7295630e-02 5.5048251e-02 7.6266639e-02 6.0532100e-02 9.0997542e-02 6.3501941e-02 8.1155480e-02 5.5841790e-02 9.1620605e-02 7.5304976e-02 5.8627379e-02 5.8302297e-02 7.2188128e-02 7.7632065e-02 7.2128571e-02 4.6353347e-02 6.4522763e-02 5.9860052e-02 5.7075256e-02 9.8501473e-02 8.0208982e-02 6.2676929e-02 6.4117314e-02 8.0306154e-02 5.9903400e-02 7.1264506e-02 8.0454669e-02 6.9667510e-02 6.2855874e-02 5.5234852e-02 7.0611788e-02 6.0083969e-02 6.3933681e-02 6.0638614e-02 4.1119113e-02 6.3291748e-02 1.2072945e-01 1.0797760e-01 1.0284307e-01 1.0630032e-01 1.1246316e-01 1.1377579e-01 1.1035397e-01 1.0939330e-01 1.1704519e-01 9.8543065e-02 8.3389076e-02 1.0253622e-01 9.6610654e-02 1.1523295e-01 1.1624035e-01 9.6621030e-02 9.6718555e-02 9.7003685e-02 1.3426257e-01 1.1013293e-01 9.8838972e-02 1.0496266e-01 1.1920082e-01 9.0400878e-02 9.6352086e-02 9.4617133e-02 8.6118226e-02 8.5443225e-02 1.1226469e-01 9.1815383e-02 1.0642172e-01 8.4132371e-02 1.1413570e-01 8.8823115e-02 1.1373227e-01 1.0228600e-01 1.0454965e-01 9.5917796e-02 8.4129252e-02 8.9732713e-02 1.0404039e-01 8.5714179e-02 1.0797760e-01 1.0611357e-01 1.0375975e-01 9.3828435e-02 1.0141953e-01 9.1231247e-02 9.8764813e-02 9.5558448e-02 7.0033377e-04 3.9650610e-04 5.3529876e-04 1.4703029e-03 2.2471049e-03 2.6137215e-04 9.1585095e-04 2.3098853e-03 3.2779352e-04 1.7003275e-03 9.5035099e-04 8.4163249e-04 3.6423601e-04 8.6760304e-04 2.6110376e-04 2.4965606e-03 5.0990123e-04 2.2208392e-03 3.4995017e-03 3.9813106e-03 4.2652650e-03 1.4776191e-03 5.3856223e-04 9.6152184e-04 1.6178695e-03 2.4296336e-03 2.2824176e-03 1.0483334e-03 6.6735604e-04 2.2471049e-03 1.7166964e-03 1.9224889e-03 2.2471049e-03 4.4953685e-04 7.5090712e-04 3.1050470e-04 1.1530910e-02 8.0837373e-05 2.6173161e-03 2.7612054e-03 2.3974656e-03 3.9140870e-04 3.5730731e-04 1.1232648e-04 8.0278741e-04 7.4728046e-02 7.6441141e-02 8.5477412e-02 9.7141382e-02 8.9947057e-02 9.5081677e-02 8.2962705e-02 6.9633999e-02 8.3013931e-02 8.6069979e-02 9.2215558e-02 7.8736928e-02 9.0603515e-02 9.4074986e-02 6.2034704e-02 7.1640320e-02 9.4150759e-02 7.8195110e-02 1.1214391e-01 8.1468219e-02 9.9059263e-02 7.2514318e-02 1.1269547e-01 9.4545020e-02 7.5842542e-02 7.5358360e-02 9.1332869e-02 9.6662705e-02 9.0277244e-02 6.2066860e-02 8.2644288e-02 7.7554694e-02 7.3959493e-02 1.1955630e-01 9.8181734e-02 7.8602674e-02 8.1755435e-02 1.0058819e-01 7.6248524e-02 8.9701900e-02 9.9938282e-02 8.7676596e-02 8.0619290e-02 7.1976555e-02 8.8793557e-02 7.6779152e-02 8.1107438e-02 7.7952944e-02 5.5245517e-02 8.0550459e-02 1.4162183e-01 1.2912349e-01 1.2423521e-01 1.2779447e-01 1.3393410e-01 1.3660889e-01 1.3105158e-01 1.3208577e-01 1.4040000e-01 1.1817736e-01 1.0200650e-01 1.2388995e-01 1.1706801e-01 1.3699958e-01 1.3682207e-01 1.1586916e-01 1.1739162e-01 1.1729454e-01 1.5902469e-01 1.3308573e-01 1.1901641e-01 1.2511327e-01 1.4289089e-01 1.1059070e-01 1.1627926e-01 1.1550831e-01 1.0561378e-01 1.0446495e-01 1.3405102e-01 1.1291439e-01 1.2888996e-01 1.0359625e-01 1.3590097e-01 1.0925250e-01 1.3665207e-01 1.2379539e-01 1.2392962e-01 1.1624448e-01 1.0286550e-01 1.0945264e-01 1.2440339e-01 1.0449561e-01 1.2912349e-01 1.2690130e-01 1.2362142e-01 1.1341467e-01 1.2276171e-01 1.1097585e-01 1.1759891e-01 1.1534218e-01 1.3143808e-04 7.3710840e-04 1.1313742e-03 2.6277162e-03 9.9332749e-04 4.8298989e-04 2.9659782e-03 1.8303797e-03 3.9657692e-03 1.4753738e-03 1.6266891e-03 7.0233916e-04 8.0313831e-04 3.4526160e-04 2.3291483e-03 1.3867759e-04 4.2228272e-03 1.6991343e-03 2.3223655e-03 3.8453210e-03 4.2904903e-04 9.9302567e-04 1.7706867e-03 9.4981017e-04 1.8259864e-03 2.0820613e-03 2.1473879e-03 2.0420431e-03 2.6277162e-03 3.0779094e-03 3.4332541e-03 2.6277162e-03 6.3280964e-04 1.0576914e-03 9.5198627e-04 1.0925795e-02 3.7286463e-04 7.9546610e-04 9.1841431e-04 2.1468126e-03 4.9129575e-04 4.3562197e-04 7.5083238e-04 1.3686608e-03 6.3901299e-02 6.4740623e-02 7.3708779e-02 8.4613714e-02 7.7866771e-02 8.2261058e-02 7.0449151e-02 5.8874682e-02 7.1767088e-02 7.3210535e-02 8.0660949e-02 6.6601983e-02 8.0033785e-02 8.1391959e-02 5.1369939e-02 6.0897790e-02 8.0716992e-02 6.7403323e-02 9.9203670e-02 7.0276809e-02 8.4922276e-02 6.1688045e-02 9.9339240e-02 8.2362360e-02 6.4928234e-02 6.4360101e-02 7.9641814e-02 8.3721620e-02 7.7549963e-02 5.2617898e-02 7.1414187e-02 6.6946935e-02 6.3031902e-02 1.0509118e-01 8.4332170e-02 6.6064468e-02 7.0064616e-02 8.8758294e-02 6.4379548e-02 7.7371173e-02 8.7052850e-02 7.5305342e-02 6.9340944e-02 6.1339869e-02 7.6377320e-02 6.5179636e-02 6.9093895e-02 6.6669498e-02 4.5609365e-02 6.8684945e-02 1.2445912e-01 1.1341836e-01 1.0935772e-01 1.1262566e-01 1.1789507e-01 1.2147174e-01 1.1488682e-01 1.1752559e-01 1.2531063e-01 1.0271865e-01 8.7888567e-02 1.0902443e-01 1.0234160e-01 1.2080033e-01 1.1990073e-01 1.0043696e-01 1.0286413e-01 1.0252340e-01 1.4292168e-01 1.1866325e-01 1.0381139e-01 1.0919240e-01 1.2785249e-01 9.6570465e-02 1.0127523e-01 1.0149554e-01 9.1688518e-02 9.0323099e-02 1.1822766e-01 9.9584713e-02 1.1452014e-01 9.0018133e-02 1.1983081e-01 9.5741335e-02 1.2190290e-01 1.0915996e-01 1.0773474e-01 1.0161859e-01 8.8729453e-02 9.5169428e-02 1.0868349e-01 9.0278091e-02 1.1341836e-01 1.1118524e-01 1.0767597e-01 9.8555096e-02 1.0809822e-01 9.6490550e-02 1.0179914e-01 1.0040847e-01 9.0953179e-04 1.6478123e-03 3.1324421e-03 9.3747882e-04 6.8074049e-04 3.4285457e-03 1.4256139e-03 3.3141786e-03 8.1135619e-04 1.2040955e-03 7.3894006e-04 1.1469835e-03 5.4914496e-05 3.0238895e-03 1.1512346e-04 2.9874978e-03 2.7356591e-03 2.9755481e-03 4.8570629e-03 9.8132331e-04 1.1267736e-03 1.9187302e-03 1.4320892e-03 2.5472569e-03 2.7129147e-03 1.2621760e-03 1.1868918e-03 3.1324421e-03 3.1260816e-03 3.4622842e-03 3.1324421e-03 7.8737454e-04 1.2923124e-03 7.7291736e-04 1.2676988e-02 1.5795155e-04 1.4073300e-03 1.3093851e-03 2.8558230e-03 2.3589004e-04 5.3160641e-04 6.3306680e-04 1.5563919e-03 6.9394652e-02 7.0160248e-02 7.9549278e-02 9.0909253e-02 8.3929778e-02 8.8133516e-02 7.5949213e-02 6.4094635e-02 7.7538115e-02 7.8838295e-02 8.6828513e-02 7.2078729e-02 8.6190925e-02 8.7328483e-02 5.6305232e-02 6.6307663e-02 8.6433769e-02 7.2861306e-02 1.0610432e-01 7.5977192e-02 9.0782134e-02 6.7147548e-02 1.0605640e-01 8.8295560e-02 7.0476640e-02 6.9912539e-02 8.5755505e-02 8.9909894e-02 8.3415192e-02 5.7694397e-02 7.7198547e-02 7.2551886e-02 6.8485682e-02 1.1174631e-01 9.0047290e-02 7.1258462e-02 7.5770197e-02 9.5276007e-02 6.9606963e-02 8.3332111e-02 9.3091350e-02 8.1019819e-02 7.5041473e-02 6.6748030e-02 8.2172293e-02 7.0413691e-02 7.4567733e-02 7.2221920e-02 5.0422561e-02 7.4234075e-02 1.3135838e-01 1.2029572e-01 1.1630277e-01 1.1941581e-01 1.2489530e-01 1.2871814e-01 1.2159882e-01 1.2460620e-01 1.3270425e-01 1.0925415e-01 9.4076611e-02 1.1596894e-01 1.0908894e-01 1.2799150e-01 1.2695158e-01 1.0694484e-01 1.0944639e-01 1.0895711e-01 1.5084375e-01 1.2591962e-01 1.1052596e-01 1.1587184e-01 1.3530738e-01 1.0320818e-01 1.0775506e-01 1.0806337e-01 9.8123191e-02 9.6541726e-02 1.2533326e-01 1.0616585e-01 1.2166800e-01 9.6181548e-02 1.2699662e-01 1.0216112e-01 1.2885603e-01 1.1626103e-01 1.1421827e-01 1.0807124e-01 9.4882428e-02 1.0171954e-01 1.1554226e-01 9.6763759e-02 1.2029572e-01 1.1801757e-01 1.1438908e-01 1.0525128e-01 1.1515210e-01 1.0301668e-01 1.0810316e-01 1.0676998e-01 2.4407151e-04 6.8243680e-04 1.6882982e-04 4.2217018e-04 8.1245396e-04 8.1915702e-04 2.7980568e-03 2.6783721e-03 2.0076713e-03 3.3526400e-04 9.3506008e-05 1.0407900e-03 7.3148476e-04 9.1895790e-04 4.8425923e-03 1.7878106e-03 2.5638304e-03 1.8092053e-03 6.2482332e-04 4.5470127e-05 3.8680919e-04 4.8577398e-04 7.0932539e-04 1.0773286e-03 2.7081281e-03 2.3916675e-03 6.8243680e-04 1.3234869e-03 1.5152295e-03 6.8243680e-04 1.7279927e-05 4.4719936e-05 7.6774714e-04 7.6386402e-03 5.1509749e-04 2.1386706e-03 2.3673979e-03 8.8641907e-04 8.8317423e-04 5.7646989e-05 1.8767975e-04 1.8238427e-04 6.4591491e-02 6.6891146e-02 7.4787553e-02 8.5653640e-02 7.8909235e-02 8.4481757e-02 7.3468926e-02 6.0165176e-02 7.2232139e-02 7.6459237e-02 8.0572670e-02 6.9287036e-02 7.8547451e-02 8.3338681e-02 5.3514192e-02 6.1787978e-02 8.4336540e-02 6.7840538e-02 9.9351761e-02 7.0839680e-02 8.9318727e-02 6.2598635e-02 1.0029777e-01 8.3444651e-02 6.5618944e-02 6.5228710e-02 7.9886645e-02 8.5622882e-02 7.9922508e-02 5.2526388e-02 7.1863670e-02 6.6948234e-02 6.3994975e-02 1.0763490e-01 8.8479248e-02 6.9931400e-02 7.1440370e-02 8.8224815e-02 6.7118281e-02 7.8968665e-02 8.8858891e-02 7.7432758e-02 7.0109240e-02 6.2023845e-02 7.8396402e-02 6.7393801e-02 7.1380489e-02 6.7813026e-02 4.6767795e-02 7.0645561e-02 1.3044475e-01 1.1734304e-01 1.1197394e-01 1.1577499e-01 1.2198760e-01 1.2346289e-01 1.1982320e-01 1.1899008e-01 1.2683842e-01 1.0736476e-01 9.1564623e-02 1.1164167e-01 1.0538958e-01 1.2475783e-01 1.2551509e-01 1.0524662e-01 1.0574315e-01 1.0607279e-01 1.4459461e-01 1.1962188e-01 1.0766800e-01 1.1407280e-01 1.2909426e-01 9.8905414e-02 1.0524346e-01 1.0359925e-01 9.4433579e-02 9.3820759e-02 1.2176744e-01 1.0065671e-01 1.1574436e-01 9.2625059e-02 1.2364363e-01 9.7538593e-02 1.2367543e-01 1.1121391e-01 1.1355049e-01 1.0493323e-01 9.2419908e-02 9.8167154e-02 1.1298864e-01 9.3668541e-02 1.1734304e-01 1.1533257e-01 1.1267510e-01 1.0222063e-01 1.1031800e-01 9.9779829e-02 1.0752614e-01 1.0448251e-01 3.8330702e-04 7.6710204e-04 5.4934344e-04 6.1141025e-04 1.8880070e-03 4.3782366e-03 4.2558302e-03 3.3445116e-03 9.0730658e-04 1.6460272e-04 1.9935351e-03 2.2277110e-04 1.5935452e-03 7.2001884e-03 1.0201171e-03 1.9163397e-03 8.7300929e-04 4.6754224e-04 3.6671499e-04 7.4258415e-04 2.1567602e-04 1.3361003e-04 9.1168360e-04 4.3156597e-03 4.1158943e-03 3.8330702e-04 1.9019978e-03 2.1146706e-03 3.8330702e-04 3.1982857e-04 2.1854146e-04 1.6719903e-03 5.9155088e-03 1.3110961e-03 2.0595508e-03 2.2774590e-03 5.2912957e-04 1.6598142e-03 4.0619000e-04 8.5702191e-04 4.6128261e-04 5.7335316e-02 5.9791552e-02 6.7034247e-02 7.7315388e-02 7.0912461e-02 7.6541197e-02 6.6231857e-02 5.3290914e-02 6.4524455e-02 6.9096848e-02 7.2330829e-02 6.2164647e-02 7.0266106e-02 7.5359485e-02 4.7211115e-02 5.4717217e-02 7.6724195e-02 6.0437574e-02 9.0217835e-02 6.3227153e-02 8.1624838e-02 5.5479636e-02 9.1272977e-02 7.5343817e-02 5.8299412e-02 5.7952393e-02 7.1727180e-02 7.7451339e-02 7.2165967e-02 4.5880845e-02 6.4164650e-02 5.9464600e-02 5.6817377e-02 9.8638775e-02 8.0838937e-02 6.3146715e-02 6.3916551e-02 7.9536591e-02 6.0201366e-02 7.1084400e-02 8.0631146e-02 6.9793274e-02 6.2554472e-02 5.4911579e-02 7.0667171e-02 6.0374722e-02 6.4102637e-02 6.0460719e-02 4.0707229e-02 6.3307106e-02 1.2135312e-01 1.0820155e-01 1.0273439e-01 1.0658023e-01 1.1268914e-01 1.1365695e-01 1.1088857e-01 1.0931070e-01 1.1680946e-01 9.8829208e-02 8.3503653e-02 1.0241354e-01 9.6518124e-02 1.1527856e-01 1.1644011e-01 9.6835948e-02 9.6892604e-02 9.7403729e-02 1.3389056e-01 1.0978147e-01 9.8889679e-02 1.0531384e-01 1.1893855e-01 9.0170315e-02 9.6654705e-02 9.4696682e-02 8.5999457e-02 8.5628213e-02 1.1232778e-01 9.1692367e-02 1.0609783e-01 8.4336563e-02 1.1417833e-01 8.8845800e-02 1.1399156e-01 1.0186496e-01 1.0510756e-01 9.6245658e-02 8.4341961e-02 8.9608741e-02 1.0408154e-01 8.5412047e-02 1.0820155e-01 1.0631639e-01 1.0398240e-01 9.3623884e-02 1.0104040e-01 9.1224725e-02 9.9332091e-02 9.5993921e-02 1.1067957e-03 1.4791390e-03 7.1256747e-05 2.0377231e-03 4.3755431e-03 5.9630791e-03 4.4970379e-03 1.5921641e-03 6.4984761e-04 3.3935862e-03 1.2039709e-04 3.0970780e-03 8.3950153e-03 2.1890332e-03 3.1326528e-03 5.0256002e-04 1.6389584e-03 6.4717383e-04 7.1019942e-04 8.9864077e-04 3.8255378e-04 1.2286350e-03 5.5229901e-03 5.1766813e-03 0.0000000e+00 1.5860612e-03 1.6773969e-03 0.0000000e+00 8.4337656e-04 4.6746407e-04 2.4549978e-03 4.7529836e-03 2.3235808e-03 4.0683267e-03 4.3260986e-03 6.7336618e-04 2.8454658e-03 1.0918918e-03 1.3756658e-03 5.7784546e-04 5.9573290e-02 6.3070670e-02 6.9597309e-02 7.9911457e-02 7.3480528e-02 7.9923883e-02 7.0144874e-02 5.5923876e-02 6.6635620e-02 7.3192589e-02 7.4096565e-02 6.5836734e-02 7.1022277e-02 7.8555696e-02 5.0423423e-02 5.7089619e-02 8.1093473e-02 6.2483167e-02 9.2251714e-02 6.5399221e-02 8.6573432e-02 5.7873871e-02 9.3861710e-02 7.7914479e-02 6.0545459e-02 6.0328179e-02 7.3725736e-02 8.0652769e-02 7.5662466e-02 4.7500835e-02 6.6267157e-02 6.1219907e-02 5.9246920e-02 1.0235525e-01 8.5584812e-02 6.7627185e-02 6.6676399e-02 8.1028522e-02 6.3874534e-02 7.4037098e-02 8.3735875e-02 7.3091049e-02 6.4875922e-02 5.7134332e-02 7.3898502e-02 6.3669341e-02 6.7483654e-02 6.3032151e-02 4.3195391e-02 6.6465430e-02 1.2757303e-01 1.1294171e-01 1.0654531e-01 1.1074736e-01 1.1756538e-01 1.1705185e-01 1.1633100e-01 1.1230305e-01 1.1988948e-01 1.0404710e-01 8.7981667e-02 1.0622547e-01 1.0061669e-01 1.2008497e-01 1.2242153e-01 1.0217012e-01 1.0084187e-01 1.0181343e-01 1.3714147e-01 1.1243585e-01 1.0356517e-01 1.1071692e-01 1.2181923e-01 9.3742899e-02 1.0137566e-01 9.8085445e-02 8.9840814e-02 8.9979544e-02 1.1682155e-01 9.4340727e-02 1.0893096e-01 8.8036209e-02 1.1887723e-01 9.1995894e-02 1.1718099e-01 1.0529554e-01 1.1115622e-01 1.0048991e-01 8.8823715e-02 9.3647258e-02 1.0909883e-01 8.9729548e-02 1.1294171e-01 1.1121254e-01 1.0947844e-01 9.8164553e-02 1.0458423e-01 9.5468337e-02 1.0529433e-01 1.0077315e-01 9.3075268e-04 1.0661545e-03 2.6597529e-04 1.6036733e-03 2.0280056e-03 1.2436972e-03 1.5688801e-04 3.1850165e-04 8.9411637e-04 1.3235015e-03 8.8731482e-04 3.4816593e-03 2.6719247e-03 3.9091992e-03 2.6159373e-03 1.1443019e-03 7.9601608e-05 2.3028989e-04 1.2135551e-03 1.4956336e-03 1.2137749e-03 2.2449952e-03 1.5932074e-03 1.1067957e-03 8.0827056e-04 9.5525701e-04 1.1067957e-03 1.2520454e-04 1.8684214e-04 3.3283736e-04 8.4659292e-03 4.3428627e-04 2.6431662e-03 3.3043825e-03 1.2192723e-03 9.6672170e-04 2.2673224e-04 4.0165289e-05 1.5556464e-04 7.0885985e-02 7.3312749e-02 8.1555170e-02 9.2789394e-02 8.5768022e-02 9.1811327e-02 8.0210428e-02 6.6299983e-02 7.8898236e-02 8.3277528e-02 8.7497229e-02 7.5768419e-02 8.5268176e-02 9.0575828e-02 5.9182538e-02 6.7899308e-02 9.1577973e-02 7.4436351e-02 1.0683101e-01 7.7459472e-02 9.6638219e-02 6.8724317e-02 1.0805306e-01 9.0746123e-02 7.1944393e-02 7.1498544e-02 8.6811878e-02 9.2796163e-02 8.6929388e-02 5.8156140e-02 7.8485798e-02 7.3355880e-02 7.0259533e-02 1.1577796e-01 9.5890635e-02 7.6480264e-02 7.8047377e-02 9.5335392e-02 7.3634293e-02 8.5899063e-02 9.6384778e-02 8.4415996e-02 7.6657094e-02 6.8177250e-02 8.5395861e-02 7.3990972e-02 7.8093423e-02 7.4294151e-02 5.1950910e-02 7.7279181e-02 1.3907744e-01 1.2568112e-01 1.2011323e-01 1.2420710e-01 1.3046004e-01 1.3207399e-01 1.2824918e-01 1.2752552e-01 1.3553911e-01 1.1524115e-01 9.8893820e-02 1.1975932e-01 1.1323008e-01 1.3322963e-01 1.3377253e-01 1.1295596e-01 1.1379378e-01 1.1416138e-01 1.5374990e-01 1.2806482e-01 1.1555054e-01 1.2219358e-01 1.3787989e-01 1.0651347e-01 1.1318026e-01 1.1161431e-01 1.0188137e-01 1.0132199e-01 1.3022140e-01 1.0855236e-01 1.2404638e-01 1.0022528e-01 1.3210134e-01 1.0532767e-01 1.3250558e-01 1.1917979e-01 1.2157791e-01 1.1297631e-01 9.9847302e-02 1.0571550e-01 1.2097128e-01 1.0080768e-01 1.2568112e-01 1.2354605e-01 1.2062969e-01 1.0973133e-01 1.1825900e-01 1.0742526e-01 1.1535080e-01 1.1244756e-01 1.9470856e-03 1.8498175e-03 4.7714250e-03 2.8358661e-03 3.0255426e-03 1.1308587e-03 6.7035566e-04 9.3284570e-04 1.3935241e-03 9.8369983e-04 5.6854836e-03 1.9144361e-03 1.0961099e-03 2.6770659e-03 6.7637792e-04 7.3922961e-04 1.6168588e-03 1.9795771e-04 8.8027763e-04 2.3819907e-03 2.3199642e-03 2.7913184e-03 1.4791390e-03 3.2257382e-03 3.5250868e-03 1.4791390e-03 4.9791374e-04 7.0216560e-04 1.6800207e-03 1.0022835e-02 5.5855445e-04 1.9786373e-03 9.4684044e-04 1.9956071e-03 4.5593799e-04 2.5049818e-04 7.2992180e-04 1.1563910e-03 6.0779252e-02 6.2273308e-02 7.0462169e-02 8.1326510e-02 7.4767830e-02 7.8734546e-02 6.8077240e-02 5.6059538e-02 6.8181020e-02 7.1050105e-02 7.6799016e-02 6.4482663e-02 7.5580358e-02 7.7962233e-02 4.9642606e-02 5.8153068e-02 7.8105114e-02 6.3436311e-02 9.5564553e-02 6.6739850e-02 8.2930379e-02 5.9008492e-02 9.5418848e-02 7.8187143e-02 6.1832470e-02 6.1508833e-02 7.5927040e-02 8.0793818e-02 7.4771898e-02 4.9618073e-02 6.7927507e-02 6.3289271e-02 6.0099335e-02 1.0140473e-01 8.1745663e-02 6.4187383e-02 6.7135505e-02 8.4768567e-02 6.1827019e-02 7.4354928e-02 8.3103529e-02 7.2159743e-02 6.6094709e-02 5.8361788e-02 7.3251937e-02 6.2103535e-02 6.6220430e-02 6.3584868e-02 4.3971154e-02 6.5863068e-02 1.2260074e-01 1.1066837e-01 1.0619606e-01 1.0899833e-01 1.1518894e-01 1.1739848e-01 1.1240635e-01 1.1296742e-01 1.2096568e-01 1.0086214e-01 8.5896751e-02 1.0590639e-01 9.9771313e-02 1.1830710e-01 1.1878365e-01 9.8989344e-02 9.9484141e-02 9.9300506e-02 1.3860781e-01 1.1421784e-01 1.0167360e-01 1.0723785e-01 1.2323222e-01 9.3791376e-02 9.8729091e-02 9.7617417e-02 8.9196630e-02 8.7906597e-02 1.1533851e-01 9.5241683e-02 1.1037299e-01 8.6684313e-02 1.1722334e-01 9.1866320e-02 1.1676032e-01 1.0620321e-01 1.0631345e-01 9.8352717e-02 8.6492752e-02 9.2837846e-02 1.0689111e-01 8.8947496e-02 1.1066837e-01 1.0877202e-01 1.0619549e-01 9.7005210e-02 1.0523294e-01 9.4129616e-02 1.0043708e-01 9.7689504e-02 1.8508011e-03 3.7513322e-03 6.0039368e-03 4.2304138e-03 1.5191600e-03 7.2789043e-04 3.6236504e-03 2.5132214e-04 3.2740913e-03 8.1034702e-03 2.4941139e-03 4.0964229e-03 6.0206143e-04 1.9190323e-03 6.6472571e-04 5.1664338e-04 1.3616103e-03 7.0613265e-04 1.0312088e-03 5.8211090e-03 5.1401914e-03 7.1256747e-05 1.1045080e-03 1.1556192e-03 7.1256747e-05 9.3818356e-04 5.1597856e-04 2.2957469e-03 4.3308939e-03 2.5276111e-03 4.3800580e-03 5.1684770e-03 5.8668191e-04 3.2395561e-03 1.2942225e-03 1.4104695e-03 4.9075437e-04 6.2060492e-02 6.5820549e-02 7.2324527e-02 8.2688153e-02 7.6151035e-02 8.3216525e-02 7.3194172e-02 5.8477367e-02 6.9270609e-02 7.6249005e-02 7.6681852e-02 6.8643243e-02 7.3331578e-02 8.1706941e-02 5.2792718e-02 5.9477668e-02 8.4501232e-02 6.5244243e-02 9.4903309e-02 6.8042766e-02 9.0018791e-02 6.0245936e-02 9.6930604e-02 8.1064051e-02 6.3026851e-02 6.2769846e-02 7.6368221e-02 8.3589775e-02 7.8680956e-02 4.9590571e-02 6.8853459e-02 6.3691512e-02 6.1750809e-02 1.0592213e-01 8.9183264e-02 7.0750492e-02 6.9363027e-02 8.3535040e-02 6.6868103e-02 7.6873580e-02 8.7073732e-02 7.6162997e-02 6.7469442e-02 5.9546534e-02 7.6928171e-02 6.6694450e-02 7.0471384e-02 6.5682900e-02 4.5133798e-02 6.9312951e-02 1.3167296e-01 1.1664541e-01 1.0993682e-01 1.1453374e-01 1.2132631e-01 1.2063781e-01 1.2028602e-01 1.1588513e-01 1.2343288e-01 1.0759677e-01 9.1184353e-02 1.0959799e-01 1.0389171e-01 1.2371908e-01 1.2606621e-01 1.0559841e-01 1.0439417e-01 1.0553794e-01 1.4077951e-01 1.1579171e-01 1.0695513e-01 1.1441431e-01 1.2538100e-01 9.6825962e-02 1.0496558e-01 1.0155971e-01 9.2933277e-02 9.3305204e-02 1.2046243e-01 9.7626604e-02 1.1224568e-01 9.1421248e-02 1.2250398e-01 9.5336276e-02 1.2112681e-01 1.0839632e-01 1.1495562e-01 1.0414545e-01 9.2136906e-02 9.6777242e-02 1.1252211e-01 9.2559606e-02 1.1664541e-01 1.1484781e-01 1.1301462e-01 1.0121871e-01 1.0770332e-01 9.8720694e-02 1.0901533e-01 1.0446815e-01 7.8277898e-04 1.7490530e-03 1.0345024e-03 5.6185312e-04 1.1591486e-03 1.1405764e-03 2.5549089e-03 1.4484284e-03 2.2580494e-03 4.5713265e-03 5.6870335e-03 4.1902203e-03 2.4320876e-03 6.0369458e-04 6.2286369e-04 2.4521502e-03 2.9038905e-03 2.2436415e-03 1.7675525e-03 9.5896000e-04 2.0377231e-03 8.9090360e-04 9.7827632e-04 2.0377231e-03 7.4516940e-04 8.4824201e-04 4.3724648e-04 1.0582513e-02 7.1366344e-04 4.1221085e-03 4.7945036e-03 2.3833891e-03 1.3170043e-03 8.5049004e-04 2.9093352e-04 6.7142903e-04 7.9558936e-02 8.2158081e-02 9.0820201e-02 1.0264403e-01 9.5277746e-02 1.0150997e-01 8.9387659e-02 7.4718776e-02 8.7974881e-02 9.2637642e-02 9.6993873e-02 8.4761019e-02 9.4485044e-02 1.0025701e-01 6.7224195e-02 7.6433848e-02 1.0126400e-01 8.3185627e-02 1.1729605e-01 8.6461029e-02 1.0658954e-01 7.7314215e-02 1.1857784e-01 1.0034666e-01 8.0683042e-02 8.0237049e-02 9.6300382e-02 1.0267579e-01 9.6489769e-02 6.6032956e-02 8.7552344e-02 8.2099586e-02 7.8915667e-02 1.2662250e-01 1.0571935e-01 8.5387558e-02 8.7148938e-02 1.0518820e-01 8.2417845e-02 9.5416475e-02 1.0627769e-01 9.3794810e-02 8.5650752e-02 7.6704239e-02 9.4843643e-02 8.2753514e-02 8.7142105e-02 8.3165683e-02 5.9528971e-02 8.6320416e-02 1.5080041e-01 1.3699340e-01 1.3123407e-01 1.3538631e-01 1.4196949e-01 1.4361623e-01 1.3957348e-01 1.3881335e-01 1.4720194e-01 1.2611020e-01 1.0906565e-01 1.3086970e-01 1.2408176e-01 1.4489915e-01 1.4541581e-01 1.2374159e-01 1.2456922e-01 1.2489938e-01 1.6613145e-01 1.3940980e-01 1.2649129e-01 1.3333884e-01 1.4960338e-01 1.1706680e-01 1.2394226e-01 1.2226369e-01 1.1222236e-01 1.1158045e-01 1.4175176e-01 1.1903032e-01 1.3525827e-01 1.1036833e-01 1.4372294e-01 1.1569638e-01 1.4384978e-01 1.3029466e-01 1.3261278e-01 1.2368173e-01 1.1003418e-01 1.1624174e-01 1.3214763e-01 1.1113124e-01 1.3699340e-01 1.3478604e-01 1.3174332e-01 1.2045775e-01 1.2933910e-01 1.1801062e-01 1.2611372e-01 1.2312584e-01 2.4038804e-03 9.9356679e-04 1.6379146e-03 2.9968879e-03 2.7777132e-03 5.0292552e-03 2.9485139e-03 1.7810659e-03 7.1187929e-03 1.0385224e-02 6.8192179e-03 4.7007047e-03 2.2536066e-03 1.6978265e-03 5.5995030e-03 5.8830752e-03 3.3086218e-03 3.5101479e-03 1.6089784e-03 4.3755431e-03 1.0574938e-03 1.0592785e-03 4.3755431e-03 2.5472554e-03 2.6641717e-03 1.0704333e-03 1.2070572e-02 2.4953874e-03 6.0785955e-03 8.5181088e-03 3.9627930e-03 3.6619699e-03 2.9233174e-03 1.7757672e-03 2.0595965e-03 9.0904959e-02 9.3846404e-02 1.0296173e-01 1.1510418e-01 1.0729700e-01 1.1521099e-01 1.0181822e-01 8.5988795e-02 1.0000742e-01 1.0504161e-01 1.0917671e-01 9.6459196e-02 1.0622808e-01 1.1358449e-01 7.7434068e-02 8.7329703e-02 1.1478646e-01 9.5582611e-02 1.2982843e-01 9.8465917e-02 1.1998512e-01 8.8160180e-02 1.3221652e-01 1.1399918e-01 9.2024225e-02 9.1371105e-02 1.0854159e-01 1.1533865e-01 1.0916600e-01 7.6158175e-02 9.9423258e-02 9.3687045e-02 9.0204262e-02 1.4138542e-01 1.1970815e-01 9.7663366e-02 9.8994798e-02 1.1736337e-01 9.4681982e-02 1.0777767e-01 1.2034441e-01 1.0671033e-01 9.7400706e-02 8.7763928e-02 1.0767516e-01 9.5332473e-02 9.9630913e-02 9.4930517e-02 6.8563663e-02 9.8462875e-02 1.6617515e-01 1.5176730e-01 1.4543395e-01 1.5072522e-01 1.5691405e-01 1.5882414e-01 1.5479314e-01 1.5416358e-01 1.6247914e-01 1.3996762e-01 1.2197918e-01 1.4500225e-01 1.3765005e-01 1.5950314e-01 1.5937612e-01 1.3710080e-01 1.3913130e-01 1.3976637e-01 1.8183098e-01 1.5420823e-01 1.4014252e-01 1.4766552e-01 1.6507182e-01 1.3020370e-01 1.3818766e-01 1.3684571e-01 1.2517707e-01 1.2500429e-01 1.5651711e-01 1.3335007e-01 1.4978001e-01 1.2432862e-01 1.5834992e-01 1.2988375e-01 1.6032290e-01 1.4373178e-01 1.4688056e-01 1.3840222e-01 1.2332107e-01 1.2926358e-01 1.4578293e-01 1.2292671e-01 1.5176730e-01 1.4922075e-01 1.4546636e-01 1.3299550e-01 1.4276363e-01 1.3134387e-01 1.4008961e-01 1.3766630e-01 5.7728358e-04 1.6620505e-03 3.0662753e-03 5.1693417e-04 6.0968463e-03 8.4744633e-04 8.7364721e-04 5.8106642e-03 6.7399476e-03 8.6083103e-03 3.1702748e-03 2.7104978e-03 3.3164143e-03 4.2509190e-03 5.8084215e-03 4.6709776e-03 9.8526568e-04 3.6786909e-04 5.9630791e-03 3.9566796e-03 4.2657824e-03 5.9630791e-03 2.3876668e-03 3.1383576e-03 1.0693622e-03 1.7187016e-02 9.8571152e-04 3.1367899e-03 3.7448988e-03 5.3108404e-03 1.2637202e-03 2.1359423e-03 1.6418787e-03 3.1352541e-03 8.3834616e-02 8.4243676e-02 9.4832859e-02 1.0703873e-01 9.9466934e-02 1.0403376e-01 9.0316788e-02 7.7914871e-02 9.2850027e-02 9.3299365e-02 1.0296391e-01 8.6082003e-02 1.0248600e-01 1.0318273e-01 6.8824902e-02 8.0299100e-02 1.0157055e-01 8.7955506e-02 1.2341155e-01 9.1142100e-02 1.0580332e-01 8.1173197e-02 1.2350931e-01 1.0460831e-01 8.4991511e-02 8.4254332e-02 1.0174542e-01 1.0573929e-01 9.8648070e-02 7.1061471e-02 9.2438015e-02 8.7508516e-02 8.2750270e-02 1.2928539e-01 1.0529438e-01 8.4835976e-02 9.0594121e-02 1.1204138e-01 8.3577196e-02 9.8754999e-02 1.0957368e-01 9.6258171e-02 8.9990776e-02 8.0898906e-02 9.7508458e-02 8.4744426e-02 8.9161769e-02 8.6853262e-02 6.2377571e-02 8.8830405e-02 1.4853162e-01 1.3772455e-01 1.3389458e-01 1.3729951e-01 1.4254710e-01 1.4753124e-01 1.3874272e-01 1.4343376e-01 1.5191156e-01 1.2542851e-01 1.0950060e-01 1.3351960e-01 1.2589032e-01 1.4575308e-01 1.4361388e-01 1.2273259e-01 1.2665814e-01 1.2593062e-01 1.7100462e-01 1.4482556e-01 1.2707776e-01 1.3245591e-01 1.5480441e-01 1.1982038e-01 1.2429326e-01 1.2550864e-01 1.1421155e-01 1.1236837e-01 1.4320023e-01 1.2379599e-01 1.4017288e-01 1.1252737e-01 1.4478118e-01 1.1925773e-01 1.4807486e-01 1.3379535e-01 1.3019772e-01 1.2505772e-01 1.1047444e-01 1.1793234e-01 1.3214851e-01 1.1203286e-01 1.3772455e-01 1.3511133e-01 1.3062456e-01 1.2117415e-01 1.3256036e-01 1.1928977e-01 1.2368263e-01 1.2328316e-01 7.8697050e-04 2.0732289e-03 9.4315564e-04 4.6001401e-03 8.4240364e-04 1.3015708e-03 4.6297460e-03 7.5292997e-03 6.5572401e-03 2.5566943e-03 1.7941741e-03 1.8226799e-03 3.9920133e-03 4.8070278e-03 2.6490734e-03 2.2737423e-03 8.3198965e-04 4.4970379e-03 1.8707122e-03 2.0801746e-03 4.4970379e-03 1.6864362e-03 2.1518496e-03 3.0908897e-04 1.2802000e-02 1.1444166e-03 2.7605116e-03 4.8428825e-03 3.3840997e-03 1.9469936e-03 1.7958172e-03 1.1565833e-03 1.8697862e-03 8.2127567e-02 8.3518119e-02 9.3314949e-02 1.0506408e-01 9.7541782e-02 1.0397584e-01 9.0341739e-02 7.6817337e-02 9.1083710e-02 9.3231274e-02 1.0048418e-01 8.5524503e-02 9.9112893e-02 1.0267345e-01 6.7846626e-02 7.8515797e-02 1.0225403e-01 8.6849217e-02 1.2022955e-01 8.9502113e-02 1.0655817e-01 7.9297559e-02 1.2165144e-01 1.0391861e-01 8.3203942e-02 8.2410584e-02 9.9528824e-02 1.0443382e-01 9.8019721e-02 6.8818975e-02 9.0543610e-02 8.5485551e-02 8.1186134e-02 1.2894588e-01 1.0651601e-01 8.5580315e-02 8.9213768e-02 1.0886353e-01 8.3754339e-02 9.7441118e-02 1.0932583e-01 9.5882843e-02 8.8282134e-02 7.9128322e-02 9.6917266e-02 8.4873719e-02 8.8927957e-02 8.5532654e-02 6.0384203e-02 8.8123284e-02 1.4980593e-01 1.3770759e-01 1.3282368e-01 1.3741111e-01 1.4254251e-01 1.4639384e-01 1.3970199e-01 1.4238069e-01 1.5040197e-01 1.2563911e-01 1.0916002e-01 1.3240724e-01 1.2489275e-01 1.4520334e-01 1.4360899e-01 1.2274102e-01 1.2644577e-01 1.2642535e-01 1.6908994e-01 1.4294672e-01 1.2654652e-01 1.3286867e-01 1.5320101e-01 1.1837980e-01 1.2451917e-01 1.2497748e-01 1.1312705e-01 1.1222677e-01 1.4268257e-01 1.2261254e-01 1.3839073e-01 1.1239899e-01 1.4421566e-01 1.1854547e-01 1.4805458e-01 1.3176737e-01 1.3127552e-01 1.2532607e-01 1.1042618e-01 1.1684289e-01 1.3160924e-01 1.1043724e-01 1.3770759e-01 1.3504379e-01 1.3066174e-01 1.1987723e-01 1.3066578e-01 1.1856367e-01 1.2478710e-01 1.2391087e-01 3.0983409e-04 7.5828890e-04 1.5917755e-03 4.8958816e-04 3.3744944e-03 2.1101097e-03 4.2400870e-03 2.8698866e-03 7.9583168e-04 2.4610899e-04 3.6436132e-04 1.4311801e-03 1.7294514e-03 8.6738167e-04 2.6111809e-03 1.6704106e-03 1.5921641e-03 8.6161593e-04 1.0547029e-03 1.5921641e-03 2.0427868e-04 3.5845705e-04 1.2194863e-04 8.2981219e-03 4.9180195e-04 1.7380522e-03 3.0734607e-03 1.0728608e-03 1.1397310e-03 3.4603128e-04 1.9200118e-04 2.8845040e-04 6.9779438e-02 7.1836392e-02 8.0319868e-02 9.1354890e-02 8.4353236e-02 9.0635736e-02 7.8599311e-02 6.5140986e-02 7.7899100e-02 8.1486701e-02 8.6472565e-02 7.4062042e-02 8.4619349e-02 8.9336326e-02 5.7575298e-02 6.6635212e-02 8.9946961e-02 7.3779929e-02 1.0532587e-01 7.6464940e-02 9.4587006e-02 6.7402630e-02 1.0673169e-01 8.9925800e-02 7.0797999e-02 7.0219100e-02 8.5721997e-02 9.1187854e-02 8.5377890e-02 5.7235173e-02 7.7431768e-02 7.2498793e-02 6.9063158e-02 1.1428266e-01 9.4218471e-02 7.4725647e-02 7.6703845e-02 9.4228329e-02 7.2261001e-02 8.4462132e-02 9.5360997e-02 8.3133678e-02 7.5506963e-02 6.7041127e-02 8.4070372e-02 7.2903149e-02 7.6784460e-02 7.3115143e-02 5.0413571e-02 7.5926129e-02 1.3636539e-01 1.2353657e-01 1.1821263e-01 1.2258303e-01 1.2822457e-01 1.3051322e-01 1.2599517e-01 1.2631512e-01 1.3406707e-01 1.1278003e-01 9.6722933e-02 1.1783731e-01 1.1110860e-01 1.3080317e-01 1.3063996e-01 1.1029647e-01 1.1216341e-01 1.1248813e-01 1.5199693e-01 1.2674108e-01 1.1318561e-01 1.1969790e-01 1.3653036e-01 1.0458853e-01 1.1112582e-01 1.1028100e-01 9.9890110e-02 9.9356661e-02 1.2805612e-01 1.0750038e-01 1.2261289e-01 9.8791075e-02 1.2975191e-01 1.0408196e-01 1.3162969e-01 1.1713290e-01 1.1886116e-01 1.1132823e-01 9.7814075e-02 1.0357451e-01 1.1833994e-01 9.8179977e-02 1.2353657e-01 1.2124412e-01 1.1787602e-01 1.0709455e-01 1.1618054e-01 1.0529428e-01 1.1269702e-01 1.1053049e-01 1.3650135e-03 5.3926014e-04 9.6875216e-04 5.5085642e-03 1.1951469e-03 2.8096772e-03 1.4033998e-03 3.8702395e-04 1.0970323e-04 3.3218009e-04 5.6326785e-04 5.8024795e-04 5.6723773e-04 3.5935032e-03 3.0059920e-03 6.4984761e-04 1.1677062e-03 1.3673782e-03 6.4984761e-04 7.6378345e-05 6.3488092e-05 8.1586688e-04 6.3954323e-03 8.4458294e-04 1.6959745e-03 2.5316364e-03 4.4648839e-04 1.3649198e-03 2.1646092e-04 4.0910219e-04 1.5323026e-04 6.2114649e-02 6.4461203e-02 7.2168083e-02 8.2712554e-02 7.6067484e-02 8.2127836e-02 7.1085856e-02 5.7869953e-02 6.9689694e-02 7.3941980e-02 7.7755077e-02 6.6772898e-02 7.5730146e-02 8.0858032e-02 5.1159438e-02 5.9263906e-02 8.1982206e-02 6.5662191e-02 9.5957099e-02 6.8346124e-02 8.6755825e-02 6.0017643e-02 9.7272303e-02 8.1103209e-02 6.3092478e-02 6.2637310e-02 7.7107544e-02 8.2765719e-02 7.7320565e-02 5.0179546e-02 6.9272103e-02 6.4477670e-02 6.1520691e-02 1.0482403e-01 8.6201836e-02 6.7723532e-02 6.8849001e-02 8.5128110e-02 6.4954612e-02 7.6260237e-02 8.6474261e-02 7.5037042e-02 6.7540885e-02 5.9554295e-02 7.5917230e-02 6.5334858e-02 6.9084098e-02 6.5352935e-02 4.4308417e-02 6.8222624e-02 1.2733314e-01 1.1424612e-01 1.0876932e-01 1.1294180e-01 1.1881213e-01 1.2027633e-01 1.1690957e-01 1.1601884e-01 1.2357066e-01 1.0430113e-01 8.8647048e-02 1.0842142e-01 1.0217893e-01 1.2134270e-01 1.2195833e-01 1.0207761e-01 1.0292571e-01 1.0341320e-01 1.4095409e-01 1.1639921e-01 1.0445121e-01 1.1097871e-01 1.2583780e-01 9.5736690e-02 1.0236688e-01 1.0085185e-01 9.1372067e-02 9.1009848e-02 1.1849408e-01 9.7905297e-02 1.1253291e-01 9.0050809e-02 1.2026601e-01 9.4848068e-02 1.2106318e-01 1.0772883e-01 1.1055160e-01 1.0223795e-01 8.9621067e-02 9.5003079e-02 1.0961118e-01 9.0242843e-02 1.1424612e-01 1.1217935e-01 1.0939970e-01 9.8767943e-02 1.0686010e-01 9.6691216e-02 1.0462140e-01 1.0177309e-01 3.4212913e-03 2.0350185e-04 2.2645835e-03 3.4346676e-03 3.6178579e-03 5.4115677e-03 1.4013939e-03 1.2010586e-03 1.9342083e-03 1.8303919e-03 3.0241355e-03 3.0182648e-03 8.7783530e-04 7.3200159e-04 3.3935862e-03 3.0016113e-03 3.3110105e-03 3.3935862e-03 9.0468402e-04 1.4291912e-03 6.5529771e-04 1.3482371e-02 1.1883350e-04 1.9129351e-03 1.8189821e-03 3.2224845e-03 2.2840556e-04 6.5009173e-04 5.8224397e-04 1.6275063e-03 7.3184911e-02 7.4026716e-02 8.3609924e-02 9.5240271e-02 8.8102740e-02 9.2390529e-02 7.9966102e-02 6.7767341e-02 8.1513618e-02 8.2938634e-02 9.0997666e-02 7.6011769e-02 9.0234918e-02 9.1578032e-02 5.9804069e-02 7.0034281e-02 9.0682677e-02 7.6686753e-02 1.1071727e-01 7.9918092e-02 9.5151478e-02 7.0899518e-02 1.1069472e-01 9.2509702e-02 7.4297007e-02 7.3732579e-02 8.9920224e-02 9.4250688e-02 8.7608739e-02 6.1121885e-02 8.1169501e-02 7.6375843e-02 7.2267735e-02 1.1652820e-01 9.4360734e-02 7.5150369e-02 7.9755441e-02 9.9609504e-02 7.3443983e-02 8.7507436e-02 9.7438140e-02 8.5130511e-02 7.8978434e-02 7.0471516e-02 8.6314807e-02 7.4241923e-02 7.8526940e-02 7.6102189e-02 5.3711374e-02 7.8190521e-02 1.3653802e-01 1.2528995e-01 1.2121146e-01 1.2435019e-01 1.2997976e-01 1.3382130e-01 1.2659694e-01 1.2959353e-01 1.3786369e-01 1.1404399e-01 9.8548323e-02 1.2087284e-01 1.1387348e-01 1.3314978e-01 1.3209550e-01 1.1169631e-01 1.1419600e-01 1.1368838e-01 1.5633317e-01 1.3093427e-01 1.1535002e-01 1.2078800e-01 1.4049523e-01 1.0785716e-01 1.1249675e-01 1.1275652e-01 1.0267406e-01 1.0105332e-01 1.3042827e-01 1.1078233e-01 1.2662105e-01 1.0064162e-01 1.3213313e-01 1.0672611e-01 1.3386868e-01 1.2116827e-01 1.1908244e-01 1.1278797e-01 9.9360911e-02 1.0635497e-01 1.2047378e-01 1.0130606e-01 1.2528995e-01 1.2297832e-01 1.1929072e-01 1.0997770e-01 1.2004306e-01 1.0767864e-01 1.1284273e-01 1.1147304e-01 2.8709378e-03 9.0791333e-03 1.3407674e-03 2.7138923e-03 2.4677711e-04 1.1444972e-03 7.6121265e-04 8.8810957e-04 7.0009018e-04 1.4757411e-04 9.0393445e-04 6.0883361e-03 5.6961877e-03 1.2039709e-04 1.8816976e-03 2.0265121e-03 1.2039709e-04 8.6239061e-04 5.2686780e-04 2.5538294e-03 4.1367540e-03 2.4461852e-03 3.1968429e-03 3.7345867e-03 3.8813913e-04 2.9749715e-03 1.1153685e-03 1.5845430e-03 6.9221070e-04 5.5493017e-02 5.8687008e-02 6.5182775e-02 7.5153394e-02 6.8882837e-02 7.5289957e-02 6.5516107e-02 5.1904720e-02 6.2434251e-02 6.8395322e-02 6.9725101e-02 6.1264247e-02 6.7000659e-02 7.3915045e-02 4.6337454e-02 5.2993228e-02 7.6203506e-02 5.8574285e-02 8.7235787e-02 6.1228310e-02 8.1349394e-02 5.3727422e-02 8.8860415e-02 7.3529860e-02 5.6419133e-02 5.6136434e-02 6.9314277e-02 7.5768587e-02 7.0921131e-02 4.3887190e-02 6.2047408e-02 5.7247874e-02 5.5122668e-02 9.7063471e-02 8.0589157e-02 6.3015465e-02 6.2273448e-02 7.6485434e-02 5.9534434e-02 6.9400424e-02 7.9100961e-02 6.8556756e-02 6.0632063e-02 5.3105900e-02 6.9320431e-02 5.9485747e-02 6.3075829e-02 5.8812039e-02 3.9389619e-02 6.2057390e-02 1.2120786e-01 1.0709958e-01 1.0095433e-01 1.0522389e-01 1.1158919e-01 1.1144992e-01 1.1038844e-01 1.0698853e-01 1.1429410e-01 9.8230586e-02 8.2643326e-02 1.0062987e-01 9.5029365e-02 1.1396256e-01 1.1592889e-01 9.6297509e-02 9.5506831e-02 9.6444702e-02 1.3110088e-01 1.0707131e-01 9.7795782e-02 1.0475251e-01 1.1626300e-01 8.8405248e-02 9.5813058e-02 9.2970070e-02 8.4549021e-02 8.4699662e-02 1.1089247e-01 8.9470273e-02 1.0356569e-01 8.3077190e-02 1.1281589e-01 8.7056416e-02 1.1197016e-01 9.9670877e-02 1.0510107e-01 9.5157512e-02 8.3537128e-02 8.8197538e-02 1.0308982e-01 8.4141549e-02 1.0709958e-01 1.0532428e-01 1.0341164e-01 9.2382015e-02 9.8953568e-02 8.9983057e-02 9.9390435e-02 9.5301345e-02 3.0771633e-03 2.2394953e-03 3.5785600e-03 4.5455517e-03 7.4728021e-04 1.0553655e-03 1.6471751e-03 1.6255623e-03 2.5337657e-03 2.0402330e-03 1.9207121e-03 1.4230439e-03 3.0970780e-03 2.6131629e-03 2.9423829e-03 3.0970780e-03 7.3620931e-04 1.2096052e-03 5.1210113e-04 1.1445573e-02 3.3743120e-04 9.3762253e-04 1.6709589e-03 2.3328362e-03 6.4367426e-04 6.1292579e-04 6.6980522e-04 1.3596425e-03 6.8771058e-02 6.9631829e-02 7.8939123e-02 9.0076585e-02 8.3106206e-02 8.8021023e-02 7.5587788e-02 6.3613086e-02 7.6977857e-02 7.8355410e-02 8.6058485e-02 7.1477483e-02 8.5350309e-02 8.7042060e-02 5.5585563e-02 6.5564323e-02 8.6285657e-02 7.2679829e-02 1.0489589e-01 7.5454083e-02 9.0429367e-02 6.6346194e-02 1.0535674e-01 8.8189227e-02 6.9809235e-02 6.9152322e-02 8.5027647e-02 8.9182145e-02 8.2908995e-02 5.6974544e-02 7.6567724e-02 7.1978204e-02 6.7853368e-02 1.1142320e-01 9.0045691e-02 7.1019587e-02 7.5131918e-02 9.4265212e-02 6.9407495e-02 8.2681448e-02 9.3017444e-02 8.0734547e-02 7.4408554e-02 6.6081064e-02 8.1800498e-02 7.0361312e-02 7.4293705e-02 7.1683996e-02 4.9412056e-02 7.3791248e-02 1.3087381e-01 1.1972349e-01 1.1554097e-01 1.1917243e-01 1.2428472e-01 1.2815053e-01 1.2126029e-01 1.2425112e-01 1.3208038e-01 1.0854852e-01 9.3334993e-02 1.1518188e-01 1.0821021e-01 1.2711496e-01 1.2583624e-01 1.0606084e-01 1.0908141e-01 1.0877992e-01 1.4997249e-01 1.2525272e-01 1.0965413e-01 1.1522136e-01 1.3472784e-01 1.0229252e-01 1.0728052e-01 1.0776484e-01 9.7267064e-02 9.5981774e-02 1.2460839e-01 1.0582154e-01 1.2096134e-01 9.5922459e-02 1.2615683e-01 1.0184377e-01 1.2900688e-01 1.1512576e-01 1.1363761e-01 1.0783825e-01 9.4308496e-02 1.0078579e-01 1.1452781e-01 9.5389301e-02 1.1972349e-01 1.1733679e-01 1.1347632e-01 1.0398208e-01 1.1403752e-01 1.0220157e-01 1.0755302e-01 1.0649118e-01 1.0147620e-02 1.1247381e-02 1.2064168e-02 6.5374061e-03 4.5897505e-03 4.8228518e-03 7.5563629e-03 9.2308338e-03 7.2321889e-03 1.6011806e-03 5.3396772e-04 8.3950153e-03 4.7739373e-03 4.9472694e-03 8.3950153e-03 4.4998786e-03 5.2439437e-03 2.2809748e-03 2.1071898e-02 2.7056396e-03 6.9610435e-03 7.8780829e-03 8.1126375e-03 3.2257880e-03 4.3393216e-03 3.1425928e-03 4.9174647e-03 1.0007131e-01 1.0106593e-01 1.1217080e-01 1.2532148e-01 1.1715442e-01 1.2248393e-01 1.0792073e-01 9.3860583e-02 1.0976919e-01 1.1122163e-01 1.2044721e-01 1.0322968e-01 1.1921347e-01 1.2149913e-01 8.4186074e-02 9.6291408e-02 1.2023049e-01 1.0442285e-01 1.4247402e-01 1.0795998e-01 1.2499239e-01 9.7246530e-02 1.4298267e-01 1.2269113e-01 1.0132833e-01 1.0059203e-01 1.1928692e-01 1.2425009e-01 1.1675840e-01 8.5744895e-02 1.0931412e-01 1.0376332e-01 9.9002718e-02 1.4971116e-01 1.2434000e-01 1.0215677e-01 1.0769100e-01 1.2998610e-01 1.0050022e-01 1.1660891e-01 1.2830083e-01 1.1408155e-01 1.0679553e-01 9.6866698e-02 1.1540492e-01 1.0158690e-01 1.0644304e-01 1.0354221e-01 7.6665035e-02 1.0598765e-01 1.7102185e-01 1.5912057e-01 1.5467066e-01 1.5843570e-01 1.6430112e-01 1.6898961e-01 1.6040927e-01 1.6442765e-01 1.7347908e-01 1.4613872e-01 1.2882261e-01 1.5426961e-01 1.4623773e-01 1.6767510e-01 1.6569930e-01 1.4326884e-01 1.4700928e-01 1.4639344e-01 1.9375995e-01 1.6572948e-01 1.4772209e-01 1.5370280e-01 1.7644069e-01 1.3951457e-01 1.4477511e-01 1.4552738e-01 1.3362833e-01 1.3186831e-01 1.6485780e-01 1.4332228e-01 1.6088049e-01 1.3176893e-01 1.6660706e-01 1.3873049e-01 1.6938110e-01 1.5434218e-01 1.5143663e-01 1.4540553e-01 1.2987912e-01 1.3768917e-01 1.5323248e-01 1.3136310e-01 1.5912057e-01 1.5638588e-01 1.5175254e-01 1.4128505e-01 1.5308268e-01 1.3923812e-01 1.4444485e-01 1.4370095e-01 2.6770864e-03 1.6033718e-03 4.5840441e-04 2.0276877e-03 2.4561050e-03 1.2787767e-03 1.0310968e-03 1.1138996e-03 7.3996134e-03 6.8122401e-03 2.1890332e-03 3.7576667e-03 4.1081994e-03 2.1890332e-03 1.7366871e-03 1.7496571e-03 3.0804223e-03 5.2624414e-03 3.0566387e-03 8.7139687e-04 2.3320271e-03 9.5854356e-04 3.5582665e-03 1.9033529e-03 2.7781156e-03 2.0582238e-03 4.8466175e-02 5.0119607e-02 5.7333122e-02 6.6635612e-02 6.0625611e-02 6.6546613e-02 5.6002103e-02 4.4610205e-02 5.5428894e-02 5.8355341e-02 6.2731565e-02 5.1936578e-02 6.1589720e-02 6.5254921e-02 3.8121898e-02 4.5707178e-02 6.5954438e-02 5.2310619e-02 7.8737277e-02 5.4221486e-02 6.9833666e-02 4.6306978e-02 8.0104533e-02 6.6061691e-02 4.9289042e-02 4.8703396e-02 6.2029842e-02 6.6459215e-02 6.1626890e-02 3.8139914e-02 5.4977441e-02 5.0955269e-02 4.7810568e-02 8.6879401e-02 6.9864176e-02 5.2960539e-02 5.4195866e-02 6.9397405e-02 5.0805294e-02 6.0771100e-02 7.0699543e-02 5.9928918e-02 5.3277849e-02 4.6137567e-02 6.0647902e-02 5.1507184e-02 5.4535520e-02 5.1271651e-02 3.2182295e-02 5.3653922e-02 1.0655841e-01 9.4809862e-02 8.9943103e-02 9.4257768e-02 9.8930460e-02 1.0102907e-01 9.7318001e-02 9.7573045e-02 1.0420091e-01 8.5339961e-02 7.1237738e-02 8.9593643e-02 8.3615246e-02 1.0099568e-01 1.0093948e-01 8.3072610e-02 8.4968711e-02 8.5464008e-02 1.2001752e-01 9.7755169e-02 8.5475001e-02 9.1482164e-02 1.0648093e-01 7.7912530e-02 8.4002788e-02 8.3421922e-02 7.3855173e-02 7.3645673e-02 9.8656580e-02 8.1073912e-02 9.4018658e-02 7.3420060e-02 1.0008227e-01 7.8012093e-02 1.0282145e-01 8.8787179e-02 9.1014600e-02 8.4377747e-02 7.2315249e-02 7.7005472e-02 8.9946277e-02 7.2132565e-02 9.4809862e-02 9.2717682e-02 8.9711332e-02 7.9921256e-02 8.7947099e-02 7.8589830e-02 8.5634568e-02 8.3685774e-02 3.6322977e-03 2.1046334e-03 3.2662345e-03 4.7576391e-03 8.8436254e-04 1.6169936e-03 5.0909036e-03 5.3937261e-03 6.9592372e-03 3.1326528e-03 7.3963134e-03 7.8155750e-03 3.1326528e-03 2.8162695e-03 2.9888005e-03 5.3841195e-03 1.1640832e-02 3.1058623e-03 3.5268449e-03 8.9518404e-04 4.1579453e-03 2.4418843e-03 2.3174283e-03 3.5804624e-03 3.9289766e-03 4.8779386e-02 4.9807262e-02 5.7295117e-02 6.7429274e-02 6.1528729e-02 6.3805672e-02 5.4672352e-02 4.4305273e-02 5.5263563e-02 5.7527840e-02 6.3439356e-02 5.1886311e-02 6.2844358e-02 6.3392021e-02 3.9105833e-02 4.6659036e-02 6.3315061e-02 5.0431450e-02 8.1151197e-02 5.3901750e-02 6.8036856e-02 4.7518043e-02 7.9948453e-02 6.3397617e-02 4.9788208e-02 4.9648688e-02 6.2516012e-02 6.6678586e-02 6.0874148e-02 3.9322135e-02 5.5164136e-02 5.1029588e-02 4.8159012e-02 8.4641068e-02 6.6410487e-02 5.1100232e-02 5.4352465e-02 7.1152366e-02 4.8870866e-02 6.0790663e-02 6.7705030e-02 5.8185400e-02 5.3489810e-02 4.6729049e-02 5.9304859e-02 4.8888831e-02 5.2875044e-02 5.1050585e-02 3.4854587e-02 5.2844524e-02 1.0451993e-01 9.3506986e-02 8.9732041e-02 9.1442484e-02 9.7717353e-02 9.9707944e-02 9.4802947e-02 9.5351404e-02 1.0312713e-01 8.4850971e-02 7.1294232e-02 8.9511382e-02 8.4075688e-02 1.0102482e-01 1.0205801e-01 8.3487981e-02 8.2948462e-02 8.2499658e-02 1.1982484e-01 9.7067919e-02 8.5815629e-02 9.0584285e-02 1.0517734e-01 7.8728615e-02 8.2465362e-02 8.1175179e-02 7.4451161e-02 7.2780961e-02 9.8027119e-02 7.9186352e-02 9.3572539e-02 7.1178913e-02 9.9960717e-02 7.6001190e-02 9.8069469e-02 9.0444802e-02 8.9768862e-02 8.1715688e-02 7.1540326e-02 7.7885383e-02 9.0855680e-02 7.5245252e-02 9.3506986e-02 9.1968167e-02 9.0109483e-02 8.2320804e-02 8.9509517e-02 7.8843854e-02 8.4366358e-02 8.1217849e-02 2.0130888e-03 1.8101212e-03 1.7679630e-03 1.5452657e-03 5.1945438e-04 1.2854163e-03 8.7808919e-03 8.2238558e-03 5.0256002e-04 2.7264330e-03 2.8467434e-03 5.0256002e-04 1.9812707e-03 1.4461308e-03 4.1136119e-03 2.5737383e-03 4.2121435e-03 4.2634118e-03 5.2197085e-03 6.4299253e-04 4.8925560e-03 2.3856210e-03 3.0302048e-03 1.5954763e-03 5.0938593e-02 5.4618269e-02 6.0341206e-02 6.9748949e-02 6.3744319e-02 7.0794949e-02 6.1613026e-02 4.7841585e-02 5.7516679e-02 6.4388046e-02 6.4176559e-02 5.7259869e-02 6.1078980e-02 6.9247074e-02 4.2780731e-02 4.8566082e-02 7.2247187e-02 5.4065395e-02 8.0844343e-02 5.6422768e-02 7.7401119e-02 4.9239970e-02 8.2976560e-02 6.8666014e-02 5.1795393e-02 5.1539865e-02 6.3922410e-02 7.0731007e-02 6.6410587e-02 3.9578438e-02 5.7098964e-02 5.2392525e-02 5.0683886e-02 9.1729236e-02 7.6796353e-02 5.9700691e-02 5.7658559e-02 7.0380963e-02 5.5891948e-02 6.4553084e-02 7.4314140e-02 6.4188376e-02 5.5862752e-02 4.8638731e-02 6.4818200e-02 5.5721441e-02 5.9023587e-02 5.4325379e-02 3.5659427e-02 5.7806321e-02 1.1646754e-01 1.0183406e-01 9.5237923e-02 9.9907095e-02 1.0622089e-01 1.0525522e-01 1.0561297e-01 1.0087833e-01 1.0779948e-01 9.3522115e-02 7.8070592e-02 9.4910280e-02 8.9633058e-02 1.0829781e-01 1.1081740e-01 9.1634033e-02 9.0337246e-02 9.1650208e-02 1.2399805e-01 1.0057671e-01 9.2649910e-02 9.9949877e-02 1.0962952e-01 8.2940269e-02 9.1028259e-02 8.7623472e-02 7.9432792e-02 8.0075100e-02 1.0524078e-01 8.3828651e-02 9.7259211e-02 7.8329945e-02 1.0714828e-01 8.1794698e-02 1.0616577e-01 9.3567438e-02 1.0077628e-01 9.0271060e-02 7.9035426e-02 8.3003648e-02 9.7873414e-02 7.9050596e-02 1.0183406e-01 1.0015100e-01 9.8558964e-02 8.7141498e-02 9.2951123e-02 8.4912476e-02 9.5252034e-02 9.0710349e-02 8.3242342e-04 1.3658963e-03 5.7230018e-04 8.1918836e-04 9.7205318e-04 4.1873206e-03 3.8010080e-03 1.6389584e-03 2.5918915e-03 2.9169588e-03 1.6389584e-03 5.6093257e-04 7.3131385e-04 1.3995850e-03 7.2853070e-03 1.1548608e-03 5.6661765e-04 1.3645766e-03 9.0462881e-04 1.5033145e-03 5.7640672e-04 1.1086000e-03 1.0041867e-03 5.6613254e-02 5.8073267e-02 6.6069758e-02 7.6238332e-02 6.9820331e-02 7.5150667e-02 6.4021588e-02 5.2221754e-02 6.4043055e-02 6.6631628e-02 7.2118099e-02 6.0001482e-02 7.1017077e-02 7.4033042e-02 4.5299125e-02 5.3744092e-02 7.4268794e-02 6.0257552e-02 8.9540407e-02 6.2698279e-02 7.8447791e-02 5.4448716e-02 9.0401373e-02 7.4795629e-02 5.7546133e-02 5.6993841e-02 7.1300530e-02 7.5820775e-02 7.0342915e-02 4.5615196e-02 6.3638314e-02 5.9296597e-02 5.5885275e-02 9.6910282e-02 7.8117499e-02 6.0390425e-02 6.2700176e-02 7.9484269e-02 5.8304382e-02 6.9718471e-02 7.9589842e-02 6.8316048e-02 6.1785991e-02 5.4151233e-02 6.9205713e-02 5.8981505e-02 6.2496988e-02 5.9482310e-02 3.9268283e-02 6.1808750e-02 1.1700097e-01 1.0528100e-01 1.0062122e-01 1.0448643e-01 1.0962576e-01 1.1218593e-01 1.0740486e-01 1.0838294e-01 1.1564442e-01 9.5242357e-02 8.0565058e-02 1.0027898e-01 9.3976493e-02 1.1211413e-01 1.1185843e-01 9.2981806e-02 9.4878465e-02 9.5039709e-02 1.3246555e-01 1.0898490e-01 9.5760022e-02 1.0161372e-01 1.1802422e-01 8.8109745e-02 9.3746381e-02 9.3301915e-02 8.3669649e-02 8.2970713e-02 1.0958444e-01 9.1015393e-02 1.0506496e-01 8.2570171e-02 1.1114727e-01 8.7646381e-02 1.1324005e-01 9.9872205e-02 1.0076379e-01 9.4009317e-02 8.1526386e-02 8.7041367e-02 1.0052094e-01 8.2193042e-02 1.0528100e-01 1.0314067e-01 9.9984788e-02 9.0308392e-02 9.8939123e-02 8.8538901e-02 9.5061969e-02 9.3157351e-02 1.7227462e-04 7.9314599e-04 8.9844173e-04 8.9518090e-04 2.8880348e-03 2.3244520e-03 6.4717383e-04 8.8327223e-04 1.0385020e-03 6.4717383e-04 4.2082433e-05 2.2535838e-05 6.1632160e-04 7.2421116e-03 6.3599645e-04 2.4204146e-03 3.0244507e-03 7.7555952e-04 1.1490838e-03 1.6721205e-04 1.6116507e-04 5.3985478e-05 6.6593275e-02 6.9140456e-02 7.6991415e-02 8.7909276e-02 8.1079796e-02 8.7140283e-02 7.5972851e-02 6.2229264e-02 7.4340995e-02 7.8983123e-02 8.2638003e-02 7.1602020e-02 8.0355120e-02 8.5886853e-02 5.5471847e-02 6.3724100e-02 8.7131187e-02 7.0026001e-02 1.0150375e-01 7.2956045e-02 9.2179245e-02 6.4526163e-02 1.0277569e-01 8.5954479e-02 6.7618774e-02 6.7207904e-02 8.2005017e-02 8.8025831e-02 8.2391127e-02 5.4193907e-02 7.3937560e-02 6.8913785e-02 6.6018815e-02 1.1053450e-01 9.1432865e-02 7.2512462e-02 7.3622571e-02 9.0228640e-02 6.9559505e-02 8.1277678e-02 9.1538587e-02 7.9923144e-02 7.2198625e-02 6.3968343e-02 8.0855092e-02 6.9835377e-02 7.3807917e-02 6.9953549e-02 4.8370205e-02 7.2961708e-02 1.3388298e-01 1.2040855e-01 1.1476271e-01 1.1886295e-01 1.2510613e-01 1.2637598e-01 1.2310531e-01 1.2187125e-01 1.2970708e-01 1.1033742e-01 9.4233672e-02 1.1441687e-01 1.0810566e-01 1.2778743e-01 1.2861773e-01 1.0813836e-01 1.0864380e-01 1.0911947e-01 1.4755823e-01 1.2232653e-01 1.1049966e-01 1.1716689e-01 1.3196625e-01 1.0144926e-01 1.0821364e-01 1.0641029e-01 9.6993665e-02 9.6571345e-02 1.2478057e-01 1.0328932e-01 1.1842664e-01 9.5377765e-02 1.2666075e-01 1.0023484e-01 1.2682498e-01 1.1377681e-01 1.1674990e-01 1.0792126e-01 9.5167233e-02 1.0076949e-01 1.1586910e-01 9.6070623e-02 1.2040855e-01 1.1835687e-01 1.1566030e-01 1.0480317e-01 1.1289922e-01 1.0248125e-01 1.1065853e-01 1.0752770e-01 1.5518070e-03 1.3360426e-03 6.1855325e-04 3.8684575e-03 2.7981409e-03 7.1019942e-04 2.9249692e-04 3.8108588e-04 7.1019942e-04 3.4811331e-04 2.0628507e-04 6.8481588e-04 6.1413327e-03 1.2640084e-03 3.0810810e-03 4.5162171e-03 6.1490495e-04 2.0823475e-03 6.6391763e-04 4.5940617e-04 4.0824218e-05 6.8980444e-02 7.2049409e-02 7.9659630e-02 9.0518795e-02 8.3601945e-02 9.0707158e-02 7.9362725e-02 6.4838475e-02 7.6843708e-02 8.2367370e-02 8.4912050e-02 7.4621958e-02 8.2116380e-02 8.9210086e-02 5.7968714e-02 6.6009408e-02 9.1025554e-02 7.2793579e-02 1.0368926e-01 7.5498978e-02 9.6154006e-02 6.6776572e-02 1.0567953e-01 8.9202124e-02 6.9982422e-02 6.9528749e-02 8.4404562e-02 9.0968518e-02 8.5580442e-02 5.6059636e-02 7.6365493e-02 7.1188605e-02 6.8464233e-02 1.1430428e-01 9.5645657e-02 7.6165252e-02 7.6295454e-02 9.2254316e-02 7.2920975e-02 8.4113452e-02 9.5088632e-02 8.3209827e-02 7.4687466e-02 6.6271074e-02 8.4049660e-02 7.3195324e-02 7.7054222e-02 7.2597553e-02 5.0198701e-02 7.5958466e-02 1.3864088e-01 1.2443023e-01 1.1820696e-01 1.2296103e-01 1.2918980e-01 1.2996108e-01 1.2761885e-01 1.2545185e-01 1.3315049e-01 1.1429516e-01 9.7708479e-02 1.1783417e-01 1.1147502e-01 1.3162480e-01 1.3265015e-01 1.1194384e-01 1.1244164e-01 1.1326233e-01 1.5100180e-01 1.2549100e-01 1.1411167e-01 1.2131599e-01 1.3539430e-01 1.0451333e-01 1.1218678e-01 1.1003412e-01 1.0016268e-01 1.0019858e-01 1.2861591e-01 1.0655136e-01 1.2158527e-01 9.9029274e-02 1.3048234e-01 1.0368193e-01 1.3098775e-01 1.1671318e-01 1.2117898e-01 1.1194010e-01 9.8811276e-02 1.0398171e-01 1.1952683e-01 9.8904418e-02 1.2443023e-01 1.2231211e-01 1.1957927e-01 1.0792296e-01 1.1588914e-01 1.0590028e-01 1.1501774e-01 1.1169315e-01 2.7458883e-04 1.9079543e-03 3.8013798e-03 4.1957403e-03 8.9864077e-04 3.1780627e-03 3.4575366e-03 8.9864077e-04 6.1086528e-04 6.3429136e-04 2.2515700e-03 7.8191545e-03 1.2413621e-03 2.0952535e-03 1.3093150e-03 1.3748953e-03 1.2388833e-03 4.8114625e-04 1.1404059e-03 1.0969768e-03 5.5584260e-02 5.7510469e-02 6.4993010e-02 7.5368447e-02 6.9057379e-02 7.3499750e-02 6.3437246e-02 5.1298347e-02 6.2635149e-02 6.6332421e-02 7.0700793e-02 5.9790543e-02 6.9153134e-02 7.2596138e-02 4.5395419e-02 5.3097576e-02 7.3376441e-02 5.8208889e-02 8.8752390e-02 6.1292160e-02 7.8242185e-02 5.3906344e-02 8.8989869e-02 7.2610553e-02 5.6579969e-02 5.6297092e-02 6.9966539e-02 7.5157531e-02 6.9589413e-02 4.4676337e-02 6.2364995e-02 5.7806901e-02 5.5013010e-02 9.5419957e-02 7.7143847e-02 6.0075763e-02 6.1882505e-02 7.8193894e-02 5.7405077e-02 6.8884889e-02 7.7590521e-02 6.7073564e-02 6.0698980e-02 5.3256477e-02 6.8055547e-02 5.7544133e-02 6.1428351e-02 5.8436070e-02 3.9618229e-02 6.0914936e-02 1.1719371e-01 1.0478727e-01 9.9928933e-02 1.0301520e-01 1.0921839e-01 1.1065431e-01 1.0694189e-01 1.0626748e-01 1.1395294e-01 9.5522682e-02 8.0692317e-02 9.9640584e-02 9.3821909e-02 1.1210560e-01 1.1313776e-01 9.3722341e-02 9.3659306e-02 9.3792773e-02 1.3107147e-01 1.0721381e-01 9.5955259e-02 1.0180084e-01 1.1608360e-01 8.7786080e-02 9.3279943e-02 9.1618132e-02 8.3505928e-02 8.2622852e-02 1.0912386e-01 8.8978076e-02 1.0355009e-01 8.1236966e-02 1.1101306e-01 8.5950464e-02 1.1026035e-01 9.9640402e-02 1.0131013e-01 9.2768985e-02 8.1325904e-02 8.7087186e-02 1.0113076e-01 8.3368795e-02 1.0478727e-01 1.0299507e-01 1.0075649e-01 9.1267488e-02 9.8760345e-02 8.8473047e-02 9.5602739e-02 9.2388034e-02 1.3192990e-03 5.6049942e-03 5.6260410e-03 3.8255378e-04 2.7098321e-03 2.9260165e-03 3.8255378e-04 8.5873923e-04 6.4551657e-04 2.7466704e-03 5.2436473e-03 2.1741200e-03 2.6488612e-03 2.5599967e-03 7.2546074e-04 2.4454411e-03 9.4817109e-04 1.6251073e-03 9.8969141e-04 5.2867682e-02 5.5519966e-02 6.2241614e-02 7.2187515e-02 6.6023504e-02 7.1524895e-02 6.1872329e-02 4.9090525e-02 5.9694897e-02 6.4712456e-02 6.7154795e-02 5.7947928e-02 6.4972194e-02 7.0357979e-02 4.3573654e-02 5.0441816e-02 7.2068579e-02 5.5680796e-02 8.4591184e-02 5.8459303e-02 7.7051055e-02 5.1193288e-02 8.5601942e-02 7.0117829e-02 5.3803216e-02 5.3535941e-02 6.6621467e-02 7.2473708e-02 6.7439009e-02 4.1816308e-02 5.9366143e-02 5.4760331e-02 5.2431175e-02 9.2978431e-02 7.6152091e-02 5.9131458e-02 5.9321453e-02 7.4096463e-02 5.5985281e-02 6.6256786e-02 7.5362720e-02 6.5046998e-02 5.7885295e-02 5.0561408e-02 6.5880549e-02 5.5988437e-02 5.9621488e-02 5.5930673e-02 3.7267604e-02 5.8818934e-02 1.1596549e-01 1.0264359e-01 9.7070235e-02 1.0079872e-01 1.0704759e-01 1.0746690e-01 1.0547300e-01 1.0308827e-01 1.1044375e-01 9.3809882e-02 7.8755194e-02 9.6766957e-02 9.1194118e-02 1.0959636e-01 1.1126853e-01 9.1984794e-02 9.1378676e-02 9.2003603e-02 1.2714849e-01 1.0351897e-01 9.3695356e-02 1.0013450e-01 1.1244271e-01 8.4898325e-02 9.1456450e-02 8.9057498e-02 8.0952577e-02 8.0704372e-02 1.0658329e-01 8.5941483e-02 1.0000975e-01 7.9159793e-02 1.0848060e-01 8.3337337e-02 1.0760265e-01 9.6215589e-02 1.0020263e-01 9.0833970e-02 7.9520366e-02 8.4523201e-02 9.8885306e-02 8.0736144e-02 1.0264359e-01 1.0090595e-01 9.8957167e-02 8.8690158e-02 9.5447639e-02 8.6121379e-02 9.4585258e-02 9.0802578e-02 6.3934178e-03 4.8940589e-03 1.2286350e-03 9.0839358e-04 1.0654961e-03 1.2286350e-03 9.6426336e-04 7.9049403e-04 1.4335758e-03 3.9777866e-03 2.4355051e-03 2.0107867e-03 4.6162602e-03 1.1770386e-04 3.4806567e-03 1.4352123e-03 1.5474906e-03 6.2303871e-04 6.0828103e-02 6.3671616e-02 7.0896871e-02 8.0864907e-02 7.4298413e-02 8.2081782e-02 7.0784097e-02 5.7058421e-02 6.8417644e-02 7.3405222e-02 7.5846906e-02 6.5924537e-02 7.3406010e-02 8.0389200e-02 5.0100593e-02 5.7793778e-02 8.2174514e-02 6.5233957e-02 9.3014438e-02 6.7186690e-02 8.6645560e-02 5.8420934e-02 9.5569964e-02 8.0832485e-02 6.1703014e-02 6.1092740e-02 7.5364208e-02 8.1326732e-02 7.6506202e-02 4.8648267e-02 6.7850123e-02 6.3129543e-02 6.0279722e-02 1.0421037e-01 8.6793624e-02 6.7911943e-02 6.7612814e-02 8.2552902e-02 6.4996479e-02 7.4977087e-02 8.6383221e-02 7.4662447e-02 6.6198278e-02 5.8199721e-02 7.5325266e-02 6.5572089e-02 6.8824430e-02 6.4314329e-02 4.2494903e-02 6.7537432e-02 1.2698890e-01 1.1333529e-01 1.0720337e-01 1.1257147e-01 1.1782339e-01 1.1890376e-01 1.1671052e-01 1.1501886e-01 1.2195484e-01 1.0334755e-01 8.7523621e-02 1.0680449e-01 1.0051071e-01 1.1973768e-01 1.2022270e-01 1.0080013e-01 1.0231108e-01 1.0334772e-01 1.3872306e-01 1.1462175e-01 1.0296153e-01 1.1014449e-01 1.2423991e-01 9.3883877e-02 1.0176675e-01 1.0022558e-01 8.9772340e-02 9.0213298e-02 1.1713918e-01 9.6981953e-02 1.1075521e-01 8.9707194e-02 1.1871449e-01 9.4162640e-02 1.2118172e-01 1.0525415e-01 1.1008916e-01 1.0200908e-01 8.8850235e-02 9.3264552e-02 1.0788029e-01 8.7700151e-02 1.1333529e-01 1.1110284e-01 1.0804665e-01 9.6438203e-02 1.0447205e-01 9.5256431e-02 1.0425068e-01 1.0162139e-01 4.7487225e-04 5.5229901e-03 5.0553045e-03 5.3192416e-03 5.5229901e-03 2.6440955e-03 3.2948907e-03 2.1817756e-03 1.9232390e-02 1.0060949e-03 5.2893888e-03 3.6565664e-03 6.4838776e-03 7.5722530e-04 2.1347736e-03 1.7405562e-03 3.5508478e-03 8.4906111e-02 8.5684875e-02 9.5941240e-02 1.0863973e-01 1.0110388e-01 1.0421406e-01 9.1634377e-02 7.8911086e-02 9.3575504e-02 9.5023267e-02 1.0394314e-01 8.7940160e-02 1.0307352e-01 1.0372095e-01 7.0927620e-02 8.1814584e-02 1.0248405e-01 8.7682138e-02 1.2553590e-01 9.1822634e-02 1.0758552e-01 8.2848043e-02 1.2459059e-01 1.0427303e-01 8.6172494e-02 8.5771372e-02 1.0276107e-01 1.0743739e-01 1.0000507e-01 7.2180344e-02 9.3352407e-02 8.8118179e-02 8.3972299e-02 1.2999219e-01 1.0601815e-01 8.6245777e-02 9.1942699e-02 1.1341937e-01 8.4389659e-02 1.0016617e-01 1.0942619e-01 9.6927434e-02 9.1068024e-02 8.2112078e-02 9.8354950e-02 8.4918144e-02 8.9929197e-02 8.7860129e-02 6.4913483e-02 8.9916257e-02 1.5108513e-01 1.3966025e-01 1.3579363e-01 1.3800221e-01 1.4462965e-01 1.4853849e-01 1.4049017e-01 1.4366186e-01 1.5284247e-01 1.2813521e-01 1.1198929e-01 1.3549048e-01 1.2835377e-01 1.4847864e-01 1.4775011e-01 1.2601872e-01 1.2764409e-01 1.2670935e-01 1.7255531e-01 1.4567287e-01 1.2987249e-01 1.3506836e-01 1.5547286e-01 1.2211481e-01 1.2607603e-01 1.2598262e-01 1.1656479e-01 1.1426125e-01 1.4534374e-01 1.2395854e-01 1.4127350e-01 1.1320894e-01 1.4734853e-01 1.1969377e-01 1.4703899e-01 1.3646872e-01 1.3305975e-01 1.2588961e-01 1.1250545e-01 1.2058130e-01 1.3549735e-01 1.1610735e-01 1.3966025e-01 1.3745925e-01 1.3401818e-01 1.2501338e-01 1.3525796e-01 1.2173379e-01 1.2646805e-01 1.2458940e-01 5.1766813e-03 3.3038909e-03 3.5089109e-03 5.1766813e-03 2.1988415e-03 2.7782772e-03 1.0688451e-03 1.7030893e-02 8.9726872e-04 4.6231995e-03 4.6736672e-03 5.3382818e-03 1.1647725e-03 1.9758755e-03 1.2750935e-03 2.7128598e-03 8.7833448e-02 8.9010914e-02 9.9253982e-02 1.1183035e-01 1.0412107e-01 1.0885582e-01 9.5547284e-02 8.2028755e-02 9.6837094e-02 9.8811318e-02 1.0697473e-01 9.1242752e-02 1.0570232e-01 1.0798221e-01 7.3423587e-02 8.4439842e-02 1.0715501e-01 9.1521191e-02 1.2824419e-01 9.5124610e-02 1.1205287e-01 8.5389026e-02 1.2841576e-01 1.0878552e-01 8.9048303e-02 8.8474213e-02 1.0589810e-01 1.1091852e-01 1.0379001e-01 7.4413565e-02 9.6465402e-02 9.1132676e-02 8.6893608e-02 1.3487393e-01 1.1110410e-01 9.0320856e-02 9.5130800e-02 1.1613190e-01 8.8387927e-02 1.0357744e-01 1.1422195e-01 1.0103671e-01 9.4160592e-02 8.4870839e-02 1.0232030e-01 8.9160088e-02 9.3889889e-02 9.1104413e-02 6.6493231e-02 9.3508454e-02 1.5639991e-01 1.4440871e-01 1.3995325e-01 1.4327783e-01 1.4942424e-01 1.5327636e-01 1.4579380e-01 1.4864466e-01 1.5749945e-01 1.3242397e-01 1.1574188e-01 1.3959357e-01 1.3216131e-01 1.5281767e-01 1.5172347e-01 1.2991524e-01 1.3242882e-01 1.3190251e-01 1.7712896e-01 1.5002220e-01 1.3380600e-01 1.3964037e-01 1.6023126e-01 1.2562692e-01 1.3071698e-01 1.3077115e-01 1.2010966e-01 1.1841338e-01 1.4987755e-01 1.2848062e-01 1.4548859e-01 1.1783301e-01 1.5172346e-01 1.2426441e-01 1.5309487e-01 1.3983154e-01 1.3778354e-01 1.3093423e-01 1.1660453e-01 1.2409289e-01 1.3931059e-01 1.1865130e-01 1.4440871e-01 1.4196632e-01 1.3805465e-01 1.2801436e-01 1.3865562e-01 1.2553921e-01 1.3109454e-01 1.2958547e-01 1.5860612e-03 1.6773969e-03 0.0000000e+00 8.4337656e-04 4.6746407e-04 2.4549978e-03 4.7529836e-03 2.3235808e-03 4.0683267e-03 4.3260986e-03 6.7336618e-04 2.8454658e-03 1.0918918e-03 1.3756658e-03 5.7784546e-04 5.9573290e-02 6.3070670e-02 6.9597309e-02 7.9911457e-02 7.3480528e-02 7.9923883e-02 7.0144874e-02 5.5923876e-02 6.6635620e-02 7.3192589e-02 7.4096565e-02 6.5836734e-02 7.1022277e-02 7.8555696e-02 5.0423423e-02 5.7089619e-02 8.1093473e-02 6.2483167e-02 9.2251714e-02 6.5399221e-02 8.6573432e-02 5.7873871e-02 9.3861710e-02 7.7914479e-02 6.0545459e-02 6.0328179e-02 7.3725736e-02 8.0652769e-02 7.5662466e-02 4.7500835e-02 6.6267157e-02 6.1219907e-02 5.9246920e-02 1.0235525e-01 8.5584812e-02 6.7627185e-02 6.6676399e-02 8.1028522e-02 6.3874534e-02 7.4037098e-02 8.3735875e-02 7.3091049e-02 6.4875922e-02 5.7134332e-02 7.3898502e-02 6.3669341e-02 6.7483654e-02 6.3032151e-02 4.3195391e-02 6.6465430e-02 1.2757303e-01 1.1294171e-01 1.0654531e-01 1.1074736e-01 1.1756538e-01 1.1705185e-01 1.1633100e-01 1.1230305e-01 1.1988948e-01 1.0404710e-01 8.7981667e-02 1.0622547e-01 1.0061669e-01 1.2008497e-01 1.2242153e-01 1.0217012e-01 1.0084187e-01 1.0181343e-01 1.3714147e-01 1.1243585e-01 1.0356517e-01 1.1071692e-01 1.2181923e-01 9.3742899e-02 1.0137566e-01 9.8085445e-02 8.9840814e-02 8.9979544e-02 1.1682155e-01 9.4340727e-02 1.0893096e-01 8.8036209e-02 1.1887723e-01 9.1995894e-02 1.1718099e-01 1.0529554e-01 1.1115622e-01 1.0048991e-01 8.8823715e-02 9.3647258e-02 1.0909883e-01 8.9729548e-02 1.1294171e-01 1.1121254e-01 1.0947844e-01 9.8164553e-02 1.0458423e-01 9.5468337e-02 1.0529433e-01 1.0077315e-01 1.0959804e-05 1.5860612e-03 1.2066237e-03 9.8801305e-04 9.8752232e-04 6.0992006e-03 2.3026455e-03 4.3295194e-03 6.8464966e-03 1.1467011e-03 3.5017117e-03 1.7326793e-03 1.1803657e-03 5.4725577e-04 7.4912594e-02 7.8462634e-02 8.6080465e-02 9.7055695e-02 8.9913940e-02 9.8246196e-02 8.6359614e-02 7.0875411e-02 8.3096610e-02 8.9373603e-02 9.1095332e-02 8.1132274e-02 8.7784959e-02 9.6471287e-02 6.3584445e-02 7.1727351e-02 9.8741504e-02 7.9297358e-02 1.1001947e-01 8.1763635e-02 1.0392038e-01 7.2462318e-02 1.1283140e-01 9.6493172e-02 7.5904241e-02 7.5362083e-02 9.0691159e-02 9.7798442e-02 9.2550220e-02 6.1191529e-02 8.2519289e-02 7.7118501e-02 7.4418954e-02 1.2241546e-01 1.0373213e-01 8.3271349e-02 8.2617382e-02 9.8302252e-02 7.9806174e-02 9.0742202e-02 1.0274136e-01 9.0294649e-02 8.0841362e-02 7.2046648e-02 9.1054206e-02 8.0166072e-02 8.3952641e-02 7.8850217e-02 5.4962225e-02 8.2583735e-02 1.4771497e-01 1.3277361e-01 1.2596285e-01 1.3150244e-01 1.3764710e-01 1.3814813e-01 1.3643324e-01 1.3364709e-01 1.4127174e-01 1.2228442e-01 1.0501430e-01 1.2555100e-01 1.1896947e-01 1.3983185e-01 1.4080838e-01 1.1967635e-01 1.2050783e-01 1.2165153e-01 1.5932394e-01 1.3324590e-01 1.2180922e-01 1.2960297e-01 1.4356572e-01 1.1163860e-01 1.2028439e-01 1.1797002e-01 1.0728569e-01 1.0776505e-01 1.3685139e-01 1.1414077e-01 1.2924282e-01 1.0675126e-01 1.3867943e-01 1.1135088e-01 1.3991357e-01 1.2389778e-01 1.2963231e-01 1.2019755e-01 1.0634263e-01 1.1117491e-01 1.2727853e-01 1.0546335e-01 1.3277361e-01 1.3050496e-01 1.2753116e-01 1.1493738e-01 1.2310368e-01 1.1333277e-01 1.2330887e-01 1.1999903e-01 1.6773969e-03 1.4044930e-03 1.1476188e-03 1.1728210e-03 6.0850239e-03 2.5611449e-03 4.7720148e-03 7.3487783e-03 1.2981958e-03 3.8044610e-03 1.9624179e-03 1.3573639e-03 6.7050163e-04 7.5875383e-02 7.9591742e-02 8.7130965e-02 9.8136915e-02 9.0968214e-02 9.9474916e-02 8.7612410e-02 7.1886206e-02 8.4072967e-02 9.0657173e-02 9.2037661e-02 8.2322796e-02 8.8559519e-02 9.7661149e-02 6.4636714e-02 7.2692172e-02 1.0010765e-01 8.0266168e-02 1.1103798e-01 8.2745924e-02 1.0537406e-01 7.3430328e-02 1.1396572e-01 9.7599809e-02 7.6869874e-02 7.6340270e-02 9.1668858e-02 9.8974404e-02 9.3760611e-02 6.2005453e-02 8.3489209e-02 7.8020463e-02 7.5407355e-02 1.2375637e-01 1.0517097e-01 8.4594299e-02 8.3682974e-02 9.9214366e-02 8.1008204e-02 9.1862911e-02 1.0394615e-01 9.1478420e-02 8.1837398e-02 7.2994499e-02 9.2227696e-02 8.1322109e-02 8.5126430e-02 7.9879628e-02 5.5860810e-02 8.3714500e-02 1.4946173e-01 1.3427629e-01 1.2730802e-01 1.3293539e-01 1.3918009e-01 1.3947497e-01 1.3805161e-01 1.3491280e-01 1.4255824e-01 1.2381647e-01 1.0639120e-01 1.2689399e-01 1.2032986e-01 1.4134878e-01 1.4247553e-01 1.2120787e-01 1.2187456e-01 1.2309328e-01 1.6066767e-01 1.3444738e-01 1.2325830e-01 1.3118369e-01 1.4483072e-01 1.1290137e-01 1.2175315e-01 1.1925251e-01 1.0857610e-01 1.0914142e-01 1.3832403e-01 1.1530308e-01 1.3045824e-01 1.0804612e-01 1.4018015e-01 1.1257905e-01 1.4124338e-01 1.2516443e-01 1.3130183e-01 1.2160995e-01 1.0773181e-01 1.1250108e-01 1.2878318e-01 1.0678720e-01 1.3427629e-01 1.3201801e-01 1.2910620e-01 1.1632723e-01 1.2438544e-01 1.1469977e-01 1.2494953e-01 1.2148287e-01 8.4337656e-04 4.6746407e-04 2.4549978e-03 4.7529836e-03 2.3235808e-03 4.0683267e-03 4.3260986e-03 6.7336618e-04 2.8454658e-03 1.0918918e-03 1.3756658e-03 5.7784546e-04 5.9573290e-02 6.3070670e-02 6.9597309e-02 7.9911457e-02 7.3480528e-02 7.9923883e-02 7.0144874e-02 5.5923876e-02 6.6635620e-02 7.3192589e-02 7.4096565e-02 6.5836734e-02 7.1022277e-02 7.8555696e-02 5.0423423e-02 5.7089619e-02 8.1093473e-02 6.2483167e-02 9.2251714e-02 6.5399221e-02 8.6573432e-02 5.7873871e-02 9.3861710e-02 7.7914479e-02 6.0545459e-02 6.0328179e-02 7.3725736e-02 8.0652769e-02 7.5662466e-02 4.7500835e-02 6.6267157e-02 6.1219907e-02 5.9246920e-02 1.0235525e-01 8.5584812e-02 6.7627185e-02 6.6676399e-02 8.1028522e-02 6.3874534e-02 7.4037098e-02 8.3735875e-02 7.3091049e-02 6.4875922e-02 5.7134332e-02 7.3898502e-02 6.3669341e-02 6.7483654e-02 6.3032151e-02 4.3195391e-02 6.6465430e-02 1.2757303e-01 1.1294171e-01 1.0654531e-01 1.1074736e-01 1.1756538e-01 1.1705185e-01 1.1633100e-01 1.1230305e-01 1.1988948e-01 1.0404710e-01 8.7981667e-02 1.0622547e-01 1.0061669e-01 1.2008497e-01 1.2242153e-01 1.0217012e-01 1.0084187e-01 1.0181343e-01 1.3714147e-01 1.1243585e-01 1.0356517e-01 1.1071692e-01 1.2181923e-01 9.3742899e-02 1.0137566e-01 9.8085445e-02 8.9840814e-02 8.9979544e-02 1.1682155e-01 9.4340727e-02 1.0893096e-01 8.8036209e-02 1.1887723e-01 9.1995894e-02 1.1718099e-01 1.0529554e-01 1.1115622e-01 1.0048991e-01 8.8823715e-02 9.3647258e-02 1.0909883e-01 8.9729548e-02 1.1294171e-01 1.1121254e-01 1.0947844e-01 9.8164553e-02 1.0458423e-01 9.5468337e-02 1.0529433e-01 1.0077315e-01 6.2742331e-05 5.7500583e-04 7.7277880e-03 4.4752900e-04 1.9151463e-03 2.3881652e-03 8.6221368e-04 8.6997251e-04 5.7712764e-05 1.4261239e-04 1.6337476e-04 6.5357511e-02 6.7564930e-02 7.5599895e-02 8.6482524e-02 7.9693502e-02 8.5392044e-02 7.4152863e-02 6.0880360e-02 7.3094884e-02 7.7108408e-02 8.1483729e-02 6.9905750e-02 7.9537736e-02 8.4220614e-02 5.4020845e-02 6.2478782e-02 8.5095725e-02 6.8776984e-02 1.0023818e-01 7.1693803e-02 8.9974847e-02 6.3277271e-02 1.0126704e-01 8.4456284e-02 6.6380508e-02 6.5944304e-02 8.0776371e-02 8.6401760e-02 8.0678654e-02 5.3232214e-02 7.2705055e-02 6.7807498e-02 6.4728976e-02 1.0860574e-01 8.9250516e-02 7.0532132e-02 7.2192370e-02 8.9155393e-02 6.7825786e-02 7.9751674e-02 8.9848634e-02 7.8255825e-02 7.0908403e-02 6.2757363e-02 7.9213209e-02 6.8195297e-02 7.2146158e-02 6.8587288e-02 4.7220467e-02 7.1390990e-02 1.3114639e-01 1.1816523e-01 1.1284157e-01 1.1675611e-01 1.2280854e-01 1.2450964e-01 1.2061835e-01 1.2011904e-01 1.2793055e-01 1.0801409e-01 9.2203467e-02 1.1250005e-01 1.0614185e-01 1.2553294e-01 1.2604996e-01 1.0581394e-01 1.0665851e-01 1.0697400e-01 1.4569576e-01 1.2071369e-01 1.0835453e-01 1.1475367e-01 1.3023667e-01 9.9676180e-02 1.0601570e-01 1.0459446e-01 9.5152798e-02 9.4544535e-02 1.2261107e-01 1.0171679e-01 1.1677938e-01 9.3513090e-02 1.2443770e-01 9.8519974e-02 1.2493623e-01 1.1202254e-01 1.1414514e-01 1.0583616e-01 9.3110547e-02 9.8863035e-02 1.1361961e-01 9.4163495e-02 1.1816523e-01 1.1608967e-01 1.1325987e-01 1.0277556e-01 1.1111172e-01 1.0049112e-01 1.0810161e-01 1.0529258e-01 8.3201047e-04 6.6553558e-03 8.0817319e-04 2.3666253e-03 2.9401972e-03 6.0430641e-04 1.3109323e-03 2.0042209e-04 2.9042771e-04 6.4823857e-05 6.4369752e-02 6.6980839e-02 7.4622998e-02 8.5371818e-02 7.8644884e-02 8.4714120e-02 7.3778054e-02 6.0124143e-02 7.1976215e-02 7.6757894e-02 8.0112003e-02 6.9445381e-02 7.7800626e-02 8.3451231e-02 5.3558729e-02 6.1563285e-02 8.4824350e-02 6.7739660e-02 9.8727492e-02 7.0619990e-02 8.9865595e-02 6.2353086e-02 1.0002589e-01 8.3464224e-02 6.5377675e-02 6.4985443e-02 7.9506921e-02 8.5546781e-02 8.0035925e-02 5.2147031e-02 7.1577598e-02 6.6610625e-02 6.3822994e-02 1.0780225e-01 8.9119904e-02 7.0461434e-02 7.1328305e-02 8.7570443e-02 6.7451743e-02 7.8878318e-02 8.9018945e-02 7.7592264e-02 6.9886540e-02 6.1789581e-02 7.8498655e-02 6.7684600e-02 7.1587759e-02 6.7704325e-02 4.6526712e-02 7.0724596e-02 1.3117528e-01 1.1766016e-01 1.1197322e-01 1.1607657e-01 1.2231416e-01 1.2340084e-01 1.2042522e-01 1.1891967e-01 1.2666041e-01 1.0778594e-01 9.1812847e-02 1.1163169e-01 1.0543690e-01 1.2494830e-01 1.2593147e-01 1.0563286e-01 1.0596141e-01 1.0649455e-01 1.4431904e-01 1.1933022e-01 1.0786987e-01 1.1454955e-01 1.2887601e-01 9.8812977e-02 1.0562884e-01 1.0369928e-01 9.4451580e-02 9.4102215e-02 1.2194237e-01 1.0054711e-01 1.1549365e-01 9.2857172e-02 1.2382256e-01 9.7583400e-02 1.2385907e-01 1.1095947e-01 1.1423829e-01 1.0528942e-01 9.2735747e-02 9.8196145e-02 1.1321168e-01 9.3608756e-02 1.1766016e-01 1.1565299e-01 1.1307354e-01 1.0223904e-01 1.1010492e-01 9.9908851e-02 1.0821954e-01 1.0496782e-01 9.9295890e-03 5.3473138e-04 2.1693539e-03 3.7076821e-03 1.8222092e-03 1.2380588e-03 7.0429726e-04 3.2576994e-04 6.7027380e-04 7.5169669e-02 7.7062740e-02 8.6033367e-02 9.7417771e-02 9.0184218e-02 9.6534643e-02 8.3912481e-02 7.0270670e-02 8.3626528e-02 8.6844061e-02 9.2545497e-02 7.9257265e-02 9.0777176e-02 9.5235076e-02 6.2208659e-02 7.1858472e-02 9.5535449e-02 7.9390740e-02 1.1189093e-01 8.2130752e-02 1.0013922e-01 7.2643917e-02 1.1330079e-01 9.5998078e-02 7.6221141e-02 7.5580594e-02 9.1730689e-02 9.7114882e-02 9.1048046e-02 6.2224749e-02 8.3137120e-02 7.8094271e-02 7.4382594e-02 1.2083561e-01 9.9831872e-02 7.9711043e-02 8.2237066e-02 1.0057287e-01 7.7408745e-02 9.0227459e-02 1.0148848e-01 8.8790024e-02 8.1095637e-02 7.2322440e-02 8.9773416e-02 7.8184960e-02 8.2186791e-02 7.8567440e-02 5.4863235e-02 8.1347337e-02 1.4275245e-01 1.3005259e-01 1.2482271e-01 1.2925722e-01 1.3482920e-01 1.3759096e-01 1.3236924e-01 1.3338627e-01 1.4130888e-01 1.1880967e-01 1.0247320e-01 1.2443347e-01 1.1741517e-01 1.3747488e-01 1.3688510e-01 1.1618864e-01 1.1858835e-01 1.1879679e-01 1.5963135e-01 1.3387511e-01 1.1938532e-01 1.2588037e-01 1.4388488e-01 1.1083228e-01 1.1728514e-01 1.1680010e-01 1.0591970e-01 1.0525056e-01 1.3476053e-01 1.1410719e-01 1.2959057e-01 1.0487194e-01 1.3643100e-01 1.1046993e-01 1.3880755e-01 1.2375874e-01 1.2479172e-01 1.1764923e-01 1.0361548e-01 1.0965651e-01 1.2456830e-01 1.0392711e-01 1.3005259e-01 1.2763609e-01 1.2394348e-01 1.1308481e-01 1.2275352e-01 1.1138648e-01 1.1846972e-01 1.1666230e-01 1.1839370e-02 9.5901133e-03 1.3740734e-02 3.5338001e-03 1.3550011e-02 8.8526707e-03 9.4699409e-03 6.3350154e-03 4.8467987e-02 5.3749987e-02 5.7717889e-02 6.5676566e-02 6.0050880e-02 7.0693363e-02 6.1877604e-02 4.6729706e-02 5.4665912e-02 6.4242849e-02 5.9590174e-02 5.6493218e-02 5.5132102e-02 6.8254515e-02 4.1974046e-02 4.5979775e-02 7.3375693e-02 5.2912711e-02 7.3889063e-02 5.3879214e-02 7.8254066e-02 4.6403740e-02 7.8645675e-02 6.7578949e-02 4.9098308e-02 4.8671019e-02 5.9827034e-02 6.7861186e-02 6.5136324e-02 3.6759287e-02 5.3980965e-02 4.9365653e-02 4.8462917e-02 9.0010105e-02 7.8830145e-02 6.1496889e-02 5.5380536e-02 6.4107134e-02 5.6871111e-02 6.2034296e-02 7.3759034e-02 6.3674900e-02 5.3119604e-02 4.6143136e-02 6.3811294e-02 5.6759011e-02 5.9030859e-02 5.2419966e-02 3.3258744e-02 5.6880106e-02 1.1655832e-01 1.0005043e-01 9.1684275e-02 9.8727002e-02 1.0425490e-01 1.0138126e-01 1.0565129e-01 9.7538138e-02 1.0314095e-01 9.2386824e-02 7.6571607e-02 9.1274575e-02 8.6336607e-02 1.0505203e-01 1.0829773e-01 9.0077109e-02 8.8858206e-02 9.1549427e-02 1.1781623e-01 9.5530481e-02 9.0068051e-02 9.8963246e-02 1.0479240e-01 7.9118056e-02 9.0207341e-02 8.5766485e-02 7.6442113e-02 7.8991975e-02 1.0229780e-01 8.0971468e-02 9.2462279e-02 7.7616682e-02 1.0394880e-01 7.9854175e-02 1.0493060e-01 8.8086863e-02 1.0105189e-01 8.9771099e-02 7.8157448e-02 7.9782471e-02 9.4953430e-02 7.4768636e-02 1.0005043e-01 9.8253215e-02 9.6744777e-02 8.3113340e-02 8.7744069e-02 8.2353939e-02 9.5830913e-02 9.0799350e-02 2.1341759e-03 1.9110407e-03 2.4747400e-03 1.4759127e-04 2.6111423e-04 2.1389787e-04 9.9338048e-04 7.1511570e-02 7.2884660e-02 8.1954557e-02 9.3478761e-02 8.6419800e-02 9.0995738e-02 7.9065079e-02 6.6366318e-02 7.9643396e-02 8.2112775e-02 8.8849792e-02 7.5066735e-02 8.7600830e-02 9.0111864e-02 5.8883625e-02 6.8498056e-02 8.9863635e-02 7.4803608e-02 1.0853136e-01 7.8097142e-02 9.4627165e-02 6.9370599e-02 1.0871906e-01 9.0676026e-02 7.2617660e-02 7.2144076e-02 8.7900274e-02 9.2810262e-02 8.6387249e-02 5.9335633e-02 7.9309675e-02 7.4397587e-02 7.0710353e-02 1.1504320e-01 9.3692385e-02 7.4629165e-02 7.8263498e-02 9.7248749e-02 7.2487054e-02 8.6011897e-02 9.5823155e-02 8.3806772e-02 7.7264691e-02 6.8838121e-02 8.4948991e-02 7.3047820e-02 7.7341294e-02 7.4548923e-02 5.2558496e-02 7.6909140e-02 1.3627575e-01 1.2430643e-01 1.1978148e-01 1.2301688e-01 1.2902659e-01 1.3200226e-01 1.2597225e-01 1.2757685e-01 1.3584109e-01 1.1348127e-01 9.7761057e-02 1.1945082e-01 1.1270375e-01 1.3215934e-01 1.3182973e-01 1.1125522e-01 1.1287096e-01 1.1260483e-01 1.5425448e-01 1.2876341e-01 1.1448536e-01 1.2024528e-01 1.3833407e-01 1.0647529e-01 1.1163750e-01 1.1113351e-01 1.0149473e-01 1.0013701e-01 1.2927004e-01 1.0879101e-01 1.2459621e-01 9.9330508e-02 1.3108770e-01 1.0504760e-01 1.3186075e-01 1.1958837e-01 1.1892921e-01 1.1162833e-01 9.8541878e-02 1.0525098e-01 1.1976536e-01 1.0049727e-01 1.2430643e-01 1.2212353e-01 1.1885667e-01 1.0916600e-01 1.1853397e-01 1.0665478e-01 1.1270995e-01 1.1063603e-01 1.5750019e-03 2.3061943e-03 2.5267586e-03 1.8816551e-03 2.4916769e-03 2.6490348e-03 5.6749564e-02 5.7171493e-02 6.5939847e-02 7.5932398e-02 6.9508351e-02 7.4656451e-02 6.2624290e-02 5.2024188e-02 6.4437230e-02 6.4937043e-02 7.2745199e-02 5.8611781e-02 7.2652586e-02 7.3573688e-02 4.4171072e-02 5.3594578e-02 7.2641168e-02 6.1050608e-02 8.9566976e-02 6.3051224e-02 7.6010398e-02 5.4225638e-02 9.0321512e-02 7.5195114e-02 5.7639533e-02 5.6852233e-02 7.1710391e-02 7.4949900e-02 6.9318916e-02 4.6230253e-02 6.3971485e-02 6.0026157e-02 5.5798348e-02 9.5989279e-02 7.6232404e-02 5.8444003e-02 6.2302257e-02 8.0224536e-02 5.7278169e-02 6.9150783e-02 7.9467477e-02 6.7704503e-02 6.1864652e-02 5.4241616e-02 6.8592620e-02 5.8516944e-02 6.1759106e-02 5.9360348e-02 3.8591759e-02 6.1151978e-02 1.1324556e-01 1.0303819e-01 9.9201558e-02 1.0318500e-01 1.0721879e-01 1.1147036e-01 1.0460948e-01 1.0828938e-01 1.1523751e-01 9.2280249e-02 7.8300492e-02 9.8832112e-02 9.2088327e-02 1.0954453e-01 1.0774163e-01 8.9704304e-02 9.3649245e-02 9.3477002e-02 1.3171389e-01 1.0896793e-01 9.3248418e-02 9.8534682e-02 1.1788866e-01 8.6730988e-02 9.1550165e-02 9.2743651e-02 8.2023117e-02 8.1036634e-02 1.0749375e-01 9.1211084e-02 1.0479473e-01 8.1639608e-02 1.0872731e-01 8.7298316e-02 1.1342725e-01 9.8507274e-02 9.7018335e-02 9.2572028e-02 7.9424005e-02 8.5123740e-02 9.7519007e-02 7.9504622e-02 1.0303819e-01 1.0060889e-01 9.6540999e-02 8.7528391e-02 9.7464514e-02 8.6517419e-02 9.1407615e-02 9.1076579e-02 4.2759888e-03 1.3998823e-03 1.8495751e-03 2.8301386e-03 3.7334484e-03 5.5009109e-02 5.4964037e-02 6.3820777e-02 7.4331569e-02 6.8062522e-02 7.0406681e-02 5.9595322e-02 4.9880526e-02 6.2237804e-02 6.2265151e-02 7.1113130e-02 5.6594278e-02 7.1451534e-02 6.9994053e-02 4.3126676e-02 5.2381128e-02 6.8520339e-02 5.7597897e-02 8.9058557e-02 6.0751652e-02 7.2510768e-02 5.3201106e-02 8.7849614e-02 7.0985642e-02 5.6028788e-02 5.5591108e-02 6.9920676e-02 7.2931234e-02 6.6633349e-02 4.5295849e-02 6.2044642e-02 5.8063752e-02 5.4097801e-02 9.1903182e-02 7.1473709e-02 5.5091248e-02 6.0376704e-02 7.9310761e-02 5.3854777e-02 6.7042056e-02 7.4968513e-02 6.4284932e-02 5.9983090e-02 5.2745182e-02 6.5463515e-02 5.4553279e-02 5.8476643e-02 5.7183259e-02 3.8909009e-02 5.8515093e-02 1.0925308e-01 9.9624317e-02 9.6622305e-02 9.8607255e-02 1.0383961e-01 1.0792916e-01 1.0029496e-01 1.0408880e-01 1.1184636e-01 8.9608429e-02 7.6157674e-02 9.6354194e-02 9.0095873e-02 1.0708448e-01 1.0618312e-01 8.7751513e-02 8.9756663e-02 8.8888346e-02 1.2885985e-01 1.0591974e-01 9.1173391e-02 9.5499762e-02 1.1429851e-01 8.5048829e-02 8.8069568e-02 8.8675858e-02 8.0228990e-02 7.8160661e-02 1.0454059e-01 8.7419578e-02 1.0196835e-01 7.7696889e-02 1.0615601e-01 8.3458946e-02 1.0727792e-01 9.7348785e-02 9.3770662e-02 8.8214646e-02 7.6638467e-02 8.3519036e-02 9.5849316e-02 7.9674217e-02 9.9624317e-02 9.7643485e-02 9.4514516e-02 8.7075933e-02 9.6245490e-02 8.4428758e-02 8.8195121e-02 8.6900395e-02 3.3656636e-03 1.2813341e-03 1.5508786e-03 5.5126016e-04 5.7906981e-02 6.0932190e-02 6.7790041e-02 7.7661589e-02 7.1242410e-02 7.8649743e-02 6.7963400e-02 5.4275670e-02 6.5215187e-02 7.0651008e-02 7.2495968e-02 6.3298898e-02 6.9915496e-02 7.7039648e-02 4.7868354e-02 5.5074415e-02 7.9095397e-02 6.1872293e-02 8.9630734e-02 6.4010779e-02 8.3786653e-02 5.5727300e-02 9.1943867e-02 7.7179306e-02 5.8787276e-02 5.8290887e-02 7.2059853e-02 7.8226343e-02 7.3481150e-02 4.5978845e-02 6.4704253e-02 5.9980041e-02 5.7435326e-02 1.0050646e-01 8.3655459e-02 6.5308029e-02 6.4668898e-02 7.9130910e-02 6.2159352e-02 7.1908147e-02 8.2739305e-02 7.1488143e-02 6.3159434e-02 5.5376396e-02 7.2163727e-02 6.2508559e-02 6.5824794e-02 6.1340761e-02 4.0472995e-02 6.4599849e-02 1.2380425e-01 1.0993395e-01 1.0373859e-01 1.0879322e-01 1.1440237e-01 1.1495444e-01 1.1335001e-01 1.1089530e-01 1.1789001e-01 1.0041407e-01 8.4708778e-02 1.0336455e-01 9.7357032e-02 1.1642404e-01 1.1748667e-01 9.8081847e-02 9.8747663e-02 9.9805801e-02 1.3456454e-01 1.1060300e-01 9.9947092e-02 1.0709744e-01 1.2004751e-01 9.0729860e-02 9.8544296e-02 9.6489030e-02 8.6759859e-02 8.7175826e-02 1.1367288e-01 9.3122372e-02 1.0688813e-01 8.6280635e-02 1.1536030e-01 9.0496365e-02 1.1670380e-01 1.0195807e-01 1.0724940e-01 9.8472199e-02 8.5899722e-02 9.0288199e-02 1.0497525e-01 8.5251170e-02 1.0993395e-01 1.0787024e-01 1.0524763e-01 9.3789157e-02 1.0121309e-01 9.2226653e-02 1.0148816e-01 9.8306001e-02 5.0782435e-04 6.2020689e-04 1.6756986e-03 7.0476887e-02 7.1501157e-02 8.0709837e-02 9.2336294e-02 8.5349594e-02 8.8954923e-02 7.7301298e-02 6.5155686e-02 7.8476830e-02 8.0402665e-02 8.7884855e-02 7.3650267e-02 8.6974315e-02 8.8299731e-02 5.7895678e-02 6.7595783e-02 8.7665774e-02 7.3314500e-02 1.0776923e-01 7.6895020e-02 9.2474273e-02 6.8514181e-02 1.0728576e-01 8.8812805e-02 7.1614033e-02 7.1214401e-02 8.6845407e-02 9.1428310e-02 8.4778392e-02 5.8702706e-02 7.8223013e-02 7.3389903e-02 6.9650470e-02 1.1290739e-01 9.1229062e-02 7.2678536e-02 7.7045799e-02 9.6518176e-02 7.0683794e-02 8.4677029e-02 9.3753287e-02 8.2039996e-02 7.6152676e-02 6.7885263e-02 8.3273481e-02 7.1166737e-02 7.5618620e-02 7.3311647e-02 5.2129744e-02 7.5414559e-02 1.3361288e-01 1.2212885e-01 1.1803753e-01 1.2062983e-01 1.2681873e-01 1.3003785e-01 1.2339855e-01 1.2552093e-01 1.3397302e-01 1.1145011e-01 9.6072866e-02 1.1773621e-01 1.1108813e-01 1.3021606e-01 1.2991222e-01 1.0941117e-01 1.1074904e-01 1.1019635e-01 1.5245649e-01 1.2709803e-01 1.1272646e-01 1.1805571e-01 1.3644429e-01 1.0507143e-01 1.0948152e-01 1.0908044e-01 1.0002018e-01 9.8259510e-02 1.2725753e-01 1.0697349e-01 1.2296793e-01 9.7285465e-02 1.2913723e-01 1.0312519e-01 1.2920697e-01 1.1832381e-01 1.1655849e-01 1.0932054e-01 9.6669076e-02 1.0377916e-01 1.1803847e-01 9.9488230e-02 1.2212885e-01 1.2004765e-01 1.1693776e-01 1.0790940e-01 1.1723226e-01 1.0500092e-01 1.1038445e-01 1.0827603e-01 1.5232780e-04 4.0007181e-04 6.5289213e-02 6.7204649e-02 7.5443787e-02 8.6444762e-02 7.9660759e-02 8.4749914e-02 7.3546883e-02 6.0641799e-02 7.3012359e-02 7.6531840e-02 8.1598668e-02 6.9499172e-02 7.9923854e-02 8.3724381e-02 5.3799024e-02 6.2453508e-02 8.4253942e-02 6.8486788e-02 1.0054294e-01 7.1576362e-02 8.9122210e-02 6.3281093e-02 1.0114322e-01 8.3993466e-02 6.6335786e-02 6.5925957e-02 8.0814161e-02 8.6165539e-02 8.0247902e-02 5.3368917e-02 7.2666724e-02 6.7816556e-02 6.4623203e-02 1.0800053e-01 8.8235002e-02 6.9728892e-02 7.2010954e-02 8.9470933e-02 6.7181463e-02 7.9529500e-02 8.9243318e-02 7.7739806e-02 7.0823200e-02 6.2720078e-02 7.8762930e-02 6.7548169e-02 7.1608713e-02 6.8390856e-02 4.7339693e-02 7.1004341e-02 1.3006516e-01 1.1747357e-01 1.1247378e-01 1.1599806e-01 1.2210677e-01 1.2411360e-01 1.1962941e-01 1.1968938e-01 1.2763702e-01 1.0730056e-01 9.1685935e-02 1.1214725e-01 1.0578284e-01 1.2500364e-01 1.2540001e-01 1.0518188e-01 1.0602704e-01 1.0612761e-01 1.4550231e-01 1.2054520e-01 1.0786099e-01 1.1396219e-01 1.2996046e-01 9.9460763e-02 1.0527691e-01 1.0405039e-01 9.4844256e-02 9.3945852e-02 1.2206303e-01 1.0136794e-01 1.1659636e-01 9.2882126e-02 1.2391115e-01 9.8044098e-02 1.2415930e-01 1.1196299e-01 1.1316845e-01 1.0506061e-01 9.2491851e-02 9.8554383e-02 1.1313163e-01 9.4057839e-02 1.1747357e-01 1.1542880e-01 1.1260458e-01 1.0255212e-01 1.1101490e-01 1.0006494e-01 1.0712947e-01 1.0442482e-01 3.2637736e-04 7.1228332e-02 7.3360309e-02 8.1848237e-02 9.3197783e-02 8.6156854e-02 9.1733116e-02 8.0052769e-02 6.6468809e-02 7.9277125e-02 8.3130576e-02 8.8073461e-02 7.5751270e-02 8.6114214e-02 9.0610516e-02 5.9264749e-02 6.8242422e-02 9.1255768e-02 7.4667198e-02 1.0756842e-01 7.7803425e-02 9.6260495e-02 6.9087389e-02 1.0845861e-01 9.0870006e-02 7.2305626e-02 7.1861030e-02 8.7311178e-02 9.3009359e-02 8.6960677e-02 5.8641082e-02 7.8892851e-02 7.3809649e-02 7.0554233e-02 1.1577001e-01 9.5424873e-02 7.6110400e-02 7.8288785e-02 9.6097634e-02 7.3451146e-02 8.6123272e-02 9.6377446e-02 8.4403314e-02 7.7003085e-02 6.8529959e-02 8.5437768e-02 7.3849362e-02 7.8032414e-02 7.4531055e-02 5.2299301e-02 7.7339214e-02 1.3853845e-01 1.2553252e-01 1.2026484e-01 1.2407451e-01 1.3030382e-01 1.3228892e-01 1.2783055e-01 1.2774776e-01 1.3586522e-01 1.1498861e-01 9.8800101e-02 1.1991948e-01 1.1333278e-01 1.3320266e-01 1.3352807e-01 1.1273476e-01 1.1373642e-01 1.1391093e-01 1.5417322e-01 1.2849633e-01 1.1550662e-01 1.2189074e-01 1.3824515e-01 1.0674666e-01 1.1296262e-01 1.1166908e-01 1.0200786e-01 1.0119861e-01 1.3020916e-01 1.0880524e-01 1.2443747e-01 1.0015009e-01 1.3208523e-01 1.0543261e-01 1.3248948e-01 1.1957063e-01 1.2107326e-01 1.1278773e-01 9.9690412e-02 1.0583253e-01 1.2090813e-01 1.0100447e-01 1.2553252e-01 1.2339293e-01 1.2039941e-01 1.0985773e-01 1.1861026e-01 1.0744824e-01 1.1483872e-01 1.1213527e-01 6.6878313e-02 6.9715409e-02 7.7364528e-02 8.8171063e-02 8.1336125e-02 8.7984980e-02 7.6788970e-02 6.2680317e-02 7.4638540e-02 7.9773718e-02 8.2744330e-02 7.2225777e-02 8.0191157e-02 8.6590874e-02 5.5913229e-02 6.3969698e-02 8.8177048e-02 7.0519412e-02 1.0143390e-01 7.3287383e-02 9.3242485e-02 6.4744474e-02 1.0311960e-01 8.6619094e-02 6.7881619e-02 6.7447267e-02 8.2187438e-02 8.8483348e-02 8.3036483e-02 5.4275541e-02 7.4193034e-02 6.9119612e-02 6.6341718e-02 1.1134910e-01 9.2648103e-02 7.3522039e-02 7.4021876e-02 9.0141442e-02 7.0408618e-02 8.1718494e-02 9.2345175e-02 8.0646828e-02 7.2501162e-02 6.4223536e-02 8.1515678e-02 7.0682605e-02 7.4552513e-02 7.0364536e-02 4.8478235e-02 7.3560525e-02 1.3517901e-01 1.2131760e-01 1.1535330e-01 1.1982809e-01 1.2602646e-01 1.2698846e-01 1.2430464e-01 1.2251062e-01 1.3021845e-01 1.1127258e-01 9.4973617e-02 1.1499323e-01 1.0869119e-01 1.2854331e-01 1.2950169e-01 1.0899747e-01 1.0948608e-01 1.1017110e-01 1.4797664e-01 1.2271397e-01 1.1121974e-01 1.1817834e-01 1.3245873e-01 1.0189203e-01 1.0916690e-01 1.0716280e-01 9.7527876e-02 9.7386912e-02 1.2555051e-01 1.0384747e-01 1.1883003e-01 9.6216108e-02 1.2741273e-01 1.0091958e-01 1.2779606e-01 1.1407020e-01 1.1794445e-01 1.0890370e-01 9.6003260e-02 1.0130628e-01 1.1658801e-01 9.6417670e-02 1.2131760e-01 1.1923852e-01 1.1654354e-01 1.0526503e-01 1.1322934e-01 1.0313209e-01 1.1184758e-01 1.0860395e-01 7.4548764e-04 4.1909104e-04 1.5729317e-03 7.9277531e-04 2.5985333e-03 2.1157871e-03 2.6363318e-04 2.7214324e-04 2.5006701e-03 1.1999234e-03 1.3641574e-03 2.4228657e-03 1.9193080e-03 1.5580763e-03 9.1547441e-05 4.2498838e-03 5.1587623e-04 4.4451535e-03 1.9710136e-04 5.9468529e-03 1.1637586e-04 4.0879090e-03 1.9577278e-03 7.5438506e-06 5.1321994e-05 9.5746588e-04 1.8362275e-03 1.6353958e-03 8.5567943e-04 2.3534169e-04 2.0155706e-04 2.8744085e-05 6.6464816e-03 6.0500135e-03 3.7609984e-03 3.0183871e-04 2.6908058e-03 1.8208811e-03 9.2994693e-04 3.0287331e-03 1.4489077e-03 1.1766146e-04 3.4058119e-05 1.3255619e-03 1.5104843e-03 1.2375803e-03 1.2042792e-04 2.2770079e-03 7.0925866e-04 1.7863102e-02 1.0250835e-02 7.3485693e-03 9.3316539e-03 1.1683662e-02 1.0169272e-02 1.2909901e-02 9.0217168e-03 1.1005819e-02 9.1383359e-03 4.6331293e-03 7.2658380e-03 6.2625155e-03 1.2163934e-02 1.5604510e-02 9.0849374e-03 6.4499430e-03 7.6430735e-03 1.6734199e-02 8.8633570e-03 7.8095179e-03 1.1083442e-02 1.1720144e-02 4.3192019e-03 7.6217573e-03 5.4833250e-03 3.8602370e-03 4.7396014e-03 1.0813609e-02 4.4772013e-03 7.7667524e-03 3.8237256e-03 1.1654961e-02 4.0471444e-03 1.1640132e-02 6.9842505e-03 1.3199398e-02 6.9808241e-03 4.8195688e-03 4.8204301e-03 9.7963275e-03 5.3012133e-03 1.0250835e-02 1.0025463e-02 1.1016974e-02 6.8718627e-03 6.8391566e-03 5.4173007e-03 1.1780352e-02 7.9156890e-03 7.3587704e-04 1.9839586e-03 1.2219042e-03 1.5405721e-03 4.1726993e-04 3.7297590e-04 1.1835574e-03 6.1737386e-04 2.7177989e-03 1.1395438e-04 5.3634867e-03 1.2095802e-03 9.5552631e-04 7.0211590e-04 1.8297765e-03 1.3098829e-03 5.8522900e-03 1.0011765e-03 2.8317516e-03 7.2859483e-04 4.4710316e-03 2.0244176e-03 7.6733040e-04 6.7685529e-04 2.1301657e-03 1.3518842e-03 6.1205846e-04 2.4644310e-03 1.1938153e-03 1.5975309e-03 5.1076446e-04 5.2791382e-03 3.0339660e-03 1.1907842e-03 3.5011674e-04 4.7257393e-03 3.5465173e-04 7.3647426e-04 2.3478030e-03 5.8214999e-04 7.6861523e-04 8.1449121e-04 5.9893306e-04 4.9750178e-04 2.3193980e-04 3.6882491e-04 2.8017975e-03 9.9802686e-05 1.3102890e-02 7.5969761e-03 6.0828162e-03 7.3064415e-03 8.8657957e-03 9.3996859e-03 9.1529212e-03 8.6958624e-03 1.0707084e-02 5.8653516e-03 2.3848099e-03 6.0110419e-03 4.6435179e-03 9.5841132e-03 1.1495876e-02 5.7351815e-03 4.7762002e-03 5.2279685e-03 1.6343054e-02 9.1684398e-03 5.3835136e-03 7.5338144e-03 1.1677909e-02 3.4411805e-03 4.9237859e-03 4.5530879e-03 2.5678146e-03 2.5808970e-03 8.5897045e-03 4.5950787e-03 7.8389611e-03 2.4182455e-03 9.2053995e-03 3.4479875e-03 1.0780684e-02 6.4366206e-03 8.7029459e-03 4.8244677e-03 2.4820766e-03 3.2994674e-03 6.9725103e-03 3.6004536e-03 7.5969761e-03 7.2055876e-03 7.4680636e-03 4.8244418e-03 6.1219436e-03 3.5524443e-03 7.3785563e-03 5.0327851e-03 4.5958248e-04 1.4323573e-04 1.2376608e-03 1.4854110e-03 8.9326822e-04 1.4516004e-04 1.6538068e-03 6.5338340e-04 1.1299287e-03 2.4074626e-03 6.9551596e-04 2.6949476e-03 7.2730987e-04 2.7608296e-03 6.5430290e-04 2.7448595e-03 1.4433165e-04 4.0981488e-03 7.0217976e-04 2.0017194e-03 8.4737221e-04 3.5875154e-04 4.2590326e-04 3.7194881e-04 5.5222558e-04 6.3134375e-04 2.4465093e-03 1.5551077e-04 5.7141821e-04 4.4617793e-04 3.7751002e-03 4.2307180e-03 3.3297057e-03 8.4238856e-05 1.8060244e-03 1.6735237e-03 1.3470834e-04 1.4289074e-03 6.1420964e-04 1.0488316e-04 6.6710002e-04 4.5037482e-04 1.3817339e-03 8.2351922e-04 1.8947713e-04 4.2527867e-03 4.2014447e-04 1.3415184e-02 6.6868372e-03 4.2910287e-03 5.8862692e-03 7.8406972e-03 6.4938295e-03 9.0912854e-03 5.6561218e-03 7.2322567e-03 6.1641630e-03 2.7540356e-03 4.2344365e-03 3.5847503e-03 8.2354910e-03 1.1610160e-02 6.2859853e-03 3.6577005e-03 4.7344286e-03 1.2033367e-02 5.6148647e-03 4.9101139e-03 7.6703569e-03 7.8664866e-03 2.1500523e-03 4.7726337e-03 2.9188064e-03 1.9107921e-03 2.6804057e-03 7.0603390e-03 2.2776004e-03 4.6992435e-03 1.8838392e-03 7.7943774e-03 1.9124732e-03 7.9102721e-03 4.1636243e-03 9.7823337e-03 4.1721395e-03 2.8414673e-03 2.6224860e-03 6.5787438e-03 3.5255969e-03 6.6868372e-03 6.5865547e-03 7.7526002e-03 4.4665713e-03 4.0404299e-03 3.0508638e-03 8.7371106e-03 5.0758684e-03 1.4741381e-04 1.6125952e-03 2.5451963e-03 2.5693849e-03 7.6673984e-04 2.4419486e-03 4.9090126e-04 2.2351893e-03 2.2914219e-03 9.4806103e-04 4.8625587e-03 2.0262116e-03 3.3701508e-03 1.9166326e-03 1.0683081e-03 9.0452408e-04 4.3648468e-03 1.8983778e-03 7.0324108e-04 1.1499379e-03 1.4013722e-03 1.4463883e-03 3.0992383e-04 2.7889447e-04 9.9370768e-04 4.4215550e-03 7.4609763e-04 1.5322654e-03 1.6670592e-03 2.5458750e-03 4.8164855e-03 4.9491294e-03 7.9622502e-04 9.3705610e-04 3.2918730e-03 3.3244867e-04 1.4917733e-03 1.2997427e-03 8.4964648e-04 1.9833350e-03 9.7663737e-04 3.0207145e-03 1.9767871e-03 1.2186915e-03 6.6584952e-03 1.4294810e-03 1.1447125e-02 4.9138585e-03 2.4483433e-03 4.3479747e-03 5.8020082e-03 4.0665925e-03 7.5951000e-03 3.6015682e-03 4.5184530e-03 5.0362911e-03 2.3873447e-03 2.3916463e-03 2.1166084e-03 5.8005881e-03 9.3326086e-03 5.1921662e-03 2.5583136e-03 3.9040792e-03 8.1942534e-03 3.2098756e-03 3.4610228e-03 6.2351763e-03 5.0378736e-03 9.3974884e-04 3.8135545e-03 1.8905878e-03 1.0935023e-03 2.2739982e-03 4.8352649e-03 1.2660181e-03 2.5068904e-03 1.6511455e-03 5.4229770e-03 1.1989559e-03 6.1054655e-03 2.0081796e-03 8.6788304e-03 3.3159020e-03 2.5546078e-03 1.5534900e-03 4.7975700e-03 2.5264412e-03 4.9138585e-03 4.8875866e-03 6.2264786e-03 3.0375207e-03 1.9750388e-03 2.0217929e-03 8.0063599e-03 4.3571190e-03 1.6611913e-03 2.0167984e-03 1.5438876e-03 3.6494229e-04 2.0217253e-03 4.6462511e-04 1.5312613e-03 2.1707116e-03 9.6955753e-04 3.3920004e-03 1.0848877e-03 3.2679078e-03 1.3217642e-03 1.7681092e-03 4.3634818e-04 4.3762590e-03 9.8982281e-04 1.4702047e-03 1.2309511e-03 6.6753502e-04 6.7519636e-04 2.4877587e-04 3.8105160e-04 8.2183923e-04 3.0399429e-03 3.2267433e-04 8.9534104e-04 8.4272052e-04 3.5139014e-03 4.8234985e-03 4.1432495e-03 3.0463297e-04 1.2193814e-03 2.4923401e-03 1.7011388e-04 1.7584273e-03 1.0444437e-03 3.3026408e-04 1.0723664e-03 7.7198092e-04 2.2859213e-03 1.4276818e-03 5.7687029e-04 4.8466907e-03 8.6936394e-04 1.2850654e-02 6.1088154e-03 3.5569698e-03 5.5719646e-03 7.1488198e-03 5.6571014e-03 8.7634622e-03 5.0674178e-03 6.2538450e-03 5.7468537e-03 2.5694606e-03 3.4855366e-03 2.9211508e-03 7.2454781e-03 1.0560745e-02 5.7784909e-03 3.4230741e-03 4.7152707e-03 1.0513823e-02 4.7114505e-03 4.2697686e-03 7.1803873e-03 6.8688743e-03 1.5447042e-03 4.5401495e-03 2.7361382e-03 1.4855517e-03 2.5857007e-03 6.2270622e-03 2.0576267e-03 3.8519560e-03 2.0130817e-03 6.8461641e-03 1.8250891e-03 7.7109246e-03 3.0910476e-03 9.4645289e-03 4.1075164e-03 2.7837137e-03 2.0667492e-03 5.7442304e-03 2.7416306e-03 6.1088154e-03 5.9744542e-03 7.0784056e-03 3.5969994e-03 3.0144395e-03 2.5834027e-03 8.5675229e-03 5.0446428e-03 9.3410747e-04 2.6108336e-03 1.8631164e-03 1.0514258e-03 2.7757095e-03 1.6027319e-03 5.9009151e-03 9.3507643e-05 4.8585250e-03 3.2279691e-03 7.0814372e-04 1.8064210e-03 4.7021523e-03 1.7906187e-03 1.7438865e-03 3.2726931e-03 2.1092470e-03 4.4199215e-04 2.5742583e-03 2.7576288e-03 2.1865740e-03 8.5954487e-04 4.3875130e-04 6.2385517e-03 2.0706463e-03 2.9895922e-03 2.4900150e-03 1.5452115e-03 1.2972442e-03 2.2089282e-03 1.3825902e-03 4.4047937e-03 1.3378707e-03 8.5528525e-04 1.4287812e-04 2.3200710e-04 1.9018574e-03 3.1193134e-03 2.7835858e-04 1.0440284e-03 6.7774868e-04 1.6434489e-03 8.2787646e-03 9.1805293e-04 8.2830461e-03 3.6334874e-03 2.7936555e-03 2.6701226e-03 4.5417785e-03 4.4199379e-03 4.7638022e-03 3.6853341e-03 5.4544340e-03 3.4799734e-03 1.6014887e-03 2.8117239e-03 2.4093914e-03 5.5035080e-03 8.3987820e-03 4.0331795e-03 1.3946498e-03 1.6013776e-03 9.9231424e-03 4.6841468e-03 3.0824955e-03 4.3724199e-03 6.0877536e-03 1.8755546e-03 2.0838499e-03 1.1387331e-03 1.4636530e-03 1.0937142e-03 4.3877001e-03 1.4753015e-03 3.8552640e-03 2.5868499e-04 5.1223304e-03 7.0168484e-04 4.3920187e-03 3.9679568e-03 5.8082286e-03 1.3720301e-03 1.2437314e-03 1.9911684e-03 4.5361159e-03 3.9571028e-03 3.6334874e-03 3.7423473e-03 5.0666856e-03 3.9975266e-03 3.7265040e-03 1.8650355e-03 5.0595778e-03 1.9211780e-03 1.4802848e-03 2.3288314e-03 6.8065434e-05 4.0423471e-03 2.2193959e-04 7.6466779e-03 9.2054349e-04 2.1021154e-03 2.1770952e-03 5.4412625e-04 2.3412508e-03 6.7735470e-03 2.1146424e-03 1.1473823e-03 2.2087962e-03 4.4699141e-03 2.0616206e-03 2.1327263e-03 2.0380999e-03 3.2539726e-03 1.3598045e-03 3.9670027e-04 4.8634117e-03 2.4197901e-03 3.2204932e-03 1.7622064e-03 3.8856461e-03 1.2334789e-03 4.0452577e-04 1.0887743e-03 6.2435560e-03 2.0292084e-04 1.0829901e-03 1.7786841e-03 4.1228310e-04 1.8859956e-03 2.3286589e-03 5.1772577e-04 4.9172870e-04 2.0007065e-04 1.2918676e-03 5.0330599e-03 3.9898095e-04 9.4287570e-03 5.3866372e-03 4.8647393e-03 5.2819054e-03 6.4598280e-03 8.0073865e-03 6.1852254e-03 7.5566712e-03 9.5128884e-03 3.7267827e-03 1.2843714e-03 4.8237356e-03 3.5085400e-03 7.3800011e-03 8.6672418e-03 3.7373665e-03 3.3006522e-03 3.2553459e-03 1.4768796e-02 8.5372346e-03 3.7597131e-03 5.0208833e-03 1.0540608e-02 2.9216135e-03 2.9987594e-03 3.5026889e-03 1.9346800e-03 1.3415748e-03 6.5619943e-03 4.2356898e-03 7.2096387e-03 1.4370251e-03 7.0771420e-03 2.7742326e-03 8.9575949e-03 5.8574207e-03 5.7119619e-03 3.0499081e-03 1.2067939e-03 2.4757916e-03 5.0772246e-03 3.1235792e-03 5.3866372e-03 5.0347068e-03 5.1609892e-03 3.8212989e-03 5.4644467e-03 2.4256658e-03 4.6018293e-03 2.9278326e-03 8.9754714e-04 1.9308173e-03 2.4780225e-03 8.6351250e-04 4.2337045e-03 2.1236680e-03 7.2381764e-04 2.1294516e-04 3.5897400e-03 7.7335429e-04 6.4715989e-03 7.0701488e-04 5.1814848e-03 2.9010443e-04 5.5409554e-03 2.4566735e-03 3.3429771e-04 3.2970103e-04 2.0470962e-03 2.3835444e-03 1.6383998e-03 1.0044594e-03 8.9092263e-04 8.3257442e-04 1.5048347e-04 7.3947599e-03 5.1765389e-03 2.4819362e-03 5.2929628e-04 4.5409213e-03 9.8467896e-04 1.3414871e-03 3.3640577e-03 1.3544701e-03 5.6680732e-04 2.1931945e-04 1.3672243e-03 8.5440953e-04 8.3314842e-04 2.6161270e-04 1.7366856e-03 5.2799539e-04 1.7649847e-02 1.0764587e-02 8.4383096e-03 9.9924928e-03 1.2273825e-02 1.1788899e-02 1.2850999e-02 1.0630699e-02 1.2964118e-02 9.0438994e-03 4.5288546e-03 8.3580766e-03 6.9988358e-03 1.3044370e-02 1.5771615e-02 8.9276151e-03 7.0025139e-03 7.7681345e-03 1.9248623e-02 1.0890288e-02 8.2148016e-03 1.1050411e-02 1.3845989e-02 5.2067991e-03 7.6819868e-03 6.2976924e-03 4.3733262e-03 4.7066273e-03 1.1733076e-02 5.6953630e-03 9.5825887e-03 4.0184455e-03 1.2555508e-02 4.8201445e-03 1.2938333e-02 8.5012649e-03 1.2613331e-02 7.2187981e-03 4.6491251e-03 5.3739269e-03 1.0213138e-02 5.7106829e-03 1.0764587e-02 1.0424589e-02 1.1013685e-02 7.3861586e-03 8.2413252e-03 5.8213434e-03 1.1031181e-02 7.7860554e-03 2.6482878e-03 4.2794287e-04 1.8319104e-03 1.5871773e-03 1.2340260e-03 2.9761573e-03 6.4181441e-04 3.9337040e-03 3.6570436e-04 2.9155752e-03 1.3382816e-05 5.6692471e-03 6.4631376e-04 2.4262663e-03 1.0063227e-03 2.3414789e-04 3.8352406e-04 2.6670153e-04 1.1841642e-03 1.3362953e-03 1.8214422e-03 1.1939941e-05 1.6841308e-04 4.0205285e-04 4.8423454e-03 5.6256276e-03 4.4224155e-03 3.0861840e-04 1.5326607e-03 2.2893524e-03 5.4845532e-04 1.9363566e-03 1.1483728e-03 6.7060376e-05 4.8890125e-04 9.7136579e-04 1.7676633e-03 1.3375465e-03 2.5372175e-04 4.0846507e-03 8.2917869e-04 1.6077651e-02 8.4460632e-03 5.5177458e-03 7.2638972e-03 9.7215743e-03 7.5597148e-03 1.1218034e-02 6.4234899e-03 8.1582316e-03 8.1119193e-03 4.1520915e-03 5.4624397e-03 4.9276011e-03 1.0142169e-02 1.4194600e-02 8.3023965e-03 4.8434975e-03 6.1466052e-03 1.3239048e-02 6.2447391e-03 6.5902144e-03 9.7724034e-03 8.6919298e-03 3.1454944e-03 6.3952659e-03 3.7622934e-03 3.0039560e-03 4.0045050e-03 8.7466217e-03 2.6847018e-03 5.4026497e-03 2.7900002e-03 9.6269482e-03 2.5687827e-03 8.7444600e-03 5.2241473e-03 1.2215015e-02 5.4919656e-03 4.2251052e-03 3.8716574e-03 8.5147448e-03 4.9424074e-03 8.4460632e-03 8.4294916e-03 9.9304629e-03 6.0757927e-03 5.1533590e-03 4.4115457e-03 1.1061881e-02 6.6918878e-03 4.2077380e-03 2.8084601e-04 8.0255307e-03 1.0009734e-03 2.4531898e-03 2.5259126e-03 4.7784400e-04 2.9272193e-03 6.4204523e-03 2.4613181e-03 8.1446100e-04 2.5131313e-03 4.2288269e-03 2.3479589e-03 2.4787654e-03 2.3219118e-03 3.4016339e-03 1.1923522e-03 3.6609993e-04 5.5309499e-03 2.7130308e-03 3.6876750e-03 2.1157012e-03 3.5197143e-03 1.1211667e-03 5.0040735e-04 1.2581494e-03 6.2453424e-03 4.9316252e-04 1.0956505e-03 1.9034882e-03 5.6262931e-04 2.1623642e-03 2.7381233e-03 6.2275542e-04 9.1647472e-04 4.3469066e-04 1.6016964e-03 5.5073116e-03 6.0170029e-04 8.2861929e-03 4.5965840e-03 4.2124912e-03 4.7715416e-03 5.5699315e-03 7.3813594e-03 5.3659110e-03 7.1660104e-03 8.8786575e-03 2.9267242e-03 8.0445420e-04 4.1635703e-03 2.8364579e-03 6.3397911e-03 7.3211567e-03 2.8639086e-03 2.8974680e-03 2.8626073e-03 1.3776826e-02 8.0525608e-03 2.9632152e-03 4.1293440e-03 9.9396499e-03 2.4124505e-03 2.4179141e-03 3.2613675e-03 1.4673422e-03 9.3714862e-04 5.6839580e-03 4.1506352e-03 6.7187933e-03 1.3083992e-03 6.0861850e-03 2.6486423e-03 8.7285257e-03 5.1099460e-03 4.7657924e-03 2.6722144e-03 7.9417630e-04 1.8862622e-03 4.0883053e-03 2.3510235e-03 4.5965840e-03 4.1923613e-03 4.1490574e-03 2.9383782e-03 4.7171745e-03 1.8344857e-03 3.7646957e-03 2.4446440e-03 3.4307781e-03 6.9180223e-04 1.9272484e-03 5.2229584e-03 1.7410764e-03 5.5262484e-03 1.3512624e-03 1.2930220e-03 5.9265821e-04 7.2336834e-03 1.6622228e-03 1.4643809e-03 1.4579196e-03 1.0665934e-03 1.2486050e-03 4.3399433e-05 1.3778574e-03 2.2390179e-03 3.1206766e-03 3.9982611e-04 7.0342073e-04 1.4793565e-03 4.5495005e-03 7.4063181e-03 6.9037801e-03 1.1215419e-03 3.4551859e-04 4.3750592e-03 1.0242073e-03 2.4559606e-03 2.2544417e-03 6.9180223e-04 1.5393939e-03 1.8990091e-03 3.7039856e-03 2.8782332e-03 1.2534261e-03 6.1345149e-03 2.1154370e-03 1.6389445e-02 8.1714731e-03 4.6971245e-03 6.9480013e-03 9.2874957e-03 6.0735661e-03 1.1557043e-02 5.0795005e-03 6.2695782e-03 8.5942939e-03 4.8981354e-03 4.6386510e-03 4.5456138e-03 9.3051239e-03 1.4039626e-02 8.8459552e-03 4.7683489e-03 6.5513763e-03 1.0462868e-02 4.3969158e-03 6.5113261e-03 1.0098096e-02 6.6155737e-03 2.7131547e-03 6.8044529e-03 3.4785563e-03 3.0414918e-03 4.6799978e-03 7.9508909e-03 2.0423594e-03 3.8146379e-03 3.3303959e-03 8.7946774e-03 2.4271150e-03 7.7798359e-03 3.9016462e-03 1.3149131e-02 5.7850137e-03 5.0633237e-03 3.7856360e-03 8.3083336e-03 4.9921128e-03 8.1714731e-03 8.2786520e-03 1.0189399e-02 5.8912773e-03 3.9649361e-03 4.4771942e-03 1.2268673e-02 7.3587328e-03 6.5787312e-03 1.3233357e-03 1.1455828e-03 1.2416155e-03 1.4002549e-03 2.1211045e-03 6.2047866e-03 1.6380421e-03 2.0343353e-03 1.2366343e-03 4.6737502e-03 2.4960613e-03 1.3635919e-03 1.1869805e-03 2.7420392e-03 1.3311387e-03 5.4030547e-04 3.4496369e-03 1.8301986e-03 2.4543913e-03 1.0360218e-03 4.9823495e-03 2.4358984e-03 7.7653049e-04 6.6085729e-04 5.4666528e-03 3.6251211e-04 9.0034369e-04 2.5308489e-03 6.8325669e-04 1.3068439e-03 1.4466712e-03 7.0328570e-04 7.2909544e-04 3.3427965e-04 8.2319726e-04 3.3236658e-03 2.7101948e-04 1.1415097e-02 6.6763180e-03 5.5975222e-03 6.7637660e-03 7.8425097e-03 9.1076654e-03 7.9396677e-03 8.6849227e-03 1.0537825e-02 4.7340864e-03 1.6981356e-03 5.5204544e-03 4.0238168e-03 8.5090240e-03 9.7837063e-03 4.5130967e-03 4.3644743e-03 4.6397935e-03 1.5929686e-02 9.2384755e-03 4.4702915e-03 6.2885333e-03 1.1607431e-02 3.1082253e-03 4.1080254e-03 4.4476701e-03 2.1448891e-03 1.9903866e-03 7.7241343e-03 4.8610346e-03 7.8308568e-03 2.2349743e-03 8.1986845e-03 3.4906235e-03 1.0785042e-02 6.0455598e-03 7.1678534e-03 4.3085958e-03 1.8327850e-03 2.7372439e-03 5.8248161e-03 2.8157351e-03 6.6763180e-03 6.1912806e-03 6.1165743e-03 3.9060199e-03 5.6854050e-03 2.9112999e-03 5.9286549e-03 4.2459890e-03 4.7261943e-03 7.5564450e-03 3.0589295e-03 9.9138151e-03 2.5436162e-03 2.3081715e-03 1.8344588e-03 1.2327595e-02 2.9913447e-03 3.4407458e-03 3.5166367e-03 2.2912956e-03 2.5985157e-03 1.0536407e-03 4.0148201e-03 5.3033668e-03 3.3636231e-03 1.5135209e-03 1.3379167e-03 2.9432336e-03 8.2210569e-03 1.2378534e-02 1.1256214e-02 3.0883819e-03 6.3877121e-04 7.6741777e-03 3.3091271e-03 5.2745018e-03 5.1312091e-03 2.0746888e-03 2.6973394e-03 4.6616800e-03 6.5765736e-03 5.8096774e-03 2.9820979e-03 7.3187243e-03 4.6783172e-03 2.3410581e-02 1.3144529e-02 8.3391705e-03 1.1305315e-02 1.4457465e-02 9.3485129e-03 1.7555848e-02 7.8838497e-03 9.1347689e-03 1.4043414e-02 9.2390419e-03 8.2683227e-03 8.4558564e-03 1.4291672e-02 2.0441558e-02 1.4348706e-02 8.7091477e-03 1.1171095e-02 1.3577182e-02 6.6301175e-03 1.1191012e-02 1.5865943e-02 9.2559664e-03 5.8683930e-03 1.1694065e-02 6.7001372e-03 6.5334334e-03 8.9560518e-03 1.2574664e-02 4.2523471e-03 6.2343724e-03 6.8791672e-03 1.3649249e-02 5.2782119e-03 1.1089361e-02 6.8777652e-03 1.9771053e-02 1.0157447e-02 9.4893989e-03 7.5319640e-03 1.3411350e-02 8.8625592e-03 1.3144529e-02 1.3390278e-02 1.5951074e-02 1.0173619e-02 7.0905829e-03 8.5554531e-03 1.8741319e-02 1.2400067e-02 4.2985441e-03 2.4763265e-03 1.0193789e-03 1.4380091e-03 3.6495663e-03 1.2003820e-03 2.0584975e-03 2.4845352e-03 1.6034070e-03 3.3059261e-04 1.8671770e-03 2.0088877e-03 1.4284810e-03 4.3098388e-04 2.3514979e-04 5.2698749e-03 1.3824059e-03 2.2360501e-03 1.8501267e-03 1.6468815e-03 1.8319470e-03 2.4154150e-03 8.5423988e-04 3.3259702e-03 1.3633177e-03 3.9750527e-04 2.1357310e-04 1.4089375e-04 1.2605369e-03 2.3906980e-03 1.0696291e-04 1.0906412e-03 5.9076498e-04 1.1304618e-03 7.2732511e-03 6.4012947e-04 8.9598694e-03 3.8314732e-03 2.5915433e-03 2.9763427e-03 4.7600376e-03 4.3053173e-03 5.3266981e-03 3.6190516e-03 5.2272723e-03 3.6549051e-03 1.5047575e-03 2.5888356e-03 2.1634980e-03 5.5055672e-03 8.5050147e-03 4.0885074e-03 1.5151975e-03 1.9844585e-03 9.6042351e-03 4.2899092e-03 2.9941597e-03 4.6609860e-03 5.8520656e-03 1.4443089e-03 2.3092039e-03 1.1621771e-03 1.1296414e-03 1.1169479e-03 4.4204603e-03 1.2641450e-03 3.4620250e-03 3.4971048e-04 5.1226200e-03 6.2752047e-04 4.7853928e-03 3.3731886e-03 6.3070664e-03 1.6662334e-03 1.2828407e-03 1.6597311e-03 4.4397739e-03 3.3292201e-03 3.8314732e-03 3.8867782e-03 5.1669823e-03 3.5417199e-03 3.1734550e-03 1.6842820e-03 5.5385508e-03 2.3108610e-03 1.0105990e-03 4.7523926e-03 2.9847642e-03 9.7105522e-03 2.6675945e-03 5.8410645e-03 1.0628436e-03 8.9773521e-03 5.4199214e-03 1.6519449e-03 1.3847497e-03 4.5932482e-03 4.2245140e-03 3.0278103e-03 1.7095869e-03 2.8675709e-03 2.8000554e-03 1.2095222e-03 1.0633409e-02 6.3588443e-03 2.3146826e-03 1.8579783e-03 7.7501487e-03 1.5279559e-03 3.0361588e-03 6.2292475e-03 2.9996444e-03 2.2092276e-03 1.2670329e-03 3.0585508e-03 1.9631739e-03 1.9448597e-03 1.6026176e-03 7.3057463e-04 1.6268750e-03 1.9360609e-02 1.3271536e-02 1.1367917e-02 1.3356276e-02 1.4871782e-02 1.6028245e-02 1.4980963e-02 1.5218503e-02 1.7630002e-02 1.0242398e-02 5.4451367e-03 1.1236806e-02 9.0841292e-03 1.5577818e-02 1.6739427e-02 9.6435556e-03 9.8426714e-03 1.0341095e-02 2.4442578e-02 1.5482723e-02 9.8424496e-03 1.2530745e-02 1.8870958e-02 7.3605563e-03 9.5556413e-03 9.6316193e-03 6.0281749e-03 6.1257342e-03 1.4596779e-02 9.4938425e-03 1.3739836e-02 6.3629397e-03 1.5181016e-02 7.9755348e-03 1.8111593e-02 1.1305790e-02 1.3376686e-02 9.8378125e-03 5.8135644e-03 6.9819428e-03 1.1631750e-02 6.1445613e-03 1.3271536e-02 1.2517306e-02 1.1992997e-02 8.3045136e-03 1.0906252e-02 7.4644491e-03 1.1536762e-02 9.7524154e-03 4.5852486e-03 9.8595493e-04 5.1231602e-03 5.3641159e-04 6.1291344e-03 9.0289414e-06 4.9719488e-03 2.7623691e-03 1.0135570e-04 4.9630072e-05 1.4573369e-03 2.2050307e-03 1.9067713e-03 6.5935996e-04 5.5655703e-04 4.9349156e-04 5.3910141e-05 7.6334144e-03 6.4541501e-03 3.6168243e-03 4.5600228e-04 3.3337204e-03 1.8551457e-03 1.2329546e-03 3.8576017e-03 1.8263928e-03 3.4082293e-04 2.8939589e-05 1.6992773e-03 1.7320592e-03 1.4340517e-03 2.7567571e-04 1.5103910e-03 8.5202938e-04 1.8535044e-02 1.1041503e-02 8.1659733e-03 1.0422568e-02 1.2515324e-02 1.1445689e-02 1.3674148e-02 1.0415137e-02 1.2394915e-02 9.4870366e-03 4.7984401e-03 8.0611256e-03 6.7876377e-03 1.2924053e-02 1.5886474e-02 9.2445598e-03 7.3109344e-03 8.4847047e-03 1.8293716e-02 1.0175519e-02 8.2306422e-03 1.1561315e-02 1.3225081e-02 4.8091709e-03 8.1976852e-03 6.4746796e-03 4.2076714e-03 5.1007444e-03 1.1683626e-02 5.5527590e-03 8.9342726e-03 4.4973686e-03 1.2442734e-02 4.9468838e-03 1.3343592e-02 7.6599210e-03 1.3497447e-02 7.8132331e-03 5.1004820e-03 5.1592495e-03 1.0155636e-02 5.1788357e-03 1.1041503e-02 1.0666367e-02 1.1283660e-02 6.9627068e-03 7.4793969e-03 5.8082013e-03 1.1981463e-02 8.5614846e-03 3.8688652e-03 7.5179133e-03 3.7407669e-03 3.1676786e-04 4.6173801e-03 4.2799166e-03 2.2501511e-03 4.2413983e-03 4.2204030e-03 4.6109813e-03 1.7937707e-03 8.2328963e-04 8.3021324e-03 4.1326900e-03 5.3767829e-03 3.8579826e-03 2.3734756e-03 1.6523409e-04 8.8835369e-04 2.5288831e-03 7.6911436e-03 1.1035999e-03 1.9572002e-03 1.3626197e-03 8.4501724e-04 3.6117590e-03 4.7013989e-03 1.0056440e-03 1.3674472e-03 9.9055737e-04 2.9505217e-03 8.8795046e-03 1.5067108e-03 6.0099517e-03 3.2148697e-03 3.5475051e-03 3.0620571e-03 4.0315627e-03 6.0415844e-03 3.4044063e-03 5.7826017e-03 7.5784861e-03 2.1065729e-03 8.8233953e-04 3.5528527e-03 2.5792431e-03 5.1245213e-03 6.2842876e-03 2.4095844e-03 1.8446522e-03 1.4167723e-03 1.2151930e-02 7.2262167e-03 2.4839286e-03 2.9019075e-03 8.5383029e-03 2.6181282e-03 1.4064800e-03 2.3288946e-03 1.7187301e-03 6.4203056e-04 4.4078979e-03 3.6116427e-03 6.0606537e-03 7.3312980e-04 4.8772717e-03 2.0701887e-03 6.3495233e-03 5.1782781e-03 3.3738477e-03 1.3922890e-03 5.6378115e-04 2.0528392e-03 3.5526857e-03 3.4142665e-03 3.2148697e-03 3.0384066e-03 3.3899600e-03 3.3852248e-03 4.7610217e-03 1.6939002e-03 2.6027995e-03 1.1563987e-03 4.9937739e-03 2.8043563e-04 6.0192505e-03 1.1047123e-03 3.7340869e-03 9.3891763e-04 5.7693223e-04 8.5742923e-04 1.1063653e-03 2.1518410e-03 1.7873418e-03 1.8443813e-03 4.7874237e-04 4.0562362e-04 6.3806313e-04 5.6896950e-03 5.3990383e-03 4.0838955e-03 7.6270784e-04 3.0255235e-03 1.8702287e-03 1.2256654e-03 1.9416278e-03 1.1863062e-03 4.8488269e-04 7.2384993e-04 1.1782779e-03 1.1397261e-03 1.1824844e-03 4.5012569e-04 4.3416102e-03 9.3532010e-04 1.7514227e-02 9.8600607e-03 7.1630436e-03 8.1480968e-03 1.1292909e-02 9.1710206e-03 1.2255610e-02 7.6051240e-03 9.9403988e-03 9.4620221e-03 5.2227781e-03 7.1398046e-03 6.5861173e-03 1.2201906e-02 1.6541006e-02 9.8696183e-03 5.7022093e-03 6.6806000e-03 1.5779735e-02 7.9447125e-03 8.2548534e-03 1.1179561e-02 1.0455124e-02 4.7301378e-03 7.3678164e-03 4.4876356e-03 4.3705173e-03 4.8557182e-03 1.0520487e-02 3.4331839e-03 7.0769130e-03 3.1675776e-03 1.1611243e-02 3.1862426e-03 9.1961094e-03 7.4495828e-03 1.3429738e-02 6.1002834e-03 5.0525215e-03 5.4421147e-03 1.0502545e-02 7.0546448e-03 9.8600607e-03 9.9581080e-03 1.1708746e-02 8.2306703e-03 7.3218404e-03 5.8241089e-03 1.2089443e-02 7.3253099e-03 3.2913459e-03 8.4499731e-03 4.8569537e-03 8.0216351e-04 3.5049401e-03 4.1209949e-03 4.1951632e-03 1.4430192e-03 2.1132422e-03 3.9581533e-03 7.7606295e-03 2.8139659e-03 3.8070595e-03 4.7702994e-03 3.8227367e-03 9.3108167e-03 1.0381275e-02 3.5346820e-03 5.2926972e-04 8.0977990e-03 2.5864687e-03 3.9952884e-03 4.6152713e-03 3.2808967e-03 5.0209478e-03 3.9992007e-03 7.6245140e-03 5.9395645e-03 4.2477403e-03 1.1072543e-02 4.9514063e-03 1.3733574e-02 6.2131974e-03 2.8230097e-03 5.6455623e-03 6.8638978e-03 3.4892544e-03 9.9148605e-03 3.2690483e-03 3.3086076e-03 7.4423917e-03 5.1197423e-03 2.7541244e-03 3.1219477e-03 6.1841097e-03 1.0726676e-02 7.6421585e-03 4.1594455e-03 6.2994749e-03 5.6115691e-03 2.0176931e-03 4.9542156e-03 8.4395555e-03 3.5502322e-03 1.8230265e-03 6.1478670e-03 3.1652703e-03 2.7049802e-03 4.9489115e-03 5.3042297e-03 1.9463142e-03 1.6986432e-03 4.1722516e-03 5.8173968e-03 2.6602408e-03 6.4952123e-03 1.5042394e-03 1.1842312e-02 5.5188608e-03 5.4735640e-03 2.9735027e-03 6.0829501e-03 4.0306925e-03 6.2131974e-03 6.3472325e-03 8.2596987e-03 4.2451973e-03 1.6828229e-03 3.7151898e-03 1.1574575e-02 7.1331719e-03 5.4811265e-03 5.5565221e-04 2.6852359e-03 1.0154324e-03 1.7597013e-04 3.1956114e-04 3.9523878e-04 1.2388706e-03 1.2655903e-03 1.6805675e-03 3.2406061e-05 1.6284558e-04 3.0055489e-04 4.9827743e-03 5.3992645e-03 4.0722394e-03 2.5388663e-04 1.8315484e-03 2.0093416e-03 5.5333384e-04 1.9333559e-03 1.0417210e-03 4.5631651e-05 3.9213732e-04 8.9628584e-04 1.5141190e-03 1.1540120e-03 1.6596111e-04 3.8108192e-03 6.9068728e-04 1.6110144e-02 8.5829426e-03 5.7514563e-03 7.4081873e-03 9.8865471e-03 7.9109476e-03 1.1246146e-02 6.7496818e-03 8.5805974e-03 8.1149529e-03 4.1088457e-03 5.6967153e-03 5.0837880e-03 1.0378372e-02 1.4309813e-02 8.2945922e-03 4.9454249e-03 6.1635386e-03 1.3817928e-02 6.6600542e-03 6.6926476e-03 9.8029996e-03 9.1474450e-03 3.3107453e-03 6.4113962e-03 3.9012077e-03 3.0861181e-03 3.9737498e-03 8.9756426e-03 2.8871345e-03 5.7720900e-03 2.7832463e-03 9.8623531e-03 2.6826772e-03 9.0033513e-03 5.5466456e-03 1.2137789e-02 5.5287165e-03 4.1655510e-03 3.9755943e-03 8.6395601e-03 5.0222445e-03 8.5829426e-03 8.5444862e-03 9.9728516e-03 6.1969528e-03 5.4521508e-03 4.4886104e-03 1.0936532e-02 6.6624227e-03 6.0879321e-03 5.2071133e-03 3.8816096e-03 5.8831678e-03 5.7002681e-03 6.1926865e-03 2.4416494e-03 1.5149583e-03 1.0539677e-02 5.8224280e-03 7.4292172e-03 5.4190952e-03 2.7168224e-03 2.8895677e-04 1.2473682e-03 3.7477469e-03 9.3088565e-03 2.0944009e-03 2.9293124e-03 2.5720420e-03 1.8897161e-03 5.1660356e-03 6.4102501e-03 2.0138936e-03 2.7413840e-03 2.0664916e-03 4.4338085e-03 1.0353410e-02 2.6118482e-03 4.2150650e-03 2.5222817e-03 3.4164904e-03 3.0582580e-03 3.1593359e-03 6.2318287e-03 2.3862805e-03 6.4839490e-03 7.9101598e-03 1.0920878e-03 5.4699759e-04 3.4046237e-03 2.2291279e-03 4.0543858e-03 4.2004727e-03 1.1970351e-03 2.0117915e-03 1.4112028e-03 1.1968526e-02 7.9088029e-03 1.7082776e-03 1.7239090e-03 9.0238817e-03 2.6874577e-03 9.4975956e-04 2.9722514e-03 1.7009640e-03 5.1343341e-04 3.7130353e-03 4.7806054e-03 6.6267337e-03 1.3220831e-03 3.9245747e-03 2.9403206e-03 7.2757164e-03 5.0266602e-03 1.8157078e-03 1.4701768e-03 3.5054511e-04 1.7883207e-03 2.3616946e-03 2.6617941e-03 2.5222817e-03 2.1723972e-03 1.9530095e-03 2.4533627e-03 4.5559375e-03 1.3609168e-03 1.2016297e-03 8.2861882e-04 4.8139327e-03 2.8209366e-03 1.1052349e-04 3.7194090e-05 1.3880093e-03 2.0960687e-03 1.8746430e-03 7.3307377e-04 5.4600497e-04 5.2531656e-04 7.7399437e-05 7.5260309e-03 6.5039124e-03 3.6958100e-03 4.3794778e-04 3.1691974e-03 1.9589162e-03 1.1747018e-03 3.8950000e-03 1.8610625e-03 3.3531102e-04 5.6607375e-05 1.7087501e-03 1.8728452e-03 1.4997100e-03 3.0123700e-04 1.5391826e-03 8.8147902e-04 1.8294246e-02 1.0829571e-02 7.9341800e-03 1.0299926e-02 1.2275737e-02 1.1220530e-02 1.3508784e-02 1.0262914e-02 1.2147087e-02 9.2823036e-03 4.6531594e-03 7.8245831e-03 6.5524640e-03 1.2610156e-02 1.5512672e-02 9.0061844e-03 7.1992659e-03 8.4158356e-03 1.7921554e-02 9.9492079e-03 7.9854978e-03 1.1342596e-02 1.2982212e-02 4.5954373e-03 8.0580657e-03 6.3922041e-03 4.0251341e-03 4.9895677e-03 1.1418706e-02 5.4787432e-03 8.7107499e-03 4.4679304e-03 1.2142235e-02 4.8867636e-03 1.3297934e-02 7.3458933e-03 1.3299484e-02 7.7358341e-03 4.9917221e-03 4.9413674e-03 9.8545070e-03 4.8770063e-03 1.0829571e-02 1.0435652e-02 1.1007902e-02 6.6474718e-03 7.1727397e-03 5.6106947e-03 1.1815640e-02 8.4729744e-03 1.5166983e-03 3.8470717e-03 4.0414722e-03 1.3464004e-03 1.0521554e-03 2.2256122e-03 8.2355212e-03 2.4972654e-03 3.6758320e-03 4.2952040e-03 1.2466978e-03 5.4237685e-03 7.4233145e-03 2.7434894e-03 1.3920410e-03 5.6705580e-03 1.6006987e-03 1.4795623e-03 2.5504964e-03 2.8217649e-03 4.8039535e-03 2.1716992e-03 5.1387834e-03 3.8511647e-03 3.4066344e-03 1.1638730e-02 3.4223746e-03 9.4998939e-03 3.3659956e-03 1.1438717e-03 2.4828929e-03 3.9653407e-03 1.5796312e-03 5.9993003e-03 1.2126234e-03 1.7377370e-03 4.6402796e-03 3.0884931e-03 1.1351744e-03 1.5562166e-03 3.9244608e-03 8.1556610e-03 5.1855221e-03 1.5226783e-03 2.8914073e-03 4.3431210e-03 9.2550137e-04 2.9272754e-03 5.3162687e-03 2.0104865e-03 7.9177243e-04 3.1975522e-03 8.1317449e-04 1.3785091e-03 2.5714969e-03 2.9511423e-03 2.8161529e-04 5.9442405e-04 1.5991249e-03 3.5615321e-03 5.8433150e-04 3.1011353e-03 9.7656296e-04 8.0983113e-03 2.3700003e-03 3.0461348e-03 1.6450583e-03 4.0872867e-03 3.5603497e-03 3.3659956e-03 3.6518311e-03 5.6844228e-03 3.2852236e-03 1.0303385e-03 1.9470044e-03 7.8778855e-03 3.7000111e-03 1.9300708e-03 2.2574717e-03 1.1284358e-03 1.0646979e-03 1.0312076e-03 5.0621817e-03 1.2060164e-03 1.8000639e-03 2.0650564e-03 2.1487796e-03 3.2333878e-03 4.0195479e-03 1.2411974e-03 2.7183002e-03 2.2726383e-03 8.5094120e-04 2.4056963e-04 6.3782113e-04 1.2937611e-03 2.4889893e-03 5.9722053e-04 1.5621785e-03 1.2375553e-03 1.3358412e-03 8.1240216e-03 1.2311022e-03 1.1517636e-02 5.2909227e-03 3.4509195e-03 3.7153220e-03 6.3154320e-03 4.5407961e-03 7.2027738e-03 3.4054800e-03 5.2043334e-03 5.7490488e-03 3.1731637e-03 3.4719309e-03 3.4347027e-03 7.1701181e-03 1.1379612e-02 6.4467389e-03 2.2692649e-03 2.9960956e-03 9.7363741e-03 4.0178908e-03 4.7338729e-03 6.7982603e-03 5.6061060e-03 2.3699712e-03 3.8192855e-03 1.4133848e-03 2.2956056e-03 2.5088406e-03 5.7314562e-03 9.5862581e-04 3.4107092e-03 9.7091810e-04 6.6778472e-03 7.6184177e-04 4.3247639e-03 4.2032521e-03 8.9746527e-03 2.5881501e-03 2.8171688e-03 3.0086239e-03 6.5256052e-03 5.3225794e-03 5.2909227e-03 5.5860698e-03 7.6013097e-03 5.5376614e-03 4.0938335e-03 3.1000848e-03 8.1789436e-03 3.7376243e-03 3.2267663e-05 8.3707549e-04 1.6905622e-03 1.5718936e-03 9.3681209e-04 1.8762095e-04 1.9684682e-04 4.1277465e-05 6.4440482e-03 6.0518000e-03 3.8422414e-03 2.6274935e-04 2.4665214e-03 1.9088192e-03 8.3864343e-04 2.9786928e-03 1.4388393e-03 8.3975474e-05 5.3416679e-05 1.2919166e-03 1.6159093e-03 1.2740163e-03 1.2266042e-04 2.3692761e-03 7.1636545e-04 1.7563748e-02 9.9676420e-03 7.0439691e-03 9.1049407e-03 1.1370346e-02 9.8276989e-03 1.2673450e-02 8.7383729e-03 1.0632094e-02 8.9036592e-03 4.4721835e-03 6.9588127e-03 5.9822651e-03 1.1786793e-02 1.5215597e-02 8.8361414e-03 6.2553172e-03 7.4909328e-03 1.6226352e-02 8.5173085e-03 7.5296182e-03 1.0823312e-02 1.1340207e-02 4.0626163e-03 7.4274805e-03 5.3041231e-03 3.6467217e-03 4.5925766e-03 1.0472609e-02 4.2980846e-03 7.4369871e-03 3.7248522e-03 1.1287932e-02 3.8978058e-03 1.1428778e-02 6.6149115e-03 1.2975959e-02 6.8214468e-03 4.6824073e-03 4.5741646e-03 9.4691004e-03 5.0187206e-03 9.9676420e-03 9.7385546e-03 1.0722589e-02 6.5565379e-03 6.4801586e-03 5.1854796e-03 1.1596572e-02 7.7633670e-03 9.9158601e-04 1.6686143e-03 1.5457871e-03 9.5071878e-04 3.0701864e-04 3.6362811e-04 4.2548991e-05 6.6288818e-03 6.0417331e-03 3.6564704e-03 2.5834714e-04 2.6458983e-03 1.8764428e-03 8.4820574e-04 3.2578450e-03 1.5209238e-03 1.5090465e-04 6.1935165e-05 1.3587649e-03 1.7158299e-03 1.3006818e-03 1.6628415e-04 2.0535582e-03 7.1188329e-04 1.7324639e-02 9.9147481e-03 7.0600417e-03 9.2840701e-03 1.1302120e-02 1.0064078e-02 1.2589616e-02 9.1080261e-03 1.0920912e-02 8.6425556e-03 4.2412792e-03 6.9624194e-03 5.8567924e-03 1.1639501e-02 1.4739829e-02 8.4651679e-03 6.3684651e-03 7.5923014e-03 1.6475338e-02 8.8252851e-03 7.3097093e-03 1.0588424e-02 1.1694482e-02 3.9807447e-03 7.3452100e-03 5.5353092e-03 3.5104587e-03 4.4753907e-03 1.0430724e-02 4.6273816e-03 7.6755144e-03 3.8450588e-03 1.1170257e-02 4.1281664e-03 1.1986478e-02 6.5423939e-03 1.2634016e-02 6.9257811e-03 4.5247923e-03 4.3922796e-03 9.1553155e-03 4.5676871e-03 9.9147481e-03 9.5995789e-03 1.0353234e-02 6.1627903e-03 6.3902007e-03 5.0235520e-03 1.1247115e-02 7.7428181e-03 9.7824889e-04 1.6624661e-03 3.0059873e-03 2.5249972e-04 6.2343657e-04 1.1719366e-03 4.0275459e-03 6.3575546e-03 5.8712921e-03 7.5872446e-04 5.6741436e-04 3.5908637e-03 6.4996051e-04 1.9776689e-03 1.6840088e-03 4.6219818e-04 1.2917587e-03 1.3743184e-03 3.0187893e-03 2.2373182e-03 9.0828703e-04 5.7426890e-03 1.5775049e-03 1.5168309e-02 7.4080646e-03 4.2538993e-03 6.3033105e-03 8.5100385e-03 5.7899432e-03 1.0520014e-02 4.8624768e-03 6.1169570e-03 7.6384090e-03 4.1091153e-03 4.1981402e-03 3.9923041e-03 8.6172736e-03 1.2997087e-02 7.8717119e-03 4.1667776e-03 5.7584992e-03 1.0387455e-02 4.3546484e-03 5.7746712e-03 9.1054055e-03 6.5301937e-03 2.3054088e-03 5.9614728e-03 3.0379183e-03 2.5067124e-03 3.9126904e-03 7.3239330e-03 1.8370981e-03 3.7021481e-03 2.7152943e-03 8.1304066e-03 2.0347479e-03 7.4172182e-03 3.6502880e-03 1.1915038e-02 5.0530642e-03 4.2458688e-03 3.2213237e-03 7.5116191e-03 4.3860606e-03 7.4080646e-03 7.4726409e-03 9.2071058e-03 5.2421868e-03 3.6675370e-03 3.8308993e-03 1.1024145e-02 6.4520833e-03 3.0133930e-04 5.1037905e-03 1.2017859e-03 2.1951761e-03 1.7686888e-03 1.8224411e-03 2.8788865e-03 3.1740291e-03 7.0420689e-04 2.2350252e-03 2.1605967e-03 1.6074282e-04 9.9312132e-04 6.3910600e-04 1.1068342e-03 2.2522084e-03 4.2563220e-04 2.1383582e-03 1.1781524e-03 1.1965920e-03 6.6826197e-03 8.9701315e-04 8.8374021e-03 3.5249181e-03 1.9006375e-03 3.2674819e-03 4.3522253e-03 3.8602822e-03 5.5032387e-03 3.5903574e-03 4.6280025e-03 3.2207897e-03 1.0860964e-03 1.8528751e-03 1.3287556e-03 4.5637650e-03 7.2040689e-03 3.3452381e-03 1.6438459e-03 2.5042800e-03 8.4078534e-03 3.6412189e-03 2.1968551e-03 4.2884610e-03 5.3072049e-03 5.5962180e-04 2.2924485e-03 1.3972917e-03 4.1775124e-04 1.0120628e-03 3.7672964e-03 1.3901921e-03 2.7802443e-03 7.8074497e-04 4.2513803e-03 8.6439557e-04 5.7017672e-03 1.9558095e-03 6.1305769e-03 2.0764524e-03 1.1750038e-03 7.8121442e-04 3.3536027e-03 1.6840705e-03 3.5249181e-03 3.4031938e-03 4.3169071e-03 2.0323666e-03 1.8104373e-03 1.0313169e-03 5.4663596e-03 2.6797009e-03 4.7017751e-03 1.4050521e-03 2.2967640e-03 1.4490557e-03 2.3196093e-03 1.7015479e-03 1.5767700e-03 5.5713099e-04 3.7392678e-03 8.9324098e-04 2.5002761e-04 8.6482380e-04 1.3585049e-04 1.1239692e-03 1.9761479e-03 8.2927740e-05 9.6014531e-04 3.4965263e-04 8.9552257e-04 5.7645956e-03 3.1975366e-04 8.7743211e-03 4.0156462e-03 2.9085611e-03 3.7265804e-03 4.9611690e-03 5.3200137e-03 5.4291747e-03 4.9090444e-03 6.4181467e-03 3.1699225e-03 9.0457257e-04 2.8707306e-03 2.0343345e-03 5.5511033e-03 7.6886258e-03 3.2962764e-03 1.9800100e-03 2.4033904e-03 1.0905558e-02 5.4434884e-03 2.6411651e-03 4.3219947e-03 7.2333465e-03 1.3483652e-03 2.2580657e-03 1.9023537e-03 8.2889320e-04 8.3511491e-04 4.6996589e-03 2.2192064e-03 4.3941392e-03 6.5409726e-04 5.2313685e-03 1.2879740e-03 6.5768476e-03 3.4674432e-03 5.6567012e-03 2.0849532e-03 8.7522015e-04 1.2925162e-03 3.9107304e-03 2.2116014e-03 4.0156462e-03 3.8265815e-03 4.4724719e-03 2.6832503e-03 3.2108081e-03 1.3825243e-03 4.7909587e-03 2.3944351e-03 1.6956470e-03 1.0108236e-03 9.3414345e-04 1.2241588e-02 1.0680036e-02 6.4457112e-03 2.1113831e-03 4.8851719e-03 3.9417362e-03 3.5304281e-03 6.9169294e-03 4.3255103e-03 1.5489174e-03 5.9380000e-04 4.2050996e-03 3.5612837e-03 3.5981180e-03 1.5473563e-03 1.0469113e-03 2.7881158e-03 2.6084642e-02 1.6915515e-02 1.3046936e-02 1.5813114e-02 1.8728660e-02 1.6622622e-02 2.0172358e-02 1.5047933e-02 1.7480656e-02 1.5121187e-02 8.9988271e-03 1.2921567e-02 1.1505555e-02 1.9202447e-02 2.2948431e-02 1.4807179e-02 1.1989561e-02 1.3522703e-02 2.4353472e-02 1.4534512e-02 1.3492376e-02 1.7699736e-02 1.8260951e-02 8.7551977e-03 1.3397972e-02 1.0605977e-02 8.1065177e-03 9.3705995e-03 1.7600384e-02 8.9170386e-03 1.3238432e-02 8.2394556e-03 1.8591466e-02 8.5466794e-03 1.8371580e-02 1.2139358e-02 2.0015536e-02 1.2667833e-02 9.3753580e-03 9.4099897e-03 1.5925767e-02 9.2577085e-03 1.6915515e-02 1.6530549e-02 1.7369847e-02 1.1736690e-02 1.2003814e-02 1.0321540e-02 1.8104120e-02 1.3766972e-02 1.5141485e-04 3.6186411e-04 5.0640473e-03 5.8846843e-03 4.5372473e-03 2.9889995e-04 1.4503455e-03 2.4048820e-03 5.6473356e-04 2.1694139e-03 1.2760726e-03 5.0001009e-05 4.2762331e-04 1.0752642e-03 1.9285330e-03 1.4459840e-03 2.6007132e-04 3.8461838e-03 8.8079628e-04 1.6321310e-02 8.6239000e-03 5.6156570e-03 7.5378743e-03 9.9017513e-03 7.7506746e-03 1.1476827e-02 6.6644304e-03 8.3385285e-03 8.2160708e-03 4.1911289e-03 5.5513041e-03 4.9688023e-03 1.0246372e-02 1.4224144e-02 8.3426053e-03 5.0473242e-03 6.4114140e-03 1.3391748e-02 6.3862951e-03 6.6425216e-03 9.9154668e-03 8.8906649e-03 3.1510729e-03 6.5653256e-03 3.9767727e-03 3.0148559e-03 4.1087786e-03 8.8891438e-03 2.8692574e-03 5.5219515e-03 2.9822205e-03 9.7392578e-03 2.7559926e-03 9.1569917e-03 5.1978118e-03 1.2376315e-02 5.7353587e-03 4.3202502e-03 3.8711781e-03 8.5388416e-03 4.7844327e-03 8.6239000e-03 8.5671038e-03 9.9856257e-03 5.9819812e-03 5.1316831e-03 4.4550423e-03 1.1213210e-02 6.9205612e-03 3.8131057e-04 6.7888158e-03 7.3363003e-03 5.3694436e-03 6.8516623e-04 1.8924051e-03 2.8895525e-03 1.2584203e-03 3.1106561e-03 1.9851820e-03 2.2993360e-04 3.0705767e-04 1.8065799e-03 2.2681792e-03 1.9895114e-03 4.4561501e-04 3.2950230e-03 1.3536253e-03 1.9442033e-02 1.0986408e-02 7.5586116e-03 9.6139706e-03 1.2432885e-02 9.8077912e-03 1.4077548e-02 8.4254038e-03 1.0370813e-02 1.0478515e-02 5.7856689e-03 7.4894276e-03 6.8497491e-03 1.2864462e-02 1.7242156e-02 1.0603978e-02 6.8089917e-03 8.2980029e-03 1.5941696e-02 8.0862824e-03 8.7712331e-03 1.2406133e-02 1.0894930e-02 4.6791769e-03 8.5829625e-03 5.4740472e-03 4.5017704e-03 5.7029968e-03 1.1309070e-02 4.0232333e-03 7.1989285e-03 4.2761837e-03 1.2289743e-02 4.0060693e-03 1.1002553e-02 7.0316526e-03 1.5021687e-02 7.5484658e-03 5.9167328e-03 5.5390768e-03 1.0934157e-02 6.4525090e-03 1.0986408e-02 1.0943118e-02 1.2502015e-02 7.9589531e-03 6.9802991e-03 6.2208864e-03 1.3663387e-02 8.8999016e-03 6.6062542e-03 5.5827314e-03 3.2137911e-03 2.4648342e-04 3.0882054e-03 1.4855278e-03 8.8238305e-04 3.0312113e-03 1.3048774e-03 1.7074342e-04 4.2409993e-05 1.2055084e-03 1.2882692e-03 1.0204762e-03 8.9780466e-05 2.0467514e-03 5.4370125e-04 1.7241595e-02 9.9776023e-03 7.2970604e-03 9.2205394e-03 1.1400153e-02 1.0313298e-02 1.2454804e-02 9.2538420e-03 1.1254653e-02 8.6534668e-03 4.2479840e-03 7.2108683e-03 6.0807502e-03 1.1908599e-02 1.4998922e-02 8.5435101e-03 6.3304436e-03 7.3992448e-03 1.7031719e-02 9.1810782e-03 7.4786833e-03 1.0592315e-02 1.2037933e-02 4.2382770e-03 7.2750145e-03 5.5074473e-03 3.6741461e-03 4.4146887e-03 1.0634443e-02 4.6823366e-03 8.0104480e-03 3.6842552e-03 1.1424845e-02 4.0945647e-03 1.1857190e-02 7.0169427e-03 1.2513676e-02 6.7752703e-03 4.4459599e-03 4.6022279e-03 9.3965771e-03 4.9465269e-03 9.9776023e-03 9.6902181e-03 1.0478640e-02 6.5221232e-03 6.8355801e-03 5.1573625e-03 1.1079009e-02 7.5518079e-03 2.6192775e-03 5.9216859e-03 4.3727502e-03 5.1296754e-03 5.3199310e-03 2.7670517e-03 1.1371035e-03 2.6020928e-03 5.1188394e-03 7.5382072e-03 2.4476107e-03 5.0616317e-03 3.9293810e-03 5.2302586e-03 1.5095801e-02 4.1435464e-03 4.4658108e-03 8.7058429e-04 4.4660793e-04 2.5555941e-04 1.2381894e-03 8.7244097e-04 2.0362511e-03 8.1311609e-04 1.5164656e-03 1.9481959e-03 1.9017569e-03 4.9004150e-04 8.1438359e-04 1.7141435e-03 4.7093504e-03 2.7065864e-03 6.0393489e-05 4.2915211e-04 3.9373776e-03 1.5669301e-03 1.2950596e-03 2.1025536e-03 1.9522193e-03 1.0322026e-03 8.4218839e-04 9.2905490e-05 1.2346326e-03 1.1781032e-03 9.8002670e-04 7.9339756e-04 1.1328834e-03 6.2948320e-04 1.4629322e-03 3.9084846e-04 1.4164544e-03 1.5298834e-03 3.8721999e-03 2.7001180e-04 1.5287601e-03 1.2669024e-03 2.1037534e-03 3.7134859e-03 8.7058429e-04 1.1775044e-03 2.8495857e-03 2.7086756e-03 1.4023687e-03 1.0191451e-03 3.8647977e-03 9.3179883e-04 1.2988786e-03 3.9786961e-03 9.7659791e-03 1.8948352e-03 3.2059058e-03 1.9902291e-03 1.7004118e-03 5.2879179e-03 6.5830280e-03 1.9368261e-03 2.2268965e-03 1.8962421e-03 4.4774419e-03 1.1162650e-02 2.6447647e-03 4.9365528e-03 3.0323131e-03 3.9972109e-03 2.9343480e-03 3.7523678e-03 6.3980393e-03 2.7120874e-03 6.2404178e-03 8.0807935e-03 1.9416596e-03 1.3024398e-03 4.0245181e-03 3.0697725e-03 5.0283287e-03 5.8270256e-03 2.3505477e-03 2.0375391e-03 1.2652208e-03 1.2527532e-02 8.0556250e-03 2.6750700e-03 2.5225228e-03 9.0764566e-03 3.4699713e-03 1.3308823e-03 2.7664696e-03 2.4678015e-03 9.5729618e-04 4.3858320e-03 4.4729680e-03 6.8828714e-03 1.1547480e-03 4.8183810e-03 2.7179325e-03 6.3087897e-03 6.0843336e-03 2.6715726e-03 1.3701564e-03 8.5085504e-04 2.7129190e-03 3.6187454e-03 4.3357364e-03 3.0323131e-03 2.8888833e-03 3.1752051e-03 4.0026050e-03 5.6110347e-03 2.1520192e-03 2.0021167e-03 9.3001678e-04 2.6139835e-03 9.7625461e-03 4.3820793e-04 2.7906691e-03 3.4740135e-03 1.5471352e-03 3.7324822e-03 3.8774805e-03 1.8049144e-03 1.0166909e-03 9.6586952e-04 2.7620874e-03 5.6379477e-03 1.4267399e-03 9.9420936e-03 6.9534902e-03 7.2046628e-03 7.1611325e-03 8.0864542e-03 1.1050679e-02 7.0236664e-03 1.0684883e-02 1.2982713e-02 4.4982901e-03 2.1523458e-03 7.1621069e-03 5.3786825e-03 9.3055499e-03 9.5487861e-03 4.3879387e-03 5.0693092e-03 4.4931267e-03 1.8805226e-02 1.2143973e-02 5.1616364e-03 5.8432704e-03 1.4239876e-02 5.0396297e-03 4.0643450e-03 5.6688069e-03 3.5911389e-03 2.3422208e-03 8.6067938e-03 6.9700590e-03 1.0540875e-02 2.8498735e-03 9.0488233e-03 4.9258801e-03 1.1781603e-02 8.6822745e-03 5.8089260e-03 4.4337196e-03 2.0267056e-03 4.1354243e-03 6.4148025e-03 4.4976834e-03 6.9534902e-03 6.4275292e-03 5.9584428e-03 5.3006657e-03 8.1437729e-03 3.9023880e-03 4.5299634e-03 3.7988456e-03 2.5123066e-03 1.1946309e-03 2.0097566e-04 1.7928786e-03 5.7143349e-04 1.2667176e-04 4.6644872e-04 4.4712161e-04 1.0683378e-03 5.7468992e-04 7.6554900e-05 3.3664265e-03 2.0582490e-04 1.3553574e-02 7.1051776e-03 4.8941009e-03 6.5257957e-03 8.3098959e-03 7.5373275e-03 9.3233389e-03 6.7621473e-03 8.4467156e-03 6.1291129e-03 2.5944515e-03 4.8248569e-03 3.9028342e-03 8.7515124e-03 1.1627531e-02 6.1178515e-03 4.1203288e-03 5.0572182e-03 1.3529172e-02 6.7984240e-03 5.0687125e-03 7.7357162e-03 9.2015610e-03 2.4873418e-03 4.9175701e-03 3.5355888e-03 2.0473249e-03 2.6605314e-03 7.6511613e-03 3.0628424e-03 5.7307078e-03 2.1169019e-03 8.3320524e-03 2.4574218e-03 9.1102030e-03 4.8098810e-03 9.5736130e-03 4.5225384e-03 2.7314130e-03 2.7690856e-03 6.7080946e-03 3.3506310e-03 7.1051776e-03 6.8798895e-03 7.7001955e-03 4.4569824e-03 4.6305164e-03 3.1848752e-03 8.4170139e-03 5.2045050e-03 6.9296246e-03 2.1360002e-03 3.7784686e-03 3.9888330e-03 1.9332806e-03 3.1165972e-03 3.4622240e-03 6.1843155e-03 4.9689115e-03 2.8276933e-03 8.4143362e-03 3.9488633e-03 1.7390613e-02 8.6823566e-03 4.7125346e-03 7.5297389e-03 9.6551734e-03 5.6073015e-03 1.2632980e-02 4.7967985e-03 5.4595549e-03 9.6779905e-03 6.1919843e-03 4.6437798e-03 4.8811137e-03 9.2634464e-03 1.4470481e-02 9.9239882e-03 5.5116951e-03 7.7393895e-03 8.8498403e-03 3.6104684e-03 7.0913900e-03 1.1068993e-02 5.6668826e-03 3.0002631e-03 7.9032607e-03 4.0760726e-03 3.7279027e-03 5.9729084e-03 7.9919733e-03 2.3383845e-03 3.2272638e-03 4.6096764e-03 8.7690968e-03 3.1364844e-03 7.9578373e-03 3.3962632e-03 1.4631125e-02 6.8663759e-03 6.4834920e-03 4.3461235e-03 8.7377082e-03 5.5255283e-03 8.6823566e-03 8.8561968e-03 1.1024987e-02 6.2645608e-03 3.5745084e-03 5.1960404e-03 1.3983177e-02 8.7265781e-03 1.5360474e-03 2.2738132e-03 6.3957013e-04 1.8470485e-03 1.9436948e-03 8.2753134e-04 1.4870044e-04 1.8575051e-04 1.1511457e-03 4.1570185e-03 4.2481246e-04 1.2022631e-02 7.4558885e-03 6.7319225e-03 7.0097246e-03 8.7236579e-03 1.0059866e-02 8.2686681e-03 9.2543911e-03 1.1644804e-02 5.5866311e-03 2.5062451e-03 6.6946690e-03 5.2622870e-03 9.8836223e-03 1.1452219e-02 5.6355000e-03 4.7339621e-03 4.6436729e-03 1.7620106e-02 1.0378141e-02 5.6675320e-03 7.1003305e-03 1.2674455e-02 4.3836233e-03 4.5891805e-03 4.7259960e-03 3.2437023e-03 2.5249532e-03 8.8366518e-03 5.2434703e-03 8.9867759e-03 2.2946416e-03 9.5089828e-03 3.7537168e-03 1.0503141e-02 7.8427036e-03 7.7804598e-03 4.4127994e-03 2.3620086e-03 3.9941388e-03 7.2903223e-03 4.7424240e-03 7.4558885e-03 7.1215300e-03 7.3539364e-03 5.7328206e-03 7.4284923e-03 3.9643246e-03 6.4306545e-03 4.3729078e-03 1.0852360e-03 3.9664584e-04 4.5123208e-04 1.2414442e-03 2.3150826e-04 1.4183402e-03 7.0426023e-04 4.8163546e-04 5.0741189e-03 3.9595383e-04 1.0937070e-02 5.0339955e-03 3.1168119e-03 4.5426488e-03 6.0432452e-03 5.3182662e-03 7.1300333e-03 4.7641753e-03 6.1358685e-03 4.4885462e-03 1.6916118e-03 3.0636842e-03 2.3987749e-03 6.3937387e-03 9.2584560e-03 4.5899059e-03 2.5786937e-03 3.4750392e-03 1.0541835e-02 4.8350707e-03 3.4370915e-03 5.7977832e-03 6.8277961e-03 1.3144796e-03 3.3638906e-03 2.1338739e-03 1.0504939e-03 1.6413828e-03 5.4173620e-03 1.8780432e-03 3.9014661e-03 1.1794316e-03 6.0207953e-03 1.3473818e-03 6.9135169e-03 3.1440431e-03 7.6724602e-03 2.9974710e-03 1.7751706e-03 1.5977690e-03 4.8531144e-03 2.4494451e-03 5.0339955e-03 4.8947958e-03 5.8464391e-03 3.1299587e-03 2.9881785e-03 1.9161069e-03 6.7804975e-03 3.6799724e-03 6.0827379e-04 2.1616612e-03 3.6536587e-03 6.0346559e-04 1.7560211e-03 1.3150048e-03 2.0808595e-03 9.6848049e-03 1.5050304e-03 8.5509882e-03 3.4934050e-03 2.3873070e-03 2.2266374e-03 4.3437966e-03 3.4432000e-03 4.8822411e-03 2.6118729e-03 4.2515298e-03 3.9280244e-03 2.1763551e-03 2.4215318e-03 2.3726469e-03 5.2469603e-03 8.8156975e-03 4.6543689e-03 1.1653780e-03 1.5634616e-03 8.3770205e-03 3.5034084e-03 3.2683461e-03 4.7014835e-03 4.7252691e-03 1.7941232e-03 2.2843067e-03 6.9328893e-04 1.6436639e-03 1.4866860e-03 4.0234517e-03 7.8970448e-04 2.8726545e-03 3.0564882e-04 4.8340126e-03 3.3681970e-04 3.2031086e-03 3.4962764e-03 6.5051951e-03 1.2935806e-03 1.7549327e-03 2.1673002e-03 4.7736712e-03 4.5514956e-03 3.4934050e-03 3.7661841e-03 5.5497035e-03 4.3927525e-03 3.3294822e-03 2.0638533e-03 5.8922322e-03 2.1421877e-03 1.0254014e-03 1.7967204e-03 2.4056491e-05 4.9606070e-04 1.5493043e-04 7.3786851e-04 5.7634641e-03 2.2821239e-04 9.8983432e-03 4.8234579e-03 3.6558248e-03 4.1003359e-03 5.8726550e-03 5.9033047e-03 6.1776943e-03 5.1708698e-03 7.0154090e-03 4.1004756e-03 1.5716690e-03 3.6383691e-03 2.8864008e-03 6.7180252e-03 9.3042807e-03 4.3918407e-03 2.3314998e-03 2.6748196e-03 1.1913776e-02 5.9266374e-03 3.6227612e-03 5.2997514e-03 7.7658365e-03 2.0655115e-03 2.8598574e-03 2.0567764e-03 1.5031818e-03 1.3330059e-03 5.6389436e-03 2.2318728e-03 4.9186471e-03 7.3077456e-04 6.3335619e-03 1.3529510e-03 6.4463256e-03 4.4695359e-03 6.6841393e-03 2.3650796e-03 1.3974597e-03 2.1137539e-03 5.1383865e-03 3.4413190e-03 4.8234579e-03 4.7468419e-03 5.6953492e-03 3.9468482e-03 4.2095924e-03 2.1520165e-03 5.7168561e-03 2.8181362e-03 2.6785665e-04 8.6267880e-04 1.5080867e-03 1.0657562e-03 9.1259932e-05 3.2834346e-03 5.6400090e-04 1.5768182e-02 8.4481987e-03 5.6912110e-03 7.5460195e-03 9.7379656e-03 8.1209399e-03 1.1078194e-02 7.1125751e-03 8.8544675e-03 7.7247994e-03 3.7380313e-03 5.6213520e-03 4.8502413e-03 1.0137958e-02 1.3681745e-02 7.7719782e-03 4.9953780e-03 6.2009166e-03 1.4059422e-02 6.9488117e-03 6.3504999e-03 9.4504672e-03 9.5001122e-03 3.1213299e-03 6.2307927e-03 4.0875652e-03 2.8287725e-03 3.7400310e-03 8.8571631e-03 3.1845212e-03 5.9757813e-03 2.8285304e-03 9.6559261e-03 2.8616391e-03 9.5937311e-03 5.3787128e-03 1.1658427e-02 5.5682032e-03 3.8827352e-03 3.6680728e-03 8.1913042e-03 4.3904219e-03 8.4481987e-03 8.3060404e-03 9.4592722e-03 5.6422254e-03 5.2631423e-03 4.2070121e-03 1.0442544e-02 6.5544595e-03 1.6777879e-03 1.6905742e-03 1.4559893e-03 2.3696578e-04 1.7661444e-03 8.8626613e-04 1.8929605e-02 1.1198434e-02 8.2126916e-03 1.0357192e-02 1.2691977e-02 1.1286676e-02 1.3897779e-02 1.0124890e-02 1.2174180e-02 9.8437553e-03 5.0898821e-03 8.1181082e-03 6.9678076e-03 1.3160374e-02 1.6460126e-02 9.6980805e-03 7.2881507e-03 8.4988687e-03 1.8112744e-02 9.9154333e-03 8.5214392e-03 1.1906445e-02 1.2939064e-02 4.9240987e-03 8.3797448e-03 6.3232280e-03 4.3836801e-03 5.2916266e-03 1.1816149e-02 5.2785357e-03 8.7418147e-03 4.4568647e-03 1.2648763e-02 4.7832566e-03 1.2911612e-02 7.7565330e-03 1.3968530e-02 7.8153448e-03 5.3323341e-03 5.3832666e-03 1.0542156e-02 5.6397042e-03 1.1198434e-02 1.0902962e-02 1.1744032e-02 7.3882415e-03 7.5970260e-03 6.0277743e-03 1.2454722e-02 8.6970669e-03 6.8936185e-04 2.4185836e-04 6.5755985e-04 5.6966351e-03 2.3035735e-04 9.8692354e-03 4.6426684e-03 3.3219514e-03 3.9792968e-03 5.6658817e-03 5.5182925e-03 6.1624599e-03 4.8454827e-03 6.5522795e-03 4.0009447e-03 1.4714328e-03 3.2975731e-03 2.5896752e-03 6.3872309e-03 9.0422907e-03 4.2582444e-03 2.2027730e-03 2.6736829e-03 1.1276803e-02 5.4428314e-03 3.3876877e-03 5.2005260e-03 7.2827649e-03 1.7342875e-03 2.7931367e-03 1.8984292e-03 1.2582586e-03 1.2661063e-03 5.3418398e-03 1.9890109e-03 4.4646783e-03 7.0136727e-04 6.0091268e-03 1.2000111e-03 6.3087140e-03 3.9600850e-03 6.7054154e-03 2.3290598e-03 1.3528267e-03 1.8394373e-03 4.8617299e-03 3.0806223e-03 4.6426684e-03 4.5595872e-03 5.5227532e-03 3.5904469e-03 3.7269772e-03 1.9354221e-03 5.7786823e-03 2.8379142e-03 1.6160265e-04 9.1316164e-04 4.5133200e-03 3.9636483e-04 1.2999342e-02 7.7990273e-03 6.7507696e-03 6.8565065e-03 9.1164934e-03 9.6187705e-03 8.8289255e-03 8.5171572e-03 1.1041936e-02 6.3870800e-03 3.1070828e-03 6.7336821e-03 5.5817760e-03 1.0377938e-02 1.2745315e-02 6.6393015e-03 4.6463353e-03 4.6865341e-03 1.7095703e-02 9.6546615e-03 6.2703784e-03 7.8942299e-03 1.1919237e-02 4.5195099e-03 4.9936591e-03 4.3235727e-03 3.5370732e-03 2.9138150e-03 9.0955590e-03 4.5162367e-03 8.4195838e-03 2.1592479e-03 9.9327314e-03 3.3056202e-03 9.5169563e-03 7.9097267e-03 8.9017922e-03 4.4124132e-03 2.8482486e-03 4.3995745e-03 8.1140558e-03 5.6448175e-03 7.7990273e-03 7.6396844e-03 8.3822247e-03 6.5839845e-03 7.5512097e-03 4.3854717e-03 7.5528676e-03 4.6977929e-03 6.1532842e-04 4.4755911e-03 9.8852555e-05 1.1287330e-02 6.2297232e-03 5.1189561e-03 5.6158860e-03 7.4118621e-03 7.9100135e-03 7.4458980e-03 7.1082862e-03 9.2159095e-03 4.9487763e-03 1.9996447e-03 5.0869737e-03 4.0020507e-03 8.3714289e-03 1.0564909e-02 5.1004357e-03 3.5205852e-03 3.7366566e-03 1.4679778e-02 7.9564069e-03 4.6553854e-03 6.3553187e-03 1.0091302e-02 3.0597107e-03 3.7953264e-03 3.3093658e-03 2.2411736e-03 1.9075393e-03 7.2834604e-03 3.5416682e-03 6.7651117e-03 1.4482690e-03 7.9807479e-03 2.4205104e-03 8.4840253e-03 5.9704010e-03 7.4790367e-03 3.4333236e-03 1.8691676e-03 2.9457380e-03 6.2606998e-03 3.9378134e-03 6.2297232e-03 6.0227040e-03 6.6475915e-03 4.7553767e-03 5.6493596e-03 2.9997214e-03 6.2979764e-03 3.7014773e-03 2.9064840e-03 2.5951953e-04 1.5239927e-02 8.3975944e-03 6.0152535e-03 7.5736149e-03 9.7205279e-03 8.7194769e-03 1.0665619e-02 7.7107157e-03 9.6513260e-03 7.3495285e-03 3.4129734e-03 5.9493098e-03 4.9891659e-03 1.0300134e-02 1.3429483e-02 7.3762928e-03 4.9967161e-03 5.9349958e-03 1.5151045e-02 7.8143476e-03 6.2821596e-03 9.0873094e-03 1.0390318e-02 3.3768176e-03 5.9427925e-03 4.2522990e-03 2.8707879e-03 3.4399883e-03 9.0442431e-03 3.6087055e-03 6.7269934e-03 2.6557188e-03 9.8316941e-03 3.0241465e-03 9.9672648e-03 5.9844482e-03 1.0964814e-02 5.3765757e-03 3.5060112e-03 3.7251338e-03 8.1205003e-03 4.4033527e-03 8.3975944e-03 8.1986819e-03 9.1329776e-03 5.6810271e-03 5.8010063e-03 4.1618459e-03 9.6695634e-03 6.1457695e-03 3.7030792e-03 2.6425725e-02 1.8599298e-02 1.5467357e-02 1.8635118e-02 2.0442342e-02 2.0541696e-02 2.1259986e-02 1.9510831e-02 2.1931276e-02 1.5294793e-02 9.1682566e-03 1.5285848e-02 1.2953400e-02 2.0823480e-02 2.2513198e-02 1.4419732e-02 1.4350471e-02 1.5480442e-02 2.9209010e-02 1.9066274e-02 1.4266029e-02 1.8093335e-02 2.3152907e-02 1.0464507e-02 1.4480769e-02 1.3685178e-02 9.2298310e-03 1.0125817e-02 1.9700965e-02 1.2730523e-02 1.7262155e-02 1.0225203e-02 2.0352884e-02 1.1535854e-02 2.3443654e-02 1.4503051e-02 1.9462802e-02 1.4723300e-02 9.8246219e-03 1.0395301e-02 1.6330792e-02 8.8773452e-03 1.8599298e-02 1.7720786e-02 1.7175716e-02 1.1774527e-02 1.4190018e-02 1.1287889e-02 1.7334951e-02 1.4959707e-02 1.2176711e-02 6.5819636e-03 5.0346277e-03 6.0397395e-03 7.7827629e-03 7.8599708e-03 8.1942510e-03 7.0899174e-03 9.0304038e-03 5.3331159e-03 2.1084043e-03 4.9833227e-03 3.9086435e-03 8.5223444e-03 1.0895531e-02 5.3819973e-03 3.7720778e-03 4.2725796e-03 1.4381123e-02 7.5978371e-03 4.7474248e-03 6.8472012e-03 9.8808760e-03 2.7678758e-03 4.1999833e-03 3.4360629e-03 2.0712504e-03 2.1185414e-03 7.4497841e-03 3.4016631e-03 6.4264729e-03 1.6858980e-03 8.1251590e-03 2.4514173e-03 8.9161864e-03 5.4840504e-03 8.2209587e-03 3.8745808e-03 2.1076714e-03 2.7824071e-03 6.3561971e-03 3.5186798e-03 6.5819636e-03 6.3377454e-03 6.9667686e-03 4.4964760e-03 5.2124475e-03 2.9917795e-03 7.0285944e-03 4.2716151e-03 1.5675754e-03 4.2437140e-03 2.9528525e-03 1.2801911e-03 5.5300675e-03 5.2313183e-04 7.0373258e-03 7.0028853e-03 1.5817688e-03 4.6017698e-03 4.2905415e-03 3.9763653e-03 1.7507004e-03 9.8088526e-04 2.0205251e-03 3.8651167e-03 2.8498196e-03 8.0083780e-03 8.7219602e-03 2.5429883e-03 8.6090275e-04 7.9906965e-03 6.1518285e-03 2.2097973e-03 5.7963314e-03 5.7460009e-03 4.2262969e-03 2.1872477e-03 8.9731860e-03 7.8793225e-03 5.8991938e-03 1.8999235e-03 7.1341168e-03 7.0170212e-03 6.4409068e-03 6.9062404e-04 3.2402506e-03 4.2346770e-03 4.9489360e-03 1.9844891e-03 6.6121605e-03 1.5675754e-03 1.4409377e-03 1.2937675e-03 4.5154908e-03 6.0031850e-03 4.0189646e-03 1.2526895e-03 2.2326743e-03 6.7976748e-04 4.4251056e-04 5.1964468e-05 1.5314410e-03 4.7093005e-04 2.2872076e-03 2.4409680e-03 6.1996952e-04 1.8531872e-03 7.0675084e-04 7.4672595e-04 2.9977980e-04 1.6320684e-03 1.1279305e-03 6.4871929e-04 6.2074215e-04 4.0109945e-03 3.1610099e-03 4.6600326e-04 4.3207236e-04 3.1187047e-03 1.7495883e-03 3.8892953e-04 1.4972893e-03 1.7881991e-03 1.3932149e-03 1.6264024e-04 3.1616233e-03 2.5553184e-03 1.9652949e-03 2.4129313e-04 2.2301817e-03 3.0218268e-03 1.9168834e-03 1.5054952e-03 6.4024254e-04 1.6084351e-03 1.4082859e-03 5.9336508e-04 3.2376306e-03 1.1102230e-16 6.3653022e-05 8.5949029e-04 1.8263557e-03 1.6881339e-03 9.7411877e-04 1.7845049e-03 6.1330704e-04 7.1368649e-04 9.0151789e-04 6.1896245e-04 2.2461726e-03 1.0406196e-03 1.1172454e-03 1.6730261e-03 1.8311894e-03 1.8071232e-06 2.5364008e-04 8.3907159e-04 3.3874526e-03 2.1375798e-03 4.5487046e-04 1.1672957e-03 2.8115805e-03 1.2138761e-03 7.0446879e-04 1.8201572e-03 1.6098088e-03 5.2152529e-04 1.0325997e-03 6.5974667e-04 9.0603694e-04 1.4060118e-03 4.3618665e-04 1.3469029e-03 7.5850938e-04 1.4500880e-03 6.7416609e-04 1.0299923e-03 2.5247687e-03 4.1453726e-04 3.7399848e-03 9.2294426e-04 1.7551096e-03 7.2749277e-04 1.1614195e-03 2.4439916e-03 6.7976748e-04 8.1575962e-04 2.1038526e-03 1.4799615e-03 3.3653084e-04 6.6936205e-04 3.8899674e-03 1.5033836e-03 6.1892938e-04 8.9565267e-04 1.1187414e-03 1.0854623e-03 1.6238473e-03 1.6997610e-03 2.5416419e-03 7.7939556e-04 1.2163589e-03 1.1645635e-03 3.7479516e-03 2.5673270e-03 2.8413719e-04 3.4882680e-04 3.5538869e-03 2.1358644e-03 1.3529179e-03 1.5262264e-03 2.0757398e-03 1.9224937e-03 7.7565401e-04 6.3114517e-04 2.1204969e-03 1.7089017e-03 5.9516371e-04 1.8253945e-03 1.7466546e-03 1.3725400e-03 9.7938279e-04 1.2586511e-03 1.2414457e-03 2.1946939e-03 3.0180258e-03 3.2655328e-04 2.0574339e-03 1.9692598e-03 1.9049224e-03 4.6812273e-03 4.4251056e-04 7.9383810e-04 2.4056659e-03 3.2171363e-03 2.0291670e-03 1.5121604e-03 3.2358269e-03 7.8092419e-04 1.6056499e-03 4.5150817e-04 2.5183573e-03 2.4854849e-03 8.1663583e-04 2.4301739e-03 9.3222022e-04 1.0572824e-03 1.7339381e-04 1.3855383e-03 1.3424761e-03 1.0242405e-03 9.7281350e-04 3.6981982e-03 3.3860618e-03 6.8052310e-04 4.8835842e-04 3.1532670e-03 2.2405021e-03 6.9163264e-04 1.9828394e-03 2.3495346e-03 1.9517373e-03 1.3548451e-04 3.7877283e-03 2.8247707e-03 2.6466102e-03 1.4866617e-04 2.8614588e-03 3.2185979e-03 2.1613637e-03 1.5430396e-03 1.0217871e-03 2.1845781e-03 1.8662401e-03 6.6257305e-04 3.7358116e-03 5.1964468e-05 1.2907411e-04 9.1062515e-04 2.1316335e-03 1.9388777e-03 1.3908595e-03 1.9526372e-03 9.5243046e-04 3.3534848e-03 2.1612689e-04 1.1409237e-04 3.7511533e-03 4.4135123e-03 6.6848080e-04 1.6638889e-03 1.5639090e-03 5.3797969e-03 4.6512834e-03 1.2304041e-03 2.1424312e-03 1.1209148e-03 3.9967200e-04 2.4397061e-03 3.5566606e-03 2.8773633e-04 1.9980195e-03 2.5921392e-03 1.0825130e-03 2.8538916e-03 3.5346603e-03 9.3770297e-04 1.4682668e-03 3.5169417e-04 2.8507687e-03 1.3414178e-03 1.7057263e-03 1.1395352e-03 1.1778138e-03 6.0686882e-03 1.8828714e-03 4.1140616e-03 2.6499598e-03 2.9837515e-03 5.4227271e-03 1.5314410e-03 2.0381041e-03 4.2816261e-03 3.9075302e-03 1.2227927e-03 2.5264748e-03 6.5268770e-03 2.9950818e-03 4.2523782e-03 4.6456758e-03 7.0284879e-04 2.6206169e-03 2.2973667e-03 2.1186617e-03 1.0662204e-03 1.4771683e-03 1.2714179e-03 1.6200996e-03 9.3400517e-04 6.3864823e-03 5.7928278e-03 1.2702417e-03 3.0680262e-04 5.4794582e-03 3.6431092e-03 7.0967201e-04 2.9368020e-03 3.3273895e-03 2.1071133e-03 1.0635659e-03 5.3575283e-03 5.0378701e-03 2.9935636e-03 1.0701915e-03 3.8904013e-03 4.2643219e-03 4.2480632e-03 6.5030200e-04 1.1722241e-03 2.1985353e-03 2.8568074e-03 1.2361632e-03 4.8426872e-03 4.7093005e-04 4.8520973e-04 9.1253531e-04 3.1519557e-03 3.8819044e-03 2.1030647e-03 9.4974174e-04 6.5992898e-04 2.7415573e-04 4.6934818e-03 4.8265016e-03 1.1075187e-03 2.2246224e-03 2.7094103e-03 7.2547386e-03 5.7651516e-03 1.2879775e-03 2.2498412e-03 1.7969640e-03 2.9988677e-04 3.2873557e-03 4.6281528e-03 3.4411066e-04 2.2557578e-03 3.0860670e-03 7.9349579e-04 3.1195585e-03 3.7700064e-03 1.7729958e-03 8.2927671e-04 3.1955044e-04 2.5127017e-03 2.3924363e-03 1.2355886e-03 6.3306711e-04 1.7271405e-03 7.3517242e-03 1.9482896e-03 4.3986367e-03 3.1257329e-03 4.1788193e-03 6.3227646e-03 2.2872076e-03 2.9291555e-03 5.5970414e-03 4.9483109e-03 1.7952206e-03 3.0186434e-03 7.6597710e-03 3.3340783e-03 5.0895780e-03 5.6951678e-03 1.1683986e-03 2.4173290e-03 2.2930538e-03 6.6766405e-03 6.0598142e-03 2.0196993e-03 3.2103351e-03 7.1260012e-04 2.1040770e-04 3.4457175e-03 4.8676879e-03 4.7880714e-05 2.6160358e-03 3.7638559e-03 1.6534967e-03 3.7166920e-03 4.7397184e-03 1.5966719e-03 1.7314841e-03 3.1225254e-04 3.8216967e-03 2.0442457e-03 2.2822879e-03 1.3421268e-03 1.4265946e-03 7.7649269e-03 2.8740126e-03 5.4160448e-03 3.5155679e-03 4.0306705e-03 6.4534752e-03 2.4409680e-03 3.0445932e-03 5.6145787e-03 4.8826195e-03 1.5492752e-03 3.4872724e-03 8.3107496e-03 4.2635519e-03 8.0787158e-04 1.6588755e-03 9.5088926e-04 1.1215622e-03 1.1319394e-03 1.0749073e-04 1.3651148e-03 1.0419898e-03 7.3951172e-03 5.6721517e-03 2.8717807e-04 1.2167194e-04 6.0929710e-03 1.9467902e-03 2.7497575e-04 2.6021687e-03 1.4711384e-03 7.6653532e-04 1.1979861e-03 4.6497866e-03 4.6528613e-03 2.0491908e-03 1.1214382e-03 3.1087470e-03 5.8331337e-03 2.9008505e-03 5.1851355e-04 1.1077981e-03 7.4180273e-04 1.1189833e-03 3.3213225e-04 1.9786092e-03 6.1996952e-04 3.2630432e-04 1.6788710e-04 1.0717507e-03 2.5327575e-03 6.9916673e-04 4.8860962e-04 5.0860173e-04 1.7852918e-03 8.1925898e-04 2.7637031e-03 3.4415701e-03 7.6048751e-04 1.3454814e-03 1.4168273e-03 9.1102129e-03 5.4634531e-03 7.0608839e-04 1.5253323e-03 6.6930140e-03 9.6900708e-04 7.0882160e-04 2.0455668e-03 3.9468549e-04 1.1422246e-04 2.4972040e-03 3.2985277e-03 4.3265068e-03 9.6084950e-04 2.6351726e-03 1.9457490e-03 6.5435669e-03 2.5549327e-03 2.2787630e-03 1.2770395e-03 6.6117888e-05 4.1362061e-04 1.2709485e-03 9.0111668e-04 1.8531872e-03 1.4879007e-03 1.4236785e-03 8.2396190e-04 2.2228636e-03 3.0260473e-04 1.7887502e-03 9.8733924e-04 2.2593145e-04 8.4529966e-04 3.3545284e-03 2.0967474e-03 4.8795197e-04 1.2181976e-03 2.8660735e-03 1.2480152e-03 6.7735579e-04 1.8245752e-03 1.6711447e-03 4.7937341e-04 1.0455121e-03 6.9960603e-04 8.5898853e-04 1.3874556e-03 4.5677202e-04 1.3792343e-03 7.7973864e-04 1.4683104e-03 6.8342524e-04 1.0561802e-03 2.6555506e-03 3.7342452e-04 3.7432085e-03 9.6667604e-04 1.7304819e-03 6.7810292e-04 1.1254700e-03 2.3291425e-03 7.0675084e-04 8.2471895e-04 2.0738735e-03 1.3951480e-03 2.9534051e-04 6.3569311e-04 3.8846725e-03 1.5360581e-03 9.9102840e-04 2.6998600e-03 1.1403522e-03 5.9435819e-04 1.1584769e-03 4.5439398e-03 2.4115910e-03 2.2376231e-04 1.3051908e-03 3.1170665e-03 2.4639357e-04 6.5093729e-04 1.0408914e-03 3.2170646e-04 6.8086675e-04 7.3723950e-04 1.9464154e-03 1.6938542e-03 1.2010351e-03 8.6567601e-04 1.2605984e-03 4.0415296e-03 6.0024917e-04 2.8241029e-03 9.3145876e-04 8.7987159e-04 1.5390142e-04 6.1621791e-04 1.2031695e-03 7.4672595e-04 6.4561848e-04 1.3132954e-03 5.7671738e-04 4.3996999e-04 1.2646330e-04 2.7819647e-03 1.1571313e-03 1.1519638e-03 1.4945239e-03 1.5137092e-03 1.7542391e-03 3.0584682e-03 3.1624125e-03 6.9423087e-04 8.0741075e-04 2.9564986e-03 2.1264808e-03 1.1956222e-03 2.4834226e-03 2.4046990e-03 2.4389289e-03 1.1428932e-04 4.1572751e-03 2.6270682e-03 3.3826290e-03 1.0906814e-05 3.3779688e-03 3.9749243e-03 1.6265579e-03 2.0735613e-03 1.7238890e-03 2.6977636e-03 1.8191981e-03 5.1180139e-04 3.3068455e-03 2.9977980e-04 3.0495320e-04 9.6732841e-04 1.7461140e-03 1.4560452e-03 1.4920651e-03 2.5772801e-03 1.7019143e-03 1.0341988e-03 4.0085546e-03 3.6492138e-03 7.4340196e-03 7.9703529e-03 1.4966880e-03 7.8440192e-04 7.7789554e-03 4.4532033e-03 2.2360209e-03 5.9110335e-03 4.1723764e-03 3.6115483e-03 1.8645164e-03 8.6587941e-03 6.9790025e-03 5.9223579e-03 1.3508421e-03 6.9679643e-03 8.8100022e-03 4.3884451e-03 9.3956602e-04 3.8113149e-03 3.5744927e-03 3.3082295e-03 7.5316334e-04 3.6491972e-03 1.6320684e-03 1.1884181e-03 4.5803930e-04 2.1497659e-03 4.0210128e-03 2.8148066e-03 1.4099016e-03 2.8014283e-03 2.0075866e-03 1.7351697e-03 8.4094224e-03 6.5677927e-03 3.9418601e-04 3.5055970e-04 7.1718250e-03 2.0973679e-03 6.4851765e-04 3.3744442e-03 1.5292791e-03 9.4071576e-04 1.7144529e-03 5.4753391e-03 5.4188405e-03 2.5828658e-03 1.5301373e-03 3.8027206e-03 7.3328244e-03 3.1192913e-03 6.2731613e-04 1.7741918e-03 8.4678687e-04 1.1412463e-03 3.3683903e-04 1.4744098e-03 1.1279305e-03 6.7560808e-04 1.3395103e-04 7.7360874e-04 2.7301747e-03 8.0481079e-04 5.3215427e-04 1.0082007e-03 2.3386408e-04 4.5650335e-03 2.1085336e-03 8.9398060e-04 1.5597528e-03 2.5626398e-03 8.9643929e-04 4.6401620e-04 2.1474828e-04 9.4200330e-04 7.4363388e-04 8.8544521e-04 1.1214375e-03 1.5513223e-03 4.7369920e-04 1.2949283e-03 5.0107186e-04 1.9686134e-03 1.5839083e-03 3.0734994e-03 1.1037013e-04 1.0184869e-03 9.4459481e-04 1.6215035e-03 3.1102930e-03 6.4871929e-04 8.4571319e-04 2.1956968e-03 2.1853516e-03 1.4074387e-03 6.7079422e-04 3.0128980e-03 5.5375407e-04 5.9745601e-03 3.5596915e-03 1.0825427e-03 1.1193941e-03 3.8619013e-03 1.7868782e-03 2.6699310e-04 7.4652731e-04 1.5644519e-03 7.6914730e-04 1.1868249e-03 2.1537215e-03 2.8904145e-03 6.7050048e-04 1.5688544e-03 1.1553603e-03 2.3985919e-03 2.8520067e-03 2.1226295e-03 2.6532317e-05 9.5443067e-04 1.5325109e-03 1.7403036e-03 3.8293675e-03 6.2074215e-04 7.8759073e-04 1.8993585e-03 2.7454611e-03 2.5765044e-03 1.0363780e-03 2.0443972e-03 1.4335859e-04 1.4016332e-03 5.4805806e-03 6.7405649e-03 6.4579738e-04 5.1748633e-03 6.3487765e-03 4.3874825e-03 6.7084849e-03 8.1049162e-03 2.6677096e-03 4.6345484e-03 1.7447861e-03 7.4893653e-03 2.9062036e-03 5.4610122e-03 3.0610024e-03 2.8530196e-03 9.9111331e-03 5.6471487e-03 8.9070297e-03 6.1633735e-03 5.5983777e-03 9.1911029e-03 4.0109945e-03 4.6927139e-03 7.3914201e-03 7.0233589e-03 3.0645626e-03 6.1283907e-03 1.0939049e-02 7.1096522e-03 3.7615860e-03 5.7439366e-03 2.3434979e-04 2.1625792e-03 4.1436319e-03 1.4386017e-03 3.2647240e-03 4.5819077e-03 2.2920937e-03 1.0610016e-03 6.9564930e-05 3.4218787e-03 2.8472107e-03 1.7665885e-03 1.7134963e-03 1.2022963e-03 8.8958832e-03 3.1032776e-03 5.2642601e-03 3.2432815e-03 4.6032265e-03 6.0005810e-03 3.1610099e-03 3.7408101e-03 6.4052558e-03 4.8371122e-03 1.3589516e-03 3.3936977e-03 9.2321743e-03 4.6963171e-03 5.0386940e-04 4.2984797e-03 8.6973796e-04 3.5526497e-04 1.7666127e-03 7.2192984e-04 6.5802622e-04 6.6868074e-04 3.2431480e-03 2.9035420e-03 1.6594087e-03 6.4854242e-04 2.1612810e-03 4.9124735e-03 1.3725331e-03 1.5050690e-03 9.8777331e-04 7.5260407e-04 4.1403958e-04 1.4453279e-04 1.2645371e-03 4.6600326e-04 2.4792089e-04 4.5627999e-04 4.7560079e-04 1.1214322e-03 2.2091820e-04 1.5167980e-03 7.9735767e-04 5.8276719e-03 2.5886189e-03 4.3280198e-04 2.9205975e-03 2.2021650e-03 1.3597486e-03 9.5466012e-04 5.1915037e-03 4.8194588e-03 2.6659023e-03 8.3877743e-04 3.6595156e-03 5.4665486e-03 3.2603866e-03 3.5246614e-04 1.2546385e-03 1.3738455e-03 1.7317120e-03 4.0173517e-04 2.8852033e-03 4.3207236e-04 2.1872967e-04 1.7262684e-04 1.6274416e-03 2.8950267e-03 1.1933707e-03 5.1057942e-04 6.2761497e-04 3.2515433e-03 4.5844712e-03 2.0397580e-03 4.4892831e-03 5.6236454e-03 2.1663608e-03 1.9457792e-03 4.4715761e-04 4.4562273e-03 2.6795180e-03 2.6855442e-03 1.3224094e-03 1.8950095e-03 8.9432615e-03 3.4981521e-03 6.3671383e-03 4.3127165e-03 4.9533188e-03 7.5137514e-03 3.1187047e-03 3.8229023e-03 6.6814123e-03 5.8612561e-03 2.0597987e-03 4.3006086e-03 9.5397630e-03 5.0775992e-03 1.3692874e-03 9.6403546e-04 1.3966479e-04 8.6664284e-04 1.6597338e-03 1.3212159e-03 1.4578093e-03 1.0432500e-03 1.9220792e-03 8.7918940e-04 4.4168060e-03 5.0670419e-04 4.4696559e-03 1.4208846e-03 1.1004494e-03 1.6436932e-04 1.5507648e-03 1.1191058e-03 1.7495883e-03 1.6621469e-03 2.5414268e-03 8.8363295e-04 4.0096791e-04 3.3482530e-04 4.2345389e-03 1.9374158e-03 1.2770848e-03 1.0395507e-03 3.9095383e-04 9.2829494e-04 2.8992176e-03 3.2975300e-03 9.5393320e-04 1.0925514e-03 1.6776296e-03 3.8507253e-03 2.3805613e-03 1.2082502e-03 2.8135004e-04 4.7639114e-04 8.7356741e-04 7.4310336e-04 2.3860646e-03 3.8892953e-04 3.1610282e-04 8.0643344e-04 1.4728895e-03 2.0712058e-03 4.6512964e-04 1.1229948e-03 8.8791739e-05 1.1956364e-03 1.3055469e-03 1.5870433e-03 3.7223827e-04 1.0219838e-03 4.8252621e-04 2.1760282e-03 1.1580239e-04 1.5036929e-03 1.5974284e-03 4.9006747e-03 5.1331960e-04 1.6812595e-03 1.3616634e-03 2.7816530e-03 3.8434871e-03 1.4972893e-03 1.8359326e-03 3.6884416e-03 3.0552141e-03 1.4996272e-03 1.2110783e-03 4.7697841e-03 1.3648602e-03 3.9191527e-04 1.9808749e-03 1.8113262e-03 2.3841094e-03 7.8154360e-04 2.2164780e-03 1.0439462e-03 5.1766229e-03 1.1157470e-03 3.6934914e-03 1.2626931e-03 5.1659909e-04 5.8959899e-05 1.4097594e-03 7.9470954e-04 1.7881991e-03 1.5872251e-03 2.1165612e-03 7.0368642e-04 9.2823209e-04 1.5533041e-04 3.3160103e-03 1.5048956e-03 2.0235000e-03 2.4966081e-03 3.5807846e-03 4.7284012e-04 2.2730788e-03 1.2853266e-03 5.0519729e-03 2.3762808e-03 2.2444800e-03 6.5500299e-04 2.4750944e-05 4.4815197e-04 1.3292061e-03 1.4780836e-03 1.3932149e-03 1.1924273e-03 1.5277304e-03 1.1896691e-03 2.0673918e-03 2.5400150e-04 1.8225589e-03 5.4227610e-04 2.9880363e-03 1.8314313e-03 2.5238977e-03 5.5757663e-05 2.3698503e-03 2.8213423e-03 1.3040881e-03 2.4190319e-03 1.1221589e-03 2.3332880e-03 1.5386845e-03 7.3215728e-04 3.3801838e-03 1.6264024e-04 2.8457958e-04 1.3051710e-03 1.8856170e-03 1.1599202e-03 1.2252261e-03 2.8396493e-03 1.3307965e-03 7.9081662e-04 1.1208231e-03 3.7513747e-03 2.2532467e-04 1.8915999e-03 1.8066444e-03 7.8138393e-03 1.7396624e-03 2.9887611e-03 2.1626603e-03 4.5716343e-03 4.7705983e-03 3.1616233e-03 3.6125170e-03 5.9695278e-03 4.2653532e-03 1.8208998e-03 2.2513824e-03 7.5822521e-03 3.0950854e-03 2.6638425e-03 2.3329339e-03 1.2833498e-03 1.8503904e-03 7.6221882e-04 7.7332789e-03 2.4545666e-03 4.1819304e-03 2.3678329e-03 3.7122984e-03 4.8118232e-03 2.5553184e-03 3.0167737e-03 5.3596333e-03 3.8038292e-03 8.6990936e-04 2.5148680e-03 7.9707274e-03 3.8479364e-03 3.0845972e-03 3.4545687e-04 3.2912795e-03 2.5772857e-03 4.0614792e-03 4.9082550e-04 6.4992316e-04 1.0688017e-03 2.7640538e-03 2.9796108e-03 1.9652949e-03 2.0639503e-03 3.2701588e-03 2.6492756e-03 2.3500195e-03 8.8549039e-04 3.5901884e-03 9.2048730e-04 3.0276112e-03 3.6195589e-03 1.4632965e-03 2.1763560e-03 1.5224813e-03 2.5475111e-03 1.6768950e-03 5.4331181e-04 3.2527051e-03 2.4129313e-04 2.7799169e-04 1.0453548e-03 1.7267901e-03 1.3027778e-03 1.3633707e-03 2.6503970e-03 1.5759024e-03 2.1731558e-03 1.7887862e-03 5.7263386e-03 8.5148931e-04 1.6383081e-03 1.3458097e-03 3.3594814e-03 3.6389958e-03 2.2301817e-03 2.5225544e-03 4.3534713e-03 3.1787209e-03 1.6954559e-03 1.3061945e-03 5.4111697e-03 1.7803664e-03 4.1741292e-03 7.9197469e-03 2.2821957e-03 5.7150571e-03 5.2344879e-03 5.9478982e-03 9.4860449e-03 3.0218268e-03 3.9157169e-03 7.0424278e-03 7.6315025e-03 4.1845322e-03 4.7751866e-03 8.3019483e-03 3.6628005e-03 5.6294536e-03 2.4239872e-03 2.7647556e-03 9.2421646e-04 1.7874511e-03 2.0292522e-03 1.9168834e-03 1.9458286e-03 3.1481713e-03 1.3908818e-03 1.3324310e-05 1.1782972e-03 5.7639460e-03 3.2058410e-03 2.4140579e-03 2.0956665e-03 3.1463866e-03 1.2261330e-03 4.0134358e-03 1.5054952e-03 1.1240475e-03 4.7360391e-04 2.7309553e-03 5.1301270e-03 2.4137049e-03 8.9922816e-05 1.2592279e-03 8.6164876e-04 1.2616309e-03 1.6936265e-03 3.4914911e-03 6.4024254e-04 8.0798030e-04 1.9823430e-03 2.5037231e-03 2.1768005e-03 8.4515239e-04 2.3117307e-03 2.1993248e-04 5.6812764e-04 1.3931392e-03 1.4308209e-03 1.6084351e-03 1.3403960e-03 1.4811963e-03 1.2106398e-03 2.4235208e-03 3.5787719e-04 1.6247178e-03 6.1681362e-04 9.2162383e-04 6.4491663e-04 1.4082859e-03 1.1774797e-03 1.5785221e-03 4.0122185e-04 7.3237681e-04 6.4530654e-05 2.8852212e-03 1.3991525e-03 1.5175131e-03 5.9336508e-04 2.9036861e-04 2.0099198e-04 5.5129609e-04 1.5179733e-03 6.8841417e-04 1.3866758e-03 1.2880812e-03 3.2376306e-03 2.6196338e-03 2.0860993e-03 2.5676122e-04 1.7880288e-03 9.1594192e-04 3.5976292e-03 3.2249197e-03 6.3653022e-05 8.5949029e-04 1.8263557e-03 1.6881339e-03 9.7411877e-04 1.7845049e-03 6.1330704e-04 4.6521065e-04 1.3489557e-03 1.6857799e-03 7.7549379e-04 1.3461987e-03 6.0478443e-04 1.0485877e-03 2.7784351e-03 1.1708179e-03 5.9587066e-04 1.2049026e-03 1.1592978e-03 4.9960727e-04 2.5830270e-03 2.2390567e-03 9.5053595e-04 5.2396832e-03 2.8622802e-03 2.1952710e-03 8.7416055e-04 1.1307517e-03 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..7c6b67fa43c5fef11101d28dd46f4c1b325b65ee --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-cosine-ml.txt @@ -0,0 +1 @@ + 2.5695885e-01 2.6882042e-01 2.3470353e-01 2.9299329e-01 2.2742702e-01 3.1253572e-01 2.4986352e-01 3.0770122e-01 2.5191977e-01 2.7931567e-01 2.8133743e-01 2.6316239e-01 2.6067201e-01 3.2982339e-01 2.8993002e-01 2.5506356e-01 2.8728051e-01 2.4952121e-01 2.8613379e-01 2.6894157e-01 2.3606353e-01 2.1670935e-01 2.3470242e-01 2.4294172e-01 2.4376454e-01 2.3228195e-01 2.3554918e-01 2.4851241e-01 2.0917546e-01 2.4971488e-01 2.4264224e-01 2.7405461e-01 1.9086415e-01 2.6346574e-01 2.5908801e-01 2.2138495e-01 2.2910721e-01 2.2169919e-01 2.0660065e-01 2.3207102e-01 2.5554688e-01 2.5153751e-01 2.6073682e-01 2.0919640e-01 3.3984433e-01 2.7503792e-01 2.1709889e-01 2.7068095e-01 3.0307041e-01 2.4529612e-01 2.2987015e-01 2.7736967e-01 3.0310708e-01 3.0544316e-01 1.9205388e-01 2.7098021e-01 2.0722466e-01 2.6387343e-01 2.8998308e-01 2.2633010e-01 2.5177075e-01 1.6347011e-01 2.4036389e-01 2.6485871e-01 2.8491965e-01 2.2273619e-01 2.4511873e-01 2.5930533e-01 2.6589995e-01 2.7797191e-01 2.3357373e-01 2.4279909e-01 2.3544532e-01 1.9447286e-01 2.3993534e-01 2.0856243e-01 2.2125251e-01 2.1988206e-01 2.0590152e-01 2.6441952e-01 2.0052739e-01 2.2978496e-01 2.4483670e-01 2.3879510e-01 2.9398425e-01 2.7541852e-01 2.3777469e-01 2.9151131e-01 2.0672752e-01 2.4584031e-01 2.7475025e-01 2.7064343e-01 2.5603684e-01 2.6165327e-01 2.4233155e-01 1.7892657e-01 2.6111203e-01 1.9965682e-01 2.4201634e-01 2.6281353e-01 3.1928221e-01 1.9731963e-01 2.7752862e-01 2.2633080e-01 2.6783167e-01 2.5447186e-01 2.6424243e-01 2.1960672e-01 2.2984242e-01 2.8788736e-01 2.8681630e-01 2.6949787e-01 2.3993685e-01 2.4440073e-01 2.5010397e-01 2.3230769e-01 2.9879682e-01 2.4200592e-01 2.6957748e-01 2.6073240e-01 2.6355347e-01 2.3403674e-01 2.2411413e-01 2.2956729e-01 2.8105976e-01 2.2913304e-01 2.4898608e-01 2.3304000e-01 2.2692988e-01 2.3728251e-01 2.2552243e-01 2.0364084e-01 2.3359511e-01 2.6619167e-01 2.6666588e-01 2.3666880e-01 2.7239113e-01 2.0146697e-01 2.3045559e-01 2.1695523e-01 2.1387991e-01 2.2366404e-01 2.2809635e-01 2.0901297e-01 2.2441100e-01 2.3418882e-01 2.8552218e-01 2.4609015e-01 2.0282492e-01 2.5940295e-01 2.7407006e-01 2.3344890e-01 2.1179142e-01 2.7047821e-01 2.9832768e-01 2.0859082e-01 2.8881331e-01 1.8384598e-01 2.5286491e-01 2.2012615e-01 2.3615775e-01 2.6845565e-01 2.3356355e-01 2.7164193e-01 2.4179380e-01 2.5247973e-01 2.5637548e-01 3.2126483e-01 2.3100774e-01 2.8832546e-01 2.0043257e-01 2.7918333e-01 2.4884522e-01 2.2904723e-01 2.3738940e-01 2.9461278e-01 2.9782005e-01 3.0332073e-01 2.5175971e-01 3.1203784e-01 2.6611535e-01 2.3713507e-01 2.2203585e-01 2.3602325e-01 2.5093670e-01 2.6860434e-01 3.0137874e-01 2.3759606e-01 2.6840346e-01 1.9200556e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-double-inp.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-double-inp.txt new file mode 100644 index 0000000000000000000000000000000000000000..7a77021775ddb61d226aa8c4ba60f0af013e4a6c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-double-inp.txt @@ -0,0 +1,20 @@ +8.278938049410748956e-01 9.035293984476246987e-01 1.862188994679486731e-01 8.921151312310462433e-01 2.061859119379583216e-02 3.440636727385729676e-01 1.533779912830328662e-01 5.701372300009802663e-01 5.510020730211558915e-01 1.792362258426003496e-01 8.086175120876580857e-01 6.115487184317183189e-01 1.233471787164852618e-02 1.441643531871039663e-03 4.044309209045688913e-01 3.561398959499905148e-01 1.281985712929750720e-01 8.663300833847481508e-01 8.696027786291581352e-01 3.611727370363766454e-01 5.283537658772616830e-01 1.440241088090119526e-01 3.112457227138950566e-01 6.031280796897889873e-01 9.230324792742518047e-01 2.332121881136874908e-01 3.192652267403439659e-02 3.466206294995559656e-01 2.988687728046366399e-01 5.116749542048093513e-02 2.584975830914494344e-01 4.302023478042227289e-01 8.003972751713522849e-01 9.364931911368097328e-01 9.737098649964673891e-01 4.718038453972229762e-01 4.526591686607864817e-01 1.056485678520797666e-01 5.883019714285405710e-01 3.846092237676981274e-01 6.461500053435473845e-01 1.013239729848824933e-01 1.216151561651189761e-01 5.159668929484659827e-01 8.452074473510227115e-01 9.885170962247968873e-01 7.623883073490128615e-01 2.291163243615434997e-02 5.775530980802381364e-01 7.820699896828091635e-01 8.239186345842965942e-01 3.391800105260227571e-01 9.546318451614538292e-01 3.789677917867695367e-01 4.526533399649290690e-02 8.366786473238587707e-01 3.082636811049858094e-01 1.173936820793450853e-01 7.631994969169442200e-02 2.997416650722183329e-01 5.795208655160232203e-01 3.942350892542011431e-01 1.175126383297261379e-01 4.928232513950027149e-01 9.421293996225950096e-01 8.365391053841342295e-02 6.868059693571844093e-01 3.589527962429440722e-01 7.592939427166059962e-01 5.623849466131448649e-01 2.110746828032050715e-01 9.824683704668600859e-01 2.661230142246236996e-01 6.162272315007123469e-01 5.023254536607497656e-01 5.202854476669782624e-02 5.835090668842095596e-01 7.864642118889143552e-01 2.504012386867506823e-01 6.728308641135989365e-01 4.610793534576096420e-01 4.820508770515909980e-01 9.720403251022265989e-01 3.100069285263498120e-01 7.681017126461753275e-01 7.956539306007082146e-02 2.593389637887737464e-01 1.137852590403054531e-01 3.885303073284454012e-01 8.599094660075957686e-01 5.215167875918280682e-02 1.620908248572288102e-01 1.859236090457663249e-01 6.247716512610480555e-01 3.415128495520775020e-01 7.034903368378029320e-01 6.037564640019568163e-01 2.338969434423310290e-01 1.002104885609900187e-02 7.866058403969036217e-01 +8.033694116033356369e-01 8.653264545544031572e-01 7.468340410754038539e-01 6.362430919910603278e-01 5.120006306625468628e-02 9.503348372633585450e-01 4.697732609626817935e-01 4.221305288459429317e-01 3.153452119838391354e-01 2.991014843442657556e-01 1.190667967280257811e-01 3.486567714509342109e-01 8.289493649885054660e-01 8.454811050800014049e-01 9.149673018211901265e-01 7.708707837193897738e-01 2.640157732122547785e-01 2.107897022189605396e-01 4.207633055054439408e-01 6.719500284654699174e-01 1.458031684893063007e-01 1.800412735886125493e-02 8.402733435220011149e-02 4.206760156883160295e-02 1.376933515041314227e-01 1.716717341022133692e-01 1.788220727652158892e-01 8.224310433402118869e-01 7.729093666867475898e-01 2.064223621025984556e-01 9.592092036227207741e-01 8.312490243754996344e-01 6.673289360369902834e-01 4.632847903690773261e-02 7.643954098358983762e-01 9.359341525615098023e-01 1.914966319163026176e-01 4.536590469402868031e-01 8.640836016538007147e-01 3.941529178175462444e-02 5.602101995205478469e-01 9.263806161941660067e-01 1.555995325944817820e-01 6.172208102950116348e-01 6.335576752812099866e-01 9.766975460368043649e-02 4.475795689539874278e-02 3.248842796104995934e-01 5.700377122149502540e-01 9.066962967256807504e-01 5.458460621505676347e-01 6.833401285581487405e-01 2.887244409544044155e-01 1.316338647016834784e-01 2.325673305245992140e-01 4.150121963188406760e-01 3.834845466366055833e-01 8.149365773968725302e-01 1.867003849450201702e-01 3.170322173543018707e-01 6.832093662682684476e-01 1.729728518929105618e-01 9.236557359702636250e-01 9.152941252150086360e-01 7.224879983096620384e-01 8.557920626598064517e-01 5.344883059251644974e-01 4.876873274449112783e-01 8.308277804506420949e-01 3.916624489322212410e-01 3.459695122273966916e-01 4.033512499027409604e-01 6.555726444913008155e-01 7.138452409380238173e-01 1.683937314599968094e-01 1.769382143486440961e-01 7.588683655178136700e-01 3.750589892880819010e-01 7.525176245126207197e-01 6.083961152538303052e-01 1.145972309907993258e-01 6.239614485809552580e-01 1.307655482065895880e-01 8.530458750670916190e-01 4.801602070124768584e-01 8.168122189863546989e-02 3.793139622744635675e-01 1.496986997776840189e-01 7.129023878302899186e-01 6.830979237438047358e-01 7.635375943876505644e-01 1.824004963251233402e-01 5.764695848992339444e-01 8.865113248731604223e-01 5.784337085544002388e-01 9.700026628755119562e-01 7.318207347905059112e-01 3.851401393936705331e-01 1.774291851193399161e-01 9.763423229242296220e-01 +9.287178470949695175e-01 1.748282433617460718e-01 9.238531711586964734e-01 8.291274445125006443e-01 9.513259272578692416e-01 7.486316801165745494e-01 6.257378457524477300e-01 2.062711693536473101e-01 3.970721244184766130e-01 2.738325225026445597e-01 8.735038948299954642e-01 5.415282140033768066e-01 5.176317904298315398e-01 5.347036264518250093e-01 7.482056965410627258e-01 4.140672582824351800e-01 8.709067272363142376e-01 9.499605569181273079e-01 5.380266748336398619e-01 4.369252161707162241e-01 8.235722216228258397e-03 4.308187193646527691e-01 6.030581482859224129e-01 7.316831195156517920e-01 5.540499846834291420e-01 2.044203040111662872e-01 8.645251782981867583e-01 1.816095717570278545e-01 9.639119168018674966e-01 3.572031072322333634e-01 5.580226816834680248e-01 5.586629875016585478e-01 7.213854320902782780e-01 8.513998260042524580e-01 6.308764347277173723e-02 4.299855362100638567e-01 8.789303907444128150e-01 9.178850359236285783e-01 2.275205845091231582e-01 1.899395443939643213e-01 7.103070862773533944e-01 9.450015289553428399e-01 1.691856364522159595e-01 7.368719616877857925e-01 9.600189536623833231e-01 5.128846522932454244e-01 6.209162727118655578e-02 7.992250598838029907e-01 9.141050280518014937e-01 1.471297785256820978e-01 7.466162372930541524e-01 4.656107650642931084e-01 6.399324135161845728e-01 2.023617619481610230e-01 1.019104648900100996e-01 4.390693688536728700e-02 9.822620353006089600e-01 2.881951852926285529e-01 6.191575015960482098e-02 8.989580763251467932e-01 4.635958631890454429e-01 1.781973138114967270e-02 7.906911683818984571e-02 6.525270776225711167e-02 3.620583622807886925e-01 2.651673718940715796e-01 5.829372395929610651e-01 2.118159824373908595e-01 5.900287159143694504e-01 9.405929925178391215e-01 9.262415619063500971e-01 5.639581506302312475e-01 4.529556154689695635e-02 2.873819210518682166e-01 5.718545934306838996e-01 9.877670791317306742e-01 4.120364488714320927e-01 9.896078045634184583e-01 3.796586997026456523e-01 1.178183652203194098e-01 6.641068305236120795e-01 4.045960610587706618e-03 2.262690437428437340e-01 7.839938005832693957e-01 7.695391333937223743e-01 3.713918392552509884e-01 4.245533341514018399e-01 1.475072494020331915e-01 6.011975181419888514e-01 5.158174017998343741e-01 1.788706151398071764e-01 8.880707130134481986e-01 6.463351030474082659e-01 6.499920635615744624e-01 8.570273676455353318e-01 6.055019270899113515e-01 2.123561211054603159e-02 2.027688787664126968e-01 1.930834215328548487e-01 5.131906052747271518e-01 +2.599990881903107010e-01 6.767857524909899336e-01 7.188217446352963558e-01 3.037178903357997672e-01 4.252381412838680541e-01 4.070924411439535984e-02 8.426710493038247485e-02 8.301517457289483426e-01 8.254603255702420705e-01 7.258533909453509514e-01 9.958706809470796451e-01 1.323408451651194584e-01 8.523995455245143571e-01 2.572405385832454705e-02 4.715363690065482727e-01 7.920130365690022378e-01 7.613745641534582775e-01 5.108305991695683002e-01 7.908714335912382376e-01 4.641131983754837043e-01 3.112627109831845873e-01 4.218013908715474436e-01 3.291577909008427394e-01 2.538715054071232213e-01 1.362470842487485401e-01 2.716429790290709745e-01 1.485325814161112534e-01 4.514539027544387517e-01 6.900835128673067365e-01 7.793407072946112457e-02 5.938024345270752624e-01 1.497853829906865553e-01 5.399567982652856424e-01 1.419209916759478496e-03 7.719776132867679497e-01 3.130795105576239523e-01 6.670071611167494030e-01 8.900596881158256979e-01 8.011158503301568645e-01 7.089295605187424520e-01 4.671116382997058114e-01 6.682965170673403899e-01 6.524835265739736823e-02 5.454288420771494783e-01 7.751910790556310049e-01 8.192595541387335256e-01 3.098855848167891835e-01 3.689971355659119601e-01 8.666507475054133769e-01 2.749042684253171220e-01 3.566565602478318775e-01 4.838173174723044978e-01 1.032975933616413489e-01 5.063065339610417492e-02 5.791168455729079900e-01 3.573337411289496668e-01 6.714098909652352898e-01 2.917057662433912846e-01 2.654964332620638467e-01 7.171804039048814694e-01 3.314488637898249657e-01 5.230399837442840649e-01 6.866534136026025692e-02 1.252966394621071178e-01 5.349397882659551184e-01 2.841423847455760709e-01 4.158473635710734362e-01 7.197062989831272128e-01 5.123869045047864113e-01 8.675622821594339840e-01 8.097441845042540054e-01 7.317178252133832439e-01 3.300847596465853462e-01 5.922311859141077273e-01 8.852619511417836318e-02 2.673412917259408994e-01 6.878259052441990651e-01 3.223000927116328462e-01 8.859387123976615319e-01 5.722722388300067742e-01 8.254877606669521750e-01 5.705299682290687624e-01 7.046478734972855262e-01 1.316324413616759559e-01 3.056358395675535800e-01 2.396516834600909140e-01 2.041201422493257311e-01 1.610755140653103989e-01 1.617012564641111538e-01 4.449920510036902144e-01 2.731012972755201274e-01 7.826874666257994662e-01 5.193612375350010746e-01 8.688804522977213729e-01 3.742157602758655610e-02 6.649628920608219307e-01 5.978149424619171315e-01 5.345645500553952711e-01 9.443202650415919441e-01 6.105837075491723498e-01 +6.387761328141735584e-01 4.210087412162694109e-01 3.777306694964789324e-01 3.576349403292201634e-01 7.272699618880260619e-01 9.173392803607671731e-02 1.212535698300880593e-01 3.871229381194544183e-01 7.735150198351389284e-01 4.687200483013695962e-01 5.161778571874678923e-01 9.839646447226980674e-01 8.626932748911960713e-01 9.618485576577924245e-01 2.997996427525421170e-01 3.955404657388794654e-01 8.480126027102616870e-01 8.194992325050480808e-01 2.800213436873294492e-01 7.188391466620779324e-01 2.289766105875049584e-01 3.838547514028287644e-01 1.363553401061209369e-01 2.131328253542326134e-01 2.666779468144075960e-02 3.252883844200405994e-01 4.207860197469600605e-01 2.991365385037647595e-01 9.180779845534067229e-01 8.787338732192649937e-01 5.404510999105649471e-01 1.735493827761729335e-01 7.405224640747264386e-01 3.927355563629583157e-01 3.957109873399460298e-01 1.313029813325972128e-01 6.434498219738993274e-01 7.162213694578050127e-01 6.454998257494671821e-01 3.808124530008022424e-01 2.027201015737234435e-01 6.667632842770417900e-01 1.609491052365198405e-01 1.192413785409307536e-02 4.546773323526854815e-01 7.733541911050207940e-01 3.902525737195561284e-01 4.006023779897505133e-01 5.156517815815246930e-01 6.135685498584592112e-01 7.062153114980724844e-01 5.505858882117883324e-01 3.541308807182554919e-01 5.237151122342533771e-01 5.230649229131387745e-01 1.973541027697351957e-01 7.940327858595511712e-01 9.998588700623055603e-01 3.878271015153827994e-01 4.455006584967207139e-01 8.376414508056347907e-01 3.310833863524501597e-01 8.020469097392601832e-01 1.890327633084128989e-01 3.830289472395409511e-01 8.605040171046141051e-02 9.978185524023941433e-01 8.333890591892906263e-01 4.509013468741837061e-01 6.355778557686052599e-01 1.422515991097305088e-01 9.549891485963732940e-01 7.535776302868563148e-01 9.306005301880662106e-01 2.444330347211679522e-01 5.828218427569508142e-01 1.261938242968304591e-01 2.829188731405173352e-01 8.100246952078660190e-01 2.032739130996042975e-01 3.997268448390065565e-01 3.882777703107541667e-01 1.102505652624736765e-01 5.826634725328041498e-01 6.508734477956333864e-01 1.777287661702166011e-01 4.857051012052149286e-02 6.850537712379254351e-01 5.012281307761055071e-01 3.329154880061502286e-01 5.006261767216675374e-01 4.542081454976160115e-01 6.777801995399822532e-01 4.271303586474960445e-01 7.820470659692947413e-01 5.143462618485082904e-01 4.071273891563575997e-02 8.503383643856671226e-01 6.877485768345151795e-01 6.498843855014626580e-01 +5.539512747016193117e-01 6.329206647391879548e-01 2.798533500321682688e-01 4.825977295850051307e-01 7.625297023172977751e-01 9.081309101427640362e-01 4.124792086535029600e-01 3.647019658319609059e-01 7.529595202332928228e-02 3.072404010876803593e-01 7.890673660964639957e-01 4.079781478915127657e-01 1.440519120695739064e-01 2.538968953804546791e-01 1.595028243568367143e-01 9.066545851872198636e-02 6.367601114674349416e-01 7.622263643880089479e-02 3.015728236404162654e-01 2.424070469873378375e-01 5.711440390241000475e-01 5.717001375511508998e-01 2.237853674032181939e-01 7.112101625753678436e-01 4.321054197012103026e-01 2.505322169010260058e-02 5.877307077139551916e-01 4.415771174397812304e-01 3.766022855145171322e-01 9.803490652619811785e-01 1.229258314111529860e-01 8.108351868714478439e-01 8.558595456964329662e-01 2.168217533833206589e-01 2.034022719386595623e-01 8.687457137579783772e-01 9.013327195854559104e-01 8.156766512673154779e-01 2.717576187546973943e-01 1.756417893371479133e-01 7.555856977566548505e-01 6.708809351312817748e-01 8.998789237886926085e-01 1.936367585946979775e-01 7.949724635465026390e-01 3.164799312763589834e-01 5.493048513173155456e-01 1.608917269168268493e-01 3.048667492191803330e-01 5.599401537727016764e-01 5.779501360842279611e-01 1.296714605309662316e-01 9.160752328055997706e-01 8.058674476110374574e-01 4.385508937505578908e-01 9.212419718012100356e-01 2.249887451242467140e-01 6.283927745352599903e-01 3.778992451536005159e-01 3.571958698867505611e-03 7.276526470528231760e-01 9.051678673805297892e-01 8.465837072484881931e-01 4.548317505393462135e-02 3.189318261926020748e-01 4.446388607398673587e-01 4.292356336344156365e-01 4.203980977718795309e-01 4.698059253071955599e-01 6.151991200848159203e-01 8.479986139404802614e-01 9.870993262459623052e-01 3.164206525899861955e-01 6.464672171639846976e-01 8.508781429592480183e-01 4.733667503354813677e-01 8.076014176740163863e-01 6.671443255679101458e-01 6.639213267047979761e-01 3.681688930741919830e-01 4.679870252651611162e-01 1.790041740686979521e-01 8.446070273663058847e-01 3.350737544979878191e-01 6.600272349677447359e-01 4.356083218487936115e-01 7.995134167346013010e-01 9.083660261041469619e-01 9.743975306734570241e-01 8.144839650654719376e-01 6.865011984586443239e-01 1.709747281999153268e-01 8.534933687161740945e-01 9.494753729726415070e-01 8.140124992294850426e-01 8.936241255316055287e-01 9.087976860818796077e-01 9.030687493451383663e-02 4.025785149840914734e-01 9.592005611533803711e-01 +5.714058727476275523e-01 7.913573761505965365e-02 9.301773447377043036e-01 4.302822433307075256e-01 4.618892554175407783e-01 1.882471300213742760e-01 6.231472878215863487e-01 2.350437450940777717e-01 8.483410480771292894e-01 8.580803842040533036e-01 4.246398783388435350e-01 5.667321565946502604e-01 7.247417018955526480e-02 5.373984417482219333e-01 8.794242091541510931e-01 9.699025554453030162e-01 8.254197752548814160e-01 7.739723972867470492e-01 6.365819416181199841e-01 3.451230687021222820e-02 1.829102490094791644e-02 9.179618383026147965e-01 4.481667270072077214e-01 4.771270250445739380e-01 1.588469404953456454e-01 3.766332499200618633e-01 5.057026248713025751e-02 9.125900914275182352e-01 8.438133644246305076e-01 3.282972411719701222e-01 6.042003956122835584e-01 7.423456085393266290e-01 1.389012737541106546e-02 3.674754266702850991e-02 2.126646727703802586e-01 3.085666164246750887e-01 4.303440338750976757e-01 1.749037978865556342e-01 2.177699993322510519e-01 6.675614739991906355e-01 1.926533336347433512e-01 8.032010572660308600e-01 4.611412981769049679e-01 9.907201268457492827e-01 8.973785930837320235e-01 6.286342392657409128e-01 8.111266245859546364e-01 1.154230969025437092e-01 8.382880466301794176e-01 1.053753927827069115e-01 9.921712862234919328e-01 9.041662667920956631e-01 3.626267376021269362e-01 2.262225368932846425e-02 8.669003741626111204e-01 7.597054897704164089e-01 4.700318514995387442e-01 4.338185014241978665e-01 1.205425463362067573e-01 2.413879270602589111e-01 5.483334840461459025e-01 2.042653841254596925e-01 5.452588940366013270e-01 3.164646091706100339e-01 1.878958248945691301e-01 2.188622304737641855e-01 2.970982599823450698e-01 5.952148400199362976e-01 9.614251220149501176e-01 5.446813400697393392e-01 5.900748097930779146e-01 2.653062526715309621e-01 5.459933097767216692e-01 3.174185404661935550e-01 1.412133354129242457e-01 1.487441669790685594e-01 3.953776242211952674e-01 5.274261039692862418e-01 1.756132307607755072e-01 4.481942852746899630e-01 6.390660088765629521e-01 2.860380430081067571e-01 5.866902519902850166e-03 3.026687645174785946e-02 1.952533570196290924e-01 2.154769096186736066e-01 8.920573593276575064e-01 5.644513191915436767e-01 5.551464696654353492e-01 4.378199413349500579e-01 8.685737643974280608e-01 7.493934764293597173e-02 9.556749726352036234e-01 6.386433482536227890e-01 8.714694524097754691e-02 1.722786161701279628e-01 6.526867532768643176e-01 8.950304705281527662e-01 6.158198776753203152e-01 9.587176904005377809e-01 +7.705718397401561948e-01 3.165816092999733655e-01 4.334200859975760878e-01 8.639807015515663657e-01 5.576514209532534849e-03 2.456745447057938625e-01 1.664686313299922338e-01 9.637084729617834133e-01 1.083448720752323569e-01 1.865218070380464388e-01 3.730358890475884426e-01 5.015351872138350542e-01 7.420710795841709562e-01 4.919420674769692248e-01 3.426558201886464872e-02 8.669984854934246199e-01 2.204243734202966376e-01 4.109792246853891662e-01 4.361732572946559472e-01 6.819306998053020763e-02 9.986304248057148447e-01 4.119289455392274313e-01 8.533050041845835487e-01 3.416914861912183632e-01 6.522191951039880697e-01 4.162803668786793088e-01 9.051674379917418189e-02 4.552378661306888397e-02 2.122677193466918633e-01 7.461518531655018105e-01 4.654688019259497489e-01 7.877564083548750373e-01 4.518328005682387127e-01 7.173857464237374248e-01 6.940056370290903498e-02 2.804574410412373764e-01 6.095681113112718652e-01 3.680596478602831123e-01 1.814569150719304025e-01 6.505055517979729807e-01 2.759585245701871026e-01 1.429501104786028431e-01 7.813891153083207808e-02 8.925314279991185540e-01 6.692056941902108091e-01 1.915141341107173822e-01 5.750233129581091562e-01 2.051961006251528108e-01 3.849013692629975614e-01 9.503788222043518807e-01 7.690419386411734282e-01 9.978147530014782607e-01 1.719584162437415298e-01 4.890758882401113894e-01 7.195660736040896399e-01 2.485818040997200828e-01 9.706486601870933928e-01 5.182604282071262558e-01 8.082072245463804983e-01 4.889961284821118248e-01 8.042893959057633158e-01 3.200685313413229593e-01 8.983245016887355661e-01 2.811495336955205371e-01 3.986095833814048417e-01 8.607229214132059436e-01 4.827620119717191960e-01 6.715610252037491623e-01 9.330824374137768329e-01 7.537710530085762750e-01 9.840804224010484269e-01 2.319352541177217564e-01 9.569114943157627229e-01 5.821928104654411351e-01 6.700479524814679788e-01 5.663434680086896211e-01 8.851091082101365526e-01 6.800562815862243315e-01 3.578475213752868589e-01 2.900164669281133367e-01 8.379170683569914235e-02 9.929972839740475177e-02 5.946248553621906741e-01 1.991332889320840405e-01 8.115065723822508792e-01 2.023388190440008616e-01 4.056545651129230823e-01 2.966825350250481552e-01 7.457176343507545546e-01 9.856015771246517954e-01 2.264338016147812160e-01 8.366528670045663141e-01 6.116829813603242849e-01 2.605933184296719274e-01 5.765962146558850643e-01 5.064075092266390188e-01 5.499615769589756287e-01 9.240234698632640020e-01 7.169900155229913530e-02 3.544181364560751168e-01 +8.154844535553099627e-01 4.797965609394789777e-01 7.476703385713100447e-01 9.086708404761600910e-01 3.191752505450355937e-01 7.611128630021511965e-01 6.246790343299296611e-01 1.942001426217137006e-01 2.789860414631386565e-01 3.236359785042408621e-02 3.178191288741717413e-01 8.372264298357038337e-01 8.872692914664047636e-01 9.589758852077276963e-01 3.123722260380168425e-01 8.980164015338999439e-01 7.260784140459818348e-01 6.567013512265649222e-01 1.028743505926521529e-01 6.821705410750319443e-01 6.889838995316139858e-01 5.587525493094736007e-02 6.921487028366646310e-01 3.616312929861494885e-01 1.673758008792780583e-01 6.626504595920326146e-01 9.125680913222075086e-01 1.424077784972291871e-01 6.508496429060767197e-01 6.615417385775157477e-01 9.654167310675311198e-01 5.536662974550183858e-01 7.092622144968085962e-03 6.694595400455760625e-01 1.828533619119211417e-01 3.421514408394116247e-01 1.242580151818144518e-01 9.888774797458224075e-01 9.777955172739735135e-01 4.271370765628749178e-01 1.211608384809655936e-01 1.580132417172936954e-01 3.242705395708289640e-01 3.268994391754735940e-01 5.213767653645562383e-03 4.475169480357120699e-01 9.593245219293577986e-01 6.994304536782350867e-01 7.063863152769014331e-01 8.381620829497931080e-01 2.760441799736219615e-01 3.755200946645842475e-01 3.627729621737311172e-01 9.518310606719182498e-01 3.577273025276901386e-01 3.991159901003488164e-01 4.187060513068554535e-01 7.422605403637314581e-01 6.697944269780702342e-01 6.020599837037767799e-01 1.571185850817550245e-01 7.519860911185742847e-01 6.635775704496444938e-01 9.487848173531471252e-01 7.900030232338028924e-01 4.143783957270819052e-01 5.618429740858444932e-01 3.737804619062014000e-01 6.179941187802344693e-01 6.553638605616040058e-01 1.009709416658691739e-01 4.935037098582963910e-01 5.485489972455533936e-01 1.024147956480448984e-01 1.195764707555347917e-01 4.910516327810896531e-01 3.551185778630389089e-01 3.857601645798814927e-01 2.074975219600547760e-01 2.084038664460790002e-01 5.268616653491025037e-01 6.948014877618717833e-01 6.179744044618615817e-01 7.063658085955483168e-01 7.925757227686872630e-01 6.199016959584816577e-01 1.163676037434490107e-01 7.425752264755586252e-01 5.403115665133301215e-01 2.546191951391015840e-01 6.961300925345208501e-01 4.003013072125547467e-01 5.906120962720950995e-02 5.879915846330325824e-01 1.213602408288709800e-01 3.801780679842765576e-01 1.731477742402802722e-01 4.624568816669496485e-01 3.304453744619206823e-01 8.810445876116090869e-02 +5.140190515373614932e-01 1.419225260054487459e-01 7.777845802285945354e-01 3.327562899409282071e-01 8.916875699762913943e-01 7.212852862736146564e-01 5.727327199433507321e-01 5.897820225918504189e-01 7.318614954542906892e-01 7.393985144455500480e-01 4.531340740296823100e-01 9.903061584426188224e-01 4.213350938331624773e-01 4.542342471963995987e-01 9.788786426453045530e-01 1.881707000343846303e-02 8.005433413647761176e-01 1.523502822273363755e-01 5.630164732287495921e-01 5.946603842470724599e-01 1.225547698678740582e-01 1.531136594724622491e-01 8.157973612638946825e-02 2.752046015644330490e-01 6.809045821946161370e-01 6.455289724528190387e-01 3.830356726830793646e-01 4.446144649678575034e-01 4.969038423960672191e-01 5.497873820641221432e-01 9.471879627821714331e-01 5.933046675329255448e-01 4.099233758501530378e-02 5.790409810134594659e-01 9.546095885251496549e-01 2.608616052375664074e-01 6.910160339170060562e-01 1.293709850476291168e-01 6.407264616302255078e-03 6.186037089828009261e-01 5.537861302543241049e-01 3.527421038298221845e-01 8.033232052121624944e-01 8.128114152830284711e-01 8.319982582278713235e-01 5.939566376046836460e-01 2.291090283499520597e-01 5.438101817725821130e-01 6.881146379117278888e-01 2.421968586304659166e-01 5.874047918543783275e-01 6.210102709484541794e-01 7.041387566450251212e-01 6.959223476278774134e-01 9.133877300988062498e-01 9.230647706207778525e-01 6.856884219815310155e-01 6.997988808693775820e-01 6.177944932528769417e-01 5.512902545683161515e-01 5.818280341729102911e-01 6.538267999985679646e-01 6.946673485935980219e-01 4.817938258357623571e-02 9.352008817207906333e-01 4.774162142215661042e-01 5.768063588692976529e-01 4.589648891483899540e-02 7.998946815651652997e-01 4.434260476954369201e-01 9.850053510925722566e-01 6.648626681529369309e-01 4.606293826856903140e-01 3.309042418210563774e-01 1.438901922508034614e-01 7.986559119276418484e-01 7.037818421334554042e-01 3.605119534240813772e-01 3.785959549258922641e-01 9.562491516841659100e-01 4.997955143590974147e-01 1.029540300938682762e-01 1.819017177001992502e-01 3.665425750262368831e-01 1.688063588370778412e-01 7.030735208313992901e-01 8.922375654244527610e-01 1.055706412056253152e-01 2.664739907746691561e-01 9.906029568647586325e-01 6.043845090140997911e-03 3.495786295043534775e-01 5.989441999519146131e-01 6.776147193866479679e-01 7.012991789852640601e-01 1.825838783477321536e-01 7.612293578749116385e-01 1.564769891240175292e-01 2.762157292905387251e-01 7.641900040015234818e-01 +4.746013333880729768e-01 7.609202966712714788e-01 2.537820854162747830e-01 1.709362234877408460e-01 1.886635378734374813e-01 2.439567014093724229e-02 7.640304718272151741e-01 3.483216170435471382e-01 7.744289278738043514e-01 4.190437573644867353e-01 5.319091476394965934e-02 8.580130976087452233e-01 6.259446446786639529e-01 8.793213970773006150e-01 2.441023074890465994e-01 7.753405549489799098e-01 8.760187573193888300e-01 5.946480724009295393e-02 2.873093046571124631e-01 8.710837851946537924e-01 9.103181731924696596e-01 6.534637257615111272e-01 4.128420398577182793e-01 4.905858108576378607e-01 6.178275806701372108e-02 6.368043900016381320e-01 2.865296941219959148e-01 6.371773028539067241e-01 4.924322796636745325e-01 1.709313290387282080e-01 1.856892551689268700e-01 9.592782603102242289e-01 5.402593764193130976e-02 7.287312244390512506e-01 5.679467572000697073e-01 6.255587794305905724e-02 3.069660218141317953e-01 1.089960430557104232e-01 5.550748245336984965e-01 2.555948886689661803e-01 4.140925514039996980e-01 1.180376445052062628e-01 8.832322629884041820e-01 7.784546946701487169e-02 3.177678935473182698e-01 6.681804863429485764e-02 7.047099396645268854e-01 4.133897376851528582e-01 5.600656990480865627e-01 3.883995683475501837e-01 4.459430113152932362e-01 4.214077227574740681e-01 4.763369230200156235e-01 2.701480661168440545e-01 4.296286564389811824e-01 9.601402258758658936e-01 6.326999441846863359e-01 2.442086919688498670e-01 8.407708423957936938e-01 3.626867985638081437e-01 3.641441713291436733e-01 7.932397565989488530e-01 8.902073520619256941e-01 1.929173010337000838e-01 7.309376779324568973e-01 7.305852858337777977e-01 6.510197444582447313e-01 9.512661608643838695e-01 8.461467164366111016e-01 9.245490147941206605e-01 2.658844813385705663e-01 9.538758859344749208e-01 8.215517204998477041e-01 8.217795540390903097e-01 7.569662091300560780e-01 6.262685322871274218e-01 5.597770510574888725e-01 8.155720175123675197e-01 8.545688745180864965e-01 8.986051518529034610e-01 2.477911506572628708e-01 8.462580108996445860e-01 6.065941220995090255e-01 6.500490804973033665e-01 1.120463882674053169e-01 9.299049132942927010e-02 1.388364074229719858e-02 5.901199124540731367e-01 2.795110110544174464e-02 1.644097083463245124e-01 5.341029647603202646e-01 5.276816677181681570e-01 5.439849107754858304e-01 5.371677986392331405e-02 4.515163125788429488e-01 5.036243367087100964e-01 5.721818679625961801e-01 5.271368612400184617e-03 7.720961020546839304e-01 9.015383457479009266e-01 +8.301526916287945701e-01 8.704609696144033348e-01 2.955689129581380303e-01 1.762209253489944727e-01 2.698172933050072553e-01 1.138095349991521399e-01 4.092588531860634760e-01 8.202978121681584467e-01 2.822241377079557356e-01 6.117376205659387223e-01 7.169923068016897938e-01 9.310256256264415331e-02 3.989664052931106708e-01 1.651874953308862803e-02 7.890202597932294282e-02 9.068686774810821305e-01 5.203866694486933842e-01 4.297748572844445336e-01 5.208786995443430712e-01 2.163224881365530816e-01 7.274307306357226111e-01 1.675784956180090823e-01 5.969822786565782691e-01 8.959750832846602453e-02 1.253794151891943764e-01 5.352628522116801291e-01 2.562706125890066300e-01 6.030433202137867044e-01 8.330717547440393833e-01 9.603613683422040914e-02 7.569714244468559450e-01 3.184801677796517128e-01 1.667069341164499896e-01 3.132470247801235619e-01 6.417752836394801097e-01 6.433909425912354152e-02 4.056860213146201710e-01 3.166772891331335327e-01 9.574059746098845247e-01 1.492907964460536974e-01 8.311513764927496162e-01 6.652928354977717396e-01 2.396804722185036374e-01 5.812361618600220270e-01 9.724228681350225445e-01 2.853983236378453414e-01 5.337719354896472979e-01 6.779446197712412081e-01 5.485102006140557540e-01 9.010109155962182648e-01 5.724439967467525037e-01 5.965540527411405947e-01 1.598667990086183321e-01 1.363934512727023041e-01 5.327536522697270405e-01 4.123866715061276222e-01 4.617251396918636841e-01 6.935944951381239898e-01 4.300337419593377453e-01 1.892407993760835128e-01 1.666936825594794724e-01 4.625634184864588772e-01 4.805197636774838355e-02 7.003542850133466224e-01 2.130226006716084974e-03 8.678863343041013367e-01 4.874478520451258623e-01 7.043560228741558848e-01 6.317719270475393722e-01 5.372392256296196766e-01 2.982649812986511995e-01 1.272558612133412037e-01 2.467337555730741983e-01 6.546893200021091097e-01 6.291921159383098150e-01 8.505920470407707379e-01 4.046520490181828578e-01 3.875732096593392795e-01 8.551517214319142024e-01 4.152602284179877090e-01 9.587779137989138611e-01 6.977437468944928112e-01 3.240620775541913634e-02 4.025873770391376061e-01 5.485549335619134270e-01 7.146066156157020455e-01 3.012702534568838519e-01 3.526414480395153594e-01 3.309707144485515284e-01 4.315687014460974913e-01 6.641934530697197747e-01 2.172886798352815507e-01 4.807480925564590057e-01 5.006795397998469177e-01 5.818100901154411586e-01 2.107716091585690732e-01 6.606606051140029301e-01 9.317629042790995797e-01 9.840326342340242061e-01 5.752000964817773898e-01 +9.843444595454536872e-01 1.339523968066913540e-02 6.082172659959028671e-03 7.828244785439336662e-01 5.069653703872761819e-01 2.804896494365415327e-01 2.112385836660957139e-02 6.016479440778699228e-02 7.457477935084961818e-01 3.445503949245375397e-01 4.063494277166557200e-01 8.630275274433116817e-01 5.948396018456146850e-01 1.400867933474212457e-01 6.997522422654076646e-01 5.766519767930851081e-01 5.419976500582250889e-01 7.474121304089603735e-01 2.951600193008566686e-01 7.980170422334191827e-01 1.829036799578199757e-01 6.317636496261300749e-01 2.812612231140887431e-02 5.464747656105657381e-01 3.909873503320924204e-01 4.940850205957293406e-01 8.157850130814222611e-01 5.111092739445756150e-01 9.336823640685747439e-01 7.157105167170837445e-01 7.778989455994214097e-01 1.398722535910470466e-01 5.642653936300449091e-01 3.218717164845980028e-01 9.717427501967056402e-01 3.665791984428700134e-01 3.874321311211759156e-02 9.437600858738082188e-02 5.679526822961932231e-01 5.141385991358327079e-01 7.497840799582222715e-02 5.736515309094968318e-01 1.928132849879083954e-01 6.924244068001785823e-01 1.748389677952593146e-01 4.469577663506929532e-01 1.738527450963387455e-01 7.195287763517190793e-01 8.861150811892871682e-01 1.058443750714600506e-01 1.941789362229970894e-01 9.188374820700584422e-02 7.706736301449305104e-01 6.718642548609364828e-01 5.981029087121966237e-01 4.832880127232569434e-01 3.073688779938709148e-01 5.156312334804930009e-01 1.777418420119527553e-01 8.885462205165685079e-01 4.486254681289014723e-02 1.345398129556140132e-01 7.467627984379916484e-01 4.384565546058830643e-01 7.217750080760946263e-01 3.949550352625393890e-01 4.307950907642028593e-01 6.087680934849041270e-01 3.294516167246774874e-01 1.316682090209408962e-01 1.824857738754404046e-01 5.332379826483617524e-01 3.567136182864261151e-02 1.976220743086236631e-01 5.849349042822560296e-01 1.133174406357483344e-01 7.711522754393199675e-01 8.557306786807005183e-01 3.038353471344266143e-01 4.422747047768413875e-01 2.537160404215925702e-01 2.372714099723788328e-01 5.906462765375103396e-01 4.849909323133470007e-01 2.692576210504484813e-01 4.540849506602829821e-01 9.664935719107857759e-01 2.044371576459835804e-01 4.505417469690352616e-01 7.110722322201217249e-01 3.051357995214963870e-01 8.978937034341526457e-01 6.090501112506481185e-01 6.595415779178889215e-01 6.565426836745864581e-01 6.565608489824376059e-01 2.679102664248229626e-01 3.819533138204529443e-01 6.609794961162380744e-01 2.289558446859882856e-01 +9.274935298374649140e-01 1.174096651033715855e-01 3.030761852629033637e-01 1.605508209527917174e-01 9.601854834873225775e-01 4.341959513718630648e-01 6.320768160802121560e-01 4.213429090614078110e-01 3.695553969042019160e-01 5.965457437116089556e-01 3.520335041155040479e-01 7.702703502247409961e-01 8.571112772962534709e-01 7.904077282532658844e-01 2.247339318352784554e-01 6.823720204503556097e-01 5.883435710582129996e-02 6.786037033312407596e-01 9.721137137641507886e-01 2.042576970668320557e-01 8.394085754806240862e-01 7.433082729552867862e-01 4.072614159870893147e-01 7.451483066617257123e-01 1.699472962789440045e-01 1.753052015584344314e-01 2.255269204788400428e-01 7.794755643807432799e-01 8.407732260470973662e-01 9.301182862857163558e-01 3.701995309382508648e-01 4.481909027604019657e-01 1.261889085033987001e-01 5.600591735875248833e-01 8.244692493969552061e-01 8.969188061645969601e-01 4.802217973423368313e-01 3.556164122713412201e-02 3.393317823164623270e-01 2.491242957582864292e-01 9.863253789366602797e-01 5.585415885291432625e-01 3.702350606362231344e-01 6.766101432620400535e-01 6.999259389475386284e-01 6.676108316872160220e-01 7.870681827507105544e-01 8.746765411259082024e-01 9.125268371282718727e-01 6.638849997061806452e-01 3.253268113800632522e-01 7.968625619248901337e-01 7.584122525443606211e-01 9.028886850768532701e-01 5.381622293189292083e-02 8.097562873320752752e-01 7.092942088208666895e-01 9.915538877968065323e-01 4.319294903327922652e-01 4.307127933969153721e-01 2.768507739641907772e-01 8.076253078288621046e-01 2.569233696442670967e-01 7.595893829724666979e-01 5.768081727897018673e-01 2.537536777625452045e-01 8.874419624636734616e-01 5.091705681832693342e-01 4.811826624992353585e-01 2.794462461940371290e-01 3.846927898276129021e-01 5.129562951959991679e-01 8.515004062224775794e-01 7.103144978683579858e-01 9.526388607201888847e-01 2.367905569592337889e-01 9.137336039323161740e-01 5.722969943101696710e-02 2.019723935481291255e-01 3.098764675203513619e-02 1.121146613918624357e-01 9.937693067724532314e-01 8.476717958861412772e-02 2.059652110343795917e-01 2.139791918759540446e-01 9.137860316709250919e-01 9.530862653366889425e-03 2.027843281683039400e-03 2.506229951837134484e-01 6.244523528392044165e-01 5.523937894075592325e-01 3.712168074031840792e-01 4.218847794299319665e-01 4.827576239387890711e-01 5.244634168840578425e-01 5.182241092381567604e-01 3.308639956263292881e-03 9.370528021570383448e-01 4.694554875029453012e-01 4.950447554541728135e-01 +1.525818111800841814e-01 4.708012184002630107e-02 3.899035965341954846e-01 3.928304521031263929e-01 5.602286661727436945e-01 9.738256658043862313e-01 9.404465779766183475e-01 5.750862754958349088e-01 9.547546956257608741e-01 2.750275291553152535e-01 1.682773435862793265e-01 5.865928471016079726e-04 8.543378154943809255e-01 3.547649971465383079e-01 5.058056647397523031e-01 9.116332486700751137e-02 7.534666421106954726e-01 3.082429494433007733e-01 4.527145111847344916e-01 5.456680635225539255e-01 2.504131242494785914e-01 2.509240770568589296e-01 3.949236999582302898e-01 8.782959620323271821e-03 2.474641132111736752e-01 8.229417958971670943e-01 3.444225768479134420e-01 4.000027489436257522e-01 4.247741954177396417e-01 2.497745404169693373e-02 4.325768602588443423e-01 7.336592463477830117e-01 7.667663267650381975e-02 4.179022553581047683e-01 8.745172741480690126e-01 9.417705509525042817e-02 2.807522782799587446e-01 8.212710101351362590e-01 2.211181944001613386e-01 4.319929503523877168e-01 1.858636923768219873e-02 6.737037795085246694e-01 7.997187114913413275e-01 2.976552505976116647e-01 3.272347030789168887e-01 5.550935453236346406e-01 9.224109746648162522e-01 3.192827922106745708e-01 3.500098324549234530e-01 7.821988386980260888e-01 4.478417135239194380e-01 1.580956175222456572e-01 5.300807813550156844e-01 5.806154798468634581e-01 9.456842911054151868e-01 7.688127895655872956e-01 8.456527833650537840e-01 1.784229089865225770e-01 8.114517450321339087e-01 8.062506298824222428e-01 2.113482500442499523e-01 2.629226789210241666e-01 6.478686221690072022e-01 6.006672861605766300e-02 7.013679843242253131e-01 8.784753961212666828e-01 3.487138165323044880e-02 4.928426758517070461e-01 5.976224683315064512e-01 7.629063997052759616e-01 2.761721278953045422e-01 7.240740503283805696e-01 6.131065729985127888e-01 1.630885615792579957e-01 8.473783868551159060e-01 8.347614542399306448e-02 8.137265626844719657e-01 8.512508664918938539e-01 2.777097816703766320e-01 1.729154355214796990e-01 2.203382750835449766e-01 6.134780912629795857e-01 3.524352564238901753e-01 5.370314860129862256e-01 8.013986113284543578e-02 2.555842138998117852e-01 6.553915758947851389e-01 9.679125599178584061e-01 2.549566319678178150e-01 4.008180804370896633e-01 9.145789951670967310e-01 2.787926039163850511e-01 8.599455912576436933e-02 9.637558000691170967e-02 8.274101203974880692e-01 1.803747268179315411e-01 2.175735407836230095e-01 7.825994939720237742e-01 7.928519890958951599e-02 8.707949373106749213e-01 +6.398420210047787160e-01 5.739624494012524059e-01 3.359672805578653998e-01 1.130399363175038641e-02 3.349439685346782269e-01 2.315484030880912147e-01 4.575228302577399875e-01 1.149494135594463229e-01 2.888244352925943836e-01 3.625470995156252485e-01 3.795973190611611203e-01 6.567047810450010736e-01 1.484039742710284715e-01 9.273251916560719676e-01 4.334256728976307871e-01 6.734771102219323513e-01 9.125080197222198430e-01 4.974393931097168542e-01 8.301481563280355136e-01 4.526450714147856047e-01 2.414236092573898151e-01 8.070129698367667359e-02 7.260400697427102923e-01 1.396509691839398215e-02 2.496450588391967429e-01 4.335741205447194435e-01 3.089314419194891803e-01 9.543503534526003307e-01 5.457977547458532364e-01 3.139663643587058406e-01 5.034762326753475792e-01 4.756788330475764104e-01 6.849334942793482428e-01 3.880666613022351052e-01 6.483446580176778218e-01 5.217503801099343530e-01 5.371145824070304720e-01 3.121260159429154468e-01 8.314121854062171968e-01 4.538695969561833410e-01 8.598896961203845724e-01 9.961993522734106099e-01 8.865717795946430613e-01 7.828987966783660379e-01 3.412415531643435695e-01 7.421170530151157685e-01 4.484104178639959359e-01 6.793217012099640462e-01 3.756179958191659951e-01 7.821287098222597933e-01 6.227726265188193722e-02 8.552983413221663112e-01 4.824668768009222619e-01 2.241531065858231031e-01 4.939536577599041856e-01 5.129566641128722182e-01 1.057984177672518511e-01 9.541452507300716146e-01 3.396646181755047511e-01 7.452588103611947901e-01 5.315559265659929311e-01 5.493475179850665358e-01 5.214824278139198466e-01 5.150075718147916204e-01 1.196075368500321146e-01 9.035665331176232495e-01 7.522653903639873185e-01 6.638708679914825384e-01 5.584174553800479446e-01 5.015819402508836511e-01 5.507698483308445248e-01 5.978677577011723976e-01 8.450418028759657529e-01 3.266677322748618995e-01 1.321610045897971819e-01 2.394354042746985600e-01 2.723972163557076831e-01 5.523301747352814539e-01 5.518043850608547185e-01 5.283968096837132755e-02 8.192733312104071297e-01 2.277106024970321219e-02 1.414998099027269252e-01 6.517281615256080851e-01 1.811694734825117781e-01 9.472370614713256920e-01 5.454497319021770485e-01 1.364119913158231556e-01 8.446142008509562871e-01 7.671725984742419069e-01 2.461161648406858804e-01 1.421724627107351369e-01 6.290652581179481118e-01 7.094144689448004248e-01 4.419656923472803367e-02 6.614741876652251440e-01 8.712193265403500586e-02 4.734931280852430202e-01 5.382037050480286133e-01 1.396459758005891283e-01 +9.709329844415439670e-01 8.998575745276288229e-01 9.151313462895852568e-01 6.920489275523904471e-01 2.892231405199537919e-01 6.750679746268205550e-01 5.515642485826798280e-01 1.065253097812824956e-01 2.957026803465776510e-01 8.937347659632134400e-01 9.800016515925590310e-01 7.745900896182087436e-01 1.570977683146633774e-01 1.482028765821026273e-01 2.111147779712029271e-01 9.683759902485811200e-01 6.550951580826911425e-01 8.728324682592377703e-01 5.044803166579884257e-01 8.285704754811143991e-01 1.693574499337324735e-02 6.032669995180495182e-02 1.687026879086964692e-01 7.701554026145973619e-01 1.429888016593102718e-01 5.881172815379975827e-02 9.704206919487038396e-01 4.450807650730836951e-01 1.597445784258376689e-01 9.849229394397314152e-01 4.220083573536804744e-01 9.357693600374825671e-01 2.313199262338369033e-01 4.556443403861323294e-01 2.590791012828855822e-01 8.438664994487065085e-01 5.519045677502344427e-01 4.702170125676508050e-01 6.814723205638187897e-01 7.418295483665861001e-01 3.684921032028853904e-01 1.501895844844561845e-01 4.214513377519605308e-01 8.600279963652578408e-01 6.625616611189292238e-01 5.200151456470966105e-01 7.881072743086801058e-01 2.771703241081423519e-01 9.034135930616548071e-01 5.848441705791300738e-01 8.341698181274771473e-01 1.966638677318299777e-01 7.059747894371543042e-01 7.013854316067694716e-01 1.828430942760242983e-01 4.745548949934464966e-01 6.306422394641082452e-01 7.760751707194470939e-01 9.813187212598396547e-01 2.293595795266353266e-01 7.749261876107090830e-01 2.384106107787011819e-01 9.721209688979495223e-01 2.715569353686980714e-01 2.915573577694993146e-01 3.579601509630966349e-01 3.085697512342830962e-01 4.070219981627976047e-01 1.989632411372218579e-01 7.330003339460906542e-01 5.397259604481572381e-01 6.931009942216573849e-01 1.385457419653816080e-01 1.140339999976658358e-01 3.980752590866034613e-01 9.471822621683767540e-01 5.476643721405823895e-01 6.824131903515884279e-02 5.844099130744569992e-01 2.346881692012994236e-01 9.436439228519653000e-01 4.855518260479008141e-02 8.157036123302675579e-01 1.169761256455048581e-01 5.532962903488753970e-01 1.100965596251435308e-01 9.789490602992410029e-01 8.433487462016989733e-01 1.272410782852178013e-01 2.885715258680641160e-01 7.990943955388217779e-01 1.565305358979097727e-01 9.160846960406943129e-02 8.521842244411678147e-01 4.474243106736998099e-01 3.843945818845087015e-01 4.710645906071458944e-01 2.398348154123419729e-01 6.435351435258193087e-01 7.656897921129046658e-01 +4.894328120406804539e-01 7.881019629214267574e-01 6.974585354155089512e-01 2.023858939857701156e-01 1.660984914264745926e-01 4.854517801734643534e-01 2.789848572630315715e-01 2.311636522410289718e-01 9.821076233980715608e-01 1.220641257408076052e-01 2.614036146663852866e-01 7.657560715165320220e-01 3.968360577545695378e-01 4.566023622802184434e-02 1.049701948619241598e-02 9.281162949127452766e-01 4.490137965769909201e-01 2.095846458383606725e-01 9.195504656719085679e-01 9.683515436855471004e-01 9.800174878114910060e-01 5.517610861380117804e-01 6.711570559348770670e-01 5.125258050287277989e-01 2.105581493613526423e-01 8.281813206544574868e-01 4.964783994807770995e-01 7.284974208756571645e-01 1.320629592816270348e-01 6.652194518096135045e-01 9.430156297917950958e-01 7.477263137894260003e-01 2.054087806450300979e-01 4.248209124837907247e-01 7.657518666018259257e-02 1.031614100713345028e-01 4.122242287567021712e-01 4.919658859336810686e-01 3.752650167259050651e-01 4.175771429986683270e-01 6.131376293448997927e-01 5.463797405837259591e-01 3.119918548921774004e-01 6.331762507678504459e-01 5.484632429281035559e-01 6.815448032785871302e-01 8.065695507425107991e-02 8.720129122297424207e-01 8.318188557125294480e-03 2.199323537180564170e-02 8.933872719887463454e-01 1.953120287872067706e-02 2.478721941404590234e-01 5.994061179859005994e-01 6.588362611693047155e-01 6.332808851020984564e-01 3.823849348043323326e-01 5.111091324899629251e-01 7.034808459110406531e-01 4.347681568463539481e-01 4.316973576672314961e-01 9.620411080123215664e-01 6.247837467655984467e-01 8.196961678222113301e-01 5.574601810887074294e-01 8.800635018469276094e-01 8.772255241161972528e-01 5.075275933138404527e-01 8.022583187266906224e-01 2.320670802521890286e-01 1.165626629103270195e-01 4.623759662685936744e-01 7.938327000737943617e-02 7.986374689793115378e-01 6.728842751465858862e-01 8.133909095059230765e-01 1.202639390769081329e-01 1.052937257108800262e-01 8.717600467040409473e-02 2.163819956545051104e-01 6.596483385763984852e-01 1.202843170392309258e-02 1.538789195854695091e-01 3.120247727263308901e-01 3.408168327248596308e-01 3.241861797851740556e-01 3.637074533655986208e-01 1.533669345890729119e-01 4.455921334699539660e-01 5.619140093874478437e-01 1.881731359879111887e-01 9.416670800570559052e-01 1.740018593664415247e-01 7.030242331869680505e-01 5.922055553954849172e-01 9.326211623391688077e-01 6.608322881013140027e-01 7.009721551241574478e-01 1.079126054675583202e-01 6.158176671761947940e-01 +5.185079639625639336e-01 9.613742991518259284e-01 5.555312825626229634e-01 2.647628827924735084e-01 6.003697207460141350e-01 5.392112376769145898e-01 6.781186965667050925e-01 9.908971748181496508e-01 4.124155872095397468e-01 9.814941401724619485e-02 2.684237785531295994e-02 1.774652505962848181e-01 1.707589529595294753e-01 4.640932098465534450e-01 2.882179883914587348e-01 7.276822905806898945e-01 6.145789546745269449e-01 1.100959863917608805e-01 6.798859723042820491e-01 9.096984032948918220e-01 3.971368455178179158e-01 2.959494950971321980e-01 3.742088799298171065e-02 1.960739526210202310e-01 7.536102695342027369e-01 6.680915510628401277e-01 4.136507204312135366e-01 3.613996339406737590e-01 3.605422038261204554e-01 7.098503555159476619e-01 8.093719147087541366e-01 6.344097009128880638e-01 3.990082448083617228e-01 2.805918009906902544e-01 7.078488167363675698e-01 9.969917259866583059e-01 9.442054998992396309e-01 1.329075240769165278e-01 6.810681350588387861e-02 8.503491437913293094e-01 8.347117439165431252e-01 2.381858201903953587e-01 7.884260706938626129e-01 7.109907917419661105e-01 6.390916681983604963e-02 6.174365227062991179e-01 5.085733343630816083e-01 1.716846139694149231e-01 9.065664924270055991e-02 5.625330757196970177e-01 3.539663480209681579e-01 8.937139525947165319e-01 3.981380511900556307e-02 7.403597927449541150e-01 3.803872284089604427e-02 6.729519695709765825e-01 5.306080908840085097e-01 2.091237680402112664e-01 5.902903662907804661e-01 2.094778612095482551e-01 7.323447855684165342e-01 3.644574495843493356e-01 2.006215478057034041e-01 3.737617545555030896e-01 5.253471759602216240e-01 4.287889547869583318e-01 7.086098806190446187e-01 4.510792335515292351e-01 6.383187180169215269e-01 8.779355722397681472e-01 4.221338898667141848e-01 6.375840144651815367e-01 8.683057298299173832e-01 6.093730356952498095e-01 9.297141161056151626e-01 7.770838342807246946e-01 6.549661287008456956e-02 2.835060738158660110e-01 4.474138867374952699e-01 8.530336387421445510e-01 3.160209657891883683e-01 8.301538680518486535e-01 6.646903097549101691e-01 7.187130118106234145e-01 1.651862041735395747e-01 9.578252676762609719e-01 6.490273812885494209e-02 9.777273484666341163e-01 8.930729829254262508e-01 9.851054752118463265e-01 4.094323402286751401e-01 1.139176240124337713e-01 7.612865863899589414e-01 2.266379302491570158e-01 6.998882496157835531e-01 9.945043379099228753e-01 7.111578056749194854e-01 7.806190603886183910e-01 3.410170920712443099e-01 9.446084168886822452e-01 +5.015172758330755931e-01 5.569527971282052237e-01 1.122406928736449094e-01 8.960352822124777461e-01 6.049568585854003810e-02 1.202196001338627918e-01 1.870314295763603196e-01 9.017590029396971296e-01 3.597904628087450485e-01 2.130941062746317671e-01 2.556281834629479111e-01 5.123669364829196438e-01 4.754061129282013409e-01 9.764470380372083369e-01 8.038663983900646848e-01 6.960491266420890666e-01 2.940135977911654264e-01 2.857282759910040326e-03 4.599343225832352999e-02 5.597554495210212977e-01 7.445266674304001908e-01 3.387528030535971180e-01 6.429542922125383031e-01 2.123331785532429627e-01 5.302332654117811739e-01 7.262555377662539557e-01 3.982425859900724507e-01 3.243388301740235402e-01 6.191064123738921898e-01 8.988047781373914580e-01 7.819700328765150088e-01 7.664269102804815992e-01 6.734095355422575757e-03 2.904762329148526945e-01 5.097537644843168625e-01 9.524734606001823423e-01 4.812869576591960463e-01 6.236868013640477493e-01 1.459170943214320726e-01 9.874505139403206844e-01 7.561708982837871407e-01 3.798591332432484924e-01 6.056633451375117438e-01 7.935708170258731764e-01 1.458141583518740569e-01 7.082511296391911237e-01 1.098798009731616343e-02 3.655618484905173160e-01 9.551862303858617009e-01 8.148959351152762487e-02 4.739306219219985294e-02 7.963357515359494876e-01 6.208332695202813944e-01 3.884182264923189409e-01 4.589167647950288531e-01 6.496652974138312775e-01 2.467528128074852889e-01 5.309593064844935206e-01 5.364606369543487574e-01 2.421352989851309756e-01 3.776834556696828660e-02 1.564861233558080267e-01 5.197231021782636740e-01 8.725375120634637494e-01 2.441225493455024820e-01 2.320363366041028330e-01 5.026358683423555185e-01 7.035766000474735771e-01 8.347805591467084563e-01 2.303229841813967393e-01 6.908373419683054850e-01 2.646662377366995056e-01 1.259467197942290007e-01 9.372770922994989595e-01 6.674216272867254940e-01 1.027944489143072238e-01 5.686267290346079806e-01 3.948222804451942958e-01 4.689706944496729868e-01 4.446117700449114807e-02 6.817992275557515081e-01 9.084821829413957106e-01 9.184021015315092518e-01 3.045815734169987632e-01 2.204958624923980537e-03 7.542672057172502553e-01 9.460844786545006269e-01 3.373139094575949848e-02 9.059565314915285494e-01 9.938525461318854504e-01 2.542072661725306437e-01 9.685734112479216229e-02 8.223629541824816203e-01 1.057429056898460118e-01 8.080679390260248063e-01 5.823014244609205914e-01 6.413551528031806725e-01 1.787341975438894170e-01 1.250471413912357388e-01 8.390281297596062782e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..86de3c7592893bdb4438c9a0e7e60b2e7e5e1727 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt @@ -0,0 +1 @@ + 5.3851648e-01 5.0990195e-01 6.4807407e-01 1.4142136e-01 6.1644140e-01 5.1961524e-01 1.7320508e-01 9.2195445e-01 4.6904158e-01 3.7416574e-01 3.7416574e-01 5.9160798e-01 9.9498744e-01 8.8317609e-01 1.1045361e+00 5.4772256e-01 1.0000000e-01 7.4161985e-01 3.3166248e-01 4.3588989e-01 3.0000000e-01 6.4807407e-01 4.6904158e-01 5.9160798e-01 5.4772256e-01 3.1622777e-01 1.4142136e-01 1.4142136e-01 5.3851648e-01 5.3851648e-01 3.8729833e-01 6.2449980e-01 8.0622577e-01 4.6904158e-01 3.7416574e-01 4.1231056e-01 4.6904158e-01 8.6602540e-01 1.4142136e-01 1.7320508e-01 1.3490738e+00 7.6811457e-01 4.5825757e-01 6.1644140e-01 5.9160798e-01 3.6055513e-01 5.8309519e-01 3.0000000e-01 2.2360680e-01 4.0037482e+00 3.6166283e+00 4.1641326e+00 3.0935417e+00 3.7920970e+00 3.4161382e+00 3.7854986e+00 2.3452079e+00 3.7496667e+00 2.8879058e+00 2.7037012e+00 3.2280025e+00 3.1464265e+00 3.7000000e+00 2.5806976e+00 3.6276714e+00 3.4351128e+00 3.0099834e+00 3.7682887e+00 2.8827071e+00 3.8535698e+00 3.0757113e+00 4.0472213e+00 3.6578682e+00 3.4161382e+00 3.5972211e+00 4.0472213e+00 4.2449971e+00 3.5312887e+00 2.4939928e+00 2.8178006e+00 2.7018512e+00 2.8948230e+00 4.1352146e+00 3.4117444e+00 3.5199432e+00 3.9115214e+00 3.6180105e+00 3.0000000e+00 3.0215890e+00 3.3120990e+00 3.5958309e+00 3.0099834e+00 2.3874673e+00 3.1527766e+00 3.0740852e+00 3.1256999e+00 3.3451457e+00 2.0904545e+00 3.0577770e+00 5.2848841e+00 4.2083251e+00 5.3018865e+00 4.6904158e+00 5.0566788e+00 6.0950800e+00 3.5916570e+00 5.6364883e+00 5.0477718e+00 5.6391489e+00 4.3566042e+00 4.5199558e+00 4.8538644e+00 4.1904654e+00 4.4170126e+00 4.6260134e+00 4.6454279e+00 6.2401923e+00 6.4984614e+00 4.1412558e+00 5.1215232e+00 4.0286474e+00 6.2112801e+00 4.1097445e+00 4.9699095e+00 5.3122500e+00 3.9774364e+00 4.0074930e+00 4.8404545e+00 5.0970580e+00 5.5461698e+00 6.0141500e+00 4.8805737e+00 4.1605288e+00 4.5705580e+00 5.7887823e+00 4.8918299e+00 4.6065171e+00 3.8961519e+00 4.7968740e+00 5.0199602e+00 4.6368092e+00 4.2083251e+00 5.2573758e+00 5.1361464e+00 4.6540305e+00 4.2766810e+00 4.4598206e+00 4.6508064e+00 4.1400483e+00 3.0000000e-01 3.3166248e-01 6.0827625e-01 1.0908712e+00 5.0990195e-01 4.2426407e-01 5.0990195e-01 1.7320508e-01 8.6602540e-01 4.5825757e-01 1.4142136e-01 6.7823300e-01 1.3601471e+00 1.6278821e+00 1.0535654e+00 5.4772256e-01 1.1747340e+00 8.3666003e-01 7.0710678e-01 7.6157731e-01 7.8102497e-01 5.5677644e-01 6.4807407e-01 2.2360680e-01 5.0000000e-01 5.9160798e-01 5.0000000e-01 3.4641016e-01 2.4494897e-01 6.7823300e-01 1.1489125e+00 1.3416408e+00 1.7320508e-01 3.0000000e-01 7.8740079e-01 1.7320508e-01 5.0990195e-01 4.5825757e-01 5.2915026e-01 8.1853528e-01 5.4772256e-01 6.7823300e-01 9.8488578e-01 1.4142136e-01 8.4852814e-01 3.6055513e-01 8.1240384e-01 3.1622777e-01 4.0963398e+00 3.6864617e+00 4.2367440e+00 2.9698485e+00 3.8118237e+00 3.3911650e+00 3.8600518e+00 2.1470911e+00 3.7881394e+00 2.8053520e+00 2.4617067e+00 3.2449961e+00 3.0413813e+00 3.7121422e+00 2.5592968e+00 3.7000000e+00 3.4336569e+00 2.9715316e+00 3.6918830e+00 2.7928480e+00 3.8935845e+00 3.0740852e+00 4.0187063e+00 3.6565011e+00 3.4467376e+00 3.6510273e+00 4.0804412e+00 4.2953463e+00 3.5383612e+00 2.4186773e+00 2.7000000e+00 2.5787594e+00 2.8548205e+00 4.1170378e+00 3.3985291e+00 3.5972211e+00 3.9786933e+00 3.5580894e+00 2.9983329e+00 2.9291637e+00 3.2434549e+00 3.6221541e+00 2.9546573e+00 2.1794495e+00 3.1032241e+00 3.0789609e+00 3.1144823e+00 3.3645208e+00 1.9131126e+00 3.0298515e+00 5.3385391e+00 4.1809090e+00 5.3572381e+00 4.7085029e+00 5.0911688e+00 6.1595454e+00 3.4799425e+00 5.6868269e+00 5.0408333e+00 5.7471732e+00 4.4192760e+00 4.5210618e+00 4.9020404e+00 4.1340053e+00 4.4022721e+00 4.6808119e+00 4.6829478e+00 6.3694584e+00 6.5314623e+00 4.0620192e+00 5.1903757e+00 4.0024992e+00 6.2617889e+00 4.1060930e+00 5.0428167e+00 5.3898052e+00 3.9812058e+00 4.0311289e+00 4.8518038e+00 5.1584882e+00 5.5919585e+00 6.1546730e+00 4.8918299e+00 4.1689327e+00 4.5475268e+00 5.8600341e+00 4.9598387e+00 4.6508064e+00 3.9153544e+00 4.8600412e+00 5.0724747e+00 4.7021272e+00 4.1809090e+00 5.3207142e+00 5.2067264e+00 4.7000000e+00 4.2497059e+00 4.4988888e+00 4.7180504e+00 4.1533119e+00 2.4494897e-01 5.0990195e-01 1.0862780e+00 2.6457513e-01 4.1231056e-01 4.3588989e-01 3.1622777e-01 8.8317609e-01 3.7416574e-01 2.6457513e-01 5.0000000e-01 1.3638182e+00 1.5874508e+00 1.0099505e+00 5.1961524e-01 1.2369317e+00 7.5498344e-01 8.3066239e-01 7.0000000e-01 5.0990195e-01 6.4807407e-01 6.4031242e-01 4.6904158e-01 5.0990195e-01 6.1644140e-01 5.4772256e-01 3.0000000e-01 3.3166248e-01 7.8102497e-01 1.0535654e+00 1.2845233e+00 3.1622777e-01 3.1622777e-01 8.5440037e-01 3.1622777e-01 3.6055513e-01 4.8989795e-01 4.3588989e-01 9.2736185e-01 3.0000000e-01 6.5574385e-01 9.5916630e-01 2.6457513e-01 7.8102497e-01 1.4142136e-01 8.0622577e-01 3.3166248e-01 4.2766810e+00 3.8496753e+00 4.4158804e+00 3.1543621e+00 3.9974992e+00 3.5510562e+00 4.0112342e+00 2.3065125e+00 3.9749214e+00 2.9495762e+00 2.6476405e+00 3.4029399e+00 3.2588341e+00 3.8794329e+00 2.7202941e+00 3.8807216e+00 3.5749126e+00 3.1527766e+00 3.8961519e+00 2.9782545e+00 4.0311289e+00 3.2588341e+00 4.2071368e+00 3.8314488e+00 3.6318040e+00 3.8340579e+00 4.2731721e+00 4.4698993e+00 3.7027017e+00 2.6153394e+00 2.8879058e+00 2.7712813e+00 3.0364453e+00 4.2825226e+00 3.5298725e+00 3.7322915e+00 4.1545156e+00 3.7669616e+00 3.1464265e+00 3.1032241e+00 3.4073450e+00 3.7854986e+00 3.1400637e+00 2.3537205e+00 3.2680269e+00 3.2326460e+00 3.2726136e+00 3.5425979e+00 2.0856654e+00 3.1953091e+00 5.4726593e+00 4.3347434e+00 5.5290144e+00 4.8682646e+00 5.2469038e+00 6.3364028e+00 3.6083237e+00 5.8660038e+00 5.2249402e+00 5.8940648e+00 4.5738387e+00 4.6936127e+00 5.0695167e+00 4.2918527e+00 4.5442271e+00 4.8270074e+00 4.8456166e+00 6.5207362e+00 6.7178866e+00 4.2508823e+00 5.3488316e+00 4.1436699e+00 6.4467046e+00 4.2813549e+00 5.1942276e+00 5.5587768e+00 4.1496988e+00 4.1856899e+00 5.0149776e+00 5.3385391e+00 5.7775427e+00 6.3126856e+00 5.0537115e+00 4.3416587e+00 4.7169906e+00 6.0406953e+00 5.0921508e+00 4.8062459e+00 4.0669399e+00 5.0269275e+00 5.2287666e+00 4.8682646e+00 4.3347434e+00 5.4753995e+00 5.3535035e+00 4.8641546e+00 4.4305756e+00 4.6615448e+00 4.8487112e+00 4.2988371e+00 6.4807407e-01 1.1661904e+00 3.3166248e-01 5.0000000e-01 3.0000000e-01 3.1622777e-01 1.0000000e+00 3.7416574e-01 2.6457513e-01 5.1961524e-01 1.5297059e+00 1.7146428e+00 1.1661904e+00 6.5574385e-01 1.3228757e+00 8.6602540e-01 8.7749644e-01 8.0622577e-01 7.0710678e-01 6.4807407e-01 5.3851648e-01 4.2426407e-01 5.4772256e-01 7.2111026e-01 6.7823300e-01 1.7320508e-01 2.2360680e-01 8.7749644e-01 1.1704700e+00 1.4247807e+00 3.1622777e-01 5.0990195e-01 1.0049876e+00 3.1622777e-01 3.0000000e-01 5.8309519e-01 6.0827625e-01 8.3666003e-01 3.0000000e-01 7.0000000e-01 9.6953597e-01 2.6457513e-01 8.6602540e-01 1.4142136e-01 9.2195445e-01 4.5825757e-01 4.1773197e+00 3.7336309e+00 4.3058100e+00 2.9849623e+00 3.8729833e+00 3.3926391e+00 3.8897301e+00 2.1118712e+00 3.8548671e+00 2.7784888e+00 2.4515301e+00 3.2680269e+00 3.1080541e+00 3.7376463e+00 2.5806976e+00 3.7762415e+00 3.4205263e+00 3.0000000e+00 3.7496667e+00 2.8160256e+00 3.8923001e+00 3.1304952e+00 4.0620192e+00 3.6851052e+00 3.5114100e+00 3.7229021e+00 4.1545156e+00 4.3497126e+00 3.5623026e+00 2.4698178e+00 2.7202941e+00 2.6038433e+00 2.8913665e+00 4.1279535e+00 3.3674916e+00 3.6069378e+00 4.0422766e+00 3.6262929e+00 2.9966648e+00 2.9376862e+00 3.2357379e+00 3.6482873e+00 2.9899833e+00 2.1633308e+00 3.1080541e+00 3.0838288e+00 3.1224990e+00 3.4132096e+00 1.9157244e+00 3.0446675e+00 5.3357286e+00 4.1773197e+00 5.4064776e+00 4.7222876e+00 5.1097945e+00 6.2153037e+00 3.4205263e+00 5.7384667e+00 5.0813384e+00 5.7844619e+00 4.4519659e+00 4.5530210e+00 4.9457052e+00 4.1303753e+00 4.3965896e+00 4.7010637e+00 4.7095647e+00 6.4140471e+00 6.5901442e+00 4.0877867e+00 5.2297227e+00 3.9862263e+00 6.3229740e+00 4.1436699e+00 5.0695167e+00 5.4387499e+00 4.0124805e+00 4.0472213e+00 4.8733972e+00 5.2172790e+00 5.6550862e+00 6.2153037e+00 4.9132474e+00 4.1988094e+00 4.5552168e+00 5.9321160e+00 4.9628621e+00 4.6690470e+00 3.9268308e+00 4.9101935e+00 5.1048996e+00 4.7602521e+00 4.1773197e+00 5.3497664e+00 5.2325902e+00 4.7455242e+00 4.2883563e+00 4.5332108e+00 4.7191101e+00 4.1496988e+00 6.1644140e-01 4.5825757e-01 2.2360680e-01 9.2195445e-01 5.2915026e-01 4.2426407e-01 3.4641016e-01 6.4031242e-01 9.7467943e-01 9.1651514e-01 1.0862780e+00 5.4772256e-01 1.7320508e-01 7.9372539e-01 2.6457513e-01 5.3851648e-01 2.6457513e-01 5.6568542e-01 5.2915026e-01 5.7445626e-01 6.3245553e-01 3.4641016e-01 2.4494897e-01 2.8284271e-01 5.3851648e-01 5.7445626e-01 5.0000000e-01 5.5677644e-01 7.8102497e-01 5.2915026e-01 4.4721360e-01 5.1961524e-01 5.2915026e-01 8.5440037e-01 2.4494897e-01 1.7320508e-01 1.4000000e+00 7.2801099e-01 4.5825757e-01 5.8309519e-01 6.4031242e-01 3.0000000e-01 5.6568542e-01 3.3166248e-01 3.0000000e-01 4.0607881e+00 3.6633318e+00 4.2190046e+00 3.1480152e+00 3.8496753e+00 3.4568772e+00 3.8249183e+00 2.3874673e+00 3.8078866e+00 2.9223278e+00 2.7586228e+00 3.2710854e+00 3.2186954e+00 3.7456642e+00 2.6267851e+00 3.6851052e+00 3.4669872e+00 3.0626786e+00 3.8340579e+00 2.9376862e+00 3.8845849e+00 3.1336879e+00 4.1036569e+00 3.7067506e+00 3.4741906e+00 3.6551334e+00 4.1085277e+00 4.2965102e+00 3.5763109e+00 2.5573424e+00 2.8740216e+00 2.7604347e+00 2.9495762e+00 4.1785165e+00 3.4380227e+00 3.5510562e+00 3.9648455e+00 3.6864617e+00 3.0364453e+00 3.0708305e+00 3.3541020e+00 3.6400549e+00 3.0659419e+00 2.4372115e+00 3.1968735e+00 3.1128765e+00 3.1670175e+00 3.3985291e+00 2.1424285e+00 3.1032241e+00 5.3131911e+00 4.2461747e+00 5.3507009e+00 4.7307505e+00 5.0960769e+00 6.1457302e+00 3.6166283e+00 5.6877060e+00 5.1009803e+00 5.6762664e+00 4.3977267e+00 4.5683695e+00 4.9010203e+00 4.2308392e+00 4.4508426e+00 4.6626173e+00 4.6882833e+00 6.2785349e+00 6.5536250e+00 4.1964271e+00 5.1643005e+00 4.0607881e+00 6.2657801e+00 4.1605288e+00 5.0079936e+00 5.3591044e+00 4.0249224e+00 4.0472213e+00 4.8836462e+00 5.1497573e+00 5.6017854e+00 6.0572271e+00 4.9234135e+00 4.2083251e+00 4.6141088e+00 5.8438001e+00 4.9203658e+00 4.6454279e+00 3.9344631e+00 4.8445846e+00 5.0616203e+00 4.6861498e+00 4.2461747e+00 5.2971691e+00 5.1730069e+00 4.7010637e+00 4.3301270e+00 4.5044423e+00 4.6786750e+00 4.1737274e+00 9.9498744e-01 7.0000000e-01 1.4594520e+00 1.0099505e+00 3.4641016e-01 8.1240384e-01 1.1618950e+00 1.5716234e+00 6.7823300e-01 6.1644140e-01 4.0000000e-01 5.9160798e-01 3.3166248e-01 3.8729833e-01 5.3851648e-01 4.1231056e-01 1.1224972e+00 6.7823300e-01 8.3066239e-01 1.0099505e+00 6.4807407e-01 5.2915026e-01 6.4807407e-01 1.0148892e+00 1.0246951e+00 5.3851648e-01 4.5825757e-01 4.7958315e-01 1.0099505e+00 9.6953597e-01 6.0827625e-01 1.0099505e+00 1.4177447e+00 6.4807407e-01 7.0000000e-01 1.8814888e+00 1.3000000e+00 6.0827625e-01 3.7416574e-01 1.1269428e+00 3.8729833e-01 1.1224972e+00 3.6055513e-01 8.0622577e-01 3.6124784e+00 3.2465366e+00 3.7868192e+00 2.9444864e+00 3.4698703e+00 3.1543621e+00 3.4073450e+00 2.3280893e+00 3.4146742e+00 2.7055499e+00 2.7147744e+00 2.9189039e+00 2.9832868e+00 3.3896903e+00 2.3366643e+00 3.2588341e+00 3.1464265e+00 2.7784888e+00 3.5468296e+00 2.7073973e+00 3.5085610e+00 2.7928480e+00 3.7709415e+00 3.3674916e+00 3.0935417e+00 3.2465366e+00 3.7121422e+00 3.8832976e+00 3.2264532e+00 2.3194827e+00 2.6758176e+00 2.5729361e+00 2.6608269e+00 3.8470768e+00 3.1400637e+00 3.1448370e+00 3.5411862e+00 3.3867388e+00 2.7239677e+00 2.8407745e+00 3.1032241e+00 3.2726136e+00 2.7892651e+00 2.3748684e+00 2.9223278e+00 2.7910571e+00 2.8548205e+00 3.0347982e+00 2.0566964e+00 2.8053520e+00 4.9061186e+00 3.9255573e+00 4.9223978e+00 4.3566042e+00 4.6978719e+00 5.7052607e+00 3.4263683e+00 5.2659282e+00 4.7349762e+00 5.2057660e+00 3.9774364e+00 4.2011903e+00 4.4833024e+00 3.9370039e+00 4.1146081e+00 4.2497059e+00 4.2918527e+00 5.7913729e+00 6.1343296e+00 3.9179076e+00 4.7275787e+00 3.7483330e+00 5.8360946e+00 3.8013156e+00 4.5760245e+00 4.9173163e+00 3.6633318e+00 3.6742346e+00 4.5066617e+00 4.7222876e+00 5.1788030e+00 5.5596762e+00 4.5453273e+00 3.8457769e+00 4.2883563e+00 5.3916602e+00 4.5022217e+00 4.2473521e+00 3.5693137e+00 4.4124823e+00 4.6411206e+00 4.2497059e+00 3.9255573e+00 4.8682646e+00 4.7391982e+00 4.2848571e+00 3.9887341e+00 4.1024383e+00 4.2649736e+00 3.8183766e+00 4.2426407e-01 5.4772256e-01 4.7958315e-01 8.6602540e-01 3.0000000e-01 4.8989795e-01 6.1644140e-01 1.3601471e+00 1.4933185e+00 9.5393920e-01 5.0990195e-01 1.2083046e+00 6.4807407e-01 8.6023253e-01 6.0000000e-01 4.5825757e-01 6.2449980e-01 5.4772256e-01 6.0827625e-01 4.5825757e-01 6.2449980e-01 6.0827625e-01 3.1622777e-01 4.2426407e-01 8.1240384e-01 9.4868330e-01 1.2083046e+00 4.7958315e-01 5.0000000e-01 9.1651514e-01 4.7958315e-01 4.6904158e-01 5.1961524e-01 4.2426407e-01 1.1090537e+00 3.1622777e-01 5.4772256e-01 8.1853528e-01 4.4721360e-01 6.7823300e-01 2.2360680e-01 7.7459667e-01 4.2426407e-01 4.2308392e+00 3.7854986e+00 4.3669211e+00 3.1272992e+00 3.9560081e+00 3.4899857e+00 3.9344631e+00 2.2781571e+00 3.9357337e+00 2.8827071e+00 2.6495283e+00 3.3361655e+00 3.2634338e+00 3.8209946e+00 2.6627054e+00 3.8353618e+00 3.4942810e+00 3.1160873e+00 3.8794329e+00 2.9495762e+00 3.9420807e+00 3.2202484e+00 4.1701319e+00 3.7828561e+00 3.5916570e+00 3.7907783e+00 4.2391037e+00 4.4147480e+00 3.6414283e+00 2.5980762e+00 2.8653098e+00 2.7549955e+00 2.9983329e+00 4.2225585e+00 3.4423829e+00 3.6414283e+00 4.1024383e+00 3.7549967e+00 3.0740852e+00 3.0626786e+00 3.3555923e+00 3.7229021e+00 3.1064449e+00 2.3388031e+00 3.2140317e+00 3.1654384e+00 3.2093613e+00 3.4957117e+00 2.0639767e+00 3.1400637e+00 5.3758720e+00 4.2638011e+00 5.4680892e+00 4.7989582e+00 5.1710734e+00 6.2801274e+00 3.5312887e+00 5.8137767e+00 5.1797683e+00 5.8077534e+00 4.4977772e+00 4.6368092e+00 5.0049975e+00 4.2272923e+00 4.4609416e+00 4.7423623e+00 4.7780749e+00 6.4397205e+00 6.6708320e+00 4.2190046e+00 5.2744668e+00 4.0620192e+00 6.3992187e+00 4.2284749e+00 5.1137071e+00 5.4963624e+00 4.0902323e+00 4.1121770e+00 4.9477268e+00 5.2886671e+00 5.7314920e+00 6.2401923e+00 4.9849774e+00 4.2871902e+00 4.6626173e+00 5.9883220e+00 4.9939964e+00 4.7318073e+00 3.9912404e+00 4.9618545e+00 5.1526692e+00 4.8031240e+00 4.2638011e+00 5.3972215e+00 5.2678269e+00 4.7968740e+00 4.3840620e+00 4.5934736e+00 4.7497368e+00 4.2178193e+00 7.8740079e-01 3.3166248e-01 5.0000000e-01 2.2360680e-01 4.6904158e-01 9.0553851e-01 1.0440307e+00 1.2369317e+00 7.0000000e-01 2.0000000e-01 8.3666003e-01 4.2426407e-01 4.4721360e-01 3.7416574e-01 6.7082039e-01 3.8729833e-01 4.4721360e-01 4.1231056e-01 2.2360680e-01 2.2360680e-01 2.2360680e-01 3.7416574e-01 3.7416574e-01 4.4721360e-01 7.3484692e-01 9.4868330e-01 3.3166248e-01 3.6055513e-01 5.4772256e-01 3.3166248e-01 7.4833148e-01 1.0000000e-01 2.4494897e-01 1.2288206e+00 6.6332496e-01 4.2426407e-01 6.0827625e-01 4.6904158e-01 4.2426407e-01 4.5825757e-01 4.2426407e-01 1.4142136e-01 3.9648455e+00 3.5623026e+00 4.1170378e+00 2.9866369e+00 3.7296112e+00 3.3256578e+00 3.7282704e+00 2.2113344e+00 3.6918830e+00 2.7802878e+00 2.5690465e+00 3.1543621e+00 3.0545049e+00 3.6249138e+00 2.4959968e+00 3.5818989e+00 3.3481338e+00 2.9206164e+00 3.6837481e+00 2.7820855e+00 3.7815341e+00 3.0049958e+00 3.9686270e+00 3.5791060e+00 3.3555923e+00 3.5454196e+00 3.9912404e+00 4.1892720e+00 3.4554305e+00 2.4020824e+00 2.7110883e+00 2.5942244e+00 2.8089144e+00 4.0509258e+00 3.3181320e+00 3.4583233e+00 3.8613469e+00 3.5383612e+00 2.9137605e+00 2.9189039e+00 3.2093613e+00 3.5242020e+00 2.9206164e+00 2.2561028e+00 3.0577770e+00 2.9899833e+00 3.0397368e+00 3.2771939e+00 1.9697716e+00 2.9698485e+00 5.2191953e+00 4.1206796e+00 5.2478567e+00 4.6162756e+00 4.9899900e+00 6.0448325e+00 3.4741906e+00 5.5803226e+00 4.9749372e+00 5.5973208e+00 4.3000000e+00 4.4474712e+00 4.7968740e+00 4.0975602e+00 4.3358967e+00 4.5661800e+00 4.5793013e+00 6.2040309e+00 6.4420494e+00 4.0472213e+00 5.0695167e+00 3.9395431e+00 6.1587336e+00 4.0373258e+00 4.9142650e+00 5.2621288e+00 3.9051248e+00 3.9357337e+00 4.7686476e+00 5.0447993e+00 5.4927225e+00 5.9849812e+00 4.8093659e+00 4.0865633e+00 4.4833024e+00 5.7463032e+00 4.8311489e+00 4.5398238e+00 3.8223030e+00 4.7455242e+00 4.9628621e+00 4.5902070e+00 4.1206796e+00 5.2009614e+00 5.0823223e+00 4.5989129e+00 4.2000000e+00 4.3977267e+00 4.5891176e+00 4.0607881e+00 5.5677644e-01 1.2845233e+00 6.7082039e-01 4.2426407e-01 3.4641016e-01 1.7916473e+00 1.9974984e+00 1.4317821e+00 9.2736185e-01 1.6124515e+00 1.1489125e+00 1.1575837e+00 1.0862780e+00 8.3066239e-01 9.1104336e-01 8.1240384e-01 6.4031242e-01 8.3066239e-01 1.0049876e+00 9.4339811e-01 4.6904158e-01 4.8989795e-01 1.1401754e+00 1.4491377e+00 1.7029386e+00 5.5677644e-01 7.0000000e-01 1.2569805e+00 5.5677644e-01 1.4142136e-01 8.6602540e-01 8.6023253e-01 6.2449980e-01 3.1622777e-01 9.5916630e-01 1.2609520e+00 4.2426407e-01 1.1575837e+00 3.6055513e-01 1.2083046e+00 7.2111026e-01 4.3794977e+00 3.9230090e+00 4.4977772e+00 3.0886890e+00 4.0435133e+00 3.5383612e+00 4.0767634e+00 2.1794495e+00 4.0360872e+00 2.8930952e+00 2.4939928e+00 3.4336569e+00 3.2326460e+00 3.9012818e+00 2.7367864e+00 3.9711459e+00 3.5707142e+00 3.1511903e+00 3.8768544e+00 2.9427878e+00 4.0570926e+00 3.2969683e+00 4.2083251e+00 3.8457769e+00 3.6905284e+00 3.9102430e+00 4.3324358e+00 4.5287967e+00 3.7229021e+00 2.6134269e+00 2.8337255e+00 2.7184554e+00 3.0413813e+00 4.2720019e+00 3.5085610e+00 3.7920970e+00 4.2320208e+00 3.7656341e+00 3.1543621e+00 3.0561414e+00 3.3615473e+00 3.8183766e+00 3.1320920e+00 2.2293497e+00 3.2449961e+00 3.2465366e+00 3.2771939e+00 3.5860842e+00 2.0049938e+00 3.1937439e+00 5.4972721e+00 4.3104524e+00 5.5821143e+00 4.8795492e+00 5.2706736e+00 6.3953108e+00 3.5028560e+00 5.9143892e+00 5.2316345e+00 5.9757845e+00 4.6292548e+00 4.7053161e+00 5.1176166e+00 4.2485292e+00 4.5276926e+00 4.8692915e+00 4.8774994e+00 6.6174013e+00 6.7557383e+00 4.2071368e+00 5.4074023e+00 4.1158231e+00 6.4984614e+00 4.2965102e+00 5.2488094e+00 5.6258333e+00 4.1677332e+00 4.2083251e+00 5.0259327e+00 5.4009258e+00 5.8300943e+00 6.4265076e+00 5.0645829e+00 4.3588989e+00 4.6968074e+00 6.1155539e+00 5.1322510e+00 4.8383882e+00 4.0853396e+00 5.0892043e+00 5.2735187e+00 4.9386233e+00 4.3104524e+00 5.5235858e+00 5.4064776e+00 4.9142650e+00 4.4294469e+00 4.7010637e+00 4.8887626e+00 4.3023250e+00 7.8740079e-01 3.4641016e-01 1.7320508e-01 7.2801099e-01 1.3114877e+00 1.5556349e+00 1.0099505e+00 5.0000000e-01 1.1000000e+00 7.5498344e-01 6.2449980e-01 7.0000000e-01 7.7459667e-01 5.2915026e-01 5.1961524e-01 2.0000000e-01 4.4721360e-01 5.0990195e-01 4.4721360e-01 2.6457513e-01 1.7320508e-01 6.5574385e-01 1.0440307e+00 1.2609520e+00 0.0000000e+00 3.4641016e-01 7.5498344e-01 0.0000000e+00 5.5677644e-01 3.7416574e-01 5.0000000e-01 9.3808315e-01 5.5677644e-01 6.5574385e-01 8.8317609e-01 2.6457513e-01 7.4161985e-01 3.4641016e-01 7.2801099e-01 2.6457513e-01 4.0435133e+00 3.6359318e+00 4.1856899e+00 2.9478806e+00 3.7709415e+00 3.3421550e+00 3.8065733e+00 2.1307276e+00 3.7389838e+00 2.7748874e+00 2.4556058e+00 3.2031235e+00 3.0133038e+00 3.6619667e+00 2.5258662e+00 3.6523965e+00 3.3852622e+00 2.9223278e+00 3.6687873e+00 2.7586228e+00 3.8457769e+00 3.0364453e+00 3.9799497e+00 3.6027767e+00 3.4014703e+00 3.6055513e+00 4.0348482e+00 4.2497059e+00 3.4942810e+00 2.3874673e+00 2.6720778e+00 2.5495098e+00 2.8178006e+00 4.0718546e+00 3.3496268e+00 3.5425979e+00 3.9293765e+00 3.5284558e+00 2.9495762e+00 2.9000000e+00 3.1984371e+00 3.5707142e+00 2.9189039e+00 2.1679483e+00 3.0626786e+00 3.0248967e+00 3.0675723e+00 3.3181320e+00 1.9104973e+00 2.9883106e+00 5.2924474e+00 4.1436699e+00 5.3113087e+00 4.6583259e+00 5.0467812e+00 6.1081912e+00 3.4525353e+00 5.6329388e+00 4.9979996e+00 5.6973678e+00 4.3749286e+00 4.4821870e+00 4.8600412e+00 4.1060930e+00 4.3760713e+00 4.6411206e+00 4.6324939e+00 6.3071388e+00 6.4876806e+00 4.0286474e+00 5.1468437e+00 3.9686270e+00 6.2112801e+00 4.0706265e+00 4.9919936e+00 5.3329167e+00 3.9446166e+00 3.9874804e+00 4.8114447e+00 5.1029403e+00 5.5443665e+00 6.0917978e+00 4.8538644e+00 4.1194660e+00 4.4933284e+00 5.8180753e+00 4.9142650e+00 4.5978256e+00 3.8729833e+00 4.8176758e+00 5.0338852e+00 4.6690470e+00 4.1436699e+00 5.2744668e+00 5.1652686e+00 4.6669048e+00 4.2201896e+00 4.4575778e+00 4.6722586e+00 4.1060930e+00 6.7823300e-01 9.3273791e-01 1.3674794e+00 5.8309519e-01 7.8740079e-01 3.4641016e-01 3.8729833e-01 3.8729833e-01 3.3166248e-01 3.6055513e-01 3.6055513e-01 9.4868330e-01 6.1644140e-01 7.8102497e-01 8.1240384e-01 5.4772256e-01 2.8284271e-01 3.7416574e-01 8.6602540e-01 8.5440037e-01 3.6055513e-01 4.5825757e-01 5.1961524e-01 7.8740079e-01 7.0710678e-01 3.0000000e-01 7.8740079e-01 1.2369317e+00 4.2426407e-01 5.0000000e-01 1.6792856e+00 1.1357817e+00 6.0827625e-01 5.4772256e-01 9.3273791e-01 3.3166248e-01 9.4868330e-01 1.0000000e-01 5.7445626e-01 3.8065733e+00 3.4554305e+00 3.9824616e+00 3.0708305e+00 3.6496575e+00 3.3331667e+00 3.6290495e+00 2.4124676e+00 3.5916570e+00 2.8705400e+00 2.7730849e+00 3.1176915e+00 3.0822070e+00 3.5791060e+00 2.5099801e+00 3.4496377e+00 3.3496268e+00 2.9257478e+00 3.6851052e+00 2.8372522e+00 3.7349699e+00 2.9597297e+00 3.9370039e+00 3.5411862e+00 3.2695565e+00 3.4322005e+00 3.8858718e+00 4.0841156e+00 3.4190642e+00 2.4372115e+00 2.7928480e+00 2.6795522e+00 2.8142495e+00 4.0348482e+00 3.3436507e+00 3.3778692e+00 3.7389838e+00 3.5199432e+00 2.9154759e+00 2.9849623e+00 3.2603681e+00 3.4684290e+00 2.9359837e+00 2.4494897e+00 3.0886890e+00 2.9782545e+00 3.0380915e+00 3.2140317e+00 2.1424285e+00 2.9782545e+00 5.1487863e+00 4.1243181e+00 5.1332251e+00 4.5628938e+00 4.9183331e+00 5.9118525e+00 3.5972211e+00 5.4635154e+00 4.9173163e+00 5.4497706e+00 4.2023803e+00 4.3965896e+00 4.6968074e+00 4.1255303e+00 4.3324358e+00 4.4833024e+00 4.5011110e+00 6.0282667e+00 6.3300869e+00 4.0681691e+00 4.9547957e+00 3.9560081e+00 6.0315835e+00 3.9912404e+00 4.8062459e+00 5.1283526e+00 3.8600518e+00 3.8858718e+00 4.7148701e+00 4.9173163e+00 5.3721504e+00 5.7887823e+00 4.7560488e+00 4.0336088e+00 4.4665423e+00 5.5991071e+00 4.7486840e+00 4.4631827e+00 3.7815341e+00 4.6292548e+00 4.8682646e+00 4.4698993e+00 4.1243181e+00 5.0970580e+00 4.9779514e+00 4.5033321e+00 4.1701319e+00 4.3162484e+00 4.5110974e+00 4.0323690e+00 4.5825757e-01 8.1853528e-01 1.2328828e+00 1.3638182e+00 8.6023253e-01 3.8729833e-01 9.9498744e-01 5.1961524e-01 6.0827625e-01 4.7958315e-01 6.6332496e-01 4.4721360e-01 3.0000000e-01 4.4721360e-01 2.8284271e-01 4.2426407e-01 4.4721360e-01 2.2360680e-01 3.0000000e-01 6.4031242e-01 8.1853528e-01 1.0816654e+00 3.4641016e-01 4.8989795e-01 7.6811457e-01 3.4641016e-01 6.4031242e-01 3.1622777e-01 3.8729833e-01 1.1832160e+00 5.3851648e-01 4.5825757e-01 6.1644140e-01 4.5825757e-01 5.0000000e-01 3.4641016e-01 5.9160798e-01 3.0000000e-01 3.9912404e+00 3.5637059e+00 4.1327957e+00 2.9444864e+00 3.7336309e+00 3.2848135e+00 3.7188708e+00 2.1307276e+00 3.7013511e+00 2.7166155e+00 2.5000000e+00 3.1336879e+00 3.0463092e+00 3.6041643e+00 2.4698178e+00 3.6027767e+00 3.3015148e+00 2.8948230e+00 3.6742346e+00 2.7477263e+00 3.7483330e+00 3.0033315e+00 3.9547440e+00 3.5580894e+00 3.3630343e+00 3.5608988e+00 4.0049969e+00 4.1928511e+00 3.4336569e+00 2.3874673e+00 2.6720778e+00 2.5573424e+00 2.7892651e+00 4.0174619e+00 3.2588341e+00 3.4365681e+00 3.8729833e+00 3.5369478e+00 2.8740216e+00 2.8757608e+00 3.1575307e+00 3.5057096e+00 2.8982753e+00 2.1863211e+00 3.0166206e+00 2.9546573e+00 3.0049958e+00 3.2726136e+00 1.9157244e+00 2.9376862e+00 5.1874849e+00 4.0779897e+00 5.2488094e+00 4.5891176e+00 4.9689033e+00 6.0506198e+00 3.3882149e+00 5.5812185e+00 4.9618545e+00 5.5982140e+00 4.2918527e+00 4.4305756e+00 4.7937459e+00 4.0521599e+00 4.2953463e+00 4.5497253e+00 4.5628938e+00 6.2112801e+00 6.4459289e+00 4.0162171e+00 5.0665570e+00 3.8897301e+00 6.1660360e+00 4.0236799e+00 4.9030603e+00 5.2649786e+00 3.8884444e+00 3.9115214e+00 4.7465777e+00 5.0517324e+00 5.5009090e+00 6.0041652e+00 4.7874837e+00 4.0681691e+00 4.4463468e+00 5.7645468e+00 4.8052055e+00 4.5188494e+00 3.7947332e+00 4.7486840e+00 4.9537864e+00 4.6000000e+00 4.0779897e+00 5.1903757e+00 5.0714889e+00 4.5978256e+00 4.1844952e+00 4.3874822e+00 4.5617979e+00 4.0224371e+00 5.8309519e-01 1.4317821e+00 1.6941074e+00 1.1269428e+00 6.1644140e-01 1.2569805e+00 8.8317609e-01 7.8740079e-01 8.2462113e-01 7.5498344e-01 6.5574385e-01 6.4807407e-01 3.0000000e-01 5.7445626e-01 6.5574385e-01 5.7445626e-01 3.1622777e-01 2.4494897e-01 7.8740079e-01 1.1747340e+00 1.3928388e+00 1.7320508e-01 3.6055513e-01 8.7177979e-01 1.7320508e-01 4.2426407e-01 5.1961524e-01 5.8309519e-01 7.9372539e-01 4.6904158e-01 7.6157731e-01 1.0344080e+00 2.0000000e-01 8.8317609e-01 3.0000000e-01 8.7177979e-01 3.7416574e-01 4.1785165e+00 3.7643060e+00 4.3162484e+00 3.0298515e+00 3.8897301e+00 3.4496377e+00 3.9344631e+00 2.1886069e+00 3.8639358e+00 2.8618176e+00 2.5019992e+00 3.3181320e+00 3.1064449e+00 3.7788887e+00 2.6324893e+00 3.7828561e+00 3.4942810e+00 3.0315013e+00 3.7643060e+00 2.8530685e+00 3.9623226e+00 3.1511903e+00 4.0877867e+00 3.7188708e+00 3.5242020e+00 3.7322915e+00 4.1581246e+00 4.3737855e+00 3.6083237e+00 2.4879711e+00 2.7586228e+00 2.6362853e+00 2.9240383e+00 4.1797129e+00 3.4539832e+00 3.6687873e+00 4.0583248e+00 3.6304270e+00 3.0610456e+00 2.9899833e+00 3.2954514e+00 3.6905284e+00 3.0215890e+00 2.2248595e+00 3.1638584e+00 3.1400637e+00 3.1780497e+00 3.4380227e+00 1.9748418e+00 3.0951575e+00 5.4092513e+00 4.2449971e+00 5.4350713e+00 4.7738873e+00 5.1633323e+00 6.2353829e+00 3.5256205e+00 5.7584720e+00 5.1097945e+00 5.8283788e+00 4.4977772e+00 4.5934736e+00 4.9809638e+00 4.1988094e+00 4.4743715e+00 4.7592016e+00 4.7528939e+00 6.4459289e+00 6.6075714e+00 4.1231056e+00 5.2706736e+00 4.0669399e+00 6.3364028e+00 4.1809090e+00 5.1176166e+00 5.4635154e+00 4.0558600e+00 4.1024383e+00 4.9234135e+00 5.2316345e+00 5.6683331e+00 6.2337790e+00 4.9648766e+00 4.2355637e+00 4.6021734e+00 5.9447456e+00 5.0338852e+00 4.7191101e+00 3.9862263e+00 4.9416596e+00 5.1526692e+00 4.7906158e+00 4.2449971e+00 5.3972215e+00 5.2867760e+00 4.7843495e+00 4.3243497e+00 4.5760245e+00 4.7916594e+00 4.2178193e+00 1.8083141e+00 2.0420578e+00 1.4662878e+00 1.0099505e+00 1.7320508e+00 1.2165525e+00 1.3190906e+00 1.1747340e+00 6.8556546e-01 1.1180340e+00 1.0295630e+00 8.6602540e-01 9.9498744e-01 1.1090537e+00 1.0344080e+00 6.7823300e-01 7.2111026e-01 1.2727922e+00 1.4764823e+00 1.7262677e+00 7.2801099e-01 7.4161985e-01 1.3190906e+00 7.2801099e-01 2.4494897e-01 9.8488578e-01 9.0553851e-01 7.8102497e-01 3.1622777e-01 1.1135529e+00 1.4177447e+00 6.1644140e-01 1.2409674e+00 4.7958315e-01 1.2884099e+00 8.2462113e-01 4.6882833e+00 4.2391037e+00 4.8135226e+00 3.4322005e+00 4.3692105e+00 3.8729833e+00 4.3931765e+00 2.5238859e+00 4.3577517e+00 3.2295511e+00 2.8390139e+00 3.7589892e+00 3.5707142e+00 4.2308392e+00 3.0643107e+00 4.2836900e+00 3.9000000e+00 3.4856850e+00 4.2154478e+00 3.2832910e+00 4.3794977e+00 3.6235342e+00 4.5442271e+00 4.1773197e+00 4.0124805e+00 4.2272923e+00 4.6551047e+00 4.8507731e+00 4.0521599e+00 2.9478806e+00 3.1764760e+00 3.0610456e+00 3.3749074e+00 4.6076024e+00 3.8379682e+00 4.1060930e+00 4.5486262e+00 4.1012193e+00 3.4828150e+00 3.3970576e+00 3.7013511e+00 4.1448764e+00 3.4684290e+00 2.5748786e+00 3.5818989e+00 3.5749126e+00 3.6083237e+00 3.9115214e+00 2.3452079e+00 3.5270384e+00 5.8189346e+00 4.6454279e+00 5.9059292e+00 5.2105662e+00 5.5982140e+00 6.7186308e+00 3.8379682e+00 6.2401923e+00 5.5668663e+00 6.2872888e+00 4.9487372e+00 5.0378567e+00 5.4415071e+00 4.5858478e+00 4.8559242e+00 5.1894123e+00 5.2048055e+00 6.9260378e+00 7.0851958e+00 4.5497253e+00 5.7271284e+00 4.4474712e+00 6.8242216e+00 4.6281746e+00 5.5686623e+00 5.9455866e+00 4.4977772e+00 4.5354162e+00 5.3572381e+00 5.7227616e+00 6.1554854e+00 6.7305275e+00 5.3953684e+00 4.6904158e+00 5.0338852e+00 6.4342832e+00 5.4497706e+00 5.1643005e+00 4.4124823e+00 5.4092513e+00 5.5955339e+00 5.2545219e+00 4.6454279e+00 5.8455111e+00 5.7245087e+00 5.2354560e+00 4.7644517e+00 5.0259327e+00 5.2057660e+00 4.6314145e+00 5.4772256e-01 4.6904158e-01 8.8881944e-01 5.5677644e-01 7.9372539e-01 8.7749644e-01 8.4261498e-01 1.2806248e+00 1.1489125e+00 1.3601471e+00 1.3416408e+00 1.0954451e+00 8.3666003e-01 8.7177979e-01 1.4177447e+00 1.4035669e+00 8.0622577e-01 6.8556546e-01 4.1231056e-01 1.3114877e+00 1.1313708e+00 5.9160798e-01 1.3114877e+00 1.7233688e+00 9.6953597e-01 9.5393920e-01 2.1447611e+00 1.6155494e+00 1.1000000e+00 1.0295630e+00 1.4317821e+00 8.3066239e-01 1.4560220e+00 6.5574385e-01 1.0816654e+00 3.9711459e+00 3.6851052e+00 4.1713307e+00 3.4684290e+00 3.8961519e+00 3.6810325e+00 3.8665230e+00 2.9017236e+00 3.8236109e+00 3.2832910e+00 3.2511536e+00 3.4205263e+00 3.4292856e+00 3.8716921e+00 2.8670542e+00 3.6469165e+00 3.6905284e+00 3.2771939e+00 3.9974992e+00 3.2233523e+00 4.0211939e+00 3.2526912e+00 4.2284749e+00 3.8444766e+00 3.5199432e+00 3.6496575e+00 4.1036569e+00 4.3011626e+00 3.7188708e+00 2.8106939e+00 3.1968735e+00 3.0886890e+00 3.1591138e+00 4.3474130e+00 3.7067506e+00 3.6400549e+00 3.9446166e+00 3.8196859e+00 3.2649655e+00 3.3749074e+00 3.6455452e+00 3.7536649e+00 3.2863353e+00 2.9291637e+00 3.4554305e+00 3.3181320e+00 3.3808283e+00 3.4914181e+00 2.6057628e+00 3.3271610e+00 5.3916602e+00 4.4485953e+00 5.3282267e+00 4.8352870e+00 5.1623638e+00 6.0835845e+00 4.0249224e+00 5.6595053e+00 5.1749396e+00 5.6053546e+00 4.4249294e+00 4.6636895e+00 4.9091751e+00 4.4654227e+00 4.6357308e+00 4.7138095e+00 4.7476310e+00 6.1562976e+00 6.5169011e+00 4.4056782e+00 5.1487863e+00 4.2906876e+00 6.2080593e+00 4.2649736e+00 5.0159745e+00 5.3103672e+00 4.1376322e+00 4.1641326e+00 4.9769469e+00 5.1068581e+00 5.5587768e+00 5.8932164e+00 5.0159745e+00 4.3116122e+00 4.7801674e+00 5.7471732e+00 4.9809638e+00 4.7138095e+00 4.0693980e+00 4.8238988e+00 5.0813384e+00 4.6518813e+00 4.4485953e+00 5.3047149e+00 5.1807335e+00 4.7138095e+00 4.4530888e+00 4.5530210e+00 4.7507894e+00 4.3335897e+00 6.1644140e-01 1.0908712e+00 6.4031242e-01 8.5440037e-01 1.0816654e+00 9.2195445e-01 1.4628739e+00 1.2727922e+00 1.4177447e+00 1.5811388e+00 1.2247449e+00 1.0488088e+00 1.1401754e+00 1.5779734e+00 1.5968719e+00 1.0440307e+00 6.5574385e-01 3.6055513e-01 1.5556349e+00 1.4352700e+00 9.6436508e-01 1.5556349e+00 1.9313208e+00 1.1832160e+00 1.1618950e+00 2.4289916e+00 1.7916473e+00 1.1618950e+00 9.3808315e-01 1.6703293e+00 8.7749644e-01 1.6431677e+00 8.3066239e-01 1.3228757e+00 3.7907783e+00 3.4842503e+00 3.9874804e+00 3.3926391e+00 3.7443290e+00 3.5171011e+00 3.6400549e+00 2.8705400e+00 3.6715120e+00 3.1464265e+00 3.2572995e+00 3.2403703e+00 3.3970576e+00 3.6945906e+00 2.7349589e+00 3.4785054e+00 3.4899857e+00 3.1654384e+00 3.9115214e+00 3.1416556e+00 3.7854986e+00 3.1272992e+00 4.0914545e+00 3.6878178e+00 3.3749074e+00 3.4899857e+00 3.9572718e+00 4.1109610e+00 3.5425979e+00 2.7568098e+00 3.1336879e+00 3.0397368e+00 3.0495901e+00 4.1689327e+00 3.5014283e+00 3.3955854e+00 3.7603191e+00 3.7403208e+00 3.0886890e+00 3.2726136e+00 3.5114100e+00 3.5679126e+00 3.1843367e+00 2.9154759e+00 3.3166248e+00 3.1448370e+00 3.2171416e+00 3.3391616e+00 2.5903668e+00 3.1827661e+00 5.1215232e+00 4.2555846e+00 5.1156622e+00 4.6238512e+00 4.9325450e+00 5.8711157e+00 3.8652296e+00 5.4598535e+00 5.0059964e+00 5.3347915e+00 4.1952354e+00 4.4799554e+00 4.6968074e+00 4.2918527e+00 4.4192760e+00 4.4698993e+00 4.5343136e+00 5.8855756e+00 6.3253458e+00 4.2883563e+00 4.9122296e+00 4.0853396e+00 6.0133186e+00 4.0951190e+00 4.7686476e+00 5.0892043e+00 3.9572718e+00 3.9547440e+00 4.7696960e+00 4.9132474e+00 5.3721504e+00 5.6364883e+00 4.8062459e+00 4.1340053e+00 4.6054316e+00 5.5434646e+00 4.7085029e+00 4.4877611e+00 3.8600518e+00 4.6076024e+00 4.8476799e+00 4.4384682e+00 4.2555846e+00 5.0616203e+00 4.9254441e+00 4.5011110e+00 4.2976738e+00 4.3416587e+00 4.4799554e+00 4.1133928e+00 5.1961524e-01 5.1961524e-01 3.8729833e-01 6.7082039e-01 4.1231056e-01 9.2736185e-01 7.8740079e-01 1.0049876e+00 1.0488088e+00 7.0710678e-01 5.2915026e-01 5.8309519e-01 1.0535654e+00 1.0630146e+00 5.3851648e-01 4.5825757e-01 3.8729833e-01 1.0099505e+00 8.3666003e-01 4.5825757e-01 1.0099505e+00 1.3601471e+00 6.4807407e-01 5.7445626e-01 1.8384776e+00 1.2369317e+00 6.7082039e-01 6.7823300e-01 1.0908712e+00 4.7958315e-01 1.0862780e+00 3.6055513e-01 7.5498344e-01 3.9509493e+00 3.5972211e+00 4.1303753e+00 3.2664966e+00 3.8105118e+00 3.5142567e+00 3.7643060e+00 2.6191602e+00 3.7603191e+00 3.0397368e+00 2.9949958e+00 3.2680269e+00 3.3015148e+00 3.7483330e+00 2.6720778e+00 3.5972211e+00 3.5071356e+00 3.1304952e+00 3.8704005e+00 3.0413813e+00 3.8665230e+00 3.1304952e+00 4.1158231e+00 3.7282704e+00 3.4365681e+00 3.5860842e+00 4.0521599e+00 4.2284749e+00 3.5791060e+00 2.6419690e+00 3.0000000e+00 2.8948230e+00 3.0000000e+00 4.2047592e+00 3.5014283e+00 3.5057096e+00 3.8858718e+00 3.7134889e+00 3.0822070e+00 3.1733263e+00 3.4568772e+00 3.6318040e+00 3.1272992e+00 2.6608269e+00 3.2710854e+00 3.1543621e+00 3.2109189e+00 3.3837849e+00 2.3302360e+00 3.1543621e+00 5.2602281e+00 4.2766810e+00 5.2678269e+00 4.7180504e+00 5.0507425e+00 6.0522723e+00 3.7603191e+00 5.6187187e+00 5.0852729e+00 5.5479726e+00 4.3243497e+00 4.5486262e+00 4.8270074e+00 4.2778499e+00 4.4508426e+00 4.5934736e+00 4.6497312e+00 6.1400326e+00 6.4768820e+00 4.2602817e+00 5.0705029e+00 4.0951190e+00 6.1822326e+00 4.1436699e+00 4.9295030e+00 5.2706736e+00 4.0074930e+00 4.0274061e+00 4.8569538e+00 5.0734604e+00 5.5226805e+00 5.9016947e+00 4.8928519e+00 4.2035699e+00 4.6551047e+00 5.7227616e+00 4.8528342e+00 4.6086874e+00 3.9217343e+00 4.7528939e+00 4.9819675e+00 4.5760245e+00 4.2766810e+00 5.2172790e+00 5.0813384e+00 4.6173586e+00 4.3255058e+00 4.4485953e+00 4.6162756e+00 4.1785165e+00 7.3484692e-01 3.1622777e-01 4.4721360e-01 2.4494897e-01 6.5574385e-01 4.1231056e-01 6.0000000e-01 5.5677644e-01 2.6457513e-01 1.7320508e-01 1.7320508e-01 5.4772256e-01 5.4772256e-01 3.4641016e-01 6.4807407e-01 8.1240384e-01 5.0000000e-01 3.8729833e-01 4.2426407e-01 5.0000000e-01 8.7177979e-01 1.7320508e-01 1.4142136e-01 1.3453624e+00 7.7459667e-01 3.7416574e-01 5.9160798e-01 5.8309519e-01 3.7416574e-01 5.9160798e-01 3.1622777e-01 2.4494897e-01 3.9749214e+00 3.5818989e+00 4.1340053e+00 3.0594117e+00 3.7589892e+00 3.3852622e+00 3.7496667e+00 2.3130067e+00 3.7215588e+00 2.8478062e+00 2.6758176e+00 3.1890437e+00 3.1224990e+00 3.6687873e+00 2.5396850e+00 3.5958309e+00 3.3985291e+00 2.9849623e+00 3.7349699e+00 2.8530685e+00 3.8131352e+00 3.0413813e+00 4.0162171e+00 3.6318040e+00 3.3852622e+00 3.5651087e+00 4.0187063e+00 4.2107007e+00 3.4957117e+00 2.4637370e+00 2.7874720e+00 2.6739484e+00 2.8618176e+00 4.1024383e+00 3.3749074e+00 3.4813790e+00 3.8794329e+00 3.5888717e+00 2.9647934e+00 2.9866369e+00 3.2832910e+00 3.5637059e+00 2.9782545e+00 2.3558438e+00 3.1192948e+00 3.0430248e+00 3.0919250e+00 3.3136083e+00 2.0493902e+00 3.0232433e+00 5.2421370e+00 4.1689327e+00 5.2668776e+00 4.6572524e+00 5.0179677e+00 6.0646517e+00 3.5510562e+00 5.6089215e+00 5.0169712e+00 5.5991071e+00 4.3162484e+00 4.4833024e+00 4.8155997e+00 4.1484937e+00 4.3680659e+00 4.5814845e+00 4.6119410e+00 6.2088646e+00 6.4668385e+00 4.1109610e+00 5.0813384e+00 3.9849718e+00 6.1830413e+00 4.0718546e+00 4.9325450e+00 5.2829916e+00 3.9382737e+00 3.9686270e+00 4.8020829e+00 5.0705029e+00 5.5163394e+00 5.9849812e+00 4.8404545e+00 4.1303753e+00 4.5453273e+00 5.7532599e+00 4.8476799e+00 4.5727453e+00 3.8561639e+00 4.7581509e+00 4.9769469e+00 4.5923850e+00 4.1689327e+00 5.2182373e+00 5.0921508e+00 4.6097722e+00 4.2379240e+00 4.4204072e+00 4.6065171e+00 4.1024383e+00 6.3245553e-01 5.0990195e-01 6.4807407e-01 1.3228757e+00 8.0622577e-01 1.0099505e+00 1.0723805e+00 8.1853528e-01 6.2449980e-01 7.1414284e-01 1.1747340e+00 1.1489125e+00 5.4772256e-01 6.4807407e-01 5.4772256e-01 1.1000000e+00 1.0535654e+00 5.4772256e-01 1.1000000e+00 1.5811388e+00 7.5498344e-01 8.6023253e-01 1.9621417e+00 1.4899664e+00 8.2462113e-01 6.4031242e-01 1.2409674e+00 6.1644140e-01 1.2922848e+00 4.6904158e-01 9.1651514e-01 3.5014283e+00 3.1827661e+00 3.6891733e+00 2.9291637e+00 3.3896903e+00 3.1368774e+00 3.3615473e+00 2.3769729e+00 3.3211444e+00 2.7404379e+00 2.7313001e+00 2.8930952e+00 2.9034462e+00 3.3436507e+00 2.3302360e+00 3.1606961e+00 3.1511903e+00 2.7331301e+00 3.4770677e+00 2.6795522e+00 3.5014283e+00 2.7294688e+00 3.7054015e+00 3.3120990e+00 3.0099834e+00 3.1543621e+00 3.6097091e+00 3.8065733e+00 3.1906112e+00 2.2737634e+00 2.6551836e+00 2.5475478e+00 2.6210685e+00 3.8144462e+00 3.1638584e+00 3.1272992e+00 3.4539832e+00 3.3015148e+00 2.7221315e+00 2.8319605e+00 3.0951575e+00 3.2280025e+00 2.7477263e+00 2.4062419e+00 2.9103264e+00 2.7748874e+00 2.8390139e+00 2.9698485e+00 2.0928450e+00 2.7856777e+00 4.8928519e+00 3.9166312e+00 4.8456166e+00 4.3162484e+00 4.6583259e+00 5.6124861e+00 3.4828150e+00 5.1749396e+00 4.6636895e+00 5.1468437e+00 3.9306488e+00 4.1496988e+00 4.4192760e+00 3.9331921e+00 4.1206796e+00 4.2201896e+00 4.2391037e+00 5.7105166e+00 6.0398675e+00 3.8704005e+00 4.6690470e+00 3.7603191e+00 5.7349804e+00 3.7496667e+00 4.5265881e+00 4.8321838e+00 3.6207734e+00 3.6455452e+00 4.4654227e+00 4.6249324e+00 5.0803543e+00 5.4607692e+00 4.5066617e+00 3.7894591e+00 4.2449971e+00 5.2915026e+00 4.4877611e+00 4.2035699e+00 3.5482390e+00 4.3428102e+00 4.5945620e+00 4.1821047e+00 3.9166312e+00 4.8176758e+00 4.7000000e+00 4.2296572e+00 3.9370039e+00 4.0521599e+00 4.2544095e+00 3.8065733e+00 5.4772256e-01 1.4142136e-01 7.4161985e-01 5.7445626e-01 6.4807407e-01 8.1853528e-01 4.3588989e-01 3.3166248e-01 4.3588989e-01 7.3484692e-01 7.7459667e-01 5.0990195e-01 3.7416574e-01 5.8309519e-01 7.5498344e-01 6.8556546e-01 5.4772256e-01 7.5498344e-01 1.0862780e+00 4.1231056e-01 3.7416574e-01 1.6278821e+00 9.4868330e-01 4.4721360e-01 4.1231056e-01 8.6023253e-01 1.4142136e-01 7.9372539e-01 2.4494897e-01 5.2915026e-01 3.9268308e+00 3.5341194e+00 4.0902323e+00 3.1080541e+00 3.7429935e+00 3.3704599e+00 3.6905284e+00 2.3937418e+00 3.6972963e+00 2.8618176e+00 2.7820855e+00 3.1638584e+00 3.1796226e+00 3.6414283e+00 2.5436195e+00 3.5594943e+00 3.3660065e+00 2.9916551e+00 3.7696154e+00 2.8879058e+00 3.7603191e+00 3.0413813e+00 4.0162171e+00 3.6124784e+00 3.3674916e+00 3.5369478e+00 3.9987498e+00 4.1725292e+00 3.4727511e+00 2.5079872e+00 2.8372522e+00 2.7294688e+00 2.8757608e+00 4.0828911e+00 3.3421550e+00 3.4146742e+00 3.8379682e+00 3.6193922e+00 2.9410882e+00 3.0166206e+00 3.2893768e+00 3.5298725e+00 2.9983329e+00 2.4474477e+00 3.1224990e+00 3.0166206e+00 3.0757113e+00 3.2954514e+00 2.1400935e+00 3.0199338e+00 5.1749396e+00 4.1496988e+00 5.2191953e+00 4.6162756e+00 4.9699095e+00 6.0116553e+00 3.5623026e+00 5.5623736e+00 4.9989999e+00 5.5181519e+00 4.2626283e+00 4.4609416e+00 4.7717921e+00 4.1460825e+00 4.3428102e+00 4.5265881e+00 4.5661800e+00 6.1163715e+00 6.4311741e+00 4.1303753e+00 5.0239427e+00 3.9623226e+00 6.1392182e+00 4.0570926e+00 4.8672374e+00 5.2220686e+00 3.9179076e+00 3.9306488e+00 4.7686476e+00 5.0229473e+00 5.4781384e+00 5.8940648e+00 4.8072861e+00 4.1036569e+00 4.5232732e+00 5.7061370e+00 4.7770284e+00 4.5199558e+00 3.8196859e+00 4.7095647e+00 4.9264592e+00 4.5486262e+00 4.1496988e+00 5.1584882e+00 5.0289164e+00 4.5705580e+00 4.2355637e+00 4.3794977e+00 4.5365185e+00 4.0607881e+00 5.0990195e-01 1.0816654e+00 4.3588989e-01 6.3245553e-01 5.7445626e-01 4.5825757e-01 3.0000000e-01 3.6055513e-01 7.3484692e-01 6.7823300e-01 2.8284271e-01 7.6157731e-01 8.6023253e-01 6.2449980e-01 6.7082039e-01 4.2426407e-01 6.2449980e-01 1.1489125e+00 3.6055513e-01 5.8309519e-01 1.4798649e+00 1.0954451e+00 5.8309519e-01 5.7445626e-01 7.8740079e-01 5.0990195e-01 8.7749644e-01 3.7416574e-01 5.0990195e-01 3.6110940e+00 3.2511536e+00 3.7775654e+00 2.7784888e+00 3.4161382e+00 3.0822070e+00 3.4322005e+00 2.1095023e+00 3.3630343e+00 2.6095977e+00 2.4494897e+00 2.8896367e+00 2.7802878e+00 3.3436507e+00 2.2605309e+00 3.2419130e+00 3.1192948e+00 2.6551836e+00 3.4073450e+00 2.5495098e+00 3.5298725e+00 2.7110883e+00 3.6810325e+00 3.2939338e+00 3.0364453e+00 3.2140317e+00 3.6565011e+00 3.8716921e+00 3.1843367e+00 2.1470911e+00 2.4959968e+00 2.3769729e+00 2.5475478e+00 3.7907783e+00 3.1128765e+00 3.1874755e+00 3.5312887e+00 3.2434549e+00 2.6776856e+00 2.7055499e+00 2.9899833e+00 3.2403703e+00 2.6627054e+00 2.1377558e+00 2.8266588e+00 2.7386128e+00 2.7928480e+00 2.9765752e+00 1.8439089e+00 2.7239677e+00 4.9598387e+00 3.8858718e+00 4.9295030e+00 4.3393548e+00 4.7095647e+00 5.7113921e+00 3.3391616e+00 5.2516664e+00 4.6765372e+00 5.2848841e+00 4.0062451e+00 4.1641326e+00 4.4911023e+00 3.8768544e+00 4.1133928e+00 4.2906876e+00 4.2860238e+00 5.8694122e+00 6.1139185e+00 3.7920970e+00 4.7644517e+00 3.7255872e+00 5.8215118e+00 3.7549967e+00 4.6162756e+00 4.9325450e+00 3.6290495e+00 3.6674242e+00 4.4922155e+00 4.7085029e+00 5.1584882e+00 5.6338264e+00 4.5354162e+00 3.7973675e+00 4.2166337e+00 5.4055527e+00 4.5672749e+00 4.2532341e+00 3.5623026e+00 4.4317040e+00 4.6722586e+00 4.2790186e+00 3.8858718e+00 4.9040799e+00 4.7947888e+00 4.3023250e+00 3.9242834e+00 4.1060930e+00 4.3289722e+00 3.8118237e+00 7.4161985e-01 4.5825757e-01 6.1644140e-01 7.4161985e-01 3.3166248e-01 3.0000000e-01 3.8729833e-01 6.7823300e-01 7.0710678e-01 4.2426407e-01 5.0990195e-01 6.7823300e-01 7.0000000e-01 6.2449980e-01 5.2915026e-01 7.0000000e-01 1.0295630e+00 3.6055513e-01 3.1622777e-01 1.5394804e+00 9.0553851e-01 3.1622777e-01 4.1231056e-01 7.7459667e-01 2.4494897e-01 7.4161985e-01 2.8284271e-01 4.6904158e-01 3.8858718e+00 3.4856850e+00 4.0459857e+00 3.0298515e+00 3.6864617e+00 3.3136083e+00 3.6441734e+00 2.3086793e+00 3.6482873e+00 2.7874720e+00 2.6944387e+00 3.1032241e+00 3.1096624e+00 3.5888717e+00 2.4718414e+00 3.5114100e+00 3.3090784e+00 2.9342802e+00 3.6972963e+00 2.8178006e+00 3.7067506e+00 2.9782545e+00 3.9560081e+00 3.5623026e+00 3.3136083e+00 3.4856850e+00 3.9484174e+00 4.1218928e+00 3.4146742e+00 2.4351591e+00 2.7622455e+00 2.6551836e+00 2.8089144e+00 4.0261644e+00 3.2848135e+00 3.3674916e+00 3.7907783e+00 3.5524639e+00 2.8827071e+00 2.9427878e+00 3.2280025e+00 3.4785054e+00 2.9308702e+00 2.3600847e+00 3.0577770e+00 2.9631065e+00 3.0166206e+00 3.2403703e+00 2.0445048e+00 2.9563491e+00 5.1244512e+00 4.0865633e+00 5.1710734e+00 4.5661800e+00 4.9173163e+00 5.9699246e+00 3.4885527e+00 5.5208695e+00 4.9446941e+00 5.4763126e+00 4.2107007e+00 4.4022721e+00 4.7191101e+00 4.0755368e+00 4.2731721e+00 4.4710178e+00 4.5177428e+00 6.0868711e+00 6.3827894e+00 4.0644803e+00 4.9739320e+00 3.8961519e+00 6.0967204e+00 3.9949969e+00 4.8218254e+00 5.1836281e+00 3.8561639e+00 3.8742741e+00 4.7116876e+00 4.9829710e+00 5.4323107e+00 5.8668561e+00 4.7486840e+00 4.0521599e+00 4.4743715e+00 5.6586217e+00 4.7265209e+00 4.4732538e+00 3.7616486e+00 4.6583259e+00 4.8713448e+00 4.4911023e+00 4.0865633e+00 5.1097945e+00 4.9769469e+00 4.5110974e+00 4.1689327e+00 4.3243497e+00 4.4855323e+00 4.0062451e+00 9.5916630e-01 9.4339811e-01 9.3808315e-01 7.7459667e-01 7.8740079e-01 7.4833148e-01 7.2801099e-01 8.0622577e-01 9.8488578e-01 9.3273791e-01 1.1532563e+00 7.7459667e-01 6.0000000e-01 9.5393920e-01 7.7459667e-01 7.0000000e-01 7.3484692e-01 5.1961524e-01 1.3416408e+00 5.3851648e-01 8.3066239e-01 1.0677078e+00 7.5498344e-01 8.0622577e-01 5.6568542e-01 8.6602540e-01 6.4031242e-01 4.5880279e+00 4.1641326e+00 4.7370877e+00 3.5651087e+00 4.3474130e+00 3.9127995e+00 4.3162484e+00 2.7313001e+00 4.3197222e+00 3.3196385e+00 3.1000000e+00 3.7389838e+00 3.6823905e+00 4.2272923e+00 3.0757113e+00 4.2023803e+00 3.9115214e+00 3.5355339e+00 4.2965102e+00 3.3808283e+00 4.3416587e+00 3.6193922e+00 4.5825757e+00 4.1928511e+00 3.9786933e+00 4.1665333e+00 4.6216880e+00 4.7979162e+00 4.0484565e+00 3.0166206e+00 3.3015148e+00 3.1906112e+00 3.4146742e+00 4.6411206e+00 3.8652296e+00 4.0261644e+00 4.4766059e+00 4.1653331e+00 3.4899857e+00 3.4971417e+00 3.7907783e+00 4.1243181e+00 3.5270384e+00 2.7892651e+00 3.6414283e+00 3.5791060e+00 3.6262929e+00 3.8923001e+00 2.5039968e+00 3.5594943e+00 5.7680153e+00 4.6850827e+00 5.8506410e+00 5.2057660e+00 5.5686623e+00 6.6580778e+00 3.9749214e+00 6.1991935e+00 5.5874860e+00 6.1692787e+00 4.8805737e+00 5.0428167e+00 5.3907328e+00 4.6540305e+00 4.8713448e+00 5.1283526e+00 5.1749396e+00 6.7926431e+00 7.0590368e+00 4.6486557e+00 5.6524331e+00 4.4821870e+00 6.7808554e+00 4.6335731e+00 5.4954527e+00 5.8719673e+00 4.4944410e+00 4.5144213e+00 5.3525695e+00 5.6674509e+00 6.1139185e+00 6.5825527e+00 5.3888774e+00 4.6936127e+00 5.0842895e+00 6.3553127e+00 5.3786615e+00 5.1283526e+00 4.3954522e+00 5.3394756e+00 5.5371473e+00 5.1730069e+00 4.6850827e+00 5.7810034e+00 5.6462377e+00 5.1788030e+00 4.7947888e+00 4.9849774e+00 5.1351728e+00 4.6281746e+00 4.7958315e-01 4.4721360e-01 2.0000000e-01 4.2426407e-01 4.4721360e-01 5.1961524e-01 4.7958315e-01 3.8729833e-01 9.2195445e-01 1.0723805e+00 5.2915026e-01 6.0000000e-01 6.7082039e-01 5.2915026e-01 9.1104336e-01 3.7416574e-01 5.0000000e-01 1.2489996e+00 8.6602540e-01 2.6457513e-01 5.4772256e-01 5.5677644e-01 5.9160798e-01 6.6332496e-01 5.7445626e-01 4.3588989e-01 3.6646964e+00 3.2465366e+00 3.8105118e+00 2.6627054e+00 3.4088121e+00 3.0149627e+00 3.4132096e+00 1.9131126e+00 3.3852622e+00 2.4535688e+00 2.2781571e+00 2.8248894e+00 2.7495454e+00 3.3120990e+00 2.1587033e+00 3.2710854e+00 3.0298515e+00 2.6191602e+00 3.3555923e+00 2.4677925e+00 3.4568772e+00 2.6795522e+00 3.6496575e+00 3.2771939e+00 3.0413813e+00 3.2310989e+00 3.6823905e+00 3.8704005e+00 3.1320920e+00 2.0832667e+00 2.3958297e+00 2.2847319e+00 2.4859606e+00 3.7336309e+00 3.0033315e+00 3.1416556e+00 3.5496479e+00 3.2202484e+00 2.5961510e+00 2.5942244e+00 2.9034462e+00 3.2109189e+00 2.6000000e+00 1.9544820e+00 2.7386128e+00 2.6814175e+00 2.7221315e+00 2.9614186e+00 1.6401219e+00 2.6476405e+00 4.8918299e+00 3.7907783e+00 4.9284886e+00 4.3011626e+00 4.6636895e+00 5.7367238e+00 3.1559468e+00 5.2773099e+00 4.6583259e+00 5.2782573e+00 3.9724048e+00 4.1194660e+00 4.4698993e+00 3.7603191e+00 3.9887341e+00 4.2308392e+00 4.2638011e+00 5.9076222e+00 6.1261734e+00 3.7296112e+00 4.7423623e+00 3.6041643e+00 5.8532043e+00 3.7054015e+00 4.5956501e+00 4.9598387e+00 3.5721142e+00 3.6083237e+00 4.4395946e+00 4.7455242e+00 5.1826634e+00 5.6947344e+00 4.4766059e+00 3.7749172e+00 4.1844952e+00 5.4267854e+00 4.5022217e+00 4.2261093e+00 3.4928498e+00 4.4192760e+00 4.6281746e+00 4.2520583e+00 3.7907783e+00 4.8764741e+00 4.7497368e+00 4.2591079e+00 3.8639358e+00 4.0681691e+00 4.2602817e+00 3.7389838e+00 5.3851648e-01 4.1231056e-01 5.7445626e-01 6.4031242e-01 3.7416574e-01 4.2426407e-01 7.4833148e-01 9.0553851e-01 1.1747340e+00 5.1961524e-01 7.5498344e-01 9.2736185e-01 5.1961524e-01 8.2462113e-01 5.0000000e-01 6.4807407e-01 1.2922848e+00 7.4833148e-01 5.4772256e-01 5.3851648e-01 6.4807407e-01 5.8309519e-01 5.7445626e-01 7.0710678e-01 5.4772256e-01 3.7629775e+00 3.3241540e+00 3.8974351e+00 2.7055499e+00 3.4971417e+00 3.0232433e+00 3.4727511e+00 1.9000000e+00 3.4626579e+00 2.4677925e+00 2.2803509e+00 2.8896367e+00 2.8160256e+00 3.3496268e+00 2.2338308e+00 3.3749074e+00 3.0413813e+00 2.6400758e+00 3.4423829e+00 2.5019992e+00 3.4957117e+00 2.7694765e+00 3.7080992e+00 3.3000000e+00 3.1272992e+00 3.3301652e+00 3.7696154e+00 3.9534795e+00 3.1843367e+00 2.1563859e+00 2.4310492e+00 2.3173260e+00 2.5475478e+00 3.7589892e+00 2.9949958e+00 3.1874755e+00 3.6373067e+00 3.3045423e+00 2.6172505e+00 2.6305893e+00 2.8948230e+00 3.2526912e+00 2.6551836e+00 1.9621417e+00 2.7622455e+00 2.6944387e+00 2.7495454e+00 3.0298515e+00 1.7088007e+00 2.6870058e+00 4.9355851e+00 3.8236109e+00 5.0059964e+00 4.3301270e+00 4.7180504e+00 5.8051701e+00 3.1352831e+00 5.3310412e+00 4.7106263e+00 5.3600373e+00 4.0509258e+00 4.1833001e+00 4.5530210e+00 3.8039453e+00 4.0546270e+00 4.3092923e+00 4.3092923e+00 5.9674115e+00 6.2016127e+00 3.7656341e+00 4.8270074e+00 3.6386811e+00 5.9203040e+00 3.7815341e+00 4.6551047e+00 5.0169712e+00 3.6455452e+00 3.6619667e+00 4.4966654e+00 4.8052055e+00 5.2583267e+00 5.7671483e+00 4.5398238e+00 3.8131352e+00 4.1785165e+00 5.5335341e+00 4.5585085e+00 4.2626283e+00 3.5454196e+00 4.5122057e+00 4.7148701e+00 4.3760713e+00 3.8236109e+00 4.9446941e+00 4.8321838e+00 4.3669211e+00 3.9446166e+00 4.1448764e+00 4.3150898e+00 3.7643060e+00 4.4721360e-01 5.4772256e-01 4.8989795e-01 3.6055513e-01 2.2360680e-01 6.0827625e-01 1.1269428e+00 1.3152946e+00 2.0000000e-01 4.4721360e-01 7.6811457e-01 2.0000000e-01 6.7082039e-01 4.2426407e-01 5.9160798e-01 9.1651514e-01 7.0000000e-01 6.4031242e-01 8.8317609e-01 3.0000000e-01 8.0622577e-01 4.8989795e-01 7.6811457e-01 3.6055513e-01 3.8845849e+00 3.4785054e+00 4.0249224e+00 2.7766887e+00 3.6027767e+00 3.1859065e+00 3.6537652e+00 1.9748418e+00 3.5749126e+00 2.6191602e+00 2.2912878e+00 3.0430248e+00 2.8354894e+00 3.5028560e+00 2.3622024e+00 3.4899857e+00 3.2341923e+00 2.7604347e+00 3.4899857e+00 2.5903668e+00 3.6945906e+00 2.8670542e+00 3.8105118e+00 3.4438351e+00 3.2357379e+00 3.4409301e+00 3.8678159e+00 4.0865633e+00 3.3331667e+00 2.2135944e+00 2.5019992e+00 2.3790755e+00 2.6495283e+00 3.9115214e+00 3.2031235e+00 3.3955854e+00 3.7682887e+00 3.3511192e+00 2.7964263e+00 2.7331301e+00 3.0413813e+00 3.4132096e+00 2.7495454e+00 2.0049938e+00 2.9017236e+00 2.8722813e+00 2.9103264e+00 3.1543621e+00 1.7406895e+00 2.8266588e+00 5.1410116e+00 3.9837169e+00 5.1487863e+00 4.5011110e+00 4.8877398e+00 5.9472683e+00 3.3045423e+00 5.4726593e+00 4.8311489e+00 5.5443665e+00 4.2166337e+00 4.3162484e+00 4.6968074e+00 3.9420807e+00 4.2154478e+00 4.4833024e+00 4.4743715e+00 6.1595454e+00 6.3206012e+00 3.8587563e+00 4.9869831e+00 3.8118237e+00 6.0481402e+00 3.9025633e+00 4.8373546e+00 5.1768716e+00 3.7788887e+00 3.8288379e+00 4.6486557e+00 4.9436828e+00 5.3795911e+00 5.9439044e+00 4.6904158e+00 3.9585351e+00 4.3370497e+00 5.6524331e+00 4.7634021e+00 4.4429720e+00 3.7148351e+00 4.6551047e+00 4.8723711e+00 4.5033321e+00 3.9837169e+00 5.1166395e+00 5.0079936e+00 4.5011110e+00 4.0484565e+00 4.2953463e+00 4.5221676e+00 3.9522146e+00 3.1622777e-01 3.4641016e-01 4.1231056e-01 4.1231056e-01 4.1231056e-01 7.9372539e-01 9.8488578e-01 4.4721360e-01 4.8989795e-01 6.2449980e-01 4.4721360e-01 8.0622577e-01 2.4494897e-01 3.3166248e-01 1.2489996e+00 7.2801099e-01 2.2360680e-01 5.0990195e-01 5.0000000e-01 4.5825757e-01 5.2915026e-01 4.7958315e-01 3.0000000e-01 3.8275318e+00 3.4088121e+00 3.9749214e+00 2.8337255e+00 3.5805028e+00 3.1733263e+00 3.5707142e+00 2.0639767e+00 3.5524639e+00 2.6115130e+00 2.4351591e+00 2.9899833e+00 2.9257478e+00 3.4741906e+00 2.3280893e+00 3.4380227e+00 3.1843367e+00 2.7820855e+00 3.5355339e+00 2.6362853e+00 3.6124784e+00 2.8530685e+00 3.8209946e+00 3.4380227e+00 3.2109189e+00 3.4000000e+00 3.8522721e+00 4.0373258e+00 3.2969683e+00 2.2583180e+00 2.5651511e+00 2.4535688e+00 2.6570661e+00 3.8961519e+00 3.1527766e+00 3.2939338e+00 3.7148351e+00 3.3985291e+00 2.7531800e+00 2.7622455e+00 3.0610456e+00 3.3719431e+00 2.7712813e+00 2.1118712e+00 2.9017236e+00 2.8372522e+00 2.8827071e+00 3.1288976e+00 1.8083141e+00 2.8124722e+00 5.0467812e+00 3.9534795e+00 5.0941143e+00 4.4609416e+00 4.8259714e+00 5.9000000e+00 3.3045423e+00 5.4396691e+00 4.8270074e+00 5.4350713e+00 4.1352146e+00 4.2883563e+00 4.6368092e+00 3.9268308e+00 4.1533119e+00 4.3931765e+00 4.4249294e+00 6.0580525e+00 6.2952363e+00 3.9000000e+00 4.9061186e+00 3.7643060e+00 6.0183054e+00 3.8768544e+00 4.7539457e+00 5.1185936e+00 3.7416574e+00 3.7709415e+00 4.6054316e+00 4.9071377e+00 5.3497664e+00 5.8455111e+00 4.6432747e+00 3.9382737e+00 4.3416587e+00 5.5955339e+00 4.6572524e+00 4.3840620e+00 3.6551334e+00 4.5858478e+00 4.7937459e+00 4.4226689e+00 3.9534795e+00 5.0378567e+00 4.9112117e+00 4.4294469e+00 4.0385641e+00 4.2343831e+00 4.4147480e+00 3.8961519e+00 1.4142136e-01 5.9160798e-01 5.7445626e-01 3.0000000e-01 6.0827625e-01 7.6811457e-01 5.0990195e-01 4.6904158e-01 3.6055513e-01 5.0990195e-01 9.6436508e-01 1.4142136e-01 3.0000000e-01 1.4071247e+00 8.7749644e-01 4.5825757e-01 5.4772256e-01 6.5574385e-01 3.3166248e-01 6.7823300e-01 2.2360680e-01 3.0000000e-01 3.8742741e+00 3.4957117e+00 4.0373258e+00 2.9983329e+00 3.6715120e+00 3.3090784e+00 3.6674242e+00 2.2759613e+00 3.6249138e+00 2.8000000e+00 2.6324893e+00 3.1176915e+00 3.0364453e+00 3.5846897e+00 2.4779023e+00 3.5014283e+00 3.3316662e+00 2.8982753e+00 3.6578682e+00 2.7802878e+00 3.7456642e+00 2.9597297e+00 3.9319207e+00 3.5411862e+00 3.2939338e+00 3.4727511e+00 3.9217343e+00 4.1231056e+00 3.4190642e+00 2.3874673e+00 2.7202941e+00 2.6038433e+00 2.7856777e+00 4.0249224e+00 3.3136083e+00 3.4073450e+00 3.7868192e+00 3.5028560e+00 2.8948230e+00 2.9240383e+00 3.2109189e+00 3.4799425e+00 2.9017236e+00 2.3151674e+00 3.0495901e+00 2.9647934e+00 3.0182777e+00 3.2264532e+00 2.0174241e+00 2.9512709e+00 5.1759057e+00 4.1048752e+00 5.1797683e+00 4.5760245e+00 4.9426713e+00 5.9690870e+00 3.5128336e+00 5.5108983e+00 4.9295030e+00 5.5190579e+00 4.2402830e+00 4.4056782e+00 4.7349762e+00 4.0914545e+00 4.3185646e+00 4.5144213e+00 4.5276926e+00 6.1139185e+00 6.3741666e+00 4.0336088e+00 5.0029991e+00 3.9306488e+00 6.0844063e+00 3.9962482e+00 4.8518038e+00 5.1865210e+00 3.8652296e+00 3.8961519e+00 4.7275787e+00 4.9699095e+00 5.4203321e+00 5.8847260e+00 4.7686476e+00 4.0435133e+00 4.4575778e+00 5.6630381e+00 4.7822589e+00 4.4899889e+00 3.7868192e+00 4.6765372e+00 4.9050994e+00 4.5188494e+00 4.1048752e+00 5.1400389e+00 5.0219518e+00 4.5387223e+00 4.1653331e+00 4.3439613e+00 4.5420260e+00 4.0323690e+00 5.7445626e-01 5.3851648e-01 3.0000000e-01 7.1414284e-01 8.5440037e-01 4.4721360e-01 3.4641016e-01 3.3166248e-01 4.4721360e-01 9.0000000e-01 1.4142136e-01 2.6457513e-01 1.3114877e+00 8.3066239e-01 5.0000000e-01 6.7823300e-01 5.7445626e-01 4.5825757e-01 6.3245553e-01 3.3166248e-01 2.2360680e-01 3.9509493e+00 3.5749126e+00 4.1133928e+00 3.0446675e+00 3.7389838e+00 3.3808283e+00 3.7509999e+00 2.3108440e+00 3.6959437e+00 2.8600699e+00 2.6551836e+00 3.1906112e+00 3.0789609e+00 3.6592349e+00 2.5416530e+00 3.5749126e+00 3.4088121e+00 2.9631065e+00 3.7067506e+00 2.8337255e+00 3.8275318e+00 3.0232433e+00 3.9949969e+00 3.6138622e+00 3.3630343e+00 3.5440090e+00 3.9899875e+00 4.1976184e+00 3.4914181e+00 2.4372115e+00 2.7676705e+00 2.6495283e+00 2.8460499e+00 4.0963398e+00 3.3911650e+00 3.4942810e+00 3.8626416e+00 3.5538711e+00 2.9698485e+00 2.9782545e+00 3.2756679e+00 3.5566838e+00 2.9597297e+00 2.3452079e+00 3.1144823e+00 3.0413813e+00 3.0903074e+00 3.2969683e+00 2.0469489e+00 3.0182777e+00 5.2602281e+00 4.1749251e+00 5.2564246e+00 4.6540305e+00 5.0209561e+00 6.0473135e+00 3.5721142e+00 5.5883808e+00 4.9979996e+00 5.6053546e+00 4.3197222e+00 4.4754888e+00 4.8104054e+00 4.1545156e+00 4.3874822e+00 4.5934736e+00 4.6065171e+00 6.2048368e+00 6.4459289e+00 4.0902323e+00 5.0823223e+00 4.0012498e+00 6.1595454e+00 4.0632499e+00 4.9355851e+00 5.2687759e+00 3.9344631e+00 3.9724048e+00 4.8010416e+00 5.0477718e+00 5.4936327e+00 5.9741108e+00 4.8414874e+00 4.1170378e+00 4.5310043e+00 5.7367238e+00 4.8672374e+00 4.5716518e+00 3.8626416e+00 4.7528939e+00 4.9819675e+00 4.5912961e+00 4.1749251e+00 5.2211110e+00 5.1029403e+00 4.6108568e+00 4.2272923e+00 4.4192760e+00 4.6270941e+00 4.1109610e+00 1.4142136e-01 7.6157731e-01 1.0392305e+00 1.2961481e+00 2.6457513e-01 5.0000000e-01 9.0553851e-01 2.6457513e-01 4.6904158e-01 4.5825757e-01 5.2915026e-01 9.7467943e-01 4.2426407e-01 5.8309519e-01 8.0622577e-01 3.1622777e-01 7.2111026e-01 2.2360680e-01 7.8740079e-01 3.7416574e-01 4.0422766e+00 3.6041643e+00 4.1749251e+00 2.9017236e+00 3.7536649e+00 3.2832910e+00 3.7603191e+00 2.0518285e+00 3.7296112e+00 2.6888659e+00 2.4041631e+00 3.1511903e+00 3.0149627e+00 3.6193922e+00 2.4718414e+00 3.6455452e+00 3.3090784e+00 2.8896367e+00 3.6537652e+00 2.7202941e+00 3.7735925e+00 3.0149627e+00 3.9534795e+00 3.5679126e+00 3.3882149e+00 3.5958309e+00 4.0311289e+00 4.2249260e+00 3.4467376e+00 2.3685439e+00 2.6324893e+00 2.5159491e+00 2.7838822e+00 4.0187063e+00 3.2603681e+00 3.4785054e+00 3.9127995e+00 3.5242020e+00 2.8827071e+00 2.8460499e+00 3.1368774e+00 3.5270384e+00 2.8861739e+00 2.1047565e+00 3.0049958e+00 2.9664794e+00 3.0099834e+00 3.2924155e+00 1.8493242e+00 2.9359837e+00 5.2172790e+00 4.0743098e+00 5.2820451e+00 4.6054316e+00 4.9919936e+00 6.0876925e+00 3.3451457e+00 5.6124861e+00 4.9689033e+00 5.6524331e+00 4.3278170e+00 4.4407207e+00 4.8238988e+00 4.0360872e+00 4.2965102e+00 4.5814845e+00 4.5880279e+00 6.2745518e+00 6.4699304e+00 3.9924930e+00 5.1048996e+00 3.8858718e+00 6.1975802e+00 4.0323690e+00 4.9426713e+00 5.3075418e+00 3.9000000e+00 3.9306488e+00 4.7602521e+00 5.0882217e+00 5.5308227e+00 6.0728906e+00 4.8010416e+00 4.0816663e+00 4.4452222e+00 5.8051701e+00 4.8414874e+00 4.5464272e+00 3.8118237e+00 4.7853944e+00 4.9849774e+00 4.6378875e+00 4.0743098e+00 5.2258971e+00 5.1097945e+00 4.6270941e+00 4.1833001e+00 4.4136153e+00 4.5978256e+00 4.0360872e+00 7.0710678e-01 1.0862780e+00 1.3190906e+00 1.7320508e-01 4.5825757e-01 8.6023253e-01 1.7320508e-01 5.0990195e-01 4.3588989e-01 5.4772256e-01 9.1104336e-01 5.0990195e-01 6.0000000e-01 8.4261498e-01 2.4494897e-01 7.6157731e-01 3.0000000e-01 7.8740079e-01 3.4641016e-01 3.9874804e+00 3.5594943e+00 4.1218928e+00 2.8460499e+00 3.6972963e+00 3.2434549e+00 3.7229021e+00 2.0074860e+00 3.6728735e+00 2.6551836e+00 2.3452079e+00 3.1096624e+00 2.9410882e+00 3.5749126e+00 2.4269322e+00 3.5902646e+00 3.2787193e+00 2.8372522e+00 3.5874782e+00 2.6645825e+00 3.7443290e+00 2.9580399e+00 3.8974351e+00 3.5199432e+00 3.3316662e+00 3.5397740e+00 3.9711459e+00 4.1749251e+00 3.4029399e+00 2.3043437e+00 2.5748786e+00 2.4556058e+00 2.7294688e+00 3.9761791e+00 3.2357379e+00 3.4496377e+00 3.8613469e+00 3.4554305e+00 2.8478062e+00 2.7964263e+00 3.0951575e+00 3.4842503e+00 2.8301943e+00 2.0518285e+00 2.9614186e+00 2.9291637e+00 2.9698485e+00 3.2403703e+00 1.7944358e+00 2.8913665e+00 5.1903757e+00 4.0373258e+00 5.2345009e+00 4.5661800e+00 4.9537864e+00 6.0382117e+00 3.3211444e+00 5.5623736e+00 4.9162994e+00 5.6169387e+00 4.2883563e+00 4.3931765e+00 4.7780749e+00 3.9962482e+00 4.2638011e+00 4.5464272e+00 4.5464272e+00 6.2377881e+00 6.4156060e+00 3.9370039e+00 5.0635956e+00 3.8548671e+00 6.1441029e+00 3.9824616e+00 4.9061186e+00 5.2621288e+00 3.8535698e+00 3.8923001e+00 4.7180504e+00 5.0368641e+00 5.4763126e+00 6.0315835e+00 4.7592016e+00 4.0348482e+00 4.4022721e+00 5.7515215e+00 4.8145612e+00 4.5088801e+00 3.7749172e+00 4.7391982e+00 4.9446941e+00 4.5902070e+00 4.0373258e+00 5.1874849e+00 5.0744458e+00 4.5814845e+00 4.1303753e+00 4.3703547e+00 4.5716518e+00 4.0037482e+00 7.8740079e-01 8.3666003e-01 6.5574385e-01 5.7445626e-01 3.1622777e-01 6.5574385e-01 1.1135529e+00 3.6055513e-01 4.6904158e-01 1.4387495e+00 1.0583005e+00 4.6904158e-01 6.4031242e-01 7.3484692e-01 5.4772256e-01 8.5440037e-01 3.7416574e-01 4.6904158e-01 3.7202150e+00 3.3541020e+00 3.8871583e+00 2.8774989e+00 3.5199432e+00 3.2031235e+00 3.5355339e+00 2.2022716e+00 3.4799425e+00 2.7000000e+00 2.5455844e+00 2.9849623e+00 2.9000000e+00 3.4612137e+00 2.3473389e+00 3.3451457e+00 3.2264532e+00 2.7874720e+00 3.5057096e+00 2.6645825e+00 3.6249138e+00 2.8124722e+00 3.7934153e+00 3.4249088e+00 3.1464265e+00 3.3181320e+00 3.7696154e+00 3.9736633e+00 3.2893768e+00 2.2561028e+00 2.6057628e+00 2.4919872e+00 2.6551836e+00 3.9051248e+00 3.2202484e+00 3.2863353e+00 3.6373067e+00 3.3526109e+00 2.7874720e+00 2.8071338e+00 3.1144823e+00 3.3555923e+00 2.7730849e+00 2.2293497e+00 2.9376862e+00 2.8600699e+00 2.9051678e+00 3.0886890e+00 1.9078784e+00 2.8319605e+00 5.0477718e+00 3.9824616e+00 5.0299105e+00 4.4530888e+00 4.8062459e+00 5.8223707e+00 3.4278273e+00 5.3721504e+00 4.7906158e+00 5.3712196e+00 4.0951190e+00 4.2638011e+00 4.5836667e+00 3.9635842e+00 4.1809090e+00 4.3692105e+00 4.3965896e+00 5.9774577e+00 6.2209324e+00 3.9064050e+00 4.8518038e+00 3.8105118e+00 5.9371710e+00 3.8496753e+00 4.7148701e+00 5.0487622e+00 3.7215588e+00 3.7643060e+00 4.5891176e+00 4.8301139e+00 5.2697249e+00 5.7428216e+00 4.6270941e+00 3.9166312e+00 4.3520110e+00 5.4972721e+00 4.6497312e+00 4.3646306e+00 3.6565011e+00 4.5210618e+00 4.7528939e+00 4.3485630e+00 3.9824616e+00 4.9969991e+00 4.8733972e+00 4.3760713e+00 4.0149720e+00 4.1976184e+00 4.4113490e+00 3.9153544e+00 3.4641016e-01 1.0440307e+00 9.7467943e-01 7.0710678e-01 1.0440307e+00 1.3784049e+00 7.1414284e-01 6.9282032e-01 1.9519221e+00 1.2247449e+00 8.1240384e-01 5.9160798e-01 1.1916375e+00 3.4641016e-01 1.0908712e+00 4.2426407e-01 8.3666003e-01 3.9974992e+00 3.6345564e+00 4.1725292e+00 3.3196385e+00 3.8665230e+00 3.5185224e+00 3.7868192e+00 2.6514147e+00 3.8013156e+00 3.0675723e+00 3.0430248e+00 3.3090784e+00 3.3630343e+00 3.7656341e+00 2.7294688e+00 3.6537652e+00 3.5114100e+00 3.1448370e+00 3.9458839e+00 3.0789609e+00 3.8832976e+00 3.1921779e+00 4.1581246e+00 3.7349699e+00 3.4871192e+00 3.6428011e+00 4.1024383e+00 4.2743421e+00 3.6110940e+00 2.7037012e+00 3.0446675e+00 2.9376862e+00 3.0479501e+00 4.2201896e+00 3.4942810e+00 3.5185224e+00 3.9306488e+00 3.7815341e+00 3.0935417e+00 3.2155870e+00 3.4583233e+00 3.6496575e+00 3.1733263e+00 2.7073973e+00 3.2939338e+00 3.1559468e+00 3.2280025e+00 3.4234486e+00 2.4124676e+00 3.1843367e+00 5.2782573e+00 4.3034870e+00 5.3084838e+00 4.7275787e+00 5.0793700e+00 6.0811183e+00 3.7696154e+00 5.6373753e+00 5.1176166e+00 5.5830099e+00 4.3669211e+00 4.5912961e+00 4.8754487e+00 4.3208795e+00 4.5055521e+00 4.6400431e+00 4.6679760e+00 6.1473572e+00 6.5192024e+00 4.2965102e+00 5.1166395e+00 4.1255303e+00 6.2120850e+00 4.1976184e+00 4.9527770e+00 5.2867760e+00 4.0583248e+00 4.0583248e+00 4.8928519e+00 5.0941143e+00 5.5614746e+00 5.9160798e+00 4.9345719e+00 4.2213742e+00 4.6432747e+00 5.7844619e+00 4.8785244e+00 4.6184413e+00 3.9534795e+00 4.8062459e+00 5.0348784e+00 4.6572524e+00 4.3034870e+00 5.2507142e+00 5.1273775e+00 4.6893496e+00 4.3886217e+00 4.4944410e+00 4.6411206e+00 4.1892720e+00 1.2609520e+00 1.1357817e+00 7.0710678e-01 1.2609520e+00 1.6309506e+00 9.0000000e-01 8.7177979e-01 2.1517435e+00 1.4899664e+00 9.6953597e-01 7.8102497e-01 1.3928388e+00 6.0000000e-01 1.3453624e+00 5.4772256e-01 1.0295630e+00 3.9471509e+00 3.6207734e+00 4.1364236e+00 3.4029399e+00 3.8587563e+00 3.5805028e+00 3.7815341e+00 2.8017851e+00 3.7881394e+00 3.1670175e+00 3.1843367e+00 3.3361655e+00 3.4132096e+00 3.7920970e+00 2.7838822e+00 3.6180105e+00 3.5707142e+00 3.2046841e+00 3.9736633e+00 3.1559468e+00 3.9089641e+00 3.2078030e+00 4.1797129e+00 3.7696154e+00 3.4813790e+00 3.6180105e+00 4.0804412e+00 4.2532341e+00 3.6386811e+00 2.7658633e+00 3.1320920e+00 3.0282008e+00 3.0967725e+00 4.2602817e+00 3.5707142e+00 3.5298725e+00 3.9025633e+00 3.8026307e+00 3.1543621e+00 3.2954514e+00 3.5440090e+00 3.6715120e+00 3.2264532e+00 2.8478062e+00 3.3630343e+00 3.2124757e+00 3.2832910e+00 3.4351128e+00 2.5337719e+00 3.2403703e+00 5.2820451e+00 4.3497126e+00 5.2782573e+00 4.7465777e+00 5.0793700e+00 6.0415230e+00 3.8871583e+00 5.6124861e+00 5.1234754e+00 5.5344376e+00 4.3508620e+00 4.6000000e+00 4.8528342e+00 4.3737855e+00 4.5365185e+00 4.6292548e+00 4.6701178e+00 6.0901560e+00 6.4853681e+00 4.3474130e+00 5.0852729e+00 4.1785165e+00 6.1749494e+00 4.2071368e+00 4.9345719e+00 5.2545219e+00 4.0706265e+00 4.0755368e+00 4.9010203e+00 5.0645829e+00 5.5272054e+00 5.8446557e+00 4.9406477e+00 4.2402830e+00 4.6904158e+00 5.7253821e+00 4.8744230e+00 4.6249324e+00 3.9761791e+00 4.7728398e+00 5.0129831e+00 4.6119410e+00 4.3497126e+00 5.2297227e+00 5.1019604e+00 4.6615448e+00 4.4022721e+00 4.4855323e+00 4.6411206e+00 4.2249260e+00 3.4641016e-01 7.5498344e-01 0.0000000e+00 5.5677644e-01 3.7416574e-01 5.0000000e-01 9.3808315e-01 5.5677644e-01 6.5574385e-01 8.8317609e-01 2.6457513e-01 7.4161985e-01 3.4641016e-01 7.2801099e-01 2.6457513e-01 4.0435133e+00 3.6359318e+00 4.1856899e+00 2.9478806e+00 3.7709415e+00 3.3421550e+00 3.8065733e+00 2.1307276e+00 3.7389838e+00 2.7748874e+00 2.4556058e+00 3.2031235e+00 3.0133038e+00 3.6619667e+00 2.5258662e+00 3.6523965e+00 3.3852622e+00 2.9223278e+00 3.6687873e+00 2.7586228e+00 3.8457769e+00 3.0364453e+00 3.9799497e+00 3.6027767e+00 3.4014703e+00 3.6055513e+00 4.0348482e+00 4.2497059e+00 3.4942810e+00 2.3874673e+00 2.6720778e+00 2.5495098e+00 2.8178006e+00 4.0718546e+00 3.3496268e+00 3.5425979e+00 3.9293765e+00 3.5284558e+00 2.9495762e+00 2.9000000e+00 3.1984371e+00 3.5707142e+00 2.9189039e+00 2.1679483e+00 3.0626786e+00 3.0248967e+00 3.0675723e+00 3.3181320e+00 1.9104973e+00 2.9883106e+00 5.2924474e+00 4.1436699e+00 5.3113087e+00 4.6583259e+00 5.0467812e+00 6.1081912e+00 3.4525353e+00 5.6329388e+00 4.9979996e+00 5.6973678e+00 4.3749286e+00 4.4821870e+00 4.8600412e+00 4.1060930e+00 4.3760713e+00 4.6411206e+00 4.6324939e+00 6.3071388e+00 6.4876806e+00 4.0286474e+00 5.1468437e+00 3.9686270e+00 6.2112801e+00 4.0706265e+00 4.9919936e+00 5.3329167e+00 3.9446166e+00 3.9874804e+00 4.8114447e+00 5.1029403e+00 5.5443665e+00 6.0917978e+00 4.8538644e+00 4.1194660e+00 4.4933284e+00 5.8180753e+00 4.9142650e+00 4.5978256e+00 3.8729833e+00 4.8176758e+00 5.0338852e+00 4.6690470e+00 4.1436699e+00 5.2744668e+00 5.1652686e+00 4.6669048e+00 4.2201896e+00 4.4575778e+00 4.6722586e+00 4.1060930e+00 5.9160798e-01 3.4641016e-01 6.4031242e-01 3.7416574e-01 3.3166248e-01 1.0392305e+00 6.0827625e-01 6.4031242e-01 9.4868330e-01 3.6055513e-01 7.2801099e-01 4.4721360e-01 6.5574385e-01 2.2360680e-01 4.2059482e+00 3.8131352e+00 4.3588989e+00 3.1796226e+00 3.9572718e+00 3.5707142e+00 3.9887341e+00 2.3874673e+00 3.9268308e+00 3.0033315e+00 2.7147744e+00 3.3970576e+00 3.2372828e+00 3.8716921e+00 2.7239677e+00 3.8183766e+00 3.6027767e+00 3.1527766e+00 3.8755645e+00 2.9916551e+00 4.0410395e+00 3.2280025e+00 4.1904654e+00 3.8236109e+00 3.5874782e+00 3.7788887e+00 4.2190046e+00 4.4294469e+00 3.6972963e+00 2.6038433e+00 2.9086079e+00 2.7892651e+00 3.0298515e+00 4.2918527e+00 3.5749126e+00 3.7269290e+00 4.1036569e+00 3.7349699e+00 3.1654384e+00 3.1288976e+00 3.4423829e+00 3.7749172e+00 3.1368774e+00 2.4207437e+00 3.2893768e+00 3.2449961e+00 3.2848135e+00 3.5142567e+00 2.1330729e+00 3.2046841e+00 5.4799635e+00 4.3577517e+00 5.4909016e+00 4.8682646e+00 5.2392748e+00 6.2904690e+00 3.6932371e+00 5.8266629e+00 5.2057660e+00 5.8566202e+00 4.5497253e+00 4.6808119e+00 5.0378567e+00 4.3197222e+00 4.5661800e+00 4.8145612e+00 4.8311489e+00 6.4730209e+00 6.6745786e+00 4.2579338e+00 5.3169540e+00 4.1773197e+00 6.3984373e+00 4.2649736e+00 5.1730069e+00 5.5172457e+00 4.1376322e+00 4.1833001e+00 5.0089919e+00 5.2915026e+00 5.7288742e+00 6.2489999e+00 5.0477718e+00 4.3301270e+00 4.7296934e+00 5.9791304e+00 5.0921508e+00 4.7979162e+00 4.0693980e+00 4.9869831e+00 5.2057660e+00 4.8207883e+00 4.3577517e+00 5.4534393e+00 5.3329167e+00 4.8311489e+00 4.4170126e+00 4.6400431e+00 4.8507731e+00 4.3150898e+00 7.5498344e-01 1.2083046e+00 4.5825757e-01 5.0990195e-01 1.5652476e+00 1.1401754e+00 7.0710678e-01 8.0622577e-01 8.7177979e-01 5.8309519e-01 9.5393920e-01 3.4641016e-01 5.4772256e-01 3.9166312e+00 3.5818989e+00 4.0951190e+00 3.1527766e+00 3.7509999e+00 3.4612137e+00 3.7682887e+00 2.4919872e+00 3.6972963e+00 2.9883106e+00 2.8248894e+00 3.2419130e+00 3.1416556e+00 3.7040518e+00 2.6210685e+00 3.5566838e+00 3.4914181e+00 3.0347982e+00 3.7563280e+00 2.9291637e+00 3.8807216e+00 3.0577770e+00 4.0360872e+00 3.6619667e+00 3.3734256e+00 3.5369478e+00 3.9837169e+00 4.1988094e+00 3.5411862e+00 2.5159491e+00 2.8757608e+00 2.7586228e+00 2.9137605e+00 4.1581246e+00 3.4914181e+00 3.5298725e+00 3.8535698e+00 3.5916570e+00 3.0512293e+00 3.0822070e+00 3.3793490e+00 3.5972211e+00 3.0315013e+00 2.5159491e+00 3.2046841e+00 3.1144823e+00 3.1654384e+00 3.3256578e+00 2.2045408e+00 3.0951575e+00 5.2971691e+00 4.2497059e+00 5.2516664e+00 4.6957428e+00 5.0497525e+00 6.0299254e+00 3.7215588e+00 5.5821143e+00 5.0249378e+00 5.5883808e+00 4.3324358e+00 4.5099889e+00 4.8155997e+00 4.2391037e+00 4.4564560e+00 4.6162756e+00 4.6314145e+00 6.1717096e+00 6.4358372e+00 4.1617304e+00 5.0813384e+00 4.0865633e+00 6.1424751e+00 4.0987803e+00 4.9446941e+00 5.2564246e+00 3.9736633e+00 4.0162171e+00 4.8373546e+00 5.0348784e+00 5.4799635e+00 5.9245253e+00 4.8774994e+00 4.1545156e+00 4.5934736e+00 5.7043843e+00 4.8969378e+00 4.6010868e+00 3.9127995e+00 4.7476310e+00 4.9929951e+00 4.5793013e+00 4.2497059e+00 5.2297227e+00 5.1117512e+00 4.6162756e+00 4.2684892e+00 4.4384682e+00 4.6604721e+00 4.1725292e+00 5.5677644e-01 3.7416574e-01 5.0000000e-01 9.3808315e-01 5.5677644e-01 6.5574385e-01 8.8317609e-01 2.6457513e-01 7.4161985e-01 3.4641016e-01 7.2801099e-01 2.6457513e-01 4.0435133e+00 3.6359318e+00 4.1856899e+00 2.9478806e+00 3.7709415e+00 3.3421550e+00 3.8065733e+00 2.1307276e+00 3.7389838e+00 2.7748874e+00 2.4556058e+00 3.2031235e+00 3.0133038e+00 3.6619667e+00 2.5258662e+00 3.6523965e+00 3.3852622e+00 2.9223278e+00 3.6687873e+00 2.7586228e+00 3.8457769e+00 3.0364453e+00 3.9799497e+00 3.6027767e+00 3.4014703e+00 3.6055513e+00 4.0348482e+00 4.2497059e+00 3.4942810e+00 2.3874673e+00 2.6720778e+00 2.5495098e+00 2.8178006e+00 4.0718546e+00 3.3496268e+00 3.5425979e+00 3.9293765e+00 3.5284558e+00 2.9495762e+00 2.9000000e+00 3.1984371e+00 3.5707142e+00 2.9189039e+00 2.1679483e+00 3.0626786e+00 3.0248967e+00 3.0675723e+00 3.3181320e+00 1.9104973e+00 2.9883106e+00 5.2924474e+00 4.1436699e+00 5.3113087e+00 4.6583259e+00 5.0467812e+00 6.1081912e+00 3.4525353e+00 5.6329388e+00 4.9979996e+00 5.6973678e+00 4.3749286e+00 4.4821870e+00 4.8600412e+00 4.1060930e+00 4.3760713e+00 4.6411206e+00 4.6324939e+00 6.3071388e+00 6.4876806e+00 4.0286474e+00 5.1468437e+00 3.9686270e+00 6.2112801e+00 4.0706265e+00 4.9919936e+00 5.3329167e+00 3.9446166e+00 3.9874804e+00 4.8114447e+00 5.1029403e+00 5.5443665e+00 6.0917978e+00 4.8538644e+00 4.1194660e+00 4.4933284e+00 5.8180753e+00 4.9142650e+00 4.5978256e+00 3.8729833e+00 4.8176758e+00 5.0338852e+00 4.6690470e+00 4.1436699e+00 5.2744668e+00 5.1652686e+00 4.6669048e+00 4.2201896e+00 4.4575778e+00 4.6722586e+00 4.1060930e+00 8.3066239e-01 7.8740079e-01 7.1414284e-01 2.0000000e-01 9.2736185e-01 1.2369317e+00 4.2426407e-01 1.1045361e+00 3.0000000e-01 1.1575837e+00 6.7823300e-01 4.4497191e+00 3.9962482e+00 4.5727453e+00 3.1937439e+00 4.1267421e+00 3.6304270e+00 4.1496988e+00 2.2912878e+00 4.1170378e+00 2.9883106e+00 2.6153394e+00 3.5142567e+00 3.3361655e+00 3.9874804e+00 2.8195744e+00 4.0435133e+00 3.6565011e+00 3.2449961e+00 3.9761791e+00 3.0430248e+00 4.1352146e+00 3.3808283e+00 4.3023250e+00 3.9357337e+00 3.7709415e+00 3.9862263e+00 4.4147480e+00 4.6076024e+00 3.8078866e+00 2.7073973e+00 2.9376862e+00 2.8231188e+00 3.1320920e+00 4.3646306e+00 3.5958309e+00 3.8626416e+00 4.3069711e+00 3.8626416e+00 3.2388269e+00 3.1559468e+00 3.4612137e+00 3.9012818e+00 3.2264532e+00 2.3430749e+00 3.3391616e+00 3.3316662e+00 3.3645208e+00 3.6687873e+00 2.1071308e+00 3.2832910e+00 5.5749439e+00 4.4022721e+00 5.6621551e+00 4.9668904e+00 5.3535035e+00 6.4761099e+00 3.6041643e+00 5.9983331e+00 5.3244718e+00 6.0440053e+00 4.7042534e+00 4.7937459e+00 5.1971146e+00 4.3439613e+00 4.6130250e+00 4.9446941e+00 4.9608467e+00 6.6850580e+00 6.8425142e+00 4.3104524e+00 5.4827001e+00 4.2047592e+00 6.5825527e+00 4.3840620e+00 5.3244718e+00 5.7035077e+00 4.2532341e+00 4.2906876e+00 5.1127292e+00 5.4817880e+00 5.9135438e+00 6.4915329e+00 5.1507281e+00 4.4474712e+00 4.7937459e+00 6.1919302e+00 5.2057660e+00 4.9203658e+00 4.1677332e+00 5.1652686e+00 5.3507009e+00 5.0109879e+00 4.4022721e+00 5.6008928e+00 5.4799635e+00 4.9909919e+00 4.5210618e+00 4.7812132e+00 4.9618545e+00 4.3874822e+00 2.6457513e-01 1.2727922e+00 7.5498344e-01 4.3588989e-01 6.0000000e-01 5.1961524e-01 4.1231056e-01 5.4772256e-01 3.6055513e-01 1.7320508e-01 3.9153544e+00 3.5242020e+00 4.0718546e+00 2.9715316e+00 3.6905284e+00 3.3060551e+00 3.6945906e+00 2.2181073e+00 3.6496575e+00 2.7748874e+00 2.5709920e+00 3.1272992e+00 3.0232433e+00 3.5958309e+00 2.4738634e+00 3.5355339e+00 3.3316662e+00 2.8948230e+00 3.6523965e+00 2.7622455e+00 3.7589892e+00 2.9698485e+00 3.9370039e+00 3.5496479e+00 3.3151169e+00 3.5014283e+00 3.9471509e+00 4.1496988e+00 3.4278273e+00 2.3748684e+00 2.6944387e+00 2.5768197e+00 2.7820855e+00 4.0274061e+00 3.3075671e+00 3.4307434e+00 3.8183766e+00 3.5028560e+00 2.8948230e+00 2.9034462e+00 3.1953091e+00 3.4942810e+00 2.8948230e+00 2.2583180e+00 3.0397368e+00 2.9681644e+00 3.0182777e+00 3.2419130e+00 1.9672316e+00 2.9478806e+00 5.1951901e+00 4.1024383e+00 5.2086467e+00 4.5891176e+00 4.9608467e+00 6.0024995e+00 3.4785054e+00 5.5398556e+00 4.9416596e+00 5.5587768e+00 4.2661458e+00 4.4170126e+00 4.7602521e+00 4.0816663e+00 4.3185646e+00 4.5365185e+00 4.5475268e+00 6.1611687e+00 6.4007812e+00 4.0236799e+00 5.0328918e+00 3.9255573e+00 6.1155539e+00 4.0062451e+00 4.8805737e+00 5.2211110e+00 3.8755645e+00 3.9089641e+00 4.7402532e+00 5.0019996e+00 5.4497706e+00 5.9371710e+00 4.7812132e+00 4.0558600e+00 4.4598206e+00 5.7000000e+00 4.8052055e+00 4.5099889e+00 3.7973675e+00 4.7063787e+00 4.9295030e+00 4.5497253e+00 4.1024383e+00 5.1672043e+00 5.0497525e+00 4.5628938e+00 4.1701319e+00 4.3646306e+00 4.5639895e+00 4.0398020e+00 1.3000000e+00 6.7823300e-01 4.2426407e-01 6.8556546e-01 5.4772256e-01 4.4721360e-01 5.1961524e-01 4.2426407e-01 2.4494897e-01 4.1060930e+00 3.7054015e+00 4.2626283e+00 3.1591138e+00 3.8820098e+00 3.4957117e+00 3.8704005e+00 2.3895606e+00 3.8483763e+00 2.9410882e+00 2.7531800e+00 3.3030289e+00 3.2357379e+00 3.7868192e+00 2.6476405e+00 3.7242449e+00 3.5057096e+00 3.1000000e+00 3.8483763e+00 2.9597297e+00 3.9242834e+00 3.1606961e+00 4.1340053e+00 3.7509999e+00 3.5099858e+00 3.6918830e+00 4.1460825e+00 4.3347434e+00 3.6110940e+00 2.5748786e+00 2.8896367e+00 2.7766887e+00 2.9748950e+00 4.2154478e+00 3.4770677e+00 3.5972211e+00 4.0062451e+00 3.7067506e+00 3.0740852e+00 3.0886890e+00 3.3882149e+00 3.6823905e+00 3.0903074e+00 2.4351591e+00 3.2264532e+00 3.1559468e+00 3.2031235e+00 3.4351128e+00 2.1307276e+00 3.1336879e+00 5.3535035e+00 4.2755117e+00 5.3907328e+00 4.7738873e+00 5.1341991e+00 6.1919302e+00 3.6345564e+00 5.7358522e+00 5.1371198e+00 5.7210139e+00 4.4350874e+00 4.6000000e+00 4.9365980e+00 4.2508823e+00 4.4698993e+00 4.6957428e+00 4.7318073e+00 6.3364028e+00 6.5924199e+00 4.2213742e+00 5.2019227e+00 4.0865633e+00 6.3111013e+00 4.1880783e+00 5.0527220e+00 5.4101756e+00 4.0533936e+00 4.0828911e+00 4.9173163e+00 5.1990384e+00 5.6435804e+00 6.1155539e+00 4.9547957e+00 4.2497059e+00 4.6604721e+00 5.8804762e+00 4.9598387e+00 4.6914816e+00 3.9686270e+00 4.8805737e+00 5.0941143e+00 4.7127487e+00 4.2755117e+00 5.3376025e+00 5.2086467e+00 4.7275787e+00 4.3520110e+00 4.5387223e+00 4.7180504e+00 4.2130749e+00 9.1104336e-01 1.3674794e+00 1.7262677e+00 7.6811457e-01 1.6462078e+00 9.1651514e-01 1.6278821e+00 1.1269428e+00 4.4530888e+00 4.0124805e+00 4.5607017e+00 3.0479501e+00 4.0718546e+00 3.5958309e+00 4.1821047e+00 2.1587033e+00 4.0816663e+00 2.9359837e+00 2.3811762e+00 3.5071356e+00 3.1685959e+00 3.9610605e+00 2.8035692e+00 4.0373258e+00 3.6578682e+00 3.1906112e+00 3.8183766e+00 2.9410882e+00 4.1557190e+00 3.3316662e+00 4.2047592e+00 3.8961519e+00 3.7376463e+00 3.9648455e+00 4.3588989e+00 4.5803930e+00 3.7802116e+00 2.6191602e+00 2.8106939e+00 2.6944387e+00 3.0692019e+00 4.3058100e+00 3.6027767e+00 3.9230090e+00 4.2988371e+00 3.7215588e+00 3.2465366e+00 3.0545049e+00 3.3926391e+00 3.8923001e+00 3.1432467e+00 2.1771541e+00 3.2832910e+00 3.3391616e+00 3.3481338e+00 3.6400549e+00 1.9824228e+00 3.2449961e+00 5.5830099e+00 4.3416587e+00 5.6258333e+00 4.9335586e+00 5.3244718e+00 6.4366140e+00 3.5213634e+00 5.9539903e+00 5.2325902e+00 6.0712437e+00 4.7053161e+00 4.7254629e+00 5.1633323e+00 4.2497059e+00 4.5596052e+00 4.9416596e+00 4.9376108e+00 6.7275553e+00 6.7594378e+00 4.1701319e+00 5.4708317e+00 4.1605288e+00 6.5222695e+00 4.3139309e+00 5.3329167e+00 5.6956123e+00 4.2000000e+00 4.2731721e+00 5.0586559e+00 5.4516053e+00 5.8532043e+00 6.5352888e+00 5.0950957e+00 4.4011362e+00 4.7275787e+00 6.1457302e+00 5.2297227e+00 4.9132474e+00 4.1521079e+00 5.1429563e+00 5.3272882e+00 4.9839743e+00 4.3416587e+00 5.5910643e+00 5.4808758e+00 4.9537864e+00 4.4192760e+00 4.7528939e+00 4.9909919e+00 4.3749286e+00 8.3666003e-01 1.1180340e+00 4.6904158e-01 9.6953597e-01 2.2360680e-01 1.0488088e+00 6.1644140e-01 4.4452222e+00 3.9912404e+00 4.5727453e+00 3.2434549e+00 4.1412558e+00 3.6469165e+00 4.1400483e+00 2.3515952e+00 4.1267421e+00 3.0149627e+00 2.6981475e+00 3.5199432e+00 3.3896903e+00 3.9974992e+00 2.8337255e+00 4.0435133e+00 3.6619667e+00 3.2695565e+00 4.0211939e+00 3.0822070e+00 4.1303753e+00 3.3985291e+00 4.3301270e+00 3.9509493e+00 3.7815341e+00 3.9912404e+00 4.4283180e+00 4.6119410e+00 3.8183766e+00 2.7440845e+00 2.9849623e+00 2.8722813e+00 3.1575307e+00 4.3829214e+00 3.6013886e+00 3.8470768e+00 4.3069711e+00 3.9038443e+00 3.2449961e+00 3.1937439e+00 3.4899857e+00 3.9064050e+00 3.2572995e+00 2.4103942e+00 3.3630343e+00 3.3376639e+00 3.3763886e+00 3.6796739e+00 2.1633308e+00 3.3015148e+00 5.5677644e+00 4.4204072e+00 5.6656862e+00 4.9749372e+00 5.3572381e+00 6.4791975e+00 3.6373067e+00 6.0049979e+00 5.3469618e+00 6.0274373e+00 4.7000000e+00 4.8104054e+00 5.2009614e+00 4.3714986e+00 4.6260134e+00 4.9406477e+00 4.9648766e+00 6.6640828e+00 6.8571131e+00 4.3520110e+00 5.4790510e+00 4.2190046e+00 6.5916614e+00 4.4022721e+00 5.3169540e+00 5.7000000e+00 4.2673177e+00 4.2953463e+00 5.1244512e+00 5.4854353e+00 5.9236813e+00 6.4699304e+00 5.1623638e+00 4.4609416e+00 4.8145612e+00 6.1951594e+00 5.1942276e+00 4.9203658e+00 4.1725292e+00 5.1652686e+00 5.3507009e+00 5.0109879e+00 4.4204072e+00 5.5973208e+00 5.4726593e+00 4.9949975e+00 4.5475268e+00 4.7853944e+00 4.9497475e+00 4.3920383e+00 4.7958315e-01 6.4807407e-01 5.0990195e-01 6.7082039e-01 5.4772256e-01 4.8989795e-01 3.7868192e+00 3.3570821e+00 3.9331921e+00 2.8178006e+00 3.5425979e+00 3.1432467e+00 3.5128336e+00 2.0663978e+00 3.5227830e+00 2.5709920e+00 2.4535688e+00 2.9376862e+00 2.9342802e+00 3.4380227e+00 2.2825424e+00 3.3955854e+00 3.1352831e+00 2.7730849e+00 3.5142567e+00 2.6267851e+00 3.5468296e+00 2.8195744e+00 3.7934153e+00 3.4161382e+00 3.1780497e+00 3.3600595e+00 3.8223030e+00 3.9887341e+00 3.2526912e+00 2.2516660e+00 2.5592968e+00 2.4556058e+00 2.6324893e+00 3.8587563e+00 3.1032241e+00 3.2280025e+00 3.6701499e+00 3.3852622e+00 2.7110883e+00 2.7386128e+00 3.0430248e+00 3.3316662e+00 2.7513633e+00 2.1189620e+00 2.8722813e+00 2.8035692e+00 2.8460499e+00 3.0951575e+00 1.7944358e+00 2.7784888e+00 4.9699095e+00 3.9012818e+00 5.0398413e+00 4.4147480e+00 4.7644517e+00 5.8532043e+00 3.2603681e+00 5.4018515e+00 4.7927028e+00 5.3581713e+00 4.0681691e+00 4.2402830e+00 4.5771170e+00 3.8742741e+00 4.0767634e+00 4.3162484e+00 4.3760713e+00 5.9958319e+00 6.2513998e+00 3.8807216e+00 4.8373546e+00 3.7013511e+00 5.9791304e+00 3.8288379e+00 4.6893496e+00 5.0724747e+00 3.6891733e+00 3.7134889e+00 4.5497253e+00 4.8713448e+00 5.3094256e+00 5.7879185e+00 4.5836667e+00 3.9038443e+00 4.3197222e+00 5.5389530e+00 4.5760245e+00 4.3324358e+00 3.5958309e+00 4.5232732e+00 4.7212287e+00 4.3485630e+00 3.9012818e+00 4.9709154e+00 4.8321838e+00 4.3577517e+00 3.9924930e+00 4.1737274e+00 4.3335897e+00 3.8405729e+00 9.9498744e-01 3.6055513e-01 9.4868330e-01 5.0000000e-01 7.4161985e-01 3.5791060e+00 3.1654384e+00 3.7336309e+00 2.7622455e+00 3.3852622e+00 2.9883106e+00 3.3120990e+00 2.0784610e+00 3.3406586e+00 2.4939928e+00 2.4839485e+00 2.7892651e+00 2.8530685e+00 3.2634338e+00 2.1817424e+00 3.2093613e+00 2.9765752e+00 2.6267851e+00 3.4263683e+00 2.5357445e+00 3.3719431e+00 2.6870058e+00 3.6523965e+00 3.2372828e+00 3.0116441e+00 3.1843367e+00 3.6469165e+00 3.8078866e+00 3.0967725e+00 2.1725561e+00 2.4939928e+00 2.3916521e+00 2.5179357e+00 3.7013511e+00 2.9495762e+00 3.0282008e+00 3.4785054e+00 3.2787193e+00 2.5573424e+00 2.6589472e+00 2.9137605e+00 3.1511903e+00 2.6419690e+00 2.1400935e+00 2.7495454e+00 2.6324893e+00 2.6962938e+00 2.9308702e+00 1.8411953e+00 2.6476405e+00 4.7864392e+00 3.7669616e+00 4.8507731e+00 4.2308392e+00 4.5880279e+00 5.6453521e+00 3.1906112e+00 5.1932649e+00 4.6281746e+00 5.1478151e+00 3.8884444e+00 4.0877867e+00 4.4022721e+00 3.7709415e+00 3.9661064e+00 4.1496988e+00 4.1856899e+00 5.7480431e+00 6.0671245e+00 3.7669616e+00 4.6529560e+00 3.5791060e+00 5.7758116e+00 3.6891733e+00 4.4877611e+00 4.8518038e+00 3.5468296e+00 3.5496479e+00 4.3897608e+00 4.6583259e+00 5.1166395e+00 5.5362442e+00 4.4294469e+00 3.7269290e+00 4.1388404e+00 5.3525695e+00 4.3920383e+00 4.1352146e+00 3.4380227e+00 4.3439613e+00 4.5541190e+00 4.1928511e+00 3.7669616e+00 4.7812132e+00 4.6540305e+00 4.2071368e+00 3.8716921e+00 4.0062451e+00 4.1509035e+00 3.6715120e+00 8.8317609e-01 3.0000000e-01 8.7177979e-01 3.7416574e-01 4.1206796e+00 3.6945906e+00 4.2555846e+00 2.9563491e+00 3.8223030e+00 3.3852622e+00 3.8626416e+00 2.1142375e+00 3.8065733e+00 2.7766887e+00 2.4372115e+00 3.2388269e+00 3.0545049e+00 3.7148351e+00 2.5475478e+00 3.7188708e+00 3.4190642e+00 2.9782545e+00 3.6945906e+00 2.7892651e+00 3.8807216e+00 3.0805844e+00 4.0236799e+00 3.6646964e+00 3.4612137e+00 3.6674242e+00 4.1000000e+00 4.3046487e+00 3.5355339e+00 2.4228083e+00 2.6925824e+00 2.5748786e+00 2.8548205e+00 4.1121770e+00 3.3778692e+00 3.5916570e+00 3.9937451e+00 3.5693137e+00 2.9883106e+00 2.9154759e+00 3.2341923e+00 3.6249138e+00 2.9546573e+00 2.1517435e+00 3.0935417e+00 3.0757113e+00 3.1080541e+00 3.3734256e+00 1.8814888e+00 3.0232433e+00 5.3235327e+00 4.1641326e+00 5.3646994e+00 4.7063787e+00 5.0852729e+00 6.1741396e+00 3.4394767e+00 5.7026310e+00 5.0467812e+00 5.7489129e+00 4.4170126e+00 4.5188494e+00 4.9040799e+00 4.1121770e+00 4.3749286e+00 4.6701178e+00 4.6850827e+00 6.3835727e+00 6.5436993e+00 4.0595566e+00 5.1903757e+00 3.9774364e+00 6.2793312e+00 4.1036569e+00 5.0428167e+00 5.4046276e+00 3.9761791e+00 4.0236799e+00 4.8456166e+00 5.1778374e+00 5.6080300e+00 6.1757591e+00 4.8836462e+00 4.1737274e+00 4.5497253e+00 5.8736701e+00 4.9457052e+00 4.6508064e+00 3.9051248e+00 4.8641546e+00 5.0665570e+00 4.7021272e+00 4.1641326e+00 5.3188345e+00 5.1990384e+00 4.6957428e+00 4.2449971e+00 4.4966654e+00 4.7031904e+00 4.1412558e+00 8.0622577e-01 2.4494897e-01 5.4772256e-01 3.8755645e+00 3.4856850e+00 4.0385641e+00 3.0626786e+00 3.6945906e+00 3.3136083e+00 3.6414283e+00 2.3515952e+00 3.6428011e+00 2.8195744e+00 2.7386128e+00 3.1192948e+00 3.1256999e+00 3.5860842e+00 2.5039968e+00 3.5114100e+00 3.3151169e+00 2.9308702e+00 3.7242449e+00 2.8354894e+00 3.7148351e+00 2.9949958e+00 3.9635842e+00 3.5510562e+00 3.3166248e+00 3.4885527e+00 3.9458839e+00 4.1243181e+00 3.4234486e+00 2.4596748e+00 2.7874720e+00 2.6776856e+00 2.8266588e+00 4.0286474e+00 3.2908965e+00 3.3674916e+00 3.7881394e+00 3.5693137e+00 2.8896367e+00 2.9698485e+00 3.2310989e+00 3.4756294e+00 2.9478806e+00 2.4062419e+00 3.0708305e+00 2.9597297e+00 3.0232433e+00 3.2434549e+00 2.1118712e+00 2.9698485e+00 5.1322510e+00 4.1036569e+00 5.1710734e+00 4.5617979e+00 4.9234135e+00 5.9581876e+00 3.5199432e+00 5.5045436e+00 4.9446941e+00 5.4763126e+00 4.2201896e+00 4.4136153e+00 4.7275787e+00 4.1048752e+00 4.3104524e+00 4.4888751e+00 4.5133136e+00 6.0638272e+00 6.3796552e+00 4.0767634e+00 4.9819675e+00 3.9217343e+00 6.0835845e+00 4.0124805e+00 4.8197510e+00 5.1662365e+00 3.8742741e+00 3.8845849e+00 4.7222876e+00 4.9648766e+00 5.4249424e+00 5.8412327e+00 4.7634021e+00 4.0472213e+00 4.4586994e+00 5.6621551e+00 4.7370877e+00 4.4665423e+00 3.7749172e+00 4.6669048e+00 4.8877398e+00 4.5155288e+00 4.1036569e+00 5.1137071e+00 4.9909919e+00 4.5354162e+00 4.1928511e+00 4.3358967e+00 4.4966654e+00 4.0112342e+00 8.6602540e-01 4.1231056e-01 4.2532341e+00 3.8131352e+00 4.3863424e+00 3.0967725e+00 3.9623226e+00 3.4914181e+00 3.9686270e+00 2.2315914e+00 3.9420807e+00 2.8809721e+00 2.5787594e+00 3.3555923e+00 3.2186954e+00 3.8301436e+00 2.6720778e+00 3.8548671e+00 3.5128336e+00 3.1016125e+00 3.8548671e+00 2.9240383e+00 3.9761791e+00 3.2218007e+00 4.1617304e+00 3.7815341e+00 3.5986108e+00 3.8052595e+00 4.2426407e+00 4.4339599e+00 3.6537652e+00 2.5729361e+00 2.8319605e+00 2.7166155e+00 2.9899833e+00 4.2261093e+00 3.4612137e+00 3.6837481e+00 4.1231056e+00 3.7296112e+00 3.0886890e+00 3.0446675e+00 3.3421550e+00 3.7376463e+00 3.0919250e+00 2.2847319e+00 3.2093613e+00 3.1764760e+00 3.2171416e+00 3.5028560e+00 2.0273135e+00 3.1416556e+00 5.4175640e+00 4.2743421e+00 5.4909016e+00 4.8145612e+00 5.1971146e+00 6.3000000e+00 3.5270384e+00 5.8266629e+00 5.1788030e+00 5.8566202e+00 4.5321077e+00 4.6465041e+00 5.0299105e+00 4.2308392e+00 4.4866469e+00 4.7812132e+00 4.7979162e+00 6.4853681e+00 6.6805688e+00 4.1964271e+00 5.3094256e+00 4.0804412e+00 6.4109282e+00 4.2367440e+00 5.1497573e+00 5.5208695e+00 4.1036569e+00 4.1352146e+00 4.9648766e+00 5.3028294e+00 5.7428216e+00 6.2841069e+00 5.0039984e+00 4.2930176e+00 4.6572524e+00 6.0124870e+00 5.0408333e+00 4.7560488e+00 4.0149720e+00 4.9909919e+00 5.1865210e+00 4.8373546e+00 4.2743421e+00 5.4313902e+00 5.3103672e+00 4.8270074e+00 4.3852024e+00 4.6184413e+00 4.7968740e+00 4.2402830e+00 5.0990195e-01 3.8496753e+00 3.4856850e+00 4.0211939e+00 3.0757113e+00 3.6810325e+00 3.3436507e+00 3.6551334e+00 2.3937418e+00 3.6262929e+00 2.8653098e+00 2.7604347e+00 3.1352831e+00 3.1032241e+00 3.6000000e+00 2.5199206e+00 3.4885527e+00 3.3570821e+00 2.9410882e+00 3.7080992e+00 2.8460499e+00 3.7496667e+00 2.9849623e+00 3.9610605e+00 3.5623026e+00 3.3015148e+00 3.4684290e+00 3.9230090e+00 4.1170378e+00 3.4380227e+00 2.4515301e+00 2.7982137e+00 2.6851443e+00 2.8301943e+00 4.0509258e+00 3.3451457e+00 3.3970576e+00 3.7749172e+00 3.5468296e+00 2.9240383e+00 2.9899833e+00 3.2649655e+00 3.4899857e+00 2.9512709e+00 2.4351591e+00 3.0967725e+00 2.9899833e+00 3.0495901e+00 3.2403703e+00 2.1307276e+00 2.9899833e+00 5.1672043e+00 4.1352146e+00 5.1672043e+00 4.5836667e+00 4.9416596e+00 5.9497899e+00 3.5846897e+00 5.4990908e+00 4.9446941e+00 5.4836119e+00 4.2296572e+00 4.4204072e+00 4.7275787e+00 4.1340053e+00 4.3428102e+00 4.5066617e+00 4.5265881e+00 6.0671245e+00 6.3671030e+00 4.0841156e+00 4.9859803e+00 3.9623226e+00 6.0704201e+00 4.0149720e+00 4.8342528e+00 5.1643005e+00 3.8820098e+00 3.9051248e+00 4.7370877e+00 4.9547957e+00 5.4101756e+00 5.8326666e+00 4.7780749e+00 4.0570926e+00 4.4833024e+00 5.6409219e+00 4.7686476e+00 4.4866469e+00 3.7986840e+00 4.6626173e+00 4.8959167e+00 4.5044423e+00 4.1352146e+00 5.1254268e+00 5.0049975e+00 4.5332108e+00 4.1928511e+00 4.3428102e+00 4.5299007e+00 4.0459857e+00 4.0422766e+00 3.6428011e+00 4.1940434e+00 3.0364453e+00 3.7986840e+00 3.4000000e+00 3.8131352e+00 2.2516660e+00 3.7643060e+00 2.8442925e+00 2.5961510e+00 3.2295511e+00 3.1000000e+00 3.7013511e+00 2.5632011e+00 3.6565011e+00 3.4278273e+00 2.9883106e+00 3.7349699e+00 2.8390139e+00 3.8652296e+00 3.0708305e+00 4.0336088e+00 3.6537652e+00 3.4263683e+00 3.6180105e+00 4.0607881e+00 4.2649736e+00 3.5298725e+00 2.4556058e+00 2.7622455e+00 2.6438608e+00 2.8722813e+00 4.1243181e+00 3.3985291e+00 3.5468296e+00 3.9382737e+00 3.5916570e+00 2.9916551e+00 2.9765752e+00 3.2771939e+00 3.6027767e+00 2.9816103e+00 2.2912878e+00 3.1256999e+00 3.0692019e+00 3.1144823e+00 3.3496268e+00 2.0049938e+00 3.0397368e+00 5.3047149e+00 4.1928511e+00 5.3254108e+00 4.6957428e+00 5.0695167e+00 6.1237244e+00 3.5369478e+00 5.6586217e+00 5.0447993e+00 5.6841886e+00 4.3806392e+00 4.5188494e+00 4.8733972e+00 4.1629317e+00 4.4068129e+00 4.6465041e+00 4.6593991e+00 6.2952363e+00 6.5145990e+00 4.1060930e+00 5.1497573e+00 4.0124805e+00 6.2345810e+00 4.1060930e+00 4.9989999e+00 5.3450912e+00 3.9761791e+00 4.0137264e+00 4.8435524e+00 5.1234754e+00 5.5668663e+00 6.0745370e+00 4.8836462e+00 4.1617304e+00 4.5585085e+00 5.8206529e+00 4.9173163e+00 4.6227697e+00 3.9000000e+00 4.8228622e+00 5.0408333e+00 4.6636895e+00 4.1928511e+00 5.2829916e+00 5.1643005e+00 4.6722586e+00 4.2638011e+00 4.4743715e+00 4.6754679e+00 4.1412558e+00 6.4031242e-01 2.6457513e-01 1.8867962e+00 6.5574385e-01 1.3784049e+00 7.3484692e-01 2.6776856e+00 5.1961524e-01 2.0322401e+00 2.6532998e+00 1.2288206e+00 1.6278821e+00 9.4868330e-01 1.8083141e+00 4.3588989e-01 1.4317821e+00 1.4866069e+00 1.3000000e+00 1.7832555e+00 1.1747340e+00 1.2124356e+00 1.0148892e+00 1.0049876e+00 7.8740079e-01 5.3851648e-01 4.5825757e-01 5.5677644e-01 1.0677078e+00 1.9104973e+00 1.9467922e+00 2.0124612e+00 1.5394804e+00 1.2041595e+00 1.6278821e+00 1.0583005e+00 3.3166248e-01 1.1832160e+00 1.5394804e+00 1.8000000e+00 1.6552945e+00 9.2736185e-01 1.5264338e+00 2.6324893e+00 1.5716234e+00 1.4212670e+00 1.4282857e+00 9.4868330e-01 2.6608269e+00 1.4899664e+00 1.8439089e+00 1.4491377e+00 1.4071247e+00 1.2449900e+00 1.4628739e+00 2.1213203e+00 2.2427661e+00 1.7029386e+00 1.3964240e+00 1.8357560e+00 8.7749644e-01 1.1045361e+00 1.1000000e+00 1.6217275e+00 1.6613248e+00 1.2369317e+00 1.0440307e+00 2.3430749e+00 2.5495098e+00 1.4491377e+00 1.3490738e+00 1.5874508e+00 2.2383029e+00 9.6953597e-01 1.2609520e+00 1.3747727e+00 9.8488578e-01 1.0246951e+00 1.3490738e+00 1.1532563e+00 1.5905974e+00 2.1023796e+00 1.4035669e+00 9.0553851e-01 1.4071247e+00 1.8165902e+00 1.5297059e+00 1.0816654e+00 1.1000000e+00 1.0000000e+00 1.3820275e+00 9.9498744e-01 1.4491377e+00 1.5132746e+00 1.5198684e+00 1.0908712e+00 1.1489125e+00 9.4868330e-01 1.4071247e+00 1.2529964e+00 6.4807407e-01 1.3820275e+00 4.2426407e-01 8.3066239e-01 2.6457513e-01 2.1400935e+00 4.2426407e-01 1.4352700e+00 2.1563859e+00 6.1644140e-01 1.2884099e+00 4.7958315e-01 1.2569805e+00 3.4641016e-01 8.2462113e-01 1.0099505e+00 1.0198039e+00 1.2845233e+00 6.5574385e-01 7.3484692e-01 8.1240384e-01 6.1644140e-01 4.1231056e-01 3.1622777e-01 6.4807407e-01 6.4807407e-01 5.0000000e-01 1.4491377e+00 1.4491377e+00 1.5297059e+00 1.0295630e+00 8.8317609e-01 1.0198039e+00 4.5825757e-01 3.7416574e-01 9.3273791e-01 9.3808315e-01 1.2609520e+00 1.1269428e+00 3.8729833e-01 1.0295630e+00 2.1118712e+00 1.0099505e+00 8.4261498e-01 8.4261498e-01 4.5825757e-01 2.1424285e+00 9.2195445e-01 1.8083141e+00 1.0630146e+00 1.6881943e+00 1.1832160e+00 1.4933185e+00 2.5000000e+00 1.6673332e+00 2.0566964e+00 1.5362291e+00 2.0880613e+00 7.8740079e-01 1.0246951e+00 1.2489996e+00 1.2165525e+00 1.3000000e+00 1.1313708e+00 1.0677078e+00 2.7166155e+00 2.9068884e+00 1.1874342e+00 1.5264338e+00 1.1000000e+00 2.6343880e+00 7.1414284e-01 1.3784049e+00 1.7262677e+00 6.1644140e-01 6.1644140e-01 1.3152946e+00 1.5427249e+00 1.9697716e+00 2.5436195e+00 1.3638182e+00 7.2801099e-01 1.2922848e+00 2.2203603e+00 1.4387495e+00 1.0488088e+00 6.1644140e-01 1.1958261e+00 1.4560220e+00 1.1224972e+00 1.0630146e+00 1.6613248e+00 1.5937377e+00 1.1224972e+00 9.5393920e-01 8.8881944e-01 1.2369317e+00 8.6023253e-01 1.8574176e+00 5.8309519e-01 1.3152946e+00 6.7082039e-01 2.7018512e+00 5.0990195e-01 2.0149442e+00 2.6514147e+00 1.2247449e+00 1.6370706e+00 8.5440037e-01 1.8601075e+00 5.4772256e-01 1.3638182e+00 1.5033296e+00 1.2083046e+00 1.7916473e+00 1.0535654e+00 1.2569805e+00 8.4852814e-01 9.2736185e-01 8.3066239e-01 6.0000000e-01 3.4641016e-01 3.1622777e-01 1.0049876e+00 1.9748418e+00 1.9544820e+00 2.0346990e+00 1.5684387e+00 1.0099505e+00 1.5556349e+00 1.0344080e+00 2.8284271e-01 1.1357817e+00 1.5427249e+00 1.7804494e+00 1.5968719e+00 8.6602540e-01 1.5362291e+00 2.6570661e+00 1.5427249e+00 1.4247807e+00 1.4177447e+00 9.6436508e-01 2.7147744e+00 1.4866069e+00 1.6155494e+00 1.2529964e+00 1.1874342e+00 9.8994949e-01 1.2124356e+00 1.9364917e+00 2.1354157e+00 1.5000000e+00 1.1401754e+00 1.6673332e+00 6.7823300e-01 8.5440037e-01 8.6023253e-01 1.4352700e+00 1.4662878e+00 1.0295630e+00 7.8740079e-01 2.2045408e+00 2.3515952e+00 1.2767145e+00 1.1357817e+00 1.4247807e+00 2.0542639e+00 7.8102497e-01 1.0392305e+00 1.1832160e+00 8.2462113e-01 8.6023253e-01 1.0908712e+00 9.5916630e-01 1.3928388e+00 1.9974984e+00 1.1489125e+00 7.0000000e-01 1.1789826e+00 1.6522712e+00 1.3228757e+00 8.3666003e-01 9.5916630e-01 7.8102497e-01 1.1575837e+00 8.2462113e-01 1.2529964e+00 1.2884099e+00 1.3114877e+00 8.8317609e-01 9.4339811e-01 7.1414284e-01 1.2124356e+00 1.0677078e+00 1.2845233e+00 7.3484692e-01 1.4899664e+00 9.7467943e-01 1.3892444e+00 5.1961524e-01 8.2462113e-01 8.5440037e-01 5.9160798e-01 1.1045361e+00 7.2801099e-01 1.5000000e+00 8.8881944e-01 5.9160798e-01 8.8881944e-01 3.1622777e-01 1.3638182e+00 7.8102497e-01 1.2369317e+00 1.0535654e+00 1.1224972e+00 1.3674794e+00 1.6093477e+00 1.7578396e+00 9.4868330e-01 6.8556546e-01 3.0000000e-01 4.3588989e-01 5.1961524e-01 1.3076697e+00 8.8881944e-01 1.3416408e+00 1.6155494e+00 8.9442719e-01 7.1414284e-01 2.0000000e-01 5.0990195e-01 1.1045361e+00 4.3588989e-01 9.1104336e-01 4.5825757e-01 7.6157731e-01 6.6332496e-01 9.6953597e-01 1.1135529e+00 5.4772256e-01 2.6608269e+00 1.3490738e+00 2.7018512e+00 1.9519221e+00 2.3537205e+00 3.5071356e+00 9.0000000e-01 3.0232433e+00 2.2293497e+00 3.2295511e+00 1.8734994e+00 1.7378147e+00 2.2516660e+00 1.2529964e+00 1.6613248e+00 2.0760539e+00 1.9974984e+00 3.8974351e+00 3.7868192e+00 1.1401754e+00 2.5806976e+00 1.2489996e+00 3.5874782e+00 1.3638182e+00 2.4433583e+00 2.8195744e+00 1.2767145e+00 1.3820275e+00 2.0639767e+00 2.5903668e+00 2.9376862e+00 3.7762415e+00 2.1047565e+00 1.4628739e+00 1.7378147e+00 3.2771939e+00 2.3706539e+00 1.9874607e+00 1.2767145e+00 2.2803509e+00 2.4186773e+00 2.1931712e+00 1.3490738e+00 2.6664583e+00 2.6019224e+00 2.0904545e+00 1.4282857e+00 1.8493242e+00 2.1587033e+00 1.4525839e+00 8.3066239e-01 5.5677644e-01 2.1587033e+00 2.4494897e-01 1.4832397e+00 2.0856654e+00 7.4833148e-01 1.1045361e+00 4.3588989e-01 1.3638182e+00 4.2426407e-01 9.2736185e-01 1.0000000e+00 6.7823300e-01 1.2449900e+00 8.0622577e-01 7.4833148e-01 4.6904158e-01 5.0990195e-01 3.8729833e-01 3.1622777e-01 3.7416574e-01 5.2915026e-01 5.1961524e-01 1.4628739e+00 1.4000000e+00 1.4899664e+00 1.0392305e+00 7.2111026e-01 1.1224972e+00 7.9372539e-01 3.7416574e-01 6.0827625e-01 1.0677078e+00 1.2206556e+00 1.0816654e+00 4.5825757e-01 9.8994949e-01 2.1071308e+00 1.0099505e+00 9.6436508e-01 9.2195445e-01 4.7958315e-01 2.1840330e+00 9.6436508e-01 1.8027756e+00 9.5393920e-01 1.5652476e+00 1.0677078e+00 1.4035669e+00 2.3685439e+00 1.6431677e+00 1.9052559e+00 1.2884099e+00 2.0928450e+00 8.1240384e-01 8.1853528e-01 1.1401754e+00 1.0677078e+00 1.2449900e+00 1.1401754e+00 9.6953597e-01 2.7092434e+00 2.7221315e+00 8.7749644e-01 1.4730920e+00 1.0723805e+00 2.4698178e+00 4.7958315e-01 1.3638182e+00 1.6431677e+00 4.6904158e-01 6.1644140e-01 1.1704700e+00 1.4071247e+00 1.7944358e+00 2.5396850e+00 1.2247449e+00 5.3851648e-01 1.1000000e+00 2.0904545e+00 1.4866069e+00 1.0000000e+00 6.4807407e-01 1.1180340e+00 1.3928388e+00 1.0677078e+00 9.5393920e-01 1.6062378e+00 1.5811388e+00 1.0392305e+00 6.7082039e-01 8.0622577e-01 1.3152946e+00 8.6023253e-01 8.6023253e-01 1.5264338e+00 9.1104336e-01 7.9372539e-01 1.4899664e+00 4.5825757e-01 8.8881944e-01 4.6904158e-01 9.1104336e-01 1.0535654e+00 3.0000000e-01 5.1961524e-01 8.0622577e-01 7.0710678e-01 7.3484692e-01 6.4031242e-01 8.0622577e-01 4.5825757e-01 7.3484692e-01 9.3273791e-01 1.1445523e+00 1.2041595e+00 3.7416574e-01 1.0630146e+00 8.5440037e-01 9.6436508e-01 6.2449980e-01 7.4161985e-01 4.1231056e-01 7.3484692e-01 1.0816654e+00 7.8740079e-01 4.5825757e-01 6.1644140e-01 3.1622777e-01 4.6904158e-01 5.5677644e-01 1.5066519e+00 3.3166248e-01 3.7416574e-01 3.1622777e-01 5.4772256e-01 1.6552945e+00 4.0000000e-01 2.0736441e+00 8.6023253e-01 2.1447611e+00 1.3527749e+00 1.7832555e+00 2.9495762e+00 9.4339811e-01 2.4617067e+00 1.7406895e+00 2.6248809e+00 1.2845233e+00 1.2247449e+00 1.7000000e+00 9.1104336e-01 1.2569805e+00 1.5132746e+00 1.3892444e+00 3.2634338e+00 3.2863353e+00 8.6023253e-01 2.0099751e+00 8.1240384e-01 3.0545049e+00 8.8317609e-01 1.8248288e+00 2.2158520e+00 7.6811457e-01 7.8102497e-01 1.5297059e+00 2.0174241e+00 2.4103942e+00 3.1527766e+00 1.5842980e+00 8.7177979e-01 1.1916375e+00 2.7568098e+00 1.7720045e+00 1.3527749e+00 6.8556546e-01 1.7262677e+00 1.8734994e+00 1.7000000e+00 8.6023253e-01 2.0808652e+00 2.0322401e+00 1.5905974e+00 1.0295630e+00 1.2884099e+00 1.5556349e+00 8.3066239e-01 2.2561028e+00 5.9160798e-01 1.5000000e+00 2.2759613e+00 7.1414284e-01 1.4662878e+00 4.8989795e-01 1.3964240e+00 5.7445626e-01 7.9372539e-01 1.1532563e+00 1.1269428e+00 1.4212670e+00 4.6904158e-01 9.3273791e-01 8.3066239e-01 6.7082039e-01 6.4807407e-01 5.5677644e-01 7.4161985e-01 5.9160798e-01 5.4772256e-01 1.6278821e+00 1.5842980e+00 1.6763055e+00 1.1874342e+00 7.8102497e-01 9.7467943e-01 3.7416574e-01 4.5825757e-01 1.0862780e+00 1.0148892e+00 1.3638182e+00 1.1747340e+00 4.2426407e-01 1.1789826e+00 2.2383029e+00 1.0908712e+00 9.2736185e-01 9.2736185e-01 6.4807407e-01 2.2847319e+00 1.0295630e+00 1.5811388e+00 9.2736185e-01 1.5556349e+00 1.0049876e+00 1.3038405e+00 2.3748684e+00 1.6278821e+00 1.9390719e+00 1.4317821e+00 1.9157244e+00 6.0827625e-01 9.0553851e-01 1.1090537e+00 1.1180340e+00 1.1401754e+00 9.3273791e-01 9.0000000e-01 2.5632011e+00 2.7892651e+00 1.1832160e+00 1.3638182e+00 9.6953597e-01 2.5238859e+00 6.6332496e-01 1.1874342e+00 1.5968719e+00 5.5677644e-01 4.5825757e-01 1.1489125e+00 1.4525839e+00 1.8734994e+00 2.4207437e+00 1.1958261e+00 6.4807407e-01 1.1747340e+00 2.1213203e+00 1.2083046e+00 8.5440037e-01 4.7958315e-01 1.0677078e+00 1.2845233e+00 1.0246951e+00 9.2736185e-01 1.4798649e+00 1.4035669e+00 9.9498744e-01 9.0553851e-01 7.3484692e-01 1.0000000e+00 6.7082039e-01 2.2181073e+00 8.3666003e-01 4.5825757e-01 1.5556349e+00 1.3190906e+00 1.9519221e+00 9.5916630e-01 2.2583180e+00 1.5937377e+00 1.2409674e+00 1.8493242e+00 9.3273791e-01 2.1283797e+00 1.4764823e+00 2.1863211e+00 1.8973666e+00 1.8947295e+00 2.1494185e+00 2.4859606e+00 2.6419690e+00 1.7748239e+00 8.4852814e-01 7.8740079e-01 7.2111026e-01 1.1401754e+00 2.2135944e+00 1.5165751e+00 2.0024984e+00 2.4372115e+00 1.8083141e+00 1.2569805e+00 9.7467943e-01 1.2845233e+00 1.9104973e+00 1.1747340e+00 1.4142136e-01 1.2165525e+00 1.3601471e+00 1.3379088e+00 1.7406895e+00 3.8729833e-01 1.2369317e+00 3.5085610e+00 2.2248595e+00 3.6290495e+00 2.8530685e+00 3.2572995e+00 4.4440972e+00 1.3928388e+00 3.9560081e+00 3.1843367e+00 4.1012193e+00 2.7276363e+00 2.6739484e+00 3.1654384e+00 2.1307276e+00 2.4839485e+00 2.9291637e+00 2.8982753e+00 4.7749346e+00 4.7465777e+00 2.0952327e+00 3.4770677e+00 2.0518285e+00 4.5343136e+00 2.2912878e+00 3.3196385e+00 3.7229021e+00 2.1771541e+00 2.2360680e+00 2.9849623e+00 3.5014283e+00 3.8807216e+00 4.6443514e+00 3.0232433e+00 2.3685439e+00 2.6324893e+00 4.2107007e+00 3.1953091e+00 2.8670542e+00 2.1118712e+00 3.1796226e+00 3.3136083e+00 3.0692019e+00 2.2248595e+00 3.5637059e+00 3.4727511e+00 2.9832868e+00 2.3811762e+00 2.7440845e+00 2.9647934e+00 2.2891046e+00 1.5811388e+00 2.1610183e+00 8.3666003e-01 1.1401754e+00 5.1961524e-01 1.4142136e+00 3.1622777e-01 1.0295630e+00 1.0099505e+00 8.3666003e-01 1.3000000e+00 9.3273791e-01 7.8740079e-01 6.1644140e-01 5.2915026e-01 3.6055513e-01 2.4494897e-01 3.1622777e-01 5.8309519e-01 6.4031242e-01 1.4832397e+00 1.4628739e+00 1.5362291e+00 1.0862780e+00 8.6023253e-01 1.2247449e+00 8.4261498e-01 3.1622777e-01 7.0000000e-01 1.1224972e+00 1.3152946e+00 1.1618950e+00 5.1961524e-01 1.0488088e+00 2.1679483e+00 1.0954451e+00 9.9498744e-01 9.8488578e-01 5.0000000e-01 2.2383029e+00 1.0344080e+00 1.9104973e+00 1.1357817e+00 1.6093477e+00 1.1575837e+00 1.5066519e+00 2.3769729e+00 1.7944358e+00 1.9052559e+00 1.3638182e+00 2.1307276e+00 9.1651514e-01 9.6436508e-01 1.2247449e+00 1.2727922e+00 1.4525839e+00 1.2727922e+00 1.0392305e+00 2.6907248e+00 2.7549955e+00 1.0246951e+00 1.5459625e+00 1.2609520e+00 2.4738634e+00 6.8556546e-01 1.4212670e+00 1.6309506e+00 6.7823300e-01 7.7459667e-01 1.3000000e+00 1.3784049e+00 1.8055470e+00 2.4959968e+00 1.3638182e+00 6.2449980e-01 1.1618950e+00 2.1142375e+00 1.5968719e+00 1.0677078e+00 8.1240384e-01 1.1874342e+00 1.5033296e+00 1.1747340e+00 1.1357817e+00 1.6792856e+00 1.6792856e+00 1.1747340e+00 8.7749644e-01 9.3273791e-01 1.4317821e+00 1.0000000e+00 9.2195445e-01 8.2462113e-01 1.0295630e+00 1.2206556e+00 5.4772256e-01 1.6309506e+00 7.8740079e-01 7.4833148e-01 1.2727922e+00 5.3851648e-01 1.3076697e+00 9.1651514e-01 1.5033296e+00 1.2247449e+00 1.2845233e+00 1.5165751e+00 1.8384776e+00 1.9078784e+00 1.0246951e+00 7.6157731e-01 5.2915026e-01 6.1644140e-01 6.3245553e-01 1.4560220e+00 7.0710678e-01 1.2369317e+00 1.7492856e+00 1.2767145e+00 5.4772256e-01 3.8729833e-01 6.2449980e-01 1.1789826e+00 6.4807407e-01 8.4852814e-01 5.0990195e-01 6.8556546e-01 6.2449980e-01 1.1000000e+00 9.7467943e-01 5.5677644e-01 2.6814175e+00 1.4317821e+00 2.8618176e+00 2.0736441e+00 2.4556058e+00 3.6918830e+00 7.6157731e-01 3.2202484e+00 2.4617067e+00 3.2954514e+00 1.9339080e+00 1.9104973e+00 2.3874673e+00 1.3638182e+00 1.6763055e+00 2.1118712e+00 2.1213203e+00 3.9924930e+00 4.0087405e+00 1.4525839e+00 2.6814175e+00 1.2369317e+00 3.8026307e+00 1.5394804e+00 2.5179357e+00 2.9698485e+00 1.4071247e+00 1.4352700e+00 2.1977261e+00 2.7820855e+00 3.1527766e+00 3.8871583e+00 2.2315914e+00 1.6340135e+00 1.9261360e+00 3.4626579e+00 2.3643181e+00 2.0784610e+00 1.3038405e+00 2.4062419e+00 2.5099801e+00 2.3021729e+00 1.4317821e+00 2.7604347e+00 2.6570661e+00 2.2000000e+00 1.6462078e+00 1.9570386e+00 2.1330729e+00 1.4764823e+00 1.5968719e+00 1.1357817e+00 1.9026298e+00 1.1269428e+00 2.2516660e+00 1.6155494e+00 1.2206556e+00 1.6522712e+00 8.8317609e-01 2.1400935e+00 1.4798649e+00 2.0371549e+00 1.8248288e+00 1.8708287e+00 2.1283797e+00 2.3937418e+00 2.5748786e+00 1.7492856e+00 9.2195445e-01 7.1414284e-01 6.7082039e-01 1.1532563e+00 2.1000000e+00 1.5524175e+00 2.0784610e+00 2.4062419e+00 1.6370706e+00 1.3453624e+00 9.1651514e-01 1.2083046e+00 1.8920888e+00 1.1357817e+00 3.6055513e-01 1.1958261e+00 1.4212670e+00 1.3711309e+00 1.7262677e+00 7.2111026e-01 1.2569805e+00 3.4467376e+00 2.1213203e+00 3.5185224e+00 2.7477263e+00 3.1591138e+00 4.3104524e+00 1.3228757e+00 3.8183766e+00 3.0116441e+00 4.0509258e+00 2.6925824e+00 2.5495098e+00 3.0740852e+00 1.9974984e+00 2.4083189e+00 2.8861739e+00 2.8089144e+00 4.7127487e+00 4.5716518e+00 1.8814888e+00 3.4029399e+00 1.9899749e+00 4.3783559e+00 2.1863211e+00 3.2603681e+00 3.6290495e+00 2.1000000e+00 2.1931712e+00 2.8670542e+00 3.3896903e+00 3.7376463e+00 4.5891176e+00 2.9068884e+00 2.2671568e+00 2.4779023e+00 4.0914545e+00 3.1654384e+00 2.7946377e+00 2.0808652e+00 3.1048349e+00 3.2357379e+00 3.0116441e+00 2.1213203e+00 3.4828150e+00 3.4161382e+00 2.9103264e+00 2.2360680e+00 2.6720778e+00 2.9495762e+00 2.2383029e+00 9.6953597e-01 5.5677644e-01 7.0710678e-01 8.3666003e-01 4.2426407e-01 6.0000000e-01 9.0553851e-01 7.6811457e-01 7.0000000e-01 4.0000000e-01 9.4868330e-01 6.4807407e-01 5.5677644e-01 7.3484692e-01 1.1045361e+00 1.1489125e+00 3.3166248e-01 9.6953597e-01 9.1651514e-01 1.0099505e+00 5.2915026e-01 9.5916630e-01 5.8309519e-01 5.1961524e-01 9.4868330e-01 8.5440037e-01 3.7416574e-01 7.0000000e-01 6.7082039e-01 4.5825757e-01 5.4772256e-01 1.5362291e+00 4.6904158e-01 3.6055513e-01 3.0000000e-01 3.8729833e-01 1.5779734e+00 3.6055513e-01 2.1189620e+00 1.0344080e+00 2.1656408e+00 1.4899664e+00 1.8466185e+00 3.0016662e+00 1.1747340e+00 2.5436195e+00 1.8814888e+00 2.5806976e+00 1.2083046e+00 1.3076697e+00 1.6911535e+00 1.0862780e+00 1.2922848e+00 1.4628739e+00 1.4628739e+00 3.2588341e+00 3.3660065e+00 1.1357817e+00 1.9824228e+00 9.3273791e-01 3.1272992e+00 9.1104336e-01 1.8275667e+00 2.2494444e+00 7.6157731e-01 7.8740079e-01 1.6155494e+00 2.0639767e+00 2.4617067e+00 3.1192948e+00 1.6552945e+00 1.0049876e+00 1.4730920e+00 2.7367864e+00 1.7578396e+00 1.4282857e+00 6.7823300e-01 1.6763055e+00 1.8493242e+00 1.5684387e+00 1.0344080e+00 2.0928450e+00 1.9949937e+00 1.5099669e+00 1.1000000e+00 1.2688578e+00 1.5264338e+00 9.4868330e-01 1.0723805e+00 9.4868330e-01 1.2727922e+00 1.1401754e+00 5.4772256e-01 7.3484692e-01 5.1961524e-01 1.5132746e+00 6.7823300e-01 1.1135529e+00 9.4868330e-01 9.1104336e-01 1.1489125e+00 1.3416408e+00 1.6186414e+00 9.9498744e-01 7.0710678e-01 5.8309519e-01 6.1644140e-01 5.8309519e-01 1.3490738e+00 1.2247449e+00 1.4317821e+00 1.4282857e+00 5.9160798e-01 9.4868330e-01 6.5574385e-01 7.8102497e-01 1.0816654e+00 4.8989795e-01 1.2247449e+00 7.3484692e-01 9.0000000e-01 8.4261498e-01 8.4261498e-01 1.3820275e+00 7.4161985e-01 2.7477263e+00 1.5198684e+00 2.5826343e+00 1.9442222e+00 2.3600847e+00 3.3421550e+00 1.4282857e+00 2.8478062e+00 2.1118712e+00 3.1717503e+00 1.8601075e+00 1.7058722e+00 2.1771541e+00 1.4764823e+00 1.8894444e+00 2.1307276e+00 1.9442222e+00 3.7656341e+00 3.6262929e+00 1.1180340e+00 2.5278449e+00 1.5264338e+00 3.3970576e+00 1.3379088e+00 2.4083189e+00 2.6608269e+00 1.2961481e+00 1.4491377e+00 2.0712315e+00 2.3832751e+00 2.7459060e+00 3.5958309e+00 2.1260292e+00 1.3820275e+00 1.7000000e+00 3.1032241e+00 2.4596748e+00 1.9646883e+00 1.3856406e+00 2.1886069e+00 2.4124676e+00 2.1260292e+00 1.5198684e+00 2.6343880e+00 2.6153394e+00 2.0639767e+00 1.4106736e+00 1.8248288e+00 2.2649503e+00 1.5811388e+00 1.2124356e+00 7.0000000e-01 5.5677644e-01 8.0622577e-01 7.4161985e-01 1.0677078e+00 5.4772256e-01 7.1414284e-01 5.0000000e-01 2.2360680e-01 5.0990195e-01 5.9160798e-01 7.1414284e-01 7.4161985e-01 2.4494897e-01 1.3601471e+00 1.2288206e+00 1.3304135e+00 9.0000000e-01 5.0000000e-01 7.4161985e-01 5.8309519e-01 6.4031242e-01 7.0710678e-01 7.9372539e-01 1.0099505e+00 7.6157731e-01 1.4142136e-01 8.4261498e-01 1.9209373e+00 7.4161985e-01 6.7823300e-01 6.4807407e-01 4.2426407e-01 2.0346990e+00 7.3484692e-01 1.7606817e+00 7.3484692e-01 1.7146428e+00 1.0049876e+00 1.4212670e+00 2.5219040e+00 1.3152946e+00 2.0396078e+00 1.3747727e+00 2.2068076e+00 8.7749644e-01 8.6023253e-01 1.2767145e+00 8.7749644e-01 1.1224972e+00 1.1618950e+00 9.8488578e-01 2.8301943e+00 2.8809721e+00 7.7459667e-01 1.5937377e+00 8.1240384e-01 2.6324893e+00 5.2915026e-01 1.4177447e+00 1.7748239e+00 4.3588989e-01 4.5825757e-01 1.1832160e+00 1.5716234e+00 1.9773720e+00 2.7018512e+00 1.2449900e+00 4.6904158e-01 9.4868330e-01 2.3108440e+00 1.4491377e+00 9.6436508e-01 4.3588989e-01 1.2884099e+00 1.4866069e+00 1.2845233e+00 7.3484692e-01 1.6822604e+00 1.6522712e+00 1.1958261e+00 7.3484692e-01 8.8317609e-01 1.2489996e+00 6.0827625e-01 1.3784049e+00 9.2736185e-01 6.4807407e-01 1.3038405e+00 5.3851648e-01 1.3674794e+00 6.4807407e-01 1.5427249e+00 1.2165525e+00 1.0630146e+00 1.2884099e+00 1.7029386e+00 1.8275667e+00 1.0049876e+00 4.4721360e-01 5.8309519e-01 6.0000000e-01 4.2426407e-01 1.5937377e+00 9.4868330e-01 1.1445523e+00 1.5811388e+00 1.2206556e+00 5.0990195e-01 5.7445626e-01 8.6602540e-01 1.1269428e+00 5.4772256e-01 9.4868330e-01 6.3245553e-01 6.2449980e-01 6.0827625e-01 9.2195445e-01 9.0000000e-01 5.1961524e-01 2.8017851e+00 1.6401219e+00 2.8618176e+00 2.1771541e+00 2.5436195e+00 3.6945906e+00 1.2727922e+00 3.2295511e+00 2.5416530e+00 3.2771939e+00 1.9078784e+00 1.9824228e+00 2.3874673e+00 1.6186414e+00 1.8734994e+00 2.1494185e+00 2.1633308e+00 3.9547440e+00 4.0484565e+00 1.6278821e+00 2.6814175e+00 1.4798649e+00 3.8105118e+00 1.5716234e+00 2.5337719e+00 2.9427878e+00 1.4352700e+00 1.4832397e+00 2.3000000e+00 2.7386128e+00 3.1400637e+00 3.7986840e+00 2.3366643e+00 1.6703293e+00 2.0856654e+00 3.4161382e+00 2.4392622e+00 2.1307276e+00 1.3638182e+00 2.3685439e+00 2.5416530e+00 2.2315914e+00 1.6401219e+00 2.7964263e+00 2.6870058e+00 2.1863211e+00 1.7233688e+00 1.9672316e+00 2.2022716e+00 1.6124515e+00 1.1135529e+00 1.1045361e+00 1.0392305e+00 1.3820275e+00 9.8488578e-01 7.8740079e-01 8.8317609e-01 7.6157731e-01 3.8729833e-01 1.4142136e-01 5.0990195e-01 6.7823300e-01 7.4161985e-01 1.4899664e+00 1.5427249e+00 1.6062378e+00 1.1224972e+00 1.0862780e+00 1.3114877e+00 7.9372539e-01 3.1622777e-01 9.0000000e-01 1.1489125e+00 1.4035669e+00 1.3152946e+00 6.4031242e-01 1.1224972e+00 2.2135944e+00 1.1916375e+00 1.0440307e+00 1.0440307e+00 5.5677644e-01 2.2293497e+00 1.0908712e+00 1.9924859e+00 1.3076697e+00 1.7058722e+00 1.3416408e+00 1.6278821e+00 2.4799194e+00 1.9235384e+00 2.0420578e+00 1.5748016e+00 2.1447611e+00 9.4868330e-01 1.1445523e+00 1.3114877e+00 1.4422205e+00 1.5459625e+00 1.3114877e+00 1.1916375e+00 2.7239677e+00 2.8827071e+00 1.2922848e+00 1.5968719e+00 1.3820275e+00 2.5961510e+00 8.5440037e-01 1.4899664e+00 1.7262677e+00 8.1240384e-01 8.8317609e-01 1.4525839e+00 1.5033296e+00 1.9287302e+00 2.5079872e+00 1.5033296e+00 8.6602540e-01 1.4317821e+00 2.1702534e+00 1.6401219e+00 1.2083046e+00 9.0553851e-01 1.2369317e+00 1.5620499e+00 1.1575837e+00 1.3076697e+00 1.7549929e+00 1.7146428e+00 1.2083046e+00 1.0630146e+00 1.0246951e+00 1.4662878e+00 1.1401754e+00 7.3484692e-01 1.0000000e+00 8.7749644e-01 5.5677644e-01 7.6157731e-01 9.4868330e-01 6.4807407e-01 8.5440037e-01 1.0099505e+00 1.2569805e+00 1.2247449e+00 4.1231056e-01 1.1916375e+00 1.0099505e+00 1.1224972e+00 7.6157731e-01 7.8740079e-01 2.0000000e-01 5.7445626e-01 1.1224972e+00 1.0148892e+00 4.4721360e-01 7.4161985e-01 5.1961524e-01 5.1961524e-01 7.3484692e-01 1.5937377e+00 4.6904158e-01 4.3588989e-01 3.8729833e-01 6.7082039e-01 1.7058722e+00 5.0000000e-01 1.9570386e+00 8.0622577e-01 2.1377558e+00 1.3416408e+00 1.7291616e+00 2.9614186e+00 8.8317609e-01 2.4959968e+00 1.8000000e+00 2.5455844e+00 1.2083046e+00 1.2369317e+00 1.6733201e+00 8.7177979e-01 1.1180340e+00 1.4000000e+00 1.3784049e+00 3.2218007e+00 3.3120990e+00 1.0246951e+00 1.9519221e+00 6.7082039e-01 3.0886890e+00 9.1104336e-01 1.7606817e+00 2.2226111e+00 7.6157731e-01 7.0710678e-01 1.5000000e+00 2.0639767e+00 2.4494897e+00 3.1288976e+00 1.5427249e+00 9.4339811e-01 1.2767145e+00 2.7586228e+00 1.6340135e+00 1.3190906e+00 5.8309519e-01 1.6941074e+00 1.8000000e+00 1.6431677e+00 8.0622577e-01 2.0199010e+00 1.9339080e+00 1.5297059e+00 1.0723805e+00 1.2449900e+00 1.4035669e+00 7.3484692e-01 9.0553851e-01 3.6055513e-01 1.1789826e+00 4.4721360e-01 1.0862780e+00 7.0710678e-01 7.2801099e-01 9.8994949e-01 1.2884099e+00 1.4832397e+00 7.0000000e-01 6.1644140e-01 5.2915026e-01 5.8309519e-01 2.8284271e-01 1.1832160e+00 8.1240384e-01 1.0246951e+00 1.2569805e+00 7.6811457e-01 4.6904158e-01 4.7958315e-01 4.7958315e-01 7.6811457e-01 2.4494897e-01 1.2000000e+00 3.7416574e-01 3.8729833e-01 3.8729833e-01 5.7445626e-01 1.3228757e+00 3.3166248e-01 2.5436195e+00 1.3453624e+00 2.4959968e+00 1.7832555e+00 2.2158520e+00 3.2848135e+00 1.2247449e+00 2.7874720e+00 2.0928450e+00 3.0033315e+00 1.6552945e+00 1.6155494e+00 2.0639767e+00 1.3638182e+00 1.7233688e+00 1.9339080e+00 1.7832555e+00 3.6083237e+00 3.6262929e+00 1.1618950e+00 2.3895606e+00 1.3000000e+00 3.3734256e+00 1.2369317e+00 2.2226111e+00 2.5416530e+00 1.1401754e+00 1.2083046e+00 1.9570386e+00 2.3021729e+00 2.7166155e+00 3.4510868e+00 2.0149442e+00 1.2288206e+00 1.5842980e+00 3.0643107e+00 2.2248595e+00 1.7663522e+00 1.1224972e+00 2.0663978e+00 2.2759613e+00 2.0149442e+00 1.3453624e+00 2.4859606e+00 2.4454039e+00 1.9493589e+00 1.3820275e+00 1.6703293e+00 2.0074860e+00 1.3190906e+00 9.8488578e-01 1.1269428e+00 8.1240384e-01 5.0990195e-01 7.0710678e-01 7.8102497e-01 9.0553851e-01 9.0553851e-01 1.0862780e+00 7.2801099e-01 1.2884099e+00 1.0862780e+00 1.1916375e+00 9.2736185e-01 8.1240384e-01 1.1313708e+00 1.2206556e+00 1.0488088e+00 2.6457513e-01 1.0954451e+00 9.3273791e-01 8.6602540e-01 8.1853528e-01 8.1240384e-01 1.7720045e+00 8.6023253e-01 1.0344080e+00 9.3273791e-01 7.5498344e-01 1.9261360e+00 9.0000000e-01 2.1142375e+00 9.6436508e-01 1.9416488e+00 1.3416408e+00 1.7058722e+00 2.7147744e+00 1.3490738e+00 2.2427661e+00 1.4560220e+00 2.5534291e+00 1.3038405e+00 1.0440307e+00 1.5362291e+00 9.1651514e-01 1.3000000e+00 1.5231546e+00 1.3490738e+00 3.1843367e+00 2.9681644e+00 5.3851648e-01 1.8894444e+00 1.0630146e+00 2.7748874e+00 7.1414284e-01 1.8055470e+00 2.0832667e+00 7.3484692e-01 9.4868330e-01 1.4035669e+00 1.8275667e+00 2.1260292e+00 3.0512293e+00 1.4491377e+00 8.5440037e-01 1.1789826e+00 2.4677925e+00 1.8627936e+00 1.3928388e+00 9.2736185e-01 1.5716234e+00 1.7549929e+00 1.5165751e+00 9.6436508e-01 1.9899749e+00 1.9748418e+00 1.4212670e+00 7.1414284e-01 1.2124356e+00 1.7000000e+00 1.0862780e+00 1.3711309e+00 6.2449980e-01 1.2845233e+00 9.9498744e-01 1.0000000e+00 1.2609520e+00 1.5588457e+00 1.7406895e+00 9.1651514e-01 4.3588989e-01 1.7320508e-01 2.6457513e-01 3.0000000e-01 1.3747727e+00 9.0000000e-01 1.2569805e+00 1.5394804e+00 9.0553851e-01 5.7445626e-01 2.4494897e-01 5.2915026e-01 1.0392305e+00 2.6457513e-01 8.7749644e-01 4.1231056e-01 6.0000000e-01 5.4772256e-01 8.4852814e-01 1.0295630e+00 4.2426407e-01 2.7386128e+00 1.4696938e+00 2.7386128e+00 2.0074860e+00 2.4248711e+00 3.5411862e+00 1.1000000e+00 3.0495901e+00 2.3043437e+00 3.2511536e+00 1.8841444e+00 1.8110770e+00 2.2912878e+00 1.4247807e+00 1.8055470e+00 2.1283797e+00 2.0273135e+00 3.8923001e+00 3.8548671e+00 1.2727922e+00 2.6191602e+00 1.3784049e+00 3.6262929e+00 1.4212670e+00 2.4677925e+00 2.8195744e+00 1.3228757e+00 1.4106736e+00 2.1494185e+00 2.5826343e+00 2.9681644e+00 3.7469988e+00 2.1977261e+00 1.4764823e+00 1.8000000e+00 3.3075671e+00 2.4248711e+00 2.0124612e+00 1.3076697e+00 2.3021729e+00 2.4799194e+00 2.2203603e+00 1.4696938e+00 2.7147744e+00 2.6551836e+00 2.1424285e+00 1.5297059e+00 1.8867962e+00 2.2045408e+00 1.5066519e+00 1.0440307e+00 8.6602540e-01 7.5498344e-01 9.1651514e-01 9.2195445e-01 1.0630146e+00 8.5440037e-01 5.2915026e-01 1.6522712e+00 1.5132746e+00 1.6278821e+00 1.1958261e+00 6.2449980e-01 6.8556546e-01 4.2426407e-01 8.6602540e-01 1.1747340e+00 9.3273791e-01 1.2409674e+00 1.0198039e+00 5.2915026e-01 1.1704700e+00 2.1236761e+00 9.7467943e-01 8.9442719e-01 8.6023253e-01 8.2462113e-01 2.2045408e+00 9.6953597e-01 1.4491377e+00 6.0000000e-01 1.6673332e+00 9.4339811e-01 1.2489996e+00 2.5019992e+00 1.2609520e+00 2.0736441e+00 1.4594520e+00 2.0074860e+00 7.0000000e-01 8.7177979e-01 1.1958261e+00 7.8102497e-01 7.8740079e-01 8.6602540e-01 9.4339811e-01 2.7147744e+00 2.8740216e+00 1.0677078e+00 1.4352700e+00 5.4772256e-01 2.6551836e+00 6.4807407e-01 1.2449900e+00 1.7691806e+00 5.0000000e-01 3.0000000e-01 1.0677078e+00 1.6643317e+00 2.0273135e+00 2.6381812e+00 1.1000000e+00 7.0710678e-01 1.0954451e+00 2.2847319e+00 1.0954451e+00 8.6602540e-01 2.2360680e-01 1.2083046e+00 1.2845233e+00 1.1618950e+00 6.0000000e-01 1.5066519e+00 1.3964240e+00 1.0440307e+00 8.3666003e-01 7.7459667e-01 8.6023253e-01 3.6055513e-01 9.8994949e-01 7.0710678e-01 4.3588989e-01 6.7823300e-01 1.0677078e+00 1.2489996e+00 5.5677644e-01 7.3484692e-01 7.7459667e-01 8.3666003e-01 3.4641016e-01 1.1489125e+00 9.0553851e-01 8.4261498e-01 9.8994949e-01 6.7082039e-01 5.4772256e-01 6.7082039e-01 7.5498344e-01 6.4031242e-01 3.7416574e-01 1.4282857e+00 5.4772256e-01 5.0000000e-01 4.5825757e-01 3.3166248e-01 1.4594520e+00 4.1231056e-01 2.3937418e+00 1.2922848e+00 2.3000000e+00 1.6911535e+00 2.0615528e+00 3.1128765e+00 1.3928388e+00 2.6438608e+00 1.9849433e+00 2.7748874e+00 1.4212670e+00 1.4662878e+00 1.8493242e+00 1.3190906e+00 1.5842980e+00 1.7146428e+00 1.6431677e+00 3.4146742e+00 3.4655447e+00 1.1874342e+00 2.1656408e+00 1.2449900e+00 3.2155870e+00 1.0535654e+00 2.0346990e+00 2.3706539e+00 9.4868330e-01 1.0488088e+00 1.8138357e+00 2.1400935e+00 2.5416530e+00 3.2388269e+00 1.8601075e+00 1.1357817e+00 1.6155494e+00 2.8301943e+00 2.0420578e+00 1.6370706e+00 9.6953597e-01 1.8248288e+00 2.0542639e+00 1.7146428e+00 1.2922848e+00 2.2934690e+00 2.2226111e+00 1.6852300e+00 1.2206556e+00 1.4594520e+00 1.8248288e+00 1.2409674e+00 5.0990195e-01 7.5498344e-01 7.7459667e-01 6.0000000e-01 6.7823300e-01 6.4031242e-01 1.6062378e+00 1.4212670e+00 1.5297059e+00 1.1747340e+00 4.2426407e-01 1.1045361e+00 1.0344080e+00 7.4833148e-01 5.7445626e-01 1.1916375e+00 1.2206556e+00 9.9498744e-01 6.2449980e-01 1.0770330e+00 2.1307276e+00 1.0295630e+00 1.0908712e+00 1.0246951e+00 7.5498344e-01 2.2825424e+00 1.0630146e+00 1.6881943e+00 7.0000000e-01 1.5000000e+00 8.6023253e-01 1.2609520e+00 2.2781571e+00 1.4696938e+00 1.7916473e+00 1.0295630e+00 2.1118712e+00 9.0553851e-01 6.0827625e-01 1.1045361e+00 7.8740079e-01 1.0908712e+00 1.1401754e+00 8.6023253e-01 2.7166155e+00 2.5709920e+00 4.3588989e-01 1.4594520e+00 9.1104336e-01 2.3537205e+00 3.6055513e-01 1.3416408e+00 1.6124515e+00 4.4721360e-01 6.1644140e-01 9.7467943e-01 1.3711309e+00 1.7029386e+00 2.5980762e+00 1.0392305e+00 3.6055513e-01 7.4161985e-01 2.0712315e+00 1.4525839e+00 9.0553851e-01 6.6332496e-01 1.1532563e+00 1.3490738e+00 1.1832160e+00 7.0000000e-01 1.5427249e+00 1.5620499e+00 1.0677078e+00 4.1231056e-01 7.9372539e-01 1.3076697e+00 7.3484692e-01 5.1961524e-01 6.4807407e-01 7.3484692e-01 8.6023253e-01 3.8729833e-01 1.2961481e+00 1.1575837e+00 1.2489996e+00 8.6023253e-01 5.8309519e-01 8.1240384e-01 7.5498344e-01 7.3484692e-01 6.2449980e-01 8.1240384e-01 9.7467943e-01 7.0000000e-01 3.0000000e-01 7.8740079e-01 1.8601075e+00 7.2111026e-01 6.7082039e-01 6.5574385e-01 4.3588989e-01 1.9974984e+00 7.2801099e-01 1.9157244e+00 8.6602540e-01 1.8138357e+00 1.1045361e+00 1.5524175e+00 2.5903668e+00 1.3490738e+00 2.0904545e+00 1.4212670e+00 2.3452079e+00 1.0583005e+00 9.7467943e-01 1.4071247e+00 9.8994949e-01 1.3000000e+00 1.3490738e+00 1.0954451e+00 2.9257478e+00 2.9410882e+00 7.4161985e-01 1.7349352e+00 9.6436508e-01 2.6832816e+00 6.7082039e-01 1.5556349e+00 1.8493242e+00 6.1644140e-01 6.6332496e-01 1.3076697e+00 1.6186414e+00 2.0346990e+00 2.7874720e+00 1.3784049e+00 5.3851648e-01 9.4339811e-01 2.4020824e+00 1.6278821e+00 1.0862780e+00 6.4807407e-01 1.4247807e+00 1.6431677e+00 1.4491377e+00 8.6602540e-01 1.8165902e+00 1.8165902e+00 1.3638182e+00 8.4261498e-01 1.0440307e+00 1.4387495e+00 7.7459667e-01 2.6457513e-01 6.5574385e-01 8.6602540e-01 4.8989795e-01 1.1445523e+00 1.1618950e+00 1.2288206e+00 7.5498344e-01 9.6436508e-01 1.0440307e+00 7.3484692e-01 5.7445626e-01 6.1644140e-01 8.3066239e-01 1.0295630e+00 9.5916630e-01 4.4721360e-01 7.4161985e-01 1.8466185e+00 8.3066239e-01 7.2111026e-01 7.0710678e-01 2.0000000e-01 1.8920888e+00 7.3484692e-01 2.1213203e+00 1.1832160e+00 1.9235384e+00 1.3964240e+00 1.7549929e+00 2.7166155e+00 1.6155494e+00 2.2494444e+00 1.6583124e+00 2.4103942e+00 1.1090537e+00 1.1832160e+00 1.5000000e+00 1.2767145e+00 1.4899664e+00 1.4456832e+00 1.3076697e+00 3.0116441e+00 3.0886890e+00 1.0862780e+00 1.8165902e+00 1.2247449e+00 2.8195744e+00 8.1240384e-01 1.6881943e+00 1.9672316e+00 7.4161985e-01 8.4261498e-01 1.5297059e+00 1.7291616e+00 2.1470911e+00 2.8213472e+00 1.5842980e+00 8.3666003e-01 1.3711309e+00 2.4372115e+00 1.7776389e+00 1.3152946e+00 8.1853528e-01 1.4628739e+00 1.7406895e+00 1.3892444e+00 1.1832160e+00 1.9519221e+00 1.9104973e+00 1.3820275e+00 1.0099505e+00 1.1489125e+00 1.5811388e+00 1.0723805e+00 4.8989795e-01 6.7823300e-01 6.2449980e-01 1.3928388e+00 1.4212670e+00 1.4899664e+00 1.0099505e+00 9.8994949e-01 1.2083046e+00 7.5498344e-01 3.4641016e-01 7.6811457e-01 1.0488088e+00 1.2767145e+00 1.1874342e+00 5.3851648e-01 1.0000000e+00 2.1023796e+00 1.0677078e+00 9.4339811e-01 9.3273791e-01 4.3588989e-01 2.1330729e+00 9.7467943e-01 1.9874607e+00 1.2124356e+00 1.7291616e+00 1.3038405e+00 1.6155494e+00 2.5159491e+00 1.8000000e+00 2.0663978e+00 1.5427249e+00 2.1954498e+00 9.4868330e-01 1.0908712e+00 1.3190906e+00 1.3341664e+00 1.4730920e+00 1.3038405e+00 1.1747340e+00 2.7892651e+00 2.9034462e+00 1.1704700e+00 1.6217275e+00 1.2845233e+00 2.6267851e+00 7.6811457e-01 1.5099669e+00 1.7663522e+00 7.2111026e-01 8.1240384e-01 1.4177447e+00 1.5362291e+00 1.9544820e+00 2.5865034e+00 1.4696938e+00 7.9372539e-01 1.3601471e+00 2.2158520e+00 1.6401219e+00 1.1916375e+00 8.2462113e-01 1.2609520e+00 1.5684387e+00 1.1832160e+00 1.2124356e+00 1.7720045e+00 1.7320508e+00 1.2083046e+00 9.7467943e-01 1.0049876e+00 1.4594520e+00 1.0677078e+00 4.2426407e-01 8.6602540e-01 1.7606817e+00 1.7146428e+00 1.7944358e+00 1.3638182e+00 8.8317609e-01 1.4491377e+00 1.0630146e+00 3.4641016e-01 8.1853528e-01 1.4071247e+00 1.5588457e+00 1.3892444e+00 7.5498344e-01 1.3114877e+00 2.4289916e+00 1.3490738e+00 1.2845233e+00 1.2609520e+00 7.9372539e-01 2.5119713e+00 1.3076697e+00 1.7748239e+00 1.1618950e+00 1.3527749e+00 1.0295630e+00 1.3304135e+00 2.1000000e+00 1.9697716e+00 1.6340135e+00 1.1224972e+00 1.9235384e+00 8.3666003e-01 8.1853528e-01 1.0099505e+00 1.3038405e+00 1.4456832e+00 1.1747340e+00 8.8317609e-01 2.4617067e+00 2.4637370e+00 1.0246951e+00 1.3379088e+00 1.3453624e+00 2.1863211e+00 6.5574385e-01 1.2489996e+00 1.3856406e+00 7.2111026e-01 8.3666003e-01 1.1357817e+00 1.1135529e+00 1.5165751e+00 2.2649503e+00 1.2000000e+00 5.9160798e-01 1.0816654e+00 1.8303005e+00 1.5000000e+00 9.4868330e-01 9.1651514e-01 9.7467943e-01 1.3190906e+00 1.0000000e+00 1.1618950e+00 1.4764823e+00 1.5099669e+00 1.0099505e+00 7.9372539e-01 8.0622577e-01 1.3747727e+00 1.0488088e+00 8.8881944e-01 1.9748418e+00 1.8973666e+00 1.9949937e+00 1.5362291e+00 7.7459667e-01 1.4071247e+00 9.5393920e-01 3.7416574e-01 1.0816654e+00 1.4764823e+00 1.6881943e+00 1.4866069e+00 7.8102497e-01 1.4899664e+00 2.6000000e+00 1.4491377e+00 1.3747727e+00 1.3453624e+00 9.5393920e-01 2.6776856e+00 1.4177447e+00 1.3747727e+00 9.7467943e-01 1.0630146e+00 7.3484692e-01 9.6436508e-01 1.8788294e+00 1.9339080e+00 1.4387495e+00 9.4868330e-01 1.5684387e+00 4.2426407e-01 5.5677644e-01 6.4807407e-01 1.1575837e+00 1.1618950e+00 7.6157731e-01 5.4772256e-01 2.1863211e+00 2.2649503e+00 1.0816654e+00 9.6436508e-01 1.1618950e+00 2.0049938e+00 5.1961524e-01 8.6023253e-01 1.1401754e+00 5.8309519e-01 6.1644140e-01 8.0622577e-01 9.4868330e-01 1.3341664e+00 2.0322401e+00 8.6023253e-01 5.0000000e-01 9.8488578e-01 1.6031220e+00 1.0816654e+00 6.0000000e-01 7.3484692e-01 6.0827625e-01 9.2736185e-01 6.4807407e-01 9.7467943e-01 1.1045361e+00 1.1045361e+00 6.3245553e-01 6.7082039e-01 4.1231056e-01 9.6436508e-01 8.1240384e-01 1.1958261e+00 1.0723805e+00 1.1789826e+00 7.2801099e-01 6.4031242e-01 6.0827625e-01 5.0990195e-01 7.5498344e-01 7.0710678e-01 6.0827625e-01 8.3666003e-01 6.6332496e-01 2.0000000e-01 6.8556546e-01 1.7464249e+00 5.7445626e-01 5.2915026e-01 4.6904158e-01 3.4641016e-01 1.8384776e+00 5.4772256e-01 1.8708287e+00 7.7459667e-01 1.8814888e+00 1.1789826e+00 1.5620499e+00 2.7092434e+00 1.1874342e+00 2.2405357e+00 1.5588457e+00 2.3430749e+00 9.7467943e-01 1.0000000e+00 1.4177447e+00 8.6602540e-01 1.1045361e+00 1.2369317e+00 1.1618950e+00 3.0049958e+00 3.0626786e+00 8.6023253e-01 1.7262677e+00 7.6157731e-01 2.8266588e+00 6.1644140e-01 1.5652476e+00 1.9672316e+00 4.7958315e-01 5.1961524e-01 1.3190906e+00 1.7748239e+00 2.1656408e+00 2.8774989e+00 1.3674794e+00 6.7823300e-01 1.1489125e+00 2.4698178e+00 1.5362291e+00 1.1357817e+00 4.3588989e-01 1.4212670e+00 1.5968719e+00 1.3601471e+00 7.7459667e-01 1.8248288e+00 1.7578396e+00 1.2767145e+00 8.1240384e-01 1.0000000e+00 1.3190906e+00 6.8556546e-01 4.2426407e-01 3.4641016e-01 4.6904158e-01 1.7378147e+00 1.2247449e+00 1.4456832e+00 1.7146428e+00 1.1618950e+00 7.8740079e-01 6.2449980e-01 9.4339811e-01 1.3000000e+00 5.4772256e-01 7.8740079e-01 7.7459667e-01 8.3066239e-01 8.1853528e-01 1.0344080e+00 7.9372539e-01 7.0000000e-01 3.0577770e+00 1.8411953e+00 3.0149627e+00 2.3452079e+00 2.7440845e+00 3.8196859e+00 1.4628739e+00 3.3361655e+00 2.6343880e+00 3.5014283e+00 2.1354157e+00 2.1330729e+00 2.5651511e+00 1.8055470e+00 2.1377558e+00 2.4041631e+00 2.3323808e+00 4.1376322e+00 4.1533119e+00 1.6583124e+00 2.8861739e+00 1.7349352e+00 3.9089641e+00 1.7233688e+00 2.7459060e+00 3.0822070e+00 1.6186414e+00 1.7088007e+00 2.4799194e+00 2.8390139e+00 3.2403703e+00 3.9610605e+00 2.5258662e+00 1.7916473e+00 2.1748563e+00 3.5510562e+00 2.7147744e+00 2.3194827e+00 1.6062378e+00 2.5514702e+00 2.7604347e+00 2.4372115e+00 1.8411953e+00 3.0033315e+00 2.9291637e+00 2.3958297e+00 1.8520259e+00 2.1656408e+00 2.4879711e+00 1.8439089e+00 1.4142136e-01 4.4721360e-01 1.5099669e+00 1.0099505e+00 1.4106736e+00 1.7029386e+00 1.0246951e+00 7.0710678e-01 3.0000000e-01 6.4031242e-01 1.2041595e+00 4.2426407e-01 7.2111026e-01 5.4772256e-01 7.5498344e-01 7.0000000e-01 1.0148892e+00 9.0000000e-01 5.7445626e-01 2.8722813e+00 1.5842980e+00 2.8861739e+00 2.1494185e+00 2.5632011e+00 3.6891733e+00 1.1045361e+00 3.1984371e+00 2.4372115e+00 3.4029399e+00 2.0346990e+00 1.9467922e+00 2.4372115e+00 1.5165751e+00 1.9052559e+00 2.2671568e+00 2.1771541e+00 4.0521599e+00 3.9912404e+00 1.3747727e+00 2.7658633e+00 1.4798649e+00 3.7709415e+00 1.5588457e+00 2.6191602e+00 2.9765752e+00 1.4628739e+00 1.5556349e+00 2.2825424e+00 2.7386128e+00 3.1144823e+00 3.9102430e+00 2.3280893e+00 1.6278821e+00 1.9313208e+00 3.4539832e+00 2.5632011e+00 2.1633308e+00 1.4491377e+00 2.4515301e+00 2.6191602e+00 2.3622024e+00 1.5842980e+00 2.8600699e+00 2.7964263e+00 2.2803509e+00 1.6522712e+00 2.0322401e+00 2.3430749e+00 1.6431677e+00 5.0990195e-01 1.6309506e+00 1.1224972e+00 1.5000000e+00 1.7832555e+00 1.1090537e+00 7.8740079e-01 4.3588989e-01 7.5498344e-01 1.3000000e+00 5.0990195e-01 6.4807407e-01 6.6332496e-01 8.3066239e-01 7.9372539e-01 1.0908712e+00 8.1853528e-01 6.7082039e-01 2.9983329e+00 1.7175564e+00 2.9949958e+00 2.2671568e+00 2.6851443e+00 3.7934153e+00 1.2247449e+00 3.3000000e+00 2.5495098e+00 3.5128336e+00 2.1447611e+00 2.0663978e+00 2.5495098e+00 1.6552945e+00 2.0420578e+00 2.3874673e+00 2.2891046e+00 4.1521079e+00 4.1000000e+00 1.4933185e+00 2.8792360e+00 1.6155494e+00 3.8729833e+00 1.6763055e+00 2.7313001e+00 3.0757113e+00 1.5811388e+00 1.6733201e+00 2.4062419e+00 2.8319605e+00 3.2155870e+00 4.0012498e+00 2.4535688e+00 1.7349352e+00 2.0420578e+00 3.5566838e+00 2.6851443e+00 2.2759613e+00 1.5684387e+00 2.5592968e+00 2.7386128e+00 2.4698178e+00 1.7175564e+00 2.9765752e+00 2.9154759e+00 2.3958297e+00 1.7748239e+00 2.1470911e+00 2.4637370e+00 1.7663522e+00 1.2806248e+00 8.3666003e-01 1.0246951e+00 1.3038405e+00 8.1853528e-01 4.2426407e-01 3.8729833e-01 5.9160798e-01 8.4261498e-01 1.4142136e-01 1.0954451e+00 3.7416574e-01 4.3588989e-01 3.8729833e-01 6.0827625e-01 1.1618950e+00 2.6457513e-01 2.5903668e+00 1.3892444e+00 2.5670995e+00 1.8814888e+00 2.2781571e+00 3.3808283e+00 1.2083046e+00 2.9000000e+00 2.1954498e+00 3.0495901e+00 1.6792856e+00 1.6763055e+00 2.1118712e+00 1.3784049e+00 1.7000000e+00 1.9442222e+00 1.8708287e+00 3.6959437e+00 3.7188708e+00 1.2609520e+00 2.4310492e+00 1.3000000e+00 3.4785054e+00 1.2688578e+00 2.2847319e+00 2.6419690e+00 1.1575837e+00 1.2409674e+00 2.0174241e+00 2.4124676e+00 2.8106939e+00 3.5369478e+00 2.0639767e+00 1.3379088e+00 1.7406895e+00 3.1224990e+00 2.2516660e+00 1.8547237e+00 1.1401754e+00 2.1047565e+00 2.3021729e+00 2.0049938e+00 1.3892444e+00 2.5416530e+00 2.4698178e+00 1.9493589e+00 1.4106736e+00 1.7058722e+00 2.0273135e+00 1.3784049e+00 9.0553851e-01 9.2195445e-01 9.0553851e-01 9.1104336e-01 1.1575837e+00 1.2609520e+00 9.5393920e-01 6.2449980e-01 1.1916375e+00 2.1817424e+00 1.0295630e+00 1.0723805e+00 1.0148892e+00 9.0000000e-01 2.3473389e+00 1.0908712e+00 1.4387495e+00 3.6055513e-01 1.4798649e+00 6.4807407e-01 1.0908712e+00 2.2693611e+00 1.2727922e+00 1.7916473e+00 1.0295630e+00 2.0149442e+00 8.1240384e-01 5.3851648e-01 1.0677078e+00 5.4772256e-01 8.3066239e-01 9.6953597e-01 7.3484692e-01 2.6495283e+00 2.5748786e+00 5.1961524e-01 1.3820275e+00 6.0827625e-01 2.3706539e+00 4.1231056e-01 1.2083046e+00 1.5937377e+00 4.2426407e-01 4.2426407e-01 8.1853528e-01 1.4212670e+00 1.7492856e+00 2.5826343e+00 8.8317609e-01 3.3166248e-01 5.5677644e-01 2.1142375e+00 1.2124356e+00 7.2111026e-01 4.6904158e-01 1.1445523e+00 1.2409674e+00 1.2083046e+00 3.6055513e-01 1.4212670e+00 1.4212670e+00 1.0392305e+00 4.7958315e-01 7.1414284e-01 1.0535654e+00 3.7416574e-01 7.2801099e-01 1.3190906e+00 1.1618950e+00 4.8989795e-01 7.4161985e-01 5.1961524e-01 7.1414284e-01 8.1240384e-01 1.5297059e+00 5.0990195e-01 5.1961524e-01 4.7958315e-01 8.5440037e-01 1.6583124e+00 5.7445626e-01 2.0371549e+00 8.7749644e-01 2.2825424e+00 1.4560220e+00 1.8411953e+00 3.1000000e+00 7.3484692e-01 2.6362853e+00 1.9287302e+00 2.6758176e+00 1.3638182e+00 1.3747727e+00 1.8220867e+00 9.1651514e-01 1.1704700e+00 1.5231546e+00 1.5165751e+00 3.3555923e+00 3.4423829e+00 1.1180340e+00 2.0904545e+00 7.0000000e-01 3.2280025e+00 1.0723805e+00 1.8920888e+00 2.3706539e+00 9.2736185e-01 8.6023253e-01 1.6155494e+00 2.2226111e+00 2.6000000e+00 3.2787193e+00 1.6552945e+00 1.1000000e+00 1.3674794e+00 2.9137605e+00 1.7291616e+00 1.4491377e+00 7.3484692e-01 1.8520259e+00 1.9287302e+00 1.8055470e+00 8.7749644e-01 2.1447611e+00 2.0542639e+00 1.6792856e+00 1.2124356e+00 1.3964240e+00 1.5000000e+00 8.3666003e-01 7.9372539e-01 1.1832160e+00 7.5498344e-01 1.1832160e+00 1.0295630e+00 4.6904158e-01 1.0440307e+00 2.0024984e+00 9.1104336e-01 7.0710678e-01 7.2111026e-01 6.4807407e-01 2.0297783e+00 8.3666003e-01 1.7776389e+00 9.8994949e-01 1.8920888e+00 1.2609520e+00 1.5684387e+00 2.7166155e+00 1.4247807e+00 2.2847319e+00 1.7406895e+00 2.2022716e+00 9.0000000e-01 1.1747340e+00 1.4317821e+00 1.1445523e+00 1.1832160e+00 1.1532563e+00 1.2041595e+00 2.8722813e+00 3.1272992e+00 1.3038405e+00 1.6673332e+00 9.1651514e-01 2.8722813e+00 8.8317609e-01 1.4798649e+00 1.9416488e+00 7.2801099e-01 6.0827625e-01 1.4071247e+00 1.8138357e+00 2.2293497e+00 2.7459060e+00 1.4456832e+00 9.0553851e-01 1.3784049e+00 2.4698178e+00 1.3928388e+00 1.1357817e+00 5.3851648e-01 1.4000000e+00 1.5588457e+00 1.3228757e+00 9.8994949e-01 1.7691806e+00 1.6583124e+00 1.2767145e+00 1.1135529e+00 1.0295630e+00 1.1575837e+00 7.5498344e-01 9.6436508e-01 1.2727922e+00 1.5264338e+00 1.3674794e+00 6.2449980e-01 1.2806248e+00 2.3958297e+00 1.2884099e+00 1.1618950e+00 1.1532563e+00 7.0000000e-01 2.4433583e+00 1.2206556e+00 1.7000000e+00 1.1357817e+00 1.4035669e+00 1.0488088e+00 1.3228757e+00 2.1886069e+00 1.9183326e+00 1.7464249e+00 1.2884099e+00 1.8601075e+00 6.7823300e-01 8.7749644e-01 1.0099505e+00 1.3038405e+00 1.3674794e+00 1.0488088e+00 8.8317609e-01 2.4454039e+00 2.5942244e+00 1.1789826e+00 1.3000000e+00 1.2609520e+00 2.3108440e+00 6.7082039e-01 1.1832160e+00 1.4282857e+00 6.6332496e-01 7.0710678e-01 1.1618950e+00 1.2165525e+00 1.6431677e+00 2.2516660e+00 1.2165525e+00 6.4031242e-01 1.1958261e+00 1.9000000e+00 1.3674794e+00 9.0553851e-01 7.7459667e-01 9.4339811e-01 1.2727922e+00 9.1651514e-01 1.1357817e+00 1.4491377e+00 1.4282857e+00 9.4868330e-01 8.7749644e-01 7.4161985e-01 1.2124356e+00 9.4868330e-01 1.0344080e+00 9.1651514e-01 8.6023253e-01 7.6157731e-01 7.1414284e-01 1.7291616e+00 8.3066239e-01 9.4868330e-01 8.7177979e-01 6.1644140e-01 1.8654758e+00 8.3666003e-01 2.2360680e+00 1.1224972e+00 2.0049938e+00 1.4317821e+00 1.8165902e+00 2.7676705e+00 1.4730920e+00 2.2847319e+00 1.5524175e+00 2.6134269e+00 1.3527749e+00 1.1575837e+00 1.6093477e+00 1.1180340e+00 1.4832397e+00 1.6217275e+00 1.4106736e+00 3.2109189e+00 3.0495901e+00 7.0710678e-01 1.9646883e+00 1.2165525e+00 2.8266588e+00 8.1240384e-01 1.8681542e+00 2.1047565e+00 8.1853528e-01 1.0148892e+00 1.5297059e+00 1.8303005e+00 2.1702534e+00 3.0495901e+00 1.5842980e+00 8.8317609e-01 1.2569805e+00 2.5179357e+00 1.9646883e+00 1.4525839e+00 9.9498744e-01 1.6248077e+00 1.8574176e+00 1.5779734e+00 1.1224972e+00 2.0760539e+00 2.0712315e+00 1.5132746e+00 8.7177979e-01 1.2884099e+00 1.7944358e+00 1.1789826e+00 5.1961524e-01 5.1961524e-01 7.1414284e-01 4.6904158e-01 1.2569805e+00 3.1622777e-01 1.7320508e-01 1.7320508e-01 6.4031242e-01 1.3228757e+00 2.2360680e-01 2.3727621e+00 1.2206556e+00 2.4758837e+00 1.7320508e+00 2.1236761e+00 3.3000000e+00 1.0295630e+00 2.8266588e+00 2.1447611e+00 2.8913665e+00 1.5297059e+00 1.5905974e+00 2.0099751e+00 1.2489996e+00 1.5132746e+00 1.7663522e+00 1.7378147e+00 3.5524639e+00 3.6619667e+00 1.2845233e+00 2.3000000e+00 1.0816654e+00 3.4205263e+00 1.2124356e+00 2.1213203e+00 2.5416530e+00 1.0677078e+00 1.0677078e+00 1.8894444e+00 2.3537205e+00 2.7640550e+00 3.4219877e+00 1.9339080e+00 1.2529964e+00 1.6340135e+00 3.0675723e+00 2.0273135e+00 1.6911535e+00 9.4868330e-01 2.0074860e+00 2.1633308e+00 1.9235384e+00 1.2206556e+00 2.3916521e+00 2.3021729e+00 1.8493242e+00 1.3820275e+00 1.5842980e+00 1.7916473e+00 1.1575837e+00 4.2426407e-01 9.8994949e-01 3.3166248e-01 9.3273791e-01 3.0000000e-01 5.8309519e-01 4.8989795e-01 8.6023253e-01 1.0954451e+00 3.7416574e-01 2.5922963e+00 1.3038405e+00 2.6570661e+00 1.9000000e+00 2.3021729e+00 3.4727511e+00 8.7749644e-01 2.9899833e+00 2.2203603e+00 3.1543621e+00 1.7860571e+00 1.7029386e+00 2.1977261e+00 1.2369317e+00 1.6124515e+00 1.9974984e+00 1.9364917e+00 3.8249183e+00 3.7762415e+00 1.1747340e+00 2.5179357e+00 1.1832160e+00 3.5651087e+00 1.3190906e+00 2.3685439e+00 2.7622455e+00 1.2124356e+00 1.2922848e+00 2.0248457e+00 2.5436195e+00 2.9103264e+00 3.7013511e+00 2.0663978e+00 1.4071247e+00 1.7146428e+00 3.2403703e+00 2.2847319e+00 1.9157244e+00 1.1789826e+00 2.2181073e+00 2.3600847e+00 2.1283797e+00 1.3038405e+00 2.6057628e+00 2.5317978e+00 2.0322401e+00 1.4142136e+00 1.7832555e+00 2.0639767e+00 1.3674794e+00 7.7459667e-01 5.0000000e-01 1.2609520e+00 2.6457513e-01 4.8989795e-01 4.2426407e-01 7.7459667e-01 1.4628739e+00 4.2426407e-01 2.3194827e+00 1.0392305e+00 2.4041631e+00 1.5905974e+00 2.0297783e+00 3.1968735e+00 7.9372539e-01 2.7018512e+00 1.9416488e+00 2.9103264e+00 1.5779734e+00 1.4560220e+00 1.9672316e+00 1.0246951e+00 1.4352700e+00 1.7860571e+00 1.6522712e+00 3.5454196e+00 3.5071356e+00 9.2736185e-01 2.2847319e+00 9.6953597e-01 3.2878564e+00 1.1224972e+00 2.1047565e+00 2.4839485e+00 1.0246951e+00 1.0630146e+00 1.7606817e+00 2.2737634e+00 2.6514147e+00 3.4409301e+00 1.8138357e+00 1.1224972e+00 1.3564660e+00 3.0166206e+00 2.0396078e+00 1.6217275e+00 9.6436508e-01 2.0049938e+00 2.1377558e+00 1.9773720e+00 1.0392305e+00 2.3473389e+00 2.3043437e+00 1.8574176e+00 1.2247449e+00 1.5620499e+00 1.8275667e+00 1.0816654e+00 8.0622577e-01 1.8841444e+00 7.1414284e-01 6.0000000e-01 5.8309519e-01 3.4641016e-01 1.9748418e+00 6.7823300e-01 1.8165902e+00 8.2462113e-01 1.7832555e+00 1.1000000e+00 1.4966630e+00 2.5961510e+00 1.3379088e+00 2.1213203e+00 1.4866069e+00 2.2427661e+00 9.0000000e-01 9.5916630e-01 1.3379088e+00 9.6436508e-01 1.1747340e+00 1.1958261e+00 1.0630146e+00 2.8722813e+00 2.9698485e+00 9.0553851e-01 1.6431677e+00 8.6023253e-01 2.7147744e+00 6.1644140e-01 1.4662878e+00 1.8357560e+00 5.0000000e-01 5.0000000e-01 1.2727922e+00 1.6401219e+00 2.0566964e+00 2.7349589e+00 1.3304135e+00 5.8309519e-01 1.0770330e+00 2.3706539e+00 1.4832397e+00 1.0344080e+00 4.5825757e-01 1.3341664e+00 1.5394804e+00 1.3076697e+00 8.2462113e-01 1.7406895e+00 1.6941074e+00 1.2369317e+00 8.3666003e-01 9.3808315e-01 1.2727922e+00 6.7082039e-01 1.1224972e+00 3.1622777e-01 4.5825757e-01 3.8729833e-01 5.9160798e-01 1.2288206e+00 2.6457513e-01 2.5357445e+00 1.3076697e+00 2.5039968e+00 1.8055470e+00 2.2113344e+00 3.3120990e+00 1.1489125e+00 2.8266588e+00 2.1023796e+00 3.0099834e+00 1.6431677e+00 1.5968719e+00 2.0542639e+00 1.2884099e+00 1.6401219e+00 1.9026298e+00 1.8055470e+00 3.6523965e+00 3.6373067e+00 1.1357817e+00 2.3811762e+00 1.2369317e+00 3.4029399e+00 1.1958261e+00 2.2360680e+00 2.5845696e+00 1.0954451e+00 1.1916375e+00 1.9416488e+00 2.3494680e+00 2.7386128e+00 3.5000000e+00 1.9899749e+00 1.2609520e+00 1.6401219e+00 3.0643107e+00 2.2113344e+00 1.7944358e+00 1.0954451e+00 2.0566964e+00 2.2494444e+00 1.9697716e+00 1.3076697e+00 2.4859606e+00 2.4248711e+00 1.9026298e+00 1.3228757e+00 1.6522712e+00 1.9924859e+00 1.3190906e+00 1.1916375e+00 1.3527749e+00 1.3228757e+00 1.7000000e+00 3.8729833e-01 1.2124356e+00 3.4971417e+00 2.2022716e+00 3.5874782e+00 2.8248894e+00 3.2295511e+00 4.3988635e+00 1.4071247e+00 3.9102430e+00 3.1336879e+00 4.0767634e+00 2.7018512e+00 2.6324893e+00 3.1272992e+00 2.1023796e+00 2.4677925e+00 2.9086079e+00 2.8670542e+00 4.7476310e+00 4.6936127e+00 2.0371549e+00 3.4452866e+00 2.0420578e+00 4.4833024e+00 2.2472205e+00 3.2954514e+00 3.6851052e+00 2.1400935e+00 2.2135944e+00 2.9512709e+00 3.4554305e+00 3.8288379e+00 4.6119410e+00 2.9899833e+00 2.3302360e+00 2.5980762e+00 4.1605288e+00 3.1859065e+00 2.8425341e+00 2.0928450e+00 3.1416556e+00 3.2832910e+00 3.0298515e+00 2.2022716e+00 3.5355339e+00 3.4496377e+00 2.9461840e+00 2.3302360e+00 2.7110883e+00 2.9580399e+00 2.2759613e+00 3.3166248e-01 2.2360680e-01 6.4031242e-01 1.3304135e+00 1.7320508e-01 2.3515952e+00 1.1000000e+00 2.4228083e+00 1.6552945e+00 2.0663978e+00 3.2388269e+00 8.8317609e-01 2.7549955e+00 2.0149442e+00 2.9017236e+00 1.5362291e+00 1.4866069e+00 1.9646883e+00 1.0862780e+00 1.4387495e+00 1.7606817e+00 1.6852300e+00 3.5608988e+00 3.5651087e+00 1.0440307e+00 2.2781571e+00 9.9498744e-01 3.3406586e+00 1.1090537e+00 2.1118712e+00 2.5099801e+00 9.8994949e-01 1.0392305e+00 1.8027756e+00 2.3021729e+00 2.6870058e+00 3.4394767e+00 1.8493242e+00 1.1618950e+00 1.4933185e+00 3.0182777e+00 2.0371549e+00 1.6552945e+00 9.2736185e-01 1.9824228e+00 2.1307276e+00 1.9131126e+00 1.1000000e+00 2.3622024e+00 2.2934690e+00 1.8165902e+00 1.2369317e+00 1.5459625e+00 1.8138357e+00 1.1135529e+00 1.4142136e-01 5.2915026e-01 1.4352700e+00 2.4494897e-01 2.3194827e+00 1.1832160e+00 2.3790755e+00 1.6401219e+00 2.0493902e+00 3.1906112e+00 1.1090537e+00 2.7092434e+00 2.0420578e+00 2.8124722e+00 1.4594520e+00 1.5099669e+00 1.9261360e+00 1.2369317e+00 1.5165751e+00 1.7175564e+00 1.6401219e+00 3.4481879e+00 3.5580894e+00 1.2083046e+00 2.2226111e+00 1.0862780e+00 3.3060551e+00 1.1401754e+00 2.0371549e+00 2.4269322e+00 1.0049876e+00 1.0049876e+00 1.8165902e+00 2.2293497e+00 2.6514147e+00 3.3105891e+00 1.8681542e+00 1.1401754e+00 1.5231546e+00 2.9698485e+00 1.9798990e+00 1.5968719e+00 9.0000000e-01 1.9235384e+00 2.1000000e+00 1.8627936e+00 1.1832160e+00 2.3130067e+00 2.2427661e+00 1.7916473e+00 1.3190906e+00 1.5099669e+00 1.7492856e+00 1.1000000e+00 5.0990195e-01 1.4142136e+00 1.4142136e-01 2.2803509e+00 1.1045361e+00 2.3452079e+00 1.6031220e+00 2.0049938e+00 3.1654384e+00 1.0246951e+00 2.6870058e+00 1.9924859e+00 2.7910571e+00 1.4247807e+00 1.4491377e+00 1.8841444e+00 1.1357817e+00 1.4282857e+00 1.6703293e+00 1.6093477e+00 3.4452866e+00 3.5185224e+00 1.1224972e+00 2.1863211e+00 1.0000000e+00 3.2787193e+00 1.0677078e+00 2.0124612e+00 2.4145393e+00 9.3273791e-01 9.5393920e-01 1.7606817e+00 2.2158520e+00 2.6210685e+00 3.3136083e+00 1.8083141e+00 1.1045361e+00 1.4899664e+00 2.9359837e+00 1.9442222e+00 1.5716234e+00 8.4261498e-01 1.8867962e+00 2.0518285e+00 1.8138357e+00 1.1045361e+00 2.2781571e+00 2.2022716e+00 1.7349352e+00 1.2328828e+00 1.4628739e+00 1.7146428e+00 1.0535654e+00 1.7606817e+00 5.4772256e-01 2.1213203e+00 1.0954451e+00 2.0049938e+00 1.3964240e+00 1.7776389e+00 2.8106939e+00 1.4317821e+00 2.3366643e+00 1.7058722e+00 2.4839485e+00 1.1445523e+00 1.2000000e+00 1.5652476e+00 1.1789826e+00 1.4212670e+00 1.4594520e+00 1.3379088e+00 3.1032241e+00 3.1780497e+00 1.0295630e+00 1.8814888e+00 1.1045361e+00 2.9171904e+00 8.1240384e-01 1.7349352e+00 2.0566964e+00 7.1414284e-01 7.9372539e-01 1.5427249e+00 1.8303005e+00 2.2472205e+00 2.9325757e+00 1.5968719e+00 8.3666003e-01 1.3416408e+00 2.5495098e+00 1.7776389e+00 1.3304135e+00 7.4161985e-01 1.5427249e+00 1.7860571e+00 1.4730920e+00 1.0954451e+00 2.0024984e+00 1.9519221e+00 1.4387495e+00 1.0099505e+00 1.1832160e+00 1.5684387e+00 9.9498744e-01 1.3038405e+00 3.6110940e+00 2.3622024e+00 3.6959437e+00 2.9748950e+00 3.3555923e+00 4.5232732e+00 1.6278821e+00 4.0472213e+00 3.3000000e+00 4.1460825e+00 2.7694765e+00 2.7676705e+00 3.2233523e+00 2.2737634e+00 2.5845696e+00 2.9849623e+00 2.9916551e+00 4.8321838e+00 4.8394215e+00 2.2494444e+00 3.5298725e+00 2.1817424e+00 4.6206060e+00 2.3622024e+00 3.3896903e+00 3.7934153e+00 2.2427661e+00 2.3130067e+00 3.0886890e+00 3.5707142e+00 3.9534795e+00 4.6797436e+00 3.1224990e+00 2.4698178e+00 2.8035692e+00 4.2497059e+00 3.2710854e+00 2.9647934e+00 2.1886069e+00 3.2186954e+00 3.3719431e+00 3.0740852e+00 2.3622024e+00 3.6373067e+00 3.5284558e+00 3.0149627e+00 2.4657656e+00 2.8035692e+00 3.0364453e+00 2.4062419e+00 2.3790755e+00 1.1747340e+00 2.4248711e+00 1.6941074e+00 2.0928450e+00 3.2465366e+00 1.0246951e+00 2.7676705e+00 2.0566964e+00 2.8861739e+00 1.5132746e+00 1.5165751e+00 1.9621417e+00 1.1789826e+00 1.4899664e+00 1.7578396e+00 1.7000000e+00 3.5454196e+00 3.5888717e+00 1.1401754e+00 2.2715633e+00 1.0677078e+00 3.3541020e+00 1.1224972e+00 2.1095023e+00 2.5039968e+00 9.9498744e-01 1.0440307e+00 1.8384776e+00 2.2956481e+00 2.6925824e+00 3.4088121e+00 1.8841444e+00 1.1832160e+00 1.5684387e+00 3.0066593e+00 2.0445048e+00 1.6703293e+00 9.3273791e-01 1.9646883e+00 2.1330729e+00 1.8788294e+00 1.1747340e+00 2.3685439e+00 2.2912878e+00 1.8027756e+00 1.2727922e+00 1.5427249e+00 1.8165902e+00 1.1532563e+00 1.3341664e+00 9.4868330e-01 9.0000000e-01 5.0990195e-01 1.5165751e+00 2.3430749e+00 1.3190906e+00 1.1532563e+00 9.5393920e-01 1.0535654e+00 1.1045361e+00 8.6602540e-01 1.5000000e+00 1.1489125e+00 7.4161985e-01 9.3273791e-01 1.6703293e+00 1.8165902e+00 1.8165902e+00 7.0710678e-01 1.4832397e+00 1.7175564e+00 1.4352700e+00 6.4031242e-01 1.1445523e+00 1.4798649e+00 1.3527749e+00 7.6157731e-01 1.3228757e+00 1.3527749e+00 1.7944358e+00 7.1414284e-01 1.4352700e+00 1.3784049e+00 1.4491377e+00 4.2426407e-01 8.8881944e-01 1.4525839e+00 9.5916630e-01 6.0827625e-01 1.1180340e+00 1.3341664e+00 5.5677644e-01 5.0000000e-01 9.6436508e-01 1.4142136e+00 1.0099505e+00 6.4807407e-01 1.2449900e+00 1.5684387e+00 7.4161985e-01 1.0770330e+00 2.3706539e+00 1.1180340e+00 1.9339080e+00 1.1618950e+00 2.0322401e+00 8.6602540e-01 6.3245553e-01 1.1357817e+00 2.6457513e-01 5.0990195e-01 9.0000000e-01 8.6602540e-01 2.7331301e+00 2.6495283e+00 6.7823300e-01 1.4071247e+00 3.1622777e-01 2.4879711e+00 5.4772256e-01 1.2529964e+00 1.7406895e+00 5.1961524e-01 4.7958315e-01 8.1240384e-01 1.6217275e+00 1.8894444e+00 2.7055499e+00 8.4261498e-01 6.4807407e-01 7.7459667e-01 2.2045408e+00 1.1135529e+00 8.3066239e-01 4.7958315e-01 1.2247449e+00 1.2124356e+00 1.2369317e+00 0.0000000e+00 1.4317821e+00 1.3747727e+00 1.0344080e+00 5.4772256e-01 7.7459667e-01 9.4868330e-01 3.3166248e-01 9.1104336e-01 6.1644140e-01 8.6023253e-01 2.6851443e+00 5.4772256e-01 7.1414284e-01 7.5498344e-01 1.0246951e+00 9.8994949e-01 5.0000000e-01 1.7406895e+00 1.5684387e+00 9.6436508e-01 7.8102497e-01 1.2845233e+00 1.2489996e+00 1.7378147e+00 4.0000000e-01 1.8165902e+00 1.0246951e+00 1.3490738e+00 5.3851648e-01 3.8729833e-01 1.4662878e+00 1.4456832e+00 7.8740079e-01 5.1961524e-01 4.5825757e-01 1.2409674e+00 7.9372539e-01 1.2961481e+00 1.3190906e+00 6.6332496e-01 9.8994949e-01 8.6602540e-01 1.5842980e+00 5.4772256e-01 5.9160798e-01 8.5440037e-01 1.5684387e+00 4.1231056e-01 6.7082039e-01 8.3066239e-01 1.3190906e+00 9.2736185e-01 1.1224972e+00 1.4730920e+00 5.0000000e-01 1.6703293e+00 1.8275667e+00 1.2206556e+00 6.0000000e-01 1.4282857e+00 6.4807407e-01 3.8729833e-01 6.0000000e-01 9.5916630e-01 9.3273791e-01 6.6332496e-01 2.4494897e-01 2.0346990e+00 1.9974984e+00 1.0148892e+00 8.4261498e-01 1.0148892e+00 1.7944358e+00 7.2801099e-01 6.4807407e-01 1.0295630e+00 8.1240384e-01 7.3484692e-01 3.3166248e-01 9.4868330e-01 1.2165525e+00 2.0124612e+00 4.2426407e-01 5.9160798e-01 5.3851648e-01 1.5716234e+00 7.8102497e-01 2.4494897e-01 8.6023253e-01 7.2801099e-01 7.4833148e-01 9.4868330e-01 7.4161985e-01 8.2462113e-01 9.0553851e-01 7.6157731e-01 7.2801099e-01 5.0000000e-01 7.4161985e-01 6.4807407e-01 1.3638182e+00 2.1794495e+00 1.0295630e+00 6.7082039e-01 1.0148892e+00 7.5498344e-01 6.6332496e-01 4.3588989e-01 1.2529964e+00 1.0295630e+00 5.5677644e-01 5.0000000e-01 1.7000000e+00 1.6792856e+00 1.4212670e+00 4.6904158e-01 1.3038405e+00 1.5264338e+00 1.0488088e+00 3.8729833e-01 8.5440037e-01 1.1357817e+00 1.0630146e+00 3.1622777e-01 9.2195445e-01 1.0148892e+00 1.7320508e+00 3.0000000e-01 1.0295630e+00 1.0000000e+00 1.2409674e+00 5.2915026e-01 5.1961524e-01 1.1874342e+00 5.8309519e-01 3.6055513e-01 8.1853528e-01 1.0770330e+00 3.8729833e-01 4.7958315e-01 6.4031242e-01 1.0099505e+00 6.3245553e-01 6.4807407e-01 1.0049876e+00 3.4799425e+00 5.2915026e-01 1.3379088e+00 9.6436508e-01 1.8734994e+00 1.8055470e+00 1.3601471e+00 2.5357445e+00 2.3706539e+00 1.7916473e+00 1.5842980e+00 8.1853528e-01 5.4772256e-01 2.4738634e+00 1.1747340e+00 2.6343880e+00 2.6457513e-01 2.1817424e+00 1.3076697e+00 8.0622577e-01 2.3086793e+00 2.2869193e+00 1.5748016e+00 1.0246951e+00 6.0827625e-01 8.8317609e-01 1.5779734e+00 2.0832667e+00 1.9748418e+00 5.4772256e-01 1.7146428e+00 1.6583124e+00 2.4269322e+00 1.3928388e+00 1.3820275e+00 1.6703293e+00 2.3706539e+00 1.1000000e+00 1.3674794e+00 1.6763055e+00 2.1307276e+00 1.7832555e+00 1.8973666e+00 2.2869193e+00 3.0282008e+00 2.2226111e+00 3.1144823e+00 1.8708287e+00 1.7233688e+00 2.2405357e+00 9.8994949e-01 1.3228757e+00 1.9339080e+00 1.9544820e+00 3.8236109e+00 3.7376463e+00 1.2609520e+00 2.5079872e+00 9.1104336e-01 3.5860842e+00 1.4730920e+00 2.3409400e+00 2.8354894e+00 1.3711309e+00 1.3638182e+00 1.9261360e+00 2.6907248e+00 2.9899833e+00 3.7934153e+00 1.9493589e+00 1.5652476e+00 1.6583124e+00 3.3181320e+00 2.1142375e+00 1.9026298e+00 1.2489996e+00 2.3086793e+00 2.3021729e+00 2.2538855e+00 1.1180340e+00 2.5337719e+00 2.4413111e+00 2.0832667e+00 1.5000000e+00 1.8411953e+00 1.9157244e+00 1.2727922e+00 8.7749644e-01 1.0148892e+00 1.4866069e+00 1.3638182e+00 9.9498744e-01 2.1095023e+00 2.0149442e+00 1.4662878e+00 1.1357817e+00 1.1357817e+00 9.2736185e-01 1.9899749e+00 9.2736185e-01 2.2135944e+00 6.0827625e-01 1.7320508e+00 9.8488578e-01 4.3588989e-01 1.8627936e+00 1.8466185e+00 1.1832160e+00 5.5677644e-01 2.6457513e-01 1.1045361e+00 1.2124356e+00 1.5937377e+00 1.4764823e+00 6.7823300e-01 1.4491377e+00 1.2206556e+00 1.9874607e+00 1.0488088e+00 1.1180340e+00 1.3747727e+00 1.9339080e+00 8.6602540e-01 1.1704700e+00 1.3527749e+00 1.6911535e+00 1.3784049e+00 1.5874508e+00 1.8466185e+00 1.4282857e+00 1.0295630e+00 6.2449980e-01 6.6332496e-01 1.2961481e+00 1.3228757e+00 1.0392305e+00 6.1644140e-01 1.9131126e+00 1.5716234e+00 1.1445523e+00 8.8881944e-01 1.4662878e+00 1.3928388e+00 1.0049876e+00 8.6023253e-01 8.8317609e-01 1.1575837e+00 1.1916375e+00 5.5677644e-01 7.3484692e-01 8.2462113e-01 1.8788294e+00 6.1644140e-01 9.1104336e-01 7.5498344e-01 1.2609520e+00 1.1704700e+00 7.3484692e-01 1.3190906e+00 8.0622577e-01 8.7177979e-01 1.0677078e+00 1.1618950e+00 8.7177979e-01 1.0677078e+00 9.2736185e-01 9.0000000e-01 8.3066239e-01 1.2124356e+00 1.1747340e+00 1.3784049e+00 1.5652476e+00 1.0198039e+00 2.2181073e+00 1.9000000e+00 1.2165525e+00 1.3038405e+00 8.6023253e-01 1.3892444e+00 2.3685439e+00 6.7082039e-01 2.2113344e+00 1.2247449e+00 1.8841444e+00 8.1240384e-01 8.1240384e-01 1.9544820e+00 1.8708287e+00 1.3000000e+00 1.1224972e+00 1.0198039e+00 9.3273791e-01 1.2727922e+00 1.8574176e+00 1.9157244e+00 8.0622577e-01 1.0535654e+00 1.3190906e+00 1.9949937e+00 9.9498744e-01 8.7177979e-01 1.1747340e+00 2.0322401e+00 6.3245553e-01 7.0710678e-01 1.2083046e+00 1.8947295e+00 1.3820275e+00 1.2529964e+00 1.8814888e+00 5.5677644e-01 5.4772256e-01 1.0677078e+00 9.0000000e-01 3.7416574e-01 4.8989795e-01 2.0976177e+00 2.2649503e+00 1.2288206e+00 7.8102497e-01 1.0049876e+00 2.0396078e+00 6.0827625e-01 6.4807407e-01 1.1575837e+00 6.1644140e-01 5.2915026e-01 6.5574385e-01 1.0862780e+00 1.4071247e+00 2.0024984e+00 6.7823300e-01 6.7082039e-01 1.0630146e+00 1.6031220e+00 7.0000000e-01 4.6904158e-01 6.4807407e-01 5.1961524e-01 6.7823300e-01 5.0990195e-01 8.6602540e-01 9.0553851e-01 8.1240384e-01 4.2426407e-01 7.4161985e-01 2.2360680e-01 5.5677644e-01 6.6332496e-01 5.7445626e-01 7.9372539e-01 8.1240384e-01 6.4031242e-01 3.8729833e-01 2.2248595e+00 2.1023796e+00 8.1240384e-01 9.0553851e-01 9.0553851e-01 1.9157244e+00 4.2426407e-01 8.0622577e-01 1.1789826e+00 5.5677644e-01 5.9160798e-01 3.7416574e-01 1.0344080e+00 1.2845233e+00 2.1633308e+00 4.3588989e-01 4.6904158e-01 6.6332496e-01 1.6062378e+00 9.1651514e-01 4.5825757e-01 7.1414284e-01 6.7823300e-01 7.6811457e-01 7.8102497e-01 6.3245553e-01 9.6436508e-01 9.8488578e-01 5.9160798e-01 3.7416574e-01 3.4641016e-01 8.3666003e-01 6.2449980e-01 1.3114877e+00 1.1357817e+00 5.2915026e-01 4.2426407e-01 1.7029386e+00 1.7233688e+00 1.3747727e+00 3.6055513e-01 1.3601471e+00 1.5165751e+00 8.8881944e-01 3.7416574e-01 7.3484692e-01 9.8994949e-01 9.6953597e-01 4.5825757e-01 7.0710678e-01 8.9442719e-01 1.6340135e+00 4.6904158e-01 9.0000000e-01 1.0723805e+00 1.1000000e+00 7.1414284e-01 5.0990195e-01 1.1045361e+00 1.7320508e-01 3.4641016e-01 4.6904158e-01 1.1357817e+00 4.8989795e-01 5.4772256e-01 3.7416574e-01 8.8881944e-01 4.3588989e-01 7.5498344e-01 1.0295630e+00 5.1961524e-01 1.0770330e+00 1.0862780e+00 2.9359837e+00 2.7766887e+00 6.5574385e-01 1.5842980e+00 3.3166248e-01 2.6419690e+00 6.7082039e-01 1.4628739e+00 1.9442222e+00 6.4807407e-01 6.7823300e-01 9.7467943e-01 1.8165902e+00 2.0493902e+00 2.9137605e+00 9.8994949e-01 8.4261498e-01 9.4339811e-01 2.3558438e+00 1.3000000e+00 1.0677078e+00 6.4807407e-01 1.4035669e+00 1.3711309e+00 1.3784049e+00 2.6457513e-01 1.6124515e+00 1.5427249e+00 1.1747340e+00 6.0827625e-01 9.6436508e-01 1.1445523e+00 5.8309519e-01 7.5498344e-01 1.0246951e+00 2.6851443e+00 2.6267851e+00 1.1045361e+00 1.3190906e+00 4.8989795e-01 2.5159491e+00 8.1240384e-01 1.2288206e+00 1.8138357e+00 7.8102497e-01 7.2801099e-01 8.3666003e-01 1.7691806e+00 1.9519221e+00 2.6944387e+00 8.0622577e-01 1.0295630e+00 1.1747340e+00 2.1587033e+00 9.2736185e-01 9.8488578e-01 7.2801099e-01 1.2165525e+00 1.0723805e+00 1.1445523e+00 5.0990195e-01 1.3453624e+00 1.1958261e+00 9.3273791e-01 7.7459667e-01 8.3666003e-01 7.8740079e-01 6.4031242e-01 5.8309519e-01 2.0049938e+00 2.1470911e+00 1.3747727e+00 6.4031242e-01 1.0246951e+00 1.9748418e+00 8.1853528e-01 5.4772256e-01 1.1747340e+00 8.3666003e-01 7.3484692e-01 5.3851648e-01 1.1916375e+00 1.4000000e+00 1.9773720e+00 5.0990195e-01 9.2195445e-01 1.1618950e+00 1.5394804e+00 3.8729833e-01 5.4772256e-01 8.3666003e-01 5.5677644e-01 4.4721360e-01 5.4772256e-01 9.0000000e-01 7.2111026e-01 5.4772256e-01 3.7416574e-01 8.6602540e-01 3.8729833e-01 3.0000000e-01 7.6157731e-01 1.9183326e+00 1.9519221e+00 1.1090537e+00 7.0000000e-01 1.1180340e+00 1.7204651e+00 7.0000000e-01 5.0990195e-01 8.8317609e-01 7.8740079e-01 7.2111026e-01 3.8729833e-01 7.8740079e-01 1.1045361e+00 1.8574176e+00 4.6904158e-01 5.7445626e-01 7.0000000e-01 1.4317821e+00 7.5498344e-01 1.4142136e-01 8.6023253e-01 5.1961524e-01 6.4807407e-01 7.6157731e-01 8.6602540e-01 7.3484692e-01 8.1240384e-01 6.1644140e-01 7.4161985e-01 3.6055513e-01 7.1414284e-01 7.2111026e-01 1.2206556e+00 2.9715316e+00 1.4177447e+00 2.9478806e+00 1.0198039e+00 2.5632011e+00 1.5033296e+00 1.1224972e+00 2.6495283e+00 2.5690465e+00 1.9773720e+00 1.4352700e+00 1.2409674e+00 4.1231056e-01 1.9748418e+00 2.4515301e+00 2.4186773e+00 1.0049876e+00 1.8357560e+00 1.9442222e+00 2.7018512e+00 1.6822604e+00 1.6552945e+00 1.9235384e+00 2.7331301e+00 1.3490738e+00 1.5297059e+00 1.9748418e+00 2.5748786e+00 2.0904545e+00 2.0273135e+00 2.5690465e+00 2.7018512e+00 1.5620499e+00 2.9223278e+00 4.1231056e-01 2.4939928e+00 1.7233688e+00 1.2922848e+00 2.6362853e+00 2.6400758e+00 1.8601075e+00 1.4525839e+00 9.6436508e-01 1.3490738e+00 1.8520259e+00 2.4248711e+00 2.2494444e+00 8.9442719e-01 2.0736441e+00 2.0371549e+00 2.7766887e+00 1.7832555e+00 1.7175564e+00 2.0322401e+00 2.6495283e+00 1.4730920e+00 1.7233688e+00 2.0124612e+00 2.3958297e+00 2.1400935e+00 2.2671568e+00 2.6248809e+00 1.7146428e+00 8.8317609e-01 2.5278449e+00 6.6332496e-01 1.5968719e+00 1.8788294e+00 7.2801099e-01 8.6602540e-01 1.1135529e+00 1.6522712e+00 1.9209373e+00 2.8948230e+00 1.1704700e+00 6.7823300e-01 7.3484692e-01 2.3194827e+00 1.6431677e+00 1.1445523e+00 8.7749644e-01 1.4628739e+00 1.5716234e+00 1.5066519e+00 6.7823300e-01 1.7578396e+00 1.7860571e+00 1.3453624e+00 5.8309519e-01 1.0862780e+00 1.5099669e+00 8.6602540e-01 1.6062378e+00 1.3747727e+00 1.2247449e+00 3.0000000e-01 6.5574385e-01 1.3076697e+00 1.2529964e+00 6.7823300e-01 7.9372539e-01 8.5440037e-01 1.3928388e+00 6.5574385e-01 1.2328828e+00 1.3490738e+00 9.1651514e-01 6.4807407e-01 7.4161985e-01 1.3820275e+00 3.7416574e-01 2.6457513e-01 6.0827625e-01 1.4071247e+00 2.2360680e-01 3.0000000e-01 5.7445626e-01 1.2247449e+00 7.3484692e-01 7.8740079e-01 1.2845233e+00 2.7658633e+00 7.3484692e-01 1.4525839e+00 1.9924859e+00 6.4031242e-01 5.7445626e-01 1.0677078e+00 1.8894444e+00 2.1656408e+00 2.9223278e+00 1.0816654e+00 8.8317609e-01 1.0677078e+00 2.4454039e+00 1.2247449e+00 1.0630146e+00 5.0000000e-01 1.4282857e+00 1.3964240e+00 1.3820275e+00 3.1622777e-01 1.6401219e+00 1.5329710e+00 1.1958261e+00 7.7459667e-01 9.6953597e-01 1.0295630e+00 4.5825757e-01 2.2912878e+00 1.5033296e+00 9.6953597e-01 2.4289916e+00 2.4248711e+00 1.7058722e+00 1.1224972e+00 6.7823300e-01 1.0630146e+00 1.7146428e+00 2.1840330e+00 2.0420578e+00 7.0000000e-01 1.9209373e+00 1.8055470e+00 2.5651511e+00 1.5588457e+00 1.5684387e+00 1.8384776e+00 2.4879711e+00 1.3038405e+00 1.5811388e+00 1.8384776e+00 2.2248595e+00 1.9313208e+00 2.0952327e+00 2.4248711e+00 1.1180340e+00 1.5066519e+00 1.7320508e-01 3.6055513e-01 7.7459667e-01 1.3228757e+00 1.6340135e+00 2.4617067e+00 8.1853528e-01 3.7416574e-01 8.3666003e-01 1.9339080e+00 1.1575837e+00 7.2801099e-01 4.3588989e-01 9.2736185e-01 1.0816654e+00 9.0000000e-01 5.4772256e-01 1.3228757e+00 1.2845233e+00 7.6811457e-01 2.4494897e-01 5.0990195e-01 1.0000000e+00 5.3851648e-01 6.6332496e-01 1.1832160e+00 1.0862780e+00 5.9160798e-01 7.7459667e-01 9.6953597e-01 1.4798649e+00 6.0000000e-01 1.0630146e+00 1.1618950e+00 1.1357817e+00 5.1961524e-01 5.0990195e-01 1.2165525e+00 4.1231056e-01 3.7416574e-01 6.9282032e-01 1.2529964e+00 3.1622777e-01 4.0000000e-01 6.1644140e-01 1.1532563e+00 6.2449980e-01 6.2449980e-01 1.0862780e+00 1.6124515e+00 1.5684387e+00 1.0246951e+00 3.4641016e-01 4.6904158e-01 1.0246951e+00 1.0583005e+00 1.3674794e+00 1.3747727e+00 7.4161985e-01 1.1704700e+00 9.4868330e-01 1.7088007e+00 7.4161985e-01 8.8317609e-01 1.0770330e+00 1.7406895e+00 6.4807407e-01 9.1651514e-01 1.0862780e+00 1.5198684e+00 1.1000000e+00 1.2845233e+00 1.5937377e+00 2.4494897e-01 8.7749644e-01 1.4422205e+00 1.7720045e+00 2.5475478e+00 9.1651514e-01 4.3588989e-01 9.2195445e-01 2.0566964e+00 1.1704700e+00 7.8740079e-01 2.8284271e-01 1.0148892e+00 1.1575837e+00 9.5916630e-01 5.1961524e-01 1.4071247e+00 1.3416408e+00 8.3666003e-01 3.8729833e-01 5.7445626e-01 9.8488578e-01 4.6904158e-01 8.4261498e-01 1.4352700e+00 1.7832555e+00 2.4839485e+00 8.8317609e-01 4.5825757e-01 9.0000000e-01 2.0615528e+00 1.0246951e+00 6.7823300e-01 1.4142136e-01 9.9498744e-01 1.1045361e+00 9.6953597e-01 4.7958315e-01 1.3341664e+00 1.2569805e+00 8.3666003e-01 5.5677644e-01 5.3851648e-01 8.1853528e-01 2.8284271e-01 9.8488578e-01 1.1357817e+00 1.9748418e+00 1.0000000e-01 7.8740079e-01 7.8740079e-01 1.4212670e+00 6.7823300e-01 4.3588989e-01 9.6436508e-01 6.1644140e-01 5.1961524e-01 7.9372539e-01 8.1240384e-01 6.7082039e-01 7.1414284e-01 5.7445626e-01 7.0710678e-01 4.6904158e-01 6.9282032e-01 7.9372539e-01 5.0990195e-01 1.2845233e+00 1.0392305e+00 1.1618950e+00 1.2041595e+00 9.1104336e-01 1.2845233e+00 8.8317609e-01 1.5748016e+00 7.1414284e-01 9.6953597e-01 1.0392305e+00 1.6217275e+00 8.3666003e-01 1.0770330e+00 1.0488088e+00 1.3379088e+00 1.0049876e+00 1.3453624e+00 1.4899664e+00 1.1618950e+00 1.1575837e+00 1.5394804e+00 1.4933185e+00 5.3851648e-01 1.4387495e+00 1.2083046e+00 1.9235384e+00 9.3273791e-01 1.0392305e+00 1.2247449e+00 1.8894444e+00 8.4852814e-01 1.1224972e+00 1.2247449e+00 1.5842980e+00 1.2922848e+00 1.5652476e+00 1.8165902e+00 1.9824228e+00 2.3452079e+00 2.3832751e+00 9.2736185e-01 1.8761663e+00 1.8947295e+00 2.6172505e+00 1.5811388e+00 1.6522712e+00 1.8083141e+00 2.7055499e+00 1.3820275e+00 1.5588457e+00 1.9000000e+00 2.4939928e+00 2.0099751e+00 2.0346990e+00 2.5238859e+00 8.6602540e-01 8.7749644e-01 1.4106736e+00 6.4031242e-01 5.0990195e-01 1.0000000e+00 6.2449980e-01 4.6904158e-01 7.7459667e-01 8.4261498e-01 6.4807407e-01 6.6332496e-01 5.4772256e-01 7.4161985e-01 5.0000000e-01 6.7082039e-01 8.3666003e-01 5.8309519e-01 1.9078784e+00 1.1916375e+00 5.9160798e-01 5.5677644e-01 9.4868330e-01 1.1445523e+00 1.0440307e+00 6.4807407e-01 1.3000000e+00 1.3304135e+00 9.2195445e-01 5.0990195e-01 5.8309519e-01 1.0488088e+00 5.3851648e-01 1.9442222e+00 1.2961481e+00 7.1414284e-01 9.8488578e-01 1.1916375e+00 1.2688578e+00 1.3964240e+00 7.7459667e-01 1.3228757e+00 1.4387495e+00 1.2206556e+00 8.1240384e-01 9.1651514e-01 1.2247449e+00 7.8102497e-01 1.5427249e+00 1.5198684e+00 2.1977261e+00 1.0862780e+00 1.1269428e+00 1.2845233e+00 2.2045408e+00 9.4339811e-01 1.1357817e+00 1.3453624e+00 1.8920888e+00 1.5297059e+00 1.7029386e+00 2.1189620e+00 6.8556546e-01 1.1180340e+00 7.6157731e-01 5.0000000e-01 8.4261498e-01 1.1135529e+00 6.2449980e-01 4.3588989e-01 7.0000000e-01 1.1916375e+00 7.2111026e-01 2.4494897e-01 9.6436508e-01 8.1240384e-01 5.9160798e-01 6.7823300e-01 8.1240384e-01 8.3066239e-01 7.6157731e-01 8.1240384e-01 6.6332496e-01 7.9372539e-01 3.8729833e-01 6.2449980e-01 6.4807407e-01 1.1269428e+00 1.2247449e+00 1.0770330e+00 4.7958315e-01 1.4628739e+00 1.3711309e+00 9.4868330e-01 6.2449980e-01 6.7082039e-01 9.0000000e-01 3.1622777e-01 4.1231056e-01 3.6055513e-01 1.2247449e+00 5.5677644e-01 5.7445626e-01 3.6055513e-01 9.5916630e-01 4.6904158e-01 7.8740079e-01 1.0908712e+00 5.4772256e-01 1.2124356e+00 3.4641016e-01 2.4494897e-01 4.2426407e-01 1.0630146e+00 6.0827625e-01 6.2449980e-01 1.1224972e+00 1.2369317e+00 8.1240384e-01 6.9282032e-01 2.4494897e-01 9.4339811e-01 5.1961524e-01 8.1853528e-01 1.1224972e+00 1.4317821e+00 1.3747727e+00 1.0344080e+00 5.4772256e-01 7.7459667e-01 9.4868330e-01 3.3166248e-01 3.1622777e-01 7.3484692e-01 1.3076697e+00 8.4261498e-01 8.0622577e-01 1.3190906e+00 6.1644140e-01 1.2845233e+00 7.9372539e-01 6.2449980e-01 1.2569805e+00 7.8102497e-01 3.6055513e-01 6.7082039e-01 9.4868330e-01 5.8309519e-01 1.0677078e+00 6.5574385e-01 6.1644140e-01 6.4031242e-01 7.6811457e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..1b7552021bf9a0606c36628f9432e8f0afe8d765 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-euclidean-ml.txt @@ -0,0 +1 @@ + 4.0515260e+00 4.2121458e+00 3.7357405e+00 4.2313317e+00 3.9136009e+00 4.3843298e+00 3.9811426e+00 4.3624182e+00 4.0642508e+00 4.2105933e+00 4.0747226e+00 3.9068586e+00 4.1637004e+00 4.4303203e+00 4.1841564e+00 4.1063279e+00 4.1862390e+00 4.0719925e+00 4.2227579e+00 4.3173531e+00 3.8811067e+00 3.7577567e+00 4.0623722e+00 3.9882453e+00 4.0432671e+00 3.9085109e+00 4.0283414e+00 4.0846110e+00 3.6459235e+00 3.9544001e+00 4.1134244e+00 4.1805752e+00 3.5121011e+00 4.2747789e+00 4.1048323e+00 3.9269426e+00 3.8932032e+00 3.8281172e+00 3.7288430e+00 4.0863477e+00 4.1527428e+00 4.1646409e+00 4.2027433e+00 3.8441594e+00 4.8419117e+00 4.2455384e+00 3.7622220e+00 4.3967923e+00 4.4663183e+00 4.0435853e+00 4.0421692e+00 4.3124625e+00 4.6499961e+00 4.5595743e+00 3.4230430e+00 4.2612266e+00 3.5676603e+00 4.0866580e+00 4.2307103e+00 3.8521940e+00 3.9951183e+00 3.1022409e+00 3.7290193e+00 4.1931517e+00 4.1127027e+00 3.6633651e+00 4.0235815e+00 3.9729858e+00 4.1980132e+00 4.1579993e+00 3.9948955e+00 3.9081966e+00 3.9031152e+00 3.5069036e+00 4.0015727e+00 3.6763496e+00 3.6614339e+00 3.6227109e+00 3.7357992e+00 4.0170026e+00 3.5216829e+00 3.9322227e+00 3.9094621e+00 4.0170286e+00 4.3264246e+00 4.3435483e+00 4.0788635e+00 4.4761765e+00 3.8468186e+00 4.1490333e+00 4.2800007e+00 4.2260191e+00 4.3031858e+00 4.1897413e+00 4.0530244e+00 3.5893641e+00 4.2186615e+00 3.7979503e+00 4.0915473e+00 4.1343073e+00 4.5063851e+00 3.6394889e+00 4.2508448e+00 3.7160826e+00 4.0105262e+00 4.1578269e+00 4.0290590e+00 3.6971819e+00 3.9414087e+00 4.2522313e+00 4.4091714e+00 4.1542292e+00 3.9594691e+00 4.0923600e+00 4.0855497e+00 3.8253075e+00 4.3034717e+00 4.0976731e+00 4.1316523e+00 4.0872717e+00 4.2643353e+00 3.8887280e+00 3.9411273e+00 3.8848001e+00 4.3481996e+00 3.8716733e+00 3.9084684e+00 3.7546361e+00 3.9354816e+00 3.8293694e+00 3.7568515e+00 3.7184961e+00 3.8404278e+00 4.2570811e+00 4.1423777e+00 4.0291411e+00 4.2094682e+00 3.6127418e+00 4.0459839e+00 3.7737985e+00 3.7647653e+00 3.9762006e+00 3.8999512e+00 3.8509090e+00 3.8975941e+00 3.8432839e+00 4.2109046e+00 4.1339124e+00 3.5898873e+00 4.0794519e+00 4.3504966e+00 3.8862612e+00 3.8332931e+00 4.2190310e+00 4.1366595e+00 3.7220268e+00 4.1250795e+00 3.3169452e+00 4.0757181e+00 3.6487114e+00 3.9513724e+00 4.0735549e+00 3.9137880e+00 3.9656942e+00 3.7724953e+00 4.0505153e+00 3.9062302e+00 4.5671852e+00 3.7542175e+00 4.3731708e+00 3.6733907e+00 4.4667545e+00 4.1004635e+00 4.0530038e+00 4.0346958e+00 4.2145752e+00 4.4298637e+00 4.2982360e+00 4.0878239e+00 4.4061563e+00 4.2115971e+00 3.8263277e+00 3.8603258e+00 3.8572375e+00 4.1051910e+00 4.3787786e+00 4.5309659e+00 4.0047055e+00 4.1308854e+00 3.6283561e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-hamming-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-hamming-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..bc4e1ddcb6e1e8699570ecc410e2cb0cfdba2507 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-hamming-ml.txt @@ -0,0 +1 @@ + 4.6000000e-01 4.3000000e-01 4.3000000e-01 5.4000000e-01 4.1000000e-01 5.3000000e-01 4.3000000e-01 5.9000000e-01 4.8000000e-01 4.7000000e-01 4.6000000e-01 4.9000000e-01 4.5000000e-01 5.5000000e-01 5.3000000e-01 4.5000000e-01 4.8000000e-01 4.7000000e-01 4.8000000e-01 5.1000000e-01 4.9000000e-01 4.4000000e-01 4.9000000e-01 4.7000000e-01 4.9000000e-01 4.7000000e-01 5.2000000e-01 4.7000000e-01 4.2000000e-01 4.9000000e-01 4.7000000e-01 5.5000000e-01 3.9000000e-01 5.5000000e-01 4.6000000e-01 4.5000000e-01 4.0000000e-01 4.8000000e-01 4.5000000e-01 4.8000000e-01 4.8000000e-01 5.0000000e-01 4.8000000e-01 4.5000000e-01 6.4000000e-01 5.7000000e-01 4.6000000e-01 5.4000000e-01 5.6000000e-01 4.8000000e-01 4.8000000e-01 5.3000000e-01 5.4000000e-01 5.3000000e-01 4.5000000e-01 5.8000000e-01 4.2000000e-01 5.4000000e-01 6.0000000e-01 5.1000000e-01 4.6000000e-01 4.1000000e-01 4.4000000e-01 5.6000000e-01 5.4000000e-01 4.8000000e-01 4.8000000e-01 5.1000000e-01 5.2000000e-01 5.5000000e-01 4.5000000e-01 4.3000000e-01 4.7000000e-01 4.7000000e-01 5.6000000e-01 4.9000000e-01 4.8000000e-01 4.5000000e-01 4.9000000e-01 4.7000000e-01 4.5000000e-01 4.5000000e-01 5.6000000e-01 4.9000000e-01 5.8000000e-01 5.4000000e-01 4.6000000e-01 5.8000000e-01 5.3000000e-01 5.4000000e-01 5.5000000e-01 5.0000000e-01 5.2000000e-01 4.8000000e-01 5.0000000e-01 3.8000000e-01 5.3000000e-01 4.8000000e-01 5.1000000e-01 4.8000000e-01 5.2000000e-01 4.7000000e-01 5.0000000e-01 4.3000000e-01 4.8000000e-01 5.2000000e-01 5.0000000e-01 4.2000000e-01 4.2000000e-01 4.7000000e-01 5.4000000e-01 5.1000000e-01 5.4000000e-01 5.1000000e-01 4.8000000e-01 4.7000000e-01 5.2000000e-01 5.2000000e-01 5.4000000e-01 5.4000000e-01 5.0000000e-01 4.5000000e-01 4.4000000e-01 4.1000000e-01 5.7000000e-01 4.6000000e-01 5.1000000e-01 5.2000000e-01 5.0000000e-01 4.8000000e-01 5.0000000e-01 4.4000000e-01 5.3000000e-01 5.2000000e-01 4.9000000e-01 5.7000000e-01 5.8000000e-01 4.9000000e-01 5.1000000e-01 4.5000000e-01 5.3000000e-01 4.5000000e-01 4.4000000e-01 3.5000000e-01 4.2000000e-01 5.3000000e-01 5.2000000e-01 5.0000000e-01 3.8000000e-01 5.2000000e-01 5.6000000e-01 4.7000000e-01 4.4000000e-01 5.1000000e-01 5.7000000e-01 4.5000000e-01 5.7000000e-01 4.3000000e-01 5.1000000e-01 3.8000000e-01 5.3000000e-01 4.8000000e-01 4.4000000e-01 5.0000000e-01 4.8000000e-01 5.0000000e-01 4.7000000e-01 6.4000000e-01 4.9000000e-01 5.2000000e-01 4.8000000e-01 5.6000000e-01 4.3000000e-01 4.8000000e-01 4.7000000e-01 6.0000000e-01 5.4000000e-01 5.5000000e-01 4.0000000e-01 5.5000000e-01 5.6000000e-01 4.9000000e-01 5.0000000e-01 4.3000000e-01 5.7000000e-01 5.0000000e-01 5.7000000e-01 4.9000000e-01 4.2000000e-01 3.9000000e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jaccard-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jaccard-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..a7570d8c3fbdf63bb2240c964941d9e48bc2ad3c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jaccard-ml.txt @@ -0,0 +1 @@ + 6.5714286e-01 6.0563380e-01 6.3235294e-01 7.3972603e-01 6.0294118e-01 7.3611111e-01 6.4179104e-01 7.7631579e-01 6.4000000e-01 6.6197183e-01 6.6666667e-01 7.0000000e-01 6.4285714e-01 7.7464789e-01 7.1621622e-01 6.4285714e-01 6.8571429e-01 6.4383562e-01 6.6666667e-01 6.5384615e-01 6.6216216e-01 6.1971831e-01 6.5333333e-01 6.5277778e-01 6.7123288e-01 6.4383562e-01 6.5000000e-01 6.3513514e-01 6.0000000e-01 6.7123288e-01 6.3513514e-01 7.4324324e-01 5.5714286e-01 7.0512821e-01 6.3888889e-01 6.0000000e-01 5.6338028e-01 6.3157895e-01 6.0810811e-01 6.2337662e-01 6.4000000e-01 6.5789474e-01 6.3157895e-01 5.6962025e-01 7.5294118e-01 7.1250000e-01 6.2162162e-01 6.7500000e-01 7.2727273e-01 6.2337662e-01 6.2337662e-01 6.7948718e-01 6.5853659e-01 6.6250000e-01 6.3380282e-01 7.3417722e-01 6.0869565e-01 7.2000000e-01 7.5949367e-01 6.4556962e-01 6.3013699e-01 5.9420290e-01 6.2857143e-01 7.1794872e-01 7.3972603e-01 6.4864865e-01 6.4864865e-01 6.8918919e-01 6.6666667e-01 7.0512821e-01 6.2500000e-01 6.2318841e-01 6.6197183e-01 6.5277778e-01 6.9135802e-01 6.6216216e-01 6.6666667e-01 6.4285714e-01 6.6216216e-01 6.8115942e-01 6.2500000e-01 6.2500000e-01 7.3684211e-01 6.4473684e-01 7.3417722e-01 7.1052632e-01 6.3888889e-01 7.3417722e-01 6.5432099e-01 6.9230769e-01 7.1428571e-01 6.7567568e-01 6.7532468e-01 6.7605634e-01 6.5789474e-01 5.4285714e-01 6.9736842e-01 6.2337662e-01 6.6233766e-01 6.7605634e-01 7.0270270e-01 6.1842105e-01 6.7567568e-01 6.2318841e-01 6.7605634e-01 6.9333333e-01 7.1428571e-01 6.0000000e-01 6.0000000e-01 6.6197183e-01 6.9230769e-01 6.8000000e-01 7.2000000e-01 6.5384615e-01 6.5753425e-01 6.6197183e-01 7.1232877e-01 6.9333333e-01 7.5000000e-01 7.1052632e-01 6.7567568e-01 6.4285714e-01 6.0273973e-01 5.8571429e-01 6.9512195e-01 6.3013699e-01 6.8918919e-01 7.0270270e-01 6.6666667e-01 6.8571429e-01 6.6666667e-01 6.1111111e-01 7.0666667e-01 6.6666667e-01 6.5333333e-01 6.8674699e-01 7.0731707e-01 6.3636364e-01 6.3750000e-01 6.1643836e-01 6.5432099e-01 5.8441558e-01 5.8666667e-01 4.7297297e-01 5.5263158e-01 6.9736842e-01 6.9333333e-01 6.5789474e-01 5.7575758e-01 6.7532468e-01 7.0886076e-01 6.4383562e-01 5.8666667e-01 6.6233766e-01 7.5000000e-01 6.2500000e-01 7.7027027e-01 6.0563380e-01 6.8000000e-01 5.6716418e-01 6.7948718e-01 6.4864865e-01 6.1971831e-01 7.1428571e-01 6.5753425e-01 6.7567568e-01 6.6197183e-01 7.7108434e-01 6.6216216e-01 7.1232877e-01 6.4000000e-01 7.0886076e-01 6.0563380e-01 6.2337662e-01 6.2666667e-01 7.7922078e-01 7.2972973e-01 7.5342466e-01 5.7971014e-01 7.3333333e-01 7.0886076e-01 6.6216216e-01 6.4102564e-01 5.8904110e-01 7.3076923e-01 6.4102564e-01 7.1250000e-01 6.4473684e-01 5.9154930e-01 5.3424658e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..da698cf511e6983e1db1bbed6e2974f3480a6903 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt @@ -0,0 +1 @@ +0.0211635609063 0.00455072769716 0.0230610904531 0.0076674324982 0.037571216894 0.029561354778 0.0115281186735 0.0225809070507 0.0346801442638 0.00321049176948 0.0232839774828 0.0321124517082 0.0244179197971 0.0331466156799 0.0373949302575 0.0411984503375 0.0218945865519 0.0198268474453 0.02395616278 0.0254898420418 0.0394901943037 0.0396613298853 0.0621120626322 0.0458316817045 0.0334832834948 0.0445794256135 0.00775602650151 0.00770188279153 0.0277044809709 0.0292851269343 0.0408206135002 0.0384837880405 0.0229145225178 0.0346801442638 0.017155900548 0.0186865079856 0.0346801442638 0.011778601551 0.0109570078484 0.0244803928224 0.0609237090857 0.0125274661674 0.0713079215249 0.05072181848 0.0329702193648 0.018118079192 0.0129441131729 0.00461235131171 0.0077204189151 0.190781073622 0.196235539354 0.202381067744 0.215567934651 0.208951210729 0.208395222197 0.202977772245 0.185995150024 0.197488445796 0.207754344906 0.207231776416 0.200895721783 0.202370257926 0.208839694602 0.184151316466 0.18979501874 0.211864590783 0.187854399445 0.231134943838 0.195836809821 0.220082308081 0.191451897661 0.227329222402 0.204586412278 0.192443163994 0.193684928543 0.206841791869 0.21612354251 0.209268704489 0.175345617131 0.197941218478 0.190625337098 0.191065854858 0.2317867516 0.214902945463 0.200203629275 0.200116177839 0.216845735492 0.194043462589 0.208271280018 0.210985756639 0.203659220099 0.197020978632 0.188475437149 0.205376756706 0.190989482965 0.1980172695 0.194499957306 0.176357663314 0.198492265793 0.255232551639 0.244270007892 0.240469208946 0.23837051036 0.248414598574 0.246817944636 0.244919131711 0.239985639856 0.249741998486 0.238825041235 0.224881988223 0.240479022193 0.236919216915 0.253134884385 0.257281336145 0.239143010746 0.231252341207 0.230464321684 0.26688757037 0.243702807017 0.239741931201 0.243722504108 0.250777226737 0.230838149152 0.233779458319 0.227365052742 0.226996712309 0.224529623997 0.248876761269 0.223504306637 0.241222705588 0.218359591571 0.251783757155 0.22178058433 0.237922879169 0.243259140467 0.243757719218 0.229784431814 0.223834512884 0.231417699032 0.245855002542 0.23260880661 0.244270007892 0.24412721812 0.245364556958 0.239069534798 0.242476824971 0.231950344971 0.238821966724 0.23119389821 0.0191037071088 0.0192689125135 0.0279599225292 0.0400056720024 0.0369973592153 0.0168401225107 0.0156647141811 0.0317569365649 0.0209196472407 0.0261095572505 0.0295911292198 0.0387034711929 0.047943015361 0.0502801086558 0.0505076233319 0.0276985510653 0.0178344200842 0.0354647529806 0.0132311775272 0.0439058315568 0.0587698655317 0.0548749800113 0.0420211683289 0.0158475875817 0.0410205075223 0.0159821398213 0.0151652303532 0.0238719428054 0.0190485214781 0.0351129659726 0.0545720530766 0.0439213935019 0.0317569365649 0.0241815879409 0.0259117209292 0.0317569365649 0.0173211662353 0.0138864344566 0.0338981856531 0.0429537854169 0.027658485714 0.0691172321094 0.0500059403041 0.0236011037029 0.0316644090037 0.0201837094834 0.0228863104939 0.0134861623597 0.176517909274 0.183281450302 0.188265462996 0.20119658342 0.194697326208 0.195209942657 0.190674274015 0.172425697578 0.182894065786 0.195529592383 0.192026007893 0.188346396823 0.186051479032 0.19538200246 0.171605310091 0.175766658214 0.199864266754 0.173408334646 0.215875435175 0.181358449055 0.208526316653 0.177419239992 0.212710197663 0.190468934035 0.178153770213 0.179556589614 0.19192211318 0.202468559318 0.196181327665 0.160572766746 0.183327527516 0.175701303909 0.177066663037 0.218355819513 0.203310616701 0.188843124957 0.186323930321 0.201194011774 0.18173319992 0.194510318604 0.197386404553 0.190483994401 0.182718294182 0.174255518011 0.19205782762 0.178239088983 0.18513760318 0.180597338326 0.163063102971 0.185284857996 0.243887274146 0.231624187925 0.22689829944 0.225289762668 0.235796354542 0.23269059518 0.23306609907 0.225602345189 0.235244584021 0.226892475392 0.212543999067 0.226908361887 0.223757309255 0.240296170501 0.245569279789 0.227339668618 0.2180947426 0.217889650333 0.252259964829 0.228879839341 0.227150610245 0.231836962279 0.236076260811 0.217244760533 0.221438124324 0.213671912157 0.213819376581 0.212040793299 0.235827974309 0.209053467969 0.226615210725 0.205271111175 0.238869689413 0.207939309627 0.223898822147 0.229226063436 0.232630777303 0.217026651272 0.211532427176 0.218347693285 0.233460132247 0.219891922362 0.231624187925 0.231652249658 0.233525349836 0.226377769419 0.228579062405 0.219098380821 0.227789794229 0.219023772057 0.0212386043587 0.00993825296406 0.0336976816686 0.0262325070397 0.0107568964379 0.0200399473123 0.0369530877134 0.00706121377315 0.0225180018442 0.0345620434634 0.0288649599558 0.0352792061476 0.0352526366714 0.0385518621347 0.0178346265809 0.0156087458393 0.0212893461987 0.0244762891905 0.0356848493955 0.0412118431681 0.0577499958146 0.0449060313177 0.0316682156452 0.0402876009485 0.00791017688182 0.00777689270989 0.0262859463939 0.0275691765497 0.0363841779992 0.0426229028778 0.0258430808451 0.0369530877134 0.0171494695134 0.0197038085728 0.0369530877134 0.00856221073911 0.010146504576 0.0213442662268 0.0572142712511 0.0116574543859 0.0671597340405 0.047388301579 0.0285557929122 0.0192111185981 0.0112749020632 0.00781127840012 0.00637566729419 0.187210918471 0.192623672543 0.198833727144 0.212008654042 0.205367416257 0.204958566039 0.199402528264 0.182409867249 0.193987091024 0.204155295526 0.20374075502 0.197257634781 0.198980554413 0.205364330922 0.180426094029 0.186163209648 0.208359044162 0.184479344493 0.227592539467 0.192334488284 0.216529219774 0.187806950452 0.223855994439 0.201226354807 0.188864300604 0.190066520684 0.203335651256 0.212548662023 0.205705049083 0.171742464799 0.194417377469 0.187136601809 0.187472552212 0.228344421279 0.211429948522 0.196622478767 0.196530972542 0.21335054643 0.190497911091 0.204702393397 0.207598118603 0.200155023454 0.193467129006 0.184880183147 0.201857382862 0.187517863683 0.194486736804 0.1909406597 0.172553592377 0.194927231832 0.251743344729 0.240762357546 0.236947962879 0.234942968898 0.244909063494 0.243388177909 0.24143957892 0.236617606758 0.24633077256 0.235257992461 0.221275459366 0.23695229759 0.23334624948 0.249598367646 0.253700089415 0.235542233543 0.227782636512 0.227013807453 0.263469639225 0.240293112196 0.236160757703 0.240179126335 0.247395319298 0.227257451928 0.23025606552 0.223937789122 0.223399862869 0.220967129276 0.245363860699 0.220116506797 0.237779687596 0.214888164759 0.248252329967 0.218347705592 0.234681014401 0.239690494574 0.240217402996 0.22632629355 0.220260809644 0.227813787503 0.242262375922 0.228918020841 0.240762357546 0.240592325032 0.241778783496 0.235418026861 0.23890352284 0.228365058816 0.235271978928 0.227703766766 0.0251699105319 0.0326109277538 0.029278801732 0.0117605763436 0.00495713045566 0.032971458625 0.022273270218 0.00845866705418 0.0338325850006 0.0418060793613 0.0560571569439 0.0475989809058 0.0526432986362 0.0287512950603 0.0156470261333 0.0286833299321 0.0125318521449 0.039628171731 0.0605879245284 0.051592881751 0.0247889558 0.0185457610439 0.0360813596602 0.0162897964903 0.0233731835598 0.00555113851114 0.00851608036789 0.039350861172 0.0494281040416 0.0428445793936 0.032971458625 0.0361084743768 0.0382083791018 0.032971458625 0.0135564720949 0.0133460812565 0.0359059029584 0.0525998249753 0.020448013216 0.0653401649712 0.0366222774812 0.0276779812669 0.0199994530533 0.0115734561992 0.0214333278479 0.0186461579961 0.171344593808 0.177166238774 0.182950771401 0.19643196185 0.18986301898 0.188443393542 0.18388535541 0.166562175459 0.177783941311 0.188923487579 0.187724110496 0.182156573883 0.18272910062 0.189078094755 0.165787292858 0.170722554665 0.192539814536 0.167433166664 0.21234151671 0.17610118578 0.201339136255 0.172496446246 0.207869320447 0.184234771078 0.173084677744 0.174586008872 0.187343267555 0.197067369338 0.190065322687 0.155992913972 0.178370192697 0.170834599332 0.171756811444 0.21215391799 0.195543956872 0.181382419503 0.180878423055 0.197648250997 0.174630568935 0.189049602981 0.190809942592 0.184007315234 0.177572802284 0.169158986096 0.1858235382 0.171054750208 0.178472858167 0.175024341592 0.15843173338 0.179125844986 0.236753289776 0.225336913605 0.221457751336 0.21877528886 0.229546376269 0.227338714151 0.226000082599 0.220106179064 0.230241030133 0.220420771483 0.206398010187 0.221506177578 0.218254053746 0.234550382603 0.23950257292 0.221077875617 0.211789394262 0.210958467875 0.247726054061 0.224160699687 0.221293898641 0.225242596712 0.231164838975 0.212084684776 0.21483758197 0.207582855086 0.208324546741 0.205668770285 0.22999948579 0.203519866038 0.221791807814 0.198729512666 0.233119883684 0.201955632148 0.21744774227 0.224658382044 0.22554531899 0.210282327898 0.205086194314 0.212901784527 0.227685054369 0.214992030153 0.225336913605 0.225426421062 0.227294020009 0.22122180599 0.223891772502 0.213328572484 0.220627825622 0.212027719306 0.036692370716 0.0274221847228 0.014614486903 0.0257905543192 0.038185072385 0.00854173398239 0.0228846598272 0.0362880440778 0.0247966202157 0.0338575762449 0.0341810302892 0.0403839419732 0.023192138006 0.0228332335778 0.0207802721216 0.030246984472 0.0385095591836 0.036116458124 0.063670015249 0.0456331337141 0.0387265099713 0.0454622939774 0.0128829753328 0.0153687384621 0.0290580106985 0.032571197138 0.0439484983687 0.0345946330706 0.0178789804494 0.038185072385 0.0229066226947 0.0243703411158 0.038185072385 0.0145921399018 0.0157758022112 0.0246899780508 0.0667942861058 0.00859371091119 0.0713780561444 0.0493343894065 0.0370078971529 0.0136450272229 0.0138896306008 0.00617904291603 0.0146027872105 0.19304474601 0.19797909852 0.204564547012 0.217893788171 0.211240589753 0.210085035577 0.204424468033 0.187966151726 0.199846283114 0.209201380502 0.209876418912 0.202509063074 0.205495616388 0.210676243708 0.185852822947 0.192014733821 0.213129345848 0.190059083655 0.233894763867 0.198141527364 0.221234996275 0.193689434624 0.229701750232 0.206631480423 0.194727732257 0.195938025333 0.209362001884 0.218147207364 0.211029564766 0.177869147322 0.200327033561 0.193124484677 0.193233901741 0.233604044389 0.215980508747 0.201289146742 0.20219168761 0.219726845187 0.195458087393 0.2103246028 0.212820091698 0.205396680761 0.199290845186 0.190742394929 0.207189399227 0.192521418757 0.199651552643 0.196594932336 0.178468769167 0.200295584792 0.256297768876 0.245841451519 0.242438509968 0.240033073248 0.249980802879 0.248953385462 0.246144223104 0.242178797272 0.252036489845 0.240176361939 0.226412773569 0.242454491215 0.238768549206 0.254841315698 0.258625650226 0.240494667361 0.232976534038 0.231927166971 0.269283834821 0.246140263748 0.241374087503 0.245035624054 0.253144665918 0.232868387014 0.235226383286 0.229279397026 0.228861516729 0.226064745168 0.250630665997 0.225726655668 0.243583203961 0.220042394187 0.253511231548 0.2237596548 0.239872842457 0.245497904196 0.24478307529 0.231328796826 0.225307213023 0.233256809435 0.247442229489 0.234444929147 0.245841451519 0.245661961251 0.246724980668 0.240848644418 0.244659004118 0.233675626419 0.239814102397 0.232532592403 0.010524058973 0.0347497758249 0.0310026365608 0.0636683508445 0.039628310023 0.0339732471818 0.0632599742009 0.0607622080336 0.0610552648732 0.0274939888458 0.0321794296029 0.0203038890155 0.0225566538528 0.0181521200419 0.0426282898435 0.00971744861179 0.0573638408809 0.0304112802741 0.0463628234513 0.044509344874 0.0125060810098 0.0372012029006 0.0399195029174 0.0351572922113 0.0381714857339 0.0236066809352 0.0675880085279 0.0489234884353 0.0636683508445 0.0451372647506 0.0495218266562 0.0636683508445 0.028718795999 0.0363358460459 0.0247409309981 0.0536494939568 0.0291783067693 0.0362475310016 0.0206193569389 0.0238060051702 0.0365127412376 0.0303247636874 0.0382706081182 0.036679138207 0.165252454208 0.168745392776 0.176697416201 0.190075579294 0.183193768644 0.182210296577 0.174895117306 0.159516185843 0.172780618769 0.179424300801 0.183362052205 0.17275366841 0.180884475296 0.18272692365 0.155645508044 0.163596126812 0.183814684533 0.163944686708 0.206939183794 0.170981324059 0.191238016882 0.165171506195 0.202635613449 0.180157192696 0.16688029242 0.167662828735 0.182473473654 0.189606814944 0.182144568907 0.150494922446 0.17311022882 0.166528964674 0.165043072843 0.205731384902 0.18659197245 0.17105591364 0.173799739615 0.193545885155 0.166211621507 0.181959703917 0.185636136233 0.177047042786 0.171588556623 0.162758165885 0.178831153511 0.164197394115 0.170920859204 0.168528635782 0.148460744764 0.171573948268 0.226522867074 0.216861787556 0.214094925757 0.211961277469 0.220987866614 0.22168305754 0.216782699249 0.215609692292 0.225208444508 0.210312198283 0.19661028383 0.214074476024 0.209773195915 0.225810880474 0.228551359916 0.210341321513 0.204691741777 0.203336697222 0.242485382573 0.21963599647 0.211892193047 0.215282949931 0.226705435666 0.20418234927 0.205942096193 0.201754356567 0.199749686219 0.196656401255 0.221913123953 0.199165134473 0.216656797893 0.191759529503 0.224569670715 0.196349964299 0.213992874847 0.217230566883 0.214548114667 0.2028246956 0.195690210734 0.20400781198 0.217750478797 0.204445613083 0.216861787556 0.216379424168 0.21668692278 0.211041069144 0.216249554038 0.204373397921 0.209459569208 0.203359671893 0.0284940982012 0.0283835072119 0.0582564069663 0.0317209110303 0.0290062929149 0.0575581968066 0.0518675712793 0.0525497818792 0.0214648817816 0.0289820919427 0.0151981567712 0.0194868108708 0.00772151270155 0.0396885548083 0.0133170749495 0.0480498930165 0.0406834920646 0.0454173876936 0.04374952779 0.0223043370554 0.0304932603619 0.0333669476098 0.0323541790178 0.036462611527 0.0283937271683 0.0581656862224 0.0386849486073 0.0582564069663 0.0386824403573 0.0427767064205 0.0582564069663 0.0225670168648 0.0303996112373 0.0186317385432 0.0589532282724 0.020129496567 0.0456219811369 0.0271705146906 0.0266114081755 0.0281840659315 0.0237276312035 0.0300017258072 0.0304084467621 0.174689674341 0.17830904459 0.186122916272 0.199541551099 0.192698821274 0.191439303409 0.184399484383 0.168993025372 0.182083678728 0.188980455315 0.192641208359 0.182376656778 0.189869605957 0.192034662906 0.165406381187 0.173160863593 0.193190156667 0.172952807322 0.216356837462 0.18028635421 0.200707489616 0.174765006311 0.211946253402 0.189197004074 0.176338357547 0.17720731475 0.191796191467 0.199121585044 0.191634817003 0.159923924784 0.182459504488 0.175769947329 0.174536678956 0.215011640541 0.195898920801 0.180555890973 0.183304141459 0.202828623711 0.175640593326 0.191448592698 0.194762075203 0.186406863824 0.181008054094 0.172240816201 0.188223844291 0.17346231425 0.180325575914 0.177965844821 0.158332658267 0.181046361448 0.235882031296 0.226280867303 0.2235366831 0.221221709251 0.230404936567 0.23094802366 0.226136073658 0.224736021115 0.234431734165 0.219824894986 0.20619515856 0.223526372128 0.219308937798 0.235287233784 0.23807753807 0.219908336162 0.214027462834 0.212631745032 0.251732773763 0.228838671706 0.221440978506 0.224753704594 0.235866097652 0.213724598783 0.215380278296 0.210994508636 0.209321687173 0.206162323946 0.231346876087 0.208300913946 0.225928825898 0.201080283138 0.234037288778 0.20558958438 0.222856381147 0.226751911512 0.223989137906 0.212135251626 0.205214537461 0.213595258296 0.2273163389 0.21417146415 0.226280867303 0.225846388312 0.226229092302 0.220705781832 0.225781259333 0.213928177909 0.218913775078 0.212729498479 0.0122225873329 0.0303322491715 0.0105162282326 0.0130161869455 0.0294681125528 0.031169289575 0.0445674903412 0.0424428041907 0.0471592401326 0.0242919299586 0.0151911030837 0.0250458377851 0.0159588466859 0.0393890228389 0.0501712969027 0.0573610210315 0.0347635318983 0.0244162409612 0.040396894165 0.00489128055225 0.0130158047868 0.0162739668734 0.0181749821427 0.0399100872217 0.0416234365466 0.032217222914 0.0303322491715 0.0260818708259 0.0277812891175 0.0303322491715 0.0068887146948 0.00350057866506 0.0298024545939 0.056647752597 0.0131029257559 0.0689399130874 0.0438694576111 0.0295733202179 0.0148293487362 0.005187559828 0.00994170320116 0.00857601759458 0.181761248694 0.187453410696 0.193365623784 0.206702523841 0.20011934013 0.199105672278 0.194191029325 0.176999836949 0.188302627791 0.199111466491 0.198126208998 0.192301121465 0.193121251528 0.199652739338 0.175765384319 0.180985589904 0.202947496477 0.178262931918 0.22241465847 0.18663850283 0.211489015658 0.182706386145 0.218266373994 0.195039865626 0.18346554188 0.184856636434 0.197755074291 0.207324505424 0.200404409787 0.166355990223 0.188830148216 0.181378679541 0.182125858248 0.222660838403 0.205968099039 0.191576935017 0.19121911341 0.207887116649 0.185081160877 0.199380829938 0.201554837448 0.194543615891 0.187991163757 0.179524780118 0.19631300534 0.181726234556 0.188970696545 0.185468734774 0.168191837054 0.18953883133 0.246767069406 0.235545780286 0.231684267956 0.229269594484 0.239724337717 0.237749571543 0.236212316891 0.230682790909 0.240645700931 0.230405224861 0.216420479968 0.231715542723 0.228337424754 0.244594612682 0.249193956399 0.230909382279 0.222229420235 0.221427946934 0.257965809127 0.234573740528 0.231288571496 0.235259005866 0.241607850531 0.22220070955 0.225060949621 0.218149643482 0.218415896436 0.21585883122 0.24017332069 0.214149237729 0.232167950695 0.20925074028 0.243199182069 0.21253980165 0.228281003551 0.23468620257 0.235446691383 0.22074773222 0.215228869367 0.222922571895 0.237553097068 0.224606387466 0.235545780286 0.235534393045 0.23712429287 0.230940484692 0.233917063813 0.223401305554 0.230524046304 0.222353817706 0.0346270873711 0.0222428303765 0.0130402644662 0.0348466257078 0.042829178942 0.0549568638218 0.0464424131108 0.050271964255 0.0260503884653 0.0122224116333 0.0282180922789 0.011864843965 0.0376053109772 0.060340947297 0.0487164281844 0.0285013587126 0.0163929694658 0.0334500762426 0.015996373074 0.0216948948211 0.00981357617646 0.00897169179454 0.0351677992971 0.0524329239391 0.0436506734112 0.0346270873711 0.0336931600411 0.03612022228 0.0346270873711 0.0123155461924 0.0128144174355 0.033561339537 0.0480641708164 0.0212422700005 0.0628734973132 0.0366806543871 0.0231396795589 0.0230741684915 0.0123098386274 0.0219470818919 0.0170687771133 0.170097980345 0.176050748737 0.181762587161 0.195138740624 0.188553715994 0.187634353283 0.182931238354 0.165421712546 0.176600578903 0.187912731345 0.186408453678 0.181027641681 0.181322068987 0.188146844457 0.16448627936 0.169388121003 0.191757508168 0.166524283083 0.21084441712 0.17494262847 0.200480456222 0.171127562134 0.206682857942 0.183402734812 0.171811556423 0.173255302855 0.186064001811 0.195873759085 0.189008503285 0.154586199139 0.177136590503 0.16961214015 0.170511848112 0.211247624618 0.194873193684 0.18053463387 0.179673493778 0.196193226513 0.173758955601 0.187869379999 0.19001738823 0.183074417621 0.176342969787 0.167870586049 0.184832516957 0.170273119593 0.177542554096 0.173865162824 0.156824879402 0.178069468543 0.235952767235 0.224377142544 0.220334312937 0.217941307072 0.228582134377 0.226295522057 0.225204079848 0.219149120214 0.229153457047 0.219423743811 0.20530168342 0.220370010221 0.217084867796 0.233463162138 0.238411309618 0.220004052761 0.210872881275 0.210171938201 0.24654768975 0.223020715486 0.220176358035 0.224293060856 0.230089603615 0.210845620347 0.213914309618 0.206670271948 0.207126565579 0.204661896322 0.228953899388 0.202543633745 0.220637247329 0.197849243585 0.232039444595 0.201016080923 0.216786754649 0.223327252818 0.224678101179 0.209454984614 0.204079198003 0.211687536154 0.226538202286 0.213579234366 0.224377142544 0.224425108032 0.226231350025 0.219899730556 0.222577135927 0.212192019374 0.21976273048 0.211209202307 0.0316629149872 0.033505533415 0.00599304401229 0.03147088677 0.0569379974042 0.0709823822809 0.0748230813085 0.0531369685873 0.043263968218 0.0546416139704 0.0249055105449 0.0688386171987 0.0687094353901 0.0814189949505 0.0424145094995 0.0312264925083 0.0671690041442 0.0297074253675 0.0326159908833 0.0331671559449 0.0312681130066 0.0653641254742 0.040141041955 0.0490986378456 0.0 0.0426893927085 0.0408370899258 0.0 0.0368318621027 0.029200211213 0.0578784327643 0.069522041979 0.0411420259607 0.0955605905857 0.0684916485028 0.0536985636371 0.0361283833003 0.0349236407736 0.0326544016244 0.0320790517512 0.190734279342 0.198011093099 0.202165335147 0.215193803756 0.208989236133 0.207943677959 0.205067684162 0.186855165408 0.196324830352 0.210203923195 0.205342231011 0.203427359944 0.198526873832 0.208498767129 0.187766259392 0.1906844311 0.213518747065 0.185598548292 0.229746939288 0.194805777978 0.222821572969 0.192481412892 0.225723793976 0.202375384198 0.192457483521 0.194300696695 0.205376522136 0.216660688755 0.21030774241 0.175307222553 0.197000980201 0.189066511629 0.191539232379 0.231058906578 0.21669613367 0.203523564636 0.200666530244 0.214595210264 0.195827171333 0.208641958794 0.209561099462 0.203960485003 0.196717491572 0.188766309975 0.205678014795 0.191492051166 0.198961164508 0.194672996958 0.180152450124 0.19944864987 0.257583228235 0.245178524187 0.240481186058 0.23786630337 0.249341975217 0.245210894146 0.246468005973 0.237456371003 0.247563146561 0.241300303041 0.22729752986 0.240555698528 0.237993038095 0.254193700204 0.26033321069 0.24220764546 0.231128442611 0.230799434891 0.264694157473 0.241194058993 0.241588310222 0.245985985156 0.248083056968 0.231516408669 0.235215479132 0.226198260689 0.228320459149 0.22623565919 0.249391170367 0.221152204365 0.239284874476 0.218351963925 0.252686589685 0.220530747351 0.234477098822 0.243363395844 0.246969168578 0.229990071021 0.225895881072 0.232959826282 0.248113114083 0.23570223574 0.245178524187 0.245562733947 0.248226439579 0.241702239505 0.242775171886 0.233515238499 0.242256602421 0.232453320509 0.0221776604768 0.0292076409984 0.0225953199419 0.0344078822964 0.0402556862186 0.0443490345462 0.0247422055695 0.0212582761966 0.0262237412261 0.0239779350196 0.0420497572884 0.0414593565817 0.0638943455501 0.0444256278823 0.0324613404347 0.0464717210574 0.0064847144584 0.00810593532016 0.026613709743 0.0281809240896 0.0431707040779 0.0364933096346 0.0236549105265 0.0316629149872 0.0188389154871 0.0197111271958 0.0316629149872 0.0126413659503 0.00989228745945 0.0276229976961 0.0619054176788 0.0136582341261 0.0735855911944 0.0517713161376 0.0346942423053 0.0171636471405 0.0128780143687 0.00330427938484 0.00787705209324 0.191124461218 0.196709270957 0.202716444922 0.215924187936 0.209335641351 0.208652638742 0.203456445375 0.186385899088 0.197740844714 0.208277146555 0.207460837635 0.201437849638 0.202451222155 0.209128071387 0.184787202976 0.190226937612 0.212288039878 0.187958804478 0.231460569504 0.196093364986 0.220613648389 0.191903262961 0.227580912528 0.204703238288 0.192798319471 0.194099784682 0.207098250381 0.21653588119 0.209689452484 0.175692486622 0.198220686642 0.190843207535 0.19145100341 0.232063526554 0.215320322312 0.200757345911 0.200512766736 0.217080327154 0.194471953875 0.2086592247 0.211165769014 0.203992872465 0.197349559187 0.188849659131 0.205722712353 0.191296031353 0.198394567468 0.194851113014 0.177048060287 0.198893335106 0.255751598489 0.244696550693 0.240850735029 0.238657915403 0.248846650839 0.247061872969 0.245367004129 0.240135191988 0.249950401792 0.239377278919 0.225435027987 0.240867925442 0.23738991955 0.253602763959 0.257909304325 0.239755581779 0.231578379023 0.230802663983 0.267123052444 0.243889033036 0.240266927764 0.244253437447 0.250943098487 0.231284290644 0.234232349856 0.227603903464 0.227483301065 0.225010355342 0.249291083828 0.223656027478 0.241455021635 0.21867583866 0.252237135554 0.222012069338 0.237944922778 0.2436864771 0.244348021388 0.230119265967 0.224342678419 0.231928535106 0.246422479994 0.233277995952 0.244696550693 0.244602643666 0.245964063641 0.239692017082 0.242916090829 0.232449793011 0.23942553099 0.231611684555 0.0349328180738 0.0397488289561 0.055917859907 0.0466657680267 0.0537525131124 0.0312322620487 0.0204661899168 0.0273925121636 0.0188335643607 0.0409848131484 0.0582072876054 0.056071962696 0.0229170555223 0.0261517042618 0.0397135231185 0.0177392332242 0.0260266328079 0.00803879586998 0.0153514357558 0.0448019039458 0.0436873202641 0.0393784019825 0.033505533415 0.038918396453 0.040710313775 0.033505533415 0.0158373818638 0.0159973219202 0.0374102115892 0.0608056403948 0.0178404535904 0.0681680501025 0.0368277650919 0.0342153028534 0.0136005609896 0.0114725513671 0.0202654226306 0.0215111320227 0.175502940168 0.180846739943 0.186993890383 0.200622200655 0.194047326964 0.191871659177 0.187230724864 0.170446969951 0.181958118895 0.192296146604 0.192168805538 0.185737701873 0.187567790504 0.1926934047 0.169562979332 0.174903941965 0.195627236518 0.171332536122 0.216941277953 0.180224282629 0.204375817171 0.176708588212 0.212007481629 0.187940100384 0.177271729963 0.178782860675 0.191678059195 0.200974345147 0.193702992688 0.160469688943 0.182595805427 0.175146841545 0.17584337797 0.215706304999 0.198413689274 0.184386485081 0.184856323095 0.20232915317 0.177913816132 0.192982627694 0.194328346433 0.187558388958 0.181714052911 0.173349805963 0.189464804214 0.174374123803 0.181953747446 0.178997842818 0.162709307879 0.18281187754 0.239644460978 0.228724428122 0.225246554022 0.222159074766 0.232927392687 0.231188577573 0.229024014707 0.22394884582 0.234228075319 0.223668927512 0.209861178703 0.225307285274 0.221985913786 0.238104571085 0.242774657148 0.224369518131 0.215280858443 0.214174032708 0.25182022511 0.228290074367 0.224826938646 0.228427350702 0.235193041024 0.216001866886 0.218122104545 0.211215702011 0.212099059638 0.209083204086 0.233569980649 0.207419375116 0.225880589953 0.202184861075 0.236687676647 0.205660738951 0.22091809464 0.228766193737 0.228457617824 0.2135856342 0.208453819381 0.216659972673 0.231192094033 0.218856194539 0.228724428122 0.228811270004 0.230578167797 0.224983578773 0.22794936407 0.216954366886 0.223518672063 0.215168993038 0.0278789967436 0.0521696459286 0.0689795584164 0.0720246274977 0.0510464689946 0.0421745394626 0.053582971262 0.0252834975098 0.0677158937278 0.0652657188093 0.081318384881 0.0463957610979 0.0317140261778 0.0668416555065 0.0279143378417 0.0293650428541 0.0349789935753 0.032697569141 0.0634994948437 0.0398508689254 0.0464805914619 0.00599304401229 0.0382168939676 0.0359888514026 0.00599304401229 0.0356302780251 0.0278103138868 0.0553135387293 0.0681148770417 0.0401494228711 0.0950529579724 0.0698373790182 0.0522664102967 0.0364245082526 0.034367526255 0.0307005764611 0.0296340954647 0.193320448722 0.200616479264 0.204805927895 0.217738543992 0.211505662739 0.210916031972 0.207785188245 0.189492055249 0.199011305592 0.212849559963 0.207930120156 0.205973985295 0.201080290256 0.211364107387 0.190111543422 0.193149888648 0.216396870129 0.188564248953 0.232124397575 0.197509520178 0.225563508571 0.194908404083 0.228408782968 0.205404773753 0.195016829794 0.196780548006 0.207983584103 0.219251719916 0.21299620972 0.177753842585 0.19963357851 0.19173429638 0.194103048534 0.233951642516 0.219652294406 0.206249008964 0.203257559758 0.217055047586 0.19863330849 0.211258733702 0.212590735452 0.206794766477 0.199327419787 0.191301214309 0.208462930841 0.194441169877 0.201765843334 0.197324504463 0.182225897174 0.202147597456 0.260363424846 0.247928312505 0.243139798114 0.240805098305 0.252082485905 0.248010938223 0.249316339351 0.240374954583 0.250342905364 0.243926059402 0.229857687769 0.243199557315 0.240557269117 0.256813113392 0.262812568913 0.244726906585 0.233983032632 0.233739246967 0.267372287242 0.243942332388 0.244149194945 0.248660562989 0.250891907974 0.234045856002 0.23797517084 0.229108542601 0.230855414047 0.228916694642 0.252083674667 0.224056856634 0.242001030284 0.221239323161 0.255322806495 0.223424493307 0.237685672547 0.245823282675 0.249646734812 0.232903558602 0.228555612396 0.235462898266 0.250614129287 0.237926457957 0.247928312505 0.248245352669 0.250761236826 0.244030479155 0.2452417146 0.236085335476 0.244925556651 0.235313123462 0.0296919493074 0.0549103775111 0.0590022859872 0.0450958795243 0.0436647014416 0.0451669494409 0.0401697758406 0.0620690815544 0.0404063812339 0.0856236153109 0.0591687282212 0.0490473517415 0.0683619493373 0.0275851844543 0.0264002504943 0.0446945676427 0.0461763497794 0.0638640733472 0.0232220049486 0.0233353107646 0.03147088677 0.029201781247 0.02614478673 0.03147088677 0.0350355717384 0.030428654072 0.0452764113597 0.0802967236314 0.0328354116031 0.0941098407344 0.0724718577687 0.0562014612922 0.0313029110017 0.0340889421853 0.0234757814211 0.028925036543 0.210407140423 0.216202187825 0.221880775368 0.234954371989 0.228487320903 0.227668142527 0.22288748901 0.205844703323 0.216768667446 0.227711464843 0.226263348504 0.220970493431 0.220784685064 0.228189601628 0.204608420295 0.209669658318 0.231537789973 0.206793805641 0.250139733355 0.215157813925 0.239903556316 0.211352210266 0.246303099122 0.223431243026 0.212079033799 0.213468334673 0.225996065584 0.235701634712 0.228967589943 0.195079844754 0.217288259849 0.209835756349 0.210818201018 0.25087781482 0.234509225035 0.220284749566 0.219817558162 0.235704379779 0.213924917847 0.22786813663 0.229992970272 0.223192642095 0.216540668547 0.208207378637 0.224921456698 0.210576921517 0.217728441709 0.214138769918 0.196963968339 0.21825274764 0.274633184392 0.263620373951 0.259724001387 0.257434131046 0.267733388911 0.265627371082 0.264299751314 0.258574475793 0.268388644672 0.258492710734 0.244710620006 0.259750243754 0.256426981572 0.272472244255 0.27686351493 0.258914849135 0.250487159092 0.249742739323 0.285410306304 0.262307563551 0.259345413586 0.26329314732 0.269271128247 0.250334873532 0.253300294048 0.246383898221 0.246645581278 0.244211963707 0.268141898451 0.242252606427 0.260005586654 0.237699783889 0.271104326635 0.240825446457 0.256147548483 0.26253800174 0.263413026859 0.249061635853 0.243583443282 0.251070491042 0.265469741093 0.25252673858 0.263620373951 0.263585528386 0.265041995624 0.258842865142 0.261802853748 0.251583944502 0.25855292944 0.250657339544 0.0450102465608 0.0439198537957 0.0420878714528 0.0489743500788 0.0458798154287 0.0561577170329 0.0578224624858 0.0224176386409 0.0849412968827 0.0784422731853 0.0630254855321 0.0686145922194 0.0402128784166 0.0342259139129 0.0607491308771 0.0616063147121 0.059057582132 0.0469260494382 0.0248556117621 0.0569379974042 0.0246670677105 0.0228986845775 0.0569379974042 0.0437638412118 0.0433305015411 0.0375189879884 0.0808012493057 0.0411311531308 0.089015155435 0.0787045494767 0.0568803303557 0.0468151856286 0.0457099341419 0.0358166687321 0.0382966404493 0.218181156993 0.223322073096 0.229705702275 0.242392790988 0.23577882008 0.236467641295 0.230139466345 0.213525276303 0.225126391191 0.234563959305 0.23447409507 0.227593305984 0.22968375989 0.236629811874 0.210530415308 0.216784944512 0.23930372472 0.216469414915 0.257297585894 0.22354090813 0.246798396633 0.218282233397 0.254457430072 0.233053108231 0.219729861221 0.220661425884 0.234112165808 0.242944509178 0.236406584132 0.202708452811 0.225406734885 0.218378296681 0.218342011887 0.259279663879 0.242430236301 0.227193609953 0.227261836804 0.24357213448 0.22167035443 0.235341335514 0.239265397892 0.231386700827 0.224381906232 0.215751708357 0.232947742221 0.219218926008 0.225693206986 0.221969156729 0.202171293512 0.225894654473 0.281426284779 0.270908280604 0.267090828143 0.26583132699 0.274949583689 0.273930107263 0.271660675229 0.267616788632 0.276815360824 0.264923167376 0.251110814116 0.267050687079 0.263175899022 0.279239724537 0.282486166675 0.264820551157 0.258570659963 0.257930961428 0.293412787858 0.270816835713 0.265787467516 0.269896000193 0.277962792777 0.257183140616 0.260538043514 0.255036127386 0.253324298915 0.251219664681 0.275354649609 0.251349757403 0.268235077166 0.245978035775 0.277988121804 0.249516964722 0.266387651101 0.269264891567 0.269706692396 0.257221982881 0.25041421232 0.257531332229 0.271532129624 0.257608408712 0.270908280604 0.270491687514 0.271003440471 0.264326332823 0.268486578685 0.258229704408 0.264793848318 0.258283260113 0.0140423845301 0.0233496953457 0.0357817640834 0.0194594148752 0.0561842963236 0.0207161808072 0.0344686045315 0.0532665757163 0.0656105748711 0.0611304155975 0.0376479555399 0.0422417749013 0.0418045042693 0.0514369987371 0.0551924216967 0.0370785412495 0.0618826263881 0.03552992519 0.0709823822809 0.0406476055096 0.0445130802003 0.0709823822809 0.0367370595656 0.0436433053629 0.0176515558758 0.0714383400943 0.0313405988749 0.050902359898 0.0456873150128 0.0406736463161 0.0404939048403 0.0386201712918 0.0390017962403 0.0411563720519 0.190383467974 0.193150151335 0.201647820189 0.214732592431 0.207848794505 0.207203203998 0.199017728171 0.184517441065 0.198117570692 0.203251095462 0.208611510757 0.196706601418 0.206520396425 0.207659337014 0.179547279859 0.188402996857 0.207901541524 0.18984126915 0.231436366396 0.196328732424 0.214604717337 0.189868309751 0.227504737805 0.2057488472 0.191940214233 0.192467873609 0.207646738607 0.21397919575 0.206530920079 0.175883310987 0.198349858418 0.192082967323 0.190004696209 0.230325216181 0.21052421867 0.194679583814 0.198537353627 0.218586319181 0.19070413186 0.206580728089 0.210890308648 0.201869808993 0.196665077614 0.187817228574 0.203604184545 0.189253631177 0.195640755776 0.193545341254 0.172360847921 0.196255157011 0.249220192749 0.240515221189 0.238164043403 0.236348677214 0.244540315717 0.246252369823 0.240132469075 0.240621741867 0.249888930273 0.233326376677 0.219986372028 0.238112203328 0.233455949013 0.249164983161 0.25077275089 0.233019708703 0.229068571188 0.227556818352 0.266813634827 0.244493984417 0.235169888951 0.238293435462 0.251527743788 0.228140449415 0.22959779378 0.226587756924 0.223543468869 0.220396683922 0.245620426642 0.22441324809 0.241383050303 0.216373265669 0.24802824928 0.221314439433 0.239346728325 0.241091845042 0.237050180919 0.227111645095 0.219282181885 0.227591404442 0.240665010845 0.227138628142 0.240515221189 0.239782829874 0.239319392462 0.23385508337 0.24006053231 0.227964392353 0.231962468731 0.227162089828 0.0243523838323 0.0383436294161 0.0281380877138 0.0591143980174 0.023507742642 0.0377968944731 0.0517794728194 0.0731209286425 0.0626942772583 0.0386453724111 0.0461236944312 0.0433886463071 0.057222198299 0.0591551673076 0.0319956615202 0.0701506857494 0.0422353525103 0.0748230813085 0.0387325777977 0.0428806382505 0.0748230813085 0.0409657262208 0.047385502674 0.0175421295668 0.0657295550805 0.0386908764688 0.0489968109439 0.0522686619491 0.0387311691946 0.0491554938498 0.0442532671202 0.0441014308007 0.0436449779054 0.189947462454 0.193087358051 0.201270443197 0.213957860003 0.207091706444 0.207812220084 0.199357780455 0.184445706942 0.197758177741 0.203406879958 0.207785745488 0.196588475912 0.205402521621 0.207935882191 0.179126107639 0.187763747972 0.208638518258 0.190249811145 0.22994679767 0.196055336563 0.215105618909 0.189122223025 0.226906503622 0.206245399814 0.191418910324 0.191795589288 0.206939834769 0.213506326546 0.206505332082 0.175157210084 0.197857598212 0.191658112797 0.189588115864 0.230508642351 0.211553886467 0.195374420383 0.198156887541 0.217288762795 0.191332101287 0.206198782834 0.211486527696 0.202202827305 0.19621322526 0.187298385594 0.203764973183 0.190101114406 0.196046751208 0.193325009164 0.171164264517 0.19631141813 0.249660253112 0.240527632421 0.23771162293 0.236702658605 0.244516745342 0.245937622906 0.240621381598 0.240549902948 0.249413960629 0.233305723303 0.219774348622 0.237625603888 0.232924324305 0.248768260048 0.250380844287 0.232789805762 0.22924781342 0.228117670835 0.26599273559 0.24389404773 0.234785533178 0.238373023947 0.251062908639 0.227440481837 0.229791016779 0.226764157548 0.223002288166 0.220422035353 0.245352612988 0.224391473735 0.240777024278 0.216738025139 0.247654798968 0.221447853711 0.240056634641 0.240035093698 0.237386138185 0.227558813593 0.219321439562 0.226978987712 0.240167124817 0.22597544824 0.240527632421 0.2396924683 0.239078218682 0.232902821621 0.239060496039 0.227569835839 0.232339810664 0.227659905228 0.0141262101806 0.0139426926457 0.0350538423884 0.0193552608508 0.0431621025158 0.0440916637552 0.050896637678 0.0388189182549 0.0267566278127 0.024110231253 0.0232035130335 0.0336464874245 0.0349533341335 0.0219308383764 0.0572621322316 0.0349099564127 0.0531369685873 0.0252590392656 0.0298245046475 0.0531369685873 0.0175869571119 0.0244711956848 0.00818635303924 0.0514811245358 0.0194723442375 0.0509148981601 0.038941584783 0.0196470806889 0.0301479553419 0.021545096414 0.0245644202822 0.0217515624084 0.179463055971 0.184039565703 0.191058390156 0.204133065196 0.197353411589 0.197439799103 0.1907070948 0.174350095292 0.18671934138 0.195225461177 0.19666314681 0.188285080293 0.192912863971 0.197716084636 0.17106321505 0.177948538218 0.199885832792 0.178001143095 0.220003811923 0.185034059424 0.207478284578 0.179492397587 0.21650741229 0.194560853934 0.181055185718 0.181934852801 0.196069170377 0.204292703623 0.197354097622 0.164139489747 0.187014857055 0.180129327357 0.179477085752 0.220692949894 0.202951258529 0.187497059139 0.188415486818 0.206311756974 0.182082560815 0.196599300923 0.200515380101 0.192263706104 0.185773424375 0.176983033355 0.193914579783 0.179773695055 0.186365398569 0.183085748173 0.163066216827 0.18672092116 0.242702110891 0.232274391832 0.228788679171 0.227187602483 0.236388804345 0.235969651611 0.232778101812 0.229721372697 0.23914299349 0.226115326683 0.212181006801 0.228757804803 0.224715842477 0.240932159296 0.244193462731 0.226111600887 0.219855639469 0.218968698082 0.256162056313 0.233278273993 0.227208648321 0.231128251362 0.240452766308 0.218806638317 0.221623839858 0.216518120679 0.214708469871 0.212229381986 0.23697508889 0.213254322456 0.230501952576 0.207030794754 0.239662752997 0.210997516317 0.22840539478 0.231383198724 0.230834439598 0.218316993527 0.211375460714 0.218990694003 0.233092437868 0.219339375552 0.232274391832 0.231855607362 0.232408829329 0.226038981221 0.230519762371 0.219567778072 0.225824086027 0.219230412501 0.0195919993919 0.0221059172753 0.0271326704392 0.0528905379198 0.0431586822848 0.0385447030867 0.0255010287112 0.0262337606525 0.0170321830128 0.0195302099147 0.0206521036595 0.0209833716129 0.0252645250533 0.0550811186683 0.0394746523074 0.043263968218 0.0280088992059 0.0316978756724 0.043263968218 0.00932281475944 0.0154549635915 0.0220221996956 0.04619442006 0.0176558634743 0.0549187053198 0.0347531723104 0.0154010655624 0.0249961358537 0.0130624709681 0.021086975526 0.0157582275316 0.172494143831 0.17784850126 0.184169405165 0.197413015598 0.190717679771 0.190383314088 0.184688114733 0.167622718241 0.179385018024 0.189454833136 0.189254205537 0.182497405034 0.184783379021 0.190756581821 0.165553322788 0.171383967321 0.193742345805 0.169993474917 0.213189630746 0.17771520654 0.201947261672 0.173029090008 0.209395719039 0.186777092552 0.174149245532 0.175318744446 0.188797436083 0.197902799323 0.191019058185 0.156992121883 0.179797341784 0.172560525004 0.172720884671 0.213854773981 0.196871318612 0.181938976962 0.181811193063 0.198969640765 0.17576606357 0.19002752539 0.193102069722 0.185493626505 0.178790364297 0.170132074883 0.187191891105 0.172843021691 0.179777298661 0.176222723299 0.157641466463 0.180195116525 0.237394679003 0.226280310947 0.222447934209 0.220486140395 0.230450693082 0.229006895923 0.227001818993 0.222289337207 0.232008422548 0.22075701885 0.20666547313 0.222449960987 0.218801277251 0.235156691974 0.239347508119 0.221054539901 0.213261323555 0.212504634094 0.249237472588 0.225979854618 0.221636903122 0.225713392695 0.233125083698 0.212685206963 0.215719410439 0.209455227207 0.208792490533 0.206361366722 0.230902205737 0.20571314342 0.223403407555 0.200311655198 0.233802587518 0.20384593219 0.22048704521 0.225216247618 0.225812871456 0.211806271505 0.205652934938 0.213231447371 0.227786843309 0.214377387143 0.226280310947 0.226104307704 0.22732908173 0.220904746241 0.224415757753 0.21378854989 0.220845147853 0.213178189936 0.0385029210536 0.0191331286834 0.0412210559221 0.0479132957057 0.0465498029846 0.0439952648454 0.0294155998226 0.0263022819525 0.0288814535105 0.0321813612841 0.0366154046916 0.0324760763417 0.0515944791638 0.0310049526439 0.0546416139704 0.0338597056505 0.0376153728239 0.0546416139704 0.019563854062 0.0269885058553 0.0157505744896 0.0625368339241 0.0143844790412 0.0526157254036 0.0338348827289 0.0297234721938 0.0233631827985 0.0204547357021 0.0243203298193 0.026443786847 0.18161798851 0.18540629351 0.193056292253 0.206460086953 0.199649422865 0.198358533748 0.191516052491 0.176005251549 0.188914967022 0.196119500233 0.199392563722 0.189524653623 0.196344377596 0.19896940691 0.172616127427 0.180167039066 0.200264745011 0.179650753185 0.223145799502 0.187130729579 0.207841346365 0.181781654149 0.218763807382 0.195938920487 0.183272877978 0.184194077304 0.198599402775 0.206127033545 0.198682577368 0.166796826532 0.189310437328 0.182538488189 0.181515358431 0.221921151125 0.202966250725 0.187732725258 0.190303161279 0.209524247949 0.182729260129 0.198439370358 0.201593038612 0.193392410161 0.187922687561 0.179193994752 0.195211914017 0.180441261226 0.187358375156 0.184927566912 0.165517695891 0.188083050798 0.242939707589 0.233299699182 0.23049760933 0.228144979761 0.237419943564 0.237777043946 0.233178399857 0.23147925362 0.241203272786 0.226929567318 0.213318762235 0.230491320835 0.22634238089 0.242302330141 0.245163744774 0.227035678455 0.220985219764 0.21961554026 0.258484704802 0.235572463293 0.228516087928 0.231839786454 0.242586485114 0.220732170317 0.222439032331 0.217865646283 0.216378419732 0.213247698263 0.238336887242 0.215052759841 0.232719204269 0.208046041848 0.241045913549 0.212450914632 0.229495311164 0.23369764439 0.231101958719 0.219113353825 0.212320151032 0.220660185426 0.234399345217 0.221293359798 0.233299699182 0.23289508713 0.233336932661 0.227805970609 0.232744111879 0.220997609023 0.226042688917 0.219773770977 0.0487507947542 0.0650775470184 0.057802762893 0.0304021403296 0.00977564963899 0.0440829229362 0.0178656109143 0.0223657443481 0.0145412613726 0.00880579384939 0.0433599813023 0.0513293545711 0.0472054655688 0.0249055105449 0.0346274013058 0.0356855998153 0.0249055105449 0.0197624673252 0.0147740519949 0.0419438715965 0.0491548979957 0.0286642180871 0.0731736765881 0.0474587573553 0.0307413987988 0.0281173399333 0.0193986394444 0.0247050709585 0.0190246608336 0.173083486457 0.179910481276 0.184747521108 0.19795391577 0.191518465927 0.190900207622 0.18706914222 0.168885152715 0.179192090216 0.192134059909 0.188561922702 0.185169221179 0.182516578227 0.191341288573 0.168884190059 0.172666616329 0.195883736852 0.16891577804 0.213018522015 0.177613800133 0.204946600226 0.174427531668 0.209108112252 0.18591519295 0.174796071764 0.17642769541 0.188436163643 0.199161967465 0.19263437332 0.157387090389 0.179769711315 0.171986018804 0.173714059835 0.214316704261 0.199152104404 0.185242989169 0.182953007753 0.198045578814 0.177854485907 0.191103831493 0.192902410163 0.18652468984 0.179244739552 0.170950373519 0.188227253613 0.173938849831 0.181251003885 0.177034455953 0.160977434421 0.181661357468 0.240293898195 0.228034626835 0.223480275138 0.221169492968 0.232239647508 0.228872448909 0.229260803782 0.221433214134 0.231450162781 0.223643381382 0.209415344527 0.223528489107 0.220601809879 0.237045012584 0.242726403737 0.224366911478 0.21417342735 0.213777217999 0.24873924373 0.225124576112 0.224039797111 0.228474279829 0.232185938187 0.214138987375 0.217816173262 0.209554716543 0.210717178803 0.208592377005 0.232371287988 0.204873194809 0.222962175481 0.201230387345 0.23556954796 0.203837920113 0.218977104053 0.226289614933 0.229286064361 0.212971252918 0.208146125074 0.215343203422 0.230520772996 0.217614435398 0.228034626835 0.228243837858 0.230522424537 0.223850190554 0.225634530741 0.215935214332 0.224460188266 0.215143400901 0.0529022439667 0.0328752197893 0.0555054115849 0.0509289255649 0.0173017354019 0.040824127775 0.0418711547682 0.0429127577978 0.0455543968368 0.0222834470095 0.0700549868865 0.0480571802481 0.0688386171987 0.0442386440219 0.048830469586 0.0688386171987 0.0328274300443 0.0405886029945 0.0207406620607 0.0561033512724 0.0322786585229 0.0341942563123 0.0291137581017 0.0267606033099 0.0411628160219 0.0351933008888 0.0409598634365 0.0395653722748 0.170381496838 0.173513369262 0.181763628401 0.194913312213 0.188005094963 0.187597093173 0.17963993246 0.164619720579 0.178109956535 0.183964962678 0.188586214824 0.177259498897 0.186421186364 0.18797705999 0.159970598511 0.168451386355 0.188698976448 0.169860506279 0.211615177079 0.176329007245 0.195682012245 0.169937526308 0.20770892717 0.185927394166 0.171947667368 0.172526710735 0.187654893417 0.194331077481 0.186965121806 0.155693053512 0.178340485408 0.171986662107 0.170058435784 0.210831670623 0.191498228781 0.175622787806 0.178712133706 0.198589692402 0.171227708945 0.186830515333 0.191211132234 0.182219297429 0.176703062328 0.167807776518 0.183929379926 0.16962686739 0.17602523241 0.173642929989 0.152568053554 0.176555503125 0.230732284086 0.221405569072 0.21873883425 0.217008773205 0.22548132942 0.226701619289 0.221306518291 0.22096326048 0.23027897199 0.21448417882 0.200865540743 0.218691208961 0.214158679542 0.230111756153 0.232289297369 0.214283536851 0.209653108975 0.208315017711 0.24732302577 0.224778673487 0.216104443077 0.219500448511 0.231884032027 0.208661452368 0.210499772687 0.206990362059 0.20415679031 0.201176497026 0.226427503413 0.204626996142 0.221691815993 0.19686908479 0.228928610762 0.20163871009 0.219806895143 0.22162291443 0.218587303851 0.207804967644 0.200129766869 0.208295752101 0.221764477978 0.208127749312 0.221405569072 0.220756657587 0.220625985458 0.214861357139 0.220620889076 0.208723632287 0.213494112425 0.208081504586 0.0844496882978 0.080315980567 0.0726849367116 0.0675030089859 0.0473153931529 0.0443066374773 0.0646752376508 0.0680246382237 0.0621370759065 0.0478849585017 0.0214355468841 0.0687094353901 0.0384979333887 0.0389190825118 0.0687094353901 0.0480894783324 0.0504544201599 0.0366660205236 0.0908834253131 0.0410486802894 0.0843383154277 0.0744888741312 0.0620377042787 0.0466247584157 0.0490662247741 0.0410809318858 0.0466044522535 0.220241217585 0.22387872958 0.231567441105 0.244549509651 0.237801597966 0.237271702597 0.229957304486 0.214803305719 0.2275731622 0.234289966045 0.237659204867 0.227701989162 0.234344570768 0.237727323575 0.210730852647 0.218580436475 0.238765139748 0.218842181584 0.260585713889 0.225852980679 0.24576980875 0.220083510611 0.25691340974 0.234991633089 0.221816502308 0.222561867727 0.236939418607 0.244255467135 0.237073335368 0.205416544089 0.227870869354 0.221241762596 0.220094090971 0.260317043933 0.241447551617 0.226019474356 0.228752673734 0.247333244315 0.221529692284 0.236780633698 0.24056209021 0.232170242771 0.226474325634 0.217767662464 0.233885787279 0.219595234935 0.226160651319 0.223600212873 0.203287025744 0.22672247966 0.280139937217 0.271046573965 0.268296536378 0.26646171863 0.275059767728 0.275799812567 0.270911764394 0.269815722172 0.27914751079 0.264301608642 0.250949666543 0.26825814336 0.263926860434 0.279625941989 0.2816892989 0.264092216423 0.259287884013 0.257990452056 0.295945646215 0.273550874886 0.265902495939 0.269211685027 0.28055869892 0.258431707188 0.260338655828 0.256388822593 0.254096852299 0.25120005211 0.275959599762 0.25362897924 0.27069009133 0.246633770083 0.278453866291 0.251048870754 0.268244717954 0.271056483898 0.268212813274 0.257487053623 0.250194192056 0.258191856723 0.27145388403 0.257986657626 0.271046573965 0.2704506544 0.270310564507 0.264635332559 0.270115829289 0.258629632749 0.263204545545 0.257890609764 0.0600044713459 0.054351041098 0.0187381084361 0.0598557442646 0.0614469307754 0.0531453686192 0.0525559397425 0.0279315026787 0.094761292833 0.0773971393828 0.0814189949505 0.0655620569523 0.0700255275487 0.0814189949505 0.0520201647503 0.0579117136152 0.0488956649362 0.0419109171187 0.0567137285928 0.0208614229037 0.0306610868801 0.0327090237565 0.063272034339 0.0543984191577 0.0634480421324 0.0583685669148 0.139330779418 0.142986837848 0.150850053523 0.163810831232 0.156868051014 0.157669598862 0.149679456655 0.133871337528 0.147188409072 0.153968295801 0.157417010427 0.14689076955 0.155379198936 0.157713790383 0.129254210759 0.137253284329 0.159227064857 0.139693977567 0.180206197156 0.145463442392 0.166351059616 0.138682374547 0.176816693259 0.155870230339 0.140840856643 0.141328264666 0.156536274226 0.163539791349 0.156545980147 0.124345584519 0.147307265103 0.141019689746 0.139034333305 0.180682036747 0.1624502749 0.146314826392 0.147811298228 0.167221256708 0.141400345074 0.156004354912 0.161263479124 0.152006654871 0.145684418167 0.136675034398 0.153561404536 0.139874017863 0.145889334783 0.142806737922 0.121109447078 0.146069682444 0.201853472765 0.191504661606 0.188213817168 0.18712354961 0.1956147304 0.196211110416 0.19205227926 0.19059963575 0.199675257584 0.184878502918 0.170804202645 0.188147863911 0.18370042204 0.200019806255 0.20290821197 0.184684396544 0.179556147674 0.178663646288 0.216648538359 0.194032671841 0.185998827234 0.190015746161 0.201299319844 0.177907547001 0.180731098286 0.176760615884 0.17358596254 0.171156072474 0.196260390732 0.174185108777 0.190914661822 0.166819613367 0.198798087344 0.171315193398 0.190200635461 0.190703604042 0.189694426068 0.178004531696 0.170196606131 0.177783458136 0.19179082566 0.177644952 0.191504661606 0.190881968726 0.191068709685 0.184514101166 0.189756786146 0.178407147108 0.184631515769 0.178548671216 0.0324892056253 0.0484887435869 0.0393579503362 0.0472627188893 0.0192847169709 0.0232472318507 0.0583393494412 0.0585791036352 0.0611786752117 0.0424145094995 0.0604358052165 0.0620651155443 0.0424145094995 0.037432002363 0.036885144463 0.057932237922 0.0663347563067 0.0402054510934 0.0745837939532 0.0379481013657 0.0474009418109 0.0343119409392 0.0339409730754 0.0428079642087 0.0427340086613 0.16139435599 0.167055199688 0.172679145019 0.186588672878 0.18018023169 0.176484226078 0.173177363378 0.15635152751 0.167324305619 0.178559723881 0.177731707828 0.172313382954 0.172981201837 0.177656055027 0.157022451257 0.161367937512 0.181005231952 0.155624650937 0.203264920982 0.165557822324 0.190457367134 0.163325089652 0.197312648791 0.172046353812 0.163261037864 0.165144860989 0.177247951111 0.186978615056 0.179504291425 0.146841839918 0.168174392768 0.160512412099 0.161906209121 0.200546381048 0.18360708151 0.170619997941 0.170849280459 0.188248015891 0.163499893332 0.178863271671 0.178580924982 0.172741578769 0.16748001244 0.159472149167 0.174802823761 0.159227769495 0.167358477359 0.164720514491 0.151099028747 0.168548224145 0.225620984689 0.214383995774 0.210968404368 0.206923044041 0.218629792329 0.216162853062 0.214556733684 0.208383921822 0.219163632298 0.210075568948 0.196412311391 0.211088115641 0.208225582965 0.224211015109 0.229824216763 0.21125046128 0.200361910494 0.199102744226 0.237092122358 0.213257676298 0.21126821314 0.214648561609 0.219950980033 0.202248505777 0.203853239286 0.195906673599 0.198467217831 0.195092454721 0.219319110187 0.191906900165 0.211043854216 0.187166023617 0.222703227226 0.190377176965 0.20432458647 0.215124925944 0.214931156714 0.198560742198 0.194613360236 0.203185148475 0.217932610212 0.206623397839 0.214383995774 0.214797954846 0.217376030313 0.212326968407 0.214327312553 0.203281369316 0.21006441216 0.200554878155 0.0431984510448 0.0262542814372 0.0292765976694 0.0197548242369 0.011556553192 0.0420248516295 0.0609848282508 0.0559281666693 0.0312264925083 0.0398958448421 0.0413044920489 0.0312264925083 0.0264369738429 0.0230439739916 0.0462734733613 0.0414691510708 0.0363662025945 0.071550533242 0.0478849716213 0.0291862789915 0.0369373377392 0.0270714127462 0.0334218260536 0.0262390277171 0.165544038214 0.172891912863 0.17727312869 0.190354892388 0.183958646909 0.183879480011 0.180336742003 0.161635841791 0.171548979047 0.185413091762 0.180663093483 0.178298498301 0.174219977581 0.184181187608 0.161990271375 0.165191063063 0.189309277337 0.161423020955 0.205054842396 0.170020243784 0.198514733988 0.166940816324 0.201451973118 0.178556962169 0.167238778058 0.168913749918 0.180647428918 0.191850429195 0.185573186785 0.149626920851 0.172104358108 0.164216849771 0.166272040954 0.207167511585 0.192763981398 0.178906141081 0.175593499508 0.189955336349 0.171151190119 0.183759195847 0.185737635433 0.179470325207 0.171692649892 0.163416602952 0.18110112813 0.167100843514 0.174311352225 0.169649991905 0.153736698586 0.174561747041 0.233945473753 0.221154951746 0.216216020369 0.214168470951 0.225368727897 0.22143399553 0.222723035108 0.213934293134 0.223870685298 0.217024022208 0.202620827758 0.216260896263 0.213476245827 0.230060267172 0.236168299839 0.217779153489 0.207119858677 0.206977473585 0.241093686538 0.217416199365 0.217140656671 0.221883614074 0.224541883596 0.206836488872 0.211062122527 0.202310625247 0.203579796415 0.201763166599 0.225319290313 0.197343747333 0.215321920645 0.194216980733 0.228558415828 0.196533366942 0.211711953148 0.218804815465 0.22300715182 0.206091661798 0.201387938181 0.208241563837 0.223688965104 0.210605474313 0.221154951746 0.221416959944 0.223922313478 0.216868041009 0.218202757079 0.208934179216 0.218220121204 0.208484033719 0.0428988991781 0.0449811252912 0.038369173012 0.0392012066073 0.0184411809504 0.0770158611592 0.0591685049971 0.0671690041442 0.0499388239897 0.0544215157144 0.0671690041442 0.0346140690794 0.0413101969584 0.0321994698404 0.0440967531043 0.0383047062906 0.0298070621094 0.0211708137004 0.0201129458309 0.0453565917011 0.0368886973605 0.0457257341 0.0417220412167 0.154888546678 0.158813196403 0.166441835812 0.179672088583 0.172784191158 0.17260621351 0.165322327284 0.149403283235 0.162445964052 0.169819706795 0.172790429714 0.162919256631 0.17010598628 0.17291506116 0.145591206314 0.153172124666 0.174534430185 0.15398983773 0.196191934512 0.160693736573 0.182015889553 0.154710886954 0.192325902157 0.170351903358 0.156482289316 0.157229639215 0.171983255038 0.179460677481 0.172268582678 0.139849276668 0.162711676043 0.156097286205 0.154728919391 0.195984213978 0.177559464338 0.161865850494 0.163584898864 0.182768842876 0.156718657607 0.171791912863 0.175973376944 0.167287390852 0.161244255475 0.152349968293 0.168972557751 0.154707872737 0.161224201041 0.158337859598 0.137882544324 0.161653779265 0.217498297387 0.207268525118 0.204089016051 0.202387117484 0.211405750921 0.211656794766 0.207586435206 0.20562940887 0.215067539532 0.200874708618 0.186922162094 0.204055283767 0.199813802793 0.216054855672 0.219141272905 0.20087129472 0.194993585948 0.193928687775 0.232251037143 0.209370889696 0.202145172225 0.205917531589 0.216546254935 0.194031863501 0.196450064794 0.191939021682 0.189735506736 0.187016932169 0.212133736422 0.189132050882 0.20640648386 0.182100826622 0.214796842045 0.186466235268 0.20445164291 0.206912827034 0.205504201388 0.193322781156 0.186101115818 0.194015369065 0.208047565025 0.194373832921 0.207268525118 0.20679029341 0.207231461284 0.201073842632 0.205977385627 0.194517602543 0.200437951551 0.194023674059 0.00847168263945 0.0208969725179 0.0218078109788 0.0404437599311 0.0404194345362 0.0297098891496 0.0297074253675 0.0216763215811 0.0230614305273 0.0297074253675 0.00860159575866 0.00340785264521 0.0287758992091 0.0571909561512 0.0141236699669 0.0709946391549 0.0479055513747 0.0306293087394 0.0170492128659 0.00888265836541 0.00729150357409 0.00480661141663 0.185289458233 0.191085055337 0.196910034804 0.210145347321 0.203571296435 0.202882056675 0.197912476038 0.180625340213 0.191835734795 0.202787038295 0.201526915714 0.195916323017 0.19637797116 0.203353873438 0.179306027922 0.184472082084 0.206754233077 0.181958697279 0.225649748274 0.19019500565 0.21522963857 0.186167360837 0.221735787446 0.198765098442 0.186973637291 0.18833349586 0.201196357535 0.21085114286 0.204041973491 0.169797982896 0.192333522046 0.184882922721 0.185665053896 0.226331622925 0.209833971543 0.195365654989 0.194769866304 0.211153682947 0.188870228152 0.202929999244 0.20531727443 0.198262875463 0.19151438955 0.183035158868 0.19999216658 0.185562442931 0.19270971507 0.189054601453 0.171539131405 0.193197031986 0.250465285504 0.239173402368 0.23520366736 0.232978158323 0.24334022521 0.241285239552 0.239942028126 0.234265108703 0.244131225098 0.234017030201 0.220001112132 0.235226825533 0.231841004595 0.248118070359 0.252690451382 0.234464488853 0.225903274535 0.225189555796 0.261354129729 0.238022768459 0.234821661628 0.238891339667 0.245085625014 0.225666229038 0.228735290931 0.22181584168 0.221922833386 0.219499858076 0.243732621444 0.217749348385 0.235627115362 0.212972717548 0.246730042299 0.216195919385 0.232029501648 0.238052252824 0.239120375862 0.224484292562 0.218871170879 0.226408868705 0.241049615623 0.227943328268 0.239173402368 0.239135618661 0.240673256099 0.234329166611 0.237299131316 0.226939754937 0.234208016817 0.226098822397 0.028402190164 0.0277439229346 0.038946334352 0.0434213038922 0.0291676419928 0.0326159908833 0.0134911389606 0.0149220924049 0.0326159908833 0.0134816006656 0.0104081517294 0.0265932070523 0.0554420028459 0.0189257472352 0.0720390188254 0.0531694571518 0.0303305078503 0.0242591305562 0.0161087615284 0.011068875208 0.00482735633945 0.188743108882 0.194722342773 0.200407653053 0.213430694909 0.206860229667 0.206917746824 0.201757002065 0.18426566592 0.195342886433 0.206526515083 0.204777293747 0.199508643938 0.199425600774 0.207211939812 0.182702196026 0.187804249003 0.210815779569 0.185880854144 0.228533589019 0.193748347964 0.21913688541 0.189440254064 0.225129338462 0.202750726696 0.19038113591 0.191653574921 0.204515978518 0.214295374936 0.207718295645 0.173066791358 0.195766348142 0.188344856347 0.189125824359 0.230149564934 0.214039936134 0.199354571186 0.198257058594 0.214135463178 0.192868986061 0.206423136152 0.209356843732 0.20214026995 0.194966833942 0.186438002469 0.20377859508 0.189699928978 0.196612341236 0.192629244536 0.174503529883 0.196914127595 0.254336052592 0.242870282486 0.238667846113 0.23688365824 0.247015930548 0.244839615824 0.243870689439 0.237954961473 0.247598002179 0.237655720218 0.223546452001 0.238671394435 0.23524462783 0.251589019272 0.256100611398 0.237973407633 0.229710932979 0.229189966161 0.264623863258 0.241418386796 0.238285878732 0.24258651618 0.248557401899 0.228985563027 0.232518055884 0.22563381547 0.2253177885 0.223189662674 0.247286475663 0.221462369267 0.239019854286 0.21687609732 0.250217711381 0.219989961903 0.23614243802 0.241179806044 0.242913875985 0.228427928816 0.222559090866 0.229759973658 0.244438457877 0.230953863516 0.242870282486 0.24276526561 0.244179265793 0.237465205634 0.24045622955 0.230407994296 0.238017253402 0.230045288889 0.0082487413397 0.0434871339043 0.050183840328 0.0462946366306 0.0331671559449 0.0413860283481 0.0432754400997 0.0331671559449 0.018728526127 0.0181135727037 0.0407852007406 0.0552412031248 0.0241124765906 0.0672915663182 0.036051090003 0.0318240646756 0.0214815502931 0.0159437959725 0.0254901889174 0.0237539942631 0.169264556488 0.175091115152 0.180818741393 0.194404360875 0.187867354301 0.185963216048 0.181699481724 0.164435072081 0.175589139186 0.186821392192 0.185627969522 0.180155862933 0.18064011609 0.186712516664 0.164002900607 0.168780956251 0.190185205445 0.164923288811 0.210475012613 0.173888729012 0.199146471591 0.170598213889 0.205685349652 0.18167215365 0.171034804287 0.172628140609 0.185232711805 0.19500403819 0.187902476327 0.154050585176 0.176234935944 0.168650273349 0.169706859713 0.209769138775 0.193110247226 0.179199013537 0.178806517751 0.195681872133 0.172338947581 0.186953230165 0.188260629529 0.181677967227 0.175468629894 0.167135725316 0.183547713996 0.168593151982 0.176177479228 0.172880341359 0.156935998004 0.176943539051 0.234533528357 0.223126396017 0.219327759561 0.216348929156 0.227347260416 0.225044163552 0.22369577874 0.21767535265 0.227960059154 0.218355967949 0.204394726122 0.219392188471 0.216234012192 0.232478694207 0.237601395838 0.219130755018 0.209451421522 0.208536786671 0.245551542374 0.221905162641 0.219288471778 0.22312655653 0.228847315313 0.210092965448 0.212618708514 0.205172062073 0.20633435877 0.203530506813 0.227844403478 0.201098042061 0.219576265526 0.196351165226 0.231028528894 0.199557870197 0.214705957114 0.222733230705 0.223437599857 0.207886214359 0.202974139337 0.210949037245 0.225748458168 0.213347187038 0.223126396017 0.223291835541 0.22533034675 0.219465519892 0.221962474599 0.211307137623 0.21852957733 0.209700938041 0.0419192621384 0.0545713872547 0.0501857282151 0.0312681130066 0.0402402021553 0.0420008929211 0.0312681130066 0.0205720172252 0.0184919891428 0.0424913715031 0.0489285308529 0.0286939286865 0.0683097144827 0.0399734095489 0.0293256914093 0.0276858239832 0.0194684720042 0.0280123772406 0.0234918354003 0.166859723597 0.173331371512 0.178503598003 0.191909917223 0.185423378094 0.184241312774 0.180294315558 0.162400014304 0.173055799755 0.185412361634 0.18273757472 0.178554599617 0.177148633502 0.184812731656 0.162323255063 0.166439616176 0.188986532191 0.162572794611 0.207449709654 0.171422042516 0.198077549025 0.168235674576 0.203119984389 0.179508471538 0.16860424661 0.170241216483 0.182496759111 0.192879913246 0.186103766816 0.151333877107 0.173669495408 0.165942821486 0.167423536544 0.20787297933 0.192134128169 0.178241744028 0.176630454655 0.192503529595 0.170982423968 0.184797056786 0.186354645979 0.179907483835 0.173050611212 0.164731478826 0.181684378357 0.167089130276 0.174542724285 0.170681059367 0.154769008136 0.175102602238 0.233527638993 0.221524217045 0.217249882254 0.214634557666 0.225748190551 0.222754126699 0.222507410575 0.215313006261 0.225475910208 0.217028556203 0.202867049651 0.217307455338 0.214308520933 0.230711314018 0.236268919515 0.217806003587 0.207670960165 0.207072717123 0.242941912447 0.219250459655 0.217623775648 0.221838573376 0.226272530404 0.207953270239 0.211176611808 0.20315950921 0.204399143496 0.201993427277 0.226020220858 0.198708591851 0.217018221505 0.194631582156 0.229236199557 0.197470332905 0.212630809879 0.22033552246 0.22249437564 0.206324343096 0.201514537553 0.209042945777 0.224134795171 0.211456208554 0.221524217045 0.221736417649 0.223988271389 0.217633390228 0.219633609699 0.209536083539 0.217632171671 0.20838956304 0.0775399671204 0.0564468400456 0.0653641254742 0.0395849667657 0.0441943006701 0.0653641254742 0.03373111328 0.0393588608039 0.0260318537498 0.035188052496 0.0394930245033 0.0375459624937 0.0389958606199 0.0129896263362 0.048815943545 0.0379517413039 0.0436570210079 0.0373605585088 0.163502150984 0.168337389523 0.175155703097 0.187828039408 0.181028588214 0.182647655516 0.175454197959 0.158708602469 0.170954310136 0.179762949884 0.180496225683 0.172501221065 0.176828601599 0.182533215894 0.154901122018 0.161684635697 0.185118234991 0.163277975005 0.203127118484 0.169348081929 0.192443927773 0.163103682661 0.200509349698 0.179853786658 0.164993784705 0.165655282459 0.179962811264 0.188228038198 0.18173609183 0.147930089008 0.171080786567 0.164375611634 0.163483874158 0.205438086531 0.18853460221 0.172643498492 0.17244334411 0.189729944562 0.167174105351 0.180637454384 0.185804185303 0.177096116234 0.169809585441 0.16090449309 0.178551065139 0.165213876 0.171240797871 0.16732581913 0.146067131815 0.17119760345 0.227732558213 0.216775565291 0.212818856201 0.212113111804 0.220864451794 0.220275362854 0.217805693503 0.214378859193 0.223348154292 0.21056133718 0.196361313338 0.212749259626 0.208612136858 0.225027947273 0.228363013794 0.210350767964 0.204552905568 0.204069917831 0.240058060786 0.217392662406 0.211260278871 0.215701291359 0.224730833936 0.202526361799 0.206282759299 0.201292044309 0.198558189974 0.196671901675 0.221200953228 0.197951565037 0.214539723027 0.191917757906 0.223779418483 0.195733699704 0.214045901526 0.214810423689 0.215727246675 0.203293848315 0.195827563807 0.20277537878 0.217057373264 0.202583470922 0.216775565291 0.216238379997 0.216660429864 0.209514540125 0.213990052328 0.203581443905 0.210749726957 0.20422943306 0.0299803388219 0.040141041955 0.0494042382248 0.0475047073328 0.040141041955 0.0460070623429 0.0427394298505 0.0575631845334 0.0966299998606 0.0399614977189 0.101837465377 0.0750240618009 0.0697188576907 0.0332240827502 0.042809511279 0.0352142478257 0.0438103448171 0.215592317841 0.220449330264 0.226776190885 0.240218246846 0.233773884944 0.231038348034 0.2263684672 0.210465175429 0.221881119192 0.231308080404 0.231995396207 0.22508238649 0.227350588366 0.232024663305 0.209334152567 0.215020396004 0.234337406165 0.211132081266 0.256379513335 0.220156177626 0.242726282533 0.216799299323 0.251315628789 0.227298889535 0.217343977664 0.218830240402 0.231491464759 0.240364579333 0.233021642066 0.20102218505 0.222551706707 0.215242680103 0.215887525017 0.254517907032 0.236803444285 0.223122921939 0.224639449766 0.242029857582 0.217221787473 0.232566133597 0.233478733428 0.226945320336 0.221662735537 0.213507733767 0.228892766884 0.213808892567 0.221389781107 0.218899946715 0.202924474402 0.222409930936 0.277044937084 0.267058523642 0.264053120566 0.260683081125 0.271161003688 0.269928478944 0.266921380471 0.262757571495 0.272983826376 0.261803183737 0.248535164351 0.264118904646 0.260747941814 0.276361888176 0.280348586054 0.262426412526 0.254046125774 0.252677731845 0.290308221249 0.267228122274 0.26327783075 0.266417210913 0.273921992864 0.255063180539 0.256547903477 0.250174111671 0.251094888905 0.247813627121 0.2719924919 0.246585755223 0.264876118769 0.241182097008 0.275004799417 0.244784301617 0.259319666846 0.267703173929 0.266005774781 0.252196034878 0.247119094967 0.255516011217 0.269407340011 0.257485027934 0.267058523642 0.267089999571 0.268509786469 0.263477813268 0.266862086523 0.25569939087 0.261126117571 0.253527997613 0.0490986378456 0.0295882981502 0.0295703235206 0.0490986378456 0.0321772405698 0.0329624694956 0.0320791928311 0.0819581869701 0.0243777845713 0.0815880274595 0.0630031242432 0.0521832400773 0.0270041961787 0.0316626367995 0.0225970983534 0.0306135777155 0.209138400452 0.213486068388 0.2205444529 0.233810061497 0.227135531522 0.225853564299 0.21966357841 0.203852341272 0.216088869802 0.224306040557 0.226217923772 0.217746213648 0.22224285579 0.226486749722 0.201086071725 0.207931744175 0.228270944293 0.206490835508 0.249940445551 0.214362660634 0.235990805565 0.209564571075 0.245741550277 0.222850058752 0.210799114332 0.211874421056 0.225608964554 0.23378761328 0.226547651045 0.194192088088 0.216536885951 0.209533041634 0.209201443093 0.249253656365 0.230961033564 0.21611849601 0.218017866257 0.236081990375 0.21084689547 0.226091432124 0.228781524913 0.221104911259 0.215367366824 0.206800674523 0.222911320613 0.208230596605 0.21525709201 0.2125575014 0.193883560936 0.215963986139 0.270701972881 0.26093677072 0.257916700448 0.255512820108 0.265026258573 0.264740784654 0.260931231555 0.258198696768 0.267948592812 0.254868213876 0.241356571396 0.257920982238 0.254009770883 0.269860597717 0.272941762013 0.255029971614 0.248480879151 0.247232962401 0.285073136127 0.262202525959 0.256323297606 0.259707446043 0.269155247223 0.248322830219 0.250262594392 0.245086205405 0.244160084795 0.241180503071 0.265837705385 0.241871800867 0.259549733542 0.235634358004 0.268589453275 0.239657575868 0.255932774798 0.261005427367 0.2590787222 0.246704864465 0.240323606031 0.248446059436 0.262200385961 0.249216212931 0.26093677072 0.260623330072 0.261243034875 0.255650557301 0.260116706756 0.248814449864 0.254094104786 0.247584466928 0.0426893927085 0.0408370899258 0.0 0.0368318621027 0.029200211213 0.0578784327643 0.069522041979 0.0411420259607 0.0955605905857 0.0684916485028 0.0536985636371 0.0361283833003 0.0349236407736 0.0326544016244 0.0320790517512 0.190734279342 0.198011093099 0.202165335147 0.215193803756 0.208989236133 0.207943677959 0.205067684162 0.186855165408 0.196324830352 0.210203923195 0.205342231011 0.203427359944 0.198526873832 0.208498767129 0.187766259392 0.1906844311 0.213518747065 0.185598548292 0.229746939288 0.194805777978 0.222821572969 0.192481412892 0.225723793976 0.202375384198 0.192457483521 0.194300696695 0.205376522136 0.216660688755 0.21030774241 0.175307222553 0.197000980201 0.189066511629 0.191539232379 0.231058906578 0.21669613367 0.203523564636 0.200666530244 0.214595210264 0.195827171333 0.208641958794 0.209561099462 0.203960485003 0.196717491572 0.188766309975 0.205678014795 0.191492051166 0.198961164508 0.194672996958 0.180152450124 0.19944864987 0.257583228235 0.245178524187 0.240481186058 0.23786630337 0.249341975217 0.245210894146 0.246468005973 0.237456371003 0.247563146561 0.241300303041 0.22729752986 0.240555698528 0.237993038095 0.254193700204 0.26033321069 0.24220764546 0.231128442611 0.230799434891 0.264694157473 0.241194058993 0.241588310222 0.245985985156 0.248083056968 0.231516408669 0.235215479132 0.226198260689 0.228320459149 0.22623565919 0.249391170367 0.221152204365 0.239284874476 0.218351963925 0.252686589685 0.220530747351 0.234477098822 0.243363395844 0.246969168578 0.229990071021 0.225895881072 0.232959826282 0.248113114083 0.23570223574 0.245178524187 0.245562733947 0.248226439579 0.241702239505 0.242775171886 0.233515238499 0.242256602421 0.232453320509 0.00485178735022 0.0426893927085 0.0245520805084 0.0237321483943 0.0251494297711 0.057422445578 0.0278324002426 0.0740340192529 0.0616449531864 0.0345266403523 0.0352178925007 0.0280808122885 0.0215364121522 0.0177797202497 0.195939824645 0.201850357058 0.207610278022 0.22033675889 0.213750209485 0.214735564684 0.209038553623 0.191585516698 0.202707820911 0.213610139259 0.211883129405 0.206450810174 0.206559465565 0.214810867748 0.189369070647 0.194741318593 0.218356798216 0.193913039155 0.235021949805 0.201158850164 0.226304640977 0.196279111135 0.23226696484 0.210758787397 0.197506357802 0.198589557713 0.211650666116 0.221261000242 0.214919567324 0.180148757389 0.203013849771 0.195747568982 0.196259855385 0.237643665501 0.221708411792 0.206639148011 0.205357745264 0.220920753341 0.200419916968 0.213509698507 0.217293606067 0.209702604063 0.202159105945 0.193545439121 0.211227865592 0.19759997516 0.20414742205 0.199914048013 0.180698578828 0.204233409175 0.261355583351 0.249944219156 0.245612323364 0.244419283687 0.254044690674 0.252080319195 0.251117898529 0.245501499007 0.254799073865 0.244461494615 0.230320341653 0.245583982907 0.241986600586 0.258340089378 0.262471019952 0.244539880639 0.237114058797 0.236754636273 0.271527597514 0.248603215251 0.244975611097 0.24946627049 0.255826675762 0.23571262824 0.239663820665 0.233206292475 0.232061847402 0.230234276049 0.254229520181 0.229085095534 0.246138012907 0.224434087127 0.257016267918 0.227571722833 0.244299258458 0.247702557023 0.249766808341 0.235948695396 0.229554249737 0.236400217176 0.250952182681 0.236961759284 0.249944219156 0.249686907558 0.250743450736 0.243657848264 0.246990711027 0.237179418187 0.244877764453 0.237415960787 0.0408370899258 0.0273499265107 0.025297636378 0.0296282714156 0.0603715739764 0.030303105355 0.0787085816014 0.0655434565706 0.0387042108075 0.0365503327535 0.0303622028421 0.0226096284705 0.0196557817863 0.19925839426 0.205348957591 0.210921560248 0.223617852816 0.217069506306 0.218059817218 0.212575335145 0.194999027921 0.205926088655 0.217171129009 0.215009852018 0.21001079042 0.209452929226 0.218136520475 0.193003747556 0.198137341753 0.221866239829 0.197043564846 0.238162085485 0.204392911714 0.229893095054 0.199683809546 0.235440202166 0.213909407049 0.200827966888 0.201961005661 0.214826820158 0.224639586072 0.218358620882 0.183440825957 0.206248208954 0.198918059014 0.19962810203 0.240932955881 0.225232933618 0.210279009964 0.208738673233 0.223984027141 0.203935797463 0.216876482587 0.220527121607 0.21308659534 0.205456328664 0.196890655988 0.214607347176 0.201007750183 0.207588221909 0.203264328774 0.184313736826 0.207665740899 0.26489357675 0.253366014308 0.248941435732 0.247733075772 0.257464720601 0.255265649267 0.254603866324 0.248608337617 0.25792057227 0.248007634376 0.233863042555 0.24891723403 0.245404277418 0.261753780229 0.266030956859 0.248122124098 0.240457509587 0.240148573616 0.27462290691 0.251686093628 0.248460344614 0.252996524531 0.258897040073 0.239091218679 0.243138472416 0.236454818573 0.235503469176 0.233728803563 0.257606144443 0.232210377675 0.249277823428 0.227793973728 0.260419107201 0.230810446264 0.247340816609 0.251008018719 0.253372216044 0.239328849004 0.233077723245 0.239855175697 0.254460096493 0.240510971982 0.253366014308 0.253147704183 0.254305992093 0.247178452498 0.250316773817 0.240645130337 0.248503359474 0.240885126472 0.0368318621027 0.029200211213 0.0578784327643 0.069522041979 0.0411420259607 0.0955605905857 0.0684916485028 0.0536985636371 0.0361283833003 0.0349236407736 0.0326544016244 0.0320790517512 0.190734279342 0.198011093099 0.202165335147 0.215193803756 0.208989236133 0.207943677959 0.205067684162 0.186855165408 0.196324830352 0.210203923195 0.205342231011 0.203427359944 0.198526873832 0.208498767129 0.187766259392 0.1906844311 0.213518747065 0.185598548292 0.229746939288 0.194805777978 0.222821572969 0.192481412892 0.225723793976 0.202375384198 0.192457483521 0.194300696695 0.205376522136 0.216660688755 0.21030774241 0.175307222553 0.197000980201 0.189066511629 0.191539232379 0.231058906578 0.21669613367 0.203523564636 0.200666530244 0.214595210264 0.195827171333 0.208641958794 0.209561099462 0.203960485003 0.196717491572 0.188766309975 0.205678014795 0.191492051166 0.198961164508 0.194672996958 0.180152450124 0.19944864987 0.257583228235 0.245178524187 0.240481186058 0.23786630337 0.249341975217 0.245210894146 0.246468005973 0.237456371003 0.247563146561 0.241300303041 0.22729752986 0.240555698528 0.237993038095 0.254193700204 0.26033321069 0.24220764546 0.231128442611 0.230799434891 0.264694157473 0.241194058993 0.241588310222 0.245985985156 0.248083056968 0.231516408669 0.235215479132 0.226198260689 0.228320459149 0.22623565919 0.249391170367 0.221152204365 0.239284874476 0.218351963925 0.252686589685 0.220530747351 0.234477098822 0.243363395844 0.246969168578 0.229990071021 0.225895881072 0.232959826282 0.248113114083 0.23570223574 0.245178524187 0.245562733947 0.248226439579 0.241702239505 0.242775171886 0.233515238499 0.242256602421 0.232453320509 0.00792313459309 0.0236297776546 0.0536541076705 0.0108412074571 0.0628560371629 0.0400426717019 0.0242916508179 0.0172043768705 0.0050674059167 0.0120480352419 0.00909567999576 0.179733982337 0.185130641595 0.191359647755 0.204666509458 0.198016972069 0.197255576955 0.19185518721 0.174855164332 0.186481505005 0.196685913747 0.196375987191 0.189833405887 0.19172374648 0.197739787295 0.173088361774 0.178764981726 0.200732295255 0.17675687262 0.220477540317 0.184804954654 0.209049846749 0.180446207623 0.21646451476 0.193507677512 0.181414841832 0.18267689502 0.195938395299 0.20515640915 0.198204629073 0.164323672477 0.186951627799 0.179636188812 0.180004434454 0.220785829324 0.203765417111 0.189073881786 0.189075562692 0.206144383335 0.182845346787 0.197260508087 0.199879524686 0.192526504243 0.186000212457 0.177429453542 0.194272636643 0.179755047797 0.186852872988 0.183420017951 0.165408476828 0.187374971211 0.244379342624 0.233352874625 0.229596132577 0.227371143644 0.2375226222 0.23596534417 0.233967797192 0.229101525181 0.238946455726 0.22794079194 0.213947298319 0.229612039594 0.226054866131 0.242325328031 0.24658946328 0.228320886035 0.220245475488 0.219410638455 0.256223296024 0.232925784221 0.228888220536 0.232841814871 0.240004491346 0.219971839687 0.222802209556 0.216357175156 0.216093165101 0.213536808192 0.238014479196 0.212556057937 0.230415334062 0.207278762186 0.240959781593 0.210762423837 0.226970181506 0.23251461018 0.23290234378 0.218739342276 0.212847200349 0.220551656017 0.235068599786 0.221910374672 0.233352874625 0.233237981586 0.234568034713 0.228343429058 0.231717374059 0.221050886534 0.227947933137 0.220165226053 0.0299322879907 0.05484354778 0.015508827535 0.0698377856312 0.0461226756997 0.0288439268589 0.0179749812614 0.00820193608475 0.0104514607363 0.00601471721254 0.182232775094 0.188144985482 0.193867682363 0.20711590793 0.200550766912 0.199860135361 0.195016228938 0.177611309591 0.188739284934 0.199919451707 0.198412537874 0.193032154463 0.193186123937 0.200329430243 0.176446801251 0.181459762374 0.203862717343 0.178811701467 0.222599641879 0.187102653143 0.212418919228 0.183165138771 0.218668582765 0.195650642874 0.183922319896 0.18531418569 0.198099986213 0.207874048966 0.201087073424 0.166709980365 0.189246782627 0.18175655241 0.182635613511 0.223327464443 0.206968516624 0.19255440527 0.191763016548 0.208041469681 0.185943960269 0.199929552497 0.20225187193 0.195263692383 0.188456709089 0.17999008547 0.196992188976 0.182563902533 0.189736470349 0.186019084029 0.168664903455 0.190216873808 0.247702840069 0.236282170601 0.232244216334 0.230002139075 0.240457584895 0.238254033101 0.237105182737 0.231183514354 0.241076068443 0.231215767875 0.217161214732 0.232270602033 0.228935795245 0.245246208475 0.249963377759 0.231700745278 0.222930449716 0.222251471523 0.258324331925 0.234942120984 0.231973156153 0.236088516952 0.242009296663 0.222723564262 0.225860019318 0.218781249758 0.219012595092 0.216618271437 0.240821050718 0.214649402949 0.232567858569 0.209986385677 0.243846113816 0.213146433073 0.228924161819 0.235098611969 0.236391467186 0.22153411273 0.216011138881 0.223520339681 0.238239770023 0.225155295827 0.236282170601 0.236275025927 0.237908750738 0.231525069379 0.234355153325 0.224056763193 0.231485450395 0.223215654925 0.0572284762647 0.0227453897084 0.0528238726337 0.0444991634471 0.0266973769114 0.0336948312224 0.0272062333147 0.0274688740262 0.0263328670099 0.185716682176 0.189934662812 0.1972487923 0.210230033124 0.203429572326 0.203652674494 0.196491149306 0.180504651022 0.193104816748 0.200892549998 0.203064104164 0.193989394378 0.199601579765 0.203900303764 0.17670118478 0.184039790634 0.205684310622 0.1846555251 0.226113704537 0.191415178407 0.212986837934 0.185538541574 0.222740004307 0.201086740863 0.187280443615 0.188038576609 0.202414184433 0.210238732317 0.203278048935 0.170511460418 0.193354539773 0.186626010428 0.185640639557 0.226777734968 0.208697723096 0.193060716385 0.194490643156 0.212667601723 0.188009393157 0.2026405567 0.206872630148 0.198376957217 0.192017886181 0.18319542151 0.200010008529 0.185964041385 0.192410951726 0.189280207199 0.168712331151 0.192752182621 0.248024768226 0.23796255467 0.234657432476 0.23319215601 0.242043627351 0.242092609424 0.238343194449 0.236048752979 0.245336704526 0.231521041315 0.21770196943 0.234612870361 0.230403022013 0.246515101463 0.249324779094 0.231381623939 0.225835327084 0.224876205525 0.262239370649 0.239554652873 0.232726560614 0.236544998844 0.246724791051 0.22460575232 0.227286958934 0.222708966341 0.220419587826 0.21790450046 0.242699459353 0.219658545965 0.236700307439 0.213086341068 0.245286981729 0.217239309033 0.234909245332 0.237182720463 0.236050820735 0.224251890124 0.21698485009 0.224622112179 0.238475066919 0.224611061526 0.23796255467 0.237436165661 0.237675287343 0.231370754771 0.236291823604 0.225200181658 0.231029643716 0.224953381888 0.0635710838178 0.0592624543316 0.0596058092469 0.0328544820604 0.0698260168465 0.0573261822931 0.0634460229613 0.0541080958794 0.147303482686 0.155161448288 0.159136079545 0.171063643845 0.16463221595 0.168584971583 0.163624194761 0.144235266953 0.153827968777 0.168063795028 0.16179307599 0.160206770568 0.155398151635 0.167887347752 0.142965424509 0.146100735749 0.17375554158 0.146517107633 0.184102282814 0.152518687773 0.182006446517 0.147493939017 0.182975421708 0.163616999091 0.148716120222 0.149770660992 0.161958247648 0.173135111896 0.168022513735 0.130738265008 0.153920373487 0.146537313722 0.147925357539 0.190546691654 0.177990062346 0.162980599048 0.157241520972 0.169863812022 0.155438284157 0.165389918434 0.170707644688 0.163197102596 0.153422018191 0.144858486622 0.164315178145 0.152440808389 0.158128021971 0.151920639439 0.132537826475 0.157359920068 0.217237278816 0.203578319465 0.19760287759 0.197919551969 0.207681519923 0.203644803794 0.206313583309 0.1971606419 0.205828867849 0.199064489236 0.184178187026 0.197538822775 0.194404518771 0.211337912042 0.217143803133 0.199166671372 0.190313841849 0.191119829867 0.222105237734 0.199176357415 0.198307304 0.204224647581 0.206706114847 0.187412194782 0.19389709622 0.185837912363 0.184448440446 0.184112718669 0.207064291919 0.180760878547 0.196874753064 0.177987251583 0.20993106085 0.180009687643 0.197504007472 0.198552660709 0.205899002329 0.189953313758 0.183706289376 0.188863124638 0.204480568311 0.189510910124 0.203578319465 0.203449868624 0.205307715981 0.196398052452 0.198053210544 0.190139115088 0.201200460168 0.192215464603 0.0646648846724 0.0408252785942 0.0324283536149 0.0110096321254 0.0094227049932 0.0109851717283 0.0161323040352 0.185995265043 0.190666336997 0.197500031875 0.21094278484 0.204249573992 0.202798103731 0.196994111724 0.180742040387 0.192881751338 0.201790619427 0.203110749465 0.195155158145 0.19908437034 0.203454592708 0.178505994536 0.184932353957 0.205646229373 0.183039730424 0.227253690432 0.191142107437 0.213742423108 0.186621634482 0.222820224457 0.199555206103 0.187692696694 0.188884746385 0.202513734055 0.21103182776 0.203761324581 0.170936996707 0.193374027451 0.18623653615 0.18612791719 0.226436319176 0.208431967216 0.193707164322 0.195058461584 0.2131353075 0.187987297837 0.203202157658 0.20562939159 0.19810682266 0.192261721083 0.183685994256 0.199939039445 0.185109577754 0.192293556305 0.189460942551 0.171326016098 0.193018202765 0.248893705524 0.238594153414 0.235368547023 0.232809197602 0.242745772985 0.241996085058 0.238752248121 0.235253956997 0.245180784501 0.232837883436 0.219103668122 0.235388183401 0.231636120508 0.247694764978 0.251385734184 0.233178069062 0.225757404345 0.224585782777 0.262534762143 0.239353867417 0.234157942476 0.23769286224 0.246342255252 0.225805082888 0.227891511242 0.222153883963 0.221706146684 0.218742321816 0.243483255972 0.218775046696 0.236736505477 0.212768083313 0.246367071263 0.216653745648 0.232866084107 0.238586937872 0.237342638049 0.224021718097 0.217958679267 0.226108360277 0.24024082191 0.227355271774 0.238594153414 0.238405022146 0.239431466759 0.233734880392 0.237715653864 0.226470518154 0.232346541994 0.225139690924 0.0393671458325 0.0464638919444 0.0721210062546 0.0652058473437 0.0727980489441 0.069475743339 0.146377403101 0.147841270546 0.157364583964 0.169967544666 0.162961576643 0.163688135507 0.153695971045 0.140223917866 0.154815274304 0.157460458795 0.165356253736 0.150768971529 0.165320051187 0.163744020488 0.133177827162 0.143578449734 0.163063976092 0.148488500757 0.186880468442 0.153029883361 0.168852310214 0.144817839236 0.183493098196 0.16367577587 0.147767272784 0.147687188567 0.164075138054 0.168705452589 0.161392920433 0.132534765042 0.154766041608 0.149414499828 0.145576557217 0.186084208163 0.165901662347 0.149128920765 0.153742006284 0.175201329509 0.146090630422 0.161672202019 0.168031911823 0.15768748388 0.152605178523 0.14358929792 0.159222856143 0.14586039418 0.151244472229 0.149339889326 0.125727972716 0.151529925609 0.203450036676 0.194986217471 0.192843589773 0.192004248179 0.198946853705 0.202034655336 0.194735870429 0.197390062155 0.205967080811 0.187074482741 0.173679535364 0.192730113791 0.187498820793 0.203203985607 0.204052975425 0.186370806955 0.184428712838 0.182999565721 0.222535792393 0.200873648 0.188899014225 0.192203777807 0.208005111614 0.182384645276 0.184037705225 0.182740791406 0.177549631149 0.174643342307 0.20005255491 0.181451046073 0.197344936488 0.172060390986 0.202165775813 0.177622921879 0.197550181919 0.195373313831 0.190875275066 0.18253037262 0.173366080571 0.181382900577 0.194116360457 0.17989694523 0.194986217471 0.19390780748 0.192735187036 0.186889353803 0.194252690611 0.181881177771 0.185754237755 0.182012475847 0.0362542669235 0.0433946419111 0.0395938166279 0.0499100911085 0.0488639489089 0.15152574063 0.154669324023 0.162831337388 0.176665753207 0.169806006203 0.167122046596 0.160378494232 0.145396880889 0.158882941041 0.165181343889 0.170013544811 0.158841213295 0.168049497633 0.168033584387 0.142281111905 0.150191536414 0.168801703096 0.149137142763 0.194382185315 0.156984666205 0.176687245859 0.151908690894 0.188914920448 0.165149458367 0.153257832095 0.154263633953 0.168972071115 0.175883337588 0.16793063653 0.137286345822 0.159386690667 0.152763325671 0.151315442368 0.191108253021 0.171290308569 0.156330262179 0.159994057675 0.180780718652 0.151317533557 0.168120728135 0.170508979962 0.162341150711 0.157837504705 0.149168793021 0.164327797527 0.148926027403 0.156192253462 0.154516403486 0.136169004568 0.157264478091 0.212122717934 0.202635989676 0.200296956824 0.197168424809 0.206818081536 0.207593997357 0.202129844535 0.201194661862 0.211287068634 0.196367072918 0.182828097116 0.200324517328 0.196191886272 0.212106652571 0.215234691898 0.196754062665 0.19012565359 0.188386972878 0.22899541165 0.20586463613 0.198258080181 0.201204812212 0.21275581912 0.190751178898 0.191559171703 0.187109767024 0.186199376174 0.182482910799 0.207969239625 0.184720534783 0.20291190811 0.176982703379 0.210810911009 0.181756418439 0.198660937707 0.204191111601 0.200384835859 0.187985877445 0.181554202617 0.190580156178 0.204342491841 0.191957847677 0.202635989676 0.20235052312 0.203098119616 0.198224746232 0.20315819974 0.190687755597 0.195275148422 0.188602851388 0.0403137962831 0.0283224608052 0.0352856337592 0.0279159520938 0.163417587591 0.169024399252 0.175174902733 0.188097949492 0.181377843525 0.182339093803 0.176249889638 0.158817342465 0.170474967966 0.180845899763 0.180001842158 0.173609983768 0.175496658011 0.182387588854 0.15630806646 0.162058645803 0.185720747493 0.161911269413 0.203372519665 0.168871283524 0.193698104443 0.163602773962 0.200347540332 0.178768462478 0.164992621499 0.165987648079 0.179610648238 0.188805808549 0.182291142535 0.147635227715 0.17074779736 0.163623388142 0.163626611748 0.205461058801 0.189137857415 0.173807850394 0.172761367844 0.189359771077 0.167597338583 0.181003226921 0.18512092453 0.177130623568 0.169726367846 0.160952549622 0.178668367454 0.164953979441 0.171439694143 0.167334380068 0.147661742228 0.171525073941 0.229212799489 0.217687295638 0.213458314816 0.212251579761 0.221837277994 0.220248120289 0.218836902814 0.21380890761 0.22315649106 0.21209021333 0.197773787684 0.213427195478 0.209691510886 0.226221325989 0.230427027381 0.212200169545 0.204828617154 0.204406576244 0.240127225685 0.217034360511 0.212651201199 0.2171572787 0.224326708779 0.203421180278 0.207247009432 0.201076078606 0.199634752126 0.197699005741 0.222082174457 0.197241869749 0.214400679015 0.192020712279 0.224886788727 0.195424460906 0.212817607264 0.215705919361 0.217495203752 0.20360497892 0.196993475509 0.204019508797 0.218720046506 0.204677118987 0.217687295638 0.217405004007 0.218489214457 0.2114130463 0.214943199157 0.204771656731 0.212548494217 0.204988233676 0.0127227257693 0.0140144534262 0.021205315625 0.187358882688 0.192125766059 0.198772205734 0.212374766999 0.205757524042 0.203466313878 0.198269360384 0.182075536022 0.193992765379 0.203217006858 0.204331991647 0.196763084721 0.20014614007 0.2043270841 0.180514052654 0.186571402116 0.206603961592 0.183553088594 0.228871000555 0.192232411854 0.214999691886 0.188338267885 0.22392031363 0.199986970587 0.189109313009 0.19048409561 0.20374267521 0.212452607057 0.205039311477 0.17250608603 0.194596025922 0.187333202847 0.187569910566 0.227244357321 0.209244321292 0.19501056691 0.196469921009 0.214533216851 0.189073691369 0.204563213894 0.206128514596 0.199072106837 0.193573357583 0.185159768848 0.200995780718 0.185854759801 0.193344854477 0.190733738309 0.173803583444 0.194263918799 0.250041858008 0.239752334137 0.236636793113 0.233547933384 0.243918830588 0.242901503454 0.239756635176 0.235879076786 0.246072511959 0.234292093183 0.220682667006 0.236686092505 0.233134556981 0.249085035733 0.25310029637 0.234845006722 0.226672837999 0.225370202913 0.263585873652 0.240269679341 0.235701118126 0.239046163418 0.247138992266 0.227341018945 0.229062028698 0.222899030248 0.223273394137 0.220071420598 0.244719204688 0.219434280561 0.237757183282 0.213627898609 0.247720909818 0.217420020786 0.232895251914 0.240194731922 0.23871780496 0.22484832656 0.219342733041 0.227741527766 0.241902288188 0.229541957119 0.239752334137 0.239709455557 0.241056941465 0.235717844261 0.239326740269 0.227988125157 0.23374803561 0.226117504317 0.0112944223112 0.0116717916683 0.180315550232 0.185624806253 0.191893759643 0.205310880338 0.198677901623 0.19742928403 0.192207723586 0.175346071899 0.186991091141 0.197103740487 0.197010559796 0.19035952375 0.192415460068 0.198031907427 0.173783350974 0.179445795208 0.200920544981 0.176980189616 0.221316628359 0.185291341794 0.209338461592 0.181164395028 0.216995536327 0.193672787851 0.182024521085 0.183353435463 0.196543976916 0.205721030365 0.198641096205 0.165041173069 0.187513275437 0.180170480519 0.180593580969 0.221064954649 0.203852710847 0.189347093282 0.189636739108 0.206917162302 0.183105626465 0.197802407018 0.200023916345 0.192826802002 0.18656768388 0.178054301819 0.194629767109 0.179904508376 0.187157083025 0.183925453374 0.166391248738 0.187799823192 0.244629672801 0.233720966397 0.230103074977 0.227589285523 0.237898238239 0.236372028201 0.234191133105 0.229406521443 0.239389014631 0.228371263352 0.214461216321 0.2301324354 0.226622699222 0.242825477857 0.247136546528 0.228837795311 0.220544275821 0.219592617307 0.256764787312 0.233410014962 0.229423132068 0.233225877209 0.240428033421 0.220595144087 0.223137647662 0.216631996383 0.216685610877 0.213950565713 0.238461242313 0.212870718316 0.230920602382 0.207535413146 0.241449474994 0.211057657029 0.226977435726 0.233226691247 0.233226664174 0.218957536154 0.213267398065 0.221167604245 0.235644719391 0.222751284499 0.233720966397 0.233655964463 0.235074683268 0.229091941731 0.232416283617 0.221592631388 0.228271321964 0.220395124759 0.00983853465038 0.190743576675 0.196127634014 0.202306832097 0.215593264431 0.208988714901 0.208014681254 0.202752493595 0.185879141861 0.197391448173 0.207587390017 0.207246286244 0.200818808941 0.202436920976 0.208560198594 0.184214467342 0.189842910423 0.211501430369 0.187510793393 0.23133568248 0.195719341744 0.219810583637 0.19153274103 0.227260064133 0.204197085901 0.192430421495 0.193730481177 0.206830708645 0.216081015857 0.209114627207 0.175418662775 0.197889059256 0.19054759391 0.191035254803 0.231501988345 0.214455580052 0.199914298524 0.200065006525 0.216984298716 0.193736240961 0.208206242937 0.210580217034 0.203385972256 0.196973524191 0.188474338185 0.205152454389 0.190585220715 0.197746685322 0.194398694242 0.176663593729 0.198324488838 0.254937931723 0.244068220209 0.240384243666 0.238035456407 0.248220627185 0.246646653405 0.244595211757 0.23972473422 0.249602645305 0.238680104062 0.224803483418 0.240405804306 0.236887764418 0.253050369586 0.257246240499 0.239074958955 0.230985078994 0.230097737916 0.266839316033 0.24359819756 0.239685124735 0.243538571704 0.250621888643 0.230851769093 0.233547789712 0.227075078845 0.226983547477 0.224362834405 0.248743336169 0.223247552937 0.241135379781 0.21805286342 0.251689329115 0.22150661959 0.237406179341 0.243353541542 0.243526559835 0.229448331316 0.22367406721 0.231427017129 0.245837459247 0.232818717405 0.244068220209 0.243969807123 0.245289155672 0.239200193755 0.242559723366 0.231895813526 0.238589343178 0.230870674099 0.185074856292 0.190966638679 0.196734232482 0.209858428647 0.203274864625 0.203041999473 0.197926990268 0.180506604609 0.191676192961 0.202741147438 0.201237653931 0.195771907735 0.19601488206 0.203403274574 0.179018659177 0.184172394714 0.206916175569 0.182062920029 0.225161143908 0.190059350101 0.215302655172 0.18583273214 0.22154130677 0.198918819466 0.186732254392 0.188033674574 0.200939126942 0.210648020541 0.203966819203 0.169461460201 0.19212652304 0.184698761261 0.185447917615 0.226382707341 0.210090272929 0.195464472523 0.194576182922 0.210724866996 0.188972223666 0.202749851185 0.205495551665 0.198311837225 0.191308228071 0.1827836447 0.199987032182 0.185758986102 0.192762378199 0.188912535548 0.170985151469 0.193136210612 0.250560354729 0.239150130043 0.235042618318 0.233086785485 0.243308731763 0.241203268173 0.2400598963 0.234273096316 0.244008938502 0.233948328457 0.219858019146 0.235053540533 0.231632132621 0.247968147104 0.252509320758 0.234318201257 0.225941055535 0.225341346975 0.261132458323 0.237859251815 0.23464919225 0.238863315744 0.244976238699 0.225403382703 0.228750181247 0.221868483944 0.221696049716 0.219446034661 0.243630311449 0.217754591911 0.235449762585 0.213055210612 0.246589228494 0.216230229788 0.232311179578 0.237695560547 0.239155145074 0.22460119101 0.218814227997 0.226159982187 0.240839111604 0.227492031494 0.239150130043 0.239069291581 0.240533107829 0.233959974366 0.236956995194 0.226761267667 0.234246750663 0.226212511867 0.0174000566092 0.0121264323023 0.0260599074483 0.0195345881682 0.024716388707 0.0269505563041 0.00951579401108 0.00952789620934 0.0312682326376 0.021380704298 0.0249359987416 0.0321633377276 0.0221499569656 0.0250465767598 0.00649156808823 0.035247677191 0.0178478945632 0.0452164673822 0.00796037757126 0.0457327534983 0.00806434689496 0.0382898633561 0.0225581753549 0.00212523780258 0.00635071079008 0.0188691425113 0.0279463141708 0.0242753243704 0.0170805625395 0.00904309726647 0.0106287996797 0.00382969063087 0.0441552932431 0.0411601284763 0.0356126616505 0.0116846154421 0.0333280322148 0.0231237862627 0.0194233884188 0.0266405905658 0.0193741592831 0.00647009737397 0.00313195595137 0.0192809490983 0.0198825976901 0.0184223649953 0.00589033700008 0.0274923472821 0.0151107135826 0.0780209291989 0.0597893086125 0.0526478480904 0.0519801851129 0.0640907142958 0.0582643284706 0.0646025548306 0.0523589334483 0.0615531482061 0.0598796956679 0.0449628875056 0.0527423072945 0.051324556652 0.0686409960047 0.0805660524071 0.0623345264313 0.0443847427682 0.0466830846559 0.0795542446923 0.0559362746494 0.057180756356 0.0643259703348 0.063211257271 0.0437185245372 0.0513548290636 0.0393714351609 0.0417127661704 0.0422926634719 0.0628873923745 0.0360959787877 0.0527047257983 0.0325588237609 0.0667976247829 0.0335624409497 0.0547114607912 0.0561052885987 0.069799650238 0.0449738444715 0.0432370258673 0.0470356297838 0.065014861143 0.055075271212 0.0597893086125 0.0609892947965 0.0676680770887 0.0593149961508 0.0554983088895 0.0479751568133 0.0662596895747 0.0497728280187 0.01649842894 0.0270940163297 0.0209738779605 0.0204474607028 0.0105739124512 0.0137437284214 0.0226997190651 0.0150100972387 0.0323011347894 0.008153166122 0.0475382748664 0.0187641297777 0.0164404692163 0.0144508320868 0.0210394349685 0.0305556358824 0.0486242638836 0.0214513889049 0.030175982789 0.0140964441541 0.0400575333251 0.0288561044949 0.0169609734513 0.0143755139823 0.0284025548343 0.0224203703593 0.013792641556 0.0304826758057 0.0219984307058 0.027438003111 0.0138498079587 0.0391014082904 0.0268826742795 0.0187520448645 0.0110691342578 0.0424853466078 0.0102534616754 0.0160596134656 0.0266767705353 0.0127998581546 0.0173245890268 0.0173211449901 0.0131739480428 0.0158792885006 0.00835247262611 0.012879803971 0.0267489620569 0.00596710410688 0.0651865992814 0.0501303259948 0.0467631748034 0.0452702235248 0.0544981935174 0.0559804285713 0.0526556031199 0.0527154428326 0.0609295585885 0.0469864956417 0.0318305696362 0.0467870360621 0.0428839020559 0.0596952123639 0.0682904935263 0.0490145651142 0.0373470202066 0.0372051258398 0.0783094346225 0.0573164765228 0.0464381891239 0.0518006344835 0.0637213875725 0.037328539174 0.0398159521041 0.0364375689163 0.0327311081772 0.0300536558581 0.0548465086523 0.039011355091 0.052932116237 0.0253006236285 0.0581288867478 0.0321437027692 0.0554616174667 0.0518535767409 0.0554912699649 0.0361633495108 0.0300500951908 0.0376868875379 0.053920841562 0.0443787428504 0.0501303259948 0.0504194914628 0.0549441033997 0.0487769599128 0.0505784847321 0.0377653392745 0.0513716565226 0.0379306609676 0.0148794236891 0.00906915259144 0.016817766784 0.022666965263 0.0181414830259 0.00943690730089 0.0254552863313 0.0160108606893 0.0217205062473 0.032481574081 0.0125336713193 0.0301048876528 0.0144114156753 0.0283145714635 0.0236499286545 0.0356994826174 0.00980454037789 0.0380382046842 0.0138535372004 0.0270349861178 0.0179310476065 0.0105970849446 0.0106446588122 0.0120133548138 0.0166102052911 0.0152998463146 0.0290596443937 0.00834773888805 0.0173051001714 0.012006228531 0.0322817993752 0.0342847484172 0.0334965405575 0.00578703582339 0.0267498608594 0.0233284219943 0.00836772170811 0.018341518653 0.0127097608473 0.00603575216863 0.0145919151911 0.0109073587446 0.0221533308504 0.0160929276419 0.00858765501382 0.0355326614102 0.0120318414802 0.067539995406 0.0482957753815 0.0406895564252 0.0402187129364 0.0525505335689 0.046337446086 0.0538706159523 0.0410532995186 0.049945680623 0.0498002070389 0.0356573113194 0.0408170339224 0.03998131445 0.0570983300794 0.0703114871106 0.0527515586 0.0326360388464 0.0357703215059 0.068007735392 0.0448145788863 0.0463929144792 0.0539072356938 0.0519007533329 0.0322898342044 0.0406511091494 0.027662402635 0.0308780067072 0.0322628111538 0.0510866561777 0.025649802677 0.0412344607513 0.0218269690426 0.0551778627391 0.0220763570226 0.0446670171533 0.0446820028935 0.060359178521 0.0338187065026 0.0337343667693 0.0362783001067 0.0544202026485 0.046225200341 0.0482957753815 0.049817335882 0.0576169439653 0.0494636606912 0.0440581542748 0.0371733017103 0.0573289834367 0.0394045266195 0.00716257307188 0.0232372547893 0.0296783885761 0.0325204811189 0.0212497773179 0.0292620976856 0.016439517739 0.0284506794787 0.0345599570635 0.018188478274 0.0407426749726 0.0271225123173 0.0318719827355 0.0364884226289 0.021914442716 0.0227786086069 0.0376078412936 0.0256329664506 0.0146652900265 0.0257412263731 0.0241649286356 0.0229439407062 0.0136364254073 0.0100256292567 0.0184950349026 0.0419365600148 0.0198805169881 0.0290470401931 0.0259349477967 0.0239003990661 0.037192775807 0.0407761513521 0.0178664543062 0.0194803036782 0.0346542430628 0.0113663417752 0.0228609184952 0.0221099611853 0.0198250112005 0.0281996969801 0.0190943935979 0.0354944682629 0.027502181016 0.0232700909093 0.0459502516894 0.0236639254111 0.0598327139142 0.0386582188727 0.0283018389706 0.0317793366202 0.0424462733589 0.0338166029949 0.0467225177273 0.0306128740578 0.0370882424143 0.0426365040044 0.0306518342203 0.028353389025 0.0289452185335 0.045605512774 0.0609187137277 0.0456096520871 0.0247062397964 0.0307047013286 0.0541889879817 0.0321986089116 0.0369056583991 0.0461635888574 0.0394191509814 0.0202672234717 0.0339543216254 0.0203331670034 0.0216962768111 0.0275014797571 0.0396207876643 0.019383687668 0.028134515225 0.0203542968498 0.0436399460151 0.0166979961449 0.0388212853573 0.0305366541923 0.0544557347145 0.0282093601187 0.0297076171269 0.0264388860156 0.0445799816631 0.0375526043804 0.0386582188727 0.0404030430489 0.0493940445937 0.0396161416365 0.0302144214958 0.0282464575757 0.0525063270042 0.0345543356833 0.0213910735206 0.0254312508458 0.0257342205184 0.0165221855109 0.0259539784544 0.0158835031429 0.0233831935041 0.0339323139257 0.0162106434865 0.03399674531 0.0200386549433 0.0299171917849 0.0320747556874 0.028046974659 0.0176794481153 0.0368388291758 0.0185008751091 0.0214981879903 0.0241691252685 0.0175651807134 0.0158907875652 0.0122369735355 0.0109776452166 0.015527622801 0.0354957768624 0.014819189185 0.0241388300803 0.019062303667 0.0287269405771 0.0357916006232 0.0364633531028 0.011219279972 0.0225748368915 0.0292557862917 0.00702356235419 0.0224830610185 0.0181511844266 0.0136555064616 0.0214280218685 0.0152776051594 0.0301845131917 0.0223495731118 0.0167956602264 0.0389756608983 0.0178914144765 0.0630604023291 0.0430080788574 0.0341031378994 0.0365519407683 0.0470199755056 0.0406269394348 0.0499352678431 0.0370580183618 0.0441324571638 0.0450148609009 0.0313792244576 0.0341228348643 0.0333031398497 0.0505703458181 0.0642434790604 0.0476293944045 0.0288843329998 0.0334940865056 0.0612862785232 0.0392274534904 0.040346557275 0.0490663667656 0.0464449738085 0.0249899515531 0.0366261362911 0.0249803957153 0.0245501998624 0.0286632518672 0.0448796558538 0.024023128213 0.0352285170514 0.0213779538928 0.0486983458625 0.0204530056806 0.043719713027 0.0366461987398 0.0563678077617 0.0312461294161 0.0303695774583 0.0296679302141 0.0480887950749 0.0392929320337 0.0430080788574 0.0443169978812 0.0521158245642 0.0424602914349 0.0361040078467 0.0312787145737 0.0538034695951 0.0366723303295 0.0189709615579 0.0257111384326 0.0217335533305 0.0223113040739 0.027967478664 0.0234852927562 0.0436322798908 0.00521461133699 0.0364760096592 0.0272343470762 0.018116669378 0.0277674536165 0.043201573857 0.0213285903305 0.0304538340544 0.0273512817022 0.0293661558134 0.0151289313008 0.0243064809516 0.0249356313294 0.024322988602 0.0196946095768 0.0139855052687 0.0405716245072 0.0223909261135 0.0287463352875 0.0239629151594 0.02510185342 0.0224608355139 0.0285849443101 0.0178309380123 0.0374526670612 0.0202528662781 0.0157931802813 0.00761690092841 0.00770880409418 0.0208073209186 0.0270724449013 0.00859765119326 0.0186885305588 0.0137210864523 0.0193875507074 0.0459800217652 0.015147695259 0.0594631949225 0.0420406539118 0.0381002519704 0.0316288068581 0.0464453358384 0.0430647918893 0.0450678160713 0.0372936406921 0.0478953066773 0.0442824164473 0.0325191406563 0.038458833697 0.0379877190085 0.0530105166244 0.0661049064219 0.0485790002934 0.0252530768184 0.0252616659632 0.0666194586026 0.0445179442461 0.0429682351205 0.0475934680999 0.0500384221554 0.032835448251 0.0334890161181 0.0210425012042 0.0304165560005 0.0269992218058 0.0464858418191 0.0236376116141 0.0409389296383 0.0119097107153 0.0510342004912 0.0165862044563 0.0364051705359 0.0459765436849 0.0532158463913 0.0240697227805 0.0285944307415 0.0355069812685 0.0512442268801 0.0481793211849 0.0420406539118 0.0442662890139 0.05312949223 0.0494910921385 0.04499219501 0.0346009573898 0.0502278687979 0.0300299308142 0.0232712606741 0.0301762988443 0.00654363258115 0.0383536762695 0.0073760670349 0.0549696818035 0.0187358469678 0.0230006125585 0.0248620926321 0.0112006081505 0.0374302122761 0.0507791829944 0.0291552453818 0.0203658790931 0.0243768337268 0.0405838171216 0.0318523232399 0.0264398107213 0.0241549255092 0.0342108755533 0.021966370396 0.0115756992435 0.0406700397742 0.029726336077 0.0360763557135 0.0237708872008 0.0347952708329 0.0167090325333 0.0113314779127 0.0183460384347 0.0474972085699 0.0106130759732 0.0188803713912 0.0264255690706 0.0134988723184 0.025504997589 0.027405254367 0.0141844800377 0.0190580694839 0.0107985492476 0.0215712414492 0.0353201768965 0.0121413245456 0.0563908913649 0.043304104353 0.0426898087169 0.0394771848203 0.0476531796683 0.0529352282385 0.044079995111 0.0508430194559 0.0587105683651 0.0390993709526 0.0249779507582 0.042747127932 0.0380731496814 0.053623295371 0.0609715530482 0.0414798961609 0.0320700568798 0.0299307088952 0.0756552859532 0.0564187262812 0.0401315540974 0.0437426630802 0.0619393205223 0.0343990170803 0.0321000148697 0.0335451545545 0.0286757976711 0.0228629245983 0.0490706959168 0.039335399235 0.0517108395757 0.0207931533413 0.0521719534959 0.0306385429933 0.0529657791748 0.0494073245643 0.0465244230409 0.0294935309982 0.022387741137 0.033135656028 0.0474637239198 0.0404408008061 0.043304104353 0.0434045699957 0.0474449484031 0.0438694244062 0.0478136370998 0.032205230585 0.0422362620829 0.0295056722481 0.0179188644218 0.028584000913 0.0304649482776 0.0216593575612 0.0411663680687 0.024518088592 0.0182283990095 0.00873472135302 0.0327024039756 0.0205661418233 0.0530659348987 0.0158790060301 0.0434559310925 0.0108179884547 0.0451149656888 0.0268976993783 0.0108384529361 0.0115766662184 0.0275567255166 0.0316971869351 0.0249562865626 0.0177154908046 0.0178955252093 0.0182906823384 0.00749636648732 0.0477714342488 0.038203551745 0.0295629098692 0.0151014852938 0.0424264199032 0.016651504005 0.0233361298106 0.0296064297332 0.0193940010564 0.0143559792056 0.00822362335298 0.0204472045407 0.0141062231631 0.0150969431722 0.00961358010122 0.0230069897016 0.0136344497691 0.0781873312389 0.061922358127 0.0567947708263 0.0549074799203 0.0663268940474 0.0637272258979 0.0651276548535 0.0583171690756 0.0677292446726 0.060191106251 0.0450946120505 0.0568830058441 0.0543288790766 0.0714589426004 0.081454303397 0.0624243269582 0.0472314410611 0.0479124253475 0.0858307227677 0.0627914211132 0.0589518720089 0.0648453559762 0.0697319385158 0.0476614784634 0.0522209851297 0.0435983831723 0.0443141443319 0.0427834724857 0.0660408790701 0.0423473900154 0.0592034189384 0.034371623803 0.0697342027164 0.0381294864628 0.0595840773258 0.0612390496755 0.0689539919437 0.0466415215244 0.0431628346368 0.0495434525796 0.0666477691564 0.0566659496435 0.061922358127 0.062735676927 0.0682003243846 0.0612178236271 0.0603270178223 0.0499653486319 0.0649071391116 0.0500436367076 0.0339571590708 0.013301666182 0.0294613107742 0.0254168369791 0.0187830848109 0.0337456927151 0.0151760493477 0.0360233288761 0.0161149469416 0.039042024755 0.00229536147376 0.0468249873675 0.0157583667263 0.0310761119725 0.0155497505138 0.0090301180649 0.0126416375855 0.0110159110748 0.0254616235228 0.0242814349621 0.0236690592627 0.00236651040215 0.00842631126501 0.0122777482071 0.0387720000407 0.041740632348 0.0402615455592 0.013434165421 0.0259957062278 0.0279382933191 0.0176919201528 0.0210030594474 0.0190515742522 0.00605687883869 0.0125440326934 0.0183812478057 0.0234838433483 0.0213287295776 0.0102611349583 0.0367608397052 0.018305882063 0.0762459812633 0.0565232340764 0.0480647092458 0.0470080033305 0.0607210126742 0.0515562009886 0.0623134210102 0.0445281266442 0.0542656274802 0.0589172543408 0.0450145677229 0.0482495838943 0.0484339717661 0.0652417317109 0.0793204584838 0.0620264746962 0.0400173133601 0.0433798691859 0.0725797896424 0.0481813389954 0.055284998644 0.0628522019597 0.0554630904838 0.0404443368976 0.0493602212467 0.0332419109523 0.0398075629785 0.0413677301877 0.0589057964055 0.0278286603835 0.0454781228443 0.029186418008 0.0632373035893 0.027173160002 0.0466041993476 0.0516248397682 0.0694549695494 0.0414073101396 0.0429302830553 0.0451579600752 0.0633364240737 0.0552241510377 0.0565232340764 0.0584051969715 0.0667610558404 0.0584355181495 0.0512925402879 0.046128531904 0.0664605075068 0.0478081299752 0.0403784266437 0.00849189377523 0.0578244327435 0.0213782060591 0.0260735654855 0.0286868543518 0.0107633182991 0.0429130184438 0.049112317265 0.0332704837119 0.015321761445 0.0277153223765 0.0397499694056 0.0358925712782 0.0304111104426 0.027505083584 0.0362377250231 0.020296537393 0.0117771620381 0.0454349567531 0.033203078919 0.0404550655622 0.028050968973 0.0329121522515 0.0157822820327 0.0128529655968 0.0212960295437 0.0481179953609 0.0170390332384 0.0197624290813 0.029414246654 0.0176689832365 0.0290804953813 0.0317312604538 0.0173770193477 0.0255273516204 0.0167054579942 0.0259572392414 0.0381174742782 0.0168503391466 0.0510138836142 0.0385473731826 0.0388016187658 0.0368178557203 0.04276997017 0.0503778656709 0.0392641023806 0.0497809957465 0.0564266766196 0.0332095636962 0.0188695135337 0.0387787813789 0.0331502464834 0.0483979502244 0.0548055143427 0.035228159998 0.0294466884888 0.0272290139414 0.0724784935896 0.0548011964122 0.0344489111929 0.0380672364964 0.060026483594 0.0303284751736 0.0272545178904 0.0327801045139 0.0239410636139 0.0177611799925 0.0444007657806 0.0400513163792 0.0497786040376 0.0204852863736 0.0470867569115 0.0308376722615 0.0532970554221 0.0452748414675 0.0407821021663 0.0268849282918 0.0168086928609 0.0279020988959 0.0414910752652 0.0343097482389 0.0385473731826 0.0381367753126 0.0413101510581 0.0377465227401 0.0435171810289 0.0268955341281 0.0365057689544 0.0254190380854 0.0372013435687 0.018490846954 0.023945912793 0.0440067159299 0.0253810829497 0.0423576975539 0.0266775910883 0.0276067076791 0.0155964706338 0.0512008225103 0.0249293141549 0.0224091031216 0.0208182862063 0.0201027144741 0.0218258381078 0.00416795120123 0.0250523436876 0.0291897433081 0.0336104473732 0.0126474260294 0.0179897427224 0.0236687976248 0.0357767564718 0.0479080621505 0.0493839683144 0.0213027411218 0.0127962715487 0.0389577422313 0.0207982711984 0.0248766740175 0.0272195414617 0.0164396805465 0.0239043019084 0.0253433507519 0.0357454256498 0.0316012688091 0.0218891275488 0.046006369731 0.0279699723289 0.0753592714385 0.0538300830176 0.0427234551893 0.0442120918411 0.0575724115573 0.0438794711912 0.0617203772126 0.0368175055007 0.0452394206226 0.0586608041232 0.0462735571883 0.0428932945735 0.0449361857949 0.0608531076963 0.0771129194067 0.0618336393757 0.0380658785095 0.0436894906412 0.0627047512052 0.0382553066725 0.0531191059331 0.0620656917263 0.0459127944105 0.0362899248016 0.0492167826913 0.0301637294202 0.0380460619309 0.0426550687382 0.0544072702012 0.0218332584664 0.0362322493618 0.031226533309 0.0587827461321 0.024852970378 0.0417700516252 0.0442659791406 0.0702283886386 0.0412678777108 0.0447863140602 0.0428258891548 0.0608093994885 0.0534400200237 0.0538300830176 0.0561072465285 0.0656581075266 0.0558523559264 0.0444818994194 0.044500683531 0.0680831562837 0.0488303143353 0.0536500814259 0.0217777943298 0.0182308184172 0.0213271967089 0.017640751735 0.0384313065823 0.0491331799987 0.0285490579199 0.0236508921207 0.0203518801552 0.0410544418794 0.0345949638149 0.024163451076 0.0208164937907 0.0331698057229 0.0214370208347 0.0129093004816 0.0378630437774 0.0285080143021 0.0349561724295 0.0213643314537 0.0378207671811 0.023156173716 0.0144257922467 0.0164328495191 0.0458898771597 0.0137078716838 0.0180512548912 0.0303444429836 0.0166949845236 0.0239375979831 0.0248141920509 0.0164642247089 0.0221495310709 0.0136759772653 0.0203492925476 0.0297801495496 0.012327304817 0.0589145352863 0.0454329273476 0.0436909431618 0.0429329583507 0.0496681092117 0.0546717056664 0.0472190839202 0.0531335572006 0.0601545762173 0.0404993410456 0.025351107679 0.0436326686228 0.0384109589864 0.0546942484238 0.0614849395874 0.0420121432296 0.0350678306139 0.034181943564 0.076607832627 0.0574938051354 0.0407284888751 0.0455392391661 0.0634559846389 0.0340449392101 0.0346886110004 0.0365094436091 0.0283668149646 0.0247441178114 0.0505723350307 0.0414268809452 0.0526572068528 0.0247442404176 0.0533283030157 0.0333480302269 0.0569330422551 0.0488445949836 0.0485166318417 0.0334202567015 0.0241088713298 0.0327605074387 0.0477602460683 0.037977730422 0.0454329273476 0.0450703466816 0.0481686005759 0.0425586735361 0.0473020173719 0.0326189496433 0.0442268465034 0.033265894657 0.0406164099076 0.0563656658624 0.0369501005762 0.0596090654503 0.0304778147307 0.0384204581231 0.0270915625525 0.0693260105947 0.0372136698768 0.037023479673 0.0317062741776 0.0317712182279 0.034804368211 0.0223060266021 0.0435100417127 0.047004084469 0.0362329082931 0.0256397756303 0.0233719185841 0.035627421568 0.0521858971919 0.0650552610656 0.0654396577163 0.0372909018916 0.0208395075735 0.0532980934411 0.0387978654626 0.0393043110859 0.0433091405548 0.0302415541747 0.0340064333643 0.0421117035102 0.0478257550421 0.0466283489206 0.035247716101 0.054937797688 0.0434605477125 0.0930034854114 0.0710598303107 0.0590352084097 0.0604460830393 0.0745555173324 0.0571183440581 0.0793617023165 0.0484952151811 0.0566053521726 0.0768259736839 0.0646780127707 0.0592344115045 0.0623544010763 0.0773513341111 0.0945306112415 0.0799933849368 0.0552426925494 0.0610368934489 0.0726391747026 0.0486086004123 0.0708765879781 0.0800292786375 0.0558578611463 0.0537011940437 0.0672777153371 0.0461852109805 0.0561622363977 0.0610513630375 0.0708198771797 0.0350965268909 0.0484124521051 0.0486964562823 0.075256407672 0.04111323881 0.0515712020719 0.0592740141065 0.0884497661149 0.0586350703024 0.0632023881374 0.0606979638812 0.078320587954 0.0707259147977 0.0710598303107 0.0736338137855 0.0835800603427 0.0732101393079 0.0599496433571 0.0625510181289 0.0864139348042 0.0667637091982 0.0350728110176 0.0244007367668 0.0194418255357 0.0278046770383 0.0387260088041 0.0187288873365 0.0305873095592 0.0241694671072 0.0257451620145 0.0153815378858 0.021353060898 0.0215496497445 0.0200984831417 0.0151985245998 0.0110228413253 0.0387908454863 0.0190085239065 0.026530841158 0.0213969993651 0.0238500818663 0.0245406806879 0.0294364678495 0.0140120927131 0.0331219747793 0.0211978333105 0.0108265764026 0.00897607251111 0.00677241917437 0.0173770286262 0.0245814114194 0.00563326172159 0.0204952689406 0.0138047466575 0.0168569359655 0.0438273371107 0.0133334601758 0.0592456323654 0.0408071286879 0.0355509485261 0.031081872872 0.0452073820968 0.0410054777303 0.04495248578 0.0356498623047 0.0456510923283 0.0431383529174 0.0306345228853 0.0358468154266 0.0353265194622 0.0511623196634 0.0646276367384 0.0471382277373 0.0240229200194 0.0254553208119 0.0642596486532 0.0418982548195 0.0409421092482 0.0466753277343 0.0478887558589 0.0293373407147 0.0326161841904 0.0195735225778 0.0272641873276 0.0254981680323 0.0447040552558 0.0215611560931 0.0381253844376 0.0111199895155 0.0491745036263 0.0145691145727 0.0368088577033 0.0425091408325 0.0528722213324 0.0238501784613 0.0272505069941 0.0325614342759 0.0492627638247 0.0449950723976 0.0408071286879 0.042880253884 0.0517160325388 0.0466599102029 0.0415966376912 0.0321745745193 0.0499813496989 0.030016506883 0.0195067443548 0.034042657808 0.0387050626348 0.0607833234632 0.0322025711177 0.0402462250605 0.019510297358 0.0548235012014 0.0429595771314 0.0252279466187 0.0223273919125 0.0407231129516 0.0368354815518 0.0293953937656 0.0290463736365 0.0328461116776 0.035143771121 0.0215923461726 0.0549385181088 0.0388660143681 0.023623079783 0.0246479181765 0.0539903453268 0.0196033552608 0.0309786977565 0.0425462811063 0.0289476729073 0.0283702078661 0.0228452205014 0.0295421719187 0.0252210967901 0.0230772437036 0.0240590413191 0.0135013916275 0.0218148808282 0.0760114892469 0.0634029220478 0.0607879784498 0.0607163777171 0.0675569624906 0.0711706523576 0.0650651993965 0.0685468614076 0.0760185520266 0.0575507664821 0.0425210776149 0.0606855343213 0.0555229333703 0.0720565340813 0.0774071503121 0.0581416488422 0.0527363337967 0.0521637490438 0.0926239124524 0.0722776295267 0.0578783465588 0.0627903946651 0.0789206671037 0.0503951188312 0.0527646858335 0.0526096570134 0.04527903594 0.0427992547678 0.0682205850017 0.0547141937568 0.067807383273 0.0414244261778 0.0707718291801 0.0483573376556 0.0717861683592 0.0644987372167 0.0647006499463 0.0513517515126 0.0419564687506 0.0495341433982 0.064364362053 0.0518587226049 0.0634029220478 0.0627776825589 0.0645131806296 0.0578143875392 0.0631581345522 0.0499505499876 0.0600491597535 0.0513712453732 0.034450057717 0.0238219725869 0.0465207238958 0.0139550218762 0.0437148052069 0.00245848843359 0.0406705408218 0.027996084796 0.00617634742576 0.0042140797155 0.0226171443798 0.0276920922451 0.023535776763 0.0178395839466 0.0140356715542 0.0166022608919 0.00395476408572 0.0457707350729 0.0404789526881 0.0327219703849 0.0114454148489 0.0362680793242 0.0217463268752 0.0196365744138 0.030477863462 0.0206103562172 0.0102714385383 0.00460445870022 0.0203218526125 0.0212018784388 0.0183586998171 0.00829854248447 0.0218572079865 0.0144443422105 0.0767877803745 0.0594466868469 0.0529468255635 0.0532520366965 0.0637146030163 0.0600960587419 0.0639510014921 0.0553587219012 0.0637257572972 0.0581054381403 0.0427611569751 0.0529559974475 0.050488809742 0.0679805704334 0.0784814462329 0.0599903091584 0.0453120703317 0.0472956572051 0.081207762841 0.0585100350392 0.0557351056658 0.0628545029747 0.0657746195642 0.0431635890019 0.0505766161122 0.0416755044323 0.0404647152471 0.0409948112567 0.0627553433594 0.0398795409571 0.0548556307871 0.0337749376373 0.0662751646447 0.0362650420971 0.058753352182 0.0559719307576 0.0677796526261 0.0456851568449 0.0415580901724 0.0455930571155 0.0632638085815 0.0519953365762 0.0594466868469 0.0601182129397 0.0655928087235 0.0568949371894 0.0552051980285 0.0466604473635 0.0640269526764 0.0493532376221 0.0428114080859 0.0513187599237 0.0353300715155 0.0136841990953 0.0339659047401 0.0392151216672 0.0331598693815 0.0346578423583 0.0330024342773 0.038276947888 0.0230224626975 0.0145213724276 0.0501490398304 0.0358866478447 0.0428680377048 0.0326728263648 0.0285835471973 0.0062386073831 0.0158573158736 0.0257280664001 0.0503050577684 0.0188536314021 0.0230369129683 0.0250256284162 0.0171680840121 0.0324912467925 0.0363869094834 0.0177612063031 0.0250245943265 0.0179627656197 0.0294197347692 0.0465091219099 0.0203095862842 0.0478795030574 0.0357982238192 0.0376241763265 0.0315623347361 0.0401002610089 0.0474897864671 0.0350582543035 0.0460362585126 0.0538318698513 0.0325365856585 0.0213227378076 0.0377990397126 0.0335693240845 0.0471274543161 0.054885621667 0.0360897261843 0.0253548565171 0.0209548211511 0.0704293001167 0.0527939534511 0.0346935966786 0.0364130601336 0.0572327947958 0.0318390512051 0.0244040617426 0.0286768378156 0.0261566126157 0.0173773142125 0.0423900706098 0.037171182215 0.0481110975007 0.0159454061909 0.0456669379702 0.0274665151098 0.0469607798152 0.0462908705234 0.039158298024 0.0212460794033 0.0171141394847 0.0299475002492 0.0420369583583 0.0395076063195 0.0357982238192 0.0363456286371 0.0414629362584 0.0409765644808 0.0445581081298 0.027819794971 0.035217696192 0.0204081888775 0.0536229831874 0.0147412411033 0.0555896434003 0.0257773616784 0.0440411962897 0.0177255540439 0.0193829905731 0.0239978426404 0.0256940264499 0.0393410530504 0.0352411367288 0.0221731284836 0.0182277280594 0.0123391761488 0.020654487001 0.0493059373458 0.0475681206642 0.0451873269558 0.025880190654 0.0390288508413 0.0309214453223 0.0313196281309 0.0264802426158 0.0266703711652 0.0198006836859 0.019520741807 0.0278514155812 0.0221034761072 0.026690732588 0.0196134707881 0.0412756734602 0.0265178671443 0.0867397306197 0.0682110021203 0.0611304199736 0.0570363597121 0.0725160802101 0.0632079822129 0.072465417084 0.0544582268199 0.0658094706322 0.0704919404627 0.0569226677141 0.0614294730407 0.0618668588016 0.0780403185985 0.0918397178914 0.0740264863472 0.0508436174709 0.0525989509117 0.0847012354501 0.0597190404435 0.0681089165817 0.0742388101657 0.0664380426823 0.0545769816583 0.0603124830305 0.0434291774759 0.0533790593767 0.0526730162139 0.07136820435 0.037381607524 0.0577096830472 0.0386410478706 0.0759869972218 0.0373998132392 0.052181569916 0.0660810261842 0.0798969360666 0.0510826914494 0.0540234946407 0.0588039304452 0.0763291167757 0.0694007087698 0.0682110021203 0.0704124305268 0.0789271737863 0.0724038719993 0.0657040131714 0.0590995697111 0.0765722724394 0.0574793382897 0.0411299322154 0.0539204969946 0.0448971315706 0.0161163886115 0.0424587591617 0.0433134103038 0.0424269061508 0.0282464178608 0.029088245744 0.0394507490162 0.0585960215177 0.0377541724349 0.0451043153526 0.0458829699472 0.0321616028135 0.0557124503126 0.0614547711288 0.0392057260171 0.0177559051904 0.0564954090624 0.0332181102648 0.0405454141429 0.0436595776199 0.0393478274847 0.0472383546112 0.0407140770645 0.0569892712332 0.049354627832 0.0439022116296 0.0637553975882 0.0455099918129 0.0646100490562 0.0426539647843 0.028431151743 0.0378227253234 0.0447657124399 0.0278050988657 0.0537117960824 0.0274550112394 0.027109787916 0.0505464931112 0.0435971104256 0.0283504838988 0.0329648689469 0.0443909167392 0.0625701957077 0.0530229796398 0.0343871989004 0.0426240079793 0.0396498771349 0.0213457948378 0.0419360502864 0.0525916757896 0.0283713421426 0.0258314536376 0.0439902997437 0.0303827915475 0.0319578433558 0.0415135400801 0.039276736137 0.0267873256159 0.0192565547583 0.03784586336 0.0425882783228 0.0300770949457 0.0411024644378 0.0235206108502 0.0627241786637 0.0400442811976 0.0439864971889 0.0336917004451 0.0474026229045 0.0427127785344 0.0426539647843 0.0447428873557 0.0545498150062 0.042821329639 0.024917555179 0.03656154349 0.0624685036836 0.0466134489709 0.0464211898017 0.0148261628982 0.0328892390973 0.0156382282878 0.00785541359441 0.0118772597644 0.013233811524 0.0262299374219 0.024092235964 0.0223052815816 0.00384479195153 0.00794260597132 0.0107672255318 0.0397240757159 0.0410257993957 0.038935751255 0.0130110302225 0.0282819504394 0.0262547979726 0.0181226466418 0.0211289335782 0.0182692961983 0.00567846812613 0.0110316054291 0.0179271098732 0.0214946645806 0.0199268176637 0.00875561948865 0.0354271933282 0.0171443662393 0.0766374045646 0.0572939392451 0.0492966948245 0.0478544225205 0.061544844174 0.0531089657897 0.0627070970919 0.0461191081801 0.0559975469422 0.0592703107177 0.045198111152 0.0494826907565 0.0493817865713 0.0662451441692 0.0799247658409 0.0623534855885 0.040791427856 0.0437433118438 0.0743922416686 0.0500539193261 0.0559768175635 0.0632764008918 0.057263588801 0.0415573436738 0.0497522962913 0.0342757495228 0.040553840562 0.0415809282216 0.0599519771943 0.0293556022635 0.0472716928172 0.029440558171 0.0642586667075 0.0282006776448 0.047766217977 0.0531327322745 0.069584244341 0.041867047458 0.0430265316703 0.045945636772 0.0640480645613 0.0558532996544 0.0572939392451 0.0591020248311 0.0672221887664 0.0591786574244 0.0527229760336 0.0467939943149 0.066452337221 0.048010057481 0.0426662887331 0.0439949614302 0.0453715955589 0.0447907897725 0.0421151539334 0.0471958607455 0.0276193449237 0.02275846799 0.0605343055308 0.046215555164 0.0540603153716 0.0428037480825 0.0313795261955 0.0125172459763 0.0208290281635 0.0350621941996 0.0569552641601 0.0299458792012 0.0309610087229 0.0366049420462 0.0292005950568 0.0426658539169 0.046550389751 0.0288402250941 0.037809939324 0.0299619998536 0.0401350153602 0.0525912191682 0.0310232912468 0.0370292782789 0.029362531619 0.0351405044644 0.0315813022426 0.0330583005053 0.0477931213785 0.0264557013575 0.0497838425895 0.0546676669155 0.0214340498054 0.0132666230167 0.0351245595911 0.0284887636401 0.0394555006358 0.0430107543153 0.024001610215 0.0265746605534 0.0212961513987 0.0686985381679 0.055308045978 0.0261438582005 0.0256904480148 0.0588231449389 0.030013743601 0.0177237336601 0.0340579413817 0.023239083403 0.0129096702298 0.0366510262704 0.0448114548715 0.0501265325324 0.0238298010541 0.0385248366721 0.0348349791307 0.0531258509004 0.0432244806886 0.0265717263897 0.0222990802833 0.0105547424712 0.0247433535936 0.0319856839059 0.0308056274733 0.029362531619 0.0282110860995 0.0297242114734 0.0317000113527 0.0411003820059 0.0219347750579 0.022168792283 0.0155469808263 0.0395024062804 0.0287670845389 0.00713435108734 0.00340555052012 0.0220748170877 0.0261325674803 0.0225086838709 0.0197301659136 0.0143163055062 0.0179547860371 0.00552830002832 0.044759241534 0.0400746442935 0.0324889323762 0.0105741309237 0.0352991764051 0.0223168808509 0.0183878728912 0.0306904112299 0.0206021475442 0.0104104939928 0.00670538845305 0.0199820984844 0.0226302237702 0.0187233601872 0.00907856174289 0.0219615297998 0.0143637742128 0.0753337380263 0.0579434032885 0.0513377916805 0.0521965044751 0.0621695045027 0.0588078748161 0.0626419440897 0.0544695113035 0.0624620281749 0.0565017669041 0.0411290585403 0.0513195597187 0.0487054775012 0.0662358469866 0.0766400648909 0.0582604755345 0.0441853596565 0.0463597810918 0.0797172873623 0.0573362873289 0.0539615186802 0.0612879452506 0.064613107429 0.0413483548752 0.0492060616579 0.0408536005927 0.0386658835072 0.0395835329499 0.0611338973657 0.0393645393569 0.053572584104 0.0330686361703 0.0645571164168 0.0355933171917 0.0584689678276 0.0540828717859 0.066313152361 0.0447064134304 0.0401381607957 0.0437269026711 0.0614075519202 0.0498319714223 0.0579434032885 0.0585128670581 0.0638402708268 0.0548281142484 0.0533071471302 0.0449195692472 0.0626153729552 0.0482343239424 0.0295703841711 0.0366619395227 0.036500768924 0.0216100790796 0.0202086133386 0.0291081498137 0.0538445159183 0.0304641791012 0.0384602037434 0.0388505887816 0.0184939827468 0.0431509831641 0.0514497599393 0.0311190761962 0.0193480902941 0.0458820296239 0.0240997287473 0.0258856149523 0.0318135695083 0.0318488388141 0.0408906139312 0.0292269134036 0.0455476970026 0.0384416242221 0.0355459730354 0.0604270677753 0.0357756191383 0.0579552498509 0.0353240752803 0.0225716123758 0.0257247517645 0.0383804782921 0.0215488097029 0.044948702922 0.0168022708851 0.0235401078242 0.0440963094143 0.0364932029322 0.0228797040221 0.0280879909327 0.0410984380147 0.0600604954457 0.0480533666552 0.0218775898923 0.029791578282 0.04158778439 0.0178882014032 0.0370867411707 0.0460836281119 0.0252155578724 0.0208740877353 0.0346851323104 0.0150019768994 0.0257835645737 0.0322493089661 0.0342156649906 0.0116183721538 0.0144931943243 0.0240188836176 0.0389068297986 0.0144502424291 0.0276541092905 0.0250061652639 0.0558771163058 0.0271661677907 0.0351271284352 0.0289684152137 0.0442230773604 0.0424650907505 0.0353240752803 0.0384614305736 0.0501641618566 0.0418657156844 0.0254781137454 0.0303709238998 0.055175185082 0.0355533707256 0.0226236551837 0.0257243733887 0.0188708962976 0.0280458447088 0.0259495603965 0.0358126700891 0.0173972925493 0.0203550537734 0.0241984077655 0.0326658712857 0.0374251933899 0.0415458358848 0.0220997016335 0.0314739029711 0.0297599572502 0.0224642058136 0.0101652715813 0.0184552115135 0.0190795074292 0.0255522841638 0.0188568594492 0.0233889500734 0.0229626929389 0.0201746110209 0.0490887418108 0.0229752614583 0.0721999639917 0.0528489482775 0.0457665690796 0.0402580156331 0.0570067559398 0.0466251225309 0.0575932728503 0.0376765605581 0.0495760090258 0.0573857799099 0.0457294834507 0.0461836275015 0.0478961825174 0.0628300939828 0.0782978077723 0.0617431168606 0.0349090738753 0.0372003973766 0.0687801137491 0.0442160827041 0.0545072221083 0.0604041835816 0.0503536267186 0.0412302683921 0.0462335606677 0.0265634879228 0.0409252862943 0.0403112510912 0.055713517152 0.0210444978159 0.042126696634 0.0241765196843 0.060672423249 0.020816933598 0.0350778856202 0.052005350197 0.067095717986 0.0355534496705 0.0422916670752 0.0461036815572 0.0628103650139 0.0591144676161 0.0528489482775 0.055715116829 0.0658580956838 0.0605104799512 0.0516473901424 0.0459913703308 0.0644951804376 0.0433395766961 0.00476396676703 0.0174479971383 0.0262117873028 0.0230803954362 0.0185627883328 0.00806620839217 0.0113814933623 0.00390319674762 0.0428177321428 0.0406615445683 0.0354624532828 0.0102263088249 0.0317971018832 0.0234656694869 0.0177926588758 0.0262276626577 0.0188776295133 0.00499606040594 0.00424685036617 0.0184558479833 0.02090066744 0.0184013332257 0.00554993882929 0.0277916235291 0.0146255258466 0.0766584518429 0.0582536091399 0.0509151171955 0.0506702158494 0.0625265075224 0.0566765790267 0.063296553303 0.0510181236531 0.0599592936046 0.0584198089247 0.0435074153396 0.0509940717271 0.0495661398107 0.0669266401341 0.0789376730305 0.0608280979649 0.0430167703447 0.0455496571569 0.0778333721022 0.054368608181 0.0555188693765 0.0628704456678 0.0616793859568 0.0418722056261 0.0499801315934 0.0381178178645 0.0399704213931 0.0409195428074 0.0612188756554 0.0349680364553 0.0510653686933 0.0315069690942 0.065089713199 0.0323745393338 0.0538917621299 0.0541667478451 0.0685063182149 0.0437802642661 0.0419063387522 0.0452698567868 0.063322234724 0.0532615551019 0.0582536091399 0.0594215850469 0.0661130238288 0.0575059193505 0.0535703049783 0.046311202915 0.0650382074097 0.0485914933238 0.018842155525 0.0240370427448 0.0209528738511 0.0208089534878 0.0111203801188 0.015917150662 0.00453488271341 0.0421322819802 0.0391458330884 0.0331083714381 0.00824403626512 0.0325132311882 0.0224847085725 0.015999516676 0.0277730715816 0.0186363801442 0.00708833477439 0.00642676690132 0.0178233234532 0.0220593051536 0.0178006297585 0.00678573265748 0.0253531080362 0.0132629896024 0.0741187095157 0.0561450172019 0.0491350925935 0.0497514221438 0.060382347286 0.0560370322524 0.0611402268324 0.051356658754 0.059586423134 0.0554899035837 0.04031824984 0.0491508161478 0.0470289167752 0.0645318276086 0.0757421493114 0.057544117742 0.0418388253062 0.0443100927117 0.0770487593095 0.0543425693403 0.0526585588221 0.0601234502371 0.0616298008584 0.039475560154 0.0476974484773 0.0379820027229 0.0371681460661 0.0383173623723 0.0591794106435 0.0360653613059 0.0506856040282 0.0307481929538 0.0627839311984 0.0325910275487 0.055237903563 0.0520576344331 0.0655568890209 0.042566036842 0.0391094232808 0.0423459347432 0.0602723002528 0.0493981873195 0.0561450172019 0.0569656980973 0.0629553672806 0.0540102942581 0.0513493159685 0.0435185067862 0.0620208318713 0.0466647868495 0.0214518595921 0.0250574648372 0.0327035392179 0.0101396500324 0.0173409936704 0.0207342919251 0.0332931627481 0.0438902497615 0.0452760516117 0.0173962021214 0.0155474932413 0.035097178823 0.0167075078514 0.0219356609327 0.0232161362242 0.0132718217012 0.0215035441799 0.0212520758854 0.0323731403418 0.0276797560909 0.0185474301296 0.0436301136913 0.0240030156204 0.0722858985812 0.0511085237891 0.0406650357841 0.0417405611462 0.0549851372416 0.0429543701734 0.0585838550979 0.0363293620395 0.0449605458738 0.0553495140321 0.0426335340052 0.0408304262584 0.0422933209142 0.0585800490211 0.0742997883793 0.058525226517 0.035206446299 0.0404435198283 0.0627381786217 0.038449263747 0.0501920171677 0.058888502058 0.0460241869732 0.0337463818569 0.0458812434451 0.0278046680998 0.0349131438742 0.0389869853015 0.0521722266345 0.0209043609517 0.0359181281747 0.0276204608178 0.056526179998 0.0222941226577 0.041152359144 0.0428797366789 0.0667722352073 0.0380782189511 0.0410414680557 0.0398735425148 0.0580112990116 0.0505852294836 0.0511085237891 0.0532571789631 0.0625496034306 0.0531021220011 0.042888351609 0.0413906647006 0.0644729984699 0.0453457651066 0.0107504885543 0.0445819304752 0.0242663168605 0.0336280858548 0.0265479358211 0.0206001571473 0.0282052030653 0.0325495864256 0.0168933846577 0.0294835383052 0.0291726320507 0.0086711740192 0.0221542204142 0.0179304565478 0.022368606911 0.0297212987281 0.0149126627655 0.0323363820236 0.0229519593576 0.0235384954017 0.0445361789109 0.0198724736323 0.0521573964273 0.0325036248406 0.025391360945 0.0274230805005 0.0366454630316 0.0344942582431 0.0390192491133 0.0331598239249 0.0393605141281 0.0342759827638 0.021260474927 0.0254073365966 0.0234580906639 0.0407813070352 0.054069225305 0.037293221414 0.0193616327469 0.0235535155332 0.0561408138747 0.0363119747552 0.0299684113953 0.0382284111131 0.0425406057591 0.0161350959097 0.0256650379468 0.0189027762255 0.0143744986934 0.0179840539103 0.0351859317755 0.0236434364907 0.0314452790447 0.0139598707264 0.0389431870684 0.016485382228 0.040355084472 0.0299825744277 0.0456481354326 0.0213868007101 0.0199817088813 0.0197684998025 0.0379808456048 0.0313521152981 0.0325036248406 0.0336827412775 0.0416843797906 0.0334784161986 0.0289224474022 0.0207816321212 0.0432746333929 0.0259935830061 0.0406061367838 0.0235468720047 0.0318323205013 0.0219961670771 0.0255808203638 0.0205571609912 0.0226267329863 0.013012280745 0.0369778751619 0.0187789289183 0.00874641080149 0.0197571101094 0.00977736627462 0.0202312308031 0.0257037864622 0.00771970434192 0.0231557657352 0.0133099999073 0.0187324692629 0.0392432332024 0.0115155558073 0.0539655210169 0.0370438046358 0.0333786958054 0.0316409822149 0.0414621149977 0.0426991016561 0.0406517401284 0.0403678402162 0.0480644681966 0.0361424947464 0.0218113942904 0.0334641673727 0.0302365918541 0.0468956611505 0.0577004528593 0.0391757625612 0.0236163775334 0.0242576354959 0.0653230280715 0.0452817250312 0.0345762397733 0.0404850805623 0.0511603238121 0.024886725311 0.0274569982987 0.0236904007661 0.0206352481789 0.0182697542476 0.041544522984 0.0289437440201 0.0406091261712 0.0126309522282 0.0451825068417 0.020490830418 0.0442568377481 0.0395946754396 0.0458654268305 0.0228937718071 0.0192636370802 0.0258703030154 0.0425840964507 0.0360074541747 0.0370438046358 0.0378930233244 0.0444471390747 0.0387351356827 0.0382610925586 0.0256300024801 0.0425154846229 0.0258478343265 0.0236633695184 0.0174990292841 0.0186530571476 0.0611247172591 0.0557656094279 0.0466088592976 0.0282320418304 0.0445464770565 0.0341524834412 0.0362512556468 0.0421364706395 0.0353354631726 0.0231187500774 0.0150626098763 0.0358189243099 0.0298015850374 0.0324439491366 0.0223980477721 0.0244141427415 0.0302418773665 0.0942328467355 0.076594072426 0.0693014297828 0.0689934026371 0.0808708035071 0.0745545238332 0.0811424703773 0.0680895982601 0.0772898278285 0.0757784502044 0.0604959493699 0.069358828284 0.0677179794145 0.0851546659241 0.0961465377952 0.0777040409423 0.0614342341624 0.0635105029946 0.0949910709403 0.0710640834394 0.0733442971825 0.0804630717269 0.0785569975811 0.0599979412685 0.0679000809486 0.0561320615274 0.0579270123928 0.0585056430456 0.0796324819571 0.0514612322157 0.068283919556 0.0494122791964 0.083373938553 0.0501855689957 0.06973964597 0.0718399524308 0.0852557355766 0.0618964253913 0.0591154896454 0.0630863991576 0.0809010142666 0.069276699042 0.076594072426 0.0775503896407 0.0833112406891 0.0743754315619 0.0713482849075 0.0642332894623 0.0813844341388 0.0662752735857 0.00943572253542 0.0115403490897 0.0385513754293 0.041736873589 0.0399182937318 0.0122452242861 0.025058464158 0.0280391379811 0.0165428935252 0.0220426648475 0.0191966494038 0.00476868066193 0.0119136882752 0.0182274158907 0.024378991238 0.0214350053307 0.00987681971674 0.0356319079498 0.0179231346829 0.0755450486118 0.0557883449648 0.0471764532756 0.0467952668235 0.0599617395979 0.0511011457957 0.0617632501287 0.0445144536833 0.0538325742859 0.0579584947727 0.0439095831377 0.0473266714409 0.047305404175 0.0642647315012 0.0781620294368 0.0608980895696 0.0396397814207 0.0431908772896 0.0719383279912 0.0477762451878 0.0541744509705 0.0619776318533 0.0551486398926 0.0392023772277 0.0486708484659 0.0332061436773 0.0385651962783 0.0405238673503 0.0580675371179 0.0281169946403 0.0449308552075 0.0290979509914 0.0622896414172 0.0272386507233 0.0473564201885 0.0503681043652 0.0686223309305 0.0411717919112 0.0420509879955 0.0438795889294 0.0621492769732 0.0535535915584 0.0557883449648 0.0575324110618 0.0656578520536 0.0569417414102 0.0500298962763 0.0450037013757 0.0656341911967 0.0473715492692 0.0144468889514 0.0470085725986 0.0485138320241 0.0451352115307 0.0199583230651 0.0301372822106 0.0318368515531 0.0256571286264 0.0278614622113 0.0258405224944 0.0121251490044 0.0124421067497 0.0257425497103 0.0259848999587 0.0265128839547 0.0147071440313 0.0356921243154 0.0239599037157 0.0844599977072 0.0649098127865 0.0563152793656 0.0552526867175 0.0691043038035 0.0592804769565 0.0705635348052 0.0516880380121 0.0615515055365 0.0669939251635 0.0527850317966 0.0564919445965 0.0566781222463 0.0735436571696 0.0873943668972 0.0699246931044 0.0483722118201 0.0515510652048 0.079719756552 0.0550353446586 0.0634817595673 0.0710367571435 0.0623974106591 0.0485852577555 0.0576004808055 0.0413084590066 0.0479219849808 0.0493882028299 0.0672450344951 0.0348287223508 0.0527462211198 0.037276801135 0.071549415301 0.0351894067592 0.0529630080357 0.0594111357934 0.0773657257414 0.0496448829109 0.0507905960575 0.0532464315334 0.0714643731585 0.0625646663061 0.0649098127865 0.0667401518449 0.0748051995393 0.0661923309263 0.0591726016457 0.0543093310288 0.074196762744 0.0559244370052 0.043526425057 0.0386335179446 0.0321768451862 0.00974615024856 0.0352347635745 0.0201959159339 0.0180776754132 0.0269400303578 0.0177526168011 0.0076128290146 0.00378003726334 0.0176983136317 0.0184021944475 0.015957334363 0.00467338620778 0.0252008449064 0.0123712673884 0.075769333126 0.0581585948517 0.051685391535 0.0510927438975 0.0624824329901 0.058237684953 0.0625760535583 0.0529652916573 0.0618856654756 0.0574514448673 0.0423317456956 0.0517508019275 0.0497149058646 0.0670867680773 0.0782168275888 0.0597219475669 0.0433096265158 0.0451925797267 0.0797528207091 0.0566461202307 0.0551309318768 0.0620350591031 0.0638200944876 0.0424059873805 0.0493045876457 0.0391604065434 0.0398454975082 0.039967724192 0.0615633801452 0.0371201263022 0.0531118088353 0.0313373664803 0.0653089870936 0.0335830893361 0.0556009421638 0.055317918307 0.0671296861942 0.0435900410315 0.0407047621897 0.0451219309698 0.062876852421 0.0526327547566 0.0581585948517 0.0591116735406 0.0652217833265 0.0570769673892 0.0545681643866 0.0459758175248 0.0634453480612 0.0477419996054 0.0302678050916 0.043643410877 0.0344015081898 0.0370220423378 0.041870658286 0.0264826423168 0.0239447061259 0.0294219290852 0.0381639993795 0.046622906961 0.0276160450358 0.0433656713185 0.0358877725536 0.0394027280044 0.0640596529703 0.0352660320463 0.0428200645336 0.0215929365207 0.0157178006612 0.00848045847957 0.0252717757761 0.0193974269851 0.0287059984697 0.018482386953 0.0255997786212 0.0310533244304 0.0267766622161 0.0163561213819 0.0202134806234 0.03120585866 0.04895305148 0.036414957876 0.00526673250197 0.012342309222 0.0430330254803 0.0254800237585 0.0266262246906 0.032239986602 0.0289090953163 0.0180202124552 0.0199181330431 0.00657470209208 0.019948180111 0.0209059906389 0.0237059104211 0.0188029876251 0.0211536929916 0.0148162018356 0.0289733910382 0.0123381921604 0.0242362829274 0.0258181102869 0.0413288399769 0.0100525363381 0.0238857865216 0.0223322555234 0.0341921837249 0.0380927560603 0.0215929365207 0.0255329039423 0.0383043472154 0.0356000313746 0.0249953874006 0.0211460552822 0.0407734262534 0.0193178864072 0.0180029141367 0.0318989553395 0.055546862882 0.0233316606029 0.0289827460324 0.0288090712213 0.0227692137274 0.0384790428766 0.0423072029606 0.0235668927239 0.02923399269 0.0234314824709 0.0353361736228 0.0517306479462 0.0262806947106 0.0449202216804 0.0352666691701 0.0392752718621 0.0317034826189 0.0393329164331 0.0490036652351 0.0325915526781 0.0480657848217 0.0556144972514 0.0314856186317 0.0229568301755 0.0394863344353 0.0351981784458 0.0468732286627 0.0534614036052 0.0352961935447 0.0267660200432 0.0206247795688 0.0716485928619 0.0553558677376 0.0350658679333 0.0348297205048 0.0591015265332 0.0349050611526 0.0239450314084 0.0312292238968 0.0291976883203 0.0191245738332 0.0424715605102 0.0407160306554 0.0507428891398 0.0195019979736 0.0455419926359 0.03094049167 0.0480027022309 0.0486981223658 0.0364405293098 0.0216724201002 0.0185311393183 0.0322264249099 0.0418711782883 0.0415641388928 0.0352666691701 0.0357541599092 0.040359691621 0.0422344237589 0.0468672273606 0.0294846892119 0.0324923805449 0.019122055634 0.028756827183 0.0587035417373 0.0144079071022 0.0301136242831 0.0361947314748 0.0239369814187 0.0355016994114 0.0354103580658 0.0251937444079 0.0240593800606 0.0195703439645 0.0308533101673 0.0370972551577 0.0220259748313 0.0576270076145 0.0487199724465 0.0508215730415 0.0469075647824 0.0527931212734 0.0620087731623 0.046977227863 0.0607033064427 0.0681905343994 0.0417090362137 0.0294084394516 0.0508408090071 0.0450400867986 0.0589677823649 0.0627613332544 0.0433082894199 0.0402642195426 0.0363046359117 0.084449006206 0.0666097386489 0.0449981598795 0.0463289168121 0.0716590868723 0.0429897098207 0.0369198960753 0.0432687121664 0.0364322199569 0.0287560795721 0.0553568599314 0.0500592177291 0.0617693212505 0.0302484927047 0.057817398636 0.0410079662048 0.0620054930234 0.0578523465697 0.0464641684152 0.0365865801942 0.0272374209649 0.0399576916682 0.0513227964118 0.0446516670985 0.0487199724465 0.0480806958126 0.0495453699635 0.0482845333883 0.0560302897599 0.0385013240981 0.0415333749992 0.0339802993211 0.0315416974034 0.0191639032899 0.00845591360235 0.0214341775937 0.0115218935447 0.00798589270321 0.013178045677 0.0100207917484 0.0199483776485 0.012776598265 0.00707708079363 0.0310463483447 0.00779811122226 0.0665120827305 0.0484731432285 0.0421114253952 0.0418216626354 0.0527853739739 0.0493966141428 0.0532439640509 0.0450980317427 0.0534929652418 0.0482563107324 0.0334028573772 0.0421738253769 0.0400650763191 0.0574010770653 0.0690115183659 0.0507807835016 0.033887277172 0.0360916940356 0.0712074753299 0.0488889567075 0.0456112729946 0.052712754383 0.0558191941722 0.0328832629679 0.0398769847778 0.0305626616722 0.03027845702 0.0307126203306 0.051853149134 0.0304315085387 0.0449208583046 0.0225669791691 0.0556104755493 0.0254498384131 0.0488668504899 0.0462046921677 0.0583599392562 0.0343786096194 0.0317076229625 0.0356254179774 0.0534761774586 0.0442401179536 0.0484731432285 0.0494784680372 0.0560638562362 0.0481022882275 0.0453399462887 0.0364100969213 0.0549637988767 0.0386182470618 0.0499647144567 0.0286991839096 0.0337581082348 0.0374427591873 0.0282526339036 0.0355267981937 0.0350362485208 0.0478325170984 0.0425063748067 0.0336958297594 0.0552821351039 0.0385952701325 0.0756759815417 0.0533281139759 0.0401812481707 0.0447968354881 0.0564105044414 0.0391525826608 0.0629279047421 0.0332827070938 0.0388499692511 0.059944716429 0.0494848197832 0.0402828974015 0.0438747097228 0.0580998054312 0.0757410641548 0.0628856838636 0.039846534973 0.047063496141 0.0543336344875 0.0312551117596 0.052807685198 0.0627916823231 0.0390112066642 0.0353058185357 0.0513654740192 0.0323261929775 0.0392211101217 0.0463708307148 0.0519963158873 0.0234224809867 0.0301000678074 0.0372829629985 0.056085164213 0.0288004220361 0.0417416575715 0.0389119081863 0.0720657500645 0.0444323135884 0.0487706488559 0.0429521653792 0.0596874437499 0.052997284605 0.0533281139759 0.055723410021 0.0657462146584 0.0546473494099 0.0397492903292 0.0452841873006 0.0707319601823 0.0522103619845 0.0233361726138 0.027152976334 0.0145101369991 0.0239084572879 0.0231796409398 0.016731423623 0.010101659051 0.00781108168527 0.0186261838058 0.0315054768721 0.011535154356 0.0661583467836 0.0529737197777 0.0515726376237 0.0472342284059 0.0573866872941 0.0602508051558 0.0535888133028 0.0563499220883 0.0655740867856 0.0494619373879 0.0355566546043 0.0516986354459 0.0478914383755 0.0636133913461 0.0714227137745 0.0519652065027 0.040040966538 0.0379888138015 0.0833863881089 0.0624232672218 0.0504889561622 0.0539483262888 0.0682863371673 0.0434492287718 0.0419221148541 0.0393420375143 0.0383919560321 0.0330409365388 0.0586345177546 0.0426935993753 0.0582011555148 0.0271038932712 0.0620596992058 0.0353264927481 0.0565229896235 0.0582751327845 0.0564224406826 0.0375222198637 0.032781658946 0.0431629716317 0.0579468986629 0.0506284295544 0.0529737197777 0.0534601982596 0.057907553496 0.0543043024926 0.0568716124285 0.0423649825393 0.0520169525452 0.0387128090446 0.0188041113809 0.0118952838684 0.0140246174547 0.0212962400935 0.00885470160798 0.025142494638 0.0164130694781 0.0148836892075 0.0382106616301 0.012548123562 0.0595024177002 0.0405144987261 0.0336829979036 0.0339109092342 0.0447786612887 0.0412528753098 0.0460796810618 0.0378073897699 0.0456289595962 0.0415318567716 0.0274434917532 0.0337586538145 0.0320559171837 0.0493065770936 0.0620181140557 0.044443934423 0.0259210454652 0.0290199212383 0.0631931226882 0.0415500215307 0.0381432011516 0.0457001329437 0.0482419653282 0.0247624956339 0.0326912275918 0.0231122641396 0.0226732232029 0.0241582823353 0.0435758556102 0.024717419625 0.037259551901 0.0161507334564 0.0474557435456 0.0186437176733 0.0428749718429 0.0381563349544 0.0522572394535 0.0270598018866 0.0256825354187 0.028101885414 0.0461630275758 0.0383904744963 0.0405144987261 0.041758354552 0.0492909187408 0.0413606774947 0.0372601462253 0.0289047461458 0.049343864353 0.0319066136889 0.0140884736377 0.0220949123029 0.0294588701998 0.0141915109934 0.0236402055251 0.0202701530019 0.0222961106051 0.0508328337578 0.0209048958304 0.0623063919994 0.0435137888223 0.0379179155361 0.0310595169631 0.0477275300573 0.0401401417297 0.0476510837343 0.0326069267264 0.0442002914704 0.048031576146 0.0373260310255 0.0383688546713 0.0396828967833 0.0540671732006 0.0691161945626 0.052697233895 0.0256698443155 0.0271866198766 0.063343063446 0.0401717345523 0.0456835007162 0.0508390224232 0.0457136947621 0.0340506342939 0.0366331778515 0.0185292352758 0.0332180639746 0.031402826069 0.0470047522912 0.0178796862446 0.0373117201537 0.0146755143259 0.0519441438124 0.0134308953908 0.0305516636587 0.0455547559496 0.0574314455133 0.025677142017 0.0334820400224 0.038185075996 0.053981408775 0.0519396327628 0.0435137888223 0.0464090712113 0.0566586435278 0.0526391071675 0.0449104072539 0.0375180555166 0.0549667946861 0.0334290223836 0.0161979126548 0.0212360948185 0.00313302296585 0.0148689509772 0.00704616989296 0.0135603719097 0.0384674432624 0.00748847746612 0.0612097252086 0.0442840466006 0.040253243803 0.0360606427738 0.0487530949526 0.0470778914902 0.0473392259982 0.0422963534873 0.051949068182 0.0444929580638 0.0309270188278 0.040473799777 0.0386243677637 0.0547837147046 0.066413370926 0.0479885282668 0.0287291459439 0.0288822795748 0.0702829288423 0.0483322868723 0.0432975336344 0.0484818516534 0.0543652596123 0.0329738645592 0.0348525988976 0.0258401695463 0.029652318833 0.0266204166929 0.0488217477328 0.0282325775461 0.0443695558327 0.0152728890442 0.0529346931915 0.0211960241124 0.0432358961175 0.0470042201915 0.0535566647123 0.027654993588 0.0276975587847 0.0349524016669 0.051450438001 0.0457471816551 0.0442840466006 0.0457673454519 0.0531390087147 0.0482613634351 0.0459084957324 0.0344895940573 0.0501063288839 0.0320407745768 0.00918896575074 0.0152928703979 0.0213827580606 0.0175694634428 0.00562421953864 0.0321021314886 0.0136468651148 0.0732793088732 0.0542749513511 0.0465599433205 0.0461499050441 0.0585282720374 0.0518386762248 0.0596896497046 0.0460357977086 0.0551020502351 0.055347044484 0.0408456006547 0.0466734650365 0.0457685420886 0.0629790052567 0.0758414718388 0.0580725583953 0.0386295601195 0.0415657641172 0.0731303485703 0.0495368191967 0.0520897347287 0.0595893389246 0.0567976430466 0.0379647331116 0.046441942626 0.0333063038869 0.036500108881 0.0377849182347 0.0570420811998 0.030012797008 0.0462545479693 0.027461418846 0.0610786707463 0.0275181743774 0.0490679602747 0.0500650525841 0.0657243645716 0.0396831631678 0.0390548397455 0.0418579268325 0.0600259300869 0.0509169565607 0.0542749513511 0.0557017357111 0.0631094362549 0.0546387182457 0.049518990394 0.0428637051261 0.0625010516077 0.0450876329039 0.0212339329669 0.0205215987455 0.0193863377449 0.00780222226267 0.0244911121423 0.0160871853235 0.0794295187203 0.0616053956912 0.0547043851777 0.0543405312398 0.065903416382 0.0608093966938 0.066232208671 0.0551996275352 0.0641635601661 0.0610614868167 0.0459223913813 0.0547672174246 0.0529488520021 0.0703578675954 0.081658744309 0.063265127216 0.046621662518 0.0487339053303 0.0819943915105 0.0586031447424 0.058550915637 0.0656481691263 0.065908662167 0.0454276311919 0.0529363521846 0.0420305180364 0.0431442291715 0.0436296366503 0.0648039964872 0.0390809908814 0.0552779379516 0.0347675148936 0.0685677387884 0.0363124697872 0.0577513229251 0.0579440183076 0.0708163109663 0.0470812852612 0.0443909494169 0.0483964045026 0.0662606542355 0.055662814312 0.0616053956912 0.0625961650479 0.0687432253304 0.0602433308395 0.0572909174214 0.0493875071422 0.0671416550914 0.051430941132 0.0176886906331 0.00927892520724 0.0135248076976 0.0386449359276 0.00800171044068 0.0600153322548 0.0424684845259 0.0378068586886 0.0345103555887 0.0469162632965 0.044830307306 0.0461600332306 0.040366688607 0.0496446456688 0.0429723253644 0.0292480429291 0.0380026884561 0.0362133523808 0.0526530252718 0.0646507321938 0.046401756397 0.0269368048123 0.0278152961494 0.0678508831968 0.0459682894465 0.0412431425852 0.0469952418705 0.0521326959742 0.0302237793371 0.0333696851677 0.0240543524356 0.0271406206916 0.0250182744726 0.0466959485275 0.0265020349694 0.0419000008026 0.0139599809083 0.0507909794153 0.0193885955935 0.0423195771966 0.0441873849773 0.0525088605043 0.0263754966172 0.0262639235251 0.0325133748369 0.0494223408625 0.0434143928247 0.0424684845259 0.0439360734198 0.0514706200386 0.0459476778298 0.0431249041549 0.0322850478478 0.0492075262735 0.0310084454102 0.0100495790147 0.0163467110294 0.0344303173981 0.0138069903297 0.0725561849391 0.0577945355153 0.0549392051976 0.0494983401957 0.0622636335656 0.0613281176125 0.0591564733558 0.055493289997 0.0660163516694 0.0564238014063 0.0428263617367 0.0551692935595 0.0528843814502 0.0687090576791 0.0786138592889 0.0595588928727 0.0427108568825 0.041401774003 0.084635719422 0.0620469795992 0.0566375025559 0.0605458826833 0.0680826326606 0.0475957607451 0.0475121922821 0.0395532575369 0.0436869591597 0.0392259763169 0.0629613789201 0.0403138291944 0.0584854937624 0.0290090498587 0.0669476358426 0.0346983343078 0.0538333844175 0.0617823249939 0.0639146571596 0.0406990904981 0.0395983575205 0.0488304003282 0.0645174644364 0.0580105192199 0.0577945355153 0.0590354282227 0.0651460923012 0.0612394393036 0.0606595939265 0.0481508588685 0.059835663962 0.0439114917806 0.0129734005291 0.03341718436 0.00522839839082 0.0644629130549 0.0490781090479 0.0460393397147 0.042060104717 0.0535473784021 0.0537667869164 0.051142158888 0.0493088817338 0.0587609555936 0.047452874403 0.0333145084294 0.0462018313471 0.0433659094036 0.0595851004773 0.0694769331379 0.0504124171924 0.034671039268 0.0338977868767 0.0768809318577 0.0551957563061 0.0471765984426 0.0517762151648 0.0612752535588 0.0380650031758 0.0388085089346 0.0327052071067 0.0338902731124 0.0299610722521 0.054082097785 0.0351427472718 0.0511360689963 0.0213341577068 0.0578680354373 0.0282202368919 0.0499673113862 0.0525352197686 0.0557073834724 0.0329893658613 0.0303826410135 0.0390336792828 0.055051644837 0.0480885204528 0.0490781090479 0.0500556489468 0.0560168186449 0.0513666437929 0.0513150683738 0.038531174408 0.0517653307967 0.0358776775011 0.0293027668161 0.00949293329263 0.0725916210374 0.0547194436592 0.0483620571735 0.0469816749901 0.0590799074957 0.0545464429945 0.0590974756513 0.0490311503881 0.0583273766433 0.0546626805606 0.0398779297173 0.0484777367952 0.0468025950712 0.0639880374892 0.0757199402524 0.0573199482995 0.0393352512936 0.0411552751652 0.0764775433632 0.0532445829203 0.052349146311 0.0590579753428 0.0602574471507 0.039602885829 0.0459481099263 0.0349175810863 0.037150941259 0.03692168916 0.0582151479375 0.0331340211018 0.0497159727507 0.0271028830882 0.0621502493207 0.0292960379946 0.0512053897158 0.0526539904442 0.0643939446385 0.0395489605444 0.0378591872997 0.0425337328718 0.0602822057384 0.0511975943751 0.0547194436592 0.0559279154126 0.0626694858218 0.0551041885347 0.0518894661445 0.0431638732605 0.0608387750183 0.0441361499894 0.0310183369902 0.0871660444405 0.0733823196322 0.0689340787316 0.0705597518969 0.0774366556509 0.07861295118 0.076322371958 0.075585060054 0.0826717778434 0.0681219966699 0.0528565530205 0.0687776982924 0.0640973745197 0.0810751053908 0.0869333145725 0.0682988428223 0.0623918658396 0.0630905228705 0.0989058396189 0.0780506774957 0.0672923155095 0.0734447484338 0.0852286487915 0.0580045491026 0.0635565107431 0.0611199283903 0.0538632888957 0.0534691027903 0.0772908944799 0.0610905623861 0.073927736475 0.0514266311669 0.0797776498939 0.0563654161709 0.0797411714169 0.0709112220147 0.075991910188 0.061929266059 0.0528794637266 0.0580761513446 0.0735774043698 0.0591743277341 0.0733823196322 0.0727567267189 0.0745131906994 0.065799040936 0.0698581828462 0.0592195179672 0.0715468662712 0.0627789092372 0.0648080233561 0.0484745494015 0.0442686485072 0.0419227062634 0.0529072690322 0.05220865754 0.0515658331236 0.0480206916873 0.056947174509 0.0470499747083 0.0323100366631 0.0443750009709 0.0415450201967 0.0583606796016 0.0686531977281 0.0497286282257 0.0341488382347 0.0345013563823 0.0748626932863 0.0530450676854 0.0459172064129 0.0515384326883 0.0594738121093 0.0355453778886 0.038659716641 0.0319368788596 0.0317165879728 0.0293867131129 0.0529269518286 0.0336846365247 0.0489212862277 0.021422361173 0.0566364033836 0.0272163294484 0.0501093106191 0.0498104699827 0.056059086848 0.0332712049783 0.0299486671345 0.0369730112559 0.0537844555125 0.0455932312627 0.0484745494015 0.0493458255088 0.0553162838774 0.0492693292197 0.0486803630647 0.0369835817896 0.0522579331221 0.0364683447074 0.022730708667 0.036743783492 0.0359076620395 0.0204805867516 0.0463990865864 0.0147686048767 0.0549817963395 0.0525592871928 0.0196223614467 0.0349129145261 0.0366443799136 0.0327544499352 0.0228629827418 0.017026958725 0.0209966358066 0.0388626349727 0.035298115438 0.0567980979705 0.0582058906835 0.0248891314561 0.0145753449597 0.0570907866116 0.0413833987479 0.0269969183741 0.0492024913366 0.039440986454 0.0359176091053 0.0265520996515 0.0615044403055 0.0540461620767 0.0485288037826 0.0239710979892 0.0540724881457 0.0603640513646 0.0428978851084 0.0134027017241 0.0368991356344 0.0354211161184 0.0355257446359 0.0210753154172 0.0377446845484 0.022730708667 0.0201673641607 0.0164841811953 0.0313109932769 0.041027522952 0.0326196770584 0.0184701671719 0.0295244972329 0.0144112105736 0.0158826147242 0.00448936000965 0.0256749628733 0.0117360219999 0.0336843126425 0.0324372887253 0.0146677582401 0.0224513170291 0.0144322007261 0.0126899376427 0.0122323800999 0.0277700509569 0.0197834641463 0.0180901487399 0.0184584697146 0.0418096588931 0.0367452397055 0.0108947935426 0.0131136689852 0.0371795334759 0.0204476192567 0.0118634455346 0.0280193213775 0.0206906520081 0.0208413846069 0.00805992628737 0.0397245591306 0.032104964073 0.0301985227624 0.0107001908888 0.0330988349255 0.0415195660786 0.0230606485279 0.0236410923325 0.0187109927435 0.0222023475244 0.0175662614613 0.0151089228932 0.0279798538754 0.0 0.00504787707871 0.0188703006517 0.0219245561497 0.0212095981034 0.0149942492526 0.0252687339312 0.016225955903 0.014982874816 0.0165713440517 0.0153992347708 0.0258128425881 0.0233194300829 0.0216404069707 0.0254243329386 0.0254506863873 0.000788850240312 0.00972288497974 0.0186083951584 0.0381473811219 0.0294157845108 0.0147578085456 0.0211609731312 0.0342335739858 0.0241609615185 0.0173732430671 0.0259324451415 0.0264222187829 0.0114741578552 0.0193298399608 0.0206022299244 0.0166568971164 0.0229188388297 0.0118905814083 0.0295550989457 0.0189848877848 0.0268759492012 0.0164070102936 0.0252554335694 0.0350869023834 0.0115237799469 0.0366507700029 0.0195520445986 0.0253794363549 0.0152320827299 0.0231766024476 0.028592244401 0.0144112105736 0.0173074040909 0.0296651493536 0.0243710502336 0.0103105434217 0.0155263675354 0.0374184940592 0.0231136537684 0.0190194510466 0.0188364876977 0.0221985054079 0.0214172478398 0.0257446692843 0.0273354856863 0.0275203255805 0.0156705691302 0.0195445751869 0.0258686776409 0.0435616108671 0.0331590462603 0.00822908256455 0.0111578554098 0.0408350390305 0.0282065307084 0.0237933699021 0.0272090918986 0.029489839001 0.0211107451245 0.0171676820259 0.0146997004559 0.0226821224304 0.022183640748 0.0185118691459 0.0264356522916 0.0241910879615 0.0208215741795 0.0237366538469 0.0205889692169 0.0263075431581 0.0263185412116 0.036342074994 0.0103523538982 0.0247947799759 0.0232770954007 0.0303205821473 0.0382875192266 0.0158826147242 0.0205185838207 0.033936913628 0.0342591359421 0.0252819239657 0.0211775313941 0.0366164619483 0.0168683582115 0.0265527528137 0.0122930639405 0.0356399190513 0.0329283261088 0.0161748547301 0.0260907486468 0.0165355786656 0.0155013603686 0.00871939649167 0.0250375042482 0.0205784254424 0.0221880849182 0.0226228234946 0.0402265547715 0.0380348851944 0.0126404460089 0.0132955588731 0.037673161045 0.023685130291 0.0159536871846 0.0317445670434 0.0244407629294 0.0250650773838 0.00665080674841 0.0431354773139 0.0336884150562 0.0346100496205 0.00755310734613 0.037025240188 0.043753914844 0.0237910793513 0.023601769275 0.0229675330633 0.0262374537149 0.0207694947791 0.0142462256068 0.0294419901123 0.00448936000965 0.00600179744469 0.0181950411243 0.0225874743282 0.0220819669003 0.0185630904749 0.0261141108628 0.0202600233978 0.0359510556228 0.0122181959079 0.00715556619596 0.0393252590156 0.0398773260728 0.015944130912 0.025117898688 0.0279596047113 0.0495072405762 0.0438343802676 0.0223301740975 0.0291958566192 0.0238057945893 0.0121408308676 0.0320439133203 0.0386704894061 0.0116681269474 0.0246115626131 0.0320654848123 0.02212022544 0.0306066666749 0.0360151785428 0.0212925403107 0.0265520435189 0.0096564754612 0.0336990775918 0.0258669646258 0.0271355620099 0.0255831511409 0.0192288539502 0.0492946436623 0.0275425360484 0.0388140188539 0.0303333899385 0.0366778253264 0.0435686919256 0.0256749628733 0.0298872725089 0.043124071299 0.0391147976424 0.020010154166 0.0305524053833 0.0506217525372 0.0340993001213 0.0427032875962 0.0428000340065 0.0127191401929 0.0244302586973 0.0258873530415 0.0224665589838 0.0196473040025 0.0259734946343 0.0180766430627 0.0245847379502 0.0205530555257 0.0515531804399 0.047284161103 0.0168752313645 0.0093274994833 0.0472940785154 0.0299859804517 0.013568861355 0.0349261393238 0.0278394065303 0.0233159306106 0.018729490125 0.0474594716238 0.0428441003445 0.0340535271365 0.0192623858156 0.0396592952666 0.0474504224755 0.0346317498496 0.0155232846365 0.0221965807346 0.023519540409 0.0251795457747 0.0186807942123 0.0328466600284 0.0117360219999 0.0111390258469 0.0171063233969 0.027248122956 0.0326787410905 0.0216187767654 0.0174724607588 0.0153838329478 0.013043913361 0.0463610530412 0.0437380208469 0.0239671160531 0.0322222363953 0.0387763357271 0.0597848214293 0.0513617728094 0.0233595904168 0.0301944308891 0.0320013483014 0.010613155035 0.0399275999718 0.0464560281456 0.0134988892532 0.0290788786594 0.0367911394695 0.0175534572334 0.0343523037935 0.0386269072909 0.0314326304477 0.0171757591643 0.0105270444576 0.0306360966619 0.0365489530298 0.0211256372062 0.0155275027866 0.0282406353658 0.0565789653078 0.0282583663219 0.0416523037157 0.0359394330813 0.0459783038684 0.0506220203623 0.0336843126425 0.0381881672141 0.0517807652888 0.0474506190651 0.0289707556044 0.0360504295798 0.0570578425896 0.0371145976129 0.0460576692202 0.0464080290911 0.0220712004031 0.0312574889523 0.0332251791601 0.054841891687 0.0503036463483 0.028999517533 0.0360798173432 0.0193249539307 0.00826557354917 0.0383933246971 0.0453200679143 0.00495350657916 0.0300295268949 0.0391229254289 0.0271682846269 0.036533385441 0.0426841669003 0.0272844902637 0.0287077471132 0.00927183865749 0.0394174170837 0.0313084618098 0.0314758507766 0.0267668687054 0.022518491913 0.05600898249 0.0343402195218 0.045509854552 0.0363226467926 0.0425348706975 0.0488168350062 0.0324372887253 0.0364696219213 0.0493441352532 0.0445105561012 0.0238995483109 0.0369592605884 0.0574925847452 0.0411978099793 0.0155890245102 0.0251635522565 0.0177733966699 0.020295344741 0.0224627747939 0.00622985531203 0.0261058920982 0.0236064364637 0.0550881017687 0.0493624804622 0.00949195374034 0.00547982782741 0.0508785947136 0.0251610996387 0.0119430843146 0.0365988456521 0.0213251727582 0.0179868212343 0.0207990673061 0.0485594746656 0.044214664825 0.0334335462828 0.0201922684831 0.0401754502972 0.0535325351547 0.0317074448278 0.0123261383208 0.0244676333996 0.0171629247157 0.0177983318365 0.0115555789303 0.021116940732 0.0146677582401 0.0105358367361 0.00915254671069 0.0167177891777 0.0294167459865 0.0147897849251 0.0123457776793 0.0164773765795 0.0252103273152 0.0171988861049 0.0305685435552 0.0365843208441 0.0175183238357 0.0218496748957 0.0205878945245 0.0593121863314 0.0470793832619 0.0159816216856 0.020744505382 0.0508661739809 0.018760262956 0.0128710885205 0.0300992820345 0.0117566522917 0.00679248320348 0.0280357325772 0.040552704264 0.0415859654681 0.0232256372519 0.0295874345059 0.0314690999231 0.0507965688951 0.0314591977122 0.0256867489287 0.0204093797109 0.00501434925534 0.0122277574576 0.0226603353781 0.0187158051887 0.0224513170291 0.020695499337 0.0231928239165 0.0198154038419 0.0292814261752 0.0103125125409 0.0227651885673 0.0153489133169 0.00921470910295 0.0183064776985 0.0377227049932 0.0290274791546 0.0152667053733 0.0215933253328 0.0343731361054 0.0245700487863 0.0169471424987 0.0257260078813 0.0268671065769 0.0111334970897 0.0193681705086 0.0211865368155 0.0163734248392 0.0228777878657 0.0117963493804 0.030068869871 0.0193762721713 0.027229886316 0.0161367068103 0.025740037188 0.0358416431275 0.0109673463454 0.0364509080414 0.0199965660095 0.0252914478428 0.014773865842 0.0226846344514 0.0279231861054 0.0144322007261 0.0171240275822 0.0292730788831 0.0236979129564 0.00966001570023 0.0151974508598 0.0372251985508 0.023311727536 0.0176051360114 0.032564167101 0.0208965631668 0.0168197313548 0.0207764664023 0.0423976172234 0.033280884693 0.00922898632458 0.0197200489933 0.0360385863631 0.00893473638299 0.014132357355 0.0250685285695 0.0103719683171 0.0167517993882 0.0135716425012 0.0350532380107 0.027879799306 0.0265658749664 0.015993421345 0.0286771298489 0.0428550440411 0.0151980773887 0.0298292088786 0.0196261415194 0.0184891599574 0.00655621912452 0.016322105066 0.0195353045374 0.0126899376427 0.0127701290018 0.0223217848231 0.0157154460048 0.0129568955631 0.00690277207156 0.0299954575682 0.0193817024498 0.0216771607077 0.0225658582257 0.0286138040807 0.0305011727447 0.0368382394098 0.0388439321615 0.0148355509953 0.0174423915339 0.0380129595202 0.0258082909988 0.0229842208153 0.0373470514825 0.0277476701907 0.0308564780706 0.00775830938446 0.0476084837369 0.0347450599297 0.0411478727988 0.00231111578025 0.0424821924039 0.0488975441679 0.0213563138315 0.0271270574956 0.0304501786922 0.0318897260431 0.0231190177025 0.0126379860304 0.0283589858381 0.0122323800999 0.0114136835682 0.0186791916183 0.0206672138954 0.0199079464104 0.0222007080182 0.0303110417953 0.0280827256688 0.0197052367937 0.0449418080277 0.0438182248518 0.0562345701188 0.0602081091792 0.0243728506009 0.019370550695 0.0596171194363 0.0411146925258 0.0330451422406 0.0550957050319 0.039836592047 0.0399228025142 0.0284759591968 0.0663825437409 0.0558252777705 0.0548034680532 0.0236830779253 0.0595333433641 0.0686676458986 0.0397842689041 0.0197958382609 0.0446061287137 0.0392287195509 0.0347118369491 0.0164026290561 0.0305214779366 0.0277700509569 0.0235155079857 0.0135888018548 0.0240589544851 0.0380160024194 0.0333951182547 0.0242014401746 0.0377621051763 0.0314790157987 0.029423203836 0.0582327389784 0.0535477924002 0.0121411421616 0.00926081168701 0.0551978876015 0.028043323191 0.0176337854836 0.041735927557 0.023998617859 0.0217227463265 0.0246395690903 0.0532994548372 0.0483234850835 0.0380931811444 0.0229416680066 0.0449797181572 0.059280772983 0.0338814552602 0.0120128740103 0.0301325195322 0.0203844916972 0.0200232099081 0.0110576598728 0.0182627991321 0.0197834641463 0.0150522414851 0.006556855609 0.0144048387324 0.0315801243453 0.0179150623078 0.0119668288225 0.022167313587 0.00841936493113 0.0453128226707 0.029523676844 0.0223084484022 0.0276272783534 0.0327625901905 0.0158536182647 0.0147737513682 0.0106741877313 0.0162753926733 0.0159900539559 0.0215767754577 0.023165172617 0.0247814128516 0.0137331692872 0.0264980088885 0.0153005942866 0.0291348842471 0.0254286093168 0.0364522562324 0.00599431682769 0.0189173783639 0.0184393411187 0.0301213577678 0.0340540018144 0.0180901487399 0.0215314467503 0.0337154513722 0.0315978293722 0.024169684578 0.0166816873996 0.0356875981931 0.0143747226708 0.0514837021891 0.0372994668391 0.0230531280792 0.0250236859971 0.0398047566678 0.021934356719 0.0118568359564 0.016712157857 0.0198275637831 0.0143476538676 0.0244078681037 0.0292462046489 0.0328036706334 0.014376575397 0.0286902071785 0.0204063466033 0.0328369650405 0.0322219054124 0.0320787692664 0.00266433410273 0.0165423262191 0.0215926529236 0.03047036768 0.0358252300263 0.0184584697146 0.0213100670766 0.032046547675 0.0333108429689 0.0306570115485 0.018573514939 0.030845411182 0.00824349209933 0.0254197321026 0.047324533609 0.0530498030585 0.0193482837408 0.0436475526218 0.0516271254066 0.0457976820229 0.0502335440929 0.0569904536613 0.0344485460271 0.0478896186827 0.0272539439051 0.0572698534085 0.0358919394369 0.050424643016 0.0434868500285 0.0309009333379 0.0632686594285 0.0501928388473 0.0594250482186 0.0482632028717 0.0482478127894 0.0570074387196 0.0418096588931 0.0448198518438 0.0553205633247 0.0514202209421 0.0325934353049 0.0491031454545 0.0660730408504 0.0548573842682 0.041501827818 0.0494594306994 0.00785967991178 0.0297803384368 0.0414124759052 0.0250716250932 0.0363814861914 0.0430927179021 0.0324719682551 0.0232534554573 0.00557282966442 0.0375065288364 0.0368003051347 0.0279998159154 0.0252506405295 0.0244125277036 0.0601405054627 0.0351799654171 0.0460355764213 0.0372310571496 0.0466115069454 0.0500875517655 0.0367452397055 0.0406580228721 0.0534802351733 0.0469702184582 0.0259083864948 0.0383091416944 0.0609916135179 0.0430915691708 0.0113258711014 0.0432937210726 0.0175511998728 0.0124157338497 0.03212454916 0.0158561794962 0.0176896387613 0.0143093876737 0.043181186929 0.0362893217044 0.0315177649353 0.0141883840024 0.0359022033885 0.0492746305333 0.0223018781484 0.0211045193185 0.0229404408443 0.0181288833884 0.0110415500412 0.00837364985146 0.0172572597582 0.0108947935426 0.00733209366776 0.0131427681285 0.0115359384743 0.0199994152339 0.0092535804352 0.0217603350968 0.0184515208125 0.0501255669434 0.0279388443296 0.0142651752221 0.0382528821443 0.0251771073359 0.0222405400165 0.018907996804 0.050407441851 0.0445874581383 0.03644061978 0.0176520694999 0.0424414179548 0.05338489352 0.0323923780445 0.0108812710336 0.0260958051981 0.0217902651406 0.0213471493056 0.0109405604951 0.0249968133743 0.0131136689852 0.00893536297262 0.00802513881767 0.0192978256402 0.0302167707341 0.018365059649 0.0130926236805 0.0184689475202 0.0343186729394 0.0436186327673 0.0296745080858 0.0408423177906 0.0469164023153 0.0321355072481 0.0293927572919 0.0115197032526 0.0423686398976 0.036129628995 0.0335484782832 0.0259949361948 0.0269704035297 0.0607621523939 0.0380404513983 0.049798653181 0.0409231346593 0.0474755392541 0.0535032395417 0.0371795334759 0.0413092694389 0.054257484038 0.0493594143559 0.0284802931744 0.0415978746211 0.0622229836323 0.0453773607077 0.0190484002067 0.0207749590973 0.00729318592678 0.0172314224853 0.0208211767145 0.0287420582694 0.0242194382748 0.0225231335589 0.0240241380028 0.0229680498334 0.0402905798782 0.0150429891355 0.0374592753112 0.0199314954492 0.0195088829088 0.00836639606005 0.0247669201274 0.0222507048525 0.0204476192567 0.021382719754 0.0305241597933 0.0212891172726 0.0134776031268 0.0107610654817 0.0367912026131 0.0227467739819 0.0252412358335 0.0153803531506 0.0101093922522 0.0191555088988 0.0375470144869 0.0363071087647 0.0222024666973 0.021689493319 0.0288400411309 0.0426974513075 0.0286740878663 0.0220177373135 0.0125507954062 0.0111145943691 0.0144555489867 0.0195277194831 0.0258649050915 0.0118634455346 0.0120062047089 0.0205457113931 0.0226933950128 0.0265812358015 0.010518368073 0.0210005232169 0.00605049088221 0.0227443548737 0.0240543202468 0.0298445720969 0.0127076622649 0.0212764877573 0.0130936058244 0.0350960563748 0.00616350137707 0.0220053566886 0.029182207808 0.0470447673611 0.0143549158293 0.0270200109504 0.0261798064669 0.0399469917707 0.0417589617676 0.0280193213775 0.0317524179632 0.0441157592928 0.0402109444472 0.0286518076954 0.0255338040061 0.0460635600013 0.0240522825965 0.0109052701432 0.0235077295524 0.0318665250498 0.0308330797717 0.0201941994246 0.026209699134 0.024272445381 0.0437154276666 0.0215651164399 0.0334110813047 0.0182660118628 0.0127706309369 0.00547043300996 0.0236736081076 0.0193715054877 0.0206906520081 0.0206326054685 0.0278093020752 0.0196854448654 0.0196839348285 0.00687772387853 0.0318861086105 0.0187829482374 0.026787715043 0.0349795665731 0.0377560197701 0.0169430774361 0.0294826475268 0.02554791035 0.044564230482 0.0307872765104 0.0282156909894 0.0140157312571 0.00304825224888 0.013131876158 0.0254598849649 0.0242359050423 0.0208413846069 0.0206178921833 0.0265921223132 0.024263714379 0.0287692622655 0.0105411880456 0.0255889461915 0.0107722057824 0.040112260752 0.0281920691871 0.0346799127455 0.00547299312148 0.0351698801996 0.0413927767406 0.0178561632488 0.0295704831304 0.0240442618733 0.028433519767 0.0198981190422 0.0164655446546 0.0293872770207 0.00805992628737 0.0103936152637 0.0224915115769 0.0226675446032 0.0164308167554 0.0187959028502 0.0318543120786 0.0236941138036 0.0214366809685 0.02158460699 0.0453234846239 0.0100348820296 0.0205571695519 0.0349449705288 0.0594353543617 0.0269232145172 0.0378563722335 0.0357646095878 0.0508614713562 0.0504482642845 0.0397245591306 0.0434432071263 0.055747628056 0.0497669350857 0.035138779746 0.0361053959246 0.0583555321158 0.0365217793435 0.0331608473129 0.0326376645366 0.0244797200401 0.0260351758605 0.0198255233977 0.055257342683 0.030607412773 0.0406724640229 0.0316826493706 0.0417015250124 0.044714144963 0.032104964073 0.0358439539437 0.0485265392209 0.0417397589401 0.0211023329973 0.0328093106913 0.055937896155 0.0382216948189 0.039178382683 0.0116462448464 0.0328449320294 0.0356452166019 0.0431040247851 0.0127555033307 0.0192172916283 0.0248029464024 0.0398126039657 0.0387676066679 0.0301985227624 0.0325088097853 0.0421224110282 0.0389248491362 0.0344395222458 0.0235149407659 0.0407276725105 0.0194230824244 0.0402634745964 0.0467109968717 0.0198309327046 0.0277799641062 0.0285372209353 0.0306922978338 0.0217975015009 0.0133715864954 0.0282760151712 0.0107001908888 0.0106288988311 0.0196086000788 0.0208164998238 0.0183683750797 0.0208677693371 0.0306825719595 0.0267116346537 0.0244223076771 0.0326854873354 0.0507787766717 0.0180800830921 0.0283453513368 0.0285692206738 0.0439964220236 0.0435330479692 0.0330988349255 0.0364104267178 0.047999984958 0.0430530022579 0.0322085934623 0.0282999450485 0.049271623839 0.0273166054796 0.0425206430225 0.0621747559979 0.0316259435967 0.0474306721161 0.0460270405303 0.055722110458 0.0616108763567 0.0415195660786 0.0463779068274 0.0600405303926 0.058487923658 0.0428330727641 0.0451145046508 0.0622811849491 0.040848815271 0.0429266615699 0.0304603009077 0.0328680598837 0.0193092495687 0.0257358921646 0.0276472175985 0.0230606485279 0.0242829569209 0.0335562675322 0.0240112096757 0.0023427193432 0.0215758461563 0.0440130376785 0.0333268924093 0.0336594835101 0.0267459293135 0.0300685155468 0.0190916149163 0.0302108808069 0.0236410923325 0.0197983357166 0.0116623663768 0.025981149508 0.0407083432987 0.0270506742353 0.00533601555818 0.0243947872794 0.0165388277645 0.0203993556991 0.0306449706863 0.0351570407904 0.0187109927435 0.0216936002715 0.0328125704384 0.0328483395342 0.0289711567156 0.0177290185247 0.0324417191457 0.00990245486371 0.0144435719572 0.0253854499058 0.0235470613048 0.0222023475244 0.0213735089495 0.0257216217897 0.0239916366178 0.0307770932602 0.0118559544137 0.0237036848381 0.0117425310805 0.0184555467145 0.0157796168272 0.0175662614613 0.0166625646078 0.0231975140243 0.0146299258577 0.0171445810537 0.00397360286698 0.029169512627 0.0192796522734 0.0184254094366 0.0151089228932 0.0104342480435 0.00810789324855 0.0108902089943 0.0236280097789 0.017384613998 0.0213096795304 0.0252727380791 0.0279798538754 0.0244605767611 0.021500625639 0.00796997392989 0.0256665045822 0.0176058458155 0.0292950203769 0.0310442784549 0.00504787707871 0.0188703006517 0.0219245561497 0.0212095981034 0.0149942492526 0.0252687339312 0.016225955903 0.0138989004364 0.0180767927652 0.0222193332382 0.0141002986592 0.0215877207325 0.0172352062998 0.0156454524782 0.031383424491 0.0212505739868 0.0142951058056 0.0253431933269 0.0219030212003 0.0156518397703 0.0263438271284 0.0285058962486 0.0193626993311 0.0417261059969 0.0313743437253 0.0260801912789 0.0155074292908 0.0227467684355 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..8ed5b9653f4ed88e146d2d5dc5b032537b426f8b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-jensenshannon-ml.txt @@ -0,0 +1 @@ +0.320369972991 0.338972466 0.308199372323 0.3452431902 0.310024768313 0.357115225615 0.311131096357 0.357391534414 0.329718053755 0.347365921475 0.335272625287 0.336451560653 0.33015370606 0.369628769749 0.344499490029 0.321622508707 0.345377707016 0.321007207534 0.350728979121 0.32809430086 0.30207071308 0.291663252492 0.30760470102 0.315976639534 0.308132467187 0.313014586878 0.310463895925 0.321091616502 0.290044394125 0.322213459935 0.315509196522 0.3331114403 0.281071919202 0.320854431887 0.332190658438 0.299342730178 0.313528775154 0.310049073937 0.288821516545 0.307662081954 0.328387688508 0.317185603454 0.332046170365 0.291912213887 0.37870970117 0.336080073379 0.304593343921 0.330138983604 0.355071759299 0.311946140607 0.302025400768 0.330940761586 0.351140062502 0.354772884287 0.272605322053 0.327957349848 0.28871110366 0.320821172951 0.340976919806 0.30757488831 0.320975346884 0.252776262329 0.314549731907 0.326876483 0.337684418756 0.296520013735 0.31493077245 0.327721982167 0.325802862624 0.341908184107 0.300481749419 0.312499767894 0.301061762121 0.27665157989 0.3082566692 0.287466396145 0.288313694552 0.296629698731 0.283556095025 0.322489360684 0.280765581604 0.297958166613 0.313189657041 0.303470399659 0.348652898212 0.331594734387 0.299446687464 0.339047458559 0.286979246044 0.316326095312 0.321618884109 0.330065896317 0.324500638067 0.328300795872 0.309002568222 0.262587468469 0.31974123777 0.286316182293 0.321162329165 0.328160620315 0.356618051635 0.289733970648 0.344507756538 0.301485561986 0.335785898715 0.322635066518 0.331480718646 0.297897604494 0.306942928189 0.350843442517 0.342585296966 0.341311053315 0.306780105123 0.313401804298 0.319978145568 0.302460397612 0.346105758567 0.312802351189 0.331552275517 0.321624157344 0.318798118247 0.301906095501 0.301585920138 0.314556178985 0.333215221158 0.306929663844 0.317083256901 0.309667679181 0.306529028004 0.30865993751 0.296031907986 0.28742420979 0.311584483038 0.319043629504 0.330278008622 0.314466433681 0.327937382021 0.296448162218 0.307033121385 0.296391953011 0.292691206116 0.297146209653 0.307929858983 0.291863681454 0.307300188104 0.306597817799 0.34718100163 0.317436210259 0.29952626739 0.330762834707 0.334951064852 0.323806678898 0.296203706701 0.33398466797 0.344931265559 0.293948734727 0.332764639313 0.272651853935 0.317324315923 0.300493570867 0.307008231016 0.333263322802 0.31390648462 0.332416491248 0.314766869708 0.321015549211 0.322909289307 0.356882966656 0.310596945263 0.343939748528 0.286269629586 0.33173459898 0.323848483719 0.305841388975 0.319266258167 0.34012363898 0.3443280395 0.353885654057 0.320544729867 0.353280499623 0.315621795536 0.312176062734 0.301562130879 0.312061680573 0.312642847966 0.326222109701 0.357417912858 0.313083593142 0.334033412713 0.295630506074 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc396c8c16032b9657101532f7e08e6aa04b2aea --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt @@ -0,0 +1 @@ + 5.0817745e-01 4.4535192e-01 5.6700421e-01 1.2418578e-01 4.8927739e-01 5.0180477e-01 1.4096146e-01 8.1242502e-01 4.1586001e-01 3.2586371e-01 3.2586371e-01 5.2942799e-01 8.6137722e-01 7.7039952e-01 9.7270522e-01 4.5581864e-01 1.0000000e-01 6.3861009e-01 3.0546431e-01 3.7427929e-01 2.5251796e-01 5.6700421e-01 3.8776762e-01 5.2942799e-01 5.0905001e-01 2.5651975e-01 1.2418578e-01 1.2418578e-01 4.5470518e-01 4.5470518e-01 3.2816937e-01 6.0181382e-01 7.3457830e-01 4.1586001e-01 3.2586371e-01 4.0147421e-01 4.1586001e-01 7.6752131e-01 1.2418578e-01 1.4096146e-01 1.2396136e+00 7.1462831e-01 4.1449626e-01 5.3588338e-01 5.2942799e-01 3.2352160e-01 5.2862779e-01 2.5251796e-01 2.0656129e-01 3.5031395e+00 3.2158090e+00 3.6682165e+00 2.7164367e+00 3.3288934e+00 3.1477087e+00 3.4033622e+00 2.0308266e+00 3.3209346e+00 2.5912926e+00 2.3257069e+00 2.8912179e+00 2.7273721e+00 3.3660466e+00 2.2876649e+00 3.1664710e+00 3.1642132e+00 2.7448172e+00 3.2474124e+00 2.5734684e+00 3.5025969e+00 2.6980573e+00 3.5983434e+00 3.3515288e+00 3.0113552e+00 3.1469325e+00 3.5526357e+00 3.7475562e+00 3.1812462e+00 2.1818668e+00 2.4927109e+00 2.3909738e+00 2.5729378e+00 3.7711998e+00 3.1620401e+00 3.1916270e+00 3.4478147e+00 3.1312883e+00 2.7541224e+00 2.6886547e+00 3.0483897e+00 3.2685282e+00 2.6752185e+00 2.0587064e+00 2.8619072e+00 2.8416143e+00 2.8554471e+00 2.9845926e+00 1.7697734e+00 2.7640668e+00 4.7690606e+00 3.8067806e+00 4.6866422e+00 4.2843668e+00 4.5417384e+00 5.4120246e+00 3.2161426e+00 5.0569442e+00 4.5165793e+00 4.9462324e+00 3.8595100e+00 4.0249346e+00 4.2787236e+00 3.7387507e+00 3.9160762e+00 4.0938708e+00 4.2028863e+00 5.5316487e+00 5.7297286e+00 3.6968486e+00 4.5074741e+00 3.6330985e+00 5.5146761e+00 3.6293227e+00 4.4495340e+00 4.7599229e+00 3.5255287e+00 3.6076762e+00 4.3339547e+00 4.5590471e+00 4.8997298e+00 5.2856169e+00 4.3511402e+00 3.7760534e+00 4.2460554e+00 5.0103780e+00 4.3808704e+00 4.1939019e+00 3.5087649e+00 4.2018804e+00 4.4140402e+00 3.9807996e+00 3.8067806e+00 4.6775324e+00 4.5250934e+00 4.0376133e+00 3.7473276e+00 3.9523060e+00 4.1709262e+00 3.7872951e+00 2.5251796e-01 3.0546431e-01 6.0060595e-01 9.5035453e-01 4.4535192e-01 4.0293660e-01 5.0090417e-01 1.4096146e-01 7.6752131e-01 4.1449626e-01 1.2418578e-01 6.2024833e-01 1.1845977e+00 1.4700179e+00 9.4309624e-01 5.0905001e-01 1.0003617e+00 8.0358695e-01 5.8851328e-01 7.0826681e-01 6.6384020e-01 4.3456114e-01 5.6700421e-01 2.0656129e-01 4.2667565e-01 5.2942799e-01 4.4417983e-01 2.8192292e-01 2.1269358e-01 5.7324170e-01 1.1056650e+00 1.2393677e+00 1.4096146e-01 2.5251796e-01 6.8961791e-01 1.4096146e-01 5.0090417e-01 4.1449626e-01 5.0270183e-01 7.3535471e-01 5.0905001e-01 5.7324170e-01 8.5690100e-01 1.2418578e-01 8.0587320e-01 3.2352160e-01 7.3496673e-01 3.0275928e-01 3.5601468e+00 3.2472699e+00 3.7137483e+00 2.6693888e+00 3.3563815e+00 3.1472333e+00 3.4276314e+00 1.9506288e+00 3.3563695e+00 2.5739370e+00 2.1870094e+00 2.9033014e+00 2.6860278e+00 3.3789262e+00 2.2884830e+00 3.2153154e+00 3.1667333e+00 2.7423060e+00 3.2269725e+00 2.5465772e+00 3.5123782e+00 2.7147889e+00 3.6030381e+00 3.3619470e+00 3.0427908e+00 3.1888219e+00 3.5910272e+00 3.7805671e+00 3.1921903e+00 2.1611020e+00 2.4491518e+00 2.3430978e+00 2.5700421e+00 3.7741357e+00 3.1615131e+00 3.2084454e+00 3.4884789e+00 3.1228939e+00 2.7575407e+00 2.6617768e+00 3.0343591e+00 3.2842184e+00 2.6656374e+00 1.9595652e+00 2.8539100e+00 2.8474367e+00 2.8585579e+00 3.0059712e+00 1.6867642e+00 2.7634340e+00 4.7806735e+00 3.8055585e+00 4.7194850e+00 4.2963997e+00 4.5579706e+00 5.4507801e+00 3.1945300e+00 5.0903533e+00 4.5297786e+00 4.9814379e+00 3.8841455e+00 4.0376849e+00 4.3069372e+00 3.7284750e+00 3.9173293e+00 4.1124749e+00 4.2221165e+00 5.5759608e+00 5.7633066e+00 3.6758942e+00 4.5370189e+00 3.6312130e+00 5.5536680e+00 3.6416405e+00 4.4736906e+00 4.7961103e+00 3.5380868e+00 3.6203213e+00 4.3467079e+00 4.5977693e+00 4.9380624e+00 5.3421274e+00 4.3637834e+00 3.7899304e+00 4.2477635e+00 5.0602038e+00 4.3953045e+00 4.2110583e+00 3.5192753e+00 4.2358121e+00 4.4378207e+00 4.0189525e+00 3.8055585e+00 4.7017335e+00 4.5483787e+00 4.0656879e+00 3.7516222e+00 3.9742971e+00 4.1845313e+00 3.7939847e+00 2.1269358e-01 4.4535192e-01 8.9366705e-01 2.1845981e-01 3.4378533e-01 3.7427929e-01 2.5651975e-01 7.7039952e-01 3.2586371e-01 2.1845981e-01 4.2667565e-01 1.2113327e+00 1.3801284e+00 8.7175869e-01 4.4651726e-01 1.0719360e+00 6.5223271e-01 7.3813096e-01 5.7867728e-01 4.4535192e-01 5.2655962e-01 6.0611244e-01 3.8776762e-01 4.0176783e-01 5.3588338e-01 5.0905001e-01 3.0000000e-01 3.0546431e-01 7.1169738e-01 9.4309624e-01 1.1327825e+00 2.5651975e-01 3.0275928e-01 8.1067767e-01 2.5651975e-01 3.2352160e-01 4.2538717e-01 3.7427929e-01 9.0252542e-01 3.0000000e-01 5.1138698e-01 7.7869083e-01 2.1845981e-01 6.6384020e-01 1.2418578e-01 6.9325418e-01 3.0546431e-01 3.7098973e+00 3.3770904e+00 3.8553941e+00 2.7868575e+00 3.4895316e+00 3.2571492e+00 3.5499573e+00 2.0646687e+00 3.4944845e+00 2.6743800e+00 2.3196869e+00 3.0181476e+00 2.8270253e+00 3.4973911e+00 2.3997585e+00 3.3600102e+00 3.2716172e+00 2.8619072e+00 3.3597438e+00 2.6649106e+00 3.6203213e+00 2.8440609e+00 3.7280682e+00 3.4822008e+00 3.1786890e+00 3.3296038e+00 3.7325066e+00 3.9121945e+00 3.3084060e+00 2.2888897e+00 2.5683989e+00 2.4649412e+00 2.6906230e+00 3.8866112e+00 3.2625043e+00 3.3219248e+00 3.6264668e+00 3.2609948e+00 2.8656468e+00 2.7738624e+00 3.1430282e+00 3.4033622e+00 2.7865812e+00 2.0797392e+00 2.9638836e+00 2.9589097e+00 2.9695568e+00 3.1337459e+00 1.7991433e+00 2.8758936e+00 4.8875515e+00 3.9111857e+00 4.8490379e+00 4.4107143e+00 4.6725771e+00 5.5854254e+00 3.2933477e+00 5.2226262e+00 4.6541348e+00 5.1068487e+00 4.0049607e+00 4.1564977e+00 4.4321573e+00 3.8331006e+00 4.0161098e+00 4.2255639e+00 4.3417782e+00 5.7091264e+00 5.8970064e+00 3.7961619e+00 4.6611065e+00 3.7313856e+00 5.6903014e+00 3.7618406e+00 4.5942943e+00 4.9290197e+00 3.6553612e+00 3.7333492e+00 4.4613366e+00 4.7342792e+00 5.0749049e+00 5.4844039e+00 4.4774673e+00 3.9102500e+00 4.3611782e+00 5.2016658e+00 4.5034762e+00 4.3281161e+00 3.6300436e+00 4.3648112e+00 4.5562166e+00 4.1482002e+00 3.9111857e+00 4.8218416e+00 4.6648403e+00 4.1879434e+00 3.8717400e+00 4.0945154e+00 4.2919258e+00 3.9013483e+00 5.6700421e-01 9.9714776e-01 3.0546431e-01 4.4417983e-01 2.5251796e-01 3.0275928e-01 8.8835966e-01 3.2586371e-01 2.1845981e-01 4.4651726e-01 1.3360558e+00 1.5022608e+00 9.9714776e-01 5.6769031e-01 1.1765359e+00 7.6752131e-01 8.1354181e-01 6.9325418e-01 6.2092891e-01 5.4292906e-01 4.5470518e-01 4.0293660e-01 4.5581864e-01 6.4704320e-01 6.2024833e-01 1.4096146e-01 2.0656129e-01 8.1354181e-01 1.0574300e+00 1.2554784e+00 3.0275928e-01 4.4535192e-01 9.2264612e-01 3.0275928e-01 2.5251796e-01 5.2862779e-01 5.0592043e-01 8.0358695e-01 2.5251796e-01 5.6454040e-01 7.9878917e-01 2.1845981e-01 7.6752131e-01 1.2418578e-01 8.1242502e-01 4.1449626e-01 3.5875094e+00 3.2277825e+00 3.7190120e+00 2.6019240e+00 3.3414931e+00 3.0741797e+00 3.3904673e+00 1.8683030e+00 3.3506325e+00 2.4892190e+00 2.1209506e+00 2.8530088e+00 2.6606291e+00 3.3264150e+00 2.2345869e+00 3.2325480e+00 3.0894572e+00 2.6859989e+00 3.1954750e+00 2.4836725e+00 3.4467337e+00 2.6928468e+00 3.5602810e+00 3.3090659e+00 3.0346426e+00 3.1953687e+00 3.5930845e+00 3.7635112e+00 3.1392617e+00 2.1242643e+00 2.3839455e+00 2.2806773e+00 2.5225548e+00 3.7070070e+00 3.0760590e+00 3.1551922e+00 3.4865435e+00 3.1033781e+00 2.6867856e+00 2.5906376e+00 2.9536363e+00 3.2348458e+00 2.6148507e+00 1.8841403e+00 2.7819255e+00 2.7801917e+00 2.7920574e+00 2.9774862e+00 1.6195190e+00 2.7001131e+00 4.7151191e+00 3.7310738e+00 4.6963107e+00 4.2348119e+00 4.5036643e+00 5.4345951e+00 3.1040223e+00 5.0660245e+00 4.4858951e+00 4.9576471e+00 3.8485743e+00 3.9894963e+00 4.2781102e+00 3.6535208e+00 3.8473084e+00 4.0656969e+00 4.1736133e+00 5.5611269e+00 5.7439963e+00 3.6142694e+00 4.5082936e+00 3.5527533e+00 5.5400450e+00 3.5988819e+00 4.4321573e+00 4.7754556e+00 3.4913787e+00 3.5638529e+00 4.2915574e+00 4.5844335e+00 4.9269527e+00 5.3501611e+00 4.3091163e+00 3.7395252e+00 4.1763853e+00 5.0687940e+00 4.3363292e+00 4.1568278e+00 3.4594086e+00 4.2175903e+00 4.4004449e+00 4.0139427e+00 3.7310738e+00 4.6611132e+00 4.5083524e+00 4.0415593e+00 3.7070350e+00 3.9354060e+00 4.1243443e+00 3.7225506e+00 4.8927739e-01 4.1449626e-01 2.0656129e-01 8.1242502e-01 5.0270183e-01 4.0293660e-01 2.8192292e-01 6.0611244e-01 8.2305664e-01 8.2899253e-01 9.3824087e-01 4.5581864e-01 1.4096146e-01 7.1840099e-01 2.1845981e-01 4.5470518e-01 2.1845981e-01 4.9674312e-01 4.2418962e-01 5.1607523e-01 6.0551856e-01 2.8192292e-01 2.1269358e-01 2.4837156e-01 4.5470518e-01 5.1607523e-01 4.2667565e-01 5.0991930e-01 6.8917100e-01 5.0270183e-01 4.1312257e-01 5.0180477e-01 5.0270183e-01 7.4549115e-01 2.1269358e-01 1.4096146e-01 1.3190071e+00 6.4755655e-01 4.1449626e-01 5.1691876e-01 6.0611244e-01 2.5251796e-01 4.9674312e-01 3.0546431e-01 3.0000000e-01 3.5310961e+00 3.2313174e+00 3.6912396e+00 2.7363446e+00 3.3486156e+00 3.1550780e+00 3.4146950e+00 2.0587064e+00 3.3422688e+00 2.6000813e+00 2.3658814e+00 2.9005672e+00 2.7581254e+00 3.3764180e+00 2.2982662e+00 3.1914715e+00 3.1684808e+00 2.7581145e+00 3.2719146e+00 2.5906376e+00 3.5076679e+00 2.7164648e+00 3.6146980e+00 3.3629944e+00 3.0317688e+00 3.1699749e+00 3.5767944e+00 3.7653940e+00 3.1912695e+00 2.2046610e+00 2.5131017e+00 2.4132939e+00 2.5882494e+00 3.7797776e+00 3.1649733e+00 3.1986968e+00 3.4685882e+00 3.1575873e+00 2.7599092e+00 2.7031874e+00 3.0575551e+00 3.2787144e+00 2.6914804e+00 2.0914773e+00 2.8714673e+00 2.8482104e+00 2.8631525e+00 3.0002861e+00 1.8009624e+00 2.7738624e+00 4.7744685e+00 3.8132783e+00 4.7036953e+00 4.2925903e+00 4.5507995e+00 5.4317036e+00 3.2245243e+00 5.0748136e+00 4.5314818e+00 4.9621679e+00 3.8715927e+00 4.0372136e+00 4.2937599e+00 3.7469906e+00 3.9213497e+00 4.1030149e+00 4.2136261e+00 5.5512721e+00 5.7499082e+00 3.7127205e+00 4.5218897e+00 3.6377830e+00 5.5357771e+00 3.6429670e+00 4.4609633e+00 4.7775824e+00 3.5373240e+00 3.6158814e+00 4.3437318e+00 4.5790474e+00 4.9211035e+00 5.3110568e+00 4.3608329e+00 3.7876656e+00 4.2543813e+00 5.0356467e+00 4.3872625e+00 4.2028863e+00 3.5161021e+00 4.2189979e+00 4.4261470e+00 4.0000622e+00 3.8132783e+00 4.6893387e+00 4.5361087e+00 4.0527696e+00 3.7622948e+00 3.9645936e+00 4.1768667e+00 3.7924679e+00 8.6137722e-01 5.7867728e-01 1.2470767e+00 8.6361309e-01 2.8192292e-01 6.9369532e-01 9.8450810e-01 1.2949422e+00 5.7324170e-01 5.3588338e-01 4.0000000e-01 4.8135521e-01 3.0546431e-01 3.2816937e-01 5.0817745e-01 3.4378533e-01 9.4558103e-01 6.2024833e-01 6.9728513e-01 9.2288144e-01 5.6700421e-01 4.3691963e-01 5.4292906e-01 8.7202528e-01 8.9095811e-01 5.0817745e-01 3.6171588e-01 3.8934542e-01 8.6361309e-01 7.9878917e-01 5.0592043e-01 8.6361309e-01 1.1959482e+00 5.4292906e-01 5.6454040e-01 1.6807352e+00 1.1055064e+00 5.0592043e-01 3.2586371e-01 9.7779835e-01 3.2816937e-01 9.4558103e-01 2.8507955e-01 6.6827038e-01 3.1533911e+00 2.8840079e+00 3.3274872e+00 2.5335921e+00 3.0169509e+00 2.8661222e+00 3.0732956e+00 1.9492232e+00 3.0013391e+00 2.3437032e+00 2.3116343e+00 2.5873149e+00 2.5591371e+00 3.0631725e+00 2.0220740e+00 2.8270253e+00 2.8656468e+00 2.4892190e+00 3.0178921e+00 2.3656538e+00 3.1846482e+00 2.4132559e+00 3.3163294e+00 3.0590735e+00 2.6993871e+00 2.8174914e+00 3.2310326e+00 3.4162231e+00 2.8802219e+00 1.9932786e+00 2.3173648e+00 2.2314118e+00 2.3212593e+00 3.4779999e+00 2.8654680e+00 2.8662571e+00 3.1113805e+00 2.8927401e+00 2.4634131e+00 2.4685230e+00 2.7948819e+00 2.9596963e+00 2.4341346e+00 2.0039447e+00 2.6000813e+00 2.5498770e+00 2.5700421e+00 2.6813098e+00 1.7123398e+00 2.4913669e+00 4.4418755e+00 3.5123791e+00 4.3488707e+00 3.9713081e+00 4.2172545e+00 5.0700045e+00 2.9631582e+00 4.7239900e+00 4.2113881e+00 4.5979409e+00 3.5255287e+00 3.7162377e+00 3.9448212e+00 3.4598280e+00 3.6097419e+00 3.7620043e+00 3.8810240e+00 5.1822310e+00 5.3953096e+00 3.4508156e+00 4.1665786e+00 3.3353616e+00 5.1763300e+00 3.3260356e+00 4.1143832e+00 4.4201622e+00 3.2188998e+00 3.2929599e+00 4.0183758e+00 4.2229849e+00 4.5637045e+00 4.9290256e+00 4.0343724e+00 3.4708900e+00 3.9559935e+00 4.6576736e+00 4.0502252e+00 3.8718131e+00 3.1963475e+00 3.8610636e+00 4.0785553e+00 3.6345765e+00 3.5123791e+00 4.3416284e+00 4.1864302e+00 3.7018916e+00 3.4568305e+00 3.6254423e+00 3.8415026e+00 3.4775621e+00 4.0293660e-01 5.0905001e-01 3.8934542e-01 8.1130291e-01 2.5251796e-01 4.2538717e-01 4.8927739e-01 1.2406194e+00 1.3074132e+00 8.5233811e-01 5.0090417e-01 1.1185330e+00 5.6700421e-01 8.1099042e-01 5.3022554e-01 4.1449626e-01 5.3665999e-01 5.0905001e-01 5.0592043e-01 4.1449626e-01 6.0181382e-01 6.0060595e-01 2.5651975e-01 3.4583729e-01 8.0064372e-01 8.1558458e-01 1.0597541e+00 3.8934542e-01 4.2667565e-01 9.0074515e-01 3.8934542e-01 4.1586001e-01 5.0180477e-01 4.0293660e-01 1.1003197e+00 2.5651975e-01 4.5581864e-01 6.6539428e-01 4.1312257e-01 5.7324170e-01 2.0656129e-01 7.1504098e-01 4.0293660e-01 3.6583368e+00 3.3018939e+00 3.7934214e+00 2.7118627e+00 3.4196168e+00 3.1646752e+00 3.4663954e+00 1.9965608e+00 3.4302944e+00 2.5753574e+00 2.2837561e+00 2.9283888e+00 2.7788099e+00 3.4116298e+00 2.3101107e+00 3.3028359e+00 3.1719381e+00 2.7826178e+00 3.2957091e+00 2.5882494e+00 3.5217244e+00 2.7724782e+00 3.6509512e+00 3.3998935e+00 3.1126354e+00 3.2681313e+00 3.6719821e+00 3.8384263e+00 3.2202056e+00 2.2240476e+00 2.4960474e+00 2.3970928e+00 2.6119950e+00 3.7951491e+00 3.1592993e+00 3.2300555e+00 3.5604418e+00 3.2025128e+00 2.7701355e+00 2.6892823e+00 3.0524247e+00 3.3177721e+00 2.7092568e+00 2.0227167e+00 2.8731220e+00 2.8671099e+00 2.8775912e+00 3.0586720e+00 1.7332099e+00 2.7865812e+00 4.7866828e+00 3.8123695e+00 4.7714708e+00 4.3189924e+00 4.5796358e+00 5.5132325e+00 3.1921277e+00 5.1489022e+00 4.5737586e+00 5.0249531e+00 3.9185849e+00 4.0697987e+00 4.3502657e+00 3.7349501e+00 3.9102370e+00 4.1311343e+00 4.2548570e+00 5.6362307e+00 5.8240252e+00 3.7174048e+00 4.5773480e+00 3.6270581e+00 5.6209145e+00 3.6774027e+00 4.5072397e+00 4.8555167e+00 3.5675580e+00 3.6401392e+00 4.3693804e+00 4.6657186e+00 5.0062061e+00 5.4227201e+00 4.3844239e+00 3.8261182e+00 4.2718480e+00 5.1373047e+00 4.4043123e+00 4.2383633e+00 3.5347392e+00 4.2868650e+00 4.4668117e+00 4.0716645e+00 3.8123695e+00 4.7338066e+00 4.5734052e+00 4.1036179e+00 3.7882079e+00 4.0078491e+00 4.1922661e+00 3.8027591e+00 6.8961791e-01 3.0546431e-01 4.4417983e-01 2.0656129e-01 4.1586001e-01 7.6625946e-01 8.9687438e-01 1.0919712e+00 5.7867728e-01 1.5422108e-01 7.3851529e-01 4.0293660e-01 4.1312257e-01 3.2586371e-01 5.7257017e-01 3.2816937e-01 4.1312257e-01 4.0147421e-01 2.0656129e-01 2.0656129e-01 2.0656129e-01 3.2586371e-01 3.2586371e-01 4.1312257e-01 7.0437330e-01 8.5205778e-01 3.0546431e-01 3.2352160e-01 5.0905001e-01 3.0546431e-01 6.5172743e-01 1.0000000e-01 2.1269358e-01 1.1283882e+00 6.1092863e-01 4.0293660e-01 5.0592043e-01 4.1586001e-01 4.0293660e-01 4.1449626e-01 3.7255734e-01 1.2418578e-01 3.4445326e+00 3.1392617e+00 3.6011035e+00 2.6118700e+00 3.2516941e+00 3.0511838e+00 3.3218097e+00 1.9189245e+00 3.2468925e+00 2.4924452e+00 2.2081024e+00 2.8038661e+00 2.6291264e+00 3.2767369e+00 2.1964719e+00 3.1025274e+00 3.0696611e+00 2.6485861e+00 3.1554034e+00 2.4715204e+00 3.4135983e+00 2.6151245e+00 3.5092032e+00 3.2604423e+00 2.9354140e+00 3.0782101e+00 3.4818889e+00 3.6726568e+00 3.0922811e+00 2.0843471e+00 2.3874354e+00 2.2845234e+00 2.4794505e+00 3.6775470e+00 3.0659000e+00 3.1055388e+00 3.3775462e+00 3.0430948e+00 2.6597612e+00 2.5873149e+00 2.9471553e+00 3.1807044e+00 2.5795723e+00 1.9450499e+00 2.7640668e+00 2.7473221e+00 2.7611864e+00 2.9015702e+00 1.6626642e+00 2.6693888e+00 4.6823704e+00 3.7130994e+00 4.6117428e+00 4.1946425e+00 4.4565357e+00 5.3399939e+00 3.1168466e+00 4.9805386e+00 4.4303862e+00 4.8738189e+00 3.7806643e+00 3.9387918e+00 4.2018804e+00 3.6441274e+00 3.8290120e+00 4.0132700e+00 4.1177139e+00 5.4615788e+00 5.6559440e+00 3.5983434e+00 4.4321573e+00 3.5405803e+00 5.4429455e+00 3.5441556e+00 4.3687483e+00 4.6853394e+00 3.4399664e+00 3.5203203e+00 4.2473048e+00 4.4861009e+00 4.8281381e+00 5.2242271e+00 4.2652659e+00 3.6876909e+00 4.1503255e+00 4.9488209e+00 4.2966585e+00 4.1071698e+00 3.4205830e+00 4.1292490e+00 4.3363292e+00 3.9150359e+00 3.7130994e+00 4.5977729e+00 4.4473292e+00 3.9643224e+00 3.6603913e+00 3.8715927e+00 4.0861975e+00 3.6954796e+00 5.0991930e-01 1.1327825e+00 5.7257017e-01 4.0293660e-01 3.0811765e-01 1.5771666e+00 1.7488874e+00 1.2431040e+00 8.1273630e-01 1.4170618e+00 1.0106392e+00 1.0389435e+00 9.3824087e-01 7.3813096e-01 7.5976039e-01 6.6491075e-01 6.0611244e-01 6.9728513e-01 8.8861541e-01 8.5177726e-01 3.8776762e-01 4.2538717e-01 1.0346741e+00 1.2943100e+00 1.5015203e+00 5.0991930e-01 6.2482915e-01 1.1473003e+00 5.0991930e-01 1.2418578e-01 7.6752131e-01 7.4586719e-01 6.0181382e-01 3.0275928e-01 7.7869083e-01 1.0440187e+00 4.0293660e-01 1.0120221e+00 3.2352160e-01 1.0597541e+00 6.4704320e-01 3.7504939e+00 3.3717768e+00 3.8731169e+00 2.7062054e+00 3.4865562e+00 3.1921903e+00 3.5262546e+00 1.9522524e+00 3.5018009e+00 2.5914913e+00 2.1818668e+00 2.9807120e+00 2.7874290e+00 3.4557351e+00 2.3604042e+00 3.3915488e+00 3.2027420e+00 2.8150728e+00 3.3206640e+00 2.6018930e+00 3.5642457e+00 2.8360166e+00 3.6902583e+00 3.4394878e+00 3.1847477e+00 3.3503379e+00 3.7461474e+00 3.9068076e+00 3.2666666e+00 2.2590074e+00 2.4950353e+00 2.3935209e+00 2.6534332e+00 3.8259590e+00 3.1834936e+00 3.2834077e+00 3.6377049e+00 3.2390016e+00 2.8060305e+00 2.7012392e+00 3.0647279e+00 3.3658240e+00 2.7423171e+00 1.9645331e+00 2.8984764e+00 2.9033203e+00 2.9139413e+00 3.1189900e+00 1.7118795e+00 2.8228127e+00 4.8290847e+00 3.8416142e+00 4.8350745e+00 4.3569606e+00 4.6261788e+00 5.5774554e+00 3.1958228e+00 5.2067803e+00 4.6153241e+00 5.0947934e+00 3.9803199e+00 4.1159553e+00 4.4131159e+00 3.7587872e+00 3.9513472e+00 4.1881498e+00 4.3024754e+00 5.7071195e+00 5.8839539e+00 3.7280682e+00 4.6419531e+00 3.6578722e+00 5.6843788e+00 3.7276068e+00 4.5625120e+00 4.9179194e+00 3.6182608e+00 3.6866535e+00 4.4136707e+00 4.7307689e+00 5.0723886e+00 5.5062533e+00 4.4301849e+00 3.8690719e+00 4.2943891e+00 5.2192815e+00 4.4536259e+00 4.2828634e+00 3.5797958e+00 4.3570079e+00 4.5278761e+00 4.1542558e+00 3.8416142e+00 4.7899951e+00 4.6341170e+00 4.1740602e+00 3.8316735e+00 4.0656969e+00 4.2413113e+00 3.8376713e+00 6.8961791e-01 3.0811765e-01 1.4096146e-01 6.4755655e-01 1.1229906e+00 1.3835747e+00 8.6361309e-01 4.2667565e-01 9.4009473e-01 7.0784540e-01 5.3665999e-01 6.2482915e-01 6.3977563e-01 4.3691963e-01 4.4651726e-01 1.5422108e-01 3.7598397e-01 4.4535192e-01 3.7598397e-01 2.1845981e-01 1.4096146e-01 5.5419992e-01 1.0065841e+00 1.1474460e+00 0.0000000e+00 3.0811765e-01 6.5223271e-01 0.0000000e+00 5.0991930e-01 3.2586371e-01 4.2667565e-01 8.3172002e-01 5.0991930e-01 5.6769031e-01 7.5082357e-01 2.1845981e-01 7.0479928e-01 3.0811765e-01 6.4755655e-01 2.1845981e-01 3.4865562e+00 3.1726595e+00 3.6377960e+00 2.5987470e+00 3.2814045e+00 3.0627375e+00 3.3515846e+00 1.8841865e+00 3.2769379e+00 2.5038079e+00 2.1311468e+00 2.8311678e+00 2.6104387e+00 3.2962520e+00 2.2214438e+00 3.1433122e+00 3.0878634e+00 2.6552472e+00 3.1570103e+00 2.4668912e+00 3.4394878e+00 2.6411293e+00 3.5233648e+00 3.2747247e+00 2.9659871e+00 3.1154783e+00 3.5134741e+00 3.7059620e+00 3.1148696e+00 2.0851901e+00 2.3731428e+00 2.2655571e+00 2.4927109e+00 3.6920087e+00 3.0823446e+00 3.1337459e+00 3.4135200e+00 3.0481703e+00 2.6780487e+00 2.5874301e+00 2.9489507e+00 3.2027420e+00 2.5873149e+00 1.8973383e+00 2.7738355e+00 2.7632614e+00 2.7778954e+00 2.9269923e+00 1.6390769e+00 2.6848587e+00 4.7106706e+00 3.7313856e+00 4.6446321e+00 4.2142736e+00 4.4836580e+00 5.3716885e+00 3.1250284e+00 5.0074019e+00 4.4485220e+00 4.9128219e+00 3.8150636e+00 3.9624529e+00 4.2358121e+00 3.6602286e+00 3.8605980e+00 4.0488387e+00 4.1418643e+00 5.4970002e+00 5.6855224e+00 3.5964347e+00 4.4685630e+00 3.5634461e+00 5.4730406e+00 3.5693950e+00 4.3989089e+00 4.7150659e+00 3.4668130e+00 3.5464993e+00 4.2723380e+00 4.5155386e+00 4.8594290e+00 5.2647079e+00 4.2921213e+00 3.7064459e+00 4.1581964e+00 4.9913682e+00 4.3286007e+00 4.1303097e+00 3.4468286e+00 4.1669742e+00 4.3729308e+00 3.9624170e+00 3.7313856e+00 4.6297577e+00 4.4844827e+00 4.0056359e+00 3.6817961e+00 3.9035218e+00 4.1179678e+00 3.7164366e+00 6.2024833e-01 8.1304731e-01 1.1868139e+00 4.8036801e-01 7.1799256e-01 2.8192292e-01 3.2816937e-01 3.2816937e-01 3.0546431e-01 3.2352160e-01 3.2352160e-01 8.5205778e-01 4.8927739e-01 6.6384020e-01 7.3496673e-01 4.5581864e-01 2.4837156e-01 3.2586371e-01 7.6752131e-01 7.4549115e-01 3.2352160e-01 4.1449626e-01 5.0180477e-01 6.8961791e-01 5.8851328e-01 2.5251796e-01 6.8961791e-01 1.0919712e+00 3.7255734e-01 4.2667565e-01 1.4993782e+00 1.0344911e+00 5.0592043e-01 4.5581864e-01 8.1304731e-01 3.0546431e-01 8.5205778e-01 1.0000000e-01 4.9766035e-01 3.3472053e+00 3.0922811e+00 3.5254266e+00 2.6661987e+00 3.2094276e+00 3.0570957e+00 3.2869053e+00 2.0190980e+00 3.1913594e+00 2.5206151e+00 2.3403819e+00 2.7928582e+00 2.6680945e+00 3.2615924e+00 2.2070201e+00 3.0233425e+00 3.0716969e+00 2.6575076e+00 3.1694367e+00 2.5088543e+00 3.4030318e+00 2.5954147e+00 3.4988409e+00 3.2483608e+00 2.8891737e+00 3.0123702e+00 3.4182420e+00 3.6203759e+00 3.0811775e+00 2.1190324e+00 2.4416796e+00 2.3440712e+00 2.4897570e+00 3.6753309e+00 3.0715435e+00 3.0851463e+00 3.3123070e+00 3.0424689e+00 2.6625505e+00 2.6241824e+00 2.9689697e+00 3.1616811e+00 2.5961850e+00 2.0559262e+00 2.7803619e+00 2.7462372e+00 2.7639489e+00 2.8736288e+00 1.7674365e+00 2.6773131e+00 4.6660957e+00 3.7173526e+00 4.5567672e+00 4.1782968e+00 4.4326194e+00 5.2720689e+00 3.1469325e+00 4.9232255e+00 4.4057732e+00 4.8164157e+00 3.7433882e+00 3.9194796e+00 4.1567419e+00 3.6582432e+00 3.8303544e+00 3.9861488e+00 4.0892044e+00 5.3882212e+00 5.5946413e+00 3.6180819e+00 4.3839191e+00 3.5469476e+00 5.3734444e+00 3.5262672e+00 4.3306501e+00 4.6237863e+00 3.4237160e+00 3.5051302e+00 4.2288456e+00 4.4201622e+00 4.7609637e+00 5.1280035e+00 4.2469785e+00 3.6684143e+00 4.1480002e+00 4.8602572e+00 4.2765700e+00 4.0824098e+00 3.4092877e+00 4.0737132e+00 4.2991233e+00 3.8524190e+00 3.7173526e+00 4.5590471e+00 4.4107160e+00 3.9202843e+00 3.6509512e+00 3.8388884e+00 4.0680120e+00 3.6894983e+00 4.1449626e-01 6.6539428e-01 1.0717668e+00 1.1847335e+00 7.0776547e-01 3.2816937e-01 9.2095040e-01 4.4651726e-01 6.0060595e-01 3.8934542e-01 6.1092863e-01 3.7598397e-01 3.0000000e-01 4.1312257e-01 2.4837156e-01 4.0293660e-01 4.1312257e-01 2.0656129e-01 3.0000000e-01 6.0611244e-01 7.3535471e-01 9.3801395e-01 3.0811765e-01 4.2538717e-01 7.1462831e-01 3.0811765e-01 5.2574978e-01 3.0275928e-01 3.2816937e-01 1.1107977e+00 4.5470518e-01 4.1449626e-01 4.8927739e-01 4.1449626e-01 4.4417983e-01 2.8192292e-01 5.2942799e-01 2.5251796e-01 3.4297053e+00 3.0906838e+00 3.5704156e+00 2.5301680e+00 3.2062204e+00 2.9663489e+00 3.2615889e+00 1.8330979e+00 3.2074600e+00 2.4030878e+00 2.1292724e+00 2.7344480e+00 2.5716369e+00 3.2053511e+00 2.1242643e+00 3.0798277e+00 2.9831836e+00 2.5729378e+00 3.0964590e+00 2.3917863e+00 3.3353616e+00 2.5635110e+00 3.4441347e+00 3.1882407e+00 2.8938821e+00 3.0477086e+00 3.4484194e+00 3.6265826e+00 3.0209783e+00 2.0203134e+00 2.3063579e+00 2.2046610e+00 2.4100833e+00 3.5972040e+00 2.9748436e+00 3.0349291e+00 3.3414931e+00 2.9918962e+00 2.5764694e+00 2.5038051e+00 2.8573838e+00 3.1113597e+00 2.5079404e+00 1.8623849e+00 2.6799601e+00 2.6656374e+00 2.6804452e+00 2.8458006e+00 1.5870088e+00 2.5906376e+00 4.6056614e+00 3.6293396e+00 4.5625120e+00 4.1184849e+00 4.3862724e+00 5.2957861e+00 3.0253131e+00 4.9300368e+00 4.3656957e+00 4.8256905e+00 3.7228356e+00 3.8717400e+00 4.1487889e+00 3.5605424e+00 3.7509165e+00 3.9489970e+00 4.0502806e+00 5.4193574e+00 5.6096505e+00 3.5201263e+00 4.3797165e+00 3.4555095e+00 5.4004015e+00 3.4805320e+00 4.3069452e+00 4.6373516e+00 3.3738930e+00 3.4478147e+00 4.1762321e+00 4.4428877e+00 4.7870294e+00 5.1982218e+00 4.1948678e+00 3.6180819e+00 4.0668114e+00 4.9227056e+00 4.2245318e+00 4.0358897e+00 3.3459883e+00 4.0835979e+00 4.2783731e+00 3.8797354e+00 3.6293396e+00 4.5370189e+00 4.3879553e+00 3.9155334e+00 3.5955337e+00 3.8113970e+00 4.0131848e+00 3.6132595e+00 5.2862779e-01 1.2431040e+00 1.5013525e+00 9.7779835e-01 5.3588338e-01 1.0669582e+00 8.1385214e-01 6.6432544e-01 7.2823007e-01 6.5223271e-01 5.1138698e-01 5.6700421e-01 2.5251796e-01 4.6472023e-01 5.6769031e-01 4.9766035e-01 2.5651975e-01 2.1269358e-01 6.6432544e-01 1.1134787e+00 1.2632199e+00 1.4096146e-01 2.8507955e-01 7.6787403e-01 1.4096146e-01 4.0293660e-01 4.4651726e-01 5.1691876e-01 7.1840099e-01 4.1586001e-01 6.3108414e-01 8.7021234e-01 2.0000000e-01 8.1385214e-01 2.5251796e-01 7.6787403e-01 3.2586371e-01 3.6025735e+00 3.2810515e+00 3.7511944e+00 2.6894009e+00 3.3904673e+00 3.1636869e+00 3.4574937e+00 1.9666356e+00 3.3893691e+00 2.5954173e+00 2.1997395e+00 2.9322283e+00 2.7092568e+00 3.4012145e+00 2.3186758e+00 3.2568914e+00 3.1861493e+00 2.7595194e+00 3.2561045e+00 2.5646808e+00 3.5381764e+00 2.7476411e+00 3.6278993e+00 3.3809159e+00 3.0768226e+00 3.2277675e+00 3.6265617e+00 3.8150532e+00 3.2176230e+00 2.1864840e+00 2.4668912e+00 2.3596992e+00 2.5949561e+00 3.7935487e+00 3.1789378e+00 3.2360886e+00 3.5252258e+00 3.1522058e+00 2.7777040e+00 2.6819136e+00 3.0473722e+00 3.3079290e+00 2.6886547e+00 1.9757309e+00 2.8726212e+00 2.8654680e+00 2.8788483e+00 3.0349462e+00 1.7160413e+00 2.7852734e+00 4.8087107e+00 3.8282466e+00 4.7531334e+00 4.3176393e+00 4.5857287e+00 5.4831923e+00 3.2147850e+00 5.1185883e+00 4.5544260e+00 5.0194259e+00 3.9185849e+00 4.0655452e+00 4.3416283e+00 3.7535680e+00 3.9509795e+00 4.1478442e+00 4.2472736e+00 5.6096505e+00 5.7957776e+00 3.6945993e+00 4.5734622e+00 3.6568202e+00 5.5854254e+00 3.6720840e+00 4.5038991e+00 4.8262859e+00 3.5684917e+00 3.6474985e+00 4.3740189e+00 4.6282931e+00 4.9713928e+00 5.3806679e+00 4.3928114e+00 3.8121990e+00 4.2612863e+00 5.1032991e+00 4.4267055e+00 4.2347444e+00 3.5464871e+00 4.2738510e+00 4.4745238e+00 4.0663411e+00 3.8282466e+00 4.7338066e+00 4.5852690e+00 4.1075310e+00 3.7823897e+00 4.0070636e+00 4.2156933e+00 3.8157950e+00 1.6177449e+00 1.7454671e+00 1.2604558e+00 8.6361309e-01 1.4955532e+00 1.0118409e+00 1.1594648e+00 9.6204649e-01 6.2081167e-01 9.1750357e-01 8.7504951e-01 7.6752131e-01 8.0660588e-01 9.5965467e-01 9.2859317e-01 5.7324170e-01 6.2205176e-01 1.1313840e+00 1.2653669e+00 1.4930627e+00 6.4755655e-01 7.0479928e-01 1.2236003e+00 6.4755655e-01 2.1269358e-01 8.5105559e-01 7.7360126e-01 7.1169738e-01 2.5651975e-01 8.7229670e-01 1.1327578e+00 5.3588338e-01 1.0269295e+00 3.8934542e-01 1.1042097e+00 7.2823007e-01 4.0317004e+00 3.6659830e+00 4.1618561e+00 3.0123702e+00 3.7804276e+00 3.4970843e+00 3.8244351e+00 2.2591077e+00 3.7930789e+00 2.8953397e+00 2.4889124e+00 3.2809188e+00 3.0866488e+00 3.7578933e+00 2.6595288e+00 3.6754272e+00 3.5073435e+00 3.1173742e+00 3.6212723e+00 2.9065572e+00 3.8667462e+00 3.1302383e+00 3.9918403e+00 3.7416229e+00 3.4760444e+00 3.6375992e+00 4.0358101e+00 4.2016915e+00 3.5683934e+00 2.5569968e+00 2.8007817e+00 2.6989368e+00 2.9539253e+00 4.1306024e+00 3.4882801e+00 3.5831257e+00 3.9280671e+00 3.5362697e+00 3.1099883e+00 3.0065416e+00 3.3706887e+00 3.6672620e+00 3.0442126e+00 2.2719663e+00 3.2032390e+00 3.2071637e+00 3.2176230e+00 3.4155491e+00 2.0139971e+00 3.1260028e+00 5.1310217e+00 4.1456639e+00 5.1323742e+00 4.6609614e+00 4.9284761e+00 5.8739676e+00 3.4984873e+00 5.5048216e+00 4.9175276e+00 5.3898806e+00 4.2781762e+00 4.4176533e+00 4.7107211e+00 4.0621414e+00 4.2494597e+00 4.4865569e+00 4.6045396e+00 6.0012333e+00 6.1816086e+00 4.0339598e+00 4.9390284e+00 3.9601358e+00 5.9803696e+00 4.0277694e+00 4.8626276e+00 5.2147981e+00 3.9185849e+00 3.9887029e+00 4.7161140e+00 5.0257341e+00 5.3673499e+00 5.7939320e+00 4.7320534e+00 4.1713411e+00 4.5995433e+00 5.5085511e+00 4.7536729e+00 4.5857356e+00 3.8819510e+00 4.6519178e+00 4.8256399e+00 4.4430890e+00 4.1456639e+00 5.0898961e+00 4.9316646e+00 4.4680158e+00 4.1325542e+00 4.3648035e+00 4.5412859e+00 4.1418557e+00 4.5581864e-01 4.1586001e-01 7.7074935e-01 5.0991930e-01 7.1840099e-01 7.2486328e-01 7.3145860e-01 1.2122249e+00 9.2112464e-01 1.1384810e+00 1.1451403e+00 9.1163729e-01 7.0386584e-01 7.4855857e-01 1.2220203e+00 1.1947245e+00 6.6827038e-01 6.2081167e-01 3.4378533e-01 1.1229906e+00 9.9348625e-01 5.2942799e-01 1.1229906e+00 1.5344133e+00 8.2275389e-01 8.5233811e-01 1.8985661e+00 1.4692412e+00 8.9653332e-01 8.7420176e-01 1.2431040e+00 7.3813096e-01 1.2951131e+00 5.5419992e-01 9.3801395e-01 3.5789198e+00 3.3663244e+00 3.7753619e+00 3.0049442e+00 3.4909841e+00 3.3695525e+00 3.5654259e+00 2.3989172e+00 3.4663502e+00 2.8427326e+00 2.7185849e+00 3.0894572e+00 3.0108764e+00 3.5617386e+00 2.5173832e+00 3.2758681e+00 3.3732554e+00 2.9816791e+00 3.4895316e+00 2.8451507e+00 3.6905956e+00 2.8989400e+00 3.8036776e+00 3.5549103e+00 3.1734856e+00 3.2772927e+00 3.6834126e+00 3.8868430e+00 3.3809159e+00 2.4588872e+00 2.7850016e+00 2.6918796e+00 2.8113773e+00 3.9804187e+00 3.3742776e+00 3.3692592e+00 3.5726491e+00 3.3587234e+00 2.9691171e+00 2.9539253e+00 3.2926883e+00 3.4584304e+00 2.9217347e+00 2.4321061e+00 3.0988783e+00 3.0546600e+00 3.0736357e+00 3.1699903e+00 2.1306832e+00 2.9913743e+00 4.9420128e+00 4.0177712e+00 4.8123874e+00 4.4703485e+00 4.7113827e+00 5.5147622e+00 3.4715574e+00 5.1811127e+00 4.6944291e+00 5.0587041e+00 4.0117533e+00 4.2085851e+00 4.4199792e+00 3.9616570e+00 4.1103933e+00 4.2537610e+00 4.3721243e+00 5.6218420e+00 5.8419148e+00 3.9412893e+00 4.6390186e+00 3.8408636e+00 5.6159291e+00 3.8175051e+00 4.5984929e+00 4.8771654e+00 3.7143727e+00 3.7943375e+00 4.5141564e+00 4.6734732e+00 5.0086627e+00 5.3396700e+00 4.5298770e+00 3.9647930e+00 4.4561969e+00 5.0778756e+00 4.5477422e+00 4.3671210e+00 3.6996802e+00 4.3269400e+00 4.5602465e+00 4.0901232e+00 4.0177712e+00 4.8233796e+00 4.6689006e+00 4.1757336e+00 3.9466531e+00 4.1130674e+00 4.3408596e+00 3.9840684e+00 5.3588338e-01 9.7098574e-01 6.0611244e-01 7.4549115e-01 1.0101422e+00 8.1242502e-01 1.2342162e+00 1.1486378e+00 1.1959482e+00 1.4468211e+00 1.0906388e+00 9.4287188e-01 1.0346741e+00 1.3793330e+00 1.4148192e+00 1.0065841e+00 5.5419992e-01 2.8507955e-01 1.3835747e+00 1.2681309e+00 9.0679720e-01 1.3835747e+00 1.6801917e+00 1.0588560e+00 1.0122141e+00 2.2040881e+00 1.5564198e+00 1.0122141e+00 7.7553525e-01 1.4987155e+00 7.4893123e-01 1.4320120e+00 7.3813096e-01 1.1765359e+00 3.3186105e+00 3.0934278e+00 3.5115632e+00 2.9015832e+00 3.2557855e+00 3.1381850e+00 3.2787144e+00 2.3983798e+00 3.2261964e+00 2.6655261e+00 2.7738368e+00 2.8425716e+00 2.9377092e+00 3.3097860e+00 2.3365894e+00 3.0236933e+00 3.1147370e+00 2.7988444e+00 3.3431646e+00 2.7201960e+00 3.4033622e+00 2.7009102e+00 3.5863979e+00 3.3171611e+00 2.9439491e+00 3.0327979e+00 3.4475678e+00 3.6195561e+00 3.1337459e+00 2.3758157e+00 2.6957302e+00 2.6219409e+00 2.6429556e+00 3.7305206e+00 3.1152653e+00 3.0762634e+00 3.3088561e+00 3.2115055e+00 2.7318540e+00 2.8101506e+00 3.0967820e+00 3.1998457e+00 2.7619926e+00 2.4596921e+00 2.9002737e+00 2.8148869e+00 2.8449691e+00 2.9378173e+00 2.1723936e+00 2.7843048e+00 4.6358686e+00 3.7621042e+00 4.5316360e+00 4.1928583e+00 4.4221843e+00 5.2364242e+00 3.2682245e+00 4.9072991e+00 4.4428425e+00 4.7561724e+00 3.7219581e+00 3.9498605e+00 4.1390009e+00 3.7275066e+00 3.8377652e+00 3.9569614e+00 4.0917968e+00 5.3289557e+00 5.5738695e+00 3.7540308e+00 4.3457024e+00 3.5797958e+00 5.3465693e+00 3.5720882e+00 4.3012547e+00 4.5936468e+00 3.4616111e+00 3.5205889e+00 4.2389017e+00 4.4031390e+00 4.7432976e+00 5.0569442e+00 4.2531342e+00 3.7092459e+00 4.2038056e+00 4.8064634e+00 4.2402361e+00 4.0806404e+00 3.4276314e+00 4.0438546e+00 4.2676463e+00 3.8085992e+00 3.7621042e+00 4.5272919e+00 4.3667579e+00 3.8946701e+00 3.7163265e+00 3.8338395e+00 4.0342445e+00 3.7061759e+00 4.4651726e-01 4.4651726e-01 3.2816937e-01 5.7257017e-01 3.4378533e-01 8.2384013e-01 6.6432544e-01 8.0758367e-01 9.3048953e-01 5.8851328e-01 4.3691963e-01 5.1691876e-01 8.8062848e-01 8.9917007e-01 5.0817745e-01 3.6171588e-01 3.2816937e-01 8.6361309e-01 7.3851529e-01 4.1449626e-01 8.6361309e-01 1.1845977e+00 5.4292906e-01 4.9766035e-01 1.6754036e+00 1.0919712e+00 5.3309112e-01 6.2024833e-01 9.7098574e-01 3.8934542e-01 9.3824087e-01 2.8507955e-01 6.5223271e-01 3.5185448e+00 3.2633258e+00 3.6996953e+00 2.8710255e+00 3.3892942e+00 3.2497279e+00 3.4561374e+00 2.2371784e+00 3.3772302e+00 2.7023432e+00 2.5704711e+00 2.9638836e+00 2.8904978e+00 3.4483274e+00 2.3826791e+00 3.1954061e+00 3.2493673e+00 2.8645447e+00 3.3669805e+00 2.7184506e+00 3.5654259e+00 2.7812639e+00 3.6908684e+00 3.4451701e+00 3.0736340e+00 3.1881331e+00 3.6017790e+00 3.7914024e+00 3.2604423e+00 2.3308454e+00 2.6548674e+00 2.5630676e+00 2.6859989e+00 3.8615219e+00 3.2492317e+00 3.2498302e+00 3.4856775e+00 3.2460061e+00 2.8456767e+00 2.8220742e+00 3.1709561e+00 3.3452644e+00 2.7965957e+00 2.2780262e+00 2.9733693e+00 2.9362769e+00 2.9511072e+00 3.0600825e+00 1.9688013e+00 2.8661222e+00 4.8176767e+00 3.8889176e+00 4.7230291e+00 4.3578295e+00 4.5962942e+00 5.4442203e+00 3.3242177e+00 5.1039076e+00 4.5914416e+00 4.9653393e+00 3.8994399e+00 4.0931001e+00 4.3174903e+00 3.8262108e+00 3.9673816e+00 4.1302370e+00 4.2654212e+00 5.5551457e+00 5.7673205e+00 3.8189943e+00 4.5366342e+00 3.7059360e+00 5.5500825e+00 3.6985459e+00 4.4934936e+00 4.7995387e+00 3.5922369e+00 3.6724425e+00 4.3963259e+00 4.6010378e+00 4.9364818e+00 5.2936070e+00 4.4094664e+00 3.8558772e+00 4.3453589e+00 5.0159331e+00 4.4225169e+00 4.2579435e+00 3.5745624e+00 4.2301614e+00 4.4459076e+00 3.9875954e+00 3.8889176e+00 4.7169919e+00 4.5531173e+00 4.0619315e+00 3.8238093e+00 3.9999729e+00 4.2141826e+00 3.8611742e+00 6.3808075e-01 3.0275928e-01 3.7598397e-01 2.1269358e-01 5.6769031e-01 3.4378533e-01 5.3022554e-01 5.0991930e-01 2.1845981e-01 1.4096146e-01 1.4096146e-01 4.5581864e-01 4.5581864e-01 3.0811765e-01 6.0670504e-01 7.3496673e-01 4.2667565e-01 3.2816937e-01 4.0293660e-01 4.2667565e-01 7.6787403e-01 1.4096146e-01 1.2418578e-01 1.2394907e+00 7.1504098e-01 3.2586371e-01 5.2942799e-01 5.2862779e-01 3.2586371e-01 5.2942799e-01 2.5651975e-01 2.1269358e-01 3.4944845e+00 3.2032390e+00 3.6588207e+00 2.7040077e+00 3.3172489e+00 3.1387381e+00 3.3902207e+00 2.0195610e+00 3.3129652e+00 2.5744164e+00 2.3173648e+00 2.8753045e+00 2.7215057e+00 3.3565935e+00 2.2694598e+00 3.1556501e+00 3.1511848e+00 2.7390328e+00 3.2351115e+00 2.5646808e+00 3.4858646e+00 2.6854398e+00 3.5885398e+00 3.3452644e+00 3.0014619e+00 3.1359624e+00 3.5442447e+00 3.7351231e+00 3.1683717e+00 2.1722580e+00 2.4832809e+00 2.3831271e+00 2.5617005e+00 3.7607269e+00 3.1489919e+00 3.1764760e+00 3.4370400e+00 3.1222134e+00 2.7420671e+00 2.6759392e+00 3.0406669e+00 3.2584404e+00 2.6649106e+00 2.0477765e+00 2.8508344e+00 2.8325946e+00 2.8443188e+00 2.9745020e+00 1.7495699e+00 2.7521074e+00 4.7498176e+00 3.7908064e+00 4.6736601e+00 4.2736523e+00 4.5261084e+00 5.4025762e+00 3.1986968e+00 5.0495127e+00 4.5070435e+00 4.9284820e+00 3.8418637e+00 4.0108142e+00 4.2628455e+00 3.7198158e+00 3.8891307e+00 4.0719001e+00 4.1917075e+00 5.5215376e+00 5.7192826e+00 3.6876129e+00 4.4897240e+00 3.6129201e+00 5.5066558e+00 3.6138577e+00 4.4349731e+00 4.7514299e+00 3.5090368e+00 3.5920050e+00 4.3185209e+00 4.5521574e+00 4.8905855e+00 5.2768100e+00 4.3339547e+00 3.7672401e+00 4.2403934e+00 4.9963306e+00 4.3598652e+00 4.1826701e+00 3.4920978e+00 4.1853524e+00 4.3933832e+00 3.9574195e+00 3.7908064e+00 4.6611791e+00 4.5034762e+00 4.0149574e+00 3.7307866e+00 3.9355645e+00 4.1498459e+00 3.7732223e+00 6.0551856e-01 4.4535192e-01 6.0670504e-01 1.1765359e+00 6.9325418e-01 9.2288144e-01 9.3637892e-01 7.3535471e-01 5.3665999e-01 5.8914551e-01 1.0576043e+00 1.0106392e+00 4.5581864e-01 5.4292906e-01 4.5581864e-01 9.4009473e-01 8.6290690e-01 4.5581864e-01 9.4009473e-01 1.3885563e+00 6.5223271e-01 7.4740267e-01 1.7041201e+00 1.3421549e+00 7.2823007e-01 6.0611244e-01 1.0653845e+00 6.0121055e-01 1.1521791e+00 4.1586001e-01 7.7919451e-01 3.1037808e+00 2.8727295e+00 3.2914954e+00 2.5112138e+00 2.9950832e+00 2.8632951e+00 3.0711321e+00 1.9332545e+00 2.9709745e+00 2.3448578e+00 2.2688022e+00 2.5914913e+00 2.5183808e+00 3.0579528e+00 2.0217308e+00 2.7906520e+00 2.8719896e+00 2.4738237e+00 2.9926636e+00 2.3440712e+00 3.1967616e+00 2.3980102e+00 3.3005331e+00 3.0483897e+00 2.6752185e+00 2.7868575e+00 3.1931777e+00 3.3968373e+00 2.8794765e+00 1.9640287e+00 2.2899742e+00 2.1990648e+00 2.3082381e+00 3.4762640e+00 2.8726212e+00 2.8752807e+00 3.0841055e+00 2.8599559e+00 2.4658566e+00 2.4543822e+00 2.7852734e+00 2.9558536e+00 2.4184985e+00 1.9730073e+00 2.5941661e+00 2.5490308e+00 2.5692104e+00 2.6676432e+00 1.6855044e+00 2.4875166e+00 4.4549901e+00 3.5192877e+00 4.3283747e+00 3.9701062e+00 4.2192020e+00 5.0364062e+00 2.9740218e+00 4.6944291e+00 4.1953299e+00 4.5849301e+00 3.5249823e+00 3.7114178e+00 3.9340128e+00 3.4663826e+00 3.6308220e+00 3.7719045e+00 3.8752244e+00 5.1489675e+00 5.3620178e+00 3.4363773e+00 4.1584261e+00 3.3485848e+00 5.1375979e+00 3.3209346e+00 4.1101337e+00 4.3926615e+00 3.2186045e+00 3.2982828e+00 4.0192434e+00 4.1885158e+00 4.5274663e+00 4.8785522e+00 4.0373008e+00 3.4619693e+00 3.9494193e+00 4.6147130e+00 4.0642632e+00 3.8698115e+00 3.2042572e+00 3.8459316e+00 4.0795080e+00 3.6227082e+00 3.5192877e+00 4.3377723e+00 4.1907472e+00 3.6992844e+00 3.4507210e+00 3.6230931e+00 3.8568809e+00 3.4855556e+00 4.5581864e-01 1.2418578e-01 6.2660376e-01 5.1607523e-01 5.2655962e-01 8.0096515e-01 4.0438741e-01 3.0546431e-01 4.0438741e-01 6.4806901e-01 7.1504098e-01 4.4535192e-01 3.2586371e-01 4.9857388e-01 7.0784540e-01 6.2081167e-01 4.5581864e-01 7.0784540e-01 9.3824087e-01 4.0147421e-01 3.2586371e-01 1.5252485e+00 8.1558458e-01 3.7598397e-01 4.0147421e-01 8.1099042e-01 1.2418578e-01 6.9006418e-01 2.1269358e-01 5.0270183e-01 3.4104878e+00 3.1150013e+00 3.5735680e+00 2.6813198e+00 3.2413086e+00 3.0598576e+00 3.2985843e+00 2.0418831e+00 3.2329790e+00 2.5171713e+00 2.3816204e+00 2.7937685e+00 2.7102198e+00 3.2724336e+00 2.2054062e+00 3.0737397e+00 3.0650685e+00 2.6738621e+00 3.1983741e+00 2.5253420e+00 3.3951420e+00 2.6185785e+00 3.5201263e+00 3.2642011e+00 2.9245132e+00 3.0559405e+00 3.4672191e+00 3.6501426e+00 3.0869409e+00 2.1449779e+00 2.4611582e+00 2.3678836e+00 2.5038051e+00 3.6798850e+00 3.0627375e+00 3.0833417e+00 3.3518108e+00 3.0810282e+00 2.6595270e+00 2.6323570e+00 2.9747184e+00 3.1719581e+00 2.6119950e+00 2.0875308e+00 2.7837517e+00 2.7481947e+00 2.7653278e+00 2.8959432e+00 1.7918633e+00 2.6810089e+00 4.6579399e+00 3.7114178e+00 4.5860934e+00 4.1845117e+00 4.4368376e+00 5.3138890e+00 3.1392617e+00 4.9608959e+00 4.4280037e+00 4.8387532e+00 3.7530908e+00 3.9304148e+00 4.1764880e+00 3.6510310e+00 3.8111653e+00 3.9838913e+00 4.1019789e+00 5.4302306e+00 5.6352651e+00 3.6334268e+00 4.4011799e+00 3.5332521e+00 5.4201202e+00 3.5378545e+00 4.3430510e+00 4.6603717e+00 3.4302376e+00 3.5053533e+00 4.2335883e+00 4.4640423e+00 4.8059592e+00 5.1881280e+00 4.2497073e+00 3.6834126e+00 4.1572779e+00 4.9128775e+00 4.2687104e+00 4.0908836e+00 3.4061169e+00 4.0989195e+00 4.3064904e+00 3.8759115e+00 3.7114178e+00 4.5707958e+00 4.4147019e+00 3.9326468e+00 3.6624594e+00 3.8494244e+00 4.0586633e+00 3.6843892e+00 4.0176783e-01 9.3801395e-01 3.7427929e-01 6.0551856e-01 4.9766035e-01 4.1449626e-01 2.5251796e-01 3.2352160e-01 7.0437330e-01 6.2024833e-01 2.4837156e-01 7.0826681e-01 8.1099042e-01 5.3665999e-01 5.7257017e-01 4.0293660e-01 5.3665999e-01 1.0321505e+00 3.2352160e-01 4.9857388e-01 1.2654843e+00 1.0181000e+00 4.9857388e-01 4.6472023e-01 6.6432544e-01 4.4535192e-01 8.1354181e-01 3.2586371e-01 4.4535192e-01 3.1652953e+00 2.9034751e+00 3.3383293e+00 2.4277398e+00 3.0113552e+00 2.8500355e+00 3.0981427e+00 1.7594421e+00 2.9944056e+00 2.3105335e+00 2.0559262e+00 2.5987706e+00 2.4171653e+00 3.0605340e+00 2.0054351e+00 2.8372675e+00 2.8748086e+00 2.4385827e+00 2.9411544e+00 2.2761106e+00 3.2149087e+00 2.3896630e+00 3.2878368e+00 3.0415738e+00 2.6906230e+00 2.8216976e+00 3.2222602e+00 3.4304873e+00 2.8822802e+00 1.8816502e+00 2.1994544e+00 2.0961718e+00 2.2729984e+00 3.4713427e+00 2.8746311e+00 2.8977380e+00 3.1239380e+00 2.8149627e+00 2.4626518e+00 2.3988202e+00 2.7512943e+00 2.9634715e+00 2.3744738e+00 1.7861398e+00 2.5679553e+00 2.5438761e+00 2.5602722e+00 2.6724144e+00 1.5132025e+00 2.4693940e+00 4.4818617e+00 3.5188715e+00 4.3693901e+00 3.9814082e+00 4.2430316e+00 5.0846750e+00 2.9378173e+00 4.7308926e+00 4.2026915e+00 4.6369546e+00 3.5592955e+00 3.7219741e+00 3.9702025e+00 3.4565374e+00 3.6485303e+00 3.8059948e+00 3.8952692e+00 5.2045888e+00 5.4038637e+00 3.3942456e+00 4.2018404e+00 3.3550631e+00 5.1838582e+00 3.3280757e+00 4.1439346e+00 4.4349731e+00 3.2284912e+00 3.3133518e+00 4.0354963e+00 4.2293106e+00 4.5707958e+00 4.9486891e+00 4.0555804e+00 3.4667821e+00 3.9402495e+00 4.6817169e+00 4.0952984e+00 3.8891597e+00 3.2181054e+00 3.8906833e+00 4.1179678e+00 3.6792093e+00 3.5188715e+00 4.3738746e+00 4.2318888e+00 3.7415007e+00 3.4482727e+00 3.6509580e+00 3.8867812e+00 3.4956677e+00 6.2660376e-01 4.1449626e-01 4.8927739e-01 7.0479928e-01 3.0546431e-01 2.5251796e-01 3.2816937e-01 5.7324170e-01 6.2538346e-01 3.7255734e-01 4.4535192e-01 5.7324170e-01 6.2482915e-01 5.3665999e-01 4.3691963e-01 6.2482915e-01 8.7420176e-01 3.2352160e-01 2.5651975e-01 1.4293465e+00 7.7360126e-01 2.5651975e-01 4.0147421e-01 7.1504098e-01 2.1269358e-01 6.2660376e-01 2.4837156e-01 4.1586001e-01 3.4011512e+00 3.1015495e+00 3.5629124e+00 2.6446848e+00 3.2242409e+00 3.0444970e+00 3.2854349e+00 1.9922212e+00 3.2208624e+00 2.4875432e+00 2.3235341e+00 2.7738624e+00 2.6761538e+00 3.2590031e+00 2.1770137e+00 3.0609726e+00 3.0488669e+00 2.6564689e+00 3.1671679e+00 2.4967542e+00 3.3778209e+00 2.5968629e+00 3.5012162e+00 3.2523394e+00 2.9093820e+00 3.0417674e+00 3.4541339e+00 3.6357801e+00 3.0695657e+00 2.1117686e+00 2.4265922e+00 2.3324013e+00 2.4794505e+00 3.6641654e+00 3.0465086e+00 3.0686943e+00 3.3395333e+00 3.0541897e+00 2.6428278e+00 2.6018930e+00 2.9558536e+00 3.1589081e+00 2.5867906e+00 2.0334278e+00 2.7624503e+00 2.7347909e+00 2.7481947e+00 2.8804789e+00 1.7294430e+00 2.6604040e+00 4.6390099e+00 3.6904099e+00 4.5721680e+00 4.1717456e+00 4.4201622e+00 5.3038306e+00 3.1101376e+00 4.9521292e+00 4.4131751e+00 4.8218478e+00 3.7351231e+00 3.9119157e+00 4.1593600e+00 3.6239123e+00 3.7806200e+00 3.9616940e+00 4.0893976e+00 5.4208566e+00 5.6225133e+00 3.6099406e+00 4.3833809e+00 3.5087649e+00 5.4106224e+00 3.5167400e+00 4.3287713e+00 4.6517741e+00 3.4091049e+00 3.4875352e+00 4.2154392e+00 4.4559638e+00 4.7948032e+00 5.1800729e+00 4.2298456e+00 3.6705550e+00 4.1464752e+00 4.8981257e+00 4.2482702e+00 4.0788778e+00 3.3871264e+00 4.0817153e+00 4.2852718e+00 3.8517038e+00 3.6904099e+00 4.5544260e+00 4.3933832e+00 3.9084933e+00 3.6377874e+00 3.8310704e+00 4.0381483e+00 3.6684338e+00 7.9016429e-01 9.0454394e-01 7.7553525e-01 6.5633874e-01 6.8961791e-01 6.5172743e-01 6.4755655e-01 6.9325418e-01 8.5690100e-01 7.5871717e-01 9.8800009e-01 6.3977563e-01 5.0503591e-01 9.0852141e-01 6.3977563e-01 6.2482915e-01 6.2605182e-01 4.4651726e-01 1.3039319e+00 4.5470518e-01 6.8801986e-01 9.4492923e-01 6.5223271e-01 6.9325418e-01 4.9674312e-01 7.6752131e-01 5.2574978e-01 3.9950977e+00 3.6682165e+00 4.1454421e+00 3.1171350e+00 3.7869887e+00 3.5623665e+00 3.8418637e+00 2.4093459e+00 3.7913585e+00 2.9787373e+00 2.6930133e+00 3.3123070e+00 3.1668302e+00 3.7985007e+00 2.6980573e+00 3.6472316e+00 3.5682291e+00 3.1756360e+00 3.6828708e+00 2.9891564e+00 3.9102500e+00 3.1455588e+00 4.0375495e+00 3.7881557e+00 3.4756264e+00 3.6204175e+00 4.0288578e+00 4.2043522e+00 3.6069560e+00 2.6127852e+00 2.9004348e+00 2.8010550e+00 3.0013391e+00 4.1901031e+00 3.5584876e+00 3.6126340e+00 3.9170234e+00 3.5823448e+00 3.1646752e+00 3.0923554e+00 3.4563987e+00 3.7021702e+00 3.1018442e+00 2.4341346e+00 3.2724336e+00 3.2604423e+00 3.2715632e+00 3.4335342e+00 2.1375243e+00 3.1807243e+00 5.1732049e+00 4.2085267e+00 5.1402548e+00 4.7090394e+00 4.9640507e+00 5.8783901e+00 3.5970425e+00 5.5195747e+00 4.9586651e+00 5.3905797e+00 4.2919639e+00 4.4545908e+00 4.7214902e+00 4.1323720e+00 4.2962344e+00 4.5078390e+00 4.6379987e+00 5.9985593e+00 6.1927610e+00 4.1173292e+00 4.9467209e+00 4.0217355e+00 5.9855018e+00 4.0597680e+00 4.8845913e+00 5.2228901e+00 3.9505682e+00 4.0262006e+00 4.7557226e+00 5.0295898e+00 5.3695643e+00 5.7704875e+00 4.7697480e+00 4.2124808e+00 4.6690776e+00 5.4857949e+00 4.7867344e+00 4.6237863e+00 3.9220577e+00 4.6512574e+00 4.8397561e+00 4.4244727e+00 4.2085267e+00 5.1102370e+00 4.9464234e+00 4.4688286e+00 4.1733964e+00 4.3844239e+00 4.5752087e+00 4.1957914e+00 3.8934542e-01 3.7598397e-01 1.5422108e-01 3.4583729e-01 3.7598397e-01 4.4651726e-01 3.8934542e-01 3.2816937e-01 8.2929029e-01 9.3610001e-01 4.3691963e-01 5.3022554e-01 5.3309112e-01 4.3691963e-01 7.5976039e-01 3.2586371e-01 4.2667565e-01 1.0733200e+00 7.4777660e-01 2.1845981e-01 5.0905001e-01 4.3456114e-01 5.2942799e-01 5.5492130e-01 4.6472023e-01 3.7427929e-01 3.2191540e+00 2.9033203e+00 3.3725057e+00 2.3744738e+00 3.0162346e+00 2.8254861e+00 3.0850817e+00 1.6867642e+00 3.0206125e+00 2.2489449e+00 1.9859316e+00 2.5612285e+00 2.4037412e+00 3.0483897e+00 1.9482601e+00 2.8711841e+00 2.8361445e+00 2.4280197e+00 2.9151666e+00 2.2428341e+00 3.1709561e+00 2.3770599e+00 3.2772927e+00 3.0392407e+00 2.7044574e+00 2.8456337e+00 3.2543813e+00 3.4363773e+00 2.8560815e+00 1.8517858e+00 2.1570512e+00 2.0577182e+00 2.2449618e+00 3.4472201e+00 2.8333788e+00 2.8654874e+00 3.1455372e+00 2.8102985e+00 2.4278629e+00 2.3504126e+00 2.7240842e+00 2.9511072e+00 2.3468577e+00 1.7140774e+00 2.5325623e+00 2.5220817e+00 2.5303132e+00 2.6704164e+00 1.4096199e+00 2.4355523e+00 4.4339908e+00 3.4713427e+00 4.3742064e+00 3.9639546e+00 4.2144772e+00 5.1104621e+00 2.8721481e+00 4.7555981e+00 4.1997133e+00 4.6264002e+00 3.5337158e+00 3.6986235e+00 3.9580816e+00 3.3951420e+00 3.5651172e+00 3.7578933e+00 3.8852294e+00 5.2312618e+00 5.4232729e+00 3.3678461e+00 4.1846468e+00 3.2909043e+00 5.2164277e+00 3.3005331e+00 4.1286019e+00 4.4583488e+00 3.1948184e+00 3.2785487e+00 4.0052019e+00 4.2627019e+00 4.5989546e+00 4.9978258e+00 4.0193656e+00 3.4602150e+00 3.9315374e+00 4.7096962e+00 4.0441539e+00 3.8751788e+00 3.1769821e+00 3.8841455e+00 4.0829381e+00 3.6559398e+00 3.4713427e+00 4.3535851e+00 4.1922661e+00 3.7063225e+00 3.4135466e+00 3.6262912e+00 3.8337160e+00 3.4586921e+00 4.5470518e-01 3.4378533e-01 4.9766035e-01 5.6631629e-01 3.2586371e-01 3.7255734e-01 6.5172743e-01 7.6625946e-01 9.7356960e-01 4.4651726e-01 7.0784540e-01 8.1273630e-01 4.4651726e-01 6.8757066e-01 4.4417983e-01 6.0670504e-01 1.1521791e+00 6.5172743e-01 4.5581864e-01 4.5470518e-01 5.6700421e-01 4.8036801e-01 5.1607523e-01 5.8851328e-01 5.0905001e-01 3.1972361e+00 2.8360340e+00 3.3243092e+00 2.2696891e+00 2.9531300e+00 2.6835197e+00 2.9981183e+00 1.5916843e+00 2.9546153e+00 2.1366783e+00 1.9099663e+00 2.4717637e+00 2.3219731e+00 2.9300203e+00 1.8705419e+00 2.8448793e+00 2.7044574e+00 2.2951567e+00 2.8430073e+00 2.1220080e+00 3.0654291e+00 2.3117864e+00 3.1749624e+00 2.9091307e+00 2.6433897e+00 2.8065044e+00 3.2000771e+00 3.3714688e+00 2.7511201e+00 1.7679293e+00 2.0426611e+00 1.9423536e+00 2.1457242e+00 3.3172489e+00 2.6940968e+00 2.7682296e+00 3.0935247e+00 2.7391698e+00 2.2996943e+00 2.2360451e+00 2.5729378e+00 2.8382774e+00 2.2413445e+00 1.6312555e+00 2.4031247e+00 2.3848740e+00 2.4037412e+00 2.5843380e+00 1.3803845e+00 2.3178393e+00 4.3373464e+00 3.3555449e+00 4.3029534e+00 3.8394246e+00 4.1166152e+00 5.0345534e+00 2.7564428e+00 4.6628709e+00 4.0929284e+00 4.5724955e+00 3.4657942e+00 3.6038441e+00 3.8912788e+00 3.2935105e+00 3.4985926e+00 3.6938082e+00 3.7771525e+00 5.1600819e+00 5.3477989e+00 3.2453592e+00 4.1245629e+00 3.1885527e+00 5.1389533e+00 3.2183480e+00 4.0411872e+00 4.3734530e+00 3.1116430e+00 3.1793624e+00 3.9065553e+00 4.1815080e+00 4.5288943e+00 4.9506208e+00 3.9281139e+00 3.3421539e+00 3.7789119e+00 4.6812838e+00 3.9623295e+00 3.7603559e+00 3.0782101e+00 3.8325036e+00 4.0241284e+00 3.6474081e+00 3.3555449e+00 4.2739171e+00 4.1339606e+00 3.6720159e+00 3.3349237e+00 3.5512382e+00 3.7511837e+00 3.3364095e+00 4.1312257e-01 5.0905001e-01 4.2538717e-01 3.2352160e-01 2.0656129e-01 5.0592043e-01 1.1017858e+00 1.2234738e+00 1.5422108e-01 4.1312257e-01 6.3924842e-01 1.5422108e-01 6.1968386e-01 4.0293660e-01 5.2942799e-01 7.7919451e-01 6.2482915e-01 5.6631629e-01 8.1385214e-01 2.5251796e-01 8.0032200e-01 4.2538717e-01 7.1462831e-01 3.2352160e-01 3.3601225e+00 3.0492285e+00 3.5130686e+00 2.4784234e+00 3.1574358e+00 2.9495683e+00 3.2305582e+00 1.7639552e+00 3.1543365e+00 2.3872777e+00 2.0066796e+00 2.7104259e+00 2.4866453e+00 3.1794134e+00 2.0999661e+00 3.0164205e+00 2.9733298e+00 2.5409084e+00 3.0313923e+00 2.3496997e+00 3.3211033e+00 2.5173832e+00 3.4035007e+00 3.1599783e+00 2.8424094e+00 2.9896107e+00 3.3894792e+00 3.5821206e+00 2.9960387e+00 1.9633030e+00 2.2546134e+00 2.1472921e+00 2.3729779e+00 3.5766918e+00 2.9692947e+00 3.0147040e+00 3.2887803e+00 2.9233984e+00 2.5628362e+00 2.4694536e+00 2.8371552e+00 3.0850817e+00 2.4681135e+00 1.7749726e+00 2.6585961e+00 2.6493446e+00 2.6623632e+00 2.8060305e+00 1.5124582e+00 2.5679553e+00 4.5912390e+00 3.6144502e+00 4.5212874e+00 4.0982070e+00 4.3637125e+00 5.2494547e+00 3.0086587e+00 4.8875515e+00 4.3294620e+00 4.7880147e+00 3.6912876e+00 3.8418637e+00 4.1117747e+00 3.5413188e+00 3.7389462e+00 3.9250135e+00 4.0233529e+00 5.3754694e+00 5.5627643e+00 3.4785161e+00 4.3436980e+00 3.4455964e+00 5.3512930e+00 3.4471566e+00 4.2776053e+00 4.5941143e+00 3.3449470e+00 3.4269051e+00 4.1524717e+00 4.3946634e+00 4.7366258e+00 5.1414650e+00 4.1713411e+00 3.5895201e+00 4.0467919e+00 4.8635964e+00 4.2075047e+00 4.0127353e+00 3.3273395e+00 4.0411872e+00 4.2480617e+00 3.8321687e+00 3.6144502e+00 4.5072985e+00 4.3598062e+00 3.8780834e+00 3.5586316e+00 3.7805671e+00 3.9971028e+00 3.6003219e+00 2.5651975e-01 2.8192292e-01 3.4378533e-01 3.4378533e-01 4.0147421e-01 7.1840099e-01 8.5690100e-01 3.7598397e-01 4.2538717e-01 5.3665999e-01 3.7598397e-01 6.6827038e-01 2.1269358e-01 3.0546431e-01 1.1320702e+00 6.2988288e-01 2.0656129e-01 4.4535192e-01 4.2667565e-01 4.1449626e-01 4.3691963e-01 3.8934542e-01 2.5251796e-01 3.3428183e+00 3.0232018e+00 3.4944845e+00 2.4950353e+00 3.1381402e+00 2.9363801e+00 3.2027420e+00 1.8084630e+00 3.1409294e+00 2.3642733e+00 2.1113036e+00 2.6784604e+00 2.5283251e+00 3.1625374e+00 2.0667297e+00 2.9950041e+00 2.9473422e+00 2.5410503e+00 3.0407257e+00 2.3596992e+00 3.2858223e+00 2.4986337e+00 3.3960124e+00 3.1519721e+00 2.8255193e+00 2.9686973e+00 3.3765772e+00 3.5575681e+00 2.9720515e+00 1.9732878e+00 2.2758449e+00 2.1765379e+00 2.3630256e+00 3.5606213e+00 2.9432282e+00 2.9813163e+00 3.2672636e+00 2.9349850e+00 2.5393641e+00 2.4678343e+00 2.8348218e+00 3.0655803e+00 2.4649412e+00 1.8381372e+00 2.6459992e+00 2.6324803e+00 2.6428278e+00 2.7886501e+00 1.5384446e+00 2.5500177e+00 4.5509522e+00 3.5863729e+00 4.4953238e+00 4.0776551e+00 4.3319640e+00 5.2308062e+00 2.9880146e+00 4.8736353e+00 4.3174903e+00 4.7496159e+00 3.6545040e+00 3.8173510e+00 4.0797075e+00 3.5129177e+00 3.6850739e+00 3.8789949e+00 4.0011311e+00 5.3516635e+00 5.5447338e+00 3.4854203e+00 4.3070102e+00 3.4066692e+00 5.3366535e+00 3.4209331e+00 4.2472818e+00 4.5769661e+00 3.3144678e+00 3.3951450e+00 4.1229838e+00 4.3815083e+00 4.7200939e+00 5.1200990e+00 4.1381062e+00 3.5750015e+00 4.0416512e+00 4.8355880e+00 4.1628805e+00 3.9898962e+00 3.2934163e+00 4.0073765e+00 4.2053647e+00 3.7839552e+00 3.5863729e+00 4.4735121e+00 4.3145846e+00 3.8316735e+00 3.5355330e+00 3.7466071e+00 3.9521135e+00 3.5718733e+00 1.2418578e-01 5.2942799e-01 4.9766035e-01 2.5251796e-01 6.0060595e-01 7.1462831e-01 4.4535192e-01 3.8776762e-01 3.2352160e-01 4.4535192e-01 8.5434758e-01 1.2418578e-01 2.5251796e-01 1.2643026e+00 8.1354181e-01 4.1449626e-01 4.5581864e-01 5.6769031e-01 3.0546431e-01 6.2024833e-01 2.0656129e-01 2.5251796e-01 3.3898078e+00 3.1105347e+00 3.5575681e+00 2.6249526e+00 3.2229246e+00 3.0488669e+00 3.3004172e+00 1.9465831e+00 3.2118493e+00 2.4993166e+00 2.2473120e+00 2.7928582e+00 2.6296047e+00 3.2639046e+00 2.1933937e+00 3.0559188e+00 3.0673749e+00 2.6440180e+00 3.1486786e+00 2.4776707e+00 3.4056271e+00 2.5954147e+00 3.4958702e+00 3.2483608e+00 2.9041534e+00 3.0378828e+00 3.4425295e+00 3.6411503e+00 3.0811775e+00 2.0851901e+00 2.3997585e+00 2.2980893e+00 2.4741664e+00 3.6714798e+00 3.0661139e+00 3.0923102e+00 3.3390355e+00 3.0288896e+00 2.6566259e+00 2.5949561e+00 2.9511072e+00 3.1662394e+00 2.5768305e+00 1.9764051e+00 2.7650187e+00 2.7420671e+00 2.7570191e+00 2.8802219e+00 1.6913411e+00 2.6662783e+00 4.6723657e+00 3.7109297e+00 4.5802461e+00 4.1829734e+00 4.4414351e+00 5.3024507e+00 3.1246867e+00 4.9479218e+00 4.4123750e+00 4.8421359e+00 3.7582429e+00 3.9238367e+00 4.1750713e+00 3.6455011e+00 3.8262353e+00 3.9966275e+00 4.0997246e+00 5.4219259e+00 5.6210533e+00 3.5985833e+00 4.4045665e+00 3.5402439e+00 5.4041845e+00 3.5288526e+00 4.3467079e+00 4.6509698e+00 3.4261305e+00 3.5087649e+00 4.2340479e+00 4.4487072e+00 4.7898591e+00 5.1726632e+00 4.2521321e+00 3.6728562e+00 4.1446028e+00 4.9002452e+00 4.2845042e+00 4.0915924e+00 3.4110551e+00 4.0971854e+00 4.3142588e+00 3.8789274e+00 3.7109297e+00 4.5753785e+00 4.4261431e+00 3.9377466e+00 3.6482464e+00 3.8509694e+00 4.0749843e+00 3.6894983e+00 5.1607523e-01 4.5470518e-01 2.5251796e-01 7.0086313e-01 8.1067767e-01 3.7598397e-01 2.8192292e-01 3.0546431e-01 3.7598397e-01 8.2654509e-01 1.2418578e-01 2.1845981e-01 1.1754055e+00 8.0326782e-01 4.2667565e-01 5.7324170e-01 4.9766035e-01 4.1449626e-01 6.0551856e-01 3.0546431e-01 2.0656129e-01 3.4780839e+00 3.2028668e+00 3.6478832e+00 2.7001131e+00 3.3123070e+00 3.1424188e+00 3.3940268e+00 2.0081113e+00 3.3027430e+00 2.5846998e+00 2.2899742e+00 2.8843735e+00 2.7014135e+00 3.3579678e+00 2.2804674e+00 3.1447330e+00 3.1614572e+00 2.7347909e+00 3.2266676e+00 2.5600441e+00 3.4989115e+00 2.6835197e+00 3.5848035e+00 3.3425343e+00 2.9944056e+00 3.1272113e+00 3.5315985e+00 3.7321938e+00 3.1736230e+00 2.1642821e+00 2.4763086e+00 2.3729779e+00 2.5613679e+00 3.7645411e+00 3.1602773e+00 3.1861493e+00 3.4298218e+00 3.1090174e+00 2.7503801e+00 2.6773131e+00 3.0414782e+00 3.2606191e+00 2.6626548e+00 2.0308266e+00 2.8549070e+00 2.8371552e+00 2.8500790e+00 2.9720515e+00 1.7439430e+00 2.7570191e+00 4.7646254e+00 3.8019108e+00 4.6714768e+00 4.2777154e+00 4.5341669e+00 5.3940437e+00 3.2096520e+00 5.0408889e+00 4.5037829e+00 4.9318274e+00 3.8493108e+00 4.0147814e+00 4.2656755e+00 3.7323432e+00 3.9122146e+00 4.0862794e+00 4.1939019e+00 5.5136120e+00 5.7113100e+00 3.6836404e+00 4.4947801e+00 3.6298047e+00 5.4953655e+00 3.6181784e+00 4.4396174e+00 4.7440323e+00 3.5161021e+00 3.6013159e+00 4.3259034e+00 4.5411167e+00 4.8804165e+00 5.2620015e+00 4.3431589e+00 3.7666196e+00 4.2394195e+00 4.9871255e+00 4.3755778e+00 4.1864815e+00 3.5032323e+00 4.1868319e+00 4.4036244e+00 3.9638383e+00 3.8019108e+00 4.6672392e+00 4.5155386e+00 4.0245873e+00 3.7349501e+00 3.9420153e+00 4.1660972e+00 3.7835217e+00 1.2418578e-01 7.0826681e-01 9.4125538e-01 1.1340084e+00 2.1845981e-01 4.4417983e-01 8.2105460e-01 2.1845981e-01 3.8776762e-01 4.1449626e-01 4.2418962e-01 9.1075311e-01 3.7255734e-01 4.8036801e-01 6.6827038e-01 2.5651975e-01 6.4704320e-01 2.0656129e-01 6.8961791e-01 3.2586371e-01 3.4686627e+00 3.1154621e+00 3.6024772e+00 2.5108475e+00 3.2290313e+00 2.9704704e+00 3.2809332e+00 1.7903952e+00 3.2349033e+00 2.3960250e+00 2.0600195e+00 2.7476411e+00 2.5610667e+00 3.2181149e+00 2.1323638e+00 3.1154281e+00 2.9881598e+00 2.5786309e+00 3.0947136e+00 2.3839455e+00 3.3448197e+00 2.5821869e+00 3.4532666e+00 3.1998457e+00 2.9200439e+00 3.0794219e+00 3.4771054e+00 3.6510733e+00 3.0328831e+00 2.0197525e+00 2.2893157e+00 2.1858172e+00 2.4166535e+00 3.6030381e+00 2.9770247e+00 3.0492285e+00 3.3713329e+00 2.9974031e+00 2.5833316e+00 2.4944673e+00 2.8535197e+00 3.1260028e+00 2.5104998e+00 1.8109223e+00 2.6804452e+00 2.6742360e+00 2.6875169e+00 2.8656044e+00 1.5447938e+00 2.5961850e+00 4.6147552e+00 3.6320149e+00 4.5850999e+00 4.1288948e+00 4.3989089e+00 5.3208661e+00 3.0146208e+00 4.9525575e+00 4.3778283e+00 4.8485483e+00 3.7416375e+00 3.8836706e+00 4.1692394e+00 3.5584719e+00 3.7545764e+00 3.9635178e+00 4.0653249e+00 5.4465238e+00 5.6318041e+00 3.5148434e+00 4.4004449e+00 3.4571917e+00 5.4256316e+00 3.4931266e+00 4.3243671e+00 4.6617210e+00 3.3863989e+00 3.4595139e+00 4.1872611e+00 4.4691596e+00 4.8126955e+00 5.2323771e+00 4.2057897e+00 3.6309135e+00 4.0714444e+00 4.9544547e+00 4.2355968e+00 4.0495222e+00 3.3563815e+00 4.1075398e+00 4.2957899e+00 3.9065020e+00 3.6320149e+00 4.5543025e+00 4.4046818e+00 3.9362563e+00 3.6038441e+00 3.8285790e+00 4.0238937e+00 3.6204282e+00 6.2538346e-01 1.0167353e+00 1.1763980e+00 1.4096146e-01 4.1449626e-01 7.4740267e-01 1.4096146e-01 4.4535192e-01 3.7427929e-01 4.5581864e-01 8.2135873e-01 4.4535192e-01 5.0503591e-01 7.3145860e-01 2.1269358e-01 7.1421512e-01 2.5251796e-01 6.8961791e-01 2.8192292e-01 3.4295980e+00 3.0905489e+00 3.5700123e+00 2.4944673e+00 3.2020292e+00 2.9613737e+00 3.2617087e+00 1.7750284e+00 3.2049793e+00 2.3909366e+00 2.0308266e+00 2.7326472e+00 2.5286673e+00 3.2028668e+00 2.1181082e+00 3.0792693e+00 2.9816970e+00 2.5624932e+00 3.0681391e+00 2.3677174e+00 3.3352475e+00 2.5566444e+00 3.4334259e+00 3.1839972e+00 2.8907702e+00 3.0462904e+00 3.4448498e+00 3.6256155e+00 3.0181476e+00 1.9946242e+00 2.2719663e+00 2.1665831e+00 2.3980102e+00 3.5922217e+00 2.9733478e+00 3.0355056e+00 3.3410264e+00 2.9673667e+00 2.5744164e+00 2.4820750e+00 2.8455141e+00 3.1100045e+00 2.4920874e+00 1.7903952e+00 2.6704164e+00 2.6637326e+00 2.6767607e+00 2.8425716e+00 1.5257596e+00 2.5839288e+00 4.6057175e+00 3.6244539e+00 4.5619285e+00 4.1170543e+00 4.3856360e+00 5.2953657e+00 3.0110441e+00 4.9290739e+00 4.3593510e+00 4.8266994e+00 3.7227460e+00 3.8675033e+00 4.1480696e+00 3.5505922e+00 3.7479505e+00 3.9489183e+00 4.0495222e+00 5.4213759e+00 5.6069704e+00 3.4988409e+00 4.3796539e+00 3.4519560e+00 5.3990720e+00 3.4751739e+00 4.3070102e+00 4.6372963e+00 3.3701472e+00 3.4467337e+00 4.1738909e+00 4.4422690e+00 4.7852959e+00 5.2004339e+00 4.1925494e+00 3.6148707e+00 4.0613681e+00 4.9222119e+00 4.2248103e+00 4.0355816e+00 3.3448336e+00 4.0832977e+00 4.2781022e+00 3.8793995e+00 3.6244539e+00 4.5369609e+00 4.3880177e+00 3.9147164e+00 3.5857963e+00 3.8105301e+00 4.0134966e+00 3.6122845e+00 7.1799256e-01 8.0358695e-01 5.5419992e-01 4.6472023e-01 2.5651975e-01 5.5419992e-01 1.0198386e+00 3.2352160e-01 4.1586001e-01 1.2565757e+00 1.0054037e+00 4.1586001e-01 5.2574978e-01 6.4806901e-01 4.5581864e-01 8.0619006e-01 3.2586371e-01 4.1586001e-01 3.3274681e+00 3.0643176e+00 3.5031390e+00 2.5831315e+00 3.1734856e+00 3.0256804e+00 3.2593968e+00 1.9049236e+00 3.1662394e+00 2.4587503e+00 2.1948123e+00 2.7522526e+00 2.5874301e+00 3.2341367e+00 2.1493214e+00 2.9966141e+00 3.0389019e+00 2.6214821e+00 3.0978571e+00 2.4463233e+00 3.3673968e+00 2.5500177e+00 3.4578354e+00 3.2240818e+00 2.8578051e+00 2.9828212e+00 3.3905763e+00 3.5904118e+00 3.0455175e+00 2.0467937e+00 2.3640301e+00 2.2638728e+00 2.4385827e+00 3.6424937e+00 3.0387448e+00 3.0543001e+00 3.2867025e+00 2.9810914e+00 2.6291524e+00 2.5579619e+00 2.9291061e+00 3.1351569e+00 2.5421955e+00 1.9274228e+00 2.7359835e+00 2.7196686e+00 2.7293044e+00 2.8418790e+00 1.6234861e+00 2.6349941e+00 4.6268918e+00 3.6735954e+00 4.5276539e+00 4.1519514e+00 4.3981255e+00 5.2510005e+00 3.0847983e+00 4.9049143e+00 4.3738893e+00 4.7804645e+00 3.7059360e+00 3.8806135e+00 4.1210200e+00 3.6007179e+00 3.7674167e+00 3.9401211e+00 4.0632074e+00 5.3683319e+00 5.5675337e+00 3.5650560e+00 4.3467079e+00 3.4964385e+00 5.3534247e+00 3.4817971e+00 4.3007421e+00 4.6057628e+00 3.3796707e+00 3.4684743e+00 4.1910954e+00 4.4033511e+00 4.7374076e+00 5.1107389e+00 4.2056854e+00 3.6417452e+00 4.1251907e+00 4.8290847e+00 4.2339606e+00 4.0576409e+00 3.3702841e+00 4.0376849e+00 4.2550416e+00 3.8015574e+00 3.6735954e+00 4.5249462e+00 4.3662620e+00 3.8699102e+00 3.5979726e+00 3.8007870e+00 4.0252278e+00 3.6568095e+00 3.0811765e-01 1.0065841e+00 9.1075311e-01 6.2538346e-01 1.0065841e+00 1.2125198e+00 7.0086313e-01 6.1623531e-01 1.8279039e+00 1.0613462e+00 6.9369532e-01 4.8135521e-01 1.1149070e+00 3.0811765e-01 9.7098574e-01 4.0293660e-01 8.0358695e-01 3.4154940e+00 3.1439160e+00 3.5872997e+00 2.8054691e+00 3.2839149e+00 3.1127876e+00 3.3274872e+00 2.2159139e+00 3.2598167e+00 2.6167778e+00 2.5758644e+00 2.8523754e+00 2.8256291e+00 3.3122081e+00 2.3003824e+00 3.0947136e+00 3.1160876e+00 2.7384939e+00 3.2940867e+00 2.6281170e+00 3.4401593e+00 2.6851662e+00 3.5758474e+00 3.3024121e+00 2.9636544e+00 3.0850893e+00 3.4935692e+00 3.6787177e+00 3.1382691e+00 2.2643860e+00 2.5838312e+00 2.4957147e+00 2.5876691e+00 3.7272092e+00 3.1148696e+00 3.1192689e+00 3.3731473e+00 3.1641238e+00 2.7164367e+00 2.7371177e+00 3.0433470e+00 3.2094276e+00 2.6989752e+00 2.2729593e+00 2.8576425e+00 2.7954161e+00 2.8234677e+00 2.9407805e+00 1.9980146e+00 2.7511201e+00 4.6993349e+00 3.7716613e+00 4.6090409e+00 4.2170163e+00 4.4740971e+00 5.3233695e+00 3.2312218e+00 4.9715358e+00 4.4649192e+00 4.8634712e+00 3.7907269e+00 3.9777091e+00 4.2102908e+00 3.7293867e+00 3.8887827e+00 4.0321207e+00 4.1304736e+00 5.4336694e+00 5.6535520e+00 3.7088191e+00 4.4333012e+00 3.6017014e+00 5.4287623e+00 3.5939935e+00 4.3696884e+00 4.6684884e+00 3.4865359e+00 3.5518450e+00 4.2777882e+00 4.4716883e+00 4.8204359e+00 5.1830573e+00 4.2975164e+00 3.7189281e+00 4.1915116e+00 4.9282164e+00 4.3126073e+00 4.1182993e+00 3.4568393e+00 4.1297239e+00 4.3496867e+00 3.9207320e+00 3.7716613e+00 4.6018276e+00 4.4564872e+00 3.9827168e+00 3.7295388e+00 3.8905364e+00 4.1037903e+00 3.7281480e+00 1.1474460e+00 1.0344911e+00 7.0043186e-01 1.1474460e+00 1.4311891e+00 8.2654509e-01 7.6787403e-01 1.9730918e+00 1.3073038e+00 7.9878917e-01 6.2407309e-01 1.2632199e+00 5.0503591e-01 1.1833480e+00 5.0905001e-01 9.4080461e-01 3.4392518e+00 3.2008338e+00 3.6261197e+00 2.9076510e+00 3.3439089e+00 3.2073032e+00 3.3907558e+00 2.3329978e+00 3.3168472e+00 2.7081478e+00 2.6929215e+00 2.9283888e+00 2.9264154e+00 3.3942456e+00 2.3848677e+00 3.1312883e+00 3.2027420e+00 2.8385969e+00 3.3781905e+00 2.7324845e+00 3.5133135e+00 2.7602023e+00 3.6563535e+00 3.3905763e+00 3.0256209e+00 3.1312883e+00 3.5423793e+00 3.7299679e+00 3.2179031e+00 2.3661539e+00 2.6906231e+00 2.6054995e+00 2.6801752e+00 3.8138933e+00 3.2027420e+00 3.1888037e+00 3.4187265e+00 3.2459231e+00 2.8060305e+00 2.8359967e+00 3.1453916e+00 3.2886661e+00 2.7943622e+00 2.3874574e+00 2.9533314e+00 2.8877105e+00 2.9141136e+00 3.0147886e+00 2.0992326e+00 2.8425716e+00 4.7630756e+00 3.8535531e+00 4.6550014e+00 4.2947468e+00 4.5389196e+00 5.3632740e+00 3.3234239e+00 5.0232338e+00 4.5363757e+00 4.8985515e+00 3.8441304e+00 4.0476997e+00 4.2599433e+00 3.8106152e+00 3.9516603e+00 4.0850903e+00 4.1995425e+00 5.4671477e+00 5.6957748e+00 3.8037734e+00 4.4771379e+00 3.6783395e+00 5.4690202e+00 3.6632177e+00 4.4259826e+00 4.7160634e+00 3.5560806e+00 3.6239123e+00 4.3464973e+00 4.5187406e+00 4.8619146e+00 5.2001625e+00 4.3635746e+00 3.7979761e+00 4.2843668e+00 4.9451734e+00 4.3709882e+00 4.1899199e+00 3.5299194e+00 4.1707976e+00 4.3972344e+00 3.9457376e+00 3.8535531e+00 4.6546556e+00 4.5024055e+00 4.0227020e+00 3.8004969e+00 3.9484773e+00 4.1635108e+00 3.8079860e+00 3.0811765e-01 6.5223271e-01 0.0000000e+00 5.0991930e-01 3.2586371e-01 4.2667565e-01 8.3172002e-01 5.0991930e-01 5.6769031e-01 7.5082357e-01 2.1845981e-01 7.0479928e-01 3.0811765e-01 6.4755655e-01 2.1845981e-01 3.4865562e+00 3.1726595e+00 3.6377960e+00 2.5987470e+00 3.2814045e+00 3.0627375e+00 3.3515846e+00 1.8841865e+00 3.2769379e+00 2.5038079e+00 2.1311468e+00 2.8311678e+00 2.6104387e+00 3.2962520e+00 2.2214438e+00 3.1433122e+00 3.0878634e+00 2.6552472e+00 3.1570103e+00 2.4668912e+00 3.4394878e+00 2.6411293e+00 3.5233648e+00 3.2747247e+00 2.9659871e+00 3.1154783e+00 3.5134741e+00 3.7059620e+00 3.1148696e+00 2.0851901e+00 2.3731428e+00 2.2655571e+00 2.4927109e+00 3.6920087e+00 3.0823446e+00 3.1337459e+00 3.4135200e+00 3.0481703e+00 2.6780487e+00 2.5874301e+00 2.9489507e+00 3.2027420e+00 2.5873149e+00 1.8973383e+00 2.7738355e+00 2.7632614e+00 2.7778954e+00 2.9269923e+00 1.6390769e+00 2.6848587e+00 4.7106706e+00 3.7313856e+00 4.6446321e+00 4.2142736e+00 4.4836580e+00 5.3716885e+00 3.1250284e+00 5.0074019e+00 4.4485220e+00 4.9128219e+00 3.8150636e+00 3.9624529e+00 4.2358121e+00 3.6602286e+00 3.8605980e+00 4.0488387e+00 4.1418643e+00 5.4970002e+00 5.6855224e+00 3.5964347e+00 4.4685630e+00 3.5634461e+00 5.4730406e+00 3.5693950e+00 4.3989089e+00 4.7150659e+00 3.4668130e+00 3.5464993e+00 4.2723380e+00 4.5155386e+00 4.8594290e+00 5.2647079e+00 4.2921213e+00 3.7064459e+00 4.1581964e+00 4.9913682e+00 4.3286007e+00 4.1303097e+00 3.4468286e+00 4.1669742e+00 4.3729308e+00 3.9624170e+00 3.7313856e+00 4.6297577e+00 4.4844827e+00 4.0056359e+00 3.6817961e+00 3.9035218e+00 4.1179678e+00 3.7164366e+00 5.2942799e-01 3.0811765e-01 6.0611244e-01 3.2586371e-01 3.0546431e-01 9.4125538e-01 6.0060595e-01 5.2574978e-01 8.1558458e-01 2.8507955e-01 6.4755655e-01 4.1312257e-01 5.5419992e-01 2.0656129e-01 3.7045940e+00 3.4142500e+00 3.8690719e+00 2.8688189e+00 3.5226542e+00 3.3385842e+00 3.6010215e+00 2.1580776e+00 3.5196916e+00 2.7652601e+00 2.4083873e+00 3.0820950e+00 2.8783309e+00 3.5617386e+00 2.4693940e+00 3.3658240e+00 3.3558214e+00 2.9322808e+00 3.4112518e+00 2.7424260e+00 3.6945405e+00 2.8867565e+00 3.7848217e+00 3.5471494e+00 3.2076743e+00 3.3449470e+00 3.7498842e+00 3.9450818e+00 3.3735875e+00 2.3490515e+00 2.6490839e+00 2.5444216e+00 2.7549369e+00 3.9621873e+00 3.3527310e+00 3.3865418e+00 3.6475099e+00 3.3024121e+00 2.9459653e+00 2.8566322e+00 3.2311957e+00 3.4653703e+00 2.8535197e+00 2.1708533e+00 3.0455175e+00 3.0364473e+00 3.0465086e+00 3.1799184e+00 1.8842354e+00 2.9509414e+00 4.9594922e+00 3.9924292e+00 4.8844442e+00 4.4804205e+00 4.7354764e+00 5.6130421e+00 3.3873806e+00 5.2583931e+00 4.7090394e+00 5.1413739e+00 4.0532097e+00 4.2156327e+00 4.4735121e+00 3.9153764e+00 4.0928954e+00 4.2826383e+00 4.4004808e+00 5.7341534e+00 5.9271927e+00 3.8700742e+00 4.7016733e+00 3.8156003e+00 5.7155096e+00 3.8175051e+00 4.6462020e+00 4.9620553e+00 3.7143727e+00 3.8000004e+00 4.5254820e+00 4.7613782e+00 5.0991856e+00 5.4888822e+00 4.5411167e+00 3.9718373e+00 4.4397363e+00 5.2074442e+00 4.5701358e+00 4.3916992e+00 3.6996802e+00 4.3970196e+00 4.6045465e+00 4.1691696e+00 3.9924292e+00 4.8725396e+00 4.7150659e+00 4.2256400e+00 3.9292780e+00 4.1454529e+00 4.3599285e+00 3.9798670e+00 6.5223271e-01 1.1268457e+00 4.1449626e-01 5.0090417e-01 1.3784393e+00 1.1053488e+00 5.8851328e-01 6.6827038e-01 7.6787403e-01 4.8036801e-01 9.0852141e-01 2.8192292e-01 5.0905001e-01 3.5117473e+00 3.2719724e+00 3.6961290e+00 2.8060101e+00 3.3799936e+00 3.2401159e+00 3.4709594e+00 2.1293854e+00 3.3643376e+00 2.6848587e+00 2.4115946e+00 2.9723937e+00 2.7984009e+00 3.4455106e+00 2.3749088e+00 3.1913397e+00 3.2574960e+00 2.8320532e+00 3.3150921e+00 2.6637326e+00 3.5883493e+00 2.7640668e+00 3.6695251e+00 3.4317240e+00 3.0617355e+00 3.1820648e+00 3.5855784e+00 3.7950050e+00 3.2620096e+00 2.2645766e+00 2.5831070e+00 2.4810276e+00 2.6563403e+00 3.8575846e+00 3.2574960e+00 3.2718360e+00 3.4856613e+00 3.1913594e+00 2.8466990e+00 2.7801709e+00 3.1437608e+00 3.3466419e+00 2.7595194e+00 2.1498672e+00 2.9543365e+00 2.9330570e+00 2.9459653e+00 3.0511838e+00 1.8555964e+00 2.8534301e+00 4.8490742e+00 3.8962785e+00 4.7308926e+00 4.3643842e+00 4.6144072e+00 5.4442665e+00 3.3129652e+00 5.0997539e+00 4.5816539e+00 4.9883395e+00 3.9213002e+00 4.0961125e+00 4.3313403e+00 3.8279951e+00 4.0003537e+00 4.1625074e+00 4.2731561e+00 5.5604940e+00 5.7635900e+00 3.7812981e+00 4.5580745e+00 3.7236571e+00 5.5437155e+00 3.6992145e+00 4.5120086e+00 4.8012240e+00 3.5989213e+00 3.6872980e+00 4.4084575e+00 4.5949523e+00 4.9306472e+00 5.2918657e+00 4.4250143e+00 3.8510143e+00 4.3338058e+00 5.0198276e+00 4.4571663e+00 4.2687771e+00 3.5910375e+00 4.2454653e+00 4.4729144e+00 4.0139676e+00 3.8962785e+00 4.7377574e+00 4.5853438e+00 4.0877338e+00 3.8179781e+00 4.0161568e+00 4.2490407e+00 3.8755800e+00 5.0991930e-01 3.2586371e-01 4.2667565e-01 8.3172002e-01 5.0991930e-01 5.6769031e-01 7.5082357e-01 2.1845981e-01 7.0479928e-01 3.0811765e-01 6.4755655e-01 2.1845981e-01 3.4865562e+00 3.1726595e+00 3.6377960e+00 2.5987470e+00 3.2814045e+00 3.0627375e+00 3.3515846e+00 1.8841865e+00 3.2769379e+00 2.5038079e+00 2.1311468e+00 2.8311678e+00 2.6104387e+00 3.2962520e+00 2.2214438e+00 3.1433122e+00 3.0878634e+00 2.6552472e+00 3.1570103e+00 2.4668912e+00 3.4394878e+00 2.6411293e+00 3.5233648e+00 3.2747247e+00 2.9659871e+00 3.1154783e+00 3.5134741e+00 3.7059620e+00 3.1148696e+00 2.0851901e+00 2.3731428e+00 2.2655571e+00 2.4927109e+00 3.6920087e+00 3.0823446e+00 3.1337459e+00 3.4135200e+00 3.0481703e+00 2.6780487e+00 2.5874301e+00 2.9489507e+00 3.2027420e+00 2.5873149e+00 1.8973383e+00 2.7738355e+00 2.7632614e+00 2.7778954e+00 2.9269923e+00 1.6390769e+00 2.6848587e+00 4.7106706e+00 3.7313856e+00 4.6446321e+00 4.2142736e+00 4.4836580e+00 5.3716885e+00 3.1250284e+00 5.0074019e+00 4.4485220e+00 4.9128219e+00 3.8150636e+00 3.9624529e+00 4.2358121e+00 3.6602286e+00 3.8605980e+00 4.0488387e+00 4.1418643e+00 5.4970002e+00 5.6855224e+00 3.5964347e+00 4.4685630e+00 3.5634461e+00 5.4730406e+00 3.5693950e+00 4.3989089e+00 4.7150659e+00 3.4668130e+00 3.5464993e+00 4.2723380e+00 4.5155386e+00 4.8594290e+00 5.2647079e+00 4.2921213e+00 3.7064459e+00 4.1581964e+00 4.9913682e+00 4.3286007e+00 4.1303097e+00 3.4468286e+00 4.1669742e+00 4.3729308e+00 3.9624170e+00 3.7313856e+00 4.6297577e+00 4.4844827e+00 4.0056359e+00 3.6817961e+00 3.9035218e+00 4.1179678e+00 3.7164366e+00 7.3813096e-01 6.8961791e-01 7.0086313e-01 2.0000000e-01 7.3805807e-01 1.0030700e+00 4.0293660e-01 9.4352681e-01 2.5251796e-01 1.0120221e+00 6.2024833e-01 3.8265307e+00 3.4552560e+00 3.9537404e+00 2.8022534e+00 3.5701225e+00 3.2863508e+00 3.6126198e+00 2.0524386e+00 3.5845127e+00 2.6848587e+00 2.2888897e+00 3.0684384e+00 2.8791325e+00 3.5464993e+00 2.4469125e+00 3.4686755e+00 3.2961206e+00 2.9072057e+00 3.4107902e+00 2.6959009e+00 3.6545040e+00 2.9195876e+00 3.7806187e+00 3.5312474e+00 3.2669151e+00 3.4295849e+00 3.8277021e+00 3.9909030e+00 3.3562690e+00 2.3465937e+00 2.5906376e+00 2.4892531e+00 2.7423171e+00 3.9193693e+00 3.2780620e+00 3.3708389e+00 3.7190229e+00 3.3268984e+00 2.8983020e+00 2.7954161e+00 3.1611584e+00 3.4557351e+00 2.8328337e+00 2.0658700e+00 2.9919517e+00 2.9960210e+00 3.0059712e+00 3.2048547e+00 1.8038968e+00 2.9141136e+00 4.9189452e+00 3.9342203e+00 4.9208723e+00 4.4494735e+00 4.7160542e+00 5.6635213e+00 3.2909043e+00 5.2946188e+00 4.7062325e+00 5.1779277e+00 4.0657707e+00 4.2053647e+00 4.4986279e+00 3.8509694e+00 4.0384339e+00 4.2739171e+00 4.3927415e+00 5.7909894e+00 5.9706827e+00 3.8237409e+00 4.7267599e+00 3.7490739e+00 5.7704875e+00 3.8154018e+00 4.6503268e+00 5.0044958e+00 3.7060524e+00 3.7762575e+00 4.5037232e+00 4.8164539e+00 5.1574084e+00 5.5860081e+00 4.5195237e+00 3.9601358e+00 4.3901702e+00 5.2992055e+00 4.5412859e+00 4.3739561e+00 3.6694918e+00 4.4403343e+00 4.6130415e+00 4.2323959e+00 3.9342203e+00 4.8773871e+00 4.7190595e+00 4.2559866e+00 3.9201797e+00 4.1523445e+00 4.3289315e+00 3.9302319e+00 2.1845981e-01 1.1486378e+00 7.0784540e-01 4.0438741e-01 5.0503591e-01 4.4651726e-01 4.0147421e-01 5.0905001e-01 3.2352160e-01 1.4096146e-01 3.4156574e+00 3.1235447e+00 3.5778307e+00 2.6097685e+00 3.2346686e+00 3.0478400e+00 3.3101102e+00 1.9193093e+00 3.2270948e+00 2.4922287e+00 2.2081369e+00 2.7965957e+00 2.6184141e+00 3.2685282e+00 2.1916898e+00 3.0773654e+00 3.0673749e+00 2.6423274e+00 3.1444983e+00 2.4678343e+00 3.4088888e+00 2.6016025e+00 3.4988409e+00 3.2521427e+00 2.9171710e+00 3.0559188e+00 3.4597125e+00 3.6553612e+00 3.0847983e+00 2.0765921e+00 2.3848740e+00 2.2817008e+00 2.4722095e+00 3.6724425e+00 3.0650478e+00 3.0981264e+00 3.3567173e+00 3.0288896e+00 2.6566259e+00 2.5851693e+00 2.9455446e+00 3.1719381e+00 2.5729378e+00 1.9450955e+00 2.7611864e+00 2.7431084e+00 2.7570191e+00 2.8884401e+00 1.6625998e+00 2.6648989e+00 4.6768871e+00 3.7101281e+00 4.5948688e+00 4.1876541e+00 4.4480565e+00 5.3202405e+00 3.1169790e+00 4.9630576e+00 4.4189653e+00 4.8572313e+00 3.7684708e+00 3.9292780e+00 4.1872611e+00 3.6418662e+00 3.8262353e+00 4.0041417e+00 4.1076175e+00 5.4411191e+00 5.6370076e+00 3.5929878e+00 4.4174698e+00 3.5389106e+00 5.4223305e+00 3.5340177e+00 4.3569684e+00 4.6672392e+00 3.4309563e+00 3.5133135e+00 4.2392500e+00 4.4661721e+00 4.8075242e+00 5.1977119e+00 4.2572858e+00 3.6784045e+00 4.1454521e+00 4.9233665e+00 4.2900309e+00 4.0984960e+00 3.4145942e+00 4.1120703e+00 4.3243538e+00 3.8957047e+00 3.7101281e+00 4.5857922e+00 4.4360042e+00 3.9497198e+00 3.6509512e+00 3.8600234e+00 4.0800357e+00 3.6915258e+00 1.2223099e+00 6.2024833e-01 3.7255734e-01 6.2081167e-01 5.0905001e-01 3.7598397e-01 4.4651726e-01 3.4583729e-01 2.1269358e-01 3.6091470e+00 3.3105724e+00 3.7708932e+00 2.7979838e+00 3.4251430e+00 3.2391031e+00 3.4951642e+00 2.1075335e+00 3.4235403e+00 2.6687055e+00 2.3989464e+00 2.9762945e+00 2.8216056e+00 3.4603628e+00 2.3673218e+00 3.2680041e+00 3.2498302e+00 2.8414525e+00 3.3361912e+00 2.6626548e+00 3.5849794e+00 2.7906520e+00 3.6926714e+00 3.4497714e+00 3.1105675e+00 3.2468925e+00 3.6557661e+00 3.8432801e+00 3.2705166e+00 2.2719663e+00 2.5786309e+00 2.4784234e+00 2.6629602e+00 3.8619321e+00 3.2465133e+00 3.2780765e+00 3.5475145e+00 3.2266676e+00 2.8416143e+00 2.7719788e+00 3.1392934e+00 3.3624692e+00 2.7656089e+00 2.1335398e+00 2.9496515e+00 2.9338590e+00 2.9447165e+00 3.0808399e+00 1.8330979e+00 2.8520904e+00 4.8482992e+00 3.8884996e+00 4.7814945e+00 4.3763964e+00 4.6280797e+00 5.5131379e+00 3.2922206e+00 5.1594703e+00 4.6125121e+00 5.0342965e+00 3.9453171e+00 4.1137002e+00 4.3683295e+00 3.8151836e+00 3.9816719e+00 4.1715493e+00 4.2963195e+00 5.6322635e+00 5.8289682e+00 3.7874513e+00 4.5945190e+00 3.7079546e+00 5.6180203e+00 3.7164611e+00 4.5394421e+00 4.8614468e+00 3.6107037e+00 3.6929889e+00 4.4201622e+00 4.6634723e+00 5.0015266e+00 5.3906681e+00 4.4348302e+00 3.8718490e+00 4.3427947e+00 5.1078309e+00 4.4583488e+00 4.2864208e+00 3.5920050e+00 4.2919639e+00 4.4953238e+00 4.0619633e+00 3.8884996e+00 4.7650352e+00 4.6046026e+00 4.1173867e+00 3.8320624e+00 4.0389546e+00 4.2480032e+00 3.8727359e+00 9.0049692e-01 1.2307737e+00 1.5483011e+00 7.1462831e-01 1.5272277e+00 9.0074515e-01 1.4700179e+00 1.0331736e+00 3.7896333e+00 3.4304205e+00 3.9179666e+00 2.7683644e+00 3.5321772e+00 3.2685282e+00 3.5962433e+00 2.0250421e+00 3.5485736e+00 2.6642702e+00 2.2244833e+00 3.0435803e+00 2.8325748e+00 3.5230967e+00 2.4205131e+00 3.4307077e+00 3.2815626e+00 2.8843735e+00 3.3658240e+00 2.6687055e+00 3.6389628e+00 2.8830783e+00 3.7490739e+00 3.5087649e+00 3.2305801e+00 3.3908902e+00 3.7876239e+00 3.9561876e+00 3.3306114e+00 2.3112968e+00 2.5604155e+00 2.4585271e+00 2.7122813e+00 3.8971376e+00 3.2667813e+00 3.3674720e+00 3.6849783e+00 3.2837729e+00 2.8840079e+00 2.7685572e+00 3.1442943e+00 3.4335342e+00 2.8028287e+00 2.0286682e+00 2.9704704e+00 2.9822537e+00 2.9868163e+00 3.1741954e+00 1.7644184e+00 2.8908296e+00 4.8985114e+00 3.9102500e+00 4.8863496e+00 4.4272873e+00 4.6888407e+00 5.6294495e+00 3.2706756e+00 5.2636641e+00 4.6765452e+00 5.1547576e+00 4.0378383e+00 4.1743230e+00 4.4638518e+00 3.8230269e+00 4.0056625e+00 4.2450961e+00 4.3676124e+00 5.7745449e+00 5.9344852e+00 3.7932062e+00 4.6942925e+00 3.7245053e+00 5.7354045e+00 3.7814598e+00 4.6271849e+00 4.9763401e+00 3.6736483e+00 3.7511498e+00 4.4747039e+00 4.7841150e+00 5.1205765e+00 5.5660104e+00 4.4889834e+00 3.9348528e+00 4.3728356e+00 5.2548517e+00 4.5226167e+00 4.3526639e+00 3.6449787e+00 4.4041536e+00 4.5787890e+00 4.1879339e+00 3.9102500e+00 4.8490587e+00 4.6897155e+00 4.2150171e+00 3.8841455e+00 4.1203091e+00 4.3121165e+00 3.9109678e+00 6.7975091e-01 9.0056222e-01 4.1586001e-01 8.2275389e-01 2.0656129e-01 9.4287188e-01 6.0121055e-01 3.8264361e+00 3.4551376e+00 3.9537404e+00 2.8149627e+00 3.5710248e+00 3.2874334e+00 3.6122384e+00 2.0711789e+00 3.5849006e+00 2.6879715e+00 2.3281827e+00 3.0685922e+00 2.8946126e+00 3.5468964e+00 2.4478108e+00 3.4686755e+00 3.2962520e+00 2.9098194e+00 3.4214793e+00 2.7033034e+00 3.6543993e+00 2.9209919e+00 3.7841436e+00 3.5321717e+00 3.2673909e+00 3.4297053e+00 3.8284763e+00 3.9909892e+00 3.3567173e+00 2.3533545e+00 2.6019240e+00 2.5015675e+00 2.7452885e+00 3.9207248e+00 3.2781950e+00 3.3698144e+00 3.7190229e+00 3.3356294e+00 2.8984764e+00 2.8022534e+00 3.1646752e+00 3.4558535e+00 2.8373077e+00 2.0905239e+00 2.9944056e+00 2.9961831e+00 3.0065426e+00 3.2053509e+00 1.8216743e+00 2.9155237e+00 4.9187518e+00 3.9355645e+00 4.9209267e+00 4.4497146e+00 4.7161140e+00 5.6635613e+00 3.2956846e+00 5.2947833e+00 4.7084108e+00 5.1767384e+00 4.0656879e+00 4.2065257e+00 4.4986941e+00 3.8543544e+00 4.0391220e+00 4.2738429e+00 4.3928114e+00 5.7890564e+00 5.9715517e+00 3.8320624e+00 4.7267005e+00 3.7498842e+00 5.7708014e+00 3.8168398e+00 4.6501080e+00 5.0044433e+00 3.7068836e+00 3.7763550e+00 4.5042646e+00 4.8165110e+00 5.1578102e+00 5.5839155e+00 4.5200609e+00 3.9608542e+00 4.3918790e+00 5.2992517e+00 4.5407542e+00 4.3739561e+00 3.6695956e+00 4.4403343e+00 4.6130415e+00 4.2323959e+00 3.9355645e+00 4.8773316e+00 4.7188476e+00 4.2560614e+00 3.9234348e+00 4.1524235e+00 4.3283407e+00 3.9303212e+00 3.8934542e-01 5.4292906e-01 4.4535192e-01 5.3309112e-01 4.5581864e-01 4.2538717e-01 3.3319064e+00 3.0058998e+00 3.4822680e+00 2.4967542e+00 3.1249915e+00 2.9284660e+00 3.1836200e+00 1.8265014e+00 3.1331426e+00 2.3481462e+00 2.1458939e+00 2.6572703e+00 2.5437119e+00 3.1519721e+00 2.0470220e+00 2.9815576e+00 2.9302134e+00 2.5421955e+00 3.0374850e+00 2.3632684e+00 3.2598750e+00 2.4873223e+00 3.3884269e+00 3.1477087e+00 2.8156804e+00 2.9556616e+00 3.3682605e+00 3.5401725e+00 2.9561205e+00 1.9790422e+00 2.2832934e+00 2.1885968e+00 2.3571494e+00 3.5486864e+00 2.9260462e+00 2.9587612e+00 3.2530866e+00 2.9361879e+00 2.5256527e+00 2.4631898e+00 2.8325946e+00 3.0534395e+00 2.4619105e+00 1.8618589e+00 2.6377354e+00 2.6235630e+00 2.6314198e+00 2.7785210e+00 1.5475473e+00 2.5392051e+00 4.5179415e+00 3.5641186e+00 4.4752183e+00 4.0626016e+00 4.3069105e+00 5.2164277e+00 2.9689697e+00 4.8634846e+00 4.3067423e+00 4.7194915e+00 3.6262912e+00 3.7979761e+00 4.0547736e+00 3.4875352e+00 3.6400497e+00 3.8418637e+00 3.9849753e+00 5.3352888e+00 5.5294519e+00 3.4830211e+00 4.2776053e+00 3.3760149e+00 5.3253207e+00 3.4004918e+00 4.2238184e+00 4.5645220e+00 3.2914954e+00 3.3718862e+00 4.0995928e+00 4.3725648e+00 4.7075069e+00 5.1063282e+00 4.1113292e+00 3.5651462e+00 4.0375056e+00 4.8132427e+00 4.1268905e+00 3.9732869e+00 3.2685282e+00 3.9810803e+00 4.1706050e+00 3.7449914e+00 3.5641186e+00 4.4464848e+00 4.2774083e+00 3.7941767e+00 3.5148434e+00 3.7206115e+00 3.9162695e+00 3.5510950e+00 8.6137722e-01 3.2352160e-01 7.6166891e-01 4.2667565e-01 6.2660376e-01 3.0634640e+00 2.7392828e+00 3.2125175e+00 2.3391296e+00 2.8734025e+00 2.6707502e+00 2.9145708e+00 1.7569738e+00 2.8671376e+00 2.1479276e+00 2.1308063e+00 2.4108292e+00 2.3854031e+00 2.8851564e+00 1.8368900e+00 2.7201546e+00 2.6724144e+00 2.2982662e+00 2.8483417e+00 2.1701312e+00 3.0045018e+00 2.2531409e+00 3.1444983e+00 2.8783309e+00 2.5586145e+00 2.6967931e+00 3.1073497e+00 3.2779401e+00 2.7018605e+00 1.8104298e+00 2.1219691e+00 2.0368741e+00 2.1367260e+00 3.2905600e+00 2.6692615e+00 2.6939411e+00 2.9863438e+00 2.7320931e+00 2.2698938e+00 2.2722516e+00 2.5933163e+00 2.7845473e+00 2.2472326e+00 1.8195937e+00 2.4037412e+00 2.3571494e+00 2.3781826e+00 2.5200525e+00 1.5411691e+00 2.3001580e+00 4.2694227e+00 3.3237039e+00 4.2115652e+00 3.7930080e+00 4.0498216e+00 4.9425633e+00 2.7652100e+00 4.5847429e+00 4.0464464e+00 4.4666326e+00 3.3738930e+00 3.5479683e+00 3.8004969e+00 3.2711669e+00 3.4346585e+00 3.6043418e+00 3.7146255e+00 5.0600559e+00 5.2639977e+00 3.2609948e+00 4.0260071e+00 3.1481222e+00 5.0506841e+00 3.1599876e+00 3.9594081e+00 4.2851208e+00 3.0501817e+00 3.1181310e+00 3.8476631e+00 4.0931004e+00 4.4378867e+00 4.8317916e+00 3.8652515e+00 3.2970551e+00 3.7653550e+00 4.5583070e+00 3.8836720e+00 3.7008091e+00 3.0188386e+00 3.7282629e+00 3.9300286e+00 3.5186510e+00 3.3237039e+00 4.1891573e+00 4.0376133e+00 3.5648733e+00 3.2885200e+00 3.4693398e+00 3.6732275e+00 3.2917360e+00 8.1385214e-01 2.5251796e-01 7.6787403e-01 3.2586371e-01 3.5846101e+00 3.2546607e+00 3.7315988e+00 2.6609901e+00 3.3659358e+00 3.1439065e+00 3.4298218e+00 1.9383545e+00 3.3723944e+00 2.5580763e+00 2.1777421e+00 2.8983020e+00 2.6954219e+00 3.3808052e+00 2.2790175e+00 3.2344170e+00 3.1579733e+00 2.7462372e+00 3.2292608e+00 2.5444216e+00 3.5028333e+00 2.7205595e+00 3.6067948e+00 3.3670797e+00 3.0557792e+00 3.2048395e+00 3.6088607e+00 3.7891577e+00 3.1900581e+00 2.1641873e+00 2.4447974e+00 2.3408917e+00 2.5700421e+00 3.7710363e+00 3.1506190e+00 3.2040124e+00 3.5027314e+00 3.1322650e+00 2.7512730e+00 2.6533250e+00 3.0299528e+00 3.2862185e+00 2.6656374e+00 1.9477421e+00 2.8481000e+00 2.8454952e+00 2.8544453e+00 3.0132514e+00 1.6658308e+00 2.7590026e+00 4.7688362e+00 3.7943375e+00 4.7263320e+00 4.2949506e+00 4.5532150e+00 5.4636648e+00 3.1768366e+00 5.1030228e+00 4.5342701e+00 4.9831843e+00 3.8820426e+00 4.0358897e+00 4.3088496e+00 3.7133362e+00 3.8949006e+00 4.1025163e+00 4.2237423e+00 5.5888590e+00 5.7742361e+00 3.6743314e+00 4.5370189e+00 3.6141649e+00 5.5687868e+00 3.6395551e+00 4.4736906e+00 4.8085584e+00 3.5338163e+00 3.6144782e+00 4.3417782e+00 4.6138035e+00 4.9524148e+00 5.3625968e+00 4.3570317e+00 3.7932922e+00 4.2488997e+00 5.0747397e+00 4.3832471e+00 4.2110583e+00 3.5113289e+00 4.2399031e+00 4.4320962e+00 4.0189525e+00 3.7943375e+00 4.7000551e+00 4.5409269e+00 4.0612006e+00 3.7475562e+00 3.9722979e+00 4.1719819e+00 3.7859347e+00 6.9325418e-01 2.1269358e-01 5.0905001e-01 3.3337914e+00 3.0376046e+00 3.4948415e+00 2.6099670e+00 3.1641230e+00 2.9745020e+00 3.2202056e+00 1.9796375e+00 3.1511769e+00 2.4469125e+00 2.3235342e+00 2.7196435e+00 2.6337042e+00 3.1881331e+00 2.1375243e+00 2.9985516e+00 2.9847499e+00 2.5867906e+00 3.1255136e+00 2.4464131e+00 3.3203249e+00 2.5432298e+00 3.4386456e+00 3.1757436e+00 2.8453413e+00 2.9797458e+00 3.3872988e+00 3.5731577e+00 3.0079222e+00 2.0710306e+00 2.3862284e+00 2.2923690e+00 2.4260574e+00 3.5964347e+00 2.9822786e+00 3.0067893e+00 3.2740296e+00 3.0040546e+00 2.5786309e+00 2.5579141e+00 2.8891491e+00 3.0885642e+00 2.5333642e+00 2.0283816e+00 2.7031874e+00 2.6626548e+00 2.6835197e+00 2.8149627e+00 1.7472675e+00 2.6016025e+00 4.5864727e+00 3.6358644e+00 4.5092329e+00 4.1008711e+00 4.3608329e+00 5.2329560e+00 3.0685922e+00 4.8761643e+00 4.3448624e+00 4.7688607e+00 3.6817961e+00 3.8534030e+00 4.1033021e+00 3.5811154e+00 3.7530357e+00 3.9183591e+00 4.0199092e+00 5.3504583e+00 5.5556442e+00 3.5517562e+00 4.3306939e+00 3.4641481e+00 5.3377017e+00 3.4637450e+00 4.2663890e+00 4.5772917e+00 3.3571533e+00 3.4296698e+00 4.1575709e+00 4.3797497e+00 4.7253427e+00 5.1097682e+00 4.1763898e+00 3.5983434e+00 4.0666282e+00 4.8419079e+00 4.2005634e+00 4.0083071e+00 3.3318481e+00 4.0278210e+00 4.2396851e+00 3.8171357e+00 3.6358644e+00 4.4969460e+00 4.3490518e+00 3.8705614e+00 3.5905415e+00 3.7766172e+00 3.9906340e+00 3.6052686e+00 7.6752131e-01 4.0147421e-01 3.6660608e+00 3.3135273e+00 3.8017611e+00 2.7018605e+00 3.4275679e+00 3.1699903e+00 3.4789136e+00 1.9730403e+00 3.4358679e+00 2.5840983e+00 2.2331309e+00 2.9422991e+00 2.7581254e+00 3.4189217e+00 2.3233512e+00 3.3121677e+00 3.1836200e+00 2.7812918e+00 3.2896000e+00 2.5816639e+00 3.5379744e+00 2.7795823e+00 3.6532797e+00 3.4029480e+00 3.1195329e+00 3.2770643e+00 3.6772238e+00 3.8493740e+00 3.2305582e+00 2.2173292e+00 2.4840285e+00 2.3814525e+00 2.6148507e+00 3.8019334e+00 3.1710836e+00 3.2447990e+00 3.5700242e+00 3.1955870e+00 2.7803619e+00 2.6879415e+00 3.0521985e+00 3.3264150e+00 2.7089627e+00 1.9908167e+00 2.8775912e+00 2.8744403e+00 2.8857836e+00 3.0658390e+00 1.7171798e+00 2.7936066e+00 4.8056033e+00 3.8247106e+00 4.7834029e+00 4.3283723e+00 4.5943507e+00 5.5219241e+00 3.2001457e+00 5.1552806e+00 4.5786596e+00 5.0423887e+00 3.9353963e+00 4.0804944e+00 4.3648743e+00 3.7469906e+00 3.9346929e+00 4.1523445e+00 4.2650653e+00 5.6468786e+00 5.8321617e+00 3.7127205e+00 4.5943003e+00 3.6444925e+00 5.6275915e+00 3.6885686e+00 4.5212945e+00 4.8635596e+00 3.5807904e+00 3.6545040e+00 4.3827087e+00 4.6717461e+00 5.0136174e+00 5.4320857e+00 4.3994790e+00 3.8323332e+00 4.2736523e+00 5.1501184e+00 4.4249262e+00 4.2490074e+00 3.5500567e+00 4.3022895e+00 4.4864974e+00 4.0933482e+00 3.8247106e+00 4.7495571e+00 4.5943072e+00 4.1245629e+00 3.7976741e+00 4.0232438e+00 4.2129603e+00 3.8158144e+00 4.4535192e-01 3.3681634e+00 3.1015495e+00 3.5417515e+00 2.6663854e+00 3.2198557e+00 3.0579528e+00 3.2934163e+00 2.0153916e+00 3.2040320e+00 2.5204039e+00 2.3388377e+00 2.7956674e+00 2.6725726e+00 3.2655357e+00 2.2078644e+00 3.0402181e+00 3.0721050e+00 2.6595270e+00 3.1749624e+00 2.5094912e+00 3.4048516e+00 2.6019240e+00 3.5045178e+00 3.2523394e+00 2.8999277e+00 3.0267460e+00 3.4333346e+00 3.6317584e+00 3.0844423e+00 2.1209506e+00 2.4419061e+00 2.3443191e+00 2.4920874e+00 3.6775470e+00 3.0715602e+00 3.0884019e+00 3.3261336e+00 3.0501817e+00 2.6631094e+00 2.6243758e+00 2.9691171e+00 3.1659032e+00 2.5983106e+00 2.0538718e+00 2.7808700e+00 2.7473221e+00 2.7650187e+00 2.8804789e+00 1.7660585e+00 2.6784604e+00 4.6691123e+00 3.7183181e+00 4.5689156e+00 4.1821417e+00 4.4377562e+00 5.2873851e+00 3.1455384e+00 4.9362269e+00 4.4131751e+00 4.8285697e+00 3.7508315e+00 3.9249912e+00 4.1665786e+00 3.6588207e+00 3.8312584e+00 3.9914600e+00 4.0953360e+00 5.4042844e+00 5.6094349e+00 3.6203759e+00 4.3940519e+00 3.5472449e+00 5.3896044e+00 3.5318477e+00 4.3383367e+00 4.6370771e+00 3.4283809e+00 3.5084967e+00 4.2335104e+00 4.4348302e+00 4.7765764e+00 5.1494118e+00 4.2515997e+00 3.6735311e+00 4.1503255e+00 4.8803856e+00 4.2802235e+00 4.0874500e+00 3.4119017e+00 4.0856134e+00 4.3069340e+00 3.8658667e+00 3.7183181e+00 4.5670800e+00 4.4180998e+00 3.9298460e+00 3.6561219e+00 3.8459316e+00 4.0712063e+00 3.6910219e+00 3.5300704e+00 3.2300705e+00 3.6894189e+00 2.6906230e+00 3.3402581e+00 3.1455455e+00 3.4142500e+00 1.9871921e+00 3.3364095e+00 2.5801041e+00 2.2579027e+00 2.8953397e+00 2.7040361e+00 3.3706887e+00 2.2848507e+00 3.1889632e+00 3.1641787e+00 2.7405950e+00 3.2351115e+00 2.5567836e+00 3.5066271e+00 2.7031874e+00 3.5985833e+00 3.3547156e+00 3.0245025e+00 3.1656773e+00 3.5695690e+00 3.7624530e+00 3.1847809e+00 2.1665831e+00 2.4678343e+00 2.3636654e+00 2.5680682e+00 3.7710578e+00 3.1606607e+00 3.1985717e+00 3.4665002e+00 3.1244487e+00 2.7540755e+00 2.6724144e+00 3.0392407e+00 3.2747247e+00 2.6671530e+00 2.0066796e+00 2.8554471e+00 2.8427684e+00 2.8549070e+00 2.9928504e+00 1.7230625e+00 2.7611864e+00 4.7742557e+00 3.8047268e+00 4.7018935e+00 4.2892152e+00 4.5488617e+00 5.4303909e+00 3.2037606e+00 5.0724791e+00 4.5217060e+00 4.9623634e+00 3.8707551e+00 4.0296740e+00 4.2915574e+00 3.7321091e+00 3.9154512e+00 4.1022778e+00 4.2113304e+00 5.5520135e+00 5.7453700e+00 3.6849669e+00 4.5212945e+00 3.6308222e+00 5.5330176e+00 3.6335075e+00 4.4607162e+00 4.7770551e+00 3.5299194e+00 3.6126659e+00 4.3390241e+00 4.5771358e+00 4.9175276e+00 5.3118739e+00 4.3561658e+00 3.7812981e+00 4.2455646e+00 5.0340960e+00 4.3872001e+00 4.2015182e+00 3.5126820e+00 4.2176412e+00 4.4249262e+00 3.9985367e+00 3.8047268e+00 4.6887893e+00 4.5358705e+00 4.0502685e+00 3.7475470e+00 3.9619683e+00 4.1767972e+00 3.7895730e+00 6.0611244e-01 2.1845981e-01 1.6212669e+00 5.6769031e-01 1.3103855e+00 7.0437330e-01 2.2923690e+00 4.4651726e-01 1.8497891e+00 2.2196852e+00 1.1283882e+00 1.3099706e+00 9.0827783e-01 1.5790055e+00 3.7427929e-01 1.4018200e+00 1.2701139e+00 1.1341579e+00 1.5133392e+00 1.1134787e+00 1.0264409e+00 8.7202528e-01 9.2264612e-01 6.6432544e-01 4.5470518e-01 4.1449626e-01 4.3456114e-01 1.0085601e+00 1.5838351e+00 1.6415861e+00 1.6742876e+00 1.3140585e+00 1.0496979e+00 1.6013574e+00 1.0054037e+00 3.0546431e-01 1.0168833e+00 1.4293465e+00 1.5774037e+00 1.5278635e+00 9.0252542e-01 1.2994764e+00 2.2231652e+00 1.4317371e+00 1.3207609e+00 1.3224963e+00 8.3649708e-01 2.2607507e+00 1.3421549e+00 1.5412452e+00 1.2539702e+00 1.2643026e+00 1.0324775e+00 1.2342162e+00 1.9387309e+00 2.1209313e+00 1.6105602e+00 1.1912106e+00 1.5832517e+00 7.2486328e-01 8.5585239e-01 9.4009473e-01 1.3873503e+00 1.3945703e+00 1.0313560e+00 8.7720955e-01 2.0658700e+00 2.2655571e+00 1.2460824e+00 1.1834841e+00 1.4368020e+00 2.0378171e+00 7.9878917e-01 1.0960883e+00 1.3102767e+00 8.5105559e-01 9.2480363e-01 1.0805899e+00 1.1043883e+00 1.4313279e+00 1.8006336e+00 1.1235486e+00 7.6625946e-01 1.1633029e+00 1.5390703e+00 1.2493717e+00 9.0965328e-01 1.0182895e+00 8.6983677e-01 1.1880428e+00 9.2095040e-01 1.2539702e+00 1.3335022e+00 1.3109705e+00 9.5035453e-01 9.2112464e-01 7.6166891e-01 1.1418127e+00 1.1276971e+00 5.6700421e-01 1.1449732e+00 4.0293660e-01 7.3813096e-01 2.1845981e-01 1.7551534e+00 3.4583729e-01 1.2603076e+00 1.7354460e+00 5.3588338e-01 1.0777972e+00 3.8934542e-01 1.0669582e+00 3.0811765e-01 8.0294841e-01 7.8768770e-01 1.0018083e+00 1.0175773e+00 5.5419992e-01 5.9426792e-01 7.3496673e-01 4.8927739e-01 3.4378533e-01 2.5651975e-01 5.2655962e-01 5.4292906e-01 4.4417983e-01 1.1634384e+00 1.1527805e+00 1.2020363e+00 8.1521713e-01 7.2526325e-01 1.0018083e+00 4.1449626e-01 3.2586371e-01 9.0277242e-01 8.3172002e-01 1.0440187e+00 9.7779835e-01 3.2816937e-01 8.1521713e-01 1.7083888e+00 8.6361309e-01 7.3145860e-01 7.3145860e-01 3.6171588e-01 1.7816674e+00 7.6914805e-01 1.6177449e+00 8.3060013e-01 1.4732400e+00 1.1107977e+00 1.3546017e+00 2.2147080e+00 1.5404344e+00 1.8624350e+00 1.3603471e+00 1.7544191e+00 6.8961791e-01 8.7478495e-01 1.0733200e+00 9.5271386e-01 1.0466623e+00 9.9348625e-01 1.0085601e+00 2.3452277e+00 2.5288464e+00 1.0480665e+00 1.3130641e+00 8.9653332e-01 2.3282127e+00 5.8914551e-01 1.2436109e+00 1.5625142e+00 4.8927739e-01 4.8927739e-01 1.1593224e+00 1.3813076e+00 1.7138020e+00 2.1603815e+00 1.1866786e+00 6.4755655e-01 1.1521791e+00 1.8620175e+00 1.2565757e+00 1.0067784e+00 4.8927739e-01 1.0056742e+00 1.2594846e+00 9.3049742e-01 8.3060013e-01 1.4762619e+00 1.3817041e+00 9.4558103e-01 7.9613242e-01 7.7074935e-01 1.0627606e+00 7.0776547e-01 1.5593809e+00 4.8036801e-01 1.2165505e+00 6.1151102e-01 2.2871743e+00 4.0176783e-01 1.7963441e+00 2.1851225e+00 1.0906388e+00 1.2884575e+00 8.0619006e-01 1.6156775e+00 5.0905001e-01 1.3093850e+00 1.2434795e+00 1.0262547e+00 1.4875372e+00 1.0069726e+00 1.0669582e+00 7.4511469e-01 8.2384013e-01 6.9728513e-01 5.3022554e-01 3.0811765e-01 2.5651975e-01 9.2264612e-01 1.6478667e+00 1.6180636e+00 1.6694817e+00 1.3199714e+00 9.2288144e-01 1.5068702e+00 9.2859317e-01 2.4837156e-01 9.3238528e-01 1.3813076e+00 1.5238543e+00 1.4346522e+00 8.1130291e-01 1.2794849e+00 2.2234347e+00 1.3629833e+00 1.2671726e+00 1.2652657e+00 8.1810461e-01 2.3116343e+00 1.2988558e+00 1.3410314e+00 1.1276971e+00 1.0590298e+00 8.2552685e-01 1.0264409e+00 1.7485421e+00 2.0171203e+00 1.4118594e+00 9.7949166e-01 1.3980896e+00 5.7324170e-01 6.6317860e-01 7.4586719e-01 1.2603076e+00 1.2604558e+00 8.7504951e-01 6.6432544e-01 1.8915404e+00 2.0711789e+00 1.1178264e+00 9.9368623e-01 1.3223897e+00 1.8515012e+00 6.6384020e-01 8.9303452e-01 1.1107977e+00 7.2823007e-01 8.1099042e-01 8.7170815e-01 9.0876485e-01 1.2370832e+00 1.6626615e+00 9.2112464e-01 6.2482915e-01 9.7377870e-01 1.3752391e+00 1.0720678e+00 7.0386584e-01 9.0876485e-01 6.8917100e-01 1.0120221e+00 8.0294841e-01 1.1276971e+00 1.1329323e+00 1.1353806e+00 8.1385214e-01 7.7588000e-01 5.8914551e-01 9.8054887e-01 1.0085601e+00 1.0879524e+00 6.2605182e-01 1.2079117e+00 8.2305664e-01 1.1903922e+00 4.4651726e-01 6.5648056e-01 7.4164639e-01 5.2942799e-01 8.9852394e-01 6.4755655e-01 1.3035649e+00 7.7074935e-01 4.8135521e-01 7.7074935e-01 2.5651975e-01 1.1022599e+00 6.8917100e-01 1.0627606e+00 8.6290690e-01 9.7759114e-01 1.1868139e+00 1.3969297e+00 1.4333755e+00 7.6166891e-01 5.6075294e-01 2.5251796e-01 3.7427929e-01 4.4651726e-01 1.1444449e+00 7.7074935e-01 1.1571858e+00 1.3491011e+00 8.2624515e-01 7.0086313e-01 2.0000000e-01 4.4535192e-01 8.9852394e-01 3.7427929e-01 7.7885297e-01 4.1449626e-01 7.0826681e-01 6.1092863e-01 8.2275389e-01 1.0198386e+00 5.0905001e-01 2.2002582e+00 1.1640914e+00 2.2347161e+00 1.6833015e+00 1.9584639e+00 2.9773446e+00 7.2852070e-01 2.5984158e+00 1.9494155e+00 2.5625921e+00 1.4644662e+00 1.4491244e+00 1.8190688e+00 1.0934620e+00 1.3861754e+00 1.6265426e+00 1.6626615e+00 3.1878246e+00 3.2549253e+00 1.0346741e+00 2.0606771e+00 1.0425476e+00 3.0882196e+00 1.1022599e+00 1.9692383e+00 2.3537589e+00 1.0082605e+00 1.0950112e+00 1.7332099e+00 2.1942739e+00 2.5032087e+00 3.0886055e+00 1.7538274e+00 1.2342162e+00 1.6237100e+00 2.7181432e+00 1.8926658e+00 1.6507294e+00 1.0082605e+00 1.8244836e+00 1.9254808e+00 1.7303440e+00 1.1640914e+00 2.1641182e+00 2.0565627e+00 1.6435752e+00 1.1782910e+00 1.4699978e+00 1.7142546e+00 1.2095267e+00 8.0326782e-01 5.0991930e-01 1.8350577e+00 2.1269358e-01 1.3537729e+00 1.7146525e+00 6.5172743e-01 8.5585239e-01 4.0438741e-01 1.1847335e+00 3.4583729e-01 9.0252542e-01 8.2372435e-01 6.2024833e-01 1.0324775e+00 6.6827038e-01 6.5172743e-01 3.8776762e-01 4.4535192e-01 3.2816937e-01 2.5651975e-01 3.2586371e-01 4.3691963e-01 5.0180477e-01 1.2342162e+00 1.1573546e+00 1.2172454e+00 8.7848692e-01 6.2205176e-01 1.1016264e+00 6.9006418e-01 3.2586371e-01 5.2371571e-01 9.4492923e-01 1.0646687e+00 1.0101422e+00 4.1449626e-01 8.2552685e-01 1.7679545e+00 9.2288144e-01 8.3888121e-01 8.2929029e-01 3.8934542e-01 1.8776878e+00 8.5434758e-01 1.5481649e+00 7.9613242e-01 1.3657247e+00 1.0085601e+00 1.2641849e+00 2.1002817e+00 1.6030661e+00 1.7482192e+00 1.2100024e+00 1.7005893e+00 6.6491075e-01 7.3535471e-01 9.7949166e-01 8.8358844e-01 1.0423677e+00 9.5498315e-01 9.1051084e-01 2.2737459e+00 2.4086493e+00 7.2486328e-01 1.2326306e+00 9.4832302e-01 2.2096958e+00 3.8934542e-01 1.1729895e+00 1.4561933e+00 3.8776762e-01 4.8927739e-01 1.0574300e+00 1.2643026e+00 1.5918956e+00 2.0914667e+00 1.0906388e+00 5.0817745e-01 1.0182895e+00 1.7457596e+00 1.2250414e+00 9.1663180e-01 5.4292906e-01 9.1750357e-01 1.1891470e+00 8.8358844e-01 7.9613242e-01 1.3916739e+00 1.3267389e+00 8.9303452e-01 5.3309112e-01 6.9325418e-01 1.0574013e+00 7.0776547e-01 7.0776547e-01 1.3071453e+00 9.0049692e-01 6.9006418e-01 1.2079117e+00 3.6171588e-01 7.1791510e-01 4.1586001e-01 9.0049692e-01 1.0069726e+00 2.5251796e-01 4.4651726e-01 6.9325418e-01 6.2538346e-01 5.9426792e-01 5.6631629e-01 6.6827038e-01 4.1449626e-01 7.0437330e-01 9.0277242e-01 1.1055069e+00 1.0496979e+00 3.2586371e-01 1.0083666e+00 7.4164639e-01 8.3888121e-01 6.0181382e-01 6.3861009e-01 3.4378533e-01 6.3808075e-01 1.0101422e+00 6.8961791e-01 4.1449626e-01 5.3588338e-01 2.5651975e-01 4.1586001e-01 5.0991930e-01 1.2869134e+00 3.0546431e-01 3.2586371e-01 3.0275928e-01 5.0905001e-01 1.5278635e+00 4.0000000e-01 1.7279861e+00 7.4586719e-01 1.7831878e+00 1.1718516e+00 1.4824233e+00 2.5111349e+00 8.3620494e-01 2.1256928e+00 1.4719311e+00 2.0814452e+00 1.0175773e+00 1.0014633e+00 1.3875139e+00 7.7885297e-01 1.1473003e+00 1.2144845e+00 1.1591754e+00 2.6773585e+00 2.7900071e+00 7.0776547e-01 1.6151673e+00 7.3496673e-01 2.6263773e+00 7.2526325e-01 1.4645804e+00 1.8755806e+00 6.3924842e-01 6.2407309e-01 1.2731262e+00 1.7507664e+00 2.0635966e+00 2.6116811e+00 1.3129189e+00 7.4855857e-01 1.1149070e+00 2.3160147e+00 1.4246028e+00 1.1229843e+00 5.6075294e-01 1.4120836e+00 1.5094575e+00 1.4108494e+00 7.4586719e-01 1.6884234e+00 1.6211869e+00 1.3017208e+00 8.1521713e-01 1.0401425e+00 1.2452704e+00 6.9728513e-01 1.8185955e+00 4.8135521e-01 1.2509218e+00 1.8049926e+00 5.8914551e-01 1.2205493e+00 4.2538717e-01 1.1912106e+00 4.6472023e-01 7.1840099e-01 8.9207714e-01 1.1017858e+00 1.1127329e+00 4.1586001e-01 7.8197925e-01 8.0326782e-01 5.7257017e-01 5.2655962e-01 4.3456114e-01 6.2660376e-01 4.8135521e-01 4.5581864e-01 1.3318128e+00 1.2468939e+00 1.3144065e+00 9.4935318e-01 6.6384020e-01 9.1075311e-01 3.2586371e-01 4.1449626e-01 1.0130748e+00 8.3280511e-01 1.0906119e+00 9.6204649e-01 3.4583729e-01 9.3296062e-01 1.7901543e+00 8.7170815e-01 7.3805807e-01 7.3805807e-01 5.2655962e-01 1.9041928e+00 8.1521713e-01 1.4138821e+00 7.3805807e-01 1.3166957e+00 9.2264612e-01 1.1533602e+00 2.0690479e+00 1.4700179e+00 1.7092525e+00 1.2231847e+00 1.5870088e+00 5.0592043e-01 7.5791688e-01 9.0575661e-01 9.1750357e-01 9.1802948e-01 8.1304731e-01 8.1638392e-01 2.1978861e+00 2.3802944e+00 1.1107977e+00 1.1386292e+00 7.9878917e-01 2.1900222e+00 6.1092863e-01 1.0480665e+00 1.4148192e+00 5.0991930e-01 3.6171588e-01 9.7825559e-01 1.2593659e+00 1.5912764e+00 2.0615043e+00 1.0056742e+00 5.6700421e-01 1.0137836e+00 1.7695175e+00 1.0597541e+00 8.0619006e-01 3.8934542e-01 8.6513410e-01 1.0755693e+00 8.4050231e-01 7.3805807e-01 1.2832075e+00 1.1947245e+00 8.0660588e-01 8.2105460e-01 5.9426792e-01 8.6983677e-01 5.3309112e-01 1.9083318e+00 6.7975091e-01 4.1449626e-01 1.2452704e+00 1.1763980e+00 1.6420607e+00 7.9016429e-01 1.9365498e+00 1.3172979e+00 1.0653845e+00 1.5684812e+00 8.1304731e-01 1.7169601e+00 1.2768639e+00 1.8804140e+00 1.6311692e+00 1.6315809e+00 1.8424891e+00 2.1489929e+00 2.2038673e+00 1.4613032e+00 8.0587320e-01 6.8961791e-01 6.4704320e-01 9.7949166e-01 1.9250543e+00 1.2802798e+00 1.5824669e+00 2.0485534e+00 1.5790055e+00 1.0078327e+00 8.2305664e-01 1.1498269e+00 1.5838351e+00 1.0137836e+00 1.2418578e-01 1.0230441e+00 1.1119327e+00 1.0941064e+00 1.4719311e+00 3.2816937e-01 1.0165138e+00 2.9338155e+00 1.9158303e+00 3.0455280e+00 2.4635485e+00 2.7430309e+00 3.7921012e+00 1.2632199e+00 3.4105293e+00 2.7619926e+00 3.3261421e+00 2.2045198e+00 2.2598424e+00 2.6204307e+00 1.8330979e+00 2.0701646e+00 2.3622531e+00 2.4525409e+00 3.9619101e+00 4.0743074e+00 1.8315269e+00 2.8475224e+00 1.7388184e+00 3.9054939e+00 1.9111264e+00 2.7377517e+00 3.1510494e+00 1.7964653e+00 1.8350071e+00 2.5277506e+00 2.9970778e+00 3.3196868e+00 3.8532018e+00 2.5453122e+00 2.0312250e+00 2.3887539e+00 3.5269824e+00 2.5986705e+00 2.4210417e+00 1.7228354e+00 2.6125646e+00 2.7062349e+00 2.4839132e+00 1.9158303e+00 2.9502077e+00 2.8139128e+00 2.4180244e+00 1.9947426e+00 2.2550764e+00 2.3956104e+00 1.9332869e+00 1.4468211e+00 1.8027242e+00 7.3851529e-01 9.0658670e-01 5.0180477e-01 1.2418578e+00 2.5651975e-01 1.0022010e+00 8.6361309e-01 7.3851529e-01 1.1055064e+00 7.8197925e-01 6.8961791e-01 4.8927739e-01 5.0270183e-01 3.2352160e-01 2.1269358e-01 2.5651975e-01 4.9857388e-01 6.0611244e-01 1.2633451e+00 1.2342162e+00 1.2794849e+00 9.3824087e-01 7.0776547e-01 1.2014753e+00 7.0429250e-01 2.5651975e-01 6.2482915e-01 1.0329901e+00 1.1593224e+00 1.1069580e+00 5.0180477e-01 8.9712482e-01 1.8394959e+00 1.0181000e+00 9.2095040e-01 9.2047746e-01 4.4417983e-01 1.9314297e+00 9.4103005e-01 1.6328100e+00 9.3238528e-01 1.3969297e+00 1.0389435e+00 1.3327491e+00 2.0961718e+00 1.7103548e+00 1.7405652e+00 1.2330392e+00 1.7474965e+00 7.7919451e-01 8.1810461e-01 1.0613462e+00 1.0417249e+00 1.2331989e+00 1.0974061e+00 9.4125538e-01 2.2568188e+00 2.4127176e+00 8.4050231e-01 1.3145067e+00 1.0960883e+00 2.1973666e+00 5.6075294e-01 1.2221471e+00 1.4467170e+00 5.7324170e-01 6.3977563e-01 1.1341579e+00 1.2436109e+00 1.5826476e+00 2.0476065e+00 1.1847335e+00 5.3665999e-01 1.0391247e+00 1.7521201e+00 1.3293211e+00 9.4492923e-01 6.9369532e-01 1.0019724e+00 1.3083079e+00 1.0406064e+00 9.3238528e-01 1.4580335e+00 1.4387122e+00 1.0576043e+00 7.0233835e-01 8.1304731e-01 1.1697902e+00 8.2372435e-01 7.6914805e-01 7.2823007e-01 8.7504951e-01 1.0611732e+00 4.5581864e-01 1.5204521e+00 6.6432544e-01 6.5172743e-01 1.0866092e+00 4.5470518e-01 1.0573285e+00 9.0074515e-01 1.3083079e+00 1.0613462e+00 1.2123540e+00 1.4190961e+00 1.6754036e+00 1.6596797e+00 8.9095811e-01 6.1947990e-01 4.2418962e-01 4.8927739e-01 6.0551856e-01 1.2951131e+00 6.2538346e-01 1.0030700e+00 1.5663312e+00 1.1396406e+00 4.5581864e-01 3.2816937e-01 5.3665999e-01 1.0166932e+00 6.0670504e-01 6.9167458e-01 4.4535192e-01 5.6075294e-01 5.3665999e-01 1.0182895e+00 9.1075311e-01 5.0991930e-01 2.2632657e+00 1.2601890e+00 2.4384530e+00 1.8269304e+00 2.0927845e+00 3.1870761e+00 6.4290921e-01 2.8096725e+00 2.1462316e+00 2.6900593e+00 1.5901181e+00 1.6364474e+00 2.0101738e+00 1.1729895e+00 1.4078246e+00 1.7083888e+00 1.8278268e+00 3.3435703e+00 3.4604677e+00 1.2331989e+00 2.2206574e+00 1.0719360e+00 3.3068858e+00 1.3163598e+00 2.0994872e+00 2.5539296e+00 1.1948578e+00 1.1991899e+00 1.8828324e+00 2.4245766e+00 2.7358293e+00 3.2702869e+00 1.8959565e+00 1.4312787e+00 1.7665622e+00 2.9527671e+00 1.9255490e+00 1.7860690e+00 1.0796583e+00 2.0205937e+00 2.0647798e+00 1.9168750e+00 1.2601890e+00 2.3069539e+00 2.1608869e+00 1.8128438e+00 1.3838212e+00 1.6376058e+00 1.7220696e+00 1.2768639e+00 1.2687651e+00 1.0344911e+00 1.5320003e+00 9.7779835e-01 1.8837258e+00 1.2979752e+00 1.0012667e+00 1.3957794e+00 7.2526325e-01 1.6850672e+00 1.2372418e+00 1.7004805e+00 1.4979666e+00 1.5611922e+00 1.7745022e+00 2.0153916e+00 2.0815027e+00 1.3830210e+00 8.1242502e-01 5.8914551e-01 5.7257017e-01 9.5676647e-01 1.7518264e+00 1.2724737e+00 1.6669115e+00 1.9675324e+00 1.4200435e+00 1.1136605e+00 7.1881659e-01 1.0072663e+00 1.5134954e+00 9.3238528e-01 3.2352160e-01 9.5222919e-01 1.1681971e+00 1.1043332e+00 1.4120836e+00 6.2205176e-01 1.0078327e+00 2.8028143e+00 1.7525933e+00 2.8815987e+00 2.2944257e+00 2.5825907e+00 3.6132031e+00 1.1179743e+00 3.2286633e+00 2.5673494e+00 3.2133201e+00 2.1127170e+00 2.0867931e+00 2.4721080e+00 1.6626615e+00 1.9456450e+00 2.2607446e+00 2.2983453e+00 3.8335668e+00 3.8818411e+00 1.6299374e+00 2.7127377e+00 1.6132118e+00 3.7182722e+00 1.7559391e+00 2.6123294e+00 2.9966900e+00 1.6625128e+00 1.7303440e+00 2.3560577e+00 2.8340159e+00 3.1407514e+00 3.7366777e+00 2.3765195e+00 1.8701780e+00 2.1933937e+00 3.3657099e+00 2.5066503e+00 2.2797241e+00 1.6331631e+00 2.4808010e+00 2.5698271e+00 2.3770285e+00 1.7525933e+00 2.8053367e+00 2.6963680e+00 2.2934334e+00 1.8202060e+00 2.1229819e+00 2.3231793e+00 1.8122257e+00 8.5462626e-01 5.0991930e-01 6.2538346e-01 8.0358695e-01 3.7255734e-01 5.3022554e-01 8.2105460e-01 6.0900723e-01 6.2482915e-01 3.0844217e-01 7.9580667e-01 5.4292906e-01 5.0991930e-01 7.0437330e-01 9.7270522e-01 9.9532071e-01 3.0546431e-01 7.9878917e-01 7.2343175e-01 7.8768770e-01 4.2418962e-01 9.0876485e-01 5.2862779e-01 4.4651726e-01 8.5205778e-01 7.4164639e-01 3.2586371e-01 5.7867728e-01 5.3309112e-01 4.1449626e-01 4.5581864e-01 1.2131545e+00 3.8776762e-01 3.2352160e-01 2.5251796e-01 3.2816937e-01 1.3221405e+00 2.8507955e-01 1.8873850e+00 9.2859317e-01 1.8730683e+00 1.4111029e+00 1.6550480e+00 2.6320302e+00 1.0406064e+00 2.2657813e+00 1.6658308e+00 2.1339968e+00 1.0072663e+00 1.1444449e+00 1.4417207e+00 8.9971984e-01 1.1192426e+00 1.2342162e+00 1.3367840e+00 2.7726042e+00 2.9277580e+00 9.9368623e-01 1.6694974e+00 7.8197925e-01 2.7493700e+00 7.5976039e-01 1.5849874e+00 1.9802196e+00 6.4290921e-01 7.1799256e-01 1.4445746e+00 1.8216794e+00 2.1462316e+00 2.6367554e+00 1.4616539e+00 9.2264612e-01 1.4088394e+00 2.3235034e+00 1.5120955e+00 1.3224963e+00 6.2024833e-01 1.4078246e+00 1.5587730e+00 1.2801437e+00 9.2859317e-01 1.8096161e+00 1.6710566e+00 1.2378278e+00 8.9653332e-01 1.0864449e+00 1.3071453e+00 9.0827783e-01 8.9159388e-01 7.7763126e-01 1.0417249e+00 9.1802948e-01 5.0905001e-01 6.2605182e-01 4.4651726e-01 1.2379511e+00 6.2024833e-01 9.5571254e-01 8.1558458e-01 7.5976039e-01 9.2944046e-01 1.0661822e+00 1.2662457e+00 8.2342214e-01 5.8851328e-01 5.1691876e-01 5.3588338e-01 5.1691876e-01 1.1717125e+00 9.6838716e-01 1.2601890e+00 1.1258723e+00 4.8135521e-01 8.3649708e-01 5.5419992e-01 6.2407309e-01 9.0965328e-01 4.2538717e-01 1.0906388e+00 5.9426792e-01 8.1638392e-01 7.3145860e-01 7.3145860e-01 1.1880428e+00 6.3861009e-01 2.2927296e+00 1.2766755e+00 2.1156916e+00 1.6869465e+00 1.9835684e+00 2.8209672e+00 1.2028939e+00 2.4458200e+00 1.8683030e+00 2.5149752e+00 1.4746001e+00 1.4371043e+00 1.7501772e+00 1.2500343e+00 1.5991931e+00 1.7211928e+00 1.6271057e+00 3.0580852e+00 3.1168283e+00 1.0328064e+00 2.0203543e+00 1.2344562e+00 2.9146252e+00 1.0941064e+00 1.9515265e+00 2.2002582e+00 1.0531192e+00 1.1790011e+00 1.7600233e+00 1.9895190e+00 2.3106402e+00 2.8876509e+00 1.7983401e+00 1.1763719e+00 1.6118154e+00 2.5100676e+00 2.0039716e+00 1.6449456e+00 1.1276917e+00 1.7245185e+00 1.9495298e+00 1.6638124e+00 1.2766755e+00 2.1512175e+00 2.1034605e+00 1.6449189e+00 1.1924295e+00 1.4645804e+00 1.8408873e+00 1.3037063e+00 1.1269972e+00 6.2482915e-01 5.0991930e-01 6.6827038e-01 7.0479928e-01 8.8358844e-01 4.5581864e-01 7.0086313e-01 4.2667565e-01 2.0656129e-01 4.4535192e-01 5.2942799e-01 7.0086313e-01 6.3861009e-01 2.1269358e-01 1.2261087e+00 1.0119857e+00 1.1001291e+00 8.1638392e-01 4.2667565e-01 7.0479928e-01 5.1691876e-01 6.0611244e-01 6.2538346e-01 6.9006418e-01 8.3812833e-01 6.4290921e-01 1.2418578e-01 7.3145860e-01 1.6044563e+00 6.2660376e-01 5.7324170e-01 5.6700421e-01 4.0293660e-01 1.7981158e+00 6.4806901e-01 1.5090287e+00 5.9426792e-01 1.4258804e+00 9.2264612e-01 1.2221471e+00 2.1613095e+00 1.2165505e+00 1.7814077e+00 1.1712156e+00 1.7475837e+00 7.0233835e-01 7.0776547e-01 1.0386594e+00 7.0233835e-01 1.0228981e+00 9.8450810e-01 8.5105559e-01 2.3257048e+00 2.4547248e+00 7.1504098e-01 1.2838690e+00 6.9369532e-01 2.2753334e+00 4.3691963e-01 1.1508502e+00 1.5109753e+00 4.0438741e-01 4.1449626e-01 1.0168833e+00 1.3670543e+00 1.6898941e+00 2.2253038e+00 1.0655560e+00 4.1586001e-01 9.0827783e-01 1.9262937e+00 1.2075315e+00 8.3888121e-01 4.0438741e-01 1.0401425e+00 1.2250414e+00 1.0755693e+00 5.9426792e-01 1.3866792e+00 1.3487634e+00 1.0056742e+00 5.9426792e-01 7.2526325e-01 1.0425476e+00 5.0592043e-01 1.2125198e+00 9.0252542e-01 5.4292906e-01 1.0679144e+00 4.5470518e-01 1.2307737e+00 5.6700421e-01 1.3629833e+00 1.1271488e+00 9.3592296e-01 1.1329323e+00 1.4903933e+00 1.5826638e+00 9.2264612e-01 3.7598397e-01 5.1691876e-01 5.3022554e-01 3.4583729e-01 1.5102079e+00 9.0478973e-01 9.6664346e-01 1.3678655e+00 1.0012667e+00 5.0090417e-01 4.9766035e-01 8.1130291e-01 1.0331736e+00 4.5581864e-01 7.6955924e-01 6.0551856e-01 6.0181382e-01 6.0060595e-01 8.1242502e-01 7.2852070e-01 5.0180477e-01 2.4944334e+00 1.5259640e+00 2.4897635e+00 2.0286682e+00 2.2758462e+00 3.2467454e+00 1.0417249e+00 2.8819633e+00 2.2804674e+00 2.7472449e+00 1.6234861e+00 1.7644184e+00 2.0587064e+00 1.4533724e+00 1.6559784e+00 1.8347926e+00 1.9605308e+00 3.3855928e+00 3.5452222e+00 1.4540815e+00 2.2852232e+00 1.3536716e+00 3.3617477e+00 1.3717027e+00 2.2096171e+00 2.5931780e+00 1.2603076e+00 1.3371180e+00 2.0643410e+00 2.4233813e+00 2.7521037e+00 3.2246201e+00 2.0784533e+00 1.5405106e+00 2.0088441e+00 2.9099860e+00 2.1140685e+00 1.9448322e+00 1.2330392e+00 2.0122105e+00 2.1687358e+00 1.8353933e+00 1.5259640e+00 2.4321505e+00 2.2783778e+00 1.8251179e+00 1.4795374e+00 1.7068208e+00 1.9049236e+00 1.5165339e+00 1.1004794e+00 9.4753140e-01 9.4125538e-01 1.1763719e+00 8.5105559e-01 6.6432544e-01 7.2526325e-01 6.4290921e-01 3.2816937e-01 1.2418578e-01 4.4535192e-01 6.2024833e-01 7.0479928e-01 1.2172454e+00 1.3021788e+00 1.3289150e+00 9.6141901e-01 8.9366705e-01 1.3003320e+00 7.1840099e-01 3.0275928e-01 8.2654509e-01 1.1056650e+00 1.2497790e+00 1.2234738e+00 6.0611244e-01 9.6141901e-01 1.8661545e+00 1.1149070e+00 1.0038051e+00 1.0038051e+00 5.0991930e-01 1.8886923e+00 1.0132664e+00 1.7427900e+00 1.0573285e+00 1.5462225e+00 1.2230220e+00 1.4700179e+00 2.2554582e+00 1.8183902e+00 1.9191337e+00 1.4359851e+00 1.8399871e+00 8.1558458e-01 9.6664346e-01 1.1754055e+00 1.1548215e+00 1.2523175e+00 1.1229906e+00 1.1149070e+00 2.3868096e+00 2.5734684e+00 1.0665149e+00 1.4148192e+00 1.1763719e+00 2.3591336e+00 6.6317860e-01 1.3545005e+00 1.6178623e+00 6.3735887e-01 7.2526325e-01 1.2709820e+00 1.4169523e+00 1.7425222e+00 2.1449779e+00 1.3015611e+00 7.4777660e-01 1.2601890e+00 1.8513630e+00 1.3897316e+00 1.1185330e+00 7.6625946e-01 1.0919712e+00 1.3783420e+00 1.0120221e+00 1.0573285e+00 1.5860263e+00 1.5022608e+00 1.0597541e+00 8.3060013e-01 8.9095811e-01 1.2107055e+00 9.5498315e-01 5.9426792e-01 8.8835966e-01 7.2486328e-01 4.3456114e-01 6.3108414e-01 7.9580667e-01 5.4292906e-01 8.0619006e-01 1.0003942e+00 1.2057554e+00 1.1282371e+00 4.0147421e-01 1.0482443e+00 8.3812833e-01 9.3049742e-01 6.4290921e-01 6.6432544e-01 2.0000000e-01 4.9766035e-01 1.1016264e+00 8.7202528e-01 4.1312257e-01 6.2660376e-01 4.4651726e-01 5.0180477e-01 5.9426792e-01 1.3172979e+00 3.8776762e-01 3.7427929e-01 3.2816937e-01 6.1151102e-01 1.5338492e+00 4.2667565e-01 1.6536633e+00 6.6827038e-01 1.8195408e+00 1.1798960e+00 1.4588731e+00 2.5552364e+00 7.7039952e-01 2.1764356e+00 1.5179392e+00 2.0659196e+00 1.0072663e+00 1.0165138e+00 1.4077317e+00 7.0523271e-01 9.7441804e-01 1.1290808e+00 1.1879078e+00 2.7003420e+00 2.8251568e+00 8.7478495e-01 1.6113870e+00 5.7257017e-01 2.6756977e+00 7.5976039e-01 1.4611141e+00 1.9290721e+00 6.4290921e-01 5.8851328e-01 1.2509218e+00 1.8216794e+00 2.1226924e+00 2.6556584e+00 1.2741904e+00 8.1527569e-01 1.1396406e+00 2.3658814e+00 1.3219975e+00 1.1377990e+00 4.8036801e-01 1.4418088e+00 1.4695582e+00 1.4097125e+00 6.6827038e-01 1.6762567e+00 1.5624022e+00 1.2731262e+00 8.4812820e-01 1.0423677e+00 1.1235486e+00 6.3808075e-01 7.0328431e-01 2.8507955e-01 9.7377870e-01 3.7598397e-01 8.9971984e-01 6.2538346e-01 6.2988288e-01 8.4591037e-01 1.1042097e+00 1.1949615e+00 5.7867728e-01 6.0121055e-01 4.2418962e-01 4.8036801e-01 2.4837156e-01 1.0588560e+00 6.3735887e-01 8.4050231e-01 1.0216438e+00 6.0900723e-01 3.8776762e-01 3.8934542e-01 3.8934542e-01 6.0900723e-01 2.1269358e-01 1.0100718e+00 3.2586371e-01 3.2816937e-01 3.2816937e-01 4.6472023e-01 1.1765359e+00 3.0546431e-01 2.1603815e+00 1.1833480e+00 2.0696037e+00 1.5733646e+00 1.8844302e+00 2.7913211e+00 1.0279631e+00 2.4069427e+00 1.8096161e+00 2.4013270e+00 1.3194762e+00 1.3641156e+00 1.6852518e+00 1.1847335e+00 1.5344133e+00 1.5901181e+00 1.5133392e+00 2.9601125e+00 3.0929882e+00 9.7994716e-01 1.9359434e+00 1.1341579e+00 2.8969791e+00 1.0267435e+00 1.8174459e+00 2.1353579e+00 9.5498315e-01 1.0067464e+00 1.6752254e+00 1.9600024e+00 2.3015655e+00 2.8161147e+00 1.7177705e+00 1.0636401e+00 1.5095556e+00 2.5229584e+00 1.8388413e+00 1.5016471e+00 9.4558103e-01 1.6620056e+00 1.8661202e+00 1.6246433e+00 1.1833480e+00 2.0524784e+00 1.9917352e+00 1.5896248e+00 1.1449732e+00 1.3635198e+00 1.6539414e+00 1.1377990e+00 7.8695083e-01 1.0194752e+00 6.9369532e-01 4.4535192e-01 6.2538346e-01 7.1169738e-01 8.2684479e-01 7.5791688e-01 8.9971984e-01 7.0394675e-01 1.0777972e+00 8.9366705e-01 9.7548738e-01 7.3805807e-01 6.9369532e-01 9.9348625e-01 1.2013436e+00 9.4287188e-01 2.1845981e-01 9.1163729e-01 7.8197925e-01 7.4777660e-01 8.0096515e-01 6.3735887e-01 1.5043029e+00 7.0776547e-01 8.7021234e-01 7.8197925e-01 7.0784540e-01 1.6629594e+00 7.2852070e-01 1.7521201e+00 7.5705927e-01 1.5812904e+00 1.1798960e+00 1.4306494e+00 2.2994849e+00 1.3047221e+00 1.9342059e+00 1.3259654e+00 2.0165210e+00 1.0919404e+00 8.7720955e-01 1.2180145e+00 7.1881659e-01 1.0466623e+00 1.2389598e+00 1.1426203e+00 2.5872805e+00 2.5766735e+00 5.0817745e-01 1.4924169e+00 8.3060013e-01 2.3982377e+00 5.8914551e-01 1.4728952e+00 1.7209381e+00 6.3808075e-01 8.3649708e-01 1.1916257e+00 1.5183917e+00 1.7983401e+00 2.4620092e+00 1.2174316e+00 7.4549115e-01 1.1136343e+00 1.9965599e+00 1.5255331e+00 1.1891470e+00 8.2384013e-01 1.2304904e+00 1.3937115e+00 1.1842231e+00 7.5705927e-01 1.6132118e+00 1.5725854e+00 1.1127329e+00 5.8914551e-01 9.8054887e-01 1.4089719e+00 9.0521488e-01 1.1043332e+00 5.3665999e-01 1.1040512e+00 8.6137722e-01 8.5335130e-01 1.0692258e+00 1.3395518e+00 1.4121163e+00 7.2343175e-01 4.0438741e-01 1.4096146e-01 2.1845981e-01 2.5251796e-01 1.2340567e+00 7.2852070e-01 1.0216438e+00 1.2599182e+00 7.7360126e-01 5.1607523e-01 2.1269358e-01 5.0270183e-01 8.3345577e-01 2.1845981e-01 7.4893123e-01 3.4378533e-01 5.3022554e-01 4.5581864e-01 6.9167458e-01 9.4080461e-01 3.4583729e-01 2.3056888e+00 1.2961380e+00 2.2790932e+00 1.7645599e+00 2.0520955e+00 3.0186066e+00 8.9827435e-01 2.6386155e+00 2.0191749e+00 2.5992685e+00 1.4843324e+00 1.5325189e+00 1.8691652e+00 1.2554784e+00 1.5582387e+00 1.7070813e+00 1.7171798e+00 3.2008583e+00 3.3121677e+00 1.1313840e+00 2.1147926e+00 1.1879078e+00 3.1280700e+00 1.1681971e+00 2.0145868e+00 2.3728666e+00 1.0720678e+00 1.1437669e+00 1.8347926e+00 2.2027051e+00 2.5315934e+00 3.0688850e+00 1.8636112e+00 1.2768639e+00 1.7126039e+00 2.7381221e+00 1.9739212e+00 1.7039473e+00 1.0573285e+00 1.8507968e+00 2.0095044e+00 1.7591313e+00 1.2961380e+00 2.2339736e+00 2.1358764e+00 1.7106141e+00 1.2731262e+00 1.5241199e+00 1.7828037e+00 1.2869134e+00 8.7720955e-01 7.4777660e-01 6.5223271e-01 7.1881659e-01 7.6914805e-01 9.3999899e-01 8.0619006e-01 4.2418962e-01 1.4104707e+00 1.2144845e+00 1.3128167e+00 1.0056742e+00 5.3665999e-01 5.6075294e-01 3.4583729e-01 8.1130291e-01 9.7730901e-01 7.8197925e-01 9.9089002e-01 8.0353565e-01 4.3691963e-01 9.6095130e-01 1.7110336e+00 7.7033318e-01 7.5196795e-01 7.0776547e-01 6.5648056e-01 1.8915404e+00 7.9878917e-01 1.2730931e+00 5.3022554e-01 1.4349259e+00 8.3620494e-01 1.0733200e+00 2.1767273e+00 1.0960883e+00 1.8048569e+00 1.2035173e+00 1.6539414e+00 6.2482915e-01 7.0523271e-01 1.0184370e+00 7.1169738e-01 6.6432544e-01 7.0480730e-01 8.1527569e-01 2.3116343e+00 2.4505705e+00 1.0085601e+00 1.2063335e+00 4.5581864e-01 2.3022338e+00 5.6700421e-01 1.0655560e+00 1.5550492e+00 4.4417983e-01 2.5251796e-01 8.8358844e-01 1.4559276e+00 1.7532140e+00 2.2755980e+00 8.9653332e-01 5.5160819e-01 9.1163729e-01 1.9862884e+00 9.1163729e-01 7.6752131e-01 2.0656129e-01 1.0632598e+00 1.0516761e+00 1.0391247e+00 5.3022554e-01 1.2756158e+00 1.1406052e+00 8.7720955e-01 7.3851529e-01 6.5633874e-01 7.0776547e-01 3.2352160e-01 9.1273187e-01 7.0043186e-01 3.7427929e-01 5.7324170e-01 9.3615100e-01 1.0733200e+00 5.0991930e-01 5.9426792e-01 6.5633874e-01 6.7975091e-01 3.0811765e-01 1.1056650e+00 7.7360126e-01 7.0429250e-01 8.2552685e-01 5.7257017e-01 5.0905001e-01 6.1968386e-01 6.5223271e-01 6.0611244e-01 3.2586371e-01 1.2028939e+00 5.0905001e-01 4.2667565e-01 4.1449626e-01 3.0546431e-01 1.2470767e+00 4.0147421e-01 2.1213832e+00 1.1521791e+00 2.0070710e+00 1.6126950e+00 1.8637576e+00 2.7490677e+00 1.2370832e+00 2.3910690e+00 1.8274132e+00 2.3004229e+00 1.1979861e+00 1.3368881e+00 1.5972416e+00 1.1093572e+00 1.3693737e+00 1.4644753e+00 1.5211725e+00 2.9013543e+00 3.0559175e+00 1.0590298e+00 1.8374244e+00 1.0423677e+00 2.8588399e+00 9.4309624e-01 1.7735968e+00 2.0979142e+00 8.5205778e-01 9.4287188e-01 1.6546836e+00 1.9109434e+00 2.2424413e+00 2.7118839e+00 1.6774684e+00 1.1029298e+00 1.6007141e+00 2.3898698e+00 1.7557336e+00 1.5191033e+00 8.5462626e-01 1.5344007e+00 1.7571295e+00 1.3898545e+00 1.1521791e+00 1.9987470e+00 1.8815752e+00 1.4085850e+00 1.0646687e+00 1.2740417e+00 1.5577803e+00 1.1296247e+00 4.0176783e-01 6.5223271e-01 6.3977563e-01 5.3022554e-01 5.7324170e-01 5.2574978e-01 1.4438552e+00 1.2221471e+00 1.3131724e+00 1.0406064e+00 3.4583729e-01 9.5943875e-01 9.2859317e-01 6.5172743e-01 5.1607523e-01 9.7548738e-01 1.0611732e+00 8.6137722e-01 5.3665999e-01 9.4854455e-01 1.8311457e+00 8.7420176e-01 8.7170815e-01 8.4050231e-01 6.5223271e-01 2.0303919e+00 8.9917007e-01 1.3866318e+00 5.7867728e-01 1.2002762e+00 7.4740267e-01 1.0440187e+00 1.9213397e+00 1.4087466e+00 1.5433565e+00 9.2836103e-01 1.6392533e+00 7.7360126e-01 5.0592043e-01 8.5585239e-01 6.8961791e-01 9.5035453e-01 9.5498315e-01 7.0776547e-01 2.1812146e+00 2.2081369e+00 3.7427929e-01 1.1335961e+00 7.7885297e-01 2.0291151e+00 3.2352160e-01 1.0661822e+00 1.3165513e+00 3.7598397e-01 5.3588338e-01 8.2305664e-01 1.1437730e+00 1.4415965e+00 2.0902718e+00 8.7848692e-01 3.2352160e-01 7.0479928e-01 1.6865203e+00 1.1904611e+00 7.5791688e-01 5.5492130e-01 8.9207714e-01 1.0805899e+00 9.6271042e-01 5.7867728e-01 1.2256881e+00 1.2481462e+00 8.8358844e-01 4.0147421e-01 6.4405773e-01 1.0887986e+00 5.9426792e-01 4.4651726e-01 5.4292906e-01 7.0437330e-01 7.0776547e-01 3.2816937e-01 1.2134101e+00 9.8820253e-01 1.0733200e+00 8.1099042e-01 4.9857388e-01 7.2172678e-01 6.5223271e-01 6.3808075e-01 5.3665999e-01 6.9369532e-01 8.2305664e-01 6.2482915e-01 2.5251796e-01 7.1799256e-01 1.5895397e+00 6.2205176e-01 5.7257017e-01 5.6769031e-01 4.0438741e-01 1.7935777e+00 6.4755655e-01 1.6267976e+00 7.4777660e-01 1.4807336e+00 9.7270522e-01 1.3173487e+00 2.1839601e+00 1.2277129e+00 1.7938033e+00 1.1948932e+00 1.8448199e+00 8.7383925e-01 8.2305664e-01 1.1418127e+00 8.4591037e-01 1.2153720e+00 1.1640914e+00 9.1163729e-01 2.3638833e+00 2.4815883e+00 6.3861009e-01 1.3946921e+00 8.5434758e-01 2.2902807e+00 6.1151102e-01 1.2452704e+00 1.5325394e+00 6.0121055e-01 6.1092863e-01 1.1228379e+00 1.3752705e+00 1.7103060e+00 2.2560685e+00 1.1879078e+00 4.5470518e-01 9.0454394e-01 1.9729066e+00 1.3650300e+00 9.0521488e-01 6.0670504e-01 1.1454006e+00 1.3674559e+00 1.2262704e+00 7.4777660e-01 1.4820085e+00 1.4947429e+00 1.1729895e+00 7.3145860e-01 8.7720955e-01 1.2163831e+00 6.5633874e-01 2.1845981e-01 5.6769031e-01 7.4777660e-01 4.2538717e-01 9.5099818e-01 9.7994716e-01 1.0119857e+00 6.5223271e-01 8.3888121e-01 1.0038051e+00 5.9426792e-01 4.6472023e-01 6.0121055e-01 8.0326782e-01 9.2836103e-01 9.0876485e-01 3.7598397e-01 6.3861009e-01 1.5602029e+00 8.0326782e-01 7.0129382e-01 7.0043186e-01 2.0000000e-01 1.6208239e+00 7.0437330e-01 1.8619092e+00 9.6271042e-01 1.6849072e+00 1.3188999e+00 1.5860263e+00 2.4084158e+00 1.5142414e+00 2.0543079e+00 1.5230852e+00 1.9980352e+00 9.4375082e-01 1.0588560e+00 1.3035649e+00 1.0035600e+00 1.2499342e+00 1.2459608e+00 1.2225634e+00 2.5586145e+00 2.7210925e+00 8.9366705e-01 1.5500052e+00 1.0014633e+00 2.5139485e+00 6.9369532e-01 1.4790710e+00 1.7580510e+00 6.2660376e-01 7.0429250e-01 1.3804167e+00 1.5625881e+00 1.8966943e+00 2.3518757e+00 1.4139741e+00 8.0358695e-01 1.3075101e+00 2.0455018e+00 1.5153654e+00 1.2234738e+00 6.6539428e-01 1.2342162e+00 1.5049644e+00 1.1591754e+00 9.6271042e-01 1.7107332e+00 1.6328100e+00 1.1880428e+00 8.3812833e-01 1.0106392e+00 1.3267389e+00 8.9767734e-01 4.2538717e-01 6.2024833e-01 6.0181382e-01 1.1431021e+00 1.1948932e+00 1.2269747e+00 8.6361309e-01 8.2552685e-01 1.2002640e+00 6.5223271e-01 3.0811765e-01 7.1462831e-01 1.0067784e+00 1.1396406e+00 1.1147518e+00 5.0817745e-01 8.5335130e-01 1.7711504e+00 1.0085601e+00 9.0454394e-01 9.0277242e-01 4.0438741e-01 1.8140813e+00 9.1075311e-01 1.7412567e+00 9.8054887e-01 1.5527694e+00 1.2155004e+00 1.4692412e+00 2.2702600e+00 1.7126039e+00 1.9279661e+00 1.4238090e+00 1.8539569e+00 8.1558458e-01 9.5035453e-01 1.1763980e+00 1.0621081e+00 1.2047214e+00 1.1205013e+00 1.1134787e+00 2.4108292e+00 2.5851693e+00 9.6095130e-01 1.4178113e+00 1.0879524e+00 2.3751496e+00 6.0900723e-01 1.3570688e+00 1.6277043e+00 5.7015910e-01 6.6491075e-01 1.2652657e+00 1.4292566e+00 1.7566567e+00 2.1846001e+00 1.2961380e+00 7.1840099e-01 1.2329148e+00 1.8795815e+00 1.3897316e+00 1.1149070e+00 6.8757066e-01 1.0960883e+00 1.3785366e+00 1.0168833e+00 9.8054887e-01 1.5871961e+00 1.5043071e+00 1.0597541e+00 7.7033318e-01 8.8861541e-01 1.2058675e+00 8.9134001e-01 3.4583729e-01 8.1130291e-01 1.5090287e+00 1.4644753e+00 1.5150043e+00 1.1847335e+00 8.1385214e-01 1.4041085e+00 8.9917007e-01 3.0811765e-01 6.6539428e-01 1.2643026e+00 1.3836712e+00 1.3112758e+00 7.0784540e-01 1.1353806e+00 2.0777059e+00 1.2396136e+00 1.1498269e+00 1.1474460e+00 6.9006418e-01 2.1775976e+00 1.1752673e+00 1.4613032e+00 1.0391247e+00 1.1810170e+00 8.7504951e-01 1.1390131e+00 1.8670836e+00 1.9048338e+00 1.5205305e+00 1.0228981e+00 1.5676403e+00 6.7975091e-01 6.6539428e-01 8.7175869e-01 1.1533602e+00 1.2459608e+00 9.7730901e-01 7.5082357e-01 2.0536508e+00 2.1838261e+00 8.9095811e-01 1.1306949e+00 1.2394907e+00 1.9665910e+00 5.6769031e-01 1.0425476e+00 1.2324706e+00 6.4704320e-01 7.3851529e-01 9.5476489e-01 1.0198386e+00 1.3510699e+00 1.8411319e+00 1.0100718e+00 5.2942799e-01 9.3801395e-01 1.5112621e+00 1.2002762e+00 7.7763126e-01 8.2899253e-01 8.2305664e-01 1.1377990e+00 9.1663180e-01 1.0391247e+00 1.2653669e+00 1.2757312e+00 9.2288144e-01 6.4405773e-01 6.6827038e-01 1.0851476e+00 9.3048953e-01 7.7074935e-01 1.6569692e+00 1.5391185e+00 1.6134578e+00 1.2794849e+00 7.1504098e-01 1.3197776e+00 7.9613242e-01 3.2586371e-01 8.6165877e-01 1.2653669e+00 1.4028652e+00 1.2701139e+00 6.6384020e-01 1.2172454e+00 2.1489775e+00 1.2262704e+00 1.1578646e+00 1.1452867e+00 7.9613242e-01 2.2808589e+00 1.1959482e+00 1.1500393e+00 9.1075311e-01 9.3999899e-01 6.4806901e-01 8.5434758e-01 1.6806723e+00 1.8184542e+00 1.3334814e+00 8.5205778e-01 1.2702636e+00 3.4583729e-01 4.3456114e-01 5.6700421e-01 1.0389435e+00 1.0122141e+00 6.4290921e-01 5.0905001e-01 1.8419636e+00 1.9902374e+00 9.3801395e-01 8.1810461e-01 1.1069580e+00 1.7940242e+00 4.4651726e-01 7.4740267e-01 1.0346741e+00 5.1691876e-01 6.0121055e-01 6.6827038e-01 8.5205778e-01 1.1776640e+00 1.6778021e+00 7.0776547e-01 4.2667565e-01 7.8695083e-01 1.3400806e+00 8.6165877e-01 5.3022554e-01 7.0437330e-01 5.0592043e-01 8.1273630e-01 6.0670504e-01 9.1075311e-01 9.7270522e-01 9.4352681e-01 6.0551856e-01 5.7257017e-01 3.4378533e-01 7.5705927e-01 8.0064372e-01 1.0450018e+00 8.4812820e-01 9.3847194e-01 6.2988288e-01 6.0611244e-01 6.0060595e-01 5.0090417e-01 7.0784540e-01 6.2538346e-01 5.0592043e-01 6.6932542e-01 5.5492130e-01 1.5422108e-01 5.6075294e-01 1.4235605e+00 4.6472023e-01 4.2418962e-01 3.8776762e-01 2.8192292e-01 1.5978583e+00 4.5581864e-01 1.6256459e+00 6.5633874e-01 1.5986180e+00 1.1106412e+00 1.3708966e+00 2.3519748e+00 1.1147518e+00 1.9798165e+00 1.3654173e+00 1.8856245e+00 7.7033318e-01 8.5335130e-01 1.1771643e+00 6.8076724e-01 9.7270522e-01 1.0165138e+00 1.0391247e+00 2.5081056e+00 2.6437138e+00 7.6716823e-01 1.4120836e+00 6.1947990e-01 2.4692682e+00 4.8927739e-01 1.3077572e+00 1.7030709e+00 3.8934542e-01 4.4651726e-01 1.1594648e+00 1.5551984e+00 1.8760773e+00 2.3977345e+00 1.1868139e+00 6.2024833e-01 1.1056650e+00 2.0821572e+00 1.2794849e+00 1.0244319e+00 3.7427929e-01 1.1646003e+00 1.3139135e+00 1.1119327e+00 6.5633874e-01 1.5344007e+00 1.4333755e+00 1.0386594e+00 6.3735887e-01 8.2372435e-01 1.0901359e+00 6.2081167e-01 3.4583729e-01 2.8192292e-01 4.1586001e-01 1.6237100e+00 1.0540105e+00 1.1816401e+00 1.4110536e+00 9.8450810e-01 6.6432544e-01 5.3665999e-01 9.0454394e-01 1.1389644e+00 5.0905001e-01 7.1799256e-01 7.1504098e-01 7.3813096e-01 7.2783368e-01 8.7021234e-01 6.9006418e-01 6.2482915e-01 2.6619364e+00 1.6754669e+00 2.5821869e+00 2.1421834e+00 2.4107926e+00 3.3209792e+00 1.2036484e+00 2.9531363e+00 2.3720162e+00 2.8824828e+00 1.7671769e+00 1.8842354e+00 2.1714345e+00 1.6176764e+00 1.8723516e+00 2.0134817e+00 2.0676751e+00 3.4808493e+00 3.6264553e+00 1.5230852e+00 2.4135751e+00 1.5351194e+00 3.4281547e+00 1.4948868e+00 2.3378347e+00 2.6680945e+00 1.3977032e+00 1.4832928e+00 2.1976523e+00 2.4795532e+00 2.8157449e+00 3.2956170e+00 2.2214438e+00 1.6336229e+00 2.1064881e+00 2.9775369e+00 2.2994849e+00 2.0603946e+00 1.3916739e+00 2.1189748e+00 2.3204945e+00 1.9672068e+00 1.6754669e+00 2.5635110e+00 2.4436082e+00 1.9753271e+00 1.6077195e+00 1.8374244e+00 2.0981613e+00 1.6585806e+00 1.2418578e-01 3.7598397e-01 1.3405045e+00 8.3812833e-01 1.1437669e+00 1.3915412e+00 8.9095811e-01 6.2538346e-01 2.5251796e-01 6.0611244e-01 9.6791960e-01 3.4583729e-01 6.2205176e-01 4.5581864e-01 6.5223271e-01 5.7867728e-01 8.2619017e-01 8.2654509e-01 4.6472023e-01 2.4061696e+00 1.3868130e+00 2.3985329e+00 1.8751958e+00 2.1591630e+00 3.1397173e+00 8.9852394e-01 2.7599154e+00 2.1335771e+00 2.7193241e+00 1.6021202e+00 1.6415861e+00 1.9851797e+00 1.3336069e+00 1.6224878e+00 1.8082080e+00 1.8350829e+00 3.3292261e+00 3.4297053e+00 1.2340567e+00 2.2298076e+00 1.2654843e+00 3.2489933e+00 1.2770118e+00 2.1337366e+00 2.4988032e+00 1.1786349e+00 1.2545301e+00 1.9393053e+00 2.3288114e+00 2.6536325e+00 3.2010862e+00 1.9648876e+00 1.3964978e+00 1.8188234e+00 2.8588023e+00 2.0766308e+00 1.8216743e+00 1.1634384e+00 1.9698860e+00 2.1147926e+00 1.8660999e+00 1.3868130e+00 2.3472906e+00 2.2417376e+00 1.8131734e+00 1.3752391e+00 1.6372749e+00 1.8856245e+00 1.3922071e+00 4.0176783e-01 1.4467170e+00 9.3049742e-01 1.2002762e+00 1.4411886e+00 9.4375082e-01 6.6432544e-01 3.7427929e-01 7.0784540e-01 1.0466623e+00 4.0176783e-01 5.6700421e-01 5.5492130e-01 6.9728513e-01 6.4405773e-01 8.7170815e-01 7.3535471e-01 5.3309112e-01 2.5193321e+00 1.5039793e+00 2.4895662e+00 1.9791576e+00 2.2673478e+00 3.2268480e+00 1.0014633e+00 2.8466240e+00 2.2303173e+00 2.8133325e+00 1.6962564e+00 1.7458338e+00 2.0805039e+00 1.4552205e+00 1.7454671e+00 1.9165907e+00 1.9332869e+00 3.4131779e+00 3.5208177e+00 1.3379696e+00 2.3275025e+00 1.3866044e+00 3.3340853e+00 1.3784233e+00 2.2317775e+00 2.5822073e+00 1.2828332e+00 1.3595018e+00 2.0485193e+00 2.4057315e+00 2.7355566e+00 3.2722484e+00 2.0762069e+00 1.4905436e+00 1.9191337e+00 2.9375895e+00 2.1864773e+00 1.9213461e+00 1.2702636e+00 2.0582667e+00 2.2206505e+00 1.9521784e+00 1.5039793e+00 2.4497583e+00 2.3480580e+00 1.9124077e+00 1.4817438e+00 1.7372199e+00 1.9937367e+00 1.5016471e+00 1.2122249e+00 6.7975091e-01 8.4050231e-01 1.0796583e+00 6.6539428e-01 3.4583729e-01 3.2816937e-01 5.2942799e-01 7.3145860e-01 1.2418578e-01 9.1163729e-01 3.2586371e-01 3.7427929e-01 3.2816937e-01 5.0592043e-01 1.0122141e+00 2.1845981e-01 2.2481791e+00 1.2631020e+00 2.1874158e+00 1.7295419e+00 1.9965608e+00 2.9333950e+00 1.0072663e+00 2.5632712e+00 1.9670002e+00 2.4858980e+00 1.3655398e+00 1.4724669e+00 1.7716601e+00 1.2125198e+00 1.4903113e+00 1.6105641e+00 1.6572339e+00 3.0940465e+00 3.2344170e+00 1.1332978e+00 2.0129643e+00 1.1341579e+00 3.0445772e+00 1.0864449e+00 1.9287276e+00 2.2802541e+00 9.8820253e-01 1.0688498e+00 1.7838044e+00 2.1039999e+00 2.4365934e+00 2.9349538e+00 1.8084630e+00 1.2266837e+00 1.7026843e+00 2.6138892e+00 1.8911946e+00 1.6476803e+00 9.7949166e-01 1.7305789e+00 1.9168750e+00 1.6065247e+00 1.2631020e+00 2.1541966e+00 2.0395401e+00 1.5896248e+00 1.1996741e+00 1.4306494e+00 1.6928004e+00 1.2436109e+00 7.5791688e-01 8.1242502e-01 7.6625946e-01 7.5976039e-01 1.0289803e+00 1.1332978e+00 7.9613242e-01 5.3665999e-01 1.1149070e+00 1.9007091e+00 9.2836103e-01 9.3610001e-01 9.1858284e-01 8.1638392e-01 2.1493214e+00 1.0132664e+00 1.1680362e+00 3.2352160e-01 1.2372418e+00 5.4292906e-01 8.7170815e-01 1.9366254e+00 1.1486378e+00 1.5564198e+00 8.7420176e-01 1.5682049e+00 6.6491075e-01 4.5470518e-01 8.8358844e-01 4.5581864e-01 8.0326782e-01 7.9878917e-01 5.9426792e-01 2.1460410e+00 2.1931311e+00 5.0180477e-01 1.0950112e+00 5.0592043e-01 2.0545952e+00 3.4378533e-01 9.3923979e-01 1.3512935e+00 3.4583729e-01 3.4583729e-01 6.6539428e-01 1.2670555e+00 1.5369942e+00 2.1465859e+00 7.2526325e-01 3.0546431e-01 5.0991930e-01 1.8206746e+00 9.8054887e-01 5.7015910e-01 3.8776762e-01 9.6664346e-01 9.9089002e-01 1.0262547e+00 3.2352160e-01 1.1127329e+00 1.1166017e+00 8.7848692e-01 3.8934542e-01 5.8914551e-01 8.8062848e-01 3.2586371e-01 6.4755655e-01 1.3011270e+00 1.0122141e+00 4.2538717e-01 6.2660376e-01 4.4651726e-01 7.0086313e-01 6.3735887e-01 1.2926374e+00 4.0176783e-01 4.2288438e-01 3.8934542e-01 8.0619006e-01 1.5230852e+00 4.6472023e-01 1.6933635e+00 7.0233835e-01 1.9584922e+00 1.2594846e+00 1.5411691e+00 2.6785768e+00 6.2605182e-01 2.3003744e+00 1.6284481e+00 2.1882128e+00 1.1729895e+00 1.1500393e+00 1.5577059e+00 7.1881659e-01 9.8985697e-01 1.2389598e+00 1.3108618e+00 2.8217641e+00 2.9357847e+00 9.3026633e-01 1.7457596e+00 5.7867728e-01 2.7994225e+00 9.3610001e-01 1.5801828e+00 2.0692197e+00 8.2384013e-01 7.4740267e-01 1.3410314e+00 1.9783833e+00 2.2683159e+00 2.8062463e+00 1.3610783e+00 9.7249562e-01 1.1868139e+00 2.5244698e+00 1.3852951e+00 1.2460824e+00 6.3808075e-01 1.6077195e+00 1.5873000e+00 1.5826476e+00 7.0233835e-01 1.7831878e+00 1.6667819e+00 1.4276261e+00 9.9519977e-01 1.1984588e+00 1.1903343e+00 7.0386584e-01 7.1840099e-01 1.1107977e+00 5.8624446e-01 9.8495853e-01 8.7504951e-01 4.1586001e-01 8.7720955e-01 1.5824669e+00 7.5976039e-01 5.5160819e-01 5.7741073e-01 5.4292906e-01 1.6736318e+00 6.7975091e-01 1.5883552e+00 8.2552685e-01 1.5948732e+00 1.1332978e+00 1.3595997e+00 2.3503762e+00 1.2554784e+00 1.9862884e+00 1.4596621e+00 1.8378727e+00 7.2852070e-01 9.6204649e-01 1.1697902e+00 9.6664346e-01 9.6271042e-01 9.5676647e-01 1.0496979e+00 2.4751922e+00 2.6546397e+00 1.2224367e+00 1.3843268e+00 7.2343175e-01 2.4751922e+00 7.5082357e-01 1.2832075e+00 1.7000773e+00 6.2988288e-01 5.0592043e-01 1.1833351e+00 1.5613251e+00 1.8886923e+00 2.3645560e+00 1.2016233e+00 7.5791688e-01 1.2125198e+00 2.0748074e+00 1.2155370e+00 1.0244319e+00 4.5470518e-01 1.1485394e+00 1.2770118e+00 1.0720678e+00 8.2552685e-01 1.5113992e+00 1.3835368e+00 1.0035600e+00 9.5571254e-01 8.2234151e-01 1.0120221e+00 6.5223271e-01 8.3888121e-01 1.1486378e+00 1.2994764e+00 1.2307737e+00 6.0181382e-01 1.0483827e+00 1.9867752e+00 1.1408504e+00 1.0391247e+00 1.0361698e+00 5.7867728e-01 2.0669733e+00 1.0646687e+00 1.4623898e+00 9.5866719e-01 1.2497790e+00 9.3048953e-01 1.1765359e+00 1.9666356e+00 1.8175297e+00 1.6242657e+00 1.1520347e+00 1.5603665e+00 5.7324170e-01 7.0233835e-01 8.8887100e-01 1.0919404e+00 1.1355826e+00 8.9712482e-01 8.1385214e-01 2.1052360e+00 2.2845234e+00 1.0166932e+00 1.1341579e+00 1.1332978e+00 2.0738150e+00 5.3309112e-01 1.0588560e+00 1.3224963e+00 5.5492130e-01 6.2538346e-01 9.8450810e-01 1.1271488e+00 1.4561933e+00 1.8911946e+00 1.0230441e+00 5.2574978e-01 1.0056742e+00 1.5916843e+00 1.1355826e+00 8.2105460e-01 7.1504098e-01 8.1527569e-01 1.1176720e+00 8.2899253e-01 9.5866719e-01 1.2943100e+00 1.2429818e+00 8.5205778e-01 7.0233835e-01 6.2660376e-01 9.8054887e-01 8.3649708e-01 8.7822463e-01 8.2899253e-01 8.1099042e-01 7.0826681e-01 5.8914551e-01 1.5042268e+00 7.3813096e-01 8.1558458e-01 7.4855857e-01 6.0121055e-01 1.6260946e+00 7.0386584e-01 1.8605327e+00 8.8503502e-01 1.6493191e+00 1.2601890e+00 1.5390703e+00 2.3592515e+00 1.4088394e+00 1.9940473e+00 1.4245508e+00 2.0716002e+00 1.1004436e+00 9.8820253e-01 1.2927814e+00 9.0056222e-01 1.2208301e+00 1.3194807e+00 1.1996741e+00 2.6153308e+00 2.6539963e+00 6.2538346e-01 1.5687169e+00 9.5271386e-01 2.4562038e+00 6.6491075e-01 1.5249255e+00 1.7538274e+00 6.6539428e-01 8.2619017e-01 1.3131724e+00 1.5409345e+00 1.8470010e+00 2.4533073e+00 1.3504603e+00 7.7039952e-01 1.2057554e+00 2.0352149e+00 1.6006330e+00 1.2331989e+00 8.0660588e-01 1.2747177e+00 1.5040391e+00 1.2426449e+00 8.8503502e-01 1.7019078e+00 1.6700310e+00 1.2144845e+00 7.4855857e-01 1.0401425e+00 1.4600567e+00 9.3296062e-01 5.0180477e-01 4.4651726e-01 6.2149089e-01 4.1586001e-01 1.0078327e+00 3.0275928e-01 1.4096146e-01 1.4096146e-01 6.0611244e-01 1.1536782e+00 2.0656129e-01 2.0491051e+00 1.0646687e+00 2.0979729e+00 1.5528443e+00 1.8279176e+00 2.8469870e+00 8.2234151e-01 2.4692682e+00 1.8399871e+00 2.3632803e+00 1.2493717e+00 1.3312249e+00 1.6756749e+00 1.0425476e+00 1.3092012e+00 1.4505265e+00 1.5123788e+00 2.9884772e+00 3.1361386e+00 1.0755693e+00 1.9017004e+00 9.3801395e-01 2.9635613e+00 9.8054887e-01 1.7833384e+00 2.1970231e+00 8.6513410e-01 8.9742724e-01 1.6159903e+00 2.0524973e+00 2.3760856e+00 2.8811560e+00 1.6399646e+00 1.0934620e+00 1.5205305e+00 2.5867433e+00 1.6928004e+00 1.4836711e+00 7.9580667e-01 1.6659943e+00 1.7839298e+00 1.5792930e+00 1.0646687e+00 2.0113485e+00 1.8901379e+00 1.5067717e+00 1.0950112e+00 1.3129189e+00 1.4875372e+00 1.0389435e+00 4.0293660e-01 8.0499049e-01 3.0546431e-01 7.8197925e-01 2.5251796e-01 5.1691876e-01 4.2538717e-01 7.4740267e-01 1.0181000e+00 3.2586371e-01 2.1717162e+00 1.1533602e+00 2.2234347e+00 1.6690840e+00 1.9433381e+00 2.9713636e+00 7.2486328e-01 2.5929835e+00 1.9489982e+00 2.5241649e+00 1.4089364e+00 1.4425304e+00 1.8012344e+00 1.0919712e+00 1.3726860e+00 1.5830057e+00 1.6408468e+00 3.1546522e+00 3.2544456e+00 1.0406064e+00 2.0352149e+00 1.0168833e+00 3.0859269e+00 1.0901359e+00 1.9325796e+00 2.3348454e+00 9.8054887e-01 1.0379132e+00 1.7250039e+00 2.1825260e+00 2.4995667e+00 3.0529994e+00 1.7458338e+00 1.2167151e+00 1.6214915e+00 2.7108297e+00 1.8418195e+00 1.6195190e+00 9.3847194e-01 1.7995863e+00 1.9034198e+00 1.7022897e+00 1.1533602e+00 2.1413027e+00 2.0233319e+00 1.6211869e+00 1.1770266e+00 1.4411886e+00 1.6502968e+00 1.1644030e+00 6.5633874e-01 4.4417983e-01 1.1332978e+00 2.1845981e-01 4.2538717e-01 3.4583729e-01 7.1504098e-01 1.4080793e+00 3.4583729e-01 1.8866180e+00 8.7848692e-01 1.9819543e+00 1.3312249e+00 1.6523803e+00 2.6988671e+00 6.9006418e-01 2.3100474e+00 1.6455737e+00 2.2934334e+00 1.2426449e+00 1.1905954e+00 1.5932297e+00 8.9095811e-01 1.2681309e+00 1.4065584e+00 1.3487634e+00 2.8835141e+00 2.9705897e+00 7.3805807e-01 1.8205354e+00 8.5462626e-01 2.8117234e+00 9.3049742e-01 1.6679957e+00 2.0758969e+00 8.4050231e-01 8.3060013e-01 1.4419145e+00 1.9521697e+00 2.2599493e+00 2.8310619e+00 1.4807336e+00 9.4558103e-01 1.2404967e+00 2.5235709e+00 1.6070713e+00 1.3102444e+00 7.5705927e-01 1.6281130e+00 1.7021627e+00 1.6240596e+00 8.7848692e-01 1.8790831e+00 1.8155245e+00 1.5040391e+00 1.0014633e+00 1.2481462e+00 1.4334902e+00 8.6165877e-01 6.6827038e-01 1.5475692e+00 5.8914551e-01 5.0503591e-01 4.9857388e-01 3.0811765e-01 1.7160413e+00 5.7324170e-01 1.5795964e+00 6.5648056e-01 1.4967461e+00 1.0182895e+00 1.3034549e+00 2.2380042e+00 1.2266837e+00 1.8619092e+00 1.2701139e+00 1.8007564e+00 7.2852070e-01 7.9016429e-01 1.0989735e+00 7.5705927e-01 1.0406064e+00 1.0184370e+00 9.3999899e-01 2.3886514e+00 2.5357185e+00 8.2684479e-01 1.3424112e+00 7.0776547e-01 2.3522207e+00 4.8927739e-01 1.2205493e+00 1.5832517e+00 4.2667565e-01 4.4417983e-01 1.0974061e+00 1.4319225e+00 1.7587110e+00 2.2711652e+00 1.1390131e+00 5.1691876e-01 1.0163549e+00 1.9782498e+00 1.2532075e+00 9.2859317e-01 4.1449626e-01 1.0852663e+00 1.2786117e+00 1.0887986e+00 6.5648056e-01 1.4596621e+00 1.3991741e+00 1.0313560e+00 6.6932542e-01 7.7553525e-01 1.0741917e+00 5.7257017e-01 9.4558103e-01 2.5651975e-01 4.1449626e-01 3.2816937e-01 4.8135521e-01 1.0908017e+00 2.1845981e-01 2.1701312e+00 1.1752673e+00 2.1084262e+00 1.6352583e+00 1.9101184e+00 2.8518881e+00 9.7825559e-01 2.4781934e+00 1.8745369e+00 2.4235816e+00 1.3078976e+00 1.3842113e+00 1.6965018e+00 1.1329323e+00 1.4319225e+00 1.5496439e+00 1.5691346e+00 3.0266197e+00 3.1503439e+00 1.0244319e+00 1.9425540e+00 1.0627606e+00 2.9623467e+00 1.0056742e+00 1.8539828e+00 2.2026387e+00 9.1163729e-01 9.9475949e-01 1.6952454e+00 2.0275673e+00 2.3581240e+00 2.8793190e+00 1.7227544e+00 1.1332978e+00 1.6029963e+00 2.5482247e+00 1.8278913e+00 1.5611067e+00 9.1163729e-01 1.6653066e+00 1.8462692e+00 1.5634147e+00 1.1752673e+00 2.0757295e+00 1.9739212e+00 1.5320003e+00 1.1179743e+00 1.3567326e+00 1.6362950e+00 1.1594648e+00 9.9475949e-01 1.1004436e+00 1.0720678e+00 1.4108494e+00 3.2816937e-01 9.8054887e-01 2.9240179e+00 1.9013501e+00 3.0016960e+00 2.4403742e+00 2.7185134e+00 3.7481490e+00 1.2643026e+00 3.3676733e+00 2.7249658e+00 3.2951180e+00 2.1701459e+00 2.2231652e+00 2.5778372e+00 1.8193838e+00 2.0594742e+00 2.3383666e+00 2.4210417e+00 3.9277558e+00 4.0319248e+00 1.8011138e+00 2.8105150e+00 1.7324239e+00 3.8595400e+00 1.8657887e+00 2.7098209e+00 3.1083486e+00 1.7551534e+00 1.8090259e+00 2.5002193e+00 2.9463843e+00 3.2688068e+00 3.8087204e+00 2.5182043e+00 1.9933741e+00 2.3692479e+00 3.4705738e+00 2.5885717e+00 2.3959721e+00 1.7005893e+00 2.5655126e+00 2.6734142e+00 2.4311441e+00 1.9013501e+00 2.9205331e+00 2.7876433e+00 2.3735872e+00 1.9516947e+00 2.2170194e+00 2.3879674e+00 1.9213461e+00 3.0546431e-01 2.0656129e-01 6.0611244e-01 1.2246352e+00 1.4096146e-01 1.9777274e+00 9.7249562e-01 2.0297383e+00 1.4616539e+00 1.7458338e+00 2.7737198e+00 7.5082357e-01 2.3931826e+00 1.7478866e+00 2.3213742e+00 1.2131545e+00 1.2498134e+00 1.6130724e+00 9.3824087e-01 1.2565757e+00 1.4029855e+00 1.4325768e+00 2.9414941e+00 3.0577671e+00 8.7720955e-01 1.8438146e+00 8.6956871e-01 2.8895427e+00 9.1310225e-01 1.7228354e+00 2.1321061e+00 8.0499049e-01 8.3345577e-01 1.5318874e+00 1.9898963e+00 2.3090270e+00 2.8491218e+00 1.5587730e+00 1.0122141e+00 1.4162017e+00 2.5326059e+00 1.6463627e+00 1.4047678e+00 7.3805807e-01 1.6165635e+00 1.7228488e+00 1.5520745e+00 9.7249562e-01 1.9420274e+00 1.8372522e+00 1.4628493e+00 1.0030700e+00 1.2523175e+00 1.4531349e+00 9.5571254e-01 1.2418578e-01 5.0270183e-01 1.2603076e+00 2.1269358e-01 1.9932786e+00 1.0168833e+00 1.9946994e+00 1.4557537e+00 1.7495699e+00 2.7337358e+00 9.0575661e-01 2.3519748e+00 1.7324239e+00 2.2796281e+00 1.1801240e+00 1.2450709e+00 1.5872286e+00 1.0267435e+00 1.3336069e+00 1.4152303e+00 1.4096199e+00 2.8778917e+00 3.0268604e+00 1.0067464e+00 1.8215944e+00 9.3824087e-01 2.8471683e+00 9.0658670e-01 1.6933635e+00 2.0801243e+00 8.0758367e-01 8.3783744e-01 1.5390703e+00 1.9310038e+00 2.2599493e+00 2.7651778e+00 1.5728839e+00 9.7949166e-01 1.4165336e+00 2.4822593e+00 1.6510537e+00 1.3842113e+00 7.5755387e-01 1.5773217e+00 1.7253276e+00 1.5255331e+00 1.0168833e+00 1.9287244e+00 1.8366596e+00 1.4599710e+00 1.0339865e+00 1.2378278e+00 1.4537266e+00 9.7249562e-01 5.0090417e-01 1.2507669e+00 1.2418578e-01 1.9589833e+00 9.7270522e-01 1.9792779e+00 1.4437673e+00 1.7230625e+00 2.7260686e+00 8.6012420e-01 2.3478326e+00 1.7190893e+00 2.2590861e+00 1.1454006e+00 1.2174316e+00 1.5614941e+00 9.5476489e-01 1.2555979e+00 1.3635198e+00 1.3969297e+00 2.8759951e+00 3.0161959e+00 9.4558103e-01 1.7925890e+00 8.6983677e-01 2.8416706e+00 8.6513410e-01 1.6742876e+00 2.0756986e+00 7.5871717e-01 7.9613242e-01 1.5107481e+00 1.9286915e+00 2.2531942e+00 2.7669732e+00 1.5384446e+00 9.7270522e-01 1.4111029e+00 2.4671050e+00 1.6105641e+00 1.3717027e+00 7.0429250e-01 1.5517600e+00 1.6837214e+00 1.4807336e+00 9.7270522e-01 1.9032219e+00 1.7953587e+00 1.4097072e+00 9.7855477e-01 1.2036484e+00 1.4110536e+00 9.4309624e-01 1.5090287e+00 5.0905001e-01 1.8619092e+00 9.1163729e-01 1.7230625e+00 1.3188999e+00 1.5883552e+00 2.4588872e+00 1.3193952e+00 2.0946464e+00 1.5338492e+00 2.0321740e+00 9.5099818e-01 1.0604511e+00 1.3277861e+00 9.3296062e-01 1.2221471e+00 1.2470767e+00 1.2266837e+00 2.6106370e+00 2.7667028e+00 8.7420176e-01 1.5746612e+00 8.9852394e-01 2.5679581e+00 6.9369532e-01 1.4905436e+00 1.8028753e+00 6.2149089e-01 6.9006418e-01 1.3813076e+00 1.6199747e+00 1.9552274e+00 2.4344864e+00 1.4148192e+00 8.0358695e-01 1.3039319e+00 2.1287551e+00 1.5153654e+00 1.2246352e+00 6.2660376e-01 1.2741904e+00 1.5160122e+00 1.2047214e+00 9.1163729e-01 1.7242097e+00 1.6420607e+00 1.2064640e+00 8.3812833e-01 1.0168833e+00 1.3257654e+00 8.6137722e-01 1.1533602e+00 3.1382691e+00 2.1485328e+00 3.1786790e+00 2.6799312e+00 2.9354140e+00 3.9353144e+00 1.5252485e+00 3.5658557e+00 2.9475994e+00 3.4455976e+00 2.3167786e+00 2.4320363e+00 2.7459122e+00 2.0598189e+00 2.2501104e+00 2.5013206e+00 2.6321552e+00 4.0910078e+00 4.2293986e+00 2.0521052e+00 2.9731112e+00 1.9619929e+00 4.0491656e+00 2.0481101e+00 2.8946126e+00 3.2860707e+00 1.9342059e+00 2.0025214e+00 2.7210925e+00 3.1148548e+00 3.4424959e+00 3.9360563e+00 2.7333517e+00 2.2078200e+00 2.6383936e+00 3.6047462e+00 2.7713578e+00 2.6121617e+00 1.8925840e+00 2.7075477e+00 2.8419886e+00 2.5215069e+00 2.1485328e+00 3.1102248e+00 2.9517129e+00 2.5041493e+00 2.1435335e+00 2.3887866e+00 2.5633372e+00 2.1544995e+00 2.0467316e+00 1.0576043e+00 2.0528819e+00 1.5379283e+00 1.8096161e+00 2.8029161e+00 8.6012420e-01 2.4272793e+00 1.8028753e+00 2.3372930e+00 1.2144845e+00 1.2985682e+00 1.6312555e+00 1.0166932e+00 1.3073038e+00 1.4333755e+00 1.4843487e+00 2.9588514e+00 3.0950947e+00 9.7949166e-01 1.8647706e+00 9.3615100e-01 2.9181741e+00 9.3049742e-01 1.7594421e+00 2.1522124e+00 8.2342214e-01 8.7720955e-01 1.5965946e+00 1.9973159e+00 2.3235032e+00 2.8379387e+00 1.6211988e+00 1.0588560e+00 1.5076049e+00 2.5254157e+00 1.6945041e+00 1.4637418e+00 7.8197925e-01 1.6130724e+00 1.7539916e+00 1.5193574e+00 1.0576043e+00 1.9849009e+00 1.8691652e+00 1.4607586e+00 1.0375119e+00 1.2741904e+00 1.4947429e+00 1.0361698e+00 1.0621081e+00 8.3649708e-01 7.6590510e-01 4.0176783e-01 1.3455136e+00 1.8827665e+00 1.1093572e+00 9.5676647e-01 9.0852141e-01 9.4309624e-01 8.9852394e-01 6.8076724e-01 1.2002762e+00 9.7825559e-01 7.0479928e-01 7.8197925e-01 1.4637418e+00 1.5390703e+00 1.4628493e+00 6.2538346e-01 1.2208301e+00 1.4754770e+00 1.2162549e+00 5.2574978e-01 1.0104465e+00 1.2832075e+00 1.1810170e+00 6.1947990e-01 1.1242402e+00 1.1718516e+00 1.6295015e+00 5.8914551e-01 1.2063335e+00 1.1879206e+00 1.4041085e+00 4.0293660e-01 7.7074935e-01 1.2709820e+00 7.7869083e-01 5.0592043e-01 9.7441804e-01 1.0621081e+00 5.0991930e-01 4.4417983e-01 8.3888121e-01 1.1770266e+00 8.6361309e-01 6.0670504e-01 1.0324775e+00 1.3844611e+00 6.2660376e-01 8.8695363e-01 2.0692197e+00 9.7441804e-01 1.6995747e+00 1.0122141e+00 1.6372749e+00 7.6752131e-01 6.0551856e-01 1.0244319e+00 2.1845981e-01 5.0090417e-01 7.2852070e-01 7.4777660e-01 2.2645802e+00 2.3019759e+00 5.7324170e-01 1.1833351e+00 2.5651975e-01 2.1907335e+00 5.0905001e-01 1.0330459e+00 1.5124582e+00 4.4651726e-01 3.8934542e-01 6.9369532e-01 1.4517959e+00 1.7036156e+00 2.3021295e+00 7.0429250e-01 5.6700421e-01 6.3977563e-01 1.9782093e+00 8.7229670e-01 6.8801986e-01 3.8934542e-01 1.1199472e+00 9.9519977e-01 1.1263042e+00 0.0000000e+00 1.1697902e+00 1.0851476e+00 9.2859317e-01 5.0905001e-01 7.1504098e-01 7.7763126e-01 3.0546431e-01 8.2135873e-01 6.0121055e-01 7.6716823e-01 2.3579605e+00 4.5581864e-01 5.8914551e-01 6.5223271e-01 8.9095811e-01 8.2552685e-01 4.4417983e-01 1.5124582e+00 1.3844611e+00 8.1810461e-01 6.6384020e-01 1.0516761e+00 1.0733200e+00 1.3725949e+00 3.0844217e-01 1.6183051e+00 8.9095811e-01 1.1426203e+00 4.5470518e-01 3.2816937e-01 1.2604558e+00 1.2459608e+00 7.1799256e-01 5.0180477e-01 3.6171588e-01 1.0269295e+00 7.1840099e-01 1.0531192e+00 1.1093572e+00 6.1092863e-01 8.4591037e-01 7.4777660e-01 1.3693737e+00 5.0905001e-01 4.8135521e-01 8.0619006e-01 1.3844611e+00 3.4378533e-01 5.3309112e-01 7.3813096e-01 1.0901359e+00 8.1273630e-01 9.6141901e-01 1.2978356e+00 4.2667565e-01 1.4573287e+00 1.5826638e+00 1.0904758e+00 5.0503591e-01 1.1258723e+00 5.4292906e-01 3.2816937e-01 5.3022554e-01 7.7869083e-01 7.5871717e-01 5.5492130e-01 2.1269358e-01 1.6596342e+00 1.6919202e+00 8.3280511e-01 7.0429250e-01 8.7202528e-01 1.5772389e+00 7.0394675e-01 5.2655962e-01 9.2836103e-01 8.0064372e-01 7.0437330e-01 3.0546431e-01 9.0478973e-01 1.1271488e+00 1.7235501e+00 4.0293660e-01 5.2942799e-01 4.5470518e-01 1.4317371e+00 6.8917100e-01 2.1269358e-01 8.1099042e-01 6.2988288e-01 6.5172743e-01 7.6166891e-01 6.2660376e-01 6.5648056e-01 7.6625946e-01 6.1947990e-01 6.4755655e-01 4.2667565e-01 6.2660376e-01 5.6700421e-01 1.2113327e+00 1.8396098e+00 8.7504951e-01 5.7257017e-01 8.3280511e-01 7.0784540e-01 5.5492130e-01 3.7427929e-01 1.0284501e+00 8.7420176e-01 5.0991930e-01 4.4417983e-01 1.4089719e+00 1.4387122e+00 1.1127329e+00 4.1586001e-01 1.1205013e+00 1.3344634e+00 9.3048953e-01 3.2816937e-01 7.4164639e-01 1.0244319e+00 9.3999899e-01 2.5651975e-01 8.1242502e-01 9.1858284e-01 1.4955532e+00 2.5251796e-01 8.7420176e-01 8.5335130e-01 1.2045536e+00 4.3691963e-01 4.4651726e-01 1.0480665e+00 4.9857388e-01 2.8507955e-01 7.3535471e-01 8.8695363e-01 3.2816937e-01 3.8934542e-01 6.0611244e-01 8.6361309e-01 6.0551856e-01 5.2655962e-01 8.3783744e-01 3.0351721e+00 4.2418962e-01 1.0941064e+00 7.5705927e-01 1.6559784e+00 1.5582387e+00 1.2112034e+00 2.1967372e+00 2.0692197e+00 1.5564198e+00 1.3693737e+00 8.0096515e-01 4.5581864e-01 2.0330276e+00 1.0137836e+00 2.3142399e+00 2.1845981e-01 1.9017011e+00 1.1228379e+00 6.6827038e-01 2.0223026e+00 1.9969203e+00 1.3792358e+00 8.7478495e-01 5.2371571e-01 8.1385214e-01 1.3793330e+00 1.7664528e+00 1.6569692e+00 5.0905001e-01 1.4644753e+00 1.4341959e+00 2.1204309e+00 1.2632199e+00 1.1880428e+00 1.5405106e+00 2.0692197e+00 9.4009473e-01 1.1355826e+00 1.4992973e+00 1.8311457e+00 1.5765737e+00 1.6311692e+00 1.9969203e+00 2.6670272e+00 1.9783833e+00 2.5784641e+00 1.6572339e+00 1.5613865e+00 1.9842916e+00 8.6110333e-01 1.0720678e+00 1.6180482e+00 1.7140774e+00 3.2123303e+00 3.2542669e+00 1.1332978e+00 2.1449779e+00 7.5976039e-01 3.1540626e+00 1.4088394e+00 1.9797139e+00 2.4825886e+00 1.3075101e+00 1.2330392e+00 1.6629594e+00 2.4148300e+00 2.6746409e+00 3.2573703e+00 1.6686069e+00 1.4322723e+00 1.4341959e+00 2.9471490e+00 1.6864366e+00 1.6385322e+00 1.1320702e+00 2.0632091e+00 1.9468380e+00 2.0389505e+00 9.7441804e-01 2.1303950e+00 2.0095672e+00 1.8517858e+00 1.4168607e+00 1.6483152e+00 1.5346983e+00 1.0866092e+00 7.2486328e-01 8.7202528e-01 1.2988558e+00 1.1847335e+00 8.6137722e-01 1.8265471e+00 1.7177705e+00 1.2107055e+00 9.9368623e-01 9.5866719e-01 7.3805807e-01 1.6506221e+00 7.3805807e-01 1.9449573e+00 5.0592043e-01 1.5350426e+00 7.8695083e-01 3.7427929e-01 1.6553809e+00 1.6249178e+00 1.0168833e+00 5.0991930e-01 2.1845981e-01 9.7270522e-01 1.0264409e+00 1.3817041e+00 1.2768639e+00 5.7324170e-01 1.1634384e+00 1.0611732e+00 1.7483574e+00 9.3048953e-01 9.0056222e-01 1.2340567e+00 1.6995747e+00 6.8076724e-01 9.1883539e-01 1.1718516e+00 1.4616896e+00 1.2125198e+00 1.2951888e+00 1.6249178e+00 1.2028939e+00 8.7420176e-01 5.3665999e-01 5.5492130e-01 1.1340084e+00 1.0720678e+00 8.3345577e-01 5.3588338e-01 1.5520745e+00 1.3258714e+00 9.5099818e-01 7.7074935e-01 1.2604558e+00 1.1891470e+00 9.2264612e-01 8.1099042e-01 7.7039952e-01 1.0389435e+00 1.0054794e+00 4.3456114e-01 6.2605182e-01 7.2823007e-01 1.5784191e+00 4.8927739e-01 7.5976039e-01 6.5223271e-01 1.0692258e+00 9.8985697e-01 6.3808075e-01 1.1178200e+00 6.6827038e-01 7.4855857e-01 8.6513410e-01 1.0122141e+00 7.6787403e-01 9.3615100e-01 7.5835500e-01 8.2654509e-01 6.9728513e-01 9.9519977e-01 9.7356960e-01 1.1306887e+00 1.2197188e+00 8.0353565e-01 1.7933375e+00 1.5916843e+00 1.0118409e+00 1.0089164e+00 7.0776547e-01 1.1591754e+00 1.8437762e+00 5.3309112e-01 1.8278913e+00 9.6838716e-01 1.4843324e+00 6.3735887e-01 7.3496673e-01 1.5570415e+00 1.5003972e+00 1.0421979e+00 9.7759114e-01 8.9070384e-01 7.8197925e-01 1.0329598e+00 1.4388174e+00 1.5204340e+00 6.9325418e-01 9.4309624e-01 1.0339865e+00 1.6134578e+00 8.0660588e-01 7.0523271e-01 1.0406064e+00 1.6372749e+00 5.1303949e-01 5.8851328e-01 1.0072663e+00 1.4951106e+00 1.0950112e+00 1.0934620e+00 1.5213929e+00 5.0991930e-01 4.5581864e-01 9.3615100e-01 7.6590510e-01 3.2586371e-01 4.2538717e-01 1.7942496e+00 1.9566981e+00 1.0636401e+00 6.6384020e-01 9.2264612e-01 1.7814077e+00 5.2371571e-01 6.0670504e-01 1.0120221e+00 4.8927739e-01 4.3691963e-01 5.6769031e-01 8.9366705e-01 1.1948578e+00 1.6982795e+00 5.7324170e-01 5.7257017e-01 8.3060013e-01 1.3824965e+00 5.7867728e-01 4.1586001e-01 5.4292906e-01 4.4651726e-01 5.7324170e-01 4.4535192e-01 7.6752131e-01 8.2105460e-01 6.9369532e-01 3.4583729e-01 7.0479928e-01 2.0656129e-01 4.3456114e-01 6.1092863e-01 4.6472023e-01 7.1840099e-01 6.9369532e-01 5.6631629e-01 3.2816937e-01 1.8058693e+00 1.8261179e+00 6.3735887e-01 7.0328431e-01 8.2684479e-01 1.6791597e+00 4.0293660e-01 6.6827038e-01 9.7377870e-01 5.0991930e-01 4.8135521e-01 3.2586371e-01 8.7021234e-01 1.1327825e+00 1.7839298e+00 3.7427929e-01 4.1586001e-01 5.5492130e-01 1.3916739e+00 7.7919451e-01 4.1449626e-01 5.8914551e-01 5.7324170e-01 6.0900723e-01 6.2407309e-01 6.0551856e-01 7.5705927e-01 7.8695083e-01 4.8135521e-01 3.2586371e-01 3.0811765e-01 7.3851529e-01 5.3665999e-01 1.1524979e+00 1.0244319e+00 4.3691963e-01 3.7255734e-01 1.4090646e+00 1.5060944e+00 1.0810263e+00 2.8507955e-01 1.2406194e+00 1.3336069e+00 7.1791510e-01 3.2586371e-01 5.9426792e-01 8.2552685e-01 8.2275389e-01 4.1449626e-01 5.8851328e-01 7.5196795e-01 1.3415658e+00 4.1586001e-01 7.2852070e-01 8.9159388e-01 9.7249562e-01 5.8914551e-01 4.4535192e-01 9.4352681e-01 1.4096146e-01 3.0811765e-01 4.1586001e-01 1.0244319e+00 4.2538717e-01 4.5581864e-01 3.2586371e-01 7.0869559e-01 3.7427929e-01 6.5223271e-01 9.2836103e-01 4.4651726e-01 8.8695363e-01 8.9971984e-01 2.4227359e+00 2.4243464e+00 5.5419992e-01 1.3235313e+00 3.0546431e-01 2.3149695e+00 6.1151102e-01 1.2036484e+00 1.6520677e+00 5.4292906e-01 5.7324170e-01 8.2305664e-01 1.5788188e+00 1.8238348e+00 2.4554026e+00 8.2552685e-01 7.0429250e-01 7.7588000e-01 2.0959492e+00 1.0466623e+00 8.6513410e-01 5.4292906e-01 1.2497790e+00 1.1215059e+00 1.2436109e+00 2.1845981e-01 1.3165513e+00 1.2256881e+00 1.0406064e+00 6.0060595e-01 8.5434758e-01 9.6664346e-01 5.1691876e-01 6.5223271e-01 8.4050231e-01 2.2451458e+00 2.2996030e+00 9.7270522e-01 1.1594648e+00 4.2538717e-01 2.1936248e+00 6.9369532e-01 1.0119857e+00 1.5297036e+00 6.6384020e-01 6.2988288e-01 7.0386584e-01 1.5113992e+00 1.7140171e+00 2.2868482e+00 6.9325418e-01 9.4080461e-01 1.0406064e+00 1.9734538e+00 7.5835500e-01 7.8695083e-01 6.2988288e-01 1.1158787e+00 9.4832302e-01 1.1055069e+00 5.0090417e-01 1.1452867e+00 1.0056742e+00 9.0277242e-01 6.3977563e-01 7.3851529e-01 6.6432544e-01 6.0611244e-01 5.1691876e-01 1.6983410e+00 1.8377590e+00 1.1500393e+00 5.6631629e-01 8.6012420e-01 1.6864433e+00 6.6539428e-01 4.5581864e-01 9.7356960e-01 6.6932542e-01 5.9426792e-01 4.5470518e-01 9.7548738e-01 1.1573546e+00 1.6772907e+00 4.4535192e-01 8.2929029e-01 9.8450810e-01 1.3812107e+00 3.2816937e-01 5.0905001e-01 6.6932542e-01 5.0991930e-01 3.7598397e-01 5.0905001e-01 7.2852070e-01 6.4704320e-01 4.5581864e-01 3.2586371e-01 7.4777660e-01 3.2816937e-01 2.5251796e-01 6.3108414e-01 1.5573817e+00 1.6420607e+00 9.0575661e-01 5.7867728e-01 9.7441804e-01 1.4917344e+00 6.2482915e-01 4.0176783e-01 7.7039952e-01 7.1799256e-01 6.4704320e-01 3.2816937e-01 7.1799256e-01 9.7270522e-01 1.5593809e+00 4.1586001e-01 4.6472023e-01 5.6454040e-01 1.2601890e+00 6.5223271e-01 1.2418578e-01 7.6716823e-01 4.4651726e-01 6.0670504e-01 6.1947990e-01 7.4777660e-01 5.9426792e-01 7.2172678e-01 5.3588338e-01 6.2660376e-01 3.2352160e-01 5.8914551e-01 6.4704320e-01 1.2013436e+00 2.3665136e+00 1.1771643e+00 2.4806944e+00 1.0018083e+00 2.1098467e+00 1.2627078e+00 8.8503502e-01 2.2024869e+00 2.1511385e+00 1.6189643e+00 1.1368070e+00 1.0688498e+00 3.4378533e-01 1.6188960e+00 1.9698860e+00 1.9254808e+00 8.8861541e-01 1.5832517e+00 1.5978297e+00 2.2712062e+00 1.4277162e+00 1.3610783e+00 1.6849072e+00 2.2645802e+00 1.1106525e+00 1.2665468e+00 1.6689743e+00 2.0995265e+00 1.7457596e+00 1.7532140e+00 2.1511385e+00 2.2712062e+00 1.3276804e+00 2.5485519e+00 3.4378533e-01 2.1870851e+00 1.4266198e+00 1.0379132e+00 2.3072128e+00 2.2736138e+00 1.6156775e+00 1.2095267e+00 8.3888121e-01 1.2277129e+00 1.6151153e+00 2.0528819e+00 1.8792214e+00 8.2624515e-01 1.7265353e+00 1.7004805e+00 2.3953564e+00 1.5733646e+00 1.4691764e+00 1.8497891e+00 2.3019759e+00 1.2238809e+00 1.4266198e+00 1.7962897e+00 2.1027465e+00 1.8635467e+00 1.9008621e+00 2.2439391e+00 1.3353353e+00 7.2526325e-01 2.1293320e+00 5.5492130e-01 1.2776560e+00 1.5193574e+00 6.2988288e-01 8.1130291e-01 8.6912228e-01 1.3752391e+00 1.6044563e+00 2.3474075e+00 9.1883539e-01 6.2024833e-01 6.4806901e-01 1.9000365e+00 1.3674559e+00 9.6664346e-01 8.1354181e-01 1.1751082e+00 1.2304904e+00 1.2256933e+00 5.7324170e-01 1.3628690e+00 1.4089364e+00 1.0866132e+00 4.8036801e-01 8.9971984e-01 1.3044654e+00 8.1130291e-01 1.3916739e+00 1.1500393e+00 9.6838716e-01 2.5251796e-01 5.5419992e-01 1.0573285e+00 1.0284501e+00 5.7324170e-01 7.1840099e-01 6.6317860e-01 1.1434428e+00 5.6769031e-01 9.7855477e-01 1.1106525e+00 8.2899253e-01 6.0670504e-01 6.2660376e-01 1.1449732e+00 3.2586371e-01 2.1845981e-01 6.0060595e-01 1.1833351e+00 2.0656129e-01 2.5251796e-01 5.1607523e-01 9.6324667e-01 5.9426792e-01 7.1799256e-01 1.0879524e+00 2.4372751e+00 7.0437330e-01 1.2331989e+00 1.7427900e+00 6.0611244e-01 5.1607523e-01 9.3615100e-01 1.6812503e+00 1.9411754e+00 2.5109747e+00 9.3801395e-01 7.7039952e-01 8.6513410e-01 2.2052183e+00 9.6324667e-01 8.9917007e-01 4.2667565e-01 1.3224963e+00 1.1912106e+00 1.3084046e+00 2.5651975e-01 1.3897316e+00 1.2541242e+00 1.1120775e+00 7.1504098e-01 9.1051084e-01 8.1521713e-01 3.6171588e-01 2.0209349e+00 1.2627078e+00 7.9878917e-01 2.1431239e+00 2.1198551e+00 1.5016009e+00 9.6141901e-01 6.2024833e-01 1.0083666e+00 1.5022608e+00 1.8803649e+00 1.7557336e+00 6.2482915e-01 1.6044563e+00 1.5582387e+00 2.2435182e+00 1.3836712e+00 1.3199714e+00 1.6568705e+00 2.1907335e+00 1.0796583e+00 1.2825987e+00 1.6205332e+00 1.9460721e+00 1.6995133e+00 1.7678302e+00 2.1198551e+00 9.1750357e-01 1.2756158e+00 1.4096146e-01 3.2352160e-01 7.1504098e-01 1.1242402e+00 1.4312787e+00 2.0223464e+00 7.3535471e-01 3.2586371e-01 7.3851529e-01 1.6386882e+00 9.4477932e-01 6.4755655e-01 3.7427929e-01 7.3805807e-01 8.6165877e-01 7.2852070e-01 5.0905001e-01 1.0922991e+00 1.0175773e+00 6.0900723e-01 2.1269358e-01 4.0176783e-01 8.2372435e-01 4.5470518e-01 5.5492130e-01 9.8495853e-01 9.0521488e-01 5.2942799e-01 6.3977563e-01 7.9878917e-01 1.2832075e+00 5.3022554e-01 8.3060013e-01 9.4500268e-01 1.0244319e+00 4.4651726e-01 4.0176783e-01 1.0230441e+00 3.4378533e-01 3.2586371e-01 6.1623531e-01 1.0330459e+00 2.5651975e-01 4.0000000e-01 5.3588338e-01 9.5676647e-01 5.3665999e-01 5.3665999e-01 9.0521488e-01 1.3865084e+00 1.3669552e+00 8.6012420e-01 2.8192292e-01 4.1586001e-01 8.4050231e-01 8.7383925e-01 1.1355826e+00 1.1712156e+00 6.2660376e-01 9.8985697e-01 8.5205778e-01 1.4909823e+00 6.3861009e-01 7.2526325e-01 9.4854455e-01 1.5124582e+00 5.6700421e-01 7.7919451e-01 8.9971984e-01 1.2483814e+00 9.4009473e-01 1.0879524e+00 1.4147273e+00 2.1269358e-01 8.1354181e-01 1.2441035e+00 1.5551238e+00 2.1137172e+00 8.2899253e-01 3.7427929e-01 8.2929029e-01 1.7587110e+00 9.6095130e-01 7.1799256e-01 2.4837156e-01 8.3280511e-01 9.3797093e-01 7.9016429e-01 4.4651726e-01 1.1833351e+00 1.0724413e+00 6.6932542e-01 3.2816937e-01 4.6472023e-01 8.0467258e-01 3.8776762e-01 7.3145860e-01 1.2564564e+00 1.5558094e+00 2.0983278e+00 7.5082357e-01 3.6171588e-01 7.6590510e-01 1.7862655e+00 8.4050231e-01 6.2024833e-01 1.2418578e-01 8.6137722e-01 8.9852394e-01 8.5462626e-01 3.8934542e-01 1.1192362e+00 1.0078327e+00 7.0386584e-01 5.0991930e-01 4.5470518e-01 6.6539428e-01 2.4837156e-01 8.5690100e-01 1.0344911e+00 1.6689743e+00 1.0000000e-01 6.8961791e-01 7.1799256e-01 1.3207609e+00 6.2024833e-01 3.7427929e-01 8.3888121e-01 5.3588338e-01 4.2288438e-01 6.4405773e-01 6.9369532e-01 5.3309112e-01 5.8914551e-01 4.6472023e-01 6.2538346e-01 4.1586001e-01 6.1623531e-01 6.4405773e-01 4.0176783e-01 1.0175773e+00 8.9303452e-01 1.0122141e+00 1.1161766e+00 7.7885297e-01 1.0755693e+00 8.1385214e-01 1.3792358e+00 5.8914551e-01 8.5462626e-01 8.7848692e-01 1.4517959e+00 7.3851529e-01 9.4854455e-01 8.6263408e-01 1.0941064e+00 8.3783744e-01 1.1172689e+00 1.3545005e+00 1.0391247e+00 1.0389435e+00 1.3163598e+00 1.3379696e+00 4.5470518e-01 1.1951875e+00 1.0632598e+00 1.6796759e+00 7.8197925e-01 8.3345577e-01 1.0540105e+00 1.7036156e+00 6.9167458e-01 8.8503502e-01 1.0279631e+00 1.3693737e+00 1.1192426e+00 1.3077572e+00 1.6183051e+00 1.6694974e+00 1.9094934e+00 1.9895190e+00 8.2384013e-01 1.6634400e+00 1.6218244e+00 2.2178691e+00 1.3008161e+00 1.3567326e+00 1.4994715e+00 2.3021295e+00 1.1763719e+00 1.3024224e+00 1.5535909e+00 2.0373882e+00 1.6756749e+00 1.7981158e+00 2.1739455e+00 7.6752131e-01 8.1354181e-01 1.3198846e+00 6.0611244e-01 4.4535192e-01 8.5335130e-01 5.3665999e-01 3.8776762e-01 6.3977563e-01 7.0429250e-01 5.2655962e-01 5.5492130e-01 4.5581864e-01 6.3861009e-01 4.2667565e-01 6.1151102e-01 6.6932542e-01 5.1691876e-01 1.5922648e+00 1.0054794e+00 4.8135521e-01 4.3456114e-01 7.6955924e-01 9.6664346e-01 8.9687438e-01 5.6700421e-01 1.0421979e+00 1.1001291e+00 8.2929029e-01 4.4535192e-01 5.1691876e-01 8.9712482e-01 4.5470518e-01 1.6914476e+00 1.1340084e+00 5.8914551e-01 8.5105559e-01 9.7548738e-01 1.0864449e+00 1.1160770e+00 6.3977563e-01 1.0720678e+00 1.2163831e+00 1.0047836e+00 6.9369532e-01 7.2343175e-01 1.0613462e+00 6.2407309e-01 1.4238090e+00 1.3511716e+00 1.9067300e+00 9.3824087e-01 1.0331736e+00 1.1327825e+00 1.9782093e+00 9.0454394e-01 1.0244319e+00 1.1833480e+00 1.5948732e+00 1.3360558e+00 1.5461469e+00 1.8900319e+00 6.2081167e-01 9.1750357e-01 6.4290921e-01 4.4417983e-01 7.0429250e-01 8.7229670e-01 5.3665999e-01 4.0438741e-01 5.6454040e-01 1.0054794e+00 5.7015910e-01 2.1269358e-01 7.5705927e-01 7.3496673e-01 5.2942799e-01 6.2024833e-01 6.6491075e-01 6.8801986e-01 6.1947990e-01 7.2172678e-01 5.5492130e-01 6.9006418e-01 3.2816937e-01 5.3665999e-01 5.6700421e-01 9.7779835e-01 1.0014633e+00 9.4854455e-01 3.8934542e-01 1.2342162e+00 1.1043332e+00 7.9580667e-01 5.3665999e-01 5.7257017e-01 7.2852070e-01 3.0275928e-01 3.4378533e-01 3.2352160e-01 1.1199472e+00 5.0991930e-01 4.6472023e-01 2.8507955e-01 7.7869083e-01 4.1586001e-01 7.1799256e-01 1.0132664e+00 5.0905001e-01 9.9519977e-01 3.0811765e-01 2.1269358e-01 4.0293660e-01 8.3060013e-01 5.0592043e-01 5.3665999e-01 9.3049742e-01 1.1263042e+00 8.0064372e-01 6.1623531e-01 2.1269358e-01 7.7588000e-01 4.4651726e-01 7.2783368e-01 1.0329901e+00 1.1697902e+00 1.0851476e+00 9.2859317e-01 5.0905001e-01 7.1504098e-01 7.7763126e-01 3.0546431e-01 2.5651975e-01 7.0437330e-01 1.0573285e+00 7.3145860e-01 6.9325418e-01 1.0901359e+00 5.3588338e-01 1.0175773e+00 6.4405773e-01 5.3665999e-01 1.0078327e+00 6.2407309e-01 3.2352160e-01 5.7257017e-01 8.5205778e-01 5.1691876e-01 9.4022486e-01 5.6769031e-01 4.8927739e-01 6.0611244e-01 6.0900723e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..daa81110a2be1a670f6163a8b255b2c6ecd5ccaa --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt @@ -0,0 +1 @@ + 2.0215050e+00 2.0988154e+00 1.8614681e+00 2.0510161e+00 1.9210911e+00 2.1323516e+00 1.9565454e+00 2.1029889e+00 1.9617871e+00 2.0544792e+00 2.0357408e+00 1.8811414e+00 2.0694693e+00 2.1245977e+00 2.0632165e+00 2.0452823e+00 2.0249330e+00 1.9635489e+00 2.0508580e+00 2.0838578e+00 1.9324052e+00 1.8224609e+00 1.9795343e+00 1.9536534e+00 1.9694910e+00 1.9075569e+00 1.9590397e+00 2.0022087e+00 1.8814000e+00 1.8884208e+00 1.9961121e+00 2.0215351e+00 1.7515769e+00 2.0756437e+00 2.0109476e+00 1.9234849e+00 1.9160076e+00 1.8550862e+00 1.7733640e+00 2.0071906e+00 2.0209542e+00 2.0616569e+00 2.0565503e+00 1.9083573e+00 2.2732431e+00 1.9975503e+00 1.9080072e+00 2.1437809e+00 2.1296295e+00 1.9739085e+00 1.9834166e+00 2.1078664e+00 2.2016840e+00 2.2080962e+00 1.7340579e+00 2.0549287e+00 1.7331748e+00 1.9559688e+00 2.0343364e+00 1.8736929e+00 1.9730416e+00 1.5308944e+00 1.8421831e+00 2.0174240e+00 2.0137378e+00 1.7956151e+00 1.9606596e+00 1.9074857e+00 2.0413879e+00 2.0070305e+00 1.9584677e+00 1.8977851e+00 1.9176239e+00 1.7067419e+00 1.9461927e+00 1.8431700e+00 1.8284576e+00 1.7778704e+00 1.8350329e+00 2.0175415e+00 1.7459063e+00 1.9242505e+00 1.8757370e+00 1.9312506e+00 2.0574808e+00 2.0894636e+00 1.9780203e+00 2.1374036e+00 1.8900436e+00 2.0273032e+00 2.0681953e+00 2.0234699e+00 2.0666449e+00 2.0663485e+00 1.9281402e+00 1.7846314e+00 2.0372479e+00 1.8831230e+00 2.0186015e+00 2.0193231e+00 2.2022665e+00 1.8145737e+00 2.0466545e+00 1.8092421e+00 1.9600687e+00 2.0322961e+00 1.9556364e+00 1.8266422e+00 1.9950345e+00 2.1038429e+00 2.1164145e+00 2.0188062e+00 1.8863331e+00 2.0006971e+00 1.9971068e+00 1.8771862e+00 2.1148855e+00 1.9570638e+00 1.9859615e+00 2.0030854e+00 2.0737344e+00 1.9739259e+00 1.9266524e+00 1.9200535e+00 2.1376689e+00 1.8944425e+00 1.9330553e+00 1.8561590e+00 1.9422954e+00 1.8874178e+00 1.8624808e+00 1.8265563e+00 1.8840519e+00 2.0515092e+00 2.0174226e+00 1.9771196e+00 2.0635988e+00 1.7334466e+00 1.9912604e+00 1.8915711e+00 1.8262636e+00 1.9369173e+00 1.9560446e+00 1.9549934e+00 1.9279230e+00 1.9021073e+00 2.0113391e+00 2.0305786e+00 1.8066806e+00 1.9656739e+00 2.1219217e+00 1.8820250e+00 1.8936826e+00 2.0565131e+00 1.9839441e+00 1.8553479e+00 1.9923760e+00 1.6393276e+00 1.9786440e+00 1.8274394e+00 1.9322611e+00 2.0404318e+00 1.9216532e+00 1.9361171e+00 1.8401373e+00 1.9908059e+00 1.9495117e+00 2.1975655e+00 1.8413913e+00 2.1528773e+00 1.8434374e+00 2.1668863e+00 2.0429273e+00 1.9980016e+00 1.9790129e+00 2.0264829e+00 2.1478843e+00 2.0899600e+00 2.0280670e+00 2.1210881e+00 1.9993891e+00 1.8646871e+00 1.9099983e+00 1.9263353e+00 2.0042495e+00 2.1365919e+00 2.1830279e+00 1.9631961e+00 2.0880004e+00 1.8348369e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..aa26b0439f568f97c95bee1b04204f24c9a1f3e0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt @@ -0,0 +1 @@ + 5.0042326e-01 4.1210927e-01 5.2133179e-01 1.1269424e-01 4.2362917e-01 5.0001522e-01 1.2085435e-01 7.4262850e-01 4.0127250e-01 3.0482299e-01 3.0482299e-01 5.0436965e-01 8.0923926e-01 7.1629168e-01 9.1424701e-01 4.1317535e-01 1.0000000e-01 6.0366256e-01 3.0017653e-01 3.3813251e-01 2.2573593e-01 5.2133179e-01 3.4080442e-01 5.0436965e-01 5.0043084e-01 2.2608083e-01 1.1269424e-01 1.1269424e-01 4.1315633e-01 4.1315633e-01 3.0490481e-01 6.0000952e-01 7.0462550e-01 4.0127250e-01 3.0482299e-01 4.0002221e-01 4.0127250e-01 7.1621748e-01 1.1269424e-01 1.2085435e-01 1.2036864e+00 7.0088477e-01 4.0125062e-01 5.0476836e-01 5.0436965e-01 3.0474106e-01 5.0436235e-01 2.2573593e-01 2.0061436e-01 3.3243227e+00 3.1068812e+00 3.5145413e+00 2.6080595e+00 3.2075731e+00 3.1014454e+00 3.3055260e+00 1.9156198e+00 3.2079238e+00 2.5066441e+00 2.1498493e+00 2.8059664e+00 2.6093989e+00 3.3021953e+00 2.2070266e+00 3.0158454e+00 3.1034764e+00 2.7009878e+00 3.1081779e+00 2.5032992e+00 3.4074959e+00 2.6050088e+00 3.5035589e+00 3.3011884e+00 2.9065890e+00 3.0117336e+00 3.4118782e+00 3.6094426e+00 3.1038958e+00 2.1042326e+00 2.4058620e+00 2.3063407e+00 2.5029614e+00 3.7025335e+00 3.1034636e+00 3.1057006e+00 3.3110189e+00 3.0065909e+00 2.7025941e+00 2.6047974e+00 3.0013665e+00 3.2025221e+00 2.6029242e+00 1.9242109e+00 2.8024935e+00 2.8013151e+00 2.8022622e+00 2.9036582e+00 1.6267693e+00 2.7028014e+00 4.6144526e+00 3.7071079e+00 4.5121787e+00 4.2031939e+00 4.4087839e+00 5.2153194e+00 3.1086291e+00 4.9093646e+00 4.4044245e+00 4.7202040e+00 3.7119486e+00 3.9066365e+00 4.1123628e+00 3.6114402e+00 3.7307413e+00 3.9194642e+00 4.1043951e+00 5.3177489e+00 5.5157728e+00 3.6035661e+00 4.3162097e+00 3.5127031e+00 5.3163123e+00 3.5077296e+00 4.3088507e+00 4.6100803e+00 3.4082578e+00 3.5068380e+00 4.2080636e+00 4.4113183e+00 4.7149608e+00 5.0316727e+00 4.2105572e+00 3.7024462e+00 4.2007769e+00 4.7331529e+00 4.2173557e+00 4.1039096e+00 3.4076329e+00 4.0157626e+00 4.2194897e+00 3.7329396e+00 3.7071079e+00 4.5119962e+00 4.3218071e+00 3.8249612e+00 3.6093673e+00 3.8105293e+00 4.0166459e+00 3.7050109e+00 2.2573593e-01 3.0017653e-01 6.0000317e-01 9.0534502e-01 4.1210927e-01 4.0004442e-01 5.0000761e-01 1.2085435e-01 7.1621748e-01 4.0125062e-01 1.1269424e-01 6.0184622e-01 1.0776294e+00 1.4092540e+00 9.0508756e-01 5.0043084e-01 9.0181717e-01 8.0004602e-01 5.2491131e-01 7.0017011e-01 6.1119267e-01 3.6452132e-01 5.2133179e-01 2.0061436e-01 4.0246123e-01 5.0436965e-01 4.1209001e-01 2.4170870e-01 2.0121983e-01 5.2167829e-01 1.1001015e+00 1.2036862e+00 1.2085435e-01 2.2573593e-01 6.3164977e-01 1.2085435e-01 5.0000761e-01 4.0125062e-01 5.0002283e-01 7.0462844e-01 5.0043084e-01 5.2167829e-01 8.0888055e-01 1.1269424e-01 8.0008884e-01 3.0474106e-01 7.0462697e-01 3.0008832e-01 3.3416860e+00 3.1112912e+00 3.5249966e+00 2.6033557e+00 3.2127499e+00 3.1015178e+00 3.3078313e+00 1.9025708e+00 3.2150318e+00 2.5060738e+00 2.1061951e+00 2.8068283e+00 2.6040016e+00 3.3032134e+00 2.2072454e+00 3.0286102e+00 3.1035443e+00 2.7011973e+00 3.1070853e+00 2.5014549e+00 3.4078435e+00 2.6080511e+00 3.5048916e+00 3.3021665e+00 2.9125999e+00 3.0213627e+00 3.4211337e+00 3.6148618e+00 3.1047537e+00 2.1027003e+00 2.4016639e+00 2.3011929e+00 2.5032633e+00 3.7028303e+00 3.1034629e+00 3.1065984e+00 3.3192072e+00 3.0078209e+00 2.7027260e+00 2.6031664e+00 3.0009332e+00 3.2037232e+00 2.6027120e+00 1.9031578e+00 2.8022915e+00 2.8015662e+00 2.8024715e+00 2.9065359e+00 1.6099792e+00 2.7029416e+00 4.6149181e+00 3.7071538e+00 4.5172866e+00 4.2039132e+00 4.4099272e+00 5.2224057e+00 3.1078968e+00 4.9146298e+00 4.4063795e+00 4.7253524e+00 3.7145622e+00 3.9080413e+00 4.1161770e+00 3.6111646e+00 3.7308314e+00 3.9209137e+00 4.1060063e+00 5.3254977e+00 5.5222404e+00 3.6024247e+00 4.3201293e+00 3.5126957e+00 5.3240486e+00 3.5093499e+00 4.3111749e+00 4.6158382e+00 3.4095576e+00 3.5076152e+00 4.2090727e+00 4.4184242e+00 4.7227808e+00 5.0458491e+00 4.2115634e+00 3.7037441e+00 4.2010125e+00 4.7466313e+00 4.2180733e+00 4.1050714e+00 3.4081972e+00 4.0212972e+00 4.2220584e+00 3.7407842e+00 3.7071538e+00 4.5144444e+00 4.3240980e+00 3.8290678e+00 3.6105228e+00 3.8128297e+00 4.0172657e+00 3.7052380e+00 2.0121983e-01 4.1210927e-01 7.9153339e-01 2.0181667e-01 3.0915245e-01 3.3813251e-01 2.2608083e-01 7.1629168e-01 3.0482299e-01 2.0181667e-01 4.0246123e-01 1.1281267e+00 1.2633045e+00 7.8890721e-01 4.1212852e-01 1.0095370e+00 6.0964891e-01 7.0470720e-01 5.2201750e-01 4.1210927e-01 4.5784410e-01 6.0017982e-01 3.4080442e-01 3.4342562e-01 5.0476836e-01 5.0043084e-01 3.0000000e-01 3.0017653e-01 7.0025283e-01 9.0508756e-01 1.0426513e+00 2.2608083e-01 3.0008832e-01 8.0046605e-01 2.2608083e-01 3.0474106e-01 4.0243965e-01 3.3813251e-01 9.0002570e-01 3.0000000e-01 4.3213914e-01 6.8170466e-01 2.0181667e-01 6.1119267e-01 1.1269424e-01 6.3178534e-01 3.0017653e-01 3.4595765e+00 3.2168311e+00 3.6364650e+00 2.7037323e+00 3.3192099e+00 3.2017763e+00 3.4107328e+00 2.0033798e+00 3.3237063e+00 2.6050967e+00 2.2121910e+00 2.9077087e+00 2.7085154e+00 3.4047917e+00 2.3071665e+00 3.1428042e+00 3.2033135e+00 2.8024935e+00 3.2103481e+00 2.6021247e+00 3.5076152e+00 2.7127272e+00 3.6073242e+00 3.4038884e+00 3.0203881e+00 3.1325879e+00 3.5317021e+00 3.7210979e+00 3.2059139e+00 2.2051638e+00 2.5023084e+00 2.4021168e+00 2.6048201e+00 3.8033004e+00 3.2030448e+00 3.2074921e+00 3.4286399e+00 3.1131211e+00 2.8028008e+00 2.7031257e+00 3.1010004e+00 3.3055260e+00 2.7040740e+00 2.0050309e+00 2.9023862e+00 2.9020767e+00 2.9028421e+00 3.0107283e+00 1.7089863e+00 2.8033666e+00 4.7142986e+00 3.8066401e+00 4.6226512e+00 4.3047830e+00 4.5107876e+00 5.3296471e+00 3.2068572e+00 5.0203871e+00 4.5089338e+00 4.8299744e+00 3.8170042e+00 4.0095939e+00 4.2200398e+00 3.7100654e+00 3.8275330e+00 4.0209836e+00 4.2079639e+00 5.4332277e+00 5.6287689e+00 3.7032748e+00 4.4237036e+00 3.6112573e+00 5.4319232e+00 3.6111754e+00 4.4135512e+00 4.7221364e+00 3.5107924e+00 3.6081749e+00 4.3098514e+00 4.5261773e+00 4.8309399e+00 5.1593152e+00 4.3120751e+00 3.8056232e+00 4.3015640e+00 4.8592534e+00 4.3174320e+00 4.2064763e+00 3.5083248e+00 4.1268500e+00 4.3236383e+00 3.8471097e+00 3.8066401e+00 4.6166518e+00 4.4251081e+00 3.9318948e+00 3.7118930e+00 3.9150333e+00 4.1165034e+00 3.8051417e+00 5.2133179e-01 9.0160400e-01 3.0017653e-01 4.1209001e-01 2.2573593e-01 3.0008832e-01 8.2418002e-01 3.0482299e-01 2.0181667e-01 4.1212852e-01 1.2363278e+00 1.3741498e+00 9.0160400e-01 5.2133802e-01 1.1133986e+00 7.1621748e-01 8.0051036e-01 6.3178534e-01 5.6347121e-01 5.0517282e-01 4.1315633e-01 4.0004442e-01 4.1317535e-01 6.0948212e-01 6.0184622e-01 1.2085435e-01 2.0061436e-01 8.0051036e-01 1.0087250e+00 1.1527669e+00 3.0008832e-01 4.1210927e-01 9.0142636e-01 3.0008832e-01 2.2573593e-01 5.0436235e-01 4.5148429e-01 8.0004602e-01 2.2573593e-01 4.8342635e-01 7.2044167e-01 2.0181667e-01 7.1621748e-01 1.1269424e-01 7.4262850e-01 4.0125062e-01 3.2983364e+00 3.0300451e+00 3.4603347e+00 2.5053901e+00 3.1338090e+00 3.0030658e+00 3.2183845e+00 1.8040969e+00 3.1419971e+00 2.4075162e+00 2.0123013e+00 2.7132680e+00 2.5163999e+00 3.2086215e+00 2.1132077e+00 2.9750754e+00 3.0049127e+00 2.6055197e+00 3.0177719e+00 2.4040962e+00 3.3110162e+00 2.5253371e+00 3.4126529e+00 3.2074182e+00 2.8380954e+00 2.9580787e+00 3.3536443e+00 3.5347730e+00 3.0101869e+00 2.0123796e+00 2.3038195e+00 2.2036797e+00 2.4099203e+00 3.6051707e+00 3.0042758e+00 3.0123228e+00 3.2490712e+00 2.9241808e+00 2.6047889e+00 2.5049231e+00 2.9016211e+00 3.1100277e+00 2.5081992e+00 1.8056342e+00 2.7040060e+00 2.7039988e+00 2.7050721e+00 2.8205713e+00 1.5147271e+00 2.6060742e+00 4.5183778e+00 3.6090052e+00 4.4337691e+00 4.1072664e+00 4.3151164e+00 5.1425125e+00 3.0092613e+00 4.8303615e+00 4.3139066e+00 4.6422789e+00 3.6259317e+00 3.8146285e+00 4.0301568e+00 3.5133848e+00 3.6358680e+00 3.8290678e+00 4.0124919e+00 5.2471177e+00 5.4403962e+00 3.5051114e+00 4.2343452e+00 3.4149831e+00 5.2455706e+00 3.4177035e+00 4.2200398e+00 4.5335328e+00 3.3168776e+00 3.4123846e+00 4.1140176e+00 4.3402553e+00 4.6459028e+00 4.9843016e+00 4.1167964e+00 3.6096226e+00 4.1026403e+00 4.6849407e+00 4.1230798e+00 4.0100505e+00 3.3123688e+00 3.9407837e+00 4.1330547e+00 3.6700537e+00 3.6090052e+00 4.4237036e+00 4.2343452e+00 3.7463488e+00 3.5181052e+00 3.7227931e+00 3.9220791e+00 3.6072781e+00 4.2362917e-01 4.0125062e-01 2.0061436e-01 7.4262850e-01 5.0002283e-01 4.0004442e-01 2.4170870e-01 6.0017982e-01 7.4329527e-01 8.0250123e-01 8.5406674e-01 4.1317535e-01 1.2085435e-01 7.0096858e-01 2.0181667e-01 4.1315633e-01 2.0181667e-01 4.5077696e-01 3.6259865e-01 5.0084481e-01 6.0017665e-01 2.4170870e-01 2.0121983e-01 2.2538848e-01 4.1315633e-01 5.0084481e-01 4.0246123e-01 5.0043842e-01 6.3164729e-01 5.0002283e-01 4.0122873e-01 5.0001522e-01 5.0002283e-01 6.7616723e-01 2.0121983e-01 1.2085435e-01 1.3008771e+00 6.0948506e-01 4.0125062e-01 5.0085236e-01 6.0017982e-01 2.2573593e-01 4.5077696e-01 3.0017653e-01 3.0000000e-01 3.3320240e+00 3.1087192e+00 3.5191371e+00 2.6110181e+00 3.2098845e+00 3.1016129e+00 3.3064697e+00 1.9242109e+00 3.2110200e+00 2.5072065e+00 2.1702438e+00 2.8063347e+00 2.6144115e+00 3.3026483e+00 2.2074446e+00 3.0213781e+00 3.1035271e+00 2.7015967e+00 3.1108570e+00 2.5049231e+00 3.4076266e+00 2.6065485e+00 3.5045818e+00 3.3016829e+00 2.9091905e+00 3.0158857e+00 3.4160038e+00 3.6117923e+00 3.1042949e+00 2.1068047e+00 2.4087956e+00 2.3099309e+00 2.5038387e+00 3.7027671e+00 3.1034919e+00 3.1060428e+00 3.3145595e+00 3.0095593e+00 2.7026925e+00 2.6061038e+00 3.0017811e+00 3.2030205e+00 2.6039803e+00 1.9366876e+00 2.8028640e+00 2.8014482e+00 2.8024453e+00 2.9049136e+00 1.6388635e+00 2.7031257e+00 4.6146430e+00 3.7072412e+00 4.5144508e+00 4.2035048e+00 4.4092709e+00 5.2185448e+00 3.1091788e+00 4.9117351e+00 4.4054277e+00 4.7224997e+00 3.7130507e+00 3.9073151e+00 4.1140274e+00 3.6117351e+00 3.7308330e+00 3.9200674e+00 4.1050815e+00 5.3212796e+00 5.5187578e+00 3.6046347e+00 4.3179262e+00 3.5127783e+00 5.3198559e+00 3.5085510e+00 4.3098508e+00 4.6126513e+00 3.4088749e+00 3.5071604e+00 4.2085176e+00 4.4144980e+00 4.7185095e+00 5.0381903e+00 4.2110099e+00 3.7030413e+00 4.2009868e+00 4.7393218e+00 4.2176488e+00 4.1043951e+00 3.4078683e+00 4.0181902e+00 4.2205976e+00 3.7363838e+00 3.7072412e+00 4.5130595e+00 4.3227928e+00 3.8267408e+00 3.6102542e+00 3.8115096e+00 4.0168944e+00 3.7051079e+00 8.0923926e-01 5.2201750e-01 1.1270411e+00 8.0928056e-01 2.4170870e-01 6.3178782e-01 9.1471442e-01 1.1573074e+00 5.2167829e-01 5.0476836e-01 4.0000000e-01 4.2270142e-01 3.0017653e-01 3.0490481e-01 5.0042326e-01 3.0915245e-01 8.5440680e-01 6.0184622e-01 6.3192325e-01 9.0142681e-01 5.2133179e-01 4.0363334e-01 5.0517282e-01 7.8890806e-01 8.2421923e-01 5.0042326e-01 3.1328089e-01 3.4085233e-01 8.0928056e-01 7.2044167e-01 4.5148429e-01 8.0928056e-01 1.0782211e+00 5.0517282e-01 4.8342635e-01 1.6097492e+00 1.0215068e+00 4.5148429e-01 3.0482299e-01 9.1446938e-01 3.0490481e-01 8.5440680e-01 2.4195741e-01 6.1135434e-01 3.0143288e+00 2.8035152e+00 3.2080663e+00 2.3476141e+00 2.9053991e+00 2.8028019e+00 3.0030626e+00 1.7519158e+00 2.9045816e+00 2.2149484e+00 2.0887699e+00 2.5048522e+00 2.3645147e+00 3.0018766e+00 1.9120303e+00 2.7085154e+00 2.8028008e+00 2.4075162e+00 2.8284908e+00 2.2272457e+00 3.1054022e+00 2.3075573e+00 3.2060163e+00 3.0018874e+00 2.6044486e+00 2.7064438e+00 3.1073418e+00 3.3054063e+00 2.8034238e+00 1.8447840e+00 2.1492024e+00 2.0607272e+00 2.2122063e+00 3.4028104e+00 2.8028007e+00 2.8036182e+00 3.0057998e+00 2.7234787e+00 2.4027927e+00 2.3234132e+00 2.7070699e+00 2.9017335e+00 2.3151346e+00 1.8036834e+00 2.5072065e+00 2.5017313e+00 2.5032633e+00 2.6031823e+00 1.5292174e+00 2.4058519e+00 4.3116266e+00 3.4064593e+00 4.2076930e+00 3.9021503e+00 4.1063936e+00 4.9099401e+00 2.8141516e+00 4.6055969e+00 4.1036742e+00 4.4145324e+00 3.4082578e+00 3.6052799e+00 3.8082804e+00 3.3123693e+00 3.4273179e+00 3.6154977e+00 3.8026444e+00 5.0117750e+00 5.2107474e+00 3.3130198e+00 4.0114753e+00 3.2109395e+00 5.0107787e+00 3.2067490e+00 4.0058313e+00 4.3058539e+00 3.1067996e+00 3.2049797e+00 3.9061098e+00 4.1066170e+00 4.4095056e+00 4.7221364e+00 3.9082316e+00 3.4019453e+00 3.9014304e+00 4.4232188e+00 3.9139973e+00 3.8023591e+00 3.1057392e+00 3.7104219e+00 3.9150553e+00 3.4248402e+00 3.4064593e+00 4.2084919e+00 4.0172759e+00 3.5193527e+00 3.3100431e+00 3.5073655e+00 3.7133435e+00 3.4036743e+00 4.0004442e-01 5.0043084e-01 3.4085233e-01 8.0046764e-01 2.2573593e-01 4.0243965e-01 4.2362917e-01 1.2036925e+00 1.1896595e+00 8.0879776e-01 5.0000761e-01 1.1006371e+00 5.2133179e-01 8.0046685e-01 5.0437695e-01 4.0125062e-01 5.0477564e-01 5.0043084e-01 4.5148429e-01 4.0125062e-01 6.0000952e-01 6.0000317e-01 2.2608083e-01 3.0922892e-01 8.0000160e-01 7.4269314e-01 9.6572569e-01 3.4085233e-01 4.0246123e-01 9.0000136e-01 3.4085233e-01 4.0127250e-01 5.0001522e-01 4.0004442e-01 1.1000003e+00 2.2608083e-01 4.1317535e-01 5.7609230e-01 4.0122873e-01 5.2167829e-01 2.0061436e-01 7.0088627e-01 4.0004442e-01 3.3852404e+00 3.1245391e+00 3.5521657e+00 2.6057331e+00 3.2281303e+00 3.1021033e+00 3.3145497e+00 1.9088256e+00 3.2358110e+00 2.5040476e+00 2.1337832e+00 2.8091158e+00 2.6173653e+00 3.3068237e+00 2.2078368e+00 3.0635687e+00 3.1029264e+00 2.7045714e+00 3.1156892e+00 2.5038387e+00 3.4072735e+00 2.6199287e+00 3.5105217e+00 3.3061800e+00 2.9316687e+00 3.0488379e+00 3.4462681e+00 3.6292576e+00 3.1074604e+00 2.1103491e+00 2.4046650e+00 2.3052527e+00 2.5074705e+00 3.7037846e+00 3.1023805e+00 3.1087156e+00 3.3416864e+00 3.0212423e+00 2.7029308e+00 2.6036513e+00 3.0012006e+00 3.2078939e+00 2.6064541e+00 1.9145304e+00 2.8026114e+00 2.8028068e+00 2.8033825e+00 2.9167099e+00 1.6147493e+00 2.7040740e+00 4.6133719e+00 3.7058811e+00 4.5290217e+00 4.2056470e+00 4.4115634e+00 5.2381327e+00 3.1057013e+00 4.9271590e+00 4.4118721e+00 4.7354168e+00 3.7201124e+00 3.9113698e+00 4.1247181e+00 3.6087856e+00 3.7244383e+00 3.9212835e+00 4.1101783e+00 5.3422962e+00 5.5362181e+00 3.6046999e+00 4.3279835e+00 3.5095358e+00 5.3412086e+00 3.5135120e+00 4.3162096e+00 4.6297141e+00 3.4124092e+00 3.5088081e+00 4.2105763e+00 4.4358170e+00 4.7408876e+00 5.0762364e+00 4.2125085e+00 3.7079173e+00 4.2021973e+00 4.7752666e+00 4.2166536e+00 4.1080028e+00 3.4084548e+00 4.0338654e+00 4.2256165e+00 3.7563734e+00 3.7058811e+00 4.5190617e+00 4.3264209e+00 3.8360186e+00 3.6136974e+00 3.8177300e+00 4.0156240e+00 3.7048582e+00 6.3164977e-01 3.0017653e-01 4.1209001e-01 2.0061436e-01 4.0127250e-01 7.0911112e-01 8.2458409e-01 1.0207396e+00 5.2201750e-01 1.2699992e-01 7.0470867e-01 4.0004442e-01 4.0122873e-01 3.0482299e-01 5.2167208e-01 3.0490481e-01 4.0122873e-01 4.0002221e-01 2.0061436e-01 2.0061436e-01 2.0061436e-01 3.0482299e-01 3.0482299e-01 4.0122873e-01 7.0008584e-01 8.0879701e-01 3.0017653e-01 3.0474106e-01 5.0043084e-01 3.0017653e-01 6.0964597e-01 1.0000000e-01 2.0121983e-01 1.1019599e+00 6.0035305e-01 4.0004442e-01 4.5148429e-01 4.0127250e-01 4.0004442e-01 4.0125062e-01 3.3808272e-01 1.1269424e-01 3.2369541e+00 3.0101869e+00 3.4219340e+00 2.5073576e+00 3.1113295e+00 3.0016913e+00 3.2074921e+00 1.8128536e+00 3.1127326e+00 2.4076937e+00 2.0429861e+00 2.7074657e+00 2.5087337e+00 3.2029987e+00 2.1087640e+00 2.9250474e+00 3.0040848e+00 2.6011837e+00 3.0090716e+00 2.4029250e+00 3.3087901e+00 2.5074281e+00 3.4046875e+00 3.2018065e+00 2.8107271e+00 2.9185950e+00 3.3183094e+00 3.5134617e+00 3.0049285e+00 2.0041542e+00 2.3049133e+00 2.2050331e+00 2.4035997e+00 3.6030023e+00 3.0040438e+00 3.0070658e+00 3.2168317e+00 2.9083216e+00 2.6031436e+00 2.5048522e+00 2.9013423e+00 3.1034810e+00 2.5032729e+00 1.8201043e+00 2.7028014e+00 2.7016556e+00 2.7027522e+00 2.8056775e+00 1.5256523e+00 2.6033557e+00 4.5162553e+00 3.6081006e+00 4.4160732e+00 4.1039121e+00 4.3103378e+00 5.1203327e+00 3.0096880e+00 4.8129366e+00 4.3058720e+00 4.6249088e+00 3.6148619e+00 3.8081633e+00 4.0157626e+00 3.5129206e+00 3.6349703e+00 3.8226858e+00 4.0057080e+00 5.2232912e+00 5.4204287e+00 3.5035589e+00 4.2200398e+00 3.4145570e+00 5.2217206e+00 3.4096180e+00 4.2110197e+00 4.5140458e+00 3.3101076e+00 3.4081996e+00 4.1095117e+00 4.3161641e+00 4.6204721e+00 4.9419857e+00 4.1123051e+00 3.6033860e+00 4.1009647e+00 4.6434791e+00 4.1197833e+00 4.0049425e+00 3.3090452e+00 3.9205015e+00 4.1230798e+00 3.6413278e+00 3.6081006e+00 4.4145323e+00 4.2254713e+00 3.7302938e+00 3.5112285e+00 3.7130507e+00 3.9190472e+00 3.6058055e+00 5.0043842e-01 1.0426513e+00 5.2167208e-01 4.0004442e-01 3.0026460e-01 1.4542931e+00 1.5965783e+00 1.1269511e+00 7.4262964e-01 1.3253871e+00 9.3306807e-01 1.0032293e+00 8.5406674e-01 7.0470720e-01 7.0633229e-01 5.7608844e-01 6.0017982e-01 6.3192325e-01 8.2418071e-01 8.0879625e-01 3.4080442e-01 4.0243965e-01 1.0030871e+00 1.2189645e+00 1.3741465e+00 5.0043842e-01 6.0201716e-01 1.1055705e+00 5.0043842e-01 1.1269424e-01 7.1621748e-01 6.7616902e-01 6.0000952e-01 3.0008832e-01 6.8170466e-01 9.3735629e-01 4.0004442e-01 9.3308853e-01 3.0474106e-01 9.6572569e-01 6.0948212e-01 3.4311880e+00 3.1440065e+00 3.5828092e+00 2.6061623e+00 3.2490712e+00 3.1047537e+00 3.3265679e+00 1.9024467e+00 3.2610547e+00 2.5066443e+00 2.1042326e+00 2.8182771e+00 2.6268573e+00 3.3136174e+00 2.2177383e+00 3.1042292e+00 3.1056084e+00 2.7106185e+00 3.1258664e+00 2.5072166e+00 3.4123850e+00 2.6397031e+00 3.5191318e+00 3.3125861e+00 2.9569988e+00 3.0825000e+00 3.4750557e+00 3.6484459e+00 3.1148203e+00 2.1231535e+00 2.4058952e+00 2.3063814e+00 2.5167763e+00 3.7071732e+00 3.1042001e+00 3.1166462e+00 3.3690976e+00 3.0370401e+00 2.7067267e+00 2.6060811e+00 3.0024163e+00 3.2157547e+00 2.6139440e+00 1.9029771e+00 2.8056558e+00 2.8068283e+00 2.8077255e+00 2.9323793e+00 1.6119586e+00 2.7091848e+00 4.6187512e+00 3.7092298e+00 4.5442576e+00 4.2099019e+00 4.4180513e+00 5.2548586e+00 3.1079005e+00 4.9407795e+00 4.4195588e+00 4.7516511e+00 3.7329384e+00 3.9191954e+00 4.1389224e+00 3.6127211e+00 3.7328453e+00 3.9318950e+00 4.1174259e+00 5.3601143e+00 5.5513974e+00 3.6073242e+00 4.3425018e+00 3.5138357e+00 5.3586950e+00 3.5235095e+00 4.3257995e+00 4.6452056e+00 3.4217238e+00 3.5154314e+00 4.2169032e+00 4.4544908e+00 4.7602896e+00 5.1057502e+00 4.2193718e+00 3.7147036e+00 4.2043114e+00 4.8058872e+00 4.2239687e+00 4.1138950e+00 3.4146523e+00 4.0526593e+00 4.2382079e+00 3.7847403e+00 3.7092298e+00 4.5291248e+00 4.3385161e+00 3.8547029e+00 3.6228903e+00 3.8290678e+00 4.0228342e+00 3.7082809e+00 6.3164977e-01 3.0026460e-01 1.2085435e-01 6.0948506e-01 1.0143978e+00 1.3131369e+00 8.0928056e-01 4.0246123e-01 8.5409862e-01 7.0016860e-01 5.0477564e-01 6.0201716e-01 5.6595908e-01 4.0363334e-01 4.1212852e-01 1.2699992e-01 3.3818226e-01 4.1210927e-01 3.3818226e-01 2.0181667e-01 1.2085435e-01 5.0855077e-01 1.0001598e+00 1.1055707e+00 0.0000000e+00 3.0026460e-01 6.0964891e-01 0.0000000e+00 5.0043842e-01 3.0482299e-01 4.0246123e-01 8.0254500e-01 5.0043842e-01 5.2133802e-01 7.0556260e-01 2.0181667e-01 7.0008735e-01 3.0026460e-01 6.0948506e-01 2.0181667e-01 3.2490712e+00 3.0153168e+00 3.4297841e+00 2.5067523e+00 3.1166337e+00 3.0027816e+00 3.2112793e+00 1.8068048e+00 3.1183051e+00 2.4116924e+00 2.0138832e+00 2.7116615e+00 2.5059537e+00 3.2048192e+00 2.1144760e+00 2.9351753e+00 3.0063019e+00 2.6019122e+00 3.0106587e+00 2.4030297e+00 3.3125861e+00 2.5120719e+00 3.4068163e+00 3.2029877e+00 2.8162444e+00 2.9267417e+00 3.3252407e+00 3.5189464e+00 3.0077107e+00 2.0051350e+00 2.3037132e+00 2.2028146e+00 2.4058620e+00 3.6044981e+00 3.0062070e+00 3.0107283e+00 3.2237456e+00 2.9105093e+00 2.6052541e+00 2.5062865e+00 2.9018772e+00 3.1056084e+00 2.5048522e+00 1.8082911e+00 2.7043948e+00 2.7029415e+00 2.7046027e+00 2.8091099e+00 1.5248852e+00 2.6055127e+00 4.5209020e+00 3.6112573e+00 4.4212031e+00 4.1056541e+00 4.3138986e+00 5.1255338e+00 3.0133997e+00 4.8167235e+00 4.3081273e+00 4.6319211e+00 3.6205854e+00 3.8114965e+00 4.0212972e+00 3.5173798e+00 3.6449970e+00 3.8299342e+00 4.0081754e+00 5.2290121e+00 5.4254411e+00 3.5039202e+00 4.2264145e+00 3.4198378e+00 5.2270034e+00 3.4138008e+00 4.2149806e+00 4.5183778e+00 3.3145502e+00 3.4118179e+00 4.1129687e+00 4.3210760e+00 4.6261633e+00 4.9512603e+00 4.1165035e+00 3.6051692e+00 4.1014742e+00 4.6540056e+00 4.1257291e+00 4.0071257e+00 3.3129914e+00 3.9274863e+00 4.1301604e+00 3.6542046e+00 3.6112573e+00 4.4192311e+00 4.2328883e+00 3.7399948e+00 3.5155767e+00 3.7180846e+00 3.9250546e+00 3.6083191e+00 6.0184622e-01 7.4263078e-01 1.1138955e+00 4.2268438e-01 7.0096708e-01 2.4170870e-01 3.0490481e-01 3.0490481e-01 3.0017653e-01 3.0474106e-01 3.0474106e-01 8.0879701e-01 4.2362917e-01 6.1119267e-01 7.0462697e-01 4.1317535e-01 2.2538848e-01 3.0482299e-01 7.1621748e-01 6.7616723e-01 3.0474106e-01 4.0125062e-01 5.0001522e-01 6.3164977e-01 5.2491131e-01 2.2573593e-01 6.3164977e-01 1.0207396e+00 3.3808272e-01 4.0246123e-01 1.4180463e+00 1.0030868e+00 4.5148429e-01 4.1317535e-01 7.4263078e-01 3.0017653e-01 8.0879701e-01 1.0000000e-01 4.5078948e-01 3.2116783e+00 3.0049285e+00 3.4072983e+00 2.5182898e+00 3.1051604e+00 3.0020136e+00 3.2049016e+00 1.8469618e+00 3.1036832e+00 2.4099081e+00 2.1180493e+00 2.7068820e+00 2.5224740e+00 3.2021231e+00 2.1097449e+00 2.9077617e+00 3.0041462e+00 2.6022422e+00 3.0134290e+00 2.4087504e+00 3.3085101e+00 2.5050799e+00 3.4038679e+00 3.2010814e+00 2.8036959e+00 2.9060895e+00 3.3058271e+00 3.5063866e+00 3.0043212e+00 2.0122773e+00 2.3159426e+00 2.2186306e+00 2.4051454e+00 3.6029749e+00 3.0041461e+00 3.0062373e+00 3.2059465e+00 2.9096170e+00 2.6032656e+00 2.5097004e+00 2.9028411e+00 3.1023606e+00 2.5057847e+00 1.8685354e+00 2.7039990e+00 2.7016498e+00 2.7029428e+00 2.8028074e+00 1.5747520e+00 2.6039937e+00 4.5157550e+00 3.6083209e+00 4.4088451e+00 4.1031691e+00 4.3089952e+00 5.1095334e+00 3.0117336e+00 4.8052574e+00 4.3035619e+00 4.6175091e+00 3.6116958e+00 3.8067089e+00 4.0107159e+00 3.5138361e+00 3.6350483e+00 3.8210210e+00 4.0037985e+00 5.2113565e+00 5.4105254e+00 3.5063553e+00 4.2147222e+00 3.4147657e+00 5.2097995e+00 3.4081036e+00 4.2080425e+00 4.5057296e+00 3.3089414e+00 3.4074852e+00 4.1084282e+00 4.3058539e+00 4.6088153e+00 4.9193995e+00 4.1112251e+00 3.6020843e+00 4.1009356e+00 4.6223848e+00 4.1190046e+00 4.0036188e+00 3.3085886e+00 3.9129256e+00 4.1197933e+00 3.6305006e+00 3.6083209e+00 4.4113183e+00 4.2225427e+00 3.7249938e+00 3.5105217e+00 3.7103007e+00 3.9184088e+00 3.6056580e+00 4.0125062e-01 5.7609230e-01 1.0095367e+00 1.0776296e+00 6.3322667e-01 3.0490481e-01 9.0140221e-01 4.1212852e-01 6.0000317e-01 3.4085233e-01 6.0035305e-01 3.3818226e-01 3.0000000e-01 4.0122873e-01 2.2538848e-01 4.0004442e-01 4.0122873e-01 2.0061436e-01 3.0000000e-01 6.0017982e-01 7.0462844e-01 8.5406616e-01 3.0026460e-01 4.0243965e-01 7.0088477e-01 3.0026460e-01 4.5783248e-01 3.0008832e-01 3.0490481e-01 1.1002025e+00 4.1315633e-01 4.0125062e-01 4.2362917e-01 4.0125062e-01 4.1209001e-01 2.4170870e-01 5.0436965e-01 2.2573593e-01 3.1712557e+00 2.9203034e+00 3.3425817e+00 2.4092081e+00 3.0228582e+00 2.9024211e+00 3.1131137e+00 1.7168003e+00 3.0276611e+00 2.3094323e+00 1.9540727e+00 2.6109956e+00 2.4153242e+00 3.1056218e+00 2.0123796e+00 2.8520945e+00 2.9050328e+00 2.5029614e+00 2.9148948e+00 2.3042831e+00 3.2109395e+00 2.4161682e+00 3.3086859e+00 3.1042389e+00 2.7244207e+00 2.8394157e+00 3.2369857e+00 3.4247142e+00 2.9077271e+00 1.9085444e+00 2.2064916e+00 2.1068047e+00 2.3066817e+00 3.5042241e+00 2.9048033e+00 2.9102290e+00 3.1338090e+00 2.8169587e+00 2.5042601e+00 2.4061715e+00 2.8017212e+00 3.0065627e+00 2.4058322e+00 1.7261843e+00 2.6037439e+00 2.6027120e+00 2.6040234e+00 2.7127458e+00 1.4350761e+00 2.5049231e+00 4.4189015e+00 3.5095669e+00 4.3257995e+00 4.0057109e+00 4.2135057e+00 5.0324952e+00 2.9113810e+00 4.7221382e+00 4.2099962e+00 4.5353918e+00 3.5215862e+00 3.7118930e+00 3.9240025e+00 3.4150232e+00 3.5401623e+00 3.7282910e+00 3.9092259e+00 5.1365012e+00 5.3314853e+00 3.4049933e+00 4.1286955e+00 3.3168890e+00 5.1347989e+00 3.3143385e+00 4.1161770e+00 4.4243750e+00 3.2143454e+00 3.3110189e+00 4.0125032e+00 4.2289520e+00 4.5343227e+00 4.8661173e+00 4.0156353e+00 3.5063553e+00 4.0017163e+00 4.5675364e+00 4.0235140e+00 3.9076272e+00 3.2116700e+00 3.8321139e+00 4.0301570e+00 3.5598557e+00 3.5095669e+00 4.3201293e+00 4.1322798e+00 3.6413292e+00 3.4157005e+00 3.6188994e+00 3.8226858e+00 3.5071409e+00 5.0436235e-01 1.1269511e+00 1.4180734e+00 9.1446938e-01 5.0476836e-01 9.6593231e-01 8.0051115e-01 6.1119558e-01 7.0176271e-01 6.0964891e-01 4.3213914e-01 5.2133179e-01 2.2573593e-01 4.1420960e-01 5.2133802e-01 4.5078948e-01 2.2608083e-01 2.0121983e-01 6.1119558e-01 1.1005364e+00 1.2089192e+00 1.2085435e-01 2.4195741e-01 7.1621884e-01 1.2085435e-01 4.0004442e-01 4.1212852e-01 5.0085236e-01 7.0096858e-01 4.0127250e-01 5.6394820e-01 8.0967961e-01 2.0000000e-01 8.0051115e-01 2.2573593e-01 7.1621884e-01 3.0482299e-01 3.3545239e+00 3.1166331e+00 3.5333785e+00 2.6054739e+00 3.2183845e+00 3.1025789e+00 3.3116521e+00 1.9046783e+00 3.2211369e+00 2.5096353e+00 2.1074907e+00 2.8107054e+00 2.6064541e+00 3.3051050e+00 2.2121875e+00 3.0393610e+00 3.1054994e+00 2.7022579e+00 3.1107490e+00 2.5027328e+00 3.4112739e+00 2.6129479e+00 3.5073688e+00 3.3035252e+00 2.9185900e+00 3.0300451e+00 3.4286400e+00 3.6205854e+00 3.1074470e+00 2.1053074e+00 2.4030297e+00 2.3022754e+00 2.5057763e+00 3.7043108e+00 3.1053329e+00 3.1100313e+00 3.3265652e+00 3.0118276e+00 2.7046025e+00 2.6052853e+00 3.0016501e+00 3.2059133e+00 2.6047974e+00 1.9052628e+00 2.8038694e+00 2.8028007e+00 2.8041967e+00 2.9102290e+00 1.6179159e+00 2.7049931e+00 4.6192199e+00 3.7100254e+00 4.5225779e+00 4.2056438e+00 4.4133506e+00 5.2278849e+00 3.1114444e+00 4.9186970e+00 4.4088300e+00 4.7323336e+00 3.7201124e+00 3.9113387e+00 4.1217116e+00 3.6152935e+00 3.7397620e+00 3.9276515e+00 4.1085246e+00 5.3314853e+00 5.5274937e+00 3.6037456e+00 4.3264210e+00 3.5173586e+00 5.3296471e+00 3.5134601e+00 4.3151165e+00 4.6204664e+00 3.4137985e+00 3.5110031e+00 4.2123903e+00 4.4237218e+00 4.7288133e+00 5.0555470e+00 4.2155430e+00 3.7056457e+00 4.2016096e+00 4.7574592e+00 4.2235569e+00 4.1072664e+00 3.4118179e+00 4.0283196e+00 4.2288238e+00 3.7532858e+00 3.7100254e+00 4.5190617e+00 4.3311362e+00 3.8383398e+00 3.6148683e+00 3.8177286e+00 4.0227665e+00 3.7075359e+00 1.5237054e+00 1.5778323e+00 1.1528553e+00 8.0928056e-01 1.4109657e+00 9.0296858e-01 1.1060939e+00 8.5617086e-01 6.0184934e-01 8.2671175e-01 8.1112984e-01 7.1621748e-01 7.2113820e-01 9.0642722e-01 9.0166476e-01 5.2167829e-01 5.6347978e-01 1.1011719e+00 1.1531951e+00 1.3523685e+00 6.0948506e-01 7.0008735e-01 1.2012929e+00 6.0948506e-01 2.0121983e-01 8.0488008e-01 7.1636719e-01 7.0025283e-01 2.2608083e-01 7.4418186e-01 9.6702272e-01 5.0476836e-01 9.0657583e-01 3.4085233e-01 1.0214933e+00 7.0176271e-01 3.7102713e+00 3.4382051e+00 3.8712380e+00 2.9060895e+00 3.5425492e+00 3.4047913e+00 3.6240078e+00 2.2025238e+00 3.5521653e+00 2.8062729e+00 2.4042873e+00 3.1166330e+00 2.9229849e+00 3.6127194e+00 2.5155829e+00 3.3866455e+00 3.4056098e+00 3.0096888e+00 3.4232171e+00 2.8068501e+00 3.7118528e+00 2.9335034e+00 3.8176417e+00 3.6116893e+00 3.2480452e+00 3.3690976e+00 3.7643838e+00 3.9429243e+00 3.4137984e+00 2.4191998e+00 2.7057317e+00 2.6060678e+00 2.8148779e+00 4.0071259e+00 3.4042418e+00 3.4154455e+00 3.6592943e+00 3.3320889e+00 3.0065584e+00 2.9059779e+00 3.3025806e+00 3.5145393e+00 2.9126049e+00 2.2031052e+00 3.1056091e+00 3.1065947e+00 3.1074470e+00 3.2280982e+00 1.9100994e+00 3.0087060e+00 4.9179684e+00 4.0090033e+00 4.8406432e+00 4.5097222e+00 4.7173415e+00 5.5505575e+00 3.4073974e+00 5.2376127e+00 4.7184838e+00 5.0477042e+00 4.0301568e+00 4.2181242e+00 4.4357103e+00 3.9120615e+00 4.0296437e+00 4.2295175e+00 4.4165186e+00 5.6553854e+00 5.8478137e+00 3.9072483e+00 4.6391758e+00 3.8129545e+00 5.6540008e+00 3.8217034e+00 4.6242406e+00 4.9412981e+00 3.7201124e+00 3.8146271e+00 4.5162248e+00 4.7490801e+00 5.0547228e+00 5.3951639e+00 4.5184830e+00 4.0138270e+00 4.5043856e+00 5.0949990e+00 4.5225786e+00 4.4133506e+00 3.7138970e+00 4.3475342e+00 4.5353918e+00 4.0747727e+00 4.0090033e+00 4.8274110e+00 4.6357670e+00 4.1493188e+00 3.9212879e+00 4.1268500e+00 4.3214438e+00 4.0081754e+00 4.1317535e-01 4.0127250e-01 7.1629303e-01 5.0043842e-01 7.0096858e-01 6.3912709e-01 7.0184453e-01 1.2003596e+00 7.9871893e-01 1.0286506e+00 1.0433442e+00 8.2635069e-01 6.3309012e-01 6.7626502e-01 1.1286016e+00 1.0782105e+00 6.1135434e-01 6.0184934e-01 3.0915245e-01 1.0143978e+00 9.0155393e-01 5.0436965e-01 1.0143978e+00 1.4324323e+00 7.4329414e-01 8.0879776e-01 1.7570482e+00 1.4092511e+00 8.1343016e-01 7.8895472e-01 1.1269511e+00 7.0470720e-01 1.2189701e+00 5.0855077e-01 8.5406616e-01 3.5025396e+00 3.3027388e+00 3.7022129e+00 2.8281704e+00 3.4036672e+00 3.3025779e+00 3.5030234e+00 2.1725076e+00 3.4018155e+00 2.7109019e+00 2.4518621e+00 3.0049127e+00 2.8364042e+00 3.5019450e+00 2.4088882e+00 3.2025657e+00 3.3031143e+00 2.9050277e+00 3.3192099e+00 2.7159616e+00 3.6057054e+00 2.8056568e+00 3.7048624e+00 3.5016345e+00 3.1026586e+00 3.2026875e+00 3.6024856e+00 3.8034160e+00 3.3035252e+00 2.3226028e+00 2.6270968e+00 2.5319718e+00 2.7081195e+00 3.9029099e+00 3.3031170e+00 3.3039553e+00 3.5023855e+00 3.2150432e+00 2.9028412e+00 2.8148779e+00 3.2051933e+00 3.4018781e+00 2.8098127e+00 2.1974660e+00 3.0055598e+00 3.0017653e+00 3.0030650e+00 3.1026235e+00 1.9002712e+00 2.9047832e+00 4.8115512e+00 3.9065683e+00 4.7047990e+00 4.4023911e+00 4.6064349e+00 5.4038141e+00 3.3119500e+00 5.1019028e+00 4.6029848e+00 4.9110422e+00 3.9076509e+00 4.1051819e+00 4.3067850e+00 3.8114951e+00 3.9246405e+00 4.1145310e+00 4.3025710e+00 5.5046681e+00 5.7049556e+00 3.8098343e+00 4.5095384e+00 3.7106240e+00 5.5035834e+00 3.7063915e+00 4.5052925e+00 4.8020893e+00 3.6066590e+00 3.7052383e+00 4.4062090e+00 4.6017113e+00 4.9033376e+00 5.2065497e+00 4.4082090e+00 3.9018737e+00 4.4013937e+00 4.9097097e+00 4.4135256e+00 4.3024877e+00 3.6059708e+00 4.2076410e+00 4.4136664e+00 3.9189006e+00 3.9065683e+00 4.7076752e+00 4.5157700e+00 4.0166034e+00 3.8091065e+00 4.0069397e+00 4.2129114e+00 3.9040721e+00 5.0476836e-01 9.1422402e-01 6.0017982e-01 6.7616723e-01 1.0001903e+00 7.4262850e-01 1.1298636e+00 1.1055799e+00 1.0782211e+00 1.4043036e+00 1.0207260e+00 9.0508712e-01 1.0030871e+00 1.2632996e+00 1.3253497e+00 1.0001598e+00 5.0855077e-01 2.4195741e-01 1.3131369e+00 1.2089895e+00 9.0007572e-01 1.3131369e+00 1.5263518e+00 1.0087393e+00 9.3308891e-01 2.1138769e+00 1.4140515e+00 9.3308891e-01 6.8160885e-01 1.4180436e+00 6.7626681e-01 1.3018145e+00 7.0470720e-01 1.1133986e+00 3.2054626e+00 3.0041787e+00 3.4044439e+00 2.6382589e+00 3.1129223e+00 3.0138245e+00 3.2030205e+00 2.1566438e+00 3.1086927e+00 2.4554557e+00 2.5269479e+00 2.7127202e+00 2.6737930e+00 3.2074207e+00 2.1510232e+00 2.9068053e+00 3.0077106e+00 2.6369414e+00 3.0816280e+00 2.4971338e+00 3.3055260e+00 2.5325155e+00 3.4206210e+00 3.2100081e+00 2.8135926e+00 2.9088609e+00 3.3100014e+00 3.5053029e+00 3.0107283e+00 2.1554609e+00 2.4508792e+00 2.3794578e+00 2.4537333e+00 3.6090037e+00 3.0077114e+00 3.0034200e+00 3.2047284e+00 2.9729700e+00 2.6131590e+00 2.5821442e+00 2.9309340e+00 3.1060464e+00 2.5610212e+00 2.2285414e+00 2.7317035e+00 2.7106184e+00 2.7159615e+00 2.8134622e+00 1.9767345e+00 2.6270952e+00 4.5095106e+00 3.6117736e+00 4.4050179e+00 4.1034650e+00 4.3058769e+00 5.1048430e+00 3.0395885e+00 4.8030341e+00 4.3077256e+00 4.6095748e+00 3.6067573e+00 3.8091370e+00 4.0067454e+00 3.5235094e+00 3.6257071e+00 3.8125141e+00 4.0031827e+00 5.2054101e+00 5.4066794e+00 3.5404367e+00 4.2082468e+00 3.4146523e+00 5.2054259e+00 3.4138230e+00 4.2042865e+00 4.5025743e+00 3.3123786e+00 3.4067938e+00 4.1072911e+00 4.3032007e+00 4.6053784e+00 4.9093646e+00 4.1089590e+00 3.6062594e+00 4.1061436e+00 4.6117560e+00 4.1111295e+00 4.0026055e+00 3.3078313e+00 3.9072844e+00 4.1120111e+00 3.6177796e+00 3.6117736e+00 4.4064445e+00 4.2133758e+00 3.7157992e+00 3.5215805e+00 3.7072609e+00 3.9105673e+00 3.6051689e+00 4.1212852e-01 4.1212852e-01 3.0490481e-01 5.2167208e-01 3.0915245e-01 8.0097499e-01 6.1119558e-01 6.9518117e-01 9.0168933e-01 5.2491131e-01 4.0363334e-01 5.0085236e-01 7.8940551e-01 8.2462252e-01 5.0042326e-01 3.1328089e-01 3.0490481e-01 8.0928056e-01 7.0470867e-01 4.0125062e-01 8.0928056e-01 1.0776294e+00 5.0517282e-01 4.5078948e-01 1.6096629e+00 1.0207396e+00 4.5847767e-01 6.0184622e-01 9.1422402e-01 3.4085233e-01 8.5406674e-01 2.4195741e-01 6.0964891e-01 3.4079041e+00 3.2018548e+00 3.6045966e+00 2.7227151e+00 3.3029106e+00 3.2014779e+00 3.4016816e+00 2.0608234e+00 3.3024690e+00 2.6067721e+00 2.3393392e+00 2.9023862e+00 2.7310942e+00 3.4010299e+00 2.3048574e+00 3.1044057e+00 3.2014774e+00 2.8036023e+00 3.2152055e+00 2.6124447e+00 3.5030234e+00 2.7035171e+00 3.6034258e+00 3.4010358e+00 3.0022434e+00 3.1033306e+00 3.5041120e+00 3.7031277e+00 3.2018065e+00 2.2177966e+00 2.5220687e+00 2.4265152e+00 2.6055197e+00 3.8016494e+00 3.2014773e+00 3.2019092e+00 3.4031881e+00 3.1122360e+00 2.8013347e+00 2.7110046e+00 3.1036553e+00 3.3009330e+00 2.7070770e+00 2.0855888e+00 2.9035486e+00 2.9008500e+00 2.9016034e+00 3.0016038e+00 1.7857124e+00 2.8028019e+00 4.7076061e+00 3.8037955e+00 4.6049801e+00 4.3013465e+00 4.5040960e+00 5.3068325e+00 3.2075036e+00 5.0037550e+00 4.5023523e+00 4.8096009e+00 3.8048551e+00 4.0031892e+00 4.2051335e+00 3.7071735e+00 3.8161632e+00 4.0093901e+00 4.2016368e+00 5.4081547e+00 5.6075434e+00 3.7075525e+00 4.4072835e+00 3.6062405e+00 5.4074634e+00 3.6038441e+00 4.4036959e+00 4.7038247e+00 3.5038075e+00 3.6028345e+00 4.3038299e+00 4.5042394e+00 4.8062731e+00 5.1150203e+00 4.3051629e+00 3.8011413e+00 4.3008955e+00 4.8153681e+00 4.3087925e+00 4.2014602e+00 3.5032125e+00 4.1063865e+00 4.3094598e+00 3.8146853e+00 3.8037955e+00 4.6054982e+00 4.4109814e+00 3.9115832e+00 3.7058197e+00 3.9043917e+00 4.1081838e+00 3.8021570e+00 6.0365948e-01 3.0008832e-01 3.3818226e-01 2.0121983e-01 5.2133802e-01 3.0915245e-01 5.0437695e-01 5.0043842e-01 2.0181667e-01 1.2085435e-01 1.2085435e-01 4.1317535e-01 4.1317535e-01 3.0026460e-01 6.0018299e-01 7.0462697e-01 4.0246123e-01 3.0490481e-01 4.0004442e-01 4.0246123e-01 7.1621884e-01 1.2085435e-01 1.1269424e-01 1.2036863e+00 7.0088627e-01 3.0482299e-01 5.0436965e-01 5.0436235e-01 3.0482299e-01 5.0436965e-01 2.2608083e-01 2.0121983e-01 3.3237063e+00 3.1056091e+00 3.5138377e+00 2.6067805e+00 3.2064817e+00 3.1008890e+00 3.3041599e+00 1.9144935e+00 3.2074507e+00 2.5042498e+00 2.1492024e+00 2.8038903e+00 2.6091437e+00 3.3015588e+00 2.2041706e+00 3.0148613e+00 3.1021975e+00 2.7007716e+00 3.1069083e+00 2.5027328e+00 3.4052051e+00 2.6037226e+00 3.5028446e+00 3.3009330e+00 2.9058292e+00 3.0107430e+00 3.4113341e+00 3.6081814e+00 3.1026177e+00 2.1035154e+00 2.4051766e+00 2.3058791e+00 2.5019964e+00 3.7017412e+00 3.1021847e+00 3.1038570e+00 3.3100818e+00 3.0059450e+00 2.7015162e+00 2.6035107e+00 3.0009631e+00 3.2017847e+00 2.6021247e+00 1.9231085e+00 2.8015882e+00 2.8007533e+00 2.8013565e+00 2.9028946e+00 1.6222582e+00 2.7017239e+00 4.6112605e+00 3.7050457e+00 4.5107898e+00 4.2023583e+00 4.4067851e+00 5.2146266e+00 3.1060428e+00 4.9089682e+00 4.4037570e+00 4.7173415e+00 3.7092301e+00 3.9050336e+00 4.1101936e+00 3.6083379e+00 3.7235997e+00 3.9149881e+00 4.1034584e+00 5.3169366e+00 5.5149065e+00 3.6029421e+00 4.3133953e+00 3.5091580e+00 5.3158283e+00 3.5057369e+00 4.3071174e+00 4.6095441e+00 3.4059695e+00 3.5048429e+00 4.2061215e+00 4.4109767e+00 4.7143113e+00 5.0310424e+00 4.2080636e+00 3.7018984e+00 4.2005765e+00 4.7313463e+00 4.2134002e+00 4.1029723e+00 3.4053426e+00 4.0133308e+00 4.2155438e+00 3.7272780e+00 3.7050457e+00 4.5097224e+00 4.3174320e+00 3.8199266e+00 3.6070223e+00 3.8081328e+00 4.0126677e+00 3.7034791e+00 6.0017665e-01 4.1210927e-01 6.0018299e-01 1.1133986e+00 6.3178534e-01 9.0142681e-01 8.5403486e-01 7.0462844e-01 5.0477564e-01 5.2491734e-01 1.0087252e+00 9.3306807e-01 4.1317535e-01 5.0517282e-01 4.1317535e-01 8.5409862e-01 7.5503094e-01 4.1317535e-01 8.5409862e-01 1.3133231e+00 6.0964891e-01 7.0548138e-01 1.5640758e+00 1.3027556e+00 7.0176271e-01 6.0017982e-01 9.6591433e-01 6.0000635e-01 1.1056693e+00 4.0127250e-01 7.1700909e-01 3.0056049e+00 2.8037508e+00 3.2038047e+00 2.3350905e+00 2.9043042e+00 2.8024566e+00 3.0040963e+00 1.7133143e+00 2.9021652e+00 2.2134864e+00 2.0299551e+00 2.5066443e+00 2.3464211e+00 3.0020171e+00 1.9120296e+00 2.7041827e+00 2.8038683e+00 2.4047867e+00 2.8219468e+00 2.2186306e+00 3.1079220e+00 2.3063026e+00 3.2048524e+00 3.0013665e+00 2.6029242e+00 2.7037323e+00 3.1033714e+00 3.3046348e+00 2.8041978e+00 1.8296471e+00 2.1344310e+00 2.0421601e+00 2.2088481e+00 3.4030563e+00 2.8038694e+00 2.8056176e+00 3.0035303e+00 2.7166861e+00 2.4032760e+00 2.3173405e+00 2.7049931e+00 2.9020943e+00 2.3107074e+00 1.7540491e+00 2.5057744e+00 2.5017294e+00 2.5032614e+00 2.6027348e+00 1.4738792e+00 2.4051328e+00 4.3150879e+00 3.4081972e+00 4.2065765e+00 3.9027794e+00 4.1082339e+00 4.9060122e+00 2.8144592e+00 4.6029848e+00 4.1031680e+00 4.4149701e+00 3.4105998e+00 3.6062867e+00 3.8091132e+00 3.3145497e+00 3.4354252e+00 3.6203140e+00 3.8031346e+00 5.0073650e+00 5.2071845e+00 3.3100796e+00 4.0129286e+00 3.2145637e+00 5.0059527e+00 3.2079238e+00 4.0069158e+00 4.3033003e+00 3.1086424e+00 3.2069551e+00 3.9078313e+00 4.1030253e+00 4.4053246e+00 4.7120702e+00 3.9105941e+00 3.4019027e+00 3.9011588e+00 4.4155733e+00 3.9183552e+00 3.8030509e+00 3.1080886e+00 3.7106642e+00 3.9186175e+00 3.4279064e+00 3.4081972e+00 4.2100505e+00 4.0214626e+00 3.5236480e+00 3.3110446e+00 3.5093262e+00 3.7177968e+00 3.4052047e+00 4.1317535e-01 1.1269424e-01 5.6371422e-01 5.0084481e-01 4.5784410e-01 8.0000239e-01 4.0006662e-01 3.0017653e-01 4.0006662e-01 6.0948800e-01 7.0088627e-01 4.1210927e-01 3.0482299e-01 4.5080200e-01 7.0016860e-01 6.0184934e-01 4.1317535e-01 7.0016860e-01 8.5406674e-01 4.0002221e-01 3.0482299e-01 1.5012719e+00 7.4269314e-01 3.3818226e-01 4.0002221e-01 8.0046685e-01 1.1269424e-01 6.3165225e-01 2.0121983e-01 5.0002283e-01 3.2274206e+00 3.0066036e+00 3.4159337e+00 2.5238518e+00 3.1081931e+00 3.0018108e+00 3.2048307e+00 1.8672243e+00 3.1090333e+00 2.4088880e+00 2.1557835e+00 2.7050009e+00 2.5327574e+00 3.2021240e+00 2.1075767e+00 2.9175658e+00 3.0027966e+00 2.6034860e+00 3.0173373e+00 2.4124132e+00 3.3060311e+00 2.5063233e+00 3.4049933e+00 3.2016467e+00 2.8074882e+00 2.9128790e+00 3.3135401e+00 3.5094636e+00 3.0034944e+00 2.0185049e+00 2.3226176e+00 2.2272636e+00 2.4061715e+00 3.6025233e+00 3.0027816e+00 3.0045159e+00 3.2117473e+00 2.9147768e+00 2.6022650e+00 2.5117111e+00 2.9035536e+00 3.1022707e+00 2.5074705e+00 1.8959538e+00 2.7040250e+00 2.7012715e+00 2.7023320e+00 2.8040244e+00 1.6015419e+00 2.6035923e+00 4.5125057e+00 3.6062867e+00 4.4120448e+00 4.1027436e+00 4.3076126e+00 5.1160601e+00 3.0101869e+00 4.8099407e+00 4.3047534e+00 4.6192050e+00 3.6105351e+00 3.8061121e+00 4.0115220e+00 3.5110245e+00 3.6271576e+00 3.8169672e+00 4.0039490e+00 5.2185416e+00 5.4163883e+00 3.5078417e+00 4.2149895e+00 3.4109302e+00 5.2173835e+00 3.4072913e+00 4.2079670e+00 4.5106052e+00 3.3073674e+00 3.4056857e+00 4.1070396e+00 4.3122866e+00 4.6159499e+00 4.9341410e+00 4.1092167e+00 3.6024856e+00 4.1011078e+00 4.6347105e+00 4.1150272e+00 4.0033723e+00 3.3063033e+00 3.9150644e+00 4.1174503e+00 3.6310621e+00 3.6062867e+00 4.4108290e+00 4.2194920e+00 3.7226812e+00 3.5095242e+00 3.7093173e+00 3.9142888e+00 3.6040604e+00 3.4342562e-01 8.5406616e-01 3.3813251e-01 6.0017665e-01 4.5078948e-01 4.0125062e-01 2.2573593e-01 3.0474106e-01 7.0008584e-01 6.0184622e-01 2.2538848e-01 7.0017011e-01 8.0046685e-01 5.0477564e-01 5.2167208e-01 4.0004442e-01 5.0477564e-01 1.0016896e+00 3.0474106e-01 4.5080200e-01 1.1531953e+00 1.0008617e+00 4.5080200e-01 4.1420960e-01 6.1119558e-01 4.1210927e-01 8.0051036e-01 3.0482299e-01 4.1210927e-01 3.0158412e+00 2.8068284e+00 3.2097099e+00 2.3108757e+00 2.9065890e+00 2.8022000e+00 3.0066661e+00 1.6225614e+00 2.9048017e+00 2.2116239e+00 1.8685354e+00 2.5096708e+00 2.3100354e+00 3.0026666e+00 1.9136652e+00 2.7108290e+00 2.8056165e+00 2.4010446e+00 2.8094419e+00 2.2042326e+00 3.1114445e+00 2.3060252e+00 3.2036636e+00 3.0010404e+00 2.6048201e+00 2.7083838e+00 3.1074856e+00 3.3083888e+00 2.8056953e+00 1.8055938e+00 2.1074903e+00 2.0078120e+00 2.2044098e+00 3.4034897e+00 2.8056164e+00 2.8086638e+00 3.0080453e+00 2.7058598e+00 2.4044765e+00 2.3071636e+00 2.7018643e+00 2.9031229e+00 2.3040302e+00 1.6345914e+00 2.5039390e+00 2.5021287e+00 2.5037126e+00 2.6035547e+00 1.3485963e+00 2.4045982e+00 4.3195471e+00 3.4105069e+00 4.2110205e+00 3.9039624e+00 4.1112641e+00 4.9115281e+00 2.8134622e+00 4.6064153e+00 4.1040152e+00 4.4216160e+00 3.4153329e+00 3.6083651e+00 3.8136432e+00 3.3170079e+00 3.4454804e+00 3.6271127e+00 3.8048208e+00 5.0136911e+00 5.2125102e+00 3.3041886e+00 4.0185527e+00 3.2193583e+00 5.0117789e+00 3.2102572e+00 4.0101485e+00 4.3071174e+00 3.1116727e+00 3.2099137e+00 3.9105756e+00 4.1073261e+00 4.4108290e+00 4.7236350e+00 3.9141183e+00 3.4025037e+00 3.9008223e+00 4.4275986e+00 3.9240716e+00 3.8046112e+00 3.1114731e+00 3.7165788e+00 3.9250546e+00 3.4397950e+00 3.4105069e+00 4.2141201e+00 4.0283725e+00 3.5323888e+00 3.3126331e+00 3.5133654e+00 3.7236064e+00 3.4073749e+00 5.6371422e-01 4.0125062e-01 4.2362917e-01 7.0008735e-01 3.0017653e-01 2.2573593e-01 3.0490481e-01 5.2167829e-01 6.0202028e-01 3.3808272e-01 4.1210927e-01 5.2167829e-01 6.0201716e-01 5.0477564e-01 4.0363334e-01 6.0201716e-01 7.8895472e-01 3.0474106e-01 2.2608083e-01 1.4017696e+00 7.1636719e-01 2.2608083e-01 4.0002221e-01 7.0088627e-01 2.0121983e-01 5.6371422e-01 2.2538848e-01 4.0127250e-01 3.2269400e+00 3.0055754e+00 3.4153574e+00 2.5158464e+00 3.1070003e+00 3.0010043e+00 3.2037264e+00 1.8447764e+00 3.1084925e+00 2.4051329e+00 2.1169795e+00 2.7031257e+00 2.5230194e+00 3.2014729e+00 2.1040690e+00 2.9167440e+00 3.0016616e+00 2.6020655e+00 3.0122358e+00 2.4077390e+00 3.3040889e+00 2.5044039e+00 3.4036241e+00 3.2011770e+00 2.8066054e+00 2.9119764e+00 3.3128842e+00 3.5083774e+00 3.0022542e+00 2.0112442e+00 2.3146813e+00 2.2178117e+00 2.4035997e+00 3.6016272e+00 3.0016466e+00 3.0030180e+00 3.2109724e+00 2.9107838e+00 2.6012056e+00 2.5072166e+00 2.9020943e+00 3.1016037e+00 2.5045154e+00 1.8665804e+00 2.7022828e+00 2.7006623e+00 2.7012715e+00 2.8031364e+00 1.5665127e+00 2.6019940e+00 4.5096473e+00 3.6042722e+00 4.4108394e+00 4.1020090e+00 4.3058539e+00 5.1154681e+00 3.0065585e+00 4.8095984e+00 4.3039464e+00 4.6166518e+00 3.6081814e+00 3.8045576e+00 4.0096177e+00 3.5076367e+00 3.6204901e+00 3.8129595e+00 4.0031500e+00 5.2178509e+00 5.4155855e+00 3.5053718e+00 4.2125027e+00 3.4076329e+00 5.2169557e+00 3.4052726e+00 4.2064771e+00 4.5101686e+00 3.3051940e+00 3.4039470e+00 4.1052782e+00 4.3120002e+00 4.6153660e+00 4.9336188e+00 4.1069501e+00 3.6018985e+00 4.1007374e+00 4.6331222e+00 4.1114855e+00 4.0025890e+00 3.3042986e+00 3.9129418e+00 4.1139050e+00 3.6259501e+00 3.6042722e+00 4.4088300e+00 4.2155438e+00 3.7181244e+00 3.5068261e+00 3.7072136e+00 3.9107458e+00 3.6027359e+00 7.1779518e-01 9.0005048e-01 6.8160885e-01 6.0980961e-01 6.3164977e-01 6.0964597e-01 6.0948506e-01 6.3178534e-01 8.0888055e-01 6.5712813e-01 9.1552331e-01 5.6595908e-01 4.5147187e-01 9.0026543e-01 5.6595908e-01 6.0201716e-01 5.6370994e-01 4.1212852e-01 1.3000455e+00 4.1315633e-01 6.1830764e-01 9.0511169e-01 6.0964891e-01 6.3178534e-01 4.5077696e-01 7.1621748e-01 4.5783248e-01 3.7510248e+00 3.5145413e+00 3.9319585e+00 3.0060350e+00 3.6168418e+00 3.5015800e+00 3.7092301e+00 2.3098782e+00 3.6209185e+00 2.9036019e+00 2.5319798e+00 3.2059465e+00 3.0125595e+00 3.7043511e+00 2.6050088e+00 3.4362995e+00 3.5023713e+00 3.1027850e+00 3.5112541e+00 2.9034027e+00 3.8056232e+00 3.0109656e+00 3.9070007e+00 3.7037948e+00 3.3177283e+00 3.4278716e+00 3.8279200e+00 4.0185639e+00 3.5049370e+00 2.5063489e+00 2.8048596e+00 2.7053910e+00 2.9045816e+00 4.1028806e+00 3.5020661e+00 3.5059137e+00 3.7249599e+00 3.4134555e+00 3.1021033e+00 3.0035422e+00 3.4012314e+00 3.6049329e+00 3.0042977e+00 2.3151346e+00 3.2021240e+00 3.2018065e+00 3.2023319e+00 3.3095196e+00 2.0139907e+00 3.1028259e+00 5.0111326e+00 4.1049446e+00 4.9203200e+00 4.6042055e+00 4.8089555e+00 5.6273643e+00 3.5051449e+00 5.3190012e+00 4.8083867e+00 5.1260178e+00 4.1140184e+00 4.3082037e+00 4.5172930e+00 4.0074572e+00 4.1195081e+00 4.3162103e+00 4.5071301e+00 5.7305902e+00 5.9266306e+00 4.0041352e+00 4.7202045e+00 3.9078680e+00 5.7295930e+00 3.9093593e+00 4.7117434e+00 5.0203874e+00 3.8087102e+00 3.9064537e+00 4.6081315e+00 4.8239967e+00 5.1282911e+00 5.4538016e+00 4.6097450e+00 4.1052255e+00 4.6016323e+00 5.1527860e+00 4.6133719e+00 4.5057296e+00 3.8063301e+00 4.4231469e+00 4.6192070e+00 4.1384491e+00 4.1049446e+00 4.9142249e+00 4.7202041e+00 4.2256252e+00 4.0099717e+00 4.2125085e+00 4.4124585e+00 4.1039188e+00 3.4085233e-01 3.3818226e-01 1.2699992e-01 3.0922892e-01 3.3818226e-01 4.1212852e-01 3.4085233e-01 3.0490481e-01 8.0250202e-01 9.0192695e-01 4.0363334e-01 5.0437695e-01 4.5847767e-01 4.0363334e-01 7.0633229e-01 3.0482299e-01 4.0246123e-01 1.0095513e+00 7.0548283e-01 2.0181667e-01 5.0043084e-01 3.6452132e-01 5.0436965e-01 5.0855778e-01 4.1420960e-01 3.3813251e-01 3.0360001e+00 2.8068283e+00 3.2199554e+00 2.3040302e+00 2.9083228e+00 2.8004229e+00 3.0040677e+00 1.6099792e+00 2.9111118e+00 2.2023225e+00 1.8444678e+00 2.5026969e+00 2.3072196e+00 3.0013665e+00 1.9023442e+00 2.7227152e+00 2.8012528e+00 2.4005053e+00 2.8054838e+00 2.2013444e+00 3.1036553e+00 2.3040711e+00 3.2026875e+00 3.0010106e+00 2.6084695e+00 2.7159628e+00 3.1166009e+00 3.3100796e+00 2.8019019e+00 1.8020058e+00 2.1029252e+00 2.0034861e+00 2.2011906e+00 3.4011297e+00 2.8012319e+00 2.8028007e+00 3.0142197e+00 2.7060544e+00 2.4007552e+00 2.3017471e+00 2.7003774e+00 2.9016034e+00 2.3011979e+00 1.6179000e+00 2.5007284e+00 2.5003793e+00 2.5007008e+00 2.6035320e+00 1.3154932e+00 2.4008859e+00 4.3091529e+00 3.4034897e+00 4.2123904e+00 3.9018704e+00 4.1056542e+00 4.9181670e+00 2.8038684e+00 4.6114520e+00 4.1039624e+00 4.4180514e+00 3.4084525e+00 3.6042874e+00 3.8104403e+00 3.3060311e+00 3.4198458e+00 3.6127194e+00 3.8032956e+00 5.0208687e+00 5.2178587e+00 3.3018328e+00 4.0133297e+00 3.2067991e+00 5.0200323e+00 3.2048524e+00 4.0067585e+00 4.3122441e+00 3.1047671e+00 3.2036090e+00 3.9049697e+00 4.1148052e+00 4.4184267e+00 4.7404155e+00 3.9065727e+00 3.4018864e+00 3.9004186e+00 4.4392802e+00 3.9110280e+00 3.8025990e+00 3.1038577e+00 3.7145622e+00 3.9140895e+00 3.4287259e+00 3.4034897e+00 4.2090841e+00 4.0156240e+00 3.5189468e+00 3.3056781e+00 3.5073617e+00 3.7102604e+00 3.4023494e+00 4.1315633e-01 3.0915245e-01 4.5078948e-01 5.2132556e-01 3.0482299e-01 3.3808272e-01 6.0964597e-01 7.0911112e-01 8.6051414e-01 4.1212852e-01 7.0016860e-01 7.4262964e-01 4.1212852e-01 6.1830489e-01 4.1209001e-01 6.0018299e-01 1.1056693e+00 6.0964597e-01 4.1317535e-01 4.1315633e-01 5.2133179e-01 4.2268438e-01 5.0084481e-01 5.2491131e-01 5.0043084e-01 2.9115264e+00 2.6338022e+00 3.0658361e+00 2.1172959e+00 2.7373419e+00 2.6040822e+00 2.8212040e+00 1.4407364e+00 2.7450517e+00 2.0182279e+00 1.7116683e+00 2.3196029e+00 2.1285888e+00 2.8091316e+00 1.7264084e+00 2.5863714e+00 2.6084695e+00 2.2054529e+00 2.6248910e+00 2.0083311e+00 2.9174382e+00 2.1301354e+00 3.0136614e+00 2.8068911e+00 2.4421128e+00 2.5659281e+00 2.9581275e+00 3.1380442e+00 2.6129786e+00 1.6191482e+00 1.9129988e+00 1.8141046e+00 2.0129571e+00 3.2064817e+00 2.6080847e+00 2.6171503e+00 2.8540078e+00 2.5288353e+00 2.2078337e+00 2.1116321e+00 2.5029614e+00 2.7108347e+00 2.1109969e+00 1.4620239e+00 2.3067198e+00 2.3048725e+00 2.3072196e+00 2.4221911e+00 1.1960519e+00 2.2090466e+00 4.1263920e+00 3.2146438e+00 4.0362393e+00 3.7082866e+00 3.9191966e+00 4.7434452e+00 2.6190662e+00 4.4302264e+00 3.9142233e+00 4.2488425e+00 3.2328627e+00 3.4177609e+00 3.6349468e+00 3.1232336e+00 3.2606481e+00 3.4419771e+00 3.6135028e+00 4.8484858e+00 5.0414126e+00 3.1077602e+00 3.8409517e+00 3.0264502e+00 4.8462395e+00 3.0224855e+00 3.8231767e+00 4.1339850e+00 2.9228238e+00 3.0173096e+00 3.7181009e+00 3.9409635e+00 4.2473796e+00 4.5888422e+00 3.7226114e+00 3.2097424e+00 3.7024938e+00 4.2924779e+00 3.7339169e+00 3.6111693e+00 2.9185950e+00 3.5470876e+00 3.7434043e+00 3.2896416e+00 3.2146438e+00 4.0283196e+00 3.8460162e+00 3.3616756e+00 3.1242731e+00 3.3284650e+00 3.5333785e+00 3.2109426e+00 4.0122873e-01 5.0043084e-01 4.0243965e-01 3.0474106e-01 2.0061436e-01 4.5148429e-01 1.1000100e+00 1.2012928e+00 1.2699992e-01 4.0122873e-01 5.6595488e-01 1.2699992e-01 6.0184309e-01 4.0004442e-01 5.0436965e-01 7.1700909e-01 6.0201716e-01 5.2132556e-01 8.0051115e-01 2.2573593e-01 8.0000080e-01 4.0243965e-01 7.0088477e-01 3.0474106e-01 3.1428043e+00 2.9119661e+00 3.3252402e+00 2.4048325e+00 3.0131919e+00 2.9019362e+00 3.1087162e+00 1.7043719e+00 3.0148571e+00 2.3090280e+00 1.9099608e+00 2.6089255e+00 2.4039780e+00 3.1034773e+00 2.0109328e+00 2.8295063e+00 2.9047982e+00 2.5011632e+00 2.9079901e+00 2.3019339e+00 3.2101766e+00 2.4088882e+00 3.3051149e+00 3.1020645e+00 2.7127201e+00 2.8219255e+00 3.2211370e+00 3.4154432e+00 2.9057760e+00 1.9031964e+00 2.2023924e+00 2.1016800e+00 2.3040177e+00 3.5033831e+00 2.9047500e+00 2.9083094e+00 3.1195526e+00 2.8078790e+00 2.5037817e+00 2.4045555e+00 2.8012577e+00 3.0040677e+00 2.4032886e+00 1.7053664e+00 2.6031367e+00 2.6019751e+00 2.6032655e+00 2.7067267e+00 1.4186217e+00 2.5039390e+00 4.4180854e+00 3.5092124e+00 4.3179254e+00 4.0043989e+00 4.2115634e+00 5.0223341e+00 2.9108451e+00 4.7142986e+00 4.2064794e+00 4.5276385e+00 3.5169841e+00 3.7092301e+00 3.9177719e+00 3.4145764e+00 3.5398518e+00 3.7257231e+00 3.9064409e+00 5.1255523e+00 5.3223081e+00 3.4028319e+00 4.1224590e+00 3.3167394e+00 5.1238108e+00 3.3110167e+00 4.1123595e+00 4.4156295e+00 3.2116669e+00 3.3094499e+00 4.0106882e+00 4.2180724e+00 4.5227110e+00 4.8462592e+00 4.0138270e+00 3.5038530e+00 4.0010263e+00 4.5481710e+00 4.0222346e+00 3.9055783e+00 3.2104685e+00 3.8231767e+00 4.0259306e+00 3.5470873e+00 3.5092124e+00 4.3162096e+00 4.1285334e+00 3.6344358e+00 3.4126369e+00 3.6148618e+00 3.8215373e+00 3.5066394e+00 2.2608083e-01 2.4170870e-01 3.0915245e-01 3.0915245e-01 4.0002221e-01 7.0096858e-01 8.0888055e-01 3.3818226e-01 4.0243965e-01 5.0477564e-01 3.3818226e-01 6.1135434e-01 2.0121983e-01 3.0017653e-01 1.1020506e+00 6.0219099e-01 2.0061436e-01 4.1210927e-01 4.0246123e-01 4.0125062e-01 4.0363334e-01 3.4085233e-01 2.2573593e-01 3.1414744e+00 2.9090612e+00 3.3237063e+00 2.4058952e+00 3.0107723e+00 2.9007492e+00 3.1056084e+00 1.7139238e+00 3.0138400e+00 2.3035512e+00 1.9525301e+00 2.6040007e+00 2.4100386e+00 3.1020779e+00 2.0037730e+00 2.8273039e+00 2.9018637e+00 2.5009579e+00 2.9077468e+00 2.3022754e+00 3.2048985e+00 2.4059812e+00 3.3038272e+00 3.1015567e+00 2.7110304e+00 2.8196992e+00 3.2199879e+00 3.4126307e+00 2.9028597e+00 1.9035634e+00 2.2044603e+00 2.1052067e+00 2.3021298e+00 3.5016873e+00 2.9018153e+00 2.9040211e+00 3.1174676e+00 2.8083845e+00 2.5012694e+00 2.4028385e+00 2.8006965e+00 3.0024198e+00 2.4021168e+00 1.7233814e+00 2.6012647e+00 2.6007117e+00 2.6012056e+00 2.7050190e+00 1.4220898e+00 2.5015263e+00 4.4109742e+00 3.5045842e+00 4.3148937e+00 4.0025815e+00 4.2071337e+00 5.0208677e+00 2.9053047e+00 4.7134688e+00 4.2051335e+00 4.5213131e+00 3.5107904e+00 3.7056862e+00 3.9129303e+00 3.4076849e+00 3.5232606e+00 3.7154827e+00 3.9043905e+00 5.1238111e+00 5.3204854e+00 3.4027174e+00 4.1161770e+00 3.3085473e+00 5.1228006e+00 3.3065392e+00 4.1085246e+00 4.4144908e+00 3.2064337e+00 3.3048953e+00 4.0063737e+00 4.2173565e+00 4.5213359e+00 4.8449108e+00 4.0082529e+00 3.5026815e+00 4.0006690e+00 4.5442582e+00 4.0132894e+00 3.9035247e+00 3.2051958e+00 3.8177289e+00 4.0170262e+00 3.5340950e+00 3.5045842e+00 4.3111747e+00 4.1186693e+00 3.6228903e+00 3.4075338e+00 3.6094379e+00 3.8124787e+00 3.5031928e+00 1.1269424e-01 5.0436965e-01 4.5078948e-01 2.2573593e-01 6.0000317e-01 7.0088477e-01 4.1210927e-01 3.4080442e-01 3.0474106e-01 4.1210927e-01 8.0883841e-01 1.1269424e-01 2.2573593e-01 1.2089253e+00 8.0051036e-01 4.0125062e-01 4.1317535e-01 5.2133802e-01 3.0017653e-01 6.0184622e-01 2.0061436e-01 2.2573593e-01 3.2211375e+00 3.0065592e+00 3.4126307e+00 2.5097024e+00 3.1069750e+00 3.0016616e+00 3.2056674e+00 1.8201133e+00 3.1066333e+00 2.4080686e+00 2.0619186e+00 2.7068820e+00 2.5107632e+00 3.2022485e+00 2.1087015e+00 2.9137660e+00 3.0040552e+00 2.6010528e+00 3.0089164e+00 2.4039766e+00 3.3085605e+00 2.5050799e+00 3.4035383e+00 3.2010814e+00 2.8057187e+00 2.9102474e+00 3.3101488e+00 3.5088135e+00 3.0043212e+00 2.0051350e+00 2.3071665e+00 2.2078183e+00 2.4034084e+00 3.6027901e+00 3.0040510e+00 3.0064311e+00 3.2097125e+00 2.9065741e+00 2.6030847e+00 2.5057763e+00 2.9016034e+00 3.1025924e+00 2.5033702e+00 1.8310475e+00 2.7029487e+00 2.7015162e+00 2.7026433e+00 2.8034238e+00 1.5358653e+00 2.6032968e+00 4.5159027e+00 3.6080734e+00 4.4115652e+00 4.1033603e+00 4.3094200e+00 5.1138780e+00 3.0100866e+00 4.8082318e+00 4.3041941e+00 4.6203464e+00 3.6127197e+00 3.8070338e+00 4.0124958e+00 3.5130651e+00 3.6349183e+00 3.8215362e+00 4.0044029e+00 5.2162094e+00 5.4145184e+00 3.5039680e+00 4.2166537e+00 3.4145702e+00 5.2146334e+00 3.4083285e+00 4.2090727e+00 4.5089181e+00 3.3091123e+00 3.4076329e+00 4.1087140e+00 4.3098021e+00 4.6133860e+00 4.9287684e+00 4.1115100e+00 3.6023704e+00 4.1007820e+00 4.6309835e+00 4.1192351e+00 4.0040240e+00 3.3086516e+00 3.9156759e+00 4.1209257e+00 3.6344375e+00 3.6080734e+00 4.4124586e+00 4.2235561e+00 3.7268101e+00 3.5102374e+00 3.7111714e+00 3.9185866e+00 3.6056580e+00 5.0084481e-01 4.1315633e-01 2.2573593e-01 7.0000303e-01 8.0046605e-01 3.3818226e-01 2.4170870e-01 3.0017653e-01 3.3818226e-01 8.0245824e-01 1.1269424e-01 2.0181667e-01 1.1133897e+00 8.0004523e-01 4.0246123e-01 5.2167829e-01 4.5078948e-01 4.0125062e-01 6.0017665e-01 3.0017653e-01 2.0061436e-01 3.3182813e+00 3.1056085e+00 3.5110034e+00 2.6060742e+00 3.2059465e+00 3.1013637e+00 3.3048926e+00 1.9099680e+00 3.2056789e+00 2.5063346e+00 2.1344310e+00 2.8057704e+00 2.6059875e+00 3.3019214e+00 2.2068463e+00 3.0117188e+00 3.1034568e+00 2.7006623e+00 3.1063566e+00 2.5023073e+00 3.4074246e+00 2.6040822e+00 3.5028878e+00 3.3008913e+00 2.9048017e+00 3.0087102e+00 3.4087684e+00 3.6077011e+00 3.1036688e+00 2.1027637e+00 2.4039664e+00 2.3040177e+00 2.5024922e+00 3.7023994e+00 3.1034532e+00 3.1054994e+00 3.3083865e+00 3.0045919e+00 2.7025560e+00 2.6039937e+00 3.0011260e+00 3.2022183e+00 2.6023240e+00 1.9156198e+00 2.8022964e+00 2.8012577e+00 2.8021795e+00 2.9028597e+00 1.6190551e+00 2.7026433e+00 4.6143249e+00 3.7070367e+00 4.5103890e+00 4.2029881e+00 4.4084395e+00 5.2126509e+00 3.1082889e+00 4.9074567e+00 4.4036930e+00 4.7183734e+00 3.7111657e+00 3.9061763e+00 4.1111077e+00 3.6112621e+00 3.7306950e+00 3.9190472e+00 4.1039096e+00 5.3148048e+00 5.5132902e+00 3.6028435e+00 4.3148929e+00 3.5126667e+00 5.3133599e+00 3.5071916e+00 4.3081091e+00 4.6080296e+00 3.4078683e+00 3.5066415e+00 4.2077543e+00 4.4087821e+00 4.7120754e+00 5.0261502e+00 4.2102487e+00 3.7020547e+00 4.2006494e+00 4.7279956e+00 4.2171591e+00 4.1035746e+00 3.4074978e+00 4.0138994e+00 4.2186688e+00 3.7302926e+00 3.7070367e+00 4.5111938e+00 4.3210760e+00 3.8236433e+00 3.6087856e+00 3.8098356e+00 4.0164852e+00 3.7049593e+00 1.1269424e-01 7.0017011e-01 9.0506343e-01 1.0426636e+00 2.0181667e-01 4.1209001e-01 8.0093081e-01 2.0181667e-01 3.4080442e-01 4.0125062e-01 3.6259865e-01 9.0029064e-01 3.3808272e-01 4.2268438e-01 6.1135434e-01 2.2608083e-01 6.0948212e-01 2.0061436e-01 6.3164977e-01 3.0482299e-01 3.1902650e+00 2.9267417e+00 3.3545239e+00 2.4065479e+00 3.0300492e+00 2.9028462e+00 3.1166330e+00 1.7073273e+00 3.0369978e+00 2.3091363e+00 1.9242178e+00 2.6129479e+00 2.4148785e+00 3.1074477e+00 2.0138888e+00 2.8680310e+00 2.9053048e+00 2.5042875e+00 2.9165009e+00 2.3038195e+00 3.2116668e+00 2.4221589e+00 3.3110857e+00 3.1060464e+00 2.7333589e+00 2.8520935e+00 3.2480481e+00 3.4313924e+00 2.9094538e+00 1.9103596e+00 2.2042535e+00 2.1040084e+00 2.3086427e+00 3.5048916e+00 2.9048754e+00 2.9119661e+00 3.1440058e+00 2.8212158e+00 2.5048147e+00 2.4054865e+00 2.8016296e+00 3.0087060e+00 2.4071454e+00 1.7108740e+00 2.6040234e+00 2.6035022e+00 2.6047905e+00 2.7176633e+00 1.4222462e+00 2.5057847e+00 4.4195581e+00 3.5098289e+00 4.3311360e+00 4.0067587e+00 4.2149806e+00 5.0390074e+00 2.9109559e+00 4.7273232e+00 4.2124122e+00 4.5405876e+00 3.5250718e+00 3.7139027e+00 3.9284285e+00 3.4150426e+00 3.5404383e+00 3.7302923e+00 3.9113385e+00 5.1434691e+00 5.3373009e+00 3.4049076e+00 4.1330547e+00 3.3170101e+00 5.1417717e+00 3.3168869e+00 4.1189487e+00 4.4302240e+00 3.2165105e+00 3.3123688e+00 4.0139003e+00 4.2362095e+00 4.5418862e+00 4.8784553e+00 4.0170271e+00 3.5083268e+00 4.0022121e+00 4.5797332e+00 4.0245435e+00 3.9092247e+00 3.2127499e+00 3.8383398e+00 4.0332236e+00 3.5687055e+00 3.5098289e+00 4.3229228e+00 4.1350002e+00 3.6463113e+00 3.4177609e+00 3.6219569e+00 3.8236419e+00 3.5076152e+00 6.0202028e-01 1.0008471e+00 1.1133984e+00 1.2085435e-01 4.0125062e-01 7.0548138e-01 1.2085435e-01 4.1210927e-01 3.3813251e-01 4.1317535e-01 8.0093160e-01 4.1210927e-01 4.5147187e-01 7.0184453e-01 2.0121983e-01 7.0088326e-01 2.2573593e-01 6.3164977e-01 2.4170870e-01 3.1712556e+00 2.9203033e+00 3.3425812e+00 2.4054865e+00 3.0228150e+00 2.9023686e+00 3.1131138e+00 1.7053664e+00 3.0276460e+00 2.3090554e+00 1.9156198e+00 2.6109872e+00 2.4094445e+00 3.1056085e+00 2.0122722e+00 2.8520934e+00 2.9050277e+00 2.5027053e+00 2.9125192e+00 2.3027405e+00 3.2109394e+00 2.4160415e+00 3.3084146e+00 3.1042008e+00 2.7243956e+00 2.8394100e+00 3.2369546e+00 3.4247119e+00 2.9077087e+00 1.9065546e+00 2.2031052e+00 2.1025721e+00 2.3063026e+00 3.5041732e+00 2.9047982e+00 2.9102300e+00 3.1338083e+00 2.8152056e+00 2.5042498e+00 2.4049187e+00 2.8014068e+00 3.0065584e+00 2.4051787e+00 1.7073273e+00 2.6035320e+00 2.6027034e+00 2.6039922e+00 2.7127202e+00 1.4197348e+00 2.5048165e+00 4.4189015e+00 3.5095163e+00 4.3257988e+00 4.0057069e+00 4.2135049e+00 5.0324949e+00 2.9108796e+00 4.7221364e+00 4.2099109e+00 4.5353940e+00 3.5215862e+00 3.7118544e+00 3.9240013e+00 3.4147901e+00 3.5401421e+00 3.7282909e+00 3.9092247e+00 5.1365094e+00 5.3314710e+00 3.4038679e+00 4.1286955e+00 3.3168613e+00 5.1347955e+00 3.3142720e+00 4.1161770e+00 4.4243750e+00 3.2143132e+00 3.3110162e+00 4.0124921e+00 4.2289511e+00 4.5343165e+00 4.8661278e+00 4.0156242e+00 3.5063341e+00 4.0016595e+00 4.5675358e+00 4.0235142e+00 3.9076269e+00 3.2116668e+00 3.8321137e+00 4.0301568e+00 3.5598554e+00 3.5095163e+00 4.3201293e+00 4.1322798e+00 3.6413274e+00 3.4154677e+00 3.6188977e+00 3.8226861e+00 3.5071388e+00 7.0096708e-01 8.0004602e-01 5.0855077e-01 4.1420960e-01 2.2608083e-01 5.0855077e-01 1.0008768e+00 3.0474106e-01 4.0127250e-01 1.1527746e+00 1.0000457e+00 4.0127250e-01 4.5783248e-01 6.0948800e-01 4.1317535e-01 8.0008964e-01 3.0482299e-01 4.0127250e-01 3.2104685e+00 3.0024155e+00 3.4059092e+00 2.5048145e+00 3.1026586e+00 3.0005260e+00 3.2022151e+00 1.8108200e+00 3.1025924e+00 2.4028974e+00 2.0417688e+00 2.7025751e+00 2.5062865e+00 3.2007416e+00 2.1027376e+00 2.9057769e+00 3.0015388e+00 2.6003213e+00 3.0043084e+00 2.4017233e+00 3.3039365e+00 2.5015263e+00 3.4013673e+00 3.2002931e+00 2.8019179e+00 2.9040262e+00 3.3045112e+00 3.5038551e+00 3.0015958e+00 2.0020172e+00 2.3035509e+00 2.2041009e+00 2.4010446e+00 3.6011254e+00 3.0015387e+00 3.0025850e+00 3.2040849e+00 2.9029297e+00 2.6009614e+00 2.5022969e+00 2.9005699e+00 3.1008537e+00 2.5011717e+00 1.8179820e+00 2.7009799e+00 2.7004102e+00 2.7008225e+00 2.8010266e+00 1.5160787e+00 2.6010449e+00 4.5093548e+00 3.6039076e+00 4.4060847e+00 4.1014985e+00 4.3050076e+00 5.1081747e+00 3.0045274e+00 4.8044751e+00 4.3019076e+00 4.6117611e+00 3.6062405e+00 3.8032982e+00 4.0063634e+00 3.5066398e+00 3.6202705e+00 3.8119529e+00 4.0019489e+00 5.2097659e+00 5.4087506e+00 3.5019664e+00 4.2090727e+00 3.4073888e+00 5.2088330e+00 3.4037266e+00 4.2046087e+00 4.5046939e+00 3.3041077e+00 3.4034672e+00 4.1044794e+00 4.3051857e+00 4.6074982e+00 4.9181673e+00 4.1061528e+00 3.6008588e+00 4.1002763e+00 4.6187512e+00 4.1110303e+00 4.0017844e+00 3.3039580e+00 3.9080413e+00 4.1118167e+00 3.6188744e+00 3.6039076e+00 4.4067826e+00 4.2136946e+00 3.7147052e+00 3.5048712e+00 3.7054764e+00 3.9103830e+00 3.6025973e+00 3.0026460e-01 1.0001598e+00 9.0029064e-01 6.0202028e-01 1.0001598e+00 1.1281352e+00 7.0000303e-01 6.0052920e-01 1.8012962e+00 9.6574369e-01 6.3178782e-01 4.2270142e-01 1.1005460e+00 3.0026460e-01 9.1422402e-01 4.0004442e-01 8.0004602e-01 3.2225450e+00 3.0091788e+00 3.4142737e+00 2.5659206e+00 3.1121190e+00 3.0065741e+00 3.2080663e+00 1.9795481e+00 3.1095927e+00 2.4291135e+00 2.3151880e+00 2.7129011e+00 2.5826416e+00 3.2051685e+00 2.1273517e+00 2.9165009e+00 3.0077149e+00 2.6132476e+00 3.0422268e+00 2.4403272e+00 3.3124044e+00 2.5166989e+00 3.4115621e+00 3.2044340e+00 2.8105357e+00 2.9137358e+00 3.3135329e+00 3.5115123e+00 3.0089448e+00 2.0634477e+00 2.3669955e+00 2.2798676e+00 2.4222743e+00 3.6065353e+00 3.0077107e+00 3.0095641e+00 3.2119165e+00 2.9352117e+00 2.6080595e+00 2.5371436e+00 2.9126008e+00 3.1051604e+00 2.5254396e+00 2.0316239e+00 2.7143597e+00 2.7050980e+00 2.7084026e+00 2.8082597e+00 1.7626307e+00 2.6129786e+00 4.5202811e+00 3.6136284e+00 4.4137920e+00 4.1051791e+00 4.3125133e+00 5.1149745e+00 3.0264326e+00 4.8090821e+00 4.3074243e+00 4.6242424e+00 3.6169385e+00 3.8113313e+00 4.0160029e+00 3.5235168e+00 3.6464145e+00 3.8279938e+00 4.0062032e+00 5.2173409e+00 5.4162239e+00 3.5202520e+00 4.2206877e+00 3.4219359e+00 5.2156030e+00 3.4146242e+00 4.2116109e+00 4.5097900e+00 3.3150972e+00 3.4115327e+00 4.1123767e+00 4.3106068e+00 4.6148396e+00 4.9296764e+00 4.1159139e+00 3.6049049e+00 4.1030781e+00 4.6336876e+00 4.1247374e+00 4.0056652e+00 3.3131419e+00 3.9194404e+00 4.1265834e+00 3.6428011e+00 3.6136284e+00 4.4157045e+00 4.2295866e+00 3.7344540e+00 3.5196603e+00 3.7152542e+00 3.9242137e+00 3.6086340e+00 1.1055707e+00 1.0030868e+00 7.0000151e-01 1.1055707e+00 1.3018102e+00 8.0245824e-01 7.1621884e-01 1.9078389e+00 1.1896594e+00 7.2044167e-01 5.3943256e-01 1.2089192e+00 4.5147187e-01 1.0776188e+00 5.0043084e-01 9.0506254e-01 3.3079985e+00 3.1046072e+00 3.5056118e+00 2.6709345e+00 3.2081329e+00 3.1065948e+00 3.3043807e+00 2.0901204e+00 3.2052035e+00 2.5276373e+00 2.4267777e+00 2.8091158e+00 2.6904888e+00 3.3041886e+00 2.2241050e+00 3.0065909e+00 3.1056084e+00 2.7155799e+00 3.1440950e+00 2.5451785e+00 3.4078458e+00 2.6152930e+00 3.5111169e+00 3.3045112e+00 2.9070941e+00 3.0065909e+00 3.4069929e+00 3.6059670e+00 3.1068940e+00 2.1702441e+00 2.4737523e+00 2.3880607e+00 2.5238437e+00 3.7056514e+00 3.1056084e+00 3.1055129e+00 3.3051256e+00 3.0372254e+00 2.7067267e+00 2.6397031e+00 3.0142197e+00 3.2037566e+00 2.6278605e+00 2.1424915e+00 2.8148768e+00 2.8047553e+00 2.8077256e+00 2.9066659e+00 1.8682644e+00 2.7127202e+00 4.6142218e+00 3.7103348e+00 4.5074842e+00 4.2035307e+00 4.4083412e+00 5.2074280e+00 3.1239158e+00 4.9041928e+00 4.4055872e+00 4.7149533e+00 3.7103439e+00 3.9081758e+00 4.1095835e+00 3.6188977e+00 3.7328455e+00 3.9187196e+00 4.1037709e+00 5.3087451e+00 5.5089283e+00 3.6218973e+00 4.3127798e+00 3.5155554e+00 5.3076917e+00 3.5109043e+00 4.3070065e+00 4.6043043e+00 3.4107931e+00 3.5076367e+00 4.2085644e+00 4.4044360e+00 4.7071579e+00 5.0144130e+00 4.2110565e+00 3.7038321e+00 4.2031939e+00 4.7176313e+00 4.2169539e+00 4.1034568e+00 3.4087523e+00 4.0110693e+00 4.2176552e+00 3.7262659e+00 3.7103348e+00 4.5099841e+00 4.3199893e+00 3.8223318e+00 3.6159248e+00 3.8096375e+00 4.0163546e+00 3.7058422e+00 3.0026460e-01 6.0964891e-01 0.0000000e+00 5.0043842e-01 3.0482299e-01 4.0246123e-01 8.0254500e-01 5.0043842e-01 5.2133802e-01 7.0556260e-01 2.0181667e-01 7.0008735e-01 3.0026460e-01 6.0948506e-01 2.0181667e-01 3.2490712e+00 3.0153168e+00 3.4297841e+00 2.5067523e+00 3.1166337e+00 3.0027816e+00 3.2112793e+00 1.8068048e+00 3.1183051e+00 2.4116924e+00 2.0138832e+00 2.7116615e+00 2.5059537e+00 3.2048192e+00 2.1144760e+00 2.9351753e+00 3.0063019e+00 2.6019122e+00 3.0106587e+00 2.4030297e+00 3.3125861e+00 2.5120719e+00 3.4068163e+00 3.2029877e+00 2.8162444e+00 2.9267417e+00 3.3252407e+00 3.5189464e+00 3.0077107e+00 2.0051350e+00 2.3037132e+00 2.2028146e+00 2.4058620e+00 3.6044981e+00 3.0062070e+00 3.0107283e+00 3.2237456e+00 2.9105093e+00 2.6052541e+00 2.5062865e+00 2.9018772e+00 3.1056084e+00 2.5048522e+00 1.8082911e+00 2.7043948e+00 2.7029415e+00 2.7046027e+00 2.8091099e+00 1.5248852e+00 2.6055127e+00 4.5209020e+00 3.6112573e+00 4.4212031e+00 4.1056541e+00 4.3138986e+00 5.1255338e+00 3.0133997e+00 4.8167235e+00 4.3081273e+00 4.6319211e+00 3.6205854e+00 3.8114965e+00 4.0212972e+00 3.5173798e+00 3.6449970e+00 3.8299342e+00 4.0081754e+00 5.2290121e+00 5.4254411e+00 3.5039202e+00 4.2264145e+00 3.4198378e+00 5.2270034e+00 3.4138008e+00 4.2149806e+00 4.5183778e+00 3.3145502e+00 3.4118179e+00 4.1129687e+00 4.3210760e+00 4.6261633e+00 4.9512603e+00 4.1165035e+00 3.6051692e+00 4.1014742e+00 4.6540056e+00 4.1257291e+00 4.0071257e+00 3.3129914e+00 3.9274863e+00 4.1301604e+00 3.6542046e+00 3.6112573e+00 4.4192311e+00 4.2328883e+00 3.7399948e+00 3.5155767e+00 3.7180846e+00 3.9250546e+00 3.6083191e+00 5.0436965e-01 3.0026460e-01 6.0017982e-01 3.0482299e-01 3.0017653e-01 9.0506343e-01 6.0000317e-01 4.5783248e-01 7.4269314e-01 2.4195741e-01 6.0948506e-01 4.0122873e-01 5.0855077e-01 2.0061436e-01 3.5243030e+00 3.3064692e+00 3.7147036e+00 2.8028227e+00 3.4072759e+00 3.3010449e+00 3.5048841e+00 2.1026764e+00 3.4081977e+00 2.7042302e+00 2.3098753e+00 3.0045117e+00 2.8027924e+00 3.5019450e+00 2.4045982e+00 3.2157547e+00 3.3025861e+00 2.9005886e+00 3.3047156e+00 2.7010554e+00 3.6058037e+00 2.8042692e+00 3.7029937e+00 3.5011559e+00 3.1065954e+00 3.2116669e+00 3.6121048e+00 3.8091015e+00 3.3031148e+00 2.3014285e+00 2.6014645e+00 2.5011992e+00 2.7018905e+00 3.9020189e+00 3.3025600e+00 3.3044825e+00 3.5110031e+00 3.2044340e+00 2.9018587e+00 2.8023124e+00 3.2006932e+00 3.4022345e+00 2.8016296e+00 2.1039819e+00 3.0015958e+00 3.0009948e+00 3.0016466e+00 3.1034780e+00 1.8068049e+00 2.9019412e+00 4.8119571e+00 3.9055005e+00 4.7117433e+00 4.4027872e+00 4.6074923e+00 5.4154953e+00 3.3059205e+00 5.1096877e+00 4.6042055e+00 4.9184662e+00 3.9101579e+00 4.1056576e+00 4.3111747e+00 3.8086187e+00 3.9240074e+00 4.1158326e+00 4.3040379e+00 5.5178488e+00 5.7157885e+00 3.8018678e+00 4.5144444e+00 3.7097243e+00 5.5166376e+00 3.7063915e+00 4.5079295e+00 4.8103281e+00 3.6066590e+00 3.7054748e+00 4.4067833e+00 4.6117275e+00 4.9151622e+00 5.2317566e+00 4.4087821e+00 3.9022961e+00 4.4006562e+00 4.9323259e+00 4.4141504e+00 4.3034963e+00 3.6059708e+00 4.2144276e+00 4.4165186e+00 3.9284285e+00 3.9055005e+00 4.7106152e+00 4.5183778e+00 4.0209837e+00 3.8074712e+00 4.0090032e+00 4.2134002e+00 3.9039579e+00 6.0964891e-01 1.1019501e+00 4.0125062e-01 5.0000761e-01 1.2632947e+00 1.1001012e+00 5.2491131e-01 6.1135434e-01 7.1621884e-01 4.2268438e-01 9.0026543e-01 2.4170870e-01 5.0043084e-01 3.4064574e+00 3.2033141e+00 3.6042703e+00 2.7067267e+00 3.3031847e+00 3.2012079e+00 3.4035361e+00 2.0125822e+00 3.3019706e+00 2.6055127e+00 2.2404570e+00 2.9047685e+00 2.7070958e+00 3.4014441e+00 2.3056303e+00 3.1043374e+00 3.2029748e+00 2.8006755e+00 3.2059945e+00 2.6027034e+00 3.5064150e+00 2.7028014e+00 3.6021536e+00 3.4005708e+00 3.0020583e+00 3.1034908e+00 3.5031921e+00 3.7043162e+00 3.2030081e+00 2.2031888e+00 2.5048145e+00 2.4051640e+00 2.6022353e+00 3.8020808e+00 3.2029748e+00 3.2045605e+00 3.4036086e+00 3.1036832e+00 2.8021575e+00 2.7039988e+00 3.1011640e+00 3.3016475e+00 2.7022579e+00 2.0191796e+00 2.9020892e+00 2.9010579e+00 2.9018587e+00 3.0016913e+00 1.7203066e+00 2.8022905e+00 4.7127831e+00 3.8062227e+00 4.6064153e+00 4.3024456e+00 4.5071341e+00 5.3066177e+00 3.2074507e+00 5.0034627e+00 4.5024143e+00 4.8135231e+00 3.8088290e+00 4.0049892e+00 4.2080447e+00 3.7100251e+00 3.8270894e+00 4.0163858e+00 4.2028588e+00 5.4079977e+00 5.6075453e+00 3.7029588e+00 4.4113160e+00 3.6111044e+00 5.4066727e+00 3.6058057e+00 4.4062018e+00 4.7037812e+00 3.5065182e+00 3.6056307e+00 4.3065776e+00 4.5036239e+00 4.8058364e+00 5.1131156e+00 4.3088094e+00 3.8014142e+00 4.3005452e+00 4.8156922e+00 4.3151195e+00 4.2027764e+00 3.5064276e+00 4.1095026e+00 4.3155223e+00 3.8226872e+00 3.8062227e+00 4.6088777e+00 4.4178507e+00 3.9190516e+00 3.7073875e+00 3.9078043e+00 4.1144923e+00 3.8043348e+00 5.0043842e-01 3.0482299e-01 4.0246123e-01 8.0254500e-01 5.0043842e-01 5.2133802e-01 7.0556260e-01 2.0181667e-01 7.0008735e-01 3.0026460e-01 6.0948506e-01 2.0181667e-01 3.2490712e+00 3.0153168e+00 3.4297841e+00 2.5067523e+00 3.1166337e+00 3.0027816e+00 3.2112793e+00 1.8068048e+00 3.1183051e+00 2.4116924e+00 2.0138832e+00 2.7116615e+00 2.5059537e+00 3.2048192e+00 2.1144760e+00 2.9351753e+00 3.0063019e+00 2.6019122e+00 3.0106587e+00 2.4030297e+00 3.3125861e+00 2.5120719e+00 3.4068163e+00 3.2029877e+00 2.8162444e+00 2.9267417e+00 3.3252407e+00 3.5189464e+00 3.0077107e+00 2.0051350e+00 2.3037132e+00 2.2028146e+00 2.4058620e+00 3.6044981e+00 3.0062070e+00 3.0107283e+00 3.2237456e+00 2.9105093e+00 2.6052541e+00 2.5062865e+00 2.9018772e+00 3.1056084e+00 2.5048522e+00 1.8082911e+00 2.7043948e+00 2.7029415e+00 2.7046027e+00 2.8091099e+00 1.5248852e+00 2.6055127e+00 4.5209020e+00 3.6112573e+00 4.4212031e+00 4.1056541e+00 4.3138986e+00 5.1255338e+00 3.0133997e+00 4.8167235e+00 4.3081273e+00 4.6319211e+00 3.6205854e+00 3.8114965e+00 4.0212972e+00 3.5173798e+00 3.6449970e+00 3.8299342e+00 4.0081754e+00 5.2290121e+00 5.4254411e+00 3.5039202e+00 4.2264145e+00 3.4198378e+00 5.2270034e+00 3.4138008e+00 4.2149806e+00 4.5183778e+00 3.3145502e+00 3.4118179e+00 4.1129687e+00 4.3210760e+00 4.6261633e+00 4.9512603e+00 4.1165035e+00 3.6051692e+00 4.1014742e+00 4.6540056e+00 4.1257291e+00 4.0071257e+00 3.3129914e+00 3.9274863e+00 4.1301604e+00 3.6542046e+00 3.6112573e+00 4.4192311e+00 4.2328883e+00 3.7399948e+00 3.5155767e+00 3.7180846e+00 3.9250546e+00 3.6083191e+00 7.0470720e-01 6.3164977e-01 7.0000303e-01 2.0000000e-01 6.4049114e-01 8.7212232e-01 4.0004442e-01 8.5437440e-01 2.2573593e-01 9.3308853e-01 6.0184622e-01 3.5152865e+00 3.2379971e+00 3.6729302e+00 2.7052554e+00 3.3425813e+00 3.2040843e+00 3.4230889e+00 2.0021220e+00 3.3530528e+00 2.6055127e+00 2.2051638e+00 2.9154879e+00 2.7227228e+00 3.4118179e+00 2.3143923e+00 3.1902650e+00 3.2048191e+00 2.8089346e+00 3.2223766e+00 2.6060092e+00 3.5107904e+00 2.7333577e+00 3.6167495e+00 3.4109217e+00 3.0488339e+00 3.1712556e+00 3.5658221e+00 3.7426726e+00 3.2127498e+00 2.2186491e+00 2.5049231e+00 2.4052960e+00 2.6139440e+00 3.8063158e+00 3.2036084e+00 3.2143157e+00 3.4603347e+00 3.1318587e+00 2.8056557e+00 2.7050980e+00 3.1020681e+00 3.3136174e+00 2.7116685e+00 2.0027889e+00 2.9047842e+00 2.9057760e+00 2.9065359e+00 3.0276459e+00 1.7091578e+00 2.8077256e+00 4.7169309e+00 3.8081280e+00 4.6399369e+00 4.3088507e+00 4.5162248e+00 5.3501952e+00 3.2067991e+00 5.0370912e+00 4.5175831e+00 4.8468186e+00 3.8290678e+00 4.0170262e+00 4.2347722e+00 3.7111714e+00 3.8289857e+00 4.0283196e+00 4.2155430e+00 5.4550926e+00 5.6472422e+00 3.7064735e+00 4.4381718e+00 3.6121030e+00 5.4538016e+00 3.6205857e+00 4.4231445e+00 4.7408825e+00 3.5189464e+00 3.6135011e+00 4.3151164e+00 4.5490925e+00 4.8546830e+00 5.1966534e+00 4.3173271e+00 3.8129545e+00 4.3038527e+00 4.8962803e+00 4.3214438e+00 4.2123903e+00 3.5127693e+00 4.1469662e+00 4.3342207e+00 3.8751227e+00 3.8081280e+00 4.6262568e+00 4.4345825e+00 3.9485180e+00 3.7201181e+00 3.9257254e+00 4.1203162e+00 3.8072915e+00 2.0181667e-01 1.1055799e+00 7.0016860e-01 4.0006662e-01 4.5147187e-01 4.1212852e-01 4.0002221e-01 5.0043084e-01 3.0474106e-01 1.2085435e-01 3.2280983e+00 3.0080445e+00 3.4166801e+00 2.5073304e+00 3.1087541e+00 3.0016255e+00 3.2064005e+00 1.8128544e+00 3.1091921e+00 2.4076934e+00 2.0429861e+00 2.7070770e+00 2.5077793e+00 3.2025221e+00 2.1086021e+00 2.9185909e+00 3.0040552e+00 2.6009247e+00 3.0080768e+00 2.4028385e+00 3.3086417e+00 2.5058827e+00 3.4038679e+00 3.2013290e+00 2.8077473e+00 2.9137660e+00 3.3136458e+00 3.5107924e+00 3.0045274e+00 2.0036964e+00 2.3048725e+00 2.2049827e+00 2.4032211e+00 3.6028345e+00 3.0040403e+00 3.0066661e+00 3.2127504e+00 2.9065741e+00 2.6030847e+00 2.5048249e+00 2.9013288e+00 3.1029264e+00 2.5029614e+00 1.8201043e+00 2.7027522e+00 2.7015465e+00 2.7026433e+00 2.8042851e+00 1.5256523e+00 2.6032253e+00 4.5160443e+00 3.6080467e+00 4.4135519e+00 4.1035779e+00 4.3098000e+00 5.1168009e+00 3.0096881e+00 4.8103297e+00 4.3048676e+00 4.6223708e+00 3.6136097e+00 3.8074712e+00 4.0139003e+00 3.5128896e+00 3.6349183e+00 3.8220063e+00 4.0049433e+00 5.2194303e+00 5.4171979e+00 3.5033669e+00 4.2181241e+00 3.4145410e+00 5.2178541e+00 3.4088042e+00 4.2099019e+00 4.5111938e+00 3.3094784e+00 3.4078458e+00 4.1090316e+00 4.3126257e+00 4.6165627e+00 4.9348334e+00 4.1118265e+00 3.6027619e+00 4.1008192e+00 4.6366757e+00 4.1194553e+00 4.0043991e+00 3.3087928e+00 3.9177721e+00 4.1218428e+00 3.6374332e+00 3.6080467e+00 4.4133507e+00 4.2243717e+00 3.7282925e+00 3.5105217e+00 3.7119499e+00 3.9187675e+00 3.6057072e+00 1.2012865e+00 6.0184622e-01 3.3808272e-01 6.0184934e-01 5.0043084e-01 3.3818226e-01 4.1212852e-01 3.0922892e-01 2.0121983e-01 3.4273160e+00 3.2064011e+00 3.6161317e+00 2.7056828e+00 3.3075153e+00 3.2008118e+00 3.4044261e+00 2.0113827e+00 3.3090710e+00 2.6035236e+00 2.2398619e+00 2.9035670e+00 2.7083020e+00 3.4017079e+00 2.3034787e+00 3.1174706e+00 3.2019092e+00 2.8008297e+00 3.2066700e+00 2.6023240e+00 3.5046442e+00 2.7041827e+00 3.6031099e+00 3.4011658e+00 3.0071110e+00 3.1127326e+00 3.5134157e+00 3.7092355e+00 3.2025439e+00 2.2031052e+00 2.5042875e+00 2.4048325e+00 2.6019131e+00 3.8016620e+00 3.2018790e+00 3.2036084e+00 3.4118203e+00 3.1063566e+00 2.8013151e+00 2.7029498e+00 3.1008327e+00 3.3019518e+00 2.7019892e+00 2.0181988e+00 2.9013773e+00 2.9007142e+00 2.9012240e+00 3.0034647e+00 1.7168003e+00 2.8015399e+00 4.7103355e+00 3.8044833e+00 4.6117631e+00 4.3023732e+00 4.5065283e+00 5.3163061e+00 3.2051927e+00 5.0102923e+00 4.5041827e+00 4.8177760e+00 3.8091017e+00 4.0050028e+00 4.2105704e+00 3.7073402e+00 3.8208501e+00 4.0138272e+00 4.2036880e+00 5.4187310e+00 5.6164039e+00 3.7027275e+00 4.4135513e+00 3.6080195e+00 5.4177192e+00 3.6056365e+00 4.4072751e+00 4.7109359e+00 3.5056751e+00 3.6045028e+00 4.3058539e+00 4.5127175e+00 4.8161488e+00 5.1342237e+00 4.3075896e+00 3.8021528e+00 4.3006308e+00 4.8339900e+00 4.3122441e+00 4.2030792e+00 3.5048429e+00 4.1140184e+00 4.3148937e+00 3.8271256e+00 3.8044833e+00 4.6097143e+00 4.4165186e+00 3.9191998e+00 3.7067060e+00 3.9080455e+00 4.1114854e+00 3.8031381e+00 9.0000091e-01 1.2014191e+00 1.5025345e+00 7.0088477e-01 1.5012926e+00 9.0000136e-01 1.4092540e+00 1.0030724e+00 3.4932678e+00 3.2284356e+00 3.6579694e+00 2.7029237e+00 3.3320310e+00 3.2025221e+00 3.4171529e+00 2.0008116e+00 3.3407229e+00 2.6032740e+00 2.2005685e+00 2.9103582e+00 2.7153679e+00 3.4082220e+00 2.3087475e+00 3.1706684e+00 3.2030687e+00 2.8057704e+00 3.2157547e+00 2.6035236e+00 3.5075879e+00 2.7233837e+00 3.6121030e+00 3.4076329e+00 3.0364247e+00 3.1548602e+00 3.5517240e+00 3.7328844e+00 3.2086545e+00 2.2116271e+00 2.5026949e+00 2.4028972e+00 2.6089340e+00 3.8042764e+00 3.2022967e+00 3.2108192e+00 3.4468872e+00 3.1231713e+00 2.8035152e+00 2.7029238e+00 3.1011647e+00 3.3095196e+00 2.7074599e+00 2.0008921e+00 2.9028462e+00 2.9036791e+00 2.9040745e+00 3.0197997e+00 1.7043730e+00 2.8047771e+00 4.7130344e+00 3.8056232e+00 4.6318991e+00 4.3063824e+00 4.5121922e+00 5.3416858e+00 3.2045522e+00 5.0302179e+00 4.5134527e+00 4.8380714e+00 3.8218532e+00 4.0124930e+00 4.2269486e+00 3.7079008e+00 3.8220112e+00 4.0214147e+00 4.2115853e+00 5.4465234e+00 5.6393868e+00 3.7043105e+00 4.4299700e+00 3.6085945e+00 5.4449998e+00 3.6148636e+00 4.4178341e+00 4.7331065e+00 3.5134671e+00 3.6094821e+00 4.3111775e+00 4.5398308e+00 4.8449119e+00 5.1826640e+00 4.3129030e+00 3.8093617e+00 4.3026669e+00 4.8806417e+00 4.3164776e+00 4.2091204e+00 3.5088587e+00 4.1368711e+00 4.3264627e+00 3.8592376e+00 3.8056232e+00 4.6204067e+00 4.4269727e+00 3.9374314e+00 3.7145622e+00 3.9192263e+00 4.1154804e+00 3.8050056e+00 6.1288055e-01 7.7603846e-01 4.0127250e-01 7.4329414e-01 2.0061436e-01 9.0508712e-01 6.0000635e-01 3.5152864e+00 3.2379970e+00 3.6729302e+00 2.7058598e+00 3.3425838e+00 3.2040874e+00 3.4230885e+00 2.0034894e+00 3.3530533e+00 2.6055423e+00 2.2123846e+00 2.9154880e+00 2.7237438e+00 3.4118184e+00 2.3143951e+00 3.1902650e+00 3.2048192e+00 2.8089552e+00 3.2228316e+00 2.6061975e+00 3.5107904e+00 2.7333644e+00 3.6167885e+00 3.4109240e+00 3.0488346e+00 3.1712557e+00 3.5658240e+00 3.7426726e+00 3.2127504e+00 2.2188249e+00 2.5053901e+00 2.4058634e+00 2.6139732e+00 3.8063206e+00 3.2036085e+00 3.2143126e+00 3.4603347e+00 3.1321581e+00 2.8056558e+00 2.7052554e+00 3.1021033e+00 3.3136175e+00 2.7117356e+00 2.0053411e+00 2.9048017e+00 2.9057761e+00 2.9065368e+00 3.0276467e+00 1.7105814e+00 2.8077315e+00 4.7169308e+00 3.8081328e+00 4.6399369e+00 4.3088509e+00 4.5162248e+00 5.3501952e+00 3.2068686e+00 5.0370912e+00 4.5175965e+00 4.8468145e+00 3.8290678e+00 4.0170299e+00 4.2347722e+00 3.7112059e+00 3.8289870e+00 4.0283196e+00 4.2155430e+00 5.4550814e+00 5.6472442e+00 3.7067060e+00 4.4381718e+00 3.6121048e+00 5.4538018e+00 3.6205918e+00 4.4231444e+00 4.7408825e+00 3.5189484e+00 3.6135011e+00 4.3151171e+00 4.5490925e+00 4.8546834e+00 5.1966394e+00 4.3173279e+00 3.8129558e+00 4.3038600e+00 4.8962803e+00 4.3214431e+00 4.2123903e+00 3.5127694e+00 4.1469662e+00 4.3342207e+00 3.8751227e+00 3.8081328e+00 4.6262568e+00 4.4345823e+00 3.9485180e+00 3.7201522e+00 3.9257254e+00 4.1203153e+00 3.8072915e+00 3.4085233e-01 5.0517282e-01 4.1210927e-01 4.5847767e-01 4.1317535e-01 4.0243965e-01 3.1409605e+00 2.9078360e+00 3.3230621e+00 2.4077390e+00 3.0097979e+00 2.9003941e+00 3.1042002e+00 1.7227897e+00 3.0135090e+00 2.3017319e+00 1.9755996e+00 2.6019350e+00 2.4142039e+00 3.1015567e+00 2.0014192e+00 2.8264551e+00 2.9006366e+00 2.5011717e+00 2.9082658e+00 2.3033727e+00 3.2022157e+00 2.4051094e+00 3.3034165e+00 3.1014454e+00 2.7104802e+00 2.8188502e+00 3.2195779e+00 3.4112824e+00 2.9016560e+00 1.9053006e+00 2.2068965e+00 2.1085395e+00 2.3018945e+00 3.5009585e+00 2.9005880e+00 2.9020766e+00 3.1165913e+00 2.8092629e+00 2.5004154e+00 2.4029432e+00 2.8007533e+00 3.0017918e+00 2.4022355e+00 1.7369589e+00 2.6007937e+00 2.6003442e+00 2.6005344e+00 2.7044628e+00 1.4329832e+00 2.5008032e+00 4.4064393e+00 3.5021596e+00 4.3131639e+00 4.0016670e+00 4.2045223e+00 5.0200323e+00 2.9028411e+00 4.7130517e+00 4.2044861e+00 4.5172866e+00 3.5073617e+00 3.7038321e+00 3.9101623e+00 3.4039470e+00 3.5128093e+00 3.7092301e+00 3.9033549e+00 5.1227972e+00 5.3193887e+00 3.4029615e+00 4.1123595e+00 3.3040255e+00 5.1222477e+00 3.3043123e+00 4.1063328e+00 4.4139149e+00 3.2038047e+00 3.3025879e+00 4.0039164e+00 4.2170349e+00 4.5206139e+00 4.8441812e+00 4.0049702e+00 3.5022104e+00 4.0005674e+00 4.5418878e+00 4.0077003e+00 3.9024858e+00 3.2025221e+00 3.8146102e+00 4.0114630e+00 3.5261349e+00 3.5021596e+00 4.3081194e+00 4.1123594e+00 3.6158324e+00 3.4049076e+00 3.6064417e+00 3.8069566e+00 3.5014492e+00 8.0923926e-01 3.0474106e-01 6.5724028e-01 4.0246123e-01 5.6371422e-01 2.8500310e+00 2.6110761e+00 3.0277528e+00 2.1510447e+00 2.7141512e+00 2.6027937e+00 2.8070667e+00 1.5787180e+00 2.7167342e+00 2.0166118e+00 1.9318287e+00 2.3071806e+00 2.1715812e+00 2.8031215e+00 1.7145956e+00 2.5336675e+00 2.6035547e+00 2.2074446e+00 2.6319765e+00 2.0282636e+00 2.9076123e+00 2.1122780e+00 3.0080768e+00 2.8027924e+00 2.4144060e+00 2.5243969e+00 2.9241685e+00 3.1150226e+00 2.6049377e+00 1.6499744e+00 1.9530826e+00 1.8666228e+00 2.0130304e+00 3.2033374e+00 2.6035250e+00 2.6059866e+00 2.8207213e+00 2.5287261e+00 2.2032583e+00 2.1244184e+00 2.5066544e+00 2.7033232e+00 2.1157724e+00 1.6395342e+00 2.3072196e+00 2.3018945e+00 2.3035850e+00 2.4072314e+00 1.3788456e+00 2.2062031e+00 4.1150297e+00 3.2079717e+00 4.0170857e+00 3.7033716e+00 3.9093672e+00 4.7228082e+00 2.6158579e+00 4.4145659e+00 3.9067187e+00 4.2256164e+00 3.2143454e+00 3.4081069e+00 3.6159248e+00 3.1148584e+00 3.2358776e+00 3.4219579e+00 3.6052692e+00 4.8260874e+00 5.0225486e+00 3.1131211e+00 3.8201108e+00 3.0142352e+00 4.8248264e+00 3.0102191e+00 3.8104451e+00 4.1158426e+00 2.9100849e+00 3.0073054e+00 3.7087638e+00 3.9191144e+00 4.2237272e+00 4.5500784e+00 3.7114840e+00 3.2036323e+00 3.7015743e+00 4.2506870e+00 3.7186993e+00 3.6043148e+00 2.9081165e+00 3.5216379e+00 3.7226348e+00 3.2449757e+00 3.2079717e+00 4.0139105e+00 3.8249612e+00 3.3311289e+00 3.1134242e+00 3.3125194e+00 3.5179632e+00 3.2049019e+00 8.0051115e-01 2.2573593e-01 7.1621884e-01 3.0482299e-01 3.3530529e+00 3.1135637e+00 3.5317001e+00 2.6021962e+00 3.2157548e+00 3.1011640e+00 3.3083865e+00 1.9014069e+00 3.2199554e+00 2.5036852e+00 2.1054816e+00 2.8056557e+00 2.6057308e+00 3.3035252e+00 2.2049636e+00 3.0369970e+00 3.1023768e+00 2.7016498e+00 3.1076516e+00 2.5011992e+00 3.4059088e+00 2.6097152e+00 3.5056297e+00 3.3028597e+00 2.9166917e+00 3.0276459e+00 3.4273156e+00 3.6176286e+00 3.1043337e+00 2.1032882e+00 2.4011650e+00 2.3009622e+00 2.5032633e+00 3.7024058e+00 3.1022094e+00 3.1056121e+00 3.3243222e+00 3.0101957e+00 2.7018643e+00 2.6020065e+00 3.0005955e+00 3.2040843e+00 2.6027120e+00 1.9019962e+00 2.8015673e+00 2.8013346e+00 2.8018959e+00 2.9083044e+00 1.6052507e+00 2.7022567e+00 4.6121162e+00 3.7052383e+00 4.5194334e+00 4.2036849e+00 4.4088275e+00 5.2263180e+00 3.1053076e+00 4.9177739e+00 4.4072684e+00 4.7260117e+00 3.7138970e+00 3.9076272e+00 4.1167962e+00 3.6081590e+00 3.7238338e+00 3.9176170e+00 4.1063328e+00 5.3296625e+00 5.5255577e+00 3.6022190e+00 4.3201293e+00 3.5092121e+00 5.3285444e+00 3.5088064e+00 4.3111749e+00 4.6192198e+00 3.4084525e+00 3.5063337e+00 4.2079639e+00 4.4229098e+00 4.7273231e+00 5.0541259e+00 4.2099019e+00 3.7043105e+00 4.2011108e+00 4.7534769e+00 4.2147199e+00 4.1050714e+00 3.4064570e+00 4.0228303e+00 4.2200398e+00 3.7407842e+00 3.7052383e+00 4.5139612e+00 4.3214432e+00 3.8271242e+00 3.6094426e+00 3.8122429e+00 4.0138280e+00 3.7039439e+00 6.3178534e-01 2.0121983e-01 5.0043084e-01 3.1326249e+00 2.9095058e+00 3.3192760e+00 2.4306373e+00 3.0110559e+00 2.9028946e+00 3.1074604e+00 1.7872757e+00 3.0111989e+00 2.3143923e+00 2.0898615e+00 2.6089349e+00 2.4398792e+00 3.1033306e+00 2.0139907e+00 2.8220753e+00 2.9050462e+00 2.5045154e+00 2.9220490e+00 2.3159976e+00 3.2100379e+00 2.4095513e+00 3.3067017e+00 3.1022615e+00 2.7099669e+00 2.8165723e+00 3.2163877e+00 3.4125151e+00 2.9058641e+00 1.9245956e+00 2.2287984e+00 2.1344529e+00 2.3089795e+00 3.5039202e+00 2.9050287e+00 2.9078401e+00 3.1149135e+00 2.8183214e+00 2.5042875e+00 2.4160514e+00 2.8047612e+00 3.0036601e+00 2.4102273e+00 1.8223604e+00 2.6061038e+00 2.6023240e+00 2.6040822e+00 2.7058598e+00 1.5384093e+00 2.5058827e+00 4.4178532e+00 3.5098740e+00 4.3151587e+00 4.0041429e+00 4.2110099e+00 5.0184788e+00 2.9154880e+00 4.7114737e+00 4.2061525e+00 4.5248210e+00 3.5155767e+00 3.7089995e+00 3.9157422e+00 3.4167041e+00 3.5401921e+00 3.7249704e+00 3.9056466e+00 5.1213055e+00 5.3189433e+00 3.4098171e+00 4.1203252e+00 3.3172667e+00 5.1196436e+00 3.3110366e+00 4.1111102e+00 4.4124656e+00 3.2115770e+00 3.3091938e+00 4.0103680e+00 4.2141675e+00 4.5185015e+00 4.8383751e+00 4.0135080e+00 3.5035589e+00 4.0015001e+00 4.5406852e+00 4.0218666e+00 3.9049967e+00 3.2103515e+00 3.8201314e+00 4.0245707e+00 3.5427188e+00 3.5098740e+00 4.3149009e+00 4.1273075e+00 3.6322643e+00 3.4139979e+00 3.6137088e+00 3.8212216e+00 3.5066417e+00 7.1621748e-01 4.0002221e-01 3.3858048e+00 3.1257747e+00 3.5528332e+00 2.6049377e+00 3.2291581e+00 3.1026235e+00 3.3158954e+00 1.9043238e+00 3.2362541e+00 2.5062158e+00 2.1151984e+00 2.8111676e+00 2.6144115e+00 3.3074459e+00 2.2106051e+00 3.0644792e+00 3.1042002e+00 2.7046286e+00 3.1155579e+00 2.5035275e+00 3.4095576e+00 2.6210983e+00 3.5110555e+00 3.3064076e+00 2.9323802e+00 3.0497665e+00 3.4467650e+00 3.6304824e+00 3.1087162e+00 2.1099919e+00 2.4034951e+00 2.3034398e+00 2.5081992e+00 3.7045399e+00 3.1036554e+00 3.1105454e+00 3.3425812e+00 3.0208515e+00 2.7039990e+00 2.6042124e+00 3.0014077e+00 3.2086215e+00 2.6068616e+00 1.9064532e+00 2.8033825e+00 2.8033607e+00 2.8042643e+00 2.9174390e+00 1.6121856e+00 2.7050791e+00 4.6165570e+00 3.7079065e+00 4.5303834e+00 4.2064764e+00 4.4135512e+00 5.2388103e+00 3.1079784e+00 4.9275471e+00 4.4124760e+00 4.7382279e+00 3.7227931e+00 3.9129335e+00 4.1268500e+00 3.6117351e+00 3.7315577e+00 3.9257254e+00 4.1111068e+00 5.3430927e+00 5.5370582e+00 3.6046347e+00 4.3307527e+00 3.5130597e+00 5.3416790e+00 3.5154388e+00 4.3179254e+00 4.6302391e+00 3.4146546e+00 3.5107904e+00 4.2125004e+00 4.4361489e+00 4.7415152e+00 5.0768435e+00 4.2149814e+00 3.7084460e+00 4.2023583e+00 4.7769945e+00 4.2205945e+00 4.1089343e+00 3.4107328e+00 4.0362383e+00 4.2295174e+00 3.7618281e+00 3.7079065e+00 4.5213131e+00 4.3307527e+00 3.8409517e+00 3.6158715e+00 3.8200965e+00 4.0195882e+00 3.7063858e+00 4.1210927e-01 3.2157661e+00 3.0055754e+00 3.4095823e+00 2.5182899e+00 3.1060146e+00 3.0020171e+00 3.2051958e+00 1.8468437e+00 3.1049591e+00 2.4099079e+00 2.1180305e+00 2.7069308e+00 2.5226256e+00 3.2022186e+00 2.1097489e+00 2.9102819e+00 3.0041469e+00 2.6022650e+00 3.0136614e+00 2.4087525e+00 3.3085287e+00 2.5053901e+00 3.4040883e+00 3.2011770e+00 2.8045980e+00 2.9078384e+00 3.3077457e+00 3.5074140e+00 3.0043867e+00 2.0123013e+00 2.3159429e+00 2.2186309e+00 2.4051787e+00 3.6030023e+00 3.0041461e+00 3.0063027e+00 3.2075252e+00 2.9100849e+00 2.6032671e+00 2.5097006e+00 2.9028412e+00 3.1024718e+00 2.5058120e+00 1.8685011e+00 2.7040002e+00 2.7016556e+00 2.7029487e+00 2.8031364e+00 1.5747356e+00 2.6040007e+00 4.5158117e+00 3.6083256e+00 4.4100325e+00 4.1032589e+00 4.3091726e+00 5.1114855e+00 3.0117223e+00 4.8065772e+00 4.3039464e+00 4.6187506e+00 3.6121095e+00 3.8069169e+00 4.0114753e+00 3.5138377e+00 3.6350529e+00 3.8212252e+00 4.0040508e+00 5.2135438e+00 5.4123528e+00 3.5063866e+00 4.2155461e+00 3.4147661e+00 5.2119900e+00 3.4083227e+00 4.2084707e+00 4.5071259e+00 3.3090897e+00 3.4075560e+00 4.1085724e+00 4.3075896e+00 4.6108642e+00 4.9236635e+00 4.1113689e+00 3.6022523e+00 4.1009647e+00 4.6262707e+00 4.1190929e+00 4.0037820e+00 3.3086298e+00 3.9141023e+00 4.1202674e+00 3.6321858e+00 3.6083256e+00 4.4117993e+00 4.2229640e+00 3.7257625e+00 3.5107118e+00 3.7106642e+00 3.9184747e+00 3.6056703e+00 3.3320214e+00 3.1087156e+00 3.5191298e+00 2.6048201e+00 3.2097208e+00 3.1014199e+00 3.3064692e+00 1.9064149e+00 3.2109426e+00 2.5061784e+00 2.1231492e+00 2.8062729e+00 2.6052659e+00 3.3025806e+00 2.2069764e+00 3.0213628e+00 3.1034889e+00 2.7008785e+00 3.1069083e+00 2.5018386e+00 3.4076243e+00 2.6061038e+00 3.5039680e+00 3.3015400e+00 2.9090661e+00 3.0158419e+00 3.4158824e+00 3.6117739e+00 3.1042038e+00 2.1025721e+00 2.4028385e+00 2.3026344e+00 2.5028039e+00 3.7026090e+00 3.1034538e+00 3.1060427e+00 3.3145497e+00 3.0064351e+00 2.7026184e+00 2.6035547e+00 3.0010106e+00 3.2029877e+00 2.6024546e+00 1.9099608e+00 2.8022622e+00 2.8013859e+00 2.8022964e+00 2.9047883e+00 1.6144390e+00 2.7027522e+00 4.6146429e+00 3.7070840e+00 4.5144445e+00 4.2034836e+00 4.4092638e+00 5.2185417e+00 3.1080879e+00 4.9117250e+00 4.4052231e+00 4.7224998e+00 3.7130492e+00 3.9071930e+00 4.1140176e+00 3.6112039e+00 3.7307535e+00 3.9200662e+00 4.1050716e+00 5.3212806e+00 5.5187164e+00 3.6026912e+00 4.3179254e+00 3.5126721e+00 5.3198415e+00 3.5083463e+00 4.3098506e+00 4.6126508e+00 3.4087523e+00 3.5071392e+00 4.2084730e+00 4.4144909e+00 4.7184838e+00 5.0381916e+00 4.2109654e+00 3.7029588e+00 4.2008334e+00 4.7393168e+00 4.2176488e+00 4.1043916e+00 3.4078439e+00 4.0181863e+00 4.2205945e+00 3.7363783e+00 3.7070840e+00 4.5130589e+00 4.3227927e+00 3.8267268e+00 3.6097220e+00 3.8114954e+00 4.0168944e+00 3.7050916e+00 6.0017982e-01 2.0181667e-01 1.5160570e+00 5.2133802e-01 1.3002451e+00 7.0008584e-01 2.1344529e+00 4.1212852e-01 1.8029854e+00 2.0342311e+00 1.1019599e+00 1.1393620e+00 9.0026497e-01 1.4543172e+00 3.3813251e-01 1.4000061e+00 1.2053003e+00 1.0426638e+00 1.4134492e+00 1.1005364e+00 9.3424697e-01 7.8890806e-01 9.0142636e-01 6.1119558e-01 4.1315633e-01 4.0125062e-01 3.6452132e-01 1.0001753e+00 1.4158897e+00 1.5195166e+00 1.5300146e+00 1.2201636e+00 1.0039209e+00 1.6000032e+00 1.0000457e+00 3.0017653e-01 9.3329055e-01 1.4017696e+00 1.5061610e+00 1.5012947e+00 9.0002570e-01 1.2124837e+00 2.0445123e+00 1.4012283e+00 1.3008855e+00 1.3009222e+00 8.0291749e-01 2.0440118e+00 1.3027556e+00 1.3788457e+00 1.2029161e+00 1.2089253e+00 9.3446811e-01 1.1298636e+00 1.9014076e+00 2.1006232e+00 1.6001224e+00 1.1139906e+00 1.4544336e+00 6.3912709e-01 7.1183012e-01 8.5409862e-01 1.3086191e+00 1.2638465e+00 9.2745734e-01 8.1117067e-01 2.0027889e+00 2.2028146e+00 1.1270327e+00 1.0776190e+00 1.4019372e+00 2.0011307e+00 7.2044167e-01 1.0208709e+00 1.3002450e+00 8.0488008e-01 9.0145141e-01 9.4622126e-01 1.1000289e+00 1.4009513e+00 1.7086186e+00 9.7694377e-01 7.0911112e-01 1.0224133e+00 1.4220925e+00 1.0923537e+00 8.2631334e-01 1.0008620e+00 7.8886139e-01 1.0777307e+00 9.0140221e-01 1.2029161e+00 1.2362755e+00 1.1897289e+00 9.0534502e-01 7.9871893e-01 6.5724028e-01 9.8998705e-01 1.1010807e+00 5.2133179e-01 1.0171340e+00 4.0004442e-01 7.0470720e-01 2.0181667e-01 1.5698091e+00 3.0922892e-01 1.2049541e+00 1.5104875e+00 5.0476836e-01 1.0069214e+00 3.4085233e-01 9.6593231e-01 3.0026460e-01 8.0004443e-01 6.6334810e-01 1.0000152e+00 8.7372177e-01 5.0855077e-01 5.2524663e-01 7.0462697e-01 4.2362917e-01 3.0915245e-01 2.2608083e-01 4.5784410e-01 5.0517282e-01 4.1209001e-01 1.0313359e+00 9.9085945e-01 1.0179856e+00 6.9600743e-01 6.3912943e-01 1.0000152e+00 4.0125062e-01 3.0482299e-01 9.0002615e-01 8.0254500e-01 9.3735629e-01 9.1446938e-01 3.0490481e-01 6.9600743e-01 1.4994060e+00 8.0928056e-01 7.0184453e-01 7.0184453e-01 3.1328089e-01 1.5989637e+00 7.0918894e-01 1.5237054e+00 6.9987517e-01 1.4060443e+00 1.1002025e+00 1.3061181e+00 2.1141220e+00 1.5030978e+00 1.8055480e+00 1.3062025e+00 1.6223413e+00 6.3164977e-01 8.1112909e-01 1.0095513e+00 8.0713433e-01 9.2867113e-01 9.0155393e-01 1.0001753e+00 2.2182690e+00 2.4124980e+00 1.0039060e+00 1.2201577e+00 8.1343016e-01 2.2176846e+00 5.2491734e-01 1.2037520e+00 1.5066999e+00 4.2362917e-01 4.2362917e-01 1.1060937e+00 1.3130978e+00 1.6177611e+00 1.9760242e+00 1.1138953e+00 6.0948506e-01 1.1056693e+00 1.6779798e+00 1.1527746e+00 1.0001601e+00 4.2362917e-01 9.1892454e-01 1.1528477e+00 8.3183672e-01 6.9987517e-01 1.4094144e+00 1.2633467e+00 8.5440680e-01 7.2036951e-01 7.1629303e-01 9.6576136e-01 6.3322667e-01 1.4267554e+00 4.2268438e-01 1.2004262e+00 6.0035621e-01 2.0860325e+00 3.4342562e-01 1.7133162e+00 1.9641993e+00 1.0207260e+00 1.0897469e+00 8.0008964e-01 1.4650300e+00 5.0043084e-01 1.3002407e+00 1.1303267e+00 9.3424659e-01 1.3473688e+00 1.0001604e+00 9.6593231e-01 6.7616545e-01 8.0097499e-01 6.3192325e-01 5.0437695e-01 3.0026460e-01 2.2608083e-01 9.0142636e-01 1.4861824e+00 1.4580174e+00 1.4889602e+00 1.1900969e+00 9.0142681e-01 1.5001212e+00 9.0166476e-01 2.2538848e-01 8.3187290e-01 1.3130978e+00 1.4197078e+00 1.4012600e+00 8.0046764e-01 1.1544060e+00 2.0075255e+00 1.3063533e+00 1.2089835e+00 1.2089313e+00 7.4275547e-01 2.0887699e+00 1.2190319e+00 1.1935069e+00 1.1010807e+00 1.0087396e+00 7.4335736e-01 9.3424697e-01 1.7023957e+00 2.0003507e+00 1.4002035e+00 9.1449234e-01 1.2643523e+00 5.2167829e-01 5.5450500e-01 6.7616902e-01 1.2049541e+00 1.1528553e+00 8.1112984e-01 6.1119558e-01 1.8053679e+00 2.0034894e+00 1.0142484e+00 9.0155438e-01 1.3009222e+00 1.8029948e+00 6.1119267e-01 8.2425704e-01 1.1002025e+00 7.0176271e-01 8.0046685e-01 7.5564478e-01 9.0026588e-01 1.2017042e+00 1.5269837e+00 7.9871893e-01 6.0201716e-01 8.6051471e-01 1.2366099e+00 9.4532171e-01 6.3309012e-01 9.0026588e-01 6.3164729e-01 9.3308853e-01 8.0004443e-01 1.1010807e+00 1.0426516e+00 1.0426760e+00 8.0051115e-01 6.8161057e-01 5.2491734e-01 8.6084272e-01 1.0001753e+00 1.0116865e+00 5.6370994e-01 1.0599087e+00 7.4329527e-01 1.1110092e+00 4.1212852e-01 5.6838732e-01 7.0478886e-01 5.0436965e-01 7.7598796e-01 6.0948506e-01 1.2192920e+00 7.1629303e-01 4.2270142e-01 7.1629303e-01 2.2608083e-01 9.7033357e-01 6.3164729e-01 9.6576136e-01 7.5503094e-01 9.1446896e-01 1.1138955e+00 1.3139296e+00 1.2705641e+00 6.5724028e-01 5.0894102e-01 2.2573593e-01 3.3813251e-01 4.1212852e-01 1.1025819e+00 7.1629303e-01 1.1039833e+00 1.2272550e+00 8.0245746e-01 7.0000303e-01 2.0000000e-01 4.1210927e-01 7.7598796e-01 3.3813251e-01 7.1700774e-01 4.0125062e-01 7.0017011e-01 6.0035305e-01 7.4329414e-01 1.0008768e+00 5.0043084e-01 2.0249458e+00 1.1061923e+00 2.0081838e+00 1.6061519e+00 1.8167511e+00 2.7171724e+00 6.3925756e-01 2.3875202e+00 1.8286180e+00 2.2239101e+00 1.2353587e+00 1.3278587e+00 1.6038059e+00 1.0207533e+00 1.2407946e+00 1.3868868e+00 1.5269837e+00 2.8396169e+00 2.9941208e+00 1.0030871e+00 1.8005082e+00 9.3733589e-01 2.8269381e+00 9.7033357e-01 1.7520952e+00 2.1193712e+00 8.6676847e-01 9.4912864e-01 1.6147493e+00 1.9768316e+00 2.2674825e+00 2.7199050e+00 1.6193612e+00 1.1298636e+00 1.6009488e+00 2.4288142e+00 1.6617386e+00 1.5199103e+00 8.6676847e-01 1.5881447e+00 1.6785116e+00 1.4886759e+00 1.1061923e+00 1.9457481e+00 1.7813291e+00 1.3946348e+00 1.0498347e+00 1.2771155e+00 1.4848797e+00 1.1157320e+00 8.0004523e-01 5.0043842e-01 1.6743483e+00 2.0121983e-01 1.3061139e+00 1.5464046e+00 6.0964597e-01 7.1183012e-01 4.0006662e-01 1.0776296e+00 3.0922892e-01 9.0002570e-01 7.3084171e-01 6.0184622e-01 9.3446811e-01 6.1135434e-01 6.0964597e-01 3.4080442e-01 4.1210927e-01 3.0490481e-01 2.2608083e-01 3.0482299e-01 4.0363334e-01 5.0001522e-01 1.1298636e+00 1.0440350e+00 1.0803561e+00 7.8935898e-01 5.6347978e-01 1.1000098e+00 6.3165225e-01 3.0482299e-01 5.0126466e-01 9.0511169e-01 1.0088926e+00 1.0001903e+00 4.0125062e-01 7.4335736e-01 1.5972311e+00 9.0142681e-01 8.0296037e-01 8.0250202e-01 3.4085233e-01 1.7081446e+00 8.0883841e-01 1.4329858e+00 7.2036951e-01 1.3050153e+00 1.0001753e+00 1.2089252e+00 2.0109333e+00 1.6000184e+00 1.7036944e+00 1.2001396e+00 1.5327217e+00 5.7608844e-01 7.0462844e-01 9.1449234e-01 8.1156529e-01 9.3733552e-01 8.5583415e-01 9.0029018e-01 2.1191883e+00 2.3098756e+00 6.3912709e-01 1.1290757e+00 9.0532049e-01 2.1139617e+00 3.4085233e-01 1.1074834e+00 1.4044980e+00 3.4080442e-01 4.2362917e-01 1.0087250e+00 1.2089253e+00 1.5132032e+00 1.8748226e+00 1.0207260e+00 5.0042326e-01 1.0008620e+00 1.5694554e+00 1.0837679e+00 9.0053003e-01 5.0517282e-01 8.2671175e-01 1.0777411e+00 8.1156529e-01 7.2036951e-01 1.3133662e+00 1.1910068e+00 8.2425704e-01 4.5847767e-01 6.3178534e-01 9.1590889e-01 6.3322667e-01 6.3322667e-01 1.2193537e+00 9.0000091e-01 6.3165225e-01 1.0599087e+00 3.1328089e-01 6.3451734e-01 4.0127250e-01 9.0000091e-01 1.0001604e+00 2.2573593e-01 4.1212852e-01 6.3178534e-01 6.0202028e-01 5.2524663e-01 5.2132556e-01 6.1135434e-01 4.0125062e-01 7.0008584e-01 9.0002615e-01 1.1001014e+00 1.0039209e+00 3.0482299e-01 1.0001751e+00 7.0478886e-01 8.0296037e-01 6.0000952e-01 6.0366256e-01 3.0915245e-01 6.0365948e-01 1.0001903e+00 6.3164977e-01 4.0125062e-01 5.0476836e-01 2.2608083e-01 4.0127250e-01 5.0043842e-01 1.2102248e+00 3.0017653e-01 3.0482299e-01 3.0008832e-01 5.0043084e-01 1.5012947e+00 4.0000000e-01 1.5653766e+00 6.7616902e-01 1.5829749e+00 1.1074742e+00 1.3373141e+00 2.2681751e+00 8.0291671e-01 1.9315820e+00 1.3458100e+00 1.7862938e+00 8.7372177e-01 8.7209348e-01 1.2093969e+00 7.1700774e-01 1.1055705e+00 1.0604287e+00 1.0451812e+00 2.3834499e+00 2.5286011e+00 6.3322667e-01 1.3903623e+00 7.0462697e-01 2.3796582e+00 6.3912943e-01 1.2792049e+00 1.6907308e+00 5.6595488e-01 5.3943256e-01 1.1400339e+00 1.5965952e+00 1.8639835e+00 2.3424496e+00 1.1635325e+00 6.7626502e-01 1.1005460e+00 2.0903382e+00 1.2459141e+00 1.0236548e+00 5.0894102e-01 1.2528590e+00 1.2949162e+00 1.2662318e+00 6.7616902e-01 1.4817248e+00 1.3908238e+00 1.1389163e+00 6.9600743e-01 8.9540816e-01 1.0858512e+00 6.3192325e-01 1.5890088e+00 4.2270142e-01 1.1330776e+00 1.5368468e+00 5.2491734e-01 1.1187430e+00 4.0243965e-01 1.1139906e+00 4.1420960e-01 7.0096858e-01 7.3895268e-01 1.1000100e+00 9.3861512e-01 4.0127250e-01 7.1708289e-01 8.0004523e-01 5.2167208e-01 4.5784410e-01 3.6452132e-01 5.6371422e-01 4.2270142e-01 4.1317535e-01 1.2159868e+00 1.0567817e+00 1.1138092e+00 8.3387677e-01 6.1119267e-01 9.0029064e-01 3.0482299e-01 4.0125062e-01 1.0003196e+00 7.4395693e-01 9.3459651e-01 8.5617086e-01 3.0922892e-01 8.0073117e-01 1.5493206e+00 7.5564478e-01 6.4049114e-01 6.4049114e-01 4.5784410e-01 1.7404389e+00 6.9600743e-01 1.3253457e+00 6.4049114e-01 1.2202193e+00 9.0142636e-01 1.1056785e+00 1.9348400e+00 1.4092540e+00 1.6176783e+00 1.1286101e+00 1.4350761e+00 4.5148429e-01 6.7720957e-01 8.1757693e-01 8.2671175e-01 8.1937731e-01 7.4263078e-01 8.0055465e-01 2.0418418e+00 2.2277117e+00 1.1002025e+00 1.0286508e+00 7.2044167e-01 2.0415798e+00 6.0035305e-01 1.0039060e+00 1.3253497e+00 5.0043842e-01 3.1328089e-01 9.0999313e-01 1.1528476e+00 1.4548293e+00 1.8637334e+00 9.1892454e-01 5.2133179e-01 9.3310976e-01 1.5801693e+00 9.6572569e-01 8.0008964e-01 3.4085233e-01 7.5508853e-01 9.6674360e-01 7.4618926e-01 6.4049114e-01 1.2101609e+00 1.0782105e+00 7.2113820e-01 8.0093081e-01 5.2524663e-01 7.8886139e-01 4.5847767e-01 1.7572657e+00 6.1288055e-01 4.0125062e-01 1.0858512e+00 1.1133984e+00 1.4858469e+00 7.1779518e-01 1.8187119e+00 1.2137020e+00 9.6591433e-01 1.4146346e+00 7.4263078e-01 1.5359852e+00 1.2093243e+00 1.7083042e+00 1.4853863e+00 1.5241361e+00 1.7234436e+00 1.9756319e+00 1.9771636e+00 1.3035495e+00 8.0008884e-01 6.3164977e-01 6.0948212e-01 9.1449234e-01 1.8179429e+00 1.2062153e+00 1.3486924e+00 1.8673780e+00 1.4543172e+00 8.7240114e-01 7.4329527e-01 1.1055892e+00 1.4158897e+00 9.3310976e-01 1.1269424e-01 9.3351278e-01 9.7600992e-01 9.6953662e-01 1.3458100e+00 3.0490481e-01 9.0320459e-01 2.7259033e+00 1.8109877e+00 2.7506971e+00 2.3226569e+00 2.5372438e+00 3.4590995e+00 1.2089192e+00 3.1281646e+00 2.5610212e+00 2.9500632e+00 1.9406671e+00 2.0633570e+00 2.3443688e+00 1.7168003e+00 1.8708330e+00 2.0857354e+00 2.2573821e+00 3.5725062e+00 3.7336869e+00 1.7229558e+00 2.5362836e+00 1.6198349e+00 3.5690915e+00 1.7116793e+00 2.4776152e+00 2.8599555e+00 1.6016303e+00 1.6534235e+00 2.3372717e+00 2.7159844e+00 3.0094888e+00 3.4430661e+00 2.3406025e+00 1.8663319e+00 2.3090404e+00 3.1586433e+00 2.3454995e+00 2.2408459e+00 1.5471213e+00 2.3192230e+00 2.4059451e+00 2.1751377e+00 1.8109877e+00 2.6757243e+00 2.4966678e+00 2.1111734e+00 1.7901165e+00 2.0121175e+00 2.1471399e+00 1.8133657e+00 1.4043036e+00 1.6388784e+00 7.0470867e-01 7.7652636e-01 5.0001522e-01 1.1269424e+00 2.2608083e-01 1.0000158e+00 8.0928056e-01 7.0470867e-01 1.0215068e+00 7.1708289e-01 6.3164977e-01 4.2362917e-01 5.0002283e-01 3.0474106e-01 2.0121983e-01 2.2608083e-01 4.5080200e-01 6.0017982e-01 1.1529284e+00 1.1298636e+00 1.1544060e+00 8.5406674e-01 6.3322667e-01 1.2000066e+00 6.3309258e-01 2.2608083e-01 6.0201716e-01 1.0030721e+00 1.1060937e+00 1.1001110e+00 5.0001522e-01 8.2458478e-01 1.6747799e+00 1.0008617e+00 9.0140221e-01 9.0140131e-01 4.1209001e-01 1.7511598e+00 9.0506299e-01 1.4854079e+00 8.3187290e-01 1.3139296e+00 1.0032293e+00 1.2362702e+00 2.0078120e+00 1.7001329e+00 1.7019430e+00 1.2016381e+00 1.5675442e+00 7.1700909e-01 7.4275547e-01 9.6574369e-01 9.3541878e-01 1.1298552e+00 1.0208844e+00 9.0506343e-01 2.1136134e+00 2.3085898e+00 7.4618926e-01 1.1897982e+00 1.0208709e+00 2.1090362e+00 5.0894102e-01 1.1286018e+00 1.4024091e+00 5.2167829e-01 5.6595908e-01 1.0426638e+00 1.2037520e+00 1.5079206e+00 1.8503663e+00 1.0776296e+00 5.0477564e-01 1.0032296e+00 1.5611241e+00 1.1910693e+00 9.0511169e-01 6.3178782e-01 9.0184172e-01 1.1896660e+00 1.0032443e+00 8.3187290e-01 1.3450688e+00 1.3020492e+00 1.0087252e+00 6.1990228e-01 7.4263078e-01 1.0458540e+00 7.3084171e-01 7.0918894e-01 7.0176271e-01 8.1112984e-01 9.6574336e-01 4.1317535e-01 1.5005626e+00 6.1119558e-01 6.0964597e-01 1.0116724e+00 4.1315633e-01 9.3848935e-01 9.0000136e-01 1.1896660e+00 9.6574369e-01 1.2003597e+00 1.4006465e+00 1.6096629e+00 1.5401713e+00 8.2421923e-01 5.3914287e-01 3.6259865e-01 4.2362917e-01 6.0017665e-01 1.2189701e+00 6.0202028e-01 8.7212232e-01 1.5067961e+00 1.1024820e+00 4.1317535e-01 3.0490481e-01 5.0477564e-01 9.3329017e-01 6.0018299e-01 6.1845783e-01 4.1210927e-01 5.0894102e-01 5.0477564e-01 1.0008620e+00 9.0029064e-01 5.0043842e-01 2.1169442e+00 1.2049539e+00 2.2014913e+00 1.7227908e+00 1.9366943e+00 2.8973091e+00 6.0383105e-01 2.5621105e+00 1.9756002e+00 2.3854144e+00 1.4163126e+00 1.4857201e+00 1.8044011e+00 1.1074834e+00 1.2661803e+00 1.4994060e+00 1.6741010e+00 3.0107625e+00 3.1586112e+00 1.1298552e+00 1.9796588e+00 1.0095370e+00 3.0090568e+00 1.1900276e+00 1.8963668e+00 2.3135911e+00 1.0782107e+00 1.0783219e+00 1.7384347e+00 2.2009981e+00 2.4793129e+00 2.9421182e+00 1.7402224e+00 1.3018103e+00 1.7072540e+00 2.6745468e+00 1.7367211e+00 1.6485141e+00 9.6691372e-01 1.8209763e+00 1.8293641e+00 1.7435092e+00 1.2049539e+00 2.0881329e+00 1.9090398e+00 1.6063540e+00 1.2407432e+00 1.4664722e+00 1.5386307e+00 1.2093243e+00 1.0943600e+00 1.0030868e+00 1.3272142e+00 9.1446938e-01 1.7295996e+00 1.1336109e+00 8.7209296e-01 1.2643054e+00 6.3912943e-01 1.4396100e+00 1.1299441e+00 1.5271597e+00 1.3148232e+00 1.4267817e+00 1.6268514e+00 1.8468437e+00 1.8305154e+00 1.1759988e+00 7.4262850e-01 5.2491734e-01 5.2167208e-01 8.5586571e-01 1.6206300e+00 1.1291536e+00 1.4631182e+00 1.7576822e+00 1.3254284e+00 1.0172489e+00 6.0605366e-01 9.1894698e-01 1.2951152e+00 8.3187290e-01 3.0474106e-01 8.1500329e-01 1.0396215e+00 9.6150595e-01 1.2528590e+00 5.6347978e-01 8.7240114e-01 2.5401214e+00 1.6166178e+00 2.5672376e+00 2.1256636e+00 2.3434890e+00 3.2706021e+00 1.0235120e+00 2.9379053e+00 2.3650373e+00 2.7819820e+00 1.7939428e+00 1.8718670e+00 2.1669217e+00 1.5269837e+00 1.7152309e+00 1.9257519e+00 2.0672316e+00 3.3962101e+00 3.5413320e+00 1.5241169e+00 2.3604685e+00 1.4422764e+00 3.3805191e+00 1.5352907e+00 2.2985560e+00 2.6785349e+00 1.4314424e+00 1.4886759e+00 2.1422340e+00 2.5406863e+00 2.8290490e+00 3.2861997e+00 2.1472832e+00 1.6782365e+00 2.1087015e+00 2.9939447e+00 2.1830772e+00 2.0525906e+00 1.3935744e+00 2.1564350e+00 2.2287876e+00 2.0426476e+00 1.6166178e+00 2.4889554e+00 2.3256006e+00 1.9561612e+00 1.6066555e+00 1.8386981e+00 2.0009986e+00 1.6313110e+00 8.0883916e-01 5.0043842e-01 6.0202028e-01 8.0004602e-01 3.3808272e-01 5.0437695e-01 8.0093081e-01 5.2838320e-01 6.0201716e-01 2.5399984e-01 7.2036819e-01 5.0517282e-01 5.0043842e-01 7.0008584e-01 9.1424701e-01 9.0157896e-01 3.0017653e-01 7.2044167e-01 6.2656178e-01 6.6334810e-01 3.6259865e-01 9.0026588e-01 5.0436235e-01 4.1212852e-01 8.0879701e-01 7.0478886e-01 3.0482299e-01 5.2201750e-01 4.5847767e-01 4.0125062e-01 4.1317535e-01 1.0363096e+00 3.4080442e-01 3.0474106e-01 2.2573593e-01 3.0490481e-01 1.2204839e+00 2.4195741e-01 1.8101835e+00 9.0166476e-01 1.7375279e+00 1.4002005e+00 1.6032003e+00 2.4532171e+00 1.0032443e+00 2.1331916e+00 1.6052507e+00 1.9422649e+00 9.1894698e-01 1.1025819e+00 1.3276411e+00 8.1719606e-01 1.0142626e+00 1.1298636e+00 1.3025621e+00 2.5612597e+00 2.7430487e+00 9.0155438e-01 1.5299064e+00 7.1708289e-01 2.5605373e+00 7.0633229e-01 1.5079428e+00 1.8443132e+00 6.0383105e-01 7.0096708e-01 1.4023806e+00 1.6740160e+00 1.9756002e+00 2.3801033e+00 1.4049093e+00 9.0142636e-01 1.4001717e+00 2.0898615e+00 1.4183606e+00 1.3009222e+00 6.0184622e-01 1.2661803e+00 1.4267527e+00 1.1084368e+00 9.0166476e-01 1.7108631e+00 1.5299252e+00 1.0782751e+00 8.1343016e-01 1.0116721e+00 1.2193537e+00 9.0026497e-01 7.9148746e-01 7.0993998e-01 9.3541878e-01 8.1937731e-01 5.0043084e-01 5.6370994e-01 4.1212852e-01 1.0782753e+00 6.0184622e-01 9.0557807e-01 7.4269314e-01 7.0633229e-01 8.2841920e-01 9.1695534e-01 1.0756891e+00 7.3084048e-01 5.2491131e-01 5.0085236e-01 5.0476836e-01 5.0085236e-01 1.1074740e+00 8.3916809e-01 1.2049539e+00 9.6501813e-01 4.2270142e-01 8.0291749e-01 5.0855077e-01 5.3943256e-01 8.2631334e-01 4.0243965e-01 1.0207260e+00 5.2524663e-01 8.0055465e-01 7.0184453e-01 7.0184453e-01 1.0777307e+00 6.0366256e-01 2.0696799e+00 1.1543334e+00 1.9286352e+00 1.6071727e+00 1.8312163e+00 2.6295459e+00 1.1153247e+00 2.3155068e+00 1.8040969e+00 2.1901871e+00 1.2562955e+00 1.3263639e+00 1.5518083e+00 1.1271226e+00 1.4557657e+00 1.4915559e+00 1.5136393e+00 2.7555974e+00 2.9267466e+00 1.0030718e+00 1.7744103e+00 1.0843333e+00 2.7324083e+00 9.6953662e-01 1.7456060e+00 2.0249458e+00 9.1568820e-01 1.0151258e+00 1.6309461e+00 1.8315348e+00 2.1358791e+00 2.5304748e+00 1.6492450e+00 1.1075720e+00 1.6001777e+00 2.2141198e+00 1.7441970e+00 1.5196090e+00 9.6683480e-01 1.4838225e+00 1.7167914e+00 1.4122282e+00 1.1543334e+00 1.9438463e+00 1.8374400e+00 1.4268530e+00 1.0778421e+00 1.2792049e+00 1.5857302e+00 1.1532421e+00 1.1019503e+00 6.0201716e-01 5.0043842e-01 6.1135434e-01 7.0008735e-01 8.1156529e-01 4.1317535e-01 7.0000303e-01 4.0246123e-01 2.0061436e-01 4.1210927e-01 5.0436965e-01 7.0000303e-01 6.0366256e-01 2.0121983e-01 1.2007726e+00 9.1916394e-01 1.0124729e+00 8.0055465e-01 4.0246123e-01 7.0008735e-01 5.0085236e-01 6.0017982e-01 6.0202028e-01 6.3165225e-01 7.4612830e-01 6.0383105e-01 1.1269424e-01 7.0184453e-01 1.4559030e+00 5.6371422e-01 5.2167829e-01 5.2133179e-01 4.0004442e-01 1.7133283e+00 6.0948800e-01 1.3743342e+00 5.2524663e-01 1.2702954e+00 9.0142636e-01 1.1286018e+00 1.9763960e+00 1.2004262e+00 1.6484371e+00 1.1066159e+00 1.5033966e+00 6.1990228e-01 6.3322667e-01 8.9538275e-01 6.1990228e-01 1.0010060e+00 9.1471442e-01 8.0488008e-01 2.0894199e+00 2.2581358e+00 7.0088627e-01 1.1085342e+00 6.3178782e-01 2.0855639e+00 4.0363334e-01 1.0293900e+00 1.3743657e+00 4.0006662e-01 4.0125062e-01 9.3329055e-01 1.2396422e+00 1.5267540e+00 1.9798779e+00 9.6591465e-01 4.0127250e-01 9.0026497e-01 1.7151603e+00 1.0797805e+00 8.0296037e-01 4.0006662e-01 8.9540816e-01 1.0837679e+00 9.6674360e-01 5.2524663e-01 1.2440789e+00 1.1938630e+00 9.1892454e-01 5.2524663e-01 6.3912943e-01 9.3733589e-01 4.5148429e-01 1.1281352e+00 9.0002570e-01 5.0517282e-01 9.4513210e-01 4.1315633e-01 1.2014191e+00 5.2133179e-01 1.3063533e+00 1.1019505e+00 8.5403370e-01 1.0426516e+00 1.3523310e+00 1.4544312e+00 9.0142636e-01 3.3818226e-01 5.0085236e-01 5.0437695e-01 3.0922892e-01 1.5001461e+00 9.0005094e-01 9.0668287e-01 1.2396475e+00 8.7209296e-01 5.0000761e-01 4.5078948e-01 8.0046764e-01 1.0030724e+00 4.1317535e-01 6.7824250e-01 6.0017665e-01 6.0000952e-01 6.0000317e-01 7.4262850e-01 6.3925756e-01 5.0001522e-01 2.4077059e+00 1.5012741e+00 2.3329496e+00 2.0008921e+00 2.2042323e+00 3.0476353e+00 9.3541878e-01 2.7309758e+00 2.2068463e+00 2.5373913e+00 1.5160787e+00 1.7043730e+00 1.9242109e+00 1.4044668e+00 1.5401330e+00 1.7168122e+00 1.9044144e+00 3.1543192e+00 3.3406961e+00 1.4044697e+00 2.1265131e+00 1.3061138e+00 3.1536525e+00 1.3069754e+00 2.1097680e+00 2.4379736e+00 1.2049541e+00 1.3017511e+00 2.0033792e+00 2.2562563e+00 2.5606009e+00 2.9377608e+00 2.0050253e+00 1.5030978e+00 2.0001168e+00 2.6390071e+00 2.0114908e+00 1.9023062e+00 1.2016381e+00 1.8467998e+00 2.0209800e+00 1.6143467e+00 1.5012741e+00 2.3121231e+00 2.1220697e+00 1.6461460e+00 1.4062065e+00 1.6118728e+00 1.8108200e+00 1.5004645e+00 1.1000005e+00 9.0305330e-01 9.0506343e-01 1.1075720e+00 8.0488008e-01 6.1119558e-01 6.3912943e-01 6.0383105e-01 3.0490481e-01 1.1269424e-01 4.1210927e-01 6.0184622e-01 7.0008735e-01 1.0803561e+00 1.2125410e+00 1.2178626e+00 9.0645118e-01 7.9153339e-01 1.3000002e+00 7.0096858e-01 3.0008832e-01 8.0245824e-01 1.1001015e+00 1.2040344e+00 1.2012928e+00 6.0017982e-01 9.0645118e-01 1.7262450e+00 1.1005460e+00 1.0000307e+00 1.0000307e+00 5.0043842e-01 1.7087610e+00 1.0003198e+00 1.6300950e+00 9.3848935e-01 1.5032156e+00 1.2007124e+00 1.4092540e+00 2.2026134e+00 1.8005395e+00 1.9004485e+00 1.4019342e+00 1.7231818e+00 7.4269314e-01 9.0668287e-01 1.1133897e+00 1.0251597e+00 1.0924484e+00 1.0143978e+00 1.1005460e+00 2.3044111e+00 2.5032992e+00 9.4511250e-01 1.3253497e+00 1.1075720e+00 2.3033192e+00 5.5450500e-01 1.3061180e+00 1.6004128e+00 5.4219811e-01 6.3912943e-01 1.2090477e+00 1.4006179e+00 1.7019555e+00 2.0185049e+00 1.2190878e+00 7.0548283e-01 1.2049539e+00 1.7202439e+00 1.2636227e+00 1.1006371e+00 7.0911112e-01 1.0207396e+00 1.2632946e+00 9.3308853e-01 9.3848935e-01 1.5130871e+00 1.3741498e+00 9.6572569e-01 6.9987517e-01 8.2421923e-01 1.0798806e+00 8.5583415e-01 5.2524663e-01 8.2418002e-01 6.3912709e-01 3.6452132e-01 5.6394820e-01 7.2036819e-01 5.0517282e-01 8.0008964e-01 1.0000005e+00 1.2000731e+00 1.1019597e+00 4.0002221e-01 1.0039063e+00 7.4612830e-01 8.3183672e-01 6.0383105e-01 6.1119558e-01 2.0000000e-01 4.5078948e-01 1.1000098e+00 7.8890806e-01 4.0122873e-01 5.6371422e-01 4.1212852e-01 5.0001522e-01 5.2524663e-01 1.2137020e+00 3.4080442e-01 3.3813251e-01 3.0490481e-01 6.0035621e-01 1.5010034e+00 4.0246123e-01 1.5265987e+00 6.1135434e-01 1.6395342e+00 1.1134850e+00 1.3309249e+00 2.3136797e+00 7.1629168e-01 1.9760038e+00 1.3748534e+00 1.8136626e+00 9.1894698e-01 9.0320459e-01 1.2661802e+00 6.0427481e-01 9.1427000e-01 9.6685270e-01 1.0777305e+00 2.4270428e+00 2.5626268e+00 8.1112909e-01 1.4228744e+00 5.2167208e-01 2.4261071e+00 7.0633229e-01 1.3043552e+00 1.7511131e+00 6.0383105e-01 5.2491131e-01 1.1330776e+00 1.6740160e+00 1.9314874e+00 2.4166999e+00 1.1400420e+00 7.4269200e-01 1.1024820e+00 2.1702438e+00 1.1639421e+00 1.0427822e+00 4.2268438e-01 1.3276412e+00 1.2710363e+00 1.3154933e+00 6.1135434e-01 1.4922566e+00 1.3466030e+00 1.1400339e+00 7.3461436e-01 9.3733552e-01 9.7694377e-01 6.0365948e-01 5.8750389e-01 2.4195741e-01 8.6051471e-01 3.3818226e-01 8.1719606e-01 6.0202028e-01 6.0219099e-01 8.0337471e-01 1.0214933e+00 1.0338224e+00 5.2201750e-01 6.0000635e-01 3.6259865e-01 4.2268438e-01 2.2538848e-01 1.0087393e+00 5.4219811e-01 7.4618926e-01 9.2019277e-01 5.2838320e-01 3.4080442e-01 3.4085233e-01 3.4085233e-01 5.2838320e-01 2.0121983e-01 9.0294373e-01 3.0482299e-01 3.0490481e-01 3.0490481e-01 4.1420960e-01 1.1133986e+00 3.0017653e-01 1.9760242e+00 1.0776188e+00 1.8598666e+00 1.5071120e+00 1.7384459e+00 2.5637810e+00 9.3426769e-01 2.2403929e+00 1.7108631e+00 2.0993246e+00 1.1405598e+00 1.2394690e+00 1.4816205e+00 1.0776296e+00 1.4324323e+00 1.4163126e+00 1.4134492e+00 2.6753615e+00 2.8540068e+00 9.1001664e-01 1.6986597e+00 1.0426638e+00 2.6697938e+00 9.0657539e-01 1.6396943e+00 1.9541963e+00 8.5583415e-01 9.0207914e-01 1.5412500e+00 1.7849153e+00 2.0880425e+00 2.4973149e+00 1.5650163e+00 1.0060994e+00 1.5001440e+00 2.2185588e+00 1.6410190e+00 1.4111252e+00 8.5440680e-01 1.4330979e+00 1.6474117e+00 1.4095656e+00 1.0776188e+00 1.8534896e+00 1.7579984e+00 1.3938438e+00 1.0171340e+00 1.1990152e+00 1.4686236e+00 1.0427822e+00 6.8261201e-01 1.0004792e+00 6.3178782e-01 4.1210927e-01 6.0202028e-01 7.0025283e-01 8.0245903e-01 6.7720957e-01 8.1719606e-01 7.0008432e-01 1.0069214e+00 7.9153339e-01 8.6054545e-01 6.4049114e-01 6.3178782e-01 9.0155393e-01 1.2000065e+00 9.0508712e-01 2.0181667e-01 8.2635069e-01 7.1708289e-01 7.0548283e-01 8.0000239e-01 5.4219811e-01 1.3530568e+00 6.3322667e-01 8.0967961e-01 7.1708289e-01 7.0016860e-01 1.5402579e+00 6.3925756e-01 1.5611241e+00 6.4620889e-01 1.4283663e+00 1.1134850e+00 1.3189663e+00 2.1346646e+00 1.3000497e+00 1.8186729e+00 1.3009674e+00 1.7335369e+00 1.0118233e+00 8.1117067e-01 1.0567664e+00 6.0605366e-01 9.2867113e-01 1.0782857e+00 1.0429127e+00 2.2917679e+00 2.4270713e+00 5.0042326e-01 1.2848760e+00 6.9987517e-01 2.2396581e+00 5.2491734e-01 1.3051737e+00 1.5457820e+00 6.0365948e-01 8.0291749e-01 1.1110184e+00 1.3561934e+00 1.6492450e+00 2.1212048e+00 1.1186586e+00 6.7616723e-01 1.1005365e+00 1.7574668e+00 1.3269962e+00 1.0777411e+00 8.0097499e-01 1.0411548e+00 1.1972915e+00 9.9911696e-01 6.4620889e-01 1.4422764e+00 1.3473056e+00 9.3861512e-01 5.2491734e-01 8.6084272e-01 1.2528048e+00 8.2498722e-01 9.6150595e-01 5.0477564e-01 1.0214931e+00 8.0923926e-01 8.0492246e-01 1.0062544e+00 1.2363856e+00 1.2438823e+00 6.2656178e-01 4.0006662e-01 1.2085435e-01 2.0181667e-01 2.2573593e-01 1.2016443e+00 6.3925756e-01 9.2019277e-01 1.1335345e+00 7.1636719e-01 5.0084481e-01 2.0121983e-01 5.0002283e-01 7.3155911e-01 2.0181667e-01 6.7626681e-01 3.0915245e-01 5.0437695e-01 4.1317535e-01 6.1845783e-01 9.0506254e-01 3.0922892e-01 2.1350025e+00 1.2189760e+00 2.0658767e+00 1.7034615e+00 1.9177947e+00 2.7775988e+00 7.7598704e-01 2.4534018e+00 1.9144928e+00 2.2857680e+00 1.2749306e+00 1.4182222e+00 1.6639408e+00 1.1527669e+00 1.4140789e+00 1.4954274e+00 1.6121856e+00 2.8913337e+00 3.0644792e+00 1.1011719e+00 1.8708183e+00 1.0777305e+00 2.8852099e+00 1.0396215e+00 1.8297117e+00 2.1701542e+00 9.4532171e-01 1.0262619e+00 1.7168122e+00 2.0059655e+00 2.3063931e+00 2.7230908e+00 1.7261949e+00 1.2093243e+00 1.7002548e+00 2.4331092e+00 1.7646791e+00 1.6080687e+00 9.3848935e-01 1.6152383e+00 1.7771159e+00 1.4971922e+00 1.2189760e+00 2.0349233e+00 1.8831259e+00 1.4665397e+00 1.1400339e+00 1.3492939e+00 1.5757399e+00 1.2102248e+00 8.1117067e-01 7.0548283e-01 6.0964891e-01 6.0605366e-01 7.0918894e-01 9.0279223e-01 8.0008964e-01 3.6259865e-01 1.3154973e+00 1.0604287e+00 1.1536694e+00 9.1892454e-01 5.0477564e-01 5.0894102e-01 3.0922892e-01 8.0046764e-01 9.0778124e-01 7.1708289e-01 8.6225026e-01 6.8685125e-01 4.0363334e-01 8.4536936e-01 1.5318139e+00 6.5832080e-01 6.7636452e-01 6.3322667e-01 5.6838732e-01 1.8053679e+00 7.2044167e-01 1.2092602e+00 5.0437695e-01 1.3018595e+00 8.0291671e-01 1.0095513e+00 1.9760044e+00 1.0208709e+00 1.6387179e+00 1.0597877e+00 1.4686236e+00 6.0201716e-01 6.0427481e-01 9.3331138e-01 7.0025283e-01 6.1119558e-01 6.0427175e-01 7.4269200e-01 2.0887699e+00 2.2281421e+00 1.0001753e+00 1.0797700e+00 4.1317535e-01 2.0885107e+00 5.2133179e-01 9.6591465e-01 1.4140457e+00 4.1209001e-01 2.2573593e-01 8.1156529e-01 1.3450340e+00 1.5966664e+00 2.0855643e+00 8.1343016e-01 4.6440171e-01 8.2635069e-01 1.8444686e+00 8.2635069e-01 7.1621748e-01 2.0061436e-01 1.0088783e+00 9.1566538e-01 1.0032296e+00 5.0437695e-01 1.1543257e+00 9.8997136e-01 8.1117067e-01 7.0470867e-01 6.0980961e-01 6.3322667e-01 3.0474106e-01 9.0031539e-01 7.0000151e-01 3.3813251e-01 5.2167829e-01 8.5403428e-01 1.0095513e+00 5.0043842e-01 5.2524663e-01 6.0980961e-01 6.1288055e-01 3.0026460e-01 1.1001015e+00 7.1636719e-01 6.3309258e-01 7.4335736e-01 5.2167208e-01 5.0043084e-01 6.0184309e-01 6.0964891e-01 6.0017982e-01 3.0482299e-01 1.1153247e+00 5.0043084e-01 4.0246123e-01 4.0125062e-01 3.0017653e-01 1.1270411e+00 4.0002221e-01 2.0175565e+00 1.1056693e+00 1.9099615e+00 1.6003257e+00 1.8055799e+00 2.6186105e+00 1.2017042e+00 2.3090806e+00 1.8007233e+00 2.1233216e+00 1.1144002e+00 1.3025622e+00 1.5097103e+00 1.0216374e+00 1.2396937e+00 1.3452695e+00 1.5005647e+00 2.7241212e+00 2.9166918e+00 1.0087396e+00 1.7168636e+00 9.3733552e-01 2.7221286e+00 9.0508756e-01 1.7046111e+00 2.0107590e+00 8.0879701e-01 9.0508712e-01 1.6049314e+00 1.8174378e+00 2.1221146e+00 2.4750525e+00 1.6096791e+00 1.1000193e+00 1.6000016e+00 2.1732693e+00 1.6308665e+00 1.5004872e+00 8.0883916e-01 1.4182493e+00 1.6308803e+00 1.2094550e+00 1.1056693e+00 1.9088565e+00 1.7377460e+00 1.2661852e+00 1.0088926e+00 1.2092662e+00 1.4340155e+00 1.1019692e+00 3.4342562e-01 6.0964891e-01 5.6595908e-01 5.0437695e-01 5.2167829e-01 4.5783248e-01 1.4023777e+00 1.1286018e+00 1.2201578e+00 1.0032443e+00 3.0922892e-01 9.0642679e-01 9.0166476e-01 6.0964597e-01 5.0084481e-01 8.6054545e-01 9.6574336e-01 8.0923926e-01 5.0477564e-01 9.0532093e-01 1.6742781e+00 7.8895472e-01 7.5564478e-01 7.4618926e-01 6.0964891e-01 1.9222003e+00 8.2462252e-01 1.2093908e+00 5.2201750e-01 1.0522594e+00 7.0548138e-01 9.3735629e-01 1.7578497e+00 1.4001717e+00 1.4326118e+00 9.0166431e-01 1.3681502e+00 7.1636719e-01 4.5148429e-01 7.1183012e-01 6.3164977e-01 9.0534502e-01 8.5583415e-01 6.3322667e-01 1.9047821e+00 2.0429861e+00 3.3813251e-01 9.4634218e-01 7.1700774e-01 1.8662975e+00 3.0474106e-01 9.1695534e-01 1.1636098e+00 3.3818226e-01 5.0476836e-01 7.4329527e-01 1.0171202e+00 1.3020942e+00 1.8013674e+00 7.8935898e-01 3.0474106e-01 7.0008735e-01 1.4927071e+00 1.0336860e+00 6.7720957e-01 5.0855778e-01 7.3895268e-01 9.4622126e-01 8.4540285e-01 5.2201750e-01 1.0621172e+00 1.0788651e+00 8.1156529e-01 4.0002221e-01 5.6618864e-01 9.6935134e-01 5.2524663e-01 4.1212852e-01 5.0517282e-01 7.0008584e-01 6.3322667e-01 3.0490481e-01 1.2003660e+00 9.1552373e-01 1.0095513e+00 8.0046685e-01 4.5080200e-01 7.0105084e-01 6.0964891e-01 6.0365948e-01 5.0477564e-01 6.3178782e-01 7.4329527e-01 6.0201716e-01 2.2573593e-01 7.0096708e-01 1.4548054e+00 5.6347978e-01 5.2167208e-01 5.2133802e-01 4.0006662e-01 1.7132643e+00 6.0948506e-01 1.4655221e+00 7.0548283e-01 1.2921474e+00 9.1424701e-01 1.1900342e+00 1.9791159e+00 1.2013591e+00 1.6491682e+00 1.1111057e+00 1.5689626e+00 8.0726668e-01 7.4329527e-01 9.8998705e-01 8.0337471e-01 1.2004198e+00 1.1061923e+00 8.2635069e-01 2.0953157e+00 2.2622460e+00 6.0366256e-01 1.2097311e+00 8.0883841e-01 2.0866883e+00 6.0035621e-01 1.0858512e+00 1.3762609e+00 6.0000635e-01 6.0035305e-01 1.0143975e+00 1.2399444e+00 1.5291965e+00 1.9842703e+00 1.0777305e+00 4.1315633e-01 9.0005048e-01 1.7303039e+00 1.2394744e+00 8.2498722e-01 6.0018299e-01 9.9013884e-01 1.2395260e+00 1.1286911e+00 7.0548283e-01 1.3081175e+00 1.3479052e+00 1.1074834e+00 7.0184453e-01 8.1117067e-01 1.1186499e+00 6.0980961e-01 2.0181667e-01 5.2133802e-01 7.0548283e-01 4.0243965e-01 8.5471446e-01 9.1001664e-01 9.1916394e-01 6.0964891e-01 8.0296037e-01 1.0000307e+00 5.2524663e-01 4.1420960e-01 6.0000635e-01 8.0004523e-01 9.0166431e-01 9.0026588e-01 3.3818226e-01 6.0366256e-01 1.4340438e+00 8.0004523e-01 7.0000454e-01 7.0000151e-01 2.0000000e-01 1.4651632e+00 7.0008584e-01 1.7369589e+00 8.4540285e-01 1.6071563e+00 1.3008770e+00 1.5130871e+00 2.3098753e+00 1.5002444e+00 2.0034559e+00 1.5005854e+00 1.8322392e+00 8.5437498e-01 1.0087393e+00 1.2192920e+00 8.4786353e-01 1.1330694e+00 1.1270325e+00 1.2012867e+00 2.4144060e+00 2.6097166e+00 7.9153339e-01 1.4330116e+00 8.7209348e-01 2.4119960e+00 6.3178782e-01 1.4094452e+00 1.7039341e+00 5.6371422e-01 6.3309258e-01 1.3130937e+00 1.5066999e+00 1.8106410e+00 2.1515742e+00 1.3253458e+00 8.0004602e-01 1.3000908e+00 1.8533295e+00 1.3748188e+00 1.2012928e+00 5.7609230e-01 1.1298636e+00 1.3741846e+00 1.0451812e+00 8.4540285e-01 1.6176927e+00 1.4854079e+00 1.0777307e+00 7.4612830e-01 9.3306807e-01 1.1910068e+00 8.1715665e-01 4.0243965e-01 6.0184622e-01 6.0000952e-01 1.0158274e+00 1.1111057e+00 1.1191444e+00 8.0928056e-01 7.4335736e-01 1.2000002e+00 6.0964891e-01 3.0026460e-01 7.0088477e-01 1.0001601e+00 1.1024820e+00 1.1005458e+00 5.0042326e-01 8.0492246e-01 1.6321742e+00 1.0001753e+00 9.0005048e-01 9.0002615e-01 4.0006662e-01 1.6390068e+00 9.0029064e-01 1.6300430e+00 8.6084272e-01 1.5035329e+00 1.2004200e+00 1.4092511e+00 2.2043907e+00 1.7002548e+00 1.9010379e+00 1.4007831e+00 1.7240342e+00 7.4269314e-01 9.0534502e-01 1.1133984e+00 9.3184922e-01 1.0597992e+00 1.0142766e+00 1.1005364e+00 2.3071806e+00 2.5048249e+00 8.4536936e-01 1.3253910e+00 1.0116865e+00 2.3056305e+00 5.2838320e-01 1.3061582e+00 1.6010223e+00 4.8391482e-01 5.7608844e-01 1.2089313e+00 1.4017695e+00 1.7039229e+00 2.0293124e+00 1.2189760e+00 7.0096858e-01 1.2016380e+00 1.7295384e+00 1.2636227e+00 1.1005460e+00 6.1830489e-01 1.0208709e+00 1.2632948e+00 9.3329055e-01 8.6084272e-01 1.5130912e+00 1.3741813e+00 9.6572569e-01 6.5832080e-01 8.2418071e-01 1.0788007e+00 7.9148662e-01 3.0922892e-01 8.0046764e-01 1.3743342e+00 1.3452695e+00 1.3745152e+00 1.0776296e+00 8.0051115e-01 1.4000349e+00 8.2462252e-01 3.0026460e-01 5.7609230e-01 1.2089253e+00 1.3131370e+00 1.3002493e+00 7.0016860e-01 1.0426760e+00 1.8951252e+00 1.2036864e+00 1.1055892e+00 1.1055707e+00 6.3165225e-01 1.9760099e+00 1.1133895e+00 1.3035495e+00 1.0032296e+00 1.1134939e+00 8.1112984e-01 1.0427944e+00 1.8040883e+00 1.9000220e+00 1.5005626e+00 1.0010060e+00 1.3844234e+00 6.1288055e-01 5.7609230e-01 7.8890721e-01 1.1056785e+00 1.1270325e+00 9.0778124e-01 7.0556260e-01 1.9141294e+00 2.1052841e+00 8.2421923e-01 1.0150395e+00 1.2036863e+00 1.9046783e+00 5.2133802e-01 9.3733589e-01 1.2010584e+00 6.0948212e-01 7.0470867e-01 8.5583357e-01 1.0008768e+00 1.3033860e+00 1.6469593e+00 9.0294373e-01 5.0436965e-01 8.5406616e-01 1.3485619e+00 1.0522594e+00 7.0993998e-01 8.0250123e-01 7.4329527e-01 1.0427822e+00 9.0053003e-01 1.0032296e+00 1.1531951e+00 1.1543259e+00 9.0142681e-01 5.6618864e-01 6.1135434e-01 9.3984267e-01 9.0168933e-01 7.1629303e-01 1.5266891e+00 1.3564850e+00 1.4198077e+00 1.1544060e+00 7.0088627e-01 1.3008812e+00 7.2036951e-01 3.0482299e-01 7.4954884e-01 1.1531951e+00 1.2645755e+00 1.2053003e+00 6.1119267e-01 1.0803561e+00 1.9177201e+00 1.1286911e+00 1.0451689e+00 1.0433444e+00 7.2036951e-01 2.0856547e+00 1.0782211e+00 1.0434746e+00 9.0029064e-01 9.0279223e-01 6.0948800e-01 8.0883841e-01 1.6097492e+00 1.8003682e+00 1.3025173e+00 8.0879701e-01 1.1347620e+00 3.0922892e-01 3.6452132e-01 5.2133179e-01 1.0032293e+00 9.3308891e-01 6.0383105e-01 5.0043084e-01 1.7170314e+00 1.9082779e+00 8.5406616e-01 7.4275547e-01 1.1001110e+00 1.7132654e+00 4.1212852e-01 7.0548138e-01 1.0030871e+00 5.0085236e-01 6.0000635e-01 6.1135434e-01 8.0879701e-01 1.1134075e+00 1.4922778e+00 6.3322667e-01 4.0246123e-01 6.8261201e-01 1.1935004e+00 7.4954884e-01 5.0437695e-01 7.0008584e-01 4.5148429e-01 7.4262964e-01 6.0018299e-01 9.0029064e-01 9.1424701e-01 8.5437440e-01 6.0017665e-01 5.2167208e-01 3.0915245e-01 6.4620889e-01 8.0000160e-01 1.0033867e+00 7.3461436e-01 8.2512420e-01 6.0219099e-01 6.0017982e-01 6.0000317e-01 5.0000761e-01 7.0016860e-01 6.0202028e-01 4.5148429e-01 5.7630313e-01 5.0855778e-01 1.2699992e-01 5.0894102e-01 1.2671752e+00 4.1420960e-01 3.6259865e-01 3.4080442e-01 2.4170870e-01 1.5133193e+00 4.1317535e-01 1.5238388e+00 6.0980961e-01 1.4557632e+00 1.1002023e+00 1.3069713e+00 2.1693127e+00 1.1005458e+00 1.8443124e+00 1.3063934e+00 1.6655594e+00 6.5832080e-01 8.0492246e-01 1.0498228e+00 5.7832449e-01 9.1424701e-01 9.0320459e-01 1.0032296e+00 2.2802814e+00 2.4537354e+00 7.1621613e-01 1.2528590e+00 5.3914287e-01 2.2781292e+00 4.2362917e-01 1.2128138e+00 1.5640141e+00 3.4085233e-01 4.1212852e-01 1.1060939e+00 1.4140458e+00 1.7081323e+00 2.1436849e+00 1.1138955e+00 6.0184622e-01 1.1001015e+00 1.8659091e+00 1.1544060e+00 1.0010209e+00 3.3813251e-01 1.0224270e+00 1.1635398e+00 9.7600992e-01 6.0980961e-01 1.4182493e+00 1.2705641e+00 8.9538275e-01 5.4219811e-01 7.3084171e-01 9.6936870e-01 6.0184934e-01 3.0922892e-01 2.4170870e-01 4.0127250e-01 1.6009488e+00 1.0040629e+00 1.0499492e+00 1.2653025e+00 9.1471442e-01 6.1119558e-01 5.0477564e-01 9.0005048e-01 1.1016049e+00 5.0043084e-01 7.0096708e-01 7.0088627e-01 7.0470720e-01 7.0176121e-01 8.0967961e-01 6.3165225e-01 6.0201716e-01 2.5221737e+00 1.6096629e+00 2.4221589e+00 2.1015969e+00 2.3098905e+00 3.1317714e+00 1.0597879e+00 2.8188299e+00 2.3040148e+00 2.6373482e+00 1.6231306e+00 1.8068049e+00 2.0210084e+00 1.5237053e+00 1.7080686e+00 1.8459262e+00 2.0034094e+00 3.2387184e+00 3.4286399e+00 1.5005854e+00 2.2285172e+00 1.4324350e+00 3.2358000e+00 1.4109628e+00 2.2110849e+00 2.5224740e+00 1.3139336e+00 1.4095777e+00 2.1090366e+00 2.3323064e+00 2.6377472e+00 2.9966835e+00 2.1144760e+00 1.6012568e+00 2.1000482e+00 2.6968519e+00 2.1346646e+00 2.0025815e+00 1.3133662e+00 1.9351024e+00 2.1377869e+00 1.7137965e+00 1.6096629e+00 2.4161682e+00 2.2434416e+00 1.7684500e+00 1.5143051e+00 1.7168636e+00 1.9368172e+00 1.6050040e+00 1.1269424e-01 3.3818226e-01 1.3017961e+00 7.4612830e-01 1.0262619e+00 1.2443200e+00 8.2421923e-01 6.0202028e-01 2.2573593e-01 6.0017982e-01 8.4572653e-01 3.0922892e-01 5.6347978e-01 4.1317535e-01 6.0964891e-01 5.2201750e-01 7.3090905e-01 8.0245824e-01 4.1420960e-01 2.2297880e+00 1.3131802e+00 2.1734835e+00 1.8042696e+00 2.0169191e+00 2.8857511e+00 7.7598796e-01 2.5607759e+00 2.0181988e+00 2.3909895e+00 1.3770846e+00 1.5195166e+00 1.7689720e+00 1.2362756e+00 1.4651863e+00 1.5800353e+00 1.7155605e+00 3.0010211e+00 3.1712557e+00 1.2016443e+00 1.9735224e+00 1.1531953e+00 2.9937162e+00 1.1401191e+00 1.9335528e+00 2.2793947e+00 1.0403116e+00 1.1237940e+00 1.8155572e+00 2.1170640e+00 2.4166673e+00 2.8369211e+00 1.8227568e+00 1.3135522e+00 1.8005404e+00 2.5443612e+00 1.8557670e+00 1.7105814e+00 1.0313359e+00 1.7226330e+00 1.8708183e+00 1.5881025e+00 1.3131802e+00 2.1363271e+00 1.9752912e+00 1.5530527e+00 1.2366099e+00 1.4501583e+00 1.6655594e+00 1.3088083e+00 3.4342562e-01 1.4024091e+00 8.3183672e-01 1.0522594e+00 1.2712749e+00 8.5437498e-01 6.1119558e-01 3.3813251e-01 7.0016860e-01 9.2867113e-01 3.4342562e-01 5.2133179e-01 5.0855778e-01 6.3192325e-01 5.6618864e-01 7.5564478e-01 7.0462844e-01 4.5847767e-01 2.3345511e+00 1.4181033e+00 2.2624227e+00 1.9044571e+00 2.1188381e+00 2.9740725e+00 8.7209348e-01 2.6511588e+00 2.1151751e+00 2.4832720e+00 1.4692287e+00 1.6190709e+00 1.8603115e+00 1.3450304e+00 1.5778323e+00 1.6856949e+00 1.8133657e+00 3.0879393e+00 3.2627356e+00 1.3017553e+00 2.0678448e+00 1.2635708e+00 3.0810441e+00 1.2366675e+00 2.0307764e+00 2.3657459e+00 1.1404856e+00 1.2257611e+00 1.9176961e+00 2.1957105e+00 2.4980314e+00 2.9055191e+00 1.9262438e+00 1.4100098e+00 1.9004485e+00 2.6116431e+00 1.9609333e+00 1.8095574e+00 1.1347620e+00 1.8037909e+00 1.9725464e+00 1.6581521e+00 1.4181033e+00 2.2355461e+00 2.0784269e+00 1.6462102e+00 1.3373104e+00 1.5468618e+00 1.7698041e+00 1.4111252e+00 1.2003596e+00 6.1288055e-01 7.4618926e-01 9.6691372e-01 5.7609230e-01 3.0922892e-01 3.0490481e-01 5.0436965e-01 7.0184453e-01 1.1269424e-01 8.2635069e-01 3.0482299e-01 3.3813251e-01 3.0490481e-01 4.5148429e-01 9.3308891e-01 2.0181667e-01 2.1221982e+00 1.2089191e+00 2.0305682e+00 1.7009400e+00 1.9088256e+00 2.7434081e+00 9.1894698e-01 2.4265154e+00 1.9046790e+00 2.2453370e+00 1.2284047e+00 1.4060413e+00 1.6267848e+00 1.1281352e+00 1.3523310e+00 1.4562730e+00 1.6032169e+00 2.8519346e+00 3.0369970e+00 1.1020600e+00 1.8342569e+00 1.0426638e+00 2.8491513e+00 1.0116721e+00 1.8114932e+00 2.1335035e+00 9.1552373e-01 1.0090312e+00 1.7079369e+00 1.9522117e+00 2.2566913e+00 2.6406601e+00 1.7139238e+00 1.2013529e+00 1.7000137e+00 2.3442222e+00 1.7386523e+00 1.6019500e+00 9.1449234e-01 1.5517970e+00 1.7435092e+00 1.3757183e+00 1.2089191e+00 2.0167186e+00 1.8496945e+00 1.3938438e+00 1.1152390e+00 1.3189663e+00 1.5429659e+00 1.2037520e+00 6.7720957e-01 7.4262850e-01 7.0911112e-01 7.0633229e-01 1.0011648e+00 1.1020600e+00 7.2036951e-01 5.0477564e-01 1.1005460e+00 1.8106900e+00 9.0166431e-01 9.0192695e-01 9.0055475e-01 8.0055465e-01 2.1027376e+00 1.0003198e+00 1.0225570e+00 3.0474106e-01 1.1299441e+00 5.0517282e-01 7.5564478e-01 1.7513222e+00 1.1055799e+00 1.4140515e+00 7.8895472e-01 1.3181953e+00 5.7608844e-01 4.1315633e-01 8.1156529e-01 4.1317535e-01 8.0004523e-01 7.2044167e-01 5.2524663e-01 1.8787830e+00 1.9768256e+00 5.0001522e-01 9.4912864e-01 4.5148429e-01 1.8635775e+00 3.0915245e-01 7.8611860e-01 1.2373911e+00 3.0922892e-01 3.0922892e-01 5.7609230e-01 1.2089834e+00 1.4324608e+00 1.9471600e+00 6.3912943e-01 3.0017653e-01 5.0043842e-01 1.7149040e+00 8.6084272e-01 4.8391482e-01 3.4080442e-01 9.0668287e-01 8.6225026e-01 9.3424659e-01 3.0474106e-01 9.3861512e-01 9.5646231e-01 7.8935898e-01 3.4085233e-01 5.2491734e-01 7.8940551e-01 3.0482299e-01 6.0948506e-01 1.3000044e+00 9.3308891e-01 4.0243965e-01 5.6371422e-01 4.1212852e-01 7.0000303e-01 5.4219811e-01 1.2105001e+00 3.4342562e-01 3.6256305e-01 3.4085233e-01 8.0008964e-01 1.5005854e+00 4.1420960e-01 1.5358856e+00 6.1990228e-01 1.7849054e+00 1.1528477e+00 1.3788456e+00 2.4261894e+00 5.6370994e-01 2.0884901e+00 1.4655452e+00 1.9390732e+00 1.1074834e+00 1.0434746e+00 1.4340155e+00 6.0605366e-01 9.1554656e-01 1.0782857e+00 1.1897288e+00 2.5394068e+00 2.6516318e+00 8.3183606e-01 1.5694554e+00 5.2201750e-01 2.5386539e+00 9.0192695e-01 1.4157600e+00 1.8949500e+00 8.0097499e-01 7.0548138e-01 1.1935069e+00 1.8443040e+00 2.0853276e+00 2.5816887e+00 1.1989547e+00 9.1424659e-01 1.1138955e+00 2.3468430e+00 1.1963432e+00 1.1270327e+00 6.0365948e-01 1.5143051e+00 1.3938114e+00 1.5079206e+00 6.1990228e-01 1.5829749e+00 1.4450801e+00 1.3189240e+00 9.1132198e-01 1.1152300e+00 1.0159134e+00 6.3309012e-01 7.0096858e-01 1.1002025e+00 4.8852375e-01 9.1024401e-01 8.1112984e-01 4.0127250e-01 8.1117067e-01 1.3486924e+00 7.0633229e-01 4.6440171e-01 5.1257987e-01 5.0517282e-01 1.5260594e+00 6.1288055e-01 1.5131090e+00 7.4335736e-01 1.4549432e+00 1.1020600e+00 1.3036236e+00 2.1691920e+00 1.1527669e+00 1.8444686e+00 1.3309288e+00 1.6567564e+00 6.3925756e-01 8.5617086e-01 1.0458540e+00 9.0668287e-01 8.4540285e-01 8.5586571e-01 1.0039209e+00 2.2782572e+00 2.4540263e+00 1.2012866e+00 1.2440282e+00 6.2656178e-01 2.2782572e+00 7.0556260e-01 1.2101609e+00 1.5639802e+00 6.0219099e-01 4.5148429e-01 1.1079931e+00 1.4142064e+00 1.7087610e+00 2.1412345e+00 1.1115204e+00 6.7720957e-01 1.1281352e+00 1.8646736e+00 1.1282162e+00 1.0010209e+00 4.1315633e-01 1.0172673e+00 1.1401191e+00 9.4532171e-01 7.4335736e-01 1.4134218e+00 1.2440229e+00 8.4786353e-01 9.0557807e-01 7.2440846e-01 9.3308853e-01 6.0964891e-01 8.0296037e-01 1.1055799e+00 1.2124837e+00 1.2014191e+00 6.0000952e-01 9.3755356e-01 1.7874653e+00 1.1024913e+00 1.0032296e+00 1.0031018e+00 5.2201750e-01 1.8640262e+00 1.0088926e+00 1.3452347e+00 9.0417295e-01 1.2040344e+00 9.0168933e-01 1.1133986e+00 1.9046783e+00 1.8005318e+00 1.6009504e+00 1.1056691e+00 1.4335330e+00 5.2167829e-01 6.1990228e-01 8.2418141e-01 1.0118233e+00 1.0151880e+00 8.2458478e-01 8.0051115e-01 2.0076819e+00 2.2050331e+00 9.3329017e-01 1.0426638e+00 1.1020600e+00 2.0062587e+00 4.5847767e-01 1.0087393e+00 1.3009222e+00 5.0855778e-01 6.0202028e-01 9.1471442e-01 1.1019505e+00 1.4044980e+00 1.7386523e+00 9.3351278e-01 4.5783248e-01 9.1892454e-01 1.4407364e+00 1.0151880e+00 8.0093081e-01 7.0088627e-01 7.4269200e-01 1.0142482e+00 8.0250123e-01 9.0417295e-01 1.2189645e+00 1.1269510e+00 8.0879701e-01 6.1990228e-01 5.6371422e-01 8.6084272e-01 8.0291749e-01 7.8935813e-01 8.0250123e-01 8.0046685e-01 7.0017011e-01 5.2491734e-01 1.3741813e+00 7.0470720e-01 7.4269314e-01 6.7626502e-01 6.0000635e-01 1.4852616e+00 6.3309012e-01 1.6636721e+00 7.5826453e-01 1.5161847e+00 1.2049539e+00 1.4220925e+00 2.2191056e+00 1.4001717e+00 1.9083789e+00 1.4007861e+00 1.7945122e+00 9.6133119e-01 9.1552373e-01 1.1416778e+00 7.7603846e-01 1.1170561e+00 1.1351073e+00 1.1152390e+00 2.3540839e+00 2.5167781e+00 6.0202028e-01 1.3687074e+00 8.0713433e-01 2.3222107e+00 5.7608844e-01 1.3563898e+00 1.6193612e+00 5.7609230e-01 7.3090905e-01 1.2201578e+00 1.4221192e+00 1.7236083e+00 2.1360791e+00 1.2373857e+00 7.1629168e-01 1.2000731e+00 1.7962160e+00 1.3755348e+00 1.1298552e+00 7.2113820e-01 1.0843962e+00 1.3150470e+00 1.0664292e+00 7.5826453e-01 1.5362595e+00 1.4451976e+00 1.0604287e+00 6.7626502e-01 8.9540816e-01 1.2552585e+00 8.0073117e-01 5.0001522e-01 4.1212852e-01 5.6347549e-01 4.0127250e-01 8.7240114e-01 3.0008832e-01 1.2085435e-01 1.2085435e-01 6.0017982e-01 1.1038933e+00 2.0061436e-01 1.9231154e+00 1.0088926e+00 1.8971338e+00 1.5035330e+00 1.7143629e+00 2.6071033e+00 7.2440846e-01 2.2781292e+00 1.7231818e+00 2.0998984e+00 1.0923537e+00 1.2224463e+00 1.4922544e+00 9.3733589e-01 1.1896725e+00 1.2782589e+00 1.4186216e+00 2.7177641e+00 2.8857013e+00 9.6674360e-01 1.6882618e+00 8.5406616e-01 2.7167827e+00 8.6084272e-01 1.6345263e+00 2.0058565e+00 7.5508853e-01 8.1715593e-01 1.5132180e+00 1.8635428e+00 2.1554613e+00 2.5926811e+00 1.5194972e+00 1.0207533e+00 1.5005626e+00 2.3165875e+00 1.5429659e+00 1.4098467e+00 7.2036819e-01 1.4724918e+00 1.5757929e+00 1.3838027e+00 1.0088926e+00 1.8378491e+00 1.6745686e+00 1.2948699e+00 9.4912864e-01 1.1635325e+00 1.3473688e+00 1.0032293e+00 4.0004442e-01 6.9509552e-01 3.0017653e-01 7.1708289e-01 2.2573593e-01 5.0085236e-01 4.0243965e-01 7.0548138e-01 1.0008617e+00 3.0482299e-01 2.0206913e+00 1.1056785e+00 2.0075255e+00 1.6053217e+00 1.8156855e+00 2.7170183e+00 6.3912709e-01 2.3873965e+00 1.8286172e+00 2.2132187e+00 1.2079042e+00 1.3276450e+00 1.6018644e+00 1.0207396e+00 1.2397507e+00 1.3715471e+00 1.5245240e+00 2.8327619e+00 2.9941199e+00 1.0032443e+00 1.7962160e+00 9.3329055e-01 2.8269181e+00 9.6936870e-01 1.7435156e+00 2.1174156e+00 8.6084272e-01 9.2351241e-01 1.6144550e+00 1.9761215e+00 2.2674248e+00 2.7114616e+00 1.6190709e+00 1.1282247e+00 1.6009322e+00 2.4285500e+00 1.6432478e+00 1.5147271e+00 8.2512420e-01 1.5839544e+00 1.6753044e+00 1.4829434e+00 1.1056785e+00 1.9427965e+00 1.7734131e+00 1.3908238e+00 1.0498226e+00 1.2712749e+00 1.4523130e+00 1.1044111e+00 6.0980961e-01 4.1209001e-01 1.1020600e+00 2.0181667e-01 4.0243965e-01 3.0922892e-01 7.0088627e-01 1.4001688e+00 3.0922892e-01 1.6797901e+00 7.8935898e-01 1.7574606e+00 1.2224463e+00 1.4618181e+00 2.4274025e+00 6.3165225e-01 2.0887499e+00 1.4865883e+00 1.9561612e+00 1.0664292e+00 1.0336863e+00 1.3939836e+00 8.2421923e-01 1.2089895e+00 1.1997296e+00 1.1938630e+00 2.5462062e+00 2.6763758e+00 6.4049114e-01 1.5645185e+00 8.0883916e-01 2.5391583e+00 8.3183672e-01 1.4351453e+00 1.8644317e+00 7.4618926e-01 6.9987517e-01 1.2680580e+00 1.7844580e+00 2.0440071e+00 2.5329067e+00 1.2921474e+00 8.5440680e-01 1.2036924e+00 2.2838099e+00 1.3737025e+00 1.1587585e+00 6.4620889e-01 1.4491800e+00 1.4507713e+00 1.4583848e+00 7.8935898e-01 1.6277433e+00 1.5384791e+00 1.3150470e+00 8.7209348e-01 1.0788651e+00 1.2179890e+00 7.4954884e-01 6.1135434e-01 1.3790270e+00 5.2491734e-01 4.5147187e-01 4.5080200e-01 3.0026460e-01 1.6179159e+00 5.2167829e-01 1.4543196e+00 5.6838732e-01 1.3502290e+00 1.0008620e+00 1.2192919e+00 2.0611274e+00 1.2013529e+00 1.7369589e+00 1.2053003e+00 1.5767956e+00 6.3925756e-01 7.1779518e-01 9.6131279e-01 6.4620889e-01 1.0032443e+00 9.3331138e-01 9.0279223e-01 2.1713885e+00 2.3476282e+00 8.0245903e-01 1.1755517e+00 6.3322667e-01 2.1693131e+00 4.2362917e-01 1.1187430e+00 1.4544336e+00 4.0246123e-01 4.1209001e-01 1.0208844e+00 1.3018144e+00 1.5969056e+00 2.0303761e+00 1.0427944e+00 5.0085236e-01 1.0008465e+00 1.7574039e+00 1.1274284e+00 9.0166476e-01 4.0125062e-01 9.3437551e-01 1.1319099e+00 9.6935134e-01 5.6838732e-01 1.3309288e+00 1.2428507e+00 9.2745734e-01 5.7630313e-01 6.8160885e-01 9.6672602e-01 5.2167208e-01 8.5440680e-01 2.2608083e-01 4.0125062e-01 3.0490481e-01 4.2270142e-01 1.0207262e+00 2.0181667e-01 2.0282636e+00 1.1133895e+00 1.9386586e+00 1.6012719e+00 1.8114342e+00 2.6515677e+00 9.0999313e-01 2.3322945e+00 1.8060515e+00 2.1578375e+00 1.1447365e+00 1.3085752e+00 1.5359734e+00 1.0426516e+00 1.3018144e+00 1.3779960e+00 1.5044724e+00 2.7627283e+00 2.9432651e+00 1.0010209e+00 1.7447170e+00 9.6576136e-01 2.7579768e+00 9.1892454e-01 1.7159977e+00 2.0420308e+00 8.2635069e-01 9.1576742e-01 1.6105700e+00 1.8662195e+00 2.1696258e+00 2.5680120e+00 1.6184785e+00 1.1020600e+00 1.6000183e+00 2.2731185e+00 1.6529028e+00 1.5029725e+00 8.2635069e-01 1.4699000e+00 1.6570292e+00 1.3301857e+00 1.1133895e+00 1.9214944e+00 1.7646791e+00 1.3272142e+00 1.0235120e+00 1.2275665e+00 1.4621584e+00 1.1060939e+00 9.1576742e-01 9.6133119e-01 9.4532171e-01 1.2662318e+00 3.0490481e-01 8.6084272e-01 2.7230933e+00 1.8083405e+00 2.7192500e+00 2.3152780e+00 2.5278895e+00 3.4307167e+00 1.2089253e+00 3.1023107e+00 2.5446557e+00 2.9238544e+00 1.9071235e+00 2.0445123e+00 2.3113306e+00 1.7148932e+00 1.8686471e+00 2.0692591e+00 2.2408459e+00 3.5448121e+00 3.7102716e+00 1.7134856e+00 2.5076804e+00 1.6187837e+00 3.5398901e+00 1.6780493e+00 2.4594169e+00 2.8277283e+00 1.5698091e+00 1.6365650e+00 2.3270565e+00 2.6739856e+00 2.9710335e+00 3.3954286e+00 2.3304578e+00 1.8446316e+00 2.3054869e+00 3.1050823e+00 2.3405162e+00 2.2288004e+00 1.5327217e+00 2.2740189e+00 2.3840998e+00 2.1122339e+00 1.8083405e+00 2.6588338e+00 2.4791402e+00 2.0688078e+00 1.7632513e+00 1.9828965e+00 2.1428811e+00 1.8095574e+00 3.0017653e-01 2.0061436e-01 6.0017982e-01 1.2012991e+00 1.2085435e-01 1.8301371e+00 9.1424659e-01 1.8223693e+00 1.4049093e+00 1.6190709e+00 2.5271432e+00 7.0556260e-01 2.1953731e+00 1.6303102e+00 2.0261311e+00 1.0363096e+00 1.1330692e+00 1.4229011e+00 8.5406674e-01 1.1527746e+00 1.2106302e+00 1.3261864e+00 2.6410980e+00 2.8004332e+00 8.1117067e-01 1.6146557e+00 7.8886054e-01 2.6375763e+00 7.9824795e-01 1.5471213e+00 1.9317133e+00 6.9509552e-01 7.3155911e-01 1.4182194e+00 1.8031267e+00 2.0887452e+00 2.5422790e+00 1.4267527e+00 9.3308891e-01 1.4006149e+00 2.2706274e+00 1.4614246e+00 1.3141581e+00 6.4049114e-01 1.4230277e+00 1.5004249e+00 1.3669148e+00 9.1424659e-01 1.7490906e+00 1.5981930e+00 1.2553121e+00 8.7212232e-01 1.0924484e+00 1.2731059e+00 9.0557807e-01 1.1269424e-01 5.0002283e-01 1.2049541e+00 2.0121983e-01 1.8447840e+00 9.3329055e-01 1.7901165e+00 1.4035225e+00 1.6222582e+00 2.4980210e+00 8.1757693e-01 2.1693127e+00 1.6187837e+00 2.0049100e+00 1.0151397e+00 1.1261381e+00 1.3938113e+00 9.0657539e-01 1.2362756e+00 1.2472959e+00 1.3154932e+00 2.6088344e+00 2.7785257e+00 9.0207914e-01 1.5972548e+00 8.5406674e-01 2.6071034e+00 7.7652636e-01 1.5358856e+00 1.8953567e+00 6.9518117e-01 7.4612718e-01 1.4220925e+00 1.7511588e+00 2.0440071e+00 2.4804818e+00 1.4362913e+00 9.1449234e-01 1.4003402e+00 2.2077377e+00 1.4867147e+00 1.3085752e+00 6.7720780e-01 1.3734975e+00 1.5100598e+00 1.3269962e+00 9.3329055e-01 1.7441015e+00 1.6143613e+00 1.2552584e+00 8.7796615e-01 1.0782751e+00 1.3029195e+00 9.1424659e-01 5.0000761e-01 1.2040406e+00 1.1269424e-01 1.8289847e+00 9.1424701e-01 1.7872748e+00 1.4023776e+00 1.6144390e+00 2.4974488e+00 8.0533198e-01 2.1691714e+00 1.6179842e+00 1.9948417e+00 9.9013884e-01 1.1186586e+00 1.3842454e+00 8.5583357e-01 1.1527671e+00 1.1990152e+00 1.3139296e+00 2.6085083e+00 2.7775771e+00 8.5440680e-01 1.5835478e+00 7.8886139e-01 2.6068470e+00 7.5508853e-01 1.5300146e+00 1.8950932e+00 6.5712813e-01 7.2036951e-01 1.4134189e+00 1.7511120e+00 2.0435901e+00 2.4807480e+00 1.4220898e+00 9.1424701e-01 1.4002005e+00 2.2048860e+00 1.4562730e+00 1.3069754e+00 6.3309258e-01 1.3632211e+00 1.4815986e+00 1.2921474e+00 9.1424701e-01 1.7351894e+00 1.5836236e+00 1.2085436e+00 8.4725834e-01 1.0597879e+00 1.2653025e+00 9.0508756e-01 1.3743342e+00 5.0043084e-01 1.7369589e+00 8.2635069e-01 1.6144390e+00 1.3008770e+00 1.5131090e+00 2.3226028e+00 1.3004854e+00 2.0107294e+00 1.5010034e+00 1.8390199e+00 8.5471446e-01 1.0087539e+00 1.2223855e+00 8.0073117e-01 1.1286018e+00 1.1270411e+00 1.2013529e+00 2.4290388e+00 2.6198428e+00 7.8895472e-01 1.4363167e+00 7.7598796e-01 2.4266979e+00 6.3178782e-01 1.4100098e+00 1.7134977e+00 5.6347549e-01 6.3165225e-01 1.3130978e+00 1.5237265e+00 1.8289379e+00 2.1979419e+00 1.3253497e+00 8.0004602e-01 1.3000455e+00 1.9028805e+00 1.3748188e+00 1.2012991e+00 5.6371422e-01 1.1400420e+00 1.3748220e+00 1.0597992e+00 8.2635069e-01 1.6184929e+00 1.4858469e+00 1.0797702e+00 7.4612830e-01 9.3329055e-01 1.1910002e+00 8.0923926e-01 1.1056785e+00 3.0089448e+00 2.1019570e+00 2.9563162e+00 2.6052626e+00 2.8107271e+00 3.6717374e+00 1.5012719e+00 3.3522198e+00 2.8186527e+00 3.1596417e+00 2.1362161e+00 2.3151198e+00 2.5461024e+00 2.0036629e+00 2.1224665e+00 2.3234228e+00 2.5150159e+00 3.7801779e+00 3.9623034e+00 2.0033816e+00 2.7467405e+00 1.9044216e+00 3.7784933e+00 1.9231092e+00 2.7237438e+00 3.0623796e+00 1.8186729e+00 1.9089573e+00 2.6097166e+00 2.8846684e+00 3.1885494e+00 3.5706709e+00 2.6109888e+00 2.1139043e+00 2.6017555e+00 3.2706949e+00 2.6138780e+00 2.5099937e+00 1.8069857e+00 2.4748863e+00 2.6338874e+00 2.2385678e+00 2.1019570e+00 2.9251726e+00 2.7321685e+00 2.2661994e+00 2.0190735e+00 2.2288464e+00 2.4131370e+00 2.1020441e+00 1.9226845e+00 1.0087252e+00 1.8684939e+00 1.5017097e+00 1.7108631e+00 2.5816562e+00 8.0533198e-01 2.2563154e+00 1.7134977e+00 2.0770423e+00 1.0604287e+00 1.2124777e+00 1.4620239e+00 9.3329017e-01 1.1896594e+00 1.2705641e+00 1.4098496e+00 2.6923501e+00 2.8659663e+00 9.1449234e-01 1.6637458e+00 8.5403428e-01 2.6902417e+00 8.3183672e-01 1.6225614e+00 1.9757175e+00 7.3084048e-01 8.1117067e-01 1.5097082e+00 1.8197097e+00 2.1169795e+00 2.5408329e+00 1.5160570e+00 1.0087393e+00 1.5001233e+00 2.2573596e+00 1.5423651e+00 1.4049376e+00 7.1708289e-01 1.4229011e+00 1.5611429e+00 1.3142952e+00 1.0087252e+00 1.8271493e+00 1.6639408e+00 1.2552635e+00 9.2768675e-01 1.1400420e+00 1.3479052e+00 1.0031018e+00 9.3184922e-01 8.0291749e-01 7.0910969e-01 3.4342562e-01 1.3028005e+00 1.6474201e+00 1.0216374e+00 8.5586571e-01 9.0026543e-01 9.0508756e-01 7.7598796e-01 5.7832449e-01 1.0522594e+00 9.0999313e-01 7.0008735e-01 7.1708289e-01 1.4049376e+00 1.4220925e+00 1.2553121e+00 6.0202028e-01 1.1170561e+00 1.4055109e+00 1.1186497e+00 4.5783248e-01 9.3306769e-01 1.2101609e+00 1.1134939e+00 5.3914287e-01 1.0144117e+00 1.1074742e+00 1.6007365e+00 5.2491734e-01 1.0797700e+00 1.1139044e+00 1.4000349e+00 4.0004442e-01 7.1629303e-01 1.2090477e+00 6.8170466e-01 4.5148429e-01 9.1427000e-01 9.3184922e-01 5.0043842e-01 4.1209001e-01 8.0296037e-01 1.0498226e+00 8.0928056e-01 6.0018299e-01 9.3446811e-01 1.3131410e+00 5.6371422e-01 7.8985507e-01 1.8949500e+00 9.1427000e-01 1.5639785e+00 9.3308891e-01 1.4501583e+00 7.1621748e-01 6.0017665e-01 1.0010209e+00 2.0181667e-01 5.0000761e-01 6.3925756e-01 7.0548283e-01 2.0162299e+00 2.0885102e+00 5.2167829e-01 1.1079931e+00 2.2608083e-01 2.0057464e+00 5.0043084e-01 9.2747919e-01 1.4186217e+00 4.1212852e-01 3.4085233e-01 6.3178782e-01 1.4043632e+00 1.6175925e+00 2.1298991e+00 6.3309258e-01 5.2133179e-01 5.6595908e-01 1.9078843e+00 7.4418186e-01 6.1830764e-01 3.4085233e-01 1.1006468e+00 9.1132198e-01 1.1010711e+00 0.0000000e+00 1.0458540e+00 9.3984267e-01 9.0166476e-01 5.0043084e-01 7.0088627e-01 7.0993998e-01 3.0017653e-01 8.0093160e-01 6.0000635e-01 7.1621613e-01 2.2268632e+00 4.1317535e-01 5.2491734e-01 6.0964891e-01 8.2421923e-01 7.4335736e-01 4.1209001e-01 1.4186217e+00 1.3131410e+00 7.4275547e-01 6.1119267e-01 9.1566538e-01 1.0095513e+00 1.1796101e+00 2.5399984e-01 1.5237074e+00 8.2421923e-01 1.0429127e+00 4.1315633e-01 3.0490481e-01 1.1528553e+00 1.1270325e+00 7.0096708e-01 5.0001522e-01 3.1328089e-01 9.0657583e-01 7.0096858e-01 9.1568820e-01 1.0216374e+00 6.0035305e-01 8.0337471e-01 7.0548283e-01 1.2396937e+00 5.0043084e-01 4.2270142e-01 8.0008964e-01 1.3131410e+00 3.0915245e-01 4.5847767e-01 7.0470720e-01 9.6936870e-01 7.4262964e-01 9.0645118e-01 1.2190260e+00 4.0246123e-01 1.3450652e+00 1.4544312e+00 1.0207258e+00 4.5147187e-01 9.6501813e-01 5.0517282e-01 3.0490481e-01 5.0437695e-01 6.8170466e-01 6.5712813e-01 5.0855778e-01 2.0121983e-01 1.4695463e+00 1.5267750e+00 7.4395693e-01 6.3309258e-01 7.8890806e-01 1.4542932e+00 7.0008432e-01 4.5784410e-01 9.0166431e-01 8.0000160e-01 7.0008584e-01 3.0017653e-01 9.0005094e-01 1.1019505e+00 1.6144405e+00 4.0004442e-01 5.0436965e-01 4.1315633e-01 1.4012283e+00 6.3164729e-01 2.0121983e-01 8.0046685e-01 6.0219099e-01 6.0964597e-01 6.5724028e-01 5.6371422e-01 5.6838732e-01 7.0911112e-01 5.3914287e-01 6.0948506e-01 4.0246123e-01 5.6371422e-01 5.2133179e-01 1.1281267e+00 1.6745375e+00 8.1112984e-01 5.2167208e-01 7.4395693e-01 7.0016860e-01 5.0855778e-01 3.3813251e-01 9.0659977e-01 7.8895472e-01 5.0043842e-01 4.1209001e-01 1.2528048e+00 1.3020492e+00 9.3861512e-01 4.0127250e-01 1.0142766e+00 1.2362810e+00 9.0168933e-01 3.0490481e-01 7.0478886e-01 1.0010209e+00 9.0279223e-01 2.2608083e-01 7.4262850e-01 9.0055475e-01 1.4109657e+00 2.2573593e-01 7.8895472e-01 8.0492246e-01 1.2000668e+00 4.0363334e-01 4.1212852e-01 1.0039060e+00 4.5080200e-01 2.4195741e-01 7.0462844e-01 7.8985507e-01 3.0490481e-01 3.4085233e-01 6.0017982e-01 8.0928056e-01 6.0017665e-01 4.5784410e-01 7.4612718e-01 2.7992301e+00 3.6259865e-01 9.6953662e-01 6.4620889e-01 1.5401330e+00 1.4140789e+00 1.1281265e+00 2.0058560e+00 1.8949500e+00 1.4140515e+00 1.2396937e+00 8.0000239e-01 4.1317535e-01 1.8064092e+00 9.3310976e-01 2.1167364e+00 2.0181667e-01 1.7570696e+00 1.0143975e+00 6.1135434e-01 1.8661434e+00 1.8197089e+00 1.2632995e+00 8.1112909e-01 5.0126466e-01 8.0051115e-01 1.2632996e+00 1.5975200e+00 1.5266891e+00 5.0043084e-01 1.3452695e+00 1.3018553e+00 1.9314575e+00 1.2089192e+00 1.0777307e+00 1.5030978e+00 1.8949500e+00 8.5409862e-01 1.0151880e+00 1.4180463e+00 1.6742781e+00 1.4542907e+00 1.4853863e+00 1.8197089e+00 2.4725511e+00 1.8443040e+00 2.3518103e+00 1.6032169e+00 1.5066818e+00 1.9080163e+00 8.0923851e-01 9.4532171e-01 1.5109394e+00 1.6179000e+00 2.9132779e+00 2.9705619e+00 1.1020600e+00 2.0185049e+00 7.0633229e-01 2.9085831e+00 1.4001717e+00 1.8310931e+00 2.3325127e+00 1.3000908e+00 1.2016381e+00 1.5402579e+00 2.3143334e+00 2.5314248e+00 3.0393618e+00 1.5405402e+00 1.4018011e+00 1.3018553e+00 2.8185850e+00 1.4728279e+00 1.5248833e+00 1.1020506e+00 2.0036931e+00 1.8191683e+00 2.0009584e+00 9.1427000e-01 1.9534067e+00 1.8336293e+00 1.8020058e+00 1.4006178e+00 1.6026130e+00 1.3506710e+00 1.0116724e+00 6.3912709e-01 7.8890806e-01 1.2190319e+00 1.0776296e+00 8.0923926e-01 1.6740888e+00 1.5650163e+00 1.0798806e+00 9.0155438e-01 9.0417295e-01 6.4049114e-01 1.4685147e+00 6.4049114e-01 1.7843537e+00 4.5148429e-01 1.4324350e+00 6.8261201e-01 3.3813251e-01 1.5401311e+00 1.4852570e+00 9.3329055e-01 5.0043842e-01 2.0181667e-01 9.1424701e-01 9.3424697e-01 1.2633467e+00 1.2093243e+00 5.2167829e-01 1.0313359e+00 9.6574336e-01 1.5965767e+00 9.0168933e-01 7.7603846e-01 1.2016443e+00 1.5639785e+00 5.7832449e-01 7.7882758e-01 1.1074742e+00 1.3452311e+00 1.1281352e+00 1.1558746e+00 1.4852570e+00 1.1153247e+00 7.8895472e-01 5.0477564e-01 5.0855778e-01 1.0426636e+00 9.4532171e-01 7.3155911e-01 5.0476836e-01 1.3669148e+00 1.1910003e+00 8.5471446e-01 7.1629303e-01 1.1528553e+00 1.0777411e+00 9.0142636e-01 8.0046685e-01 7.1629168e-01 1.0032293e+00 9.1892413e-01 3.6452132e-01 5.6370994e-01 7.0176271e-01 1.4157327e+00 4.2362917e-01 7.0633229e-01 6.0964891e-01 1.0062544e+00 9.1554656e-01 6.0365948e-01 1.0235118e+00 6.1135434e-01 6.7626502e-01 7.5508853e-01 9.3308891e-01 7.1621884e-01 8.5403428e-01 6.5712608e-01 8.0245824e-01 6.3192325e-01 9.1132198e-01 8.6051414e-01 1.0242692e+00 1.0232576e+00 6.8685125e-01 1.5761415e+00 1.4407364e+00 9.0296858e-01 8.3689956e-01 6.3322667e-01 1.0451812e+00 1.5490134e+00 4.5847767e-01 1.6529028e+00 8.3916809e-01 1.2749306e+00 5.4219811e-01 7.0462697e-01 1.3611955e+00 1.3103292e+00 9.0792879e-01 9.1446896e-01 8.2421853e-01 7.1708289e-01 9.0683128e-01 1.1954899e+00 1.2957636e+00 6.3178534e-01 9.0508756e-01 8.7796615e-01 1.4198077e+00 7.2113820e-01 6.0427481e-01 1.0032443e+00 1.4501583e+00 4.5216167e-01 5.2491131e-01 9.1894698e-01 1.2738390e+00 9.4912864e-01 1.0207533e+00 1.3523292e+00 5.0043842e-01 4.1317535e-01 8.5403428e-01 7.0910969e-01 3.0482299e-01 4.0243965e-01 1.6491696e+00 1.8289467e+00 1.0060994e+00 6.1119267e-01 9.0142636e-01 1.6484371e+00 5.0126466e-01 6.0018299e-01 9.3308853e-01 4.2362917e-01 4.0363334e-01 5.2133802e-01 7.9153339e-01 1.0782107e+00 1.5275160e+00 5.2167829e-01 5.2167208e-01 6.9987517e-01 1.2633516e+00 5.2201750e-01 4.0127250e-01 5.0517282e-01 4.1212852e-01 5.2167829e-01 4.1210927e-01 7.1621748e-01 8.0093081e-01 6.3178782e-01 3.0922892e-01 7.0008735e-01 2.0061436e-01 3.6452132e-01 6.0035305e-01 4.1420960e-01 7.0096858e-01 6.3178782e-01 5.2132556e-01 3.0490481e-01 1.5634961e+00 1.6740875e+00 5.4219811e-01 5.8750389e-01 8.0245903e-01 1.5263478e+00 4.0004442e-01 6.1135434e-01 8.6051471e-01 5.0043842e-01 4.2270142e-01 3.0482299e-01 8.0967961e-01 1.0426513e+00 1.5757929e+00 3.3813251e-01 4.0127250e-01 5.0855778e-01 1.3133662e+00 7.1700909e-01 4.0125062e-01 5.2491734e-01 5.2167829e-01 5.2838320e-01 5.3943256e-01 6.0017665e-01 6.4620889e-01 6.8261201e-01 4.2270142e-01 3.0482299e-01 3.0026460e-01 7.0470867e-01 5.0477564e-01 1.1038840e+00 1.0010209e+00 4.0363334e-01 3.3808272e-01 1.2528049e+00 1.4182049e+00 9.2033101e-01 2.4195741e-01 1.2036925e+00 1.2362756e+00 6.3451734e-01 3.0482299e-01 5.2524663e-01 7.4335736e-01 7.4329414e-01 4.0125062e-01 5.2491131e-01 6.7636452e-01 1.1755449e+00 4.0127250e-01 6.3925756e-01 7.9148746e-01 9.1424659e-01 5.2491734e-01 4.1210927e-01 8.5437440e-01 1.2085435e-01 3.0026460e-01 4.0127250e-01 1.0010209e+00 4.0243965e-01 4.1317535e-01 3.0482299e-01 6.0444249e-01 3.3813251e-01 6.0964891e-01 9.0166431e-01 4.1212852e-01 7.8985507e-01 8.1719606e-01 2.1378157e+00 2.2009978e+00 5.0855077e-01 1.2175952e+00 3.0017653e-01 2.1167404e+00 6.0035621e-01 1.0597879e+00 1.5265797e+00 5.0517282e-01 5.2167829e-01 7.4329527e-01 1.5072282e+00 1.7227391e+00 2.2434054e+00 7.4335736e-01 6.3309258e-01 6.8161057e-01 2.0107350e+00 9.2867113e-01 7.5508853e-01 5.0517282e-01 1.2040344e+00 1.0178820e+00 1.2037520e+00 2.0181667e-01 1.1636098e+00 1.0621172e+00 1.0032443e+00 6.0000317e-01 8.0883841e-01 9.0668287e-01 5.0085236e-01 6.0964891e-01 7.4618926e-01 2.0118073e+00 2.0884859e+00 9.1424701e-01 1.1060939e+00 4.0243965e-01 2.0057764e+00 6.3178782e-01 9.1916394e-01 1.4198627e+00 6.1119267e-01 6.0219099e-01 6.3309012e-01 1.4134218e+00 1.6179000e+00 2.1265315e+00 6.3178534e-01 9.0506254e-01 1.0032443e+00 1.9078396e+00 6.5712608e-01 6.8261201e-01 6.0219099e-01 1.1003034e+00 9.0532049e-01 1.1001014e+00 5.0000761e-01 1.0433444e+00 9.1892454e-01 9.0002615e-01 5.6595908e-01 7.0470867e-01 6.1119558e-01 6.0017982e-01 5.0085236e-01 1.5275160e+00 1.6747664e+00 1.0434746e+00 5.2132556e-01 8.0533198e-01 1.5264802e+00 5.7609230e-01 4.1317535e-01 8.6051414e-01 5.7630313e-01 5.2524663e-01 4.1315633e-01 8.6054545e-01 1.0440350e+00 1.5412701e+00 4.1210927e-01 8.0250202e-01 9.1471442e-01 1.3130978e+00 3.0490481e-01 5.0043084e-01 5.7630313e-01 5.0043842e-01 3.3818226e-01 5.0043084e-01 6.3925756e-01 6.0948212e-01 4.1317535e-01 3.0482299e-01 7.0548283e-01 3.0490481e-01 2.2573593e-01 5.6394820e-01 1.3634093e+00 1.4858469e+00 8.1757693e-01 5.2201750e-01 9.1427000e-01 1.3523380e+00 6.0201716e-01 3.4342562e-01 7.1629168e-01 7.0096708e-01 6.0948212e-01 3.0490481e-01 7.0096708e-01 9.1424701e-01 1.4267554e+00 4.0127250e-01 4.1420960e-01 4.8342635e-01 1.2049539e+00 6.0964891e-01 1.1269424e-01 7.1621613e-01 4.1212852e-01 6.0018299e-01 5.3914287e-01 7.0548283e-01 5.2524663e-01 7.0105084e-01 5.0476836e-01 5.6371422e-01 3.0474106e-01 5.2491734e-01 6.0948212e-01 1.2000065e+00 2.0187441e+00 1.0498228e+00 2.2315584e+00 1.0000152e+00 1.8808952e+00 1.1286798e+00 7.5826453e-01 1.9821126e+00 1.9334872e+00 1.4094023e+00 9.7944085e-01 1.0090312e+00 3.0915245e-01 1.4094022e+00 1.7226330e+00 1.6785116e+00 8.2418071e-01 1.4544336e+00 1.4183053e+00 2.0448570e+00 1.3189240e+00 1.1989547e+00 1.6071563e+00 2.0162299e+00 9.7599312e-01 1.1287691e+00 1.5299044e+00 1.8304280e+00 1.5694554e+00 1.5966664e+00 1.9334872e+00 2.0448570e+00 1.2223853e+00 2.3135239e+00 3.0915245e-01 2.0415522e+00 1.2703001e+00 9.2351241e-01 2.1487275e+00 2.0854181e+00 1.4650300e+00 1.1157320e+00 8.0296037e-01 1.2013591e+00 1.4650276e+00 1.8684939e+00 1.6818191e+00 8.0245746e-01 1.5324961e+00 1.5271597e+00 2.1953922e+00 1.5071120e+00 1.3457716e+00 1.8029854e+00 2.0885102e+00 1.0837575e+00 1.2703001e+00 1.7133162e+00 1.9522053e+00 1.7369702e+00 1.6941963e+00 2.0286286e+00 1.1213597e+00 6.3912943e-01 1.9163315e+00 5.0855778e-01 1.1310337e+00 1.3142952e+00 6.0219099e-01 8.0046764e-01 7.2904264e-01 1.2366099e+00 1.4559030e+00 2.0467625e+00 7.7882758e-01 6.0184622e-01 6.0948800e-01 1.7296063e+00 1.2395260e+00 9.0668287e-01 8.0051036e-01 1.0231745e+00 1.0411548e+00 1.0543951e+00 5.2167829e-01 1.1356187e+00 1.2079042e+00 9.3439622e-01 4.2268438e-01 8.1719606e-01 1.2192978e+00 8.0046764e-01 1.3133662e+00 1.0434746e+00 8.3916809e-01 2.2573593e-01 5.0855077e-01 9.3848935e-01 9.0659977e-01 5.2167829e-01 7.0096858e-01 5.5450500e-01 1.0287902e+00 5.2133802e-01 8.4725834e-01 9.7599312e-01 8.0250123e-01 6.0018299e-01 5.6371422e-01 1.0171340e+00 3.0482299e-01 2.0181667e-01 6.0000317e-01 1.1079931e+00 2.0061436e-01 2.2573593e-01 5.0084481e-01 8.1683095e-01 5.2524663e-01 7.0096708e-01 1.0116865e+00 2.2278855e+00 7.0008584e-01 1.1298552e+00 1.6300950e+00 6.0017982e-01 5.0084481e-01 8.5403428e-01 1.6097507e+00 1.8284464e+00 2.3350903e+00 8.5406616e-01 7.1629168e-01 7.5508853e-01 2.1138813e+00 8.1683095e-01 8.2462252e-01 4.0246123e-01 1.3009222e+00 1.1139906e+00 1.3000951e+00 2.2608083e-01 1.2636227e+00 1.1315710e+00 1.1002119e+00 7.0088627e-01 9.0029018e-01 6.9600743e-01 3.1328089e-01 1.8661354e+00 1.1286798e+00 7.2044167e-01 1.9755679e+00 1.9314520e+00 1.3741466e+00 9.0645118e-01 6.0184622e-01 1.0001751e+00 1.3741498e+00 1.7083042e+00 1.6308665e+00 6.0201716e-01 1.4559030e+00 1.4140789e+00 2.0433026e+00 1.3131370e+00 1.1900969e+00 1.6049479e+00 2.0057464e+00 9.6691372e-01 1.1304042e+00 1.5237285e+00 1.7843627e+00 1.5639785e+00 1.5975352e+00 1.9314520e+00 8.2671175e-01 1.1543257e+00 1.2085435e-01 3.0474106e-01 7.0088627e-01 1.0144117e+00 1.3018103e+00 1.7709153e+00 7.0462844e-01 3.0482299e-01 7.0470867e-01 1.4857440e+00 8.1457587e-01 6.0948506e-01 3.3813251e-01 6.4049114e-01 7.4954884e-01 6.3925756e-01 5.0043084e-01 1.0090834e+00 8.7372177e-01 5.2838320e-01 2.0121983e-01 3.4342562e-01 7.3084171e-01 4.1315633e-01 5.0855778e-01 9.1024401e-01 8.2498722e-01 5.0436965e-01 5.6595908e-01 7.2044167e-01 1.2101609e+00 5.0437695e-01 6.9987517e-01 8.1457660e-01 1.0010209e+00 4.1212852e-01 3.4342562e-01 9.3351278e-01 3.0915245e-01 3.0482299e-01 6.0052920e-01 9.2747919e-01 2.2608083e-01 4.0000000e-01 5.0476836e-01 8.5586571e-01 5.0477564e-01 5.0477564e-01 8.2498722e-01 1.2635707e+00 1.2396421e+00 8.0533198e-01 2.4170870e-01 4.0127250e-01 7.4618926e-01 8.0726668e-01 1.0151880e+00 1.1066159e+00 5.6371422e-01 9.1554656e-01 8.0879701e-01 1.3523345e+00 6.0366256e-01 6.3912943e-01 9.0532093e-01 1.4186217e+00 5.2133179e-01 7.1700909e-01 8.1719606e-01 1.0923439e+00 8.5409862e-01 1.0116865e+00 1.3253496e+00 2.0121983e-01 8.0051036e-01 1.1269596e+00 1.4140457e+00 1.8721285e+00 8.0250123e-01 3.3813251e-01 8.0250202e-01 1.5969056e+00 8.4536936e-01 7.0096708e-01 2.2538848e-01 7.4395693e-01 8.3222261e-01 7.1779518e-01 4.1212852e-01 1.1079931e+00 9.4151244e-01 5.7630313e-01 3.0490481e-01 4.1420960e-01 6.9509395e-01 3.4080442e-01 7.0184453e-01 1.1527745e+00 1.4140486e+00 1.8971345e+00 7.0556260e-01 3.1328089e-01 7.0910969e-01 1.6486410e+00 7.4618926e-01 6.0184622e-01 1.1269424e-01 8.0923926e-01 7.7598796e-01 8.0883916e-01 3.4085233e-01 1.0235254e+00 8.7240114e-01 6.3309012e-01 5.0043842e-01 4.1315633e-01 5.7609230e-01 2.2538848e-01 8.0888055e-01 1.0030868e+00 1.5299044e+00 1.0000000e-01 6.3164977e-01 7.0096708e-01 1.3008855e+00 6.0184622e-01 3.3813251e-01 8.0296037e-01 5.0476836e-01 3.6256305e-01 5.6618864e-01 6.3178782e-01 4.5847767e-01 5.2491734e-01 4.1420960e-01 6.0202028e-01 4.0127250e-01 6.0052920e-01 5.6618864e-01 3.4342562e-01 8.7372177e-01 8.2425704e-01 9.3308891e-01 1.1005554e+00 7.1700774e-01 9.6674360e-01 8.0051115e-01 1.2632995e+00 5.2491734e-01 8.0883916e-01 7.8935898e-01 1.4043632e+00 7.0470867e-01 9.0532093e-01 7.5502989e-01 9.6953662e-01 7.4612718e-01 1.0222576e+00 1.3061180e+00 1.0032296e+00 1.0032293e+00 1.1900276e+00 1.3017553e+00 4.1315633e-01 1.1093621e+00 1.0088783e+00 1.5263498e+00 7.1708289e-01 7.3155911e-01 1.0040629e+00 1.6175925e+00 6.1845783e-01 7.5826453e-01 9.3426769e-01 1.2396937e+00 1.0142626e+00 1.2128138e+00 1.5237074e+00 1.5299064e+00 1.6885111e+00 1.8315348e+00 8.0097499e-01 1.6050900e+00 1.5160591e+00 2.0074169e+00 1.1389082e+00 1.2275665e+00 1.3502668e+00 2.1298991e+00 1.1075720e+00 1.2113964e+00 1.3632538e+00 1.7639471e+00 1.4922544e+00 1.7133283e+00 2.0290146e+00 7.1621748e-01 8.0051036e-01 1.3008813e+00 6.0017982e-01 4.1210927e-01 8.0492246e-01 5.0477564e-01 3.4080442e-01 5.6595908e-01 6.3309258e-01 4.5784410e-01 5.0855778e-01 4.1317535e-01 6.0366256e-01 4.0246123e-01 6.0035621e-01 5.7630313e-01 5.0085236e-01 1.4407390e+00 9.1892413e-01 4.2270142e-01 3.6452132e-01 6.7824250e-01 9.0668287e-01 8.2458409e-01 5.2133179e-01 9.0792879e-01 1.0124729e+00 8.0250202e-01 4.1210927e-01 5.0085236e-01 8.2458478e-01 4.1315633e-01 1.6100639e+00 1.0426636e+00 5.2491734e-01 8.0488008e-01 8.6054545e-01 1.0116721e+00 9.7291273e-01 5.6595908e-01 9.4532171e-01 1.1186499e+00 9.1681464e-01 6.3178782e-01 6.2656178e-01 9.6574369e-01 5.3943256e-01 1.4007831e+00 1.3033860e+00 1.7572550e+00 8.5406674e-01 1.0030724e+00 1.0426513e+00 1.9078843e+00 9.0005048e-01 1.0010209e+00 1.0776188e+00 1.4549432e+00 1.2363278e+00 1.5032156e+00 1.8103044e+00 6.0184934e-01 8.2671175e-01 6.0383105e-01 4.1209001e-01 6.3309258e-01 7.4418186e-01 5.0477564e-01 4.0006662e-01 4.8342635e-01 9.1892413e-01 4.8391482e-01 2.0121983e-01 6.4620889e-01 7.0462697e-01 5.0436965e-01 6.0184622e-01 5.7608844e-01 6.1830764e-01 5.3914287e-01 7.0105084e-01 5.0855778e-01 6.3165225e-01 3.0490481e-01 5.0477564e-01 5.2133179e-01 9.1446938e-01 8.7209348e-01 9.0532093e-01 3.4085233e-01 1.1298636e+00 9.6150595e-01 7.2036819e-01 5.0477564e-01 5.2167208e-01 6.3925756e-01 3.0008832e-01 3.0915245e-01 3.0474106e-01 1.1006468e+00 5.0043842e-01 4.1420960e-01 2.4195741e-01 6.8170466e-01 4.0127250e-01 7.0096708e-01 1.0003198e+00 5.0043084e-01 9.1132198e-01 3.0026460e-01 2.0121983e-01 4.0004442e-01 6.9987517e-01 4.5148429e-01 5.0477564e-01 8.3183672e-01 1.1010711e+00 8.0000160e-01 6.0052920e-01 2.0121983e-01 6.8161057e-01 4.1212852e-01 7.0176121e-01 1.0030721e+00 1.0458540e+00 9.3984267e-01 9.0166476e-01 5.0043084e-01 7.0088627e-01 7.0993998e-01 3.0017653e-01 2.2608083e-01 7.0008584e-01 9.3848935e-01 7.0184453e-01 6.3178534e-01 9.6936870e-01 5.0476836e-01 8.7372177e-01 5.6618864e-01 5.0477564e-01 8.7240114e-01 5.3943256e-01 3.0474106e-01 5.2167208e-01 8.0879701e-01 5.0085236e-01 9.0279268e-01 5.2133802e-01 4.2362917e-01 6.0017982e-01 5.2838320e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt new file mode 100644 index 0000000000000000000000000000000000000000..3e2759df30c14c1503818cc6a400a370e8fd8e89 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt @@ -0,0 +1 @@ + 1.1781739e+00 8.4573383e-01 1.1040164e+00 2.6033464e-01 1.0391769e+00 6.5951091e-01 2.6643250e-01 1.6215602e+00 9.6424206e-01 5.8926015e-01 4.4417668e-01 1.2158053e+00 1.5196051e+00 1.4342986e+00 2.2147971e+00 1.0267382e+00 1.3103399e-01 1.0246015e+00 7.0646671e-01 4.6190224e-01 5.3352899e-01 6.8496652e-01 6.2944414e-01 5.1453676e-01 1.1649855e+00 3.8639663e-01 1.3340137e-01 2.6033464e-01 8.5141186e-01 9.9757114e-01 5.0629646e-01 1.3963590e+00 1.6851313e+00 9.6424206e-01 7.1143905e-01 4.8636669e-01 9.6424206e-01 1.4309353e+00 2.3749211e-01 1.8699153e-01 2.8644037e+00 1.0938603e+00 5.4968254e-01 7.9227302e-01 1.2158053e+00 7.0111465e-01 9.1831777e-01 5.2374483e-01 4.7680727e-01 3.4225655e+00 2.9886388e+00 3.5231786e+00 3.4844976e+00 3.4140450e+00 2.8802393e+00 3.0292175e+00 2.9585178e+00 3.2500773e+00 2.8104851e+00 3.8076036e+00 2.7718515e+00 3.6661618e+00 3.0567513e+00 2.4313960e+00 3.1540282e+00 2.7718124e+00 2.7494216e+00 4.0917474e+00 3.0136333e+00 3.0855821e+00 2.8833448e+00 3.7756717e+00 3.0462644e+00 3.0262994e+00 3.1582448e+00 3.6064873e+00 3.6179250e+00 3.0140884e+00 2.7108793e+00 3.1480683e+00 3.0769261e+00 2.8006021e+00 3.5140011e+00 2.7293962e+00 2.7724816e+00 3.3142477e+00 3.8377035e+00 2.4725633e+00 3.1307104e+00 3.0248446e+00 2.9240118e+00 2.9852014e+00 3.1515797e+00 2.8921724e+00 2.4678110e+00 2.6524994e+00 2.9083443e+00 2.7444686e+00 2.7478281e+00 4.2652803e+00 3.6712837e+00 4.4571526e+00 3.7518856e+00 4.1563042e+00 5.0327536e+00 3.5110505e+00 4.5914343e+00 4.4347154e+00 4.7605836e+00 3.6465897e+00 3.9644198e+00 4.1403419e+00 3.9458900e+00 4.0035735e+00 3.9244083e+00 3.7394250e+00 5.1213512e+00 5.6085412e+00 4.1515197e+00 4.3260896e+00 3.5311267e+00 5.2010517e+00 3.7194928e+00 4.0104626e+00 4.2547108e+00 3.5326627e+00 3.3344440e+00 4.1152831e+00 4.1647603e+00 4.7306329e+00 5.0503286e+00 4.1958529e+00 3.4649010e+00 3.7290073e+00 5.0848756e+00 4.0161825e+00 3.6208873e+00 3.2588014e+00 4.1126592e+00 4.3082433e+00 4.1887411e+00 3.6712837e+00 4.3324309e+00 4.3552675e+00 4.1561376e+00 4.0674499e+00 3.7933592e+00 3.8117183e+00 3.3250640e+00 5.2374483e-01 4.3319335e-01 1.3890415e+00 2.1841707e+00 9.9973471e-01 9.3211669e-01 6.4636269e-01 2.7124234e-01 1.7245677e+00 9.3727156e-01 1.7819563e-01 7.5570839e-01 2.5520912e+00 3.3809114e+00 2.1782802e+00 1.1854382e+00 2.0937104e+00 1.8662528e+00 1.1155937e+00 1.6542533e+00 1.4482752e+00 8.4881492e-01 9.7259094e-01 1.6562722e-01 9.7322023e-01 1.2100516e+00 9.9111027e-01 5.3286499e-01 2.8394141e-01 1.1346946e+00 2.5666454e+00 2.8608436e+00 2.7124234e-01 4.9009568e-01 1.3630799e+00 2.7124234e-01 6.0647055e-01 9.5529726e-01 1.1682143e+00 1.6911681e+00 7.6195008e-01 1.2774622e+00 1.9003949e+00 1.7819563e-01 1.8642334e+00 5.8652824e-01 1.6860841e+00 7.0235100e-01 3.5517185e+00 3.0793995e+00 3.5669733e+00 2.7166736e+00 3.1838913e+00 2.5120824e+00 3.1938169e+00 2.0428688e+00 3.1039816e+00 2.2561090e+00 2.8016158e+00 2.6226738e+00 2.9050141e+00 2.8502197e+00 2.0976260e+00 3.1846091e+00 2.5890531e+00 2.2584354e+00 3.4434621e+00 2.3329638e+00 3.1272801e+00 2.5616006e+00 3.3203580e+00 2.7436923e+00 2.8484236e+00 3.0948526e+00 3.4151452e+00 3.5708989e+00 2.7939968e+00 2.0736054e+00 2.3834491e+00 2.2886612e+00 2.3204706e+00 3.1632397e+00 2.5205525e+00 3.0112889e+00 3.3433635e+00 3.2300528e+00 2.2657938e+00 2.4705763e+00 2.4462190e+00 2.8038852e+00 2.4332562e+00 2.2089299e+00 2.4060762e+00 2.2734727e+00 2.3627180e+00 2.7012637e+00 1.8976741e+00 2.3590971e+00 4.3837112e+00 3.3195686e+00 4.4453895e+00 3.6018522e+00 4.1012355e+00 5.0512932e+00 2.8774753e+00 4.5344585e+00 4.0827835e+00 5.0801762e+00 3.7291677e+00 3.6888814e+00 4.1064223e+00 3.4625304e+00 3.7552252e+00 3.9939605e+00 3.6781201e+00 5.5433523e+00 5.4381439e+00 3.4976392e+00 4.4223824e+00 3.2288234e+00 5.1217596e+00 3.4157742e+00 4.1643077e+00 4.3726405e+00 3.2842292e+00 3.2296198e+00 3.9190152e+00 4.1591876e+00 4.6244312e+00 5.4884429e+00 4.0035368e+00 3.2202996e+00 3.3301365e+00 5.1089381e+00 4.2054648e+00 3.6234874e+00 3.1421933e+00 4.1502382e+00 4.3306814e+00 4.2256435e+00 3.3195686e+00 4.4219948e+00 4.4973329e+00 4.1152664e+00 3.6487297e+00 3.7329401e+00 4.0033827e+00 3.2017663e+00 2.8394141e-01 9.9272943e-01 1.8549949e+00 4.9772204e-01 5.9738093e-01 7.8305765e-01 3.7622328e-01 1.4342986e+00 5.0621589e-01 4.9772204e-01 6.9001472e-01 2.2742100e+00 3.0330374e+00 1.8410898e+00 8.5582452e-01 1.8552074e+00 1.4758765e+00 9.8932340e-01 1.2824303e+00 9.4580058e-01 7.0175090e-01 5.8564715e-01 6.1067563e-01 6.6453319e-01 9.2528705e-01 7.6195008e-01 1.7002750e-01 3.1093967e-01 1.0044375e+00 2.1686473e+00 2.5011215e+00 3.7622328e-01 3.6669623e-01 1.1883075e+00 3.7622328e-01 5.8652824e-01 6.7745878e-01 7.9191984e-01 2.0937821e+00 3.6228991e-01 9.5582164e-01 1.5272557e+00 4.9772204e-01 1.4755007e+00 1.3340137e-01 1.3666101e+00 4.3319335e-01 3.7283414e+00 3.2257816e+00 3.7651559e+00 3.1082143e+00 3.4606263e+00 2.7705999e+00 3.2962379e+00 2.4179023e+00 3.3643791e+00 2.5175848e+00 3.2317517e+00 2.8135312e+00 3.3502574e+00 3.0859108e+00 2.3316915e+00 3.3832002e+00 2.7540885e+00 2.5906747e+00 3.8459512e+00 2.7110494e+00 3.2296198e+00 2.8510843e+00 3.6612067e+00 3.0231940e+00 3.1083626e+00 3.3221751e+00 3.6999785e+00 3.7824508e+00 3.0223050e+00 2.4549511e+00 2.7813487e+00 2.6993734e+00 2.6424988e+00 3.4348309e+00 2.6680185e+00 3.0548262e+00 3.5357687e+00 3.6340473e+00 2.4474337e+00 2.8211532e+00 2.7662396e+00 3.0069386e+00 2.7817508e+00 2.6121651e+00 2.7000040e+00 2.4677010e+00 2.5915377e+00 2.9544131e+00 2.2712863e+00 2.6277952e+00 4.4682388e+00 3.5629824e+00 4.6484688e+00 3.8140427e+00 4.2790737e+00 5.2629823e+00 3.1332305e+00 4.7710813e+00 4.3977196e+00 5.1429155e+00 3.8634878e+00 3.9555042e+00 4.3021827e+00 3.7450218e+00 3.9451569e+00 4.1141318e+00 3.8729361e+00 5.5923916e+00 5.7171219e+00 3.8836634e+00 4.5660923e+00 3.4290419e+00 5.3764415e+00 3.6907517e+00 4.2782894e+00 4.5393827e+00 3.5302656e+00 3.4102235e+00 4.1476932e+00 4.3814983e+00 4.8831870e+00 5.5467550e+00 4.2276454e+00 3.4820326e+00 3.6311162e+00 5.3207972e+00 4.2656428e+00 3.7854499e+00 3.3178004e+00 4.3254709e+00 4.4873379e+00 4.3956810e+00 3.5629824e+00 4.5607328e+00 4.6030755e+00 4.3016138e+00 3.9622355e+00 3.9225802e+00 4.0577906e+00 3.3684813e+00 1.2515236e+00 2.1021589e+00 7.0646671e-01 8.4383266e-01 5.2374483e-01 3.8525820e-01 1.6876653e+00 7.3502408e-01 3.6319073e-01 5.0299964e-01 2.5372015e+00 3.2897546e+00 2.1021589e+00 1.1117653e+00 2.0978519e+00 1.7286097e+00 1.1937015e+00 1.5323598e+00 1.1874605e+00 8.6297946e-01 7.6710016e-01 5.3827772e-01 8.8540687e-01 1.1730565e+00 1.0034646e+00 2.6643250e-01 2.4808718e-01 1.2168625e+00 2.4209958e+00 2.7605308e+00 3.8525820e-01 5.6164055e-01 1.4300979e+00 3.8525820e-01 3.5266705e-01 9.1831777e-01 1.0556536e+00 1.8570904e+00 3.5266705e-01 1.1671832e+00 1.7581227e+00 3.6319073e-01 1.7245677e+00 2.3749211e-01 1.6215602e+00 6.7030885e-01 3.7702988e+00 3.2513048e+00 3.7854692e+00 2.9445918e+00 3.4252075e+00 2.6854877e+00 3.3289662e+00 2.2084365e+00 3.3482393e+00 2.3872006e+00 3.0088381e+00 2.7858967e+00 3.2051999e+00 3.0423378e+00 2.2727200e+00 3.4066596e+00 2.7026316e+00 2.4942728e+00 3.7194777e+00 2.5718070e+00 3.2266664e+00 2.8009311e+00 3.5699473e+00 2.9607930e+00 3.0876835e+00 3.3257458e+00 3.6752903e+00 3.7792524e+00 2.9772187e+00 2.3405405e+00 2.6225184e+00 2.5379456e+00 2.5530959e+00 3.3522700e+00 2.6036900e+00 3.0973166e+00 3.5528019e+00 3.5210610e+00 2.4001125e+00 2.6797931e+00 2.6323855e+00 2.9822614e+00 2.6747723e+00 2.4035668e+00 2.5939621e+00 2.4241443e+00 2.5291529e+00 2.9226858e+00 2.0959349e+00 2.5480036e+00 4.4738080e+00 3.4750769e+00 4.6459778e+00 3.7712854e+00 4.2573656e+00 5.2660922e+00 2.9665251e+00 4.7582165e+00 4.3221591e+00 5.2027091e+00 3.8786505e+00 3.8957203e+00 4.2952898e+00 3.6300721e+00 3.8796854e+00 4.1217239e+00 3.8539421e+00 5.6722969e+00 5.6818418e+00 3.7421166e+00 4.5832488e+00 3.3486395e+00 5.3611963e+00 3.6296692e+00 4.3021827e+00 4.5620086e+00 3.4793229e+00 3.3827920e+00 4.0990020e+00 4.3836503e+00 4.8653269e+00 5.6359100e+00 4.1798856e+00 3.4290065e+00 3.5331571e+00 5.3326389e+00 4.2899049e+00 3.7762521e+00 3.2871170e+00 4.3357622e+00 4.4879065e+00 4.4101806e+00 3.4750769e+00 4.5719131e+00 4.6252914e+00 4.2958117e+00 3.8764095e+00 3.9087616e+00 4.0828629e+00 3.3281063e+00 8.9980139e-01 6.8064066e-01 4.6472955e-01 1.7695601e+00 1.1682143e+00 5.3827772e-01 5.3286499e-01 1.4108003e+00 1.6357068e+00 1.3406177e+00 2.0471148e+00 8.8540687e-01 2.9145160e-01 9.8663349e-01 4.9772204e-01 6.8921053e-01 3.7371902e-01 5.3360548e-01 8.2263932e-01 5.9279023e-01 1.3884168e+00 5.4248468e-01 3.3872939e-01 5.2066928e-01 9.9757114e-01 1.1836141e+00 7.1971771e-01 1.1867923e+00 1.5097838e+00 1.1682143e+00 9.2945909e-01 6.4884272e-01 1.1682143e+00 1.5630357e+00 4.8016385e-01 2.7124234e-01 3.0617227e+00 1.1744248e+00 5.8374436e-01 6.1345624e-01 1.4108003e+00 4.9009568e-01 1.0413386e+00 4.3319335e-01 6.9189100e-01 3.5573943e+00 3.1141702e+00 3.6648465e+00 3.6881887e+00 3.5883824e+00 3.0468381e+00 3.1315658e+00 3.1515797e+00 3.4214871e+00 2.9743594e+00 4.0164863e+00 2.9182492e+00 3.8928105e+00 3.2158144e+00 2.6006889e+00 3.3027067e+00 2.9031809e+00 2.9465762e+00 4.3027855e+00 3.2186029e+00 3.1845052e+00 3.0688421e+00 3.9670252e+00 3.2223969e+00 3.2005819e+00 3.3183883e+00 3.7835219e+00 3.7624114e+00 3.1706932e+00 2.9238797e+00 3.3563322e+00 3.2896970e+00 2.9943889e+00 3.6782511e+00 2.8525048e+00 2.8501434e+00 3.4560405e+00 4.0524463e+00 2.6189855e+00 3.3240937e+00 3.2080454e+00 3.0726532e+00 3.1844624e+00 3.3537486e+00 3.0707196e+00 2.6200714e+00 2.8136838e+00 3.0798325e+00 2.9434145e+00 2.9219863e+00 4.3385668e+00 3.8211670e+00 4.5879449e+00 3.8900790e+00 4.2758494e+00 5.1630878e+00 3.6606996e+00 4.7359279e+00 4.6113949e+00 4.8204842e+00 3.7540483e+00 4.1248784e+00 4.2705921e+00 4.1081181e+00 4.1285849e+00 4.0208302e+00 3.8718630e+00 5.1706118e+00 5.7653526e+00 4.3529699e+00 4.4302351e+00 3.6643052e+00 5.3499285e+00 3.8863166e+00 4.1025634e+00 4.3705834e+00 3.6895783e+00 3.4655259e+00 4.2576017e+00 4.3078329e+00 4.8848930e+00 5.1059908e+00 4.3355275e+00 3.6287723e+00 3.9015858e+00 5.2167033e+00 4.0809175e+00 3.7394250e+00 3.3885059e+00 4.2346521e+00 4.4182506e+00 4.3085803e+00 3.8211670e+00 4.4331391e+00 4.4402220e+00 4.2825037e+00 4.2456731e+00 3.9239772e+00 3.8761056e+00 3.4480528e+00 1.5196051e+00 1.2824303e+00 2.6220224e+00 1.9839744e+00 5.4248468e-01 1.3880441e+00 2.2398377e+00 2.5185753e+00 6.5993495e-01 1.2140269e+00 2.2670334e-01 1.0140902e+00 4.4901474e-01 4.6310132e-01 1.1825559e+00 5.9738093e-01 1.2799022e+00 1.4364110e+00 1.3915110e+00 2.1479410e+00 1.2515236e+00 9.9544409e-01 1.2188859e+00 1.8419620e+00 2.0002725e+00 1.1587093e+00 6.6217390e-01 7.6869104e-01 1.9839744e+00 1.7287715e+00 9.9282597e-01 1.9839744e+00 2.4262873e+00 1.2419907e+00 1.0737552e+00 3.8557204e+00 2.0456732e+00 1.0753036e+00 4.4417668e-01 2.2089621e+00 5.0629646e-01 1.9071648e+00 5.5576380e-01 1.4985933e+00 3.3087308e+00 2.9428879e+00 3.4716469e+00 4.0891690e+00 3.6027276e+00 3.2367228e+00 2.9085289e+00 3.7111791e+00 3.3928277e+00 3.3150290e+00 4.5928107e+00 2.9594198e+00 4.2678298e+00 3.2621413e+00 2.8156205e+00 3.1507918e+00 2.9937665e+00 3.2188610e+00 4.5717893e+00 3.5888229e+00 3.0697068e+00 3.2000980e+00 4.1198780e+00 3.3377308e+00 3.2155231e+00 3.2352945e+00 3.7547729e+00 3.6294383e+00 3.2310889e+00 3.2831808e+00 3.7736317e+00 3.7263104e+00 3.2475076e+00 3.7907966e+00 2.9840079e+00 2.6164035e+00 3.2920107e+00 4.3046992e+00 2.7582086e+00 3.6782988e+00 3.5276458e+00 3.0726914e+00 3.4670753e+00 3.9103065e+00 3.3340819e+00 2.7470239e+00 2.9746673e+00 3.1328218e+00 3.4555378e+00 3.1318122e+00 4.0752096e+00 3.9330937e+00 4.3762388e+00 3.8407425e+00 4.1274362e+00 4.9032073e+00 4.0261574e+00 4.5547766e+00 4.6534818e+00 4.3582690e+00 3.5326627e+00 4.1405270e+00 4.0947877e+00 4.2953569e+00 4.1533819e+00 3.7981559e+00 3.7518931e+00 4.6218430e+00 5.6203182e+00 4.6338556e+00 4.1503558e+00 3.7655153e+00 5.1552615e+00 3.9363994e+00 3.8053980e+00 4.0787299e+00 3.7177375e+00 3.4172455e+00 4.2121483e+00 4.1116484e+00 4.7277367e+00 4.5452377e+00 4.2828893e+00 3.6617103e+00 4.0381240e+00 4.9437127e+00 3.7768622e+00 3.5869495e+00 3.3594066e+00 4.0056296e+00 4.1979142e+00 4.0739556e+00 3.9330937e+00 4.1628496e+00 4.1341118e+00 4.1117268e+00 4.3552101e+00 3.7951858e+00 3.5859295e+00 3.4280549e+00 5.0370871e-01 1.1854382e+00 8.2574748e-01 1.1968529e+00 2.9724335e-01 9.8896933e-01 1.0391769e+00 2.0112023e+00 2.6653431e+00 1.5111262e+00 6.4636269e-01 1.6262201e+00 1.1040164e+00 9.8966705e-01 9.2934901e-01 5.3040146e-01 7.1789533e-01 3.9472619e-01 1.0556536e+00 5.1318506e-01 7.7368489e-01 7.3633268e-01 5.0731024e-01 7.5303835e-01 9.7659801e-01 1.7897583e+00 2.1453760e+00 8.2574748e-01 6.9001472e-01 1.1202045e+00 8.2574748e-01 9.6424206e-01 6.2046469e-01 5.3827772e-01 2.5404386e+00 5.3988754e-01 6.7372733e-01 1.1459117e+00 9.5361455e-01 1.1160907e+00 4.7951153e-01 1.1016806e+00 5.5109043e-01 3.7667766e+00 3.2399456e+00 3.8211099e+00 3.3920086e+00 3.5974024e+00 2.9126202e+00 3.2661365e+00 2.7296888e+00 3.4884812e+00 2.6863536e+00 3.5939578e+00 2.8820992e+00 3.6783922e+00 3.1916608e+00 2.4616677e+00 3.4465420e+00 2.8051321e+00 2.8088029e+00 4.1173049e+00 2.9788024e+00 3.2021702e+00 3.0140681e+00 3.8639979e+00 3.1756883e+00 3.2362495e+00 3.4136564e+00 3.8424217e+00 3.8484723e+00 3.1221021e+00 2.7251978e+00 3.0739866e+00 3.0068046e+00 2.8468838e+00 3.5726594e+00 2.7099355e+00 2.9743925e+00 3.5889631e+00 3.9062347e+00 2.5235038e+00 3.0623697e+00 2.9777787e+00 3.0820765e+00 3.0110500e+00 2.9445347e+00 2.8809757e+00 2.5543631e+00 2.7073441e+00 3.0792230e+00 2.5679169e+00 2.7817508e+00 4.4017095e+00 3.6741422e+00 4.6939918e+00 3.8825162e+00 4.3049794e+00 5.3127345e+00 3.3002804e+00 4.8514877e+00 4.5630927e+00 5.0475012e+00 3.8518880e+00 4.0758615e+00 4.3442223e+00 3.8984747e+00 3.9980344e+00 4.0855291e+00 3.9215612e+00 5.4851910e+00 5.8312870e+00 4.1416475e+00 4.5535489e+00 3.5028870e+00 5.4694510e+00 3.8234999e+00 4.2411026e+00 4.5531893e+00 3.6365882e+00 3.4540568e+00 4.2272070e+00 4.4531017e+00 4.9839411e+00 5.4520873e+00 4.3016933e+00 3.6054769e+00 3.7985950e+00 5.3693255e+00 4.1776636e+00 3.8035129e+00 3.3594554e+00 4.3469553e+00 4.4886880e+00 4.4112274e+00 3.6741422e+00 4.5435532e+00 4.5534752e+00 4.3346048e+00 4.1329859e+00 3.9643709e+00 3.9674740e+00 3.4024060e+00 1.3630799e+00 7.1446962e-01 8.4383266e-01 2.4808718e-01 9.6424206e-01 1.2783641e+00 1.6962086e+00 2.4702874e+00 1.2824303e+00 2.9691107e-01 1.2631980e+00 9.3957399e-01 4.9617437e-01 7.4965096e-01 7.2553812e-01 4.8492463e-01 3.3125444e-01 9.2426065e-01 2.6812643e-01 3.3395426e-01 2.4808718e-01 5.8926015e-01 7.3502408e-01 5.4956349e-01 1.6376300e+00 1.9421609e+00 7.1446962e-01 4.9160020e-01 6.5622658e-01 7.1446962e-01 1.1785203e+00 1.2076330e-01 2.8845946e-01 2.6135503e+00 8.6638670e-01 5.7543116e-01 9.9282597e-01 9.6424206e-01 9.3211669e-01 6.7030885e-01 7.8100392e-01 2.3749211e-01 3.4362741e+00 2.9772187e+00 3.5154539e+00 3.2993605e+00 3.3443673e+00 2.7564382e+00 3.0285957e+00 2.7337208e+00 3.1980682e+00 2.6433553e+00 3.5789723e+00 2.6973512e+00 3.4963203e+00 2.9759207e+00 2.3127671e+00 3.1412273e+00 2.6774449e+00 2.6095931e+00 3.9436181e+00 2.8415480e+00 3.0475523e+00 2.7865107e+00 3.6589666e+00 2.9471549e+00 2.9637920e+00 3.1238401e+00 3.5511257e+00 3.5866237e+00 2.9292978e+00 2.5500042e+00 2.9620296e+00 2.8874183e+00 2.6658729e+00 3.4048426e+00 2.6224103e+00 2.7775195e+00 3.2991484e+00 3.6986035e+00 2.3717154e+00 2.9594198e+00 2.8613259e+00 2.8592001e+00 2.8393888e+00 2.9284199e+00 2.7478281e+00 2.3715604e+00 2.5423572e+00 2.8329679e+00 2.5370255e+00 2.6226761e+00 4.2550363e+00 3.5587552e+00 4.4384175e+00 3.6863990e+00 4.1157774e+00 5.0262130e+00 3.3282378e+00 4.5651798e+00 4.3425674e+00 4.8115590e+00 3.6359466e+00 3.8813909e+00 4.1126592e+00 3.8106374e+00 3.9142555e+00 3.9091502e+00 3.6969354e+00 5.1996380e+00 5.5654572e+00 3.9942942e+00 4.3261607e+00 3.4228883e+00 5.1764012e+00 3.6303894e+00 4.0165247e+00 4.2627936e+00 3.4508614e+00 3.2748167e+00 4.0461381e+00 4.1489951e+00 4.6983104e+00 5.1372583e+00 4.1280577e+00 3.3829235e+00 3.6112201e+00 5.0844328e+00 4.0217556e+00 3.5877667e+00 3.1942059e+00 4.1021303e+00 4.2899049e+00 4.1807095e+00 3.5587552e+00 4.3276502e+00 4.3608503e+00 4.1273623e+00 3.9585431e+00 3.7540483e+00 3.8154519e+00 3.2543470e+00 7.7313507e-01 2.2058495e+00 1.2553676e+00 5.5109043e-01 3.3742167e-01 3.0507869e+00 3.8084614e+00 2.6171176e+00 1.6268459e+00 2.6113513e+00 2.2457527e+00 1.6784057e+00 2.0471148e+00 1.6480463e+00 1.3225313e+00 1.2819528e+00 7.6880092e-01 1.3915110e+00 1.6886167e+00 1.5043671e+00 7.8918675e-01 6.7745878e-01 1.6911617e+00 2.9348176e+00 3.2792996e+00 7.7313507e-01 1.0082548e+00 1.9190366e+00 7.7313507e-01 2.3749211e-01 1.4309353e+00 1.5685186e+00 1.3963590e+00 6.9420840e-01 1.6514950e+00 2.2742046e+00 5.5109043e-01 2.2440749e+00 7.3283576e-01 2.1421205e+00 1.1730565e+00 4.0382971e+00 3.5072516e+00 4.0204750e+00 2.8157524e+00 3.5602797e+00 2.7716933e+00 3.6026548e+00 1.9881684e+00 3.5249607e+00 2.3719578e+00 2.7108793e+00 2.9588139e+00 3.1000099e+00 3.1914275e+00 2.3942229e+00 3.6456797e+00 2.8533918e+00 2.5518058e+00 3.6496659e+00 2.5198146e+00 3.4451131e+00 2.9183683e+00 3.5989425e+00 3.0794356e+00 3.2576813e+00 3.5320163e+00 3.8261152e+00 3.9741896e+00 3.1180182e+00 2.3364082e+00 2.5170134e+00 2.4274466e+00 2.6068690e+00 3.4218333e+00 2.7386417e+00 3.3934324e+00 3.7851452e+00 3.4854109e+00 2.5636830e+00 2.6200486e+00 2.6174942e+00 3.1669559e+00 2.6880360e+00 2.1675629e+00 2.6284424e+00 2.5986852e+00 2.6571682e+00 3.0828753e+00 1.9438939e+00 2.6338308e+00 4.6899445e+00 3.5257224e+00 4.8360835e+00 3.9149039e+00 4.4239486e+00 5.4654338e+00 2.8575769e+00 4.9368840e+00 4.3795070e+00 5.4971381e+00 4.1073887e+00 3.9867349e+00 4.4778796e+00 3.6113294e+00 3.9521234e+00 4.3324165e+00 4.0348180e+00 6.0067497e+00 5.8007868e+00 3.6612067e+00 4.8067419e+00 3.4133837e+00 5.5245725e+00 3.7158962e+00 4.5501061e+00 4.8067452e+00 3.5898573e+00 3.5494058e+00 4.2132256e+00 4.5903044e+00 5.0235775e+00 5.9805492e+00 4.2919572e+00 3.5520531e+00 3.5821954e+00 5.5319518e+00 4.5355231e+00 3.9801247e+00 3.4489678e+00 4.5459210e+00 4.6801759e+00 4.6148655e+00 3.5257224e+00 4.7911899e+00 4.8567489e+00 4.4697071e+00 3.9039516e+00 4.0848538e+00 4.3320055e+00 3.4824517e+00 1.5154593e+00 7.1671402e-01 2.6643250e-01 7.9347379e-01 2.3528246e+00 3.1744385e+00 1.9839744e+00 9.9059199e-01 1.9029496e+00 1.6532822e+00 9.3451915e-01 1.4586696e+00 1.2483935e+00 7.4743804e-01 7.4957404e-01 2.9691107e-01 8.0686941e-01 9.9973471e-01 7.9394533e-01 3.6319073e-01 1.8699153e-01 9.9891776e-01 2.3345854e+00 2.6422396e+00 0.0000000e+00 3.3742167e-01 1.1857824e+00 0.0000000e+00 6.6918102e-01 7.4445830e-01 9.7322023e-01 1.9284841e+00 6.6918102e-01 1.1393372e+00 1.6942803e+00 3.7371902e-01 1.6386105e+00 4.5257749e-01 1.4715172e+00 4.9772204e-01 3.5602797e+00 3.0968979e+00 3.5933352e+00 2.8998722e+00 3.2656298e+00 2.6029746e+00 3.1974447e+00 2.2445103e+00 3.1601923e+00 2.3946216e+00 3.0209665e+00 2.6867317e+00 3.0775658e+00 2.9161244e+00 2.1946278e+00 3.2137635e+00 2.6502891e+00 2.3652711e+00 3.6096140e+00 2.4893065e+00 3.1578003e+00 2.6568473e+00 3.4426472e+00 2.8187901e+00 2.9128857e+00 3.1418202e+00 3.4847097e+00 3.6205958e+00 2.8694312e+00 2.2223283e+00 2.5588203e+00 2.4651138e+00 2.4413293e+00 3.2621861e+00 2.5834128e+00 2.9995857e+00 3.3733791e+00 3.3817876e+00 2.3263008e+00 2.6305758e+00 2.5756071e+00 2.8533918e+00 2.5683063e+00 2.4187322e+00 2.5258216e+00 2.3250308e+00 2.4413618e+00 2.7691536e+00 2.1006933e+00 2.4608850e+00 4.4119896e+00 3.4290419e+00 4.4942656e+00 3.6650933e+00 4.1590662e+00 5.0899438e+00 3.0333619e+00 4.5799469e+00 4.1882413e+00 5.0726081e+00 3.7613721e+00 3.7859992e+00 4.1623715e+00 3.6029758e+00 3.8608065e+00 4.0352348e+00 3.7266849e+00 5.5043247e+00 5.5172732e+00 3.6569442e+00 4.4568115e+00 3.3324016e+00 5.1765224e+00 3.5192065e+00 4.1799642e+00 4.3857400e+00 3.3769078e+00 3.2906843e+00 4.0034548e+00 4.1917183e+00 4.6854597e+00 5.4444866e+00 4.0904298e+00 3.2962678e+00 3.4250783e+00 5.1569386e+00 4.2213314e+00 3.6582637e+00 3.2059261e+00 4.1937040e+00 4.3826532e+00 4.2786320e+00 3.4290419e+00 4.4549850e+00 4.5270304e+00 4.1816268e+00 3.7777251e+00 3.7924144e+00 4.0173731e+00 3.2613828e+00 1.0034646e+00 1.7753099e+00 2.1070188e+00 8.6079202e-01 1.6751898e+00 5.4248468e-01 6.0365341e-01 4.6310132e-01 4.4901474e-01 7.0111465e-01 4.4713936e-01 1.0328871e+00 1.0722301e+00 1.0271920e+00 1.6860841e+00 8.8540687e-01 5.2066928e-01 7.3502408e-01 1.4309353e+00 1.5630357e+00 7.3985997e-01 9.6257499e-01 1.1608422e+00 1.5154593e+00 1.2617482e+00 4.9009568e-01 1.5154593e+00 2.0192952e+00 7.8100392e-01 6.9001472e-01 3.4112480e+00 1.6736143e+00 8.5090098e-01 5.5183182e-01 1.7753099e+00 4.3319335e-01 1.5054343e+00 1.2076330e-01 1.0428797e+00 3.2901237e+00 2.9292978e+00 3.4367371e+00 3.8111737e+00 3.4729880e+00 3.0672734e+00 2.9473505e+00 3.3901879e+00 3.2662947e+00 3.1144880e+00 4.2413542e+00 2.8660589e+00 3.9495965e+00 3.1433256e+00 2.6375433e+00 3.0908568e+00 2.9081458e+00 2.9702968e+00 4.3236374e+00 3.3103937e+00 3.0964304e+00 3.0179755e+00 3.9313682e+00 3.1669001e+00 3.0754584e+00 3.1432906e+00 3.6245464e+00 3.5873526e+00 3.1179878e+00 2.9918256e+00 3.4776059e+00 3.4142800e+00 3.0198617e+00 3.6568155e+00 2.8980988e+00 2.6944324e+00 3.2512254e+00 4.0479095e+00 2.6293791e+00 3.4291613e+00 3.2968205e+00 2.9799791e+00 3.2239661e+00 3.5774656e+00 3.1299499e+00 2.6069579e+00 2.8203994e+00 2.9888842e+00 3.1470877e+00 2.9476507e+00 4.1975965e+00 3.8311128e+00 4.3861256e+00 3.7921747e+00 4.1446583e+00 4.9211801e+00 3.8442384e+00 4.5238497e+00 4.5231439e+00 4.5451210e+00 3.5805575e+00 4.0469570e+00 4.0990882e+00 4.1579560e+00 4.1249171e+00 3.8727781e+00 3.7290616e+00 4.8292468e+00 5.5757877e+00 4.3965263e+00 4.2248397e+00 3.6936498e+00 5.1256160e+00 3.8221804e+00 3.8961870e+00 4.1176453e+00 3.6242665e+00 3.3807801e+00 4.1671042e+00 4.0787299e+00 4.6798442e+00 4.7374542e+00 4.2466909e+00 3.5432140e+00 3.8759165e+00 4.9689016e+00 3.9204411e+00 3.5927937e+00 3.3203721e+00 4.0348754e+00 4.2531600e+00 4.1147391e+00 3.8311128e+00 4.2401451e+00 4.2502727e+00 4.1279956e+00 4.2116129e+00 3.7856899e+00 3.7242025e+00 3.3954918e+00 9.3865015e-01 1.1459117e+00 1.8505741e+00 2.5636327e+00 1.3972701e+00 4.6310132e-01 1.4327294e+00 1.0013399e+00 7.2679299e-01 8.2574748e-01 6.2187934e-01 5.8496636e-01 1.7002750e-01 9.5361455e-01 3.5639126e-01 5.3827772e-01 4.9617437e-01 4.7680727e-01 6.9189100e-01 7.7259801e-01 1.6911681e+00 2.0326426e+00 7.1671402e-01 5.6788283e-01 8.9258315e-01 7.1671402e-01 1.0551281e+00 3.6669623e-01 3.9699460e-01 2.5716465e+00 6.8921053e-01 6.2148529e-01 1.0391769e+00 9.3865015e-01 9.9111027e-01 5.3286499e-01 9.2006504e-01 3.5266705e-01 3.5819899e+00 3.0902007e+00 3.6482741e+00 3.3284223e+00 3.4528558e+00 2.8062636e+00 3.1283731e+00 2.7130802e+00 3.3201500e+00 2.6478976e+00 3.5696084e+00 2.7728704e+00 3.5649049e+00 3.0583917e+00 2.3718219e+00 3.2763163e+00 2.7180029e+00 2.6779045e+00 4.0150919e+00 2.8864805e+00 3.1083977e+00 2.8822332e+00 3.7402558e+00 3.0304088e+00 3.0793541e+00 3.2506893e+00 3.6756049e+00 3.7003058e+00 3.0054875e+00 2.6160903e+00 2.9965118e+00 2.9238797e+00 2.7351276e+00 3.4650507e+00 2.6418165e+00 2.8577585e+00 3.4252075e+00 3.7832878e+00 2.4227175e+00 2.9917855e+00 2.8903467e+00 2.9460322e+00 2.9034030e+00 2.9191699e+00 2.7908173e+00 2.4332562e+00 2.6000033e+00 2.9338362e+00 2.5416562e+00 2.6797931e+00 4.3169600e+00 3.6002350e+00 4.5501061e+00 3.7611228e+00 4.1952298e+00 5.1491201e+00 3.2996439e+00 4.6835622e+00 4.4311275e+00 4.9194005e+00 3.7316761e+00 3.9622355e+00 4.2152785e+00 3.8426553e+00 3.9520057e+00 3.9894324e+00 3.7877346e+00 5.3234168e+00 5.6801405e+00 4.0465336e+00 4.4289683e+00 3.4509848e+00 5.3007226e+00 3.7123045e+00 4.1128937e+00 4.3848871e+00 3.5295907e+00 3.3480190e+00 4.1214168e+00 4.2758432e+00 4.8208082e+00 5.2754050e+00 4.2018689e+00 3.4688327e+00 3.6716137e+00 5.2146461e+00 4.0903576e+00 3.6733277e+00 3.2612647e+00 4.2126998e+00 4.3809966e+00 4.2914999e+00 3.6002350e+00 4.4223824e+00 4.4497684e+00 4.2250047e+00 4.0330034e+00 3.8460049e+00 3.8818416e+00 3.3084835e+00 6.2729876e-01 2.6091054e+00 3.4299177e+00 2.2340939e+00 1.2368073e+00 2.1640372e+00 1.8992968e+00 1.1925354e+00 1.7015647e+00 1.4288989e+00 9.5582164e-01 9.7391954e-01 2.9724335e-01 1.0376697e+00 1.2583645e+00 1.0495503e+00 5.0731024e-01 2.8845946e-01 1.2384679e+00 2.5831347e+00 2.8967543e+00 2.6643250e-01 5.4873947e-01 1.4369223e+00 2.6643250e-01 5.0370871e-01 1.0013399e+00 1.2082987e+00 1.6761482e+00 6.8299624e-01 1.3528452e+00 1.9417181e+00 2.6206799e-01 1.8882412e+00 5.3690447e-01 1.7295385e+00 7.4445830e-01 3.6974389e+00 3.2246529e+00 3.7127916e+00 2.8221999e+00 3.3289662e+00 2.6369282e+00 3.3348648e+00 2.1165503e+00 3.2465431e+00 2.3709411e+00 2.8608899e+00 2.7655496e+00 3.0110500e+00 2.9862341e+00 2.2391292e+00 3.3332541e+00 2.7176350e+00 2.3810734e+00 3.5657791e+00 2.4469788e+00 3.2638546e+00 2.7057900e+00 3.4512743e+00 2.8728052e+00 2.9934131e+00 3.2431147e+00 3.5582624e+00 3.7179545e+00 2.9335017e+00 2.1999209e+00 2.4893065e+00 2.3915367e+00 2.4540260e+00 3.2923304e+00 2.6414379e+00 3.1466196e+00 3.4901671e+00 3.3542627e+00 2.3973915e+00 2.5861640e+00 2.5561973e+00 2.9420425e+00 2.5609364e+00 2.2836399e+00 2.5303888e+00 2.4035745e+00 2.4950488e+00 2.8435005e+00 2.0000785e+00 2.4916202e+00 4.5218181e+00 3.4492861e+00 4.5921002e+00 3.7366932e+00 4.2432727e+00 5.1949299e+00 2.9709788e+00 4.6735988e+00 4.2160797e+00 5.2249989e+00 3.8759828e+00 3.8289542e+00 4.2545385e+00 3.5878027e+00 3.8924868e+00 4.1403048e+00 3.8179103e+00 5.6801405e+00 5.5805905e+00 3.6100547e+00 4.5709635e+00 3.3584734e+00 5.2629823e+00 3.5576748e+00 4.3070506e+00 4.5135384e+00 3.4273212e+00 3.3707040e+00 4.0596064e+00 4.2990937e+00 4.7676077e+00 5.6256469e+00 4.1454035e+00 3.3551224e+00 3.4472672e+00 5.2603070e+00 4.3452859e+00 3.7614312e+00 3.2825924e+00 4.3002370e+00 4.4796258e+00 4.3809022e+00 3.4492861e+00 4.5673965e+00 4.6446301e+00 4.2677070e+00 3.7864370e+00 3.8796124e+00 4.1423594e+00 3.3352922e+00 2.9361142e+00 3.6728262e+00 2.4980859e+00 1.5364600e+00 2.5390784e+00 2.1113072e+00 1.6578570e+00 1.9353585e+00 1.4375287e+00 1.3425463e+00 1.1993279e+00 9.0115406e-01 1.3418210e+00 1.6061161e+00 1.4416694e+00 7.3727571e-01 7.1781501e-01 1.6797637e+00 2.7692440e+00 3.1313820e+00 7.9347379e-01 9.7352372e-01 1.8600648e+00 7.9347379e-01 2.1119253e-01 1.3612390e+00 1.4580439e+00 1.6571634e+00 5.0731024e-01 1.5980974e+00 2.1674065e+00 6.7984069e-01 2.1059482e+00 6.2457556e-01 2.0330443e+00 1.1132823e+00 4.2319020e+00 3.7044235e+00 4.2326669e+00 3.1432906e+00 3.8172627e+00 3.0425144e+00 3.7866079e+00 2.3206274e+00 3.7650177e+00 2.6608343e+00 3.0454230e+00 3.1914925e+00 3.4221447e+00 3.4413652e+00 2.6453561e+00 3.8539838e+00 3.0892079e+00 2.8357998e+00 3.9683086e+00 2.8336784e+00 3.6477040e+00 3.1799040e+00 3.8944724e+00 3.3434129e+00 3.4994776e+00 3.7569353e+00 4.0775935e+00 4.2049294e+00 3.3684490e+00 2.6363662e+00 2.8414019e+00 2.7526519e+00 2.8906655e+00 3.7008234e+00 2.9737492e+00 3.5555916e+00 3.9977110e+00 3.7960948e+00 2.7978670e+00 2.9332077e+00 2.9200513e+00 3.4002561e+00 2.9851921e+00 2.5032729e+00 2.9159414e+00 2.8324645e+00 2.9104902e+00 3.3286096e+00 2.2670901e+00 2.9042354e+00 4.8902416e+00 3.8029663e+00 5.0697571e+00 4.1657422e+00 4.6611282e+00 5.6979337e+00 3.1565039e+00 5.1794156e+00 4.6677357e+00 5.6656906e+00 4.3138249e+00 4.2590423e+00 4.7118516e+00 3.9079657e+00 4.2090892e+00 4.5409993e+00 4.2707579e+00 6.1569683e+00 6.0684263e+00 3.9837013e+00 5.0178221e+00 3.6761530e+00 5.7743610e+00 3.9890690e+00 4.7480354e+00 5.0151961e+00 3.8518880e+00 3.7849163e+00 4.4740109e+00 4.8191103e+00 5.2745801e+00 6.1258486e+00 4.5520039e+00 3.8145791e+00 3.8707244e+00 5.7618968e+00 4.7193263e+00 4.2030300e+00 3.6843246e+00 4.7664506e+00 4.9031551e+00 4.8333734e+00 3.8029663e+00 5.0038635e+00 5.0562579e+00 4.7021393e+00 4.1966653e+00 4.3193180e+00 4.5127918e+00 3.7195417e+00 9.8143688e-01 5.9868400e-01 1.4402716e+00 5.6992880e-01 9.8663349e-01 1.4928150e+00 1.1361809e+00 1.7216149e+00 1.8856736e+00 1.8789959e+00 2.5107352e+00 1.7228721e+00 1.3724737e+00 1.5661153e+00 2.2847787e+00 2.4120925e+00 1.4985933e+00 7.9011741e-01 5.9738093e-01 2.3528246e+00 2.0826771e+00 1.2100516e+00 2.3528246e+00 2.8601865e+00 1.6304499e+00 1.5111262e+00 4.2257604e+00 2.5031609e+00 1.6091095e+00 1.0739839e+00 2.6091054e+00 9.8932340e-01 2.3488496e+00 9.3392552e-01 1.8848176e+00 3.4513181e+00 3.2138675e+00 3.6568023e+00 4.4832075e+00 3.8715598e+00 3.6399979e+00 3.2048569e+00 4.1609431e+00 3.6276988e+00 3.7852753e+00 5.0007603e+00 3.3356061e+00 4.5726588e+00 3.6020324e+00 3.2283316e+00 3.3543125e+00 3.4317803e+00 3.5762357e+00 4.8853618e+00 3.9697083e+00 3.4608105e+00 3.5194529e+00 4.4307529e+00 3.6664068e+00 3.4821665e+00 3.4661369e+00 3.9690303e+00 3.8732280e+00 3.5908374e+00 3.6384054e+00 4.1605480e+00 4.1054173e+00 3.6121763e+00 4.1591449e+00 3.4571840e+00 2.9726287e+00 3.5108834e+00 4.5938444e+00 3.1869025e+00 4.0859475e+00 3.9449709e+00 3.4111584e+00 3.8289196e+00 4.3382952e+00 3.7437946e+00 3.1530215e+00 3.3792174e+00 3.4400302e+00 3.8876642e+00 3.5288768e+00 4.4107365e+00 4.3401559e+00 4.5910423e+00 4.1731099e+00 4.4383007e+00 5.0605479e+00 4.5288382e+00 4.7400085e+00 4.9337127e+00 4.5282137e+00 3.8167418e+00 4.4582410e+00 4.3491396e+00 4.7099690e+00 4.5667632e+00 4.1110524e+00 4.0457882e+00 4.6970439e+00 5.8050200e+00 4.9831785e+00 4.3869525e+00 4.2045486e+00 5.3107398e+00 4.2598936e+00 4.0608563e+00 4.2495756e+00 4.0560071e+00 3.7740189e+00 4.5388860e+00 4.2824837e+00 4.9058468e+00 4.5708763e+00 4.6120616e+00 3.9763551e+00 4.3872260e+00 5.0860672e+00 4.0998055e+00 3.8946369e+00 3.7330702e+00 4.2352833e+00 4.4742220e+00 4.3047259e+00 4.3401559e+00 4.4192906e+00 4.4017154e+00 4.3831142e+00 4.6832544e+00 4.0909816e+00 3.9225445e+00 3.8229302e+00 1.2140269e+00 2.2031378e+00 1.3945864e+00 1.5674943e+00 2.3519816e+00 1.7695601e+00 2.3060361e+00 2.6440626e+00 2.5730128e+00 3.3484034e+00 2.4570006e+00 2.1775427e+00 2.3990667e+00 3.0314484e+00 3.2003667e+00 2.3345854e+00 9.9891776e-01 5.8565201e-01 3.1744385e+00 2.9106021e+00 2.1090950e+00 3.1744385e+00 3.6015962e+00 2.4316107e+00 2.2478972e+00 5.0583620e+00 3.1946200e+00 2.2571919e+00 1.5783735e+00 3.4098353e+00 1.5848534e+00 3.0815481e+00 1.7053877e+00 2.6874763e+00 3.8897688e+00 3.6527400e+00 4.1085323e+00 5.1878354e+00 4.4401043e+00 4.2306533e+00 3.5668974e+00 4.8855250e+00 4.1984227e+00 4.3936084e+00 5.7667342e+00 3.8604223e+00 5.3386416e+00 4.1481805e+00 3.8457422e+00 3.8556395e+00 3.9253518e+00 4.2633467e+00 5.5746944e+00 4.6805795e+00 3.8185179e+00 4.1531225e+00 5.0514886e+00 4.2706189e+00 4.0732692e+00 4.0031242e+00 4.5383244e+00 4.3266944e+00 4.1312924e+00 4.3745457e+00 4.8862079e+00 4.8484299e+00 4.2820168e+00 4.7051757e+00 3.9401848e+00 3.2884177e+00 3.9767257e+00 5.2985037e+00 3.7419338e+00 4.7600849e+00 4.5926355e+00 3.9322406e+00 4.5116211e+00 5.0823619e+00 4.3725296e+00 3.7236864e+00 3.9623539e+00 4.0300759e+00 4.6141946e+00 4.1447443e+00 4.5866744e+00 4.8386745e+00 4.9461657e+00 4.6106150e+00 4.7813164e+00 5.3858108e+00 5.0919277e+00 5.1446449e+00 5.4739987e+00 4.5885042e+00 4.1414025e+00 4.9586480e+00 4.7213864e+00 5.2471037e+00 4.9661074e+00 4.3830009e+00 4.4568388e+00 4.6901031e+00 6.2154767e+00 5.6469307e+00 4.6501660e+00 4.6626232e+00 5.7036296e+00 4.7932843e+00 4.3038060e+00 4.5618709e+00 4.5655950e+00 4.2115551e+00 4.9692114e+00 4.7030193e+00 5.3377504e+00 4.5914343e+00 5.0293150e+00 4.5146706e+00 4.9581881e+00 5.4087027e+00 4.2557771e+00 4.2671438e+00 4.1737149e+00 4.5756985e+00 4.7660397e+00 4.6314702e+00 4.8386745e+00 4.6734470e+00 4.5970177e+00 4.7412505e+00 5.2464126e+00 4.4890534e+00 4.0948317e+00 4.2440420e+00 1.0013399e+00 5.0299964e-01 4.6310132e-01 1.2040900e+00 5.9738093e-01 1.2286837e+00 1.4541908e+00 1.4279677e+00 2.1539145e+00 1.2617482e+00 9.9544409e-01 1.2082987e+00 1.8489243e+00 2.0066856e+00 1.1587093e+00 6.6217390e-01 7.5179033e-01 1.9839744e+00 1.7063292e+00 9.6659661e-01 1.9839744e+00 2.4156729e+00 1.2419907e+00 1.0495503e+00 3.8490499e+00 2.0330726e+00 1.0871867e+00 5.4779717e-01 2.2031378e+00 5.3106808e-01 1.9004159e+00 5.5576380e-01 1.4899949e+00 3.4307448e+00 3.0710756e+00 3.5952799e+00 4.1669813e+00 3.7116384e+00 3.3536981e+00 3.0466130e+00 3.7729829e+00 3.5082606e+00 3.4067800e+00 4.6484249e+00 3.0744089e+00 4.3424419e+00 3.3858347e+00 2.9098729e+00 3.2669110e+00 3.1198644e+00 3.3210229e+00 4.6553382e+00 3.6737423e+00 3.2048569e+00 3.2989479e+00 4.2245828e+00 3.4587219e+00 3.3255241e+00 3.3484846e+00 3.8660480e+00 3.7512964e+00 3.3482610e+00 3.3605387e+00 3.8511468e+00 3.8014113e+00 3.3411134e+00 3.9109127e+00 3.1105014e+00 2.7597977e+00 3.4146222e+00 4.3904048e+00 2.8767763e+00 3.7646132e+00 3.6317356e+00 3.1996946e+00 3.5585167e+00 3.9690108e+00 3.4365573e+00 2.8705339e+00 3.0890888e+00 3.2456270e+00 3.5108688e+00 3.2367228e+00 4.2147014e+00 4.0489906e+00 4.5035700e+00 3.9755362e+00 4.2591912e+00 5.0350768e+00 4.1207838e+00 4.6882252e+00 4.7707308e+00 4.4918348e+00 3.6612573e+00 4.2568131e+00 4.2184327e+00 4.3988058e+00 4.2632946e+00 3.9245996e+00 3.8864624e+00 4.7642090e+00 5.7424408e+00 4.7299069e+00 4.2784034e+00 3.8797952e+00 5.2832732e+00 4.0458553e+00 3.9446593e+00 4.2181053e+00 3.8300888e+00 3.5427774e+00 4.3354099e+00 4.2438935e+00 4.8511407e+00 4.6817036e+00 4.4041714e+00 3.7859242e+00 4.1665370e+00 5.0618540e+00 3.9138566e+00 3.7274783e+00 3.4833346e+00 4.1288327e+00 4.3215818e+00 4.1859543e+00 4.0489906e+00 4.2965095e+00 4.2626474e+00 4.2257654e+00 4.4572702e+00 3.9184477e+00 3.7230473e+00 3.5604297e+00 1.0161882e+00 6.9420840e-01 4.8012872e-01 4.8284931e-01 6.9738730e-01 5.5709100e-01 5.3095950e-01 1.1723315e+00 3.1271814e-01 1.8699153e-01 2.9145160e-01 8.6143605e-01 1.0061402e+00 4.5257749e-01 1.4146831e+00 1.6902182e+00 9.9059199e-01 7.2340544e-01 5.0370871e-01 9.9059199e-01 1.4369223e+00 2.7124234e-01 1.3340137e-01 2.8614050e+00 1.1016806e+00 4.2656951e-01 7.5906970e-01 1.2087236e+00 7.1325427e-01 9.2761923e-01 5.3988754e-01 4.9448466e-01 3.3643791e+00 2.9159414e+00 3.4617249e+00 3.4323687e+00 3.3505903e+00 2.8169505e+00 2.9517065e+00 2.9146662e+00 3.1941250e+00 2.7393282e+00 3.7736317e+00 2.6933089e+00 3.6308668e+00 2.9914579e+00 2.3560812e+00 3.0907905e+00 2.6932687e+00 2.7021788e+00 4.0389540e+00 2.9648098e+00 2.9980910e+00 2.8201257e+00 3.7183934e+00 2.9922398e+00 2.9661287e+00 3.0950932e+00 3.5513156e+00 3.5484439e+00 2.9420200e+00 2.6629529e+00 3.1013619e+00 3.0347859e+00 2.7417410e+00 3.4474072e+00 2.6495954e+00 2.6875764e+00 3.2488445e+00 3.7904353e+00 2.3985415e+00 3.0725852e+00 2.9704304e+00 2.8556850e+00 2.9300511e+00 3.1104511e+00 2.8291506e+00 2.4008046e+00 2.5836379e+00 2.8456807e+00 2.6907656e+00 2.6814159e+00 4.1737238e+00 3.5932878e+00 4.3853076e+00 3.6802688e+00 4.0749525e+00 4.9692376e+00 3.4394110e+00 4.5331007e+00 4.3742923e+00 4.6787296e+00 3.5632387e+00 3.8923023e+00 4.0628985e+00 3.8689922e+00 3.9102807e+00 3.8336686e+00 3.6675649e+00 5.0555526e+00 5.5454277e+00 4.0994961e+00 4.2439469e+00 3.4449831e+00 5.1429556e+00 3.6472400e+00 3.9304610e+00 4.1916938e+00 3.4565067e+00 3.2536517e+00 4.0373591e+00 4.1087274e+00 4.6703619e+00 4.9904781e+00 4.1152831e+00 3.4023949e+00 3.6756751e+00 5.0151763e+00 3.9231895e+00 3.5466262e+00 3.1760855e+00 4.0346845e+00 4.2216886e+00 4.1038501e+00 3.5932878e+00 4.2504107e+00 4.2656428e+00 4.0705667e+00 3.9971917e+00 3.7133040e+00 3.7182295e+00 3.2440381e+00 7.3339246e-01 9.9973471e-01 7.7988766e-01 1.4669572e+00 1.3868865e+00 1.4360884e+00 2.0344949e+00 1.2593779e+00 9.3451915e-01 1.1232628e+00 1.8421759e+00 1.9514085e+00 1.0061402e+00 9.6168382e-01 9.7747632e-01 1.9029496e+00 1.6513423e+00 7.7821113e-01 1.9029496e+00 2.4366790e+00 1.1857824e+00 1.1156669e+00 3.7575639e+00 2.1090460e+00 1.1623508e+00 7.4500632e-01 2.1481102e+00 7.3851064e-01 1.9301732e+00 5.6262711e-01 1.4458364e+00 3.0574507e+00 2.7604800e+00 3.2354442e+00 3.9296796e+00 3.3802783e+00 3.0910114e+00 2.7653977e+00 3.6086433e+00 3.1477156e+00 3.2299948e+00 4.4531271e+00 2.8182580e+00 4.0359059e+00 3.0838698e+00 2.6832031e+00 2.9127171e+00 2.8999239e+00 3.0235972e+00 4.3556993e+00 3.4142800e+00 2.9871882e+00 2.9947610e+00 3.9084388e+00 3.1359326e+00 2.9852014e+00 3.0007807e+00 3.4997296e+00 3.4243092e+00 3.0709062e+00 3.0889274e+00 3.6054231e+00 3.5510321e+00 3.0652992e+00 3.6307363e+00 2.9199707e+00 2.5302845e+00 3.0705222e+00 4.0683526e+00 2.6430958e+00 3.5303998e+00 3.3838093e+00 2.9011205e+00 3.2808511e+00 3.7876206e+00 3.1898591e+00 2.6081677e+00 2.8342553e+00 2.9259899e+00 3.3400468e+00 2.9809771e+00 4.0130133e+00 3.8156727e+00 4.1823301e+00 3.6854232e+00 3.9919300e+00 4.6844795e+00 3.9756960e+00 4.3245814e+00 4.4396397e+00 4.2453585e+00 3.3946374e+00 3.9634682e+00 3.9204865e+00 4.1772364e+00 4.0766155e+00 3.6959934e+00 3.5831715e+00 4.4790872e+00 5.3894840e+00 4.4405497e+00 4.0027890e+00 3.6857786e+00 4.9137586e+00 3.7567964e+00 3.6729588e+00 3.8728151e+00 3.5543966e+00 3.2848126e+00 4.0598486e+00 3.8712880e+00 4.4886485e+00 4.3722180e+00 4.1373490e+00 3.4683949e+00 3.8543469e+00 4.7248681e+00 3.7193644e+00 3.4383872e+00 3.2381387e+00 3.8297356e+00 4.0647650e+00 3.9099360e+00 3.8156727e+00 4.0266221e+00 4.0296168e+00 3.9579549e+00 4.1722551e+00 3.6379295e+00 3.5328511e+00 3.3224979e+00 1.0061402e+00 2.6525508e-01 8.2148003e-01 1.1879760e+00 1.0251165e+00 1.8544941e+00 9.4128180e-01 7.1446962e-01 9.4128180e-01 1.4726083e+00 1.6607117e+00 9.9973471e-01 7.4965096e-01 1.0510795e+00 1.6532822e+00 1.4055304e+00 8.6143605e-01 1.6532822e+00 2.0368618e+00 9.3178083e-01 7.1143905e-01 3.5363390e+00 1.6307900e+00 8.0686941e-01 2.6184788e-01 1.8811296e+00 1.4276574e-01 1.5165187e+00 3.5874135e-01 1.1682143e+00 3.5420892e+00 3.1213639e+00 3.6765721e+00 3.9907084e+00 3.7063187e+00 3.2329517e+00 3.1017380e+00 3.5164906e+00 3.5204595e+00 3.2215483e+00 4.4016409e+00 3.0251724e+00 4.2008255e+00 3.3367044e+00 2.7940225e+00 3.3344791e+00 3.0219495e+00 3.1880051e+00 4.5546425e+00 3.5075399e+00 3.1952269e+00 3.2406785e+00 4.1563139e+00 3.3848806e+00 3.3178898e+00 3.3859281e+00 3.8870730e+00 3.7997125e+00 3.2944053e+00 3.2110142e+00 3.6683444e+00 3.6131226e+00 3.2236002e+00 3.8317071e+00 2.9830921e+00 2.7973167e+00 3.4787185e+00 4.2995699e+00 2.7671612e+00 3.5982071e+00 3.4619260e+00 3.1665431e+00 3.4310890e+00 3.7235030e+00 3.2953414e+00 2.7679629e+00 2.9819595e+00 3.2106653e+00 3.2879746e+00 3.1196883e+00 4.2713770e+00 3.9634682e+00 4.5846990e+00 3.9587038e+00 4.2895460e+00 5.1416821e+00 3.9119997e+00 4.7572039e+00 4.7460626e+00 4.6638344e+00 3.7280454e+00 4.2349181e+00 4.2803486e+00 4.2908242e+00 4.2152718e+00 3.9857018e+00 3.9070797e+00 4.9741957e+00 5.8097018e+00 4.6049285e+00 4.3788345e+00 3.7893243e+00 5.3689314e+00 4.0140467e+00 4.0363955e+00 4.3259832e+00 3.8006490e+00 3.5269016e+00 4.3297076e+00 4.3216441e+00 4.9220177e+00 4.9100057e+00 4.4024600e+00 3.7489348e+00 4.0736926e+00 5.1891895e+00 3.9903212e+00 3.7514870e+00 3.4564041e+00 4.2166575e+00 4.3944655e+00 4.2851348e+00 3.9634682e+00 4.3836343e+00 4.3634474e+00 4.2898748e+00 4.4067690e+00 3.9524852e+00 3.7906894e+00 3.5162082e+00 8.3156200e-01 1.1417173e+00 5.8221430e-01 7.3339246e-01 1.0428797e+00 5.5247822e-01 3.5266705e-01 2.9537172e-01 9.6466498e-01 1.0034646e+00 2.8553149e-01 1.6415483e+00 1.8567917e+00 9.3451915e-01 7.2553812e-01 3.4520795e-01 9.3451915e-01 1.5364952e+00 3.7960845e-01 5.9589853e-01 2.7723424e+00 1.3124532e+00 7.5130648e-01 1.0314203e+00 1.1925354e+00 9.9272943e-01 1.0839891e+00 7.1143905e-01 5.6164055e-01 3.0511653e+00 2.6629268e+00 3.1545235e+00 3.1980310e+00 3.0467396e+00 2.5772061e+00 2.7369167e+00 2.7576827e+00 2.8651003e+00 2.5868532e+00 3.5774656e+00 2.4748633e+00 3.3139897e+00 2.7217211e+00 2.1506369e+00 2.7852281e+00 2.5158340e+00 2.4059801e+00 3.7433691e+00 2.7041074e+00 2.8389661e+00 2.5310559e+00 3.4176981e+00 2.6902386e+00 2.6527549e+00 2.7866168e+00 3.2144386e+00 3.2675625e+00 2.6971865e+00 2.3822357e+00 2.8532332e+00 2.7780124e+00 2.4721123e+00 3.1952919e+00 2.5042136e+00 2.5315299e+00 2.9556760e+00 3.4693709e+00 2.1993495e+00 2.8460127e+00 2.7344861e+00 2.5960616e+00 2.6558880e+00 2.9309653e+00 2.5980406e+00 2.1695355e+00 2.3550298e+00 2.5518802e+00 2.5245372e+00 2.4441489e+00 4.0319503e+00 3.3933783e+00 4.1146478e+00 3.4339804e+00 3.8578841e+00 4.6712206e+00 3.3248410e+00 4.2174528e+00 4.0704050e+00 4.4988341e+00 3.3546526e+00 3.6317702e+00 3.8139412e+00 3.6743372e+00 3.7645284e+00 3.6614225e+00 3.4131399e+00 4.8439861e+00 5.2322637e+00 3.8189229e+00 4.0256031e+00 3.2902097e+00 4.8190347e+00 3.3870859e+00 3.7223145e+00 3.9080259e+00 3.2141230e+00 3.0414452e+00 3.8022687e+00 3.7869665e+00 4.3507688e+00 4.7565318e+00 3.8893282e+00 3.1162635e+00 3.3877625e+00 4.7282676e+00 3.7917280e+00 3.3122397e+00 2.9763122e+00 3.7889092e+00 4.0173731e+00 3.8788191e+00 3.3933783e+00 4.0384828e+00 4.0914753e+00 3.8500022e+00 3.7349483e+00 3.4804621e+00 3.5920363e+00 3.0535851e+00 7.5284003e-01 9.3865015e-01 8.5442446e-01 1.6409761e+00 7.0463400e-01 5.4408162e-01 7.5179033e-01 1.2786676e+00 1.4553344e+00 7.8100392e-01 1.0100290e+00 1.2786676e+00 1.4586696e+00 1.2008045e+00 7.2638147e-01 1.4586696e+00 1.8445759e+00 7.3985997e-01 5.0731024e-01 3.3136601e+00 1.4580439e+00 5.4702555e-01 3.2339566e-01 1.6607117e+00 3.5366952e-01 1.3290015e+00 3.5639126e-01 9.6825676e-01 3.4059851e+00 2.9602214e+00 3.5257340e+00 3.7492673e+00 3.5115913e+00 3.0191277e+00 2.9517483e+00 3.2720571e+00 3.3411331e+00 2.9834222e+00 4.1580731e+00 2.8211532e+00 3.9717534e+00 3.1414634e+00 2.5643903e+00 3.1728076e+00 2.8176969e+00 2.9703822e+00 4.3244606e+00 3.2734582e+00 3.0210021e+00 3.0274003e+00 3.9433841e+00 3.1866772e+00 3.1269680e+00 3.2103093e+00 3.7065013e+00 3.6299273e+00 3.0909484e+00 2.9772513e+00 3.4297321e+00 3.3756936e+00 2.9971174e+00 3.6243253e+00 2.7759818e+00 2.6501700e+00 3.3189004e+00 4.0764633e+00 2.5559925e+00 3.3602243e+00 3.2356862e+00 2.9780147e+00 3.2026716e+00 3.4783251e+00 3.0685582e+00 2.5635668e+00 2.7679629e+00 3.0129566e+00 3.0370166e+00 2.8975180e+00 4.1264564e+00 3.7496421e+00 4.4295218e+00 3.7774558e+00 4.1191095e+00 5.0038078e+00 3.6756489e+00 4.6074750e+00 4.5494422e+00 4.5665604e+00 3.5702411e+00 4.0355009e+00 4.1137066e+00 4.0638414e+00 4.0067360e+00 3.8250619e+00 3.7375780e+00 4.9153359e+00 5.6444336e+00 4.3773916e+00 4.2331397e+00 3.5751580e+00 5.2199809e+00 3.8075775e+00 3.9003626e+00 4.1989415e+00 3.5967191e+00 3.3381510e+00 4.1394208e+00 4.1772604e+00 4.7627066e+00 4.8574416e+00 4.2113835e+00 3.5565415e+00 3.8744064e+00 5.0458107e+00 3.8530980e+00 3.5894641e+00 3.2635788e+00 4.0605147e+00 4.2327162e+00 4.1232607e+00 3.7496421e+00 4.2381044e+00 4.2216886e+00 4.1152818e+00 4.1901775e+00 3.7759339e+00 3.6506667e+00 3.3268510e+00 1.0748172e+00 7.2889003e-01 1.5046031e+00 7.9398919e-01 8.1148630e-01 8.8835337e-01 9.9058911e-01 1.2262672e+00 1.1380274e+00 1.3972288e+00 1.7741287e+00 1.2483935e+00 1.0474897e+00 1.1240042e+00 1.2483935e+00 1.4149548e+00 8.1096210e-01 5.7672351e-01 3.0082939e+00 9.6865373e-01 8.2273123e-01 9.5195566e-01 1.4288989e+00 8.3246212e-01 9.4996842e-01 9.2092295e-01 8.7375509e-01 4.0151215e+00 3.5231786e+00 4.1026785e+00 3.8908802e+00 3.9665585e+00 3.3438394e+00 3.5293285e+00 3.2540384e+00 3.8314935e+00 3.1634347e+00 4.1178317e+00 3.2512254e+00 4.1561446e+00 3.5717750e+00 2.8833448e+00 3.7345570e+00 3.1952822e+00 3.2548783e+00 4.5820687e+00 3.4621656e+00 3.5141919e+00 3.4137993e+00 4.2939653e+00 3.5777026e+00 3.5926398e+00 3.7328374e+00 4.1920808e+00 4.1652091e+00 3.5073274e+00 3.1921998e+00 3.5706840e+00 3.5044583e+00 3.2904241e+00 3.9914615e+00 3.1120432e+00 3.2204607e+00 3.8807669e+00 4.3582892e+00 2.9219284e+00 3.5476488e+00 3.4540639e+00 3.4397115e+00 3.4680001e+00 3.4670753e+00 3.3367044e+00 2.9471549e+00 3.1205392e+00 3.4518638e+00 3.0783517e+00 3.2145380e+00 4.6697624e+00 4.0951447e+00 4.9940386e+00 4.2442248e+00 4.6312366e+00 5.5957028e+00 3.7901714e+00 5.1629764e+00 4.9662629e+00 5.2245876e+00 4.1326097e+00 4.4648550e+00 4.6557857e+00 4.3477764e+00 4.3833898e+00 4.3689111e+00 4.2520226e+00 5.6153369e+00 6.1715044e+00 4.6178873e+00 4.8201070e+00 3.9129644e+00 5.7808751e+00 4.2195149e+00 4.4949009e+00 4.8099428e+00 4.0213766e+00 3.8049151e+00 4.5961475e+00 4.7475868e+00 5.3061067e+00 5.5699347e+00 4.6684257e+00 3.9900166e+00 4.2272640e+00 5.6441645e+00 4.4197984e+00 4.1176453e+00 3.7157925e+00 4.6326704e+00 4.7820862e+00 4.6921349e+00 4.0951447e+00 4.8160040e+00 4.8050680e+00 4.6459078e+00 4.5554678e+00 4.2905569e+00 4.2115152e+00 3.7649212e+00 5.9314593e-01 8.0686941e-01 2.9691107e-01 6.2826980e-01 5.0121118e-01 6.6653737e-01 7.0834786e-01 4.6310132e-01 1.9251840e+00 2.1737519e+00 7.4743804e-01 5.5009731e-01 8.0748088e-01 7.4743804e-01 1.1828955e+00 4.6964680e-01 5.8942278e-01 2.4421558e+00 9.8677196e-01 4.9772204e-01 1.1660949e+00 8.4116354e-01 1.2196311e+00 7.7538587e-01 1.0376697e+00 4.4499696e-01 3.0983271e+00 2.5986852e+00 3.1534326e+00 2.8897192e+00 2.9336987e+00 2.3392252e+00 2.6586759e+00 2.3702978e+00 2.8165027e+00 2.2079130e+00 3.2363154e+00 2.2664200e+00 3.1218253e+00 2.5673178e+00 1.8638939e+00 2.7710337e+00 2.2535803e+00 2.2156046e+00 3.5264693e+00 2.4375344e+00 2.6410495e+00 2.3635223e+00 3.2419868e+00 2.5535068e+00 2.5663186e+00 2.7372400e+00 3.1657714e+00 3.1910277e+00 2.5035271e+00 2.1450705e+00 2.5644558e+00 2.5011730e+00 2.2417546e+00 2.9810976e+00 2.2012005e+00 2.4146140e+00 2.9247451e+00 3.2953953e+00 1.9474034e+00 2.5368532e+00 2.4541089e+00 2.4554575e+00 2.4210504e+00 2.5661600e+00 2.3207574e+00 1.9628165e+00 2.1171985e+00 2.4261018e+00 2.1366217e+00 2.1917681e+00 3.8609963e+00 3.1157672e+00 4.0464741e+00 3.2769657e+00 3.7011972e+00 4.6584806e+00 2.9074576e+00 4.1962146e+00 3.9292452e+00 4.4717831e+00 3.2385313e+00 3.4507621e+00 3.7050324e+00 3.3601278e+00 3.4577371e+00 3.4991206e+00 3.2980589e+00 4.9174048e+00 5.1685262e+00 3.5822256e+00 3.9345678e+00 2.9743611e+00 4.8043725e+00 3.1946631e+00 3.6425792e+00 3.9147944e+00 3.0137993e+00 2.8509730e+00 3.6160190e+00 3.7930649e+00 4.3160863e+00 4.8705552e+00 3.6935350e+00 2.9765851e+00 3.2157655e+00 4.7030966e+00 3.6383061e+00 3.1964791e+00 2.7656084e+00 3.7055141e+00 3.8768834e+00 3.7701724e+00 3.1157672e+00 3.9366463e+00 3.9674740e+00 3.7027144e+00 3.5167570e+00 3.3369517e+00 3.4319544e+00 2.8332022e+00 9.6865373e-01 3.9487224e-01 5.8131330e-01 5.6003943e-01 5.0621589e-01 7.1247632e-01 8.0317491e-01 1.7053539e+00 2.0491684e+00 7.4957404e-01 6.5459290e-01 9.3991103e-01 7.4957404e-01 1.0954558e+00 4.2737382e-01 4.9430028e-01 2.5884539e+00 7.4949264e-01 6.4432393e-01 1.0251728e+00 9.7391954e-01 1.0055888e+00 5.9279023e-01 9.4588685e-01 4.3798311e-01 3.5017283e+00 3.0032209e+00 3.5640998e+00 3.2626300e+00 3.3723783e+00 2.7101865e+00 3.0361435e+00 2.6574564e+00 3.2363742e+00 2.5684615e+00 3.5220489e+00 2.6863775e+00 3.5035562e+00 2.9639854e+00 2.2954282e+00 3.1974234e+00 2.6186896e+00 2.5919605e+00 3.9485388e+00 2.8137879e+00 3.0123600e+00 2.8059985e+00 3.6581986e+00 2.9351026e+00 2.9984934e+00 3.1711590e+00 3.5947528e+00 3.6146776e+00 2.9159819e+00 2.5508141e+00 2.9298445e+00 2.8588898e+00 2.6582994e+00 3.3705985e+00 2.5395254e+00 2.7634723e+00 3.3411818e+00 3.7151762e+00 2.3273691e+00 2.9184140e+00 2.8006021e+00 2.8512853e+00 2.8277392e+00 2.8675465e+00 2.7048983e+00 2.3342128e+00 2.5075548e+00 2.8488482e+00 2.4938133e+00 2.5939117e+00 4.2210242e+00 3.5094230e+00 4.4613495e+00 3.6611526e+00 4.1011462e+00 5.0575391e+00 3.2183295e+00 4.5889909e+00 4.3421582e+00 4.8334387e+00 3.6441411e+00 3.8749352e+00 4.1286607e+00 3.7602700e+00 3.8694583e+00 3.9027404e+00 3.6910974e+00 5.2330448e+00 5.5920875e+00 3.9683831e+00 4.3421746e+00 3.3618744e+00 5.2099570e+00 3.6296154e+00 4.0192804e+00 4.2904705e+00 3.4453138e+00 3.2560920e+00 4.0303932e+00 4.1835729e+00 4.7330562e+00 5.1897695e+00 4.1126264e+00 3.3744864e+00 3.5691373e+00 5.1336306e+00 3.9986271e+00 3.5735980e+00 3.1698618e+00 4.1283627e+00 4.2954773e+00 4.2156054e+00 3.5094230e+00 4.3310092e+00 4.3633885e+00 4.1455698e+00 3.9545856e+00 3.7585686e+00 3.7901495e+00 3.2094268e+00 9.5902306e-01 1.1795364e+00 9.6032771e-01 5.8652824e-01 3.3395426e-01 1.0753036e+00 2.5524007e+00 2.8349345e+00 2.9691107e-01 5.1396090e-01 1.3127309e+00 2.9691107e-01 7.4426155e-01 9.3211669e-01 1.1729612e+00 1.7369516e+00 8.7560645e-01 1.2666796e+00 1.8751947e+00 2.9724335e-01 1.8489906e+00 6.7745878e-01 1.6555341e+00 7.0111465e-01 3.4067014e+00 2.9452188e+00 3.4231096e+00 2.6265336e+00 3.0474186e+00 2.3887954e+00 3.0652160e+00 1.9891259e+00 2.9589070e+00 2.1699637e+00 2.7527251e+00 2.5008826e+00 2.7949298e+00 2.7160947e+00 1.9851008e+00 3.0428102e+00 2.4755098e+00 2.1256864e+00 3.3327734e+00 2.2236827e+00 3.0131023e+00 2.4300526e+00 3.1928299e+00 2.6040863e+00 2.7075499e+00 2.9536823e+00 3.2710263e+00 3.4338296e+00 2.6673396e+00 1.9555334e+00 2.2858019e+00 2.1897213e+00 2.1973377e+00 3.0392888e+00 2.4158794e+00 2.8941568e+00 3.2025759e+00 3.1091590e+00 2.1471303e+00 2.3710990e+00 2.3347284e+00 2.6698387e+00 2.3133518e+00 2.1525596e+00 2.2918773e+00 2.1454625e+00 2.2398142e+00 2.5636830e+00 1.8343082e+00 2.2388656e+00 4.2714137e+00 3.2107728e+00 4.3091817e+00 3.4717121e+00 3.9768763e+00 4.9078859e+00 2.8122927e+00 4.3885241e+00 3.9504682e+00 4.9558940e+00 3.6044479e+00 3.5632387e+00 3.9760735e+00 3.3646188e+00 3.6594046e+00 3.8782141e+00 3.5443654e+00 5.4091146e+00 5.2988184e+00 3.3878489e+00 4.2952367e+00 3.1303129e+00 4.9761618e+00 3.2919446e+00 4.0362588e+00 4.2291285e+00 3.1618923e+00 3.1077588e+00 3.7959134e+00 4.0112445e+00 4.4810404e+00 5.3509795e+00 3.8831153e+00 3.0844802e+00 3.1980603e+00 4.9707249e+00 4.0945548e+00 3.4918172e+00 3.0237585e+00 4.0192804e+00 4.2092252e+00 4.1017980e+00 3.2107728e+00 4.2952415e+00 4.3790330e+00 3.9936934e+00 3.5312555e+00 3.6065699e+00 3.8937621e+00 3.0840989e+00 4.2827238e-01 3.7398306e-01 6.4241342e-01 7.7828522e-01 4.8636669e-01 1.6800011e+00 1.9622194e+00 8.0686941e-01 5.7691891e-01 7.1789533e-01 8.0686941e-01 1.2139401e+00 2.9406726e-01 3.1507080e-01 2.6166211e+00 9.1398375e-01 3.4909881e-01 9.4580058e-01 9.6922609e-01 9.6659661e-01 7.2638147e-01 8.2574748e-01 3.6704030e-01 3.2939549e+00 2.8018134e+00 3.3643791e+00 3.1688464e+00 3.1882120e+00 2.5926123e+00 2.8420400e+00 2.6229843e+00 3.0569434e+00 2.4659441e+00 3.4932809e+00 2.5062529e+00 3.4038365e+00 2.8103848e+00 2.1284730e+00 2.9880998e+00 2.4809350e+00 2.4830222e+00 3.8129321e+00 2.7155086e+00 2.8370039e+00 2.6306749e+00 3.5140670e+00 2.8045035e+00 2.8143558e+00 2.9698163e+00 3.4126571e+00 3.4177063e+00 2.7508387e+00 2.4282690e+00 2.8424700e+00 2.7781836e+00 2.5174968e+00 3.2360555e+00 2.4214382e+00 2.5753180e+00 3.1397228e+00 3.5790751e+00 2.1850441e+00 2.8131786e+00 2.7177153e+00 2.6876771e+00 2.6993734e+00 2.8253248e+00 2.5871836e+00 2.1990767e+00 2.3678133e+00 2.6762366e+00 2.4070540e+00 2.4551607e+00 4.0383784e+00 3.3671653e+00 4.2642537e+00 3.5070157e+00 3.9193977e+00 4.8684861e+00 3.1505769e+00 4.4165051e+00 4.1898287e+00 4.6203725e+00 3.4386758e+00 3.7047820e+00 3.9273366e+00 3.6237778e+00 3.6947343e+00 3.6968840e+00 3.5190023e+00 5.0398879e+00 5.4089760e+00 3.8611646e+00 4.1322470e+00 3.2145601e+00 5.0295849e+00 3.4546083e+00 3.8248698e+00 4.1055247e+00 3.2664139e+00 3.0788010e+00 3.8567883e+00 4.0060320e+00 4.5478501e+00 4.9912214e+00 3.9339246e+00 3.2236553e+00 3.4677443e+00 4.9178815e+00 3.8042163e+00 3.4041321e+00 2.9939885e+00 3.9171296e+00 4.0866646e+00 3.9845548e+00 3.3671653e+00 4.1322520e+00 4.1520423e+00 3.9277271e+00 3.7880802e+00 3.5624203e+00 3.5967687e+00 3.0549169e+00 2.3749211e-01 9.2006504e-01 1.0428797e+00 4.2450569e-01 1.3899721e+00 1.6555341e+00 9.9973471e-01 7.5230154e-01 3.7960845e-01 9.9973471e-01 1.5086315e+00 2.6033464e-01 2.9724335e-01 2.8989712e+00 1.1937015e+00 5.7988427e-01 7.8318003e-01 1.2583645e+00 7.0463400e-01 1.0034646e+00 4.7680727e-01 5.2374483e-01 3.3114294e+00 2.8933417e+00 3.4177063e+00 3.4461307e+00 3.3255941e+00 2.8176969e+00 2.9380167e+00 2.9507452e+00 3.1524130e+00 2.7797208e+00 3.7960371e+00 2.6995806e+00 3.6095704e+00 2.9762135e+00 2.3753547e+00 3.0506196e+00 2.7121484e+00 2.6831858e+00 4.0299127e+00 2.9653560e+00 3.0144396e+00 2.8058449e+00 3.7011663e+00 2.9654419e+00 2.9344166e+00 3.0597490e+00 3.5085997e+00 3.5226725e+00 2.9395346e+00 2.6564538e+00 3.1076159e+00 3.0365838e+00 2.7379532e+00 3.4446760e+00 2.6796909e+00 2.6912430e+00 3.2129972e+00 3.7686900e+00 2.4108238e+00 3.0879511e+00 2.9762529e+00 2.8408428e+00 2.9254092e+00 3.1396427e+00 2.8384396e+00 2.3985415e+00 2.5881776e+00 2.8229620e+00 2.7289403e+00 2.6869860e+00 4.1910480e+00 3.6130663e+00 4.3602249e+00 3.6707779e+00 4.0745117e+00 4.9277939e+00 3.4934873e+00 4.4880495e+00 4.3514534e+00 4.6654573e+00 3.5594056e+00 3.8864758e+00 4.0498126e+00 3.8963526e+00 3.9502563e+00 3.8456535e+00 3.6509387e+00 5.0146974e+00 5.5101576e+00 4.0937915e+00 4.2345704e+00 3.4807992e+00 5.0960662e+00 3.6438388e+00 3.9190152e+00 4.1487738e+00 3.4580678e+00 3.2588014e+00 4.0378648e+00 4.0580582e+00 4.6285945e+00 4.9381887e+00 4.1199488e+00 3.3816601e+00 3.6553790e+00 4.9813108e+00 3.9405186e+00 3.5335600e+00 3.1869497e+00 4.0186781e+00 4.2240093e+00 4.0988575e+00 3.6130663e+00 4.2429720e+00 4.2712088e+00 4.0719125e+00 3.9975817e+00 3.7087600e+00 3.7375363e+00 3.2561951e+00 7.6824760e-01 8.5141186e-01 3.6086962e-01 1.6207126e+00 1.8802756e+00 7.9394533e-01 5.3286499e-01 4.3319335e-01 7.9394533e-01 1.3370188e+00 1.3340137e-01 3.6319073e-01 2.6778759e+00 1.0720705e+00 6.3173774e-01 1.0072799e+00 1.0495503e+00 9.3727156e-01 8.5893964e-01 7.0463400e-01 3.3395426e-01 3.3027871e+00 2.8812178e+00 3.3955887e+00 3.2888081e+00 3.2512254e+00 2.7283479e+00 2.9463809e+00 2.7764635e+00 3.0911130e+00 2.6620270e+00 3.6054231e+00 2.6430457e+00 3.4442793e+00 2.9123088e+00 2.2793286e+00 3.0205073e+00 2.6595069e+00 2.5635668e+00 3.8866925e+00 2.8178210e+00 3.0060120e+00 2.7101865e+00 3.5930006e+00 2.8829084e+00 2.8651003e+00 3.0121201e+00 3.4400598e+00 3.4869142e+00 2.8725792e+00 2.5068325e+00 2.9480926e+00 2.8720011e+00 2.6183827e+00 3.3619078e+00 2.6263990e+00 2.7176350e+00 3.1874455e+00 3.6289342e+00 2.3459758e+00 2.9476507e+00 2.8536577e+00 2.7917808e+00 2.7959976e+00 2.9585178e+00 2.7268209e+00 2.3347284e+00 2.5080346e+00 2.7508387e+00 2.5565749e+00 2.5881776e+00 4.2068537e+00 3.5342439e+00 4.3380559e+00 3.6271373e+00 4.0499864e+00 4.9127682e+00 3.3748745e+00 4.4574738e+00 4.2666131e+00 4.7143178e+00 3.5549829e+00 3.8149934e+00 4.0227420e+00 3.7946027e+00 3.8919837e+00 3.8432323e+00 3.6208873e+00 5.0849580e+00 5.4596452e+00 3.9569474e+00 4.2354064e+00 3.4126422e+00 5.0611947e+00 3.5638945e+00 3.9334644e+00 4.1519487e+00 3.3885059e+00 3.2191166e+00 3.9849073e+00 4.0334328e+00 4.5859724e+00 5.0075986e+00 4.0680600e+00 3.3134027e+00 3.5670952e+00 4.9632121e+00 3.9675062e+00 3.5176551e+00 3.1453377e+00 4.0038982e+00 4.2114761e+00 4.0820077e+00 3.5342439e+00 4.2453199e+00 4.2844704e+00 4.0426067e+00 3.8984747e+00 3.6765607e+00 3.7642725e+00 3.2184749e+00 2.6033464e-01 9.9962901e-01 2.1664244e+00 2.5030472e+00 3.6319073e-01 4.2737382e-01 1.2004100e+00 3.6319073e-01 6.1067563e-01 6.7030885e-01 8.0996690e-01 2.1006743e+00 4.0020411e-01 9.4057729e-01 1.4985933e+00 5.0731024e-01 1.4656715e+00 1.6562722e-01 1.3630799e+00 4.4417668e-01 3.6433721e+00 3.1333439e+00 3.6757970e+00 3.0281244e+00 3.3717708e+00 2.6624050e+00 3.1998148e+00 2.3430115e+00 3.2729115e+00 2.4219924e+00 3.1700354e+00 2.7177110e+00 3.2760900e+00 2.9826961e+00 2.2410752e+00 3.2981044e+00 2.6452183e+00 2.4901533e+00 3.7687554e+00 2.6225184e+00 3.1280668e+00 2.7635526e+00 3.5692464e+00 2.9177616e+00 3.0187150e+00 3.2354747e+00 3.6116754e+00 3.6909005e+00 2.9234404e+00 2.3731183e+00 2.6987010e+00 2.6178190e+00 2.5515904e+00 3.3308561e+00 2.5554841e+00 2.9570491e+00 3.4460544e+00 3.5549612e+00 2.3407691e+00 2.7326628e+00 2.6614903e+00 2.9042354e+00 2.6919656e+00 2.5430017e+00 2.6000033e+00 2.3578684e+00 2.4871797e+00 2.8599439e+00 2.2045434e+00 2.5287499e+00 4.3690091e+00 3.4628576e+00 4.5552847e+00 3.7077077e+00 4.1799642e+00 5.1678263e+00 3.0379779e+00 4.6720960e+00 4.3013447e+00 5.0550361e+00 3.7713495e+00 3.8605708e+00 4.2104897e+00 3.6525334e+00 3.8549710e+00 4.0229434e+00 3.7708197e+00 5.5011831e+00 5.6245097e+00 3.7945557e+00 4.4755001e+00 3.3306776e+00 5.2815051e+00 3.5995462e+00 4.1814664e+00 4.4417427e+00 3.4376057e+00 3.3113002e+00 4.0501276e+00 4.2847585e+00 4.7905454e+00 5.4600808e+00 4.1319681e+00 3.3795104e+00 3.5192584e+00 5.2359042e+00 4.1708373e+00 3.6809073e+00 3.2190305e+00 4.2365575e+00 4.3973146e+00 4.3149219e+00 3.4628576e+00 4.4657183e+00 4.5132257e+00 4.2167698e+00 3.8749352e+00 3.8293474e+00 3.9628758e+00 3.2623926e+00 1.0371214e+00 2.3606689e+00 2.6764689e+00 1.8699153e-01 4.0363332e-01 1.2627589e+00 1.8699153e-01 5.6164055e-01 7.8305765e-01 9.7747632e-01 1.8924893e+00 5.6164055e-01 1.0881632e+00 1.6837963e+00 2.8845946e-01 1.6545637e+00 3.5266705e-01 1.5108472e+00 5.3286499e-01 3.5596461e+00 3.0642731e+00 3.5820652e+00 2.8366432e+00 3.2382208e+00 2.5375138e+00 3.1537738e+00 2.1559444e+00 3.1474432e+00 2.2926143e+00 2.9585178e+00 2.6250629e+00 3.0590120e+00 2.8699760e+00 2.1233327e+00 3.2024265e+00 2.5670381e+00 2.3272067e+00 3.5735096e+00 2.4368431e+00 3.0826231e+00 2.6212838e+00 3.4052824e+00 2.7833861e+00 2.8923043e+00 3.1255601e+00 3.4747564e+00 3.5908785e+00 2.8135312e+00 2.1839196e+00 2.5032729e+00 2.4158569e+00 2.3928312e+00 3.2017644e+00 2.4862299e+00 2.9403226e+00 3.3545989e+00 3.3587820e+00 2.2520446e+00 2.5607059e+00 2.5059290e+00 2.8073565e+00 2.5209771e+00 2.3430115e+00 2.4562939e+00 2.2633781e+00 2.3755041e+00 2.7368591e+00 2.0165442e+00 2.3969046e+00 4.3354025e+00 3.3475977e+00 4.4615703e+00 3.6095772e+00 4.0990362e+00 5.0710534e+00 2.9144612e+00 4.5627576e+00 4.1522674e+00 5.0316497e+00 3.7102339e+00 3.7341705e+00 4.1195531e+00 3.5174471e+00 3.7659275e+00 3.9693828e+00 3.6809073e+00 5.4858042e+00 5.4945039e+00 3.6088006e+00 4.4109170e+00 3.2362256e+00 5.1634795e+00 3.4678413e+00 4.1322470e+00 4.3666536e+00 3.3199204e+00 3.2266664e+00 3.9433408e+00 4.1815045e+00 4.6694810e+00 5.4392260e+00 4.0273519e+00 3.2552513e+00 3.3773249e+00 5.1375752e+00 4.1484621e+00 3.6075786e+00 3.1365574e+00 4.1554935e+00 4.3260165e+00 4.2353581e+00 3.3475977e+00 4.4043042e+00 4.4676627e+00 4.1295047e+00 3.7244531e+00 3.7408419e+00 3.9430201e+00 3.1856251e+00 1.6790448e+00 1.8683303e+00 9.9891776e-01 7.3735391e-01 3.8639663e-01 9.9891776e-01 1.5462701e+00 4.4713936e-01 5.6262711e-01 2.7653818e+00 1.3238834e+00 5.9868400e-01 1.0167074e+00 1.1817121e+00 1.0267382e+00 1.1036371e+00 7.4965096e-01 5.9868400e-01 2.9920629e+00 2.5767485e+00 3.0904477e+00 3.1383073e+00 2.9738737e+00 2.5155127e+00 2.6450302e+00 2.7097016e+00 2.8120208e+00 2.4963677e+00 3.5442384e+00 2.3737852e+00 3.2878773e+00 2.6552959e+00 2.0482711e+00 2.7132601e+00 2.4244329e+00 2.3725931e+00 3.6825624e+00 2.6567419e+00 2.7277627e+00 2.4551607e+00 3.3586468e+00 2.6490703e+00 2.5878996e+00 2.7146857e+00 3.1604264e+00 3.1862677e+00 2.6121387e+00 2.3320406e+00 2.8060955e+00 2.7397840e+00 2.4059801e+00 3.1251810e+00 2.4123722e+00 2.4266062e+00 2.8827369e+00 3.4219145e+00 2.1146056e+00 2.7787333e+00 2.6868306e+00 2.5237903e+00 2.5969195e+00 2.8858666e+00 2.5292454e+00 2.1030528e+00 2.2789104e+00 2.4843930e+00 2.4502513e+00 2.3681813e+00 3.9129285e+00 3.2963379e+00 4.0307050e+00 3.3579713e+00 3.7573925e+00 4.6072225e+00 3.2350676e+00 4.1666050e+00 4.0096938e+00 4.3939440e+00 3.2458961e+00 3.5448948e+00 3.7163166e+00 3.5735211e+00 3.6303034e+00 3.5366397e+00 3.3347301e+00 4.7764597e+00 5.1656525e+00 3.7678733e+00 3.9190152e+00 3.1752055e+00 4.7655871e+00 3.2963860e+00 3.6257668e+00 3.8480918e+00 3.1163356e+00 2.9401017e+00 3.7060704e+00 3.7400429e+00 4.2905130e+00 4.6982735e+00 3.7862785e+00 3.0555922e+00 3.3519252e+00 4.6433941e+00 3.6672706e+00 3.2313825e+00 2.8704346e+00 3.6888814e+00 3.9001229e+00 3.7578379e+00 3.2963379e+00 3.9355102e+00 3.9693842e+00 3.7298088e+00 3.6452459e+00 3.3776637e+00 3.4666091e+00 2.9570067e+00 4.5257749e-01 2.3345854e+00 2.1006743e+00 1.4408765e+00 2.3345854e+00 2.7201861e+00 1.6242170e+00 1.4334280e+00 4.2461520e+00 2.2960397e+00 1.5510150e+00 8.3619405e-01 2.5963945e+00 7.1671402e-01 2.2031378e+00 9.3957399e-01 1.8662528e+00 3.9018608e+00 3.5587525e+00 4.0758181e+00 4.6738622e+00 4.2315488e+00 3.8362958e+00 3.5101695e+00 4.2349456e+00 4.0096351e+00 3.8957954e+00 5.1177048e+00 3.5857491e+00 4.8511271e+00 3.8770675e+00 3.4324591e+00 3.7687554e+00 3.5952204e+00 3.8095276e+00 5.1880951e+00 4.1733990e+00 3.6719420e+00 3.8275992e+00 4.7391892e+00 3.9417326e+00 3.8406076e+00 3.8597390e+00 4.3729123e+00 4.2482658e+00 3.8534412e+00 3.8740219e+00 4.3496532e+00 4.2951960e+00 3.8572117e+00 4.4028225e+00 3.5707989e+00 3.2084035e+00 3.9057558e+00 4.9165227e+00 3.3635180e+00 4.2694314e+00 4.1082916e+00 3.6886188e+00 4.0716087e+00 4.4411156e+00 3.9335446e+00 3.3496034e+00 3.5830335e+00 3.7561390e+00 4.0088699e+00 3.7413425e+00 4.6436290e+00 4.5471223e+00 4.9787007e+00 4.4481204e+00 4.7341193e+00 5.4826137e+00 4.5863292e+00 5.1433212e+00 5.2725182e+00 4.8836508e+00 4.1393671e+00 4.7672599e+00 4.7092337e+00 4.9106562e+00 4.7707455e+00 4.3996661e+00 4.3591552e+00 5.0844032e+00 6.2257171e+00 5.2378683e+00 4.7433741e+00 4.3742533e+00 5.7435198e+00 4.5678545e+00 4.3840310e+00 4.6485090e+00 4.3482964e+00 4.0364176e+00 4.8328894e+00 4.6980887e+00 5.3298852e+00 5.0020990e+00 4.9051796e+00 4.2757519e+00 4.6314634e+00 5.5369700e+00 4.3420582e+00 4.1857666e+00 3.9786340e+00 4.6138259e+00 4.8044656e+00 4.6911542e+00 4.5471223e+00 4.7508760e+00 4.7161034e+00 4.7355095e+00 4.9879154e+00 4.4154797e+00 4.1545903e+00 4.0343137e+00 2.6422396e+00 2.3867296e+00 1.6154069e+00 2.6422396e+00 3.0703842e+00 1.9080710e+00 1.7295385e+00 4.5475791e+00 2.6621202e+00 1.8051284e+00 1.1105716e+00 2.8967543e+00 1.0474897e+00 2.5495727e+00 1.1795364e+00 2.1617152e+00 3.8171826e+00 3.5339654e+00 4.0163479e+00 4.8425911e+00 4.2514283e+00 3.9557653e+00 3.4792404e+00 4.4740529e+00 4.0150475e+00 4.0717494e+00 5.3501548e+00 3.6486698e+00 4.9910943e+00 3.9350582e+00 3.5547141e+00 3.7282030e+00 3.6962934e+00 3.9420318e+00 5.2895497e+00 4.3341610e+00 3.6960949e+00 3.8986276e+00 4.8106103e+00 4.0206152e+00 3.8664495e+00 3.8454452e+00 4.3675713e+00 4.2173036e+00 3.9169317e+00 4.0237417e+00 4.5248905e+00 4.4756871e+00 3.9778974e+00 4.4827624e+00 3.6962934e+00 3.1970228e+00 3.8646917e+00 5.0103465e+00 3.4775294e+00 4.4295579e+00 4.2690345e+00 3.7344524e+00 4.1995700e+00 4.6716989e+00 4.0716455e+00 3.4573201e+00 3.6936958e+00 3.8056210e+00 4.2211876e+00 3.8604223e+00 4.5958210e+00 4.6323449e+00 4.9087471e+00 4.4703758e+00 4.7121623e+00 5.3828288e+00 4.7798674e+00 5.0815530e+00 5.2996504e+00 4.7231435e+00 4.0911974e+00 4.7955081e+00 4.6606898e+00 5.0156099e+00 4.8233009e+00 4.3540706e+00 4.3488974e+00 4.8785742e+00 6.1614901e+00 5.3577416e+00 4.6571075e+00 4.4651793e+00 5.6630236e+00 4.6077729e+00 4.3065165e+00 4.5525984e+00 4.3873290e+00 4.0638414e+00 4.8447047e+00 4.6322938e+00 5.2676175e+00 4.7796156e+00 4.9133278e+00 4.3194697e+00 4.7202167e+00 5.4208419e+00 4.2794874e+00 4.1728215e+00 4.0165591e+00 4.5422714e+00 4.7447407e+00 4.6112705e+00 4.6323449e+00 4.6754921e+00 4.6293227e+00 4.6871898e+00 5.0428586e+00 4.3953592e+00 4.1024574e+00 4.0848110e+00 3.3742167e-01 1.1857824e+00 0.0000000e+00 6.6918102e-01 7.4445830e-01 9.7322023e-01 1.9284841e+00 6.6918102e-01 1.1393372e+00 1.6942803e+00 3.7371902e-01 1.6386105e+00 4.5257749e-01 1.4715172e+00 4.9772204e-01 3.5602797e+00 3.0968979e+00 3.5933352e+00 2.8998722e+00 3.2656298e+00 2.6029746e+00 3.1974447e+00 2.2445103e+00 3.1601923e+00 2.3946216e+00 3.0209665e+00 2.6867317e+00 3.0775658e+00 2.9161244e+00 2.1946278e+00 3.2137635e+00 2.6502891e+00 2.3652711e+00 3.6096140e+00 2.4893065e+00 3.1578003e+00 2.6568473e+00 3.4426472e+00 2.8187901e+00 2.9128857e+00 3.1418202e+00 3.4847097e+00 3.6205958e+00 2.8694312e+00 2.2223283e+00 2.5588203e+00 2.4651138e+00 2.4413293e+00 3.2621861e+00 2.5834128e+00 2.9995857e+00 3.3733791e+00 3.3817876e+00 2.3263008e+00 2.6305758e+00 2.5756071e+00 2.8533918e+00 2.5683063e+00 2.4187322e+00 2.5258216e+00 2.3250308e+00 2.4413618e+00 2.7691536e+00 2.1006933e+00 2.4608850e+00 4.4119896e+00 3.4290419e+00 4.4942656e+00 3.6650933e+00 4.1590662e+00 5.0899438e+00 3.0333619e+00 4.5799469e+00 4.1882413e+00 5.0726081e+00 3.7613721e+00 3.7859992e+00 4.1623715e+00 3.6029758e+00 3.8608065e+00 4.0352348e+00 3.7266849e+00 5.5043247e+00 5.5172732e+00 3.6569442e+00 4.4568115e+00 3.3324016e+00 5.1765224e+00 3.5192065e+00 4.1799642e+00 4.3857400e+00 3.3769078e+00 3.2906843e+00 4.0034548e+00 4.1917183e+00 4.6854597e+00 5.4444866e+00 4.0904298e+00 3.2962678e+00 3.4250783e+00 5.1569386e+00 4.2213314e+00 3.6582637e+00 3.2059261e+00 4.1937040e+00 4.3826532e+00 4.2786320e+00 3.4290419e+00 4.4549850e+00 4.5270304e+00 4.1816268e+00 3.7777251e+00 3.7924144e+00 4.0173731e+00 3.2613828e+00 9.2006504e-01 3.3742167e-01 8.6080744e-01 5.0621589e-01 7.0646671e-01 2.1664244e+00 7.2679299e-01 8.9712099e-01 1.4681660e+00 5.4873947e-01 1.4074199e+00 4.9617437e-01 1.2206236e+00 2.5698045e-01 3.4986942e+00 3.0427234e+00 3.5520531e+00 3.0444864e+00 3.2783159e+00 2.6723101e+00 3.1333743e+00 2.4360210e+00 3.1627463e+00 2.4904253e+00 3.2338077e+00 2.6808015e+00 3.2240677e+00 2.9412073e+00 2.2206950e+00 3.1669559e+00 2.6716144e+00 2.4623998e+00 3.7173707e+00 2.6198784e+00 3.1208539e+00 2.6854361e+00 3.5171200e+00 2.8753360e+00 2.9157449e+00 3.1157529e+00 3.4945103e+00 3.5956983e+00 2.8873581e+00 2.3297122e+00 2.7075732e+00 2.6220688e+00 2.5143128e+00 3.3225169e+00 2.6164571e+00 2.9213819e+00 3.3323415e+00 3.4842326e+00 2.3487772e+00 2.7507828e+00 2.6991998e+00 2.8571158e+00 2.6614903e+00 2.6122501e+00 2.6121387e+00 2.3527202e+00 2.4822998e+00 2.7826627e+00 2.2477567e+00 2.5188545e+00 4.3590737e+00 3.4800724e+00 4.4652192e+00 3.6820633e+00 4.1423404e+00 5.0632361e+00 3.1594575e+00 4.5764429e+00 4.2442248e+00 4.9703970e+00 3.7054123e+00 3.8144340e+00 4.1322520e+00 3.6772717e+00 3.8704422e+00 3.9786899e+00 3.7187193e+00 5.3973273e+00 5.5276243e+00 3.7838435e+00 4.3978718e+00 3.3669786e+00 5.1732410e+00 3.5478651e+00 4.1195682e+00 4.3422158e+00 3.3925731e+00 3.2818178e+00 4.0157845e+00 4.1753467e+00 4.6824968e+00 5.3318394e+00 4.0983108e+00 3.3321312e+00 3.5171976e+00 5.1116177e+00 4.1480571e+00 3.6395566e+00 3.1983718e+00 4.1451783e+00 4.3355345e+00 4.2161052e+00 3.4800724e+00 4.4037157e+00 4.4559384e+00 4.1399085e+00 3.8303307e+00 3.7678377e+00 3.9434740e+00 3.2672961e+00 1.1857824e+00 1.7590894e+00 5.4715569e-01 6.1787077e-01 3.0224093e+00 1.4977817e+00 8.1744862e-01 9.4676850e-01 1.4369223e+00 8.6079202e-01 1.2896554e+00 5.3286499e-01 7.6195008e-01 3.1536923e+00 2.8019556e+00 3.2823965e+00 3.4754319e+00 3.2348803e+00 2.8339836e+00 2.8678686e+00 3.0569237e+00 3.0422163e+00 2.8599505e+00 3.8711727e+00 2.6769819e+00 3.5769114e+00 2.9369339e+00 2.3887701e+00 2.9172679e+00 2.7450499e+00 2.6744413e+00 3.9868196e+00 2.9825819e+00 3.0070640e+00 2.7478281e+00 3.6492544e+00 2.9260177e+00 2.8398296e+00 2.9417237e+00 3.3879693e+00 3.4191352e+00 2.9103957e+00 2.6495864e+00 3.1359829e+00 3.0635119e+00 2.7246726e+00 3.4310966e+00 2.7450499e+00 2.6593850e+00 3.0929063e+00 3.7090709e+00 2.4372581e+00 3.1206171e+00 3.0186562e+00 2.7973689e+00 2.9151879e+00 3.2261028e+00 2.8631702e+00 2.4096686e+00 2.5984928e+00 2.7564382e+00 2.8056103e+00 2.6945402e+00 4.1622883e+00 3.6243461e+00 4.2495237e+00 3.6308369e+00 4.0200378e+00 4.7940036e+00 3.6050689e+00 4.3664479e+00 4.2800934e+00 4.5553898e+00 3.4840330e+00 3.8323637e+00 3.9571437e+00 3.9163572e+00 3.9605759e+00 3.7909588e+00 3.5846709e+00 4.8756388e+00 5.3862972e+00 4.0807979e+00 4.1385728e+00 3.5138167e+00 4.9592897e+00 3.5910983e+00 3.8379532e+00 4.0230039e+00 3.4134019e+00 3.2269518e+00 3.9906410e+00 3.9261146e+00 4.4982182e+00 4.7746016e+00 4.0736767e+00 3.3286256e+00 3.6393910e+00 4.8333249e+00 3.9033387e+00 3.4776516e+00 3.1661860e+00 3.9124707e+00 4.1473618e+00 3.9899548e+00 3.6243461e+00 4.1607944e+00 4.1969547e+00 3.9859042e+00 3.9511939e+00 3.6382505e+00 3.7066628e+00 3.2552942e+00 6.6918102e-01 7.4445830e-01 9.7322023e-01 1.9284841e+00 6.6918102e-01 1.1393372e+00 1.6942803e+00 3.7371902e-01 1.6386105e+00 4.5257749e-01 1.4715172e+00 4.9772204e-01 3.5602797e+00 3.0968979e+00 3.5933352e+00 2.8998722e+00 3.2656298e+00 2.6029746e+00 3.1974447e+00 2.2445103e+00 3.1601923e+00 2.3946216e+00 3.0209665e+00 2.6867317e+00 3.0775658e+00 2.9161244e+00 2.1946278e+00 3.2137635e+00 2.6502891e+00 2.3652711e+00 3.6096140e+00 2.4893065e+00 3.1578003e+00 2.6568473e+00 3.4426472e+00 2.8187901e+00 2.9128857e+00 3.1418202e+00 3.4847097e+00 3.6205958e+00 2.8694312e+00 2.2223283e+00 2.5588203e+00 2.4651138e+00 2.4413293e+00 3.2621861e+00 2.5834128e+00 2.9995857e+00 3.3733791e+00 3.3817876e+00 2.3263008e+00 2.6305758e+00 2.5756071e+00 2.8533918e+00 2.5683063e+00 2.4187322e+00 2.5258216e+00 2.3250308e+00 2.4413618e+00 2.7691536e+00 2.1006933e+00 2.4608850e+00 4.4119896e+00 3.4290419e+00 4.4942656e+00 3.6650933e+00 4.1590662e+00 5.0899438e+00 3.0333619e+00 4.5799469e+00 4.1882413e+00 5.0726081e+00 3.7613721e+00 3.7859992e+00 4.1623715e+00 3.6029758e+00 3.8608065e+00 4.0352348e+00 3.7266849e+00 5.5043247e+00 5.5172732e+00 3.6569442e+00 4.4568115e+00 3.3324016e+00 5.1765224e+00 3.5192065e+00 4.1799642e+00 4.3857400e+00 3.3769078e+00 3.2906843e+00 4.0034548e+00 4.1917183e+00 4.6854597e+00 5.4444866e+00 4.0904298e+00 3.2962678e+00 3.4250783e+00 5.1569386e+00 4.2213314e+00 3.6582637e+00 3.2059261e+00 4.1937040e+00 4.3826532e+00 4.2786320e+00 3.4290419e+00 4.4549850e+00 4.5270304e+00 4.1816268e+00 3.7777251e+00 3.7924144e+00 4.0173731e+00 3.2613828e+00 1.2563834e+00 1.3681903e+00 1.6242170e+00 4.6126066e-01 1.4691503e+00 2.0743925e+00 5.0370871e-01 2.0365895e+00 5.2374483e-01 1.9494772e+00 1.0034646e+00 4.0320101e+00 3.4981749e+00 4.0289839e+00 2.9648238e+00 3.6116412e+00 2.8362334e+00 3.5807825e+00 2.1594400e+00 3.5619276e+00 2.4608850e+00 2.9150653e+00 2.9806848e+00 3.2524084e+00 3.2332049e+00 2.4351674e+00 3.6506644e+00 2.8794131e+00 2.6371069e+00 3.7842148e+00 2.6442387e+00 3.4386758e+00 2.9743384e+00 3.6958304e+00 3.1396988e+00 3.2947222e+00 3.5521670e+00 3.8756118e+00 3.9969338e+00 3.1587328e+00 2.4432066e+00 2.6604221e+00 2.5745994e+00 2.6880360e+00 3.4951118e+00 2.7657429e+00 3.3524671e+00 3.7924883e+00 3.6104716e+00 2.5876530e+00 2.7410968e+00 2.7238850e+00 3.1914275e+00 2.7871336e+00 2.3484202e+00 2.7125180e+00 2.6235600e+00 2.7012637e+00 3.1219910e+00 2.0888843e+00 2.6969064e+00 4.6820911e+00 3.5968849e+00 4.8607426e+00 3.9563487e+00 4.4501699e+00 5.4913616e+00 2.9743611e+00 4.9743359e+00 4.4659463e+00 5.4618868e+00 4.1043393e+00 4.0513907e+00 4.5016468e+00 3.7087600e+00 4.0024694e+00 4.3310092e+00 4.0611789e+00 5.9599083e+00 5.8632763e+00 3.7995759e+00 4.8081465e+00 3.4697005e+00 5.5699347e+00 3.7817852e+00 4.5398887e+00 4.8101536e+00 3.6425657e+00 3.5739550e+00 4.2642554e+00 4.6155808e+00 5.0696209e+00 5.9318766e+00 4.3420618e+00 3.6079861e+00 3.6711700e+00 5.5546786e+00 4.5127918e+00 3.9935485e+00 3.4733020e+00 4.5569739e+00 4.6922818e+00 4.6236700e+00 3.5968849e+00 4.7939394e+00 4.8471780e+00 4.4913725e+00 3.9942507e+00 4.1085491e+00 4.3067090e+00 3.5093006e+00 3.1271814e-01 2.6440626e+00 9.6964683e-01 5.8796666e-01 9.8545402e-01 1.0013399e+00 9.2426065e-01 7.6195008e-01 7.3283576e-01 2.6643250e-01 3.3524935e+00 2.9103383e+00 3.4378505e+00 3.2794093e+00 3.2805279e+00 2.7218307e+00 2.9677934e+00 2.7417113e+00 3.1265865e+00 2.6350666e+00 3.5810092e+00 2.6509959e+00 3.4564670e+00 2.9240118e+00 2.2778214e+00 3.0636652e+00 2.6473174e+00 2.5673371e+00 3.9008585e+00 2.8131786e+00 3.0066012e+00 2.7310040e+00 3.6088006e+00 2.8947302e+00 2.8966017e+00 3.0506196e+00 3.4785143e+00 3.5188926e+00 2.8816131e+00 2.5125550e+00 2.9397900e+00 2.8645995e+00 2.6245231e+00 3.3639057e+00 2.6028733e+00 2.7271822e+00 3.2253861e+00 3.6489825e+00 2.3376510e+00 2.9371604e+00 2.8382974e+00 2.8051321e+00 2.8006021e+00 2.9309089e+00 2.7184808e+00 2.3312464e+00 2.5047936e+00 2.7731354e+00 2.5341497e+00 2.5862794e+00 4.2119757e+00 3.5278864e+00 4.3705395e+00 3.6366115e+00 4.0640736e+00 4.9516709e+00 3.3348040e+00 4.4927271e+00 4.2867968e+00 4.7459453e+00 3.5773144e+00 3.8303307e+00 4.0501276e+00 3.7856794e+00 3.8862115e+00 3.8584574e+00 3.6392860e+00 5.1247727e+00 5.4955777e+00 3.9594564e+00 4.2633399e+00 3.3993739e+00 5.1011948e+00 3.5798230e+00 3.9561606e+00 4.1885925e+00 3.4019138e+00 3.2277183e+00 3.9971830e+00 4.0727212e+00 4.6247900e+00 5.0557045e+00 4.0800856e+00 3.3285999e+00 3.5685643e+00 5.0078455e+00 3.9761694e+00 3.5324648e+00 3.1505332e+00 4.0358238e+00 4.2334406e+00 4.1156691e+00 3.5278864e+00 4.2682695e+00 4.3053166e+00 4.0686429e+00 3.9122205e+00 3.6972894e+00 3.7712394e+00 3.2160302e+00 2.8326674e+00 1.0103954e+00 4.2829723e-01 7.9126749e-01 1.1795364e+00 7.3442235e-01 8.5582452e-01 6.1158310e-01 4.8284931e-01 3.4789406e+00 3.0164286e+00 3.5708825e+00 3.4760111e+00 3.4435701e+00 2.8856827e+00 3.0483404e+00 2.9286177e+00 3.2959553e+00 2.7769569e+00 3.7899650e+00 2.7721706e+00 3.6919547e+00 3.0773843e+00 2.4199353e+00 3.1984671e+00 2.7597977e+00 2.7743820e+00 4.1049898e+00 3.0189963e+00 3.0754044e+00 2.9033794e+00 3.7972498e+00 3.0781443e+00 3.0628742e+00 3.1980682e+00 3.6529321e+00 3.6479042e+00 3.0224061e+00 2.7237896e+00 3.1475538e+00 3.0809334e+00 2.8106441e+00 3.5217353e+00 2.7064382e+00 2.7753422e+00 3.3543209e+00 3.8636687e+00 2.4678110e+00 3.1212622e+00 3.0250044e+00 2.9444840e+00 2.9956969e+00 3.1281937e+00 2.8892226e+00 2.4772050e+00 2.6547819e+00 2.9364676e+00 2.7130802e+00 2.7488632e+00 4.2524457e+00 3.6566905e+00 4.4856620e+00 3.7659004e+00 4.1610154e+00 5.0768465e+00 3.4623926e+00 4.6393183e+00 4.4611186e+00 4.7773180e+00 3.6552032e+00 3.9746118e+00 4.1574253e+00 3.9234139e+00 3.9686223e+00 3.9172103e+00 3.7603950e+00 5.1648090e+00 5.6463490e+00 4.1614237e+00 4.3393711e+00 3.5009132e+00 5.2503936e+00 3.7276020e+00 4.0260707e+00 4.3007127e+00 3.5361708e+00 3.3347521e+00 4.1191095e+00 4.2183675e+00 4.7752353e+00 5.1049558e+00 4.1955154e+00 3.4902431e+00 3.7536489e+00 5.1215318e+00 4.0036288e+00 3.6385337e+00 3.2536517e+00 4.1326097e+00 4.3100988e+00 4.1978681e+00 3.6566905e+00 4.3438151e+00 4.3538983e+00 4.1591001e+00 4.0714399e+00 3.8024850e+00 3.7974783e+00 3.3185266e+00 2.0833080e+00 2.8648636e+00 3.5532593e+00 1.6555341e+00 3.5410343e+00 2.0840787e+00 3.3747131e+00 2.3883072e+00 4.3833871e+00 3.9159762e+00 4.2941647e+00 2.3488350e+00 3.6240540e+00 2.9044888e+00 4.0815608e+00 1.5532921e+00 3.6825697e+00 2.4113529e+00 1.7998094e+00 3.2616916e+00 2.5529439e+00 3.3821744e+00 2.6637769e+00 3.9531209e+00 3.1831859e+00 2.5836708e+00 3.1669559e+00 2.2907828e+00 3.8684560e+00 3.0202406e+00 3.4019580e+00 3.1886068e+00 3.4332960e+00 3.7685816e+00 3.8803374e+00 4.1746389e+00 3.3102735e+00 2.2304221e+00 2.1489616e+00 2.0501444e+00 2.6225712e+00 3.4164974e+00 3.0901976e+00 3.9885258e+00 4.0802502e+00 3.0869095e+00 2.9336463e+00 2.3936974e+00 2.5327316e+00 3.4518638e+00 2.5837552e+00 1.5782205e+00 2.6521862e+00 2.9662386e+00 2.9040188e+00 3.2768109e+00 1.6628177e+00 2.7685987e+00 5.0448046e+00 3.5141919e+00 4.9824611e+00 4.0549341e+00 4.5981277e+00 5.5863508e+00 2.6647036e+00 5.0241554e+00 4.1998979e+00 5.9440534e+00 4.4432394e+00 3.9560997e+00 4.6422438e+00 3.4164839e+00 4.0005863e+00 4.6454886e+00 4.2390210e+00 6.5166388e+00 5.6880370e+00 3.1944389e+00 5.0789131e+00 3.4956324e+00 5.5310286e+00 3.6881384e+00 4.9152167e+00 5.0890922e+00 3.6527501e+00 3.7902440e+00 4.2540354e+00 4.7585941e+00 5.0389487e+00 6.4918086e+00 4.3280601e+00 3.6284588e+00 3.4969965e+00 5.6399353e+00 4.9671290e+00 4.2659568e+00 3.6994310e+00 4.7714894e+00 4.8963175e+00 4.8281202e+00 3.5141919e+00 5.0683437e+00 5.1871515e+00 4.6280145e+00 3.7055141e+00 4.2764028e+00 4.7873094e+00 3.7371542e+00 1.1433971e+00 1.6774310e+00 6.8299624e-01 1.6304499e+00 2.4808718e-01 1.5886765e+00 7.6250797e-01 4.0055392e+00 3.4676312e+00 4.0289839e+00 3.2391776e+00 3.6989507e+00 2.9466089e+00 3.5208636e+00 2.4804256e+00 3.6211670e+00 2.6281173e+00 3.2921089e+00 3.0161637e+00 3.5345457e+00 3.2983536e+00 2.5210242e+00 3.6506644e+00 2.9161244e+00 2.7938108e+00 4.0292846e+00 2.8755116e+00 3.4075988e+00 3.0797683e+00 3.8646774e+00 3.2397520e+00 3.3586779e+00 3.5819899e+00 3.9571013e+00 4.0234614e+00 3.2253861e+00 2.6519927e+00 2.9269738e+00 2.8491914e+00 2.8419330e+00 3.6148101e+00 2.8039428e+00 3.2558795e+00 3.7924883e+00 3.8389577e+00 2.6284424e+00 2.9648238e+00 2.9126202e+00 3.2245885e+00 2.9718548e+00 2.6864788e+00 2.8651003e+00 2.6637996e+00 2.7789114e+00 3.1894122e+00 2.3748697e+00 2.8127546e+00 4.6364269e+00 3.7133040e+00 4.8825792e+00 4.0097653e+00 4.4740109e+00 5.5106999e+00 3.1817279e+00 5.0169254e+00 4.6066522e+00 5.3636182e+00 4.0783379e+00 4.1550948e+00 4.5252166e+00 3.8770438e+00 4.0814269e+00 4.3063766e+00 4.0872895e+00 5.8336247e+00 5.9533030e+00 4.0437148e+00 4.7859703e+00 3.5604924e+00 5.6269403e+00 3.8926783e+00 4.4927794e+00 4.7879866e+00 3.7291513e+00 3.6035976e+00 4.3384511e+00 4.6385717e+00 5.1321867e+00 5.8049832e+00 4.4149501e+00 3.6953819e+00 3.8133051e+00 5.5737973e+00 4.4415093e+00 3.9935485e+00 3.5037963e+00 4.5569739e+00 4.6922818e+00 4.6236700e+00 3.7133040e+00 4.7716971e+00 4.8030835e+00 4.5149959e+00 4.1509766e+00 4.1343605e+00 4.2319568e+00 3.5394847e+00 7.6869104e-01 1.2471855e+00 8.7636491e-01 9.9981032e-01 7.8863556e-01 7.0733904e-01 3.2400577e+00 2.7256768e+00 3.3173156e+00 3.2734582e+00 3.1889456e+00 2.6198618e+00 2.7351941e+00 2.7665224e+00 3.0627699e+00 2.5021229e+00 3.6608924e+00 2.4643905e+00 3.5457670e+00 2.8045035e+00 2.1368327e+00 2.9466857e+00 2.4386380e+00 2.5729082e+00 3.8963334e+00 2.8235662e+00 2.7242811e+00 2.6575342e+00 3.5598437e+00 2.8418228e+00 2.8206835e+00 2.9462527e+00 3.4233881e+00 3.3667899e+00 2.7322904e+00 2.5411273e+00 2.9638750e+00 2.9140871e+00 2.5797070e+00 3.2425969e+00 2.3780833e+00 2.4351544e+00 3.0892387e+00 3.6720185e+00 2.1688001e+00 2.8939857e+00 2.7945402e+00 2.6616170e+00 2.7767058e+00 2.9769851e+00 2.6347557e+00 2.1986118e+00 2.3753308e+00 2.6828901e+00 2.5283291e+00 2.4839186e+00 3.8851614e+00 3.3427747e+00 4.1909067e+00 3.4628626e+00 3.8305140e+00 4.8043725e+00 3.1800308e+00 4.3815267e+00 4.2038608e+00 4.4513681e+00 3.3256952e+00 3.6826282e+00 3.8475722e+00 3.6210755e+00 3.6107649e+00 3.5632387e+00 3.4596612e+00 4.8847297e+00 5.3781989e+00 3.9435460e+00 4.0131264e+00 3.1614358e+00 4.9957986e+00 3.4408340e+00 3.7000441e+00 4.0284550e+00 3.2354442e+00 3.0107962e+00 3.8036057e+00 3.9713385e+00 4.5180638e+00 4.8486868e+00 3.8729425e+00 3.2243808e+00 3.5087561e+00 4.8402519e+00 3.6359784e+00 3.3268022e+00 2.9240118e+00 3.8232660e+00 3.9709252e+00 3.8746323e+00 3.3427747e+00 4.0131316e+00 4.0031779e+00 3.8300809e+00 3.7945557e+00 3.4841580e+00 3.4283673e+00 2.9863682e+00 1.9060194e+00 3.1239235e-01 1.5583422e+00 4.8124784e-01 1.2220171e+00 3.3785962e+00 2.9374280e+00 3.5071305e+00 3.8740792e+00 3.5491790e+00 3.0669573e+00 2.9018296e+00 3.4251046e+00 3.3648459e+00 3.0744865e+00 4.3230411e+00 2.8485664e+00 4.1027662e+00 3.1626116e+00 2.6442554e+00 3.1724373e+00 2.8315630e+00 3.0534300e+00 4.4306138e+00 3.3882074e+00 2.9857887e+00 3.0959220e+00 4.0072094e+00 3.2240677e+00 3.1644964e+00 3.2264712e+00 3.7352584e+00 3.6230125e+00 3.1206853e+00 3.1023948e+00 3.5580276e+00 3.5096299e+00 3.0877778e+00 3.6577352e+00 2.7900553e+00 2.5838366e+00 3.3069107e+00 4.1792641e+00 2.5911812e+00 3.4684046e+00 3.3165070e+00 2.9868392e+00 3.2999163e+00 3.6373219e+00 3.1449351e+00 2.5937039e+00 2.8148578e+00 3.0518873e+00 3.1967428e+00 2.9647081e+00 4.0498613e+00 3.7819451e+00 4.3976398e+00 3.7644678e+00 4.0879497e+00 4.9574979e+00 3.7577430e+00 4.5772252e+00 4.5796941e+00 4.4589650e+00 3.5295907e+00 4.0592075e+00 4.0919364e+00 4.1226882e+00 4.0237849e+00 3.7803562e+00 3.7136035e+00 4.7772875e+00 5.6344258e+00 4.4679358e+00 4.1805116e+00 3.6013971e+00 5.1936459e+00 3.8460802e+00 3.8293150e+00 4.1365715e+00 3.6263469e+00 3.3344860e+00 4.1404384e+00 4.1465376e+00 4.7500857e+00 4.7258629e+00 4.2123837e+00 3.5757376e+00 3.9028467e+00 5.0127222e+00 3.7704782e+00 3.5495399e+00 3.2637691e+00 4.0284560e+00 4.1958516e+00 4.1011034e+00 3.7819451e+00 4.1793948e+00 4.1561376e+00 4.1029254e+00 4.2472742e+00 3.7624633e+00 3.5705606e+00 3.3154318e+00 1.8882412e+00 5.3690447e-01 1.7295385e+00 7.4445830e-01 3.5842571e+00 3.0831073e+00 3.5905412e+00 2.6850208e+00 3.1920496e+00 2.4895611e+00 3.1874455e+00 1.9825106e+00 3.1280291e+00 2.1902526e+00 2.7631963e+00 2.5991209e+00 2.9183874e+00 2.8448970e+00 2.0635463e+00 3.2072460e+00 2.5480786e+00 2.2627580e+00 3.4383056e+00 2.3172372e+00 3.0909340e+00 2.5623863e+00 3.3194064e+00 2.7506751e+00 2.8644451e+00 3.1134606e+00 3.4405052e+00 3.5767292e+00 2.7771563e+00 2.0712834e+00 2.3618912e+00 2.2737657e+00 2.3098587e+00 3.1429166e+00 2.4666493e+00 2.9899331e+00 3.3598261e+00 3.2396917e+00 2.2342806e+00 2.4357274e+00 2.4181291e+00 2.7984743e+00 2.4231383e+00 2.1599940e+00 2.3764241e+00 2.2561857e+00 2.3387588e+00 2.7074009e+00 1.8390751e+00 2.3351007e+00 4.3436399e+00 3.2756710e+00 4.4477490e+00 3.5866422e+00 4.0782068e+00 5.0677790e+00 2.7922251e+00 4.5545199e+00 4.0836814e+00 5.0715858e+00 3.7130862e+00 3.6733277e+00 4.0983149e+00 3.4111720e+00 3.6933050e+00 3.9623038e+00 3.6711803e+00 5.5579136e+00 5.4498365e+00 3.4842014e+00 4.4103781e+00 3.1690867e+00 5.1441957e+00 3.3997317e+00 4.1528029e+00 4.3901202e+00 3.2630747e+00 3.2035560e+00 3.8955732e+00 4.1857725e+00 4.6435471e+00 5.5146776e+00 3.9762768e+00 3.2193184e+00 3.3255820e+00 5.1213824e+00 4.1678001e+00 3.6124079e+00 3.1107135e+00 4.1457358e+00 4.3076787e+00 4.2130787e+00 3.2756710e+00 4.4066811e+00 4.4713485e+00 4.0952473e+00 3.6289876e+00 3.7168749e+00 3.9644506e+00 3.1662754e+00 1.5140329e+00 3.3872939e-01 1.1649855e+00 3.5691650e+00 3.1595321e+00 3.7055656e+00 4.0160835e+00 3.7376603e+00 3.2592987e+00 3.1435650e+00 3.5370651e+00 3.5437638e+00 3.2591886e+00 4.4166410e+00 3.0676819e+00 4.2127293e+00 3.3654329e+00 2.8346837e+00 3.3660906e+00 3.0613575e+00 3.2026716e+00 4.5808841e+00 3.5275705e+00 3.2454510e+00 3.2718756e+00 4.1819826e+00 3.4031279e+00 3.3454884e+00 3.4170637e+00 3.9109404e+00 3.8358967e+00 3.3305911e+00 3.2315455e+00 3.6883726e+00 3.6296117e+00 3.2506700e+00 3.8623185e+00 3.0230066e+00 2.8458833e+00 3.5111771e+00 4.3201594e+00 2.8024863e+00 3.6263297e+00 3.4825375e+00 3.1978058e+00 3.4556048e+00 3.7429398e+00 3.3240937e+00 2.7959976e+00 3.0137031e+00 3.2391776e+00 3.3180586e+00 3.1510639e+00 4.3279818e+00 4.0059487e+00 4.6233424e+00 3.9929211e+00 4.3355275e+00 5.1718231e+00 3.9512216e+00 4.7810147e+00 4.7732950e+00 4.7150495e+00 3.7777251e+00 4.2731987e+00 4.3246862e+00 4.3347988e+00 4.2753666e+00 4.0433740e+00 3.9425600e+00 5.0081332e+00 5.8406251e+00 4.6274156e+00 4.4284929e+00 3.8398842e+00 5.3940263e+00 4.0533472e+00 4.0818092e+00 4.3543675e+00 3.8429689e+00 3.5715666e+00 4.3728103e+00 4.3436347e+00 4.9498040e+00 4.9393850e+00 4.4487185e+00 3.7756717e+00 4.0901950e+00 5.2287042e+00 4.0497882e+00 3.7884247e+00 3.5028854e+00 4.2624115e+00 4.4485334e+00 4.3403092e+00 4.0059487e+00 4.4317896e+00 4.4210531e+00 4.3442496e+00 4.4457375e+00 3.9985746e+00 3.8504489e+00 3.5592028e+00 1.4309353e+00 5.3528567e-01 3.7908776e+00 3.2731841e+00 3.8215973e+00 3.1206853e+00 3.5080970e+00 2.7892862e+00 3.3363505e+00 2.4070515e+00 3.4174587e+00 2.5169099e+00 3.2261716e+00 2.8456035e+00 3.3834513e+00 3.1193847e+00 2.3599428e+00 3.4420978e+00 2.7676217e+00 2.6211360e+00 3.8782821e+00 2.7318604e+00 3.2516765e+00 2.8950591e+00 3.6956240e+00 3.0573546e+00 3.1595622e+00 3.3778208e+00 3.7543715e+00 3.8301935e+00 3.0538048e+00 2.4889600e+00 2.7975756e+00 2.7172725e+00 2.6747723e+00 3.4570094e+00 2.6710885e+00 3.0859941e+00 3.5894820e+00 3.6730945e+00 2.4678645e+00 2.8348872e+00 2.7756196e+00 3.0423378e+00 2.8112845e+00 2.6077230e+00 2.7173555e+00 2.4925318e+00 2.6151930e+00 2.9985224e+00 2.2768387e+00 2.6523384e+00 4.4886181e+00 3.5762214e+00 4.6936725e+00 3.8412437e+00 4.3086181e+00 5.3124523e+00 3.1125049e+00 4.8185220e+00 4.4330566e+00 5.1853832e+00 3.9019516e+00 3.9878172e+00 4.3438773e+00 3.7545919e+00 3.9571174e+00 4.1452084e+00 3.9080206e+00 5.6409887e+00 5.7635531e+00 3.9041153e+00 4.6071696e+00 3.4361835e+00 5.4269728e+00 3.7248960e+00 4.3153491e+00 4.5881410e+00 3.5627565e+00 3.4386758e+00 4.1762134e+00 4.4334431e+00 4.9338087e+00 5.6026788e+00 4.2556298e+00 3.5163766e+00 3.6516985e+00 5.3754384e+00 4.2899814e+00 3.8175194e+00 3.3436392e+00 4.3710164e+00 4.5233951e+00 4.4426760e+00 3.5762214e+00 4.5972906e+00 4.6375405e+00 4.3421746e+00 3.9932553e+00 3.9596590e+00 4.0813696e+00 3.3867904e+00 9.9272943e-01 3.3624661e+00 2.9811147e+00 3.5018936e+00 3.8169093e+00 3.5209477e+00 3.0838698e+00 2.9939885e+00 3.3707744e+00 3.3216375e+00 3.1074562e+00 4.2293025e+00 2.8939100e+00 3.9735251e+00 3.1779321e+00 2.6513305e+00 3.1539115e+00 2.9206559e+00 2.9923096e+00 4.3522137e+00 3.3213891e+00 3.1222272e+00 3.0540027e+00 3.9664528e+00 3.2012517e+00 3.1248526e+00 3.2007609e+00 3.6824267e+00 3.6418210e+00 3.1482435e+00 3.0088381e+00 3.4838907e+00 3.4206811e+00 3.0415159e+00 3.6826470e+00 2.9006138e+00 2.7293873e+00 3.3112277e+00 4.0819926e+00 2.6432089e+00 3.4355346e+00 3.3034492e+00 3.0164601e+00 3.2442582e+00 3.5631690e+00 3.1415769e+00 2.6264645e+00 2.8384396e+00 3.0300747e+00 3.1354811e+00 2.9649167e+00 4.2304738e+00 3.8482047e+00 4.4439318e+00 3.8285351e+00 4.1849277e+00 4.9874125e+00 3.8271288e+00 4.5862820e+00 4.5664639e+00 4.6040986e+00 3.6270951e+00 4.0846199e+00 4.1503558e+00 4.1702140e+00 4.1407964e+00 3.9121183e+00 3.7737683e+00 4.8997002e+00 5.6369181e+00 4.4180349e+00 4.2780098e+00 3.7035075e+00 5.1920494e+00 3.8582580e+00 3.9463952e+00 4.1826549e+00 3.6583100e+00 3.4129797e+00 4.2036908e+00 4.1443501e+00 4.7432980e+00 4.8153136e+00 4.2825977e+00 3.5821023e+00 3.9040345e+00 5.0374022e+00 3.9556226e+00 3.6351652e+00 3.3487998e+00 4.0905154e+00 4.2992014e+00 4.1693135e+00 3.8482047e+00 4.2897273e+00 4.2963449e+00 4.1754173e+00 4.2443816e+00 3.8297356e+00 3.7573406e+00 3.4190329e+00 3.4434283e+00 2.9833205e+00 3.5091456e+00 3.1516030e+00 3.2866494e+00 2.6849207e+00 3.0541761e+00 2.5654361e+00 3.1545670e+00 2.5403244e+00 3.3918434e+00 2.6608343e+00 3.3413616e+00 2.9302185e+00 2.2379234e+00 3.1290373e+00 2.6442995e+00 2.5077372e+00 3.8111267e+00 2.7069456e+00 3.0566678e+00 2.7098649e+00 3.5644689e+00 2.8826061e+00 2.9134932e+00 3.0944404e+00 3.4986589e+00 3.5664548e+00 2.8806273e+00 2.4158569e+00 2.8131786e+00 2.7333339e+00 2.5637470e+00 3.3370594e+00 2.5885603e+00 2.8220110e+00 3.2904739e+00 3.5710204e+00 2.3287218e+00 2.8315630e+00 2.7529707e+00 2.8293212e+00 2.7254528e+00 2.7527251e+00 2.6524994e+00 2.3299431e+00 2.4822440e+00 2.7803033e+00 2.3731496e+00 2.5423572e+00 4.2830420e+00 3.4939592e+00 4.4286678e+00 3.6575173e+00 4.1044791e+00 5.0220848e+00 3.2200733e+00 4.5468375e+00 4.2700249e+00 4.8698851e+00 3.6462342e+00 3.8237490e+00 4.0990020e+00 3.7208580e+00 3.8692104e+00 3.9203597e+00 3.6817365e+00 5.2775206e+00 5.5250867e+00 3.8676958e+00 4.3392543e+00 3.3693781e+00 5.1524083e+00 3.5650934e+00 4.0437994e+00 4.2783342e+00 3.3968462e+00 3.2517273e+00 4.0065881e+00 4.1377875e+00 4.6677357e+00 5.2142247e+00 4.0893000e+00 3.3307003e+00 3.5369003e+00 5.0771896e+00 4.0613196e+00 3.5869628e+00 3.1695162e+00 4.1006440e+00 4.2899814e+00 4.1769447e+00 3.4939592e+00 4.3422191e+00 4.3859843e+00 4.1114107e+00 3.8721945e+00 3.7365034e+00 3.8554667e+00 3.2330990e+00 7.4500632e-01 3.1271814e-01 2.7864553e+00 1.1117653e+00 1.8291315e+00 9.1459005e-01 3.2771828e+00 8.5582452e-01 2.5020950e+00 3.7722922e+00 1.4404415e+00 2.6850561e+00 1.2884095e+00 1.9346765e+00 4.6190224e-01 1.7610224e+00 1.9545276e+00 2.5064746e+00 2.4134734e+00 1.4291842e+00 1.4855627e+00 1.8305602e+00 1.4494865e+00 1.0355160e+00 6.8921053e-01 9.5529726e-01 7.2626021e-01 1.4025367e+00 2.2620298e+00 2.6646285e+00 2.6984190e+00 1.9245986e+00 1.7053476e+00 1.9940477e+00 1.3238834e+00 4.4901474e-01 2.2514668e+00 1.7899689e+00 2.4621620e+00 2.3008240e+00 1.1820572e+00 2.0593667e+00 3.3235867e+00 2.0701817e+00 1.6811909e+00 1.7438018e+00 1.2168151e+00 2.9923086e+00 1.8570167e+00 1.8407084e+00 1.9774894e+00 1.2374249e+00 1.3146181e+00 1.4369760e+00 1.6548985e+00 3.0339990e+00 1.3065206e+00 1.8441719e+00 1.9017153e+00 1.0169098e+00 1.5490835e+00 1.1480416e+00 2.3912363e+00 2.1724397e+00 1.4252775e+00 1.0284221e+00 2.2390158e+00 2.3611228e+00 2.6121814e+00 1.3139868e+00 2.0833701e+00 1.8624251e+00 1.5270661e+00 1.1605968e+00 9.3589904e-01 1.4360842e+00 1.2967707e+00 1.5740302e+00 8.5349066e-01 1.4639724e+00 2.1546616e+00 1.6538197e+00 1.2783641e+00 1.8320269e+00 1.7168897e+00 1.7042715e+00 1.0288355e+00 1.3960908e+00 1.0327124e+00 1.4702446e+00 1.2287925e+00 1.9774894e+00 1.3826233e+00 1.6072393e+00 1.3472497e+00 1.9439880e+00 1.1295026e+00 1.6414265e+00 1.5177322e+00 6.8496652e-01 2.3745921e+00 9.3211669e-01 1.2784093e+00 3.1271814e-01 2.7526949e+00 7.8034610e-01 1.8874930e+00 3.3568278e+00 7.7863029e-01 2.4620981e+00 7.9999102e-01 1.3194463e+00 4.5257749e-01 1.0705713e+00 1.5282070e+00 2.3189157e+00 1.9824340e+00 7.4029244e-01 1.0636179e+00 1.6347187e+00 1.0722301e+00 7.4849274e-01 5.3988754e-01 1.0632334e+00 7.0213871e-01 8.4383266e-01 1.8384560e+00 2.2399960e+00 2.2847962e+00 1.4577178e+00 1.3022697e+00 1.2927254e+00 6.8064066e-01 4.4417668e-01 2.0964002e+00 1.1252542e+00 1.9840858e+00 1.8038514e+00 6.0365341e-01 1.6354514e+00 2.8387736e+00 1.5364600e+00 1.0539473e+00 1.1361809e+00 7.8649633e-01 2.4634199e+00 1.2983546e+00 1.5835083e+00 1.4983760e+00 1.4748100e+00 1.0180846e+00 1.2694582e+00 2.0850659e+00 2.4405647e+00 1.6897529e+00 1.8533655e+00 2.0793529e+00 7.4797652e-01 1.3453828e+00 1.1770444e+00 1.9571621e+00 1.6977813e+00 1.1421260e+00 8.3850424e-01 2.6029823e+00 2.7071356e+00 2.3733266e+00 1.3878105e+00 1.5050081e+00 2.3020930e+00 1.2450968e+00 1.1247714e+00 1.3455945e+00 1.0453799e+00 7.4157869e-01 1.3630233e+00 1.3061954e+00 1.8456576e+00 2.6048102e+00 1.4425815e+00 9.9058911e-01 1.5658693e+00 2.1444356e+00 1.4166079e+00 7.2727886e-01 7.9343577e-01 1.1384575e+00 1.4013840e+00 1.2776135e+00 1.4983760e+00 1.4006413e+00 1.5375255e+00 1.2650236e+00 1.7250893e+00 9.0221296e-01 1.2767751e+00 9.2060977e-01 2.5673851e+00 8.6079202e-01 1.6428179e+00 8.7623959e-01 3.1131006e+00 6.6453319e-01 2.3246810e+00 3.5720588e+00 1.2918836e+00 2.4857868e+00 1.0845006e+00 1.8135469e+00 3.9472619e-01 1.6028858e+00 1.8029164e+00 2.2526468e+00 2.2305704e+00 1.2920175e+00 1.3194463e+00 1.5620078e+00 1.2567627e+00 8.7273869e-01 5.3095950e-01 7.1671402e-01 4.2827238e-01 1.2022652e+00 2.1186438e+00 2.4755072e+00 2.5212188e+00 1.7582453e+00 1.4360884e+00 1.8400908e+00 1.3147484e+00 2.6680274e-01 2.0194508e+00 1.6709595e+00 2.2587909e+00 2.1030957e+00 1.0161846e+00 1.8732616e+00 3.1496799e+00 1.8819614e+00 1.5700887e+00 1.5933926e+00 1.0543640e+00 2.8415314e+00 1.6890927e+00 1.6862498e+00 1.7038925e+00 1.0251132e+00 1.0245496e+00 1.1781513e+00 1.5212572e+00 2.8050734e+00 1.1091494e+00 1.5452835e+00 1.9080234e+00 8.5359653e-01 1.2416734e+00 8.9528108e-01 2.1088803e+00 1.9097018e+00 1.2522193e+00 7.4612152e-01 2.3284654e+00 2.1556564e+00 2.3436971e+00 1.1651790e+00 1.8364691e+00 1.6976628e+00 1.2371704e+00 1.0463225e+00 8.5302032e-01 1.1623508e+00 1.0682140e+00 1.2723284e+00 6.7955751e-01 1.2572095e+00 2.2840066e+00 1.3572135e+00 1.0082548e+00 1.5613089e+00 1.5962380e+00 1.5974627e+00 7.9671887e-01 1.1799226e+00 8.3571552e-01 1.2674750e+00 1.0543826e+00 1.7038925e+00 1.2197800e+00 1.4811026e+00 1.1132425e+00 1.6485749e+00 8.6295295e-01 1.5402909e+00 1.2957413e+00 1.7240804e+00 1.2117746e+00 2.5620931e+00 9.4346743e-01 1.9481085e+00 1.0013399e+00 1.0383354e+00 1.7091506e+00 7.5651431e-01 1.6169211e+00 1.4074199e+00 2.3606801e+00 1.6642999e+00 1.0677270e+00 9.5748562e-01 5.4702555e-01 2.2752108e+00 1.3619011e+00 1.2144903e+00 1.4245490e+00 1.7677805e+00 2.1070188e+00 2.0042865e+00 2.3026776e+00 1.5583422e+00 8.7856768e-01 3.6704030e-01 4.8644514e-01 1.0013399e+00 1.3262124e+00 1.6642999e+00 2.6524441e+00 2.3938089e+00 9.9234874e-01 1.6199145e+00 4.6126066e-01 7.3978204e-01 1.8066960e+00 7.9191984e-01 8.2250769e-01 9.3727156e-01 1.6415483e+00 1.4092680e+00 1.6304499e+00 9.1432842e-01 1.1795364e+00 3.1638147e+00 1.4103498e+00 2.9322745e+00 2.0247895e+00 2.5487652e+00 3.5082844e+00 1.0453705e+00 2.9611604e+00 1.9449446e+00 4.1343567e+00 2.6451449e+00 1.7869811e+00 2.6253748e+00 1.1973458e+00 1.9817270e+00 2.7838011e+00 2.2840066e+00 4.7706180e+00 3.4576971e+00 8.9870984e-01 3.1324336e+00 1.5639220e+00 3.4016597e+00 1.5728443e+00 3.0734808e+00 3.1995683e+00 1.6368228e+00 1.9546803e+00 2.1052860e+00 2.8313078e+00 2.9375460e+00 4.8020420e+00 2.1735035e+00 1.6493848e+00 1.3576485e+00 3.5774884e+00 3.2045691e+00 2.3952974e+00 1.8988804e+00 2.8268459e+00 2.8989852e+00 2.8927951e+00 1.4103498e+00 3.1063891e+00 3.2893581e+00 2.6241058e+00 1.4441104e+00 2.3170196e+00 3.0817543e+00 1.9124815e+00 1.0026233e+00 1.1867923e+00 2.3572427e+00 3.6939647e-01 1.6408576e+00 2.7392425e+00 8.8835337e-01 1.6805749e+00 5.5399712e-01 1.2745081e+00 7.5303835e-01 1.1820572e+00 1.1301977e+00 1.4315442e+00 1.4464138e+00 1.2423523e+00 6.4626422e-01 7.5230154e-01 6.2536527e-01 4.0664863e-01 5.0731024e-01 4.0158746e-01 6.2543628e-01 6.4884272e-01 1.4014424e+00 1.6702453e+00 1.7317202e+00 1.0390957e+00 7.1781501e-01 1.4073416e+00 1.5165187e+00 7.3502408e-01 1.2122797e+00 1.2421878e+00 1.4565053e+00 1.3559191e+00 6.8064066e-01 1.0943185e+00 2.3628816e+00 1.1638514e+00 1.1627754e+00 1.0519629e+00 5.3106808e-01 2.1057450e+00 1.0403581e+00 1.9325284e+00 1.0596309e+00 1.3779504e+00 7.6633520e-01 1.2315180e+00 1.9698667e+00 2.0697950e+00 1.4385383e+00 1.0743031e+00 2.5609592e+00 1.1664463e+00 7.0702759e-01 1.1055841e+00 1.3757605e+00 1.4784016e+00 1.4566739e+00 7.9213303e-01 3.1107848e+00 2.2607358e+00 1.5267093e+00 1.6037239e+00 1.2804073e+00 1.9864213e+00 5.4310586e-01 1.5475402e+00 1.5328931e+00 5.4647209e-01 7.9343577e-01 9.7668596e-01 1.1862065e+00 1.4760549e+00 3.1060327e+00 1.0849536e+00 3.7234239e-01 8.8571256e-01 2.0333305e+00 1.9196784e+00 9.5289573e-01 8.6297946e-01 1.2392529e+00 1.4996752e+00 1.3752205e+00 1.0596309e+00 1.6198849e+00 1.8691588e+00 1.2188552e+00 9.2906468e-01 8.7042892e-01 1.8304530e+00 9.8621003e-01 1.4220241e+00 1.5496729e+00 1.1125144e+00 7.4201890e-01 2.1434858e+00 6.0719477e-01 1.5102780e+00 5.6262711e-01 5.7267643e-01 1.3990971e+00 5.4408162e-01 5.2316125e-01 1.5323598e+00 8.2317311e-01 1.1694177e+00 5.6003943e-01 1.0600958e+00 5.1318506e-01 8.8354057e-01 1.1892978e+00 1.3456285e+00 1.4234329e+00 5.0311426e-01 8.2976237e-01 1.0655776e+00 1.1267154e+00 4.4786319e-01 6.7424840e-01 6.4241342e-01 1.4834540e+00 1.4207811e+00 1.3630799e+00 5.2795793e-01 7.8571751e-01 5.3988754e-01 6.8299624e-01 5.6992880e-01 1.6313928e+00 3.1093967e-01 5.0876385e-01 2.8653046e-01 6.5622658e-01 1.3398293e+00 2.2670334e-01 2.2472150e+00 8.9528108e-01 2.1908074e+00 1.1815770e+00 1.7549185e+00 2.8271782e+00 1.2987661e+00 2.2927322e+00 1.7056353e+00 3.1591627e+00 1.6557083e+00 1.2615426e+00 1.8432274e+00 1.1833606e+00 1.4858600e+00 1.8676774e+00 1.3771658e+00 3.7547288e+00 3.1005581e+00 1.4815836e+00 2.2650937e+00 9.5252488e-01 2.8687133e+00 1.0290036e+00 2.0855599e+00 2.2987772e+00 9.0705646e-01 9.6267538e-01 1.4839640e+00 2.0473137e+00 2.3780534e+00 3.7918985e+00 1.5792523e+00 8.4221906e-01 9.2300698e-01 2.9301148e+00 2.2149712e+00 1.3941954e+00 8.9564079e-01 1.9843979e+00 2.0984088e+00 2.1003345e+00 8.9528108e-01 2.2276119e+00 2.3923111e+00 1.8829565e+00 1.3046645e+00 1.4645285e+00 2.0631582e+00 9.0331700e-01 2.9007820e+00 1.0677270e+00 1.9884030e+00 3.5404087e+00 8.9973730e-01 2.7097598e+00 9.8896933e-01 1.4521880e+00 7.3735391e-01 1.1060455e+00 1.7358574e+00 2.5457091e+00 2.1802781e+00 5.9868400e-01 1.3038475e+00 1.8531597e+00 1.2895008e+00 1.0351584e+00 8.4116354e-01 1.3290015e+00 8.7070822e-01 1.0061402e+00 2.0523180e+00 2.4354079e+00 2.4861842e+00 1.6612475e+00 1.4482752e+00 1.3000067e+00 4.4417668e-01 6.8064066e-01 2.3457352e+00 1.2097457e+00 2.1562626e+00 1.9604379e+00 7.8034610e-01 1.8447318e+00 3.0052273e+00 1.6924215e+00 1.1656549e+00 1.2692102e+00 1.0351584e+00 2.6195047e+00 1.4577178e+00 1.3905452e+00 1.5765058e+00 1.5178511e+00 1.0862363e+00 1.2425116e+00 2.1288976e+00 2.5085097e+00 1.7889699e+00 2.0235792e+00 1.9184220e+00 6.6154242e-01 1.4831058e+00 1.2157849e+00 2.0573833e+00 1.6866006e+00 1.0122929e+00 9.0072498e-01 2.4680266e+00 2.8037035e+00 2.5716465e+00 1.3193736e+00 1.5270661e+00 2.3974481e+00 1.4129334e+00 9.9186850e-01 1.3586792e+00 1.1900564e+00 7.8649633e-01 1.4261046e+00 1.4313173e+00 1.9693923e+00 2.5032449e+00 1.4908532e+00 1.1825071e+00 1.7301809e+00 2.1927243e+00 1.1883807e+00 7.0823896e-01 8.2574748e-01 1.1508346e+00 1.3435624e+00 1.2769092e+00 1.5765058e+00 1.3121204e+00 1.3947465e+00 1.2781560e+00 1.8941016e+00 9.4449485e-01 1.0327124e+00 9.1221028e-01 2.4983699e+00 1.0001615e+00 9.3727156e-01 2.0156046e+00 1.4610933e+00 2.0818555e+00 1.4925824e+00 2.8275182e+00 1.8765007e+00 1.3658611e+00 1.8892371e+00 9.4900087e-01 2.5853758e+00 1.8063869e+00 2.0403844e+00 1.9103325e+00 2.2554051e+00 2.6063293e+00 2.6670659e+00 2.8999367e+00 1.9965452e+00 1.0765554e+00 7.8898008e-01 7.5921691e-01 1.3580560e+00 1.9753995e+00 1.7807988e+00 2.8573306e+00 2.8966014e+00 1.8587118e+00 1.7290357e+00 9.4346743e-01 1.0932187e+00 2.1982921e+00 1.2728402e+00 2.6033464e-01 1.2680818e+00 1.7824360e+00 1.6364088e+00 2.0664368e+00 3.9699460e-01 1.4644159e+00 3.6567369e+00 2.0227452e+00 3.6362580e+00 2.6431572e+00 3.1825084e+00 4.2569960e+00 1.1649315e+00 3.7040279e+00 2.8079889e+00 4.6643094e+00 3.1456885e+00 2.5368652e+00 3.2881355e+00 1.9057424e+00 2.5373943e+00 3.2972877e+00 2.8812938e+00 5.2957248e+00 4.3256332e+00 1.8261865e+00 3.7402681e+00 2.0260681e+00 4.2089146e+00 2.2931022e+00 3.6001832e+00 3.8156954e+00 2.2665638e+00 2.4364115e+00 2.8123267e+00 3.5007685e+00 3.7249152e+00 5.3249013e+00 2.8816817e+00 2.2758405e+00 2.0704519e+00 4.3322711e+00 3.6389537e+00 2.9225385e+00 2.3454418e+00 3.4545556e+00 3.5207953e+00 3.5188476e+00 2.0227452e+00 3.7070275e+00 3.8401809e+00 3.2712836e+00 2.2870689e+00 2.9197390e+00 3.4787880e+00 2.3479440e+00 1.8015956e+00 2.9300280e+00 9.4226820e-01 1.8443182e+00 6.2046469e-01 1.3340137e+00 5.0731024e-01 1.2583559e+00 1.1751408e+00 1.7063292e+00 1.5923247e+00 1.2788332e+00 7.3035754e-01 1.0391769e+00 6.6194168e-01 2.9537172e-01 2.8845946e-01 3.7622328e-01 6.2760421e-01 7.7259801e-01 1.4843174e+00 1.8353890e+00 1.8732616e+00 1.1492120e+00 9.8621003e-01 1.4916922e+00 1.4186317e+00 5.4702555e-01 1.4349060e+00 1.2616939e+00 1.6526705e+00 1.5077694e+00 6.5951091e-01 1.2429329e+00 2.5190636e+00 1.3124532e+00 1.1415080e+00 1.1102613e+00 5.1210327e-01 2.2412909e+00 1.1466385e+00 2.0209769e+00 1.3581397e+00 1.4351001e+00 9.3899770e-01 1.3860326e+00 1.9736520e+00 2.3116417e+00 1.4395013e+00 1.3256797e+00 2.5152621e+00 1.1895067e+00 1.0230389e+00 1.2126763e+00 1.7102781e+00 1.7732497e+00 1.5528794e+00 8.7017583e-01 2.9799960e+00 2.3789847e+00 1.8031686e+00 1.6479163e+00 1.5433090e+00 2.0188390e+00 8.9564079e-01 1.5340057e+00 1.4361609e+00 8.5359653e-01 9.3591761e-01 1.2375842e+00 1.0932909e+00 1.5255827e+00 2.9419617e+00 1.3503714e+00 5.7743200e-01 1.0870568e+00 2.0633836e+00 1.9646340e+00 9.8006549e-01 1.0101003e+00 1.2839264e+00 1.6205305e+00 1.4633215e+00 1.3581397e+00 1.6723912e+00 1.9304834e+00 1.3785508e+00 1.2852279e+00 1.0122929e+00 1.8669942e+00 1.1301977e+00 1.7293858e+00 1.1132823e+00 1.5940674e+00 1.2647627e+00 7.0155617e-01 2.0524860e+00 9.1916314e-01 9.0143387e-01 1.7090768e+00 7.7500385e-01 1.6060095e+00 1.1202045e+00 1.5217697e+00 1.2283051e+00 1.5431751e+00 1.8486311e+00 2.0116712e+00 2.0744305e+00 1.1308980e+00 8.6249502e-01 8.7618973e-01 9.4738284e-01 7.7051641e-01 1.2102028e+00 8.1844705e-01 1.9297683e+00 2.0868978e+00 1.6471661e+00 8.6143605e-01 6.0365341e-01 5.7743200e-01 1.3481077e+00 8.0628657e-01 1.1400599e+00 5.2860161e-01 9.6999820e-01 7.8957903e-01 1.3189781e+00 8.0128554e-01 6.6918102e-01 2.6783589e+00 1.1902996e+00 2.8052881e+00 1.7833755e+00 2.2807524e+00 3.4730319e+00 7.8369762e-01 2.9612706e+00 2.2200035e+00 3.7113566e+00 2.2079590e+00 1.7773274e+00 2.4240040e+00 1.2586273e+00 1.6606465e+00 2.3345591e+00 2.0100747e+00 4.3781379e+00 3.6673897e+00 1.6336953e+00 2.8241758e+00 1.1071867e+00 3.5077760e+00 1.5364148e+00 2.6605007e+00 2.9756588e+00 1.4305490e+00 1.5019762e+00 1.9806289e+00 2.7459951e+00 3.0159022e+00 4.4377151e+00 2.0446123e+00 1.5157660e+00 1.4706419e+00 3.5410473e+00 2.6488235e+00 2.0119986e+00 1.3953413e+00 2.5748429e+00 2.6034011e+00 2.6304123e+00 1.1902996e+00 2.7818749e+00 2.8834870e+00 2.3861430e+00 1.6719199e+00 2.0259174e+00 2.4855987e+00 1.3894554e+00 2.6621352e+00 1.3234208e+00 2.6096596e+00 2.2340939e+00 3.3444949e+00 2.5679785e+00 1.9118907e+00 1.7502251e+00 1.3868450e+00 3.2376571e+00 2.3245757e+00 2.2030084e+00 2.3874774e+00 2.7435279e+00 3.0963501e+00 2.9911365e+00 3.3313369e+00 2.5528922e+00 1.6215602e+00 1.1232628e+00 1.1083720e+00 1.9130507e+00 2.3463017e+00 2.5105454e+00 3.5809242e+00 3.3974315e+00 1.8325077e+00 2.4726944e+00 1.3889514e+00 1.6150266e+00 2.7833542e+00 1.7312416e+00 7.0111465e-01 1.8556044e+00 2.5019422e+00 2.3097506e+00 2.6016512e+00 1.2007565e+00 2.0949830e+00 4.1622891e+00 2.3984916e+00 3.9595754e+00 3.0477055e+00 3.5738045e+00 4.5102220e+00 1.5833139e+00 3.9547990e+00 2.8883510e+00 5.1681640e+00 3.6715203e+00 2.8100260e+00 3.6615020e+00 2.1175666e+00 2.9197870e+00 3.8026677e+00 3.3142297e+00 5.7988752e+00 4.3773719e+00 1.6802144e+00 4.1689985e+00 2.5051488e+00 4.3637125e+00 2.6075737e+00 4.1031841e+00 4.2218983e+00 2.6731954e+00 2.9685228e+00 3.1235748e+00 3.8333962e+00 3.9200272e+00 5.8238336e+00 3.1861618e+00 2.6684075e+00 2.3174914e+00 4.5851647e+00 4.2037872e+00 3.4173362e+00 2.9015754e+00 3.8649606e+00 3.9284352e+00 3.9274419e+00 2.3984916e+00 4.1396215e+00 4.3152972e+00 3.6556493e+00 2.4306199e+00 3.3534589e+00 4.0726739e+00 2.9019830e+00 1.9649078e+00 4.5716421e-01 6.0725725e-01 1.0082512e+00 4.0020411e-01 9.6216255e-01 1.8879475e+00 1.3283978e+00 6.9493020e-01 5.9382214e-01 1.3116762e+00 7.1128716e-01 6.9976890e-01 8.6291569e-01 1.2356595e+00 1.0989171e+00 3.1093967e-01 1.2231205e+00 1.5729927e+00 1.6302590e+00 8.2263932e-01 8.7786730e-01 6.2729876e-01 9.5483435e-01 1.0328871e+00 1.7091506e+00 4.5071694e-01 1.2824303e+00 1.1188225e+00 3.5622944e-01 1.0163696e+00 2.1159028e+00 8.2380019e-01 4.6137216e-01 4.2450569e-01 5.0629646e-01 1.7321630e+00 5.8565201e-01 1.8627348e+00 1.0140018e+00 1.9095789e+00 1.0347180e+00 1.4794093e+00 2.5851550e+00 1.6987423e+00 2.1172382e+00 1.7999889e+00 2.6937126e+00 1.1946586e+00 1.2274756e+00 1.5304430e+00 1.4222936e+00 1.3705079e+00 1.4369760e+00 1.1056213e+00 3.3133435e+00 3.0027854e+00 1.9037709e+00 1.8688892e+00 9.6470639e-01 2.7156484e+00 1.0119180e+00 1.6591942e+00 1.9679139e+00 7.8369762e-01 6.0848963e-01 1.3509456e+00 1.8177289e+00 2.2200035e+00 3.3498688e+00 1.4311753e+00 8.4040822e-01 1.2474502e+00 2.6426508e+00 1.7620244e+00 1.0560148e+00 5.3362004e-01 1.6100417e+00 1.7340403e+00 1.6942922e+00 1.0140018e+00 1.8496575e+00 1.9626001e+00 1.5340961e+00 1.4294738e+00 1.1293709e+00 1.5949054e+00 6.4398240e-01 1.7472907e+00 1.7451622e+00 2.3128200e+00 2.0364367e+00 1.1795364e+00 7.5358247e-01 8.5582452e-01 2.5764453e+00 1.4435947e+00 1.1399118e+00 1.4681660e+00 1.7387082e+00 2.0628406e+00 1.8244206e+00 2.2981140e+00 1.7651851e+00 1.0308265e+00 7.7934221e-01 7.7863029e-01 1.2082987e+00 1.5285763e+00 2.1068341e+00 2.8909913e+00 2.3684734e+00 6.2479428e-01 1.9481438e+00 9.9891776e-01 1.1557309e+00 1.9516972e+00 9.8896933e-01 1.2918836e+00 1.3154759e+00 1.9018319e+00 1.7043940e+00 1.6876317e+00 1.4136421e+00 1.4845363e+00 3.4227731e+00 1.7797546e+00 2.8993041e+00 2.1584174e+00 2.6985144e+00 3.3744038e+00 1.7790388e+00 2.8051892e+00 1.8256311e+00 4.2196161e+00 2.7909300e+00 1.8699505e+00 2.6716730e+00 1.6273208e+00 2.3931485e+00 2.9994905e+00 2.3643994e+00 4.7587356e+00 3.2663266e+00 8.6629251e-01 3.2140857e+00 2.0311002e+00 3.1918979e+00 1.6793067e+00 3.1869276e+00 3.1309476e+00 1.8104252e+00 2.1858235e+00 2.2467893e+00 2.6763967e+00 2.7532877e+00 4.7380019e+00 2.3330174e+00 1.6923429e+00 1.4009491e+00 3.4550204e+00 3.4609647e+00 2.5225714e+00 2.1699387e+00 2.8630132e+00 3.0349029e+00 2.9631214e+00 1.7797546e+00 3.2114945e+00 3.4557456e+00 2.7355167e+00 1.5237929e+00 2.4389172e+00 3.3539591e+00 2.2150193e+00 8.7774396e-01 8.7560645e-01 6.6918102e-01 8.5695467e-01 1.6281675e+00 1.2552875e+00 9.0276183e-01 4.7723749e-01 9.6922609e-01 3.4909881e-01 4.4701039e-01 6.6835176e-01 8.7807032e-01 8.7272262e-01 2.1119253e-01 1.2038778e+00 1.5064820e+00 1.5654738e+00 7.8630314e-01 5.8942278e-01 8.9320425e-01 1.1940983e+00 8.6887698e-01 1.4210091e+00 7.4201890e-01 1.2452417e+00 1.0494370e+00 2.3749211e-01 9.1435339e-01 2.1409786e+00 8.2148003e-01 6.5993495e-01 5.7516438e-01 2.8835410e-01 1.8418099e+00 6.4756318e-01 1.8787743e+00 9.0810653e-01 1.6779282e+00 7.7021931e-01 1.3319441e+00 2.3098596e+00 1.7659238e+00 1.7880416e+00 1.4280932e+00 2.6604707e+00 1.1753998e+00 9.4281519e-01 1.3471074e+00 1.3158313e+00 1.3974368e+00 1.4547739e+00 8.7568652e-01 3.2288696e+00 2.6753697e+00 1.6330922e+00 1.7674989e+00 1.0240850e+00 2.3852911e+00 7.4743804e-01 1.5932991e+00 1.7495490e+00 5.8796666e-01 5.8374436e-01 1.1339991e+00 1.5083690e+00 1.8912106e+00 3.2526896e+00 1.2423778e+00 4.2436984e-01 8.5959137e-01 2.4097678e+00 1.8344669e+00 9.0791603e-01 5.8796666e-01 1.4645285e+00 1.6477112e+00 1.6088132e+00 9.0810653e-01 1.7454599e+00 1.9428935e+00 1.4315280e+00 1.1694177e+00 9.9244707e-01 1.7007353e+00 6.6154242e-01 1.4832888e+00 6.1810529e-01 7.1128716e-01 1.8601631e+00 9.7397874e-01 1.2254650e+00 6.8496652e-01 1.4755282e+00 9.0753778e-01 1.0443931e+00 1.3169341e+00 1.6226440e+00 1.6498870e+00 7.4980278e-01 8.0686941e-01 1.1940983e+00 1.2255953e+00 5.6318359e-01 1.1503759e+00 6.6361830e-01 1.4063472e+00 1.5603679e+00 1.6837563e+00 3.6536845e-01 9.5761359e-01 8.4619410e-01 8.6958016e-01 7.7821113e-01 1.6196626e+00 5.7306091e-01 4.4786319e-01 3.6086172e-01 8.2608188e-01 1.1831978e+00 3.8480889e-01 2.4265852e+00 1.2696247e+00 2.4764170e+00 1.5584328e+00 2.0444851e+00 3.1426915e+00 1.4493286e+00 2.6430316e+00 2.1446703e+00 3.2893516e+00 1.7955663e+00 1.6408995e+00 2.1004081e+00 1.5285733e+00 1.7064050e+00 2.0142932e+00 1.6802708e+00 3.9009617e+00 3.4821230e+00 1.8809382e+00 2.4651410e+00 1.1989033e+00 3.2268928e+00 1.3782117e+00 2.2651964e+00 2.5478630e+00 1.2124370e+00 1.1789342e+00 1.8358339e+00 2.3443222e+00 2.7210373e+00 3.9221023e+00 1.9136809e+00 1.2486828e+00 1.4646971e+00 3.1951870e+00 2.3252489e+00 1.6537705e+00 1.0855082e+00 2.1947734e+00 2.3108044e+00 2.2621105e+00 1.2696247e+00 2.4484679e+00 2.5504328e+00 2.0873736e+00 1.6773040e+00 1.7023842e+00 2.1476736e+00 1.1560388e+00 1.3558057e+00 1.5283845e+00 2.1664244e+00 1.9784646e+00 1.1457159e+00 1.0355160e+00 1.4985549e+00 1.0494370e+00 6.0365341e-01 2.6033464e-01 7.3803207e-01 5.6864482e-01 9.7352372e-01 1.8229204e+00 2.2308199e+00 2.2668270e+00 1.4769275e+00 1.3385535e+00 1.5931825e+00 1.1248155e+00 2.1466080e-01 1.9117251e+00 1.3652496e+00 2.0207624e+00 1.8704283e+00 7.6880092e-01 1.6220723e+00 2.8778954e+00 1.6265611e+00 1.2621791e+00 1.3042843e+00 7.7313507e-01 2.5362196e+00 1.4082507e+00 1.8291996e+00 1.6183246e+00 1.3603639e+00 1.0878281e+00 1.3564590e+00 1.9053825e+00 2.6072470e+00 1.4737985e+00 1.6790332e+00 2.1679999e+00 9.4182667e-01 1.2929545e+00 1.1391970e+00 2.0265696e+00 1.8799960e+00 1.3547660e+00 8.8029208e-01 2.6196958e+00 2.4872661e+00 2.2706454e+00 1.4300844e+00 1.7151590e+00 2.0626281e+00 1.1997534e+00 1.2637010e+00 1.2307777e+00 1.0813975e+00 9.6603754e-01 1.3834169e+00 1.0564307e+00 1.5971466e+00 2.5708690e+00 1.4735640e+00 9.4160439e-01 1.5222760e+00 1.9572025e+00 1.7004687e+00 8.9142733e-01 1.0459007e+00 1.1049324e+00 1.4763267e+00 1.2674750e+00 1.6183246e+00 1.4769125e+00 1.6832034e+00 1.2843405e+00 1.6410601e+00 9.6706760e-01 1.5985259e+00 1.1910776e+00 1.0088064e+00 1.9822205e+00 1.3115314e+00 7.2626021e-01 8.5225534e-01 1.4476734e+00 8.6297946e-01 1.0334797e+00 1.2160426e+00 1.5358725e+00 1.3833366e+00 5.3528567e-01 1.2712561e+00 1.5367336e+00 1.6013312e+00 8.9845135e-01 9.1916314e-01 2.4152660e-01 1.0495503e+00 1.3530247e+00 1.8419620e+00 3.4651700e-01 1.2220171e+00 1.0116179e+00 6.2046469e-01 1.0696792e+00 2.0057768e+00 7.5914566e-01 4.4499696e-01 4.0664863e-01 8.1224041e-01 1.6406723e+00 5.8942278e-01 1.9060542e+00 9.6301827e-01 2.1281559e+00 1.1449868e+00 1.6017068e+00 2.8050285e+00 1.4536311e+00 2.3373419e+00 1.9472489e+00 2.8613983e+00 1.3924555e+00 1.3756347e+00 1.7433862e+00 1.3615778e+00 1.3332278e+00 1.5654312e+00 1.2872568e+00 3.4973752e+00 3.1986815e+00 1.9281666e+00 2.0588451e+00 8.3270853e-01 2.9373687e+00 1.1828955e+00 1.8231885e+00 2.1962402e+00 9.5979989e-01 7.5532639e-01 1.4672797e+00 2.0720690e+00 2.4566102e+00 3.5648048e+00 1.5414664e+00 1.0212756e+00 1.2733735e+00 2.8900916e+00 1.8289569e+00 1.2092544e+00 6.4558417e-01 1.8428644e+00 1.8966444e+00 1.9319316e+00 9.6301827e-01 2.0102936e+00 2.1030669e+00 1.7380754e+00 1.5489952e+00 1.3296349e+00 1.6538197e+00 6.3357758e-01 1.4295948e+00 5.4873947e-01 1.6126413e+00 5.8496636e-01 1.1009910e+00 6.0725725e-01 9.5139638e-01 1.3098483e+00 1.3941599e+00 1.6617787e+00 8.6702860e-01 4.2826573e-01 8.0996690e-01 8.1324137e-01 2.8553149e-01 9.9883272e-01 1.0921061e+00 1.8259719e+00 1.6053711e+00 1.1828265e+00 8.3161134e-01 7.0834786e-01 5.3106808e-01 9.8233874e-01 3.5366952e-01 1.4106682e+00 4.6484021e-01 7.5179033e-01 6.2055338e-01 7.8324937e-01 1.1546456e+00 4.7149050e-01 2.7022699e+00 1.3084256e+00 2.4620452e+00 1.5488588e+00 2.1433843e+00 3.0477876e+00 1.5122060e+00 2.4794486e+00 1.8496575e+00 3.5092631e+00 2.0205369e+00 1.5421829e+00 2.1550478e+00 1.4847626e+00 1.9338323e+00 2.2845215e+00 1.7093196e+00 4.0428524e+00 3.2768847e+00 1.4413624e+00 2.6112105e+00 1.4262166e+00 3.0341947e+00 1.2919157e+00 2.4486747e+00 2.5390232e+00 1.2420951e+00 1.3836252e+00 1.8380693e+00 2.2098781e+00 2.5420974e+00 4.0353061e+00 1.9425259e+00 1.0808550e+00 1.0871507e+00 3.1511951e+00 2.6568698e+00 1.7619640e+00 1.3391481e+00 2.2882514e+00 2.4739376e+00 2.4163220e+00 1.3084256e+00 2.5943375e+00 2.7895659e+00 2.2249457e+00 1.4927500e+00 1.8163092e+00 2.5068377e+00 1.3832520e+00 1.1807138e+00 2.3735475e+00 1.4416726e+00 7.3803207e-01 1.4480380e+00 1.6571634e+00 1.9125650e+00 1.5766889e+00 1.9793333e+00 1.6323793e+00 1.4021778e+00 1.1659675e+00 1.2498767e+00 1.3539814e+00 1.2332482e+00 2.0826771e+00 2.7811716e+00 2.1646850e+00 3.7371902e-01 2.0122804e+00 1.1585774e+00 1.3127802e+00 1.8544941e+00 1.1485726e+00 1.7450075e+00 1.3972701e+00 1.9880179e+00 1.7517164e+00 1.6394680e+00 1.8002228e+00 1.5490387e+00 2.9816674e+00 1.3976606e+00 2.4151949e+00 1.7787946e+00 2.2180206e+00 2.8804995e+00 1.7355261e+00 2.3592859e+00 1.2412454e+00 3.7977608e+00 2.4485045e+00 1.3668906e+00 2.2064746e+00 1.1631247e+00 1.9116990e+00 2.5849220e+00 2.0027932e+00 4.3925033e+00 2.6611027e+00 3.7234239e-01 2.7559143e+00 1.7089501e+00 2.6795765e+00 1.2450968e+00 2.8073641e+00 2.7667084e+00 1.4485479e+00 1.9038618e+00 1.7262603e+00 2.3286441e+00 2.2609611e+00 4.4068441e+00 1.7897439e+00 1.4300608e+00 1.1275945e+00 2.9337206e+00 3.0746426e+00 2.2005676e+00 1.9094387e+00 2.4292641e+00 2.5401664e+00 2.4975057e+00 1.3976606e+00 2.7518188e+00 2.9966927e+00 2.2416615e+00 9.2104245e-01 2.0302905e+00 3.0030765e+00 1.9507955e+00 1.9593598e+00 9.5666050e-01 1.1447875e+00 1.0324994e+00 1.3800294e+00 1.7386688e+00 1.7301705e+00 2.0251376e+00 1.2143895e+00 3.6924035e-01 2.6643250e-01 3.1271814e-01 5.3690447e-01 1.1566759e+00 1.3335853e+00 2.2553589e+00 2.0395552e+00 1.0374728e+00 1.1879760e+00 2.9406726e-01 4.0650681e-01 1.4164313e+00 3.6319073e-01 9.3305124e-01 5.5709100e-01 1.1791615e+00 9.8143688e-01 1.2231662e+00 7.9042934e-01 7.5817225e-01 2.9833953e+00 1.3537061e+00 2.7591591e+00 1.8262769e+00 2.3975382e+00 3.3499130e+00 1.2034779e+00 2.7851895e+00 1.9405021e+00 3.8845156e+00 2.3750632e+00 1.6954582e+00 2.4431790e+00 1.3394090e+00 1.9751740e+00 2.5771567e+00 2.0432035e+00 4.4739802e+00 3.4420978e+00 1.1727925e+00 2.9298786e+00 1.4800982e+00 3.2892623e+00 1.4456510e+00 2.8154123e+00 2.9321762e+00 1.4509441e+00 1.6902348e+00 2.0142932e+00 2.5791547e+00 2.8031074e+00 4.4835636e+00 2.1018908e+00 1.3894554e+00 1.2250001e+00 3.4334168e+00 2.9754074e+00 2.1241116e+00 1.6323629e+00 2.6113665e+00 2.7403495e+00 2.7045382e+00 1.3537061e+00 2.9092469e+00 3.0943267e+00 2.4717839e+00 1.4839640e+00 2.1082363e+00 2.8334846e+00 1.6627952e+00 1.2426609e+00 1.7313027e+00 1.2372185e+00 1.1631247e+00 1.1195889e+00 1.5188976e+00 1.0845006e+00 8.2263932e-01 1.9012930e+00 2.1909047e+00 2.2638611e+00 1.4908532e+00 1.2008045e+00 8.7223523e-01 5.7002996e-01 1.0697164e+00 2.2410714e+00 9.6470639e-01 1.8639992e+00 1.6786018e+00 7.4743804e-01 1.6592561e+00 2.7039438e+00 1.4162972e+00 1.0024224e+00 1.0401603e+00 1.0580730e+00 2.3284654e+00 1.2231205e+00 1.2611129e+00 1.1791615e+00 1.6899776e+00 9.5793067e-01 1.1548640e+00 2.3712314e+00 2.0275068e+00 2.0149111e+00 1.9649183e+00 2.1679212e+00 7.8905316e-01 1.3385913e+00 1.3061285e+00 1.6571634e+00 1.2299006e+00 9.3495766e-01 9.4613565e-01 2.8415314e+00 2.9130399e+00 2.3454203e+00 1.4655406e+00 1.0267382e+00 2.6085350e+00 1.2515236e+00 1.1837505e+00 1.7109084e+00 9.9111027e-01 5.2374483e-01 1.2552875e+00 1.7513749e+00 2.1661990e+00 2.9392776e+00 1.3022811e+00 1.1259771e+00 1.5663601e+00 2.4310503e+00 1.1268523e+00 7.5840628e-01 4.7680727e-01 1.3348163e+00 1.3454539e+00 1.4034689e+00 1.1791615e+00 1.4139320e+00 1.4450127e+00 1.2754470e+00 1.6940148e+00 9.2620264e-01 9.4281519e-01 4.9160020e-01 9.3054395e-01 4.1781009e-01 4.6190224e-01 8.0369154e-01 9.6816967e-01 1.1548640e+00 4.6557224e-01 8.2518769e-01 1.2073068e+00 1.2487994e+00 4.5257749e-01 7.8164792e-01 1.0374728e+00 1.4711456e+00 1.1089653e+00 1.1997868e+00 7.6195008e-01 1.0018628e+00 8.9796526e-01 5.8785093e-01 6.0098693e-01 1.8456219e+00 6.5622658e-01 6.9001472e-01 5.4715569e-01 3.1093967e-01 1.5254459e+00 4.8636669e-01 2.2683520e+00 1.0914354e+00 1.9823217e+00 1.1675117e+00 1.6963493e+00 2.6008457e+00 1.7128336e+00 2.0692339e+00 1.5728043e+00 3.0096252e+00 1.5213092e+00 1.1599200e+00 1.6580031e+00 1.3691582e+00 1.6116709e+00 1.8005954e+00 1.2641532e+00 3.5755981e+00 2.8921647e+00 1.5229350e+00 2.1046875e+00 1.2108278e+00 2.6299105e+00 8.9496218e-01 1.9702691e+00 2.0808148e+00 8.0585922e-01 9.4983854e-01 1.4326334e+00 1.7811974e+00 2.1211631e+00 3.5687116e+00 1.5311195e+00 7.1811205e-01 1.0257884e+00 2.6607812e+00 2.2075002e+00 1.3273841e+00 9.2853136e-01 1.7721541e+00 1.9757526e+00 1.8755628e+00 1.0914354e+00 2.1076593e+00 2.2924992e+00 1.7080157e+00 1.2150638e+00 1.3228669e+00 2.0678512e+00 1.0435585e+00 8.3930091e-01 1.0246689e+00 1.2483935e+00 9.2934901e-01 1.2786676e+00 1.0167074e+00 1.2794668e+00 1.2845002e+00 1.3705288e+00 1.0262066e+00 6.1158310e-01 1.6007620e+00 2.1232609e+00 1.4700482e+00 6.0145223e-01 1.5227019e+00 1.1234881e+00 1.1051628e+00 1.1975697e+00 9.1241332e-01 1.9821649e+00 1.0739839e+00 1.4719712e+00 1.2657553e+00 1.0246689e+00 1.8799916e+00 1.1304806e+00 2.3473055e+00 9.3001231e-01 1.7895396e+00 1.0784109e+00 1.5778477e+00 2.3110268e+00 1.7258314e+00 1.7588444e+00 8.0501785e-01 3.1299934e+00 1.7625999e+00 7.4394765e-01 1.5582385e+00 9.7850690e-01 1.4989725e+00 1.9419525e+00 1.2877346e+00 3.7053544e+00 2.3011615e+00 7.8305765e-01 2.1061327e+00 1.2737998e+00 2.1925140e+00 6.0604502e-01 2.1121593e+00 2.0810604e+00 8.0686941e-01 1.2420238e+00 1.1264142e+00 1.6698499e+00 1.7264467e+00 3.7248620e+00 1.2214818e+00 7.0111465e-01 5.3487449e-01 2.3978329e+00 2.4200364e+00 1.4831058e+00 1.2723027e+00 1.7715216e+00 1.9225896e+00 1.8845666e+00 9.3001231e-01 2.0954738e+00 2.3579846e+00 1.6403910e+00 5.2719130e-01 1.3587682e+00 2.3456726e+00 1.3154759e+00 5.0299964e-01 8.2155022e-01 8.8684653e-01 1.0935878e+00 4.8492463e-01 9.8860056e-01 1.2858521e+00 1.3288928e+00 6.2451737e-01 6.2760421e-01 1.0463002e+00 1.4889605e+00 1.0762241e+00 1.1975697e+00 8.4271175e-01 1.0854927e+00 8.7560645e-01 5.3352899e-01 7.0810362e-01 1.9474744e+00 7.1781501e-01 7.2553812e-01 6.1968090e-01 3.6924035e-01 1.6978139e+00 6.0510141e-01 2.1983316e+00 1.0378652e+00 1.8773521e+00 9.9490014e-01 1.5974238e+00 2.4585483e+00 1.7380659e+00 1.8957007e+00 1.4179266e+00 2.9495957e+00 1.4948761e+00 1.0683666e+00 1.5886178e+00 1.3564059e+00 1.6294524e+00 1.7819921e+00 1.1268523e+00 3.4719349e+00 2.7528980e+00 1.4535731e+00 2.0452826e+00 1.2150379e+00 2.4732935e+00 8.6167901e-01 1.8885847e+00 1.9433611e+00 7.9744128e-01 9.1854596e-01 1.3349909e+00 1.6250498e+00 1.9838258e+00 3.4743868e+00 1.4520430e+00 5.1406096e-01 7.3595190e-01 2.5794085e+00 2.1692945e+00 1.1973560e+00 9.2123504e-01 1.7205327e+00 1.9329718e+00 1.8817619e+00 1.0378652e+00 2.0262673e+00 2.2533761e+00 1.7016580e+00 1.1862896e+00 1.2748646e+00 2.0406838e+00 9.6984925e-01 3.6319073e-01 6.1968090e-01 7.8521221e-01 5.6113157e-01 1.2463647e+00 1.6309592e+00 1.6676963e+00 8.9796526e-01 8.9789119e-01 1.2621791e+00 1.3154759e+00 6.8124108e-01 1.3901973e+00 9.9970024e-01 1.4357022e+00 1.2962951e+00 4.8012872e-01 1.0246015e+00 2.2910733e+00 1.0720705e+00 8.8779355e-01 8.4724089e-01 2.4152660e-01 1.9817257e+00 8.8354057e-01 2.0655284e+00 1.2495886e+00 1.6398109e+00 9.9332012e-01 1.4769125e+00 2.2251642e+00 2.1023706e+00 1.7015856e+00 1.4609179e+00 2.6557282e+00 1.2410479e+00 1.0733560e+00 1.3593949e+00 1.6013655e+00 1.6915504e+00 1.5864800e+00 9.7957718e-01 3.1644964e+00 2.6137665e+00 1.7509262e+00 1.7860234e+00 1.3941000e+00 2.2824049e+00 8.7876634e-01 1.6464371e+00 1.6642217e+00 7.8808432e-01 8.5400786e-01 1.3018901e+00 1.3652161e+00 1.7805677e+00 3.1380968e+00 1.4095411e+00 5.8483448e-01 1.0816610e+00 2.2968622e+00 1.9911692e+00 1.0509799e+00 8.9223438e-01 1.4369760e+00 1.7217513e+00 1.5811148e+00 1.2495886e+00 1.8031513e+00 2.0209769e+00 1.4702446e+00 1.2810704e+00 1.0813343e+00 1.8691588e+00 1.0259679e+00 5.6788283e-01 5.3362004e-01 7.7368489e-01 1.6022591e+00 1.9873741e+00 2.0277089e+00 1.2494231e+00 1.1089653e+00 1.4561750e+00 1.2033093e+00 3.3742167e-01 1.6597443e+00 1.2265630e+00 1.7784712e+00 1.6384023e+00 6.1436388e-01 1.3800294e+00 2.6463489e+00 1.4025367e+00 1.1237500e+00 1.1244975e+00 5.5399712e-01 2.3227610e+00 1.2000527e+00 1.8734557e+00 1.4137602e+00 1.3887598e+00 9.6005858e-01 1.3202421e+00 1.9632584e+00 2.3879303e+00 1.4839475e+00 1.4995474e+00 2.3336107e+00 1.0014276e+00 1.1074656e+00 1.1350466e+00 1.8013325e+00 1.7379612e+00 1.3863777e+00 8.2339084e-01 2.8225734e+00 2.4523537e+00 2.0154418e+00 1.5091823e+00 1.5393373e+00 2.0723760e+00 9.8233874e-01 1.3702101e+00 1.3545502e+00 8.7875749e-01 8.4830222e-01 1.2549787e+00 1.1060185e+00 1.5823028e+00 2.7877979e+00 1.3537061e+00 7.2012544e-01 1.2954496e+00 2.0208193e+00 1.7781563e+00 8.8029208e-01 9.2256644e-01 1.1605968e+00 1.4991046e+00 1.3162835e+00 1.4137602e+00 1.5442127e+00 1.7645705e+00 1.2692218e+00 1.4162972e+00 9.1557526e-01 1.6722331e+00 1.0708496e+00 6.2826980e-01 1.0161846e+00 1.6718164e+00 1.9471640e+00 1.9947662e+00 1.3566251e+00 1.0412209e+00 1.7655766e+00 1.7163342e+00 7.1671402e-01 1.3277490e+00 1.5771462e+00 1.7793591e+00 1.6725709e+00 9.6964683e-01 1.3947746e+00 2.6556269e+00 1.5119726e+00 1.4702773e+00 1.3966512e+00 8.2199752e-01 2.4266623e+00 1.3925524e+00 2.0577807e+00 1.4034689e+00 1.2545960e+00 9.4767129e-01 1.3281960e+00 1.7401681e+00 2.4345214e+00 1.1896374e+00 1.0436620e+00 2.5015865e+00 1.2764504e+00 8.9223438e-01 1.1006735e+00 1.6953806e+00 1.7900496e+00 1.5985782e+00 8.8098199e-01 2.9595238e+00 2.0497239e+00 1.6965355e+00 1.5863720e+00 1.6496643e+00 1.7201711e+00 8.3409556e-01 1.5639220e+00 1.3496867e+00 8.9427872e-01 1.0978602e+00 1.1314785e+00 9.1432842e-01 1.2235673e+00 2.9196060e+00 1.2400775e+00 6.4083823e-01 1.0643984e+00 1.8241883e+00 2.0498818e+00 1.0696576e+00 1.1919906e+00 1.2042673e+00 1.5543054e+00 1.3831011e+00 1.4034689e+00 1.6218749e+00 1.9188761e+00 1.2920921e+00 1.1337565e+00 1.0067405e+00 1.9865217e+00 1.3029486e+00 9.5748562e-01 1.9681165e+00 2.2573397e+00 2.3235953e+00 1.5741400e+00 1.1016806e+00 1.6166760e+00 1.2896217e+00 3.8830315e-01 1.7972266e+00 1.5164233e+00 2.0064286e+00 1.8697578e+00 8.5494999e-01 1.6681709e+00 2.9309853e+00 1.6503473e+00 1.4467905e+00 1.4113341e+00 9.2189946e-01 2.6393526e+00 1.4852749e+00 1.4601858e+00 1.3160132e+00 8.7649478e-01 6.4756318e-01 8.3256255e-01 1.5094087e+00 2.4769347e+00 1.0668784e+00 1.2459961e+00 1.9408738e+00 6.5485710e-01 8.4116354e-01 6.0795234e-01 1.7154199e+00 1.4961901e+00 9.9551062e-01 3.9472619e-01 2.4940166e+00 2.0216642e+00 2.0463301e+00 1.0230389e+00 1.4612117e+00 1.6595118e+00 8.5582452e-01 9.5437259e-01 9.5694342e-01 7.7934221e-01 7.3851064e-01 8.5695467e-01 7.6638235e-01 1.1767396e+00 2.5076596e+00 9.4281519e-01 7.1971771e-01 1.2830542e+00 1.5700842e+00 1.4287578e+00 5.3095950e-01 8.6291569e-01 6.6154242e-01 1.0050638e+00 8.5606908e-01 1.3160132e+00 1.0514970e+00 1.3171874e+00 7.9433323e-01 1.2774110e+00 4.7509249e-01 1.3730080e+00 9.7659801e-01 1.1663747e+00 1.4582411e+00 1.5261646e+00 7.3570584e-01 5.8785093e-01 7.6039875e-01 1.1605726e+00 9.6964683e-01 1.4553344e+00 6.3765570e-01 1.1681709e+00 1.0005243e+00 2.9691107e-01 8.7856768e-01 2.0651943e+00 7.3735391e-01 6.0653347e-01 4.7837533e-01 3.7398306e-01 1.7406274e+00 5.5183182e-01 1.8498714e+00 8.1329726e-01 1.7508641e+00 8.2125107e-01 1.3423724e+00 2.4127395e+00 1.6384023e+00 1.9130927e+00 1.5043381e+00 2.6917823e+00 1.1782159e+00 9.6249568e-01 1.3877621e+00 1.2214135e+00 1.2719770e+00 1.4200371e+00 9.4526659e-01 3.3044115e+00 2.7645066e+00 1.6390945e+00 1.7948322e+00 8.7588404e-01 2.5003659e+00 7.4157869e-01 1.6267504e+00 1.8590427e+00 5.4310586e-01 5.2316125e-01 1.1372412e+00 1.6472029e+00 2.0021586e+00 3.3409572e+00 1.2314733e+00 5.4779717e-01 9.4822835e-01 2.4877874e+00 1.8001237e+00 9.6012811e-01 4.8644514e-01 1.5074309e+00 1.6452353e+00 1.6151033e+00 8.1329726e-01 1.7721541e+00 1.9352496e+00 1.4226963e+00 1.1564263e+00 1.0022114e+00 1.6574535e+00 5.8132667e-01 5.6318359e-01 5.3286499e-01 4.3341454e-01 1.2747045e+00 1.3163443e+00 2.1153648e+00 1.9183153e+00 1.1909838e+00 1.0657373e+00 5.8852220e-01 6.2225308e-01 1.3220343e+00 4.0443437e-01 1.0982562e+00 6.1619693e-01 1.0378442e+00 8.8917809e-01 1.0970024e+00 8.2199752e-01 6.9493020e-01 3.0003610e+00 1.5102474e+00 2.7635526e+00 1.8759428e+00 2.4405125e+00 3.3586044e+00 1.4659783e+00 2.7980993e+00 2.0759743e+00 3.8255754e+00 2.3211022e+00 1.7886572e+00 2.4450156e+00 1.5788967e+00 2.1011800e+00 2.5635734e+00 2.0416027e+00 4.3880097e+00 3.5282390e+00 1.4609179e+00 2.9105517e+00 1.6043433e+00 3.3245307e+00 1.5187698e+00 2.7743367e+00 2.8814354e+00 1.4896588e+00 1.6771528e+00 2.1027324e+00 2.5396337e+00 2.8265966e+00 4.3745133e+00 2.1946278e+00 1.4104380e+00 1.3873057e+00 3.4289479e+00 2.9514502e+00 2.1043046e+00 1.6198849e+00 2.5820471e+00 2.7513626e+00 2.6746677e+00 1.5102474e+00 2.9036877e+00 3.0793854e+00 2.4777911e+00 1.6406409e+00 2.1046875e+00 2.7982275e+00 1.6824284e+00 1.4276574e-01 7.9394533e-01 1.3473710e+00 1.5367336e+00 2.5040512e+00 2.2893871e+00 1.0820670e+00 1.4237364e+00 3.6704030e-01 5.8785093e-01 1.6733127e+00 6.1158310e-01 7.1781501e-01 7.8318003e-01 1.4288989e+00 1.2280749e+00 1.4809953e+00 7.0150436e-01 1.0034788e+00 3.1877520e+00 1.5005648e+00 2.9634183e+00 2.0359721e+00 2.5953129e+00 3.5470571e+00 1.1634940e+00 2.9839272e+00 2.0686806e+00 4.1156592e+00 2.6069476e+00 1.8659064e+00 2.6504363e+00 1.4017266e+00 2.1040122e+00 2.7893850e+00 2.2677890e+00 4.7183507e+00 3.5819899e+00 1.1465705e+00 3.1455771e+00 1.6263647e+00 3.4643576e+00 1.6254447e+00 3.0471395e+00 3.1646326e+00 1.6517237e+00 1.9156890e+00 2.1886203e+00 2.8006555e+00 2.9856138e+00 4.7315684e+00 2.2694993e+00 1.6130651e+00 1.3903392e+00 3.6256164e+00 3.1929372e+00 2.3573820e+00 1.8552594e+00 2.8291427e+00 2.9408913e+00 2.9120555e+00 1.5005648e+00 3.1237582e+00 3.3065647e+00 2.6677639e+00 1.5962380e+00 2.3224070e+00 3.0542458e+00 1.8794605e+00 8.3156200e-01 1.4460310e+00 1.6013312e+00 2.5509456e+00 2.3359909e+00 1.1395071e+00 1.4612871e+00 4.8644514e-01 6.6244727e-01 1.7247525e+00 6.6453319e-01 6.8496652e-01 8.5330525e-01 1.4567673e+00 1.2739414e+00 1.5213580e+00 6.7904052e-01 1.0560797e+00 3.2869799e+00 1.6218234e+00 3.0463976e+00 2.1264009e+00 2.6948572e+00 3.6228821e+00 1.2747978e+00 3.0537173e+00 2.1607143e+00 4.1937512e+00 2.6849827e+00 1.9680122e+00 2.7382122e+00 1.5399252e+00 2.2309601e+00 2.8826191e+00 2.3479440e+00 4.7798805e+00 3.6690959e+00 1.2447718e+00 3.2325186e+00 1.7450415e+00 3.5380106e+00 1.7243835e+00 3.1258440e+00 3.2275370e+00 1.7473394e+00 2.0003230e+00 2.2955340e+00 2.8573132e+00 3.0588804e+00 4.7837445e+00 2.3799967e+00 1.6861899e+00 1.4737985e+00 3.7047689e+00 3.2828775e+00 2.4345890e+00 1.9408738e+00 2.9104325e+00 3.0383020e+00 2.9993404e+00 1.6218234e+00 3.2132904e+00 3.3994957e+00 2.7639400e+00 1.7088499e+00 2.4110070e+00 3.1406474e+00 1.9689207e+00 8.9196595e-01 9.9107019e-01 1.7478610e+00 1.5467508e+00 1.1459117e+00 7.5303835e-01 6.0365341e-01 5.1453676e-01 9.1435339e-01 2.3749211e-01 1.4031123e+00 3.2313211e-01 7.2263841e-01 5.2290002e-01 7.1740234e-01 1.0975976e+00 3.1271814e-01 2.5686027e+00 1.1418735e+00 2.3704417e+00 1.4573208e+00 2.0173981e+00 2.9893605e+00 1.3924555e+00 2.4418079e+00 1.7809408e+00 3.4092863e+00 1.8988911e+00 1.4127713e+00 2.0371950e+00 1.3095379e+00 1.7286430e+00 2.1358640e+00 1.6228818e+00 3.9920035e+00 3.2072460e+00 1.3897098e+00 2.4925270e+00 1.2375842e+00 2.9891693e+00 1.1418958e+00 2.3510928e+00 2.4945648e+00 1.0792736e+00 1.2447083e+00 1.7021399e+00 2.1843621e+00 2.4864921e+00 3.9967418e+00 1.7954132e+00 1.0172824e+00 1.0869385e+00 3.0619758e+00 2.5242226e+00 1.6782397e+00 1.1896845e+00 2.1746675e+00 2.3309032e+00 2.2706367e+00 1.1418735e+00 2.4800313e+00 2.6530340e+00 2.0689155e+00 1.3443777e+00 1.6837594e+00 2.3748600e+00 1.2545769e+00 1.0660846e+00 1.6498377e+00 1.2783641e+00 1.1376397e+00 1.0898613e+00 1.0585628e+00 9.2189946e-01 8.0142393e-01 8.8029208e-01 1.9920533e+00 8.0501785e-01 1.0699859e+00 8.7105035e-01 7.9448303e-01 1.7999592e+00 8.1251986e-01 1.9227723e+00 4.6137216e-01 1.6965186e+00 7.0213871e-01 1.2723284e+00 2.3160615e+00 1.4526546e+00 1.7912701e+00 1.0739839e+00 2.8496420e+00 1.4032361e+00 6.3302304e-01 1.3757605e+00 7.8863556e-01 1.1001816e+00 1.5547583e+00 9.8152003e-01 3.4772360e+00 2.4799120e+00 1.1619556e+00 1.8622587e+00 7.5769247e-01 2.3162319e+00 4.6128322e-01 1.7816685e+00 1.9387331e+00 4.5729032e-01 7.5817225e-01 8.9223438e-01 1.6541379e+00 1.8404767e+00 3.5381276e+00 9.9244707e-01 4.4901474e-01 4.6557224e-01 2.4199101e+00 1.9790803e+00 1.0974789e+00 7.5914566e-01 1.5781281e+00 1.6567524e+00 1.6951864e+00 4.6137216e-01 1.8193470e+00 2.0336808e+00 1.4275349e+00 7.0834786e-01 1.0588854e+00 1.8801322e+00 7.4965096e-01 1.1803522e+00 1.5908164e+00 1.9645622e+00 4.2238505e-01 1.2220171e+00 1.0116179e+00 8.5731385e-01 1.1485726e+00 1.9317000e+00 7.9664122e-01 5.6097460e-01 5.3106808e-01 1.0334797e+00 1.5679493e+00 6.8124108e-01 2.0247774e+00 1.0499569e+00 2.3371797e+00 1.3332950e+00 1.7744903e+00 3.0154970e+00 1.3277924e+00 2.5520970e+00 2.1193865e+00 3.0297355e+00 1.5881698e+00 1.5547948e+00 1.9487821e+00 1.4037678e+00 1.3973195e+00 1.7249900e+00 1.4967902e+00 3.6762761e+00 3.3933664e+00 2.0023741e+00 2.2484516e+00 8.6702860e-01 3.1482546e+00 1.3659877e+00 2.0060004e+00 2.4114658e+00 1.1530661e+00 9.5944179e-01 1.6364369e+00 2.2989490e+00 2.6726954e+00 3.7560452e+00 1.7032717e+00 1.2286922e+00 1.4040978e+00 3.1041910e+00 1.9523740e+00 1.4097206e+00 8.4169735e-01 2.0525205e+00 2.0729884e+00 2.1328505e+00 1.0499569e+00 2.1908074e+00 2.2633850e+00 1.9289706e+00 1.6929462e+00 1.5333884e+00 1.7729821e+00 7.9671887e-01 1.1060455e+00 2.5932658e+00 1.1359179e+00 2.2153658e+00 2.0116430e+00 9.6825676e-01 1.9538525e+00 2.9958431e+00 1.7387082e+00 1.1339874e+00 1.2823616e+00 1.2471855e+00 2.5771468e+00 1.5006766e+00 1.5158960e+00 1.7131342e+00 1.9169015e+00 1.3850497e+00 1.5416258e+00 2.5358032e+00 2.4678381e+00 2.2144600e+00 2.3737220e+00 2.1274158e+00 9.8372339e-01 1.7887913e+00 1.5921275e+00 2.1896791e+00 1.7854129e+00 1.2218858e+00 1.2670969e+00 2.6904561e+00 3.2109839e+00 2.7851183e+00 1.6425353e+00 1.5729927e+00 2.8211634e+00 1.6904600e+00 1.2882518e+00 1.7618849e+00 1.4390193e+00 9.9282597e-01 1.7222401e+00 1.8692144e+00 2.3979391e+00 2.7477433e+00 1.7762263e+00 1.4761145e+00 1.9687854e+00 2.5941073e+00 1.2723200e+00 1.0497372e+00 9.7397874e-01 1.5327853e+00 1.6373339e+00 1.6177026e+00 1.7131342e+00 1.6177237e+00 1.6189835e+00 1.6013655e+00 2.1620604e+00 1.2836487e+00 1.0769609e+00 1.0246689e+00 1.9326437e+00 1.4149714e+00 2.0593667e+00 1.9008579e+00 7.7368489e-01 1.6801694e+00 2.9457974e+00 1.6627284e+00 1.3215146e+00 1.3491191e+00 8.3512263e-01 2.6175043e+00 1.4565053e+00 1.6449760e+00 1.5357227e+00 1.1692720e+00 9.2780124e-01 1.1582405e+00 1.7355630e+00 2.5925902e+00 1.3094338e+00 1.5678176e+00 2.0102053e+00 7.6952423e-01 1.1716038e+00 9.4417605e-01 1.9573929e+00 1.7612938e+00 1.1827746e+00 6.8675486e-01 2.4881500e+00 2.3327432e+00 2.2476505e+00 1.2375842e+00 1.6387331e+00 1.9108109e+00 1.1188225e+00 1.0733560e+00 1.0560148e+00 1.0005243e+00 8.6347207e-01 1.2199459e+00 9.0753778e-01 1.4483156e+00 2.4625089e+00 1.3082342e+00 8.7375509e-01 1.4601811e+00 1.8000065e+00 1.5372053e+00 7.0097130e-01 9.6204815e-01 9.1315231e-01 1.2848917e+00 1.0993651e+00 1.5357227e+00 1.2764003e+00 1.5003224e+00 1.1101208e+00 1.5658291e+00 7.8808432e-01 1.4489917e+00 1.0920053e+00 1.8302572e+00 1.0943114e+00 1.1955102e+00 1.6415483e+00 9.5491981e-01 1.7343175e+00 1.2563834e+00 1.7780218e+00 1.5661153e+00 1.3901973e+00 1.7352481e+00 1.3724737e+00 2.9349297e+00 1.4110819e+00 2.3154474e+00 1.6753059e+00 2.1644875e+00 2.7793057e+00 1.8300579e+00 2.2275691e+00 1.2267563e+00 3.6839155e+00 2.3163493e+00 1.3205795e+00 2.1115079e+00 1.3018219e+00 1.9822480e+00 2.5100153e+00 1.8661672e+00 4.2327578e+00 2.6573893e+00 6.0725725e-01 2.6633209e+00 1.7222058e+00 2.5939799e+00 1.1664463e+00 2.6821825e+00 2.5963941e+00 1.3509199e+00 1.7816323e+00 1.7046308e+00 2.1381589e+00 2.1542571e+00 4.2222578e+00 1.7881987e+00 1.2473306e+00 1.0083490e+00 2.8478148e+00 2.9960208e+00 2.0583207e+00 1.7939407e+00 2.3128527e+00 2.4854841e+00 2.4090630e+00 1.4110819e+00 2.6669716e+00 2.9270626e+00 2.1822548e+00 9.7289027e-01 1.9265423e+00 2.9135583e+00 1.8510296e+00 1.1608422e+00 9.5483435e-01 6.7975587e-01 9.6424206e-01 1.8685422e+00 6.9420840e-01 1.8699153e-01 2.6643250e-01 7.6880092e-01 1.4668684e+00 4.7680727e-01 2.1966727e+00 1.2150638e+00 2.3282955e+00 1.3855601e+00 1.8709248e+00 2.9899796e+00 1.5396352e+00 2.5003659e+00 2.1099656e+00 3.0668593e+00 1.5989333e+00 1.5788417e+00 1.9566602e+00 1.5639220e+00 1.6339738e+00 1.8236402e+00 1.4967013e+00 3.6603010e+00 3.3937895e+00 1.9915789e+00 2.2840141e+00 1.1223478e+00 3.1075626e+00 1.3520885e+00 2.0407191e+00 2.3526669e+00 1.1508346e+00 9.9970980e-01 1.7227103e+00 2.1946041e+00 2.6155078e+00 3.6958832e+00 1.8054416e+00 1.1477199e+00 1.3984076e+00 3.0713671e+00 2.0894840e+00 1.4301682e+00 9.0552938e-01 2.0395038e+00 2.1489811e+00 2.1344915e+00 1.2150638e+00 2.2517887e+00 2.3533226e+00 1.9673072e+00 1.7095802e+00 1.5528301e+00 1.9068051e+00 9.3899770e-01 3.4893361e-01 1.4098163e+00 4.4901474e-01 9.4301660e-01 4.9009568e-01 1.1908452e+00 9.6032771e-01 1.2627589e+00 7.8945238e-01 7.3502408e-01 2.8451486e+00 1.1622402e+00 2.7058576e+00 1.7424022e+00 2.2846522e+00 3.3213689e+00 9.3810350e-01 2.7757276e+00 1.8894571e+00 3.8131048e+00 2.3010216e+00 1.5984421e+00 2.3698153e+00 1.1049324e+00 1.7539088e+00 2.4591578e+00 1.9849730e+00 4.4474865e+00 3.3956070e+00 1.1104964e+00 2.8478148e+00 1.2628565e+00 3.2741783e+00 1.3548265e+00 2.7443454e+00 2.9214972e+00 1.3520885e+00 1.5950569e+00 1.8924015e+00 2.5961001e+00 2.7889301e+00 4.4811770e+00 1.9680122e+00 1.3672690e+00 1.1906665e+00 3.3943858e+00 2.8533575e+00 2.0610968e+00 1.5261646e+00 2.5498486e+00 2.6295980e+00 2.6227721e+00 1.1622402e+00 2.8191421e+00 2.9841287e+00 2.3684093e+00 1.3684638e+00 2.0228721e+00 2.7146999e+00 1.5430544e+00 1.2073068e+00 4.2737382e-01 1.1404637e+00 3.1271814e-01 9.6032771e-01 7.5303835e-01 1.1016806e+00 9.6606527e-01 5.6318359e-01 2.6951278e+00 1.0877340e+00 2.5880472e+00 1.5788417e+00 2.1577755e+00 3.1981141e+00 1.0053189e+00 2.6422640e+00 1.8441670e+00 3.6556493e+00 2.1516272e+00 1.5283932e+00 2.2572409e+00 1.1515368e+00 1.7244934e+00 2.3310687e+00 1.8210464e+00 4.2584241e+00 3.3382181e+00 1.2189366e+00 2.7191331e+00 1.1859692e+00 3.1732334e+00 1.2980649e+00 2.5768210e+00 2.7513616e+00 1.2636762e+00 1.4403062e+00 1.8020431e+00 2.4433699e+00 2.6920515e+00 4.2945779e+00 1.8903935e+00 1.2074964e+00 1.0277379e+00 3.3038558e+00 2.6967685e+00 1.8755883e+00 1.3730080e+00 2.4290237e+00 2.5228632e+00 2.5343900e+00 1.0877340e+00 2.6795155e+00 2.8549884e+00 2.2878474e+00 1.3941000e+00 1.9010194e+00 2.5529515e+00 1.3637808e+00 1.0801003e+00 2.2778358e+00 9.5491981e-01 5.9448670e-01 5.9589853e-01 3.3742167e-01 1.9403546e+00 7.3727571e-01 1.8011631e+00 1.0580730e+00 1.6859882e+00 8.4110582e-01 1.3396881e+00 2.3254107e+00 1.8940865e+00 1.8320164e+00 1.6099822e+00 2.5455416e+00 1.0698235e+00 1.0938968e+00 1.3476330e+00 1.4941922e+00 1.4633215e+00 1.3755629e+00 8.7649478e-01 3.1069376e+00 2.7702855e+00 1.8674396e+00 1.7104256e+00 1.1065179e+00 2.4455843e+00 9.1688392e-01 1.4945651e+00 1.6975565e+00 7.1757390e-01 5.5102439e-01 1.2274184e+00 1.5152116e+00 1.9568855e+00 3.1286065e+00 1.3281960e+00 6.0709980e-01 1.0827099e+00 2.4180452e+00 1.7168537e+00 8.4814328e-01 5.4968254e-01 1.4259927e+00 1.6175327e+00 1.5676792e+00 1.0580730e+00 1.6914438e+00 1.8627823e+00 1.4252775e+00 1.3670172e+00 9.8340962e-01 1.5690664e+00 6.4292875e-01 1.2799022e+00 3.7622328e-01 9.3727156e-01 7.2340544e-01 8.7070822e-01 1.0517510e+00 4.9772204e-01 2.6753497e+00 1.1327780e+00 2.4219935e+00 1.5112031e+00 2.0792734e+00 3.0229728e+00 1.3206164e+00 2.4652397e+00 1.7009790e+00 3.5349296e+00 2.0290396e+00 1.4008516e+00 2.1030738e+00 1.2197800e+00 1.7532536e+00 2.2495068e+00 1.7048463e+00 4.1210195e+00 3.1691829e+00 1.1769133e+00 2.5856062e+00 1.2767751e+00 2.9863081e+00 1.1384575e+00 2.4711712e+00 2.5838439e+00 1.1268523e+00 1.3640383e+00 1.7178039e+00 2.2416335e+00 2.4908014e+00 4.1279448e+00 1.8102703e+00 1.0585628e+00 1.0110609e+00 3.0999848e+00 2.6577347e+00 1.7876312e+00 1.3164631e+00 2.2615790e+00 2.4095273e+00 2.3580977e+00 1.1327780e+00 2.5710650e+00 2.7600070e+00 2.1383264e+00 1.2571099e+00 1.7683536e+00 2.5188615e+00 1.3683626e+00 1.3381984e+00 1.9104439e+00 1.7447553e+00 2.1191178e+00 5.2290002e-01 1.5506355e+00 3.7401310e+00 2.0532672e+00 3.6450987e+00 2.6791066e+00 3.2198971e+00 4.2474168e+00 1.2374249e+00 3.6904578e+00 2.7448024e+00 4.7359552e+00 3.2167523e+00 2.5268732e+00 3.3111493e+00 1.8901504e+00 2.5824715e+00 3.3694826e+00 2.9225385e+00 5.3651760e+00 4.2632085e+00 1.6938497e+00 3.7848480e+00 2.0962051e+00 4.1703198e+00 2.2884248e+00 3.6689921e+00 3.8480511e+00 2.2915999e+00 2.5084155e+00 2.8222270e+00 3.5057930e+00 3.6931154e+00 5.3885667e+00 2.8913445e+00 2.2944283e+00 2.0536056e+00 4.3194837e+00 3.7370068e+00 2.9859935e+00 2.4261724e+00 3.4875553e+00 3.5613793e+00 3.5512500e+00 2.0532672e+00 3.7558873e+00 3.9047630e+00 3.2988390e+00 2.2352837e+00 2.9604482e+00 3.5852990e+00 2.4345890e+00 7.1446962e-01 4.7680727e-01 8.6080744e-01 1.0528937e+00 2.6643250e-01 2.4784392e+00 9.6779954e-01 2.4056700e+00 1.4093245e+00 1.9680122e+00 3.0432377e+00 1.1095018e+00 2.5046513e+00 1.7969297e+00 3.4167115e+00 1.9006720e+00 1.3928921e+00 2.0543866e+00 1.1288261e+00 1.5650139e+00 2.0901630e+00 1.6223748e+00 4.0330923e+00 3.2470423e+00 1.3554912e+00 2.4968262e+00 1.0256272e+00 3.0550867e+00 1.1407226e+00 2.3454418e+00 2.5560105e+00 1.0597600e+00 1.1958054e+00 1.6477280e+00 2.2779375e+00 2.5604758e+00 4.0677826e+00 1.7340403e+00 1.0472149e+00 1.0317636e+00 3.1283758e+00 2.4552133e+00 1.6602736e+00 1.1211328e+00 2.2084219e+00 2.3071243e+00 2.3006257e+00 9.6779954e-01 2.4647768e+00 2.6219630e+00 2.0691920e+00 1.3232765e+00 1.6800415e+00 2.3045354e+00 1.1399118e+00 2.6525508e-01 6.6194168e-01 1.5279052e+00 4.8284931e-01 2.2240009e+00 1.2628565e+00 2.2754107e+00 1.3512603e+00 1.8635082e+00 2.9164540e+00 1.6496295e+00 2.4127395e+00 2.0563471e+00 3.0426144e+00 1.5827764e+00 1.5566996e+00 1.9230842e+00 1.6230251e+00 1.7204639e+00 1.8421714e+00 1.4471806e+00 3.6003163e+00 3.3322323e+00 1.9737130e+00 2.2612204e+00 1.2180372e+00 3.0253495e+00 1.3338820e+00 2.0126070e+00 2.2700141e+00 1.1450371e+00 1.0044165e+00 1.7168897e+00 2.0924575e+00 2.5354258e+00 3.6216411e+00 1.8094028e+00 1.0735412e+00 1.3351581e+00 3.0117528e+00 2.1161544e+00 1.3888000e+00 9.3005809e-01 2.0016409e+00 2.1479330e+00 2.1191972e+00 1.2628565e+00 2.2323235e+00 2.3582915e+00 1.9639632e+00 1.7034312e+00 1.5340961e+00 1.9379753e+00 9.6779954e-01 6.0647055e-01 1.3810470e+00 2.3749211e-01 2.2111682e+00 1.0514970e+00 2.2223402e+00 1.2585091e+00 1.7887495e+00 2.8752401e+00 1.4450035e+00 2.3620441e+00 1.8870562e+00 3.0854059e+00 1.5854288e+00 1.3907384e+00 1.8599878e+00 1.3776606e+00 1.5509730e+00 1.8163092e+00 1.3995189e+00 3.6797126e+00 3.2203837e+00 1.7354649e+00 2.2402012e+00 1.0327124e+00 2.9556082e+00 1.1508346e+00 2.0324938e+00 2.2869296e+00 9.8115739e-01 9.3443769e-01 1.5799528e+00 2.0763861e+00 2.4587811e+00 3.7098479e+00 1.6697722e+00 9.5240225e-01 1.1656779e+00 2.9602833e+00 2.1358640e+00 1.3782117e+00 8.5400786e-01 1.9683111e+00 2.0924338e+00 2.0712315e+00 1.0514970e+00 2.2110297e+00 2.3461934e+00 1.8840843e+00 1.4831574e+00 1.4659783e+00 1.9682209e+00 8.9496218e-01 1.7964452e+00 6.5622658e-01 2.0655284e+00 1.1268523e+00 1.7764179e+00 9.9332012e-01 1.5158960e+00 2.3895003e+00 1.8982283e+00 1.8651393e+00 1.5387077e+00 2.7528000e+00 1.2871947e+00 1.1001946e+00 1.4627474e+00 1.4880729e+00 1.6030181e+00 1.6047598e+00 1.0374207e+00 3.2910073e+00 2.7655862e+00 1.7002168e+00 1.8814596e+00 1.2390194e+00 2.4548042e+00 8.7876634e-01 1.7158367e+00 1.8151170e+00 7.5016118e-01 7.8272551e-01 1.3241046e+00 1.5455843e+00 1.9524619e+00 3.2834453e+00 1.4300844e+00 5.8483448e-01 1.0263139e+00 2.4682518e+00 1.9911692e+00 1.0783755e+00 7.8808432e-01 1.5539983e+00 1.7882304e+00 1.6881750e+00 1.1268523e+00 1.8822939e+00 2.0779047e+00 1.5475657e+00 1.2810704e+00 1.1339991e+00 1.8534885e+00 9.0513514e-01 1.2087510e+00 3.4293561e+00 1.8554780e+00 3.4031864e+00 2.4420991e+00 2.9637920e+00 4.0403658e+00 1.1828717e+00 3.4998500e+00 2.6632870e+00 4.3954130e+00 2.8761203e+00 2.3399831e+00 3.0445122e+00 1.7890328e+00 2.3476777e+00 3.0401775e+00 2.6527529e+00 5.0321758e+00 4.1557153e+00 1.7943181e+00 3.4850530e+00 1.8421879e+00 4.0156620e+00 2.0769537e+00 3.3460838e+00 3.5735182e+00 2.0311110e+00 2.1883894e+00 2.6137665e+00 3.2724268e+00 3.5184202e+00 5.0524110e+00 2.6818545e+00 2.0664108e+00 1.9589565e+00 4.0923995e+00 3.3884524e+00 2.6885740e+00 2.0959739e+00 3.1948685e+00 3.2743590e+00 3.2448452e+00 1.8554780e+00 3.4633688e+00 3.5839347e+00 3.0150212e+00 2.1174980e+00 2.6708841e+00 3.2242395e+00 2.1262653e+00 2.3423978e+00 1.0035466e+00 2.2827159e+00 1.3153660e+00 1.8615039e+00 2.9298417e+00 1.3184035e+00 2.4022001e+00 1.8151170e+00 3.2315412e+00 1.7166676e+00 1.3595814e+00 1.9250594e+00 1.2570691e+00 1.5534985e+00 1.9352496e+00 1.4849081e+00 3.8359772e+00 3.2064915e+00 1.5410939e+00 2.3431625e+00 1.0302848e+00 2.9742645e+00 1.1013771e+00 2.1700970e+00 2.3919091e+00 9.7531402e-01 1.0396764e+00 1.5925492e+00 2.1393811e+00 2.4733960e+00 3.8624002e+00 1.6816960e+00 9.5650957e-01 1.0890388e+00 3.0080097e+00 2.2891398e+00 1.5007156e+00 9.6470639e-01 2.0543866e+00 2.1765531e+00 2.1487165e+00 1.0035466e+00 2.3180617e+00 2.4663563e+00 1.9433990e+00 1.3718709e+00 1.5414664e+00 2.1305612e+00 1.0107221e+00 1.7770053e+00 1.3000021e+00 1.3205171e+00 8.3930091e-01 1.8258497e+00 2.8432746e+00 1.7831595e+00 2.1193624e+00 1.2896554e+00 8.9496218e-01 1.6446727e+00 1.0946825e+00 2.1632524e+00 1.4041749e+00 5.4207852e-01 1.2077572e+00 2.1213447e+00 2.4069921e+00 2.9335119e+00 8.2206766e-01 1.6918279e+00 2.1851365e+00 1.7733720e+00 7.3278119e-01 1.4407642e+00 1.6273345e+00 1.3293020e+00 1.2924610e+00 1.7503171e+00 1.9276214e+00 2.3545376e+00 1.2450968e+00 1.8184976e+00 2.1894327e+00 1.8463545e+00 3.4893361e-01 1.0719022e+00 1.3834169e+00 1.0621362e+00 7.1740234e-01 1.0327832e+00 1.7770053e+00 6.9976890e-01 5.1210327e-01 9.9313181e-01 2.0841099e+00 1.0825311e+00 5.0208681e-01 1.3466860e+00 1.7937749e+00 8.2148003e-01 1.2268833e+00 2.4485240e+00 1.2563297e+00 1.9934469e+00 1.2524426e+00 2.8471336e+00 1.4358042e+00 7.3339246e-01 1.4342819e+00 4.9772204e-01 6.9457760e-01 1.4636741e+00 1.1233354e+00 3.5605638e+00 2.5755365e+00 1.2907457e+00 1.8667489e+00 3.7622328e-01 2.4814136e+00 6.2818221e-01 1.8112028e+00 2.1131807e+00 5.7672351e-01 7.9999102e-01 8.5275415e-01 1.9102507e+00 2.0267836e+00 3.6643554e+00 9.0168685e-01 8.3216780e-01 8.3306409e-01 2.5178144e+00 1.8656026e+00 1.2019259e+00 7.6362786e-01 1.6472011e+00 1.5943283e+00 1.7001179e+00 0.0000000e+00 1.8078806e+00 1.9570111e+00 1.3920954e+00 7.6195008e-01 1.1016806e+00 1.7729341e+00 7.1446962e-01 1.0816610e+00 7.3851064e-01 7.2248857e-01 3.0483776e+00 5.6342615e-01 1.3118081e+00 1.4889605e+00 9.8006369e-01 1.1737270e+00 4.2737382e-01 2.1131807e+00 1.7428500e+00 1.0543640e+00 8.5494999e-01 2.0376324e+00 1.3288928e+00 2.4590893e+00 5.9382214e-01 1.9576761e+00 9.8006369e-01 1.3739792e+00 8.5141186e-01 6.2055338e-01 1.3918500e+00 1.3907270e+00 9.7789352e-01 6.6861320e-01 6.5233704e-01 2.1059482e+00 9.8663349e-01 1.4035018e+00 1.7831595e+00 7.7880944e-01 1.4027992e+00 9.8677196e-01 1.5191564e+00 4.3798311e-01 6.8554305e-01 6.2111408e-01 1.7937749e+00 6.4241342e-01 9.9981032e-01 6.7780188e-01 1.6099640e+00 8.3640969e-01 1.4769275e+00 1.5684930e+00 6.3173774e-01 1.7302001e+00 2.0286216e+00 1.2711306e+00 1.0474897e+00 2.1700788e+00 8.2827027e-01 5.2290002e-01 7.5863433e-01 1.2491511e+00 1.0565061e+00 9.7542502e-01 3.3872939e-01 2.7982543e+00 2.0758695e+00 1.7342859e+00 1.1984110e+00 9.9693045e-01 1.8354727e+00 6.0840510e-01 1.1145077e+00 1.3082023e+00 5.2283051e-01 5.1857575e-01 4.7149050e-01 1.1471723e+00 1.3839439e+00 2.8837687e+00 5.8522871e-01 5.3667800e-01 9.0098101e-01 1.8496383e+00 1.3956631e+00 4.8016385e-01 6.2451737e-01 9.5139638e-01 1.0316097e+00 1.1168387e+00 8.2148003e-01 1.1408175e+00 1.3888569e+00 8.7588404e-01 9.9189360e-01 4.8124784e-01 1.3365773e+00 6.0566865e-01 1.4097462e+00 2.4566860e+00 1.1582635e+00 1.2895008e+00 1.6771691e+00 6.6244727e-01 8.5330525e-01 4.2110953e-01 1.5929148e+00 1.0739839e+00 5.6992880e-01 5.5102439e-01 2.4009228e+00 1.8321979e+00 2.1944657e+00 6.8299624e-01 1.3125970e+00 1.6253273e+00 1.0353506e+00 7.4661256e-01 1.1022402e+00 9.6950963e-01 8.7649478e-01 5.0731024e-01 1.1544356e+00 1.2559800e+00 2.5390784e+00 4.9009568e-01 1.1268617e+00 1.4819274e+00 1.4649720e+00 9.9544409e-01 6.0942760e-01 9.8006526e-01 5.9589853e-01 4.3937875e-01 6.7904052e-01 1.2268833e+00 6.0365341e-01 8.3354038e-01 4.3719837e-01 1.3221954e+00 4.2932160e-01 1.0251165e+00 9.7833010e-01 3.6949435e+00 6.0653347e-01 1.6944472e+00 1.5821553e+00 1.6484241e+00 1.7861438e+00 1.1497964e+00 2.7265330e+00 2.4114658e+00 1.7100754e+00 1.5191564e+00 1.8544941e+00 9.8143688e-01 2.9288318e+00 1.1208167e+00 2.6441923e+00 4.9772204e-01 2.0065422e+00 1.3857067e+00 8.4632640e-01 2.0655380e+00 2.0890644e+00 1.6229726e+00 9.3175410e-01 6.4813570e-01 1.8882412e+00 1.6282537e+00 2.0045623e+00 2.3010727e+00 4.0443437e-01 1.9471640e+00 1.6420881e+00 2.2200703e+00 1.1092092e+00 1.3077539e+00 1.2486828e+00 2.4485240e+00 1.1714086e+00 1.4815200e+00 1.3709657e+00 2.1645801e+00 1.5528645e+00 2.0592947e+00 2.2565403e+00 3.2107953e+00 2.2989490e+00 4.0089941e+00 2.5709804e+00 1.9412285e+00 2.6814987e+00 1.0808305e+00 1.6177026e+00 2.5906314e+00 2.3241321e+00 4.7335798e+00 3.7356640e+00 1.5467170e+00 3.0855313e+00 1.1828955e+00 3.6907456e+00 1.7719327e+00 2.9776826e+00 3.3258154e+00 1.7290027e+00 1.8703975e+00 2.1032002e+00 3.0991288e+00 3.2379874e+00 4.8403184e+00 2.1396216e+00 1.8765527e+00 1.6420881e+00 3.7688016e+00 2.8977291e+00 2.3525703e+00 1.7721385e+00 2.8780663e+00 2.8053505e+00 2.9124074e+00 1.2563297e+00 3.0197298e+00 3.1129968e+00 2.6135061e+00 1.7341866e+00 2.3184326e+00 2.7661123e+00 1.7090768e+00 1.2067996e+00 1.8641580e+00 1.3940244e+00 1.3162189e+00 8.8198158e-01 2.2794791e+00 2.1012392e+00 1.5525661e+00 1.0918469e+00 2.2063254e+00 1.1211328e+00 2.4017426e+00 1.1211328e+00 2.2284888e+00 6.3765570e-01 1.5168126e+00 1.2830542e+00 7.2263841e-01 1.5939137e+00 1.6681833e+00 1.2435436e+00 4.6557224e-01 3.1271814e-01 2.2147971e+00 1.2909648e+00 1.4589882e+00 1.7351918e+00 8.5359653e-01 1.8877628e+00 1.2647627e+00 1.8001617e+00 9.2780124e-01 1.2301583e+00 1.1566759e+00 1.9934469e+00 1.1506301e+00 1.5274241e+00 1.1815770e+00 1.6939440e+00 1.2016246e+00 1.9452063e+00 1.8368886e+00 2.7696320e+00 1.7002168e+00 6.6444642e-01 1.2360344e+00 1.3162958e+00 1.5606124e+00 1.8019802e+00 1.1903794e+00 3.3139779e+00 1.5262653e+00 1.2463647e+00 1.7598642e+00 1.6038123e+00 1.5053088e+00 8.4040822e-01 1.8873059e+00 1.7273593e+00 1.0791305e+00 1.4542898e+00 8.8167165e-01 1.3277924e+00 1.1132823e+00 3.3576107e+00 9.4738284e-01 1.0119180e+00 9.3046944e-01 1.8017473e+00 2.2743623e+00 1.4404916e+00 1.5380438e+00 1.4761813e+00 1.5955619e+00 1.5999471e+00 1.2524426e+00 1.7473897e+00 2.0612423e+00 1.3691763e+00 6.7534282e-01 1.2539581e+00 2.2701663e+00 1.5558007e+00 1.5218782e+00 2.4628184e+00 1.5932824e+00 3.2458126e+00 2.5692387e+00 1.4348047e+00 1.8937847e+00 9.2060977e-01 2.4408782e+00 3.8250534e+00 1.0499398e+00 2.8336242e+00 2.0769357e+00 2.6064495e+00 1.0813975e+00 1.3021456e+00 2.4993477e+00 2.2323451e+00 2.1662332e+00 1.8260680e+00 2.0200580e+00 1.1770826e+00 2.1383117e+00 2.5736499e+00 3.0399892e+00 1.5323598e+00 1.2212784e+00 1.7944590e+00 2.3235953e+00 1.3759094e+00 1.3385913e+00 1.3604806e+00 2.8471336e+00 1.0797751e+00 9.4588685e-01 1.6150266e+00 2.9366827e+00 1.8217819e+00 1.3773939e+00 2.3541561e+00 1.1723315e+00 6.4232366e-01 1.8822595e+00 1.3566020e+00 4.2656951e-01 5.7691891e-01 2.2149281e+00 2.2825823e+00 2.4730728e+00 7.0958226e-01 1.4300979e+00 1.9425292e+00 1.2122797e+00 4.9430028e-01 1.0215032e+00 1.0391769e+00 7.2638147e-01 9.8137813e-01 1.1659675e+00 1.5397131e+00 2.3056726e+00 1.0072799e+00 1.1569911e+00 1.6871910e+00 1.6699010e+00 7.9127668e-01 4.3341454e-01 8.2155022e-01 5.7672351e-01 6.8304299e-01 6.6412342e-01 1.4358042e+00 7.0097130e-01 8.1019167e-01 6.5485710e-01 1.6386105e+00 4.6472955e-01 7.2626021e-01 8.9802947e-01 8.9083207e-01 9.8663349e-01 1.0101003e+00 1.2666796e+00 7.2340544e-01 3.1120413e+00 1.9012831e+00 1.3662822e+00 1.4214310e+00 1.0271885e+00 1.7789322e+00 2.8835410e-01 1.4717950e+00 1.5613089e+00 4.5716421e-01 8.2373020e-01 3.8830315e-01 1.2833190e+00 1.3103990e+00 3.1817011e+00 4.8644514e-01 5.9610506e-01 8.0162421e-01 1.8503155e+00 1.7547273e+00 9.3865015e-01 8.9973730e-01 1.1346946e+00 1.2001902e+00 1.2260535e+00 7.3339246e-01 1.3976606e+00 1.6479131e+00 9.4228329e-01 5.0621589e-01 7.1671402e-01 1.7153988e+00 9.3451915e-01 1.7865803e+00 1.3700593e+00 7.2638147e-01 5.3458689e-01 2.2505972e+00 1.6524504e+00 2.2440955e+00 5.5576380e-01 1.5638518e+00 1.3688560e+00 1.0552128e+00 7.1143905e-01 8.2518769e-01 1.0245496e+00 9.9235657e-01 6.7030885e-01 8.3156325e-01 9.6025744e-01 2.3337038e+00 6.8299624e-01 1.1166319e+00 1.5524781e+00 1.1685901e+00 1.1719135e+00 6.6412342e-01 1.1159239e+00 2.6643250e-01 4.7488466e-01 4.3341454e-01 1.4342819e+00 5.7691891e-01 8.8366512e-01 3.3492202e-01 1.3576953e+00 4.2110953e-01 1.2033093e+00 1.1777985e+00 8.7819565e-01 1.8719964e+00 1.5530949e+00 3.9773949e+00 2.6834336e+00 1.0194189e+00 2.2401597e+00 7.0463400e-01 2.6908242e+00 8.9981614e-01 2.2443541e+00 2.5055082e+00 9.6168382e-01 1.2786676e+00 1.1515752e+00 2.2564140e+00 2.2581551e+00 4.0837847e+00 1.1737270e+00 1.1984110e+00 1.0100915e+00 2.7760526e+00 2.2855612e+00 1.6668656e+00 1.2419907e+00 2.0207624e+00 1.9399964e+00 2.0427084e+00 4.9772204e-01 2.1876191e+00 2.3343528e+00 1.7191609e+00 7.3633268e-01 1.5086315e+00 2.2088314e+00 1.2082987e+00 1.1857824e+00 1.2636762e+00 3.3874427e+00 2.5564450e+00 1.8349829e+00 1.6578570e+00 5.8813453e-01 2.5222553e+00 1.0240850e+00 1.6676963e+00 2.1419072e+00 9.3827844e-01 9.8741108e-01 8.7169308e-01 2.0802526e+00 2.1175243e+00 3.5451448e+00 8.2097460e-01 1.3248988e+00 1.4633215e+00 2.4116155e+00 1.5361480e+00 1.2935378e+00 9.5818710e-01 1.5578153e+00 1.3192053e+00 1.5035025e+00 6.9457760e-01 1.5912796e+00 1.6259926e+00 1.1892978e+00 1.1294987e+00 1.0978602e+00 1.4813076e+00 9.1948999e-01 8.1819403e-01 2.2419326e+00 2.2807501e+00 2.5846003e+00 6.4497192e-01 1.4107908e+00 2.0247998e+00 1.3509199e+00 5.5183182e-01 1.2328847e+00 1.1911894e+00 9.0810653e-01 9.7397874e-01 1.4379681e+00 1.6702453e+00 2.3957048e+00 9.4716675e-01 1.4061835e+00 1.8616601e+00 1.6979390e+00 5.2290002e-01 7.0376604e-01 9.7757519e-01 6.9976890e-01 4.8012872e-01 6.5622658e-01 1.4636741e+00 5.9074344e-01 5.5183182e-01 5.8926015e-01 1.7101283e+00 6.2055338e-01 5.2374483e-01 1.0096792e+00 2.4983023e+00 2.0024830e+00 2.0009022e+00 9.4244262e-01 1.2563297e+00 1.6864324e+00 8.0788963e-01 8.3930091e-01 1.0038277e+00 7.0810362e-01 5.9074344e-01 6.2055338e-01 9.0121804e-01 1.2356595e+00 2.5673851e+00 7.1082758e-01 6.9066640e-01 1.1671832e+00 1.6263298e+00 1.2372185e+00 2.6033464e-01 7.2248857e-01 6.6653737e-01 8.5606908e-01 8.7588404e-01 1.1233354e+00 9.0810653e-01 1.1795009e+00 7.1867388e-01 1.2188386e+00 3.1239235e-01 1.1894366e+00 7.5921691e-01 2.7729820e+00 4.4273104e+00 1.7851048e+00 3.5860697e+00 2.3211451e+00 3.2572853e+00 1.7681972e+00 1.6466818e+00 3.1677578e+00 2.9074188e+00 2.8617360e+00 2.1557081e+00 2.3917474e+00 3.9487224e-01 2.8587345e+00 3.1370513e+00 3.5889276e+00 1.8806886e+00 2.0412779e+00 2.4100318e+00 3.0088533e+00 2.0247746e+00 2.1265123e+00 2.0926464e+00 3.5605638e+00 1.8217810e+00 1.8066213e+00 2.3669524e+00 3.5958907e+00 2.5091153e+00 2.1661990e+00 3.0374915e+00 2.7063291e+00 1.8195505e+00 2.8431665e+00 6.1655427e-01 2.1507483e+00 2.1438129e+00 1.7230435e+00 2.3108260e+00 2.5097011e+00 1.8135469e+00 1.5638528e+00 9.0791603e-01 2.8200316e+00 1.7992895e+00 2.2827159e+00 2.3805592e+00 1.0279218e+00 2.6120156e+00 2.2030084e+00 2.6289851e+00 1.7477225e+00 1.8297977e+00 1.8176515e+00 2.5755365e+00 1.8486085e+00 2.1438129e+00 1.7993706e+00 2.0846858e+00 2.0084695e+00 2.7218129e+00 2.6544629e+00 2.7850656e+00 1.6064410e+00 2.7362607e+00 1.2723027e+00 2.8153418e+00 2.8097763e+00 1.4630671e+00 1.8911656e+00 1.6976298e+00 2.3931138e+00 2.3316649e+00 4.4654565e+00 1.7621453e+00 1.4315442e+00 9.9921804e-01 3.0176875e+00 3.0491088e+00 2.1855415e+00 1.8898572e+00 2.4817766e+00 2.5552736e+00 2.5674482e+00 1.2907457e+00 2.7588865e+00 3.0041677e+00 2.2870308e+00 9.4057729e-01 2.0520412e+00 2.9779211e+00 1.8911656e+00 1.9172403e+00 1.5033800e+00 1.5778466e+00 4.2450569e-01 7.6773108e-01 1.5016932e+00 1.3345218e+00 1.1346946e+00 1.0902078e+00 1.2416734e+00 1.9196757e+00 1.1117653e+00 1.6095257e+00 2.0596575e+00 1.0943114e+00 8.7072347e-01 9.2729770e-01 1.4434261e+00 3.8830315e-01 3.6319073e-01 4.1088655e-01 1.8667489e+00 1.6562722e-01 4.2450569e-01 5.9279023e-01 1.8877121e+00 8.2518769e-01 9.7789352e-01 1.4886316e+00 2.7335291e+00 9.1459005e-01 1.8213026e+00 2.2454046e+00 7.7259801e-01 8.0376328e-01 1.0525811e+00 2.1168634e+00 2.2814168e+00 3.7089872e+00 1.0767714e+00 1.0755005e+00 1.1631285e+00 2.6946772e+00 1.7497347e+00 1.2634840e+00 7.1971771e-01 1.7438018e+00 1.6356845e+00 1.7637315e+00 3.7622328e-01 1.8511762e+00 1.9311191e+00 1.4699785e+00 1.1016806e+00 1.1928774e+00 1.6354514e+00 6.5233704e-01 2.0052498e+00 1.7681972e+00 1.2007144e+00 2.1235855e+00 2.2484715e+00 1.6942544e+00 1.0546367e+00 5.1386894e-01 2.3251407e+00 1.7093881e+00 2.0273081e+00 2.2255325e+00 6.9493020e-01 2.3316649e+00 1.8640280e+00 2.3781796e+00 1.4043141e+00 1.6126002e+00 1.5456113e+00 2.4814136e+00 1.5467508e+00 1.8811164e+00 1.5963715e+00 2.0694478e+00 1.7422855e+00 2.4276706e+00 2.4143104e+00 1.5837613e+00 1.7028549e+00 2.6643250e-01 7.3283576e-01 6.1619693e-01 1.4102704e+00 1.5157660e+00 3.3107238e+00 7.0702759e-01 4.6964680e-01 7.3731902e-01 2.0564363e+00 1.8389778e+00 9.9058911e-01 7.8305765e-01 1.2692102e+00 1.3637808e+00 1.3483908e+00 6.2818221e-01 1.5635907e+00 1.7874832e+00 1.0817627e+00 4.8284931e-01 7.9664122e-01 1.7693113e+00 8.5141186e-01 7.7538587e-01 1.4522625e+00 1.1678338e+00 1.2100516e+00 1.1294987e+00 1.4712028e+00 1.8985225e+00 1.2171256e+00 1.5155373e+00 1.9939611e+00 1.4342819e+00 6.6653737e-01 7.1511757e-01 1.2680818e+00 5.4772790e-01 6.0868934e-01 6.7484334e-01 1.8112028e+00 3.8639663e-01 5.2413598e-01 7.9227302e-01 1.9656037e+00 7.9656884e-01 7.1789533e-01 1.2970125e+00 1.6649242e+00 1.5382030e+00 1.4107908e+00 5.4248468e-01 9.6424206e-01 1.6581712e+00 1.4527629e+00 1.5643033e+00 2.0014001e+00 1.0048958e+00 1.4365091e+00 1.0328871e+00 1.6659456e+00 6.7424840e-01 1.0427348e+00 9.3481345e-01 2.1131807e+00 8.1596583e-01 1.1349095e+00 1.1009910e+00 2.0312552e+00 1.0961859e+00 1.4886316e+00 1.7139439e+00 4.8016385e-01 6.4687084e-01 1.4356300e+00 1.6309773e+00 3.2287360e+00 7.3391501e-01 4.4499696e-01 8.4121419e-01 2.1133414e+00 1.6592561e+00 8.3333283e-01 5.2066928e-01 1.2097457e+00 1.2911242e+00 1.2850973e+00 5.7672351e-01 1.4812088e+00 1.6720834e+00 1.0285902e+00 7.2340544e-01 6.8124108e-01 1.5683551e+00 6.1067563e-01 8.0990117e-01 1.4468934e+00 1.7768340e+00 2.9867606e+00 8.8098199e-01 6.6217390e-01 1.1327663e+00 2.1506380e+00 1.2980342e+00 5.4779717e-01 1.3340137e-01 1.1051628e+00 1.1634940e+00 1.1952607e+00 7.9999102e-01 1.2953104e+00 1.4320028e+00 9.9155078e-01 1.1867923e+00 5.7526462e-01 1.1726810e+00 2.6680274e-01 1.2602457e+00 1.2678174e+00 2.9703757e+00 1.3103399e-01 8.4439576e-01 1.0887336e+00 1.6811909e+00 1.4435947e+00 7.9778097e-01 8.9789119e-01 9.2528705e-01 8.7435479e-01 9.9613800e-01 8.5275415e-01 1.0871867e+00 1.3186900e+00 6.8124108e-01 8.2317311e-01 5.4397563e-01 1.4334280e+00 9.0121513e-01 6.7419212e-01 2.1234744e+00 1.3330747e+00 1.2524426e+00 1.6423187e+00 1.1112287e+00 1.7731481e+00 1.0412209e+00 1.5779602e+00 8.1552831e-01 1.2367326e+00 1.0877340e+00 1.9102507e+00 1.1360631e+00 1.4957547e+00 1.1495900e+00 1.6944472e+00 1.0511712e+00 1.7894533e+00 1.6403454e+00 2.3936810e+00 1.3012342e+00 1.5364148e+00 1.7852089e+00 7.8659640e-01 2.0467146e+00 1.4387140e+00 1.9055720e+00 1.0341095e+00 1.3049404e+00 1.1996837e+00 2.0267836e+00 1.2898173e+00 1.6473842e+00 1.2092432e+00 1.6223502e+00 1.2928268e+00 2.1087983e+00 1.9576761e+00 2.9790337e+00 3.1661621e+00 3.6343153e+00 1.9094387e+00 2.2505084e+00 2.4932991e+00 3.0919113e+00 2.0983540e+00 2.2774299e+00 2.1822207e+00 3.6643554e+00 1.9784646e+00 2.0041121e+00 2.4741311e+00 3.6564145e+00 2.5932898e+00 2.3540393e+00 3.1383476e+00 9.6758101e-01 1.2012033e+00 1.6658010e+00 1.4135473e+00 8.6985276e-01 9.6249568e-01 9.3451915e-01 8.2380019e-01 9.6993876e-01 9.0168685e-01 1.0632334e+00 1.2723027e+00 6.4232366e-01 8.7376399e-01 5.8942278e-01 1.4153467e+00 9.6559725e-01 6.0709980e-01 2.1192618e+00 1.8400866e+00 8.3619405e-01 7.2626021e-01 1.2848171e+00 1.4775384e+00 1.4500356e+00 8.3216780e-01 1.5874797e+00 1.8427499e+00 1.2442620e+00 8.6985276e-01 8.3878265e-01 1.7484907e+00 7.7500385e-01 2.4608044e+00 2.2758533e+00 1.3186900e+00 1.1601403e+00 1.7655861e+00 1.8899115e+00 1.9324044e+00 8.3306409e-01 2.0122449e+00 2.2830055e+00 1.6787550e+00 8.1019167e-01 1.3243478e+00 2.1959912e+00 1.1244567e+00 1.9511379e+00 1.7500667e+00 2.2774574e+00 1.1011934e+00 1.2684800e+00 1.1435764e+00 2.5178144e+00 1.1861264e+00 1.4342819e+00 1.3109392e+00 2.2026274e+00 1.5858048e+00 2.0711809e+00 2.3400013e+00 1.0557584e+00 1.3438727e+00 1.0821769e+00 8.4383266e-01 1.0493821e+00 1.8656026e+00 7.8957903e-01 5.5399712e-01 1.0737552e+00 2.2030214e+00 1.1115276e+00 2.1119253e-01 1.3352177e+00 6.6627781e-01 7.2272795e-01 8.6751530e-01 9.1936743e-01 1.2019259e+00 8.7588404e-01 1.0946184e+00 8.0162421e-01 1.4236959e+00 4.0664863e-01 9.8463602e-01 6.8496652e-01 1.2266388e+00 1.2615426e+00 1.3010124e+00 7.6362786e-01 1.4014424e+00 1.5148689e+00 1.0932736e+00 1.2210779e+00 6.9618131e-01 1.2059294e+00 2.0855006e-01 4.7509249e-01 3.1239235e-01 1.6472011e+00 4.6557224e-01 7.5810578e-01 4.3937875e-01 1.5999820e+00 5.6262711e-01 1.1233867e+00 1.3019241e+00 3.9472619e-01 1.5943283e+00 3.3742167e-01 4.8284931e-01 3.4893361e-01 1.6410601e+00 6.6154242e-01 9.3451915e-01 1.2980649e+00 1.7001179e+00 5.2283051e-01 6.7484334e-01 3.3872939e-01 1.6485749e+00 6.6653737e-01 1.1055440e+00 1.3931316e+00 1.8078806e+00 1.9570111e+00 1.3920954e+00 7.6195008e-01 1.1016806e+00 1.7729341e+00 7.1446962e-01 3.8639663e-01 6.2027457e-01 1.8723846e+00 8.0990117e-01 9.0447834e-01 1.4243850e+00 7.9227302e-01 2.1007225e+00 1.0230346e+00 7.1789533e-01 1.5391678e+00 1.3603920e+00 4.6137216e-01 1.1083720e+00 1.1686836e+00 1.1908452e+00 2.1561807e+00 1.2583645e+00 1.0722301e+00 7.7259801e-01 1.2001902e+00 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce80cb1ead3f88bec0fbaf6d48cb4fc584e52168 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-seuclidean-ml.txt @@ -0,0 +1 @@ + 1.4330520e+01 1.4635426e+01 1.3450855e+01 1.4761140e+01 1.3508642e+01 1.5434417e+01 1.3887693e+01 1.5166776e+01 1.3966038e+01 1.4950451e+01 1.4564587e+01 1.3834201e+01 1.4347008e+01 1.5641962e+01 1.4689053e+01 1.4418720e+01 1.4545856e+01 1.4151822e+01 1.4669017e+01 1.5150750e+01 1.3770166e+01 1.3288969e+01 1.4048191e+01 1.4049959e+01 1.4164158e+01 1.3727834e+01 1.4074687e+01 1.4321303e+01 1.2497330e+01 1.3820273e+01 1.4441030e+01 1.4780222e+01 1.2504339e+01 1.5022245e+01 1.4263650e+01 1.3704507e+01 1.3694385e+01 1.3667517e+01 1.3177468e+01 1.4391931e+01 1.4893903e+01 1.4475753e+01 1.4440707e+01 1.3603096e+01 1.6889651e+01 1.4731174e+01 1.3337775e+01 1.5187532e+01 1.5667271e+01 1.4226037e+01 1.4203554e+01 1.5272898e+01 1.6031460e+01 1.5991549e+01 1.1855060e+01 1.4844776e+01 1.2475182e+01 1.4408126e+01 1.4836870e+01 1.3472986e+01 1.4089281e+01 1.1018298e+01 1.3183296e+01 1.4590802e+01 1.4404230e+01 1.2717623e+01 1.3983283e+01 1.4017133e+01 1.4608005e+01 1.4402553e+01 1.3977803e+01 1.4091040e+01 1.3977459e+01 1.2630449e+01 1.4160109e+01 1.3029417e+01 1.2654432e+01 1.2794946e+01 1.3194978e+01 1.4378745e+01 1.2431908e+01 1.3852651e+01 1.3748358e+01 1.4003568e+01 1.5066681e+01 1.5192826e+01 1.4370013e+01 1.5792545e+01 1.3547546e+01 1.4411543e+01 1.4794215e+01 1.4924312e+01 1.4789153e+01 1.4875055e+01 1.4208537e+01 1.2786148e+01 1.4882476e+01 1.3302010e+01 1.4354774e+01 1.4542129e+01 1.5889633e+01 1.2928185e+01 1.4877868e+01 1.2890902e+01 1.4406165e+01 1.4498123e+01 1.4303273e+01 1.3207002e+01 1.3954732e+01 1.4841248e+01 1.5427799e+01 1.4363463e+01 1.3976277e+01 1.4284878e+01 1.4457991e+01 1.3369469e+01 1.5246610e+01 1.4487573e+01 1.4525176e+01 1.4505865e+01 1.5037347e+01 1.3834927e+01 1.3758988e+01 1.3424987e+01 1.4914766e+01 1.3783923e+01 1.3434291e+01 1.2895927e+01 1.3870360e+01 1.3342977e+01 1.3094322e+01 1.3057847e+01 1.3322375e+01 1.4940650e+01 1.4476829e+01 1.4197503e+01 1.4597035e+01 1.2963234e+01 1.4011414e+01 1.3181409e+01 1.3339615e+01 1.3928735e+01 1.3508015e+01 1.3170749e+01 1.3529133e+01 1.3454724e+01 1.4883437e+01 1.4564565e+01 1.2474313e+01 1.4435790e+01 1.5285703e+01 1.3701736e+01 1.3578312e+01 1.4807311e+01 1.4281072e+01 1.2920213e+01 1.4427803e+01 1.1408611e+01 1.4097334e+01 1.2868115e+01 1.3903683e+01 1.3800332e+01 1.3439339e+01 1.4062651e+01 1.3242107e+01 1.4400424e+01 1.3826132e+01 1.5991146e+01 1.3118258e+01 1.5377390e+01 1.2858378e+01 1.5249567e+01 1.4081585e+01 1.4458052e+01 1.4175623e+01 1.4850069e+01 1.5506668e+01 1.5014770e+01 1.4337030e+01 1.5214705e+01 1.4803729e+01 1.3188675e+01 1.3437739e+01 1.3409394e+01 1.4607386e+01 1.5394271e+01 1.5946451e+01 1.3769364e+01 1.4181208e+01 1.2551765e+01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-spearman-ml.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-spearman-ml.txt new file mode 100644 index 0000000000000000000000000000000000000000..b50fe3af1912d20aaa737eedc1b6e096a7005876 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/pdist-spearman-ml.txt @@ -0,0 +1 @@ + 9.3540954e-01 9.7904590e-01 8.6703870e-01 1.1569997e+00 8.7174317e-01 1.0627183e+00 9.1272727e-01 1.1593999e+00 9.7573357e-01 1.0072127e+00 1.0536814e+00 9.6276028e-01 9.7700570e-01 1.1513951e+00 1.0719592e+00 9.2178818e-01 1.0004680e+00 9.3689769e-01 9.8205821e-01 1.0332673e+00 9.4517852e-01 8.9437744e-01 9.7556556e-01 9.0460246e-01 9.7210921e-01 9.2230423e-01 9.9605161e-01 9.6852085e-01 8.4162016e-01 9.6667267e-01 9.7759376e-01 9.9757576e-01 7.6992499e-01 1.0151695e+00 9.8691869e-01 9.0325833e-01 8.6665467e-01 8.8844884e-01 8.4553255e-01 9.7700570e-01 9.5159916e-01 9.8906691e-01 1.0551935e+00 9.1973597e-01 1.3266247e+00 1.0982778e+00 8.4531653e-01 1.0887369e+00 1.0984938e+00 9.9851185e-01 9.0701470e-01 1.0639304e+00 1.2392919e+00 1.1422502e+00 8.1725773e-01 1.1844944e+00 7.8219022e-01 1.0817162e+00 1.2196100e+00 1.0003120e+00 1.0164536e+00 7.0724272e-01 9.7981398e-01 1.1134953e+00 1.0671107e+00 9.3600960e-01 9.9984398e-01 1.0356916e+00 1.1248005e+00 1.0696310e+00 1.0634263e+00 9.6472847e-01 9.9365137e-01 8.5724572e-01 1.1257846e+00 8.9930993e-01 9.4903090e-01 9.0667867e-01 9.1231923e-01 1.0573777e+00 9.0105011e-01 9.5255926e-01 1.0177978e+00 1.0606901e+00 1.1966997e+00 1.0891929e+00 1.0085089e+00 1.2640264e+00 9.3246925e-01 1.0198020e+00 1.2055806e+00 1.1237924e+00 1.1060666e+00 1.0517252e+00 1.0684668e+00 7.6844884e-01 1.0572697e+00 8.7373537e-01 9.6283228e-01 9.9350735e-01 1.2412601e+00 7.6322832e-01 1.0298950e+00 8.6148215e-01 1.0042724e+00 9.7012901e-01 9.3712571e-01 8.5845785e-01 8.5862586e-01 1.0336634e+00 1.0955536e+00 9.5302730e-01 9.8696670e-01 1.0633063e+00 1.0026643e+00 9.6380438e-01 1.1711251e+00 9.9273927e-01 1.0260906e+00 1.0863966e+00 1.0482808e+00 9.0361836e-01 9.2358836e-01 8.7794779e-01 1.2461206e+00 9.2985299e-01 1.0418962e+00 9.4660666e-01 9.5636364e-01 9.0646265e-01 9.9113111e-01 8.3027903e-01 9.3341734e-01 1.1378938e+00 1.0548215e+00 1.0086889e+00 1.1998920e+00 8.6063006e-01 1.0255506e+00 8.4786079e-01 1.0090729e+00 9.2542454e-01 9.5176718e-01 9.3477348e-01 9.0091809e-01 9.6404440e-01 1.1158716e+00 9.9614761e-01 7.7682568e-01 1.0605461e+00 1.0895650e+00 9.0065407e-01 8.7173117e-01 9.9821182e-01 1.2165617e+00 8.6127813e-01 1.1111071e+00 7.9015902e-01 1.0433843e+00 8.6510651e-01 1.0019202e+00 1.0154815e+00 9.4381038e-01 9.8646265e-01 1.0062526e+00 9.7426943e-01 9.8191419e-01 1.3038944e+00 8.6277828e-01 1.0830243e+00 8.6851485e-01 1.1192559e+00 9.9120312e-01 9.6540054e-01 9.1072307e-01 1.1775698e+00 1.1139154e+00 1.1083468e+00 9.9593159e-01 1.0825923e+00 1.1115032e+00 9.7430543e-01 9.5605161e-01 9.2800480e-01 9.4369037e-01 1.1136034e+00 1.1382898e+00 9.5937594e-01 9.8843084e-01 7.4563456e-01 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-bool-data.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-bool-data.txt new file mode 100644 index 0000000000000000000000000000000000000000..df0d838f517f6c0afd8954ad87bc83886e0e3de4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-bool-data.txt @@ -0,0 +1,100 @@ +0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 1 1 +1 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 0 1 0 1 1 1 0 1 0 1 1 1 1 +0 1 0 1 1 0 0 1 1 1 1 0 1 0 0 1 1 0 1 1 1 0 1 1 0 1 1 1 0 1 +1 1 1 0 0 1 1 0 0 1 1 1 0 0 1 1 0 1 1 1 0 1 1 0 0 0 0 1 0 0 +1 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 1 0 0 +1 0 1 1 0 0 0 1 1 1 1 1 0 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 1 1 +0 1 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 1 1 1 0 1 0 +1 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1 0 0 1 0 +1 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 1 1 0 1 1 1 0 0 1 1 +1 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 0 0 1 1 0 0 +1 0 1 0 1 1 0 1 1 0 1 1 0 1 1 1 1 0 0 1 1 0 0 0 0 1 0 0 0 0 +1 1 1 1 0 1 0 0 0 0 0 1 0 1 1 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 +1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 1 +0 1 1 0 0 1 1 0 0 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 0 0 1 1 1 1 +1 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 0 1 1 +1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 0 0 +1 1 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 0 1 1 0 1 0 0 0 1 1 1 1 1 +0 0 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 1 1 0 0 1 0 1 0 0 0 +1 0 1 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 1 1 0 1 1 0 1 1 +0 0 0 0 1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 1 1 +0 1 0 0 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 1 0 0 1 0 1 1 0 1 0 +1 0 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 1 +0 0 1 0 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 1 0 +0 1 0 1 1 1 0 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1 1 0 0 1 1 0 0 1 +0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 +1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 0 1 0 1 0 1 1 1 0 0 0 1 1 1 0 +1 0 0 0 1 1 1 0 0 1 0 1 1 1 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 1 +0 1 0 0 0 1 1 1 0 1 1 1 0 1 0 0 1 1 1 1 0 1 0 1 0 1 1 0 1 1 +0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 +0 0 1 0 1 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 1 0 1 0 +1 1 0 1 1 1 1 1 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 0 1 0 0 0 0 1 +0 1 0 0 0 1 0 1 1 0 0 1 0 0 0 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 +1 1 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1 1 0 0 0 1 0 1 0 1 0 1 0 1 +1 1 1 0 1 0 0 1 1 0 1 1 1 0 1 0 1 1 0 0 0 1 1 0 0 1 1 1 1 1 +0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 0 1 1 0 0 1 0 +1 1 1 1 0 1 0 1 0 1 1 1 1 0 1 0 1 0 1 0 1 1 0 0 1 0 1 0 0 0 +0 0 0 0 1 1 1 0 1 1 0 0 1 1 1 1 0 1 0 1 1 1 1 1 1 0 0 0 0 0 +0 1 1 1 0 0 0 1 1 1 0 1 0 0 1 1 1 1 1 0 1 0 0 1 0 0 0 0 1 1 +0 1 0 0 1 1 1 1 0 0 1 0 1 0 1 1 0 0 1 0 0 1 1 0 0 0 0 1 0 0 +1 1 0 1 0 0 1 1 0 0 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 1 0 0 1 +0 1 1 0 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 1 +0 0 1 1 1 0 1 0 0 1 1 0 0 0 1 1 1 0 1 0 0 0 0 1 1 0 1 1 0 0 +1 0 1 1 1 1 1 1 1 1 0 1 0 0 0 1 0 1 0 0 0 1 1 0 0 1 0 0 0 0 +1 0 1 1 1 0 1 1 1 1 0 0 1 0 1 1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 +1 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 1 0 0 1 0 1 0 1 1 1 0 0 0 1 +1 0 1 0 1 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 0 1 1 0 1 0 0 1 0 0 +0 1 1 0 1 0 1 1 1 1 1 0 0 0 0 1 0 1 0 0 1 1 1 1 0 1 0 1 1 1 +0 1 0 1 1 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 1 1 1 0 0 1 0 1 +1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 +1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 +1 1 1 1 0 0 0 1 0 1 1 0 0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 1 0 0 +1 0 1 0 0 1 1 1 1 0 1 1 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0 0 0 0 +0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 0 1 0 1 0 0 0 1 0 0 1 0 +0 0 0 1 0 0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 1 1 1 0 1 0 1 1 1 0 +1 1 0 0 0 0 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 +1 0 1 1 1 0 1 0 1 0 0 1 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0 0 0 1 +0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 1 0 0 +0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 1 1 0 0 0 1 0 1 1 0 1 1 1 0 0 +0 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 1 0 +1 0 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1 +0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 1 0 0 0 +1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 1 0 0 1 0 1 0 +0 1 0 1 1 1 1 1 0 1 0 1 1 0 0 1 1 0 1 1 0 1 1 0 1 1 0 0 0 1 +1 0 1 1 1 0 0 0 1 0 0 1 0 0 0 1 0 1 1 1 0 0 1 1 1 1 0 0 0 1 +0 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1 1 0 1 +0 0 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 +0 1 1 1 1 1 0 0 0 0 0 1 0 1 1 1 1 1 0 1 1 1 0 0 1 0 0 1 1 1 +1 1 1 1 0 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 0 1 0 1 0 1 1 0 0 0 +1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 1 1 1 1 1 0 0 1 1 1 1 1 0 +0 0 0 0 1 1 1 0 1 0 0 1 1 0 0 1 1 1 1 0 0 1 0 1 0 0 0 1 0 0 +1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 0 1 0 0 0 0 1 1 0 1 1 0 +1 0 1 1 0 1 0 1 0 1 1 0 1 1 1 0 0 1 0 0 1 1 0 0 1 1 0 1 0 1 +1 1 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 +0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 1 1 +1 1 1 0 1 1 1 1 1 0 0 0 0 1 0 0 1 0 1 0 1 1 1 0 0 1 0 0 1 1 +1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 0 1 0 0 1 0 0 1 0 +1 0 1 1 0 0 1 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 1 1 0 +1 1 1 1 1 0 0 1 0 0 1 1 1 0 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 +1 0 1 1 0 0 1 1 0 1 1 1 0 0 0 1 0 1 0 0 0 1 1 1 1 1 0 0 1 0 +0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 1 1 0 0 1 0 0 1 0 0 +1 1 1 0 0 0 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1 1 1 0 1 1 1 0 1 0 +1 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 1 1 0 0 1 0 1 0 1 +1 0 0 0 1 0 1 1 0 1 0 0 0 1 0 1 0 0 0 0 1 1 1 0 1 0 1 1 0 1 +0 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 1 0 1 1 0 0 0 0 0 0 1 1 1 +0 1 0 0 1 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 1 1 0 0 1 0 1 0 0 0 +0 1 0 1 1 1 1 1 1 1 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 +0 0 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 +1 0 0 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1 1 0 0 1 0 1 0 1 0 1 0 0 +1 0 0 0 1 0 1 0 0 0 1 1 0 0 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 0 +0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 0 1 0 0 0 1 1 0 +1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 0 +0 1 0 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 1 1 1 0 1 +0 0 0 1 1 0 1 0 1 0 1 0 0 0 1 1 1 0 1 1 0 0 0 1 1 0 0 1 0 1 +1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 +0 1 1 0 0 0 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 0 1 0 1 0 0 0 +1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 1 1 0 0 1 +0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 +1 1 1 0 1 1 0 1 1 0 1 1 0 1 0 0 1 0 0 0 1 1 1 1 0 1 1 0 1 1 +0 0 1 1 1 0 0 0 0 1 1 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 1 0 0 1 +0 0 0 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 0 0 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-double-data.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-double-data.txt new file mode 100644 index 0000000000000000000000000000000000000000..039ac506f5590f953ffc6598c11197d3fade2bbd --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-double-data.txt @@ -0,0 +1,100 @@ +1.172993630434470589e+02 1.905532343119886605e+02 2.613653823499444115e+02 1.570270816248337269e+02 2.373767637129340642e+02 2.175366144750510671e+02 2.609909144757107242e+02 2.086671686166440622e+02 2.674986450118991002e+02 1.395992762090408235e+02 1.115453060949917159e+02 1.531034842395609701e+02 2.621042034264289668e+02 2.958729454449504033e+02 2.137960368830719062e+02 2.606436280968571282e+02 2.492136530687155869e+02 2.770806237064748530e+02 2.667325121892417883e+02 2.909243437665674037e+02 1.570328417294508085e+02 1.738762543815240917e+02 1.514157955792608163e+02 2.264748814818163396e+02 1.911869834397498380e+02 2.083706054660671043e+02 2.778115921852293013e+02 1.330374814391803397e+02 2.988697222234711717e+02 2.534167825404447001e+02 +2.090964891529778242e+02 1.322006104643973003e+02 1.443415945355371832e+02 2.991388772264980389e+02 1.649302344777914868e+02 2.839528641910463875e+02 1.677159709681393736e+02 2.597553832458208944e+02 2.861055547321268477e+02 1.866431671806918189e+02 2.131812134614140177e+02 1.881465139477683124e+02 1.271865139985419262e+02 1.821608865941132649e+02 2.793653846657656459e+02 1.745982981552271838e+02 2.440893905635109888e+02 1.926469151980868446e+02 2.752453852984189098e+02 1.333479229516146347e+02 1.756311805755703404e+02 2.039367615619088383e+02 2.441861159155101575e+02 2.136111324500645594e+02 2.893808960992043922e+02 2.723220466017930335e+02 2.367879096909125565e+02 2.831541206793258425e+02 2.017643187924728068e+02 1.293072046241175030e+02 +2.311242818257193221e+02 2.180694109009666306e+02 2.728791416531455525e+02 1.239345918565636993e+02 2.885729762050686418e+02 2.082619393005260804e+02 2.331416004257805525e+02 1.003112528445347778e+02 2.796331120515330895e+02 2.804679740148056339e+02 2.466936828597247597e+02 1.422398585800914361e+02 1.312115029632765015e+02 1.324417143647877708e+02 2.161716508991076466e+02 1.791489656100356171e+02 2.239038785146145472e+02 2.456511993086799919e+02 2.885023077068626662e+02 2.127338775308419940e+02 2.468090724782538246e+02 2.704135008577740109e+02 1.144148504575758665e+02 1.641571759150080538e+02 2.473349551308716343e+02 2.366620528761779667e+02 1.208143167141831498e+02 1.403705034199327599e+02 2.061073908129479548e+02 1.482034962693051057e+02 +1.938319500339997035e+02 2.000523826243218650e+02 1.356134735235139317e+02 1.224357428573656250e+02 1.262840705282213918e+02 1.112797762573139977e+02 1.727826315738305993e+02 2.199559683100150664e+02 1.817290208723558180e+02 2.185579898773881951e+02 1.772844462934412491e+02 1.589145011846130728e+02 1.017520743541414703e+02 2.836990856171782980e+02 1.265544072638776640e+02 2.503473341476423855e+02 2.178539278172635534e+02 2.063574432066289432e+02 1.473169457524925861e+02 1.112719632489760784e+02 1.195996070145015722e+02 1.345099678548529312e+02 2.992645259487585463e+02 2.692242364540683752e+02 2.139649193607747861e+02 2.313659165106297451e+02 2.524185025119667785e+02 2.678714004815313388e+02 1.111457754393238702e+02 1.296443575800298902e+02 +1.183944097426736306e+02 2.750477277868330930e+02 1.688558971333346790e+02 1.432283295687057034e+02 2.226043174503911359e+02 1.825124733235978169e+02 1.806485153578007612e+02 2.270256019866706936e+02 2.852913053786990076e+02 2.867562520175486043e+02 2.795056496733417362e+02 1.142488895870292822e+02 1.502985045661773427e+02 2.246907359526948937e+02 2.051158858061974115e+02 2.663351441156772808e+02 2.864853431806749882e+02 2.276548949573071070e+02 2.678087640355958001e+02 2.266463576941352187e+02 1.886763304826383774e+02 1.150603609957262563e+02 1.596187994714221929e+02 1.844565420383776484e+02 1.730173420200940768e+02 1.427940137102308995e+02 1.774757620992130001e+02 2.563086691508434001e+02 1.666317348809653822e+02 1.878143419608473437e+02 +1.642344698640436036e+02 1.591648429561690818e+02 1.561851029939521140e+02 1.854367091922420059e+02 1.494951311500319093e+02 2.443780767043579942e+02 2.741090240793212160e+02 1.519200656263381006e+02 1.391711947382712538e+02 1.482414334940778815e+02 2.574425018646875287e+02 1.455120022089010945e+02 1.620904376421240727e+02 2.098493186451893848e+02 2.377904829227144887e+02 2.881187570801528750e+02 1.785609418793050054e+02 1.500483139796714340e+02 1.697371065898091729e+02 1.824143324642365087e+02 2.329862749140337712e+02 1.372006180078979298e+02 2.250666134242961789e+02 1.760894707637434067e+02 1.874161150869196035e+02 2.860410495381969440e+02 1.539271628213176086e+02 1.051658254213322152e+02 1.501619097950496666e+02 1.205717364486104515e+02 +1.275638286377957371e+02 2.620802183565458563e+02 2.290828196339760723e+02 2.591630015014513333e+02 2.102568650793322149e+02 2.385080320420775593e+02 2.683788150825365619e+02 1.808700201925492763e+02 1.972184450648797451e+02 2.382313686117472287e+02 1.733526990293641177e+02 2.369802981553972074e+02 1.835652530901061823e+02 1.274084560526275141e+02 2.403488205519001326e+02 2.713515297463850402e+02 1.455311801633137065e+02 1.889430214806582171e+02 1.676324321357484735e+02 2.327799977696781184e+02 2.846419393176552148e+02 1.510702433968490936e+02 1.361559014852734606e+02 1.732199851325496525e+02 2.451323003571785364e+02 1.833444866660036894e+02 2.451280287301300405e+02 1.669088211440060832e+02 2.768492228383354359e+02 2.445882168033535038e+02 +2.905092787520428601e+02 2.948076984760371033e+02 1.731080208454208673e+02 2.825532355845657548e+02 1.108820315678514845e+02 2.862013985457700755e+02 2.111453776876104769e+02 2.614428154999528147e+02 1.461523265575596042e+02 2.304914832379158156e+02 2.502987607420118934e+02 2.474276046141548875e+02 1.739607960146905725e+02 2.098700376203710789e+02 2.373226438948917121e+02 1.258493219462072119e+02 2.692932028872633055e+02 2.819145908444669999e+02 1.941653933285864468e+02 1.666395497972145847e+02 2.371919109091950588e+02 1.978302896313488191e+02 1.951483674191611613e+02 2.694357972099330141e+02 2.387068160427941450e+02 2.826084316255729618e+02 1.350954172043159929e+02 1.414479610501084039e+02 1.407657276334374501e+02 2.725513503737778365e+02 +2.055761393809777360e+02 1.070553196069381556e+02 1.045726024365074096e+02 1.611577217417760153e+02 1.258091705742062629e+02 1.038769334534844120e+02 2.956016304760584035e+02 1.586570076132481972e+02 1.636816353299032585e+02 2.375674325770941095e+02 2.085436646116971531e+02 2.088922128397473443e+02 2.316234644183506930e+02 2.623581653234684268e+02 1.714245300492981698e+02 2.844387943099641234e+02 1.469270259610659650e+02 1.157700922187784727e+02 2.367694595159086361e+02 1.548671738744121740e+02 2.013687686570863207e+02 1.860374943080277887e+02 1.733446602950305930e+02 2.488507085609763010e+02 2.929099979257852056e+02 1.825615338506695480e+02 1.338575452835397925e+02 1.491478381149757979e+02 1.116052925520655066e+02 2.341983606431906537e+02 +1.014445800974648222e+02 2.539987638010908597e+02 1.871788778457793399e+02 1.454231386314719998e+02 2.284640297096368045e+02 1.174773591296971915e+02 1.395683165851895637e+02 1.137193571402578414e+02 2.370662356797280950e+02 1.767292649815032064e+02 2.688513591587910696e+02 2.913902923086397436e+02 1.122392290694582897e+02 1.366157623619356229e+02 2.667409125457835444e+02 1.834435599491967537e+02 1.437174343391732236e+02 1.130622879516462120e+02 2.898543289046954214e+02 1.559795378531963479e+02 1.765577834073310157e+02 2.422955620302867885e+02 2.384835032255701321e+02 1.708163174135501094e+02 2.012159081107001839e+02 2.825663186839160517e+02 2.627299211659199045e+02 2.173916205317264883e+02 1.878835852278120910e+02 2.578733373077019451e+02 +2.843897417914848802e+02 2.685865547709703378e+02 2.810255710736182664e+02 2.572690897085278152e+02 2.416998564827035523e+02 1.770932574976374099e+02 2.021652319180342943e+02 1.414744641219446351e+02 1.464677002516696405e+02 1.831165552459343644e+02 1.157177632931430651e+02 2.625289386264841482e+02 2.972225480003540952e+02 1.024156386789293265e+02 2.305099741095138768e+02 2.241903749843916671e+02 1.157222019118702292e+02 1.533205318359311775e+02 1.179505454242311799e+02 2.666741766563739020e+02 2.792728900733587238e+02 1.222170248460037811e+02 2.573772727215269924e+02 1.535874607134987286e+02 1.231830862844115728e+02 2.584552954023608891e+02 2.541883057030129862e+02 1.001259630352790566e+02 2.332879439260797767e+02 2.240027888381033563e+02 +1.537092645679641123e+02 1.737278083620151392e+02 1.736358049797527201e+02 2.251608985235982630e+02 1.812387130195175473e+02 1.605621432944637377e+02 1.880655312831545700e+02 2.234500385148787700e+02 1.156918728696038272e+02 2.243685096423413654e+02 1.934342626327970720e+02 1.850952349553267027e+02 2.629944548485545965e+02 1.410418270562070973e+02 1.442479234012843960e+02 2.244518961458842909e+02 1.350755563946989923e+02 1.207094763037939913e+02 1.849900977633715797e+02 1.712315707730903398e+02 1.136025349108833495e+02 2.266901327137990734e+02 2.049289406654929735e+02 2.168279721613268407e+02 2.802488024880154285e+02 2.288593244920211873e+02 2.512942787545493957e+02 1.605416563468323261e+02 1.449848598254574483e+02 1.444073785399158396e+02 +1.576600406756634243e+02 1.316580100950168912e+02 2.530050469343043460e+02 1.319013133578224028e+02 2.708693079386434306e+02 1.256852413190491689e+02 1.471714019119002046e+02 1.119112141125198576e+02 1.482405279774543772e+02 2.151504825709631064e+02 1.449998801809978488e+02 2.163638771503673581e+02 1.272949254250747657e+02 2.476027791419436141e+02 2.891208457332292028e+02 2.642744540427622724e+02 1.972643066216432999e+02 2.480891057982425423e+02 1.265454595896786003e+02 2.957735252703171227e+02 1.831389323451852533e+02 2.674516147697771657e+02 1.404389674972707667e+02 1.350952754772052913e+02 2.169062951790871807e+02 2.445227715623778408e+02 1.771545655819627427e+02 2.729961759152714649e+02 2.655105689521545855e+02 1.887977700062222084e+02 +1.336462666694000632e+02 1.333709897858500995e+02 2.263366393511863350e+02 1.847175439991091821e+02 1.121699721143812383e+02 1.985314153845103533e+02 2.097626398761568396e+02 1.994292542548276970e+02 2.119822099620050722e+02 1.121578896112172430e+02 2.285640262135607372e+02 1.530452060058861719e+02 2.280757825791220625e+02 1.002584314437652893e+02 1.549763597162410349e+02 1.962603185897801836e+02 1.520023734031539107e+02 2.188357004065238129e+02 2.078620274892635678e+02 2.253215106546470281e+02 1.707542413836397373e+02 2.818584030117174279e+02 2.256862624833151472e+02 1.123882683852972377e+02 2.188298604829752776e+02 1.623779544769217296e+02 2.272253780943444212e+02 1.236449568833132560e+02 1.456708971140968174e+02 2.173334506159979753e+02 +1.355111076933105210e+02 2.882277378633141325e+02 1.458332953325788139e+02 2.038461345794760007e+02 2.077052275373579278e+02 2.430957456359013804e+02 2.398926697516154150e+02 1.861334604823129553e+02 1.056851094080089695e+02 1.250491536199931772e+02 1.475324860190441427e+02 2.446126161547439324e+02 2.283994822545897705e+02 1.411463500178549850e+02 1.017206978570942510e+02 2.805514386584911790e+02 1.128847993259780083e+02 2.326583828053989862e+02 1.968387029218569069e+02 2.013375618903088480e+02 2.981010702857409456e+02 1.018614681114941902e+02 1.799507821883679526e+02 1.133741465580100396e+02 1.235533581072856038e+02 1.980629645203880500e+02 2.289642287691829097e+02 1.596082722591768288e+02 1.905110471998515322e+02 1.789448781159623820e+02 +2.588286452268601465e+02 1.978130463173739599e+02 1.052689337312009599e+02 1.316763830509305251e+02 2.659236586726388509e+02 1.637014132384438767e+02 1.416031833329826668e+02 2.638665530652568236e+02 1.007257384115875425e+02 1.143900271701907769e+02 2.977834670475828602e+02 1.589765734727692745e+02 1.903975572290986520e+02 2.371635535037608804e+02 1.840341975670916668e+02 2.047003785265828242e+02 2.798969769773281655e+02 2.731706896262927557e+02 1.266878907904394254e+02 1.882415083052244427e+02 2.273996647906652129e+02 1.051754139634791869e+02 1.949647447346334843e+02 2.153583447980240919e+02 2.763468452623635585e+02 1.126493843527773322e+02 1.566047572050934491e+02 1.655928523150526246e+02 1.733528322945315949e+02 1.292815908595541146e+02 +1.453195062153936874e+02 1.443849872704900008e+02 2.393030362110915519e+02 2.203850914291498668e+02 2.628192548589183275e+02 1.142161203389242132e+02 2.954875947743198594e+02 1.914138981839176950e+02 1.956478457154231023e+02 1.282875398486639824e+02 2.801001077571227142e+02 2.478095646281364566e+02 2.467477848581343949e+02 2.819656424464902784e+02 2.951823714077539194e+02 1.777239847229775478e+02 1.197979896746704185e+02 1.481181033052623661e+02 1.906710229153984528e+02 2.142395628283543658e+02 2.300980272040501973e+02 2.228884003748859186e+02 2.473330601440014220e+02 1.391193242835927322e+02 2.836257563055140736e+02 1.510096324299383923e+02 2.202302141125946946e+02 1.931468179284185851e+02 1.332427495686727639e+02 2.591048546650930575e+02 +1.878681542531208208e+02 1.576240359584147654e+02 2.653849736815447500e+02 2.963544993865212973e+02 2.044592436730770828e+02 2.022626486161902903e+02 2.692262675681025144e+02 2.660999355751699227e+02 2.275843495473382347e+02 1.090849337992742818e+02 2.095602584555617227e+02 1.896271059113536808e+02 1.103822849104477513e+02 2.916911739044173260e+02 1.131212278363718582e+02 2.998892666268029643e+02 2.476782245756396605e+02 2.259689579913920738e+02 1.853942231198421950e+02 1.358270117521841200e+02 1.538630682720535674e+02 1.002148317174243601e+02 2.538393939061405433e+02 1.631649956267838206e+02 2.086654853664906000e+02 2.065167771482954322e+02 2.184161808630845485e+02 2.204789814939956045e+02 2.876785893506615821e+02 2.415299687386639675e+02 +2.578989465605797591e+02 2.309888943086805853e+02 2.139372792253111584e+02 1.438019921733897775e+02 2.686852572045135616e+02 1.347038004304963579e+02 2.662658866335509060e+02 2.378358170108797367e+02 2.901455078003721155e+02 2.653867524737770509e+02 1.011162296015096302e+02 1.236447329941733528e+02 2.440241295351771669e+02 1.285889645706482725e+02 1.234088480316093808e+02 2.765916670935633874e+02 1.132915304101479421e+02 2.967043774237617413e+02 2.960414394814537786e+02 1.923965028192617410e+02 2.177448618307050765e+02 2.328047369831131732e+02 1.702256773965170282e+02 2.320080409490440729e+02 2.962065584958517093e+02 1.421971909775941185e+02 1.416181340866144183e+02 2.318260414882616374e+02 1.990521696869427046e+02 1.291045564046920333e+02 +1.562042774178686386e+02 1.004265446278790392e+02 2.987714610921041185e+02 1.843637355858842284e+02 1.975513718825063165e+02 2.869996482942455032e+02 1.598134132589713943e+02 1.814921031876193638e+02 2.433389905907341983e+02 2.220363745053336970e+02 1.548306942100590504e+02 2.274512269554506361e+02 2.173006200058655963e+02 2.139515436667214772e+02 1.820439741095771353e+02 2.954110718222074183e+02 2.706126458816278273e+02 2.546812106115172583e+02 1.499899738326257363e+02 1.498010641912065921e+02 1.897725780579399668e+02 2.531561160917130167e+02 2.568891780637028432e+02 2.223136077092870551e+02 1.518604819103856585e+02 1.610422120589223027e+02 1.090455809489133259e+02 1.950503873748027388e+02 1.235704160644129388e+02 2.711492093024702967e+02 +2.039597038432034424e+02 2.026680584622021684e+02 1.365818873512059213e+02 2.909476552420245525e+02 1.721994194158640425e+02 1.854386667051114443e+02 2.287109571295530372e+02 1.912591665763447963e+02 1.607322994166321450e+02 2.949516230628389053e+02 2.522065912002103403e+02 1.869433122585654701e+02 1.235797649248940644e+02 1.522422059501078024e+02 2.738245135411146975e+02 1.059681837441489307e+02 1.013027238331489173e+02 1.660100598156148237e+02 2.454471731623151243e+02 2.467503196183328100e+02 2.584564749953993896e+02 2.079587352810677316e+02 1.650926041957846451e+02 2.269719270682073784e+02 2.376254891983122093e+02 1.510146656008620596e+02 2.672848371954185041e+02 2.692845974117340688e+02 2.180714754246087921e+02 2.186797802447831884e+02 +1.704231257711912519e+02 1.993416036368699906e+02 2.293703655438095268e+02 1.494582642918422266e+02 1.988970317734676030e+02 2.329763291241497711e+02 2.594871448385057420e+02 2.168089936885102134e+02 1.825320854593447280e+02 1.816754553181755796e+02 2.164740515812325725e+02 2.676208645391697019e+02 1.298365075936954725e+02 1.802664596093496243e+02 1.015344620621038132e+02 1.955048336384612639e+02 1.938953913674110083e+02 2.716932071347151805e+02 2.391085978949223829e+02 1.852300387899809380e+02 2.933293185307651356e+02 2.502753353909542966e+02 1.326128348575908262e+02 1.132638325194699433e+02 1.382024010322260494e+02 1.899310337488860796e+02 2.577639546186944699e+02 2.130234590296898887e+02 2.056292296528304746e+02 2.070746044453983927e+02 +2.712524956603344890e+02 1.103212761114690750e+02 1.501201791543782917e+02 1.588084859702673555e+02 1.780379814134324192e+02 1.938691258391782810e+02 1.322057441019641146e+02 1.105823874551086590e+02 2.879365916037821194e+02 2.457617763012990224e+02 1.036189749330240488e+02 1.682919366264929124e+02 2.271749409116763161e+02 2.468308259697249127e+02 2.530034131464132088e+02 2.481420904342841709e+02 1.546080547019561209e+02 1.278414739842506265e+02 2.234886960240669111e+02 2.535365186455997843e+02 1.599130733896959669e+02 1.151371295028686035e+02 2.378656188176093451e+02 2.901072209563180877e+02 2.524076257924749882e+02 2.849501171254129304e+02 1.802791659856764568e+02 1.527418387706650833e+02 2.578820596338672431e+02 1.208856989199291263e+02 +1.884906470590645711e+02 2.304295185581007672e+02 1.035923344330140736e+02 1.647061655195892627e+02 1.910201770870304472e+02 1.752788518438422614e+02 2.763014227316762117e+02 2.545709641405486252e+02 1.642694881393259152e+02 1.850698110761380804e+02 2.423689469305483328e+02 2.821007056776016384e+02 1.440765548977453250e+02 1.082195827231368952e+02 1.292487205530619008e+02 2.136496853657876613e+02 2.268509220579896635e+02 2.999629735037570981e+02 2.135306905316524535e+02 2.807718279523737692e+02 1.079256111018183759e+02 2.233050677333321801e+02 1.960571416898615951e+02 2.930642308139058514e+02 1.350490077967585307e+02 2.626074042719769750e+02 2.812196827814445328e+02 2.812753678081913336e+02 1.893738913514469004e+02 1.237248675858835725e+02 +2.024005284879252144e+02 2.663611407988397559e+02 2.687079844301063076e+02 1.583164038086077312e+02 1.451019436850150441e+02 1.100558451420041450e+02 2.083655450975085159e+02 2.034012033819327598e+02 2.745375932717230398e+02 1.454718097055225599e+02 1.519068131933423729e+02 2.522666952972969625e+02 2.409340029943109300e+02 1.697386944425205115e+02 1.092659514648129289e+02 2.785598218078254149e+02 1.404092026094307357e+02 2.152301424167146990e+02 1.170396027347833723e+02 2.495323893679063474e+02 2.070836095469416591e+02 2.187978925167305135e+02 1.478606128149070855e+02 1.189323178954538207e+02 2.012925160284665651e+02 2.080878545398990127e+02 1.510128433840351647e+02 1.657302151838663065e+02 2.177026636795220043e+02 1.221198981216710422e+02 +1.411258561955272341e+02 1.419717097672817374e+02 2.247481951315160984e+02 2.805973971111802712e+02 2.755562061324142178e+02 2.039769327420251557e+02 2.994080883760036045e+02 2.417843309736466040e+02 1.023751441731232319e+02 1.491356884971497152e+02 2.542464200475323821e+02 1.496044144381669128e+02 2.829129207809560285e+02 2.479316882407134699e+02 2.441205876677642550e+02 2.045492313770996020e+02 2.855582203360229414e+02 2.884005586284110336e+02 2.039668453101600676e+02 1.690279206477617890e+02 2.136822090795746760e+02 1.254275901194574772e+02 1.084851042192170922e+02 1.656011685190305229e+02 1.415195951026897774e+02 1.578115814760412263e+02 2.619737257057257693e+02 1.492347147839753347e+02 1.627213988646173561e+02 1.343297485726322691e+02 +2.544675070683062756e+02 1.367461330002975899e+02 2.928364121110963652e+02 2.024865028281971036e+02 2.758937379397792142e+02 1.293527538914390220e+02 1.003170531204512059e+02 1.514803620238746760e+02 2.603616046431354789e+02 1.790387290949859960e+02 1.954717187769221027e+02 1.325226280128280223e+02 1.522166198122710625e+02 1.162911821325583048e+02 2.798489406348742250e+02 2.521718932296424498e+02 2.622327475379161115e+02 1.027798265388270949e+02 2.437256510683693023e+02 1.911771820917219884e+02 2.722604457055863350e+02 2.850557929858495640e+02 1.953760157441756746e+02 2.473572905253965644e+02 1.891404804097296051e+02 1.514672503279451803e+02 2.213565012031598940e+02 2.253356064978207769e+02 2.044629345029305227e+02 2.805872739342098612e+02 +2.859142434488251183e+02 1.016009480575973356e+02 1.779351649172412522e+02 2.205171340775500539e+02 2.104472905774927369e+02 1.755755724600441567e+02 2.751836189782782185e+02 2.820692049982218350e+02 1.337557428916256015e+02 1.569761138230965969e+02 1.991757527032745543e+02 2.615974376894962461e+02 1.944849272958306017e+02 1.868411694165790777e+02 2.994394032068257729e+02 2.802783326794233290e+02 2.693871918204162625e+02 1.750293298802730249e+02 1.468161278725061720e+02 1.272003326865558108e+02 2.233103517167062932e+02 2.103066399402185027e+02 2.720825853079193735e+02 2.728915492341989193e+02 2.160004538807991992e+02 1.325145501710478015e+02 2.549827549782140466e+02 2.921469675413995901e+02 1.846231529604695822e+02 1.391152989663993651e+02 +2.538717579982014456e+02 1.450483481068324352e+02 2.720200816305956550e+02 1.120834821105324011e+02 1.703801876168104741e+02 1.091293661435919233e+02 1.410263490040598526e+02 1.910022197757120352e+02 2.505223413771657022e+02 2.069613533172621374e+02 1.367200764291426935e+02 1.269156762039037574e+02 1.459486945063737267e+02 1.585863332989725905e+02 1.433846106215619329e+02 2.893202513225785424e+02 1.754070497414596730e+02 1.678900237854272746e+02 2.363821059303507752e+02 1.088858921730617908e+02 1.962435837543239927e+02 2.151311182954276831e+02 1.943029551670006754e+02 1.670799798236046172e+02 1.348235227224938910e+02 2.005836112104490212e+02 2.601588534628079969e+02 1.194827586439497935e+02 2.131891535893303740e+02 1.835674362703964277e+02 +2.872207377280434457e+02 1.680389491751975299e+02 2.268072198735419533e+02 1.324343035526375729e+02 2.746241572770433095e+02 2.142161570690199710e+02 1.852290440736100550e+02 1.772431485621305285e+02 1.144750125154023266e+02 2.162070901557998468e+02 1.490690769171257557e+02 2.904041493178549445e+02 2.673617561413327621e+02 2.904362235840736730e+02 1.438791831406123833e+02 2.596893065528289526e+02 2.617155941751458386e+02 2.388486986717779246e+02 2.718819501315180105e+02 1.265484539827731680e+02 2.508989305854047700e+02 1.677208481362706323e+02 1.527665277518251230e+02 2.069026506407369084e+02 2.223100964495413336e+02 2.859845330217733022e+02 1.430291068893224349e+02 1.186508486537613436e+02 2.043257492072551713e+02 2.909823892985461953e+02 +2.385945641230763670e+02 2.011887933217761031e+02 1.622448188725907983e+02 1.738874847453056987e+02 1.669498482708885376e+02 1.853462372214463016e+02 1.514500885098960907e+02 1.569159134451362547e+02 2.521399095730983504e+02 1.246878140446721659e+02 1.758330561641313352e+02 2.722601647479554003e+02 1.679012078705679869e+02 1.710944469563905272e+02 2.012619557548435978e+02 2.130692925302264200e+02 2.489118511754019778e+02 1.553758318484749452e+02 2.531318516516165857e+02 1.895498740333992487e+02 2.010265603399928409e+02 1.805605111948569856e+02 2.471772127430102159e+02 2.822665908577009759e+02 1.256656757093761314e+02 1.218957078832023626e+02 2.851942693987446660e+02 2.434079459678487751e+02 2.183256665756584312e+02 1.881473862468819220e+02 +2.878274557836845133e+02 1.654481949983921254e+02 1.215681808546938214e+02 2.567820905945674781e+02 2.104106688330284101e+02 2.960796083414018085e+02 2.020680111052573693e+02 2.328934707961639106e+02 1.081575190462602336e+02 1.003340046261853189e+02 2.009697278729638299e+02 2.231963192062537757e+02 1.203849639323555323e+02 1.187994179134823156e+02 2.211937485225296030e+02 1.667300587261732119e+02 1.727379541915926211e+02 2.085029285798690353e+02 2.440827389167183981e+02 2.864522928573259151e+02 2.974890568790378893e+02 2.102945085846974393e+02 1.972598274048171447e+02 1.762889209976547136e+02 1.346946323322499666e+02 1.554434255958064170e+02 2.915634104756007901e+02 1.434053307556222876e+02 1.055800565037633163e+02 2.043924431141962259e+02 +1.494596010135965116e+02 1.369114048625681335e+02 1.414146701131132886e+02 1.383970135097982848e+02 1.734304788623498155e+02 1.594301265610334610e+02 1.040146208229407137e+02 2.208381597698417806e+02 2.904998286250861383e+02 1.300157615397056929e+02 2.667076669416877621e+02 1.062418844419948556e+02 2.717657999079561364e+02 1.054097765488278640e+02 2.401074677516734823e+02 1.045408432466875297e+02 1.330046749931937882e+02 2.297648034226271534e+02 1.488059718063634307e+02 1.725671935994615183e+02 1.330818497812682608e+02 2.341687919103425770e+02 2.983144736799429211e+02 2.798846823197050071e+02 2.218705077010061473e+02 2.681931695329894865e+02 2.339384973461015420e+02 2.893058480095726281e+02 1.539801301873031321e+02 2.746688360458649640e+02 +1.330701439354522222e+02 1.727884450558678395e+02 2.309082669627648272e+02 2.027633892073664299e+02 2.725503026364725656e+02 1.999882667367585896e+02 1.904108867169430255e+02 2.952458047945178805e+02 2.903769421220866320e+02 1.593020200554085477e+02 1.236139458806368623e+02 2.670862420061573062e+02 2.910830183895285472e+02 1.860711175093342149e+02 2.161724988935532963e+02 2.564488756979296795e+02 1.231566645138573648e+02 1.554206254375235403e+02 1.148558104746345521e+02 1.512714227454516163e+02 1.953024826710307025e+02 1.296022137194406127e+02 1.500450396815122076e+02 2.611742573447975246e+02 1.601671705158374550e+02 2.391666762859087214e+02 2.566415095930981352e+02 1.923304801412870404e+02 1.194174883996373353e+02 1.970722090829630986e+02 +1.912113734453868688e+02 1.498407015577022605e+02 2.038188614169363007e+02 1.315017316695561647e+02 2.564290419741012101e+02 1.890015309531812022e+02 2.451565642315005960e+02 2.794356592632736920e+02 2.286941218755985972e+02 1.959549984609147941e+02 1.183834182035568716e+02 2.102820643179567242e+02 1.748108698585573393e+02 1.534379248653211221e+02 1.919662859034699522e+02 1.273611408042816464e+02 1.848163823983119585e+02 1.719445827292381637e+02 1.098466009889928898e+02 2.781108902268393877e+02 2.089286134506138524e+02 2.324518337977864348e+02 1.983840049195213169e+02 1.897881971862217370e+02 1.057077761008814605e+02 2.693629461665184408e+02 1.359710117509105487e+02 2.191184409971657487e+02 1.295811391257115304e+02 1.272165218667991553e+02 +1.987244486959793903e+02 1.516360617950651317e+02 2.198509518241761498e+02 2.494181713303175911e+02 2.903223989223247372e+02 2.847249789220907132e+02 1.747037051964282171e+02 1.610307305098726829e+02 1.866621867053561061e+02 1.016530888490581503e+02 2.606194448419089440e+02 1.820037020201941402e+02 2.650669443765450524e+02 1.137210849453726098e+02 1.329244106101075715e+02 1.741312140090854257e+02 2.301425980066611885e+02 1.051708772384664030e+02 1.994040172335078864e+02 1.874773290907829733e+02 2.745616984783777070e+02 2.354781865911449756e+02 1.598287033335407159e+02 2.650689470710170212e+02 1.643692352330562017e+02 2.991199217036622713e+02 2.713535332162406348e+02 2.516280148665988463e+02 1.124367393830256532e+02 1.725070309959049837e+02 +1.637875882282461077e+02 1.407642428016634426e+02 2.759741260511348173e+02 1.982469453863400304e+02 2.966736241669494802e+02 2.756530253528777052e+02 1.426661371226006167e+02 1.585144634205103102e+02 2.836415355000413001e+02 2.468213340046699784e+02 2.898204535963063790e+02 1.711408259966125343e+02 1.900542569026269177e+02 1.112151031999617032e+02 2.679918109779015936e+02 2.737346364036235400e+02 2.597479311885246602e+02 1.719445390286030886e+02 2.361360157374418236e+02 1.123330408578339785e+02 1.214203690485689719e+02 2.552722899309185891e+02 2.436705678248840456e+02 1.596697357728296254e+02 2.533254006866929444e+02 2.066863222258713790e+02 1.194370826184286329e+02 2.943584774485435673e+02 1.636272134478143130e+02 1.191267138602315185e+02 +2.350924626651462006e+02 2.263138093076711357e+02 2.206572605284771385e+02 1.704171521239532296e+02 2.000250897638135257e+02 2.966317084215347109e+02 1.350543763227695138e+02 1.248113195978286285e+02 1.480602782771696297e+02 2.391913401309390679e+02 1.908758915801345779e+02 2.476074601271855045e+02 2.408834383325319095e+02 1.009169451940341560e+02 2.567526834523320645e+02 1.791854948779896688e+02 1.412277552146151152e+02 2.660711025781407670e+02 2.073940326990519054e+02 2.509760072499196610e+02 1.358593750308925223e+02 2.127422683140523532e+02 1.874643773621423293e+02 2.844455725631112273e+02 2.197223292953194118e+02 2.049519862750077266e+02 1.674367936692717365e+02 2.806316262053937294e+02 2.040091003350897836e+02 2.675290975004411962e+02 +1.483513543637005796e+02 2.384008274111940011e+02 2.834409911154408519e+02 1.344593118283445392e+02 2.346883831968173979e+02 1.381882879805813218e+02 1.241165074750676638e+02 2.186327911062819567e+02 2.466602279029802673e+02 1.573094529523951906e+02 1.568918412618390903e+02 2.289205163045023710e+02 1.170165333644822283e+02 1.742406104080407658e+02 2.082974381484526702e+02 1.600869123712819260e+02 2.399160913983472199e+02 2.877189278027444743e+02 2.845252294036096146e+02 2.342337907657317544e+02 1.496264758341107779e+02 2.905797831387872066e+02 2.824703799011629144e+02 1.047015685176013307e+02 1.056531628249932169e+02 2.778559625738202499e+02 1.693549799118289343e+02 1.654193764711911570e+02 1.062077606699500762e+02 1.159643419206647792e+02 +2.694780377267857716e+02 2.229138360502907403e+02 2.407432883969363218e+02 1.240072643521201741e+02 2.128611568148922970e+02 2.114050669978733481e+02 1.042337934877265297e+02 1.044783539591350490e+02 2.706611056394938259e+02 1.972285130309975898e+02 1.959046941044780681e+02 2.915493579522836853e+02 1.131994346897827342e+02 1.197362406389762839e+02 2.877593780027675621e+02 1.089470964294721824e+02 1.996015695685267417e+02 2.185569019121031999e+02 2.102686704320404374e+02 2.955299037924150980e+02 2.987478446256551479e+02 2.517129931888254646e+02 1.552463625479420557e+02 2.295020326441428153e+02 2.886454895961533111e+02 1.869792800456660871e+02 2.703426621835664037e+02 1.873514421416134326e+02 2.714620374401066556e+02 1.623625260081516331e+02 +1.457420078291350194e+02 1.926195242081234369e+02 1.841639049563959247e+02 1.397830290030836125e+02 1.287503203163068406e+02 1.684614546803193775e+02 2.820658047345126533e+02 2.986548244924653090e+02 2.631399932039782925e+02 2.870930868530864473e+02 1.141938207690214426e+02 2.868552010662050407e+02 2.019110175402121286e+02 2.840219745246005232e+02 2.848478851173646262e+02 1.902287203163165259e+02 2.696968940302964484e+02 1.690355482825476656e+02 2.171695948786692725e+02 1.960363641465239652e+02 2.930566891688549731e+02 1.380341365242818483e+02 1.769912313914243214e+02 1.164985277343077996e+02 2.079184380436491324e+02 2.871364788135472850e+02 1.796231479741346391e+02 1.115892945700443875e+02 1.922852518794877028e+02 1.851500906627327083e+02 +2.894943401361737187e+02 1.972990286414578804e+02 2.801948561309920933e+02 1.993490085147259947e+02 2.539099743775018112e+02 2.972486389690005240e+02 1.162404922698449354e+02 1.801898545246462504e+02 1.283416456049016858e+02 2.289248555429664407e+02 2.419505668531598985e+02 2.755101537543703216e+02 2.786083442131507013e+02 2.461931811431258552e+02 2.699066237266536064e+02 1.088542193903703179e+02 2.302113104476973149e+02 2.158136503417114227e+02 2.797451432348925096e+02 2.832754349673875822e+02 2.207567008139471909e+02 2.920947868166995249e+02 1.300092217647513735e+02 2.953259288980694350e+02 2.539624465668687492e+02 1.304833679125420645e+02 1.051395153781939484e+02 1.855592224876973830e+02 2.160289702497469477e+02 1.227895712666205981e+02 +1.029685235386965587e+02 1.410297052380113882e+02 1.832105986621241982e+02 1.016727951098498579e+02 2.130361696974732126e+02 1.817578553203918830e+02 2.644724203174304193e+02 1.713346250427240420e+02 1.297164370175517547e+02 1.072810924841072193e+02 1.083932811014470161e+02 2.860684171745337494e+02 2.893854146138399983e+02 1.677808320623732925e+02 2.343535290724524600e+02 1.209564642240636090e+02 1.329537830609780542e+02 2.924542956964438645e+02 2.733376468658280487e+02 1.397146179999238598e+02 1.103570089598620285e+02 2.231457082965310690e+02 1.056672424832338635e+02 2.887779644840117612e+02 1.127167878193751704e+02 1.387640376146708263e+02 1.791595456124304633e+02 2.709107895779202408e+02 2.238624693992912569e+02 1.773395240564728397e+02 +2.317578772498348769e+02 1.294950944138938667e+02 1.126253428029936572e+02 1.371351849575549693e+02 1.785990678455200964e+02 1.021081186758702444e+02 1.471984209931611360e+02 2.907355141803875540e+02 1.881128962816476644e+02 2.776434621780599628e+02 2.231668573818950279e+02 1.905362514139340817e+02 1.921875823712000226e+02 1.027725913116546792e+02 2.939602582690168902e+02 1.776540079128602656e+02 2.761214484196684111e+02 1.042033722248946646e+02 1.812858538041361385e+02 1.739774673118114663e+02 2.626640185867897799e+02 1.702975408841979288e+02 2.558138050153142729e+02 1.733257751657050392e+02 2.918973111180089859e+02 2.499103812623473857e+02 1.210050998380505973e+02 2.819910650801346605e+02 1.887952629909842699e+02 1.910084514453274380e+02 +2.212539479167726029e+02 2.774434360961662378e+02 2.337566454731646104e+02 2.345785537275947661e+02 2.365459264006348405e+02 1.983982238092833086e+02 2.030822332599765332e+02 1.995891111618029186e+02 2.834365683300363798e+02 1.036872616932399609e+02 2.192093181482490252e+02 2.601252995545215754e+02 2.498786393235831724e+02 2.102914196276636858e+02 1.344974807588668000e+02 2.319076536245909210e+02 2.769341510052834110e+02 2.705990780330756138e+02 1.679097240924248240e+02 2.394521666103182724e+02 2.042111123157340842e+02 1.679545908808316028e+02 1.638112120198904051e+02 2.498667640522866407e+02 1.298749690282424183e+02 2.953546510122243944e+02 2.420377599473625025e+02 1.972281420856064642e+02 1.511153679243939223e+02 1.785899871179086063e+02 +2.568297621323404201e+02 2.469847896802298237e+02 2.766623631158322496e+02 2.476135901735717937e+02 1.788596740963971570e+02 1.849716544556056874e+02 2.568516536462929594e+02 1.692762419184084877e+02 1.468834240718183537e+02 2.716053370235183593e+02 1.674083895790932957e+02 2.340636951853666687e+02 1.637725360284847227e+02 1.316562872243186177e+02 2.850086566701365882e+02 2.066513343106022944e+02 2.990778363456342390e+02 1.780020440519503495e+02 2.906711993591478631e+02 2.149926413975278479e+02 2.151504627144789765e+02 1.458362697904619836e+02 2.339644011324822657e+02 1.740513991402896181e+02 1.804876886135730842e+02 1.706585538790989176e+02 1.113370339871644603e+02 2.032819788543359039e+02 1.225434838619497526e+02 1.558188197132453183e+02 +2.752385657001058803e+02 1.704994416021052643e+02 1.607090409105587696e+02 2.031247490318933444e+02 1.333383797740430339e+02 1.922643047184382112e+02 2.665685682619526915e+02 2.611043497447243453e+02 2.444450591022788615e+02 1.012899678037660181e+02 2.236752860048796947e+02 1.164606756896235993e+02 1.768812782093617955e+02 2.532808672341815850e+02 1.308823477633827395e+02 1.683394957344131626e+02 1.787390150786144716e+02 1.962681762314343530e+02 1.178176219749694980e+02 2.151624908275416885e+02 2.951256579216935734e+02 2.058583926262361388e+02 2.348769662163374790e+02 2.500118096543036472e+02 2.065978549387351109e+02 1.732426267043477139e+02 2.575950640438621804e+02 1.826939497339359946e+02 1.586062531006688801e+02 1.141086110094916819e+02 +2.107478059550890066e+02 1.212326460542207940e+02 2.154852140069355073e+02 2.624147598788578648e+02 1.169795422214265699e+02 1.682202484364929660e+02 2.987700686247625299e+02 2.259973608163532504e+02 1.912690930240648015e+02 1.896338093439390775e+02 2.747727757049322008e+02 2.388804299971102978e+02 2.538821160842531128e+02 1.839990833334872491e+02 2.839611350159472067e+02 2.953225980324958755e+02 1.674336071760058076e+02 1.609172697163818953e+02 2.902596210806400450e+02 1.513824951234124114e+02 1.873458283487339600e+02 1.695960935104061491e+02 2.116215526550050470e+02 1.849422962892989233e+02 1.434256749723924713e+02 1.304784783123307079e+02 2.632948417544853328e+02 1.656472047377057777e+02 2.303125851744007377e+02 1.681993961373014486e+02 +1.104191565760665128e+02 1.750924257030650040e+02 1.242494131306669090e+02 1.541741282893887899e+02 2.585460716706878657e+02 2.286423505464783261e+02 1.890990979891397501e+02 2.707781238779197679e+02 2.619171833457787670e+02 2.695823002806438353e+02 1.941989480397771786e+02 1.389058748786196134e+02 1.283479072532797431e+02 2.347481590897206729e+02 1.518985431591505630e+02 1.757095590143896402e+02 2.225334593093496096e+02 2.231309387578290568e+02 1.039310896134069395e+02 2.614149485334186238e+02 2.212890027388380076e+02 1.425609106790709859e+02 1.376620423520403733e+02 2.403640719649376933e+02 1.152284694789922526e+02 2.108068210397188409e+02 2.526640691383259991e+02 2.323633859683563969e+02 2.720522122905912283e+02 2.498034621012949685e+02 +2.223449436042899947e+02 2.823923482876032267e+02 1.728419664392092727e+02 1.542710015610415724e+02 2.699062389875002737e+02 1.776741825057288793e+02 1.800001384193664080e+02 1.819433000632012636e+02 1.436484983468620840e+02 2.344086094824976954e+02 2.824459866922626361e+02 1.860318500101035681e+02 1.749968777772715498e+02 2.792448396035428004e+02 2.134719239619671498e+02 2.649346822194891047e+02 2.535109715864082602e+02 1.651109960016319178e+02 2.407385671793928736e+02 2.276937454871455770e+02 2.965404491761371446e+02 1.771850291606413634e+02 2.317902380753697855e+02 2.233400563607936817e+02 2.471010629200553694e+02 2.999085009765063319e+02 1.263611681933084725e+02 2.954593528043474180e+02 2.279026703099021915e+02 2.630592311905735414e+02 +1.662671322607742752e+02 1.600442354914371208e+02 2.476541290397616137e+02 1.471310870365195740e+02 2.302232198157895198e+02 2.833854716762933776e+02 1.464787719165046553e+02 1.913553080525503560e+02 1.014594285276723156e+02 2.182963956218923158e+02 1.629807715448000636e+02 2.692152036144454428e+02 2.287521686048013976e+02 2.982465613581407524e+02 1.646080094271899839e+02 1.685350412843276899e+02 2.638506951547767585e+02 2.931520510309920837e+02 1.395453733045734168e+02 2.192750645467382355e+02 1.118562057344099543e+02 2.210439168983162972e+02 1.977199388190010438e+02 2.248771354041466566e+02 2.967583759675493411e+02 1.144799677712354793e+02 2.877369511761256149e+02 2.831237961244747225e+02 2.909105411130262269e+02 2.550977837950437390e+02 +1.519738194711488006e+02 1.042788193386050608e+02 1.298121344332743377e+02 1.827398187867084971e+02 2.371985543371917800e+02 1.647119082252074236e+02 2.792046599520904238e+02 1.737333830141970452e+02 2.019611337599129968e+02 2.402390448779260623e+02 2.107045415433176174e+02 2.447101973248666411e+02 1.584507446746840174e+02 2.877533155913679366e+02 1.209142860803932251e+02 1.903846717728129931e+02 1.485923447895592631e+02 1.040627746119376695e+02 2.329784390325348795e+02 1.136264746597146882e+02 1.019818146651219024e+02 2.395077159260278847e+02 2.571474008697522322e+02 2.507839876514990465e+02 2.649762964978717719e+02 1.398370322453145889e+02 1.116668292809188614e+02 1.262068209877756289e+02 2.561228606182183967e+02 1.019925993853918413e+02 +2.525550526067758881e+02 2.649927164229666232e+02 1.457764901336312846e+02 1.519121804298574148e+02 1.112983565335166247e+02 2.979018464293943680e+02 2.517559946611144142e+02 1.257251989750113239e+02 2.377842966816966737e+02 2.692916709774201536e+02 1.558791612193160745e+02 2.988101508442036334e+02 1.264682305510686575e+02 2.586186621657187743e+02 2.397705732393993969e+02 1.799773948514575750e+02 2.289212202830902072e+02 2.551439950194432242e+02 2.270410183155361210e+02 2.624250216967006395e+02 2.894508375480465361e+02 1.106681053253299183e+02 1.696755343387707171e+02 2.302155275158106917e+02 1.445113211107399138e+02 1.886794441144848236e+02 2.129906512422033131e+02 2.340704769023953986e+02 1.082933010325512981e+02 1.977265970892881626e+02 +2.874406426475449052e+02 1.913451373833616742e+02 2.647704607931181044e+02 1.881279366057496532e+02 2.840067538093052804e+02 2.179159896935567247e+02 1.839859875309309132e+02 1.189702187115672132e+02 2.794517441847542614e+02 2.815599370853284427e+02 1.258259904677427699e+02 1.428483537633051412e+02 2.541426109645265967e+02 1.338781623221585164e+02 2.877181693280556374e+02 2.041742222547631513e+02 2.429167887622087392e+02 1.861891141000048435e+02 2.815058357304060337e+02 2.932279451804108703e+02 1.428092602118218792e+02 1.129541128601477595e+02 1.104970415865426503e+02 1.361068733124779726e+02 1.702082770497633533e+02 1.583852379729134157e+02 1.614070717213254511e+02 1.054529192214523476e+02 1.116913943762218366e+02 1.806474879921846366e+02 +1.904583320230821926e+02 1.477903225290235980e+02 2.926623631581093150e+02 2.267002240281469199e+02 1.643763662729302268e+02 2.199235242233247902e+02 1.853923849032223359e+02 2.941726936508506469e+02 2.665966841434134835e+02 1.199566433868006357e+02 2.951991052054676175e+02 1.594510101065885124e+02 1.458298791153635534e+02 1.532145001211049475e+02 1.411023254500616133e+02 2.140513226665028128e+02 1.678784758049908419e+02 1.708308530430679184e+02 2.099440033407245778e+02 2.664570659333852518e+02 2.959905162222905801e+02 2.829445582187913715e+02 2.588706049990775000e+02 1.722199615074994483e+02 2.869184560072056343e+02 1.681559218785307053e+02 1.503240659973911306e+02 2.588597461006905291e+02 2.678295026364270939e+02 2.154561503934444886e+02 +2.071927904539387839e+02 2.171736003654224305e+02 1.593735315924418785e+02 2.947356579175152547e+02 1.742775794491871011e+02 2.184611101357660914e+02 2.225198306238390842e+02 2.168369296352294668e+02 1.755672175076374231e+02 2.252214925755263835e+02 1.563369877784152209e+02 2.085332604119019209e+02 2.572482649031854862e+02 2.951800051631508950e+02 1.079183556031880329e+02 1.218838648771928774e+02 2.685371616407055626e+02 2.419162624723466877e+02 1.022244855205179022e+02 1.101224552326326602e+02 2.597819405832950679e+02 1.134555412120959517e+02 2.870491931154815575e+02 1.374365654160442318e+02 2.645641258978021142e+02 2.531141673781916666e+02 2.361747183362105886e+02 1.893108861581111171e+02 1.539026912190118139e+02 2.501170032332128415e+02 +2.547888423116186232e+02 1.853670755857669974e+02 1.389074705955763420e+02 2.709929622842061008e+02 1.228800068832790515e+02 2.778321736112652616e+02 1.309641642706778555e+02 1.156980811627219055e+02 1.431313378740429982e+02 1.646591400066212714e+02 1.920182917083556049e+02 2.178001706163468043e+02 2.235489712948179886e+02 1.079088316874027242e+02 2.447091545393394370e+02 2.320303973549428065e+02 2.359105911115680101e+02 2.382951907588607128e+02 1.062067779247245483e+02 2.905379355334102911e+02 2.023335418134440715e+02 2.128348219019524095e+02 2.865957710750057004e+02 1.782427960783044796e+02 2.856139874187100531e+02 1.139905905655008098e+02 2.264676166669663360e+02 2.479179013019825675e+02 1.746165350218777803e+02 2.255842464851874070e+02 +1.883869033800616819e+02 1.965817072065136699e+02 1.890868666652849015e+02 1.898737766004000491e+02 2.779218373710688184e+02 2.134628932560298722e+02 1.100835458783813436e+02 2.768750976313177148e+02 2.547073561014202880e+02 2.728160162818061281e+02 1.733645011505617504e+02 1.625036971255624394e+02 2.977754324167240156e+02 1.632372616873928450e+02 2.174045665187836107e+02 2.606964806055048030e+02 1.625508452643421720e+02 1.715067940576683441e+02 1.218481476549646629e+02 2.842560845538128547e+02 1.928678337146606623e+02 2.708765321293922739e+02 2.077020047066411621e+02 2.923591890868326004e+02 2.230876482822842206e+02 2.689925468225608256e+02 1.036588336737814586e+02 2.052618530546818363e+02 2.648220111560104897e+02 1.868396012623422280e+02 +1.785937212608853315e+02 2.973454718025594161e+02 2.368986004504845084e+02 1.146953890760472348e+02 1.265905165006724644e+02 2.255973396401841455e+02 2.163675674740596264e+02 1.527913853500098185e+02 2.283358642424602465e+02 2.759303134283557597e+02 2.876072117803540777e+02 2.029362495845153944e+02 1.212425121544320490e+02 1.100001317370093830e+02 2.335268996183764330e+02 2.375268130741384027e+02 2.336339660612213436e+02 2.462747325703657282e+02 2.841981652294566061e+02 1.081959034831858446e+02 1.291296469376330833e+02 2.602425849072438950e+02 2.575669438145553727e+02 2.135342654708205714e+02 2.294373105308322067e+02 2.706502840281193016e+02 2.928412927772634475e+02 1.330151104176747765e+02 1.533759962548247131e+02 2.744006234275867655e+02 +2.257735103076358882e+02 2.728385269717355186e+02 2.290872800510813363e+02 2.330934692803050154e+02 1.037274604992595215e+02 2.674079561164307961e+02 1.195755645916240866e+02 1.402804464035359047e+02 2.170516922702277611e+02 2.744725918691634661e+02 2.930458735600458908e+02 1.496408395971007224e+02 1.595562419103408729e+02 2.835538666488008630e+02 1.780163567793609332e+02 2.906408145890961237e+02 1.133853019218590248e+02 1.494630592331960770e+02 1.214592101712915451e+02 2.263015460193574881e+02 2.598100406717117608e+02 1.963383361449393192e+02 2.235083985338561376e+02 2.946475410923074492e+02 1.758055989844200724e+02 2.637780439251395137e+02 2.875400021086666698e+02 1.577781508415756662e+02 2.146553072676672684e+02 1.798181279868336446e+02 +2.620574340171276617e+02 2.153711882285265915e+02 2.245961661539886904e+02 2.054509343172356921e+02 2.926008719008261210e+02 2.432564531143420652e+02 2.303655720936658611e+02 1.615953803481287991e+02 2.918921003884012748e+02 2.760746977013722017e+02 1.909442200188182710e+02 1.596536528765051060e+02 2.491411570718119037e+02 2.924629085319008936e+02 2.587604848561293807e+02 1.524605619386706792e+02 2.737599884275671798e+02 2.090365453766356723e+02 1.610548024559351461e+02 1.018774121963877803e+02 2.410901898572944049e+02 1.875862586601133444e+02 2.588626077539996686e+02 2.579873618626863845e+02 2.838744453525392828e+02 2.580071516854936817e+02 2.114887112935771256e+02 2.675506009048368696e+02 1.260391751775616029e+02 1.858866479221875920e+02 +1.963224789638335892e+02 2.444908535968891954e+02 1.962779352478895589e+02 1.553096436749702889e+02 2.483662294276224429e+02 1.067992874414757978e+02 2.633849667942634483e+02 2.454321751613854588e+02 1.854433418739394028e+02 2.562889653665436072e+02 2.506342746416453622e+02 1.900819942764665598e+02 1.704565979131312474e+02 2.916979173024495822e+02 1.898592592817412310e+02 2.687872145548625440e+02 1.525347862509104004e+02 2.786582104923993484e+02 2.310813531087783872e+02 1.166208530157265386e+02 2.602471623613457723e+02 2.102772607982462034e+02 2.183751071150112466e+02 1.065011561509572999e+02 2.813176394708128782e+02 1.792292558016025623e+02 2.804083600455996361e+02 1.557890480883644102e+02 2.439522159916458861e+02 2.652201783594097719e+02 +1.425266334964659904e+02 2.075049705342416928e+02 1.704914602333145126e+02 1.886474594627911756e+02 1.252313163849750595e+02 2.836097447326676502e+02 1.406399617929505439e+02 2.414245225193989768e+02 2.576349788827002385e+02 1.486724691707949262e+02 1.092388214497626961e+02 1.685935770192617724e+02 2.033388664740227227e+02 1.390809359458484948e+02 1.056188661648174758e+02 2.350581131530574055e+02 1.964295662906907012e+02 2.578831766420791496e+02 1.109952979966328144e+02 2.027546721440710940e+02 2.501377690830167637e+02 2.111868593440530617e+02 2.324728205186171692e+02 2.453971856382445935e+02 1.723822394524685819e+02 2.872924628066301693e+02 1.140766727214026446e+02 2.221345013854892159e+02 1.728173248741775296e+02 2.676400838220500873e+02 +1.711571121866394947e+02 1.085759247733173396e+02 2.001753766691515750e+02 2.760446855018309407e+02 2.056587091496190567e+02 1.121827347031253197e+02 2.274644480946081444e+02 2.571858980756533128e+02 2.945439217283808375e+02 1.913312305877045674e+02 1.500446430731354894e+02 1.650397772114545489e+02 2.581660073502400792e+02 2.094009769144933273e+02 1.731816092302842094e+02 2.727903589313663133e+02 2.606648610353666982e+02 1.460656197586831695e+02 2.016951883706858268e+02 1.247477859691891240e+02 1.732157361502286221e+02 1.195560196858487245e+02 1.253893910664414904e+02 2.455457677441618216e+02 1.778732818035962850e+02 2.490436815297808266e+02 1.487573988963908960e+02 1.937302250034929898e+02 1.502426775501600389e+02 1.110841009912817583e+02 +2.382535443835092508e+02 1.972031918916456732e+02 2.576267295349729807e+02 1.730194312205534288e+02 1.301593684828995094e+02 1.624008376323430127e+02 2.060036399923972681e+02 1.233366573394677630e+02 2.194763391620297739e+02 1.701495187616251314e+02 1.223397596968992218e+02 1.987622577877627350e+02 2.511738650001373117e+02 2.130204435763062634e+02 1.993899817227978133e+02 1.597764561560970265e+02 1.205224890815559604e+02 2.184250491898233690e+02 1.755709834516516139e+02 2.741081010321077542e+02 2.104755291992826187e+02 2.698148014221883386e+02 1.299106544858947814e+02 2.008369880697999292e+02 2.938716155581552130e+02 2.671516623028076083e+02 1.332347035771324215e+02 1.291435420390463378e+02 1.835021202063177554e+02 2.002866194329941720e+02 +2.554906544300547182e+02 2.365682876454178540e+02 2.924004211094360244e+02 1.662852505275750730e+02 1.123350814405425808e+02 1.910015128879867632e+02 1.341551373493250594e+02 1.313122940860927770e+02 2.397311819484906152e+02 1.559268654058377024e+02 1.407120959783594003e+02 2.371419051640040152e+02 2.217591327496910480e+02 1.881187811266301537e+02 1.632462641154496907e+02 2.970940639140721373e+02 2.422917505999918433e+02 1.356966040631749593e+02 1.702398486895437486e+02 2.608644720933497183e+02 2.783751927848827563e+02 2.951746624002826138e+02 1.720706565846523688e+02 1.275268866601749096e+02 1.880990845238362681e+02 1.129502795714700625e+02 2.919985401845127626e+02 2.747497807112307555e+02 2.667734033775608395e+02 1.373740617490475699e+02 +2.115416415080857746e+02 1.431719947715498336e+02 1.718744824503889674e+02 1.075365968452523902e+02 2.220100335193473029e+02 1.965127222891928795e+02 1.062726056237197838e+02 2.631794488147562561e+02 1.658640190278337627e+02 1.169182569761068464e+02 1.645780782039788619e+02 2.940728738870184316e+02 2.979920277570993790e+02 2.125849825405138631e+02 1.533327700316632161e+02 2.655551337415409421e+02 1.329075684859120088e+02 2.686536376777100941e+02 2.299223677315555676e+02 2.123135030200585334e+02 1.474417961566917654e+02 2.899688778344954017e+02 1.439992490259426461e+02 1.606165457016644780e+02 2.854253601360321682e+02 2.837928223954166924e+02 1.868865943198568402e+02 1.809928275876523571e+02 1.583918020284682484e+02 2.384217495701244331e+02 +1.181670050605631417e+02 1.525653020190297582e+02 2.615084872177121724e+02 1.755024420886775829e+02 2.989795566898581001e+02 1.573585789513378188e+02 1.903575226478752711e+02 1.641861715477102166e+02 2.943146494922903003e+02 2.038802368327418719e+02 2.581560000437879694e+02 1.504995935930718076e+02 1.095655891680627008e+02 2.628623226127134558e+02 1.069018430130149255e+02 2.750818506761686422e+02 1.121786007219489818e+02 1.106710601660877415e+02 1.217291564359016149e+02 2.915199334459504144e+02 1.325859381653097557e+02 1.737237090326784141e+02 1.036075961875061751e+02 2.392327113385031510e+02 2.486092083099548233e+02 1.259492139939950306e+02 2.665249241620523435e+02 2.103119814995928039e+02 2.718465347096271216e+02 2.018653364759854298e+02 +2.085808638159350608e+02 2.977621083099649582e+02 1.394173606621695285e+02 2.232898484647512873e+02 1.347812725162832521e+02 1.574683348766579627e+02 1.827258429860655724e+02 2.827887224427595356e+02 2.608349632236463549e+02 2.370910079389979046e+02 2.033290260845359398e+02 1.566531500677691042e+02 2.982287288081304837e+02 2.998057140577807900e+02 1.906108269451214596e+02 2.023344526730545851e+02 1.717672594576409040e+02 2.093320563180507747e+02 2.649028095061802333e+02 2.840422446800275793e+02 2.111868958418739908e+02 1.803076798272542760e+02 2.311954915496957312e+02 1.563425451766251513e+02 2.610066662710300989e+02 1.855286443040786537e+02 1.478912573842241045e+02 2.544380211258828410e+02 2.799416317427427430e+02 2.238937193404353252e+02 +1.269470316997365131e+02 1.895539822645488357e+02 2.443421824114378467e+02 2.632321641240823737e+02 2.164919638664115951e+02 1.042697198382110884e+02 2.896061632271033659e+02 2.068164163046922681e+02 2.059671371408958294e+02 2.352532326493898722e+02 1.046233655847859296e+02 2.755187319279126541e+02 2.344641322699609987e+02 1.434858288567621969e+02 1.255438908126368176e+02 2.548141480364848803e+02 1.466719626681152704e+02 2.020892715394597872e+02 1.195107046056347713e+02 2.012968701954913797e+02 1.996902768982717191e+02 1.560547951636197013e+02 2.162555170020900164e+02 1.483278604161245084e+02 2.615607136845001151e+02 2.424344777210258997e+02 2.524090919470299070e+02 1.726167614603126026e+02 2.199373130240069258e+02 2.318614758097714912e+02 +1.590143031424979370e+02 1.933970326403360502e+02 1.227042846200323112e+02 2.107086401017011781e+02 2.844049872407889552e+02 1.420899421875644464e+02 1.736571760246831673e+02 1.130876049831349661e+02 1.470306210908964317e+02 2.959723384067232246e+02 1.438030965279091049e+02 1.685928342779160403e+02 1.351720793691902713e+02 1.909711091249450590e+02 1.477005416416634205e+02 1.010528808923594681e+02 2.205493627613245167e+02 2.367352422049318079e+02 1.224997665062844305e+02 1.620949451166091251e+02 1.270634404764108467e+02 2.673321646154778932e+02 1.618882934467209225e+02 1.208967331765591524e+02 2.073956586593529607e+02 1.223277950209799059e+02 2.625820210851194361e+02 2.262632377752408672e+02 2.222881433937307349e+02 1.716205611551696961e+02 +2.376094214038359667e+02 2.287867757784330820e+02 2.035778067022395703e+02 2.546588007138803391e+02 1.514832565507949198e+02 1.736683542684334327e+02 1.991020520349750598e+02 1.873563480883249213e+02 1.589186331386689801e+02 1.042563150975229149e+02 2.019924784676414902e+02 1.136537158101241971e+02 1.091264020137841158e+02 1.352770409719844054e+02 2.178414513482917414e+02 1.831380105899948489e+02 1.114225947990316570e+02 1.736029819106907439e+02 1.354612112967272424e+02 1.996055424300992627e+02 2.905125217944571432e+02 2.980326934372309893e+02 1.560898949881966473e+02 1.943286005606112212e+02 2.429797193518882636e+02 2.652714760000731076e+02 2.863852813340179182e+02 1.838252831614893239e+02 1.814799327205894315e+02 2.338290144642930954e+02 +2.526381992552952340e+02 2.089745531365245483e+02 1.869938021147821701e+02 2.864405091884094645e+02 1.736924996547539877e+02 1.479914815134324613e+02 2.132537252074255321e+02 1.830098172980584934e+02 2.476607236946428827e+02 1.066503395377639265e+02 1.405219898965278276e+02 2.743866427972425299e+02 2.269305408710248173e+02 2.791638036143738191e+02 1.824422387811073634e+02 1.852994662516045423e+02 2.777032940597408128e+02 2.109153407914434126e+02 2.214759900082639490e+02 1.857033490029854761e+02 1.302118293337227328e+02 1.889562709124264188e+02 1.844813915245081546e+02 2.875482403705134402e+02 2.022892465111445404e+02 2.230217175841083872e+02 2.843056043891419904e+02 2.350834055358549222e+02 2.080929758762673032e+02 2.770814576487081240e+02 +2.389430507965955428e+02 2.463651891862864147e+02 2.369578462650186452e+02 1.902366989508459199e+02 2.003468797600664004e+02 2.681735461841141728e+02 2.362787745532336601e+02 2.323782975776413480e+02 2.525302892415198812e+02 2.828059530799229151e+02 2.840327053185673662e+02 1.223941816187275435e+02 1.056255174412387134e+02 1.386503050117574105e+02 1.384325506562210535e+02 1.176641636239777426e+02 1.670688688422628161e+02 2.506322552784647826e+02 1.181229702988334083e+02 2.607048520072489737e+02 1.667476448166365515e+02 1.310085831735554223e+02 1.553111545647699927e+02 2.907454039462255651e+02 2.844644695877585718e+02 1.989933906493695019e+02 2.662036190025202131e+02 1.792754658114438371e+02 1.073664330563030944e+02 2.793141822468826376e+02 +2.640306978448612654e+02 2.458161373226257069e+02 1.015510894380497575e+02 1.527048938693112916e+02 2.893334394723561900e+02 2.994916089563248534e+02 1.054055716033572452e+02 2.278819528330843127e+02 1.890909183007994443e+02 2.134436011261824433e+02 2.654189934957544210e+02 1.780852604264427725e+02 2.222277079756825628e+02 2.689688042831336361e+02 2.232046857529678050e+02 1.778434593737022169e+02 1.336418515516146783e+02 2.739064893378349552e+02 2.065065746675076355e+02 1.329712924393647313e+02 2.176938186185978736e+02 1.918043587714230114e+02 2.280421349429639122e+02 1.182282112372680842e+02 1.370131137248831692e+02 1.716251366233928195e+02 2.412427837766657888e+02 2.738208811966829899e+02 1.471415247536169488e+02 1.638288393831292353e+02 +2.669085627842696908e+02 2.477147782526785136e+02 1.718200513884793565e+02 2.299346472745743597e+02 2.016242169414389309e+02 1.631378839470685307e+02 1.859938403107781255e+02 1.609729169019194330e+02 1.536303039404505171e+02 2.234728543554556950e+02 1.953401084257108096e+02 2.920381588589057174e+02 2.034966688752892310e+02 1.019427894404581139e+02 2.980736970140829953e+02 1.738263823108001418e+02 1.531314323312329293e+02 1.400030133312995702e+02 1.802287961283190043e+02 1.719909696301723443e+02 1.974918793689569725e+02 1.666882741246514001e+02 2.879569025675030502e+02 1.334044307903087088e+02 1.016937569869423896e+02 1.660343944328368764e+02 2.214967229035601974e+02 2.539424882366704992e+02 1.211914878013190133e+02 2.835892388637473687e+02 +1.704109091340931741e+02 1.337843054639438378e+02 1.570106251098002588e+02 2.123587857442842335e+02 2.788290802167920219e+02 2.795601449888932848e+02 1.220747715539721696e+02 1.179984498565524405e+02 1.552783750686872963e+02 1.257256444039083192e+02 2.312614004137946893e+02 1.971625968209403084e+02 1.208837070227885135e+02 2.231693789143681386e+02 2.332576722664892941e+02 1.659208209363902711e+02 1.979623049620595907e+02 2.497459328714609512e+02 2.540243570817084446e+02 1.309045902221261599e+02 2.376613837929333499e+02 2.140333351750954023e+02 2.231625169053620539e+02 2.869160136215916737e+02 1.282002159167354023e+02 1.029173927424986488e+02 2.432034421383394545e+02 1.495648010251883306e+02 1.971910657968611247e+02 1.358409247687675361e+02 +1.833826243837603442e+02 2.960483510370855811e+02 2.343723986770386318e+02 1.560358896543934293e+02 2.499669478251469172e+02 1.762005778153444169e+02 1.918050503412152921e+02 2.089352602085182866e+02 2.770127170480132008e+02 1.268157216157417224e+02 2.670673189640755822e+02 1.547628252866769287e+02 2.602514896343354849e+02 1.557532905756793866e+02 2.574076233589491949e+02 2.646855654359934533e+02 1.749681240869035719e+02 2.465698370051858035e+02 1.076897610845538082e+02 2.337637497458482301e+02 1.791847918196868932e+02 1.967068388721293104e+02 2.340964493346380095e+02 2.762770912600988140e+02 1.174465260954359564e+02 2.950490567997024982e+02 1.354710376622284116e+02 2.342233227246520642e+02 1.617966271393036379e+02 2.107879984327653915e+02 +2.493754578342164336e+02 2.275093847135933061e+02 1.466148442335522191e+02 2.261697123059220189e+02 1.213252451599347950e+02 1.628949300801819504e+02 2.100466501082228206e+02 1.508908296808102989e+02 1.488199564735201079e+02 1.727131563468088302e+02 2.306747713688439205e+02 2.570279850661015644e+02 2.309125192178541113e+02 2.422081718543400370e+02 1.769407234272878782e+02 2.688532243604371956e+02 2.276780878660686085e+02 1.065345319601523641e+02 1.535069430280279050e+02 1.717902253122074967e+02 2.876755354986605084e+02 1.683056100689713332e+02 1.120105413679224569e+02 1.755508096146901664e+02 2.095863991316655870e+02 1.523590730880595174e+02 2.944635547123552897e+02 1.444697311944634066e+02 2.165062978405008494e+02 1.410128743297030098e+02 +1.434402193906418006e+02 2.368914090178307106e+02 1.963465933374949941e+02 1.914557752364961516e+02 2.870767419320768568e+02 2.044699144835463187e+02 1.223520556576680036e+02 2.352284247043744472e+02 2.917945011866975165e+02 2.225925999946875322e+02 2.240309397680480288e+02 2.048455962243571093e+02 1.188048963943729035e+02 2.200553599997707579e+02 1.885605934416515765e+02 2.863412817843446874e+02 2.913876692311304737e+02 2.446563674684449552e+02 2.981153955140326843e+02 1.111775924383378253e+02 2.239868361016714857e+02 2.540473271011064469e+02 1.343930974769885438e+02 2.368686732696482409e+02 1.175691554116390591e+02 1.014879352562223715e+02 1.330784448687188046e+02 2.045426156006566885e+02 1.168174380391246245e+02 1.704438548713551995e+02 +2.696784010384477597e+02 2.991318545155386346e+02 2.120364825583467336e+02 1.950895785161033018e+02 1.216112431291165592e+02 2.438998438799096391e+02 1.588292735755803733e+02 2.347670069791354024e+02 1.862846309471772770e+02 2.258642611266068343e+02 1.423367506635381119e+02 2.692888471853933083e+02 2.950212092401994255e+02 2.331327670110776467e+02 1.542291422318579635e+02 2.809064569107727038e+02 2.358857646534314654e+02 2.378124255062788563e+02 2.664164586086786812e+02 1.387157904298663880e+02 2.297158046581682243e+02 2.386372312695162634e+02 1.246509391338716171e+02 2.338956320284196408e+02 1.820257170558419944e+02 1.957425768708682767e+02 1.680974560138464540e+02 1.288235048549348676e+02 1.483029350020115089e+02 1.744880718659300669e+02 +2.512494238114035738e+02 1.112846425403449615e+02 2.472643304237797395e+02 1.241745840646870818e+02 1.808849124644312099e+02 2.524760780760417731e+02 1.836118621524309447e+02 1.408362492891266982e+02 1.099623406752946693e+02 2.383967522197594064e+02 2.436606913384966049e+02 2.770699525768120566e+02 2.597573569531676867e+02 2.935649366424795517e+02 2.702790297508025219e+02 2.563597369995835606e+02 2.279477293752616447e+02 2.477470305460766440e+02 1.962131167814513333e+02 2.859744526791636190e+02 2.703401534622389590e+02 2.763052603711840902e+02 2.934416645125817809e+02 2.193475948646207030e+02 2.822891098008749395e+02 1.085391177109117820e+02 1.782208012387337703e+02 2.335496863699061976e+02 1.715066387390946829e+02 1.948062204233656303e+02 +2.879262290016004613e+02 1.676743911135137068e+02 1.403503828589753937e+02 2.744454339345198832e+02 2.935124358491533485e+02 2.920282649929100671e+02 1.390240222956847447e+02 2.426642861805074745e+02 1.217336684570653489e+02 1.311823750440439085e+02 1.647679902066092836e+02 2.962811279981685288e+02 2.945746172932865647e+02 2.005257587949587332e+02 2.072045953580022228e+02 2.893049469033056766e+02 1.913962360581630833e+02 1.823675529874825543e+02 1.830342103129283373e+02 1.222396004373517400e+02 2.248239872372262482e+02 1.170253438297526429e+02 2.853825568202013301e+02 2.214973458763422514e+02 2.563932510909227176e+02 2.144837192650675206e+02 1.793062298958048473e+02 2.920176466690815005e+02 1.515607839109829627e+02 1.981203765908239802e+02 +1.733053660232129403e+02 1.312183264386245583e+02 1.276233157677672807e+02 2.020942572504836789e+02 2.314817368496994732e+02 2.242589617101967008e+02 2.160504620978007893e+02 2.360595788588375399e+02 2.952977074031120992e+02 2.334652590044975682e+02 1.243453875174208747e+02 1.916144242306085630e+02 1.092365115042800596e+02 1.478765005471206280e+02 2.191946613400726278e+02 2.879274886834762697e+02 2.733443652356662597e+02 1.858481832262083344e+02 2.193747651131673706e+02 2.695165737089945424e+02 2.960753121523491700e+02 1.890691006834304631e+02 2.638343907584013550e+02 1.510492177865631334e+02 1.878288206285384661e+02 2.726561149875388992e+02 1.704246795027074199e+02 1.006381753343381718e+02 2.153734239260733148e+02 2.551451126036402854e+02 +1.591849792872858984e+02 1.304671215023752779e+02 1.427456440770346831e+02 2.882324895344759170e+02 1.680635293254793510e+02 1.205800311663507642e+02 2.861305963205076637e+02 1.219224106654408928e+02 2.467003871618023538e+02 2.830287806498602095e+02 1.445950870572595193e+02 2.496562286252286640e+02 1.464987579205844099e+02 2.848280464142704318e+02 2.785616857190397013e+02 1.837468579783306950e+02 1.246964377230690673e+02 1.251791080124520050e+02 1.496399061799681363e+02 1.375936265087168522e+02 2.547928467777094852e+02 2.554856419260690927e+02 1.285559318166884850e+02 2.092144446410586909e+02 2.868951534942014518e+02 1.178319347908447270e+02 1.347784205269015274e+02 2.851299399919766984e+02 1.754694686670390809e+02 1.016886128619324694e+02 +2.606618423405234353e+02 2.125366732076933545e+02 2.822772640751277322e+02 1.096405633955119185e+02 2.437561663288932721e+02 2.129146561548243994e+02 1.148823764090175530e+02 1.516868774610028368e+02 2.090025176018670265e+02 1.817684320186263562e+02 1.584667226055155709e+02 1.501973711988126468e+02 2.530199923706828713e+02 1.847948752811591930e+02 1.778871618489498303e+02 1.664551902511519188e+02 1.100020157933824265e+02 1.352000835393275509e+02 1.710981737682794801e+02 1.530513645967782566e+02 2.588476693974693035e+02 1.775587245068043956e+02 2.006331886716666588e+02 1.389709403689849694e+02 2.489553638298030194e+02 1.673604491791948021e+02 1.991154502489720812e+02 2.423848982654565418e+02 2.882603768001737308e+02 1.620650086718309240e+02 +2.723642490909132903e+02 1.680927290528325670e+02 1.005734627393615455e+02 1.598916606218045047e+02 1.672547346703738071e+02 2.361420151042074451e+02 2.741857058408131707e+02 2.533004150866734392e+02 2.036092771261417340e+02 1.091915011443997230e+02 1.145604210422382323e+02 1.209982156413156247e+02 2.749595368914399387e+02 2.177794513808643160e+02 2.054163746311436967e+02 2.185860861470465579e+02 1.504022045473846845e+02 1.713704456854883347e+02 2.175221629008602804e+02 1.230663148243889253e+02 2.419648244223723168e+02 1.383010418990747326e+02 2.040260833828849059e+02 2.966316994044250919e+02 1.630596872908637351e+02 2.562534082821714492e+02 2.549425872735235998e+02 1.983522705781282127e+02 1.524860865223137694e+02 2.736848821358530586e+02 +1.277021385004174192e+02 2.448445434866889343e+02 1.296687360965440803e+02 1.874271582575348702e+02 1.145742775945452792e+02 1.884744688522491742e+02 1.336298647132909423e+02 1.523816963142488419e+02 2.658270705367647224e+02 1.781637174983711134e+02 1.154610011723892171e+02 2.005342781476718415e+02 1.303166615041172918e+02 2.397284110571510496e+02 1.612912854182502542e+02 2.821645080329541315e+02 2.544831471501324813e+02 2.622237400581972224e+02 1.417212269902922230e+02 2.054005404298748658e+02 1.092142219674599062e+02 1.652051184306486107e+02 2.825679563619778492e+02 2.056286073102957630e+02 1.772062144904277545e+02 1.163520479257007310e+02 1.006186351926139366e+02 1.734025793931427586e+02 1.446958902579306709e+02 2.025820689614877779e+02 +1.798382687901162740e+02 1.604629760861514001e+02 2.668981169240885265e+02 2.763242846779806996e+02 1.318105471716862098e+02 2.191362245125996537e+02 2.770758446308884686e+02 2.308910816293108326e+02 2.956895796828827656e+02 1.566426856848869988e+02 2.326210561246332418e+02 1.206555816723871715e+02 2.603144096756907970e+02 1.172571782204154829e+02 2.219493974369055991e+02 2.385109304229506790e+02 2.599678734377965839e+02 2.850516346518521686e+02 1.472948582444382168e+02 2.234296740595885922e+02 1.427895312415343199e+02 2.848238578369252423e+02 2.260232767550441508e+02 1.544648385858973541e+02 1.163971462755376791e+02 1.762731012775239492e+02 1.089523563056807660e+02 1.663966154222005116e+02 1.342495772836978745e+02 2.922401077696804350e+02 +2.806557294060240224e+02 1.077657131130299604e+02 1.622983596366119059e+02 1.723469481204717795e+02 2.678046848873893850e+02 1.442059922525422451e+02 2.629931208031973711e+02 2.741083495447689415e+02 1.194142462414748707e+02 1.688961325073638022e+02 2.967954354880449728e+02 1.822107331135221671e+02 1.292333403080546645e+02 1.856814508383810391e+02 2.103923137448445573e+02 2.517859299913771451e+02 2.551152596962431574e+02 2.077883190793959898e+02 2.986930461834413677e+02 1.196764061335889551e+02 2.378823960447958257e+02 1.692017967083341432e+02 1.471250494556689432e+02 2.608355254883699672e+02 1.757172426071724942e+02 2.629426236813185369e+02 1.040244734248400533e+02 1.533558690719498827e+02 2.011860465194789072e+02 1.720545334339216765e+02 +2.966488050331527688e+02 1.809989340563203086e+02 1.871527370563514978e+02 2.315558973515319394e+02 2.657682292004950000e+02 2.237816732699509998e+02 2.282045922056215090e+02 1.846236325909775928e+02 1.644827554373339353e+02 2.760250360653360531e+02 2.492622345937652995e+02 1.483432536002697191e+02 1.527550390024584601e+02 1.573429964258168070e+02 2.090721206423400247e+02 2.535819867756219708e+02 2.420536340362719159e+02 1.691914404667937788e+02 2.388696721384086459e+02 2.593840245957078423e+02 1.331872961625781500e+02 1.116342264469163581e+02 1.680964276125217793e+02 1.555020753508222526e+02 2.422052215908822177e+02 2.626184375196450560e+02 2.674230788003709449e+02 1.948146659156083729e+02 2.663681889818526543e+02 2.795342087705012659e+02 +1.674728956867265310e+02 2.635505920196726493e+02 1.395353777027027604e+02 1.883233466008314565e+02 1.249441512057495913e+02 2.512189370435067417e+02 2.719913755602378842e+02 1.237326636617429614e+02 2.939951219495833357e+02 1.686366002602222807e+02 1.800181056076297068e+02 2.288525977776352818e+02 2.717306800175948638e+02 1.565292507387619594e+02 1.445460932655216766e+02 2.092313282690445249e+02 2.370375511382032698e+02 2.880525812713749474e+02 1.172567175017127141e+02 1.112412797274302250e+02 2.246954385922853135e+02 2.812359340959551446e+02 1.004168603505609241e+02 1.005387863078678805e+02 1.815971195408835683e+02 2.811251817522295937e+02 2.605765849402707772e+02 2.298114360271968621e+02 2.557293814584297706e+02 2.542416589790913122e+02 +2.943583269632734414e+02 1.442274778682184717e+02 2.700917391987959491e+02 2.527420049761408904e+02 1.527279900348522688e+02 1.841979337126335281e+02 2.902442440856567600e+02 2.889101481258517765e+02 1.828125218264408716e+02 1.133179379993730862e+02 1.484787634874768116e+02 2.676352293304336740e+02 1.452118425579454311e+02 2.636966617786087568e+02 1.313546620759107100e+02 1.834019443937838787e+02 2.892465421328221282e+02 2.575015388377624959e+02 1.970702343003932242e+02 2.507528167727347181e+02 1.724897096143170074e+02 2.664268628760375464e+02 1.365257050051324370e+02 1.198011035974838308e+02 1.176831988053894520e+02 1.070946883963453899e+02 1.964638491125322446e+02 2.570844982939356100e+02 1.593905150913052466e+02 1.202569936867807598e+02 +2.734271498156417692e+02 2.352133531486530842e+02 2.590835237087205769e+02 2.260994493040042528e+02 1.805421354394846105e+02 2.728408805160995598e+02 2.367263522625478913e+02 2.580210451062748689e+02 1.204524877415260562e+02 2.946465680607327613e+02 1.547220269335912803e+02 1.186203172746691337e+02 1.923878728892914864e+02 1.094127410697402354e+02 2.222837240826847278e+02 1.529333599077602628e+02 1.861450256630199647e+02 2.125583079944122176e+02 1.527591657960447264e+02 2.694001797345342766e+02 1.986063989766776388e+02 2.192493126389772442e+02 2.986827335637019587e+02 2.790660387254000625e+02 2.781487003899754313e+02 2.564198676846006606e+02 2.597551240338123648e+02 2.358970425952163907e+02 1.951628676328612357e+02 1.078208269500064347e+02 +1.190762776130697205e+02 2.951075493308472346e+02 1.091043363430719069e+02 2.824365312299846664e+02 2.445811468414383398e+02 2.538090805786315514e+02 1.230092364266577363e+02 2.633887649939744051e+02 1.865216093980499181e+02 1.540388898662323243e+02 2.047343894245035756e+02 1.431412534309083640e+02 2.857794001060171922e+02 1.492366175285521592e+02 1.380934567887849198e+02 1.331831467466375898e+02 1.149412013934811796e+02 2.205070844660474734e+02 2.939252657951740844e+02 2.049464694042562769e+02 2.047902832862141054e+02 1.810793422252176015e+02 2.005356992447976836e+02 1.381400138775680375e+02 2.582445444487385657e+02 1.698212931623984616e+02 2.252085951830697468e+02 1.808378144669676999e+02 1.307311344108444473e+02 1.050024101356033697e+02 +1.722314120162143354e+02 2.530014253763471856e+02 1.298340795948372772e+02 2.948664870226410812e+02 2.383106068289312702e+02 1.822969205106659558e+02 2.285226769051377005e+02 2.759417691711663565e+02 2.120970517474504220e+02 2.831046044310812704e+02 2.320579821788242612e+02 1.286125039667014960e+02 1.609837368065715282e+02 2.931112965353385107e+02 1.441758663366052531e+02 2.810263276191118962e+02 1.239857273771131077e+02 2.399447548605567988e+02 1.460208836055017514e+02 1.205325462037979491e+02 2.112513935912650993e+02 1.036793750016967692e+02 1.113202625217208777e+02 1.646612561683649574e+02 1.018350908838390581e+02 1.263835026124204859e+02 2.766683711501553944e+02 1.682407929561517506e+02 2.677103056024840271e+02 2.147294480454548307e+02 +2.763536852866382105e+02 1.511976958084401872e+02 1.026794659371155944e+02 1.805990415690671398e+02 2.442493962549426385e+02 1.881796213041043018e+02 1.028768312506858535e+02 2.787706953534510603e+02 2.589640601731795755e+02 1.730107396932538677e+02 2.218419822849910190e+02 2.651646152747807719e+02 1.476149140151474342e+02 1.986450675254654072e+02 1.050693447352362853e+02 1.819666738706916931e+02 2.873544952103893593e+02 1.472060704631180954e+02 1.297023844405691761e+02 2.824778443572924971e+02 2.918073394139615289e+02 2.128134400148996974e+02 2.223096450508596149e+02 2.761940547406351811e+02 1.348708672340777639e+02 1.857009592938832441e+02 1.062906640064134649e+02 2.104442283262811202e+02 2.812954268214299418e+02 2.739038950945439979e+02 +1.837264129055918147e+02 2.399207190527903322e+02 2.843910623120511900e+02 1.773207161532972975e+02 2.056581469496123873e+02 1.558029517788254168e+02 1.458438122541016924e+02 1.893030782939712253e+02 1.139027557376393673e+02 2.228775749423569437e+02 1.367670384452707140e+02 2.854480456674787092e+02 2.424985140340279202e+02 2.940521113211518696e+02 1.330693282221190259e+02 1.212599008475133076e+02 2.754747741586869552e+02 1.062856492128348549e+02 1.212724485003486166e+02 2.100514698158626743e+02 2.547262582240854272e+02 1.999488755181088777e+02 2.578561029518564283e+02 2.784200494851090752e+02 2.728829168298310606e+02 2.071711407548560544e+02 1.708729380756020362e+02 2.726254883308487251e+02 1.104364015278258364e+02 1.175773277008901090e+02 +2.554381337818412305e+02 1.634513906120204183e+02 2.309962436793083214e+02 2.460443770945291249e+02 1.618890365991254896e+02 1.046310291743186980e+02 2.772116654811295575e+02 2.098555252827713957e+02 2.309383801112169863e+02 2.845300950466865402e+02 1.268119123926061320e+02 1.697885006171669602e+02 1.901887742560337529e+02 2.605757830463372215e+02 2.755463791239279772e+02 1.771647294768940810e+02 2.403902735905423356e+02 1.774352552408031443e+02 1.796883744424403631e+02 2.736192366006921475e+02 2.118505050785533967e+02 1.873353967662169453e+02 1.802980863638028950e+02 1.869858546159753132e+02 1.200946851663063342e+02 2.350811068219035178e+02 2.018941614745772313e+02 1.010158706413519525e+02 1.661546933057649937e+02 2.570882207683835077e+02 +2.856134023048114159e+02 1.356279054667102741e+02 1.225310201562991494e+02 1.529777144242077327e+02 2.936506440162480658e+02 2.589580133771784176e+02 1.864782805190425279e+02 1.931182124516369640e+02 2.913608028278327993e+02 1.555662042949096531e+02 1.173676742008071301e+02 2.242990267171766732e+02 2.651338851871976203e+02 1.128980005738893482e+02 1.283582653966309408e+02 2.071495534530326097e+02 1.241509031508031740e+02 2.393403040292282640e+02 2.829812266966206380e+02 2.294799861563923287e+02 2.129576840814710295e+02 2.165539860914115877e+02 1.357366103660294243e+02 2.396252028023287153e+02 1.395106368224716107e+02 1.700689743264745744e+02 1.253435651632085950e+02 1.508112259783626428e+02 2.310267786371028933e+02 2.311667616985857876e+02 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-int-data.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-int-data.txt new file mode 100644 index 0000000000000000000000000000000000000000..4fd11b7509e65b01393a6af6125d1b304d524bd7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-int-data.txt @@ -0,0 +1,100 @@ +-67 65 82 64 51 1 -12 2 -84 -52 12 82 -45 -84 -41 31 -49 36 -70 40 -74 -99 32 64 -6 43 -53 -43 43 96 +-58 20 25 99 -25 78 -6 59 -23 30 36 25 -8 83 -43 -7 -8 42 -90 96 46 88 31 12 68 -21 -6 7 78 -19 +-66 -51 0 13 42 -43 -30 -29 20 10 -24 -5 -42 38 -56 6 1 -80 -65 -91 89 64 -21 49 -84 41 6 -78 71 -2 +-50 -84 -50 -66 46 -88 -10 -28 -25 6 -7 10 -35 86 41 -17 72 -67 13 -67 -76 -84 -15 35 67 40 90 38 -1 -47 +-51 27 -48 26 -73 -46 -68 -56 -38 -4 49 -64 57 -86 -80 70 50 34 84 97 -76 3 -54 -89 -7 -53 15 36 -28 85 +2 -59 4 30 70 -42 -26 -1 27 -90 -18 95 -10 -36 43 24 86 -8 -100 92 80 -40 17 -93 -81 54 -8 84 -53 38 +-80 0 -71 -41 -33 9 -61 0 26 80 64 67 74 68 -72 78 -72 -52 -19 37 -33 -24 -11 -71 -53 -16 25 56 -74 0 +71 -23 49 -36 -43 -70 82 69 -100 -27 50 20 30 84 -33 90 49 39 -52 -51 -86 -76 -72 -88 12 91 -96 -61 -87 -47 +21 39 1 78 68 -80 -54 71 17 -94 34 -20 14 -5 -24 55 -84 -50 -90 -24 -79 -81 53 -50 22 -13 -92 78 -22 -50 +-47 -73 77 -93 -20 51 -37 -14 -37 -18 -8 -14 -71 29 -27 -5 54 77 -7 -2 15 -23 98 -34 -65 -78 -77 -90 -5 -35 +92 -33 71 24 43 -19 50 -40 -48 -33 -51 -14 23 40 -78 -14 -76 1 52 69 93 5 -13 30 -60 -20 -54 49 -52 93 +32 -86 21 -41 -86 -38 97 -35 -37 -89 -15 -18 -46 -37 8 63 -63 -61 57 50 43 -27 -45 98 -56 -81 16 -38 -25 -28 +-18 19 -52 -86 92 -72 23 35 20 57 69 -22 52 -66 -74 -29 -1 -10 -97 22 -97 -93 -70 87 85 -31 42 -29 -10 -36 +78 80 -93 68 41 84 -37 -62 38 -9 99 -60 90 47 -33 -40 -59 97 -28 9 35 -6 -60 -83 -39 -97 -25 -78 95 40 +79 -35 -45 -46 69 10 29 -88 98 -44 66 11 45 -58 -11 -25 51 -44 54 30 59 98 35 -28 93 86 99 19 -27 -83 +80 77 -72 57 -35 -27 86 -67 11 77 -28 -89 -30 -31 -72 64 -95 -75 92 -32 -96 -14 6 -83 -66 -58 71 -17 58 -53 +-1 17 -72 82 -57 -48 -7 -44 -80 85 -99 -9 27 -11 24 13 86 18 67 -9 12 77 98 49 49 12 -82 45 31 -68 +-13 -75 -26 17 91 12 -95 -62 -54 -60 22 50 86 58 -11 -11 -21 31 16 -15 67 90 1 80 -57 -98 35 -54 51 91 +28 -75 -31 49 0 73 75 -66 50 -77 -20 82 -40 -90 -28 32 -44 89 -75 -33 -11 -19 -55 79 18 2 -39 -49 78 -72 +14 56 78 69 -40 -20 -39 71 99 -89 60 -82 -1 -77 -42 94 -41 35 72 11 -13 89 -52 -41 -93 43 -39 -61 68 -4 +88 18 -90 -75 -49 46 -28 -48 -69 -64 77 -8 91 -65 62 -27 -19 34 10 78 82 49 -34 63 78 -88 -17 -37 -85 91 +4 36 -77 -75 -12 70 42 8 7 -31 -69 -74 -65 18 85 -92 91 16 -15 24 -74 -56 71 -70 -90 20 13 73 -68 -65 +92 22 -31 -73 -59 -78 -20 -11 -61 36 -40 34 -96 -12 51 -45 -12 12 -3 -42 -71 68 -8 -91 50 -73 -96 -46 -38 -4 +-87 44 -58 -83 70 -81 32 29 -79 45 -64 -52 57 73 -80 69 7 -22 31 -71 -34 -33 47 79 -17 6 -77 -89 3 50 +85 2 73 -88 -99 -13 -76 1 -90 51 30 -52 75 -2 -8 10 -83 -40 -5 -79 82 19 79 94 49 4 66 -76 6 -48 +29 -34 66 -93 45 -1 -98 92 -92 29 -10 64 -23 -81 -73 -62 -18 37 -29 -50 -52 90 -28 24 -4 -67 -33 25 -78 93 +57 -46 36 -16 34 -59 -96 -86 64 2 28 42 -32 6 -17 37 38 -40 -92 55 -22 -42 11 -77 12 81 -89 -39 -30 -39 +-72 -68 -41 -5 93 55 24 -6 84 77 30 33 -51 -62 6 -5 -83 60 -1 -64 7 -7 -92 31 5 -21 -34 -14 21 -33 +26 -75 -36 -54 -21 -38 -49 -20 82 73 -84 -5 -69 84 -87 12 7 -67 -40 -50 -35 -65 80 -83 -2 1 34 -16 91 82 +61 -21 1 -64 -56 -61 74 16 0 38 51 34 -35 37 -28 -52 -14 61 14 58 50 27 -43 -27 14 56 -16 -78 50 -89 +45 -47 -61 68 -41 -70 14 -51 49 -84 64 -65 88 -39 -88 28 -55 -18 81 -2 -1 -45 65 -6 62 16 71 71 -1 47 +47 60 22 -42 -5 -74 12 66 89 -82 -85 65 74 0 -18 56 -39 84 -65 -42 -33 -60 23 33 -8 -72 3 -64 -3 -25 +-70 11 -19 -12 -1 -50 -89 -61 78 28 55 92 -17 86 -17 -45 -31 68 -24 -99 -59 27 79 -2 21 -80 54 9 14 -70 +-38 52 -99 50 -46 -63 -74 -41 -43 -62 -81 38 -99 17 -94 -6 44 -20 -13 -30 71 -43 43 -28 -8 57 -93 98 4 42 +-17 -27 -60 -22 86 -49 39 -83 72 -16 82 74 73 -29 16 -59 81 -60 -96 51 -62 -55 -79 -31 -15 -67 -18 -83 -61 -86 +28 37 -44 7 -17 -10 -65 8 -78 -17 -46 -5 -35 -86 13 -16 27 24 60 -12 -48 -45 16 -33 70 -45 -63 -60 21 70 +-75 -89 -93 -93 62 -44 -39 46 31 57 72 30 -65 29 66 -53 2 -2 71 -90 -73 -40 -63 32 68 30 25 98 38 92 +88 3 5 73 -2 -61 -94 79 99 94 71 -83 -40 80 -79 -14 -34 -99 -52 27 23 13 13 -35 -74 13 43 -19 2 -62 +92 -47 -27 9 -68 -86 -57 43 9 -81 -9 69 52 -28 80 -13 -6 -44 -81 -89 -10 30 -64 86 -76 -11 -100 15 12 -62 +76 -42 39 70 74 79 84 -52 18 -58 78 53 89 58 -32 20 -51 35 12 37 -70 -21 5 97 67 -25 -25 -10 2 30 +-84 26 -60 -34 11 -27 47 85 -89 29 54 -53 66 -9 12 4 92 70 2 -12 -55 72 -62 -79 -8 68 -19 12 -8 -100 +78 -97 -76 86 -47 42 99 -3 9 49 -84 86 26 43 -26 90 23 -66 -37 -35 25 -12 -42 -12 96 -15 48 87 -95 -12 +-60 57 -30 -4 -84 24 -82 -5 34 56 76 81 -64 23 32 34 -41 -48 -6 77 -42 64 87 92 82 59 9 -71 -56 -45 +-74 -90 -27 93 33 15 -35 -73 78 23 17 -28 9 63 9 35 15 32 0 -4 -32 54 -76 14 -14 -8 16 -43 -81 57 +-2 22 85 -33 -48 74 64 -59 -27 17 -65 27 -50 -81 41 -69 -26 -29 -83 48 -81 51 58 -62 -63 -55 -63 39 32 -34 +98 -99 13 25 -10 43 -62 50 82 -90 -51 40 -71 82 27 -73 19 -62 37 10 -21 45 -94 -45 -41 -3 44 86 -2 27 +-80 -89 -57 87 -42 19 32 -49 37 -4 -30 54 46 -3 -92 89 60 37 -86 38 61 93 45 -45 -86 54 21 45 50 -53 +7 -68 71 -6 41 -72 67 45 15 46 85 59 82 19 65 75 -62 -35 47 -51 23 41 -54 27 -99 14 9 69 60 62 +99 -51 83 -47 -19 -57 -22 51 -52 52 92 80 69 1 -31 0 -19 -54 73 -5 3 82 -86 -84 -95 -83 -92 -52 -90 -79 +43 -75 62 99 66 -43 -38 -21 23 35 -63 -61 -46 5 3 -90 -28 55 87 89 -29 -46 23 -61 -5 10 -70 -63 50 -14 +39 38 10 66 -24 -45 55 -33 31 29 44 31 73 44 6 69 -21 -58 -3 93 -51 86 -16 -88 88 -30 75 78 -20 -12 +-11 11 -19 40 82 6 10 22 90 -78 -88 -49 72 69 -62 42 -23 22 -38 -98 0 -3 -43 20 9 18 -67 -7 22 21 +99 80 -55 74 43 -31 60 -26 -29 -6 75 60 92 -42 85 18 1 1 -74 -44 -12 72 -57 -98 99 62 45 -40 -39 -75 +50 30 -18 -29 -80 -59 -96 46 -99 -76 -13 -75 -93 -95 -45 62 -37 53 -96 57 -40 3 14 -45 -84 58 75 16 37 -6 +1 -47 87 -99 -22 -22 -20 71 -91 13 35 -80 75 65 -87 16 -37 99 -60 49 52 18 55 -11 18 24 -65 -80 8 -79 +-8 -87 86 -9 -64 -76 59 -52 -89 18 13 70 44 93 99 62 39 49 83 28 72 -71 -13 -71 -22 44 -87 73 -68 80 +41 -26 44 -63 -26 -83 -44 63 -51 -48 52 -8 55 73 -45 84 40 45 32 -34 -78 -46 -79 57 -40 11 34 -75 -20 91 +94 9 -35 -5 3 59 -63 2 -7 -72 -34 -70 78 99 -29 37 11 91 61 29 85 -15 59 79 47 41 19 -18 -92 47 +-59 -89 57 -72 -79 88 -85 18 -35 -96 -57 33 83 70 -55 -16 -21 72 -53 89 -44 -86 9 -44 -26 78 2 -93 -75 6 +55 73 89 80 -69 -93 -39 -88 62 49 91 -68 87 -26 40 16 -49 -53 -57 23 -97 39 -78 44 -15 1 60 -87 43 -42 +-2 -23 -74 -80 -59 52 -58 68 64 97 -86 -41 -88 35 49 3 -40 90 34 -2 3 13 -95 8 -1 6 75 92 19 -31 +57 76 65 3 37 -72 -43 57 64 -23 41 87 26 76 -18 -32 28 47 11 47 -33 -12 4 81 -92 -47 -81 43 -2 5 +68 74 66 -89 -95 -40 -78 -58 -54 -20 2 20 94 -35 58 -20 41 77 0 95 39 14 36 -40 -85 -60 -63 82 0 58 +-61 -99 61 10 -2 -31 -70 37 -77 -10 85 95 -28 70 -81 -78 -68 -33 -77 77 -6 42 -100 -68 -59 -86 -42 -74 35 -32 +64 -1 -1 -64 51 11 -65 47 -87 -8 5 58 22 -80 68 -25 24 59 -25 -75 95 -22 -73 27 86 -39 -98 -1 -17 -32 +94 -50 -53 -62 -53 46 50 38 -95 -77 40 -38 -23 -14 -68 -20 -47 23 -8 -12 -92 -69 -97 30 94 -45 47 -81 82 -60 +28 67 -48 4 74 27 -30 12 -32 35 91 -83 30 -55 -7 79 97 11 93 -45 -79 31 78 65 84 -23 -26 17 -61 43 +44 60 -88 72 31 98 55 -4 66 -14 10 -81 -40 66 -15 21 69 -98 34 3 75 18 98 -6 47 -39 31 -19 30 -51 +-6 18 -93 31 51 -20 -16 -33 -38 -19 71 4 -53 23 97 1 -28 -72 -44 -48 45 33 -76 86 64 49 -45 -34 -9 -76 +-19 8 28 -27 -51 -58 -36 63 -92 -95 70 41 -38 -49 -95 -100 43 97 -60 -5 -56 45 -13 -3 20 -10 -21 -85 -5 63 +-74 -74 -74 -39 -57 -12 51 11 -11 -22 -26 -54 71 24 -37 77 -90 77 75 86 -53 3 69 -99 -82 -59 30 81 -21 -86 +67 63 87 -15 60 -82 87 51 -39 -49 -16 74 51 17 6 47 98 89 -20 -98 97 -61 18 34 37 -36 37 -96 90 44 +53 -8 37 -76 -61 70 -77 -11 98 -80 12 -80 6 -89 8 -59 -69 -100 -52 -30 95 -58 61 29 52 -64 -51 10 16 -58 +54 -10 49 62 76 -25 80 36 13 5 59 -65 14 41 26 -78 23 -45 -51 -85 91 -43 -61 -37 94 27 -11 49 98 48 +53 -51 27 34 28 -53 18 17 31 -31 59 71 -34 25 54 -84 -34 -24 76 38 -36 15 -1 56 2 -12 0 26 -38 -62 +4 -94 -63 -21 -95 -42 -12 86 14 -86 -1 80 -48 62 -47 -52 3 91 -86 11 79 32 -24 -33 -54 19 -17 28 -33 -97 +-18 41 84 1 -83 48 -99 -64 26 -52 3 -64 68 -98 93 -79 -97 11 88 74 41 -31 -42 -35 -66 18 97 -30 19 -93 +-19 42 61 -91 -20 59 -11 -64 -60 85 -6 -71 33 -52 46 51 -86 -77 74 -4 74 -81 1 -39 -30 12 -12 20 66 60 +86 1 -67 -91 -92 -22 91 -90 -45 26 53 -6 99 46 -29 -40 -99 57 -45 -47 -3 -86 90 -78 -33 73 90 -51 -75 2 +88 -34 -2 30 -18 35 -23 90 99 -49 90 -79 94 -38 48 67 -35 -58 81 -24 18 -54 83 65 -58 -12 13 89 -59 57 +92 -99 94 -73 97 -78 -93 98 -78 95 -21 -17 -11 -92 69 -60 86 9 -36 -18 -33 -39 -65 74 -65 37 -49 87 -28 -81 +-95 2 -18 20 93 54 86 -63 -5 -89 17 -9 75 -66 -64 -82 -46 -48 82 5 -89 19 -32 -45 53 -47 21 -9 40 34 +86 87 55 -41 49 -10 -6 -7 -99 23 90 -50 -9 -81 77 65 29 -21 22 -82 19 48 -24 -72 75 -66 -69 -17 72 6 +13 37 96 31 -65 -54 -91 -27 84 52 -9 -28 85 96 14 63 -34 -29 -85 78 -75 -44 -30 -5 4 72 -45 6 13 71 +96 -69 67 59 69 46 80 42 81 30 89 -45 -10 -44 25 31 89 16 -36 86 31 92 1 5 -2 92 -11 77 20 40 +-48 98 -100 30 54 9 84 -88 5 48 93 56 -94 -89 81 33 44 -30 -95 -98 29 -33 13 -26 -59 -80 -68 -40 12 11 +82 -63 -30 -67 54 -68 50 -63 -91 -68 -45 -66 -58 16 -25 9 -50 -59 -55 4 -2 0 -63 67 30 -21 -8 55 21 -68 +9 -8 56 -6 84 81 -63 -35 81 56 -50 -54 96 -51 86 0 66 -4 -18 65 -26 -57 8 78 -54 17 18 86 21 68 +9 38 33 16 3 86 -57 28 -6 -44 -42 -2 3 -71 -86 23 34 -29 33 -30 67 63 -11 76 -65 92 30 -66 61 1 +-72 -85 -1 64 -79 -78 -1 15 -35 -32 80 33 -36 -82 24 -65 -23 29 38 -31 87 55 -18 -52 -77 -22 -11 54 62 -48 +65 -77 50 16 41 -94 -21 16 85 24 60 86 -78 -13 69 46 55 5 -27 -18 -6 -1 59 -62 -58 -99 -49 -84 89 18 +-21 -15 -55 60 78 98 67 94 58 -5 -36 42 36 73 13 72 -78 -68 41 -37 -33 -46 -80 40 13 -44 -71 -8 15 -77 +16 -93 -42 -10 14 57 -54 -3 -44 -21 30 -93 71 25 -60 -94 93 5 -94 -84 -72 1 -50 -34 23 -15 15 18 72 -29 +-22 -82 -30 -87 -88 -25 46 32 -30 -55 -79 -85 71 -89 -57 -88 21 53 -100 -64 -92 -97 56 -51 -17 -34 -31 6 -68 84 +-53 -51 90 -38 -61 57 -63 67 22 22 70 44 43 97 20 -62 -74 72 83 -32 35 -66 -29 5 -88 55 -94 94 -19 55 +57 51 29 -42 -21 63 -57 7 -48 -87 -60 -55 -77 -53 -1 -85 64 60 53 71 41 59 -61 -73 -12 86 90 10 -60 -38 +2 -9 14 67 -2 70 11 -78 26 -55 -86 -25 99 66 63 64 46 59 66 -37 -78 -70 63 1 -20 2 46 50 34 19 +-87 -40 75 -11 -88 -80 -95 -20 -92 -28 83 24 88 -39 83 -36 -61 56 99 -73 -59 -85 -49 -10 91 12 -79 -18 -15 6 +35 -74 -4 -15 40 -87 81 -22 -12 -46 14 9 98 -35 -2 -12 57 -74 -52 71 70 -70 -61 -47 89 44 33 -100 54 42 +-4 -34 80 -12 -15 -9 -8 -29 89 -55 -33 89 16 -33 -73 -82 98 27 88 59 48 20 -67 -21 -86 11 -50 46 64 -8 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-uint-data.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-uint-data.txt new file mode 100644 index 0000000000000000000000000000000000000000..c1ec7a5d64e540428507ee5a3358743b6c034ebc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/random-uint-data.txt @@ -0,0 +1,100 @@ +52 34 59 34 64 20 89 69 26 93 95 32 17 93 77 49 51 60 51 27 60 10 61 2 16 30 41 68 65 0 +43 74 11 37 32 61 72 29 47 21 7 47 68 58 22 33 29 37 14 45 71 1 67 79 69 9 6 6 95 78 +86 20 68 67 43 5 77 70 96 37 79 71 35 30 22 4 56 28 33 50 97 17 85 52 21 5 57 19 35 97 +15 21 99 4 54 39 15 29 68 21 50 76 64 51 79 0 24 5 65 95 90 51 99 82 9 80 61 32 2 38 +46 97 53 96 51 84 18 42 30 52 82 77 72 59 1 67 72 16 14 63 70 94 20 27 38 70 86 95 41 75 +2 35 45 63 92 76 81 60 62 72 90 46 47 33 1 30 54 22 50 85 63 61 22 79 45 53 45 33 8 28 +43 41 14 79 2 77 95 16 74 19 17 78 47 12 68 55 3 2 77 10 35 86 52 33 47 26 98 42 48 86 +18 32 85 4 91 10 69 68 15 42 58 77 88 64 91 43 56 30 92 11 52 23 43 92 65 50 68 8 80 81 +20 57 38 44 62 10 80 25 32 11 70 32 13 50 41 55 44 0 28 83 5 1 34 94 55 52 56 24 76 21 +36 43 59 28 10 59 4 41 64 98 54 66 44 3 37 41 67 10 85 23 58 35 58 34 35 79 46 18 1 51 +72 63 85 51 23 91 3 56 35 72 38 26 91 0 68 98 27 10 12 71 30 1 14 47 47 88 17 68 78 46 +53 47 1 89 95 53 11 45 46 6 91 20 57 35 58 79 60 3 21 45 4 18 59 96 36 12 13 83 52 46 +33 91 82 24 97 28 50 43 65 22 14 44 32 57 33 10 34 77 58 6 27 90 26 77 62 81 87 96 0 32 +96 44 59 3 47 18 0 91 83 68 48 26 67 82 39 18 88 47 80 0 57 40 30 7 57 74 49 37 57 65 +18 44 0 46 47 30 65 79 53 8 26 42 80 76 30 61 82 93 78 25 89 49 55 15 86 63 35 74 41 11 +18 14 40 90 91 79 80 36 33 72 25 56 73 28 65 27 62 17 60 84 23 70 32 26 77 97 47 94 72 1 +82 36 68 10 83 83 40 42 51 55 82 6 37 69 93 82 64 13 54 30 45 36 87 59 1 80 39 93 11 61 +78 34 53 39 64 52 52 22 33 69 71 82 57 37 78 52 62 31 87 68 70 5 85 94 41 75 38 45 84 22 +36 23 51 15 61 76 88 85 36 96 21 60 34 61 72 60 69 81 5 17 16 82 30 61 39 96 40 70 42 71 +45 30 60 50 78 90 36 40 11 85 42 14 61 3 66 53 68 14 41 30 97 74 79 91 64 8 1 53 52 33 +55 24 35 4 49 51 44 70 93 78 25 65 1 29 96 12 93 94 13 65 4 47 84 10 90 12 36 48 21 36 +17 74 61 54 21 83 35 97 47 90 57 11 16 39 95 78 23 40 23 55 17 51 20 73 98 93 50 32 58 4 +84 76 78 33 50 29 11 20 5 93 63 22 91 92 44 85 62 25 63 92 36 26 57 33 8 74 69 64 78 91 +58 34 91 71 37 84 28 90 28 37 97 7 26 44 59 18 58 64 31 83 16 17 50 36 65 81 19 63 66 64 +20 71 1 35 87 5 47 27 6 95 86 75 74 9 94 93 26 5 61 3 97 88 0 57 21 64 46 24 86 12 +23 53 31 39 37 77 29 51 85 10 41 91 67 82 50 91 53 72 75 81 50 63 52 92 83 49 92 50 26 9 +38 43 13 87 11 45 28 16 27 61 70 52 77 9 57 42 73 22 32 95 23 91 93 63 16 44 26 9 93 83 +77 68 21 96 44 45 9 2 14 2 67 90 55 82 67 21 18 64 31 16 2 27 86 42 34 72 22 98 91 33 +89 66 87 76 0 32 81 39 55 76 23 56 51 53 75 79 30 86 1 66 64 14 46 84 92 19 95 47 77 97 +88 79 61 26 66 92 54 22 15 25 26 0 76 27 17 59 48 4 42 61 65 91 0 62 55 79 29 88 10 11 +24 89 91 39 56 36 16 86 41 31 14 35 7 71 77 74 33 11 49 7 96 83 31 63 90 49 96 22 58 86 +45 7 93 44 50 54 83 80 3 36 11 38 14 17 10 84 96 94 26 34 26 75 72 0 41 89 96 47 39 88 +0 95 2 22 68 38 0 3 51 6 13 10 14 49 75 69 25 39 63 67 12 80 37 77 10 90 60 35 84 37 +98 56 99 75 49 66 3 33 65 86 1 79 91 23 69 98 91 73 95 45 64 26 99 75 49 77 71 55 42 18 +80 39 26 94 85 42 91 27 14 57 36 34 10 44 38 77 23 39 54 25 32 5 17 9 66 3 67 94 20 11 +88 80 30 77 72 67 16 75 84 87 60 89 21 94 24 11 63 8 79 89 37 18 6 82 76 70 81 95 67 95 +92 36 55 55 43 18 76 94 30 74 95 38 45 95 54 87 22 57 4 65 15 90 90 38 73 24 67 24 36 25 +98 30 34 68 11 48 42 38 80 23 12 91 77 22 65 2 88 31 70 12 46 63 17 63 27 76 21 71 70 7 +76 29 56 12 41 66 22 96 8 6 7 13 27 10 77 90 2 76 30 24 81 88 19 16 93 13 30 24 98 96 +45 94 89 41 52 14 71 88 80 74 7 85 44 69 65 88 4 15 84 97 86 5 53 15 39 34 9 10 45 20 +95 47 45 96 71 10 36 10 90 49 7 68 14 46 97 89 82 58 69 34 93 77 90 9 27 91 29 27 22 17 +80 6 29 26 34 59 10 55 32 53 18 72 39 40 29 35 52 64 2 64 38 83 16 46 53 20 19 8 10 67 +47 44 79 32 58 82 26 69 0 26 4 73 95 98 61 96 20 38 3 92 6 5 25 24 42 49 15 92 80 16 +74 37 86 84 47 15 56 36 43 59 72 72 74 73 49 54 26 5 40 80 78 48 4 65 31 70 14 91 88 72 +91 45 73 62 83 40 49 3 27 79 80 90 3 3 58 44 7 66 77 42 37 25 20 91 47 63 71 7 72 22 +51 3 36 90 45 84 18 55 75 78 42 62 86 63 65 67 46 75 1 79 2 85 85 60 36 92 34 89 66 99 +36 99 0 63 89 65 54 58 52 28 98 27 67 1 45 71 35 52 55 55 44 23 46 89 83 37 8 2 92 75 +51 13 71 2 9 95 23 60 24 98 86 43 32 16 75 70 92 78 26 84 29 14 35 55 61 89 73 59 76 44 +59 57 28 92 33 50 70 94 89 67 70 38 53 16 35 70 35 92 39 78 88 80 71 1 93 21 87 64 49 84 +29 6 17 45 38 65 41 48 81 69 34 12 2 14 41 71 16 92 69 27 61 74 58 20 75 19 39 66 57 82 +12 8 14 85 97 31 58 31 20 76 6 42 29 95 60 94 15 84 86 69 73 52 73 57 12 66 89 65 60 84 +20 74 96 34 83 41 8 37 22 36 30 25 20 8 58 73 9 75 76 73 84 38 16 24 95 95 68 66 43 19 +33 15 25 80 48 69 63 39 16 45 6 77 14 46 38 15 64 85 49 5 59 28 9 4 23 68 59 26 1 75 +35 45 3 6 34 59 55 51 81 59 59 93 18 41 8 44 88 7 86 4 88 90 24 54 73 62 89 13 44 92 +72 60 68 83 39 32 30 15 98 92 69 94 51 48 9 0 4 1 30 92 40 1 61 82 66 4 39 10 93 87 +12 20 34 72 33 31 67 71 67 47 98 76 53 29 17 17 13 31 43 76 25 37 8 39 9 5 96 41 87 66 +96 30 2 57 57 10 14 17 86 76 35 94 42 54 18 24 19 34 12 42 18 11 83 65 86 38 45 17 60 70 +19 62 71 99 35 60 96 30 44 80 78 15 14 5 32 43 10 26 81 72 41 98 30 87 75 8 53 33 25 95 +22 0 38 57 88 7 47 83 49 41 52 1 14 93 41 3 18 42 15 57 28 74 97 2 18 48 64 25 77 69 +36 95 65 81 44 41 6 74 62 16 72 81 15 72 31 5 22 17 19 6 7 15 82 10 31 93 11 45 41 11 +22 76 14 62 34 65 82 5 57 51 51 5 1 6 17 43 28 31 90 99 48 14 96 49 95 40 87 85 40 51 +95 13 99 46 52 80 4 18 95 94 0 46 10 80 3 34 60 15 86 10 28 59 6 35 14 93 18 8 3 65 +57 37 6 31 45 85 42 34 47 92 48 40 7 17 5 74 67 62 0 74 58 21 23 3 5 24 50 54 99 19 +24 14 10 4 36 33 88 51 40 66 40 56 65 23 43 13 82 62 27 88 89 91 36 37 19 11 50 39 96 68 +82 7 39 80 52 90 57 17 61 15 51 71 82 15 21 44 4 46 75 50 78 18 63 75 98 45 6 16 57 25 +0 26 56 74 62 84 71 42 25 86 68 10 73 0 71 6 15 99 1 51 45 42 5 49 3 35 84 29 15 36 +60 78 76 3 95 73 36 57 35 44 50 42 85 57 18 69 37 42 75 79 15 12 74 72 51 36 79 3 58 71 +69 24 16 96 17 25 21 94 71 78 74 39 7 96 3 12 13 16 7 99 65 72 12 28 75 44 55 8 75 67 +3 13 92 9 92 83 69 91 65 92 29 63 46 1 4 62 29 85 47 93 81 3 15 23 63 50 17 9 13 13 +9 18 46 53 0 86 10 41 87 89 24 25 70 73 8 23 27 76 66 46 58 39 28 1 99 64 59 13 7 68 +72 57 90 50 47 57 34 27 94 39 23 31 74 77 45 74 18 49 96 8 95 50 20 81 73 55 72 2 32 15 +87 77 74 5 99 86 5 65 97 39 17 74 48 87 20 66 28 2 18 58 49 22 79 23 36 30 64 20 71 32 +35 43 66 96 63 77 18 90 47 86 94 19 88 79 23 12 38 4 56 42 36 2 77 1 3 17 64 52 31 24 +80 2 4 39 61 60 74 83 28 28 61 10 71 82 44 29 55 30 1 58 81 79 34 41 85 82 84 55 22 12 +76 77 58 92 90 0 54 28 77 68 58 12 1 81 37 28 19 60 71 59 25 83 8 49 52 11 28 65 59 70 +14 1 92 90 5 48 28 78 1 42 54 43 60 83 72 19 28 33 12 52 18 15 56 95 39 33 37 70 53 23 +53 76 26 31 18 81 83 79 25 1 82 43 50 24 63 49 5 23 66 37 80 41 63 77 2 28 15 21 32 93 +80 41 81 7 37 95 19 42 57 30 12 25 29 34 41 45 87 8 20 95 63 16 99 55 16 61 16 36 81 25 +32 30 2 81 23 25 88 30 37 76 52 77 79 58 21 58 10 0 13 32 72 80 3 75 75 25 21 9 79 18 +26 13 36 63 43 2 50 41 65 18 88 44 82 75 73 24 1 30 54 68 15 18 22 50 41 99 27 96 51 53 +22 4 76 11 85 88 28 75 1 2 92 66 63 3 58 43 53 5 1 24 99 90 87 87 41 1 85 37 98 92 +16 39 13 88 60 55 35 11 34 23 23 85 79 41 79 87 65 78 47 83 88 78 35 84 30 61 37 58 25 55 +27 33 15 76 82 79 73 92 93 78 18 38 22 96 63 92 41 9 50 96 14 55 8 60 15 61 97 56 43 22 +42 34 94 11 35 70 50 49 36 34 59 14 87 84 88 83 4 69 29 99 35 24 2 18 97 97 74 88 91 49 +33 25 71 12 60 2 48 22 81 33 27 95 54 25 53 14 20 43 26 96 98 37 64 27 72 33 78 45 22 61 +61 21 91 38 92 47 26 90 78 96 58 41 21 72 81 61 55 9 55 60 28 25 25 74 73 81 64 16 49 39 +90 89 12 93 91 23 82 36 63 58 73 81 49 32 60 39 4 84 73 16 18 26 58 85 46 28 82 91 72 7 +79 41 28 76 33 70 47 6 18 64 40 54 45 61 28 63 87 83 38 9 65 68 62 45 80 63 89 29 20 40 +20 59 58 23 61 79 35 19 78 2 26 48 90 34 69 31 31 42 92 33 18 74 28 47 45 52 36 89 19 40 +58 13 72 24 31 26 73 72 84 29 85 99 20 32 54 92 8 80 86 58 23 80 59 21 76 75 90 76 92 57 +74 53 80 51 8 88 84 63 82 99 97 77 38 9 51 61 37 20 68 47 65 21 53 82 85 96 62 65 35 4 +71 82 14 18 88 79 38 76 66 27 10 10 62 54 80 21 6 57 83 33 52 10 97 37 6 38 12 51 0 84 +95 30 75 92 84 30 55 57 32 44 53 24 77 81 34 84 69 85 91 33 50 72 62 79 62 12 59 75 99 81 +38 42 47 1 11 34 27 77 70 85 89 84 79 15 14 54 78 93 72 68 63 39 98 72 55 32 93 0 13 21 +3 15 10 15 3 31 84 89 53 5 60 41 66 77 45 12 68 68 50 68 99 64 46 54 30 56 2 90 99 78 +66 10 27 89 42 16 9 98 16 2 68 51 0 22 73 60 69 96 37 69 30 36 20 21 51 26 65 13 74 86 +94 58 34 97 77 88 90 75 47 30 6 36 89 66 48 9 20 6 52 45 0 37 99 46 11 53 53 72 94 40 +5 71 50 96 89 71 80 43 27 95 49 9 74 28 62 65 64 97 2 55 58 11 69 0 31 22 73 20 66 11 +63 39 84 62 64 5 56 92 26 86 19 20 56 85 42 48 56 51 54 29 26 95 72 38 70 61 16 54 57 19 +76 97 40 99 73 68 98 92 97 62 73 1 29 72 18 70 90 4 98 95 70 36 65 45 86 36 88 38 64 54 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/selfdual-4d-polytope.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/selfdual-4d-polytope.txt new file mode 100644 index 0000000000000000000000000000000000000000..47ce4a7ae522fc2a2bbaa9d8ca285913b8ef0712 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/data/selfdual-4d-polytope.txt @@ -0,0 +1,27 @@ +# The facets of a self-dual 4-dim regular polytope +# with 24 octahedron facets. Taken from cddlib. +# Format b + Ax >= 0 + 1 1 1 1 1 + 1 1 1 1 -1 + 1 1 1 -1 1 + 1 1 1 -1 -1 + 1 1 -1 1 1 + 1 1 -1 1 -1 + 1 1 -1 -1 1 + 1 1 -1 -1 -1 + 1 -1 1 1 1 + 1 -1 1 1 -1 + 1 -1 1 -1 1 + 1 -1 1 -1 -1 + 1 -1 -1 1 1 + 1 -1 -1 1 -1 + 1 -1 -1 -1 1 + 1 -1 -1 -1 -1 + 1 2 0 0 0 + 1 0 2 0 0 + 1 0 0 2 0 + 1 0 0 0 2 + 1 -2 0 0 0 + 1 0 -2 0 0 + 1 0 0 -2 0 + 1 0 0 0 -2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__plotutils.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__plotutils.py new file mode 100644 index 0000000000000000000000000000000000000000..0e2553bf7ad56e97b567e5b334ccf17921f7f7f3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__plotutils.py @@ -0,0 +1,91 @@ +import pytest +import numpy as np +from numpy.testing import assert_, assert_array_equal, assert_allclose + +try: + import matplotlib + matplotlib.rcParams['backend'] = 'Agg' + import matplotlib.pyplot as plt + has_matplotlib = True +except Exception: + has_matplotlib = False + +from scipy.spatial import \ + delaunay_plot_2d, voronoi_plot_2d, convex_hull_plot_2d, \ + Delaunay, Voronoi, ConvexHull + + +@pytest.mark.skipif(not has_matplotlib, reason="Matplotlib not available") +class TestPlotting: + points = [(0,0), (0,1), (1,0), (1,1)] + + def test_delaunay(self): + # Smoke test + fig = plt.figure() + obj = Delaunay(self.points) + s_before = obj.simplices.copy() + r = delaunay_plot_2d(obj, ax=fig.gca()) + assert_array_equal(obj.simplices, s_before) # shouldn't modify + assert_(r is fig) + delaunay_plot_2d(obj, ax=fig.gca()) + + def test_voronoi(self): + # Smoke test + fig = plt.figure() + obj = Voronoi(self.points) + r = voronoi_plot_2d(obj, ax=fig.gca()) + assert_(r is fig) + voronoi_plot_2d(obj) + voronoi_plot_2d(obj, show_vertices=False) + + def test_convex_hull(self): + # Smoke test + fig = plt.figure() + tri = ConvexHull(self.points) + r = convex_hull_plot_2d(tri, ax=fig.gca()) + assert_(r is fig) + convex_hull_plot_2d(tri) + + def test_gh_19653(self): + # aspect ratio sensitivity of voronoi_plot_2d + # infinite Voronoi edges + points = np.array([[245.059986986012, 10.971011721360075], + [320.49044143557785, 10.970258360366753], + [239.79023081978914, 13.108487516946218], + [263.38325791238833, 12.93241352743668], + [219.53334398353175, 13.346107628161008]]) + vor = Voronoi(points) + fig = voronoi_plot_2d(vor) + ax = fig.gca() + infinite_segments = ax.collections[1].get_segments() + expected_segments = np.array([[[282.77256, -254.76904], + [282.729714, -4544.744698]], + [[282.77256014, -254.76904029], + [430.08561382, 4032.67658742]], + [[229.26733285, -20.39957514], + [-168.17167404, -4291.92545966]], + [[289.93433364, 5151.40412217], + [330.40553385, 9441.18887532]]]) + assert_allclose(infinite_segments, expected_segments) + + def test_gh_19653_smaller_aspect(self): + # reasonable behavior for less extreme aspect + # ratio + points = np.array([[24.059986986012, 10.971011721360075], + [32.49044143557785, 10.970258360366753], + [23.79023081978914, 13.108487516946218], + [26.38325791238833, 12.93241352743668], + [21.53334398353175, 13.346107628161008]]) + vor = Voronoi(points) + fig = voronoi_plot_2d(vor) + ax = fig.gca() + infinite_segments = ax.collections[1].get_segments() + expected_segments = np.array([[[28.274979, 8.335027], + [28.270463, -42.19763338]], + [[28.27497869, 8.33502697], + [43.73223829, 56.44555501]], + [[22.51805823, 11.8621754], + [-12.09266506, -24.95694485]], + [[29.53092448, 78.46952378], + [33.82572726, 128.81934455]]]) + assert_allclose(infinite_segments, expected_segments) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__procrustes.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__procrustes.py new file mode 100644 index 0000000000000000000000000000000000000000..42a3c4d35bd55e2ffecefb691c805f517c56d6ca --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test__procrustes.py @@ -0,0 +1,116 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_equal, assert_almost_equal +from pytest import raises as assert_raises + +from scipy.spatial import procrustes + + +class TestProcrustes: + def setup_method(self): + """creates inputs""" + # an L + self.data1 = np.array([[1, 3], [1, 2], [1, 1], [2, 1]], 'd') + + # a larger, shifted, mirrored L + self.data2 = np.array([[4, -2], [4, -4], [4, -6], [2, -6]], 'd') + + # an L shifted up 1, right 1, and with point 4 shifted an extra .5 + # to the right + # pointwise distance disparity with data1: 3*(2) + (1 + 1.5^2) + self.data3 = np.array([[2, 4], [2, 3], [2, 2], [3, 2.5]], 'd') + + # data4, data5 are standardized (trace(A*A') = 1). + # procrustes should return an identical copy if they are used + # as the first matrix argument. + shiftangle = np.pi / 8 + self.data4 = np.array([[1, 0], [0, 1], [-1, 0], + [0, -1]], 'd') / np.sqrt(4) + self.data5 = np.array([[np.cos(shiftangle), np.sin(shiftangle)], + [np.cos(np.pi / 2 - shiftangle), + np.sin(np.pi / 2 - shiftangle)], + [-np.cos(shiftangle), + -np.sin(shiftangle)], + [-np.cos(np.pi / 2 - shiftangle), + -np.sin(np.pi / 2 - shiftangle)]], + 'd') / np.sqrt(4) + + def test_procrustes(self): + # tests procrustes' ability to match two matrices. + # + # the second matrix is a rotated, shifted, scaled, and mirrored version + # of the first, in two dimensions only + # + # can shift, mirror, and scale an 'L'? + a, b, disparity = procrustes(self.data1, self.data2) + assert_allclose(b, a) + assert_almost_equal(disparity, 0.) + + # if first mtx is standardized, leaves first mtx unchanged? + m4, m5, disp45 = procrustes(self.data4, self.data5) + assert_equal(m4, self.data4) + + # at worst, data3 is an 'L' with one point off by .5 + m1, m3, disp13 = procrustes(self.data1, self.data3) + #assert_(disp13 < 0.5 ** 2) + + def test_procrustes2(self): + # procrustes disparity should not depend on order of matrices + m1, m3, disp13 = procrustes(self.data1, self.data3) + m3_2, m1_2, disp31 = procrustes(self.data3, self.data1) + assert_almost_equal(disp13, disp31) + + # try with 3d, 8 pts per + rand1 = np.array([[2.61955202, 0.30522265, 0.55515826], + [0.41124708, -0.03966978, -0.31854548], + [0.91910318, 1.39451809, -0.15295084], + [2.00452023, 0.50150048, 0.29485268], + [0.09453595, 0.67528885, 0.03283872], + [0.07015232, 2.18892599, -1.67266852], + [0.65029688, 1.60551637, 0.80013549], + [-0.6607528, 0.53644208, 0.17033891]]) + + rand3 = np.array([[0.0809969, 0.09731461, -0.173442], + [-1.84888465, -0.92589646, -1.29335743], + [0.67031855, -1.35957463, 0.41938621], + [0.73967209, -0.20230757, 0.52418027], + [0.17752796, 0.09065607, 0.29827466], + [0.47999368, -0.88455717, -0.57547934], + [-0.11486344, -0.12608506, -0.3395779], + [-0.86106154, -0.28687488, 0.9644429]]) + res1, res3, disp13 = procrustes(rand1, rand3) + res3_2, res1_2, disp31 = procrustes(rand3, rand1) + assert_almost_equal(disp13, disp31) + + def test_procrustes_shape_mismatch(self): + assert_raises(ValueError, procrustes, + np.array([[1, 2], [3, 4]]), + np.array([[5, 6, 7], [8, 9, 10]])) + + def test_procrustes_empty_rows_or_cols(self): + empty = np.array([[]]) + assert_raises(ValueError, procrustes, empty, empty) + + def test_procrustes_no_variation(self): + assert_raises(ValueError, procrustes, + np.array([[42, 42], [42, 42]]), + np.array([[45, 45], [45, 45]])) + + def test_procrustes_bad_number_of_dimensions(self): + # fewer dimensions in one dataset + assert_raises(ValueError, procrustes, + np.array([1, 1, 2, 3, 5, 8]), + np.array([[1, 2], [3, 4]])) + + # fewer dimensions in both datasets + assert_raises(ValueError, procrustes, + np.array([1, 1, 2, 3, 5, 8]), + np.array([1, 1, 2, 3, 5, 8])) + + # zero dimensions + assert_raises(ValueError, procrustes, np.array(7), np.array(11)) + + # extra dimensions + assert_raises(ValueError, procrustes, + np.array([[[11], [7]]]), + np.array([[[5, 13]]])) + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_distance.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_distance.py new file mode 100644 index 0000000000000000000000000000000000000000..14989b9a2bc674af468f7d241278b02b75b10a8c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_distance.py @@ -0,0 +1,2267 @@ +# +# Author: Damian Eads +# Date: April 17, 2008 +# +# Copyright (C) 2008 Damian Eads +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys +import os.path + +from functools import wraps, partial +import weakref + +import numpy as np +import warnings +from numpy.linalg import norm +from numpy.testing import (verbose, assert_, + assert_array_equal, assert_equal, + assert_almost_equal, assert_allclose, + break_cycles, IS_PYPY) +import pytest + +import scipy.spatial.distance + +from scipy.spatial.distance import ( + squareform, pdist, cdist, num_obs_y, num_obs_dm, is_valid_dm, is_valid_y, + _validate_vector, _METRICS_NAMES) + +# these were missing: chebyshev cityblock +# jensenshannon and seuclidean are referenced by string name. +from scipy.spatial.distance import (braycurtis, canberra, chebyshev, cityblock, + correlation, cosine, dice, euclidean, + hamming, jaccard, jensenshannon, + kulczynski1, mahalanobis, + minkowski, rogerstanimoto, + russellrao, seuclidean, sokalmichener, # noqa: F401 + sokalsneath, sqeuclidean, yule) +from scipy._lib._util import np_long, np_ulong + + +@pytest.fixture(params=_METRICS_NAMES, scope="session") +def metric(request): + """ + Fixture for all metrics in scipy.spatial.distance + """ + return request.param + + +_filenames = [ + "cdist-X1.txt", + "cdist-X2.txt", + "iris.txt", + "pdist-boolean-inp.txt", + "pdist-chebyshev-ml-iris.txt", + "pdist-chebyshev-ml.txt", + "pdist-cityblock-ml-iris.txt", + "pdist-cityblock-ml.txt", + "pdist-correlation-ml-iris.txt", + "pdist-correlation-ml.txt", + "pdist-cosine-ml-iris.txt", + "pdist-cosine-ml.txt", + "pdist-double-inp.txt", + "pdist-euclidean-ml-iris.txt", + "pdist-euclidean-ml.txt", + "pdist-hamming-ml.txt", + "pdist-jaccard-ml.txt", + "pdist-jensenshannon-ml-iris.txt", + "pdist-jensenshannon-ml.txt", + "pdist-minkowski-3.2-ml-iris.txt", + "pdist-minkowski-3.2-ml.txt", + "pdist-minkowski-5.8-ml-iris.txt", + "pdist-seuclidean-ml-iris.txt", + "pdist-seuclidean-ml.txt", + "pdist-spearman-ml.txt", + "random-bool-data.txt", + "random-double-data.txt", + "random-int-data.txt", + "random-uint-data.txt", + ] + +_tdist = np.array([[0, 662, 877, 255, 412, 996], + [662, 0, 295, 468, 268, 400], + [877, 295, 0, 754, 564, 138], + [255, 468, 754, 0, 219, 869], + [412, 268, 564, 219, 0, 669], + [996, 400, 138, 869, 669, 0]], dtype='double') + +_ytdist = squareform(_tdist) + +# A hashmap of expected output arrays for the tests. These arrays +# come from a list of text files, which are read prior to testing. +# Each test loads inputs and outputs from this dictionary. +eo = {} + + +def load_testing_files(): + for fn in _filenames: + name = fn.replace(".txt", "").replace("-ml", "") + fqfn = os.path.join(os.path.dirname(__file__), 'data', fn) + fp = open(fqfn) + eo[name] = np.loadtxt(fp) + fp.close() + eo['pdist-boolean-inp'] = np.bool_(eo['pdist-boolean-inp']) + eo['random-bool-data'] = np.bool_(eo['random-bool-data']) + eo['random-float32-data'] = np.float32(eo['random-double-data']) + eo['random-int-data'] = np_long(eo['random-int-data']) + eo['random-uint-data'] = np_ulong(eo['random-uint-data']) + + +load_testing_files() + + +def _is_32bit(): + return np.intp(0).itemsize < 8 + + +def _chk_asarrays(arrays, axis=None): + arrays = [np.asanyarray(a) for a in arrays] + if axis is None: + # np < 1.10 ravel removes subclass from arrays + arrays = [np.ravel(a) if a.ndim != 1 else a + for a in arrays] + axis = 0 + arrays = tuple(np.atleast_1d(a) for a in arrays) + if axis < 0: + if not all(a.ndim == arrays[0].ndim for a in arrays): + raise ValueError("array ndim must be the same for neg axis") + axis = range(arrays[0].ndim)[axis] + return arrays + (axis,) + + +def _chk_weights(arrays, weights=None, axis=None, + force_weights=False, simplify_weights=True, + pos_only=False, neg_check=False, + nan_screen=False, mask_screen=False, + ddof=None): + chked = _chk_asarrays(arrays, axis=axis) + arrays, axis = chked[:-1], chked[-1] + + simplify_weights = simplify_weights and not force_weights + if not force_weights and mask_screen: + force_weights = any(np.ma.getmask(a) is not np.ma.nomask for a in arrays) + + if nan_screen: + has_nans = [np.isnan(np.sum(a)) for a in arrays] + if any(has_nans): + mask_screen = True + force_weights = True + arrays = tuple(np.ma.masked_invalid(a) if has_nan else a + for a, has_nan in zip(arrays, has_nans)) + + if weights is not None: + weights = np.asanyarray(weights) + elif force_weights: + weights = np.ones(arrays[0].shape[axis]) + else: + return arrays + (weights, axis) + + if ddof: + weights = _freq_weights(weights) + + if mask_screen: + weights = _weight_masked(arrays, weights, axis) + + if not all(weights.shape == (a.shape[axis],) for a in arrays): + raise ValueError("weights shape must match arrays along axis") + if neg_check and (weights < 0).any(): + raise ValueError("weights cannot be negative") + + if pos_only: + pos_weights = np.nonzero(weights > 0)[0] + if pos_weights.size < weights.size: + arrays = tuple(np.take(a, pos_weights, axis=axis) for a in arrays) + weights = weights[pos_weights] + if simplify_weights and (weights == 1).all(): + weights = None + return arrays + (weights, axis) + + +def _freq_weights(weights): + if weights is None: + return weights + int_weights = weights.astype(int) + if (weights != int_weights).any(): + raise ValueError("frequency (integer count-type) weights required %s" % weights) + return int_weights + + +def _weight_masked(arrays, weights, axis): + if axis is None: + axis = 0 + weights = np.asanyarray(weights) + for a in arrays: + axis_mask = np.ma.getmask(a) + if axis_mask is np.ma.nomask: + continue + if a.ndim > 1: + not_axes = tuple(i for i in range(a.ndim) if i != axis) + axis_mask = axis_mask.any(axis=not_axes) + weights *= 1 - axis_mask.astype(int) + return weights + + +def _rand_split(arrays, weights, axis, split_per, seed=None): + # Coerce `arrays` to float64 if integer, to avoid nan-to-integer issues + arrays = [arr.astype(np.float64) if np.issubdtype(arr.dtype, np.integer) + else arr for arr in arrays] + + # inverse operation for stats.collapse_weights + weights = np.array(weights, dtype=np.float64) # modified inplace; need a copy + seeded_rand = np.random.RandomState(seed) + + def mytake(a, ix, axis): + record = np.asanyarray(np.take(a, ix, axis=axis)) + return record.reshape([a.shape[i] if i != axis else 1 + for i in range(a.ndim)]) + + n_obs = arrays[0].shape[axis] + assert all(a.shape[axis] == n_obs for a in arrays), \ + "data must be aligned on sample axis" + for i in range(int(split_per) * n_obs): + split_ix = seeded_rand.randint(n_obs + i) + prev_w = weights[split_ix] + q = seeded_rand.rand() + weights[split_ix] = q * prev_w + weights = np.append(weights, (1. - q) * prev_w) + arrays = [np.append(a, mytake(a, split_ix, axis=axis), + axis=axis) for a in arrays] + return arrays, weights + + +def _rough_check(a, b, compare_assert=partial(assert_allclose, atol=1e-5), + key=lambda x: x, w=None): + check_a = key(a) + check_b = key(b) + try: + if np.array(check_a != check_b).any(): # try strict equality for string types + compare_assert(check_a, check_b) + except AttributeError: # masked array + compare_assert(check_a, check_b) + except (TypeError, ValueError): # nested data structure + for a_i, b_i in zip(check_a, check_b): + _rough_check(a_i, b_i, compare_assert=compare_assert) + +# diff from test_stats: +# n_args=2, weight_arg='w', default_axis=None +# ma_safe = False, nan_safe = False +def _weight_checked(fn, n_args=2, default_axis=None, key=lambda x: x, weight_arg='w', + squeeze=True, silent=False, + ones_test=True, const_test=True, dup_test=True, + split_test=True, dud_test=True, ma_safe=False, ma_very_safe=False, + nan_safe=False, split_per=1.0, seed=0, + compare_assert=partial(assert_allclose, atol=1e-5)): + """runs fn on its arguments 2 or 3 ways, checks that the results are the same, + then returns the same thing it would have returned before""" + @wraps(fn) + def wrapped(*args, **kwargs): + result = fn(*args, **kwargs) + + arrays = args[:n_args] + rest = args[n_args:] + weights = kwargs.get(weight_arg, None) + axis = kwargs.get('axis', default_axis) + + chked = _chk_weights(arrays, weights=weights, axis=axis, + force_weights=True, mask_screen=True) + arrays, weights, axis = chked[:-2], chked[-2], chked[-1] + if squeeze: + arrays = [np.atleast_1d(a.squeeze()) for a in arrays] + + try: + # WEIGHTS CHECK 1: EQUAL WEIGHTED OBSERVATIONS + args = tuple(arrays) + rest + if ones_test: + kwargs[weight_arg] = weights + _rough_check(result, fn(*args, **kwargs), key=key) + if const_test: + kwargs[weight_arg] = weights * 101.0 + _rough_check(result, fn(*args, **kwargs), key=key) + kwargs[weight_arg] = weights * 0.101 + try: + _rough_check(result, fn(*args, **kwargs), key=key) + except Exception as e: + raise type(e)((e, arrays, weights)) from e + + # WEIGHTS CHECK 2: ADDL 0-WEIGHTED OBS + if dud_test: + # add randomly resampled rows, weighted at 0 + dud_arrays, dud_weights = _rand_split(arrays, weights, axis, + split_per=split_per, seed=seed) + dud_weights[:weights.size] = weights # not exactly 1 because of masked arrays # noqa: E501 + dud_weights[weights.size:] = 0 + dud_args = tuple(dud_arrays) + rest + kwargs[weight_arg] = dud_weights + _rough_check(result, fn(*dud_args, **kwargs), key=key) + # increase the value of those 0-weighted rows + for a in dud_arrays: + indexer = [slice(None)] * a.ndim + indexer[axis] = slice(weights.size, None) + indexer = tuple(indexer) + a[indexer] = a[indexer] * 101 + dud_args = tuple(dud_arrays) + rest + _rough_check(result, fn(*dud_args, **kwargs), key=key) + # set those 0-weighted rows to NaNs + for a in dud_arrays: + indexer = [slice(None)] * a.ndim + indexer[axis] = slice(weights.size, None) + indexer = tuple(indexer) + a[indexer] = a[indexer] * np.nan + if kwargs.get("nan_policy", None) == "omit" and nan_safe: + dud_args = tuple(dud_arrays) + rest + _rough_check(result, fn(*dud_args, **kwargs), key=key) + # mask out those nan values + if ma_safe: + dud_arrays = [np.ma.masked_invalid(a) for a in dud_arrays] + dud_args = tuple(dud_arrays) + rest + _rough_check(result, fn(*dud_args, **kwargs), key=key) + if ma_very_safe: + kwargs[weight_arg] = None + _rough_check(result, fn(*dud_args, **kwargs), key=key) + del dud_arrays, dud_args, dud_weights + + # WEIGHTS CHECK 3: DUPLICATE DATA (DUMB SPLITTING) + if dup_test: + dup_arrays = [np.append(a, a, axis=axis) for a in arrays] + dup_weights = np.append(weights, weights) / 2.0 + dup_args = tuple(dup_arrays) + rest + kwargs[weight_arg] = dup_weights + _rough_check(result, fn(*dup_args, **kwargs), key=key) + del dup_args, dup_arrays, dup_weights + + # WEIGHT CHECK 3: RANDOM SPLITTING + if split_test and split_per > 0: + split = _rand_split(arrays, weights, axis, + split_per=split_per, seed=seed) + split_arrays, split_weights = split + split_args = tuple(split_arrays) + rest + kwargs[weight_arg] = split_weights + _rough_check(result, fn(*split_args, **kwargs), key=key) + except NotImplementedError as e: + # when some combination of arguments makes weighting impossible, + # this is the desired response + if not silent: + warnings.warn(f"{fn.__name__} NotImplemented weights: {e}", + stacklevel=3) + return result + return wrapped + + +wcdist = _weight_checked(cdist, default_axis=1, squeeze=False) +wcdist_no_const = _weight_checked(cdist, default_axis=1, + squeeze=False, const_test=False) +wpdist = _weight_checked(pdist, default_axis=1, squeeze=False, n_args=1) +wpdist_no_const = _weight_checked(pdist, default_axis=1, squeeze=False, + const_test=False, n_args=1) +wrogerstanimoto = _weight_checked(rogerstanimoto) +wmatching = whamming = _weight_checked(hamming, dud_test=False) +wyule = _weight_checked(yule) +wdice = _weight_checked(dice) +wcityblock = _weight_checked(cityblock) +wchebyshev = _weight_checked(chebyshev) +wcosine = _weight_checked(cosine) +wcorrelation = _weight_checked(correlation) +wkulczynski1 = _weight_checked(kulczynski1) +wjaccard = _weight_checked(jaccard) +weuclidean = _weight_checked(euclidean, const_test=False) +wsqeuclidean = _weight_checked(sqeuclidean, const_test=False) +wbraycurtis = _weight_checked(braycurtis) +wcanberra = _weight_checked(canberra, const_test=False) +wsokalsneath = _weight_checked(sokalsneath) +wsokalmichener = _weight_checked(sokalmichener) +wrussellrao = _weight_checked(russellrao) + + +class TestCdist: + + def setup_method(self): + self.rnd_eo_names = ['random-float32-data', 'random-int-data', + 'random-uint-data', 'random-double-data', + 'random-bool-data'] + self.valid_upcasts = {'bool': [np_ulong, np_long, np.float32, np.float64], + 'uint': [np_long, np.float32, np.float64], + 'int': [np.float32, np.float64], + 'float32': [np.float64]} + + def test_cdist_extra_args(self, metric): + # Tests that args and kwargs are correctly handled + + X1 = [[1., 2., 3.], [1.2, 2.3, 3.4], [2.2, 2.3, 4.4]] + X2 = [[7., 5., 8.], [7.5, 5.8, 8.4], [5.5, 5.8, 4.4]] + kwargs = {"N0tV4l1D_p4raM": 3.14, "w": np.arange(3)} + args = [3.14] * 200 + + with pytest.raises(TypeError): + cdist(X1, X2, metric=metric, **kwargs) + with pytest.raises(TypeError): + cdist(X1, X2, metric=eval(metric), **kwargs) + with pytest.raises(TypeError): + cdist(X1, X2, metric="test_" + metric, **kwargs) + with pytest.raises(TypeError): + cdist(X1, X2, metric=metric, *args) + with pytest.raises(TypeError): + cdist(X1, X2, metric=eval(metric), *args) + with pytest.raises(TypeError): + cdist(X1, X2, metric="test_" + metric, *args) + + def test_cdist_extra_args_custom(self): + # Tests that args and kwargs are correctly handled + # also for custom metric + def _my_metric(x, y, arg, kwarg=1, kwarg2=2): + return arg + kwarg + kwarg2 + + X1 = [[1., 2., 3.], [1.2, 2.3, 3.4], [2.2, 2.3, 4.4]] + X2 = [[7., 5., 8.], [7.5, 5.8, 8.4], [5.5, 5.8, 4.4]] + kwargs = {"N0tV4l1D_p4raM": 3.14, "w": np.arange(3)} + args = [3.14] * 200 + + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, *args) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, **kwargs) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, kwarg=2.2, kwarg2=3.3) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1, 2, kwarg=2.2) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1, 2, kwarg=2.2) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1.1, 2.2, 3.3) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1.1, 2.2) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1.1) + with pytest.raises(TypeError): + cdist(X1, X2, _my_metric, 1.1, kwarg=2.2, kwarg2=3.3) + + # this should work + assert_allclose(cdist(X1, X2, metric=_my_metric, + arg=1.1, kwarg2=3.3), 5.4) + + def test_cdist_euclidean_random_unicode(self): + eps = 1e-15 + X1 = eo['cdist-X1'] + X2 = eo['cdist-X2'] + Y1 = wcdist_no_const(X1, X2, 'euclidean') + Y2 = wcdist_no_const(X1, X2, 'test_euclidean') + assert_allclose(Y1, Y2, rtol=eps, verbose=verbose > 2) + + @pytest.mark.parametrize("p", [0.1, 0.25, 1.0, 1.23, + 2.0, 3.8, 4.6, np.inf]) + def test_cdist_minkowski_random(self, p): + eps = 1e-13 + X1 = eo['cdist-X1'] + X2 = eo['cdist-X2'] + Y1 = wcdist_no_const(X1, X2, 'minkowski', p=p) + Y2 = wcdist_no_const(X1, X2, 'test_minkowski', p=p) + assert_allclose(Y1, Y2, atol=0, rtol=eps, verbose=verbose > 2) + + def test_cdist_cosine_random(self): + eps = 1e-14 + X1 = eo['cdist-X1'] + X2 = eo['cdist-X2'] + Y1 = wcdist(X1, X2, 'cosine') + + # Naive implementation + def norms(X): + return np.linalg.norm(X, axis=1).reshape(-1, 1) + + Y2 = 1 - np.dot((X1 / norms(X1)), (X2 / norms(X2)).T) + + assert_allclose(Y1, Y2, rtol=eps, verbose=verbose > 2) + + def test_cdist_mahalanobis(self): + # 1-dimensional observations + x1 = np.array([[2], [3]]) + x2 = np.array([[2], [5]]) + dist = cdist(x1, x2, metric='mahalanobis') + assert_allclose(dist, [[0.0, np.sqrt(4.5)], [np.sqrt(0.5), np.sqrt(2)]]) + + # 2-dimensional observations + x1 = np.array([[0, 0], [-1, 0]]) + x2 = np.array([[0, 2], [1, 0], [0, -2]]) + dist = cdist(x1, x2, metric='mahalanobis') + rt2 = np.sqrt(2) + assert_allclose(dist, [[rt2, rt2, rt2], [2, 2 * rt2, 2]]) + + # Too few observations + with pytest.raises(ValueError): + cdist([[0, 1]], [[2, 3]], metric='mahalanobis') + + def test_cdist_custom_notdouble(self): + class myclass: + pass + + def _my_metric(x, y): + if not isinstance(x[0], myclass) or not isinstance(y[0], myclass): + raise ValueError("Type has been changed") + return 1.123 + data = np.array([[myclass()]], dtype=object) + cdist_y = cdist(data, data, metric=_my_metric) + right_y = 1.123 + assert_equal(cdist_y, right_y, verbose=verbose > 2) + + def _check_calling_conventions(self, X1, X2, metric, eps=1e-07, **kwargs): + # helper function for test_cdist_calling_conventions + try: + y1 = cdist(X1, X2, metric=metric, **kwargs) + y2 = cdist(X1, X2, metric=eval(metric), **kwargs) + y3 = cdist(X1, X2, metric="test_" + metric, **kwargs) + except Exception as e: + e_cls = e.__class__ + if verbose > 2: + print(e_cls.__name__) + print(e) + with pytest.raises(e_cls): + cdist(X1, X2, metric=metric, **kwargs) + with pytest.raises(e_cls): + cdist(X1, X2, metric=eval(metric), **kwargs) + with pytest.raises(e_cls): + cdist(X1, X2, metric="test_" + metric, **kwargs) + else: + assert_allclose(y1, y2, rtol=eps, verbose=verbose > 2) + assert_allclose(y1, y3, rtol=eps, verbose=verbose > 2) + + def test_cdist_calling_conventions(self, metric): + # Ensures that specifying the metric with a str or scipy function + # gives the same behaviour (i.e. same result or same exception). + # NOTE: The correctness should be checked within each metric tests. + for eo_name in self.rnd_eo_names: + # subsampling input data to speed-up tests + # NOTE: num samples needs to be > than dimensions for mahalanobis + X1 = eo[eo_name][::5, ::-2] + X2 = eo[eo_name][1::5, ::2] + if verbose > 2: + print("testing: ", metric, " with: ", eo_name) + if metric in {'dice', 'yule', + 'rogerstanimoto', + 'russellrao', 'sokalmichener', + 'sokalsneath', + 'kulczynski1'} and 'bool' not in eo_name: + # python version permits non-bools e.g. for fuzzy logic + continue + self._check_calling_conventions(X1, X2, metric) + + # Testing built-in metrics with extra args + if metric == "seuclidean": + X12 = np.vstack([X1, X2]).astype(np.float64) + V = np.var(X12, axis=0, ddof=1) + self._check_calling_conventions(X1, X2, metric, V=V) + elif metric == "mahalanobis": + X12 = np.vstack([X1, X2]).astype(np.float64) + V = np.atleast_2d(np.cov(X12.T)) + VI = np.array(np.linalg.inv(V).T) + self._check_calling_conventions(X1, X2, metric, VI=VI) + + def test_cdist_dtype_equivalence(self, metric): + # Tests that the result is not affected by type up-casting + eps = 1e-07 + tests = [(eo['random-bool-data'], self.valid_upcasts['bool']), + (eo['random-uint-data'], self.valid_upcasts['uint']), + (eo['random-int-data'], self.valid_upcasts['int']), + (eo['random-float32-data'], self.valid_upcasts['float32'])] + for test in tests: + X1 = test[0][::5, ::-2] + X2 = test[0][1::5, ::2] + try: + y1 = cdist(X1, X2, metric=metric) + except Exception as e: + e_cls = e.__class__ + if verbose > 2: + print(e_cls.__name__) + print(e) + for new_type in test[1]: + X1new = new_type(X1) + X2new = new_type(X2) + with pytest.raises(e_cls): + cdist(X1new, X2new, metric=metric) + else: + for new_type in test[1]: + y2 = cdist(new_type(X1), new_type(X2), metric=metric) + assert_allclose(y1, y2, rtol=eps, verbose=verbose > 2) + + def test_cdist_out(self, metric): + # Test that out parameter works properly + eps = 1e-15 + X1 = eo['cdist-X1'] + X2 = eo['cdist-X2'] + out_r, out_c = X1.shape[0], X2.shape[0] + + kwargs = dict() + if metric == 'minkowski': + kwargs['p'] = 1.23 + out1 = np.empty((out_r, out_c), dtype=np.float64) + Y1 = cdist(X1, X2, metric, **kwargs) + Y2 = cdist(X1, X2, metric, out=out1, **kwargs) + + # test that output is numerically equivalent + assert_allclose(Y1, Y2, rtol=eps, verbose=verbose > 2) + + # test that Y_test1 and out1 are the same object + assert_(Y2 is out1) + + # test for incorrect shape + out2 = np.empty((out_r-1, out_c+1), dtype=np.float64) + with pytest.raises(ValueError): + cdist(X1, X2, metric, out=out2, **kwargs) + + # test for C-contiguous order + out3 = np.empty( + (2 * out_r, 2 * out_c), dtype=np.float64)[::2, ::2] + out4 = np.empty((out_r, out_c), dtype=np.float64, order='F') + with pytest.raises(ValueError): + cdist(X1, X2, metric, out=out3, **kwargs) + with pytest.raises(ValueError): + cdist(X1, X2, metric, out=out4, **kwargs) + + # test for incorrect dtype + out5 = np.empty((out_r, out_c), dtype=np.int64) + with pytest.raises(ValueError): + cdist(X1, X2, metric, out=out5, **kwargs) + + def test_striding(self, metric): + # test that striding is handled correct with calls to + # _copy_array_if_base_present + eps = 1e-15 + X1 = eo['cdist-X1'][::2, ::2] + X2 = eo['cdist-X2'][::2, ::2] + X1_copy = X1.copy() + X2_copy = X2.copy() + + # confirm equivalence + assert_equal(X1, X1_copy) + assert_equal(X2, X2_copy) + # confirm contiguity + assert_(not X1.flags.c_contiguous) + assert_(not X2.flags.c_contiguous) + assert_(X1_copy.flags.c_contiguous) + assert_(X2_copy.flags.c_contiguous) + + kwargs = dict() + if metric == 'minkowski': + kwargs['p'] = 1.23 + Y1 = cdist(X1, X2, metric, **kwargs) + Y2 = cdist(X1_copy, X2_copy, metric, **kwargs) + # test that output is numerically equivalent + assert_allclose(Y1, Y2, rtol=eps, verbose=verbose > 2) + + def test_cdist_refcount(self, metric): + x1 = np.random.rand(10, 10) + x2 = np.random.rand(10, 10) + + kwargs = dict() + if metric == 'minkowski': + kwargs['p'] = 1.23 + + out = cdist(x1, x2, metric=metric, **kwargs) + + # Check reference counts aren't messed up. If we only hold weak + # references, the arrays should be deallocated. + weak_refs = [weakref.ref(v) for v in (x1, x2, out)] + del x1, x2, out + + if IS_PYPY: + break_cycles() + assert all(weak_ref() is None for weak_ref in weak_refs) + + +class TestPdist: + + def setup_method(self): + self.rnd_eo_names = ['random-float32-data', 'random-int-data', + 'random-uint-data', 'random-double-data', + 'random-bool-data'] + self.valid_upcasts = {'bool': [np_ulong, np_long, np.float32, np.float64], + 'uint': [np_long, np.float32, np.float64], + 'int': [np.float32, np.float64], + 'float32': [np.float64]} + + def test_pdist_extra_args(self, metric): + # Tests that args and kwargs are correctly handled + X1 = [[1., 2.], [1.2, 2.3], [2.2, 2.3]] + kwargs = {"N0tV4l1D_p4raM": 3.14, "w": np.arange(2)} + args = [3.14] * 200 + + with pytest.raises(TypeError): + pdist(X1, metric=metric, **kwargs) + with pytest.raises(TypeError): + pdist(X1, metric=eval(metric), **kwargs) + with pytest.raises(TypeError): + pdist(X1, metric="test_" + metric, **kwargs) + with pytest.raises(TypeError): + pdist(X1, metric=metric, *args) + with pytest.raises(TypeError): + pdist(X1, metric=eval(metric), *args) + with pytest.raises(TypeError): + pdist(X1, metric="test_" + metric, *args) + + def test_pdist_extra_args_custom(self): + # Tests that args and kwargs are correctly handled + # also for custom metric + def _my_metric(x, y, arg, kwarg=1, kwarg2=2): + return arg + kwarg + kwarg2 + + X1 = [[1., 2.], [1.2, 2.3], [2.2, 2.3]] + kwargs = {"N0tV4l1D_p4raM": 3.14, "w": np.arange(2)} + args = [3.14] * 200 + + with pytest.raises(TypeError): + pdist(X1, _my_metric) + with pytest.raises(TypeError): + pdist(X1, _my_metric, *args) + with pytest.raises(TypeError): + pdist(X1, _my_metric, **kwargs) + with pytest.raises(TypeError): + pdist(X1, _my_metric, kwarg=2.2, kwarg2=3.3) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1, 2, kwarg=2.2) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1, 2, kwarg=2.2) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1.1, 2.2, 3.3) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1.1, 2.2) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1.1) + with pytest.raises(TypeError): + pdist(X1, _my_metric, 1.1, kwarg=2.2, kwarg2=3.3) + + # these should work + assert_allclose(pdist(X1, metric=_my_metric, + arg=1.1, kwarg2=3.3), 5.4) + + def test_pdist_euclidean_random(self): + eps = 1e-07 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-euclidean'] + Y_test1 = wpdist_no_const(X, 'euclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_euclidean_random_u(self): + eps = 1e-07 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-euclidean'] + Y_test1 = wpdist_no_const(X, 'euclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_euclidean_random_float32(self): + eps = 1e-07 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-euclidean'] + Y_test1 = wpdist_no_const(X, 'euclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_euclidean_random_nonC(self): + eps = 1e-07 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-euclidean'] + Y_test2 = wpdist_no_const(X, 'test_euclidean') + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_euclidean_iris_double(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-euclidean-iris'] + Y_test1 = wpdist_no_const(X, 'euclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_euclidean_iris_float32(self): + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-euclidean-iris'] + Y_test1 = wpdist_no_const(X, 'euclidean') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + @pytest.mark.slow + def test_pdist_euclidean_iris_nonC(self): + # Test pdist(X, 'test_euclidean') [the non-C implementation] on the + # Iris data set. + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-euclidean-iris'] + Y_test2 = wpdist_no_const(X, 'test_euclidean') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_seuclidean_random(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-seuclidean'] + Y_test1 = pdist(X, 'seuclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_seuclidean_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-seuclidean'] + Y_test1 = pdist(X, 'seuclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + # Check no error is raise when V has float32 dtype (#11171). + V = np.var(X, axis=0, ddof=1) + Y_test2 = pdist(X, 'seuclidean', V=V) + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_seuclidean_random_nonC(self): + # Test pdist(X, 'test_sqeuclidean') [the non-C implementation] + eps = 1e-07 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-seuclidean'] + Y_test2 = pdist(X, 'test_seuclidean') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_seuclidean_iris(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-seuclidean-iris'] + Y_test1 = pdist(X, 'seuclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_seuclidean_iris_float32(self): + # Tests pdist(X, 'seuclidean') on the Iris data set (float32). + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-seuclidean-iris'] + Y_test1 = pdist(X, 'seuclidean') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_seuclidean_iris_nonC(self): + # Test pdist(X, 'test_seuclidean') [the non-C implementation] on the + # Iris data set. + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-seuclidean-iris'] + Y_test2 = pdist(X, 'test_seuclidean') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_cosine_random(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-cosine'] + Y_test1 = wpdist(X, 'cosine') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_cosine_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-cosine'] + Y_test1 = wpdist(X, 'cosine') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_cosine_random_nonC(self): + # Test pdist(X, 'test_cosine') [the non-C implementation] + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-cosine'] + Y_test2 = wpdist(X, 'test_cosine') + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_cosine_iris(self): + eps = 1e-05 + X = eo['iris'] + Y_right = eo['pdist-cosine-iris'] + Y_test1 = wpdist(X, 'cosine') + assert_allclose(Y_test1, Y_right, atol=eps) + + @pytest.mark.slow + def test_pdist_cosine_iris_float32(self): + eps = 1e-05 + X = np.float32(eo['iris']) + Y_right = eo['pdist-cosine-iris'] + Y_test1 = wpdist(X, 'cosine') + assert_allclose(Y_test1, Y_right, atol=eps, verbose=verbose > 2) + + @pytest.mark.slow + def test_pdist_cosine_iris_nonC(self): + eps = 1e-05 + X = eo['iris'] + Y_right = eo['pdist-cosine-iris'] + Y_test2 = wpdist(X, 'test_cosine') + assert_allclose(Y_test2, Y_right, atol=eps) + + def test_pdist_cosine_bounds(self): + # Test adapted from @joernhees's example at gh-5208: case where + # cosine distance used to be negative. XXX: very sensitive to the + # specific norm computation. + x = np.abs(np.random.RandomState(1337).rand(91)) + X = np.vstack([x, x]) + assert_(wpdist(X, 'cosine')[0] >= 0, + msg='cosine distance should be non-negative') + + def test_pdist_cityblock_random(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-cityblock'] + Y_test1 = wpdist_no_const(X, 'cityblock') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_cityblock_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-cityblock'] + Y_test1 = wpdist_no_const(X, 'cityblock') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_cityblock_random_nonC(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-cityblock'] + Y_test2 = wpdist_no_const(X, 'test_cityblock') + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_cityblock_iris(self): + eps = 1e-14 + X = eo['iris'] + Y_right = eo['pdist-cityblock-iris'] + Y_test1 = wpdist_no_const(X, 'cityblock') + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_cityblock_iris_float32(self): + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-cityblock-iris'] + Y_test1 = wpdist_no_const(X, 'cityblock') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + @pytest.mark.slow + def test_pdist_cityblock_iris_nonC(self): + # Test pdist(X, 'test_cityblock') [the non-C implementation] on the + # Iris data set. + eps = 1e-14 + X = eo['iris'] + Y_right = eo['pdist-cityblock-iris'] + Y_test2 = wpdist_no_const(X, 'test_cityblock') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_correlation_random(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-correlation'] + Y_test1 = wpdist(X, 'correlation') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_correlation_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-correlation'] + Y_test1 = wpdist(X, 'correlation') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_correlation_random_nonC(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-correlation'] + Y_test2 = wpdist(X, 'test_correlation') + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_correlation_iris(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-correlation-iris'] + Y_test1 = wpdist(X, 'correlation') + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_correlation_iris_float32(self): + eps = 1e-7 + X = eo['iris'] + Y_right = np.float32(eo['pdist-correlation-iris']) + Y_test1 = wpdist(X, 'correlation') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + @pytest.mark.slow + def test_pdist_correlation_iris_nonC(self): + if sys.maxsize > 2**32: + eps = 1e-7 + else: + pytest.skip("see gh-16456") + X = eo['iris'] + Y_right = eo['pdist-correlation-iris'] + Y_test2 = wpdist(X, 'test_correlation') + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.parametrize("p", [0.1, 0.25, 1.0, 2.0, 3.2, np.inf]) + def test_pdist_minkowski_random_p(self, p): + eps = 1e-13 + X = eo['pdist-double-inp'] + Y1 = wpdist_no_const(X, 'minkowski', p=p) + Y2 = wpdist_no_const(X, 'test_minkowski', p=p) + assert_allclose(Y1, Y2, atol=0, rtol=eps) + + def test_pdist_minkowski_random(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-minkowski-3.2'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=3.2) + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_minkowski_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-minkowski-3.2'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=3.2) + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_minkowski_random_nonC(self): + eps = 1e-7 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-minkowski-3.2'] + Y_test2 = wpdist_no_const(X, 'test_minkowski', p=3.2) + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_minkowski_3_2_iris(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-minkowski-3.2-iris'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=3.2) + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_minkowski_3_2_iris_float32(self): + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-minkowski-3.2-iris'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=3.2) + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_minkowski_3_2_iris_nonC(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-minkowski-3.2-iris'] + Y_test2 = wpdist_no_const(X, 'test_minkowski', p=3.2) + assert_allclose(Y_test2, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_minkowski_5_8_iris(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-minkowski-5.8-iris'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=5.8) + assert_allclose(Y_test1, Y_right, rtol=eps) + + @pytest.mark.slow + def test_pdist_minkowski_5_8_iris_float32(self): + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-minkowski-5.8-iris'] + Y_test1 = wpdist_no_const(X, 'minkowski', p=5.8) + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + @pytest.mark.slow + def test_pdist_minkowski_5_8_iris_nonC(self): + eps = 1e-7 + X = eo['iris'] + Y_right = eo['pdist-minkowski-5.8-iris'] + Y_test2 = wpdist_no_const(X, 'test_minkowski', p=5.8) + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_mahalanobis(self): + # 1-dimensional observations + x = np.array([2.0, 2.0, 3.0, 5.0]).reshape(-1, 1) + dist = pdist(x, metric='mahalanobis') + assert_allclose(dist, [0.0, np.sqrt(0.5), np.sqrt(4.5), + np.sqrt(0.5), np.sqrt(4.5), np.sqrt(2.0)]) + + # 2-dimensional observations + x = np.array([[0, 0], [-1, 0], [0, 2], [1, 0], [0, -2]]) + dist = pdist(x, metric='mahalanobis') + rt2 = np.sqrt(2) + assert_allclose(dist, [rt2, rt2, rt2, rt2, 2, 2 * rt2, 2, 2, 2 * rt2, 2]) + + # Too few observations + with pytest.raises(ValueError): + wpdist([[0, 1], [2, 3]], metric='mahalanobis') + + def test_pdist_hamming_random(self): + eps = 1e-15 + X = eo['pdist-boolean-inp'] + Y_right = eo['pdist-hamming'] + Y_test1 = wpdist(X, 'hamming') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_hamming_random_float32(self): + eps = 1e-15 + X = np.float32(eo['pdist-boolean-inp']) + Y_right = eo['pdist-hamming'] + Y_test1 = wpdist(X, 'hamming') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_hamming_random_nonC(self): + eps = 1e-15 + X = eo['pdist-boolean-inp'] + Y_right = eo['pdist-hamming'] + Y_test2 = wpdist(X, 'test_hamming') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_dhamming_random(self): + eps = 1e-15 + X = np.float64(eo['pdist-boolean-inp']) + Y_right = eo['pdist-hamming'] + Y_test1 = wpdist(X, 'hamming') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_dhamming_random_float32(self): + eps = 1e-15 + X = np.float32(eo['pdist-boolean-inp']) + Y_right = eo['pdist-hamming'] + Y_test1 = wpdist(X, 'hamming') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_dhamming_random_nonC(self): + eps = 1e-15 + X = np.float64(eo['pdist-boolean-inp']) + Y_right = eo['pdist-hamming'] + Y_test2 = wpdist(X, 'test_hamming') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_jaccard_random(self): + eps = 1e-8 + X = eo['pdist-boolean-inp'] + Y_right = eo['pdist-jaccard'] + Y_test1 = wpdist(X, 'jaccard') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_jaccard_random_float32(self): + eps = 1e-8 + X = np.float32(eo['pdist-boolean-inp']) + Y_right = eo['pdist-jaccard'] + Y_test1 = wpdist(X, 'jaccard') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_jaccard_random_nonC(self): + eps = 1e-8 + X = eo['pdist-boolean-inp'] + Y_right = eo['pdist-jaccard'] + Y_test2 = wpdist(X, 'test_jaccard') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_djaccard_random(self): + eps = 1e-8 + X = np.float64(eo['pdist-boolean-inp']) + Y_right = eo['pdist-jaccard'] + Y_test1 = wpdist(X, 'jaccard') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_djaccard_random_float32(self): + eps = 1e-8 + X = np.float32(eo['pdist-boolean-inp']) + Y_right = eo['pdist-jaccard'] + Y_test1 = wpdist(X, 'jaccard') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_djaccard_allzeros(self): + eps = 1e-15 + Y = pdist(np.zeros((5, 3)), 'jaccard') + assert_allclose(np.zeros(10), Y, rtol=eps) + + def test_pdist_djaccard_random_nonC(self): + eps = 1e-8 + X = np.float64(eo['pdist-boolean-inp']) + Y_right = eo['pdist-jaccard'] + Y_test2 = wpdist(X, 'test_jaccard') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_jensenshannon_random(self): + eps = 1e-11 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-jensenshannon'] + Y_test1 = pdist(X, 'jensenshannon') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_jensenshannon_random_float32(self): + eps = 1e-8 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-jensenshannon'] + Y_test1 = pdist(X, 'jensenshannon') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + def test_pdist_jensenshannon_random_nonC(self): + eps = 1e-11 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-jensenshannon'] + Y_test2 = pdist(X, 'test_jensenshannon') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_jensenshannon_iris(self): + if _is_32bit(): + # Test failing on 32-bit Linux on Azure otherwise, see gh-12810 + eps = 2.5e-10 + else: + eps = 1e-12 + + X = eo['iris'] + Y_right = eo['pdist-jensenshannon-iris'] + Y_test1 = pdist(X, 'jensenshannon') + assert_allclose(Y_test1, Y_right, atol=eps) + + def test_pdist_jensenshannon_iris_float32(self): + eps = 1e-06 + X = np.float32(eo['iris']) + Y_right = eo['pdist-jensenshannon-iris'] + Y_test1 = pdist(X, 'jensenshannon') + assert_allclose(Y_test1, Y_right, atol=eps, verbose=verbose > 2) + + def test_pdist_jensenshannon_iris_nonC(self): + eps = 5e-5 + X = eo['iris'] + Y_right = eo['pdist-jensenshannon-iris'] + Y_test2 = pdist(X, 'test_jensenshannon') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_djaccard_allzeros_nonC(self): + eps = 1e-15 + Y = pdist(np.zeros((5, 3)), 'test_jaccard') + assert_allclose(np.zeros(10), Y, rtol=eps) + + def test_pdist_chebyshev_random(self): + eps = 1e-8 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-chebyshev'] + Y_test1 = pdist(X, 'chebyshev') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_chebyshev_random_float32(self): + eps = 1e-7 + X = np.float32(eo['pdist-double-inp']) + Y_right = eo['pdist-chebyshev'] + Y_test1 = pdist(X, 'chebyshev') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + def test_pdist_chebyshev_random_nonC(self): + eps = 1e-8 + X = eo['pdist-double-inp'] + Y_right = eo['pdist-chebyshev'] + Y_test2 = pdist(X, 'test_chebyshev') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_chebyshev_iris(self): + eps = 1e-14 + X = eo['iris'] + Y_right = eo['pdist-chebyshev-iris'] + Y_test1 = pdist(X, 'chebyshev') + assert_allclose(Y_test1, Y_right, rtol=eps) + + def test_pdist_chebyshev_iris_float32(self): + eps = 1e-5 + X = np.float32(eo['iris']) + Y_right = eo['pdist-chebyshev-iris'] + Y_test1 = pdist(X, 'chebyshev') + assert_allclose(Y_test1, Y_right, rtol=eps, verbose=verbose > 2) + + def test_pdist_chebyshev_iris_nonC(self): + eps = 1e-14 + X = eo['iris'] + Y_right = eo['pdist-chebyshev-iris'] + Y_test2 = pdist(X, 'test_chebyshev') + assert_allclose(Y_test2, Y_right, rtol=eps) + + def test_pdist_matching_mtica1(self): + # Test matching(*,*) with mtica example #1 (nums). + m = wmatching(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wmatching(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + assert_allclose(m, 0.6, rtol=0, atol=1e-10) + assert_allclose(m2, 0.6, rtol=0, atol=1e-10) + + def test_pdist_matching_mtica2(self): + # Test matching(*,*) with mtica example #2. + m = wmatching(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wmatching(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + assert_allclose(m, 2 / 3, rtol=0, atol=1e-10) + assert_allclose(m2, 2 / 3, rtol=0, atol=1e-10) + + def test_pdist_jaccard_mtica1(self): + m = wjaccard(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wjaccard(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + assert_allclose(m, 0.6, rtol=0, atol=1e-10) + assert_allclose(m2, 0.6, rtol=0, atol=1e-10) + + def test_pdist_jaccard_mtica2(self): + m = wjaccard(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wjaccard(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + assert_allclose(m, 2 / 3, rtol=0, atol=1e-10) + assert_allclose(m2, 2 / 3, rtol=0, atol=1e-10) + + def test_pdist_yule_mtica1(self): + m = wyule(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wyule(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 2, rtol=0, atol=1e-10) + assert_allclose(m2, 2, rtol=0, atol=1e-10) + + def test_pdist_yule_mtica2(self): + m = wyule(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wyule(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 2, rtol=0, atol=1e-10) + assert_allclose(m2, 2, rtol=0, atol=1e-10) + + def test_pdist_dice_mtica1(self): + m = wdice(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wdice(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 3 / 7, rtol=0, atol=1e-10) + assert_allclose(m2, 3 / 7, rtol=0, atol=1e-10) + + def test_pdist_dice_mtica2(self): + m = wdice(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wdice(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 0.5, rtol=0, atol=1e-10) + assert_allclose(m2, 0.5, rtol=0, atol=1e-10) + + def test_pdist_sokalsneath_mtica1(self): + m = sokalsneath(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = sokalsneath(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 3 / 4, rtol=0, atol=1e-10) + assert_allclose(m2, 3 / 4, rtol=0, atol=1e-10) + + def test_pdist_sokalsneath_mtica2(self): + m = wsokalsneath(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wsokalsneath(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 4 / 5, rtol=0, atol=1e-10) + assert_allclose(m2, 4 / 5, rtol=0, atol=1e-10) + + def test_pdist_rogerstanimoto_mtica1(self): + m = wrogerstanimoto(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wrogerstanimoto(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 3 / 4, rtol=0, atol=1e-10) + assert_allclose(m2, 3 / 4, rtol=0, atol=1e-10) + + def test_pdist_rogerstanimoto_mtica2(self): + m = wrogerstanimoto(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wrogerstanimoto(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 4 / 5, rtol=0, atol=1e-10) + assert_allclose(m2, 4 / 5, rtol=0, atol=1e-10) + + def test_pdist_russellrao_mtica1(self): + m = wrussellrao(np.array([1, 0, 1, 1, 0]), + np.array([1, 1, 0, 1, 1])) + m2 = wrussellrao(np.array([1, 0, 1, 1, 0], dtype=bool), + np.array([1, 1, 0, 1, 1], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 3 / 5, rtol=0, atol=1e-10) + assert_allclose(m2, 3 / 5, rtol=0, atol=1e-10) + + def test_pdist_russellrao_mtica2(self): + m = wrussellrao(np.array([1, 0, 1]), + np.array([1, 1, 0])) + m2 = wrussellrao(np.array([1, 0, 1], dtype=bool), + np.array([1, 1, 0], dtype=bool)) + if verbose > 2: + print(m) + assert_allclose(m, 2 / 3, rtol=0, atol=1e-10) + assert_allclose(m2, 2 / 3, rtol=0, atol=1e-10) + + @pytest.mark.slow + def test_pdist_canberra_match(self): + D = eo['iris'] + if verbose > 2: + print(D.shape, D.dtype) + eps = 1e-15 + y1 = wpdist_no_const(D, "canberra") + y2 = wpdist_no_const(D, "test_canberra") + assert_allclose(y1, y2, rtol=eps, verbose=verbose > 2) + + def test_pdist_canberra_ticket_711(self): + # Test pdist(X, 'canberra') to see if Canberra gives the right result + # as reported on gh-1238. + eps = 1e-8 + pdist_y = wpdist_no_const(([3.3], [3.4]), "canberra") + right_y = 0.01492537 + assert_allclose(pdist_y, right_y, atol=eps, verbose=verbose > 2) + + def test_pdist_custom_notdouble(self): + # tests that when using a custom metric the data type is not altered + class myclass: + pass + + def _my_metric(x, y): + if not isinstance(x[0], myclass) or not isinstance(y[0], myclass): + raise ValueError("Type has been changed") + return 1.123 + data = np.array([[myclass()], [myclass()]], dtype=object) + pdist_y = pdist(data, metric=_my_metric) + right_y = 1.123 + assert_equal(pdist_y, right_y, verbose=verbose > 2) + + def _check_calling_conventions(self, X, metric, eps=1e-07, **kwargs): + # helper function for test_pdist_calling_conventions + try: + y1 = pdist(X, metric=metric, **kwargs) + y2 = pdist(X, metric=eval(metric), **kwargs) + y3 = pdist(X, metric="test_" + metric, **kwargs) + except Exception as e: + e_cls = e.__class__ + if verbose > 2: + print(e_cls.__name__) + print(e) + with pytest.raises(e_cls): + pdist(X, metric=metric, **kwargs) + with pytest.raises(e_cls): + pdist(X, metric=eval(metric), **kwargs) + with pytest.raises(e_cls): + pdist(X, metric="test_" + metric, **kwargs) + else: + assert_allclose(y1, y2, rtol=eps, verbose=verbose > 2) + assert_allclose(y1, y3, rtol=eps, verbose=verbose > 2) + + def test_pdist_calling_conventions(self, metric): + # Ensures that specifying the metric with a str or scipy function + # gives the same behaviour (i.e. same result or same exception). + # NOTE: The correctness should be checked within each metric tests. + # NOTE: Extra args should be checked with a dedicated test + for eo_name in self.rnd_eo_names: + # subsampling input data to speed-up tests + # NOTE: num samples needs to be > than dimensions for mahalanobis + X = eo[eo_name][::5, ::2] + if verbose > 2: + print("testing: ", metric, " with: ", eo_name) + if metric in {'dice', 'yule', 'matching', + 'rogerstanimoto', 'russellrao', 'sokalmichener', + 'sokalsneath', + 'kulczynski1'} and 'bool' not in eo_name: + # python version permits non-bools e.g. for fuzzy logic + continue + self._check_calling_conventions(X, metric) + + # Testing built-in metrics with extra args + if metric == "seuclidean": + V = np.var(X.astype(np.float64), axis=0, ddof=1) + self._check_calling_conventions(X, metric, V=V) + elif metric == "mahalanobis": + V = np.atleast_2d(np.cov(X.astype(np.float64).T)) + VI = np.array(np.linalg.inv(V).T) + self._check_calling_conventions(X, metric, VI=VI) + + def test_pdist_dtype_equivalence(self, metric): + # Tests that the result is not affected by type up-casting + eps = 1e-07 + tests = [(eo['random-bool-data'], self.valid_upcasts['bool']), + (eo['random-uint-data'], self.valid_upcasts['uint']), + (eo['random-int-data'], self.valid_upcasts['int']), + (eo['random-float32-data'], self.valid_upcasts['float32'])] + for test in tests: + X1 = test[0][::5, ::2] + try: + y1 = pdist(X1, metric=metric) + except Exception as e: + e_cls = e.__class__ + if verbose > 2: + print(e_cls.__name__) + print(e) + for new_type in test[1]: + X2 = new_type(X1) + with pytest.raises(e_cls): + pdist(X2, metric=metric) + else: + for new_type in test[1]: + y2 = pdist(new_type(X1), metric=metric) + assert_allclose(y1, y2, rtol=eps, verbose=verbose > 2) + + def test_pdist_out(self, metric): + # Test that out parameter works properly + eps = 1e-15 + X = eo['random-float32-data'][::5, ::2] + out_size = int((X.shape[0] * (X.shape[0] - 1)) / 2) + + kwargs = dict() + if metric == 'minkowski': + kwargs['p'] = 1.23 + out1 = np.empty(out_size, dtype=np.float64) + Y_right = pdist(X, metric, **kwargs) + Y_test1 = pdist(X, metric, out=out1, **kwargs) + + # test that output is numerically equivalent + assert_allclose(Y_test1, Y_right, rtol=eps) + + # test that Y_test1 and out1 are the same object + assert_(Y_test1 is out1) + + # test for incorrect shape + out2 = np.empty(out_size + 3, dtype=np.float64) + with pytest.raises(ValueError): + pdist(X, metric, out=out2, **kwargs) + + # test for (C-)contiguous output + out3 = np.empty(2 * out_size, dtype=np.float64)[::2] + with pytest.raises(ValueError): + pdist(X, metric, out=out3, **kwargs) + + # test for incorrect dtype + out5 = np.empty(out_size, dtype=np.int64) + with pytest.raises(ValueError): + pdist(X, metric, out=out5, **kwargs) + + def test_striding(self, metric): + # test that striding is handled correct with calls to + # _copy_array_if_base_present + eps = 1e-15 + X = eo['random-float32-data'][::5, ::2] + X_copy = X.copy() + + # confirm contiguity + assert_(not X.flags.c_contiguous) + assert_(X_copy.flags.c_contiguous) + + kwargs = dict() + if metric == 'minkowski': + kwargs['p'] = 1.23 + Y1 = pdist(X, metric, **kwargs) + Y2 = pdist(X_copy, metric, **kwargs) + # test that output is numerically equivalent + assert_allclose(Y1, Y2, rtol=eps, verbose=verbose > 2) + +class TestSomeDistanceFunctions: + + def setup_method(self): + # 1D arrays + x = np.array([1.0, 2.0, 3.0]) + y = np.array([1.0, 1.0, 5.0]) + + self.cases = [(x, y)] + + def test_minkowski(self): + for x, y in self.cases: + dist1 = minkowski(x, y, p=1) + assert_almost_equal(dist1, 3.0) + dist1p5 = minkowski(x, y, p=1.5) + assert_almost_equal(dist1p5, (1.0 + 2.0**1.5)**(2. / 3)) + dist2 = minkowski(x, y, p=2) + assert_almost_equal(dist2, 5.0 ** 0.5) + dist0p25 = minkowski(x, y, p=0.25) + assert_almost_equal(dist0p25, (1.0 + 2.0 ** 0.25) ** 4) + + # Check that casting input to minimum scalar type doesn't affect result + # (issue #10262). This could be extended to more test inputs with + # np.min_scalar_type(np.max(input_matrix)). + a = np.array([352, 916]) + b = np.array([350, 660]) + assert_equal(minkowski(a, b), + minkowski(a.astype('uint16'), b.astype('uint16'))) + + def test_euclidean(self): + for x, y in self.cases: + dist = weuclidean(x, y) + assert_almost_equal(dist, np.sqrt(5)) + + def test_sqeuclidean(self): + for x, y in self.cases: + dist = wsqeuclidean(x, y) + assert_almost_equal(dist, 5.0) + + def test_cosine(self): + for x, y in self.cases: + dist = wcosine(x, y) + assert_almost_equal(dist, 1.0 - 18.0 / (np.sqrt(14) * np.sqrt(27))) + + def test_cosine_output_dtype(self): + # Regression test for gh-19541 + assert isinstance(wcorrelation([1, 1], [1, 1], centered=False), float) + assert isinstance(wcosine([1, 1], [1, 1]), float) + + def test_correlation(self): + xm = np.array([-1.0, 0, 1.0]) + ym = np.array([-4.0 / 3, -4.0 / 3, 5.0 - 7.0 / 3]) + for x, y in self.cases: + dist = wcorrelation(x, y) + assert_almost_equal(dist, 1.0 - np.dot(xm, ym) / (norm(xm) * norm(ym))) + + def test_correlation_positive(self): + # Regression test for gh-12320 (negative return value due to rounding + x = np.array([0., 0., 0., 0., 0., 0., -2., 0., 0., 0., -2., -2., -2., + 0., -2., 0., -2., 0., 0., -1., -2., 0., 1., 0., 0., -2., + 0., 0., -2., 0., -2., -2., -2., -2., -2., -2., 0.]) + y = np.array([1., 1., 1., 1., 1., 1., -1., 1., 1., 1., -1., -1., -1., + 1., -1., 1., -1., 1., 1., 0., -1., 1., 2., 1., 1., -1., + 1., 1., -1., 1., -1., -1., -1., -1., -1., -1., 1.]) + dist = correlation(x, y) + assert 0 <= dist <= 10 * np.finfo(np.float64).eps + + def test_mahalanobis(self): + x = np.array([1.0, 2.0, 3.0]) + y = np.array([1.0, 1.0, 5.0]) + vi = np.array([[2.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 2.0]]) + for x, y in self.cases: + dist = mahalanobis(x, y, vi) + assert_almost_equal(dist, np.sqrt(6.0)) + + +class TestSquareForm: + checked_dtypes = [np.float64, np.float32, np.int32, np.int8, bool] + + def test_squareform_matrix(self): + for dtype in self.checked_dtypes: + self.check_squareform_matrix(dtype) + + def test_squareform_vector(self): + for dtype in self.checked_dtypes: + self.check_squareform_vector(dtype) + + def check_squareform_matrix(self, dtype): + A = np.zeros((0, 0), dtype=dtype) + rA = squareform(A) + assert_equal(rA.shape, (0,)) + assert_equal(rA.dtype, dtype) + + A = np.zeros((1, 1), dtype=dtype) + rA = squareform(A) + assert_equal(rA.shape, (0,)) + assert_equal(rA.dtype, dtype) + + A = np.array([[0, 4.2], [4.2, 0]], dtype=dtype) + rA = squareform(A) + assert_equal(rA.shape, (1,)) + assert_equal(rA.dtype, dtype) + assert_array_equal(rA, np.array([4.2], dtype=dtype)) + + def check_squareform_vector(self, dtype): + v = np.zeros((0,), dtype=dtype) + rv = squareform(v) + assert_equal(rv.shape, (1, 1)) + assert_equal(rv.dtype, dtype) + assert_array_equal(rv, [[0]]) + + v = np.array([8.3], dtype=dtype) + rv = squareform(v) + assert_equal(rv.shape, (2, 2)) + assert_equal(rv.dtype, dtype) + assert_array_equal(rv, np.array([[0, 8.3], [8.3, 0]], dtype=dtype)) + + def test_squareform_multi_matrix(self): + for n in range(2, 5): + self.check_squareform_multi_matrix(n) + + def check_squareform_multi_matrix(self, n): + X = np.random.rand(n, 4) + Y = wpdist_no_const(X) + assert_equal(len(Y.shape), 1) + A = squareform(Y) + Yr = squareform(A) + s = A.shape + k = 0 + if verbose >= 3: + print(A.shape, Y.shape, Yr.shape) + assert_equal(len(s), 2) + assert_equal(len(Yr.shape), 1) + assert_equal(s[0], s[1]) + for i in range(0, s[0]): + for j in range(i + 1, s[1]): + if i != j: + assert_equal(A[i, j], Y[k]) + k += 1 + else: + assert_equal(A[i, j], 0) + + +class TestNumObsY: + + def test_num_obs_y_multi_matrix(self): + for n in range(2, 10): + X = np.random.rand(n, 4) + Y = wpdist_no_const(X) + assert_equal(num_obs_y(Y), n) + + def test_num_obs_y_1(self): + # Tests num_obs_y(y) on a condensed distance matrix over 1 + # observations. Expecting exception. + with pytest.raises(ValueError): + self.check_y(1) + + def test_num_obs_y_2(self): + # Tests num_obs_y(y) on a condensed distance matrix over 2 + # observations. + assert_(self.check_y(2)) + + def test_num_obs_y_3(self): + assert_(self.check_y(3)) + + def test_num_obs_y_4(self): + assert_(self.check_y(4)) + + def test_num_obs_y_5_10(self): + for i in range(5, 16): + self.minit(i) + + def test_num_obs_y_2_100(self): + # Tests num_obs_y(y) on 100 improper condensed distance matrices. + # Expecting exception. + a = set() + for n in range(2, 16): + a.add(n * (n - 1) / 2) + for i in range(5, 105): + if i not in a: + with pytest.raises(ValueError): + self.bad_y(i) + + def minit(self, n): + assert_(self.check_y(n)) + + def bad_y(self, n): + y = np.random.rand(n) + return num_obs_y(y) + + def check_y(self, n): + return num_obs_y(self.make_y(n)) == n + + def make_y(self, n): + return np.random.rand((n * (n - 1)) // 2) + + +class TestNumObsDM: + + def test_num_obs_dm_multi_matrix(self): + for n in range(1, 10): + X = np.random.rand(n, 4) + Y = wpdist_no_const(X) + A = squareform(Y) + if verbose >= 3: + print(A.shape, Y.shape) + assert_equal(num_obs_dm(A), n) + + def test_num_obs_dm_0(self): + # Tests num_obs_dm(D) on a 0x0 distance matrix. Expecting exception. + assert_(self.check_D(0)) + + def test_num_obs_dm_1(self): + # Tests num_obs_dm(D) on a 1x1 distance matrix. + assert_(self.check_D(1)) + + def test_num_obs_dm_2(self): + assert_(self.check_D(2)) + + def test_num_obs_dm_3(self): + assert_(self.check_D(2)) + + def test_num_obs_dm_4(self): + assert_(self.check_D(4)) + + def check_D(self, n): + return num_obs_dm(self.make_D(n)) == n + + def make_D(self, n): + return np.random.rand(n, n) + + +def is_valid_dm_throw(D): + return is_valid_dm(D, throw=True) + + +class TestIsValidDM: + + def test_is_valid_dm_improper_shape_1D_E(self): + D = np.zeros((5,), dtype=np.float64) + with pytest.raises(ValueError): + is_valid_dm_throw(D) + + def test_is_valid_dm_improper_shape_1D_F(self): + D = np.zeros((5,), dtype=np.float64) + assert_equal(is_valid_dm(D), False) + + def test_is_valid_dm_improper_shape_3D_E(self): + D = np.zeros((3, 3, 3), dtype=np.float64) + with pytest.raises(ValueError): + is_valid_dm_throw(D) + + def test_is_valid_dm_improper_shape_3D_F(self): + D = np.zeros((3, 3, 3), dtype=np.float64) + assert_equal(is_valid_dm(D), False) + + def test_is_valid_dm_nonzero_diagonal_E(self): + y = np.random.rand(10) + D = squareform(y) + for i in range(0, 5): + D[i, i] = 2.0 + with pytest.raises(ValueError): + is_valid_dm_throw(D) + + def test_is_valid_dm_nonzero_diagonal_F(self): + y = np.random.rand(10) + D = squareform(y) + for i in range(0, 5): + D[i, i] = 2.0 + assert_equal(is_valid_dm(D), False) + + def test_is_valid_dm_asymmetric_E(self): + y = np.random.rand(10) + D = squareform(y) + D[1, 3] = D[3, 1] + 1 + with pytest.raises(ValueError): + is_valid_dm_throw(D) + + def test_is_valid_dm_asymmetric_F(self): + y = np.random.rand(10) + D = squareform(y) + D[1, 3] = D[3, 1] + 1 + assert_equal(is_valid_dm(D), False) + + def test_is_valid_dm_correct_1_by_1(self): + D = np.zeros((1, 1), dtype=np.float64) + assert_equal(is_valid_dm(D), True) + + def test_is_valid_dm_correct_2_by_2(self): + y = np.random.rand(1) + D = squareform(y) + assert_equal(is_valid_dm(D), True) + + def test_is_valid_dm_correct_3_by_3(self): + y = np.random.rand(3) + D = squareform(y) + assert_equal(is_valid_dm(D), True) + + def test_is_valid_dm_correct_4_by_4(self): + y = np.random.rand(6) + D = squareform(y) + assert_equal(is_valid_dm(D), True) + + def test_is_valid_dm_correct_5_by_5(self): + y = np.random.rand(10) + D = squareform(y) + assert_equal(is_valid_dm(D), True) + + +def is_valid_y_throw(y): + return is_valid_y(y, throw=True) + + +class TestIsValidY: + # If test case name ends on "_E" then an exception is expected for the + # given input, if it ends in "_F" then False is expected for the is_valid_y + # check. Otherwise the input is expected to be valid. + + def test_is_valid_y_improper_shape_2D_E(self): + y = np.zeros((3, 3,), dtype=np.float64) + with pytest.raises(ValueError): + is_valid_y_throw(y) + + def test_is_valid_y_improper_shape_2D_F(self): + y = np.zeros((3, 3,), dtype=np.float64) + assert_equal(is_valid_y(y), False) + + def test_is_valid_y_improper_shape_3D_E(self): + y = np.zeros((3, 3, 3), dtype=np.float64) + with pytest.raises(ValueError): + is_valid_y_throw(y) + + def test_is_valid_y_improper_shape_3D_F(self): + y = np.zeros((3, 3, 3), dtype=np.float64) + assert_equal(is_valid_y(y), False) + + def test_is_valid_y_correct_2_by_2(self): + y = self.correct_n_by_n(2) + assert_equal(is_valid_y(y), True) + + def test_is_valid_y_correct_3_by_3(self): + y = self.correct_n_by_n(3) + assert_equal(is_valid_y(y), True) + + def test_is_valid_y_correct_4_by_4(self): + y = self.correct_n_by_n(4) + assert_equal(is_valid_y(y), True) + + def test_is_valid_y_correct_5_by_5(self): + y = self.correct_n_by_n(5) + assert_equal(is_valid_y(y), True) + + def test_is_valid_y_2_100(self): + a = set() + for n in range(2, 16): + a.add(n * (n - 1) / 2) + for i in range(5, 105): + if i not in a: + with pytest.raises(ValueError): + self.bad_y(i) + + def bad_y(self, n): + y = np.random.rand(n) + return is_valid_y(y, throw=True) + + def correct_n_by_n(self, n): + y = np.random.rand((n * (n - 1)) // 2) + return y + + +@pytest.mark.parametrize("p", [-10.0, -0.5, 0.0]) +def test_bad_p(p): + # Raise ValueError if p <=0. + with pytest.raises(ValueError): + minkowski([1, 2], [3, 4], p) + with pytest.raises(ValueError): + minkowski([1, 2], [3, 4], p, [1, 1]) + + +def test_sokalsneath_all_false(): + # Regression test for ticket #876 + with pytest.raises(ValueError): + sokalsneath([False, False, False], [False, False, False]) + + +def test_canberra(): + # Regression test for ticket #1430. + assert_equal(wcanberra([1, 2, 3], [2, 4, 6]), 1) + assert_equal(wcanberra([1, 1, 0, 0], [1, 0, 1, 0]), 2) + + +def test_braycurtis(): + # Regression test for ticket #1430. + assert_almost_equal(wbraycurtis([1, 2, 3], [2, 4, 6]), 1. / 3, decimal=15) + assert_almost_equal(wbraycurtis([1, 1, 0, 0], [1, 0, 1, 0]), 0.5, decimal=15) + + +def test_euclideans(): + # Regression test for ticket #1328. + x1 = np.array([1, 1, 1]) + x2 = np.array([0, 0, 0]) + + # Basic test of the calculation. + assert_almost_equal(wsqeuclidean(x1, x2), 3.0, decimal=14) + assert_almost_equal(weuclidean(x1, x2), np.sqrt(3), decimal=14) + + # Check flattening for (1, N) or (N, 1) inputs + with pytest.raises(ValueError, match="Input vector should be 1-D"): + weuclidean(x1[np.newaxis, :], x2[np.newaxis, :]), np.sqrt(3) + with pytest.raises(ValueError, match="Input vector should be 1-D"): + wsqeuclidean(x1[np.newaxis, :], x2[np.newaxis, :]) + with pytest.raises(ValueError, match="Input vector should be 1-D"): + wsqeuclidean(x1[:, np.newaxis], x2[:, np.newaxis]) + + # Distance metrics only defined for vectors (= 1-D) + x = np.arange(4).reshape(2, 2) + with pytest.raises(ValueError): + weuclidean(x, x) + with pytest.raises(ValueError): + wsqeuclidean(x, x) + + # Another check, with random data. + rs = np.random.RandomState(1234567890) + x = rs.rand(10) + y = rs.rand(10) + d1 = weuclidean(x, y) + d2 = wsqeuclidean(x, y) + assert_almost_equal(d1**2, d2, decimal=14) + + +def test_hamming_unequal_length(): + # Regression test for gh-4290. + x = [0, 0, 1] + y = [1, 0, 1, 0] + # Used to give an AttributeError from ndarray.mean called on bool + with pytest.raises(ValueError): + whamming(x, y) + + +def test_hamming_unequal_length_with_w(): + u = [0, 0, 1] + v = [0, 0, 1] + w = [1, 0, 1, 0] + msg = "'w' should have the same length as 'u' and 'v'." + with pytest.raises(ValueError, match=msg): + whamming(u, v, w) + + +def test_hamming_string_array(): + # https://github.com/scikit-learn/scikit-learn/issues/4014 + a = np.array(['eggs', 'spam', 'spam', 'eggs', 'spam', 'spam', 'spam', + 'spam', 'spam', 'spam', 'spam', 'eggs', 'eggs', 'spam', + 'eggs', 'eggs', 'eggs', 'eggs', 'eggs', 'spam'], + dtype='|S4') + b = np.array(['eggs', 'spam', 'spam', 'eggs', 'eggs', 'spam', 'spam', + 'spam', 'spam', 'eggs', 'spam', 'eggs', 'spam', 'eggs', + 'spam', 'spam', 'eggs', 'spam', 'spam', 'eggs'], + dtype='|S4') + desired = 0.45 + assert_allclose(whamming(a, b), desired) + + +def test_minkowski_w(): + # Regression test for gh-8142. + arr_in = np.array([[83.33333333, 100., 83.33333333, 100., 36., + 60., 90., 150., 24., 48.], + [83.33333333, 100., 83.33333333, 100., 36., + 60., 90., 150., 24., 48.]]) + p0 = pdist(arr_in, metric='minkowski', p=1, w=None) + c0 = cdist(arr_in, arr_in, metric='minkowski', p=1, w=None) + p1 = pdist(arr_in, metric='minkowski', p=1) + c1 = cdist(arr_in, arr_in, metric='minkowski', p=1) + + assert_allclose(p0, p1, rtol=1e-15) + assert_allclose(c0, c1, rtol=1e-15) + + +def test_sqeuclidean_dtypes(): + # Assert that sqeuclidean returns the right types of values. + # Integer types should be converted to floating for stability. + # Floating point types should be the same as the input. + x = [1, 2, 3] + y = [4, 5, 6] + + for dtype in [np.int8, np.int16, np.int32, np.int64]: + d = wsqeuclidean(np.asarray(x, dtype=dtype), np.asarray(y, dtype=dtype)) + assert_(np.issubdtype(d.dtype, np.floating)) + + for dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: + umax = np.iinfo(dtype).max + d1 = wsqeuclidean([0], np.asarray([umax], dtype=dtype)) + d2 = wsqeuclidean(np.asarray([umax], dtype=dtype), [0]) + + assert_equal(d1, d2) + assert_equal(d1, np.float64(umax)**2) + + dtypes = [np.float32, np.float64, np.complex64, np.complex128] + for dtype in ['float16', 'float128']: + # These aren't present in older numpy versions; float128 may also not + # be present on all platforms. + if hasattr(np, dtype): + dtypes.append(getattr(np, dtype)) + + for dtype in dtypes: + d = wsqeuclidean(np.asarray(x, dtype=dtype), np.asarray(y, dtype=dtype)) + assert_equal(d.dtype, dtype) + + +def test_sokalmichener(): + # Test that sokalmichener has the same result for bool and int inputs. + p = [True, True, False] + q = [True, False, True] + x = [int(b) for b in p] + y = [int(b) for b in q] + dist1 = sokalmichener(p, q) + dist2 = sokalmichener(x, y) + # These should be exactly the same. + assert_equal(dist1, dist2) + + +def test_sokalmichener_with_weight(): + # from: | 1 | | 0 | + # to: | 1 | | 1 | + # weight| | 1 | | 0.2 + ntf = 0 * 1 + 0 * 0.2 + nft = 0 * 1 + 1 * 0.2 + ntt = 1 * 1 + 0 * 0.2 + nff = 0 * 1 + 0 * 0.2 + expected = 2 * (nft + ntf) / (ntt + nff + 2 * (nft + ntf)) + assert_almost_equal(expected, 0.2857143) + actual = sokalmichener([1, 0], [1, 1], w=[1, 0.2]) + assert_almost_equal(expected, actual) + + a1 = [False, False, True, True, True, False, False, True, True, True, True, + True, True, False, True, False, False, False, True, True] + a2 = [True, True, True, False, False, True, True, True, False, True, + True, True, True, True, False, False, False, True, True, True] + + for w in [0.05, 0.1, 1.0, 20.0]: + assert_almost_equal(sokalmichener(a2, a1, [w]), 0.6666666666666666) + + +def test_modifies_input(metric): + # test whether cdist or pdist modifies input arrays + X1 = np.asarray([[1., 2., 3.], + [1.2, 2.3, 3.4], + [2.2, 2.3, 4.4], + [22.2, 23.3, 44.4]]) + X1_copy = X1.copy() + cdist(X1, X1, metric) + pdist(X1, metric) + assert_array_equal(X1, X1_copy) + + +def test_Xdist_deprecated_args(metric): + # testing both cdist and pdist deprecated warnings + X1 = np.asarray([[1., 2., 3.], + [1.2, 2.3, 3.4], + [2.2, 2.3, 4.4], + [22.2, 23.3, 44.4]]) + + with pytest.raises(TypeError): + cdist(X1, X1, metric, 2.) + + with pytest.raises(TypeError): + pdist(X1, metric, 2.) + + for arg in ["p", "V", "VI"]: + kwargs = {arg: "foo"} + + if ((arg == "V" and metric == "seuclidean") + or (arg == "VI" and metric == "mahalanobis") + or (arg == "p" and metric == "minkowski")): + continue + + with pytest.raises(TypeError): + cdist(X1, X1, metric, **kwargs) + + with pytest.raises(TypeError): + pdist(X1, metric, **kwargs) + + +def test_Xdist_non_negative_weights(metric): + X = eo['random-float32-data'][::5, ::2] + w = np.ones(X.shape[1]) + w[::5] = -w[::5] + + if metric in ['seuclidean', 'mahalanobis', 'jensenshannon']: + pytest.skip("not applicable") + + for m in [metric, eval(metric), "test_" + metric]: + with pytest.raises(ValueError): + pdist(X, m, w=w) + with pytest.raises(ValueError): + cdist(X, X, m, w=w) + + +def test__validate_vector(): + x = [1, 2, 3] + y = _validate_vector(x) + assert_array_equal(y, x) + + y = _validate_vector(x, dtype=np.float64) + assert_array_equal(y, x) + assert_equal(y.dtype, np.float64) + + x = [1] + y = _validate_vector(x) + assert_equal(y.ndim, 1) + assert_equal(y, x) + + x = 1 + with pytest.raises(ValueError, match="Input vector should be 1-D"): + _validate_vector(x) + + x = np.arange(5).reshape(1, -1, 1) + with pytest.raises(ValueError, match="Input vector should be 1-D"): + _validate_vector(x) + + x = [[1, 2], [3, 4]] + with pytest.raises(ValueError, match="Input vector should be 1-D"): + _validate_vector(x) + +def test_yule_all_same(): + # Test yule avoids a divide by zero when exactly equal + x = np.ones((2, 6), dtype=bool) + d = wyule(x[0], x[0]) + assert d == 0.0 + + d = pdist(x, 'yule') + assert_equal(d, [0.0]) + + d = cdist(x[:1], x[:1], 'yule') + assert_equal(d, [[0.0]]) + + +def test_jensenshannon(): + assert_almost_equal(jensenshannon([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], 2.0), + 1.0) + assert_almost_equal(jensenshannon([1.0, 0.0], [0.5, 0.5]), + 0.46450140402245893) + assert_almost_equal(jensenshannon([1.0, 0.0, 0.0], [1.0, 0.0, 0.0]), 0.0) + + assert_almost_equal(jensenshannon([[1.0, 2.0]], [[0.5, 1.5]], axis=0), + [0.0, 0.0]) + assert_almost_equal(jensenshannon([[1.0, 2.0]], [[0.5, 1.5]], axis=1), + [0.0649045]) + assert_almost_equal(jensenshannon([[1.0, 2.0]], [[0.5, 1.5]], axis=0, + keepdims=True), [[0.0, 0.0]]) + assert_almost_equal(jensenshannon([[1.0, 2.0]], [[0.5, 1.5]], axis=1, + keepdims=True), [[0.0649045]]) + + a = np.array([[1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12]]) + b = np.array([[13, 14, 15, 16], + [17, 18, 19, 20], + [21, 22, 23, 24]]) + + assert_almost_equal(jensenshannon(a, b, axis=0), + [0.1954288, 0.1447697, 0.1138377, 0.0927636]) + assert_almost_equal(jensenshannon(a, b, axis=1), + [0.1402339, 0.0399106, 0.0201815]) + + +def test_gh_17703(): + arr_1 = np.array([1, 0, 0]) + arr_2 = np.array([2, 0, 0]) + expected = dice(arr_1, arr_2) + actual = pdist([arr_1, arr_2], metric='dice') + assert_allclose(actual, expected) + actual = cdist(np.atleast_2d(arr_1), + np.atleast_2d(arr_2), metric='dice') + assert_allclose(actual, expected) + + +def test_immutable_input(metric): + if metric in ("jensenshannon", "mahalanobis", "seuclidean"): + pytest.skip("not applicable") + x = np.arange(10, dtype=np.float64) + x.setflags(write=False) + getattr(scipy.spatial.distance, metric)(x, x, w=x) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_hausdorff.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_hausdorff.py new file mode 100644 index 0000000000000000000000000000000000000000..c5a5255a7c6c1a9c85f4526ec1bcae67f745307b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_hausdorff.py @@ -0,0 +1,172 @@ +import numpy as np +from numpy.testing import (assert_allclose, + assert_array_equal, + assert_equal) +import pytest +from scipy.spatial.distance import directed_hausdorff +from scipy.spatial import distance +from scipy._lib._util import check_random_state + + +class TestHausdorff: + # Test various properties of the directed Hausdorff code. + + def setup_method(self): + np.random.seed(1234) + random_angles = np.random.random(100) * np.pi * 2 + random_columns = np.column_stack( + (random_angles, random_angles, np.zeros(100))) + random_columns[..., 0] = np.cos(random_columns[..., 0]) + random_columns[..., 1] = np.sin(random_columns[..., 1]) + random_columns_2 = np.column_stack( + (random_angles, random_angles, np.zeros(100))) + random_columns_2[1:, 0] = np.cos(random_columns_2[1:, 0]) * 2.0 + random_columns_2[1:, 1] = np.sin(random_columns_2[1:, 1]) * 2.0 + # move one point farther out so we don't have two perfect circles + random_columns_2[0, 0] = np.cos(random_columns_2[0, 0]) * 3.3 + random_columns_2[0, 1] = np.sin(random_columns_2[0, 1]) * 3.3 + self.path_1 = random_columns + self.path_2 = random_columns_2 + self.path_1_4d = np.insert(self.path_1, 3, 5, axis=1) + self.path_2_4d = np.insert(self.path_2, 3, 27, axis=1) + + def test_symmetry(self): + # Ensure that the directed (asymmetric) Hausdorff distance is + # actually asymmetric + + forward = directed_hausdorff(self.path_1, self.path_2)[0] + reverse = directed_hausdorff(self.path_2, self.path_1)[0] + assert forward != reverse + + def test_brute_force_comparison_forward(self): + # Ensure that the algorithm for directed_hausdorff gives the + # same result as the simple / brute force approach in the + # forward direction. + actual = directed_hausdorff(self.path_1, self.path_2)[0] + # brute force over rows: + expected = max(np.amin(distance.cdist(self.path_1, self.path_2), + axis=1)) + assert_allclose(actual, expected) + + def test_brute_force_comparison_reverse(self): + # Ensure that the algorithm for directed_hausdorff gives the + # same result as the simple / brute force approach in the + # reverse direction. + actual = directed_hausdorff(self.path_2, self.path_1)[0] + # brute force over columns: + expected = max(np.amin(distance.cdist(self.path_1, self.path_2), + axis=0)) + assert_allclose(actual, expected) + + def test_degenerate_case(self): + # The directed Hausdorff distance must be zero if both input + # data arrays match. + actual = directed_hausdorff(self.path_1, self.path_1)[0] + assert_allclose(actual, 0.0) + + def test_2d_data_forward(self): + # Ensure that 2D data is handled properly for a simple case + # relative to brute force approach. + actual = directed_hausdorff(self.path_1[..., :2], + self.path_2[..., :2])[0] + expected = max(np.amin(distance.cdist(self.path_1[..., :2], + self.path_2[..., :2]), + axis=1)) + assert_allclose(actual, expected) + + def test_4d_data_reverse(self): + # Ensure that 4D data is handled properly for a simple case + # relative to brute force approach. + actual = directed_hausdorff(self.path_2_4d, self.path_1_4d)[0] + # brute force over columns: + expected = max(np.amin(distance.cdist(self.path_1_4d, self.path_2_4d), + axis=0)) + assert_allclose(actual, expected) + + def test_indices(self): + # Ensure that correct point indices are returned -- they should + # correspond to the Hausdorff pair + path_simple_1 = np.array([[-1,-12],[0,0], [1,1], [3,7], [1,2]]) + path_simple_2 = np.array([[0,0], [1,1], [4,100], [10,9]]) + actual = directed_hausdorff(path_simple_2, path_simple_1)[1:] + expected = (2, 3) + assert_array_equal(actual, expected) + + def test_random_state(self): + # ensure that the global random state is not modified because + # the directed Hausdorff algorithm uses randomization + rs = check_random_state(None) + old_global_state = rs.get_state() + directed_hausdorff(self.path_1, self.path_2) + rs2 = check_random_state(None) + new_global_state = rs2.get_state() + assert_equal(new_global_state, old_global_state) + + @pytest.mark.parametrize("seed", [None, 27870671]) + def test_random_state_None_int(self, seed): + # check that seed values of None or int do not alter global + # random state + rs = check_random_state(None) + old_global_state = rs.get_state() + directed_hausdorff(self.path_1, self.path_2, seed) + rs2 = check_random_state(None) + new_global_state = rs2.get_state() + assert_equal(new_global_state, old_global_state) + + def test_invalid_dimensions(self): + # Ensure that a ValueError is raised when the number of columns + # is not the same + rng = np.random.default_rng(189048172503940875434364128139223470523) + A = rng.random((3, 2)) + B = rng.random((3, 5)) + msg = r"need to have the same number of columns" + with pytest.raises(ValueError, match=msg): + directed_hausdorff(A, B) + + @pytest.mark.parametrize("A, B, seed, expected", [ + # the two cases from gh-11332 + ([(0,0)], + [(0,1), (0,0)], + 0, + (0.0, 0, 1)), + ([(0,0)], + [(0,1), (0,0)], + 1, + (0.0, 0, 1)), + # slightly more complex case + ([(-5, 3), (0,0)], + [(0,1), (0,0), (-5, 3)], + 77098, + # the maximum minimum distance will + # be the last one found, but a unique + # solution is not guaranteed more broadly + (0.0, 1, 1)), + ]) + def test_subsets(self, A, B, seed, expected): + # verify fix for gh-11332 + actual = directed_hausdorff(u=A, v=B, seed=seed) + # check distance + assert_allclose(actual[0], expected[0]) + # check indices + assert actual[1:] == expected[1:] + + +@pytest.mark.xslow +def test_massive_arr_overflow(): + # on 64-bit systems we should be able to + # handle arrays that exceed the indexing + # size of a 32-bit signed integer + try: + import psutil + except ModuleNotFoundError: + pytest.skip("psutil required to check available memory") + if psutil.virtual_memory().available < 80*2**30: + # Don't run the test if there is less than 80 gig of RAM available. + pytest.skip('insufficient memory available to run this test') + size = int(3e9) + arr1 = np.zeros(shape=(size, 2)) + arr2 = np.zeros(shape=(3, 2)) + arr1[size - 1] = [5, 5] + actual = directed_hausdorff(u=arr1, v=arr2) + assert_allclose(actual[0], 7.0710678118654755) + assert_allclose(actual[1], size - 1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_kdtree.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_kdtree.py new file mode 100644 index 0000000000000000000000000000000000000000..faf87872ea3b69d86d9d2fc90b546e409914ba53 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_kdtree.py @@ -0,0 +1,1533 @@ +# Copyright Anne M. Archibald 2008 +# Released under the scipy license + +import os +from numpy.testing import (assert_equal, assert_array_equal, assert_, + assert_almost_equal, assert_array_almost_equal, + assert_allclose) +from pytest import raises as assert_raises +import pytest +from platform import python_implementation +import numpy as np +from scipy.spatial import KDTree, Rectangle, distance_matrix, cKDTree +from scipy.spatial._ckdtree import cKDTreeNode +from scipy.spatial import minkowski_distance + +import itertools + +@pytest.fixture(params=[KDTree, cKDTree]) +def kdtree_type(request): + return request.param + + +def KDTreeTest(kls): + """Class decorator to create test cases for KDTree and cKDTree + + Tests use the class variable ``kdtree_type`` as the tree constructor. + """ + if not kls.__name__.startswith('_Test'): + raise RuntimeError("Expected a class name starting with _Test") + + for tree in (KDTree, cKDTree): + test_name = kls.__name__[1:] + '_' + tree.__name__ + + if test_name in globals(): + raise RuntimeError("Duplicated test name: " + test_name) + + # Create a new sub-class with kdtree_type defined + test_case = type(test_name, (kls,), {'kdtree_type': tree}) + globals()[test_name] = test_case + return kls + + +def distance_box(a, b, p, boxsize): + diff = a - b + diff[diff > 0.5 * boxsize] -= boxsize + diff[diff < -0.5 * boxsize] += boxsize + d = minkowski_distance(diff, 0, p) + return d + +class ConsistencyTests: + def distance(self, a, b, p): + return minkowski_distance(a, b, p) + + def test_nearest(self): + x = self.x + d, i = self.kdtree.query(x, 1) + assert_almost_equal(d**2, np.sum((x-self.data[i])**2)) + eps = 1e-8 + assert_(np.all(np.sum((self.data-x[np.newaxis, :])**2, axis=1) > d**2-eps)) + + def test_m_nearest(self): + x = self.x + m = self.m + dd, ii = self.kdtree.query(x, m) + d = np.amax(dd) + i = ii[np.argmax(dd)] + assert_almost_equal(d**2, np.sum((x-self.data[i])**2)) + eps = 1e-8 + assert_equal( + np.sum(np.sum((self.data-x[np.newaxis, :])**2, axis=1) < d**2+eps), + m, + ) + + def test_points_near(self): + x = self.x + d = self.d + dd, ii = self.kdtree.query(x, k=self.kdtree.n, distance_upper_bound=d) + eps = 1e-8 + hits = 0 + for near_d, near_i in zip(dd, ii): + if near_d == np.inf: + continue + hits += 1 + assert_almost_equal(near_d**2, np.sum((x-self.data[near_i])**2)) + assert_(near_d < d+eps, f"near_d={near_d:g} should be less than {d:g}") + assert_equal(np.sum(self.distance(self.data, x, 2) < d**2+eps), hits) + + def test_points_near_l1(self): + x = self.x + d = self.d + dd, ii = self.kdtree.query(x, k=self.kdtree.n, p=1, distance_upper_bound=d) + eps = 1e-8 + hits = 0 + for near_d, near_i in zip(dd, ii): + if near_d == np.inf: + continue + hits += 1 + assert_almost_equal(near_d, self.distance(x, self.data[near_i], 1)) + assert_(near_d < d+eps, f"near_d={near_d:g} should be less than {d:g}") + assert_equal(np.sum(self.distance(self.data, x, 1) < d+eps), hits) + + def test_points_near_linf(self): + x = self.x + d = self.d + dd, ii = self.kdtree.query(x, k=self.kdtree.n, p=np.inf, distance_upper_bound=d) + eps = 1e-8 + hits = 0 + for near_d, near_i in zip(dd, ii): + if near_d == np.inf: + continue + hits += 1 + assert_almost_equal(near_d, self.distance(x, self.data[near_i], np.inf)) + assert_(near_d < d+eps, f"near_d={near_d:g} should be less than {d:g}") + assert_equal(np.sum(self.distance(self.data, x, np.inf) < d+eps), hits) + + def test_approx(self): + x = self.x + k = self.k + eps = 0.1 + d_real, i_real = self.kdtree.query(x, k) + d, i = self.kdtree.query(x, k, eps=eps) + assert_(np.all(d <= d_real*(1+eps))) + + +@KDTreeTest +class _Test_random(ConsistencyTests): + def setup_method(self): + self.n = 100 + self.m = 4 + np.random.seed(1234) + self.data = np.random.randn(self.n, self.m) + self.kdtree = self.kdtree_type(self.data, leafsize=2) + self.x = np.random.randn(self.m) + self.d = 0.2 + self.k = 10 + + +@KDTreeTest +class _Test_random_far(_Test_random): + def setup_method(self): + super().setup_method() + self.x = np.random.randn(self.m)+10 + + +@KDTreeTest +class _Test_small(ConsistencyTests): + def setup_method(self): + self.data = np.array([[0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1]]) + self.kdtree = self.kdtree_type(self.data) + self.n = self.kdtree.n + self.m = self.kdtree.m + np.random.seed(1234) + self.x = np.random.randn(3) + self.d = 0.5 + self.k = 4 + + def test_nearest(self): + assert_array_equal( + self.kdtree.query((0, 0, 0.1), 1), + (0.1, 0)) + + def test_nearest_two(self): + assert_array_equal( + self.kdtree.query((0, 0, 0.1), 2), + ([0.1, 0.9], [0, 1])) + + +@KDTreeTest +class _Test_small_nonleaf(_Test_small): + def setup_method(self): + super().setup_method() + self.kdtree = self.kdtree_type(self.data, leafsize=1) + + +class Test_vectorization_KDTree: + def setup_method(self): + self.data = np.array([[0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1]]) + self.kdtree = KDTree(self.data) + + def test_single_query(self): + d, i = self.kdtree.query(np.array([0, 0, 0])) + assert_(isinstance(d, float)) + assert_(np.issubdtype(i, np.signedinteger)) + + def test_vectorized_query(self): + d, i = self.kdtree.query(np.zeros((2, 4, 3))) + assert_equal(np.shape(d), (2, 4)) + assert_equal(np.shape(i), (2, 4)) + + def test_single_query_multiple_neighbors(self): + s = 23 + kk = self.kdtree.n+s + d, i = self.kdtree.query(np.array([0, 0, 0]), k=kk) + assert_equal(np.shape(d), (kk,)) + assert_equal(np.shape(i), (kk,)) + assert_(np.all(~np.isfinite(d[-s:]))) + assert_(np.all(i[-s:] == self.kdtree.n)) + + def test_vectorized_query_multiple_neighbors(self): + s = 23 + kk = self.kdtree.n+s + d, i = self.kdtree.query(np.zeros((2, 4, 3)), k=kk) + assert_equal(np.shape(d), (2, 4, kk)) + assert_equal(np.shape(i), (2, 4, kk)) + assert_(np.all(~np.isfinite(d[:, :, -s:]))) + assert_(np.all(i[:, :, -s:] == self.kdtree.n)) + + def test_query_raises_for_k_none(self): + x = 1.0 + with pytest.raises(ValueError, match="k must be an integer or*"): + self.kdtree.query(x, k=None) + +class Test_vectorization_cKDTree: + def setup_method(self): + self.data = np.array([[0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1]]) + self.kdtree = cKDTree(self.data) + + def test_single_query(self): + d, i = self.kdtree.query([0, 0, 0]) + assert_(isinstance(d, float)) + assert_(isinstance(i, int)) + + def test_vectorized_query(self): + d, i = self.kdtree.query(np.zeros((2, 4, 3))) + assert_equal(np.shape(d), (2, 4)) + assert_equal(np.shape(i), (2, 4)) + + def test_vectorized_query_noncontiguous_values(self): + np.random.seed(1234) + qs = np.random.randn(3, 1000).T + ds, i_s = self.kdtree.query(qs) + for q, d, i in zip(qs, ds, i_s): + assert_equal(self.kdtree.query(q), (d, i)) + + def test_single_query_multiple_neighbors(self): + s = 23 + kk = self.kdtree.n+s + d, i = self.kdtree.query([0, 0, 0], k=kk) + assert_equal(np.shape(d), (kk,)) + assert_equal(np.shape(i), (kk,)) + assert_(np.all(~np.isfinite(d[-s:]))) + assert_(np.all(i[-s:] == self.kdtree.n)) + + def test_vectorized_query_multiple_neighbors(self): + s = 23 + kk = self.kdtree.n+s + d, i = self.kdtree.query(np.zeros((2, 4, 3)), k=kk) + assert_equal(np.shape(d), (2, 4, kk)) + assert_equal(np.shape(i), (2, 4, kk)) + assert_(np.all(~np.isfinite(d[:, :, -s:]))) + assert_(np.all(i[:, :, -s:] == self.kdtree.n)) + +class ball_consistency: + tol = 0.0 + + def distance(self, a, b, p): + return minkowski_distance(a * 1.0, b * 1.0, p) + + def test_in_ball(self): + x = np.atleast_2d(self.x) + d = np.broadcast_to(self.d, x.shape[:-1]) + l = self.T.query_ball_point(x, self.d, p=self.p, eps=self.eps) + for i, ind in enumerate(l): + dist = self.distance(self.data[ind], x[i], self.p) - d[i]*(1.+self.eps) + norm = self.distance(self.data[ind], x[i], self.p) + d[i]*(1.+self.eps) + assert_array_equal(dist < self.tol * norm, True) + + def test_found_all(self): + x = np.atleast_2d(self.x) + d = np.broadcast_to(self.d, x.shape[:-1]) + l = self.T.query_ball_point(x, self.d, p=self.p, eps=self.eps) + for i, ind in enumerate(l): + c = np.ones(self.T.n, dtype=bool) + c[ind] = False + dist = self.distance(self.data[c], x[i], self.p) - d[i]/(1.+self.eps) + norm = self.distance(self.data[c], x[i], self.p) + d[i]/(1.+self.eps) + assert_array_equal(dist > -self.tol * norm, True) + +@KDTreeTest +class _Test_random_ball(ball_consistency): + def setup_method(self): + n = 100 + m = 4 + np.random.seed(1234) + self.data = np.random.randn(n, m) + self.T = self.kdtree_type(self.data, leafsize=2) + self.x = np.random.randn(m) + self.p = 2. + self.eps = 0 + self.d = 0.2 + + +@KDTreeTest +class _Test_random_ball_periodic(ball_consistency): + def distance(self, a, b, p): + return distance_box(a, b, p, 1.0) + + def setup_method(self): + n = 10000 + m = 4 + np.random.seed(1234) + self.data = np.random.uniform(size=(n, m)) + self.T = self.kdtree_type(self.data, leafsize=2, boxsize=1) + self.x = np.full(m, 0.1) + self.p = 2. + self.eps = 0 + self.d = 0.2 + + def test_in_ball_outside(self): + l = self.T.query_ball_point(self.x + 1.0, self.d, p=self.p, eps=self.eps) + for i in l: + assert_(self.distance(self.data[i], self.x, self.p) <= self.d*(1.+self.eps)) + l = self.T.query_ball_point(self.x - 1.0, self.d, p=self.p, eps=self.eps) + for i in l: + assert_(self.distance(self.data[i], self.x, self.p) <= self.d*(1.+self.eps)) + + def test_found_all_outside(self): + c = np.ones(self.T.n, dtype=bool) + l = self.T.query_ball_point(self.x + 1.0, self.d, p=self.p, eps=self.eps) + c[l] = False + assert np.all( + self.distance(self.data[c], self.x, self.p) >= self.d/(1.+self.eps) + ) + + l = self.T.query_ball_point(self.x - 1.0, self.d, p=self.p, eps=self.eps) + c[l] = False + assert np.all( + self.distance(self.data[c], self.x, self.p) >= self.d/(1.+self.eps) + ) + + +@KDTreeTest +class _Test_random_ball_largep_issue9890(ball_consistency): + + # allow some roundoff errors due to numerical issues + tol = 1e-13 + + def setup_method(self): + n = 1000 + m = 2 + np.random.seed(123) + self.data = np.random.randint(100, 1000, size=(n, m)) + self.T = self.kdtree_type(self.data) + self.x = self.data + self.p = 100 + self.eps = 0 + self.d = 10 + + +@KDTreeTest +class _Test_random_ball_approx(_Test_random_ball): + + def setup_method(self): + super().setup_method() + self.eps = 0.1 + + +@KDTreeTest +class _Test_random_ball_approx_periodic(_Test_random_ball): + + def setup_method(self): + super().setup_method() + self.eps = 0.1 + + +@KDTreeTest +class _Test_random_ball_far(_Test_random_ball): + + def setup_method(self): + super().setup_method() + self.d = 2. + +@KDTreeTest +class _Test_random_ball_far_periodic(_Test_random_ball_periodic): + + def setup_method(self): + super().setup_method() + self.d = 2. + + +@KDTreeTest +class _Test_random_ball_l1(_Test_random_ball): + + def setup_method(self): + super().setup_method() + self.p = 1 + + +@KDTreeTest +class _Test_random_ball_linf(_Test_random_ball): + + def setup_method(self): + super().setup_method() + self.p = np.inf + + +def test_random_ball_vectorized(kdtree_type): + n = 20 + m = 5 + np.random.seed(1234) + T = kdtree_type(np.random.randn(n, m)) + + r = T.query_ball_point(np.random.randn(2, 3, m), 1) + assert_equal(r.shape, (2, 3)) + assert_(isinstance(r[0, 0], list)) + + +def test_query_ball_point_multithreading(kdtree_type): + np.random.seed(0) + n = 5000 + k = 2 + points = np.random.randn(n, k) + T = kdtree_type(points) + l1 = T.query_ball_point(points, 0.003, workers=1) + l2 = T.query_ball_point(points, 0.003, workers=64) + l3 = T.query_ball_point(points, 0.003, workers=-1) + + for i in range(n): + if l1[i] or l2[i]: + assert_array_equal(l1[i], l2[i]) + + for i in range(n): + if l1[i] or l3[i]: + assert_array_equal(l1[i], l3[i]) + + +class two_trees_consistency: + + def distance(self, a, b, p): + return minkowski_distance(a, b, p) + + def test_all_in_ball(self): + r = self.T1.query_ball_tree(self.T2, self.d, p=self.p, eps=self.eps) + for i, l in enumerate(r): + for j in l: + assert (self.distance(self.data1[i], self.data2[j], self.p) + <= self.d*(1.+self.eps)) + + def test_found_all(self): + r = self.T1.query_ball_tree(self.T2, self.d, p=self.p, eps=self.eps) + for i, l in enumerate(r): + c = np.ones(self.T2.n, dtype=bool) + c[l] = False + assert np.all(self.distance(self.data2[c], self.data1[i], self.p) + >= self.d/(1.+self.eps)) + + +@KDTreeTest +class _Test_two_random_trees(two_trees_consistency): + + def setup_method(self): + n = 50 + m = 4 + np.random.seed(1234) + self.data1 = np.random.randn(n, m) + self.T1 = self.kdtree_type(self.data1, leafsize=2) + self.data2 = np.random.randn(n, m) + self.T2 = self.kdtree_type(self.data2, leafsize=2) + self.p = 2. + self.eps = 0 + self.d = 0.2 + + +@KDTreeTest +class _Test_two_random_trees_periodic(two_trees_consistency): + def distance(self, a, b, p): + return distance_box(a, b, p, 1.0) + + def setup_method(self): + n = 50 + m = 4 + np.random.seed(1234) + self.data1 = np.random.uniform(size=(n, m)) + self.T1 = self.kdtree_type(self.data1, leafsize=2, boxsize=1.0) + self.data2 = np.random.uniform(size=(n, m)) + self.T2 = self.kdtree_type(self.data2, leafsize=2, boxsize=1.0) + self.p = 2. + self.eps = 0 + self.d = 0.2 + + +@KDTreeTest +class _Test_two_random_trees_far(_Test_two_random_trees): + + def setup_method(self): + super().setup_method() + self.d = 2 + + +@KDTreeTest +class _Test_two_random_trees_far_periodic(_Test_two_random_trees_periodic): + + def setup_method(self): + super().setup_method() + self.d = 2 + + +@KDTreeTest +class _Test_two_random_trees_linf(_Test_two_random_trees): + + def setup_method(self): + super().setup_method() + self.p = np.inf + + +@KDTreeTest +class _Test_two_random_trees_linf_periodic(_Test_two_random_trees_periodic): + + def setup_method(self): + super().setup_method() + self.p = np.inf + + +class Test_rectangle: + + def setup_method(self): + self.rect = Rectangle([0, 0], [1, 1]) + + def test_min_inside(self): + assert_almost_equal(self.rect.min_distance_point([0.5, 0.5]), 0) + + def test_min_one_side(self): + assert_almost_equal(self.rect.min_distance_point([0.5, 1.5]), 0.5) + + def test_min_two_sides(self): + assert_almost_equal(self.rect.min_distance_point([2, 2]), np.sqrt(2)) + + def test_max_inside(self): + assert_almost_equal(self.rect.max_distance_point([0.5, 0.5]), 1/np.sqrt(2)) + + def test_max_one_side(self): + assert_almost_equal(self.rect.max_distance_point([0.5, 1.5]), + np.hypot(0.5, 1.5)) + + def test_max_two_sides(self): + assert_almost_equal(self.rect.max_distance_point([2, 2]), 2*np.sqrt(2)) + + def test_split(self): + less, greater = self.rect.split(0, 0.1) + assert_array_equal(less.maxes, [0.1, 1]) + assert_array_equal(less.mins, [0, 0]) + assert_array_equal(greater.maxes, [1, 1]) + assert_array_equal(greater.mins, [0.1, 0]) + + +def test_distance_l2(): + assert_almost_equal(minkowski_distance([0, 0], [1, 1], 2), np.sqrt(2)) + + +def test_distance_l1(): + assert_almost_equal(minkowski_distance([0, 0], [1, 1], 1), 2) + + +def test_distance_linf(): + assert_almost_equal(minkowski_distance([0, 0], [1, 1], np.inf), 1) + + +def test_distance_vectorization(): + np.random.seed(1234) + x = np.random.randn(10, 1, 3) + y = np.random.randn(1, 7, 3) + assert_equal(minkowski_distance(x, y).shape, (10, 7)) + + +class count_neighbors_consistency: + def test_one_radius(self): + r = 0.2 + assert_equal(self.T1.count_neighbors(self.T2, r), + np.sum([len(l) for l in self.T1.query_ball_tree(self.T2, r)])) + + def test_large_radius(self): + r = 1000 + assert_equal(self.T1.count_neighbors(self.T2, r), + np.sum([len(l) for l in self.T1.query_ball_tree(self.T2, r)])) + + def test_multiple_radius(self): + rs = np.exp(np.linspace(np.log(0.01), np.log(10), 3)) + results = self.T1.count_neighbors(self.T2, rs) + assert_(np.all(np.diff(results) >= 0)) + for r, result in zip(rs, results): + assert_equal(self.T1.count_neighbors(self.T2, r), result) + +@KDTreeTest +class _Test_count_neighbors(count_neighbors_consistency): + def setup_method(self): + n = 50 + m = 2 + np.random.seed(1234) + self.T1 = self.kdtree_type(np.random.randn(n, m), leafsize=2) + self.T2 = self.kdtree_type(np.random.randn(n, m), leafsize=2) + + +class sparse_distance_matrix_consistency: + + def distance(self, a, b, p): + return minkowski_distance(a, b, p) + + def test_consistency_with_neighbors(self): + M = self.T1.sparse_distance_matrix(self.T2, self.r) + r = self.T1.query_ball_tree(self.T2, self.r) + for i, l in enumerate(r): + for j in l: + assert_almost_equal( + M[i, j], + self.distance(self.T1.data[i], self.T2.data[j], self.p), + decimal=14 + ) + for ((i, j), d) in M.items(): + assert_(j in r[i]) + + def test_zero_distance(self): + # raises an exception for bug 870 (FIXME: Does it?) + self.T1.sparse_distance_matrix(self.T1, self.r) + + def test_consistency(self): + # Test consistency with a distance_matrix + M1 = self.T1.sparse_distance_matrix(self.T2, self.r) + expected = distance_matrix(self.T1.data, self.T2.data) + expected[expected > self.r] = 0 + assert_array_almost_equal(M1.toarray(), expected, decimal=14) + + def test_against_logic_error_regression(self): + # regression test for gh-5077 logic error + np.random.seed(0) + too_many = np.array(np.random.randn(18, 2), dtype=int) + tree = self.kdtree_type( + too_many, balanced_tree=False, compact_nodes=False) + d = tree.sparse_distance_matrix(tree, 3).toarray() + assert_array_almost_equal(d, d.T, decimal=14) + + def test_ckdtree_return_types(self): + # brute-force reference + ref = np.zeros((self.n, self.n)) + for i in range(self.n): + for j in range(self.n): + v = self.data1[i, :] - self.data2[j, :] + ref[i, j] = np.dot(v, v) + ref = np.sqrt(ref) + ref[ref > self.r] = 0. + # test return type 'dict' + dist = np.zeros((self.n, self.n)) + r = self.T1.sparse_distance_matrix(self.T2, self.r, output_type='dict') + for i, j in r.keys(): + dist[i, j] = r[(i, j)] + assert_array_almost_equal(ref, dist, decimal=14) + # test return type 'ndarray' + dist = np.zeros((self.n, self.n)) + r = self.T1.sparse_distance_matrix(self.T2, self.r, + output_type='ndarray') + for k in range(r.shape[0]): + i = r['i'][k] + j = r['j'][k] + v = r['v'][k] + dist[i, j] = v + assert_array_almost_equal(ref, dist, decimal=14) + # test return type 'dok_matrix' + r = self.T1.sparse_distance_matrix(self.T2, self.r, + output_type='dok_matrix') + assert_array_almost_equal(ref, r.toarray(), decimal=14) + # test return type 'coo_matrix' + r = self.T1.sparse_distance_matrix(self.T2, self.r, + output_type='coo_matrix') + assert_array_almost_equal(ref, r.toarray(), decimal=14) + + +@KDTreeTest +class _Test_sparse_distance_matrix(sparse_distance_matrix_consistency): + def setup_method(self): + n = 50 + m = 4 + np.random.seed(1234) + data1 = np.random.randn(n, m) + data2 = np.random.randn(n, m) + self.T1 = self.kdtree_type(data1, leafsize=2) + self.T2 = self.kdtree_type(data2, leafsize=2) + self.r = 0.5 + self.p = 2 + self.data1 = data1 + self.data2 = data2 + self.n = n + self.m = m + + +def test_distance_matrix(): + m = 10 + n = 11 + k = 4 + np.random.seed(1234) + xs = np.random.randn(m, k) + ys = np.random.randn(n, k) + ds = distance_matrix(xs, ys) + assert_equal(ds.shape, (m, n)) + for i in range(m): + for j in range(n): + assert_almost_equal(minkowski_distance(xs[i], ys[j]), ds[i, j]) + + +def test_distance_matrix_looping(): + m = 10 + n = 11 + k = 4 + np.random.seed(1234) + xs = np.random.randn(m, k) + ys = np.random.randn(n, k) + ds = distance_matrix(xs, ys) + dsl = distance_matrix(xs, ys, threshold=1) + assert_equal(ds, dsl) + + +def check_onetree_query(T, d): + r = T.query_ball_tree(T, d) + s = set() + for i, l in enumerate(r): + for j in l: + if i < j: + s.add((i, j)) + + assert_(s == T.query_pairs(d)) + +def test_onetree_query(kdtree_type): + np.random.seed(0) + n = 50 + k = 4 + points = np.random.randn(n, k) + T = kdtree_type(points) + check_onetree_query(T, 0.1) + + points = np.random.randn(3*n, k) + points[:n] *= 0.001 + points[n:2*n] += 2 + T = kdtree_type(points) + check_onetree_query(T, 0.1) + check_onetree_query(T, 0.001) + check_onetree_query(T, 0.00001) + check_onetree_query(T, 1e-6) + + +def test_query_pairs_single_node(kdtree_type): + tree = kdtree_type([[0, 1]]) + assert_equal(tree.query_pairs(0.5), set()) + + +def test_kdtree_query_pairs(kdtree_type): + np.random.seed(0) + n = 50 + k = 2 + r = 0.1 + r2 = r**2 + points = np.random.randn(n, k) + T = kdtree_type(points) + # brute force reference + brute = set() + for i in range(n): + for j in range(i+1, n): + v = points[i, :] - points[j, :] + if np.dot(v, v) <= r2: + brute.add((i, j)) + l0 = sorted(brute) + # test default return type + s = T.query_pairs(r) + l1 = sorted(s) + assert_array_equal(l0, l1) + # test return type 'set' + s = T.query_pairs(r, output_type='set') + l1 = sorted(s) + assert_array_equal(l0, l1) + # test return type 'ndarray' + s = set() + arr = T.query_pairs(r, output_type='ndarray') + for i in range(arr.shape[0]): + s.add((int(arr[i, 0]), int(arr[i, 1]))) + l2 = sorted(s) + assert_array_equal(l0, l2) + + +def test_query_pairs_eps(kdtree_type): + spacing = np.sqrt(2) + # irrational spacing to have potential rounding errors + x_range = np.linspace(0, 3 * spacing, 4) + y_range = np.linspace(0, 3 * spacing, 4) + xy_array = [(xi, yi) for xi in x_range for yi in y_range] + tree = kdtree_type(xy_array) + pairs_eps = tree.query_pairs(r=spacing, eps=.1) + # result: 24 with eps, 16 without due to rounding + pairs = tree.query_pairs(r=spacing * 1.01) + # result: 24 + assert_equal(pairs, pairs_eps) + + +def test_ball_point_ints(kdtree_type): + # Regression test for #1373. + x, y = np.mgrid[0:4, 0:4] + points = list(zip(x.ravel(), y.ravel())) + tree = kdtree_type(points) + assert_equal(sorted([4, 8, 9, 12]), + sorted(tree.query_ball_point((2, 0), 1))) + points = np.asarray(points, dtype=float) + tree = kdtree_type(points) + assert_equal(sorted([4, 8, 9, 12]), + sorted(tree.query_ball_point((2, 0), 1))) + + +def test_kdtree_comparisons(): + # Regression test: node comparisons were done wrong in 0.12 w/Py3. + nodes = [KDTree.node() for _ in range(3)] + assert_equal(sorted(nodes), sorted(nodes[::-1])) + + +def test_kdtree_build_modes(kdtree_type): + # check if different build modes for KDTree give similar query results + np.random.seed(0) + n = 5000 + k = 4 + points = np.random.randn(n, k) + T1 = kdtree_type(points).query(points, k=5)[-1] + T2 = kdtree_type(points, compact_nodes=False).query(points, k=5)[-1] + T3 = kdtree_type(points, balanced_tree=False).query(points, k=5)[-1] + T4 = kdtree_type(points, compact_nodes=False, + balanced_tree=False).query(points, k=5)[-1] + assert_array_equal(T1, T2) + assert_array_equal(T1, T3) + assert_array_equal(T1, T4) + +def test_kdtree_pickle(kdtree_type): + # test if it is possible to pickle a KDTree + import pickle + np.random.seed(0) + n = 50 + k = 4 + points = np.random.randn(n, k) + T1 = kdtree_type(points) + tmp = pickle.dumps(T1) + T2 = pickle.loads(tmp) + T1 = T1.query(points, k=5)[-1] + T2 = T2.query(points, k=5)[-1] + assert_array_equal(T1, T2) + +def test_kdtree_pickle_boxsize(kdtree_type): + # test if it is possible to pickle a periodic KDTree + import pickle + np.random.seed(0) + n = 50 + k = 4 + points = np.random.uniform(size=(n, k)) + T1 = kdtree_type(points, boxsize=1.0) + tmp = pickle.dumps(T1) + T2 = pickle.loads(tmp) + T1 = T1.query(points, k=5)[-1] + T2 = T2.query(points, k=5)[-1] + assert_array_equal(T1, T2) + +def test_kdtree_copy_data(kdtree_type): + # check if copy_data=True makes the kd-tree + # impervious to data corruption by modification of + # the data arrray + np.random.seed(0) + n = 5000 + k = 4 + points = np.random.randn(n, k) + T = kdtree_type(points, copy_data=True) + q = points.copy() + T1 = T.query(q, k=5)[-1] + points[...] = np.random.randn(n, k) + T2 = T.query(q, k=5)[-1] + assert_array_equal(T1, T2) + +def test_ckdtree_parallel(kdtree_type, monkeypatch): + # check if parallel=True also generates correct query results + np.random.seed(0) + n = 5000 + k = 4 + points = np.random.randn(n, k) + T = kdtree_type(points) + T1 = T.query(points, k=5, workers=64)[-1] + T2 = T.query(points, k=5, workers=-1)[-1] + T3 = T.query(points, k=5)[-1] + assert_array_equal(T1, T2) + assert_array_equal(T1, T3) + + monkeypatch.setattr(os, 'cpu_count', lambda: None) + with pytest.raises(NotImplementedError, match="Cannot determine the"): + T.query(points, 1, workers=-1) + + +def test_ckdtree_view(): + # Check that the nodes can be correctly viewed from Python. + # This test also sanity checks each node in the cKDTree, and + # thus verifies the internal structure of the kd-tree. + np.random.seed(0) + n = 100 + k = 4 + points = np.random.randn(n, k) + kdtree = cKDTree(points) + + # walk the whole kd-tree and sanity check each node + def recurse_tree(n): + assert_(isinstance(n, cKDTreeNode)) + if n.split_dim == -1: + assert_(n.lesser is None) + assert_(n.greater is None) + assert_(n.indices.shape[0] <= kdtree.leafsize) + else: + recurse_tree(n.lesser) + recurse_tree(n.greater) + x = n.lesser.data_points[:, n.split_dim] + y = n.greater.data_points[:, n.split_dim] + assert_(x.max() < y.min()) + + recurse_tree(kdtree.tree) + # check that indices are correctly retrieved + n = kdtree.tree + assert_array_equal(np.sort(n.indices), range(100)) + # check that data_points are correctly retrieved + assert_array_equal(kdtree.data[n.indices, :], n.data_points) + +# KDTree is specialized to type double points, so no need to make +# a unit test corresponding to test_ball_point_ints() + +def test_kdtree_list_k(kdtree_type): + # check kdtree periodic boundary + n = 200 + m = 2 + klist = [1, 2, 3] + kint = 3 + + np.random.seed(1234) + data = np.random.uniform(size=(n, m)) + kdtree = kdtree_type(data, leafsize=1) + + # check agreement between arange(1, k+1) and k + dd, ii = kdtree.query(data, klist) + dd1, ii1 = kdtree.query(data, kint) + assert_equal(dd, dd1) + assert_equal(ii, ii1) + + # now check skipping one element + klist = np.array([1, 3]) + kint = 3 + dd, ii = kdtree.query(data, kint) + dd1, ii1 = kdtree.query(data, klist) + assert_equal(dd1, dd[..., klist - 1]) + assert_equal(ii1, ii[..., klist - 1]) + + # check k == 1 special case + # and k == [1] non-special case + dd, ii = kdtree.query(data, 1) + dd1, ii1 = kdtree.query(data, [1]) + assert_equal(len(dd.shape), 1) + assert_equal(len(dd1.shape), 2) + assert_equal(dd, np.ravel(dd1)) + assert_equal(ii, np.ravel(ii1)) + +def test_kdtree_box(kdtree_type): + # check ckdtree periodic boundary + n = 2000 + m = 3 + k = 3 + np.random.seed(1234) + data = np.random.uniform(size=(n, m)) + kdtree = kdtree_type(data, leafsize=1, boxsize=1.0) + + # use the standard python KDTree for the simulated periodic box + kdtree2 = kdtree_type(data, leafsize=1) + + for p in [1, 2, 3.0, np.inf]: + dd, ii = kdtree.query(data, k, p=p) + + dd1, ii1 = kdtree.query(data + 1.0, k, p=p) + assert_almost_equal(dd, dd1) + assert_equal(ii, ii1) + + dd1, ii1 = kdtree.query(data - 1.0, k, p=p) + assert_almost_equal(dd, dd1) + assert_equal(ii, ii1) + + dd2, ii2 = simulate_periodic_box(kdtree2, data, k, boxsize=1.0, p=p) + assert_almost_equal(dd, dd2) + assert_equal(ii, ii2) + +def test_kdtree_box_0boxsize(kdtree_type): + # check ckdtree periodic boundary that mimics non-periodic + n = 2000 + m = 2 + k = 3 + np.random.seed(1234) + data = np.random.uniform(size=(n, m)) + kdtree = kdtree_type(data, leafsize=1, boxsize=0.0) + + # use the standard python KDTree for the simulated periodic box + kdtree2 = kdtree_type(data, leafsize=1) + + for p in [1, 2, np.inf]: + dd, ii = kdtree.query(data, k, p=p) + + dd1, ii1 = kdtree2.query(data, k, p=p) + assert_almost_equal(dd, dd1) + assert_equal(ii, ii1) + +def test_kdtree_box_upper_bounds(kdtree_type): + data = np.linspace(0, 2, 10).reshape(-1, 2) + data[:, 1] += 10 + with pytest.raises(ValueError): + kdtree_type(data, leafsize=1, boxsize=1.0) + with pytest.raises(ValueError): + kdtree_type(data, leafsize=1, boxsize=(0.0, 2.0)) + # skip a dimension. + kdtree_type(data, leafsize=1, boxsize=(2.0, 0.0)) + +def test_kdtree_box_lower_bounds(kdtree_type): + data = np.linspace(-1, 1, 10) + assert_raises(ValueError, kdtree_type, data, leafsize=1, boxsize=1.0) + +def simulate_periodic_box(kdtree, data, k, boxsize, p): + dd = [] + ii = [] + x = np.arange(3 ** data.shape[1]) + nn = np.array(np.unravel_index(x, [3] * data.shape[1])).T + nn = nn - 1.0 + for n in nn: + image = data + n * 1.0 * boxsize + dd2, ii2 = kdtree.query(image, k, p=p) + dd2 = dd2.reshape(-1, k) + ii2 = ii2.reshape(-1, k) + dd.append(dd2) + ii.append(ii2) + dd = np.concatenate(dd, axis=-1) + ii = np.concatenate(ii, axis=-1) + + result = np.empty([len(data), len(nn) * k], dtype=[ + ('ii', 'i8'), + ('dd', 'f8')]) + result['ii'][:] = ii + result['dd'][:] = dd + result.sort(order='dd') + return result['dd'][:, :k], result['ii'][:, :k] + + +@pytest.mark.skipif(python_implementation() == 'PyPy', + reason="Fails on PyPy CI runs. See #9507") +def test_ckdtree_memuse(): + # unit test adaptation of gh-5630 + + # NOTE: this will fail when run via valgrind, + # because rss is no longer a reliable memory usage indicator. + + try: + import resource + except ImportError: + # resource is not available on Windows + return + # Make some data + dx, dy = 0.05, 0.05 + y, x = np.mgrid[slice(1, 5 + dy, dy), + slice(1, 5 + dx, dx)] + z = np.sin(x)**10 + np.cos(10 + y*x) * np.cos(x) + z_copy = np.empty_like(z) + z_copy[:] = z + # Place FILLVAL in z_copy at random number of random locations + FILLVAL = 99. + mask = np.random.randint(0, z.size, np.random.randint(50) + 5) + z_copy.flat[mask] = FILLVAL + igood = np.vstack(np.nonzero(x != FILLVAL)).T + ibad = np.vstack(np.nonzero(x == FILLVAL)).T + mem_use = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + # burn-in + for i in range(10): + tree = cKDTree(igood) + # count memleaks while constructing and querying cKDTree + num_leaks = 0 + for i in range(100): + mem_use = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + tree = cKDTree(igood) + dist, iquery = tree.query(ibad, k=4, p=2) + new_mem_use = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + if new_mem_use > mem_use: + num_leaks += 1 + # ideally zero leaks, but errors might accidentally happen + # outside cKDTree + assert_(num_leaks < 10) + +def test_kdtree_weights(kdtree_type): + + data = np.linspace(0, 1, 4).reshape(-1, 1) + tree1 = kdtree_type(data, leafsize=1) + weights = np.ones(len(data), dtype='f4') + + nw = tree1._build_weights(weights) + assert_array_equal(nw, [4, 2, 1, 1, 2, 1, 1]) + + assert_raises(ValueError, tree1._build_weights, weights[:-1]) + + for i in range(10): + # since weights are uniform, these shall agree: + c1 = tree1.count_neighbors(tree1, np.linspace(0, 10, i)) + c2 = tree1.count_neighbors(tree1, np.linspace(0, 10, i), + weights=(weights, weights)) + c3 = tree1.count_neighbors(tree1, np.linspace(0, 10, i), + weights=(weights, None)) + c4 = tree1.count_neighbors(tree1, np.linspace(0, 10, i), + weights=(None, weights)) + tree1.count_neighbors(tree1, np.linspace(0, 10, i), + weights=weights) + + assert_array_equal(c1, c2) + assert_array_equal(c1, c3) + assert_array_equal(c1, c4) + + for i in range(len(data)): + # this tests removal of one data point by setting weight to 0 + w1 = weights.copy() + w1[i] = 0 + data2 = data[w1 != 0] + tree2 = kdtree_type(data2) + + c1 = tree1.count_neighbors(tree1, np.linspace(0, 10, 100), + weights=(w1, w1)) + # "c2 is correct" + c2 = tree2.count_neighbors(tree2, np.linspace(0, 10, 100)) + + assert_array_equal(c1, c2) + + #this asserts for two different trees, singular weights + # crashes + assert_raises(ValueError, tree1.count_neighbors, + tree2, np.linspace(0, 10, 100), weights=w1) + +def test_kdtree_count_neighbous_multiple_r(kdtree_type): + n = 2000 + m = 2 + np.random.seed(1234) + data = np.random.normal(size=(n, m)) + kdtree = kdtree_type(data, leafsize=1) + r0 = [0, 0.01, 0.01, 0.02, 0.05] + i0 = np.arange(len(r0)) + n0 = kdtree.count_neighbors(kdtree, r0) + nnc = kdtree.count_neighbors(kdtree, r0, cumulative=False) + assert_equal(n0, nnc.cumsum()) + + for i, r in zip(itertools.permutations(i0), + itertools.permutations(r0)): + # permute n0 by i and it shall agree + n = kdtree.count_neighbors(kdtree, r) + assert_array_equal(n, n0[list(i)]) + +def test_len0_arrays(kdtree_type): + # make sure len-0 arrays are handled correctly + # in range queries (gh-5639) + np.random.seed(1234) + X = np.random.rand(10, 2) + Y = np.random.rand(10, 2) + tree = kdtree_type(X) + # query_ball_point (single) + d, i = tree.query([.5, .5], k=1) + z = tree.query_ball_point([.5, .5], 0.1*d) + assert_array_equal(z, []) + # query_ball_point (multiple) + d, i = tree.query(Y, k=1) + mind = d.min() + z = tree.query_ball_point(Y, 0.1*mind) + y = np.empty(shape=(10, ), dtype=object) + y.fill([]) + assert_array_equal(y, z) + # query_ball_tree + other = kdtree_type(Y) + y = tree.query_ball_tree(other, 0.1*mind) + assert_array_equal(10*[[]], y) + # count_neighbors + y = tree.count_neighbors(other, 0.1*mind) + assert_(y == 0) + # sparse_distance_matrix + y = tree.sparse_distance_matrix(other, 0.1*mind, output_type='dok_matrix') + assert_array_equal(y == np.zeros((10, 10)), True) + y = tree.sparse_distance_matrix(other, 0.1*mind, output_type='coo_matrix') + assert_array_equal(y == np.zeros((10, 10)), True) + y = tree.sparse_distance_matrix(other, 0.1*mind, output_type='dict') + assert_equal(y, {}) + y = tree.sparse_distance_matrix(other, 0.1*mind, output_type='ndarray') + _dtype = [('i', np.intp), ('j', np.intp), ('v', np.float64)] + res_dtype = np.dtype(_dtype, align=True) + z = np.empty(shape=(0, ), dtype=res_dtype) + assert_array_equal(y, z) + # query_pairs + d, i = tree.query(X, k=2) + mind = d[:, -1].min() + y = tree.query_pairs(0.1*mind, output_type='set') + assert_equal(y, set()) + y = tree.query_pairs(0.1*mind, output_type='ndarray') + z = np.empty(shape=(0, 2), dtype=np.intp) + assert_array_equal(y, z) + +def test_kdtree_duplicated_inputs(kdtree_type): + # check kdtree with duplicated inputs + n = 1024 + for m in range(1, 8): + data = np.ones((n, m)) + data[n//2:] = 2 + + for balanced, compact in itertools.product((False, True), repeat=2): + kdtree = kdtree_type(data, balanced_tree=balanced, + compact_nodes=compact, leafsize=1) + assert kdtree.size == 3 + + tree = (kdtree.tree if kdtree_type is cKDTree else + kdtree.tree._node) + + assert_equal( + np.sort(tree.lesser.indices), + np.arange(0, n // 2)) + assert_equal( + np.sort(tree.greater.indices), + np.arange(n // 2, n)) + + +def test_kdtree_noncumulative_nondecreasing(kdtree_type): + # check kdtree with duplicated inputs + + # it shall not divide more than 3 nodes. + # root left (1), and right (2) + kdtree = kdtree_type([[0]], leafsize=1) + + assert_raises(ValueError, kdtree.count_neighbors, + kdtree, [0.1, 0], cumulative=False) + +def test_short_knn(kdtree_type): + + # The test case is based on github: #6425 by @SteveDoyle2 + + xyz = np.array([ + [0., 0., 0.], + [1.01, 0., 0.], + [0., 1., 0.], + [0., 1.01, 0.], + [1., 0., 0.], + [1., 1., 0.]], + dtype='float64') + + ckdt = kdtree_type(xyz) + + deq, ieq = ckdt.query(xyz, k=4, distance_upper_bound=0.2) + + assert_array_almost_equal(deq, + [[0., np.inf, np.inf, np.inf], + [0., 0.01, np.inf, np.inf], + [0., 0.01, np.inf, np.inf], + [0., 0.01, np.inf, np.inf], + [0., 0.01, np.inf, np.inf], + [0., np.inf, np.inf, np.inf]]) + +def test_query_ball_point_vector_r(kdtree_type): + + np.random.seed(1234) + data = np.random.normal(size=(100, 3)) + query = np.random.normal(size=(100, 3)) + tree = kdtree_type(data) + d = np.random.uniform(0, 0.3, size=len(query)) + + rvector = tree.query_ball_point(query, d) + rscalar = [tree.query_ball_point(qi, di) for qi, di in zip(query, d)] + for a, b in zip(rvector, rscalar): + assert_array_equal(sorted(a), sorted(b)) + +def test_query_ball_point_length(kdtree_type): + + np.random.seed(1234) + data = np.random.normal(size=(100, 3)) + query = np.random.normal(size=(100, 3)) + tree = kdtree_type(data) + d = 0.3 + + length = tree.query_ball_point(query, d, return_length=True) + length2 = [len(ind) for ind in tree.query_ball_point(query, d, return_length=False)] + length3 = [len(tree.query_ball_point(qi, d)) for qi in query] + length4 = [tree.query_ball_point(qi, d, return_length=True) for qi in query] + assert_array_equal(length, length2) + assert_array_equal(length, length3) + assert_array_equal(length, length4) + +def test_discontiguous(kdtree_type): + + np.random.seed(1234) + data = np.random.normal(size=(100, 3)) + d_contiguous = np.arange(100) * 0.04 + d_discontiguous = np.ascontiguousarray( + np.arange(100)[::-1] * 0.04)[::-1] + query_contiguous = np.random.normal(size=(100, 3)) + query_discontiguous = np.ascontiguousarray(query_contiguous.T).T + assert query_discontiguous.strides[-1] != query_contiguous.strides[-1] + assert d_discontiguous.strides[-1] != d_contiguous.strides[-1] + + tree = kdtree_type(data) + + length1 = tree.query_ball_point(query_contiguous, + d_contiguous, return_length=True) + length2 = tree.query_ball_point(query_discontiguous, + d_discontiguous, return_length=True) + + assert_array_equal(length1, length2) + + d1, i1 = tree.query(query_contiguous, 1) + d2, i2 = tree.query(query_discontiguous, 1) + + assert_array_equal(d1, d2) + assert_array_equal(i1, i2) + + +@pytest.mark.parametrize("balanced_tree, compact_nodes", + [(True, False), + (True, True), + (False, False), + (False, True)]) +def test_kdtree_empty_input(kdtree_type, balanced_tree, compact_nodes): + # https://github.com/scipy/scipy/issues/5040 + np.random.seed(1234) + empty_v3 = np.empty(shape=(0, 3)) + query_v3 = np.ones(shape=(1, 3)) + query_v2 = np.ones(shape=(2, 3)) + + tree = kdtree_type(empty_v3, balanced_tree=balanced_tree, + compact_nodes=compact_nodes) + length = tree.query_ball_point(query_v3, 0.3, return_length=True) + assert length == 0 + + dd, ii = tree.query(query_v2, 2) + assert ii.shape == (2, 2) + assert dd.shape == (2, 2) + assert np.isinf(dd).all() + + N = tree.count_neighbors(tree, [0, 1]) + assert_array_equal(N, [0, 0]) + + M = tree.sparse_distance_matrix(tree, 0.3) + assert M.shape == (0, 0) + +@KDTreeTest +class _Test_sorted_query_ball_point: + def setup_method(self): + np.random.seed(1234) + self.x = np.random.randn(100, 1) + self.ckdt = self.kdtree_type(self.x) + + def test_return_sorted_True(self): + idxs_list = self.ckdt.query_ball_point(self.x, 1., return_sorted=True) + for idxs in idxs_list: + assert_array_equal(idxs, sorted(idxs)) + + for xi in self.x: + idxs = self.ckdt.query_ball_point(xi, 1., return_sorted=True) + assert_array_equal(idxs, sorted(idxs)) + + def test_return_sorted_None(self): + """Previous behavior was to sort the returned indices if there were + multiple points per query but not sort them if there was a single point + per query.""" + idxs_list = self.ckdt.query_ball_point(self.x, 1.) + for idxs in idxs_list: + assert_array_equal(idxs, sorted(idxs)) + + idxs_list_single = [self.ckdt.query_ball_point(xi, 1.) for xi in self.x] + idxs_list_False = self.ckdt.query_ball_point(self.x, 1., return_sorted=False) + for idxs0, idxs1 in zip(idxs_list_False, idxs_list_single): + assert_array_equal(idxs0, idxs1) + + +def test_kdtree_complex_data(): + # Test that KDTree rejects complex input points (gh-9108) + points = np.random.rand(10, 2).view(complex) + + with pytest.raises(TypeError, match="complex data"): + t = KDTree(points) + + t = KDTree(points.real) + + with pytest.raises(TypeError, match="complex data"): + t.query(points) + + with pytest.raises(TypeError, match="complex data"): + t.query_ball_point(points, r=1) + + +def test_kdtree_tree_access(): + # Test KDTree.tree can be used to traverse the KDTree + np.random.seed(1234) + points = np.random.rand(100, 4) + t = KDTree(points) + root = t.tree + + assert isinstance(root, KDTree.innernode) + assert root.children == points.shape[0] + + # Visit the tree and assert some basic properties for each node + nodes = [root] + while nodes: + n = nodes.pop(-1) + + if isinstance(n, KDTree.leafnode): + assert isinstance(n.children, int) + assert n.children == len(n.idx) + assert_array_equal(points[n.idx], n._node.data_points) + else: + assert isinstance(n, KDTree.innernode) + assert isinstance(n.split_dim, int) + assert 0 <= n.split_dim < t.m + assert isinstance(n.split, float) + assert isinstance(n.children, int) + assert n.children == n.less.children + n.greater.children + nodes.append(n.greater) + nodes.append(n.less) + + +def test_kdtree_attributes(): + # Test KDTree's attributes are available + np.random.seed(1234) + points = np.random.rand(100, 4) + t = KDTree(points) + + assert isinstance(t.m, int) + assert t.n == points.shape[0] + + assert isinstance(t.n, int) + assert t.m == points.shape[1] + + assert isinstance(t.leafsize, int) + assert t.leafsize == 10 + + assert_array_equal(t.maxes, np.amax(points, axis=0)) + assert_array_equal(t.mins, np.amin(points, axis=0)) + assert t.data is points + + +@pytest.mark.parametrize("kdtree_class", [KDTree, cKDTree]) +def test_kdtree_count_neighbors_weighted(kdtree_class): + np.random.seed(1234) + r = np.arange(0.05, 1, 0.05) + + A = np.random.random(21).reshape((7,3)) + B = np.random.random(45).reshape((15,3)) + + wA = np.random.random(7) + wB = np.random.random(15) + + kdA = kdtree_class(A) + kdB = kdtree_class(B) + + nAB = kdA.count_neighbors(kdB, r, cumulative=False, weights=(wA,wB)) + + # Compare against brute-force + weights = wA[None, :] * wB[:, None] + dist = np.linalg.norm(A[None, :, :] - B[:, None, :], axis=-1) + expect = [np.sum(weights[(prev_radius < dist) & (dist <= radius)]) + for prev_radius, radius in zip(itertools.chain([0], r[:-1]), r)] + assert_allclose(nAB, expect) + + +def test_kdtree_nan(): + vals = [1, 5, -10, 7, -4, -16, -6, 6, 3, -11] + n = len(vals) + data = np.concatenate([vals, np.full(n, np.nan)])[:, None] + with pytest.raises(ValueError, match="must be finite"): + KDTree(data) + + +def test_nonfinite_inputs_gh_18223(): + rng = np.random.default_rng(12345) + coords = rng.uniform(size=(100, 3), low=0.0, high=0.1) + t = KDTree(coords, balanced_tree=False, compact_nodes=False) + bad_coord = [np.nan for _ in range(3)] + + with pytest.raises(ValueError, match="must be finite"): + t.query(bad_coord) + with pytest.raises(ValueError, match="must be finite"): + t.query_ball_point(bad_coord, 1) + + coords[0, :] = np.nan + with pytest.raises(ValueError, match="must be finite"): + KDTree(coords, balanced_tree=True, compact_nodes=False) + with pytest.raises(ValueError, match="must be finite"): + KDTree(coords, balanced_tree=False, compact_nodes=True) + with pytest.raises(ValueError, match="must be finite"): + KDTree(coords, balanced_tree=True, compact_nodes=True) + with pytest.raises(ValueError, match="must be finite"): + KDTree(coords, balanced_tree=False, compact_nodes=False) + + +@pytest.mark.parametrize("incantation", [cKDTree, KDTree]) +def test_gh_18800(incantation): + # our prohibition on non-finite values + # in kd-tree workflows means we need + # coercion to NumPy arrays enforced + + class ArrLike(np.ndarray): + def __new__(cls, input_array): + obj = np.asarray(input_array).view(cls) + # we override all() to mimic the problem + # pandas DataFrames encountered in gh-18800 + obj.all = None + return obj + + def __array_finalize__(self, obj): + if obj is None: + return + self.all = getattr(obj, 'all', None) + + points = [ + [66.22, 32.54], + [22.52, 22.39], + [31.01, 81.21], + ] + arr = np.array(points) + arr_like = ArrLike(arr) + tree = incantation(points, 10) + tree.query(arr_like, 1) + tree.query_ball_point(arr_like, 200) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_qhull.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_qhull.py new file mode 100644 index 0000000000000000000000000000000000000000..9b51ba5ce6d00bed39554d3f8e0a7598368fdb4a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_qhull.py @@ -0,0 +1,1186 @@ +import os +import copy + +import numpy as np +from numpy.testing import (assert_equal, assert_almost_equal, + assert_, assert_allclose, assert_array_equal) +import pytest +from pytest import raises as assert_raises + +import scipy.spatial._qhull as qhull +from scipy.spatial import cKDTree as KDTree +from scipy.spatial import Voronoi + +import itertools + +def sorted_tuple(x): + return tuple(sorted(x)) + + +def assert_unordered_tuple_list_equal(a, b, tpl=tuple): + if isinstance(a, np.ndarray): + a = a.tolist() + if isinstance(b, np.ndarray): + b = b.tolist() + a = list(map(tpl, a)) + a.sort() + b = list(map(tpl, b)) + b.sort() + assert_equal(a, b) + + +np.random.seed(1234) + +points = [(0,0), (0,1), (1,0), (1,1), (0.5, 0.5), (0.5, 1.5)] + +pathological_data_1 = np.array([ + [-3.14,-3.14], [-3.14,-2.36], [-3.14,-1.57], [-3.14,-0.79], + [-3.14,0.0], [-3.14,0.79], [-3.14,1.57], [-3.14,2.36], + [-3.14,3.14], [-2.36,-3.14], [-2.36,-2.36], [-2.36,-1.57], + [-2.36,-0.79], [-2.36,0.0], [-2.36,0.79], [-2.36,1.57], + [-2.36,2.36], [-2.36,3.14], [-1.57,-0.79], [-1.57,0.79], + [-1.57,-1.57], [-1.57,0.0], [-1.57,1.57], [-1.57,-3.14], + [-1.57,-2.36], [-1.57,2.36], [-1.57,3.14], [-0.79,-1.57], + [-0.79,1.57], [-0.79,-3.14], [-0.79,-2.36], [-0.79,-0.79], + [-0.79,0.0], [-0.79,0.79], [-0.79,2.36], [-0.79,3.14], + [0.0,-3.14], [0.0,-2.36], [0.0,-1.57], [0.0,-0.79], [0.0,0.0], + [0.0,0.79], [0.0,1.57], [0.0,2.36], [0.0,3.14], [0.79,-3.14], + [0.79,-2.36], [0.79,-0.79], [0.79,0.0], [0.79,0.79], + [0.79,2.36], [0.79,3.14], [0.79,-1.57], [0.79,1.57], + [1.57,-3.14], [1.57,-2.36], [1.57,2.36], [1.57,3.14], + [1.57,-1.57], [1.57,0.0], [1.57,1.57], [1.57,-0.79], + [1.57,0.79], [2.36,-3.14], [2.36,-2.36], [2.36,-1.57], + [2.36,-0.79], [2.36,0.0], [2.36,0.79], [2.36,1.57], + [2.36,2.36], [2.36,3.14], [3.14,-3.14], [3.14,-2.36], + [3.14,-1.57], [3.14,-0.79], [3.14,0.0], [3.14,0.79], + [3.14,1.57], [3.14,2.36], [3.14,3.14], +]) + +pathological_data_2 = np.array([ + [-1, -1], [-1, 0], [-1, 1], + [0, -1], [0, 0], [0, 1], + [1, -1 - np.finfo(np.float64).eps], [1, 0], [1, 1], +]) + +bug_2850_chunks = [np.random.rand(10, 2), + np.array([[0,0], [0,1], [1,0], [1,1]]) # add corners + ] + +# same with some additional chunks +bug_2850_chunks_2 = (bug_2850_chunks + + [np.random.rand(10, 2), + 0.25 + np.array([[0,0], [0,1], [1,0], [1,1]])]) + +DATASETS = { + 'some-points': np.asarray(points), + 'random-2d': np.random.rand(30, 2), + 'random-3d': np.random.rand(30, 3), + 'random-4d': np.random.rand(30, 4), + 'random-5d': np.random.rand(30, 5), + 'random-6d': np.random.rand(10, 6), + 'random-7d': np.random.rand(10, 7), + 'random-8d': np.random.rand(10, 8), + 'pathological-1': pathological_data_1, + 'pathological-2': pathological_data_2 +} + +INCREMENTAL_DATASETS = { + 'bug-2850': (bug_2850_chunks, None), + 'bug-2850-2': (bug_2850_chunks_2, None), +} + + +def _add_inc_data(name, chunksize): + """ + Generate incremental datasets from basic data sets + """ + points = DATASETS[name] + ndim = points.shape[1] + + opts = None + nmin = ndim + 2 + + if name == 'some-points': + # since Qz is not allowed, use QJ + opts = 'QJ Pp' + elif name == 'pathological-1': + # include enough points so that we get different x-coordinates + nmin = 12 + + chunks = [points[:nmin]] + for j in range(nmin, len(points), chunksize): + chunks.append(points[j:j+chunksize]) + + new_name = "%s-chunk-%d" % (name, chunksize) + assert new_name not in INCREMENTAL_DATASETS + INCREMENTAL_DATASETS[new_name] = (chunks, opts) + + +for name in DATASETS: + for chunksize in 1, 4, 16: + _add_inc_data(name, chunksize) + + +class Test_Qhull: + def test_swapping(self): + # Check that Qhull state swapping works + + x = qhull._Qhull(b'v', + np.array([[0,0],[0,1],[1,0],[1,1.],[0.5,0.5]]), + b'Qz') + xd = copy.deepcopy(x.get_voronoi_diagram()) + + y = qhull._Qhull(b'v', + np.array([[0,0],[0,1],[1,0],[1,2.]]), + b'Qz') + yd = copy.deepcopy(y.get_voronoi_diagram()) + + xd2 = copy.deepcopy(x.get_voronoi_diagram()) + x.close() + yd2 = copy.deepcopy(y.get_voronoi_diagram()) + y.close() + + assert_raises(RuntimeError, x.get_voronoi_diagram) + assert_raises(RuntimeError, y.get_voronoi_diagram) + + assert_allclose(xd[0], xd2[0]) + assert_unordered_tuple_list_equal(xd[1], xd2[1], tpl=sorted_tuple) + assert_unordered_tuple_list_equal(xd[2], xd2[2], tpl=sorted_tuple) + assert_unordered_tuple_list_equal(xd[3], xd2[3], tpl=sorted_tuple) + assert_array_equal(xd[4], xd2[4]) + + assert_allclose(yd[0], yd2[0]) + assert_unordered_tuple_list_equal(yd[1], yd2[1], tpl=sorted_tuple) + assert_unordered_tuple_list_equal(yd[2], yd2[2], tpl=sorted_tuple) + assert_unordered_tuple_list_equal(yd[3], yd2[3], tpl=sorted_tuple) + assert_array_equal(yd[4], yd2[4]) + + x.close() + assert_raises(RuntimeError, x.get_voronoi_diagram) + y.close() + assert_raises(RuntimeError, y.get_voronoi_diagram) + + def test_issue_8051(self): + points = np.array( + [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2],[2, 0], [2, 1], [2, 2]] + ) + Voronoi(points) + + +class TestUtilities: + """ + Check that utility functions work. + + """ + + def test_find_simplex(self): + # Simple check that simplex finding works + points = np.array([(0,0), (0,1), (1,1), (1,0)], dtype=np.float64) + tri = qhull.Delaunay(points) + + # +---+ + # |\ 0| + # | \ | + # |1 \| + # +---+ + + assert_equal(tri.simplices, [[1, 3, 2], [3, 1, 0]]) + + for p in [(0.25, 0.25, 1), + (0.75, 0.75, 0), + (0.3, 0.2, 1)]: + i = tri.find_simplex(p[:2]) + assert_equal(i, p[2], err_msg=f'{p!r}') + j = qhull.tsearch(tri, p[:2]) + assert_equal(i, j) + + def test_plane_distance(self): + # Compare plane distance from hyperplane equations obtained from Qhull + # to manually computed plane equations + x = np.array([(0,0), (1, 1), (1, 0), (0.99189033, 0.37674127), + (0.99440079, 0.45182168)], dtype=np.float64) + p = np.array([0.99966555, 0.15685619], dtype=np.float64) + + tri = qhull.Delaunay(x) + + z = tri.lift_points(x) + pz = tri.lift_points(p) + + dist = tri.plane_distance(p) + + for j, v in enumerate(tri.simplices): + x1 = z[v[0]] + x2 = z[v[1]] + x3 = z[v[2]] + + n = np.cross(x1 - x3, x2 - x3) + n /= np.sqrt(np.dot(n, n)) + n *= -np.sign(n[2]) + + d = np.dot(n, pz - x3) + + assert_almost_equal(dist[j], d) + + def test_convex_hull(self): + # Simple check that the convex hull seems to works + points = np.array([(0,0), (0,1), (1,1), (1,0)], dtype=np.float64) + tri = qhull.Delaunay(points) + + # +---+ + # |\ 0| + # | \ | + # |1 \| + # +---+ + + assert_equal(tri.convex_hull, [[3, 2], [1, 2], [1, 0], [3, 0]]) + + def test_volume_area(self): + #Basic check that we get back the correct volume and area for a cube + points = np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), + (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]) + hull = qhull.ConvexHull(points) + + assert_allclose(hull.volume, 1., rtol=1e-14, + err_msg="Volume of cube is incorrect") + assert_allclose(hull.area, 6., rtol=1e-14, + err_msg="Area of cube is incorrect") + + def test_random_volume_area(self): + #Test that the results for a random 10-point convex are + #coherent with the output of qconvex Qt s FA + points = np.array([(0.362568364506, 0.472712355305, 0.347003084477), + (0.733731893414, 0.634480295684, 0.950513180209), + (0.511239955611, 0.876839441267, 0.418047827863), + (0.0765906233393, 0.527373281342, 0.6509863541), + (0.146694972056, 0.596725793348, 0.894860986685), + (0.513808585741, 0.069576205858, 0.530890338876), + (0.512343805118, 0.663537132612, 0.037689295973), + (0.47282965018, 0.462176697655, 0.14061843691), + (0.240584597123, 0.778660020591, 0.722913476339), + (0.951271745935, 0.967000673944, 0.890661319684)]) + + hull = qhull.ConvexHull(points) + assert_allclose(hull.volume, 0.14562013, rtol=1e-07, + err_msg="Volume of random polyhedron is incorrect") + assert_allclose(hull.area, 1.6670425, rtol=1e-07, + err_msg="Area of random polyhedron is incorrect") + + def test_incremental_volume_area_random_input(self): + """Test that incremental mode gives the same volume/area as + non-incremental mode and incremental mode with restart""" + nr_points = 20 + dim = 3 + points = np.random.random((nr_points, dim)) + inc_hull = qhull.ConvexHull(points[:dim+1, :], incremental=True) + inc_restart_hull = qhull.ConvexHull(points[:dim+1, :], incremental=True) + for i in range(dim+1, nr_points): + hull = qhull.ConvexHull(points[:i+1, :]) + inc_hull.add_points(points[i:i+1, :]) + inc_restart_hull.add_points(points[i:i+1, :], restart=True) + assert_allclose(hull.volume, inc_hull.volume, rtol=1e-7) + assert_allclose(hull.volume, inc_restart_hull.volume, rtol=1e-7) + assert_allclose(hull.area, inc_hull.area, rtol=1e-7) + assert_allclose(hull.area, inc_restart_hull.area, rtol=1e-7) + + def _check_barycentric_transforms(self, tri, err_msg="", + unit_cube=False, + unit_cube_tol=0): + """Check that a triangulation has reasonable barycentric transforms""" + vertices = tri.points[tri.simplices] + sc = 1/(tri.ndim + 1.0) + centroids = vertices.sum(axis=1) * sc + + # Either: (i) the simplex has a `nan` barycentric transform, + # or, (ii) the centroid is in the simplex + + def barycentric_transform(tr, x): + r = tr[:,-1,:] + Tinv = tr[:,:-1,:] + return np.einsum('ijk,ik->ij', Tinv, x - r) + + eps = np.finfo(float).eps + + c = barycentric_transform(tri.transform, centroids) + with np.errstate(invalid="ignore"): + ok = np.isnan(c).all(axis=1) | (abs(c - sc)/sc < 0.1).all(axis=1) + + assert_(ok.all(), f"{err_msg} {np.nonzero(~ok)}") + + # Invalid simplices must be (nearly) zero volume + q = vertices[:,:-1,:] - vertices[:,-1,None,:] + volume = np.array([np.linalg.det(q[k,:,:]) + for k in range(tri.nsimplex)]) + ok = np.isfinite(tri.transform[:,0,0]) | (volume < np.sqrt(eps)) + assert_(ok.all(), f"{err_msg} {np.nonzero(~ok)}") + + # Also, find_simplex for the centroid should end up in some + # simplex for the non-degenerate cases + j = tri.find_simplex(centroids) + ok = (j != -1) | np.isnan(tri.transform[:,0,0]) + assert_(ok.all(), f"{err_msg} {np.nonzero(~ok)}") + + if unit_cube: + # If in unit cube, no interior point should be marked out of hull + at_boundary = (centroids <= unit_cube_tol).any(axis=1) + at_boundary |= (centroids >= 1 - unit_cube_tol).any(axis=1) + + ok = (j != -1) | at_boundary + assert_(ok.all(), f"{err_msg} {np.nonzero(~ok)}") + + def test_degenerate_barycentric_transforms(self): + # The triangulation should not produce invalid barycentric + # transforms that stump the simplex finding + data = np.load(os.path.join(os.path.dirname(__file__), 'data', + 'degenerate_pointset.npz')) + points = data['c'] + data.close() + + tri = qhull.Delaunay(points) + + # Check that there are not too many invalid simplices + bad_count = np.isnan(tri.transform[:,0,0]).sum() + assert_(bad_count < 23, bad_count) + + # Check the transforms + self._check_barycentric_transforms(tri) + + @pytest.mark.slow + def test_more_barycentric_transforms(self): + # Triangulate some "nasty" grids + + eps = np.finfo(float).eps + + npoints = {2: 70, 3: 11, 4: 5, 5: 3} + + for ndim in range(2, 6): + # Generate an uniform grid in n-d unit cube + x = np.linspace(0, 1, npoints[ndim]) + grid = np.c_[ + list(map(np.ravel, np.broadcast_arrays(*np.ix_(*([x]*ndim))))) + ].T + + err_msg = "ndim=%d" % ndim + + # Check using regular grid + tri = qhull.Delaunay(grid) + self._check_barycentric_transforms(tri, err_msg=err_msg, + unit_cube=True) + + # Check with eps-perturbations + np.random.seed(1234) + m = (np.random.rand(grid.shape[0]) < 0.2) + grid[m,:] += 2*eps*(np.random.rand(*grid[m,:].shape) - 0.5) + + tri = qhull.Delaunay(grid) + self._check_barycentric_transforms(tri, err_msg=err_msg, + unit_cube=True, + unit_cube_tol=2*eps) + + # Check with duplicated data + tri = qhull.Delaunay(np.r_[grid, grid]) + self._check_barycentric_transforms(tri, err_msg=err_msg, + unit_cube=True, + unit_cube_tol=2*eps) + + +class TestVertexNeighborVertices: + def _check(self, tri): + expected = [set() for j in range(tri.points.shape[0])] + for s in tri.simplices: + for a in s: + for b in s: + if a != b: + expected[a].add(b) + + indptr, indices = tri.vertex_neighbor_vertices + + got = [set(map(int, indices[indptr[j]:indptr[j+1]])) + for j in range(tri.points.shape[0])] + + assert_equal(got, expected, err_msg=f"{got!r} != {expected!r}") + + def test_triangle(self): + points = np.array([(0,0), (0,1), (1,0)], dtype=np.float64) + tri = qhull.Delaunay(points) + self._check(tri) + + def test_rectangle(self): + points = np.array([(0,0), (0,1), (1,1), (1,0)], dtype=np.float64) + tri = qhull.Delaunay(points) + self._check(tri) + + def test_complicated(self): + points = np.array([(0,0), (0,1), (1,1), (1,0), + (0.5, 0.5), (0.9, 0.5)], dtype=np.float64) + tri = qhull.Delaunay(points) + self._check(tri) + + +class TestDelaunay: + """ + Check that triangulation works. + + """ + def test_masked_array_fails(self): + masked_array = np.ma.masked_all(1) + assert_raises(ValueError, qhull.Delaunay, masked_array) + + def test_array_with_nans_fails(self): + points_with_nan = np.array([(0,0), (0,1), (1,1), (1,np.nan)], dtype=np.float64) + assert_raises(ValueError, qhull.Delaunay, points_with_nan) + + def test_nd_simplex(self): + # simple smoke test: triangulate a n-dimensional simplex + for nd in range(2, 8): + points = np.zeros((nd+1, nd)) + for j in range(nd): + points[j,j] = 1.0 + points[-1,:] = 1.0 + + tri = qhull.Delaunay(points) + + tri.simplices.sort() + + assert_equal(tri.simplices, np.arange(nd+1, dtype=int)[None, :]) + assert_equal(tri.neighbors, -1 + np.zeros((nd+1), dtype=int)[None,:]) + + def test_2d_square(self): + # simple smoke test: 2d square + points = np.array([(0,0), (0,1), (1,1), (1,0)], dtype=np.float64) + tri = qhull.Delaunay(points) + + assert_equal(tri.simplices, [[1, 3, 2], [3, 1, 0]]) + assert_equal(tri.neighbors, [[-1, -1, 1], [-1, -1, 0]]) + + def test_duplicate_points(self): + x = np.array([0, 1, 0, 1], dtype=np.float64) + y = np.array([0, 0, 1, 1], dtype=np.float64) + + xp = np.r_[x, x] + yp = np.r_[y, y] + + # shouldn't fail on duplicate points + qhull.Delaunay(np.c_[x, y]) + qhull.Delaunay(np.c_[xp, yp]) + + def test_pathological(self): + # both should succeed + points = DATASETS['pathological-1'] + tri = qhull.Delaunay(points) + assert_equal(tri.points[tri.simplices].max(), points.max()) + assert_equal(tri.points[tri.simplices].min(), points.min()) + + points = DATASETS['pathological-2'] + tri = qhull.Delaunay(points) + assert_equal(tri.points[tri.simplices].max(), points.max()) + assert_equal(tri.points[tri.simplices].min(), points.min()) + + def test_joggle(self): + # Check that the option QJ indeed guarantees that all input points + # occur as vertices of the triangulation + + points = np.random.rand(10, 2) + points = np.r_[points, points] # duplicate input data + + tri = qhull.Delaunay(points, qhull_options="QJ Qbb Pp") + assert_array_equal(np.unique(tri.simplices.ravel()), + np.arange(len(points))) + + def test_coplanar(self): + # Check that the coplanar point output option indeed works + points = np.random.rand(10, 2) + points = np.r_[points, points] # duplicate input data + + tri = qhull.Delaunay(points) + + assert_(len(np.unique(tri.simplices.ravel())) == len(points)//2) + assert_(len(tri.coplanar) == len(points)//2) + + assert_(len(np.unique(tri.coplanar[:,2])) == len(points)//2) + + assert_(np.all(tri.vertex_to_simplex >= 0)) + + def test_furthest_site(self): + points = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1.1, 1.1)] + tri = qhull.Delaunay(points, furthest_site=True) + + expected = np.array([(1, 4, 0), (4, 2, 0)]) # from Qhull + assert_array_equal(tri.simplices, expected) + + @pytest.mark.parametrize("name", sorted(INCREMENTAL_DATASETS)) + def test_incremental(self, name): + # Test incremental construction of the triangulation + + chunks, opts = INCREMENTAL_DATASETS[name] + points = np.concatenate(chunks, axis=0) + + obj = qhull.Delaunay(chunks[0], incremental=True, + qhull_options=opts) + for chunk in chunks[1:]: + obj.add_points(chunk) + + obj2 = qhull.Delaunay(points) + + obj3 = qhull.Delaunay(chunks[0], incremental=True, + qhull_options=opts) + if len(chunks) > 1: + obj3.add_points(np.concatenate(chunks[1:], axis=0), + restart=True) + + # Check that the incremental mode agrees with upfront mode + if name.startswith('pathological'): + # XXX: These produce valid but different triangulations. + # They look OK when plotted, but how to check them? + + assert_array_equal(np.unique(obj.simplices.ravel()), + np.arange(points.shape[0])) + assert_array_equal(np.unique(obj2.simplices.ravel()), + np.arange(points.shape[0])) + else: + assert_unordered_tuple_list_equal(obj.simplices, obj2.simplices, + tpl=sorted_tuple) + + assert_unordered_tuple_list_equal(obj2.simplices, obj3.simplices, + tpl=sorted_tuple) + + +def assert_hulls_equal(points, facets_1, facets_2): + # Check that two convex hulls constructed from the same point set + # are equal + + facets_1 = set(map(sorted_tuple, facets_1)) + facets_2 = set(map(sorted_tuple, facets_2)) + + if facets_1 != facets_2 and points.shape[1] == 2: + # The direct check fails for the pathological cases + # --- then the convex hull from Delaunay differs (due + # to rounding error etc.) from the hull computed + # otherwise, by the question whether (tricoplanar) + # points that lie almost exactly on the hull are + # included as vertices of the hull or not. + # + # So we check the result, and accept it if the Delaunay + # hull line segments are a subset of the usual hull. + + eps = 1000 * np.finfo(float).eps + + for a, b in facets_1: + for ap, bp in facets_2: + t = points[bp] - points[ap] + t /= np.linalg.norm(t) # tangent + n = np.array([-t[1], t[0]]) # normal + + # check that the two line segments are parallel + # to the same line + c1 = np.dot(n, points[b] - points[ap]) + c2 = np.dot(n, points[a] - points[ap]) + if not np.allclose(np.dot(c1, n), 0): + continue + if not np.allclose(np.dot(c2, n), 0): + continue + + # Check that the segment (a, b) is contained in (ap, bp) + c1 = np.dot(t, points[a] - points[ap]) + c2 = np.dot(t, points[b] - points[ap]) + c3 = np.dot(t, points[bp] - points[ap]) + if c1 < -eps or c1 > c3 + eps: + continue + if c2 < -eps or c2 > c3 + eps: + continue + + # OK: + break + else: + raise AssertionError("comparison fails") + + # it was OK + return + + assert_equal(facets_1, facets_2) + + +class TestConvexHull: + def test_masked_array_fails(self): + masked_array = np.ma.masked_all(1) + assert_raises(ValueError, qhull.ConvexHull, masked_array) + + def test_array_with_nans_fails(self): + points_with_nan = np.array([(0,0), (1,1), (2,np.nan)], dtype=np.float64) + assert_raises(ValueError, qhull.ConvexHull, points_with_nan) + + @pytest.mark.parametrize("name", sorted(DATASETS)) + def test_hull_consistency_tri(self, name): + # Check that a convex hull returned by qhull in ndim + # and the hull constructed from ndim delaunay agree + points = DATASETS[name] + + tri = qhull.Delaunay(points) + hull = qhull.ConvexHull(points) + + assert_hulls_equal(points, tri.convex_hull, hull.simplices) + + # Check that the hull extremes are as expected + if points.shape[1] == 2: + assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) + else: + assert_equal(np.unique(hull.simplices), hull.vertices) + + @pytest.mark.parametrize("name", sorted(INCREMENTAL_DATASETS)) + def test_incremental(self, name): + # Test incremental construction of the convex hull + chunks, _ = INCREMENTAL_DATASETS[name] + points = np.concatenate(chunks, axis=0) + + obj = qhull.ConvexHull(chunks[0], incremental=True) + for chunk in chunks[1:]: + obj.add_points(chunk) + + obj2 = qhull.ConvexHull(points) + + obj3 = qhull.ConvexHull(chunks[0], incremental=True) + if len(chunks) > 1: + obj3.add_points(np.concatenate(chunks[1:], axis=0), + restart=True) + + # Check that the incremental mode agrees with upfront mode + assert_hulls_equal(points, obj.simplices, obj2.simplices) + assert_hulls_equal(points, obj.simplices, obj3.simplices) + + def test_vertices_2d(self): + # The vertices should be in counterclockwise order in 2-D + np.random.seed(1234) + points = np.random.rand(30, 2) + + hull = qhull.ConvexHull(points) + assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) + + # Check counterclockwiseness + x, y = hull.points[hull.vertices].T + angle = np.arctan2(y - y.mean(), x - x.mean()) + assert_(np.all(np.diff(np.unwrap(angle)) > 0)) + + def test_volume_area(self): + # Basic check that we get back the correct volume and area for a cube + points = np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), + (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]) + tri = qhull.ConvexHull(points) + + assert_allclose(tri.volume, 1., rtol=1e-14) + assert_allclose(tri.area, 6., rtol=1e-14) + + @pytest.mark.parametrize("incremental", [False, True]) + def test_good2d(self, incremental): + # Make sure the QGn option gives the correct value of "good". + points = np.array([[0.2, 0.2], + [0.2, 0.4], + [0.4, 0.4], + [0.4, 0.2], + [0.3, 0.6]]) + hull = qhull.ConvexHull(points=points, + incremental=incremental, + qhull_options='QG4') + expected = np.array([False, True, False, False], dtype=bool) + actual = hull.good + assert_equal(actual, expected) + + @pytest.mark.parametrize("visibility", [ + "QG4", # visible=True + "QG-4", # visible=False + ]) + @pytest.mark.parametrize("new_gen, expected", [ + # add generator that places QG4 inside hull + # so all facets are invisible + (np.array([[0.3, 0.7]]), + np.array([False, False, False, False, False], dtype=bool)), + # adding a generator on the opposite side of the square + # should preserve the single visible facet & add one invisible + # facet + (np.array([[0.3, -0.7]]), + np.array([False, True, False, False, False], dtype=bool)), + # split the visible facet on top of the square into two + # visible facets, with visibility at the end of the array + # because add_points concatenates + (np.array([[0.3, 0.41]]), + np.array([False, False, False, True, True], dtype=bool)), + # with our current Qhull options, coplanarity will not count + # for visibility; this case shifts one visible & one invisible + # facet & adds a coplanar facet + # simplex at index position 2 is the shifted visible facet + # the final simplex is the coplanar facet + (np.array([[0.5, 0.6], [0.6, 0.6]]), + np.array([False, False, True, False, False], dtype=bool)), + # place the new generator such that it envelops the query + # point within the convex hull, but only just barely within + # the double precision limit + # NOTE: testing exact degeneracy is less predictable than this + # scenario, perhaps because of the default Qt option we have + # enabled for Qhull to handle precision matters + (np.array([[0.3, 0.6 + 1e-16]]), + np.array([False, False, False, False, False], dtype=bool)), + ]) + def test_good2d_incremental_changes(self, new_gen, expected, + visibility): + # use the usual square convex hull + # generators from test_good2d + points = np.array([[0.2, 0.2], + [0.2, 0.4], + [0.4, 0.4], + [0.4, 0.2], + [0.3, 0.6]]) + hull = qhull.ConvexHull(points=points, + incremental=True, + qhull_options=visibility) + hull.add_points(new_gen) + actual = hull.good + if '-' in visibility: + expected = np.invert(expected) + assert_equal(actual, expected) + + @pytest.mark.parametrize("incremental", [False, True]) + def test_good2d_no_option(self, incremental): + # handle case where good attribute doesn't exist + # because Qgn or Qg-n wasn't specified + points = np.array([[0.2, 0.2], + [0.2, 0.4], + [0.4, 0.4], + [0.4, 0.2], + [0.3, 0.6]]) + hull = qhull.ConvexHull(points=points, + incremental=incremental) + actual = hull.good + assert actual is None + # preserve None after incremental addition + if incremental: + hull.add_points(np.zeros((1, 2))) + actual = hull.good + assert actual is None + + @pytest.mark.parametrize("incremental", [False, True]) + def test_good2d_inside(self, incremental): + # Make sure the QGn option gives the correct value of "good". + # When point n is inside the convex hull of the rest, good is + # all False. + points = np.array([[0.2, 0.2], + [0.2, 0.4], + [0.4, 0.4], + [0.4, 0.2], + [0.3, 0.3]]) + hull = qhull.ConvexHull(points=points, + incremental=incremental, + qhull_options='QG4') + expected = np.array([False, False, False, False], dtype=bool) + actual = hull.good + assert_equal(actual, expected) + + @pytest.mark.parametrize("incremental", [False, True]) + def test_good3d(self, incremental): + # Make sure the QGn option gives the correct value of "good" + # for a 3d figure + points = np.array([[0.0, 0.0, 0.0], + [0.90029516, -0.39187448, 0.18948093], + [0.48676420, -0.72627633, 0.48536925], + [0.57651530, -0.81179274, -0.09285832], + [0.67846893, -0.71119562, 0.18406710]]) + hull = qhull.ConvexHull(points=points, + incremental=incremental, + qhull_options='QG0') + expected = np.array([True, False, False, False], dtype=bool) + assert_equal(hull.good, expected) + +class TestVoronoi: + + @pytest.mark.parametrize("qhull_opts, extra_pts", [ + # option Qz (default for SciPy) will add + # an extra point at infinity + ("Qbb Qc Qz", 1), + ("Qbb Qc", 0), + ]) + @pytest.mark.parametrize("n_pts", [50, 100]) + @pytest.mark.parametrize("ndim", [2, 3]) + def test_point_region_structure(self, + qhull_opts, + n_pts, + extra_pts, + ndim): + # see gh-16773 + rng = np.random.default_rng(7790) + points = rng.random((n_pts, ndim)) + vor = Voronoi(points, qhull_options=qhull_opts) + pt_region = vor.point_region + assert pt_region.max() == n_pts - 1 + extra_pts + assert pt_region.size == len(vor.regions) - extra_pts + assert len(vor.regions) == n_pts + extra_pts + assert vor.points.shape[0] == n_pts + # if there is an empty sublist in the Voronoi + # regions data structure, it should never be + # indexed because it corresponds to an internally + # added point at infinity and is not a member of the + # generators (input points) + if extra_pts: + sublens = [len(x) for x in vor.regions] + # only one point at infinity (empty region) + # is allowed + assert sublens.count(0) == 1 + assert sublens.index(0) not in pt_region + + def test_masked_array_fails(self): + masked_array = np.ma.masked_all(1) + assert_raises(ValueError, qhull.Voronoi, masked_array) + + def test_simple(self): + # Simple case with known Voronoi diagram + points = [(0, 0), (0, 1), (0, 2), + (1, 0), (1, 1), (1, 2), + (2, 0), (2, 1), (2, 2)] + + # qhull v o Fv Qbb Qc Qz < dat + output = """ + 2 + 5 10 1 + -10.101 -10.101 + 0.5 0.5 + 0.5 1.5 + 1.5 0.5 + 1.5 1.5 + 2 0 1 + 3 2 0 1 + 2 0 2 + 3 3 0 1 + 4 1 2 4 3 + 3 4 0 2 + 2 0 3 + 3 4 0 3 + 2 0 4 + 0 + 12 + 4 0 3 0 1 + 4 0 1 0 1 + 4 1 4 1 2 + 4 1 2 0 2 + 4 2 5 0 2 + 4 3 4 1 3 + 4 3 6 0 3 + 4 4 5 2 4 + 4 4 7 3 4 + 4 5 8 0 4 + 4 6 7 0 3 + 4 7 8 0 4 + """ + self._compare_qvoronoi(points, output) + + def _compare_qvoronoi(self, points, output, **kw): + """Compare to output from 'qvoronoi o Fv < data' to Voronoi()""" + + # Parse output + output = [list(map(float, x.split())) for x in output.strip().splitlines()] + nvertex = int(output[1][0]) + vertices = list(map(tuple, output[3:2+nvertex])) # exclude inf + nregion = int(output[1][1]) + regions = [[int(y)-1 for y in x[1:]] + for x in output[2+nvertex:2+nvertex+nregion]] + ridge_points = [[int(y) for y in x[1:3]] + for x in output[3+nvertex+nregion:]] + ridge_vertices = [[int(y)-1 for y in x[3:]] + for x in output[3+nvertex+nregion:]] + + # Compare results + vor = qhull.Voronoi(points, **kw) + + def sorttuple(x): + return tuple(sorted(x)) + + assert_allclose(vor.vertices, vertices) + assert_equal(set(map(tuple, vor.regions)), + set(map(tuple, regions))) + + p1 = list(zip(list(map(sorttuple, ridge_points)), + list(map(sorttuple, ridge_vertices)))) + p2 = list(zip(list(map(sorttuple, vor.ridge_points.tolist())), + list(map(sorttuple, vor.ridge_vertices)))) + p1.sort() + p2.sort() + + assert_equal(p1, p2) + + @pytest.mark.parametrize("name", sorted(DATASETS)) + def test_ridges(self, name): + # Check that the ridges computed by Voronoi indeed separate + # the regions of nearest neighborhood, by comparing the result + # to KDTree. + + points = DATASETS[name] + + tree = KDTree(points) + vor = qhull.Voronoi(points) + + for p, v in vor.ridge_dict.items(): + # consider only finite ridges + if not np.all(np.asarray(v) >= 0): + continue + + ridge_midpoint = vor.vertices[v].mean(axis=0) + d = 1e-6 * (points[p[0]] - ridge_midpoint) + + dist, k = tree.query(ridge_midpoint + d, k=1) + assert_equal(k, p[0]) + + dist, k = tree.query(ridge_midpoint - d, k=1) + assert_equal(k, p[1]) + + def test_furthest_site(self): + points = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1.1, 1.1)] + + # qhull v o Fv Qbb Qc Qu < dat + output = """ + 2 + 3 5 1 + -10.101 -10.101 + 0.6000000000000001 0.5 + 0.5 0.6000000000000001 + 3 0 2 1 + 2 0 1 + 2 0 2 + 0 + 3 0 2 1 + 5 + 4 0 2 0 2 + 4 0 4 1 2 + 4 0 1 0 1 + 4 1 4 0 1 + 4 2 4 0 2 + """ + self._compare_qvoronoi(points, output, furthest_site=True) + + def test_furthest_site_flag(self): + points = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1.1, 1.1)] + + vor = Voronoi(points) + assert_equal(vor.furthest_site,False) + vor = Voronoi(points,furthest_site=True) + assert_equal(vor.furthest_site,True) + + @pytest.mark.parametrize("name", sorted(INCREMENTAL_DATASETS)) + def test_incremental(self, name): + # Test incremental construction of the triangulation + + if INCREMENTAL_DATASETS[name][0][0].shape[1] > 3: + # too slow (testing of the result --- qhull is still fast) + return + + chunks, opts = INCREMENTAL_DATASETS[name] + points = np.concatenate(chunks, axis=0) + + obj = qhull.Voronoi(chunks[0], incremental=True, + qhull_options=opts) + for chunk in chunks[1:]: + obj.add_points(chunk) + + obj2 = qhull.Voronoi(points) + + obj3 = qhull.Voronoi(chunks[0], incremental=True, + qhull_options=opts) + if len(chunks) > 1: + obj3.add_points(np.concatenate(chunks[1:], axis=0), + restart=True) + + # -- Check that the incremental mode agrees with upfront mode + assert_equal(len(obj.point_region), len(obj2.point_region)) + assert_equal(len(obj.point_region), len(obj3.point_region)) + + # The vertices may be in different order or duplicated in + # the incremental map + for objx in obj, obj3: + vertex_map = {-1: -1} + for i, v in enumerate(objx.vertices): + for j, v2 in enumerate(obj2.vertices): + if np.allclose(v, v2): + vertex_map[i] = j + + def remap(x): + if hasattr(x, '__len__'): + return tuple({remap(y) for y in x}) + try: + return vertex_map[x] + except KeyError as e: + message = (f"incremental result has spurious vertex " + f"at {objx.vertices[x]!r}") + raise AssertionError(message) from e + + def simplified(x): + items = set(map(sorted_tuple, x)) + if () in items: + items.remove(()) + items = [x for x in items if len(x) > 1] + items.sort() + return items + + assert_equal( + simplified(remap(objx.regions)), + simplified(obj2.regions) + ) + assert_equal( + simplified(remap(objx.ridge_vertices)), + simplified(obj2.ridge_vertices) + ) + + # XXX: compare ridge_points --- not clear exactly how to do this + + +class Test_HalfspaceIntersection: + def assert_unordered_allclose(self, arr1, arr2, rtol=1e-7): + """Check that every line in arr1 is only once in arr2""" + assert_equal(arr1.shape, arr2.shape) + + truths = np.zeros((arr1.shape[0],), dtype=bool) + for l1 in arr1: + indexes = np.nonzero((abs(arr2 - l1) < rtol).all(axis=1))[0] + assert_equal(indexes.shape, (1,)) + truths[indexes[0]] = True + assert_(truths.all()) + + @pytest.mark.parametrize("dt", [np.float64, int]) + def test_cube_halfspace_intersection(self, dt): + halfspaces = np.array([[-1, 0, 0], + [0, -1, 0], + [1, 0, -2], + [0, 1, -2]], dtype=dt) + feasible_point = np.array([1, 1], dtype=dt) + + points = np.array([[0.0, 0.0], [2.0, 0.0], [0.0, 2.0], [2.0, 2.0]]) + + hull = qhull.HalfspaceIntersection(halfspaces, feasible_point) + + assert_allclose(hull.intersections, points) + + def test_self_dual_polytope_intersection(self): + fname = os.path.join(os.path.dirname(__file__), 'data', + 'selfdual-4d-polytope.txt') + ineqs = np.genfromtxt(fname) + halfspaces = -np.hstack((ineqs[:, 1:], ineqs[:, :1])) + + feas_point = np.array([0., 0., 0., 0.]) + hs = qhull.HalfspaceIntersection(halfspaces, feas_point) + + assert_equal(hs.intersections.shape, (24, 4)) + + assert_almost_equal(hs.dual_volume, 32.0) + assert_equal(len(hs.dual_facets), 24) + for facet in hs.dual_facets: + assert_equal(len(facet), 6) + + dists = halfspaces[:, -1] + halfspaces[:, :-1].dot(feas_point) + self.assert_unordered_allclose((halfspaces[:, :-1].T/dists).T, hs.dual_points) + + points = itertools.permutations([0., 0., 0.5, -0.5]) + for point in points: + assert_equal(np.sum((hs.intersections == point).all(axis=1)), 1) + + def test_wrong_feasible_point(self): + halfspaces = np.array([[-1.0, 0.0, 0.0], + [0.0, -1.0, 0.0], + [1.0, 0.0, -1.0], + [0.0, 1.0, -1.0]]) + feasible_point = np.array([0.5, 0.5, 0.5]) + #Feasible point is (ndim,) instead of (ndim-1,) + assert_raises(ValueError, + qhull.HalfspaceIntersection, halfspaces, feasible_point) + feasible_point = np.array([[0.5], [0.5]]) + #Feasible point is (ndim-1, 1) instead of (ndim-1,) + assert_raises(ValueError, + qhull.HalfspaceIntersection, halfspaces, feasible_point) + feasible_point = np.array([[0.5, 0.5]]) + #Feasible point is (1, ndim-1) instead of (ndim-1,) + assert_raises(ValueError, + qhull.HalfspaceIntersection, halfspaces, feasible_point) + + feasible_point = np.array([-0.5, -0.5]) + #Feasible point is outside feasible region + assert_raises(qhull.QhullError, + qhull.HalfspaceIntersection, halfspaces, feasible_point) + + def test_incremental(self): + #Cube + halfspaces = np.array([[0., 0., -1., -0.5], + [0., -1., 0., -0.5], + [-1., 0., 0., -0.5], + [1., 0., 0., -0.5], + [0., 1., 0., -0.5], + [0., 0., 1., -0.5]]) + #Cut each summit + extra_normals = np.array([[1., 1., 1.], + [1., 1., -1.], + [1., -1., 1.], + [1, -1., -1.]]) + offsets = np.array([[-1.]]*8) + extra_halfspaces = np.hstack((np.vstack((extra_normals, -extra_normals)), + offsets)) + + feas_point = np.array([0., 0., 0.]) + + inc_hs = qhull.HalfspaceIntersection(halfspaces, feas_point, incremental=True) + + inc_res_hs = qhull.HalfspaceIntersection(halfspaces, feas_point, + incremental=True) + + for i, ehs in enumerate(extra_halfspaces): + inc_hs.add_halfspaces(ehs[np.newaxis, :]) + + inc_res_hs.add_halfspaces(ehs[np.newaxis, :], restart=True) + + total = np.vstack((halfspaces, extra_halfspaces[:i+1, :])) + + hs = qhull.HalfspaceIntersection(total, feas_point) + + assert_allclose(inc_hs.halfspaces, inc_res_hs.halfspaces) + assert_allclose(inc_hs.halfspaces, hs.halfspaces) + + #Direct computation and restart should have points in same order + assert_allclose(hs.intersections, inc_res_hs.intersections) + #Incremental will have points in different order than direct computation + self.assert_unordered_allclose(inc_hs.intersections, hs.intersections) + + inc_hs.close() + + def test_cube(self): + # Halfspaces of the cube: + halfspaces = np.array([[-1., 0., 0., 0.], # x >= 0 + [1., 0., 0., -1.], # x <= 1 + [0., -1., 0., 0.], # y >= 0 + [0., 1., 0., -1.], # y <= 1 + [0., 0., -1., 0.], # z >= 0 + [0., 0., 1., -1.]]) # z <= 1 + point = np.array([0.5, 0.5, 0.5]) + + hs = qhull.HalfspaceIntersection(halfspaces, point) + + # qhalf H0.5,0.5,0.5 o < input.txt + qhalf_points = np.array([ + [-2, 0, 0], + [2, 0, 0], + [0, -2, 0], + [0, 2, 0], + [0, 0, -2], + [0, 0, 2]]) + qhalf_facets = [ + [2, 4, 0], + [4, 2, 1], + [5, 2, 0], + [2, 5, 1], + [3, 4, 1], + [4, 3, 0], + [5, 3, 1], + [3, 5, 0]] + + assert len(qhalf_facets) == len(hs.dual_facets) + for a, b in zip(qhalf_facets, hs.dual_facets): + assert set(a) == set(b) # facet orientation can differ + + assert_allclose(hs.dual_points, qhalf_points) + + +@pytest.mark.parametrize("diagram_type", [Voronoi, qhull.Delaunay]) +def test_gh_20623(diagram_type): + rng = np.random.default_rng(123) + invalid_data = rng.random((4, 10, 3)) + with pytest.raises(ValueError, match="dimensions"): + diagram_type(invalid_data) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_slerp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_slerp.py new file mode 100644 index 0000000000000000000000000000000000000000..4754d5254fa6df6de7ce4cc464bba5be150862b2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_slerp.py @@ -0,0 +1,416 @@ +import numpy as np +from numpy.testing import assert_allclose + +import pytest +from scipy.spatial import geometric_slerp + + +def _generate_spherical_points(ndim=3, n_pts=2): + # generate uniform points on sphere + # see: https://stackoverflow.com/a/23785326 + # tentatively extended to arbitrary dims + # for 0-sphere it will always produce antipodes + np.random.seed(123) + points = np.random.normal(size=(n_pts, ndim)) + points /= np.linalg.norm(points, axis=1)[:, np.newaxis] + return points[0], points[1] + + +class TestGeometricSlerp: + # Test various properties of the geometric slerp code + + @pytest.mark.parametrize("n_dims", [2, 3, 5, 7, 9]) + @pytest.mark.parametrize("n_pts", [0, 3, 17]) + def test_shape_property(self, n_dims, n_pts): + # geometric_slerp output shape should match + # input dimensionality & requested number + # of interpolation points + start, end = _generate_spherical_points(n_dims, 2) + + actual = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, n_pts)) + + assert actual.shape == (n_pts, n_dims) + + @pytest.mark.parametrize("n_dims", [2, 3, 5, 7, 9]) + @pytest.mark.parametrize("n_pts", [3, 17]) + def test_include_ends(self, n_dims, n_pts): + # geometric_slerp should return a data structure + # that includes the start and end coordinates + # when t includes 0 and 1 ends + # this is convenient for plotting surfaces represented + # by interpolations for example + + # the generator doesn't work so well for the unit + # sphere (it always produces antipodes), so use + # custom values there + start, end = _generate_spherical_points(n_dims, 2) + + actual = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, n_pts)) + + assert_allclose(actual[0], start) + assert_allclose(actual[-1], end) + + @pytest.mark.parametrize("start, end", [ + # both arrays are not flat + (np.zeros((1, 3)), np.ones((1, 3))), + # only start array is not flat + (np.zeros((1, 3)), np.ones(3)), + # only end array is not flat + (np.zeros(1), np.ones((3, 1))), + ]) + def test_input_shape_flat(self, start, end): + # geometric_slerp should handle input arrays that are + # not flat appropriately + with pytest.raises(ValueError, match='one-dimensional'): + geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 10)) + + @pytest.mark.parametrize("start, end", [ + # 7-D and 3-D ends + (np.zeros(7), np.ones(3)), + # 2-D and 1-D ends + (np.zeros(2), np.ones(1)), + # empty, "3D" will also get caught this way + (np.array([]), np.ones(3)), + ]) + def test_input_dim_mismatch(self, start, end): + # geometric_slerp must appropriately handle cases where + # an interpolation is attempted across two different + # dimensionalities + with pytest.raises(ValueError, match='dimensions'): + geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 10)) + + @pytest.mark.parametrize("start, end", [ + # both empty + (np.array([]), np.array([])), + ]) + def test_input_at_least1d(self, start, end): + # empty inputs to geometric_slerp must + # be handled appropriately when not detected + # by mismatch + with pytest.raises(ValueError, match='at least two-dim'): + geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 10)) + + @pytest.mark.parametrize("start, end, expected", [ + # North and South Poles are definitely antipodes + # but should be handled gracefully now + (np.array([0, 0, 1.0]), np.array([0, 0, -1.0]), "warning"), + # this case will issue a warning & be handled + # gracefully as well; + # North Pole was rotated very slightly + # using r = R.from_euler('x', 0.035, degrees=True) + # to achieve Euclidean distance offset from diameter by + # 9.328908379124812e-08, within the default tol + (np.array([0.00000000e+00, + -6.10865200e-04, + 9.99999813e-01]), np.array([0, 0, -1.0]), "warning"), + # this case should succeed without warning because a + # sufficiently large + # rotation was applied to North Pole point to shift it + # to a Euclidean distance of 2.3036691931821451e-07 + # from South Pole, which is larger than tol + (np.array([0.00000000e+00, + -9.59930941e-04, + 9.99999539e-01]), np.array([0, 0, -1.0]), "success"), + ]) + def test_handle_antipodes(self, start, end, expected): + # antipodal points must be handled appropriately; + # there are an infinite number of possible geodesic + # interpolations between them in higher dims + if expected == "warning": + with pytest.warns(UserWarning, match='antipodes'): + res = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 10)) + else: + res = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 10)) + + # antipodes or near-antipodes should still produce + # slerp paths on the surface of the sphere (but they + # may be ambiguous): + assert_allclose(np.linalg.norm(res, axis=1), 1.0) + + @pytest.mark.parametrize("start, end, expected", [ + # 2-D with n_pts=4 (two new interpolation points) + # this is an actual circle + (np.array([1, 0]), + np.array([0, 1]), + np.array([[1, 0], + [np.sqrt(3) / 2, 0.5], # 30 deg on unit circle + [0.5, np.sqrt(3) / 2], # 60 deg on unit circle + [0, 1]])), + # likewise for 3-D (add z = 0 plane) + # this is an ordinary sphere + (np.array([1, 0, 0]), + np.array([0, 1, 0]), + np.array([[1, 0, 0], + [np.sqrt(3) / 2, 0.5, 0], + [0.5, np.sqrt(3) / 2, 0], + [0, 1, 0]])), + # for 5-D, pad more columns with constants + # zeros are easiest--non-zero values on unit + # circle are more difficult to reason about + # at higher dims + (np.array([1, 0, 0, 0, 0]), + np.array([0, 1, 0, 0, 0]), + np.array([[1, 0, 0, 0, 0], + [np.sqrt(3) / 2, 0.5, 0, 0, 0], + [0.5, np.sqrt(3) / 2, 0, 0, 0], + [0, 1, 0, 0, 0]])), + + ]) + def test_straightforward_examples(self, start, end, expected): + # some straightforward interpolation tests, sufficiently + # simple to use the unit circle to deduce expected values; + # for larger dimensions, pad with constants so that the + # data is N-D but simpler to reason about + actual = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 4)) + assert_allclose(actual, expected, atol=1e-16) + + @pytest.mark.parametrize("t", [ + # both interval ends clearly violate limits + np.linspace(-20, 20, 300), + # only one interval end violating limit slightly + np.linspace(-0.0001, 0.0001, 17), + ]) + def test_t_values_limits(self, t): + # geometric_slerp() should appropriately handle + # interpolation parameters < 0 and > 1 + with pytest.raises(ValueError, match='interpolation parameter'): + _ = geometric_slerp(start=np.array([1, 0]), + end=np.array([0, 1]), + t=t) + + @pytest.mark.parametrize("start, end", [ + (np.array([1]), + np.array([0])), + (np.array([0]), + np.array([1])), + (np.array([-17.7]), + np.array([165.9])), + ]) + def test_0_sphere_handling(self, start, end): + # it does not make sense to interpolate the set of + # two points that is the 0-sphere + with pytest.raises(ValueError, match='at least two-dim'): + _ = geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 4)) + + @pytest.mark.parametrize("tol", [ + # an integer currently raises + 5, + # string raises + "7", + # list and arrays also raise + [5, 6, 7], np.array(9.0), + ]) + def test_tol_type(self, tol): + # geometric_slerp() should raise if tol is not + # a suitable float type + with pytest.raises(ValueError, match='must be a float'): + _ = geometric_slerp(start=np.array([1, 0]), + end=np.array([0, 1]), + t=np.linspace(0, 1, 5), + tol=tol) + + @pytest.mark.parametrize("tol", [ + -5e-6, + -7e-10, + ]) + def test_tol_sign(self, tol): + # geometric_slerp() currently handles negative + # tol values, as long as they are floats + _ = geometric_slerp(start=np.array([1, 0]), + end=np.array([0, 1]), + t=np.linspace(0, 1, 5), + tol=tol) + + @pytest.mark.parametrize("start, end", [ + # 1-sphere (circle) with one point at origin + # and the other on the circle + (np.array([1, 0]), np.array([0, 0])), + # 2-sphere (normal sphere) with both points + # just slightly off sphere by the same amount + # in different directions + (np.array([1 + 1e-6, 0, 0]), + np.array([0, 1 - 1e-6, 0])), + # same thing in 4-D + (np.array([1 + 1e-6, 0, 0, 0]), + np.array([0, 1 - 1e-6, 0, 0])), + ]) + def test_unit_sphere_enforcement(self, start, end): + # geometric_slerp() should raise on input that clearly + # cannot be on an n-sphere of radius 1 + with pytest.raises(ValueError, match='unit n-sphere'): + geometric_slerp(start=start, + end=end, + t=np.linspace(0, 1, 5)) + + @pytest.mark.parametrize("start, end", [ + # 1-sphere 45 degree case + (np.array([1, 0]), + np.array([np.sqrt(2) / 2., + np.sqrt(2) / 2.])), + # 2-sphere 135 degree case + (np.array([1, 0]), + np.array([-np.sqrt(2) / 2., + np.sqrt(2) / 2.])), + ]) + @pytest.mark.parametrize("t_func", [ + np.linspace, np.logspace]) + def test_order_handling(self, start, end, t_func): + # geometric_slerp() should handle scenarios with + # ascending and descending t value arrays gracefully; + # results should simply be reversed + + # for scrambled / unsorted parameters, the same values + # should be returned, just in scrambled order + + num_t_vals = 20 + np.random.seed(789) + forward_t_vals = t_func(0, 10, num_t_vals) + # normalize to max of 1 + forward_t_vals /= forward_t_vals.max() + reverse_t_vals = np.flipud(forward_t_vals) + shuffled_indices = np.arange(num_t_vals) + np.random.shuffle(shuffled_indices) + scramble_t_vals = forward_t_vals.copy()[shuffled_indices] + + forward_results = geometric_slerp(start=start, + end=end, + t=forward_t_vals) + reverse_results = geometric_slerp(start=start, + end=end, + t=reverse_t_vals) + scrambled_results = geometric_slerp(start=start, + end=end, + t=scramble_t_vals) + + # check fidelity to input order + assert_allclose(forward_results, np.flipud(reverse_results)) + assert_allclose(forward_results[shuffled_indices], + scrambled_results) + + @pytest.mark.parametrize("t", [ + # string: + "15, 5, 7", + # complex numbers currently produce a warning + # but not sure we need to worry about it too much: + # [3 + 1j, 5 + 2j], + ]) + def test_t_values_conversion(self, t): + with pytest.raises(ValueError): + _ = geometric_slerp(start=np.array([1]), + end=np.array([0]), + t=t) + + def test_accept_arraylike(self): + # array-like support requested by reviewer + # in gh-10380 + actual = geometric_slerp([1, 0], [0, 1], [0, 1/3, 0.5, 2/3, 1]) + + # expected values are based on visual inspection + # of the unit circle for the progressions along + # the circumference provided in t + expected = np.array([[1, 0], + [np.sqrt(3) / 2, 0.5], + [np.sqrt(2) / 2, + np.sqrt(2) / 2], + [0.5, np.sqrt(3) / 2], + [0, 1]], dtype=np.float64) + # Tyler's original Cython implementation of geometric_slerp + # can pass at atol=0 here, but on balance we will accept + # 1e-16 for an implementation that avoids Cython and + # makes up accuracy ground elsewhere + assert_allclose(actual, expected, atol=1e-16) + + def test_scalar_t(self): + # when t is a scalar, return value is a single + # interpolated point of the appropriate dimensionality + # requested by reviewer in gh-10380 + actual = geometric_slerp([1, 0], [0, 1], 0.5) + expected = np.array([np.sqrt(2) / 2, + np.sqrt(2) / 2], dtype=np.float64) + assert actual.shape == (2,) + assert_allclose(actual, expected) + + @pytest.mark.parametrize('start', [ + np.array([1, 0, 0]), + np.array([0, 1]), + ]) + @pytest.mark.parametrize('t', [ + np.array(1), + np.array([1]), + np.array([[1]]), + np.array([[[1]]]), + np.array([]), + np.linspace(0, 1, 5), + ]) + def test_degenerate_input(self, start, t): + if np.asarray(t).ndim > 1: + with pytest.raises(ValueError): + geometric_slerp(start=start, end=start, t=t) + else: + + shape = (t.size,) + start.shape + expected = np.full(shape, start) + + actual = geometric_slerp(start=start, end=start, t=t) + assert_allclose(actual, expected) + + # Check that degenerate and non-degenerate + # inputs yield the same size + non_degenerate = geometric_slerp(start=start, end=start[::-1], t=t) + assert actual.size == non_degenerate.size + + @pytest.mark.parametrize('k', np.logspace(-10, -1, 10)) + def test_numerical_stability_pi(self, k): + # geometric_slerp should have excellent numerical + # stability for angles approaching pi between + # the start and end points + angle = np.pi - k + ts = np.linspace(0, 1, 100) + P = np.array([1, 0, 0, 0]) + Q = np.array([np.cos(angle), np.sin(angle), 0, 0]) + # the test should only be enforced for cases where + # geometric_slerp determines that the input is actually + # on the unit sphere + with np.testing.suppress_warnings() as sup: + sup.filter(UserWarning) + result = geometric_slerp(P, Q, ts, 1e-18) + norms = np.linalg.norm(result, axis=1) + error = np.max(np.abs(norms - 1)) + assert error < 4e-15 + + @pytest.mark.parametrize('t', [ + [[0, 0.5]], + [[[[[[[[[0, 0.5]]]]]]]]], + ]) + def test_interpolation_param_ndim(self, t): + # regression test for gh-14465 + arr1 = np.array([0, 1]) + arr2 = np.array([1, 0]) + + with pytest.raises(ValueError): + geometric_slerp(start=arr1, + end=arr2, + t=t) + + with pytest.raises(ValueError): + geometric_slerp(start=arr1, + end=arr1, + t=t) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_spherical_voronoi.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_spherical_voronoi.py new file mode 100644 index 0000000000000000000000000000000000000000..621a961ebb6e495701763af4ded10b431a63a992 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/tests/test_spherical_voronoi.py @@ -0,0 +1,358 @@ +import numpy as np +import itertools +from numpy.testing import (assert_equal, + assert_almost_equal, + assert_array_equal, + assert_array_almost_equal) +import pytest +from pytest import raises as assert_raises +from scipy.spatial import SphericalVoronoi, distance +from scipy.optimize import linear_sum_assignment +from scipy.constants import golden as phi +from scipy.special import gamma + + +TOL = 1E-10 + + +def _generate_tetrahedron(): + return np.array([[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]]) + + +def _generate_cube(): + return np.array(list(itertools.product([-1, 1.], repeat=3))) + + +def _generate_octahedron(): + return np.array([[-1, 0, 0], [+1, 0, 0], [0, -1, 0], + [0, +1, 0], [0, 0, -1], [0, 0, +1]]) + + +def _generate_dodecahedron(): + + x1 = _generate_cube() + x2 = np.array([[0, -phi, -1 / phi], + [0, -phi, +1 / phi], + [0, +phi, -1 / phi], + [0, +phi, +1 / phi]]) + x3 = np.array([[-1 / phi, 0, -phi], + [+1 / phi, 0, -phi], + [-1 / phi, 0, +phi], + [+1 / phi, 0, +phi]]) + x4 = np.array([[-phi, -1 / phi, 0], + [-phi, +1 / phi, 0], + [+phi, -1 / phi, 0], + [+phi, +1 / phi, 0]]) + return np.concatenate((x1, x2, x3, x4)) + + +def _generate_icosahedron(): + x = np.array([[0, -1, -phi], + [0, -1, +phi], + [0, +1, -phi], + [0, +1, +phi]]) + return np.concatenate([np.roll(x, i, axis=1) for i in range(3)]) + + +def _generate_polytope(name): + polygons = ["triangle", "square", "pentagon", "hexagon", "heptagon", + "octagon", "nonagon", "decagon", "undecagon", "dodecagon"] + polyhedra = ["tetrahedron", "cube", "octahedron", "dodecahedron", + "icosahedron"] + if name not in polygons and name not in polyhedra: + raise ValueError("unrecognized polytope") + + if name in polygons: + n = polygons.index(name) + 3 + thetas = np.linspace(0, 2 * np.pi, n, endpoint=False) + p = np.vstack([np.cos(thetas), np.sin(thetas)]).T + elif name == "tetrahedron": + p = _generate_tetrahedron() + elif name == "cube": + p = _generate_cube() + elif name == "octahedron": + p = _generate_octahedron() + elif name == "dodecahedron": + p = _generate_dodecahedron() + elif name == "icosahedron": + p = _generate_icosahedron() + + return p / np.linalg.norm(p, axis=1, keepdims=True) + + +def _hypersphere_area(dim, radius): + # https://en.wikipedia.org/wiki/N-sphere#Closed_forms + return 2 * np.pi**(dim / 2) / gamma(dim / 2) * radius**(dim - 1) + + +def _sample_sphere(n, dim, seed=None): + # Sample points uniformly at random from the hypersphere + rng = np.random.RandomState(seed=seed) + points = rng.randn(n, dim) + points /= np.linalg.norm(points, axis=1, keepdims=True) + return points + + +class TestSphericalVoronoi: + + def setup_method(self): + self.points = np.array([ + [-0.78928481, -0.16341094, 0.59188373], + [-0.66839141, 0.73309634, 0.12578818], + [0.32535778, -0.92476944, -0.19734181], + [-0.90177102, -0.03785291, -0.43055335], + [0.71781344, 0.68428936, 0.12842096], + [-0.96064876, 0.23492353, -0.14820556], + [0.73181537, -0.22025898, -0.6449281], + [0.79979205, 0.54555747, 0.25039913]] + ) + + def test_constructor(self): + center = np.array([1, 2, 3]) + radius = 2 + s1 = SphericalVoronoi(self.points) + # user input checks in SphericalVoronoi now require + # the radius / center to match the generators so adjust + # accordingly here + s2 = SphericalVoronoi(self.points * radius, radius) + s3 = SphericalVoronoi(self.points + center, center=center) + s4 = SphericalVoronoi(self.points * radius + center, radius, center) + assert_array_equal(s1.center, np.array([0, 0, 0])) + assert_equal(s1.radius, 1) + assert_array_equal(s2.center, np.array([0, 0, 0])) + assert_equal(s2.radius, 2) + assert_array_equal(s3.center, center) + assert_equal(s3.radius, 1) + assert_array_equal(s4.center, center) + assert_equal(s4.radius, radius) + + # Test a non-sequence/-ndarray based array-like + s5 = SphericalVoronoi(memoryview(self.points)) # type: ignore[arg-type] + assert_array_equal(s5.center, np.array([0, 0, 0])) + assert_equal(s5.radius, 1) + + def test_vertices_regions_translation_invariance(self): + sv_origin = SphericalVoronoi(self.points) + center = np.array([1, 1, 1]) + sv_translated = SphericalVoronoi(self.points + center, center=center) + assert_equal(sv_origin.regions, sv_translated.regions) + assert_array_almost_equal(sv_origin.vertices + center, + sv_translated.vertices) + + def test_vertices_regions_scaling_invariance(self): + sv_unit = SphericalVoronoi(self.points) + sv_scaled = SphericalVoronoi(self.points * 2, 2) + assert_equal(sv_unit.regions, sv_scaled.regions) + assert_array_almost_equal(sv_unit.vertices * 2, + sv_scaled.vertices) + + def test_old_radius_api_error(self): + with pytest.raises(ValueError, match='`radius` is `None`. *'): + SphericalVoronoi(self.points, radius=None) + + def test_sort_vertices_of_regions(self): + sv = SphericalVoronoi(self.points) + unsorted_regions = sv.regions + sv.sort_vertices_of_regions() + assert_equal(sorted(sv.regions), sorted(unsorted_regions)) + + def test_sort_vertices_of_regions_flattened(self): + expected = sorted([[0, 6, 5, 2, 3], [2, 3, 10, 11, 8, 7], [0, 6, 4, 1], + [4, 8, 7, 5, 6], [9, 11, 10], [2, 7, 5], + [1, 4, 8, 11, 9], [0, 3, 10, 9, 1]]) + expected = list(itertools.chain(*sorted(expected))) # type: ignore + sv = SphericalVoronoi(self.points) + sv.sort_vertices_of_regions() + actual = list(itertools.chain(*sorted(sv.regions))) + assert_array_equal(actual, expected) + + def test_sort_vertices_of_regions_dimensionality(self): + points = np.array([[1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [0.5, 0.5, 0.5, 0.5]]) + with pytest.raises(TypeError, match="three-dimensional"): + sv = SphericalVoronoi(points) + sv.sort_vertices_of_regions() + + def test_num_vertices(self): + # for any n >= 3, a spherical Voronoi diagram has 2n - 4 + # vertices; this is a direct consequence of Euler's formula + # as explained by Dinis and Mamede (2010) Proceedings of the + # 2010 International Symposium on Voronoi Diagrams in Science + # and Engineering + sv = SphericalVoronoi(self.points) + expected = self.points.shape[0] * 2 - 4 + actual = sv.vertices.shape[0] + assert_equal(actual, expected) + + def test_voronoi_circles(self): + sv = SphericalVoronoi(self.points) + for vertex in sv.vertices: + distances = distance.cdist(sv.points, np.array([vertex])) + closest = np.array(sorted(distances)[0:3]) + assert_almost_equal(closest[0], closest[1], 7, str(vertex)) + assert_almost_equal(closest[0], closest[2], 7, str(vertex)) + + def test_duplicate_point_handling(self): + # an exception should be raised for degenerate generators + # related to Issue# 7046 + self.degenerate = np.concatenate((self.points, self.points)) + with assert_raises(ValueError): + SphericalVoronoi(self.degenerate) + + def test_incorrect_radius_handling(self): + # an exception should be raised if the radius provided + # cannot possibly match the input generators + with assert_raises(ValueError): + SphericalVoronoi(self.points, radius=0.98) + + def test_incorrect_center_handling(self): + # an exception should be raised if the center provided + # cannot possibly match the input generators + with assert_raises(ValueError): + SphericalVoronoi(self.points, center=[0.1, 0, 0]) + + @pytest.mark.parametrize("dim", range(2, 6)) + @pytest.mark.parametrize("shift", [False, True]) + def test_single_hemisphere_handling(self, dim, shift): + n = 10 + points = _sample_sphere(n, dim, seed=0) + points[:, 0] = np.abs(points[:, 0]) + center = (np.arange(dim) + 1) * shift + sv = SphericalVoronoi(points + center, center=center) + dots = np.einsum('ij,ij->i', sv.vertices - center, + sv.points[sv._simplices[:, 0]] - center) + circumradii = np.arccos(np.clip(dots, -1, 1)) + assert np.max(circumradii) > np.pi / 2 + + @pytest.mark.parametrize("n", [1, 2, 10]) + @pytest.mark.parametrize("dim", range(2, 6)) + @pytest.mark.parametrize("shift", [False, True]) + def test_rank_deficient(self, n, dim, shift): + center = (np.arange(dim) + 1) * shift + points = _sample_sphere(n, dim - 1, seed=0) + points = np.hstack([points, np.zeros((n, 1))]) + with pytest.raises(ValueError, match="Rank of input points"): + SphericalVoronoi(points + center, center=center) + + @pytest.mark.parametrize("dim", range(2, 6)) + def test_higher_dimensions(self, dim): + n = 100 + points = _sample_sphere(n, dim, seed=0) + sv = SphericalVoronoi(points) + assert sv.vertices.shape[1] == dim + assert len(sv.regions) == n + + # verify Euler characteristic + cell_counts = [] + simplices = np.sort(sv._simplices) + for i in range(1, dim + 1): + cells = [] + for indices in itertools.combinations(range(dim), i): + cells.append(simplices[:, list(indices)]) + cells = np.unique(np.concatenate(cells), axis=0) + cell_counts.append(len(cells)) + expected_euler = 1 + (-1)**(dim-1) + actual_euler = sum([(-1)**i * e for i, e in enumerate(cell_counts)]) + assert expected_euler == actual_euler + + @pytest.mark.parametrize("dim", range(2, 6)) + def test_cross_polytope_regions(self, dim): + # The hypercube is the dual of the cross-polytope, so the voronoi + # vertices of the cross-polytope lie on the points of the hypercube. + + # generate points of the cross-polytope + points = np.concatenate((-np.eye(dim), np.eye(dim))) + sv = SphericalVoronoi(points) + assert all([len(e) == 2**(dim - 1) for e in sv.regions]) + + # generate points of the hypercube + expected = np.vstack(list(itertools.product([-1, 1], repeat=dim))) + expected = expected.astype(np.float64) / np.sqrt(dim) + + # test that Voronoi vertices are correctly placed + dist = distance.cdist(sv.vertices, expected) + res = linear_sum_assignment(dist) + assert dist[res].sum() < TOL + + @pytest.mark.parametrize("dim", range(2, 6)) + def test_hypercube_regions(self, dim): + # The cross-polytope is the dual of the hypercube, so the voronoi + # vertices of the hypercube lie on the points of the cross-polytope. + + # generate points of the hypercube + points = np.vstack(list(itertools.product([-1, 1], repeat=dim))) + points = points.astype(np.float64) / np.sqrt(dim) + sv = SphericalVoronoi(points) + + # generate points of the cross-polytope + expected = np.concatenate((-np.eye(dim), np.eye(dim))) + + # test that Voronoi vertices are correctly placed + dist = distance.cdist(sv.vertices, expected) + res = linear_sum_assignment(dist) + assert dist[res].sum() < TOL + + @pytest.mark.parametrize("n", [10, 500]) + @pytest.mark.parametrize("dim", [2, 3]) + @pytest.mark.parametrize("radius", [0.5, 1, 2]) + @pytest.mark.parametrize("shift", [False, True]) + @pytest.mark.parametrize("single_hemisphere", [False, True]) + def test_area_reconstitution(self, n, dim, radius, shift, + single_hemisphere): + points = _sample_sphere(n, dim, seed=0) + + # move all points to one side of the sphere for single-hemisphere test + if single_hemisphere: + points[:, 0] = np.abs(points[:, 0]) + + center = (np.arange(dim) + 1) * shift + points = radius * points + center + + sv = SphericalVoronoi(points, radius=radius, center=center) + areas = sv.calculate_areas() + assert_almost_equal(areas.sum(), _hypersphere_area(dim, radius)) + + @pytest.mark.parametrize("poly", ["triangle", "dodecagon", + "tetrahedron", "cube", "octahedron", + "dodecahedron", "icosahedron"]) + def test_equal_area_reconstitution(self, poly): + points = _generate_polytope(poly) + n, dim = points.shape + sv = SphericalVoronoi(points) + areas = sv.calculate_areas() + assert_almost_equal(areas, _hypersphere_area(dim, 1) / n) + + def test_area_unsupported_dimension(self): + dim = 4 + points = np.concatenate((-np.eye(dim), np.eye(dim))) + sv = SphericalVoronoi(points) + with pytest.raises(TypeError, match="Only supported"): + sv.calculate_areas() + + @pytest.mark.parametrize("radius", [1, 1.]) + @pytest.mark.parametrize("center", [None, (1, 2, 3), (1., 2., 3.)]) + def test_attribute_types(self, radius, center): + points = radius * self.points + if center is not None: + points += center + + sv = SphericalVoronoi(points, radius=radius, center=center) + assert sv.points.dtype is np.dtype(np.float64) + assert sv.center.dtype is np.dtype(np.float64) + assert isinstance(sv.radius, float) + + def test_region_types(self): + # Tests that region integer type does not change + # See Issue #13412 + sv = SphericalVoronoi(self.points) + dtype = type(sv.regions[0][0]) + # also enforce nested list type per gh-19177 + for region in sv.regions: + assert isinstance(region, list) + sv.sort_vertices_of_regions() + assert type(sv.regions[0][0]) == dtype + sv.sort_vertices_of_regions() + assert type(sv.regions[0][0]) == dtype diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..abe4a32f20d1f8740910a16de9e67a53621bc3e3 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/__init__.py @@ -0,0 +1,29 @@ +""" +Spatial Transformations (:mod:`scipy.spatial.transform`) +======================================================== + +.. currentmodule:: scipy.spatial.transform + +This package implements various spatial transformations. For now, +only rotations are supported. + +Rotations in 3 dimensions +------------------------- +.. autosummary:: + :toctree: generated/ + + Rotation + Slerp + RotationSpline +""" +from ._rotation import Rotation, Slerp +from ._rotation_spline import RotationSpline + +# Deprecated namespaces, to be removed in v2.0.0 +from . import rotation + +__all__ = ['Rotation', 'Slerp', 'RotationSpline'] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e7665001535e2fd44c14fc2b0488e0e2eee3b542 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation.pyi @@ -0,0 +1,65 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +from collections.abc import Sequence +import numpy as np + +if TYPE_CHECKING: + import numpy.typing as npt + +_IntegerType = int | np.integer + + +class Rotation: + def __init__(self, quat: npt.ArrayLike, normalize: bool = ..., copy: bool = ...) -> None: ... + @property + def single(self) -> bool: ... + def __len__(self) -> int: ... + @classmethod + def from_quat(cls, quat: npt.ArrayLike) -> Rotation: ... + @classmethod + def from_matrix(cls, matrix: npt.ArrayLike) -> Rotation: ... + @classmethod + def from_rotvec(cls, rotvec: npt.ArrayLike, degrees: bool = ...) -> Rotation: ... + @classmethod + def from_euler(cls, seq: str, angles: float | npt.ArrayLike, degrees: bool = ...) -> Rotation: ... + @classmethod + def from_davenport(cls, axes: npt.ArrayLike, order: str, angles: float | npt.ArrayLike, degrees: bool = ...) -> Rotation: ... + @classmethod + def from_mrp(cls, mrp: npt.ArrayLike) -> Rotation: ... + def as_quat(self, canonical: bool) -> np.ndarray: ... + def as_matrix(self) -> np.ndarray: ... + def as_rotvec(self, degrees: bool = ...) -> np.ndarray: ... + def as_euler(self, seq: str, degrees: bool = ...) -> np.ndarray: ... + def as_davenport(self, axes: npt.ArrayLike, order: str, degrees: bool = ...) -> np.ndarray: ... + def as_mrp(self) -> np.ndarray: ... + @classmethod + def concatenate(cls, rotations: Sequence[Rotation]) -> Rotation: ... + def apply(self, vectors: npt.ArrayLike, inverse: bool = ...) -> np.ndarray: ... + def __mul__(self, other: Rotation) -> Rotation: ... + def __pow__(self, n: float, modulus: int | None) -> Rotation: ... + def inv(self) -> Rotation: ... + def magnitude(self) -> np.ndarray | float: ... + def approx_equal(self, other: Rotation, atol: float | None, degrees: bool = ...) -> np.ndarray | bool: ... + def mean(self, weights: npt.ArrayLike | None = ...) -> Rotation: ... + def reduce(self, left: Rotation | None = ..., right: Rotation | None = ..., + return_indices: bool = ...) -> Rotation | tuple[Rotation, np.ndarray, np.ndarray]: ... + @classmethod + def create_group(cls, group: str, axis: str = ...) -> Rotation: ... + def __getitem__(self, indexer: int | slice | npt.ArrayLike) -> Rotation: ... + @classmethod + def identity(cls, num: int | None = ...) -> Rotation: ... + @classmethod + def random(cls, num: int | None = ..., + random_state: _IntegerType | np.random.Generator | np.random.RandomState | None = ...) -> Rotation: ... + @classmethod + def align_vectors(cls, a: npt.ArrayLike, b: npt.ArrayLike, + weights: npt.ArrayLike | None = ..., + return_sensitivity: bool = ...) -> tuple[Rotation, float] | tuple[Rotation, float, np.ndarray]:... + +class Slerp: + times: np.ndarray + timedelta: np.ndarray + rotations: Rotation + rotvecs: np.ndarray + def __init__(self, times: npt.ArrayLike, rotations: Rotation) -> None: ... + def __call__(self, times: npt.ArrayLike) -> Rotation: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_groups.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_groups.py new file mode 100644 index 0000000000000000000000000000000000000000..870e9b9e2b44bff56b8228a70607e29f8173accc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_groups.py @@ -0,0 +1,140 @@ +import numpy as np +from scipy.constants import golden as phi + + +def icosahedral(cls): + g1 = tetrahedral(cls).as_quat() + a = 0.5 + b = 0.5 / phi + c = phi / 2 + g2 = np.array([[+a, +b, +c, 0], + [+a, +b, -c, 0], + [+a, +c, 0, +b], + [+a, +c, 0, -b], + [+a, -b, +c, 0], + [+a, -b, -c, 0], + [+a, -c, 0, +b], + [+a, -c, 0, -b], + [+a, 0, +b, +c], + [+a, 0, +b, -c], + [+a, 0, -b, +c], + [+a, 0, -b, -c], + [+b, +a, 0, +c], + [+b, +a, 0, -c], + [+b, +c, +a, 0], + [+b, +c, -a, 0], + [+b, -a, 0, +c], + [+b, -a, 0, -c], + [+b, -c, +a, 0], + [+b, -c, -a, 0], + [+b, 0, +c, +a], + [+b, 0, +c, -a], + [+b, 0, -c, +a], + [+b, 0, -c, -a], + [+c, +a, +b, 0], + [+c, +a, -b, 0], + [+c, +b, 0, +a], + [+c, +b, 0, -a], + [+c, -a, +b, 0], + [+c, -a, -b, 0], + [+c, -b, 0, +a], + [+c, -b, 0, -a], + [+c, 0, +a, +b], + [+c, 0, +a, -b], + [+c, 0, -a, +b], + [+c, 0, -a, -b], + [0, +a, +c, +b], + [0, +a, +c, -b], + [0, +a, -c, +b], + [0, +a, -c, -b], + [0, +b, +a, +c], + [0, +b, +a, -c], + [0, +b, -a, +c], + [0, +b, -a, -c], + [0, +c, +b, +a], + [0, +c, +b, -a], + [0, +c, -b, +a], + [0, +c, -b, -a]]) + return cls.from_quat(np.concatenate((g1, g2))) + + +def octahedral(cls): + g1 = tetrahedral(cls).as_quat() + c = np.sqrt(2) / 2 + g2 = np.array([[+c, 0, 0, +c], + [0, +c, 0, +c], + [0, 0, +c, +c], + [0, 0, -c, +c], + [0, -c, 0, +c], + [-c, 0, 0, +c], + [0, +c, +c, 0], + [0, -c, +c, 0], + [+c, 0, +c, 0], + [-c, 0, +c, 0], + [+c, +c, 0, 0], + [-c, +c, 0, 0]]) + return cls.from_quat(np.concatenate((g1, g2))) + + +def tetrahedral(cls): + g1 = np.eye(4) + c = 0.5 + g2 = np.array([[c, -c, -c, +c], + [c, -c, +c, +c], + [c, +c, -c, +c], + [c, +c, +c, +c], + [c, -c, -c, -c], + [c, -c, +c, -c], + [c, +c, -c, -c], + [c, +c, +c, -c]]) + return cls.from_quat(np.concatenate((g1, g2))) + + +def dicyclic(cls, n, axis=2): + g1 = cyclic(cls, n, axis).as_rotvec() + + thetas = np.linspace(0, np.pi, n, endpoint=False) + rv = np.pi * np.vstack([np.zeros(n), np.cos(thetas), np.sin(thetas)]).T + g2 = np.roll(rv, axis, axis=1) + return cls.from_rotvec(np.concatenate((g1, g2))) + + +def cyclic(cls, n, axis=2): + thetas = np.linspace(0, 2 * np.pi, n, endpoint=False) + rv = np.vstack([thetas, np.zeros(n), np.zeros(n)]).T + return cls.from_rotvec(np.roll(rv, axis, axis=1)) + + +def create_group(cls, group, axis='Z'): + if not isinstance(group, str): + raise ValueError("`group` argument must be a string") + + permitted_axes = ['x', 'y', 'z', 'X', 'Y', 'Z'] + if axis not in permitted_axes: + raise ValueError("`axis` must be one of " + ", ".join(permitted_axes)) + + if group in ['I', 'O', 'T']: + symbol = group + order = 1 + elif group[:1] in ['C', 'D'] and group[1:].isdigit(): + symbol = group[:1] + order = int(group[1:]) + else: + raise ValueError("`group` must be one of 'I', 'O', 'T', 'Dn', 'Cn'") + + if order < 1: + raise ValueError("Group order must be positive") + + axis = 'xyz'.index(axis.lower()) + if symbol == 'I': + return icosahedral(cls) + elif symbol == 'O': + return octahedral(cls) + elif symbol == 'T': + return tetrahedral(cls) + elif symbol == 'D': + return dicyclic(cls, order, axis=axis) + elif symbol == 'C': + return cyclic(cls, order, axis=axis) + else: + assert False diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_spline.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_spline.py new file mode 100644 index 0000000000000000000000000000000000000000..1c07129721e3652d018ffc3be485e6ed785f5564 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/_rotation_spline.py @@ -0,0 +1,460 @@ +import numpy as np +from scipy.linalg import solve_banded +from ._rotation import Rotation + + +def _create_skew_matrix(x): + """Create skew-symmetric matrices corresponding to vectors. + + Parameters + ---------- + x : ndarray, shape (n, 3) + Set of vectors. + + Returns + ------- + ndarray, shape (n, 3, 3) + """ + result = np.zeros((len(x), 3, 3)) + result[:, 0, 1] = -x[:, 2] + result[:, 0, 2] = x[:, 1] + result[:, 1, 0] = x[:, 2] + result[:, 1, 2] = -x[:, 0] + result[:, 2, 0] = -x[:, 1] + result[:, 2, 1] = x[:, 0] + return result + + +def _matrix_vector_product_of_stacks(A, b): + """Compute the product of stack of matrices and vectors.""" + return np.einsum("ijk,ik->ij", A, b) + + +def _angular_rate_to_rotvec_dot_matrix(rotvecs): + """Compute matrices to transform angular rates to rot. vector derivatives. + + The matrices depend on the current attitude represented as a rotation + vector. + + Parameters + ---------- + rotvecs : ndarray, shape (n, 3) + Set of rotation vectors. + + Returns + ------- + ndarray, shape (n, 3, 3) + """ + norm = np.linalg.norm(rotvecs, axis=1) + k = np.empty_like(norm) + + mask = norm > 1e-4 + nm = norm[mask] + k[mask] = (1 - 0.5 * nm / np.tan(0.5 * nm)) / nm**2 + mask = ~mask + nm = norm[mask] + k[mask] = 1/12 + 1/720 * nm**2 + + skew = _create_skew_matrix(rotvecs) + + result = np.empty((len(rotvecs), 3, 3)) + result[:] = np.identity(3) + result[:] += 0.5 * skew + result[:] += k[:, None, None] * np.matmul(skew, skew) + + return result + + +def _rotvec_dot_to_angular_rate_matrix(rotvecs): + """Compute matrices to transform rot. vector derivatives to angular rates. + + The matrices depend on the current attitude represented as a rotation + vector. + + Parameters + ---------- + rotvecs : ndarray, shape (n, 3) + Set of rotation vectors. + + Returns + ------- + ndarray, shape (n, 3, 3) + """ + norm = np.linalg.norm(rotvecs, axis=1) + k1 = np.empty_like(norm) + k2 = np.empty_like(norm) + + mask = norm > 1e-4 + nm = norm[mask] + k1[mask] = (1 - np.cos(nm)) / nm ** 2 + k2[mask] = (nm - np.sin(nm)) / nm ** 3 + + mask = ~mask + nm = norm[mask] + k1[mask] = 0.5 - nm ** 2 / 24 + k2[mask] = 1 / 6 - nm ** 2 / 120 + + skew = _create_skew_matrix(rotvecs) + + result = np.empty((len(rotvecs), 3, 3)) + result[:] = np.identity(3) + result[:] -= k1[:, None, None] * skew + result[:] += k2[:, None, None] * np.matmul(skew, skew) + + return result + + +def _angular_acceleration_nonlinear_term(rotvecs, rotvecs_dot): + """Compute the non-linear term in angular acceleration. + + The angular acceleration contains a quadratic term with respect to + the derivative of the rotation vector. This function computes that. + + Parameters + ---------- + rotvecs : ndarray, shape (n, 3) + Set of rotation vectors. + rotvecs_dot : ndarray, shape (n, 3) + Set of rotation vector derivatives. + + Returns + ------- + ndarray, shape (n, 3) + """ + norm = np.linalg.norm(rotvecs, axis=1) + dp = np.sum(rotvecs * rotvecs_dot, axis=1) + cp = np.cross(rotvecs, rotvecs_dot) + ccp = np.cross(rotvecs, cp) + dccp = np.cross(rotvecs_dot, cp) + + k1 = np.empty_like(norm) + k2 = np.empty_like(norm) + k3 = np.empty_like(norm) + + mask = norm > 1e-4 + nm = norm[mask] + k1[mask] = (-nm * np.sin(nm) - 2 * (np.cos(nm) - 1)) / nm ** 4 + k2[mask] = (-2 * nm + 3 * np.sin(nm) - nm * np.cos(nm)) / nm ** 5 + k3[mask] = (nm - np.sin(nm)) / nm ** 3 + + mask = ~mask + nm = norm[mask] + k1[mask] = 1/12 - nm ** 2 / 180 + k2[mask] = -1/60 + nm ** 2 / 12604 + k3[mask] = 1/6 - nm ** 2 / 120 + + dp = dp[:, None] + k1 = k1[:, None] + k2 = k2[:, None] + k3 = k3[:, None] + + return dp * (k1 * cp + k2 * ccp) + k3 * dccp + + +def _compute_angular_rate(rotvecs, rotvecs_dot): + """Compute angular rates given rotation vectors and its derivatives. + + Parameters + ---------- + rotvecs : ndarray, shape (n, 3) + Set of rotation vectors. + rotvecs_dot : ndarray, shape (n, 3) + Set of rotation vector derivatives. + + Returns + ------- + ndarray, shape (n, 3) + """ + return _matrix_vector_product_of_stacks( + _rotvec_dot_to_angular_rate_matrix(rotvecs), rotvecs_dot) + + +def _compute_angular_acceleration(rotvecs, rotvecs_dot, rotvecs_dot_dot): + """Compute angular acceleration given rotation vector and its derivatives. + + Parameters + ---------- + rotvecs : ndarray, shape (n, 3) + Set of rotation vectors. + rotvecs_dot : ndarray, shape (n, 3) + Set of rotation vector derivatives. + rotvecs_dot_dot : ndarray, shape (n, 3) + Set of rotation vector second derivatives. + + Returns + ------- + ndarray, shape (n, 3) + """ + return (_compute_angular_rate(rotvecs, rotvecs_dot_dot) + + _angular_acceleration_nonlinear_term(rotvecs, rotvecs_dot)) + + +def _create_block_3_diagonal_matrix(A, B, d): + """Create a 3-diagonal block matrix as banded. + + The matrix has the following structure: + + DB... + ADB.. + .ADB. + ..ADB + ...AD + + The blocks A, B and D are 3-by-3 matrices. The D matrices has the form + d * I. + + Parameters + ---------- + A : ndarray, shape (n, 3, 3) + Stack of A blocks. + B : ndarray, shape (n, 3, 3) + Stack of B blocks. + d : ndarray, shape (n + 1,) + Values for diagonal blocks. + + Returns + ------- + ndarray, shape (11, 3 * (n + 1)) + Matrix in the banded form as used by `scipy.linalg.solve_banded`. + """ + ind = np.arange(3) + ind_blocks = np.arange(len(A)) + + A_i = np.empty_like(A, dtype=int) + A_i[:] = ind[:, None] + A_i += 3 * (1 + ind_blocks[:, None, None]) + + A_j = np.empty_like(A, dtype=int) + A_j[:] = ind + A_j += 3 * ind_blocks[:, None, None] + + B_i = np.empty_like(B, dtype=int) + B_i[:] = ind[:, None] + B_i += 3 * ind_blocks[:, None, None] + + B_j = np.empty_like(B, dtype=int) + B_j[:] = ind + B_j += 3 * (1 + ind_blocks[:, None, None]) + + diag_i = diag_j = np.arange(3 * len(d)) + i = np.hstack((A_i.ravel(), B_i.ravel(), diag_i)) + j = np.hstack((A_j.ravel(), B_j.ravel(), diag_j)) + values = np.hstack((A.ravel(), B.ravel(), np.repeat(d, 3))) + + u = 5 + l = 5 + result = np.zeros((u + l + 1, 3 * len(d))) + result[u + i - j, j] = values + return result + + +class RotationSpline: + """Interpolate rotations with continuous angular rate and acceleration. + + The rotation vectors between each consecutive orientation are cubic + functions of time and it is guaranteed that angular rate and acceleration + are continuous. Such interpolation are analogous to cubic spline + interpolation. + + Refer to [1]_ for math and implementation details. + + Parameters + ---------- + times : array_like, shape (N,) + Times of the known rotations. At least 2 times must be specified. + rotations : `Rotation` instance + Rotations to perform the interpolation between. Must contain N + rotations. + + Methods + ------- + __call__ + + References + ---------- + .. [1] `Smooth Attitude Interpolation + `_ + + Examples + -------- + >>> from scipy.spatial.transform import Rotation, RotationSpline + >>> import numpy as np + + Define the sequence of times and rotations from the Euler angles: + + >>> times = [0, 10, 20, 40] + >>> angles = [[-10, 20, 30], [0, 15, 40], [-30, 45, 30], [20, 45, 90]] + >>> rotations = Rotation.from_euler('XYZ', angles, degrees=True) + + Create the interpolator object: + + >>> spline = RotationSpline(times, rotations) + + Interpolate the Euler angles, angular rate and acceleration: + + >>> angular_rate = np.rad2deg(spline(times, 1)) + >>> angular_acceleration = np.rad2deg(spline(times, 2)) + >>> times_plot = np.linspace(times[0], times[-1], 100) + >>> angles_plot = spline(times_plot).as_euler('XYZ', degrees=True) + >>> angular_rate_plot = np.rad2deg(spline(times_plot, 1)) + >>> angular_acceleration_plot = np.rad2deg(spline(times_plot, 2)) + + On this plot you see that Euler angles are continuous and smooth: + + >>> import matplotlib.pyplot as plt + >>> plt.plot(times_plot, angles_plot) + >>> plt.plot(times, angles, 'x') + >>> plt.title("Euler angles") + >>> plt.show() + + The angular rate is also smooth: + + >>> plt.plot(times_plot, angular_rate_plot) + >>> plt.plot(times, angular_rate, 'x') + >>> plt.title("Angular rate") + >>> plt.show() + + The angular acceleration is continuous, but not smooth. Also note that + the angular acceleration is not a piecewise-linear function, because + it is different from the second derivative of the rotation vector (which + is a piecewise-linear function as in the cubic spline). + + >>> plt.plot(times_plot, angular_acceleration_plot) + >>> plt.plot(times, angular_acceleration, 'x') + >>> plt.title("Angular acceleration") + >>> plt.show() + """ + # Parameters for the solver for angular rate. + MAX_ITER = 10 + TOL = 1e-9 + + def _solve_for_angular_rates(self, dt, angular_rates, rotvecs): + angular_rate_first = angular_rates[0].copy() + + A = _angular_rate_to_rotvec_dot_matrix(rotvecs) + A_inv = _rotvec_dot_to_angular_rate_matrix(rotvecs) + M = _create_block_3_diagonal_matrix( + 2 * A_inv[1:-1] / dt[1:-1, None, None], + 2 * A[1:-1] / dt[1:-1, None, None], + 4 * (1 / dt[:-1] + 1 / dt[1:])) + + b0 = 6 * (rotvecs[:-1] * dt[:-1, None] ** -2 + + rotvecs[1:] * dt[1:, None] ** -2) + b0[0] -= 2 / dt[0] * A_inv[0].dot(angular_rate_first) + b0[-1] -= 2 / dt[-1] * A[-1].dot(angular_rates[-1]) + + for iteration in range(self.MAX_ITER): + rotvecs_dot = _matrix_vector_product_of_stacks(A, angular_rates) + delta_beta = _angular_acceleration_nonlinear_term( + rotvecs[:-1], rotvecs_dot[:-1]) + b = b0 - delta_beta + angular_rates_new = solve_banded((5, 5), M, b.ravel()) + angular_rates_new = angular_rates_new.reshape((-1, 3)) + + delta = np.abs(angular_rates_new - angular_rates[:-1]) + angular_rates[:-1] = angular_rates_new + if np.all(delta < self.TOL * (1 + np.abs(angular_rates_new))): + break + + rotvecs_dot = _matrix_vector_product_of_stacks(A, angular_rates) + angular_rates = np.vstack((angular_rate_first, angular_rates[:-1])) + + return angular_rates, rotvecs_dot + + def __init__(self, times, rotations): + from scipy.interpolate import PPoly + + if rotations.single: + raise ValueError("`rotations` must be a sequence of rotations.") + + if len(rotations) == 1: + raise ValueError("`rotations` must contain at least 2 rotations.") + + times = np.asarray(times, dtype=float) + if times.ndim != 1: + raise ValueError("`times` must be 1-dimensional.") + + if len(times) != len(rotations): + raise ValueError("Expected number of rotations to be equal to " + "number of timestamps given, got {} rotations " + "and {} timestamps." + .format(len(rotations), len(times))) + + dt = np.diff(times) + if np.any(dt <= 0): + raise ValueError("Values in `times` must be in a strictly " + "increasing order.") + + rotvecs = (rotations[:-1].inv() * rotations[1:]).as_rotvec() + angular_rates = rotvecs / dt[:, None] + + if len(rotations) == 2: + rotvecs_dot = angular_rates + else: + angular_rates, rotvecs_dot = self._solve_for_angular_rates( + dt, angular_rates, rotvecs) + + dt = dt[:, None] + coeff = np.empty((4, len(times) - 1, 3)) + coeff[0] = (-2 * rotvecs + dt * angular_rates + + dt * rotvecs_dot) / dt ** 3 + coeff[1] = (3 * rotvecs - 2 * dt * angular_rates + - dt * rotvecs_dot) / dt ** 2 + coeff[2] = angular_rates + coeff[3] = 0 + + self.times = times + self.rotations = rotations + self.interpolator = PPoly(coeff, times) + + def __call__(self, times, order=0): + """Compute interpolated values. + + Parameters + ---------- + times : float or array_like + Times of interest. + order : {0, 1, 2}, optional + Order of differentiation: + + * 0 (default) : return Rotation + * 1 : return the angular rate in rad/sec + * 2 : return the angular acceleration in rad/sec/sec + + Returns + ------- + Interpolated Rotation, angular rate or acceleration. + """ + if order not in [0, 1, 2]: + raise ValueError("`order` must be 0, 1 or 2.") + + times = np.asarray(times, dtype=float) + if times.ndim > 1: + raise ValueError("`times` must be at most 1-dimensional.") + + singe_time = times.ndim == 0 + times = np.atleast_1d(times) + + rotvecs = self.interpolator(times) + if order == 0: + index = np.searchsorted(self.times, times, side='right') + index -= 1 + index[index < 0] = 0 + n_segments = len(self.times) - 1 + index[index > n_segments - 1] = n_segments - 1 + result = self.rotations[index] * Rotation.from_rotvec(rotvecs) + elif order == 1: + rotvecs_dot = self.interpolator(times, 1) + result = _compute_angular_rate(rotvecs, rotvecs_dot) + elif order == 2: + rotvecs_dot = self.interpolator(times, 1) + rotvecs_dot_dot = self.interpolator(times, 2) + result = _compute_angular_acceleration(rotvecs, rotvecs_dot, + rotvecs_dot_dot) + else: + assert False + + if singe_time: + result = result[0] + + return result diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/rotation.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/rotation.py new file mode 100644 index 0000000000000000000000000000000000000000..8aee10d960cffe9ce8aa7581ca633c6aad1d17a8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/rotation.py @@ -0,0 +1,24 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.spatial` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'Rotation', + 'Slerp', + 'check_random_state', + 'create_group', + 're', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="spatial.transform", module="rotation", + private_modules=["_rotation"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation.py new file mode 100644 index 0000000000000000000000000000000000000000..2b2356da4a8413ddbb1eae1fcfc9b052a5e8d5a4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation.py @@ -0,0 +1,1906 @@ +import pytest + +import numpy as np +from numpy.testing import assert_equal, assert_array_almost_equal +from numpy.testing import assert_allclose +from scipy.spatial.transform import Rotation, Slerp +from scipy.stats import special_ortho_group +from itertools import permutations + +import pickle +import copy + +def basis_vec(axis): + if axis == 'x': + return [1, 0, 0] + elif axis == 'y': + return [0, 1, 0] + elif axis == 'z': + return [0, 0, 1] + +def test_generic_quat_matrix(): + x = np.array([[3, 4, 0, 0], [5, 12, 0, 0]]) + r = Rotation.from_quat(x) + expected_quat = x / np.array([[5], [13]]) + assert_array_almost_equal(r.as_quat(), expected_quat) + + +def test_from_single_1d_quaternion(): + x = np.array([3, 4, 0, 0]) + r = Rotation.from_quat(x) + expected_quat = x / 5 + assert_array_almost_equal(r.as_quat(), expected_quat) + + +def test_from_single_2d_quaternion(): + x = np.array([[3, 4, 0, 0]]) + r = Rotation.from_quat(x) + expected_quat = x / 5 + assert_array_almost_equal(r.as_quat(), expected_quat) + + +def test_from_square_quat_matrix(): + # Ensure proper norm array broadcasting + x = np.array([ + [3, 0, 0, 4], + [5, 0, 12, 0], + [0, 0, 0, 1], + [-1, -1, -1, 1], + [0, 0, 0, -1], # Check double cover + [-1, -1, -1, -1] # Check double cover + ]) + r = Rotation.from_quat(x) + expected_quat = x / np.array([[5], [13], [1], [2], [1], [2]]) + assert_array_almost_equal(r.as_quat(), expected_quat) + + +def test_quat_double_to_canonical_single_cover(): + x = np.array([ + [-1, 0, 0, 0], + [0, -1, 0, 0], + [0, 0, -1, 0], + [0, 0, 0, -1], + [-1, -1, -1, -1] + ]) + r = Rotation.from_quat(x) + expected_quat = np.abs(x) / np.linalg.norm(x, axis=1)[:, None] + assert_allclose(r.as_quat(canonical=True), expected_quat) + + +def test_quat_double_cover(): + # See the Rotation.from_quat() docstring for scope of the quaternion + # double cover property. + # Check from_quat and as_quat(canonical=False) + q = np.array([0, 0, 0, -1]) + r = Rotation.from_quat(q) + assert_equal(q, r.as_quat(canonical=False)) + + # Check composition and inverse + q = np.array([1, 0, 0, 1])/np.sqrt(2) # 90 deg rotation about x + r = Rotation.from_quat(q) + r3 = r*r*r + assert_allclose(r.as_quat(canonical=False)*np.sqrt(2), + [1, 0, 0, 1]) + assert_allclose(r.inv().as_quat(canonical=False)*np.sqrt(2), + [-1, 0, 0, 1]) + assert_allclose(r3.as_quat(canonical=False)*np.sqrt(2), + [1, 0, 0, -1]) + assert_allclose(r3.inv().as_quat(canonical=False)*np.sqrt(2), + [-1, 0, 0, -1]) + + # More sanity checks + assert_allclose((r*r.inv()).as_quat(canonical=False), + [0, 0, 0, 1], atol=2e-16) + assert_allclose((r3*r3.inv()).as_quat(canonical=False), + [0, 0, 0, 1], atol=2e-16) + assert_allclose((r*r3).as_quat(canonical=False), + [0, 0, 0, -1], atol=2e-16) + assert_allclose((r.inv()*r3.inv()).as_quat(canonical=False), + [0, 0, 0, -1], atol=2e-16) + + +def test_malformed_1d_from_quat(): + with pytest.raises(ValueError): + Rotation.from_quat(np.array([1, 2, 3])) + + +def test_malformed_2d_from_quat(): + with pytest.raises(ValueError): + Rotation.from_quat(np.array([ + [1, 2, 3, 4, 5], + [4, 5, 6, 7, 8] + ])) + + +def test_zero_norms_from_quat(): + x = np.array([ + [3, 4, 0, 0], + [0, 0, 0, 0], + [5, 0, 12, 0] + ]) + with pytest.raises(ValueError): + Rotation.from_quat(x) + + +def test_as_matrix_single_1d_quaternion(): + quat = [0, 0, 0, 1] + mat = Rotation.from_quat(quat).as_matrix() + # mat.shape == (3,3) due to 1d input + assert_array_almost_equal(mat, np.eye(3)) + + +def test_as_matrix_single_2d_quaternion(): + quat = [[0, 0, 1, 1]] + mat = Rotation.from_quat(quat).as_matrix() + assert_equal(mat.shape, (1, 3, 3)) + expected_mat = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + assert_array_almost_equal(mat[0], expected_mat) + + +def test_as_matrix_from_square_input(): + quats = [ + [0, 0, 1, 1], + [0, 1, 0, 1], + [0, 0, 0, 1], + [0, 0, 0, -1] + ] + mat = Rotation.from_quat(quats).as_matrix() + assert_equal(mat.shape, (4, 3, 3)) + + expected0 = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + assert_array_almost_equal(mat[0], expected0) + + expected1 = np.array([ + [0, 0, 1], + [0, 1, 0], + [-1, 0, 0] + ]) + assert_array_almost_equal(mat[1], expected1) + + assert_array_almost_equal(mat[2], np.eye(3)) + assert_array_almost_equal(mat[3], np.eye(3)) + + +def test_as_matrix_from_generic_input(): + quats = [ + [0, 0, 1, 1], + [0, 1, 0, 1], + [1, 2, 3, 4] + ] + mat = Rotation.from_quat(quats).as_matrix() + assert_equal(mat.shape, (3, 3, 3)) + + expected0 = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + assert_array_almost_equal(mat[0], expected0) + + expected1 = np.array([ + [0, 0, 1], + [0, 1, 0], + [-1, 0, 0] + ]) + assert_array_almost_equal(mat[1], expected1) + + expected2 = np.array([ + [0.4, -2, 2.2], + [2.8, 1, 0.4], + [-1, 2, 2] + ]) / 3 + assert_array_almost_equal(mat[2], expected2) + + +def test_from_single_2d_matrix(): + mat = [ + [0, 0, 1], + [1, 0, 0], + [0, 1, 0] + ] + expected_quat = [0.5, 0.5, 0.5, 0.5] + assert_array_almost_equal( + Rotation.from_matrix(mat).as_quat(), + expected_quat) + + +def test_from_single_3d_matrix(): + mat = np.array([ + [0, 0, 1], + [1, 0, 0], + [0, 1, 0] + ]).reshape((1, 3, 3)) + expected_quat = np.array([0.5, 0.5, 0.5, 0.5]).reshape((1, 4)) + assert_array_almost_equal( + Rotation.from_matrix(mat).as_quat(), + expected_quat) + + +def test_from_matrix_calculation(): + expected_quat = np.array([1, 1, 6, 1]) / np.sqrt(39) + mat = np.array([ + [-0.8974359, -0.2564103, 0.3589744], + [0.3589744, -0.8974359, 0.2564103], + [0.2564103, 0.3589744, 0.8974359] + ]) + assert_array_almost_equal( + Rotation.from_matrix(mat).as_quat(), + expected_quat) + assert_array_almost_equal( + Rotation.from_matrix(mat.reshape((1, 3, 3))).as_quat(), + expected_quat.reshape((1, 4))) + + +def test_matrix_calculation_pipeline(): + mat = special_ortho_group.rvs(3, size=10, random_state=0) + assert_array_almost_equal(Rotation.from_matrix(mat).as_matrix(), mat) + + +def test_from_matrix_ortho_output(): + rnd = np.random.RandomState(0) + mat = rnd.random_sample((100, 3, 3)) + ortho_mat = Rotation.from_matrix(mat).as_matrix() + + mult_result = np.einsum('...ij,...jk->...ik', ortho_mat, + ortho_mat.transpose((0, 2, 1))) + + eye3d = np.zeros((100, 3, 3)) + for i in range(3): + eye3d[:, i, i] = 1.0 + + assert_array_almost_equal(mult_result, eye3d) + + +def test_from_1d_single_rotvec(): + rotvec = [1, 0, 0] + expected_quat = np.array([0.4794255, 0, 0, 0.8775826]) + result = Rotation.from_rotvec(rotvec) + assert_array_almost_equal(result.as_quat(), expected_quat) + + +def test_from_2d_single_rotvec(): + rotvec = [[1, 0, 0]] + expected_quat = np.array([[0.4794255, 0, 0, 0.8775826]]) + result = Rotation.from_rotvec(rotvec) + assert_array_almost_equal(result.as_quat(), expected_quat) + + +def test_from_generic_rotvec(): + rotvec = [ + [1, 2, 2], + [1, -1, 0.5], + [0, 0, 0] + ] + expected_quat = np.array([ + [0.3324983, 0.6649967, 0.6649967, 0.0707372], + [0.4544258, -0.4544258, 0.2272129, 0.7316889], + [0, 0, 0, 1] + ]) + assert_array_almost_equal( + Rotation.from_rotvec(rotvec).as_quat(), + expected_quat) + + +def test_from_rotvec_small_angle(): + rotvec = np.array([ + [5e-4 / np.sqrt(3), -5e-4 / np.sqrt(3), 5e-4 / np.sqrt(3)], + [0.2, 0.3, 0.4], + [0, 0, 0] + ]) + + quat = Rotation.from_rotvec(rotvec).as_quat() + # cos(theta/2) ~~ 1 for small theta + assert_allclose(quat[0, 3], 1) + # sin(theta/2) / theta ~~ 0.5 for small theta + assert_allclose(quat[0, :3], rotvec[0] * 0.5) + + assert_allclose(quat[1, 3], 0.9639685) + assert_allclose( + quat[1, :3], + np.array([ + 0.09879603932153465, + 0.14819405898230198, + 0.19759207864306931 + ])) + + assert_equal(quat[2], np.array([0, 0, 0, 1])) + + +def test_degrees_from_rotvec(): + rotvec1 = [1.0 / np.cbrt(3), 1.0 / np.cbrt(3), 1.0 / np.cbrt(3)] + rot1 = Rotation.from_rotvec(rotvec1, degrees=True) + quat1 = rot1.as_quat() + + rotvec2 = np.deg2rad(rotvec1) + rot2 = Rotation.from_rotvec(rotvec2) + quat2 = rot2.as_quat() + + assert_allclose(quat1, quat2) + + +def test_malformed_1d_from_rotvec(): + with pytest.raises(ValueError, match='Expected `rot_vec` to have shape'): + Rotation.from_rotvec([1, 2]) + + +def test_malformed_2d_from_rotvec(): + with pytest.raises(ValueError, match='Expected `rot_vec` to have shape'): + Rotation.from_rotvec([ + [1, 2, 3, 4], + [5, 6, 7, 8] + ]) + + +def test_as_generic_rotvec(): + quat = np.array([ + [1, 2, -1, 0.5], + [1, -1, 1, 0.0003], + [0, 0, 0, 1] + ]) + quat /= np.linalg.norm(quat, axis=1)[:, None] + + rotvec = Rotation.from_quat(quat).as_rotvec() + angle = np.linalg.norm(rotvec, axis=1) + + assert_allclose(quat[:, 3], np.cos(angle/2)) + assert_allclose(np.cross(rotvec, quat[:, :3]), np.zeros((3, 3))) + + +def test_as_rotvec_single_1d_input(): + quat = np.array([1, 2, -3, 2]) + expected_rotvec = np.array([0.5772381, 1.1544763, -1.7317144]) + + actual_rotvec = Rotation.from_quat(quat).as_rotvec() + + assert_equal(actual_rotvec.shape, (3,)) + assert_allclose(actual_rotvec, expected_rotvec) + + +def test_as_rotvec_single_2d_input(): + quat = np.array([[1, 2, -3, 2]]) + expected_rotvec = np.array([[0.5772381, 1.1544763, -1.7317144]]) + + actual_rotvec = Rotation.from_quat(quat).as_rotvec() + + assert_equal(actual_rotvec.shape, (1, 3)) + assert_allclose(actual_rotvec, expected_rotvec) + + +def test_as_rotvec_degrees(): + # x->y, y->z, z->x + mat = [[0, 0, 1], [1, 0, 0], [0, 1, 0]] + rot = Rotation.from_matrix(mat) + rotvec = rot.as_rotvec(degrees=True) + angle = np.linalg.norm(rotvec) + assert_allclose(angle, 120.0) + assert_allclose(rotvec[0], rotvec[1]) + assert_allclose(rotvec[1], rotvec[2]) + + +def test_rotvec_calc_pipeline(): + # Include small angles + rotvec = np.array([ + [0, 0, 0], + [1, -1, 2], + [-3e-4, 3.5e-4, 7.5e-5] + ]) + assert_allclose(Rotation.from_rotvec(rotvec).as_rotvec(), rotvec) + assert_allclose(Rotation.from_rotvec(rotvec, degrees=True).as_rotvec(degrees=True), + rotvec) + + +def test_from_1d_single_mrp(): + mrp = [0, 0, 1.0] + expected_quat = np.array([0, 0, 1, 0]) + result = Rotation.from_mrp(mrp) + assert_array_almost_equal(result.as_quat(), expected_quat) + + +def test_from_2d_single_mrp(): + mrp = [[0, 0, 1.0]] + expected_quat = np.array([[0, 0, 1, 0]]) + result = Rotation.from_mrp(mrp) + assert_array_almost_equal(result.as_quat(), expected_quat) + + +def test_from_generic_mrp(): + mrp = np.array([ + [1, 2, 2], + [1, -1, 0.5], + [0, 0, 0]]) + expected_quat = np.array([ + [0.2, 0.4, 0.4, -0.8], + [0.61538462, -0.61538462, 0.30769231, -0.38461538], + [0, 0, 0, 1]]) + assert_array_almost_equal(Rotation.from_mrp(mrp).as_quat(), expected_quat) + + +def test_malformed_1d_from_mrp(): + with pytest.raises(ValueError, match='Expected `mrp` to have shape'): + Rotation.from_mrp([1, 2]) + + +def test_malformed_2d_from_mrp(): + with pytest.raises(ValueError, match='Expected `mrp` to have shape'): + Rotation.from_mrp([ + [1, 2, 3, 4], + [5, 6, 7, 8] + ]) + + +def test_as_generic_mrp(): + quat = np.array([ + [1, 2, -1, 0.5], + [1, -1, 1, 0.0003], + [0, 0, 0, 1]]) + quat /= np.linalg.norm(quat, axis=1)[:, None] + + expected_mrp = np.array([ + [0.33333333, 0.66666667, -0.33333333], + [0.57725028, -0.57725028, 0.57725028], + [0, 0, 0]]) + assert_array_almost_equal(Rotation.from_quat(quat).as_mrp(), expected_mrp) + +def test_past_180_degree_rotation(): + # ensure that a > 180 degree rotation is returned as a <180 rotation in MRPs + # in this case 270 should be returned as -90 + expected_mrp = np.array([-np.tan(np.pi/2/4), 0.0, 0]) + assert_array_almost_equal( + Rotation.from_euler('xyz', [270, 0, 0], degrees=True).as_mrp(), + expected_mrp + ) + + +def test_as_mrp_single_1d_input(): + quat = np.array([1, 2, -3, 2]) + expected_mrp = np.array([0.16018862, 0.32037724, -0.48056586]) + + actual_mrp = Rotation.from_quat(quat).as_mrp() + + assert_equal(actual_mrp.shape, (3,)) + assert_allclose(actual_mrp, expected_mrp) + + +def test_as_mrp_single_2d_input(): + quat = np.array([[1, 2, -3, 2]]) + expected_mrp = np.array([[0.16018862, 0.32037724, -0.48056586]]) + + actual_mrp = Rotation.from_quat(quat).as_mrp() + + assert_equal(actual_mrp.shape, (1, 3)) + assert_allclose(actual_mrp, expected_mrp) + + +def test_mrp_calc_pipeline(): + actual_mrp = np.array([ + [0, 0, 0], + [1, -1, 2], + [0.41421356, 0, 0], + [0.1, 0.2, 0.1]]) + expected_mrp = np.array([ + [0, 0, 0], + [-0.16666667, 0.16666667, -0.33333333], + [0.41421356, 0, 0], + [0.1, 0.2, 0.1]]) + assert_allclose(Rotation.from_mrp(actual_mrp).as_mrp(), expected_mrp) + + +def test_from_euler_single_rotation(): + quat = Rotation.from_euler('z', 90, degrees=True).as_quat() + expected_quat = np.array([0, 0, 1, 1]) / np.sqrt(2) + assert_allclose(quat, expected_quat) + + +def test_single_intrinsic_extrinsic_rotation(): + extrinsic = Rotation.from_euler('z', 90, degrees=True).as_matrix() + intrinsic = Rotation.from_euler('Z', 90, degrees=True).as_matrix() + assert_allclose(extrinsic, intrinsic) + + +def test_from_euler_rotation_order(): + # Intrinsic rotation is same as extrinsic with order reversed + rnd = np.random.RandomState(0) + a = rnd.randint(low=0, high=180, size=(6, 3)) + b = a[:, ::-1] + x = Rotation.from_euler('xyz', a, degrees=True).as_quat() + y = Rotation.from_euler('ZYX', b, degrees=True).as_quat() + assert_allclose(x, y) + + +def test_from_euler_elementary_extrinsic_rotation(): + # Simple test to check if extrinsic rotations are implemented correctly + mat = Rotation.from_euler('zx', [90, 90], degrees=True).as_matrix() + expected_mat = np.array([ + [0, -1, 0], + [0, 0, -1], + [1, 0, 0] + ]) + assert_array_almost_equal(mat, expected_mat) + + +def test_from_euler_intrinsic_rotation_312(): + angles = [ + [30, 60, 45], + [30, 60, 30], + [45, 30, 60] + ] + mat = Rotation.from_euler('ZXY', angles, degrees=True).as_matrix() + + assert_array_almost_equal(mat[0], np.array([ + [0.3061862, -0.2500000, 0.9185587], + [0.8838835, 0.4330127, -0.1767767], + [-0.3535534, 0.8660254, 0.3535534] + ])) + + assert_array_almost_equal(mat[1], np.array([ + [0.5334936, -0.2500000, 0.8080127], + [0.8080127, 0.4330127, -0.3995191], + [-0.2500000, 0.8660254, 0.4330127] + ])) + + assert_array_almost_equal(mat[2], np.array([ + [0.0473672, -0.6123725, 0.7891491], + [0.6597396, 0.6123725, 0.4355958], + [-0.7500000, 0.5000000, 0.4330127] + ])) + + +def test_from_euler_intrinsic_rotation_313(): + angles = [ + [30, 60, 45], + [30, 60, 30], + [45, 30, 60] + ] + mat = Rotation.from_euler('ZXZ', angles, degrees=True).as_matrix() + + assert_array_almost_equal(mat[0], np.array([ + [0.43559574, -0.78914913, 0.4330127], + [0.65973961, -0.04736717, -0.750000], + [0.61237244, 0.61237244, 0.500000] + ])) + + assert_array_almost_equal(mat[1], np.array([ + [0.6250000, -0.64951905, 0.4330127], + [0.64951905, 0.1250000, -0.750000], + [0.4330127, 0.750000, 0.500000] + ])) + + assert_array_almost_equal(mat[2], np.array([ + [-0.1767767, -0.91855865, 0.35355339], + [0.88388348, -0.30618622, -0.35355339], + [0.4330127, 0.25000000, 0.8660254] + ])) + + +def test_from_euler_extrinsic_rotation_312(): + angles = [ + [30, 60, 45], + [30, 60, 30], + [45, 30, 60] + ] + mat = Rotation.from_euler('zxy', angles, degrees=True).as_matrix() + + assert_array_almost_equal(mat[0], np.array([ + [0.91855865, 0.1767767, 0.35355339], + [0.25000000, 0.4330127, -0.8660254], + [-0.30618622, 0.88388348, 0.35355339] + ])) + + assert_array_almost_equal(mat[1], np.array([ + [0.96650635, -0.0580127, 0.2500000], + [0.25000000, 0.4330127, -0.8660254], + [-0.0580127, 0.89951905, 0.4330127] + ])) + + assert_array_almost_equal(mat[2], np.array([ + [0.65973961, -0.04736717, 0.7500000], + [0.61237244, 0.61237244, -0.5000000], + [-0.43559574, 0.78914913, 0.4330127] + ])) + + +def test_from_euler_extrinsic_rotation_313(): + angles = [ + [30, 60, 45], + [30, 60, 30], + [45, 30, 60] + ] + mat = Rotation.from_euler('zxz', angles, degrees=True).as_matrix() + + assert_array_almost_equal(mat[0], np.array([ + [0.43559574, -0.65973961, 0.61237244], + [0.78914913, -0.04736717, -0.61237244], + [0.4330127, 0.75000000, 0.500000] + ])) + + assert_array_almost_equal(mat[1], np.array([ + [0.62500000, -0.64951905, 0.4330127], + [0.64951905, 0.12500000, -0.750000], + [0.4330127, 0.75000000, 0.500000] + ])) + + assert_array_almost_equal(mat[2], np.array([ + [-0.1767767, -0.88388348, 0.4330127], + [0.91855865, -0.30618622, -0.250000], + [0.35355339, 0.35355339, 0.8660254] + ])) + + +@pytest.mark.parametrize("seq_tuple", permutations("xyz")) +@pytest.mark.parametrize("intrinsic", (False, True)) +def test_as_euler_asymmetric_axes(seq_tuple, intrinsic): + # helper function for mean error tests + def test_stats(error, mean_max, rms_max): + mean = np.mean(error, axis=0) + std = np.std(error, axis=0) + rms = np.hypot(mean, std) + assert np.all(np.abs(mean) < mean_max) + assert np.all(rms < rms_max) + + rnd = np.random.RandomState(0) + n = 1000 + angles = np.empty((n, 3)) + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles[:, 1] = rnd.uniform(low=-np.pi / 2, high=np.pi / 2, size=(n,)) + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + + seq = "".join(seq_tuple) + if intrinsic: + # Extrinsic rotation (wrt to global world) at lower case + # intrinsinc (WRT the object itself) lower case. + seq = seq.upper() + rotation = Rotation.from_euler(seq, angles) + angles_quat = rotation.as_euler(seq) + angles_mat = rotation._as_euler_from_matrix(seq) + assert_allclose(angles, angles_quat, atol=0, rtol=1e-12) + assert_allclose(angles, angles_mat, atol=0, rtol=1e-12) + test_stats(angles_quat - angles, 1e-15, 1e-14) + test_stats(angles_mat - angles, 1e-15, 1e-14) + + + +@pytest.mark.parametrize("seq_tuple", permutations("xyz")) +@pytest.mark.parametrize("intrinsic", (False, True)) +def test_as_euler_symmetric_axes(seq_tuple, intrinsic): + # helper function for mean error tests + def test_stats(error, mean_max, rms_max): + mean = np.mean(error, axis=0) + std = np.std(error, axis=0) + rms = np.hypot(mean, std) + assert np.all(np.abs(mean) < mean_max) + assert np.all(rms < rms_max) + + rnd = np.random.RandomState(0) + n = 1000 + angles = np.empty((n, 3)) + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,)) + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + + # Rotation of the form A/B/A are rotation around symmetric axes + seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]]) + if intrinsic: + seq = seq.upper() + rotation = Rotation.from_euler(seq, angles) + angles_quat = rotation.as_euler(seq) + angles_mat = rotation._as_euler_from_matrix(seq) + assert_allclose(angles, angles_quat, atol=0, rtol=1e-13) + assert_allclose(angles, angles_mat, atol=0, rtol=1e-9) + test_stats(angles_quat - angles, 1e-16, 1e-14) + test_stats(angles_mat - angles, 1e-15, 1e-13) + + +@pytest.mark.parametrize("seq_tuple", permutations("xyz")) +@pytest.mark.parametrize("intrinsic", (False, True)) +def test_as_euler_degenerate_asymmetric_axes(seq_tuple, intrinsic): + # Since we cannot check for angle equality, we check for rotation matrix + # equality + angles = np.array([ + [45, 90, 35], + [35, -90, 20], + [35, 90, 25], + [25, -90, 15]]) + + seq = "".join(seq_tuple) + if intrinsic: + # Extrinsic rotation (wrt to global world) at lower case + # Intrinsic (WRT the object itself) upper case. + seq = seq.upper() + rotation = Rotation.from_euler(seq, angles, degrees=True) + mat_expected = rotation.as_matrix() + + with pytest.warns(UserWarning, match="Gimbal lock"): + angle_estimates = rotation.as_euler(seq, degrees=True) + mat_estimated = Rotation.from_euler(seq, angle_estimates, degrees=True).as_matrix() + + assert_array_almost_equal(mat_expected, mat_estimated) + + +@pytest.mark.parametrize("seq_tuple", permutations("xyz")) +@pytest.mark.parametrize("intrinsic", (False, True)) +def test_as_euler_degenerate_symmetric_axes(seq_tuple, intrinsic): + # Since we cannot check for angle equality, we check for rotation matrix + # equality + angles = np.array([ + [15, 0, 60], + [35, 0, 75], + [60, 180, 35], + [15, -180, 25]]) + + # Rotation of the form A/B/A are rotation around symmetric axes + seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]]) + if intrinsic: + # Extrinsic rotation (wrt to global world) at lower case + # Intrinsic (WRT the object itself) upper case. + seq = seq.upper() + rotation = Rotation.from_euler(seq, angles, degrees=True) + mat_expected = rotation.as_matrix() + + with pytest.warns(UserWarning, match="Gimbal lock"): + angle_estimates = rotation.as_euler(seq, degrees=True) + mat_estimated = Rotation.from_euler(seq, angle_estimates, degrees=True).as_matrix() + + assert_array_almost_equal(mat_expected, mat_estimated) + + +@pytest.mark.parametrize("seq_tuple", permutations("xyz")) +@pytest.mark.parametrize("intrinsic", (False, True)) +def test_as_euler_degenerate_compare_algorithms(seq_tuple, intrinsic): + # this test makes sure that both algorithms are doing the same choices + # in degenerate cases + + # asymmetric axes + angles = np.array([ + [45, 90, 35], + [35, -90, 20], + [35, 90, 25], + [25, -90, 15]]) + + seq = "".join(seq_tuple) + if intrinsic: + # Extrinsic rotation (wrt to global world at lower case + # Intrinsic (WRT the object itself) upper case. + seq = seq.upper() + + rot = Rotation.from_euler(seq, angles, degrees=True) + with pytest.warns(UserWarning, match="Gimbal lock"): + estimates_matrix = rot._as_euler_from_matrix(seq, degrees=True) + with pytest.warns(UserWarning, match="Gimbal lock"): + estimates_quat = rot.as_euler(seq, degrees=True) + assert_allclose( + estimates_matrix[:, [0, 2]], estimates_quat[:, [0, 2]], atol=0, rtol=1e-12 + ) + assert_allclose(estimates_matrix[:, 1], estimates_quat[:, 1], atol=0, rtol=1e-7) + + # symmetric axes + # Absolute error tolerance must be looser to directly compare the results + # from both algorithms, because of numerical loss of precision for the + # method _as_euler_from_matrix near a zero angle value + + angles = np.array([ + [15, 0, 60], + [35, 0, 75], + [60, 180, 35], + [15, -180, 25]]) + + idx = angles[:, 1] == 0 # find problematic angles indices + + # Rotation of the form A/B/A are rotation around symmetric axes + seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]]) + if intrinsic: + # Extrinsinc rotation (wrt to global world) at lower case + # Intrinsic (WRT the object itself) upper case. + seq = seq.upper() + + rot = Rotation.from_euler(seq, angles, degrees=True) + with pytest.warns(UserWarning, match="Gimbal lock"): + estimates_matrix = rot._as_euler_from_matrix(seq, degrees=True) + with pytest.warns(UserWarning, match="Gimbal lock"): + estimates_quat = rot.as_euler(seq, degrees=True) + assert_allclose( + estimates_matrix[:, [0, 2]], estimates_quat[:, [0, 2]], atol=0, rtol=1e-12 + ) + + assert_allclose( + estimates_matrix[~idx, 1], estimates_quat[~idx, 1], atol=0, rtol=1e-7 + ) + + assert_allclose( + estimates_matrix[idx, 1], estimates_quat[idx, 1], atol=1e-6 + ) # problematic, angles[1] = 0 + + +def test_inv(): + rnd = np.random.RandomState(0) + n = 10 + p = Rotation.random(num=n, random_state=rnd) + q = p.inv() + + p_mat = p.as_matrix() + q_mat = q.as_matrix() + result1 = np.einsum('...ij,...jk->...ik', p_mat, q_mat) + result2 = np.einsum('...ij,...jk->...ik', q_mat, p_mat) + + eye3d = np.empty((n, 3, 3)) + eye3d[:] = np.eye(3) + + assert_array_almost_equal(result1, eye3d) + assert_array_almost_equal(result2, eye3d) + + +def test_inv_single_rotation(): + rnd = np.random.RandomState(0) + p = Rotation.random(random_state=rnd) + q = p.inv() + + p_mat = p.as_matrix() + q_mat = q.as_matrix() + res1 = np.dot(p_mat, q_mat) + res2 = np.dot(q_mat, p_mat) + + eye = np.eye(3) + + assert_array_almost_equal(res1, eye) + assert_array_almost_equal(res2, eye) + + x = Rotation.random(num=1, random_state=rnd) + y = x.inv() + + x_matrix = x.as_matrix() + y_matrix = y.as_matrix() + result1 = np.einsum('...ij,...jk->...ik', x_matrix, y_matrix) + result2 = np.einsum('...ij,...jk->...ik', y_matrix, x_matrix) + + eye3d = np.empty((1, 3, 3)) + eye3d[:] = np.eye(3) + + assert_array_almost_equal(result1, eye3d) + assert_array_almost_equal(result2, eye3d) + + +def test_identity_magnitude(): + n = 10 + assert_allclose(Rotation.identity(n).magnitude(), 0) + assert_allclose(Rotation.identity(n).inv().magnitude(), 0) + + +def test_single_identity_magnitude(): + assert Rotation.identity().magnitude() == 0 + assert Rotation.identity().inv().magnitude() == 0 + + +def test_identity_invariance(): + n = 10 + p = Rotation.random(n, random_state=0) + + result = p * Rotation.identity(n) + assert_array_almost_equal(p.as_quat(), result.as_quat()) + + result = result * p.inv() + assert_array_almost_equal(result.magnitude(), np.zeros(n)) + + +def test_single_identity_invariance(): + n = 10 + p = Rotation.random(n, random_state=0) + + result = p * Rotation.identity() + assert_array_almost_equal(p.as_quat(), result.as_quat()) + + result = result * p.inv() + assert_array_almost_equal(result.magnitude(), np.zeros(n)) + + +def test_magnitude(): + r = Rotation.from_quat(np.eye(4)) + result = r.magnitude() + assert_array_almost_equal(result, [np.pi, np.pi, np.pi, 0]) + + r = Rotation.from_quat(-np.eye(4)) + result = r.magnitude() + assert_array_almost_equal(result, [np.pi, np.pi, np.pi, 0]) + + +def test_magnitude_single_rotation(): + r = Rotation.from_quat(np.eye(4)) + result1 = r[0].magnitude() + assert_allclose(result1, np.pi) + + result2 = r[3].magnitude() + assert_allclose(result2, 0) + + +def test_approx_equal(): + rng = np.random.RandomState(0) + p = Rotation.random(10, random_state=rng) + q = Rotation.random(10, random_state=rng) + r = p * q.inv() + r_mag = r.magnitude() + atol = np.median(r_mag) # ensure we get mix of Trues and Falses + assert_equal(p.approx_equal(q, atol), (r_mag < atol)) + + +def test_approx_equal_single_rotation(): + # also tests passing single argument to approx_equal + p = Rotation.from_rotvec([0, 0, 1e-9]) # less than default atol of 1e-8 + q = Rotation.from_quat(np.eye(4)) + assert p.approx_equal(q[3]) + assert not p.approx_equal(q[0]) + + # test passing atol and using degrees + assert not p.approx_equal(q[3], atol=1e-10) + assert not p.approx_equal(q[3], atol=1e-8, degrees=True) + with pytest.warns(UserWarning, match="atol must be set"): + assert p.approx_equal(q[3], degrees=True) + + +def test_mean(): + axes = np.concatenate((-np.eye(3), np.eye(3))) + thetas = np.linspace(0, np.pi / 2, 100) + for t in thetas: + r = Rotation.from_rotvec(t * axes) + assert_allclose(r.mean().magnitude(), 0, atol=1E-10) + + +def test_weighted_mean(): + # test that doubling a weight is equivalent to including a rotation twice. + axes = np.array([[0, 0, 0], [1, 0, 0], [1, 0, 0]]) + thetas = np.linspace(0, np.pi / 2, 100) + for t in thetas: + rw = Rotation.from_rotvec(t * axes[:2]) + mw = rw.mean(weights=[1, 2]) + + r = Rotation.from_rotvec(t * axes) + m = r.mean() + assert_allclose((m * mw.inv()).magnitude(), 0, atol=1E-10) + + +def test_mean_invalid_weights(): + with pytest.raises(ValueError, match="non-negative"): + r = Rotation.from_quat(np.eye(4)) + r.mean(weights=-np.ones(4)) + + +def test_reduction_no_indices(): + result = Rotation.identity().reduce(return_indices=False) + assert isinstance(result, Rotation) + + +def test_reduction_none_indices(): + result = Rotation.identity().reduce(return_indices=True) + assert type(result) == tuple + assert len(result) == 3 + + reduced, left_best, right_best = result + assert left_best is None + assert right_best is None + + +def test_reduction_scalar_calculation(): + rng = np.random.RandomState(0) + l = Rotation.random(5, random_state=rng) + r = Rotation.random(10, random_state=rng) + p = Rotation.random(7, random_state=rng) + reduced, left_best, right_best = p.reduce(l, r, return_indices=True) + + # Loop implementation of the vectorized calculation in Rotation.reduce + scalars = np.zeros((len(l), len(p), len(r))) + for i, li in enumerate(l): + for j, pj in enumerate(p): + for k, rk in enumerate(r): + scalars[i, j, k] = np.abs((li * pj * rk).as_quat()[3]) + scalars = np.reshape(np.moveaxis(scalars, 1, 0), (scalars.shape[1], -1)) + + max_ind = np.argmax(np.reshape(scalars, (len(p), -1)), axis=1) + left_best_check = max_ind // len(r) + right_best_check = max_ind % len(r) + assert (left_best == left_best_check).all() + assert (right_best == right_best_check).all() + + reduced_check = l[left_best_check] * p * r[right_best_check] + mag = (reduced.inv() * reduced_check).magnitude() + assert_array_almost_equal(mag, np.zeros(len(p))) + + +def test_apply_single_rotation_single_point(): + mat = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + r_1d = Rotation.from_matrix(mat) + r_2d = Rotation.from_matrix(np.expand_dims(mat, axis=0)) + + v_1d = np.array([1, 2, 3]) + v_2d = np.expand_dims(v_1d, axis=0) + v1d_rotated = np.array([-2, 1, 3]) + v2d_rotated = np.expand_dims(v1d_rotated, axis=0) + + assert_allclose(r_1d.apply(v_1d), v1d_rotated) + assert_allclose(r_1d.apply(v_2d), v2d_rotated) + assert_allclose(r_2d.apply(v_1d), v2d_rotated) + assert_allclose(r_2d.apply(v_2d), v2d_rotated) + + v1d_inverse = np.array([2, -1, 3]) + v2d_inverse = np.expand_dims(v1d_inverse, axis=0) + + assert_allclose(r_1d.apply(v_1d, inverse=True), v1d_inverse) + assert_allclose(r_1d.apply(v_2d, inverse=True), v2d_inverse) + assert_allclose(r_2d.apply(v_1d, inverse=True), v2d_inverse) + assert_allclose(r_2d.apply(v_2d, inverse=True), v2d_inverse) + + +def test_apply_single_rotation_multiple_points(): + mat = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + r1 = Rotation.from_matrix(mat) + r2 = Rotation.from_matrix(np.expand_dims(mat, axis=0)) + + v = np.array([[1, 2, 3], [4, 5, 6]]) + v_rotated = np.array([[-2, 1, 3], [-5, 4, 6]]) + + assert_allclose(r1.apply(v), v_rotated) + assert_allclose(r2.apply(v), v_rotated) + + v_inverse = np.array([[2, -1, 3], [5, -4, 6]]) + + assert_allclose(r1.apply(v, inverse=True), v_inverse) + assert_allclose(r2.apply(v, inverse=True), v_inverse) + + +def test_apply_multiple_rotations_single_point(): + mat = np.empty((2, 3, 3)) + mat[0] = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + mat[1] = np.array([ + [1, 0, 0], + [0, 0, -1], + [0, 1, 0] + ]) + r = Rotation.from_matrix(mat) + + v1 = np.array([1, 2, 3]) + v2 = np.expand_dims(v1, axis=0) + + v_rotated = np.array([[-2, 1, 3], [1, -3, 2]]) + + assert_allclose(r.apply(v1), v_rotated) + assert_allclose(r.apply(v2), v_rotated) + + v_inverse = np.array([[2, -1, 3], [1, 3, -2]]) + + assert_allclose(r.apply(v1, inverse=True), v_inverse) + assert_allclose(r.apply(v2, inverse=True), v_inverse) + + +def test_apply_multiple_rotations_multiple_points(): + mat = np.empty((2, 3, 3)) + mat[0] = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + mat[1] = np.array([ + [1, 0, 0], + [0, 0, -1], + [0, 1, 0] + ]) + r = Rotation.from_matrix(mat) + + v = np.array([[1, 2, 3], [4, 5, 6]]) + v_rotated = np.array([[-2, 1, 3], [4, -6, 5]]) + assert_allclose(r.apply(v), v_rotated) + + v_inverse = np.array([[2, -1, 3], [4, 6, -5]]) + assert_allclose(r.apply(v, inverse=True), v_inverse) + + +def test_getitem(): + mat = np.empty((2, 3, 3)) + mat[0] = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + mat[1] = np.array([ + [1, 0, 0], + [0, 0, -1], + [0, 1, 0] + ]) + r = Rotation.from_matrix(mat) + + assert_allclose(r[0].as_matrix(), mat[0], atol=1e-15) + assert_allclose(r[1].as_matrix(), mat[1], atol=1e-15) + assert_allclose(r[:-1].as_matrix(), np.expand_dims(mat[0], axis=0), atol=1e-15) + + +def test_getitem_single(): + with pytest.raises(TypeError, match='not subscriptable'): + Rotation.identity()[0] + + +def test_setitem_single(): + r = Rotation.identity() + with pytest.raises(TypeError, match='not subscriptable'): + r[0] = Rotation.identity() + + +def test_setitem_slice(): + rng = np.random.RandomState(seed=0) + r1 = Rotation.random(10, random_state=rng) + r2 = Rotation.random(5, random_state=rng) + r1[1:6] = r2 + assert_equal(r1[1:6].as_quat(), r2.as_quat()) + + +def test_setitem_integer(): + rng = np.random.RandomState(seed=0) + r1 = Rotation.random(10, random_state=rng) + r2 = Rotation.random(random_state=rng) + r1[1] = r2 + assert_equal(r1[1].as_quat(), r2.as_quat()) + + +def test_setitem_wrong_type(): + r = Rotation.random(10, random_state=0) + with pytest.raises(TypeError, match='Rotation object'): + r[0] = 1 + + +def test_n_rotations(): + mat = np.empty((2, 3, 3)) + mat[0] = np.array([ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ]) + mat[1] = np.array([ + [1, 0, 0], + [0, 0, -1], + [0, 1, 0] + ]) + r = Rotation.from_matrix(mat) + + assert_equal(len(r), 2) + assert_equal(len(r[:-1]), 1) + + +def test_random_rotation_shape(): + rnd = np.random.RandomState(0) + assert_equal(Rotation.random(random_state=rnd).as_quat().shape, (4,)) + assert_equal(Rotation.random(None, random_state=rnd).as_quat().shape, (4,)) + + assert_equal(Rotation.random(1, random_state=rnd).as_quat().shape, (1, 4)) + assert_equal(Rotation.random(5, random_state=rnd).as_quat().shape, (5, 4)) + + +def test_align_vectors_no_rotation(): + x = np.array([[1, 2, 3], [4, 5, 6]]) + y = x.copy() + + r, rssd = Rotation.align_vectors(x, y) + assert_array_almost_equal(r.as_matrix(), np.eye(3)) + assert_allclose(rssd, 0, atol=1e-6) + + +def test_align_vectors_no_noise(): + rnd = np.random.RandomState(0) + c = Rotation.random(random_state=rnd) + b = rnd.normal(size=(5, 3)) + a = c.apply(b) + + est, rssd = Rotation.align_vectors(a, b) + assert_allclose(c.as_quat(), est.as_quat()) + assert_allclose(rssd, 0, atol=1e-7) + + +def test_align_vectors_improper_rotation(): + # Tests correct logic for issue #10444 + x = np.array([[0.89299824, -0.44372674, 0.0752378], + [0.60221789, -0.47564102, -0.6411702]]) + y = np.array([[0.02386536, -0.82176463, 0.5693271], + [-0.27654929, -0.95191427, -0.1318321]]) + + est, rssd = Rotation.align_vectors(x, y) + assert_allclose(x, est.apply(y), atol=1e-6) + assert_allclose(rssd, 0, atol=1e-7) + + +def test_align_vectors_rssd_sensitivity(): + rssd_expected = 0.141421356237308 + sens_expected = np.array([[0.2, 0. , 0.], + [0. , 1.5, 1.], + [0. , 1. , 1.]]) + atol = 1e-6 + a = [[0, 1, 0], [0, 1, 1], [0, 1, 1]] + b = [[1, 0, 0], [1, 1.1, 0], [1, 0.9, 0]] + rot, rssd, sens = Rotation.align_vectors(a, b, return_sensitivity=True) + assert np.isclose(rssd, rssd_expected, atol=atol) + assert np.allclose(sens, sens_expected, atol=atol) + + +def test_align_vectors_scaled_weights(): + n = 10 + a = Rotation.random(n, random_state=0).apply([1, 0, 0]) + b = Rotation.random(n, random_state=1).apply([1, 0, 0]) + scale = 2 + + est1, rssd1, cov1 = Rotation.align_vectors(a, b, np.ones(n), True) + est2, rssd2, cov2 = Rotation.align_vectors(a, b, scale * np.ones(n), True) + + assert_allclose(est1.as_matrix(), est2.as_matrix()) + assert_allclose(np.sqrt(scale) * rssd1, rssd2, atol=1e-6) + assert_allclose(cov1, cov2) + + +def test_align_vectors_noise(): + rnd = np.random.RandomState(0) + n_vectors = 100 + rot = Rotation.random(random_state=rnd) + vectors = rnd.normal(size=(n_vectors, 3)) + result = rot.apply(vectors) + + # The paper adds noise as independently distributed angular errors + sigma = np.deg2rad(1) + tolerance = 1.5 * sigma + noise = Rotation.from_rotvec( + rnd.normal( + size=(n_vectors, 3), + scale=sigma + ) + ) + + # Attitude errors must preserve norm. Hence apply individual random + # rotations to each vector. + noisy_result = noise.apply(result) + + est, rssd, cov = Rotation.align_vectors(noisy_result, vectors, + return_sensitivity=True) + + # Use rotation compositions to find out closeness + error_vector = (rot * est.inv()).as_rotvec() + assert_allclose(error_vector[0], 0, atol=tolerance) + assert_allclose(error_vector[1], 0, atol=tolerance) + assert_allclose(error_vector[2], 0, atol=tolerance) + + # Check error bounds using covariance matrix + cov *= sigma + assert_allclose(cov[0, 0], 0, atol=tolerance) + assert_allclose(cov[1, 1], 0, atol=tolerance) + assert_allclose(cov[2, 2], 0, atol=tolerance) + + assert_allclose(rssd, np.sum((noisy_result - est.apply(vectors))**2)**0.5) + + +def test_align_vectors_invalid_input(): + with pytest.raises(ValueError, match="Expected input `a` to have shape"): + Rotation.align_vectors([1, 2, 3, 4], [1, 2, 3]) + + with pytest.raises(ValueError, match="Expected input `b` to have shape"): + Rotation.align_vectors([1, 2, 3], [1, 2, 3, 4]) + + with pytest.raises(ValueError, match="Expected inputs `a` and `b` " + "to have same shapes"): + Rotation.align_vectors([[1, 2, 3],[4, 5, 6]], [[1, 2, 3]]) + + with pytest.raises(ValueError, + match="Expected `weights` to be 1 dimensional"): + Rotation.align_vectors([[1, 2, 3]], [[1, 2, 3]], weights=[[1]]) + + with pytest.raises(ValueError, + match="Expected `weights` to have number of values"): + Rotation.align_vectors([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], + weights=[1, 2, 3]) + + with pytest.raises(ValueError, + match="`weights` may not contain negative values"): + Rotation.align_vectors([[1, 2, 3]], [[1, 2, 3]], weights=[-1]) + + with pytest.raises(ValueError, + match="Only one infinite weight is allowed"): + Rotation.align_vectors([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], + weights=[np.inf, np.inf]) + + with pytest.raises(ValueError, + match="Cannot align zero length primary vectors"): + Rotation.align_vectors([[0, 0, 0]], [[1, 2, 3]]) + + with pytest.raises(ValueError, + match="Cannot return sensitivity matrix"): + Rotation.align_vectors([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], + return_sensitivity=True, weights=[np.inf, 1]) + + with pytest.raises(ValueError, + match="Cannot return sensitivity matrix"): + Rotation.align_vectors([[1, 2, 3]], [[1, 2, 3]], + return_sensitivity=True) + + +def test_align_vectors_align_constrain(): + # Align the primary +X B axis with the primary +Y A axis, and rotate about + # it such that the +Y B axis (residual of the [1, 1, 0] secondary b vector) + # is aligned with the +Z A axis (residual of the [0, 1, 1] secondary a + # vector) + atol = 1e-12 + b = [[1, 0, 0], [1, 1, 0]] + a = [[0, 1, 0], [0, 1, 1]] + m_expected = np.array([[0, 0, 1], + [1, 0, 0], + [0, 1, 0]]) + R, rssd = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + assert_allclose(R.apply(b), a, atol=atol) # Pri and sec align exactly + assert np.isclose(rssd, 0, atol=atol) + + # Do the same but with an inexact secondary rotation + b = [[1, 0, 0], [1, 2, 0]] + rssd_expected = 1.0 + R, rssd = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + assert_allclose(R.apply(b)[0], a[0], atol=atol) # Only pri aligns exactly + assert np.isclose(rssd, rssd_expected, atol=atol) + a_expected = [[0, 1, 0], [0, 1, 2]] + assert_allclose(R.apply(b), a_expected, atol=atol) + + # Check random vectors + b = [[1, 2, 3], [-2, 3, -1]] + a = [[-1, 3, 2], [1, -1, 2]] + rssd_expected = 1.3101595297515016 + R, rssd = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.apply(b)[0], a[0], atol=atol) # Only pri aligns exactly + assert np.isclose(rssd, rssd_expected, atol=atol) + + +def test_align_vectors_near_inf(): + # align_vectors should return near the same result for high weights as for + # infinite weights. rssd will be different with floating point error on the + # exactly aligned vector being multiplied by a large non-infinite weight + n = 100 + mats = [] + for i in range(6): + mats.append(Rotation.random(n, random_state=10 + i).as_matrix()) + + for i in range(n): + # Get random pairs of 3-element vectors + a = [1*mats[0][i][0], 2*mats[1][i][0]] + b = [3*mats[2][i][0], 4*mats[3][i][0]] + + R, _ = Rotation.align_vectors(a, b, weights=[1e10, 1]) + R2, _ = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.as_matrix(), R2.as_matrix(), atol=1e-4) + + for i in range(n): + # Get random triplets of 3-element vectors + a = [1*mats[0][i][0], 2*mats[1][i][0], 3*mats[2][i][0]] + b = [4*mats[3][i][0], 5*mats[4][i][0], 6*mats[5][i][0]] + + R, _ = Rotation.align_vectors(a, b, weights=[1e10, 2, 1]) + R2, _ = Rotation.align_vectors(a, b, weights=[np.inf, 2, 1]) + assert_allclose(R.as_matrix(), R2.as_matrix(), atol=1e-4) + + +def test_align_vectors_parallel(): + atol = 1e-12 + a = [[1, 0, 0], [0, 1, 0]] + b = [[0, 1, 0], [0, 1, 0]] + m_expected = np.array([[0, 1, 0], + [-1, 0, 0], + [0, 0, 1]]) + R, _ = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + R, _ = Rotation.align_vectors(a[0], b[0]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + assert_allclose(R.apply(b[0]), a[0], atol=atol) + + b = [[1, 0, 0], [1, 0, 0]] + m_expected = np.array([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) + R, _ = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + R, _ = Rotation.align_vectors(a[0], b[0]) + assert_allclose(R.as_matrix(), m_expected, atol=atol) + assert_allclose(R.apply(b[0]), a[0], atol=atol) + + +def test_align_vectors_antiparallel(): + # Test exact 180 deg rotation + atol = 1e-12 + as_to_test = np.array([[[1, 0, 0], [0, 1, 0]], + [[0, 1, 0], [1, 0, 0]], + [[0, 0, 1], [0, 1, 0]]]) + bs_to_test = [[-a[0], a[1]] for a in as_to_test] + for a, b in zip(as_to_test, bs_to_test): + R, _ = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + assert_allclose(R.magnitude(), np.pi, atol=atol) + assert_allclose(R.apply(b[0]), a[0], atol=atol) + + # Test exact rotations near 180 deg + Rs = Rotation.random(100, random_state=0) + dRs = Rotation.from_rotvec(Rs.as_rotvec()*1e-4) # scale down to small angle + a = [[ 1, 0, 0], [0, 1, 0]] + b = [[-1, 0, 0], [0, 1, 0]] + as_to_test = [] + for dR in dRs: + as_to_test.append([dR.apply(a[0]), a[1]]) + for a in as_to_test: + R, _ = Rotation.align_vectors(a, b, weights=[np.inf, 1]) + R2, _ = Rotation.align_vectors(a, b, weights=[1e10, 1]) + assert_allclose(R.as_matrix(), R2.as_matrix(), atol=atol) + + +def test_align_vectors_primary_only(): + atol = 1e-12 + mats_a = Rotation.random(100, random_state=0).as_matrix() + mats_b = Rotation.random(100, random_state=1).as_matrix() + for mat_a, mat_b in zip(mats_a, mats_b): + # Get random 3-element unit vectors + a = mat_a[0] + b = mat_b[0] + + # Compare to align_vectors with primary only + R, rssd = Rotation.align_vectors(a, b) + assert_allclose(R.apply(b), a, atol=atol) + assert np.isclose(rssd, 0, atol=atol) + + +def test_slerp(): + rnd = np.random.RandomState(0) + + key_rots = Rotation.from_quat(rnd.uniform(size=(5, 4))) + key_quats = key_rots.as_quat() + + key_times = [0, 1, 2, 3, 4] + interpolator = Slerp(key_times, key_rots) + + times = [0, 0.5, 0.25, 1, 1.5, 2, 2.75, 3, 3.25, 3.60, 4] + interp_rots = interpolator(times) + interp_quats = interp_rots.as_quat() + + # Dot products are affected by sign of quaternions + interp_quats[interp_quats[:, -1] < 0] *= -1 + # Checking for quaternion equality, perform same operation + key_quats[key_quats[:, -1] < 0] *= -1 + + # Equality at keyframes, including both endpoints + assert_allclose(interp_quats[0], key_quats[0]) + assert_allclose(interp_quats[3], key_quats[1]) + assert_allclose(interp_quats[5], key_quats[2]) + assert_allclose(interp_quats[7], key_quats[3]) + assert_allclose(interp_quats[10], key_quats[4]) + + # Constant angular velocity between keyframes. Check by equating + # cos(theta) between quaternion pairs with equal time difference. + cos_theta1 = np.sum(interp_quats[0] * interp_quats[2]) + cos_theta2 = np.sum(interp_quats[2] * interp_quats[1]) + assert_allclose(cos_theta1, cos_theta2) + + cos_theta4 = np.sum(interp_quats[3] * interp_quats[4]) + cos_theta5 = np.sum(interp_quats[4] * interp_quats[5]) + assert_allclose(cos_theta4, cos_theta5) + + # theta1: 0 -> 0.25, theta3 : 0.5 -> 1 + # Use double angle formula for double the time difference + cos_theta3 = np.sum(interp_quats[1] * interp_quats[3]) + assert_allclose(cos_theta3, 2 * (cos_theta1**2) - 1) + + # Miscellaneous checks + assert_equal(len(interp_rots), len(times)) + + +def test_slerp_rot_is_rotation(): + with pytest.raises(TypeError, match="must be a `Rotation` instance"): + r = np.array([[1,2,3,4], + [0,0,0,1]]) + t = np.array([0, 1]) + Slerp(t, r) + + +def test_slerp_single_rot(): + msg = "must be a sequence of at least 2 rotations" + with pytest.raises(ValueError, match=msg): + r = Rotation.from_quat([1, 2, 3, 4]) + Slerp([1], r) + + +def test_slerp_rot_len1(): + msg = "must be a sequence of at least 2 rotations" + with pytest.raises(ValueError, match=msg): + r = Rotation.from_quat([[1, 2, 3, 4]]) + Slerp([1], r) + + +def test_slerp_time_dim_mismatch(): + with pytest.raises(ValueError, + match="times to be specified in a 1 dimensional array"): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(2, 4))) + t = np.array([[1], + [2]]) + Slerp(t, r) + + +def test_slerp_num_rotations_mismatch(): + with pytest.raises(ValueError, match="number of rotations to be equal to " + "number of timestamps"): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(5, 4))) + t = np.arange(7) + Slerp(t, r) + + +def test_slerp_equal_times(): + with pytest.raises(ValueError, match="strictly increasing order"): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(5, 4))) + t = [0, 1, 2, 2, 4] + Slerp(t, r) + + +def test_slerp_decreasing_times(): + with pytest.raises(ValueError, match="strictly increasing order"): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(5, 4))) + t = [0, 1, 3, 2, 4] + Slerp(t, r) + + +def test_slerp_call_time_dim_mismatch(): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(5, 4))) + t = np.arange(5) + s = Slerp(t, r) + + with pytest.raises(ValueError, + match="`times` must be at most 1-dimensional."): + interp_times = np.array([[3.5], + [4.2]]) + s(interp_times) + + +def test_slerp_call_time_out_of_range(): + rnd = np.random.RandomState(0) + r = Rotation.from_quat(rnd.uniform(size=(5, 4))) + t = np.arange(5) + 1 + s = Slerp(t, r) + + with pytest.raises(ValueError, match="times must be within the range"): + s([0, 1, 2]) + with pytest.raises(ValueError, match="times must be within the range"): + s([1, 2, 6]) + + +def test_slerp_call_scalar_time(): + r = Rotation.from_euler('X', [0, 80], degrees=True) + s = Slerp([0, 1], r) + + r_interpolated = s(0.25) + r_interpolated_expected = Rotation.from_euler('X', 20, degrees=True) + + delta = r_interpolated * r_interpolated_expected.inv() + + assert_allclose(delta.magnitude(), 0, atol=1e-16) + + +def test_multiplication_stability(): + qs = Rotation.random(50, random_state=0) + rs = Rotation.random(1000, random_state=1) + for q in qs: + rs *= q * rs + assert_allclose(np.linalg.norm(rs.as_quat(), axis=1), 1) + + +def test_pow(): + atol = 1e-14 + p = Rotation.random(10, random_state=0) + p_inv = p.inv() + # Test the short-cuts and other integers + for n in [-5, -2, -1, 0, 1, 2, 5]: + # Test accuracy + q = p ** n + r = Rotation.identity(10) + for _ in range(abs(n)): + if n > 0: + r = r * p + else: + r = r * p_inv + ang = (q * r.inv()).magnitude() + assert np.all(ang < atol) + + # Test shape preservation + r = Rotation.from_quat([0, 0, 0, 1]) + assert (r**n).as_quat().shape == (4,) + r = Rotation.from_quat([[0, 0, 0, 1]]) + assert (r**n).as_quat().shape == (1, 4) + + # Large angle fractional + for n in [-1.5, -0.5, -0.0, 0.0, 0.5, 1.5]: + q = p ** n + r = Rotation.from_rotvec(n * p.as_rotvec()) + assert_allclose(q.as_quat(), r.as_quat(), atol=atol) + + # Small angle + p = Rotation.from_rotvec([1e-12, 0, 0]) + n = 3 + q = p ** n + r = Rotation.from_rotvec(n * p.as_rotvec()) + assert_allclose(q.as_quat(), r.as_quat(), atol=atol) + + +def test_pow_errors(): + p = Rotation.random(random_state=0) + with pytest.raises(NotImplementedError, match='modulus not supported'): + pow(p, 1, 1) + + +def test_rotation_within_numpy_array(): + single = Rotation.random(random_state=0) + multiple = Rotation.random(2, random_state=1) + + array = np.array(single) + assert_equal(array.shape, ()) + + array = np.array(multiple) + assert_equal(array.shape, (2,)) + assert_allclose(array[0].as_matrix(), multiple[0].as_matrix()) + assert_allclose(array[1].as_matrix(), multiple[1].as_matrix()) + + array = np.array([single]) + assert_equal(array.shape, (1,)) + assert_equal(array[0], single) + + array = np.array([multiple]) + assert_equal(array.shape, (1, 2)) + assert_allclose(array[0, 0].as_matrix(), multiple[0].as_matrix()) + assert_allclose(array[0, 1].as_matrix(), multiple[1].as_matrix()) + + array = np.array([single, multiple], dtype=object) + assert_equal(array.shape, (2,)) + assert_equal(array[0], single) + assert_equal(array[1], multiple) + + array = np.array([multiple, multiple, multiple]) + assert_equal(array.shape, (3, 2)) + + +def test_pickling(): + r = Rotation.from_quat([0, 0, np.sin(np.pi/4), np.cos(np.pi/4)]) + pkl = pickle.dumps(r) + unpickled = pickle.loads(pkl) + assert_allclose(r.as_matrix(), unpickled.as_matrix(), atol=1e-15) + + +def test_deepcopy(): + r = Rotation.from_quat([0, 0, np.sin(np.pi/4), np.cos(np.pi/4)]) + r1 = copy.deepcopy(r) + assert_allclose(r.as_matrix(), r1.as_matrix(), atol=1e-15) + + +def test_as_euler_contiguous(): + r = Rotation.from_quat([0, 0, 0, 1]) + e1 = r.as_euler('xyz') # extrinsic euler rotation + e2 = r.as_euler('XYZ') # intrinsic + assert e1.flags['C_CONTIGUOUS'] is True + assert e2.flags['C_CONTIGUOUS'] is True + assert all(i >= 0 for i in e1.strides) + assert all(i >= 0 for i in e2.strides) + + +def test_concatenate(): + rotation = Rotation.random(10, random_state=0) + sizes = [1, 2, 3, 1, 3] + starts = [0] + list(np.cumsum(sizes)) + split = [rotation[i:i + n] for i, n in zip(starts, sizes)] + result = Rotation.concatenate(split) + assert_equal(rotation.as_quat(), result.as_quat()) + + +def test_concatenate_wrong_type(): + with pytest.raises(TypeError, match='Rotation objects only'): + Rotation.concatenate([Rotation.identity(), 1, None]) + + +# Regression test for gh-16663 +def test_len_and_bool(): + rotation_multi_empty = Rotation(np.empty((0, 4))) + rotation_multi_one = Rotation([[0, 0, 0, 1]]) + rotation_multi = Rotation([[0, 0, 0, 1], [0, 0, 0, 1]]) + rotation_single = Rotation([0, 0, 0, 1]) + + assert len(rotation_multi_empty) == 0 + assert len(rotation_multi_one) == 1 + assert len(rotation_multi) == 2 + with pytest.raises(TypeError, match="Single rotation has no len()."): + len(rotation_single) + + # Rotation should always be truthy. See gh-16663 + assert rotation_multi_empty + assert rotation_multi_one + assert rotation_multi + assert rotation_single + + +def test_from_davenport_single_rotation(): + axis = [0, 0, 1] + quat = Rotation.from_davenport(axis, 'extrinsic', 90, + degrees=True).as_quat() + expected_quat = np.array([0, 0, 1, 1]) / np.sqrt(2) + assert_allclose(quat, expected_quat) + + +def test_from_davenport_one_or_two_axes(): + ez = [0, 0, 1] + ey = [0, 1, 0] + + # Single rotation, single axis, axes.shape == (3, ) + rot = Rotation.from_rotvec(np.array(ez) * np.pi/4) + rot_dav = Rotation.from_davenport(ez, 'e', np.pi/4) + assert_allclose(rot.as_quat(canonical=True), + rot_dav.as_quat(canonical=True)) + + # Single rotation, single axis, axes.shape == (1, 3) + rot = Rotation.from_rotvec([np.array(ez) * np.pi/4]) + rot_dav = Rotation.from_davenport([ez], 'e', [np.pi/4]) + assert_allclose(rot.as_quat(canonical=True), + rot_dav.as_quat(canonical=True)) + + # Single rotation, two axes, axes.shape == (2, 3) + rot = Rotation.from_rotvec([np.array(ez) * np.pi/4, + np.array(ey) * np.pi/6]) + rot = rot[0] * rot[1] + rot_dav = Rotation.from_davenport([ey, ez], 'e', [np.pi/6, np.pi/4]) + assert_allclose(rot.as_quat(canonical=True), + rot_dav.as_quat(canonical=True)) + + # Two rotations, single axis, axes.shape == (3, ) + rot = Rotation.from_rotvec([np.array(ez) * np.pi/6, + np.array(ez) * np.pi/4]) + rot_dav = Rotation.from_davenport([ez], 'e', [np.pi/6, np.pi/4]) + assert_allclose(rot.as_quat(canonical=True), + rot_dav.as_quat(canonical=True)) + + +def test_from_davenport_invalid_input(): + ez = [0, 0, 1] + ey = [0, 1, 0] + ezy = [0, 1, 1] + with pytest.raises(ValueError, match="must be orthogonal"): + Rotation.from_davenport([ez, ezy], 'e', [0, 0]) + with pytest.raises(ValueError, match="must be orthogonal"): + Rotation.from_davenport([ez, ey, ezy], 'e', [0, 0, 0]) + with pytest.raises(ValueError, match="order should be"): + Rotation.from_davenport([ez], 'xyz', [0]) + with pytest.raises(ValueError, match="Expected `angles`"): + Rotation.from_davenport([ez, ey, ez], 'e', [0, 1, 2, 3]) + + +def test_as_davenport(): + rnd = np.random.RandomState(0) + n = 100 + angles = np.empty((n, 3)) + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles_middle = rnd.uniform(low=0, high=np.pi, size=(n,)) + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + lambdas = rnd.uniform(low=0, high=np.pi, size=(20,)) + + e1 = np.array([1, 0, 0]) + e2 = np.array([0, 1, 0]) + + for lamb in lambdas: + ax_lamb = [e1, e2, Rotation.from_rotvec(lamb*e2).apply(e1)] + angles[:, 1] = angles_middle - lamb + for order in ['extrinsic', 'intrinsic']: + ax = ax_lamb if order == 'intrinsic' else ax_lamb[::-1] + rot = Rotation.from_davenport(ax, order, angles) + angles_dav = rot.as_davenport(ax, order) + assert_allclose(angles_dav, angles) + + +def test_as_davenport_degenerate(): + # Since we cannot check for angle equality, we check for rotation matrix + # equality + rnd = np.random.RandomState(0) + n = 5 + angles = np.empty((n, 3)) + + # symmetric sequences + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles_middle = [rnd.choice([0, np.pi]) for i in range(n)] + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + lambdas = rnd.uniform(low=0, high=np.pi, size=(5,)) + + e1 = np.array([1, 0, 0]) + e2 = np.array([0, 1, 0]) + + for lamb in lambdas: + ax_lamb = [e1, e2, Rotation.from_rotvec(lamb*e2).apply(e1)] + angles[:, 1] = angles_middle - lamb + for order in ['extrinsic', 'intrinsic']: + ax = ax_lamb if order == 'intrinsic' else ax_lamb[::-1] + rot = Rotation.from_davenport(ax, order, angles) + with pytest.warns(UserWarning, match="Gimbal lock"): + angles_dav = rot.as_davenport(ax, order) + mat_expected = rot.as_matrix() + mat_estimated = Rotation.from_davenport(ax, order, angles_dav).as_matrix() + assert_array_almost_equal(mat_expected, mat_estimated) + + +def test_compare_from_davenport_from_euler(): + rnd = np.random.RandomState(0) + n = 100 + angles = np.empty((n, 3)) + + # symmetric sequences + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,)) + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + for order in ['extrinsic', 'intrinsic']: + for seq_tuple in permutations('xyz'): + seq = ''.join([seq_tuple[0], seq_tuple[1], seq_tuple[0]]) + ax = [basis_vec(i) for i in seq] + if order == 'intrinsic': + seq = seq.upper() + eul = Rotation.from_euler(seq, angles) + dav = Rotation.from_davenport(ax, order, angles) + assert_allclose(eul.as_quat(canonical=True), dav.as_quat(canonical=True), + rtol=1e-12) + + # asymmetric sequences + angles[:, 1] -= np.pi / 2 + for order in ['extrinsic', 'intrinsic']: + for seq_tuple in permutations('xyz'): + seq = ''.join(seq_tuple) + ax = [basis_vec(i) for i in seq] + if order == 'intrinsic': + seq = seq.upper() + eul = Rotation.from_euler(seq, angles) + dav = Rotation.from_davenport(ax, order, angles) + assert_allclose(eul.as_quat(), dav.as_quat(), rtol=1e-12) + + +def test_compare_as_davenport_as_euler(): + rnd = np.random.RandomState(0) + n = 100 + angles = np.empty((n, 3)) + + # symmetric sequences + angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,)) + angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,)) + for order in ['extrinsic', 'intrinsic']: + for seq_tuple in permutations('xyz'): + seq = ''.join([seq_tuple[0], seq_tuple[1], seq_tuple[0]]) + ax = [basis_vec(i) for i in seq] + if order == 'intrinsic': + seq = seq.upper() + rot = Rotation.from_euler(seq, angles) + eul = rot.as_euler(seq) + dav = rot.as_davenport(ax, order) + assert_allclose(eul, dav, rtol=1e-12) + + # asymmetric sequences + angles[:, 1] -= np.pi / 2 + for order in ['extrinsic', 'intrinsic']: + for seq_tuple in permutations('xyz'): + seq = ''.join(seq_tuple) + ax = [basis_vec(i) for i in seq] + if order == 'intrinsic': + seq = seq.upper() + rot = Rotation.from_euler(seq, angles) + eul = rot.as_euler(seq) + dav = rot.as_davenport(ax, order) + assert_allclose(eul, dav, rtol=1e-12) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_groups.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_groups.py new file mode 100644 index 0000000000000000000000000000000000000000..befe60c13c5a5863a2ae50f9d20e6d054795f6b9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_groups.py @@ -0,0 +1,169 @@ +import pytest + +import numpy as np +from numpy.testing import assert_array_almost_equal +from scipy.spatial.transform import Rotation +from scipy.optimize import linear_sum_assignment +from scipy.spatial.distance import cdist +from scipy.constants import golden as phi +from scipy.spatial import cKDTree + + +TOL = 1E-12 +NS = range(1, 13) +NAMES = ["I", "O", "T"] + ["C%d" % n for n in NS] + ["D%d" % n for n in NS] +SIZES = [60, 24, 12] + list(NS) + [2 * n for n in NS] + + +def _calculate_rmsd(P, Q): + """Calculates the root-mean-square distance between the points of P and Q. + The distance is taken as the minimum over all possible matchings. It is + zero if P and Q are identical and non-zero if not. + """ + distance_matrix = cdist(P, Q, metric='sqeuclidean') + matching = linear_sum_assignment(distance_matrix) + return np.sqrt(distance_matrix[matching].sum()) + + +def _generate_pyramid(n, axis): + thetas = np.linspace(0, 2 * np.pi, n + 1)[:-1] + P = np.vstack([np.zeros(n), np.cos(thetas), np.sin(thetas)]).T + P = np.concatenate((P, [[1, 0, 0]])) + return np.roll(P, axis, axis=1) + + +def _generate_prism(n, axis): + thetas = np.linspace(0, 2 * np.pi, n + 1)[:-1] + bottom = np.vstack([-np.ones(n), np.cos(thetas), np.sin(thetas)]).T + top = np.vstack([+np.ones(n), np.cos(thetas), np.sin(thetas)]).T + P = np.concatenate((bottom, top)) + return np.roll(P, axis, axis=1) + + +def _generate_icosahedron(): + x = np.array([[0, -1, -phi], + [0, -1, +phi], + [0, +1, -phi], + [0, +1, +phi]]) + return np.concatenate([np.roll(x, i, axis=1) for i in range(3)]) + + +def _generate_octahedron(): + return np.array([[-1, 0, 0], [+1, 0, 0], [0, -1, 0], + [0, +1, 0], [0, 0, -1], [0, 0, +1]]) + + +def _generate_tetrahedron(): + return np.array([[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]]) + + +@pytest.mark.parametrize("name", [-1, None, True, np.array(['C3'])]) +def test_group_type(name): + with pytest.raises(ValueError, + match="must be a string"): + Rotation.create_group(name) + + +@pytest.mark.parametrize("name", ["Q", " ", "CA", "C ", "DA", "D ", "I2", ""]) +def test_group_name(name): + with pytest.raises(ValueError, + match="must be one of 'I', 'O', 'T', 'Dn', 'Cn'"): + Rotation.create_group(name) + + +@pytest.mark.parametrize("name", ["C0", "D0"]) +def test_group_order_positive(name): + with pytest.raises(ValueError, + match="Group order must be positive"): + Rotation.create_group(name) + + +@pytest.mark.parametrize("axis", ['A', 'b', 0, 1, 2, 4, False, None]) +def test_axis_valid(axis): + with pytest.raises(ValueError, + match="`axis` must be one of"): + Rotation.create_group("C1", axis) + + +def test_icosahedral(): + """The icosahedral group fixes the rotations of an icosahedron. Here we + test that the icosahedron is invariant after application of the elements + of the rotation group.""" + P = _generate_icosahedron() + for g in Rotation.create_group("I"): + g = Rotation.from_quat(g.as_quat()) + assert _calculate_rmsd(P, g.apply(P)) < TOL + + +def test_octahedral(): + """Test that the octahedral group correctly fixes the rotations of an + octahedron.""" + P = _generate_octahedron() + for g in Rotation.create_group("O"): + assert _calculate_rmsd(P, g.apply(P)) < TOL + + +def test_tetrahedral(): + """Test that the tetrahedral group correctly fixes the rotations of a + tetrahedron.""" + P = _generate_tetrahedron() + for g in Rotation.create_group("T"): + assert _calculate_rmsd(P, g.apply(P)) < TOL + + +@pytest.mark.parametrize("n", NS) +@pytest.mark.parametrize("axis", 'XYZ') +def test_dicyclic(n, axis): + """Test that the dicyclic group correctly fixes the rotations of a + prism.""" + P = _generate_prism(n, axis='XYZ'.index(axis)) + for g in Rotation.create_group("D%d" % n, axis=axis): + assert _calculate_rmsd(P, g.apply(P)) < TOL + + +@pytest.mark.parametrize("n", NS) +@pytest.mark.parametrize("axis", 'XYZ') +def test_cyclic(n, axis): + """Test that the cyclic group correctly fixes the rotations of a + pyramid.""" + P = _generate_pyramid(n, axis='XYZ'.index(axis)) + for g in Rotation.create_group("C%d" % n, axis=axis): + assert _calculate_rmsd(P, g.apply(P)) < TOL + + +@pytest.mark.parametrize("name, size", zip(NAMES, SIZES)) +def test_group_sizes(name, size): + assert len(Rotation.create_group(name)) == size + + +@pytest.mark.parametrize("name, size", zip(NAMES, SIZES)) +def test_group_no_duplicates(name, size): + g = Rotation.create_group(name) + kdtree = cKDTree(g.as_quat()) + assert len(kdtree.query_pairs(1E-3)) == 0 + + +@pytest.mark.parametrize("name, size", zip(NAMES, SIZES)) +def test_group_symmetry(name, size): + g = Rotation.create_group(name) + q = np.concatenate((-g.as_quat(), g.as_quat())) + distance = np.sort(cdist(q, q)) + deltas = np.max(distance, axis=0) - np.min(distance, axis=0) + assert (deltas < TOL).all() + + +@pytest.mark.parametrize("name", NAMES) +def test_reduction(name): + """Test that the elements of the rotation group are correctly + mapped onto the identity rotation.""" + g = Rotation.create_group(name) + f = g.reduce(g) + assert_array_almost_equal(f.magnitude(), np.zeros(len(g))) + + +@pytest.mark.parametrize("name", NAMES) +def test_single_reduction(name): + g = Rotation.create_group(name) + f = g[-1].reduce(g) + assert_array_almost_equal(f.magnitude(), 0) + assert f.as_quat().shape == (4,) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_spline.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_spline.py new file mode 100644 index 0000000000000000000000000000000000000000..6441431f2fb54a95edd6364c4671bc55a2ce2b8f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/spatial/transform/tests/test_rotation_spline.py @@ -0,0 +1,162 @@ +from itertools import product +import numpy as np +from numpy.testing import assert_allclose +from pytest import raises +from scipy.spatial.transform import Rotation, RotationSpline +from scipy.spatial.transform._rotation_spline import ( + _angular_rate_to_rotvec_dot_matrix, + _rotvec_dot_to_angular_rate_matrix, + _matrix_vector_product_of_stacks, + _angular_acceleration_nonlinear_term, + _create_block_3_diagonal_matrix) + + +def test_angular_rate_to_rotvec_conversions(): + np.random.seed(0) + rv = np.random.randn(4, 3) + A = _angular_rate_to_rotvec_dot_matrix(rv) + A_inv = _rotvec_dot_to_angular_rate_matrix(rv) + + # When the rotation vector is aligned with the angular rate, then + # the rotation vector rate and angular rate are the same. + assert_allclose(_matrix_vector_product_of_stacks(A, rv), rv) + assert_allclose(_matrix_vector_product_of_stacks(A_inv, rv), rv) + + # A and A_inv must be reciprocal to each other. + I_stack = np.empty((4, 3, 3)) + I_stack[:] = np.eye(3) + assert_allclose(np.matmul(A, A_inv), I_stack, atol=1e-15) + + +def test_angular_rate_nonlinear_term(): + # The only simple test is to check that the term is zero when + # the rotation vector + np.random.seed(0) + rv = np.random.rand(4, 3) + assert_allclose(_angular_acceleration_nonlinear_term(rv, rv), 0, + atol=1e-19) + + +def test_create_block_3_diagonal_matrix(): + np.random.seed(0) + A = np.empty((4, 3, 3)) + A[:] = np.arange(1, 5)[:, None, None] + + B = np.empty((4, 3, 3)) + B[:] = -np.arange(1, 5)[:, None, None] + d = 10 * np.arange(10, 15) + + banded = _create_block_3_diagonal_matrix(A, B, d) + + # Convert the banded matrix to the full matrix. + k, l = list(zip(*product(np.arange(banded.shape[0]), + np.arange(banded.shape[1])))) + k = np.asarray(k) + l = np.asarray(l) + + i = k - 5 + l + j = l + values = banded.ravel() + mask = (i >= 0) & (i < 15) + i = i[mask] + j = j[mask] + values = values[mask] + full = np.zeros((15, 15)) + full[i, j] = values + + zero = np.zeros((3, 3)) + eye = np.eye(3) + + # Create the reference full matrix in the most straightforward manner. + ref = np.block([ + [d[0] * eye, B[0], zero, zero, zero], + [A[0], d[1] * eye, B[1], zero, zero], + [zero, A[1], d[2] * eye, B[2], zero], + [zero, zero, A[2], d[3] * eye, B[3]], + [zero, zero, zero, A[3], d[4] * eye], + ]) + + assert_allclose(full, ref, atol=1e-19) + + +def test_spline_2_rotations(): + times = [0, 10] + rotations = Rotation.from_euler('xyz', [[0, 0, 0], [10, -20, 30]], + degrees=True) + spline = RotationSpline(times, rotations) + + rv = (rotations[0].inv() * rotations[1]).as_rotvec() + rate = rv / (times[1] - times[0]) + times_check = np.array([-1, 5, 12]) + dt = times_check - times[0] + rv_ref = rate * dt[:, None] + + assert_allclose(spline(times_check).as_rotvec(), rv_ref) + assert_allclose(spline(times_check, 1), np.resize(rate, (3, 3))) + assert_allclose(spline(times_check, 2), 0, atol=1e-16) + + +def test_constant_attitude(): + times = np.arange(10) + rotations = Rotation.from_rotvec(np.ones((10, 3))) + spline = RotationSpline(times, rotations) + + times_check = np.linspace(-1, 11) + assert_allclose(spline(times_check).as_rotvec(), 1, rtol=1e-15) + assert_allclose(spline(times_check, 1), 0, atol=1e-17) + assert_allclose(spline(times_check, 2), 0, atol=1e-17) + + assert_allclose(spline(5.5).as_rotvec(), 1, rtol=1e-15) + assert_allclose(spline(5.5, 1), 0, atol=1e-17) + assert_allclose(spline(5.5, 2), 0, atol=1e-17) + + +def test_spline_properties(): + times = np.array([0, 5, 15, 27]) + angles = [[-5, 10, 27], [3, 5, 38], [-12, 10, 25], [-15, 20, 11]] + + rotations = Rotation.from_euler('xyz', angles, degrees=True) + spline = RotationSpline(times, rotations) + + assert_allclose(spline(times).as_euler('xyz', degrees=True), angles) + assert_allclose(spline(0).as_euler('xyz', degrees=True), angles[0]) + + h = 1e-8 + rv0 = spline(times).as_rotvec() + rvm = spline(times - h).as_rotvec() + rvp = spline(times + h).as_rotvec() + # rtol bumped from 1e-15 to 1.5e-15 in gh18414 for linux 32 bit + assert_allclose(rv0, 0.5 * (rvp + rvm), rtol=1.5e-15) + + r0 = spline(times, 1) + rm = spline(times - h, 1) + rp = spline(times + h, 1) + assert_allclose(r0, 0.5 * (rm + rp), rtol=1e-14) + + a0 = spline(times, 2) + am = spline(times - h, 2) + ap = spline(times + h, 2) + assert_allclose(a0, am, rtol=1e-7) + assert_allclose(a0, ap, rtol=1e-7) + + +def test_error_handling(): + raises(ValueError, RotationSpline, [1.0], Rotation.random()) + + r = Rotation.random(10) + t = np.arange(10).reshape(5, 2) + raises(ValueError, RotationSpline, t, r) + + t = np.arange(9) + raises(ValueError, RotationSpline, t, r) + + t = np.arange(10) + t[5] = 0 + raises(ValueError, RotationSpline, t, r) + + t = np.arange(10) + + s = RotationSpline(t, r) + raises(ValueError, s, 10, -1) + + raises(ValueError, s, np.arange(10).reshape(5, 2)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8a8bdcd73a2fd80b87d9c570b40bddbe318a8d60 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/__init__.py @@ -0,0 +1,643 @@ +""" +.. _statsrefmanual: + +========================================== +Statistical functions (:mod:`scipy.stats`) +========================================== + +.. currentmodule:: scipy.stats + +This module contains a large number of probability distributions, +summary and frequency statistics, correlation functions and statistical +tests, masked statistics, kernel density estimation, quasi-Monte Carlo +functionality, and more. + +Statistics is a very large area, and there are topics that are out of scope +for SciPy and are covered by other packages. Some of the most important ones +are: + +- `statsmodels `__: + regression, linear models, time series analysis, extensions to topics + also covered by ``scipy.stats``. +- `Pandas `__: tabular data, time series + functionality, interfaces to other statistical languages. +- `PyMC `__: Bayesian statistical + modeling, probabilistic machine learning. +- `scikit-learn `__: classification, regression, + model selection. +- `Seaborn `__: statistical data visualization. +- `rpy2 `__: Python to R bridge. + + +Probability distributions +========================= + +Each univariate distribution is an instance of a subclass of `rv_continuous` +(`rv_discrete` for discrete distributions): + +.. autosummary:: + :toctree: generated/ + + rv_continuous + rv_discrete + rv_histogram + +Continuous distributions +------------------------ + +.. autosummary:: + :toctree: generated/ + + alpha -- Alpha + anglit -- Anglit + arcsine -- Arcsine + argus -- Argus + beta -- Beta + betaprime -- Beta Prime + bradford -- Bradford + burr -- Burr (Type III) + burr12 -- Burr (Type XII) + cauchy -- Cauchy + chi -- Chi + chi2 -- Chi-squared + cosine -- Cosine + crystalball -- Crystalball + dgamma -- Double Gamma + dweibull -- Double Weibull + erlang -- Erlang + expon -- Exponential + exponnorm -- Exponentially Modified Normal + exponweib -- Exponentiated Weibull + exponpow -- Exponential Power + f -- F (Snecdor F) + fatiguelife -- Fatigue Life (Birnbaum-Saunders) + fisk -- Fisk + foldcauchy -- Folded Cauchy + foldnorm -- Folded Normal + genlogistic -- Generalized Logistic + gennorm -- Generalized normal + genpareto -- Generalized Pareto + genexpon -- Generalized Exponential + genextreme -- Generalized Extreme Value + gausshyper -- Gauss Hypergeometric + gamma -- Gamma + gengamma -- Generalized gamma + genhalflogistic -- Generalized Half Logistic + genhyperbolic -- Generalized Hyperbolic + geninvgauss -- Generalized Inverse Gaussian + gibrat -- Gibrat + gompertz -- Gompertz (Truncated Gumbel) + gumbel_r -- Right Sided Gumbel, Log-Weibull, Fisher-Tippett, Extreme Value Type I + gumbel_l -- Left Sided Gumbel, etc. + halfcauchy -- Half Cauchy + halflogistic -- Half Logistic + halfnorm -- Half Normal + halfgennorm -- Generalized Half Normal + hypsecant -- Hyperbolic Secant + invgamma -- Inverse Gamma + invgauss -- Inverse Gaussian + invweibull -- Inverse Weibull + jf_skew_t -- Jones and Faddy Skew-T + johnsonsb -- Johnson SB + johnsonsu -- Johnson SU + kappa4 -- Kappa 4 parameter + kappa3 -- Kappa 3 parameter + ksone -- Distribution of Kolmogorov-Smirnov one-sided test statistic + kstwo -- Distribution of Kolmogorov-Smirnov two-sided test statistic + kstwobign -- Limiting Distribution of scaled Kolmogorov-Smirnov two-sided test statistic. + laplace -- Laplace + laplace_asymmetric -- Asymmetric Laplace + levy -- Levy + levy_l + levy_stable + logistic -- Logistic + loggamma -- Log-Gamma + loglaplace -- Log-Laplace (Log Double Exponential) + lognorm -- Log-Normal + loguniform -- Log-Uniform + lomax -- Lomax (Pareto of the second kind) + maxwell -- Maxwell + mielke -- Mielke's Beta-Kappa + moyal -- Moyal + nakagami -- Nakagami + ncx2 -- Non-central chi-squared + ncf -- Non-central F + nct -- Non-central Student's T + norm -- Normal (Gaussian) + norminvgauss -- Normal Inverse Gaussian + pareto -- Pareto + pearson3 -- Pearson type III + powerlaw -- Power-function + powerlognorm -- Power log normal + powernorm -- Power normal + rdist -- R-distribution + rayleigh -- Rayleigh + rel_breitwigner -- Relativistic Breit-Wigner + rice -- Rice + recipinvgauss -- Reciprocal Inverse Gaussian + semicircular -- Semicircular + skewcauchy -- Skew Cauchy + skewnorm -- Skew normal + studentized_range -- Studentized Range + t -- Student's T + trapezoid -- Trapezoidal + triang -- Triangular + truncexpon -- Truncated Exponential + truncnorm -- Truncated Normal + truncpareto -- Truncated Pareto + truncweibull_min -- Truncated minimum Weibull distribution + tukeylambda -- Tukey-Lambda + uniform -- Uniform + vonmises -- Von-Mises (Circular) + vonmises_line -- Von-Mises (Line) + wald -- Wald + weibull_min -- Minimum Weibull (see Frechet) + weibull_max -- Maximum Weibull (see Frechet) + wrapcauchy -- Wrapped Cauchy + +The ``fit`` method of the univariate continuous distributions uses +maximum likelihood estimation to fit the distribution to a data set. +The ``fit`` method can accept regular data or *censored data*. +Censored data is represented with instances of the `CensoredData` +class. + +.. autosummary:: + :toctree: generated/ + + CensoredData + + +Multivariate distributions +-------------------------- + +.. autosummary:: + :toctree: generated/ + + multivariate_normal -- Multivariate normal distribution + matrix_normal -- Matrix normal distribution + dirichlet -- Dirichlet + dirichlet_multinomial -- Dirichlet multinomial distribution + wishart -- Wishart + invwishart -- Inverse Wishart + multinomial -- Multinomial distribution + special_ortho_group -- SO(N) group + ortho_group -- O(N) group + unitary_group -- U(N) group + random_correlation -- random correlation matrices + multivariate_t -- Multivariate t-distribution + multivariate_hypergeom -- Multivariate hypergeometric distribution + random_table -- Distribution of random tables with given marginals + uniform_direction -- Uniform distribution on S(N-1) + vonmises_fisher -- Von Mises-Fisher distribution + +`scipy.stats.multivariate_normal` methods accept instances +of the following class to represent the covariance. + +.. autosummary:: + :toctree: generated/ + + Covariance -- Representation of a covariance matrix + + +Discrete distributions +---------------------- + +.. autosummary:: + :toctree: generated/ + + bernoulli -- Bernoulli + betabinom -- Beta-Binomial + betanbinom -- Beta-Negative Binomial + binom -- Binomial + boltzmann -- Boltzmann (Truncated Discrete Exponential) + dlaplace -- Discrete Laplacian + geom -- Geometric + hypergeom -- Hypergeometric + logser -- Logarithmic (Log-Series, Series) + nbinom -- Negative Binomial + nchypergeom_fisher -- Fisher's Noncentral Hypergeometric + nchypergeom_wallenius -- Wallenius's Noncentral Hypergeometric + nhypergeom -- Negative Hypergeometric + planck -- Planck (Discrete Exponential) + poisson -- Poisson + randint -- Discrete Uniform + skellam -- Skellam + yulesimon -- Yule-Simon + zipf -- Zipf (Zeta) + zipfian -- Zipfian + + +An overview of statistical functions is given below. Many of these functions +have a similar version in `scipy.stats.mstats` which work for masked arrays. + +Summary statistics +================== + +.. autosummary:: + :toctree: generated/ + + describe -- Descriptive statistics + gmean -- Geometric mean + hmean -- Harmonic mean + pmean -- Power mean + kurtosis -- Fisher or Pearson kurtosis + mode -- Modal value + moment -- Central moment + expectile -- Expectile + skew -- Skewness + kstat -- + kstatvar -- + tmean -- Truncated arithmetic mean + tvar -- Truncated variance + tmin -- + tmax -- + tstd -- + tsem -- + variation -- Coefficient of variation + find_repeats + rankdata + tiecorrect + trim_mean + gstd -- Geometric Standard Deviation + iqr + sem + bayes_mvs + mvsdist + entropy + differential_entropy + median_abs_deviation + +Frequency statistics +==================== + +.. autosummary:: + :toctree: generated/ + + cumfreq + percentileofscore + scoreatpercentile + relfreq + +.. autosummary:: + :toctree: generated/ + + binned_statistic -- Compute a binned statistic for a set of data. + binned_statistic_2d -- Compute a 2-D binned statistic for a set of data. + binned_statistic_dd -- Compute a d-D binned statistic for a set of data. + +Hypothesis Tests and related functions +====================================== +SciPy has many functions for performing hypothesis tests that return a +test statistic and a p-value, and several of them return confidence intervals +and/or other related information. + +The headings below are based on common uses of the functions within, but due to +the wide variety of statistical procedures, any attempt at coarse-grained +categorization will be imperfect. Also, note that tests within the same heading +are not interchangeable in general (e.g. many have different distributional +assumptions). + +One Sample Tests / Paired Sample Tests +-------------------------------------- +One sample tests are typically used to assess whether a single sample was +drawn from a specified distribution or a distribution with specified properties +(e.g. zero mean). + +.. autosummary:: + :toctree: generated/ + + ttest_1samp + binomtest + quantile_test + skewtest + kurtosistest + normaltest + jarque_bera + shapiro + anderson + cramervonmises + ks_1samp + goodness_of_fit + chisquare + power_divergence + +Paired sample tests are often used to assess whether two samples were drawn +from the same distribution; they differ from the independent sample tests below +in that each observation in one sample is treated as paired with a +closely-related observation in the other sample (e.g. when environmental +factors are controlled between observations within a pair but not among pairs). +They can also be interpreted or used as one-sample tests (e.g. tests on the +mean or median of *differences* between paired observations). + +.. autosummary:: + :toctree: generated/ + + ttest_rel + wilcoxon + +Association/Correlation Tests +----------------------------- + +These tests are often used to assess whether there is a relationship (e.g. +linear) between paired observations in multiple samples or among the +coordinates of multivariate observations. + +.. autosummary:: + :toctree: generated/ + + linregress + pearsonr + spearmanr + pointbiserialr + kendalltau + weightedtau + somersd + siegelslopes + theilslopes + page_trend_test + multiscale_graphcorr + +These association tests and are to work with samples in the form of contingency +tables. Supporting functions are available in `scipy.stats.contingency`. + +.. autosummary:: + :toctree: generated/ + + chi2_contingency + fisher_exact + barnard_exact + boschloo_exact + +Independent Sample Tests +------------------------ +Independent sample tests are typically used to assess whether multiple samples +were independently drawn from the same distribution or different distributions +with a shared property (e.g. equal means). + +Some tests are specifically for comparing two samples. + +.. autosummary:: + :toctree: generated/ + + ttest_ind_from_stats + poisson_means_test + ttest_ind + mannwhitneyu + bws_test + ranksums + brunnermunzel + mood + ansari + cramervonmises_2samp + epps_singleton_2samp + ks_2samp + kstest + +Others are generalized to multiple samples. + +.. autosummary:: + :toctree: generated/ + + f_oneway + tukey_hsd + dunnett + kruskal + alexandergovern + fligner + levene + bartlett + median_test + friedmanchisquare + anderson_ksamp + +Resampling and Monte Carlo Methods +---------------------------------- +The following functions can reproduce the p-value and confidence interval +results of most of the functions above, and often produce accurate results in a +wider variety of conditions. They can also be used to perform hypothesis tests +and generate confidence intervals for custom statistics. This flexibility comes +at the cost of greater computational requirements and stochastic results. + +.. autosummary:: + :toctree: generated/ + + monte_carlo_test + permutation_test + bootstrap + +Instances of the following object can be passed into some hypothesis test +functions to perform a resampling or Monte Carlo version of the hypothesis +test. + +.. autosummary:: + :toctree: generated/ + + MonteCarloMethod + PermutationMethod + BootstrapMethod + +Multiple Hypothesis Testing and Meta-Analysis +--------------------------------------------- +These functions are for assessing the results of individual tests as a whole. +Functions for performing specific multiple hypothesis tests (e.g. post hoc +tests) are listed above. + +.. autosummary:: + :toctree: generated/ + + combine_pvalues + false_discovery_control + + +The following functions are related to the tests above but do not belong in the +above categories. + +Quasi-Monte Carlo +================= + +.. toctree:: + :maxdepth: 4 + + stats.qmc + +Contingency Tables +================== + +.. toctree:: + :maxdepth: 4 + + stats.contingency + +Masked statistics functions +=========================== + +.. toctree:: + + stats.mstats + + +Other statistical functionality +=============================== + +Transformations +--------------- + +.. autosummary:: + :toctree: generated/ + + boxcox + boxcox_normmax + boxcox_llf + yeojohnson + yeojohnson_normmax + yeojohnson_llf + obrientransform + sigmaclip + trimboth + trim1 + zmap + zscore + gzscore + +Statistical distances +--------------------- + +.. autosummary:: + :toctree: generated/ + + wasserstein_distance + wasserstein_distance_nd + energy_distance + +Sampling +-------- + +.. toctree:: + :maxdepth: 4 + + stats.sampling + +Random variate generation / CDF Inversion +----------------------------------------- + +.. autosummary:: + :toctree: generated/ + + rvs_ratio_uniforms + +Fitting / Survival Analysis +--------------------------- + +.. autosummary:: + :toctree: generated/ + + fit + ecdf + logrank + +Directional statistical functions +--------------------------------- + +.. autosummary:: + :toctree: generated/ + + directional_stats + circmean + circvar + circstd + +Sensitivity Analysis +-------------------- + +.. autosummary:: + :toctree: generated/ + + sobol_indices + +Plot-tests +---------- + +.. autosummary:: + :toctree: generated/ + + ppcc_max + ppcc_plot + probplot + boxcox_normplot + yeojohnson_normplot + +Univariate and multivariate kernel density estimation +----------------------------------------------------- + +.. autosummary:: + :toctree: generated/ + + gaussian_kde + +Warnings / Errors used in :mod:`scipy.stats` +-------------------------------------------- + +.. autosummary:: + :toctree: generated/ + + DegenerateDataWarning + ConstantInputWarning + NearConstantInputWarning + FitError + +Result classes used in :mod:`scipy.stats` +----------------------------------------- + +.. warning:: + + These classes are private, but they are included here because instances + of them are returned by other statistical functions. User import and + instantiation is not supported. + +.. toctree:: + :maxdepth: 2 + + stats._result_classes + +""" # noqa: E501 + +from ._warnings_errors import (ConstantInputWarning, NearConstantInputWarning, + DegenerateDataWarning, FitError) +from ._stats_py import * +from ._variation import variation +from .distributions import * +from ._morestats import * +from ._multicomp import * +from ._binomtest import binomtest +from ._binned_statistic import * +from ._kde import gaussian_kde +from . import mstats +from . import qmc +from ._multivariate import * +from . import contingency +from .contingency import chi2_contingency +from ._censored_data import CensoredData +from ._resampling import (bootstrap, monte_carlo_test, permutation_test, + MonteCarloMethod, PermutationMethod, BootstrapMethod) +from ._entropy import * +from ._hypotests import * +from ._rvs_sampling import rvs_ratio_uniforms +from ._page_trend_test import page_trend_test +from ._mannwhitneyu import mannwhitneyu +from ._bws_test import bws_test +from ._fit import fit, goodness_of_fit +from ._covariance import Covariance +from ._sensitivity_analysis import * +from ._survival import * + +# Deprecated namespaces, to be removed in v2.0.0 +from . import ( + biasedurn, kde, morestats, mstats_basic, mstats_extras, mvn, stats +) + + +__all__ = [s for s in dir() if not s.startswith("_")] # Remove dunders. + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_axis_nan_policy.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_axis_nan_policy.py new file mode 100644 index 0000000000000000000000000000000000000000..b83274df7ec044b51fb5b378459e0c7d7e063af4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_axis_nan_policy.py @@ -0,0 +1,642 @@ +# Many scipy.stats functions support `axis` and `nan_policy` parameters. +# When the two are combined, it can be tricky to get all the behavior just +# right. This file contains utility functions useful for scipy.stats functions +# that support `axis` and `nan_policy`, including a decorator that +# automatically adds `axis` and `nan_policy` arguments to a function. + +import numpy as np +from functools import wraps +from scipy._lib._docscrape import FunctionDoc, Parameter +from scipy._lib._util import _contains_nan, AxisError, _get_nan +import inspect + + +def _broadcast_arrays(arrays, axis=None): + """ + Broadcast shapes of arrays, ignoring incompatibility of specified axes + """ + new_shapes = _broadcast_array_shapes(arrays, axis=axis) + if axis is None: + new_shapes = [new_shapes]*len(arrays) + return [np.broadcast_to(array, new_shape) + for array, new_shape in zip(arrays, new_shapes)] + + +def _broadcast_array_shapes(arrays, axis=None): + """ + Broadcast shapes of arrays, ignoring incompatibility of specified axes + """ + shapes = [np.asarray(arr).shape for arr in arrays] + return _broadcast_shapes(shapes, axis) + + +def _broadcast_shapes(shapes, axis=None): + """ + Broadcast shapes, ignoring incompatibility of specified axes + """ + if not shapes: + return shapes + + # input validation + if axis is not None: + axis = np.atleast_1d(axis) + axis_int = axis.astype(int) + if not np.array_equal(axis_int, axis): + raise AxisError('`axis` must be an integer, a ' + 'tuple of integers, or `None`.') + axis = axis_int + + # First, ensure all shapes have same number of dimensions by prepending 1s. + n_dims = max([len(shape) for shape in shapes]) + new_shapes = np.ones((len(shapes), n_dims), dtype=int) + for row, shape in zip(new_shapes, shapes): + row[len(row)-len(shape):] = shape # can't use negative indices (-0:) + + # Remove the shape elements of the axes to be ignored, but remember them. + if axis is not None: + axis[axis < 0] = n_dims + axis[axis < 0] + axis = np.sort(axis) + if axis[-1] >= n_dims or axis[0] < 0: + message = (f"`axis` is out of bounds " + f"for array of dimension {n_dims}") + raise AxisError(message) + + if len(np.unique(axis)) != len(axis): + raise AxisError("`axis` must contain only distinct elements") + + removed_shapes = new_shapes[:, axis] + new_shapes = np.delete(new_shapes, axis, axis=1) + + # If arrays are broadcastable, shape elements that are 1 may be replaced + # with a corresponding non-1 shape element. Assuming arrays are + # broadcastable, that final shape element can be found with: + new_shape = np.max(new_shapes, axis=0) + # except in case of an empty array: + new_shape *= new_shapes.all(axis=0) + + # Among all arrays, there can only be one unique non-1 shape element. + # Therefore, if any non-1 shape element does not match what we found + # above, the arrays must not be broadcastable after all. + if np.any(~((new_shapes == 1) | (new_shapes == new_shape))): + raise ValueError("Array shapes are incompatible for broadcasting.") + + if axis is not None: + # Add back the shape elements that were ignored + new_axis = axis - np.arange(len(axis)) + new_shapes = [tuple(np.insert(new_shape, new_axis, removed_shape)) + for removed_shape in removed_shapes] + return new_shapes + else: + return tuple(new_shape) + + +def _broadcast_array_shapes_remove_axis(arrays, axis=None): + """ + Broadcast shapes of arrays, dropping specified axes + + Given a sequence of arrays `arrays` and an integer or tuple `axis`, find + the shape of the broadcast result after consuming/dropping `axis`. + In other words, return output shape of a typical hypothesis test on + `arrays` vectorized along `axis`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats._axis_nan_policy import _broadcast_array_shapes + >>> a = np.zeros((5, 2, 1)) + >>> b = np.zeros((9, 3)) + >>> _broadcast_array_shapes((a, b), 1) + (5, 3) + """ + # Note that here, `axis=None` means do not consume/drop any axes - _not_ + # ravel arrays before broadcasting. + shapes = [arr.shape for arr in arrays] + return _broadcast_shapes_remove_axis(shapes, axis) + + +def _broadcast_shapes_remove_axis(shapes, axis=None): + """ + Broadcast shapes, dropping specified axes + + Same as _broadcast_array_shapes, but given a sequence + of array shapes `shapes` instead of the arrays themselves. + """ + shapes = _broadcast_shapes(shapes, axis) + shape = shapes[0] + if axis is not None: + shape = np.delete(shape, axis) + return tuple(shape) + + +def _broadcast_concatenate(arrays, axis, paired=False): + """Concatenate arrays along an axis with broadcasting.""" + arrays = _broadcast_arrays(arrays, axis if not paired else None) + res = np.concatenate(arrays, axis=axis) + return res + + +# TODO: add support for `axis` tuples +def _remove_nans(samples, paired): + "Remove nans from paired or unpaired 1D samples" + # potential optimization: don't copy arrays that don't contain nans + if not paired: + return [sample[~np.isnan(sample)] for sample in samples] + + # for paired samples, we need to remove the whole pair when any part + # has a nan + nans = np.isnan(samples[0]) + for sample in samples[1:]: + nans = nans | np.isnan(sample) + not_nans = ~nans + return [sample[not_nans] for sample in samples] + + +def _remove_sentinel(samples, paired, sentinel): + "Remove sentinel values from paired or unpaired 1D samples" + # could consolidate with `_remove_nans`, but it's not quite as simple as + # passing `sentinel=np.nan` because `(np.nan == np.nan) is False` + + # potential optimization: don't copy arrays that don't contain sentinel + if not paired: + return [sample[sample != sentinel] for sample in samples] + + # for paired samples, we need to remove the whole pair when any part + # has a nan + sentinels = (samples[0] == sentinel) + for sample in samples[1:]: + sentinels = sentinels | (sample == sentinel) + not_sentinels = ~sentinels + return [sample[not_sentinels] for sample in samples] + + +def _masked_arrays_2_sentinel_arrays(samples): + # masked arrays in `samples` are converted to regular arrays, and values + # corresponding with masked elements are replaced with a sentinel value + + # return without modifying arrays if none have a mask + has_mask = False + for sample in samples: + mask = getattr(sample, 'mask', False) + has_mask = has_mask or np.any(mask) + if not has_mask: + return samples, None # None means there is no sentinel value + + # Choose a sentinel value. We can't use `np.nan`, because sentinel (masked) + # values are always omitted, but there are different nan policies. + dtype = np.result_type(*samples) + dtype = dtype if np.issubdtype(dtype, np.number) else np.float64 + for i in range(len(samples)): + # Things get more complicated if the arrays are of different types. + # We could have different sentinel values for each array, but + # the purpose of this code is convenience, not efficiency. + samples[i] = samples[i].astype(dtype, copy=False) + + inexact = np.issubdtype(dtype, np.inexact) + info = np.finfo if inexact else np.iinfo + max_possible, min_possible = info(dtype).max, info(dtype).min + nextafter = np.nextafter if inexact else (lambda x, _: x - 1) + + sentinel = max_possible + # For simplicity, min_possible/np.infs are not candidate sentinel values + while sentinel > min_possible: + for sample in samples: + if np.any(sample == sentinel): # choose a new sentinel value + sentinel = nextafter(sentinel, -np.inf) + break + else: # when sentinel value is OK, break the while loop + break + else: + message = ("This function replaces masked elements with sentinel " + "values, but the data contains all distinct values of this " + "data type. Consider promoting the dtype to `np.float64`.") + raise ValueError(message) + + # replace masked elements with sentinel value + out_samples = [] + for sample in samples: + mask = getattr(sample, 'mask', None) + if mask is not None: # turn all masked arrays into sentinel arrays + mask = np.broadcast_to(mask, sample.shape) + sample = sample.data.copy() if np.any(mask) else sample.data + sample = np.asarray(sample) # `sample.data` could be a memoryview? + sample[mask] = sentinel + out_samples.append(sample) + + return out_samples, sentinel + + +def _check_empty_inputs(samples, axis): + """ + Check for empty sample; return appropriate output for a vectorized hypotest + """ + # if none of the samples are empty, we need to perform the test + if not any(sample.size == 0 for sample in samples): + return None + # otherwise, the statistic and p-value will be either empty arrays or + # arrays with NaNs. Produce the appropriate array and return it. + output_shape = _broadcast_array_shapes_remove_axis(samples, axis) + output = np.ones(output_shape) * _get_nan(*samples) + return output + + +def _add_reduced_axes(res, reduced_axes, keepdims): + """ + Add reduced axes back to all the arrays in the result object + if keepdims = True. + """ + return ([np.expand_dims(output, reduced_axes) for output in res] + if keepdims else res) + + +# Standard docstring / signature entries for `axis`, `nan_policy`, `keepdims` +_name = 'axis' +_desc = ( + """If an int, the axis of the input along which to compute the statistic. +The statistic of each axis-slice (e.g. row) of the input will appear in a +corresponding element of the output. +If ``None``, the input will be raveled before computing the statistic.""" + .split('\n')) + + +def _get_axis_params(default_axis=0, _name=_name, _desc=_desc): # bind NOW + _type = f"int or None, default: {default_axis}" + _axis_parameter_doc = Parameter(_name, _type, _desc) + _axis_parameter = inspect.Parameter(_name, + inspect.Parameter.KEYWORD_ONLY, + default=default_axis) + return _axis_parameter_doc, _axis_parameter + + +_name = 'nan_policy' +_type = "{'propagate', 'omit', 'raise'}" +_desc = ( + """Defines how to handle input NaNs. + +- ``propagate``: if a NaN is present in the axis slice (e.g. row) along + which the statistic is computed, the corresponding entry of the output + will be NaN. +- ``omit``: NaNs will be omitted when performing the calculation. + If insufficient data remains in the axis slice along which the + statistic is computed, the corresponding entry of the output will be + NaN. +- ``raise``: if a NaN is present, a ``ValueError`` will be raised.""" + .split('\n')) +_nan_policy_parameter_doc = Parameter(_name, _type, _desc) +_nan_policy_parameter = inspect.Parameter(_name, + inspect.Parameter.KEYWORD_ONLY, + default='propagate') + +_name = 'keepdims' +_type = "bool, default: False" +_desc = ( + """If this is set to True, the axes which are reduced are left +in the result as dimensions with size one. With this option, +the result will broadcast correctly against the input array.""" + .split('\n')) +_keepdims_parameter_doc = Parameter(_name, _type, _desc) +_keepdims_parameter = inspect.Parameter(_name, + inspect.Parameter.KEYWORD_ONLY, + default=False) + +_standard_note_addition = ( + """\nBeginning in SciPy 1.9, ``np.matrix`` inputs (not recommended for new +code) are converted to ``np.ndarray`` before the calculation is performed. In +this case, the output will be a scalar or ``np.ndarray`` of appropriate shape +rather than a 2D ``np.matrix``. Similarly, while masked elements of masked +arrays are ignored, the output will be a scalar or ``np.ndarray`` rather than a +masked array with ``mask=False``.""").split('\n') + + +def _axis_nan_policy_factory(tuple_to_result, default_axis=0, + n_samples=1, paired=False, + result_to_tuple=None, too_small=0, + n_outputs=2, kwd_samples=[], override=None): + """Factory for a wrapper that adds axis/nan_policy params to a function. + + Parameters + ---------- + tuple_to_result : callable + Callable that returns an object of the type returned by the function + being wrapped (e.g. the namedtuple or dataclass returned by a + statistical test) provided the separate components (e.g. statistic, + pvalue). + default_axis : int, default: 0 + The default value of the axis argument. Standard is 0 except when + backwards compatibility demands otherwise (e.g. `None`). + n_samples : int or callable, default: 1 + The number of data samples accepted by the function + (e.g. `mannwhitneyu`), a callable that accepts a dictionary of + parameters passed into the function and returns the number of data + samples (e.g. `wilcoxon`), or `None` to indicate an arbitrary number + of samples (e.g. `kruskal`). + paired : {False, True} + Whether the function being wrapped treats the samples as paired (i.e. + corresponding elements of each sample should be considered as different + components of the same sample.) + result_to_tuple : callable, optional + Function that unpacks the results of the function being wrapped into + a tuple. This is essentially the inverse of `tuple_to_result`. Default + is `None`, which is appropriate for statistical tests that return a + statistic, pvalue tuple (rather than, e.g., a non-iterable datalass). + too_small : int or callable, default: 0 + The largest unnacceptably small sample for the function being wrapped. + For example, some functions require samples of size two or more or they + raise an error. This argument prevents the error from being raised when + input is not 1D and instead places a NaN in the corresponding element + of the result. If callable, it must accept a list of samples, axis, + and a dictionary of keyword arguments passed to the wrapper function as + arguments and return a bool indicating weather the samples passed are + too small. + n_outputs : int or callable, default: 2 + The number of outputs produced by the function given 1d sample(s). For + example, hypothesis tests that return a namedtuple or result object + with attributes ``statistic`` and ``pvalue`` use the default + ``n_outputs=2``; summary statistics with scalar output use + ``n_outputs=1``. Alternatively, may be a callable that accepts a + dictionary of arguments passed into the wrapped function and returns + the number of outputs corresponding with those arguments. + kwd_samples : sequence, default: [] + The names of keyword parameters that should be treated as samples. For + example, `gmean` accepts as its first argument a sample `a` but + also `weights` as a fourth, optional keyword argument. In this case, we + use `n_samples=1` and kwd_samples=['weights']. + override : dict, default: {'vectorization': False, 'nan_propagation': True} + Pass a dictionary with ``'vectorization': True`` to ensure that the + decorator overrides the function's behavior for multimensional input. + Use ``'nan_propagation': False`` to ensure that the decorator does not + override the function's behavior for ``nan_policy='propagate'``. + (See `scipy.stats.mode`, for example.) + """ + # Specify which existing behaviors the decorator must override + temp = override or {} + override = {'vectorization': False, + 'nan_propagation': True} + override.update(temp) + + if result_to_tuple is None: + def result_to_tuple(res): + return res + + if not callable(too_small): + def is_too_small(samples, *ts_args, axis=-1, **ts_kwargs): + for sample in samples: + if sample.shape[axis] <= too_small: + return True + return False + else: + is_too_small = too_small + + def axis_nan_policy_decorator(hypotest_fun_in): + @wraps(hypotest_fun_in) + def axis_nan_policy_wrapper(*args, _no_deco=False, **kwds): + + if _no_deco: # for testing, decorator does nothing + return hypotest_fun_in(*args, **kwds) + + # We need to be flexible about whether position or keyword + # arguments are used, but we need to make sure users don't pass + # both for the same parameter. To complicate matters, some + # functions accept samples with *args, and some functions already + # accept `axis` and `nan_policy` as positional arguments. + # The strategy is to make sure that there is no duplication + # between `args` and `kwds`, combine the two into `kwds`, then + # the samples, `nan_policy`, and `axis` from `kwds`, as they are + # dealt with separately. + + # Check for intersection between positional and keyword args + params = list(inspect.signature(hypotest_fun_in).parameters) + if n_samples is None: + # Give unique names to each positional sample argument + # Note that *args can't be provided as a keyword argument + params = [f"arg{i}" for i in range(len(args))] + params[1:] + + # raise if there are too many positional args + maxarg = (np.inf if inspect.getfullargspec(hypotest_fun_in).varargs + else len(inspect.getfullargspec(hypotest_fun_in).args)) + if len(args) > maxarg: # let the function raise the right error + hypotest_fun_in(*args, **kwds) + + # raise if multiple values passed for same parameter + d_args = dict(zip(params, args)) + intersection = set(d_args) & set(kwds) + if intersection: # let the function raise the right error + hypotest_fun_in(*args, **kwds) + + # Consolidate other positional and keyword args into `kwds` + kwds.update(d_args) + + # rename avoids UnboundLocalError + if callable(n_samples): + # Future refactoring idea: no need for callable n_samples. + # Just replace `n_samples` and `kwd_samples` with a single + # list of the names of all samples, and treat all of them + # as `kwd_samples` are treated below. + n_samp = n_samples(kwds) + else: + n_samp = n_samples or len(args) + + # get the number of outputs + n_out = n_outputs # rename to avoid UnboundLocalError + if callable(n_out): + n_out = n_out(kwds) + + # If necessary, rearrange function signature: accept other samples + # as positional args right after the first n_samp args + kwd_samp = [name for name in kwd_samples + if kwds.get(name, None) is not None] + n_kwd_samp = len(kwd_samp) + if not kwd_samp: + hypotest_fun_out = hypotest_fun_in + else: + def hypotest_fun_out(*samples, **kwds): + new_kwds = dict(zip(kwd_samp, samples[n_samp:])) + kwds.update(new_kwds) + return hypotest_fun_in(*samples[:n_samp], **kwds) + + # Extract the things we need here + try: # if something is missing + samples = [np.atleast_1d(kwds.pop(param)) + for param in (params[:n_samp] + kwd_samp)] + except KeyError: # let the function raise the right error + # might need to revisit this if required arg is not a "sample" + hypotest_fun_in(*args, **kwds) + vectorized = True if 'axis' in params else False + vectorized = vectorized and not override['vectorization'] + axis = kwds.pop('axis', default_axis) + nan_policy = kwds.pop('nan_policy', 'propagate') + keepdims = kwds.pop("keepdims", False) + del args # avoid the possibility of passing both `args` and `kwds` + + # convert masked arrays to regular arrays with sentinel values + samples, sentinel = _masked_arrays_2_sentinel_arrays(samples) + + # standardize to always work along last axis + reduced_axes = axis + if axis is None: + if samples: + # when axis=None, take the maximum of all dimensions since + # all the dimensions are reduced. + n_dims = np.max([sample.ndim for sample in samples]) + reduced_axes = tuple(range(n_dims)) + samples = [np.asarray(sample.ravel()) for sample in samples] + else: + samples = _broadcast_arrays(samples, axis=axis) + axis = np.atleast_1d(axis) + n_axes = len(axis) + # move all axes in `axis` to the end to be raveled + samples = [np.moveaxis(sample, axis, range(-len(axis), 0)) + for sample in samples] + shapes = [sample.shape for sample in samples] + # New shape is unchanged for all axes _not_ in `axis` + # At the end, we append the product of the shapes of the axes + # in `axis`. Appending -1 doesn't work for zero-size arrays! + new_shapes = [shape[:-n_axes] + (np.prod(shape[-n_axes:]),) + for shape in shapes] + samples = [sample.reshape(new_shape) + for sample, new_shape in zip(samples, new_shapes)] + axis = -1 # work over the last axis + NaN = _get_nan(*samples) + + # if axis is not needed, just handle nan_policy and return + ndims = np.array([sample.ndim for sample in samples]) + if np.all(ndims <= 1): + # Addresses nan_policy == "raise" + if nan_policy != 'propagate' or override['nan_propagation']: + contains_nan = [_contains_nan(sample, nan_policy)[0] + for sample in samples] + else: + # Behave as though there are no NaNs (even if there are) + contains_nan = [False]*len(samples) + + # Addresses nan_policy == "propagate" + if any(contains_nan) and (nan_policy == 'propagate' + and override['nan_propagation']): + res = np.full(n_out, NaN) + res = _add_reduced_axes(res, reduced_axes, keepdims) + return tuple_to_result(*res) + + # Addresses nan_policy == "omit" + if any(contains_nan) and nan_policy == 'omit': + # consider passing in contains_nan + samples = _remove_nans(samples, paired) + + # ideally, this is what the behavior would be: + # if is_too_small(samples): + # return tuple_to_result(NaN, NaN) + # but some existing functions raise exceptions, and changing + # behavior of those would break backward compatibility. + + if sentinel: + samples = _remove_sentinel(samples, paired, sentinel) + res = hypotest_fun_out(*samples, **kwds) + res = result_to_tuple(res) + res = _add_reduced_axes(res, reduced_axes, keepdims) + return tuple_to_result(*res) + + # check for empty input + # ideally, move this to the top, but some existing functions raise + # exceptions for empty input, so overriding it would break + # backward compatibility. + empty_output = _check_empty_inputs(samples, axis) + # only return empty output if zero sized input is too small. + if ( + empty_output is not None + and (is_too_small(samples, kwds) or empty_output.size == 0) + ): + res = [empty_output.copy() for i in range(n_out)] + res = _add_reduced_axes(res, reduced_axes, keepdims) + return tuple_to_result(*res) + + # otherwise, concatenate all samples along axis, remembering where + # each separate sample begins + lengths = np.array([sample.shape[axis] for sample in samples]) + split_indices = np.cumsum(lengths) + x = _broadcast_concatenate(samples, axis) + + # Addresses nan_policy == "raise" + if nan_policy != 'propagate' or override['nan_propagation']: + contains_nan, _ = _contains_nan(x, nan_policy) + else: + contains_nan = False # behave like there are no NaNs + + if vectorized and not contains_nan and not sentinel: + res = hypotest_fun_out(*samples, axis=axis, **kwds) + res = result_to_tuple(res) + res = _add_reduced_axes(res, reduced_axes, keepdims) + return tuple_to_result(*res) + + # Addresses nan_policy == "omit" + if contains_nan and nan_policy == 'omit': + def hypotest_fun(x): + samples = np.split(x, split_indices)[:n_samp+n_kwd_samp] + samples = _remove_nans(samples, paired) + if sentinel: + samples = _remove_sentinel(samples, paired, sentinel) + if is_too_small(samples, kwds): + return np.full(n_out, NaN) + return result_to_tuple(hypotest_fun_out(*samples, **kwds)) + + # Addresses nan_policy == "propagate" + elif (contains_nan and nan_policy == 'propagate' + and override['nan_propagation']): + def hypotest_fun(x): + if np.isnan(x).any(): + return np.full(n_out, NaN) + + samples = np.split(x, split_indices)[:n_samp+n_kwd_samp] + if sentinel: + samples = _remove_sentinel(samples, paired, sentinel) + if is_too_small(samples, kwds): + return np.full(n_out, NaN) + return result_to_tuple(hypotest_fun_out(*samples, **kwds)) + + else: + def hypotest_fun(x): + samples = np.split(x, split_indices)[:n_samp+n_kwd_samp] + if sentinel: + samples = _remove_sentinel(samples, paired, sentinel) + if is_too_small(samples, kwds): + return np.full(n_out, NaN) + return result_to_tuple(hypotest_fun_out(*samples, **kwds)) + + x = np.moveaxis(x, axis, 0) + res = np.apply_along_axis(hypotest_fun, axis=0, arr=x) + res = _add_reduced_axes(res, reduced_axes, keepdims) + return tuple_to_result(*res) + + _axis_parameter_doc, _axis_parameter = _get_axis_params(default_axis) + doc = FunctionDoc(axis_nan_policy_wrapper) + parameter_names = [param.name for param in doc['Parameters']] + if 'axis' in parameter_names: + doc['Parameters'][parameter_names.index('axis')] = ( + _axis_parameter_doc) + else: + doc['Parameters'].append(_axis_parameter_doc) + if 'nan_policy' in parameter_names: + doc['Parameters'][parameter_names.index('nan_policy')] = ( + _nan_policy_parameter_doc) + else: + doc['Parameters'].append(_nan_policy_parameter_doc) + if 'keepdims' in parameter_names: + doc['Parameters'][parameter_names.index('keepdims')] = ( + _keepdims_parameter_doc) + else: + doc['Parameters'].append(_keepdims_parameter_doc) + doc['Notes'] += _standard_note_addition + doc = str(doc).split("\n", 1)[1] # remove signature + axis_nan_policy_wrapper.__doc__ = str(doc) + + sig = inspect.signature(axis_nan_policy_wrapper) + parameters = sig.parameters + parameter_list = list(parameters.values()) + if 'axis' not in parameters: + parameter_list.append(_axis_parameter) + if 'nan_policy' not in parameters: + parameter_list.append(_nan_policy_parameter) + if 'keepdims' not in parameters: + parameter_list.append(_keepdims_parameter) + sig = sig.replace(parameters=parameter_list) + axis_nan_policy_wrapper.__signature__ = sig + + return axis_nan_policy_wrapper + return axis_nan_policy_decorator diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_biasedurn.pxd b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_biasedurn.pxd new file mode 100644 index 0000000000000000000000000000000000000000..92785f08dbec30a4db286fcb85b42d7221e2228e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_biasedurn.pxd @@ -0,0 +1,27 @@ +# Declare the class with cdef +cdef extern from "biasedurn/stocc.h" nogil: + cdef cppclass CFishersNCHypergeometric: + CFishersNCHypergeometric(int, int, int, double, double) except + + int mode() + double mean() + double variance() + double probability(int x) + double moments(double * mean, double * var) + + cdef cppclass CWalleniusNCHypergeometric: + CWalleniusNCHypergeometric() except + + CWalleniusNCHypergeometric(int, int, int, double, double) except + + int mode() + double mean() + double variance() + double probability(int x) + double moments(double * mean, double * var) + + cdef cppclass StochasticLib3: + StochasticLib3(int seed) except + + double Random() except + + void SetAccuracy(double accur) + int FishersNCHyp (int n, int m, int N, double odds) except + + int WalleniusNCHyp (int n, int m, int N, double odds) except + + double(*next_double)() + double(*next_normal)(const double m, const double s) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binned_statistic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binned_statistic.py new file mode 100644 index 0000000000000000000000000000000000000000..c624bb8c79be23ad515d003649f9efe9ea3e775d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binned_statistic.py @@ -0,0 +1,795 @@ +import builtins +from warnings import catch_warnings, simplefilter +import numpy as np +from operator import index +from collections import namedtuple + +__all__ = ['binned_statistic', + 'binned_statistic_2d', + 'binned_statistic_dd'] + + +BinnedStatisticResult = namedtuple('BinnedStatisticResult', + ('statistic', 'bin_edges', 'binnumber')) + + +def binned_statistic(x, values, statistic='mean', + bins=10, range=None): + """ + Compute a binned statistic for one or more sets of data. + + This is a generalization of a histogram function. A histogram divides + the space into bins, and returns the count of the number of points in + each bin. This function allows the computation of the sum, mean, median, + or other statistic of the values (or set of values) within each bin. + + Parameters + ---------- + x : (N,) array_like + A sequence of values to be binned. + values : (N,) array_like or list of (N,) array_like + The data on which the statistic will be computed. This must be + the same shape as `x`, or a set of sequences - each the same shape as + `x`. If `values` is a set of sequences, the statistic will be computed + on each independently. + statistic : string or callable, optional + The statistic to compute (default is 'mean'). + The following statistics are available: + + * 'mean' : compute the mean of values for points within each bin. + Empty bins will be represented by NaN. + * 'std' : compute the standard deviation within each bin. This + is implicitly calculated with ddof=0. + * 'median' : compute the median of values for points within each + bin. Empty bins will be represented by NaN. + * 'count' : compute the count of points within each bin. This is + identical to an unweighted histogram. `values` array is not + referenced. + * 'sum' : compute the sum of values for points within each bin. + This is identical to a weighted histogram. + * 'min' : compute the minimum of values for points within each bin. + Empty bins will be represented by NaN. + * 'max' : compute the maximum of values for point within each bin. + Empty bins will be represented by NaN. + * function : a user-defined function which takes a 1D array of + values, and outputs a single numerical statistic. This function + will be called on the values in each bin. Empty bins will be + represented by function([]), or NaN if this returns an error. + + bins : int or sequence of scalars, optional + If `bins` is an int, it defines the number of equal-width bins in the + given range (10 by default). If `bins` is a sequence, it defines the + bin edges, including the rightmost edge, allowing for non-uniform bin + widths. Values in `x` that are smaller than lowest bin edge are + assigned to bin number 0, values beyond the highest bin are assigned to + ``bins[-1]``. If the bin edges are specified, the number of bins will + be, (nx = len(bins)-1). + range : (float, float) or [(float, float)], optional + The lower and upper range of the bins. If not provided, range + is simply ``(x.min(), x.max())``. Values outside the range are + ignored. + + Returns + ------- + statistic : array + The values of the selected statistic in each bin. + bin_edges : array of dtype float + Return the bin edges ``(length(statistic)+1)``. + binnumber: 1-D ndarray of ints + Indices of the bins (corresponding to `bin_edges`) in which each value + of `x` belongs. Same length as `values`. A binnumber of `i` means the + corresponding value is between (bin_edges[i-1], bin_edges[i]). + + See Also + -------- + numpy.digitize, numpy.histogram, binned_statistic_2d, binned_statistic_dd + + Notes + ----- + All but the last (righthand-most) bin is half-open. In other words, if + `bins` is ``[1, 2, 3, 4]``, then the first bin is ``[1, 2)`` (including 1, + but excluding 2) and the second ``[2, 3)``. The last bin, however, is + ``[3, 4]``, which *includes* 4. + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + First some basic examples: + + Create two evenly spaced bins in the range of the given sample, and sum the + corresponding values in each of those bins: + + >>> values = [1.0, 1.0, 2.0, 1.5, 3.0] + >>> stats.binned_statistic([1, 1, 2, 5, 7], values, 'sum', bins=2) + BinnedStatisticResult(statistic=array([4. , 4.5]), + bin_edges=array([1., 4., 7.]), binnumber=array([1, 1, 1, 2, 2])) + + Multiple arrays of values can also be passed. The statistic is calculated + on each set independently: + + >>> values = [[1.0, 1.0, 2.0, 1.5, 3.0], [2.0, 2.0, 4.0, 3.0, 6.0]] + >>> stats.binned_statistic([1, 1, 2, 5, 7], values, 'sum', bins=2) + BinnedStatisticResult(statistic=array([[4. , 4.5], + [8. , 9. ]]), bin_edges=array([1., 4., 7.]), + binnumber=array([1, 1, 1, 2, 2])) + + >>> stats.binned_statistic([1, 2, 1, 2, 4], np.arange(5), statistic='mean', + ... bins=3) + BinnedStatisticResult(statistic=array([1., 2., 4.]), + bin_edges=array([1., 2., 3., 4.]), + binnumber=array([1, 2, 1, 2, 3])) + + As a second example, we now generate some random data of sailing boat speed + as a function of wind speed, and then determine how fast our boat is for + certain wind speeds: + + >>> rng = np.random.default_rng() + >>> windspeed = 8 * rng.random(500) + >>> boatspeed = .3 * windspeed**.5 + .2 * rng.random(500) + >>> bin_means, bin_edges, binnumber = stats.binned_statistic(windspeed, + ... boatspeed, statistic='median', bins=[1,2,3,4,5,6,7]) + >>> plt.figure() + >>> plt.plot(windspeed, boatspeed, 'b.', label='raw data') + >>> plt.hlines(bin_means, bin_edges[:-1], bin_edges[1:], colors='g', lw=5, + ... label='binned statistic of data') + >>> plt.legend() + + Now we can use ``binnumber`` to select all datapoints with a windspeed + below 1: + + >>> low_boatspeed = boatspeed[binnumber == 0] + + As a final example, we will use ``bin_edges`` and ``binnumber`` to make a + plot of a distribution that shows the mean and distribution around that + mean per bin, on top of a regular histogram and the probability + distribution function: + + >>> x = np.linspace(0, 5, num=500) + >>> x_pdf = stats.maxwell.pdf(x) + >>> samples = stats.maxwell.rvs(size=10000) + + >>> bin_means, bin_edges, binnumber = stats.binned_statistic(x, x_pdf, + ... statistic='mean', bins=25) + >>> bin_width = (bin_edges[1] - bin_edges[0]) + >>> bin_centers = bin_edges[1:] - bin_width/2 + + >>> plt.figure() + >>> plt.hist(samples, bins=50, density=True, histtype='stepfilled', + ... alpha=0.2, label='histogram of data') + >>> plt.plot(x, x_pdf, 'r-', label='analytical pdf') + >>> plt.hlines(bin_means, bin_edges[:-1], bin_edges[1:], colors='g', lw=2, + ... label='binned statistic of data') + >>> plt.plot((binnumber - 0.5) * bin_width, x_pdf, 'g.', alpha=0.5) + >>> plt.legend(fontsize=10) + >>> plt.show() + + """ + try: + N = len(bins) + except TypeError: + N = 1 + + if N != 1: + bins = [np.asarray(bins, float)] + + if range is not None: + if len(range) == 2: + range = [range] + + medians, edges, binnumbers = binned_statistic_dd( + [x], values, statistic, bins, range) + + return BinnedStatisticResult(medians, edges[0], binnumbers) + + +BinnedStatistic2dResult = namedtuple('BinnedStatistic2dResult', + ('statistic', 'x_edge', 'y_edge', + 'binnumber')) + + +def binned_statistic_2d(x, y, values, statistic='mean', + bins=10, range=None, expand_binnumbers=False): + """ + Compute a bidimensional binned statistic for one or more sets of data. + + This is a generalization of a histogram2d function. A histogram divides + the space into bins, and returns the count of the number of points in + each bin. This function allows the computation of the sum, mean, median, + or other statistic of the values (or set of values) within each bin. + + Parameters + ---------- + x : (N,) array_like + A sequence of values to be binned along the first dimension. + y : (N,) array_like + A sequence of values to be binned along the second dimension. + values : (N,) array_like or list of (N,) array_like + The data on which the statistic will be computed. This must be + the same shape as `x`, or a list of sequences - each with the same + shape as `x`. If `values` is such a list, the statistic will be + computed on each independently. + statistic : string or callable, optional + The statistic to compute (default is 'mean'). + The following statistics are available: + + * 'mean' : compute the mean of values for points within each bin. + Empty bins will be represented by NaN. + * 'std' : compute the standard deviation within each bin. This + is implicitly calculated with ddof=0. + * 'median' : compute the median of values for points within each + bin. Empty bins will be represented by NaN. + * 'count' : compute the count of points within each bin. This is + identical to an unweighted histogram. `values` array is not + referenced. + * 'sum' : compute the sum of values for points within each bin. + This is identical to a weighted histogram. + * 'min' : compute the minimum of values for points within each bin. + Empty bins will be represented by NaN. + * 'max' : compute the maximum of values for point within each bin. + Empty bins will be represented by NaN. + * function : a user-defined function which takes a 1D array of + values, and outputs a single numerical statistic. This function + will be called on the values in each bin. Empty bins will be + represented by function([]), or NaN if this returns an error. + + bins : int or [int, int] or array_like or [array, array], optional + The bin specification: + + * the number of bins for the two dimensions (nx = ny = bins), + * the number of bins in each dimension (nx, ny = bins), + * the bin edges for the two dimensions (x_edge = y_edge = bins), + * the bin edges in each dimension (x_edge, y_edge = bins). + + If the bin edges are specified, the number of bins will be, + (nx = len(x_edge)-1, ny = len(y_edge)-1). + + range : (2,2) array_like, optional + The leftmost and rightmost edges of the bins along each dimension + (if not specified explicitly in the `bins` parameters): + [[xmin, xmax], [ymin, ymax]]. All values outside of this range will be + considered outliers and not tallied in the histogram. + expand_binnumbers : bool, optional + 'False' (default): the returned `binnumber` is a shape (N,) array of + linearized bin indices. + 'True': the returned `binnumber` is 'unraveled' into a shape (2,N) + ndarray, where each row gives the bin numbers in the corresponding + dimension. + See the `binnumber` returned value, and the `Examples` section. + + .. versionadded:: 0.17.0 + + Returns + ------- + statistic : (nx, ny) ndarray + The values of the selected statistic in each two-dimensional bin. + x_edge : (nx + 1) ndarray + The bin edges along the first dimension. + y_edge : (ny + 1) ndarray + The bin edges along the second dimension. + binnumber : (N,) array of ints or (2,N) ndarray of ints + This assigns to each element of `sample` an integer that represents the + bin in which this observation falls. The representation depends on the + `expand_binnumbers` argument. See `Notes` for details. + + + See Also + -------- + numpy.digitize, numpy.histogram2d, binned_statistic, binned_statistic_dd + + Notes + ----- + Binedges: + All but the last (righthand-most) bin is half-open. In other words, if + `bins` is ``[1, 2, 3, 4]``, then the first bin is ``[1, 2)`` (including 1, + but excluding 2) and the second ``[2, 3)``. The last bin, however, is + ``[3, 4]``, which *includes* 4. + + `binnumber`: + This returned argument assigns to each element of `sample` an integer that + represents the bin in which it belongs. The representation depends on the + `expand_binnumbers` argument. If 'False' (default): The returned + `binnumber` is a shape (N,) array of linearized indices mapping each + element of `sample` to its corresponding bin (using row-major ordering). + Note that the returned linearized bin indices are used for an array with + extra bins on the outer binedges to capture values outside of the defined + bin bounds. + If 'True': The returned `binnumber` is a shape (2,N) ndarray where + each row indicates bin placements for each dimension respectively. In each + dimension, a binnumber of `i` means the corresponding value is between + (D_edge[i-1], D_edge[i]), where 'D' is either 'x' or 'y'. + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> from scipy import stats + + Calculate the counts with explicit bin-edges: + + >>> x = [0.1, 0.1, 0.1, 0.6] + >>> y = [2.1, 2.6, 2.1, 2.1] + >>> binx = [0.0, 0.5, 1.0] + >>> biny = [2.0, 2.5, 3.0] + >>> ret = stats.binned_statistic_2d(x, y, None, 'count', bins=[binx, biny]) + >>> ret.statistic + array([[2., 1.], + [1., 0.]]) + + The bin in which each sample is placed is given by the `binnumber` + returned parameter. By default, these are the linearized bin indices: + + >>> ret.binnumber + array([5, 6, 5, 9]) + + The bin indices can also be expanded into separate entries for each + dimension using the `expand_binnumbers` parameter: + + >>> ret = stats.binned_statistic_2d(x, y, None, 'count', bins=[binx, biny], + ... expand_binnumbers=True) + >>> ret.binnumber + array([[1, 1, 1, 2], + [1, 2, 1, 1]]) + + Which shows that the first three elements belong in the xbin 1, and the + fourth into xbin 2; and so on for y. + + """ + + # This code is based on np.histogram2d + try: + N = len(bins) + except TypeError: + N = 1 + + if N != 1 and N != 2: + xedges = yedges = np.asarray(bins, float) + bins = [xedges, yedges] + + medians, edges, binnumbers = binned_statistic_dd( + [x, y], values, statistic, bins, range, + expand_binnumbers=expand_binnumbers) + + return BinnedStatistic2dResult(medians, edges[0], edges[1], binnumbers) + + +BinnedStatisticddResult = namedtuple('BinnedStatisticddResult', + ('statistic', 'bin_edges', + 'binnumber')) + + +def _bincount(x, weights): + if np.iscomplexobj(weights): + a = np.bincount(x, np.real(weights)) + b = np.bincount(x, np.imag(weights)) + z = a + b*1j + + else: + z = np.bincount(x, weights) + return z + + +def binned_statistic_dd(sample, values, statistic='mean', + bins=10, range=None, expand_binnumbers=False, + binned_statistic_result=None): + """ + Compute a multidimensional binned statistic for a set of data. + + This is a generalization of a histogramdd function. A histogram divides + the space into bins, and returns the count of the number of points in + each bin. This function allows the computation of the sum, mean, median, + or other statistic of the values within each bin. + + Parameters + ---------- + sample : array_like + Data to histogram passed as a sequence of N arrays of length D, or + as an (N,D) array. + values : (N,) array_like or list of (N,) array_like + The data on which the statistic will be computed. This must be + the same shape as `sample`, or a list of sequences - each with the + same shape as `sample`. If `values` is such a list, the statistic + will be computed on each independently. + statistic : string or callable, optional + The statistic to compute (default is 'mean'). + The following statistics are available: + + * 'mean' : compute the mean of values for points within each bin. + Empty bins will be represented by NaN. + * 'median' : compute the median of values for points within each + bin. Empty bins will be represented by NaN. + * 'count' : compute the count of points within each bin. This is + identical to an unweighted histogram. `values` array is not + referenced. + * 'sum' : compute the sum of values for points within each bin. + This is identical to a weighted histogram. + * 'std' : compute the standard deviation within each bin. This + is implicitly calculated with ddof=0. If the number of values + within a given bin is 0 or 1, the computed standard deviation value + will be 0 for the bin. + * 'min' : compute the minimum of values for points within each bin. + Empty bins will be represented by NaN. + * 'max' : compute the maximum of values for point within each bin. + Empty bins will be represented by NaN. + * function : a user-defined function which takes a 1D array of + values, and outputs a single numerical statistic. This function + will be called on the values in each bin. Empty bins will be + represented by function([]), or NaN if this returns an error. + + bins : sequence or positive int, optional + The bin specification must be in one of the following forms: + + * A sequence of arrays describing the bin edges along each dimension. + * The number of bins for each dimension (nx, ny, ... = bins). + * The number of bins for all dimensions (nx = ny = ... = bins). + range : sequence, optional + A sequence of lower and upper bin edges to be used if the edges are + not given explicitly in `bins`. Defaults to the minimum and maximum + values along each dimension. + expand_binnumbers : bool, optional + 'False' (default): the returned `binnumber` is a shape (N,) array of + linearized bin indices. + 'True': the returned `binnumber` is 'unraveled' into a shape (D,N) + ndarray, where each row gives the bin numbers in the corresponding + dimension. + See the `binnumber` returned value, and the `Examples` section of + `binned_statistic_2d`. + binned_statistic_result : binnedStatisticddResult + Result of a previous call to the function in order to reuse bin edges + and bin numbers with new values and/or a different statistic. + To reuse bin numbers, `expand_binnumbers` must have been set to False + (the default) + + .. versionadded:: 0.17.0 + + Returns + ------- + statistic : ndarray, shape(nx1, nx2, nx3,...) + The values of the selected statistic in each two-dimensional bin. + bin_edges : list of ndarrays + A list of D arrays describing the (nxi + 1) bin edges for each + dimension. + binnumber : (N,) array of ints or (D,N) ndarray of ints + This assigns to each element of `sample` an integer that represents the + bin in which this observation falls. The representation depends on the + `expand_binnumbers` argument. See `Notes` for details. + + + See Also + -------- + numpy.digitize, numpy.histogramdd, binned_statistic, binned_statistic_2d + + Notes + ----- + Binedges: + All but the last (righthand-most) bin is half-open in each dimension. In + other words, if `bins` is ``[1, 2, 3, 4]``, then the first bin is + ``[1, 2)`` (including 1, but excluding 2) and the second ``[2, 3)``. The + last bin, however, is ``[3, 4]``, which *includes* 4. + + `binnumber`: + This returned argument assigns to each element of `sample` an integer that + represents the bin in which it belongs. The representation depends on the + `expand_binnumbers` argument. If 'False' (default): The returned + `binnumber` is a shape (N,) array of linearized indices mapping each + element of `sample` to its corresponding bin (using row-major ordering). + If 'True': The returned `binnumber` is a shape (D,N) ndarray where + each row indicates bin placements for each dimension respectively. In each + dimension, a binnumber of `i` means the corresponding value is between + (bin_edges[D][i-1], bin_edges[D][i]), for each dimension 'D'. + + .. versionadded:: 0.11.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> from mpl_toolkits.mplot3d import Axes3D + + Take an array of 600 (x, y) coordinates as an example. + `binned_statistic_dd` can handle arrays of higher dimension `D`. But a plot + of dimension `D+1` is required. + + >>> mu = np.array([0., 1.]) + >>> sigma = np.array([[1., -0.5],[-0.5, 1.5]]) + >>> multinormal = stats.multivariate_normal(mu, sigma) + >>> data = multinormal.rvs(size=600, random_state=235412) + >>> data.shape + (600, 2) + + Create bins and count how many arrays fall in each bin: + + >>> N = 60 + >>> x = np.linspace(-3, 3, N) + >>> y = np.linspace(-3, 4, N) + >>> ret = stats.binned_statistic_dd(data, np.arange(600), bins=[x, y], + ... statistic='count') + >>> bincounts = ret.statistic + + Set the volume and the location of bars: + + >>> dx = x[1] - x[0] + >>> dy = y[1] - y[0] + >>> x, y = np.meshgrid(x[:-1]+dx/2, y[:-1]+dy/2) + >>> z = 0 + + >>> bincounts = bincounts.ravel() + >>> x = x.ravel() + >>> y = y.ravel() + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111, projection='3d') + >>> with np.errstate(divide='ignore'): # silence random axes3d warning + ... ax.bar3d(x, y, z, dx, dy, bincounts) + + Reuse bin numbers and bin edges with new values: + + >>> ret2 = stats.binned_statistic_dd(data, -np.arange(600), + ... binned_statistic_result=ret, + ... statistic='mean') + """ + known_stats = ['mean', 'median', 'count', 'sum', 'std', 'min', 'max'] + if not callable(statistic) and statistic not in known_stats: + raise ValueError(f'invalid statistic {statistic!r}') + + try: + bins = index(bins) + except TypeError: + # bins is not an integer + pass + # If bins was an integer-like object, now it is an actual Python int. + + # NOTE: for _bin_edges(), see e.g. gh-11365 + if isinstance(bins, int) and not np.isfinite(sample).all(): + raise ValueError(f'{sample!r} contains non-finite values.') + + # `Ndim` is the number of dimensions (e.g. `2` for `binned_statistic_2d`) + # `Dlen` is the length of elements along each dimension. + # This code is based on np.histogramdd + try: + # `sample` is an ND-array. + Dlen, Ndim = sample.shape + except (AttributeError, ValueError): + # `sample` is a sequence of 1D arrays. + sample = np.atleast_2d(sample).T + Dlen, Ndim = sample.shape + + # Store initial shape of `values` to preserve it in the output + values = np.asarray(values) + input_shape = list(values.shape) + # Make sure that `values` is 2D to iterate over rows + values = np.atleast_2d(values) + Vdim, Vlen = values.shape + + # Make sure `values` match `sample` + if statistic != 'count' and Vlen != Dlen: + raise AttributeError('The number of `values` elements must match the ' + 'length of each `sample` dimension.') + + try: + M = len(bins) + if M != Ndim: + raise AttributeError('The dimension of bins must be equal ' + 'to the dimension of the sample x.') + except TypeError: + bins = Ndim * [bins] + + if binned_statistic_result is None: + nbin, edges, dedges = _bin_edges(sample, bins, range) + binnumbers = _bin_numbers(sample, nbin, edges, dedges) + else: + edges = binned_statistic_result.bin_edges + nbin = np.array([len(edges[i]) + 1 for i in builtins.range(Ndim)]) + # +1 for outlier bins + dedges = [np.diff(edges[i]) for i in builtins.range(Ndim)] + binnumbers = binned_statistic_result.binnumber + + # Avoid overflow with double precision. Complex `values` -> `complex128`. + result_type = np.result_type(values, np.float64) + result = np.empty([Vdim, nbin.prod()], dtype=result_type) + + if statistic in {'mean', np.mean}: + result.fill(np.nan) + flatcount = _bincount(binnumbers, None) + a = flatcount.nonzero() + for vv in builtins.range(Vdim): + flatsum = _bincount(binnumbers, values[vv]) + result[vv, a] = flatsum[a] / flatcount[a] + elif statistic in {'std', np.std}: + result.fill(np.nan) + flatcount = _bincount(binnumbers, None) + a = flatcount.nonzero() + for vv in builtins.range(Vdim): + flatsum = _bincount(binnumbers, values[vv]) + delta = values[vv] - flatsum[binnumbers] / flatcount[binnumbers] + std = np.sqrt( + _bincount(binnumbers, delta*np.conj(delta))[a] / flatcount[a] + ) + result[vv, a] = std + result = np.real(result) + elif statistic == 'count': + result = np.empty([Vdim, nbin.prod()], dtype=np.float64) + result.fill(0) + flatcount = _bincount(binnumbers, None) + a = np.arange(len(flatcount)) + result[:, a] = flatcount[np.newaxis, :] + elif statistic in {'sum', np.sum}: + result.fill(0) + for vv in builtins.range(Vdim): + flatsum = _bincount(binnumbers, values[vv]) + a = np.arange(len(flatsum)) + result[vv, a] = flatsum + elif statistic in {'median', np.median}: + result.fill(np.nan) + for vv in builtins.range(Vdim): + i = np.lexsort((values[vv], binnumbers)) + _, j, counts = np.unique(binnumbers[i], + return_index=True, return_counts=True) + mid = j + (counts - 1) / 2 + mid_a = values[vv, i][np.floor(mid).astype(int)] + mid_b = values[vv, i][np.ceil(mid).astype(int)] + medians = (mid_a + mid_b) / 2 + result[vv, binnumbers[i][j]] = medians + elif statistic in {'min', np.min}: + result.fill(np.nan) + for vv in builtins.range(Vdim): + i = np.argsort(values[vv])[::-1] # Reversed so the min is last + result[vv, binnumbers[i]] = values[vv, i] + elif statistic in {'max', np.max}: + result.fill(np.nan) + for vv in builtins.range(Vdim): + i = np.argsort(values[vv]) + result[vv, binnumbers[i]] = values[vv, i] + elif callable(statistic): + with np.errstate(invalid='ignore'), catch_warnings(): + simplefilter("ignore", RuntimeWarning) + try: + null = statistic([]) + except Exception: + null = np.nan + if np.iscomplexobj(null): + result = result.astype(np.complex128) + result.fill(null) + try: + _calc_binned_statistic( + Vdim, binnumbers, result, values, statistic + ) + except ValueError: + result = result.astype(np.complex128) + _calc_binned_statistic( + Vdim, binnumbers, result, values, statistic + ) + + # Shape into a proper matrix + result = result.reshape(np.append(Vdim, nbin)) + + # Remove outliers (indices 0 and -1 for each bin-dimension). + core = tuple([slice(None)] + Ndim * [slice(1, -1)]) + result = result[core] + + # Unravel binnumbers into an ndarray, each row the bins for each dimension + if expand_binnumbers and Ndim > 1: + binnumbers = np.asarray(np.unravel_index(binnumbers, nbin)) + + if np.any(result.shape[1:] != nbin - 2): + raise RuntimeError('Internal Shape Error') + + # Reshape to have output (`result`) match input (`values`) shape + result = result.reshape(input_shape[:-1] + list(nbin-2)) + + return BinnedStatisticddResult(result, edges, binnumbers) + + +def _calc_binned_statistic(Vdim, bin_numbers, result, values, stat_func): + unique_bin_numbers = np.unique(bin_numbers) + for vv in builtins.range(Vdim): + bin_map = _create_binned_data(bin_numbers, unique_bin_numbers, + values, vv) + for i in unique_bin_numbers: + stat = stat_func(np.array(bin_map[i])) + if np.iscomplexobj(stat) and not np.iscomplexobj(result): + raise ValueError("The statistic function returns complex ") + result[vv, i] = stat + + +def _create_binned_data(bin_numbers, unique_bin_numbers, values, vv): + """ Create hashmap of bin ids to values in bins + key: bin number + value: list of binned data + """ + bin_map = dict() + for i in unique_bin_numbers: + bin_map[i] = [] + for i in builtins.range(len(bin_numbers)): + bin_map[bin_numbers[i]].append(values[vv, i]) + return bin_map + + +def _bin_edges(sample, bins=None, range=None): + """ Create edge arrays + """ + Dlen, Ndim = sample.shape + + nbin = np.empty(Ndim, int) # Number of bins in each dimension + edges = Ndim * [None] # Bin edges for each dim (will be 2D array) + dedges = Ndim * [None] # Spacing between edges (will be 2D array) + + # Select range for each dimension + # Used only if number of bins is given. + if range is None: + smin = np.atleast_1d(np.array(sample.min(axis=0), float)) + smax = np.atleast_1d(np.array(sample.max(axis=0), float)) + else: + if len(range) != Ndim: + raise ValueError( + f"range given for {len(range)} dimensions; {Ndim} required") + smin = np.empty(Ndim) + smax = np.empty(Ndim) + for i in builtins.range(Ndim): + if range[i][1] < range[i][0]: + raise ValueError( + "In {}range, start must be <= stop".format( + f"dimension {i + 1} of " if Ndim > 1 else "")) + smin[i], smax[i] = range[i] + + # Make sure the bins have a finite width. + for i in builtins.range(len(smin)): + if smin[i] == smax[i]: + smin[i] = smin[i] - .5 + smax[i] = smax[i] + .5 + + # Preserve sample floating point precision in bin edges + edges_dtype = (sample.dtype if np.issubdtype(sample.dtype, np.floating) + else float) + + # Create edge arrays + for i in builtins.range(Ndim): + if np.isscalar(bins[i]): + nbin[i] = bins[i] + 2 # +2 for outlier bins + edges[i] = np.linspace(smin[i], smax[i], nbin[i] - 1, + dtype=edges_dtype) + else: + edges[i] = np.asarray(bins[i], edges_dtype) + nbin[i] = len(edges[i]) + 1 # +1 for outlier bins + dedges[i] = np.diff(edges[i]) + + nbin = np.asarray(nbin) + + return nbin, edges, dedges + + +def _bin_numbers(sample, nbin, edges, dedges): + """Compute the bin number each sample falls into, in each dimension + """ + Dlen, Ndim = sample.shape + + sampBin = [ + np.digitize(sample[:, i], edges[i]) + for i in range(Ndim) + ] + + # Using `digitize`, values that fall on an edge are put in the right bin. + # For the rightmost bin, we want values equal to the right + # edge to be counted in the last bin, and not as an outlier. + for i in range(Ndim): + # Find the rounding precision + dedges_min = dedges[i].min() + if dedges_min == 0: + raise ValueError('The smallest edge difference is numerically 0.') + decimal = int(-np.log10(dedges_min)) + 6 + # Find which points are on the rightmost edge. + on_edge = np.where((sample[:, i] >= edges[i][-1]) & + (np.around(sample[:, i], decimal) == + np.around(edges[i][-1], decimal)))[0] + # Shift these points one bin to the left. + sampBin[i][on_edge] -= 1 + + # Compute the sample indices in the flattened statistic matrix. + binnumbers = np.ravel_multi_index(sampBin, nbin) + + return binnumbers diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binomtest.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binomtest.py new file mode 100644 index 0000000000000000000000000000000000000000..bdf21117383374e730ab052fcbb0b5b7fca029c1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_binomtest.py @@ -0,0 +1,375 @@ +from math import sqrt +import numpy as np +from scipy._lib._util import _validate_int +from scipy.optimize import brentq +from scipy.special import ndtri +from ._discrete_distns import binom +from ._common import ConfidenceInterval + + +class BinomTestResult: + """ + Result of `scipy.stats.binomtest`. + + Attributes + ---------- + k : int + The number of successes (copied from `binomtest` input). + n : int + The number of trials (copied from `binomtest` input). + alternative : str + Indicates the alternative hypothesis specified in the input + to `binomtest`. It will be one of ``'two-sided'``, ``'greater'``, + or ``'less'``. + statistic: float + The estimate of the proportion of successes. + pvalue : float + The p-value of the hypothesis test. + + """ + def __init__(self, k, n, alternative, statistic, pvalue): + self.k = k + self.n = n + self.alternative = alternative + self.statistic = statistic + self.pvalue = pvalue + + # add alias for backward compatibility + self.proportion_estimate = statistic + + def __repr__(self): + s = ("BinomTestResult(" + f"k={self.k}, " + f"n={self.n}, " + f"alternative={self.alternative!r}, " + f"statistic={self.statistic}, " + f"pvalue={self.pvalue})") + return s + + def proportion_ci(self, confidence_level=0.95, method='exact'): + """ + Compute the confidence interval for ``statistic``. + + Parameters + ---------- + confidence_level : float, optional + Confidence level for the computed confidence interval + of the estimated proportion. Default is 0.95. + method : {'exact', 'wilson', 'wilsoncc'}, optional + Selects the method used to compute the confidence interval + for the estimate of the proportion: + + 'exact' : + Use the Clopper-Pearson exact method [1]_. + 'wilson' : + Wilson's method, without continuity correction ([2]_, [3]_). + 'wilsoncc' : + Wilson's method, with continuity correction ([2]_, [3]_). + + Default is ``'exact'``. + + Returns + ------- + ci : ``ConfidenceInterval`` object + The object has attributes ``low`` and ``high`` that hold the + lower and upper bounds of the confidence interval. + + References + ---------- + .. [1] C. J. Clopper and E. S. Pearson, The use of confidence or + fiducial limits illustrated in the case of the binomial, + Biometrika, Vol. 26, No. 4, pp 404-413 (Dec. 1934). + .. [2] E. B. Wilson, Probable inference, the law of succession, and + statistical inference, J. Amer. Stat. Assoc., 22, pp 209-212 + (1927). + .. [3] Robert G. Newcombe, Two-sided confidence intervals for the + single proportion: comparison of seven methods, Statistics + in Medicine, 17, pp 857-872 (1998). + + Examples + -------- + >>> from scipy.stats import binomtest + >>> result = binomtest(k=7, n=50, p=0.1) + >>> result.statistic + 0.14 + >>> result.proportion_ci() + ConfidenceInterval(low=0.05819170033997342, high=0.26739600249700846) + """ + if method not in ('exact', 'wilson', 'wilsoncc'): + raise ValueError(f"method ('{method}') must be one of 'exact', " + "'wilson' or 'wilsoncc'.") + if not (0 <= confidence_level <= 1): + raise ValueError(f'confidence_level ({confidence_level}) must be in ' + 'the interval [0, 1].') + if method == 'exact': + low, high = _binom_exact_conf_int(self.k, self.n, + confidence_level, + self.alternative) + else: + # method is 'wilson' or 'wilsoncc' + low, high = _binom_wilson_conf_int(self.k, self.n, + confidence_level, + self.alternative, + correction=method == 'wilsoncc') + return ConfidenceInterval(low=low, high=high) + + +def _findp(func): + try: + p = brentq(func, 0, 1) + except RuntimeError: + raise RuntimeError('numerical solver failed to converge when ' + 'computing the confidence limits') from None + except ValueError as exc: + raise ValueError('brentq raised a ValueError; report this to the ' + 'SciPy developers') from exc + return p + + +def _binom_exact_conf_int(k, n, confidence_level, alternative): + """ + Compute the estimate and confidence interval for the binomial test. + + Returns proportion, prop_low, prop_high + """ + if alternative == 'two-sided': + alpha = (1 - confidence_level) / 2 + if k == 0: + plow = 0.0 + else: + plow = _findp(lambda p: binom.sf(k-1, n, p) - alpha) + if k == n: + phigh = 1.0 + else: + phigh = _findp(lambda p: binom.cdf(k, n, p) - alpha) + elif alternative == 'less': + alpha = 1 - confidence_level + plow = 0.0 + if k == n: + phigh = 1.0 + else: + phigh = _findp(lambda p: binom.cdf(k, n, p) - alpha) + elif alternative == 'greater': + alpha = 1 - confidence_level + if k == 0: + plow = 0.0 + else: + plow = _findp(lambda p: binom.sf(k-1, n, p) - alpha) + phigh = 1.0 + return plow, phigh + + +def _binom_wilson_conf_int(k, n, confidence_level, alternative, correction): + # This function assumes that the arguments have already been validated. + # In particular, `alternative` must be one of 'two-sided', 'less' or + # 'greater'. + p = k / n + if alternative == 'two-sided': + z = ndtri(0.5 + 0.5*confidence_level) + else: + z = ndtri(confidence_level) + + # For reference, the formulas implemented here are from + # Newcombe (1998) (ref. [3] in the proportion_ci docstring). + denom = 2*(n + z**2) + center = (2*n*p + z**2)/denom + q = 1 - p + if correction: + if alternative == 'less' or k == 0: + lo = 0.0 + else: + dlo = (1 + z*sqrt(z**2 - 2 - 1/n + 4*p*(n*q + 1))) / denom + lo = center - dlo + if alternative == 'greater' or k == n: + hi = 1.0 + else: + dhi = (1 + z*sqrt(z**2 + 2 - 1/n + 4*p*(n*q - 1))) / denom + hi = center + dhi + else: + delta = z/denom * sqrt(4*n*p*q + z**2) + if alternative == 'less' or k == 0: + lo = 0.0 + else: + lo = center - delta + if alternative == 'greater' or k == n: + hi = 1.0 + else: + hi = center + delta + + return lo, hi + + +def binomtest(k, n, p=0.5, alternative='two-sided'): + """ + Perform a test that the probability of success is p. + + The binomial test [1]_ is a test of the null hypothesis that the + probability of success in a Bernoulli experiment is `p`. + + Details of the test can be found in many texts on statistics, such + as section 24.5 of [2]_. + + Parameters + ---------- + k : int + The number of successes. + n : int + The number of trials. + p : float, optional + The hypothesized probability of success, i.e. the expected + proportion of successes. The value must be in the interval + ``0 <= p <= 1``. The default value is ``p = 0.5``. + alternative : {'two-sided', 'greater', 'less'}, optional + Indicates the alternative hypothesis. The default value is + 'two-sided'. + + Returns + ------- + result : `~scipy.stats._result_classes.BinomTestResult` instance + The return value is an object with the following attributes: + + k : int + The number of successes (copied from `binomtest` input). + n : int + The number of trials (copied from `binomtest` input). + alternative : str + Indicates the alternative hypothesis specified in the input + to `binomtest`. It will be one of ``'two-sided'``, ``'greater'``, + or ``'less'``. + statistic : float + The estimate of the proportion of successes. + pvalue : float + The p-value of the hypothesis test. + + The object has the following methods: + + proportion_ci(confidence_level=0.95, method='exact') : + Compute the confidence interval for ``statistic``. + + Notes + ----- + .. versionadded:: 1.7.0 + + References + ---------- + .. [1] Binomial test, https://en.wikipedia.org/wiki/Binomial_test + .. [2] Jerrold H. Zar, Biostatistical Analysis (fifth edition), + Prentice Hall, Upper Saddle River, New Jersey USA (2010) + + Examples + -------- + >>> from scipy.stats import binomtest + + A car manufacturer claims that no more than 10% of their cars are unsafe. + 15 cars are inspected for safety, 3 were found to be unsafe. Test the + manufacturer's claim: + + >>> result = binomtest(3, n=15, p=0.1, alternative='greater') + >>> result.pvalue + 0.18406106910639114 + + The null hypothesis cannot be rejected at the 5% level of significance + because the returned p-value is greater than the critical value of 5%. + + The test statistic is equal to the estimated proportion, which is simply + ``3/15``: + + >>> result.statistic + 0.2 + + We can use the `proportion_ci()` method of the result to compute the + confidence interval of the estimate: + + >>> result.proportion_ci(confidence_level=0.95) + ConfidenceInterval(low=0.05684686759024681, high=1.0) + + """ + k = _validate_int(k, 'k', minimum=0) + n = _validate_int(n, 'n', minimum=1) + if k > n: + raise ValueError(f'k ({k}) must not be greater than n ({n}).') + + if not (0 <= p <= 1): + raise ValueError(f"p ({p}) must be in range [0,1]") + + if alternative not in ('two-sided', 'less', 'greater'): + raise ValueError(f"alternative ('{alternative}') not recognized; \n" + "must be 'two-sided', 'less' or 'greater'") + if alternative == 'less': + pval = binom.cdf(k, n, p) + elif alternative == 'greater': + pval = binom.sf(k-1, n, p) + else: + # alternative is 'two-sided' + d = binom.pmf(k, n, p) + rerr = 1 + 1e-7 + if k == p * n: + # special case as shortcut, would also be handled by `else` below + pval = 1. + elif k < p * n: + ix = _binary_search_for_binom_tst(lambda x1: -binom.pmf(x1, n, p), + -d*rerr, np.ceil(p * n), n) + # y is the number of terms between mode and n that are <= d*rerr. + # ix gave us the first term where a(ix) <= d*rerr < a(ix-1) + # if the first equality doesn't hold, y=n-ix. Otherwise, we + # need to include ix as well as the equality holds. Note that + # the equality will hold in very very rare situations due to rerr. + y = n - ix + int(d*rerr == binom.pmf(ix, n, p)) + pval = binom.cdf(k, n, p) + binom.sf(n - y, n, p) + else: + ix = _binary_search_for_binom_tst(lambda x1: binom.pmf(x1, n, p), + d*rerr, 0, np.floor(p * n)) + # y is the number of terms between 0 and mode that are <= d*rerr. + # we need to add a 1 to account for the 0 index. + # For comparing this with old behavior, see + # tst_binary_srch_for_binom_tst method in test_morestats. + y = ix + 1 + pval = binom.cdf(y-1, n, p) + binom.sf(k-1, n, p) + + pval = min(1.0, pval) + + result = BinomTestResult(k=k, n=n, alternative=alternative, + statistic=k/n, pvalue=pval) + return result + + +def _binary_search_for_binom_tst(a, d, lo, hi): + """ + Conducts an implicit binary search on a function specified by `a`. + + Meant to be used on the binomial PMF for the case of two-sided tests + to obtain the value on the other side of the mode where the tail + probability should be computed. The values on either side of + the mode are always in order, meaning binary search is applicable. + + Parameters + ---------- + a : callable + The function over which to perform binary search. Its values + for inputs lo and hi should be in ascending order. + d : float + The value to search. + lo : int + The lower end of range to search. + hi : int + The higher end of the range to search. + + Returns + ------- + int + The index, i between lo and hi + such that a(i)<=d d: + hi = mid-1 + else: + return mid + if a(lo) <= d: + return lo + else: + return lo-1 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_boost/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_boost/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..681694192be623c0920955fd1bf8573e516060ce --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_boost/__init__.py @@ -0,0 +1,53 @@ +from scipy.stats._boost.beta_ufunc import ( + _beta_pdf, _beta_cdf, _beta_sf, _beta_ppf, + _beta_isf, _beta_mean, _beta_variance, + _beta_skewness, _beta_kurtosis_excess, +) + +from scipy.stats._boost.binom_ufunc import ( + _binom_pdf, _binom_cdf, _binom_sf, _binom_ppf, + _binom_isf, _binom_mean, _binom_variance, + _binom_skewness, _binom_kurtosis_excess, +) + +from scipy.stats._boost.nbinom_ufunc import ( + _nbinom_pdf, _nbinom_cdf, _nbinom_sf, _nbinom_ppf, + _nbinom_isf, _nbinom_mean, _nbinom_variance, + _nbinom_skewness, _nbinom_kurtosis_excess, +) + +from scipy.stats._boost.hypergeom_ufunc import ( + _hypergeom_pdf, _hypergeom_cdf, _hypergeom_sf, _hypergeom_ppf, + _hypergeom_isf, _hypergeom_mean, _hypergeom_variance, + _hypergeom_skewness, _hypergeom_kurtosis_excess, +) + +from scipy.stats._boost.ncf_ufunc import ( + _ncf_pdf, _ncf_cdf, _ncf_sf, _ncf_ppf, + _ncf_isf, _ncf_mean, _ncf_variance, + _ncf_skewness, _ncf_kurtosis_excess, +) + +from scipy.stats._boost.ncx2_ufunc import ( + _ncx2_pdf, _ncx2_cdf, _ncx2_sf, _ncx2_ppf, + _ncx2_isf, _ncx2_mean, _ncx2_variance, + _ncx2_skewness, _ncx2_kurtosis_excess, +) + +from scipy.stats._boost.nct_ufunc import ( + _nct_pdf, _nct_cdf, _nct_sf, _nct_ppf, + _nct_isf, _nct_mean, _nct_variance, + _nct_skewness, _nct_kurtosis_excess, +) + +from scipy.stats._boost.skewnorm_ufunc import ( + _skewnorm_pdf, _skewnorm_cdf, _skewnorm_sf, _skewnorm_ppf, + _skewnorm_isf, _skewnorm_mean, _skewnorm_variance, + _skewnorm_skewness, _skewnorm_kurtosis_excess, +) + +from scipy.stats._boost.invgauss_ufunc import ( + _invgauss_pdf, _invgauss_cdf, _invgauss_sf, _invgauss_ppf, + _invgauss_isf, _invgauss_mean, _invgauss_variance, + _invgauss_skewness, _invgauss_kurtosis_excess, +) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_bws_test.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_bws_test.py new file mode 100644 index 0000000000000000000000000000000000000000..6496ecfba798dc7ad719f784a57896e296590675 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_bws_test.py @@ -0,0 +1,177 @@ +import numpy as np +from functools import partial +from scipy import stats + + +def _bws_input_validation(x, y, alternative, method): + ''' Input validation and standardization for bws test''' + x, y = np.atleast_1d(x, y) + if x.ndim > 1 or y.ndim > 1: + raise ValueError('`x` and `y` must be exactly one-dimensional.') + if np.isnan(x).any() or np.isnan(y).any(): + raise ValueError('`x` and `y` must not contain NaNs.') + if np.size(x) == 0 or np.size(y) == 0: + raise ValueError('`x` and `y` must be of nonzero size.') + + z = stats.rankdata(np.concatenate((x, y))) + x, y = z[:len(x)], z[len(x):] + + alternatives = {'two-sided', 'less', 'greater'} + alternative = alternative.lower() + if alternative not in alternatives: + raise ValueError(f'`alternative` must be one of {alternatives}.') + + method = stats.PermutationMethod() if method is None else method + if not isinstance(method, stats.PermutationMethod): + raise ValueError('`method` must be an instance of ' + '`scipy.stats.PermutationMethod`') + + return x, y, alternative, method + + +def _bws_statistic(x, y, alternative, axis): + '''Compute the BWS test statistic for two independent samples''' + # Public function currently does not accept `axis`, but `permutation_test` + # uses `axis` to make vectorized call. + + Ri, Hj = np.sort(x, axis=axis), np.sort(y, axis=axis) + n, m = Ri.shape[axis], Hj.shape[axis] + i, j = np.arange(1, n+1), np.arange(1, m+1) + + Bx_num = Ri - (m + n)/n * i + By_num = Hj - (m + n)/m * j + + if alternative == 'two-sided': + Bx_num *= Bx_num + By_num *= By_num + else: + Bx_num *= np.abs(Bx_num) + By_num *= np.abs(By_num) + + Bx_den = i/(n+1) * (1 - i/(n+1)) * m*(m+n)/n + By_den = j/(m+1) * (1 - j/(m+1)) * n*(m+n)/m + + Bx = 1/n * np.sum(Bx_num/Bx_den, axis=axis) + By = 1/m * np.sum(By_num/By_den, axis=axis) + + B = (Bx + By) / 2 if alternative == 'two-sided' else (Bx - By) / 2 + + return B + + +def bws_test(x, y, *, alternative="two-sided", method=None): + r'''Perform the Baumgartner-Weiss-Schindler test on two independent samples. + + The Baumgartner-Weiss-Schindler (BWS) test is a nonparametric test of + the null hypothesis that the distribution underlying sample `x` + is the same as the distribution underlying sample `y`. Unlike + the Kolmogorov-Smirnov, Wilcoxon, and Cramer-Von Mises tests, + the BWS test weights the integral by the variance of the difference + in cumulative distribution functions (CDFs), emphasizing the tails of the + distributions, which increases the power of the test in many applications. + + Parameters + ---------- + x, y : array-like + 1-d arrays of samples. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + Let *F(u)* and *G(u)* be the cumulative distribution functions of the + distributions underlying `x` and `y`, respectively. Then the following + alternative hypotheses are available: + + * 'two-sided': the distributions are not equal, i.e. *F(u) ≠ G(u)* for + at least one *u*. + * 'less': the distribution underlying `x` is stochastically less than + the distribution underlying `y`, i.e. *F(u) >= G(u)* for all *u*. + * 'greater': the distribution underlying `x` is stochastically greater + than the distribution underlying `y`, i.e. *F(u) <= G(u)* for all + *u*. + + Under a more restrictive set of assumptions, the alternative hypotheses + can be expressed in terms of the locations of the distributions; + see [2] section 5.1. + method : PermutationMethod, optional + Configures the method used to compute the p-value. The default is + the default `PermutationMethod` object. + + Returns + ------- + res : PermutationTestResult + An object with attributes: + + statistic : float + The observed test statistic of the data. + pvalue : float + The p-value for the given alternative. + null_distribution : ndarray + The values of the test statistic generated under the null hypothesis. + + See also + -------- + scipy.stats.wilcoxon, scipy.stats.mannwhitneyu, scipy.stats.ttest_ind + + Notes + ----- + When ``alternative=='two-sided'``, the statistic is defined by the + equations given in [1]_ Section 2. This statistic is not appropriate for + one-sided alternatives; in that case, the statistic is the *negative* of + that given by the equations in [1]_ Section 2. Consequently, when the + distribution of the first sample is stochastically greater than that of the + second sample, the statistic will tend to be positive. + + References + ---------- + .. [1] Neuhäuser, M. (2005). Exact Tests Based on the + Baumgartner-Weiss-Schindler Statistic: A Survey. Statistical Papers, + 46(1), 1-29. + .. [2] Fay, M. P., & Proschan, M. A. (2010). Wilcoxon-Mann-Whitney or t-test? + On assumptions for hypothesis tests and multiple interpretations of + decision rules. Statistics surveys, 4, 1. + + Examples + -------- + We follow the example of table 3 in [1]_: Fourteen children were divided + randomly into two groups. Their ranks at performing a specific tests are + as follows. + + >>> import numpy as np + >>> x = [1, 2, 3, 4, 6, 7, 8] + >>> y = [5, 9, 10, 11, 12, 13, 14] + + We use the BWS test to assess whether there is a statistically significant + difference between the two groups. + The null hypothesis is that there is no difference in the distributions of + performance between the two groups. We decide that a significance level of + 1% is required to reject the null hypothesis in favor of the alternative + that the distributions are different. + Since the number of samples is very small, we can compare the observed test + statistic against the *exact* distribution of the test statistic under the + null hypothesis. + + >>> from scipy.stats import bws_test + >>> res = bws_test(x, y) + >>> print(res.statistic) + 5.132167152575315 + + This agrees with :math:`B = 5.132` reported in [1]_. The *p*-value produced + by `bws_test` also agrees with :math:`p = 0.0029` reported in [1]_. + + >>> print(res.pvalue) + 0.002913752913752914 + + Because the p-value is below our threshold of 1%, we take this as evidence + against the null hypothesis in favor of the alternative that there is a + difference in performance between the two groups. + ''' + + x, y, alternative, method = _bws_input_validation(x, y, alternative, + method) + bws_statistic = partial(_bws_statistic, alternative=alternative) + + permutation_alternative = 'less' if alternative == 'less' else 'greater' + res = stats.permutation_test((x, y), bws_statistic, + alternative=permutation_alternative, + **method._asdict()) + + return res diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_censored_data.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_censored_data.py new file mode 100644 index 0000000000000000000000000000000000000000..f6fee500f1d97db0bae9ebff26824d4d894c7f39 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_censored_data.py @@ -0,0 +1,459 @@ +import numpy as np + + +def _validate_1d(a, name, allow_inf=False): + if np.ndim(a) != 1: + raise ValueError(f'`{name}` must be a one-dimensional sequence.') + if np.isnan(a).any(): + raise ValueError(f'`{name}` must not contain nan.') + if not allow_inf and np.isinf(a).any(): + raise ValueError(f'`{name}` must contain only finite values.') + + +def _validate_interval(interval): + interval = np.asarray(interval) + if interval.shape == (0,): + # The input was a sequence with length 0. + interval = interval.reshape((0, 2)) + if interval.ndim != 2 or interval.shape[-1] != 2: + raise ValueError('`interval` must be a two-dimensional array with ' + 'shape (m, 2), where m is the number of ' + 'interval-censored values, but got shape ' + f'{interval.shape}') + + if np.isnan(interval).any(): + raise ValueError('`interval` must not contain nan.') + if np.isinf(interval).all(axis=1).any(): + raise ValueError('In each row in `interval`, both values must not' + ' be infinite.') + if (interval[:, 0] > interval[:, 1]).any(): + raise ValueError('In each row of `interval`, the left value must not' + ' exceed the right value.') + + uncensored_mask = interval[:, 0] == interval[:, 1] + left_mask = np.isinf(interval[:, 0]) + right_mask = np.isinf(interval[:, 1]) + interval_mask = np.isfinite(interval).all(axis=1) & ~uncensored_mask + + uncensored2 = interval[uncensored_mask, 0] + left2 = interval[left_mask, 1] + right2 = interval[right_mask, 0] + interval2 = interval[interval_mask] + + return uncensored2, left2, right2, interval2 + + +def _validate_x_censored(x, censored): + x = np.asarray(x) + if x.ndim != 1: + raise ValueError('`x` must be one-dimensional.') + censored = np.asarray(censored) + if censored.ndim != 1: + raise ValueError('`censored` must be one-dimensional.') + if (~np.isfinite(x)).any(): + raise ValueError('`x` must not contain nan or inf.') + if censored.size != x.size: + raise ValueError('`x` and `censored` must have the same length.') + return x, censored.astype(bool) + + +class CensoredData: + """ + Instances of this class represent censored data. + + Instances may be passed to the ``fit`` method of continuous + univariate SciPy distributions for maximum likelihood estimation. + The *only* method of the univariate continuous distributions that + understands `CensoredData` is the ``fit`` method. An instance of + `CensoredData` can not be passed to methods such as ``pdf`` and + ``cdf``. + + An observation is said to be *censored* when the precise value is unknown, + but it has a known upper and/or lower bound. The conventional terminology + is: + + * left-censored: an observation is below a certain value but it is + unknown by how much. + * right-censored: an observation is above a certain value but it is + unknown by how much. + * interval-censored: an observation lies somewhere on an interval between + two values. + + Left-, right-, and interval-censored data can be represented by + `CensoredData`. + + For convenience, the class methods ``left_censored`` and + ``right_censored`` are provided to create a `CensoredData` + instance from a single one-dimensional array of measurements + and a corresponding boolean array to indicate which measurements + are censored. The class method ``interval_censored`` accepts two + one-dimensional arrays that hold the lower and upper bounds of the + intervals. + + Parameters + ---------- + uncensored : array_like, 1D + Uncensored observations. + left : array_like, 1D + Left-censored observations. + right : array_like, 1D + Right-censored observations. + interval : array_like, 2D, with shape (m, 2) + Interval-censored observations. Each row ``interval[k, :]`` + represents the interval for the kth interval-censored observation. + + Notes + ----- + In the input array `interval`, the lower bound of the interval may + be ``-inf``, and the upper bound may be ``inf``, but at least one must be + finite. When the lower bound is ``-inf``, the row represents a left- + censored observation, and when the upper bound is ``inf``, the row + represents a right-censored observation. If the length of an interval + is 0 (i.e. ``interval[k, 0] == interval[k, 1]``, the observation is + treated as uncensored. So one can represent all the types of censored + and uncensored data in ``interval``, but it is generally more convenient + to use `uncensored`, `left` and `right` for uncensored, left-censored and + right-censored observations, respectively. + + Examples + -------- + In the most general case, a censored data set may contain values that + are left-censored, right-censored, interval-censored, and uncensored. + For example, here we create a data set with five observations. Two + are uncensored (values 1 and 1.5), one is a left-censored observation + of 0, one is a right-censored observation of 10 and one is + interval-censored in the interval [2, 3]. + + >>> import numpy as np + >>> from scipy.stats import CensoredData + >>> data = CensoredData(uncensored=[1, 1.5], left=[0], right=[10], + ... interval=[[2, 3]]) + >>> print(data) + CensoredData(5 values: 2 not censored, 1 left-censored, + 1 right-censored, 1 interval-censored) + + Equivalently, + + >>> data = CensoredData(interval=[[1, 1], + ... [1.5, 1.5], + ... [-np.inf, 0], + ... [10, np.inf], + ... [2, 3]]) + >>> print(data) + CensoredData(5 values: 2 not censored, 1 left-censored, + 1 right-censored, 1 interval-censored) + + A common case is to have a mix of uncensored observations and censored + observations that are all right-censored (or all left-censored). For + example, consider an experiment in which six devices are started at + various times and left running until they fail. Assume that time is + measured in hours, and the experiment is stopped after 30 hours, even + if all the devices have not failed by that time. We might end up with + data such as this:: + + Device Start-time Fail-time Time-to-failure + 1 0 13 13 + 2 2 24 22 + 3 5 22 17 + 4 8 23 15 + 5 10 *** >20 + 6 12 *** >18 + + Two of the devices had not failed when the experiment was stopped; + the observations of the time-to-failure for these two devices are + right-censored. We can represent this data with + + >>> data = CensoredData(uncensored=[13, 22, 17, 15], right=[20, 18]) + >>> print(data) + CensoredData(6 values: 4 not censored, 2 right-censored) + + Alternatively, we can use the method `CensoredData.right_censored` to + create a representation of this data. The time-to-failure observations + are put the list ``ttf``. The ``censored`` list indicates which values + in ``ttf`` are censored. + + >>> ttf = [13, 22, 17, 15, 20, 18] + >>> censored = [False, False, False, False, True, True] + + Pass these lists to `CensoredData.right_censored` to create an + instance of `CensoredData`. + + >>> data = CensoredData.right_censored(ttf, censored) + >>> print(data) + CensoredData(6 values: 4 not censored, 2 right-censored) + + If the input data is interval censored and already stored in two + arrays, one holding the low end of the intervals and another + holding the high ends, the class method ``interval_censored`` can + be used to create the `CensoredData` instance. + + This example creates an instance with four interval-censored values. + The intervals are [10, 11], [0.5, 1], [2, 3], and [12.5, 13.5]. + + >>> a = [10, 0.5, 2, 12.5] # Low ends of the intervals + >>> b = [11, 1.0, 3, 13.5] # High ends of the intervals + >>> data = CensoredData.interval_censored(low=a, high=b) + >>> print(data) + CensoredData(4 values: 0 not censored, 4 interval-censored) + + Finally, we create and censor some data from the `weibull_min` + distribution, and then fit `weibull_min` to that data. We'll assume + that the location parameter is known to be 0. + + >>> from scipy.stats import weibull_min + >>> rng = np.random.default_rng() + + Create the random data set. + + >>> x = weibull_min.rvs(2.5, loc=0, scale=30, size=250, random_state=rng) + >>> x[x > 40] = 40 # Right-censor values greater or equal to 40. + + Create the `CensoredData` instance with the `right_censored` method. + The censored values are those where the value is 40. + + >>> data = CensoredData.right_censored(x, x == 40) + >>> print(data) + CensoredData(250 values: 215 not censored, 35 right-censored) + + 35 values have been right-censored. + + Fit `weibull_min` to the censored data. We expect to shape and scale + to be approximately 2.5 and 30, respectively. + + >>> weibull_min.fit(data, floc=0) + (2.3575922823897315, 0, 30.40650074451254) + + """ + + def __init__(self, uncensored=None, *, left=None, right=None, + interval=None): + if uncensored is None: + uncensored = [] + if left is None: + left = [] + if right is None: + right = [] + if interval is None: + interval = np.empty((0, 2)) + + _validate_1d(uncensored, 'uncensored') + _validate_1d(left, 'left') + _validate_1d(right, 'right') + uncensored2, left2, right2, interval2 = _validate_interval(interval) + + self._uncensored = np.concatenate((uncensored, uncensored2)) + self._left = np.concatenate((left, left2)) + self._right = np.concatenate((right, right2)) + # Note that by construction, the private attribute _interval + # will be a 2D array that contains only finite values representing + # intervals with nonzero but finite length. + self._interval = interval2 + + def __repr__(self): + uncensored_str = " ".join(np.array_repr(self._uncensored).split()) + left_str = " ".join(np.array_repr(self._left).split()) + right_str = " ".join(np.array_repr(self._right).split()) + interval_str = " ".join(np.array_repr(self._interval).split()) + return (f"CensoredData(uncensored={uncensored_str}, left={left_str}, " + f"right={right_str}, interval={interval_str})") + + def __str__(self): + num_nc = len(self._uncensored) + num_lc = len(self._left) + num_rc = len(self._right) + num_ic = len(self._interval) + n = num_nc + num_lc + num_rc + num_ic + parts = [f'{num_nc} not censored'] + if num_lc > 0: + parts.append(f'{num_lc} left-censored') + if num_rc > 0: + parts.append(f'{num_rc} right-censored') + if num_ic > 0: + parts.append(f'{num_ic} interval-censored') + return f'CensoredData({n} values: ' + ', '.join(parts) + ')' + + # This is not a complete implementation of the arithmetic operators. + # All we need is subtracting a scalar and dividing by a scalar. + + def __sub__(self, other): + return CensoredData(uncensored=self._uncensored - other, + left=self._left - other, + right=self._right - other, + interval=self._interval - other) + + def __truediv__(self, other): + return CensoredData(uncensored=self._uncensored / other, + left=self._left / other, + right=self._right / other, + interval=self._interval / other) + + def __len__(self): + """ + The number of values (censored and not censored). + """ + return (len(self._uncensored) + len(self._left) + len(self._right) + + len(self._interval)) + + def num_censored(self): + """ + Number of censored values. + """ + return len(self._left) + len(self._right) + len(self._interval) + + @classmethod + def right_censored(cls, x, censored): + """ + Create a `CensoredData` instance of right-censored data. + + Parameters + ---------- + x : array_like + `x` is the array of observed data or measurements. + `x` must be a one-dimensional sequence of finite numbers. + censored : array_like of bool + `censored` must be a one-dimensional sequence of boolean + values. If ``censored[k]`` is True, the corresponding value + in `x` is right-censored. That is, the value ``x[k]`` + is the lower bound of the true (but unknown) value. + + Returns + ------- + data : `CensoredData` + An instance of `CensoredData` that represents the + collection of uncensored and right-censored values. + + Examples + -------- + >>> from scipy.stats import CensoredData + + Two uncensored values (4 and 10) and two right-censored values + (24 and 25). + + >>> data = CensoredData.right_censored([4, 10, 24, 25], + ... [False, False, True, True]) + >>> data + CensoredData(uncensored=array([ 4., 10.]), + left=array([], dtype=float64), right=array([24., 25.]), + interval=array([], shape=(0, 2), dtype=float64)) + >>> print(data) + CensoredData(4 values: 2 not censored, 2 right-censored) + """ + x, censored = _validate_x_censored(x, censored) + return cls(uncensored=x[~censored], right=x[censored]) + + @classmethod + def left_censored(cls, x, censored): + """ + Create a `CensoredData` instance of left-censored data. + + Parameters + ---------- + x : array_like + `x` is the array of observed data or measurements. + `x` must be a one-dimensional sequence of finite numbers. + censored : array_like of bool + `censored` must be a one-dimensional sequence of boolean + values. If ``censored[k]`` is True, the corresponding value + in `x` is left-censored. That is, the value ``x[k]`` + is the upper bound of the true (but unknown) value. + + Returns + ------- + data : `CensoredData` + An instance of `CensoredData` that represents the + collection of uncensored and left-censored values. + + Examples + -------- + >>> from scipy.stats import CensoredData + + Two uncensored values (0.12 and 0.033) and two left-censored values + (both 1e-3). + + >>> data = CensoredData.left_censored([0.12, 0.033, 1e-3, 1e-3], + ... [False, False, True, True]) + >>> data + CensoredData(uncensored=array([0.12 , 0.033]), + left=array([0.001, 0.001]), right=array([], dtype=float64), + interval=array([], shape=(0, 2), dtype=float64)) + >>> print(data) + CensoredData(4 values: 2 not censored, 2 left-censored) + """ + x, censored = _validate_x_censored(x, censored) + return cls(uncensored=x[~censored], left=x[censored]) + + @classmethod + def interval_censored(cls, low, high): + """ + Create a `CensoredData` instance of interval-censored data. + + This method is useful when all the data is interval-censored, and + the low and high ends of the intervals are already stored in + separate one-dimensional arrays. + + Parameters + ---------- + low : array_like + The one-dimensional array containing the low ends of the + intervals. + high : array_like + The one-dimensional array containing the high ends of the + intervals. + + Returns + ------- + data : `CensoredData` + An instance of `CensoredData` that represents the + collection of censored values. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import CensoredData + + ``a`` and ``b`` are the low and high ends of a collection of + interval-censored values. + + >>> a = [0.5, 2.0, 3.0, 5.5] + >>> b = [1.0, 2.5, 3.5, 7.0] + >>> data = CensoredData.interval_censored(low=a, high=b) + >>> print(data) + CensoredData(4 values: 0 not censored, 4 interval-censored) + """ + _validate_1d(low, 'low', allow_inf=True) + _validate_1d(high, 'high', allow_inf=True) + if len(low) != len(high): + raise ValueError('`low` and `high` must have the same length.') + interval = np.column_stack((low, high)) + uncensored, left, right, interval = _validate_interval(interval) + return cls(uncensored=uncensored, left=left, right=right, + interval=interval) + + def _uncensor(self): + """ + This function is used when a non-censored version of the data + is needed to create a rough estimate of the parameters of a + distribution via the method of moments or some similar method. + The data is "uncensored" by taking the given endpoints as the + data for the left- or right-censored data, and the mean for the + interval-censored data. + """ + data = np.concatenate((self._uncensored, self._left, self._right, + self._interval.mean(axis=1))) + return data + + def _supported(self, a, b): + """ + Return a subset of self containing the values that are in + (or overlap with) the interval (a, b). + """ + uncensored = self._uncensored + uncensored = uncensored[(a < uncensored) & (uncensored < b)] + left = self._left + left = left[a < left] + right = self._right + right = right[right < b] + interval = self._interval + interval = interval[(a < interval[:, 1]) & (interval[:, 0] < b)] + return CensoredData(uncensored, left=left, right=right, + interval=interval) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_common.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_common.py new file mode 100644 index 0000000000000000000000000000000000000000..4011d425cc4afea3c7ee8937526b13f1f92b0850 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_common.py @@ -0,0 +1,5 @@ +from collections import namedtuple + + +ConfidenceInterval = namedtuple("ConfidenceInterval", ["low", "high"]) +ConfidenceInterval. __doc__ = "Class for confidence intervals." diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_constants.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..374fadda992e135c025a658e9f9dcec9263c0eb9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_constants.py @@ -0,0 +1,39 @@ +""" +Statistics-related constants. + +""" +import numpy as np + + +# The smallest representable positive number such that 1.0 + _EPS != 1.0. +_EPS = np.finfo(float).eps + +# The largest [in magnitude] usable floating value. +_XMAX = np.finfo(float).max + +# The log of the largest usable floating value; useful for knowing +# when exp(something) will overflow +_LOGXMAX = np.log(_XMAX) + +# The smallest [in magnitude] usable (i.e. not subnormal) double precision +# floating value. +_XMIN = np.finfo(float).tiny + +# The log of the smallest [in magnitude] usable (i.e not subnormal) +# double precision floating value. +_LOGXMIN = np.log(_XMIN) + +# -special.psi(1) +_EULER = 0.577215664901532860606512090082402431042 + +# special.zeta(3, 1) Apery's constant +_ZETA3 = 1.202056903159594285399738161511449990765 + +# sqrt(pi) +_SQRT_PI = 1.772453850905516027298167483341145182798 + +# sqrt(2/pi) +_SQRT_2_OVER_PI = 0.7978845608028654 + +# log(sqrt(2/pi)) +_LOG_SQRT_2_OVER_PI = -0.22579135264472744 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_continuous_distns.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_continuous_distns.py new file mode 100644 index 0000000000000000000000000000000000000000..c9481c2e0cda526d27a8e45f0971169e8baa2bba --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_continuous_distns.py @@ -0,0 +1,11931 @@ +# +# Author: Travis Oliphant 2002-2011 with contributions from +# SciPy Developers 2004-2011 +# +import warnings +from collections.abc import Iterable +from functools import wraps, cached_property +import ctypes + +import numpy as np +from numpy.polynomial import Polynomial +from scipy._lib.doccer import (extend_notes_in_docstring, + replace_notes_in_docstring, + inherit_docstring_from) +from scipy._lib._ccallback import LowLevelCallable +from scipy import optimize +from scipy import integrate +import scipy.special as sc + +import scipy.special._ufuncs as scu +from scipy._lib._util import _lazyselect, _lazywhere + +from . import _stats +from ._tukeylambda_stats import (tukeylambda_variance as _tlvar, + tukeylambda_kurtosis as _tlkurt) +from ._distn_infrastructure import ( + get_distribution_names, _kurtosis, + rv_continuous, _skew, _get_fixed_fit_value, _check_shape, _ShapeInfo) +from ._ksstats import kolmogn, kolmognp, kolmogni +from ._constants import (_XMIN, _LOGXMIN, _EULER, _ZETA3, _SQRT_PI, + _SQRT_2_OVER_PI, _LOG_SQRT_2_OVER_PI) +from ._censored_data import CensoredData +import scipy.stats._boost as _boost +from scipy.optimize import root_scalar +from scipy.stats._warnings_errors import FitError +import scipy.stats as stats + + +def _remove_optimizer_parameters(kwds): + """ + Remove the optimizer-related keyword arguments 'loc', 'scale' and + 'optimizer' from `kwds`. Then check that `kwds` is empty, and + raise `TypeError("Unknown arguments: %s." % kwds)` if it is not. + + This function is used in the fit method of distributions that override + the default method and do not use the default optimization code. + + `kwds` is modified in-place. + """ + kwds.pop('loc', None) + kwds.pop('scale', None) + kwds.pop('optimizer', None) + kwds.pop('method', None) + if kwds: + raise TypeError("Unknown arguments: %s." % kwds) + + +def _call_super_mom(fun): + # If fit method is overridden only for MLE and doesn't specify what to do + # if method == 'mm' or with censored data, this decorator calls the generic + # implementation. + @wraps(fun) + def wrapper(self, data, *args, **kwds): + method = kwds.get('method', 'mle').lower() + censored = isinstance(data, CensoredData) + if method == 'mm' or (censored and data.num_censored() > 0): + return super(type(self), self).fit(data, *args, **kwds) + else: + if censored: + # data is an instance of CensoredData, but actually holds + # no censored values, so replace it with the array of + # uncensored values. + data = data._uncensored + return fun(self, data, *args, **kwds) + + return wrapper + + +def _get_left_bracket(fun, rbrack, lbrack=None): + # find left bracket for `root_scalar`. A guess for lbrack may be provided. + lbrack = lbrack or rbrack - 1 + diff = rbrack - lbrack + + # if there is no sign change in `fun` between the brackets, expand + # rbrack - lbrack until a sign change occurs + def interval_contains_root(lbrack, rbrack): + # return true if the signs disagree. + return np.sign(fun(lbrack)) != np.sign(fun(rbrack)) + + while not interval_contains_root(lbrack, rbrack): + diff *= 2 + lbrack = rbrack - diff + + msg = ("The solver could not find a bracket containing a " + "root to an MLE first order condition.") + if np.isinf(lbrack): + raise FitSolverError(msg) + + return lbrack + + +class ksone_gen(rv_continuous): + r"""Kolmogorov-Smirnov one-sided test statistic distribution. + + This is the distribution of the one-sided Kolmogorov-Smirnov (KS) + statistics :math:`D_n^+` and :math:`D_n^-` + for a finite sample size ``n >= 1`` (the shape parameter). + + %(before_notes)s + + See Also + -------- + kstwobign, kstwo, kstest + + Notes + ----- + :math:`D_n^+` and :math:`D_n^-` are given by + + .. math:: + + D_n^+ &= \text{sup}_x (F_n(x) - F(x)),\\ + D_n^- &= \text{sup}_x (F(x) - F_n(x)),\\ + + where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF. + `ksone` describes the distribution under the null hypothesis of the KS test + that the empirical CDF corresponds to :math:`n` i.i.d. random variates + with CDF :math:`F`. + + %(after_notes)s + + References + ---------- + .. [1] Birnbaum, Z. W. and Tingey, F.H. "One-sided confidence contours + for probability distribution functions", The Annals of Mathematical + Statistics, 22(4), pp 592-596 (1951). + + %(example)s + + """ + def _argcheck(self, n): + return (n >= 1) & (n == np.round(n)) + + def _shape_info(self): + return [_ShapeInfo("n", True, (1, np.inf), (True, False))] + + def _pdf(self, x, n): + return -scu._smirnovp(n, x) + + def _cdf(self, x, n): + return scu._smirnovc(n, x) + + def _sf(self, x, n): + return sc.smirnov(n, x) + + def _ppf(self, q, n): + return scu._smirnovci(n, q) + + def _isf(self, q, n): + return sc.smirnovi(n, q) + + +ksone = ksone_gen(a=0.0, b=1.0, name='ksone') + + +class kstwo_gen(rv_continuous): + r"""Kolmogorov-Smirnov two-sided test statistic distribution. + + This is the distribution of the two-sided Kolmogorov-Smirnov (KS) + statistic :math:`D_n` for a finite sample size ``n >= 1`` + (the shape parameter). + + %(before_notes)s + + See Also + -------- + kstwobign, ksone, kstest + + Notes + ----- + :math:`D_n` is given by + + .. math:: + + D_n = \text{sup}_x |F_n(x) - F(x)| + + where :math:`F` is a (continuous) CDF and :math:`F_n` is an empirical CDF. + `kstwo` describes the distribution under the null hypothesis of the KS test + that the empirical CDF corresponds to :math:`n` i.i.d. random variates + with CDF :math:`F`. + + %(after_notes)s + + References + ---------- + .. [1] Simard, R., L'Ecuyer, P. "Computing the Two-Sided + Kolmogorov-Smirnov Distribution", Journal of Statistical Software, + Vol 39, 11, 1-18 (2011). + + %(example)s + + """ + def _argcheck(self, n): + return (n >= 1) & (n == np.round(n)) + + def _shape_info(self): + return [_ShapeInfo("n", True, (1, np.inf), (True, False))] + + def _get_support(self, n): + return (0.5/(n if not isinstance(n, Iterable) else np.asanyarray(n)), + 1.0) + + def _pdf(self, x, n): + return kolmognp(n, x) + + def _cdf(self, x, n): + return kolmogn(n, x) + + def _sf(self, x, n): + return kolmogn(n, x, cdf=False) + + def _ppf(self, q, n): + return kolmogni(n, q, cdf=True) + + def _isf(self, q, n): + return kolmogni(n, q, cdf=False) + + +# Use the pdf, (not the ppf) to compute moments +kstwo = kstwo_gen(momtype=0, a=0.0, b=1.0, name='kstwo') + + +class kstwobign_gen(rv_continuous): + r"""Limiting distribution of scaled Kolmogorov-Smirnov two-sided test statistic. + + This is the asymptotic distribution of the two-sided Kolmogorov-Smirnov + statistic :math:`\sqrt{n} D_n` that measures the maximum absolute + distance of the theoretical (continuous) CDF from the empirical CDF. + (see `kstest`). + + %(before_notes)s + + See Also + -------- + ksone, kstwo, kstest + + Notes + ----- + :math:`\sqrt{n} D_n` is given by + + .. math:: + + D_n = \text{sup}_x |F_n(x) - F(x)| + + where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF. + `kstwobign` describes the asymptotic distribution (i.e. the limit of + :math:`\sqrt{n} D_n`) under the null hypothesis of the KS test that the + empirical CDF corresponds to i.i.d. random variates with CDF :math:`F`. + + %(after_notes)s + + References + ---------- + .. [1] Feller, W. "On the Kolmogorov-Smirnov Limit Theorems for Empirical + Distributions", Ann. Math. Statist. Vol 19, 177-189 (1948). + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + return -scu._kolmogp(x) + + def _cdf(self, x): + return scu._kolmogc(x) + + def _sf(self, x): + return sc.kolmogorov(x) + + def _ppf(self, q): + return scu._kolmogci(q) + + def _isf(self, q): + return sc.kolmogi(q) + + +kstwobign = kstwobign_gen(a=0.0, name='kstwobign') + + +## Normal distribution + +# loc = mu, scale = std +# Keep these implementations out of the class definition so they can be reused +# by other distributions. +_norm_pdf_C = np.sqrt(2*np.pi) +_norm_pdf_logC = np.log(_norm_pdf_C) + + +def _norm_pdf(x): + return np.exp(-x**2/2.0) / _norm_pdf_C + + +def _norm_logpdf(x): + return -x**2 / 2.0 - _norm_pdf_logC + + +def _norm_cdf(x): + return sc.ndtr(x) + + +def _norm_logcdf(x): + return sc.log_ndtr(x) + + +def _norm_ppf(q): + return sc.ndtri(q) + + +def _norm_sf(x): + return _norm_cdf(-x) + + +def _norm_logsf(x): + return _norm_logcdf(-x) + + +def _norm_isf(q): + return -_norm_ppf(q) + + +class norm_gen(rv_continuous): + r"""A normal continuous random variable. + + The location (``loc``) keyword specifies the mean. + The scale (``scale``) keyword specifies the standard deviation. + + %(before_notes)s + + Notes + ----- + The probability density function for `norm` is: + + .. math:: + + f(x) = \frac{\exp(-x^2/2)}{\sqrt{2\pi}} + + for a real number :math:`x`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.standard_normal(size) + + def _pdf(self, x): + # norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi) + return _norm_pdf(x) + + def _logpdf(self, x): + return _norm_logpdf(x) + + def _cdf(self, x): + return _norm_cdf(x) + + def _logcdf(self, x): + return _norm_logcdf(x) + + def _sf(self, x): + return _norm_sf(x) + + def _logsf(self, x): + return _norm_logsf(x) + + def _ppf(self, q): + return _norm_ppf(q) + + def _isf(self, q): + return _norm_isf(q) + + def _stats(self): + return 0.0, 1.0, 0.0, 0.0 + + def _entropy(self): + return 0.5*(np.log(2*np.pi)+1) + + @_call_super_mom + @replace_notes_in_docstring(rv_continuous, notes="""\ + For the normal distribution, method of moments and maximum likelihood + estimation give identical fits, and explicit formulas for the estimates + are available. + This function uses these explicit formulas for the maximum likelihood + estimation of the normal distribution parameters, so the + `optimizer` and `method` arguments are ignored.\n\n""") + def fit(self, data, **kwds): + floc = kwds.pop('floc', None) + fscale = kwds.pop('fscale', None) + + _remove_optimizer_parameters(kwds) + + if floc is not None and fscale is not None: + # This check is for consistency with `rv_continuous.fit`. + # Without this check, this function would just return the + # parameters that were given. + raise ValueError("All parameters fixed. There is nothing to " + "optimize.") + + data = np.asarray(data) + + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + if floc is None: + loc = data.mean() + else: + loc = floc + + if fscale is None: + scale = np.sqrt(((data - loc)**2).mean()) + else: + scale = fscale + + return loc, scale + + def _munp(self, n): + """ + @returns Moments of standard normal distribution for integer n >= 0 + + See eq. 16 of https://arxiv.org/abs/1209.4340v2 + """ + if n % 2 == 0: + return sc.factorial2(n - 1) + else: + return 0. + + +norm = norm_gen(name='norm') + + +class alpha_gen(rv_continuous): + r"""An alpha continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `alpha` ([1]_, [2]_) is: + + .. math:: + + f(x, a) = \frac{1}{x^2 \Phi(a) \sqrt{2\pi}} * + \exp(-\frac{1}{2} (a-1/x)^2) + + where :math:`\Phi` is the normal CDF, :math:`x > 0`, and :math:`a > 0`. + + `alpha` takes ``a`` as a shape parameter. + + %(after_notes)s + + References + ---------- + .. [1] Johnson, Kotz, and Balakrishnan, "Continuous Univariate + Distributions, Volume 1", Second Edition, John Wiley and Sons, + p. 173 (1994). + .. [2] Anthony A. Salvia, "Reliability applications of the Alpha + Distribution", IEEE Transactions on Reliability, Vol. R-34, + No. 3, pp. 251-252 (1985). + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _pdf(self, x, a): + # alpha.pdf(x, a) = 1/(x**2*Phi(a)*sqrt(2*pi)) * exp(-1/2 * (a-1/x)**2) + return 1.0/(x**2)/_norm_cdf(a)*_norm_pdf(a-1.0/x) + + def _logpdf(self, x, a): + return -2*np.log(x) + _norm_logpdf(a-1.0/x) - np.log(_norm_cdf(a)) + + def _cdf(self, x, a): + return _norm_cdf(a-1.0/x) / _norm_cdf(a) + + def _ppf(self, q, a): + return 1.0/np.asarray(a - _norm_ppf(q*_norm_cdf(a))) + + def _stats(self, a): + return [np.inf]*2 + [np.nan]*2 + + +alpha = alpha_gen(a=0.0, name='alpha') + + +class anglit_gen(rv_continuous): + r"""An anglit continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `anglit` is: + + .. math:: + + f(x) = \sin(2x + \pi/2) = \cos(2x) + + for :math:`-\pi/4 \le x \le \pi/4`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # anglit.pdf(x) = sin(2*x + \pi/2) = cos(2*x) + return np.cos(2*x) + + def _cdf(self, x): + return np.sin(x+np.pi/4)**2.0 + + def _sf(self, x): + return np.cos(x + np.pi / 4) ** 2.0 + + def _ppf(self, q): + return np.arcsin(np.sqrt(q))-np.pi/4 + + def _stats(self): + return 0.0, np.pi*np.pi/16-0.5, 0.0, -2*(np.pi**4 - 96)/(np.pi*np.pi-8)**2 + + def _entropy(self): + return 1-np.log(2) + + +anglit = anglit_gen(a=-np.pi/4, b=np.pi/4, name='anglit') + + +class arcsine_gen(rv_continuous): + r"""An arcsine continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `arcsine` is: + + .. math:: + + f(x) = \frac{1}{\pi \sqrt{x (1-x)}} + + for :math:`0 < x < 1`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # arcsine.pdf(x) = 1/(pi*sqrt(x*(1-x))) + with np.errstate(divide='ignore'): + return 1.0/np.pi/np.sqrt(x*(1-x)) + + def _cdf(self, x): + return 2.0/np.pi*np.arcsin(np.sqrt(x)) + + def _ppf(self, q): + return np.sin(np.pi/2.0*q)**2.0 + + def _stats(self): + mu = 0.5 + mu2 = 1.0/8 + g1 = 0 + g2 = -3.0/2.0 + return mu, mu2, g1, g2 + + def _entropy(self): + return -0.24156447527049044468 + + +arcsine = arcsine_gen(a=0.0, b=1.0, name='arcsine') + + +class FitDataError(ValueError): + """Raised when input data is inconsistent with fixed parameters.""" + # This exception is raised by, for example, beta_gen.fit when both floc + # and fscale are fixed and there are values in the data not in the open + # interval (floc, floc+fscale). + def __init__(self, distr, lower, upper): + self.args = ( + "Invalid values in `data`. Maximum likelihood " + f"estimation with {distr!r} requires that {lower!r} < " + f"(x - loc)/scale < {upper!r} for each x in `data`.", + ) + + +class FitSolverError(FitError): + """ + Raised when a solver fails to converge while fitting a distribution. + """ + # This exception is raised by, for example, beta_gen.fit when + # optimize.fsolve returns with ier != 1. + def __init__(self, mesg): + emsg = "Solver for the MLE equations failed to converge: " + emsg += mesg.replace('\n', '') + self.args = (emsg,) + + +def _beta_mle_a(a, b, n, s1): + # The zeros of this function give the MLE for `a`, with + # `b`, `n` and `s1` given. `s1` is the sum of the logs of + # the data. `n` is the number of data points. + psiab = sc.psi(a + b) + func = s1 - n * (-psiab + sc.psi(a)) + return func + + +def _beta_mle_ab(theta, n, s1, s2): + # Zeros of this function are critical points of + # the maximum likelihood function. Solving this system + # for theta (which contains a and b) gives the MLE for a and b + # given `n`, `s1` and `s2`. `s1` is the sum of the logs of the data, + # and `s2` is the sum of the logs of 1 - data. `n` is the number + # of data points. + a, b = theta + psiab = sc.psi(a + b) + func = [s1 - n * (-psiab + sc.psi(a)), + s2 - n * (-psiab + sc.psi(b))] + return func + + +class beta_gen(rv_continuous): + r"""A beta continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `beta` is: + + .. math:: + + f(x, a, b) = \frac{\Gamma(a+b) x^{a-1} (1-x)^{b-1}} + {\Gamma(a) \Gamma(b)} + + for :math:`0 <= x <= 1`, :math:`a > 0`, :math:`b > 0`, where + :math:`\Gamma` is the gamma function (`scipy.special.gamma`). + + `beta` takes :math:`a` and :math:`b` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _rvs(self, a, b, size=None, random_state=None): + return random_state.beta(a, b, size) + + def _pdf(self, x, a, b): + # gamma(a+b) * x**(a-1) * (1-x)**(b-1) + # beta.pdf(x, a, b) = ------------------------------------ + # gamma(a)*gamma(b) + with np.errstate(over='ignore'): + return _boost._beta_pdf(x, a, b) + + def _logpdf(self, x, a, b): + lPx = sc.xlog1py(b - 1.0, -x) + sc.xlogy(a - 1.0, x) + lPx -= sc.betaln(a, b) + return lPx + + def _cdf(self, x, a, b): + return _boost._beta_cdf(x, a, b) + + def _sf(self, x, a, b): + return _boost._beta_sf(x, a, b) + + def _isf(self, x, a, b): + return sc.betainccinv(a, b, x) + + def _ppf(self, q, a, b): + return sc.betaincinv(a, b, q) + + def _stats(self, a, b): + return ( + _boost._beta_mean(a, b), + _boost._beta_variance(a, b), + _boost._beta_skewness(a, b), + _boost._beta_kurtosis_excess(a, b)) + + def _fitstart(self, data): + if isinstance(data, CensoredData): + data = data._uncensor() + + g1 = _skew(data) + g2 = _kurtosis(data) + + def func(x): + a, b = x + sk = 2*(b-a)*np.sqrt(a + b + 1) / (a + b + 2) / np.sqrt(a*b) + ku = a**3 - a**2*(2*b-1) + b**2*(b+1) - 2*a*b*(b+2) + ku /= a*b*(a+b+2)*(a+b+3) + ku *= 6 + return [sk-g1, ku-g2] + a, b = optimize.fsolve(func, (1.0, 1.0)) + return super()._fitstart(data, args=(a, b)) + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + In the special case where `method="MLE"` and + both `floc` and `fscale` are given, a + `ValueError` is raised if any value `x` in `data` does not satisfy + `floc < x < floc + fscale`.\n\n""") + def fit(self, data, *args, **kwds): + # Override rv_continuous.fit, so we can more efficiently handle the + # case where floc and fscale are given. + + floc = kwds.get('floc', None) + fscale = kwds.get('fscale', None) + + if floc is None or fscale is None: + # do general fit + return super().fit(data, *args, **kwds) + + # We already got these from kwds, so just pop them. + kwds.pop('floc', None) + kwds.pop('fscale', None) + + f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a']) + f1 = _get_fixed_fit_value(kwds, ['f1', 'fb', 'fix_b']) + + _remove_optimizer_parameters(kwds) + + if f0 is not None and f1 is not None: + # This check is for consistency with `rv_continuous.fit`. + raise ValueError("All parameters fixed. There is nothing to " + "optimize.") + + # Special case: loc and scale are constrained, so we are fitting + # just the shape parameters. This can be done much more efficiently + # than the method used in `rv_continuous.fit`. (See the subsection + # "Two unknown parameters" in the section "Maximum likelihood" of + # the Wikipedia article on the Beta distribution for the formulas.) + + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + # Normalize the data to the interval [0, 1]. + data = (np.ravel(data) - floc) / fscale + if np.any(data <= 0) or np.any(data >= 1): + raise FitDataError("beta", lower=floc, upper=floc + fscale) + + xbar = data.mean() + + if f0 is not None or f1 is not None: + # One of the shape parameters is fixed. + + if f0 is not None: + # The shape parameter a is fixed, so swap the parameters + # and flip the data. We always solve for `a`. The result + # will be swapped back before returning. + b = f0 + data = 1 - data + xbar = 1 - xbar + else: + b = f1 + + # Initial guess for a. Use the formula for the mean of the beta + # distribution, E[x] = a / (a + b), to generate a reasonable + # starting point based on the mean of the data and the given + # value of b. + a = b * xbar / (1 - xbar) + + # Compute the MLE for `a` by solving _beta_mle_a. + theta, info, ier, mesg = optimize.fsolve( + _beta_mle_a, a, + args=(b, len(data), np.log(data).sum()), + full_output=True + ) + if ier != 1: + raise FitSolverError(mesg=mesg) + a = theta[0] + + if f0 is not None: + # The shape parameter a was fixed, so swap back the + # parameters. + a, b = b, a + + else: + # Neither of the shape parameters is fixed. + + # s1 and s2 are used in the extra arguments passed to _beta_mle_ab + # by optimize.fsolve. + s1 = np.log(data).sum() + s2 = sc.log1p(-data).sum() + + # Use the "method of moments" to estimate the initial + # guess for a and b. + fac = xbar * (1 - xbar) / data.var(ddof=0) - 1 + a = xbar * fac + b = (1 - xbar) * fac + + # Compute the MLE for a and b by solving _beta_mle_ab. + theta, info, ier, mesg = optimize.fsolve( + _beta_mle_ab, [a, b], + args=(len(data), s1, s2), + full_output=True + ) + if ier != 1: + raise FitSolverError(mesg=mesg) + a, b = theta + + return a, b, floc, fscale + + def _entropy(self, a, b): + def regular(a, b): + return (sc.betaln(a, b) - (a - 1) * sc.psi(a) - + (b - 1) * sc.psi(b) + (a + b - 2) * sc.psi(a + b)) + + def asymptotic_ab_large(a, b): + sum_ab = a + b + log_term = 0.5 * ( + np.log(2*np.pi) + np.log(a) + np.log(b) - 3*np.log(sum_ab) + 1 + ) + t1 = 110/sum_ab + 20*sum_ab**-2.0 + sum_ab**-3.0 - 2*sum_ab**-4.0 + t2 = -50/a - 10*a**-2.0 - a**-3.0 + a**-4.0 + t3 = -50/b - 10*b**-2.0 - b**-3.0 + b**-4.0 + return log_term + (t1 + t2 + t3) / 120 + + def asymptotic_b_large(a, b): + sum_ab = a + b + t1 = sc.gammaln(a) - (a - 1) * sc.psi(a) + t2 = ( + - 1/(2*b) + 1/(12*b) - b**-2.0/12 - b**-3.0/120 + b**-4.0/120 + + b**-5.0/252 - b**-6.0/252 + 1/sum_ab - 1/(12*sum_ab) + + sum_ab**-2.0/6 + sum_ab**-3.0/120 - sum_ab**-4.0/60 + - sum_ab**-5.0/252 + sum_ab**-6.0/126 + ) + log_term = sum_ab*np.log1p(a/b) + np.log(b) - 2*np.log(sum_ab) + return t1 + t2 + log_term + + def threshold_large(v): + if v == 1.0: + return 1000 + + j = np.log10(v) + digits = int(j) + d = int(v / 10 ** digits) + 2 + return d*10**(7 + j) + + if a >= 4.96e6 and b >= 4.96e6: + return asymptotic_ab_large(a, b) + elif a <= 4.9e6 and b - a >= 1e6 and b >= threshold_large(a): + return asymptotic_b_large(a, b) + elif b <= 4.9e6 and a - b >= 1e6 and a >= threshold_large(b): + return asymptotic_b_large(b, a) + else: + return regular(a, b) + + +beta = beta_gen(a=0.0, b=1.0, name='beta') + + +class betaprime_gen(rv_continuous): + r"""A beta prime continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `betaprime` is: + + .. math:: + + f(x, a, b) = \frac{x^{a-1} (1+x)^{-a-b}}{\beta(a, b)} + + for :math:`x >= 0`, :math:`a > 0`, :math:`b > 0`, where + :math:`\beta(a, b)` is the beta function (see `scipy.special.beta`). + + `betaprime` takes ``a`` and ``b`` as shape parameters. + + The distribution is related to the `beta` distribution as follows: + If :math:`X` follows a beta distribution with parameters :math:`a, b`, + then :math:`Y = X/(1-X)` has a beta prime distribution with + parameters :math:`a, b` ([1]_). + + The beta prime distribution is a reparametrized version of the + F distribution. The beta prime distribution with shape parameters + ``a`` and ``b`` and ``scale = s`` is equivalent to the F distribution + with parameters ``d1 = 2*a``, ``d2 = 2*b`` and ``scale = (a/b)*s``. + For example, + + >>> from scipy.stats import betaprime, f + >>> x = [1, 2, 5, 10] + >>> a = 12 + >>> b = 5 + >>> betaprime.pdf(x, a, b, scale=2) + array([0.00541179, 0.08331299, 0.14669185, 0.03150079]) + >>> f.pdf(x, 2*a, 2*b, scale=(a/b)*2) + array([0.00541179, 0.08331299, 0.14669185, 0.03150079]) + + %(after_notes)s + + References + ---------- + .. [1] Beta prime distribution, Wikipedia, + https://en.wikipedia.org/wiki/Beta_prime_distribution + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _rvs(self, a, b, size=None, random_state=None): + u1 = gamma.rvs(a, size=size, random_state=random_state) + u2 = gamma.rvs(b, size=size, random_state=random_state) + return u1 / u2 + + def _pdf(self, x, a, b): + # betaprime.pdf(x, a, b) = x**(a-1) * (1+x)**(-a-b) / beta(a, b) + return np.exp(self._logpdf(x, a, b)) + + def _logpdf(self, x, a, b): + return sc.xlogy(a - 1.0, x) - sc.xlog1py(a + b, x) - sc.betaln(a, b) + + def _cdf(self, x, a, b): + # note: f2 is the direct way to compute the cdf if the relationship + # to the beta distribution is used. + # however, for very large x, x/(1+x) == 1. since the distribution + # has very fat tails if b is small, this can cause inaccurate results + # use the following relationship of the incomplete beta function: + # betainc(x, a, b) = 1 - betainc(1-x, b, a) + # see gh-17631 + return _lazywhere( + x > 1, [x, a, b], + lambda x_, a_, b_: beta._sf(1/(1+x_), b_, a_), + f2=lambda x_, a_, b_: beta._cdf(x_/(1+x_), a_, b_)) + + def _sf(self, x, a, b): + return _lazywhere( + x > 1, [x, a, b], + lambda x_, a_, b_: beta._cdf(1/(1+x_), b_, a_), + f2=lambda x_, a_, b_: beta._sf(x_/(1+x_), a_, b_) + ) + + def _ppf(self, p, a, b): + p, a, b = np.broadcast_arrays(p, a, b) + # by default, compute compute the ppf by solving the following: + # p = beta._cdf(x/(1+x), a, b). This implies x = r/(1-r) with + # r = beta._ppf(p, a, b). This can cause numerical issues if r is + # very close to 1. in that case, invert the alternative expression of + # the cdf: p = beta._sf(1/(1+x), b, a). + r = stats.beta._ppf(p, a, b) + with np.errstate(divide='ignore'): + out = r / (1 - r) + i = (r > 0.9999) + out[i] = 1/stats.beta._isf(p[i], b[i], a[i]) - 1 + return out + + def _munp(self, n, a, b): + return _lazywhere( + b > n, (a, b), + lambda a, b: np.prod([(a+i-1)/(b-i) for i in range(1, n+1)], axis=0), + fillvalue=np.inf) + + +betaprime = betaprime_gen(a=0.0, name='betaprime') + + +class bradford_gen(rv_continuous): + r"""A Bradford continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `bradford` is: + + .. math:: + + f(x, c) = \frac{c}{\log(1+c) (1+cx)} + + for :math:`0 <= x <= 1` and :math:`c > 0`. + + `bradford` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # bradford.pdf(x, c) = c / (k * (1+c*x)) + return c / (c*x + 1.0) / sc.log1p(c) + + def _cdf(self, x, c): + return sc.log1p(c*x) / sc.log1p(c) + + def _ppf(self, q, c): + return sc.expm1(q * sc.log1p(c)) / c + + def _stats(self, c, moments='mv'): + k = np.log(1.0+c) + mu = (c-k)/(c*k) + mu2 = ((c+2.0)*k-2.0*c)/(2*c*k*k) + g1 = None + g2 = None + if 's' in moments: + g1 = np.sqrt(2)*(12*c*c-9*c*k*(c+2)+2*k*k*(c*(c+3)+3)) + g1 /= np.sqrt(c*(c*(k-2)+2*k))*(3*c*(k-2)+6*k) + if 'k' in moments: + g2 = (c**3*(k-3)*(k*(3*k-16)+24)+12*k*c*c*(k-4)*(k-3) + + 6*c*k*k*(3*k-14) + 12*k**3) + g2 /= 3*c*(c*(k-2)+2*k)**2 + return mu, mu2, g1, g2 + + def _entropy(self, c): + k = np.log(1+c) + return k/2.0 - np.log(c/k) + + +bradford = bradford_gen(a=0.0, b=1.0, name='bradford') + + +class burr_gen(rv_continuous): + r"""A Burr (Type III) continuous random variable. + + %(before_notes)s + + See Also + -------- + fisk : a special case of either `burr` or `burr12` with ``d=1`` + burr12 : Burr Type XII distribution + mielke : Mielke Beta-Kappa / Dagum distribution + + Notes + ----- + The probability density function for `burr` is: + + .. math:: + + f(x; c, d) = c d \frac{x^{-c - 1}} + {{(1 + x^{-c})}^{d + 1}} + + for :math:`x >= 0` and :math:`c, d > 0`. + + `burr` takes ``c`` and ``d`` as shape parameters for :math:`c` and + :math:`d`. + + This is the PDF corresponding to the third CDF given in Burr's list; + specifically, it is equation (11) in Burr's paper [1]_. The distribution + is also commonly referred to as the Dagum distribution [2]_. If the + parameter :math:`c < 1` then the mean of the distribution does not + exist and if :math:`c < 2` the variance does not exist [2]_. + The PDF is finite at the left endpoint :math:`x = 0` if :math:`c * d >= 1`. + + %(after_notes)s + + References + ---------- + .. [1] Burr, I. W. "Cumulative frequency functions", Annals of + Mathematical Statistics, 13(2), pp 215-232 (1942). + .. [2] https://en.wikipedia.org/wiki/Dagum_distribution + .. [3] Kleiber, Christian. "A guide to the Dagum distributions." + Modeling Income Distributions and Lorenz Curves pp 97-117 (2008). + + %(example)s + + """ + # Do not set _support_mask to rv_continuous._open_support_mask + # Whether the left-hand endpoint is suitable for pdf evaluation is dependent + # on the values of c and d: if c*d >= 1, the pdf is finite, otherwise infinite. + + def _shape_info(self): + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + id = _ShapeInfo("d", False, (0, np.inf), (False, False)) + return [ic, id] + + def _pdf(self, x, c, d): + # burr.pdf(x, c, d) = c * d * x**(-c-1) * (1+x**(-c))**(-d-1) + output = _lazywhere( + x == 0, [x, c, d], + lambda x_, c_, d_: c_ * d_ * (x_**(c_*d_-1)) / (1 + x_**c_), + f2=lambda x_, c_, d_: (c_ * d_ * (x_ ** (-c_ - 1.0)) / + ((1 + x_ ** (-c_)) ** (d_ + 1.0)))) + if output.ndim == 0: + return output[()] + return output + + def _logpdf(self, x, c, d): + output = _lazywhere( + x == 0, [x, c, d], + lambda x_, c_, d_: (np.log(c_) + np.log(d_) + sc.xlogy(c_*d_ - 1, x_) + - (d_+1) * sc.log1p(x_**(c_))), + f2=lambda x_, c_, d_: (np.log(c_) + np.log(d_) + + sc.xlogy(-c_ - 1, x_) + - sc.xlog1py(d_+1, x_**(-c_)))) + if output.ndim == 0: + return output[()] + return output + + def _cdf(self, x, c, d): + return (1 + x**(-c))**(-d) + + def _logcdf(self, x, c, d): + return sc.log1p(x**(-c)) * (-d) + + def _sf(self, x, c, d): + return np.exp(self._logsf(x, c, d)) + + def _logsf(self, x, c, d): + return np.log1p(- (1 + x**(-c))**(-d)) + + def _ppf(self, q, c, d): + return (q**(-1.0/d) - 1)**(-1.0/c) + + def _isf(self, q, c, d): + _q = sc.xlog1py(-1.0 / d, -q) + return sc.expm1(_q) ** (-1.0 / c) + + def _stats(self, c, d): + nc = np.arange(1, 5).reshape(4,1) / c + # ek is the kth raw moment, e1 is the mean e2-e1**2 variance etc. + e1, e2, e3, e4 = sc.beta(d + nc, 1. - nc) * d + mu = np.where(c > 1.0, e1, np.nan) + mu2_if_c = e2 - mu**2 + mu2 = np.where(c > 2.0, mu2_if_c, np.nan) + g1 = _lazywhere( + c > 3.0, + (c, e1, e2, e3, mu2_if_c), + lambda c, e1, e2, e3, mu2_if_c: ((e3 - 3*e2*e1 + 2*e1**3) + / np.sqrt((mu2_if_c)**3)), + fillvalue=np.nan) + g2 = _lazywhere( + c > 4.0, + (c, e1, e2, e3, e4, mu2_if_c), + lambda c, e1, e2, e3, e4, mu2_if_c: ( + ((e4 - 4*e3*e1 + 6*e2*e1**2 - 3*e1**4) / mu2_if_c**2) - 3), + fillvalue=np.nan) + if np.ndim(c) == 0: + return mu.item(), mu2.item(), g1.item(), g2.item() + return mu, mu2, g1, g2 + + def _munp(self, n, c, d): + def __munp(n, c, d): + nc = 1. * n / c + return d * sc.beta(1.0 - nc, d + nc) + n, c, d = np.asarray(n), np.asarray(c), np.asarray(d) + return _lazywhere((c > n) & (n == n) & (d == d), (c, d, n), + lambda c, d, n: __munp(n, c, d), + np.nan) + + +burr = burr_gen(a=0.0, name='burr') + + +class burr12_gen(rv_continuous): + r"""A Burr (Type XII) continuous random variable. + + %(before_notes)s + + See Also + -------- + fisk : a special case of either `burr` or `burr12` with ``d=1`` + burr : Burr Type III distribution + + Notes + ----- + The probability density function for `burr12` is: + + .. math:: + + f(x; c, d) = c d \frac{x^{c-1}} + {(1 + x^c)^{d + 1}} + + for :math:`x >= 0` and :math:`c, d > 0`. + + `burr12` takes ``c`` and ``d`` as shape parameters for :math:`c` + and :math:`d`. + + This is the PDF corresponding to the twelfth CDF given in Burr's list; + specifically, it is equation (20) in Burr's paper [1]_. + + %(after_notes)s + + The Burr type 12 distribution is also sometimes referred to as + the Singh-Maddala distribution from NIST [2]_. + + References + ---------- + .. [1] Burr, I. W. "Cumulative frequency functions", Annals of + Mathematical Statistics, 13(2), pp 215-232 (1942). + + .. [2] https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/b12pdf.htm + + .. [3] "Burr distribution", + https://en.wikipedia.org/wiki/Burr_distribution + + %(example)s + + """ + def _shape_info(self): + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + id = _ShapeInfo("d", False, (0, np.inf), (False, False)) + return [ic, id] + + def _pdf(self, x, c, d): + # burr12.pdf(x, c, d) = c * d * x**(c-1) * (1+x**(c))**(-d-1) + return np.exp(self._logpdf(x, c, d)) + + def _logpdf(self, x, c, d): + return np.log(c) + np.log(d) + sc.xlogy(c - 1, x) + sc.xlog1py(-d-1, x**c) + + def _cdf(self, x, c, d): + return -sc.expm1(self._logsf(x, c, d)) + + def _logcdf(self, x, c, d): + return sc.log1p(-(1 + x**c)**(-d)) + + def _sf(self, x, c, d): + return np.exp(self._logsf(x, c, d)) + + def _logsf(self, x, c, d): + return sc.xlog1py(-d, x**c) + + def _ppf(self, q, c, d): + # The following is an implementation of + # ((1 - q)**(-1.0/d) - 1)**(1.0/c) + # that does a better job handling small values of q. + return sc.expm1(-1/d * sc.log1p(-q))**(1/c) + + def _munp(self, n, c, d): + def moment_if_exists(n, c, d): + nc = 1. * n / c + return d * sc.beta(1.0 + nc, d - nc) + + return _lazywhere(c * d > n, (n, c, d), moment_if_exists, + fillvalue=np.nan) + + +burr12 = burr12_gen(a=0.0, name='burr12') + + +class fisk_gen(burr_gen): + r"""A Fisk continuous random variable. + + The Fisk distribution is also known as the log-logistic distribution. + + %(before_notes)s + + See Also + -------- + burr + + Notes + ----- + The probability density function for `fisk` is: + + .. math:: + + f(x, c) = \frac{c x^{c-1}} + {(1 + x^c)^2} + + for :math:`x >= 0` and :math:`c > 0`. + + Please note that the above expression can be transformed into the following + one, which is also commonly used: + + .. math:: + + f(x, c) = \frac{c x^{-c-1}} + {(1 + x^{-c})^2} + + `fisk` takes ``c`` as a shape parameter for :math:`c`. + + `fisk` is a special case of `burr` or `burr12` with ``d=1``. + + Suppose ``X`` is a logistic random variable with location ``l`` + and scale ``s``. Then ``Y = exp(X)`` is a Fisk (log-logistic) + random variable with ``scale = exp(l)`` and shape ``c = 1/s``. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2) + return burr._pdf(x, c, 1.0) + + def _cdf(self, x, c): + return burr._cdf(x, c, 1.0) + + def _sf(self, x, c): + return burr._sf(x, c, 1.0) + + def _logpdf(self, x, c): + # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2) + return burr._logpdf(x, c, 1.0) + + def _logcdf(self, x, c): + return burr._logcdf(x, c, 1.0) + + def _logsf(self, x, c): + return burr._logsf(x, c, 1.0) + + def _ppf(self, x, c): + return burr._ppf(x, c, 1.0) + + def _isf(self, q, c): + return burr._isf(q, c, 1.0) + + def _munp(self, n, c): + return burr._munp(n, c, 1.0) + + def _stats(self, c): + return burr._stats(c, 1.0) + + def _entropy(self, c): + return 2 - np.log(c) + + +fisk = fisk_gen(a=0.0, name='fisk') + + +class cauchy_gen(rv_continuous): + r"""A Cauchy continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `cauchy` is + + .. math:: + + f(x) = \frac{1}{\pi (1 + x^2)} + + for a real number :math:`x`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # cauchy.pdf(x) = 1 / (pi * (1 + x**2)) + return 1.0/np.pi/(1.0+x*x) + + def _cdf(self, x): + return 0.5 + 1.0/np.pi*np.arctan(x) + + def _ppf(self, q): + return np.tan(np.pi*q-np.pi/2.0) + + def _sf(self, x): + return 0.5 - 1.0/np.pi*np.arctan(x) + + def _isf(self, q): + return np.tan(np.pi/2.0-np.pi*q) + + def _stats(self): + return np.nan, np.nan, np.nan, np.nan + + def _entropy(self): + return np.log(4*np.pi) + + def _fitstart(self, data, args=None): + # Initialize ML guesses using quartiles instead of moments. + if isinstance(data, CensoredData): + data = data._uncensor() + p25, p50, p75 = np.percentile(data, [25, 50, 75]) + return p50, (p75 - p25)/2 + + +cauchy = cauchy_gen(name='cauchy') + + +class chi_gen(rv_continuous): + r"""A chi continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `chi` is: + + .. math:: + + f(x, k) = \frac{1}{2^{k/2-1} \Gamma \left( k/2 \right)} + x^{k-1} \exp \left( -x^2/2 \right) + + for :math:`x >= 0` and :math:`k > 0` (degrees of freedom, denoted ``df`` + in the implementation). :math:`\Gamma` is the gamma function + (`scipy.special.gamma`). + + Special cases of `chi` are: + + - ``chi(1, loc, scale)`` is equivalent to `halfnorm` + - ``chi(2, 0, scale)`` is equivalent to `rayleigh` + - ``chi(3, 0, scale)`` is equivalent to `maxwell` + + `chi` takes ``df`` as a shape parameter. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("df", False, (0, np.inf), (False, False))] + + def _rvs(self, df, size=None, random_state=None): + return np.sqrt(chi2.rvs(df, size=size, random_state=random_state)) + + def _pdf(self, x, df): + # x**(df-1) * exp(-x**2/2) + # chi.pdf(x, df) = ------------------------- + # 2**(df/2-1) * gamma(df/2) + return np.exp(self._logpdf(x, df)) + + def _logpdf(self, x, df): + l = np.log(2) - .5*np.log(2)*df - sc.gammaln(.5*df) + return l + sc.xlogy(df - 1., x) - .5*x**2 + + def _cdf(self, x, df): + return sc.gammainc(.5*df, .5*x**2) + + def _sf(self, x, df): + return sc.gammaincc(.5*df, .5*x**2) + + def _ppf(self, q, df): + return np.sqrt(2*sc.gammaincinv(.5*df, q)) + + def _isf(self, q, df): + return np.sqrt(2*sc.gammainccinv(.5*df, q)) + + def _stats(self, df): + # poch(df/2, 1/2) = gamma(df/2 + 1/2) / gamma(df/2) + mu = np.sqrt(2) * sc.poch(0.5 * df, 0.5) + mu2 = df - mu*mu + g1 = (2*mu**3.0 + mu*(1-2*df))/np.asarray(np.power(mu2, 1.5)) + g2 = 2*df*(1.0-df)-6*mu**4 + 4*mu**2 * (2*df-1) + g2 /= np.asarray(mu2**2.0) + return mu, mu2, g1, g2 + + def _entropy(self, df): + + def regular_formula(df): + return (sc.gammaln(.5 * df) + + 0.5 * (df - np.log(2) - (df - 1) * sc.digamma(0.5 * df))) + + def asymptotic_formula(df): + return (0.5 + np.log(np.pi)/2 - (df**-1)/6 - (df**-2)/6 + - 4/45*(df**-3) + (df**-4)/15) + + return _lazywhere(df < 3e2, (df, ), regular_formula, + f2=asymptotic_formula) + + +chi = chi_gen(a=0.0, name='chi') + + +class chi2_gen(rv_continuous): + r"""A chi-squared continuous random variable. + + For the noncentral chi-square distribution, see `ncx2`. + + %(before_notes)s + + See Also + -------- + ncx2 + + Notes + ----- + The probability density function for `chi2` is: + + .. math:: + + f(x, k) = \frac{1}{2^{k/2} \Gamma \left( k/2 \right)} + x^{k/2-1} \exp \left( -x/2 \right) + + for :math:`x > 0` and :math:`k > 0` (degrees of freedom, denoted ``df`` + in the implementation). + + `chi2` takes ``df`` as a shape parameter. + + The chi-squared distribution is a special case of the gamma + distribution, with gamma parameters ``a = df/2``, ``loc = 0`` and + ``scale = 2``. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("df", False, (0, np.inf), (False, False))] + + def _rvs(self, df, size=None, random_state=None): + return random_state.chisquare(df, size) + + def _pdf(self, x, df): + # chi2.pdf(x, df) = 1 / (2*gamma(df/2)) * (x/2)**(df/2-1) * exp(-x/2) + return np.exp(self._logpdf(x, df)) + + def _logpdf(self, x, df): + return sc.xlogy(df/2.-1, x) - x/2. - sc.gammaln(df/2.) - (np.log(2)*df)/2. + + def _cdf(self, x, df): + return sc.chdtr(df, x) + + def _sf(self, x, df): + return sc.chdtrc(df, x) + + def _isf(self, p, df): + return sc.chdtri(df, p) + + def _ppf(self, p, df): + return 2*sc.gammaincinv(df/2, p) + + def _stats(self, df): + mu = df + mu2 = 2*df + g1 = 2*np.sqrt(2.0/df) + g2 = 12.0/df + return mu, mu2, g1, g2 + + def _entropy(self, df): + half_df = 0.5 * df + + def regular_formula(half_df): + return (half_df + np.log(2) + sc.gammaln(half_df) + + (1 - half_df) * sc.psi(half_df)) + + def asymptotic_formula(half_df): + # plug in the above formula the following asymptotic + # expansions: + # ln(gamma(a)) ~ (a - 0.5) * ln(a) - a + 0.5 * ln(2 * pi) + + # 1/(12 * a) - 1/(360 * a**3) + # psi(a) ~ ln(a) - 1/(2 * a) - 1/(3 * a**2) + 1/120 * a**4) + c = np.log(2) + 0.5*(1 + np.log(2*np.pi)) + h = 0.5/half_df + return (h*(-2/3 + h*(-1/3 + h*(-4/45 + h/7.5))) + + 0.5*np.log(half_df) + c) + + return _lazywhere(half_df < 125, (half_df, ), + regular_formula, + f2=asymptotic_formula) + + +chi2 = chi2_gen(a=0.0, name='chi2') + + +class cosine_gen(rv_continuous): + r"""A cosine continuous random variable. + + %(before_notes)s + + Notes + ----- + The cosine distribution is an approximation to the normal distribution. + The probability density function for `cosine` is: + + .. math:: + + f(x) = \frac{1}{2\pi} (1+\cos(x)) + + for :math:`-\pi \le x \le \pi`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # cosine.pdf(x) = 1/(2*pi) * (1+cos(x)) + return 1.0/2/np.pi*(1+np.cos(x)) + + def _logpdf(self, x): + c = np.cos(x) + return _lazywhere(c != -1, (c,), + lambda c: np.log1p(c) - np.log(2*np.pi), + fillvalue=-np.inf) + + def _cdf(self, x): + return scu._cosine_cdf(x) + + def _sf(self, x): + return scu._cosine_cdf(-x) + + def _ppf(self, p): + return scu._cosine_invcdf(p) + + def _isf(self, p): + return -scu._cosine_invcdf(p) + + def _stats(self): + v = (np.pi * np.pi / 3.0) - 2.0 + k = -6.0 * (np.pi**4 - 90) / (5.0 * (np.pi * np.pi - 6)**2) + return 0.0, v, 0.0, k + + def _entropy(self): + return np.log(4*np.pi)-1.0 + + +cosine = cosine_gen(a=-np.pi, b=np.pi, name='cosine') + + +class dgamma_gen(rv_continuous): + r"""A double gamma continuous random variable. + + The double gamma distribution is also known as the reflected gamma + distribution [1]_. + + %(before_notes)s + + Notes + ----- + The probability density function for `dgamma` is: + + .. math:: + + f(x, a) = \frac{1}{2\Gamma(a)} |x|^{a-1} \exp(-|x|) + + for a real number :math:`x` and :math:`a > 0`. :math:`\Gamma` is the + gamma function (`scipy.special.gamma`). + + `dgamma` takes ``a`` as a shape parameter for :math:`a`. + + %(after_notes)s + + References + ---------- + .. [1] Johnson, Kotz, and Balakrishnan, "Continuous Univariate + Distributions, Volume 1", Second Edition, John Wiley and Sons + (1994). + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _rvs(self, a, size=None, random_state=None): + u = random_state.uniform(size=size) + gm = gamma.rvs(a, size=size, random_state=random_state) + return gm * np.where(u >= 0.5, 1, -1) + + def _pdf(self, x, a): + # dgamma.pdf(x, a) = 1 / (2*gamma(a)) * abs(x)**(a-1) * exp(-abs(x)) + ax = abs(x) + return 1.0/(2*sc.gamma(a))*ax**(a-1.0) * np.exp(-ax) + + def _logpdf(self, x, a): + ax = abs(x) + return sc.xlogy(a - 1.0, ax) - ax - np.log(2) - sc.gammaln(a) + + def _cdf(self, x, a): + return np.where(x > 0, + 0.5 + 0.5*sc.gammainc(a, x), + 0.5*sc.gammaincc(a, -x)) + + def _sf(self, x, a): + return np.where(x > 0, + 0.5*sc.gammaincc(a, x), + 0.5 + 0.5*sc.gammainc(a, -x)) + + def _entropy(self, a): + return stats.gamma._entropy(a) - np.log(0.5) + + def _ppf(self, q, a): + return np.where(q > 0.5, + sc.gammaincinv(a, 2*q - 1), + -sc.gammainccinv(a, 2*q)) + + def _isf(self, q, a): + return np.where(q > 0.5, + -sc.gammaincinv(a, 2*q - 1), + sc.gammainccinv(a, 2*q)) + + def _stats(self, a): + mu2 = a*(a+1.0) + return 0.0, mu2, 0.0, (a+2.0)*(a+3.0)/mu2-3.0 + + +dgamma = dgamma_gen(name='dgamma') + + +class dweibull_gen(rv_continuous): + r"""A double Weibull continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `dweibull` is given by + + .. math:: + + f(x, c) = c / 2 |x|^{c-1} \exp(-|x|^c) + + for a real number :math:`x` and :math:`c > 0`. + + `dweibull` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _rvs(self, c, size=None, random_state=None): + u = random_state.uniform(size=size) + w = weibull_min.rvs(c, size=size, random_state=random_state) + return w * (np.where(u >= 0.5, 1, -1)) + + def _pdf(self, x, c): + # dweibull.pdf(x, c) = c / 2 * abs(x)**(c-1) * exp(-abs(x)**c) + ax = abs(x) + Px = c / 2.0 * ax**(c-1.0) * np.exp(-ax**c) + return Px + + def _logpdf(self, x, c): + ax = abs(x) + return np.log(c) - np.log(2.0) + sc.xlogy(c - 1.0, ax) - ax**c + + def _cdf(self, x, c): + Cx1 = 0.5 * np.exp(-abs(x)**c) + return np.where(x > 0, 1 - Cx1, Cx1) + + def _ppf(self, q, c): + fac = 2. * np.where(q <= 0.5, q, 1. - q) + fac = np.power(-np.log(fac), 1.0 / c) + return np.where(q > 0.5, fac, -fac) + + def _sf(self, x, c): + half_weibull_min_sf = 0.5 * stats.weibull_min._sf(np.abs(x), c) + return np.where(x > 0, half_weibull_min_sf, 1 - half_weibull_min_sf) + + def _isf(self, q, c): + double_q = 2. * np.where(q <= 0.5, q, 1. - q) + weibull_min_isf = stats.weibull_min._isf(double_q, c) + return np.where(q > 0.5, -weibull_min_isf, weibull_min_isf) + + def _munp(self, n, c): + return (1 - (n % 2)) * sc.gamma(1.0 + 1.0 * n / c) + + # since we know that all odd moments are zeros, return them at once. + # returning Nones from _stats makes the public stats call _munp + # so overall we're saving one or two gamma function evaluations here. + def _stats(self, c): + return 0, None, 0, None + + def _entropy(self, c): + h = stats.weibull_min._entropy(c) - np.log(0.5) + return h + + +dweibull = dweibull_gen(name='dweibull') + + +class expon_gen(rv_continuous): + r"""An exponential continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `expon` is: + + .. math:: + + f(x) = \exp(-x) + + for :math:`x \ge 0`. + + %(after_notes)s + + A common parameterization for `expon` is in terms of the rate parameter + ``lambda``, such that ``pdf = lambda * exp(-lambda * x)``. This + parameterization corresponds to using ``scale = 1 / lambda``. + + The exponential distribution is a special case of the gamma + distributions, with gamma shape parameter ``a = 1``. + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.standard_exponential(size) + + def _pdf(self, x): + # expon.pdf(x) = exp(-x) + return np.exp(-x) + + def _logpdf(self, x): + return -x + + def _cdf(self, x): + return -sc.expm1(-x) + + def _ppf(self, q): + return -sc.log1p(-q) + + def _sf(self, x): + return np.exp(-x) + + def _logsf(self, x): + return -x + + def _isf(self, q): + return -np.log(q) + + def _stats(self): + return 1.0, 1.0, 2.0, 6.0 + + def _entropy(self): + return 1.0 + + @_call_super_mom + @replace_notes_in_docstring(rv_continuous, notes="""\ + When `method='MLE'`, + this function uses explicit formulas for the maximum likelihood + estimation of the exponential distribution parameters, so the + `optimizer`, `loc` and `scale` keyword arguments are + ignored.\n\n""") + def fit(self, data, *args, **kwds): + if len(args) > 0: + raise TypeError("Too many arguments.") + + floc = kwds.pop('floc', None) + fscale = kwds.pop('fscale', None) + + _remove_optimizer_parameters(kwds) + + if floc is not None and fscale is not None: + # This check is for consistency with `rv_continuous.fit`. + raise ValueError("All parameters fixed. There is nothing to " + "optimize.") + + data = np.asarray(data) + + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + data_min = data.min() + + if floc is None: + # ML estimate of the location is the minimum of the data. + loc = data_min + else: + loc = floc + if data_min < loc: + # There are values that are less than the specified loc. + raise FitDataError("expon", lower=floc, upper=np.inf) + + if fscale is None: + # ML estimate of the scale is the shifted mean. + scale = data.mean() - loc + else: + scale = fscale + + # We expect the return values to be floating point, so ensure it + # by explicitly converting to float. + return float(loc), float(scale) + + +expon = expon_gen(a=0.0, name='expon') + + +class exponnorm_gen(rv_continuous): + r"""An exponentially modified Normal continuous random variable. + + Also known as the exponentially modified Gaussian distribution [1]_. + + %(before_notes)s + + Notes + ----- + The probability density function for `exponnorm` is: + + .. math:: + + f(x, K) = \frac{1}{2K} \exp\left(\frac{1}{2 K^2} - x / K \right) + \text{erfc}\left(-\frac{x - 1/K}{\sqrt{2}}\right) + + where :math:`x` is a real number and :math:`K > 0`. + + It can be thought of as the sum of a standard normal random variable + and an independent exponentially distributed random variable with rate + ``1/K``. + + %(after_notes)s + + An alternative parameterization of this distribution (for example, in + the Wikipedia article [1]_) involves three parameters, :math:`\mu`, + :math:`\lambda` and :math:`\sigma`. + + In the present parameterization this corresponds to having ``loc`` and + ``scale`` equal to :math:`\mu` and :math:`\sigma`, respectively, and + shape parameter :math:`K = 1/(\sigma\lambda)`. + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] Exponentially modified Gaussian distribution, Wikipedia, + https://en.wikipedia.org/wiki/Exponentially_modified_Gaussian_distribution + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("K", False, (0, np.inf), (False, False))] + + def _rvs(self, K, size=None, random_state=None): + expval = random_state.standard_exponential(size) * K + gval = random_state.standard_normal(size) + return expval + gval + + def _pdf(self, x, K): + return np.exp(self._logpdf(x, K)) + + def _logpdf(self, x, K): + invK = 1.0 / K + exparg = invK * (0.5 * invK - x) + return exparg + _norm_logcdf(x - invK) - np.log(K) + + def _cdf(self, x, K): + invK = 1.0 / K + expval = invK * (0.5 * invK - x) + logprod = expval + _norm_logcdf(x - invK) + return _norm_cdf(x) - np.exp(logprod) + + def _sf(self, x, K): + invK = 1.0 / K + expval = invK * (0.5 * invK - x) + logprod = expval + _norm_logcdf(x - invK) + return _norm_cdf(-x) + np.exp(logprod) + + def _stats(self, K): + K2 = K * K + opK2 = 1.0 + K2 + skw = 2 * K**3 * opK2**(-1.5) + krt = 6.0 * K2 * K2 * opK2**(-2) + return K, opK2, skw, krt + + +exponnorm = exponnorm_gen(name='exponnorm') + + +def _pow1pm1(x, y): + """ + Compute (1 + x)**y - 1. + + Uses expm1 and xlog1py to avoid loss of precision when + (1 + x)**y is close to 1. + + Note that the inverse of this function with respect to x is + ``_pow1pm1(x, 1/y)``. That is, if + + t = _pow1pm1(x, y) + + then + + x = _pow1pm1(t, 1/y) + """ + return np.expm1(sc.xlog1py(y, x)) + + +class exponweib_gen(rv_continuous): + r"""An exponentiated Weibull continuous random variable. + + %(before_notes)s + + See Also + -------- + weibull_min, numpy.random.Generator.weibull + + Notes + ----- + The probability density function for `exponweib` is: + + .. math:: + + f(x, a, c) = a c [1-\exp(-x^c)]^{a-1} \exp(-x^c) x^{c-1} + + and its cumulative distribution function is: + + .. math:: + + F(x, a, c) = [1-\exp(-x^c)]^a + + for :math:`x > 0`, :math:`a > 0`, :math:`c > 0`. + + `exponweib` takes :math:`a` and :math:`c` as shape parameters: + + * :math:`a` is the exponentiation parameter, + with the special case :math:`a=1` corresponding to the + (non-exponentiated) Weibull distribution `weibull_min`. + * :math:`c` is the shape parameter of the non-exponentiated Weibull law. + + %(after_notes)s + + References + ---------- + https://en.wikipedia.org/wiki/Exponentiated_Weibull_distribution + + %(example)s + + """ + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + return [ia, ic] + + def _pdf(self, x, a, c): + # exponweib.pdf(x, a, c) = + # a * c * (1-exp(-x**c))**(a-1) * exp(-x**c)*x**(c-1) + return np.exp(self._logpdf(x, a, c)) + + def _logpdf(self, x, a, c): + negxc = -x**c + exm1c = -sc.expm1(negxc) + logp = (np.log(a) + np.log(c) + sc.xlogy(a - 1.0, exm1c) + + negxc + sc.xlogy(c - 1.0, x)) + return logp + + def _cdf(self, x, a, c): + exm1c = -sc.expm1(-x**c) + return exm1c**a + + def _ppf(self, q, a, c): + return (-sc.log1p(-q**(1.0/a)))**np.asarray(1.0/c) + + def _sf(self, x, a, c): + return -_pow1pm1(-np.exp(-x**c), a) + + def _isf(self, p, a, c): + return (-np.log(-_pow1pm1(-p, 1/a)))**(1/c) + + +exponweib = exponweib_gen(a=0.0, name='exponweib') + + +class exponpow_gen(rv_continuous): + r"""An exponential power continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `exponpow` is: + + .. math:: + + f(x, b) = b x^{b-1} \exp(1 + x^b - \exp(x^b)) + + for :math:`x \ge 0`, :math:`b > 0`. Note that this is a different + distribution from the exponential power distribution that is also known + under the names "generalized normal" or "generalized Gaussian". + + `exponpow` takes ``b`` as a shape parameter for :math:`b`. + + %(after_notes)s + + References + ---------- + http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Exponentialpower.pdf + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("b", False, (0, np.inf), (False, False))] + + def _pdf(self, x, b): + # exponpow.pdf(x, b) = b * x**(b-1) * exp(1 + x**b - exp(x**b)) + return np.exp(self._logpdf(x, b)) + + def _logpdf(self, x, b): + xb = x**b + f = 1 + np.log(b) + sc.xlogy(b - 1.0, x) + xb - np.exp(xb) + return f + + def _cdf(self, x, b): + return -sc.expm1(-sc.expm1(x**b)) + + def _sf(self, x, b): + return np.exp(-sc.expm1(x**b)) + + def _isf(self, x, b): + return (sc.log1p(-np.log(x)))**(1./b) + + def _ppf(self, q, b): + return pow(sc.log1p(-sc.log1p(-q)), 1.0/b) + + +exponpow = exponpow_gen(a=0.0, name='exponpow') + + +class fatiguelife_gen(rv_continuous): + r"""A fatigue-life (Birnbaum-Saunders) continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `fatiguelife` is: + + .. math:: + + f(x, c) = \frac{x+1}{2c\sqrt{2\pi x^3}} \exp(-\frac{(x-1)^2}{2x c^2}) + + for :math:`x >= 0` and :math:`c > 0`. + + `fatiguelife` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + References + ---------- + .. [1] "Birnbaum-Saunders distribution", + https://en.wikipedia.org/wiki/Birnbaum-Saunders_distribution + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _rvs(self, c, size=None, random_state=None): + z = random_state.standard_normal(size) + x = 0.5*c*z + x2 = x*x + t = 1.0 + 2*x2 + 2*x*np.sqrt(1 + x2) + return t + + def _pdf(self, x, c): + # fatiguelife.pdf(x, c) = + # (x+1) / (2*c*sqrt(2*pi*x**3)) * exp(-(x-1)**2/(2*x*c**2)) + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + return (np.log(x+1) - (x-1)**2 / (2.0*x*c**2) - np.log(2*c) - + 0.5*(np.log(2*np.pi) + 3*np.log(x))) + + def _cdf(self, x, c): + return _norm_cdf(1.0 / c * (np.sqrt(x) - 1.0/np.sqrt(x))) + + def _ppf(self, q, c): + tmp = c * _norm_ppf(q) + return 0.25 * (tmp + np.sqrt(tmp**2 + 4))**2 + + def _sf(self, x, c): + return _norm_sf(1.0 / c * (np.sqrt(x) - 1.0/np.sqrt(x))) + + def _isf(self, q, c): + tmp = -c * _norm_ppf(q) + return 0.25 * (tmp + np.sqrt(tmp**2 + 4))**2 + + def _stats(self, c): + # NB: the formula for kurtosis in wikipedia seems to have an error: + # it's 40, not 41. At least it disagrees with the one from Wolfram + # Alpha. And the latter one, below, passes the tests, while the wiki + # one doesn't So far I didn't have the guts to actually check the + # coefficients from the expressions for the raw moments. + c2 = c*c + mu = c2 / 2.0 + 1.0 + den = 5.0 * c2 + 4.0 + mu2 = c2*den / 4.0 + g1 = 4 * c * (11*c2 + 6.0) / np.power(den, 1.5) + g2 = 6 * c2 * (93*c2 + 40.0) / den**2.0 + return mu, mu2, g1, g2 + + +fatiguelife = fatiguelife_gen(a=0.0, name='fatiguelife') + + +class foldcauchy_gen(rv_continuous): + r"""A folded Cauchy continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `foldcauchy` is: + + .. math:: + + f(x, c) = \frac{1}{\pi (1+(x-c)^2)} + \frac{1}{\pi (1+(x+c)^2)} + + for :math:`x \ge 0` and :math:`c \ge 0`. + + `foldcauchy` takes ``c`` as a shape parameter for :math:`c`. + + %(example)s + + """ + def _argcheck(self, c): + return c >= 0 + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (True, False))] + + def _rvs(self, c, size=None, random_state=None): + return abs(cauchy.rvs(loc=c, size=size, + random_state=random_state)) + + def _pdf(self, x, c): + # foldcauchy.pdf(x, c) = 1/(pi*(1+(x-c)**2)) + 1/(pi*(1+(x+c)**2)) + return 1.0/np.pi*(1.0/(1+(x-c)**2) + 1.0/(1+(x+c)**2)) + + def _cdf(self, x, c): + return 1.0/np.pi*(np.arctan(x-c) + np.arctan(x+c)) + + def _sf(self, x, c): + # 1 - CDF(x, c) = 1 - (atan(x - c) + atan(x + c))/pi + # = ((pi/2 - atan(x - c)) + (pi/2 - atan(x + c)))/pi + # = (acot(x - c) + acot(x + c))/pi + # = (atan2(1, x - c) + atan2(1, x + c))/pi + return (np.arctan2(1, x - c) + np.arctan2(1, x + c))/np.pi + + def _stats(self, c): + return np.inf, np.inf, np.nan, np.nan + + +foldcauchy = foldcauchy_gen(a=0.0, name='foldcauchy') + + +class f_gen(rv_continuous): + r"""An F continuous random variable. + + For the noncentral F distribution, see `ncf`. + + %(before_notes)s + + See Also + -------- + ncf + + Notes + ----- + The F distribution with :math:`df_1 > 0` and :math:`df_2 > 0` degrees of freedom is + the distribution of the ratio of two independent chi-squared distributions with + :math:`df_1` and :math:`df_2` degrees of freedom, after rescaling by + :math:`df_2 / df_1`. + + The probability density function for `f` is: + + .. math:: + + f(x, df_1, df_2) = \frac{df_2^{df_2/2} df_1^{df_1/2} x^{df_1 / 2-1}} + {(df_2+df_1 x)^{(df_1+df_2)/2} + B(df_1/2, df_2/2)} + + for :math:`x > 0`. + + `f` accepts shape parameters ``dfn`` and ``dfd`` for :math:`df_1`, the degrees of + freedom of the chi-squared distribution in the numerator, and :math:`df_2`, the + degrees of freedom of the chi-squared distribution in the denominator, respectively. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + idfn = _ShapeInfo("dfn", False, (0, np.inf), (False, False)) + idfd = _ShapeInfo("dfd", False, (0, np.inf), (False, False)) + return [idfn, idfd] + + def _rvs(self, dfn, dfd, size=None, random_state=None): + return random_state.f(dfn, dfd, size) + + def _pdf(self, x, dfn, dfd): + # df2**(df2/2) * df1**(df1/2) * x**(df1/2-1) + # F.pdf(x, df1, df2) = -------------------------------------------- + # (df2+df1*x)**((df1+df2)/2) * B(df1/2, df2/2) + return np.exp(self._logpdf(x, dfn, dfd)) + + def _logpdf(self, x, dfn, dfd): + n = 1.0 * dfn + m = 1.0 * dfd + lPx = (m/2 * np.log(m) + n/2 * np.log(n) + sc.xlogy(n/2 - 1, x) + - (((n+m)/2) * np.log(m + n*x) + sc.betaln(n/2, m/2))) + return lPx + + def _cdf(self, x, dfn, dfd): + return sc.fdtr(dfn, dfd, x) + + def _sf(self, x, dfn, dfd): + return sc.fdtrc(dfn, dfd, x) + + def _ppf(self, q, dfn, dfd): + return sc.fdtri(dfn, dfd, q) + + def _stats(self, dfn, dfd): + v1, v2 = 1. * dfn, 1. * dfd + v2_2, v2_4, v2_6, v2_8 = v2 - 2., v2 - 4., v2 - 6., v2 - 8. + + mu = _lazywhere( + v2 > 2, (v2, v2_2), + lambda v2, v2_2: v2 / v2_2, + np.inf) + + mu2 = _lazywhere( + v2 > 4, (v1, v2, v2_2, v2_4), + lambda v1, v2, v2_2, v2_4: + 2 * v2 * v2 * (v1 + v2_2) / (v1 * v2_2**2 * v2_4), + np.inf) + + g1 = _lazywhere( + v2 > 6, (v1, v2_2, v2_4, v2_6), + lambda v1, v2_2, v2_4, v2_6: + (2 * v1 + v2_2) / v2_6 * np.sqrt(v2_4 / (v1 * (v1 + v2_2))), + np.nan) + g1 *= np.sqrt(8.) + + g2 = _lazywhere( + v2 > 8, (g1, v2_6, v2_8), + lambda g1, v2_6, v2_8: (8 + g1 * g1 * v2_6) / v2_8, + np.nan) + g2 *= 3. / 2. + + return mu, mu2, g1, g2 + + def _entropy(self, dfn, dfd): + # the formula found in literature is incorrect. This one yields the + # same result as numerical integration using the generic entropy + # definition. This is also tested in tests/test_conntinous_basic + half_dfn = 0.5 * dfn + half_dfd = 0.5 * dfd + half_sum = 0.5 * (dfn + dfd) + + return (np.log(dfd) - np.log(dfn) + sc.betaln(half_dfn, half_dfd) + + (1 - half_dfn) * sc.psi(half_dfn) - (1 + half_dfd) * + sc.psi(half_dfd) + half_sum * sc.psi(half_sum)) + + +f = f_gen(a=0.0, name='f') + + +## Folded Normal +## abs(Z) where (Z is normal with mu=L and std=S so that c=abs(L)/S) +## +## note: regress docs have scale parameter correct, but first parameter +## he gives is a shape parameter A = c * scale + +## Half-normal is folded normal with shape-parameter c=0. + +class foldnorm_gen(rv_continuous): + r"""A folded normal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `foldnorm` is: + + .. math:: + + f(x, c) = \sqrt{2/\pi} cosh(c x) \exp(-\frac{x^2+c^2}{2}) + + for :math:`x \ge 0` and :math:`c \ge 0`. + + `foldnorm` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, c): + return c >= 0 + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (True, False))] + + def _rvs(self, c, size=None, random_state=None): + return abs(random_state.standard_normal(size) + c) + + def _pdf(self, x, c): + # foldnormal.pdf(x, c) = sqrt(2/pi) * cosh(c*x) * exp(-(x**2+c**2)/2) + return _norm_pdf(x + c) + _norm_pdf(x-c) + + def _cdf(self, x, c): + sqrt_two = np.sqrt(2) + return 0.5 * (sc.erf((x - c)/sqrt_two) + sc.erf((x + c)/sqrt_two)) + + def _sf(self, x, c): + return _norm_sf(x - c) + _norm_sf(x + c) + + def _stats(self, c): + # Regina C. Elandt, Technometrics 3, 551 (1961) + # https://www.jstor.org/stable/1266561 + # + c2 = c*c + expfac = np.exp(-0.5*c2) / np.sqrt(2.*np.pi) + + mu = 2.*expfac + c * sc.erf(c/np.sqrt(2)) + mu2 = c2 + 1 - mu*mu + + g1 = 2. * (mu*mu*mu - c2*mu - expfac) + g1 /= np.power(mu2, 1.5) + + g2 = c2 * (c2 + 6.) + 3 + 8.*expfac*mu + g2 += (2. * (c2 - 3.) - 3. * mu**2) * mu**2 + g2 = g2 / mu2**2.0 - 3. + + return mu, mu2, g1, g2 + + +foldnorm = foldnorm_gen(a=0.0, name='foldnorm') + + +class weibull_min_gen(rv_continuous): + r"""Weibull minimum continuous random variable. + + The Weibull Minimum Extreme Value distribution, from extreme value theory + (Fisher-Gnedenko theorem), is also often simply called the Weibull + distribution. It arises as the limiting distribution of the rescaled + minimum of iid random variables. + + %(before_notes)s + + See Also + -------- + weibull_max, numpy.random.Generator.weibull, exponweib + + Notes + ----- + The probability density function for `weibull_min` is: + + .. math:: + + f(x, c) = c x^{c-1} \exp(-x^c) + + for :math:`x > 0`, :math:`c > 0`. + + `weibull_min` takes ``c`` as a shape parameter for :math:`c`. + (named :math:`k` in Wikipedia article and :math:`a` in + ``numpy.random.weibull``). Special shape values are :math:`c=1` and + :math:`c=2` where Weibull distribution reduces to the `expon` and + `rayleigh` distributions respectively. + + Suppose ``X`` is an exponentially distributed random variable with + scale ``s``. Then ``Y = X**k`` is `weibull_min` distributed with shape + ``c = 1/k`` and scale ``s**k``. + + %(after_notes)s + + References + ---------- + https://en.wikipedia.org/wiki/Weibull_distribution + + https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # weibull_min.pdf(x, c) = c * x**(c-1) * exp(-x**c) + return c*pow(x, c-1)*np.exp(-pow(x, c)) + + def _logpdf(self, x, c): + return np.log(c) + sc.xlogy(c - 1, x) - pow(x, c) + + def _cdf(self, x, c): + return -sc.expm1(-pow(x, c)) + + def _ppf(self, q, c): + return pow(-sc.log1p(-q), 1.0/c) + + def _sf(self, x, c): + return np.exp(self._logsf(x, c)) + + def _logsf(self, x, c): + return -pow(x, c) + + def _isf(self, q, c): + return (-np.log(q))**(1/c) + + def _munp(self, n, c): + return sc.gamma(1.0+n*1.0/c) + + def _entropy(self, c): + return -_EULER / c - np.log(c) + _EULER + 1 + + @extend_notes_in_docstring(rv_continuous, notes="""\ + If ``method='mm'``, parameters fixed by the user are respected, and the + remaining parameters are used to match distribution and sample moments + where possible. For example, if the user fixes the location with + ``floc``, the parameters will only match the distribution skewness and + variance to the sample skewness and variance; no attempt will be made + to match the means or minimize a norm of the errors. + \n\n""") + def fit(self, data, *args, **kwds): + + if isinstance(data, CensoredData): + if data.num_censored() == 0: + data = data._uncensor() + else: + return super().fit(data, *args, **kwds) + + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + # this extracts fixed shape, location, and scale however they + # are specified, and also leaves them in `kwds` + data, fc, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + method = kwds.get("method", "mle").lower() + + # See https://en.wikipedia.org/wiki/Weibull_distribution#Moments for + # moment formulas. + def skew(c): + gamma1 = sc.gamma(1+1/c) + gamma2 = sc.gamma(1+2/c) + gamma3 = sc.gamma(1+3/c) + num = 2 * gamma1**3 - 3*gamma1*gamma2 + gamma3 + den = (gamma2 - gamma1**2)**(3/2) + return num/den + + # For c in [1e2, 3e4], population skewness appears to approach + # asymptote near -1.139, but past c > 3e4, skewness begins to vary + # wildly, and MoM won't provide a good guess. Get out early. + s = stats.skew(data) + max_c = 1e4 + s_min = skew(max_c) + if s < s_min and method != "mm" and fc is None and not args: + return super().fit(data, *args, **kwds) + + # If method is method of moments, we don't need the user's guesses. + # Otherwise, extract the guesses from args and kwds. + if method == "mm": + c, loc, scale = None, None, None + else: + c = args[0] if len(args) else None + loc = kwds.pop('loc', None) + scale = kwds.pop('scale', None) + + if fc is None and c is None: # not fixed and no guess: use MoM + # Solve for c that matches sample distribution skewness to sample + # skewness. + # we start having numerical issues with `weibull_min` with + # parameters outside this range - and not just in this method. + # We could probably improve the situation by doing everything + # in the log space, but that is for another time. + c = root_scalar(lambda c: skew(c) - s, bracket=[0.02, max_c], + method='bisect').root + elif fc is not None: # fixed: use it + c = fc + + if fscale is None and scale is None: + v = np.var(data) + scale = np.sqrt(v / (sc.gamma(1+2/c) - sc.gamma(1+1/c)**2)) + elif fscale is not None: + scale = fscale + + if floc is None and loc is None: + m = np.mean(data) + loc = m - scale*sc.gamma(1 + 1/c) + elif floc is not None: + loc = floc + + if method == 'mm': + return c, loc, scale + else: + # At this point, parameter "guesses" may equal the fixed parameters + # in kwds. No harm in passing them as guesses, too. + return super().fit(data, c, loc=loc, scale=scale, **kwds) + + +weibull_min = weibull_min_gen(a=0.0, name='weibull_min') + + +class truncweibull_min_gen(rv_continuous): + r"""A doubly truncated Weibull minimum continuous random variable. + + %(before_notes)s + + See Also + -------- + weibull_min, truncexpon + + Notes + ----- + The probability density function for `truncweibull_min` is: + + .. math:: + + f(x, a, b, c) = \frac{c x^{c-1} \exp(-x^c)}{\exp(-a^c) - \exp(-b^c)} + + for :math:`a < x <= b`, :math:`0 \le a < b` and :math:`c > 0`. + + `truncweibull_min` takes :math:`a`, :math:`b`, and :math:`c` as shape + parameters. + + Notice that the truncation values, :math:`a` and :math:`b`, are defined in + standardized form: + + .. math:: + + a = (u_l - loc)/scale + b = (u_r - loc)/scale + + where :math:`u_l` and :math:`u_r` are the specific left and right + truncation values, respectively. In other words, the support of the + distribution becomes :math:`(a*scale + loc) < x <= (b*scale + loc)` when + :math:`loc` and/or :math:`scale` are provided. + + %(after_notes)s + + References + ---------- + + .. [1] Rinne, H. "The Weibull Distribution: A Handbook". CRC Press (2009). + + %(example)s + + """ + def _argcheck(self, c, a, b): + return (a >= 0.) & (b > a) & (c > 0.) + + def _shape_info(self): + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + ia = _ShapeInfo("a", False, (0, np.inf), (True, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ic, ia, ib] + + def _fitstart(self, data): + # Arbitrary, but default a=b=c=1 is not valid + return super()._fitstart(data, args=(1, 0, 1)) + + def _get_support(self, c, a, b): + return a, b + + def _pdf(self, x, c, a, b): + denum = (np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return (c * pow(x, c-1) * np.exp(-pow(x, c))) / denum + + def _logpdf(self, x, c, a, b): + logdenum = np.log(np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return np.log(c) + sc.xlogy(c - 1, x) - pow(x, c) - logdenum + + def _cdf(self, x, c, a, b): + num = (np.exp(-pow(a, c)) - np.exp(-pow(x, c))) + denum = (np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return num / denum + + def _logcdf(self, x, c, a, b): + lognum = np.log(np.exp(-pow(a, c)) - np.exp(-pow(x, c))) + logdenum = np.log(np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return lognum - logdenum + + def _sf(self, x, c, a, b): + num = (np.exp(-pow(x, c)) - np.exp(-pow(b, c))) + denum = (np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return num / denum + + def _logsf(self, x, c, a, b): + lognum = np.log(np.exp(-pow(x, c)) - np.exp(-pow(b, c))) + logdenum = np.log(np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return lognum - logdenum + + def _isf(self, q, c, a, b): + return pow( + -np.log((1 - q) * np.exp(-pow(b, c)) + q * np.exp(-pow(a, c))), 1/c + ) + + def _ppf(self, q, c, a, b): + return pow( + -np.log((1 - q) * np.exp(-pow(a, c)) + q * np.exp(-pow(b, c))), 1/c + ) + + def _munp(self, n, c, a, b): + gamma_fun = sc.gamma(n/c + 1.) * ( + sc.gammainc(n/c + 1., pow(b, c)) - sc.gammainc(n/c + 1., pow(a, c)) + ) + denum = (np.exp(-pow(a, c)) - np.exp(-pow(b, c))) + return gamma_fun / denum + + +truncweibull_min = truncweibull_min_gen(name='truncweibull_min') + + +class weibull_max_gen(rv_continuous): + r"""Weibull maximum continuous random variable. + + The Weibull Maximum Extreme Value distribution, from extreme value theory + (Fisher-Gnedenko theorem), is the limiting distribution of rescaled + maximum of iid random variables. This is the distribution of -X + if X is from the `weibull_min` function. + + %(before_notes)s + + See Also + -------- + weibull_min + + Notes + ----- + The probability density function for `weibull_max` is: + + .. math:: + + f(x, c) = c (-x)^{c-1} \exp(-(-x)^c) + + for :math:`x < 0`, :math:`c > 0`. + + `weibull_max` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + References + ---------- + https://en.wikipedia.org/wiki/Weibull_distribution + + https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # weibull_max.pdf(x, c) = c * (-x)**(c-1) * exp(-(-x)**c) + return c*pow(-x, c-1)*np.exp(-pow(-x, c)) + + def _logpdf(self, x, c): + return np.log(c) + sc.xlogy(c-1, -x) - pow(-x, c) + + def _cdf(self, x, c): + return np.exp(-pow(-x, c)) + + def _logcdf(self, x, c): + return -pow(-x, c) + + def _sf(self, x, c): + return -sc.expm1(-pow(-x, c)) + + def _ppf(self, q, c): + return -pow(-np.log(q), 1.0/c) + + def _munp(self, n, c): + val = sc.gamma(1.0+n*1.0/c) + if int(n) % 2: + sgn = -1 + else: + sgn = 1 + return sgn * val + + def _entropy(self, c): + return -_EULER / c - np.log(c) + _EULER + 1 + + +weibull_max = weibull_max_gen(b=0.0, name='weibull_max') + + +class genlogistic_gen(rv_continuous): + r"""A generalized logistic continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `genlogistic` is: + + .. math:: + + f(x, c) = c \frac{\exp(-x)} + {(1 + \exp(-x))^{c+1}} + + for real :math:`x` and :math:`c > 0`. In literature, different + generalizations of the logistic distribution can be found. This is the type 1 + generalized logistic distribution according to [1]_. It is also referred to + as the skew-logistic distribution [2]_. + + `genlogistic` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + References + ---------- + .. [1] Johnson et al. "Continuous Univariate Distributions", Volume 2, + Wiley. 1995. + .. [2] "Generalized Logistic Distribution", Wikipedia, + https://en.wikipedia.org/wiki/Generalized_logistic_distribution + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # genlogistic.pdf(x, c) = c * exp(-x) / (1 + exp(-x))**(c+1) + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + # Two mathematically equivalent expressions for log(pdf(x, c)): + # log(pdf(x, c)) = log(c) - x - (c + 1)*log(1 + exp(-x)) + # = log(c) + c*x - (c + 1)*log(1 + exp(x)) + mult = -(c - 1) * (x < 0) - 1 + absx = np.abs(x) + return np.log(c) + mult*absx - (c+1) * sc.log1p(np.exp(-absx)) + + def _cdf(self, x, c): + Cx = (1+np.exp(-x))**(-c) + return Cx + + def _logcdf(self, x, c): + return -c * np.log1p(np.exp(-x)) + + def _ppf(self, q, c): + return -np.log(sc.powm1(q, -1.0/c)) + + def _sf(self, x, c): + return -sc.expm1(self._logcdf(x, c)) + + def _isf(self, q, c): + return self._ppf(1 - q, c) + + def _stats(self, c): + mu = _EULER + sc.psi(c) + mu2 = np.pi*np.pi/6.0 + sc.zeta(2, c) + g1 = -2*sc.zeta(3, c) + 2*_ZETA3 + g1 /= np.power(mu2, 1.5) + g2 = np.pi**4/15.0 + 6*sc.zeta(4, c) + g2 /= mu2**2.0 + return mu, mu2, g1, g2 + + def _entropy(self, c): + return _lazywhere(c < 8e6, (c, ), + lambda c: -np.log(c) + sc.psi(c + 1) + _EULER + 1, + # asymptotic expansion: psi(c) ~ log(c) - 1/(2 * c) + # a = -log(c) + psi(c + 1) + # = -log(c) + psi(c) + 1/c + # ~ -log(c) + log(c) - 1/(2 * c) + 1/c + # = 1/(2 * c) + f2=lambda c: 1/(2 * c) + _EULER + 1) + + +genlogistic = genlogistic_gen(name='genlogistic') + + +class genpareto_gen(rv_continuous): + r"""A generalized Pareto continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `genpareto` is: + + .. math:: + + f(x, c) = (1 + c x)^{-1 - 1/c} + + defined for :math:`x \ge 0` if :math:`c \ge 0`, and for + :math:`0 \le x \le -1/c` if :math:`c < 0`. + + `genpareto` takes ``c`` as a shape parameter for :math:`c`. + + For :math:`c=0`, `genpareto` reduces to the exponential + distribution, `expon`: + + .. math:: + + f(x, 0) = \exp(-x) + + For :math:`c=-1`, `genpareto` is uniform on ``[0, 1]``: + + .. math:: + + f(x, -1) = 1 + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, c): + return np.isfinite(c) + + def _shape_info(self): + return [_ShapeInfo("c", False, (-np.inf, np.inf), (False, False))] + + def _get_support(self, c): + c = np.asarray(c) + b = _lazywhere(c < 0, (c,), + lambda c: -1. / c, + np.inf) + a = np.where(c >= 0, self.a, self.a) + return a, b + + def _pdf(self, x, c): + # genpareto.pdf(x, c) = (1 + c * x)**(-1 - 1/c) + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + return _lazywhere((x == x) & (c != 0), (x, c), + lambda x, c: -sc.xlog1py(c + 1., c*x) / c, + -x) + + def _cdf(self, x, c): + return -sc.inv_boxcox1p(-x, -c) + + def _sf(self, x, c): + return sc.inv_boxcox(-x, -c) + + def _logsf(self, x, c): + return _lazywhere((x == x) & (c != 0), (x, c), + lambda x, c: -sc.log1p(c*x) / c, + -x) + + def _ppf(self, q, c): + return -sc.boxcox1p(-q, -c) + + def _isf(self, q, c): + return -sc.boxcox(q, -c) + + def _stats(self, c, moments='mv'): + if 'm' not in moments: + m = None + else: + m = _lazywhere(c < 1, (c,), + lambda xi: 1/(1 - xi), + np.inf) + if 'v' not in moments: + v = None + else: + v = _lazywhere(c < 1/2, (c,), + lambda xi: 1 / (1 - xi)**2 / (1 - 2*xi), + np.nan) + if 's' not in moments: + s = None + else: + s = _lazywhere(c < 1/3, (c,), + lambda xi: (2 * (1 + xi) * np.sqrt(1 - 2*xi) / + (1 - 3*xi)), + np.nan) + if 'k' not in moments: + k = None + else: + k = _lazywhere(c < 1/4, (c,), + lambda xi: (3 * (1 - 2*xi) * (2*xi**2 + xi + 3) / + (1 - 3*xi) / (1 - 4*xi) - 3), + np.nan) + return m, v, s, k + + def _munp(self, n, c): + def __munp(n, c): + val = 0.0 + k = np.arange(0, n + 1) + for ki, cnk in zip(k, sc.comb(n, k)): + val = val + cnk * (-1) ** ki / (1.0 - c * ki) + return np.where(c * n < 1, val * (-1.0 / c) ** n, np.inf) + return _lazywhere(c != 0, (c,), + lambda c: __munp(n, c), + sc.gamma(n + 1)) + + def _entropy(self, c): + return 1. + c + + +genpareto = genpareto_gen(a=0.0, name='genpareto') + + +class genexpon_gen(rv_continuous): + r"""A generalized exponential continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `genexpon` is: + + .. math:: + + f(x, a, b, c) = (a + b (1 - \exp(-c x))) + \exp(-a x - b x + \frac{b}{c} (1-\exp(-c x))) + + for :math:`x \ge 0`, :math:`a, b, c > 0`. + + `genexpon` takes :math:`a`, :math:`b` and :math:`c` as shape parameters. + + %(after_notes)s + + References + ---------- + H.K. Ryu, "An Extension of Marshall and Olkin's Bivariate Exponential + Distribution", Journal of the American Statistical Association, 1993. + + N. Balakrishnan, Asit P. Basu (editors), *The Exponential Distribution: + Theory, Methods and Applications*, Gordon and Breach, 1995. + ISBN 10: 2884491929 + + %(example)s + + """ + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + return [ia, ib, ic] + + def _pdf(self, x, a, b, c): + # genexpon.pdf(x, a, b, c) = (a + b * (1 - exp(-c*x))) * \ + # exp(-a*x - b*x + b/c * (1-exp(-c*x))) + return (a + b*(-sc.expm1(-c*x)))*np.exp((-a-b)*x + + b*(-sc.expm1(-c*x))/c) + + def _logpdf(self, x, a, b, c): + return np.log(a+b*(-sc.expm1(-c*x))) + (-a-b)*x+b*(-sc.expm1(-c*x))/c + + def _cdf(self, x, a, b, c): + return -sc.expm1((-a-b)*x + b*(-sc.expm1(-c*x))/c) + + def _ppf(self, p, a, b, c): + s = a + b + t = (b - c*np.log1p(-p))/s + return (t + sc.lambertw(-b/s * np.exp(-t)).real)/c + + def _sf(self, x, a, b, c): + return np.exp((-a-b)*x + b*(-sc.expm1(-c*x))/c) + + def _isf(self, p, a, b, c): + s = a + b + t = (b - c*np.log(p))/s + return (t + sc.lambertw(-b/s * np.exp(-t)).real)/c + + +genexpon = genexpon_gen(a=0.0, name='genexpon') + + +class genextreme_gen(rv_continuous): + r"""A generalized extreme value continuous random variable. + + %(before_notes)s + + See Also + -------- + gumbel_r + + Notes + ----- + For :math:`c=0`, `genextreme` is equal to `gumbel_r` with + probability density function + + .. math:: + + f(x) = \exp(-\exp(-x)) \exp(-x), + + where :math:`-\infty < x < \infty`. + + For :math:`c \ne 0`, the probability density function for `genextreme` is: + + .. math:: + + f(x, c) = \exp(-(1-c x)^{1/c}) (1-c x)^{1/c-1}, + + where :math:`-\infty < x \le 1/c` if :math:`c > 0` and + :math:`1/c \le x < \infty` if :math:`c < 0`. + + Note that several sources and software packages use the opposite + convention for the sign of the shape parameter :math:`c`. + + `genextreme` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, c): + return np.isfinite(c) + + def _shape_info(self): + return [_ShapeInfo("c", False, (-np.inf, np.inf), (False, False))] + + def _get_support(self, c): + _b = np.where(c > 0, 1.0 / np.maximum(c, _XMIN), np.inf) + _a = np.where(c < 0, 1.0 / np.minimum(c, -_XMIN), -np.inf) + return _a, _b + + def _loglogcdf(self, x, c): + # Returns log(-log(cdf(x, c))) + return _lazywhere((x == x) & (c != 0), (x, c), + lambda x, c: sc.log1p(-c*x)/c, -x) + + def _pdf(self, x, c): + # genextreme.pdf(x, c) = + # exp(-exp(-x))*exp(-x), for c==0 + # exp(-(1-c*x)**(1/c))*(1-c*x)**(1/c-1), for x \le 1/c, c > 0 + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + cx = _lazywhere((x == x) & (c != 0), (x, c), lambda x, c: c*x, 0.0) + logex2 = sc.log1p(-cx) + logpex2 = self._loglogcdf(x, c) + pex2 = np.exp(logpex2) + # Handle special cases + np.putmask(logpex2, (c == 0) & (x == -np.inf), 0.0) + logpdf = _lazywhere(~((cx == 1) | (cx == -np.inf)), + (pex2, logpex2, logex2), + lambda pex2, lpex2, lex2: -pex2 + lpex2 - lex2, + fillvalue=-np.inf) + np.putmask(logpdf, (c == 1) & (x == 1), 0.0) + return logpdf + + def _logcdf(self, x, c): + return -np.exp(self._loglogcdf(x, c)) + + def _cdf(self, x, c): + return np.exp(self._logcdf(x, c)) + + def _sf(self, x, c): + return -sc.expm1(self._logcdf(x, c)) + + def _ppf(self, q, c): + x = -np.log(-np.log(q)) + return _lazywhere((x == x) & (c != 0), (x, c), + lambda x, c: -sc.expm1(-c * x) / c, x) + + def _isf(self, q, c): + x = -np.log(-sc.log1p(-q)) + return _lazywhere((x == x) & (c != 0), (x, c), + lambda x, c: -sc.expm1(-c * x) / c, x) + + def _stats(self, c): + def g(n): + return sc.gamma(n * c + 1) + g1 = g(1) + g2 = g(2) + g3 = g(3) + g4 = g(4) + g2mg12 = np.where(abs(c) < 1e-7, (c*np.pi)**2.0/6.0, g2-g1**2.0) + gam2k = np.where(abs(c) < 1e-7, np.pi**2.0/6.0, + sc.expm1(sc.gammaln(2.0*c+1.0)-2*sc.gammaln(c + 1.0))/c**2.0) + eps = 1e-14 + gamk = np.where(abs(c) < eps, -_EULER, sc.expm1(sc.gammaln(c + 1))/c) + + m = np.where(c < -1.0, np.nan, -gamk) + v = np.where(c < -0.5, np.nan, g1**2.0*gam2k) + + # skewness + sk1 = _lazywhere(c >= -1./3, + (c, g1, g2, g3, g2mg12), + lambda c, g1, g2, g3, g2mg12: + np.sign(c)*(-g3 + (g2 + 2*g2mg12)*g1)/g2mg12**1.5, + fillvalue=np.nan) + sk = np.where(abs(c) <= eps**0.29, 12*np.sqrt(6)*_ZETA3/np.pi**3, sk1) + + # kurtosis + ku1 = _lazywhere(c >= -1./4, + (g1, g2, g3, g4, g2mg12), + lambda g1, g2, g3, g4, g2mg12: + (g4 + (-4*g3 + 3*(g2 + g2mg12)*g1)*g1)/g2mg12**2, + fillvalue=np.nan) + ku = np.where(abs(c) <= (eps)**0.23, 12.0/5.0, ku1-3.0) + return m, v, sk, ku + + def _fitstart(self, data): + if isinstance(data, CensoredData): + data = data._uncensor() + # This is better than the default shape of (1,). + g = _skew(data) + if g < 0: + a = 0.5 + else: + a = -0.5 + return super()._fitstart(data, args=(a,)) + + def _munp(self, n, c): + k = np.arange(0, n+1) + vals = 1.0/c**n * np.sum( + sc.comb(n, k) * (-1)**k * sc.gamma(c*k + 1), + axis=0) + return np.where(c*n > -1, vals, np.inf) + + def _entropy(self, c): + return _EULER*(1 - c) + 1 + + +genextreme = genextreme_gen(name='genextreme') + + +def _digammainv(y): + """Inverse of the digamma function (real positive arguments only). + + This function is used in the `fit` method of `gamma_gen`. + The function uses either optimize.fsolve or optimize.newton + to solve `sc.digamma(x) - y = 0`. There is probably room for + improvement, but currently it works over a wide range of y: + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> y = 64*rng.standard_normal(1000000) + >>> y.min(), y.max() + (-311.43592651416662, 351.77388222276869) + >>> x = [_digammainv(t) for t in y] + >>> np.abs(sc.digamma(x) - y).max() + 1.1368683772161603e-13 + + """ + _em = 0.5772156649015328606065120 + + def func(x): + return sc.digamma(x) - y + + if y > -0.125: + x0 = np.exp(y) + 0.5 + if y < 10: + # Some experimentation shows that newton reliably converges + # must faster than fsolve in this y range. For larger y, + # newton sometimes fails to converge. + value = optimize.newton(func, x0, tol=1e-10) + return value + elif y > -3: + x0 = np.exp(y/2.332) + 0.08661 + else: + x0 = 1.0 / (-y - _em) + + value, info, ier, mesg = optimize.fsolve(func, x0, xtol=1e-11, + full_output=True) + if ier != 1: + raise RuntimeError("_digammainv: fsolve failed, y = %r" % y) + + return value[0] + + +## Gamma (Use MATLAB and MATHEMATICA (b=theta=scale, a=alpha=shape) definition) + +## gamma(a, loc, scale) with a an integer is the Erlang distribution +## gamma(1, loc, scale) is the Exponential distribution +## gamma(df/2, 0, 2) is the chi2 distribution with df degrees of freedom. + +class gamma_gen(rv_continuous): + r"""A gamma continuous random variable. + + %(before_notes)s + + See Also + -------- + erlang, expon + + Notes + ----- + The probability density function for `gamma` is: + + .. math:: + + f(x, a) = \frac{x^{a-1} e^{-x}}{\Gamma(a)} + + for :math:`x \ge 0`, :math:`a > 0`. Here :math:`\Gamma(a)` refers to the + gamma function. + + `gamma` takes ``a`` as a shape parameter for :math:`a`. + + When :math:`a` is an integer, `gamma` reduces to the Erlang + distribution, and when :math:`a=1` to the exponential distribution. + + Gamma distributions are sometimes parameterized with two variables, + with a probability density function of: + + .. math:: + + f(x, \alpha, \beta) = + \frac{\beta^\alpha x^{\alpha - 1} e^{-\beta x }}{\Gamma(\alpha)} + + Note that this parameterization is equivalent to the above, with + ``scale = 1 / beta``. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _rvs(self, a, size=None, random_state=None): + return random_state.standard_gamma(a, size) + + def _pdf(self, x, a): + # gamma.pdf(x, a) = x**(a-1) * exp(-x) / gamma(a) + return np.exp(self._logpdf(x, a)) + + def _logpdf(self, x, a): + return sc.xlogy(a-1.0, x) - x - sc.gammaln(a) + + def _cdf(self, x, a): + return sc.gammainc(a, x) + + def _sf(self, x, a): + return sc.gammaincc(a, x) + + def _ppf(self, q, a): + return sc.gammaincinv(a, q) + + def _isf(self, q, a): + return sc.gammainccinv(a, q) + + def _stats(self, a): + return a, a, 2.0/np.sqrt(a), 6.0/a + + def _entropy(self, a): + + def regular_formula(a): + return sc.psi(a) * (1-a) + a + sc.gammaln(a) + + def asymptotic_formula(a): + # plug in above formula the expansions: + # psi(a) ~ ln(a) - 1/2a - 1/12a^2 + 1/120a^4 + # gammaln(a) ~ a * ln(a) - a - 1/2 * ln(a) + 1/2 ln(2 * pi) + + # 1/12a - 1/360a^3 + return (0.5 * (1. + np.log(2*np.pi) + np.log(a)) - 1/(3 * a) + - (a**-2.)/12 - (a**-3.)/90 + (a**-4.)/120) + + return _lazywhere(a < 250, (a, ), regular_formula, + f2=asymptotic_formula) + + def _fitstart(self, data): + # The skewness of the gamma distribution is `2 / np.sqrt(a)`. + # We invert that to estimate the shape `a` using the skewness + # of the data. The formula is regularized with 1e-8 in the + # denominator to allow for degenerate data where the skewness + # is close to 0. + if isinstance(data, CensoredData): + data = data._uncensor() + sk = _skew(data) + a = 4 / (1e-8 + sk**2) + return super()._fitstart(data, args=(a,)) + + @extend_notes_in_docstring(rv_continuous, notes="""\ + When the location is fixed by using the argument `floc` + and `method='MLE'`, this + function uses explicit formulas or solves a simpler numerical + problem than the full ML optimization problem. So in that case, + the `optimizer`, `loc` and `scale` arguments are ignored. + \n\n""") + def fit(self, data, *args, **kwds): + floc = kwds.get('floc', None) + method = kwds.get('method', 'mle') + + if (isinstance(data, CensoredData) or + floc is None and method.lower() != 'mm'): + # loc is not fixed or we're not doing standard MLE. + # Use the default fit method. + return super().fit(data, *args, **kwds) + + # We already have this value, so just pop it from kwds. + kwds.pop('floc', None) + + f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a']) + fscale = kwds.pop('fscale', None) + + _remove_optimizer_parameters(kwds) + + if f0 is not None and floc is not None and fscale is not None: + # This check is for consistency with `rv_continuous.fit`. + # Without this check, this function would just return the + # parameters that were given. + raise ValueError("All parameters fixed. There is nothing to " + "optimize.") + + # Fixed location is handled by shifting the data. + data = np.asarray(data) + + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + # Use explicit formulas for mm (gh-19884) + if method.lower() == 'mm': + m1 = np.mean(data) + m2 = np.var(data) + m3 = np.mean((data - m1) ** 3) + a, loc, scale = f0, floc, fscale + # Three unknowns + if a is None and loc is None and scale is None: + scale = m3 / (2 * m2) + # Two unknowns + if loc is None and scale is None: + scale = np.sqrt(m2 / a) + if a is None and scale is None: + scale = m2 / (m1 - loc) + if a is None and loc is None: + a = m2 / (scale ** 2) + # One unknown + if a is None: + a = (m1 - loc) / scale + if loc is None: + loc = m1 - a * scale + if scale is None: + scale = (m1 - loc) / a + return a, loc, scale + + # Special case: loc is fixed. + + # NB: data == loc is ok if a >= 1; the below check is more strict. + if np.any(data <= floc): + raise FitDataError("gamma", lower=floc, upper=np.inf) + + if floc != 0: + # Don't do the subtraction in-place, because `data` might be a + # view of the input array. + data = data - floc + xbar = data.mean() + + # Three cases to handle: + # * shape and scale both free + # * shape fixed, scale free + # * shape free, scale fixed + + if fscale is None: + # scale is free + if f0 is not None: + # shape is fixed + a = f0 + else: + # shape and scale are both free. + # The MLE for the shape parameter `a` is the solution to: + # np.log(a) - sc.digamma(a) - np.log(xbar) + + # np.log(data).mean() = 0 + s = np.log(xbar) - np.log(data).mean() + aest = (3-s + np.sqrt((s-3)**2 + 24*s)) / (12*s) + xa = aest*(1-0.4) + xb = aest*(1+0.4) + a = optimize.brentq(lambda a: np.log(a) - sc.digamma(a) - s, + xa, xb, disp=0) + + # The MLE for the scale parameter is just the data mean + # divided by the shape parameter. + scale = xbar / a + else: + # scale is fixed, shape is free + # The MLE for the shape parameter `a` is the solution to: + # sc.digamma(a) - np.log(data).mean() + np.log(fscale) = 0 + c = np.log(data).mean() - np.log(fscale) + a = _digammainv(c) + scale = fscale + + return a, floc, scale + + +gamma = gamma_gen(a=0.0, name='gamma') + + +class erlang_gen(gamma_gen): + """An Erlang continuous random variable. + + %(before_notes)s + + See Also + -------- + gamma + + Notes + ----- + The Erlang distribution is a special case of the Gamma distribution, with + the shape parameter `a` an integer. Note that this restriction is not + enforced by `erlang`. It will, however, generate a warning the first time + a non-integer value is used for the shape parameter. + + Refer to `gamma` for examples. + + """ + + def _argcheck(self, a): + allint = np.all(np.floor(a) == a) + if not allint: + # An Erlang distribution shouldn't really have a non-integer + # shape parameter, so warn the user. + message = ('The shape parameter of the erlang distribution ' + f'has been given a non-integer value {a!r}.') + warnings.warn(message, RuntimeWarning, stacklevel=3) + return a > 0 + + def _shape_info(self): + return [_ShapeInfo("a", True, (1, np.inf), (True, False))] + + def _fitstart(self, data): + # Override gamma_gen_fitstart so that an integer initial value is + # used. (Also regularize the division, to avoid issues when + # _skew(data) is 0 or close to 0.) + if isinstance(data, CensoredData): + data = data._uncensor() + a = int(4.0 / (1e-8 + _skew(data)**2)) + return super(gamma_gen, self)._fitstart(data, args=(a,)) + + # Trivial override of the fit method, so we can monkey-patch its + # docstring. + @extend_notes_in_docstring(rv_continuous, notes="""\ + The Erlang distribution is generally defined to have integer values + for the shape parameter. This is not enforced by the `erlang` class. + When fitting the distribution, it will generally return a non-integer + value for the shape parameter. By using the keyword argument + `f0=`, the fit method can be constrained to fit the data to + a specific integer shape parameter.""") + def fit(self, data, *args, **kwds): + return super().fit(data, *args, **kwds) + + +erlang = erlang_gen(a=0.0, name='erlang') + + +class gengamma_gen(rv_continuous): + r"""A generalized gamma continuous random variable. + + %(before_notes)s + + See Also + -------- + gamma, invgamma, weibull_min + + Notes + ----- + The probability density function for `gengamma` is ([1]_): + + .. math:: + + f(x, a, c) = \frac{|c| x^{c a-1} \exp(-x^c)}{\Gamma(a)} + + for :math:`x \ge 0`, :math:`a > 0`, and :math:`c \ne 0`. + :math:`\Gamma` is the gamma function (`scipy.special.gamma`). + + `gengamma` takes :math:`a` and :math:`c` as shape parameters. + + %(after_notes)s + + References + ---------- + .. [1] E.W. Stacy, "A Generalization of the Gamma Distribution", + Annals of Mathematical Statistics, Vol 33(3), pp. 1187--1192. + + %(example)s + + """ + def _argcheck(self, a, c): + return (a > 0) & (c != 0) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ic = _ShapeInfo("c", False, (-np.inf, np.inf), (False, False)) + return [ia, ic] + + def _pdf(self, x, a, c): + return np.exp(self._logpdf(x, a, c)) + + def _logpdf(self, x, a, c): + return _lazywhere((x != 0) | (c > 0), (x, c), + lambda x, c: (np.log(abs(c)) + sc.xlogy(c*a - 1, x) + - x**c - sc.gammaln(a)), + fillvalue=-np.inf) + + def _cdf(self, x, a, c): + xc = x**c + val1 = sc.gammainc(a, xc) + val2 = sc.gammaincc(a, xc) + return np.where(c > 0, val1, val2) + + def _rvs(self, a, c, size=None, random_state=None): + r = random_state.standard_gamma(a, size=size) + return r**(1./c) + + def _sf(self, x, a, c): + xc = x**c + val1 = sc.gammainc(a, xc) + val2 = sc.gammaincc(a, xc) + return np.where(c > 0, val2, val1) + + def _ppf(self, q, a, c): + val1 = sc.gammaincinv(a, q) + val2 = sc.gammainccinv(a, q) + return np.where(c > 0, val1, val2)**(1.0/c) + + def _isf(self, q, a, c): + val1 = sc.gammaincinv(a, q) + val2 = sc.gammainccinv(a, q) + return np.where(c > 0, val2, val1)**(1.0/c) + + def _munp(self, n, a, c): + # Pochhammer symbol: sc.pocha,n) = gamma(a+n)/gamma(a) + return sc.poch(a, n*1.0/c) + + def _entropy(self, a, c): + def regular(a, c): + val = sc.psi(a) + A = a * (1 - val) + val / c + B = sc.gammaln(a) - np.log(abs(c)) + h = A + B + return h + + def asymptotic(a, c): + # using asymptotic expansions for gammaln and psi (see gh-18093) + return (norm._entropy() - np.log(a)/2 + - np.log(np.abs(c)) + (a**-1.)/6 - (a**-3.)/90 + + (np.log(a) - (a**-1.)/2 - (a**-2.)/12 + (a**-4.)/120)/c) + + h = _lazywhere(a >= 2e2, (a, c), f=asymptotic, f2=regular) + return h + + +gengamma = gengamma_gen(a=0.0, name='gengamma') + + +class genhalflogistic_gen(rv_continuous): + r"""A generalized half-logistic continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `genhalflogistic` is: + + .. math:: + + f(x, c) = \frac{2 (1 - c x)^{1/(c-1)}}{[1 + (1 - c x)^{1/c}]^2} + + for :math:`0 \le x \le 1/c`, and :math:`c > 0`. + + `genhalflogistic` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _get_support(self, c): + return self.a, 1.0/c + + def _pdf(self, x, c): + # genhalflogistic.pdf(x, c) = + # 2 * (1-c*x)**(1/c-1) / (1+(1-c*x)**(1/c))**2 + limit = 1.0/c + tmp = np.asarray(1-c*x) + tmp0 = tmp**(limit-1) + tmp2 = tmp0*tmp + return 2*tmp0 / (1+tmp2)**2 + + def _cdf(self, x, c): + limit = 1.0/c + tmp = np.asarray(1-c*x) + tmp2 = tmp**(limit) + return (1.0-tmp2) / (1+tmp2) + + def _ppf(self, q, c): + return 1.0/c*(1-((1.0-q)/(1.0+q))**c) + + def _entropy(self, c): + return 2 - (2*c+1)*np.log(2) + + +genhalflogistic = genhalflogistic_gen(a=0.0, name='genhalflogistic') + + +class genhyperbolic_gen(rv_continuous): + r"""A generalized hyperbolic continuous random variable. + + %(before_notes)s + + See Also + -------- + t, norminvgauss, geninvgauss, laplace, cauchy + + Notes + ----- + The probability density function for `genhyperbolic` is: + + .. math:: + + f(x, p, a, b) = + \frac{(a^2 - b^2)^{p/2}} + {\sqrt{2\pi}a^{p-1/2} + K_p\Big(\sqrt{a^2 - b^2}\Big)} + e^{bx} \times \frac{K_{p - 1/2} + (a \sqrt{1 + x^2})} + {(\sqrt{1 + x^2})^{1/2 - p}} + + for :math:`x, p \in ( - \infty; \infty)`, + :math:`|b| < a` if :math:`p \ge 0`, + :math:`|b| \le a` if :math:`p < 0`. + :math:`K_{p}(.)` denotes the modified Bessel function of the second + kind and order :math:`p` (`scipy.special.kv`) + + `genhyperbolic` takes ``p`` as a tail parameter, + ``a`` as a shape parameter, + ``b`` as a skewness parameter. + + %(after_notes)s + + The original parameterization of the Generalized Hyperbolic Distribution + is found in [1]_ as follows + + .. math:: + + f(x, \lambda, \alpha, \beta, \delta, \mu) = + \frac{(\gamma/\delta)^\lambda}{\sqrt{2\pi}K_\lambda(\delta \gamma)} + e^{\beta (x - \mu)} \times \frac{K_{\lambda - 1/2} + (\alpha \sqrt{\delta^2 + (x - \mu)^2})} + {(\sqrt{\delta^2 + (x - \mu)^2} / \alpha)^{1/2 - \lambda}} + + for :math:`x \in ( - \infty; \infty)`, + :math:`\gamma := \sqrt{\alpha^2 - \beta^2}`, + :math:`\lambda, \mu \in ( - \infty; \infty)`, + :math:`\delta \ge 0, |\beta| < \alpha` if :math:`\lambda \ge 0`, + :math:`\delta > 0, |\beta| \le \alpha` if :math:`\lambda < 0`. + + The location-scale-based parameterization implemented in + SciPy is based on [2]_, where :math:`a = \alpha\delta`, + :math:`b = \beta\delta`, :math:`p = \lambda`, + :math:`scale=\delta` and :math:`loc=\mu` + + Moments are implemented based on [3]_ and [4]_. + + For the distributions that are a special case such as Student's t, + it is not recommended to rely on the implementation of genhyperbolic. + To avoid potential numerical problems and for performance reasons, + the methods of the specific distributions should be used. + + References + ---------- + .. [1] O. Barndorff-Nielsen, "Hyperbolic Distributions and Distributions + on Hyperbolae", Scandinavian Journal of Statistics, Vol. 5(3), + pp. 151-157, 1978. https://www.jstor.org/stable/4615705 + + .. [2] Eberlein E., Prause K. (2002) The Generalized Hyperbolic Model: + Financial Derivatives and Risk Measures. In: Geman H., Madan D., + Pliska S.R., Vorst T. (eds) Mathematical Finance - Bachelier + Congress 2000. Springer Finance. Springer, Berlin, Heidelberg. + :doi:`10.1007/978-3-662-12429-1_12` + + .. [3] Scott, David J, Würtz, Diethelm, Dong, Christine and Tran, + Thanh Tam, (2009), Moments of the generalized hyperbolic + distribution, MPRA Paper, University Library of Munich, Germany, + https://EconPapers.repec.org/RePEc:pra:mprapa:19081. + + .. [4] E. Eberlein and E. A. von Hammerstein. Generalized hyperbolic + and inverse Gaussian distributions: Limiting cases and approximation + of processes. FDM Preprint 80, April 2003. University of Freiburg. + https://freidok.uni-freiburg.de/fedora/objects/freidok:7974/datastreams/FILE1/content + + %(example)s + + """ + + def _argcheck(self, p, a, b): + return (np.logical_and(np.abs(b) < a, p >= 0) + | np.logical_and(np.abs(b) <= a, p < 0)) + + def _shape_info(self): + ip = _ShapeInfo("p", False, (-np.inf, np.inf), (False, False)) + ia = _ShapeInfo("a", False, (0, np.inf), (True, False)) + ib = _ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) + return [ip, ia, ib] + + def _fitstart(self, data): + # Arbitrary, but the default p = a = b = 1 is not valid; the + # distribution requires |b| < a if p >= 0. + return super()._fitstart(data, args=(1, 1, 0.5)) + + def _logpdf(self, x, p, a, b): + # kve instead of kv works better for large values of p + # and smaller values of sqrt(a^2 - b^2) + @np.vectorize + def _logpdf_single(x, p, a, b): + return _stats.genhyperbolic_logpdf(x, p, a, b) + + return _logpdf_single(x, p, a, b) + + def _pdf(self, x, p, a, b): + # kve instead of kv works better for large values of p + # and smaller values of sqrt(a^2 - b^2) + @np.vectorize + def _pdf_single(x, p, a, b): + return _stats.genhyperbolic_pdf(x, p, a, b) + + return _pdf_single(x, p, a, b) + + # np.vectorize isn't currently designed to be used as a decorator, + # so use a lambda instead. This allows us to decorate the function + # with `np.vectorize` and still provide the `otypes` parameter. + # The first argument to `vectorize` is `func.__get__(object)` for + # compatibility with Python 3.9. In Python 3.10, this can be + # simplified to just `func`. + @lambda func: np.vectorize(func.__get__(object), otypes=[np.float64]) + @staticmethod + def _integrate_pdf(x0, x1, p, a, b): + """ + Integrate the pdf of the genhyberbolic distribution from x0 to x1. + This is a private function used by _cdf() and _sf() only; either x0 + will be -inf or x1 will be inf. + """ + user_data = np.array([p, a, b], float).ctypes.data_as(ctypes.c_void_p) + llc = LowLevelCallable.from_cython(_stats, '_genhyperbolic_pdf', + user_data) + d = np.sqrt((a + b)*(a - b)) + mean = b/d * sc.kv(p + 1, d) / sc.kv(p, d) + epsrel = 1e-10 + epsabs = 0 + if x0 < mean < x1: + # If the interval includes the mean, integrate over the two + # intervals [x0, mean] and [mean, x1] and add. If we try to do + # the integral in one call of quad and the non-infinite endpoint + # is far in the tail, quad might return an incorrect result + # because it does not "see" the peak of the PDF. + intgrl = (integrate.quad(llc, x0, mean, + epsrel=epsrel, epsabs=epsabs)[0] + + integrate.quad(llc, mean, x1, + epsrel=epsrel, epsabs=epsabs)[0]) + else: + intgrl = integrate.quad(llc, x0, x1, + epsrel=epsrel, epsabs=epsabs)[0] + if np.isnan(intgrl): + msg = ("Infinite values encountered in scipy.special.kve. " + "Values replaced by NaN to avoid incorrect results.") + warnings.warn(msg, RuntimeWarning, stacklevel=3) + return max(0.0, min(1.0, intgrl)) + + def _cdf(self, x, p, a, b): + return self._integrate_pdf(-np.inf, x, p, a, b) + + def _sf(self, x, p, a, b): + return self._integrate_pdf(x, np.inf, p, a, b) + + def _rvs(self, p, a, b, size=None, random_state=None): + # note: X = b * V + sqrt(V) * X has a + # generalized hyperbolic distribution + # if X is standard normal and V is + # geninvgauss(p = p, b = t2, loc = loc, scale = t3) + t1 = np.float_power(a, 2) - np.float_power(b, 2) + # b in the GIG + t2 = np.float_power(t1, 0.5) + # scale in the GIG + t3 = np.float_power(t1, - 0.5) + gig = geninvgauss.rvs( + p=p, + b=t2, + scale=t3, + size=size, + random_state=random_state + ) + normst = norm.rvs(size=size, random_state=random_state) + + return b * gig + np.sqrt(gig) * normst + + def _stats(self, p, a, b): + # https://mpra.ub.uni-muenchen.de/19081/1/MPRA_paper_19081.pdf + # https://freidok.uni-freiburg.de/fedora/objects/freidok:7974/datastreams/FILE1/content + # standardized moments + p, a, b = np.broadcast_arrays(p, a, b) + t1 = np.float_power(a, 2) - np.float_power(b, 2) + t1 = np.float_power(t1, 0.5) + t2 = np.float_power(1, 2) * np.float_power(t1, - 1) + integers = np.linspace(0, 4, 5) + # make integers perpendicular to existing dimensions + integers = integers.reshape(integers.shape + (1,) * p.ndim) + b0, b1, b2, b3, b4 = sc.kv(p + integers, t1) + r1, r2, r3, r4 = (b / b0 for b in (b1, b2, b3, b4)) + + m = b * t2 * r1 + v = ( + t2 * r1 + np.float_power(b, 2) * np.float_power(t2, 2) * + (r2 - np.float_power(r1, 2)) + ) + m3e = ( + np.float_power(b, 3) * np.float_power(t2, 3) * + (r3 - 3 * b2 * b1 * np.float_power(b0, -2) + + 2 * np.float_power(r1, 3)) + + 3 * b * np.float_power(t2, 2) * + (r2 - np.float_power(r1, 2)) + ) + s = m3e * np.float_power(v, - 3 / 2) + m4e = ( + np.float_power(b, 4) * np.float_power(t2, 4) * + (r4 - 4 * b3 * b1 * np.float_power(b0, - 2) + + 6 * b2 * np.float_power(b1, 2) * np.float_power(b0, - 3) - + 3 * np.float_power(r1, 4)) + + np.float_power(b, 2) * np.float_power(t2, 3) * + (6 * r3 - 12 * b2 * b1 * np.float_power(b0, - 2) + + 6 * np.float_power(r1, 3)) + + 3 * np.float_power(t2, 2) * r2 + ) + k = m4e * np.float_power(v, -2) - 3 + + return m, v, s, k + + +genhyperbolic = genhyperbolic_gen(name='genhyperbolic') + + +class gompertz_gen(rv_continuous): + r"""A Gompertz (or truncated Gumbel) continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `gompertz` is: + + .. math:: + + f(x, c) = c \exp(x) \exp(-c (e^x-1)) + + for :math:`x \ge 0`, :math:`c > 0`. + + `gompertz` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # gompertz.pdf(x, c) = c * exp(x) * exp(-c*(exp(x)-1)) + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + return np.log(c) + x - c * sc.expm1(x) + + def _cdf(self, x, c): + return -sc.expm1(-c * sc.expm1(x)) + + def _ppf(self, q, c): + return sc.log1p(-1.0 / c * sc.log1p(-q)) + + def _sf(self, x, c): + return np.exp(-c * sc.expm1(x)) + + def _isf(self, p, c): + return sc.log1p(-np.log(p)/c) + + def _entropy(self, c): + return 1.0 - np.log(c) - sc._ufuncs._scaled_exp1(c)/c + + +gompertz = gompertz_gen(a=0.0, name='gompertz') + + +def _average_with_log_weights(x, logweights): + x = np.asarray(x) + logweights = np.asarray(logweights) + maxlogw = logweights.max() + weights = np.exp(logweights - maxlogw) + return np.average(x, weights=weights) + + +class gumbel_r_gen(rv_continuous): + r"""A right-skewed Gumbel continuous random variable. + + %(before_notes)s + + See Also + -------- + gumbel_l, gompertz, genextreme + + Notes + ----- + The probability density function for `gumbel_r` is: + + .. math:: + + f(x) = \exp(-(x + e^{-x})) + + The Gumbel distribution is sometimes referred to as a type I Fisher-Tippett + distribution. It is also related to the extreme value distribution, + log-Weibull and Gompertz distributions. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # gumbel_r.pdf(x) = exp(-(x + exp(-x))) + return np.exp(self._logpdf(x)) + + def _logpdf(self, x): + return -x - np.exp(-x) + + def _cdf(self, x): + return np.exp(-np.exp(-x)) + + def _logcdf(self, x): + return -np.exp(-x) + + def _ppf(self, q): + return -np.log(-np.log(q)) + + def _sf(self, x): + return -sc.expm1(-np.exp(-x)) + + def _isf(self, p): + return -np.log(-np.log1p(-p)) + + def _stats(self): + return _EULER, np.pi*np.pi/6.0, 12*np.sqrt(6)/np.pi**3 * _ZETA3, 12.0/5 + + def _entropy(self): + # https://en.wikipedia.org/wiki/Gumbel_distribution + return _EULER + 1. + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + # By the method of maximum likelihood, the estimators of the + # location and scale are the roots of the equations defined in + # `func` and the value of the expression for `loc` that follows. + # The first `func` is a first order derivative of the log-likelihood + # equation and the second is from Source: Statistical Distributions, + # 3rd Edition. Evans, Hastings, and Peacock (2000), Page 101. + + def get_loc_from_scale(scale): + return -scale * (sc.logsumexp(-data / scale) - np.log(len(data))) + + if fscale is not None: + # if the scale is fixed, the location can be analytically + # determined. + scale = fscale + loc = get_loc_from_scale(scale) + else: + # A different function is solved depending on whether the location + # is fixed. + if floc is not None: + loc = floc + + # equation to use if the location is fixed. + # note that one cannot use the equation in Evans, Hastings, + # and Peacock (2000) (since it assumes that the derivative + # w.r.t. the log-likelihood is zero). however, it is easy to + # derive the MLE condition directly if loc is fixed + def func(scale): + term1 = (loc - data) * np.exp((loc - data) / scale) + data + term2 = len(data) * (loc + scale) + return term1.sum() - term2 + else: + + # equation to use if both location and scale are free + def func(scale): + sdata = -data / scale + wavg = _average_with_log_weights(data, logweights=sdata) + return data.mean() - wavg - scale + + # set brackets for `root_scalar` to use when optimizing over the + # scale such that a root is likely between them. Use user supplied + # guess or default 1. + brack_start = kwds.get('scale', 1) + lbrack, rbrack = brack_start / 2, brack_start * 2 + + # if a root is not between the brackets, iteratively expand them + # until they include a sign change, checking after each bracket is + # modified. + def interval_contains_root(lbrack, rbrack): + # return true if the signs disagree. + return (np.sign(func(lbrack)) != + np.sign(func(rbrack))) + while (not interval_contains_root(lbrack, rbrack) + and (lbrack > 0 or rbrack < np.inf)): + lbrack /= 2 + rbrack *= 2 + + res = optimize.root_scalar(func, bracket=(lbrack, rbrack), + rtol=1e-14, xtol=1e-14) + scale = res.root + loc = floc if floc is not None else get_loc_from_scale(scale) + return loc, scale + + +gumbel_r = gumbel_r_gen(name='gumbel_r') + + +class gumbel_l_gen(rv_continuous): + r"""A left-skewed Gumbel continuous random variable. + + %(before_notes)s + + See Also + -------- + gumbel_r, gompertz, genextreme + + Notes + ----- + The probability density function for `gumbel_l` is: + + .. math:: + + f(x) = \exp(x - e^x) + + The Gumbel distribution is sometimes referred to as a type I Fisher-Tippett + distribution. It is also related to the extreme value distribution, + log-Weibull and Gompertz distributions. + + %(after_notes)s + + %(example)s + + """ + + def _shape_info(self): + return [] + + def _pdf(self, x): + # gumbel_l.pdf(x) = exp(x - exp(x)) + return np.exp(self._logpdf(x)) + + def _logpdf(self, x): + return x - np.exp(x) + + def _cdf(self, x): + return -sc.expm1(-np.exp(x)) + + def _ppf(self, q): + return np.log(-sc.log1p(-q)) + + def _logsf(self, x): + return -np.exp(x) + + def _sf(self, x): + return np.exp(-np.exp(x)) + + def _isf(self, x): + return np.log(-np.log(x)) + + def _stats(self): + return -_EULER, np.pi*np.pi/6.0, \ + -12*np.sqrt(6)/np.pi**3 * _ZETA3, 12.0/5 + + def _entropy(self): + return _EULER + 1. + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + # The fit method of `gumbel_r` can be used for this distribution with + # small modifications. The process to do this is + # 1. pass the sign negated data into `gumbel_r.fit` + # - if the location is fixed, it should also be negated. + # 2. negate the sign of the resulting location, leaving the scale + # unmodified. + # `gumbel_r.fit` holds necessary input checks. + + if kwds.get('floc') is not None: + kwds['floc'] = -kwds['floc'] + loc_r, scale_r, = gumbel_r.fit(-np.asarray(data), *args, **kwds) + return -loc_r, scale_r + + +gumbel_l = gumbel_l_gen(name='gumbel_l') + + +class halfcauchy_gen(rv_continuous): + r"""A Half-Cauchy continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `halfcauchy` is: + + .. math:: + + f(x) = \frac{2}{\pi (1 + x^2)} + + for :math:`x \ge 0`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # halfcauchy.pdf(x) = 2 / (pi * (1 + x**2)) + return 2.0/np.pi/(1.0+x*x) + + def _logpdf(self, x): + return np.log(2.0/np.pi) - sc.log1p(x*x) + + def _cdf(self, x): + return 2.0/np.pi*np.arctan(x) + + def _ppf(self, q): + return np.tan(np.pi/2*q) + + def _sf(self, x): + return 2.0/np.pi * np.arctan2(1, x) + + def _isf(self, p): + return 1.0/np.tan(np.pi*p/2) + + def _stats(self): + return np.inf, np.inf, np.nan, np.nan + + def _entropy(self): + return np.log(2*np.pi) + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + # location is independent from the scale + data_min = np.min(data) + if floc is not None: + if data_min < floc: + # There are values that are less than the specified loc. + raise FitDataError("halfcauchy", lower=floc, upper=np.inf) + loc = floc + else: + # if not provided, location MLE is the minimal data point + loc = data_min + + # find scale + def find_scale(loc, data): + shifted_data = data - loc + n = data.size + shifted_data_squared = np.square(shifted_data) + + def fun_to_solve(scale): + denominator = scale**2 + shifted_data_squared + return 2 * np.sum(shifted_data_squared/denominator) - n + + small = np.finfo(1.0).tiny**0.5 # avoid underflow + res = root_scalar(fun_to_solve, bracket=(small, np.max(shifted_data))) + return res.root + + if fscale is not None: + scale = fscale + else: + scale = find_scale(loc, data) + + return loc, scale + + +halfcauchy = halfcauchy_gen(a=0.0, name='halfcauchy') + + +class halflogistic_gen(rv_continuous): + r"""A half-logistic continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `halflogistic` is: + + .. math:: + + f(x) = \frac{ 2 e^{-x} }{ (1+e^{-x})^2 } + = \frac{1}{2} \text{sech}(x/2)^2 + + for :math:`x \ge 0`. + + %(after_notes)s + + References + ---------- + .. [1] Asgharzadeh et al (2011). "Comparisons of Methods of Estimation for the + Half-Logistic Distribution". Selcuk J. Appl. Math. 93-108. + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # halflogistic.pdf(x) = 2 * exp(-x) / (1+exp(-x))**2 + # = 1/2 * sech(x/2)**2 + return np.exp(self._logpdf(x)) + + def _logpdf(self, x): + return np.log(2) - x - 2. * sc.log1p(np.exp(-x)) + + def _cdf(self, x): + return np.tanh(x/2.0) + + def _ppf(self, q): + return 2*np.arctanh(q) + + def _sf(self, x): + return 2 * sc.expit(-x) + + def _isf(self, q): + return _lazywhere(q < 0.5, (q, ), + lambda q: -sc.logit(0.5 * q), + f2=lambda q: 2*np.arctanh(1 - q)) + + def _munp(self, n): + if n == 1: + return 2*np.log(2) + if n == 2: + return np.pi*np.pi/3.0 + if n == 3: + return 9*_ZETA3 + if n == 4: + return 7*np.pi**4 / 15.0 + return 2*(1-pow(2.0, 1-n))*sc.gamma(n+1)*sc.zeta(n, 1) + + def _entropy(self): + return 2-np.log(2) + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + def find_scale(data, loc): + # scale is solution to a fix point problem ([1] 2.6) + # use approximate MLE as starting point ([1] 3.1) + n_observations = data.shape[0] + sorted_data = np.sort(data, axis=0) + p = np.arange(1, n_observations + 1)/(n_observations + 1) + q = 1 - p + pp1 = 1 + p + alpha = p - 0.5 * q * pp1 * np.log(pp1 / q) + beta = 0.5 * q * pp1 + sorted_data = sorted_data - loc + B = 2 * np.sum(alpha[1:] * sorted_data[1:]) + C = 2 * np.sum(beta[1:] * sorted_data[1:]**2) + # starting guess + scale = ((B + np.sqrt(B**2 + 8 * n_observations * C)) + /(4 * n_observations)) + + # relative tolerance of fix point iterator + rtol = 1e-8 + relative_residual = 1 + shifted_mean = sorted_data.mean() # y_mean - y_min + + # find fix point by repeated application of eq. (2.6) + # simplify as + # exp(-x) / (1 + exp(-x)) = 1 / (1 + exp(x)) + # = expit(-x)) + while relative_residual > rtol: + sum_term = sorted_data * sc.expit(-sorted_data/scale) + scale_new = shifted_mean - 2/n_observations * sum_term.sum() + relative_residual = abs((scale - scale_new)/scale) + scale = scale_new + return scale + + # location is independent from the scale + data_min = np.min(data) + if floc is not None: + if data_min < floc: + # There are values that are less than the specified loc. + raise FitDataError("halflogistic", lower=floc, upper=np.inf) + loc = floc + else: + # if not provided, location MLE is the minimal data point + loc = data_min + + # scale depends on location + scale = fscale if fscale is not None else find_scale(data, loc) + + return loc, scale + + +halflogistic = halflogistic_gen(a=0.0, name='halflogistic') + + +class halfnorm_gen(rv_continuous): + r"""A half-normal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `halfnorm` is: + + .. math:: + + f(x) = \sqrt{2/\pi} \exp(-x^2 / 2) + + for :math:`x >= 0`. + + `halfnorm` is a special case of `chi` with ``df=1``. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return abs(random_state.standard_normal(size=size)) + + def _pdf(self, x): + # halfnorm.pdf(x) = sqrt(2/pi) * exp(-x**2/2) + return np.sqrt(2.0/np.pi)*np.exp(-x*x/2.0) + + def _logpdf(self, x): + return 0.5 * np.log(2.0/np.pi) - x*x/2.0 + + def _cdf(self, x): + return sc.erf(x / np.sqrt(2)) + + def _ppf(self, q): + return _norm_ppf((1+q)/2.0) + + def _sf(self, x): + return 2 * _norm_sf(x) + + def _isf(self, p): + return _norm_isf(p/2) + + def _stats(self): + return (np.sqrt(2.0/np.pi), + 1-2.0/np.pi, + np.sqrt(2)*(4-np.pi)/(np.pi-2)**1.5, + 8*(np.pi-3)/(np.pi-2)**2) + + def _entropy(self): + return 0.5*np.log(np.pi/2.0)+0.5 + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + data_min = np.min(data) + + if floc is not None: + if data_min < floc: + # There are values that are less than the specified loc. + raise FitDataError("halfnorm", lower=floc, upper=np.inf) + loc = floc + else: + loc = data_min + + if fscale is not None: + scale = fscale + else: + scale = stats.moment(data, order=2, center=loc)**0.5 + + return loc, scale + + +halfnorm = halfnorm_gen(a=0.0, name='halfnorm') + + +class hypsecant_gen(rv_continuous): + r"""A hyperbolic secant continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `hypsecant` is: + + .. math:: + + f(x) = \frac{1}{\pi} \text{sech}(x) + + for a real number :math:`x`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + # hypsecant.pdf(x) = 1/pi * sech(x) + return 1.0/(np.pi*np.cosh(x)) + + def _cdf(self, x): + return 2.0/np.pi*np.arctan(np.exp(x)) + + def _ppf(self, q): + return np.log(np.tan(np.pi*q/2.0)) + + def _sf(self, x): + return 2.0/np.pi*np.arctan(np.exp(-x)) + + def _isf(self, q): + return -np.log(np.tan(np.pi*q/2.0)) + + def _stats(self): + return 0, np.pi*np.pi/4, 0, 2 + + def _entropy(self): + return np.log(2*np.pi) + + +hypsecant = hypsecant_gen(name='hypsecant') + + +class gausshyper_gen(rv_continuous): + r"""A Gauss hypergeometric continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `gausshyper` is: + + .. math:: + + f(x, a, b, c, z) = C x^{a-1} (1-x)^{b-1} (1+zx)^{-c} + + for :math:`0 \le x \le 1`, :math:`a,b > 0`, :math:`c` a real number, + :math:`z > -1`, and :math:`C = \frac{1}{B(a, b) F[2, 1](c, a; a+b; -z)}`. + :math:`F[2, 1]` is the Gauss hypergeometric function + `scipy.special.hyp2f1`. + + `gausshyper` takes :math:`a`, :math:`b`, :math:`c` and :math:`z` as shape + parameters. + + %(after_notes)s + + References + ---------- + .. [1] Armero, C., and M. J. Bayarri. "Prior Assessments for Prediction in + Queues." *Journal of the Royal Statistical Society*. Series D (The + Statistician) 43, no. 1 (1994): 139-53. doi:10.2307/2348939 + + %(example)s + + """ + + def _argcheck(self, a, b, c, z): + # z > -1 per gh-10134 + return (a > 0) & (b > 0) & (c == c) & (z > -1) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + ic = _ShapeInfo("c", False, (-np.inf, np.inf), (False, False)) + iz = _ShapeInfo("z", False, (-1, np.inf), (False, False)) + return [ia, ib, ic, iz] + + def _pdf(self, x, a, b, c, z): + normalization_constant = sc.beta(a, b) * sc.hyp2f1(c, a, a + b, -z) + return (1./normalization_constant * x**(a - 1.) * (1. - x)**(b - 1.0) + / (1.0 + z*x)**c) + + def _munp(self, n, a, b, c, z): + fac = sc.beta(n+a, b) / sc.beta(a, b) + num = sc.hyp2f1(c, a+n, a+b+n, -z) + den = sc.hyp2f1(c, a, a+b, -z) + return fac*num / den + + +gausshyper = gausshyper_gen(a=0.0, b=1.0, name='gausshyper') + + +class invgamma_gen(rv_continuous): + r"""An inverted gamma continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `invgamma` is: + + .. math:: + + f(x, a) = \frac{x^{-a-1}}{\Gamma(a)} \exp(-\frac{1}{x}) + + for :math:`x >= 0`, :math:`a > 0`. :math:`\Gamma` is the gamma function + (`scipy.special.gamma`). + + `invgamma` takes ``a`` as a shape parameter for :math:`a`. + + `invgamma` is a special case of `gengamma` with ``c=-1``, and it is a + different parameterization of the scaled inverse chi-squared distribution. + Specifically, if the scaled inverse chi-squared distribution is + parameterized with degrees of freedom :math:`\nu` and scaling parameter + :math:`\tau^2`, then it can be modeled using `invgamma` with + ``a=`` :math:`\nu/2` and ``scale=`` :math:`\nu \tau^2/2`. + + %(after_notes)s + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, a): + # invgamma.pdf(x, a) = x**(-a-1) / gamma(a) * exp(-1/x) + return np.exp(self._logpdf(x, a)) + + def _logpdf(self, x, a): + return -(a+1) * np.log(x) - sc.gammaln(a) - 1.0/x + + def _cdf(self, x, a): + return sc.gammaincc(a, 1.0 / x) + + def _ppf(self, q, a): + return 1.0 / sc.gammainccinv(a, q) + + def _sf(self, x, a): + return sc.gammainc(a, 1.0 / x) + + def _isf(self, q, a): + return 1.0 / sc.gammaincinv(a, q) + + def _stats(self, a, moments='mvsk'): + m1 = _lazywhere(a > 1, (a,), lambda x: 1. / (x - 1.), np.inf) + m2 = _lazywhere(a > 2, (a,), lambda x: 1. / (x - 1.)**2 / (x - 2.), + np.inf) + + g1, g2 = None, None + if 's' in moments: + g1 = _lazywhere( + a > 3, (a,), + lambda x: 4. * np.sqrt(x - 2.) / (x - 3.), np.nan) + if 'k' in moments: + g2 = _lazywhere( + a > 4, (a,), + lambda x: 6. * (5. * x - 11.) / (x - 3.) / (x - 4.), np.nan) + return m1, m2, g1, g2 + + def _entropy(self, a): + def regular(a): + h = a - (a + 1.0) * sc.psi(a) + sc.gammaln(a) + return h + + def asymptotic(a): + # gammaln(a) ~ a * ln(a) - a - 0.5 * ln(a) + 0.5 * ln(2 * pi) + # psi(a) ~ ln(a) - 1 / (2 * a) + h = ((1 - 3*np.log(a) + np.log(2) + np.log(np.pi))/2 + + 2/3*a**-1. + a**-2./12 - a**-3./90 - a**-4./120) + return h + + h = _lazywhere(a >= 2e2, (a,), f=asymptotic, f2=regular) + return h + + +invgamma = invgamma_gen(a=0.0, name='invgamma') + + +class invgauss_gen(rv_continuous): + r"""An inverse Gaussian continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `invgauss` is: + + .. math:: + + f(x; \mu) = \frac{1}{\sqrt{2 \pi x^3}} + \exp\left(-\frac{(x-\mu)^2}{2 \mu^2 x}\right) + + for :math:`x \ge 0` and :math:`\mu > 0`. + + `invgauss` takes ``mu`` as a shape parameter for :math:`\mu`. + + %(after_notes)s + + A common shape-scale parameterization of the inverse Gaussian distribution + has density + + .. math:: + + f(x; \nu, \lambda) = \sqrt{\frac{\lambda}{2 \pi x^3}} + \exp\left( -\frac{\lambda(x-\nu)^2}{2 \nu^2 x}\right) + + Using ``nu`` for :math:`\nu` and ``lam`` for :math:`\lambda`, this + parameterization is equivalent to the one above with ``mu = nu/lam``, + ``loc = 0``, and ``scale = lam``. + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("mu", False, (0, np.inf), (False, False))] + + def _rvs(self, mu, size=None, random_state=None): + return random_state.wald(mu, 1.0, size=size) + + def _pdf(self, x, mu): + # invgauss.pdf(x, mu) = + # 1 / sqrt(2*pi*x**3) * exp(-(x-mu)**2/(2*x*mu**2)) + return 1.0/np.sqrt(2*np.pi*x**3.0)*np.exp(-1.0/(2*x)*((x-mu)/mu)**2) + + def _logpdf(self, x, mu): + return -0.5*np.log(2*np.pi) - 1.5*np.log(x) - ((x-mu)/mu)**2/(2*x) + + # approach adapted from equations in + # https://journal.r-project.org/archive/2016-1/giner-smyth.pdf, + # not R code. see gh-13616 + + def _logcdf(self, x, mu): + fac = 1 / np.sqrt(x) + a = _norm_logcdf(fac * ((x / mu) - 1)) + b = 2 / mu + _norm_logcdf(-fac * ((x / mu) + 1)) + return a + np.log1p(np.exp(b - a)) + + def _logsf(self, x, mu): + fac = 1 / np.sqrt(x) + a = _norm_logsf(fac * ((x / mu) - 1)) + b = 2 / mu + _norm_logcdf(-fac * (x + mu) / mu) + return a + np.log1p(-np.exp(b - a)) + + def _sf(self, x, mu): + return np.exp(self._logsf(x, mu)) + + def _cdf(self, x, mu): + return np.exp(self._logcdf(x, mu)) + + def _ppf(self, x, mu): + with np.errstate(divide='ignore', over='ignore', invalid='ignore'): + x, mu = np.broadcast_arrays(x, mu) + ppf = _boost._invgauss_ppf(x, mu, 1) + i_wt = x > 0.5 # "wrong tail" - sometimes too inaccurate + ppf[i_wt] = _boost._invgauss_isf(1-x[i_wt], mu[i_wt], 1) + i_nan = np.isnan(ppf) + ppf[i_nan] = super()._ppf(x[i_nan], mu[i_nan]) + return ppf + + def _isf(self, x, mu): + with np.errstate(divide='ignore', over='ignore', invalid='ignore'): + x, mu = np.broadcast_arrays(x, mu) + isf = _boost._invgauss_isf(x, mu, 1) + i_wt = x > 0.5 # "wrong tail" - sometimes too inaccurate + isf[i_wt] = _boost._invgauss_ppf(1-x[i_wt], mu[i_wt], 1) + i_nan = np.isnan(isf) + isf[i_nan] = super()._isf(x[i_nan], mu[i_nan]) + return isf + + def _stats(self, mu): + return mu, mu**3.0, 3*np.sqrt(mu), 15*mu + + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + method = kwds.get('method', 'mle') + + if (isinstance(data, CensoredData) or type(self) == wald_gen + or method.lower() == 'mm'): + return super().fit(data, *args, **kwds) + + data, fshape_s, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + ''' + Source: Statistical Distributions, 3rd Edition. Evans, Hastings, + and Peacock (2000), Page 121. Their shape parameter is equivalent to + SciPy's with the conversion `fshape_s = fshape / scale`. + + MLE formulas are not used in 3 conditions: + - `loc` is not fixed + - `mu` is fixed + These cases fall back on the superclass fit method. + - `loc` is fixed but translation results in negative data raises + a `FitDataError`. + ''' + if floc is None or fshape_s is not None: + return super().fit(data, *args, **kwds) + elif np.any(data - floc < 0): + raise FitDataError("invgauss", lower=0, upper=np.inf) + else: + data = data - floc + fshape_n = np.mean(data) + if fscale is None: + fscale = len(data) / (np.sum(data ** -1 - fshape_n ** -1)) + fshape_s = fshape_n / fscale + return fshape_s, floc, fscale + + def _entropy(self, mu): + """ + Ref.: https://moser-isi.ethz.ch/docs/papers/smos-2012-10.pdf (eq. 9) + """ + # a = log(2*pi*e*mu**3) + # = 1 + log(2*pi) + 3 * log(mu) + a = 1. + np.log(2 * np.pi) + 3 * np.log(mu) + # b = exp(2/mu) * exp1(2/mu) + # = _scaled_exp1(2/mu) / (2/mu) + r = 2/mu + b = sc._ufuncs._scaled_exp1(r)/r + return 0.5 * a - 1.5 * b + + +invgauss = invgauss_gen(a=0.0, name='invgauss') + + +class geninvgauss_gen(rv_continuous): + r"""A Generalized Inverse Gaussian continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `geninvgauss` is: + + .. math:: + + f(x, p, b) = x^{p-1} \exp(-b (x + 1/x) / 2) / (2 K_p(b)) + + where `x > 0`, `p` is a real number and `b > 0`\([1]_). + :math:`K_p` is the modified Bessel function of second kind of order `p` + (`scipy.special.kv`). + + %(after_notes)s + + The inverse Gaussian distribution `stats.invgauss(mu)` is a special case of + `geninvgauss` with `p = -1/2`, `b = 1 / mu` and `scale = mu`. + + Generating random variates is challenging for this distribution. The + implementation is based on [2]_. + + References + ---------- + .. [1] O. Barndorff-Nielsen, P. Blaesild, C. Halgreen, "First hitting time + models for the generalized inverse gaussian distribution", + Stochastic Processes and their Applications 7, pp. 49--54, 1978. + + .. [2] W. Hoermann and J. Leydold, "Generating generalized inverse Gaussian + random variates", Statistics and Computing, 24(4), p. 547--557, 2014. + + %(example)s + + """ + def _argcheck(self, p, b): + return (p == p) & (b > 0) + + def _shape_info(self): + ip = _ShapeInfo("p", False, (-np.inf, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ip, ib] + + def _logpdf(self, x, p, b): + # kve instead of kv works better for large values of b + # warn if kve produces infinite values and replace by nan + # otherwise c = -inf and the results are often incorrect + def logpdf_single(x, p, b): + return _stats.geninvgauss_logpdf(x, p, b) + + logpdf_single = np.vectorize(logpdf_single, otypes=[np.float64]) + + z = logpdf_single(x, p, b) + if np.isnan(z).any(): + msg = ("Infinite values encountered in scipy.special.kve(p, b). " + "Values replaced by NaN to avoid incorrect results.") + warnings.warn(msg, RuntimeWarning, stacklevel=3) + return z + + def _pdf(self, x, p, b): + # relying on logpdf avoids overflow of x**(p-1) for large x and p + return np.exp(self._logpdf(x, p, b)) + + def _cdf(self, x, *args): + _a, _b = self._get_support(*args) + + def _cdf_single(x, *args): + p, b = args + user_data = np.array([p, b], float).ctypes.data_as(ctypes.c_void_p) + llc = LowLevelCallable.from_cython(_stats, '_geninvgauss_pdf', + user_data) + + return integrate.quad(llc, _a, x)[0] + + _cdf_single = np.vectorize(_cdf_single, otypes=[np.float64]) + + return _cdf_single(x, *args) + + def _logquasipdf(self, x, p, b): + # log of the quasi-density (w/o normalizing constant) used in _rvs + return _lazywhere(x > 0, (x, p, b), + lambda x, p, b: (p - 1)*np.log(x) - b*(x + 1/x)/2, + -np.inf) + + def _rvs(self, p, b, size=None, random_state=None): + # if p and b are scalar, use _rvs_scalar, otherwise need to create + # output by iterating over parameters + if np.isscalar(p) and np.isscalar(b): + out = self._rvs_scalar(p, b, size, random_state) + elif p.size == 1 and b.size == 1: + out = self._rvs_scalar(p.item(), b.item(), size, random_state) + else: + # When this method is called, size will be a (possibly empty) + # tuple of integers. It will not be None; if `size=None` is passed + # to `rvs()`, size will be the empty tuple (). + + p, b = np.broadcast_arrays(p, b) + # p and b now have the same shape. + + # `shp` is the shape of the blocks of random variates that are + # generated for each combination of parameters associated with + # broadcasting p and b. + # bc is a tuple the same length as size. The values + # in bc are bools. If bc[j] is True, it means that + # entire axis is filled in for a given combination of the + # broadcast arguments. + shp, bc = _check_shape(p.shape, size) + + # `numsamples` is the total number of variates to be generated + # for each combination of the input arguments. + numsamples = int(np.prod(shp)) + + # `out` is the array to be returned. It is filled in the + # loop below. + out = np.empty(size) + + it = np.nditer([p, b], + flags=['multi_index'], + op_flags=[['readonly'], ['readonly']]) + while not it.finished: + # Convert the iterator's multi_index into an index into the + # `out` array where the call to _rvs_scalar() will be stored. + # Where bc is True, we use a full slice; otherwise we use the + # index value from it.multi_index. len(it.multi_index) might + # be less than len(bc), and in that case we want to align these + # two sequences to the right, so the loop variable j runs from + # -len(size) to 0. This doesn't cause an IndexError, as + # bc[j] will be True in those cases where it.multi_index[j] + # would cause an IndexError. + idx = tuple((it.multi_index[j] if not bc[j] else slice(None)) + for j in range(-len(size), 0)) + out[idx] = self._rvs_scalar(it[0], it[1], numsamples, + random_state).reshape(shp) + it.iternext() + + if size == (): + out = out.item() + return out + + def _rvs_scalar(self, p, b, numsamples, random_state): + # following [2], the quasi-pdf is used instead of the pdf for the + # generation of rvs + invert_res = False + if not numsamples: + numsamples = 1 + if p < 0: + # note: if X is geninvgauss(p, b), then 1/X is geninvgauss(-p, b) + p = -p + invert_res = True + m = self._mode(p, b) + + # determine method to be used following [2] + ratio_unif = True + if p >= 1 or b > 1: + # ratio of uniforms with mode shift below + mode_shift = True + elif b >= min(0.5, 2 * np.sqrt(1 - p) / 3): + # ratio of uniforms without mode shift below + mode_shift = False + else: + # new algorithm in [2] + ratio_unif = False + + # prepare sampling of rvs + size1d = tuple(np.atleast_1d(numsamples)) + N = np.prod(size1d) # number of rvs needed, reshape upon return + x = np.zeros(N) + simulated = 0 + + if ratio_unif: + # use ratio of uniforms method + if mode_shift: + a2 = -2 * (p + 1) / b - m + a1 = 2 * m * (p - 1) / b - 1 + # find roots of x**3 + a2*x**2 + a1*x + m (Cardano's formula) + p1 = a1 - a2**2 / 3 + q1 = 2 * a2**3 / 27 - a2 * a1 / 3 + m + phi = np.arccos(-q1 * np.sqrt(-27 / p1**3) / 2) + s1 = -np.sqrt(-4 * p1 / 3) + root1 = s1 * np.cos(phi / 3 + np.pi / 3) - a2 / 3 + root2 = -s1 * np.cos(phi / 3) - a2 / 3 + # root3 = s1 * np.cos(phi / 3 - np.pi / 3) - a2 / 3 + + # if g is the quasipdf, rescale: g(x) / g(m) which we can write + # as exp(log(g(x)) - log(g(m))). This is important + # since for large values of p and b, g cannot be evaluated. + # denote the rescaled quasipdf by h + lm = self._logquasipdf(m, p, b) + d1 = self._logquasipdf(root1, p, b) - lm + d2 = self._logquasipdf(root2, p, b) - lm + # compute the bounding rectangle w.r.t. h. Note that + # np.exp(0.5*d1) = np.sqrt(g(root1)/g(m)) = np.sqrt(h(root1)) + vmin = (root1 - m) * np.exp(0.5 * d1) + vmax = (root2 - m) * np.exp(0.5 * d2) + umax = 1 # umax = sqrt(h(m)) = 1 + + def logqpdf(x): + return self._logquasipdf(x, p, b) - lm + + c = m + else: + # ratio of uniforms without mode shift + # compute np.sqrt(quasipdf(m)) + umax = np.exp(0.5*self._logquasipdf(m, p, b)) + xplus = ((1 + p) + np.sqrt((1 + p)**2 + b**2))/b + vmin = 0 + # compute xplus * np.sqrt(quasipdf(xplus)) + vmax = xplus * np.exp(0.5 * self._logquasipdf(xplus, p, b)) + c = 0 + + def logqpdf(x): + return self._logquasipdf(x, p, b) + + if vmin >= vmax: + raise ValueError("vmin must be smaller than vmax.") + if umax <= 0: + raise ValueError("umax must be positive.") + + i = 1 + while simulated < N: + k = N - simulated + # simulate uniform rvs on [0, umax] and [vmin, vmax] + u = umax * random_state.uniform(size=k) + v = random_state.uniform(size=k) + v = vmin + (vmax - vmin) * v + rvs = v / u + c + # rewrite acceptance condition u**2 <= pdf(rvs) by taking logs + accept = (2*np.log(u) <= logqpdf(rvs)) + num_accept = np.sum(accept) + if num_accept > 0: + x[simulated:(simulated + num_accept)] = rvs[accept] + simulated += num_accept + + if (simulated == 0) and (i*N >= 50000): + msg = ("Not a single random variate could be generated " + f"in {i*N} attempts. Sampling does not appear to " + "work for the provided parameters.") + raise RuntimeError(msg) + i += 1 + else: + # use new algorithm in [2] + x0 = b / (1 - p) + xs = np.max((x0, 2 / b)) + k1 = np.exp(self._logquasipdf(m, p, b)) + A1 = k1 * x0 + if x0 < 2 / b: + k2 = np.exp(-b) + if p > 0: + A2 = k2 * ((2 / b)**p - x0**p) / p + else: + A2 = k2 * np.log(2 / b**2) + else: + k2, A2 = 0, 0 + k3 = xs**(p - 1) + A3 = 2 * k3 * np.exp(-xs * b / 2) / b + A = A1 + A2 + A3 + + # [2]: rejection constant is < 2.73; so expected runtime is finite + while simulated < N: + k = N - simulated + h, rvs = np.zeros(k), np.zeros(k) + # simulate uniform rvs on [x1, x2] and [0, y2] + u = random_state.uniform(size=k) + v = A * random_state.uniform(size=k) + cond1 = v <= A1 + cond2 = np.logical_not(cond1) & (v <= A1 + A2) + cond3 = np.logical_not(cond1 | cond2) + # subdomain (0, x0) + rvs[cond1] = x0 * v[cond1] / A1 + h[cond1] = k1 + # subdomain (x0, 2 / b) + if p > 0: + rvs[cond2] = (x0**p + (v[cond2] - A1) * p / k2)**(1 / p) + else: + rvs[cond2] = b * np.exp((v[cond2] - A1) * np.exp(b)) + h[cond2] = k2 * rvs[cond2]**(p - 1) + # subdomain (xs, infinity) + z = np.exp(-xs * b / 2) - b * (v[cond3] - A1 - A2) / (2 * k3) + rvs[cond3] = -2 / b * np.log(z) + h[cond3] = k3 * np.exp(-rvs[cond3] * b / 2) + # apply rejection method + accept = (np.log(u * h) <= self._logquasipdf(rvs, p, b)) + num_accept = sum(accept) + if num_accept > 0: + x[simulated:(simulated + num_accept)] = rvs[accept] + simulated += num_accept + + rvs = np.reshape(x, size1d) + if invert_res: + rvs = 1 / rvs + return rvs + + def _mode(self, p, b): + # distinguish cases to avoid catastrophic cancellation (see [2]) + if p < 1: + return b / (np.sqrt((p - 1)**2 + b**2) + 1 - p) + else: + return (np.sqrt((1 - p)**2 + b**2) - (1 - p)) / b + + def _munp(self, n, p, b): + num = sc.kve(p + n, b) + denom = sc.kve(p, b) + inf_vals = np.isinf(num) | np.isinf(denom) + if inf_vals.any(): + msg = ("Infinite values encountered in the moment calculation " + "involving scipy.special.kve. Values replaced by NaN to " + "avoid incorrect results.") + warnings.warn(msg, RuntimeWarning, stacklevel=3) + m = np.full_like(num, np.nan, dtype=np.float64) + m[~inf_vals] = num[~inf_vals] / denom[~inf_vals] + else: + m = num / denom + return m + + +geninvgauss = geninvgauss_gen(a=0.0, name="geninvgauss") + + +class norminvgauss_gen(rv_continuous): + r"""A Normal Inverse Gaussian continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `norminvgauss` is: + + .. math:: + + f(x, a, b) = \frac{a \, K_1(a \sqrt{1 + x^2})}{\pi \sqrt{1 + x^2}} \, + \exp(\sqrt{a^2 - b^2} + b x) + + where :math:`x` is a real number, the parameter :math:`a` is the tail + heaviness and :math:`b` is the asymmetry parameter satisfying + :math:`a > 0` and :math:`|b| <= a`. + :math:`K_1` is the modified Bessel function of second kind + (`scipy.special.k1`). + + %(after_notes)s + + A normal inverse Gaussian random variable `Y` with parameters `a` and `b` + can be expressed as a normal mean-variance mixture: + `Y = b * V + sqrt(V) * X` where `X` is `norm(0,1)` and `V` is + `invgauss(mu=1/sqrt(a**2 - b**2))`. This representation is used + to generate random variates. + + Another common parametrization of the distribution (see Equation 2.1 in + [2]_) is given by the following expression of the pdf: + + .. math:: + + g(x, \alpha, \beta, \delta, \mu) = + \frac{\alpha\delta K_1\left(\alpha\sqrt{\delta^2 + (x - \mu)^2}\right)} + {\pi \sqrt{\delta^2 + (x - \mu)^2}} \, + e^{\delta \sqrt{\alpha^2 - \beta^2} + \beta (x - \mu)} + + In SciPy, this corresponds to + `a = alpha * delta, b = beta * delta, loc = mu, scale=delta`. + + References + ---------- + .. [1] O. Barndorff-Nielsen, "Hyperbolic Distributions and Distributions on + Hyperbolae", Scandinavian Journal of Statistics, Vol. 5(3), + pp. 151-157, 1978. + + .. [2] O. Barndorff-Nielsen, "Normal Inverse Gaussian Distributions and + Stochastic Volatility Modelling", Scandinavian Journal of + Statistics, Vol. 24, pp. 1-13, 1997. + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _argcheck(self, a, b): + return (a > 0) & (np.absolute(b) < a) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) + return [ia, ib] + + def _fitstart(self, data): + # Arbitrary, but the default a = b = 1 is not valid; the distribution + # requires |b| < a. + return super()._fitstart(data, args=(1, 0.5)) + + def _pdf(self, x, a, b): + gamma = np.sqrt(a**2 - b**2) + fac1 = a / np.pi + sq = np.hypot(1, x) # reduce overflows + return fac1 * sc.k1e(a * sq) * np.exp(b*x - a*sq + gamma) / sq + + def _sf(self, x, a, b): + if np.isscalar(x): + # If x is a scalar, then so are a and b. + return integrate.quad(self._pdf, x, np.inf, args=(a, b))[0] + else: + a = np.atleast_1d(a) + b = np.atleast_1d(b) + result = [] + for (x0, a0, b0) in zip(x, a, b): + result.append(integrate.quad(self._pdf, x0, np.inf, + args=(a0, b0))[0]) + return np.array(result) + + def _isf(self, q, a, b): + def _isf_scalar(q, a, b): + + def eq(x, a, b, q): + # Solve eq(x, a, b, q) = 0 to obtain isf(x, a, b) = q. + return self._sf(x, a, b) - q + + # Find a bracketing interval for the root. + # Start at the mean, and grow the length of the interval + # by 2 each iteration until there is a sign change in eq. + xm = self.mean(a, b) + em = eq(xm, a, b, q) + if em == 0: + # Unlikely, but might as well check. + return xm + if em > 0: + delta = 1 + left = xm + right = xm + delta + while eq(right, a, b, q) > 0: + delta = 2*delta + right = xm + delta + else: + # em < 0 + delta = 1 + right = xm + left = xm - delta + while eq(left, a, b, q) < 0: + delta = 2*delta + left = xm - delta + result = optimize.brentq(eq, left, right, args=(a, b, q), + xtol=self.xtol) + return result + + if np.isscalar(q): + return _isf_scalar(q, a, b) + else: + result = [] + for (q0, a0, b0) in zip(q, a, b): + result.append(_isf_scalar(q0, a0, b0)) + return np.array(result) + + def _rvs(self, a, b, size=None, random_state=None): + # note: X = b * V + sqrt(V) * X is norminvgaus(a,b) if X is standard + # normal and V is invgauss(mu=1/sqrt(a**2 - b**2)) + gamma = np.sqrt(a**2 - b**2) + ig = invgauss.rvs(mu=1/gamma, size=size, random_state=random_state) + return b * ig + np.sqrt(ig) * norm.rvs(size=size, + random_state=random_state) + + def _stats(self, a, b): + gamma = np.sqrt(a**2 - b**2) + mean = b / gamma + variance = a**2 / gamma**3 + skewness = 3.0 * b / (a * np.sqrt(gamma)) + kurtosis = 3.0 * (1 + 4 * b**2 / a**2) / gamma + return mean, variance, skewness, kurtosis + + +norminvgauss = norminvgauss_gen(name="norminvgauss") + + +class invweibull_gen(rv_continuous): + """An inverted Weibull continuous random variable. + + This distribution is also known as the Fréchet distribution or the + type II extreme value distribution. + + %(before_notes)s + + Notes + ----- + The probability density function for `invweibull` is: + + .. math:: + + f(x, c) = c x^{-c-1} \\exp(-x^{-c}) + + for :math:`x > 0`, :math:`c > 0`. + + `invweibull` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + References + ---------- + F.R.S. de Gusmao, E.M.M Ortega and G.M. Cordeiro, "The generalized inverse + Weibull distribution", Stat. Papers, vol. 52, pp. 591-619, 2011. + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # invweibull.pdf(x, c) = c * x**(-c-1) * exp(-x**(-c)) + xc1 = np.power(x, -c - 1.0) + xc2 = np.power(x, -c) + xc2 = np.exp(-xc2) + return c * xc1 * xc2 + + def _cdf(self, x, c): + xc1 = np.power(x, -c) + return np.exp(-xc1) + + def _sf(self, x, c): + return -np.expm1(-x**-c) + + def _ppf(self, q, c): + return np.power(-np.log(q), -1.0/c) + + def _isf(self, p, c): + return (-np.log1p(-p))**(-1/c) + + def _munp(self, n, c): + return sc.gamma(1 - n / c) + + def _entropy(self, c): + return 1+_EULER + _EULER / c - np.log(c) + + def _fitstart(self, data, args=None): + # invweibull requires c > 1 for the first moment to exist, so use 2.0 + args = (2.0,) if args is None else args + return super()._fitstart(data, args=args) + + +invweibull = invweibull_gen(a=0, name='invweibull') + + +class jf_skew_t_gen(rv_continuous): + r"""Jones and Faddy skew-t distribution. + + %(before_notes)s + + Notes + ----- + The probability density function for `jf_skew_t` is: + + .. math:: + + f(x; a, b) = C_{a,b}^{-1} + \left(1+\frac{x}{\left(a+b+x^2\right)^{1/2}}\right)^{a+1/2} + \left(1-\frac{x}{\left(a+b+x^2\right)^{1/2}}\right)^{b+1/2} + + for real numbers :math:`a>0` and :math:`b>0`, where + :math:`C_{a,b} = 2^{a+b-1}B(a,b)(a+b)^{1/2}`, and :math:`B` denotes the + beta function (`scipy.special.beta`). + + When :math:`ab`, the distribution is positively skewed. If :math:`a=b`, then + we recover the `t` distribution with :math:`2a` degrees of freedom. + + `jf_skew_t` takes :math:`a` and :math:`b` as shape parameters. + + %(after_notes)s + + References + ---------- + .. [1] M.C. Jones and M.J. Faddy. "A skew extension of the t distribution, + with applications" *Journal of the Royal Statistical Society*. + Series B (Statistical Methodology) 65, no. 1 (2003): 159-174. + :doi:`10.1111/1467-9868.00378` + + %(example)s + + """ + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _pdf(self, x, a, b): + c = 2 ** (a + b - 1) * sc.beta(a, b) * np.sqrt(a + b) + d1 = (1 + x / np.sqrt(a + b + x ** 2)) ** (a + 0.5) + d2 = (1 - x / np.sqrt(a + b + x ** 2)) ** (b + 0.5) + return d1 * d2 / c + + def _rvs(self, a, b, size=None, random_state=None): + d1 = random_state.beta(a, b, size) + d2 = (2 * d1 - 1) * np.sqrt(a + b) + d3 = 2 * np.sqrt(d1 * (1 - d1)) + return d2 / d3 + + def _cdf(self, x, a, b): + y = (1 + x / np.sqrt(a + b + x ** 2)) * 0.5 + return sc.betainc(a, b, y) + + def _ppf(self, q, a, b): + d1 = beta.ppf(q, a, b) + d2 = (2 * d1 - 1) * np.sqrt(a + b) + d3 = 2 * np.sqrt(d1 * (1 - d1)) + return d2 / d3 + + def _munp(self, n, a, b): + """Returns the n-th moment(s) where all the following hold: + + - n >= 0 + - a > n / 2 + - b > n / 2 + + The result is np.nan in all other cases. + """ + def nth_moment(n_k, a_k, b_k): + """Computes E[T^(n_k)] where T is skew-t distributed with + parameters a_k and b_k. + """ + num = (a_k + b_k) ** (0.5 * n_k) + denom = 2 ** n_k * sc.beta(a_k, b_k) + + indices = np.arange(n_k + 1) + sgn = np.where(indices % 2 > 0, -1, 1) + d = sc.beta(a_k + 0.5 * n_k - indices, b_k - 0.5 * n_k + indices) + sum_terms = sc.comb(n_k, indices) * sgn * d + + return num / denom * sum_terms.sum() + + nth_moment_valid = (a > 0.5 * n) & (b > 0.5 * n) & (n >= 0) + return _lazywhere( + nth_moment_valid, + (n, a, b), + np.vectorize(nth_moment, otypes=[np.float64]), + np.nan, + ) + + +jf_skew_t = jf_skew_t_gen(name='jf_skew_t') + + +class johnsonsb_gen(rv_continuous): + r"""A Johnson SB continuous random variable. + + %(before_notes)s + + See Also + -------- + johnsonsu + + Notes + ----- + The probability density function for `johnsonsb` is: + + .. math:: + + f(x, a, b) = \frac{b}{x(1-x)} \phi(a + b \log \frac{x}{1-x} ) + + where :math:`x`, :math:`a`, and :math:`b` are real scalars; :math:`b > 0` + and :math:`x \in [0,1]`. :math:`\phi` is the pdf of the normal + distribution. + + `johnsonsb` takes :math:`a` and :math:`b` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _argcheck(self, a, b): + return (b > 0) & (a == a) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (-np.inf, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _pdf(self, x, a, b): + # johnsonsb.pdf(x, a, b) = b / (x*(1-x)) * phi(a + b * log(x/(1-x))) + trm = _norm_pdf(a + b*sc.logit(x)) + return b*1.0/(x*(1-x))*trm + + def _cdf(self, x, a, b): + return _norm_cdf(a + b*sc.logit(x)) + + def _ppf(self, q, a, b): + return sc.expit(1.0 / b * (_norm_ppf(q) - a)) + + def _sf(self, x, a, b): + return _norm_sf(a + b*sc.logit(x)) + + def _isf(self, q, a, b): + return sc.expit(1.0 / b * (_norm_isf(q) - a)) + + +johnsonsb = johnsonsb_gen(a=0.0, b=1.0, name='johnsonsb') + + +class johnsonsu_gen(rv_continuous): + r"""A Johnson SU continuous random variable. + + %(before_notes)s + + See Also + -------- + johnsonsb + + Notes + ----- + The probability density function for `johnsonsu` is: + + .. math:: + + f(x, a, b) = \frac{b}{\sqrt{x^2 + 1}} + \phi(a + b \log(x + \sqrt{x^2 + 1})) + + where :math:`x`, :math:`a`, and :math:`b` are real scalars; :math:`b > 0`. + :math:`\phi` is the pdf of the normal distribution. + + `johnsonsu` takes :math:`a` and :math:`b` as shape parameters. + + The first four central moments are calculated according to the formulas + in [1]_. + + %(after_notes)s + + References + ---------- + .. [1] Taylor Enterprises. "Johnson Family of Distributions". + https://variation.com/wp-content/distribution_analyzer_help/hs126.htm + + %(example)s + + """ + def _argcheck(self, a, b): + return (b > 0) & (a == a) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (-np.inf, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _pdf(self, x, a, b): + # johnsonsu.pdf(x, a, b) = b / sqrt(x**2 + 1) * + # phi(a + b * log(x + sqrt(x**2 + 1))) + x2 = x*x + trm = _norm_pdf(a + b * np.arcsinh(x)) + return b*1.0/np.sqrt(x2+1.0)*trm + + def _cdf(self, x, a, b): + return _norm_cdf(a + b * np.arcsinh(x)) + + def _ppf(self, q, a, b): + return np.sinh((_norm_ppf(q) - a) / b) + + def _sf(self, x, a, b): + return _norm_sf(a + b * np.arcsinh(x)) + + def _isf(self, x, a, b): + return np.sinh((_norm_isf(x) - a) / b) + + def _stats(self, a, b, moments='mv'): + # Naive implementation of first and second moment to address gh-18071. + # https://variation.com/wp-content/distribution_analyzer_help/hs126.htm + # Numerical improvements left to future enhancements. + mu, mu2, g1, g2 = None, None, None, None + + bn2 = b**-2. + expbn2 = np.exp(bn2) + a_b = a / b + + if 'm' in moments: + mu = -expbn2**0.5 * np.sinh(a_b) + if 'v' in moments: + mu2 = 0.5*sc.expm1(bn2)*(expbn2*np.cosh(2*a_b) + 1) + if 's' in moments: + t1 = expbn2**.5 * sc.expm1(bn2)**0.5 + t2 = 3*np.sinh(a_b) + t3 = expbn2 * (expbn2 + 2) * np.sinh(3*a_b) + denom = np.sqrt(2) * (1 + expbn2 * np.cosh(2*a_b))**(3/2) + g1 = -t1 * (t2 + t3) / denom + if 'k' in moments: + t1 = 3 + 6*expbn2 + t2 = 4*expbn2**2 * (expbn2 + 2) * np.cosh(2*a_b) + t3 = expbn2**2 * np.cosh(4*a_b) + t4 = -3 + 3*expbn2**2 + 2*expbn2**3 + expbn2**4 + denom = 2*(1 + expbn2*np.cosh(2*a_b))**2 + g2 = (t1 + t2 + t3*t4) / denom - 3 + return mu, mu2, g1, g2 + + +johnsonsu = johnsonsu_gen(name='johnsonsu') + + +class laplace_gen(rv_continuous): + r"""A Laplace continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `laplace` is + + .. math:: + + f(x) = \frac{1}{2} \exp(-|x|) + + for a real number :math:`x`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.laplace(0, 1, size=size) + + def _pdf(self, x): + # laplace.pdf(x) = 1/2 * exp(-abs(x)) + return 0.5*np.exp(-abs(x)) + + def _cdf(self, x): + with np.errstate(over='ignore'): + return np.where(x > 0, 1.0 - 0.5*np.exp(-x), 0.5*np.exp(x)) + + def _sf(self, x): + # By symmetry... + return self._cdf(-x) + + def _ppf(self, q): + return np.where(q > 0.5, -np.log(2*(1-q)), np.log(2*q)) + + def _isf(self, q): + # By symmetry... + return -self._ppf(q) + + def _stats(self): + return 0, 2, 0, 3 + + def _entropy(self): + return np.log(2)+1 + + @_call_super_mom + @replace_notes_in_docstring(rv_continuous, notes="""\ + This function uses explicit formulas for the maximum likelihood + estimation of the Laplace distribution parameters, so the keyword + arguments `loc`, `scale`, and `optimizer` are ignored.\n\n""") + def fit(self, data, *args, **kwds): + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + # Source: Statistical Distributions, 3rd Edition. Evans, Hastings, + # and Peacock (2000), Page 124 + + if floc is None: + floc = np.median(data) + + if fscale is None: + fscale = (np.sum(np.abs(data - floc))) / len(data) + + return floc, fscale + + +laplace = laplace_gen(name='laplace') + + +class laplace_asymmetric_gen(rv_continuous): + r"""An asymmetric Laplace continuous random variable. + + %(before_notes)s + + See Also + -------- + laplace : Laplace distribution + + Notes + ----- + The probability density function for `laplace_asymmetric` is + + .. math:: + + f(x, \kappa) &= \frac{1}{\kappa+\kappa^{-1}}\exp(-x\kappa),\quad x\ge0\\ + &= \frac{1}{\kappa+\kappa^{-1}}\exp(x/\kappa),\quad x<0\\ + + for :math:`-\infty < x < \infty`, :math:`\kappa > 0`. + + `laplace_asymmetric` takes ``kappa`` as a shape parameter for + :math:`\kappa`. For :math:`\kappa = 1`, it is identical to a + Laplace distribution. + + %(after_notes)s + + Note that the scale parameter of some references is the reciprocal of + SciPy's ``scale``. For example, :math:`\lambda = 1/2` in the + parameterization of [1]_ is equivalent to ``scale = 2`` with + `laplace_asymmetric`. + + References + ---------- + .. [1] "Asymmetric Laplace distribution", Wikipedia + https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution + + .. [2] Kozubowski TJ and Podgórski K. A Multivariate and + Asymmetric Generalization of Laplace Distribution, + Computational Statistics 15, 531--540 (2000). + :doi:`10.1007/PL00022717` + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("kappa", False, (0, np.inf), (False, False))] + + def _pdf(self, x, kappa): + return np.exp(self._logpdf(x, kappa)) + + def _logpdf(self, x, kappa): + kapinv = 1/kappa + lPx = x * np.where(x >= 0, -kappa, kapinv) + lPx -= np.log(kappa+kapinv) + return lPx + + def _cdf(self, x, kappa): + kapinv = 1/kappa + kappkapinv = kappa+kapinv + return np.where(x >= 0, + 1 - np.exp(-x*kappa)*(kapinv/kappkapinv), + np.exp(x*kapinv)*(kappa/kappkapinv)) + + def _sf(self, x, kappa): + kapinv = 1/kappa + kappkapinv = kappa+kapinv + return np.where(x >= 0, + np.exp(-x*kappa)*(kapinv/kappkapinv), + 1 - np.exp(x*kapinv)*(kappa/kappkapinv)) + + def _ppf(self, q, kappa): + kapinv = 1/kappa + kappkapinv = kappa+kapinv + return np.where(q >= kappa/kappkapinv, + -np.log((1 - q)*kappkapinv*kappa)*kapinv, + np.log(q*kappkapinv/kappa)*kappa) + + def _isf(self, q, kappa): + kapinv = 1/kappa + kappkapinv = kappa+kapinv + return np.where(q <= kapinv/kappkapinv, + -np.log(q*kappkapinv*kappa)*kapinv, + np.log((1 - q)*kappkapinv/kappa)*kappa) + + def _stats(self, kappa): + kapinv = 1/kappa + mn = kapinv - kappa + var = kapinv*kapinv + kappa*kappa + g1 = 2.0*(1-np.power(kappa, 6))/np.power(1+np.power(kappa, 4), 1.5) + g2 = 6.0*(1+np.power(kappa, 8))/np.power(1+np.power(kappa, 4), 2) + return mn, var, g1, g2 + + def _entropy(self, kappa): + return 1 + np.log(kappa+1/kappa) + + +laplace_asymmetric = laplace_asymmetric_gen(name='laplace_asymmetric') + + +def _check_fit_input_parameters(dist, data, args, kwds): + if not isinstance(data, CensoredData): + data = np.asarray(data) + + floc = kwds.get('floc', None) + fscale = kwds.get('fscale', None) + + num_shapes = len(dist.shapes.split(",")) if dist.shapes else 0 + fshape_keys = [] + fshapes = [] + + # user has many options for fixing the shape, so here we standardize it + # into 'f' + the number of the shape. + # Adapted from `_reduce_func` in `_distn_infrastructure.py`: + if dist.shapes: + shapes = dist.shapes.replace(',', ' ').split() + for j, s in enumerate(shapes): + key = 'f' + str(j) + names = [key, 'f' + s, 'fix_' + s] + val = _get_fixed_fit_value(kwds, names) + fshape_keys.append(key) + fshapes.append(val) + if val is not None: + kwds[key] = val + + # determine if there are any unknown arguments in kwds + known_keys = {'loc', 'scale', 'optimizer', 'method', + 'floc', 'fscale', *fshape_keys} + unknown_keys = set(kwds).difference(known_keys) + if unknown_keys: + raise TypeError(f"Unknown keyword arguments: {unknown_keys}.") + + if len(args) > num_shapes: + raise TypeError("Too many positional arguments.") + + if None not in {floc, fscale, *fshapes}: + # This check is for consistency with `rv_continuous.fit`. + # Without this check, this function would just return the + # parameters that were given. + raise RuntimeError("All parameters fixed. There is nothing to " + "optimize.") + + uncensored = data._uncensor() if isinstance(data, CensoredData) else data + if not np.isfinite(uncensored).all(): + raise ValueError("The data contains non-finite values.") + + return (data, *fshapes, floc, fscale) + + +class levy_gen(rv_continuous): + r"""A Levy continuous random variable. + + %(before_notes)s + + See Also + -------- + levy_stable, levy_l + + Notes + ----- + The probability density function for `levy` is: + + .. math:: + + f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp\left(-\frac{1}{2x}\right) + + for :math:`x > 0`. + + This is the same as the Levy-stable distribution with :math:`a=1/2` and + :math:`b=1`. + + %(after_notes)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import levy + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + + Calculate the first four moments: + + >>> mean, var, skew, kurt = levy.stats(moments='mvsk') + + Display the probability density function (``pdf``): + + >>> # `levy` is very heavy-tailed. + >>> # To show a nice plot, let's cut off the upper 40 percent. + >>> a, b = levy.ppf(0), levy.ppf(0.6) + >>> x = np.linspace(a, b, 100) + >>> ax.plot(x, levy.pdf(x), + ... 'r-', lw=5, alpha=0.6, label='levy pdf') + + Alternatively, the distribution object can be called (as a function) + to fix the shape, location and scale parameters. This returns a "frozen" + RV object holding the given parameters fixed. + + Freeze the distribution and display the frozen ``pdf``: + + >>> rv = levy() + >>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + + Check accuracy of ``cdf`` and ``ppf``: + + >>> vals = levy.ppf([0.001, 0.5, 0.999]) + >>> np.allclose([0.001, 0.5, 0.999], levy.cdf(vals)) + True + + Generate random numbers: + + >>> r = levy.rvs(size=1000) + + And compare the histogram: + + >>> # manual binning to ignore the tail + >>> bins = np.concatenate((np.linspace(a, b, 20), [np.max(r)])) + >>> ax.hist(r, bins=bins, density=True, histtype='stepfilled', alpha=0.2) + >>> ax.set_xlim([x[0], x[-1]]) + >>> ax.legend(loc='best', frameon=False) + >>> plt.show() + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [] + + def _pdf(self, x): + # levy.pdf(x) = 1 / (x * sqrt(2*pi*x)) * exp(-1/(2*x)) + return 1 / np.sqrt(2*np.pi*x) / x * np.exp(-1/(2*x)) + + def _cdf(self, x): + # Equivalent to 2*norm.sf(np.sqrt(1/x)) + return sc.erfc(np.sqrt(0.5 / x)) + + def _sf(self, x): + return sc.erf(np.sqrt(0.5 / x)) + + def _ppf(self, q): + # Equivalent to 1.0/(norm.isf(q/2)**2) or 0.5/(erfcinv(q)**2) + val = _norm_isf(q/2) + return 1.0 / (val * val) + + def _isf(self, p): + return 1/(2*sc.erfinv(p)**2) + + def _stats(self): + return np.inf, np.inf, np.nan, np.nan + + +levy = levy_gen(a=0.0, name="levy") + + +class levy_l_gen(rv_continuous): + r"""A left-skewed Levy continuous random variable. + + %(before_notes)s + + See Also + -------- + levy, levy_stable + + Notes + ----- + The probability density function for `levy_l` is: + + .. math:: + f(x) = \frac{1}{|x| \sqrt{2\pi |x|}} \exp{ \left(-\frac{1}{2|x|} \right)} + + for :math:`x < 0`. + + This is the same as the Levy-stable distribution with :math:`a=1/2` and + :math:`b=-1`. + + %(after_notes)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import levy_l + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + + Calculate the first four moments: + + >>> mean, var, skew, kurt = levy_l.stats(moments='mvsk') + + Display the probability density function (``pdf``): + + >>> # `levy_l` is very heavy-tailed. + >>> # To show a nice plot, let's cut off the lower 40 percent. + >>> a, b = levy_l.ppf(0.4), levy_l.ppf(1) + >>> x = np.linspace(a, b, 100) + >>> ax.plot(x, levy_l.pdf(x), + ... 'r-', lw=5, alpha=0.6, label='levy_l pdf') + + Alternatively, the distribution object can be called (as a function) + to fix the shape, location and scale parameters. This returns a "frozen" + RV object holding the given parameters fixed. + + Freeze the distribution and display the frozen ``pdf``: + + >>> rv = levy_l() + >>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + + Check accuracy of ``cdf`` and ``ppf``: + + >>> vals = levy_l.ppf([0.001, 0.5, 0.999]) + >>> np.allclose([0.001, 0.5, 0.999], levy_l.cdf(vals)) + True + + Generate random numbers: + + >>> r = levy_l.rvs(size=1000) + + And compare the histogram: + + >>> # manual binning to ignore the tail + >>> bins = np.concatenate(([np.min(r)], np.linspace(a, b, 20))) + >>> ax.hist(r, bins=bins, density=True, histtype='stepfilled', alpha=0.2) + >>> ax.set_xlim([x[0], x[-1]]) + >>> ax.legend(loc='best', frameon=False) + >>> plt.show() + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [] + + def _pdf(self, x): + # levy_l.pdf(x) = 1 / (abs(x) * sqrt(2*pi*abs(x))) * exp(-1/(2*abs(x))) + ax = abs(x) + return 1/np.sqrt(2*np.pi*ax)/ax*np.exp(-1/(2*ax)) + + def _cdf(self, x): + ax = abs(x) + return 2 * _norm_cdf(1 / np.sqrt(ax)) - 1 + + def _sf(self, x): + ax = abs(x) + return 2 * _norm_sf(1 / np.sqrt(ax)) + + def _ppf(self, q): + val = _norm_ppf((q + 1.0) / 2) + return -1.0 / (val * val) + + def _isf(self, p): + return -1/_norm_isf(p/2)**2 + + def _stats(self): + return np.inf, np.inf, np.nan, np.nan + + +levy_l = levy_l_gen(b=0.0, name="levy_l") + + +class logistic_gen(rv_continuous): + r"""A logistic (or Sech-squared) continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `logistic` is: + + .. math:: + + f(x) = \frac{\exp(-x)} + {(1+\exp(-x))^2} + + `logistic` is a special case of `genlogistic` with ``c=1``. + + Remark that the survival function (``logistic.sf``) is equal to the + Fermi-Dirac distribution describing fermionic statistics. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.logistic(size=size) + + def _pdf(self, x): + # logistic.pdf(x) = exp(-x) / (1+exp(-x))**2 + return np.exp(self._logpdf(x)) + + def _logpdf(self, x): + y = -np.abs(x) + return y - 2. * sc.log1p(np.exp(y)) + + def _cdf(self, x): + return sc.expit(x) + + def _logcdf(self, x): + return sc.log_expit(x) + + def _ppf(self, q): + return sc.logit(q) + + def _sf(self, x): + return sc.expit(-x) + + def _logsf(self, x): + return sc.log_expit(-x) + + def _isf(self, q): + return -sc.logit(q) + + def _stats(self): + return 0, np.pi*np.pi/3.0, 0, 6.0/5.0 + + def _entropy(self): + # https://en.wikipedia.org/wiki/Logistic_distribution + return 2.0 + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + n = len(data) + + # rv_continuous provided guesses + loc, scale = self._fitstart(data) + # these are trumped by user-provided guesses + loc, scale = kwds.get('loc', loc), kwds.get('scale', scale) + + # the maximum likelihood estimators `a` and `b` of the location and + # scale parameters are roots of the two equations described in `func`. + # Source: Statistical Distributions, 3rd Edition. Evans, Hastings, and + # Peacock (2000), Page 130 + + def dl_dloc(loc, scale=fscale): + c = (data - loc) / scale + return np.sum(sc.expit(c)) - n/2 + + def dl_dscale(scale, loc=floc): + c = (data - loc) / scale + return np.sum(c*np.tanh(c/2)) - n + + def func(params): + loc, scale = params + return dl_dloc(loc, scale), dl_dscale(scale, loc) + + if fscale is not None and floc is None: + res = optimize.root(dl_dloc, (loc,)) + loc = res.x[0] + scale = fscale + elif floc is not None and fscale is None: + res = optimize.root(dl_dscale, (scale,)) + scale = res.x[0] + loc = floc + else: + res = optimize.root(func, (loc, scale)) + loc, scale = res.x + + # Note: gh-18176 reported data for which the reported MLE had + # `scale < 0`. To fix the bug, we return abs(scale). This is OK because + # `dl_dscale` and `dl_dloc` are even and odd functions of `scale`, + # respectively, so if `-scale` is a solution, so is `scale`. + scale = abs(scale) + return ((loc, scale) if res.success + else super().fit(data, *args, **kwds)) + + +logistic = logistic_gen(name='logistic') + + +class loggamma_gen(rv_continuous): + r"""A log gamma continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `loggamma` is: + + .. math:: + + f(x, c) = \frac{\exp(c x - \exp(x))} + {\Gamma(c)} + + for all :math:`x, c > 0`. Here, :math:`\Gamma` is the + gamma function (`scipy.special.gamma`). + + `loggamma` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _rvs(self, c, size=None, random_state=None): + # Use the property of the gamma distribution Gamma(c) + # Gamma(c) ~ Gamma(c + 1)*U**(1/c), + # where U is uniform on [0, 1]. (See, e.g., + # G. Marsaglia and W.W. Tsang, "A simple method for generating gamma + # variables", https://doi.org/10.1145/358407.358414) + # So + # log(Gamma(c)) ~ log(Gamma(c + 1)) + log(U)/c + # Generating a sample with this formulation is a bit slower + # than the more obvious log(Gamma(c)), but it avoids loss + # of precision when c << 1. + return (np.log(random_state.gamma(c + 1, size=size)) + + np.log(random_state.uniform(size=size))/c) + + def _pdf(self, x, c): + # loggamma.pdf(x, c) = exp(c*x-exp(x)) / gamma(c) + return np.exp(c*x-np.exp(x)-sc.gammaln(c)) + + def _logpdf(self, x, c): + return c*x - np.exp(x) - sc.gammaln(c) + + def _cdf(self, x, c): + # This function is gammainc(c, exp(x)), where gammainc(c, z) is + # the regularized incomplete gamma function. + # The first term in a series expansion of gamminc(c, z) is + # z**c/Gamma(c+1); see 6.5.29 of Abramowitz & Stegun (and refer + # back to 6.5.1, 6.5.2 and 6.5.4 for the relevant notation). + # This can also be found in the wikipedia article + # https://en.wikipedia.org/wiki/Incomplete_gamma_function. + # Here we use that formula when x is sufficiently negative that + # exp(x) will result in subnormal numbers and lose precision. + # We evaluate the log of the expression first to allow the possible + # cancellation of the terms in the division, and then exponentiate. + # That is, + # exp(x)**c/Gamma(c+1) = exp(log(exp(x)**c/Gamma(c+1))) + # = exp(c*x - gammaln(c+1)) + return _lazywhere(x < _LOGXMIN, (x, c), + lambda x, c: np.exp(c*x - sc.gammaln(c+1)), + f2=lambda x, c: sc.gammainc(c, np.exp(x))) + + def _ppf(self, q, c): + # The expression used when g < _XMIN inverts the one term expansion + # given in the comments of _cdf(). + g = sc.gammaincinv(c, q) + return _lazywhere(g < _XMIN, (g, q, c), + lambda g, q, c: (np.log(q) + sc.gammaln(c+1))/c, + f2=lambda g, q, c: np.log(g)) + + def _sf(self, x, c): + # See the comments for _cdf() for how x < _LOGXMIN is handled. + return _lazywhere(x < _LOGXMIN, (x, c), + lambda x, c: -np.expm1(c*x - sc.gammaln(c+1)), + f2=lambda x, c: sc.gammaincc(c, np.exp(x))) + + def _isf(self, q, c): + # The expression used when g < _XMIN inverts the complement of + # the one term expansion given in the comments of _cdf(). + g = sc.gammainccinv(c, q) + return _lazywhere(g < _XMIN, (g, q, c), + lambda g, q, c: (np.log1p(-q) + sc.gammaln(c+1))/c, + f2=lambda g, q, c: np.log(g)) + + def _stats(self, c): + # See, for example, "A Statistical Study of Log-Gamma Distribution", by + # Ping Shing Chan (thesis, McMaster University, 1993). + mean = sc.digamma(c) + var = sc.polygamma(1, c) + skewness = sc.polygamma(2, c) / np.power(var, 1.5) + excess_kurtosis = sc.polygamma(3, c) / (var*var) + return mean, var, skewness, excess_kurtosis + + def _entropy(self, c): + def regular(c): + h = sc.gammaln(c) - c * sc.digamma(c) + c + return h + + def asymptotic(c): + # using asymptotic expansions for gammaln and psi (see gh-18093) + term = -0.5*np.log(c) + c**-1./6 - c**-3./90 + c**-5./210 + h = norm._entropy() + term + return h + + h = _lazywhere(c >= 45, (c, ), f=asymptotic, f2=regular) + return h + + +loggamma = loggamma_gen(name='loggamma') + + +class loglaplace_gen(rv_continuous): + r"""A log-Laplace continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `loglaplace` is: + + .. math:: + + f(x, c) = \begin{cases}\frac{c}{2} x^{ c-1} &\text{for } 0 < x < 1\\ + \frac{c}{2} x^{-c-1} &\text{for } x \ge 1 + \end{cases} + + for :math:`c > 0`. + + `loglaplace` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + Suppose a random variable ``X`` follows the Laplace distribution with + location ``a`` and scale ``b``. Then ``Y = exp(X)`` follows the + log-Laplace distribution with ``c = 1 / b`` and ``scale = exp(a)``. + + References + ---------- + T.J. Kozubowski and K. Podgorski, "A log-Laplace growth rate model", + The Mathematical Scientist, vol. 28, pp. 49-60, 2003. + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # loglaplace.pdf(x, c) = c / 2 * x**(c-1), for 0 < x < 1 + # = c / 2 * x**(-c-1), for x >= 1 + cd2 = c/2.0 + c = np.where(x < 1, c, -c) + return cd2*x**(c-1) + + def _cdf(self, x, c): + return np.where(x < 1, 0.5*x**c, 1-0.5*x**(-c)) + + def _sf(self, x, c): + return np.where(x < 1, 1 - 0.5*x**c, 0.5*x**(-c)) + + def _ppf(self, q, c): + return np.where(q < 0.5, (2.0*q)**(1.0/c), (2*(1.0-q))**(-1.0/c)) + + def _isf(self, q, c): + return np.where(q > 0.5, (2.0*(1.0 - q))**(1.0/c), (2*q)**(-1.0/c)) + + def _munp(self, n, c): + with np.errstate(divide='ignore'): + c2, n2 = c**2, n**2 + return np.where(n2 < c2, c2 / (c2 - n2), np.inf) + + def _entropy(self, c): + return np.log(2.0/c) + 1.0 + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + data, fc, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + # Specialize MLE only when location is known. + if floc is None: + return super(type(self), self).fit(data, *args, **kwds) + + # Raise an error if any observation has zero likelihood. + if np.any(data <= floc): + raise FitDataError("loglaplace", lower=floc, upper=np.inf) + + # Remove location from data. + if floc != 0: + data = data - floc + + # When location is zero, the log-Laplace distribution is related to + # the Laplace distribution in that if X ~ Laplace(loc=a, scale=b), + # then Y = exp(X) ~ LogLaplace(c=1/b, loc=0, scale=exp(a)). It can + # be shown that the MLE for Y is the same as the MLE for X = ln(Y). + # Therefore, we reuse the formulas from laplace.fit() and transform + # the result back into log-laplace's parameter space. + a, b = laplace.fit(np.log(data), + floc=np.log(fscale) if fscale is not None else None, + fscale=1/fc if fc is not None else None, + method='mle') + loc = floc + scale = np.exp(a) if fscale is None else fscale + c = 1 / b if fc is None else fc + return c, loc, scale + +loglaplace = loglaplace_gen(a=0.0, name='loglaplace') + + +def _lognorm_logpdf(x, s): + return _lazywhere(x != 0, (x, s), + lambda x, s: (-np.log(x)**2 / (2 * s**2) + - np.log(s * x * np.sqrt(2 * np.pi))), + -np.inf) + + +class lognorm_gen(rv_continuous): + r"""A lognormal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `lognorm` is: + + .. math:: + + f(x, s) = \frac{1}{s x \sqrt{2\pi}} + \exp\left(-\frac{\log^2(x)}{2s^2}\right) + + for :math:`x > 0`, :math:`s > 0`. + + `lognorm` takes ``s`` as a shape parameter for :math:`s`. + + %(after_notes)s + + Suppose a normally distributed random variable ``X`` has mean ``mu`` and + standard deviation ``sigma``. Then ``Y = exp(X)`` is lognormally + distributed with ``s = sigma`` and ``scale = exp(mu)``. + + %(example)s + + The logarithm of a log-normally distributed random variable is + normally distributed: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy import stats + >>> fig, ax = plt.subplots(1, 1) + >>> mu, sigma = 2, 0.5 + >>> X = stats.norm(loc=mu, scale=sigma) + >>> Y = stats.lognorm(s=sigma, scale=np.exp(mu)) + >>> x = np.linspace(*X.interval(0.999)) + >>> y = Y.rvs(size=10000) + >>> ax.plot(x, X.pdf(x), label='X (pdf)') + >>> ax.hist(np.log(y), density=True, bins=x, label='log(Y) (histogram)') + >>> ax.legend() + >>> plt.show() + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [_ShapeInfo("s", False, (0, np.inf), (False, False))] + + def _rvs(self, s, size=None, random_state=None): + return np.exp(s * random_state.standard_normal(size)) + + def _pdf(self, x, s): + # lognorm.pdf(x, s) = 1 / (s*x*sqrt(2*pi)) * exp(-1/2*(log(x)/s)**2) + return np.exp(self._logpdf(x, s)) + + def _logpdf(self, x, s): + return _lognorm_logpdf(x, s) + + def _cdf(self, x, s): + return _norm_cdf(np.log(x) / s) + + def _logcdf(self, x, s): + return _norm_logcdf(np.log(x) / s) + + def _ppf(self, q, s): + return np.exp(s * _norm_ppf(q)) + + def _sf(self, x, s): + return _norm_sf(np.log(x) / s) + + def _logsf(self, x, s): + return _norm_logsf(np.log(x) / s) + + def _isf(self, q, s): + return np.exp(s * _norm_isf(q)) + + def _stats(self, s): + p = np.exp(s*s) + mu = np.sqrt(p) + mu2 = p*(p-1) + g1 = np.sqrt(p-1)*(2+p) + g2 = np.polyval([1, 2, 3, 0, -6.0], p) + return mu, mu2, g1, g2 + + def _entropy(self, s): + return 0.5 * (1 + np.log(2*np.pi) + 2 * np.log(s)) + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + When `method='MLE'` and + the location parameter is fixed by using the `floc` argument, + this function uses explicit formulas for the maximum likelihood + estimation of the log-normal shape and scale parameters, so the + `optimizer`, `loc` and `scale` keyword arguments are ignored. + If the location is free, a likelihood maximum is found by + setting its partial derivative wrt to location to 0, and + solving by substituting the analytical expressions of shape + and scale (or provided parameters). + See, e.g., equation 3.1 in + A. Clifford Cohen & Betty Jones Whitten (1980) + Estimation in the Three-Parameter Lognormal Distribution, + Journal of the American Statistical Association, 75:370, 399-404 + https://doi.org/10.2307/2287466 + \n\n""") + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + parameters = _check_fit_input_parameters(self, data, args, kwds) + data, fshape, floc, fscale = parameters + data_min = np.min(data) + + def get_shape_scale(loc): + # Calculate maximum likelihood scale and shape with analytical + # formulas unless provided by the user + if fshape is None or fscale is None: + lndata = np.log(data - loc) + scale = fscale or np.exp(lndata.mean()) + shape = fshape or np.sqrt(np.mean((lndata - np.log(scale))**2)) + return shape, scale + + def dL_dLoc(loc): + # Derivative of (positive) LL w.r.t. loc + shape, scale = get_shape_scale(loc) + shifted = data - loc + return np.sum((1 + np.log(shifted/scale)/shape**2)/shifted) + + def ll(loc): + # (Positive) log-likelihood + shape, scale = get_shape_scale(loc) + return -self.nnlf((shape, loc, scale), data) + + if floc is None: + # The location must be less than the minimum of the data. + # Back off a bit to avoid numerical issues. + spacing = np.spacing(data_min) + rbrack = data_min - spacing + + # Find the right end of the bracket by successive doubling of the + # distance to data_min. We're interested in a maximum LL, so the + # slope dL_dLoc_rbrack should be negative at the right end. + # optimization for later: share shape, scale + dL_dLoc_rbrack = dL_dLoc(rbrack) + ll_rbrack = ll(rbrack) + delta = 2 * spacing # 2 * (data_min - rbrack) + while dL_dLoc_rbrack >= -1e-6: + rbrack = data_min - delta + dL_dLoc_rbrack = dL_dLoc(rbrack) + delta *= 2 + + if not np.isfinite(rbrack) or not np.isfinite(dL_dLoc_rbrack): + # If we never find a negative slope, either we missed it or the + # slope is always positive. It's usually the latter, + # which means + # loc = data_min - spacing + # But sometimes when shape and/or scale are fixed there are + # other issues, so be cautious. + return super().fit(data, *args, **kwds) + + # Now find the left end of the bracket. Guess is `rbrack-1` + # unless that is too small of a difference to resolve. Double + # the size of the interval until the left end is found. + lbrack = np.minimum(np.nextafter(rbrack, -np.inf), rbrack-1) + dL_dLoc_lbrack = dL_dLoc(lbrack) + delta = 2 * (rbrack - lbrack) + while (np.isfinite(lbrack) and np.isfinite(dL_dLoc_lbrack) + and np.sign(dL_dLoc_lbrack) == np.sign(dL_dLoc_rbrack)): + lbrack = rbrack - delta + dL_dLoc_lbrack = dL_dLoc(lbrack) + delta *= 2 + + # I don't recall observing this, but just in case... + if not np.isfinite(lbrack) or not np.isfinite(dL_dLoc_lbrack): + return super().fit(data, *args, **kwds) + + # If we have a valid bracket, find the root + res = root_scalar(dL_dLoc, bracket=(lbrack, rbrack)) + if not res.converged: + return super().fit(data, *args, **kwds) + + # If the slope was positive near the minimum of the data, + # the maximum LL could be there instead of at the root. Compare + # the LL of the two points to decide. + ll_root = ll(res.root) + loc = res.root if ll_root > ll_rbrack else data_min-spacing + + else: + if floc >= data_min: + raise FitDataError("lognorm", lower=0., upper=np.inf) + loc = floc + + shape, scale = get_shape_scale(loc) + if not (self._argcheck(shape) and scale > 0): + return super().fit(data, *args, **kwds) + return shape, loc, scale + + +lognorm = lognorm_gen(a=0.0, name='lognorm') + + +class gibrat_gen(rv_continuous): + r"""A Gibrat continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `gibrat` is: + + .. math:: + + f(x) = \frac{1}{x \sqrt{2\pi}} \exp(-\frac{1}{2} (\log(x))^2) + + `gibrat` is a special case of `lognorm` with ``s=1``. + + %(after_notes)s + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return np.exp(random_state.standard_normal(size)) + + def _pdf(self, x): + # gibrat.pdf(x) = 1/(x*sqrt(2*pi)) * exp(-1/2*(log(x))**2) + return np.exp(self._logpdf(x)) + + def _logpdf(self, x): + return _lognorm_logpdf(x, 1.0) + + def _cdf(self, x): + return _norm_cdf(np.log(x)) + + def _ppf(self, q): + return np.exp(_norm_ppf(q)) + + def _sf(self, x): + return _norm_sf(np.log(x)) + + def _isf(self, p): + return np.exp(_norm_isf(p)) + + def _stats(self): + p = np.e + mu = np.sqrt(p) + mu2 = p * (p - 1) + g1 = np.sqrt(p - 1) * (2 + p) + g2 = np.polyval([1, 2, 3, 0, -6.0], p) + return mu, mu2, g1, g2 + + def _entropy(self): + return 0.5 * np.log(2 * np.pi) + 0.5 + + +gibrat = gibrat_gen(a=0.0, name='gibrat') + + +class maxwell_gen(rv_continuous): + r"""A Maxwell continuous random variable. + + %(before_notes)s + + Notes + ----- + A special case of a `chi` distribution, with ``df=3``, ``loc=0.0``, + and given ``scale = a``, where ``a`` is the parameter used in the + Mathworld description [1]_. + + The probability density function for `maxwell` is: + + .. math:: + + f(x) = \sqrt{2/\pi}x^2 \exp(-x^2/2) + + for :math:`x >= 0`. + + %(after_notes)s + + References + ---------- + .. [1] http://mathworld.wolfram.com/MaxwellDistribution.html + + %(example)s + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return chi.rvs(3.0, size=size, random_state=random_state) + + def _pdf(self, x): + # maxwell.pdf(x) = sqrt(2/pi)x**2 * exp(-x**2/2) + return _SQRT_2_OVER_PI*x*x*np.exp(-x*x/2.0) + + def _logpdf(self, x): + # Allow x=0 without 'divide by zero' warnings + with np.errstate(divide='ignore'): + return _LOG_SQRT_2_OVER_PI + 2*np.log(x) - 0.5*x*x + + def _cdf(self, x): + return sc.gammainc(1.5, x*x/2.0) + + def _ppf(self, q): + return np.sqrt(2*sc.gammaincinv(1.5, q)) + + def _sf(self, x): + return sc.gammaincc(1.5, x*x/2.0) + + def _isf(self, q): + return np.sqrt(2*sc.gammainccinv(1.5, q)) + + def _stats(self): + val = 3*np.pi-8 + return (2*np.sqrt(2.0/np.pi), + 3-8/np.pi, + np.sqrt(2)*(32-10*np.pi)/val**1.5, + (-12*np.pi*np.pi + 160*np.pi - 384) / val**2.0) + + def _entropy(self): + return _EULER + 0.5*np.log(2*np.pi)-0.5 + + +maxwell = maxwell_gen(a=0.0, name='maxwell') + + +class mielke_gen(rv_continuous): + r"""A Mielke Beta-Kappa / Dagum continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `mielke` is: + + .. math:: + + f(x, k, s) = \frac{k x^{k-1}}{(1+x^s)^{1+k/s}} + + for :math:`x > 0` and :math:`k, s > 0`. The distribution is sometimes + called Dagum distribution ([2]_). It was already defined in [3]_, called + a Burr Type III distribution (`burr` with parameters ``c=s`` and + ``d=k/s``). + + `mielke` takes ``k`` and ``s`` as shape parameters. + + %(after_notes)s + + References + ---------- + .. [1] Mielke, P.W., 1973 "Another Family of Distributions for Describing + and Analyzing Precipitation Data." J. Appl. Meteor., 12, 275-280 + .. [2] Dagum, C., 1977 "A new model for personal income distribution." + Economie Appliquee, 33, 327-367. + .. [3] Burr, I. W. "Cumulative frequency functions", Annals of + Mathematical Statistics, 13(2), pp 215-232 (1942). + + %(example)s + + """ + def _shape_info(self): + ik = _ShapeInfo("k", False, (0, np.inf), (False, False)) + i_s = _ShapeInfo("s", False, (0, np.inf), (False, False)) + return [ik, i_s] + + def _pdf(self, x, k, s): + return k*x**(k-1.0) / (1.0+x**s)**(1.0+k*1.0/s) + + def _logpdf(self, x, k, s): + # Allow x=0 without 'divide by zero' warnings. + with np.errstate(divide='ignore'): + return np.log(k) + np.log(x)*(k - 1) - np.log1p(x**s)*(1 + k/s) + + def _cdf(self, x, k, s): + return x**k / (1.0+x**s)**(k*1.0/s) + + def _ppf(self, q, k, s): + qsk = pow(q, s*1.0/k) + return pow(qsk/(1.0-qsk), 1.0/s) + + def _munp(self, n, k, s): + def nth_moment(n, k, s): + # n-th moment is defined for -k < n < s + return sc.gamma((k+n)/s)*sc.gamma(1-n/s)/sc.gamma(k/s) + + return _lazywhere(n < s, (n, k, s), nth_moment, np.inf) + + +mielke = mielke_gen(a=0.0, name='mielke') + + +class kappa4_gen(rv_continuous): + r"""Kappa 4 parameter distribution. + + %(before_notes)s + + Notes + ----- + The probability density function for kappa4 is: + + .. math:: + + f(x, h, k) = (1 - k x)^{1/k - 1} (1 - h (1 - k x)^{1/k})^{1/h-1} + + if :math:`h` and :math:`k` are not equal to 0. + + If :math:`h` or :math:`k` are zero then the pdf can be simplified: + + h = 0 and k != 0:: + + kappa4.pdf(x, h, k) = (1.0 - k*x)**(1.0/k - 1.0)* + exp(-(1.0 - k*x)**(1.0/k)) + + h != 0 and k = 0:: + + kappa4.pdf(x, h, k) = exp(-x)*(1.0 - h*exp(-x))**(1.0/h - 1.0) + + h = 0 and k = 0:: + + kappa4.pdf(x, h, k) = exp(-x)*exp(-exp(-x)) + + kappa4 takes :math:`h` and :math:`k` as shape parameters. + + The kappa4 distribution returns other distributions when certain + :math:`h` and :math:`k` values are used. + + +------+-------------+----------------+------------------+ + | h | k=0.0 | k=1.0 | -inf<=k<=inf | + +======+=============+================+==================+ + | -1.0 | Logistic | | Generalized | + | | | | Logistic(1) | + | | | | | + | | logistic(x) | | | + +------+-------------+----------------+------------------+ + | 0.0 | Gumbel | Reverse | Generalized | + | | | Exponential(2) | Extreme Value | + | | | | | + | | gumbel_r(x) | | genextreme(x, k) | + +------+-------------+----------------+------------------+ + | 1.0 | Exponential | Uniform | Generalized | + | | | | Pareto | + | | | | | + | | expon(x) | uniform(x) | genpareto(x, -k) | + +------+-------------+----------------+------------------+ + + (1) There are at least five generalized logistic distributions. + Four are described here: + https://en.wikipedia.org/wiki/Generalized_logistic_distribution + The "fifth" one is the one kappa4 should match which currently + isn't implemented in scipy: + https://en.wikipedia.org/wiki/Talk:Generalized_logistic_distribution + https://www.mathwave.com/help/easyfit/html/analyses/distributions/gen_logistic.html + (2) This distribution is currently not in scipy. + + References + ---------- + J.C. Finney, "Optimization of a Skewed Logistic Distribution With Respect + to the Kolmogorov-Smirnov Test", A Dissertation Submitted to the Graduate + Faculty of the Louisiana State University and Agricultural and Mechanical + College, (August, 2004), + https://digitalcommons.lsu.edu/gradschool_dissertations/3672 + + J.R.M. Hosking, "The four-parameter kappa distribution". IBM J. Res. + Develop. 38 (3), 25 1-258 (1994). + + B. Kumphon, A. Kaew-Man, P. Seenoi, "A Rainfall Distribution for the Lampao + Site in the Chi River Basin, Thailand", Journal of Water Resource and + Protection, vol. 4, 866-869, (2012). + :doi:`10.4236/jwarp.2012.410101` + + C. Winchester, "On Estimation of the Four-Parameter Kappa Distribution", A + Thesis Submitted to Dalhousie University, Halifax, Nova Scotia, (March + 2000). + http://www.nlc-bnc.ca/obj/s4/f2/dsk2/ftp01/MQ57336.pdf + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, h, k): + shape = np.broadcast_arrays(h, k)[0].shape + return np.full(shape, fill_value=True) + + def _shape_info(self): + ih = _ShapeInfo("h", False, (-np.inf, np.inf), (False, False)) + ik = _ShapeInfo("k", False, (-np.inf, np.inf), (False, False)) + return [ih, ik] + + def _get_support(self, h, k): + condlist = [np.logical_and(h > 0, k > 0), + np.logical_and(h > 0, k == 0), + np.logical_and(h > 0, k < 0), + np.logical_and(h <= 0, k > 0), + np.logical_and(h <= 0, k == 0), + np.logical_and(h <= 0, k < 0)] + + def f0(h, k): + return (1.0 - np.float_power(h, -k))/k + + def f1(h, k): + return np.log(h) + + def f3(h, k): + a = np.empty(np.shape(h)) + a[:] = -np.inf + return a + + def f5(h, k): + return 1.0/k + + _a = _lazyselect(condlist, + [f0, f1, f0, f3, f3, f5], + [h, k], + default=np.nan) + + def f0(h, k): + return 1.0/k + + def f1(h, k): + a = np.empty(np.shape(h)) + a[:] = np.inf + return a + + _b = _lazyselect(condlist, + [f0, f1, f1, f0, f1, f1], + [h, k], + default=np.nan) + return _a, _b + + def _pdf(self, x, h, k): + # kappa4.pdf(x, h, k) = (1.0 - k*x)**(1.0/k - 1.0)* + # (1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h-1) + return np.exp(self._logpdf(x, h, k)) + + def _logpdf(self, x, h, k): + condlist = [np.logical_and(h != 0, k != 0), + np.logical_and(h == 0, k != 0), + np.logical_and(h != 0, k == 0), + np.logical_and(h == 0, k == 0)] + + def f0(x, h, k): + '''pdf = (1.0 - k*x)**(1.0/k - 1.0)*( + 1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h-1.0) + logpdf = ... + ''' + return (sc.xlog1py(1.0/k - 1.0, -k*x) + + sc.xlog1py(1.0/h - 1.0, -h*(1.0 - k*x)**(1.0/k))) + + def f1(x, h, k): + '''pdf = (1.0 - k*x)**(1.0/k - 1.0)*np.exp(-( + 1.0 - k*x)**(1.0/k)) + logpdf = ... + ''' + return sc.xlog1py(1.0/k - 1.0, -k*x) - (1.0 - k*x)**(1.0/k) + + def f2(x, h, k): + '''pdf = np.exp(-x)*(1.0 - h*np.exp(-x))**(1.0/h - 1.0) + logpdf = ... + ''' + return -x + sc.xlog1py(1.0/h - 1.0, -h*np.exp(-x)) + + def f3(x, h, k): + '''pdf = np.exp(-x-np.exp(-x)) + logpdf = ... + ''' + return -x - np.exp(-x) + + return _lazyselect(condlist, + [f0, f1, f2, f3], + [x, h, k], + default=np.nan) + + def _cdf(self, x, h, k): + return np.exp(self._logcdf(x, h, k)) + + def _logcdf(self, x, h, k): + condlist = [np.logical_and(h != 0, k != 0), + np.logical_and(h == 0, k != 0), + np.logical_and(h != 0, k == 0), + np.logical_and(h == 0, k == 0)] + + def f0(x, h, k): + '''cdf = (1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h) + logcdf = ... + ''' + return (1.0/h)*sc.log1p(-h*(1.0 - k*x)**(1.0/k)) + + def f1(x, h, k): + '''cdf = np.exp(-(1.0 - k*x)**(1.0/k)) + logcdf = ... + ''' + return -(1.0 - k*x)**(1.0/k) + + def f2(x, h, k): + '''cdf = (1.0 - h*np.exp(-x))**(1.0/h) + logcdf = ... + ''' + return (1.0/h)*sc.log1p(-h*np.exp(-x)) + + def f3(x, h, k): + '''cdf = np.exp(-np.exp(-x)) + logcdf = ... + ''' + return -np.exp(-x) + + return _lazyselect(condlist, + [f0, f1, f2, f3], + [x, h, k], + default=np.nan) + + def _ppf(self, q, h, k): + condlist = [np.logical_and(h != 0, k != 0), + np.logical_and(h == 0, k != 0), + np.logical_and(h != 0, k == 0), + np.logical_and(h == 0, k == 0)] + + def f0(q, h, k): + return 1.0/k*(1.0 - ((1.0 - (q**h))/h)**k) + + def f1(q, h, k): + return 1.0/k*(1.0 - (-np.log(q))**k) + + def f2(q, h, k): + '''ppf = -np.log((1.0 - (q**h))/h) + ''' + return -sc.log1p(-(q**h)) + np.log(h) + + def f3(q, h, k): + return -np.log(-np.log(q)) + + return _lazyselect(condlist, + [f0, f1, f2, f3], + [q, h, k], + default=np.nan) + + def _get_stats_info(self, h, k): + condlist = [ + np.logical_and(h < 0, k >= 0), + k < 0, + ] + + def f0(h, k): + return (-1.0/h*k).astype(int) + + def f1(h, k): + return (-1.0/k).astype(int) + + return _lazyselect(condlist, [f0, f1], [h, k], default=5) + + def _stats(self, h, k): + maxr = self._get_stats_info(h, k) + outputs = [None if np.any(r < maxr) else np.nan for r in range(1, 5)] + return outputs[:] + + def _mom1_sc(self, m, *args): + maxr = self._get_stats_info(args[0], args[1]) + if m >= maxr: + return np.nan + return integrate.quad(self._mom_integ1, 0, 1, args=(m,)+args)[0] + + +kappa4 = kappa4_gen(name='kappa4') + + +class kappa3_gen(rv_continuous): + r"""Kappa 3 parameter distribution. + + %(before_notes)s + + Notes + ----- + The probability density function for `kappa3` is: + + .. math:: + + f(x, a) = a (a + x^a)^{-(a + 1)/a} + + for :math:`x > 0` and :math:`a > 0`. + + `kappa3` takes ``a`` as a shape parameter for :math:`a`. + + References + ---------- + P.W. Mielke and E.S. Johnson, "Three-Parameter Kappa Distribution Maximum + Likelihood and Likelihood Ratio Tests", Methods in Weather Research, + 701-707, (September, 1973), + :doi:`10.1175/1520-0493(1973)101<0701:TKDMLE>2.3.CO;2` + + B. Kumphon, "Maximum Entropy and Maximum Likelihood Estimation for the + Three-Parameter Kappa Distribution", Open Journal of Statistics, vol 2, + 415-419 (2012), :doi:`10.4236/ojs.2012.24050` + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _pdf(self, x, a): + # kappa3.pdf(x, a) = a*(a + x**a)**(-(a + 1)/a), for x > 0 + return a*(a + x**a)**(-1.0/a-1) + + def _cdf(self, x, a): + return x*(a + x**a)**(-1.0/a) + + def _sf(self, x, a): + x, a = np.broadcast_arrays(x, a) # some code paths pass scalars + sf = super()._sf(x, a) + + # When the SF is small, another formulation is typically more accurate. + # However, it blows up for large `a`, so use it only if it also returns + # a small value of the SF. + cutoff = 0.01 + i = sf < cutoff + sf2 = -sc.expm1(sc.xlog1py(-1.0 / a[i], a[i] * x[i]**-a[i])) + i2 = sf2 > cutoff + sf2[i2] = sf[i][i2] # replace bad values with original values + + sf[i] = sf2 + return sf + + def _ppf(self, q, a): + return (a/(q**-a - 1.0))**(1.0/a) + + def _isf(self, q, a): + lg = sc.xlog1py(-a, -q) + denom = sc.expm1(lg) + return (a / denom)**(1.0 / a) + + def _stats(self, a): + outputs = [None if np.any(i < a) else np.nan for i in range(1, 5)] + return outputs[:] + + def _mom1_sc(self, m, *args): + if np.any(m >= args[0]): + return np.nan + return integrate.quad(self._mom_integ1, 0, 1, args=(m,)+args)[0] + + +kappa3 = kappa3_gen(a=0.0, name='kappa3') + + +class moyal_gen(rv_continuous): + r"""A Moyal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `moyal` is: + + .. math:: + + f(x) = \exp(-(x + \exp(-x))/2) / \sqrt{2\pi} + + for a real number :math:`x`. + + %(after_notes)s + + This distribution has utility in high-energy physics and radiation + detection. It describes the energy loss of a charged relativistic + particle due to ionization of the medium [1]_. It also provides an + approximation for the Landau distribution. For an in depth description + see [2]_. For additional description, see [3]_. + + References + ---------- + .. [1] J.E. Moyal, "XXX. Theory of ionization fluctuations", + The London, Edinburgh, and Dublin Philosophical Magazine + and Journal of Science, vol 46, 263-280, (1955). + :doi:`10.1080/14786440308521076` (gated) + .. [2] G. Cordeiro et al., "The beta Moyal: a useful skew distribution", + International Journal of Research and Reviews in Applied Sciences, + vol 10, 171-192, (2012). + http://www.arpapress.com/Volumes/Vol10Issue2/IJRRAS_10_2_02.pdf + .. [3] C. Walck, "Handbook on Statistical Distributions for + Experimentalists; International Report SUF-PFY/96-01", Chapter 26, + University of Stockholm: Stockholm, Sweden, (2007). + http://www.stat.rice.edu/~dobelman/textfiles/DistributionsHandbook.pdf + + .. versionadded:: 1.1.0 + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + u1 = gamma.rvs(a=0.5, scale=2, size=size, + random_state=random_state) + return -np.log(u1) + + def _pdf(self, x): + return np.exp(-0.5 * (x + np.exp(-x))) / np.sqrt(2*np.pi) + + def _cdf(self, x): + return sc.erfc(np.exp(-0.5 * x) / np.sqrt(2)) + + def _sf(self, x): + return sc.erf(np.exp(-0.5 * x) / np.sqrt(2)) + + def _ppf(self, x): + return -np.log(2 * sc.erfcinv(x)**2) + + def _stats(self): + mu = np.log(2) + np.euler_gamma + mu2 = np.pi**2 / 2 + g1 = 28 * np.sqrt(2) * sc.zeta(3) / np.pi**3 + g2 = 4. + return mu, mu2, g1, g2 + + def _munp(self, n): + if n == 1.0: + return np.log(2) + np.euler_gamma + elif n == 2.0: + return np.pi**2 / 2 + (np.log(2) + np.euler_gamma)**2 + elif n == 3.0: + tmp1 = 1.5 * np.pi**2 * (np.log(2)+np.euler_gamma) + tmp2 = (np.log(2)+np.euler_gamma)**3 + tmp3 = 14 * sc.zeta(3) + return tmp1 + tmp2 + tmp3 + elif n == 4.0: + tmp1 = 4 * 14 * sc.zeta(3) * (np.log(2) + np.euler_gamma) + tmp2 = 3 * np.pi**2 * (np.log(2) + np.euler_gamma)**2 + tmp3 = (np.log(2) + np.euler_gamma)**4 + tmp4 = 7 * np.pi**4 / 4 + return tmp1 + tmp2 + tmp3 + tmp4 + else: + # return generic for higher moments + # return rv_continuous._mom1_sc(self, n, b) + return self._mom1_sc(n) + + +moyal = moyal_gen(name="moyal") + + +class nakagami_gen(rv_continuous): + r"""A Nakagami continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `nakagami` is: + + .. math:: + + f(x, \nu) = \frac{2 \nu^\nu}{\Gamma(\nu)} x^{2\nu-1} \exp(-\nu x^2) + + for :math:`x >= 0`, :math:`\nu > 0`. The distribution was introduced in + [2]_, see also [1]_ for further information. + + `nakagami` takes ``nu`` as a shape parameter for :math:`\nu`. + + %(after_notes)s + + References + ---------- + .. [1] "Nakagami distribution", Wikipedia + https://en.wikipedia.org/wiki/Nakagami_distribution + .. [2] M. Nakagami, "The m-distribution - A general formula of intensity + distribution of rapid fading", Statistical methods in radio wave + propagation, Pergamon Press, 1960, 3-36. + :doi:`10.1016/B978-0-08-009306-2.50005-4` + + %(example)s + + """ + def _argcheck(self, nu): + return nu > 0 + + def _shape_info(self): + return [_ShapeInfo("nu", False, (0, np.inf), (False, False))] + + def _pdf(self, x, nu): + return np.exp(self._logpdf(x, nu)) + + def _logpdf(self, x, nu): + # nakagami.pdf(x, nu) = 2 * nu**nu / gamma(nu) * + # x**(2*nu-1) * exp(-nu*x**2) + return (np.log(2) + sc.xlogy(nu, nu) - sc.gammaln(nu) + + sc.xlogy(2*nu - 1, x) - nu*x**2) + + def _cdf(self, x, nu): + return sc.gammainc(nu, nu*x*x) + + def _ppf(self, q, nu): + return np.sqrt(1.0/nu*sc.gammaincinv(nu, q)) + + def _sf(self, x, nu): + return sc.gammaincc(nu, nu*x*x) + + def _isf(self, p, nu): + return np.sqrt(1/nu * sc.gammainccinv(nu, p)) + + def _stats(self, nu): + mu = sc.poch(nu, 0.5)/np.sqrt(nu) + mu2 = 1.0-mu*mu + g1 = mu * (1 - 4*nu*mu2) / 2.0 / nu / np.power(mu2, 1.5) + g2 = -6*mu**4*nu + (8*nu-2)*mu**2-2*nu + 1 + g2 /= nu*mu2**2.0 + return mu, mu2, g1, g2 + + def _entropy(self, nu): + shape = np.shape(nu) + # because somehow this isn't taken care of by the infrastructure... + nu = np.atleast_1d(nu) + A = sc.gammaln(nu) + B = nu - (nu - 0.5) * sc.digamma(nu) + C = -0.5 * np.log(nu) - np.log(2) + h = A + B + C + # This is the asymptotic sum of A and B (see gh-17868) + norm_entropy = stats.norm._entropy() + # Above, this is lost to rounding error for large nu, so use the + # asymptotic sum when the approximation becomes accurate + i = nu > 5e4 # roundoff error ~ approximation error + # -1 / (12 * nu) is the O(1/nu) term; see gh-17929 + h[i] = C[i] + norm_entropy - 1/(12*nu[i]) + return h.reshape(shape)[()] + + def _rvs(self, nu, size=None, random_state=None): + # this relationship can be found in [1] or by a direct calculation + return np.sqrt(random_state.standard_gamma(nu, size=size) / nu) + + def _fitstart(self, data, args=None): + if isinstance(data, CensoredData): + data = data._uncensor() + if args is None: + args = (1.0,) * self.numargs + # Analytical justified estimates + # see: https://docs.scipy.org/doc/scipy/reference/tutorial/stats/continuous_nakagami.html + loc = np.min(data) + scale = np.sqrt(np.sum((data - loc)**2) / len(data)) + return args + (loc, scale) + + +nakagami = nakagami_gen(a=0.0, name="nakagami") + + +# The function name ncx2 is an abbreviation for noncentral chi squared. +def _ncx2_log_pdf(x, df, nc): + # We use (xs**2 + ns**2)/2 = (xs - ns)**2/2 + xs*ns, and include the + # factor of exp(-xs*ns) into the ive function to improve numerical + # stability at large values of xs. See also `rice.pdf`. + df2 = df/2.0 - 1.0 + xs, ns = np.sqrt(x), np.sqrt(nc) + res = sc.xlogy(df2/2.0, x/nc) - 0.5*(xs - ns)**2 + corr = sc.ive(df2, xs*ns) / 2.0 + # Return res + np.log(corr) avoiding np.log(0) + return _lazywhere( + corr > 0, + (res, corr), + f=lambda r, c: r + np.log(c), + fillvalue=-np.inf) + + +class ncx2_gen(rv_continuous): + r"""A non-central chi-squared continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `ncx2` is: + + .. math:: + + f(x, k, \lambda) = \frac{1}{2} \exp(-(\lambda+x)/2) + (x/\lambda)^{(k-2)/4} I_{(k-2)/2}(\sqrt{\lambda x}) + + for :math:`x >= 0`, :math:`k > 0` and :math:`\lambda \ge 0`. + :math:`k` specifies the degrees of freedom (denoted ``df`` in the + implementation) and :math:`\lambda` is the non-centrality parameter + (denoted ``nc`` in the implementation). :math:`I_\nu` denotes the + modified Bessel function of first order of degree :math:`\nu` + (`scipy.special.iv`). + + `ncx2` takes ``df`` and ``nc`` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, df, nc): + return (df > 0) & np.isfinite(df) & (nc >= 0) + + def _shape_info(self): + idf = _ShapeInfo("df", False, (0, np.inf), (False, False)) + inc = _ShapeInfo("nc", False, (0, np.inf), (True, False)) + return [idf, inc] + + def _rvs(self, df, nc, size=None, random_state=None): + return random_state.noncentral_chisquare(df, nc, size) + + def _logpdf(self, x, df, nc): + cond = np.ones_like(x, dtype=bool) & (nc != 0) + return _lazywhere(cond, (x, df, nc), f=_ncx2_log_pdf, + f2=lambda x, df, _: chi2._logpdf(x, df)) + + def _pdf(self, x, df, nc): + cond = np.ones_like(x, dtype=bool) & (nc != 0) + with np.errstate(over='ignore'): # see gh-17432 + return _lazywhere(cond, (x, df, nc), f=_boost._ncx2_pdf, + f2=lambda x, df, _: chi2._pdf(x, df)) + + def _cdf(self, x, df, nc): + cond = np.ones_like(x, dtype=bool) & (nc != 0) + with np.errstate(over='ignore'): # see gh-17432 + return _lazywhere(cond, (x, df, nc), f=_boost._ncx2_cdf, + f2=lambda x, df, _: chi2._cdf(x, df)) + + def _ppf(self, q, df, nc): + cond = np.ones_like(q, dtype=bool) & (nc != 0) + with np.errstate(over='ignore'): # see gh-17432 + return _lazywhere(cond, (q, df, nc), f=_boost._ncx2_ppf, + f2=lambda x, df, _: chi2._ppf(x, df)) + + def _sf(self, x, df, nc): + cond = np.ones_like(x, dtype=bool) & (nc != 0) + with np.errstate(over='ignore'): # see gh-17432 + return _lazywhere(cond, (x, df, nc), f=_boost._ncx2_sf, + f2=lambda x, df, _: chi2._sf(x, df)) + + def _isf(self, x, df, nc): + cond = np.ones_like(x, dtype=bool) & (nc != 0) + with np.errstate(over='ignore'): # see gh-17432 + return _lazywhere(cond, (x, df, nc), f=_boost._ncx2_isf, + f2=lambda x, df, _: chi2._isf(x, df)) + + def _stats(self, df, nc): + return ( + _boost._ncx2_mean(df, nc), + _boost._ncx2_variance(df, nc), + _boost._ncx2_skewness(df, nc), + _boost._ncx2_kurtosis_excess(df, nc), + ) + + +ncx2 = ncx2_gen(a=0.0, name='ncx2') + + +class ncf_gen(rv_continuous): + r"""A non-central F distribution continuous random variable. + + %(before_notes)s + + See Also + -------- + scipy.stats.f : Fisher distribution + + Notes + ----- + The probability density function for `ncf` is: + + .. math:: + + f(x, n_1, n_2, \lambda) = + \exp\left(\frac{\lambda}{2} + + \lambda n_1 \frac{x}{2(n_1 x + n_2)} + \right) + n_1^{n_1/2} n_2^{n_2/2} x^{n_1/2 - 1} \\ + (n_2 + n_1 x)^{-(n_1 + n_2)/2} + \gamma(n_1/2) \gamma(1 + n_2/2) \\ + \frac{L^{\frac{n_1}{2}-1}_{n_2/2} + \left(-\lambda n_1 \frac{x}{2(n_1 x + n_2)}\right)} + {B(n_1/2, n_2/2) + \gamma\left(\frac{n_1 + n_2}{2}\right)} + + for :math:`n_1, n_2 > 0`, :math:`\lambda \ge 0`. Here :math:`n_1` is the + degrees of freedom in the numerator, :math:`n_2` the degrees of freedom in + the denominator, :math:`\lambda` the non-centrality parameter, + :math:`\gamma` is the logarithm of the Gamma function, :math:`L_n^k` is a + generalized Laguerre polynomial and :math:`B` is the beta function. + + `ncf` takes ``df1``, ``df2`` and ``nc`` as shape parameters. If ``nc=0``, + the distribution becomes equivalent to the Fisher distribution. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, df1, df2, nc): + return (df1 > 0) & (df2 > 0) & (nc >= 0) + + def _shape_info(self): + idf1 = _ShapeInfo("df1", False, (0, np.inf), (False, False)) + idf2 = _ShapeInfo("df2", False, (0, np.inf), (False, False)) + inc = _ShapeInfo("nc", False, (0, np.inf), (True, False)) + return [idf1, idf2, inc] + + def _rvs(self, dfn, dfd, nc, size=None, random_state=None): + return random_state.noncentral_f(dfn, dfd, nc, size) + + def _pdf(self, x, dfn, dfd, nc): + # ncf.pdf(x, df1, df2, nc) = exp(nc/2 + nc*df1*x/(2*(df1*x+df2))) * + # df1**(df1/2) * df2**(df2/2) * x**(df1/2-1) * + # (df2+df1*x)**(-(df1+df2)/2) * + # gamma(df1/2)*gamma(1+df2/2) * + # L^{v1/2-1}^{v2/2}(-nc*v1*x/(2*(v1*x+v2))) / + # (B(v1/2, v2/2) * gamma((v1+v2)/2)) + return _boost._ncf_pdf(x, dfn, dfd, nc) + + def _cdf(self, x, dfn, dfd, nc): + return _boost._ncf_cdf(x, dfn, dfd, nc) + + def _ppf(self, q, dfn, dfd, nc): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._ncf_ppf(q, dfn, dfd, nc) + + def _sf(self, x, dfn, dfd, nc): + return _boost._ncf_sf(x, dfn, dfd, nc) + + def _isf(self, x, dfn, dfd, nc): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._ncf_isf(x, dfn, dfd, nc) + + def _munp(self, n, dfn, dfd, nc): + val = (dfn * 1.0/dfd)**n + term = sc.gammaln(n+0.5*dfn) + sc.gammaln(0.5*dfd-n) - sc.gammaln(dfd*0.5) + val *= np.exp(-nc / 2.0+term) + val *= sc.hyp1f1(n+0.5*dfn, 0.5*dfn, 0.5*nc) + return val + + def _stats(self, dfn, dfd, nc, moments='mv'): + mu = _boost._ncf_mean(dfn, dfd, nc) + mu2 = _boost._ncf_variance(dfn, dfd, nc) + g1 = _boost._ncf_skewness(dfn, dfd, nc) if 's' in moments else None + g2 = _boost._ncf_kurtosis_excess( + dfn, dfd, nc) if 'k' in moments else None + return mu, mu2, g1, g2 + + +ncf = ncf_gen(a=0.0, name='ncf') + + +class t_gen(rv_continuous): + r"""A Student's t continuous random variable. + + For the noncentral t distribution, see `nct`. + + %(before_notes)s + + See Also + -------- + nct + + Notes + ----- + The probability density function for `t` is: + + .. math:: + + f(x, \nu) = \frac{\Gamma((\nu+1)/2)} + {\sqrt{\pi \nu} \Gamma(\nu/2)} + (1+x^2/\nu)^{-(\nu+1)/2} + + where :math:`x` is a real number and the degrees of freedom parameter + :math:`\nu` (denoted ``df`` in the implementation) satisfies + :math:`\nu > 0`. :math:`\Gamma` is the gamma function + (`scipy.special.gamma`). + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("df", False, (0, np.inf), (False, False))] + + def _rvs(self, df, size=None, random_state=None): + return random_state.standard_t(df, size=size) + + def _pdf(self, x, df): + return _lazywhere( + df == np.inf, (x, df), + f=lambda x, df: norm._pdf(x), + f2=lambda x, df: ( + np.exp(self._logpdf(x, df)) + ) + ) + + def _logpdf(self, x, df): + + def t_logpdf(x, df): + return (np.log(sc.poch(0.5 * df, 0.5)) + - 0.5 * (np.log(df) + np.log(np.pi)) + - (df + 1)/2*np.log1p(x * x/df)) + + def norm_logpdf(x, df): + return norm._logpdf(x) + + return _lazywhere(df == np.inf, (x, df, ), f=norm_logpdf, f2=t_logpdf) + + def _cdf(self, x, df): + return sc.stdtr(df, x) + + def _sf(self, x, df): + return sc.stdtr(df, -x) + + def _ppf(self, q, df): + return sc.stdtrit(df, q) + + def _isf(self, q, df): + return -sc.stdtrit(df, q) + + def _stats(self, df): + # infinite df -> normal distribution (0.0, 1.0, 0.0, 0.0) + infinite_df = np.isposinf(df) + + mu = np.where(df > 1, 0.0, np.inf) + + condlist = ((df > 1) & (df <= 2), + (df > 2) & np.isfinite(df), + infinite_df) + choicelist = (lambda df: np.broadcast_to(np.inf, df.shape), + lambda df: df / (df-2.0), + lambda df: np.broadcast_to(1, df.shape)) + mu2 = _lazyselect(condlist, choicelist, (df,), np.nan) + + g1 = np.where(df > 3, 0.0, np.nan) + + condlist = ((df > 2) & (df <= 4), + (df > 4) & np.isfinite(df), + infinite_df) + choicelist = (lambda df: np.broadcast_to(np.inf, df.shape), + lambda df: 6.0 / (df-4.0), + lambda df: np.broadcast_to(0, df.shape)) + g2 = _lazyselect(condlist, choicelist, (df,), np.nan) + + return mu, mu2, g1, g2 + + def _entropy(self, df): + if df == np.inf: + return norm._entropy() + + def regular(df): + half = df/2 + half1 = (df + 1)/2 + return (half1*(sc.digamma(half1) - sc.digamma(half)) + + np.log(np.sqrt(df)*sc.beta(half, 0.5))) + + def asymptotic(df): + # Formula from Wolfram Alpha: + # "asymptotic expansion (d+1)/2 * (digamma((d+1)/2) - digamma(d/2)) + # + log(sqrt(d) * beta(d/2, 1/2))" + h = (norm._entropy() + 1/df + (df**-2.)/4 - (df**-3.)/6 + - (df**-4.)/8 + 3/10*(df**-5.) + (df**-6.)/4) + return h + + h = _lazywhere(df >= 100, (df, ), f=asymptotic, f2=regular) + return h + + +t = t_gen(name='t') + + +class nct_gen(rv_continuous): + r"""A non-central Student's t continuous random variable. + + %(before_notes)s + + Notes + ----- + If :math:`Y` is a standard normal random variable and :math:`V` is + an independent chi-square random variable (`chi2`) with :math:`k` degrees + of freedom, then + + .. math:: + + X = \frac{Y + c}{\sqrt{V/k}} + + has a non-central Student's t distribution on the real line. + The degrees of freedom parameter :math:`k` (denoted ``df`` in the + implementation) satisfies :math:`k > 0` and the noncentrality parameter + :math:`c` (denoted ``nc`` in the implementation) is a real number. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, df, nc): + return (df > 0) & (nc == nc) + + def _shape_info(self): + idf = _ShapeInfo("df", False, (0, np.inf), (False, False)) + inc = _ShapeInfo("nc", False, (-np.inf, np.inf), (False, False)) + return [idf, inc] + + def _rvs(self, df, nc, size=None, random_state=None): + n = norm.rvs(loc=nc, size=size, random_state=random_state) + c2 = chi2.rvs(df, size=size, random_state=random_state) + return n * np.sqrt(df) / np.sqrt(c2) + + def _pdf(self, x, df, nc): + # Boost version has accuracy issues in left tail; see gh-16591 + n = df*1.0 + nc = nc*1.0 + x2 = x*x + ncx2 = nc*nc*x2 + fac1 = n + x2 + trm1 = (n/2.*np.log(n) + sc.gammaln(n+1) + - (n*np.log(2) + nc*nc/2 + (n/2)*np.log(fac1) + + sc.gammaln(n/2))) + Px = np.exp(trm1) + valF = ncx2 / (2*fac1) + trm1 = (np.sqrt(2)*nc*x*sc.hyp1f1(n/2+1, 1.5, valF) + / np.asarray(fac1*sc.gamma((n+1)/2))) + trm2 = (sc.hyp1f1((n+1)/2, 0.5, valF) + / np.asarray(np.sqrt(fac1)*sc.gamma(n/2+1))) + Px *= trm1+trm2 + return np.clip(Px, 0, None) + + def _cdf(self, x, df, nc): + with np.errstate(over='ignore'): # see gh-17432 + return np.clip(_boost._nct_cdf(x, df, nc), 0, 1) + + def _ppf(self, q, df, nc): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._nct_ppf(q, df, nc) + + def _sf(self, x, df, nc): + with np.errstate(over='ignore'): # see gh-17432 + return np.clip(_boost._nct_sf(x, df, nc), 0, 1) + + def _isf(self, x, df, nc): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._nct_isf(x, df, nc) + + def _stats(self, df, nc, moments='mv'): + mu = _boost._nct_mean(df, nc) + mu2 = _boost._nct_variance(df, nc) + g1 = _boost._nct_skewness(df, nc) if 's' in moments else None + g2 = _boost._nct_kurtosis_excess(df, nc) if 'k' in moments else None + return mu, mu2, g1, g2 + + +nct = nct_gen(name="nct") + + +class pareto_gen(rv_continuous): + r"""A Pareto continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `pareto` is: + + .. math:: + + f(x, b) = \frac{b}{x^{b+1}} + + for :math:`x \ge 1`, :math:`b > 0`. + + `pareto` takes ``b`` as a shape parameter for :math:`b`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("b", False, (0, np.inf), (False, False))] + + def _pdf(self, x, b): + # pareto.pdf(x, b) = b / x**(b+1) + return b * x**(-b-1) + + def _cdf(self, x, b): + return 1 - x**(-b) + + def _ppf(self, q, b): + return pow(1-q, -1.0/b) + + def _sf(self, x, b): + return x**(-b) + + def _isf(self, q, b): + return np.power(q, -1.0 / b) + + def _stats(self, b, moments='mv'): + mu, mu2, g1, g2 = None, None, None, None + if 'm' in moments: + mask = b > 1 + bt = np.extract(mask, b) + mu = np.full(np.shape(b), fill_value=np.inf) + np.place(mu, mask, bt / (bt-1.0)) + if 'v' in moments: + mask = b > 2 + bt = np.extract(mask, b) + mu2 = np.full(np.shape(b), fill_value=np.inf) + np.place(mu2, mask, bt / (bt-2.0) / (bt-1.0)**2) + if 's' in moments: + mask = b > 3 + bt = np.extract(mask, b) + g1 = np.full(np.shape(b), fill_value=np.nan) + vals = 2 * (bt + 1.0) * np.sqrt(bt - 2.0) / ((bt - 3.0) * np.sqrt(bt)) + np.place(g1, mask, vals) + if 'k' in moments: + mask = b > 4 + bt = np.extract(mask, b) + g2 = np.full(np.shape(b), fill_value=np.nan) + vals = (6.0*np.polyval([1.0, 1.0, -6, -2], bt) / + np.polyval([1.0, -7.0, 12.0, 0.0], bt)) + np.place(g2, mask, vals) + return mu, mu2, g1, g2 + + def _entropy(self, c): + return 1 + 1.0/c - np.log(c) + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + parameters = _check_fit_input_parameters(self, data, args, kwds) + data, fshape, floc, fscale = parameters + + # ensure that any fixed parameters don't violate constraints of the + # distribution before continuing. + if floc is not None and np.min(data) - floc < (fscale or 0): + raise FitDataError("pareto", lower=1, upper=np.inf) + + ndata = data.shape[0] + + def get_shape(scale, location): + # The first-order necessary condition on `shape` can be solved in + # closed form + return ndata / np.sum(np.log((data - location) / scale)) + + if floc is fscale is None: + # The support of the distribution is `(x - loc)/scale > 0`. + # The method of Lagrange multipliers turns this constraint + # into an equation that can be solved numerically. + # See gh-12545 for details. + + def dL_dScale(shape, scale): + # The partial derivative of the log-likelihood function w.r.t. + # the scale. + return ndata * shape / scale + + def dL_dLocation(shape, location): + # The partial derivative of the log-likelihood function w.r.t. + # the location. + return (shape + 1) * np.sum(1 / (data - location)) + + def fun_to_solve(scale): + # optimize the scale by setting the partial derivatives + # w.r.t. to location and scale equal and solving. + location = np.min(data) - scale + shape = fshape or get_shape(scale, location) + return dL_dLocation(shape, location) - dL_dScale(shape, scale) + + def interval_contains_root(lbrack, rbrack): + # return true if the signs disagree. + return (np.sign(fun_to_solve(lbrack)) != + np.sign(fun_to_solve(rbrack))) + + # set brackets for `root_scalar` to use when optimizing over the + # scale such that a root is likely between them. Use user supplied + # guess or default 1. + brack_start = float(kwds.get('scale', 1)) + lbrack, rbrack = brack_start / 2, brack_start * 2 + # if a root is not between the brackets, iteratively expand them + # until they include a sign change, checking after each bracket is + # modified. + while (not interval_contains_root(lbrack, rbrack) + and (lbrack > 0 or rbrack < np.inf)): + lbrack /= 2 + rbrack *= 2 + res = root_scalar(fun_to_solve, bracket=[lbrack, rbrack]) + if res.converged: + scale = res.root + loc = np.min(data) - scale + shape = fshape or get_shape(scale, loc) + + # The Pareto distribution requires that its parameters satisfy + # the condition `fscale + floc <= min(data)`. However, to + # avoid numerical issues, we require that `fscale + floc` + # is strictly less than `min(data)`. If this condition + # is not satisfied, reduce the scale with `np.nextafter` to + # ensure that data does not fall outside of the support. + if not (scale + loc) < np.min(data): + scale = np.min(data) - loc + scale = np.nextafter(scale, 0) + return shape, loc, scale + else: + return super().fit(data, **kwds) + elif floc is None: + loc = np.min(data) - fscale + else: + loc = floc + # Source: Evans, Hastings, and Peacock (2000), Statistical + # Distributions, 3rd. Ed., John Wiley and Sons. Page 149. + scale = fscale or np.min(data) - loc + shape = fshape or get_shape(scale, loc) + return shape, loc, scale + + +pareto = pareto_gen(a=1.0, name="pareto") + + +class lomax_gen(rv_continuous): + r"""A Lomax (Pareto of the second kind) continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `lomax` is: + + .. math:: + + f(x, c) = \frac{c}{(1+x)^{c+1}} + + for :math:`x \ge 0`, :math:`c > 0`. + + `lomax` takes ``c`` as a shape parameter for :math:`c`. + + `lomax` is a special case of `pareto` with ``loc=-1.0``. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # lomax.pdf(x, c) = c / (1+x)**(c+1) + return c*1.0/(1.0+x)**(c+1.0) + + def _logpdf(self, x, c): + return np.log(c) - (c+1)*sc.log1p(x) + + def _cdf(self, x, c): + return -sc.expm1(-c*sc.log1p(x)) + + def _sf(self, x, c): + return np.exp(-c*sc.log1p(x)) + + def _logsf(self, x, c): + return -c*sc.log1p(x) + + def _ppf(self, q, c): + return sc.expm1(-sc.log1p(-q)/c) + + def _isf(self, q, c): + return q**(-1.0 / c) - 1 + + def _stats(self, c): + mu, mu2, g1, g2 = pareto.stats(c, loc=-1.0, moments='mvsk') + return mu, mu2, g1, g2 + + def _entropy(self, c): + return 1+1.0/c-np.log(c) + + +lomax = lomax_gen(a=0.0, name="lomax") + + +class pearson3_gen(rv_continuous): + r"""A pearson type III continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `pearson3` is: + + .. math:: + + f(x, \kappa) = \frac{|\beta|}{\Gamma(\alpha)} + (\beta (x - \zeta))^{\alpha - 1} + \exp(-\beta (x - \zeta)) + + where: + + .. math:: + + \beta = \frac{2}{\kappa} + + \alpha = \beta^2 = \frac{4}{\kappa^2} + + \zeta = -\frac{\alpha}{\beta} = -\beta + + :math:`\Gamma` is the gamma function (`scipy.special.gamma`). + Pass the skew :math:`\kappa` into `pearson3` as the shape parameter + ``skew``. + + %(after_notes)s + + %(example)s + + References + ---------- + R.W. Vogel and D.E. McMartin, "Probability Plot Goodness-of-Fit and + Skewness Estimation Procedures for the Pearson Type 3 Distribution", Water + Resources Research, Vol.27, 3149-3158 (1991). + + L.R. Salvosa, "Tables of Pearson's Type III Function", Ann. Math. Statist., + Vol.1, 191-198 (1930). + + "Using Modern Computing Tools to Fit the Pearson Type III Distribution to + Aviation Loads Data", Office of Aviation Research (2003). + + """ + def _preprocess(self, x, skew): + # The real 'loc' and 'scale' are handled in the calling pdf(...). The + # local variables 'loc' and 'scale' within pearson3._pdf are set to + # the defaults just to keep them as part of the equations for + # documentation. + loc = 0.0 + scale = 1.0 + + # If skew is small, return _norm_pdf. The divide between pearson3 + # and norm was found by brute force and is approximately a skew of + # 0.000016. No one, I hope, would actually use a skew value even + # close to this small. + norm2pearson_transition = 0.000016 + + ans, x, skew = np.broadcast_arrays(1.0, x, skew) + ans = ans.copy() + + # mask is True where skew is small enough to use the normal approx. + mask = np.absolute(skew) < norm2pearson_transition + invmask = ~mask + + beta = 2.0 / (skew[invmask] * scale) + alpha = (scale * beta)**2 + zeta = loc - alpha / beta + + transx = beta * (x[invmask] - zeta) + return ans, x, transx, mask, invmask, beta, alpha, zeta + + def _argcheck(self, skew): + # The _argcheck function in rv_continuous only allows positive + # arguments. The skew argument for pearson3 can be zero (which I want + # to handle inside pearson3._pdf) or negative. So just return True + # for all skew args. + return np.isfinite(skew) + + def _shape_info(self): + return [_ShapeInfo("skew", False, (-np.inf, np.inf), (False, False))] + + def _stats(self, skew): + m = 0.0 + v = 1.0 + s = skew + k = 1.5*skew**2 + return m, v, s, k + + def _pdf(self, x, skew): + # pearson3.pdf(x, skew) = abs(beta) / gamma(alpha) * + # (beta * (x - zeta))**(alpha - 1) * exp(-beta*(x - zeta)) + # Do the calculation in _logpdf since helps to limit + # overflow/underflow problems + ans = np.exp(self._logpdf(x, skew)) + if ans.ndim == 0: + if np.isnan(ans): + return 0.0 + return ans + ans[np.isnan(ans)] = 0.0 + return ans + + def _logpdf(self, x, skew): + # PEARSON3 logpdf GAMMA logpdf + # np.log(abs(beta)) + # + (alpha - 1)*np.log(beta*(x - zeta)) + (a - 1)*np.log(x) + # - beta*(x - zeta) - x + # - sc.gammalnalpha) - sc.gammalna) + ans, x, transx, mask, invmask, beta, alpha, _ = ( + self._preprocess(x, skew)) + + ans[mask] = np.log(_norm_pdf(x[mask])) + # use logpdf instead of _logpdf to fix issue mentioned in gh-12640 + # (_logpdf does not return correct result for alpha = 1) + ans[invmask] = np.log(abs(beta)) + gamma.logpdf(transx, alpha) + return ans + + def _cdf(self, x, skew): + ans, x, transx, mask, invmask, _, alpha, _ = ( + self._preprocess(x, skew)) + + ans[mask] = _norm_cdf(x[mask]) + + skew = np.broadcast_to(skew, invmask.shape) + invmask1a = np.logical_and(invmask, skew > 0) + invmask1b = skew[invmask] > 0 + # use cdf instead of _cdf to fix issue mentioned in gh-12640 + # (_cdf produces NaNs for inputs outside support) + ans[invmask1a] = gamma.cdf(transx[invmask1b], alpha[invmask1b]) + + # The gamma._cdf approach wasn't working with negative skew. + # Note that multiplying the skew by -1 reflects about x=0. + # So instead of evaluating the CDF with negative skew at x, + # evaluate the SF with positive skew at -x. + invmask2a = np.logical_and(invmask, skew < 0) + invmask2b = skew[invmask] < 0 + # gamma._sf produces NaNs when transx < 0, so use gamma.sf + ans[invmask2a] = gamma.sf(transx[invmask2b], alpha[invmask2b]) + + return ans + + def _sf(self, x, skew): + ans, x, transx, mask, invmask, _, alpha, _ = ( + self._preprocess(x, skew)) + + ans[mask] = _norm_sf(x[mask]) + + skew = np.broadcast_to(skew, invmask.shape) + invmask1a = np.logical_and(invmask, skew > 0) + invmask1b = skew[invmask] > 0 + ans[invmask1a] = gamma.sf(transx[invmask1b], alpha[invmask1b]) + + invmask2a = np.logical_and(invmask, skew < 0) + invmask2b = skew[invmask] < 0 + ans[invmask2a] = gamma.cdf(transx[invmask2b], alpha[invmask2b]) + + return ans + + def _rvs(self, skew, size=None, random_state=None): + skew = np.broadcast_to(skew, size) + ans, _, _, mask, invmask, beta, alpha, zeta = ( + self._preprocess([0], skew)) + + nsmall = mask.sum() + nbig = mask.size - nsmall + ans[mask] = random_state.standard_normal(nsmall) + ans[invmask] = random_state.standard_gamma(alpha, nbig)/beta + zeta + + if size == (): + ans = ans[0] + return ans + + def _ppf(self, q, skew): + ans, q, _, mask, invmask, beta, alpha, zeta = ( + self._preprocess(q, skew)) + ans[mask] = _norm_ppf(q[mask]) + q = q[invmask] + q[beta < 0] = 1 - q[beta < 0] # for negative skew; see gh-17050 + ans[invmask] = sc.gammaincinv(alpha, q)/beta + zeta + return ans + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + Note that method of moments (`method='MM'`) is not + available for this distribution.\n\n""") + def fit(self, data, *args, **kwds): + if kwds.get("method", None) == 'MM': + raise NotImplementedError("Fit `method='MM'` is not available for " + "the Pearson3 distribution. Please try " + "the default `method='MLE'`.") + else: + return super(type(self), self).fit(data, *args, **kwds) + + +pearson3 = pearson3_gen(name="pearson3") + + +class powerlaw_gen(rv_continuous): + r"""A power-function continuous random variable. + + %(before_notes)s + + See Also + -------- + pareto + + Notes + ----- + The probability density function for `powerlaw` is: + + .. math:: + + f(x, a) = a x^{a-1} + + for :math:`0 \le x \le 1`, :math:`a > 0`. + + `powerlaw` takes ``a`` as a shape parameter for :math:`a`. + + %(after_notes)s + + For example, the support of `powerlaw` can be adjusted from the default + interval ``[0, 1]`` to the interval ``[c, c+d]`` by setting ``loc=c`` and + ``scale=d``. For a power-law distribution with infinite support, see + `pareto`. + + `powerlaw` is a special case of `beta` with ``b=1``. + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _pdf(self, x, a): + # powerlaw.pdf(x, a) = a * x**(a-1) + return a*x**(a-1.0) + + def _logpdf(self, x, a): + return np.log(a) + sc.xlogy(a - 1, x) + + def _cdf(self, x, a): + return x**(a*1.0) + + def _logcdf(self, x, a): + return a*np.log(x) + + def _ppf(self, q, a): + return pow(q, 1.0/a) + + def _sf(self, p, a): + return -sc.powm1(p, a) + + def _munp(self, n, a): + # The following expression is correct for all real n (provided a > 0). + return a / (a + n) + + def _stats(self, a): + return (a / (a + 1.0), + a / (a + 2.0) / (a + 1.0) ** 2, + -2.0 * ((a - 1.0) / (a + 3.0)) * np.sqrt((a + 2.0) / a), + 6 * np.polyval([1, -1, -6, 2], a) / (a * (a + 3.0) * (a + 4))) + + def _entropy(self, a): + return 1 - 1.0/a - np.log(a) + + def _support_mask(self, x, a): + return (super()._support_mask(x, a) + & ((x != 0) | (a >= 1))) + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + Notes specifically for ``powerlaw.fit``: If the location is a free + parameter and the value returned for the shape parameter is less than + one, the true maximum likelihood approaches infinity. This causes + numerical difficulties, and the resulting estimates are approximate. + \n\n""") + def fit(self, data, *args, **kwds): + # Summary of the strategy: + # + # 1) If the scale and location are fixed, return the shape according + # to a formula. + # + # 2) If the scale is fixed, there are two possibilities for the other + # parameters - one corresponding with shape less than one, and + # another with shape greater than one. Calculate both, and return + # whichever has the better log-likelihood. + # + # At this point, the scale is known to be free. + # + # 3) If the location is fixed, return the scale and shape according to + # formulas (or, if the shape is fixed, the fixed shape). + # + # At this point, the location and scale are both free. There are + # separate equations depending on whether the shape is less than one or + # greater than one. + # + # 4a) If the shape is less than one, there are formulas for shape, + # location, and scale. + # 4b) If the shape is greater than one, there are formulas for shape + # and scale, but there is a condition for location to be solved + # numerically. + # + # If the shape is fixed and less than one, we use 4a. + # If the shape is fixed and greater than one, we use 4b. + # If the shape is also free, we calculate fits using both 4a and 4b + # and choose the one that results a better log-likelihood. + # + # In many cases, the use of `np.nextafter` is used to avoid numerical + # issues. + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + if len(np.unique(data)) == 1: + return super().fit(data, *args, **kwds) + + data, fshape, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + penalized_nllf_args = [data, (self._fitstart(data),)] + penalized_nllf = self._reduce_func(penalized_nllf_args, {})[1] + + # ensure that any fixed parameters don't violate constraints of the + # distribution before continuing. The support of the distribution + # is `0 < (x - loc)/scale < 1`. + if floc is not None: + if not data.min() > floc: + raise FitDataError('powerlaw', 0, 1) + if fscale is not None and not data.max() <= floc + fscale: + raise FitDataError('powerlaw', 0, 1) + + if fscale is not None: + if fscale <= 0: + raise ValueError("Negative or zero `fscale` is outside the " + "range allowed by the distribution.") + if fscale <= np.ptp(data): + msg = "`fscale` must be greater than the range of data." + raise ValueError(msg) + + def get_shape(data, loc, scale): + # The first-order necessary condition on `shape` can be solved in + # closed form. It can be used no matter the assumption of the + # value of the shape. + N = len(data) + return - N / (np.sum(np.log(data - loc)) - N*np.log(scale)) + + def get_scale(data, loc): + # analytical solution for `scale` based on the location. + # It can be used no matter the assumption of the value of the + # shape. + return data.max() - loc + + # 1) The location and scale are both fixed. Analytically determine the + # shape. + if fscale is not None and floc is not None: + return get_shape(data, floc, fscale), floc, fscale + + # 2) The scale is fixed. There are two possibilities for the other + # parameters. Choose the option with better log-likelihood. + if fscale is not None: + # using `data.min()` as the optimal location + loc_lt1 = np.nextafter(data.min(), -np.inf) + shape_lt1 = fshape or get_shape(data, loc_lt1, fscale) + ll_lt1 = penalized_nllf((shape_lt1, loc_lt1, fscale), data) + + # using `data.max() - scale` as the optimal location + loc_gt1 = np.nextafter(data.max() - fscale, np.inf) + shape_gt1 = fshape or get_shape(data, loc_gt1, fscale) + ll_gt1 = penalized_nllf((shape_gt1, loc_gt1, fscale), data) + + if ll_lt1 < ll_gt1: + return shape_lt1, loc_lt1, fscale + else: + return shape_gt1, loc_gt1, fscale + + # 3) The location is fixed. Return the analytical scale and the + # analytical (or fixed) shape. + if floc is not None: + scale = get_scale(data, floc) + shape = fshape or get_shape(data, floc, scale) + return shape, floc, scale + + # 4) Location and scale are both free + # 4a) Use formulas that assume `shape <= 1`. + + def fit_loc_scale_w_shape_lt_1(): + loc = np.nextafter(data.min(), -np.inf) + if np.abs(loc) < np.finfo(loc.dtype).tiny: + loc = np.sign(loc) * np.finfo(loc.dtype).tiny + scale = np.nextafter(get_scale(data, loc), np.inf) + shape = fshape or get_shape(data, loc, scale) + return shape, loc, scale + + # 4b) Fit under the assumption that `shape > 1`. The support + # of the distribution is `(x - loc)/scale <= 1`. The method of Lagrange + # multipliers turns this constraint into the condition that + # dL_dScale - dL_dLocation must be zero, which is solved numerically. + # (Alternatively, substitute the constraint into the objective + # function before deriving the likelihood equation for location.) + + def dL_dScale(data, shape, scale): + # The partial derivative of the log-likelihood function w.r.t. + # the scale. + return -data.shape[0] * shape / scale + + def dL_dLocation(data, shape, loc): + # The partial derivative of the log-likelihood function w.r.t. + # the location. + return (shape - 1) * np.sum(1 / (loc - data)) # -1/(data-loc) + + def dL_dLocation_star(loc): + # The derivative of the log-likelihood function w.r.t. + # the location, given optimal shape and scale + scale = np.nextafter(get_scale(data, loc), -np.inf) + shape = fshape or get_shape(data, loc, scale) + return dL_dLocation(data, shape, loc) + + def fun_to_solve(loc): + # optimize the location by setting the partial derivatives + # w.r.t. to location and scale equal and solving. + scale = np.nextafter(get_scale(data, loc), -np.inf) + shape = fshape or get_shape(data, loc, scale) + return (dL_dScale(data, shape, scale) + - dL_dLocation(data, shape, loc)) + + def fit_loc_scale_w_shape_gt_1(): + # set brackets for `root_scalar` to use when optimizing over the + # location such that a root is likely between them. + rbrack = np.nextafter(data.min(), -np.inf) + + # if the sign of `dL_dLocation_star` is positive at rbrack, + # we're not going to find the root we're looking for + delta = (data.min() - rbrack) + while dL_dLocation_star(rbrack) > 0: + rbrack = data.min() - delta + delta *= 2 + + def interval_contains_root(lbrack, rbrack): + # Check if the interval (lbrack, rbrack) contains the root. + return (np.sign(fun_to_solve(lbrack)) + != np.sign(fun_to_solve(rbrack))) + + lbrack = rbrack - 1 + + # if the sign doesn't change between the brackets, move the left + # bracket until it does. (The right bracket remains fixed at the + # maximum permissible value.) + i = 1.0 + while (not interval_contains_root(lbrack, rbrack) + and lbrack != -np.inf): + lbrack = (data.min() - i) + i *= 2 + + root = optimize.root_scalar(fun_to_solve, bracket=(lbrack, rbrack)) + + loc = np.nextafter(root.root, -np.inf) + scale = np.nextafter(get_scale(data, loc), np.inf) + shape = fshape or get_shape(data, loc, scale) + return shape, loc, scale + + # Shape is fixed - choose 4a or 4b accordingly. + if fshape is not None and fshape <= 1: + return fit_loc_scale_w_shape_lt_1() + elif fshape is not None and fshape > 1: + return fit_loc_scale_w_shape_gt_1() + + # Shape is free + fit_shape_lt1 = fit_loc_scale_w_shape_lt_1() + ll_lt1 = self.nnlf(fit_shape_lt1, data) + + fit_shape_gt1 = fit_loc_scale_w_shape_gt_1() + ll_gt1 = self.nnlf(fit_shape_gt1, data) + + if ll_lt1 <= ll_gt1 and fit_shape_lt1[0] <= 1: + return fit_shape_lt1 + elif ll_lt1 > ll_gt1 and fit_shape_gt1[0] > 1: + return fit_shape_gt1 + else: + return super().fit(data, *args, **kwds) + + +powerlaw = powerlaw_gen(a=0.0, b=1.0, name="powerlaw") + + +class powerlognorm_gen(rv_continuous): + r"""A power log-normal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `powerlognorm` is: + + .. math:: + + f(x, c, s) = \frac{c}{x s} \phi(\log(x)/s) + (\Phi(-\log(x)/s))^{c-1} + + where :math:`\phi` is the normal pdf, and :math:`\Phi` is the normal cdf, + and :math:`x > 0`, :math:`s, c > 0`. + + `powerlognorm` takes :math:`c` and :math:`s` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + ic = _ShapeInfo("c", False, (0, np.inf), (False, False)) + i_s = _ShapeInfo("s", False, (0, np.inf), (False, False)) + return [ic, i_s] + + def _pdf(self, x, c, s): + return np.exp(self._logpdf(x, c, s)) + + def _logpdf(self, x, c, s): + return (np.log(c) - np.log(x) - np.log(s) + + _norm_logpdf(np.log(x) / s) + + _norm_logcdf(-np.log(x) / s) * (c - 1.)) + + def _cdf(self, x, c, s): + return -sc.expm1(self._logsf(x, c, s)) + + def _ppf(self, q, c, s): + return self._isf(1 - q, c, s) + + def _sf(self, x, c, s): + return np.exp(self._logsf(x, c, s)) + + def _logsf(self, x, c, s): + return _norm_logcdf(-np.log(x) / s) * c + + def _isf(self, q, c, s): + return np.exp(-_norm_ppf(q**(1/c)) * s) + + +powerlognorm = powerlognorm_gen(a=0.0, name="powerlognorm") + + +class powernorm_gen(rv_continuous): + r"""A power normal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `powernorm` is: + + .. math:: + + f(x, c) = c \phi(x) (\Phi(-x))^{c-1} + + where :math:`\phi` is the normal pdf, :math:`\Phi` is the normal cdf, + :math:`x` is any real, and :math:`c > 0` [1]_. + + `powernorm` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + References + ---------- + .. [1] NIST Engineering Statistics Handbook, Section 1.3.6.6.13, + https://www.itl.nist.gov/div898/handbook//eda/section3/eda366d.htm + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + def _pdf(self, x, c): + # powernorm.pdf(x, c) = c * phi(x) * (Phi(-x))**(c-1) + return c*_norm_pdf(x) * (_norm_cdf(-x)**(c-1.0)) + + def _logpdf(self, x, c): + return np.log(c) + _norm_logpdf(x) + (c-1)*_norm_logcdf(-x) + + def _cdf(self, x, c): + return -sc.expm1(self._logsf(x, c)) + + def _ppf(self, q, c): + return -_norm_ppf(pow(1.0 - q, 1.0 / c)) + + def _sf(self, x, c): + return np.exp(self._logsf(x, c)) + + def _logsf(self, x, c): + return c * _norm_logcdf(-x) + + def _isf(self, q, c): + return -_norm_ppf(np.exp(np.log(q) / c)) + + +powernorm = powernorm_gen(name='powernorm') + + +class rdist_gen(rv_continuous): + r"""An R-distributed (symmetric beta) continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `rdist` is: + + .. math:: + + f(x, c) = \frac{(1-x^2)^{c/2-1}}{B(1/2, c/2)} + + for :math:`-1 \le x \le 1`, :math:`c > 0`. `rdist` is also called the + symmetric beta distribution: if B has a `beta` distribution with + parameters (c/2, c/2), then X = 2*B - 1 follows a R-distribution with + parameter c. + + `rdist` takes ``c`` as a shape parameter for :math:`c`. + + This distribution includes the following distribution kernels as + special cases:: + + c = 2: uniform + c = 3: `semicircular` + c = 4: Epanechnikov (parabolic) + c = 6: quartic (biweight) + c = 8: triweight + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("c", False, (0, np.inf), (False, False))] + + # use relation to the beta distribution for pdf, cdf, etc + def _pdf(self, x, c): + return np.exp(self._logpdf(x, c)) + + def _logpdf(self, x, c): + return -np.log(2) + beta._logpdf((x + 1)/2, c/2, c/2) + + def _cdf(self, x, c): + return beta._cdf((x + 1)/2, c/2, c/2) + + def _sf(self, x, c): + return beta._sf((x + 1)/2, c/2, c/2) + + def _ppf(self, q, c): + return 2*beta._ppf(q, c/2, c/2) - 1 + + def _rvs(self, c, size=None, random_state=None): + return 2 * random_state.beta(c/2, c/2, size) - 1 + + def _munp(self, n, c): + numerator = (1 - (n % 2)) * sc.beta((n + 1.0) / 2, c / 2.0) + return numerator / sc.beta(1. / 2, c / 2.) + + +rdist = rdist_gen(a=-1.0, b=1.0, name="rdist") + + +class rayleigh_gen(rv_continuous): + r"""A Rayleigh continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `rayleigh` is: + + .. math:: + + f(x) = x \exp(-x^2/2) + + for :math:`x \ge 0`. + + `rayleigh` is a special case of `chi` with ``df=2``. + + %(after_notes)s + + %(example)s + + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return chi.rvs(2, size=size, random_state=random_state) + + def _pdf(self, r): + # rayleigh.pdf(r) = r * exp(-r**2/2) + return np.exp(self._logpdf(r)) + + def _logpdf(self, r): + return np.log(r) - 0.5 * r * r + + def _cdf(self, r): + return -sc.expm1(-0.5 * r**2) + + def _ppf(self, q): + return np.sqrt(-2 * sc.log1p(-q)) + + def _sf(self, r): + return np.exp(self._logsf(r)) + + def _logsf(self, r): + return -0.5 * r * r + + def _isf(self, q): + return np.sqrt(-2 * np.log(q)) + + def _stats(self): + val = 4 - np.pi + return (np.sqrt(np.pi/2), + val/2, + 2*(np.pi-3)*np.sqrt(np.pi)/val**1.5, + 6*np.pi/val-16/val**2) + + def _entropy(self): + return _EULER/2.0 + 1 - 0.5*np.log(2) + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + Notes specifically for ``rayleigh.fit``: If the location is fixed with + the `floc` parameter, this method uses an analytical formula to find + the scale. Otherwise, this function uses a numerical root finder on + the first order conditions of the log-likelihood function to find the + MLE. Only the (optional) `loc` parameter is used as the initial guess + for the root finder; the `scale` parameter and any other parameters + for the optimizer are ignored.\n\n""") + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + data, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + + def scale_mle(loc): + # Source: Statistical Distributions, 3rd Edition. Evans, Hastings, + # and Peacock (2000), Page 175 + return (np.sum((data - loc) ** 2) / (2 * len(data))) ** .5 + + def loc_mle(loc): + # This implicit equation for `loc` is used when + # both `loc` and `scale` are free. + xm = data - loc + s1 = xm.sum() + s2 = (xm**2).sum() + s3 = (1/xm).sum() + return s1 - s2/(2*len(data))*s3 + + def loc_mle_scale_fixed(loc, scale=fscale): + # This implicit equation for `loc` is used when + # `scale` is fixed but `loc` is not. + xm = data - loc + return xm.sum() - scale**2 * (1/xm).sum() + + if floc is not None: + # `loc` is fixed, analytically determine `scale`. + if np.any(data - floc <= 0): + raise FitDataError("rayleigh", lower=1, upper=np.inf) + else: + return floc, scale_mle(floc) + + # Account for user provided guess of `loc`. + loc0 = kwds.get('loc') + if loc0 is None: + # Use _fitstart to estimate loc; ignore the returned scale. + loc0 = self._fitstart(data)[0] + + fun = loc_mle if fscale is None else loc_mle_scale_fixed + rbrack = np.nextafter(np.min(data), -np.inf) + lbrack = _get_left_bracket(fun, rbrack) + res = optimize.root_scalar(fun, bracket=(lbrack, rbrack)) + if not res.converged: + raise FitSolverError(res.flag) + loc = res.root + scale = fscale or scale_mle(loc) + return loc, scale + + +rayleigh = rayleigh_gen(a=0.0, name="rayleigh") + + +class reciprocal_gen(rv_continuous): + r"""A loguniform or reciprocal continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for this class is: + + .. math:: + + f(x, a, b) = \frac{1}{x \log(b/a)} + + for :math:`a \le x \le b`, :math:`b > a > 0`. This class takes + :math:`a` and :math:`b` as shape parameters. + + %(after_notes)s + + %(example)s + + This doesn't show the equal probability of ``0.01``, ``0.1`` and + ``1``. This is best when the x-axis is log-scaled: + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + >>> ax.hist(np.log10(r)) + >>> ax.set_ylabel("Frequency") + >>> ax.set_xlabel("Value of random variable") + >>> ax.xaxis.set_major_locator(plt.FixedLocator([-2, -1, 0])) + >>> ticks = ["$10^{{ {} }}$".format(i) for i in [-2, -1, 0]] + >>> ax.set_xticklabels(ticks) # doctest: +SKIP + >>> plt.show() + + This random variable will be log-uniform regardless of the base chosen for + ``a`` and ``b``. Let's specify with base ``2`` instead: + + >>> rvs = %(name)s(2**-2, 2**0).rvs(size=1000) + + Values of ``1/4``, ``1/2`` and ``1`` are equally likely with this random + variable. Here's the histogram: + + >>> fig, ax = plt.subplots(1, 1) + >>> ax.hist(np.log2(rvs)) + >>> ax.set_ylabel("Frequency") + >>> ax.set_xlabel("Value of random variable") + >>> ax.xaxis.set_major_locator(plt.FixedLocator([-2, -1, 0])) + >>> ticks = ["$2^{{ {} }}$".format(i) for i in [-2, -1, 0]] + >>> ax.set_xticklabels(ticks) # doctest: +SKIP + >>> plt.show() + + """ + def _argcheck(self, a, b): + return (a > 0) & (b > a) + + def _shape_info(self): + ia = _ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = _ShapeInfo("b", False, (0, np.inf), (False, False)) + return [ia, ib] + + def _fitstart(self, data): + if isinstance(data, CensoredData): + data = data._uncensor() + # Reasonable, since support is [a, b] + return super()._fitstart(data, args=(np.min(data), np.max(data))) + + def _get_support(self, a, b): + return a, b + + def _pdf(self, x, a, b): + # reciprocal.pdf(x, a, b) = 1 / (x*(log(b) - log(a))) + return np.exp(self._logpdf(x, a, b)) + + def _logpdf(self, x, a, b): + return -np.log(x) - np.log(np.log(b) - np.log(a)) + + def _cdf(self, x, a, b): + return (np.log(x)-np.log(a)) / (np.log(b) - np.log(a)) + + def _ppf(self, q, a, b): + return np.exp(np.log(a) + q*(np.log(b) - np.log(a))) + + def _munp(self, n, a, b): + t1 = 1 / (np.log(b) - np.log(a)) / n + t2 = np.real(np.exp(_log_diff(n * np.log(b), n*np.log(a)))) + return t1 * t2 + + def _entropy(self, a, b): + return 0.5*(np.log(a) + np.log(b)) + np.log(np.log(b) - np.log(a)) + + fit_note = """\ + `loguniform`/`reciprocal` is over-parameterized. `fit` automatically + fixes `scale` to 1 unless `fscale` is provided by the user.\n\n""" + + @extend_notes_in_docstring(rv_continuous, notes=fit_note) + def fit(self, data, *args, **kwds): + fscale = kwds.pop('fscale', 1) + return super().fit(data, *args, fscale=fscale, **kwds) + + # Details related to the decision of not defining + # the survival function for this distribution can be + # found in the PR: https://github.com/scipy/scipy/pull/18614 + + +loguniform = reciprocal_gen(name="loguniform") +reciprocal = reciprocal_gen(name="reciprocal") + + +class rice_gen(rv_continuous): + r"""A Rice continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `rice` is: + + .. math:: + + f(x, b) = x \exp(- \frac{x^2 + b^2}{2}) I_0(x b) + + for :math:`x >= 0`, :math:`b > 0`. :math:`I_0` is the modified Bessel + function of order zero (`scipy.special.i0`). + + `rice` takes ``b`` as a shape parameter for :math:`b`. + + %(after_notes)s + + The Rice distribution describes the length, :math:`r`, of a 2-D vector with + components :math:`(U+u, V+v)`, where :math:`U, V` are constant, :math:`u, + v` are independent Gaussian random variables with standard deviation + :math:`s`. Let :math:`R = \sqrt{U^2 + V^2}`. Then the pdf of :math:`r` is + ``rice.pdf(x, R/s, scale=s)``. + + %(example)s + + """ + def _argcheck(self, b): + return b >= 0 + + def _shape_info(self): + return [_ShapeInfo("b", False, (0, np.inf), (True, False))] + + def _rvs(self, b, size=None, random_state=None): + # https://en.wikipedia.org/wiki/Rice_distribution + t = b/np.sqrt(2) + random_state.standard_normal(size=(2,) + size) + return np.sqrt((t*t).sum(axis=0)) + + def _cdf(self, x, b): + return sc.chndtr(np.square(x), 2, np.square(b)) + + def _ppf(self, q, b): + return np.sqrt(sc.chndtrix(q, 2, np.square(b))) + + def _pdf(self, x, b): + # rice.pdf(x, b) = x * exp(-(x**2+b**2)/2) * I[0](x*b) + # + # We use (x**2 + b**2)/2 = ((x-b)**2)/2 + xb. + # The factor of np.exp(-xb) is then included in the i0e function + # in place of the modified Bessel function, i0, improving + # numerical stability for large values of xb. + return x * np.exp(-(x-b)*(x-b)/2.0) * sc.i0e(x*b) + + def _munp(self, n, b): + nd2 = n/2.0 + n1 = 1 + nd2 + b2 = b*b/2.0 + return (2.0**(nd2) * np.exp(-b2) * sc.gamma(n1) * + sc.hyp1f1(n1, 1, b2)) + + +rice = rice_gen(a=0.0, name="rice") + + +class recipinvgauss_gen(rv_continuous): + r"""A reciprocal inverse Gaussian continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `recipinvgauss` is: + + .. math:: + + f(x, \mu) = \frac{1}{\sqrt{2\pi x}} + \exp\left(\frac{-(1-\mu x)^2}{2\mu^2x}\right) + + for :math:`x \ge 0`. + + `recipinvgauss` takes ``mu`` as a shape parameter for :math:`\mu`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("mu", False, (0, np.inf), (False, False))] + + def _pdf(self, x, mu): + # recipinvgauss.pdf(x, mu) = + # 1/sqrt(2*pi*x) * exp(-(1-mu*x)**2/(2*x*mu**2)) + return np.exp(self._logpdf(x, mu)) + + def _logpdf(self, x, mu): + return _lazywhere(x > 0, (x, mu), + lambda x, mu: (-(1 - mu*x)**2.0 / (2*x*mu**2.0) + - 0.5*np.log(2*np.pi*x)), + fillvalue=-np.inf) + + def _cdf(self, x, mu): + trm1 = 1.0/mu - x + trm2 = 1.0/mu + x + isqx = 1.0/np.sqrt(x) + return _norm_cdf(-isqx*trm1) - np.exp(2.0/mu)*_norm_cdf(-isqx*trm2) + + def _sf(self, x, mu): + trm1 = 1.0/mu - x + trm2 = 1.0/mu + x + isqx = 1.0/np.sqrt(x) + return _norm_cdf(isqx*trm1) + np.exp(2.0/mu)*_norm_cdf(-isqx*trm2) + + def _rvs(self, mu, size=None, random_state=None): + return 1.0/random_state.wald(mu, 1.0, size=size) + + +recipinvgauss = recipinvgauss_gen(a=0.0, name='recipinvgauss') + + +class semicircular_gen(rv_continuous): + r"""A semicircular continuous random variable. + + %(before_notes)s + + See Also + -------- + rdist + + Notes + ----- + The probability density function for `semicircular` is: + + .. math:: + + f(x) = \frac{2}{\pi} \sqrt{1-x^2} + + for :math:`-1 \le x \le 1`. + + The distribution is a special case of `rdist` with `c = 3`. + + %(after_notes)s + + References + ---------- + .. [1] "Wigner semicircle distribution", + https://en.wikipedia.org/wiki/Wigner_semicircle_distribution + + %(example)s + + """ + def _shape_info(self): + return [] + + def _pdf(self, x): + return 2.0/np.pi*np.sqrt(1-x*x) + + def _logpdf(self, x): + return np.log(2/np.pi) + 0.5*sc.log1p(-x*x) + + def _cdf(self, x): + return 0.5+1.0/np.pi*(x*np.sqrt(1-x*x) + np.arcsin(x)) + + def _ppf(self, q): + return rdist._ppf(q, 3) + + def _rvs(self, size=None, random_state=None): + # generate values uniformly distributed on the area under the pdf + # (semi-circle) by randomly generating the radius and angle + r = np.sqrt(random_state.uniform(size=size)) + a = np.cos(np.pi * random_state.uniform(size=size)) + return r * a + + def _stats(self): + return 0, 0.25, 0, -1.0 + + def _entropy(self): + return 0.64472988584940017414 + + +semicircular = semicircular_gen(a=-1.0, b=1.0, name="semicircular") + + +class skewcauchy_gen(rv_continuous): + r"""A skewed Cauchy random variable. + + %(before_notes)s + + See Also + -------- + cauchy : Cauchy distribution + + Notes + ----- + + The probability density function for `skewcauchy` is: + + .. math:: + + f(x) = \frac{1}{\pi \left(\frac{x^2}{\left(a\, \text{sign}(x) + 1 + \right)^2} + 1 \right)} + + for a real number :math:`x` and skewness parameter :math:`-1 < a < 1`. + + When :math:`a=0`, the distribution reduces to the usual Cauchy + distribution. + + %(after_notes)s + + References + ---------- + .. [1] "Skewed generalized *t* distribution", Wikipedia + https://en.wikipedia.org/wiki/Skewed_generalized_t_distribution#Skewed_Cauchy_distribution + + %(example)s + + """ + def _argcheck(self, a): + return np.abs(a) < 1 + + def _shape_info(self): + return [_ShapeInfo("a", False, (-1.0, 1.0), (False, False))] + + def _pdf(self, x, a): + return 1 / (np.pi * (x**2 / (a * np.sign(x) + 1)**2 + 1)) + + def _cdf(self, x, a): + return np.where(x <= 0, + (1 - a) / 2 + (1 - a) / np.pi * np.arctan(x / (1 - a)), + (1 - a) / 2 + (1 + a) / np.pi * np.arctan(x / (1 + a))) + + def _ppf(self, x, a): + i = x < self._cdf(0, a) + return np.where(i, + np.tan(np.pi / (1 - a) * (x - (1 - a) / 2)) * (1 - a), + np.tan(np.pi / (1 + a) * (x - (1 - a) / 2)) * (1 + a)) + + def _stats(self, a, moments='mvsk'): + return np.nan, np.nan, np.nan, np.nan + + def _fitstart(self, data): + # Use 0 as the initial guess of the skewness shape parameter. + # For the location and scale, estimate using the median and + # quartiles. + if isinstance(data, CensoredData): + data = data._uncensor() + p25, p50, p75 = np.percentile(data, [25, 50, 75]) + return 0.0, p50, (p75 - p25)/2 + + +skewcauchy = skewcauchy_gen(name='skewcauchy') + + +class skewnorm_gen(rv_continuous): + r"""A skew-normal random variable. + + %(before_notes)s + + Notes + ----- + The pdf is:: + + skewnorm.pdf(x, a) = 2 * norm.pdf(x) * norm.cdf(a*x) + + `skewnorm` takes a real number :math:`a` as a skewness parameter + When ``a = 0`` the distribution is identical to a normal distribution + (`norm`). `rvs` implements the method of [1]_. + + %(after_notes)s + + %(example)s + + References + ---------- + .. [1] A. Azzalini and A. Capitanio (1999). Statistical applications of + the multivariate skew-normal distribution. J. Roy. Statist. Soc., + B 61, 579-602. :arxiv:`0911.2093` + + """ + def _argcheck(self, a): + return np.isfinite(a) + + def _shape_info(self): + return [_ShapeInfo("a", False, (-np.inf, np.inf), (False, False))] + + def _pdf(self, x, a): + return _lazywhere( + a == 0, (x, a), lambda x, a: _norm_pdf(x), + f2=lambda x, a: 2.*_norm_pdf(x)*_norm_cdf(a*x) + ) + + def _logpdf(self, x, a): + return _lazywhere( + a == 0, (x, a), lambda x, a: _norm_logpdf(x), + f2=lambda x, a: np.log(2)+_norm_logpdf(x)+_norm_logcdf(a*x), + ) + + def _cdf(self, x, a): + a = np.atleast_1d(a) + cdf = _boost._skewnorm_cdf(x, 0, 1, a) + # for some reason, a isn't broadcasted if some of x are invalid + a = np.broadcast_to(a, cdf.shape) + # Boost is not accurate in left tail when a > 0 + i_small_cdf = (cdf < 1e-6) & (a > 0) + cdf[i_small_cdf] = super()._cdf(x[i_small_cdf], a[i_small_cdf]) + return np.clip(cdf, 0, 1) + + def _ppf(self, x, a): + return _boost._skewnorm_ppf(x, 0, 1, a) + + def _sf(self, x, a): + # Boost's SF is implemented this way. Use whatever customizations + # we made in the _cdf. + return self._cdf(-x, -a) + + def _isf(self, x, a): + return _boost._skewnorm_isf(x, 0, 1, a) + + def _rvs(self, a, size=None, random_state=None): + u0 = random_state.normal(size=size) + v = random_state.normal(size=size) + d = a/np.sqrt(1 + a**2) + u1 = d*u0 + v*np.sqrt(1 - d**2) + return np.where(u0 >= 0, u1, -u1) + + def _stats(self, a, moments='mvsk'): + output = [None, None, None, None] + const = np.sqrt(2/np.pi) * a/np.sqrt(1 + a**2) + + if 'm' in moments: + output[0] = const + if 'v' in moments: + output[1] = 1 - const**2 + if 's' in moments: + output[2] = ((4 - np.pi)/2) * (const/np.sqrt(1 - const**2))**3 + if 'k' in moments: + output[3] = (2*(np.pi - 3)) * (const**4/(1 - const**2)**2) + + return output + + # For odd order, the each noncentral moment of the skew-normal distribution + # with location 0 and scale 1 can be expressed as a polynomial in delta, + # where delta = a/sqrt(1 + a**2) and `a` is the skew-normal shape + # parameter. The dictionary _skewnorm_odd_moments defines those + # polynomials for orders up to 19. The dict is implemented as a cached + # property to reduce the impact of the creation of the dict on import time. + @cached_property + def _skewnorm_odd_moments(self): + skewnorm_odd_moments = { + 1: Polynomial([1]), + 3: Polynomial([3, -1]), + 5: Polynomial([15, -10, 3]), + 7: Polynomial([105, -105, 63, -15]), + 9: Polynomial([945, -1260, 1134, -540, 105]), + 11: Polynomial([10395, -17325, 20790, -14850, 5775, -945]), + 13: Polynomial([135135, -270270, 405405, -386100, 225225, -73710, + 10395]), + 15: Polynomial([2027025, -4729725, 8513505, -10135125, 7882875, + -3869775, 1091475, -135135]), + 17: Polynomial([34459425, -91891800, 192972780, -275675400, + 268017750, -175429800, 74220300, -18378360, + 2027025]), + 19: Polynomial([654729075, -1964187225, 4714049340, -7856748900, + 9166207050, -7499623950, 4230557100, -1571349780, + 346621275, -34459425]), + } + return skewnorm_odd_moments + + def _munp(self, order, a): + if order & 1: + if order > 19: + raise NotImplementedError("skewnorm noncentral moments not " + "implemented for odd orders greater " + "than 19.") + # Use the precomputed polynomials that were derived from the + # moment generating function. + delta = a/np.sqrt(1 + a**2) + return (delta * self._skewnorm_odd_moments[order](delta**2) + * _SQRT_2_OVER_PI) + else: + # For even order, the moment is just (order-1)!!, where !! is the + # notation for the double factorial; for an odd integer m, m!! is + # m*(m-2)*...*3*1. + # We could use special.factorial2, but we know the argument is odd, + # so avoid the overhead of that function and compute the result + # directly here. + return sc.gamma((order + 1)/2) * 2**(order/2) / _SQRT_PI + + @extend_notes_in_docstring(rv_continuous, notes="""\ + If ``method='mm'``, parameters fixed by the user are respected, and the + remaining parameters are used to match distribution and sample moments + where possible. For example, if the user fixes the location with + ``floc``, the parameters will only match the distribution skewness and + variance to the sample skewness and variance; no attempt will be made + to match the means or minimize a norm of the errors. + Note that the maximum possible skewness magnitude of a + `scipy.stats.skewnorm` distribution is approximately 0.9952717; if the + magnitude of the data's sample skewness exceeds this, the returned + shape parameter ``a`` will be infinite. + \n\n""") + def fit(self, data, *args, **kwds): + if kwds.pop("superfit", False): + return super().fit(data, *args, **kwds) + if isinstance(data, CensoredData): + if data.num_censored() == 0: + data = data._uncensor() + else: + return super().fit(data, *args, **kwds) + + # this extracts fixed shape, location, and scale however they + # are specified, and also leaves them in `kwds` + data, fa, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + method = kwds.get("method", "mle").lower() + + # See https://en.wikipedia.org/wiki/Skew_normal_distribution for + # moment formulas. + def skew_d(d): # skewness in terms of delta + return (4-np.pi)/2 * ((d * np.sqrt(2 / np.pi))**3 + / (1 - 2*d**2 / np.pi)**(3/2)) + def d_skew(skew): # delta in terms of skewness + s_23 = np.abs(skew)**(2/3) + return np.sign(skew) * np.sqrt( + np.pi/2 * s_23 / (s_23 + ((4 - np.pi)/2)**(2/3)) + ) + + # If method is method of moments, we don't need the user's guesses. + # Otherwise, extract the guesses from args and kwds. + if method == "mm": + a, loc, scale = None, None, None + else: + a = args[0] if len(args) else None + loc = kwds.pop('loc', None) + scale = kwds.pop('scale', None) + + if fa is None and a is None: # not fixed and no guess: use MoM + # Solve for a that matches sample distribution skewness to sample + # skewness. + s = stats.skew(data) + if method == 'mle': + # For MLE initial conditions, clip skewness to a large but + # reasonable value in case the data skewness is out-of-range. + s = np.clip(s, -0.99, 0.99) + else: + s_max = skew_d(1) + s = np.clip(s, -s_max, s_max) + d = d_skew(s) + with np.errstate(divide='ignore'): + a = np.sqrt(np.divide(d**2, (1-d**2)))*np.sign(s) + else: + a = fa if fa is not None else a + d = a / np.sqrt(1 + a**2) + + if fscale is None and scale is None: + v = np.var(data) + scale = np.sqrt(v / (1 - 2*d**2/np.pi)) + elif fscale is not None: + scale = fscale + + if floc is None and loc is None: + m = np.mean(data) + loc = m - scale*d*np.sqrt(2/np.pi) + elif floc is not None: + loc = floc + + if method == 'mm': + return a, loc, scale + else: + # At this point, parameter "guesses" may equal the fixed parameters + # in kwds. No harm in passing them as guesses, too. + return super().fit(data, a, loc=loc, scale=scale, **kwds) + + +skewnorm = skewnorm_gen(name='skewnorm') + + +class trapezoid_gen(rv_continuous): + r"""A trapezoidal continuous random variable. + + %(before_notes)s + + Notes + ----- + The trapezoidal distribution can be represented with an up-sloping line + from ``loc`` to ``(loc + c*scale)``, then constant to ``(loc + d*scale)`` + and then downsloping from ``(loc + d*scale)`` to ``(loc+scale)``. This + defines the trapezoid base from ``loc`` to ``(loc+scale)`` and the flat + top from ``c`` to ``d`` proportional to the position along the base + with ``0 <= c <= d <= 1``. When ``c=d``, this is equivalent to `triang` + with the same values for `loc`, `scale` and `c`. + The method of [1]_ is used for computing moments. + + `trapezoid` takes :math:`c` and :math:`d` as shape parameters. + + %(after_notes)s + + The standard form is in the range [0, 1] with c the mode. + The location parameter shifts the start to `loc`. + The scale parameter changes the width from 1 to `scale`. + + %(example)s + + References + ---------- + .. [1] Kacker, R.N. and Lawrence, J.F. (2007). Trapezoidal and triangular + distributions for Type B evaluation of standard uncertainty. + Metrologia 44, 117-127. :doi:`10.1088/0026-1394/44/2/003` + + + """ + def _argcheck(self, c, d): + return (c >= 0) & (c <= 1) & (d >= 0) & (d <= 1) & (d >= c) + + def _shape_info(self): + ic = _ShapeInfo("c", False, (0, 1.0), (True, True)) + id = _ShapeInfo("d", False, (0, 1.0), (True, True)) + return [ic, id] + + def _pdf(self, x, c, d): + u = 2 / (d-c+1) + + return _lazyselect([x < c, + (c <= x) & (x <= d), + x > d], + [lambda x, c, d, u: u * x / c, + lambda x, c, d, u: u, + lambda x, c, d, u: u * (1-x) / (1-d)], + (x, c, d, u)) + + def _cdf(self, x, c, d): + return _lazyselect([x < c, + (c <= x) & (x <= d), + x > d], + [lambda x, c, d: x**2 / c / (d-c+1), + lambda x, c, d: (c + 2 * (x-c)) / (d-c+1), + lambda x, c, d: 1-((1-x) ** 2 + / (d-c+1) / (1-d))], + (x, c, d)) + + def _ppf(self, q, c, d): + qc, qd = self._cdf(c, c, d), self._cdf(d, c, d) + condlist = [q < qc, q <= qd, q > qd] + choicelist = [np.sqrt(q * c * (1 + d - c)), + 0.5 * q * (1 + d - c) + 0.5 * c, + 1 - np.sqrt((1 - q) * (d - c + 1) * (1 - d))] + return np.select(condlist, choicelist) + + def _munp(self, n, c, d): + # Using the parameterization from Kacker, 2007, with + # a=bottom left, c=top left, d=top right, b=bottom right, then + # E[X^n] = h/(n+1)/(n+2) [(b^{n+2}-d^{n+2})/(b-d) + # - ((c^{n+2} - a^{n+2})/(c-a)] + # with h = 2/((b-a) - (d-c)). The corresponding parameterization + # in scipy, has a'=loc, c'=loc+c*scale, d'=loc+d*scale, b'=loc+scale, + # which for standard form reduces to a'=0, b'=1, c'=c, d'=d. + # Substituting into E[X^n] gives the bd' term as (1 - d^{n+2})/(1 - d) + # and the ac' term as c^{n-1} for the standard form. The bd' term has + # numerical difficulties near d=1, so replace (1 - d^{n+2})/(1-d) + # with expm1((n+2)*log(d))/(d-1). + # Testing with n=18 for c=(1e-30,1-eps) shows that this is stable. + # We still require an explicit test for d=1 to prevent divide by zero, + # and now a test for d=0 to prevent log(0). + ab_term = c**(n+1) + dc_term = _lazyselect( + [d == 0.0, (0.0 < d) & (d < 1.0), d == 1.0], + [lambda d: 1.0, + lambda d: np.expm1((n+2) * np.log(d)) / (d-1.0), + lambda d: n+2], + [d]) + val = 2.0 / (1.0+d-c) * (dc_term - ab_term) / ((n+1) * (n+2)) + return val + + def _entropy(self, c, d): + # Using the parameterization from Wikipedia (van Dorp, 2003) + # with a=bottom left, c=top left, d=top right, b=bottom right + # gives a'=loc, b'=loc+c*scale, c'=loc+d*scale, d'=loc+scale, + # which for loc=0, scale=1 is a'=0, b'=c, c'=d, d'=1. + # Substituting into the entropy formula from Wikipedia gives + # the following result. + return 0.5 * (1.0-d+c) / (1.0+d-c) + np.log(0.5 * (1.0+d-c)) + + +trapezoid = trapezoid_gen(a=0.0, b=1.0, name="trapezoid") +# Note: alias kept for backwards compatibility. Rename was done +# because trapz is a slur in colloquial English (see gh-12924). +trapz = trapezoid_gen(a=0.0, b=1.0, name="trapz") +if trapz.__doc__: + trapz.__doc__ = "trapz is an alias for `trapezoid`" + + +class triang_gen(rv_continuous): + r"""A triangular continuous random variable. + + %(before_notes)s + + Notes + ----- + The triangular distribution can be represented with an up-sloping line from + ``loc`` to ``(loc + c*scale)`` and then downsloping for ``(loc + c*scale)`` + to ``(loc + scale)``. + + `triang` takes ``c`` as a shape parameter for :math:`0 \le c \le 1`. + + %(after_notes)s + + The standard form is in the range [0, 1] with c the mode. + The location parameter shifts the start to `loc`. + The scale parameter changes the width from 1 to `scale`. + + %(example)s + + """ + def _rvs(self, c, size=None, random_state=None): + return random_state.triangular(0, c, 1, size) + + def _argcheck(self, c): + return (c >= 0) & (c <= 1) + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, 1.0), (True, True))] + + def _pdf(self, x, c): + # 0: edge case where c=0 + # 1: generalised case for x < c, don't use x <= c, as it doesn't cope + # with c = 0. + # 2: generalised case for x >= c, but doesn't cope with c = 1 + # 3: edge case where c=1 + r = _lazyselect([c == 0, + x < c, + (x >= c) & (c != 1), + c == 1], + [lambda x, c: 2 - 2 * x, + lambda x, c: 2 * x / c, + lambda x, c: 2 * (1 - x) / (1 - c), + lambda x, c: 2 * x], + (x, c)) + return r + + def _cdf(self, x, c): + r = _lazyselect([c == 0, + x < c, + (x >= c) & (c != 1), + c == 1], + [lambda x, c: 2*x - x*x, + lambda x, c: x * x / c, + lambda x, c: (x*x - 2*x + c) / (c-1), + lambda x, c: x * x], + (x, c)) + return r + + def _ppf(self, q, c): + return np.where(q < c, np.sqrt(c * q), 1-np.sqrt((1-c) * (1-q))) + + def _stats(self, c): + return ((c+1.0)/3.0, + (1.0-c+c*c)/18, + np.sqrt(2)*(2*c-1)*(c+1)*(c-2) / (5*np.power((1.0-c+c*c), 1.5)), + -3.0/5.0) + + def _entropy(self, c): + return 0.5-np.log(2) + + +triang = triang_gen(a=0.0, b=1.0, name="triang") + + +class truncexpon_gen(rv_continuous): + r"""A truncated exponential continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `truncexpon` is: + + .. math:: + + f(x, b) = \frac{\exp(-x)}{1 - \exp(-b)} + + for :math:`0 <= x <= b`. + + `truncexpon` takes ``b`` as a shape parameter for :math:`b`. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("b", False, (0, np.inf), (False, False))] + + def _get_support(self, b): + return self.a, b + + def _pdf(self, x, b): + # truncexpon.pdf(x, b) = exp(-x) / (1-exp(-b)) + return np.exp(-x)/(-sc.expm1(-b)) + + def _logpdf(self, x, b): + return -x - np.log(-sc.expm1(-b)) + + def _cdf(self, x, b): + return sc.expm1(-x)/sc.expm1(-b) + + def _ppf(self, q, b): + return -sc.log1p(q*sc.expm1(-b)) + + def _sf(self, x, b): + return (np.exp(-b) - np.exp(-x))/sc.expm1(-b) + + def _isf(self, q, b): + return -np.log(np.exp(-b) - q * sc.expm1(-b)) + + def _munp(self, n, b): + # wrong answer with formula, same as in continuous.pdf + # return sc.gamman+1)-sc.gammainc1+n, b) + if n == 1: + return (1-(b+1)*np.exp(-b))/(-sc.expm1(-b)) + elif n == 2: + return 2*(1-0.5*(b*b+2*b+2)*np.exp(-b))/(-sc.expm1(-b)) + else: + # return generic for higher moments + return super()._munp(n, b) + + def _entropy(self, b): + eB = np.exp(b) + return np.log(eB-1)+(1+eB*(b-1.0))/(1.0-eB) + + +truncexpon = truncexpon_gen(a=0.0, name='truncexpon') + + +# logsumexp trick for log(p + q) with only log(p) and log(q) +def _log_sum(log_p, log_q): + return sc.logsumexp([log_p, log_q], axis=0) + + +# same as above, but using -exp(x) = exp(x + πi) +def _log_diff(log_p, log_q): + return sc.logsumexp([log_p, log_q+np.pi*1j], axis=0) + + +def _log_gauss_mass(a, b): + """Log of Gaussian probability mass within an interval""" + a, b = np.broadcast_arrays(a, b) + + # Calculations in right tail are inaccurate, so we'll exploit the + # symmetry and work only in the left tail + case_left = b <= 0 + case_right = a > 0 + case_central = ~(case_left | case_right) + + def mass_case_left(a, b): + return _log_diff(_norm_logcdf(b), _norm_logcdf(a)) + + def mass_case_right(a, b): + return mass_case_left(-b, -a) + + def mass_case_central(a, b): + # Previously, this was implemented as: + # left_mass = mass_case_left(a, 0) + # right_mass = mass_case_right(0, b) + # return _log_sum(left_mass, right_mass) + # Catastrophic cancellation occurs as np.exp(log_mass) approaches 1. + # Correct for this with an alternative formulation. + # We're not concerned with underflow here: if only one term + # underflows, it was insignificant; if both terms underflow, + # the result can't accurately be represented in logspace anyway + # because sc.log1p(x) ~ x for small x. + return sc.log1p(-_norm_cdf(a) - _norm_cdf(-b)) + + # _lazyselect not working; don't care to debug it + out = np.full_like(a, fill_value=np.nan, dtype=np.complex128) + if a[case_left].size: + out[case_left] = mass_case_left(a[case_left], b[case_left]) + if a[case_right].size: + out[case_right] = mass_case_right(a[case_right], b[case_right]) + if a[case_central].size: + out[case_central] = mass_case_central(a[case_central], b[case_central]) + return np.real(out) # discard ~0j + + +class truncnorm_gen(rv_continuous): + r"""A truncated normal continuous random variable. + + %(before_notes)s + + Notes + ----- + This distribution is the normal distribution centered on ``loc`` (default + 0), with standard deviation ``scale`` (default 1), and truncated at ``a`` + and ``b`` *standard deviations* from ``loc``. For arbitrary ``loc`` and + ``scale``, ``a`` and ``b`` are *not* the abscissae at which the shifted + and scaled distribution is truncated. + + .. note:: + If ``a_trunc`` and ``b_trunc`` are the abscissae at which we wish + to truncate the distribution (as opposed to the number of standard + deviations from ``loc``), then we can calculate the distribution + parameters ``a`` and ``b`` as follows:: + + a, b = (a_trunc - loc) / scale, (b_trunc - loc) / scale + + This is a common point of confusion. For additional clarification, + please see the example below. + + %(example)s + + In the examples above, ``loc=0`` and ``scale=1``, so the plot is truncated + at ``a`` on the left and ``b`` on the right. However, suppose we were to + produce the same histogram with ``loc = 1`` and ``scale=0.5``. + + >>> loc, scale = 1, 0.5 + >>> rv = truncnorm(a, b, loc=loc, scale=scale) + >>> x = np.linspace(truncnorm.ppf(0.01, a, b), + ... truncnorm.ppf(0.99, a, b), 100) + >>> r = rv.rvs(size=1000) + + >>> fig, ax = plt.subplots(1, 1) + >>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + >>> ax.hist(r, density=True, bins='auto', histtype='stepfilled', alpha=0.2) + >>> ax.set_xlim(a, b) + >>> ax.legend(loc='best', frameon=False) + >>> plt.show() + + Note that the distribution is no longer appears to be truncated at + abscissae ``a`` and ``b``. That is because the *standard* normal + distribution is first truncated at ``a`` and ``b``, *then* the resulting + distribution is scaled by ``scale`` and shifted by ``loc``. If we instead + want the shifted and scaled distribution to be truncated at ``a`` and + ``b``, we need to transform these values before passing them as the + distribution parameters. + + >>> a_transformed, b_transformed = (a - loc) / scale, (b - loc) / scale + >>> rv = truncnorm(a_transformed, b_transformed, loc=loc, scale=scale) + >>> x = np.linspace(truncnorm.ppf(0.01, a, b), + ... truncnorm.ppf(0.99, a, b), 100) + >>> r = rv.rvs(size=10000) + + >>> fig, ax = plt.subplots(1, 1) + >>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + >>> ax.hist(r, density=True, bins='auto', histtype='stepfilled', alpha=0.2) + >>> ax.set_xlim(a-0.1, b+0.1) + >>> ax.legend(loc='best', frameon=False) + >>> plt.show() + """ + + def _argcheck(self, a, b): + return a < b + + def _shape_info(self): + ia = _ShapeInfo("a", False, (-np.inf, np.inf), (True, False)) + ib = _ShapeInfo("b", False, (-np.inf, np.inf), (False, True)) + return [ia, ib] + + def _fitstart(self, data): + # Reasonable, since support is [a, b] + if isinstance(data, CensoredData): + data = data._uncensor() + return super()._fitstart(data, args=(np.min(data), np.max(data))) + + def _get_support(self, a, b): + return a, b + + def _pdf(self, x, a, b): + return np.exp(self._logpdf(x, a, b)) + + def _logpdf(self, x, a, b): + return _norm_logpdf(x) - _log_gauss_mass(a, b) + + def _cdf(self, x, a, b): + return np.exp(self._logcdf(x, a, b)) + + def _logcdf(self, x, a, b): + x, a, b = np.broadcast_arrays(x, a, b) + logcdf = np.asarray(_log_gauss_mass(a, x) - _log_gauss_mass(a, b)) + i = logcdf > -0.1 # avoid catastrophic cancellation + if np.any(i): + logcdf[i] = np.log1p(-np.exp(self._logsf(x[i], a[i], b[i]))) + return logcdf + + def _sf(self, x, a, b): + return np.exp(self._logsf(x, a, b)) + + def _logsf(self, x, a, b): + x, a, b = np.broadcast_arrays(x, a, b) + logsf = np.asarray(_log_gauss_mass(x, b) - _log_gauss_mass(a, b)) + i = logsf > -0.1 # avoid catastrophic cancellation + if np.any(i): + logsf[i] = np.log1p(-np.exp(self._logcdf(x[i], a[i], b[i]))) + return logsf + + def _entropy(self, a, b): + A = _norm_cdf(a) + B = _norm_cdf(b) + Z = B - A + C = np.log(np.sqrt(2 * np.pi * np.e) * Z) + D = (a * _norm_pdf(a) - b * _norm_pdf(b)) / (2 * Z) + h = C + D + return h + + def _ppf(self, q, a, b): + q, a, b = np.broadcast_arrays(q, a, b) + + case_left = a < 0 + case_right = ~case_left + + def ppf_left(q, a, b): + log_Phi_x = _log_sum(_norm_logcdf(a), + np.log(q) + _log_gauss_mass(a, b)) + return sc.ndtri_exp(log_Phi_x) + + def ppf_right(q, a, b): + log_Phi_x = _log_sum(_norm_logcdf(-b), + np.log1p(-q) + _log_gauss_mass(a, b)) + return -sc.ndtri_exp(log_Phi_x) + + out = np.empty_like(q) + + q_left = q[case_left] + q_right = q[case_right] + + if q_left.size: + out[case_left] = ppf_left(q_left, a[case_left], b[case_left]) + if q_right.size: + out[case_right] = ppf_right(q_right, a[case_right], b[case_right]) + + return out + + def _isf(self, q, a, b): + # Mostly copy-paste of _ppf, but I think this is simpler than combining + q, a, b = np.broadcast_arrays(q, a, b) + + case_left = b < 0 + case_right = ~case_left + + def isf_left(q, a, b): + log_Phi_x = _log_diff(_norm_logcdf(b), + np.log(q) + _log_gauss_mass(a, b)) + return sc.ndtri_exp(np.real(log_Phi_x)) + + def isf_right(q, a, b): + log_Phi_x = _log_diff(_norm_logcdf(-a), + np.log1p(-q) + _log_gauss_mass(a, b)) + return -sc.ndtri_exp(np.real(log_Phi_x)) + + out = np.empty_like(q) + + q_left = q[case_left] + q_right = q[case_right] + + if q_left.size: + out[case_left] = isf_left(q_left, a[case_left], b[case_left]) + if q_right.size: + out[case_right] = isf_right(q_right, a[case_right], b[case_right]) + + return out + + def _munp(self, n, a, b): + def n_th_moment(n, a, b): + """ + Returns n-th moment. Defined only if n >= 0. + Function cannot broadcast due to the loop over n + """ + pA, pB = self._pdf(np.asarray([a, b]), a, b) + probs = [pA, -pB] + moments = [0, 1] + for k in range(1, n+1): + # a or b might be infinite, and the corresponding pdf value + # is 0 in that case, but nan is returned for the + # multiplication. However, as b->infinity, pdf(b)*b**k -> 0. + # So it is safe to use _lazywhere to avoid the nan. + vals = _lazywhere(probs, [probs, [a, b]], + lambda x, y: x * y**(k-1), fillvalue=0) + mk = np.sum(vals) + (k-1) * moments[-2] + moments.append(mk) + return moments[-1] + + return _lazywhere((n >= 0) & (a == a) & (b == b), (n, a, b), + np.vectorize(n_th_moment, otypes=[np.float64]), + np.nan) + + def _stats(self, a, b, moments='mv'): + pA, pB = self.pdf(np.array([a, b]), a, b) + + def _truncnorm_stats_scalar(a, b, pA, pB, moments): + m1 = pA - pB + mu = m1 + # use _lazywhere to avoid nan (See detailed comment in _munp) + probs = [pA, -pB] + vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y, + fillvalue=0) + m2 = 1 + np.sum(vals) + vals = _lazywhere(probs, [probs, [a-mu, b-mu]], lambda x, y: x*y, + fillvalue=0) + # mu2 = m2 - mu**2, but not as numerically stable as: + # mu2 = (a-mu)*pA - (b-mu)*pB + 1 + mu2 = 1 + np.sum(vals) + vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y**2, + fillvalue=0) + m3 = 2*m1 + np.sum(vals) + vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y**3, + fillvalue=0) + m4 = 3*m2 + np.sum(vals) + + mu3 = m3 + m1 * (-3*m2 + 2*m1**2) + g1 = mu3 / np.power(mu2, 1.5) + mu4 = m4 + m1*(-4*m3 + 3*m1*(2*m2 - m1**2)) + g2 = mu4 / mu2**2 - 3 + return mu, mu2, g1, g2 + + _truncnorm_stats = np.vectorize(_truncnorm_stats_scalar, + excluded=('moments',)) + return _truncnorm_stats(a, b, pA, pB, moments) + + +truncnorm = truncnorm_gen(name='truncnorm', momtype=1) + + +class truncpareto_gen(rv_continuous): + r"""An upper truncated Pareto continuous random variable. + + %(before_notes)s + + See Also + -------- + pareto : Pareto distribution + + Notes + ----- + The probability density function for `truncpareto` is: + + .. math:: + + f(x, b, c) = \frac{b}{1 - c^{-b}} \frac{1}{x^{b+1}} + + for :math:`b > 0`, :math:`c > 1` and :math:`1 \le x \le c`. + + `truncpareto` takes `b` and `c` as shape parameters for :math:`b` and + :math:`c`. + + Notice that the upper truncation value :math:`c` is defined in + standardized form so that random values of an unscaled, unshifted variable + are within the range ``[1, c]``. + If ``u_r`` is the upper bound to a scaled and/or shifted variable, + then ``c = (u_r - loc) / scale``. In other words, the support of the + distribution becomes ``(scale + loc) <= x <= (c*scale + loc)`` when + `scale` and/or `loc` are provided. + + %(after_notes)s + + References + ---------- + .. [1] Burroughs, S. M., and Tebbens S. F. + "Upper-truncated power laws in natural systems." + Pure and Applied Geophysics 158.4 (2001): 741-757. + + %(example)s + + """ + + def _shape_info(self): + ib = _ShapeInfo("b", False, (0.0, np.inf), (False, False)) + ic = _ShapeInfo("c", False, (1.0, np.inf), (False, False)) + return [ib, ic] + + def _argcheck(self, b, c): + return (b > 0.) & (c > 1.) + + def _get_support(self, b, c): + return self.a, c + + def _pdf(self, x, b, c): + return b * x**-(b+1) / (1 - 1/c**b) + + def _logpdf(self, x, b, c): + return np.log(b) - np.log(-np.expm1(-b*np.log(c))) - (b+1)*np.log(x) + + def _cdf(self, x, b, c): + return (1 - x**-b) / (1 - 1/c**b) + + def _logcdf(self, x, b, c): + return np.log1p(-x**-b) - np.log1p(-1/c**b) + + def _ppf(self, q, b, c): + return pow(1 - (1 - 1/c**b)*q, -1/b) + + def _sf(self, x, b, c): + return (x**-b - 1/c**b) / (1 - 1/c**b) + + def _logsf(self, x, b, c): + return np.log(x**-b - 1/c**b) - np.log1p(-1/c**b) + + def _isf(self, q, b, c): + return pow(1/c**b + (1 - 1/c**b)*q, -1/b) + + def _entropy(self, b, c): + return -(np.log(b/(1 - 1/c**b)) + + (b+1)*(np.log(c)/(c**b - 1) - 1/b)) + + def _munp(self, n, b, c): + if (n == b).all(): + return b*np.log(c) / (1 - 1/c**b) + else: + return b / (b-n) * (c**b - c**n) / (c**b - 1) + + def _fitstart(self, data): + if isinstance(data, CensoredData): + data = data._uncensor() + b, loc, scale = pareto.fit(data) + c = (max(data) - loc)/scale + return b, c, loc, scale + + @_call_super_mom + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + if kwds.pop("superfit", False): + return super().fit(data, *args, **kwds) + + def log_mean(x): + return np.mean(np.log(x)) + + def harm_mean(x): + return 1/np.mean(1/x) + + def get_b(c, loc, scale): + u = (data-loc)/scale + harm_m = harm_mean(u) + log_m = log_mean(u) + quot = (harm_m-1)/log_m + return (1 - (quot-1) / (quot - (1 - 1/c)*harm_m/np.log(c)))/log_m + + def get_c(loc, scale): + return (mx - loc)/scale + + def get_loc(fc, fscale): + if fscale: # (fscale and fc) or (fscale and not fc) + loc = mn - fscale + return loc + if fc: + loc = (fc*mn - mx)/(fc - 1) + return loc + + def get_scale(loc): + return mn - loc + + # Functions used for optimisation; partial derivatives of + # the Lagrangian, set to equal 0. + + def dL_dLoc(loc, b_=None): + # Partial derivative wrt location. + # Optimised upon when no parameters, or only b, are fixed. + scale = get_scale(loc) + c = get_c(loc, scale) + b = get_b(c, loc, scale) if b_ is None else b_ + harm_m = harm_mean((data - loc)/scale) + return 1 - (1 + (c - 1)/(c**(b+1) - c)) * (1 - 1/(b+1)) * harm_m + + def dL_dB(b, logc, logm): + # Partial derivative wrt b. + # Optimised upon whenever at least one parameter but b is fixed, + # and b is free. + return b - np.log1p(b*logc / (1 - b*logm)) / logc + + def fallback(data, *args, **kwargs): + # Should any issue arise, default to the general fit method. + return super(truncpareto_gen, self).fit(data, *args, **kwargs) + + parameters = _check_fit_input_parameters(self, data, args, kwds) + data, fb, fc, floc, fscale = parameters + mn, mx = data.min(), data.max() + mn_inf = np.nextafter(mn, -np.inf) + + if (fb is not None + and fc is not None + and floc is not None + and fscale is not None): + raise ValueError("All parameters fixed." + "There is nothing to optimize.") + elif fc is None and floc is None and fscale is None: + if fb is None: + def cond_b(loc): + # b is positive only if this function is positive + scale = get_scale(loc) + c = get_c(loc, scale) + harm_m = harm_mean((data - loc)/scale) + return (1 + 1/(c-1)) * np.log(c) / harm_m - 1 + + # This gives an upper bound on loc allowing for a positive b. + # Iteratively look for a bracket for root_scalar. + mn_inf = np.nextafter(mn, -np.inf) + rbrack = mn_inf + i = 0 + lbrack = rbrack - 1 + while ((lbrack > -np.inf) + and (cond_b(lbrack)*cond_b(rbrack) >= 0)): + i += 1 + lbrack = rbrack - np.power(2., i) + if not lbrack > -np.inf: + return fallback(data, *args, **kwds) + res = root_scalar(cond_b, bracket=(lbrack, rbrack)) + if not res.converged: + return fallback(data, *args, **kwds) + + # Determine the MLE for loc. + # Iteratively look for a bracket for root_scalar. + rbrack = res.root - 1e-3 # grad_loc is numerically ill-behaved + lbrack = rbrack - 1 + i = 0 + while ((lbrack > -np.inf) + and (dL_dLoc(lbrack)*dL_dLoc(rbrack) >= 0)): + i += 1 + lbrack = rbrack - np.power(2., i) + if not lbrack > -np.inf: + return fallback(data, *args, **kwds) + res = root_scalar(dL_dLoc, bracket=(lbrack, rbrack)) + if not res.converged: + return fallback(data, *args, **kwds) + loc = res.root + scale = get_scale(loc) + c = get_c(loc, scale) + b = get_b(c, loc, scale) + + std_data = (data - loc)/scale + # The expression of b relies on b being bounded above. + up_bound_b = min(1/log_mean(std_data), + 1/(harm_mean(std_data)-1)) + if not (b < up_bound_b): + return fallback(data, *args, **kwds) + else: + # We know b is positive (or a FitError will be triggered) + # so we let loc get close to min(data). + rbrack = mn_inf + lbrack = mn_inf - 1 + i = 0 + # Iteratively look for a bracket for root_scalar. + while (lbrack > -np.inf + and (dL_dLoc(lbrack, fb) + * dL_dLoc(rbrack, fb) >= 0)): + i += 1 + lbrack = rbrack - 2**i + if not lbrack > -np.inf: + return fallback(data, *args, **kwds) + res = root_scalar(dL_dLoc, (fb,), + bracket=(lbrack, rbrack)) + if not res.converged: + return fallback(data, *args, **kwds) + loc = res.root + scale = get_scale(loc) + c = get_c(loc, scale) + b = fb + else: + # At least one of the parameters determining the support is fixed; + # the others then have analytical expressions from the constraints. + # The completely determined case (fixed c, loc and scale) + # has to be checked for not overflowing the support. + # If not fixed, b has to be determined numerically. + loc = floc if floc is not None else get_loc(fc, fscale) + scale = fscale or get_scale(loc) + c = fc or get_c(loc, scale) + + # Unscaled, translated values should be positive when the location + # is fixed. If it is not the case, we end up with negative `scale` + # and `c`, which would trigger a FitError before exiting the + # method. + if floc is not None and data.min() - floc < 0: + raise FitDataError("truncpareto", lower=1, upper=c) + + # Standardised values should be within the distribution support + # when all parameters controlling it are fixed. If it not the case, + # `fc` is overridden by `c` determined from `floc` and `fscale` when + # raising the exception. + if fc and (floc is not None) and fscale: + if data.max() > fc*fscale + floc: + raise FitDataError("truncpareto", lower=1, + upper=get_c(loc, scale)) + + # The other constraints should be automatically satisfied + # from the analytical expressions of the parameters. + # If fc or fscale are respectively less than one or less than 0, + # a FitError is triggered before exiting the method. + + if fb is None: + std_data = (data - loc)/scale + logm = log_mean(std_data) + logc = np.log(c) + # Condition for a positive root to exist. + if not (2*logm < logc): + return fallback(data, *args, **kwds) + + lbrack = 1/logm + 1/(logm - logc) + rbrack = np.nextafter(1/logm, 0) + try: + res = root_scalar(dL_dB, (logc, logm), + bracket=(lbrack, rbrack)) + # we should then never get there + if not res.converged: + return fallback(data, *args, **kwds) + b = res.root + except ValueError: + b = rbrack + else: + b = fb + + # The distribution requires that `scale+loc <= data <= c*scale+loc`. + # To avoid numerical issues, some tuning may be necessary. + # We adjust `scale` to satisfy the lower bound, and we adjust + # `c` to satisfy the upper bound. + if not (scale+loc) < mn: + if fscale: + loc = np.nextafter(loc, -np.inf) + else: + scale = get_scale(loc) + scale = np.nextafter(scale, 0) + if not (c*scale+loc) > mx: + c = get_c(loc, scale) + c = np.nextafter(c, np.inf) + + if not (np.all(self._argcheck(b, c)) and (scale > 0)): + return fallback(data, *args, **kwds) + + params_override = b, c, loc, scale + if floc is None and fscale is None: + # Based on testing in gh-16782, the following methods are only + # reliable if either `floc` or `fscale` are provided. They are + # fast, though, so might as well see if they are better than the + # generic method. + params_super = fallback(data, *args, **kwds) + nllf_override = self.nnlf(params_override, data) + nllf_super = self.nnlf(params_super, data) + if nllf_super < nllf_override: + return params_super + + return params_override + + +truncpareto = truncpareto_gen(a=1.0, name='truncpareto') + + +class tukeylambda_gen(rv_continuous): + r"""A Tukey-Lamdba continuous random variable. + + %(before_notes)s + + Notes + ----- + A flexible distribution, able to represent and interpolate between the + following distributions: + + - Cauchy (:math:`lambda = -1`) + - logistic (:math:`lambda = 0`) + - approx Normal (:math:`lambda = 0.14`) + - uniform from -1 to 1 (:math:`lambda = 1`) + + `tukeylambda` takes a real number :math:`lambda` (denoted ``lam`` + in the implementation) as a shape parameter. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, lam): + return np.isfinite(lam) + + def _shape_info(self): + return [_ShapeInfo("lam", False, (-np.inf, np.inf), (False, False))] + + def _pdf(self, x, lam): + Fx = np.asarray(sc.tklmbda(x, lam)) + Px = Fx**(lam-1.0) + (np.asarray(1-Fx))**(lam-1.0) + Px = 1.0/np.asarray(Px) + return np.where((lam <= 0) | (abs(x) < 1.0/np.asarray(lam)), Px, 0.0) + + def _cdf(self, x, lam): + return sc.tklmbda(x, lam) + + def _ppf(self, q, lam): + return sc.boxcox(q, lam) - sc.boxcox1p(-q, lam) + + def _stats(self, lam): + return 0, _tlvar(lam), 0, _tlkurt(lam) + + def _entropy(self, lam): + def integ(p): + return np.log(pow(p, lam-1)+pow(1-p, lam-1)) + return integrate.quad(integ, 0, 1)[0] + + +tukeylambda = tukeylambda_gen(name='tukeylambda') + + +class FitUniformFixedScaleDataError(FitDataError): + def __init__(self, ptp, fscale): + self.args = ( + "Invalid values in `data`. Maximum likelihood estimation with " + "the uniform distribution and fixed scale requires that " + f"np.ptp(data) <= fscale, but np.ptp(data) = {ptp} and " + f"fscale = {fscale}." + ) + + +class uniform_gen(rv_continuous): + r"""A uniform continuous random variable. + + In the standard form, the distribution is uniform on ``[0, 1]``. Using + the parameters ``loc`` and ``scale``, one obtains the uniform distribution + on ``[loc, loc + scale]``. + + %(before_notes)s + + %(example)s + + """ + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.uniform(0.0, 1.0, size) + + def _pdf(self, x): + return 1.0*(x == x) + + def _cdf(self, x): + return x + + def _ppf(self, q): + return q + + def _stats(self): + return 0.5, 1.0/12, 0, -1.2 + + def _entropy(self): + return 0.0 + + @_call_super_mom + def fit(self, data, *args, **kwds): + """ + Maximum likelihood estimate for the location and scale parameters. + + `uniform.fit` uses only the following parameters. Because exact + formulas are used, the parameters related to optimization that are + available in the `fit` method of other distributions are ignored + here. The only positional argument accepted is `data`. + + Parameters + ---------- + data : array_like + Data to use in calculating the maximum likelihood estimate. + floc : float, optional + Hold the location parameter fixed to the specified value. + fscale : float, optional + Hold the scale parameter fixed to the specified value. + + Returns + ------- + loc, scale : float + Maximum likelihood estimates for the location and scale. + + Notes + ----- + An error is raised if `floc` is given and any values in `data` are + less than `floc`, or if `fscale` is given and `fscale` is less + than ``data.max() - data.min()``. An error is also raised if both + `floc` and `fscale` are given. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import uniform + + We'll fit the uniform distribution to `x`: + + >>> x = np.array([2, 2.5, 3.1, 9.5, 13.0]) + + For a uniform distribution MLE, the location is the minimum of the + data, and the scale is the maximum minus the minimum. + + >>> loc, scale = uniform.fit(x) + >>> loc + 2.0 + >>> scale + 11.0 + + If we know the data comes from a uniform distribution where the support + starts at 0, we can use `floc=0`: + + >>> loc, scale = uniform.fit(x, floc=0) + >>> loc + 0.0 + >>> scale + 13.0 + + Alternatively, if we know the length of the support is 12, we can use + `fscale=12`: + + >>> loc, scale = uniform.fit(x, fscale=12) + >>> loc + 1.5 + >>> scale + 12.0 + + In that last example, the support interval is [1.5, 13.5]. This + solution is not unique. For example, the distribution with ``loc=2`` + and ``scale=12`` has the same likelihood as the one above. When + `fscale` is given and it is larger than ``data.max() - data.min()``, + the parameters returned by the `fit` method center the support over + the interval ``[data.min(), data.max()]``. + + """ + if len(args) > 0: + raise TypeError("Too many arguments.") + + floc = kwds.pop('floc', None) + fscale = kwds.pop('fscale', None) + + _remove_optimizer_parameters(kwds) + + if floc is not None and fscale is not None: + # This check is for consistency with `rv_continuous.fit`. + raise ValueError("All parameters fixed. There is nothing to " + "optimize.") + + data = np.asarray(data) + + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + # MLE for the uniform distribution + # -------------------------------- + # The PDF is + # + # f(x, loc, scale) = {1/scale for loc <= x <= loc + scale + # {0 otherwise} + # + # The likelihood function is + # L(x, loc, scale) = (1/scale)**n + # where n is len(x), assuming loc <= x <= loc + scale for all x. + # The log-likelihood is + # l(x, loc, scale) = -n*log(scale) + # The log-likelihood is maximized by making scale as small as possible, + # while keeping loc <= x <= loc + scale. So if neither loc nor scale + # are fixed, the log-likelihood is maximized by choosing + # loc = x.min() + # scale = np.ptp(x) + # If loc is fixed, it must be less than or equal to x.min(), and then + # the scale is + # scale = x.max() - loc + # If scale is fixed, it must not be less than np.ptp(x). If scale is + # greater than np.ptp(x), the solution is not unique. Note that the + # likelihood does not depend on loc, except for the requirement that + # loc <= x <= loc + scale. All choices of loc for which + # x.max() - scale <= loc <= x.min() + # have the same log-likelihood. In this case, we choose loc such that + # the support is centered over the interval [data.min(), data.max()]: + # loc = x.min() = 0.5*(scale - np.ptp(x)) + + if fscale is None: + # scale is not fixed. + if floc is None: + # loc is not fixed, scale is not fixed. + loc = data.min() + scale = np.ptp(data) + else: + # loc is fixed, scale is not fixed. + loc = floc + scale = data.max() - loc + if data.min() < loc: + raise FitDataError("uniform", lower=loc, upper=loc + scale) + else: + # loc is not fixed, scale is fixed. + ptp = np.ptp(data) + if ptp > fscale: + raise FitUniformFixedScaleDataError(ptp=ptp, fscale=fscale) + # If ptp < fscale, the ML estimate is not unique; see the comments + # above. We choose the distribution for which the support is + # centered over the interval [data.min(), data.max()]. + loc = data.min() - 0.5*(fscale - ptp) + scale = fscale + + # We expect the return values to be floating point, so ensure it + # by explicitly converting to float. + return float(loc), float(scale) + + +uniform = uniform_gen(a=0.0, b=1.0, name='uniform') + + +class vonmises_gen(rv_continuous): + r"""A Von Mises continuous random variable. + + %(before_notes)s + + See Also + -------- + scipy.stats.vonmises_fisher : Von-Mises Fisher distribution on a + hypersphere + + Notes + ----- + The probability density function for `vonmises` and `vonmises_line` is: + + .. math:: + + f(x, \kappa) = \frac{ \exp(\kappa \cos(x)) }{ 2 \pi I_0(\kappa) } + + for :math:`-\pi \le x \le \pi`, :math:`\kappa \ge 0`. :math:`I_0` is the + modified Bessel function of order zero (`scipy.special.i0`). + + `vonmises` is a circular distribution which does not restrict the + distribution to a fixed interval. Currently, there is no circular + distribution framework in SciPy. The ``cdf`` is implemented such that + ``cdf(x + 2*np.pi) == cdf(x) + 1``. + + `vonmises_line` is the same distribution, defined on :math:`[-\pi, \pi]` + on the real line. This is a regular (i.e. non-circular) distribution. + + Note about distribution parameters: `vonmises` and `vonmises_line` take + ``kappa`` as a shape parameter (concentration) and ``loc`` as the location + (circular mean). A ``scale`` parameter is accepted but does not have any + effect. + + Examples + -------- + Import the necessary modules. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import vonmises + + Define distribution parameters. + + >>> loc = 0.5 * np.pi # circular mean + >>> kappa = 1 # concentration + + Compute the probability density at ``x=0`` via the ``pdf`` method. + + >>> vonmises.pdf(0, loc=loc, kappa=kappa) + 0.12570826359722018 + + Verify that the percentile function ``ppf`` inverts the cumulative + distribution function ``cdf`` up to floating point accuracy. + + >>> x = 1 + >>> cdf_value = vonmises.cdf(x, loc=loc, kappa=kappa) + >>> ppf_value = vonmises.ppf(cdf_value, loc=loc, kappa=kappa) + >>> x, cdf_value, ppf_value + (1, 0.31489339900904967, 1.0000000000000004) + + Draw 1000 random variates by calling the ``rvs`` method. + + >>> sample_size = 1000 + >>> sample = vonmises(loc=loc, kappa=kappa).rvs(sample_size) + + Plot the von Mises density on a Cartesian and polar grid to emphasize + that it is a circular distribution. + + >>> fig = plt.figure(figsize=(12, 6)) + >>> left = plt.subplot(121) + >>> right = plt.subplot(122, projection='polar') + >>> x = np.linspace(-np.pi, np.pi, 500) + >>> vonmises_pdf = vonmises.pdf(x, loc=loc, kappa=kappa) + >>> ticks = [0, 0.15, 0.3] + + The left image contains the Cartesian plot. + + >>> left.plot(x, vonmises_pdf) + >>> left.set_yticks(ticks) + >>> number_of_bins = int(np.sqrt(sample_size)) + >>> left.hist(sample, density=True, bins=number_of_bins) + >>> left.set_title("Cartesian plot") + >>> left.set_xlim(-np.pi, np.pi) + >>> left.grid(True) + + The right image contains the polar plot. + + >>> right.plot(x, vonmises_pdf, label="PDF") + >>> right.set_yticks(ticks) + >>> right.hist(sample, density=True, bins=number_of_bins, + ... label="Histogram") + >>> right.set_title("Polar plot") + >>> right.legend(bbox_to_anchor=(0.15, 1.06)) + + """ + def _shape_info(self): + return [_ShapeInfo("kappa", False, (0, np.inf), (True, False))] + + def _argcheck(self, kappa): + return kappa >= 0 + + def _rvs(self, kappa, size=None, random_state=None): + return random_state.vonmises(0.0, kappa, size=size) + + @inherit_docstring_from(rv_continuous) + def rvs(self, *args, **kwds): + rvs = super().rvs(*args, **kwds) + return np.mod(rvs + np.pi, 2*np.pi) - np.pi + + def _pdf(self, x, kappa): + # vonmises.pdf(x, kappa) = exp(kappa * cos(x)) / (2*pi*I[0](kappa)) + # = exp(kappa * (cos(x) - 1)) / + # (2*pi*exp(-kappa)*I[0](kappa)) + # = exp(kappa * cosm1(x)) / (2*pi*i0e(kappa)) + return np.exp(kappa*sc.cosm1(x)) / (2*np.pi*sc.i0e(kappa)) + + def _logpdf(self, x, kappa): + # vonmises.pdf(x, kappa) = exp(kappa * cosm1(x)) / (2*pi*i0e(kappa)) + return kappa * sc.cosm1(x) - np.log(2*np.pi) - np.log(sc.i0e(kappa)) + + def _cdf(self, x, kappa): + return _stats.von_mises_cdf(kappa, x) + + def _stats_skip(self, kappa): + return 0, None, 0, None + + def _entropy(self, kappa): + # vonmises.entropy(kappa) = -kappa * I[1](kappa) / I[0](kappa) + + # log(2 * np.pi * I[0](kappa)) + # = -kappa * I[1](kappa) * exp(-kappa) / + # (I[0](kappa) * exp(-kappa)) + + # log(2 * np.pi * + # I[0](kappa) * exp(-kappa) / exp(-kappa)) + # = -kappa * sc.i1e(kappa) / sc.i0e(kappa) + + # log(2 * np.pi * i0e(kappa)) + kappa + return (-kappa * sc.i1e(kappa) / sc.i0e(kappa) + + np.log(2 * np.pi * sc.i0e(kappa)) + kappa) + + @extend_notes_in_docstring(rv_continuous, notes="""\ + The default limits of integration are endpoints of the interval + of width ``2*pi`` centered at `loc` (e.g. ``[-pi, pi]`` when + ``loc=0``).\n\n""") + def expect(self, func=None, args=(), loc=0, scale=1, lb=None, ub=None, + conditional=False, **kwds): + _a, _b = -np.pi, np.pi + + if lb is None: + lb = loc + _a + if ub is None: + ub = loc + _b + + return super().expect(func, args, loc, + scale, lb, ub, conditional, **kwds) + + @_call_super_mom + @extend_notes_in_docstring(rv_continuous, notes="""\ + Fit data is assumed to represent angles and will be wrapped onto the + unit circle. `f0` and `fscale` are ignored; the returned shape is + always the maximum likelihood estimate and the scale is always + 1. Initial guesses are ignored.\n\n""") + def fit(self, data, *args, **kwds): + if kwds.pop('superfit', False): + return super().fit(data, *args, **kwds) + + data, fshape, floc, fscale = _check_fit_input_parameters(self, data, + args, kwds) + if self.a == -np.pi: + # vonmises line case, here the default fit method will be used + return super().fit(data, *args, **kwds) + + # wrap data to interval [0, 2*pi] + data = np.mod(data, 2 * np.pi) + + def find_mu(data): + return stats.circmean(data) + + def find_kappa(data, loc): + # Usually, sources list the following as the equation to solve for + # the MLE of the shape parameter: + # r = I[1](kappa)/I[0](kappa), where r = mean resultant length + # This is valid when the location is the MLE of location. + # More generally, when the location may be fixed at an arbitrary + # value, r should be defined as follows: + r = np.sum(np.cos(loc - data))/len(data) + # See gh-18128 for more information. + + # The function r[0](kappa) := I[1](kappa)/I[0](kappa) is monotonic + # increasing from r[0](0) = 0 to r[0](+inf) = 1. The partial + # derivative of the log likelihood function with respect to kappa + # is monotonic decreasing in kappa. + if r == 1: + # All observations are (almost) equal to the mean. Return + # some large kappa such that r[0](kappa) = 1.0 numerically. + return 1e16 + elif r > 0: + def solve_for_kappa(kappa): + return sc.i1e(kappa)/sc.i0e(kappa) - r + + # The bounds of the root of r[0](kappa) = r are derived from + # selected bounds of r[0](x) given in [1, Eq. 11 & 16]. See + # gh-20102 for details. + # + # [1] Amos, D. E. (1973). Computation of Modified Bessel + # Functions and Their Ratios. Mathematics of Computation, + # 28(125): 239-251. + lower_bound = r/(1-r)/(1+r) + upper_bound = 2*lower_bound + + # The bounds are violated numerically for certain values of r, + # where solve_for_kappa evaluated at the bounds have the same + # sign. This indicates numerical imprecision of i1e()/i0e(). + # Return the violated bound in this case as it's more accurate. + if solve_for_kappa(lower_bound) >= 0: + return lower_bound + elif solve_for_kappa(upper_bound) <= 0: + return upper_bound + else: + root_res = root_scalar(solve_for_kappa, method="brentq", + bracket=(lower_bound, upper_bound)) + return root_res.root + else: + # if the provided floc is very far from the circular mean, + # the mean resultant length r can become negative. + # In that case, the equation + # I[1](kappa)/I[0](kappa) = r does not have a solution. + # The maximum likelihood kappa is then 0 which practically + # results in the uniform distribution on the circle. As + # vonmises is defined for kappa > 0, return instead the + # smallest floating point value. + # See gh-18190 for more information + return np.finfo(float).tiny + + # location likelihood equation has a solution independent of kappa + loc = floc if floc is not None else find_mu(data) + # shape likelihood equation depends on location + shape = fshape if fshape is not None else find_kappa(data, loc) + + loc = np.mod(loc + np.pi, 2 * np.pi) - np.pi # ensure in [-pi, pi] + return shape, loc, 1 # scale is not handled + + +vonmises = vonmises_gen(name='vonmises') +vonmises_line = vonmises_gen(a=-np.pi, b=np.pi, name='vonmises_line') + + +class wald_gen(invgauss_gen): + r"""A Wald continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `wald` is: + + .. math:: + + f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp(- \frac{ (x-1)^2 }{ 2x }) + + for :math:`x >= 0`. + + `wald` is a special case of `invgauss` with ``mu=1``. + + %(after_notes)s + + %(example)s + """ + _support_mask = rv_continuous._open_support_mask + + def _shape_info(self): + return [] + + def _rvs(self, size=None, random_state=None): + return random_state.wald(1.0, 1.0, size=size) + + def _pdf(self, x): + # wald.pdf(x) = 1/sqrt(2*pi*x**3) * exp(-(x-1)**2/(2*x)) + return invgauss._pdf(x, 1.0) + + def _cdf(self, x): + return invgauss._cdf(x, 1.0) + + def _sf(self, x): + return invgauss._sf(x, 1.0) + + def _ppf(self, x): + return invgauss._ppf(x, 1.0) + + def _isf(self, x): + return invgauss._isf(x, 1.0) + + def _logpdf(self, x): + return invgauss._logpdf(x, 1.0) + + def _logcdf(self, x): + return invgauss._logcdf(x, 1.0) + + def _logsf(self, x): + return invgauss._logsf(x, 1.0) + + def _stats(self): + return 1.0, 1.0, 3.0, 15.0 + + def _entropy(self): + return invgauss._entropy(1.0) + + +wald = wald_gen(a=0.0, name="wald") + + +class wrapcauchy_gen(rv_continuous): + r"""A wrapped Cauchy continuous random variable. + + %(before_notes)s + + Notes + ----- + The probability density function for `wrapcauchy` is: + + .. math:: + + f(x, c) = \frac{1-c^2}{2\pi (1+c^2 - 2c \cos(x))} + + for :math:`0 \le x \le 2\pi`, :math:`0 < c < 1`. + + `wrapcauchy` takes ``c`` as a shape parameter for :math:`c`. + + %(after_notes)s + + %(example)s + + """ + def _argcheck(self, c): + return (c > 0) & (c < 1) + + def _shape_info(self): + return [_ShapeInfo("c", False, (0, 1), (False, False))] + + def _pdf(self, x, c): + # wrapcauchy.pdf(x, c) = (1-c**2) / (2*pi*(1+c**2-2*c*cos(x))) + return (1.0-c*c)/(2*np.pi*(1+c*c-2*c*np.cos(x))) + + def _cdf(self, x, c): + + def f1(x, cr): + # CDF for 0 <= x < pi + return 1/np.pi * np.arctan(cr*np.tan(x/2)) + + def f2(x, cr): + # CDF for pi <= x <= 2*pi + return 1 - 1/np.pi * np.arctan(cr*np.tan((2*np.pi - x)/2)) + + cr = (1 + c)/(1 - c) + return _lazywhere(x < np.pi, (x, cr), f=f1, f2=f2) + + def _ppf(self, q, c): + val = (1.0-c)/(1.0+c) + rcq = 2*np.arctan(val*np.tan(np.pi*q)) + rcmq = 2*np.pi-2*np.arctan(val*np.tan(np.pi*(1-q))) + return np.where(q < 1.0/2, rcq, rcmq) + + def _entropy(self, c): + return np.log(2*np.pi*(1-c*c)) + + def _fitstart(self, data): + # Use 0.5 as the initial guess of the shape parameter. + # For the location and scale, use the minimum and + # peak-to-peak/(2*pi), respectively. + if isinstance(data, CensoredData): + data = data._uncensor() + return 0.5, np.min(data), np.ptp(data)/(2*np.pi) + + +wrapcauchy = wrapcauchy_gen(a=0.0, b=2*np.pi, name='wrapcauchy') + + +class gennorm_gen(rv_continuous): + r"""A generalized normal continuous random variable. + + %(before_notes)s + + See Also + -------- + laplace : Laplace distribution + norm : normal distribution + + Notes + ----- + The probability density function for `gennorm` is [1]_: + + .. math:: + + f(x, \beta) = \frac{\beta}{2 \Gamma(1/\beta)} \exp(-|x|^\beta), + + where :math:`x` is a real number, :math:`\beta > 0` and + :math:`\Gamma` is the gamma function (`scipy.special.gamma`). + + `gennorm` takes ``beta`` as a shape parameter for :math:`\beta`. + For :math:`\beta = 1`, it is identical to a Laplace distribution. + For :math:`\beta = 2`, it is identical to a normal distribution + (with ``scale=1/sqrt(2)``). + + References + ---------- + + .. [1] "Generalized normal distribution, Version 1", + https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1 + + .. [2] Nardon, Martina, and Paolo Pianca. "Simulation techniques for + generalized Gaussian densities." Journal of Statistical + Computation and Simulation 79.11 (2009): 1317-1329 + + .. [3] Wicklin, Rick. "Simulate data from a generalized Gaussian + distribution" in The DO Loop blog, September 21, 2016, + https://blogs.sas.com/content/iml/2016/09/21/simulate-generalized-gaussian-sas.html + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("beta", False, (0, np.inf), (False, False))] + + def _pdf(self, x, beta): + return np.exp(self._logpdf(x, beta)) + + def _logpdf(self, x, beta): + return np.log(0.5*beta) - sc.gammaln(1.0/beta) - abs(x)**beta + + def _cdf(self, x, beta): + c = 0.5 * np.sign(x) + # evaluating (.5 + c) first prevents numerical cancellation + return (0.5 + c) - c * sc.gammaincc(1.0/beta, abs(x)**beta) + + def _ppf(self, x, beta): + c = np.sign(x - 0.5) + # evaluating (1. + c) first prevents numerical cancellation + return c * sc.gammainccinv(1.0/beta, (1.0 + c) - 2.0*c*x)**(1.0/beta) + + def _sf(self, x, beta): + return self._cdf(-x, beta) + + def _isf(self, x, beta): + return -self._ppf(x, beta) + + def _stats(self, beta): + c1, c3, c5 = sc.gammaln([1.0/beta, 3.0/beta, 5.0/beta]) + return 0., np.exp(c3 - c1), 0., np.exp(c5 + c1 - 2.0*c3) - 3. + + def _entropy(self, beta): + return 1. / beta - np.log(.5 * beta) + sc.gammaln(1. / beta) + + def _rvs(self, beta, size=None, random_state=None): + # see [2]_ for the algorithm + # see [3]_ for reference implementation in SAS + z = random_state.gamma(1/beta, size=size) + y = z ** (1/beta) + # convert y to array to ensure masking support + y = np.asarray(y) + mask = random_state.random(size=y.shape) < 0.5 + y[mask] = -y[mask] + return y + + +gennorm = gennorm_gen(name='gennorm') + + +class halfgennorm_gen(rv_continuous): + r"""The upper half of a generalized normal continuous random variable. + + %(before_notes)s + + See Also + -------- + gennorm : generalized normal distribution + expon : exponential distribution + halfnorm : half normal distribution + + Notes + ----- + The probability density function for `halfgennorm` is: + + .. math:: + + f(x, \beta) = \frac{\beta}{\Gamma(1/\beta)} \exp(-|x|^\beta) + + for :math:`x, \beta > 0`. :math:`\Gamma` is the gamma function + (`scipy.special.gamma`). + + `halfgennorm` takes ``beta`` as a shape parameter for :math:`\beta`. + For :math:`\beta = 1`, it is identical to an exponential distribution. + For :math:`\beta = 2`, it is identical to a half normal distribution + (with ``scale=1/sqrt(2)``). + + References + ---------- + + .. [1] "Generalized normal distribution, Version 1", + https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1 + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("beta", False, (0, np.inf), (False, False))] + + def _pdf(self, x, beta): + # beta + # halfgennorm.pdf(x, beta) = ------------- exp(-|x|**beta) + # gamma(1/beta) + return np.exp(self._logpdf(x, beta)) + + def _logpdf(self, x, beta): + return np.log(beta) - sc.gammaln(1.0/beta) - x**beta + + def _cdf(self, x, beta): + return sc.gammainc(1.0/beta, x**beta) + + def _ppf(self, x, beta): + return sc.gammaincinv(1.0/beta, x)**(1.0/beta) + + def _sf(self, x, beta): + return sc.gammaincc(1.0/beta, x**beta) + + def _isf(self, x, beta): + return sc.gammainccinv(1.0/beta, x)**(1.0/beta) + + def _entropy(self, beta): + return 1.0/beta - np.log(beta) + sc.gammaln(1.0/beta) + + +halfgennorm = halfgennorm_gen(a=0, name='halfgennorm') + + +class crystalball_gen(rv_continuous): + r""" + Crystalball distribution + + %(before_notes)s + + Notes + ----- + The probability density function for `crystalball` is: + + .. math:: + + f(x, \beta, m) = \begin{cases} + N \exp(-x^2 / 2), &\text{for } x > -\beta\\ + N A (B - x)^{-m} &\text{for } x \le -\beta + \end{cases} + + where :math:`A = (m / |\beta|)^m \exp(-\beta^2 / 2)`, + :math:`B = m/|\beta| - |\beta|` and :math:`N` is a normalisation constant. + + `crystalball` takes :math:`\beta > 0` and :math:`m > 1` as shape + parameters. :math:`\beta` defines the point where the pdf changes + from a power-law to a Gaussian distribution. :math:`m` is the power + of the power-law tail. + + %(after_notes)s + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] "Crystal Ball Function", + https://en.wikipedia.org/wiki/Crystal_Ball_function + + %(example)s + """ + def _argcheck(self, beta, m): + """ + Shape parameter bounds are m > 1 and beta > 0. + """ + return (m > 1) & (beta > 0) + + def _shape_info(self): + ibeta = _ShapeInfo("beta", False, (0, np.inf), (False, False)) + im = _ShapeInfo("m", False, (1, np.inf), (False, False)) + return [ibeta, im] + + def _fitstart(self, data): + # Arbitrary, but the default m=1 is not valid + return super()._fitstart(data, args=(1, 1.5)) + + def _pdf(self, x, beta, m): + """ + Return PDF of the crystalball function. + + -- + | exp(-x**2 / 2), for x > -beta + crystalball.pdf(x, beta, m) = N * | + | A * (B - x)**(-m), for x <= -beta + -- + """ + N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) + + _norm_pdf_C * _norm_cdf(beta)) + + def rhs(x, beta, m): + return np.exp(-x**2 / 2) + + def lhs(x, beta, m): + return ((m/beta)**m * np.exp(-beta**2 / 2.0) * + (m/beta - beta - x)**(-m)) + + return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) + + def _logpdf(self, x, beta, m): + """ + Return the log of the PDF of the crystalball function. + """ + N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) + + _norm_pdf_C * _norm_cdf(beta)) + + def rhs(x, beta, m): + return -x**2/2 + + def lhs(x, beta, m): + return m*np.log(m/beta) - beta**2/2 - m*np.log(m/beta - beta - x) + + return np.log(N) + _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) + + def _cdf(self, x, beta, m): + """ + Return CDF of the crystalball function + """ + N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) + + _norm_pdf_C * _norm_cdf(beta)) + + def rhs(x, beta, m): + return ((m/beta) * np.exp(-beta**2 / 2.0) / (m-1) + + _norm_pdf_C * (_norm_cdf(x) - _norm_cdf(-beta))) + + def lhs(x, beta, m): + return ((m/beta)**m * np.exp(-beta**2 / 2.0) * + (m/beta - beta - x)**(-m+1) / (m-1)) + + return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) + + def _ppf(self, p, beta, m): + N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) + + _norm_pdf_C * _norm_cdf(beta)) + pbeta = N * (m/beta) * np.exp(-beta**2/2) / (m - 1) + + def ppf_less(p, beta, m): + eb2 = np.exp(-beta**2/2) + C = (m/beta) * eb2 / (m-1) + N = 1/(C + _norm_pdf_C * _norm_cdf(beta)) + return (m/beta - beta - + ((m - 1)*(m/beta)**(-m)/eb2*p/N)**(1/(1-m))) + + def ppf_greater(p, beta, m): + eb2 = np.exp(-beta**2/2) + C = (m/beta) * eb2 / (m-1) + N = 1/(C + _norm_pdf_C * _norm_cdf(beta)) + return _norm_ppf(_norm_cdf(-beta) + (1/_norm_pdf_C)*(p/N - C)) + + return _lazywhere(p < pbeta, (p, beta, m), f=ppf_less, f2=ppf_greater) + + def _munp(self, n, beta, m): + """ + Returns the n-th non-central moment of the crystalball function. + """ + N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) + + _norm_pdf_C * _norm_cdf(beta)) + + def n_th_moment(n, beta, m): + """ + Returns n-th moment. Defined only if n+1 < m + Function cannot broadcast due to the loop over n + """ + A = (m/beta)**m * np.exp(-beta**2 / 2.0) + B = m/beta - beta + rhs = (2**((n-1)/2.0) * sc.gamma((n+1)/2) * + (1.0 + (-1)**n * sc.gammainc((n+1)/2, beta**2 / 2))) + lhs = np.zeros(rhs.shape) + for k in range(n + 1): + lhs += (sc.binom(n, k) * B**(n-k) * (-1)**k / (m - k - 1) * + (m/beta)**(-m + k + 1)) + return A * lhs + rhs + + return N * _lazywhere(n + 1 < m, (n, beta, m), + np.vectorize(n_th_moment, otypes=[np.float64]), + np.inf) + + +crystalball = crystalball_gen(name='crystalball', longname="A Crystalball Function") + + +def _argus_phi(chi): + """ + Utility function for the argus distribution used in the pdf, sf and + moment calculation. + Note that for all x > 0: + gammainc(1.5, x**2/2) = 2 * (_norm_cdf(x) - x * _norm_pdf(x) - 0.5). + This can be verified directly by noting that the cdf of Gamma(1.5) can + be written as erf(sqrt(x)) - 2*sqrt(x)*exp(-x)/sqrt(Pi). + We use gammainc instead of the usual definition because it is more precise + for small chi. + """ + return sc.gammainc(1.5, chi**2/2) / 2 + + +class argus_gen(rv_continuous): + r""" + Argus distribution + + %(before_notes)s + + Notes + ----- + The probability density function for `argus` is: + + .. math:: + + f(x, \chi) = \frac{\chi^3}{\sqrt{2\pi} \Psi(\chi)} x \sqrt{1-x^2} + \exp(-\chi^2 (1 - x^2)/2) + + for :math:`0 < x < 1` and :math:`\chi > 0`, where + + .. math:: + + \Psi(\chi) = \Phi(\chi) - \chi \phi(\chi) - 1/2 + + with :math:`\Phi` and :math:`\phi` being the CDF and PDF of a standard + normal distribution, respectively. + + `argus` takes :math:`\chi` as shape a parameter. Details about sampling + from the ARGUS distribution can be found in [2]_. + + %(after_notes)s + + References + ---------- + .. [1] "ARGUS distribution", + https://en.wikipedia.org/wiki/ARGUS_distribution + .. [2] Christoph Baumgarten "Random variate generation by fast numerical + inversion in the varying parameter case." Research in Statistics, + vol. 1, 2023, doi:10.1080/27684520.2023.2279060. + + .. versionadded:: 0.19.0 + + %(example)s + """ + def _shape_info(self): + return [_ShapeInfo("chi", False, (0, np.inf), (False, False))] + + def _logpdf(self, x, chi): + # for x = 0 or 1, logpdf returns -np.inf + with np.errstate(divide='ignore'): + y = 1.0 - x*x + A = 3*np.log(chi) - _norm_pdf_logC - np.log(_argus_phi(chi)) + return A + np.log(x) + 0.5*np.log1p(-x*x) - chi**2 * y / 2 + + def _pdf(self, x, chi): + return np.exp(self._logpdf(x, chi)) + + def _cdf(self, x, chi): + return 1.0 - self._sf(x, chi) + + def _sf(self, x, chi): + return _argus_phi(chi * np.sqrt(1 - x**2)) / _argus_phi(chi) + + def _rvs(self, chi, size=None, random_state=None): + chi = np.asarray(chi) + if chi.size == 1: + out = self._rvs_scalar(chi, numsamples=size, + random_state=random_state) + else: + shp, bc = _check_shape(chi.shape, size) + numsamples = int(np.prod(shp)) + out = np.empty(size) + it = np.nditer([chi], + flags=['multi_index'], + op_flags=[['readonly']]) + while not it.finished: + idx = tuple((it.multi_index[j] if not bc[j] else slice(None)) + for j in range(-len(size), 0)) + r = self._rvs_scalar(it[0], numsamples=numsamples, + random_state=random_state) + out[idx] = r.reshape(shp) + it.iternext() + + if size == (): + out = out[()] + return out + + def _rvs_scalar(self, chi, numsamples=None, random_state=None): + # if chi <= 1.8: + # use rejection method, see Devroye: + # Non-Uniform Random Variate Generation, 1986, section II.3.2. + # write: PDF f(x) = c * g(x) * h(x), where + # h is [0,1]-valued and g is a density + # we use two ways to write f + # + # Case 1: + # write g(x) = 3*x*sqrt(1-x**2), h(x) = exp(-chi**2 (1-x**2) / 2) + # If X has a distribution with density g its ppf G_inv is given by: + # G_inv(u) = np.sqrt(1 - u**(2/3)) + # + # Case 2: + # g(x) = chi**2 * x * exp(-chi**2 * (1-x**2)/2) / (1 - exp(-chi**2 /2)) + # h(x) = sqrt(1 - x**2), 0 <= x <= 1 + # one can show that + # G_inv(u) = np.sqrt(2*np.log(u*(np.exp(chi**2/2)-1)+1))/chi + # = np.sqrt(1 + 2*np.log(np.exp(-chi**2/2)*(1-u)+u)/chi**2) + # the latter expression is used for precision with small chi + # + # In both cases, the inverse cdf of g can be written analytically, and + # we can apply the rejection method: + # + # REPEAT + # Generate U uniformly distributed on [0, 1] + # Generate X with density g (e.g. via inverse transform sampling: + # X = G_inv(V) with V uniformly distributed on [0, 1]) + # UNTIL X <= h(X) + # RETURN X + # + # We use case 1 for chi <= 0.5 as it maintains precision for small chi + # and case 2 for 0.5 < chi <= 1.8 due to its speed for moderate chi. + # + # if chi > 1.8: + # use relation to the Gamma distribution: if X is ARGUS with parameter + # chi), then Y = chi**2 * (1 - X**2) / 2 has density proportional to + # sqrt(u) * exp(-u) on [0, chi**2 / 2], i.e. a Gamma(3/2) distribution + # conditioned on [0, chi**2 / 2]). Therefore, to sample X from the + # ARGUS distribution, we sample Y from the gamma distribution, keeping + # only samples on [0, chi**2 / 2], and apply the inverse + # transformation X = (1 - 2*Y/chi**2)**(1/2). Since we only + # look at chi > 1.8, gamma(1.5).cdf(chi**2/2) is large enough such + # Y falls in the interval [0, chi**2 / 2] with a high probability: + # stats.gamma(1.5).cdf(1.8**2/2) = 0.644... + # + # The points to switch between the different methods are determined + # by a comparison of the runtime of the different methods. However, + # the runtime is platform-dependent. The implemented values should + # ensure a good overall performance and are supported by an analysis + # of the rejection constants of different methods. + + size1d = tuple(np.atleast_1d(numsamples)) + N = int(np.prod(size1d)) + x = np.zeros(N) + simulated = 0 + chi2 = chi * chi + if chi <= 0.5: + d = -chi2 / 2 + while simulated < N: + k = N - simulated + u = random_state.uniform(size=k) + v = random_state.uniform(size=k) + z = v**(2/3) + # acceptance condition: u <= h(G_inv(v)). This simplifies to + accept = (np.log(u) <= d * z) + num_accept = np.sum(accept) + if num_accept > 0: + # we still need to transform z=v**(2/3) to X = G_inv(v) + rvs = np.sqrt(1 - z[accept]) + x[simulated:(simulated + num_accept)] = rvs + simulated += num_accept + elif chi <= 1.8: + echi = np.exp(-chi2 / 2) + while simulated < N: + k = N - simulated + u = random_state.uniform(size=k) + v = random_state.uniform(size=k) + z = 2 * np.log(echi * (1 - v) + v) / chi2 + # as in case one, simplify u <= h(G_inv(v)) and then transform + # z to the target distribution X = G_inv(v) + accept = (u**2 + z <= 0) + num_accept = np.sum(accept) + if num_accept > 0: + rvs = np.sqrt(1 + z[accept]) + x[simulated:(simulated + num_accept)] = rvs + simulated += num_accept + else: + # conditional Gamma for chi > 1.8 + while simulated < N: + k = N - simulated + g = random_state.standard_gamma(1.5, size=k) + accept = (g <= chi2 / 2) + num_accept = np.sum(accept) + if num_accept > 0: + x[simulated:(simulated + num_accept)] = g[accept] + simulated += num_accept + x = np.sqrt(1 - 2 * x / chi2) + + return np.reshape(x, size1d) + + def _stats(self, chi): + # need to ensure that dtype is float + # otherwise the mask below does not work for integers + chi = np.asarray(chi, dtype=float) + phi = _argus_phi(chi) + m = np.sqrt(np.pi/8) * chi * sc.ive(1, chi**2/4) / phi + # compute second moment, use Taylor expansion for small chi (<= 0.1) + mu2 = np.empty_like(chi) + mask = chi > 0.1 + c = chi[mask] + mu2[mask] = 1 - 3 / c**2 + c * _norm_pdf(c) / phi[mask] + c = chi[~mask] + coef = [-358/65690625, 0, -94/1010625, 0, 2/2625, 0, 6/175, 0, 0.4] + mu2[~mask] = np.polyval(coef, c) + return m, mu2 - m**2, None, None + + +argus = argus_gen(name='argus', longname="An Argus Function", a=0.0, b=1.0) + + +class rv_histogram(rv_continuous): + """ + Generates a distribution given by a histogram. + This is useful to generate a template distribution from a binned + datasample. + + As a subclass of the `rv_continuous` class, `rv_histogram` inherits from it + a collection of generic methods (see `rv_continuous` for the full list), + and implements them based on the properties of the provided binned + datasample. + + Parameters + ---------- + histogram : tuple of array_like + Tuple containing two array_like objects. + The first containing the content of n bins, + the second containing the (n+1) bin boundaries. + In particular, the return value of `numpy.histogram` is accepted. + + density : bool, optional + If False, assumes the histogram is proportional to counts per bin; + otherwise, assumes it is proportional to a density. + For constant bin widths, these are equivalent, but the distinction + is important when bin widths vary (see Notes). + If None (default), sets ``density=True`` for backwards compatibility, + but warns if the bin widths are variable. Set `density` explicitly + to silence the warning. + + .. versionadded:: 1.10.0 + + Notes + ----- + When a histogram has unequal bin widths, there is a distinction between + histograms that are proportional to counts per bin and histograms that are + proportional to probability density over a bin. If `numpy.histogram` is + called with its default ``density=False``, the resulting histogram is the + number of counts per bin, so ``density=False`` should be passed to + `rv_histogram`. If `numpy.histogram` is called with ``density=True``, the + resulting histogram is in terms of probability density, so ``density=True`` + should be passed to `rv_histogram`. To avoid warnings, always pass + ``density`` explicitly when the input histogram has unequal bin widths. + + There are no additional shape parameters except for the loc and scale. + The pdf is defined as a stepwise function from the provided histogram. + The cdf is a linear interpolation of the pdf. + + .. versionadded:: 0.19.0 + + Examples + -------- + + Create a scipy.stats distribution from a numpy histogram + + >>> import scipy.stats + >>> import numpy as np + >>> data = scipy.stats.norm.rvs(size=100000, loc=0, scale=1.5, + ... random_state=123) + >>> hist = np.histogram(data, bins=100) + >>> hist_dist = scipy.stats.rv_histogram(hist, density=False) + + Behaves like an ordinary scipy rv_continuous distribution + + >>> hist_dist.pdf(1.0) + 0.20538577847618705 + >>> hist_dist.cdf(2.0) + 0.90818568543056499 + + PDF is zero above (below) the highest (lowest) bin of the histogram, + defined by the max (min) of the original dataset + + >>> hist_dist.pdf(np.max(data)) + 0.0 + >>> hist_dist.cdf(np.max(data)) + 1.0 + >>> hist_dist.pdf(np.min(data)) + 7.7591907244498314e-05 + >>> hist_dist.cdf(np.min(data)) + 0.0 + + PDF and CDF follow the histogram + + >>> import matplotlib.pyplot as plt + >>> X = np.linspace(-5.0, 5.0, 100) + >>> fig, ax = plt.subplots() + >>> ax.set_title("PDF from Template") + >>> ax.hist(data, density=True, bins=100) + >>> ax.plot(X, hist_dist.pdf(X), label='PDF') + >>> ax.plot(X, hist_dist.cdf(X), label='CDF') + >>> ax.legend() + >>> fig.show() + + """ + _support_mask = rv_continuous._support_mask + + def __init__(self, histogram, *args, density=None, **kwargs): + """ + Create a new distribution using the given histogram + + Parameters + ---------- + histogram : tuple of array_like + Tuple containing two array_like objects. + The first containing the content of n bins, + the second containing the (n+1) bin boundaries. + In particular, the return value of np.histogram is accepted. + density : bool, optional + If False, assumes the histogram is proportional to counts per bin; + otherwise, assumes it is proportional to a density. + For constant bin widths, these are equivalent. + If None (default), sets ``density=True`` for backward + compatibility, but warns if the bin widths are variable. Set + `density` explicitly to silence the warning. + """ + self._histogram = histogram + self._density = density + if len(histogram) != 2: + raise ValueError("Expected length 2 for parameter histogram") + self._hpdf = np.asarray(histogram[0]) + self._hbins = np.asarray(histogram[1]) + if len(self._hpdf) + 1 != len(self._hbins): + raise ValueError("Number of elements in histogram content " + "and histogram boundaries do not match, " + "expected n and n+1.") + self._hbin_widths = self._hbins[1:] - self._hbins[:-1] + bins_vary = not np.allclose(self._hbin_widths, self._hbin_widths[0]) + if density is None and bins_vary: + message = ("Bin widths are not constant. Assuming `density=True`." + "Specify `density` explicitly to silence this warning.") + warnings.warn(message, RuntimeWarning, stacklevel=2) + density = True + elif not density: + self._hpdf = self._hpdf / self._hbin_widths + + self._hpdf = self._hpdf / float(np.sum(self._hpdf * self._hbin_widths)) + self._hcdf = np.cumsum(self._hpdf * self._hbin_widths) + self._hpdf = np.hstack([0.0, self._hpdf, 0.0]) + self._hcdf = np.hstack([0.0, self._hcdf]) + # Set support + kwargs['a'] = self.a = self._hbins[0] + kwargs['b'] = self.b = self._hbins[-1] + super().__init__(*args, **kwargs) + + def _pdf(self, x): + """ + PDF of the histogram + """ + return self._hpdf[np.searchsorted(self._hbins, x, side='right')] + + def _cdf(self, x): + """ + CDF calculated from the histogram + """ + return np.interp(x, self._hbins, self._hcdf) + + def _ppf(self, x): + """ + Percentile function calculated from the histogram + """ + return np.interp(x, self._hcdf, self._hbins) + + def _munp(self, n): + """Compute the n-th non-central moment.""" + integrals = (self._hbins[1:]**(n+1) - self._hbins[:-1]**(n+1)) / (n+1) + return np.sum(self._hpdf[1:-1] * integrals) + + def _entropy(self): + """Compute entropy of distribution""" + res = _lazywhere(self._hpdf[1:-1] > 0.0, + (self._hpdf[1:-1],), + np.log, + 0.0) + return -np.sum(self._hpdf[1:-1] * res * self._hbin_widths) + + def _updated_ctor_param(self): + """ + Set the histogram as additional constructor argument + """ + dct = super()._updated_ctor_param() + dct['histogram'] = self._histogram + dct['density'] = self._density + return dct + + +class studentized_range_gen(rv_continuous): + r"""A studentized range continuous random variable. + + %(before_notes)s + + See Also + -------- + t: Student's t distribution + + Notes + ----- + The probability density function for `studentized_range` is: + + .. math:: + + f(x; k, \nu) = \frac{k(k-1)\nu^{\nu/2}}{\Gamma(\nu/2) + 2^{\nu/2-1}} \int_{0}^{\infty} \int_{-\infty}^{\infty} + s^{\nu} e^{-\nu s^2/2} \phi(z) \phi(sx + z) + [\Phi(sx + z) - \Phi(z)]^{k-2} \,dz \,ds + + for :math:`x ≥ 0`, :math:`k > 1`, and :math:`\nu > 0`. + + `studentized_range` takes ``k`` for :math:`k` and ``df`` for :math:`\nu` + as shape parameters. + + When :math:`\nu` exceeds 100,000, an asymptotic approximation (infinite + degrees of freedom) is used to compute the cumulative distribution + function [4]_ and probability distribution function. + + %(after_notes)s + + References + ---------- + + .. [1] "Studentized range distribution", + https://en.wikipedia.org/wiki/Studentized_range_distribution + .. [2] Batista, Ben Dêivide, et al. "Externally Studentized Normal Midrange + Distribution." Ciência e Agrotecnologia, vol. 41, no. 4, 2017, pp. + 378-389., doi:10.1590/1413-70542017414047716. + .. [3] Harter, H. Leon. "Tables of Range and Studentized Range." The Annals + of Mathematical Statistics, vol. 31, no. 4, 1960, pp. 1122-1147. + JSTOR, www.jstor.org/stable/2237810. Accessed 18 Feb. 2021. + .. [4] Lund, R. E., and J. R. Lund. "Algorithm AS 190: Probabilities and + Upper Quantiles for the Studentized Range." Journal of the Royal + Statistical Society. Series C (Applied Statistics), vol. 32, no. 2, + 1983, pp. 204-210. JSTOR, www.jstor.org/stable/2347300. Accessed 18 + Feb. 2021. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import studentized_range + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + + Calculate the first four moments: + + >>> k, df = 3, 10 + >>> mean, var, skew, kurt = studentized_range.stats(k, df, moments='mvsk') + + Display the probability density function (``pdf``): + + >>> x = np.linspace(studentized_range.ppf(0.01, k, df), + ... studentized_range.ppf(0.99, k, df), 100) + >>> ax.plot(x, studentized_range.pdf(x, k, df), + ... 'r-', lw=5, alpha=0.6, label='studentized_range pdf') + + Alternatively, the distribution object can be called (as a function) + to fix the shape, location and scale parameters. This returns a "frozen" + RV object holding the given parameters fixed. + + Freeze the distribution and display the frozen ``pdf``: + + >>> rv = studentized_range(k, df) + >>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + + Check accuracy of ``cdf`` and ``ppf``: + + >>> vals = studentized_range.ppf([0.001, 0.5, 0.999], k, df) + >>> np.allclose([0.001, 0.5, 0.999], studentized_range.cdf(vals, k, df)) + True + + Rather than using (``studentized_range.rvs``) to generate random variates, + which is very slow for this distribution, we can approximate the inverse + CDF using an interpolator, and then perform inverse transform sampling + with this approximate inverse CDF. + + This distribution has an infinite but thin right tail, so we focus our + attention on the leftmost 99.9 percent. + + >>> a, b = studentized_range.ppf([0, .999], k, df) + >>> a, b + 0, 7.41058083802274 + + >>> from scipy.interpolate import interp1d + >>> rng = np.random.default_rng() + >>> xs = np.linspace(a, b, 50) + >>> cdf = studentized_range.cdf(xs, k, df) + # Create an interpolant of the inverse CDF + >>> ppf = interp1d(cdf, xs, fill_value='extrapolate') + # Perform inverse transform sampling using the interpolant + >>> r = ppf(rng.uniform(size=1000)) + + And compare the histogram: + + >>> ax.hist(r, density=True, histtype='stepfilled', alpha=0.2) + >>> ax.legend(loc='best', frameon=False) + >>> plt.show() + + """ + + def _argcheck(self, k, df): + return (k > 1) & (df > 0) + + def _shape_info(self): + ik = _ShapeInfo("k", False, (1, np.inf), (False, False)) + idf = _ShapeInfo("df", False, (0, np.inf), (False, False)) + return [ik, idf] + + def _fitstart(self, data): + # Default is k=1, but that is not a valid value of the parameter. + return super()._fitstart(data, args=(2, 1)) + + def _munp(self, K, k, df): + cython_symbol = '_studentized_range_moment' + _a, _b = self._get_support() + # all three of these are used to create a numpy array so they must + # be the same shape. + + def _single_moment(K, k, df): + log_const = _stats._studentized_range_pdf_logconst(k, df) + arg = [K, k, df, log_const] + usr_data = np.array(arg, float).ctypes.data_as(ctypes.c_void_p) + + llc = LowLevelCallable.from_cython(_stats, cython_symbol, usr_data) + + ranges = [(-np.inf, np.inf), (0, np.inf), (_a, _b)] + opts = dict(epsabs=1e-11, epsrel=1e-12) + + return integrate.nquad(llc, ranges=ranges, opts=opts)[0] + + ufunc = np.frompyfunc(_single_moment, 3, 1) + return np.asarray(ufunc(K, k, df), dtype=np.float64)[()] + + def _pdf(self, x, k, df): + + def _single_pdf(q, k, df): + # The infinite form of the PDF is derived from the infinite + # CDF. + if df < 100000: + cython_symbol = '_studentized_range_pdf' + log_const = _stats._studentized_range_pdf_logconst(k, df) + arg = [q, k, df, log_const] + usr_data = np.array(arg, float).ctypes.data_as(ctypes.c_void_p) + ranges = [(-np.inf, np.inf), (0, np.inf)] + + else: + cython_symbol = '_studentized_range_pdf_asymptotic' + arg = [q, k] + usr_data = np.array(arg, float).ctypes.data_as(ctypes.c_void_p) + ranges = [(-np.inf, np.inf)] + + llc = LowLevelCallable.from_cython(_stats, cython_symbol, usr_data) + opts = dict(epsabs=1e-11, epsrel=1e-12) + return integrate.nquad(llc, ranges=ranges, opts=opts)[0] + + ufunc = np.frompyfunc(_single_pdf, 3, 1) + return np.asarray(ufunc(x, k, df), dtype=np.float64)[()] + + def _cdf(self, x, k, df): + + def _single_cdf(q, k, df): + # "When the degrees of freedom V are infinite the probability + # integral takes [on a] simpler form," and a single asymptotic + # integral is evaluated rather than the standard double integral. + # (Lund, Lund, page 205) + if df < 100000: + cython_symbol = '_studentized_range_cdf' + log_const = _stats._studentized_range_cdf_logconst(k, df) + arg = [q, k, df, log_const] + usr_data = np.array(arg, float).ctypes.data_as(ctypes.c_void_p) + ranges = [(-np.inf, np.inf), (0, np.inf)] + + else: + cython_symbol = '_studentized_range_cdf_asymptotic' + arg = [q, k] + usr_data = np.array(arg, float).ctypes.data_as(ctypes.c_void_p) + ranges = [(-np.inf, np.inf)] + + llc = LowLevelCallable.from_cython(_stats, cython_symbol, usr_data) + opts = dict(epsabs=1e-11, epsrel=1e-12) + return integrate.nquad(llc, ranges=ranges, opts=opts)[0] + + ufunc = np.frompyfunc(_single_cdf, 3, 1) + + # clip p-values to ensure they are in [0, 1]. + return np.clip(np.asarray(ufunc(x, k, df), dtype=np.float64)[()], 0, 1) + + +studentized_range = studentized_range_gen(name='studentized_range', a=0, + b=np.inf) + + +class rel_breitwigner_gen(rv_continuous): + r"""A relativistic Breit-Wigner random variable. + + %(before_notes)s + + See Also + -------- + cauchy: Cauchy distribution, also known as the Breit-Wigner distribution. + + Notes + ----- + + The probability density function for `rel_breitwigner` is + + .. math:: + + f(x, \rho) = \frac{k}{(x^2 - \rho^2)^2 + \rho^2} + + where + + .. math:: + k = \frac{2\sqrt{2}\rho^2\sqrt{\rho^2 + 1}} + {\pi\sqrt{\rho^2 + \rho\sqrt{\rho^2 + 1}}} + + The relativistic Breit-Wigner distribution is used in high energy physics + to model resonances [1]_. It gives the uncertainty in the invariant mass, + :math:`M` [2]_, of a resonance with characteristic mass :math:`M_0` and + decay-width :math:`\Gamma`, where :math:`M`, :math:`M_0` and :math:`\Gamma` + are expressed in natural units. In SciPy's parametrization, the shape + parameter :math:`\rho` is equal to :math:`M_0/\Gamma` and takes values in + :math:`(0, \infty)`. + + Equivalently, the relativistic Breit-Wigner distribution is said to give + the uncertainty in the center-of-mass energy :math:`E_{\text{cm}}`. In + natural units, the speed of light :math:`c` is equal to 1 and the invariant + mass :math:`M` is equal to the rest energy :math:`Mc^2`. In the + center-of-mass frame, the rest energy is equal to the total energy [3]_. + + %(after_notes)s + + :math:`\rho = M/\Gamma` and :math:`\Gamma` is the scale parameter. For + example, if one seeks to model the :math:`Z^0` boson with :math:`M_0 + \approx 91.1876 \text{ GeV}` and :math:`\Gamma \approx 2.4952\text{ GeV}` + [4]_ one can set ``rho=91.1876/2.4952`` and ``scale=2.4952``. + + To ensure a physically meaningful result when using the `fit` method, one + should set ``floc=0`` to fix the location parameter to 0. + + References + ---------- + .. [1] Relativistic Breit-Wigner distribution, Wikipedia, + https://en.wikipedia.org/wiki/Relativistic_Breit-Wigner_distribution + .. [2] Invariant mass, Wikipedia, + https://en.wikipedia.org/wiki/Invariant_mass + .. [3] Center-of-momentum frame, Wikipedia, + https://en.wikipedia.org/wiki/Center-of-momentum_frame + .. [4] M. Tanabashi et al. (Particle Data Group) Phys. Rev. D 98, 030001 - + Published 17 August 2018 + + %(example)s + + """ + def _argcheck(self, rho): + return rho > 0 + + def _shape_info(self): + return [_ShapeInfo("rho", False, (0, np.inf), (False, False))] + + def _pdf(self, x, rho): + # C = k / rho**2 + C = np.sqrt( + 2 * (1 + 1/rho**2) / (1 + np.sqrt(1 + 1/rho**2)) + ) * 2 / np.pi + with np.errstate(over='ignore'): + return C / (((x - rho)*(x + rho)/rho)**2 + 1) + + def _cdf(self, x, rho): + # C = k / (2 * rho**2) / np.sqrt(1 + 1/rho**2) + C = np.sqrt(2/(1 + np.sqrt(1 + 1/rho**2)))/np.pi + result = ( + np.sqrt(-1 + 1j/rho) + * np.arctan(x/np.sqrt(-rho*(rho + 1j))) + ) + result = C * 2 * np.imag(result) + # Sometimes above formula produces values greater than 1. + return np.clip(result, None, 1) + + def _munp(self, n, rho): + if n == 1: + # C = k / (2 * rho) + C = np.sqrt( + 2 * (1 + 1/rho**2) / (1 + np.sqrt(1 + 1/rho**2)) + ) / np.pi * rho + return C * (np.pi/2 + np.arctan(rho)) + if n == 2: + # C = pi * k / (4 * rho) + C = np.sqrt( + (1 + 1/rho**2) / (2 * (1 + np.sqrt(1 + 1/rho**2))) + ) * rho + result = (1 - rho * 1j) / np.sqrt(-1 - 1j/rho) + return 2 * C * np.real(result) + else: + return np.inf + + def _stats(self, rho): + # Returning None from stats makes public stats use _munp. + # nan values will be omitted from public stats. Skew and + # kurtosis are actually infinite. + return None, None, np.nan, np.nan + + @inherit_docstring_from(rv_continuous) + def fit(self, data, *args, **kwds): + # Override rv_continuous.fit to better handle case where floc is set. + data, _, floc, fscale = _check_fit_input_parameters( + self, data, args, kwds + ) + + censored = isinstance(data, CensoredData) + if censored: + if data.num_censored() == 0: + # There are no censored values in data, so replace the + # CensoredData instance with a regular array. + data = data._uncensored + censored = False + + if floc is None or censored: + return super().fit(data, *args, **kwds) + + if fscale is None: + # The interquartile range approximates the scale parameter gamma. + # The median approximates rho * gamma. + p25, p50, p75 = np.quantile(data - floc, [0.25, 0.5, 0.75]) + scale_0 = p75 - p25 + rho_0 = p50 / scale_0 + if not args: + args = [rho_0] + if "scale" not in kwds: + kwds["scale"] = scale_0 + else: + M_0 = np.median(data - floc) + rho_0 = M_0 / fscale + if not args: + args = [rho_0] + return super().fit(data, *args, **kwds) + + +rel_breitwigner = rel_breitwigner_gen(a=0.0, name="rel_breitwigner") + + +# Collect names of classes and objects in this module. +pairs = list(globals().copy().items()) +_distn_names, _distn_gen_names = get_distribution_names(pairs, rv_continuous) + +__all__ = _distn_names + _distn_gen_names + ['rv_histogram'] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_covariance.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_covariance.py new file mode 100644 index 0000000000000000000000000000000000000000..812a3ec62eff46a06ea4e058670081874e82c021 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_covariance.py @@ -0,0 +1,633 @@ +from functools import cached_property + +import numpy as np +from scipy import linalg +from scipy.stats import _multivariate + + +__all__ = ["Covariance"] + + +class Covariance: + """ + Representation of a covariance matrix + + Calculations involving covariance matrices (e.g. data whitening, + multivariate normal function evaluation) are often performed more + efficiently using a decomposition of the covariance matrix instead of the + covariance matrix itself. This class allows the user to construct an + object representing a covariance matrix using any of several + decompositions and perform calculations using a common interface. + + .. note:: + + The `Covariance` class cannot be instantiated directly. Instead, use + one of the factory methods (e.g. `Covariance.from_diagonal`). + + Examples + -------- + The `Covariance` class is is used by calling one of its + factory methods to create a `Covariance` object, then pass that + representation of the `Covariance` matrix as a shape parameter of a + multivariate distribution. + + For instance, the multivariate normal distribution can accept an array + representing a covariance matrix: + + >>> from scipy import stats + >>> import numpy as np + >>> d = [1, 2, 3] + >>> A = np.diag(d) # a diagonal covariance matrix + >>> x = [4, -2, 5] # a point of interest + >>> dist = stats.multivariate_normal(mean=[0, 0, 0], cov=A) + >>> dist.pdf(x) + 4.9595685102808205e-08 + + but the calculations are performed in a very generic way that does not + take advantage of any special properties of the covariance matrix. Because + our covariance matrix is diagonal, we can use ``Covariance.from_diagonal`` + to create an object representing the covariance matrix, and + `multivariate_normal` can use this to compute the probability density + function more efficiently. + + >>> cov = stats.Covariance.from_diagonal(d) + >>> dist = stats.multivariate_normal(mean=[0, 0, 0], cov=cov) + >>> dist.pdf(x) + 4.9595685102808205e-08 + + """ + def __init__(self): + message = ("The `Covariance` class cannot be instantiated directly. " + "Please use one of the factory methods " + "(e.g. `Covariance.from_diagonal`).") + raise NotImplementedError(message) + + @staticmethod + def from_diagonal(diagonal): + r""" + Return a representation of a covariance matrix from its diagonal. + + Parameters + ---------- + diagonal : array_like + The diagonal elements of a diagonal matrix. + + Notes + ----- + Let the diagonal elements of a diagonal covariance matrix :math:`D` be + stored in the vector :math:`d`. + + When all elements of :math:`d` are strictly positive, whitening of a + data point :math:`x` is performed by computing + :math:`x \cdot d^{-1/2}`, where the inverse square root can be taken + element-wise. + :math:`\log\det{D}` is calculated as :math:`-2 \sum(\log{d})`, + where the :math:`\log` operation is performed element-wise. + + This `Covariance` class supports singular covariance matrices. When + computing ``_log_pdet``, non-positive elements of :math:`d` are + ignored. Whitening is not well defined when the point to be whitened + does not lie in the span of the columns of the covariance matrix. The + convention taken here is to treat the inverse square root of + non-positive elements of :math:`d` as zeros. + + Examples + -------- + Prepare a symmetric positive definite covariance matrix ``A`` and a + data point ``x``. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> n = 5 + >>> A = np.diag(rng.random(n)) + >>> x = rng.random(size=n) + + Extract the diagonal from ``A`` and create the `Covariance` object. + + >>> d = np.diag(A) + >>> cov = stats.Covariance.from_diagonal(d) + + Compare the functionality of the `Covariance` object against a + reference implementations. + + >>> res = cov.whiten(x) + >>> ref = np.diag(d**-0.5) @ x + >>> np.allclose(res, ref) + True + >>> res = cov.log_pdet + >>> ref = np.linalg.slogdet(A)[-1] + >>> np.allclose(res, ref) + True + + """ + return CovViaDiagonal(diagonal) + + @staticmethod + def from_precision(precision, covariance=None): + r""" + Return a representation of a covariance from its precision matrix. + + Parameters + ---------- + precision : array_like + The precision matrix; that is, the inverse of a square, symmetric, + positive definite covariance matrix. + covariance : array_like, optional + The square, symmetric, positive definite covariance matrix. If not + provided, this may need to be calculated (e.g. to evaluate the + cumulative distribution function of + `scipy.stats.multivariate_normal`) by inverting `precision`. + + Notes + ----- + Let the covariance matrix be :math:`A`, its precision matrix be + :math:`P = A^{-1}`, and :math:`L` be the lower Cholesky factor such + that :math:`L L^T = P`. + Whitening of a data point :math:`x` is performed by computing + :math:`x^T L`. :math:`\log\det{A}` is calculated as + :math:`-2tr(\log{L})`, where the :math:`\log` operation is performed + element-wise. + + This `Covariance` class does not support singular covariance matrices + because the precision matrix does not exist for a singular covariance + matrix. + + Examples + -------- + Prepare a symmetric positive definite precision matrix ``P`` and a + data point ``x``. (If the precision matrix is not already available, + consider the other factory methods of the `Covariance` class.) + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> n = 5 + >>> P = rng.random(size=(n, n)) + >>> P = P @ P.T # a precision matrix must be positive definite + >>> x = rng.random(size=n) + + Create the `Covariance` object. + + >>> cov = stats.Covariance.from_precision(P) + + Compare the functionality of the `Covariance` object against + reference implementations. + + >>> res = cov.whiten(x) + >>> ref = x @ np.linalg.cholesky(P) + >>> np.allclose(res, ref) + True + >>> res = cov.log_pdet + >>> ref = -np.linalg.slogdet(P)[-1] + >>> np.allclose(res, ref) + True + + """ + return CovViaPrecision(precision, covariance) + + @staticmethod + def from_cholesky(cholesky): + r""" + Representation of a covariance provided via the (lower) Cholesky factor + + Parameters + ---------- + cholesky : array_like + The lower triangular Cholesky factor of the covariance matrix. + + Notes + ----- + Let the covariance matrix be :math:`A` and :math:`L` be the lower + Cholesky factor such that :math:`L L^T = A`. + Whitening of a data point :math:`x` is performed by computing + :math:`L^{-1} x`. :math:`\log\det{A}` is calculated as + :math:`2tr(\log{L})`, where the :math:`\log` operation is performed + element-wise. + + This `Covariance` class does not support singular covariance matrices + because the Cholesky decomposition does not exist for a singular + covariance matrix. + + Examples + -------- + Prepare a symmetric positive definite covariance matrix ``A`` and a + data point ``x``. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> n = 5 + >>> A = rng.random(size=(n, n)) + >>> A = A @ A.T # make the covariance symmetric positive definite + >>> x = rng.random(size=n) + + Perform the Cholesky decomposition of ``A`` and create the + `Covariance` object. + + >>> L = np.linalg.cholesky(A) + >>> cov = stats.Covariance.from_cholesky(L) + + Compare the functionality of the `Covariance` object against + reference implementation. + + >>> from scipy.linalg import solve_triangular + >>> res = cov.whiten(x) + >>> ref = solve_triangular(L, x, lower=True) + >>> np.allclose(res, ref) + True + >>> res = cov.log_pdet + >>> ref = np.linalg.slogdet(A)[-1] + >>> np.allclose(res, ref) + True + + """ + return CovViaCholesky(cholesky) + + @staticmethod + def from_eigendecomposition(eigendecomposition): + r""" + Representation of a covariance provided via eigendecomposition + + Parameters + ---------- + eigendecomposition : sequence + A sequence (nominally a tuple) containing the eigenvalue and + eigenvector arrays as computed by `scipy.linalg.eigh` or + `numpy.linalg.eigh`. + + Notes + ----- + Let the covariance matrix be :math:`A`, let :math:`V` be matrix of + eigenvectors, and let :math:`W` be the diagonal matrix of eigenvalues + such that `V W V^T = A`. + + When all of the eigenvalues are strictly positive, whitening of a + data point :math:`x` is performed by computing + :math:`x^T (V W^{-1/2})`, where the inverse square root can be taken + element-wise. + :math:`\log\det{A}` is calculated as :math:`tr(\log{W})`, + where the :math:`\log` operation is performed element-wise. + + This `Covariance` class supports singular covariance matrices. When + computing ``_log_pdet``, non-positive eigenvalues are ignored. + Whitening is not well defined when the point to be whitened + does not lie in the span of the columns of the covariance matrix. The + convention taken here is to treat the inverse square root of + non-positive eigenvalues as zeros. + + Examples + -------- + Prepare a symmetric positive definite covariance matrix ``A`` and a + data point ``x``. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> n = 5 + >>> A = rng.random(size=(n, n)) + >>> A = A @ A.T # make the covariance symmetric positive definite + >>> x = rng.random(size=n) + + Perform the eigendecomposition of ``A`` and create the `Covariance` + object. + + >>> w, v = np.linalg.eigh(A) + >>> cov = stats.Covariance.from_eigendecomposition((w, v)) + + Compare the functionality of the `Covariance` object against + reference implementations. + + >>> res = cov.whiten(x) + >>> ref = x @ (v @ np.diag(w**-0.5)) + >>> np.allclose(res, ref) + True + >>> res = cov.log_pdet + >>> ref = np.linalg.slogdet(A)[-1] + >>> np.allclose(res, ref) + True + + """ + return CovViaEigendecomposition(eigendecomposition) + + def whiten(self, x): + """ + Perform a whitening transformation on data. + + "Whitening" ("white" as in "white noise", in which each frequency has + equal magnitude) transforms a set of random variables into a new set of + random variables with unit-diagonal covariance. When a whitening + transform is applied to a sample of points distributed according to + a multivariate normal distribution with zero mean, the covariance of + the transformed sample is approximately the identity matrix. + + Parameters + ---------- + x : array_like + An array of points. The last dimension must correspond with the + dimensionality of the space, i.e., the number of columns in the + covariance matrix. + + Returns + ------- + x_ : array_like + The transformed array of points. + + References + ---------- + .. [1] "Whitening Transformation". Wikipedia. + https://en.wikipedia.org/wiki/Whitening_transformation + .. [2] Novak, Lukas, and Miroslav Vorechovsky. "Generalization of + coloring linear transformation". Transactions of VSB 18.2 + (2018): 31-35. :doi:`10.31490/tces-2018-0013` + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> n = 3 + >>> A = rng.random(size=(n, n)) + >>> cov_array = A @ A.T # make matrix symmetric positive definite + >>> precision = np.linalg.inv(cov_array) + >>> cov_object = stats.Covariance.from_precision(precision) + >>> x = rng.multivariate_normal(np.zeros(n), cov_array, size=(10000)) + >>> x_ = cov_object.whiten(x) + >>> np.cov(x_, rowvar=False) # near-identity covariance + array([[0.97862122, 0.00893147, 0.02430451], + [0.00893147, 0.96719062, 0.02201312], + [0.02430451, 0.02201312, 0.99206881]]) + + """ + return self._whiten(np.asarray(x)) + + def colorize(self, x): + """ + Perform a colorizing transformation on data. + + "Colorizing" ("color" as in "colored noise", in which different + frequencies may have different magnitudes) transforms a set of + uncorrelated random variables into a new set of random variables with + the desired covariance. When a coloring transform is applied to a + sample of points distributed according to a multivariate normal + distribution with identity covariance and zero mean, the covariance of + the transformed sample is approximately the covariance matrix used + in the coloring transform. + + Parameters + ---------- + x : array_like + An array of points. The last dimension must correspond with the + dimensionality of the space, i.e., the number of columns in the + covariance matrix. + + Returns + ------- + x_ : array_like + The transformed array of points. + + References + ---------- + .. [1] "Whitening Transformation". Wikipedia. + https://en.wikipedia.org/wiki/Whitening_transformation + .. [2] Novak, Lukas, and Miroslav Vorechovsky. "Generalization of + coloring linear transformation". Transactions of VSB 18.2 + (2018): 31-35. :doi:`10.31490/tces-2018-0013` + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng(1638083107694713882823079058616272161) + >>> n = 3 + >>> A = rng.random(size=(n, n)) + >>> cov_array = A @ A.T # make matrix symmetric positive definite + >>> cholesky = np.linalg.cholesky(cov_array) + >>> cov_object = stats.Covariance.from_cholesky(cholesky) + >>> x = rng.multivariate_normal(np.zeros(n), np.eye(n), size=(10000)) + >>> x_ = cov_object.colorize(x) + >>> cov_data = np.cov(x_, rowvar=False) + >>> np.allclose(cov_data, cov_array, rtol=3e-2) + True + """ + return self._colorize(np.asarray(x)) + + @property + def log_pdet(self): + """ + Log of the pseudo-determinant of the covariance matrix + """ + return np.array(self._log_pdet, dtype=float)[()] + + @property + def rank(self): + """ + Rank of the covariance matrix + """ + return np.array(self._rank, dtype=int)[()] + + @property + def covariance(self): + """ + Explicit representation of the covariance matrix + """ + return self._covariance + + @property + def shape(self): + """ + Shape of the covariance array + """ + return self._shape + + def _validate_matrix(self, A, name): + A = np.atleast_2d(A) + m, n = A.shape[-2:] + if m != n or A.ndim != 2 or not (np.issubdtype(A.dtype, np.integer) or + np.issubdtype(A.dtype, np.floating)): + message = (f"The input `{name}` must be a square, " + "two-dimensional array of real numbers.") + raise ValueError(message) + return A + + def _validate_vector(self, A, name): + A = np.atleast_1d(A) + if A.ndim != 1 or not (np.issubdtype(A.dtype, np.integer) or + np.issubdtype(A.dtype, np.floating)): + message = (f"The input `{name}` must be a one-dimensional array " + "of real numbers.") + raise ValueError(message) + return A + + +class CovViaPrecision(Covariance): + + def __init__(self, precision, covariance=None): + precision = self._validate_matrix(precision, 'precision') + if covariance is not None: + covariance = self._validate_matrix(covariance, 'covariance') + message = "`precision.shape` must equal `covariance.shape`." + if precision.shape != covariance.shape: + raise ValueError(message) + + self._chol_P = np.linalg.cholesky(precision) + self._log_pdet = -2*np.log(np.diag(self._chol_P)).sum(axis=-1) + self._rank = precision.shape[-1] # must be full rank if invertible + self._precision = precision + self._cov_matrix = covariance + self._shape = precision.shape + self._allow_singular = False + + def _whiten(self, x): + return x @ self._chol_P + + @cached_property + def _covariance(self): + n = self._shape[-1] + return (linalg.cho_solve((self._chol_P, True), np.eye(n)) + if self._cov_matrix is None else self._cov_matrix) + + def _colorize(self, x): + return linalg.solve_triangular(self._chol_P.T, x.T, lower=False).T + + +def _dot_diag(x, d): + # If d were a full diagonal matrix, x @ d would always do what we want. + # Special treatment is needed for n-dimensional `d` in which each row + # includes only the diagonal elements of a covariance matrix. + return x * d if x.ndim < 2 else x * np.expand_dims(d, -2) + + +class CovViaDiagonal(Covariance): + + def __init__(self, diagonal): + diagonal = self._validate_vector(diagonal, 'diagonal') + + i_zero = diagonal <= 0 + positive_diagonal = np.array(diagonal, dtype=np.float64) + + positive_diagonal[i_zero] = 1 # ones don't affect determinant + self._log_pdet = np.sum(np.log(positive_diagonal), axis=-1) + + psuedo_reciprocals = 1 / np.sqrt(positive_diagonal) + psuedo_reciprocals[i_zero] = 0 + + self._sqrt_diagonal = np.sqrt(diagonal) + self._LP = psuedo_reciprocals + self._rank = positive_diagonal.shape[-1] - i_zero.sum(axis=-1) + self._covariance = np.apply_along_axis(np.diag, -1, diagonal) + self._i_zero = i_zero + self._shape = self._covariance.shape + self._allow_singular = True + + def _whiten(self, x): + return _dot_diag(x, self._LP) + + def _colorize(self, x): + return _dot_diag(x, self._sqrt_diagonal) + + def _support_mask(self, x): + """ + Check whether x lies in the support of the distribution. + """ + return ~np.any(_dot_diag(x, self._i_zero), axis=-1) + + +class CovViaCholesky(Covariance): + + def __init__(self, cholesky): + L = self._validate_matrix(cholesky, 'cholesky') + + self._factor = L + self._log_pdet = 2*np.log(np.diag(self._factor)).sum(axis=-1) + self._rank = L.shape[-1] # must be full rank for cholesky + self._shape = L.shape + self._allow_singular = False + + @cached_property + def _covariance(self): + return self._factor @ self._factor.T + + def _whiten(self, x): + res = linalg.solve_triangular(self._factor, x.T, lower=True).T + return res + + def _colorize(self, x): + return x @ self._factor.T + + +class CovViaEigendecomposition(Covariance): + + def __init__(self, eigendecomposition): + eigenvalues, eigenvectors = eigendecomposition + eigenvalues = self._validate_vector(eigenvalues, 'eigenvalues') + eigenvectors = self._validate_matrix(eigenvectors, 'eigenvectors') + message = ("The shapes of `eigenvalues` and `eigenvectors` " + "must be compatible.") + try: + eigenvalues = np.expand_dims(eigenvalues, -2) + eigenvectors, eigenvalues = np.broadcast_arrays(eigenvectors, + eigenvalues) + eigenvalues = eigenvalues[..., 0, :] + except ValueError: + raise ValueError(message) + + i_zero = eigenvalues <= 0 + positive_eigenvalues = np.array(eigenvalues, dtype=np.float64) + + positive_eigenvalues[i_zero] = 1 # ones don't affect determinant + self._log_pdet = np.sum(np.log(positive_eigenvalues), axis=-1) + + psuedo_reciprocals = 1 / np.sqrt(positive_eigenvalues) + psuedo_reciprocals[i_zero] = 0 + + self._LP = eigenvectors * psuedo_reciprocals + self._LA = eigenvectors * np.sqrt(eigenvalues) + self._rank = positive_eigenvalues.shape[-1] - i_zero.sum(axis=-1) + self._w = eigenvalues + self._v = eigenvectors + self._shape = eigenvectors.shape + self._null_basis = eigenvectors * i_zero + # This is only used for `_support_mask`, not to decide whether + # the covariance is singular or not. + self._eps = _multivariate._eigvalsh_to_eps(eigenvalues) * 10**3 + self._allow_singular = True + + def _whiten(self, x): + return x @ self._LP + + def _colorize(self, x): + return x @ self._LA.T + + @cached_property + def _covariance(self): + return (self._v * self._w) @ self._v.T + + def _support_mask(self, x): + """ + Check whether x lies in the support of the distribution. + """ + residual = np.linalg.norm(x @ self._null_basis, axis=-1) + in_support = residual < self._eps + return in_support + + +class CovViaPSD(Covariance): + """ + Representation of a covariance provided via an instance of _PSD + """ + + def __init__(self, psd): + self._LP = psd.U + self._log_pdet = psd.log_pdet + self._rank = psd.rank + self._covariance = psd._M + self._shape = psd._M.shape + self._psd = psd + self._allow_singular = False # by default + + def _whiten(self, x): + return x @ self._LP + + def _support_mask(self, x): + return self._psd._support_mask(x) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_crosstab.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_crosstab.py new file mode 100644 index 0000000000000000000000000000000000000000..ee762a2700bf3e13bc251c5287630c4a237aa2ae --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_crosstab.py @@ -0,0 +1,204 @@ +import numpy as np +from scipy.sparse import coo_matrix +from scipy._lib._bunch import _make_tuple_bunch + + +CrosstabResult = _make_tuple_bunch( + "CrosstabResult", ["elements", "count"] +) + + +def crosstab(*args, levels=None, sparse=False): + """ + Return table of counts for each possible unique combination in ``*args``. + + When ``len(args) > 1``, the array computed by this function is + often referred to as a *contingency table* [1]_. + + The arguments must be sequences with the same length. The second return + value, `count`, is an integer array with ``len(args)`` dimensions. If + `levels` is None, the shape of `count` is ``(n0, n1, ...)``, where ``nk`` + is the number of unique elements in ``args[k]``. + + Parameters + ---------- + *args : sequences + A sequence of sequences whose unique aligned elements are to be + counted. The sequences in args must all be the same length. + levels : sequence, optional + If `levels` is given, it must be a sequence that is the same length as + `args`. Each element in `levels` is either a sequence or None. If it + is a sequence, it gives the values in the corresponding sequence in + `args` that are to be counted. If any value in the sequences in `args` + does not occur in the corresponding sequence in `levels`, that value + is ignored and not counted in the returned array `count`. The default + value of `levels` for ``args[i]`` is ``np.unique(args[i])`` + sparse : bool, optional + If True, return a sparse matrix. The matrix will be an instance of + the `scipy.sparse.coo_matrix` class. Because SciPy's sparse matrices + must be 2-d, only two input sequences are allowed when `sparse` is + True. Default is False. + + Returns + ------- + res : CrosstabResult + An object containing the following attributes: + + elements : tuple of numpy.ndarrays. + Tuple of length ``len(args)`` containing the arrays of elements + that are counted in `count`. These can be interpreted as the + labels of the corresponding dimensions of `count`. If `levels` was + given, then if ``levels[i]`` is not None, ``elements[i]`` will + hold the values given in ``levels[i]``. + count : numpy.ndarray or scipy.sparse.coo_matrix + Counts of the unique elements in ``zip(*args)``, stored in an + array. Also known as a *contingency table* when ``len(args) > 1``. + + See Also + -------- + numpy.unique + + Notes + ----- + .. versionadded:: 1.7.0 + + References + ---------- + .. [1] "Contingency table", http://en.wikipedia.org/wiki/Contingency_table + + Examples + -------- + >>> from scipy.stats.contingency import crosstab + + Given the lists `a` and `x`, create a contingency table that counts the + frequencies of the corresponding pairs. + + >>> a = ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'] + >>> x = ['X', 'X', 'X', 'Y', 'Z', 'Z', 'Y', 'Y', 'Z', 'Z'] + >>> res = crosstab(a, x) + >>> avals, xvals = res.elements + >>> avals + array(['A', 'B'], dtype='>> xvals + array(['X', 'Y', 'Z'], dtype='>> res.count + array([[2, 3, 0], + [1, 0, 4]]) + + So `('A', 'X')` occurs twice, `('A', 'Y')` occurs three times, etc. + + Higher dimensional contingency tables can be created. + + >>> p = [0, 0, 0, 0, 1, 1, 1, 0, 0, 1] + >>> res = crosstab(a, x, p) + >>> res.count + array([[[2, 0], + [2, 1], + [0, 0]], + [[1, 0], + [0, 0], + [1, 3]]]) + >>> res.count.shape + (2, 3, 2) + + The values to be counted can be set by using the `levels` argument. + It allows the elements of interest in each input sequence to be + given explicitly instead finding the unique elements of the sequence. + + For example, suppose one of the arguments is an array containing the + answers to a survey question, with integer values 1 to 4. Even if the + value 1 does not occur in the data, we want an entry for it in the table. + + >>> q1 = [2, 3, 3, 2, 4, 4, 2, 3, 4, 4, 4, 3, 3, 3, 4] # 1 does not occur. + >>> q2 = [4, 4, 2, 2, 2, 4, 1, 1, 2, 2, 4, 2, 2, 2, 4] # 3 does not occur. + >>> options = [1, 2, 3, 4] + >>> res = crosstab(q1, q2, levels=(options, options)) + >>> res.count + array([[0, 0, 0, 0], + [1, 1, 0, 1], + [1, 4, 0, 1], + [0, 3, 0, 3]]) + + If `levels` is given, but an element of `levels` is None, the unique values + of the corresponding argument are used. For example, + + >>> res = crosstab(q1, q2, levels=(None, options)) + >>> res.elements + [array([2, 3, 4]), [1, 2, 3, 4]] + >>> res.count + array([[1, 1, 0, 1], + [1, 4, 0, 1], + [0, 3, 0, 3]]) + + If we want to ignore the pairs where 4 occurs in ``q2``, we can + give just the values [1, 2] to `levels`, and the 4 will be ignored: + + >>> res = crosstab(q1, q2, levels=(None, [1, 2])) + >>> res.elements + [array([2, 3, 4]), [1, 2]] + >>> res.count + array([[1, 1], + [1, 4], + [0, 3]]) + + Finally, let's repeat the first example, but return a sparse matrix: + + >>> res = crosstab(a, x, sparse=True) + >>> res.count + <2x3 sparse matrix of type '' + with 4 stored elements in COOrdinate format> + >>> res.count.A + array([[2, 3, 0], + [1, 0, 4]]) + + """ + nargs = len(args) + if nargs == 0: + raise TypeError("At least one input sequence is required.") + + len0 = len(args[0]) + if not all(len(a) == len0 for a in args[1:]): + raise ValueError("All input sequences must have the same length.") + + if sparse and nargs != 2: + raise ValueError("When `sparse` is True, only two input sequences " + "are allowed.") + + if levels is None: + # Call np.unique with return_inverse=True on each argument. + actual_levels, indices = zip(*[np.unique(a, return_inverse=True) + for a in args]) + else: + # `levels` is not None... + if len(levels) != nargs: + raise ValueError('len(levels) must equal the number of input ' + 'sequences') + + args = [np.asarray(arg) for arg in args] + mask = np.zeros((nargs, len0), dtype=np.bool_) + inv = np.zeros((nargs, len0), dtype=np.intp) + actual_levels = [] + for k, (levels_list, arg) in enumerate(zip(levels, args)): + if levels_list is None: + levels_list, inv[k, :] = np.unique(arg, return_inverse=True) + mask[k, :] = True + else: + q = arg == np.asarray(levels_list).reshape(-1, 1) + mask[k, :] = np.any(q, axis=0) + qnz = q.T.nonzero() + inv[k, qnz[0]] = qnz[1] + actual_levels.append(levels_list) + + mask_all = mask.all(axis=0) + indices = tuple(inv[:, mask_all]) + + if sparse: + count = coo_matrix((np.ones(len(indices[0]), dtype=int), + (indices[0], indices[1]))) + count.sum_duplicates() + else: + shape = [len(u) for u in actual_levels] + count = np.zeros(shape, dtype=int) + np.add.at(count, indices, 1) + + return CrosstabResult(actual_levels, count) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_discrete_distns.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_discrete_distns.py new file mode 100644 index 0000000000000000000000000000000000000000..909202ab8c1bae75bd51e67d4cc7c02d4ca794f7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_discrete_distns.py @@ -0,0 +1,1954 @@ +# +# Author: Travis Oliphant 2002-2011 with contributions from +# SciPy Developers 2004-2011 +# +from functools import partial + +from scipy import special +from scipy.special import entr, logsumexp, betaln, gammaln as gamln, zeta +from scipy._lib._util import _lazywhere, rng_integers +from scipy.interpolate import interp1d + +from numpy import floor, ceil, log, exp, sqrt, log1p, expm1, tanh, cosh, sinh + +import numpy as np + +from ._distn_infrastructure import (rv_discrete, get_distribution_names, + _check_shape, _ShapeInfo) +import scipy.stats._boost as _boost +from ._biasedurn import (_PyFishersNCHypergeometric, + _PyWalleniusNCHypergeometric, + _PyStochasticLib3) + + +def _isintegral(x): + return x == np.round(x) + + +class binom_gen(rv_discrete): + r"""A binomial discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `binom` is: + + .. math:: + + f(k) = \binom{n}{k} p^k (1-p)^{n-k} + + for :math:`k \in \{0, 1, \dots, n\}`, :math:`0 \leq p \leq 1` + + `binom` takes :math:`n` and :math:`p` as shape parameters, + where :math:`p` is the probability of a single success + and :math:`1-p` is the probability of a single failure. + + %(after_notes)s + + %(example)s + + See Also + -------- + hypergeom, nbinom, nhypergeom + + """ + def _shape_info(self): + return [_ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("p", False, (0, 1), (True, True))] + + def _rvs(self, n, p, size=None, random_state=None): + return random_state.binomial(n, p, size) + + def _argcheck(self, n, p): + return (n >= 0) & _isintegral(n) & (p >= 0) & (p <= 1) + + def _get_support(self, n, p): + return self.a, n + + def _logpmf(self, x, n, p): + k = floor(x) + combiln = (gamln(n+1) - (gamln(k+1) + gamln(n-k+1))) + return combiln + special.xlogy(k, p) + special.xlog1py(n-k, -p) + + def _pmf(self, x, n, p): + # binom.pmf(k) = choose(n, k) * p**k * (1-p)**(n-k) + return _boost._binom_pdf(x, n, p) + + def _cdf(self, x, n, p): + k = floor(x) + return _boost._binom_cdf(k, n, p) + + def _sf(self, x, n, p): + k = floor(x) + return _boost._binom_sf(k, n, p) + + def _isf(self, x, n, p): + return _boost._binom_isf(x, n, p) + + def _ppf(self, q, n, p): + return _boost._binom_ppf(q, n, p) + + def _stats(self, n, p, moments='mv'): + mu = _boost._binom_mean(n, p) + var = _boost._binom_variance(n, p) + g1, g2 = None, None + if 's' in moments: + g1 = _boost._binom_skewness(n, p) + if 'k' in moments: + g2 = _boost._binom_kurtosis_excess(n, p) + return mu, var, g1, g2 + + def _entropy(self, n, p): + k = np.r_[0:n + 1] + vals = self._pmf(k, n, p) + return np.sum(entr(vals), axis=0) + + +binom = binom_gen(name='binom') + + +class bernoulli_gen(binom_gen): + r"""A Bernoulli discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `bernoulli` is: + + .. math:: + + f(k) = \begin{cases}1-p &\text{if } k = 0\\ + p &\text{if } k = 1\end{cases} + + for :math:`k` in :math:`\{0, 1\}`, :math:`0 \leq p \leq 1` + + `bernoulli` takes :math:`p` as shape parameter, + where :math:`p` is the probability of a single success + and :math:`1-p` is the probability of a single failure. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("p", False, (0, 1), (True, True))] + + def _rvs(self, p, size=None, random_state=None): + return binom_gen._rvs(self, 1, p, size=size, random_state=random_state) + + def _argcheck(self, p): + return (p >= 0) & (p <= 1) + + def _get_support(self, p): + # Overrides binom_gen._get_support!x + return self.a, self.b + + def _logpmf(self, x, p): + return binom._logpmf(x, 1, p) + + def _pmf(self, x, p): + # bernoulli.pmf(k) = 1-p if k = 0 + # = p if k = 1 + return binom._pmf(x, 1, p) + + def _cdf(self, x, p): + return binom._cdf(x, 1, p) + + def _sf(self, x, p): + return binom._sf(x, 1, p) + + def _isf(self, x, p): + return binom._isf(x, 1, p) + + def _ppf(self, q, p): + return binom._ppf(q, 1, p) + + def _stats(self, p): + return binom._stats(1, p) + + def _entropy(self, p): + return entr(p) + entr(1-p) + + +bernoulli = bernoulli_gen(b=1, name='bernoulli') + + +class betabinom_gen(rv_discrete): + r"""A beta-binomial discrete random variable. + + %(before_notes)s + + Notes + ----- + The beta-binomial distribution is a binomial distribution with a + probability of success `p` that follows a beta distribution. + + The probability mass function for `betabinom` is: + + .. math:: + + f(k) = \binom{n}{k} \frac{B(k + a, n - k + b)}{B(a, b)} + + for :math:`k \in \{0, 1, \dots, n\}`, :math:`n \geq 0`, :math:`a > 0`, + :math:`b > 0`, where :math:`B(a, b)` is the beta function. + + `betabinom` takes :math:`n`, :math:`a`, and :math:`b` as shape parameters. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Beta-binomial_distribution + + %(after_notes)s + + .. versionadded:: 1.4.0 + + See Also + -------- + beta, binom + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("a", False, (0, np.inf), (False, False)), + _ShapeInfo("b", False, (0, np.inf), (False, False))] + + def _rvs(self, n, a, b, size=None, random_state=None): + p = random_state.beta(a, b, size) + return random_state.binomial(n, p, size) + + def _get_support(self, n, a, b): + return 0, n + + def _argcheck(self, n, a, b): + return (n >= 0) & _isintegral(n) & (a > 0) & (b > 0) + + def _logpmf(self, x, n, a, b): + k = floor(x) + combiln = -log(n + 1) - betaln(n - k + 1, k + 1) + return combiln + betaln(k + a, n - k + b) - betaln(a, b) + + def _pmf(self, x, n, a, b): + return exp(self._logpmf(x, n, a, b)) + + def _stats(self, n, a, b, moments='mv'): + e_p = a / (a + b) + e_q = 1 - e_p + mu = n * e_p + var = n * (a + b + n) * e_p * e_q / (a + b + 1) + g1, g2 = None, None + if 's' in moments: + g1 = 1.0 / sqrt(var) + g1 *= (a + b + 2 * n) * (b - a) + g1 /= (a + b + 2) * (a + b) + if 'k' in moments: + g2 = (a + b).astype(e_p.dtype) + g2 *= (a + b - 1 + 6 * n) + g2 += 3 * a * b * (n - 2) + g2 += 6 * n ** 2 + g2 -= 3 * e_p * b * n * (6 - n) + g2 -= 18 * e_p * e_q * n ** 2 + g2 *= (a + b) ** 2 * (1 + a + b) + g2 /= (n * a * b * (a + b + 2) * (a + b + 3) * (a + b + n)) + g2 -= 3 + return mu, var, g1, g2 + + +betabinom = betabinom_gen(name='betabinom') + + +class nbinom_gen(rv_discrete): + r"""A negative binomial discrete random variable. + + %(before_notes)s + + Notes + ----- + Negative binomial distribution describes a sequence of i.i.d. Bernoulli + trials, repeated until a predefined, non-random number of successes occurs. + + The probability mass function of the number of failures for `nbinom` is: + + .. math:: + + f(k) = \binom{k+n-1}{n-1} p^n (1-p)^k + + for :math:`k \ge 0`, :math:`0 < p \leq 1` + + `nbinom` takes :math:`n` and :math:`p` as shape parameters where :math:`n` + is the number of successes, :math:`p` is the probability of a single + success, and :math:`1-p` is the probability of a single failure. + + Another common parameterization of the negative binomial distribution is + in terms of the mean number of failures :math:`\mu` to achieve :math:`n` + successes. The mean :math:`\mu` is related to the probability of success + as + + .. math:: + + p = \frac{n}{n + \mu} + + The number of successes :math:`n` may also be specified in terms of a + "dispersion", "heterogeneity", or "aggregation" parameter :math:`\alpha`, + which relates the mean :math:`\mu` to the variance :math:`\sigma^2`, + e.g. :math:`\sigma^2 = \mu + \alpha \mu^2`. Regardless of the convention + used for :math:`\alpha`, + + .. math:: + + p &= \frac{\mu}{\sigma^2} \\ + n &= \frac{\mu^2}{\sigma^2 - \mu} + + %(after_notes)s + + %(example)s + + See Also + -------- + hypergeom, binom, nhypergeom + + """ + def _shape_info(self): + return [_ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("p", False, (0, 1), (True, True))] + + def _rvs(self, n, p, size=None, random_state=None): + return random_state.negative_binomial(n, p, size) + + def _argcheck(self, n, p): + return (n > 0) & (p > 0) & (p <= 1) + + def _pmf(self, x, n, p): + # nbinom.pmf(k) = choose(k+n-1, n-1) * p**n * (1-p)**k + return _boost._nbinom_pdf(x, n, p) + + def _logpmf(self, x, n, p): + coeff = gamln(n+x) - gamln(x+1) - gamln(n) + return coeff + n*log(p) + special.xlog1py(x, -p) + + def _cdf(self, x, n, p): + k = floor(x) + return _boost._nbinom_cdf(k, n, p) + + def _logcdf(self, x, n, p): + k = floor(x) + k, n, p = np.broadcast_arrays(k, n, p) + cdf = self._cdf(k, n, p) + cond = cdf > 0.5 + def f1(k, n, p): + return np.log1p(-special.betainc(k + 1, n, 1 - p)) + + # do calc in place + logcdf = cdf + with np.errstate(divide='ignore'): + logcdf[cond] = f1(k[cond], n[cond], p[cond]) + logcdf[~cond] = np.log(cdf[~cond]) + return logcdf + + def _sf(self, x, n, p): + k = floor(x) + return _boost._nbinom_sf(k, n, p) + + def _isf(self, x, n, p): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._nbinom_isf(x, n, p) + + def _ppf(self, q, n, p): + with np.errstate(over='ignore'): # see gh-17432 + return _boost._nbinom_ppf(q, n, p) + + def _stats(self, n, p): + return ( + _boost._nbinom_mean(n, p), + _boost._nbinom_variance(n, p), + _boost._nbinom_skewness(n, p), + _boost._nbinom_kurtosis_excess(n, p), + ) + + +nbinom = nbinom_gen(name='nbinom') + + +class betanbinom_gen(rv_discrete): + r"""A beta-negative-binomial discrete random variable. + + %(before_notes)s + + Notes + ----- + The beta-negative-binomial distribution is a negative binomial + distribution with a probability of success `p` that follows a + beta distribution. + + The probability mass function for `betanbinom` is: + + .. math:: + + f(k) = \binom{n + k - 1}{k} \frac{B(a + n, b + k)}{B(a, b)} + + for :math:`k \ge 0`, :math:`n \geq 0`, :math:`a > 0`, + :math:`b > 0`, where :math:`B(a, b)` is the beta function. + + `betanbinom` takes :math:`n`, :math:`a`, and :math:`b` as shape parameters. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Beta_negative_binomial_distribution + + %(after_notes)s + + .. versionadded:: 1.12.0 + + See Also + -------- + betabinom : Beta binomial distribution + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("a", False, (0, np.inf), (False, False)), + _ShapeInfo("b", False, (0, np.inf), (False, False))] + + def _rvs(self, n, a, b, size=None, random_state=None): + p = random_state.beta(a, b, size) + return random_state.negative_binomial(n, p, size) + + def _argcheck(self, n, a, b): + return (n >= 0) & _isintegral(n) & (a > 0) & (b > 0) + + def _logpmf(self, x, n, a, b): + k = floor(x) + combiln = -np.log(n + k) - betaln(n, k + 1) + return combiln + betaln(a + n, b + k) - betaln(a, b) + + def _pmf(self, x, n, a, b): + return exp(self._logpmf(x, n, a, b)) + + def _stats(self, n, a, b, moments='mv'): + # reference: Wolfram Alpha input + # BetaNegativeBinomialDistribution[a, b, n] + def mean(n, a, b): + return n * b / (a - 1.) + mu = _lazywhere(a > 1, (n, a, b), f=mean, fillvalue=np.inf) + def var(n, a, b): + return (n * b * (n + a - 1.) * (a + b - 1.) + / ((a - 2.) * (a - 1.)**2.)) + var = _lazywhere(a > 2, (n, a, b), f=var, fillvalue=np.inf) + g1, g2 = None, None + def skew(n, a, b): + return ((2 * n + a - 1.) * (2 * b + a - 1.) + / (a - 3.) / sqrt(n * b * (n + a - 1.) * (b + a - 1.) + / (a - 2.))) + if 's' in moments: + g1 = _lazywhere(a > 3, (n, a, b), f=skew, fillvalue=np.inf) + def kurtosis(n, a, b): + term = (a - 2.) + term_2 = ((a - 1.)**2. * (a**2. + a * (6 * b - 1.) + + 6. * (b - 1.) * b) + + 3. * n**2. * ((a + 5.) * b**2. + (a + 5.) + * (a - 1.) * b + 2. * (a - 1.)**2) + + 3 * (a - 1.) * n + * ((a + 5.) * b**2. + (a + 5.) * (a - 1.) * b + + 2. * (a - 1.)**2.)) + denominator = ((a - 4.) * (a - 3.) * b * n + * (a + b - 1.) * (a + n - 1.)) + # Wolfram Alpha uses Pearson kurtosis, so we substract 3 to get + # scipy's Fisher kurtosis + return term * term_2 / denominator - 3. + if 'k' in moments: + g2 = _lazywhere(a > 4, (n, a, b), f=kurtosis, fillvalue=np.inf) + return mu, var, g1, g2 + + +betanbinom = betanbinom_gen(name='betanbinom') + + +class geom_gen(rv_discrete): + r"""A geometric discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `geom` is: + + .. math:: + + f(k) = (1-p)^{k-1} p + + for :math:`k \ge 1`, :math:`0 < p \leq 1` + + `geom` takes :math:`p` as shape parameter, + where :math:`p` is the probability of a single success + and :math:`1-p` is the probability of a single failure. + + %(after_notes)s + + See Also + -------- + planck + + %(example)s + + """ + + def _shape_info(self): + return [_ShapeInfo("p", False, (0, 1), (True, True))] + + def _rvs(self, p, size=None, random_state=None): + return random_state.geometric(p, size=size) + + def _argcheck(self, p): + return (p <= 1) & (p > 0) + + def _pmf(self, k, p): + return np.power(1-p, k-1) * p + + def _logpmf(self, k, p): + return special.xlog1py(k - 1, -p) + log(p) + + def _cdf(self, x, p): + k = floor(x) + return -expm1(log1p(-p)*k) + + def _sf(self, x, p): + return np.exp(self._logsf(x, p)) + + def _logsf(self, x, p): + k = floor(x) + return k*log1p(-p) + + def _ppf(self, q, p): + vals = ceil(log1p(-q) / log1p(-p)) + temp = self._cdf(vals-1, p) + return np.where((temp >= q) & (vals > 0), vals-1, vals) + + def _stats(self, p): + mu = 1.0/p + qr = 1.0-p + var = qr / p / p + g1 = (2.0-p) / sqrt(qr) + g2 = np.polyval([1, -6, 6], p)/(1.0-p) + return mu, var, g1, g2 + + def _entropy(self, p): + return -np.log(p) - np.log1p(-p) * (1.0-p) / p + + +geom = geom_gen(a=1, name='geom', longname="A geometric") + + +class hypergeom_gen(rv_discrete): + r"""A hypergeometric discrete random variable. + + The hypergeometric distribution models drawing objects from a bin. + `M` is the total number of objects, `n` is total number of Type I objects. + The random variate represents the number of Type I objects in `N` drawn + without replacement from the total population. + + %(before_notes)s + + Notes + ----- + The symbols used to denote the shape parameters (`M`, `n`, and `N`) are not + universally accepted. See the Examples for a clarification of the + definitions used here. + + The probability mass function is defined as, + + .. math:: p(k, M, n, N) = \frac{\binom{n}{k} \binom{M - n}{N - k}} + {\binom{M}{N}} + + for :math:`k \in [\max(0, N - M + n), \min(n, N)]`, where the binomial + coefficients are defined as, + + .. math:: \binom{n}{k} \equiv \frac{n!}{k! (n - k)!}. + + %(after_notes)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import hypergeom + >>> import matplotlib.pyplot as plt + + Suppose we have a collection of 20 animals, of which 7 are dogs. Then if + we want to know the probability of finding a given number of dogs if we + choose at random 12 of the 20 animals, we can initialize a frozen + distribution and plot the probability mass function: + + >>> [M, n, N] = [20, 7, 12] + >>> rv = hypergeom(M, n, N) + >>> x = np.arange(0, n+1) + >>> pmf_dogs = rv.pmf(x) + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(x, pmf_dogs, 'bo') + >>> ax.vlines(x, 0, pmf_dogs, lw=2) + >>> ax.set_xlabel('# of dogs in our group of chosen animals') + >>> ax.set_ylabel('hypergeom PMF') + >>> plt.show() + + Instead of using a frozen distribution we can also use `hypergeom` + methods directly. To for example obtain the cumulative distribution + function, use: + + >>> prb = hypergeom.cdf(x, M, n, N) + + And to generate random numbers: + + >>> R = hypergeom.rvs(M, n, N, size=10) + + See Also + -------- + nhypergeom, binom, nbinom + + """ + def _shape_info(self): + return [_ShapeInfo("M", True, (0, np.inf), (True, False)), + _ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("N", True, (0, np.inf), (True, False))] + + def _rvs(self, M, n, N, size=None, random_state=None): + return random_state.hypergeometric(n, M-n, N, size=size) + + def _get_support(self, M, n, N): + return np.maximum(N-(M-n), 0), np.minimum(n, N) + + def _argcheck(self, M, n, N): + cond = (M > 0) & (n >= 0) & (N >= 0) + cond &= (n <= M) & (N <= M) + cond &= _isintegral(M) & _isintegral(n) & _isintegral(N) + return cond + + def _logpmf(self, k, M, n, N): + tot, good = M, n + bad = tot - good + result = (betaln(good+1, 1) + betaln(bad+1, 1) + betaln(tot-N+1, N+1) - + betaln(k+1, good-k+1) - betaln(N-k+1, bad-N+k+1) - + betaln(tot+1, 1)) + return result + + def _pmf(self, k, M, n, N): + return _boost._hypergeom_pdf(k, n, N, M) + + def _cdf(self, k, M, n, N): + return _boost._hypergeom_cdf(k, n, N, M) + + def _stats(self, M, n, N): + M, n, N = 1. * M, 1. * n, 1. * N + m = M - n + + # Boost kurtosis_excess doesn't return the same as the value + # computed here. + g2 = M * (M + 1) - 6. * N * (M - N) - 6. * n * m + g2 *= (M - 1) * M * M + g2 += 6. * n * N * (M - N) * m * (5. * M - 6) + g2 /= n * N * (M - N) * m * (M - 2.) * (M - 3.) + return ( + _boost._hypergeom_mean(n, N, M), + _boost._hypergeom_variance(n, N, M), + _boost._hypergeom_skewness(n, N, M), + g2, + ) + + def _entropy(self, M, n, N): + k = np.r_[N - (M - n):min(n, N) + 1] + vals = self.pmf(k, M, n, N) + return np.sum(entr(vals), axis=0) + + def _sf(self, k, M, n, N): + return _boost._hypergeom_sf(k, n, N, M) + + def _logsf(self, k, M, n, N): + res = [] + for quant, tot, good, draw in zip(*np.broadcast_arrays(k, M, n, N)): + if (quant + 0.5) * (tot + 0.5) < (good - 0.5) * (draw - 0.5): + # Less terms to sum if we calculate log(1-cdf) + res.append(log1p(-exp(self.logcdf(quant, tot, good, draw)))) + else: + # Integration over probability mass function using logsumexp + k2 = np.arange(quant + 1, draw + 1) + res.append(logsumexp(self._logpmf(k2, tot, good, draw))) + return np.asarray(res) + + def _logcdf(self, k, M, n, N): + res = [] + for quant, tot, good, draw in zip(*np.broadcast_arrays(k, M, n, N)): + if (quant + 0.5) * (tot + 0.5) > (good - 0.5) * (draw - 0.5): + # Less terms to sum if we calculate log(1-sf) + res.append(log1p(-exp(self.logsf(quant, tot, good, draw)))) + else: + # Integration over probability mass function using logsumexp + k2 = np.arange(0, quant + 1) + res.append(logsumexp(self._logpmf(k2, tot, good, draw))) + return np.asarray(res) + + +hypergeom = hypergeom_gen(name='hypergeom') + + +class nhypergeom_gen(rv_discrete): + r"""A negative hypergeometric discrete random variable. + + Consider a box containing :math:`M` balls:, :math:`n` red and + :math:`M-n` blue. We randomly sample balls from the box, one + at a time and *without* replacement, until we have picked :math:`r` + blue balls. `nhypergeom` is the distribution of the number of + red balls :math:`k` we have picked. + + %(before_notes)s + + Notes + ----- + The symbols used to denote the shape parameters (`M`, `n`, and `r`) are not + universally accepted. See the Examples for a clarification of the + definitions used here. + + The probability mass function is defined as, + + .. math:: f(k; M, n, r) = \frac{{{k+r-1}\choose{k}}{{M-r-k}\choose{n-k}}} + {{M \choose n}} + + for :math:`k \in [0, n]`, :math:`n \in [0, M]`, :math:`r \in [0, M-n]`, + and the binomial coefficient is: + + .. math:: \binom{n}{k} \equiv \frac{n!}{k! (n - k)!}. + + It is equivalent to observing :math:`k` successes in :math:`k+r-1` + samples with :math:`k+r`'th sample being a failure. The former + can be modelled as a hypergeometric distribution. The probability + of the latter is simply the number of failures remaining + :math:`M-n-(r-1)` divided by the size of the remaining population + :math:`M-(k+r-1)`. This relationship can be shown as: + + .. math:: NHG(k;M,n,r) = HG(k;M,n,k+r-1)\frac{(M-n-(r-1))}{(M-(k+r-1))} + + where :math:`NHG` is probability mass function (PMF) of the + negative hypergeometric distribution and :math:`HG` is the + PMF of the hypergeometric distribution. + + %(after_notes)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import nhypergeom + >>> import matplotlib.pyplot as plt + + Suppose we have a collection of 20 animals, of which 7 are dogs. + Then if we want to know the probability of finding a given number + of dogs (successes) in a sample with exactly 12 animals that + aren't dogs (failures), we can initialize a frozen distribution + and plot the probability mass function: + + >>> M, n, r = [20, 7, 12] + >>> rv = nhypergeom(M, n, r) + >>> x = np.arange(0, n+2) + >>> pmf_dogs = rv.pmf(x) + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(x, pmf_dogs, 'bo') + >>> ax.vlines(x, 0, pmf_dogs, lw=2) + >>> ax.set_xlabel('# of dogs in our group with given 12 failures') + >>> ax.set_ylabel('nhypergeom PMF') + >>> plt.show() + + Instead of using a frozen distribution we can also use `nhypergeom` + methods directly. To for example obtain the probability mass + function, use: + + >>> prb = nhypergeom.pmf(x, M, n, r) + + And to generate random numbers: + + >>> R = nhypergeom.rvs(M, n, r, size=10) + + To verify the relationship between `hypergeom` and `nhypergeom`, use: + + >>> from scipy.stats import hypergeom, nhypergeom + >>> M, n, r = 45, 13, 8 + >>> k = 6 + >>> nhypergeom.pmf(k, M, n, r) + 0.06180776620271643 + >>> hypergeom.pmf(k, M, n, k+r-1) * (M - n - (r-1)) / (M - (k+r-1)) + 0.06180776620271644 + + See Also + -------- + hypergeom, binom, nbinom + + References + ---------- + .. [1] Negative Hypergeometric Distribution on Wikipedia + https://en.wikipedia.org/wiki/Negative_hypergeometric_distribution + + .. [2] Negative Hypergeometric Distribution from + http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Negativehypergeometric.pdf + + """ + + def _shape_info(self): + return [_ShapeInfo("M", True, (0, np.inf), (True, False)), + _ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("r", True, (0, np.inf), (True, False))] + + def _get_support(self, M, n, r): + return 0, n + + def _argcheck(self, M, n, r): + cond = (n >= 0) & (n <= M) & (r >= 0) & (r <= M-n) + cond &= _isintegral(M) & _isintegral(n) & _isintegral(r) + return cond + + def _rvs(self, M, n, r, size=None, random_state=None): + + @_vectorize_rvs_over_shapes + def _rvs1(M, n, r, size, random_state): + # invert cdf by calculating all values in support, scalar M, n, r + a, b = self.support(M, n, r) + ks = np.arange(a, b+1) + cdf = self.cdf(ks, M, n, r) + ppf = interp1d(cdf, ks, kind='next', fill_value='extrapolate') + rvs = ppf(random_state.uniform(size=size)).astype(int) + if size is None: + return rvs.item() + return rvs + + return _rvs1(M, n, r, size=size, random_state=random_state) + + def _logpmf(self, k, M, n, r): + cond = ((r == 0) & (k == 0)) + result = _lazywhere(~cond, (k, M, n, r), + lambda k, M, n, r: + (-betaln(k+1, r) + betaln(k+r, 1) - + betaln(n-k+1, M-r-n+1) + betaln(M-r-k+1, 1) + + betaln(n+1, M-n+1) - betaln(M+1, 1)), + fillvalue=0.0) + return result + + def _pmf(self, k, M, n, r): + # same as the following but numerically more precise + # return comb(k+r-1, k) * comb(M-r-k, n-k) / comb(M, n) + return exp(self._logpmf(k, M, n, r)) + + def _stats(self, M, n, r): + # Promote the datatype to at least float + # mu = rn / (M-n+1) + M, n, r = 1.*M, 1.*n, 1.*r + mu = r*n / (M-n+1) + + var = r*(M+1)*n / ((M-n+1)*(M-n+2)) * (1 - r / (M-n+1)) + + # The skew and kurtosis are mathematically + # intractable so return `None`. See [2]_. + g1, g2 = None, None + return mu, var, g1, g2 + + +nhypergeom = nhypergeom_gen(name='nhypergeom') + + +# FIXME: Fails _cdfvec +class logser_gen(rv_discrete): + r"""A Logarithmic (Log-Series, Series) discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `logser` is: + + .. math:: + + f(k) = - \frac{p^k}{k \log(1-p)} + + for :math:`k \ge 1`, :math:`0 < p < 1` + + `logser` takes :math:`p` as shape parameter, + where :math:`p` is the probability of a single success + and :math:`1-p` is the probability of a single failure. + + %(after_notes)s + + %(example)s + + """ + + def _shape_info(self): + return [_ShapeInfo("p", False, (0, 1), (True, True))] + + def _rvs(self, p, size=None, random_state=None): + # looks wrong for p>0.5, too few k=1 + # trying to use generic is worse, no k=1 at all + return random_state.logseries(p, size=size) + + def _argcheck(self, p): + return (p > 0) & (p < 1) + + def _pmf(self, k, p): + # logser.pmf(k) = - p**k / (k*log(1-p)) + return -np.power(p, k) * 1.0 / k / special.log1p(-p) + + def _stats(self, p): + r = special.log1p(-p) + mu = p / (p - 1.0) / r + mu2p = -p / r / (p - 1.0)**2 + var = mu2p - mu*mu + mu3p = -p / r * (1.0+p) / (1.0 - p)**3 + mu3 = mu3p - 3*mu*mu2p + 2*mu**3 + g1 = mu3 / np.power(var, 1.5) + + mu4p = -p / r * ( + 1.0 / (p-1)**2 - 6*p / (p - 1)**3 + 6*p*p / (p-1)**4) + mu4 = mu4p - 4*mu3p*mu + 6*mu2p*mu*mu - 3*mu**4 + g2 = mu4 / var**2 - 3.0 + return mu, var, g1, g2 + + +logser = logser_gen(a=1, name='logser', longname='A logarithmic') + + +class poisson_gen(rv_discrete): + r"""A Poisson discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `poisson` is: + + .. math:: + + f(k) = \exp(-\mu) \frac{\mu^k}{k!} + + for :math:`k \ge 0`. + + `poisson` takes :math:`\mu \geq 0` as shape parameter. + When :math:`\mu = 0`, the ``pmf`` method + returns ``1.0`` at quantile :math:`k = 0`. + + %(after_notes)s + + %(example)s + + """ + + def _shape_info(self): + return [_ShapeInfo("mu", False, (0, np.inf), (True, False))] + + # Override rv_discrete._argcheck to allow mu=0. + def _argcheck(self, mu): + return mu >= 0 + + def _rvs(self, mu, size=None, random_state=None): + return random_state.poisson(mu, size) + + def _logpmf(self, k, mu): + Pk = special.xlogy(k, mu) - gamln(k + 1) - mu + return Pk + + def _pmf(self, k, mu): + # poisson.pmf(k) = exp(-mu) * mu**k / k! + return exp(self._logpmf(k, mu)) + + def _cdf(self, x, mu): + k = floor(x) + return special.pdtr(k, mu) + + def _sf(self, x, mu): + k = floor(x) + return special.pdtrc(k, mu) + + def _ppf(self, q, mu): + vals = ceil(special.pdtrik(q, mu)) + vals1 = np.maximum(vals - 1, 0) + temp = special.pdtr(vals1, mu) + return np.where(temp >= q, vals1, vals) + + def _stats(self, mu): + var = mu + tmp = np.asarray(mu) + mu_nonzero = tmp > 0 + g1 = _lazywhere(mu_nonzero, (tmp,), lambda x: sqrt(1.0/x), np.inf) + g2 = _lazywhere(mu_nonzero, (tmp,), lambda x: 1.0/x, np.inf) + return mu, var, g1, g2 + + +poisson = poisson_gen(name="poisson", longname='A Poisson') + + +class planck_gen(rv_discrete): + r"""A Planck discrete exponential random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `planck` is: + + .. math:: + + f(k) = (1-\exp(-\lambda)) \exp(-\lambda k) + + for :math:`k \ge 0` and :math:`\lambda > 0`. + + `planck` takes :math:`\lambda` as shape parameter. The Planck distribution + can be written as a geometric distribution (`geom`) with + :math:`p = 1 - \exp(-\lambda)` shifted by ``loc = -1``. + + %(after_notes)s + + See Also + -------- + geom + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("lambda", False, (0, np.inf), (False, False))] + + def _argcheck(self, lambda_): + return lambda_ > 0 + + def _pmf(self, k, lambda_): + return -expm1(-lambda_)*exp(-lambda_*k) + + def _cdf(self, x, lambda_): + k = floor(x) + return -expm1(-lambda_*(k+1)) + + def _sf(self, x, lambda_): + return exp(self._logsf(x, lambda_)) + + def _logsf(self, x, lambda_): + k = floor(x) + return -lambda_*(k+1) + + def _ppf(self, q, lambda_): + vals = ceil(-1.0/lambda_ * log1p(-q)-1) + vals1 = (vals-1).clip(*(self._get_support(lambda_))) + temp = self._cdf(vals1, lambda_) + return np.where(temp >= q, vals1, vals) + + def _rvs(self, lambda_, size=None, random_state=None): + # use relation to geometric distribution for sampling + p = -expm1(-lambda_) + return random_state.geometric(p, size=size) - 1.0 + + def _stats(self, lambda_): + mu = 1/expm1(lambda_) + var = exp(-lambda_)/(expm1(-lambda_))**2 + g1 = 2*cosh(lambda_/2.0) + g2 = 4+2*cosh(lambda_) + return mu, var, g1, g2 + + def _entropy(self, lambda_): + C = -expm1(-lambda_) + return lambda_*exp(-lambda_)/C - log(C) + + +planck = planck_gen(a=0, name='planck', longname='A discrete exponential ') + + +class boltzmann_gen(rv_discrete): + r"""A Boltzmann (Truncated Discrete Exponential) random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `boltzmann` is: + + .. math:: + + f(k) = (1-\exp(-\lambda)) \exp(-\lambda k) / (1-\exp(-\lambda N)) + + for :math:`k = 0,..., N-1`. + + `boltzmann` takes :math:`\lambda > 0` and :math:`N > 0` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("lambda_", False, (0, np.inf), (False, False)), + _ShapeInfo("N", True, (0, np.inf), (False, False))] + + def _argcheck(self, lambda_, N): + return (lambda_ > 0) & (N > 0) & _isintegral(N) + + def _get_support(self, lambda_, N): + return self.a, N - 1 + + def _pmf(self, k, lambda_, N): + # boltzmann.pmf(k) = + # (1-exp(-lambda_)*exp(-lambda_*k)/(1-exp(-lambda_*N)) + fact = (1-exp(-lambda_))/(1-exp(-lambda_*N)) + return fact*exp(-lambda_*k) + + def _cdf(self, x, lambda_, N): + k = floor(x) + return (1-exp(-lambda_*(k+1)))/(1-exp(-lambda_*N)) + + def _ppf(self, q, lambda_, N): + qnew = q*(1-exp(-lambda_*N)) + vals = ceil(-1.0/lambda_ * log(1-qnew)-1) + vals1 = (vals-1).clip(0.0, np.inf) + temp = self._cdf(vals1, lambda_, N) + return np.where(temp >= q, vals1, vals) + + def _stats(self, lambda_, N): + z = exp(-lambda_) + zN = exp(-lambda_*N) + mu = z/(1.0-z)-N*zN/(1-zN) + var = z/(1.0-z)**2 - N*N*zN/(1-zN)**2 + trm = (1-zN)/(1-z) + trm2 = (z*trm**2 - N*N*zN) + g1 = z*(1+z)*trm**3 - N**3*zN*(1+zN) + g1 = g1 / trm2**(1.5) + g2 = z*(1+4*z+z*z)*trm**4 - N**4 * zN*(1+4*zN+zN*zN) + g2 = g2 / trm2 / trm2 + return mu, var, g1, g2 + + +boltzmann = boltzmann_gen(name='boltzmann', a=0, + longname='A truncated discrete exponential ') + + +class randint_gen(rv_discrete): + r"""A uniform discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `randint` is: + + .. math:: + + f(k) = \frac{1}{\texttt{high} - \texttt{low}} + + for :math:`k \in \{\texttt{low}, \dots, \texttt{high} - 1\}`. + + `randint` takes :math:`\texttt{low}` and :math:`\texttt{high}` as shape + parameters. + + %(after_notes)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import randint + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + + Calculate the first four moments: + + >>> low, high = 7, 31 + >>> mean, var, skew, kurt = randint.stats(low, high, moments='mvsk') + + Display the probability mass function (``pmf``): + + >>> x = np.arange(low - 5, high + 5) + >>> ax.plot(x, randint.pmf(x, low, high), 'bo', ms=8, label='randint pmf') + >>> ax.vlines(x, 0, randint.pmf(x, low, high), colors='b', lw=5, alpha=0.5) + + Alternatively, the distribution object can be called (as a function) to + fix the shape and location. This returns a "frozen" RV object holding the + given parameters fixed. + + Freeze the distribution and display the frozen ``pmf``: + + >>> rv = randint(low, high) + >>> ax.vlines(x, 0, rv.pmf(x), colors='k', linestyles='-', + ... lw=1, label='frozen pmf') + >>> ax.legend(loc='lower center') + >>> plt.show() + + Check the relationship between the cumulative distribution function + (``cdf``) and its inverse, the percent point function (``ppf``): + + >>> q = np.arange(low, high) + >>> p = randint.cdf(q, low, high) + >>> np.allclose(q, randint.ppf(p, low, high)) + True + + Generate random numbers: + + >>> r = randint.rvs(low, high, size=1000) + + """ + + def _shape_info(self): + return [_ShapeInfo("low", True, (-np.inf, np.inf), (False, False)), + _ShapeInfo("high", True, (-np.inf, np.inf), (False, False))] + + def _argcheck(self, low, high): + return (high > low) & _isintegral(low) & _isintegral(high) + + def _get_support(self, low, high): + return low, high-1 + + def _pmf(self, k, low, high): + # randint.pmf(k) = 1./(high - low) + p = np.ones_like(k) / (high - low) + return np.where((k >= low) & (k < high), p, 0.) + + def _cdf(self, x, low, high): + k = floor(x) + return (k - low + 1.) / (high - low) + + def _ppf(self, q, low, high): + vals = ceil(q * (high - low) + low) - 1 + vals1 = (vals - 1).clip(low, high) + temp = self._cdf(vals1, low, high) + return np.where(temp >= q, vals1, vals) + + def _stats(self, low, high): + m2, m1 = np.asarray(high), np.asarray(low) + mu = (m2 + m1 - 1.0) / 2 + d = m2 - m1 + var = (d*d - 1) / 12.0 + g1 = 0.0 + g2 = -6.0/5.0 * (d*d + 1.0) / (d*d - 1.0) + return mu, var, g1, g2 + + def _rvs(self, low, high, size=None, random_state=None): + """An array of *size* random integers >= ``low`` and < ``high``.""" + if np.asarray(low).size == 1 and np.asarray(high).size == 1: + # no need to vectorize in that case + return rng_integers(random_state, low, high, size=size) + + if size is not None: + # NumPy's RandomState.randint() doesn't broadcast its arguments. + # Use `broadcast_to()` to extend the shapes of low and high + # up to size. Then we can use the numpy.vectorize'd + # randint without needing to pass it a `size` argument. + low = np.broadcast_to(low, size) + high = np.broadcast_to(high, size) + randint = np.vectorize(partial(rng_integers, random_state), + otypes=[np.dtype(int)]) + return randint(low, high) + + def _entropy(self, low, high): + return log(high - low) + + +randint = randint_gen(name='randint', longname='A discrete uniform ' + '(random integer)') + + +# FIXME: problems sampling. +class zipf_gen(rv_discrete): + r"""A Zipf (Zeta) discrete random variable. + + %(before_notes)s + + See Also + -------- + zipfian + + Notes + ----- + The probability mass function for `zipf` is: + + .. math:: + + f(k, a) = \frac{1}{\zeta(a) k^a} + + for :math:`k \ge 1`, :math:`a > 1`. + + `zipf` takes :math:`a > 1` as shape parameter. :math:`\zeta` is the + Riemann zeta function (`scipy.special.zeta`) + + The Zipf distribution is also known as the zeta distribution, which is + a special case of the Zipfian distribution (`zipfian`). + + %(after_notes)s + + References + ---------- + .. [1] "Zeta Distribution", Wikipedia, + https://en.wikipedia.org/wiki/Zeta_distribution + + %(example)s + + Confirm that `zipf` is the large `n` limit of `zipfian`. + + >>> import numpy as np + >>> from scipy.stats import zipf, zipfian + >>> k = np.arange(11) + >>> np.allclose(zipf.pmf(k, a), zipfian.pmf(k, a, n=10000000)) + True + + """ + + def _shape_info(self): + return [_ShapeInfo("a", False, (1, np.inf), (False, False))] + + def _rvs(self, a, size=None, random_state=None): + return random_state.zipf(a, size=size) + + def _argcheck(self, a): + return a > 1 + + def _pmf(self, k, a): + k = k.astype(np.float64) + # zipf.pmf(k, a) = 1/(zeta(a) * k**a) + Pk = 1.0 / special.zeta(a, 1) * k**-a + return Pk + + def _munp(self, n, a): + return _lazywhere( + a > n + 1, (a, n), + lambda a, n: special.zeta(a - n, 1) / special.zeta(a, 1), + np.inf) + + +zipf = zipf_gen(a=1, name='zipf', longname='A Zipf') + + +def _gen_harmonic_gt1(n, a): + """Generalized harmonic number, a > 1""" + # See https://en.wikipedia.org/wiki/Harmonic_number; search for "hurwitz" + return zeta(a, 1) - zeta(a, n+1) + + +def _gen_harmonic_leq1(n, a): + """Generalized harmonic number, a <= 1""" + if not np.size(n): + return n + n_max = np.max(n) # loop starts at maximum of all n + out = np.zeros_like(a, dtype=float) + # add terms of harmonic series; starting from smallest to avoid roundoff + for i in np.arange(n_max, 0, -1, dtype=float): + mask = i <= n # don't add terms after nth + out[mask] += 1/i**a[mask] + return out + + +def _gen_harmonic(n, a): + """Generalized harmonic number""" + n, a = np.broadcast_arrays(n, a) + return _lazywhere(a > 1, (n, a), + f=_gen_harmonic_gt1, f2=_gen_harmonic_leq1) + + +class zipfian_gen(rv_discrete): + r"""A Zipfian discrete random variable. + + %(before_notes)s + + See Also + -------- + zipf + + Notes + ----- + The probability mass function for `zipfian` is: + + .. math:: + + f(k, a, n) = \frac{1}{H_{n,a} k^a} + + for :math:`k \in \{1, 2, \dots, n-1, n\}`, :math:`a \ge 0`, + :math:`n \in \{1, 2, 3, \dots\}`. + + `zipfian` takes :math:`a` and :math:`n` as shape parameters. + :math:`H_{n,a}` is the :math:`n`:sup:`th` generalized harmonic + number of order :math:`a`. + + The Zipfian distribution reduces to the Zipf (zeta) distribution as + :math:`n \rightarrow \infty`. + + %(after_notes)s + + References + ---------- + .. [1] "Zipf's Law", Wikipedia, https://en.wikipedia.org/wiki/Zipf's_law + .. [2] Larry Leemis, "Zipf Distribution", Univariate Distribution + Relationships. http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Zipf.pdf + + %(example)s + + Confirm that `zipfian` reduces to `zipf` for large `n`, `a > 1`. + + >>> import numpy as np + >>> from scipy.stats import zipf, zipfian + >>> k = np.arange(11) + >>> np.allclose(zipfian.pmf(k, a=3.5, n=10000000), zipf.pmf(k, a=3.5)) + True + + """ + + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (True, False)), + _ShapeInfo("n", True, (0, np.inf), (False, False))] + + def _argcheck(self, a, n): + # we need np.asarray here because moment (maybe others) don't convert + return (a >= 0) & (n > 0) & (n == np.asarray(n, dtype=int)) + + def _get_support(self, a, n): + return 1, n + + def _pmf(self, k, a, n): + k = k.astype(np.float64) + return 1.0 / _gen_harmonic(n, a) * k**-a + + def _cdf(self, k, a, n): + return _gen_harmonic(k, a) / _gen_harmonic(n, a) + + def _sf(self, k, a, n): + k = k + 1 # # to match SciPy convention + # see http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Zipf.pdf + return ((k**a*(_gen_harmonic(n, a) - _gen_harmonic(k, a)) + 1) + / (k**a*_gen_harmonic(n, a))) + + def _stats(self, a, n): + # see # see http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Zipf.pdf + Hna = _gen_harmonic(n, a) + Hna1 = _gen_harmonic(n, a-1) + Hna2 = _gen_harmonic(n, a-2) + Hna3 = _gen_harmonic(n, a-3) + Hna4 = _gen_harmonic(n, a-4) + mu1 = Hna1/Hna + mu2n = (Hna2*Hna - Hna1**2) + mu2d = Hna**2 + mu2 = mu2n / mu2d + g1 = (Hna3/Hna - 3*Hna1*Hna2/Hna**2 + 2*Hna1**3/Hna**3)/mu2**(3/2) + g2 = (Hna**3*Hna4 - 4*Hna**2*Hna1*Hna3 + 6*Hna*Hna1**2*Hna2 + - 3*Hna1**4) / mu2n**2 + g2 -= 3 + return mu1, mu2, g1, g2 + + +zipfian = zipfian_gen(a=1, name='zipfian', longname='A Zipfian') + + +class dlaplace_gen(rv_discrete): + r"""A Laplacian discrete random variable. + + %(before_notes)s + + Notes + ----- + The probability mass function for `dlaplace` is: + + .. math:: + + f(k) = \tanh(a/2) \exp(-a |k|) + + for integers :math:`k` and :math:`a > 0`. + + `dlaplace` takes :math:`a` as shape parameter. + + %(after_notes)s + + %(example)s + + """ + + def _shape_info(self): + return [_ShapeInfo("a", False, (0, np.inf), (False, False))] + + def _pmf(self, k, a): + # dlaplace.pmf(k) = tanh(a/2) * exp(-a*abs(k)) + return tanh(a/2.0) * exp(-a * abs(k)) + + def _cdf(self, x, a): + k = floor(x) + + def f(k, a): + return 1.0 - exp(-a * k) / (exp(a) + 1) + + def f2(k, a): + return exp(a * (k + 1)) / (exp(a) + 1) + + return _lazywhere(k >= 0, (k, a), f=f, f2=f2) + + def _ppf(self, q, a): + const = 1 + exp(a) + vals = ceil(np.where(q < 1.0 / (1 + exp(-a)), + log(q*const) / a - 1, + -log((1-q) * const) / a)) + vals1 = vals - 1 + return np.where(self._cdf(vals1, a) >= q, vals1, vals) + + def _stats(self, a): + ea = exp(a) + mu2 = 2.*ea/(ea-1.)**2 + mu4 = 2.*ea*(ea**2+10.*ea+1.) / (ea-1.)**4 + return 0., mu2, 0., mu4/mu2**2 - 3. + + def _entropy(self, a): + return a / sinh(a) - log(tanh(a/2.0)) + + def _rvs(self, a, size=None, random_state=None): + # The discrete Laplace is equivalent to the two-sided geometric + # distribution with PMF: + # f(k) = (1 - alpha)/(1 + alpha) * alpha^abs(k) + # Reference: + # https://www.sciencedirect.com/science/ + # article/abs/pii/S0378375804003519 + # Furthermore, the two-sided geometric distribution is + # equivalent to the difference between two iid geometric + # distributions. + # Reference (page 179): + # https://pdfs.semanticscholar.org/61b3/ + # b99f466815808fd0d03f5d2791eea8b541a1.pdf + # Thus, we can leverage the following: + # 1) alpha = e^-a + # 2) probability_of_success = 1 - alpha (Bernoulli trial) + probOfSuccess = -np.expm1(-np.asarray(a)) + x = random_state.geometric(probOfSuccess, size=size) + y = random_state.geometric(probOfSuccess, size=size) + return x - y + + +dlaplace = dlaplace_gen(a=-np.inf, + name='dlaplace', longname='A discrete Laplacian') + + +class skellam_gen(rv_discrete): + r"""A Skellam discrete random variable. + + %(before_notes)s + + Notes + ----- + Probability distribution of the difference of two correlated or + uncorrelated Poisson random variables. + + Let :math:`k_1` and :math:`k_2` be two Poisson-distributed r.v. with + expected values :math:`\lambda_1` and :math:`\lambda_2`. Then, + :math:`k_1 - k_2` follows a Skellam distribution with parameters + :math:`\mu_1 = \lambda_1 - \rho \sqrt{\lambda_1 \lambda_2}` and + :math:`\mu_2 = \lambda_2 - \rho \sqrt{\lambda_1 \lambda_2}`, where + :math:`\rho` is the correlation coefficient between :math:`k_1` and + :math:`k_2`. If the two Poisson-distributed r.v. are independent then + :math:`\rho = 0`. + + Parameters :math:`\mu_1` and :math:`\mu_2` must be strictly positive. + + For details see: https://en.wikipedia.org/wiki/Skellam_distribution + + `skellam` takes :math:`\mu_1` and :math:`\mu_2` as shape parameters. + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("mu1", False, (0, np.inf), (False, False)), + _ShapeInfo("mu2", False, (0, np.inf), (False, False))] + + def _rvs(self, mu1, mu2, size=None, random_state=None): + n = size + return (random_state.poisson(mu1, n) - + random_state.poisson(mu2, n)) + + def _pmf(self, x, mu1, mu2): + with np.errstate(over='ignore'): # see gh-17432 + px = np.where(x < 0, + _boost._ncx2_pdf(2*mu2, 2*(1-x), 2*mu1)*2, + _boost._ncx2_pdf(2*mu1, 2*(1+x), 2*mu2)*2) + # ncx2.pdf() returns nan's for extremely low probabilities + return px + + def _cdf(self, x, mu1, mu2): + x = floor(x) + with np.errstate(over='ignore'): # see gh-17432 + px = np.where(x < 0, + _boost._ncx2_cdf(2*mu2, -2*x, 2*mu1), + 1 - _boost._ncx2_cdf(2*mu1, 2*(x+1), 2*mu2)) + return px + + def _stats(self, mu1, mu2): + mean = mu1 - mu2 + var = mu1 + mu2 + g1 = mean / sqrt((var)**3) + g2 = 1 / var + return mean, var, g1, g2 + + +skellam = skellam_gen(a=-np.inf, name="skellam", longname='A Skellam') + + +class yulesimon_gen(rv_discrete): + r"""A Yule-Simon discrete random variable. + + %(before_notes)s + + Notes + ----- + + The probability mass function for the `yulesimon` is: + + .. math:: + + f(k) = \alpha B(k, \alpha+1) + + for :math:`k=1,2,3,...`, where :math:`\alpha>0`. + Here :math:`B` refers to the `scipy.special.beta` function. + + The sampling of random variates is based on pg 553, Section 6.3 of [1]_. + Our notation maps to the referenced logic via :math:`\alpha=a-1`. + + For details see the wikipedia entry [2]_. + + References + ---------- + .. [1] Devroye, Luc. "Non-uniform Random Variate Generation", + (1986) Springer, New York. + + .. [2] https://en.wikipedia.org/wiki/Yule-Simon_distribution + + %(after_notes)s + + %(example)s + + """ + def _shape_info(self): + return [_ShapeInfo("alpha", False, (0, np.inf), (False, False))] + + def _rvs(self, alpha, size=None, random_state=None): + E1 = random_state.standard_exponential(size) + E2 = random_state.standard_exponential(size) + ans = ceil(-E1 / log1p(-exp(-E2 / alpha))) + return ans + + def _pmf(self, x, alpha): + return alpha * special.beta(x, alpha + 1) + + def _argcheck(self, alpha): + return (alpha > 0) + + def _logpmf(self, x, alpha): + return log(alpha) + special.betaln(x, alpha + 1) + + def _cdf(self, x, alpha): + return 1 - x * special.beta(x, alpha + 1) + + def _sf(self, x, alpha): + return x * special.beta(x, alpha + 1) + + def _logsf(self, x, alpha): + return log(x) + special.betaln(x, alpha + 1) + + def _stats(self, alpha): + mu = np.where(alpha <= 1, np.inf, alpha / (alpha - 1)) + mu2 = np.where(alpha > 2, + alpha**2 / ((alpha - 2.0) * (alpha - 1)**2), + np.inf) + mu2 = np.where(alpha <= 1, np.nan, mu2) + g1 = np.where(alpha > 3, + sqrt(alpha - 2) * (alpha + 1)**2 / (alpha * (alpha - 3)), + np.inf) + g1 = np.where(alpha <= 2, np.nan, g1) + g2 = np.where(alpha > 4, + alpha + 3 + ((11 * alpha**3 - 49 * alpha - 22) / + (alpha * (alpha - 4) * (alpha - 3))), + np.inf) + g2 = np.where(alpha <= 2, np.nan, g2) + return mu, mu2, g1, g2 + + +yulesimon = yulesimon_gen(name='yulesimon', a=1) + + +def _vectorize_rvs_over_shapes(_rvs1): + """Decorator that vectorizes _rvs method to work on ndarray shapes""" + # _rvs1 must be a _function_ that accepts _scalar_ args as positional + # arguments, `size` and `random_state` as keyword arguments. + # _rvs1 must return a random variate array with shape `size`. If `size` is + # None, _rvs1 must return a scalar. + # When applied to _rvs1, this decorator broadcasts ndarray args + # and loops over them, calling _rvs1 for each set of scalar args. + # For usage example, see _nchypergeom_gen + def _rvs(*args, size, random_state): + _rvs1_size, _rvs1_indices = _check_shape(args[0].shape, size) + + size = np.array(size) + _rvs1_size = np.array(_rvs1_size) + _rvs1_indices = np.array(_rvs1_indices) + + if np.all(_rvs1_indices): # all args are scalars + return _rvs1(*args, size, random_state) + + out = np.empty(size) + + # out.shape can mix dimensions associated with arg_shape and _rvs1_size + # Sort them to arg_shape + _rvs1_size for easy indexing of dimensions + # corresponding with the different sets of scalar args + j0 = np.arange(out.ndim) + j1 = np.hstack((j0[~_rvs1_indices], j0[_rvs1_indices])) + out = np.moveaxis(out, j1, j0) + + for i in np.ndindex(*size[~_rvs1_indices]): + # arg can be squeezed because singleton dimensions will be + # associated with _rvs1_size, not arg_shape per _check_shape + out[i] = _rvs1(*[np.squeeze(arg)[i] for arg in args], + _rvs1_size, random_state) + + return np.moveaxis(out, j0, j1) # move axes back before returning + return _rvs + + +class _nchypergeom_gen(rv_discrete): + r"""A noncentral hypergeometric discrete random variable. + + For subclassing by nchypergeom_fisher_gen and nchypergeom_wallenius_gen. + + """ + + rvs_name = None + dist = None + + def _shape_info(self): + return [_ShapeInfo("M", True, (0, np.inf), (True, False)), + _ShapeInfo("n", True, (0, np.inf), (True, False)), + _ShapeInfo("N", True, (0, np.inf), (True, False)), + _ShapeInfo("odds", False, (0, np.inf), (False, False))] + + def _get_support(self, M, n, N, odds): + N, m1, n = M, n, N # follow Wikipedia notation + m2 = N - m1 + x_min = np.maximum(0, n - m2) + x_max = np.minimum(n, m1) + return x_min, x_max + + def _argcheck(self, M, n, N, odds): + M, n = np.asarray(M), np.asarray(n), + N, odds = np.asarray(N), np.asarray(odds) + cond1 = (M.astype(int) == M) & (M >= 0) + cond2 = (n.astype(int) == n) & (n >= 0) + cond3 = (N.astype(int) == N) & (N >= 0) + cond4 = odds > 0 + cond5 = N <= M + cond6 = n <= M + return cond1 & cond2 & cond3 & cond4 & cond5 & cond6 + + def _rvs(self, M, n, N, odds, size=None, random_state=None): + + @_vectorize_rvs_over_shapes + def _rvs1(M, n, N, odds, size, random_state): + length = np.prod(size) + urn = _PyStochasticLib3() + rv_gen = getattr(urn, self.rvs_name) + rvs = rv_gen(N, n, M, odds, length, random_state) + rvs = rvs.reshape(size) + return rvs + + return _rvs1(M, n, N, odds, size=size, random_state=random_state) + + def _pmf(self, x, M, n, N, odds): + + x, M, n, N, odds = np.broadcast_arrays(x, M, n, N, odds) + if x.size == 0: # np.vectorize doesn't work with zero size input + return np.empty_like(x) + + @np.vectorize + def _pmf1(x, M, n, N, odds): + urn = self.dist(N, n, M, odds, 1e-12) + return urn.probability(x) + + return _pmf1(x, M, n, N, odds) + + def _stats(self, M, n, N, odds, moments): + + @np.vectorize + def _moments1(M, n, N, odds): + urn = self.dist(N, n, M, odds, 1e-12) + return urn.moments() + + m, v = (_moments1(M, n, N, odds) if ("m" in moments or "v" in moments) + else (None, None)) + s, k = None, None + return m, v, s, k + + +class nchypergeom_fisher_gen(_nchypergeom_gen): + r"""A Fisher's noncentral hypergeometric discrete random variable. + + Fisher's noncentral hypergeometric distribution models drawing objects of + two types from a bin. `M` is the total number of objects, `n` is the + number of Type I objects, and `odds` is the odds ratio: the odds of + selecting a Type I object rather than a Type II object when there is only + one object of each type. + The random variate represents the number of Type I objects drawn if we + take a handful of objects from the bin at once and find out afterwards + that we took `N` objects. + + %(before_notes)s + + See Also + -------- + nchypergeom_wallenius, hypergeom, nhypergeom + + Notes + ----- + Let mathematical symbols :math:`N`, :math:`n`, and :math:`M` correspond + with parameters `N`, `n`, and `M` (respectively) as defined above. + + The probability mass function is defined as + + .. math:: + + p(x; M, n, N, \omega) = + \frac{\binom{n}{x}\binom{M - n}{N-x}\omega^x}{P_0}, + + for + :math:`x \in [x_l, x_u]`, + :math:`M \in {\mathbb N}`, + :math:`n \in [0, M]`, + :math:`N \in [0, M]`, + :math:`\omega > 0`, + where + :math:`x_l = \max(0, N - (M - n))`, + :math:`x_u = \min(N, n)`, + + .. math:: + + P_0 = \sum_{y=x_l}^{x_u} \binom{n}{y}\binom{M - n}{N-y}\omega^y, + + and the binomial coefficients are defined as + + .. math:: \binom{n}{k} \equiv \frac{n!}{k! (n - k)!}. + + `nchypergeom_fisher` uses the BiasedUrn package by Agner Fog with + permission for it to be distributed under SciPy's license. + + The symbols used to denote the shape parameters (`N`, `n`, and `M`) are not + universally accepted; they are chosen for consistency with `hypergeom`. + + Note that Fisher's noncentral hypergeometric distribution is distinct + from Wallenius' noncentral hypergeometric distribution, which models + drawing a pre-determined `N` objects from a bin one by one. + When the odds ratio is unity, however, both distributions reduce to the + ordinary hypergeometric distribution. + + %(after_notes)s + + References + ---------- + .. [1] Agner Fog, "Biased Urn Theory". + https://cran.r-project.org/web/packages/BiasedUrn/vignettes/UrnTheory.pdf + + .. [2] "Fisher's noncentral hypergeometric distribution", Wikipedia, + https://en.wikipedia.org/wiki/Fisher's_noncentral_hypergeometric_distribution + + %(example)s + + """ + + rvs_name = "rvs_fisher" + dist = _PyFishersNCHypergeometric + + +nchypergeom_fisher = nchypergeom_fisher_gen( + name='nchypergeom_fisher', + longname="A Fisher's noncentral hypergeometric") + + +class nchypergeom_wallenius_gen(_nchypergeom_gen): + r"""A Wallenius' noncentral hypergeometric discrete random variable. + + Wallenius' noncentral hypergeometric distribution models drawing objects of + two types from a bin. `M` is the total number of objects, `n` is the + number of Type I objects, and `odds` is the odds ratio: the odds of + selecting a Type I object rather than a Type II object when there is only + one object of each type. + The random variate represents the number of Type I objects drawn if we + draw a pre-determined `N` objects from a bin one by one. + + %(before_notes)s + + See Also + -------- + nchypergeom_fisher, hypergeom, nhypergeom + + Notes + ----- + Let mathematical symbols :math:`N`, :math:`n`, and :math:`M` correspond + with parameters `N`, `n`, and `M` (respectively) as defined above. + + The probability mass function is defined as + + .. math:: + + p(x; N, n, M) = \binom{n}{x} \binom{M - n}{N-x} + \int_0^1 \left(1-t^{\omega/D}\right)^x\left(1-t^{1/D}\right)^{N-x} dt + + for + :math:`x \in [x_l, x_u]`, + :math:`M \in {\mathbb N}`, + :math:`n \in [0, M]`, + :math:`N \in [0, M]`, + :math:`\omega > 0`, + where + :math:`x_l = \max(0, N - (M - n))`, + :math:`x_u = \min(N, n)`, + + .. math:: + + D = \omega(n - x) + ((M - n)-(N-x)), + + and the binomial coefficients are defined as + + .. math:: \binom{n}{k} \equiv \frac{n!}{k! (n - k)!}. + + `nchypergeom_wallenius` uses the BiasedUrn package by Agner Fog with + permission for it to be distributed under SciPy's license. + + The symbols used to denote the shape parameters (`N`, `n`, and `M`) are not + universally accepted; they are chosen for consistency with `hypergeom`. + + Note that Wallenius' noncentral hypergeometric distribution is distinct + from Fisher's noncentral hypergeometric distribution, which models + take a handful of objects from the bin at once, finding out afterwards + that `N` objects were taken. + When the odds ratio is unity, however, both distributions reduce to the + ordinary hypergeometric distribution. + + %(after_notes)s + + References + ---------- + .. [1] Agner Fog, "Biased Urn Theory". + https://cran.r-project.org/web/packages/BiasedUrn/vignettes/UrnTheory.pdf + + .. [2] "Wallenius' noncentral hypergeometric distribution", Wikipedia, + https://en.wikipedia.org/wiki/Wallenius'_noncentral_hypergeometric_distribution + + %(example)s + + """ + + rvs_name = "rvs_wallenius" + dist = _PyWalleniusNCHypergeometric + + +nchypergeom_wallenius = nchypergeom_wallenius_gen( + name='nchypergeom_wallenius', + longname="A Wallenius' noncentral hypergeometric") + + +# Collect names of classes and objects in this module. +pairs = list(globals().copy().items()) +_distn_names, _distn_gen_names = get_distribution_names(pairs, rv_discrete) + +__all__ = _distn_names + _distn_gen_names diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distn_infrastructure.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distn_infrastructure.py new file mode 100644 index 0000000000000000000000000000000000000000..3eb1e85be30c7e4ebc67113ede53e687f48d955d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distn_infrastructure.py @@ -0,0 +1,4098 @@ +# +# Author: Travis Oliphant 2002-2011 with contributions from +# SciPy Developers 2004-2011 +# +from scipy._lib._util import getfullargspec_no_self as _getfullargspec + +import sys +import keyword +import re +import types +import warnings +from itertools import zip_longest + +from scipy._lib import doccer +from ._distr_params import distcont, distdiscrete +from scipy._lib._util import check_random_state + +from scipy.special import comb, entr + + +# for root finding for continuous distribution ppf, and maximum likelihood +# estimation +from scipy import optimize + +# for functions of continuous distributions (e.g. moments, entropy, cdf) +from scipy import integrate + +# to approximate the pdf of a continuous distribution given its cdf +from scipy._lib._finite_differences import _derivative + +# for scipy.stats.entropy. Attempts to import just that function or file +# have cause import problems +from scipy import stats + +from numpy import (arange, putmask, ones, shape, ndarray, zeros, floor, + logical_and, log, sqrt, place, argmax, vectorize, asarray, + nan, inf, isinf, empty) + +import numpy as np +from ._constants import _XMAX, _LOGXMAX +from ._censored_data import CensoredData +from scipy.stats._warnings_errors import FitError + +# These are the docstring parts used for substitution in specific +# distribution docstrings + +docheaders = {'methods': """\nMethods\n-------\n""", + 'notes': """\nNotes\n-----\n""", + 'examples': """\nExamples\n--------\n"""} + +_doc_rvs = """\ +rvs(%(shapes)s, loc=0, scale=1, size=1, random_state=None) + Random variates. +""" +_doc_pdf = """\ +pdf(x, %(shapes)s, loc=0, scale=1) + Probability density function. +""" +_doc_logpdf = """\ +logpdf(x, %(shapes)s, loc=0, scale=1) + Log of the probability density function. +""" +_doc_pmf = """\ +pmf(k, %(shapes)s, loc=0, scale=1) + Probability mass function. +""" +_doc_logpmf = """\ +logpmf(k, %(shapes)s, loc=0, scale=1) + Log of the probability mass function. +""" +_doc_cdf = """\ +cdf(x, %(shapes)s, loc=0, scale=1) + Cumulative distribution function. +""" +_doc_logcdf = """\ +logcdf(x, %(shapes)s, loc=0, scale=1) + Log of the cumulative distribution function. +""" +_doc_sf = """\ +sf(x, %(shapes)s, loc=0, scale=1) + Survival function (also defined as ``1 - cdf``, but `sf` is sometimes more accurate). +""" # noqa: E501 +_doc_logsf = """\ +logsf(x, %(shapes)s, loc=0, scale=1) + Log of the survival function. +""" +_doc_ppf = """\ +ppf(q, %(shapes)s, loc=0, scale=1) + Percent point function (inverse of ``cdf`` --- percentiles). +""" +_doc_isf = """\ +isf(q, %(shapes)s, loc=0, scale=1) + Inverse survival function (inverse of ``sf``). +""" +_doc_moment = """\ +moment(order, %(shapes)s, loc=0, scale=1) + Non-central moment of the specified order. +""" +_doc_stats = """\ +stats(%(shapes)s, loc=0, scale=1, moments='mv') + Mean('m'), variance('v'), skew('s'), and/or kurtosis('k'). +""" +_doc_entropy = """\ +entropy(%(shapes)s, loc=0, scale=1) + (Differential) entropy of the RV. +""" +_doc_fit = """\ +fit(data) + Parameter estimates for generic data. + See `scipy.stats.rv_continuous.fit `__ for detailed documentation of the + keyword arguments. +""" # noqa: E501 +_doc_expect = """\ +expect(func, args=(%(shapes_)s), loc=0, scale=1, lb=None, ub=None, conditional=False, **kwds) + Expected value of a function (of one argument) with respect to the distribution. +""" # noqa: E501 +_doc_expect_discrete = """\ +expect(func, args=(%(shapes_)s), loc=0, lb=None, ub=None, conditional=False) + Expected value of a function (of one argument) with respect to the distribution. +""" +_doc_median = """\ +median(%(shapes)s, loc=0, scale=1) + Median of the distribution. +""" +_doc_mean = """\ +mean(%(shapes)s, loc=0, scale=1) + Mean of the distribution. +""" +_doc_var = """\ +var(%(shapes)s, loc=0, scale=1) + Variance of the distribution. +""" +_doc_std = """\ +std(%(shapes)s, loc=0, scale=1) + Standard deviation of the distribution. +""" +_doc_interval = """\ +interval(confidence, %(shapes)s, loc=0, scale=1) + Confidence interval with equal areas around the median. +""" +_doc_allmethods = ''.join([docheaders['methods'], _doc_rvs, _doc_pdf, + _doc_logpdf, _doc_cdf, _doc_logcdf, _doc_sf, + _doc_logsf, _doc_ppf, _doc_isf, _doc_moment, + _doc_stats, _doc_entropy, _doc_fit, + _doc_expect, _doc_median, + _doc_mean, _doc_var, _doc_std, _doc_interval]) + +_doc_default_longsummary = """\ +As an instance of the `rv_continuous` class, `%(name)s` object inherits from it +a collection of generic methods (see below for the full list), +and completes them with details specific for this particular distribution. +""" + +_doc_default_frozen_note = """ +Alternatively, the object may be called (as a function) to fix the shape, +location, and scale parameters returning a "frozen" continuous RV object: + +rv = %(name)s(%(shapes)s, loc=0, scale=1) + - Frozen RV object with the same methods but holding the given shape, + location, and scale fixed. +""" +_doc_default_example = """\ +Examples +-------- +>>> import numpy as np +>>> from scipy.stats import %(name)s +>>> import matplotlib.pyplot as plt +>>> fig, ax = plt.subplots(1, 1) + +Calculate the first four moments: + +%(set_vals_stmt)s +>>> mean, var, skew, kurt = %(name)s.stats(%(shapes)s, moments='mvsk') + +Display the probability density function (``pdf``): + +>>> x = np.linspace(%(name)s.ppf(0.01, %(shapes)s), +... %(name)s.ppf(0.99, %(shapes)s), 100) +>>> ax.plot(x, %(name)s.pdf(x, %(shapes)s), +... 'r-', lw=5, alpha=0.6, label='%(name)s pdf') + +Alternatively, the distribution object can be called (as a function) +to fix the shape, location and scale parameters. This returns a "frozen" +RV object holding the given parameters fixed. + +Freeze the distribution and display the frozen ``pdf``: + +>>> rv = %(name)s(%(shapes)s) +>>> ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf') + +Check accuracy of ``cdf`` and ``ppf``: + +>>> vals = %(name)s.ppf([0.001, 0.5, 0.999], %(shapes)s) +>>> np.allclose([0.001, 0.5, 0.999], %(name)s.cdf(vals, %(shapes)s)) +True + +Generate random numbers: + +>>> r = %(name)s.rvs(%(shapes)s, size=1000) + +And compare the histogram: + +>>> ax.hist(r, density=True, bins='auto', histtype='stepfilled', alpha=0.2) +>>> ax.set_xlim([x[0], x[-1]]) +>>> ax.legend(loc='best', frameon=False) +>>> plt.show() + +""" + +_doc_default_locscale = """\ +The probability density above is defined in the "standardized" form. To shift +and/or scale the distribution use the ``loc`` and ``scale`` parameters. +Specifically, ``%(name)s.pdf(x, %(shapes)s, loc, scale)`` is identically +equivalent to ``%(name)s.pdf(y, %(shapes)s) / scale`` with +``y = (x - loc) / scale``. Note that shifting the location of a distribution +does not make it a "noncentral" distribution; noncentral generalizations of +some distributions are available in separate classes. +""" + +_doc_default = ''.join([_doc_default_longsummary, + _doc_allmethods, + '\n', + _doc_default_example]) + +_doc_default_before_notes = ''.join([_doc_default_longsummary, + _doc_allmethods]) + +docdict = { + 'rvs': _doc_rvs, + 'pdf': _doc_pdf, + 'logpdf': _doc_logpdf, + 'cdf': _doc_cdf, + 'logcdf': _doc_logcdf, + 'sf': _doc_sf, + 'logsf': _doc_logsf, + 'ppf': _doc_ppf, + 'isf': _doc_isf, + 'stats': _doc_stats, + 'entropy': _doc_entropy, + 'fit': _doc_fit, + 'moment': _doc_moment, + 'expect': _doc_expect, + 'interval': _doc_interval, + 'mean': _doc_mean, + 'std': _doc_std, + 'var': _doc_var, + 'median': _doc_median, + 'allmethods': _doc_allmethods, + 'longsummary': _doc_default_longsummary, + 'frozennote': _doc_default_frozen_note, + 'example': _doc_default_example, + 'default': _doc_default, + 'before_notes': _doc_default_before_notes, + 'after_notes': _doc_default_locscale +} + +# Reuse common content between continuous and discrete docs, change some +# minor bits. +docdict_discrete = docdict.copy() + +docdict_discrete['pmf'] = _doc_pmf +docdict_discrete['logpmf'] = _doc_logpmf +docdict_discrete['expect'] = _doc_expect_discrete +_doc_disc_methods = ['rvs', 'pmf', 'logpmf', 'cdf', 'logcdf', 'sf', 'logsf', + 'ppf', 'isf', 'stats', 'entropy', 'expect', 'median', + 'mean', 'var', 'std', 'interval'] +for obj in _doc_disc_methods: + docdict_discrete[obj] = docdict_discrete[obj].replace(', scale=1', '') + +_doc_disc_methods_err_varname = ['cdf', 'logcdf', 'sf', 'logsf'] +for obj in _doc_disc_methods_err_varname: + docdict_discrete[obj] = docdict_discrete[obj].replace('(x, ', '(k, ') + +docdict_discrete.pop('pdf') +docdict_discrete.pop('logpdf') + +_doc_allmethods = ''.join([docdict_discrete[obj] for obj in _doc_disc_methods]) +docdict_discrete['allmethods'] = docheaders['methods'] + _doc_allmethods + +docdict_discrete['longsummary'] = _doc_default_longsummary.replace( + 'rv_continuous', 'rv_discrete') + +_doc_default_frozen_note = """ +Alternatively, the object may be called (as a function) to fix the shape and +location parameters returning a "frozen" discrete RV object: + +rv = %(name)s(%(shapes)s, loc=0) + - Frozen RV object with the same methods but holding the given shape and + location fixed. +""" +docdict_discrete['frozennote'] = _doc_default_frozen_note + +_doc_default_discrete_example = """\ +Examples +-------- +>>> import numpy as np +>>> from scipy.stats import %(name)s +>>> import matplotlib.pyplot as plt +>>> fig, ax = plt.subplots(1, 1) + +Calculate the first four moments: + +%(set_vals_stmt)s +>>> mean, var, skew, kurt = %(name)s.stats(%(shapes)s, moments='mvsk') + +Display the probability mass function (``pmf``): + +>>> x = np.arange(%(name)s.ppf(0.01, %(shapes)s), +... %(name)s.ppf(0.99, %(shapes)s)) +>>> ax.plot(x, %(name)s.pmf(x, %(shapes)s), 'bo', ms=8, label='%(name)s pmf') +>>> ax.vlines(x, 0, %(name)s.pmf(x, %(shapes)s), colors='b', lw=5, alpha=0.5) + +Alternatively, the distribution object can be called (as a function) +to fix the shape and location. This returns a "frozen" RV object holding +the given parameters fixed. + +Freeze the distribution and display the frozen ``pmf``: + +>>> rv = %(name)s(%(shapes)s) +>>> ax.vlines(x, 0, rv.pmf(x), colors='k', linestyles='-', lw=1, +... label='frozen pmf') +>>> ax.legend(loc='best', frameon=False) +>>> plt.show() + +Check accuracy of ``cdf`` and ``ppf``: + +>>> prob = %(name)s.cdf(x, %(shapes)s) +>>> np.allclose(x, %(name)s.ppf(prob, %(shapes)s)) +True + +Generate random numbers: + +>>> r = %(name)s.rvs(%(shapes)s, size=1000) +""" + + +_doc_default_discrete_locscale = """\ +The probability mass function above is defined in the "standardized" form. +To shift distribution use the ``loc`` parameter. +Specifically, ``%(name)s.pmf(k, %(shapes)s, loc)`` is identically +equivalent to ``%(name)s.pmf(k - loc, %(shapes)s)``. +""" + +docdict_discrete['example'] = _doc_default_discrete_example +docdict_discrete['after_notes'] = _doc_default_discrete_locscale + +_doc_default_before_notes = ''.join([docdict_discrete['longsummary'], + docdict_discrete['allmethods']]) +docdict_discrete['before_notes'] = _doc_default_before_notes + +_doc_default_disc = ''.join([docdict_discrete['longsummary'], + docdict_discrete['allmethods'], + docdict_discrete['frozennote'], + docdict_discrete['example']]) +docdict_discrete['default'] = _doc_default_disc + +# clean up all the separate docstring elements, we do not need them anymore +for obj in [s for s in dir() if s.startswith('_doc_')]: + exec('del ' + obj) +del obj + + +def _moment(data, n, mu=None): + if mu is None: + mu = data.mean() + return ((data - mu)**n).mean() + + +def _moment_from_stats(n, mu, mu2, g1, g2, moment_func, args): + if (n == 0): + return 1.0 + elif (n == 1): + if mu is None: + val = moment_func(1, *args) + else: + val = mu + elif (n == 2): + if mu2 is None or mu is None: + val = moment_func(2, *args) + else: + val = mu2 + mu*mu + elif (n == 3): + if g1 is None or mu2 is None or mu is None: + val = moment_func(3, *args) + else: + mu3 = g1 * np.power(mu2, 1.5) # 3rd central moment + val = mu3+3*mu*mu2+mu*mu*mu # 3rd non-central moment + elif (n == 4): + if g1 is None or g2 is None or mu2 is None or mu is None: + val = moment_func(4, *args) + else: + mu4 = (g2+3.0)*(mu2**2.0) # 4th central moment + mu3 = g1*np.power(mu2, 1.5) # 3rd central moment + val = mu4+4*mu*mu3+6*mu*mu*mu2+mu*mu*mu*mu + else: + val = moment_func(n, *args) + + return val + + +def _skew(data): + """ + skew is third central moment / variance**(1.5) + """ + data = np.ravel(data) + mu = data.mean() + m2 = ((data - mu)**2).mean() + m3 = ((data - mu)**3).mean() + return m3 / np.power(m2, 1.5) + + +def _kurtosis(data): + """kurtosis is fourth central moment / variance**2 - 3.""" + data = np.ravel(data) + mu = data.mean() + m2 = ((data - mu)**2).mean() + m4 = ((data - mu)**4).mean() + return m4 / m2**2 - 3 + + +def _fit_determine_optimizer(optimizer): + if not callable(optimizer) and isinstance(optimizer, str): + if not optimizer.startswith('fmin_'): + optimizer = "fmin_"+optimizer + if optimizer == 'fmin_': + optimizer = 'fmin' + try: + optimizer = getattr(optimize, optimizer) + except AttributeError as e: + raise ValueError("%s is not a valid optimizer" % optimizer) from e + return optimizer + + +def _sum_finite(x): + """ + For a 1D array x, return a tuple containing the sum of the + finite values of x and the number of nonfinite values. + + This is a utility function used when evaluating the negative + loglikelihood for a distribution and an array of samples. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats._distn_infrastructure import _sum_finite + >>> tot, nbad = _sum_finite(np.array([-2, -np.inf, 5, 1])) + >>> tot + 4.0 + >>> nbad + 1 + """ + finite_x = np.isfinite(x) + bad_count = finite_x.size - np.count_nonzero(finite_x) + return np.sum(x[finite_x]), bad_count + + +# Frozen RV class +class rv_frozen: + + def __init__(self, dist, *args, **kwds): + self.args = args + self.kwds = kwds + + # create a new instance + self.dist = dist.__class__(**dist._updated_ctor_param()) + + shapes, _, _ = self.dist._parse_args(*args, **kwds) + self.a, self.b = self.dist._get_support(*shapes) + + @property + def random_state(self): + return self.dist._random_state + + @random_state.setter + def random_state(self, seed): + self.dist._random_state = check_random_state(seed) + + def cdf(self, x): + return self.dist.cdf(x, *self.args, **self.kwds) + + def logcdf(self, x): + return self.dist.logcdf(x, *self.args, **self.kwds) + + def ppf(self, q): + return self.dist.ppf(q, *self.args, **self.kwds) + + def isf(self, q): + return self.dist.isf(q, *self.args, **self.kwds) + + def rvs(self, size=None, random_state=None): + kwds = self.kwds.copy() + kwds.update({'size': size, 'random_state': random_state}) + return self.dist.rvs(*self.args, **kwds) + + def sf(self, x): + return self.dist.sf(x, *self.args, **self.kwds) + + def logsf(self, x): + return self.dist.logsf(x, *self.args, **self.kwds) + + def stats(self, moments='mv'): + kwds = self.kwds.copy() + kwds.update({'moments': moments}) + return self.dist.stats(*self.args, **kwds) + + def median(self): + return self.dist.median(*self.args, **self.kwds) + + def mean(self): + return self.dist.mean(*self.args, **self.kwds) + + def var(self): + return self.dist.var(*self.args, **self.kwds) + + def std(self): + return self.dist.std(*self.args, **self.kwds) + + def moment(self, order=None): + return self.dist.moment(order, *self.args, **self.kwds) + + def entropy(self): + return self.dist.entropy(*self.args, **self.kwds) + + def interval(self, confidence=None): + return self.dist.interval(confidence, *self.args, **self.kwds) + + def expect(self, func=None, lb=None, ub=None, conditional=False, **kwds): + # expect method only accepts shape parameters as positional args + # hence convert self.args, self.kwds, also loc/scale + # See the .expect method docstrings for the meaning of + # other parameters. + a, loc, scale = self.dist._parse_args(*self.args, **self.kwds) + if isinstance(self.dist, rv_discrete): + return self.dist.expect(func, a, loc, lb, ub, conditional, **kwds) + else: + return self.dist.expect(func, a, loc, scale, lb, ub, + conditional, **kwds) + + def support(self): + return self.dist.support(*self.args, **self.kwds) + + +class rv_discrete_frozen(rv_frozen): + + def pmf(self, k): + return self.dist.pmf(k, *self.args, **self.kwds) + + def logpmf(self, k): # No error + return self.dist.logpmf(k, *self.args, **self.kwds) + + +class rv_continuous_frozen(rv_frozen): + + def pdf(self, x): + return self.dist.pdf(x, *self.args, **self.kwds) + + def logpdf(self, x): + return self.dist.logpdf(x, *self.args, **self.kwds) + + +def argsreduce(cond, *args): + """Clean arguments to: + + 1. Ensure all arguments are iterable (arrays of dimension at least one + 2. If cond != True and size > 1, ravel(args[i]) where ravel(condition) is + True, in 1D. + + Return list of processed arguments. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats._distn_infrastructure import argsreduce + >>> rng = np.random.default_rng() + >>> A = rng.random((4, 5)) + >>> B = 2 + >>> C = rng.random((1, 5)) + >>> cond = np.ones(A.shape) + >>> [A1, B1, C1] = argsreduce(cond, A, B, C) + >>> A1.shape + (4, 5) + >>> B1.shape + (1,) + >>> C1.shape + (1, 5) + >>> cond[2,:] = 0 + >>> [A1, B1, C1] = argsreduce(cond, A, B, C) + >>> A1.shape + (15,) + >>> B1.shape + (1,) + >>> C1.shape + (15,) + + """ + # some distributions assume arguments are iterable. + newargs = np.atleast_1d(*args) + + # np.atleast_1d returns an array if only one argument, or a list of arrays + # if more than one argument. + if not isinstance(newargs, (list, tuple)): + newargs = (newargs,) + + if np.all(cond): + # broadcast arrays with cond + *newargs, cond = np.broadcast_arrays(*newargs, cond) + return [arg.ravel() for arg in newargs] + + s = cond.shape + # np.extract returns flattened arrays, which are not broadcastable together + # unless they are either the same size or size == 1. + return [(arg if np.size(arg) == 1 + else np.extract(cond, np.broadcast_to(arg, s))) + for arg in newargs] + + +parse_arg_template = """ +def _parse_args(self, %(shape_arg_str)s %(locscale_in)s): + return (%(shape_arg_str)s), %(locscale_out)s + +def _parse_args_rvs(self, %(shape_arg_str)s %(locscale_in)s, size=None): + return self._argcheck_rvs(%(shape_arg_str)s %(locscale_out)s, size=size) + +def _parse_args_stats(self, %(shape_arg_str)s %(locscale_in)s, moments='mv'): + return (%(shape_arg_str)s), %(locscale_out)s, moments +""" + + +class rv_generic: + """Class which encapsulates common functionality between rv_discrete + and rv_continuous. + + """ + + def __init__(self, seed=None): + super().__init__() + + # figure out if _stats signature has 'moments' keyword + sig = _getfullargspec(self._stats) + self._stats_has_moments = ((sig.varkw is not None) or + ('moments' in sig.args) or + ('moments' in sig.kwonlyargs)) + self._random_state = check_random_state(seed) + + @property + def random_state(self): + """Get or set the generator object for generating random variates. + + If `random_state` is None (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, that instance is used. + + """ + return self._random_state + + @random_state.setter + def random_state(self, seed): + self._random_state = check_random_state(seed) + + def __setstate__(self, state): + try: + self.__dict__.update(state) + # attaches the dynamically created methods on each instance. + # if a subclass overrides rv_generic.__setstate__, or implements + # it's own _attach_methods, then it must make sure that + # _attach_argparser_methods is called. + self._attach_methods() + except ValueError: + # reconstitute an old pickle scipy<1.6, that contains + # (_ctor_param, random_state) as state + self._ctor_param = state[0] + self._random_state = state[1] + self.__init__() + + def _attach_methods(self): + """Attaches dynamically created methods to the rv_* instance. + + This method must be overridden by subclasses, and must itself call + _attach_argparser_methods. This method is called in __init__ in + subclasses, and in __setstate__ + """ + raise NotImplementedError + + def _attach_argparser_methods(self): + """ + Generates the argument-parsing functions dynamically and attaches + them to the instance. + + Should be called from `_attach_methods`, typically in __init__ and + during unpickling (__setstate__) + """ + ns = {} + exec(self._parse_arg_template, ns) + # NB: attach to the instance, not class + for name in ['_parse_args', '_parse_args_stats', '_parse_args_rvs']: + setattr(self, name, types.MethodType(ns[name], self)) + + def _construct_argparser( + self, meths_to_inspect, locscale_in, locscale_out): + """Construct the parser string for the shape arguments. + + This method should be called in __init__ of a class for each + distribution. It creates the `_parse_arg_template` attribute that is + then used by `_attach_argparser_methods` to dynamically create and + attach the `_parse_args`, `_parse_args_stats`, `_parse_args_rvs` + methods to the instance. + + If self.shapes is a non-empty string, interprets it as a + comma-separated list of shape parameters. + + Otherwise inspects the call signatures of `meths_to_inspect` + and constructs the argument-parsing functions from these. + In this case also sets `shapes` and `numargs`. + """ + + if self.shapes: + # sanitize the user-supplied shapes + if not isinstance(self.shapes, str): + raise TypeError('shapes must be a string.') + + shapes = self.shapes.replace(',', ' ').split() + + for field in shapes: + if keyword.iskeyword(field): + raise SyntaxError('keywords cannot be used as shapes.') + if not re.match('^[_a-zA-Z][_a-zA-Z0-9]*$', field): + raise SyntaxError( + 'shapes must be valid python identifiers') + else: + # find out the call signatures (_pdf, _cdf etc), deduce shape + # arguments. Generic methods only have 'self, x', any further args + # are shapes. + shapes_list = [] + for meth in meths_to_inspect: + shapes_args = _getfullargspec(meth) # NB does not contain self + args = shapes_args.args[1:] # peel off 'x', too + + if args: + shapes_list.append(args) + + # *args or **kwargs are not allowed w/automatic shapes + if shapes_args.varargs is not None: + raise TypeError( + '*args are not allowed w/out explicit shapes') + if shapes_args.varkw is not None: + raise TypeError( + '**kwds are not allowed w/out explicit shapes') + if shapes_args.kwonlyargs: + raise TypeError( + 'kwonly args are not allowed w/out explicit shapes') + if shapes_args.defaults is not None: + raise TypeError('defaults are not allowed for shapes') + + if shapes_list: + shapes = shapes_list[0] + + # make sure the signatures are consistent + for item in shapes_list: + if item != shapes: + raise TypeError('Shape arguments are inconsistent.') + else: + shapes = [] + + # have the arguments, construct the method from template + shapes_str = ', '.join(shapes) + ', ' if shapes else '' # NB: not None + dct = dict(shape_arg_str=shapes_str, + locscale_in=locscale_in, + locscale_out=locscale_out, + ) + + # this string is used by _attach_argparser_methods + self._parse_arg_template = parse_arg_template % dct + + self.shapes = ', '.join(shapes) if shapes else None + if not hasattr(self, 'numargs'): + # allows more general subclassing with *args + self.numargs = len(shapes) + + def _construct_doc(self, docdict, shapes_vals=None): + """Construct the instance docstring with string substitutions.""" + tempdict = docdict.copy() + tempdict['name'] = self.name or 'distname' + tempdict['shapes'] = self.shapes or '' + + if shapes_vals is None: + shapes_vals = () + vals = ', '.join('%.3g' % val for val in shapes_vals) + tempdict['vals'] = vals + + tempdict['shapes_'] = self.shapes or '' + if self.shapes and self.numargs == 1: + tempdict['shapes_'] += ',' + + if self.shapes: + tempdict['set_vals_stmt'] = f'>>> {self.shapes} = {vals}' + else: + tempdict['set_vals_stmt'] = '' + + if self.shapes is None: + # remove shapes from call parameters if there are none + for item in ['default', 'before_notes']: + tempdict[item] = tempdict[item].replace( + "\n%(shapes)s : array_like\n shape parameters", "") + for i in range(2): + if self.shapes is None: + # necessary because we use %(shapes)s in two forms (w w/o ", ") + self.__doc__ = self.__doc__.replace("%(shapes)s, ", "") + try: + self.__doc__ = doccer.docformat(self.__doc__, tempdict) + except TypeError as e: + raise Exception("Unable to construct docstring for " + f"distribution \"{self.name}\": {repr(e)}") from e + + # correct for empty shapes + self.__doc__ = self.__doc__.replace('(, ', '(').replace(', )', ')') + + def _construct_default_doc(self, longname=None, + docdict=None, discrete='continuous'): + """Construct instance docstring from the default template.""" + if longname is None: + longname = 'A' + self.__doc__ = ''.join([f'{longname} {discrete} random variable.', + '\n\n%(before_notes)s\n', docheaders['notes'], + '\n%(example)s']) + self._construct_doc(docdict) + + def freeze(self, *args, **kwds): + """Freeze the distribution for the given arguments. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution. Should include all + the non-optional arguments, may include ``loc`` and ``scale``. + + Returns + ------- + rv_frozen : rv_frozen instance + The frozen distribution. + + """ + if isinstance(self, rv_continuous): + return rv_continuous_frozen(self, *args, **kwds) + else: + return rv_discrete_frozen(self, *args, **kwds) + + def __call__(self, *args, **kwds): + return self.freeze(*args, **kwds) + __call__.__doc__ = freeze.__doc__ + + # The actual calculation functions (no basic checking need be done) + # If these are defined, the others won't be looked at. + # Otherwise, the other set can be defined. + def _stats(self, *args, **kwds): + return None, None, None, None + + # Noncentral moments (also known as the moment about the origin). + # Expressed in LaTeX, munp would be $\mu'_{n}$, i.e. "mu-sub-n-prime". + # The primed mu is a widely used notation for the noncentral moment. + def _munp(self, n, *args): + # Silence floating point warnings from integration. + with np.errstate(all='ignore'): + vals = self.generic_moment(n, *args) + return vals + + def _argcheck_rvs(self, *args, **kwargs): + # Handle broadcasting and size validation of the rvs method. + # Subclasses should not have to override this method. + # The rule is that if `size` is not None, then `size` gives the + # shape of the result (integer values of `size` are treated as + # tuples with length 1; i.e. `size=3` is the same as `size=(3,)`.) + # + # `args` is expected to contain the shape parameters (if any), the + # location and the scale in a flat tuple (e.g. if there are two + # shape parameters `a` and `b`, `args` will be `(a, b, loc, scale)`). + # The only keyword argument expected is 'size'. + size = kwargs.get('size', None) + all_bcast = np.broadcast_arrays(*args) + + def squeeze_left(a): + while a.ndim > 0 and a.shape[0] == 1: + a = a[0] + return a + + # Eliminate trivial leading dimensions. In the convention + # used by numpy's random variate generators, trivial leading + # dimensions are effectively ignored. In other words, when `size` + # is given, trivial leading dimensions of the broadcast parameters + # in excess of the number of dimensions in size are ignored, e.g. + # >>> np.random.normal([[1, 3, 5]], [[[[0.01]]]], size=3) + # array([ 1.00104267, 3.00422496, 4.99799278]) + # If `size` is not given, the exact broadcast shape is preserved: + # >>> np.random.normal([[1, 3, 5]], [[[[0.01]]]]) + # array([[[[ 1.00862899, 3.00061431, 4.99867122]]]]) + # + all_bcast = [squeeze_left(a) for a in all_bcast] + bcast_shape = all_bcast[0].shape + bcast_ndim = all_bcast[0].ndim + + if size is None: + size_ = bcast_shape + else: + size_ = tuple(np.atleast_1d(size)) + + # Check compatibility of size_ with the broadcast shape of all + # the parameters. This check is intended to be consistent with + # how the numpy random variate generators (e.g. np.random.normal, + # np.random.beta) handle their arguments. The rule is that, if size + # is given, it determines the shape of the output. Broadcasting + # can't change the output size. + + # This is the standard broadcasting convention of extending the + # shape with fewer dimensions with enough dimensions of length 1 + # so that the two shapes have the same number of dimensions. + ndiff = bcast_ndim - len(size_) + if ndiff < 0: + bcast_shape = (1,)*(-ndiff) + bcast_shape + elif ndiff > 0: + size_ = (1,)*ndiff + size_ + + # This compatibility test is not standard. In "regular" broadcasting, + # two shapes are compatible if for each dimension, the lengths are the + # same or one of the lengths is 1. Here, the length of a dimension in + # size_ must not be less than the corresponding length in bcast_shape. + ok = all([bcdim == 1 or bcdim == szdim + for (bcdim, szdim) in zip(bcast_shape, size_)]) + if not ok: + raise ValueError("size does not match the broadcast shape of " + f"the parameters. {size}, {size_}, {bcast_shape}") + + param_bcast = all_bcast[:-2] + loc_bcast = all_bcast[-2] + scale_bcast = all_bcast[-1] + + return param_bcast, loc_bcast, scale_bcast, size_ + + # These are the methods you must define (standard form functions) + # NB: generic _pdf, _logpdf, _cdf are different for + # rv_continuous and rv_discrete hence are defined in there + def _argcheck(self, *args): + """Default check for correct values on args and keywords. + + Returns condition array of 1's where arguments are correct and + 0's where they are not. + + """ + cond = 1 + for arg in args: + cond = logical_and(cond, (asarray(arg) > 0)) + return cond + + def _get_support(self, *args, **kwargs): + """Return the support of the (unscaled, unshifted) distribution. + + *Must* be overridden by distributions which have support dependent + upon the shape parameters of the distribution. Any such override + *must not* set or change any of the class members, as these members + are shared amongst all instances of the distribution. + + Parameters + ---------- + arg1, arg2, ... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + + Returns + ------- + a, b : numeric (float, or int or +/-np.inf) + end-points of the distribution's support for the specified + shape parameters. + """ + return self.a, self.b + + def _support_mask(self, x, *args): + a, b = self._get_support(*args) + with np.errstate(invalid='ignore'): + return (a <= x) & (x <= b) + + def _open_support_mask(self, x, *args): + a, b = self._get_support(*args) + with np.errstate(invalid='ignore'): + return (a < x) & (x < b) + + def _rvs(self, *args, size=None, random_state=None): + # This method must handle size being a tuple, and it must + # properly broadcast *args and size. size might be + # an empty tuple, which means a scalar random variate is to be + # generated. + + # Use basic inverse cdf algorithm for RV generation as default. + U = random_state.uniform(size=size) + Y = self._ppf(U, *args) + return Y + + def _logcdf(self, x, *args): + with np.errstate(divide='ignore'): + return log(self._cdf(x, *args)) + + def _sf(self, x, *args): + return 1.0-self._cdf(x, *args) + + def _logsf(self, x, *args): + with np.errstate(divide='ignore'): + return log(self._sf(x, *args)) + + def _ppf(self, q, *args): + return self._ppfvec(q, *args) + + def _isf(self, q, *args): + return self._ppf(1.0-q, *args) # use correct _ppf for subclasses + + # These are actually called, and should not be overwritten if you + # want to keep error checking. + def rvs(self, *args, **kwds): + """Random variates of given type. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + scale : array_like, optional + Scale parameter (default=1). + size : int or tuple of ints, optional + Defining number of random variates (default is 1). + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `random_state` is None (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is + used, seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, that instance is used. + + Returns + ------- + rvs : ndarray or scalar + Random variates of given `size`. + + """ + discrete = kwds.pop('discrete', None) + rndm = kwds.pop('random_state', None) + args, loc, scale, size = self._parse_args_rvs(*args, **kwds) + cond = logical_and(self._argcheck(*args), (scale >= 0)) + if not np.all(cond): + message = ("Domain error in arguments. The `scale` parameter must " + "be positive for all distributions, and many " + "distributions have restrictions on shape parameters. " + f"Please see the `scipy.stats.{self.name}` " + "documentation for details.") + raise ValueError(message) + + if np.all(scale == 0): + return loc*ones(size, 'd') + + # extra gymnastics needed for a custom random_state + if rndm is not None: + random_state_saved = self._random_state + random_state = check_random_state(rndm) + else: + random_state = self._random_state + + vals = self._rvs(*args, size=size, random_state=random_state) + + vals = vals * scale + loc + + # do not forget to restore the _random_state + if rndm is not None: + self._random_state = random_state_saved + + # Cast to int if discrete + if discrete and not isinstance(self, rv_sample): + if size == (): + vals = int(vals) + else: + vals = vals.astype(np.int64) + + return vals + + def stats(self, *args, **kwds): + """Some statistics of the given RV. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional (continuous RVs only) + scale parameter (default=1) + moments : str, optional + composed of letters ['mvsk'] defining which moments to compute: + 'm' = mean, + 'v' = variance, + 's' = (Fisher's) skew, + 'k' = (Fisher's) kurtosis. + (default is 'mv') + + Returns + ------- + stats : sequence + of requested moments. + + """ + args, loc, scale, moments = self._parse_args_stats(*args, **kwds) + # scale = 1 by construction for discrete RVs + loc, scale = map(asarray, (loc, scale)) + args = tuple(map(asarray, args)) + cond = self._argcheck(*args) & (scale > 0) & (loc == loc) + output = [] + default = np.full(shape(cond), fill_value=self.badvalue) + + # Use only entries that are valid in calculation + if np.any(cond): + goodargs = argsreduce(cond, *(args+(scale, loc))) + scale, loc, goodargs = goodargs[-2], goodargs[-1], goodargs[:-2] + + if self._stats_has_moments: + mu, mu2, g1, g2 = self._stats(*goodargs, + **{'moments': moments}) + else: + mu, mu2, g1, g2 = self._stats(*goodargs) + + if 'm' in moments: + if mu is None: + mu = self._munp(1, *goodargs) + out0 = default.copy() + place(out0, cond, mu * scale + loc) + output.append(out0) + + if 'v' in moments: + if mu2 is None: + mu2p = self._munp(2, *goodargs) + if mu is None: + mu = self._munp(1, *goodargs) + # if mean is inf then var is also inf + with np.errstate(invalid='ignore'): + mu2 = np.where(~np.isinf(mu), mu2p - mu**2, np.inf) + out0 = default.copy() + place(out0, cond, mu2 * scale * scale) + output.append(out0) + + if 's' in moments: + if g1 is None: + mu3p = self._munp(3, *goodargs) + if mu is None: + mu = self._munp(1, *goodargs) + if mu2 is None: + mu2p = self._munp(2, *goodargs) + with np.errstate(invalid='ignore'): + mu2 = mu2p - mu * mu + with np.errstate(invalid='ignore'): + mu3 = (-mu*mu - 3*mu2)*mu + mu3p + g1 = mu3 / np.power(mu2, 1.5) + out0 = default.copy() + place(out0, cond, g1) + output.append(out0) + + if 'k' in moments: + if g2 is None: + mu4p = self._munp(4, *goodargs) + if mu is None: + mu = self._munp(1, *goodargs) + if mu2 is None: + mu2p = self._munp(2, *goodargs) + with np.errstate(invalid='ignore'): + mu2 = mu2p - mu * mu + if g1 is None: + mu3 = None + else: + # (mu2**1.5) breaks down for nan and inf + mu3 = g1 * np.power(mu2, 1.5) + if mu3 is None: + mu3p = self._munp(3, *goodargs) + with np.errstate(invalid='ignore'): + mu3 = (-mu * mu - 3 * mu2) * mu + mu3p + with np.errstate(invalid='ignore'): + mu4 = ((-mu**2 - 6*mu2) * mu - 4*mu3)*mu + mu4p + g2 = mu4 / mu2**2.0 - 3.0 + out0 = default.copy() + place(out0, cond, g2) + output.append(out0) + else: # no valid args + output = [default.copy() for _ in moments] + + output = [out[()] for out in output] + if len(output) == 1: + return output[0] + else: + return tuple(output) + + def entropy(self, *args, **kwds): + """Differential entropy of the RV. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + scale : array_like, optional (continuous distributions only). + Scale parameter (default=1). + + Notes + ----- + Entropy is defined base `e`: + + >>> import numpy as np + >>> from scipy.stats._distn_infrastructure import rv_discrete + >>> drv = rv_discrete(values=((0, 1), (0.5, 0.5))) + >>> np.allclose(drv.entropy(), np.log(2.0)) + True + + """ + args, loc, scale = self._parse_args(*args, **kwds) + # NB: for discrete distributions scale=1 by construction in _parse_args + loc, scale = map(asarray, (loc, scale)) + args = tuple(map(asarray, args)) + cond0 = self._argcheck(*args) & (scale > 0) & (loc == loc) + output = zeros(shape(cond0), 'd') + place(output, (1-cond0), self.badvalue) + goodargs = argsreduce(cond0, scale, *args) + goodscale = goodargs[0] + goodargs = goodargs[1:] + place(output, cond0, self.vecentropy(*goodargs) + log(goodscale)) + return output[()] + + def moment(self, order, *args, **kwds): + """non-central moment of distribution of specified order. + + Parameters + ---------- + order : int, order >= 1 + Order of moment. + arg1, arg2, arg3,... : float + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + """ + n = order + shapes, loc, scale = self._parse_args(*args, **kwds) + args = np.broadcast_arrays(*(*shapes, loc, scale)) + *shapes, loc, scale = args + + i0 = np.logical_and(self._argcheck(*shapes), scale > 0) + i1 = np.logical_and(i0, loc == 0) + i2 = np.logical_and(i0, loc != 0) + + args = argsreduce(i0, *shapes, loc, scale) + *shapes, loc, scale = args + + if (floor(n) != n): + raise ValueError("Moment must be an integer.") + if (n < 0): + raise ValueError("Moment must be positive.") + mu, mu2, g1, g2 = None, None, None, None + if (n > 0) and (n < 5): + if self._stats_has_moments: + mdict = {'moments': {1: 'm', 2: 'v', 3: 'vs', 4: 'mvsk'}[n]} + else: + mdict = {} + mu, mu2, g1, g2 = self._stats(*shapes, **mdict) + val = np.empty(loc.shape) # val needs to be indexed by loc + val[...] = _moment_from_stats(n, mu, mu2, g1, g2, self._munp, shapes) + + # Convert to transformed X = L + S*Y + # E[X^n] = E[(L+S*Y)^n] = L^n sum(comb(n, k)*(S/L)^k E[Y^k], k=0...n) + result = zeros(i0.shape) + place(result, ~i0, self.badvalue) + + if i1.any(): + res1 = scale[loc == 0]**n * val[loc == 0] + place(result, i1, res1) + + if i2.any(): + mom = [mu, mu2, g1, g2] + arrs = [i for i in mom if i is not None] + idx = [i for i in range(4) if mom[i] is not None] + if any(idx): + arrs = argsreduce(loc != 0, *arrs) + j = 0 + for i in idx: + mom[i] = arrs[j] + j += 1 + mu, mu2, g1, g2 = mom + args = argsreduce(loc != 0, *shapes, loc, scale, val) + *shapes, loc, scale, val = args + + res2 = zeros(loc.shape, dtype='d') + fac = scale / loc + for k in range(n): + valk = _moment_from_stats(k, mu, mu2, g1, g2, self._munp, + shapes) + res2 += comb(n, k, exact=True)*fac**k * valk + res2 += fac**n * val + res2 *= loc**n + place(result, i2, res2) + + return result[()] + + def median(self, *args, **kwds): + """Median of the distribution. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + Location parameter, Default is 0. + scale : array_like, optional + Scale parameter, Default is 1. + + Returns + ------- + median : float + The median of the distribution. + + See Also + -------- + rv_discrete.ppf + Inverse of the CDF + + """ + return self.ppf(0.5, *args, **kwds) + + def mean(self, *args, **kwds): + """Mean of the distribution. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + mean : float + the mean of the distribution + + """ + kwds['moments'] = 'm' + res = self.stats(*args, **kwds) + if isinstance(res, ndarray) and res.ndim == 0: + return res[()] + return res + + def var(self, *args, **kwds): + """Variance of the distribution. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + var : float + the variance of the distribution + + """ + kwds['moments'] = 'v' + res = self.stats(*args, **kwds) + if isinstance(res, ndarray) and res.ndim == 0: + return res[()] + return res + + def std(self, *args, **kwds): + """Standard deviation of the distribution. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + std : float + standard deviation of the distribution + + """ + kwds['moments'] = 'v' + res = sqrt(self.stats(*args, **kwds)) + return res + + def interval(self, confidence, *args, **kwds): + """Confidence interval with equal areas around the median. + + Parameters + ---------- + confidence : array_like of float + Probability that an rv will be drawn from the returned range. + Each value should be in the range [0, 1]. + arg1, arg2, ... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + location parameter, Default is 0. + scale : array_like, optional + scale parameter, Default is 1. + + Returns + ------- + a, b : ndarray of float + end-points of range that contain ``100 * alpha %`` of the rv's + possible values. + + Notes + ----- + This is implemented as ``ppf([p_tail, 1-p_tail])``, where + ``ppf`` is the inverse cumulative distribution function and + ``p_tail = (1-confidence)/2``. Suppose ``[c, d]`` is the support of a + discrete distribution; then ``ppf([0, 1]) == (c-1, d)``. Therefore, + when ``confidence=1`` and the distribution is discrete, the left end + of the interval will be beyond the support of the distribution. + For discrete distributions, the interval will limit the probability + in each tail to be less than or equal to ``p_tail`` (usually + strictly less). + + """ + alpha = confidence + + alpha = asarray(alpha) + if np.any((alpha > 1) | (alpha < 0)): + raise ValueError("alpha must be between 0 and 1 inclusive") + q1 = (1.0-alpha)/2 + q2 = (1.0+alpha)/2 + a = self.ppf(q1, *args, **kwds) + b = self.ppf(q2, *args, **kwds) + return a, b + + def support(self, *args, **kwargs): + """Support of the distribution. + + Parameters + ---------- + arg1, arg2, ... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + location parameter, Default is 0. + scale : array_like, optional + scale parameter, Default is 1. + + Returns + ------- + a, b : array_like + end-points of the distribution's support. + + """ + args, loc, scale = self._parse_args(*args, **kwargs) + arrs = np.broadcast_arrays(*args, loc, scale) + args, loc, scale = arrs[:-2], arrs[-2], arrs[-1] + cond = self._argcheck(*args) & (scale > 0) + _a, _b = self._get_support(*args) + if cond.all(): + return _a * scale + loc, _b * scale + loc + elif cond.ndim == 0: + return self.badvalue, self.badvalue + # promote bounds to at least float to fill in the badvalue + _a, _b = np.asarray(_a).astype('d'), np.asarray(_b).astype('d') + out_a, out_b = _a * scale + loc, _b * scale + loc + place(out_a, 1-cond, self.badvalue) + place(out_b, 1-cond, self.badvalue) + return out_a, out_b + + def nnlf(self, theta, x): + """Negative loglikelihood function. + Notes + ----- + This is ``-sum(log pdf(x, theta), axis=0)`` where `theta` are the + parameters (including loc and scale). + """ + loc, scale, args = self._unpack_loc_scale(theta) + if not self._argcheck(*args) or scale <= 0: + return inf + x = (asarray(x)-loc) / scale + n_log_scale = len(x) * log(scale) + if np.any(~self._support_mask(x, *args)): + return inf + return self._nnlf(x, *args) + n_log_scale + + def _nnlf(self, x, *args): + return -np.sum(self._logpxf(x, *args), axis=0) + + def _nlff_and_penalty(self, x, args, log_fitfun): + # negative log fit function + cond0 = ~self._support_mask(x, *args) + n_bad = np.count_nonzero(cond0, axis=0) + if n_bad > 0: + x = argsreduce(~cond0, x)[0] + logff = log_fitfun(x, *args) + finite_logff = np.isfinite(logff) + n_bad += np.sum(~finite_logff, axis=0) + if n_bad > 0: + penalty = n_bad * log(_XMAX) * 100 + return -np.sum(logff[finite_logff], axis=0) + penalty + return -np.sum(logff, axis=0) + + def _penalized_nnlf(self, theta, x): + """Penalized negative loglikelihood function. + i.e., - sum (log pdf(x, theta), axis=0) + penalty + where theta are the parameters (including loc and scale) + """ + loc, scale, args = self._unpack_loc_scale(theta) + if not self._argcheck(*args) or scale <= 0: + return inf + x = asarray((x-loc) / scale) + n_log_scale = len(x) * log(scale) + return self._nlff_and_penalty(x, args, self._logpxf) + n_log_scale + + def _penalized_nlpsf(self, theta, x): + """Penalized negative log product spacing function. + i.e., - sum (log (diff (cdf (x, theta))), axis=0) + penalty + where theta are the parameters (including loc and scale) + Follows reference [1] of scipy.stats.fit + """ + loc, scale, args = self._unpack_loc_scale(theta) + if not self._argcheck(*args) or scale <= 0: + return inf + x = (np.sort(x) - loc)/scale + + def log_psf(x, *args): + x, lj = np.unique(x, return_counts=True) # fast for sorted x + cdf_data = self._cdf(x, *args) if x.size else [] + if not (x.size and 1 - cdf_data[-1] <= 0): + cdf = np.concatenate(([0], cdf_data, [1])) + lj = np.concatenate((lj, [1])) + else: + cdf = np.concatenate(([0], cdf_data)) + # here we could use logcdf w/ logsumexp trick to take differences, + # but in the context of the method, it seems unlikely to matter + return lj * np.log(np.diff(cdf) / lj) + + return self._nlff_and_penalty(x, args, log_psf) + + +class _ShapeInfo: + def __init__(self, name, integrality=False, domain=(-np.inf, np.inf), + inclusive=(True, True)): + self.name = name + self.integrality = integrality + + domain = list(domain) + if np.isfinite(domain[0]) and not inclusive[0]: + domain[0] = np.nextafter(domain[0], np.inf) + if np.isfinite(domain[1]) and not inclusive[1]: + domain[1] = np.nextafter(domain[1], -np.inf) + self.domain = domain + + +def _get_fixed_fit_value(kwds, names): + """ + Given names such as `['f0', 'fa', 'fix_a']`, check that there is + at most one non-None value in `kwds` associaed with those names. + Return that value, or None if none of the names occur in `kwds`. + As a side effect, all occurrences of those names in `kwds` are + removed. + """ + vals = [(name, kwds.pop(name)) for name in names if name in kwds] + if len(vals) > 1: + repeated = [name for name, val in vals] + raise ValueError("fit method got multiple keyword arguments to " + "specify the same fixed parameter: " + + ', '.join(repeated)) + return vals[0][1] if vals else None + + +# continuous random variables: implement maybe later +# +# hf --- Hazard Function (PDF / SF) +# chf --- Cumulative hazard function (-log(SF)) +# psf --- Probability sparsity function (reciprocal of the pdf) in +# units of percent-point-function (as a function of q). +# Also, the derivative of the percent-point function. + + +class rv_continuous(rv_generic): + """A generic continuous random variable class meant for subclassing. + + `rv_continuous` is a base class to construct specific distribution classes + and instances for continuous random variables. It cannot be used + directly as a distribution. + + Parameters + ---------- + momtype : int, optional + The type of generic moment calculation to use: 0 for pdf, 1 (default) + for ppf. + a : float, optional + Lower bound of the support of the distribution, default is minus + infinity. + b : float, optional + Upper bound of the support of the distribution, default is plus + infinity. + xtol : float, optional + The tolerance for fixed point calculation for generic ppf. + badvalue : float, optional + The value in a result arrays that indicates a value that for which + some argument restriction is violated, default is np.nan. + name : str, optional + The name of the instance. This string is used to construct the default + example for distributions. + longname : str, optional + This string is used as part of the first line of the docstring returned + when a subclass has no docstring of its own. Note: `longname` exists + for backwards compatibility, do not use for new subclasses. + shapes : str, optional + The shape of the distribution. For example ``"m, n"`` for a + distribution that takes two integers as the two shape arguments for all + its methods. If not provided, shape parameters will be inferred from + the signature of the private methods, ``_pdf`` and ``_cdf`` of the + instance. + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Methods + ------- + rvs + pdf + logpdf + cdf + logcdf + sf + logsf + ppf + isf + moment + stats + entropy + expect + median + mean + std + var + interval + __call__ + fit + fit_loc_scale + nnlf + support + + Notes + ----- + Public methods of an instance of a distribution class (e.g., ``pdf``, + ``cdf``) check their arguments and pass valid arguments to private, + computational methods (``_pdf``, ``_cdf``). For ``pdf(x)``, ``x`` is valid + if it is within the support of the distribution. + Whether a shape parameter is valid is decided by an ``_argcheck`` method + (which defaults to checking that its arguments are strictly positive.) + + **Subclassing** + + New random variables can be defined by subclassing the `rv_continuous` class + and re-defining at least the ``_pdf`` or the ``_cdf`` method (normalized + to location 0 and scale 1). + + If positive argument checking is not correct for your RV + then you will also need to re-define the ``_argcheck`` method. + + For most of the scipy.stats distributions, the support interval doesn't + depend on the shape parameters. ``x`` being in the support interval is + equivalent to ``self.a <= x <= self.b``. If either of the endpoints of + the support do depend on the shape parameters, then + i) the distribution must implement the ``_get_support`` method; and + ii) those dependent endpoints must be omitted from the distribution's + call to the ``rv_continuous`` initializer. + + Correct, but potentially slow defaults exist for the remaining + methods but for speed and/or accuracy you can over-ride:: + + _logpdf, _cdf, _logcdf, _ppf, _rvs, _isf, _sf, _logsf + + The default method ``_rvs`` relies on the inverse of the cdf, ``_ppf``, + applied to a uniform random variate. In order to generate random variates + efficiently, either the default ``_ppf`` needs to be overwritten (e.g. + if the inverse cdf can expressed in an explicit form) or a sampling + method needs to be implemented in a custom ``_rvs`` method. + + If possible, you should override ``_isf``, ``_sf`` or ``_logsf``. + The main reason would be to improve numerical accuracy: for example, + the survival function ``_sf`` is computed as ``1 - _cdf`` which can + result in loss of precision if ``_cdf(x)`` is close to one. + + **Methods that can be overwritten by subclasses** + :: + + _rvs + _pdf + _cdf + _sf + _ppf + _isf + _stats + _munp + _entropy + _argcheck + _get_support + + There are additional (internal and private) generic methods that can + be useful for cross-checking and for debugging, but might work in all + cases when directly called. + + A note on ``shapes``: subclasses need not specify them explicitly. In this + case, `shapes` will be automatically deduced from the signatures of the + overridden methods (`pdf`, `cdf` etc). + If, for some reason, you prefer to avoid relying on introspection, you can + specify ``shapes`` explicitly as an argument to the instance constructor. + + + **Frozen Distributions** + + Normally, you must provide shape parameters (and, optionally, location and + scale parameters to each call of a method of a distribution. + + Alternatively, the object may be called (as a function) to fix the shape, + location, and scale parameters returning a "frozen" continuous RV object: + + rv = generic(, loc=0, scale=1) + `rv_frozen` object with the same methods but holding the given shape, + location, and scale fixed + + **Statistics** + + Statistics are computed using numerical integration by default. + For speed you can redefine this using ``_stats``: + + - take shape parameters and return mu, mu2, g1, g2 + - If you can't compute one of these, return it as None + - Can also be defined with a keyword argument ``moments``, which is a + string composed of "m", "v", "s", and/or "k". + Only the components appearing in string should be computed and + returned in the order "m", "v", "s", or "k" with missing values + returned as None. + + Alternatively, you can override ``_munp``, which takes ``n`` and shape + parameters and returns the n-th non-central moment of the distribution. + + **Deepcopying / Pickling** + + If a distribution or frozen distribution is deepcopied (pickled/unpickled, + etc.), any underlying random number generator is deepcopied with it. An + implication is that if a distribution relies on the singleton RandomState + before copying, it will rely on a copy of that random state after copying, + and ``np.random.seed`` will no longer control the state. + + Examples + -------- + To create a new Gaussian distribution, we would do the following: + + >>> from scipy.stats import rv_continuous + >>> class gaussian_gen(rv_continuous): + ... "Gaussian distribution" + ... def _pdf(self, x): + ... return np.exp(-x**2 / 2.) / np.sqrt(2.0 * np.pi) + >>> gaussian = gaussian_gen(name='gaussian') + + ``scipy.stats`` distributions are *instances*, so here we subclass + `rv_continuous` and create an instance. With this, we now have + a fully functional distribution with all relevant methods automagically + generated by the framework. + + Note that above we defined a standard normal distribution, with zero mean + and unit variance. Shifting and scaling of the distribution can be done + by using ``loc`` and ``scale`` parameters: ``gaussian.pdf(x, loc, scale)`` + essentially computes ``y = (x - loc) / scale`` and + ``gaussian._pdf(y) / scale``. + + """ + + def __init__(self, momtype=1, a=None, b=None, xtol=1e-14, + badvalue=None, name=None, longname=None, + shapes=None, seed=None): + + super().__init__(seed) + + # save the ctor parameters, cf generic freeze + self._ctor_param = dict( + momtype=momtype, a=a, b=b, xtol=xtol, + badvalue=badvalue, name=name, longname=longname, + shapes=shapes, seed=seed) + + if badvalue is None: + badvalue = nan + if name is None: + name = 'Distribution' + self.badvalue = badvalue + self.name = name + self.a = a + self.b = b + if a is None: + self.a = -inf + if b is None: + self.b = inf + self.xtol = xtol + self.moment_type = momtype + self.shapes = shapes + + self._construct_argparser(meths_to_inspect=[self._pdf, self._cdf], + locscale_in='loc=0, scale=1', + locscale_out='loc, scale') + self._attach_methods() + + if longname is None: + if name[0] in ['aeiouAEIOU']: + hstr = "An " + else: + hstr = "A " + longname = hstr + name + + if sys.flags.optimize < 2: + # Skip adding docstrings if interpreter is run with -OO + if self.__doc__ is None: + self._construct_default_doc(longname=longname, + docdict=docdict, + discrete='continuous') + else: + dct = dict(distcont) + self._construct_doc(docdict, dct.get(self.name)) + + def __getstate__(self): + dct = self.__dict__.copy() + + # these methods will be remade in __setstate__ + # _random_state attribute is taken care of by rv_generic + attrs = ["_parse_args", "_parse_args_stats", "_parse_args_rvs", + "_cdfvec", "_ppfvec", "vecentropy", "generic_moment"] + [dct.pop(attr, None) for attr in attrs] + return dct + + def _attach_methods(self): + """ + Attaches dynamically created methods to the rv_continuous instance. + """ + # _attach_methods is responsible for calling _attach_argparser_methods + self._attach_argparser_methods() + + # nin correction + self._ppfvec = vectorize(self._ppf_single, otypes='d') + self._ppfvec.nin = self.numargs + 1 + self.vecentropy = vectorize(self._entropy, otypes='d') + self._cdfvec = vectorize(self._cdf_single, otypes='d') + self._cdfvec.nin = self.numargs + 1 + + if self.moment_type == 0: + self.generic_moment = vectorize(self._mom0_sc, otypes='d') + else: + self.generic_moment = vectorize(self._mom1_sc, otypes='d') + # Because of the *args argument of _mom0_sc, vectorize cannot count the + # number of arguments correctly. + self.generic_moment.nin = self.numargs + 1 + + def _updated_ctor_param(self): + """Return the current version of _ctor_param, possibly updated by user. + + Used by freezing. + Keep this in sync with the signature of __init__. + """ + dct = self._ctor_param.copy() + dct['a'] = self.a + dct['b'] = self.b + dct['xtol'] = self.xtol + dct['badvalue'] = self.badvalue + dct['name'] = self.name + dct['shapes'] = self.shapes + return dct + + def _ppf_to_solve(self, x, q, *args): + return self.cdf(*(x, )+args)-q + + def _ppf_single(self, q, *args): + factor = 10. + left, right = self._get_support(*args) + + if np.isinf(left): + left = min(-factor, right) + while self._ppf_to_solve(left, q, *args) > 0.: + left, right = left * factor, left + # left is now such that cdf(left) <= q + # if right has changed, then cdf(right) > q + + if np.isinf(right): + right = max(factor, left) + while self._ppf_to_solve(right, q, *args) < 0.: + left, right = right, right * factor + # right is now such that cdf(right) >= q + + return optimize.brentq(self._ppf_to_solve, + left, right, args=(q,)+args, xtol=self.xtol) + + # moment from definition + def _mom_integ0(self, x, m, *args): + return x**m * self.pdf(x, *args) + + def _mom0_sc(self, m, *args): + _a, _b = self._get_support(*args) + return integrate.quad(self._mom_integ0, _a, _b, + args=(m,)+args)[0] + + # moment calculated using ppf + def _mom_integ1(self, q, m, *args): + return (self.ppf(q, *args))**m + + def _mom1_sc(self, m, *args): + return integrate.quad(self._mom_integ1, 0, 1, args=(m,)+args)[0] + + def _pdf(self, x, *args): + return _derivative(self._cdf, x, dx=1e-5, args=args, order=5) + + # Could also define any of these + def _logpdf(self, x, *args): + p = self._pdf(x, *args) + with np.errstate(divide='ignore'): + return log(p) + + def _logpxf(self, x, *args): + # continuous distributions have PDF, discrete have PMF, but sometimes + # the distinction doesn't matter. This lets us use `_logpxf` for both + # discrete and continuous distributions. + return self._logpdf(x, *args) + + def _cdf_single(self, x, *args): + _a, _b = self._get_support(*args) + return integrate.quad(self._pdf, _a, x, args=args)[0] + + def _cdf(self, x, *args): + return self._cdfvec(x, *args) + + # generic _argcheck, _logcdf, _sf, _logsf, _ppf, _isf, _rvs are defined + # in rv_generic + + def pdf(self, x, *args, **kwds): + """Probability density function at x of the given RV. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + pdf : ndarray + Probability density function evaluated at x + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._support_mask(x, *args) & (scale > 0) + cond = cond0 & cond1 + output = zeros(shape(cond), dtyp) + putmask(output, (1-cond0)+np.isnan(x), self.badvalue) + if np.any(cond): + goodargs = argsreduce(cond, *((x,)+args+(scale,))) + scale, goodargs = goodargs[-1], goodargs[:-1] + place(output, cond, self._pdf(*goodargs) / scale) + if output.ndim == 0: + return output[()] + return output + + def logpdf(self, x, *args, **kwds): + """Log of the probability density function at x of the given RV. + + This uses a more numerically accurate calculation if available. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + logpdf : array_like + Log of the probability density function evaluated at x + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._support_mask(x, *args) & (scale > 0) + cond = cond0 & cond1 + output = empty(shape(cond), dtyp) + output.fill(-inf) + putmask(output, (1-cond0)+np.isnan(x), self.badvalue) + if np.any(cond): + goodargs = argsreduce(cond, *((x,)+args+(scale,))) + scale, goodargs = goodargs[-1], goodargs[:-1] + place(output, cond, self._logpdf(*goodargs) - log(scale)) + if output.ndim == 0: + return output[()] + return output + + def cdf(self, x, *args, **kwds): + """ + Cumulative distribution function of the given RV. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + cdf : ndarray + Cumulative distribution function evaluated at `x` + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._open_support_mask(x, *args) & (scale > 0) + cond2 = (x >= np.asarray(_b)) & cond0 + cond = cond0 & cond1 + output = zeros(shape(cond), dtyp) + place(output, (1-cond0)+np.isnan(x), self.badvalue) + place(output, cond2, 1.0) + if np.any(cond): # call only if at least 1 entry + goodargs = argsreduce(cond, *((x,)+args)) + place(output, cond, self._cdf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def logcdf(self, x, *args, **kwds): + """Log of the cumulative distribution function at x of the given RV. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + logcdf : array_like + Log of the cumulative distribution function evaluated at x + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._open_support_mask(x, *args) & (scale > 0) + cond2 = (x >= _b) & cond0 + cond = cond0 & cond1 + output = empty(shape(cond), dtyp) + output.fill(-inf) + place(output, (1-cond0)*(cond1 == cond1)+np.isnan(x), self.badvalue) + place(output, cond2, 0.0) + if np.any(cond): # call only if at least 1 entry + goodargs = argsreduce(cond, *((x,)+args)) + place(output, cond, self._logcdf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def sf(self, x, *args, **kwds): + """Survival function (1 - `cdf`) at x of the given RV. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + sf : array_like + Survival function evaluated at x + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._open_support_mask(x, *args) & (scale > 0) + cond2 = cond0 & (x <= _a) + cond = cond0 & cond1 + output = zeros(shape(cond), dtyp) + place(output, (1-cond0)+np.isnan(x), self.badvalue) + place(output, cond2, 1.0) + if np.any(cond): + goodargs = argsreduce(cond, *((x,)+args)) + place(output, cond, self._sf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def logsf(self, x, *args, **kwds): + """Log of the survival function of the given RV. + + Returns the log of the "survival function," defined as (1 - `cdf`), + evaluated at `x`. + + Parameters + ---------- + x : array_like + quantiles + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + logsf : ndarray + Log of the survival function evaluated at `x`. + + """ + args, loc, scale = self._parse_args(*args, **kwds) + x, loc, scale = map(asarray, (x, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + dtyp = np.promote_types(x.dtype, np.float64) + x = np.asarray((x - loc)/scale, dtype=dtyp) + cond0 = self._argcheck(*args) & (scale > 0) + cond1 = self._open_support_mask(x, *args) & (scale > 0) + cond2 = cond0 & (x <= _a) + cond = cond0 & cond1 + output = empty(shape(cond), dtyp) + output.fill(-inf) + place(output, (1-cond0)+np.isnan(x), self.badvalue) + place(output, cond2, 0.0) + if np.any(cond): + goodargs = argsreduce(cond, *((x,)+args)) + place(output, cond, self._logsf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def ppf(self, q, *args, **kwds): + """Percent point function (inverse of `cdf`) at q of the given RV. + + Parameters + ---------- + q : array_like + lower tail probability + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + x : array_like + quantile corresponding to the lower tail probability q. + + """ + args, loc, scale = self._parse_args(*args, **kwds) + q, loc, scale = map(asarray, (q, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + cond0 = self._argcheck(*args) & (scale > 0) & (loc == loc) + cond1 = (0 < q) & (q < 1) + cond2 = cond0 & (q == 0) + cond3 = cond0 & (q == 1) + cond = cond0 & cond1 + output = np.full(shape(cond), fill_value=self.badvalue) + + lower_bound = _a * scale + loc + upper_bound = _b * scale + loc + place(output, cond2, argsreduce(cond2, lower_bound)[0]) + place(output, cond3, argsreduce(cond3, upper_bound)[0]) + + if np.any(cond): # call only if at least 1 entry + goodargs = argsreduce(cond, *((q,)+args+(scale, loc))) + scale, loc, goodargs = goodargs[-2], goodargs[-1], goodargs[:-2] + place(output, cond, self._ppf(*goodargs) * scale + loc) + if output.ndim == 0: + return output[()] + return output + + def isf(self, q, *args, **kwds): + """Inverse survival function (inverse of `sf`) at q of the given RV. + + Parameters + ---------- + q : array_like + upper tail probability + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + location parameter (default=0) + scale : array_like, optional + scale parameter (default=1) + + Returns + ------- + x : ndarray or scalar + Quantile corresponding to the upper tail probability q. + + """ + args, loc, scale = self._parse_args(*args, **kwds) + q, loc, scale = map(asarray, (q, loc, scale)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + cond0 = self._argcheck(*args) & (scale > 0) & (loc == loc) + cond1 = (0 < q) & (q < 1) + cond2 = cond0 & (q == 1) + cond3 = cond0 & (q == 0) + cond = cond0 & cond1 + output = np.full(shape(cond), fill_value=self.badvalue) + + lower_bound = _a * scale + loc + upper_bound = _b * scale + loc + place(output, cond2, argsreduce(cond2, lower_bound)[0]) + place(output, cond3, argsreduce(cond3, upper_bound)[0]) + + if np.any(cond): + goodargs = argsreduce(cond, *((q,)+args+(scale, loc))) + scale, loc, goodargs = goodargs[-2], goodargs[-1], goodargs[:-2] + place(output, cond, self._isf(*goodargs) * scale + loc) + if output.ndim == 0: + return output[()] + return output + + def _unpack_loc_scale(self, theta): + try: + loc = theta[-2] + scale = theta[-1] + args = tuple(theta[:-2]) + except IndexError as e: + raise ValueError("Not enough input arguments.") from e + return loc, scale, args + + def _nnlf_and_penalty(self, x, args): + """ + Compute the penalized negative log-likelihood for the + "standardized" data (i.e. already shifted by loc and + scaled by scale) for the shape parameters in `args`. + + `x` can be a 1D numpy array or a CensoredData instance. + """ + if isinstance(x, CensoredData): + # Filter out the data that is not in the support. + xs = x._supported(*self._get_support(*args)) + n_bad = len(x) - len(xs) + i1, i2 = xs._interval.T + terms = [ + # logpdf of the noncensored data. + self._logpdf(xs._uncensored, *args), + # logcdf of the left-censored data. + self._logcdf(xs._left, *args), + # logsf of the right-censored data. + self._logsf(xs._right, *args), + # log of probability of the interval-censored data. + np.log(self._delta_cdf(i1, i2, *args)), + ] + else: + cond0 = ~self._support_mask(x, *args) + n_bad = np.count_nonzero(cond0) + if n_bad > 0: + x = argsreduce(~cond0, x)[0] + terms = [self._logpdf(x, *args)] + + totals, bad_counts = zip(*[_sum_finite(term) for term in terms]) + total = sum(totals) + n_bad += sum(bad_counts) + + return -total + n_bad * _LOGXMAX * 100 + + def _penalized_nnlf(self, theta, x): + """Penalized negative loglikelihood function. + + i.e., - sum (log pdf(x, theta), axis=0) + penalty + where theta are the parameters (including loc and scale) + """ + loc, scale, args = self._unpack_loc_scale(theta) + if not self._argcheck(*args) or scale <= 0: + return inf + if isinstance(x, CensoredData): + x = (x - loc) / scale + n_log_scale = (len(x) - x.num_censored()) * log(scale) + else: + x = (x - loc) / scale + n_log_scale = len(x) * log(scale) + + return self._nnlf_and_penalty(x, args) + n_log_scale + + def _fitstart(self, data, args=None): + """Starting point for fit (shape arguments + loc + scale).""" + if args is None: + args = (1.0,)*self.numargs + loc, scale = self._fit_loc_scale_support(data, *args) + return args + (loc, scale) + + def _reduce_func(self, args, kwds, data=None): + """ + Return the (possibly reduced) function to optimize in order to find MLE + estimates for the .fit method. + """ + # Convert fixed shape parameters to the standard numeric form: e.g. for + # stats.beta, shapes='a, b'. To fix `a`, the caller can give a value + # for `f0`, `fa` or 'fix_a'. The following converts the latter two + # into the first (numeric) form. + shapes = [] + if self.shapes: + shapes = self.shapes.replace(',', ' ').split() + for j, s in enumerate(shapes): + key = 'f' + str(j) + names = [key, 'f' + s, 'fix_' + s] + val = _get_fixed_fit_value(kwds, names) + if val is not None: + kwds[key] = val + + args = list(args) + Nargs = len(args) + fixedn = [] + names = ['f%d' % n for n in range(Nargs - 2)] + ['floc', 'fscale'] + x0 = [] + for n, key in enumerate(names): + if key in kwds: + fixedn.append(n) + args[n] = kwds.pop(key) + else: + x0.append(args[n]) + + methods = {"mle", "mm"} + method = kwds.pop('method', "mle").lower() + if method == "mm": + n_params = len(shapes) + 2 - len(fixedn) + exponents = (np.arange(1, n_params+1))[:, np.newaxis] + data_moments = np.sum(data[None, :]**exponents/len(data), axis=1) + + def objective(theta, x): + return self._moment_error(theta, x, data_moments) + + elif method == "mle": + objective = self._penalized_nnlf + else: + raise ValueError(f"Method '{method}' not available; " + f"must be one of {methods}") + + if len(fixedn) == 0: + func = objective + restore = None + else: + if len(fixedn) == Nargs: + raise ValueError( + "All parameters fixed. There is nothing to optimize.") + + def restore(args, theta): + # Replace with theta for all numbers not in fixedn + # This allows the non-fixed values to vary, but + # we still call self.nnlf with all parameters. + i = 0 + for n in range(Nargs): + if n not in fixedn: + args[n] = theta[i] + i += 1 + return args + + def func(theta, x): + newtheta = restore(args[:], theta) + return objective(newtheta, x) + + return x0, func, restore, args + + def _moment_error(self, theta, x, data_moments): + loc, scale, args = self._unpack_loc_scale(theta) + if not self._argcheck(*args) or scale <= 0: + return inf + + dist_moments = np.array([self.moment(i+1, *args, loc=loc, scale=scale) + for i in range(len(data_moments))]) + if np.any(np.isnan(dist_moments)): + raise ValueError("Method of moments encountered a non-finite " + "distribution moment and cannot continue. " + "Consider trying method='MLE'.") + + return (((data_moments - dist_moments) / + np.maximum(np.abs(data_moments), 1e-8))**2).sum() + + def fit(self, data, *args, **kwds): + r""" + Return estimates of shape (if applicable), location, and scale + parameters from data. The default estimation method is Maximum + Likelihood Estimation (MLE), but Method of Moments (MM) + is also available. + + Starting estimates for the fit are given by input arguments; + for any arguments not provided with starting estimates, + ``self._fitstart(data)`` is called to generate such. + + One can hold some parameters fixed to specific values by passing in + keyword arguments ``f0``, ``f1``, ..., ``fn`` (for shape parameters) + and ``floc`` and ``fscale`` (for location and scale parameters, + respectively). + + Parameters + ---------- + data : array_like or `CensoredData` instance + Data to use in estimating the distribution parameters. + arg1, arg2, arg3,... : floats, optional + Starting value(s) for any shape-characterizing arguments (those not + provided will be determined by a call to ``_fitstart(data)``). + No default value. + **kwds : floats, optional + - `loc`: initial guess of the distribution's location parameter. + - `scale`: initial guess of the distribution's scale parameter. + + Special keyword arguments are recognized as holding certain + parameters fixed: + + - f0...fn : hold respective shape parameters fixed. + Alternatively, shape parameters to fix can be specified by name. + For example, if ``self.shapes == "a, b"``, ``fa`` and ``fix_a`` + are equivalent to ``f0``, and ``fb`` and ``fix_b`` are + equivalent to ``f1``. + + - floc : hold location parameter fixed to specified value. + + - fscale : hold scale parameter fixed to specified value. + + - optimizer : The optimizer to use. The optimizer must take + ``func`` and starting position as the first two arguments, + plus ``args`` (for extra arguments to pass to the + function to be optimized) and ``disp``. + The ``fit`` method calls the optimizer with ``disp=0`` to suppress output. + The optimizer must return the estimated parameters. + + - method : The method to use. The default is "MLE" (Maximum + Likelihood Estimate); "MM" (Method of Moments) + is also available. + + Raises + ------ + TypeError, ValueError + If an input is invalid + `~scipy.stats.FitError` + If fitting fails or the fit produced would be invalid + + Returns + ------- + parameter_tuple : tuple of floats + Estimates for any shape parameters (if applicable), followed by + those for location and scale. For most random variables, shape + statistics will be returned, but there are exceptions (e.g. + ``norm``). + + Notes + ----- + With ``method="MLE"`` (default), the fit is computed by minimizing + the negative log-likelihood function. A large, finite penalty + (rather than infinite negative log-likelihood) is applied for + observations beyond the support of the distribution. + + With ``method="MM"``, the fit is computed by minimizing the L2 norm + of the relative errors between the first *k* raw (about zero) data + moments and the corresponding distribution moments, where *k* is the + number of non-fixed parameters. + More precisely, the objective function is:: + + (((data_moments - dist_moments) + / np.maximum(np.abs(data_moments), 1e-8))**2).sum() + + where the constant ``1e-8`` avoids division by zero in case of + vanishing data moments. Typically, this error norm can be reduced to + zero. + Note that the standard method of moments can produce parameters for + which some data are outside the support of the fitted distribution; + this implementation does nothing to prevent this. + + For either method, + the returned answer is not guaranteed to be globally optimal; it + may only be locally optimal, or the optimization may fail altogether. + If the data contain any of ``np.nan``, ``np.inf``, or ``-np.inf``, + the `fit` method will raise a ``RuntimeError``. + + When passing a ``CensoredData`` instance to ``data``, the log-likelihood + function is defined as: + + .. math:: + + l(\pmb{\theta}; k) & = \sum + \log(f(k_u; \pmb{\theta})) + + \sum + \log(F(k_l; \pmb{\theta})) \\ + & + \sum + \log(1 - F(k_r; \pmb{\theta})) \\ + & + \sum + \log(F(k_{\text{high}, i}; \pmb{\theta}) + - F(k_{\text{low}, i}; \pmb{\theta})) + + where :math:`f` and :math:`F` are the pdf and cdf, respectively, of the + function being fitted, :math:`\pmb{\theta}` is the parameter vector, + :math:`u` are the indices of uncensored observations, + :math:`l` are the indices of left-censored observations, + :math:`r` are the indices of right-censored observations, + subscripts "low"/"high" denote endpoints of interval-censored observations, and + :math:`i` are the indices of interval-censored observations. + + Examples + -------- + + Generate some data to fit: draw random variates from the `beta` + distribution + + >>> import numpy as np + >>> from scipy.stats import beta + >>> a, b = 1., 2. + >>> rng = np.random.default_rng(172786373191770012695001057628748821561) + >>> x = beta.rvs(a, b, size=1000, random_state=rng) + + Now we can fit all four parameters (``a``, ``b``, ``loc`` and + ``scale``): + + >>> a1, b1, loc1, scale1 = beta.fit(x) + >>> a1, b1, loc1, scale1 + (1.0198945204435628, 1.9484708982737828, 4.372241314917588e-05, 0.9979078845964814) + + The fit can be done also using a custom optimizer: + + >>> from scipy.optimize import minimize + >>> def custom_optimizer(func, x0, args=(), disp=0): + ... res = minimize(func, x0, args, method="slsqp", options={"disp": disp}) + ... if res.success: + ... return res.x + ... raise RuntimeError('optimization routine failed') + >>> a1, b1, loc1, scale1 = beta.fit(x, method="MLE", optimizer=custom_optimizer) + >>> a1, b1, loc1, scale1 + (1.0198821087258905, 1.948484145914738, 4.3705304486881485e-05, 0.9979104663953395) + + We can also use some prior knowledge about the dataset: let's keep + ``loc`` and ``scale`` fixed: + + >>> a1, b1, loc1, scale1 = beta.fit(x, floc=0, fscale=1) + >>> loc1, scale1 + (0, 1) + + We can also keep shape parameters fixed by using ``f``-keywords. To + keep the zero-th shape parameter ``a`` equal 1, use ``f0=1`` or, + equivalently, ``fa=1``: + + >>> a1, b1, loc1, scale1 = beta.fit(x, fa=1, floc=0, fscale=1) + >>> a1 + 1 + + Not all distributions return estimates for the shape parameters. + ``norm`` for example just returns estimates for location and scale: + + >>> from scipy.stats import norm + >>> x = norm.rvs(a, b, size=1000, random_state=123) + >>> loc1, scale1 = norm.fit(x) + >>> loc1, scale1 + (0.92087172783841631, 2.0015750750324668) + """ # noqa: E501 + method = kwds.get('method', "mle").lower() + + censored = isinstance(data, CensoredData) + if censored: + if method != 'mle': + raise ValueError('For censored data, the method must' + ' be "MLE".') + if data.num_censored() == 0: + # There are no censored values in data, so replace the + # CensoredData instance with a regular array. + data = data._uncensored + censored = False + + Narg = len(args) + if Narg > self.numargs: + raise TypeError("Too many input arguments.") + + # Check the finiteness of data only if data is not an instance of + # CensoredData. The arrays in a CensoredData instance have already + # been validated. + if not censored: + # Note: `ravel()` is called for backwards compatibility. + data = np.asarray(data).ravel() + if not np.isfinite(data).all(): + raise ValueError("The data contains non-finite values.") + + start = [None]*2 + if (Narg < self.numargs) or not ('loc' in kwds and + 'scale' in kwds): + # get distribution specific starting locations + start = self._fitstart(data) + args += start[Narg:-2] + loc = kwds.pop('loc', start[-2]) + scale = kwds.pop('scale', start[-1]) + args += (loc, scale) + x0, func, restore, args = self._reduce_func(args, kwds, data=data) + optimizer = kwds.pop('optimizer', optimize.fmin) + # convert string to function in scipy.optimize + optimizer = _fit_determine_optimizer(optimizer) + # by now kwds must be empty, since everybody took what they needed + if kwds: + raise TypeError("Unknown arguments: %s." % kwds) + + # In some cases, method of moments can be done with fsolve/root + # instead of an optimizer, but sometimes no solution exists, + # especially when the user fixes parameters. Minimizing the sum + # of squares of the error generalizes to these cases. + vals = optimizer(func, x0, args=(data,), disp=0) + obj = func(vals, data) + + if restore is not None: + vals = restore(args, vals) + vals = tuple(vals) + + loc, scale, shapes = self._unpack_loc_scale(vals) + if not (np.all(self._argcheck(*shapes)) and scale > 0): + raise FitError("Optimization converged to parameters that are " + "outside the range allowed by the distribution.") + + if method == 'mm': + if not np.isfinite(obj): + raise FitError("Optimization failed: either a data moment " + "or fitted distribution moment is " + "non-finite.") + + return vals + + def _fit_loc_scale_support(self, data, *args): + """Estimate loc and scale parameters from data accounting for support. + + Parameters + ---------- + data : array_like + Data to fit. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + + Returns + ------- + Lhat : float + Estimated location parameter for the data. + Shat : float + Estimated scale parameter for the data. + + """ + if isinstance(data, CensoredData): + # For this estimate, "uncensor" the data by taking the + # given endpoints as the data for the left- or right-censored + # data, and the mean for the interval-censored data. + data = data._uncensor() + else: + data = np.asarray(data) + + # Estimate location and scale according to the method of moments. + loc_hat, scale_hat = self.fit_loc_scale(data, *args) + + # Compute the support according to the shape parameters. + self._argcheck(*args) + _a, _b = self._get_support(*args) + a, b = _a, _b + support_width = b - a + + # If the support is empty then return the moment-based estimates. + if support_width <= 0: + return loc_hat, scale_hat + + # Compute the proposed support according to the loc and scale + # estimates. + a_hat = loc_hat + a * scale_hat + b_hat = loc_hat + b * scale_hat + + # Use the moment-based estimates if they are compatible with the data. + data_a = np.min(data) + data_b = np.max(data) + if a_hat < data_a and data_b < b_hat: + return loc_hat, scale_hat + + # Otherwise find other estimates that are compatible with the data. + data_width = data_b - data_a + rel_margin = 0.1 + margin = data_width * rel_margin + + # For a finite interval, both the location and scale + # should have interesting values. + if support_width < np.inf: + loc_hat = (data_a - a) - margin + scale_hat = (data_width + 2 * margin) / support_width + return loc_hat, scale_hat + + # For a one-sided interval, use only an interesting location parameter. + if a > -np.inf: + return (data_a - a) - margin, 1 + elif b < np.inf: + return (data_b - b) + margin, 1 + else: + raise RuntimeError + + def fit_loc_scale(self, data, *args): + """ + Estimate loc and scale parameters from data using 1st and 2nd moments. + + Parameters + ---------- + data : array_like + Data to fit. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + + Returns + ------- + Lhat : float + Estimated location parameter for the data. + Shat : float + Estimated scale parameter for the data. + + """ + mu, mu2 = self.stats(*args, **{'moments': 'mv'}) + tmp = asarray(data) + muhat = tmp.mean() + mu2hat = tmp.var() + Shat = sqrt(mu2hat / mu2) + with np.errstate(invalid='ignore'): + Lhat = muhat - Shat*mu + if not np.isfinite(Lhat): + Lhat = 0 + if not (np.isfinite(Shat) and (0 < Shat)): + Shat = 1 + return Lhat, Shat + + def _entropy(self, *args): + def integ(x): + val = self._pdf(x, *args) + return entr(val) + + # upper limit is often inf, so suppress warnings when integrating + _a, _b = self._get_support(*args) + with np.errstate(over='ignore'): + h = integrate.quad(integ, _a, _b)[0] + + if not np.isnan(h): + return h + else: + # try with different limits if integration problems + low, upp = self.ppf([1e-10, 1. - 1e-10], *args) + if np.isinf(_b): + upper = upp + else: + upper = _b + if np.isinf(_a): + lower = low + else: + lower = _a + return integrate.quad(integ, lower, upper)[0] + + def expect(self, func=None, args=(), loc=0, scale=1, lb=None, ub=None, + conditional=False, **kwds): + """Calculate expected value of a function with respect to the + distribution by numerical integration. + + The expected value of a function ``f(x)`` with respect to a + distribution ``dist`` is defined as:: + + ub + E[f(x)] = Integral(f(x) * dist.pdf(x)), + lb + + where ``ub`` and ``lb`` are arguments and ``x`` has the ``dist.pdf(x)`` + distribution. If the bounds ``lb`` and ``ub`` correspond to the + support of the distribution, e.g. ``[-inf, inf]`` in the default + case, then the integral is the unrestricted expectation of ``f(x)``. + Also, the function ``f(x)`` may be defined such that ``f(x)`` is ``0`` + outside a finite interval in which case the expectation is + calculated within the finite range ``[lb, ub]``. + + Parameters + ---------- + func : callable, optional + Function for which integral is calculated. Takes only one argument. + The default is the identity mapping f(x) = x. + args : tuple, optional + Shape parameters of the distribution. + loc : float, optional + Location parameter (default=0). + scale : float, optional + Scale parameter (default=1). + lb, ub : scalar, optional + Lower and upper bound for integration. Default is set to the + support of the distribution. + conditional : bool, optional + If True, the integral is corrected by the conditional probability + of the integration interval. The return value is the expectation + of the function, conditional on being in the given interval. + Default is False. + + Additional keyword arguments are passed to the integration routine. + + Returns + ------- + expect : float + The calculated expected value. + + Notes + ----- + The integration behavior of this function is inherited from + `scipy.integrate.quad`. Neither this function nor + `scipy.integrate.quad` can verify whether the integral exists or is + finite. For example ``cauchy(0).mean()`` returns ``np.nan`` and + ``cauchy(0).expect()`` returns ``0.0``. + + Likewise, the accuracy of results is not verified by the function. + `scipy.integrate.quad` is typically reliable for integrals that are + numerically favorable, but it is not guaranteed to converge + to a correct value for all possible intervals and integrands. This + function is provided for convenience; for critical applications, + check results against other integration methods. + + The function is not vectorized. + + Examples + -------- + + To understand the effect of the bounds of integration consider + + >>> from scipy.stats import expon + >>> expon(1).expect(lambda x: 1, lb=0.0, ub=2.0) + 0.6321205588285578 + + This is close to + + >>> expon(1).cdf(2.0) - expon(1).cdf(0.0) + 0.6321205588285577 + + If ``conditional=True`` + + >>> expon(1).expect(lambda x: 1, lb=0.0, ub=2.0, conditional=True) + 1.0000000000000002 + + The slight deviation from 1 is due to numerical integration. + + The integrand can be treated as a complex-valued function + by passing ``complex_func=True`` to `scipy.integrate.quad` . + + >>> import numpy as np + >>> from scipy.stats import vonmises + >>> res = vonmises(loc=2, kappa=1).expect(lambda x: np.exp(1j*x), + ... complex_func=True) + >>> res + (-0.18576377217422957+0.40590124735052263j) + + >>> np.angle(res) # location of the (circular) distribution + 2.0 + + """ + lockwds = {'loc': loc, + 'scale': scale} + self._argcheck(*args) + _a, _b = self._get_support(*args) + if func is None: + def fun(x, *args): + return x * self.pdf(x, *args, **lockwds) + else: + def fun(x, *args): + return func(x) * self.pdf(x, *args, **lockwds) + if lb is None: + lb = loc + _a * scale + if ub is None: + ub = loc + _b * scale + + cdf_bounds = self.cdf([lb, ub], *args, **lockwds) + invfac = cdf_bounds[1] - cdf_bounds[0] + + kwds['args'] = args + + # split interval to help integrator w/ infinite support; see gh-8928 + alpha = 0.05 # split body from tails at probability mass `alpha` + inner_bounds = np.array([alpha, 1-alpha]) + cdf_inner_bounds = cdf_bounds[0] + invfac * inner_bounds + c, d = loc + self._ppf(cdf_inner_bounds, *args) * scale + + # Do not silence warnings from integration. + lbc = integrate.quad(fun, lb, c, **kwds)[0] + cd = integrate.quad(fun, c, d, **kwds)[0] + dub = integrate.quad(fun, d, ub, **kwds)[0] + vals = (lbc + cd + dub) + + if conditional: + vals /= invfac + return np.array(vals)[()] # make it a numpy scalar like other methods + + def _param_info(self): + shape_info = self._shape_info() + loc_info = _ShapeInfo("loc", False, (-np.inf, np.inf), (False, False)) + scale_info = _ShapeInfo("scale", False, (0, np.inf), (False, False)) + param_info = shape_info + [loc_info, scale_info] + return param_info + + # For now, _delta_cdf is a private method. + def _delta_cdf(self, x1, x2, *args, loc=0, scale=1): + """ + Compute CDF(x2) - CDF(x1). + + Where x1 is greater than the median, compute SF(x1) - SF(x2), + otherwise compute CDF(x2) - CDF(x1). + + This function is only useful if `dist.sf(x, ...)` has an implementation + that is numerically more accurate than `1 - dist.cdf(x, ...)`. + """ + cdf1 = self.cdf(x1, *args, loc=loc, scale=scale) + # Possible optimizations (needs investigation-these might not be + # better): + # * Use _lazywhere instead of np.where + # * Instead of cdf1 > 0.5, compare x1 to the median. + result = np.where(cdf1 > 0.5, + (self.sf(x1, *args, loc=loc, scale=scale) + - self.sf(x2, *args, loc=loc, scale=scale)), + self.cdf(x2, *args, loc=loc, scale=scale) - cdf1) + if result.ndim == 0: + result = result[()] + return result + + +# Helpers for the discrete distributions +def _drv2_moment(self, n, *args): + """Non-central moment of discrete distribution.""" + def fun(x): + return np.power(x, n) * self._pmf(x, *args) + + _a, _b = self._get_support(*args) + return _expect(fun, _a, _b, self.ppf(0.5, *args), self.inc) + + +def _drv2_ppfsingle(self, q, *args): # Use basic bisection algorithm + _a, _b = self._get_support(*args) + b = _b + a = _a + if isinf(b): # Be sure ending point is > q + b = int(max(100*q, 10)) + while 1: + if b >= _b: + qb = 1.0 + break + qb = self._cdf(b, *args) + if (qb < q): + b += 10 + else: + break + else: + qb = 1.0 + if isinf(a): # be sure starting point < q + a = int(min(-100*q, -10)) + while 1: + if a <= _a: + qb = 0.0 + break + qa = self._cdf(a, *args) + if (qa > q): + a -= 10 + else: + break + else: + qa = self._cdf(a, *args) + + while 1: + if (qa == q): + return a + if (qb == q): + return b + if b <= a+1: + if qa > q: + return a + else: + return b + c = int((a+b)/2.0) + qc = self._cdf(c, *args) + if (qc < q): + if a != c: + a = c + else: + raise RuntimeError('updating stopped, endless loop') + qa = qc + elif (qc > q): + if b != c: + b = c + else: + raise RuntimeError('updating stopped, endless loop') + qb = qc + else: + return c + + +# Must over-ride one of _pmf or _cdf or pass in +# x_k, p(x_k) lists in initialization + + +class rv_discrete(rv_generic): + """A generic discrete random variable class meant for subclassing. + + `rv_discrete` is a base class to construct specific distribution classes + and instances for discrete random variables. It can also be used + to construct an arbitrary distribution defined by a list of support + points and corresponding probabilities. + + Parameters + ---------- + a : float, optional + Lower bound of the support of the distribution, default: 0 + b : float, optional + Upper bound of the support of the distribution, default: plus infinity + moment_tol : float, optional + The tolerance for the generic calculation of moments. + values : tuple of two array_like, optional + ``(xk, pk)`` where ``xk`` are integers and ``pk`` are the non-zero + probabilities between 0 and 1 with ``sum(pk) = 1``. ``xk`` + and ``pk`` must have the same shape, and ``xk`` must be unique. + inc : integer, optional + Increment for the support of the distribution. + Default is 1. (other values have not been tested) + badvalue : float, optional + The value in a result arrays that indicates a value that for which + some argument restriction is violated, default is np.nan. + name : str, optional + The name of the instance. This string is used to construct the default + example for distributions. + longname : str, optional + This string is used as part of the first line of the docstring returned + when a subclass has no docstring of its own. Note: `longname` exists + for backwards compatibility, do not use for new subclasses. + shapes : str, optional + The shape of the distribution. For example "m, n" for a distribution + that takes two integers as the two shape arguments for all its methods + If not provided, shape parameters will be inferred from + the signatures of the private methods, ``_pmf`` and ``_cdf`` of + the instance. + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Methods + ------- + rvs + pmf + logpmf + cdf + logcdf + sf + logsf + ppf + isf + moment + stats + entropy + expect + median + mean + std + var + interval + __call__ + support + + Notes + ----- + This class is similar to `rv_continuous`. Whether a shape parameter is + valid is decided by an ``_argcheck`` method (which defaults to checking + that its arguments are strictly positive.) + The main differences are as follows. + + - The support of the distribution is a set of integers. + - Instead of the probability density function, ``pdf`` (and the + corresponding private ``_pdf``), this class defines the + *probability mass function*, `pmf` (and the corresponding + private ``_pmf``.) + - There is no ``scale`` parameter. + - The default implementations of methods (e.g. ``_cdf``) are not designed + for distributions with support that is unbounded below (i.e. + ``a=-np.inf``), so they must be overridden. + + To create a new discrete distribution, we would do the following: + + >>> from scipy.stats import rv_discrete + >>> class poisson_gen(rv_discrete): + ... "Poisson distribution" + ... def _pmf(self, k, mu): + ... return exp(-mu) * mu**k / factorial(k) + + and create an instance:: + + >>> poisson = poisson_gen(name="poisson") + + Note that above we defined the Poisson distribution in the standard form. + Shifting the distribution can be done by providing the ``loc`` parameter + to the methods of the instance. For example, ``poisson.pmf(x, mu, loc)`` + delegates the work to ``poisson._pmf(x-loc, mu)``. + + **Discrete distributions from a list of probabilities** + + Alternatively, you can construct an arbitrary discrete rv defined + on a finite set of values ``xk`` with ``Prob{X=xk} = pk`` by using the + ``values`` keyword argument to the `rv_discrete` constructor. + + **Deepcopying / Pickling** + + If a distribution or frozen distribution is deepcopied (pickled/unpickled, + etc.), any underlying random number generator is deepcopied with it. An + implication is that if a distribution relies on the singleton RandomState + before copying, it will rely on a copy of that random state after copying, + and ``np.random.seed`` will no longer control the state. + + Examples + -------- + Custom made discrete distribution: + + >>> import numpy as np + >>> from scipy import stats + >>> xk = np.arange(7) + >>> pk = (0.1, 0.2, 0.3, 0.1, 0.1, 0.0, 0.2) + >>> custm = stats.rv_discrete(name='custm', values=(xk, pk)) + >>> + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + >>> ax.plot(xk, custm.pmf(xk), 'ro', ms=12, mec='r') + >>> ax.vlines(xk, 0, custm.pmf(xk), colors='r', lw=4) + >>> plt.show() + + Random number generation: + + >>> R = custm.rvs(size=100) + + """ + def __new__(cls, a=0, b=inf, name=None, badvalue=None, + moment_tol=1e-8, values=None, inc=1, longname=None, + shapes=None, seed=None): + + if values is not None: + # dispatch to a subclass + return super().__new__(rv_sample) + else: + # business as usual + return super().__new__(cls) + + def __init__(self, a=0, b=inf, name=None, badvalue=None, + moment_tol=1e-8, values=None, inc=1, longname=None, + shapes=None, seed=None): + + super().__init__(seed) + + # cf generic freeze + self._ctor_param = dict( + a=a, b=b, name=name, badvalue=badvalue, + moment_tol=moment_tol, values=values, inc=inc, + longname=longname, shapes=shapes, seed=seed) + + if badvalue is None: + badvalue = nan + self.badvalue = badvalue + self.a = a + self.b = b + self.moment_tol = moment_tol + self.inc = inc + self.shapes = shapes + + if values is not None: + raise ValueError("rv_discrete.__init__(..., values != None, ...)") + + self._construct_argparser(meths_to_inspect=[self._pmf, self._cdf], + locscale_in='loc=0', + # scale=1 for discrete RVs + locscale_out='loc, 1') + self._attach_methods() + self._construct_docstrings(name, longname) + + def __getstate__(self): + dct = self.__dict__.copy() + # these methods will be remade in __setstate__ + attrs = ["_parse_args", "_parse_args_stats", "_parse_args_rvs", + "_cdfvec", "_ppfvec", "generic_moment"] + [dct.pop(attr, None) for attr in attrs] + return dct + + def _attach_methods(self): + """Attaches dynamically created methods to the rv_discrete instance.""" + self._cdfvec = vectorize(self._cdf_single, otypes='d') + self.vecentropy = vectorize(self._entropy) + + # _attach_methods is responsible for calling _attach_argparser_methods + self._attach_argparser_methods() + + # nin correction needs to be after we know numargs + # correct nin for generic moment vectorization + _vec_generic_moment = vectorize(_drv2_moment, otypes='d') + _vec_generic_moment.nin = self.numargs + 2 + self.generic_moment = types.MethodType(_vec_generic_moment, self) + + # correct nin for ppf vectorization + _vppf = vectorize(_drv2_ppfsingle, otypes='d') + _vppf.nin = self.numargs + 2 + self._ppfvec = types.MethodType(_vppf, self) + + # now that self.numargs is defined, we can adjust nin + self._cdfvec.nin = self.numargs + 1 + + def _construct_docstrings(self, name, longname): + if name is None: + name = 'Distribution' + self.name = name + + # generate docstring for subclass instances + if longname is None: + if name[0] in ['aeiouAEIOU']: + hstr = "An " + else: + hstr = "A " + longname = hstr + name + + if sys.flags.optimize < 2: + # Skip adding docstrings if interpreter is run with -OO + if self.__doc__ is None: + self._construct_default_doc(longname=longname, + docdict=docdict_discrete, + discrete='discrete') + else: + dct = dict(distdiscrete) + self._construct_doc(docdict_discrete, dct.get(self.name)) + + # discrete RV do not have the scale parameter, remove it + self.__doc__ = self.__doc__.replace( + '\n scale : array_like, ' + 'optional\n scale parameter (default=1)', '') + + def _updated_ctor_param(self): + """Return the current version of _ctor_param, possibly updated by user. + + Used by freezing. + Keep this in sync with the signature of __init__. + """ + dct = self._ctor_param.copy() + dct['a'] = self.a + dct['b'] = self.b + dct['badvalue'] = self.badvalue + dct['moment_tol'] = self.moment_tol + dct['inc'] = self.inc + dct['name'] = self.name + dct['shapes'] = self.shapes + return dct + + def _nonzero(self, k, *args): + return floor(k) == k + + def _pmf(self, k, *args): + return self._cdf(k, *args) - self._cdf(k-1, *args) + + def _logpmf(self, k, *args): + return log(self._pmf(k, *args)) + + def _logpxf(self, k, *args): + # continuous distributions have PDF, discrete have PMF, but sometimes + # the distinction doesn't matter. This lets us use `_logpxf` for both + # discrete and continuous distributions. + return self._logpmf(k, *args) + + def _unpack_loc_scale(self, theta): + try: + loc = theta[-1] + scale = 1 + args = tuple(theta[:-1]) + except IndexError as e: + raise ValueError("Not enough input arguments.") from e + return loc, scale, args + + def _cdf_single(self, k, *args): + _a, _b = self._get_support(*args) + m = arange(int(_a), k+1) + return np.sum(self._pmf(m, *args), axis=0) + + def _cdf(self, x, *args): + k = floor(x) + return self._cdfvec(k, *args) + + # generic _logcdf, _sf, _logsf, _ppf, _isf, _rvs defined in rv_generic + + def rvs(self, *args, **kwargs): + """Random variates of given type. + + Parameters + ---------- + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + size : int or tuple of ints, optional + Defining number of random variates (Default is 1). Note that `size` + has to be given as keyword, not as positional argument. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `random_state` is None (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is + used, seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, that instance is used. + + Returns + ------- + rvs : ndarray or scalar + Random variates of given `size`. + + """ + kwargs['discrete'] = True + return super().rvs(*args, **kwargs) + + def pmf(self, k, *args, **kwds): + """Probability mass function at k of the given RV. + + Parameters + ---------- + k : array_like + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information) + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + pmf : array_like + Probability mass function evaluated at k + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k <= _b) + if not isinstance(self, rv_sample): + cond1 = cond1 & self._nonzero(k, *args) + cond = cond0 & cond1 + output = zeros(shape(cond), 'd') + place(output, (1-cond0) + np.isnan(k), self.badvalue) + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, np.clip(self._pmf(*goodargs), 0, 1)) + if output.ndim == 0: + return output[()] + return output + + def logpmf(self, k, *args, **kwds): + """Log of the probability mass function at k of the given RV. + + Parameters + ---------- + k : array_like + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter. Default is 0. + + Returns + ------- + logpmf : array_like + Log of the probability mass function evaluated at k. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k <= _b) + if not isinstance(self, rv_sample): + cond1 = cond1 & self._nonzero(k, *args) + cond = cond0 & cond1 + output = empty(shape(cond), 'd') + output.fill(-inf) + place(output, (1-cond0) + np.isnan(k), self.badvalue) + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, self._logpmf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def cdf(self, k, *args, **kwds): + """Cumulative distribution function of the given RV. + + Parameters + ---------- + k : array_like, int + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + cdf : ndarray + Cumulative distribution function evaluated at `k`. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k < _b) + cond2 = (k >= _b) + cond3 = np.isneginf(k) + cond = cond0 & cond1 & np.isfinite(k) + + output = zeros(shape(cond), 'd') + place(output, cond2*(cond0 == cond0), 1.0) + place(output, cond3*(cond0 == cond0), 0.0) + place(output, (1-cond0) + np.isnan(k), self.badvalue) + + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, np.clip(self._cdf(*goodargs), 0, 1)) + if output.ndim == 0: + return output[()] + return output + + def logcdf(self, k, *args, **kwds): + """Log of the cumulative distribution function at k of the given RV. + + Parameters + ---------- + k : array_like, int + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + logcdf : array_like + Log of the cumulative distribution function evaluated at k. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k < _b) + cond2 = (k >= _b) + cond = cond0 & cond1 + output = empty(shape(cond), 'd') + output.fill(-inf) + place(output, (1-cond0) + np.isnan(k), self.badvalue) + place(output, cond2*(cond0 == cond0), 0.0) + + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, self._logcdf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def sf(self, k, *args, **kwds): + """Survival function (1 - `cdf`) at k of the given RV. + + Parameters + ---------- + k : array_like + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + sf : array_like + Survival function evaluated at k. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k < _b) + cond2 = ((k < _a) | np.isneginf(k)) & cond0 + cond = cond0 & cond1 & np.isfinite(k) + output = zeros(shape(cond), 'd') + place(output, (1-cond0) + np.isnan(k), self.badvalue) + place(output, cond2, 1.0) + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, np.clip(self._sf(*goodargs), 0, 1)) + if output.ndim == 0: + return output[()] + return output + + def logsf(self, k, *args, **kwds): + """Log of the survival function of the given RV. + + Returns the log of the "survival function," defined as 1 - `cdf`, + evaluated at `k`. + + Parameters + ---------- + k : array_like + Quantiles. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + logsf : ndarray + Log of the survival function evaluated at `k`. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + k, loc = map(asarray, (k, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + k = asarray(k-loc) + cond0 = self._argcheck(*args) + cond1 = (k >= _a) & (k < _b) + cond2 = (k < _a) & cond0 + cond = cond0 & cond1 + output = empty(shape(cond), 'd') + output.fill(-inf) + place(output, (1-cond0) + np.isnan(k), self.badvalue) + place(output, cond2, 0.0) + if np.any(cond): + goodargs = argsreduce(cond, *((k,)+args)) + place(output, cond, self._logsf(*goodargs)) + if output.ndim == 0: + return output[()] + return output + + def ppf(self, q, *args, **kwds): + """Percent point function (inverse of `cdf`) at q of the given RV. + + Parameters + ---------- + q : array_like + Lower tail probability. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + k : array_like + Quantile corresponding to the lower tail probability, q. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + q, loc = map(asarray, (q, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + cond0 = self._argcheck(*args) & (loc == loc) + cond1 = (q > 0) & (q < 1) + cond2 = (q == 1) & cond0 + cond = cond0 & cond1 + output = np.full(shape(cond), fill_value=self.badvalue, dtype='d') + # output type 'd' to handle nin and inf + place(output, (q == 0)*(cond == cond), _a-1 + loc) + place(output, cond2, _b + loc) + if np.any(cond): + goodargs = argsreduce(cond, *((q,)+args+(loc,))) + loc, goodargs = goodargs[-1], goodargs[:-1] + place(output, cond, self._ppf(*goodargs) + loc) + + if output.ndim == 0: + return output[()] + return output + + def isf(self, q, *args, **kwds): + """Inverse survival function (inverse of `sf`) at q of the given RV. + + Parameters + ---------- + q : array_like + Upper tail probability. + arg1, arg2, arg3,... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + loc : array_like, optional + Location parameter (default=0). + + Returns + ------- + k : ndarray or scalar + Quantile corresponding to the upper tail probability, q. + + """ + args, loc, _ = self._parse_args(*args, **kwds) + q, loc = map(asarray, (q, loc)) + args = tuple(map(asarray, args)) + _a, _b = self._get_support(*args) + cond0 = self._argcheck(*args) & (loc == loc) + cond1 = (q > 0) & (q < 1) + cond2 = (q == 1) & cond0 + cond3 = (q == 0) & cond0 + cond = cond0 & cond1 + + # same problem as with ppf; copied from ppf and changed + output = np.full(shape(cond), fill_value=self.badvalue, dtype='d') + # output type 'd' to handle nin and inf + lower_bound = _a - 1 + loc + upper_bound = _b + loc + place(output, cond2*(cond == cond), lower_bound) + place(output, cond3*(cond == cond), upper_bound) + + # call place only if at least 1 valid argument + if np.any(cond): + goodargs = argsreduce(cond, *((q,)+args+(loc,))) + loc, goodargs = goodargs[-1], goodargs[:-1] + # PB same as ticket 766 + place(output, cond, self._isf(*goodargs) + loc) + + if output.ndim == 0: + return output[()] + return output + + def _entropy(self, *args): + if hasattr(self, 'pk'): + return stats.entropy(self.pk) + else: + _a, _b = self._get_support(*args) + return _expect(lambda x: entr(self.pmf(x, *args)), + _a, _b, self.ppf(0.5, *args), self.inc) + + def expect(self, func=None, args=(), loc=0, lb=None, ub=None, + conditional=False, maxcount=1000, tolerance=1e-10, chunksize=32): + """ + Calculate expected value of a function with respect to the distribution + for discrete distribution by numerical summation. + + Parameters + ---------- + func : callable, optional + Function for which the expectation value is calculated. + Takes only one argument. + The default is the identity mapping f(k) = k. + args : tuple, optional + Shape parameters of the distribution. + loc : float, optional + Location parameter. + Default is 0. + lb, ub : int, optional + Lower and upper bound for the summation, default is set to the + support of the distribution, inclusive (``lb <= k <= ub``). + conditional : bool, optional + If true then the expectation is corrected by the conditional + probability of the summation interval. The return value is the + expectation of the function, `func`, conditional on being in + the given interval (k such that ``lb <= k <= ub``). + Default is False. + maxcount : int, optional + Maximal number of terms to evaluate (to avoid an endless loop for + an infinite sum). Default is 1000. + tolerance : float, optional + Absolute tolerance for the summation. Default is 1e-10. + chunksize : int, optional + Iterate over the support of a distributions in chunks of this size. + Default is 32. + + Returns + ------- + expect : float + Expected value. + + Notes + ----- + For heavy-tailed distributions, the expected value may or + may not exist, + depending on the function, `func`. If it does exist, but the + sum converges + slowly, the accuracy of the result may be rather low. For instance, for + ``zipf(4)``, accuracy for mean, variance in example is only 1e-5. + increasing `maxcount` and/or `chunksize` may improve the result, + but may also make zipf very slow. + + The function is not vectorized. + + """ + if func is None: + def fun(x): + # loc and args from outer scope + return (x+loc)*self._pmf(x, *args) + else: + def fun(x): + # loc and args from outer scope + return func(x+loc)*self._pmf(x, *args) + # used pmf because _pmf does not check support in randint and there + # might be problems(?) with correct self.a, self.b at this stage maybe + # not anymore, seems to work now with _pmf + + _a, _b = self._get_support(*args) + if lb is None: + lb = _a + else: + lb = lb - loc # convert bound for standardized distribution + if ub is None: + ub = _b + else: + ub = ub - loc # convert bound for standardized distribution + if conditional: + invfac = self.sf(lb-1, *args) - self.sf(ub, *args) + else: + invfac = 1.0 + + if isinstance(self, rv_sample): + res = self._expect(fun, lb, ub) + return res / invfac + + # iterate over the support, starting from the median + x0 = self.ppf(0.5, *args) + res = _expect(fun, lb, ub, x0, self.inc, maxcount, tolerance, chunksize) + return res / invfac + + def _param_info(self): + shape_info = self._shape_info() + loc_info = _ShapeInfo("loc", True, (-np.inf, np.inf), (False, False)) + param_info = shape_info + [loc_info] + return param_info + + +def _expect(fun, lb, ub, x0, inc, maxcount=1000, tolerance=1e-10, + chunksize=32): + """Helper for computing the expectation value of `fun`.""" + # short-circuit if the support size is small enough + if (ub - lb) <= chunksize: + supp = np.arange(lb, ub+1, inc) + vals = fun(supp) + return np.sum(vals) + + # otherwise, iterate starting from x0 + if x0 < lb: + x0 = lb + if x0 > ub: + x0 = ub + + count, tot = 0, 0. + # iterate over [x0, ub] inclusive + for x in _iter_chunked(x0, ub+1, chunksize=chunksize, inc=inc): + count += x.size + delta = np.sum(fun(x)) + tot += delta + if abs(delta) < tolerance * x.size: + break + if count > maxcount: + warnings.warn('expect(): sum did not converge', + RuntimeWarning, stacklevel=3) + return tot + + # iterate over [lb, x0) + for x in _iter_chunked(x0-1, lb-1, chunksize=chunksize, inc=-inc): + count += x.size + delta = np.sum(fun(x)) + tot += delta + if abs(delta) < tolerance * x.size: + break + if count > maxcount: + warnings.warn('expect(): sum did not converge', + RuntimeWarning, stacklevel=3) + break + + return tot + + +def _iter_chunked(x0, x1, chunksize=4, inc=1): + """Iterate from x0 to x1 in chunks of chunksize and steps inc. + + x0 must be finite, x1 need not be. In the latter case, the iterator is + infinite. + Handles both x0 < x1 and x0 > x1. In the latter case, iterates downwards + (make sure to set inc < 0.) + + >>> from scipy.stats._distn_infrastructure import _iter_chunked + >>> [x for x in _iter_chunked(2, 5, inc=2)] + [array([2, 4])] + >>> [x for x in _iter_chunked(2, 11, inc=2)] + [array([2, 4, 6, 8]), array([10])] + >>> [x for x in _iter_chunked(2, -5, inc=-2)] + [array([ 2, 0, -2, -4])] + >>> [x for x in _iter_chunked(2, -9, inc=-2)] + [array([ 2, 0, -2, -4]), array([-6, -8])] + + """ + if inc == 0: + raise ValueError('Cannot increment by zero.') + if chunksize <= 0: + raise ValueError('Chunk size must be positive; got %s.' % chunksize) + + s = 1 if inc > 0 else -1 + stepsize = abs(chunksize * inc) + + x = x0 + while (x - x1) * inc < 0: + delta = min(stepsize, abs(x - x1)) + step = delta * s + supp = np.arange(x, x + step, inc) + x += step + yield supp + + +class rv_sample(rv_discrete): + """A 'sample' discrete distribution defined by the support and values. + + The ctor ignores most of the arguments, only needs the `values` argument. + """ + + def __init__(self, a=0, b=inf, name=None, badvalue=None, + moment_tol=1e-8, values=None, inc=1, longname=None, + shapes=None, seed=None): + + super(rv_discrete, self).__init__(seed) + + if values is None: + raise ValueError("rv_sample.__init__(..., values=None,...)") + + # cf generic freeze + self._ctor_param = dict( + a=a, b=b, name=name, badvalue=badvalue, + moment_tol=moment_tol, values=values, inc=inc, + longname=longname, shapes=shapes, seed=seed) + + if badvalue is None: + badvalue = nan + self.badvalue = badvalue + self.moment_tol = moment_tol + self.inc = inc + self.shapes = shapes + self.vecentropy = self._entropy + + xk, pk = values + + if np.shape(xk) != np.shape(pk): + raise ValueError("xk and pk must have the same shape.") + if np.less(pk, 0.0).any(): + raise ValueError("All elements of pk must be non-negative.") + if not np.allclose(np.sum(pk), 1): + raise ValueError("The sum of provided pk is not 1.") + if not len(set(np.ravel(xk))) == np.size(xk): + raise ValueError("xk may not contain duplicate values.") + + indx = np.argsort(np.ravel(xk)) + self.xk = np.take(np.ravel(xk), indx, 0) + self.pk = np.take(np.ravel(pk), indx, 0) + self.a = self.xk[0] + self.b = self.xk[-1] + + self.qvals = np.cumsum(self.pk, axis=0) + + self.shapes = ' ' # bypass inspection + + self._construct_argparser(meths_to_inspect=[self._pmf], + locscale_in='loc=0', + # scale=1 for discrete RVs + locscale_out='loc, 1') + + self._attach_methods() + + self._construct_docstrings(name, longname) + + def __getstate__(self): + dct = self.__dict__.copy() + + # these methods will be remade in rv_generic.__setstate__, + # which calls rv_generic._attach_methods + attrs = ["_parse_args", "_parse_args_stats", "_parse_args_rvs"] + [dct.pop(attr, None) for attr in attrs] + + return dct + + def _attach_methods(self): + """Attaches dynamically created argparser methods.""" + self._attach_argparser_methods() + + def _get_support(self, *args): + """Return the support of the (unscaled, unshifted) distribution. + + Parameters + ---------- + arg1, arg2, ... : array_like + The shape parameter(s) for the distribution (see docstring of the + instance object for more information). + + Returns + ------- + a, b : numeric (float, or int or +/-np.inf) + end-points of the distribution's support. + """ + return self.a, self.b + + def _pmf(self, x): + return np.select([x == k for k in self.xk], + [np.broadcast_arrays(p, x)[0] for p in self.pk], 0) + + def _cdf(self, x): + xx, xxk = np.broadcast_arrays(x[:, None], self.xk) + indx = np.argmax(xxk > xx, axis=-1) - 1 + return self.qvals[indx] + + def _ppf(self, q): + qq, sqq = np.broadcast_arrays(q[..., None], self.qvals) + indx = argmax(sqq >= qq, axis=-1) + return self.xk[indx] + + def _rvs(self, size=None, random_state=None): + # Need to define it explicitly, otherwise .rvs() with size=None + # fails due to explicit broadcasting in _ppf + U = random_state.uniform(size=size) + if size is None: + U = np.array(U, ndmin=1) + Y = self._ppf(U)[0] + else: + Y = self._ppf(U) + return Y + + def _entropy(self): + return stats.entropy(self.pk) + + def generic_moment(self, n): + n = asarray(n) + return np.sum(self.xk**n[np.newaxis, ...] * self.pk, axis=0) + + def _expect(self, fun, lb, ub, *args, **kwds): + # ignore all args, just do a brute force summation + supp = self.xk[(lb <= self.xk) & (self.xk <= ub)] + vals = fun(supp) + return np.sum(vals) + + +def _check_shape(argshape, size): + """ + This is a utility function used by `_rvs()` in the class geninvgauss_gen. + It compares the tuple argshape to the tuple size. + + Parameters + ---------- + argshape : tuple of integers + Shape of the arguments. + size : tuple of integers or integer + Size argument of rvs(). + + Returns + ------- + The function returns two tuples, scalar_shape and bc. + + scalar_shape : tuple + Shape to which the 1-d array of random variates returned by + _rvs_scalar() is converted when it is copied into the + output array of _rvs(). + + bc : tuple of booleans + bc is an tuple the same length as size. bc[j] is True if the data + associated with that index is generated in one call of _rvs_scalar(). + + """ + scalar_shape = [] + bc = [] + for argdim, sizedim in zip_longest(argshape[::-1], size[::-1], + fillvalue=1): + if sizedim > argdim or (argdim == sizedim == 1): + scalar_shape.append(sizedim) + bc.append(True) + else: + bc.append(False) + return tuple(scalar_shape[::-1]), tuple(bc[::-1]) + + +def get_distribution_names(namespace_pairs, rv_base_class): + """Collect names of statistical distributions and their generators. + + Parameters + ---------- + namespace_pairs : sequence + A snapshot of (name, value) pairs in the namespace of a module. + rv_base_class : class + The base class of random variable generator classes in a module. + + Returns + ------- + distn_names : list of strings + Names of the statistical distributions. + distn_gen_names : list of strings + Names of the generators of the statistical distributions. + Note that these are not simply the names of the statistical + distributions, with a _gen suffix added. + + """ + distn_names = [] + distn_gen_names = [] + for name, value in namespace_pairs: + if name.startswith('_'): + continue + if name.endswith('_gen') and issubclass(value, rv_base_class): + distn_gen_names.append(name) + if isinstance(value, rv_base_class): + distn_names.append(name) + return distn_names, distn_gen_names diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distr_params.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distr_params.py new file mode 100644 index 0000000000000000000000000000000000000000..2b86298d592f799ba2fe0ba88091308b0b54e839 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_distr_params.py @@ -0,0 +1,288 @@ +""" +Sane parameters for stats.distributions. +""" +import numpy as np + +distcont = [ + ['alpha', (3.5704770516650459,)], + ['anglit', ()], + ['arcsine', ()], + ['argus', (1.0,)], + ['beta', (2.3098496451481823, 0.62687954300963677)], + ['betaprime', (5, 6)], + ['bradford', (0.29891359763170633,)], + ['burr', (10.5, 4.3)], + ['burr12', (10, 4)], + ['cauchy', ()], + ['chi', (78,)], + ['chi2', (55,)], + ['cosine', ()], + ['crystalball', (2.0, 3.0)], + ['dgamma', (1.1023326088288166,)], + ['dweibull', (2.0685080649914673,)], + ['erlang', (10,)], + ['expon', ()], + ['exponnorm', (1.5,)], + ['exponpow', (2.697119160358469,)], + ['exponweib', (2.8923945291034436, 1.9505288745913174)], + ['f', (29, 18)], + ['fatiguelife', (29,)], # correction numargs = 1 + ['fisk', (3.0857548622253179,)], + ['foldcauchy', (4.7164673455831894,)], + ['foldnorm', (1.9521253373555869,)], + ['gamma', (1.9932305483800778,)], + ['gausshyper', (13.763771604130699, 3.1189636648681431, + 2.5145980350183019, 5.1811649903971615)], # veryslow + ['genexpon', (9.1325976465418908, 16.231956600590632, 3.2819552690843983)], + ['genextreme', (-0.1,)], + ['gengamma', (4.4162385429431925, 3.1193091679242761)], + ['gengamma', (4.4162385429431925, -3.1193091679242761)], + ['genhalflogistic', (0.77274727809929322,)], + ['genhyperbolic', (0.5, 1.5, -0.5,)], + ['geninvgauss', (2.3, 1.5)], + ['genlogistic', (0.41192440799679475,)], + ['gennorm', (1.2988442399460265,)], + ['halfgennorm', (0.6748054997000371,)], + ['genpareto', (0.1,)], # use case with finite moments + ['gibrat', ()], + ['gompertz', (0.94743713075105251,)], + ['gumbel_l', ()], + ['gumbel_r', ()], + ['halfcauchy', ()], + ['halflogistic', ()], + ['halfnorm', ()], + ['hypsecant', ()], + ['invgamma', (4.0668996136993067,)], + ['invgauss', (0.14546264555347513,)], + ['invweibull', (10.58,)], + ['jf_skew_t', (8, 4)], + ['johnsonsb', (4.3172675099141058, 3.1837781130785063)], + ['johnsonsu', (2.554395574161155, 2.2482281679651965)], + ['kappa4', (0.0, 0.0)], + ['kappa4', (-0.1, 0.1)], + ['kappa4', (0.0, 0.1)], + ['kappa4', (0.1, 0.0)], + ['kappa3', (1.0,)], + ['ksone', (1000,)], # replace 22 by 100 to avoid failing range, ticket 956 + ['kstwo', (10,)], + ['kstwobign', ()], + ['laplace', ()], + ['laplace_asymmetric', (2,)], + ['levy', ()], + ['levy_l', ()], + ['levy_stable', (1.8, -0.5)], + ['loggamma', (0.41411931826052117,)], + ['logistic', ()], + ['loglaplace', (3.2505926592051435,)], + ['lognorm', (0.95368226960575331,)], + ['loguniform', (0.01, 1.25)], + ['lomax', (1.8771398388773268,)], + ['maxwell', ()], + ['mielke', (10.4, 4.6)], + ['moyal', ()], + ['nakagami', (4.9673794866666237,)], + ['ncf', (27, 27, 0.41578441799226107)], + ['nct', (14, 0.24045031331198066)], + ['ncx2', (21, 1.0560465975116415)], + ['norm', ()], + ['norminvgauss', (1.25, 0.5)], + ['pareto', (2.621716532144454,)], + ['pearson3', (0.1,)], + ['pearson3', (-2,)], + ['powerlaw', (1.6591133289905851,)], + ['powerlaw', (0.6591133289905851,)], + ['powerlognorm', (2.1413923530064087, 0.44639540782048337)], + ['powernorm', (4.4453652254590779,)], + ['rayleigh', ()], + ['rdist', (1.6,)], + ['recipinvgauss', (0.63004267809369119,)], + ['reciprocal', (0.01, 1.25)], + ['rel_breitwigner', (36.545206797050334, )], + ['rice', (0.7749725210111873,)], + ['semicircular', ()], + ['skewcauchy', (0.5,)], + ['skewnorm', (4.0,)], + ['studentized_range', (3.0, 10.0)], + ['t', (2.7433514990818093,)], + ['trapezoid', (0.2, 0.8)], + ['triang', (0.15785029824528218,)], + ['truncexpon', (4.6907725456810478,)], + ['truncnorm', (-1.0978730080013919, 2.7306754109031979)], + ['truncnorm', (0.1, 2.)], + ['truncpareto', (1.8, 5.3)], + ['truncpareto', (2, 5)], + ['truncweibull_min', (2.5, 0.25, 1.75)], + ['tukeylambda', (3.1321477856738267,)], + ['uniform', ()], + ['vonmises', (3.9939042581071398,)], + ['vonmises_line', (3.9939042581071398,)], + ['wald', ()], + ['weibull_max', (2.8687961709100187,)], + ['weibull_min', (1.7866166930421596,)], + ['wrapcauchy', (0.031071279018614728,)]] + + +distdiscrete = [ + ['bernoulli',(0.3,)], + ['betabinom', (5, 2.3, 0.63)], + ['betanbinom', (5, 9.3, 1)], + ['binom', (5, 0.4)], + ['boltzmann',(1.4, 19)], + ['dlaplace', (0.8,)], # 0.5 + ['geom', (0.5,)], + ['hypergeom',(30, 12, 6)], + ['hypergeom',(21,3,12)], # numpy.random (3,18,12) numpy ticket:921 + ['hypergeom',(21,18,11)], # numpy.random (18,3,11) numpy ticket:921 + ['nchypergeom_fisher', (140, 80, 60, 0.5)], + ['nchypergeom_wallenius', (140, 80, 60, 0.5)], + ['logser', (0.6,)], # re-enabled, numpy ticket:921 + ['nbinom', (0.4, 0.4)], # from tickets: 583 + ['nbinom', (5, 0.5)], + ['planck', (0.51,)], # 4.1 + ['poisson', (0.6,)], + ['randint', (7, 31)], + ['skellam', (15, 8)], + ['zipf', (6.6,)], + ['zipfian', (0.75, 15)], + ['zipfian', (1.25, 10)], + ['yulesimon', (11.0,)], + ['nhypergeom', (20, 7, 1)] +] + + +invdistdiscrete = [ + # In each of the following, at least one shape parameter is invalid + ['hypergeom', (3, 3, 4)], + ['nhypergeom', (5, 2, 8)], + ['nchypergeom_fisher', (3, 3, 4, 1)], + ['nchypergeom_wallenius', (3, 3, 4, 1)], + ['bernoulli', (1.5, )], + ['binom', (10, 1.5)], + ['betabinom', (10, -0.4, -0.5)], + ['betanbinom', (10, -0.4, -0.5)], + ['boltzmann', (-1, 4)], + ['dlaplace', (-0.5, )], + ['geom', (1.5, )], + ['logser', (1.5, )], + ['nbinom', (10, 1.5)], + ['planck', (-0.5, )], + ['poisson', (-0.5, )], + ['randint', (5, 2)], + ['skellam', (-5, -2)], + ['zipf', (-2, )], + ['yulesimon', (-2, )], + ['zipfian', (-0.75, 15)] +] + + +invdistcont = [ + # In each of the following, at least one shape parameter is invalid + ['alpha', (-1, )], + ['anglit', ()], + ['arcsine', ()], + ['argus', (-1, )], + ['beta', (-2, 2)], + ['betaprime', (-2, 2)], + ['bradford', (-1, )], + ['burr', (-1, 1)], + ['burr12', (-1, 1)], + ['cauchy', ()], + ['chi', (-1, )], + ['chi2', (-1, )], + ['cosine', ()], + ['crystalball', (-1, 2)], + ['dgamma', (-1, )], + ['dweibull', (-1, )], + ['erlang', (-1, )], + ['expon', ()], + ['exponnorm', (-1, )], + ['exponweib', (1, -1)], + ['exponpow', (-1, )], + ['f', (10, -10)], + ['fatiguelife', (-1, )], + ['fisk', (-1, )], + ['foldcauchy', (-1, )], + ['foldnorm', (-1, )], + ['genlogistic', (-1, )], + ['gennorm', (-1, )], + ['genpareto', (np.inf, )], + ['genexpon', (1, 2, -3)], + ['genextreme', (np.inf, )], + ['genhyperbolic', (0.5, -0.5, -1.5,)], + ['gausshyper', (1, 2, 3, -4)], + ['gamma', (-1, )], + ['gengamma', (-1, 0)], + ['genhalflogistic', (-1, )], + ['geninvgauss', (1, 0)], + ['gibrat', ()], + ['gompertz', (-1, )], + ['gumbel_r', ()], + ['gumbel_l', ()], + ['halfcauchy', ()], + ['halflogistic', ()], + ['halfnorm', ()], + ['halfgennorm', (-1, )], + ['hypsecant', ()], + ['invgamma', (-1, )], + ['invgauss', (-1, )], + ['invweibull', (-1, )], + ['jf_skew_t', (-1, 0)], + ['johnsonsb', (1, -2)], + ['johnsonsu', (1, -2)], + ['kappa4', (np.nan, 0)], + ['kappa3', (-1, )], + ['ksone', (-1, )], + ['kstwo', (-1, )], + ['kstwobign', ()], + ['laplace', ()], + ['laplace_asymmetric', (-1, )], + ['levy', ()], + ['levy_l', ()], + ['levy_stable', (-1, 1)], + ['logistic', ()], + ['loggamma', (-1, )], + ['loglaplace', (-1, )], + ['lognorm', (-1, )], + ['loguniform', (10, 5)], + ['lomax', (-1, )], + ['maxwell', ()], + ['mielke', (1, -2)], + ['moyal', ()], + ['nakagami', (-1, )], + ['ncx2', (-1, 2)], + ['ncf', (10, 20, -1)], + ['nct', (-1, 2)], + ['norm', ()], + ['norminvgauss', (5, -10)], + ['pareto', (-1, )], + ['pearson3', (np.nan, )], + ['powerlaw', (-1, )], + ['powerlognorm', (1, -2)], + ['powernorm', (-1, )], + ['rdist', (-1, )], + ['rayleigh', ()], + ['rice', (-1, )], + ['recipinvgauss', (-1, )], + ['semicircular', ()], + ['skewnorm', (np.inf, )], + ['studentized_range', (-1, 1)], + ['rel_breitwigner', (-2, )], + ['t', (-1, )], + ['trapezoid', (0, 2)], + ['triang', (2, )], + ['truncexpon', (-1, )], + ['truncnorm', (10, 5)], + ['truncpareto', (-1, 5)], + ['truncpareto', (1.8, .5)], + ['truncweibull_min', (-2.5, 0.25, 1.75)], + ['tukeylambda', (np.nan, )], + ['uniform', ()], + ['vonmises', (-1, )], + ['vonmises_line', (-1, )], + ['wald', ()], + ['weibull_min', (-1, )], + ['weibull_max', (-1, )], + ['wrapcauchy', (2, )], + ['reciprocal', (15, 10)], + ['skewcauchy', (2, )] +] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_entropy.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_entropy.py new file mode 100644 index 0000000000000000000000000000000000000000..f4a81b05749fda69191eab0876eb4bfba8be8b59 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_entropy.py @@ -0,0 +1,423 @@ +""" +Created on Fri Apr 2 09:06:05 2021 + +@author: matth +""" + +from __future__ import annotations +import math +import numpy as np +from scipy import special +from ._axis_nan_policy import _axis_nan_policy_factory, _broadcast_arrays + +__all__ = ['entropy', 'differential_entropy'] + + +@_axis_nan_policy_factory( + lambda x: x, + n_samples=lambda kwgs: ( + 2 if ("qk" in kwgs and kwgs["qk"] is not None) + else 1 + ), + n_outputs=1, result_to_tuple=lambda x: (x,), paired=True, + too_small=-1 # entropy doesn't have too small inputs +) +def entropy(pk: np.typing.ArrayLike, + qk: np.typing.ArrayLike | None = None, + base: float | None = None, + axis: int = 0 + ) -> np.number | np.ndarray: + """ + Calculate the Shannon entropy/relative entropy of given distribution(s). + + If only probabilities `pk` are given, the Shannon entropy is calculated as + ``H = -sum(pk * log(pk))``. + + If `qk` is not None, then compute the relative entropy + ``D = sum(pk * log(pk / qk))``. This quantity is also known + as the Kullback-Leibler divergence. + + This routine will normalize `pk` and `qk` if they don't sum to 1. + + Parameters + ---------- + pk : array_like + Defines the (discrete) distribution. Along each axis-slice of ``pk``, + element ``i`` is the (possibly unnormalized) probability of event + ``i``. + qk : array_like, optional + Sequence against which the relative entropy is computed. Should be in + the same format as `pk`. + base : float, optional + The logarithmic base to use, defaults to ``e`` (natural logarithm). + axis : int, optional + The axis along which the entropy is calculated. Default is 0. + + Returns + ------- + S : {float, array_like} + The calculated entropy. + + Notes + ----- + Informally, the Shannon entropy quantifies the expected uncertainty + inherent in the possible outcomes of a discrete random variable. + For example, + if messages consisting of sequences of symbols from a set are to be + encoded and transmitted over a noiseless channel, then the Shannon entropy + ``H(pk)`` gives a tight lower bound for the average number of units of + information needed per symbol if the symbols occur with frequencies + governed by the discrete distribution `pk` [1]_. The choice of base + determines the choice of units; e.g., ``e`` for nats, ``2`` for bits, etc. + + The relative entropy, ``D(pk|qk)``, quantifies the increase in the average + number of units of information needed per symbol if the encoding is + optimized for the probability distribution `qk` instead of the true + distribution `pk`. Informally, the relative entropy quantifies the expected + excess in surprise experienced if one believes the true distribution is + `qk` when it is actually `pk`. + + A related quantity, the cross entropy ``CE(pk, qk)``, satisfies the + equation ``CE(pk, qk) = H(pk) + D(pk|qk)`` and can also be calculated with + the formula ``CE = -sum(pk * log(qk))``. It gives the average + number of units of information needed per symbol if an encoding is + optimized for the probability distribution `qk` when the true distribution + is `pk`. It is not computed directly by `entropy`, but it can be computed + using two calls to the function (see Examples). + + See [2]_ for more information. + + References + ---------- + .. [1] Shannon, C.E. (1948), A Mathematical Theory of Communication. + Bell System Technical Journal, 27: 379-423. + https://doi.org/10.1002/j.1538-7305.1948.tb01338.x + .. [2] Thomas M. Cover and Joy A. Thomas. 2006. Elements of Information + Theory (Wiley Series in Telecommunications and Signal Processing). + Wiley-Interscience, USA. + + + Examples + -------- + The outcome of a fair coin is the most uncertain: + + >>> import numpy as np + >>> from scipy.stats import entropy + >>> base = 2 # work in units of bits + >>> pk = np.array([1/2, 1/2]) # fair coin + >>> H = entropy(pk, base=base) + >>> H + 1.0 + >>> H == -np.sum(pk * np.log(pk)) / np.log(base) + True + + The outcome of a biased coin is less uncertain: + + >>> qk = np.array([9/10, 1/10]) # biased coin + >>> entropy(qk, base=base) + 0.46899559358928117 + + The relative entropy between the fair coin and biased coin is calculated + as: + + >>> D = entropy(pk, qk, base=base) + >>> D + 0.7369655941662062 + >>> D == np.sum(pk * np.log(pk/qk)) / np.log(base) + True + + The cross entropy can be calculated as the sum of the entropy and + relative entropy`: + + >>> CE = entropy(pk, base=base) + entropy(pk, qk, base=base) + >>> CE + 1.736965594166206 + >>> CE == -np.sum(pk * np.log(qk)) / np.log(base) + True + + """ + if base is not None and base <= 0: + raise ValueError("`base` must be a positive number or `None`.") + + pk = np.asarray(pk) + with np.errstate(invalid='ignore'): + pk = 1.0*pk / np.sum(pk, axis=axis, keepdims=True) + if qk is None: + vec = special.entr(pk) + else: + qk = np.asarray(qk) + pk, qk = _broadcast_arrays((pk, qk), axis=None) # don't ignore any axes + sum_kwargs = dict(axis=axis, keepdims=True) + qk = 1.0*qk / np.sum(qk, **sum_kwargs) # type: ignore[operator, call-overload] + vec = special.rel_entr(pk, qk) + S = np.sum(vec, axis=axis) + if base is not None: + S /= np.log(base) + return S + + +def _differential_entropy_is_too_small(samples, kwargs, axis=-1): + values = samples[0] + n = values.shape[axis] + window_length = kwargs.get("window_length", + math.floor(math.sqrt(n) + 0.5)) + if not 2 <= 2 * window_length < n: + return True + return False + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,), + too_small=_differential_entropy_is_too_small +) +def differential_entropy( + values: np.typing.ArrayLike, + *, + window_length: int | None = None, + base: float | None = None, + axis: int = 0, + method: str = "auto", +) -> np.number | np.ndarray: + r"""Given a sample of a distribution, estimate the differential entropy. + + Several estimation methods are available using the `method` parameter. By + default, a method is selected based the size of the sample. + + Parameters + ---------- + values : sequence + Sample from a continuous distribution. + window_length : int, optional + Window length for computing Vasicek estimate. Must be an integer + between 1 and half of the sample size. If ``None`` (the default), it + uses the heuristic value + + .. math:: + \left \lfloor \sqrt{n} + 0.5 \right \rfloor + + where :math:`n` is the sample size. This heuristic was originally + proposed in [2]_ and has become common in the literature. + base : float, optional + The logarithmic base to use, defaults to ``e`` (natural logarithm). + axis : int, optional + The axis along which the differential entropy is calculated. + Default is 0. + method : {'vasicek', 'van es', 'ebrahimi', 'correa', 'auto'}, optional + The method used to estimate the differential entropy from the sample. + Default is ``'auto'``. See Notes for more information. + + Returns + ------- + entropy : float + The calculated differential entropy. + + Notes + ----- + This function will converge to the true differential entropy in the limit + + .. math:: + n \to \infty, \quad m \to \infty, \quad \frac{m}{n} \to 0 + + The optimal choice of ``window_length`` for a given sample size depends on + the (unknown) distribution. Typically, the smoother the density of the + distribution, the larger the optimal value of ``window_length`` [1]_. + + The following options are available for the `method` parameter. + + * ``'vasicek'`` uses the estimator presented in [1]_. This is + one of the first and most influential estimators of differential entropy. + * ``'van es'`` uses the bias-corrected estimator presented in [3]_, which + is not only consistent but, under some conditions, asymptotically normal. + * ``'ebrahimi'`` uses an estimator presented in [4]_, which was shown + in simulation to have smaller bias and mean squared error than + the Vasicek estimator. + * ``'correa'`` uses the estimator presented in [5]_ based on local linear + regression. In a simulation study, it had consistently smaller mean + square error than the Vasiceck estimator, but it is more expensive to + compute. + * ``'auto'`` selects the method automatically (default). Currently, + this selects ``'van es'`` for very small samples (<10), ``'ebrahimi'`` + for moderate sample sizes (11-1000), and ``'vasicek'`` for larger + samples, but this behavior is subject to change in future versions. + + All estimators are implemented as described in [6]_. + + References + ---------- + .. [1] Vasicek, O. (1976). A test for normality based on sample entropy. + Journal of the Royal Statistical Society: + Series B (Methodological), 38(1), 54-59. + .. [2] Crzcgorzewski, P., & Wirczorkowski, R. (1999). Entropy-based + goodness-of-fit test for exponentiality. Communications in + Statistics-Theory and Methods, 28(5), 1183-1202. + .. [3] Van Es, B. (1992). Estimating functionals related to a density by a + class of statistics based on spacings. Scandinavian Journal of + Statistics, 61-72. + .. [4] Ebrahimi, N., Pflughoeft, K., & Soofi, E. S. (1994). Two measures + of sample entropy. Statistics & Probability Letters, 20(3), 225-234. + .. [5] Correa, J. C. (1995). A new estimator of entropy. Communications + in Statistics-Theory and Methods, 24(10), 2439-2449. + .. [6] Noughabi, H. A. (2015). Entropy Estimation Using Numerical Methods. + Annals of Data Science, 2(2), 231-241. + https://link.springer.com/article/10.1007/s40745-015-0045-9 + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import differential_entropy, norm + + Entropy of a standard normal distribution: + + >>> rng = np.random.default_rng() + >>> values = rng.standard_normal(100) + >>> differential_entropy(values) + 1.3407817436640392 + + Compare with the true entropy: + + >>> float(norm.entropy()) + 1.4189385332046727 + + For several sample sizes between 5 and 1000, compare the accuracy of + the ``'vasicek'``, ``'van es'``, and ``'ebrahimi'`` methods. Specifically, + compare the root mean squared error (over 1000 trials) between the estimate + and the true differential entropy of the distribution. + + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> + >>> + >>> def rmse(res, expected): + ... '''Root mean squared error''' + ... return np.sqrt(np.mean((res - expected)**2)) + >>> + >>> + >>> a, b = np.log10(5), np.log10(1000) + >>> ns = np.round(np.logspace(a, b, 10)).astype(int) + >>> reps = 1000 # number of repetitions for each sample size + >>> expected = stats.expon.entropy() + >>> + >>> method_errors = {'vasicek': [], 'van es': [], 'ebrahimi': []} + >>> for method in method_errors: + ... for n in ns: + ... rvs = stats.expon.rvs(size=(reps, n), random_state=rng) + ... res = stats.differential_entropy(rvs, method=method, axis=-1) + ... error = rmse(res, expected) + ... method_errors[method].append(error) + >>> + >>> for method, errors in method_errors.items(): + ... plt.loglog(ns, errors, label=method) + >>> + >>> plt.legend() + >>> plt.xlabel('sample size') + >>> plt.ylabel('RMSE (1000 trials)') + >>> plt.title('Entropy Estimator Error (Exponential Distribution)') + + """ + values = np.asarray(values) + values = np.moveaxis(values, axis, -1) + n = values.shape[-1] # number of observations + + if window_length is None: + window_length = math.floor(math.sqrt(n) + 0.5) + + if not 2 <= 2 * window_length < n: + raise ValueError( + f"Window length ({window_length}) must be positive and less " + f"than half the sample size ({n}).", + ) + + if base is not None and base <= 0: + raise ValueError("`base` must be a positive number or `None`.") + + sorted_data = np.sort(values, axis=-1) + + methods = {"vasicek": _vasicek_entropy, + "van es": _van_es_entropy, + "correa": _correa_entropy, + "ebrahimi": _ebrahimi_entropy, + "auto": _vasicek_entropy} + method = method.lower() + if method not in methods: + message = f"`method` must be one of {set(methods)}" + raise ValueError(message) + + if method == "auto": + if n <= 10: + method = 'van es' + elif n <= 1000: + method = 'ebrahimi' + else: + method = 'vasicek' + + res = methods[method](sorted_data, window_length) + + if base is not None: + res /= np.log(base) + + return res + + +def _pad_along_last_axis(X, m): + """Pad the data for computing the rolling window difference.""" + # scales a bit better than method in _vasicek_like_entropy + shape = np.array(X.shape) + shape[-1] = m + Xl = np.broadcast_to(X[..., [0]], shape) # [0] vs 0 to maintain shape + Xr = np.broadcast_to(X[..., [-1]], shape) + return np.concatenate((Xl, X, Xr), axis=-1) + + +def _vasicek_entropy(X, m): + """Compute the Vasicek estimator as described in [6] Eq. 1.3.""" + n = X.shape[-1] + X = _pad_along_last_axis(X, m) + differences = X[..., 2 * m:] - X[..., : -2 * m:] + logs = np.log(n/(2*m) * differences) + return np.mean(logs, axis=-1) + + +def _van_es_entropy(X, m): + """Compute the van Es estimator as described in [6].""" + # No equation number, but referred to as HVE_mn. + # Typo: there should be a log within the summation. + n = X.shape[-1] + difference = X[..., m:] - X[..., :-m] + term1 = 1/(n-m) * np.sum(np.log((n+1)/m * difference), axis=-1) + k = np.arange(m, n+1) + return term1 + np.sum(1/k) + np.log(m) - np.log(n+1) + + +def _ebrahimi_entropy(X, m): + """Compute the Ebrahimi estimator as described in [6].""" + # No equation number, but referred to as HE_mn + n = X.shape[-1] + X = _pad_along_last_axis(X, m) + + differences = X[..., 2 * m:] - X[..., : -2 * m:] + + i = np.arange(1, n+1).astype(float) + ci = np.ones_like(i)*2 + ci[i <= m] = 1 + (i[i <= m] - 1)/m + ci[i >= n - m + 1] = 1 + (n - i[i >= n-m+1])/m + + logs = np.log(n * differences / (ci * m)) + return np.mean(logs, axis=-1) + + +def _correa_entropy(X, m): + """Compute the Correa estimator as described in [6].""" + # No equation number, but referred to as HC_mn + n = X.shape[-1] + X = _pad_along_last_axis(X, m) + + i = np.arange(1, n+1) + dj = np.arange(-m, m+1)[:, None] + j = i + dj + j0 = j + m - 1 # 0-indexed version of j + + Xibar = np.mean(X[..., j0], axis=-2, keepdims=True) + difference = X[..., j0] - Xibar + num = np.sum(difference*dj, axis=-2) # dj is d-i + den = n*np.sum(difference**2, axis=-2) + return -np.mean(np.log(num/den), axis=-1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_fit.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_fit.py new file mode 100644 index 0000000000000000000000000000000000000000..b23e33d74a2c422c91f8eb275af286f004b61b5e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_fit.py @@ -0,0 +1,1351 @@ +import warnings +from collections import namedtuple +import numpy as np +from scipy import optimize, stats +from scipy._lib._util import check_random_state + + +def _combine_bounds(name, user_bounds, shape_domain, integral): + """Intersection of user-defined bounds and distribution PDF/PMF domain""" + + user_bounds = np.atleast_1d(user_bounds) + + if user_bounds[0] > user_bounds[1]: + message = (f"There are no values for `{name}` on the interval " + f"{list(user_bounds)}.") + raise ValueError(message) + + bounds = (max(user_bounds[0], shape_domain[0]), + min(user_bounds[1], shape_domain[1])) + + if integral and (np.ceil(bounds[0]) > np.floor(bounds[1])): + message = (f"There are no integer values for `{name}` on the interval " + f"defined by the user-provided bounds and the domain " + "of the distribution.") + raise ValueError(message) + elif not integral and (bounds[0] > bounds[1]): + message = (f"There are no values for `{name}` on the interval " + f"defined by the user-provided bounds and the domain " + "of the distribution.") + raise ValueError(message) + + if not np.all(np.isfinite(bounds)): + message = (f"The intersection of user-provided bounds for `{name}` " + f"and the domain of the distribution is not finite. Please " + f"provide finite bounds for shape `{name}` in `bounds`.") + raise ValueError(message) + + return bounds + + +class FitResult: + r"""Result of fitting a discrete or continuous distribution to data + + Attributes + ---------- + params : namedtuple + A namedtuple containing the maximum likelihood estimates of the + shape parameters, location, and (if applicable) scale of the + distribution. + success : bool or None + Whether the optimizer considered the optimization to terminate + successfully or not. + message : str or None + Any status message provided by the optimizer. + + """ + + def __init__(self, dist, data, discrete, res): + self._dist = dist + self._data = data + self.discrete = discrete + self.pxf = getattr(dist, "pmf", None) or getattr(dist, "pdf", None) + + shape_names = [] if dist.shapes is None else dist.shapes.split(", ") + if not discrete: + FitParams = namedtuple('FitParams', shape_names + ['loc', 'scale']) + else: + FitParams = namedtuple('FitParams', shape_names + ['loc']) + + self.params = FitParams(*res.x) + + # Optimizer can report success even when nllf is infinite + if res.success and not np.isfinite(self.nllf()): + res.success = False + res.message = ("Optimization converged to parameter values that " + "are inconsistent with the data.") + self.success = getattr(res, "success", None) + self.message = getattr(res, "message", None) + + def __repr__(self): + keys = ["params", "success", "message"] + m = max(map(len, keys)) + 1 + return '\n'.join([key.rjust(m) + ': ' + repr(getattr(self, key)) + for key in keys if getattr(self, key) is not None]) + + def nllf(self, params=None, data=None): + """Negative log-likelihood function + + Evaluates the negative of the log-likelihood function of the provided + data at the provided parameters. + + Parameters + ---------- + params : tuple, optional + The shape parameters, location, and (if applicable) scale of the + distribution as a single tuple. Default is the maximum likelihood + estimates (``self.params``). + data : array_like, optional + The data for which the log-likelihood function is to be evaluated. + Default is the data to which the distribution was fit. + + Returns + ------- + nllf : float + The negative of the log-likelihood function. + + """ + params = params if params is not None else self.params + data = data if data is not None else self._data + return self._dist.nnlf(theta=params, x=data) + + def plot(self, ax=None, *, plot_type="hist"): + """Visually compare the data against the fitted distribution. + + Available only if `matplotlib` is installed. + + Parameters + ---------- + ax : `matplotlib.axes.Axes` + Axes object to draw the plot onto, otherwise uses the current Axes. + plot_type : {"hist", "qq", "pp", "cdf"} + Type of plot to draw. Options include: + + - "hist": Superposes the PDF/PMF of the fitted distribution + over a normalized histogram of the data. + - "qq": Scatter plot of theoretical quantiles against the + empirical quantiles. Specifically, the x-coordinates are the + values of the fitted distribution PPF evaluated at the + percentiles ``(np.arange(1, n) - 0.5)/n``, where ``n`` is the + number of data points, and the y-coordinates are the sorted + data points. + - "pp": Scatter plot of theoretical percentiles against the + observed percentiles. Specifically, the x-coordinates are the + percentiles ``(np.arange(1, n) - 0.5)/n``, where ``n`` is + the number of data points, and the y-coordinates are the values + of the fitted distribution CDF evaluated at the sorted + data points. + - "cdf": Superposes the CDF of the fitted distribution over the + empirical CDF. Specifically, the x-coordinates of the empirical + CDF are the sorted data points, and the y-coordinates are the + percentiles ``(np.arange(1, n) - 0.5)/n``, where ``n`` is + the number of data points. + + Returns + ------- + ax : `matplotlib.axes.Axes` + The matplotlib Axes object on which the plot was drawn. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt # matplotlib must be installed + >>> rng = np.random.default_rng() + >>> data = stats.nbinom(5, 0.5).rvs(size=1000, random_state=rng) + >>> bounds = [(0, 30), (0, 1)] + >>> res = stats.fit(stats.nbinom, data, bounds) + >>> ax = res.plot() # save matplotlib Axes object + + The `matplotlib.axes.Axes` object can be used to customize the plot. + See `matplotlib.axes.Axes` documentation for details. + + >>> ax.set_xlabel('number of trials') # customize axis label + >>> ax.get_children()[0].set_linewidth(5) # customize line widths + >>> ax.legend() + >>> plt.show() + """ + try: + import matplotlib # noqa: F401 + except ModuleNotFoundError as exc: + message = "matplotlib must be installed to use method `plot`." + raise ModuleNotFoundError(message) from exc + + plots = {'histogram': self._hist_plot, 'qq': self._qq_plot, + 'pp': self._pp_plot, 'cdf': self._cdf_plot, + 'hist': self._hist_plot} + if plot_type.lower() not in plots: + message = f"`plot_type` must be one of {set(plots.keys())}" + raise ValueError(message) + plot = plots[plot_type.lower()] + + if ax is None: + import matplotlib.pyplot as plt + ax = plt.gca() + + fit_params = np.atleast_1d(self.params) + + return plot(ax=ax, fit_params=fit_params) + + def _hist_plot(self, ax, fit_params): + from matplotlib.ticker import MaxNLocator + + support = self._dist.support(*fit_params) + lb = support[0] if np.isfinite(support[0]) else min(self._data) + ub = support[1] if np.isfinite(support[1]) else max(self._data) + pxf = "PMF" if self.discrete else "PDF" + + if self.discrete: + x = np.arange(lb, ub + 2) + y = self.pxf(x, *fit_params) + ax.vlines(x[:-1], 0, y[:-1], label='Fitted Distribution PMF', + color='C0') + options = dict(density=True, bins=x, align='left', color='C1') + ax.xaxis.set_major_locator(MaxNLocator(integer=True)) + ax.set_xlabel('k') + ax.set_ylabel('PMF') + else: + x = np.linspace(lb, ub, 200) + y = self.pxf(x, *fit_params) + ax.plot(x, y, '--', label='Fitted Distribution PDF', color='C0') + options = dict(density=True, bins=50, align='mid', color='C1') + ax.set_xlabel('x') + ax.set_ylabel('PDF') + + if len(self._data) > 50 or self.discrete: + ax.hist(self._data, label="Histogram of Data", **options) + else: + ax.plot(self._data, np.zeros_like(self._data), "*", + label='Data', color='C1') + + ax.set_title(rf"Fitted $\tt {self._dist.name}$ {pxf} and Histogram") + ax.legend(*ax.get_legend_handles_labels()) + return ax + + def _qp_plot(self, ax, fit_params, qq): + data = np.sort(self._data) + ps = self._plotting_positions(len(self._data)) + + if qq: + qp = "Quantiles" + plot_type = 'Q-Q' + x = self._dist.ppf(ps, *fit_params) + y = data + else: + qp = "Percentiles" + plot_type = 'P-P' + x = ps + y = self._dist.cdf(data, *fit_params) + + ax.plot(x, y, '.', label=f'Fitted Distribution {plot_type}', + color='C0', zorder=1) + xlim = ax.get_xlim() + ylim = ax.get_ylim() + lim = [min(xlim[0], ylim[0]), max(xlim[1], ylim[1])] + if not qq: + lim = max(lim[0], 0), min(lim[1], 1) + + if self.discrete and qq: + q_min, q_max = int(lim[0]), int(lim[1]+1) + q_ideal = np.arange(q_min, q_max) + # q_ideal = np.unique(self._dist.ppf(ps, *fit_params)) + ax.plot(q_ideal, q_ideal, 'o', label='Reference', color='k', + alpha=0.25, markerfacecolor='none', clip_on=True) + elif self.discrete and not qq: + # The intent of this is to match the plot that would be produced + # if x were continuous on [0, 1] and y were cdf(ppf(x)). + # It can be approximated by letting x = np.linspace(0, 1, 1000), + # but this might not look great when zooming in. The vertical + # portions are included to indicate where the transition occurs + # where the data completely obscures the horizontal portions. + p_min, p_max = lim + a, b = self._dist.support(*fit_params) + p_min = max(p_min, 0 if np.isfinite(a) else 1e-3) + p_max = min(p_max, 1 if np.isfinite(b) else 1-1e-3) + q_min, q_max = self._dist.ppf([p_min, p_max], *fit_params) + qs = np.arange(q_min-1, q_max+1) + ps = self._dist.cdf(qs, *fit_params) + ax.step(ps, ps, '-', label='Reference', color='k', alpha=0.25, + clip_on=True) + else: + ax.plot(lim, lim, '-', label='Reference', color='k', alpha=0.25, + clip_on=True) + + ax.set_xlim(lim) + ax.set_ylim(lim) + ax.set_xlabel(rf"Fitted $\tt {self._dist.name}$ Theoretical {qp}") + ax.set_ylabel(f"Data {qp}") + ax.set_title(rf"Fitted $\tt {self._dist.name}$ {plot_type} Plot") + ax.legend(*ax.get_legend_handles_labels()) + ax.set_aspect('equal') + return ax + + def _qq_plot(self, **kwargs): + return self._qp_plot(qq=True, **kwargs) + + def _pp_plot(self, **kwargs): + return self._qp_plot(qq=False, **kwargs) + + def _plotting_positions(self, n, a=.5): + # See https://en.wikipedia.org/wiki/Q%E2%80%93Q_plot#Plotting_positions + k = np.arange(1, n+1) + return (k-a) / (n + 1 - 2*a) + + def _cdf_plot(self, ax, fit_params): + data = np.sort(self._data) + ecdf = self._plotting_positions(len(self._data)) + ls = '--' if len(np.unique(data)) < 30 else '.' + xlabel = 'k' if self.discrete else 'x' + ax.step(data, ecdf, ls, label='Empirical CDF', color='C1', zorder=0) + + xlim = ax.get_xlim() + q = np.linspace(*xlim, 300) + tcdf = self._dist.cdf(q, *fit_params) + + ax.plot(q, tcdf, label='Fitted Distribution CDF', color='C0', zorder=1) + ax.set_xlim(xlim) + ax.set_ylim(0, 1) + ax.set_xlabel(xlabel) + ax.set_ylabel("CDF") + ax.set_title(rf"Fitted $\tt {self._dist.name}$ and Empirical CDF") + handles, labels = ax.get_legend_handles_labels() + ax.legend(handles[::-1], labels[::-1]) + return ax + + +def fit(dist, data, bounds=None, *, guess=None, method='mle', + optimizer=optimize.differential_evolution): + r"""Fit a discrete or continuous distribution to data + + Given a distribution, data, and bounds on the parameters of the + distribution, return maximum likelihood estimates of the parameters. + + Parameters + ---------- + dist : `scipy.stats.rv_continuous` or `scipy.stats.rv_discrete` + The object representing the distribution to be fit to the data. + data : 1D array_like + The data to which the distribution is to be fit. If the data contain + any of ``np.nan``, ``np.inf``, or -``np.inf``, the fit method will + raise a ``ValueError``. + bounds : dict or sequence of tuples, optional + If a dictionary, each key is the name of a parameter of the + distribution, and the corresponding value is a tuple containing the + lower and upper bound on that parameter. If the distribution is + defined only for a finite range of values of that parameter, no entry + for that parameter is required; e.g., some distributions have + parameters which must be on the interval [0, 1]. Bounds for parameters + location (``loc``) and scale (``scale``) are optional; by default, + they are fixed to 0 and 1, respectively. + + If a sequence, element *i* is a tuple containing the lower and upper + bound on the *i*\ th parameter of the distribution. In this case, + bounds for *all* distribution shape parameters must be provided. + Optionally, bounds for location and scale may follow the + distribution shape parameters. + + If a shape is to be held fixed (e.g. if it is known), the + lower and upper bounds may be equal. If a user-provided lower or upper + bound is beyond a bound of the domain for which the distribution is + defined, the bound of the distribution's domain will replace the + user-provided value. Similarly, parameters which must be integral + will be constrained to integral values within the user-provided bounds. + guess : dict or array_like, optional + If a dictionary, each key is the name of a parameter of the + distribution, and the corresponding value is a guess for the value + of the parameter. + + If a sequence, element *i* is a guess for the *i*\ th parameter of the + distribution. In this case, guesses for *all* distribution shape + parameters must be provided. + + If `guess` is not provided, guesses for the decision variables will + not be passed to the optimizer. If `guess` is provided, guesses for + any missing parameters will be set at the mean of the lower and + upper bounds. Guesses for parameters which must be integral will be + rounded to integral values, and guesses that lie outside the + intersection of the user-provided bounds and the domain of the + distribution will be clipped. + method : {'mle', 'mse'} + With ``method="mle"`` (default), the fit is computed by minimizing + the negative log-likelihood function. A large, finite penalty + (rather than infinite negative log-likelihood) is applied for + observations beyond the support of the distribution. + With ``method="mse"``, the fit is computed by minimizing + the negative log-product spacing function. The same penalty is applied + for observations beyond the support. We follow the approach of [1]_, + which is generalized for samples with repeated observations. + optimizer : callable, optional + `optimizer` is a callable that accepts the following positional + argument. + + fun : callable + The objective function to be optimized. `fun` accepts one argument + ``x``, candidate shape parameters of the distribution, and returns + the objective function value given ``x``, `dist`, and the provided + `data`. + The job of `optimizer` is to find values of the decision variables + that minimizes `fun`. + + `optimizer` must also accept the following keyword argument. + + bounds : sequence of tuples + The bounds on values of the decision variables; each element will + be a tuple containing the lower and upper bound on a decision + variable. + + If `guess` is provided, `optimizer` must also accept the following + keyword argument. + + x0 : array_like + The guesses for each decision variable. + + If the distribution has any shape parameters that must be integral or + if the distribution is discrete and the location parameter is not + fixed, `optimizer` must also accept the following keyword argument. + + integrality : array_like of bools + For each decision variable, True if the decision variable + must be constrained to integer values and False if the decision + variable is continuous. + + `optimizer` must return an object, such as an instance of + `scipy.optimize.OptimizeResult`, which holds the optimal values of + the decision variables in an attribute ``x``. If attributes + ``fun``, ``status``, or ``message`` are provided, they will be + included in the result object returned by `fit`. + + Returns + ------- + result : `~scipy.stats._result_classes.FitResult` + An object with the following fields. + + params : namedtuple + A namedtuple containing the maximum likelihood estimates of the + shape parameters, location, and (if applicable) scale of the + distribution. + success : bool or None + Whether the optimizer considered the optimization to terminate + successfully or not. + message : str or None + Any status message provided by the optimizer. + + The object has the following method: + + nllf(params=None, data=None) + By default, the negative log-likehood function at the fitted + `params` for the given `data`. Accepts a tuple containing + alternative shapes, location, and scale of the distribution and + an array of alternative data. + + plot(ax=None) + Superposes the PDF/PMF of the fitted distribution over a normalized + histogram of the data. + + See Also + -------- + rv_continuous, rv_discrete + + Notes + ----- + Optimization is more likely to converge to the maximum likelihood estimate + when the user provides tight bounds containing the maximum likelihood + estimate. For example, when fitting a binomial distribution to data, the + number of experiments underlying each sample may be known, in which case + the corresponding shape parameter ``n`` can be fixed. + + References + ---------- + .. [1] Shao, Yongzhao, and Marjorie G. Hahn. "Maximum product of spacings + method: a unified formulation with illustration of strong + consistency." Illinois Journal of Mathematics 43.3 (1999): 489-499. + + Examples + -------- + Suppose we wish to fit a distribution to the following data. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> dist = stats.nbinom + >>> shapes = (5, 0.5) + >>> data = dist.rvs(*shapes, size=1000, random_state=rng) + + Suppose we do not know how the data were generated, but we suspect that + it follows a negative binomial distribution with parameters *n* and *p*\. + (See `scipy.stats.nbinom`.) We believe that the parameter *n* was fewer + than 30, and we know that the parameter *p* must lie on the interval + [0, 1]. We record this information in a variable `bounds` and pass + this information to `fit`. + + >>> bounds = [(0, 30), (0, 1)] + >>> res = stats.fit(dist, data, bounds) + + `fit` searches within the user-specified `bounds` for the + values that best match the data (in the sense of maximum likelihood + estimation). In this case, it found shape values similar to those + from which the data were actually generated. + + >>> res.params + FitParams(n=5.0, p=0.5028157644634368, loc=0.0) # may vary + + We can visualize the results by superposing the probability mass function + of the distribution (with the shapes fit to the data) over a normalized + histogram of the data. + + >>> import matplotlib.pyplot as plt # matplotlib must be installed to plot + >>> res.plot() + >>> plt.show() + + Note that the estimate for *n* was exactly integral; this is because + the domain of the `nbinom` PMF includes only integral *n*, and the `nbinom` + object "knows" that. `nbinom` also knows that the shape *p* must be a + value between 0 and 1. In such a case - when the domain of the distribution + with respect to a parameter is finite - we are not required to specify + bounds for the parameter. + + >>> bounds = {'n': (0, 30)} # omit parameter p using a `dict` + >>> res2 = stats.fit(dist, data, bounds) + >>> res2.params + FitParams(n=5.0, p=0.5016492009232932, loc=0.0) # may vary + + If we wish to force the distribution to be fit with *n* fixed at 6, we can + set both the lower and upper bounds on *n* to 6. Note, however, that the + value of the objective function being optimized is typically worse (higher) + in this case. + + >>> bounds = {'n': (6, 6)} # fix parameter `n` + >>> res3 = stats.fit(dist, data, bounds) + >>> res3.params + FitParams(n=6.0, p=0.5486556076755706, loc=0.0) # may vary + >>> res3.nllf() > res.nllf() + True # may vary + + Note that the numerical results of the previous examples are typical, but + they may vary because the default optimizer used by `fit`, + `scipy.optimize.differential_evolution`, is stochastic. However, we can + customize the settings used by the optimizer to ensure reproducibility - + or even use a different optimizer entirely - using the `optimizer` + parameter. + + >>> from scipy.optimize import differential_evolution + >>> rng = np.random.default_rng(767585560716548) + >>> def optimizer(fun, bounds, *, integrality): + ... return differential_evolution(fun, bounds, strategy='best2bin', + ... seed=rng, integrality=integrality) + >>> bounds = [(0, 30), (0, 1)] + >>> res4 = stats.fit(dist, data, bounds, optimizer=optimizer) + >>> res4.params + FitParams(n=5.0, p=0.5015183149259951, loc=0.0) + + """ + # --- Input Validation / Standardization --- # + user_bounds = bounds + user_guess = guess + + # distribution input validation and information collection + if hasattr(dist, "pdf"): # can't use isinstance for types + default_bounds = {'loc': (0, 0), 'scale': (1, 1)} + discrete = False + elif hasattr(dist, "pmf"): + default_bounds = {'loc': (0, 0)} + discrete = True + else: + message = ("`dist` must be an instance of `rv_continuous` " + "or `rv_discrete.`") + raise ValueError(message) + + try: + param_info = dist._param_info() + except AttributeError as e: + message = (f"Distribution `{dist.name}` is not yet supported by " + "`scipy.stats.fit` because shape information has " + "not been defined.") + raise ValueError(message) from e + + # data input validation + data = np.asarray(data) + if data.ndim != 1: + message = "`data` must be exactly one-dimensional." + raise ValueError(message) + if not (np.issubdtype(data.dtype, np.number) + and np.all(np.isfinite(data))): + message = "All elements of `data` must be finite numbers." + raise ValueError(message) + + # bounds input validation and information collection + n_params = len(param_info) + n_shapes = n_params - (1 if discrete else 2) + param_list = [param.name for param in param_info] + param_names = ", ".join(param_list) + shape_names = ", ".join(param_list[:n_shapes]) + + if user_bounds is None: + user_bounds = {} + + if isinstance(user_bounds, dict): + default_bounds.update(user_bounds) + user_bounds = default_bounds + user_bounds_array = np.empty((n_params, 2)) + for i in range(n_params): + param_name = param_info[i].name + user_bound = user_bounds.pop(param_name, None) + if user_bound is None: + user_bound = param_info[i].domain + user_bounds_array[i] = user_bound + if user_bounds: + message = ("Bounds provided for the following unrecognized " + f"parameters will be ignored: {set(user_bounds)}") + warnings.warn(message, RuntimeWarning, stacklevel=2) + + else: + try: + user_bounds = np.asarray(user_bounds, dtype=float) + if user_bounds.size == 0: + user_bounds = np.empty((0, 2)) + except ValueError as e: + message = ("Each element of a `bounds` sequence must be a tuple " + "containing two elements: the lower and upper bound of " + "a distribution parameter.") + raise ValueError(message) from e + if (user_bounds.ndim != 2 or user_bounds.shape[1] != 2): + message = ("Each element of `bounds` must be a tuple specifying " + "the lower and upper bounds of a shape parameter") + raise ValueError(message) + if user_bounds.shape[0] < n_shapes: + message = (f"A `bounds` sequence must contain at least {n_shapes} " + "elements: tuples specifying the lower and upper " + f"bounds of all shape parameters {shape_names}.") + raise ValueError(message) + if user_bounds.shape[0] > n_params: + message = ("A `bounds` sequence may not contain more than " + f"{n_params} elements: tuples specifying the lower and " + "upper bounds of distribution parameters " + f"{param_names}.") + raise ValueError(message) + + user_bounds_array = np.empty((n_params, 2)) + user_bounds_array[n_shapes:] = list(default_bounds.values()) + user_bounds_array[:len(user_bounds)] = user_bounds + + user_bounds = user_bounds_array + validated_bounds = [] + for i in range(n_params): + name = param_info[i].name + user_bound = user_bounds_array[i] + param_domain = param_info[i].domain + integral = param_info[i].integrality + combined = _combine_bounds(name, user_bound, param_domain, integral) + validated_bounds.append(combined) + + bounds = np.asarray(validated_bounds) + integrality = [param.integrality for param in param_info] + + # guess input validation + + if user_guess is None: + guess_array = None + elif isinstance(user_guess, dict): + default_guess = {param.name: np.mean(bound) + for param, bound in zip(param_info, bounds)} + unrecognized = set(user_guess) - set(default_guess) + if unrecognized: + message = ("Guesses provided for the following unrecognized " + f"parameters will be ignored: {unrecognized}") + warnings.warn(message, RuntimeWarning, stacklevel=2) + default_guess.update(user_guess) + + message = ("Each element of `guess` must be a scalar " + "guess for a distribution parameter.") + try: + guess_array = np.asarray([default_guess[param.name] + for param in param_info], dtype=float) + except ValueError as e: + raise ValueError(message) from e + + else: + message = ("Each element of `guess` must be a scalar " + "guess for a distribution parameter.") + try: + user_guess = np.asarray(user_guess, dtype=float) + except ValueError as e: + raise ValueError(message) from e + if user_guess.ndim != 1: + raise ValueError(message) + if user_guess.shape[0] < n_shapes: + message = (f"A `guess` sequence must contain at least {n_shapes} " + "elements: scalar guesses for the distribution shape " + f"parameters {shape_names}.") + raise ValueError(message) + if user_guess.shape[0] > n_params: + message = ("A `guess` sequence may not contain more than " + f"{n_params} elements: scalar guesses for the " + f"distribution parameters {param_names}.") + raise ValueError(message) + + guess_array = np.mean(bounds, axis=1) + guess_array[:len(user_guess)] = user_guess + + if guess_array is not None: + guess_rounded = guess_array.copy() + + guess_rounded[integrality] = np.round(guess_rounded[integrality]) + rounded = np.where(guess_rounded != guess_array)[0] + for i in rounded: + message = (f"Guess for parameter `{param_info[i].name}` " + f"rounded from {guess_array[i]} to {guess_rounded[i]}.") + warnings.warn(message, RuntimeWarning, stacklevel=2) + + guess_clipped = np.clip(guess_rounded, bounds[:, 0], bounds[:, 1]) + clipped = np.where(guess_clipped != guess_rounded)[0] + for i in clipped: + message = (f"Guess for parameter `{param_info[i].name}` " + f"clipped from {guess_rounded[i]} to " + f"{guess_clipped[i]}.") + warnings.warn(message, RuntimeWarning, stacklevel=2) + + guess = guess_clipped + else: + guess = None + + # --- Fitting --- # + def nllf(free_params, data=data): # bind data NOW + with np.errstate(invalid='ignore', divide='ignore'): + return dist._penalized_nnlf(free_params, data) + + def nlpsf(free_params, data=data): # bind data NOW + with np.errstate(invalid='ignore', divide='ignore'): + return dist._penalized_nlpsf(free_params, data) + + methods = {'mle': nllf, 'mse': nlpsf} + objective = methods[method.lower()] + + with np.errstate(invalid='ignore', divide='ignore'): + kwds = {} + if bounds is not None: + kwds['bounds'] = bounds + if np.any(integrality): + kwds['integrality'] = integrality + if guess is not None: + kwds['x0'] = guess + res = optimizer(objective, **kwds) + + return FitResult(dist, data, discrete, res) + + +GoodnessOfFitResult = namedtuple('GoodnessOfFitResult', + ('fit_result', 'statistic', 'pvalue', + 'null_distribution')) + + +def goodness_of_fit(dist, data, *, known_params=None, fit_params=None, + guessed_params=None, statistic='ad', n_mc_samples=9999, + random_state=None): + r""" + Perform a goodness of fit test comparing data to a distribution family. + + Given a distribution family and data, perform a test of the null hypothesis + that the data were drawn from a distribution in that family. Any known + parameters of the distribution may be specified. Remaining parameters of + the distribution will be fit to the data, and the p-value of the test + is computed accordingly. Several statistics for comparing the distribution + to data are available. + + Parameters + ---------- + dist : `scipy.stats.rv_continuous` + The object representing the distribution family under the null + hypothesis. + data : 1D array_like + Finite, uncensored data to be tested. + known_params : dict, optional + A dictionary containing name-value pairs of known distribution + parameters. Monte Carlo samples are randomly drawn from the + null-hypothesized distribution with these values of the parameters. + Before the statistic is evaluated for each Monte Carlo sample, only + remaining unknown parameters of the null-hypothesized distribution + family are fit to the samples; the known parameters are held fixed. + If all parameters of the distribution family are known, then the step + of fitting the distribution family to each sample is omitted. + fit_params : dict, optional + A dictionary containing name-value pairs of distribution parameters + that have already been fit to the data, e.g. using `scipy.stats.fit` + or the ``fit`` method of `dist`. Monte Carlo samples are drawn from the + null-hypothesized distribution with these specified values of the + parameter. On those Monte Carlo samples, however, these and all other + unknown parameters of the null-hypothesized distribution family are + fit before the statistic is evaluated. + guessed_params : dict, optional + A dictionary containing name-value pairs of distribution parameters + which have been guessed. These parameters are always considered as + free parameters and are fit both to the provided `data` as well as + to the Monte Carlo samples drawn from the null-hypothesized + distribution. The purpose of these `guessed_params` is to be used as + initial values for the numerical fitting procedure. + statistic : {"ad", "ks", "cvm", "filliben"} or callable, optional + The statistic used to compare data to a distribution after fitting + unknown parameters of the distribution family to the data. The + Anderson-Darling ("ad") [1]_, Kolmogorov-Smirnov ("ks") [1]_, + Cramer-von Mises ("cvm") [1]_, and Filliben ("filliben") [7]_ + statistics are available. Alternatively, a callable with signature + ``(dist, data, axis)`` may be supplied to compute the statistic. Here + ``dist`` is a frozen distribution object (potentially with array + parameters), ``data`` is an array of Monte Carlo samples (of + compatible shape), and ``axis`` is the axis of ``data`` along which + the statistic must be computed. + n_mc_samples : int, default: 9999 + The number of Monte Carlo samples drawn from the null hypothesized + distribution to form the null distribution of the statistic. The + sample size of each is the same as the given `data`. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate the Monte Carlo + samples. + + If `random_state` is ``None`` (default), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, then the provided instance is used. + + Returns + ------- + res : GoodnessOfFitResult + An object with the following attributes. + + fit_result : `~scipy.stats._result_classes.FitResult` + An object representing the fit of the provided `dist` to `data`. + This object includes the values of distribution family parameters + that fully define the null-hypothesized distribution, that is, + the distribution from which Monte Carlo samples are drawn. + statistic : float + The value of the statistic comparing provided `data` to the + null-hypothesized distribution. + pvalue : float + The proportion of elements in the null distribution with + statistic values at least as extreme as the statistic value of the + provided `data`. + null_distribution : ndarray + The value of the statistic for each Monte Carlo sample + drawn from the null-hypothesized distribution. + + Notes + ----- + This is a generalized Monte Carlo goodness-of-fit procedure, special cases + of which correspond with various Anderson-Darling tests, Lilliefors' test, + etc. The test is described in [2]_, [3]_, and [4]_ as a parametric + bootstrap test. This is a Monte Carlo test in which parameters that + specify the distribution from which samples are drawn have been estimated + from the data. We describe the test using "Monte Carlo" rather than + "parametric bootstrap" throughout to avoid confusion with the more familiar + nonparametric bootstrap, and describe how the test is performed below. + + *Traditional goodness of fit tests* + + Traditionally, critical values corresponding with a fixed set of + significance levels are pre-calculated using Monte Carlo methods. Users + perform the test by calculating the value of the test statistic only for + their observed `data` and comparing this value to tabulated critical + values. This practice is not very flexible, as tables are not available for + all distributions and combinations of known and unknown parameter values. + Also, results can be inaccurate when critical values are interpolated from + limited tabulated data to correspond with the user's sample size and + fitted parameter values. To overcome these shortcomings, this function + allows the user to perform the Monte Carlo trials adapted to their + particular data. + + *Algorithmic overview* + + In brief, this routine executes the following steps: + + 1. Fit unknown parameters to the given `data`, thereby forming the + "null-hypothesized" distribution, and compute the statistic of + this pair of data and distribution. + 2. Draw random samples from this null-hypothesized distribution. + 3. Fit the unknown parameters to each random sample. + 4. Calculate the statistic between each sample and the distribution that + has been fit to the sample. + 5. Compare the value of the statistic corresponding with `data` from (1) + against the values of the statistic corresponding with the random + samples from (4). The p-value is the proportion of samples with a + statistic value greater than or equal to the statistic of the observed + data. + + In more detail, the steps are as follows. + + First, any unknown parameters of the distribution family specified by + `dist` are fit to the provided `data` using maximum likelihood estimation. + (One exception is the normal distribution with unknown location and scale: + we use the bias-corrected standard deviation ``np.std(data, ddof=1)`` for + the scale as recommended in [1]_.) + These values of the parameters specify a particular member of the + distribution family referred to as the "null-hypothesized distribution", + that is, the distribution from which the data were sampled under the null + hypothesis. The `statistic`, which compares data to a distribution, is + computed between `data` and the null-hypothesized distribution. + + Next, many (specifically `n_mc_samples`) new samples, each containing the + same number of observations as `data`, are drawn from the + null-hypothesized distribution. All unknown parameters of the distribution + family `dist` are fit to *each resample*, and the `statistic` is computed + between each sample and its corresponding fitted distribution. These + values of the statistic form the Monte Carlo null distribution (not to be + confused with the "null-hypothesized distribution" above). + + The p-value of the test is the proportion of statistic values in the Monte + Carlo null distribution that are at least as extreme as the statistic value + of the provided `data`. More precisely, the p-value is given by + + .. math:: + + p = \frac{b + 1} + {m + 1} + + where :math:`b` is the number of statistic values in the Monte Carlo null + distribution that are greater than or equal to the statistic value + calculated for `data`, and :math:`m` is the number of elements in the + Monte Carlo null distribution (`n_mc_samples`). The addition of :math:`1` + to the numerator and denominator can be thought of as including the + value of the statistic corresponding with `data` in the null distribution, + but a more formal explanation is given in [5]_. + + *Limitations* + + The test can be very slow for some distribution families because unknown + parameters of the distribution family must be fit to each of the Monte + Carlo samples, and for most distributions in SciPy, distribution fitting + performed via numerical optimization. + + *Anti-Pattern* + + For this reason, it may be tempting + to treat parameters of the distribution pre-fit to `data` (by the user) + as though they were `known_params`, as specification of all parameters of + the distribution precludes the need to fit the distribution to each Monte + Carlo sample. (This is essentially how the original Kilmogorov-Smirnov + test is performed.) Although such a test can provide evidence against the + null hypothesis, the test is conservative in the sense that small p-values + will tend to (greatly) *overestimate* the probability of making a type I + error (that is, rejecting the null hypothesis although it is true), and the + power of the test is low (that is, it is less likely to reject the null + hypothesis even when the null hypothesis is false). + This is because the Monte Carlo samples are less likely to agree with the + null-hypothesized distribution as well as `data`. This tends to increase + the values of the statistic recorded in the null distribution, so that a + larger number of them exceed the value of statistic for `data`, thereby + inflating the p-value. + + References + ---------- + .. [1] M. A. Stephens (1974). "EDF Statistics for Goodness of Fit and + Some Comparisons." Journal of the American Statistical Association, + Vol. 69, pp. 730-737. + .. [2] W. Stute, W. G. Manteiga, and M. P. Quindimil (1993). + "Bootstrap based goodness-of-fit-tests." Metrika 40.1: 243-256. + .. [3] C. Genest, & B Rémillard. (2008). "Validity of the parametric + bootstrap for goodness-of-fit testing in semiparametric models." + Annales de l'IHP Probabilités et statistiques. Vol. 44. No. 6. + .. [4] I. Kojadinovic and J. Yan (2012). "Goodness-of-fit testing based on + a weighted bootstrap: A fast large-sample alternative to the + parametric bootstrap." Canadian Journal of Statistics 40.3: 480-500. + .. [5] B. Phipson and G. K. Smyth (2010). "Permutation P-values Should + Never Be Zero: Calculating Exact P-values When Permutations Are + Randomly Drawn." Statistical Applications in Genetics and Molecular + Biology 9.1. + .. [6] H. W. Lilliefors (1967). "On the Kolmogorov-Smirnov test for + normality with mean and variance unknown." Journal of the American + statistical Association 62.318: 399-402. + .. [7] Filliben, James J. "The probability plot correlation coefficient + test for normality." Technometrics 17.1 (1975): 111-117. + + Examples + -------- + A well-known test of the null hypothesis that data were drawn from a + given distribution is the Kolmogorov-Smirnov (KS) test, available in SciPy + as `scipy.stats.ks_1samp`. Suppose we wish to test whether the following + data: + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> x = stats.uniform.rvs(size=75, random_state=rng) + + were sampled from a normal distribution. To perform a KS test, the + empirical distribution function of the observed data will be compared + against the (theoretical) cumulative distribution function of a normal + distribution. Of course, to do this, the normal distribution under the null + hypothesis must be fully specified. This is commonly done by first fitting + the ``loc`` and ``scale`` parameters of the distribution to the observed + data, then performing the test. + + >>> loc, scale = np.mean(x), np.std(x, ddof=1) + >>> cdf = stats.norm(loc, scale).cdf + >>> stats.ks_1samp(x, cdf) + KstestResult(statistic=0.1119257570456813, pvalue=0.2827756409939257) + + An advantage of the KS-test is that the p-value - the probability of + obtaining a value of the test statistic under the null hypothesis as + extreme as the value obtained from the observed data - can be calculated + exactly and efficiently. `goodness_of_fit` can only approximate these + results. + + >>> known_params = {'loc': loc, 'scale': scale} + >>> res = stats.goodness_of_fit(stats.norm, x, known_params=known_params, + ... statistic='ks', random_state=rng) + >>> res.statistic, res.pvalue + (0.1119257570456813, 0.2788) + + The statistic matches exactly, but the p-value is estimated by forming + a "Monte Carlo null distribution", that is, by explicitly drawing random + samples from `scipy.stats.norm` with the provided parameters and + calculating the stastic for each. The fraction of these statistic values + at least as extreme as ``res.statistic`` approximates the exact p-value + calculated by `scipy.stats.ks_1samp`. + + However, in many cases, we would prefer to test only that the data were + sampled from one of *any* member of the normal distribution family, not + specifically from the normal distribution with the location and scale + fitted to the observed sample. In this case, Lilliefors [6]_ argued that + the KS test is far too conservative (that is, the p-value overstates + the actual probability of rejecting a true null hypothesis) and thus lacks + power - the ability to reject the null hypothesis when the null hypothesis + is actually false. + Indeed, our p-value above is approximately 0.28, which is far too large + to reject the null hypothesis at any common significance level. + + Consider why this might be. Note that in the KS test above, the statistic + always compares data against the CDF of a normal distribution fitted to the + *observed data*. This tends to reduce the value of the statistic for the + observed data, but it is "unfair" when computing the statistic for other + samples, such as those we randomly draw to form the Monte Carlo null + distribution. It is easy to correct for this: whenever we compute the KS + statistic of a sample, we use the CDF of a normal distribution fitted + to *that sample*. The null distribution in this case has not been + calculated exactly and is tyically approximated using Monte Carlo methods + as described above. This is where `goodness_of_fit` excels. + + >>> res = stats.goodness_of_fit(stats.norm, x, statistic='ks', + ... random_state=rng) + >>> res.statistic, res.pvalue + (0.1119257570456813, 0.0196) + + Indeed, this p-value is much smaller, and small enough to (correctly) + reject the null hypothesis at common significance levels, including 5% and + 2.5%. + + However, the KS statistic is not very sensitive to all deviations from + normality. The original advantage of the KS statistic was the ability + to compute the null distribution theoretically, but a more sensitive + statistic - resulting in a higher test power - can be used now that we can + approximate the null distribution + computationally. The Anderson-Darling statistic [1]_ tends to be more + sensitive, and critical values of the this statistic have been tabulated + for various significance levels and sample sizes using Monte Carlo methods. + + >>> res = stats.anderson(x, 'norm') + >>> print(res.statistic) + 1.2139573337497467 + >>> print(res.critical_values) + [0.549 0.625 0.75 0.875 1.041] + >>> print(res.significance_level) + [15. 10. 5. 2.5 1. ] + + Here, the observed value of the statistic exceeds the critical value + corresponding with a 1% significance level. This tells us that the p-value + of the observed data is less than 1%, but what is it? We could interpolate + from these (already-interpolated) values, but `goodness_of_fit` can + estimate it directly. + + >>> res = stats.goodness_of_fit(stats.norm, x, statistic='ad', + ... random_state=rng) + >>> res.statistic, res.pvalue + (1.2139573337497467, 0.0034) + + A further advantage is that use of `goodness_of_fit` is not limited to + a particular set of distributions or conditions on which parameters + are known versus which must be estimated from data. Instead, + `goodness_of_fit` can estimate p-values relatively quickly for any + distribution with a sufficiently fast and reliable ``fit`` method. For + instance, here we perform a goodness of fit test using the Cramer-von Mises + statistic against the Rayleigh distribution with known location and unknown + scale. + + >>> rng = np.random.default_rng() + >>> x = stats.chi(df=2.2, loc=0, scale=2).rvs(size=1000, random_state=rng) + >>> res = stats.goodness_of_fit(stats.rayleigh, x, statistic='cvm', + ... known_params={'loc': 0}, random_state=rng) + + This executes fairly quickly, but to check the reliability of the ``fit`` + method, we should inspect the fit result. + + >>> res.fit_result # location is as specified, and scale is reasonable + params: FitParams(loc=0.0, scale=2.1026719844231243) + success: True + message: 'The fit was performed successfully.' + >>> import matplotlib.pyplot as plt # matplotlib must be installed to plot + >>> res.fit_result.plot() + >>> plt.show() + + If the distribution is not fit to the observed data as well as possible, + the test may not control the type I error rate, that is, the chance of + rejecting the null hypothesis even when it is true. + + We should also look for extreme outliers in the null distribution that + may be caused by unreliable fitting. These do not necessarily invalidate + the result, but they tend to reduce the test's power. + + >>> _, ax = plt.subplots() + >>> ax.hist(np.log10(res.null_distribution)) + >>> ax.set_xlabel("log10 of CVM statistic under the null hypothesis") + >>> ax.set_ylabel("Frequency") + >>> ax.set_title("Histogram of the Monte Carlo null distribution") + >>> plt.show() + + This plot seems reassuring. + + If ``fit`` method is working reliably, and if the distribution of the test + statistic is not particularly sensitive to the values of the fitted + parameters, then the p-value provided by `goodness_of_fit` is expected to + be a good approximation. + + >>> res.statistic, res.pvalue + (0.2231991510248692, 0.0525) + + """ + args = _gof_iv(dist, data, known_params, fit_params, guessed_params, + statistic, n_mc_samples, random_state) + (dist, data, fixed_nhd_params, fixed_rfd_params, guessed_nhd_params, + guessed_rfd_params, statistic, n_mc_samples_int, random_state) = args + + # Fit null hypothesis distribution to data + nhd_fit_fun = _get_fit_fun(dist, data, guessed_nhd_params, + fixed_nhd_params) + nhd_vals = nhd_fit_fun(data) + nhd_dist = dist(*nhd_vals) + + def rvs(size): + return nhd_dist.rvs(size=size, random_state=random_state) + + # Define statistic + fit_fun = _get_fit_fun(dist, data, guessed_rfd_params, fixed_rfd_params) + if callable(statistic): + compare_fun = statistic + else: + compare_fun = _compare_dict[statistic] + alternative = getattr(compare_fun, 'alternative', 'greater') + + def statistic_fun(data, axis): + # Make things simple by always working along the last axis. + data = np.moveaxis(data, axis, -1) + rfd_vals = fit_fun(data) + rfd_dist = dist(*rfd_vals) + return compare_fun(rfd_dist, data, axis=-1) + + res = stats.monte_carlo_test(data, rvs, statistic_fun, vectorized=True, + n_resamples=n_mc_samples, axis=-1, + alternative=alternative) + opt_res = optimize.OptimizeResult() + opt_res.success = True + opt_res.message = "The fit was performed successfully." + opt_res.x = nhd_vals + # Only continuous distributions for now, hence discrete=False + # There's no fundamental limitation; it's just that we're not using + # stats.fit, discrete distributions don't have `fit` method, and + # we haven't written any vectorized fit functions for a discrete + # distribution yet. + return GoodnessOfFitResult(FitResult(dist, data, False, opt_res), + res.statistic, res.pvalue, + res.null_distribution) + + +def _get_fit_fun(dist, data, guessed_params, fixed_params): + + shape_names = [] if dist.shapes is None else dist.shapes.split(", ") + param_names = shape_names + ['loc', 'scale'] + fparam_names = ['f'+name for name in param_names] + all_fixed = not set(fparam_names).difference(fixed_params) + guessed_shapes = [guessed_params.pop(x, None) + for x in shape_names if x in guessed_params] + + if all_fixed: + def fit_fun(data): + return [fixed_params[name] for name in fparam_names] + # Define statistic, including fitting distribution to data + elif dist in _fit_funs: + def fit_fun(data): + params = _fit_funs[dist](data, **fixed_params) + params = np.asarray(np.broadcast_arrays(*params)) + if params.ndim > 1: + params = params[..., np.newaxis] + return params + else: + def fit_fun_1d(data): + return dist.fit(data, *guessed_shapes, **guessed_params, + **fixed_params) + + def fit_fun(data): + params = np.apply_along_axis(fit_fun_1d, axis=-1, arr=data) + if params.ndim > 1: + params = params.T[..., np.newaxis] + return params + + return fit_fun + + +# Vectorized fitting functions. These are to accept ND `data` in which each +# row (slice along last axis) is a sample to fit and scalar fixed parameters. +# They return a tuple of shape parameter arrays, each of shape data.shape[:-1]. +def _fit_norm(data, floc=None, fscale=None): + loc = floc + scale = fscale + if loc is None and scale is None: + loc = np.mean(data, axis=-1) + scale = np.std(data, ddof=1, axis=-1) + elif loc is None: + loc = np.mean(data, axis=-1) + elif scale is None: + scale = np.sqrt(((data - loc)**2).mean(axis=-1)) + return loc, scale + + +_fit_funs = {stats.norm: _fit_norm} # type: ignore[attr-defined] + + +# Vectorized goodness of fit statistic functions. These accept a frozen +# distribution object and `data` in which each row (slice along last axis) is +# a sample. + + +def _anderson_darling(dist, data, axis): + x = np.sort(data, axis=-1) + n = data.shape[-1] + i = np.arange(1, n+1) + Si = (2*i - 1)/n * (dist.logcdf(x) + dist.logsf(x[..., ::-1])) + S = np.sum(Si, axis=-1) + return -n - S + + +def _compute_dplus(cdfvals): # adapted from _stats_py before gh-17062 + n = cdfvals.shape[-1] + return (np.arange(1.0, n + 1) / n - cdfvals).max(axis=-1) + + +def _compute_dminus(cdfvals): + n = cdfvals.shape[-1] + return (cdfvals - np.arange(0.0, n)/n).max(axis=-1) + + +def _kolmogorov_smirnov(dist, data, axis): + x = np.sort(data, axis=-1) + cdfvals = dist.cdf(x) + Dplus = _compute_dplus(cdfvals) # always works along last axis + Dminus = _compute_dminus(cdfvals) + return np.maximum(Dplus, Dminus) + + +def _corr(X, M): + # Correlation coefficient r, simplified and vectorized as we need it. + # See [7] Equation (2). Lemma 1/2 are only for distributions symmetric + # about 0. + Xm = X.mean(axis=-1, keepdims=True) + Mm = M.mean(axis=-1, keepdims=True) + num = np.sum((X - Xm) * (M - Mm), axis=-1) + den = np.sqrt(np.sum((X - Xm)**2, axis=-1) * np.sum((M - Mm)**2, axis=-1)) + return num/den + + +def _filliben(dist, data, axis): + # [7] Section 8 # 1 + X = np.sort(data, axis=-1) + + # [7] Section 8 # 2 + n = data.shape[-1] + k = np.arange(1, n+1) + # Filliben used an approximation for the uniform distribution order + # statistic medians. + # m = (k - .3175)/(n + 0.365) + # m[-1] = 0.5**(1/n) + # m[0] = 1 - m[-1] + # We can just as easily use the (theoretically) exact values. See e.g. + # https://en.wikipedia.org/wiki/Order_statistic + # "Order statistics sampled from a uniform distribution" + m = stats.beta(k, n + 1 - k).median() + + # [7] Section 8 # 3 + M = dist.ppf(m) + + # [7] Section 8 # 4 + return _corr(X, M) +_filliben.alternative = 'less' # type: ignore[attr-defined] + + +def _cramer_von_mises(dist, data, axis): + x = np.sort(data, axis=-1) + n = data.shape[-1] + cdfvals = dist.cdf(x) + u = (2*np.arange(1, n+1) - 1)/(2*n) + w = 1 / (12*n) + np.sum((u - cdfvals)**2, axis=-1) + return w + + +_compare_dict = {"ad": _anderson_darling, "ks": _kolmogorov_smirnov, + "cvm": _cramer_von_mises, "filliben": _filliben} + + +def _gof_iv(dist, data, known_params, fit_params, guessed_params, statistic, + n_mc_samples, random_state): + + if not isinstance(dist, stats.rv_continuous): + message = ("`dist` must be a (non-frozen) instance of " + "`stats.rv_continuous`.") + raise TypeError(message) + + data = np.asarray(data, dtype=float) + if not data.ndim == 1: + message = "`data` must be a one-dimensional array of numbers." + raise ValueError(message) + + # Leave validation of these key/value pairs to the `fit` method, + # but collect these into dictionaries that will be used + known_params = known_params or dict() + fit_params = fit_params or dict() + guessed_params = guessed_params or dict() + + known_params_f = {("f"+key): val for key, val in known_params.items()} + fit_params_f = {("f"+key): val for key, val in fit_params.items()} + + # These are the values of parameters of the null distribution family + # with which resamples are drawn + fixed_nhd_params = known_params_f.copy() + fixed_nhd_params.update(fit_params_f) + + # These are fixed when fitting the distribution family to resamples + fixed_rfd_params = known_params_f.copy() + + # These are used as guesses when fitting the distribution family to + # the original data + guessed_nhd_params = guessed_params.copy() + + # These are used as guesses when fitting the distribution family to + # resamples + guessed_rfd_params = fit_params.copy() + guessed_rfd_params.update(guessed_params) + + if not callable(statistic): + statistic = statistic.lower() + statistics = {'ad', 'ks', 'cvm', 'filliben'} + if statistic not in statistics: + message = f"`statistic` must be one of {statistics}." + raise ValueError(message) + + n_mc_samples_int = int(n_mc_samples) + if n_mc_samples_int != n_mc_samples: + message = "`n_mc_samples` must be an integer." + raise TypeError(message) + + random_state = check_random_state(random_state) + + return (dist, data, fixed_nhd_params, fixed_rfd_params, guessed_nhd_params, + guessed_rfd_params, statistic, n_mc_samples_int, random_state) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_generate_pyx.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_generate_pyx.py new file mode 100644 index 0000000000000000000000000000000000000000..a9647b53ca97018e20136b927d6bf71e3251bfd7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_generate_pyx.py @@ -0,0 +1,27 @@ +import pathlib +import subprocess +import sys +import os +import argparse + + +def make_boost(outdir): + # Call code generator inside _boost directory + code_gen = pathlib.Path(__file__).parent / '_boost/include/code_gen.py' + subprocess.run([sys.executable, str(code_gen), '-o', outdir], + check=True) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-o", "--outdir", type=str, + help="Path to the output directory") + args = parser.parse_args() + + if not args.outdir: + raise ValueError("A path to the output directory is required") + else: + # Meson build + srcdir_abs = pathlib.Path(os.path.abspath(os.path.dirname(__file__))) + outdir_abs = pathlib.Path(os.getcwd()) / args.outdir + make_boost(outdir_abs) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_hypotests.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_hypotests.py new file mode 100644 index 0000000000000000000000000000000000000000..ebf8cdb4f971b45e023200337b007d7d8fac0eb4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_hypotests.py @@ -0,0 +1,2021 @@ +from collections import namedtuple +from dataclasses import dataclass +from math import comb +import numpy as np +import warnings +from itertools import combinations +import scipy.stats +from scipy.optimize import shgo +from . import distributions +from ._common import ConfidenceInterval +from ._continuous_distns import chi2, norm +from scipy.special import gamma, kv, gammaln +from scipy.fft import ifft +from ._stats_pythran import _a_ij_Aij_Dij2 +from ._stats_pythran import ( + _concordant_pairs as _P, _discordant_pairs as _Q +) +from ._axis_nan_policy import _axis_nan_policy_factory +from scipy.stats import _stats_py + +__all__ = ['epps_singleton_2samp', 'cramervonmises', 'somersd', + 'barnard_exact', 'boschloo_exact', 'cramervonmises_2samp', + 'tukey_hsd', 'poisson_means_test'] + +Epps_Singleton_2sampResult = namedtuple('Epps_Singleton_2sampResult', + ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(Epps_Singleton_2sampResult, n_samples=2, too_small=4) +def epps_singleton_2samp(x, y, t=(0.4, 0.8)): + """Compute the Epps-Singleton (ES) test statistic. + + Test the null hypothesis that two samples have the same underlying + probability distribution. + + Parameters + ---------- + x, y : array-like + The two samples of observations to be tested. Input must not have more + than one dimension. Samples can have different lengths. + t : array-like, optional + The points (t1, ..., tn) where the empirical characteristic function is + to be evaluated. It should be positive distinct numbers. The default + value (0.4, 0.8) is proposed in [1]_. Input must not have more than + one dimension. + + Returns + ------- + statistic : float + The test statistic. + pvalue : float + The associated p-value based on the asymptotic chi2-distribution. + + See Also + -------- + ks_2samp, anderson_ksamp + + Notes + ----- + Testing whether two samples are generated by the same underlying + distribution is a classical question in statistics. A widely used test is + the Kolmogorov-Smirnov (KS) test which relies on the empirical + distribution function. Epps and Singleton introduce a test based on the + empirical characteristic function in [1]_. + + One advantage of the ES test compared to the KS test is that is does + not assume a continuous distribution. In [1]_, the authors conclude + that the test also has a higher power than the KS test in many + examples. They recommend the use of the ES test for discrete samples as + well as continuous samples with at least 25 observations each, whereas + `anderson_ksamp` is recommended for smaller sample sizes in the + continuous case. + + The p-value is computed from the asymptotic distribution of the test + statistic which follows a `chi2` distribution. If the sample size of both + `x` and `y` is below 25, the small sample correction proposed in [1]_ is + applied to the test statistic. + + The default values of `t` are determined in [1]_ by considering + various distributions and finding good values that lead to a high power + of the test in general. Table III in [1]_ gives the optimal values for + the distributions tested in that study. The values of `t` are scaled by + the semi-interquartile range in the implementation, see [1]_. + + References + ---------- + .. [1] T. W. Epps and K. J. Singleton, "An omnibus test for the two-sample + problem using the empirical characteristic function", Journal of + Statistical Computation and Simulation 26, p. 177--203, 1986. + + .. [2] S. J. Goerg and J. Kaiser, "Nonparametric testing of distributions + - the Epps-Singleton two-sample test using the empirical characteristic + function", The Stata Journal 9(3), p. 454--465, 2009. + + """ + # x and y are converted to arrays by the decorator + t = np.asarray(t) + # check if x and y are valid inputs + nx, ny = len(x), len(y) + if (nx < 5) or (ny < 5): + raise ValueError('x and y should have at least 5 elements, but len(x) ' + f'= {nx} and len(y) = {ny}.') + if not np.isfinite(x).all(): + raise ValueError('x must not contain nonfinite values.') + if not np.isfinite(y).all(): + raise ValueError('y must not contain nonfinite values.') + n = nx + ny + + # check if t is valid + if t.ndim > 1: + raise ValueError(f't must be 1d, but t.ndim equals {t.ndim}.') + if np.less_equal(t, 0).any(): + raise ValueError('t must contain positive elements only.') + + # rescale t with semi-iqr as proposed in [1]; import iqr here to avoid + # circular import + from scipy.stats import iqr + sigma = iqr(np.hstack((x, y))) / 2 + ts = np.reshape(t, (-1, 1)) / sigma + + # covariance estimation of ES test + gx = np.vstack((np.cos(ts*x), np.sin(ts*x))).T # shape = (nx, 2*len(t)) + gy = np.vstack((np.cos(ts*y), np.sin(ts*y))).T + cov_x = np.cov(gx.T, bias=True) # the test uses biased cov-estimate + cov_y = np.cov(gy.T, bias=True) + est_cov = (n/nx)*cov_x + (n/ny)*cov_y + est_cov_inv = np.linalg.pinv(est_cov) + r = np.linalg.matrix_rank(est_cov_inv) + if r < 2*len(t): + warnings.warn('Estimated covariance matrix does not have full rank. ' + 'This indicates a bad choice of the input t and the ' + 'test might not be consistent.', # see p. 183 in [1]_ + stacklevel=2) + + # compute test statistic w distributed asympt. as chisquare with df=r + g_diff = np.mean(gx, axis=0) - np.mean(gy, axis=0) + w = n*np.dot(g_diff.T, np.dot(est_cov_inv, g_diff)) + + # apply small-sample correction + if (max(nx, ny) < 25): + corr = 1.0/(1.0 + n**(-0.45) + 10.1*(nx**(-1.7) + ny**(-1.7))) + w = corr * w + + p = chi2.sf(w, r) + + return Epps_Singleton_2sampResult(w, p) + + +def poisson_means_test(k1, n1, k2, n2, *, diff=0, alternative='two-sided'): + r""" + Performs the Poisson means test, AKA the "E-test". + + This is a test of the null hypothesis that the difference between means of + two Poisson distributions is `diff`. The samples are provided as the + number of events `k1` and `k2` observed within measurement intervals + (e.g. of time, space, number of observations) of sizes `n1` and `n2`. + + Parameters + ---------- + k1 : int + Number of events observed from distribution 1. + n1: float + Size of sample from distribution 1. + k2 : int + Number of events observed from distribution 2. + n2 : float + Size of sample from distribution 2. + diff : float, default=0 + The hypothesized difference in means between the distributions + underlying the samples. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the difference between distribution means is not + equal to `diff` + * 'less': the difference between distribution means is less than + `diff` + * 'greater': the difference between distribution means is greater + than `diff` + + Returns + ------- + statistic : float + The test statistic (see [1]_ equation 3.3). + pvalue : float + The probability of achieving such an extreme value of the test + statistic under the null hypothesis. + + Notes + ----- + + Let: + + .. math:: X_1 \sim \mbox{Poisson}(\mathtt{n1}\lambda_1) + + be a random variable independent of + + .. math:: X_2 \sim \mbox{Poisson}(\mathtt{n2}\lambda_2) + + and let ``k1`` and ``k2`` be the observed values of :math:`X_1` + and :math:`X_2`, respectively. Then `poisson_means_test` uses the number + of observed events ``k1`` and ``k2`` from samples of size ``n1`` and + ``n2``, respectively, to test the null hypothesis that + + .. math:: + H_0: \lambda_1 - \lambda_2 = \mathtt{diff} + + A benefit of the E-test is that it has good power for small sample sizes, + which can reduce sampling costs [1]_. It has been evaluated and determined + to be more powerful than the comparable C-test, sometimes referred to as + the Poisson exact test. + + References + ---------- + .. [1] Krishnamoorthy, K., & Thomson, J. (2004). A more powerful test for + comparing two Poisson means. Journal of Statistical Planning and + Inference, 119(1), 23-35. + + .. [2] Przyborowski, J., & Wilenski, H. (1940). Homogeneity of results in + testing samples from Poisson series: With an application to testing + clover seed for dodder. Biometrika, 31(3/4), 313-323. + + Examples + -------- + + Suppose that a gardener wishes to test the number of dodder (weed) seeds + in a sack of clover seeds that they buy from a seed company. It has + previously been established that the number of dodder seeds in clover + follows the Poisson distribution. + + A 100 gram sample is drawn from the sack before being shipped to the + gardener. The sample is analyzed, and it is found to contain no dodder + seeds; that is, `k1` is 0. However, upon arrival, the gardener draws + another 100 gram sample from the sack. This time, three dodder seeds are + found in the sample; that is, `k2` is 3. The gardener would like to + know if the difference is significant and not due to chance. The + null hypothesis is that the difference between the two samples is merely + due to chance, or that :math:`\lambda_1 - \lambda_2 = \mathtt{diff}` + where :math:`\mathtt{diff} = 0`. The alternative hypothesis is that the + difference is not due to chance, or :math:`\lambda_1 - \lambda_2 \ne 0`. + The gardener selects a significance level of 5% to reject the null + hypothesis in favor of the alternative [2]_. + + >>> import scipy.stats as stats + >>> res = stats.poisson_means_test(0, 100, 3, 100) + >>> res.statistic, res.pvalue + (-1.7320508075688772, 0.08837900929018157) + + The p-value is .088, indicating a near 9% chance of observing a value of + the test statistic under the null hypothesis. This exceeds 5%, so the + gardener does not reject the null hypothesis as the difference cannot be + regarded as significant at this level. + """ + + _poisson_means_test_iv(k1, n1, k2, n2, diff, alternative) + + # "for a given k_1 and k_2, an estimate of \lambda_2 is given by" [1] (3.4) + lmbd_hat2 = ((k1 + k2) / (n1 + n2) - diff * n1 / (n1 + n2)) + + # "\hat{\lambda_{2k}} may be less than or equal to zero ... and in this + # case the null hypothesis cannot be rejected ... [and] it is not necessary + # to compute the p-value". [1] page 26 below eq. (3.6). + if lmbd_hat2 <= 0: + return _stats_py.SignificanceResult(0, 1) + + # The unbiased variance estimate [1] (3.2) + var = k1 / (n1 ** 2) + k2 / (n2 ** 2) + + # The _observed_ pivot statistic from the input. It follows the + # unnumbered equation following equation (3.3) This is used later in + # comparison with the computed pivot statistics in an indicator function. + t_k1k2 = (k1 / n1 - k2 / n2 - diff) / np.sqrt(var) + + # Equation (3.5) of [1] is lengthy, so it is broken into several parts, + # beginning here. Note that the probability mass function of poisson is + # exp^(-\mu)*\mu^k/k!, so and this is called with shape \mu, here noted + # here as nlmbd_hat*. The strategy for evaluating the double summation in + # (3.5) is to create two arrays of the values of the two products inside + # the summation and then broadcast them together into a matrix, and then + # sum across the entire matrix. + + # Compute constants (as seen in the first and second separated products in + # (3.5).). (This is the shape (\mu) parameter of the poisson distribution.) + nlmbd_hat1 = n1 * (lmbd_hat2 + diff) + nlmbd_hat2 = n2 * lmbd_hat2 + + # Determine summation bounds for tail ends of distribution rather than + # summing to infinity. `x1*` is for the outer sum and `x2*` is the inner + # sum. + x1_lb, x1_ub = distributions.poisson.ppf([1e-10, 1 - 1e-16], nlmbd_hat1) + x2_lb, x2_ub = distributions.poisson.ppf([1e-10, 1 - 1e-16], nlmbd_hat2) + + # Construct arrays to function as the x_1 and x_2 counters on the summation + # in (3.5). `x1` is in columns and `x2` is in rows to allow for + # broadcasting. + x1 = np.arange(x1_lb, x1_ub + 1) + x2 = np.arange(x2_lb, x2_ub + 1)[:, None] + + # These are the two products in equation (3.5) with `prob_x1` being the + # first (left side) and `prob_x2` being the second (right side). (To + # make as clear as possible: the 1st contains a "+ d" term, the 2nd does + # not.) + prob_x1 = distributions.poisson.pmf(x1, nlmbd_hat1) + prob_x2 = distributions.poisson.pmf(x2, nlmbd_hat2) + + # compute constants for use in the "pivot statistic" per the + # unnumbered equation following (3.3). + lmbd_x1 = x1 / n1 + lmbd_x2 = x2 / n2 + lmbds_diff = lmbd_x1 - lmbd_x2 - diff + var_x1x2 = lmbd_x1 / n1 + lmbd_x2 / n2 + + # This is the 'pivot statistic' for use in the indicator of the summation + # (left side of "I[.]"). + with np.errstate(invalid='ignore', divide='ignore'): + t_x1x2 = lmbds_diff / np.sqrt(var_x1x2) + + # `[indicator]` implements the "I[.] ... the indicator function" per + # the paragraph following equation (3.5). + if alternative == 'two-sided': + indicator = np.abs(t_x1x2) >= np.abs(t_k1k2) + elif alternative == 'less': + indicator = t_x1x2 <= t_k1k2 + else: + indicator = t_x1x2 >= t_k1k2 + + # Multiply all combinations of the products together, exclude terms + # based on the `indicator` and then sum. (3.5) + pvalue = np.sum((prob_x1 * prob_x2)[indicator]) + return _stats_py.SignificanceResult(t_k1k2, pvalue) + + +def _poisson_means_test_iv(k1, n1, k2, n2, diff, alternative): + # """check for valid types and values of input to `poisson_mean_test`.""" + if k1 != int(k1) or k2 != int(k2): + raise TypeError('`k1` and `k2` must be integers.') + + count_err = '`k1` and `k2` must be greater than or equal to 0.' + if k1 < 0 or k2 < 0: + raise ValueError(count_err) + + if n1 <= 0 or n2 <= 0: + raise ValueError('`n1` and `n2` must be greater than 0.') + + if diff < 0: + raise ValueError('diff must be greater than or equal to 0.') + + alternatives = {'two-sided', 'less', 'greater'} + if alternative.lower() not in alternatives: + raise ValueError(f"Alternative must be one of '{alternatives}'.") + + +class CramerVonMisesResult: + def __init__(self, statistic, pvalue): + self.statistic = statistic + self.pvalue = pvalue + + def __repr__(self): + return (f"{self.__class__.__name__}(statistic={self.statistic}, " + f"pvalue={self.pvalue})") + + +def _psi1_mod(x): + """ + psi1 is defined in equation 1.10 in Csörgő, S. and Faraway, J. (1996). + This implements a modified version by excluding the term V(x) / 12 + (here: _cdf_cvm_inf(x) / 12) to avoid evaluating _cdf_cvm_inf(x) + twice in _cdf_cvm. + + Implementation based on MAPLE code of Julian Faraway and R code of the + function pCvM in the package goftest (v1.1.1), permission granted + by Adrian Baddeley. Main difference in the implementation: the code + here keeps adding terms of the series until the terms are small enough. + """ + + def _ed2(y): + z = y**2 / 4 + b = kv(1/4, z) + kv(3/4, z) + return np.exp(-z) * (y/2)**(3/2) * b / np.sqrt(np.pi) + + def _ed3(y): + z = y**2 / 4 + c = np.exp(-z) / np.sqrt(np.pi) + return c * (y/2)**(5/2) * (2*kv(1/4, z) + 3*kv(3/4, z) - kv(5/4, z)) + + def _Ak(k, x): + m = 2*k + 1 + sx = 2 * np.sqrt(x) + y1 = x**(3/4) + y2 = x**(5/4) + + e1 = m * gamma(k + 1/2) * _ed2((4 * k + 3)/sx) / (9 * y1) + e2 = gamma(k + 1/2) * _ed3((4 * k + 1) / sx) / (72 * y2) + e3 = 2 * (m + 2) * gamma(k + 3/2) * _ed3((4 * k + 5) / sx) / (12 * y2) + e4 = 7 * m * gamma(k + 1/2) * _ed2((4 * k + 1) / sx) / (144 * y1) + e5 = 7 * m * gamma(k + 1/2) * _ed2((4 * k + 5) / sx) / (144 * y1) + + return e1 + e2 + e3 + e4 + e5 + + x = np.asarray(x) + tot = np.zeros_like(x, dtype='float') + cond = np.ones_like(x, dtype='bool') + k = 0 + while np.any(cond): + z = -_Ak(k, x[cond]) / (np.pi * gamma(k + 1)) + tot[cond] = tot[cond] + z + cond[cond] = np.abs(z) >= 1e-7 + k += 1 + + return tot + + +def _cdf_cvm_inf(x): + """ + Calculate the cdf of the Cramér-von Mises statistic (infinite sample size). + + See equation 1.2 in Csörgő, S. and Faraway, J. (1996). + + Implementation based on MAPLE code of Julian Faraway and R code of the + function pCvM in the package goftest (v1.1.1), permission granted + by Adrian Baddeley. Main difference in the implementation: the code + here keeps adding terms of the series until the terms are small enough. + + The function is not expected to be accurate for large values of x, say + x > 4, when the cdf is very close to 1. + """ + x = np.asarray(x) + + def term(x, k): + # this expression can be found in [2], second line of (1.3) + u = np.exp(gammaln(k + 0.5) - gammaln(k+1)) / (np.pi**1.5 * np.sqrt(x)) + y = 4*k + 1 + q = y**2 / (16*x) + b = kv(0.25, q) + return u * np.sqrt(y) * np.exp(-q) * b + + tot = np.zeros_like(x, dtype='float') + cond = np.ones_like(x, dtype='bool') + k = 0 + while np.any(cond): + z = term(x[cond], k) + tot[cond] = tot[cond] + z + cond[cond] = np.abs(z) >= 1e-7 + k += 1 + + return tot + + +def _cdf_cvm(x, n=None): + """ + Calculate the cdf of the Cramér-von Mises statistic for a finite sample + size n. If N is None, use the asymptotic cdf (n=inf). + + See equation 1.8 in Csörgő, S. and Faraway, J. (1996) for finite samples, + 1.2 for the asymptotic cdf. + + The function is not expected to be accurate for large values of x, say + x > 2, when the cdf is very close to 1 and it might return values > 1 + in that case, e.g. _cdf_cvm(2.0, 12) = 1.0000027556716846. Moreover, it + is not accurate for small values of n, especially close to the bounds of + the distribution's domain, [1/(12*n), n/3], where the value jumps to 0 + and 1, respectively. These are limitations of the approximation by Csörgő + and Faraway (1996) implemented in this function. + """ + x = np.asarray(x) + if n is None: + y = _cdf_cvm_inf(x) + else: + # support of the test statistic is [12/n, n/3], see 1.1 in [2] + y = np.zeros_like(x, dtype='float') + sup = (1./(12*n) < x) & (x < n/3.) + # note: _psi1_mod does not include the term _cdf_cvm_inf(x) / 12 + # therefore, we need to add it here + y[sup] = _cdf_cvm_inf(x[sup]) * (1 + 1./(12*n)) + _psi1_mod(x[sup]) / n + y[x >= n/3] = 1 + + if y.ndim == 0: + return y[()] + return y + + +def _cvm_result_to_tuple(res): + return res.statistic, res.pvalue + + +@_axis_nan_policy_factory(CramerVonMisesResult, n_samples=1, too_small=1, + result_to_tuple=_cvm_result_to_tuple) +def cramervonmises(rvs, cdf, args=()): + """Perform the one-sample Cramér-von Mises test for goodness of fit. + + This performs a test of the goodness of fit of a cumulative distribution + function (cdf) :math:`F` compared to the empirical distribution function + :math:`F_n` of observed random variates :math:`X_1, ..., X_n` that are + assumed to be independent and identically distributed ([1]_). + The null hypothesis is that the :math:`X_i` have cumulative distribution + :math:`F`. + + Parameters + ---------- + rvs : array_like + A 1-D array of observed values of the random variables :math:`X_i`. + cdf : str or callable + The cumulative distribution function :math:`F` to test the + observations against. If a string, it should be the name of a + distribution in `scipy.stats`. If a callable, that callable is used + to calculate the cdf: ``cdf(x, *args) -> float``. + args : tuple, optional + Distribution parameters. These are assumed to be known; see Notes. + + Returns + ------- + res : object with attributes + statistic : float + Cramér-von Mises statistic. + pvalue : float + The p-value. + + See Also + -------- + kstest, cramervonmises_2samp + + Notes + ----- + .. versionadded:: 1.6.0 + + The p-value relies on the approximation given by equation 1.8 in [2]_. + It is important to keep in mind that the p-value is only accurate if + one tests a simple hypothesis, i.e. the parameters of the reference + distribution are known. If the parameters are estimated from the data + (composite hypothesis), the computed p-value is not reliable. + + References + ---------- + .. [1] Cramér-von Mises criterion, Wikipedia, + https://en.wikipedia.org/wiki/Cram%C3%A9r%E2%80%93von_Mises_criterion + .. [2] Csörgő, S. and Faraway, J. (1996). The Exact and Asymptotic + Distribution of Cramér-von Mises Statistics. Journal of the + Royal Statistical Society, pp. 221-234. + + Examples + -------- + + Suppose we wish to test whether data generated by ``scipy.stats.norm.rvs`` + were, in fact, drawn from the standard normal distribution. We choose a + significance level of ``alpha=0.05``. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng(165417232101553420507139617764912913465) + >>> x = stats.norm.rvs(size=500, random_state=rng) + >>> res = stats.cramervonmises(x, 'norm') + >>> res.statistic, res.pvalue + (0.1072085112565724, 0.5508482238203407) + + The p-value exceeds our chosen significance level, so we do not + reject the null hypothesis that the observed sample is drawn from the + standard normal distribution. + + Now suppose we wish to check whether the same samples shifted by 2.1 is + consistent with being drawn from a normal distribution with a mean of 2. + + >>> y = x + 2.1 + >>> res = stats.cramervonmises(y, 'norm', args=(2,)) + >>> res.statistic, res.pvalue + (0.8364446265294695, 0.00596286797008283) + + Here we have used the `args` keyword to specify the mean (``loc``) + of the normal distribution to test the data against. This is equivalent + to the following, in which we create a frozen normal distribution with + mean 2.1, then pass its ``cdf`` method as an argument. + + >>> frozen_dist = stats.norm(loc=2) + >>> res = stats.cramervonmises(y, frozen_dist.cdf) + >>> res.statistic, res.pvalue + (0.8364446265294695, 0.00596286797008283) + + In either case, we would reject the null hypothesis that the observed + sample is drawn from a normal distribution with a mean of 2 (and default + variance of 1) because the p-value is less than our chosen + significance level. + + """ + if isinstance(cdf, str): + cdf = getattr(distributions, cdf).cdf + + vals = np.sort(np.asarray(rvs)) + + if vals.size <= 1: + raise ValueError('The sample must contain at least two observations.') + + n = len(vals) + cdfvals = cdf(vals, *args) + + u = (2*np.arange(1, n+1) - 1)/(2*n) + w = 1/(12*n) + np.sum((u - cdfvals)**2) + + # avoid small negative values that can occur due to the approximation + p = max(0, 1. - _cdf_cvm(w, n)) + + return CramerVonMisesResult(statistic=w, pvalue=p) + + +def _get_wilcoxon_distr(n): + """ + Distribution of probability of the Wilcoxon ranksum statistic r_plus (sum + of ranks of positive differences). + Returns an array with the probabilities of all the possible ranks + r = 0, ..., n*(n+1)/2 + """ + c = np.ones(1, dtype=np.float64) + for k in range(1, n + 1): + prev_c = c + c = np.zeros(k * (k + 1) // 2 + 1, dtype=np.float64) + m = len(prev_c) + c[:m] = prev_c * 0.5 + c[-m:] += prev_c * 0.5 + return c + + +def _get_wilcoxon_distr2(n): + """ + Distribution of probability of the Wilcoxon ranksum statistic r_plus (sum + of ranks of positive differences). + Returns an array with the probabilities of all the possible ranks + r = 0, ..., n*(n+1)/2 + This is a slower reference function + References + ---------- + .. [1] 1. Harris T, Hardin JW. Exact Wilcoxon Signed-Rank and Wilcoxon + Mann-Whitney Ranksum Tests. The Stata Journal. 2013;13(2):337-343. + """ + ai = np.arange(1, n+1)[:, None] + t = n*(n+1)/2 + q = 2*t + j = np.arange(q) + theta = 2*np.pi/q*j + phi_sp = np.prod(np.cos(theta*ai), axis=0) + phi_s = np.exp(1j*theta*t) * phi_sp + p = np.real(ifft(phi_s)) + res = np.zeros(int(t)+1) + res[:-1:] = p[::2] + res[0] /= 2 + res[-1] = res[0] + return res + + +def _tau_b(A): + """Calculate Kendall's tau-b and p-value from contingency table.""" + # See [2] 2.2 and 4.2 + + # contingency table must be truly 2D + if A.shape[0] == 1 or A.shape[1] == 1: + return np.nan, np.nan + + NA = A.sum() + PA = _P(A) + QA = _Q(A) + Sri2 = (A.sum(axis=1)**2).sum() + Scj2 = (A.sum(axis=0)**2).sum() + denominator = (NA**2 - Sri2)*(NA**2 - Scj2) + + tau = (PA-QA)/(denominator)**0.5 + + numerator = 4*(_a_ij_Aij_Dij2(A) - (PA - QA)**2 / NA) + s02_tau_b = numerator/denominator + if s02_tau_b == 0: # Avoid divide by zero + return tau, 0 + Z = tau/s02_tau_b**0.5 + p = 2*norm.sf(abs(Z)) # 2-sided p-value + + return tau, p + + +def _somers_d(A, alternative='two-sided'): + """Calculate Somers' D and p-value from contingency table.""" + # See [3] page 1740 + + # contingency table must be truly 2D + if A.shape[0] <= 1 or A.shape[1] <= 1: + return np.nan, np.nan + + NA = A.sum() + NA2 = NA**2 + PA = _P(A) + QA = _Q(A) + Sri2 = (A.sum(axis=1)**2).sum() + + d = (PA - QA)/(NA2 - Sri2) + + S = _a_ij_Aij_Dij2(A) - (PA-QA)**2/NA + + with np.errstate(divide='ignore'): + Z = (PA - QA)/(4*(S))**0.5 + + p = scipy.stats._stats_py._get_pvalue(Z, distributions.norm, alternative) + + return d, p + + +@dataclass +class SomersDResult: + statistic: float + pvalue: float + table: np.ndarray + + +def somersd(x, y=None, alternative='two-sided'): + r"""Calculates Somers' D, an asymmetric measure of ordinal association. + + Like Kendall's :math:`\tau`, Somers' :math:`D` is a measure of the + correspondence between two rankings. Both statistics consider the + difference between the number of concordant and discordant pairs in two + rankings :math:`X` and :math:`Y`, and both are normalized such that values + close to 1 indicate strong agreement and values close to -1 indicate + strong disagreement. They differ in how they are normalized. To show the + relationship, Somers' :math:`D` can be defined in terms of Kendall's + :math:`\tau_a`: + + .. math:: + D(Y|X) = \frac{\tau_a(X, Y)}{\tau_a(X, X)} + + Suppose the first ranking :math:`X` has :math:`r` distinct ranks and the + second ranking :math:`Y` has :math:`s` distinct ranks. These two lists of + :math:`n` rankings can also be viewed as an :math:`r \times s` contingency + table in which element :math:`i, j` is the number of rank pairs with rank + :math:`i` in ranking :math:`X` and rank :math:`j` in ranking :math:`Y`. + Accordingly, `somersd` also allows the input data to be supplied as a + single, 2D contingency table instead of as two separate, 1D rankings. + + Note that the definition of Somers' :math:`D` is asymmetric: in general, + :math:`D(Y|X) \neq D(X|Y)`. ``somersd(x, y)`` calculates Somers' + :math:`D(Y|X)`: the "row" variable :math:`X` is treated as an independent + variable, and the "column" variable :math:`Y` is dependent. For Somers' + :math:`D(X|Y)`, swap the input lists or transpose the input table. + + Parameters + ---------- + x : array_like + 1D array of rankings, treated as the (row) independent variable. + Alternatively, a 2D contingency table. + y : array_like, optional + If `x` is a 1D array of rankings, `y` is a 1D array of rankings of the + same length, treated as the (column) dependent variable. + If `x` is 2D, `y` is ignored. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + * 'two-sided': the rank correlation is nonzero + * 'less': the rank correlation is negative (less than zero) + * 'greater': the rank correlation is positive (greater than zero) + + Returns + ------- + res : SomersDResult + A `SomersDResult` object with the following fields: + + statistic : float + The Somers' :math:`D` statistic. + pvalue : float + The p-value for a hypothesis test whose null + hypothesis is an absence of association, :math:`D=0`. + See notes for more information. + table : 2D array + The contingency table formed from rankings `x` and `y` (or the + provided contingency table, if `x` is a 2D array) + + See Also + -------- + kendalltau : Calculates Kendall's tau, another correlation measure. + weightedtau : Computes a weighted version of Kendall's tau. + spearmanr : Calculates a Spearman rank-order correlation coefficient. + pearsonr : Calculates a Pearson correlation coefficient. + + Notes + ----- + This function follows the contingency table approach of [2]_ and + [3]_. *p*-values are computed based on an asymptotic approximation of + the test statistic distribution under the null hypothesis :math:`D=0`. + + Theoretically, hypothesis tests based on Kendall's :math:`tau` and Somers' + :math:`D` should be identical. + However, the *p*-values returned by `kendalltau` are based + on the null hypothesis of *independence* between :math:`X` and :math:`Y` + (i.e. the population from which pairs in :math:`X` and :math:`Y` are + sampled contains equal numbers of all possible pairs), which is more + specific than the null hypothesis :math:`D=0` used here. If the null + hypothesis of independence is desired, it is acceptable to use the + *p*-value returned by `kendalltau` with the statistic returned by + `somersd` and vice versa. For more information, see [2]_. + + Contingency tables are formatted according to the convention used by + SAS and R: the first ranking supplied (``x``) is the "row" variable, and + the second ranking supplied (``y``) is the "column" variable. This is + opposite the convention of Somers' original paper [1]_. + + References + ---------- + .. [1] Robert H. Somers, "A New Asymmetric Measure of Association for + Ordinal Variables", *American Sociological Review*, Vol. 27, No. 6, + pp. 799--811, 1962. + + .. [2] Morton B. Brown and Jacqueline K. Benedetti, "Sampling Behavior of + Tests for Correlation in Two-Way Contingency Tables", *Journal of + the American Statistical Association* Vol. 72, No. 358, pp. + 309--315, 1977. + + .. [3] SAS Institute, Inc., "The FREQ Procedure (Book Excerpt)", + *SAS/STAT 9.2 User's Guide, Second Edition*, SAS Publishing, 2009. + + .. [4] Laerd Statistics, "Somers' d using SPSS Statistics", *SPSS + Statistics Tutorials and Statistical Guides*, + https://statistics.laerd.com/spss-tutorials/somers-d-using-spss-statistics.php, + Accessed July 31, 2020. + + Examples + -------- + We calculate Somers' D for the example given in [4]_, in which a hotel + chain owner seeks to determine the association between hotel room + cleanliness and customer satisfaction. The independent variable, hotel + room cleanliness, is ranked on an ordinal scale: "below average (1)", + "average (2)", or "above average (3)". The dependent variable, customer + satisfaction, is ranked on a second scale: "very dissatisfied (1)", + "moderately dissatisfied (2)", "neither dissatisfied nor satisfied (3)", + "moderately satisfied (4)", or "very satisfied (5)". 189 customers + respond to the survey, and the results are cast into a contingency table + with the hotel room cleanliness as the "row" variable and customer + satisfaction as the "column" variable. + + +-----+-----+-----+-----+-----+-----+ + | | (1) | (2) | (3) | (4) | (5) | + +=====+=====+=====+=====+=====+=====+ + | (1) | 27 | 25 | 14 | 7 | 0 | + +-----+-----+-----+-----+-----+-----+ + | (2) | 7 | 14 | 18 | 35 | 12 | + +-----+-----+-----+-----+-----+-----+ + | (3) | 1 | 3 | 2 | 7 | 17 | + +-----+-----+-----+-----+-----+-----+ + + For example, 27 customers assigned their room a cleanliness ranking of + "below average (1)" and a corresponding satisfaction of "very + dissatisfied (1)". We perform the analysis as follows. + + >>> from scipy.stats import somersd + >>> table = [[27, 25, 14, 7, 0], [7, 14, 18, 35, 12], [1, 3, 2, 7, 17]] + >>> res = somersd(table) + >>> res.statistic + 0.6032766111513396 + >>> res.pvalue + 1.0007091191074533e-27 + + The value of the Somers' D statistic is approximately 0.6, indicating + a positive correlation between room cleanliness and customer satisfaction + in the sample. + The *p*-value is very small, indicating a very small probability of + observing such an extreme value of the statistic under the null + hypothesis that the statistic of the entire population (from which + our sample of 189 customers is drawn) is zero. This supports the + alternative hypothesis that the true value of Somers' D for the population + is nonzero. + + """ + x, y = np.array(x), np.array(y) + if x.ndim == 1: + if x.size != y.size: + raise ValueError("Rankings must be of equal length.") + table = scipy.stats.contingency.crosstab(x, y)[1] + elif x.ndim == 2: + if np.any(x < 0): + raise ValueError("All elements of the contingency table must be " + "non-negative.") + if np.any(x != x.astype(int)): + raise ValueError("All elements of the contingency table must be " + "integer.") + if x.nonzero()[0].size < 2: + raise ValueError("At least two elements of the contingency table " + "must be nonzero.") + table = x + else: + raise ValueError("x must be either a 1D or 2D array") + # The table type is converted to a float to avoid an integer overflow + d, p = _somers_d(table.astype(float), alternative) + + # add alias for consistency with other correlation functions + res = SomersDResult(d, p, table) + res.correlation = d + return res + + +# This could be combined with `_all_partitions` in `_resampling.py` +def _all_partitions(nx, ny): + """ + Partition a set of indices into two fixed-length sets in all possible ways + + Partition a set of indices 0 ... nx + ny - 1 into two sets of length nx and + ny in all possible ways (ignoring order of elements). + """ + z = np.arange(nx+ny) + for c in combinations(z, nx): + x = np.array(c) + mask = np.ones(nx+ny, bool) + mask[x] = False + y = z[mask] + yield x, y + + +def _compute_log_combinations(n): + """Compute all log combination of C(n, k).""" + gammaln_arr = gammaln(np.arange(n + 1) + 1) + return gammaln(n + 1) - gammaln_arr - gammaln_arr[::-1] + + +@dataclass +class BarnardExactResult: + statistic: float + pvalue: float + + +def barnard_exact(table, alternative="two-sided", pooled=True, n=32): + r"""Perform a Barnard exact test on a 2x2 contingency table. + + Parameters + ---------- + table : array_like of ints + A 2x2 contingency table. Elements should be non-negative integers. + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the null and alternative hypotheses. Default is 'two-sided'. + Please see explanations in the Notes section below. + + pooled : bool, optional + Whether to compute score statistic with pooled variance (as in + Student's t-test, for example) or unpooled variance (as in Welch's + t-test). Default is ``True``. + + n : int, optional + Number of sampling points used in the construction of the sampling + method. Note that this argument will automatically be converted to + the next higher power of 2 since `scipy.stats.qmc.Sobol` is used to + select sample points. Default is 32. Must be positive. In most cases, + 32 points is enough to reach good precision. More points comes at + performance cost. + + Returns + ------- + ber : BarnardExactResult + A result object with the following attributes. + + statistic : float + The Wald statistic with pooled or unpooled variance, depending + on the user choice of `pooled`. + + pvalue : float + P-value, the probability of obtaining a distribution at least as + extreme as the one that was actually observed, assuming that the + null hypothesis is true. + + See Also + -------- + chi2_contingency : Chi-square test of independence of variables in a + contingency table. + fisher_exact : Fisher exact test on a 2x2 contingency table. + boschloo_exact : Boschloo's exact test on a 2x2 contingency table, + which is an uniformly more powerful alternative to Fisher's exact test. + + Notes + ----- + Barnard's test is an exact test used in the analysis of contingency + tables. It examines the association of two categorical variables, and + is a more powerful alternative than Fisher's exact test + for 2x2 contingency tables. + + Let's define :math:`X_0` a 2x2 matrix representing the observed sample, + where each column stores the binomial experiment, as in the example + below. Let's also define :math:`p_1, p_2` the theoretical binomial + probabilities for :math:`x_{11}` and :math:`x_{12}`. When using + Barnard exact test, we can assert three different null hypotheses : + + - :math:`H_0 : p_1 \geq p_2` versus :math:`H_1 : p_1 < p_2`, + with `alternative` = "less" + + - :math:`H_0 : p_1 \leq p_2` versus :math:`H_1 : p_1 > p_2`, + with `alternative` = "greater" + + - :math:`H_0 : p_1 = p_2` versus :math:`H_1 : p_1 \neq p_2`, + with `alternative` = "two-sided" (default one) + + In order to compute Barnard's exact test, we are using the Wald + statistic [3]_ with pooled or unpooled variance. + Under the default assumption that both variances are equal + (``pooled = True``), the statistic is computed as: + + .. math:: + + T(X) = \frac{ + \hat{p}_1 - \hat{p}_2 + }{ + \sqrt{ + \hat{p}(1 - \hat{p}) + (\frac{1}{c_1} + + \frac{1}{c_2}) + } + } + + with :math:`\hat{p}_1, \hat{p}_2` and :math:`\hat{p}` the estimator of + :math:`p_1, p_2` and :math:`p`, the latter being the combined probability, + given the assumption that :math:`p_1 = p_2`. + + If this assumption is invalid (``pooled = False``), the statistic is: + + .. math:: + + T(X) = \frac{ + \hat{p}_1 - \hat{p}_2 + }{ + \sqrt{ + \frac{\hat{p}_1 (1 - \hat{p}_1)}{c_1} + + \frac{\hat{p}_2 (1 - \hat{p}_2)}{c_2} + } + } + + The p-value is then computed as: + + .. math:: + + \sum + \binom{c_1}{x_{11}} + \binom{c_2}{x_{12}} + \pi^{x_{11} + x_{12}} + (1 - \pi)^{t - x_{11} - x_{12}} + + where the sum is over all 2x2 contingency tables :math:`X` such that: + * :math:`T(X) \leq T(X_0)` when `alternative` = "less", + * :math:`T(X) \geq T(X_0)` when `alternative` = "greater", or + * :math:`T(X) \geq |T(X_0)|` when `alternative` = "two-sided". + Above, :math:`c_1, c_2` are the sum of the columns 1 and 2, + and :math:`t` the total (sum of the 4 sample's element). + + The returned p-value is the maximum p-value taken over the nuisance + parameter :math:`\pi`, where :math:`0 \leq \pi \leq 1`. + + This function's complexity is :math:`O(n c_1 c_2)`, where `n` is the + number of sample points. + + References + ---------- + .. [1] Barnard, G. A. "Significance Tests for 2x2 Tables". *Biometrika*. + 34.1/2 (1947): 123-138. :doi:`dpgkg3` + + .. [2] Mehta, Cyrus R., and Pralay Senchaudhuri. "Conditional versus + unconditional exact tests for comparing two binomials." + *Cytel Software Corporation* 675 (2003): 1-5. + + .. [3] "Wald Test". *Wikipedia*. https://en.wikipedia.org/wiki/Wald_test + + Examples + -------- + An example use of Barnard's test is presented in [2]_. + + Consider the following example of a vaccine efficacy study + (Chan, 1998). In a randomized clinical trial of 30 subjects, 15 were + inoculated with a recombinant DNA influenza vaccine and the 15 were + inoculated with a placebo. Twelve of the 15 subjects in the placebo + group (80%) eventually became infected with influenza whereas for the + vaccine group, only 7 of the 15 subjects (47%) became infected. The + data are tabulated as a 2 x 2 table:: + + Vaccine Placebo + Yes 7 12 + No 8 3 + + When working with statistical hypothesis testing, we usually use a + threshold probability or significance level upon which we decide + to reject the null hypothesis :math:`H_0`. Suppose we choose the common + significance level of 5%. + + Our alternative hypothesis is that the vaccine will lower the chance of + becoming infected with the virus; that is, the probability :math:`p_1` of + catching the virus with the vaccine will be *less than* the probability + :math:`p_2` of catching the virus without the vaccine. Therefore, we call + `barnard_exact` with the ``alternative="less"`` option: + + >>> import scipy.stats as stats + >>> res = stats.barnard_exact([[7, 12], [8, 3]], alternative="less") + >>> res.statistic + -1.894... + >>> res.pvalue + 0.03407... + + Under the null hypothesis that the vaccine will not lower the chance of + becoming infected, the probability of obtaining test results at least as + extreme as the observed data is approximately 3.4%. Since this p-value is + less than our chosen significance level, we have evidence to reject + :math:`H_0` in favor of the alternative. + + Suppose we had used Fisher's exact test instead: + + >>> _, pvalue = stats.fisher_exact([[7, 12], [8, 3]], alternative="less") + >>> pvalue + 0.0640... + + With the same threshold significance of 5%, we would not have been able + to reject the null hypothesis in favor of the alternative. As stated in + [2]_, Barnard's test is uniformly more powerful than Fisher's exact test + because Barnard's test does not condition on any margin. Fisher's test + should only be used when both sets of marginals are fixed. + + """ + if n <= 0: + raise ValueError( + "Number of points `n` must be strictly positive, " + f"found {n!r}" + ) + + table = np.asarray(table, dtype=np.int64) + + if not table.shape == (2, 2): + raise ValueError("The input `table` must be of shape (2, 2).") + + if np.any(table < 0): + raise ValueError("All values in `table` must be nonnegative.") + + if 0 in table.sum(axis=0): + # If both values in column are zero, the p-value is 1 and + # the score's statistic is NaN. + return BarnardExactResult(np.nan, 1.0) + + total_col_1, total_col_2 = table.sum(axis=0) + + x1 = np.arange(total_col_1 + 1, dtype=np.int64).reshape(-1, 1) + x2 = np.arange(total_col_2 + 1, dtype=np.int64).reshape(1, -1) + + # We need to calculate the wald statistics for each combination of x1 and + # x2. + p1, p2 = x1 / total_col_1, x2 / total_col_2 + + if pooled: + p = (x1 + x2) / (total_col_1 + total_col_2) + variances = p * (1 - p) * (1 / total_col_1 + 1 / total_col_2) + else: + variances = p1 * (1 - p1) / total_col_1 + p2 * (1 - p2) / total_col_2 + + # To avoid warning when dividing by 0 + with np.errstate(divide="ignore", invalid="ignore"): + wald_statistic = np.divide((p1 - p2), np.sqrt(variances)) + + wald_statistic[p1 == p2] = 0 # Removing NaN values + + wald_stat_obs = wald_statistic[table[0, 0], table[0, 1]] + + if alternative == "two-sided": + index_arr = np.abs(wald_statistic) >= abs(wald_stat_obs) + elif alternative == "less": + index_arr = wald_statistic <= wald_stat_obs + elif alternative == "greater": + index_arr = wald_statistic >= wald_stat_obs + else: + msg = ( + "`alternative` should be one of {'two-sided', 'less', 'greater'}," + f" found {alternative!r}" + ) + raise ValueError(msg) + + x1_sum_x2 = x1 + x2 + + x1_log_comb = _compute_log_combinations(total_col_1) + x2_log_comb = _compute_log_combinations(total_col_2) + x1_sum_x2_log_comb = x1_log_comb[x1] + x2_log_comb[x2] + + result = shgo( + _get_binomial_log_p_value_with_nuisance_param, + args=(x1_sum_x2, x1_sum_x2_log_comb, index_arr), + bounds=((0, 1),), + n=n, + sampling_method="sobol", + ) + + # result.fun is the negative log pvalue and therefore needs to be + # changed before return + p_value = np.clip(np.exp(-result.fun), a_min=0, a_max=1) + return BarnardExactResult(wald_stat_obs, p_value) + + +@dataclass +class BoschlooExactResult: + statistic: float + pvalue: float + + +def boschloo_exact(table, alternative="two-sided", n=32): + r"""Perform Boschloo's exact test on a 2x2 contingency table. + + Parameters + ---------- + table : array_like of ints + A 2x2 contingency table. Elements should be non-negative integers. + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the null and alternative hypotheses. Default is 'two-sided'. + Please see explanations in the Notes section below. + + n : int, optional + Number of sampling points used in the construction of the sampling + method. Note that this argument will automatically be converted to + the next higher power of 2 since `scipy.stats.qmc.Sobol` is used to + select sample points. Default is 32. Must be positive. In most cases, + 32 points is enough to reach good precision. More points comes at + performance cost. + + Returns + ------- + ber : BoschlooExactResult + A result object with the following attributes. + + statistic : float + The statistic used in Boschloo's test; that is, the p-value + from Fisher's exact test. + + pvalue : float + P-value, the probability of obtaining a distribution at least as + extreme as the one that was actually observed, assuming that the + null hypothesis is true. + + See Also + -------- + chi2_contingency : Chi-square test of independence of variables in a + contingency table. + fisher_exact : Fisher exact test on a 2x2 contingency table. + barnard_exact : Barnard's exact test, which is a more powerful alternative + than Fisher's exact test for 2x2 contingency tables. + + Notes + ----- + Boschloo's test is an exact test used in the analysis of contingency + tables. It examines the association of two categorical variables, and + is a uniformly more powerful alternative to Fisher's exact test + for 2x2 contingency tables. + + Boschloo's exact test uses the p-value of Fisher's exact test as a + statistic, and Boschloo's p-value is the probability under the null + hypothesis of observing such an extreme value of this statistic. + + Let's define :math:`X_0` a 2x2 matrix representing the observed sample, + where each column stores the binomial experiment, as in the example + below. Let's also define :math:`p_1, p_2` the theoretical binomial + probabilities for :math:`x_{11}` and :math:`x_{12}`. When using + Boschloo exact test, we can assert three different alternative hypotheses: + + - :math:`H_0 : p_1=p_2` versus :math:`H_1 : p_1 < p_2`, + with `alternative` = "less" + + - :math:`H_0 : p_1=p_2` versus :math:`H_1 : p_1 > p_2`, + with `alternative` = "greater" + + - :math:`H_0 : p_1=p_2` versus :math:`H_1 : p_1 \neq p_2`, + with `alternative` = "two-sided" (default) + + There are multiple conventions for computing a two-sided p-value when the + null distribution is asymmetric. Here, we apply the convention that the + p-value of a two-sided test is twice the minimum of the p-values of the + one-sided tests (clipped to 1.0). Note that `fisher_exact` follows a + different convention, so for a given `table`, the statistic reported by + `boschloo_exact` may differ from the p-value reported by `fisher_exact` + when ``alternative='two-sided'``. + + .. versionadded:: 1.7.0 + + References + ---------- + .. [1] R.D. Boschloo. "Raised conditional level of significance for the + 2 x 2-table when testing the equality of two probabilities", + Statistica Neerlandica, 24(1), 1970 + + .. [2] "Boschloo's test", Wikipedia, + https://en.wikipedia.org/wiki/Boschloo%27s_test + + .. [3] Lise M. Saari et al. "Employee attitudes and job satisfaction", + Human Resource Management, 43(4), 395-407, 2004, + :doi:`10.1002/hrm.20032`. + + Examples + -------- + In the following example, we consider the article "Employee + attitudes and job satisfaction" [3]_ + which reports the results of a survey from 63 scientists and 117 college + professors. Of the 63 scientists, 31 said they were very satisfied with + their jobs, whereas 74 of the college professors were very satisfied + with their work. Is this significant evidence that college + professors are happier with their work than scientists? + The following table summarizes the data mentioned above:: + + college professors scientists + Very Satisfied 74 31 + Dissatisfied 43 32 + + When working with statistical hypothesis testing, we usually use a + threshold probability or significance level upon which we decide + to reject the null hypothesis :math:`H_0`. Suppose we choose the common + significance level of 5%. + + Our alternative hypothesis is that college professors are truly more + satisfied with their work than scientists. Therefore, we expect + :math:`p_1` the proportion of very satisfied college professors to be + greater than :math:`p_2`, the proportion of very satisfied scientists. + We thus call `boschloo_exact` with the ``alternative="greater"`` option: + + >>> import scipy.stats as stats + >>> res = stats.boschloo_exact([[74, 31], [43, 32]], alternative="greater") + >>> res.statistic + 0.0483... + >>> res.pvalue + 0.0355... + + Under the null hypothesis that scientists are happier in their work than + college professors, the probability of obtaining test + results at least as extreme as the observed data is approximately 3.55%. + Since this p-value is less than our chosen significance level, we have + evidence to reject :math:`H_0` in favor of the alternative hypothesis. + + """ + hypergeom = distributions.hypergeom + + if n <= 0: + raise ValueError( + "Number of points `n` must be strictly positive," + f" found {n!r}" + ) + + table = np.asarray(table, dtype=np.int64) + + if not table.shape == (2, 2): + raise ValueError("The input `table` must be of shape (2, 2).") + + if np.any(table < 0): + raise ValueError("All values in `table` must be nonnegative.") + + if 0 in table.sum(axis=0): + # If both values in column are zero, the p-value is 1 and + # the score's statistic is NaN. + return BoschlooExactResult(np.nan, np.nan) + + total_col_1, total_col_2 = table.sum(axis=0) + total = total_col_1 + total_col_2 + x1 = np.arange(total_col_1 + 1, dtype=np.int64).reshape(1, -1) + x2 = np.arange(total_col_2 + 1, dtype=np.int64).reshape(-1, 1) + x1_sum_x2 = x1 + x2 + + if alternative == 'less': + pvalues = hypergeom.cdf(x1, total, x1_sum_x2, total_col_1).T + elif alternative == 'greater': + # Same formula as the 'less' case, but with the second column. + pvalues = hypergeom.cdf(x2, total, x1_sum_x2, total_col_2).T + elif alternative == 'two-sided': + boschloo_less = boschloo_exact(table, alternative="less", n=n) + boschloo_greater = boschloo_exact(table, alternative="greater", n=n) + + res = ( + boschloo_less if boschloo_less.pvalue < boschloo_greater.pvalue + else boschloo_greater + ) + + # Two-sided p-value is defined as twice the minimum of the one-sided + # p-values + pvalue = np.clip(2 * res.pvalue, a_min=0, a_max=1) + return BoschlooExactResult(res.statistic, pvalue) + else: + msg = ( + f"`alternative` should be one of {'two-sided', 'less', 'greater'}," + f" found {alternative!r}" + ) + raise ValueError(msg) + + fisher_stat = pvalues[table[0, 0], table[0, 1]] + + # fisher_stat * (1+1e-13) guards us from small numerical error. It is + # equivalent to np.isclose with relative tol of 1e-13 and absolute tol of 0 + # For more throughout explanations, see gh-14178 + index_arr = pvalues <= fisher_stat * (1+1e-13) + + x1, x2, x1_sum_x2 = x1.T, x2.T, x1_sum_x2.T + x1_log_comb = _compute_log_combinations(total_col_1) + x2_log_comb = _compute_log_combinations(total_col_2) + x1_sum_x2_log_comb = x1_log_comb[x1] + x2_log_comb[x2] + + result = shgo( + _get_binomial_log_p_value_with_nuisance_param, + args=(x1_sum_x2, x1_sum_x2_log_comb, index_arr), + bounds=((0, 1),), + n=n, + sampling_method="sobol", + ) + + # result.fun is the negative log pvalue and therefore needs to be + # changed before return + p_value = np.clip(np.exp(-result.fun), a_min=0, a_max=1) + return BoschlooExactResult(fisher_stat, p_value) + + +def _get_binomial_log_p_value_with_nuisance_param( + nuisance_param, x1_sum_x2, x1_sum_x2_log_comb, index_arr +): + r""" + Compute the log pvalue in respect of a nuisance parameter considering + a 2x2 sample space. + + Parameters + ---------- + nuisance_param : float + nuisance parameter used in the computation of the maximisation of + the p-value. Must be between 0 and 1 + + x1_sum_x2 : ndarray + Sum of x1 and x2 inside barnard_exact + + x1_sum_x2_log_comb : ndarray + sum of the log combination of x1 and x2 + + index_arr : ndarray of boolean + + Returns + ------- + p_value : float + Return the maximum p-value considering every nuisance parameter + between 0 and 1 + + Notes + ----- + + Both Barnard's test and Boschloo's test iterate over a nuisance parameter + :math:`\pi \in [0, 1]` to find the maximum p-value. To search this + maxima, this function return the negative log pvalue with respect to the + nuisance parameter passed in params. This negative log p-value is then + used in `shgo` to find the minimum negative pvalue which is our maximum + pvalue. + + Also, to compute the different combination used in the + p-values' computation formula, this function uses `gammaln` which is + more tolerant for large value than `scipy.special.comb`. `gammaln` gives + a log combination. For the little precision loss, performances are + improved a lot. + """ + t1, t2 = x1_sum_x2.shape + n = t1 + t2 - 2 + with np.errstate(divide="ignore", invalid="ignore"): + log_nuisance = np.log( + nuisance_param, + out=np.zeros_like(nuisance_param), + where=nuisance_param >= 0, + ) + log_1_minus_nuisance = np.log( + 1 - nuisance_param, + out=np.zeros_like(nuisance_param), + where=1 - nuisance_param >= 0, + ) + + nuisance_power_x1_x2 = log_nuisance * x1_sum_x2 + nuisance_power_x1_x2[(x1_sum_x2 == 0)[:, :]] = 0 + + nuisance_power_n_minus_x1_x2 = log_1_minus_nuisance * (n - x1_sum_x2) + nuisance_power_n_minus_x1_x2[(x1_sum_x2 == n)[:, :]] = 0 + + tmp_log_values_arr = ( + x1_sum_x2_log_comb + + nuisance_power_x1_x2 + + nuisance_power_n_minus_x1_x2 + ) + + tmp_values_from_index = tmp_log_values_arr[index_arr] + + # To avoid dividing by zero in log function and getting inf value, + # values are centered according to the max + max_value = tmp_values_from_index.max() + + # To have better result's precision, the log pvalue is taken here. + # Indeed, pvalue is included inside [0, 1] interval. Passing the + # pvalue to log makes the interval a lot bigger ([-inf, 0]), and thus + # help us to achieve better precision + with np.errstate(divide="ignore", invalid="ignore"): + log_probs = np.exp(tmp_values_from_index - max_value).sum() + log_pvalue = max_value + np.log( + log_probs, + out=np.full_like(log_probs, -np.inf), + where=log_probs > 0, + ) + + # Since shgo find the minima, minus log pvalue is returned + return -log_pvalue + + +def _pval_cvm_2samp_exact(s, m, n): + """ + Compute the exact p-value of the Cramer-von Mises two-sample test + for a given value s of the test statistic. + m and n are the sizes of the samples. + + [1] Y. Xiao, A. Gordon, and A. Yakovlev, "A C++ Program for + the Cramér-Von Mises Two-Sample Test", J. Stat. Soft., + vol. 17, no. 8, pp. 1-15, Dec. 2006. + [2] T. W. Anderson "On the Distribution of the Two-Sample Cramer-von Mises + Criterion," The Annals of Mathematical Statistics, Ann. Math. Statist. + 33(3), 1148-1159, (September, 1962) + """ + + # [1, p. 3] + lcm = np.lcm(m, n) + # [1, p. 4], below eq. 3 + a = lcm // m + b = lcm // n + # Combine Eq. 9 in [2] with Eq. 2 in [1] and solve for $\zeta$ + # Hint: `s` is $U$ in [2], and $T_2$ in [1] is $T$ in [2] + mn = m * n + zeta = lcm ** 2 * (m + n) * (6 * s - mn * (4 * mn - 1)) // (6 * mn ** 2) + + # bound maximum value that may appear in `gs` (remember both rows!) + zeta_bound = lcm**2 * (m + n) # bound elements in row 1 + combinations = comb(m + n, m) # sum of row 2 + max_gs = max(zeta_bound, combinations) + dtype = np.min_scalar_type(max_gs) + + # the frequency table of $g_{u, v}^+$ defined in [1, p. 6] + gs = ([np.array([[0], [1]], dtype=dtype)] + + [np.empty((2, 0), dtype=dtype) for _ in range(m)]) + for u in range(n + 1): + next_gs = [] + tmp = np.empty((2, 0), dtype=dtype) + for v, g in enumerate(gs): + # Calculate g recursively with eq. 11 in [1]. Even though it + # doesn't look like it, this also does 12/13 (all of Algorithm 1). + vi, i0, i1 = np.intersect1d(tmp[0], g[0], return_indices=True) + tmp = np.concatenate([ + np.stack([vi, tmp[1, i0] + g[1, i1]]), + np.delete(tmp, i0, 1), + np.delete(g, i1, 1) + ], 1) + res = (a * v - b * u) ** 2 + tmp[0] += res.astype(dtype) + next_gs.append(tmp) + gs = next_gs + value, freq = gs[m] + return np.float64(np.sum(freq[value >= zeta]) / combinations) + + +@_axis_nan_policy_factory(CramerVonMisesResult, n_samples=2, too_small=1, + result_to_tuple=_cvm_result_to_tuple) +def cramervonmises_2samp(x, y, method='auto'): + """Perform the two-sample Cramér-von Mises test for goodness of fit. + + This is the two-sample version of the Cramér-von Mises test ([1]_): + for two independent samples :math:`X_1, ..., X_n` and + :math:`Y_1, ..., Y_m`, the null hypothesis is that the samples + come from the same (unspecified) continuous distribution. + + Parameters + ---------- + x : array_like + A 1-D array of observed values of the random variables :math:`X_i`. + y : array_like + A 1-D array of observed values of the random variables :math:`Y_i`. + method : {'auto', 'asymptotic', 'exact'}, optional + The method used to compute the p-value, see Notes for details. + The default is 'auto'. + + Returns + ------- + res : object with attributes + statistic : float + Cramér-von Mises statistic. + pvalue : float + The p-value. + + See Also + -------- + cramervonmises, anderson_ksamp, epps_singleton_2samp, ks_2samp + + Notes + ----- + .. versionadded:: 1.7.0 + + The statistic is computed according to equation 9 in [2]_. The + calculation of the p-value depends on the keyword `method`: + + - ``asymptotic``: The p-value is approximated by using the limiting + distribution of the test statistic. + - ``exact``: The exact p-value is computed by enumerating all + possible combinations of the test statistic, see [2]_. + + If ``method='auto'``, the exact approach is used + if both samples contain equal to or less than 20 observations, + otherwise the asymptotic distribution is used. + + If the underlying distribution is not continuous, the p-value is likely to + be conservative (Section 6.2 in [3]_). When ranking the data to compute + the test statistic, midranks are used if there are ties. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Cramer-von_Mises_criterion + .. [2] Anderson, T.W. (1962). On the distribution of the two-sample + Cramer-von-Mises criterion. The Annals of Mathematical + Statistics, pp. 1148-1159. + .. [3] Conover, W.J., Practical Nonparametric Statistics, 1971. + + Examples + -------- + + Suppose we wish to test whether two samples generated by + ``scipy.stats.norm.rvs`` have the same distribution. We choose a + significance level of alpha=0.05. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> x = stats.norm.rvs(size=100, random_state=rng) + >>> y = stats.norm.rvs(size=70, random_state=rng) + >>> res = stats.cramervonmises_2samp(x, y) + >>> res.statistic, res.pvalue + (0.29376470588235293, 0.1412873014573014) + + The p-value exceeds our chosen significance level, so we do not + reject the null hypothesis that the observed samples are drawn from the + same distribution. + + For small sample sizes, one can compute the exact p-values: + + >>> x = stats.norm.rvs(size=7, random_state=rng) + >>> y = stats.t.rvs(df=2, size=6, random_state=rng) + >>> res = stats.cramervonmises_2samp(x, y, method='exact') + >>> res.statistic, res.pvalue + (0.197802197802198, 0.31643356643356646) + + The p-value based on the asymptotic distribution is a good approximation + even though the sample size is small. + + >>> res = stats.cramervonmises_2samp(x, y, method='asymptotic') + >>> res.statistic, res.pvalue + (0.197802197802198, 0.2966041181527128) + + Independent of the method, one would not reject the null hypothesis at the + chosen significance level in this example. + + """ + xa = np.sort(np.asarray(x)) + ya = np.sort(np.asarray(y)) + + if xa.size <= 1 or ya.size <= 1: + raise ValueError('x and y must contain at least two observations.') + if method not in ['auto', 'exact', 'asymptotic']: + raise ValueError('method must be either auto, exact or asymptotic.') + + nx = len(xa) + ny = len(ya) + + if method == 'auto': + if max(nx, ny) > 20: + method = 'asymptotic' + else: + method = 'exact' + + # get ranks of x and y in the pooled sample + z = np.concatenate([xa, ya]) + # in case of ties, use midrank (see [1]) + r = scipy.stats.rankdata(z, method='average') + rx = r[:nx] + ry = r[nx:] + + # compute U (eq. 10 in [2]) + u = nx * np.sum((rx - np.arange(1, nx+1))**2) + u += ny * np.sum((ry - np.arange(1, ny+1))**2) + + # compute T (eq. 9 in [2]) + k, N = nx*ny, nx + ny + t = u / (k*N) - (4*k - 1)/(6*N) + + if method == 'exact': + p = _pval_cvm_2samp_exact(u, nx, ny) + else: + # compute expected value and variance of T (eq. 11 and 14 in [2]) + et = (1 + 1/N)/6 + vt = (N+1) * (4*k*N - 3*(nx**2 + ny**2) - 2*k) + vt = vt / (45 * N**2 * 4 * k) + + # computed the normalized statistic (eq. 15 in [2]) + tn = 1/6 + (t - et) / np.sqrt(45 * vt) + + # approximate distribution of tn with limiting distribution + # of the one-sample test statistic + # if tn < 0.003, the _cdf_cvm_inf(tn) < 1.28*1e-18, return 1.0 directly + if tn < 0.003: + p = 1.0 + else: + p = max(0, 1. - _cdf_cvm_inf(tn)) + + return CramerVonMisesResult(statistic=t, pvalue=p) + + +class TukeyHSDResult: + """Result of `scipy.stats.tukey_hsd`. + + Attributes + ---------- + statistic : float ndarray + The computed statistic of the test for each comparison. The element + at index ``(i, j)`` is the statistic for the comparison between groups + ``i`` and ``j``. + pvalue : float ndarray + The associated p-value from the studentized range distribution. The + element at index ``(i, j)`` is the p-value for the comparison + between groups ``i`` and ``j``. + + Notes + ----- + The string representation of this object displays the most recently + calculated confidence interval, and if none have been previously + calculated, it will evaluate ``confidence_interval()``. + + References + ---------- + .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "7.4.7.1. Tukey's + Method." + https://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm, + 28 November 2020. + """ + + def __init__(self, statistic, pvalue, _nobs, _ntreatments, _stand_err): + self.statistic = statistic + self.pvalue = pvalue + self._ntreatments = _ntreatments + self._nobs = _nobs + self._stand_err = _stand_err + self._ci = None + self._ci_cl = None + + def __str__(self): + # Note: `__str__` prints the confidence intervals from the most + # recent call to `confidence_interval`. If it has not been called, + # it will be called with the default CL of .95. + if self._ci is None: + self.confidence_interval(confidence_level=.95) + s = ("Tukey's HSD Pairwise Group Comparisons" + f" ({self._ci_cl*100:.1f}% Confidence Interval)\n") + s += "Comparison Statistic p-value Lower CI Upper CI\n" + for i in range(self.pvalue.shape[0]): + for j in range(self.pvalue.shape[0]): + if i != j: + s += (f" ({i} - {j}) {self.statistic[i, j]:>10.3f}" + f"{self.pvalue[i, j]:>10.3f}" + f"{self._ci.low[i, j]:>10.3f}" + f"{self._ci.high[i, j]:>10.3f}\n") + return s + + def confidence_interval(self, confidence_level=.95): + """Compute the confidence interval for the specified confidence level. + + Parameters + ---------- + confidence_level : float, optional + Confidence level for the computed confidence interval + of the estimated proportion. Default is .95. + + Returns + ------- + ci : ``ConfidenceInterval`` object + The object has attributes ``low`` and ``high`` that hold the + lower and upper bounds of the confidence intervals for each + comparison. The high and low values are accessible for each + comparison at index ``(i, j)`` between groups ``i`` and ``j``. + + References + ---------- + .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "7.4.7.1. + Tukey's Method." + https://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm, + 28 November 2020. + + Examples + -------- + >>> from scipy.stats import tukey_hsd + >>> group0 = [24.5, 23.5, 26.4, 27.1, 29.9] + >>> group1 = [28.4, 34.2, 29.5, 32.2, 30.1] + >>> group2 = [26.1, 28.3, 24.3, 26.2, 27.8] + >>> result = tukey_hsd(group0, group1, group2) + >>> ci = result.confidence_interval() + >>> ci.low + array([[-3.649159, -8.249159, -3.909159], + [ 0.950841, -3.649159, 0.690841], + [-3.389159, -7.989159, -3.649159]]) + >>> ci.high + array([[ 3.649159, -0.950841, 3.389159], + [ 8.249159, 3.649159, 7.989159], + [ 3.909159, -0.690841, 3.649159]]) + """ + # check to see if the supplied confidence level matches that of the + # previously computed CI. + if (self._ci is not None and self._ci_cl is not None and + confidence_level == self._ci_cl): + return self._ci + + if not 0 < confidence_level < 1: + raise ValueError("Confidence level must be between 0 and 1.") + # determine the critical value of the studentized range using the + # appropriate confidence level, number of treatments, and degrees + # of freedom as determined by the number of data less the number of + # treatments. ("Confidence limits for Tukey's method")[1]. Note that + # in the cases of unequal sample sizes there will be a criterion for + # each group comparison. + params = (confidence_level, self._nobs, self._ntreatments - self._nobs) + srd = distributions.studentized_range.ppf(*params) + # also called maximum critical value, the Tukey criterion is the + # studentized range critical value * the square root of mean square + # error over the sample size. + tukey_criterion = srd * self._stand_err + # the confidence levels are determined by the + # `mean_differences` +- `tukey_criterion` + upper_conf = self.statistic + tukey_criterion + lower_conf = self.statistic - tukey_criterion + self._ci = ConfidenceInterval(low=lower_conf, high=upper_conf) + self._ci_cl = confidence_level + return self._ci + + +def _tukey_hsd_iv(args): + if (len(args)) < 2: + raise ValueError("There must be more than 1 treatment.") + args = [np.asarray(arg) for arg in args] + for arg in args: + if arg.ndim != 1: + raise ValueError("Input samples must be one-dimensional.") + if arg.size <= 1: + raise ValueError("Input sample size must be greater than one.") + if np.isinf(arg).any(): + raise ValueError("Input samples must be finite.") + return args + + +def tukey_hsd(*args): + """Perform Tukey's HSD test for equality of means over multiple treatments. + + Tukey's honestly significant difference (HSD) test performs pairwise + comparison of means for a set of samples. Whereas ANOVA (e.g. `f_oneway`) + assesses whether the true means underlying each sample are identical, + Tukey's HSD is a post hoc test used to compare the mean of each sample + to the mean of each other sample. + + The null hypothesis is that the distributions underlying the samples all + have the same mean. The test statistic, which is computed for every + possible pairing of samples, is simply the difference between the sample + means. For each pair, the p-value is the probability under the null + hypothesis (and other assumptions; see notes) of observing such an extreme + value of the statistic, considering that many pairwise comparisons are + being performed. Confidence intervals for the difference between each pair + of means are also available. + + Parameters + ---------- + sample1, sample2, ... : array_like + The sample measurements for each group. There must be at least + two arguments. + + Returns + ------- + result : `~scipy.stats._result_classes.TukeyHSDResult` instance + The return value is an object with the following attributes: + + statistic : float ndarray + The computed statistic of the test for each comparison. The element + at index ``(i, j)`` is the statistic for the comparison between + groups ``i`` and ``j``. + pvalue : float ndarray + The computed p-value of the test for each comparison. The element + at index ``(i, j)`` is the p-value for the comparison between + groups ``i`` and ``j``. + + The object has the following methods: + + confidence_interval(confidence_level=0.95): + Compute the confidence interval for the specified confidence level. + + See Also + -------- + dunnett : performs comparison of means against a control group. + + Notes + ----- + The use of this test relies on several assumptions. + + 1. The observations are independent within and among groups. + 2. The observations within each group are normally distributed. + 3. The distributions from which the samples are drawn have the same finite + variance. + + The original formulation of the test was for samples of equal size [6]_. + In case of unequal sample sizes, the test uses the Tukey-Kramer method + [4]_. + + References + ---------- + .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "7.4.7.1. Tukey's + Method." + https://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm, + 28 November 2020. + .. [2] Abdi, Herve & Williams, Lynne. (2021). "Tukey's Honestly Significant + Difference (HSD) Test." + https://personal.utdallas.edu/~herve/abdi-HSD2010-pretty.pdf + .. [3] "One-Way ANOVA Using SAS PROC ANOVA & PROC GLM." SAS + Tutorials, 2007, www.stattutorials.com/SAS/TUTORIAL-PROC-GLM.htm. + .. [4] Kramer, Clyde Young. "Extension of Multiple Range Tests to Group + Means with Unequal Numbers of Replications." Biometrics, vol. 12, + no. 3, 1956, pp. 307-310. JSTOR, www.jstor.org/stable/3001469. + Accessed 25 May 2021. + .. [5] NIST/SEMATECH e-Handbook of Statistical Methods, "7.4.3.3. + The ANOVA table and tests of hypotheses about means" + https://www.itl.nist.gov/div898/handbook/prc/section4/prc433.htm, + 2 June 2021. + .. [6] Tukey, John W. "Comparing Individual Means in the Analysis of + Variance." Biometrics, vol. 5, no. 2, 1949, pp. 99-114. JSTOR, + www.jstor.org/stable/3001913. Accessed 14 June 2021. + + + Examples + -------- + Here are some data comparing the time to relief of three brands of + headache medicine, reported in minutes. Data adapted from [3]_. + + >>> import numpy as np + >>> from scipy.stats import tukey_hsd + >>> group0 = [24.5, 23.5, 26.4, 27.1, 29.9] + >>> group1 = [28.4, 34.2, 29.5, 32.2, 30.1] + >>> group2 = [26.1, 28.3, 24.3, 26.2, 27.8] + + We would like to see if the means between any of the groups are + significantly different. First, visually examine a box and whisker plot. + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + >>> ax.boxplot([group0, group1, group2]) + >>> ax.set_xticklabels(["group0", "group1", "group2"]) # doctest: +SKIP + >>> ax.set_ylabel("mean") # doctest: +SKIP + >>> plt.show() + + From the box and whisker plot, we can see overlap in the interquartile + ranges group 1 to group 2 and group 3, but we can apply the ``tukey_hsd`` + test to determine if the difference between means is significant. We + set a significance level of .05 to reject the null hypothesis. + + >>> res = tukey_hsd(group0, group1, group2) + >>> print(res) + Tukey's HSD Pairwise Group Comparisons (95.0% Confidence Interval) + Comparison Statistic p-value Lower CI Upper CI + (0 - 1) -4.600 0.014 -8.249 -0.951 + (0 - 2) -0.260 0.980 -3.909 3.389 + (1 - 0) 4.600 0.014 0.951 8.249 + (1 - 2) 4.340 0.020 0.691 7.989 + (2 - 0) 0.260 0.980 -3.389 3.909 + (2 - 1) -4.340 0.020 -7.989 -0.691 + + The null hypothesis is that each group has the same mean. The p-value for + comparisons between ``group0`` and ``group1`` as well as ``group1`` and + ``group2`` do not exceed .05, so we reject the null hypothesis that they + have the same means. The p-value of the comparison between ``group0`` + and ``group2`` exceeds .05, so we accept the null hypothesis that there + is not a significant difference between their means. + + We can also compute the confidence interval associated with our chosen + confidence level. + + >>> group0 = [24.5, 23.5, 26.4, 27.1, 29.9] + >>> group1 = [28.4, 34.2, 29.5, 32.2, 30.1] + >>> group2 = [26.1, 28.3, 24.3, 26.2, 27.8] + >>> result = tukey_hsd(group0, group1, group2) + >>> conf = res.confidence_interval(confidence_level=.99) + >>> for ((i, j), l) in np.ndenumerate(conf.low): + ... # filter out self comparisons + ... if i != j: + ... h = conf.high[i,j] + ... print(f"({i} - {j}) {l:>6.3f} {h:>6.3f}") + (0 - 1) -9.480 0.280 + (0 - 2) -5.140 4.620 + (1 - 0) -0.280 9.480 + (1 - 2) -0.540 9.220 + (2 - 0) -4.620 5.140 + (2 - 1) -9.220 0.540 + """ + args = _tukey_hsd_iv(args) + ntreatments = len(args) + means = np.asarray([np.mean(arg) for arg in args]) + nsamples_treatments = np.asarray([a.size for a in args]) + nobs = np.sum(nsamples_treatments) + + # determine mean square error [5]. Note that this is sometimes called + # mean square error within. + mse = (np.sum([np.var(arg, ddof=1) for arg in args] * + (nsamples_treatments - 1)) / (nobs - ntreatments)) + + # The calculation of the standard error differs when treatments differ in + # size. See ("Unequal sample sizes")[1]. + if np.unique(nsamples_treatments).size == 1: + # all input groups are the same length, so only one value needs to be + # calculated [1]. + normalize = 2 / nsamples_treatments[0] + else: + # to compare groups of differing sizes, we must compute a variance + # value for each individual comparison. Use broadcasting to get the + # resulting matrix. [3], verified against [4] (page 308). + normalize = 1 / nsamples_treatments + 1 / nsamples_treatments[None].T + + # the standard error is used in the computation of the tukey criterion and + # finding the p-values. + stand_err = np.sqrt(normalize * mse / 2) + + # the mean difference is the test statistic. + mean_differences = means[None].T - means + + # Calculate the t-statistic to use within the survival function of the + # studentized range to get the p-value. + t_stat = np.abs(mean_differences) / stand_err + + params = t_stat, ntreatments, nobs - ntreatments + pvalues = distributions.studentized_range.sf(*params) + + return TukeyHSDResult(mean_differences, pvalues, ntreatments, + nobs, stand_err) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_kde.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_kde.py new file mode 100644 index 0000000000000000000000000000000000000000..c1e4ed6acc64392a155f1b21b8d63813552a3f99 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_kde.py @@ -0,0 +1,728 @@ +#------------------------------------------------------------------------------- +# +# Define classes for (uni/multi)-variate kernel density estimation. +# +# Currently, only Gaussian kernels are implemented. +# +# Written by: Robert Kern +# +# Date: 2004-08-09 +# +# Modified: 2005-02-10 by Robert Kern. +# Contributed to SciPy +# 2005-10-07 by Robert Kern. +# Some fixes to match the new scipy_core +# +# Copyright 2004-2005 by Enthought, Inc. +# +#------------------------------------------------------------------------------- + +# Standard library imports. +import warnings + +# SciPy imports. +from scipy import linalg, special +from scipy._lib._util import check_random_state + +from numpy import (asarray, atleast_2d, reshape, zeros, newaxis, exp, pi, + sqrt, ravel, power, atleast_1d, squeeze, sum, transpose, + ones, cov) +import numpy as np + +# Local imports. +from . import _mvn +from ._stats import gaussian_kernel_estimate, gaussian_kernel_estimate_log + +# deprecated import to be removed in SciPy 1.13.0 +from scipy.special import logsumexp # noqa: F401 + + +__all__ = ['gaussian_kde'] + + +class gaussian_kde: + """Representation of a kernel-density estimate using Gaussian kernels. + + Kernel density estimation is a way to estimate the probability density + function (PDF) of a random variable in a non-parametric way. + `gaussian_kde` works for both uni-variate and multi-variate data. It + includes automatic bandwidth determination. The estimation works best for + a unimodal distribution; bimodal or multi-modal distributions tend to be + oversmoothed. + + Parameters + ---------- + dataset : array_like + Datapoints to estimate from. In case of univariate data this is a 1-D + array, otherwise a 2-D array with shape (# of dims, # of data). + bw_method : str, scalar or callable, optional + The method used to calculate the estimator bandwidth. This can be + 'scott', 'silverman', a scalar constant or a callable. If a scalar, + this will be used directly as `kde.factor`. If a callable, it should + take a `gaussian_kde` instance as only parameter and return a scalar. + If None (default), 'scott' is used. See Notes for more details. + weights : array_like, optional + weights of datapoints. This must be the same shape as dataset. + If None (default), the samples are assumed to be equally weighted + + Attributes + ---------- + dataset : ndarray + The dataset with which `gaussian_kde` was initialized. + d : int + Number of dimensions. + n : int + Number of datapoints. + neff : int + Effective number of datapoints. + + .. versionadded:: 1.2.0 + factor : float + The bandwidth factor, obtained from `kde.covariance_factor`. The square + of `kde.factor` multiplies the covariance matrix of the data in the kde + estimation. + covariance : ndarray + The covariance matrix of `dataset`, scaled by the calculated bandwidth + (`kde.factor`). + inv_cov : ndarray + The inverse of `covariance`. + + Methods + ------- + evaluate + __call__ + integrate_gaussian + integrate_box_1d + integrate_box + integrate_kde + pdf + logpdf + resample + set_bandwidth + covariance_factor + + Notes + ----- + Bandwidth selection strongly influences the estimate obtained from the KDE + (much more so than the actual shape of the kernel). Bandwidth selection + can be done by a "rule of thumb", by cross-validation, by "plug-in + methods" or by other means; see [3]_, [4]_ for reviews. `gaussian_kde` + uses a rule of thumb, the default is Scott's Rule. + + Scott's Rule [1]_, implemented as `scotts_factor`, is:: + + n**(-1./(d+4)), + + with ``n`` the number of data points and ``d`` the number of dimensions. + In the case of unequally weighted points, `scotts_factor` becomes:: + + neff**(-1./(d+4)), + + with ``neff`` the effective number of datapoints. + Silverman's Rule [2]_, implemented as `silverman_factor`, is:: + + (n * (d + 2) / 4.)**(-1. / (d + 4)). + + or in the case of unequally weighted points:: + + (neff * (d + 2) / 4.)**(-1. / (d + 4)). + + Good general descriptions of kernel density estimation can be found in [1]_ + and [2]_, the mathematics for this multi-dimensional implementation can be + found in [1]_. + + With a set of weighted samples, the effective number of datapoints ``neff`` + is defined by:: + + neff = sum(weights)^2 / sum(weights^2) + + as detailed in [5]_. + + `gaussian_kde` does not currently support data that lies in a + lower-dimensional subspace of the space in which it is expressed. For such + data, consider performing principle component analysis / dimensionality + reduction and using `gaussian_kde` with the transformed data. + + References + ---------- + .. [1] D.W. Scott, "Multivariate Density Estimation: Theory, Practice, and + Visualization", John Wiley & Sons, New York, Chicester, 1992. + .. [2] B.W. Silverman, "Density Estimation for Statistics and Data + Analysis", Vol. 26, Monographs on Statistics and Applied Probability, + Chapman and Hall, London, 1986. + .. [3] B.A. Turlach, "Bandwidth Selection in Kernel Density Estimation: A + Review", CORE and Institut de Statistique, Vol. 19, pp. 1-33, 1993. + .. [4] D.M. Bashtannyk and R.J. Hyndman, "Bandwidth selection for kernel + conditional density estimation", Computational Statistics & Data + Analysis, Vol. 36, pp. 279-298, 2001. + .. [5] Gray P. G., 1969, Journal of the Royal Statistical Society. + Series A (General), 132, 272 + + Examples + -------- + Generate some random two-dimensional data: + + >>> import numpy as np + >>> from scipy import stats + >>> def measure(n): + ... "Measurement model, return two coupled measurements." + ... m1 = np.random.normal(size=n) + ... m2 = np.random.normal(scale=0.5, size=n) + ... return m1+m2, m1-m2 + + >>> m1, m2 = measure(2000) + >>> xmin = m1.min() + >>> xmax = m1.max() + >>> ymin = m2.min() + >>> ymax = m2.max() + + Perform a kernel density estimate on the data: + + >>> X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j] + >>> positions = np.vstack([X.ravel(), Y.ravel()]) + >>> values = np.vstack([m1, m2]) + >>> kernel = stats.gaussian_kde(values) + >>> Z = np.reshape(kernel(positions).T, X.shape) + + Plot the results: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.imshow(np.rot90(Z), cmap=plt.cm.gist_earth_r, + ... extent=[xmin, xmax, ymin, ymax]) + >>> ax.plot(m1, m2, 'k.', markersize=2) + >>> ax.set_xlim([xmin, xmax]) + >>> ax.set_ylim([ymin, ymax]) + >>> plt.show() + + """ + def __init__(self, dataset, bw_method=None, weights=None): + self.dataset = atleast_2d(asarray(dataset)) + if not self.dataset.size > 1: + raise ValueError("`dataset` input should have multiple elements.") + + self.d, self.n = self.dataset.shape + + if weights is not None: + self._weights = atleast_1d(weights).astype(float) + self._weights /= sum(self._weights) + if self.weights.ndim != 1: + raise ValueError("`weights` input should be one-dimensional.") + if len(self._weights) != self.n: + raise ValueError("`weights` input should be of length n") + self._neff = 1/sum(self._weights**2) + + # This can be converted to a warning once gh-10205 is resolved + if self.d > self.n: + msg = ("Number of dimensions is greater than number of samples. " + "This results in a singular data covariance matrix, which " + "cannot be treated using the algorithms implemented in " + "`gaussian_kde`. Note that `gaussian_kde` interprets each " + "*column* of `dataset` to be a point; consider transposing " + "the input to `dataset`.") + raise ValueError(msg) + + try: + self.set_bandwidth(bw_method=bw_method) + except linalg.LinAlgError as e: + msg = ("The data appears to lie in a lower-dimensional subspace " + "of the space in which it is expressed. This has resulted " + "in a singular data covariance matrix, which cannot be " + "treated using the algorithms implemented in " + "`gaussian_kde`. Consider performing principle component " + "analysis / dimensionality reduction and using " + "`gaussian_kde` with the transformed data.") + raise linalg.LinAlgError(msg) from e + + def evaluate(self, points): + """Evaluate the estimated pdf on a set of points. + + Parameters + ---------- + points : (# of dimensions, # of points)-array + Alternatively, a (# of dimensions,) vector can be passed in and + treated as a single point. + + Returns + ------- + values : (# of points,)-array + The values at each point. + + Raises + ------ + ValueError : if the dimensionality of the input points is different than + the dimensionality of the KDE. + + """ + points = atleast_2d(asarray(points)) + + d, m = points.shape + if d != self.d: + if d == 1 and m == self.d: + # points was passed in as a row vector + points = reshape(points, (self.d, 1)) + m = 1 + else: + msg = (f"points have dimension {d}, " + f"dataset has dimension {self.d}") + raise ValueError(msg) + + output_dtype, spec = _get_output_dtype(self.covariance, points) + result = gaussian_kernel_estimate[spec]( + self.dataset.T, self.weights[:, None], + points.T, self.cho_cov, output_dtype) + + return result[:, 0] + + __call__ = evaluate + + def integrate_gaussian(self, mean, cov): + """ + Multiply estimated density by a multivariate Gaussian and integrate + over the whole space. + + Parameters + ---------- + mean : aray_like + A 1-D array, specifying the mean of the Gaussian. + cov : array_like + A 2-D array, specifying the covariance matrix of the Gaussian. + + Returns + ------- + result : scalar + The value of the integral. + + Raises + ------ + ValueError + If the mean or covariance of the input Gaussian differs from + the KDE's dimensionality. + + """ + mean = atleast_1d(squeeze(mean)) + cov = atleast_2d(cov) + + if mean.shape != (self.d,): + raise ValueError("mean does not have dimension %s" % self.d) + if cov.shape != (self.d, self.d): + raise ValueError("covariance does not have dimension %s" % self.d) + + # make mean a column vector + mean = mean[:, newaxis] + + sum_cov = self.covariance + cov + + # This will raise LinAlgError if the new cov matrix is not s.p.d + # cho_factor returns (ndarray, bool) where bool is a flag for whether + # or not ndarray is upper or lower triangular + sum_cov_chol = linalg.cho_factor(sum_cov) + + diff = self.dataset - mean + tdiff = linalg.cho_solve(sum_cov_chol, diff) + + sqrt_det = np.prod(np.diagonal(sum_cov_chol[0])) + norm_const = power(2 * pi, sum_cov.shape[0] / 2.0) * sqrt_det + + energies = sum(diff * tdiff, axis=0) / 2.0 + result = sum(exp(-energies)*self.weights, axis=0) / norm_const + + return result + + def integrate_box_1d(self, low, high): + """ + Computes the integral of a 1D pdf between two bounds. + + Parameters + ---------- + low : scalar + Lower bound of integration. + high : scalar + Upper bound of integration. + + Returns + ------- + value : scalar + The result of the integral. + + Raises + ------ + ValueError + If the KDE is over more than one dimension. + + """ + if self.d != 1: + raise ValueError("integrate_box_1d() only handles 1D pdfs") + + stdev = ravel(sqrt(self.covariance))[0] + + normalized_low = ravel((low - self.dataset) / stdev) + normalized_high = ravel((high - self.dataset) / stdev) + + value = np.sum(self.weights*( + special.ndtr(normalized_high) - + special.ndtr(normalized_low))) + return value + + def integrate_box(self, low_bounds, high_bounds, maxpts=None): + """Computes the integral of a pdf over a rectangular interval. + + Parameters + ---------- + low_bounds : array_like + A 1-D array containing the lower bounds of integration. + high_bounds : array_like + A 1-D array containing the upper bounds of integration. + maxpts : int, optional + The maximum number of points to use for integration. + + Returns + ------- + value : scalar + The result of the integral. + + """ + if maxpts is not None: + extra_kwds = {'maxpts': maxpts} + else: + extra_kwds = {} + + value, inform = _mvn.mvnun_weighted(low_bounds, high_bounds, + self.dataset, self.weights, + self.covariance, **extra_kwds) + if inform: + msg = ('An integral in _mvn.mvnun requires more points than %s' % + (self.d * 1000)) + warnings.warn(msg, stacklevel=2) + + return value + + def integrate_kde(self, other): + """ + Computes the integral of the product of this kernel density estimate + with another. + + Parameters + ---------- + other : gaussian_kde instance + The other kde. + + Returns + ------- + value : scalar + The result of the integral. + + Raises + ------ + ValueError + If the KDEs have different dimensionality. + + """ + if other.d != self.d: + raise ValueError("KDEs are not the same dimensionality") + + # we want to iterate over the smallest number of points + if other.n < self.n: + small = other + large = self + else: + small = self + large = other + + sum_cov = small.covariance + large.covariance + sum_cov_chol = linalg.cho_factor(sum_cov) + result = 0.0 + for i in range(small.n): + mean = small.dataset[:, i, newaxis] + diff = large.dataset - mean + tdiff = linalg.cho_solve(sum_cov_chol, diff) + + energies = sum(diff * tdiff, axis=0) / 2.0 + result += sum(exp(-energies)*large.weights, axis=0)*small.weights[i] + + sqrt_det = np.prod(np.diagonal(sum_cov_chol[0])) + norm_const = power(2 * pi, sum_cov.shape[0] / 2.0) * sqrt_det + + result /= norm_const + + return result + + def resample(self, size=None, seed=None): + """Randomly sample a dataset from the estimated pdf. + + Parameters + ---------- + size : int, optional + The number of samples to draw. If not provided, then the size is + the same as the effective number of samples in the underlying + dataset. + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + resample : (self.d, `size`) ndarray + The sampled dataset. + + """ # numpy/numpydoc#87 # noqa: E501 + if size is None: + size = int(self.neff) + + random_state = check_random_state(seed) + norm = transpose(random_state.multivariate_normal( + zeros((self.d,), float), self.covariance, size=size + )) + indices = random_state.choice(self.n, size=size, p=self.weights) + means = self.dataset[:, indices] + + return means + norm + + def scotts_factor(self): + """Compute Scott's factor. + + Returns + ------- + s : float + Scott's factor. + """ + return power(self.neff, -1./(self.d+4)) + + def silverman_factor(self): + """Compute the Silverman factor. + + Returns + ------- + s : float + The silverman factor. + """ + return power(self.neff*(self.d+2.0)/4.0, -1./(self.d+4)) + + # Default method to calculate bandwidth, can be overwritten by subclass + covariance_factor = scotts_factor + covariance_factor.__doc__ = """Computes the coefficient (`kde.factor`) that + multiplies the data covariance matrix to obtain the kernel covariance + matrix. The default is `scotts_factor`. A subclass can overwrite this + method to provide a different method, or set it through a call to + `kde.set_bandwidth`.""" + + def set_bandwidth(self, bw_method=None): + """Compute the estimator bandwidth with given method. + + The new bandwidth calculated after a call to `set_bandwidth` is used + for subsequent evaluations of the estimated density. + + Parameters + ---------- + bw_method : str, scalar or callable, optional + The method used to calculate the estimator bandwidth. This can be + 'scott', 'silverman', a scalar constant or a callable. If a + scalar, this will be used directly as `kde.factor`. If a callable, + it should take a `gaussian_kde` instance as only parameter and + return a scalar. If None (default), nothing happens; the current + `kde.covariance_factor` method is kept. + + Notes + ----- + .. versionadded:: 0.11 + + Examples + -------- + >>> import numpy as np + >>> import scipy.stats as stats + >>> x1 = np.array([-7, -5, 1, 4, 5.]) + >>> kde = stats.gaussian_kde(x1) + >>> xs = np.linspace(-10, 10, num=50) + >>> y1 = kde(xs) + >>> kde.set_bandwidth(bw_method='silverman') + >>> y2 = kde(xs) + >>> kde.set_bandwidth(bw_method=kde.factor / 3.) + >>> y3 = kde(xs) + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.plot(x1, np.full(x1.shape, 1 / (4. * x1.size)), 'bo', + ... label='Data points (rescaled)') + >>> ax.plot(xs, y1, label='Scott (default)') + >>> ax.plot(xs, y2, label='Silverman') + >>> ax.plot(xs, y3, label='Const (1/3 * Silverman)') + >>> ax.legend() + >>> plt.show() + + """ + if bw_method is None: + pass + elif bw_method == 'scott': + self.covariance_factor = self.scotts_factor + elif bw_method == 'silverman': + self.covariance_factor = self.silverman_factor + elif np.isscalar(bw_method) and not isinstance(bw_method, str): + self._bw_method = 'use constant' + self.covariance_factor = lambda: bw_method + elif callable(bw_method): + self._bw_method = bw_method + self.covariance_factor = lambda: self._bw_method(self) + else: + msg = "`bw_method` should be 'scott', 'silverman', a scalar " \ + "or a callable." + raise ValueError(msg) + + self._compute_covariance() + + def _compute_covariance(self): + """Computes the covariance matrix for each Gaussian kernel using + covariance_factor(). + """ + self.factor = self.covariance_factor() + # Cache covariance and Cholesky decomp of covariance + if not hasattr(self, '_data_cho_cov'): + self._data_covariance = atleast_2d(cov(self.dataset, rowvar=1, + bias=False, + aweights=self.weights)) + self._data_cho_cov = linalg.cholesky(self._data_covariance, + lower=True) + + self.covariance = self._data_covariance * self.factor**2 + self.cho_cov = (self._data_cho_cov * self.factor).astype(np.float64) + self.log_det = 2*np.log(np.diag(self.cho_cov + * np.sqrt(2*pi))).sum() + + @property + def inv_cov(self): + # Re-compute from scratch each time because I'm not sure how this is + # used in the wild. (Perhaps users change the `dataset`, since it's + # not a private attribute?) `_compute_covariance` used to recalculate + # all these, so we'll recalculate everything now that this is a + # a property. + self.factor = self.covariance_factor() + self._data_covariance = atleast_2d(cov(self.dataset, rowvar=1, + bias=False, aweights=self.weights)) + return linalg.inv(self._data_covariance) / self.factor**2 + + def pdf(self, x): + """ + Evaluate the estimated pdf on a provided set of points. + + Notes + ----- + This is an alias for `gaussian_kde.evaluate`. See the ``evaluate`` + docstring for more details. + + """ + return self.evaluate(x) + + def logpdf(self, x): + """ + Evaluate the log of the estimated pdf on a provided set of points. + """ + points = atleast_2d(x) + + d, m = points.shape + if d != self.d: + if d == 1 and m == self.d: + # points was passed in as a row vector + points = reshape(points, (self.d, 1)) + m = 1 + else: + msg = (f"points have dimension {d}, " + f"dataset has dimension {self.d}") + raise ValueError(msg) + + output_dtype, spec = _get_output_dtype(self.covariance, points) + result = gaussian_kernel_estimate_log[spec]( + self.dataset.T, self.weights[:, None], + points.T, self.cho_cov, output_dtype) + + return result[:, 0] + + def marginal(self, dimensions): + """Return a marginal KDE distribution + + Parameters + ---------- + dimensions : int or 1-d array_like + The dimensions of the multivariate distribution corresponding + with the marginal variables, that is, the indices of the dimensions + that are being retained. The other dimensions are marginalized out. + + Returns + ------- + marginal_kde : gaussian_kde + An object representing the marginal distribution. + + Notes + ----- + .. versionadded:: 1.10.0 + + """ + + dims = np.atleast_1d(dimensions) + + if not np.issubdtype(dims.dtype, np.integer): + msg = ("Elements of `dimensions` must be integers - the indices " + "of the marginal variables being retained.") + raise ValueError(msg) + + n = len(self.dataset) # number of dimensions + original_dims = dims.copy() + + dims[dims < 0] = n + dims[dims < 0] + + if len(np.unique(dims)) != len(dims): + msg = ("All elements of `dimensions` must be unique.") + raise ValueError(msg) + + i_invalid = (dims < 0) | (dims >= n) + if np.any(i_invalid): + msg = (f"Dimensions {original_dims[i_invalid]} are invalid " + f"for a distribution in {n} dimensions.") + raise ValueError(msg) + + dataset = self.dataset[dims] + weights = self.weights + + return gaussian_kde(dataset, bw_method=self.covariance_factor(), + weights=weights) + + @property + def weights(self): + try: + return self._weights + except AttributeError: + self._weights = ones(self.n)/self.n + return self._weights + + @property + def neff(self): + try: + return self._neff + except AttributeError: + self._neff = 1/sum(self.weights**2) + return self._neff + + +def _get_output_dtype(covariance, points): + """ + Calculates the output dtype and the "spec" (=C type name). + + This was necessary in order to deal with the fused types in the Cython + routine `gaussian_kernel_estimate`. See gh-10824 for details. + """ + output_dtype = np.common_type(covariance, points) + itemsize = np.dtype(output_dtype).itemsize + if itemsize == 4: + spec = 'float' + elif itemsize == 8: + spec = 'double' + elif itemsize in (12, 16): + spec = 'long double' + else: + raise ValueError( + f"{output_dtype} has unexpected item size: {itemsize}" + ) + + return output_dtype, spec diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_ksstats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_ksstats.py new file mode 100644 index 0000000000000000000000000000000000000000..2bc60da7bba862a1b16f4b41c66e523f985ac415 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_ksstats.py @@ -0,0 +1,600 @@ +# Compute the two-sided one-sample Kolmogorov-Smirnov Prob(Dn <= d) where: +# D_n = sup_x{|F_n(x) - F(x)|}, +# F_n(x) is the empirical CDF for a sample of size n {x_i: i=1,...,n}, +# F(x) is the CDF of a probability distribution. +# +# Exact methods: +# Prob(D_n >= d) can be computed via a matrix algorithm of Durbin[1] +# or a recursion algorithm due to Pomeranz[2]. +# Marsaglia, Tsang & Wang[3] gave a computation-efficient way to perform +# the Durbin algorithm. +# D_n >= d <==> D_n+ >= d or D_n- >= d (the one-sided K-S statistics), hence +# Prob(D_n >= d) = 2*Prob(D_n+ >= d) - Prob(D_n+ >= d and D_n- >= d). +# For d > 0.5, the latter intersection probability is 0. +# +# Approximate methods: +# For d close to 0.5, ignoring that intersection term may still give a +# reasonable approximation. +# Li-Chien[4] and Korolyuk[5] gave an asymptotic formula extending +# Kolmogorov's initial asymptotic, suitable for large d. (See +# scipy.special.kolmogorov for that asymptotic) +# Pelz-Good[6] used the functional equation for Jacobi theta functions to +# transform the Li-Chien/Korolyuk formula produce a computational formula +# suitable for small d. +# +# Simard and L'Ecuyer[7] provided an algorithm to decide when to use each of +# the above approaches and it is that which is used here. +# +# Other approaches: +# Carvalho[8] optimizes Durbin's matrix algorithm for large values of d. +# Moscovich and Nadler[9] use FFTs to compute the convolutions. + +# References: +# [1] Durbin J (1968). +# "The Probability that the Sample Distribution Function Lies Between Two +# Parallel Straight Lines." +# Annals of Mathematical Statistics, 39, 398-411. +# [2] Pomeranz J (1974). +# "Exact Cumulative Distribution of the Kolmogorov-Smirnov Statistic for +# Small Samples (Algorithm 487)." +# Communications of the ACM, 17(12), 703-704. +# [3] Marsaglia G, Tsang WW, Wang J (2003). +# "Evaluating Kolmogorov's Distribution." +# Journal of Statistical Software, 8(18), 1-4. +# [4] LI-CHIEN, C. (1956). +# "On the exact distribution of the statistics of A. N. Kolmogorov and +# their asymptotic expansion." +# Acta Matematica Sinica, 6, 55-81. +# [5] KOROLYUK, V. S. (1960). +# "Asymptotic analysis of the distribution of the maximum deviation in +# the Bernoulli scheme." +# Theor. Probability Appl., 4, 339-366. +# [6] Pelz W, Good IJ (1976). +# "Approximating the Lower Tail-areas of the Kolmogorov-Smirnov One-sample +# Statistic." +# Journal of the Royal Statistical Society, Series B, 38(2), 152-156. +# [7] Simard, R., L'Ecuyer, P. (2011) +# "Computing the Two-Sided Kolmogorov-Smirnov Distribution", +# Journal of Statistical Software, Vol 39, 11, 1-18. +# [8] Carvalho, Luis (2015) +# "An Improved Evaluation of Kolmogorov's Distribution" +# Journal of Statistical Software, Code Snippets; Vol 65(3), 1-8. +# [9] Amit Moscovich, Boaz Nadler (2017) +# "Fast calculation of boundary crossing probabilities for Poisson +# processes", +# Statistics & Probability Letters, Vol 123, 177-182. + + +import numpy as np +import scipy.special +import scipy.special._ufuncs as scu +from scipy._lib._finite_differences import _derivative + +_E128 = 128 +_EP128 = np.ldexp(np.longdouble(1), _E128) +_EM128 = np.ldexp(np.longdouble(1), -_E128) + +_SQRT2PI = np.sqrt(2 * np.pi) +_LOG_2PI = np.log(2 * np.pi) +_MIN_LOG = -708 +_SQRT3 = np.sqrt(3) +_PI_SQUARED = np.pi ** 2 +_PI_FOUR = np.pi ** 4 +_PI_SIX = np.pi ** 6 + +# [Lifted from _loggamma.pxd.] If B_m are the Bernoulli numbers, +# then Stirling coeffs are B_{2j}/(2j)/(2j-1) for j=8,...1. +_STIRLING_COEFFS = [-2.955065359477124183e-2, 6.4102564102564102564e-3, + -1.9175269175269175269e-3, 8.4175084175084175084e-4, + -5.952380952380952381e-4, 7.9365079365079365079e-4, + -2.7777777777777777778e-3, 8.3333333333333333333e-2] + + +def _log_nfactorial_div_n_pow_n(n): + # Computes n! / n**n + # = (n-1)! / n**(n-1) + # Uses Stirling's approximation, but removes n*log(n) up-front to + # avoid subtractive cancellation. + # = log(n)/2 - n + log(sqrt(2pi)) + sum B_{2j}/(2j)/(2j-1)/n**(2j-1) + rn = 1.0/n + return np.log(n)/2 - n + _LOG_2PI/2 + rn * np.polyval(_STIRLING_COEFFS, rn/n) + + +def _clip_prob(p): + """clips a probability to range 0<=p<=1.""" + return np.clip(p, 0.0, 1.0) + + +def _select_and_clip_prob(cdfprob, sfprob, cdf=True): + """Selects either the CDF or SF, and then clips to range 0<=p<=1.""" + p = np.where(cdf, cdfprob, sfprob) + return _clip_prob(p) + + +def _kolmogn_DMTW(n, d, cdf=True): + r"""Computes the Kolmogorov CDF: Pr(D_n <= d) using the MTW approach to + the Durbin matrix algorithm. + + Durbin (1968); Marsaglia, Tsang, Wang (2003). [1], [3]. + """ + # Write d = (k-h)/n, where k is positive integer and 0 <= h < 1 + # Generate initial matrix H of size m*m where m=(2k-1) + # Compute k-th row of (n!/n^n) * H^n, scaling intermediate results. + # Requires memory O(m^2) and computation O(m^2 log(n)). + # Most suitable for small m. + + if d >= 1.0: + return _select_and_clip_prob(1.0, 0.0, cdf) + nd = n * d + if nd <= 0.5: + return _select_and_clip_prob(0.0, 1.0, cdf) + k = int(np.ceil(nd)) + h = k - nd + m = 2 * k - 1 + + H = np.zeros([m, m]) + + # Initialize: v is first column (and last row) of H + # v[j] = (1-h^(j+1)/(j+1)! (except for v[-1]) + # w[j] = 1/(j)! + # q = k-th row of H (actually i!/n^i*H^i) + intm = np.arange(1, m + 1) + v = 1.0 - h ** intm + w = np.empty(m) + fac = 1.0 + for j in intm: + w[j - 1] = fac + fac /= j # This might underflow. Isn't a problem. + v[j - 1] *= fac + tt = max(2 * h - 1.0, 0)**m - 2*h**m + v[-1] = (1.0 + tt) * fac + + for i in range(1, m): + H[i - 1:, i] = w[:m - i + 1] + H[:, 0] = v + H[-1, :] = np.flip(v, axis=0) + + Hpwr = np.eye(np.shape(H)[0]) # Holds intermediate powers of H + nn = n + expnt = 0 # Scaling of Hpwr + Hexpnt = 0 # Scaling of H + while nn > 0: + if nn % 2: + Hpwr = np.matmul(Hpwr, H) + expnt += Hexpnt + H = np.matmul(H, H) + Hexpnt *= 2 + # Scale as needed. + if np.abs(H[k - 1, k - 1]) > _EP128: + H /= _EP128 + Hexpnt += _E128 + nn = nn // 2 + + p = Hpwr[k - 1, k - 1] + + # Multiply by n!/n^n + for i in range(1, n + 1): + p = i * p / n + if np.abs(p) < _EM128: + p *= _EP128 + expnt -= _E128 + + # unscale + if expnt != 0: + p = np.ldexp(p, expnt) + + return _select_and_clip_prob(p, 1.0-p, cdf) + + +def _pomeranz_compute_j1j2(i, n, ll, ceilf, roundf): + """Compute the endpoints of the interval for row i.""" + if i == 0: + j1, j2 = -ll - ceilf - 1, ll + ceilf - 1 + else: + # i + 1 = 2*ip1div2 + ip1mod2 + ip1div2, ip1mod2 = divmod(i + 1, 2) + if ip1mod2 == 0: # i is odd + if ip1div2 == n + 1: + j1, j2 = n - ll - ceilf - 1, n + ll + ceilf - 1 + else: + j1, j2 = ip1div2 - 1 - ll - roundf - 1, ip1div2 + ll - 1 + ceilf - 1 + else: + j1, j2 = ip1div2 - 1 - ll - 1, ip1div2 + ll + roundf - 1 + + return max(j1 + 2, 0), min(j2, n) + + +def _kolmogn_Pomeranz(n, x, cdf=True): + r"""Computes Pr(D_n <= d) using the Pomeranz recursion algorithm. + + Pomeranz (1974) [2] + """ + + # V is n*(2n+2) matrix. + # Each row is convolution of the previous row and probabilities from a + # Poisson distribution. + # Desired CDF probability is n! V[n-1, 2n+1] (final entry in final row). + # Only two rows are needed at any given stage: + # - Call them V0 and V1. + # - Swap each iteration + # Only a few (contiguous) entries in each row can be non-zero. + # - Keep track of start and end (j1 and j2 below) + # - V0s and V1s track the start in the two rows + # Scale intermediate results as needed. + # Only a few different Poisson distributions can occur + t = n * x + ll = int(np.floor(t)) + f = 1.0 * (t - ll) # fractional part of t + g = min(f, 1.0 - f) + ceilf = (1 if f > 0 else 0) + roundf = (1 if f > 0.5 else 0) + npwrs = 2 * (ll + 1) # Maximum number of powers needed in convolutions + gpower = np.empty(npwrs) # gpower = (g/n)^m/m! + twogpower = np.empty(npwrs) # twogpower = (2g/n)^m/m! + onem2gpower = np.empty(npwrs) # onem2gpower = ((1-2g)/n)^m/m! + # gpower etc are *almost* Poisson probs, just missing normalizing factor. + + gpower[0] = 1.0 + twogpower[0] = 1.0 + onem2gpower[0] = 1.0 + expnt = 0 + g_over_n, two_g_over_n, one_minus_two_g_over_n = g/n, 2*g/n, (1 - 2*g)/n + for m in range(1, npwrs): + gpower[m] = gpower[m - 1] * g_over_n / m + twogpower[m] = twogpower[m - 1] * two_g_over_n / m + onem2gpower[m] = onem2gpower[m - 1] * one_minus_two_g_over_n / m + + V0 = np.zeros([npwrs]) + V1 = np.zeros([npwrs]) + V1[0] = 1 # first row + V0s, V1s = 0, 0 # start indices of the two rows + + j1, j2 = _pomeranz_compute_j1j2(0, n, ll, ceilf, roundf) + for i in range(1, 2 * n + 2): + # Preserve j1, V1, V1s, V0s from last iteration + k1 = j1 + V0, V1 = V1, V0 + V0s, V1s = V1s, V0s + V1.fill(0.0) + j1, j2 = _pomeranz_compute_j1j2(i, n, ll, ceilf, roundf) + if i == 1 or i == 2 * n + 1: + pwrs = gpower + else: + pwrs = (twogpower if i % 2 else onem2gpower) + ln2 = j2 - k1 + 1 + if ln2 > 0: + conv = np.convolve(V0[k1 - V0s:k1 - V0s + ln2], pwrs[:ln2]) + conv_start = j1 - k1 # First index to use from conv + conv_len = j2 - j1 + 1 # Number of entries to use from conv + V1[:conv_len] = conv[conv_start:conv_start + conv_len] + # Scale to avoid underflow. + if 0 < np.max(V1) < _EM128: + V1 *= _EP128 + expnt -= _E128 + V1s = V0s + j1 - k1 + + # multiply by n! + ans = V1[n - V1s] + for m in range(1, n + 1): + if np.abs(ans) > _EP128: + ans *= _EM128 + expnt += _E128 + ans *= m + + # Undo any intermediate scaling + if expnt != 0: + ans = np.ldexp(ans, expnt) + ans = _select_and_clip_prob(ans, 1.0 - ans, cdf) + return ans + + +def _kolmogn_PelzGood(n, x, cdf=True): + """Computes the Pelz-Good approximation to Prob(Dn <= x) with 0<=x<=1. + + Start with Li-Chien, Korolyuk approximation: + Prob(Dn <= x) ~ K0(z) + K1(z)/sqrt(n) + K2(z)/n + K3(z)/n**1.5 + where z = x*sqrt(n). + Transform each K_(z) using Jacobi theta functions into a form suitable + for small z. + Pelz-Good (1976). [6] + """ + if x <= 0.0: + return _select_and_clip_prob(0.0, 1.0, cdf=cdf) + if x >= 1.0: + return _select_and_clip_prob(1.0, 0.0, cdf=cdf) + + z = np.sqrt(n) * x + zsquared, zthree, zfour, zsix = z**2, z**3, z**4, z**6 + + qlog = -_PI_SQUARED / 8 / zsquared + if qlog < _MIN_LOG: # z ~ 0.041743441416853426 + return _select_and_clip_prob(0.0, 1.0, cdf=cdf) + + q = np.exp(qlog) + + # Coefficients of terms in the sums for K1, K2 and K3 + k1a = -zsquared + k1b = _PI_SQUARED / 4 + + k2a = 6 * zsix + 2 * zfour + k2b = (2 * zfour - 5 * zsquared) * _PI_SQUARED / 4 + k2c = _PI_FOUR * (1 - 2 * zsquared) / 16 + + k3d = _PI_SIX * (5 - 30 * zsquared) / 64 + k3c = _PI_FOUR * (-60 * zsquared + 212 * zfour) / 16 + k3b = _PI_SQUARED * (135 * zfour - 96 * zsix) / 4 + k3a = -30 * zsix - 90 * z**8 + + K0to3 = np.zeros(4) + # Use a Horner scheme to evaluate sum c_i q^(i^2) + # Reduces to a sum over odd integers. + maxk = int(np.ceil(16 * z / np.pi)) + for k in range(maxk, 0, -1): + m = 2 * k - 1 + msquared, mfour, msix = m**2, m**4, m**6 + qpower = np.power(q, 8 * k) + coeffs = np.array([1.0, + k1a + k1b*msquared, + k2a + k2b*msquared + k2c*mfour, + k3a + k3b*msquared + k3c*mfour + k3d*msix]) + K0to3 *= qpower + K0to3 += coeffs + K0to3 *= q + K0to3 *= _SQRT2PI + # z**10 > 0 as z > 0.04 + K0to3 /= np.array([z, 6 * zfour, 72 * z**7, 6480 * z**10]) + + # Now do the other sum over the other terms, all integers k + # K_2: (pi^2 k^2) q^(k^2), + # K_3: (3pi^2 k^2 z^2 - pi^4 k^4)*q^(k^2) + # Don't expect much subtractive cancellation so use direct calculation + q = np.exp(-_PI_SQUARED / 2 / zsquared) + ks = np.arange(maxk, 0, -1) + ksquared = ks ** 2 + sqrt3z = _SQRT3 * z + kspi = np.pi * ks + qpwers = q ** ksquared + k2extra = np.sum(ksquared * qpwers) + k2extra *= _PI_SQUARED * _SQRT2PI/(-36 * zthree) + K0to3[2] += k2extra + k3extra = np.sum((sqrt3z + kspi) * (sqrt3z - kspi) * ksquared * qpwers) + k3extra *= _PI_SQUARED * _SQRT2PI/(216 * zsix) + K0to3[3] += k3extra + powers_of_n = np.power(n * 1.0, np.arange(len(K0to3)) / 2.0) + K0to3 /= powers_of_n + + if not cdf: + K0to3 *= -1 + K0to3[0] += 1 + + Ksum = sum(K0to3) + return Ksum + + +def _kolmogn(n, x, cdf=True): + """Computes the CDF(or SF) for the two-sided Kolmogorov-Smirnov statistic. + + x must be of type float, n of type integer. + + Simard & L'Ecuyer (2011) [7]. + """ + if np.isnan(n): + return n # Keep the same type of nan + if int(n) != n or n <= 0: + return np.nan + if x >= 1.0: + return _select_and_clip_prob(1.0, 0.0, cdf=cdf) + if x <= 0.0: + return _select_and_clip_prob(0.0, 1.0, cdf=cdf) + t = n * x + if t <= 1.0: # Ruben-Gambino: 1/2n <= x <= 1/n + if t <= 0.5: + return _select_and_clip_prob(0.0, 1.0, cdf=cdf) + if n <= 140: + prob = np.prod(np.arange(1, n+1) * (1.0/n) * (2*t - 1)) + else: + prob = np.exp(_log_nfactorial_div_n_pow_n(n) + n * np.log(2*t-1)) + return _select_and_clip_prob(prob, 1.0 - prob, cdf=cdf) + if t >= n - 1: # Ruben-Gambino + prob = 2 * (1.0 - x)**n + return _select_and_clip_prob(1 - prob, prob, cdf=cdf) + if x >= 0.5: # Exact: 2 * smirnov + prob = 2 * scipy.special.smirnov(n, x) + return _select_and_clip_prob(1.0 - prob, prob, cdf=cdf) + + nxsquared = t * x + if n <= 140: + if nxsquared <= 0.754693: + prob = _kolmogn_DMTW(n, x, cdf=True) + return _select_and_clip_prob(prob, 1.0 - prob, cdf=cdf) + if nxsquared <= 4: + prob = _kolmogn_Pomeranz(n, x, cdf=True) + return _select_and_clip_prob(prob, 1.0 - prob, cdf=cdf) + # Now use Miller approximation of 2*smirnov + prob = 2 * scipy.special.smirnov(n, x) + return _select_and_clip_prob(1.0 - prob, prob, cdf=cdf) + + # Split CDF and SF as they have different cutoffs on nxsquared. + if not cdf: + if nxsquared >= 370.0: + return 0.0 + if nxsquared >= 2.2: + prob = 2 * scipy.special.smirnov(n, x) + return _clip_prob(prob) + # Fall through and compute the SF as 1.0-CDF + if nxsquared >= 18.0: + cdfprob = 1.0 + elif n <= 100000 and n * x**1.5 <= 1.4: + cdfprob = _kolmogn_DMTW(n, x, cdf=True) + else: + cdfprob = _kolmogn_PelzGood(n, x, cdf=True) + return _select_and_clip_prob(cdfprob, 1.0 - cdfprob, cdf=cdf) + + +def _kolmogn_p(n, x): + """Computes the PDF for the two-sided Kolmogorov-Smirnov statistic. + + x must be of type float, n of type integer. + """ + if np.isnan(n): + return n # Keep the same type of nan + if int(n) != n or n <= 0: + return np.nan + if x >= 1.0 or x <= 0: + return 0 + t = n * x + if t <= 1.0: + # Ruben-Gambino: n!/n^n * (2t-1)^n -> 2 n!/n^n * n^2 * (2t-1)^(n-1) + if t <= 0.5: + return 0.0 + if n <= 140: + prd = np.prod(np.arange(1, n) * (1.0 / n) * (2 * t - 1)) + else: + prd = np.exp(_log_nfactorial_div_n_pow_n(n) + (n-1) * np.log(2 * t - 1)) + return prd * 2 * n**2 + if t >= n - 1: + # Ruben-Gambino : 1-2(1-x)**n -> 2n*(1-x)**(n-1) + return 2 * (1.0 - x) ** (n-1) * n + if x >= 0.5: + return 2 * scipy.stats.ksone.pdf(x, n) + + # Just take a small delta. + # Ideally x +/- delta would stay within [i/n, (i+1)/n] for some integer a. + # as the CDF is a piecewise degree n polynomial. + # It has knots at 1/n, 2/n, ... (n-1)/n + # and is not a C-infinity function at the knots + delta = x / 2.0**16 + delta = min(delta, x - 1.0/n) + delta = min(delta, 0.5 - x) + + def _kk(_x): + return kolmogn(n, _x) + + return _derivative(_kk, x, dx=delta, order=5) + + +def _kolmogni(n, p, q): + """Computes the PPF/ISF of kolmogn. + + n of type integer, n>= 1 + p is the CDF, q the SF, p+q=1 + """ + if np.isnan(n): + return n # Keep the same type of nan + if int(n) != n or n <= 0: + return np.nan + if p <= 0: + return 1.0/n + if q <= 0: + return 1.0 + delta = np.exp((np.log(p) - scipy.special.loggamma(n+1))/n) + if delta <= 1.0/n: + return (delta + 1.0 / n) / 2 + x = -np.expm1(np.log(q/2.0)/n) + if x >= 1 - 1.0/n: + return x + x1 = scu._kolmogci(p)/np.sqrt(n) + x1 = min(x1, 1.0 - 1.0/n) + + def _f(x): + return _kolmogn(n, x) - p + + return scipy.optimize.brentq(_f, 1.0/n, x1, xtol=1e-14) + + +def kolmogn(n, x, cdf=True): + """Computes the CDF for the two-sided Kolmogorov-Smirnov distribution. + + The two-sided Kolmogorov-Smirnov distribution has as its CDF Pr(D_n <= x), + for a sample of size n drawn from a distribution with CDF F(t), where + :math:`D_n &= sup_t |F_n(t) - F(t)|`, and + :math:`F_n(t)` is the Empirical Cumulative Distribution Function of the sample. + + Parameters + ---------- + n : integer, array_like + the number of samples + x : float, array_like + The K-S statistic, float between 0 and 1 + cdf : bool, optional + whether to compute the CDF(default=true) or the SF. + + Returns + ------- + cdf : ndarray + CDF (or SF it cdf is False) at the specified locations. + + The return value has shape the result of numpy broadcasting n and x. + """ + it = np.nditer([n, x, cdf, None], + op_dtypes=[None, np.float64, np.bool_, np.float64]) + for _n, _x, _cdf, z in it: + if np.isnan(_n): + z[...] = _n + continue + if int(_n) != _n: + raise ValueError(f'n is not integral: {_n}') + z[...] = _kolmogn(int(_n), _x, cdf=_cdf) + result = it.operands[-1] + return result + + +def kolmognp(n, x): + """Computes the PDF for the two-sided Kolmogorov-Smirnov distribution. + + Parameters + ---------- + n : integer, array_like + the number of samples + x : float, array_like + The K-S statistic, float between 0 and 1 + + Returns + ------- + pdf : ndarray + The PDF at the specified locations + + The return value has shape the result of numpy broadcasting n and x. + """ + it = np.nditer([n, x, None]) + for _n, _x, z in it: + if np.isnan(_n): + z[...] = _n + continue + if int(_n) != _n: + raise ValueError(f'n is not integral: {_n}') + z[...] = _kolmogn_p(int(_n), _x) + result = it.operands[-1] + return result + + +def kolmogni(n, q, cdf=True): + """Computes the PPF(or ISF) for the two-sided Kolmogorov-Smirnov distribution. + + Parameters + ---------- + n : integer, array_like + the number of samples + q : float, array_like + Probabilities, float between 0 and 1 + cdf : bool, optional + whether to compute the PPF(default=true) or the ISF. + + Returns + ------- + ppf : ndarray + PPF (or ISF if cdf is False) at the specified locations + + The return value has shape the result of numpy broadcasting n and x. + """ + it = np.nditer([n, q, cdf, None]) + for _n, _q, _cdf, z in it: + if np.isnan(_n): + z[...] = _n + continue + if int(_n) != _n: + raise ValueError(f'n is not integral: {_n}') + _pcdf, _psf = (_q, 1-_q) if _cdf else (1-_q, _q) + z[...] = _kolmogni(int(_n), _pcdf, _psf) + result = it.operands[-1] + return result diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..13d143e3dd562e76d75c3517d2d999fcf5640c8f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/__init__.py @@ -0,0 +1,1224 @@ +# + +import warnings +from functools import partial + +import numpy as np + +from scipy import optimize +from scipy import integrate +from scipy.integrate._quadrature import _builtincoeffs +from scipy import interpolate +from scipy.interpolate import RectBivariateSpline +import scipy.special as sc +from scipy._lib._util import _lazywhere +from .._distn_infrastructure import rv_continuous, _ShapeInfo +from .._continuous_distns import uniform, expon, _norm_pdf, _norm_cdf +from .levyst import Nolan +from scipy._lib.doccer import inherit_docstring_from + + +__all__ = ["levy_stable", "levy_stable_gen", "pdf_from_cf_with_fft"] + +# Stable distributions are known for various parameterisations +# some being advantageous for numerical considerations and others +# useful due to their location/scale awareness. +# +# Here we follow [NO] convention (see the references in the docstring +# for levy_stable_gen below). +# +# S0 / Z0 / x0 (aka Zoleterav's M) +# S1 / Z1 / x1 +# +# Where S* denotes parameterisation, Z* denotes standardized +# version where gamma = 1, delta = 0 and x* denotes variable. +# +# Scipy's original Stable was a random variate generator. It +# uses S1 and unfortunately is not a location/scale aware. + + +# default numerical integration tolerance +# used for epsrel in piecewise and both epsrel and epsabs in dni +# (epsabs needed in dni since weighted quad requires epsabs > 0) +_QUAD_EPS = 1.2e-14 + + +def _Phi_Z0(alpha, t): + return ( + -np.tan(np.pi * alpha / 2) * (np.abs(t) ** (1 - alpha) - 1) + if alpha != 1 + else -2.0 * np.log(np.abs(t)) / np.pi + ) + + +def _Phi_Z1(alpha, t): + return ( + np.tan(np.pi * alpha / 2) + if alpha != 1 + else -2.0 * np.log(np.abs(t)) / np.pi + ) + + +def _cf(Phi, t, alpha, beta): + """Characteristic function.""" + return np.exp( + -(np.abs(t) ** alpha) * (1 - 1j * beta * np.sign(t) * Phi(alpha, t)) + ) + + +_cf_Z0 = partial(_cf, _Phi_Z0) +_cf_Z1 = partial(_cf, _Phi_Z1) + + +def _pdf_single_value_cf_integrate(Phi, x, alpha, beta, **kwds): + """To improve DNI accuracy convert characteristic function in to real + valued integral using Euler's formula, then exploit cosine symmetry to + change limits to [0, inf). Finally use cosine addition formula to split + into two parts that can be handled by weighted quad pack. + """ + quad_eps = kwds.get("quad_eps", _QUAD_EPS) + + def integrand1(t): + if t == 0: + return 0 + return np.exp(-(t ** alpha)) * ( + np.cos(beta * (t ** alpha) * Phi(alpha, t)) + ) + + def integrand2(t): + if t == 0: + return 0 + return np.exp(-(t ** alpha)) * ( + np.sin(beta * (t ** alpha) * Phi(alpha, t)) + ) + + with np.errstate(invalid="ignore"): + int1, *ret1 = integrate.quad( + integrand1, + 0, + np.inf, + weight="cos", + wvar=x, + limit=1000, + epsabs=quad_eps, + epsrel=quad_eps, + full_output=1, + ) + + int2, *ret2 = integrate.quad( + integrand2, + 0, + np.inf, + weight="sin", + wvar=x, + limit=1000, + epsabs=quad_eps, + epsrel=quad_eps, + full_output=1, + ) + + return (int1 + int2) / np.pi + + +_pdf_single_value_cf_integrate_Z0 = partial( + _pdf_single_value_cf_integrate, _Phi_Z0 +) +_pdf_single_value_cf_integrate_Z1 = partial( + _pdf_single_value_cf_integrate, _Phi_Z1 +) + + +def _nolan_round_x_near_zeta(x0, alpha, zeta, x_tol_near_zeta): + """Round x close to zeta for Nolan's method in [NO].""" + # "8. When |x0-beta*tan(pi*alpha/2)| is small, the + # computations of the density and cumulative have numerical problems. + # The program works around this by setting + # z = beta*tan(pi*alpha/2) when + # |z-beta*tan(pi*alpha/2)| < tol(5)*alpha**(1/alpha). + # (The bound on the right is ad hoc, to get reasonable behavior + # when alpha is small)." + # where tol(5) = 0.5e-2 by default. + # + # We seem to have partially addressed this through re-expression of + # g(theta) here, but it still needs to be used in some extreme cases. + # Perhaps tol(5) = 0.5e-2 could be reduced for our implementation. + if np.abs(x0 - zeta) < x_tol_near_zeta * alpha ** (1 / alpha): + x0 = zeta + return x0 + + +def _nolan_round_difficult_input( + x0, alpha, beta, zeta, x_tol_near_zeta, alpha_tol_near_one +): + """Round difficult input values for Nolan's method in [NO].""" + + # following Nolan's STABLE, + # "1. When 0 < |alpha-1| < 0.005, the program has numerical problems + # evaluating the pdf and cdf. The current version of the program sets + # alpha=1 in these cases. This approximation is not bad in the S0 + # parameterization." + if np.abs(alpha - 1) < alpha_tol_near_one: + alpha = 1.0 + + # "2. When alpha=1 and |beta| < 0.005, the program has numerical + # problems. The current version sets beta=0." + # We seem to have addressed this through re-expression of g(theta) here + + x0 = _nolan_round_x_near_zeta(x0, alpha, zeta, x_tol_near_zeta) + return x0, alpha, beta + + +def _pdf_single_value_piecewise_Z1(x, alpha, beta, **kwds): + # convert from Nolan's S_1 (aka S) to S_0 (aka Zolaterev M) + # parameterization + + zeta = -beta * np.tan(np.pi * alpha / 2.0) + x0 = x + zeta if alpha != 1 else x + + return _pdf_single_value_piecewise_Z0(x0, alpha, beta, **kwds) + + +def _pdf_single_value_piecewise_Z0(x0, alpha, beta, **kwds): + + quad_eps = kwds.get("quad_eps", _QUAD_EPS) + x_tol_near_zeta = kwds.get("piecewise_x_tol_near_zeta", 0.005) + alpha_tol_near_one = kwds.get("piecewise_alpha_tol_near_one", 0.005) + + zeta = -beta * np.tan(np.pi * alpha / 2.0) + x0, alpha, beta = _nolan_round_difficult_input( + x0, alpha, beta, zeta, x_tol_near_zeta, alpha_tol_near_one + ) + + # some other known distribution pdfs / analytical cases + # TODO: add more where possible with test coverage, + # eg https://en.wikipedia.org/wiki/Stable_distribution#Other_analytic_cases + if alpha == 2.0: + # normal + return _norm_pdf(x0 / np.sqrt(2)) / np.sqrt(2) + elif alpha == 0.5 and beta == 1.0: + # levy + # since S(1/2, 1, gamma, delta; ) == + # S(1/2, 1, gamma, gamma + delta; ). + _x = x0 + 1 + if _x <= 0: + return 0 + + return 1 / np.sqrt(2 * np.pi * _x) / _x * np.exp(-1 / (2 * _x)) + elif alpha == 0.5 and beta == 0.0 and x0 != 0: + # analytical solution [HO] + S, C = sc.fresnel([1 / np.sqrt(2 * np.pi * np.abs(x0))]) + arg = 1 / (4 * np.abs(x0)) + return ( + np.sin(arg) * (0.5 - S[0]) + np.cos(arg) * (0.5 - C[0]) + ) / np.sqrt(2 * np.pi * np.abs(x0) ** 3) + elif alpha == 1.0 and beta == 0.0: + # cauchy + return 1 / (1 + x0 ** 2) / np.pi + + return _pdf_single_value_piecewise_post_rounding_Z0( + x0, alpha, beta, quad_eps, x_tol_near_zeta + ) + + +def _pdf_single_value_piecewise_post_rounding_Z0(x0, alpha, beta, quad_eps, + x_tol_near_zeta): + """Calculate pdf using Nolan's methods as detailed in [NO].""" + + _nolan = Nolan(alpha, beta, x0) + zeta = _nolan.zeta + xi = _nolan.xi + c2 = _nolan.c2 + g = _nolan.g + + # round x0 to zeta again if needed. zeta was recomputed and may have + # changed due to floating point differences. + # See https://github.com/scipy/scipy/pull/18133 + x0 = _nolan_round_x_near_zeta(x0, alpha, zeta, x_tol_near_zeta) + # handle Nolan's initial case logic + if x0 == zeta: + return ( + sc.gamma(1 + 1 / alpha) + * np.cos(xi) + / np.pi + / ((1 + zeta ** 2) ** (1 / alpha / 2)) + ) + elif x0 < zeta: + return _pdf_single_value_piecewise_post_rounding_Z0( + -x0, alpha, -beta, quad_eps, x_tol_near_zeta + ) + + # following Nolan, we may now assume + # x0 > zeta when alpha != 1 + # beta != 0 when alpha == 1 + + # spare calculating integral on null set + # use isclose as macos has fp differences + if np.isclose(-xi, np.pi / 2, rtol=1e-014, atol=1e-014): + return 0.0 + + def integrand(theta): + # limit any numerical issues leading to g_1 < 0 near theta limits + g_1 = g(theta) + if not np.isfinite(g_1) or g_1 < 0: + g_1 = 0 + return g_1 * np.exp(-g_1) + + with np.errstate(all="ignore"): + peak = optimize.bisect( + lambda t: g(t) - 1, -xi, np.pi / 2, xtol=quad_eps + ) + + # this integrand can be very peaked, so we need to force + # QUADPACK to evaluate the function inside its support + # + + # lastly, we add additional samples at + # ~exp(-100), ~exp(-10), ~exp(-5), ~exp(-1) + # to improve QUADPACK's detection of rapidly descending tail behavior + # (this choice is fairly ad hoc) + tail_points = [ + optimize.bisect(lambda t: g(t) - exp_height, -xi, np.pi / 2) + for exp_height in [100, 10, 5] + # exp_height = 1 is handled by peak + ] + intg_points = [0, peak] + tail_points + intg, *ret = integrate.quad( + integrand, + -xi, + np.pi / 2, + points=intg_points, + limit=100, + epsrel=quad_eps, + epsabs=0, + full_output=1, + ) + + return c2 * intg + + +def _cdf_single_value_piecewise_Z1(x, alpha, beta, **kwds): + # convert from Nolan's S_1 (aka S) to S_0 (aka Zolaterev M) + # parameterization + + zeta = -beta * np.tan(np.pi * alpha / 2.0) + x0 = x + zeta if alpha != 1 else x + + return _cdf_single_value_piecewise_Z0(x0, alpha, beta, **kwds) + + +def _cdf_single_value_piecewise_Z0(x0, alpha, beta, **kwds): + + quad_eps = kwds.get("quad_eps", _QUAD_EPS) + x_tol_near_zeta = kwds.get("piecewise_x_tol_near_zeta", 0.005) + alpha_tol_near_one = kwds.get("piecewise_alpha_tol_near_one", 0.005) + + zeta = -beta * np.tan(np.pi * alpha / 2.0) + x0, alpha, beta = _nolan_round_difficult_input( + x0, alpha, beta, zeta, x_tol_near_zeta, alpha_tol_near_one + ) + + # some other known distribution cdfs / analytical cases + # TODO: add more where possible with test coverage, + # eg https://en.wikipedia.org/wiki/Stable_distribution#Other_analytic_cases + if alpha == 2.0: + # normal + return _norm_cdf(x0 / np.sqrt(2)) + elif alpha == 0.5 and beta == 1.0: + # levy + # since S(1/2, 1, gamma, delta; ) == + # S(1/2, 1, gamma, gamma + delta; ). + _x = x0 + 1 + if _x <= 0: + return 0 + + return sc.erfc(np.sqrt(0.5 / _x)) + elif alpha == 1.0 and beta == 0.0: + # cauchy + return 0.5 + np.arctan(x0) / np.pi + + return _cdf_single_value_piecewise_post_rounding_Z0( + x0, alpha, beta, quad_eps, x_tol_near_zeta + ) + + +def _cdf_single_value_piecewise_post_rounding_Z0(x0, alpha, beta, quad_eps, + x_tol_near_zeta): + """Calculate cdf using Nolan's methods as detailed in [NO].""" + _nolan = Nolan(alpha, beta, x0) + zeta = _nolan.zeta + xi = _nolan.xi + c1 = _nolan.c1 + # c2 = _nolan.c2 + c3 = _nolan.c3 + g = _nolan.g + # round x0 to zeta again if needed. zeta was recomputed and may have + # changed due to floating point differences. + # See https://github.com/scipy/scipy/pull/18133 + x0 = _nolan_round_x_near_zeta(x0, alpha, zeta, x_tol_near_zeta) + # handle Nolan's initial case logic + if (alpha == 1 and beta < 0) or x0 < zeta: + # NOTE: Nolan's paper has a typo here! + # He states F(x) = 1 - F(x, alpha, -beta), but this is clearly + # incorrect since F(-infty) would be 1.0 in this case + # Indeed, the alpha != 1, x0 < zeta case is correct here. + return 1 - _cdf_single_value_piecewise_post_rounding_Z0( + -x0, alpha, -beta, quad_eps, x_tol_near_zeta + ) + elif x0 == zeta: + return 0.5 - xi / np.pi + + # following Nolan, we may now assume + # x0 > zeta when alpha != 1 + # beta > 0 when alpha == 1 + + # spare calculating integral on null set + # use isclose as macos has fp differences + if np.isclose(-xi, np.pi / 2, rtol=1e-014, atol=1e-014): + return c1 + + def integrand(theta): + g_1 = g(theta) + return np.exp(-g_1) + + with np.errstate(all="ignore"): + # shrink supports where required + left_support = -xi + right_support = np.pi / 2 + if alpha > 1: + # integrand(t) monotonic 0 to 1 + if integrand(-xi) != 0.0: + res = optimize.minimize( + integrand, + (-xi,), + method="L-BFGS-B", + bounds=[(-xi, np.pi / 2)], + ) + left_support = res.x[0] + else: + # integrand(t) monotonic 1 to 0 + if integrand(np.pi / 2) != 0.0: + res = optimize.minimize( + integrand, + (np.pi / 2,), + method="L-BFGS-B", + bounds=[(-xi, np.pi / 2)], + ) + right_support = res.x[0] + + intg, *ret = integrate.quad( + integrand, + left_support, + right_support, + points=[left_support, right_support], + limit=100, + epsrel=quad_eps, + epsabs=0, + full_output=1, + ) + + return c1 + c3 * intg + + +def _rvs_Z1(alpha, beta, size=None, random_state=None): + """Simulate random variables using Nolan's methods as detailed in [NO]. + """ + + def alpha1func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W): + return ( + 2 + / np.pi + * ( + (np.pi / 2 + bTH) * tanTH + - beta * np.log((np.pi / 2 * W * cosTH) / (np.pi / 2 + bTH)) + ) + ) + + def beta0func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W): + return ( + W + / (cosTH / np.tan(aTH) + np.sin(TH)) + * ((np.cos(aTH) + np.sin(aTH) * tanTH) / W) ** (1.0 / alpha) + ) + + def otherwise(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W): + # alpha is not 1 and beta is not 0 + val0 = beta * np.tan(np.pi * alpha / 2) + th0 = np.arctan(val0) / alpha + val3 = W / (cosTH / np.tan(alpha * (th0 + TH)) + np.sin(TH)) + res3 = val3 * ( + ( + np.cos(aTH) + + np.sin(aTH) * tanTH + - val0 * (np.sin(aTH) - np.cos(aTH) * tanTH) + ) + / W + ) ** (1.0 / alpha) + return res3 + + def alphanot1func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W): + res = _lazywhere( + beta == 0, + (alpha, beta, TH, aTH, bTH, cosTH, tanTH, W), + beta0func, + f2=otherwise, + ) + return res + + alpha = np.broadcast_to(alpha, size) + beta = np.broadcast_to(beta, size) + TH = uniform.rvs( + loc=-np.pi / 2.0, scale=np.pi, size=size, random_state=random_state + ) + W = expon.rvs(size=size, random_state=random_state) + aTH = alpha * TH + bTH = beta * TH + cosTH = np.cos(TH) + tanTH = np.tan(TH) + res = _lazywhere( + alpha == 1, + (alpha, beta, TH, aTH, bTH, cosTH, tanTH, W), + alpha1func, + f2=alphanot1func, + ) + return res + + +def _fitstart_S0(data): + alpha, beta, delta1, gamma = _fitstart_S1(data) + + # Formulas for mapping parameters in S1 parameterization to + # those in S0 parameterization can be found in [NO]. Note that + # only delta changes. + if alpha != 1: + delta0 = delta1 + beta * gamma * np.tan(np.pi * alpha / 2.0) + else: + delta0 = delta1 + 2 * beta * gamma * np.log(gamma) / np.pi + + return alpha, beta, delta0, gamma + + +def _fitstart_S1(data): + # We follow McCullock 1986 method - Simple Consistent Estimators + # of Stable Distribution Parameters + + # fmt: off + # Table III and IV + nu_alpha_range = [2.439, 2.5, 2.6, 2.7, 2.8, 3, 3.2, 3.5, 4, + 5, 6, 8, 10, 15, 25] + nu_beta_range = [0, 0.1, 0.2, 0.3, 0.5, 0.7, 1] + + # table III - alpha = psi_1(nu_alpha, nu_beta) + alpha_table = np.array([ + [2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000], + [1.916, 1.924, 1.924, 1.924, 1.924, 1.924, 1.924], + [1.808, 1.813, 1.829, 1.829, 1.829, 1.829, 1.829], + [1.729, 1.730, 1.737, 1.745, 1.745, 1.745, 1.745], + [1.664, 1.663, 1.663, 1.668, 1.676, 1.676, 1.676], + [1.563, 1.560, 1.553, 1.548, 1.547, 1.547, 1.547], + [1.484, 1.480, 1.471, 1.460, 1.448, 1.438, 1.438], + [1.391, 1.386, 1.378, 1.364, 1.337, 1.318, 1.318], + [1.279, 1.273, 1.266, 1.250, 1.210, 1.184, 1.150], + [1.128, 1.121, 1.114, 1.101, 1.067, 1.027, 0.973], + [1.029, 1.021, 1.014, 1.004, 0.974, 0.935, 0.874], + [0.896, 0.892, 0.884, 0.883, 0.855, 0.823, 0.769], + [0.818, 0.812, 0.806, 0.801, 0.780, 0.756, 0.691], + [0.698, 0.695, 0.692, 0.689, 0.676, 0.656, 0.597], + [0.593, 0.590, 0.588, 0.586, 0.579, 0.563, 0.513]]).T + # transpose because interpolation with `RectBivariateSpline` is with + # `nu_beta` as `x` and `nu_alpha` as `y` + + # table IV - beta = psi_2(nu_alpha, nu_beta) + beta_table = np.array([ + [0, 2.160, 1.000, 1.000, 1.000, 1.000, 1.000], + [0, 1.592, 3.390, 1.000, 1.000, 1.000, 1.000], + [0, 0.759, 1.800, 1.000, 1.000, 1.000, 1.000], + [0, 0.482, 1.048, 1.694, 1.000, 1.000, 1.000], + [0, 0.360, 0.760, 1.232, 2.229, 1.000, 1.000], + [0, 0.253, 0.518, 0.823, 1.575, 1.000, 1.000], + [0, 0.203, 0.410, 0.632, 1.244, 1.906, 1.000], + [0, 0.165, 0.332, 0.499, 0.943, 1.560, 1.000], + [0, 0.136, 0.271, 0.404, 0.689, 1.230, 2.195], + [0, 0.109, 0.216, 0.323, 0.539, 0.827, 1.917], + [0, 0.096, 0.190, 0.284, 0.472, 0.693, 1.759], + [0, 0.082, 0.163, 0.243, 0.412, 0.601, 1.596], + [0, 0.074, 0.147, 0.220, 0.377, 0.546, 1.482], + [0, 0.064, 0.128, 0.191, 0.330, 0.478, 1.362], + [0, 0.056, 0.112, 0.167, 0.285, 0.428, 1.274]]).T + + # Table V and VII + # These are ordered with decreasing `alpha_range`; so we will need to + # reverse them as required by RectBivariateSpline. + alpha_range = [2, 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, + 1, 0.9, 0.8, 0.7, 0.6, 0.5][::-1] + beta_range = [0, 0.25, 0.5, 0.75, 1] + + # Table V - nu_c = psi_3(alpha, beta) + nu_c_table = np.array([ + [1.908, 1.908, 1.908, 1.908, 1.908], + [1.914, 1.915, 1.916, 1.918, 1.921], + [1.921, 1.922, 1.927, 1.936, 1.947], + [1.927, 1.930, 1.943, 1.961, 1.987], + [1.933, 1.940, 1.962, 1.997, 2.043], + [1.939, 1.952, 1.988, 2.045, 2.116], + [1.946, 1.967, 2.022, 2.106, 2.211], + [1.955, 1.984, 2.067, 2.188, 2.333], + [1.965, 2.007, 2.125, 2.294, 2.491], + [1.980, 2.040, 2.205, 2.435, 2.696], + [2.000, 2.085, 2.311, 2.624, 2.973], + [2.040, 2.149, 2.461, 2.886, 3.356], + [2.098, 2.244, 2.676, 3.265, 3.912], + [2.189, 2.392, 3.004, 3.844, 4.775], + [2.337, 2.634, 3.542, 4.808, 6.247], + [2.588, 3.073, 4.534, 6.636, 9.144]])[::-1].T + # transpose because interpolation with `RectBivariateSpline` is with + # `beta` as `x` and `alpha` as `y` + + # Table VII - nu_zeta = psi_5(alpha, beta) + nu_zeta_table = np.array([ + [0, 0.000, 0.000, 0.000, 0.000], + [0, -0.017, -0.032, -0.049, -0.064], + [0, -0.030, -0.061, -0.092, -0.123], + [0, -0.043, -0.088, -0.132, -0.179], + [0, -0.056, -0.111, -0.170, -0.232], + [0, -0.066, -0.134, -0.206, -0.283], + [0, -0.075, -0.154, -0.241, -0.335], + [0, -0.084, -0.173, -0.276, -0.390], + [0, -0.090, -0.192, -0.310, -0.447], + [0, -0.095, -0.208, -0.346, -0.508], + [0, -0.098, -0.223, -0.380, -0.576], + [0, -0.099, -0.237, -0.424, -0.652], + [0, -0.096, -0.250, -0.469, -0.742], + [0, -0.089, -0.262, -0.520, -0.853], + [0, -0.078, -0.272, -0.581, -0.997], + [0, -0.061, -0.279, -0.659, -1.198]])[::-1].T + # fmt: on + + psi_1 = RectBivariateSpline(nu_beta_range, nu_alpha_range, + alpha_table, kx=1, ky=1, s=0) + + def psi_1_1(nu_beta, nu_alpha): + return psi_1(nu_beta, nu_alpha) \ + if nu_beta > 0 else psi_1(-nu_beta, nu_alpha) + + psi_2 = RectBivariateSpline(nu_beta_range, nu_alpha_range, + beta_table, kx=1, ky=1, s=0) + + def psi_2_1(nu_beta, nu_alpha): + return psi_2(nu_beta, nu_alpha) \ + if nu_beta > 0 else -psi_2(-nu_beta, nu_alpha) + + phi_3 = RectBivariateSpline(beta_range, alpha_range, nu_c_table, + kx=1, ky=1, s=0) + + def phi_3_1(beta, alpha): + return phi_3(beta, alpha) if beta > 0 else phi_3(-beta, alpha) + + phi_5 = RectBivariateSpline(beta_range, alpha_range, nu_zeta_table, + kx=1, ky=1, s=0) + + def phi_5_1(beta, alpha): + return phi_5(beta, alpha) if beta > 0 else -phi_5(-beta, alpha) + + # quantiles + p05 = np.percentile(data, 5) + p50 = np.percentile(data, 50) + p95 = np.percentile(data, 95) + p25 = np.percentile(data, 25) + p75 = np.percentile(data, 75) + + nu_alpha = (p95 - p05) / (p75 - p25) + nu_beta = (p95 + p05 - 2 * p50) / (p95 - p05) + + if nu_alpha >= 2.439: + eps = np.finfo(float).eps + alpha = np.clip(psi_1_1(nu_beta, nu_alpha)[0, 0], eps, 2.) + beta = np.clip(psi_2_1(nu_beta, nu_alpha)[0, 0], -1.0, 1.0) + else: + alpha = 2.0 + beta = np.sign(nu_beta) + c = (p75 - p25) / phi_3_1(beta, alpha)[0, 0] + zeta = p50 + c * phi_5_1(beta, alpha)[0, 0] + delta = zeta-beta*c*np.tan(np.pi*alpha/2.) if alpha != 1. else zeta + + return (alpha, beta, delta, c) + + +class levy_stable_gen(rv_continuous): + r"""A Levy-stable continuous random variable. + + %(before_notes)s + + See Also + -------- + levy, levy_l, cauchy, norm + + Notes + ----- + The distribution for `levy_stable` has characteristic function: + + .. math:: + + \varphi(t, \alpha, \beta, c, \mu) = + e^{it\mu -|ct|^{\alpha}(1-i\beta\operatorname{sign}(t)\Phi(\alpha, t))} + + where two different parameterizations are supported. The first :math:`S_1`: + + .. math:: + + \Phi = \begin{cases} + \tan \left({\frac {\pi \alpha }{2}}\right)&\alpha \neq 1\\ + -{\frac {2}{\pi }}\log |t|&\alpha =1 + \end{cases} + + The second :math:`S_0`: + + .. math:: + + \Phi = \begin{cases} + -\tan \left({\frac {\pi \alpha }{2}}\right)(|ct|^{1-\alpha}-1) + &\alpha \neq 1\\ + -{\frac {2}{\pi }}\log |ct|&\alpha =1 + \end{cases} + + + The probability density function for `levy_stable` is: + + .. math:: + + f(x) = \frac{1}{2\pi}\int_{-\infty}^\infty \varphi(t)e^{-ixt}\,dt + + where :math:`-\infty < t < \infty`. This integral does not have a known + closed form. + + `levy_stable` generalizes several distributions. Where possible, they + should be used instead. Specifically, when the shape parameters + assume the values in the table below, the corresponding equivalent + distribution should be used. + + ========= ======== =========== + ``alpha`` ``beta`` Equivalent + ========= ======== =========== + 1/2 -1 `levy_l` + 1/2 1 `levy` + 1 0 `cauchy` + 2 any `norm` (with ``scale=sqrt(2)``) + ========= ======== =========== + + Evaluation of the pdf uses Nolan's piecewise integration approach with the + Zolotarev :math:`M` parameterization by default. There is also the option + to use direct numerical integration of the standard parameterization of the + characteristic function or to evaluate by taking the FFT of the + characteristic function. + + The default method can changed by setting the class variable + ``levy_stable.pdf_default_method`` to one of 'piecewise' for Nolan's + approach, 'dni' for direct numerical integration, or 'fft-simpson' for the + FFT based approach. For the sake of backwards compatibility, the methods + 'best' and 'zolotarev' are equivalent to 'piecewise' and the method + 'quadrature' is equivalent to 'dni'. + + The parameterization can be changed by setting the class variable + ``levy_stable.parameterization`` to either 'S0' or 'S1'. + The default is 'S1'. + + To improve performance of piecewise and direct numerical integration one + can specify ``levy_stable.quad_eps`` (defaults to 1.2e-14). This is used + as both the absolute and relative quadrature tolerance for direct numerical + integration and as the relative quadrature tolerance for the piecewise + method. One can also specify ``levy_stable.piecewise_x_tol_near_zeta`` + (defaults to 0.005) for how close x is to zeta before it is considered the + same as x [NO]. The exact check is + ``abs(x0 - zeta) < piecewise_x_tol_near_zeta*alpha**(1/alpha)``. One can + also specify ``levy_stable.piecewise_alpha_tol_near_one`` (defaults to + 0.005) for how close alpha is to 1 before being considered equal to 1. + + To increase accuracy of FFT calculation one can specify + ``levy_stable.pdf_fft_grid_spacing`` (defaults to 0.001) and + ``pdf_fft_n_points_two_power`` (defaults to None which means a value is + calculated that sufficiently covers the input range). + + Further control over FFT calculation is available by setting + ``pdf_fft_interpolation_degree`` (defaults to 3) for spline order and + ``pdf_fft_interpolation_level`` for determining the number of points to use + in the Newton-Cotes formula when approximating the characteristic function + (considered experimental). + + Evaluation of the cdf uses Nolan's piecewise integration approach with the + Zolatarev :math:`S_0` parameterization by default. There is also the option + to evaluate through integration of an interpolated spline of the pdf + calculated by means of the FFT method. The settings affecting FFT + calculation are the same as for pdf calculation. The default cdf method can + be changed by setting ``levy_stable.cdf_default_method`` to either + 'piecewise' or 'fft-simpson'. For cdf calculations the Zolatarev method is + superior in accuracy, so FFT is disabled by default. + + Fitting estimate uses quantile estimation method in [MC]. MLE estimation of + parameters in fit method uses this quantile estimate initially. Note that + MLE doesn't always converge if using FFT for pdf calculations; this will be + the case if alpha <= 1 where the FFT approach doesn't give good + approximations. + + Any non-missing value for the attribute + ``levy_stable.pdf_fft_min_points_threshold`` will set + ``levy_stable.pdf_default_method`` to 'fft-simpson' if a valid + default method is not otherwise set. + + + + .. warning:: + + For pdf calculations FFT calculation is considered experimental. + + For cdf calculations FFT calculation is considered experimental. Use + Zolatarev's method instead (default). + + The probability density above is defined in the "standardized" form. To + shift and/or scale the distribution use the ``loc`` and ``scale`` + parameters. + Generally ``%(name)s.pdf(x, %(shapes)s, loc, scale)`` is identically + equivalent to ``%(name)s.pdf(y, %(shapes)s) / scale`` with + ``y = (x - loc) / scale``, except in the ``S1`` parameterization if + ``alpha == 1``. In that case ``%(name)s.pdf(x, %(shapes)s, loc, scale)`` + is identically equivalent to ``%(name)s.pdf(y, %(shapes)s) / scale`` with + ``y = (x - loc - 2 * beta * scale * np.log(scale) / np.pi) / scale``. + See [NO2]_ Definition 1.8 for more information. + Note that shifting the location of a distribution + does not make it a "noncentral" distribution. + + References + ---------- + .. [MC] McCulloch, J., 1986. Simple consistent estimators of stable + distribution parameters. Communications in Statistics - Simulation and + Computation 15, 11091136. + .. [WZ] Wang, Li and Zhang, Ji-Hong, 2008. Simpson's rule based FFT method + to compute densities of stable distribution. + .. [NO] Nolan, J., 1997. Numerical Calculation of Stable Densities and + distributions Functions. + .. [NO2] Nolan, J., 2018. Stable Distributions: Models for Heavy Tailed + Data. + .. [HO] Hopcraft, K. I., Jakeman, E., Tanner, R. M. J., 1999. Lévy random + walks with fluctuating step number and multiscale behavior. + + %(example)s + + """ + # Configurable options as class variables + # (accessible from self by attribute lookup). + parameterization = "S1" + pdf_default_method = "piecewise" + cdf_default_method = "piecewise" + quad_eps = _QUAD_EPS + piecewise_x_tol_near_zeta = 0.005 + piecewise_alpha_tol_near_one = 0.005 + pdf_fft_min_points_threshold = None + pdf_fft_grid_spacing = 0.001 + pdf_fft_n_points_two_power = None + pdf_fft_interpolation_level = 3 + pdf_fft_interpolation_degree = 3 + + def _argcheck(self, alpha, beta): + return (alpha > 0) & (alpha <= 2) & (beta <= 1) & (beta >= -1) + + def _shape_info(self): + ialpha = _ShapeInfo("alpha", False, (0, 2), (False, True)) + ibeta = _ShapeInfo("beta", False, (-1, 1), (True, True)) + return [ialpha, ibeta] + + def _parameterization(self): + allowed = ("S0", "S1") + pz = self.parameterization + if pz not in allowed: + raise RuntimeError( + f"Parameterization '{pz}' in supported list: {allowed}" + ) + return pz + + @inherit_docstring_from(rv_continuous) + def rvs(self, *args, **kwds): + X1 = super().rvs(*args, **kwds) + + kwds.pop("discrete", None) + kwds.pop("random_state", None) + (alpha, beta), delta, gamma, size = self._parse_args_rvs(*args, **kwds) + + # shift location for this parameterisation (S1) + X1 = np.where( + alpha == 1.0, X1 + 2 * beta * gamma * np.log(gamma) / np.pi, X1 + ) + + if self._parameterization() == "S0": + return np.where( + alpha == 1.0, + X1 - (beta * 2 * gamma * np.log(gamma) / np.pi), + X1 - gamma * beta * np.tan(np.pi * alpha / 2.0), + ) + elif self._parameterization() == "S1": + return X1 + + def _rvs(self, alpha, beta, size=None, random_state=None): + return _rvs_Z1(alpha, beta, size, random_state) + + @inherit_docstring_from(rv_continuous) + def pdf(self, x, *args, **kwds): + # override base class version to correct + # location for S1 parameterization + if self._parameterization() == "S0": + return super().pdf(x, *args, **kwds) + elif self._parameterization() == "S1": + (alpha, beta), delta, gamma = self._parse_args(*args, **kwds) + if np.all(np.reshape(alpha, (1, -1))[0, :] != 1): + return super().pdf(x, *args, **kwds) + else: + # correct location for this parameterisation + x = np.reshape(x, (1, -1))[0, :] + x, alpha, beta = np.broadcast_arrays(x, alpha, beta) + + data_in = np.dstack((x, alpha, beta))[0] + data_out = np.empty(shape=(len(data_in), 1)) + # group data in unique arrays of alpha, beta pairs + uniq_param_pairs = np.unique(data_in[:, 1:], axis=0) + for pair in uniq_param_pairs: + _alpha, _beta = pair + _delta = ( + delta + 2 * _beta * gamma * np.log(gamma) / np.pi + if _alpha == 1.0 + else delta + ) + data_mask = np.all(data_in[:, 1:] == pair, axis=-1) + _x = data_in[data_mask, 0] + data_out[data_mask] = ( + super() + .pdf(_x, _alpha, _beta, loc=_delta, scale=gamma) + .reshape(len(_x), 1) + ) + output = data_out.T[0] + if output.shape == (1,): + return output[0] + return output + + def _pdf(self, x, alpha, beta): + if self._parameterization() == "S0": + _pdf_single_value_piecewise = _pdf_single_value_piecewise_Z0 + _pdf_single_value_cf_integrate = _pdf_single_value_cf_integrate_Z0 + _cf = _cf_Z0 + elif self._parameterization() == "S1": + _pdf_single_value_piecewise = _pdf_single_value_piecewise_Z1 + _pdf_single_value_cf_integrate = _pdf_single_value_cf_integrate_Z1 + _cf = _cf_Z1 + + x = np.asarray(x).reshape(1, -1)[0, :] + + x, alpha, beta = np.broadcast_arrays(x, alpha, beta) + + data_in = np.dstack((x, alpha, beta))[0] + data_out = np.empty(shape=(len(data_in), 1)) + + pdf_default_method_name = self.pdf_default_method + if pdf_default_method_name in ("piecewise", "best", "zolotarev"): + pdf_single_value_method = _pdf_single_value_piecewise + elif pdf_default_method_name in ("dni", "quadrature"): + pdf_single_value_method = _pdf_single_value_cf_integrate + elif ( + pdf_default_method_name == "fft-simpson" + or self.pdf_fft_min_points_threshold is not None + ): + pdf_single_value_method = None + + pdf_single_value_kwds = { + "quad_eps": self.quad_eps, + "piecewise_x_tol_near_zeta": self.piecewise_x_tol_near_zeta, + "piecewise_alpha_tol_near_one": self.piecewise_alpha_tol_near_one, + } + + fft_grid_spacing = self.pdf_fft_grid_spacing + fft_n_points_two_power = self.pdf_fft_n_points_two_power + fft_interpolation_level = self.pdf_fft_interpolation_level + fft_interpolation_degree = self.pdf_fft_interpolation_degree + + # group data in unique arrays of alpha, beta pairs + uniq_param_pairs = np.unique(data_in[:, 1:], axis=0) + for pair in uniq_param_pairs: + data_mask = np.all(data_in[:, 1:] == pair, axis=-1) + data_subset = data_in[data_mask] + if pdf_single_value_method is not None: + data_out[data_mask] = np.array( + [ + pdf_single_value_method( + _x, _alpha, _beta, **pdf_single_value_kwds + ) + for _x, _alpha, _beta in data_subset + ] + ).reshape(len(data_subset), 1) + else: + warnings.warn( + "Density calculations experimental for FFT method." + + " Use combination of piecewise and dni methods instead.", + RuntimeWarning, stacklevel=3, + ) + _alpha, _beta = pair + _x = data_subset[:, (0,)] + + if _alpha < 1.0: + raise RuntimeError( + "FFT method does not work well for alpha less than 1." + ) + + # need enough points to "cover" _x for interpolation + if fft_grid_spacing is None and fft_n_points_two_power is None: + raise ValueError( + "One of fft_grid_spacing or fft_n_points_two_power " + + "needs to be set." + ) + max_abs_x = np.max(np.abs(_x)) + h = ( + 2 ** (3 - fft_n_points_two_power) * max_abs_x + if fft_grid_spacing is None + else fft_grid_spacing + ) + q = ( + np.ceil(np.log(2 * max_abs_x / h) / np.log(2)) + 2 + if fft_n_points_two_power is None + else int(fft_n_points_two_power) + ) + + # for some parameters, the range of x can be quite + # large, let's choose an arbitrary cut off (8GB) to save on + # computer memory. + MAX_Q = 30 + if q > MAX_Q: + raise RuntimeError( + "fft_n_points_two_power has a maximum " + + f"value of {MAX_Q}" + ) + + density_x, density = pdf_from_cf_with_fft( + lambda t: _cf(t, _alpha, _beta), + h=h, + q=q, + level=fft_interpolation_level, + ) + f = interpolate.InterpolatedUnivariateSpline( + density_x, np.real(density), k=fft_interpolation_degree + ) # patch FFT to use cubic + data_out[data_mask] = f(_x) + + return data_out.T[0] + + @inherit_docstring_from(rv_continuous) + def cdf(self, x, *args, **kwds): + # override base class version to correct + # location for S1 parameterization + # NOTE: this is near identical to pdf() above + if self._parameterization() == "S0": + return super().cdf(x, *args, **kwds) + elif self._parameterization() == "S1": + (alpha, beta), delta, gamma = self._parse_args(*args, **kwds) + if np.all(np.reshape(alpha, (1, -1))[0, :] != 1): + return super().cdf(x, *args, **kwds) + else: + # correct location for this parameterisation + x = np.reshape(x, (1, -1))[0, :] + x, alpha, beta = np.broadcast_arrays(x, alpha, beta) + + data_in = np.dstack((x, alpha, beta))[0] + data_out = np.empty(shape=(len(data_in), 1)) + # group data in unique arrays of alpha, beta pairs + uniq_param_pairs = np.unique(data_in[:, 1:], axis=0) + for pair in uniq_param_pairs: + _alpha, _beta = pair + _delta = ( + delta + 2 * _beta * gamma * np.log(gamma) / np.pi + if _alpha == 1.0 + else delta + ) + data_mask = np.all(data_in[:, 1:] == pair, axis=-1) + _x = data_in[data_mask, 0] + data_out[data_mask] = ( + super() + .cdf(_x, _alpha, _beta, loc=_delta, scale=gamma) + .reshape(len(_x), 1) + ) + output = data_out.T[0] + if output.shape == (1,): + return output[0] + return output + + def _cdf(self, x, alpha, beta): + if self._parameterization() == "S0": + _cdf_single_value_piecewise = _cdf_single_value_piecewise_Z0 + _cf = _cf_Z0 + elif self._parameterization() == "S1": + _cdf_single_value_piecewise = _cdf_single_value_piecewise_Z1 + _cf = _cf_Z1 + + x = np.asarray(x).reshape(1, -1)[0, :] + + x, alpha, beta = np.broadcast_arrays(x, alpha, beta) + + data_in = np.dstack((x, alpha, beta))[0] + data_out = np.empty(shape=(len(data_in), 1)) + + cdf_default_method_name = self.cdf_default_method + if cdf_default_method_name == "piecewise": + cdf_single_value_method = _cdf_single_value_piecewise + elif cdf_default_method_name == "fft-simpson": + cdf_single_value_method = None + + cdf_single_value_kwds = { + "quad_eps": self.quad_eps, + "piecewise_x_tol_near_zeta": self.piecewise_x_tol_near_zeta, + "piecewise_alpha_tol_near_one": self.piecewise_alpha_tol_near_one, + } + + fft_grid_spacing = self.pdf_fft_grid_spacing + fft_n_points_two_power = self.pdf_fft_n_points_two_power + fft_interpolation_level = self.pdf_fft_interpolation_level + fft_interpolation_degree = self.pdf_fft_interpolation_degree + + # group data in unique arrays of alpha, beta pairs + uniq_param_pairs = np.unique(data_in[:, 1:], axis=0) + for pair in uniq_param_pairs: + data_mask = np.all(data_in[:, 1:] == pair, axis=-1) + data_subset = data_in[data_mask] + if cdf_single_value_method is not None: + data_out[data_mask] = np.array( + [ + cdf_single_value_method( + _x, _alpha, _beta, **cdf_single_value_kwds + ) + for _x, _alpha, _beta in data_subset + ] + ).reshape(len(data_subset), 1) + else: + warnings.warn( + "Cumulative density calculations experimental for FFT" + + " method. Use piecewise method instead.", + RuntimeWarning, stacklevel=3, + ) + _alpha, _beta = pair + _x = data_subset[:, (0,)] + + # need enough points to "cover" _x for interpolation + if fft_grid_spacing is None and fft_n_points_two_power is None: + raise ValueError( + "One of fft_grid_spacing or fft_n_points_two_power " + + "needs to be set." + ) + max_abs_x = np.max(np.abs(_x)) + h = ( + 2 ** (3 - fft_n_points_two_power) * max_abs_x + if fft_grid_spacing is None + else fft_grid_spacing + ) + q = ( + np.ceil(np.log(2 * max_abs_x / h) / np.log(2)) + 2 + if fft_n_points_two_power is None + else int(fft_n_points_two_power) + ) + + density_x, density = pdf_from_cf_with_fft( + lambda t: _cf(t, _alpha, _beta), + h=h, + q=q, + level=fft_interpolation_level, + ) + f = interpolate.InterpolatedUnivariateSpline( + density_x, np.real(density), k=fft_interpolation_degree + ) + data_out[data_mask] = np.array( + [f.integral(self.a, float(x_1.squeeze())) for x_1 in _x] + ).reshape(data_out[data_mask].shape) + + return data_out.T[0] + + def _fitstart(self, data): + if self._parameterization() == "S0": + _fitstart = _fitstart_S0 + elif self._parameterization() == "S1": + _fitstart = _fitstart_S1 + return _fitstart(data) + + def _stats(self, alpha, beta): + mu = 0 if alpha > 1 else np.nan + mu2 = 2 if alpha == 2 else np.inf + g1 = 0.0 if alpha == 2.0 else np.nan + g2 = 0.0 if alpha == 2.0 else np.nan + return mu, mu2, g1, g2 + + +# cotes numbers - see sequence from http://oeis.org/A100642 +Cotes_table = np.array( + [[], [1]] + [v[2] for v in _builtincoeffs.values()], dtype=object +) +Cotes = np.array( + [ + np.pad(r, (0, len(Cotes_table) - 1 - len(r)), mode='constant') + for r in Cotes_table + ] +) + + +def pdf_from_cf_with_fft(cf, h=0.01, q=9, level=3): + """Calculates pdf from characteristic function. + + Uses fast Fourier transform with Newton-Cotes integration following [WZ]. + Defaults to using Simpson's method (3-point Newton-Cotes integration). + + Parameters + ---------- + cf : callable + Single argument function from float -> complex expressing a + characteristic function for some distribution. + h : Optional[float] + Step size for Newton-Cotes integration. Default: 0.01 + q : Optional[int] + Use 2**q steps when performing Newton-Cotes integration. + The infinite integral in the inverse Fourier transform will then + be restricted to the interval [-2**q * h / 2, 2**q * h / 2]. Setting + the number of steps equal to a power of 2 allows the fft to be + calculated in O(n*log(n)) time rather than O(n**2). + Default: 9 + level : Optional[int] + Calculate integral using n-point Newton-Cotes integration for + n = level. The 3-point Newton-Cotes formula corresponds to Simpson's + rule. Default: 3 + + Returns + ------- + x_l : ndarray + Array of points x at which pdf is estimated. 2**q equally spaced + points from -pi/h up to but not including pi/h. + density : ndarray + Estimated values of pdf corresponding to cf at points in x_l. + + References + ---------- + .. [WZ] Wang, Li and Zhang, Ji-Hong, 2008. Simpson's rule based FFT method + to compute densities of stable distribution. + """ + n = level + N = 2**q + steps = np.arange(0, N) + L = N * h / 2 + x_l = np.pi * (steps - N / 2) / L + if level > 1: + indices = np.arange(n).reshape(n, 1) + s1 = np.sum( + (-1) ** steps * Cotes[n, indices] * np.fft.fft( + (-1)**steps * cf(-L + h * steps + h * indices / (n - 1)) + ) * np.exp( + 1j * np.pi * indices / (n - 1) + - 2 * 1j * np.pi * indices * steps / + (N * (n - 1)) + ), + axis=0 + ) + else: + s1 = (-1) ** steps * Cotes[n, 0] * np.fft.fft( + (-1) ** steps * cf(-L + h * steps) + ) + density = h * s1 / (2 * np.pi * np.sum(Cotes[n])) + return (x_l, density) + + +levy_stable = levy_stable_gen(name="levy_stable") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/levyst.cpython-39-darwin.so b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/levyst.cpython-39-darwin.so new file mode 100644 index 0000000000000000000000000000000000000000..aa9f249ff1d1f40dfb3d5900a06ff5f0d248597f Binary files /dev/null and b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_levy_stable/levyst.cpython-39-darwin.so differ diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mannwhitneyu.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mannwhitneyu.py new file mode 100644 index 0000000000000000000000000000000000000000..a796edf075e70627461927849b3f6fdb0788035f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mannwhitneyu.py @@ -0,0 +1,519 @@ +import numpy as np +from collections import namedtuple +from scipy import special +from scipy import stats +from scipy.stats._stats_py import _rankdata +from ._axis_nan_policy import _axis_nan_policy_factory + + +def _broadcast_concatenate(x, y, axis): + '''Broadcast then concatenate arrays, leaving concatenation axis last''' + x = np.moveaxis(x, axis, -1) + y = np.moveaxis(y, axis, -1) + z = np.broadcast(x[..., 0], y[..., 0]) + x = np.broadcast_to(x, z.shape + (x.shape[-1],)) + y = np.broadcast_to(y, z.shape + (y.shape[-1],)) + z = np.concatenate((x, y), axis=-1) + return x, y, z + + +class _MWU: + '''Distribution of MWU statistic under the null hypothesis''' + # Possible improvement: if m and n are small enough, use integer arithmetic + + def __init__(self): + '''Minimal initializer''' + self._fmnks = -np.ones((1, 1, 1)) + self._recursive = None + + def pmf(self, k, m, n): + + # In practice, `pmf` is never called with k > m*n/2. + # If it were, we'd exploit symmetry here: + # k = np.array(k, copy=True) + # k2 = m*n - k + # i = k2 < k + # k[i] = k2[i] + + if (self._recursive is None and m <= 500 and n <= 500 + or self._recursive): + return self.pmf_recursive(k, m, n) + else: + return self.pmf_iterative(k, m, n) + + def pmf_recursive(self, k, m, n): + '''Probability mass function, recursive version''' + self._resize_fmnks(m, n, np.max(k)) + # could loop over just the unique elements, but probably not worth + # the time to find them + for i in np.ravel(k): + self._f(m, n, i) + return self._fmnks[m, n, k] / special.binom(m + n, m) + + def pmf_iterative(self, k, m, n): + '''Probability mass function, iterative version''' + fmnks = {} + for i in np.ravel(k): + fmnks = _mwu_f_iterative(m, n, i, fmnks) + return (np.array([fmnks[(m, n, ki)] for ki in k]) + / special.binom(m + n, m)) + + def cdf(self, k, m, n): + '''Cumulative distribution function''' + + # In practice, `cdf` is never called with k > m*n/2. + # If it were, we'd exploit symmetry here rather than in `sf` + pmfs = self.pmf(np.arange(0, np.max(k) + 1), m, n) + cdfs = np.cumsum(pmfs) + return cdfs[k] + + def sf(self, k, m, n): + '''Survival function''' + # Note that both CDF and SF include the PMF at k. The p-value is + # calculated from the SF and should include the mass at k, so this + # is desirable + + # Use the fact that the distribution is symmetric; i.e. + # _f(m, n, m*n-k) = _f(m, n, k), and sum from the left + kc = np.asarray(m*n - k) # complement of k + i = k < kc + if np.any(i): + kc[i] = k[i] + cdfs = np.asarray(self.cdf(kc, m, n)) + cdfs[i] = 1. - cdfs[i] + self.pmf(kc[i], m, n) + else: + cdfs = np.asarray(self.cdf(kc, m, n)) + return cdfs[()] + + def _resize_fmnks(self, m, n, k): + '''If necessary, expand the array that remembers PMF values''' + # could probably use `np.pad` but I'm not sure it would save code + shape_old = np.array(self._fmnks.shape) + shape_new = np.array((m+1, n+1, k+1)) + if np.any(shape_new > shape_old): + shape = np.maximum(shape_old, shape_new) + fmnks = -np.ones(shape) # create the new array + m0, n0, k0 = shape_old + fmnks[:m0, :n0, :k0] = self._fmnks # copy remembered values + self._fmnks = fmnks + + def _f(self, m, n, k): + '''Recursive implementation of function of [3] Theorem 2.5''' + + # [3] Theorem 2.5 Line 1 + if k < 0 or m < 0 or n < 0 or k > m*n: + return 0 + + # if already calculated, return the value + if self._fmnks[m, n, k] >= 0: + return self._fmnks[m, n, k] + + if k == 0 and m >= 0 and n >= 0: # [3] Theorem 2.5 Line 2 + fmnk = 1 + else: # [3] Theorem 2.5 Line 3 / Equation 3 + fmnk = self._f(m-1, n, k-n) + self._f(m, n-1, k) + + self._fmnks[m, n, k] = fmnk # remember result + + return fmnk + + +# Maintain state for faster repeat calls to mannwhitneyu w/ method='exact' +_mwu_state = _MWU() + + +def _mwu_f_iterative(m, n, k, fmnks): + '''Iterative implementation of function of [3] Theorem 2.5''' + + def _base_case(m, n, k): + '''Base cases from recursive version''' + + # if already calculated, return the value + if fmnks.get((m, n, k), -1) >= 0: + return fmnks[(m, n, k)] + + # [3] Theorem 2.5 Line 1 + elif k < 0 or m < 0 or n < 0 or k > m*n: + return 0 + + # [3] Theorem 2.5 Line 2 + elif k == 0 and m >= 0 and n >= 0: + return 1 + + return None + + stack = [(m, n, k)] + fmnk = None + + while stack: + # Popping only if necessary would save a tiny bit of time, but NWI. + m, n, k = stack.pop() + + # If we're at a base case, continue (stack unwinds) + fmnk = _base_case(m, n, k) + if fmnk is not None: + fmnks[(m, n, k)] = fmnk + continue + + # If both terms are base cases, continue (stack unwinds) + f1 = _base_case(m-1, n, k-n) + f2 = _base_case(m, n-1, k) + if f1 is not None and f2 is not None: + # [3] Theorem 2.5 Line 3 / Equation 3 + fmnk = f1 + f2 + fmnks[(m, n, k)] = fmnk + continue + + # recurse deeper + stack.append((m, n, k)) + if f1 is None: + stack.append((m-1, n, k-n)) + if f2 is None: + stack.append((m, n-1, k)) + + return fmnks + + +def _get_mwu_z(U, n1, n2, t, axis=0, continuity=True): + '''Standardized MWU statistic''' + # Follows mannwhitneyu [2] + mu = n1 * n2 / 2 + n = n1 + n2 + + # Tie correction according to [2], "Normal approximation and tie correction" + # "A more computationally-efficient form..." + tie_term = (t**3 - t).sum(axis=-1) + s = np.sqrt(n1*n2/12 * ((n + 1) - tie_term/(n*(n-1)))) + + numerator = U - mu + + # Continuity correction. + # Because SF is always used to calculate the p-value, we can always + # _subtract_ 0.5 for the continuity correction. This always increases the + # p-value to account for the rest of the probability mass _at_ q = U. + if continuity: + numerator -= 0.5 + + # no problem evaluating the norm SF at an infinity + with np.errstate(divide='ignore', invalid='ignore'): + z = numerator / s + return z + + +def _mwu_input_validation(x, y, use_continuity, alternative, axis, method): + ''' Input validation and standardization for mannwhitneyu ''' + # Would use np.asarray_chkfinite, but infs are OK + x, y = np.atleast_1d(x), np.atleast_1d(y) + if np.isnan(x).any() or np.isnan(y).any(): + raise ValueError('`x` and `y` must not contain NaNs.') + if np.size(x) == 0 or np.size(y) == 0: + raise ValueError('`x` and `y` must be of nonzero size.') + + bools = {True, False} + if use_continuity not in bools: + raise ValueError(f'`use_continuity` must be one of {bools}.') + + alternatives = {"two-sided", "less", "greater"} + alternative = alternative.lower() + if alternative not in alternatives: + raise ValueError(f'`alternative` must be one of {alternatives}.') + + axis_int = int(axis) + if axis != axis_int: + raise ValueError('`axis` must be an integer.') + + if not isinstance(method, stats.PermutationMethod): + methods = {"asymptotic", "exact", "auto"} + method = method.lower() + if method not in methods: + raise ValueError(f'`method` must be one of {methods}.') + + return x, y, use_continuity, alternative, axis_int, method + + +def _mwu_choose_method(n1, n2, ties): + """Choose method 'asymptotic' or 'exact' depending on input size, ties""" + + # if both inputs are large, asymptotic is OK + if n1 > 8 and n2 > 8: + return "asymptotic" + + # if there are any ties, asymptotic is preferred + if ties: + return "asymptotic" + + return "exact" + + +MannwhitneyuResult = namedtuple('MannwhitneyuResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(MannwhitneyuResult, n_samples=2) +def mannwhitneyu(x, y, use_continuity=True, alternative="two-sided", + axis=0, method="auto"): + r'''Perform the Mann-Whitney U rank test on two independent samples. + + The Mann-Whitney U test is a nonparametric test of the null hypothesis + that the distribution underlying sample `x` is the same as the + distribution underlying sample `y`. It is often used as a test of + difference in location between distributions. + + Parameters + ---------- + x, y : array-like + N-d arrays of samples. The arrays must be broadcastable except along + the dimension given by `axis`. + use_continuity : bool, optional + Whether a continuity correction (1/2) should be applied. + Default is True when `method` is ``'asymptotic'``; has no effect + otherwise. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + Let *F(u)* and *G(u)* be the cumulative distribution functions of the + distributions underlying `x` and `y`, respectively. Then the following + alternative hypotheses are available: + + * 'two-sided': the distributions are not equal, i.e. *F(u) ≠ G(u)* for + at least one *u*. + * 'less': the distribution underlying `x` is stochastically less + than the distribution underlying `y`, i.e. *F(u) > G(u)* for all *u*. + * 'greater': the distribution underlying `x` is stochastically greater + than the distribution underlying `y`, i.e. *F(u) < G(u)* for all *u*. + + Note that the mathematical expressions in the alternative hypotheses + above describe the CDFs of the underlying distributions. The directions + of the inequalities appear inconsistent with the natural language + description at first glance, but they are not. For example, suppose + *X* and *Y* are random variables that follow distributions with CDFs + *F* and *G*, respectively. If *F(u) > G(u)* for all *u*, samples drawn + from *X* tend to be less than those drawn from *Y*. + + Under a more restrictive set of assumptions, the alternative hypotheses + can be expressed in terms of the locations of the distributions; + see [5] section 5.1. + axis : int, optional + Axis along which to perform the test. Default is 0. + method : {'auto', 'asymptotic', 'exact'} or `PermutationMethod` instance, optional + Selects the method used to calculate the *p*-value. + Default is 'auto'. The following options are available. + + * ``'asymptotic'``: compares the standardized test statistic + against the normal distribution, correcting for ties. + * ``'exact'``: computes the exact *p*-value by comparing the observed + :math:`U` statistic against the exact distribution of the :math:`U` + statistic under the null hypothesis. No correction is made for ties. + * ``'auto'``: chooses ``'exact'`` when the size of one of the samples + is less than or equal to 8 and there are no ties; + chooses ``'asymptotic'`` otherwise. + * `PermutationMethod` instance. In this case, the p-value + is computed using `permutation_test` with the provided + configuration options and other appropriate settings. + + Returns + ------- + res : MannwhitneyuResult + An object containing attributes: + + statistic : float + The Mann-Whitney U statistic corresponding with sample `x`. See + Notes for the test statistic corresponding with sample `y`. + pvalue : float + The associated *p*-value for the chosen `alternative`. + + Notes + ----- + If ``U1`` is the statistic corresponding with sample `x`, then the + statistic corresponding with sample `y` is + ``U2 = x.shape[axis] * y.shape[axis] - U1``. + + `mannwhitneyu` is for independent samples. For related / paired samples, + consider `scipy.stats.wilcoxon`. + + `method` ``'exact'`` is recommended when there are no ties and when either + sample size is less than 8 [1]_. The implementation follows the recurrence + relation originally proposed in [1]_ as it is described in [3]_. + Note that the exact method is *not* corrected for ties, but + `mannwhitneyu` will not raise errors or warnings if there are ties in the + data. If there are ties and either samples is small (fewer than ~10 + observations), consider passing an instance of `PermutationMethod` + as the `method` to perform a permutation test. + + The Mann-Whitney U test is a non-parametric version of the t-test for + independent samples. When the means of samples from the populations + are normally distributed, consider `scipy.stats.ttest_ind`. + + See Also + -------- + scipy.stats.wilcoxon, scipy.stats.ranksums, scipy.stats.ttest_ind + + References + ---------- + .. [1] H.B. Mann and D.R. Whitney, "On a test of whether one of two random + variables is stochastically larger than the other", The Annals of + Mathematical Statistics, Vol. 18, pp. 50-60, 1947. + .. [2] Mann-Whitney U Test, Wikipedia, + http://en.wikipedia.org/wiki/Mann-Whitney_U_test + .. [3] A. Di Bucchianico, "Combinatorics, computer algebra, and the + Wilcoxon-Mann-Whitney test", Journal of Statistical Planning and + Inference, Vol. 79, pp. 349-364, 1999. + .. [4] Rosie Shier, "Statistics: 2.3 The Mann-Whitney U Test", Mathematics + Learning Support Centre, 2004. + .. [5] Michael P. Fay and Michael A. Proschan. "Wilcoxon-Mann-Whitney + or t-test? On assumptions for hypothesis tests and multiple \ + interpretations of decision rules." Statistics surveys, Vol. 4, pp. + 1-39, 2010. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2857732/ + + Examples + -------- + We follow the example from [4]_: nine randomly sampled young adults were + diagnosed with type II diabetes at the ages below. + + >>> males = [19, 22, 16, 29, 24] + >>> females = [20, 11, 17, 12] + + We use the Mann-Whitney U test to assess whether there is a statistically + significant difference in the diagnosis age of males and females. + The null hypothesis is that the distribution of male diagnosis ages is + the same as the distribution of female diagnosis ages. We decide + that a confidence level of 95% is required to reject the null hypothesis + in favor of the alternative that the distributions are different. + Since the number of samples is very small and there are no ties in the + data, we can compare the observed test statistic against the *exact* + distribution of the test statistic under the null hypothesis. + + >>> from scipy.stats import mannwhitneyu + >>> U1, p = mannwhitneyu(males, females, method="exact") + >>> print(U1) + 17.0 + + `mannwhitneyu` always reports the statistic associated with the first + sample, which, in this case, is males. This agrees with :math:`U_M = 17` + reported in [4]_. The statistic associated with the second statistic + can be calculated: + + >>> nx, ny = len(males), len(females) + >>> U2 = nx*ny - U1 + >>> print(U2) + 3.0 + + This agrees with :math:`U_F = 3` reported in [4]_. The two-sided + *p*-value can be calculated from either statistic, and the value produced + by `mannwhitneyu` agrees with :math:`p = 0.11` reported in [4]_. + + >>> print(p) + 0.1111111111111111 + + The exact distribution of the test statistic is asymptotically normal, so + the example continues by comparing the exact *p*-value against the + *p*-value produced using the normal approximation. + + >>> _, pnorm = mannwhitneyu(males, females, method="asymptotic") + >>> print(pnorm) + 0.11134688653314041 + + Here `mannwhitneyu`'s reported *p*-value appears to conflict with the + value :math:`p = 0.09` given in [4]_. The reason is that [4]_ + does not apply the continuity correction performed by `mannwhitneyu`; + `mannwhitneyu` reduces the distance between the test statistic and the + mean :math:`\mu = n_x n_y / 2` by 0.5 to correct for the fact that the + discrete statistic is being compared against a continuous distribution. + Here, the :math:`U` statistic used is less than the mean, so we reduce + the distance by adding 0.5 in the numerator. + + >>> import numpy as np + >>> from scipy.stats import norm + >>> U = min(U1, U2) + >>> N = nx + ny + >>> z = (U - nx*ny/2 + 0.5) / np.sqrt(nx*ny * (N + 1)/ 12) + >>> p = 2 * norm.cdf(z) # use CDF to get p-value from smaller statistic + >>> print(p) + 0.11134688653314041 + + If desired, we can disable the continuity correction to get a result + that agrees with that reported in [4]_. + + >>> _, pnorm = mannwhitneyu(males, females, use_continuity=False, + ... method="asymptotic") + >>> print(pnorm) + 0.0864107329737 + + Regardless of whether we perform an exact or asymptotic test, the + probability of the test statistic being as extreme or more extreme by + chance exceeds 5%, so we do not consider the results statistically + significant. + + Suppose that, before seeing the data, we had hypothesized that females + would tend to be diagnosed at a younger age than males. + In that case, it would be natural to provide the female ages as the + first input, and we would have performed a one-sided test using + ``alternative = 'less'``: females are diagnosed at an age that is + stochastically less than that of males. + + >>> res = mannwhitneyu(females, males, alternative="less", method="exact") + >>> print(res) + MannwhitneyuResult(statistic=3.0, pvalue=0.05555555555555555) + + Again, the probability of getting a sufficiently low value of the + test statistic by chance under the null hypothesis is greater than 5%, + so we do not reject the null hypothesis in favor of our alternative. + + If it is reasonable to assume that the means of samples from the + populations are normally distributed, we could have used a t-test to + perform the analysis. + + >>> from scipy.stats import ttest_ind + >>> res = ttest_ind(females, males, alternative="less") + >>> print(res) + Ttest_indResult(statistic=-2.239334696520584, pvalue=0.030068441095757924) + + Under this assumption, the *p*-value would be low enough to reject the + null hypothesis in favor of the alternative. + + ''' + + x, y, use_continuity, alternative, axis_int, method = ( + _mwu_input_validation(x, y, use_continuity, alternative, axis, method)) + + x, y, xy = _broadcast_concatenate(x, y, axis) + + n1, n2 = x.shape[-1], y.shape[-1] + + # Follows [2] + ranks, t = _rankdata(xy, 'average', return_ties=True) # method 2, step 1 + R1 = ranks[..., :n1].sum(axis=-1) # method 2, step 2 + U1 = R1 - n1*(n1+1)/2 # method 2, step 3 + U2 = n1 * n2 - U1 # as U1 + U2 = n1 * n2 + + if alternative == "greater": + U, f = U1, 1 # U is the statistic to use for p-value, f is a factor + elif alternative == "less": + U, f = U2, 1 # Due to symmetry, use SF of U2 rather than CDF of U1 + else: + U, f = np.maximum(U1, U2), 2 # multiply SF by two for two-sided test + + if method == "auto": + method = _mwu_choose_method(n1, n2, np.any(t > 1)) + + if method == "exact": + p = _mwu_state.sf(U.astype(int), min(n1, n2), max(n1, n2)) + elif method == "asymptotic": + z = _get_mwu_z(U, n1, n2, t, continuity=use_continuity) + p = stats.norm.sf(z) + else: # `PermutationMethod` instance (already validated) + def statistic(x, y, axis): + return mannwhitneyu(x, y, use_continuity=use_continuity, + alternative=alternative, axis=axis, + method="asymptotic").statistic + + res = stats.permutation_test((x, y), statistic, axis=axis, + **method._asdict(), alternative=alternative) + p = res.pvalue + f = 1 + + p *= f + + # Ensure that test statistic is not greater than 1 + # This could happen for exact test when U = m*n/2 + p = np.clip(p, 0, 1) + + return MannwhitneyuResult(U1, p) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_morestats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_morestats.py new file mode 100644 index 0000000000000000000000000000000000000000..8da9345041842106da5f05164155f65c3142e9b9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_morestats.py @@ -0,0 +1,4933 @@ +from __future__ import annotations +import math +import warnings +from collections import namedtuple + +import numpy as np +from numpy import (isscalar, r_, log, around, unique, asarray, zeros, + arange, sort, amin, amax, sqrt, array, atleast_1d, # noqa: F401 + compress, pi, exp, ravel, count_nonzero, sin, cos, # noqa: F401 + arctan2, hypot) + +from scipy import optimize, special, interpolate, stats +from scipy._lib._bunch import _make_tuple_bunch +from scipy._lib._util import _rename_parameter, _contains_nan, _get_nan + +from ._ansari_swilk_statistics import gscale, swilk +from . import _stats_py, _wilcoxon +from ._fit import FitResult +from ._stats_py import find_repeats, _get_pvalue, SignificanceResult # noqa: F401 +from .contingency import chi2_contingency +from . import distributions +from ._distn_infrastructure import rv_generic +from ._axis_nan_policy import _axis_nan_policy_factory + + +__all__ = ['mvsdist', + 'bayes_mvs', 'kstat', 'kstatvar', 'probplot', 'ppcc_max', 'ppcc_plot', + 'boxcox_llf', 'boxcox', 'boxcox_normmax', 'boxcox_normplot', + 'shapiro', 'anderson', 'ansari', 'bartlett', 'levene', + 'fligner', 'mood', 'wilcoxon', 'median_test', + 'circmean', 'circvar', 'circstd', 'anderson_ksamp', + 'yeojohnson_llf', 'yeojohnson', 'yeojohnson_normmax', + 'yeojohnson_normplot', 'directional_stats', + 'false_discovery_control' + ] + + +Mean = namedtuple('Mean', ('statistic', 'minmax')) +Variance = namedtuple('Variance', ('statistic', 'minmax')) +Std_dev = namedtuple('Std_dev', ('statistic', 'minmax')) + + +def bayes_mvs(data, alpha=0.90): + r""" + Bayesian confidence intervals for the mean, var, and std. + + Parameters + ---------- + data : array_like + Input data, if multi-dimensional it is flattened to 1-D by `bayes_mvs`. + Requires 2 or more data points. + alpha : float, optional + Probability that the returned confidence interval contains + the true parameter. + + Returns + ------- + mean_cntr, var_cntr, std_cntr : tuple + The three results are for the mean, variance and standard deviation, + respectively. Each result is a tuple of the form:: + + (center, (lower, upper)) + + with `center` the mean of the conditional pdf of the value given the + data, and `(lower, upper)` a confidence interval, centered on the + median, containing the estimate to a probability ``alpha``. + + See Also + -------- + mvsdist + + Notes + ----- + Each tuple of mean, variance, and standard deviation estimates represent + the (center, (lower, upper)) with center the mean of the conditional pdf + of the value given the data and (lower, upper) is a confidence interval + centered on the median, containing the estimate to a probability + ``alpha``. + + Converts data to 1-D and assumes all data has the same mean and variance. + Uses Jeffrey's prior for variance and std. + + Equivalent to ``tuple((x.mean(), x.interval(alpha)) for x in mvsdist(dat))`` + + References + ---------- + T.E. Oliphant, "A Bayesian perspective on estimating mean, variance, and + standard-deviation from data", https://scholarsarchive.byu.edu/facpub/278, + 2006. + + Examples + -------- + First a basic example to demonstrate the outputs: + + >>> from scipy import stats + >>> data = [6, 9, 12, 7, 8, 8, 13] + >>> mean, var, std = stats.bayes_mvs(data) + >>> mean + Mean(statistic=9.0, minmax=(7.103650222612533, 10.896349777387467)) + >>> var + Variance(statistic=10.0, minmax=(3.176724206..., 24.45910382...)) + >>> std + Std_dev(statistic=2.9724954732045084, + minmax=(1.7823367265645143, 4.945614605014631)) + + Now we generate some normally distributed random data, and get estimates of + mean and standard deviation with 95% confidence intervals for those + estimates: + + >>> n_samples = 100000 + >>> data = stats.norm.rvs(size=n_samples) + >>> res_mean, res_var, res_std = stats.bayes_mvs(data, alpha=0.95) + + >>> import matplotlib.pyplot as plt + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.hist(data, bins=100, density=True, label='Histogram of data') + >>> ax.vlines(res_mean.statistic, 0, 0.5, colors='r', label='Estimated mean') + >>> ax.axvspan(res_mean.minmax[0],res_mean.minmax[1], facecolor='r', + ... alpha=0.2, label=r'Estimated mean (95% limits)') + >>> ax.vlines(res_std.statistic, 0, 0.5, colors='g', label='Estimated scale') + >>> ax.axvspan(res_std.minmax[0],res_std.minmax[1], facecolor='g', alpha=0.2, + ... label=r'Estimated scale (95% limits)') + + >>> ax.legend(fontsize=10) + >>> ax.set_xlim([-4, 4]) + >>> ax.set_ylim([0, 0.5]) + >>> plt.show() + + """ + m, v, s = mvsdist(data) + if alpha >= 1 or alpha <= 0: + raise ValueError("0 < alpha < 1 is required, but alpha=%s was given." + % alpha) + + m_res = Mean(m.mean(), m.interval(alpha)) + v_res = Variance(v.mean(), v.interval(alpha)) + s_res = Std_dev(s.mean(), s.interval(alpha)) + + return m_res, v_res, s_res + + +def mvsdist(data): + """ + 'Frozen' distributions for mean, variance, and standard deviation of data. + + Parameters + ---------- + data : array_like + Input array. Converted to 1-D using ravel. + Requires 2 or more data-points. + + Returns + ------- + mdist : "frozen" distribution object + Distribution object representing the mean of the data. + vdist : "frozen" distribution object + Distribution object representing the variance of the data. + sdist : "frozen" distribution object + Distribution object representing the standard deviation of the data. + + See Also + -------- + bayes_mvs + + Notes + ----- + The return values from ``bayes_mvs(data)`` is equivalent to + ``tuple((x.mean(), x.interval(0.90)) for x in mvsdist(data))``. + + In other words, calling ``.mean()`` and ``.interval(0.90)`` + on the three distribution objects returned from this function will give + the same results that are returned from `bayes_mvs`. + + References + ---------- + T.E. Oliphant, "A Bayesian perspective on estimating mean, variance, and + standard-deviation from data", https://scholarsarchive.byu.edu/facpub/278, + 2006. + + Examples + -------- + >>> from scipy import stats + >>> data = [6, 9, 12, 7, 8, 8, 13] + >>> mean, var, std = stats.mvsdist(data) + + We now have frozen distribution objects "mean", "var" and "std" that we can + examine: + + >>> mean.mean() + 9.0 + >>> mean.interval(0.95) + (6.6120585482655692, 11.387941451734431) + >>> mean.std() + 1.1952286093343936 + + """ + x = ravel(data) + n = len(x) + if n < 2: + raise ValueError("Need at least 2 data-points.") + xbar = x.mean() + C = x.var() + if n > 1000: # gaussian approximations for large n + mdist = distributions.norm(loc=xbar, scale=math.sqrt(C / n)) + sdist = distributions.norm(loc=math.sqrt(C), scale=math.sqrt(C / (2. * n))) + vdist = distributions.norm(loc=C, scale=math.sqrt(2.0 / n) * C) + else: + nm1 = n - 1 + fac = n * C / 2. + val = nm1 / 2. + mdist = distributions.t(nm1, loc=xbar, scale=math.sqrt(C / nm1)) + sdist = distributions.gengamma(val, -2, scale=math.sqrt(fac)) + vdist = distributions.invgamma(val, scale=fac) + return mdist, vdist, sdist + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, default_axis=None +) +def kstat(data, n=2): + r""" + Return the nth k-statistic (1<=n<=4 so far). + + The nth k-statistic k_n is the unique symmetric unbiased estimator of the + nth cumulant kappa_n. + + Parameters + ---------- + data : array_like + Input array. Note that n-D input gets flattened. + n : int, {1, 2, 3, 4}, optional + Default is equal to 2. + + Returns + ------- + kstat : float + The nth k-statistic. + + See Also + -------- + kstatvar : Returns an unbiased estimator of the variance of the k-statistic + moment : Returns the n-th central moment about the mean for a sample. + + Notes + ----- + For a sample size n, the first few k-statistics are given by: + + .. math:: + + k_{1} = \mu + k_{2} = \frac{n}{n-1} m_{2} + k_{3} = \frac{ n^{2} } {(n-1) (n-2)} m_{3} + k_{4} = \frac{ n^{2} [(n + 1)m_{4} - 3(n - 1) m^2_{2}]} {(n-1) (n-2) (n-3)} + + where :math:`\mu` is the sample mean, :math:`m_2` is the sample + variance, and :math:`m_i` is the i-th sample central moment. + + References + ---------- + http://mathworld.wolfram.com/k-Statistic.html + + http://mathworld.wolfram.com/Cumulant.html + + Examples + -------- + >>> from scipy import stats + >>> from numpy.random import default_rng + >>> rng = default_rng() + + As sample size increases, n-th moment and n-th k-statistic converge to the + same number (although they aren't identical). In the case of the normal + distribution, they converge to zero. + + >>> for n in [2, 3, 4, 5, 6, 7]: + ... x = rng.normal(size=10**n) + ... m, k = stats.moment(x, 3), stats.kstat(x, 3) + ... print("%.3g %.3g %.3g" % (m, k, m-k)) + -0.631 -0.651 0.0194 # random + 0.0282 0.0283 -8.49e-05 + -0.0454 -0.0454 1.36e-05 + 7.53e-05 7.53e-05 -2.26e-09 + 0.00166 0.00166 -4.99e-09 + -2.88e-06 -2.88e-06 8.63e-13 + """ + if n > 4 or n < 1: + raise ValueError("k-statistics only supported for 1<=n<=4") + n = int(n) + S = np.zeros(n + 1, np.float64) + data = ravel(data) + N = data.size + + # raise ValueError on empty input + if N == 0: + raise ValueError("Data input must not be empty") + + # on nan input, return nan without warning + if np.isnan(np.sum(data)): + return np.nan + + for k in range(1, n + 1): + S[k] = np.sum(data**k, axis=0) + if n == 1: + return S[1] * 1.0/N + elif n == 2: + return (N*S[2] - S[1]**2.0) / (N*(N - 1.0)) + elif n == 3: + return (2*S[1]**3 - 3*N*S[1]*S[2] + N*N*S[3]) / (N*(N - 1.0)*(N - 2.0)) + elif n == 4: + return ((-6*S[1]**4 + 12*N*S[1]**2 * S[2] - 3*N*(N-1.0)*S[2]**2 - + 4*N*(N+1)*S[1]*S[3] + N*N*(N+1)*S[4]) / + (N*(N-1.0)*(N-2.0)*(N-3.0))) + else: + raise ValueError("Should not be here.") + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, default_axis=None +) +def kstatvar(data, n=2): + r"""Return an unbiased estimator of the variance of the k-statistic. + + See `kstat` for more details of the k-statistic. + + Parameters + ---------- + data : array_like + Input array. Note that n-D input gets flattened. + n : int, {1, 2}, optional + Default is equal to 2. + + Returns + ------- + kstatvar : float + The nth k-statistic variance. + + See Also + -------- + kstat : Returns the n-th k-statistic. + moment : Returns the n-th central moment about the mean for a sample. + + Notes + ----- + The variances of the first few k-statistics are given by: + + .. math:: + + var(k_{1}) = \frac{\kappa^2}{n} + var(k_{2}) = \frac{\kappa^4}{n} + \frac{2\kappa^2_{2}}{n - 1} + var(k_{3}) = \frac{\kappa^6}{n} + \frac{9 \kappa_2 \kappa_4}{n - 1} + + \frac{9 \kappa^2_{3}}{n - 1} + + \frac{6 n \kappa^3_{2}}{(n-1) (n-2)} + var(k_{4}) = \frac{\kappa^8}{n} + \frac{16 \kappa_2 \kappa_6}{n - 1} + + \frac{48 \kappa_{3} \kappa_5}{n - 1} + + \frac{34 \kappa^2_{4}}{n-1} + + \frac{72 n \kappa^2_{2} \kappa_4}{(n - 1) (n - 2)} + + \frac{144 n \kappa_{2} \kappa^2_{3}}{(n - 1) (n - 2)} + + \frac{24 (n + 1) n \kappa^4_{2}}{(n - 1) (n - 2) (n - 3)} + """ # noqa: E501 + data = ravel(data) + N = len(data) + if n == 1: + return kstat(data, n=2) * 1.0/N + elif n == 2: + k2 = kstat(data, n=2) + k4 = kstat(data, n=4) + return (2*N*k2**2 + (N-1)*k4) / (N*(N+1)) + else: + raise ValueError("Only n=1 or n=2 supported.") + + +def _calc_uniform_order_statistic_medians(n): + """Approximations of uniform order statistic medians. + + Parameters + ---------- + n : int + Sample size. + + Returns + ------- + v : 1d float array + Approximations of the order statistic medians. + + References + ---------- + .. [1] James J. Filliben, "The Probability Plot Correlation Coefficient + Test for Normality", Technometrics, Vol. 17, pp. 111-117, 1975. + + Examples + -------- + Order statistics of the uniform distribution on the unit interval + are marginally distributed according to beta distributions. + The expectations of these order statistic are evenly spaced across + the interval, but the distributions are skewed in a way that + pushes the medians slightly towards the endpoints of the unit interval: + + >>> import numpy as np + >>> n = 4 + >>> k = np.arange(1, n+1) + >>> from scipy.stats import beta + >>> a = k + >>> b = n-k+1 + >>> beta.mean(a, b) + array([0.2, 0.4, 0.6, 0.8]) + >>> beta.median(a, b) + array([0.15910358, 0.38572757, 0.61427243, 0.84089642]) + + The Filliben approximation uses the exact medians of the smallest + and greatest order statistics, and the remaining medians are approximated + by points spread evenly across a sub-interval of the unit interval: + + >>> from scipy.stats._morestats import _calc_uniform_order_statistic_medians + >>> _calc_uniform_order_statistic_medians(n) + array([0.15910358, 0.38545246, 0.61454754, 0.84089642]) + + This plot shows the skewed distributions of the order statistics + of a sample of size four from a uniform distribution on the unit interval: + + >>> import matplotlib.pyplot as plt + >>> x = np.linspace(0.0, 1.0, num=50, endpoint=True) + >>> pdfs = [beta.pdf(x, a[i], b[i]) for i in range(n)] + >>> plt.figure() + >>> plt.plot(x, pdfs[0], x, pdfs[1], x, pdfs[2], x, pdfs[3]) + + """ + v = np.empty(n, dtype=np.float64) + v[-1] = 0.5**(1.0 / n) + v[0] = 1 - v[-1] + i = np.arange(2, n) + v[1:-1] = (i - 0.3175) / (n + 0.365) + return v + + +def _parse_dist_kw(dist, enforce_subclass=True): + """Parse `dist` keyword. + + Parameters + ---------- + dist : str or stats.distributions instance. + Several functions take `dist` as a keyword, hence this utility + function. + enforce_subclass : bool, optional + If True (default), `dist` needs to be a + `_distn_infrastructure.rv_generic` instance. + It can sometimes be useful to set this keyword to False, if a function + wants to accept objects that just look somewhat like such an instance + (for example, they have a ``ppf`` method). + + """ + if isinstance(dist, rv_generic): + pass + elif isinstance(dist, str): + try: + dist = getattr(distributions, dist) + except AttributeError as e: + raise ValueError("%s is not a valid distribution name" % dist) from e + elif enforce_subclass: + msg = ("`dist` should be a stats.distributions instance or a string " + "with the name of such a distribution.") + raise ValueError(msg) + + return dist + + +def _add_axis_labels_title(plot, xlabel, ylabel, title): + """Helper function to add axes labels and a title to stats plots.""" + try: + if hasattr(plot, 'set_title'): + # Matplotlib Axes instance or something that looks like it + plot.set_title(title) + plot.set_xlabel(xlabel) + plot.set_ylabel(ylabel) + else: + # matplotlib.pyplot module + plot.title(title) + plot.xlabel(xlabel) + plot.ylabel(ylabel) + except Exception: + # Not an MPL object or something that looks (enough) like it. + # Don't crash on adding labels or title + pass + + +def probplot(x, sparams=(), dist='norm', fit=True, plot=None, rvalue=False): + """ + Calculate quantiles for a probability plot, and optionally show the plot. + + Generates a probability plot of sample data against the quantiles of a + specified theoretical distribution (the normal distribution by default). + `probplot` optionally calculates a best-fit line for the data and plots the + results using Matplotlib or a given plot function. + + Parameters + ---------- + x : array_like + Sample/response data from which `probplot` creates the plot. + sparams : tuple, optional + Distribution-specific shape parameters (shape parameters plus location + and scale). + dist : str or stats.distributions instance, optional + Distribution or distribution function name. The default is 'norm' for a + normal probability plot. Objects that look enough like a + stats.distributions instance (i.e. they have a ``ppf`` method) are also + accepted. + fit : bool, optional + Fit a least-squares regression (best-fit) line to the sample data if + True (default). + plot : object, optional + If given, plots the quantiles. + If given and `fit` is True, also plots the least squares fit. + `plot` is an object that has to have methods "plot" and "text". + The `matplotlib.pyplot` module or a Matplotlib Axes object can be used, + or a custom object with the same methods. + Default is None, which means that no plot is created. + rvalue : bool, optional + If `plot` is provided and `fit` is True, setting `rvalue` to True + includes the coefficient of determination on the plot. + Default is False. + + Returns + ------- + (osm, osr) : tuple of ndarrays + Tuple of theoretical quantiles (osm, or order statistic medians) and + ordered responses (osr). `osr` is simply sorted input `x`. + For details on how `osm` is calculated see the Notes section. + (slope, intercept, r) : tuple of floats, optional + Tuple containing the result of the least-squares fit, if that is + performed by `probplot`. `r` is the square root of the coefficient of + determination. If ``fit=False`` and ``plot=None``, this tuple is not + returned. + + Notes + ----- + Even if `plot` is given, the figure is not shown or saved by `probplot`; + ``plt.show()`` or ``plt.savefig('figname.png')`` should be used after + calling `probplot`. + + `probplot` generates a probability plot, which should not be confused with + a Q-Q or a P-P plot. Statsmodels has more extensive functionality of this + type, see ``statsmodels.api.ProbPlot``. + + The formula used for the theoretical quantiles (horizontal axis of the + probability plot) is Filliben's estimate:: + + quantiles = dist.ppf(val), for + + 0.5**(1/n), for i = n + val = (i - 0.3175) / (n + 0.365), for i = 2, ..., n-1 + 1 - 0.5**(1/n), for i = 1 + + where ``i`` indicates the i-th ordered value and ``n`` is the total number + of values. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> nsample = 100 + >>> rng = np.random.default_rng() + + A t distribution with small degrees of freedom: + + >>> ax1 = plt.subplot(221) + >>> x = stats.t.rvs(3, size=nsample, random_state=rng) + >>> res = stats.probplot(x, plot=plt) + + A t distribution with larger degrees of freedom: + + >>> ax2 = plt.subplot(222) + >>> x = stats.t.rvs(25, size=nsample, random_state=rng) + >>> res = stats.probplot(x, plot=plt) + + A mixture of two normal distributions with broadcasting: + + >>> ax3 = plt.subplot(223) + >>> x = stats.norm.rvs(loc=[0,5], scale=[1,1.5], + ... size=(nsample//2,2), random_state=rng).ravel() + >>> res = stats.probplot(x, plot=plt) + + A standard normal distribution: + + >>> ax4 = plt.subplot(224) + >>> x = stats.norm.rvs(loc=0, scale=1, size=nsample, random_state=rng) + >>> res = stats.probplot(x, plot=plt) + + Produce a new figure with a loggamma distribution, using the ``dist`` and + ``sparams`` keywords: + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> x = stats.loggamma.rvs(c=2.5, size=500, random_state=rng) + >>> res = stats.probplot(x, dist=stats.loggamma, sparams=(2.5,), plot=ax) + >>> ax.set_title("Probplot for loggamma dist with shape parameter 2.5") + + Show the results with Matplotlib: + + >>> plt.show() + + """ + x = np.asarray(x) + if x.size == 0: + if fit: + return (x, x), (np.nan, np.nan, 0.0) + else: + return x, x + + osm_uniform = _calc_uniform_order_statistic_medians(len(x)) + dist = _parse_dist_kw(dist, enforce_subclass=False) + if sparams is None: + sparams = () + if isscalar(sparams): + sparams = (sparams,) + if not isinstance(sparams, tuple): + sparams = tuple(sparams) + + osm = dist.ppf(osm_uniform, *sparams) + osr = sort(x) + if fit: + # perform a linear least squares fit. + slope, intercept, r, prob, _ = _stats_py.linregress(osm, osr) + + if plot is not None: + plot.plot(osm, osr, 'bo') + if fit: + plot.plot(osm, slope*osm + intercept, 'r-') + _add_axis_labels_title(plot, xlabel='Theoretical quantiles', + ylabel='Ordered Values', + title='Probability Plot') + + # Add R^2 value to the plot as text + if fit and rvalue: + xmin = amin(osm) + xmax = amax(osm) + ymin = amin(x) + ymax = amax(x) + posx = xmin + 0.70 * (xmax - xmin) + posy = ymin + 0.01 * (ymax - ymin) + plot.text(posx, posy, "$R^2=%1.4f$" % r**2) + + if fit: + return (osm, osr), (slope, intercept, r) + else: + return osm, osr + + +def ppcc_max(x, brack=(0.0, 1.0), dist='tukeylambda'): + """Calculate the shape parameter that maximizes the PPCC. + + The probability plot correlation coefficient (PPCC) plot can be used + to determine the optimal shape parameter for a one-parameter family + of distributions. ``ppcc_max`` returns the shape parameter that would + maximize the probability plot correlation coefficient for the given + data to a one-parameter family of distributions. + + Parameters + ---------- + x : array_like + Input array. + brack : tuple, optional + Triple (a,b,c) where (a>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> c = 2.5 + >>> x = stats.weibull_min.rvs(c, scale=4, size=2000, random_state=rng) + + Generate the PPCC plot for this data with the Weibull distribution. + + >>> fig, ax = plt.subplots(figsize=(8, 6)) + >>> res = stats.ppcc_plot(x, c/2, 2*c, dist='weibull_min', plot=ax) + + We calculate the value where the shape should reach its maximum and a + red line is drawn there. The line should coincide with the highest + point in the PPCC graph. + + >>> cmax = stats.ppcc_max(x, brack=(c/2, 2*c), dist='weibull_min') + >>> ax.axvline(cmax, color='r') + >>> plt.show() + + """ + dist = _parse_dist_kw(dist) + osm_uniform = _calc_uniform_order_statistic_medians(len(x)) + osr = sort(x) + + # this function computes the x-axis values of the probability plot + # and computes a linear regression (including the correlation) + # and returns 1-r so that a minimization function maximizes the + # correlation + def tempfunc(shape, mi, yvals, func): + xvals = func(mi, shape) + r, prob = _stats_py.pearsonr(xvals, yvals) + return 1 - r + + return optimize.brent(tempfunc, brack=brack, + args=(osm_uniform, osr, dist.ppf)) + + +def ppcc_plot(x, a, b, dist='tukeylambda', plot=None, N=80): + """Calculate and optionally plot probability plot correlation coefficient. + + The probability plot correlation coefficient (PPCC) plot can be used to + determine the optimal shape parameter for a one-parameter family of + distributions. It cannot be used for distributions without shape + parameters + (like the normal distribution) or with multiple shape parameters. + + By default a Tukey-Lambda distribution (`stats.tukeylambda`) is used. A + Tukey-Lambda PPCC plot interpolates from long-tailed to short-tailed + distributions via an approximately normal one, and is therefore + particularly useful in practice. + + Parameters + ---------- + x : array_like + Input array. + a, b : scalar + Lower and upper bounds of the shape parameter to use. + dist : str or stats.distributions instance, optional + Distribution or distribution function name. Objects that look enough + like a stats.distributions instance (i.e. they have a ``ppf`` method) + are also accepted. The default is ``'tukeylambda'``. + plot : object, optional + If given, plots PPCC against the shape parameter. + `plot` is an object that has to have methods "plot" and "text". + The `matplotlib.pyplot` module or a Matplotlib Axes object can be used, + or a custom object with the same methods. + Default is None, which means that no plot is created. + N : int, optional + Number of points on the horizontal axis (equally distributed from + `a` to `b`). + + Returns + ------- + svals : ndarray + The shape values for which `ppcc` was calculated. + ppcc : ndarray + The calculated probability plot correlation coefficient values. + + See Also + -------- + ppcc_max, probplot, boxcox_normplot, tukeylambda + + References + ---------- + J.J. Filliben, "The Probability Plot Correlation Coefficient Test for + Normality", Technometrics, Vol. 17, pp. 111-117, 1975. + + Examples + -------- + First we generate some random data from a Weibull distribution + with shape parameter 2.5, and plot the histogram of the data: + + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> rng = np.random.default_rng() + >>> c = 2.5 + >>> x = stats.weibull_min.rvs(c, scale=4, size=2000, random_state=rng) + + Take a look at the histogram of the data. + + >>> fig1, ax = plt.subplots(figsize=(9, 4)) + >>> ax.hist(x, bins=50) + >>> ax.set_title('Histogram of x') + >>> plt.show() + + Now we explore this data with a PPCC plot as well as the related + probability plot and Box-Cox normplot. A red line is drawn where we + expect the PPCC value to be maximal (at the shape parameter ``c`` + used above): + + >>> fig2 = plt.figure(figsize=(12, 4)) + >>> ax1 = fig2.add_subplot(1, 3, 1) + >>> ax2 = fig2.add_subplot(1, 3, 2) + >>> ax3 = fig2.add_subplot(1, 3, 3) + >>> res = stats.probplot(x, plot=ax1) + >>> res = stats.boxcox_normplot(x, -4, 4, plot=ax2) + >>> res = stats.ppcc_plot(x, c/2, 2*c, dist='weibull_min', plot=ax3) + >>> ax3.axvline(c, color='r') + >>> plt.show() + + """ + if b <= a: + raise ValueError("`b` has to be larger than `a`.") + + svals = np.linspace(a, b, num=N) + ppcc = np.empty_like(svals) + for k, sval in enumerate(svals): + _, r2 = probplot(x, sval, dist=dist, fit=True) + ppcc[k] = r2[-1] + + if plot is not None: + plot.plot(svals, ppcc, 'x') + _add_axis_labels_title(plot, xlabel='Shape Values', + ylabel='Prob Plot Corr. Coef.', + title='(%s) PPCC Plot' % dist) + + return svals, ppcc + + +def _log_mean(logx): + # compute log of mean of x from log(x) + return special.logsumexp(logx, axis=0) - np.log(len(logx)) + + +def _log_var(logx): + # compute log of variance of x from log(x) + logmean = _log_mean(logx) + pij = np.full_like(logx, np.pi * 1j, dtype=np.complex128) + logxmu = special.logsumexp([logx, logmean + pij], axis=0) + return np.real(special.logsumexp(2 * logxmu, axis=0)) - np.log(len(logx)) + + +def boxcox_llf(lmb, data): + r"""The boxcox log-likelihood function. + + Parameters + ---------- + lmb : scalar + Parameter for Box-Cox transformation. See `boxcox` for details. + data : array_like + Data to calculate Box-Cox log-likelihood for. If `data` is + multi-dimensional, the log-likelihood is calculated along the first + axis. + + Returns + ------- + llf : float or ndarray + Box-Cox log-likelihood of `data` given `lmb`. A float for 1-D `data`, + an array otherwise. + + See Also + -------- + boxcox, probplot, boxcox_normplot, boxcox_normmax + + Notes + ----- + The Box-Cox log-likelihood function is defined here as + + .. math:: + + llf = (\lambda - 1) \sum_i(\log(x_i)) - + N/2 \log(\sum_i (y_i - \bar{y})^2 / N), + + where ``y`` is the Box-Cox transformed input data ``x``. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> from mpl_toolkits.axes_grid1.inset_locator import inset_axes + + Generate some random variates and calculate Box-Cox log-likelihood values + for them for a range of ``lmbda`` values: + + >>> rng = np.random.default_rng() + >>> x = stats.loggamma.rvs(5, loc=10, size=1000, random_state=rng) + >>> lmbdas = np.linspace(-2, 10) + >>> llf = np.zeros(lmbdas.shape, dtype=float) + >>> for ii, lmbda in enumerate(lmbdas): + ... llf[ii] = stats.boxcox_llf(lmbda, x) + + Also find the optimal lmbda value with `boxcox`: + + >>> x_most_normal, lmbda_optimal = stats.boxcox(x) + + Plot the log-likelihood as function of lmbda. Add the optimal lmbda as a + horizontal line to check that that's really the optimum: + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(lmbdas, llf, 'b.-') + >>> ax.axhline(stats.boxcox_llf(lmbda_optimal, x), color='r') + >>> ax.set_xlabel('lmbda parameter') + >>> ax.set_ylabel('Box-Cox log-likelihood') + + Now add some probability plots to show that where the log-likelihood is + maximized the data transformed with `boxcox` looks closest to normal: + + >>> locs = [3, 10, 4] # 'lower left', 'center', 'lower right' + >>> for lmbda, loc in zip([-1, lmbda_optimal, 9], locs): + ... xt = stats.boxcox(x, lmbda=lmbda) + ... (osm, osr), (slope, intercept, r_sq) = stats.probplot(xt) + ... ax_inset = inset_axes(ax, width="20%", height="20%", loc=loc) + ... ax_inset.plot(osm, osr, 'c.', osm, slope*osm + intercept, 'k-') + ... ax_inset.set_xticklabels([]) + ... ax_inset.set_yticklabels([]) + ... ax_inset.set_title(r'$\lambda=%1.2f$' % lmbda) + + >>> plt.show() + + """ + data = np.asarray(data) + N = data.shape[0] + if N == 0: + return np.nan + + logdata = np.log(data) + + # Compute the variance of the transformed data. + if lmb == 0: + logvar = np.log(np.var(logdata, axis=0)) + else: + # Transform without the constant offset 1/lmb. The offset does + # not affect the variance, and the subtraction of the offset can + # lead to loss of precision. + # Division by lmb can be factored out to enhance numerical stability. + logx = lmb * logdata + logvar = _log_var(logx) - 2 * np.log(abs(lmb)) + + return (lmb - 1) * np.sum(logdata, axis=0) - N/2 * logvar + + +def _boxcox_conf_interval(x, lmax, alpha): + # Need to find the lambda for which + # f(x,lmbda) >= f(x,lmax) - 0.5*chi^2_alpha;1 + fac = 0.5 * distributions.chi2.ppf(1 - alpha, 1) + target = boxcox_llf(lmax, x) - fac + + def rootfunc(lmbda, data, target): + return boxcox_llf(lmbda, data) - target + + # Find positive endpoint of interval in which answer is to be found + newlm = lmax + 0.5 + N = 0 + while (rootfunc(newlm, x, target) > 0.0) and (N < 500): + newlm += 0.1 + N += 1 + + if N == 500: + raise RuntimeError("Could not find endpoint.") + + lmplus = optimize.brentq(rootfunc, lmax, newlm, args=(x, target)) + + # Now find negative interval in the same way + newlm = lmax - 0.5 + N = 0 + while (rootfunc(newlm, x, target) > 0.0) and (N < 500): + newlm -= 0.1 + N += 1 + + if N == 500: + raise RuntimeError("Could not find endpoint.") + + lmminus = optimize.brentq(rootfunc, newlm, lmax, args=(x, target)) + return lmminus, lmplus + + +def boxcox(x, lmbda=None, alpha=None, optimizer=None): + r"""Return a dataset transformed by a Box-Cox power transformation. + + Parameters + ---------- + x : ndarray + Input array to be transformed. + + If `lmbda` is not None, this is an alias of + `scipy.special.boxcox`. + Returns nan if ``x < 0``; returns -inf if ``x == 0 and lmbda < 0``. + + If `lmbda` is None, array must be positive, 1-dimensional, and + non-constant. + + lmbda : scalar, optional + If `lmbda` is None (default), find the value of `lmbda` that maximizes + the log-likelihood function and return it as the second output + argument. + + If `lmbda` is not None, do the transformation for that value. + + alpha : float, optional + If `lmbda` is None and `alpha` is not None (default), return the + ``100 * (1-alpha)%`` confidence interval for `lmbda` as the third + output argument. Must be between 0.0 and 1.0. + + If `lmbda` is not None, `alpha` is ignored. + optimizer : callable, optional + If `lmbda` is None, `optimizer` is the scalar optimizer used to find + the value of `lmbda` that minimizes the negative log-likelihood + function. `optimizer` is a callable that accepts one argument: + + fun : callable + The objective function, which evaluates the negative + log-likelihood function at a provided value of `lmbda` + + and returns an object, such as an instance of + `scipy.optimize.OptimizeResult`, which holds the optimal value of + `lmbda` in an attribute `x`. + + See the example in `boxcox_normmax` or the documentation of + `scipy.optimize.minimize_scalar` for more information. + + If `lmbda` is not None, `optimizer` is ignored. + + Returns + ------- + boxcox : ndarray + Box-Cox power transformed array. + maxlog : float, optional + If the `lmbda` parameter is None, the second returned argument is + the `lmbda` that maximizes the log-likelihood function. + (min_ci, max_ci) : tuple of float, optional + If `lmbda` parameter is None and `alpha` is not None, this returned + tuple of floats represents the minimum and maximum confidence limits + given `alpha`. + + See Also + -------- + probplot, boxcox_normplot, boxcox_normmax, boxcox_llf + + Notes + ----- + The Box-Cox transform is given by:: + + y = (x**lmbda - 1) / lmbda, for lmbda != 0 + log(x), for lmbda = 0 + + `boxcox` requires the input data to be positive. Sometimes a Box-Cox + transformation provides a shift parameter to achieve this; `boxcox` does + not. Such a shift parameter is equivalent to adding a positive constant to + `x` before calling `boxcox`. + + The confidence limits returned when `alpha` is provided give the interval + where: + + .. math:: + + llf(\hat{\lambda}) - llf(\lambda) < \frac{1}{2}\chi^2(1 - \alpha, 1), + + with ``llf`` the log-likelihood function and :math:`\chi^2` the chi-squared + function. + + References + ---------- + G.E.P. Box and D.R. Cox, "An Analysis of Transformations", Journal of the + Royal Statistical Society B, 26, 211-252 (1964). + + Examples + -------- + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + We generate some random variates from a non-normal distribution and make a + probability plot for it, to show it is non-normal in the tails: + + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(211) + >>> x = stats.loggamma.rvs(5, size=500) + 5 + >>> prob = stats.probplot(x, dist=stats.norm, plot=ax1) + >>> ax1.set_xlabel('') + >>> ax1.set_title('Probplot against normal distribution') + + We now use `boxcox` to transform the data so it's closest to normal: + + >>> ax2 = fig.add_subplot(212) + >>> xt, _ = stats.boxcox(x) + >>> prob = stats.probplot(xt, dist=stats.norm, plot=ax2) + >>> ax2.set_title('Probplot after Box-Cox transformation') + + >>> plt.show() + + """ + x = np.asarray(x) + + if lmbda is not None: # single transformation + return special.boxcox(x, lmbda) + + if x.ndim != 1: + raise ValueError("Data must be 1-dimensional.") + + if x.size == 0: + return x + + if np.all(x == x[0]): + raise ValueError("Data must not be constant.") + + if np.any(x <= 0): + raise ValueError("Data must be positive.") + + # If lmbda=None, find the lmbda that maximizes the log-likelihood function. + lmax = boxcox_normmax(x, method='mle', optimizer=optimizer) + y = boxcox(x, lmax) + + if alpha is None: + return y, lmax + else: + # Find confidence interval + interval = _boxcox_conf_interval(x, lmax, alpha) + return y, lmax, interval + + +def _boxcox_inv_lmbda(x, y): + # compute lmbda given x and y for Box-Cox transformation + num = special.lambertw(-(x ** (-1 / y)) * np.log(x) / y, k=-1) + return np.real(-num / np.log(x) - 1 / y) + + +class _BigFloat: + def __repr__(self): + return "BIG_FLOAT" + + +def boxcox_normmax( + x, brack=None, method='pearsonr', optimizer=None, *, ymax=_BigFloat() +): + """Compute optimal Box-Cox transform parameter for input data. + + Parameters + ---------- + x : array_like + Input array. All entries must be positive, finite, real numbers. + brack : 2-tuple, optional, default (-2.0, 2.0) + The starting interval for a downhill bracket search for the default + `optimize.brent` solver. Note that this is in most cases not + critical; the final result is allowed to be outside this bracket. + If `optimizer` is passed, `brack` must be None. + method : str, optional + The method to determine the optimal transform parameter (`boxcox` + ``lmbda`` parameter). Options are: + + 'pearsonr' (default) + Maximizes the Pearson correlation coefficient between + ``y = boxcox(x)`` and the expected values for ``y`` if `x` would be + normally-distributed. + + 'mle' + Maximizes the log-likelihood `boxcox_llf`. This is the method used + in `boxcox`. + + 'all' + Use all optimization methods available, and return all results. + Useful to compare different methods. + optimizer : callable, optional + `optimizer` is a callable that accepts one argument: + + fun : callable + The objective function to be minimized. `fun` accepts one argument, + the Box-Cox transform parameter `lmbda`, and returns the value of + the function (e.g., the negative log-likelihood) at the provided + argument. The job of `optimizer` is to find the value of `lmbda` + that *minimizes* `fun`. + + and returns an object, such as an instance of + `scipy.optimize.OptimizeResult`, which holds the optimal value of + `lmbda` in an attribute `x`. + + See the example below or the documentation of + `scipy.optimize.minimize_scalar` for more information. + ymax : float, optional + The unconstrained optimal transform parameter may cause Box-Cox + transformed data to have extreme magnitude or even overflow. + This parameter constrains MLE optimization such that the magnitude + of the transformed `x` does not exceed `ymax`. The default is + the maximum value of the input dtype. If set to infinity, + `boxcox_normmax` returns the unconstrained optimal lambda. + Ignored when ``method='pearsonr'``. + + Returns + ------- + maxlog : float or ndarray + The optimal transform parameter found. An array instead of a scalar + for ``method='all'``. + + See Also + -------- + boxcox, boxcox_llf, boxcox_normplot, scipy.optimize.minimize_scalar + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + We can generate some data and determine the optimal ``lmbda`` in various + ways: + + >>> rng = np.random.default_rng() + >>> x = stats.loggamma.rvs(5, size=30, random_state=rng) + 5 + >>> y, lmax_mle = stats.boxcox(x) + >>> lmax_pearsonr = stats.boxcox_normmax(x) + + >>> lmax_mle + 2.217563431465757 + >>> lmax_pearsonr + 2.238318660200961 + >>> stats.boxcox_normmax(x, method='all') + array([2.23831866, 2.21756343]) + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> prob = stats.boxcox_normplot(x, -10, 10, plot=ax) + >>> ax.axvline(lmax_mle, color='r') + >>> ax.axvline(lmax_pearsonr, color='g', ls='--') + + >>> plt.show() + + Alternatively, we can define our own `optimizer` function. Suppose we + are only interested in values of `lmbda` on the interval [6, 7], we + want to use `scipy.optimize.minimize_scalar` with ``method='bounded'``, + and we want to use tighter tolerances when optimizing the log-likelihood + function. To do this, we define a function that accepts positional argument + `fun` and uses `scipy.optimize.minimize_scalar` to minimize `fun` subject + to the provided bounds and tolerances: + + >>> from scipy import optimize + >>> options = {'xatol': 1e-12} # absolute tolerance on `x` + >>> def optimizer(fun): + ... return optimize.minimize_scalar(fun, bounds=(6, 7), + ... method="bounded", options=options) + >>> stats.boxcox_normmax(x, optimizer=optimizer) + 6.000... + """ + x = np.asarray(x) + end_msg = "exceed specified `ymax`." + if isinstance(ymax, _BigFloat): + dtype = x.dtype if np.issubdtype(x.dtype, np.floating) else np.float64 + # 10000 is a safety factor because `special.boxcox` overflows prematurely. + ymax = np.finfo(dtype).max / 10000 + end_msg = f"overflow in {dtype}." + elif ymax <= 0: + raise ValueError("`ymax` must be strictly positive") + + # If optimizer is not given, define default 'brent' optimizer. + if optimizer is None: + + # Set default value for `brack`. + if brack is None: + brack = (-2.0, 2.0) + + def _optimizer(func, args): + return optimize.brent(func, args=args, brack=brack) + + # Otherwise check optimizer. + else: + if not callable(optimizer): + raise ValueError("`optimizer` must be a callable") + + if brack is not None: + raise ValueError("`brack` must be None if `optimizer` is given") + + # `optimizer` is expected to return a `OptimizeResult` object, we here + # get the solution to the optimization problem. + def _optimizer(func, args): + def func_wrapped(x): + return func(x, *args) + return getattr(optimizer(func_wrapped), 'x', None) + + def _pearsonr(x): + osm_uniform = _calc_uniform_order_statistic_medians(len(x)) + xvals = distributions.norm.ppf(osm_uniform) + + def _eval_pearsonr(lmbda, xvals, samps): + # This function computes the x-axis values of the probability plot + # and computes a linear regression (including the correlation) and + # returns ``1 - r`` so that a minimization function maximizes the + # correlation. + y = boxcox(samps, lmbda) + yvals = np.sort(y) + r, prob = _stats_py.pearsonr(xvals, yvals) + return 1 - r + + return _optimizer(_eval_pearsonr, args=(xvals, x)) + + def _mle(x): + def _eval_mle(lmb, data): + # function to minimize + return -boxcox_llf(lmb, data) + + return _optimizer(_eval_mle, args=(x,)) + + def _all(x): + maxlog = np.empty(2, dtype=float) + maxlog[0] = _pearsonr(x) + maxlog[1] = _mle(x) + return maxlog + + methods = {'pearsonr': _pearsonr, + 'mle': _mle, + 'all': _all} + if method not in methods.keys(): + raise ValueError("Method %s not recognized." % method) + + optimfunc = methods[method] + + try: + res = optimfunc(x) + except ValueError as e: + if "infs or NaNs" in str(e): + message = ("The `x` argument of `boxcox_normmax` must contain " + "only positive, finite, real numbers.") + raise ValueError(message) from e + else: + raise e + + if res is None: + message = ("The `optimizer` argument of `boxcox_normmax` must return " + "an object containing the optimal `lmbda` in attribute `x`.") + raise ValueError(message) + elif not np.isinf(ymax): # adjust the final lambda + # x > 1, boxcox(x) > 0; x < 1, boxcox(x) < 0 + xmax, xmin = np.max(x), np.min(x) + if xmin >= 1: + x_treme = xmax + elif xmax <= 1: + x_treme = xmin + else: # xmin < 1 < xmax + indicator = special.boxcox(xmax, res) > abs(special.boxcox(xmin, res)) + if isinstance(res, np.ndarray): + indicator = indicator[1] # select corresponds with 'mle' + x_treme = xmax if indicator else xmin + + mask = abs(special.boxcox(x_treme, res)) > ymax + if np.any(mask): + message = ( + f"The optimal lambda is {res}, but the returned lambda is the " + f"constrained optimum to ensure that the maximum or the minimum " + f"of the transformed data does not " + end_msg + ) + warnings.warn(message, stacklevel=2) + + # Return the constrained lambda to ensure the transformation + # does not cause overflow or exceed specified `ymax` + constrained_res = _boxcox_inv_lmbda(x_treme, ymax * np.sign(x_treme - 1)) + + if isinstance(res, np.ndarray): + res[mask] = constrained_res + else: + res = constrained_res + return res + + +def _normplot(method, x, la, lb, plot=None, N=80): + """Compute parameters for a Box-Cox or Yeo-Johnson normality plot, + optionally show it. + + See `boxcox_normplot` or `yeojohnson_normplot` for details. + """ + + if method == 'boxcox': + title = 'Box-Cox Normality Plot' + transform_func = boxcox + else: + title = 'Yeo-Johnson Normality Plot' + transform_func = yeojohnson + + x = np.asarray(x) + if x.size == 0: + return x + + if lb <= la: + raise ValueError("`lb` has to be larger than `la`.") + + if method == 'boxcox' and np.any(x <= 0): + raise ValueError("Data must be positive.") + + lmbdas = np.linspace(la, lb, num=N) + ppcc = lmbdas * 0.0 + for i, val in enumerate(lmbdas): + # Determine for each lmbda the square root of correlation coefficient + # of transformed x + z = transform_func(x, lmbda=val) + _, (_, _, r) = probplot(z, dist='norm', fit=True) + ppcc[i] = r + + if plot is not None: + plot.plot(lmbdas, ppcc, 'x') + _add_axis_labels_title(plot, xlabel='$\\lambda$', + ylabel='Prob Plot Corr. Coef.', + title=title) + + return lmbdas, ppcc + + +def boxcox_normplot(x, la, lb, plot=None, N=80): + """Compute parameters for a Box-Cox normality plot, optionally show it. + + A Box-Cox normality plot shows graphically what the best transformation + parameter is to use in `boxcox` to obtain a distribution that is close + to normal. + + Parameters + ---------- + x : array_like + Input array. + la, lb : scalar + The lower and upper bounds for the ``lmbda`` values to pass to `boxcox` + for Box-Cox transformations. These are also the limits of the + horizontal axis of the plot if that is generated. + plot : object, optional + If given, plots the quantiles and least squares fit. + `plot` is an object that has to have methods "plot" and "text". + The `matplotlib.pyplot` module or a Matplotlib Axes object can be used, + or a custom object with the same methods. + Default is None, which means that no plot is created. + N : int, optional + Number of points on the horizontal axis (equally distributed from + `la` to `lb`). + + Returns + ------- + lmbdas : ndarray + The ``lmbda`` values for which a Box-Cox transform was done. + ppcc : ndarray + Probability Plot Correlelation Coefficient, as obtained from `probplot` + when fitting the Box-Cox transformed input `x` against a normal + distribution. + + See Also + -------- + probplot, boxcox, boxcox_normmax, boxcox_llf, ppcc_max + + Notes + ----- + Even if `plot` is given, the figure is not shown or saved by + `boxcox_normplot`; ``plt.show()`` or ``plt.savefig('figname.png')`` + should be used after calling `probplot`. + + Examples + -------- + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + Generate some non-normally distributed data, and create a Box-Cox plot: + + >>> x = stats.loggamma.rvs(5, size=500) + 5 + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> prob = stats.boxcox_normplot(x, -20, 20, plot=ax) + + Determine and plot the optimal ``lmbda`` to transform ``x`` and plot it in + the same plot: + + >>> _, maxlog = stats.boxcox(x) + >>> ax.axvline(maxlog, color='r') + + >>> plt.show() + + """ + return _normplot('boxcox', x, la, lb, plot, N) + + +def yeojohnson(x, lmbda=None): + r"""Return a dataset transformed by a Yeo-Johnson power transformation. + + Parameters + ---------- + x : ndarray + Input array. Should be 1-dimensional. + lmbda : float, optional + If ``lmbda`` is ``None``, find the lambda that maximizes the + log-likelihood function and return it as the second output argument. + Otherwise the transformation is done for the given value. + + Returns + ------- + yeojohnson: ndarray + Yeo-Johnson power transformed array. + maxlog : float, optional + If the `lmbda` parameter is None, the second returned argument is + the lambda that maximizes the log-likelihood function. + + See Also + -------- + probplot, yeojohnson_normplot, yeojohnson_normmax, yeojohnson_llf, boxcox + + Notes + ----- + The Yeo-Johnson transform is given by:: + + y = ((x + 1)**lmbda - 1) / lmbda, for x >= 0, lmbda != 0 + log(x + 1), for x >= 0, lmbda = 0 + -((-x + 1)**(2 - lmbda) - 1) / (2 - lmbda), for x < 0, lmbda != 2 + -log(-x + 1), for x < 0, lmbda = 2 + + Unlike `boxcox`, `yeojohnson` does not require the input data to be + positive. + + .. versionadded:: 1.2.0 + + + References + ---------- + I. Yeo and R.A. Johnson, "A New Family of Power Transformations to + Improve Normality or Symmetry", Biometrika 87.4 (2000): + + + Examples + -------- + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + We generate some random variates from a non-normal distribution and make a + probability plot for it, to show it is non-normal in the tails: + + >>> fig = plt.figure() + >>> ax1 = fig.add_subplot(211) + >>> x = stats.loggamma.rvs(5, size=500) + 5 + >>> prob = stats.probplot(x, dist=stats.norm, plot=ax1) + >>> ax1.set_xlabel('') + >>> ax1.set_title('Probplot against normal distribution') + + We now use `yeojohnson` to transform the data so it's closest to normal: + + >>> ax2 = fig.add_subplot(212) + >>> xt, lmbda = stats.yeojohnson(x) + >>> prob = stats.probplot(xt, dist=stats.norm, plot=ax2) + >>> ax2.set_title('Probplot after Yeo-Johnson transformation') + + >>> plt.show() + + """ + x = np.asarray(x) + if x.size == 0: + return x + + if np.issubdtype(x.dtype, np.complexfloating): + raise ValueError('Yeo-Johnson transformation is not defined for ' + 'complex numbers.') + + if np.issubdtype(x.dtype, np.integer): + x = x.astype(np.float64, copy=False) + + if lmbda is not None: + return _yeojohnson_transform(x, lmbda) + + # if lmbda=None, find the lmbda that maximizes the log-likelihood function. + lmax = yeojohnson_normmax(x) + y = _yeojohnson_transform(x, lmax) + + return y, lmax + + +def _yeojohnson_transform(x, lmbda): + """Returns `x` transformed by the Yeo-Johnson power transform with given + parameter `lmbda`. + """ + dtype = x.dtype if np.issubdtype(x.dtype, np.floating) else np.float64 + out = np.zeros_like(x, dtype=dtype) + pos = x >= 0 # binary mask + + # when x >= 0 + if abs(lmbda) < np.spacing(1.): + out[pos] = np.log1p(x[pos]) + else: # lmbda != 0 + # more stable version of: ((x + 1) ** lmbda - 1) / lmbda + out[pos] = np.expm1(lmbda * np.log1p(x[pos])) / lmbda + + # when x < 0 + if abs(lmbda - 2) > np.spacing(1.): + out[~pos] = -np.expm1((2 - lmbda) * np.log1p(-x[~pos])) / (2 - lmbda) + else: # lmbda == 2 + out[~pos] = -np.log1p(-x[~pos]) + + return out + + +def yeojohnson_llf(lmb, data): + r"""The yeojohnson log-likelihood function. + + Parameters + ---------- + lmb : scalar + Parameter for Yeo-Johnson transformation. See `yeojohnson` for + details. + data : array_like + Data to calculate Yeo-Johnson log-likelihood for. If `data` is + multi-dimensional, the log-likelihood is calculated along the first + axis. + + Returns + ------- + llf : float + Yeo-Johnson log-likelihood of `data` given `lmb`. + + See Also + -------- + yeojohnson, probplot, yeojohnson_normplot, yeojohnson_normmax + + Notes + ----- + The Yeo-Johnson log-likelihood function is defined here as + + .. math:: + + llf = -N/2 \log(\hat{\sigma}^2) + (\lambda - 1) + \sum_i \text{ sign }(x_i)\log(|x_i| + 1) + + where :math:`\hat{\sigma}^2` is estimated variance of the Yeo-Johnson + transformed input data ``x``. + + .. versionadded:: 1.2.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + >>> from mpl_toolkits.axes_grid1.inset_locator import inset_axes + + Generate some random variates and calculate Yeo-Johnson log-likelihood + values for them for a range of ``lmbda`` values: + + >>> x = stats.loggamma.rvs(5, loc=10, size=1000) + >>> lmbdas = np.linspace(-2, 10) + >>> llf = np.zeros(lmbdas.shape, dtype=float) + >>> for ii, lmbda in enumerate(lmbdas): + ... llf[ii] = stats.yeojohnson_llf(lmbda, x) + + Also find the optimal lmbda value with `yeojohnson`: + + >>> x_most_normal, lmbda_optimal = stats.yeojohnson(x) + + Plot the log-likelihood as function of lmbda. Add the optimal lmbda as a + horizontal line to check that that's really the optimum: + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(lmbdas, llf, 'b.-') + >>> ax.axhline(stats.yeojohnson_llf(lmbda_optimal, x), color='r') + >>> ax.set_xlabel('lmbda parameter') + >>> ax.set_ylabel('Yeo-Johnson log-likelihood') + + Now add some probability plots to show that where the log-likelihood is + maximized the data transformed with `yeojohnson` looks closest to normal: + + >>> locs = [3, 10, 4] # 'lower left', 'center', 'lower right' + >>> for lmbda, loc in zip([-1, lmbda_optimal, 9], locs): + ... xt = stats.yeojohnson(x, lmbda=lmbda) + ... (osm, osr), (slope, intercept, r_sq) = stats.probplot(xt) + ... ax_inset = inset_axes(ax, width="20%", height="20%", loc=loc) + ... ax_inset.plot(osm, osr, 'c.', osm, slope*osm + intercept, 'k-') + ... ax_inset.set_xticklabels([]) + ... ax_inset.set_yticklabels([]) + ... ax_inset.set_title(r'$\lambda=%1.2f$' % lmbda) + + >>> plt.show() + + """ + data = np.asarray(data) + n_samples = data.shape[0] + + if n_samples == 0: + return np.nan + + trans = _yeojohnson_transform(data, lmb) + trans_var = trans.var(axis=0) + loglike = np.empty_like(trans_var) + + # Avoid RuntimeWarning raised by np.log when the variance is too low + tiny_variance = trans_var < np.finfo(trans_var.dtype).tiny + loglike[tiny_variance] = np.inf + + loglike[~tiny_variance] = ( + -n_samples / 2 * np.log(trans_var[~tiny_variance])) + loglike[~tiny_variance] += ( + (lmb - 1) * (np.sign(data) * np.log1p(np.abs(data))).sum(axis=0)) + return loglike + + +def yeojohnson_normmax(x, brack=None): + """Compute optimal Yeo-Johnson transform parameter. + + Compute optimal Yeo-Johnson transform parameter for input data, using + maximum likelihood estimation. + + Parameters + ---------- + x : array_like + Input array. + brack : 2-tuple, optional + The starting interval for a downhill bracket search with + `optimize.brent`. Note that this is in most cases not critical; the + final result is allowed to be outside this bracket. If None, + `optimize.fminbound` is used with bounds that avoid overflow. + + Returns + ------- + maxlog : float + The optimal transform parameter found. + + See Also + -------- + yeojohnson, yeojohnson_llf, yeojohnson_normplot + + Notes + ----- + .. versionadded:: 1.2.0 + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + Generate some data and determine optimal ``lmbda`` + + >>> rng = np.random.default_rng() + >>> x = stats.loggamma.rvs(5, size=30, random_state=rng) + 5 + >>> lmax = stats.yeojohnson_normmax(x) + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> prob = stats.yeojohnson_normplot(x, -10, 10, plot=ax) + >>> ax.axvline(lmax, color='r') + + >>> plt.show() + + """ + def _neg_llf(lmbda, data): + llf = yeojohnson_llf(lmbda, data) + # reject likelihoods that are inf which are likely due to small + # variance in the transformed space + llf[np.isinf(llf)] = -np.inf + return -llf + + with np.errstate(invalid='ignore'): + if not np.all(np.isfinite(x)): + raise ValueError('Yeo-Johnson input must be finite.') + if np.all(x == 0): + return 1.0 + if brack is not None: + return optimize.brent(_neg_llf, brack=brack, args=(x,)) + x = np.asarray(x) + dtype = x.dtype if np.issubdtype(x.dtype, np.floating) else np.float64 + # Allow values up to 20 times the maximum observed value to be safely + # transformed without over- or underflow. + log1p_max_x = np.log1p(20 * np.max(np.abs(x))) + # Use half of floating point's exponent range to allow safe computation + # of the variance of the transformed data. + log_eps = np.log(np.finfo(dtype).eps) + log_tiny_float = (np.log(np.finfo(dtype).tiny) - log_eps) / 2 + log_max_float = (np.log(np.finfo(dtype).max) + log_eps) / 2 + # Compute the bounds by approximating the inverse of the Yeo-Johnson + # transform on the smallest and largest floating point exponents, given + # the largest data we expect to observe. See [1] for further details. + # [1] https://github.com/scipy/scipy/pull/18852#issuecomment-1630286174 + lb = log_tiny_float / log1p_max_x + ub = log_max_float / log1p_max_x + # Convert the bounds if all or some of the data is negative. + if np.all(x < 0): + lb, ub = 2 - ub, 2 - lb + elif np.any(x < 0): + lb, ub = max(2 - ub, lb), min(2 - lb, ub) + # Match `optimize.brent`'s tolerance. + tol_brent = 1.48e-08 + return optimize.fminbound(_neg_llf, lb, ub, args=(x,), xtol=tol_brent) + + +def yeojohnson_normplot(x, la, lb, plot=None, N=80): + """Compute parameters for a Yeo-Johnson normality plot, optionally show it. + + A Yeo-Johnson normality plot shows graphically what the best + transformation parameter is to use in `yeojohnson` to obtain a + distribution that is close to normal. + + Parameters + ---------- + x : array_like + Input array. + la, lb : scalar + The lower and upper bounds for the ``lmbda`` values to pass to + `yeojohnson` for Yeo-Johnson transformations. These are also the + limits of the horizontal axis of the plot if that is generated. + plot : object, optional + If given, plots the quantiles and least squares fit. + `plot` is an object that has to have methods "plot" and "text". + The `matplotlib.pyplot` module or a Matplotlib Axes object can be used, + or a custom object with the same methods. + Default is None, which means that no plot is created. + N : int, optional + Number of points on the horizontal axis (equally distributed from + `la` to `lb`). + + Returns + ------- + lmbdas : ndarray + The ``lmbda`` values for which a Yeo-Johnson transform was done. + ppcc : ndarray + Probability Plot Correlelation Coefficient, as obtained from `probplot` + when fitting the Box-Cox transformed input `x` against a normal + distribution. + + See Also + -------- + probplot, yeojohnson, yeojohnson_normmax, yeojohnson_llf, ppcc_max + + Notes + ----- + Even if `plot` is given, the figure is not shown or saved by + `boxcox_normplot`; ``plt.show()`` or ``plt.savefig('figname.png')`` + should be used after calling `probplot`. + + .. versionadded:: 1.2.0 + + Examples + -------- + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + Generate some non-normally distributed data, and create a Yeo-Johnson plot: + + >>> x = stats.loggamma.rvs(5, size=500) + 5 + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> prob = stats.yeojohnson_normplot(x, -20, 20, plot=ax) + + Determine and plot the optimal ``lmbda`` to transform ``x`` and plot it in + the same plot: + + >>> _, maxlog = stats.yeojohnson(x) + >>> ax.axvline(maxlog, color='r') + + >>> plt.show() + + """ + return _normplot('yeojohnson', x, la, lb, plot, N) + + +ShapiroResult = namedtuple('ShapiroResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(ShapiroResult, n_samples=1, too_small=2, default_axis=None) +def shapiro(x): + r"""Perform the Shapiro-Wilk test for normality. + + The Shapiro-Wilk test tests the null hypothesis that the + data was drawn from a normal distribution. + + Parameters + ---------- + x : array_like + Array of sample data. + + Returns + ------- + statistic : float + The test statistic. + p-value : float + The p-value for the hypothesis test. + + See Also + -------- + anderson : The Anderson-Darling test for normality + kstest : The Kolmogorov-Smirnov test for goodness of fit. + + Notes + ----- + The algorithm used is described in [4]_ but censoring parameters as + described are not implemented. For N > 5000 the W test statistic is + accurate, but the p-value may not be. + + References + ---------- + .. [1] https://www.itl.nist.gov/div898/handbook/prc/section2/prc213.htm + :doi:`10.18434/M32189` + .. [2] Shapiro, S. S. & Wilk, M.B, "An analysis of variance test for + normality (complete samples)", Biometrika, 1965, Vol. 52, + pp. 591-611, :doi:`10.2307/2333709` + .. [3] Razali, N. M. & Wah, Y. B., "Power comparisons of Shapiro-Wilk, + Kolmogorov-Smirnov, Lilliefors and Anderson-Darling tests", Journal + of Statistical Modeling and Analytics, 2011, Vol. 2, pp. 21-33. + .. [4] Royston P., "Remark AS R94: A Remark on Algorithm AS 181: The + W-test for Normality", 1995, Applied Statistics, Vol. 44, + :doi:`10.2307/2986146` + .. [5] Phipson B., and Smyth, G. K., "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn", Statistical Applications in Genetics and Molecular Biology, + 2010, Vol.9, :doi:`10.2202/1544-6115.1585` + .. [6] Panagiotakos, D. B., "The value of p-value in biomedical + research", The Open Cardiovascular Medicine Journal, 2008, Vol.2, + pp. 97-99, :doi:`10.2174/1874192400802010097` + + Examples + -------- + Suppose we wish to infer from measurements whether the weights of adult + human males in a medical study are not normally distributed [2]_. + The weights (lbs) are recorded in the array ``x`` below. + + >>> import numpy as np + >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) + + The normality test of [1]_ and [2]_ begins by computing a statistic based + on the relationship between the observations and the expected order + statistics of a normal distribution. + + >>> from scipy import stats + >>> res = stats.shapiro(x) + >>> res.statistic + 0.7888147830963135 + + The value of this statistic tends to be high (close to 1) for samples drawn + from a normal distribution. + + The test is performed by comparing the observed value of the statistic + against the null distribution: the distribution of statistic values formed + under the null hypothesis that the weights were drawn from a normal + distribution. For this normality test, the null distribution is not easy to + calculate exactly, so it is usually approximated by Monte Carlo methods, + that is, drawing many samples of the same size as ``x`` from a normal + distribution and computing the values of the statistic for each. + + >>> def statistic(x): + ... # Get only the `shapiro` statistic; ignore its p-value + ... return stats.shapiro(x).statistic + >>> ref = stats.monte_carlo_test(x, stats.norm.rvs, statistic, + ... alternative='less') + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> bins = np.linspace(0.65, 1, 50) + >>> def plot(ax): # we'll reuse this + ... ax.hist(ref.null_distribution, density=True, bins=bins) + ... ax.set_title("Shapiro-Wilk Test Null Distribution \n" + ... "(Monte Carlo Approximation, 11 Observations)") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution less than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> annotation = (f'p-value={res.pvalue:.6f}\n(highlighted area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (0.75, 0.1), (0.68, 0.7), arrowprops=props) + >>> i_extreme = np.where(bins <= res.statistic)[0] + >>> for i in i_extreme: + ... ax.patches[i].set_color('C1') + >>> plt.xlim(0.65, 0.9) + >>> plt.ylim(0, 4) + >>> plt.show + >>> res.pvalue + 0.006703833118081093 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from a normally distributed population that produces such an + extreme value of the statistic - this may be taken as evidence against + the null hypothesis in favor of the alternative: the weights were not + drawn from a normal distribution. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence *for* the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [5]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + + """ + x = np.ravel(x).astype(np.float64) + + N = len(x) + if N < 3: + raise ValueError("Data must be at least length 3.") + + a = zeros(N//2, dtype=np.float64) + init = 0 + + y = sort(x) + y -= x[N//2] # subtract the median (or a nearby value); see gh-15777 + + w, pw, ifault = swilk(y, a, init) + if ifault not in [0, 2]: + warnings.warn("scipy.stats.shapiro: Input data has range zero. The" + " results may not be accurate.", stacklevel=2) + if N > 5000: + warnings.warn("scipy.stats.shapiro: For N > 5000, computed p-value " + f"may not be accurate. Current N is {N}.", + stacklevel=2) + + # `w` and `pw` are always Python floats, which are double precision. + # We want to ensure that they are NumPy floats, so until dtypes are + # respected, we can explicitly convert each to float64 (faster than + # `np.array([w, pw])`). + return ShapiroResult(np.float64(w), np.float64(pw)) + + +# Values from Stephens, M A, "EDF Statistics for Goodness of Fit and +# Some Comparisons", Journal of the American Statistical +# Association, Vol. 69, Issue 347, Sept. 1974, pp 730-737 +_Avals_norm = array([0.576, 0.656, 0.787, 0.918, 1.092]) +_Avals_expon = array([0.922, 1.078, 1.341, 1.606, 1.957]) +# From Stephens, M A, "Goodness of Fit for the Extreme Value Distribution", +# Biometrika, Vol. 64, Issue 3, Dec. 1977, pp 583-588. +_Avals_gumbel = array([0.474, 0.637, 0.757, 0.877, 1.038]) +# From Stephens, M A, "Tests of Fit for the Logistic Distribution Based +# on the Empirical Distribution Function.", Biometrika, +# Vol. 66, Issue 3, Dec. 1979, pp 591-595. +_Avals_logistic = array([0.426, 0.563, 0.660, 0.769, 0.906, 1.010]) +# From Richard A. Lockhart and Michael A. Stephens "Estimation and Tests of +# Fit for the Three-Parameter Weibull Distribution" +# Journal of the Royal Statistical Society.Series B(Methodological) +# Vol. 56, No. 3 (1994), pp. 491-500, table 1. Keys are c*100 +_Avals_weibull = [[0.292, 0.395, 0.467, 0.522, 0.617, 0.711, 0.836, 0.931], + [0.295, 0.399, 0.471, 0.527, 0.623, 0.719, 0.845, 0.941], + [0.298, 0.403, 0.476, 0.534, 0.631, 0.728, 0.856, 0.954], + [0.301, 0.408, 0.483, 0.541, 0.640, 0.738, 0.869, 0.969], + [0.305, 0.414, 0.490, 0.549, 0.650, 0.751, 0.885, 0.986], + [0.309, 0.421, 0.498, 0.559, 0.662, 0.765, 0.902, 1.007], + [0.314, 0.429, 0.508, 0.570, 0.676, 0.782, 0.923, 1.030], + [0.320, 0.438, 0.519, 0.583, 0.692, 0.802, 0.947, 1.057], + [0.327, 0.448, 0.532, 0.598, 0.711, 0.824, 0.974, 1.089], + [0.334, 0.469, 0.547, 0.615, 0.732, 0.850, 1.006, 1.125], + [0.342, 0.472, 0.563, 0.636, 0.757, 0.879, 1.043, 1.167]] +_Avals_weibull = np.array(_Avals_weibull) +_cvals_weibull = np.linspace(0, 0.5, 11) +_get_As_weibull = interpolate.interp1d(_cvals_weibull, _Avals_weibull.T, + kind='linear', bounds_error=False, + fill_value=_Avals_weibull[-1]) + + +def _weibull_fit_check(params, x): + # Refine the fit returned by `weibull_min.fit` to ensure that the first + # order necessary conditions are satisfied. If not, raise an error. + # Here, use `m` for the shape parameter to be consistent with [7] + # and avoid confusion with `c` as defined in [7]. + n = len(x) + m, u, s = params + + def dnllf_dm(m, u): + # Partial w.r.t. shape w/ optimal scale. See [7] Equation 5. + xu = x-u + return (1/m - (xu**m*np.log(xu)).sum()/(xu**m).sum() + + np.log(xu).sum()/n) + + def dnllf_du(m, u): + # Partial w.r.t. loc w/ optimal scale. See [7] Equation 6. + xu = x-u + return (m-1)/m*(xu**-1).sum() - n*(xu**(m-1)).sum()/(xu**m).sum() + + def get_scale(m, u): + # Partial w.r.t. scale solved in terms of shape and location. + # See [7] Equation 7. + return ((x-u)**m/n).sum()**(1/m) + + def dnllf(params): + # Partial derivatives of the NLLF w.r.t. parameters, i.e. + # first order necessary conditions for MLE fit. + return [dnllf_dm(*params), dnllf_du(*params)] + + suggestion = ("Maximum likelihood estimation is known to be challenging " + "for the three-parameter Weibull distribution. Consider " + "performing a custom goodness-of-fit test using " + "`scipy.stats.monte_carlo_test`.") + + if np.allclose(u, np.min(x)) or m < 1: + # The critical values provided by [7] don't seem to control the + # Type I error rate in this case. Error out. + message = ("Maximum likelihood estimation has converged to " + "a solution in which the location is equal to the minimum " + "of the data, the shape parameter is less than 2, or both. " + "The table of critical values in [7] does not " + "include this case. " + suggestion) + raise ValueError(message) + + try: + # Refine the MLE / verify that first-order necessary conditions are + # satisfied. If so, the critical values provided in [7] seem reliable. + with np.errstate(over='raise', invalid='raise'): + res = optimize.root(dnllf, params[:-1]) + + message = ("Solution of MLE first-order conditions failed: " + f"{res.message}. `anderson` cannot continue. " + suggestion) + if not res.success: + raise ValueError(message) + + except (FloatingPointError, ValueError) as e: + message = ("An error occurred while fitting the Weibull distribution " + "to the data, so `anderson` cannot continue. " + suggestion) + raise ValueError(message) from e + + m, u = res.x + s = get_scale(m, u) + return m, u, s + + +AndersonResult = _make_tuple_bunch('AndersonResult', + ['statistic', 'critical_values', + 'significance_level'], ['fit_result']) + + +def anderson(x, dist='norm'): + """Anderson-Darling test for data coming from a particular distribution. + + The Anderson-Darling test tests the null hypothesis that a sample is + drawn from a population that follows a particular distribution. + For the Anderson-Darling test, the critical values depend on + which distribution is being tested against. This function works + for normal, exponential, logistic, weibull_min, or Gumbel (Extreme Value + Type I) distributions. + + Parameters + ---------- + x : array_like + Array of sample data. + dist : {'norm', 'expon', 'logistic', 'gumbel', 'gumbel_l', 'gumbel_r', 'extreme1', 'weibull_min'}, optional + The type of distribution to test against. The default is 'norm'. + The names 'extreme1', 'gumbel_l' and 'gumbel' are synonyms for the + same distribution. + + Returns + ------- + result : AndersonResult + An object with the following attributes: + + statistic : float + The Anderson-Darling test statistic. + critical_values : list + The critical values for this distribution. + significance_level : list + The significance levels for the corresponding critical values + in percents. The function returns critical values for a + differing set of significance levels depending on the + distribution that is being tested against. + fit_result : `~scipy.stats._result_classes.FitResult` + An object containing the results of fitting the distribution to + the data. + + See Also + -------- + kstest : The Kolmogorov-Smirnov test for goodness-of-fit. + + Notes + ----- + Critical values provided are for the following significance levels: + + normal/exponential + 15%, 10%, 5%, 2.5%, 1% + logistic + 25%, 10%, 5%, 2.5%, 1%, 0.5% + gumbel_l / gumbel_r + 25%, 10%, 5%, 2.5%, 1% + weibull_min + 50%, 25%, 15%, 10%, 5%, 2.5%, 1%, 0.5% + + If the returned statistic is larger than these critical values then + for the corresponding significance level, the null hypothesis that + the data come from the chosen distribution can be rejected. + The returned statistic is referred to as 'A2' in the references. + + For `weibull_min`, maximum likelihood estimation is known to be + challenging. If the test returns successfully, then the first order + conditions for a maximum likehood estimate have been verified and + the critical values correspond relatively well to the significance levels, + provided that the sample is sufficiently large (>10 observations [7]). + However, for some data - especially data with no left tail - `anderson` + is likely to result in an error message. In this case, consider + performing a custom goodness of fit test using + `scipy.stats.monte_carlo_test`. + + References + ---------- + .. [1] https://www.itl.nist.gov/div898/handbook/prc/section2/prc213.htm + .. [2] Stephens, M. A. (1974). EDF Statistics for Goodness of Fit and + Some Comparisons, Journal of the American Statistical Association, + Vol. 69, pp. 730-737. + .. [3] Stephens, M. A. (1976). Asymptotic Results for Goodness-of-Fit + Statistics with Unknown Parameters, Annals of Statistics, Vol. 4, + pp. 357-369. + .. [4] Stephens, M. A. (1977). Goodness of Fit for the Extreme Value + Distribution, Biometrika, Vol. 64, pp. 583-588. + .. [5] Stephens, M. A. (1977). Goodness of Fit with Special Reference + to Tests for Exponentiality , Technical Report No. 262, + Department of Statistics, Stanford University, Stanford, CA. + .. [6] Stephens, M. A. (1979). Tests of Fit for the Logistic Distribution + Based on the Empirical Distribution Function, Biometrika, Vol. 66, + pp. 591-595. + .. [7] Richard A. Lockhart and Michael A. Stephens "Estimation and Tests of + Fit for the Three-Parameter Weibull Distribution" + Journal of the Royal Statistical Society.Series B(Methodological) + Vol. 56, No. 3 (1994), pp. 491-500, Table 0. + + Examples + -------- + Test the null hypothesis that a random sample was drawn from a normal + distribution (with unspecified mean and standard deviation). + + >>> import numpy as np + >>> from scipy.stats import anderson + >>> rng = np.random.default_rng() + >>> data = rng.random(size=35) + >>> res = anderson(data) + >>> res.statistic + 0.8398018749744764 + >>> res.critical_values + array([0.527, 0.6 , 0.719, 0.839, 0.998]) + >>> res.significance_level + array([15. , 10. , 5. , 2.5, 1. ]) + + The value of the statistic (barely) exceeds the critical value associated + with a significance level of 2.5%, so the null hypothesis may be rejected + at a significance level of 2.5%, but not at a significance level of 1%. + + """ # numpy/numpydoc#87 # noqa: E501 + dist = dist.lower() + if dist in {'extreme1', 'gumbel'}: + dist = 'gumbel_l' + dists = {'norm', 'expon', 'gumbel_l', + 'gumbel_r', 'logistic', 'weibull_min'} + + if dist not in dists: + raise ValueError(f"Invalid distribution; dist must be in {dists}.") + y = sort(x) + xbar = np.mean(x, axis=0) + N = len(y) + if dist == 'norm': + s = np.std(x, ddof=1, axis=0) + w = (y - xbar) / s + fit_params = xbar, s + logcdf = distributions.norm.logcdf(w) + logsf = distributions.norm.logsf(w) + sig = array([15, 10, 5, 2.5, 1]) + critical = around(_Avals_norm / (1.0 + 4.0/N - 25.0/N/N), 3) + elif dist == 'expon': + w = y / xbar + fit_params = 0, xbar + logcdf = distributions.expon.logcdf(w) + logsf = distributions.expon.logsf(w) + sig = array([15, 10, 5, 2.5, 1]) + critical = around(_Avals_expon / (1.0 + 0.6/N), 3) + elif dist == 'logistic': + def rootfunc(ab, xj, N): + a, b = ab + tmp = (xj - a) / b + tmp2 = exp(tmp) + val = [np.sum(1.0/(1+tmp2), axis=0) - 0.5*N, + np.sum(tmp*(1.0-tmp2)/(1+tmp2), axis=0) + N] + return array(val) + + sol0 = array([xbar, np.std(x, ddof=1, axis=0)]) + sol = optimize.fsolve(rootfunc, sol0, args=(x, N), xtol=1e-5) + w = (y - sol[0]) / sol[1] + fit_params = sol + logcdf = distributions.logistic.logcdf(w) + logsf = distributions.logistic.logsf(w) + sig = array([25, 10, 5, 2.5, 1, 0.5]) + critical = around(_Avals_logistic / (1.0 + 0.25/N), 3) + elif dist == 'gumbel_r': + xbar, s = distributions.gumbel_r.fit(x) + w = (y - xbar) / s + fit_params = xbar, s + logcdf = distributions.gumbel_r.logcdf(w) + logsf = distributions.gumbel_r.logsf(w) + sig = array([25, 10, 5, 2.5, 1]) + critical = around(_Avals_gumbel / (1.0 + 0.2/sqrt(N)), 3) + elif dist == 'gumbel_l': + xbar, s = distributions.gumbel_l.fit(x) + w = (y - xbar) / s + fit_params = xbar, s + logcdf = distributions.gumbel_l.logcdf(w) + logsf = distributions.gumbel_l.logsf(w) + sig = array([25, 10, 5, 2.5, 1]) + critical = around(_Avals_gumbel / (1.0 + 0.2/sqrt(N)), 3) + elif dist == 'weibull_min': + message = ("Critical values of the test statistic are given for the " + "asymptotic distribution. These may not be accurate for " + "samples with fewer than 10 observations. Consider using " + "`scipy.stats.monte_carlo_test`.") + if N < 10: + warnings.warn(message, stacklevel=2) + # [7] writes our 'c' as 'm', and they write `c = 1/m`. Use their names. + m, loc, scale = distributions.weibull_min.fit(y) + m, loc, scale = _weibull_fit_check((m, loc, scale), y) + fit_params = m, loc, scale + logcdf = stats.weibull_min(*fit_params).logcdf(y) + logsf = stats.weibull_min(*fit_params).logsf(y) + c = 1 / m # m and c are as used in [7] + sig = array([0.5, 0.75, 0.85, 0.9, 0.95, 0.975, 0.99, 0.995]) + critical = _get_As_weibull(c) + # Goodness-of-fit tests should only be used to provide evidence + # _against_ the null hypothesis. Be conservative and round up. + critical = np.round(critical + 0.0005, decimals=3) + + i = arange(1, N + 1) + A2 = -N - np.sum((2*i - 1.0) / N * (logcdf + logsf[::-1]), axis=0) + + # FitResult initializer expects an optimize result, so let's work with it + message = '`anderson` successfully fit the distribution to the data.' + res = optimize.OptimizeResult(success=True, message=message) + res.x = np.array(fit_params) + fit_result = FitResult(getattr(distributions, dist), y, + discrete=False, res=res) + + return AndersonResult(A2, critical, sig, fit_result=fit_result) + + +def _anderson_ksamp_midrank(samples, Z, Zstar, k, n, N): + """Compute A2akN equation 7 of Scholz and Stephens. + + Parameters + ---------- + samples : sequence of 1-D array_like + Array of sample arrays. + Z : array_like + Sorted array of all observations. + Zstar : array_like + Sorted array of unique observations. + k : int + Number of samples. + n : array_like + Number of observations in each sample. + N : int + Total number of observations. + + Returns + ------- + A2aKN : float + The A2aKN statistics of Scholz and Stephens 1987. + + """ + A2akN = 0. + Z_ssorted_left = Z.searchsorted(Zstar, 'left') + if N == Zstar.size: + lj = 1. + else: + lj = Z.searchsorted(Zstar, 'right') - Z_ssorted_left + Bj = Z_ssorted_left + lj / 2. + for i in arange(0, k): + s = np.sort(samples[i]) + s_ssorted_right = s.searchsorted(Zstar, side='right') + Mij = s_ssorted_right.astype(float) + fij = s_ssorted_right - s.searchsorted(Zstar, 'left') + Mij -= fij / 2. + inner = lj / float(N) * (N*Mij - Bj*n[i])**2 / (Bj*(N - Bj) - N*lj/4.) + A2akN += inner.sum() / n[i] + A2akN *= (N - 1.) / N + return A2akN + + +def _anderson_ksamp_right(samples, Z, Zstar, k, n, N): + """Compute A2akN equation 6 of Scholz & Stephens. + + Parameters + ---------- + samples : sequence of 1-D array_like + Array of sample arrays. + Z : array_like + Sorted array of all observations. + Zstar : array_like + Sorted array of unique observations. + k : int + Number of samples. + n : array_like + Number of observations in each sample. + N : int + Total number of observations. + + Returns + ------- + A2KN : float + The A2KN statistics of Scholz and Stephens 1987. + + """ + A2kN = 0. + lj = Z.searchsorted(Zstar[:-1], 'right') - Z.searchsorted(Zstar[:-1], + 'left') + Bj = lj.cumsum() + for i in arange(0, k): + s = np.sort(samples[i]) + Mij = s.searchsorted(Zstar[:-1], side='right') + inner = lj / float(N) * (N * Mij - Bj * n[i])**2 / (Bj * (N - Bj)) + A2kN += inner.sum() / n[i] + return A2kN + + +Anderson_ksampResult = _make_tuple_bunch( + 'Anderson_ksampResult', + ['statistic', 'critical_values', 'pvalue'], [] +) + + +def anderson_ksamp(samples, midrank=True, *, method=None): + """The Anderson-Darling test for k-samples. + + The k-sample Anderson-Darling test is a modification of the + one-sample Anderson-Darling test. It tests the null hypothesis + that k-samples are drawn from the same population without having + to specify the distribution function of that population. The + critical values depend on the number of samples. + + Parameters + ---------- + samples : sequence of 1-D array_like + Array of sample data in arrays. + midrank : bool, optional + Type of Anderson-Darling test which is computed. Default + (True) is the midrank test applicable to continuous and + discrete populations. If False, the right side empirical + distribution is used. + method : PermutationMethod, optional + Defines the method used to compute the p-value. If `method` is an + instance of `PermutationMethod`, the p-value is computed using + `scipy.stats.permutation_test` with the provided configuration options + and other appropriate settings. Otherwise, the p-value is interpolated + from tabulated values. + + Returns + ------- + res : Anderson_ksampResult + An object containing attributes: + + statistic : float + Normalized k-sample Anderson-Darling test statistic. + critical_values : array + The critical values for significance levels 25%, 10%, 5%, 2.5%, 1%, + 0.5%, 0.1%. + pvalue : float + The approximate p-value of the test. If `method` is not + provided, the value is floored / capped at 0.1% / 25%. + + Raises + ------ + ValueError + If fewer than 2 samples are provided, a sample is empty, or no + distinct observations are in the samples. + + See Also + -------- + ks_2samp : 2 sample Kolmogorov-Smirnov test + anderson : 1 sample Anderson-Darling test + + Notes + ----- + [1]_ defines three versions of the k-sample Anderson-Darling test: + one for continuous distributions and two for discrete + distributions, in which ties between samples may occur. The + default of this routine is to compute the version based on the + midrank empirical distribution function. This test is applicable + to continuous and discrete data. If midrank is set to False, the + right side empirical distribution is used for a test for discrete + data. According to [1]_, the two discrete test statistics differ + only slightly if a few collisions due to round-off errors occur in + the test not adjusted for ties between samples. + + The critical values corresponding to the significance levels from 0.01 + to 0.25 are taken from [1]_. p-values are floored / capped + at 0.1% / 25%. Since the range of critical values might be extended in + future releases, it is recommended not to test ``p == 0.25``, but rather + ``p >= 0.25`` (analogously for the lower bound). + + .. versionadded:: 0.14.0 + + References + ---------- + .. [1] Scholz, F. W and Stephens, M. A. (1987), K-Sample + Anderson-Darling Tests, Journal of the American Statistical + Association, Vol. 82, pp. 918-924. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> res = stats.anderson_ksamp([rng.normal(size=50), + ... rng.normal(loc=0.5, size=30)]) + >>> res.statistic, res.pvalue + (1.974403288713695, 0.04991293614572478) + >>> res.critical_values + array([0.325, 1.226, 1.961, 2.718, 3.752, 4.592, 6.546]) + + The null hypothesis that the two random samples come from the same + distribution can be rejected at the 5% level because the returned + test value is greater than the critical value for 5% (1.961) but + not at the 2.5% level. The interpolation gives an approximate + p-value of 4.99%. + + >>> samples = [rng.normal(size=50), rng.normal(size=30), + ... rng.normal(size=20)] + >>> res = stats.anderson_ksamp(samples) + >>> res.statistic, res.pvalue + (-0.29103725200789504, 0.25) + >>> res.critical_values + array([ 0.44925884, 1.3052767 , 1.9434184 , 2.57696569, 3.41634856, + 4.07210043, 5.56419101]) + + The null hypothesis cannot be rejected for three samples from an + identical distribution. The reported p-value (25%) has been capped and + may not be very accurate (since it corresponds to the value 0.449 + whereas the statistic is -0.291). + + In such cases where the p-value is capped or when sample sizes are + small, a permutation test may be more accurate. + + >>> method = stats.PermutationMethod(n_resamples=9999, random_state=rng) + >>> res = stats.anderson_ksamp(samples, method=method) + >>> res.pvalue + 0.5254 + + """ + k = len(samples) + if (k < 2): + raise ValueError("anderson_ksamp needs at least two samples") + + samples = list(map(np.asarray, samples)) + Z = np.sort(np.hstack(samples)) + N = Z.size + Zstar = np.unique(Z) + if Zstar.size < 2: + raise ValueError("anderson_ksamp needs more than one distinct " + "observation") + + n = np.array([sample.size for sample in samples]) + if np.any(n == 0): + raise ValueError("anderson_ksamp encountered sample without " + "observations") + + if midrank: + A2kN_fun = _anderson_ksamp_midrank + else: + A2kN_fun = _anderson_ksamp_right + A2kN = A2kN_fun(samples, Z, Zstar, k, n, N) + + def statistic(*samples): + return A2kN_fun(samples, Z, Zstar, k, n, N) + + if method is not None: + res = stats.permutation_test(samples, statistic, **method._asdict(), + alternative='greater') + + H = (1. / n).sum() + hs_cs = (1. / arange(N - 1, 1, -1)).cumsum() + h = hs_cs[-1] + 1 + g = (hs_cs / arange(2, N)).sum() + + a = (4*g - 6) * (k - 1) + (10 - 6*g)*H + b = (2*g - 4)*k**2 + 8*h*k + (2*g - 14*h - 4)*H - 8*h + 4*g - 6 + c = (6*h + 2*g - 2)*k**2 + (4*h - 4*g + 6)*k + (2*h - 6)*H + 4*h + d = (2*h + 6)*k**2 - 4*h*k + sigmasq = (a*N**3 + b*N**2 + c*N + d) / ((N - 1.) * (N - 2.) * (N - 3.)) + m = k - 1 + A2 = (A2kN - m) / math.sqrt(sigmasq) + + # The b_i values are the interpolation coefficients from Table 2 + # of Scholz and Stephens 1987 + b0 = np.array([0.675, 1.281, 1.645, 1.96, 2.326, 2.573, 3.085]) + b1 = np.array([-0.245, 0.25, 0.678, 1.149, 1.822, 2.364, 3.615]) + b2 = np.array([-0.105, -0.305, -0.362, -0.391, -0.396, -0.345, -0.154]) + critical = b0 + b1 / math.sqrt(m) + b2 / m + + sig = np.array([0.25, 0.1, 0.05, 0.025, 0.01, 0.005, 0.001]) + + if A2 < critical.min() and method is None: + p = sig.max() + msg = (f"p-value capped: true value larger than {p}. Consider " + "specifying `method` " + "(e.g. `method=stats.PermutationMethod()`.)") + warnings.warn(msg, stacklevel=2) + elif A2 > critical.max() and method is None: + p = sig.min() + msg = (f"p-value floored: true value smaller than {p}. Consider " + "specifying `method` " + "(e.g. `method=stats.PermutationMethod()`.)") + warnings.warn(msg, stacklevel=2) + elif method is None: + # interpolation of probit of significance level + pf = np.polyfit(critical, log(sig), 2) + p = math.exp(np.polyval(pf, A2)) + else: + p = res.pvalue if method is not None else p + + # create result object with alias for backward compatibility + res = Anderson_ksampResult(A2, critical, p) + res.significance_level = p + return res + + +AnsariResult = namedtuple('AnsariResult', ('statistic', 'pvalue')) + + +class _ABW: + """Distribution of Ansari-Bradley W-statistic under the null hypothesis.""" + # TODO: calculate exact distribution considering ties + # We could avoid summing over more than half the frequencies, + # but initially it doesn't seem worth the extra complexity + + def __init__(self): + """Minimal initializer.""" + self.m = None + self.n = None + self.astart = None + self.total = None + self.freqs = None + + def _recalc(self, n, m): + """When necessary, recalculate exact distribution.""" + if n != self.n or m != self.m: + self.n, self.m = n, m + # distribution is NOT symmetric when m + n is odd + # n is len(x), m is len(y), and ratio of scales is defined x/y + astart, a1, _ = gscale(n, m) + self.astart = astart # minimum value of statistic + # Exact distribution of test statistic under null hypothesis + # expressed as frequencies/counts/integers to maintain precision. + # Stored as floats to avoid overflow of sums. + self.freqs = a1.astype(np.float64) + self.total = self.freqs.sum() # could calculate from m and n + # probability mass is self.freqs / self.total; + + def pmf(self, k, n, m): + """Probability mass function.""" + self._recalc(n, m) + # The convention here is that PMF at k = 12.5 is the same as at k = 12, + # -> use `floor` in case of ties. + ind = np.floor(k - self.astart).astype(int) + return self.freqs[ind] / self.total + + def cdf(self, k, n, m): + """Cumulative distribution function.""" + self._recalc(n, m) + # Null distribution derived without considering ties is + # approximate. Round down to avoid Type I error. + ind = np.ceil(k - self.astart).astype(int) + return self.freqs[:ind+1].sum() / self.total + + def sf(self, k, n, m): + """Survival function.""" + self._recalc(n, m) + # Null distribution derived without considering ties is + # approximate. Round down to avoid Type I error. + ind = np.floor(k - self.astart).astype(int) + return self.freqs[ind:].sum() / self.total + + +# Maintain state for faster repeat calls to ansari w/ method='exact' +_abw_state = _ABW() + + +@_axis_nan_policy_factory(AnsariResult, n_samples=2) +def ansari(x, y, alternative='two-sided'): + """Perform the Ansari-Bradley test for equal scale parameters. + + The Ansari-Bradley test ([1]_, [2]_) is a non-parametric test + for the equality of the scale parameter of the distributions + from which two samples were drawn. The null hypothesis states that + the ratio of the scale of the distribution underlying `x` to the scale + of the distribution underlying `y` is 1. + + Parameters + ---------- + x, y : array_like + Arrays of sample data. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the ratio of scales is not equal to 1. + * 'less': the ratio of scales is less than 1. + * 'greater': the ratio of scales is greater than 1. + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float + The Ansari-Bradley test statistic. + pvalue : float + The p-value of the hypothesis test. + + See Also + -------- + fligner : A non-parametric test for the equality of k variances + mood : A non-parametric test for the equality of two scale parameters + + Notes + ----- + The p-value given is exact when the sample sizes are both less than + 55 and there are no ties, otherwise a normal approximation for the + p-value is used. + + References + ---------- + .. [1] Ansari, A. R. and Bradley, R. A. (1960) Rank-sum tests for + dispersions, Annals of Mathematical Statistics, 31, 1174-1189. + .. [2] Sprent, Peter and N.C. Smeeton. Applied nonparametric + statistical methods. 3rd ed. Chapman and Hall/CRC. 2001. + Section 5.8.2. + .. [3] Nathaniel E. Helwig "Nonparametric Dispersion and Equality + Tests" at http://users.stat.umn.edu/~helwig/notes/npde-Notes.pdf + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import ansari + >>> rng = np.random.default_rng() + + For these examples, we'll create three random data sets. The first + two, with sizes 35 and 25, are drawn from a normal distribution with + mean 0 and standard deviation 2. The third data set has size 25 and + is drawn from a normal distribution with standard deviation 1.25. + + >>> x1 = rng.normal(loc=0, scale=2, size=35) + >>> x2 = rng.normal(loc=0, scale=2, size=25) + >>> x3 = rng.normal(loc=0, scale=1.25, size=25) + + First we apply `ansari` to `x1` and `x2`. These samples are drawn + from the same distribution, so we expect the Ansari-Bradley test + should not lead us to conclude that the scales of the distributions + are different. + + >>> ansari(x1, x2) + AnsariResult(statistic=541.0, pvalue=0.9762532927399098) + + With a p-value close to 1, we cannot conclude that there is a + significant difference in the scales (as expected). + + Now apply the test to `x1` and `x3`: + + >>> ansari(x1, x3) + AnsariResult(statistic=425.0, pvalue=0.0003087020407974518) + + The probability of observing such an extreme value of the statistic + under the null hypothesis of equal scales is only 0.03087%. We take this + as evidence against the null hypothesis in favor of the alternative: + the scales of the distributions from which the samples were drawn + are not equal. + + We can use the `alternative` parameter to perform a one-tailed test. + In the above example, the scale of `x1` is greater than `x3` and so + the ratio of scales of `x1` and `x3` is greater than 1. This means + that the p-value when ``alternative='greater'`` should be near 0 and + hence we should be able to reject the null hypothesis: + + >>> ansari(x1, x3, alternative='greater') + AnsariResult(statistic=425.0, pvalue=0.0001543510203987259) + + As we can see, the p-value is indeed quite low. Use of + ``alternative='less'`` should thus yield a large p-value: + + >>> ansari(x1, x3, alternative='less') + AnsariResult(statistic=425.0, pvalue=0.9998643258449039) + + """ + if alternative not in {'two-sided', 'greater', 'less'}: + raise ValueError("'alternative' must be 'two-sided'," + " 'greater', or 'less'.") + x, y = asarray(x), asarray(y) + n = len(x) + m = len(y) + if m < 1: + raise ValueError("Not enough other observations.") + if n < 1: + raise ValueError("Not enough test observations.") + + N = m + n + xy = r_[x, y] # combine + rank = _stats_py.rankdata(xy) + symrank = amin(array((rank, N - rank + 1)), 0) + AB = np.sum(symrank[:n], axis=0) + uxy = unique(xy) + repeats = (len(uxy) != len(xy)) + exact = ((m < 55) and (n < 55) and not repeats) + if repeats and (m < 55 or n < 55): + warnings.warn("Ties preclude use of exact statistic.", stacklevel=2) + if exact: + if alternative == 'two-sided': + pval = 2.0 * np.minimum(_abw_state.cdf(AB, n, m), + _abw_state.sf(AB, n, m)) + elif alternative == 'greater': + # AB statistic is _smaller_ when ratio of scales is larger, + # so this is the opposite of the usual calculation + pval = _abw_state.cdf(AB, n, m) + else: + pval = _abw_state.sf(AB, n, m) + return AnsariResult(AB, min(1.0, pval)) + + # otherwise compute normal approximation + if N % 2: # N odd + mnAB = n * (N+1.0)**2 / 4.0 / N + varAB = n * m * (N+1.0) * (3+N**2) / (48.0 * N**2) + else: + mnAB = n * (N+2.0) / 4.0 + varAB = m * n * (N+2) * (N-2.0) / 48 / (N-1.0) + if repeats: # adjust variance estimates + # compute np.sum(tj * rj**2,axis=0) + fac = np.sum(symrank**2, axis=0) + if N % 2: # N odd + varAB = m * n * (16*N*fac - (N+1)**4) / (16.0 * N**2 * (N-1)) + else: # N even + varAB = m * n * (16*fac - N*(N+2)**2) / (16.0 * N * (N-1)) + + # Small values of AB indicate larger dispersion for the x sample. + # Large values of AB indicate larger dispersion for the y sample. + # This is opposite to the way we define the ratio of scales. see [1]_. + z = (mnAB - AB) / sqrt(varAB) + pvalue = _get_pvalue(z, distributions.norm, alternative) + return AnsariResult(AB[()], pvalue[()]) + + +BartlettResult = namedtuple('BartlettResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(BartlettResult, n_samples=None) +def bartlett(*samples): + r"""Perform Bartlett's test for equal variances. + + Bartlett's test tests the null hypothesis that all input samples + are from populations with equal variances. For samples + from significantly non-normal populations, Levene's test + `levene` is more robust. + + Parameters + ---------- + sample1, sample2, ... : array_like + arrays of sample data. Only 1d arrays are accepted, they may have + different lengths. + + Returns + ------- + statistic : float + The test statistic. + pvalue : float + The p-value of the test. + + See Also + -------- + fligner : A non-parametric test for the equality of k variances + levene : A robust parametric test for equality of k variances + + Notes + ----- + Conover et al. (1981) examine many of the existing parametric and + nonparametric tests by extensive simulations and they conclude that the + tests proposed by Fligner and Killeen (1976) and Levene (1960) appear to be + superior in terms of robustness of departures from normality and power + ([3]_). + + References + ---------- + .. [1] https://www.itl.nist.gov/div898/handbook/eda/section3/eda357.htm + .. [2] Snedecor, George W. and Cochran, William G. (1989), Statistical + Methods, Eighth Edition, Iowa State University Press. + .. [3] Park, C. and Lindsay, B. G. (1999). Robust Scale Estimation and + Hypothesis Testing based on Quadratic Inference Function. Technical + Report #99-03, Center for Likelihood Studies, Pennsylvania State + University. + .. [4] Bartlett, M. S. (1937). Properties of Sufficiency and Statistical + Tests. Proceedings of the Royal Society of London. Series A, + Mathematical and Physical Sciences, Vol. 160, No.901, pp. 268-282. + .. [5] C.I. BLISS (1952), The Statistics of Bioassay: With Special + Reference to the Vitamins, pp 499-503, + :doi:`10.1016/C2013-0-12584-6`. + .. [6] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [7] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are + superior to t and F tests in biomedical research. The American + Statistician, 52(2), 127-132. + + Examples + -------- + In [5]_, the influence of vitamin C on the tooth growth of guinea pigs + was investigated. In a control study, 60 subjects were divided into + small dose, medium dose, and large dose groups that received + daily doses of 0.5, 1.0 and 2.0 mg of vitamin C, respectively. + After 42 days, the tooth growth was measured. + + The ``small_dose``, ``medium_dose``, and ``large_dose`` arrays below record + tooth growth measurements of the three groups in microns. + + >>> import numpy as np + >>> small_dose = np.array([ + ... 4.2, 11.5, 7.3, 5.8, 6.4, 10, 11.2, 11.2, 5.2, 7, + ... 15.2, 21.5, 17.6, 9.7, 14.5, 10, 8.2, 9.4, 16.5, 9.7 + ... ]) + >>> medium_dose = np.array([ + ... 16.5, 16.5, 15.2, 17.3, 22.5, 17.3, 13.6, 14.5, 18.8, 15.5, + ... 19.7, 23.3, 23.6, 26.4, 20, 25.2, 25.8, 21.2, 14.5, 27.3 + ... ]) + >>> large_dose = np.array([ + ... 23.6, 18.5, 33.9, 25.5, 26.4, 32.5, 26.7, 21.5, 23.3, 29.5, + ... 25.5, 26.4, 22.4, 24.5, 24.8, 30.9, 26.4, 27.3, 29.4, 23 + ... ]) + + The `bartlett` statistic is sensitive to differences in variances + between the samples. + + >>> from scipy import stats + >>> res = stats.bartlett(small_dose, medium_dose, large_dose) + >>> res.statistic + 0.6654670663030519 + + The value of the statistic tends to be high when there is a large + difference in variances. + + We can test for inequality of variance among the groups by comparing the + observed value of the statistic against the null distribution: the + distribution of statistic values derived under the null hypothesis that + the population variances of the three groups are equal. + + For this test, the null distribution follows the chi-square distribution + as shown below. + + >>> import matplotlib.pyplot as plt + >>> k = 3 # number of samples + >>> dist = stats.chi2(df=k-1) + >>> val = np.linspace(0, 5, 100) + >>> pdf = dist.pdf(val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(val, pdf, color='C0') + ... ax.set_title("Bartlett Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + ... ax.set_xlim(0, 5) + ... ax.set_ylim(0, 1) + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution greater than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (1.5, 0.22), (2.25, 0.3), arrowprops=props) + >>> i = val >= res.statistic + >>> ax.fill_between(val[i], y1=0, y2=pdf[i], color='C0') + >>> plt.show() + + >>> res.pvalue + 0.71696121509966 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from distributions with identical variances that produces + such an extreme value of the statistic - this may be taken as evidence + against the null hypothesis in favor of the alternative: the variances of + the groups are not equal. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [6]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + - Small p-values are not evidence for a *large* effect; rather, they can + only provide evidence for a "significant" effect, meaning that they are + unlikely to have occurred under the null hypothesis. + + Note that the chi-square distribution provides the null distribution + when the observations are normally distributed. For small samples + drawn from non-normal populations, it may be more appropriate to + perform a + permutation test: Under the null hypothesis that all three samples were + drawn from the same population, each of the measurements is equally likely + to have been observed in any of the three samples. Therefore, we can form + a randomized null distribution by calculating the statistic under many + randomly-generated partitionings of the observations into the three + samples. + + >>> def statistic(*samples): + ... return stats.bartlett(*samples).statistic + >>> ref = stats.permutation_test( + ... (small_dose, medium_dose, large_dose), statistic, + ... permutation_type='independent', alternative='greater' + ... ) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> bins = np.linspace(0, 5, 25) + >>> ax.hist( + ... ref.null_distribution, bins=bins, density=True, facecolor="C1" + ... ) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'randomized null distribution']) + >>> plot(ax) + >>> plt.show() + + >>> ref.pvalue # randomized test p-value + 0.5387 # may vary + + Note that there is significant disagreement between the p-value calculated + here and the asymptotic approximation returned by `bartlett` above. + The statistical inferences that can be drawn rigorously from a permutation + test are limited; nonetheless, they may be the preferred approach in many + circumstances [7]_. + + Following is another generic example where the null hypothesis would be + rejected. + + Test whether the lists `a`, `b` and `c` come from populations + with equal variances. + + >>> a = [8.88, 9.12, 9.04, 8.98, 9.00, 9.08, 9.01, 8.85, 9.06, 8.99] + >>> b = [8.88, 8.95, 9.29, 9.44, 9.15, 9.58, 8.36, 9.18, 8.67, 9.05] + >>> c = [8.95, 9.12, 8.95, 8.85, 9.03, 8.84, 9.07, 8.98, 8.86, 8.98] + >>> stat, p = stats.bartlett(a, b, c) + >>> p + 1.1254782518834628e-05 + + The very small p-value suggests that the populations do not have equal + variances. + + This is not surprising, given that the sample variance of `b` is much + larger than that of `a` and `c`: + + >>> [np.var(x, ddof=1) for x in [a, b, c]] + [0.007054444444444413, 0.13073888888888888, 0.008890000000000002] + + """ + k = len(samples) + if k < 2: + raise ValueError("Must enter at least two input sample vectors.") + + # Handle empty input and input that is not 1d + for sample in samples: + if np.asanyarray(sample).size == 0: + NaN = _get_nan(*samples) # get NaN of result_dtype of all samples + return BartlettResult(NaN, NaN) + + Ni = np.empty(k) + ssq = np.empty(k, 'd') + for j in range(k): + Ni[j] = len(samples[j]) + ssq[j] = np.var(samples[j], ddof=1) + Ntot = np.sum(Ni, axis=0) + spsq = np.sum((Ni - 1)*ssq, axis=0) / (1.0*(Ntot - k)) + numer = (Ntot*1.0 - k) * log(spsq) - np.sum((Ni - 1.0)*log(ssq), axis=0) + denom = 1.0 + 1.0/(3*(k - 1)) * ((np.sum(1.0/(Ni - 1.0), axis=0)) - + 1.0/(Ntot - k)) + T = numer / denom + pval = distributions.chi2.sf(T, k - 1) # 1 - cdf + + return BartlettResult(T, pval) + + +LeveneResult = namedtuple('LeveneResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(LeveneResult, n_samples=None) +def levene(*samples, center='median', proportiontocut=0.05): + r"""Perform Levene test for equal variances. + + The Levene test tests the null hypothesis that all input samples + are from populations with equal variances. Levene's test is an + alternative to Bartlett's test `bartlett` in the case where + there are significant deviations from normality. + + Parameters + ---------- + sample1, sample2, ... : array_like + The sample data, possibly with different lengths. Only one-dimensional + samples are accepted. + center : {'mean', 'median', 'trimmed'}, optional + Which function of the data to use in the test. The default + is 'median'. + proportiontocut : float, optional + When `center` is 'trimmed', this gives the proportion of data points + to cut from each end. (See `scipy.stats.trim_mean`.) + Default is 0.05. + + Returns + ------- + statistic : float + The test statistic. + pvalue : float + The p-value for the test. + + See Also + -------- + fligner : A non-parametric test for the equality of k variances + bartlett : A parametric test for equality of k variances in normal samples + + Notes + ----- + Three variations of Levene's test are possible. The possibilities + and their recommended usages are: + + * 'median' : Recommended for skewed (non-normal) distributions> + * 'mean' : Recommended for symmetric, moderate-tailed distributions. + * 'trimmed' : Recommended for heavy-tailed distributions. + + The test version using the mean was proposed in the original article + of Levene ([2]_) while the median and trimmed mean have been studied by + Brown and Forsythe ([3]_), sometimes also referred to as Brown-Forsythe + test. + + References + ---------- + .. [1] https://www.itl.nist.gov/div898/handbook/eda/section3/eda35a.htm + .. [2] Levene, H. (1960). In Contributions to Probability and Statistics: + Essays in Honor of Harold Hotelling, I. Olkin et al. eds., + Stanford University Press, pp. 278-292. + .. [3] Brown, M. B. and Forsythe, A. B. (1974), Journal of the American + Statistical Association, 69, 364-367 + .. [4] C.I. BLISS (1952), The Statistics of Bioassay: With Special + Reference to the Vitamins, pp 499-503, + :doi:`10.1016/C2013-0-12584-6`. + .. [5] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [6] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are + superior to t and F tests in biomedical research. The American + Statistician, 52(2), 127-132. + + Examples + -------- + In [4]_, the influence of vitamin C on the tooth growth of guinea pigs + was investigated. In a control study, 60 subjects were divided into + small dose, medium dose, and large dose groups that received + daily doses of 0.5, 1.0 and 2.0 mg of vitamin C, respectively. + After 42 days, the tooth growth was measured. + + The ``small_dose``, ``medium_dose``, and ``large_dose`` arrays below record + tooth growth measurements of the three groups in microns. + + >>> import numpy as np + >>> small_dose = np.array([ + ... 4.2, 11.5, 7.3, 5.8, 6.4, 10, 11.2, 11.2, 5.2, 7, + ... 15.2, 21.5, 17.6, 9.7, 14.5, 10, 8.2, 9.4, 16.5, 9.7 + ... ]) + >>> medium_dose = np.array([ + ... 16.5, 16.5, 15.2, 17.3, 22.5, 17.3, 13.6, 14.5, 18.8, 15.5, + ... 19.7, 23.3, 23.6, 26.4, 20, 25.2, 25.8, 21.2, 14.5, 27.3 + ... ]) + >>> large_dose = np.array([ + ... 23.6, 18.5, 33.9, 25.5, 26.4, 32.5, 26.7, 21.5, 23.3, 29.5, + ... 25.5, 26.4, 22.4, 24.5, 24.8, 30.9, 26.4, 27.3, 29.4, 23 + ... ]) + + The `levene` statistic is sensitive to differences in variances + between the samples. + + >>> from scipy import stats + >>> res = stats.levene(small_dose, medium_dose, large_dose) + >>> res.statistic + 0.6457341109631506 + + The value of the statistic tends to be high when there is a large + difference in variances. + + We can test for inequality of variance among the groups by comparing the + observed value of the statistic against the null distribution: the + distribution of statistic values derived under the null hypothesis that + the population variances of the three groups are equal. + + For this test, the null distribution follows the F distribution as shown + below. + + >>> import matplotlib.pyplot as plt + >>> k, n = 3, 60 # number of samples, total number of observations + >>> dist = stats.f(dfn=k-1, dfd=n-k) + >>> val = np.linspace(0, 5, 100) + >>> pdf = dist.pdf(val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(val, pdf, color='C0') + ... ax.set_title("Levene Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + ... ax.set_xlim(0, 5) + ... ax.set_ylim(0, 1) + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution greater than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (1.5, 0.22), (2.25, 0.3), arrowprops=props) + >>> i = val >= res.statistic + >>> ax.fill_between(val[i], y1=0, y2=pdf[i], color='C0') + >>> plt.show() + + >>> res.pvalue + 0.5280694573759905 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from distributions with identical variances that produces + such an extreme value of the statistic - this may be taken as evidence + against the null hypothesis in favor of the alternative: the variances of + the groups are not equal. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [5]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + - Small p-values are not evidence for a *large* effect; rather, they can + only provide evidence for a "significant" effect, meaning that they are + unlikely to have occurred under the null hypothesis. + + Note that the F distribution provides an asymptotic approximation of the + null distribution. + For small samples, it may be more appropriate to perform a permutation + test: Under the null hypothesis that all three samples were drawn from + the same population, each of the measurements is equally likely to have + been observed in any of the three samples. Therefore, we can form a + randomized null distribution by calculating the statistic under many + randomly-generated partitionings of the observations into the three + samples. + + >>> def statistic(*samples): + ... return stats.levene(*samples).statistic + >>> ref = stats.permutation_test( + ... (small_dose, medium_dose, large_dose), statistic, + ... permutation_type='independent', alternative='greater' + ... ) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> bins = np.linspace(0, 5, 25) + >>> ax.hist( + ... ref.null_distribution, bins=bins, density=True, facecolor="C1" + ... ) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'randomized null distribution']) + >>> plot(ax) + >>> plt.show() + + >>> ref.pvalue # randomized test p-value + 0.4559 # may vary + + Note that there is significant disagreement between the p-value calculated + here and the asymptotic approximation returned by `levene` above. + The statistical inferences that can be drawn rigorously from a permutation + test are limited; nonetheless, they may be the preferred approach in many + circumstances [6]_. + + Following is another generic example where the null hypothesis would be + rejected. + + Test whether the lists `a`, `b` and `c` come from populations + with equal variances. + + >>> a = [8.88, 9.12, 9.04, 8.98, 9.00, 9.08, 9.01, 8.85, 9.06, 8.99] + >>> b = [8.88, 8.95, 9.29, 9.44, 9.15, 9.58, 8.36, 9.18, 8.67, 9.05] + >>> c = [8.95, 9.12, 8.95, 8.85, 9.03, 8.84, 9.07, 8.98, 8.86, 8.98] + >>> stat, p = stats.levene(a, b, c) + >>> p + 0.002431505967249681 + + The small p-value suggests that the populations do not have equal + variances. + + This is not surprising, given that the sample variance of `b` is much + larger than that of `a` and `c`: + + >>> [np.var(x, ddof=1) for x in [a, b, c]] + [0.007054444444444413, 0.13073888888888888, 0.008890000000000002] + + """ + if center not in ['mean', 'median', 'trimmed']: + raise ValueError("center must be 'mean', 'median' or 'trimmed'.") + + k = len(samples) + if k < 2: + raise ValueError("Must enter at least two input sample vectors.") + + Ni = np.empty(k) + Yci = np.empty(k, 'd') + + if center == 'median': + + def func(x): + return np.median(x, axis=0) + + elif center == 'mean': + + def func(x): + return np.mean(x, axis=0) + + else: # center == 'trimmed' + samples = tuple(_stats_py.trimboth(np.sort(sample), proportiontocut) + for sample in samples) + + def func(x): + return np.mean(x, axis=0) + + for j in range(k): + Ni[j] = len(samples[j]) + Yci[j] = func(samples[j]) + Ntot = np.sum(Ni, axis=0) + + # compute Zij's + Zij = [None] * k + for i in range(k): + Zij[i] = abs(asarray(samples[i]) - Yci[i]) + + # compute Zbari + Zbari = np.empty(k, 'd') + Zbar = 0.0 + for i in range(k): + Zbari[i] = np.mean(Zij[i], axis=0) + Zbar += Zbari[i] * Ni[i] + + Zbar /= Ntot + numer = (Ntot - k) * np.sum(Ni * (Zbari - Zbar)**2, axis=0) + + # compute denom_variance + dvar = 0.0 + for i in range(k): + dvar += np.sum((Zij[i] - Zbari[i])**2, axis=0) + + denom = (k - 1.0) * dvar + + W = numer / denom + pval = distributions.f.sf(W, k-1, Ntot-k) # 1 - cdf + return LeveneResult(W, pval) + + +def _apply_func(x, g, func): + # g is list of indices into x + # separating x into different groups + # func should be applied over the groups + g = unique(r_[0, g, len(x)]) + output = [func(x[g[k]:g[k+1]]) for k in range(len(g) - 1)] + + return asarray(output) + + +FlignerResult = namedtuple('FlignerResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(FlignerResult, n_samples=None) +def fligner(*samples, center='median', proportiontocut=0.05): + r"""Perform Fligner-Killeen test for equality of variance. + + Fligner's test tests the null hypothesis that all input samples + are from populations with equal variances. Fligner-Killeen's test is + distribution free when populations are identical [2]_. + + Parameters + ---------- + sample1, sample2, ... : array_like + Arrays of sample data. Need not be the same length. + center : {'mean', 'median', 'trimmed'}, optional + Keyword argument controlling which function of the data is used in + computing the test statistic. The default is 'median'. + proportiontocut : float, optional + When `center` is 'trimmed', this gives the proportion of data points + to cut from each end. (See `scipy.stats.trim_mean`.) + Default is 0.05. + + Returns + ------- + statistic : float + The test statistic. + pvalue : float + The p-value for the hypothesis test. + + See Also + -------- + bartlett : A parametric test for equality of k variances in normal samples + levene : A robust parametric test for equality of k variances + + Notes + ----- + As with Levene's test there are three variants of Fligner's test that + differ by the measure of central tendency used in the test. See `levene` + for more information. + + Conover et al. (1981) examine many of the existing parametric and + nonparametric tests by extensive simulations and they conclude that the + tests proposed by Fligner and Killeen (1976) and Levene (1960) appear to be + superior in terms of robustness of departures from normality and power + [3]_. + + References + ---------- + .. [1] Park, C. and Lindsay, B. G. (1999). Robust Scale Estimation and + Hypothesis Testing based on Quadratic Inference Function. Technical + Report #99-03, Center for Likelihood Studies, Pennsylvania State + University. + https://cecas.clemson.edu/~cspark/cv/paper/qif/draftqif2.pdf + .. [2] Fligner, M.A. and Killeen, T.J. (1976). Distribution-free two-sample + tests for scale. 'Journal of the American Statistical Association.' + 71(353), 210-213. + .. [3] Park, C. and Lindsay, B. G. (1999). Robust Scale Estimation and + Hypothesis Testing based on Quadratic Inference Function. Technical + Report #99-03, Center for Likelihood Studies, Pennsylvania State + University. + .. [4] Conover, W. J., Johnson, M. E. and Johnson M. M. (1981). A + comparative study of tests for homogeneity of variances, with + applications to the outer continental shelf bidding data. + Technometrics, 23(4), 351-361. + .. [5] C.I. BLISS (1952), The Statistics of Bioassay: With Special + Reference to the Vitamins, pp 499-503, + :doi:`10.1016/C2013-0-12584-6`. + .. [6] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [7] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are + superior to t and F tests in biomedical research. The American + Statistician, 52(2), 127-132. + + Examples + -------- + In [5]_, the influence of vitamin C on the tooth growth of guinea pigs + was investigated. In a control study, 60 subjects were divided into + small dose, medium dose, and large dose groups that received + daily doses of 0.5, 1.0 and 2.0 mg of vitamin C, respectively. + After 42 days, the tooth growth was measured. + + The ``small_dose``, ``medium_dose``, and ``large_dose`` arrays below record + tooth growth measurements of the three groups in microns. + + >>> import numpy as np + >>> small_dose = np.array([ + ... 4.2, 11.5, 7.3, 5.8, 6.4, 10, 11.2, 11.2, 5.2, 7, + ... 15.2, 21.5, 17.6, 9.7, 14.5, 10, 8.2, 9.4, 16.5, 9.7 + ... ]) + >>> medium_dose = np.array([ + ... 16.5, 16.5, 15.2, 17.3, 22.5, 17.3, 13.6, 14.5, 18.8, 15.5, + ... 19.7, 23.3, 23.6, 26.4, 20, 25.2, 25.8, 21.2, 14.5, 27.3 + ... ]) + >>> large_dose = np.array([ + ... 23.6, 18.5, 33.9, 25.5, 26.4, 32.5, 26.7, 21.5, 23.3, 29.5, + ... 25.5, 26.4, 22.4, 24.5, 24.8, 30.9, 26.4, 27.3, 29.4, 23 + ... ]) + + The `fligner` statistic is sensitive to differences in variances + between the samples. + + >>> from scipy import stats + >>> res = stats.fligner(small_dose, medium_dose, large_dose) + >>> res.statistic + 1.3878943408857916 + + The value of the statistic tends to be high when there is a large + difference in variances. + + We can test for inequality of variance among the groups by comparing the + observed value of the statistic against the null distribution: the + distribution of statistic values derived under the null hypothesis that + the population variances of the three groups are equal. + + For this test, the null distribution follows the chi-square distribution + as shown below. + + >>> import matplotlib.pyplot as plt + >>> k = 3 # number of samples + >>> dist = stats.chi2(df=k-1) + >>> val = np.linspace(0, 8, 100) + >>> pdf = dist.pdf(val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(val, pdf, color='C0') + ... ax.set_title("Fligner Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + ... ax.set_xlim(0, 8) + ... ax.set_ylim(0, 0.5) + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution greater than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (1.5, 0.22), (2.25, 0.3), arrowprops=props) + >>> i = val >= res.statistic + >>> ax.fill_between(val[i], y1=0, y2=pdf[i], color='C0') + >>> plt.show() + + >>> res.pvalue + 0.49960016501182125 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from distributions with identical variances that produces + such an extreme value of the statistic - this may be taken as evidence + against the null hypothesis in favor of the alternative: the variances of + the groups are not equal. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [6]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + - Small p-values are not evidence for a *large* effect; rather, they can + only provide evidence for a "significant" effect, meaning that they are + unlikely to have occurred under the null hypothesis. + + Note that the chi-square distribution provides an asymptotic approximation + of the null distribution. + For small samples, it may be more appropriate to perform a + permutation test: Under the null hypothesis that all three samples were + drawn from the same population, each of the measurements is equally likely + to have been observed in any of the three samples. Therefore, we can form + a randomized null distribution by calculating the statistic under many + randomly-generated partitionings of the observations into the three + samples. + + >>> def statistic(*samples): + ... return stats.fligner(*samples).statistic + >>> ref = stats.permutation_test( + ... (small_dose, medium_dose, large_dose), statistic, + ... permutation_type='independent', alternative='greater' + ... ) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> bins = np.linspace(0, 8, 25) + >>> ax.hist( + ... ref.null_distribution, bins=bins, density=True, facecolor="C1" + ... ) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'randomized null distribution']) + >>> plot(ax) + >>> plt.show() + + >>> ref.pvalue # randomized test p-value + 0.4332 # may vary + + Note that there is significant disagreement between the p-value calculated + here and the asymptotic approximation returned by `fligner` above. + The statistical inferences that can be drawn rigorously from a permutation + test are limited; nonetheless, they may be the preferred approach in many + circumstances [7]_. + + Following is another generic example where the null hypothesis would be + rejected. + + Test whether the lists `a`, `b` and `c` come from populations + with equal variances. + + >>> a = [8.88, 9.12, 9.04, 8.98, 9.00, 9.08, 9.01, 8.85, 9.06, 8.99] + >>> b = [8.88, 8.95, 9.29, 9.44, 9.15, 9.58, 8.36, 9.18, 8.67, 9.05] + >>> c = [8.95, 9.12, 8.95, 8.85, 9.03, 8.84, 9.07, 8.98, 8.86, 8.98] + >>> stat, p = stats.fligner(a, b, c) + >>> p + 0.00450826080004775 + + The small p-value suggests that the populations do not have equal + variances. + + This is not surprising, given that the sample variance of `b` is much + larger than that of `a` and `c`: + + >>> [np.var(x, ddof=1) for x in [a, b, c]] + [0.007054444444444413, 0.13073888888888888, 0.008890000000000002] + + """ + if center not in ['mean', 'median', 'trimmed']: + raise ValueError("center must be 'mean', 'median' or 'trimmed'.") + + k = len(samples) + if k < 2: + raise ValueError("Must enter at least two input sample vectors.") + + # Handle empty input + for sample in samples: + if sample.size == 0: + NaN = _get_nan(*samples) + return FlignerResult(NaN, NaN) + + if center == 'median': + + def func(x): + return np.median(x, axis=0) + + elif center == 'mean': + + def func(x): + return np.mean(x, axis=0) + + else: # center == 'trimmed' + samples = tuple(_stats_py.trimboth(sample, proportiontocut) + for sample in samples) + + def func(x): + return np.mean(x, axis=0) + + Ni = asarray([len(samples[j]) for j in range(k)]) + Yci = asarray([func(samples[j]) for j in range(k)]) + Ntot = np.sum(Ni, axis=0) + # compute Zij's + Zij = [abs(asarray(samples[i]) - Yci[i]) for i in range(k)] + allZij = [] + g = [0] + for i in range(k): + allZij.extend(list(Zij[i])) + g.append(len(allZij)) + + ranks = _stats_py.rankdata(allZij) + sample = distributions.norm.ppf(ranks / (2*(Ntot + 1.0)) + 0.5) + + # compute Aibar + Aibar = _apply_func(sample, g, np.sum) / Ni + anbar = np.mean(sample, axis=0) + varsq = np.var(sample, axis=0, ddof=1) + Xsq = np.sum(Ni * (asarray(Aibar) - anbar)**2.0, axis=0) / varsq + pval = distributions.chi2.sf(Xsq, k - 1) # 1 - cdf + return FlignerResult(Xsq, pval) + + +@_axis_nan_policy_factory(lambda x1: (x1,), n_samples=4, n_outputs=1) +def _mood_inner_lc(xy, x, diffs, sorted_xy, n, m, N) -> float: + # Obtain the unique values and their frequencies from the pooled samples. + # "a_j, + b_j, = t_j, for j = 1, ... k" where `k` is the number of unique + # classes, and "[t]he number of values associated with the x's and y's in + # the jth class will be denoted by a_j, and b_j respectively." + # (Mielke, 312) + # Reuse previously computed sorted array and `diff` arrays to obtain the + # unique values and counts. Prepend `diffs` with a non-zero to indicate + # that the first element should be marked as not matching what preceded it. + diffs_prep = np.concatenate(([1], diffs)) + # Unique elements are where the was a difference between elements in the + # sorted array + uniques = sorted_xy[diffs_prep != 0] + # The count of each element is the bin size for each set of consecutive + # differences where the difference is zero. Replace nonzero differences + # with 1 and then use the cumulative sum to count the indices. + t = np.bincount(np.cumsum(np.asarray(diffs_prep != 0, dtype=int)))[1:] + k = len(uniques) + js = np.arange(1, k + 1, dtype=int) + # the `b` array mentioned in the paper is not used, outside of the + # calculation of `t`, so we do not need to calculate it separately. Here + # we calculate `a`. In plain language, `a[j]` is the number of values in + # `x` that equal `uniques[j]`. + sorted_xyx = np.sort(np.concatenate((xy, x))) + diffs = np.diff(sorted_xyx) + diffs_prep = np.concatenate(([1], diffs)) + diff_is_zero = np.asarray(diffs_prep != 0, dtype=int) + xyx_counts = np.bincount(np.cumsum(diff_is_zero))[1:] + a = xyx_counts - t + # "Define .. a_0 = b_0 = t_0 = S_0 = 0" (Mielke 312) so we shift `a` + # and `t` arrays over 1 to allow a first element of 0 to accommodate this + # indexing. + t = np.concatenate(([0], t)) + a = np.concatenate(([0], a)) + # S is built from `t`, so it does not need a preceding zero added on. + S = np.cumsum(t) + # define a copy of `S` with a prepending zero for later use to avoid + # the need for indexing. + S_i_m1 = np.concatenate(([0], S[:-1])) + + # Psi, as defined by the 6th unnumbered equation on page 313 (Mielke). + # Note that in the paper there is an error where the denominator `2` is + # squared when it should be the entire equation. + def psi(indicator): + return (indicator - (N + 1)/2)**2 + + # define summation range for use in calculation of phi, as seen in sum + # in the unnumbered equation on the bottom of page 312 (Mielke). + s_lower = S[js - 1] + 1 + s_upper = S[js] + 1 + phi_J = [np.arange(s_lower[idx], s_upper[idx]) for idx in range(k)] + + # for every range in the above array, determine the sum of psi(I) for + # every element in the range. Divide all the sums by `t`. Following the + # last unnumbered equation on page 312. + phis = [np.sum(psi(I_j)) for I_j in phi_J] / t[js] + + # `T` is equal to a[j] * phi[j], per the first unnumbered equation on + # page 312. `phis` is already in the order based on `js`, so we index + # into `a` with `js` as well. + T = sum(phis * a[js]) + + # The approximate statistic + E_0_T = n * (N * N - 1) / 12 + + varM = (m * n * (N + 1.0) * (N ** 2 - 4) / 180 - + m * n / (180 * N * (N - 1)) * np.sum( + t * (t**2 - 1) * (t**2 - 4 + (15 * (N - S - S_i_m1) ** 2)) + )) + + return ((T - E_0_T) / np.sqrt(varM),) + + +def _mood_too_small(samples, kwargs, axis=-1): + x, y = samples + n = x.shape[axis] + m = y.shape[axis] + N = m + n + return N < 3 + + +@_axis_nan_policy_factory(SignificanceResult, n_samples=2, too_small=_mood_too_small) +def mood(x, y, axis=0, alternative="two-sided"): + """Perform Mood's test for equal scale parameters. + + Mood's two-sample test for scale parameters is a non-parametric + test for the null hypothesis that two samples are drawn from the + same distribution with the same scale parameter. + + Parameters + ---------- + x, y : array_like + Arrays of sample data. + axis : int, optional + The axis along which the samples are tested. `x` and `y` can be of + different length along `axis`. + If `axis` is None, `x` and `y` are flattened and the test is done on + all values in the flattened arrays. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the scales of the distributions underlying `x` and `y` + are different. + * 'less': the scale of the distribution underlying `x` is less than + the scale of the distribution underlying `y`. + * 'greater': the scale of the distribution underlying `x` is greater + than the scale of the distribution underlying `y`. + + .. versionadded:: 1.7.0 + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : scalar or ndarray + The z-score for the hypothesis test. For 1-D inputs a scalar is + returned. + pvalue : scalar ndarray + The p-value for the hypothesis test. + + See Also + -------- + fligner : A non-parametric test for the equality of k variances + ansari : A non-parametric test for the equality of 2 variances + bartlett : A parametric test for equality of k variances in normal samples + levene : A parametric test for equality of k variances + + Notes + ----- + The data are assumed to be drawn from probability distributions ``f(x)`` + and ``f(x/s) / s`` respectively, for some probability density function f. + The null hypothesis is that ``s == 1``. + + For multi-dimensional arrays, if the inputs are of shapes + ``(n0, n1, n2, n3)`` and ``(n0, m1, n2, n3)``, then if ``axis=1``, the + resulting z and p values will have shape ``(n0, n2, n3)``. Note that + ``n1`` and ``m1`` don't have to be equal, but the other dimensions do. + + References + ---------- + [1] Mielke, Paul W. "Note on Some Squared Rank Tests with Existing Ties." + Technometrics, vol. 9, no. 2, 1967, pp. 312-14. JSTOR, + https://doi.org/10.2307/1266427. Accessed 18 May 2022. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> x2 = rng.standard_normal((2, 45, 6, 7)) + >>> x1 = rng.standard_normal((2, 30, 6, 7)) + >>> res = stats.mood(x1, x2, axis=1) + >>> res.pvalue.shape + (2, 6, 7) + + Find the number of points where the difference in scale is not significant: + + >>> (res.pvalue > 0.1).sum() + 78 + + Perform the test with different scales: + + >>> x1 = rng.standard_normal((2, 30)) + >>> x2 = rng.standard_normal((2, 35)) * 10.0 + >>> stats.mood(x1, x2, axis=1) + SignificanceResult(statistic=array([-5.76174136, -6.12650783]), + pvalue=array([8.32505043e-09, 8.98287869e-10])) + + """ + x = np.asarray(x, dtype=float) + y = np.asarray(y, dtype=float) + + if axis < 0: + axis = x.ndim + axis + + # Determine shape of the result arrays + res_shape = tuple([x.shape[ax] for ax in range(len(x.shape)) if ax != axis]) + if not (res_shape == tuple([y.shape[ax] for ax in range(len(y.shape)) if + ax != axis])): + raise ValueError("Dimensions of x and y on all axes except `axis` " + "should match") + + n = x.shape[axis] + m = y.shape[axis] + N = m + n + if N < 3: + raise ValueError("Not enough observations.") + + xy = np.concatenate((x, y), axis=axis) + # determine if any of the samples contain ties + sorted_xy = np.sort(xy, axis=axis) + diffs = np.diff(sorted_xy, axis=axis) + if 0 in diffs: + z = np.asarray(_mood_inner_lc(xy, x, diffs, sorted_xy, n, m, N, + axis=axis)) + else: + if axis != 0: + xy = np.moveaxis(xy, axis, 0) + + xy = xy.reshape(xy.shape[0], -1) + # Generalized to the n-dimensional case by adding the axis argument, + # and using for loops, since rankdata is not vectorized. For improving + # performance consider vectorizing rankdata function. + all_ranks = np.empty_like(xy) + for j in range(xy.shape[1]): + all_ranks[:, j] = _stats_py.rankdata(xy[:, j]) + + Ri = all_ranks[:n] + M = np.sum((Ri - (N + 1.0) / 2) ** 2, axis=0) + # Approx stat. + mnM = n * (N * N - 1.0) / 12 + varM = m * n * (N + 1.0) * (N + 2) * (N - 2) / 180 + z = (M - mnM) / sqrt(varM) + pval = _get_pvalue(z, distributions.norm, alternative) + + if res_shape == (): + # Return scalars, not 0-D arrays + z = z[0] + pval = pval[0] + else: + z.shape = res_shape + pval.shape = res_shape + return SignificanceResult(z[()], pval[()]) + + +WilcoxonResult = _make_tuple_bunch('WilcoxonResult', ['statistic', 'pvalue']) + + +def wilcoxon_result_unpacker(res): + if hasattr(res, 'zstatistic'): + return res.statistic, res.pvalue, res.zstatistic + else: + return res.statistic, res.pvalue + + +def wilcoxon_result_object(statistic, pvalue, zstatistic=None): + res = WilcoxonResult(statistic, pvalue) + if zstatistic is not None: + res.zstatistic = zstatistic + return res + + +def wilcoxon_outputs(kwds): + method = kwds.get('method', 'auto') + if method == 'approx': + return 3 + return 2 + + +@_rename_parameter("mode", "method") +@_axis_nan_policy_factory( + wilcoxon_result_object, paired=True, + n_samples=lambda kwds: 2 if kwds.get('y', None) is not None else 1, + result_to_tuple=wilcoxon_result_unpacker, n_outputs=wilcoxon_outputs, +) +def wilcoxon(x, y=None, zero_method="wilcox", correction=False, + alternative="two-sided", method='auto', *, axis=0): + """Calculate the Wilcoxon signed-rank test. + + The Wilcoxon signed-rank test tests the null hypothesis that two + related paired samples come from the same distribution. In particular, + it tests whether the distribution of the differences ``x - y`` is symmetric + about zero. It is a non-parametric version of the paired T-test. + + Parameters + ---------- + x : array_like + Either the first set of measurements (in which case ``y`` is the second + set of measurements), or the differences between two sets of + measurements (in which case ``y`` is not to be specified.) Must be + one-dimensional. + y : array_like, optional + Either the second set of measurements (if ``x`` is the first set of + measurements), or not specified (if ``x`` is the differences between + two sets of measurements.) Must be one-dimensional. + + .. warning:: + When `y` is provided, `wilcoxon` calculates the test statistic + based on the ranks of the absolute values of ``d = x - y``. + Roundoff error in the subtraction can result in elements of ``d`` + being assigned different ranks even when they would be tied with + exact arithmetic. Rather than passing `x` and `y` separately, + consider computing the difference ``x - y``, rounding as needed to + ensure that only truly unique elements are numerically distinct, + and passing the result as `x`, leaving `y` at the default (None). + + zero_method : {"wilcox", "pratt", "zsplit"}, optional + There are different conventions for handling pairs of observations + with equal values ("zero-differences", or "zeros"). + + * "wilcox": Discards all zero-differences (default); see [4]_. + * "pratt": Includes zero-differences in the ranking process, + but drops the ranks of the zeros (more conservative); see [3]_. + In this case, the normal approximation is adjusted as in [5]_. + * "zsplit": Includes zero-differences in the ranking process and + splits the zero rank between positive and negative ones. + + correction : bool, optional + If True, apply continuity correction by adjusting the Wilcoxon rank + statistic by 0.5 towards the mean value when computing the + z-statistic if a normal approximation is used. Default is False. + alternative : {"two-sided", "greater", "less"}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + In the following, let ``d`` represent the difference between the paired + samples: ``d = x - y`` if both ``x`` and ``y`` are provided, or + ``d = x`` otherwise. + + * 'two-sided': the distribution underlying ``d`` is not symmetric + about zero. + * 'less': the distribution underlying ``d`` is stochastically less + than a distribution symmetric about zero. + * 'greater': the distribution underlying ``d`` is stochastically + greater than a distribution symmetric about zero. + + method : {"auto", "exact", "approx"} or `PermutationMethod` instance, optional + Method to calculate the p-value, see Notes. Default is "auto". + + axis : int or None, default: 0 + If an int, the axis of the input along which to compute the statistic. + The statistic of each axis-slice (e.g. row) of the input will appear + in a corresponding element of the output. If ``None``, the input will + be raveled before computing the statistic. + + Returns + ------- + An object with the following attributes. + + statistic : array_like + If `alternative` is "two-sided", the sum of the ranks of the + differences above or below zero, whichever is smaller. + Otherwise the sum of the ranks of the differences above zero. + pvalue : array_like + The p-value for the test depending on `alternative` and `method`. + zstatistic : array_like + When ``method = 'approx'``, this is the normalized z-statistic:: + + z = (T - mn - d) / se + + where ``T`` is `statistic` as defined above, ``mn`` is the mean of the + distribution under the null hypothesis, ``d`` is a continuity + correction, and ``se`` is the standard error. + When ``method != 'approx'``, this attribute is not available. + + See Also + -------- + kruskal, mannwhitneyu + + Notes + ----- + In the following, let ``d`` represent the difference between the paired + samples: ``d = x - y`` if both ``x`` and ``y`` are provided, or ``d = x`` + otherwise. Assume that all elements of ``d`` are independent and + identically distributed observations, and all are distinct and nonzero. + + - When ``len(d)`` is sufficiently large, the null distribution of the + normalized test statistic (`zstatistic` above) is approximately normal, + and ``method = 'approx'`` can be used to compute the p-value. + + - When ``len(d)`` is small, the normal approximation may not be accurate, + and ``method='exact'`` is preferred (at the cost of additional + execution time). + + - The default, ``method='auto'``, selects between the two: when + ``len(d) <= 50`` and there are no zeros, the exact method is used; + otherwise, the approximate method is used. + + The presence of "ties" (i.e. not all elements of ``d`` are unique) or + "zeros" (i.e. elements of ``d`` are zero) changes the null distribution + of the test statistic, and ``method='exact'`` no longer calculates + the exact p-value. If ``method='approx'``, the z-statistic is adjusted + for more accurate comparison against the standard normal, but still, + for finite sample sizes, the standard normal is only an approximation of + the true null distribution of the z-statistic. For such situations, the + `method` parameter also accepts instances `PermutationMethod`. In this + case, the p-value is computed using `permutation_test` with the provided + configuration options and other appropriate settings. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test + .. [2] Conover, W.J., Practical Nonparametric Statistics, 1971. + .. [3] Pratt, J.W., Remarks on Zeros and Ties in the Wilcoxon Signed + Rank Procedures, Journal of the American Statistical Association, + Vol. 54, 1959, pp. 655-667. :doi:`10.1080/01621459.1959.10501526` + .. [4] Wilcoxon, F., Individual Comparisons by Ranking Methods, + Biometrics Bulletin, Vol. 1, 1945, pp. 80-83. :doi:`10.2307/3001968` + .. [5] Cureton, E.E., The Normal Approximation to the Signed-Rank + Sampling Distribution When Zero Differences are Present, + Journal of the American Statistical Association, Vol. 62, 1967, + pp. 1068-1069. :doi:`10.1080/01621459.1967.10500917` + + Examples + -------- + In [4]_, the differences in height between cross- and self-fertilized + corn plants is given as follows: + + >>> d = [6, 8, 14, 16, 23, 24, 28, 29, 41, -48, 49, 56, 60, -67, 75] + + Cross-fertilized plants appear to be higher. To test the null + hypothesis that there is no height difference, we can apply the + two-sided test: + + >>> from scipy.stats import wilcoxon + >>> res = wilcoxon(d) + >>> res.statistic, res.pvalue + (24.0, 0.041259765625) + + Hence, we would reject the null hypothesis at a confidence level of 5%, + concluding that there is a difference in height between the groups. + To confirm that the median of the differences can be assumed to be + positive, we use: + + >>> res = wilcoxon(d, alternative='greater') + >>> res.statistic, res.pvalue + (96.0, 0.0206298828125) + + This shows that the null hypothesis that the median is negative can be + rejected at a confidence level of 5% in favor of the alternative that + the median is greater than zero. The p-values above are exact. Using the + normal approximation gives very similar values: + + >>> res = wilcoxon(d, method='approx') + >>> res.statistic, res.pvalue + (24.0, 0.04088813291185591) + + Note that the statistic changed to 96 in the one-sided case (the sum + of ranks of positive differences) whereas it is 24 in the two-sided + case (the minimum of sum of ranks above and below zero). + + In the example above, the differences in height between paired plants are + provided to `wilcoxon` directly. Alternatively, `wilcoxon` accepts two + samples of equal length, calculates the differences between paired + elements, then performs the test. Consider the samples ``x`` and ``y``: + + >>> import numpy as np + >>> x = np.array([0.5, 0.825, 0.375, 0.5]) + >>> y = np.array([0.525, 0.775, 0.325, 0.55]) + >>> res = wilcoxon(x, y, alternative='greater') + >>> res + WilcoxonResult(statistic=5.0, pvalue=0.5625) + + Note that had we calculated the differences by hand, the test would have + produced different results: + + >>> d = [-0.025, 0.05, 0.05, -0.05] + >>> ref = wilcoxon(d, alternative='greater') + >>> ref + WilcoxonResult(statistic=6.0, pvalue=0.4375) + + The substantial difference is due to roundoff error in the results of + ``x-y``: + + >>> d - (x-y) + array([2.08166817e-17, 6.93889390e-17, 1.38777878e-17, 4.16333634e-17]) + + Even though we expected all the elements of ``(x-y)[1:]`` to have the same + magnitude ``0.05``, they have slightly different magnitudes in practice, + and therefore are assigned different ranks in the test. Before performing + the test, consider calculating ``d`` and adjusting it as necessary to + ensure that theoretically identically values are not numerically distinct. + For example: + + >>> d2 = np.around(x - y, decimals=3) + >>> wilcoxon(d2, alternative='greater') + WilcoxonResult(statistic=6.0, pvalue=0.4375) + + """ + return _wilcoxon._wilcoxon_nd(x, y, zero_method, correction, alternative, + method, axis) + + +MedianTestResult = _make_tuple_bunch( + 'MedianTestResult', + ['statistic', 'pvalue', 'median', 'table'], [] +) + + +def median_test(*samples, ties='below', correction=True, lambda_=1, + nan_policy='propagate'): + """Perform a Mood's median test. + + Test that two or more samples come from populations with the same median. + + Let ``n = len(samples)`` be the number of samples. The "grand median" of + all the data is computed, and a contingency table is formed by + classifying the values in each sample as being above or below the grand + median. The contingency table, along with `correction` and `lambda_`, + are passed to `scipy.stats.chi2_contingency` to compute the test statistic + and p-value. + + Parameters + ---------- + sample1, sample2, ... : array_like + The set of samples. There must be at least two samples. + Each sample must be a one-dimensional sequence containing at least + one value. The samples are not required to have the same length. + ties : str, optional + Determines how values equal to the grand median are classified in + the contingency table. The string must be one of:: + + "below": + Values equal to the grand median are counted as "below". + "above": + Values equal to the grand median are counted as "above". + "ignore": + Values equal to the grand median are not counted. + + The default is "below". + correction : bool, optional + If True, *and* there are just two samples, apply Yates' correction + for continuity when computing the test statistic associated with + the contingency table. Default is True. + lambda_ : float or str, optional + By default, the statistic computed in this test is Pearson's + chi-squared statistic. `lambda_` allows a statistic from the + Cressie-Read power divergence family to be used instead. See + `power_divergence` for details. + Default is 1 (Pearson's chi-squared statistic). + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. 'propagate' returns nan, + 'raise' throws an error, 'omit' performs the calculations ignoring nan + values. Default is 'propagate'. + + Returns + ------- + res : MedianTestResult + An object containing attributes: + + statistic : float + The test statistic. The statistic that is returned is determined + by `lambda_`. The default is Pearson's chi-squared statistic. + pvalue : float + The p-value of the test. + median : float + The grand median. + table : ndarray + The contingency table. The shape of the table is (2, n), where + n is the number of samples. The first row holds the counts of the + values above the grand median, and the second row holds the counts + of the values below the grand median. The table allows further + analysis with, for example, `scipy.stats.chi2_contingency`, or with + `scipy.stats.fisher_exact` if there are two samples, without having + to recompute the table. If ``nan_policy`` is "propagate" and there + are nans in the input, the return value for ``table`` is ``None``. + + See Also + -------- + kruskal : Compute the Kruskal-Wallis H-test for independent samples. + mannwhitneyu : Computes the Mann-Whitney rank test on samples x and y. + + Notes + ----- + .. versionadded:: 0.15.0 + + References + ---------- + .. [1] Mood, A. M., Introduction to the Theory of Statistics. McGraw-Hill + (1950), pp. 394-399. + .. [2] Zar, J. H., Biostatistical Analysis, 5th ed. Prentice Hall (2010). + See Sections 8.12 and 10.15. + + Examples + -------- + A biologist runs an experiment in which there are three groups of plants. + Group 1 has 16 plants, group 2 has 15 plants, and group 3 has 17 plants. + Each plant produces a number of seeds. The seed counts for each group + are:: + + Group 1: 10 14 14 18 20 22 24 25 31 31 32 39 43 43 48 49 + Group 2: 28 30 31 33 34 35 36 40 44 55 57 61 91 92 99 + Group 3: 0 3 9 22 23 25 25 33 34 34 40 45 46 48 62 67 84 + + The following code applies Mood's median test to these samples. + + >>> g1 = [10, 14, 14, 18, 20, 22, 24, 25, 31, 31, 32, 39, 43, 43, 48, 49] + >>> g2 = [28, 30, 31, 33, 34, 35, 36, 40, 44, 55, 57, 61, 91, 92, 99] + >>> g3 = [0, 3, 9, 22, 23, 25, 25, 33, 34, 34, 40, 45, 46, 48, 62, 67, 84] + >>> from scipy.stats import median_test + >>> res = median_test(g1, g2, g3) + + The median is + + >>> res.median + 34.0 + + and the contingency table is + + >>> res.table + array([[ 5, 10, 7], + [11, 5, 10]]) + + `p` is too large to conclude that the medians are not the same: + + >>> res.pvalue + 0.12609082774093244 + + The "G-test" can be performed by passing ``lambda_="log-likelihood"`` to + `median_test`. + + >>> res = median_test(g1, g2, g3, lambda_="log-likelihood") + >>> res.pvalue + 0.12224779737117837 + + The median occurs several times in the data, so we'll get a different + result if, for example, ``ties="above"`` is used: + + >>> res = median_test(g1, g2, g3, ties="above") + >>> res.pvalue + 0.063873276069553273 + + >>> res.table + array([[ 5, 11, 9], + [11, 4, 8]]) + + This example demonstrates that if the data set is not large and there + are values equal to the median, the p-value can be sensitive to the + choice of `ties`. + + """ + if len(samples) < 2: + raise ValueError('median_test requires two or more samples.') + + ties_options = ['below', 'above', 'ignore'] + if ties not in ties_options: + raise ValueError(f"invalid 'ties' option '{ties}'; 'ties' must be one " + f"of: {str(ties_options)[1:-1]}") + + data = [np.asarray(sample) for sample in samples] + + # Validate the sizes and shapes of the arguments. + for k, d in enumerate(data): + if d.size == 0: + raise ValueError("Sample %d is empty. All samples must " + "contain at least one value." % (k + 1)) + if d.ndim != 1: + raise ValueError("Sample %d has %d dimensions. All " + "samples must be one-dimensional sequences." % + (k + 1, d.ndim)) + + cdata = np.concatenate(data) + contains_nan, nan_policy = _contains_nan(cdata, nan_policy) + if contains_nan and nan_policy == 'propagate': + return MedianTestResult(np.nan, np.nan, np.nan, None) + + if contains_nan: + grand_median = np.median(cdata[~np.isnan(cdata)]) + else: + grand_median = np.median(cdata) + # When the minimum version of numpy supported by scipy is 1.9.0, + # the above if/else statement can be replaced by the single line: + # grand_median = np.nanmedian(cdata) + + # Create the contingency table. + table = np.zeros((2, len(data)), dtype=np.int64) + for k, sample in enumerate(data): + sample = sample[~np.isnan(sample)] + + nabove = count_nonzero(sample > grand_median) + nbelow = count_nonzero(sample < grand_median) + nequal = sample.size - (nabove + nbelow) + table[0, k] += nabove + table[1, k] += nbelow + if ties == "below": + table[1, k] += nequal + elif ties == "above": + table[0, k] += nequal + + # Check that no row or column of the table is all zero. + # Such a table can not be given to chi2_contingency, because it would have + # a zero in the table of expected frequencies. + rowsums = table.sum(axis=1) + if rowsums[0] == 0: + raise ValueError("All values are below the grand median (%r)." % + grand_median) + if rowsums[1] == 0: + raise ValueError("All values are above the grand median (%r)." % + grand_median) + if ties == "ignore": + # We already checked that each sample has at least one value, but it + # is possible that all those values equal the grand median. If `ties` + # is "ignore", that would result in a column of zeros in `table`. We + # check for that case here. + zero_cols = np.nonzero((table == 0).all(axis=0))[0] + if len(zero_cols) > 0: + msg = ("All values in sample %d are equal to the grand " + "median (%r), so they are ignored, resulting in an " + "empty sample." % (zero_cols[0] + 1, grand_median)) + raise ValueError(msg) + + stat, p, dof, expected = chi2_contingency(table, lambda_=lambda_, + correction=correction) + return MedianTestResult(stat, p, grand_median, table) + + +def _circfuncs_common(samples, high, low): + # Ensure samples are array-like and size is not zero + if samples.size == 0: + NaN = _get_nan(samples) + return NaN, NaN, NaN + + # Recast samples as radians that range between 0 and 2 pi and calculate + # the sine and cosine + sin_samp = sin((samples - low)*2.*pi / (high - low)) + cos_samp = cos((samples - low)*2.*pi / (high - low)) + + return samples, sin_samp, cos_samp + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, default_axis=None, + result_to_tuple=lambda x: (x,) +) +def circmean(samples, high=2*pi, low=0, axis=None, nan_policy='propagate'): + """Compute the circular mean for samples in a range. + + Parameters + ---------- + samples : array_like + Input array. + high : float or int, optional + High boundary for the sample range. Default is ``2*pi``. + low : float or int, optional + Low boundary for the sample range. Default is 0. + + Returns + ------- + circmean : float + Circular mean. + + See Also + -------- + circstd : Circular standard deviation. + circvar : Circular variance. + + Examples + -------- + For simplicity, all angles are printed out in degrees. + + >>> import numpy as np + >>> from scipy.stats import circmean + >>> import matplotlib.pyplot as plt + >>> angles = np.deg2rad(np.array([20, 30, 330])) + >>> circmean = circmean(angles) + >>> np.rad2deg(circmean) + 7.294976657784009 + + >>> mean = angles.mean() + >>> np.rad2deg(mean) + 126.66666666666666 + + Plot and compare the circular mean against the arithmetic mean. + + >>> plt.plot(np.cos(np.linspace(0, 2*np.pi, 500)), + ... np.sin(np.linspace(0, 2*np.pi, 500)), + ... c='k') + >>> plt.scatter(np.cos(angles), np.sin(angles), c='k') + >>> plt.scatter(np.cos(circmean), np.sin(circmean), c='b', + ... label='circmean') + >>> plt.scatter(np.cos(mean), np.sin(mean), c='r', label='mean') + >>> plt.legend() + >>> plt.axis('equal') + >>> plt.show() + + """ + samples, sin_samp, cos_samp = _circfuncs_common(samples, high, low) + sin_sum = sin_samp.sum(axis) + cos_sum = cos_samp.sum(axis) + res = arctan2(sin_sum, cos_sum) + + res = np.asarray(res) + res[res < 0] += 2*pi + res = res[()] + + return res*(high - low)/2.0/pi + low + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, default_axis=None, + result_to_tuple=lambda x: (x,) +) +def circvar(samples, high=2*pi, low=0, axis=None, nan_policy='propagate'): + """Compute the circular variance for samples assumed to be in a range. + + Parameters + ---------- + samples : array_like + Input array. + high : float or int, optional + High boundary for the sample range. Default is ``2*pi``. + low : float or int, optional + Low boundary for the sample range. Default is 0. + + Returns + ------- + circvar : float + Circular variance. + + See Also + -------- + circmean : Circular mean. + circstd : Circular standard deviation. + + Notes + ----- + This uses the following definition of circular variance: ``1-R``, where + ``R`` is the mean resultant vector. The + returned value is in the range [0, 1], 0 standing for no variance, and 1 + for a large variance. In the limit of small angles, this value is similar + to half the 'linear' variance. + + References + ---------- + .. [1] Fisher, N.I. *Statistical analysis of circular data*. Cambridge + University Press, 1993. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import circvar + >>> import matplotlib.pyplot as plt + >>> samples_1 = np.array([0.072, -0.158, 0.077, 0.108, 0.286, + ... 0.133, -0.473, -0.001, -0.348, 0.131]) + >>> samples_2 = np.array([0.111, -0.879, 0.078, 0.733, 0.421, + ... 0.104, -0.136, -0.867, 0.012, 0.105]) + >>> circvar_1 = circvar(samples_1) + >>> circvar_2 = circvar(samples_2) + + Plot the samples. + + >>> fig, (left, right) = plt.subplots(ncols=2) + >>> for image in (left, right): + ... image.plot(np.cos(np.linspace(0, 2*np.pi, 500)), + ... np.sin(np.linspace(0, 2*np.pi, 500)), + ... c='k') + ... image.axis('equal') + ... image.axis('off') + >>> left.scatter(np.cos(samples_1), np.sin(samples_1), c='k', s=15) + >>> left.set_title(f"circular variance: {np.round(circvar_1, 2)!r}") + >>> right.scatter(np.cos(samples_2), np.sin(samples_2), c='k', s=15) + >>> right.set_title(f"circular variance: {np.round(circvar_2, 2)!r}") + >>> plt.show() + + """ + samples, sin_samp, cos_samp = _circfuncs_common(samples, high, low) + sin_mean = sin_samp.mean(axis) + cos_mean = cos_samp.mean(axis) + # hypot can go slightly above 1 due to rounding errors + with np.errstate(invalid='ignore'): + R = np.minimum(1, hypot(sin_mean, cos_mean)) + + res = 1. - R + return res + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, default_axis=None, + result_to_tuple=lambda x: (x,) +) +def circstd(samples, high=2*pi, low=0, axis=None, nan_policy='propagate', *, + normalize=False): + """ + Compute the circular standard deviation for samples assumed to be in the + range [low to high]. + + Parameters + ---------- + samples : array_like + Input array. + high : float or int, optional + High boundary for the sample range. Default is ``2*pi``. + low : float or int, optional + Low boundary for the sample range. Default is 0. + normalize : boolean, optional + If True, the returned value is equal to ``sqrt(-2*log(R))`` and does + not depend on the variable units. If False (default), the returned + value is scaled by ``((high-low)/(2*pi))``. + + Returns + ------- + circstd : float + Circular standard deviation. + + See Also + -------- + circmean : Circular mean. + circvar : Circular variance. + + Notes + ----- + This uses a definition of circular standard deviation from [1]_. + Essentially, the calculation is as follows. + + .. code-block:: python + + import numpy as np + C = np.cos(samples).mean() + S = np.sin(samples).mean() + R = np.sqrt(C**2 + S**2) + l = 2*np.pi / (high-low) + circstd = np.sqrt(-2*np.log(R)) / l + + In the limit of small angles, it returns a number close to the 'linear' + standard deviation. + + References + ---------- + .. [1] Mardia, K. V. (1972). 2. In *Statistics of Directional Data* + (pp. 18-24). Academic Press. :doi:`10.1016/C2013-0-07425-7`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import circstd + >>> import matplotlib.pyplot as plt + >>> samples_1 = np.array([0.072, -0.158, 0.077, 0.108, 0.286, + ... 0.133, -0.473, -0.001, -0.348, 0.131]) + >>> samples_2 = np.array([0.111, -0.879, 0.078, 0.733, 0.421, + ... 0.104, -0.136, -0.867, 0.012, 0.105]) + >>> circstd_1 = circstd(samples_1) + >>> circstd_2 = circstd(samples_2) + + Plot the samples. + + >>> fig, (left, right) = plt.subplots(ncols=2) + >>> for image in (left, right): + ... image.plot(np.cos(np.linspace(0, 2*np.pi, 500)), + ... np.sin(np.linspace(0, 2*np.pi, 500)), + ... c='k') + ... image.axis('equal') + ... image.axis('off') + >>> left.scatter(np.cos(samples_1), np.sin(samples_1), c='k', s=15) + >>> left.set_title(f"circular std: {np.round(circstd_1, 2)!r}") + >>> right.plot(np.cos(np.linspace(0, 2*np.pi, 500)), + ... np.sin(np.linspace(0, 2*np.pi, 500)), + ... c='k') + >>> right.scatter(np.cos(samples_2), np.sin(samples_2), c='k', s=15) + >>> right.set_title(f"circular std: {np.round(circstd_2, 2)!r}") + >>> plt.show() + + """ + samples, sin_samp, cos_samp = _circfuncs_common(samples, high, low) + sin_mean = sin_samp.mean(axis) # [1] (2.2.3) + cos_mean = cos_samp.mean(axis) # [1] (2.2.3) + # hypot can go slightly above 1 due to rounding errors + with np.errstate(invalid='ignore'): + R = np.minimum(1, hypot(sin_mean, cos_mean)) # [1] (2.2.4) + + res = sqrt(-2*log(R)) + if not normalize: + res *= (high-low)/(2.*pi) # [1] (2.3.14) w/ (2.3.7) + return res + + +class DirectionalStats: + def __init__(self, mean_direction, mean_resultant_length): + self.mean_direction = mean_direction + self.mean_resultant_length = mean_resultant_length + + def __repr__(self): + return (f"DirectionalStats(mean_direction={self.mean_direction}," + f" mean_resultant_length={self.mean_resultant_length})") + + +def directional_stats(samples, *, axis=0, normalize=True): + """ + Computes sample statistics for directional data. + + Computes the directional mean (also called the mean direction vector) and + mean resultant length of a sample of vectors. + + The directional mean is a measure of "preferred direction" of vector data. + It is analogous to the sample mean, but it is for use when the length of + the data is irrelevant (e.g. unit vectors). + + The mean resultant length is a value between 0 and 1 used to quantify the + dispersion of directional data: the smaller the mean resultant length, the + greater the dispersion. Several definitions of directional variance + involving the mean resultant length are given in [1]_ and [2]_. + + Parameters + ---------- + samples : array_like + Input array. Must be at least two-dimensional, and the last axis of the + input must correspond with the dimensionality of the vector space. + When the input is exactly two dimensional, this means that each row + of the data is a vector observation. + axis : int, default: 0 + Axis along which the directional mean is computed. + normalize: boolean, default: True + If True, normalize the input to ensure that each observation is a + unit vector. It the observations are already unit vectors, consider + setting this to False to avoid unnecessary computation. + + Returns + ------- + res : DirectionalStats + An object containing attributes: + + mean_direction : ndarray + Directional mean. + mean_resultant_length : ndarray + The mean resultant length [1]_. + + See Also + -------- + circmean: circular mean; i.e. directional mean for 2D *angles* + circvar: circular variance; i.e. directional variance for 2D *angles* + + Notes + ----- + This uses a definition of directional mean from [1]_. + Assuming the observations are unit vectors, the calculation is as follows. + + .. code-block:: python + + mean = samples.mean(axis=0) + mean_resultant_length = np.linalg.norm(mean) + mean_direction = mean / mean_resultant_length + + This definition is appropriate for *directional* data (i.e. vector data + for which the magnitude of each observation is irrelevant) but not + for *axial* data (i.e. vector data for which the magnitude and *sign* of + each observation is irrelevant). + + Several definitions of directional variance involving the mean resultant + length ``R`` have been proposed, including ``1 - R`` [1]_, ``1 - R**2`` + [2]_, and ``2 * (1 - R)`` [2]_. Rather than choosing one, this function + returns ``R`` as attribute `mean_resultant_length` so the user can compute + their preferred measure of dispersion. + + References + ---------- + .. [1] Mardia, Jupp. (2000). *Directional Statistics* + (p. 163). Wiley. + + .. [2] https://en.wikipedia.org/wiki/Directional_statistics + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import directional_stats + >>> data = np.array([[3, 4], # first observation, 2D vector space + ... [6, -8]]) # second observation + >>> dirstats = directional_stats(data) + >>> dirstats.mean_direction + array([1., 0.]) + + In contrast, the regular sample mean of the vectors would be influenced + by the magnitude of each observation. Furthermore, the result would not be + a unit vector. + + >>> data.mean(axis=0) + array([4.5, -2.]) + + An exemplary use case for `directional_stats` is to find a *meaningful* + center for a set of observations on a sphere, e.g. geographical locations. + + >>> data = np.array([[0.8660254, 0.5, 0.], + ... [0.8660254, -0.5, 0.]]) + >>> dirstats = directional_stats(data) + >>> dirstats.mean_direction + array([1., 0., 0.]) + + The regular sample mean on the other hand yields a result which does not + lie on the surface of the sphere. + + >>> data.mean(axis=0) + array([0.8660254, 0., 0.]) + + The function also returns the mean resultant length, which + can be used to calculate a directional variance. For example, using the + definition ``Var(z) = 1 - R`` from [2]_ where ``R`` is the + mean resultant length, we can calculate the directional variance of the + vectors in the above example as: + + >>> 1 - dirstats.mean_resultant_length + 0.13397459716167093 + """ + samples = np.asarray(samples) + if samples.ndim < 2: + raise ValueError("samples must at least be two-dimensional. " + f"Instead samples has shape: {samples.shape!r}") + samples = np.moveaxis(samples, axis, 0) + if normalize: + vectornorms = np.linalg.norm(samples, axis=-1, keepdims=True) + samples = samples/vectornorms + mean = np.mean(samples, axis=0) + mean_resultant_length = np.linalg.norm(mean, axis=-1, keepdims=True) + mean_direction = mean / mean_resultant_length + return DirectionalStats(mean_direction, + mean_resultant_length.squeeze(-1)[()]) + + +def false_discovery_control(ps, *, axis=0, method='bh'): + """Adjust p-values to control the false discovery rate. + + The false discovery rate (FDR) is the expected proportion of rejected null + hypotheses that are actually true. + If the null hypothesis is rejected when the *adjusted* p-value falls below + a specified level, the false discovery rate is controlled at that level. + + Parameters + ---------- + ps : 1D array_like + The p-values to adjust. Elements must be real numbers between 0 and 1. + axis : int + The axis along which to perform the adjustment. The adjustment is + performed independently along each axis-slice. If `axis` is None, `ps` + is raveled before performing the adjustment. + method : {'bh', 'by'} + The false discovery rate control procedure to apply: ``'bh'`` is for + Benjamini-Hochberg [1]_ (Eq. 1), ``'by'`` is for Benjaminini-Yekutieli + [2]_ (Theorem 1.3). The latter is more conservative, but it is + guaranteed to control the FDR even when the p-values are not from + independent tests. + + Returns + ------- + ps_adusted : array_like + The adjusted p-values. If the null hypothesis is rejected where these + fall below a specified level, the false discovery rate is controlled + at that level. + + See Also + -------- + combine_pvalues + statsmodels.stats.multitest.multipletests + + Notes + ----- + In multiple hypothesis testing, false discovery control procedures tend to + offer higher power than familywise error rate control procedures (e.g. + Bonferroni correction [1]_). + + If the p-values correspond with independent tests (or tests with + "positive regression dependencies" [2]_), rejecting null hypotheses + corresponding with Benjamini-Hochberg-adjusted p-values below :math:`q` + controls the false discovery rate at a level less than or equal to + :math:`q m_0 / m`, where :math:`m_0` is the number of true null hypotheses + and :math:`m` is the total number of null hypotheses tested. The same is + true even for dependent tests when the p-values are adjusted accorded to + the more conservative Benjaminini-Yekutieli procedure. + + The adjusted p-values produced by this function are comparable to those + produced by the R function ``p.adjust`` and the statsmodels function + `statsmodels.stats.multitest.multipletests`. Please consider the latter + for more advanced methods of multiple comparison correction. + + References + ---------- + .. [1] Benjamini, Yoav, and Yosef Hochberg. "Controlling the false + discovery rate: a practical and powerful approach to multiple + testing." Journal of the Royal statistical society: series B + (Methodological) 57.1 (1995): 289-300. + + .. [2] Benjamini, Yoav, and Daniel Yekutieli. "The control of the false + discovery rate in multiple testing under dependency." Annals of + statistics (2001): 1165-1188. + + .. [3] TileStats. FDR - Benjamini-Hochberg explained - Youtube. + https://www.youtube.com/watch?v=rZKa4tW2NKs. + + .. [4] Neuhaus, Karl-Ludwig, et al. "Improved thrombolysis in acute + myocardial infarction with front-loaded administration of alteplase: + results of the rt-PA-APSAC patency study (TAPS)." Journal of the + American College of Cardiology 19.5 (1992): 885-891. + + Examples + -------- + We follow the example from [1]_. + + Thrombolysis with recombinant tissue-type plasminogen activator (rt-PA) + and anisoylated plasminogen streptokinase activator (APSAC) in + myocardial infarction has been proved to reduce mortality. [4]_ + investigated the effects of a new front-loaded administration of rt-PA + versus those obtained with a standard regimen of APSAC, in a randomized + multicentre trial in 421 patients with acute myocardial infarction. + + There were four families of hypotheses tested in the study, the last of + which was "cardiac and other events after the start of thrombolitic + treatment". FDR control may be desired in this family of hypotheses + because it would not be appropriate to conclude that the front-loaded + treatment is better if it is merely equivalent to the previous treatment. + + The p-values corresponding with the 15 hypotheses in this family were + + >>> ps = [0.0001, 0.0004, 0.0019, 0.0095, 0.0201, 0.0278, 0.0298, 0.0344, + ... 0.0459, 0.3240, 0.4262, 0.5719, 0.6528, 0.7590, 1.000] + + If the chosen significance level is 0.05, we may be tempted to reject the + null hypotheses for the tests corresponding with the first nine p-values, + as the first nine p-values fall below the chosen significance level. + However, this would ignore the problem of "multiplicity": if we fail to + correct for the fact that multiple comparisons are being performed, we + are more likely to incorrectly reject true null hypotheses. + + One approach to the multiplicity problem is to control the family-wise + error rate (FWER), that is, the rate at which the null hypothesis is + rejected when it is actually true. A common procedure of this kind is the + Bonferroni correction [1]_. We begin by multiplying the p-values by the + number of hypotheses tested. + + >>> import numpy as np + >>> np.array(ps) * len(ps) + array([1.5000e-03, 6.0000e-03, 2.8500e-02, 1.4250e-01, 3.0150e-01, + 4.1700e-01, 4.4700e-01, 5.1600e-01, 6.8850e-01, 4.8600e+00, + 6.3930e+00, 8.5785e+00, 9.7920e+00, 1.1385e+01, 1.5000e+01]) + + To control the FWER at 5%, we reject only the hypotheses corresponding + with adjusted p-values less than 0.05. In this case, only the hypotheses + corresponding with the first three p-values can be rejected. According to + [1]_, these three hypotheses concerned "allergic reaction" and "two + different aspects of bleeding." + + An alternative approach is to control the false discovery rate: the + expected fraction of rejected null hypotheses that are actually true. The + advantage of this approach is that it typically affords greater power: an + increased rate of rejecting the null hypothesis when it is indeed false. To + control the false discovery rate at 5%, we apply the Benjamini-Hochberg + p-value adjustment. + + >>> from scipy import stats + >>> stats.false_discovery_control(ps) + array([0.0015 , 0.003 , 0.0095 , 0.035625 , 0.0603 , + 0.06385714, 0.06385714, 0.0645 , 0.0765 , 0.486 , + 0.58118182, 0.714875 , 0.75323077, 0.81321429, 1. ]) + + Now, the first *four* adjusted p-values fall below 0.05, so we would reject + the null hypotheses corresponding with these *four* p-values. Rejection + of the fourth null hypothesis was particularly important to the original + study as it led to the conclusion that the new treatment had a + "substantially lower in-hospital mortality rate." + + """ + # Input Validation and Special Cases + ps = np.asarray(ps) + + ps_in_range = (np.issubdtype(ps.dtype, np.number) + and np.all(ps == np.clip(ps, 0, 1))) + if not ps_in_range: + raise ValueError("`ps` must include only numbers between 0 and 1.") + + methods = {'bh', 'by'} + if method.lower() not in methods: + raise ValueError(f"Unrecognized `method` '{method}'." + f"Method must be one of {methods}.") + method = method.lower() + + if axis is None: + axis = 0 + ps = ps.ravel() + + axis = np.asarray(axis)[()] + if not np.issubdtype(axis.dtype, np.integer) or axis.size != 1: + raise ValueError("`axis` must be an integer or `None`") + + if ps.size <= 1 or ps.shape[axis] <= 1: + return ps[()] + + ps = np.moveaxis(ps, axis, -1) + m = ps.shape[-1] + + # Main Algorithm + # Equivalent to the ideas of [1] and [2], except that this adjusts the + # p-values as described in [3]. The results are similar to those produced + # by R's p.adjust. + + # "Let [ps] be the ordered observed p-values..." + order = np.argsort(ps, axis=-1) + ps = np.take_along_axis(ps, order, axis=-1) # this copies ps + + # Equation 1 of [1] rearranged to reject when p is less than specified q + i = np.arange(1, m+1) + ps *= m / i + + # Theorem 1.3 of [2] + if method == 'by': + ps *= np.sum(1 / i) + + # accounts for rejecting all null hypotheses i for i < k, where k is + # defined in Eq. 1 of either [1] or [2]. See [3]. Starting with the index j + # of the second to last element, we replace element j with element j+1 if + # the latter is smaller. + np.minimum.accumulate(ps[..., ::-1], out=ps[..., ::-1], axis=-1) + + # Restore original order of axes and data + np.put_along_axis(ps, order, values=ps.copy(), axis=-1) + ps = np.moveaxis(ps, -1, axis) + + return np.clip(ps, 0, 1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..6b2d46b271d03e3d9fca895d5726ea2a6c78471f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_basic.py @@ -0,0 +1,3564 @@ +""" +An extension of scipy.stats._stats_py to support masked arrays + +""" +# Original author (2007): Pierre GF Gerard-Marchant + + +__all__ = ['argstoarray', + 'count_tied_groups', + 'describe', + 'f_oneway', 'find_repeats','friedmanchisquare', + 'kendalltau','kendalltau_seasonal','kruskal','kruskalwallis', + 'ks_twosamp', 'ks_2samp', 'kurtosis', 'kurtosistest', + 'ks_1samp', 'kstest', + 'linregress', + 'mannwhitneyu', 'meppf','mode','moment','mquantiles','msign', + 'normaltest', + 'obrientransform', + 'pearsonr','plotting_positions','pointbiserialr', + 'rankdata', + 'scoreatpercentile','sem', + 'sen_seasonal_slopes','skew','skewtest','spearmanr', + 'siegelslopes', 'theilslopes', + 'tmax','tmean','tmin','trim','trimboth', + 'trimtail','trima','trimr','trimmed_mean','trimmed_std', + 'trimmed_stde','trimmed_var','tsem','ttest_1samp','ttest_onesamp', + 'ttest_ind','ttest_rel','tvar', + 'variation', + 'winsorize', + 'brunnermunzel', + ] + +import numpy as np +from numpy import ndarray +import numpy.ma as ma +from numpy.ma import masked, nomask +import math + +import itertools +import warnings +from collections import namedtuple + +from . import distributions +from scipy._lib._util import _rename_parameter, _contains_nan +from scipy._lib._bunch import _make_tuple_bunch +import scipy.special as special +import scipy.stats._stats_py + +from ._stats_mstats_common import ( + _find_repeats, + linregress as stats_linregress, + LinregressResult as stats_LinregressResult, + theilslopes as stats_theilslopes, + siegelslopes as stats_siegelslopes + ) + + +def _chk_asarray(a, axis): + # Always returns a masked array, raveled for axis=None + a = ma.asanyarray(a) + if axis is None: + a = ma.ravel(a) + outaxis = 0 + else: + outaxis = axis + return a, outaxis + + +def _chk2_asarray(a, b, axis): + a = ma.asanyarray(a) + b = ma.asanyarray(b) + if axis is None: + a = ma.ravel(a) + b = ma.ravel(b) + outaxis = 0 + else: + outaxis = axis + return a, b, outaxis + + +def _chk_size(a, b): + a = ma.asanyarray(a) + b = ma.asanyarray(b) + (na, nb) = (a.size, b.size) + if na != nb: + raise ValueError("The size of the input array should match!" + f" ({na} <> {nb})") + return (a, b, na) + + +def _ttest_finish(df, t, alternative): + """Common code between all 3 t-test functions.""" + # We use ``stdtr`` directly here to preserve masked arrays + + if alternative == 'less': + pval = special.stdtr(df, t) + elif alternative == 'greater': + pval = special.stdtr(df, -t) + elif alternative == 'two-sided': + pval = special.stdtr(df, -np.abs(t))*2 + else: + raise ValueError("alternative must be " + "'less', 'greater' or 'two-sided'") + + if t.ndim == 0: + t = t[()] + if pval.ndim == 0: + pval = pval[()] + + return t, pval + + +def argstoarray(*args): + """ + Constructs a 2D array from a group of sequences. + + Sequences are filled with missing values to match the length of the longest + sequence. + + Parameters + ---------- + *args : sequences + Group of sequences. + + Returns + ------- + argstoarray : MaskedArray + A ( `m` x `n` ) masked array, where `m` is the number of arguments and + `n` the length of the longest argument. + + Notes + ----- + `numpy.ma.vstack` has identical behavior, but is called with a sequence + of sequences. + + Examples + -------- + A 2D masked array constructed from a group of sequences is returned. + + >>> from scipy.stats.mstats import argstoarray + >>> argstoarray([1, 2, 3], [4, 5, 6]) + masked_array( + data=[[1.0, 2.0, 3.0], + [4.0, 5.0, 6.0]], + mask=[[False, False, False], + [False, False, False]], + fill_value=1e+20) + + The returned masked array filled with missing values when the lengths of + sequences are different. + + >>> argstoarray([1, 3], [4, 5, 6]) + masked_array( + data=[[1.0, 3.0, --], + [4.0, 5.0, 6.0]], + mask=[[False, False, True], + [False, False, False]], + fill_value=1e+20) + + """ + if len(args) == 1 and not isinstance(args[0], ndarray): + output = ma.asarray(args[0]) + if output.ndim != 2: + raise ValueError("The input should be 2D") + else: + n = len(args) + m = max([len(k) for k in args]) + output = ma.array(np.empty((n,m), dtype=float), mask=True) + for (k,v) in enumerate(args): + output[k,:len(v)] = v + + output[np.logical_not(np.isfinite(output._data))] = masked + return output + + +def find_repeats(arr): + """Find repeats in arr and return a tuple (repeats, repeat_count). + + The input is cast to float64. Masked values are discarded. + + Parameters + ---------- + arr : sequence + Input array. The array is flattened if it is not 1D. + + Returns + ------- + repeats : ndarray + Array of repeated values. + counts : ndarray + Array of counts. + + Examples + -------- + >>> from scipy.stats import mstats + >>> mstats.find_repeats([2, 1, 2, 3, 2, 2, 5]) + (array([2.]), array([4])) + + In the above example, 2 repeats 4 times. + + >>> mstats.find_repeats([[10, 20, 1, 2], [5, 5, 4, 4]]) + (array([4., 5.]), array([2, 2])) + + In the above example, both 4 and 5 repeat 2 times. + + """ + # Make sure we get a copy. ma.compressed promises a "new array", but can + # actually return a reference. + compr = np.asarray(ma.compressed(arr), dtype=np.float64) + try: + need_copy = np.may_share_memory(compr, arr) + except AttributeError: + # numpy < 1.8.2 bug: np.may_share_memory([], []) raises, + # while in numpy 1.8.2 and above it just (correctly) returns False. + need_copy = False + if need_copy: + compr = compr.copy() + return _find_repeats(compr) + + +def count_tied_groups(x, use_missing=False): + """ + Counts the number of tied values. + + Parameters + ---------- + x : sequence + Sequence of data on which to counts the ties + use_missing : bool, optional + Whether to consider missing values as tied. + + Returns + ------- + count_tied_groups : dict + Returns a dictionary (nb of ties: nb of groups). + + Examples + -------- + >>> from scipy.stats import mstats + >>> import numpy as np + >>> z = [0, 0, 0, 2, 2, 2, 3, 3, 4, 5, 6] + >>> mstats.count_tied_groups(z) + {2: 1, 3: 2} + + In the above example, the ties were 0 (3x), 2 (3x) and 3 (2x). + + >>> z = np.ma.array([0, 0, 1, 2, 2, 2, 3, 3, 4, 5, 6]) + >>> mstats.count_tied_groups(z) + {2: 2, 3: 1} + >>> z[[1,-1]] = np.ma.masked + >>> mstats.count_tied_groups(z, use_missing=True) + {2: 2, 3: 1} + + """ + nmasked = ma.getmask(x).sum() + # We need the copy as find_repeats will overwrite the initial data + data = ma.compressed(x).copy() + (ties, counts) = find_repeats(data) + nties = {} + if len(ties): + nties = dict(zip(np.unique(counts), itertools.repeat(1))) + nties.update(dict(zip(*find_repeats(counts)))) + + if nmasked and use_missing: + try: + nties[nmasked] += 1 + except KeyError: + nties[nmasked] = 1 + + return nties + + +def rankdata(data, axis=None, use_missing=False): + """Returns the rank (also known as order statistics) of each data point + along the given axis. + + If some values are tied, their rank is averaged. + If some values are masked, their rank is set to 0 if use_missing is False, + or set to the average rank of the unmasked values if use_missing is True. + + Parameters + ---------- + data : sequence + Input data. The data is transformed to a masked array + axis : {None,int}, optional + Axis along which to perform the ranking. + If None, the array is first flattened. An exception is raised if + the axis is specified for arrays with a dimension larger than 2 + use_missing : bool, optional + Whether the masked values have a rank of 0 (False) or equal to the + average rank of the unmasked values (True). + + """ + def _rank1d(data, use_missing=False): + n = data.count() + rk = np.empty(data.size, dtype=float) + idx = data.argsort() + rk[idx[:n]] = np.arange(1,n+1) + + if use_missing: + rk[idx[n:]] = (n+1)/2. + else: + rk[idx[n:]] = 0 + + repeats = find_repeats(data.copy()) + for r in repeats[0]: + condition = (data == r).filled(False) + rk[condition] = rk[condition].mean() + return rk + + data = ma.array(data, copy=False) + if axis is None: + if data.ndim > 1: + return _rank1d(data.ravel(), use_missing).reshape(data.shape) + else: + return _rank1d(data, use_missing) + else: + return ma.apply_along_axis(_rank1d,axis,data,use_missing).view(ndarray) + + +ModeResult = namedtuple('ModeResult', ('mode', 'count')) + + +def mode(a, axis=0): + """ + Returns an array of the modal (most common) value in the passed array. + + Parameters + ---------- + a : array_like + n-dimensional array of which to find mode(s). + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + + Returns + ------- + mode : ndarray + Array of modal values. + count : ndarray + Array of counts for each mode. + + Notes + ----- + For more details, see `scipy.stats.mode`. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> from scipy.stats import mstats + >>> m_arr = np.ma.array([1, 1, 0, 0, 0, 0], mask=[0, 0, 1, 1, 1, 0]) + >>> mstats.mode(m_arr) # note that most zeros are masked + ModeResult(mode=array([1.]), count=array([2.])) + + """ + return _mode(a, axis=axis, keepdims=True) + + +def _mode(a, axis=0, keepdims=True): + # Don't want to expose `keepdims` from the public `mstats.mode` + a, axis = _chk_asarray(a, axis) + + def _mode1D(a): + (rep,cnt) = find_repeats(a) + if not cnt.ndim: + return (0, 0) + elif cnt.size: + return (rep[cnt.argmax()], cnt.max()) + else: + return (a.min(), 1) + + if axis is None: + output = _mode1D(ma.ravel(a)) + output = (ma.array(output[0]), ma.array(output[1])) + else: + output = ma.apply_along_axis(_mode1D, axis, a) + if keepdims is None or keepdims: + newshape = list(a.shape) + newshape[axis] = 1 + slices = [slice(None)] * output.ndim + slices[axis] = 0 + modes = output[tuple(slices)].reshape(newshape) + slices[axis] = 1 + counts = output[tuple(slices)].reshape(newshape) + output = (modes, counts) + else: + output = np.moveaxis(output, axis, 0) + + return ModeResult(*output) + + +def _betai(a, b, x): + x = np.asanyarray(x) + x = ma.where(x < 1.0, x, 1.0) # if x > 1 then return 1.0 + return special.betainc(a, b, x) + + +def msign(x): + """Returns the sign of x, or 0 if x is masked.""" + return ma.filled(np.sign(x), 0) + + +def pearsonr(x, y): + r""" + Pearson correlation coefficient and p-value for testing non-correlation. + + The Pearson correlation coefficient [1]_ measures the linear relationship + between two datasets. The calculation of the p-value relies on the + assumption that each dataset is normally distributed. (See Kowalski [3]_ + for a discussion of the effects of non-normality of the input on the + distribution of the correlation coefficient.) Like other correlation + coefficients, this one varies between -1 and +1 with 0 implying no + correlation. Correlations of -1 or +1 imply an exact linear relationship. + + Parameters + ---------- + x : (N,) array_like + Input array. + y : (N,) array_like + Input array. + + Returns + ------- + r : float + Pearson's correlation coefficient. + p-value : float + Two-tailed p-value. + + Warns + ----- + `~scipy.stats.ConstantInputWarning` + Raised if an input is a constant array. The correlation coefficient + is not defined in this case, so ``np.nan`` is returned. + + `~scipy.stats.NearConstantInputWarning` + Raised if an input is "nearly" constant. The array ``x`` is considered + nearly constant if ``norm(x - mean(x)) < 1e-13 * abs(mean(x))``. + Numerical errors in the calculation ``x - mean(x)`` in this case might + result in an inaccurate calculation of r. + + See Also + -------- + spearmanr : Spearman rank-order correlation coefficient. + kendalltau : Kendall's tau, a correlation measure for ordinal data. + + Notes + ----- + The correlation coefficient is calculated as follows: + + .. math:: + + r = \frac{\sum (x - m_x) (y - m_y)} + {\sqrt{\sum (x - m_x)^2 \sum (y - m_y)^2}} + + where :math:`m_x` is the mean of the vector x and :math:`m_y` is + the mean of the vector y. + + Under the assumption that x and y are drawn from + independent normal distributions (so the population correlation coefficient + is 0), the probability density function of the sample correlation + coefficient r is ([1]_, [2]_): + + .. math:: + + f(r) = \frac{{(1-r^2)}^{n/2-2}}{\mathrm{B}(\frac{1}{2},\frac{n}{2}-1)} + + where n is the number of samples, and B is the beta function. This + is sometimes referred to as the exact distribution of r. This is + the distribution that is used in `pearsonr` to compute the p-value. + The distribution is a beta distribution on the interval [-1, 1], + with equal shape parameters a = b = n/2 - 1. In terms of SciPy's + implementation of the beta distribution, the distribution of r is:: + + dist = scipy.stats.beta(n/2 - 1, n/2 - 1, loc=-1, scale=2) + + The p-value returned by `pearsonr` is a two-sided p-value. The p-value + roughly indicates the probability of an uncorrelated system + producing datasets that have a Pearson correlation at least as extreme + as the one computed from these datasets. More precisely, for a + given sample with correlation coefficient r, the p-value is + the probability that abs(r') of a random sample x' and y' drawn from + the population with zero correlation would be greater than or equal + to abs(r). In terms of the object ``dist`` shown above, the p-value + for a given r and length n can be computed as:: + + p = 2*dist.cdf(-abs(r)) + + When n is 2, the above continuous distribution is not well-defined. + One can interpret the limit of the beta distribution as the shape + parameters a and b approach a = b = 0 as a discrete distribution with + equal probability masses at r = 1 and r = -1. More directly, one + can observe that, given the data x = [x1, x2] and y = [y1, y2], and + assuming x1 != x2 and y1 != y2, the only possible values for r are 1 + and -1. Because abs(r') for any sample x' and y' with length 2 will + be 1, the two-sided p-value for a sample of length 2 is always 1. + + References + ---------- + .. [1] "Pearson correlation coefficient", Wikipedia, + https://en.wikipedia.org/wiki/Pearson_correlation_coefficient + .. [2] Student, "Probable error of a correlation coefficient", + Biometrika, Volume 6, Issue 2-3, 1 September 1908, pp. 302-310. + .. [3] C. J. Kowalski, "On the Effects of Non-Normality on the Distribution + of the Sample Product-Moment Correlation Coefficient" + Journal of the Royal Statistical Society. Series C (Applied + Statistics), Vol. 21, No. 1 (1972), pp. 1-12. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> from scipy.stats import mstats + >>> mstats.pearsonr([1, 2, 3, 4, 5], [10, 9, 2.5, 6, 4]) + (-0.7426106572325057, 0.1505558088534455) + + There is a linear dependence between x and y if y = a + b*x + e, where + a,b are constants and e is a random error term, assumed to be independent + of x. For simplicity, assume that x is standard normal, a=0, b=1 and let + e follow a normal distribution with mean zero and standard deviation s>0. + + >>> s = 0.5 + >>> x = stats.norm.rvs(size=500) + >>> e = stats.norm.rvs(scale=s, size=500) + >>> y = x + e + >>> mstats.pearsonr(x, y) + (0.9029601878969703, 8.428978827629898e-185) # may vary + + This should be close to the exact value given by + + >>> 1/np.sqrt(1 + s**2) + 0.8944271909999159 + + For s=0.5, we observe a high level of correlation. In general, a large + variance of the noise reduces the correlation, while the correlation + approaches one as the variance of the error goes to zero. + + It is important to keep in mind that no correlation does not imply + independence unless (x, y) is jointly normal. Correlation can even be zero + when there is a very simple dependence structure: if X follows a + standard normal distribution, let y = abs(x). Note that the correlation + between x and y is zero. Indeed, since the expectation of x is zero, + cov(x, y) = E[x*y]. By definition, this equals E[x*abs(x)] which is zero + by symmetry. The following lines of code illustrate this observation: + + >>> y = np.abs(x) + >>> mstats.pearsonr(x, y) + (-0.016172891856853524, 0.7182823678751942) # may vary + + A non-zero correlation coefficient can be misleading. For example, if X has + a standard normal distribution, define y = x if x < 0 and y = 0 otherwise. + A simple calculation shows that corr(x, y) = sqrt(2/Pi) = 0.797..., + implying a high level of correlation: + + >>> y = np.where(x < 0, x, 0) + >>> mstats.pearsonr(x, y) + (0.8537091583771509, 3.183461621422181e-143) # may vary + + This is unintuitive since there is no dependence of x and y if x is larger + than zero which happens in about half of the cases if we sample x and y. + """ + (x, y, n) = _chk_size(x, y) + (x, y) = (x.ravel(), y.ravel()) + # Get the common mask and the total nb of unmasked elements + m = ma.mask_or(ma.getmask(x), ma.getmask(y)) + n -= m.sum() + df = n-2 + if df < 0: + return (masked, masked) + + return scipy.stats._stats_py.pearsonr( + ma.masked_array(x, mask=m).compressed(), + ma.masked_array(y, mask=m).compressed()) + + +def spearmanr(x, y=None, use_ties=True, axis=None, nan_policy='propagate', + alternative='two-sided'): + """ + Calculates a Spearman rank-order correlation coefficient and the p-value + to test for non-correlation. + + The Spearman correlation is a nonparametric measure of the linear + relationship between two datasets. Unlike the Pearson correlation, the + Spearman correlation does not assume that both datasets are normally + distributed. Like other correlation coefficients, this one varies + between -1 and +1 with 0 implying no correlation. Correlations of -1 or + +1 imply a monotonic relationship. Positive correlations imply that + as `x` increases, so does `y`. Negative correlations imply that as `x` + increases, `y` decreases. + + Missing values are discarded pair-wise: if a value is missing in `x`, the + corresponding value in `y` is masked. + + The p-value roughly indicates the probability of an uncorrelated system + producing datasets that have a Spearman correlation at least as extreme + as the one computed from these datasets. The p-values are not entirely + reliable but are probably reasonable for datasets larger than 500 or so. + + Parameters + ---------- + x, y : 1D or 2D array_like, y is optional + One or two 1-D or 2-D arrays containing multiple variables and + observations. When these are 1-D, each represents a vector of + observations of a single variable. For the behavior in the 2-D case, + see under ``axis``, below. + use_ties : bool, optional + DO NOT USE. Does not do anything, keyword is only left in place for + backwards compatibility reasons. + axis : int or None, optional + If axis=0 (default), then each column represents a variable, with + observations in the rows. If axis=1, the relationship is transposed: + each row represents a variable, while the columns contain observations. + If axis=None, then both arrays will be raveled. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. 'propagate' returns nan, + 'raise' throws an error, 'omit' performs the calculations ignoring nan + values. Default is 'propagate'. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the correlation is nonzero + * 'less': the correlation is negative (less than zero) + * 'greater': the correlation is positive (greater than zero) + + .. versionadded:: 1.7.0 + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float or ndarray (2-D square) + Spearman correlation matrix or correlation coefficient (if only 2 + variables are given as parameters). Correlation matrix is square + with length equal to total number of variables (columns or rows) in + ``a`` and ``b`` combined. + pvalue : float + The p-value for a hypothesis test whose null hypothesis + is that two sets of data are linearly uncorrelated. See + `alternative` above for alternative hypotheses. `pvalue` has the + same shape as `statistic`. + + References + ---------- + [CRCProbStat2000] section 14.7 + + """ + if not use_ties: + raise ValueError("`use_ties=False` is not supported in SciPy >= 1.2.0") + + # Always returns a masked array, raveled if axis=None + x, axisout = _chk_asarray(x, axis) + if y is not None: + # Deal only with 2-D `x` case. + y, _ = _chk_asarray(y, axis) + if axisout == 0: + x = ma.column_stack((x, y)) + else: + x = ma.vstack((x, y)) + + if axisout == 1: + # To simplify the code that follow (always use `n_obs, n_vars` shape) + x = x.T + + if nan_policy == 'omit': + x = ma.masked_invalid(x) + + def _spearmanr_2cols(x): + # Mask the same observations for all variables, and then drop those + # observations (can't leave them masked, rankdata is weird). + x = ma.mask_rowcols(x, axis=0) + x = x[~x.mask.any(axis=1), :] + + # If either column is entirely NaN or Inf + if not np.any(x.data): + res = scipy.stats._stats_py.SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + m = ma.getmask(x) + n_obs = x.shape[0] + dof = n_obs - 2 - int(m.sum(axis=0)[0]) + if dof < 0: + raise ValueError("The input must have at least 3 entries!") + + # Gets the ranks and rank differences + x_ranked = rankdata(x, axis=0) + rs = ma.corrcoef(x_ranked, rowvar=False).data + + # rs can have elements equal to 1, so avoid zero division warnings + with np.errstate(divide='ignore'): + # clip the small negative values possibly caused by rounding + # errors before taking the square root + t = rs * np.sqrt((dof / ((rs+1.0) * (1.0-rs))).clip(0)) + + t, prob = _ttest_finish(dof, t, alternative) + + # For backwards compatibility, return scalars when comparing 2 columns + if rs.shape == (2, 2): + res = scipy.stats._stats_py.SignificanceResult(rs[1, 0], + prob[1, 0]) + res.correlation = rs[1, 0] + return res + else: + res = scipy.stats._stats_py.SignificanceResult(rs, prob) + res.correlation = rs + return res + + # Need to do this per pair of variables, otherwise the dropped observations + # in a third column mess up the result for a pair. + n_vars = x.shape[1] + if n_vars == 2: + return _spearmanr_2cols(x) + else: + rs = np.ones((n_vars, n_vars), dtype=float) + prob = np.zeros((n_vars, n_vars), dtype=float) + for var1 in range(n_vars - 1): + for var2 in range(var1+1, n_vars): + result = _spearmanr_2cols(x[:, [var1, var2]]) + rs[var1, var2] = result.correlation + rs[var2, var1] = result.correlation + prob[var1, var2] = result.pvalue + prob[var2, var1] = result.pvalue + + res = scipy.stats._stats_py.SignificanceResult(rs, prob) + res.correlation = rs + return res + + +def _kendall_p_exact(n, c, alternative='two-sided'): + + # Use the fact that distribution is symmetric: always calculate a CDF in + # the left tail. + # This will be the one-sided p-value if `c` is on the side of + # the null distribution predicted by the alternative hypothesis. + # The two-sided p-value will be twice this value. + # If `c` is on the other side of the null distribution, we'll need to + # take the complement and add back the probability mass at `c`. + in_right_tail = (c >= (n*(n-1))//2 - c) + alternative_greater = (alternative == 'greater') + c = int(min(c, (n*(n-1))//2 - c)) + + # Exact p-value, see Maurice G. Kendall, "Rank Correlation Methods" + # (4th Edition), Charles Griffin & Co., 1970. + if n <= 0: + raise ValueError(f'n ({n}) must be positive') + elif c < 0 or 4*c > n*(n-1): + raise ValueError(f'c ({c}) must satisfy 0 <= 4c <= n(n-1) = {n*(n-1)}.') + elif n == 1: + prob = 1.0 + p_mass_at_c = 1 + elif n == 2: + prob = 1.0 + p_mass_at_c = 0.5 + elif c == 0: + prob = 2.0/math.factorial(n) if n < 171 else 0.0 + p_mass_at_c = prob/2 + elif c == 1: + prob = 2.0/math.factorial(n-1) if n < 172 else 0.0 + p_mass_at_c = (n-1)/math.factorial(n) + elif 4*c == n*(n-1) and alternative == 'two-sided': + # I'm sure there's a simple formula for p_mass_at_c in this + # case, but I don't know it. Use generic formula for one-sided p-value. + prob = 1.0 + elif n < 171: + new = np.zeros(c+1) + new[0:2] = 1.0 + for j in range(3,n+1): + new = np.cumsum(new) + if j <= c: + new[j:] -= new[:c+1-j] + prob = 2.0*np.sum(new)/math.factorial(n) + p_mass_at_c = new[-1]/math.factorial(n) + else: + new = np.zeros(c+1) + new[0:2] = 1.0 + for j in range(3, n+1): + new = np.cumsum(new)/j + if j <= c: + new[j:] -= new[:c+1-j] + prob = np.sum(new) + p_mass_at_c = new[-1]/2 + + if alternative != 'two-sided': + # if the alternative hypothesis and alternative agree, + # one-sided p-value is half the two-sided p-value + if in_right_tail == alternative_greater: + prob /= 2 + else: + prob = 1 - prob/2 + p_mass_at_c + + prob = np.clip(prob, 0, 1) + + return prob + + +def kendalltau(x, y, use_ties=True, use_missing=False, method='auto', + alternative='two-sided'): + """ + Computes Kendall's rank correlation tau on two variables *x* and *y*. + + Parameters + ---------- + x : sequence + First data list (for example, time). + y : sequence + Second data list. + use_ties : {True, False}, optional + Whether ties correction should be performed. + use_missing : {False, True}, optional + Whether missing data should be allocated a rank of 0 (False) or the + average rank (True) + method : {'auto', 'asymptotic', 'exact'}, optional + Defines which method is used to calculate the p-value [1]_. + 'asymptotic' uses a normal approximation valid for large samples. + 'exact' computes the exact p-value, but can only be used if no ties + are present. As the sample size increases, the 'exact' computation + time may grow and the result may lose some precision. + 'auto' is the default and selects the appropriate + method based on a trade-off between speed and accuracy. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the rank correlation is nonzero + * 'less': the rank correlation is negative (less than zero) + * 'greater': the rank correlation is positive (greater than zero) + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float + The tau statistic. + pvalue : float + The p-value for a hypothesis test whose null hypothesis is + an absence of association, tau = 0. + + References + ---------- + .. [1] Maurice G. Kendall, "Rank Correlation Methods" (4th Edition), + Charles Griffin & Co., 1970. + + """ + (x, y, n) = _chk_size(x, y) + (x, y) = (x.flatten(), y.flatten()) + m = ma.mask_or(ma.getmask(x), ma.getmask(y)) + if m is not nomask: + x = ma.array(x, mask=m, copy=True) + y = ma.array(y, mask=m, copy=True) + # need int() here, otherwise numpy defaults to 32 bit + # integer on all Windows architectures, causing overflow. + # int() will keep it infinite precision. + n -= int(m.sum()) + + if n < 2: + res = scipy.stats._stats_py.SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + rx = ma.masked_equal(rankdata(x, use_missing=use_missing), 0) + ry = ma.masked_equal(rankdata(y, use_missing=use_missing), 0) + idx = rx.argsort() + (rx, ry) = (rx[idx], ry[idx]) + C = np.sum([((ry[i+1:] > ry[i]) * (rx[i+1:] > rx[i])).filled(0).sum() + for i in range(len(ry)-1)], dtype=float) + D = np.sum([((ry[i+1:] < ry[i])*(rx[i+1:] > rx[i])).filled(0).sum() + for i in range(len(ry)-1)], dtype=float) + xties = count_tied_groups(x) + yties = count_tied_groups(y) + if use_ties: + corr_x = np.sum([v*k*(k-1) for (k,v) in xties.items()], dtype=float) + corr_y = np.sum([v*k*(k-1) for (k,v) in yties.items()], dtype=float) + denom = ma.sqrt((n*(n-1)-corr_x)/2. * (n*(n-1)-corr_y)/2.) + else: + denom = n*(n-1)/2. + tau = (C-D) / denom + + if method == 'exact' and (xties or yties): + raise ValueError("Ties found, exact method cannot be used.") + + if method == 'auto': + if (not xties and not yties) and (n <= 33 or min(C, n*(n-1)/2.0-C) <= 1): + method = 'exact' + else: + method = 'asymptotic' + + if not xties and not yties and method == 'exact': + prob = _kendall_p_exact(n, C, alternative) + + elif method == 'asymptotic': + var_s = n*(n-1)*(2*n+5) + if use_ties: + var_s -= np.sum([v*k*(k-1)*(2*k+5)*1. for (k,v) in xties.items()]) + var_s -= np.sum([v*k*(k-1)*(2*k+5)*1. for (k,v) in yties.items()]) + v1 = (np.sum([v*k*(k-1) for (k, v) in xties.items()], dtype=float) * + np.sum([v*k*(k-1) for (k, v) in yties.items()], dtype=float)) + v1 /= 2.*n*(n-1) + if n > 2: + v2 = np.sum([v*k*(k-1)*(k-2) for (k,v) in xties.items()], + dtype=float) * \ + np.sum([v*k*(k-1)*(k-2) for (k,v) in yties.items()], + dtype=float) + v2 /= 9.*n*(n-1)*(n-2) + else: + v2 = 0 + else: + v1 = v2 = 0 + + var_s /= 18. + var_s += (v1 + v2) + z = (C-D)/np.sqrt(var_s) + prob = scipy.stats._stats_py._get_pvalue(z, distributions.norm, alternative) + else: + raise ValueError("Unknown method "+str(method)+" specified, please " + "use auto, exact or asymptotic.") + + res = scipy.stats._stats_py.SignificanceResult(tau[()], prob[()]) + res.correlation = tau + return res + + +def kendalltau_seasonal(x): + """ + Computes a multivariate Kendall's rank correlation tau, for seasonal data. + + Parameters + ---------- + x : 2-D ndarray + Array of seasonal data, with seasons in columns. + + """ + x = ma.array(x, subok=True, copy=False, ndmin=2) + (n,m) = x.shape + n_p = x.count(0) + + S_szn = sum(msign(x[i:]-x[i]).sum(0) for i in range(n)) + S_tot = S_szn.sum() + + n_tot = x.count() + ties = count_tied_groups(x.compressed()) + corr_ties = sum(v*k*(k-1) for (k,v) in ties.items()) + denom_tot = ma.sqrt(1.*n_tot*(n_tot-1)*(n_tot*(n_tot-1)-corr_ties))/2. + + R = rankdata(x, axis=0, use_missing=True) + K = ma.empty((m,m), dtype=int) + covmat = ma.empty((m,m), dtype=float) + denom_szn = ma.empty(m, dtype=float) + for j in range(m): + ties_j = count_tied_groups(x[:,j].compressed()) + corr_j = sum(v*k*(k-1) for (k,v) in ties_j.items()) + cmb = n_p[j]*(n_p[j]-1) + for k in range(j,m,1): + K[j,k] = sum(msign((x[i:,j]-x[i,j])*(x[i:,k]-x[i,k])).sum() + for i in range(n)) + covmat[j,k] = (K[j,k] + 4*(R[:,j]*R[:,k]).sum() - + n*(n_p[j]+1)*(n_p[k]+1))/3. + K[k,j] = K[j,k] + covmat[k,j] = covmat[j,k] + + denom_szn[j] = ma.sqrt(cmb*(cmb-corr_j)) / 2. + + var_szn = covmat.diagonal() + + z_szn = msign(S_szn) * (abs(S_szn)-1) / ma.sqrt(var_szn) + z_tot_ind = msign(S_tot) * (abs(S_tot)-1) / ma.sqrt(var_szn.sum()) + z_tot_dep = msign(S_tot) * (abs(S_tot)-1) / ma.sqrt(covmat.sum()) + + prob_szn = special.erfc(abs(z_szn.data)/np.sqrt(2)) + prob_tot_ind = special.erfc(abs(z_tot_ind)/np.sqrt(2)) + prob_tot_dep = special.erfc(abs(z_tot_dep)/np.sqrt(2)) + + chi2_tot = (z_szn*z_szn).sum() + chi2_trd = m * z_szn.mean()**2 + output = {'seasonal tau': S_szn/denom_szn, + 'global tau': S_tot/denom_tot, + 'global tau (alt)': S_tot/denom_szn.sum(), + 'seasonal p-value': prob_szn, + 'global p-value (indep)': prob_tot_ind, + 'global p-value (dep)': prob_tot_dep, + 'chi2 total': chi2_tot, + 'chi2 trend': chi2_trd, + } + return output + + +PointbiserialrResult = namedtuple('PointbiserialrResult', ('correlation', + 'pvalue')) + + +def pointbiserialr(x, y): + """Calculates a point biserial correlation coefficient and its p-value. + + Parameters + ---------- + x : array_like of bools + Input array. + y : array_like + Input array. + + Returns + ------- + correlation : float + R value + pvalue : float + 2-tailed p-value + + Notes + ----- + Missing values are considered pair-wise: if a value is missing in x, + the corresponding value in y is masked. + + For more details on `pointbiserialr`, see `scipy.stats.pointbiserialr`. + + """ + x = ma.fix_invalid(x, copy=True).astype(bool) + y = ma.fix_invalid(y, copy=True).astype(float) + # Get rid of the missing data + m = ma.mask_or(ma.getmask(x), ma.getmask(y)) + if m is not nomask: + unmask = np.logical_not(m) + x = x[unmask] + y = y[unmask] + + n = len(x) + # phat is the fraction of x values that are True + phat = x.sum() / float(n) + y0 = y[~x] # y-values where x is False + y1 = y[x] # y-values where x is True + y0m = y0.mean() + y1m = y1.mean() + + rpb = (y1m - y0m)*np.sqrt(phat * (1-phat)) / y.std() + + df = n-2 + t = rpb*ma.sqrt(df/(1.0-rpb**2)) + prob = _betai(0.5*df, 0.5, df/(df+t*t)) + + return PointbiserialrResult(rpb, prob) + + +def linregress(x, y=None): + r""" + Linear regression calculation + + Note that the non-masked version is used, and that this docstring is + replaced by the non-masked docstring + some info on missing data. + + """ + if y is None: + x = ma.array(x) + if x.shape[0] == 2: + x, y = x + elif x.shape[1] == 2: + x, y = x.T + else: + raise ValueError("If only `x` is given as input, " + "it has to be of shape (2, N) or (N, 2), " + f"provided shape was {x.shape}") + else: + x = ma.array(x) + y = ma.array(y) + + x = x.flatten() + y = y.flatten() + + if np.amax(x) == np.amin(x) and len(x) > 1: + raise ValueError("Cannot calculate a linear regression " + "if all x values are identical") + + m = ma.mask_or(ma.getmask(x), ma.getmask(y), shrink=False) + if m is not nomask: + x = ma.array(x, mask=m) + y = ma.array(y, mask=m) + if np.any(~m): + result = stats_linregress(x.data[~m], y.data[~m]) + else: + # All data is masked + result = stats_LinregressResult(slope=None, intercept=None, + rvalue=None, pvalue=None, + stderr=None, + intercept_stderr=None) + else: + result = stats_linregress(x.data, y.data) + + return result + + +def theilslopes(y, x=None, alpha=0.95, method='separate'): + r""" + Computes the Theil-Sen estimator for a set of points (x, y). + + `theilslopes` implements a method for robust linear regression. It + computes the slope as the median of all slopes between paired values. + + Parameters + ---------- + y : array_like + Dependent variable. + x : array_like or None, optional + Independent variable. If None, use ``arange(len(y))`` instead. + alpha : float, optional + Confidence degree between 0 and 1. Default is 95% confidence. + Note that `alpha` is symmetric around 0.5, i.e. both 0.1 and 0.9 are + interpreted as "find the 90% confidence interval". + method : {'joint', 'separate'}, optional + Method to be used for computing estimate for intercept. + Following methods are supported, + + * 'joint': Uses np.median(y - slope * x) as intercept. + * 'separate': Uses np.median(y) - slope * np.median(x) + as intercept. + + The default is 'separate'. + + .. versionadded:: 1.8.0 + + Returns + ------- + result : ``TheilslopesResult`` instance + The return value is an object with the following attributes: + + slope : float + Theil slope. + intercept : float + Intercept of the Theil line. + low_slope : float + Lower bound of the confidence interval on `slope`. + high_slope : float + Upper bound of the confidence interval on `slope`. + + See Also + -------- + siegelslopes : a similar technique using repeated medians + + + Notes + ----- + For more details on `theilslopes`, see `scipy.stats.theilslopes`. + + """ + y = ma.asarray(y).flatten() + if x is None: + x = ma.arange(len(y), dtype=float) + else: + x = ma.asarray(x).flatten() + if len(x) != len(y): + raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") + + m = ma.mask_or(ma.getmask(x), ma.getmask(y)) + y._mask = x._mask = m + # Disregard any masked elements of x or y + y = y.compressed() + x = x.compressed().astype(float) + # We now have unmasked arrays so can use `scipy.stats.theilslopes` + return stats_theilslopes(y, x, alpha=alpha, method=method) + + +def siegelslopes(y, x=None, method="hierarchical"): + r""" + Computes the Siegel estimator for a set of points (x, y). + + `siegelslopes` implements a method for robust linear regression + using repeated medians to fit a line to the points (x, y). + The method is robust to outliers with an asymptotic breakdown point + of 50%. + + Parameters + ---------- + y : array_like + Dependent variable. + x : array_like or None, optional + Independent variable. If None, use ``arange(len(y))`` instead. + method : {'hierarchical', 'separate'} + If 'hierarchical', estimate the intercept using the estimated + slope ``slope`` (default option). + If 'separate', estimate the intercept independent of the estimated + slope. See Notes for details. + + Returns + ------- + result : ``SiegelslopesResult`` instance + The return value is an object with the following attributes: + + slope : float + Estimate of the slope of the regression line. + intercept : float + Estimate of the intercept of the regression line. + + See Also + -------- + theilslopes : a similar technique without repeated medians + + Notes + ----- + For more details on `siegelslopes`, see `scipy.stats.siegelslopes`. + + """ + y = ma.asarray(y).ravel() + if x is None: + x = ma.arange(len(y), dtype=float) + else: + x = ma.asarray(x).ravel() + if len(x) != len(y): + raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") + + m = ma.mask_or(ma.getmask(x), ma.getmask(y)) + y._mask = x._mask = m + # Disregard any masked elements of x or y + y = y.compressed() + x = x.compressed().astype(float) + # We now have unmasked arrays so can use `scipy.stats.siegelslopes` + return stats_siegelslopes(y, x, method=method) + + +SenSeasonalSlopesResult = _make_tuple_bunch('SenSeasonalSlopesResult', + ['intra_slope', 'inter_slope']) + + +def sen_seasonal_slopes(x): + r""" + Computes seasonal Theil-Sen and Kendall slope estimators. + + The seasonal generalization of Sen's slope computes the slopes between all + pairs of values within a "season" (column) of a 2D array. It returns an + array containing the median of these "within-season" slopes for each + season (the Theil-Sen slope estimator of each season), and it returns the + median of the within-season slopes across all seasons (the seasonal Kendall + slope estimator). + + Parameters + ---------- + x : 2D array_like + Each column of `x` contains measurements of the dependent variable + within a season. The independent variable (usually time) of each season + is assumed to be ``np.arange(x.shape[0])``. + + Returns + ------- + result : ``SenSeasonalSlopesResult`` instance + The return value is an object with the following attributes: + + intra_slope : ndarray + For each season, the Theil-Sen slope estimator: the median of + within-season slopes. + inter_slope : float + The seasonal Kendall slope estimateor: the median of within-season + slopes *across all* seasons. + + See Also + -------- + theilslopes : the analogous function for non-seasonal data + scipy.stats.theilslopes : non-seasonal slopes for non-masked arrays + + Notes + ----- + The slopes :math:`d_{ijk}` within season :math:`i` are: + + .. math:: + + d_{ijk} = \frac{x_{ij} - x_{ik}} + {j - k} + + for pairs of distinct integer indices :math:`j, k` of :math:`x`. + + Element :math:`i` of the returned `intra_slope` array is the median of the + :math:`d_{ijk}` over all :math:`j < k`; this is the Theil-Sen slope + estimator of season :math:`i`. The returned `inter_slope` value, better + known as the seasonal Kendall slope estimator, is the median of the + :math:`d_{ijk}` over all :math:`i, j, k`. + + References + ---------- + .. [1] Hirsch, Robert M., James R. Slack, and Richard A. Smith. + "Techniques of trend analysis for monthly water quality data." + *Water Resources Research* 18.1 (1982): 107-121. + + Examples + -------- + Suppose we have 100 observations of a dependent variable for each of four + seasons: + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> x = rng.random(size=(100, 4)) + + We compute the seasonal slopes as: + + >>> from scipy import stats + >>> intra_slope, inter_slope = stats.mstats.sen_seasonal_slopes(x) + + If we define a function to compute all slopes between observations within + a season: + + >>> def dijk(yi): + ... n = len(yi) + ... x = np.arange(n) + ... dy = yi - yi[:, np.newaxis] + ... dx = x - x[:, np.newaxis] + ... # we only want unique pairs of distinct indices + ... mask = np.triu(np.ones((n, n), dtype=bool), k=1) + ... return dy[mask]/dx[mask] + + then element ``i`` of ``intra_slope`` is the median of ``dijk[x[:, i]]``: + + >>> i = 2 + >>> np.allclose(np.median(dijk(x[:, i])), intra_slope[i]) + True + + and ``inter_slope`` is the median of the values returned by ``dijk`` for + all seasons: + + >>> all_slopes = np.concatenate([dijk(x[:, i]) for i in range(x.shape[1])]) + >>> np.allclose(np.median(all_slopes), inter_slope) + True + + Because the data are randomly generated, we would expect the median slopes + to be nearly zero both within and across all seasons, and indeed they are: + + >>> intra_slope.data + array([ 0.00124504, -0.00277761, -0.00221245, -0.00036338]) + >>> inter_slope + -0.0010511779872922058 + + """ + x = ma.array(x, subok=True, copy=False, ndmin=2) + (n,_) = x.shape + # Get list of slopes per season + szn_slopes = ma.vstack([(x[i+1:]-x[i])/np.arange(1,n-i)[:,None] + for i in range(n)]) + szn_medslopes = ma.median(szn_slopes, axis=0) + medslope = ma.median(szn_slopes, axis=None) + return SenSeasonalSlopesResult(szn_medslopes, medslope) + + +Ttest_1sampResult = namedtuple('Ttest_1sampResult', ('statistic', 'pvalue')) + + +def ttest_1samp(a, popmean, axis=0, alternative='two-sided'): + """ + Calculates the T-test for the mean of ONE group of scores. + + Parameters + ---------- + a : array_like + sample observation + popmean : float or array_like + expected value in null hypothesis, if array_like than it must have the + same shape as `a` excluding the axis dimension + axis : int or None, optional + Axis along which to compute test. If None, compute over the whole + array `a`. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the mean of the underlying distribution of the sample + is different than the given population mean (`popmean`) + * 'less': the mean of the underlying distribution of the sample is + less than the given population mean (`popmean`) + * 'greater': the mean of the underlying distribution of the sample is + greater than the given population mean (`popmean`) + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float or array + t-statistic + pvalue : float or array + The p-value + + Notes + ----- + For more details on `ttest_1samp`, see `scipy.stats.ttest_1samp`. + + """ + a, axis = _chk_asarray(a, axis) + if a.size == 0: + return (np.nan, np.nan) + + x = a.mean(axis=axis) + v = a.var(axis=axis, ddof=1) + n = a.count(axis=axis) + # force df to be an array for masked division not to throw a warning + df = ma.asanyarray(n - 1.0) + svar = ((n - 1.0) * v) / df + with np.errstate(divide='ignore', invalid='ignore'): + t = (x - popmean) / ma.sqrt(svar / n) + + t, prob = _ttest_finish(df, t, alternative) + return Ttest_1sampResult(t, prob) + + +ttest_onesamp = ttest_1samp + + +Ttest_indResult = namedtuple('Ttest_indResult', ('statistic', 'pvalue')) + + +def ttest_ind(a, b, axis=0, equal_var=True, alternative='two-sided'): + """ + Calculates the T-test for the means of TWO INDEPENDENT samples of scores. + + Parameters + ---------- + a, b : array_like + The arrays must have the same shape, except in the dimension + corresponding to `axis` (the first, by default). + axis : int or None, optional + Axis along which to compute test. If None, compute over the whole + arrays, `a`, and `b`. + equal_var : bool, optional + If True, perform a standard independent 2 sample test that assumes equal + population variances. + If False, perform Welch's t-test, which does not assume equal population + variance. + + .. versionadded:: 0.17.0 + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions underlying the samples + are unequal. + * 'less': the mean of the distribution underlying the first sample + is less than the mean of the distribution underlying the second + sample. + * 'greater': the mean of the distribution underlying the first + sample is greater than the mean of the distribution underlying + the second sample. + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float or array + The calculated t-statistic. + pvalue : float or array + The p-value. + + Notes + ----- + For more details on `ttest_ind`, see `scipy.stats.ttest_ind`. + + """ + a, b, axis = _chk2_asarray(a, b, axis) + + if a.size == 0 or b.size == 0: + return Ttest_indResult(np.nan, np.nan) + + (x1, x2) = (a.mean(axis), b.mean(axis)) + (v1, v2) = (a.var(axis=axis, ddof=1), b.var(axis=axis, ddof=1)) + (n1, n2) = (a.count(axis), b.count(axis)) + + if equal_var: + # force df to be an array for masked division not to throw a warning + df = ma.asanyarray(n1 + n2 - 2.0) + svar = ((n1-1)*v1+(n2-1)*v2) / df + denom = ma.sqrt(svar*(1.0/n1 + 1.0/n2)) # n-D computation here! + else: + vn1 = v1/n1 + vn2 = v2/n2 + with np.errstate(divide='ignore', invalid='ignore'): + df = (vn1 + vn2)**2 / (vn1**2 / (n1 - 1) + vn2**2 / (n2 - 1)) + + # If df is undefined, variances are zero. + # It doesn't matter what df is as long as it is not NaN. + df = np.where(np.isnan(df), 1, df) + denom = ma.sqrt(vn1 + vn2) + + with np.errstate(divide='ignore', invalid='ignore'): + t = (x1-x2) / denom + + t, prob = _ttest_finish(df, t, alternative) + return Ttest_indResult(t, prob) + + +Ttest_relResult = namedtuple('Ttest_relResult', ('statistic', 'pvalue')) + + +def ttest_rel(a, b, axis=0, alternative='two-sided'): + """ + Calculates the T-test on TWO RELATED samples of scores, a and b. + + Parameters + ---------- + a, b : array_like + The arrays must have the same shape. + axis : int or None, optional + Axis along which to compute test. If None, compute over the whole + arrays, `a`, and `b`. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions underlying the samples + are unequal. + * 'less': the mean of the distribution underlying the first sample + is less than the mean of the distribution underlying the second + sample. + * 'greater': the mean of the distribution underlying the first + sample is greater than the mean of the distribution underlying + the second sample. + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float or array + t-statistic + pvalue : float or array + two-tailed p-value + + Notes + ----- + For more details on `ttest_rel`, see `scipy.stats.ttest_rel`. + + """ + a, b, axis = _chk2_asarray(a, b, axis) + if len(a) != len(b): + raise ValueError('unequal length arrays') + + if a.size == 0 or b.size == 0: + return Ttest_relResult(np.nan, np.nan) + + n = a.count(axis) + df = ma.asanyarray(n-1.0) + d = (a-b).astype('d') + dm = d.mean(axis) + v = d.var(axis=axis, ddof=1) + denom = ma.sqrt(v / n) + with np.errstate(divide='ignore', invalid='ignore'): + t = dm / denom + + t, prob = _ttest_finish(df, t, alternative) + return Ttest_relResult(t, prob) + + +MannwhitneyuResult = namedtuple('MannwhitneyuResult', ('statistic', + 'pvalue')) + + +def mannwhitneyu(x,y, use_continuity=True): + """ + Computes the Mann-Whitney statistic + + Missing values in `x` and/or `y` are discarded. + + Parameters + ---------- + x : sequence + Input + y : sequence + Input + use_continuity : {True, False}, optional + Whether a continuity correction (1/2.) should be taken into account. + + Returns + ------- + statistic : float + The minimum of the Mann-Whitney statistics + pvalue : float + Approximate two-sided p-value assuming a normal distribution. + + """ + x = ma.asarray(x).compressed().view(ndarray) + y = ma.asarray(y).compressed().view(ndarray) + ranks = rankdata(np.concatenate([x,y])) + (nx, ny) = (len(x), len(y)) + nt = nx + ny + U = ranks[:nx].sum() - nx*(nx+1)/2. + U = max(U, nx*ny - U) + u = nx*ny - U + + mu = (nx*ny)/2. + sigsq = (nt**3 - nt)/12. + ties = count_tied_groups(ranks) + sigsq -= sum(v*(k**3-k) for (k,v) in ties.items())/12. + sigsq *= nx*ny/float(nt*(nt-1)) + + if use_continuity: + z = (U - 1/2. - mu) / ma.sqrt(sigsq) + else: + z = (U - mu) / ma.sqrt(sigsq) + + prob = special.erfc(abs(z)/np.sqrt(2)) + return MannwhitneyuResult(u, prob) + + +KruskalResult = namedtuple('KruskalResult', ('statistic', 'pvalue')) + + +def kruskal(*args): + """ + Compute the Kruskal-Wallis H-test for independent samples + + Parameters + ---------- + sample1, sample2, ... : array_like + Two or more arrays with the sample measurements can be given as + arguments. + + Returns + ------- + statistic : float + The Kruskal-Wallis H statistic, corrected for ties + pvalue : float + The p-value for the test using the assumption that H has a chi + square distribution + + Notes + ----- + For more details on `kruskal`, see `scipy.stats.kruskal`. + + Examples + -------- + >>> from scipy.stats.mstats import kruskal + + Random samples from three different brands of batteries were tested + to see how long the charge lasted. Results were as follows: + + >>> a = [6.3, 5.4, 5.7, 5.2, 5.0] + >>> b = [6.9, 7.0, 6.1, 7.9] + >>> c = [7.2, 6.9, 6.1, 6.5] + + Test the hypothesis that the distribution functions for all of the brands' + durations are identical. Use 5% level of significance. + + >>> kruskal(a, b, c) + KruskalResult(statistic=7.113812154696133, pvalue=0.028526948491942164) + + The null hypothesis is rejected at the 5% level of significance + because the returned p-value is less than the critical value of 5%. + + """ + output = argstoarray(*args) + ranks = ma.masked_equal(rankdata(output, use_missing=False), 0) + sumrk = ranks.sum(-1) + ngrp = ranks.count(-1) + ntot = ranks.count() + H = 12./(ntot*(ntot+1)) * (sumrk**2/ngrp).sum() - 3*(ntot+1) + # Tie correction + ties = count_tied_groups(ranks) + T = 1. - sum(v*(k**3-k) for (k,v) in ties.items())/float(ntot**3-ntot) + if T == 0: + raise ValueError('All numbers are identical in kruskal') + + H /= T + df = len(output) - 1 + prob = distributions.chi2.sf(H, df) + return KruskalResult(H, prob) + + +kruskalwallis = kruskal + + +@_rename_parameter("mode", "method") +def ks_1samp(x, cdf, args=(), alternative="two-sided", method='auto'): + """ + Computes the Kolmogorov-Smirnov test on one sample of masked values. + + Missing values in `x` are discarded. + + Parameters + ---------- + x : array_like + a 1-D array of observations of random variables. + cdf : str or callable + If a string, it should be the name of a distribution in `scipy.stats`. + If a callable, that callable is used to calculate the cdf. + args : tuple, sequence, optional + Distribution parameters, used if `cdf` is a string. + alternative : {'two-sided', 'less', 'greater'}, optional + Indicates the alternative hypothesis. Default is 'two-sided'. + method : {'auto', 'exact', 'asymp'}, optional + Defines the method used for calculating the p-value. + The following options are available (default is 'auto'): + + * 'auto' : use 'exact' for small size arrays, 'asymp' for large + * 'exact' : use approximation to exact distribution of test statistic + * 'asymp' : use asymptotic distribution of test statistic + + Returns + ------- + d : float + Value of the Kolmogorov Smirnov test + p : float + Corresponding p-value. + + """ + alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( + alternative.lower()[0], alternative) + return scipy.stats._stats_py.ks_1samp( + x, cdf, args=args, alternative=alternative, method=method) + + +@_rename_parameter("mode", "method") +def ks_2samp(data1, data2, alternative="two-sided", method='auto'): + """ + Computes the Kolmogorov-Smirnov test on two samples. + + Missing values in `x` and/or `y` are discarded. + + Parameters + ---------- + data1 : array_like + First data set + data2 : array_like + Second data set + alternative : {'two-sided', 'less', 'greater'}, optional + Indicates the alternative hypothesis. Default is 'two-sided'. + method : {'auto', 'exact', 'asymp'}, optional + Defines the method used for calculating the p-value. + The following options are available (default is 'auto'): + + * 'auto' : use 'exact' for small size arrays, 'asymp' for large + * 'exact' : use approximation to exact distribution of test statistic + * 'asymp' : use asymptotic distribution of test statistic + + Returns + ------- + d : float + Value of the Kolmogorov Smirnov test + p : float + Corresponding p-value. + + """ + # Ideally this would be accomplished by + # ks_2samp = scipy.stats._stats_py.ks_2samp + # but the circular dependencies between _mstats_basic and stats prevent that. + alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( + alternative.lower()[0], alternative) + return scipy.stats._stats_py.ks_2samp(data1, data2, + alternative=alternative, + method=method) + + +ks_twosamp = ks_2samp + + +@_rename_parameter("mode", "method") +def kstest(data1, data2, args=(), alternative='two-sided', method='auto'): + """ + + Parameters + ---------- + data1 : array_like + data2 : str, callable or array_like + args : tuple, sequence, optional + Distribution parameters, used if `data1` or `data2` are strings. + alternative : str, as documented in stats.kstest + method : str, as documented in stats.kstest + + Returns + ------- + tuple of (K-S statistic, probability) + + """ + return scipy.stats._stats_py.kstest(data1, data2, args, + alternative=alternative, method=method) + + +def trima(a, limits=None, inclusive=(True,True)): + """ + Trims an array by masking the data outside some given limits. + + Returns a masked version of the input array. + + Parameters + ---------- + a : array_like + Input array. + limits : {None, tuple}, optional + Tuple of (lower limit, upper limit) in absolute values. + Values of the input array lower (greater) than the lower (upper) limit + will be masked. A limit is None indicates an open interval. + inclusive : (bool, bool) tuple, optional + Tuple of (lower flag, upper flag), indicating whether values exactly + equal to the lower (upper) limit are allowed. + + Examples + -------- + >>> from scipy.stats.mstats import trima + >>> import numpy as np + + >>> a = np.arange(10) + + The interval is left-closed and right-open, i.e., `[2, 8)`. + Trim the array by keeping only values in the interval. + + >>> trima(a, limits=(2, 8), inclusive=(True, False)) + masked_array(data=[--, --, 2, 3, 4, 5, 6, 7, --, --], + mask=[ True, True, False, False, False, False, False, False, + True, True], + fill_value=999999) + + """ + a = ma.asarray(a) + a.unshare_mask() + if (limits is None) or (limits == (None, None)): + return a + + (lower_lim, upper_lim) = limits + (lower_in, upper_in) = inclusive + condition = False + if lower_lim is not None: + if lower_in: + condition |= (a < lower_lim) + else: + condition |= (a <= lower_lim) + + if upper_lim is not None: + if upper_in: + condition |= (a > upper_lim) + else: + condition |= (a >= upper_lim) + + a[condition.filled(True)] = masked + return a + + +def trimr(a, limits=None, inclusive=(True, True), axis=None): + """ + Trims an array by masking some proportion of the data on each end. + Returns a masked version of the input array. + + Parameters + ---------- + a : sequence + Input array. + limits : {None, tuple}, optional + Tuple of the percentages to cut on each side of the array, with respect + to the number of unmasked data, as floats between 0. and 1. + Noting n the number of unmasked data before trimming, the + (n*limits[0])th smallest data and the (n*limits[1])th largest data are + masked, and the total number of unmasked data after trimming is + n*(1.-sum(limits)). The value of one limit can be set to None to + indicate an open interval. + inclusive : {(True,True) tuple}, optional + Tuple of flags indicating whether the number of data being masked on + the left (right) end should be truncated (True) or rounded (False) to + integers. + axis : {None,int}, optional + Axis along which to trim. If None, the whole array is trimmed, but its + shape is maintained. + + """ + def _trimr1D(a, low_limit, up_limit, low_inclusive, up_inclusive): + n = a.count() + idx = a.argsort() + if low_limit: + if low_inclusive: + lowidx = int(low_limit*n) + else: + lowidx = int(np.round(low_limit*n)) + a[idx[:lowidx]] = masked + if up_limit is not None: + if up_inclusive: + upidx = n - int(n*up_limit) + else: + upidx = n - int(np.round(n*up_limit)) + a[idx[upidx:]] = masked + return a + + a = ma.asarray(a) + a.unshare_mask() + if limits is None: + return a + + # Check the limits + (lolim, uplim) = limits + errmsg = "The proportion to cut from the %s should be between 0. and 1." + if lolim is not None: + if lolim > 1. or lolim < 0: + raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) + if uplim is not None: + if uplim > 1. or uplim < 0: + raise ValueError(errmsg % 'end' + "(got %s)" % uplim) + + (loinc, upinc) = inclusive + + if axis is None: + shp = a.shape + return _trimr1D(a.ravel(),lolim,uplim,loinc,upinc).reshape(shp) + else: + return ma.apply_along_axis(_trimr1D, axis, a, lolim,uplim,loinc,upinc) + + +trimdoc = """ + Parameters + ---------- + a : sequence + Input array + limits : {None, tuple}, optional + If `relative` is False, tuple (lower limit, upper limit) in absolute values. + Values of the input array lower (greater) than the lower (upper) limit are + masked. + + If `relative` is True, tuple (lower percentage, upper percentage) to cut + on each side of the array, with respect to the number of unmasked data. + + Noting n the number of unmasked data before trimming, the (n*limits[0])th + smallest data and the (n*limits[1])th largest data are masked, and the + total number of unmasked data after trimming is n*(1.-sum(limits)) + In each case, the value of one limit can be set to None to indicate an + open interval. + + If limits is None, no trimming is performed + inclusive : {(bool, bool) tuple}, optional + If `relative` is False, tuple indicating whether values exactly equal + to the absolute limits are allowed. + If `relative` is True, tuple indicating whether the number of data + being masked on each side should be rounded (True) or truncated + (False). + relative : bool, optional + Whether to consider the limits as absolute values (False) or proportions + to cut (True). + axis : int, optional + Axis along which to trim. +""" + + +def trim(a, limits=None, inclusive=(True,True), relative=False, axis=None): + """ + Trims an array by masking the data outside some given limits. + + Returns a masked version of the input array. + + %s + + Examples + -------- + >>> from scipy.stats.mstats import trim + >>> z = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10] + >>> print(trim(z,(3,8))) + [-- -- 3 4 5 6 7 8 -- --] + >>> print(trim(z,(0.1,0.2),relative=True)) + [-- 2 3 4 5 6 7 8 -- --] + + """ + if relative: + return trimr(a, limits=limits, inclusive=inclusive, axis=axis) + else: + return trima(a, limits=limits, inclusive=inclusive) + + +if trim.__doc__: + trim.__doc__ = trim.__doc__ % trimdoc + + +def trimboth(data, proportiontocut=0.2, inclusive=(True,True), axis=None): + """ + Trims the smallest and largest data values. + + Trims the `data` by masking the ``int(proportiontocut * n)`` smallest and + ``int(proportiontocut * n)`` largest values of data along the given axis, + where n is the number of unmasked values before trimming. + + Parameters + ---------- + data : ndarray + Data to trim. + proportiontocut : float, optional + Percentage of trimming (as a float between 0 and 1). + If n is the number of unmasked values before trimming, the number of + values after trimming is ``(1 - 2*proportiontocut) * n``. + Default is 0.2. + inclusive : {(bool, bool) tuple}, optional + Tuple indicating whether the number of data being masked on each side + should be rounded (True) or truncated (False). + axis : int, optional + Axis along which to perform the trimming. + If None, the input array is first flattened. + + """ + return trimr(data, limits=(proportiontocut,proportiontocut), + inclusive=inclusive, axis=axis) + + +def trimtail(data, proportiontocut=0.2, tail='left', inclusive=(True,True), + axis=None): + """ + Trims the data by masking values from one tail. + + Parameters + ---------- + data : array_like + Data to trim. + proportiontocut : float, optional + Percentage of trimming. If n is the number of unmasked values + before trimming, the number of values after trimming is + ``(1 - proportiontocut) * n``. Default is 0.2. + tail : {'left','right'}, optional + If 'left' the `proportiontocut` lowest values will be masked. + If 'right' the `proportiontocut` highest values will be masked. + Default is 'left'. + inclusive : {(bool, bool) tuple}, optional + Tuple indicating whether the number of data being masked on each side + should be rounded (True) or truncated (False). Default is + (True, True). + axis : int, optional + Axis along which to perform the trimming. + If None, the input array is first flattened. Default is None. + + Returns + ------- + trimtail : ndarray + Returned array of same shape as `data` with masked tail values. + + """ + tail = str(tail).lower()[0] + if tail == 'l': + limits = (proportiontocut,None) + elif tail == 'r': + limits = (None, proportiontocut) + else: + raise TypeError("The tail argument should be in ('left','right')") + + return trimr(data, limits=limits, axis=axis, inclusive=inclusive) + + +trim1 = trimtail + + +def trimmed_mean(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, + axis=None): + """Returns the trimmed mean of the data along the given axis. + + %s + + """ + if (not isinstance(limits,tuple)) and isinstance(limits,float): + limits = (limits, limits) + if relative: + return trimr(a,limits=limits,inclusive=inclusive,axis=axis).mean(axis=axis) + else: + return trima(a,limits=limits,inclusive=inclusive).mean(axis=axis) + + +if trimmed_mean.__doc__: + trimmed_mean.__doc__ = trimmed_mean.__doc__ % trimdoc + + +def trimmed_var(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, + axis=None, ddof=0): + """Returns the trimmed variance of the data along the given axis. + + %s + ddof : {0,integer}, optional + Means Delta Degrees of Freedom. The denominator used during computations + is (n-ddof). DDOF=0 corresponds to a biased estimate, DDOF=1 to an un- + biased estimate of the variance. + + """ + if (not isinstance(limits,tuple)) and isinstance(limits,float): + limits = (limits, limits) + if relative: + out = trimr(a,limits=limits, inclusive=inclusive,axis=axis) + else: + out = trima(a,limits=limits,inclusive=inclusive) + + return out.var(axis=axis, ddof=ddof) + + +if trimmed_var.__doc__: + trimmed_var.__doc__ = trimmed_var.__doc__ % trimdoc + + +def trimmed_std(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, + axis=None, ddof=0): + """Returns the trimmed standard deviation of the data along the given axis. + + %s + ddof : {0,integer}, optional + Means Delta Degrees of Freedom. The denominator used during computations + is (n-ddof). DDOF=0 corresponds to a biased estimate, DDOF=1 to an un- + biased estimate of the variance. + + """ + if (not isinstance(limits,tuple)) and isinstance(limits,float): + limits = (limits, limits) + if relative: + out = trimr(a,limits=limits,inclusive=inclusive,axis=axis) + else: + out = trima(a,limits=limits,inclusive=inclusive) + return out.std(axis=axis,ddof=ddof) + + +if trimmed_std.__doc__: + trimmed_std.__doc__ = trimmed_std.__doc__ % trimdoc + + +def trimmed_stde(a, limits=(0.1,0.1), inclusive=(1,1), axis=None): + """ + Returns the standard error of the trimmed mean along the given axis. + + Parameters + ---------- + a : sequence + Input array + limits : {(0.1,0.1), tuple of float}, optional + tuple (lower percentage, upper percentage) to cut on each side of the + array, with respect to the number of unmasked data. + + If n is the number of unmasked data before trimming, the values + smaller than ``n * limits[0]`` and the values larger than + ``n * `limits[1]`` are masked, and the total number of unmasked + data after trimming is ``n * (1.-sum(limits))``. In each case, + the value of one limit can be set to None to indicate an open interval. + If `limits` is None, no trimming is performed. + inclusive : {(bool, bool) tuple} optional + Tuple indicating whether the number of data being masked on each side + should be rounded (True) or truncated (False). + axis : int, optional + Axis along which to trim. + + Returns + ------- + trimmed_stde : scalar or ndarray + + """ + def _trimmed_stde_1D(a, low_limit, up_limit, low_inclusive, up_inclusive): + "Returns the standard error of the trimmed mean for a 1D input data." + n = a.count() + idx = a.argsort() + if low_limit: + if low_inclusive: + lowidx = int(low_limit*n) + else: + lowidx = np.round(low_limit*n) + a[idx[:lowidx]] = masked + if up_limit is not None: + if up_inclusive: + upidx = n - int(n*up_limit) + else: + upidx = n - np.round(n*up_limit) + a[idx[upidx:]] = masked + a[idx[:lowidx]] = a[idx[lowidx]] + a[idx[upidx:]] = a[idx[upidx-1]] + winstd = a.std(ddof=1) + return winstd / ((1-low_limit-up_limit)*np.sqrt(len(a))) + + a = ma.array(a, copy=True, subok=True) + a.unshare_mask() + if limits is None: + return a.std(axis=axis,ddof=1)/ma.sqrt(a.count(axis)) + if (not isinstance(limits,tuple)) and isinstance(limits,float): + limits = (limits, limits) + + # Check the limits + (lolim, uplim) = limits + errmsg = "The proportion to cut from the %s should be between 0. and 1." + if lolim is not None: + if lolim > 1. or lolim < 0: + raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) + if uplim is not None: + if uplim > 1. or uplim < 0: + raise ValueError(errmsg % 'end' + "(got %s)" % uplim) + + (loinc, upinc) = inclusive + if (axis is None): + return _trimmed_stde_1D(a.ravel(),lolim,uplim,loinc,upinc) + else: + if a.ndim > 2: + raise ValueError("Array 'a' must be at most two dimensional, " + "but got a.ndim = %d" % a.ndim) + return ma.apply_along_axis(_trimmed_stde_1D, axis, a, + lolim,uplim,loinc,upinc) + + +def _mask_to_limits(a, limits, inclusive): + """Mask an array for values outside of given limits. + + This is primarily a utility function. + + Parameters + ---------- + a : array + limits : (float or None, float or None) + A tuple consisting of the (lower limit, upper limit). Values in the + input array less than the lower limit or greater than the upper limit + will be masked out. None implies no limit. + inclusive : (bool, bool) + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to lower or upper are allowed. + + Returns + ------- + A MaskedArray. + + Raises + ------ + A ValueError if there are no values within the given limits. + """ + lower_limit, upper_limit = limits + lower_include, upper_include = inclusive + am = ma.MaskedArray(a) + if lower_limit is not None: + if lower_include: + am = ma.masked_less(am, lower_limit) + else: + am = ma.masked_less_equal(am, lower_limit) + + if upper_limit is not None: + if upper_include: + am = ma.masked_greater(am, upper_limit) + else: + am = ma.masked_greater_equal(am, upper_limit) + + if am.count() == 0: + raise ValueError("No array values within given limits") + + return am + + +def tmean(a, limits=None, inclusive=(True, True), axis=None): + """ + Compute the trimmed mean. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None (default), then all + values are used. Either of the limit values in the tuple can also be + None representing a half-open interval. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. If None, compute over the + whole array. Default is None. + + Returns + ------- + tmean : float + + Notes + ----- + For more details on `tmean`, see `scipy.stats.tmean`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import mstats + >>> a = np.array([[6, 8, 3, 0], + ... [3, 9, 1, 2], + ... [8, 7, 8, 2], + ... [5, 6, 0, 2], + ... [4, 5, 5, 2]]) + ... + ... + >>> mstats.tmean(a, (2,5)) + 3.3 + >>> mstats.tmean(a, (2,5), axis=0) + masked_array(data=[4.0, 5.0, 4.0, 2.0], + mask=[False, False, False, False], + fill_value=1e+20) + + """ + return trima(a, limits=limits, inclusive=inclusive).mean(axis=axis) + + +def tvar(a, limits=None, inclusive=(True, True), axis=0, ddof=1): + """ + Compute the trimmed variance + + This function computes the sample variance of an array of values, + while ignoring values which are outside of given `limits`. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None, then all values are + used. Either of the limit values in the tuple can also be None + representing a half-open interval. The default value is None. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. If None, compute over the + whole array. Default is zero. + ddof : int, optional + Delta degrees of freedom. Default is 1. + + Returns + ------- + tvar : float + Trimmed variance. + + Notes + ----- + For more details on `tvar`, see `scipy.stats.tvar`. + + """ + a = a.astype(float).ravel() + if limits is None: + n = (~a.mask).sum() # todo: better way to do that? + return np.ma.var(a) * n/(n-1.) + am = _mask_to_limits(a, limits=limits, inclusive=inclusive) + + return np.ma.var(am, axis=axis, ddof=ddof) + + +def tmin(a, lowerlimit=None, axis=0, inclusive=True): + """ + Compute the trimmed minimum + + Parameters + ---------- + a : array_like + array of values + lowerlimit : None or float, optional + Values in the input array less than the given limit will be ignored. + When lowerlimit is None, then all values are used. The default value + is None. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + inclusive : {True, False}, optional + This flag determines whether values exactly equal to the lower limit + are included. The default value is True. + + Returns + ------- + tmin : float, int or ndarray + + Notes + ----- + For more details on `tmin`, see `scipy.stats.tmin`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import mstats + >>> a = np.array([[6, 8, 3, 0], + ... [3, 2, 1, 2], + ... [8, 1, 8, 2], + ... [5, 3, 0, 2], + ... [4, 7, 5, 2]]) + ... + >>> mstats.tmin(a, 5) + masked_array(data=[5, 7, 5, --], + mask=[False, False, False, True], + fill_value=999999) + + """ + a, axis = _chk_asarray(a, axis) + am = trima(a, (lowerlimit, None), (inclusive, False)) + return ma.minimum.reduce(am, axis) + + +def tmax(a, upperlimit=None, axis=0, inclusive=True): + """ + Compute the trimmed maximum + + This function computes the maximum value of an array along a given axis, + while ignoring values larger than a specified upper limit. + + Parameters + ---------- + a : array_like + array of values + upperlimit : None or float, optional + Values in the input array greater than the given limit will be ignored. + When upperlimit is None, then all values are used. The default value + is None. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + inclusive : {True, False}, optional + This flag determines whether values exactly equal to the upper limit + are included. The default value is True. + + Returns + ------- + tmax : float, int or ndarray + + Notes + ----- + For more details on `tmax`, see `scipy.stats.tmax`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import mstats + >>> a = np.array([[6, 8, 3, 0], + ... [3, 9, 1, 2], + ... [8, 7, 8, 2], + ... [5, 6, 0, 2], + ... [4, 5, 5, 2]]) + ... + ... + >>> mstats.tmax(a, 4) + masked_array(data=[4, --, 3, 2], + mask=[False, True, False, False], + fill_value=999999) + + """ + a, axis = _chk_asarray(a, axis) + am = trima(a, (None, upperlimit), (False, inclusive)) + return ma.maximum.reduce(am, axis) + + +def tsem(a, limits=None, inclusive=(True, True), axis=0, ddof=1): + """ + Compute the trimmed standard error of the mean. + + This function finds the standard error of the mean for given + values, ignoring values outside the given `limits`. + + Parameters + ---------- + a : array_like + array of values + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None, then all values are + used. Either of the limit values in the tuple can also be None + representing a half-open interval. The default value is None. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. If None, compute over the + whole array. Default is zero. + ddof : int, optional + Delta degrees of freedom. Default is 1. + + Returns + ------- + tsem : float + + Notes + ----- + For more details on `tsem`, see `scipy.stats.tsem`. + + """ + a = ma.asarray(a).ravel() + if limits is None: + n = float(a.count()) + return a.std(axis=axis, ddof=ddof)/ma.sqrt(n) + + am = trima(a.ravel(), limits, inclusive) + sd = np.sqrt(am.var(axis=axis, ddof=ddof)) + return sd / np.sqrt(am.count()) + + +def winsorize(a, limits=None, inclusive=(True, True), inplace=False, + axis=None, nan_policy='propagate'): + """Returns a Winsorized version of the input array. + + The (limits[0])th lowest values are set to the (limits[0])th percentile, + and the (limits[1])th highest values are set to the (1 - limits[1])th + percentile. + Masked values are skipped. + + + Parameters + ---------- + a : sequence + Input array. + limits : {None, tuple of float}, optional + Tuple of the percentages to cut on each side of the array, with respect + to the number of unmasked data, as floats between 0. and 1. + Noting n the number of unmasked data before trimming, the + (n*limits[0])th smallest data and the (n*limits[1])th largest data are + masked, and the total number of unmasked data after trimming + is n*(1.-sum(limits)) The value of one limit can be set to None to + indicate an open interval. + inclusive : {(True, True) tuple}, optional + Tuple indicating whether the number of data being masked on each side + should be truncated (True) or rounded (False). + inplace : {False, True}, optional + Whether to winsorize in place (True) or to use a copy (False) + axis : {None, int}, optional + Axis along which to trim. If None, the whole array is trimmed, but its + shape is maintained. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': allows nan values and may overwrite or propagate them + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Notes + ----- + This function is applied to reduce the effect of possibly spurious outliers + by limiting the extreme values. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.mstats import winsorize + + A shuffled array contains integers from 1 to 10. + + >>> a = np.array([10, 4, 9, 8, 5, 3, 7, 2, 1, 6]) + + The 10% of the lowest value (i.e., `1`) and the 20% of the highest + values (i.e., `9` and `10`) are replaced. + + >>> winsorize(a, limits=[0.1, 0.2]) + masked_array(data=[8, 4, 8, 8, 5, 3, 7, 2, 2, 6], + mask=False, + fill_value=999999) + + """ + def _winsorize1D(a, low_limit, up_limit, low_include, up_include, + contains_nan, nan_policy): + n = a.count() + idx = a.argsort() + if contains_nan: + nan_count = np.count_nonzero(np.isnan(a)) + if low_limit: + if low_include: + lowidx = int(low_limit * n) + else: + lowidx = np.round(low_limit * n).astype(int) + if contains_nan and nan_policy == 'omit': + lowidx = min(lowidx, n-nan_count-1) + a[idx[:lowidx]] = a[idx[lowidx]] + if up_limit is not None: + if up_include: + upidx = n - int(n * up_limit) + else: + upidx = n - np.round(n * up_limit).astype(int) + if contains_nan and nan_policy == 'omit': + a[idx[upidx:-nan_count]] = a[idx[upidx - 1]] + else: + a[idx[upidx:]] = a[idx[upidx - 1]] + return a + + contains_nan, nan_policy = _contains_nan(a, nan_policy) + # We are going to modify a: better make a copy + a = ma.array(a, copy=np.logical_not(inplace)) + + if limits is None: + return a + if (not isinstance(limits, tuple)) and isinstance(limits, float): + limits = (limits, limits) + + # Check the limits + (lolim, uplim) = limits + errmsg = "The proportion to cut from the %s should be between 0. and 1." + if lolim is not None: + if lolim > 1. or lolim < 0: + raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) + if uplim is not None: + if uplim > 1. or uplim < 0: + raise ValueError(errmsg % 'end' + "(got %s)" % uplim) + + (loinc, upinc) = inclusive + + if axis is None: + shp = a.shape + return _winsorize1D(a.ravel(), lolim, uplim, loinc, upinc, + contains_nan, nan_policy).reshape(shp) + else: + return ma.apply_along_axis(_winsorize1D, axis, a, lolim, uplim, loinc, + upinc, contains_nan, nan_policy) + + +def moment(a, moment=1, axis=0): + """ + Calculates the nth moment about the mean for a sample. + + Parameters + ---------- + a : array_like + data + moment : int, optional + order of central moment that is returned + axis : int or None, optional + Axis along which the central moment is computed. Default is 0. + If None, compute over the whole array `a`. + + Returns + ------- + n-th central moment : ndarray or float + The appropriate moment along the given axis or over all values if axis + is None. The denominator for the moment calculation is the number of + observations, no degrees of freedom correction is done. + + Notes + ----- + For more details about `moment`, see `scipy.stats.moment`. + + """ + a, axis = _chk_asarray(a, axis) + if a.size == 0: + moment_shape = list(a.shape) + del moment_shape[axis] + dtype = a.dtype.type if a.dtype.kind in 'fc' else np.float64 + # empty array, return nan(s) with shape matching `moment` + out_shape = (moment_shape if np.isscalar(moment) + else [len(moment)] + moment_shape) + if len(out_shape) == 0: + return dtype(np.nan) + else: + return ma.array(np.full(out_shape, np.nan, dtype=dtype)) + + # for array_like moment input, return a value for each. + if not np.isscalar(moment): + mean = a.mean(axis, keepdims=True) + mmnt = [_moment(a, i, axis, mean=mean) for i in moment] + return ma.array(mmnt) + else: + return _moment(a, moment, axis) + + +# Moment with optional pre-computed mean, equal to a.mean(axis, keepdims=True) +def _moment(a, moment, axis, *, mean=None): + if np.abs(moment - np.round(moment)) > 0: + raise ValueError("All moment parameters must be integers") + + if moment == 0 or moment == 1: + # By definition the zeroth moment about the mean is 1, and the first + # moment is 0. + shape = list(a.shape) + del shape[axis] + dtype = a.dtype.type if a.dtype.kind in 'fc' else np.float64 + + if len(shape) == 0: + return dtype(1.0 if moment == 0 else 0.0) + else: + return (ma.ones(shape, dtype=dtype) if moment == 0 + else ma.zeros(shape, dtype=dtype)) + else: + # Exponentiation by squares: form exponent sequence + n_list = [moment] + current_n = moment + while current_n > 2: + if current_n % 2: + current_n = (current_n-1)/2 + else: + current_n /= 2 + n_list.append(current_n) + + # Starting point for exponentiation by squares + mean = a.mean(axis, keepdims=True) if mean is None else mean + a_zero_mean = a - mean + if n_list[-1] == 1: + s = a_zero_mean.copy() + else: + s = a_zero_mean**2 + + # Perform multiplications + for n in n_list[-2::-1]: + s = s**2 + if n % 2: + s *= a_zero_mean + return s.mean(axis) + + +def variation(a, axis=0, ddof=0): + """ + Compute the coefficient of variation. + + The coefficient of variation is the standard deviation divided by the + mean. This function is equivalent to:: + + np.std(x, axis=axis, ddof=ddof) / np.mean(x) + + The default for ``ddof`` is 0, but many definitions of the coefficient + of variation use the square root of the unbiased sample variance + for the sample standard deviation, which corresponds to ``ddof=1``. + + Parameters + ---------- + a : array_like + Input array. + axis : int or None, optional + Axis along which to calculate the coefficient of variation. Default + is 0. If None, compute over the whole array `a`. + ddof : int, optional + Delta degrees of freedom. Default is 0. + + Returns + ------- + variation : ndarray + The calculated variation along the requested axis. + + Notes + ----- + For more details about `variation`, see `scipy.stats.variation`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.mstats import variation + >>> a = np.array([2,8,4]) + >>> variation(a) + 0.5345224838248487 + >>> b = np.array([2,8,3,4]) + >>> c = np.ma.masked_array(b, mask=[0,0,1,0]) + >>> variation(c) + 0.5345224838248487 + + In the example above, it can be seen that this works the same as + `scipy.stats.variation` except 'stats.mstats.variation' ignores masked + array elements. + + """ + a, axis = _chk_asarray(a, axis) + return a.std(axis, ddof=ddof)/a.mean(axis) + + +def skew(a, axis=0, bias=True): + """ + Computes the skewness of a data set. + + Parameters + ---------- + a : ndarray + data + axis : int or None, optional + Axis along which skewness is calculated. Default is 0. + If None, compute over the whole array `a`. + bias : bool, optional + If False, then the calculations are corrected for statistical bias. + + Returns + ------- + skewness : ndarray + The skewness of values along an axis, returning 0 where all values are + equal. + + Notes + ----- + For more details about `skew`, see `scipy.stats.skew`. + + """ + a, axis = _chk_asarray(a,axis) + mean = a.mean(axis, keepdims=True) + m2 = _moment(a, 2, axis, mean=mean) + m3 = _moment(a, 3, axis, mean=mean) + zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) + with np.errstate(all='ignore'): + vals = ma.where(zero, 0, m3 / m2**1.5) + + if not bias and zero is not ma.masked and m2 is not ma.masked: + n = a.count(axis) + can_correct = ~zero & (n > 2) + if can_correct.any(): + n = np.extract(can_correct, n) + m2 = np.extract(can_correct, m2) + m3 = np.extract(can_correct, m3) + nval = ma.sqrt((n-1.0)*n)/(n-2.0)*m3/m2**1.5 + np.place(vals, can_correct, nval) + return vals + + +def kurtosis(a, axis=0, fisher=True, bias=True): + """ + Computes the kurtosis (Fisher or Pearson) of a dataset. + + Kurtosis is the fourth central moment divided by the square of the + variance. If Fisher's definition is used, then 3.0 is subtracted from + the result to give 0.0 for a normal distribution. + + If bias is False then the kurtosis is calculated using k statistics to + eliminate bias coming from biased moment estimators + + Use `kurtosistest` to see if result is close enough to normal. + + Parameters + ---------- + a : array + data for which the kurtosis is calculated + axis : int or None, optional + Axis along which the kurtosis is calculated. Default is 0. + If None, compute over the whole array `a`. + fisher : bool, optional + If True, Fisher's definition is used (normal ==> 0.0). If False, + Pearson's definition is used (normal ==> 3.0). + bias : bool, optional + If False, then the calculations are corrected for statistical bias. + + Returns + ------- + kurtosis : array + The kurtosis of values along an axis. If all values are equal, + return -3 for Fisher's definition and 0 for Pearson's definition. + + Notes + ----- + For more details about `kurtosis`, see `scipy.stats.kurtosis`. + + """ + a, axis = _chk_asarray(a, axis) + mean = a.mean(axis, keepdims=True) + m2 = _moment(a, 2, axis, mean=mean) + m4 = _moment(a, 4, axis, mean=mean) + zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) + with np.errstate(all='ignore'): + vals = ma.where(zero, 0, m4 / m2**2.0) + + if not bias and zero is not ma.masked and m2 is not ma.masked: + n = a.count(axis) + can_correct = ~zero & (n > 3) + if can_correct.any(): + n = np.extract(can_correct, n) + m2 = np.extract(can_correct, m2) + m4 = np.extract(can_correct, m4) + nval = 1.0/(n-2)/(n-3)*((n*n-1.0)*m4/m2**2.0-3*(n-1)**2.0) + np.place(vals, can_correct, nval+3.0) + if fisher: + return vals - 3 + else: + return vals + + +DescribeResult = namedtuple('DescribeResult', ('nobs', 'minmax', 'mean', + 'variance', 'skewness', + 'kurtosis')) + + +def describe(a, axis=0, ddof=0, bias=True): + """ + Computes several descriptive statistics of the passed array. + + Parameters + ---------- + a : array_like + Data array + axis : int or None, optional + Axis along which to calculate statistics. Default 0. If None, + compute over the whole array `a`. + ddof : int, optional + degree of freedom (default 0); note that default ddof is different + from the same routine in stats.describe + bias : bool, optional + If False, then the skewness and kurtosis calculations are corrected for + statistical bias. + + Returns + ------- + nobs : int + (size of the data (discarding missing values) + + minmax : (int, int) + min, max + + mean : float + arithmetic mean + + variance : float + unbiased variance + + skewness : float + biased skewness + + kurtosis : float + biased kurtosis + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.mstats import describe + >>> ma = np.ma.array(range(6), mask=[0, 0, 0, 1, 1, 1]) + >>> describe(ma) + DescribeResult(nobs=3, minmax=(masked_array(data=0, + mask=False, + fill_value=999999), masked_array(data=2, + mask=False, + fill_value=999999)), mean=1.0, variance=0.6666666666666666, + skewness=masked_array(data=0., mask=False, fill_value=1e+20), + kurtosis=-1.5) + + """ + a, axis = _chk_asarray(a, axis) + n = a.count(axis) + mm = (ma.minimum.reduce(a, axis=axis), ma.maximum.reduce(a, axis=axis)) + m = a.mean(axis) + v = a.var(axis, ddof=ddof) + sk = skew(a, axis, bias=bias) + kurt = kurtosis(a, axis, bias=bias) + + return DescribeResult(n, mm, m, v, sk, kurt) + + +def stde_median(data, axis=None): + """Returns the McKean-Schrader estimate of the standard error of the sample + median along the given axis. masked values are discarded. + + Parameters + ---------- + data : ndarray + Data to trim. + axis : {None,int}, optional + Axis along which to perform the trimming. + If None, the input array is first flattened. + + """ + def _stdemed_1D(data): + data = np.sort(data.compressed()) + n = len(data) + z = 2.5758293035489004 + k = int(np.round((n+1)/2. - z * np.sqrt(n/4.),0)) + return ((data[n-k] - data[k-1])/(2.*z)) + + data = ma.array(data, copy=False, subok=True) + if (axis is None): + return _stdemed_1D(data) + else: + if data.ndim > 2: + raise ValueError("Array 'data' must be at most two dimensional, " + "but got data.ndim = %d" % data.ndim) + return ma.apply_along_axis(_stdemed_1D, axis, data) + + +SkewtestResult = namedtuple('SkewtestResult', ('statistic', 'pvalue')) + + +def skewtest(a, axis=0, alternative='two-sided'): + """ + Tests whether the skew is different from the normal distribution. + + Parameters + ---------- + a : array_like + The data to be tested + axis : int or None, optional + Axis along which statistics are calculated. Default is 0. + If None, compute over the whole array `a`. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the skewness of the distribution underlying the sample + is different from that of the normal distribution (i.e. 0) + * 'less': the skewness of the distribution underlying the sample + is less than that of the normal distribution + * 'greater': the skewness of the distribution underlying the sample + is greater than that of the normal distribution + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : array_like + The computed z-score for this test. + pvalue : array_like + A p-value for the hypothesis test + + Notes + ----- + For more details about `skewtest`, see `scipy.stats.skewtest`. + + """ + a, axis = _chk_asarray(a, axis) + if axis is None: + a = a.ravel() + axis = 0 + b2 = skew(a,axis) + n = a.count(axis) + if np.min(n) < 8: + raise ValueError( + "skewtest is not valid with less than 8 samples; %i samples" + " were given." % np.min(n)) + + y = b2 * ma.sqrt(((n+1)*(n+3)) / (6.0*(n-2))) + beta2 = (3.0*(n*n+27*n-70)*(n+1)*(n+3)) / ((n-2.0)*(n+5)*(n+7)*(n+9)) + W2 = -1 + ma.sqrt(2*(beta2-1)) + delta = 1/ma.sqrt(0.5*ma.log(W2)) + alpha = ma.sqrt(2.0/(W2-1)) + y = ma.where(y == 0, 1, y) + Z = delta*ma.log(y/alpha + ma.sqrt((y/alpha)**2+1)) + pvalue = scipy.stats._stats_py._get_pvalue(Z, distributions.norm, alternative) + + return SkewtestResult(Z[()], pvalue[()]) + + +KurtosistestResult = namedtuple('KurtosistestResult', ('statistic', 'pvalue')) + + +def kurtosistest(a, axis=0, alternative='two-sided'): + """ + Tests whether a dataset has normal kurtosis + + Parameters + ---------- + a : array_like + array of the sample data + axis : int or None, optional + Axis along which to compute test. Default is 0. If None, + compute over the whole array `a`. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the kurtosis of the distribution underlying the sample + is different from that of the normal distribution + * 'less': the kurtosis of the distribution underlying the sample + is less than that of the normal distribution + * 'greater': the kurtosis of the distribution underlying the sample + is greater than that of the normal distribution + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : array_like + The computed z-score for this test. + pvalue : array_like + The p-value for the hypothesis test + + Notes + ----- + For more details about `kurtosistest`, see `scipy.stats.kurtosistest`. + + """ + a, axis = _chk_asarray(a, axis) + n = a.count(axis=axis) + if np.min(n) < 5: + raise ValueError( + "kurtosistest requires at least 5 observations; %i observations" + " were given." % np.min(n)) + if np.min(n) < 20: + warnings.warn( + "kurtosistest only valid for n>=20 ... continuing anyway, n=%i" % np.min(n), + stacklevel=2, + ) + + b2 = kurtosis(a, axis, fisher=False) + E = 3.0*(n-1) / (n+1) + varb2 = 24.0*n*(n-2.)*(n-3) / ((n+1)*(n+1.)*(n+3)*(n+5)) + x = (b2-E)/ma.sqrt(varb2) + sqrtbeta1 = 6.0*(n*n-5*n+2)/((n+7)*(n+9)) * np.sqrt((6.0*(n+3)*(n+5)) / + (n*(n-2)*(n-3))) + A = 6.0 + 8.0/sqrtbeta1 * (2.0/sqrtbeta1 + np.sqrt(1+4.0/(sqrtbeta1**2))) + term1 = 1 - 2./(9.0*A) + denom = 1 + x*ma.sqrt(2/(A-4.0)) + if np.ma.isMaskedArray(denom): + # For multi-dimensional array input + denom[denom == 0.0] = masked + elif denom == 0.0: + denom = masked + + term2 = np.ma.where(denom > 0, ma.power((1-2.0/A)/denom, 1/3.0), + -ma.power(-(1-2.0/A)/denom, 1/3.0)) + Z = (term1 - term2) / np.sqrt(2/(9.0*A)) + pvalue = scipy.stats._stats_py._get_pvalue(Z, distributions.norm, alternative) + + return KurtosistestResult(Z[()], pvalue[()]) + + +NormaltestResult = namedtuple('NormaltestResult', ('statistic', 'pvalue')) + + +def normaltest(a, axis=0): + """ + Tests whether a sample differs from a normal distribution. + + Parameters + ---------- + a : array_like + The array containing the data to be tested. + axis : int or None, optional + Axis along which to compute test. Default is 0. If None, + compute over the whole array `a`. + + Returns + ------- + statistic : float or array + ``s^2 + k^2``, where ``s`` is the z-score returned by `skewtest` and + ``k`` is the z-score returned by `kurtosistest`. + pvalue : float or array + A 2-sided chi squared probability for the hypothesis test. + + Notes + ----- + For more details about `normaltest`, see `scipy.stats.normaltest`. + + """ + a, axis = _chk_asarray(a, axis) + s, _ = skewtest(a, axis) + k, _ = kurtosistest(a, axis) + k2 = s*s + k*k + + return NormaltestResult(k2, distributions.chi2.sf(k2, 2)) + + +def mquantiles(a, prob=list([.25,.5,.75]), alphap=.4, betap=.4, axis=None, + limit=()): + """ + Computes empirical quantiles for a data array. + + Samples quantile are defined by ``Q(p) = (1-gamma)*x[j] + gamma*x[j+1]``, + where ``x[j]`` is the j-th order statistic, and gamma is a function of + ``j = floor(n*p + m)``, ``m = alphap + p*(1 - alphap - betap)`` and + ``g = n*p + m - j``. + + Reinterpreting the above equations to compare to **R** lead to the + equation: ``p(k) = (k - alphap)/(n + 1 - alphap - betap)`` + + Typical values of (alphap,betap) are: + - (0,1) : ``p(k) = k/n`` : linear interpolation of cdf + (**R** type 4) + - (.5,.5) : ``p(k) = (k - 1/2.)/n`` : piecewise linear function + (**R** type 5) + - (0,0) : ``p(k) = k/(n+1)`` : + (**R** type 6) + - (1,1) : ``p(k) = (k-1)/(n-1)``: p(k) = mode[F(x[k])]. + (**R** type 7, **R** default) + - (1/3,1/3): ``p(k) = (k-1/3)/(n+1/3)``: Then p(k) ~ median[F(x[k])]. + The resulting quantile estimates are approximately median-unbiased + regardless of the distribution of x. + (**R** type 8) + - (3/8,3/8): ``p(k) = (k-3/8)/(n+1/4)``: Blom. + The resulting quantile estimates are approximately unbiased + if x is normally distributed + (**R** type 9) + - (.4,.4) : approximately quantile unbiased (Cunnane) + - (.35,.35): APL, used with PWM + + Parameters + ---------- + a : array_like + Input data, as a sequence or array of dimension at most 2. + prob : array_like, optional + List of quantiles to compute. + alphap : float, optional + Plotting positions parameter, default is 0.4. + betap : float, optional + Plotting positions parameter, default is 0.4. + axis : int, optional + Axis along which to perform the trimming. + If None (default), the input array is first flattened. + limit : tuple, optional + Tuple of (lower, upper) values. + Values of `a` outside this open interval are ignored. + + Returns + ------- + mquantiles : MaskedArray + An array containing the calculated quantiles. + + Notes + ----- + This formulation is very similar to **R** except the calculation of + ``m`` from ``alphap`` and ``betap``, where in **R** ``m`` is defined + with each type. + + References + ---------- + .. [1] *R* statistical software: https://www.r-project.org/ + .. [2] *R* ``quantile`` function: + http://stat.ethz.ch/R-manual/R-devel/library/stats/html/quantile.html + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.mstats import mquantiles + >>> a = np.array([6., 47., 49., 15., 42., 41., 7., 39., 43., 40., 36.]) + >>> mquantiles(a) + array([ 19.2, 40. , 42.8]) + + Using a 2D array, specifying axis and limit. + + >>> data = np.array([[ 6., 7., 1.], + ... [ 47., 15., 2.], + ... [ 49., 36., 3.], + ... [ 15., 39., 4.], + ... [ 42., 40., -999.], + ... [ 41., 41., -999.], + ... [ 7., -999., -999.], + ... [ 39., -999., -999.], + ... [ 43., -999., -999.], + ... [ 40., -999., -999.], + ... [ 36., -999., -999.]]) + >>> print(mquantiles(data, axis=0, limit=(0, 50))) + [[19.2 14.6 1.45] + [40. 37.5 2.5 ] + [42.8 40.05 3.55]] + + >>> data[:, 2] = -999. + >>> print(mquantiles(data, axis=0, limit=(0, 50))) + [[19.200000000000003 14.6 --] + [40.0 37.5 --] + [42.800000000000004 40.05 --]] + + """ + def _quantiles1D(data,m,p): + x = np.sort(data.compressed()) + n = len(x) + if n == 0: + return ma.array(np.empty(len(p), dtype=float), mask=True) + elif n == 1: + return ma.array(np.resize(x, p.shape), mask=nomask) + aleph = (n*p + m) + k = np.floor(aleph.clip(1, n-1)).astype(int) + gamma = (aleph-k).clip(0,1) + return (1.-gamma)*x[(k-1).tolist()] + gamma*x[k.tolist()] + + data = ma.array(a, copy=False) + if data.ndim > 2: + raise TypeError("Array should be 2D at most !") + + if limit: + condition = (limit[0] < data) & (data < limit[1]) + data[~condition.filled(True)] = masked + + p = np.atleast_1d(np.asarray(prob)) + m = alphap + p*(1.-alphap-betap) + # Computes quantiles along axis (or globally) + if (axis is None): + return _quantiles1D(data, m, p) + + return ma.apply_along_axis(_quantiles1D, axis, data, m, p) + + +def scoreatpercentile(data, per, limit=(), alphap=.4, betap=.4): + """Calculate the score at the given 'per' percentile of the + sequence a. For example, the score at per=50 is the median. + + This function is a shortcut to mquantile + + """ + if (per < 0) or (per > 100.): + raise ValueError("The percentile should be between 0. and 100. !" + " (got %s)" % per) + + return mquantiles(data, prob=[per/100.], alphap=alphap, betap=betap, + limit=limit, axis=0).squeeze() + + +def plotting_positions(data, alpha=0.4, beta=0.4): + """ + Returns plotting positions (or empirical percentile points) for the data. + + Plotting positions are defined as ``(i-alpha)/(n+1-alpha-beta)``, where: + - i is the rank order statistics + - n is the number of unmasked values along the given axis + - `alpha` and `beta` are two parameters. + + Typical values for `alpha` and `beta` are: + - (0,1) : ``p(k) = k/n``, linear interpolation of cdf (R, type 4) + - (.5,.5) : ``p(k) = (k-1/2.)/n``, piecewise linear function + (R, type 5) + - (0,0) : ``p(k) = k/(n+1)``, Weibull (R type 6) + - (1,1) : ``p(k) = (k-1)/(n-1)``, in this case, + ``p(k) = mode[F(x[k])]``. That's R default (R type 7) + - (1/3,1/3): ``p(k) = (k-1/3)/(n+1/3)``, then + ``p(k) ~ median[F(x[k])]``. + The resulting quantile estimates are approximately median-unbiased + regardless of the distribution of x. (R type 8) + - (3/8,3/8): ``p(k) = (k-3/8)/(n+1/4)``, Blom. + The resulting quantile estimates are approximately unbiased + if x is normally distributed (R type 9) + - (.4,.4) : approximately quantile unbiased (Cunnane) + - (.35,.35): APL, used with PWM + - (.3175, .3175): used in scipy.stats.probplot + + Parameters + ---------- + data : array_like + Input data, as a sequence or array of dimension at most 2. + alpha : float, optional + Plotting positions parameter. Default is 0.4. + beta : float, optional + Plotting positions parameter. Default is 0.4. + + Returns + ------- + positions : MaskedArray + The calculated plotting positions. + + """ + data = ma.array(data, copy=False).reshape(1,-1) + n = data.count() + plpos = np.empty(data.size, dtype=float) + plpos[n:] = 0 + plpos[data.argsort(axis=None)[:n]] = ((np.arange(1, n+1) - alpha) / + (n + 1.0 - alpha - beta)) + return ma.array(plpos, mask=data._mask) + + +meppf = plotting_positions + + +def obrientransform(*args): + """ + Computes a transform on input data (any number of columns). Used to + test for homogeneity of variance prior to running one-way stats. Each + array in ``*args`` is one level of a factor. If an `f_oneway()` run on + the transformed data and found significant, variances are unequal. From + Maxwell and Delaney, p.112. + + Returns: transformed data for use in an ANOVA + """ + data = argstoarray(*args).T + v = data.var(axis=0,ddof=1) + m = data.mean(0) + n = data.count(0).astype(float) + # result = ((N-1.5)*N*(a-m)**2 - 0.5*v*(n-1))/((n-1)*(n-2)) + data -= m + data **= 2 + data *= (n-1.5)*n + data -= 0.5*v*(n-1) + data /= (n-1.)*(n-2.) + if not ma.allclose(v,data.mean(0)): + raise ValueError("Lack of convergence in obrientransform.") + + return data + + +def sem(a, axis=0, ddof=1): + """ + Calculates the standard error of the mean of the input array. + + Also sometimes called standard error of measurement. + + Parameters + ---------- + a : array_like + An array containing the values for which the standard error is + returned. + axis : int or None, optional + If axis is None, ravel `a` first. If axis is an integer, this will be + the axis over which to operate. Defaults to 0. + ddof : int, optional + Delta degrees-of-freedom. How many degrees of freedom to adjust + for bias in limited samples relative to the population estimate + of variance. Defaults to 1. + + Returns + ------- + s : ndarray or float + The standard error of the mean in the sample(s), along the input axis. + + Notes + ----- + The default value for `ddof` changed in scipy 0.15.0 to be consistent with + `scipy.stats.sem` as well as with the most common definition used (like in + the R documentation). + + Examples + -------- + Find standard error along the first axis: + + >>> import numpy as np + >>> from scipy import stats + >>> a = np.arange(20).reshape(5,4) + >>> print(stats.mstats.sem(a)) + [2.8284271247461903 2.8284271247461903 2.8284271247461903 + 2.8284271247461903] + + Find standard error across the whole array, using n degrees of freedom: + + >>> print(stats.mstats.sem(a, axis=None, ddof=0)) + 1.2893796958227628 + + """ + a, axis = _chk_asarray(a, axis) + n = a.count(axis=axis) + s = a.std(axis=axis, ddof=ddof) / ma.sqrt(n) + return s + + +F_onewayResult = namedtuple('F_onewayResult', ('statistic', 'pvalue')) + + +def f_oneway(*args): + """ + Performs a 1-way ANOVA, returning an F-value and probability given + any number of groups. From Heiman, pp.394-7. + + Usage: ``f_oneway(*args)``, where ``*args`` is 2 or more arrays, + one per treatment group. + + Returns + ------- + statistic : float + The computed F-value of the test. + pvalue : float + The associated p-value from the F-distribution. + + """ + # Construct a single array of arguments: each row is a group + data = argstoarray(*args) + ngroups = len(data) + ntot = data.count() + sstot = (data**2).sum() - (data.sum())**2/float(ntot) + ssbg = (data.count(-1) * (data.mean(-1)-data.mean())**2).sum() + sswg = sstot-ssbg + dfbg = ngroups-1 + dfwg = ntot - ngroups + msb = ssbg/float(dfbg) + msw = sswg/float(dfwg) + f = msb/msw + prob = special.fdtrc(dfbg, dfwg, f) # equivalent to stats.f.sf + + return F_onewayResult(f, prob) + + +FriedmanchisquareResult = namedtuple('FriedmanchisquareResult', + ('statistic', 'pvalue')) + + +def friedmanchisquare(*args): + """Friedman Chi-Square is a non-parametric, one-way within-subjects ANOVA. + This function calculates the Friedman Chi-square test for repeated measures + and returns the result, along with the associated probability value. + + Each input is considered a given group. Ideally, the number of treatments + among each group should be equal. If this is not the case, only the first + n treatments are taken into account, where n is the number of treatments + of the smallest group. + If a group has some missing values, the corresponding treatments are masked + in the other groups. + The test statistic is corrected for ties. + + Masked values in one group are propagated to the other groups. + + Returns + ------- + statistic : float + the test statistic. + pvalue : float + the associated p-value. + + """ + data = argstoarray(*args).astype(float) + k = len(data) + if k < 3: + raise ValueError("Less than 3 groups (%i): " % k + + "the Friedman test is NOT appropriate.") + + ranked = ma.masked_values(rankdata(data, axis=0), 0) + if ranked._mask is not nomask: + ranked = ma.mask_cols(ranked) + ranked = ranked.compressed().reshape(k,-1).view(ndarray) + else: + ranked = ranked._data + (k,n) = ranked.shape + # Ties correction + repeats = [find_repeats(row) for row in ranked.T] + ties = np.array([y for x, y in repeats if x.size > 0]) + tie_correction = 1 - (ties**3-ties).sum()/float(n*(k**3-k)) + + ssbg = np.sum((ranked.sum(-1) - n*(k+1)/2.)**2) + chisq = ssbg * 12./(n*k*(k+1)) * 1./tie_correction + + return FriedmanchisquareResult(chisq, + distributions.chi2.sf(chisq, k-1)) + + +BrunnerMunzelResult = namedtuple('BrunnerMunzelResult', ('statistic', 'pvalue')) + + +def brunnermunzel(x, y, alternative="two-sided", distribution="t"): + """ + Compute the Brunner-Munzel test on samples x and y. + + Any missing values in `x` and/or `y` are discarded. + + The Brunner-Munzel test is a nonparametric test of the null hypothesis that + when values are taken one by one from each group, the probabilities of + getting large values in both groups are equal. + Unlike the Wilcoxon-Mann-Whitney's U test, this does not require the + assumption of equivariance of two groups. Note that this does not assume + the distributions are same. This test works on two independent samples, + which may have different sizes. + + Parameters + ---------- + x, y : array_like + Array of samples, should be one-dimensional. + alternative : 'less', 'two-sided', or 'greater', optional + Whether to get the p-value for the one-sided hypothesis ('less' + or 'greater') or for the two-sided hypothesis ('two-sided'). + Defaults value is 'two-sided' . + distribution : 't' or 'normal', optional + Whether to get the p-value by t-distribution or by standard normal + distribution. + Defaults value is 't' . + + Returns + ------- + statistic : float + The Brunner-Munzer W statistic. + pvalue : float + p-value assuming an t distribution. One-sided or + two-sided, depending on the choice of `alternative` and `distribution`. + + See Also + -------- + mannwhitneyu : Mann-Whitney rank test on two samples. + + Notes + ----- + For more details on `brunnermunzel`, see `scipy.stats.brunnermunzel`. + + Examples + -------- + >>> from scipy.stats.mstats import brunnermunzel + >>> import numpy as np + >>> x1 = [1, 2, np.nan, np.nan, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1] + >>> x2 = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4] + >>> brunnermunzel(x1, x2) + BrunnerMunzelResult(statistic=1.4723186918922935, pvalue=0.15479415300426624) # may vary + + """ # noqa: E501 + x = ma.asarray(x).compressed().view(ndarray) + y = ma.asarray(y).compressed().view(ndarray) + nx = len(x) + ny = len(y) + if nx == 0 or ny == 0: + return BrunnerMunzelResult(np.nan, np.nan) + rankc = rankdata(np.concatenate((x,y))) + rankcx = rankc[0:nx] + rankcy = rankc[nx:nx+ny] + rankcx_mean = np.mean(rankcx) + rankcy_mean = np.mean(rankcy) + rankx = rankdata(x) + ranky = rankdata(y) + rankx_mean = np.mean(rankx) + ranky_mean = np.mean(ranky) + + Sx = np.sum(np.power(rankcx - rankx - rankcx_mean + rankx_mean, 2.0)) + Sx /= nx - 1 + Sy = np.sum(np.power(rankcy - ranky - rankcy_mean + ranky_mean, 2.0)) + Sy /= ny - 1 + + wbfn = nx * ny * (rankcy_mean - rankcx_mean) + wbfn /= (nx + ny) * np.sqrt(nx * Sx + ny * Sy) + + if distribution == "t": + df_numer = np.power(nx * Sx + ny * Sy, 2.0) + df_denom = np.power(nx * Sx, 2.0) / (nx - 1) + df_denom += np.power(ny * Sy, 2.0) / (ny - 1) + df = df_numer / df_denom + p = distributions.t.cdf(wbfn, df) + elif distribution == "normal": + p = distributions.norm.cdf(wbfn) + else: + raise ValueError( + "distribution should be 't' or 'normal'") + + if alternative == "greater": + pass + elif alternative == "less": + p = 1 - p + elif alternative == "two-sided": + p = 2 * np.min([p, 1-p]) + else: + raise ValueError( + "alternative should be 'less', 'greater' or 'two-sided'") + + return BrunnerMunzelResult(wbfn, p) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_extras.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_extras.py new file mode 100644 index 0000000000000000000000000000000000000000..f711e28dd36d0f190e49d6411deebb43a3884b27 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_mstats_extras.py @@ -0,0 +1,521 @@ +""" +Additional statistics functions with support for masked arrays. + +""" + +# Original author (2007): Pierre GF Gerard-Marchant + + +__all__ = ['compare_medians_ms', + 'hdquantiles', 'hdmedian', 'hdquantiles_sd', + 'idealfourths', + 'median_cihs','mjci','mquantiles_cimj', + 'rsh', + 'trimmed_mean_ci',] + + +import numpy as np +from numpy import float64, ndarray + +import numpy.ma as ma +from numpy.ma import MaskedArray + +from . import _mstats_basic as mstats + +from scipy.stats.distributions import norm, beta, t, binom + + +def hdquantiles(data, prob=list([.25,.5,.75]), axis=None, var=False,): + """ + Computes quantile estimates with the Harrell-Davis method. + + The quantile estimates are calculated as a weighted linear combination + of order statistics. + + Parameters + ---------- + data : array_like + Data array. + prob : sequence, optional + Sequence of probabilities at which to compute the quantiles. + axis : int or None, optional + Axis along which to compute the quantiles. If None, use a flattened + array. + var : bool, optional + Whether to return the variance of the estimate. + + Returns + ------- + hdquantiles : MaskedArray + A (p,) array of quantiles (if `var` is False), or a (2,p) array of + quantiles and variances (if `var` is True), where ``p`` is the + number of quantiles. + + See Also + -------- + hdquantiles_sd + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.mstats import hdquantiles + >>> + >>> # Sample data + >>> data = np.array([1.2, 2.5, 3.7, 4.0, 5.1, 6.3, 7.0, 8.2, 9.4]) + >>> + >>> # Probabilities at which to compute quantiles + >>> probabilities = [0.25, 0.5, 0.75] + >>> + >>> # Compute Harrell-Davis quantile estimates + >>> quantile_estimates = hdquantiles(data, prob=probabilities) + >>> + >>> # Display the quantile estimates + >>> for i, quantile in enumerate(probabilities): + ... print(f"{int(quantile * 100)}th percentile: {quantile_estimates[i]}") + 25th percentile: 3.1505820231763066 # may vary + 50th percentile: 5.194344084883956 + 75th percentile: 7.430626414674935 + + """ + def _hd_1D(data,prob,var): + "Computes the HD quantiles for a 1D array. Returns nan for invalid data." + xsorted = np.squeeze(np.sort(data.compressed().view(ndarray))) + # Don't use length here, in case we have a numpy scalar + n = xsorted.size + + hd = np.empty((2,len(prob)), float64) + if n < 2: + hd.flat = np.nan + if var: + return hd + return hd[0] + + v = np.arange(n+1) / float(n) + betacdf = beta.cdf + for (i,p) in enumerate(prob): + _w = betacdf(v, (n+1)*p, (n+1)*(1-p)) + w = _w[1:] - _w[:-1] + hd_mean = np.dot(w, xsorted) + hd[0,i] = hd_mean + # + hd[1,i] = np.dot(w, (xsorted-hd_mean)**2) + # + hd[0, prob == 0] = xsorted[0] + hd[0, prob == 1] = xsorted[-1] + if var: + hd[1, prob == 0] = hd[1, prob == 1] = np.nan + return hd + return hd[0] + # Initialization & checks + data = ma.array(data, copy=False, dtype=float64) + p = np.atleast_1d(np.asarray(prob)) + # Computes quantiles along axis (or globally) + if (axis is None) or (data.ndim == 1): + result = _hd_1D(data, p, var) + else: + if data.ndim > 2: + raise ValueError("Array 'data' must be at most two dimensional, " + "but got data.ndim = %d" % data.ndim) + result = ma.apply_along_axis(_hd_1D, axis, data, p, var) + + return ma.fix_invalid(result, copy=False) + + +def hdmedian(data, axis=-1, var=False): + """ + Returns the Harrell-Davis estimate of the median along the given axis. + + Parameters + ---------- + data : ndarray + Data array. + axis : int, optional + Axis along which to compute the quantiles. If None, use a flattened + array. + var : bool, optional + Whether to return the variance of the estimate. + + Returns + ------- + hdmedian : MaskedArray + The median values. If ``var=True``, the variance is returned inside + the masked array. E.g. for a 1-D array the shape change from (1,) to + (2,). + + """ + result = hdquantiles(data,[0.5], axis=axis, var=var) + return result.squeeze() + + +def hdquantiles_sd(data, prob=list([.25,.5,.75]), axis=None): + """ + The standard error of the Harrell-Davis quantile estimates by jackknife. + + Parameters + ---------- + data : array_like + Data array. + prob : sequence, optional + Sequence of quantiles to compute. + axis : int, optional + Axis along which to compute the quantiles. If None, use a flattened + array. + + Returns + ------- + hdquantiles_sd : MaskedArray + Standard error of the Harrell-Davis quantile estimates. + + See Also + -------- + hdquantiles + + """ + def _hdsd_1D(data, prob): + "Computes the std error for 1D arrays." + xsorted = np.sort(data.compressed()) + n = len(xsorted) + + hdsd = np.empty(len(prob), float64) + if n < 2: + hdsd.flat = np.nan + + vv = np.arange(n) / float(n-1) + betacdf = beta.cdf + + for (i,p) in enumerate(prob): + _w = betacdf(vv, n*p, n*(1-p)) + w = _w[1:] - _w[:-1] + # cumulative sum of weights and data points if + # ith point is left out for jackknife + mx_ = np.zeros_like(xsorted) + mx_[1:] = np.cumsum(w * xsorted[:-1]) + # similar but from the right + mx_[:-1] += np.cumsum(w[::-1] * xsorted[:0:-1])[::-1] + hdsd[i] = np.sqrt(mx_.var() * (n - 1)) + return hdsd + + # Initialization & checks + data = ma.array(data, copy=False, dtype=float64) + p = np.atleast_1d(np.asarray(prob)) + # Computes quantiles along axis (or globally) + if (axis is None): + result = _hdsd_1D(data, p) + else: + if data.ndim > 2: + raise ValueError("Array 'data' must be at most two dimensional, " + "but got data.ndim = %d" % data.ndim) + result = ma.apply_along_axis(_hdsd_1D, axis, data, p) + + return ma.fix_invalid(result, copy=False).ravel() + + +def trimmed_mean_ci(data, limits=(0.2,0.2), inclusive=(True,True), + alpha=0.05, axis=None): + """ + Selected confidence interval of the trimmed mean along the given axis. + + Parameters + ---------- + data : array_like + Input data. + limits : {None, tuple}, optional + None or a two item tuple. + Tuple of the percentages to cut on each side of the array, with respect + to the number of unmasked data, as floats between 0. and 1. If ``n`` + is the number of unmasked data before trimming, then + (``n * limits[0]``)th smallest data and (``n * limits[1]``)th + largest data are masked. The total number of unmasked data after + trimming is ``n * (1. - sum(limits))``. + The value of one limit can be set to None to indicate an open interval. + + Defaults to (0.2, 0.2). + inclusive : (2,) tuple of boolean, optional + If relative==False, tuple indicating whether values exactly equal to + the absolute limits are allowed. + If relative==True, tuple indicating whether the number of data being + masked on each side should be rounded (True) or truncated (False). + + Defaults to (True, True). + alpha : float, optional + Confidence level of the intervals. + + Defaults to 0.05. + axis : int, optional + Axis along which to cut. If None, uses a flattened version of `data`. + + Defaults to None. + + Returns + ------- + trimmed_mean_ci : (2,) ndarray + The lower and upper confidence intervals of the trimmed data. + + """ + data = ma.array(data, copy=False) + trimmed = mstats.trimr(data, limits=limits, inclusive=inclusive, axis=axis) + tmean = trimmed.mean(axis) + tstde = mstats.trimmed_stde(data,limits=limits,inclusive=inclusive,axis=axis) + df = trimmed.count(axis) - 1 + tppf = t.ppf(1-alpha/2.,df) + return np.array((tmean - tppf*tstde, tmean+tppf*tstde)) + + +def mjci(data, prob=[0.25,0.5,0.75], axis=None): + """ + Returns the Maritz-Jarrett estimators of the standard error of selected + experimental quantiles of the data. + + Parameters + ---------- + data : ndarray + Data array. + prob : sequence, optional + Sequence of quantiles to compute. + axis : int or None, optional + Axis along which to compute the quantiles. If None, use a flattened + array. + + """ + def _mjci_1D(data, p): + data = np.sort(data.compressed()) + n = data.size + prob = (np.array(p) * n + 0.5).astype(int) + betacdf = beta.cdf + + mj = np.empty(len(prob), float64) + x = np.arange(1,n+1, dtype=float64) / n + y = x - 1./n + for (i,m) in enumerate(prob): + W = betacdf(x,m-1,n-m) - betacdf(y,m-1,n-m) + C1 = np.dot(W,data) + C2 = np.dot(W,data**2) + mj[i] = np.sqrt(C2 - C1**2) + return mj + + data = ma.array(data, copy=False) + if data.ndim > 2: + raise ValueError("Array 'data' must be at most two dimensional, " + "but got data.ndim = %d" % data.ndim) + + p = np.atleast_1d(np.asarray(prob)) + # Computes quantiles along axis (or globally) + if (axis is None): + return _mjci_1D(data, p) + else: + return ma.apply_along_axis(_mjci_1D, axis, data, p) + + +def mquantiles_cimj(data, prob=[0.25,0.50,0.75], alpha=0.05, axis=None): + """ + Computes the alpha confidence interval for the selected quantiles of the + data, with Maritz-Jarrett estimators. + + Parameters + ---------- + data : ndarray + Data array. + prob : sequence, optional + Sequence of quantiles to compute. + alpha : float, optional + Confidence level of the intervals. + axis : int or None, optional + Axis along which to compute the quantiles. + If None, use a flattened array. + + Returns + ------- + ci_lower : ndarray + The lower boundaries of the confidence interval. Of the same length as + `prob`. + ci_upper : ndarray + The upper boundaries of the confidence interval. Of the same length as + `prob`. + + """ + alpha = min(alpha, 1 - alpha) + z = norm.ppf(1 - alpha/2.) + xq = mstats.mquantiles(data, prob, alphap=0, betap=0, axis=axis) + smj = mjci(data, prob, axis=axis) + return (xq - z * smj, xq + z * smj) + + +def median_cihs(data, alpha=0.05, axis=None): + """ + Computes the alpha-level confidence interval for the median of the data. + + Uses the Hettmasperger-Sheather method. + + Parameters + ---------- + data : array_like + Input data. Masked values are discarded. The input should be 1D only, + or `axis` should be set to None. + alpha : float, optional + Confidence level of the intervals. + axis : int or None, optional + Axis along which to compute the quantiles. If None, use a flattened + array. + + Returns + ------- + median_cihs + Alpha level confidence interval. + + """ + def _cihs_1D(data, alpha): + data = np.sort(data.compressed()) + n = len(data) + alpha = min(alpha, 1-alpha) + k = int(binom._ppf(alpha/2., n, 0.5)) + gk = binom.cdf(n-k,n,0.5) - binom.cdf(k-1,n,0.5) + if gk < 1-alpha: + k -= 1 + gk = binom.cdf(n-k,n,0.5) - binom.cdf(k-1,n,0.5) + gkk = binom.cdf(n-k-1,n,0.5) - binom.cdf(k,n,0.5) + I = (gk - 1 + alpha)/(gk - gkk) + lambd = (n-k) * I / float(k + (n-2*k)*I) + lims = (lambd*data[k] + (1-lambd)*data[k-1], + lambd*data[n-k-1] + (1-lambd)*data[n-k]) + return lims + data = ma.array(data, copy=False) + # Computes quantiles along axis (or globally) + if (axis is None): + result = _cihs_1D(data, alpha) + else: + if data.ndim > 2: + raise ValueError("Array 'data' must be at most two dimensional, " + "but got data.ndim = %d" % data.ndim) + result = ma.apply_along_axis(_cihs_1D, axis, data, alpha) + + return result + + +def compare_medians_ms(group_1, group_2, axis=None): + """ + Compares the medians from two independent groups along the given axis. + + The comparison is performed using the McKean-Schrader estimate of the + standard error of the medians. + + Parameters + ---------- + group_1 : array_like + First dataset. Has to be of size >=7. + group_2 : array_like + Second dataset. Has to be of size >=7. + axis : int, optional + Axis along which the medians are estimated. If None, the arrays are + flattened. If `axis` is not None, then `group_1` and `group_2` + should have the same shape. + + Returns + ------- + compare_medians_ms : {float, ndarray} + If `axis` is None, then returns a float, otherwise returns a 1-D + ndarray of floats with a length equal to the length of `group_1` + along `axis`. + + Examples + -------- + + >>> from scipy import stats + >>> a = [1, 2, 3, 4, 5, 6, 7] + >>> b = [8, 9, 10, 11, 12, 13, 14] + >>> stats.mstats.compare_medians_ms(a, b, axis=None) + 1.0693225866553746e-05 + + The function is vectorized to compute along a given axis. + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> x = rng.random(size=(3, 7)) + >>> y = rng.random(size=(3, 8)) + >>> stats.mstats.compare_medians_ms(x, y, axis=1) + array([0.36908985, 0.36092538, 0.2765313 ]) + + References + ---------- + .. [1] McKean, Joseph W., and Ronald M. Schrader. "A comparison of methods + for studentizing the sample median." Communications in + Statistics-Simulation and Computation 13.6 (1984): 751-773. + + """ + (med_1, med_2) = (ma.median(group_1,axis=axis), ma.median(group_2,axis=axis)) + (std_1, std_2) = (mstats.stde_median(group_1, axis=axis), + mstats.stde_median(group_2, axis=axis)) + W = np.abs(med_1 - med_2) / ma.sqrt(std_1**2 + std_2**2) + return 1 - norm.cdf(W) + + +def idealfourths(data, axis=None): + """ + Returns an estimate of the lower and upper quartiles. + + Uses the ideal fourths algorithm. + + Parameters + ---------- + data : array_like + Input array. + axis : int, optional + Axis along which the quartiles are estimated. If None, the arrays are + flattened. + + Returns + ------- + idealfourths : {list of floats, masked array} + Returns the two internal values that divide `data` into four parts + using the ideal fourths algorithm either along the flattened array + (if `axis` is None) or along `axis` of `data`. + + """ + def _idf(data): + x = data.compressed() + n = len(x) + if n < 3: + return [np.nan,np.nan] + (j,h) = divmod(n/4. + 5/12.,1) + j = int(j) + qlo = (1-h)*x[j-1] + h*x[j] + k = n - j + qup = (1-h)*x[k] + h*x[k-1] + return [qlo, qup] + data = ma.sort(data, axis=axis).view(MaskedArray) + if (axis is None): + return _idf(data) + else: + return ma.apply_along_axis(_idf, axis, data) + + +def rsh(data, points=None): + """ + Evaluates Rosenblatt's shifted histogram estimators for each data point. + + Rosenblatt's estimator is a centered finite-difference approximation to the + derivative of the empirical cumulative distribution function. + + Parameters + ---------- + data : sequence + Input data, should be 1-D. Masked values are ignored. + points : sequence or None, optional + Sequence of points where to evaluate Rosenblatt shifted histogram. + If None, use the data. + + """ + data = ma.array(data, copy=False) + if points is None: + points = data + else: + points = np.atleast_1d(np.asarray(points)) + + if data.ndim != 1: + raise AttributeError("The input array should be 1D only !") + + n = data.count() + r = idealfourths(data, axis=None) + h = 1.2 * (r[-1]-r[0]) / n**(1./5) + nhi = (data[:,None] <= points[None,:] + h).sum(0) + nlo = (data[:,None] < points[None,:] - h).sum(0) + return (nhi-nlo) / (2.*n*h) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multicomp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multicomp.py new file mode 100644 index 0000000000000000000000000000000000000000..c12ce65a91dbb0a6fed48e06127f8902ca71b9bf --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multicomp.py @@ -0,0 +1,459 @@ +from __future__ import annotations + +import warnings +from dataclasses import dataclass, field +from typing import TYPE_CHECKING + +import numpy as np + +from scipy import stats +from scipy.optimize import minimize_scalar +from scipy.stats._common import ConfidenceInterval +from scipy.stats._qmc import check_random_state +from scipy.stats._stats_py import _var + +if TYPE_CHECKING: + import numpy.typing as npt + from scipy._lib._util import DecimalNumber, SeedType + from typing import Literal, Sequence # noqa: UP035 + + +__all__ = [ + 'dunnett' +] + + +@dataclass +class DunnettResult: + """Result object returned by `scipy.stats.dunnett`. + + Attributes + ---------- + statistic : float ndarray + The computed statistic of the test for each comparison. The element + at index ``i`` is the statistic for the comparison between + groups ``i`` and the control. + pvalue : float ndarray + The computed p-value of the test for each comparison. The element + at index ``i`` is the p-value for the comparison between + group ``i`` and the control. + """ + statistic: np.ndarray + pvalue: np.ndarray + _alternative: Literal['two-sided', 'less', 'greater'] = field(repr=False) + _rho: np.ndarray = field(repr=False) + _df: int = field(repr=False) + _std: float = field(repr=False) + _mean_samples: np.ndarray = field(repr=False) + _mean_control: np.ndarray = field(repr=False) + _n_samples: np.ndarray = field(repr=False) + _n_control: int = field(repr=False) + _rng: SeedType = field(repr=False) + _ci: ConfidenceInterval | None = field(default=None, repr=False) + _ci_cl: DecimalNumber | None = field(default=None, repr=False) + + def __str__(self): + # Note: `__str__` prints the confidence intervals from the most + # recent call to `confidence_interval`. If it has not been called, + # it will be called with the default CL of .95. + if self._ci is None: + self.confidence_interval(confidence_level=.95) + s = ( + "Dunnett's test" + f" ({self._ci_cl*100:.1f}% Confidence Interval)\n" + "Comparison Statistic p-value Lower CI Upper CI\n" + ) + for i in range(self.pvalue.size): + s += (f" (Sample {i} - Control) {self.statistic[i]:>10.3f}" + f"{self.pvalue[i]:>10.3f}" + f"{self._ci.low[i]:>10.3f}" + f"{self._ci.high[i]:>10.3f}\n") + + return s + + def _allowance( + self, confidence_level: DecimalNumber = 0.95, tol: DecimalNumber = 1e-3 + ) -> float: + """Allowance. + + It is the quantity to add/subtract from the observed difference + between the means of observed groups and the mean of the control + group. The result gives confidence limits. + + Parameters + ---------- + confidence_level : float, optional + Confidence level for the computed confidence interval. + Default is .95. + tol : float, optional + A tolerance for numerical optimization: the allowance will produce + a confidence within ``10*tol*(1 - confidence_level)`` of the + specified level, or a warning will be emitted. Tight tolerances + may be impractical due to noisy evaluation of the objective. + Default is 1e-3. + + Returns + ------- + allowance : float + Allowance around the mean. + """ + alpha = 1 - confidence_level + + def pvalue_from_stat(statistic): + statistic = np.array(statistic) + sf = _pvalue_dunnett( + rho=self._rho, df=self._df, + statistic=statistic, alternative=self._alternative, + rng=self._rng + ) + return abs(sf - alpha)/alpha + + # Evaluation of `pvalue_from_stat` is noisy due to the use of RQMC to + # evaluate `multivariate_t.cdf`. `minimize_scalar` is not designed + # to tolerate a noisy objective function and may fail to find the + # minimum accurately. We mitigate this possibility with the validation + # step below, but implementation of a noise-tolerant root finder or + # minimizer would be a welcome enhancement. See gh-18150. + res = minimize_scalar(pvalue_from_stat, method='brent', tol=tol) + critical_value = res.x + + # validation + # tol*10 because tol=1e-3 means we tolerate a 1% change at most + if res.success is False or res.fun >= tol*10: + warnings.warn( + "Computation of the confidence interval did not converge to " + "the desired level. The confidence level corresponding with " + f"the returned interval is approximately {alpha*(1+res.fun)}.", + stacklevel=3 + ) + + # From [1] p. 1101 between (1) and (3) + allowance = critical_value*self._std*np.sqrt( + 1/self._n_samples + 1/self._n_control + ) + return abs(allowance) + + def confidence_interval( + self, confidence_level: DecimalNumber = 0.95 + ) -> ConfidenceInterval: + """Compute the confidence interval for the specified confidence level. + + Parameters + ---------- + confidence_level : float, optional + Confidence level for the computed confidence interval. + Default is .95. + + Returns + ------- + ci : ``ConfidenceInterval`` object + The object has attributes ``low`` and ``high`` that hold the + lower and upper bounds of the confidence intervals for each + comparison. The high and low values are accessible for each + comparison at index ``i`` for each group ``i``. + + """ + # check to see if the supplied confidence level matches that of the + # previously computed CI. + if (self._ci is not None) and (confidence_level == self._ci_cl): + return self._ci + + if not (0 < confidence_level < 1): + raise ValueError("Confidence level must be between 0 and 1.") + + allowance = self._allowance(confidence_level=confidence_level) + diff_means = self._mean_samples - self._mean_control + + low = diff_means-allowance + high = diff_means+allowance + + if self._alternative == 'greater': + high = [np.inf] * len(diff_means) + elif self._alternative == 'less': + low = [-np.inf] * len(diff_means) + + self._ci_cl = confidence_level + self._ci = ConfidenceInterval( + low=low, + high=high + ) + return self._ci + + +def dunnett( + *samples: npt.ArrayLike, # noqa: D417 + control: npt.ArrayLike, + alternative: Literal['two-sided', 'less', 'greater'] = "two-sided", + random_state: SeedType = None +) -> DunnettResult: + """Dunnett's test: multiple comparisons of means against a control group. + + This is an implementation of Dunnett's original, single-step test as + described in [1]_. + + Parameters + ---------- + sample1, sample2, ... : 1D array_like + The sample measurements for each experimental group. + control : 1D array_like + The sample measurements for the control group. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + + The null hypothesis is that the means of the distributions underlying + the samples and control are equal. The following alternative + hypotheses are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions underlying the samples + and control are unequal. + * 'less': the means of the distributions underlying the samples + are less than the mean of the distribution underlying the control. + * 'greater': the means of the distributions underlying the + samples are greater than the mean of the distribution underlying + the control. + random_state : {None, int, `numpy.random.Generator`}, optional + If `random_state` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(random_state)``. + If `random_state` is already a ``Generator`` instance, then the + provided instance is used. + + The random number generator is used to control the randomized + Quasi-Monte Carlo integration of the multivariate-t distribution. + + Returns + ------- + res : `~scipy.stats._result_classes.DunnettResult` + An object containing attributes: + + statistic : float ndarray + The computed statistic of the test for each comparison. The element + at index ``i`` is the statistic for the comparison between + groups ``i`` and the control. + pvalue : float ndarray + The computed p-value of the test for each comparison. The element + at index ``i`` is the p-value for the comparison between + group ``i`` and the control. + + And the following method: + + confidence_interval(confidence_level=0.95) : + Compute the difference in means of the groups + with the control +- the allowance. + + See Also + -------- + tukey_hsd : performs pairwise comparison of means. + + Notes + ----- + Like the independent-sample t-test, Dunnett's test [1]_ is used to make + inferences about the means of distributions from which samples were drawn. + However, when multiple t-tests are performed at a fixed significance level, + the "family-wise error rate" - the probability of incorrectly rejecting the + null hypothesis in at least one test - will exceed the significance level. + Dunnett's test is designed to perform multiple comparisons while + controlling the family-wise error rate. + + Dunnett's test compares the means of multiple experimental groups + against a single control group. Tukey's Honestly Significant Difference Test + is another multiple-comparison test that controls the family-wise error + rate, but `tukey_hsd` performs *all* pairwise comparisons between groups. + When pairwise comparisons between experimental groups are not needed, + Dunnett's test is preferable due to its higher power. + + + The use of this test relies on several assumptions. + + 1. The observations are independent within and among groups. + 2. The observations within each group are normally distributed. + 3. The distributions from which the samples are drawn have the same finite + variance. + + References + ---------- + .. [1] Charles W. Dunnett. "A Multiple Comparison Procedure for Comparing + Several Treatments with a Control." + Journal of the American Statistical Association, 50:272, 1096-1121, + :doi:`10.1080/01621459.1955.10501294`, 1955. + + Examples + -------- + In [1]_, the influence of drugs on blood count measurements on three groups + of animal is investigated. + + The following table summarizes the results of the experiment in which + two groups received different drugs, and one group acted as a control. + Blood counts (in millions of cells per cubic millimeter) were recorded:: + + >>> import numpy as np + >>> control = np.array([7.40, 8.50, 7.20, 8.24, 9.84, 8.32]) + >>> drug_a = np.array([9.76, 8.80, 7.68, 9.36]) + >>> drug_b = np.array([12.80, 9.68, 12.16, 9.20, 10.55]) + + We would like to see if the means between any of the groups are + significantly different. First, visually examine a box and whisker plot. + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(1, 1) + >>> ax.boxplot([control, drug_a, drug_b]) + >>> ax.set_xticklabels(["Control", "Drug A", "Drug B"]) # doctest: +SKIP + >>> ax.set_ylabel("mean") # doctest: +SKIP + >>> plt.show() + + Note the overlapping interquartile ranges of the drug A group and control + group and the apparent separation between the drug B group and control + group. + + Next, we will use Dunnett's test to assess whether the difference + between group means is significant while controlling the family-wise error + rate: the probability of making any false discoveries. + Let the null hypothesis be that the experimental groups have the same + mean as the control and the alternative be that an experimental group does + not have the same mean as the control. We will consider a 5% family-wise + error rate to be acceptable, and therefore we choose 0.05 as the threshold + for significance. + + >>> from scipy.stats import dunnett + >>> res = dunnett(drug_a, drug_b, control=control) + >>> res.pvalue + array([0.62004941, 0.0059035 ]) # may vary + + The p-value corresponding with the comparison between group A and control + exceeds 0.05, so we do not reject the null hypothesis for that comparison. + However, the p-value corresponding with the comparison between group B + and control is less than 0.05, so we consider the experimental results + to be evidence against the null hypothesis in favor of the alternative: + group B has a different mean than the control group. + + """ + samples_, control_, rng = _iv_dunnett( + samples=samples, control=control, + alternative=alternative, random_state=random_state + ) + + rho, df, n_group, n_samples, n_control = _params_dunnett( + samples=samples_, control=control_ + ) + + statistic, std, mean_control, mean_samples = _statistic_dunnett( + samples_, control_, df, n_samples, n_control + ) + + pvalue = _pvalue_dunnett( + rho=rho, df=df, statistic=statistic, alternative=alternative, rng=rng + ) + + return DunnettResult( + statistic=statistic, pvalue=pvalue, + _alternative=alternative, + _rho=rho, _df=df, _std=std, + _mean_samples=mean_samples, + _mean_control=mean_control, + _n_samples=n_samples, + _n_control=n_control, + _rng=rng + ) + + +def _iv_dunnett( + samples: Sequence[npt.ArrayLike], + control: npt.ArrayLike, + alternative: Literal['two-sided', 'less', 'greater'], + random_state: SeedType +) -> tuple[list[np.ndarray], np.ndarray, SeedType]: + """Input validation for Dunnett's test.""" + rng = check_random_state(random_state) + + if alternative not in {'two-sided', 'less', 'greater'}: + raise ValueError( + "alternative must be 'less', 'greater' or 'two-sided'" + ) + + ndim_msg = "Control and samples groups must be 1D arrays" + n_obs_msg = "Control and samples groups must have at least 1 observation" + + control = np.asarray(control) + samples_ = [np.asarray(sample) for sample in samples] + + # samples checks + samples_control: list[np.ndarray] = samples_ + [control] + for sample in samples_control: + if sample.ndim > 1: + raise ValueError(ndim_msg) + + if sample.size < 1: + raise ValueError(n_obs_msg) + + return samples_, control, rng + + +def _params_dunnett( + samples: list[np.ndarray], control: np.ndarray +) -> tuple[np.ndarray, int, int, np.ndarray, int]: + """Specific parameters for Dunnett's test. + + Degree of freedom is the number of observations minus the number of groups + including the control. + """ + n_samples = np.array([sample.size for sample in samples]) + + # From [1] p. 1100 d.f. = (sum N)-(p+1) + n_sample = n_samples.sum() + n_control = control.size + n = n_sample + n_control + n_groups = len(samples) + df = n - n_groups - 1 + + # From [1] p. 1103 rho_ij = 1/sqrt((N0/Ni+1)(N0/Nj+1)) + rho = n_control/n_samples + 1 + rho = 1/np.sqrt(rho[:, None] * rho[None, :]) + np.fill_diagonal(rho, 1) + + return rho, df, n_groups, n_samples, n_control + + +def _statistic_dunnett( + samples: list[np.ndarray], control: np.ndarray, df: int, + n_samples: np.ndarray, n_control: int +) -> tuple[np.ndarray, float, np.ndarray, np.ndarray]: + """Statistic of Dunnett's test. + + Computation based on the original single-step test from [1]. + """ + mean_control = np.mean(control) + mean_samples = np.array([np.mean(sample) for sample in samples]) + all_samples = [control] + samples + all_means = np.concatenate([[mean_control], mean_samples]) + + # Variance estimate s^2 from [1] Eq. 1 + s2 = np.sum([_var(sample, mean=mean)*sample.size + for sample, mean in zip(all_samples, all_means)]) / df + std = np.sqrt(s2) + + # z score inferred from [1] unlabeled equation after Eq. 1 + z = (mean_samples - mean_control) / np.sqrt(1/n_samples + 1/n_control) + + return z / std, std, mean_control, mean_samples + + +def _pvalue_dunnett( + rho: np.ndarray, df: int, statistic: np.ndarray, + alternative: Literal['two-sided', 'less', 'greater'], + rng: SeedType = None +) -> np.ndarray: + """pvalue from the multivariate t-distribution. + + Critical values come from the multivariate student-t distribution. + """ + statistic = statistic.reshape(-1, 1) + + mvt = stats.multivariate_t(shape=rho, df=df, seed=rng) + if alternative == "two-sided": + statistic = abs(statistic) + pvalue = 1 - mvt.cdf(statistic, lower_limit=-statistic) + elif alternative == "greater": + pvalue = 1 - mvt.cdf(statistic, lower_limit=-np.inf) + else: + pvalue = 1 - mvt.cdf(np.inf, lower_limit=statistic) + + return np.atleast_1d(pvalue) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multivariate.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multivariate.py new file mode 100644 index 0000000000000000000000000000000000000000..ddb59ae8c68ae0f0018f8cccfd79c7fadee742ec --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_multivariate.py @@ -0,0 +1,6981 @@ +# +# Author: Joris Vankerschaver 2013 +# +import math +import numpy as np +import scipy.linalg +from scipy._lib import doccer +from scipy.special import (gammaln, psi, multigammaln, xlogy, entr, betaln, + ive, loggamma) +from scipy._lib._util import check_random_state, _lazywhere +from scipy.linalg.blas import drot, get_blas_funcs +from ._continuous_distns import norm +from ._discrete_distns import binom +from . import _mvn, _covariance, _rcont +from ._qmvnt import _qmvt +from ._morestats import directional_stats +from scipy.optimize import root_scalar + +__all__ = ['multivariate_normal', + 'matrix_normal', + 'dirichlet', + 'dirichlet_multinomial', + 'wishart', + 'invwishart', + 'multinomial', + 'special_ortho_group', + 'ortho_group', + 'random_correlation', + 'unitary_group', + 'multivariate_t', + 'multivariate_hypergeom', + 'random_table', + 'uniform_direction', + 'vonmises_fisher'] + +_LOG_2PI = np.log(2 * np.pi) +_LOG_2 = np.log(2) +_LOG_PI = np.log(np.pi) + + +_doc_random_state = """\ +seed : {None, int, np.random.RandomState, np.random.Generator}, optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. +""" + + +def _squeeze_output(out): + """ + Remove single-dimensional entries from array and convert to scalar, + if necessary. + """ + out = out.squeeze() + if out.ndim == 0: + out = out[()] + return out + + +def _eigvalsh_to_eps(spectrum, cond=None, rcond=None): + """Determine which eigenvalues are "small" given the spectrum. + + This is for compatibility across various linear algebra functions + that should agree about whether or not a Hermitian matrix is numerically + singular and what is its numerical matrix rank. + This is designed to be compatible with scipy.linalg.pinvh. + + Parameters + ---------- + spectrum : 1d ndarray + Array of eigenvalues of a Hermitian matrix. + cond, rcond : float, optional + Cutoff for small eigenvalues. + Singular values smaller than rcond * largest_eigenvalue are + considered zero. + If None or -1, suitable machine precision is used. + + Returns + ------- + eps : float + Magnitude cutoff for numerical negligibility. + + """ + if rcond is not None: + cond = rcond + if cond in [None, -1]: + t = spectrum.dtype.char.lower() + factor = {'f': 1E3, 'd': 1E6} + cond = factor[t] * np.finfo(t).eps + eps = cond * np.max(abs(spectrum)) + return eps + + +def _pinv_1d(v, eps=1e-5): + """A helper function for computing the pseudoinverse. + + Parameters + ---------- + v : iterable of numbers + This may be thought of as a vector of eigenvalues or singular values. + eps : float + Values with magnitude no greater than eps are considered negligible. + + Returns + ------- + v_pinv : 1d float ndarray + A vector of pseudo-inverted numbers. + + """ + return np.array([0 if abs(x) <= eps else 1/x for x in v], dtype=float) + + +class _PSD: + """ + Compute coordinated functions of a symmetric positive semidefinite matrix. + + This class addresses two issues. Firstly it allows the pseudoinverse, + the logarithm of the pseudo-determinant, and the rank of the matrix + to be computed using one call to eigh instead of three. + Secondly it allows these functions to be computed in a way + that gives mutually compatible results. + All of the functions are computed with a common understanding as to + which of the eigenvalues are to be considered negligibly small. + The functions are designed to coordinate with scipy.linalg.pinvh() + but not necessarily with np.linalg.det() or with np.linalg.matrix_rank(). + + Parameters + ---------- + M : array_like + Symmetric positive semidefinite matrix (2-D). + cond, rcond : float, optional + Cutoff for small eigenvalues. + Singular values smaller than rcond * largest_eigenvalue are + considered zero. + If None or -1, suitable machine precision is used. + lower : bool, optional + Whether the pertinent array data is taken from the lower + or upper triangle of M. (Default: lower) + check_finite : bool, optional + Whether to check that the input matrices contain only finite + numbers. Disabling may give a performance gain, but may result + in problems (crashes, non-termination) if the inputs do contain + infinities or NaNs. + allow_singular : bool, optional + Whether to allow a singular matrix. (Default: True) + + Notes + ----- + The arguments are similar to those of scipy.linalg.pinvh(). + + """ + + def __init__(self, M, cond=None, rcond=None, lower=True, + check_finite=True, allow_singular=True): + self._M = np.asarray(M) + + # Compute the symmetric eigendecomposition. + # Note that eigh takes care of array conversion, chkfinite, + # and assertion that the matrix is square. + s, u = scipy.linalg.eigh(M, lower=lower, check_finite=check_finite) + + eps = _eigvalsh_to_eps(s, cond, rcond) + if np.min(s) < -eps: + msg = "The input matrix must be symmetric positive semidefinite." + raise ValueError(msg) + d = s[s > eps] + if len(d) < len(s) and not allow_singular: + msg = ("When `allow_singular is False`, the input matrix must be " + "symmetric positive definite.") + raise np.linalg.LinAlgError(msg) + s_pinv = _pinv_1d(s, eps) + U = np.multiply(u, np.sqrt(s_pinv)) + + # Save the eigenvector basis, and tolerance for testing support + self.eps = 1e3*eps + self.V = u[:, s <= eps] + + # Initialize the eagerly precomputed attributes. + self.rank = len(d) + self.U = U + self.log_pdet = np.sum(np.log(d)) + + # Initialize attributes to be lazily computed. + self._pinv = None + + def _support_mask(self, x): + """ + Check whether x lies in the support of the distribution. + """ + residual = np.linalg.norm(x @ self.V, axis=-1) + in_support = residual < self.eps + return in_support + + @property + def pinv(self): + if self._pinv is None: + self._pinv = np.dot(self.U, self.U.T) + return self._pinv + + +class multi_rv_generic: + """ + Class which encapsulates common functionality between all multivariate + distributions. + """ + def __init__(self, seed=None): + super().__init__() + self._random_state = check_random_state(seed) + + @property + def random_state(self): + """ Get or set the Generator object for generating random variates. + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + """ + return self._random_state + + @random_state.setter + def random_state(self, seed): + self._random_state = check_random_state(seed) + + def _get_random_state(self, random_state): + if random_state is not None: + return check_random_state(random_state) + else: + return self._random_state + + +class multi_rv_frozen: + """ + Class which encapsulates common functionality between all frozen + multivariate distributions. + """ + @property + def random_state(self): + return self._dist._random_state + + @random_state.setter + def random_state(self, seed): + self._dist._random_state = check_random_state(seed) + + +_mvn_doc_default_callparams = """\ +mean : array_like, default: ``[0]`` + Mean of the distribution. +cov : array_like or `Covariance`, default: ``[1]`` + Symmetric positive (semi)definite covariance matrix of the distribution. +allow_singular : bool, default: ``False`` + Whether to allow a singular covariance matrix. This is ignored if `cov` is + a `Covariance` object. +""" + +_mvn_doc_callparams_note = """\ +Setting the parameter `mean` to `None` is equivalent to having `mean` +be the zero-vector. The parameter `cov` can be a scalar, in which case +the covariance matrix is the identity times that value, a vector of +diagonal entries for the covariance matrix, a two-dimensional array_like, +or a `Covariance` object. +""" + +_mvn_doc_frozen_callparams = "" + +_mvn_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +mvn_docdict_params = { + '_mvn_doc_default_callparams': _mvn_doc_default_callparams, + '_mvn_doc_callparams_note': _mvn_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +mvn_docdict_noparams = { + '_mvn_doc_default_callparams': _mvn_doc_frozen_callparams, + '_mvn_doc_callparams_note': _mvn_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class multivariate_normal_gen(multi_rv_generic): + r"""A multivariate normal random variable. + + The `mean` keyword specifies the mean. The `cov` keyword specifies the + covariance matrix. + + Methods + ------- + pdf(x, mean=None, cov=1, allow_singular=False) + Probability density function. + logpdf(x, mean=None, cov=1, allow_singular=False) + Log of the probability density function. + cdf(x, mean=None, cov=1, allow_singular=False, maxpts=1000000*dim, abseps=1e-5, releps=1e-5, lower_limit=None) + Cumulative distribution function. + logcdf(x, mean=None, cov=1, allow_singular=False, maxpts=1000000*dim, abseps=1e-5, releps=1e-5) + Log of the cumulative distribution function. + rvs(mean=None, cov=1, size=1, random_state=None) + Draw random samples from a multivariate normal distribution. + entropy(mean=None, cov=1) + Compute the differential entropy of the multivariate normal. + fit(x, fix_mean=None, fix_cov=None) + Fit a multivariate normal distribution to data. + + Parameters + ---------- + %(_mvn_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + %(_mvn_doc_callparams_note)s + + The covariance matrix `cov` may be an instance of a subclass of + `Covariance`, e.g. `scipy.stats.CovViaPrecision`. If so, `allow_singular` + is ignored. + + Otherwise, `cov` must be a symmetric positive semidefinite + matrix when `allow_singular` is True; it must be (strictly) positive + definite when `allow_singular` is False. + Symmetry is not checked; only the lower triangular portion is used. + The determinant and inverse of `cov` are computed + as the pseudo-determinant and pseudo-inverse, respectively, so + that `cov` does not need to have full rank. + + The probability density function for `multivariate_normal` is + + .. math:: + + f(x) = \frac{1}{\sqrt{(2 \pi)^k \det \Sigma}} + \exp\left( -\frac{1}{2} (x - \mu)^T \Sigma^{-1} (x - \mu) \right), + + where :math:`\mu` is the mean, :math:`\Sigma` the covariance matrix, + :math:`k` the rank of :math:`\Sigma`. In case of singular :math:`\Sigma`, + SciPy extends this definition according to [1]_. + + .. versionadded:: 0.14.0 + + References + ---------- + .. [1] Multivariate Normal Distribution - Degenerate Case, Wikipedia, + https://en.wikipedia.org/wiki/Multivariate_normal_distribution#Degenerate_case + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import multivariate_normal + + >>> x = np.linspace(0, 5, 10, endpoint=False) + >>> y = multivariate_normal.pdf(x, mean=2.5, cov=0.5); y + array([ 0.00108914, 0.01033349, 0.05946514, 0.20755375, 0.43939129, + 0.56418958, 0.43939129, 0.20755375, 0.05946514, 0.01033349]) + >>> fig1 = plt.figure() + >>> ax = fig1.add_subplot(111) + >>> ax.plot(x, y) + >>> plt.show() + + Alternatively, the object may be called (as a function) to fix the mean + and covariance parameters, returning a "frozen" multivariate normal + random variable: + + >>> rv = multivariate_normal(mean=None, cov=1, allow_singular=False) + >>> # Frozen object with the same methods but holding the given + >>> # mean and covariance fixed. + + The input quantiles can be any shape of array, as long as the last + axis labels the components. This allows us for instance to + display the frozen pdf for a non-isotropic random variable in 2D as + follows: + + >>> x, y = np.mgrid[-1:1:.01, -1:1:.01] + >>> pos = np.dstack((x, y)) + >>> rv = multivariate_normal([0.5, -0.2], [[2.0, 0.3], [0.3, 0.5]]) + >>> fig2 = plt.figure() + >>> ax2 = fig2.add_subplot(111) + >>> ax2.contourf(x, y, rv.pdf(pos)) + + """ # noqa: E501 + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, mvn_docdict_params) + + def __call__(self, mean=None, cov=1, allow_singular=False, seed=None): + """Create a frozen multivariate normal distribution. + + See `multivariate_normal_frozen` for more information. + """ + return multivariate_normal_frozen(mean, cov, + allow_singular=allow_singular, + seed=seed) + + def _process_parameters(self, mean, cov, allow_singular=True): + """ + Infer dimensionality from mean or covariance matrix, ensure that + mean and covariance are full vector resp. matrix. + """ + if isinstance(cov, _covariance.Covariance): + return self._process_parameters_Covariance(mean, cov) + else: + # Before `Covariance` classes were introduced, + # `multivariate_normal` accepted plain arrays as `cov` and used the + # following input validation. To avoid disturbing the behavior of + # `multivariate_normal` when plain arrays are used, we use the + # original input validation here. + dim, mean, cov = self._process_parameters_psd(None, mean, cov) + # After input validation, some methods then processed the arrays + # with a `_PSD` object and used that to perform computation. + # To avoid branching statements in each method depending on whether + # `cov` is an array or `Covariance` object, we always process the + # array with `_PSD`, and then use wrapper that satisfies the + # `Covariance` interface, `CovViaPSD`. + psd = _PSD(cov, allow_singular=allow_singular) + cov_object = _covariance.CovViaPSD(psd) + return dim, mean, cov_object + + def _process_parameters_Covariance(self, mean, cov): + dim = cov.shape[-1] + mean = np.array([0.]) if mean is None else mean + message = (f"`cov` represents a covariance matrix in {dim} dimensions," + f"and so `mean` must be broadcastable to shape {(dim,)}") + try: + mean = np.broadcast_to(mean, dim) + except ValueError as e: + raise ValueError(message) from e + return dim, mean, cov + + def _process_parameters_psd(self, dim, mean, cov): + # Try to infer dimensionality + if dim is None: + if mean is None: + if cov is None: + dim = 1 + else: + cov = np.asarray(cov, dtype=float) + if cov.ndim < 2: + dim = 1 + else: + dim = cov.shape[0] + else: + mean = np.asarray(mean, dtype=float) + dim = mean.size + else: + if not np.isscalar(dim): + raise ValueError("Dimension of random variable must be " + "a scalar.") + + # Check input sizes and return full arrays for mean and cov if + # necessary + if mean is None: + mean = np.zeros(dim) + mean = np.asarray(mean, dtype=float) + + if cov is None: + cov = 1.0 + cov = np.asarray(cov, dtype=float) + + if dim == 1: + mean = mean.reshape(1) + cov = cov.reshape(1, 1) + + if mean.ndim != 1 or mean.shape[0] != dim: + raise ValueError("Array 'mean' must be a vector of length %d." % + dim) + if cov.ndim == 0: + cov = cov * np.eye(dim) + elif cov.ndim == 1: + cov = np.diag(cov) + elif cov.ndim == 2 and cov.shape != (dim, dim): + rows, cols = cov.shape + if rows != cols: + msg = ("Array 'cov' must be square if it is two dimensional," + " but cov.shape = %s." % str(cov.shape)) + else: + msg = ("Dimension mismatch: array 'cov' is of shape %s," + " but 'mean' is a vector of length %d.") + msg = msg % (str(cov.shape), len(mean)) + raise ValueError(msg) + elif cov.ndim > 2: + raise ValueError("Array 'cov' must be at most two-dimensional," + " but cov.ndim = %d" % cov.ndim) + + return dim, mean, cov + + def _process_quantiles(self, x, dim): + """ + Adjust quantiles array so that last axis labels the components of + each data point. + """ + x = np.asarray(x, dtype=float) + + if x.ndim == 0: + x = x[np.newaxis] + elif x.ndim == 1: + if dim == 1: + x = x[:, np.newaxis] + else: + x = x[np.newaxis, :] + + return x + + def _logpdf(self, x, mean, cov_object): + """Log of the multivariate normal probability density function. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the log of the probability + density function + mean : ndarray + Mean of the distribution + cov_object : Covariance + An object representing the Covariance matrix + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + log_det_cov, rank = cov_object.log_pdet, cov_object.rank + dev = x - mean + if dev.ndim > 1: + log_det_cov = log_det_cov[..., np.newaxis] + rank = rank[..., np.newaxis] + maha = np.sum(np.square(cov_object.whiten(dev)), axis=-1) + return -0.5 * (rank * _LOG_2PI + log_det_cov + maha) + + def logpdf(self, x, mean=None, cov=1, allow_singular=False): + """Log of the multivariate normal probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_mvn_doc_default_callparams)s + + Returns + ------- + pdf : ndarray or scalar + Log of the probability density function evaluated at `x` + + Notes + ----- + %(_mvn_doc_callparams_note)s + + """ + params = self._process_parameters(mean, cov, allow_singular) + dim, mean, cov_object = params + x = self._process_quantiles(x, dim) + out = self._logpdf(x, mean, cov_object) + if np.any(cov_object.rank < dim): + out_of_bounds = ~cov_object._support_mask(x-mean) + out[out_of_bounds] = -np.inf + return _squeeze_output(out) + + def pdf(self, x, mean=None, cov=1, allow_singular=False): + """Multivariate normal probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_mvn_doc_default_callparams)s + + Returns + ------- + pdf : ndarray or scalar + Probability density function evaluated at `x` + + Notes + ----- + %(_mvn_doc_callparams_note)s + + """ + params = self._process_parameters(mean, cov, allow_singular) + dim, mean, cov_object = params + x = self._process_quantiles(x, dim) + out = np.exp(self._logpdf(x, mean, cov_object)) + if np.any(cov_object.rank < dim): + out_of_bounds = ~cov_object._support_mask(x-mean) + out[out_of_bounds] = 0.0 + return _squeeze_output(out) + + def _cdf(self, x, mean, cov, maxpts, abseps, releps, lower_limit): + """Multivariate normal cumulative distribution function. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the cumulative distribution function. + mean : ndarray + Mean of the distribution + cov : array_like + Covariance matrix of the distribution + maxpts : integer + The maximum number of points to use for integration + abseps : float + Absolute error tolerance + releps : float + Relative error tolerance + lower_limit : array_like, optional + Lower limit of integration of the cumulative distribution function. + Default is negative infinity. Must be broadcastable with `x`. + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'cdf' instead. + + + .. versionadded:: 1.0.0 + + """ + lower = (np.full(mean.shape, -np.inf) + if lower_limit is None else lower_limit) + # In 2d, _mvn.mvnun accepts input in which `lower` bound elements + # are greater than `x`. Not so in other dimensions. Fix this by + # ensuring that lower bounds are indeed lower when passed, then + # set signs of resulting CDF manually. + b, a = np.broadcast_arrays(x, lower) + i_swap = b < a + signs = (-1)**(i_swap.sum(axis=-1)) # odd # of swaps -> negative + a, b = a.copy(), b.copy() + a[i_swap], b[i_swap] = b[i_swap], a[i_swap] + n = x.shape[-1] + limits = np.concatenate((a, b), axis=-1) + + # mvnun expects 1-d arguments, so process points sequentially + def func1d(limits): + return _mvn.mvnun(limits[:n], limits[n:], mean, cov, + maxpts, abseps, releps)[0] + + out = np.apply_along_axis(func1d, -1, limits) * signs + return _squeeze_output(out) + + def logcdf(self, x, mean=None, cov=1, allow_singular=False, maxpts=None, + abseps=1e-5, releps=1e-5, *, lower_limit=None): + """Log of the multivariate normal cumulative distribution function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_mvn_doc_default_callparams)s + maxpts : integer, optional + The maximum number of points to use for integration + (default `1000000*dim`) + abseps : float, optional + Absolute error tolerance (default 1e-5) + releps : float, optional + Relative error tolerance (default 1e-5) + lower_limit : array_like, optional + Lower limit of integration of the cumulative distribution function. + Default is negative infinity. Must be broadcastable with `x`. + + Returns + ------- + cdf : ndarray or scalar + Log of the cumulative distribution function evaluated at `x` + + Notes + ----- + %(_mvn_doc_callparams_note)s + + .. versionadded:: 1.0.0 + + """ + params = self._process_parameters(mean, cov, allow_singular) + dim, mean, cov_object = params + cov = cov_object.covariance + x = self._process_quantiles(x, dim) + if not maxpts: + maxpts = 1000000 * dim + cdf = self._cdf(x, mean, cov, maxpts, abseps, releps, lower_limit) + # the log of a negative real is complex, and cdf can be negative + # if lower limit is greater than upper limit + cdf = cdf + 0j if np.any(cdf < 0) else cdf + out = np.log(cdf) + return out + + def cdf(self, x, mean=None, cov=1, allow_singular=False, maxpts=None, + abseps=1e-5, releps=1e-5, *, lower_limit=None): + """Multivariate normal cumulative distribution function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_mvn_doc_default_callparams)s + maxpts : integer, optional + The maximum number of points to use for integration + (default `1000000*dim`) + abseps : float, optional + Absolute error tolerance (default 1e-5) + releps : float, optional + Relative error tolerance (default 1e-5) + lower_limit : array_like, optional + Lower limit of integration of the cumulative distribution function. + Default is negative infinity. Must be broadcastable with `x`. + + Returns + ------- + cdf : ndarray or scalar + Cumulative distribution function evaluated at `x` + + Notes + ----- + %(_mvn_doc_callparams_note)s + + .. versionadded:: 1.0.0 + + """ + params = self._process_parameters(mean, cov, allow_singular) + dim, mean, cov_object = params + cov = cov_object.covariance + x = self._process_quantiles(x, dim) + if not maxpts: + maxpts = 1000000 * dim + out = self._cdf(x, mean, cov, maxpts, abseps, releps, lower_limit) + return out + + def rvs(self, mean=None, cov=1, size=1, random_state=None): + """Draw random samples from a multivariate normal distribution. + + Parameters + ---------- + %(_mvn_doc_default_callparams)s + size : integer, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray or scalar + Random variates of size (`size`, `N`), where `N` is the + dimension of the random variable. + + Notes + ----- + %(_mvn_doc_callparams_note)s + + """ + dim, mean, cov_object = self._process_parameters(mean, cov) + random_state = self._get_random_state(random_state) + + if isinstance(cov_object, _covariance.CovViaPSD): + cov = cov_object.covariance + out = random_state.multivariate_normal(mean, cov, size) + out = _squeeze_output(out) + else: + size = size or tuple() + if not np.iterable(size): + size = (size,) + shape = tuple(size) + (cov_object.shape[-1],) + x = random_state.normal(size=shape) + out = mean + cov_object.colorize(x) + return out + + def entropy(self, mean=None, cov=1): + """Compute the differential entropy of the multivariate normal. + + Parameters + ---------- + %(_mvn_doc_default_callparams)s + + Returns + ------- + h : scalar + Entropy of the multivariate normal distribution + + Notes + ----- + %(_mvn_doc_callparams_note)s + + """ + dim, mean, cov_object = self._process_parameters(mean, cov) + return 0.5 * (cov_object.rank * (_LOG_2PI + 1) + cov_object.log_pdet) + + def fit(self, x, fix_mean=None, fix_cov=None): + """Fit a multivariate normal distribution to data. + + Parameters + ---------- + x : ndarray (m, n) + Data the distribution is fitted to. Must have two axes. + The first axis of length `m` represents the number of vectors + the distribution is fitted to. The second axis of length `n` + determines the dimensionality of the fitted distribution. + fix_mean : ndarray(n, ) + Fixed mean vector. Must have length `n`. + fix_cov: ndarray (n, n) + Fixed covariance matrix. Must have shape `(n, n)`. + + Returns + ------- + mean : ndarray (n, ) + Maximum likelihood estimate of the mean vector + cov : ndarray (n, n) + Maximum likelihood estimate of the covariance matrix + + """ + # input validation for data to be fitted + x = np.asarray(x) + if x.ndim != 2: + raise ValueError("`x` must be two-dimensional.") + + n_vectors, dim = x.shape + + # parameter estimation + # reference: https://home.ttic.edu/~shubhendu/Slides/Estimation.pdf + if fix_mean is not None: + # input validation for `fix_mean` + fix_mean = np.atleast_1d(fix_mean) + if fix_mean.shape != (dim, ): + msg = ("`fix_mean` must be a one-dimensional array the same " + "length as the dimensionality of the vectors `x`.") + raise ValueError(msg) + mean = fix_mean + else: + mean = x.mean(axis=0) + + if fix_cov is not None: + # input validation for `fix_cov` + fix_cov = np.atleast_2d(fix_cov) + # validate shape + if fix_cov.shape != (dim, dim): + msg = ("`fix_cov` must be a two-dimensional square array " + "of same side length as the dimensionality of the " + "vectors `x`.") + raise ValueError(msg) + # validate positive semidefiniteness + # a trimmed down copy from _PSD + s, u = scipy.linalg.eigh(fix_cov, lower=True, check_finite=True) + eps = _eigvalsh_to_eps(s) + if np.min(s) < -eps: + msg = "`fix_cov` must be symmetric positive semidefinite." + raise ValueError(msg) + cov = fix_cov + else: + centered_data = x - mean + cov = centered_data.T @ centered_data / n_vectors + return mean, cov + + +multivariate_normal = multivariate_normal_gen() + + +class multivariate_normal_frozen(multi_rv_frozen): + def __init__(self, mean=None, cov=1, allow_singular=False, seed=None, + maxpts=None, abseps=1e-5, releps=1e-5): + """Create a frozen multivariate normal distribution. + + Parameters + ---------- + mean : array_like, default: ``[0]`` + Mean of the distribution. + cov : array_like, default: ``[1]`` + Symmetric positive (semi)definite covariance matrix of the + distribution. + allow_singular : bool, default: ``False`` + Whether to allow a singular covariance matrix. + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + maxpts : integer, optional + The maximum number of points to use for integration of the + cumulative distribution function (default `1000000*dim`) + abseps : float, optional + Absolute error tolerance for the cumulative distribution function + (default 1e-5) + releps : float, optional + Relative error tolerance for the cumulative distribution function + (default 1e-5) + + Examples + -------- + When called with the default parameters, this will create a 1D random + variable with mean 0 and covariance 1: + + >>> from scipy.stats import multivariate_normal + >>> r = multivariate_normal() + >>> r.mean + array([ 0.]) + >>> r.cov + array([[1.]]) + + """ # numpy/numpydoc#87 # noqa: E501 + self._dist = multivariate_normal_gen(seed) + self.dim, self.mean, self.cov_object = ( + self._dist._process_parameters(mean, cov, allow_singular)) + self.allow_singular = allow_singular or self.cov_object._allow_singular + if not maxpts: + maxpts = 1000000 * self.dim + self.maxpts = maxpts + self.abseps = abseps + self.releps = releps + + @property + def cov(self): + return self.cov_object.covariance + + def logpdf(self, x): + x = self._dist._process_quantiles(x, self.dim) + out = self._dist._logpdf(x, self.mean, self.cov_object) + if np.any(self.cov_object.rank < self.dim): + out_of_bounds = ~self.cov_object._support_mask(x-self.mean) + out[out_of_bounds] = -np.inf + return _squeeze_output(out) + + def pdf(self, x): + return np.exp(self.logpdf(x)) + + def logcdf(self, x, *, lower_limit=None): + cdf = self.cdf(x, lower_limit=lower_limit) + # the log of a negative real is complex, and cdf can be negative + # if lower limit is greater than upper limit + cdf = cdf + 0j if np.any(cdf < 0) else cdf + out = np.log(cdf) + return out + + def cdf(self, x, *, lower_limit=None): + x = self._dist._process_quantiles(x, self.dim) + out = self._dist._cdf(x, self.mean, self.cov_object.covariance, + self.maxpts, self.abseps, self.releps, + lower_limit) + return _squeeze_output(out) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.mean, self.cov_object, size, random_state) + + def entropy(self): + """Computes the differential entropy of the multivariate normal. + + Returns + ------- + h : scalar + Entropy of the multivariate normal distribution + + """ + log_pdet = self.cov_object.log_pdet + rank = self.cov_object.rank + return 0.5 * (rank * (_LOG_2PI + 1) + log_pdet) + + +# Set frozen generator docstrings from corresponding docstrings in +# multivariate_normal_gen and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'logcdf', 'cdf', 'rvs']: + method = multivariate_normal_gen.__dict__[name] + method_frozen = multivariate_normal_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat(method.__doc__, + mvn_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, mvn_docdict_params) + +_matnorm_doc_default_callparams = """\ +mean : array_like, optional + Mean of the distribution (default: `None`) +rowcov : array_like, optional + Among-row covariance matrix of the distribution (default: `1`) +colcov : array_like, optional + Among-column covariance matrix of the distribution (default: `1`) +""" + +_matnorm_doc_callparams_note = """\ +If `mean` is set to `None` then a matrix of zeros is used for the mean. +The dimensions of this matrix are inferred from the shape of `rowcov` and +`colcov`, if these are provided, or set to `1` if ambiguous. + +`rowcov` and `colcov` can be two-dimensional array_likes specifying the +covariance matrices directly. Alternatively, a one-dimensional array will +be be interpreted as the entries of a diagonal matrix, and a scalar or +zero-dimensional array will be interpreted as this value times the +identity matrix. +""" + +_matnorm_doc_frozen_callparams = "" + +_matnorm_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +matnorm_docdict_params = { + '_matnorm_doc_default_callparams': _matnorm_doc_default_callparams, + '_matnorm_doc_callparams_note': _matnorm_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +matnorm_docdict_noparams = { + '_matnorm_doc_default_callparams': _matnorm_doc_frozen_callparams, + '_matnorm_doc_callparams_note': _matnorm_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class matrix_normal_gen(multi_rv_generic): + r"""A matrix normal random variable. + + The `mean` keyword specifies the mean. The `rowcov` keyword specifies the + among-row covariance matrix. The 'colcov' keyword specifies the + among-column covariance matrix. + + Methods + ------- + pdf(X, mean=None, rowcov=1, colcov=1) + Probability density function. + logpdf(X, mean=None, rowcov=1, colcov=1) + Log of the probability density function. + rvs(mean=None, rowcov=1, colcov=1, size=1, random_state=None) + Draw random samples. + entropy(rowcol=1, colcov=1) + Differential entropy. + + Parameters + ---------- + %(_matnorm_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + %(_matnorm_doc_callparams_note)s + + The covariance matrices specified by `rowcov` and `colcov` must be + (symmetric) positive definite. If the samples in `X` are + :math:`m \times n`, then `rowcov` must be :math:`m \times m` and + `colcov` must be :math:`n \times n`. `mean` must be the same shape as `X`. + + The probability density function for `matrix_normal` is + + .. math:: + + f(X) = (2 \pi)^{-\frac{mn}{2}}|U|^{-\frac{n}{2}} |V|^{-\frac{m}{2}} + \exp\left( -\frac{1}{2} \mathrm{Tr}\left[ U^{-1} (X-M) V^{-1} + (X-M)^T \right] \right), + + where :math:`M` is the mean, :math:`U` the among-row covariance matrix, + :math:`V` the among-column covariance matrix. + + The `allow_singular` behaviour of the `multivariate_normal` + distribution is not currently supported. Covariance matrices must be + full rank. + + The `matrix_normal` distribution is closely related to the + `multivariate_normal` distribution. Specifically, :math:`\mathrm{Vec}(X)` + (the vector formed by concatenating the columns of :math:`X`) has a + multivariate normal distribution with mean :math:`\mathrm{Vec}(M)` + and covariance :math:`V \otimes U` (where :math:`\otimes` is the Kronecker + product). Sampling and pdf evaluation are + :math:`\mathcal{O}(m^3 + n^3 + m^2 n + m n^2)` for the matrix normal, but + :math:`\mathcal{O}(m^3 n^3)` for the equivalent multivariate normal, + making this equivalent form algorithmically inefficient. + + .. versionadded:: 0.17.0 + + Examples + -------- + + >>> import numpy as np + >>> from scipy.stats import matrix_normal + + >>> M = np.arange(6).reshape(3,2); M + array([[0, 1], + [2, 3], + [4, 5]]) + >>> U = np.diag([1,2,3]); U + array([[1, 0, 0], + [0, 2, 0], + [0, 0, 3]]) + >>> V = 0.3*np.identity(2); V + array([[ 0.3, 0. ], + [ 0. , 0.3]]) + >>> X = M + 0.1; X + array([[ 0.1, 1.1], + [ 2.1, 3.1], + [ 4.1, 5.1]]) + >>> matrix_normal.pdf(X, mean=M, rowcov=U, colcov=V) + 0.023410202050005054 + + >>> # Equivalent multivariate normal + >>> from scipy.stats import multivariate_normal + >>> vectorised_X = X.T.flatten() + >>> equiv_mean = M.T.flatten() + >>> equiv_cov = np.kron(V,U) + >>> multivariate_normal.pdf(vectorised_X, mean=equiv_mean, cov=equiv_cov) + 0.023410202050005054 + + Alternatively, the object may be called (as a function) to fix the mean + and covariance parameters, returning a "frozen" matrix normal + random variable: + + >>> rv = matrix_normal(mean=None, rowcov=1, colcov=1) + >>> # Frozen object with the same methods but holding the given + >>> # mean and covariance fixed. + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, matnorm_docdict_params) + + def __call__(self, mean=None, rowcov=1, colcov=1, seed=None): + """Create a frozen matrix normal distribution. + + See `matrix_normal_frozen` for more information. + + """ + return matrix_normal_frozen(mean, rowcov, colcov, seed=seed) + + def _process_parameters(self, mean, rowcov, colcov): + """ + Infer dimensionality from mean or covariance matrices. Handle + defaults. Ensure compatible dimensions. + """ + + # Process mean + if mean is not None: + mean = np.asarray(mean, dtype=float) + meanshape = mean.shape + if len(meanshape) != 2: + raise ValueError("Array `mean` must be two dimensional.") + if np.any(meanshape == 0): + raise ValueError("Array `mean` has invalid shape.") + + # Process among-row covariance + rowcov = np.asarray(rowcov, dtype=float) + if rowcov.ndim == 0: + if mean is not None: + rowcov = rowcov * np.identity(meanshape[0]) + else: + rowcov = rowcov * np.identity(1) + elif rowcov.ndim == 1: + rowcov = np.diag(rowcov) + rowshape = rowcov.shape + if len(rowshape) != 2: + raise ValueError("`rowcov` must be a scalar or a 2D array.") + if rowshape[0] != rowshape[1]: + raise ValueError("Array `rowcov` must be square.") + if rowshape[0] == 0: + raise ValueError("Array `rowcov` has invalid shape.") + numrows = rowshape[0] + + # Process among-column covariance + colcov = np.asarray(colcov, dtype=float) + if colcov.ndim == 0: + if mean is not None: + colcov = colcov * np.identity(meanshape[1]) + else: + colcov = colcov * np.identity(1) + elif colcov.ndim == 1: + colcov = np.diag(colcov) + colshape = colcov.shape + if len(colshape) != 2: + raise ValueError("`colcov` must be a scalar or a 2D array.") + if colshape[0] != colshape[1]: + raise ValueError("Array `colcov` must be square.") + if colshape[0] == 0: + raise ValueError("Array `colcov` has invalid shape.") + numcols = colshape[0] + + # Ensure mean and covariances compatible + if mean is not None: + if meanshape[0] != numrows: + raise ValueError("Arrays `mean` and `rowcov` must have the " + "same number of rows.") + if meanshape[1] != numcols: + raise ValueError("Arrays `mean` and `colcov` must have the " + "same number of columns.") + else: + mean = np.zeros((numrows, numcols)) + + dims = (numrows, numcols) + + return dims, mean, rowcov, colcov + + def _process_quantiles(self, X, dims): + """ + Adjust quantiles array so that last two axes labels the components of + each data point. + """ + X = np.asarray(X, dtype=float) + if X.ndim == 2: + X = X[np.newaxis, :] + if X.shape[-2:] != dims: + raise ValueError("The shape of array `X` is not compatible " + "with the distribution parameters.") + return X + + def _logpdf(self, dims, X, mean, row_prec_rt, log_det_rowcov, + col_prec_rt, log_det_colcov): + """Log of the matrix normal probability density function. + + Parameters + ---------- + dims : tuple + Dimensions of the matrix variates + X : ndarray + Points at which to evaluate the log of the probability + density function + mean : ndarray + Mean of the distribution + row_prec_rt : ndarray + A decomposition such that np.dot(row_prec_rt, row_prec_rt.T) + is the inverse of the among-row covariance matrix + log_det_rowcov : float + Logarithm of the determinant of the among-row covariance matrix + col_prec_rt : ndarray + A decomposition such that np.dot(col_prec_rt, col_prec_rt.T) + is the inverse of the among-column covariance matrix + log_det_colcov : float + Logarithm of the determinant of the among-column covariance matrix + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + numrows, numcols = dims + roll_dev = np.moveaxis(X-mean, -1, 0) + scale_dev = np.tensordot(col_prec_rt.T, + np.dot(roll_dev, row_prec_rt), 1) + maha = np.sum(np.sum(np.square(scale_dev), axis=-1), axis=0) + return -0.5 * (numrows*numcols*_LOG_2PI + numcols*log_det_rowcov + + numrows*log_det_colcov + maha) + + def logpdf(self, X, mean=None, rowcov=1, colcov=1): + """Log of the matrix normal probability density function. + + Parameters + ---------- + X : array_like + Quantiles, with the last two axes of `X` denoting the components. + %(_matnorm_doc_default_callparams)s + + Returns + ------- + logpdf : ndarray + Log of the probability density function evaluated at `X` + + Notes + ----- + %(_matnorm_doc_callparams_note)s + + """ + dims, mean, rowcov, colcov = self._process_parameters(mean, rowcov, + colcov) + X = self._process_quantiles(X, dims) + rowpsd = _PSD(rowcov, allow_singular=False) + colpsd = _PSD(colcov, allow_singular=False) + out = self._logpdf(dims, X, mean, rowpsd.U, rowpsd.log_pdet, colpsd.U, + colpsd.log_pdet) + return _squeeze_output(out) + + def pdf(self, X, mean=None, rowcov=1, colcov=1): + """Matrix normal probability density function. + + Parameters + ---------- + X : array_like + Quantiles, with the last two axes of `X` denoting the components. + %(_matnorm_doc_default_callparams)s + + Returns + ------- + pdf : ndarray + Probability density function evaluated at `X` + + Notes + ----- + %(_matnorm_doc_callparams_note)s + + """ + return np.exp(self.logpdf(X, mean, rowcov, colcov)) + + def rvs(self, mean=None, rowcov=1, colcov=1, size=1, random_state=None): + """Draw random samples from a matrix normal distribution. + + Parameters + ---------- + %(_matnorm_doc_default_callparams)s + size : integer, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray or scalar + Random variates of size (`size`, `dims`), where `dims` is the + dimension of the random matrices. + + Notes + ----- + %(_matnorm_doc_callparams_note)s + + """ + size = int(size) + dims, mean, rowcov, colcov = self._process_parameters(mean, rowcov, + colcov) + rowchol = scipy.linalg.cholesky(rowcov, lower=True) + colchol = scipy.linalg.cholesky(colcov, lower=True) + random_state = self._get_random_state(random_state) + # We aren't generating standard normal variates with size=(size, + # dims[0], dims[1]) directly to ensure random variates remain backwards + # compatible. See https://github.com/scipy/scipy/pull/12312 for more + # details. + std_norm = random_state.standard_normal( + size=(dims[1], size, dims[0]) + ).transpose(1, 2, 0) + out = mean + np.einsum('jp,ipq,kq->ijk', + rowchol, std_norm, colchol, + optimize=True) + if size == 1: + out = out.reshape(mean.shape) + return out + + def entropy(self, rowcov=1, colcov=1): + """Log of the matrix normal probability density function. + + Parameters + ---------- + rowcov : array_like, optional + Among-row covariance matrix of the distribution (default: `1`) + colcov : array_like, optional + Among-column covariance matrix of the distribution (default: `1`) + + Returns + ------- + entropy : float + Entropy of the distribution + + Notes + ----- + %(_matnorm_doc_callparams_note)s + + """ + dummy_mean = np.zeros((rowcov.shape[0], colcov.shape[0])) + dims, _, rowcov, colcov = self._process_parameters(dummy_mean, + rowcov, + colcov) + rowpsd = _PSD(rowcov, allow_singular=False) + colpsd = _PSD(colcov, allow_singular=False) + + return self._entropy(dims, rowpsd.log_pdet, colpsd.log_pdet) + + def _entropy(self, dims, row_cov_logdet, col_cov_logdet): + n, p = dims + return (0.5 * n * p * (1 + _LOG_2PI) + 0.5 * p * row_cov_logdet + + 0.5 * n * col_cov_logdet) + + +matrix_normal = matrix_normal_gen() + + +class matrix_normal_frozen(multi_rv_frozen): + """ + Create a frozen matrix normal distribution. + + Parameters + ---------- + %(_matnorm_doc_default_callparams)s + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is `None` the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import matrix_normal + + >>> distn = matrix_normal(mean=np.zeros((3,3))) + >>> X = distn.rvs(); X + array([[-0.02976962, 0.93339138, -0.09663178], + [ 0.67405524, 0.28250467, -0.93308929], + [-0.31144782, 0.74535536, 1.30412916]]) + >>> distn.pdf(X) + 2.5160642368346784e-05 + >>> distn.logpdf(X) + -10.590229595124615 + """ + + def __init__(self, mean=None, rowcov=1, colcov=1, seed=None): + self._dist = matrix_normal_gen(seed) + self.dims, self.mean, self.rowcov, self.colcov = \ + self._dist._process_parameters(mean, rowcov, colcov) + self.rowpsd = _PSD(self.rowcov, allow_singular=False) + self.colpsd = _PSD(self.colcov, allow_singular=False) + + def logpdf(self, X): + X = self._dist._process_quantiles(X, self.dims) + out = self._dist._logpdf(self.dims, X, self.mean, self.rowpsd.U, + self.rowpsd.log_pdet, self.colpsd.U, + self.colpsd.log_pdet) + return _squeeze_output(out) + + def pdf(self, X): + return np.exp(self.logpdf(X)) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.mean, self.rowcov, self.colcov, size, + random_state) + + def entropy(self): + return self._dist._entropy(self.dims, self.rowpsd.log_pdet, + self.colpsd.log_pdet) + + +# Set frozen generator docstrings from corresponding docstrings in +# matrix_normal_gen and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'rvs', 'entropy']: + method = matrix_normal_gen.__dict__[name] + method_frozen = matrix_normal_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat(method.__doc__, + matnorm_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, matnorm_docdict_params) + +_dirichlet_doc_default_callparams = """\ +alpha : array_like + The concentration parameters. The number of entries determines the + dimensionality of the distribution. +""" +_dirichlet_doc_frozen_callparams = "" + +_dirichlet_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +dirichlet_docdict_params = { + '_dirichlet_doc_default_callparams': _dirichlet_doc_default_callparams, + '_doc_random_state': _doc_random_state +} + +dirichlet_docdict_noparams = { + '_dirichlet_doc_default_callparams': _dirichlet_doc_frozen_callparams, + '_doc_random_state': _doc_random_state +} + + +def _dirichlet_check_parameters(alpha): + alpha = np.asarray(alpha) + if np.min(alpha) <= 0: + raise ValueError("All parameters must be greater than 0") + elif alpha.ndim != 1: + raise ValueError("Parameter vector 'a' must be one dimensional, " + f"but a.shape = {alpha.shape}.") + return alpha + + +def _dirichlet_check_input(alpha, x): + x = np.asarray(x) + + if x.shape[0] + 1 != alpha.shape[0] and x.shape[0] != alpha.shape[0]: + raise ValueError("Vector 'x' must have either the same number " + "of entries as, or one entry fewer than, " + f"parameter vector 'a', but alpha.shape = {alpha.shape} " + f"and x.shape = {x.shape}.") + + if x.shape[0] != alpha.shape[0]: + xk = np.array([1 - np.sum(x, 0)]) + if xk.ndim == 1: + x = np.append(x, xk) + elif xk.ndim == 2: + x = np.vstack((x, xk)) + else: + raise ValueError("The input must be one dimensional or a two " + "dimensional matrix containing the entries.") + + if np.min(x) < 0: + raise ValueError("Each entry in 'x' must be greater than or equal " + "to zero.") + + if np.max(x) > 1: + raise ValueError("Each entry in 'x' must be smaller or equal one.") + + # Check x_i > 0 or alpha_i > 1 + xeq0 = (x == 0) + alphalt1 = (alpha < 1) + if x.shape != alpha.shape: + alphalt1 = np.repeat(alphalt1, x.shape[-1], axis=-1).reshape(x.shape) + chk = np.logical_and(xeq0, alphalt1) + + if np.sum(chk): + raise ValueError("Each entry in 'x' must be greater than zero if its " + "alpha is less than one.") + + if (np.abs(np.sum(x, 0) - 1.0) > 10e-10).any(): + raise ValueError("The input vector 'x' must lie within the normal " + "simplex. but np.sum(x, 0) = %s." % np.sum(x, 0)) + + return x + + +def _lnB(alpha): + r"""Internal helper function to compute the log of the useful quotient. + + .. math:: + + B(\alpha) = \frac{\prod_{i=1}{K}\Gamma(\alpha_i)} + {\Gamma\left(\sum_{i=1}^{K} \alpha_i \right)} + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + B : scalar + Helper quotient, internal use only + + """ + return np.sum(gammaln(alpha)) - gammaln(np.sum(alpha)) + + +class dirichlet_gen(multi_rv_generic): + r"""A Dirichlet random variable. + + The ``alpha`` keyword specifies the concentration parameters of the + distribution. + + .. versionadded:: 0.15.0 + + Methods + ------- + pdf(x, alpha) + Probability density function. + logpdf(x, alpha) + Log of the probability density function. + rvs(alpha, size=1, random_state=None) + Draw random samples from a Dirichlet distribution. + mean(alpha) + The mean of the Dirichlet distribution + var(alpha) + The variance of the Dirichlet distribution + cov(alpha) + The covariance of the Dirichlet distribution + entropy(alpha) + Compute the differential entropy of the Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + Each :math:`\alpha` entry must be positive. The distribution has only + support on the simplex defined by + + .. math:: + \sum_{i=1}^{K} x_i = 1 + + where :math:`0 < x_i < 1`. + + If the quantiles don't lie within the simplex, a ValueError is raised. + + The probability density function for `dirichlet` is + + .. math:: + + f(x) = \frac{1}{\mathrm{B}(\boldsymbol\alpha)} \prod_{i=1}^K x_i^{\alpha_i - 1} + + where + + .. math:: + + \mathrm{B}(\boldsymbol\alpha) = \frac{\prod_{i=1}^K \Gamma(\alpha_i)} + {\Gamma\bigl(\sum_{i=1}^K \alpha_i\bigr)} + + and :math:`\boldsymbol\alpha=(\alpha_1,\ldots,\alpha_K)`, the + concentration parameters and :math:`K` is the dimension of the space + where :math:`x` takes values. + + Note that the `dirichlet` interface is somewhat inconsistent. + The array returned by the rvs function is transposed + with respect to the format expected by the pdf and logpdf. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import dirichlet + + Generate a dirichlet random variable + + >>> quantiles = np.array([0.2, 0.2, 0.6]) # specify quantiles + >>> alpha = np.array([0.4, 5, 15]) # specify concentration parameters + >>> dirichlet.pdf(quantiles, alpha) + 0.2843831684937255 + + The same PDF but following a log scale + + >>> dirichlet.logpdf(quantiles, alpha) + -1.2574327653159187 + + Once we specify the dirichlet distribution + we can then calculate quantities of interest + + >>> dirichlet.mean(alpha) # get the mean of the distribution + array([0.01960784, 0.24509804, 0.73529412]) + >>> dirichlet.var(alpha) # get variance + array([0.00089829, 0.00864603, 0.00909517]) + >>> dirichlet.entropy(alpha) # calculate the differential entropy + -4.3280162474082715 + + We can also return random samples from the distribution + + >>> dirichlet.rvs(alpha, size=1, random_state=1) + array([[0.00766178, 0.24670518, 0.74563305]]) + >>> dirichlet.rvs(alpha, size=2, random_state=2) + array([[0.01639427, 0.1292273 , 0.85437844], + [0.00156917, 0.19033695, 0.80809388]]) + + Alternatively, the object may be called (as a function) to fix + concentration parameters, returning a "frozen" Dirichlet + random variable: + + >>> rv = dirichlet(alpha) + >>> # Frozen object with the same methods but holding the given + >>> # concentration parameters fixed. + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, dirichlet_docdict_params) + + def __call__(self, alpha, seed=None): + return dirichlet_frozen(alpha, seed=seed) + + def _logpdf(self, x, alpha): + """Log of the Dirichlet probability density function. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the log of the probability + density function + %(_dirichlet_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + lnB = _lnB(alpha) + return - lnB + np.sum((xlogy(alpha - 1, x.T)).T, 0) + + def logpdf(self, x, alpha): + """Log of the Dirichlet probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + pdf : ndarray or scalar + Log of the probability density function evaluated at `x`. + + """ + alpha = _dirichlet_check_parameters(alpha) + x = _dirichlet_check_input(alpha, x) + + out = self._logpdf(x, alpha) + return _squeeze_output(out) + + def pdf(self, x, alpha): + """The Dirichlet probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + pdf : ndarray or scalar + The probability density function evaluated at `x`. + + """ + alpha = _dirichlet_check_parameters(alpha) + x = _dirichlet_check_input(alpha, x) + + out = np.exp(self._logpdf(x, alpha)) + return _squeeze_output(out) + + def mean(self, alpha): + """Mean of the Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + mu : ndarray or scalar + Mean of the Dirichlet distribution. + + """ + alpha = _dirichlet_check_parameters(alpha) + + out = alpha / (np.sum(alpha)) + return _squeeze_output(out) + + def var(self, alpha): + """Variance of the Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + v : ndarray or scalar + Variance of the Dirichlet distribution. + + """ + + alpha = _dirichlet_check_parameters(alpha) + + alpha0 = np.sum(alpha) + out = (alpha * (alpha0 - alpha)) / ((alpha0 * alpha0) * (alpha0 + 1)) + return _squeeze_output(out) + + def cov(self, alpha): + """Covariance matrix of the Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + cov : ndarray + The covariance matrix of the distribution. + """ + + alpha = _dirichlet_check_parameters(alpha) + alpha0 = np.sum(alpha) + a = alpha / alpha0 + + cov = (np.diag(a) - np.outer(a, a)) / (alpha0 + 1) + return _squeeze_output(cov) + + def entropy(self, alpha): + """ + Differential entropy of the Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + + Returns + ------- + h : scalar + Entropy of the Dirichlet distribution + + """ + + alpha = _dirichlet_check_parameters(alpha) + + alpha0 = np.sum(alpha) + lnB = _lnB(alpha) + K = alpha.shape[0] + + out = lnB + (alpha0 - K) * scipy.special.psi(alpha0) - np.sum( + (alpha - 1) * scipy.special.psi(alpha)) + return _squeeze_output(out) + + def rvs(self, alpha, size=1, random_state=None): + """ + Draw random samples from a Dirichlet distribution. + + Parameters + ---------- + %(_dirichlet_doc_default_callparams)s + size : int, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray or scalar + Random variates of size (`size`, `N`), where `N` is the + dimension of the random variable. + + """ + alpha = _dirichlet_check_parameters(alpha) + random_state = self._get_random_state(random_state) + return random_state.dirichlet(alpha, size=size) + + +dirichlet = dirichlet_gen() + + +class dirichlet_frozen(multi_rv_frozen): + def __init__(self, alpha, seed=None): + self.alpha = _dirichlet_check_parameters(alpha) + self._dist = dirichlet_gen(seed) + + def logpdf(self, x): + return self._dist.logpdf(x, self.alpha) + + def pdf(self, x): + return self._dist.pdf(x, self.alpha) + + def mean(self): + return self._dist.mean(self.alpha) + + def var(self): + return self._dist.var(self.alpha) + + def cov(self): + return self._dist.cov(self.alpha) + + def entropy(self): + return self._dist.entropy(self.alpha) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.alpha, size, random_state) + + +# Set frozen generator docstrings from corresponding docstrings in +# multivariate_normal_gen and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'rvs', 'mean', 'var', 'cov', 'entropy']: + method = dirichlet_gen.__dict__[name] + method_frozen = dirichlet_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, dirichlet_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, dirichlet_docdict_params) + + +_wishart_doc_default_callparams = """\ +df : int + Degrees of freedom, must be greater than or equal to dimension of the + scale matrix +scale : array_like + Symmetric positive definite scale matrix of the distribution +""" + +_wishart_doc_callparams_note = "" + +_wishart_doc_frozen_callparams = "" + +_wishart_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +wishart_docdict_params = { + '_doc_default_callparams': _wishart_doc_default_callparams, + '_doc_callparams_note': _wishart_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +wishart_docdict_noparams = { + '_doc_default_callparams': _wishart_doc_frozen_callparams, + '_doc_callparams_note': _wishart_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class wishart_gen(multi_rv_generic): + r"""A Wishart random variable. + + The `df` keyword specifies the degrees of freedom. The `scale` keyword + specifies the scale matrix, which must be symmetric and positive definite. + In this context, the scale matrix is often interpreted in terms of a + multivariate normal precision matrix (the inverse of the covariance + matrix). These arguments must satisfy the relationship + ``df > scale.ndim - 1``, but see notes on using the `rvs` method with + ``df < scale.ndim``. + + Methods + ------- + pdf(x, df, scale) + Probability density function. + logpdf(x, df, scale) + Log of the probability density function. + rvs(df, scale, size=1, random_state=None) + Draw random samples from a Wishart distribution. + entropy() + Compute the differential entropy of the Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + %(_doc_random_state)s + + Raises + ------ + scipy.linalg.LinAlgError + If the scale matrix `scale` is not positive definite. + + See Also + -------- + invwishart, chi2 + + Notes + ----- + %(_doc_callparams_note)s + + The scale matrix `scale` must be a symmetric positive definite + matrix. Singular matrices, including the symmetric positive semi-definite + case, are not supported. Symmetry is not checked; only the lower triangular + portion is used. + + The Wishart distribution is often denoted + + .. math:: + + W_p(\nu, \Sigma) + + where :math:`\nu` is the degrees of freedom and :math:`\Sigma` is the + :math:`p \times p` scale matrix. + + The probability density function for `wishart` has support over positive + definite matrices :math:`S`; if :math:`S \sim W_p(\nu, \Sigma)`, then + its PDF is given by: + + .. math:: + + f(S) = \frac{|S|^{\frac{\nu - p - 1}{2}}}{2^{ \frac{\nu p}{2} } + |\Sigma|^\frac{\nu}{2} \Gamma_p \left ( \frac{\nu}{2} \right )} + \exp\left( -tr(\Sigma^{-1} S) / 2 \right) + + If :math:`S \sim W_p(\nu, \Sigma)` (Wishart) then + :math:`S^{-1} \sim W_p^{-1}(\nu, \Sigma^{-1})` (inverse Wishart). + + If the scale matrix is 1-dimensional and equal to one, then the Wishart + distribution :math:`W_1(\nu, 1)` collapses to the :math:`\chi^2(\nu)` + distribution. + + The algorithm [2]_ implemented by the `rvs` method may + produce numerically singular matrices with :math:`p - 1 < \nu < p`; the + user may wish to check for this condition and generate replacement samples + as necessary. + + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] M.L. Eaton, "Multivariate Statistics: A Vector Space Approach", + Wiley, 1983. + .. [2] W.B. Smith and R.R. Hocking, "Algorithm AS 53: Wishart Variate + Generator", Applied Statistics, vol. 21, pp. 341-345, 1972. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import wishart, chi2 + >>> x = np.linspace(1e-5, 8, 100) + >>> w = wishart.pdf(x, df=3, scale=1); w[:5] + array([ 0.00126156, 0.10892176, 0.14793434, 0.17400548, 0.1929669 ]) + >>> c = chi2.pdf(x, 3); c[:5] + array([ 0.00126156, 0.10892176, 0.14793434, 0.17400548, 0.1929669 ]) + >>> plt.plot(x, w) + >>> plt.show() + + The input quantiles can be any shape of array, as long as the last + axis labels the components. + + Alternatively, the object may be called (as a function) to fix the degrees + of freedom and scale parameters, returning a "frozen" Wishart random + variable: + + >>> rv = wishart(df=1, scale=1) + >>> # Frozen object with the same methods but holding the given + >>> # degrees of freedom and scale fixed. + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, wishart_docdict_params) + + def __call__(self, df=None, scale=None, seed=None): + """Create a frozen Wishart distribution. + + See `wishart_frozen` for more information. + """ + return wishart_frozen(df, scale, seed) + + def _process_parameters(self, df, scale): + if scale is None: + scale = 1.0 + scale = np.asarray(scale, dtype=float) + + if scale.ndim == 0: + scale = scale[np.newaxis, np.newaxis] + elif scale.ndim == 1: + scale = np.diag(scale) + elif scale.ndim == 2 and not scale.shape[0] == scale.shape[1]: + raise ValueError("Array 'scale' must be square if it is two" + " dimensional, but scale.scale = %s." + % str(scale.shape)) + elif scale.ndim > 2: + raise ValueError("Array 'scale' must be at most two-dimensional," + " but scale.ndim = %d" % scale.ndim) + + dim = scale.shape[0] + + if df is None: + df = dim + elif not np.isscalar(df): + raise ValueError("Degrees of freedom must be a scalar.") + elif df <= dim - 1: + raise ValueError("Degrees of freedom must be greater than the " + "dimension of scale matrix minus 1.") + + return dim, df, scale + + def _process_quantiles(self, x, dim): + """ + Adjust quantiles array so that last axis labels the components of + each data point. + """ + x = np.asarray(x, dtype=float) + + if x.ndim == 0: + x = x * np.eye(dim)[:, :, np.newaxis] + if x.ndim == 1: + if dim == 1: + x = x[np.newaxis, np.newaxis, :] + else: + x = np.diag(x)[:, :, np.newaxis] + elif x.ndim == 2: + if not x.shape[0] == x.shape[1]: + raise ValueError("Quantiles must be square if they are two" + " dimensional, but x.shape = %s." + % str(x.shape)) + x = x[:, :, np.newaxis] + elif x.ndim == 3: + if not x.shape[0] == x.shape[1]: + raise ValueError("Quantiles must be square in the first two" + " dimensions if they are three dimensional" + ", but x.shape = %s." % str(x.shape)) + elif x.ndim > 3: + raise ValueError("Quantiles must be at most two-dimensional with" + " an additional dimension for multiple" + "components, but x.ndim = %d" % x.ndim) + + # Now we have 3-dim array; should have shape [dim, dim, *] + if not x.shape[0:2] == (dim, dim): + raise ValueError('Quantiles have incompatible dimensions: should' + f' be {(dim, dim)}, got {x.shape[0:2]}.') + + return x + + def _process_size(self, size): + size = np.asarray(size) + + if size.ndim == 0: + size = size[np.newaxis] + elif size.ndim > 1: + raise ValueError('Size must be an integer or tuple of integers;' + ' thus must have dimension <= 1.' + ' Got size.ndim = %s' % str(tuple(size))) + n = size.prod() + shape = tuple(size) + + return n, shape + + def _logpdf(self, x, dim, df, scale, log_det_scale, C): + """Log of the Wishart probability density function. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the log of the probability + density function + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + scale : ndarray + Scale matrix + log_det_scale : float + Logarithm of the determinant of the scale matrix + C : ndarray + Cholesky factorization of the scale matrix, lower triagular. + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + # log determinant of x + # Note: x has components along the last axis, so that x.T has + # components alone the 0-th axis. Then since det(A) = det(A'), this + # gives us a 1-dim vector of determinants + + # Retrieve tr(scale^{-1} x) + log_det_x = np.empty(x.shape[-1]) + scale_inv_x = np.empty(x.shape) + tr_scale_inv_x = np.empty(x.shape[-1]) + for i in range(x.shape[-1]): + _, log_det_x[i] = self._cholesky_logdet(x[:, :, i]) + scale_inv_x[:, :, i] = scipy.linalg.cho_solve((C, True), x[:, :, i]) + tr_scale_inv_x[i] = scale_inv_x[:, :, i].trace() + + # Log PDF + out = ((0.5 * (df - dim - 1) * log_det_x - 0.5 * tr_scale_inv_x) - + (0.5 * df * dim * _LOG_2 + 0.5 * df * log_det_scale + + multigammaln(0.5*df, dim))) + + return out + + def logpdf(self, x, df, scale): + """Log of the Wishart probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + Each quantile must be a symmetric positive definite matrix. + %(_doc_default_callparams)s + + Returns + ------- + pdf : ndarray + Log of the probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + + """ + dim, df, scale = self._process_parameters(df, scale) + x = self._process_quantiles(x, dim) + + # Cholesky decomposition of scale, get log(det(scale)) + C, log_det_scale = self._cholesky_logdet(scale) + + out = self._logpdf(x, dim, df, scale, log_det_scale, C) + return _squeeze_output(out) + + def pdf(self, x, df, scale): + """Wishart probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + Each quantile must be a symmetric positive definite matrix. + %(_doc_default_callparams)s + + Returns + ------- + pdf : ndarray + Probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + + """ + return np.exp(self.logpdf(x, df, scale)) + + def _mean(self, dim, df, scale): + """Mean of the Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'mean' instead. + + """ + return df * scale + + def mean(self, df, scale): + """Mean of the Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mean : float + The mean of the distribution + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._mean(dim, df, scale) + return _squeeze_output(out) + + def _mode(self, dim, df, scale): + """Mode of the Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'mode' instead. + + """ + if df >= dim + 1: + out = (df-dim-1) * scale + else: + out = None + return out + + def mode(self, df, scale): + """Mode of the Wishart distribution + + Only valid if the degrees of freedom are greater than the dimension of + the scale matrix. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mode : float or None + The Mode of the distribution + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._mode(dim, df, scale) + return _squeeze_output(out) if out is not None else out + + def _var(self, dim, df, scale): + """Variance of the Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'var' instead. + + """ + var = scale**2 + diag = scale.diagonal() # 1 x dim array + var += np.outer(diag, diag) + var *= df + return var + + def var(self, df, scale): + """Variance of the Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + var : float + The variance of the distribution + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._var(dim, df, scale) + return _squeeze_output(out) + + def _standard_rvs(self, n, shape, dim, df, random_state): + """ + Parameters + ---------- + n : integer + Number of variates to generate + shape : iterable + Shape of the variates to generate + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'rvs' instead. + + """ + # Random normal variates for off-diagonal elements + n_tril = dim * (dim-1) // 2 + covariances = random_state.normal( + size=n*n_tril).reshape(shape+(n_tril,)) + + # Random chi-square variates for diagonal elements + variances = (np.r_[[random_state.chisquare(df-(i+1)+1, size=n)**0.5 + for i in range(dim)]].reshape((dim,) + + shape[::-1]).T) + + # Create the A matri(ces) - lower triangular + A = np.zeros(shape + (dim, dim)) + + # Input the covariances + size_idx = tuple([slice(None, None, None)]*len(shape)) + tril_idx = np.tril_indices(dim, k=-1) + A[size_idx + tril_idx] = covariances + + # Input the variances + diag_idx = np.diag_indices(dim) + A[size_idx + diag_idx] = variances + + return A + + def _rvs(self, n, shape, dim, df, C, random_state): + """Draw random samples from a Wishart distribution. + + Parameters + ---------- + n : integer + Number of variates to generate + shape : iterable + Shape of the variates to generate + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + C : ndarray + Cholesky factorization of the scale matrix, lower triangular. + %(_doc_random_state)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'rvs' instead. + + """ + random_state = self._get_random_state(random_state) + # Calculate the matrices A, which are actually lower triangular + # Cholesky factorizations of a matrix B such that B ~ W(df, I) + A = self._standard_rvs(n, shape, dim, df, random_state) + + # Calculate SA = C A A' C', where SA ~ W(df, scale) + # Note: this is the product of a (lower) (lower) (lower)' (lower)' + # or, denoting B = AA', it is C B C' where C is the lower + # triangular Cholesky factorization of the scale matrix. + # this appears to conflict with the instructions in [1]_, which + # suggest that it should be D' B D where D is the lower + # triangular factorization of the scale matrix. However, it is + # meant to refer to the Bartlett (1933) representation of a + # Wishart random variate as L A A' L' where L is lower triangular + # so it appears that understanding D' to be upper triangular + # is either a typo in or misreading of [1]_. + for index in np.ndindex(shape): + CA = np.dot(C, A[index]) + A[index] = np.dot(CA, CA.T) + + return A + + def rvs(self, df, scale, size=1, random_state=None): + """Draw random samples from a Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + size : integer or iterable of integers, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray + Random variates of shape (`size`) + (``dim``, ``dim``), where + ``dim`` is the dimension of the scale matrix. + + Notes + ----- + %(_doc_callparams_note)s + + """ + n, shape = self._process_size(size) + dim, df, scale = self._process_parameters(df, scale) + + # Cholesky decomposition of scale + C = scipy.linalg.cholesky(scale, lower=True) + + out = self._rvs(n, shape, dim, df, C, random_state) + + return _squeeze_output(out) + + def _entropy(self, dim, df, log_det_scale): + """Compute the differential entropy of the Wishart. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + log_det_scale : float + Logarithm of the determinant of the scale matrix + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'entropy' instead. + + """ + return ( + 0.5 * (dim+1) * log_det_scale + + 0.5 * dim * (dim+1) * _LOG_2 + + multigammaln(0.5*df, dim) - + 0.5 * (df - dim - 1) * np.sum( + [psi(0.5*(df + 1 - (i+1))) for i in range(dim)] + ) + + 0.5 * df * dim + ) + + def entropy(self, df, scale): + """Compute the differential entropy of the Wishart. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + h : scalar + Entropy of the Wishart distribution + + Notes + ----- + %(_doc_callparams_note)s + + """ + dim, df, scale = self._process_parameters(df, scale) + _, log_det_scale = self._cholesky_logdet(scale) + return self._entropy(dim, df, log_det_scale) + + def _cholesky_logdet(self, scale): + """Compute Cholesky decomposition and determine (log(det(scale)). + + Parameters + ---------- + scale : ndarray + Scale matrix. + + Returns + ------- + c_decomp : ndarray + The Cholesky decomposition of `scale`. + logdet : scalar + The log of the determinant of `scale`. + + Notes + ----- + This computation of ``logdet`` is equivalent to + ``np.linalg.slogdet(scale)``. It is ~2x faster though. + + """ + c_decomp = scipy.linalg.cholesky(scale, lower=True) + logdet = 2 * np.sum(np.log(c_decomp.diagonal())) + return c_decomp, logdet + + +wishart = wishart_gen() + + +class wishart_frozen(multi_rv_frozen): + """Create a frozen Wishart distribution. + + Parameters + ---------- + df : array_like + Degrees of freedom of the distribution + scale : array_like + Scale matrix of the distribution + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + """ + def __init__(self, df, scale, seed=None): + self._dist = wishart_gen(seed) + self.dim, self.df, self.scale = self._dist._process_parameters( + df, scale) + self.C, self.log_det_scale = self._dist._cholesky_logdet(self.scale) + + def logpdf(self, x): + x = self._dist._process_quantiles(x, self.dim) + + out = self._dist._logpdf(x, self.dim, self.df, self.scale, + self.log_det_scale, self.C) + return _squeeze_output(out) + + def pdf(self, x): + return np.exp(self.logpdf(x)) + + def mean(self): + out = self._dist._mean(self.dim, self.df, self.scale) + return _squeeze_output(out) + + def mode(self): + out = self._dist._mode(self.dim, self.df, self.scale) + return _squeeze_output(out) if out is not None else out + + def var(self): + out = self._dist._var(self.dim, self.df, self.scale) + return _squeeze_output(out) + + def rvs(self, size=1, random_state=None): + n, shape = self._dist._process_size(size) + out = self._dist._rvs(n, shape, self.dim, self.df, + self.C, random_state) + return _squeeze_output(out) + + def entropy(self): + return self._dist._entropy(self.dim, self.df, self.log_det_scale) + + +# Set frozen generator docstrings from corresponding docstrings in +# Wishart and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'mean', 'mode', 'var', 'rvs', 'entropy']: + method = wishart_gen.__dict__[name] + method_frozen = wishart_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, wishart_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, wishart_docdict_params) + + +class invwishart_gen(wishart_gen): + r"""An inverse Wishart random variable. + + The `df` keyword specifies the degrees of freedom. The `scale` keyword + specifies the scale matrix, which must be symmetric and positive definite. + In this context, the scale matrix is often interpreted in terms of a + multivariate normal covariance matrix. + + Methods + ------- + pdf(x, df, scale) + Probability density function. + logpdf(x, df, scale) + Log of the probability density function. + rvs(df, scale, size=1, random_state=None) + Draw random samples from an inverse Wishart distribution. + entropy(df, scale) + Differential entropy of the distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + %(_doc_random_state)s + + Raises + ------ + scipy.linalg.LinAlgError + If the scale matrix `scale` is not positive definite. + + See Also + -------- + wishart + + Notes + ----- + %(_doc_callparams_note)s + + The scale matrix `scale` must be a symmetric positive definite + matrix. Singular matrices, including the symmetric positive semi-definite + case, are not supported. Symmetry is not checked; only the lower triangular + portion is used. + + The inverse Wishart distribution is often denoted + + .. math:: + + W_p^{-1}(\nu, \Psi) + + where :math:`\nu` is the degrees of freedom and :math:`\Psi` is the + :math:`p \times p` scale matrix. + + The probability density function for `invwishart` has support over positive + definite matrices :math:`S`; if :math:`S \sim W^{-1}_p(\nu, \Sigma)`, + then its PDF is given by: + + .. math:: + + f(S) = \frac{|\Sigma|^\frac{\nu}{2}}{2^{ \frac{\nu p}{2} } + |S|^{\frac{\nu + p + 1}{2}} \Gamma_p \left(\frac{\nu}{2} \right)} + \exp\left( -tr(\Sigma S^{-1}) / 2 \right) + + If :math:`S \sim W_p^{-1}(\nu, \Psi)` (inverse Wishart) then + :math:`S^{-1} \sim W_p(\nu, \Psi^{-1})` (Wishart). + + If the scale matrix is 1-dimensional and equal to one, then the inverse + Wishart distribution :math:`W_1(\nu, 1)` collapses to the + inverse Gamma distribution with parameters shape = :math:`\frac{\nu}{2}` + and scale = :math:`\frac{1}{2}`. + + Instead of inverting a randomly generated Wishart matrix as described in [2], + here the algorithm in [4] is used to directly generate a random inverse-Wishart + matrix without inversion. + + .. versionadded:: 0.16.0 + + References + ---------- + .. [1] M.L. Eaton, "Multivariate Statistics: A Vector Space Approach", + Wiley, 1983. + .. [2] M.C. Jones, "Generating Inverse Wishart Matrices", Communications + in Statistics - Simulation and Computation, vol. 14.2, pp.511-514, + 1985. + .. [3] Gupta, M. and Srivastava, S. "Parametric Bayesian Estimation of + Differential Entropy and Relative Entropy". Entropy 12, 818 - 843. + 2010. + .. [4] S.D. Axen, "Efficiently generating inverse-Wishart matrices and + their Cholesky factors", :arXiv:`2310.15884v1`. 2023. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import invwishart, invgamma + >>> x = np.linspace(0.01, 1, 100) + >>> iw = invwishart.pdf(x, df=6, scale=1) + >>> iw[:3] + array([ 1.20546865e-15, 5.42497807e-06, 4.45813929e-03]) + >>> ig = invgamma.pdf(x, 6/2., scale=1./2) + >>> ig[:3] + array([ 1.20546865e-15, 5.42497807e-06, 4.45813929e-03]) + >>> plt.plot(x, iw) + >>> plt.show() + + The input quantiles can be any shape of array, as long as the last + axis labels the components. + + Alternatively, the object may be called (as a function) to fix the degrees + of freedom and scale parameters, returning a "frozen" inverse Wishart + random variable: + + >>> rv = invwishart(df=1, scale=1) + >>> # Frozen object with the same methods but holding the given + >>> # degrees of freedom and scale fixed. + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, wishart_docdict_params) + + def __call__(self, df=None, scale=None, seed=None): + """Create a frozen inverse Wishart distribution. + + See `invwishart_frozen` for more information. + + """ + return invwishart_frozen(df, scale, seed) + + def _logpdf(self, x, dim, df, log_det_scale, C): + """Log of the inverse Wishart probability density function. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the log of the probability + density function. + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + log_det_scale : float + Logarithm of the determinant of the scale matrix + C : ndarray + Cholesky factorization of the scale matrix, lower triagular. + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + # Retrieve tr(scale x^{-1}) + log_det_x = np.empty(x.shape[-1]) + tr_scale_x_inv = np.empty(x.shape[-1]) + trsm = get_blas_funcs(('trsm'), (x,)) + if dim > 1: + for i in range(x.shape[-1]): + Cx, log_det_x[i] = self._cholesky_logdet(x[:, :, i]) + A = trsm(1., Cx, C, side=0, lower=True) + tr_scale_x_inv[i] = np.linalg.norm(A)**2 + else: + log_det_x[:] = np.log(x[0, 0]) + tr_scale_x_inv[:] = C[0, 0]**2 / x[0, 0] + + # Log PDF + out = ((0.5 * df * log_det_scale - 0.5 * tr_scale_x_inv) - + (0.5 * df * dim * _LOG_2 + 0.5 * (df + dim + 1) * log_det_x) - + multigammaln(0.5*df, dim)) + + return out + + def logpdf(self, x, df, scale): + """Log of the inverse Wishart probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + Each quantile must be a symmetric positive definite matrix. + %(_doc_default_callparams)s + + Returns + ------- + pdf : ndarray + Log of the probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + + """ + dim, df, scale = self._process_parameters(df, scale) + x = self._process_quantiles(x, dim) + C, log_det_scale = self._cholesky_logdet(scale) + out = self._logpdf(x, dim, df, log_det_scale, C) + return _squeeze_output(out) + + def pdf(self, x, df, scale): + """Inverse Wishart probability density function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + Each quantile must be a symmetric positive definite matrix. + %(_doc_default_callparams)s + + Returns + ------- + pdf : ndarray + Probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + + """ + return np.exp(self.logpdf(x, df, scale)) + + def _mean(self, dim, df, scale): + """Mean of the inverse Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'mean' instead. + + """ + if df > dim + 1: + out = scale / (df - dim - 1) + else: + out = None + return out + + def mean(self, df, scale): + """Mean of the inverse Wishart distribution. + + Only valid if the degrees of freedom are greater than the dimension of + the scale matrix plus one. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mean : float or None + The mean of the distribution + + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._mean(dim, df, scale) + return _squeeze_output(out) if out is not None else out + + def _mode(self, dim, df, scale): + """Mode of the inverse Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'mode' instead. + + """ + return scale / (df + dim + 1) + + def mode(self, df, scale): + """Mode of the inverse Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mode : float + The Mode of the distribution + + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._mode(dim, df, scale) + return _squeeze_output(out) + + def _var(self, dim, df, scale): + """Variance of the inverse Wishart distribution. + + Parameters + ---------- + dim : int + Dimension of the scale matrix + %(_doc_default_callparams)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'var' instead. + + """ + if df > dim + 3: + var = (df - dim + 1) * scale**2 + diag = scale.diagonal() # 1 x dim array + var += (df - dim - 1) * np.outer(diag, diag) + var /= (df - dim) * (df - dim - 1)**2 * (df - dim - 3) + else: + var = None + return var + + def var(self, df, scale): + """Variance of the inverse Wishart distribution. + + Only valid if the degrees of freedom are greater than the dimension of + the scale matrix plus three. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + var : float + The variance of the distribution + """ + dim, df, scale = self._process_parameters(df, scale) + out = self._var(dim, df, scale) + return _squeeze_output(out) if out is not None else out + + def _inv_standard_rvs(self, n, shape, dim, df, random_state): + """ + Parameters + ---------- + n : integer + Number of variates to generate + shape : iterable + Shape of the variates to generate + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Returns + ------- + A : ndarray + Random variates of shape (`shape`) + (``dim``, ``dim``). + Each slice `A[..., :, :]` is lower-triangular, and its + inverse is the lower Cholesky factor of a draw from + `invwishart(df, np.eye(dim))`. + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'rvs' instead. + + """ + A = np.zeros(shape + (dim, dim)) + + # Random normal variates for off-diagonal elements + tri_rows, tri_cols = np.tril_indices(dim, k=-1) + n_tril = dim * (dim-1) // 2 + A[..., tri_rows, tri_cols] = random_state.normal( + size=(*shape, n_tril), + ) + + # Random chi variates for diagonal elements + rows = np.arange(dim) + chi_dfs = (df - dim + 1) + rows + A[..., rows, rows] = random_state.chisquare( + df=chi_dfs, size=(*shape, dim), + )**0.5 + + return A + + def _rvs(self, n, shape, dim, df, C, random_state): + """Draw random samples from an inverse Wishart distribution. + + Parameters + ---------- + n : integer + Number of variates to generate + shape : iterable + Shape of the variates to generate + dim : int + Dimension of the scale matrix + df : int + Degrees of freedom + C : ndarray + Cholesky factorization of the scale matrix, lower triagular. + %(_doc_random_state)s + + Notes + ----- + As this function does no argument checking, it should not be + called directly; use 'rvs' instead. + + """ + random_state = self._get_random_state(random_state) + # Get random draws A such that inv(A) ~ iW(df, I) + A = self._inv_standard_rvs(n, shape, dim, df, random_state) + + # Calculate SA = (CA)'^{-1} (CA)^{-1} ~ iW(df, scale) + trsm = get_blas_funcs(('trsm'), (A,)) + trmm = get_blas_funcs(('trmm'), (A,)) + + for index in np.ndindex(A.shape[:-2]): + if dim > 1: + # Calculate CA + # Get CA = C A^{-1} via triangular solver + CA = trsm(1., A[index], C, side=1, lower=True) + # get SA + A[index] = trmm(1., CA, CA, side=1, lower=True, trans_a=True) + else: + A[index][0, 0] = (C[0, 0] / A[index][0, 0])**2 + + return A + + def rvs(self, df, scale, size=1, random_state=None): + """Draw random samples from an inverse Wishart distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + size : integer or iterable of integers, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray + Random variates of shape (`size`) + (``dim``, ``dim``), where + ``dim`` is the dimension of the scale matrix. + + Notes + ----- + %(_doc_callparams_note)s + + """ + n, shape = self._process_size(size) + dim, df, scale = self._process_parameters(df, scale) + + # Cholesky decomposition of scale + C = scipy.linalg.cholesky(scale, lower=True) + + out = self._rvs(n, shape, dim, df, C, random_state) + + return _squeeze_output(out) + + def _entropy(self, dim, df, log_det_scale): + # reference: eq. (17) from ref. 3 + psi_eval_points = [0.5 * (df - dim + i) for i in range(1, dim + 1)] + psi_eval_points = np.asarray(psi_eval_points) + return multigammaln(0.5 * df, dim) + 0.5 * dim * df + \ + 0.5 * (dim + 1) * (log_det_scale - _LOG_2) - \ + 0.5 * (df + dim + 1) * \ + psi(psi_eval_points, out=psi_eval_points).sum() + + def entropy(self, df, scale): + dim, df, scale = self._process_parameters(df, scale) + _, log_det_scale = self._cholesky_logdet(scale) + return self._entropy(dim, df, log_det_scale) + + +invwishart = invwishart_gen() + + +class invwishart_frozen(multi_rv_frozen): + def __init__(self, df, scale, seed=None): + """Create a frozen inverse Wishart distribution. + + Parameters + ---------- + df : array_like + Degrees of freedom of the distribution + scale : array_like + Scale matrix of the distribution + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is None the `numpy.random.Generator` singleton is used. + If `seed` is an int, a new ``Generator`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` instance then that instance is + used. + + """ + self._dist = invwishart_gen(seed) + self.dim, self.df, self.scale = self._dist._process_parameters( + df, scale + ) + + # Get the determinant via Cholesky factorization + self.C = scipy.linalg.cholesky(self.scale, lower=True) + self.log_det_scale = 2 * np.sum(np.log(self.C.diagonal())) + + def logpdf(self, x): + x = self._dist._process_quantiles(x, self.dim) + out = self._dist._logpdf(x, self.dim, self.df, + self.log_det_scale, self.C) + return _squeeze_output(out) + + def pdf(self, x): + return np.exp(self.logpdf(x)) + + def mean(self): + out = self._dist._mean(self.dim, self.df, self.scale) + return _squeeze_output(out) if out is not None else out + + def mode(self): + out = self._dist._mode(self.dim, self.df, self.scale) + return _squeeze_output(out) + + def var(self): + out = self._dist._var(self.dim, self.df, self.scale) + return _squeeze_output(out) if out is not None else out + + def rvs(self, size=1, random_state=None): + n, shape = self._dist._process_size(size) + + out = self._dist._rvs(n, shape, self.dim, self.df, + self.C, random_state) + + return _squeeze_output(out) + + def entropy(self): + return self._dist._entropy(self.dim, self.df, self.log_det_scale) + + +# Set frozen generator docstrings from corresponding docstrings in +# inverse Wishart and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'mean', 'mode', 'var', 'rvs']: + method = invwishart_gen.__dict__[name] + method_frozen = wishart_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, wishart_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, wishart_docdict_params) + +_multinomial_doc_default_callparams = """\ +n : int + Number of trials +p : array_like + Probability of a trial falling into each category; should sum to 1 +""" + +_multinomial_doc_callparams_note = """\ +`n` should be a nonnegative integer. Each element of `p` should be in the +interval :math:`[0,1]` and the elements should sum to 1. If they do not sum to +1, the last element of the `p` array is not used and is replaced with the +remaining probability left over from the earlier elements. +""" + +_multinomial_doc_frozen_callparams = "" + +_multinomial_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +multinomial_docdict_params = { + '_doc_default_callparams': _multinomial_doc_default_callparams, + '_doc_callparams_note': _multinomial_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +multinomial_docdict_noparams = { + '_doc_default_callparams': _multinomial_doc_frozen_callparams, + '_doc_callparams_note': _multinomial_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class multinomial_gen(multi_rv_generic): + r"""A multinomial random variable. + + Methods + ------- + pmf(x, n, p) + Probability mass function. + logpmf(x, n, p) + Log of the probability mass function. + rvs(n, p, size=1, random_state=None) + Draw random samples from a multinomial distribution. + entropy(n, p) + Compute the entropy of the multinomial distribution. + cov(n, p) + Compute the covariance matrix of the multinomial distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + %(_doc_callparams_note)s + + The probability mass function for `multinomial` is + + .. math:: + + f(x) = \frac{n!}{x_1! \cdots x_k!} p_1^{x_1} \cdots p_k^{x_k}, + + supported on :math:`x=(x_1, \ldots, x_k)` where each :math:`x_i` is a + nonnegative integer and their sum is :math:`n`. + + .. versionadded:: 0.19.0 + + Examples + -------- + + >>> from scipy.stats import multinomial + >>> rv = multinomial(8, [0.3, 0.2, 0.5]) + >>> rv.pmf([1, 3, 4]) + 0.042000000000000072 + + The multinomial distribution for :math:`k=2` is identical to the + corresponding binomial distribution (tiny numerical differences + notwithstanding): + + >>> from scipy.stats import binom + >>> multinomial.pmf([3, 4], n=7, p=[0.4, 0.6]) + 0.29030399999999973 + >>> binom.pmf(3, 7, 0.4) + 0.29030400000000012 + + The functions ``pmf``, ``logpmf``, ``entropy``, and ``cov`` support + broadcasting, under the convention that the vector parameters (``x`` and + ``p``) are interpreted as if each row along the last axis is a single + object. For instance: + + >>> multinomial.pmf([[3, 4], [3, 5]], n=[7, 8], p=[.3, .7]) + array([0.2268945, 0.25412184]) + + Here, ``x.shape == (2, 2)``, ``n.shape == (2,)``, and ``p.shape == (2,)``, + but following the rules mentioned above they behave as if the rows + ``[3, 4]`` and ``[3, 5]`` in ``x`` and ``[.3, .7]`` in ``p`` were a single + object, and as if we had ``x.shape = (2,)``, ``n.shape = (2,)``, and + ``p.shape = ()``. To obtain the individual elements without broadcasting, + we would do this: + + >>> multinomial.pmf([3, 4], n=7, p=[.3, .7]) + 0.2268945 + >>> multinomial.pmf([3, 5], 8, p=[.3, .7]) + 0.25412184 + + This broadcasting also works for ``cov``, where the output objects are + square matrices of size ``p.shape[-1]``. For example: + + >>> multinomial.cov([4, 5], [[.3, .7], [.4, .6]]) + array([[[ 0.84, -0.84], + [-0.84, 0.84]], + [[ 1.2 , -1.2 ], + [-1.2 , 1.2 ]]]) + + In this example, ``n.shape == (2,)`` and ``p.shape == (2, 2)``, and + following the rules above, these broadcast as if ``p.shape == (2,)``. + Thus the result should also be of shape ``(2,)``, but since each output is + a :math:`2 \times 2` matrix, the result in fact has shape ``(2, 2, 2)``, + where ``result[0]`` is equal to ``multinomial.cov(n=4, p=[.3, .7])`` and + ``result[1]`` is equal to ``multinomial.cov(n=5, p=[.4, .6])``. + + Alternatively, the object may be called (as a function) to fix the `n` and + `p` parameters, returning a "frozen" multinomial random variable: + + >>> rv = multinomial(n=7, p=[.3, .7]) + >>> # Frozen object with the same methods but holding the given + >>> # degrees of freedom and scale fixed. + + See also + -------- + scipy.stats.binom : The binomial distribution. + numpy.random.Generator.multinomial : Sampling from the multinomial distribution. + scipy.stats.multivariate_hypergeom : + The multivariate hypergeometric distribution. + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = \ + doccer.docformat(self.__doc__, multinomial_docdict_params) + + def __call__(self, n, p, seed=None): + """Create a frozen multinomial distribution. + + See `multinomial_frozen` for more information. + """ + return multinomial_frozen(n, p, seed) + + def _process_parameters(self, n, p, eps=1e-15): + """Returns: n_, p_, npcond. + + n_ and p_ are arrays of the correct shape; npcond is a boolean array + flagging values out of the domain. + """ + p = np.array(p, dtype=np.float64, copy=True) + p_adjusted = 1. - p[..., :-1].sum(axis=-1) + i_adjusted = np.abs(p_adjusted) > eps + p[i_adjusted, -1] = p_adjusted[i_adjusted] + + # true for bad p + pcond = np.any(p < 0, axis=-1) + pcond |= np.any(p > 1, axis=-1) + + n = np.array(n, dtype=int, copy=True) + + # true for bad n + ncond = n < 0 + + return n, p, ncond | pcond + + def _process_quantiles(self, x, n, p): + """Returns: x_, xcond. + + x_ is an int array; xcond is a boolean array flagging values out of the + domain. + """ + xx = np.asarray(x, dtype=int) + + if xx.ndim == 0: + raise ValueError("x must be an array.") + + if xx.size != 0 and not xx.shape[-1] == p.shape[-1]: + raise ValueError("Size of each quantile should be size of p: " + "received %d, but expected %d." % + (xx.shape[-1], p.shape[-1])) + + # true for x out of the domain + cond = np.any(xx != x, axis=-1) + cond |= np.any(xx < 0, axis=-1) + cond = cond | (np.sum(xx, axis=-1) != n) + + return xx, cond + + def _checkresult(self, result, cond, bad_value): + result = np.asarray(result) + + if cond.ndim != 0: + result[cond] = bad_value + elif cond: + if result.ndim == 0: + return bad_value + result[...] = bad_value + return result + + def _logpmf(self, x, n, p): + return gammaln(n+1) + np.sum(xlogy(x, p) - gammaln(x+1), axis=-1) + + def logpmf(self, x, n, p): + """Log of the Multinomial probability mass function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_doc_default_callparams)s + + Returns + ------- + logpmf : ndarray or scalar + Log of the probability mass function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + """ + n, p, npcond = self._process_parameters(n, p) + x, xcond = self._process_quantiles(x, n, p) + + result = self._logpmf(x, n, p) + + # replace values for which x was out of the domain; broadcast + # xcond to the right shape + xcond_ = xcond | np.zeros(npcond.shape, dtype=np.bool_) + result = self._checkresult(result, xcond_, -np.inf) + + # replace values bad for n or p; broadcast npcond to the right shape + npcond_ = npcond | np.zeros(xcond.shape, dtype=np.bool_) + return self._checkresult(result, npcond_, np.nan) + + def pmf(self, x, n, p): + """Multinomial probability mass function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_doc_default_callparams)s + + Returns + ------- + pmf : ndarray or scalar + Probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + """ + return np.exp(self.logpmf(x, n, p)) + + def mean(self, n, p): + """Mean of the Multinomial distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mean : float + The mean of the distribution + """ + n, p, npcond = self._process_parameters(n, p) + result = n[..., np.newaxis]*p + return self._checkresult(result, npcond, np.nan) + + def cov(self, n, p): + """Covariance matrix of the multinomial distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + cov : ndarray + The covariance matrix of the distribution + """ + n, p, npcond = self._process_parameters(n, p) + + nn = n[..., np.newaxis, np.newaxis] + result = nn * np.einsum('...j,...k->...jk', -p, p) + + # change the diagonal + for i in range(p.shape[-1]): + result[..., i, i] += n*p[..., i] + + return self._checkresult(result, npcond, np.nan) + + def entropy(self, n, p): + r"""Compute the entropy of the multinomial distribution. + + The entropy is computed using this expression: + + .. math:: + + f(x) = - \log n! - n\sum_{i=1}^k p_i \log p_i + + \sum_{i=1}^k \sum_{x=0}^n \binom n x p_i^x(1-p_i)^{n-x} \log x! + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + h : scalar + Entropy of the Multinomial distribution + + Notes + ----- + %(_doc_callparams_note)s + """ + n, p, npcond = self._process_parameters(n, p) + + x = np.r_[1:np.max(n)+1] + + term1 = n*np.sum(entr(p), axis=-1) + term1 -= gammaln(n+1) + + n = n[..., np.newaxis] + new_axes_needed = max(p.ndim, n.ndim) - x.ndim + 1 + x.shape += (1,)*new_axes_needed + + term2 = np.sum(binom.pmf(x, n, p)*gammaln(x+1), + axis=(-1, -1-new_axes_needed)) + + return self._checkresult(term1 + term2, npcond, np.nan) + + def rvs(self, n, p, size=None, random_state=None): + """Draw random samples from a Multinomial distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + size : integer or iterable of integers, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray or scalar + Random variates of shape (`size`, `len(p)`) + + Notes + ----- + %(_doc_callparams_note)s + """ + n, p, npcond = self._process_parameters(n, p) + random_state = self._get_random_state(random_state) + return random_state.multinomial(n, p, size) + + +multinomial = multinomial_gen() + + +class multinomial_frozen(multi_rv_frozen): + r"""Create a frozen Multinomial distribution. + + Parameters + ---------- + n : int + number of trials + p: array_like + probability of a trial falling into each category; should sum to 1 + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + """ + def __init__(self, n, p, seed=None): + self._dist = multinomial_gen(seed) + self.n, self.p, self.npcond = self._dist._process_parameters(n, p) + + # monkey patch self._dist + def _process_parameters(n, p): + return self.n, self.p, self.npcond + + self._dist._process_parameters = _process_parameters + + def logpmf(self, x): + return self._dist.logpmf(x, self.n, self.p) + + def pmf(self, x): + return self._dist.pmf(x, self.n, self.p) + + def mean(self): + return self._dist.mean(self.n, self.p) + + def cov(self): + return self._dist.cov(self.n, self.p) + + def entropy(self): + return self._dist.entropy(self.n, self.p) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.n, self.p, size, random_state) + + +# Set frozen generator docstrings from corresponding docstrings in +# multinomial and fill in default strings in class docstrings +for name in ['logpmf', 'pmf', 'mean', 'cov', 'rvs']: + method = multinomial_gen.__dict__[name] + method_frozen = multinomial_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, multinomial_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, + multinomial_docdict_params) + + +class special_ortho_group_gen(multi_rv_generic): + r"""A Special Orthogonal matrix (SO(N)) random variable. + + Return a random rotation matrix, drawn from the Haar distribution + (the only uniform distribution on SO(N)) with a determinant of +1. + + The `dim` keyword specifies the dimension N. + + Methods + ------- + rvs(dim=None, size=1, random_state=None) + Draw random samples from SO(N). + + Parameters + ---------- + dim : scalar + Dimension of matrices + seed : {None, int, np.random.RandomState, np.random.Generator}, optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Notes + ----- + This class is wrapping the random_rot code from the MDP Toolkit, + https://github.com/mdp-toolkit/mdp-toolkit + + Return a random rotation matrix, drawn from the Haar distribution + (the only uniform distribution on SO(N)). + The algorithm is described in the paper + Stewart, G.W., "The efficient generation of random orthogonal + matrices with an application to condition estimators", SIAM Journal + on Numerical Analysis, 17(3), pp. 403-409, 1980. + For more information see + https://en.wikipedia.org/wiki/Orthogonal_matrix#Randomization + + See also the similar `ortho_group`. For a random rotation in three + dimensions, see `scipy.spatial.transform.Rotation.random`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import special_ortho_group + >>> x = special_ortho_group.rvs(3) + + >>> np.dot(x, x.T) + array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16], + [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16], + [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]]) + + >>> import scipy.linalg + >>> scipy.linalg.det(x) + 1.0 + + This generates one random matrix from SO(3). It is orthogonal and + has a determinant of 1. + + Alternatively, the object may be called (as a function) to fix the `dim` + parameter, returning a "frozen" special_ortho_group random variable: + + >>> rv = special_ortho_group(5) + >>> # Frozen object with the same methods but holding the + >>> # dimension parameter fixed. + + See Also + -------- + ortho_group, scipy.spatial.transform.Rotation.random + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__) + + def __call__(self, dim=None, seed=None): + """Create a frozen SO(N) distribution. + + See `special_ortho_group_frozen` for more information. + """ + return special_ortho_group_frozen(dim, seed=seed) + + def _process_parameters(self, dim): + """Dimension N must be specified; it cannot be inferred.""" + if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim): + raise ValueError("""Dimension of rotation must be specified, + and must be a scalar greater than 1.""") + + return dim + + def rvs(self, dim, size=1, random_state=None): + """Draw random samples from SO(N). + + Parameters + ---------- + dim : integer + Dimension of rotation space (N). + size : integer, optional + Number of samples to draw (default 1). + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim) + + """ + random_state = self._get_random_state(random_state) + + size = int(size) + size = (size,) if size > 1 else () + + dim = self._process_parameters(dim) + + # H represents a (dim, dim) matrix, while D represents the diagonal of + # a (dim, dim) diagonal matrix. The algorithm that follows is + # broadcasted on the leading shape in `size` to vectorize along + # samples. + H = np.empty(size + (dim, dim)) + H[..., :, :] = np.eye(dim) + D = np.empty(size + (dim,)) + + for n in range(dim-1): + + # x is a vector with length dim-n, xrow and xcol are views of it as + # a row vector and column vector respectively. It's important they + # are views and not copies because we are going to modify x + # in-place. + x = random_state.normal(size=size + (dim-n,)) + xrow = x[..., None, :] + xcol = x[..., :, None] + + # This is the squared norm of x, without vectorization it would be + # dot(x, x), to have proper broadcasting we use matmul and squeeze + # out (convert to scalar) the resulting 1x1 matrix + norm2 = np.matmul(xrow, xcol).squeeze((-2, -1)) + + x0 = x[..., 0].copy() + D[..., n] = np.where(x0 != 0, np.sign(x0), 1) + x[..., 0] += D[..., n]*np.sqrt(norm2) + + # In renormalizing x we have to append an additional axis with + # [..., None] to broadcast the scalar against the vector x + x /= np.sqrt((norm2 - x0**2 + x[..., 0]**2) / 2.)[..., None] + + # Householder transformation, without vectorization the RHS can be + # written as outer(H @ x, x) (apart from the slicing) + H[..., :, n:] -= np.matmul(H[..., :, n:], xcol) * xrow + + D[..., -1] = (-1)**(dim-1)*D[..., :-1].prod(axis=-1) + + # Without vectorization this could be written as H = diag(D) @ H, + # left-multiplication by a diagonal matrix amounts to multiplying each + # row of H by an element of the diagonal, so we add a dummy axis for + # the column index + H *= D[..., :, None] + return H + + +special_ortho_group = special_ortho_group_gen() + + +class special_ortho_group_frozen(multi_rv_frozen): + def __init__(self, dim=None, seed=None): + """Create a frozen SO(N) distribution. + + Parameters + ---------- + dim : scalar + Dimension of matrices + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Examples + -------- + >>> from scipy.stats import special_ortho_group + >>> g = special_ortho_group(5) + >>> x = g.rvs() + + """ # numpy/numpydoc#87 # noqa: E501 + self._dist = special_ortho_group_gen(seed) + self.dim = self._dist._process_parameters(dim) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.dim, size, random_state) + + +class ortho_group_gen(multi_rv_generic): + r"""An Orthogonal matrix (O(N)) random variable. + + Return a random orthogonal matrix, drawn from the O(N) Haar + distribution (the only uniform distribution on O(N)). + + The `dim` keyword specifies the dimension N. + + Methods + ------- + rvs(dim=None, size=1, random_state=None) + Draw random samples from O(N). + + Parameters + ---------- + dim : scalar + Dimension of matrices + seed : {None, int, np.random.RandomState, np.random.Generator}, optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Notes + ----- + This class is closely related to `special_ortho_group`. + + Some care is taken to avoid numerical error, as per the paper by Mezzadri. + + References + ---------- + .. [1] F. Mezzadri, "How to generate random matrices from the classical + compact groups", :arXiv:`math-ph/0609050v2`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> x = ortho_group.rvs(3) + + >>> np.dot(x, x.T) + array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16], + [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16], + [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]]) + + >>> import scipy.linalg + >>> np.fabs(scipy.linalg.det(x)) + 1.0 + + This generates one random matrix from O(3). It is orthogonal and + has a determinant of +1 or -1. + + Alternatively, the object may be called (as a function) to fix the `dim` + parameter, returning a "frozen" ortho_group random variable: + + >>> rv = ortho_group(5) + >>> # Frozen object with the same methods but holding the + >>> # dimension parameter fixed. + + See Also + -------- + special_ortho_group + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__) + + def __call__(self, dim=None, seed=None): + """Create a frozen O(N) distribution. + + See `ortho_group_frozen` for more information. + """ + return ortho_group_frozen(dim, seed=seed) + + def _process_parameters(self, dim): + """Dimension N must be specified; it cannot be inferred.""" + if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim): + raise ValueError("Dimension of rotation must be specified," + "and must be a scalar greater than 1.") + + return dim + + def rvs(self, dim, size=1, random_state=None): + """Draw random samples from O(N). + + Parameters + ---------- + dim : integer + Dimension of rotation space (N). + size : integer, optional + Number of samples to draw (default 1). + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim) + + """ + random_state = self._get_random_state(random_state) + + size = int(size) + + dim = self._process_parameters(dim) + + size = (size,) if size > 1 else () + z = random_state.normal(size=size + (dim, dim)) + q, r = np.linalg.qr(z) + # The last two dimensions are the rows and columns of R matrices. + # Extract the diagonals. Note that this eliminates a dimension. + d = r.diagonal(offset=0, axis1=-2, axis2=-1) + # Add back a dimension for proper broadcasting: we're dividing + # each row of each R matrix by the diagonal of the R matrix. + q *= (d/abs(d))[..., np.newaxis, :] # to broadcast properly + return q + + +ortho_group = ortho_group_gen() + + +class ortho_group_frozen(multi_rv_frozen): + def __init__(self, dim=None, seed=None): + """Create a frozen O(N) distribution. + + Parameters + ---------- + dim : scalar + Dimension of matrices + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Examples + -------- + >>> from scipy.stats import ortho_group + >>> g = ortho_group(5) + >>> x = g.rvs() + + """ # numpy/numpydoc#87 # noqa: E501 + self._dist = ortho_group_gen(seed) + self.dim = self._dist._process_parameters(dim) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.dim, size, random_state) + + +class random_correlation_gen(multi_rv_generic): + r"""A random correlation matrix. + + Return a random correlation matrix, given a vector of eigenvalues. + + The `eigs` keyword specifies the eigenvalues of the correlation matrix, + and implies the dimension. + + Methods + ------- + rvs(eigs=None, random_state=None) + Draw random correlation matrices, all with eigenvalues eigs. + + Parameters + ---------- + eigs : 1d ndarray + Eigenvalues of correlation matrix + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + tol : float, optional + Tolerance for input parameter checks + diag_tol : float, optional + Tolerance for deviation of the diagonal of the resulting + matrix. Default: 1e-7 + + Raises + ------ + RuntimeError + Floating point error prevented generating a valid correlation + matrix. + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim), + each having eigenvalues eigs. + + Notes + ----- + + Generates a random correlation matrix following a numerically stable + algorithm spelled out by Davies & Higham. This algorithm uses a single O(N) + similarity transformation to construct a symmetric positive semi-definite + matrix, and applies a series of Givens rotations to scale it to have ones + on the diagonal. + + References + ---------- + + .. [1] Davies, Philip I; Higham, Nicholas J; "Numerically stable generation + of correlation matrices and their factors", BIT 2000, Vol. 40, + No. 4, pp. 640 651 + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import random_correlation + >>> rng = np.random.default_rng() + >>> x = random_correlation.rvs((.5, .8, 1.2, 1.5), random_state=rng) + >>> x + array([[ 1. , -0.02423399, 0.03130519, 0.4946965 ], + [-0.02423399, 1. , 0.20334736, 0.04039817], + [ 0.03130519, 0.20334736, 1. , 0.02694275], + [ 0.4946965 , 0.04039817, 0.02694275, 1. ]]) + >>> import scipy.linalg + >>> e, v = scipy.linalg.eigh(x) + >>> e + array([ 0.5, 0.8, 1.2, 1.5]) + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__) + + def __call__(self, eigs, seed=None, tol=1e-13, diag_tol=1e-7): + """Create a frozen random correlation matrix. + + See `random_correlation_frozen` for more information. + """ + return random_correlation_frozen(eigs, seed=seed, tol=tol, + diag_tol=diag_tol) + + def _process_parameters(self, eigs, tol): + eigs = np.asarray(eigs, dtype=float) + dim = eigs.size + + if eigs.ndim != 1 or eigs.shape[0] != dim or dim <= 1: + raise ValueError("Array 'eigs' must be a vector of length " + "greater than 1.") + + if np.fabs(np.sum(eigs) - dim) > tol: + raise ValueError("Sum of eigenvalues must equal dimensionality.") + + for x in eigs: + if x < -tol: + raise ValueError("All eigenvalues must be non-negative.") + + return dim, eigs + + def _givens_to_1(self, aii, ajj, aij): + """Computes a 2x2 Givens matrix to put 1's on the diagonal. + + The input matrix is a 2x2 symmetric matrix M = [ aii aij ; aij ajj ]. + + The output matrix g is a 2x2 anti-symmetric matrix of the form + [ c s ; -s c ]; the elements c and s are returned. + + Applying the output matrix to the input matrix (as b=g.T M g) + results in a matrix with bii=1, provided tr(M) - det(M) >= 1 + and floating point issues do not occur. Otherwise, some other + valid rotation is returned. When tr(M)==2, also bjj=1. + + """ + aiid = aii - 1. + ajjd = ajj - 1. + + if ajjd == 0: + # ajj==1, so swap aii and ajj to avoid division by zero + return 0., 1. + + dd = math.sqrt(max(aij**2 - aiid*ajjd, 0)) + + # The choice of t should be chosen to avoid cancellation [1] + t = (aij + math.copysign(dd, aij)) / ajjd + c = 1. / math.sqrt(1. + t*t) + if c == 0: + # Underflow + s = 1.0 + else: + s = c*t + return c, s + + def _to_corr(self, m): + """ + Given a psd matrix m, rotate to put one's on the diagonal, turning it + into a correlation matrix. This also requires the trace equal the + dimensionality. Note: modifies input matrix + """ + # Check requirements for in-place Givens + if not (m.flags.c_contiguous and m.dtype == np.float64 and + m.shape[0] == m.shape[1]): + raise ValueError() + + d = m.shape[0] + for i in range(d-1): + if m[i, i] == 1: + continue + elif m[i, i] > 1: + for j in range(i+1, d): + if m[j, j] < 1: + break + else: + for j in range(i+1, d): + if m[j, j] > 1: + break + + c, s = self._givens_to_1(m[i, i], m[j, j], m[i, j]) + + # Use BLAS to apply Givens rotations in-place. Equivalent to: + # g = np.eye(d) + # g[i, i] = g[j,j] = c + # g[j, i] = -s; g[i, j] = s + # m = np.dot(g.T, np.dot(m, g)) + mv = m.ravel() + drot(mv, mv, c, -s, n=d, + offx=i*d, incx=1, offy=j*d, incy=1, + overwrite_x=True, overwrite_y=True) + drot(mv, mv, c, -s, n=d, + offx=i, incx=d, offy=j, incy=d, + overwrite_x=True, overwrite_y=True) + + return m + + def rvs(self, eigs, random_state=None, tol=1e-13, diag_tol=1e-7): + """Draw random correlation matrices. + + Parameters + ---------- + eigs : 1d ndarray + Eigenvalues of correlation matrix + tol : float, optional + Tolerance for input parameter checks + diag_tol : float, optional + Tolerance for deviation of the diagonal of the resulting + matrix. Default: 1e-7 + + Raises + ------ + RuntimeError + Floating point error prevented generating a valid correlation + matrix. + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim), + each having eigenvalues eigs. + + """ + dim, eigs = self._process_parameters(eigs, tol=tol) + + random_state = self._get_random_state(random_state) + + m = ortho_group.rvs(dim, random_state=random_state) + m = np.dot(np.dot(m, np.diag(eigs)), m.T) # Set the trace of m + m = self._to_corr(m) # Carefully rotate to unit diagonal + + # Check diagonal + if abs(m.diagonal() - 1).max() > diag_tol: + raise RuntimeError("Failed to generate a valid correlation matrix") + + return m + + +random_correlation = random_correlation_gen() + + +class random_correlation_frozen(multi_rv_frozen): + def __init__(self, eigs, seed=None, tol=1e-13, diag_tol=1e-7): + """Create a frozen random correlation matrix distribution. + + Parameters + ---------- + eigs : 1d ndarray + Eigenvalues of correlation matrix + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + tol : float, optional + Tolerance for input parameter checks + diag_tol : float, optional + Tolerance for deviation of the diagonal of the resulting + matrix. Default: 1e-7 + + Raises + ------ + RuntimeError + Floating point error prevented generating a valid correlation + matrix. + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim), + each having eigenvalues eigs. + """ # numpy/numpydoc#87 # noqa: E501 + + self._dist = random_correlation_gen(seed) + self.tol = tol + self.diag_tol = diag_tol + _, self.eigs = self._dist._process_parameters(eigs, tol=self.tol) + + def rvs(self, random_state=None): + return self._dist.rvs(self.eigs, random_state=random_state, + tol=self.tol, diag_tol=self.diag_tol) + + +class unitary_group_gen(multi_rv_generic): + r"""A matrix-valued U(N) random variable. + + Return a random unitary matrix. + + The `dim` keyword specifies the dimension N. + + Methods + ------- + rvs(dim=None, size=1, random_state=None) + Draw random samples from U(N). + + Parameters + ---------- + dim : scalar + Dimension of matrices, must be greater than 1. + seed : {None, int, np.random.RandomState, np.random.Generator}, optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Notes + ----- + This class is similar to `ortho_group`. + + References + ---------- + .. [1] F. Mezzadri, "How to generate random matrices from the classical + compact groups", :arXiv:`math-ph/0609050v2`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import unitary_group + >>> x = unitary_group.rvs(3) + + >>> np.dot(x, x.conj().T) + array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16], + [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16], + [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]]) + + This generates one random matrix from U(3). The dot product confirms that + it is unitary up to machine precision. + + Alternatively, the object may be called (as a function) to fix the `dim` + parameter, return a "frozen" unitary_group random variable: + + >>> rv = unitary_group(5) + + See Also + -------- + ortho_group + + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__) + + def __call__(self, dim=None, seed=None): + """Create a frozen (U(N)) n-dimensional unitary matrix distribution. + + See `unitary_group_frozen` for more information. + """ + return unitary_group_frozen(dim, seed=seed) + + def _process_parameters(self, dim): + """Dimension N must be specified; it cannot be inferred.""" + if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim): + raise ValueError("Dimension of rotation must be specified," + "and must be a scalar greater than 1.") + + return dim + + def rvs(self, dim, size=1, random_state=None): + """Draw random samples from U(N). + + Parameters + ---------- + dim : integer + Dimension of space (N). + size : integer, optional + Number of samples to draw (default 1). + + Returns + ------- + rvs : ndarray or scalar + Random size N-dimensional matrices, dimension (size, dim, dim) + + """ + random_state = self._get_random_state(random_state) + + size = int(size) + + dim = self._process_parameters(dim) + + size = (size,) if size > 1 else () + z = 1/math.sqrt(2)*(random_state.normal(size=size + (dim, dim)) + + 1j*random_state.normal(size=size + (dim, dim))) + q, r = np.linalg.qr(z) + # The last two dimensions are the rows and columns of R matrices. + # Extract the diagonals. Note that this eliminates a dimension. + d = r.diagonal(offset=0, axis1=-2, axis2=-1) + # Add back a dimension for proper broadcasting: we're dividing + # each row of each R matrix by the diagonal of the R matrix. + q *= (d/abs(d))[..., np.newaxis, :] # to broadcast properly + return q + + +unitary_group = unitary_group_gen() + + +class unitary_group_frozen(multi_rv_frozen): + def __init__(self, dim=None, seed=None): + """Create a frozen (U(N)) n-dimensional unitary matrix distribution. + + Parameters + ---------- + dim : scalar + Dimension of matrices + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Examples + -------- + >>> from scipy.stats import unitary_group + >>> x = unitary_group(3) + >>> x.rvs() + + """ # numpy/numpydoc#87 # noqa: E501 + self._dist = unitary_group_gen(seed) + self.dim = self._dist._process_parameters(dim) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.dim, size, random_state) + + +_mvt_doc_default_callparams = """\ +loc : array_like, optional + Location of the distribution. (default ``0``) +shape : array_like, optional + Positive semidefinite matrix of the distribution. (default ``1``) +df : float, optional + Degrees of freedom of the distribution; must be greater than zero. + If ``np.inf`` then results are multivariate normal. The default is ``1``. +allow_singular : bool, optional + Whether to allow a singular matrix. (default ``False``) +""" + +_mvt_doc_callparams_note = """\ +Setting the parameter `loc` to ``None`` is equivalent to having `loc` +be the zero-vector. The parameter `shape` can be a scalar, in which case +the shape matrix is the identity times that value, a vector of +diagonal entries for the shape matrix, or a two-dimensional array_like. +""" + +_mvt_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +mvt_docdict_params = { + '_mvt_doc_default_callparams': _mvt_doc_default_callparams, + '_mvt_doc_callparams_note': _mvt_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +mvt_docdict_noparams = { + '_mvt_doc_default_callparams': "", + '_mvt_doc_callparams_note': _mvt_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class multivariate_t_gen(multi_rv_generic): + r"""A multivariate t-distributed random variable. + + The `loc` parameter specifies the location. The `shape` parameter specifies + the positive semidefinite shape matrix. The `df` parameter specifies the + degrees of freedom. + + In addition to calling the methods below, the object itself may be called + as a function to fix the location, shape matrix, and degrees of freedom + parameters, returning a "frozen" multivariate t-distribution random. + + Methods + ------- + pdf(x, loc=None, shape=1, df=1, allow_singular=False) + Probability density function. + logpdf(x, loc=None, shape=1, df=1, allow_singular=False) + Log of the probability density function. + cdf(x, loc=None, shape=1, df=1, allow_singular=False, *, + maxpts=None, lower_limit=None, random_state=None) + Cumulative distribution function. + rvs(loc=None, shape=1, df=1, size=1, random_state=None) + Draw random samples from a multivariate t-distribution. + entropy(loc=None, shape=1, df=1) + Differential entropy of a multivariate t-distribution. + + Parameters + ---------- + %(_mvt_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + %(_mvt_doc_callparams_note)s + The matrix `shape` must be a (symmetric) positive semidefinite matrix. The + determinant and inverse of `shape` are computed as the pseudo-determinant + and pseudo-inverse, respectively, so that `shape` does not need to have + full rank. + + The probability density function for `multivariate_t` is + + .. math:: + + f(x) = \frac{\Gamma((\nu + p)/2)}{\Gamma(\nu/2)\nu^{p/2}\pi^{p/2}|\Sigma|^{1/2}} + \left[1 + \frac{1}{\nu} (\mathbf{x} - \boldsymbol{\mu})^{\top} + \boldsymbol{\Sigma}^{-1} + (\mathbf{x} - \boldsymbol{\mu}) \right]^{-(\nu + p)/2}, + + where :math:`p` is the dimension of :math:`\mathbf{x}`, + :math:`\boldsymbol{\mu}` is the :math:`p`-dimensional location, + :math:`\boldsymbol{\Sigma}` the :math:`p \times p`-dimensional shape + matrix, and :math:`\nu` is the degrees of freedom. + + .. versionadded:: 1.6.0 + + References + ---------- + .. [1] Arellano-Valle et al. "Shannon Entropy and Mutual Information for + Multivariate Skew-Elliptical Distributions". Scandinavian Journal + of Statistics. Vol. 40, issue 1. + + Examples + -------- + The object may be called (as a function) to fix the `loc`, `shape`, + `df`, and `allow_singular` parameters, returning a "frozen" + multivariate_t random variable: + + >>> import numpy as np + >>> from scipy.stats import multivariate_t + >>> rv = multivariate_t([1.0, -0.5], [[2.1, 0.3], [0.3, 1.5]], df=2) + >>> # Frozen object with the same methods but holding the given location, + >>> # scale, and degrees of freedom fixed. + + Create a contour plot of the PDF. + + >>> import matplotlib.pyplot as plt + >>> x, y = np.mgrid[-1:3:.01, -2:1.5:.01] + >>> pos = np.dstack((x, y)) + >>> fig, ax = plt.subplots(1, 1) + >>> ax.set_aspect('equal') + >>> plt.contourf(x, y, rv.pdf(pos)) + + """ + + def __init__(self, seed=None): + """Initialize a multivariate t-distributed random variable. + + Parameters + ---------- + seed : Random state. + + """ + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, mvt_docdict_params) + self._random_state = check_random_state(seed) + + def __call__(self, loc=None, shape=1, df=1, allow_singular=False, + seed=None): + """Create a frozen multivariate t-distribution. + + See `multivariate_t_frozen` for parameters. + """ + if df == np.inf: + return multivariate_normal_frozen(mean=loc, cov=shape, + allow_singular=allow_singular, + seed=seed) + return multivariate_t_frozen(loc=loc, shape=shape, df=df, + allow_singular=allow_singular, seed=seed) + + def pdf(self, x, loc=None, shape=1, df=1, allow_singular=False): + """Multivariate t-distribution probability density function. + + Parameters + ---------- + x : array_like + Points at which to evaluate the probability density function. + %(_mvt_doc_default_callparams)s + + Returns + ------- + pdf : Probability density function evaluated at `x`. + + Examples + -------- + >>> from scipy.stats import multivariate_t + >>> x = [0.4, 5] + >>> loc = [0, 1] + >>> shape = [[1, 0.1], [0.1, 1]] + >>> df = 7 + >>> multivariate_t.pdf(x, loc, shape, df) + 0.00075713 + + """ + dim, loc, shape, df = self._process_parameters(loc, shape, df) + x = self._process_quantiles(x, dim) + shape_info = _PSD(shape, allow_singular=allow_singular) + logpdf = self._logpdf(x, loc, shape_info.U, shape_info.log_pdet, df, + dim, shape_info.rank) + return np.exp(logpdf) + + def logpdf(self, x, loc=None, shape=1, df=1): + """Log of the multivariate t-distribution probability density function. + + Parameters + ---------- + x : array_like + Points at which to evaluate the log of the probability density + function. + %(_mvt_doc_default_callparams)s + + Returns + ------- + logpdf : Log of the probability density function evaluated at `x`. + + Examples + -------- + >>> from scipy.stats import multivariate_t + >>> x = [0.4, 5] + >>> loc = [0, 1] + >>> shape = [[1, 0.1], [0.1, 1]] + >>> df = 7 + >>> multivariate_t.logpdf(x, loc, shape, df) + -7.1859802 + + See Also + -------- + pdf : Probability density function. + + """ + dim, loc, shape, df = self._process_parameters(loc, shape, df) + x = self._process_quantiles(x, dim) + shape_info = _PSD(shape) + return self._logpdf(x, loc, shape_info.U, shape_info.log_pdet, df, dim, + shape_info.rank) + + def _logpdf(self, x, loc, prec_U, log_pdet, df, dim, rank): + """Utility method `pdf`, `logpdf` for parameters. + + Parameters + ---------- + x : ndarray + Points at which to evaluate the log of the probability density + function. + loc : ndarray + Location of the distribution. + prec_U : ndarray + A decomposition such that `np.dot(prec_U, prec_U.T)` is the inverse + of the shape matrix. + log_pdet : float + Logarithm of the determinant of the shape matrix. + df : float + Degrees of freedom of the distribution. + dim : int + Dimension of the quantiles x. + rank : int + Rank of the shape matrix. + + Notes + ----- + As this function does no argument checking, it should not be called + directly; use 'logpdf' instead. + + """ + if df == np.inf: + return multivariate_normal._logpdf(x, loc, prec_U, log_pdet, rank) + + dev = x - loc + maha = np.square(np.dot(dev, prec_U)).sum(axis=-1) + + t = 0.5 * (df + dim) + A = gammaln(t) + B = gammaln(0.5 * df) + C = dim/2. * np.log(df * np.pi) + D = 0.5 * log_pdet + E = -t * np.log(1 + (1./df) * maha) + + return _squeeze_output(A - B - C - D + E) + + def _cdf(self, x, loc, shape, df, dim, maxpts=None, lower_limit=None, + random_state=None): + + # All of this - random state validation, maxpts, apply_along_axis, + # etc. needs to go in this private method unless we want + # frozen distribution's `cdf` method to duplicate it or call `cdf`, + # which would require re-processing parameters + if random_state is not None: + rng = check_random_state(random_state) + else: + rng = self._random_state + + if not maxpts: + maxpts = 1000 * dim + + x = self._process_quantiles(x, dim) + lower_limit = (np.full(loc.shape, -np.inf) + if lower_limit is None else lower_limit) + + # remove the mean + x, lower_limit = x - loc, lower_limit - loc + + b, a = np.broadcast_arrays(x, lower_limit) + i_swap = b < a + signs = (-1)**(i_swap.sum(axis=-1)) # odd # of swaps -> negative + a, b = a.copy(), b.copy() + a[i_swap], b[i_swap] = b[i_swap], a[i_swap] + n = x.shape[-1] + limits = np.concatenate((a, b), axis=-1) + + def func1d(limits): + a, b = limits[:n], limits[n:] + return _qmvt(maxpts, df, shape, a, b, rng)[0] + + res = np.apply_along_axis(func1d, -1, limits) * signs + # Fixing the output shape for existing distributions is a separate + # issue. For now, let's keep this consistent with pdf. + return _squeeze_output(res) + + def cdf(self, x, loc=None, shape=1, df=1, allow_singular=False, *, + maxpts=None, lower_limit=None, random_state=None): + """Multivariate t-distribution cumulative distribution function. + + Parameters + ---------- + x : array_like + Points at which to evaluate the cumulative distribution function. + %(_mvt_doc_default_callparams)s + maxpts : int, optional + Maximum number of points to use for integration. The default is + 1000 times the number of dimensions. + lower_limit : array_like, optional + Lower limit of integration of the cumulative distribution function. + Default is negative infinity. Must be broadcastable with `x`. + %(_doc_random_state)s + + Returns + ------- + cdf : ndarray or scalar + Cumulative distribution function evaluated at `x`. + + Examples + -------- + >>> from scipy.stats import multivariate_t + >>> x = [0.4, 5] + >>> loc = [0, 1] + >>> shape = [[1, 0.1], [0.1, 1]] + >>> df = 7 + >>> multivariate_t.cdf(x, loc, shape, df) + 0.64798491 + + """ + dim, loc, shape, df = self._process_parameters(loc, shape, df) + shape = _PSD(shape, allow_singular=allow_singular)._M + + return self._cdf(x, loc, shape, df, dim, maxpts, + lower_limit, random_state) + + def _entropy(self, dim, df=1, shape=1): + if df == np.inf: + return multivariate_normal(None, cov=shape).entropy() + + shape_info = _PSD(shape) + shape_term = 0.5 * shape_info.log_pdet + + def regular(dim, df): + halfsum = 0.5 * (dim + df) + half_df = 0.5 * df + return ( + -gammaln(halfsum) + gammaln(half_df) + + 0.5 * dim * np.log(df * np.pi) + halfsum + * (psi(halfsum) - psi(half_df)) + + shape_term + ) + + def asymptotic(dim, df): + # Formula from Wolfram Alpha: + # "asymptotic expansion -gammaln((m+d)/2) + gammaln(d/2) + (m*log(d*pi))/2 + # + ((m+d)/2) * (digamma((m+d)/2) - digamma(d/2))" + return ( + dim * norm._entropy() + dim / df + - dim * (dim - 2) * df**-2.0 / 4 + + dim**2 * (dim - 2) * df**-3.0 / 6 + + dim * (-3 * dim**3 + 8 * dim**2 - 8) * df**-4.0 / 24 + + dim**2 * (3 * dim**3 - 10 * dim**2 + 16) * df**-5.0 / 30 + + shape_term + )[()] + + # preserves ~12 digits accuracy up to at least `dim=1e5`. See gh-18465. + threshold = dim * 100 * 4 / (np.log(dim) + 1) + return _lazywhere(df >= threshold, (dim, df), f=asymptotic, f2=regular) + + def entropy(self, loc=None, shape=1, df=1): + """Calculate the differential entropy of a multivariate + t-distribution. + + Parameters + ---------- + %(_mvt_doc_default_callparams)s + + Returns + ------- + h : float + Differential entropy + + """ + dim, loc, shape, df = self._process_parameters(None, shape, df) + return self._entropy(dim, df, shape) + + def rvs(self, loc=None, shape=1, df=1, size=1, random_state=None): + """Draw random samples from a multivariate t-distribution. + + Parameters + ---------- + %(_mvt_doc_default_callparams)s + size : integer, optional + Number of samples to draw (default 1). + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray or scalar + Random variates of size (`size`, `P`), where `P` is the + dimension of the random variable. + + Examples + -------- + >>> from scipy.stats import multivariate_t + >>> x = [0.4, 5] + >>> loc = [0, 1] + >>> shape = [[1, 0.1], [0.1, 1]] + >>> df = 7 + >>> multivariate_t.rvs(loc, shape, df) + array([[0.93477495, 3.00408716]]) + + """ + # For implementation details, see equation (3): + # + # Hofert, "On Sampling from the Multivariatet Distribution", 2013 + # http://rjournal.github.io/archive/2013-2/hofert.pdf + # + dim, loc, shape, df = self._process_parameters(loc, shape, df) + if random_state is not None: + rng = check_random_state(random_state) + else: + rng = self._random_state + + if np.isinf(df): + x = np.ones(size) + else: + x = rng.chisquare(df, size=size) / df + + z = rng.multivariate_normal(np.zeros(dim), shape, size=size) + samples = loc + z / np.sqrt(x)[..., None] + return _squeeze_output(samples) + + def _process_quantiles(self, x, dim): + """ + Adjust quantiles array so that last axis labels the components of + each data point. + """ + x = np.asarray(x, dtype=float) + if x.ndim == 0: + x = x[np.newaxis] + elif x.ndim == 1: + if dim == 1: + x = x[:, np.newaxis] + else: + x = x[np.newaxis, :] + return x + + def _process_parameters(self, loc, shape, df): + """ + Infer dimensionality from location array and shape matrix, handle + defaults, and ensure compatible dimensions. + """ + if loc is None and shape is None: + loc = np.asarray(0, dtype=float) + shape = np.asarray(1, dtype=float) + dim = 1 + elif loc is None: + shape = np.asarray(shape, dtype=float) + if shape.ndim < 2: + dim = 1 + else: + dim = shape.shape[0] + loc = np.zeros(dim) + elif shape is None: + loc = np.asarray(loc, dtype=float) + dim = loc.size + shape = np.eye(dim) + else: + shape = np.asarray(shape, dtype=float) + loc = np.asarray(loc, dtype=float) + dim = loc.size + + if dim == 1: + loc = loc.reshape(1) + shape = shape.reshape(1, 1) + + if loc.ndim != 1 or loc.shape[0] != dim: + raise ValueError("Array 'loc' must be a vector of length %d." % + dim) + if shape.ndim == 0: + shape = shape * np.eye(dim) + elif shape.ndim == 1: + shape = np.diag(shape) + elif shape.ndim == 2 and shape.shape != (dim, dim): + rows, cols = shape.shape + if rows != cols: + msg = ("Array 'cov' must be square if it is two dimensional," + " but cov.shape = %s." % str(shape.shape)) + else: + msg = ("Dimension mismatch: array 'cov' is of shape %s," + " but 'loc' is a vector of length %d.") + msg = msg % (str(shape.shape), len(loc)) + raise ValueError(msg) + elif shape.ndim > 2: + raise ValueError("Array 'cov' must be at most two-dimensional," + " but cov.ndim = %d" % shape.ndim) + + # Process degrees of freedom. + if df is None: + df = 1 + elif df <= 0: + raise ValueError("'df' must be greater than zero.") + elif np.isnan(df): + raise ValueError("'df' is 'nan' but must be greater than zero or 'np.inf'.") + + return dim, loc, shape, df + + +class multivariate_t_frozen(multi_rv_frozen): + + def __init__(self, loc=None, shape=1, df=1, allow_singular=False, + seed=None): + """Create a frozen multivariate t distribution. + + Parameters + ---------- + %(_mvt_doc_default_callparams)s + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import multivariate_t + >>> loc = np.zeros(3) + >>> shape = np.eye(3) + >>> df = 10 + >>> dist = multivariate_t(loc, shape, df) + >>> dist.rvs() + array([[ 0.81412036, -1.53612361, 0.42199647]]) + >>> dist.pdf([1, 1, 1]) + array([0.01237803]) + + """ + self._dist = multivariate_t_gen(seed) + dim, loc, shape, df = self._dist._process_parameters(loc, shape, df) + self.dim, self.loc, self.shape, self.df = dim, loc, shape, df + self.shape_info = _PSD(shape, allow_singular=allow_singular) + + def logpdf(self, x): + x = self._dist._process_quantiles(x, self.dim) + U = self.shape_info.U + log_pdet = self.shape_info.log_pdet + return self._dist._logpdf(x, self.loc, U, log_pdet, self.df, self.dim, + self.shape_info.rank) + + def cdf(self, x, *, maxpts=None, lower_limit=None, random_state=None): + x = self._dist._process_quantiles(x, self.dim) + return self._dist._cdf(x, self.loc, self.shape, self.df, self.dim, + maxpts, lower_limit, random_state) + + def pdf(self, x): + return np.exp(self.logpdf(x)) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(loc=self.loc, + shape=self.shape, + df=self.df, + size=size, + random_state=random_state) + + def entropy(self): + return self._dist._entropy(self.dim, self.df, self.shape) + + +multivariate_t = multivariate_t_gen() + + +# Set frozen generator docstrings from corresponding docstrings in +# multivariate_t_gen and fill in default strings in class docstrings +for name in ['logpdf', 'pdf', 'rvs', 'cdf', 'entropy']: + method = multivariate_t_gen.__dict__[name] + method_frozen = multivariate_t_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat(method.__doc__, + mvt_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, mvt_docdict_params) + + +_mhg_doc_default_callparams = """\ +m : array_like + The number of each type of object in the population. + That is, :math:`m[i]` is the number of objects of + type :math:`i`. +n : array_like + The number of samples taken from the population. +""" + +_mhg_doc_callparams_note = """\ +`m` must be an array of positive integers. If the quantile +:math:`i` contains values out of the range :math:`[0, m_i]` +where :math:`m_i` is the number of objects of type :math:`i` +in the population or if the parameters are inconsistent with one +another (e.g. ``x.sum() != n``), methods return the appropriate +value (e.g. ``0`` for ``pmf``). If `m` or `n` contain negative +values, the result will contain ``nan`` there. +""" + +_mhg_doc_frozen_callparams = "" + +_mhg_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +mhg_docdict_params = { + '_doc_default_callparams': _mhg_doc_default_callparams, + '_doc_callparams_note': _mhg_doc_callparams_note, + '_doc_random_state': _doc_random_state +} + +mhg_docdict_noparams = { + '_doc_default_callparams': _mhg_doc_frozen_callparams, + '_doc_callparams_note': _mhg_doc_frozen_callparams_note, + '_doc_random_state': _doc_random_state +} + + +class multivariate_hypergeom_gen(multi_rv_generic): + r"""A multivariate hypergeometric random variable. + + Methods + ------- + pmf(x, m, n) + Probability mass function. + logpmf(x, m, n) + Log of the probability mass function. + rvs(m, n, size=1, random_state=None) + Draw random samples from a multivariate hypergeometric + distribution. + mean(m, n) + Mean of the multivariate hypergeometric distribution. + var(m, n) + Variance of the multivariate hypergeometric distribution. + cov(m, n) + Compute the covariance matrix of the multivariate + hypergeometric distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + %(_doc_random_state)s + + Notes + ----- + %(_doc_callparams_note)s + + The probability mass function for `multivariate_hypergeom` is + + .. math:: + + P(X_1 = x_1, X_2 = x_2, \ldots, X_k = x_k) = \frac{\binom{m_1}{x_1} + \binom{m_2}{x_2} \cdots \binom{m_k}{x_k}}{\binom{M}{n}}, \\ \quad + (x_1, x_2, \ldots, x_k) \in \mathbb{N}^k \text{ with } + \sum_{i=1}^k x_i = n + + where :math:`m_i` are the number of objects of type :math:`i`, :math:`M` + is the total number of objects in the population (sum of all the + :math:`m_i`), and :math:`n` is the size of the sample to be taken + from the population. + + .. versionadded:: 1.6.0 + + Examples + -------- + To evaluate the probability mass function of the multivariate + hypergeometric distribution, with a dichotomous population of size + :math:`10` and :math:`20`, at a sample of size :math:`12` with + :math:`8` objects of the first type and :math:`4` objects of the + second type, use: + + >>> from scipy.stats import multivariate_hypergeom + >>> multivariate_hypergeom.pmf(x=[8, 4], m=[10, 20], n=12) + 0.0025207176631464523 + + The `multivariate_hypergeom` distribution is identical to the + corresponding `hypergeom` distribution (tiny numerical differences + notwithstanding) when only two types (good and bad) of objects + are present in the population as in the example above. Consider + another example for a comparison with the hypergeometric distribution: + + >>> from scipy.stats import hypergeom + >>> multivariate_hypergeom.pmf(x=[3, 1], m=[10, 5], n=4) + 0.4395604395604395 + >>> hypergeom.pmf(k=3, M=15, n=4, N=10) + 0.43956043956044005 + + The functions ``pmf``, ``logpmf``, ``mean``, ``var``, ``cov``, and ``rvs`` + support broadcasting, under the convention that the vector parameters + (``x``, ``m``, and ``n``) are interpreted as if each row along the last + axis is a single object. For instance, we can combine the previous two + calls to `multivariate_hypergeom` as + + >>> multivariate_hypergeom.pmf(x=[[8, 4], [3, 1]], m=[[10, 20], [10, 5]], + ... n=[12, 4]) + array([0.00252072, 0.43956044]) + + This broadcasting also works for ``cov``, where the output objects are + square matrices of size ``m.shape[-1]``. For example: + + >>> multivariate_hypergeom.cov(m=[[7, 9], [10, 15]], n=[8, 12]) + array([[[ 1.05, -1.05], + [-1.05, 1.05]], + [[ 1.56, -1.56], + [-1.56, 1.56]]]) + + That is, ``result[0]`` is equal to + ``multivariate_hypergeom.cov(m=[7, 9], n=8)`` and ``result[1]`` is equal + to ``multivariate_hypergeom.cov(m=[10, 15], n=12)``. + + Alternatively, the object may be called (as a function) to fix the `m` + and `n` parameters, returning a "frozen" multivariate hypergeometric + random variable. + + >>> rv = multivariate_hypergeom(m=[10, 20], n=12) + >>> rv.pmf(x=[8, 4]) + 0.0025207176631464523 + + See Also + -------- + scipy.stats.hypergeom : The hypergeometric distribution. + scipy.stats.multinomial : The multinomial distribution. + + References + ---------- + .. [1] The Multivariate Hypergeometric Distribution, + http://www.randomservices.org/random/urn/MultiHypergeometric.html + .. [2] Thomas J. Sargent and John Stachurski, 2020, + Multivariate Hypergeometric Distribution + https://python.quantecon.org/multi_hyper.html + """ + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, mhg_docdict_params) + + def __call__(self, m, n, seed=None): + """Create a frozen multivariate_hypergeom distribution. + + See `multivariate_hypergeom_frozen` for more information. + """ + return multivariate_hypergeom_frozen(m, n, seed=seed) + + def _process_parameters(self, m, n): + m = np.asarray(m) + n = np.asarray(n) + if m.size == 0: + m = m.astype(int) + if n.size == 0: + n = n.astype(int) + if not np.issubdtype(m.dtype, np.integer): + raise TypeError("'m' must an array of integers.") + if not np.issubdtype(n.dtype, np.integer): + raise TypeError("'n' must an array of integers.") + if m.ndim == 0: + raise ValueError("'m' must be an array with" + " at least one dimension.") + + # check for empty arrays + if m.size != 0: + n = n[..., np.newaxis] + + m, n = np.broadcast_arrays(m, n) + + # check for empty arrays + if m.size != 0: + n = n[..., 0] + + mcond = m < 0 + + M = m.sum(axis=-1) + + ncond = (n < 0) | (n > M) + return M, m, n, mcond, ncond, np.any(mcond, axis=-1) | ncond + + def _process_quantiles(self, x, M, m, n): + x = np.asarray(x) + if not np.issubdtype(x.dtype, np.integer): + raise TypeError("'x' must an array of integers.") + if x.ndim == 0: + raise ValueError("'x' must be an array with" + " at least one dimension.") + if not x.shape[-1] == m.shape[-1]: + raise ValueError(f"Size of each quantile must be size of 'm': " + f"received {x.shape[-1]}, " + f"but expected {m.shape[-1]}.") + + # check for empty arrays + if m.size != 0: + n = n[..., np.newaxis] + M = M[..., np.newaxis] + + x, m, n, M = np.broadcast_arrays(x, m, n, M) + + # check for empty arrays + if m.size != 0: + n, M = n[..., 0], M[..., 0] + + xcond = (x < 0) | (x > m) + return (x, M, m, n, xcond, + np.any(xcond, axis=-1) | (x.sum(axis=-1) != n)) + + def _checkresult(self, result, cond, bad_value): + result = np.asarray(result) + if cond.ndim != 0: + result[cond] = bad_value + elif cond: + return bad_value + if result.ndim == 0: + return result[()] + return result + + def _logpmf(self, x, M, m, n, mxcond, ncond): + # This equation of the pmf comes from the relation, + # n combine r = beta(n+1, 1) / beta(r+1, n-r+1) + num = np.zeros_like(m, dtype=np.float64) + den = np.zeros_like(n, dtype=np.float64) + m, x = m[~mxcond], x[~mxcond] + M, n = M[~ncond], n[~ncond] + num[~mxcond] = (betaln(m+1, 1) - betaln(x+1, m-x+1)) + den[~ncond] = (betaln(M+1, 1) - betaln(n+1, M-n+1)) + num[mxcond] = np.nan + den[ncond] = np.nan + num = num.sum(axis=-1) + return num - den + + def logpmf(self, x, m, n): + """Log of the multivariate hypergeometric probability mass function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_doc_default_callparams)s + + Returns + ------- + logpmf : ndarray or scalar + Log of the probability mass function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + """ + M, m, n, mcond, ncond, mncond = self._process_parameters(m, n) + (x, M, m, n, xcond, + xcond_reduced) = self._process_quantiles(x, M, m, n) + mxcond = mcond | xcond + ncond = ncond | np.zeros(n.shape, dtype=np.bool_) + + result = self._logpmf(x, M, m, n, mxcond, ncond) + + # replace values for which x was out of the domain; broadcast + # xcond to the right shape + xcond_ = xcond_reduced | np.zeros(mncond.shape, dtype=np.bool_) + result = self._checkresult(result, xcond_, -np.inf) + + # replace values bad for n or m; broadcast + # mncond to the right shape + mncond_ = mncond | np.zeros(xcond_reduced.shape, dtype=np.bool_) + return self._checkresult(result, mncond_, np.nan) + + def pmf(self, x, m, n): + """Multivariate hypergeometric probability mass function. + + Parameters + ---------- + x : array_like + Quantiles, with the last axis of `x` denoting the components. + %(_doc_default_callparams)s + + Returns + ------- + pmf : ndarray or scalar + Probability density function evaluated at `x` + + Notes + ----- + %(_doc_callparams_note)s + """ + out = np.exp(self.logpmf(x, m, n)) + return out + + def mean(self, m, n): + """Mean of the multivariate hypergeometric distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + mean : array_like or scalar + The mean of the distribution + """ + M, m, n, _, _, mncond = self._process_parameters(m, n) + # check for empty arrays + if m.size != 0: + M, n = M[..., np.newaxis], n[..., np.newaxis] + cond = (M == 0) + M = np.ma.masked_array(M, mask=cond) + mu = n*(m/M) + if m.size != 0: + mncond = (mncond[..., np.newaxis] | + np.zeros(mu.shape, dtype=np.bool_)) + return self._checkresult(mu, mncond, np.nan) + + def var(self, m, n): + """Variance of the multivariate hypergeometric distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + array_like + The variances of the components of the distribution. This is + the diagonal of the covariance matrix of the distribution + """ + M, m, n, _, _, mncond = self._process_parameters(m, n) + # check for empty arrays + if m.size != 0: + M, n = M[..., np.newaxis], n[..., np.newaxis] + cond = (M == 0) & (M-1 == 0) + M = np.ma.masked_array(M, mask=cond) + output = n * m/M * (M-m)/M * (M-n)/(M-1) + if m.size != 0: + mncond = (mncond[..., np.newaxis] | + np.zeros(output.shape, dtype=np.bool_)) + return self._checkresult(output, mncond, np.nan) + + def cov(self, m, n): + """Covariance matrix of the multivariate hypergeometric distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + + Returns + ------- + cov : array_like + The covariance matrix of the distribution + """ + # see [1]_ for the formula and [2]_ for implementation + # cov( x_i,x_j ) = -n * (M-n)/(M-1) * (K_i*K_j) / (M**2) + M, m, n, _, _, mncond = self._process_parameters(m, n) + # check for empty arrays + if m.size != 0: + M = M[..., np.newaxis, np.newaxis] + n = n[..., np.newaxis, np.newaxis] + cond = (M == 0) & (M-1 == 0) + M = np.ma.masked_array(M, mask=cond) + output = (-n * (M-n)/(M-1) * + np.einsum("...i,...j->...ij", m, m) / (M**2)) + # check for empty arrays + if m.size != 0: + M, n = M[..., 0, 0], n[..., 0, 0] + cond = cond[..., 0, 0] + dim = m.shape[-1] + # diagonal entries need to be computed differently + for i in range(dim): + output[..., i, i] = (n * (M-n) * m[..., i]*(M-m[..., i])) + output[..., i, i] = output[..., i, i] / (M-1) + output[..., i, i] = output[..., i, i] / (M**2) + if m.size != 0: + mncond = (mncond[..., np.newaxis, np.newaxis] | + np.zeros(output.shape, dtype=np.bool_)) + return self._checkresult(output, mncond, np.nan) + + def rvs(self, m, n, size=None, random_state=None): + """Draw random samples from a multivariate hypergeometric distribution. + + Parameters + ---------- + %(_doc_default_callparams)s + size : integer or iterable of integers, optional + Number of samples to draw. Default is ``None``, in which case a + single variate is returned as an array with shape ``m.shape``. + %(_doc_random_state)s + + Returns + ------- + rvs : array_like + Random variates of shape ``size`` or ``m.shape`` + (if ``size=None``). + + Notes + ----- + %(_doc_callparams_note)s + + Also note that NumPy's `multivariate_hypergeometric` sampler is not + used as it doesn't support broadcasting. + """ + M, m, n, _, _, _ = self._process_parameters(m, n) + + random_state = self._get_random_state(random_state) + + if size is not None and isinstance(size, int): + size = (size, ) + + if size is None: + rvs = np.empty(m.shape, dtype=m.dtype) + else: + rvs = np.empty(size + (m.shape[-1], ), dtype=m.dtype) + rem = M + + # This sampler has been taken from numpy gh-13794 + # https://github.com/numpy/numpy/pull/13794 + for c in range(m.shape[-1] - 1): + rem = rem - m[..., c] + n0mask = n == 0 + rvs[..., c] = (~n0mask * + random_state.hypergeometric(m[..., c], + rem + n0mask, + n + n0mask, + size=size)) + n = n - rvs[..., c] + rvs[..., m.shape[-1] - 1] = n + + return rvs + + +multivariate_hypergeom = multivariate_hypergeom_gen() + + +class multivariate_hypergeom_frozen(multi_rv_frozen): + def __init__(self, m, n, seed=None): + self._dist = multivariate_hypergeom_gen(seed) + (self.M, self.m, self.n, + self.mcond, self.ncond, + self.mncond) = self._dist._process_parameters(m, n) + + # monkey patch self._dist + def _process_parameters(m, n): + return (self.M, self.m, self.n, + self.mcond, self.ncond, + self.mncond) + self._dist._process_parameters = _process_parameters + + def logpmf(self, x): + return self._dist.logpmf(x, self.m, self.n) + + def pmf(self, x): + return self._dist.pmf(x, self.m, self.n) + + def mean(self): + return self._dist.mean(self.m, self.n) + + def var(self): + return self._dist.var(self.m, self.n) + + def cov(self): + return self._dist.cov(self.m, self.n) + + def rvs(self, size=1, random_state=None): + return self._dist.rvs(self.m, self.n, + size=size, + random_state=random_state) + + +# Set frozen generator docstrings from corresponding docstrings in +# multivariate_hypergeom and fill in default strings in class docstrings +for name in ['logpmf', 'pmf', 'mean', 'var', 'cov', 'rvs']: + method = multivariate_hypergeom_gen.__dict__[name] + method_frozen = multivariate_hypergeom_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, mhg_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, + mhg_docdict_params) + + +class random_table_gen(multi_rv_generic): + r"""Contingency tables from independent samples with fixed marginal sums. + + This is the distribution of random tables with given row and column vector + sums. This distribution represents the set of random tables under the null + hypothesis that rows and columns are independent. It is used in hypothesis + tests of independence. + + Because of assumed independence, the expected frequency of each table + element can be computed from the row and column sums, so that the + distribution is completely determined by these two vectors. + + Methods + ------- + logpmf(x) + Log-probability of table `x` to occur in the distribution. + pmf(x) + Probability of table `x` to occur in the distribution. + mean(row, col) + Mean table. + rvs(row, col, size=None, method=None, random_state=None) + Draw random tables with given row and column vector sums. + + Parameters + ---------- + %(_doc_row_col)s + %(_doc_random_state)s + + Notes + ----- + %(_doc_row_col_note)s + + Random elements from the distribution are generated either with Boyett's + [1]_ or Patefield's algorithm [2]_. Boyett's algorithm has + O(N) time and space complexity, where N is the total sum of entries in the + table. Patefield's algorithm has O(K x log(N)) time complexity, where K is + the number of cells in the table and requires only a small constant work + space. By default, the `rvs` method selects the fastest algorithm based on + the input, but you can specify the algorithm with the keyword `method`. + Allowed values are "boyett" and "patefield". + + .. versionadded:: 1.10.0 + + Examples + -------- + >>> from scipy.stats import random_table + + >>> row = [1, 5] + >>> col = [2, 3, 1] + >>> random_table.mean(row, col) + array([[0.33333333, 0.5 , 0.16666667], + [1.66666667, 2.5 , 0.83333333]]) + + Alternatively, the object may be called (as a function) to fix the row + and column vector sums, returning a "frozen" distribution. + + >>> dist = random_table(row, col) + >>> dist.rvs(random_state=123) + array([[1., 0., 0.], + [1., 3., 1.]]) + + References + ---------- + .. [1] J. Boyett, AS 144 Appl. Statist. 28 (1979) 329-332 + .. [2] W.M. Patefield, AS 159 Appl. Statist. 30 (1981) 91-97 + """ + + def __init__(self, seed=None): + super().__init__(seed) + + def __call__(self, row, col, *, seed=None): + """Create a frozen distribution of tables with given marginals. + + See `random_table_frozen` for more information. + """ + return random_table_frozen(row, col, seed=seed) + + def logpmf(self, x, row, col): + """Log-probability of table to occur in the distribution. + + Parameters + ---------- + %(_doc_x)s + %(_doc_row_col)s + + Returns + ------- + logpmf : ndarray or scalar + Log of the probability mass function evaluated at `x`. + + Notes + ----- + %(_doc_row_col_note)s + + If row and column marginals of `x` do not match `row` and `col`, + negative infinity is returned. + + Examples + -------- + >>> from scipy.stats import random_table + >>> import numpy as np + + >>> x = [[1, 5, 1], [2, 3, 1]] + >>> row = np.sum(x, axis=1) + >>> col = np.sum(x, axis=0) + >>> random_table.logpmf(x, row, col) + -1.6306401200847027 + + Alternatively, the object may be called (as a function) to fix the row + and column vector sums, returning a "frozen" distribution. + + >>> d = random_table(row, col) + >>> d.logpmf(x) + -1.6306401200847027 + """ + r, c, n = self._process_parameters(row, col) + x = np.asarray(x) + + if x.ndim < 2: + raise ValueError("`x` must be at least two-dimensional") + + dtype_is_int = np.issubdtype(x.dtype, np.integer) + with np.errstate(invalid='ignore'): + if not dtype_is_int and not np.all(x.astype(int) == x): + raise ValueError("`x` must contain only integral values") + + # x does not contain NaN if we arrive here + if np.any(x < 0): + raise ValueError("`x` must contain only non-negative values") + + r2 = np.sum(x, axis=-1) + c2 = np.sum(x, axis=-2) + + if r2.shape[-1] != len(r): + raise ValueError("shape of `x` must agree with `row`") + + if c2.shape[-1] != len(c): + raise ValueError("shape of `x` must agree with `col`") + + res = np.empty(x.shape[:-2]) + + mask = np.all(r2 == r, axis=-1) & np.all(c2 == c, axis=-1) + + def lnfac(x): + return gammaln(x + 1) + + res[mask] = (np.sum(lnfac(r), axis=-1) + np.sum(lnfac(c), axis=-1) + - lnfac(n) - np.sum(lnfac(x[mask]), axis=(-1, -2))) + res[~mask] = -np.inf + + return res[()] + + def pmf(self, x, row, col): + """Probability of table to occur in the distribution. + + Parameters + ---------- + %(_doc_x)s + %(_doc_row_col)s + + Returns + ------- + pmf : ndarray or scalar + Probability mass function evaluated at `x`. + + Notes + ----- + %(_doc_row_col_note)s + + If row and column marginals of `x` do not match `row` and `col`, + zero is returned. + + Examples + -------- + >>> from scipy.stats import random_table + >>> import numpy as np + + >>> x = [[1, 5, 1], [2, 3, 1]] + >>> row = np.sum(x, axis=1) + >>> col = np.sum(x, axis=0) + >>> random_table.pmf(x, row, col) + 0.19580419580419592 + + Alternatively, the object may be called (as a function) to fix the row + and column vector sums, returning a "frozen" distribution. + + >>> d = random_table(row, col) + >>> d.pmf(x) + 0.19580419580419592 + """ + return np.exp(self.logpmf(x, row, col)) + + def mean(self, row, col): + """Mean of distribution of conditional tables. + %(_doc_mean_params)s + + Returns + ------- + mean: ndarray + Mean of the distribution. + + Notes + ----- + %(_doc_row_col_note)s + + Examples + -------- + >>> from scipy.stats import random_table + + >>> row = [1, 5] + >>> col = [2, 3, 1] + >>> random_table.mean(row, col) + array([[0.33333333, 0.5 , 0.16666667], + [1.66666667, 2.5 , 0.83333333]]) + + Alternatively, the object may be called (as a function) to fix the row + and column vector sums, returning a "frozen" distribution. + + >>> d = random_table(row, col) + >>> d.mean() + array([[0.33333333, 0.5 , 0.16666667], + [1.66666667, 2.5 , 0.83333333]]) + """ + r, c, n = self._process_parameters(row, col) + return np.outer(r, c) / n + + def rvs(self, row, col, *, size=None, method=None, random_state=None): + """Draw random tables with fixed column and row marginals. + + Parameters + ---------- + %(_doc_row_col)s + size : integer, optional + Number of samples to draw (default 1). + method : str, optional + Which method to use, "boyett" or "patefield". If None (default), + selects the fastest method for this input. + %(_doc_random_state)s + + Returns + ------- + rvs : ndarray + Random 2D tables of shape (`size`, `len(row)`, `len(col)`). + + Notes + ----- + %(_doc_row_col_note)s + + Examples + -------- + >>> from scipy.stats import random_table + + >>> row = [1, 5] + >>> col = [2, 3, 1] + >>> random_table.rvs(row, col, random_state=123) + array([[1., 0., 0.], + [1., 3., 1.]]) + + Alternatively, the object may be called (as a function) to fix the row + and column vector sums, returning a "frozen" distribution. + + >>> d = random_table(row, col) + >>> d.rvs(random_state=123) + array([[1., 0., 0.], + [1., 3., 1.]]) + """ + r, c, n = self._process_parameters(row, col) + size, shape = self._process_size_shape(size, r, c) + + random_state = self._get_random_state(random_state) + meth = self._process_rvs_method(method, r, c, n) + + return meth(r, c, n, size, random_state).reshape(shape) + + @staticmethod + def _process_parameters(row, col): + """ + Check that row and column vectors are one-dimensional, that they do + not contain negative or non-integer entries, and that the sums over + both vectors are equal. + """ + r = np.array(row, dtype=np.int64, copy=True) + c = np.array(col, dtype=np.int64, copy=True) + + if np.ndim(r) != 1: + raise ValueError("`row` must be one-dimensional") + if np.ndim(c) != 1: + raise ValueError("`col` must be one-dimensional") + + if np.any(r < 0): + raise ValueError("each element of `row` must be non-negative") + if np.any(c < 0): + raise ValueError("each element of `col` must be non-negative") + + n = np.sum(r) + if n != np.sum(c): + raise ValueError("sums over `row` and `col` must be equal") + + if not np.all(r == np.asarray(row)): + raise ValueError("each element of `row` must be an integer") + if not np.all(c == np.asarray(col)): + raise ValueError("each element of `col` must be an integer") + + return r, c, n + + @staticmethod + def _process_size_shape(size, r, c): + """ + Compute the number of samples to be drawn and the shape of the output + """ + shape = (len(r), len(c)) + + if size is None: + return 1, shape + + size = np.atleast_1d(size) + if not np.issubdtype(size.dtype, np.integer) or np.any(size < 0): + raise ValueError("`size` must be a non-negative integer or `None`") + + return np.prod(size), tuple(size) + shape + + @classmethod + def _process_rvs_method(cls, method, r, c, n): + known_methods = { + None: cls._rvs_select(r, c, n), + "boyett": cls._rvs_boyett, + "patefield": cls._rvs_patefield, + } + try: + return known_methods[method] + except KeyError: + raise ValueError(f"'{method}' not recognized, " + f"must be one of {set(known_methods)}") + + @classmethod + def _rvs_select(cls, r, c, n): + fac = 1.0 # benchmarks show that this value is about 1 + k = len(r) * len(c) # number of cells + # n + 1 guards against failure if n == 0 + if n > fac * np.log(n + 1) * k: + return cls._rvs_patefield + return cls._rvs_boyett + + @staticmethod + def _rvs_boyett(row, col, ntot, size, random_state): + return _rcont.rvs_rcont1(row, col, ntot, size, random_state) + + @staticmethod + def _rvs_patefield(row, col, ntot, size, random_state): + return _rcont.rvs_rcont2(row, col, ntot, size, random_state) + + +random_table = random_table_gen() + + +class random_table_frozen(multi_rv_frozen): + def __init__(self, row, col, *, seed=None): + self._dist = random_table_gen(seed) + self._params = self._dist._process_parameters(row, col) + + # monkey patch self._dist + def _process_parameters(r, c): + return self._params + self._dist._process_parameters = _process_parameters + + def logpmf(self, x): + return self._dist.logpmf(x, None, None) + + def pmf(self, x): + return self._dist.pmf(x, None, None) + + def mean(self): + return self._dist.mean(None, None) + + def rvs(self, size=None, method=None, random_state=None): + # optimisations are possible here + return self._dist.rvs(None, None, size=size, method=method, + random_state=random_state) + + +_ctab_doc_row_col = """\ +row : array_like + Sum of table entries in each row. +col : array_like + Sum of table entries in each column.""" + +_ctab_doc_x = """\ +x : array-like + Two-dimensional table of non-negative integers, or a + multi-dimensional array with the last two dimensions + corresponding with the tables.""" + +_ctab_doc_row_col_note = """\ +The row and column vectors must be one-dimensional, not empty, +and each sum up to the same value. They cannot contain negative +or noninteger entries.""" + +_ctab_doc_mean_params = f""" +Parameters +---------- +{_ctab_doc_row_col}""" + +_ctab_doc_row_col_note_frozen = """\ +See class definition for a detailed description of parameters.""" + +_ctab_docdict = { + "_doc_random_state": _doc_random_state, + "_doc_row_col": _ctab_doc_row_col, + "_doc_x": _ctab_doc_x, + "_doc_mean_params": _ctab_doc_mean_params, + "_doc_row_col_note": _ctab_doc_row_col_note, +} + +_ctab_docdict_frozen = _ctab_docdict.copy() +_ctab_docdict_frozen.update({ + "_doc_row_col": "", + "_doc_mean_params": "", + "_doc_row_col_note": _ctab_doc_row_col_note_frozen, +}) + + +def _docfill(obj, docdict, template=None): + obj.__doc__ = doccer.docformat(template or obj.__doc__, docdict) + + +# Set frozen generator docstrings from corresponding docstrings in +# random_table and fill in default strings in class docstrings +_docfill(random_table_gen, _ctab_docdict) +for name in ['logpmf', 'pmf', 'mean', 'rvs']: + method = random_table_gen.__dict__[name] + method_frozen = random_table_frozen.__dict__[name] + _docfill(method_frozen, _ctab_docdict_frozen, method.__doc__) + _docfill(method, _ctab_docdict) + + +class uniform_direction_gen(multi_rv_generic): + r"""A vector-valued uniform direction. + + Return a random direction (unit vector). The `dim` keyword specifies + the dimensionality of the space. + + Methods + ------- + rvs(dim=None, size=1, random_state=None) + Draw random directions. + + Parameters + ---------- + dim : scalar + Dimension of directions. + seed : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Notes + ----- + This distribution generates unit vectors uniformly distributed on + the surface of a hypersphere. These can be interpreted as random + directions. + For example, if `dim` is 3, 3D vectors from the surface of :math:`S^2` + will be sampled. + + References + ---------- + .. [1] Marsaglia, G. (1972). "Choosing a Point from the Surface of a + Sphere". Annals of Mathematical Statistics. 43 (2): 645-646. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import uniform_direction + >>> x = uniform_direction.rvs(3) + >>> np.linalg.norm(x) + 1. + + This generates one random direction, a vector on the surface of + :math:`S^2`. + + Alternatively, the object may be called (as a function) to return a frozen + distribution with fixed `dim` parameter. Here, + we create a `uniform_direction` with ``dim=3`` and draw 5 observations. + The samples are then arranged in an array of shape 5x3. + + >>> rng = np.random.default_rng() + >>> uniform_sphere_dist = uniform_direction(3) + >>> unit_vectors = uniform_sphere_dist.rvs(5, random_state=rng) + >>> unit_vectors + array([[ 0.56688642, -0.1332634 , -0.81294566], + [-0.427126 , -0.74779278, 0.50830044], + [ 0.3793989 , 0.92346629, 0.05715323], + [ 0.36428383, -0.92449076, -0.11231259], + [-0.27733285, 0.94410968, -0.17816678]]) + """ + + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__) + + def __call__(self, dim=None, seed=None): + """Create a frozen n-dimensional uniform direction distribution. + + See `uniform_direction` for more information. + """ + return uniform_direction_frozen(dim, seed=seed) + + def _process_parameters(self, dim): + """Dimension N must be specified; it cannot be inferred.""" + if dim is None or not np.isscalar(dim) or dim < 1 or dim != int(dim): + raise ValueError("Dimension of vector must be specified, " + "and must be an integer greater than 0.") + + return int(dim) + + def rvs(self, dim, size=None, random_state=None): + """Draw random samples from S(N-1). + + Parameters + ---------- + dim : integer + Dimension of space (N). + size : int or tuple of ints, optional + Given a shape of, for example, (m,n,k), m*n*k samples are + generated, and packed in an m-by-n-by-k arrangement. + Because each sample is N-dimensional, the output shape + is (m,n,k,N). If no shape is specified, a single (N-D) + sample is returned. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is + used, seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + rvs : ndarray + Random direction vectors + + """ + random_state = self._get_random_state(random_state) + if size is None: + size = np.array([], dtype=int) + size = np.atleast_1d(size) + + dim = self._process_parameters(dim) + + samples = _sample_uniform_direction(dim, size, random_state) + return samples + + +uniform_direction = uniform_direction_gen() + + +class uniform_direction_frozen(multi_rv_frozen): + def __init__(self, dim=None, seed=None): + """Create a frozen n-dimensional uniform direction distribution. + + Parameters + ---------- + dim : int + Dimension of matrices + seed : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Examples + -------- + >>> from scipy.stats import uniform_direction + >>> x = uniform_direction(3) + >>> x.rvs() + + """ + self._dist = uniform_direction_gen(seed) + self.dim = self._dist._process_parameters(dim) + + def rvs(self, size=None, random_state=None): + return self._dist.rvs(self.dim, size, random_state) + + +def _sample_uniform_direction(dim, size, random_state): + """ + Private method to generate uniform directions + Reference: Marsaglia, G. (1972). "Choosing a Point from the Surface of a + Sphere". Annals of Mathematical Statistics. 43 (2): 645-646. + """ + samples_shape = np.append(size, dim) + samples = random_state.standard_normal(samples_shape) + samples /= np.linalg.norm(samples, axis=-1, keepdims=True) + return samples + + +_dirichlet_mn_doc_default_callparams = """\ +alpha : array_like + The concentration parameters. The number of entries along the last axis + determines the dimensionality of the distribution. Each entry must be + strictly positive. +n : int or array_like + The number of trials. Each element must be a strictly positive integer. +""" + +_dirichlet_mn_doc_frozen_callparams = "" + +_dirichlet_mn_doc_frozen_callparams_note = """\ +See class definition for a detailed description of parameters.""" + +dirichlet_mn_docdict_params = { + '_dirichlet_mn_doc_default_callparams': _dirichlet_mn_doc_default_callparams, + '_doc_random_state': _doc_random_state +} + +dirichlet_mn_docdict_noparams = { + '_dirichlet_mn_doc_default_callparams': _dirichlet_mn_doc_frozen_callparams, + '_doc_random_state': _doc_random_state +} + + +def _dirichlet_multinomial_check_parameters(alpha, n, x=None): + + alpha = np.asarray(alpha) + n = np.asarray(n) + + if x is not None: + # Ensure that `x` and `alpha` are arrays. If the shapes are + # incompatible, NumPy will raise an appropriate error. + try: + x, alpha = np.broadcast_arrays(x, alpha) + except ValueError as e: + msg = "`x` and `alpha` must be broadcastable." + raise ValueError(msg) from e + + x_int = np.floor(x) + if np.any(x < 0) or np.any(x != x_int): + raise ValueError("`x` must contain only non-negative integers.") + x = x_int + + if np.any(alpha <= 0): + raise ValueError("`alpha` must contain only positive values.") + + n_int = np.floor(n) + if np.any(n <= 0) or np.any(n != n_int): + raise ValueError("`n` must be a positive integer.") + n = n_int + + sum_alpha = np.sum(alpha, axis=-1) + sum_alpha, n = np.broadcast_arrays(sum_alpha, n) + + return (alpha, sum_alpha, n) if x is None else (alpha, sum_alpha, n, x) + + +class dirichlet_multinomial_gen(multi_rv_generic): + r"""A Dirichlet multinomial random variable. + + The Dirichlet multinomial distribution is a compound probability + distribution: it is the multinomial distribution with number of trials + `n` and class probabilities ``p`` randomly sampled from a Dirichlet + distribution with concentration parameters ``alpha``. + + Methods + ------- + logpmf(x, alpha, n): + Log of the probability mass function. + pmf(x, alpha, n): + Probability mass function. + mean(alpha, n): + Mean of the Dirichlet multinomial distribution. + var(alpha, n): + Variance of the Dirichlet multinomial distribution. + cov(alpha, n): + The covariance of the Dirichlet multinomial distribution. + + Parameters + ---------- + %(_dirichlet_mn_doc_default_callparams)s + %(_doc_random_state)s + + See Also + -------- + scipy.stats.dirichlet : The dirichlet distribution. + scipy.stats.multinomial : The multinomial distribution. + + References + ---------- + .. [1] Dirichlet-multinomial distribution, Wikipedia, + https://www.wikipedia.org/wiki/Dirichlet-multinomial_distribution + + Examples + -------- + >>> from scipy.stats import dirichlet_multinomial + + Get the PMF + + >>> n = 6 # number of trials + >>> alpha = [3, 4, 5] # concentration parameters + >>> x = [1, 2, 3] # counts + >>> dirichlet_multinomial.pmf(x, alpha, n) + 0.08484162895927604 + + If the sum of category counts does not equal the number of trials, + the probability mass is zero. + + >>> dirichlet_multinomial.pmf(x, alpha, n=7) + 0.0 + + Get the log of the PMF + + >>> dirichlet_multinomial.logpmf(x, alpha, n) + -2.4669689491013327 + + Get the mean + + >>> dirichlet_multinomial.mean(alpha, n) + array([1.5, 2. , 2.5]) + + Get the variance + + >>> dirichlet_multinomial.var(alpha, n) + array([1.55769231, 1.84615385, 2.01923077]) + + Get the covariance + + >>> dirichlet_multinomial.cov(alpha, n) + array([[ 1.55769231, -0.69230769, -0.86538462], + [-0.69230769, 1.84615385, -1.15384615], + [-0.86538462, -1.15384615, 2.01923077]]) + + Alternatively, the object may be called (as a function) to fix the + `alpha` and `n` parameters, returning a "frozen" Dirichlet multinomial + random variable. + + >>> dm = dirichlet_multinomial(alpha, n) + >>> dm.pmf(x) + 0.08484162895927579 + + All methods are fully vectorized. Each element of `x` and `alpha` is + a vector (along the last axis), each element of `n` is an + integer (scalar), and the result is computed element-wise. + + >>> x = [[1, 2, 3], [4, 5, 6]] + >>> alpha = [[1, 2, 3], [4, 5, 6]] + >>> n = [6, 15] + >>> dirichlet_multinomial.pmf(x, alpha, n) + array([0.06493506, 0.02626937]) + + >>> dirichlet_multinomial.cov(alpha, n).shape # both covariance matrices + (2, 3, 3) + + Broadcasting according to standard NumPy conventions is supported. Here, + we have four sets of concentration parameters (each a two element vector) + for each of three numbers of trials (each a scalar). + + >>> alpha = [[3, 4], [4, 5], [5, 6], [6, 7]] + >>> n = [[6], [7], [8]] + >>> dirichlet_multinomial.mean(alpha, n).shape + (3, 4, 2) + + """ + def __init__(self, seed=None): + super().__init__(seed) + self.__doc__ = doccer.docformat(self.__doc__, + dirichlet_mn_docdict_params) + + def __call__(self, alpha, n, seed=None): + return dirichlet_multinomial_frozen(alpha, n, seed=seed) + + def logpmf(self, x, alpha, n): + """The log of the probability mass function. + + Parameters + ---------- + x: ndarray + Category counts (non-negative integers). Must be broadcastable + with shape parameter ``alpha``. If multidimensional, the last axis + must correspond with the categories. + %(_dirichlet_mn_doc_default_callparams)s + + Returns + ------- + out: ndarray or scalar + Log of the probability mass function. + + """ + + a, Sa, n, x = _dirichlet_multinomial_check_parameters(alpha, n, x) + + out = np.asarray(loggamma(Sa) + loggamma(n + 1) - loggamma(n + Sa)) + out += (loggamma(x + a) - (loggamma(a) + loggamma(x + 1))).sum(axis=-1) + np.place(out, n != x.sum(axis=-1), -np.inf) + return out[()] + + def pmf(self, x, alpha, n): + """Probability mass function for a Dirichlet multinomial distribution. + + Parameters + ---------- + x: ndarray + Category counts (non-negative integers). Must be broadcastable + with shape parameter ``alpha``. If multidimensional, the last axis + must correspond with the categories. + %(_dirichlet_mn_doc_default_callparams)s + + Returns + ------- + out: ndarray or scalar + Probability mass function. + + """ + return np.exp(self.logpmf(x, alpha, n)) + + def mean(self, alpha, n): + """Mean of a Dirichlet multinomial distribution. + + Parameters + ---------- + %(_dirichlet_mn_doc_default_callparams)s + + Returns + ------- + out: ndarray + Mean of a Dirichlet multinomial distribution. + + """ + a, Sa, n = _dirichlet_multinomial_check_parameters(alpha, n) + n, Sa = n[..., np.newaxis], Sa[..., np.newaxis] + return n * a / Sa + + def var(self, alpha, n): + """The variance of the Dirichlet multinomial distribution. + + Parameters + ---------- + %(_dirichlet_mn_doc_default_callparams)s + + Returns + ------- + out: array_like + The variances of the components of the distribution. This is + the diagonal of the covariance matrix of the distribution. + + """ + a, Sa, n = _dirichlet_multinomial_check_parameters(alpha, n) + n, Sa = n[..., np.newaxis], Sa[..., np.newaxis] + return n * a / Sa * (1 - a/Sa) * (n + Sa) / (1 + Sa) + + def cov(self, alpha, n): + """Covariance matrix of a Dirichlet multinomial distribution. + + Parameters + ---------- + %(_dirichlet_mn_doc_default_callparams)s + + Returns + ------- + out : array_like + The covariance matrix of the distribution. + + """ + a, Sa, n = _dirichlet_multinomial_check_parameters(alpha, n) + var = dirichlet_multinomial.var(a, n) + + n, Sa = n[..., np.newaxis, np.newaxis], Sa[..., np.newaxis, np.newaxis] + aiaj = a[..., :, np.newaxis] * a[..., np.newaxis, :] + cov = -n * aiaj / Sa ** 2 * (n + Sa) / (1 + Sa) + + ii = np.arange(cov.shape[-1]) + cov[..., ii, ii] = var + return cov + + +dirichlet_multinomial = dirichlet_multinomial_gen() + + +class dirichlet_multinomial_frozen(multi_rv_frozen): + def __init__(self, alpha, n, seed=None): + alpha, Sa, n = _dirichlet_multinomial_check_parameters(alpha, n) + self.alpha = alpha + self.n = n + self._dist = dirichlet_multinomial_gen(seed) + + def logpmf(self, x): + return self._dist.logpmf(x, self.alpha, self.n) + + def pmf(self, x): + return self._dist.pmf(x, self.alpha, self.n) + + def mean(self): + return self._dist.mean(self.alpha, self.n) + + def var(self): + return self._dist.var(self.alpha, self.n) + + def cov(self): + return self._dist.cov(self.alpha, self.n) + + +# Set frozen generator docstrings from corresponding docstrings in +# dirichlet_multinomial and fill in default strings in class docstrings. +for name in ['logpmf', 'pmf', 'mean', 'var', 'cov']: + method = dirichlet_multinomial_gen.__dict__[name] + method_frozen = dirichlet_multinomial_frozen.__dict__[name] + method_frozen.__doc__ = doccer.docformat( + method.__doc__, dirichlet_mn_docdict_noparams) + method.__doc__ = doccer.docformat(method.__doc__, + dirichlet_mn_docdict_params) + + +class vonmises_fisher_gen(multi_rv_generic): + r"""A von Mises-Fisher variable. + + The `mu` keyword specifies the mean direction vector. The `kappa` keyword + specifies the concentration parameter. + + Methods + ------- + pdf(x, mu=None, kappa=1) + Probability density function. + logpdf(x, mu=None, kappa=1) + Log of the probability density function. + rvs(mu=None, kappa=1, size=1, random_state=None) + Draw random samples from a von Mises-Fisher distribution. + entropy(mu=None, kappa=1) + Compute the differential entropy of the von Mises-Fisher distribution. + fit(data) + Fit a von Mises-Fisher distribution to data. + + Parameters + ---------- + mu : array_like + Mean direction of the distribution. Must be a one-dimensional unit + vector of norm 1. + kappa : float + Concentration parameter. Must be positive. + seed : {None, int, np.random.RandomState, np.random.Generator}, optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + See Also + -------- + scipy.stats.vonmises : Von-Mises Fisher distribution in 2D on a circle + uniform_direction : uniform distribution on the surface of a hypersphere + + Notes + ----- + The von Mises-Fisher distribution is a directional distribution on the + surface of the unit hypersphere. The probability density + function of a unit vector :math:`\mathbf{x}` is + + .. math:: + + f(\mathbf{x}) = \frac{\kappa^{d/2-1}}{(2\pi)^{d/2}I_{d/2-1}(\kappa)} + \exp\left(\kappa \mathbf{\mu}^T\mathbf{x}\right), + + where :math:`\mathbf{\mu}` is the mean direction, :math:`\kappa` the + concentration parameter, :math:`d` the dimension and :math:`I` the + modified Bessel function of the first kind. As :math:`\mu` represents + a direction, it must be a unit vector or in other words, a point + on the hypersphere: :math:`\mathbf{\mu}\in S^{d-1}`. :math:`\kappa` is a + concentration parameter, which means that it must be positive + (:math:`\kappa>0`) and that the distribution becomes more narrow with + increasing :math:`\kappa`. In that sense, the reciprocal value + :math:`1/\kappa` resembles the variance parameter of the normal + distribution. + + The von Mises-Fisher distribution often serves as an analogue of the + normal distribution on the sphere. Intuitively, for unit vectors, a + useful distance measure is given by the angle :math:`\alpha` between + them. This is exactly what the scalar product + :math:`\mathbf{\mu}^T\mathbf{x}=\cos(\alpha)` in the + von Mises-Fisher probability density function describes: the angle + between the mean direction :math:`\mathbf{\mu}` and the vector + :math:`\mathbf{x}`. The larger the angle between them, the smaller the + probability to observe :math:`\mathbf{x}` for this particular mean + direction :math:`\mathbf{\mu}`. + + In dimensions 2 and 3, specialized algorithms are used for fast sampling + [2]_, [3]_. For dimensions of 4 or higher the rejection sampling algorithm + described in [4]_ is utilized. This implementation is partially based on + the geomstats package [5]_, [6]_. + + .. versionadded:: 1.11 + + References + ---------- + .. [1] Von Mises-Fisher distribution, Wikipedia, + https://en.wikipedia.org/wiki/Von_Mises%E2%80%93Fisher_distribution + .. [2] Mardia, K., and Jupp, P. Directional statistics. Wiley, 2000. + .. [3] J. Wenzel. Numerically stable sampling of the von Mises Fisher + distribution on S2. + https://www.mitsuba-renderer.org/~wenzel/files/vmf.pdf + .. [4] Wood, A. Simulation of the von mises fisher distribution. + Communications in statistics-simulation and computation 23, + 1 (1994), 157-164. https://doi.org/10.1080/03610919408813161 + .. [5] geomstats, Github. MIT License. Accessed: 06.01.2023. + https://github.com/geomstats/geomstats + .. [6] Miolane, N. et al. Geomstats: A Python Package for Riemannian + Geometry in Machine Learning. Journal of Machine Learning Research + 21 (2020). http://jmlr.org/papers/v21/19-027.html + + Examples + -------- + **Visualization of the probability density** + + Plot the probability density in three dimensions for increasing + concentration parameter. The density is calculated by the ``pdf`` + method. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import vonmises_fisher + >>> from matplotlib.colors import Normalize + >>> n_grid = 100 + >>> u = np.linspace(0, np.pi, n_grid) + >>> v = np.linspace(0, 2 * np.pi, n_grid) + >>> u_grid, v_grid = np.meshgrid(u, v) + >>> vertices = np.stack([np.cos(v_grid) * np.sin(u_grid), + ... np.sin(v_grid) * np.sin(u_grid), + ... np.cos(u_grid)], + ... axis=2) + >>> x = np.outer(np.cos(v), np.sin(u)) + >>> y = np.outer(np.sin(v), np.sin(u)) + >>> z = np.outer(np.ones_like(u), np.cos(u)) + >>> def plot_vmf_density(ax, x, y, z, vertices, mu, kappa): + ... vmf = vonmises_fisher(mu, kappa) + ... pdf_values = vmf.pdf(vertices) + ... pdfnorm = Normalize(vmin=pdf_values.min(), vmax=pdf_values.max()) + ... ax.plot_surface(x, y, z, rstride=1, cstride=1, + ... facecolors=plt.cm.viridis(pdfnorm(pdf_values)), + ... linewidth=0) + ... ax.set_aspect('equal') + ... ax.view_init(azim=-130, elev=0) + ... ax.axis('off') + ... ax.set_title(rf"$\kappa={kappa}$") + >>> fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(9, 4), + ... subplot_kw={"projection": "3d"}) + >>> left, middle, right = axes + >>> mu = np.array([-np.sqrt(0.5), -np.sqrt(0.5), 0]) + >>> plot_vmf_density(left, x, y, z, vertices, mu, 5) + >>> plot_vmf_density(middle, x, y, z, vertices, mu, 20) + >>> plot_vmf_density(right, x, y, z, vertices, mu, 100) + >>> plt.subplots_adjust(top=1, bottom=0.0, left=0.0, right=1.0, wspace=0.) + >>> plt.show() + + As we increase the concentration parameter, the points are getting more + clustered together around the mean direction. + + **Sampling** + + Draw 5 samples from the distribution using the ``rvs`` method resulting + in a 5x3 array. + + >>> rng = np.random.default_rng() + >>> mu = np.array([0, 0, 1]) + >>> samples = vonmises_fisher(mu, 20).rvs(5, random_state=rng) + >>> samples + array([[ 0.3884594 , -0.32482588, 0.86231516], + [ 0.00611366, -0.09878289, 0.99509023], + [-0.04154772, -0.01637135, 0.99900239], + [-0.14613735, 0.12553507, 0.98126695], + [-0.04429884, -0.23474054, 0.97104814]]) + + These samples are unit vectors on the sphere :math:`S^2`. To verify, + let us calculate their euclidean norms: + + >>> np.linalg.norm(samples, axis=1) + array([1., 1., 1., 1., 1.]) + + Plot 20 observations drawn from the von Mises-Fisher distribution for + increasing concentration parameter :math:`\kappa`. The red dot highlights + the mean direction :math:`\mu`. + + >>> def plot_vmf_samples(ax, x, y, z, mu, kappa): + ... vmf = vonmises_fisher(mu, kappa) + ... samples = vmf.rvs(20) + ... ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=0, + ... alpha=0.2) + ... ax.scatter(samples[:, 0], samples[:, 1], samples[:, 2], c='k', s=5) + ... ax.scatter(mu[0], mu[1], mu[2], c='r', s=30) + ... ax.set_aspect('equal') + ... ax.view_init(azim=-130, elev=0) + ... ax.axis('off') + ... ax.set_title(rf"$\kappa={kappa}$") + >>> mu = np.array([-np.sqrt(0.5), -np.sqrt(0.5), 0]) + >>> fig, axes = plt.subplots(nrows=1, ncols=3, + ... subplot_kw={"projection": "3d"}, + ... figsize=(9, 4)) + >>> left, middle, right = axes + >>> plot_vmf_samples(left, x, y, z, mu, 5) + >>> plot_vmf_samples(middle, x, y, z, mu, 20) + >>> plot_vmf_samples(right, x, y, z, mu, 100) + >>> plt.subplots_adjust(top=1, bottom=0.0, left=0.0, + ... right=1.0, wspace=0.) + >>> plt.show() + + The plots show that with increasing concentration :math:`\kappa` the + resulting samples are centered more closely around the mean direction. + + **Fitting the distribution parameters** + + The distribution can be fitted to data using the ``fit`` method returning + the estimated parameters. As a toy example let's fit the distribution to + samples drawn from a known von Mises-Fisher distribution. + + >>> mu, kappa = np.array([0, 0, 1]), 20 + >>> samples = vonmises_fisher(mu, kappa).rvs(1000, random_state=rng) + >>> mu_fit, kappa_fit = vonmises_fisher.fit(samples) + >>> mu_fit, kappa_fit + (array([0.01126519, 0.01044501, 0.99988199]), 19.306398751730995) + + We see that the estimated parameters `mu_fit` and `kappa_fit` are + very close to the ground truth parameters. + + """ + def __init__(self, seed=None): + super().__init__(seed) + + def __call__(self, mu=None, kappa=1, seed=None): + """Create a frozen von Mises-Fisher distribution. + + See `vonmises_fisher_frozen` for more information. + """ + return vonmises_fisher_frozen(mu, kappa, seed=seed) + + def _process_parameters(self, mu, kappa): + """ + Infer dimensionality from mu and ensure that mu is a one-dimensional + unit vector and kappa positive. + """ + mu = np.asarray(mu) + if mu.ndim > 1: + raise ValueError("'mu' must have one-dimensional shape.") + if not np.allclose(np.linalg.norm(mu), 1.): + raise ValueError("'mu' must be a unit vector of norm 1.") + if not mu.size > 1: + raise ValueError("'mu' must have at least two entries.") + kappa_error_msg = "'kappa' must be a positive scalar." + if not np.isscalar(kappa) or kappa < 0: + raise ValueError(kappa_error_msg) + if float(kappa) == 0.: + raise ValueError("For 'kappa=0' the von Mises-Fisher distribution " + "becomes the uniform distribution on the sphere " + "surface. Consider using " + "'scipy.stats.uniform_direction' instead.") + dim = mu.size + + return dim, mu, kappa + + def _check_data_vs_dist(self, x, dim): + if x.shape[-1] != dim: + raise ValueError("The dimensionality of the last axis of 'x' must " + "match the dimensionality of the " + "von Mises Fisher distribution.") + if not np.allclose(np.linalg.norm(x, axis=-1), 1.): + msg = "'x' must be unit vectors of norm 1 along last dimension." + raise ValueError(msg) + + def _log_norm_factor(self, dim, kappa): + # normalization factor is given by + # c = kappa**(dim/2-1)/((2*pi)**(dim/2)*I[dim/2-1](kappa)) + # = kappa**(dim/2-1)*exp(-kappa) / + # ((2*pi)**(dim/2)*I[dim/2-1](kappa)*exp(-kappa) + # = kappa**(dim/2-1)*exp(-kappa) / + # ((2*pi)**(dim/2)*ive[dim/2-1](kappa) + # Then the log is given by + # log c = 1/2*(dim -1)*log(kappa) - kappa - -1/2*dim*ln(2*pi) - + # ive[dim/2-1](kappa) + halfdim = 0.5 * dim + return (0.5 * (dim - 2)*np.log(kappa) - halfdim * _LOG_2PI - + np.log(ive(halfdim - 1, kappa)) - kappa) + + def _logpdf(self, x, dim, mu, kappa): + """Log of the von Mises-Fisher probability density function. + + As this function does no argument checking, it should not be + called directly; use 'logpdf' instead. + + """ + x = np.asarray(x) + self._check_data_vs_dist(x, dim) + dotproducts = np.einsum('i,...i->...', mu, x) + return self._log_norm_factor(dim, kappa) + kappa * dotproducts + + def logpdf(self, x, mu=None, kappa=1): + """Log of the von Mises-Fisher probability density function. + + Parameters + ---------- + x : array_like + Points at which to evaluate the log of the probability + density function. The last axis of `x` must correspond + to unit vectors of the same dimensionality as the distribution. + mu : array_like, default: None + Mean direction of the distribution. Must be a one-dimensional unit + vector of norm 1. + kappa : float, default: 1 + Concentration parameter. Must be positive. + + Returns + ------- + logpdf : ndarray or scalar + Log of the probability density function evaluated at `x`. + + """ + dim, mu, kappa = self._process_parameters(mu, kappa) + return self._logpdf(x, dim, mu, kappa) + + def pdf(self, x, mu=None, kappa=1): + """Von Mises-Fisher probability density function. + + Parameters + ---------- + x : array_like + Points at which to evaluate the probability + density function. The last axis of `x` must correspond + to unit vectors of the same dimensionality as the distribution. + mu : array_like + Mean direction of the distribution. Must be a one-dimensional unit + vector of norm 1. + kappa : float + Concentration parameter. Must be positive. + + Returns + ------- + pdf : ndarray or scalar + Probability density function evaluated at `x`. + + """ + dim, mu, kappa = self._process_parameters(mu, kappa) + return np.exp(self._logpdf(x, dim, mu, kappa)) + + def _rvs_2d(self, mu, kappa, size, random_state): + """ + In 2D, the von Mises-Fisher distribution reduces to the + von Mises distribution which can be efficiently sampled by numpy. + This method is much faster than the general rejection + sampling based algorithm. + + """ + mean_angle = np.arctan2(mu[1], mu[0]) + angle_samples = random_state.vonmises(mean_angle, kappa, size=size) + samples = np.stack([np.cos(angle_samples), np.sin(angle_samples)], + axis=-1) + return samples + + def _rvs_3d(self, kappa, size, random_state): + """ + Generate samples from a von Mises-Fisher distribution + with mu = [1, 0, 0] and kappa. Samples then have to be + rotated towards the desired mean direction mu. + This method is much faster than the general rejection + sampling based algorithm. + Reference: https://www.mitsuba-renderer.org/~wenzel/files/vmf.pdf + + """ + if size is None: + sample_size = 1 + else: + sample_size = size + + # compute x coordinate acc. to equation from section 3.1 + x = random_state.random(sample_size) + x = 1. + np.log(x + (1. - x) * np.exp(-2 * kappa))/kappa + + # (y, z) are random 2D vectors that only have to be + # normalized accordingly. Then (x, y z) follow a VMF distribution + temp = np.sqrt(1. - np.square(x)) + uniformcircle = _sample_uniform_direction(2, sample_size, random_state) + samples = np.stack([x, temp * uniformcircle[..., 0], + temp * uniformcircle[..., 1]], + axis=-1) + if size is None: + samples = np.squeeze(samples) + return samples + + def _rejection_sampling(self, dim, kappa, size, random_state): + """ + Generate samples from a n-dimensional von Mises-Fisher distribution + with mu = [1, 0, ..., 0] and kappa via rejection sampling. + Samples then have to be rotated towards the desired mean direction mu. + Reference: https://doi.org/10.1080/03610919408813161 + """ + dim_minus_one = dim - 1 + # calculate number of requested samples + if size is not None: + if not np.iterable(size): + size = (size, ) + n_samples = math.prod(size) + else: + n_samples = 1 + # calculate envelope for rejection sampler (eq. 4) + sqrt = np.sqrt(4 * kappa ** 2. + dim_minus_one ** 2) + envelop_param = (-2 * kappa + sqrt) / dim_minus_one + if envelop_param == 0: + # the regular formula suffers from loss of precision for high + # kappa. This can only be detected by checking for 0 here. + # Workaround: expansion for sqrt variable + # https://www.wolframalpha.com/input?i=sqrt%284*x%5E2%2Bd%5E2%29 + # e = (-2 * k + sqrt(k**2 + d**2)) / d + # ~ (-2 * k + 2 * k + d**2/(4 * k) - d**4/(64 * k**3)) / d + # = d/(4 * k) - d**3/(64 * k**3) + envelop_param = (dim_minus_one/4 * kappa**-1. + - dim_minus_one**3/64 * kappa**-3.) + # reference step 0 + node = (1. - envelop_param) / (1. + envelop_param) + # t = ln(1 - ((1-x)/(1+x))**2) + # = ln(4 * x / (1+x)**2) + # = ln(4) + ln(x) - 2*log1p(x) + correction = (kappa * node + dim_minus_one + * (np.log(4) + np.log(envelop_param) + - 2 * np.log1p(envelop_param))) + n_accepted = 0 + x = np.zeros((n_samples, )) + halfdim = 0.5 * dim_minus_one + # main loop + while n_accepted < n_samples: + # generate candidates acc. to reference step 1 + sym_beta = random_state.beta(halfdim, halfdim, + size=n_samples - n_accepted) + coord_x = (1 - (1 + envelop_param) * sym_beta) / ( + 1 - (1 - envelop_param) * sym_beta) + # accept or reject: reference step 2 + # reformulation for numerical stability: + # t = ln(1 - (1-x)/(1+x) * y) + # = ln((1 + x - y +x*y)/(1 +x)) + accept_tol = random_state.random(n_samples - n_accepted) + criterion = ( + kappa * coord_x + + dim_minus_one * (np.log((1 + envelop_param - coord_x + + coord_x * envelop_param) / (1 + envelop_param))) + - correction) > np.log(accept_tol) + accepted_iter = np.sum(criterion) + x[n_accepted:n_accepted + accepted_iter] = coord_x[criterion] + n_accepted += accepted_iter + # concatenate x and remaining coordinates: step 3 + coord_rest = _sample_uniform_direction(dim_minus_one, n_accepted, + random_state) + coord_rest = np.einsum( + '...,...i->...i', np.sqrt(1 - x ** 2), coord_rest) + samples = np.concatenate([x[..., None], coord_rest], axis=1) + # reshape output to (size, dim) + if size is not None: + samples = samples.reshape(size + (dim, )) + else: + samples = np.squeeze(samples) + return samples + + def _rotate_samples(self, samples, mu, dim): + """A QR decomposition is used to find the rotation that maps the + north pole (1, 0,...,0) to the vector mu. This rotation is then + applied to all samples. + + Parameters + ---------- + samples: array_like, shape = [..., n] + mu : array-like, shape=[n, ] + Point to parametrise the rotation. + + Returns + ------- + samples : rotated samples + + """ + base_point = np.zeros((dim, )) + base_point[0] = 1. + embedded = np.concatenate([mu[None, :], np.zeros((dim - 1, dim))]) + rotmatrix, _ = np.linalg.qr(np.transpose(embedded)) + if np.allclose(np.matmul(rotmatrix, base_point[:, None])[:, 0], mu): + rotsign = 1 + else: + rotsign = -1 + + # apply rotation + samples = np.einsum('ij,...j->...i', rotmatrix, samples) * rotsign + return samples + + def _rvs(self, dim, mu, kappa, size, random_state): + if dim == 2: + samples = self._rvs_2d(mu, kappa, size, random_state) + elif dim == 3: + samples = self._rvs_3d(kappa, size, random_state) + else: + samples = self._rejection_sampling(dim, kappa, size, + random_state) + + if dim != 2: + samples = self._rotate_samples(samples, mu, dim) + return samples + + def rvs(self, mu=None, kappa=1, size=1, random_state=None): + """Draw random samples from a von Mises-Fisher distribution. + + Parameters + ---------- + mu : array_like + Mean direction of the distribution. Must be a one-dimensional unit + vector of norm 1. + kappa : float + Concentration parameter. Must be positive. + size : int or tuple of ints, optional + Given a shape of, for example, (m,n,k), m*n*k samples are + generated, and packed in an m-by-n-by-k arrangement. + Because each sample is N-dimensional, the output shape + is (m,n,k,N). If no shape is specified, a single (N-D) + sample is returned. + random_state : {None, int, np.random.RandomState, np.random.Generator}, + optional + Used for drawing random variates. + If `seed` is `None`, the `~np.random.RandomState` singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, seeded + with seed. + If `seed` is already a ``RandomState`` or ``Generator`` instance, + then that object is used. + Default is `None`. + + Returns + ------- + rvs : ndarray + Random variates of shape (`size`, `N`), where `N` is the + dimension of the distribution. + + """ + dim, mu, kappa = self._process_parameters(mu, kappa) + random_state = self._get_random_state(random_state) + samples = self._rvs(dim, mu, kappa, size, random_state) + return samples + + def _entropy(self, dim, kappa): + halfdim = 0.5 * dim + return (-self._log_norm_factor(dim, kappa) - kappa * + ive(halfdim, kappa) / ive(halfdim - 1, kappa)) + + def entropy(self, mu=None, kappa=1): + """Compute the differential entropy of the von Mises-Fisher + distribution. + + Parameters + ---------- + mu : array_like, default: None + Mean direction of the distribution. Must be a one-dimensional unit + vector of norm 1. + kappa : float, default: 1 + Concentration parameter. Must be positive. + + Returns + ------- + h : scalar + Entropy of the von Mises-Fisher distribution. + + """ + dim, _, kappa = self._process_parameters(mu, kappa) + return self._entropy(dim, kappa) + + def fit(self, x): + """Fit the von Mises-Fisher distribution to data. + + Parameters + ---------- + x : array-like + Data the distribution is fitted to. Must be two dimensional. + The second axis of `x` must be unit vectors of norm 1 and + determine the dimensionality of the fitted + von Mises-Fisher distribution. + + Returns + ------- + mu : ndarray + Estimated mean direction. + kappa : float + Estimated concentration parameter. + + """ + # validate input data + x = np.asarray(x) + if x.ndim != 2: + raise ValueError("'x' must be two dimensional.") + if not np.allclose(np.linalg.norm(x, axis=-1), 1.): + msg = "'x' must be unit vectors of norm 1 along last dimension." + raise ValueError(msg) + dim = x.shape[-1] + + # mu is simply the directional mean + dirstats = directional_stats(x) + mu = dirstats.mean_direction + r = dirstats.mean_resultant_length + + # kappa is the solution to the equation: + # r = I[dim/2](kappa) / I[dim/2 -1](kappa) + # = I[dim/2](kappa) * exp(-kappa) / I[dim/2 -1](kappa) * exp(-kappa) + # = ive(dim/2, kappa) / ive(dim/2 -1, kappa) + + halfdim = 0.5 * dim + + def solve_for_kappa(kappa): + bessel_vals = ive([halfdim, halfdim - 1], kappa) + return bessel_vals[0]/bessel_vals[1] - r + + root_res = root_scalar(solve_for_kappa, method="brentq", + bracket=(1e-8, 1e9)) + kappa = root_res.root + return mu, kappa + + +vonmises_fisher = vonmises_fisher_gen() + + +class vonmises_fisher_frozen(multi_rv_frozen): + def __init__(self, mu=None, kappa=1, seed=None): + """Create a frozen von Mises-Fisher distribution. + + Parameters + ---------- + mu : array_like, default: None + Mean direction of the distribution. + kappa : float, default: 1 + Concentration parameter. Must be positive. + seed : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + """ + self._dist = vonmises_fisher_gen(seed) + self.dim, self.mu, self.kappa = ( + self._dist._process_parameters(mu, kappa) + ) + + def logpdf(self, x): + """ + Parameters + ---------- + x : array_like + Points at which to evaluate the log of the probability + density function. The last axis of `x` must correspond + to unit vectors of the same dimensionality as the distribution. + + Returns + ------- + logpdf : ndarray or scalar + Log of probability density function evaluated at `x`. + + """ + return self._dist._logpdf(x, self.dim, self.mu, self.kappa) + + def pdf(self, x): + """ + Parameters + ---------- + x : array_like + Points at which to evaluate the log of the probability + density function. The last axis of `x` must correspond + to unit vectors of the same dimensionality as the distribution. + + Returns + ------- + pdf : ndarray or scalar + Probability density function evaluated at `x`. + + """ + return np.exp(self.logpdf(x)) + + def rvs(self, size=1, random_state=None): + """Draw random variates from the Von Mises-Fisher distribution. + + Parameters + ---------- + size : int or tuple of ints, optional + Given a shape of, for example, (m,n,k), m*n*k samples are + generated, and packed in an m-by-n-by-k arrangement. + Because each sample is N-dimensional, the output shape + is (m,n,k,N). If no shape is specified, a single (N-D) + sample is returned. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance + then that instance is used. + + Returns + ------- + rvs : ndarray or scalar + Random variates of size (`size`, `N`), where `N` is the + dimension of the distribution. + + """ + random_state = self._dist._get_random_state(random_state) + return self._dist._rvs(self.dim, self.mu, self.kappa, size, + random_state) + + def entropy(self): + """ + Calculate the differential entropy of the von Mises-Fisher + distribution. + + Returns + ------- + h: float + Entropy of the Von Mises-Fisher distribution. + + """ + return self._dist._entropy(self.dim, self.kappa) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_odds_ratio.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_odds_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..3dd588c4972b39e720a4359099ff8157460f5497 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_odds_ratio.py @@ -0,0 +1,482 @@ +import numpy as np + +from scipy.special import ndtri +from scipy.optimize import brentq +from ._discrete_distns import nchypergeom_fisher +from ._common import ConfidenceInterval + + +def _sample_odds_ratio(table): + """ + Given a table [[a, b], [c, d]], compute a*d/(b*c). + + Return nan if the numerator and denominator are 0. + Return inf if just the denominator is 0. + """ + # table must be a 2x2 numpy array. + if table[1, 0] > 0 and table[0, 1] > 0: + oddsratio = table[0, 0] * table[1, 1] / (table[1, 0] * table[0, 1]) + elif table[0, 0] == 0 or table[1, 1] == 0: + oddsratio = np.nan + else: + oddsratio = np.inf + return oddsratio + + +def _solve(func): + """ + Solve func(nc) = 0. func must be an increasing function. + """ + # We could just as well call the variable `x` instead of `nc`, but we + # always call this function with functions for which nc (the noncentrality + # parameter) is the variable for which we are solving. + nc = 1.0 + value = func(nc) + if value == 0: + return nc + + # Multiplicative factor by which to increase or decrease nc when + # searching for a bracketing interval. + factor = 2.0 + # Find a bracketing interval. + if value > 0: + nc /= factor + while func(nc) > 0: + nc /= factor + lo = nc + hi = factor*nc + else: + nc *= factor + while func(nc) < 0: + nc *= factor + lo = nc/factor + hi = nc + + # lo and hi bracket the solution for nc. + nc = brentq(func, lo, hi, xtol=1e-13) + return nc + + +def _nc_hypergeom_mean_inverse(x, M, n, N): + """ + For the given noncentral hypergeometric parameters x, M, n,and N + (table[0,0], total, row 0 sum and column 0 sum, resp., of a 2x2 + contingency table), find the noncentrality parameter of Fisher's + noncentral hypergeometric distribution whose mean is x. + """ + nc = _solve(lambda nc: nchypergeom_fisher.mean(M, n, N, nc) - x) + return nc + + +def _hypergeom_params_from_table(table): + # The notation M, n and N is consistent with stats.hypergeom and + # stats.nchypergeom_fisher. + x = table[0, 0] + M = table.sum() + n = table[0].sum() + N = table[:, 0].sum() + return x, M, n, N + + +def _ci_upper(table, alpha): + """ + Compute the upper end of the confidence interval. + """ + if _sample_odds_ratio(table) == np.inf: + return np.inf + + x, M, n, N = _hypergeom_params_from_table(table) + + # nchypergeom_fisher.cdf is a decreasing function of nc, so we negate + # it in the lambda expression. + nc = _solve(lambda nc: -nchypergeom_fisher.cdf(x, M, n, N, nc) + alpha) + return nc + + +def _ci_lower(table, alpha): + """ + Compute the lower end of the confidence interval. + """ + if _sample_odds_ratio(table) == 0: + return 0 + + x, M, n, N = _hypergeom_params_from_table(table) + + nc = _solve(lambda nc: nchypergeom_fisher.sf(x - 1, M, n, N, nc) - alpha) + return nc + + +def _conditional_oddsratio(table): + """ + Conditional MLE of the odds ratio for the 2x2 contingency table. + """ + x, M, n, N = _hypergeom_params_from_table(table) + # Get the bounds of the support. The support of the noncentral + # hypergeometric distribution with parameters M, n, and N is the same + # for all values of the noncentrality parameter, so we can use 1 here. + lo, hi = nchypergeom_fisher.support(M, n, N, 1) + + # Check if x is at one of the extremes of the support. If so, we know + # the odds ratio is either 0 or inf. + if x == lo: + # x is at the low end of the support. + return 0 + if x == hi: + # x is at the high end of the support. + return np.inf + + nc = _nc_hypergeom_mean_inverse(x, M, n, N) + return nc + + +def _conditional_oddsratio_ci(table, confidence_level=0.95, + alternative='two-sided'): + """ + Conditional exact confidence interval for the odds ratio. + """ + if alternative == 'two-sided': + alpha = 0.5*(1 - confidence_level) + lower = _ci_lower(table, alpha) + upper = _ci_upper(table, alpha) + elif alternative == 'less': + lower = 0.0 + upper = _ci_upper(table, 1 - confidence_level) + else: + # alternative == 'greater' + lower = _ci_lower(table, 1 - confidence_level) + upper = np.inf + + return lower, upper + + +def _sample_odds_ratio_ci(table, confidence_level=0.95, + alternative='two-sided'): + oddsratio = _sample_odds_ratio(table) + log_or = np.log(oddsratio) + se = np.sqrt((1/table).sum()) + if alternative == 'less': + z = ndtri(confidence_level) + loglow = -np.inf + loghigh = log_or + z*se + elif alternative == 'greater': + z = ndtri(confidence_level) + loglow = log_or - z*se + loghigh = np.inf + else: + # alternative is 'two-sided' + z = ndtri(0.5*confidence_level + 0.5) + loglow = log_or - z*se + loghigh = log_or + z*se + + return np.exp(loglow), np.exp(loghigh) + + +class OddsRatioResult: + """ + Result of `scipy.stats.contingency.odds_ratio`. See the + docstring for `odds_ratio` for more details. + + Attributes + ---------- + statistic : float + The computed odds ratio. + + * If `kind` is ``'sample'``, this is sample (or unconditional) + estimate, given by + ``table[0, 0]*table[1, 1]/(table[0, 1]*table[1, 0])``. + * If `kind` is ``'conditional'``, this is the conditional + maximum likelihood estimate for the odds ratio. It is + the noncentrality parameter of Fisher's noncentral + hypergeometric distribution with the same hypergeometric + parameters as `table` and whose mean is ``table[0, 0]``. + + Methods + ------- + confidence_interval : + Confidence interval for the odds ratio. + """ + + def __init__(self, _table, _kind, statistic): + # for now, no need to make _table and _kind public, since this sort of + # information is returned in very few `scipy.stats` results + self._table = _table + self._kind = _kind + self.statistic = statistic + + def __repr__(self): + return f"OddsRatioResult(statistic={self.statistic})" + + def confidence_interval(self, confidence_level=0.95, + alternative='two-sided'): + """ + Confidence interval for the odds ratio. + + Parameters + ---------- + confidence_level: float + Desired confidence level for the confidence interval. + The value must be given as a fraction between 0 and 1. + Default is 0.95 (meaning 95%). + + alternative : {'two-sided', 'less', 'greater'}, optional + The alternative hypothesis of the hypothesis test to which the + confidence interval corresponds. That is, suppose the null + hypothesis is that the true odds ratio equals ``OR`` and the + confidence interval is ``(low, high)``. Then the following options + for `alternative` are available (default is 'two-sided'): + + * 'two-sided': the true odds ratio is not equal to ``OR``. There + is evidence against the null hypothesis at the chosen + `confidence_level` if ``high < OR`` or ``low > OR``. + * 'less': the true odds ratio is less than ``OR``. The ``low`` end + of the confidence interval is 0, and there is evidence against + the null hypothesis at the chosen `confidence_level` if + ``high < OR``. + * 'greater': the true odds ratio is greater than ``OR``. The + ``high`` end of the confidence interval is ``np.inf``, and there + is evidence against the null hypothesis at the chosen + `confidence_level` if ``low > OR``. + + Returns + ------- + ci : ``ConfidenceInterval`` instance + The confidence interval, represented as an object with + attributes ``low`` and ``high``. + + Notes + ----- + When `kind` is ``'conditional'``, the limits of the confidence + interval are the conditional "exact confidence limits" as described + by Fisher [1]_. The conditional odds ratio and confidence interval are + also discussed in Section 4.1.2 of the text by Sahai and Khurshid [2]_. + + When `kind` is ``'sample'``, the confidence interval is computed + under the assumption that the logarithm of the odds ratio is normally + distributed with standard error given by:: + + se = sqrt(1/a + 1/b + 1/c + 1/d) + + where ``a``, ``b``, ``c`` and ``d`` are the elements of the + contingency table. (See, for example, [2]_, section 3.1.3.2, + or [3]_, section 2.3.3). + + References + ---------- + .. [1] R. A. Fisher (1935), The logic of inductive inference, + Journal of the Royal Statistical Society, Vol. 98, No. 1, + pp. 39-82. + .. [2] H. Sahai and A. Khurshid (1996), Statistics in Epidemiology: + Methods, Techniques, and Applications, CRC Press LLC, Boca + Raton, Florida. + .. [3] Alan Agresti, An Introduction to Categorical Data Analysis + (second edition), Wiley, Hoboken, NJ, USA (2007). + """ + if alternative not in ['two-sided', 'less', 'greater']: + raise ValueError("`alternative` must be 'two-sided', 'less' or " + "'greater'.") + + if confidence_level < 0 or confidence_level > 1: + raise ValueError('confidence_level must be between 0 and 1') + + if self._kind == 'conditional': + ci = self._conditional_odds_ratio_ci(confidence_level, alternative) + else: + ci = self._sample_odds_ratio_ci(confidence_level, alternative) + return ci + + def _conditional_odds_ratio_ci(self, confidence_level=0.95, + alternative='two-sided'): + """ + Confidence interval for the conditional odds ratio. + """ + + table = self._table + if 0 in table.sum(axis=0) or 0 in table.sum(axis=1): + # If both values in a row or column are zero, the p-value is 1, + # the odds ratio is NaN and the confidence interval is (0, inf). + ci = (0, np.inf) + else: + ci = _conditional_oddsratio_ci(table, + confidence_level=confidence_level, + alternative=alternative) + return ConfidenceInterval(low=ci[0], high=ci[1]) + + def _sample_odds_ratio_ci(self, confidence_level=0.95, + alternative='two-sided'): + """ + Confidence interval for the sample odds ratio. + """ + if confidence_level < 0 or confidence_level > 1: + raise ValueError('confidence_level must be between 0 and 1') + + table = self._table + if 0 in table.sum(axis=0) or 0 in table.sum(axis=1): + # If both values in a row or column are zero, the p-value is 1, + # the odds ratio is NaN and the confidence interval is (0, inf). + ci = (0, np.inf) + else: + ci = _sample_odds_ratio_ci(table, + confidence_level=confidence_level, + alternative=alternative) + return ConfidenceInterval(low=ci[0], high=ci[1]) + + +def odds_ratio(table, *, kind='conditional'): + r""" + Compute the odds ratio for a 2x2 contingency table. + + Parameters + ---------- + table : array_like of ints + A 2x2 contingency table. Elements must be non-negative integers. + kind : str, optional + Which kind of odds ratio to compute, either the sample + odds ratio (``kind='sample'``) or the conditional odds ratio + (``kind='conditional'``). Default is ``'conditional'``. + + Returns + ------- + result : `~scipy.stats._result_classes.OddsRatioResult` instance + The returned object has two computed attributes: + + statistic : float + * If `kind` is ``'sample'``, this is sample (or unconditional) + estimate, given by + ``table[0, 0]*table[1, 1]/(table[0, 1]*table[1, 0])``. + * If `kind` is ``'conditional'``, this is the conditional + maximum likelihood estimate for the odds ratio. It is + the noncentrality parameter of Fisher's noncentral + hypergeometric distribution with the same hypergeometric + parameters as `table` and whose mean is ``table[0, 0]``. + + The object has the method `confidence_interval` that computes + the confidence interval of the odds ratio. + + See Also + -------- + scipy.stats.fisher_exact + relative_risk + + Notes + ----- + The conditional odds ratio was discussed by Fisher (see "Example 1" + of [1]_). Texts that cover the odds ratio include [2]_ and [3]_. + + .. versionadded:: 1.10.0 + + References + ---------- + .. [1] R. A. Fisher (1935), The logic of inductive inference, + Journal of the Royal Statistical Society, Vol. 98, No. 1, + pp. 39-82. + .. [2] Breslow NE, Day NE (1980). Statistical methods in cancer research. + Volume I - The analysis of case-control studies. IARC Sci Publ. + (32):5-338. PMID: 7216345. (See section 4.2.) + .. [3] H. Sahai and A. Khurshid (1996), Statistics in Epidemiology: + Methods, Techniques, and Applications, CRC Press LLC, Boca + Raton, Florida. + .. [4] Berger, Jeffrey S. et al. "Aspirin for the Primary Prevention of + Cardiovascular Events in Women and Men: A Sex-Specific + Meta-analysis of Randomized Controlled Trials." + JAMA, 295(3):306-313, :doi:`10.1001/jama.295.3.306`, 2006. + + Examples + -------- + In epidemiology, individuals are classified as "exposed" or + "unexposed" to some factor or treatment. If the occurrence of some + illness is under study, those who have the illness are often + classified as "cases", and those without it are "noncases". The + counts of the occurrences of these classes gives a contingency + table:: + + exposed unexposed + cases a b + noncases c d + + The sample odds ratio may be written ``(a/c) / (b/d)``. ``a/c`` can + be interpreted as the odds of a case occurring in the exposed group, + and ``b/d`` as the odds of a case occurring in the unexposed group. + The sample odds ratio is the ratio of these odds. If the odds ratio + is greater than 1, it suggests that there is a positive association + between being exposed and being a case. + + Interchanging the rows or columns of the contingency table inverts + the odds ratio, so it is import to understand the meaning of labels + given to the rows and columns of the table when interpreting the + odds ratio. + + In [4]_, the use of aspirin to prevent cardiovascular events in women + and men was investigated. The study notably concluded: + + ...aspirin therapy reduced the risk of a composite of + cardiovascular events due to its effect on reducing the risk of + ischemic stroke in women [...] + + The article lists studies of various cardiovascular events. Let's + focus on the ischemic stoke in women. + + The following table summarizes the results of the experiment in which + participants took aspirin or a placebo on a regular basis for several + years. Cases of ischemic stroke were recorded:: + + Aspirin Control/Placebo + Ischemic stroke 176 230 + No stroke 21035 21018 + + The question we ask is "Is there evidence that the aspirin reduces the + risk of ischemic stroke?" + + Compute the odds ratio: + + >>> from scipy.stats.contingency import odds_ratio + >>> res = odds_ratio([[176, 230], [21035, 21018]]) + >>> res.statistic + 0.7646037659999126 + + For this sample, the odds of getting an ischemic stroke for those who have + been taking aspirin are 0.76 times that of those + who have received the placebo. + + To make statistical inferences about the population under study, + we can compute the 95% confidence interval for the odds ratio: + + >>> res.confidence_interval(confidence_level=0.95) + ConfidenceInterval(low=0.6241234078749812, high=0.9354102892100372) + + The 95% confidence interval for the conditional odds ratio is + approximately (0.62, 0.94). + + The fact that the entire 95% confidence interval falls below 1 supports + the authors' conclusion that the aspirin was associated with a + statistically significant reduction in ischemic stroke. + """ + if kind not in ['conditional', 'sample']: + raise ValueError("`kind` must be 'conditional' or 'sample'.") + + c = np.asarray(table) + + if c.shape != (2, 2): + raise ValueError(f"Invalid shape {c.shape}. The input `table` must be " + "of shape (2, 2).") + + if not np.issubdtype(c.dtype, np.integer): + raise ValueError("`table` must be an array of integers, but got " + f"type {c.dtype}") + c = c.astype(np.int64) + + if np.any(c < 0): + raise ValueError("All values in `table` must be nonnegative.") + + if 0 in c.sum(axis=0) or 0 in c.sum(axis=1): + # If both values in a row or column are zero, the p-value is NaN and + # the odds ratio is NaN. + result = OddsRatioResult(_table=c, _kind=kind, statistic=np.nan) + return result + + if kind == 'sample': + oddsratio = _sample_odds_ratio(c) + else: # kind is 'conditional' + oddsratio = _conditional_oddsratio(c) + + result = OddsRatioResult(_table=c, _kind=kind, statistic=oddsratio) + return result diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_page_trend_test.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_page_trend_test.py new file mode 100644 index 0000000000000000000000000000000000000000..87a4d0d17c07ce609cc575fc7dc61af75d2b9c51 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_page_trend_test.py @@ -0,0 +1,479 @@ +from itertools import permutations +import numpy as np +import math +from ._continuous_distns import norm +import scipy.stats +from dataclasses import dataclass + + +@dataclass +class PageTrendTestResult: + statistic: float + pvalue: float + method: str + + +def page_trend_test(data, ranked=False, predicted_ranks=None, method='auto'): + r""" + Perform Page's Test, a measure of trend in observations between treatments. + + Page's Test (also known as Page's :math:`L` test) is useful when: + + * there are :math:`n \geq 3` treatments, + * :math:`m \geq 2` subjects are observed for each treatment, and + * the observations are hypothesized to have a particular order. + + Specifically, the test considers the null hypothesis that + + .. math:: + + m_1 = m_2 = m_3 \cdots = m_n, + + where :math:`m_j` is the mean of the observed quantity under treatment + :math:`j`, against the alternative hypothesis that + + .. math:: + + m_1 \leq m_2 \leq m_3 \leq \cdots \leq m_n, + + where at least one inequality is strict. + + As noted by [4]_, Page's :math:`L` test has greater statistical power than + the Friedman test against the alternative that there is a difference in + trend, as Friedman's test only considers a difference in the means of the + observations without considering their order. Whereas Spearman :math:`\rho` + considers the correlation between the ranked observations of two variables + (e.g. the airspeed velocity of a swallow vs. the weight of the coconut it + carries), Page's :math:`L` is concerned with a trend in an observation + (e.g. the airspeed velocity of a swallow) across several distinct + treatments (e.g. carrying each of five coconuts of different weight) even + as the observation is repeated with multiple subjects (e.g. one European + swallow and one African swallow). + + Parameters + ---------- + data : array-like + A :math:`m \times n` array; the element in row :math:`i` and + column :math:`j` is the observation corresponding with subject + :math:`i` and treatment :math:`j`. By default, the columns are + assumed to be arranged in order of increasing predicted mean. + + ranked : boolean, optional + By default, `data` is assumed to be observations rather than ranks; + it will be ranked with `scipy.stats.rankdata` along ``axis=1``. If + `data` is provided in the form of ranks, pass argument ``True``. + + predicted_ranks : array-like, optional + The predicted ranks of the column means. If not specified, + the columns are assumed to be arranged in order of increasing + predicted mean, so the default `predicted_ranks` are + :math:`[1, 2, \dots, n-1, n]`. + + method : {'auto', 'asymptotic', 'exact'}, optional + Selects the method used to calculate the *p*-value. The following + options are available. + + * 'auto': selects between 'exact' and 'asymptotic' to + achieve reasonably accurate results in reasonable time (default) + * 'asymptotic': compares the standardized test statistic against + the normal distribution + * 'exact': computes the exact *p*-value by comparing the observed + :math:`L` statistic against those realized by all possible + permutations of ranks (under the null hypothesis that each + permutation is equally likely) + + Returns + ------- + res : PageTrendTestResult + An object containing attributes: + + statistic : float + Page's :math:`L` test statistic. + pvalue : float + The associated *p*-value + method : {'asymptotic', 'exact'} + The method used to compute the *p*-value + + See Also + -------- + rankdata, friedmanchisquare, spearmanr + + Notes + ----- + As noted in [1]_, "the :math:`n` 'treatments' could just as well represent + :math:`n` objects or events or performances or persons or trials ranked." + Similarly, the :math:`m` 'subjects' could equally stand for :math:`m` + "groupings by ability or some other control variable, or judges doing + the ranking, or random replications of some other sort." + + The procedure for calculating the :math:`L` statistic, adapted from + [1]_, is: + + 1. "Predetermine with careful logic the appropriate hypotheses + concerning the predicted ordering of the experimental results. + If no reasonable basis for ordering any treatments is known, the + :math:`L` test is not appropriate." + 2. "As in other experiments, determine at what level of confidence + you will reject the null hypothesis that there is no agreement of + experimental results with the monotonic hypothesis." + 3. "Cast the experimental material into a two-way table of :math:`n` + columns (treatments, objects ranked, conditions) and :math:`m` + rows (subjects, replication groups, levels of control variables)." + 4. "When experimental observations are recorded, rank them across each + row", e.g. ``ranks = scipy.stats.rankdata(data, axis=1)``. + 5. "Add the ranks in each column", e.g. + ``colsums = np.sum(ranks, axis=0)``. + 6. "Multiply each sum of ranks by the predicted rank for that same + column", e.g. ``products = predicted_ranks * colsums``. + 7. "Sum all such products", e.g. ``L = products.sum()``. + + [1]_ continues by suggesting use of the standardized statistic + + .. math:: + + \chi_L^2 = \frac{\left[12L-3mn(n+1)^2\right]^2}{mn^2(n^2-1)(n+1)} + + "which is distributed approximately as chi-square with 1 degree of + freedom. The ordinary use of :math:`\chi^2` tables would be + equivalent to a two-sided test of agreement. If a one-sided test + is desired, *as will almost always be the case*, the probability + discovered in the chi-square table should be *halved*." + + However, this standardized statistic does not distinguish between the + observed values being well correlated with the predicted ranks and being + _anti_-correlated with the predicted ranks. Instead, we follow [2]_ + and calculate the standardized statistic + + .. math:: + + \Lambda = \frac{L - E_0}{\sqrt{V_0}}, + + where :math:`E_0 = \frac{1}{4} mn(n+1)^2` and + :math:`V_0 = \frac{1}{144} mn^2(n+1)(n^2-1)`, "which is asymptotically + normal under the null hypothesis". + + The *p*-value for ``method='exact'`` is generated by comparing the observed + value of :math:`L` against the :math:`L` values generated for all + :math:`(n!)^m` possible permutations of ranks. The calculation is performed + using the recursive method of [5]. + + The *p*-values are not adjusted for the possibility of ties. When + ties are present, the reported ``'exact'`` *p*-values may be somewhat + larger (i.e. more conservative) than the true *p*-value [2]_. The + ``'asymptotic'``` *p*-values, however, tend to be smaller (i.e. less + conservative) than the ``'exact'`` *p*-values. + + References + ---------- + .. [1] Ellis Batten Page, "Ordered hypotheses for multiple treatments: + a significant test for linear ranks", *Journal of the American + Statistical Association* 58(301), p. 216--230, 1963. + + .. [2] Markus Neuhauser, *Nonparametric Statistical Test: A computational + approach*, CRC Press, p. 150--152, 2012. + + .. [3] Statext LLC, "Page's L Trend Test - Easy Statistics", *Statext - + Statistics Study*, https://www.statext.com/practice/PageTrendTest03.php, + Accessed July 12, 2020. + + .. [4] "Page's Trend Test", *Wikipedia*, WikimediaFoundation, + https://en.wikipedia.org/wiki/Page%27s_trend_test, + Accessed July 12, 2020. + + .. [5] Robert E. Odeh, "The exact distribution of Page's L-statistic in + the two-way layout", *Communications in Statistics - Simulation and + Computation*, 6(1), p. 49--61, 1977. + + Examples + -------- + We use the example from [3]_: 10 students are asked to rate three + teaching methods - tutorial, lecture, and seminar - on a scale of 1-5, + with 1 being the lowest and 5 being the highest. We have decided that + a confidence level of 99% is required to reject the null hypothesis in + favor of our alternative: that the seminar will have the highest ratings + and the tutorial will have the lowest. Initially, the data have been + tabulated with each row representing an individual student's ratings of + the three methods in the following order: tutorial, lecture, seminar. + + >>> table = [[3, 4, 3], + ... [2, 2, 4], + ... [3, 3, 5], + ... [1, 3, 2], + ... [2, 3, 2], + ... [2, 4, 5], + ... [1, 2, 4], + ... [3, 4, 4], + ... [2, 4, 5], + ... [1, 3, 4]] + + Because the tutorial is hypothesized to have the lowest ratings, the + column corresponding with tutorial rankings should be first; the seminar + is hypothesized to have the highest ratings, so its column should be last. + Since the columns are already arranged in this order of increasing + predicted mean, we can pass the table directly into `page_trend_test`. + + >>> from scipy.stats import page_trend_test + >>> res = page_trend_test(table) + >>> res + PageTrendTestResult(statistic=133.5, pvalue=0.0018191161948127822, + method='exact') + + This *p*-value indicates that there is a 0.1819% chance that + the :math:`L` statistic would reach such an extreme value under the null + hypothesis. Because 0.1819% is less than 1%, we have evidence to reject + the null hypothesis in favor of our alternative at a 99% confidence level. + + The value of the :math:`L` statistic is 133.5. To check this manually, + we rank the data such that high scores correspond with high ranks, settling + ties with an average rank: + + >>> from scipy.stats import rankdata + >>> ranks = rankdata(table, axis=1) + >>> ranks + array([[1.5, 3. , 1.5], + [1.5, 1.5, 3. ], + [1.5, 1.5, 3. ], + [1. , 3. , 2. ], + [1.5, 3. , 1.5], + [1. , 2. , 3. ], + [1. , 2. , 3. ], + [1. , 2.5, 2.5], + [1. , 2. , 3. ], + [1. , 2. , 3. ]]) + + We add the ranks within each column, multiply the sums by the + predicted ranks, and sum the products. + + >>> import numpy as np + >>> m, n = ranks.shape + >>> predicted_ranks = np.arange(1, n+1) + >>> L = (predicted_ranks * np.sum(ranks, axis=0)).sum() + >>> res.statistic == L + True + + As presented in [3]_, the asymptotic approximation of the *p*-value is the + survival function of the normal distribution evaluated at the standardized + test statistic: + + >>> from scipy.stats import norm + >>> E0 = (m*n*(n+1)**2)/4 + >>> V0 = (m*n**2*(n+1)*(n**2-1))/144 + >>> Lambda = (L-E0)/np.sqrt(V0) + >>> p = norm.sf(Lambda) + >>> p + 0.0012693433690751756 + + This does not precisely match the *p*-value reported by `page_trend_test` + above. The asymptotic distribution is not very accurate, nor conservative, + for :math:`m \leq 12` and :math:`n \leq 8`, so `page_trend_test` chose to + use ``method='exact'`` based on the dimensions of the table and the + recommendations in Page's original paper [1]_. To override + `page_trend_test`'s choice, provide the `method` argument. + + >>> res = page_trend_test(table, method="asymptotic") + >>> res + PageTrendTestResult(statistic=133.5, pvalue=0.0012693433690751756, + method='asymptotic') + + If the data are already ranked, we can pass in the ``ranks`` instead of + the ``table`` to save computation time. + + >>> res = page_trend_test(ranks, # ranks of data + ... ranked=True, # data is already ranked + ... ) + >>> res + PageTrendTestResult(statistic=133.5, pvalue=0.0018191161948127822, + method='exact') + + Suppose the raw data had been tabulated in an order different from the + order of predicted means, say lecture, seminar, tutorial. + + >>> table = np.asarray(table)[:, [1, 2, 0]] + + Since the arrangement of this table is not consistent with the assumed + ordering, we can either rearrange the table or provide the + `predicted_ranks`. Remembering that the lecture is predicted + to have the middle rank, the seminar the highest, and tutorial the lowest, + we pass: + + >>> res = page_trend_test(table, # data as originally tabulated + ... predicted_ranks=[2, 3, 1], # our predicted order + ... ) + >>> res + PageTrendTestResult(statistic=133.5, pvalue=0.0018191161948127822, + method='exact') + + """ + + # Possible values of the method parameter and the corresponding function + # used to evaluate the p value + methods = {"asymptotic": _l_p_asymptotic, + "exact": _l_p_exact, + "auto": None} + if method not in methods: + raise ValueError(f"`method` must be in {set(methods)}") + + ranks = np.asarray(data) + if ranks.ndim != 2: # TODO: relax this to accept 3d arrays? + raise ValueError("`data` must be a 2d array.") + + m, n = ranks.shape + if m < 2 or n < 3: + raise ValueError("Page's L is only appropriate for data with two " + "or more rows and three or more columns.") + + if np.any(np.isnan(data)): + raise ValueError("`data` contains NaNs, which cannot be ranked " + "meaningfully") + + # ensure NumPy array and rank the data if it's not already ranked + if ranked: + # Only a basic check on whether data is ranked. Checking that the data + # is properly ranked could take as much time as ranking it. + if not (ranks.min() >= 1 and ranks.max() <= ranks.shape[1]): + raise ValueError("`data` is not properly ranked. Rank the data or " + "pass `ranked=False`.") + else: + ranks = scipy.stats.rankdata(data, axis=-1) + + # generate predicted ranks if not provided, ensure valid NumPy array + if predicted_ranks is None: + predicted_ranks = np.arange(1, n+1) + else: + predicted_ranks = np.asarray(predicted_ranks) + if (predicted_ranks.ndim < 1 or + (set(predicted_ranks) != set(range(1, n+1)) or + len(predicted_ranks) != n)): + raise ValueError(f"`predicted_ranks` must include each integer " + f"from 1 to {n} (the number of columns in " + f"`data`) exactly once.") + + if not isinstance(ranked, bool): + raise TypeError("`ranked` must be boolean.") + + # Calculate the L statistic + L = _l_vectorized(ranks, predicted_ranks) + + # Calculate the p-value + if method == "auto": + method = _choose_method(ranks) + p_fun = methods[method] # get the function corresponding with the method + p = p_fun(L, m, n) + + page_result = PageTrendTestResult(statistic=L, pvalue=p, method=method) + return page_result + + +def _choose_method(ranks): + '''Choose method for computing p-value automatically''' + m, n = ranks.shape + if n > 8 or (m > 12 and n > 3) or m > 20: # as in [1], [4] + method = "asymptotic" + else: + method = "exact" + return method + + +def _l_vectorized(ranks, predicted_ranks): + '''Calculate's Page's L statistic for each page of a 3d array''' + colsums = ranks.sum(axis=-2, keepdims=True) + products = predicted_ranks * colsums + Ls = products.sum(axis=-1) + Ls = Ls[0] if Ls.size == 1 else Ls.ravel() + return Ls + + +def _l_p_asymptotic(L, m, n): + '''Calculate the p-value of Page's L from the asymptotic distribution''' + # Using [1] as a reference, the asymptotic p-value would be calculated as: + # chi_L = (12*L - 3*m*n*(n+1)**2)**2/(m*n**2*(n**2-1)*(n+1)) + # p = chi2.sf(chi_L, df=1, loc=0, scale=1)/2 + # but this is insensitive to the direction of the hypothesized ranking + + # See [2] page 151 + E0 = (m*n*(n+1)**2)/4 + V0 = (m*n**2*(n+1)*(n**2-1))/144 + Lambda = (L-E0)/np.sqrt(V0) + # This is a one-sided "greater" test - calculate the probability that the + # L statistic under H0 would be greater than the observed L statistic + p = norm.sf(Lambda) + return p + + +def _l_p_exact(L, m, n): + '''Calculate the p-value of Page's L exactly''' + # [1] uses m, n; [5] uses n, k. + # Switch convention here because exact calculation code references [5]. + L, n, k = int(L), int(m), int(n) + _pagel_state.set_k(k) + return _pagel_state.sf(L, n) + + +class _PageL: + '''Maintains state between `page_trend_test` executions''' + + def __init__(self): + '''Lightweight initialization''' + self.all_pmfs = {} + + def set_k(self, k): + '''Calculate lower and upper limits of L for single row''' + self.k = k + # See [5] top of page 52 + self.a, self.b = (k*(k+1)*(k+2))//6, (k*(k+1)*(2*k+1))//6 + + def sf(self, l, n): + '''Survival function of Page's L statistic''' + ps = [self.pmf(l, n) for l in range(l, n*self.b + 1)] + return np.sum(ps) + + def p_l_k_1(self): + '''Relative frequency of each L value over all possible single rows''' + + # See [5] Equation (6) + ranks = range(1, self.k+1) + # generate all possible rows of length k + rank_perms = np.array(list(permutations(ranks))) + # compute Page's L for all possible rows + Ls = (ranks*rank_perms).sum(axis=1) + # count occurrences of each L value + counts = np.histogram(Ls, np.arange(self.a-0.5, self.b+1.5))[0] + # factorial(k) is number of possible permutations + return counts/math.factorial(self.k) + + def pmf(self, l, n): + '''Recursive function to evaluate p(l, k, n); see [5] Equation 1''' + + if n not in self.all_pmfs: + self.all_pmfs[n] = {} + if self.k not in self.all_pmfs[n]: + self.all_pmfs[n][self.k] = {} + + # Cache results to avoid repeating calculation. Initially this was + # written with lru_cache, but this seems faster? Also, we could add + # an option to save this for future lookup. + if l in self.all_pmfs[n][self.k]: + return self.all_pmfs[n][self.k][l] + + if n == 1: + ps = self.p_l_k_1() # [5] Equation 6 + ls = range(self.a, self.b+1) + # not fast, but we'll only be here once + self.all_pmfs[n][self.k] = {l: p for l, p in zip(ls, ps)} + return self.all_pmfs[n][self.k][l] + + p = 0 + low = max(l-(n-1)*self.b, self.a) # [5] Equation 2 + high = min(l-(n-1)*self.a, self.b) + + # [5] Equation 1 + for t in range(low, high+1): + p1 = self.pmf(l-t, n-1) + p2 = self.pmf(t, 1) + p += p1*p2 + self.all_pmfs[n][self.k][l] = p + return p + + +# Maintain state for faster repeat calls to page_trend_test w/ method='exact' +_pagel_state = _PageL() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc.py new file mode 100644 index 0000000000000000000000000000000000000000..dfdd3fc233fc2c078a31a48327c042e0b0c36ae5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc.py @@ -0,0 +1,2786 @@ +"""Quasi-Monte Carlo engines and helpers.""" +from __future__ import annotations + +import copy +import math +import numbers +import os +import warnings +from abc import ABC, abstractmethod +from functools import partial +from typing import ( + Callable, + ClassVar, + Literal, + overload, + TYPE_CHECKING, +) + +import numpy as np + +if TYPE_CHECKING: + import numpy.typing as npt + from scipy._lib._util import ( + DecimalNumber, GeneratorType, IntNumber, SeedType + ) + +import scipy.stats as stats +from scipy._lib._util import rng_integers, _rng_spawn +from scipy.sparse.csgraph import minimum_spanning_tree +from scipy.spatial import distance, Voronoi +from scipy.special import gammainc +from ._sobol import ( + _initialize_v, _cscramble, _fill_p_cumulative, _draw, _fast_forward, + _categorize, _MAXDIM +) +from ._qmc_cy import ( + _cy_wrapper_centered_discrepancy, + _cy_wrapper_wrap_around_discrepancy, + _cy_wrapper_mixture_discrepancy, + _cy_wrapper_l2_star_discrepancy, + _cy_wrapper_update_discrepancy, + _cy_van_der_corput_scrambled, + _cy_van_der_corput, +) + + +__all__ = ['scale', 'discrepancy', 'geometric_discrepancy', 'update_discrepancy', + 'QMCEngine', 'Sobol', 'Halton', 'LatinHypercube', 'PoissonDisk', + 'MultinomialQMC', 'MultivariateNormalQMC'] + + +@overload +def check_random_state(seed: IntNumber | None = ...) -> np.random.Generator: + ... + + +@overload +def check_random_state(seed: GeneratorType) -> GeneratorType: + ... + + +# Based on scipy._lib._util.check_random_state +def check_random_state(seed=None): + """Turn `seed` into a `numpy.random.Generator` instance. + + Parameters + ---------- + seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` or ``RandomState`` instance, then + the provided instance is used. + + Returns + ------- + seed : {`numpy.random.Generator`, `numpy.random.RandomState`} + Random number generator. + + """ + if seed is None or isinstance(seed, (numbers.Integral, np.integer)): + return np.random.default_rng(seed) + elif isinstance(seed, (np.random.RandomState, np.random.Generator)): + return seed + else: + raise ValueError(f'{seed!r} cannot be used to seed a' + ' numpy.random.Generator instance') + + +def scale( + sample: npt.ArrayLike, + l_bounds: npt.ArrayLike, + u_bounds: npt.ArrayLike, + *, + reverse: bool = False +) -> np.ndarray: + r"""Sample scaling from unit hypercube to different bounds. + + To convert a sample from :math:`[0, 1)` to :math:`[a, b), b>a`, + with :math:`a` the lower bounds and :math:`b` the upper bounds. + The following transformation is used: + + .. math:: + + (b - a) \cdot \text{sample} + a + + Parameters + ---------- + sample : array_like (n, d) + Sample to scale. + l_bounds, u_bounds : array_like (d,) + Lower and upper bounds (resp. :math:`a`, :math:`b`) of transformed + data. If `reverse` is True, range of the original data to transform + to the unit hypercube. + reverse : bool, optional + Reverse the transformation from different bounds to the unit hypercube. + Default is False. + + Returns + ------- + sample : array_like (n, d) + Scaled sample. + + Examples + -------- + Transform 3 samples in the unit hypercube to bounds: + + >>> from scipy.stats import qmc + >>> l_bounds = [-2, 0] + >>> u_bounds = [6, 5] + >>> sample = [[0.5 , 0.75], + ... [0.5 , 0.5], + ... [0.75, 0.25]] + >>> sample_scaled = qmc.scale(sample, l_bounds, u_bounds) + >>> sample_scaled + array([[2. , 3.75], + [2. , 2.5 ], + [4. , 1.25]]) + + And convert back to the unit hypercube: + + >>> sample_ = qmc.scale(sample_scaled, l_bounds, u_bounds, reverse=True) + >>> sample_ + array([[0.5 , 0.75], + [0.5 , 0.5 ], + [0.75, 0.25]]) + + """ + sample = np.asarray(sample) + + # Checking bounds and sample + if not sample.ndim == 2: + raise ValueError('Sample is not a 2D array') + + lower, upper = _validate_bounds( + l_bounds=l_bounds, u_bounds=u_bounds, d=sample.shape[1] + ) + + if not reverse: + # Checking that sample is within the hypercube + if (sample.max() > 1.) or (sample.min() < 0.): + raise ValueError('Sample is not in unit hypercube') + + return sample * (upper - lower) + lower + else: + # Checking that sample is within the bounds + if not (np.all(sample >= lower) and np.all(sample <= upper)): + raise ValueError('Sample is out of bounds') + + return (sample - lower) / (upper - lower) + + +def _ensure_in_unit_hypercube(sample: npt.ArrayLike) -> np.ndarray: + """Ensure that sample is a 2D array and is within a unit hypercube + + Parameters + ---------- + sample : array_like (n, d) + A 2D array of points. + + Returns + ------- + np.ndarray + The array interpretation of the input sample + + Raises + ------ + ValueError + If the input is not a 2D array or contains points outside of + a unit hypercube. + """ + sample = np.asarray(sample, dtype=np.float64, order="C") + + if not sample.ndim == 2: + raise ValueError("Sample is not a 2D array") + + if (sample.max() > 1.) or (sample.min() < 0.): + raise ValueError("Sample is not in unit hypercube") + + return sample + + +def discrepancy( + sample: npt.ArrayLike, + *, + iterative: bool = False, + method: Literal["CD", "WD", "MD", "L2-star"] = "CD", + workers: IntNumber = 1) -> float: + """Discrepancy of a given sample. + + Parameters + ---------- + sample : array_like (n, d) + The sample to compute the discrepancy from. + iterative : bool, optional + Must be False if not using it for updating the discrepancy. + Default is False. Refer to the notes for more details. + method : str, optional + Type of discrepancy, can be ``CD``, ``WD``, ``MD`` or ``L2-star``. + Refer to the notes for more details. Default is ``CD``. + workers : int, optional + Number of workers to use for parallel processing. If -1 is given all + CPU threads are used. Default is 1. + + Returns + ------- + discrepancy : float + Discrepancy. + + See Also + -------- + geometric_discrepancy + + Notes + ----- + The discrepancy is a uniformity criterion used to assess the space filling + of a number of samples in a hypercube. A discrepancy quantifies the + distance between the continuous uniform distribution on a hypercube and the + discrete uniform distribution on :math:`n` distinct sample points. + + The lower the value is, the better the coverage of the parameter space is. + + For a collection of subsets of the hypercube, the discrepancy is the + difference between the fraction of sample points in one of those + subsets and the volume of that subset. There are different definitions of + discrepancy corresponding to different collections of subsets. Some + versions take a root mean square difference over subsets instead of + a maximum. + + A measure of uniformity is reasonable if it satisfies the following + criteria [1]_: + + 1. It is invariant under permuting factors and/or runs. + 2. It is invariant under rotation of the coordinates. + 3. It can measure not only uniformity of the sample over the hypercube, + but also the projection uniformity of the sample over non-empty + subset of lower dimension hypercubes. + 4. There is some reasonable geometric meaning. + 5. It is easy to compute. + 6. It satisfies the Koksma-Hlawka-like inequality. + 7. It is consistent with other criteria in experimental design. + + Four methods are available: + + * ``CD``: Centered Discrepancy - subspace involves a corner of the + hypercube + * ``WD``: Wrap-around Discrepancy - subspace can wrap around bounds + * ``MD``: Mixture Discrepancy - mix between CD/WD covering more criteria + * ``L2-star``: L2-star discrepancy - like CD BUT variant to rotation + + See [2]_ for precise definitions of each method. + + Lastly, using ``iterative=True``, it is possible to compute the + discrepancy as if we had :math:`n+1` samples. This is useful if we want + to add a point to a sampling and check the candidate which would give the + lowest discrepancy. Then you could just update the discrepancy with + each candidate using `update_discrepancy`. This method is faster than + computing the discrepancy for a large number of candidates. + + References + ---------- + .. [1] Fang et al. "Design and modeling for computer experiments". + Computer Science and Data Analysis Series, 2006. + .. [2] Zhou Y.-D. et al. "Mixture discrepancy for quasi-random point sets." + Journal of Complexity, 29 (3-4) , pp. 283-301, 2013. + .. [3] T. T. Warnock. "Computational investigations of low discrepancy + point sets." Applications of Number Theory to Numerical + Analysis, Academic Press, pp. 319-343, 1972. + + Examples + -------- + Calculate the quality of the sample using the discrepancy: + + >>> import numpy as np + >>> from scipy.stats import qmc + >>> space = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + >>> l_bounds = [0.5, 0.5] + >>> u_bounds = [6.5, 6.5] + >>> space = qmc.scale(space, l_bounds, u_bounds, reverse=True) + >>> space + array([[0.08333333, 0.41666667], + [0.25 , 0.91666667], + [0.41666667, 0.25 ], + [0.58333333, 0.75 ], + [0.75 , 0.08333333], + [0.91666667, 0.58333333]]) + >>> qmc.discrepancy(space) + 0.008142039609053464 + + We can also compute iteratively the ``CD`` discrepancy by using + ``iterative=True``. + + >>> disc_init = qmc.discrepancy(space[:-1], iterative=True) + >>> disc_init + 0.04769081147119336 + >>> qmc.update_discrepancy(space[-1], space[:-1], disc_init) + 0.008142039609053513 + + """ + sample = _ensure_in_unit_hypercube(sample) + + workers = _validate_workers(workers) + + methods = { + "CD": _cy_wrapper_centered_discrepancy, + "WD": _cy_wrapper_wrap_around_discrepancy, + "MD": _cy_wrapper_mixture_discrepancy, + "L2-star": _cy_wrapper_l2_star_discrepancy, + } + + if method in methods: + return methods[method](sample, iterative, workers=workers) + else: + raise ValueError(f"{method!r} is not a valid method. It must be one of" + f" {set(methods)!r}") + + +def geometric_discrepancy( + sample: npt.ArrayLike, + method: Literal["mindist", "mst"] = "mindist", + metric: str = "euclidean") -> float: + """Discrepancy of a given sample based on its geometric properties. + + Parameters + ---------- + sample : array_like (n, d) + The sample to compute the discrepancy from. + method : {"mindist", "mst"}, optional + The method to use. One of ``mindist`` for minimum distance (default) + or ``mst`` for minimum spanning tree. + metric : str or callable, optional + The distance metric to use. See the documentation + for `scipy.spatial.distance.pdist` for the available metrics and + the default. + + Returns + ------- + discrepancy : float + Discrepancy (higher values correspond to greater sample uniformity). + + See Also + -------- + discrepancy + + Notes + ----- + The discrepancy can serve as a simple measure of quality of a random sample. + This measure is based on the geometric properties of the distribution of points + in the sample, such as the minimum distance between any pair of points, or + the mean edge length in a minimum spanning tree. + + The higher the value is, the better the coverage of the parameter space is. + Note that this is different from `scipy.stats.qmc.discrepancy`, where lower + values correspond to higher quality of the sample. + + Also note that when comparing different sampling strategies using this function, + the sample size must be kept constant. + + It is possible to calculate two metrics from the minimum spanning tree: + the mean edge length and the standard deviation of edges lengths. Using + both metrics offers a better picture of uniformity than either metric alone, + with higher mean and lower standard deviation being preferable (see [1]_ + for a brief discussion). This function currently only calculates the mean + edge length. + + References + ---------- + .. [1] Franco J. et al. "Minimum Spanning Tree: A new approach to assess the quality + of the design of computer experiments." Chemometrics and Intelligent Laboratory + Systems, 97 (2), pp. 164-169, 2009. + + Examples + -------- + Calculate the quality of the sample using the minimum euclidean distance + (the defaults): + + >>> import numpy as np + >>> from scipy.stats import qmc + >>> rng = np.random.default_rng(191468432622931918890291693003068437394) + >>> sample = qmc.LatinHypercube(d=2, seed=rng).random(50) + >>> qmc.geometric_discrepancy(sample) + 0.03708161435687876 + + Calculate the quality using the mean edge length in the minimum + spanning tree: + + >>> qmc.geometric_discrepancy(sample, method='mst') + 0.1105149978798376 + + Display the minimum spanning tree and the points with + the smallest distance: + + >>> import matplotlib.pyplot as plt + >>> from matplotlib.lines import Line2D + >>> from scipy.sparse.csgraph import minimum_spanning_tree + >>> from scipy.spatial.distance import pdist, squareform + >>> dist = pdist(sample) + >>> mst = minimum_spanning_tree(squareform(dist)) + >>> edges = np.where(mst.toarray() > 0) + >>> edges = np.asarray(edges).T + >>> min_dist = np.min(dist) + >>> min_idx = np.argwhere(squareform(dist) == min_dist)[0] + >>> fig, ax = plt.subplots(figsize=(10, 5)) + >>> _ = ax.set(aspect='equal', xlabel=r'$x_1$', ylabel=r'$x_2$', + ... xlim=[0, 1], ylim=[0, 1]) + >>> for edge in edges: + ... ax.plot(sample[edge, 0], sample[edge, 1], c='k') + >>> ax.scatter(sample[:, 0], sample[:, 1]) + >>> ax.add_patch(plt.Circle(sample[min_idx[0]], min_dist, color='red', fill=False)) + >>> markers = [ + ... Line2D([0], [0], marker='o', lw=0, label='Sample points'), + ... Line2D([0], [0], color='k', label='Minimum spanning tree'), + ... Line2D([0], [0], marker='o', lw=0, markerfacecolor='w', markeredgecolor='r', + ... label='Minimum point-to-point distance'), + ... ] + >>> ax.legend(handles=markers, loc='center left', bbox_to_anchor=(1, 0.5)); + >>> plt.show() + + """ + sample = _ensure_in_unit_hypercube(sample) + if sample.shape[0] < 2: + raise ValueError("Sample must contain at least two points") + + distances = distance.pdist(sample, metric=metric) # type: ignore[call-overload] + + if np.any(distances == 0.0): + warnings.warn("Sample contains duplicate points.", stacklevel=2) + + if method == "mindist": + return np.min(distances[distances.nonzero()]) + elif method == "mst": + fully_connected_graph = distance.squareform(distances) + mst = minimum_spanning_tree(fully_connected_graph) + distances = mst[mst.nonzero()] + # TODO consider returning both the mean and the standard deviation + # see [1] for a discussion + return np.mean(distances) + else: + raise ValueError(f"{method!r} is not a valid method. " + f"It must be one of {{'mindist', 'mst'}}") + + +def update_discrepancy( + x_new: npt.ArrayLike, + sample: npt.ArrayLike, + initial_disc: DecimalNumber) -> float: + """Update the centered discrepancy with a new sample. + + Parameters + ---------- + x_new : array_like (1, d) + The new sample to add in `sample`. + sample : array_like (n, d) + The initial sample. + initial_disc : float + Centered discrepancy of the `sample`. + + Returns + ------- + discrepancy : float + Centered discrepancy of the sample composed of `x_new` and `sample`. + + Examples + -------- + We can also compute iteratively the discrepancy by using + ``iterative=True``. + + >>> import numpy as np + >>> from scipy.stats import qmc + >>> space = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + >>> l_bounds = [0.5, 0.5] + >>> u_bounds = [6.5, 6.5] + >>> space = qmc.scale(space, l_bounds, u_bounds, reverse=True) + >>> disc_init = qmc.discrepancy(space[:-1], iterative=True) + >>> disc_init + 0.04769081147119336 + >>> qmc.update_discrepancy(space[-1], space[:-1], disc_init) + 0.008142039609053513 + + """ + sample = np.asarray(sample, dtype=np.float64, order="C") + x_new = np.asarray(x_new, dtype=np.float64, order="C") + + # Checking that sample is within the hypercube and 2D + if not sample.ndim == 2: + raise ValueError('Sample is not a 2D array') + + if (sample.max() > 1.) or (sample.min() < 0.): + raise ValueError('Sample is not in unit hypercube') + + # Checking that x_new is within the hypercube and 1D + if not x_new.ndim == 1: + raise ValueError('x_new is not a 1D array') + + if not (np.all(x_new >= 0) and np.all(x_new <= 1)): + raise ValueError('x_new is not in unit hypercube') + + if x_new.shape[0] != sample.shape[1]: + raise ValueError("x_new and sample must be broadcastable") + + return _cy_wrapper_update_discrepancy(x_new, sample, initial_disc) + + +def _perturb_discrepancy(sample: np.ndarray, i1: int, i2: int, k: int, + disc: float): + """Centered discrepancy after an elementary perturbation of a LHS. + + An elementary perturbation consists of an exchange of coordinates between + two points: ``sample[i1, k] <-> sample[i2, k]``. By construction, + this operation conserves the LHS properties. + + Parameters + ---------- + sample : array_like (n, d) + The sample (before permutation) to compute the discrepancy from. + i1 : int + The first line of the elementary permutation. + i2 : int + The second line of the elementary permutation. + k : int + The column of the elementary permutation. + disc : float + Centered discrepancy of the design before permutation. + + Returns + ------- + discrepancy : float + Centered discrepancy of the design after permutation. + + References + ---------- + .. [1] Jin et al. "An efficient algorithm for constructing optimal design + of computer experiments", Journal of Statistical Planning and + Inference, 2005. + + """ + n = sample.shape[0] + + z_ij = sample - 0.5 + + # Eq (19) + c_i1j = (1. / n ** 2. + * np.prod(0.5 * (2. + abs(z_ij[i1, :]) + + abs(z_ij) - abs(z_ij[i1, :] - z_ij)), axis=1)) + c_i2j = (1. / n ** 2. + * np.prod(0.5 * (2. + abs(z_ij[i2, :]) + + abs(z_ij) - abs(z_ij[i2, :] - z_ij)), axis=1)) + + # Eq (20) + c_i1i1 = (1. / n ** 2 * np.prod(1 + abs(z_ij[i1, :])) + - 2. / n * np.prod(1. + 0.5 * abs(z_ij[i1, :]) + - 0.5 * z_ij[i1, :] ** 2)) + c_i2i2 = (1. / n ** 2 * np.prod(1 + abs(z_ij[i2, :])) + - 2. / n * np.prod(1. + 0.5 * abs(z_ij[i2, :]) + - 0.5 * z_ij[i2, :] ** 2)) + + # Eq (22), typo in the article in the denominator i2 -> i1 + num = (2 + abs(z_ij[i2, k]) + abs(z_ij[:, k]) + - abs(z_ij[i2, k] - z_ij[:, k])) + denum = (2 + abs(z_ij[i1, k]) + abs(z_ij[:, k]) + - abs(z_ij[i1, k] - z_ij[:, k])) + gamma = num / denum + + # Eq (23) + c_p_i1j = gamma * c_i1j + # Eq (24) + c_p_i2j = c_i2j / gamma + + alpha = (1 + abs(z_ij[i2, k])) / (1 + abs(z_ij[i1, k])) + beta = (2 - abs(z_ij[i2, k])) / (2 - abs(z_ij[i1, k])) + + g_i1 = np.prod(1. + abs(z_ij[i1, :])) + g_i2 = np.prod(1. + abs(z_ij[i2, :])) + h_i1 = np.prod(1. + 0.5 * abs(z_ij[i1, :]) - 0.5 * (z_ij[i1, :] ** 2)) + h_i2 = np.prod(1. + 0.5 * abs(z_ij[i2, :]) - 0.5 * (z_ij[i2, :] ** 2)) + + # Eq (25), typo in the article g is missing + c_p_i1i1 = ((g_i1 * alpha) / (n ** 2) - 2. * alpha * beta * h_i1 / n) + # Eq (26), typo in the article n ** 2 + c_p_i2i2 = ((g_i2 / ((n ** 2) * alpha)) - (2. * h_i2 / (n * alpha * beta))) + + # Eq (26) + sum_ = c_p_i1j - c_i1j + c_p_i2j - c_i2j + + mask = np.ones(n, dtype=bool) + mask[[i1, i2]] = False + sum_ = sum(sum_[mask]) + + disc_ep = (disc + c_p_i1i1 - c_i1i1 + c_p_i2i2 - c_i2i2 + 2 * sum_) + + return disc_ep + + +def primes_from_2_to(n: int) -> np.ndarray: + """Prime numbers from 2 to *n*. + + Parameters + ---------- + n : int + Sup bound with ``n >= 6``. + + Returns + ------- + primes : list(int) + Primes in ``2 <= p < n``. + + Notes + ----- + Taken from [1]_ by P.T. Roy, written consent given on 23.04.2021 + by the original author, Bruno Astrolino, for free use in SciPy under + the 3-clause BSD. + + References + ---------- + .. [1] `StackOverflow `_. + + """ + sieve = np.ones(n // 3 + (n % 6 == 2), dtype=bool) + for i in range(1, int(n ** 0.5) // 3 + 1): + k = 3 * i + 1 | 1 + sieve[k * k // 3::2 * k] = False + sieve[k * (k - 2 * (i & 1) + 4) // 3::2 * k] = False + return np.r_[2, 3, ((3 * np.nonzero(sieve)[0][1:] + 1) | 1)] + + +def n_primes(n: IntNumber) -> list[int]: + """List of the n-first prime numbers. + + Parameters + ---------- + n : int + Number of prime numbers wanted. + + Returns + ------- + primes : list(int) + List of primes. + + """ + primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, + 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, + 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, + 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997][:n] # type: ignore[misc] + + if len(primes) < n: + big_number = 2000 + while 'Not enough primes': + primes = primes_from_2_to(big_number)[:n] # type: ignore + if len(primes) == n: + break + big_number += 1000 + + return primes + + +def _van_der_corput_permutations( + base: IntNumber, *, random_state: SeedType = None +) -> np.ndarray: + """Permutations for scrambling a Van der Corput sequence. + + Parameters + ---------- + base : int + Base of the sequence. + random_state : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Returns + ------- + permutations : array_like + Permutation indices. + + Notes + ----- + In Algorithm 1 of Owen 2017, a permutation of `np.arange(base)` is + created for each positive integer `k` such that `1 - base**-k < 1` + using floating-point arithmetic. For double precision floats, the + condition `1 - base**-k < 1` can also be written as `base**-k > + 2**-54`, which makes it more apparent how many permutations we need + to create. + """ + rng = check_random_state(random_state) + count = math.ceil(54 / math.log2(base)) - 1 + permutations = np.repeat(np.arange(base)[None], count, axis=0) + for perm in permutations: + rng.shuffle(perm) + + return permutations + + +def van_der_corput( + n: IntNumber, + base: IntNumber = 2, + *, + start_index: IntNumber = 0, + scramble: bool = False, + permutations: npt.ArrayLike | None = None, + seed: SeedType = None, + workers: IntNumber = 1) -> np.ndarray: + """Van der Corput sequence. + + Pseudo-random number generator based on a b-adic expansion. + + Scrambling uses permutations of the remainders (see [1]_). Multiple + permutations are applied to construct a point. The sequence of + permutations has to be the same for all points of the sequence. + + Parameters + ---------- + n : int + Number of element of the sequence. + base : int, optional + Base of the sequence. Default is 2. + start_index : int, optional + Index to start the sequence from. Default is 0. + scramble : bool, optional + If True, use Owen scrambling. Otherwise no scrambling is done. + Default is True. + permutations : array_like, optional + Permutations used for scrambling. + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + workers : int, optional + Number of workers to use for parallel processing. If -1 is + given all CPU threads are used. Default is 1. + + Returns + ------- + sequence : list (n,) + Sequence of Van der Corput. + + References + ---------- + .. [1] A. B. Owen. "A randomized Halton algorithm in R", + :arxiv:`1706.02808`, 2017. + + """ + if base < 2: + raise ValueError("'base' must be at least 2") + + if scramble: + if permutations is None: + permutations = _van_der_corput_permutations( + base=base, random_state=seed + ) + else: + permutations = np.asarray(permutations) + + permutations = permutations.astype(np.int64) + return _cy_van_der_corput_scrambled(n, base, start_index, + permutations, workers) + + else: + return _cy_van_der_corput(n, base, start_index, workers) + + +class QMCEngine(ABC): + """A generic Quasi-Monte Carlo sampler class meant for subclassing. + + QMCEngine is a base class to construct a specific Quasi-Monte Carlo + sampler. It cannot be used directly as a sampler. + + Parameters + ---------- + d : int + Dimension of the parameter space. + optimization : {None, "random-cd", "lloyd"}, optional + Whether to use an optimization scheme to improve the quality after + sampling. Note that this is a post-processing step that does not + guarantee that all properties of the sample will be conserved. + Default is None. + + * ``random-cd``: random permutations of coordinates to lower the + centered discrepancy. The best sample based on the centered + discrepancy is constantly updated. Centered discrepancy-based + sampling shows better space-filling robustness toward 2D and 3D + subprojections compared to using other discrepancy measures. + * ``lloyd``: Perturb samples using a modified Lloyd-Max algorithm. + The process converges to equally spaced samples. + + .. versionadded:: 1.10.0 + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Notes + ----- + By convention samples are distributed over the half-open interval + ``[0, 1)``. Instances of the class can access the attributes: ``d`` for + the dimension; and ``rng`` for the random number generator (used for the + ``seed``). + + **Subclassing** + + When subclassing `QMCEngine` to create a new sampler, ``__init__`` and + ``random`` must be redefined. + + * ``__init__(d, seed=None)``: at least fix the dimension. If the sampler + does not take advantage of a ``seed`` (deterministic methods like + Halton), this parameter can be omitted. + * ``_random(n, *, workers=1)``: draw ``n`` from the engine. ``workers`` + is used for parallelism. See `Halton` for example. + + Optionally, two other methods can be overwritten by subclasses: + + * ``reset``: Reset the engine to its original state. + * ``fast_forward``: If the sequence is deterministic (like Halton + sequence), then ``fast_forward(n)`` is skipping the ``n`` first draw. + + Examples + -------- + To create a random sampler based on ``np.random.random``, we would do the + following: + + >>> from scipy.stats import qmc + >>> class RandomEngine(qmc.QMCEngine): + ... def __init__(self, d, seed=None): + ... super().__init__(d=d, seed=seed) + ... + ... + ... def _random(self, n=1, *, workers=1): + ... return self.rng.random((n, self.d)) + ... + ... + ... def reset(self): + ... super().__init__(d=self.d, seed=self.rng_seed) + ... return self + ... + ... + ... def fast_forward(self, n): + ... self.random(n) + ... return self + + After subclassing `QMCEngine` to define the sampling strategy we want to + use, we can create an instance to sample from. + + >>> engine = RandomEngine(2) + >>> engine.random(5) + array([[0.22733602, 0.31675834], # random + [0.79736546, 0.67625467], + [0.39110955, 0.33281393], + [0.59830875, 0.18673419], + [0.67275604, 0.94180287]]) + + We can also reset the state of the generator and resample again. + + >>> _ = engine.reset() + >>> engine.random(5) + array([[0.22733602, 0.31675834], # random + [0.79736546, 0.67625467], + [0.39110955, 0.33281393], + [0.59830875, 0.18673419], + [0.67275604, 0.94180287]]) + + """ + + @abstractmethod + def __init__( + self, + d: IntNumber, + *, + optimization: Literal["random-cd", "lloyd"] | None = None, + seed: SeedType = None + ) -> None: + if not np.issubdtype(type(d), np.integer) or d < 0: + raise ValueError('d must be a non-negative integer value') + + self.d = d + + if isinstance(seed, np.random.Generator): + # Spawn a Generator that we can own and reset. + self.rng = _rng_spawn(seed, 1)[0] + else: + # Create our instance of Generator, does not need spawning + # Also catch RandomState which cannot be spawned + self.rng = check_random_state(seed) + self.rng_seed = copy.deepcopy(self.rng) + + self.num_generated = 0 + + config = { + # random-cd + "n_nochange": 100, + "n_iters": 10_000, + "rng": self.rng, + + # lloyd + "tol": 1e-5, + "maxiter": 10, + "qhull_options": None, + } + self.optimization_method = _select_optimizer(optimization, config) + + @abstractmethod + def _random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + ... + + def random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + """Draw `n` in the half-open interval ``[0, 1)``. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. + Default is 1. + workers : int, optional + Only supported with `Halton`. + Number of workers to use for parallel processing. If -1 is + given all CPU threads are used. Default is 1. It becomes faster + than one worker for `n` greater than :math:`10^3`. + + Returns + ------- + sample : array_like (n, d) + QMC sample. + + """ + sample = self._random(n, workers=workers) + if self.optimization_method is not None: + sample = self.optimization_method(sample) + + self.num_generated += n + return sample + + def integers( + self, + l_bounds: npt.ArrayLike, + *, + u_bounds: npt.ArrayLike | None = None, + n: IntNumber = 1, + endpoint: bool = False, + workers: IntNumber = 1 + ) -> np.ndarray: + r""" + Draw `n` integers from `l_bounds` (inclusive) to `u_bounds` + (exclusive), or if endpoint=True, `l_bounds` (inclusive) to + `u_bounds` (inclusive). + + Parameters + ---------- + l_bounds : int or array-like of ints + Lowest (signed) integers to be drawn (unless ``u_bounds=None``, + in which case this parameter is 0 and this value is used for + `u_bounds`). + u_bounds : int or array-like of ints, optional + If provided, one above the largest (signed) integer to be drawn + (see above for behavior if ``u_bounds=None``). + If array-like, must contain integer values. + n : int, optional + Number of samples to generate in the parameter space. + Default is 1. + endpoint : bool, optional + If true, sample from the interval ``[l_bounds, u_bounds]`` instead + of the default ``[l_bounds, u_bounds)``. Defaults is False. + workers : int, optional + Number of workers to use for parallel processing. If -1 is + given all CPU threads are used. Only supported when using `Halton` + Default is 1. + + Returns + ------- + sample : array_like (n, d) + QMC sample. + + Notes + ----- + It is safe to just use the same ``[0, 1)`` to integer mapping + with QMC that you would use with MC. You still get unbiasedness, + a strong law of large numbers, an asymptotically infinite variance + reduction and a finite sample variance bound. + + To convert a sample from :math:`[0, 1)` to :math:`[a, b), b>a`, + with :math:`a` the lower bounds and :math:`b` the upper bounds, + the following transformation is used: + + .. math:: + + \text{floor}((b - a) \cdot \text{sample} + a) + + """ + if u_bounds is None: + u_bounds = l_bounds + l_bounds = 0 + + u_bounds = np.atleast_1d(u_bounds) + l_bounds = np.atleast_1d(l_bounds) + + if endpoint: + u_bounds = u_bounds + 1 + + if (not np.issubdtype(l_bounds.dtype, np.integer) or + not np.issubdtype(u_bounds.dtype, np.integer)): + message = ("'u_bounds' and 'l_bounds' must be integers or" + " array-like of integers") + raise ValueError(message) + + if isinstance(self, Halton): + sample = self.random(n=n, workers=workers) + else: + sample = self.random(n=n) + + sample = scale(sample, l_bounds=l_bounds, u_bounds=u_bounds) + sample = np.floor(sample).astype(np.int64) + + return sample + + def reset(self) -> QMCEngine: + """Reset the engine to base state. + + Returns + ------- + engine : QMCEngine + Engine reset to its base state. + + """ + seed = copy.deepcopy(self.rng_seed) + self.rng = check_random_state(seed) + self.num_generated = 0 + return self + + def fast_forward(self, n: IntNumber) -> QMCEngine: + """Fast-forward the sequence by `n` positions. + + Parameters + ---------- + n : int + Number of points to skip in the sequence. + + Returns + ------- + engine : QMCEngine + Engine reset to its base state. + + """ + self.random(n=n) + return self + + +class Halton(QMCEngine): + """Halton sequence. + + Pseudo-random number generator that generalize the Van der Corput sequence + for multiple dimensions. The Halton sequence uses the base-two Van der + Corput sequence for the first dimension, base-three for its second and + base-:math:`n` for its n-dimension. + + Parameters + ---------- + d : int + Dimension of the parameter space. + scramble : bool, optional + If True, use Owen scrambling. Otherwise no scrambling is done. + Default is True. + optimization : {None, "random-cd", "lloyd"}, optional + Whether to use an optimization scheme to improve the quality after + sampling. Note that this is a post-processing step that does not + guarantee that all properties of the sample will be conserved. + Default is None. + + * ``random-cd``: random permutations of coordinates to lower the + centered discrepancy. The best sample based on the centered + discrepancy is constantly updated. Centered discrepancy-based + sampling shows better space-filling robustness toward 2D and 3D + subprojections compared to using other discrepancy measures. + * ``lloyd``: Perturb samples using a modified Lloyd-Max algorithm. + The process converges to equally spaced samples. + + .. versionadded:: 1.10.0 + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Notes + ----- + The Halton sequence has severe striping artifacts for even modestly + large dimensions. These can be ameliorated by scrambling. Scrambling + also supports replication-based error estimates and extends + applicabiltiy to unbounded integrands. + + References + ---------- + .. [1] Halton, "On the efficiency of certain quasi-random sequences of + points in evaluating multi-dimensional integrals", Numerische + Mathematik, 1960. + .. [2] A. B. Owen. "A randomized Halton algorithm in R", + :arxiv:`1706.02808`, 2017. + + Examples + -------- + Generate samples from a low discrepancy sequence of Halton. + + >>> from scipy.stats import qmc + >>> sampler = qmc.Halton(d=2, scramble=False) + >>> sample = sampler.random(n=5) + >>> sample + array([[0. , 0. ], + [0.5 , 0.33333333], + [0.25 , 0.66666667], + [0.75 , 0.11111111], + [0.125 , 0.44444444]]) + + Compute the quality of the sample using the discrepancy criterion. + + >>> qmc.discrepancy(sample) + 0.088893711419753 + + If some wants to continue an existing design, extra points can be obtained + by calling again `random`. Alternatively, you can skip some points like: + + >>> _ = sampler.fast_forward(5) + >>> sample_continued = sampler.random(n=5) + >>> sample_continued + array([[0.3125 , 0.37037037], + [0.8125 , 0.7037037 ], + [0.1875 , 0.14814815], + [0.6875 , 0.48148148], + [0.4375 , 0.81481481]]) + + Finally, samples can be scaled to bounds. + + >>> l_bounds = [0, 2] + >>> u_bounds = [10, 5] + >>> qmc.scale(sample_continued, l_bounds, u_bounds) + array([[3.125 , 3.11111111], + [8.125 , 4.11111111], + [1.875 , 2.44444444], + [6.875 , 3.44444444], + [4.375 , 4.44444444]]) + + """ + + def __init__( + self, d: IntNumber, *, scramble: bool = True, + optimization: Literal["random-cd", "lloyd"] | None = None, + seed: SeedType = None + ) -> None: + # Used in `scipy.integrate.qmc_quad` + self._init_quad = {'d': d, 'scramble': True, + 'optimization': optimization} + super().__init__(d=d, optimization=optimization, seed=seed) + self.seed = seed + + # important to have ``type(bdim) == int`` for performance reason + self.base = [int(bdim) for bdim in n_primes(d)] + self.scramble = scramble + + self._initialize_permutations() + + def _initialize_permutations(self) -> None: + """Initialize permutations for all Van der Corput sequences. + + Permutations are only needed for scrambling. + """ + self._permutations: list = [None] * len(self.base) + if self.scramble: + for i, bdim in enumerate(self.base): + permutations = _van_der_corput_permutations( + base=bdim, random_state=self.rng + ) + + self._permutations[i] = permutations + + def _random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + """Draw `n` in the half-open interval ``[0, 1)``. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + workers : int, optional + Number of workers to use for parallel processing. If -1 is + given all CPU threads are used. Default is 1. It becomes faster + than one worker for `n` greater than :math:`10^3`. + + Returns + ------- + sample : array_like (n, d) + QMC sample. + + """ + workers = _validate_workers(workers) + # Generate a sample using a Van der Corput sequence per dimension. + sample = [van_der_corput(n, bdim, start_index=self.num_generated, + scramble=self.scramble, + permutations=self._permutations[i], + workers=workers) + for i, bdim in enumerate(self.base)] + + return np.array(sample).T.reshape(n, self.d) + + +class LatinHypercube(QMCEngine): + r"""Latin hypercube sampling (LHS). + + A Latin hypercube sample [1]_ generates :math:`n` points in + :math:`[0,1)^{d}`. Each univariate marginal distribution is stratified, + placing exactly one point in :math:`[j/n, (j+1)/n)` for + :math:`j=0,1,...,n-1`. They are still applicable when :math:`n << d`. + + Parameters + ---------- + d : int + Dimension of the parameter space. + scramble : bool, optional + When False, center samples within cells of a multi-dimensional grid. + Otherwise, samples are randomly placed within cells of the grid. + + .. note:: + Setting ``scramble=False`` does not ensure deterministic output. + For that, use the `seed` parameter. + + Default is True. + + .. versionadded:: 1.10.0 + + optimization : {None, "random-cd", "lloyd"}, optional + Whether to use an optimization scheme to improve the quality after + sampling. Note that this is a post-processing step that does not + guarantee that all properties of the sample will be conserved. + Default is None. + + * ``random-cd``: random permutations of coordinates to lower the + centered discrepancy. The best sample based on the centered + discrepancy is constantly updated. Centered discrepancy-based + sampling shows better space-filling robustness toward 2D and 3D + subprojections compared to using other discrepancy measures. + * ``lloyd``: Perturb samples using a modified Lloyd-Max algorithm. + The process converges to equally spaced samples. + + .. versionadded:: 1.8.0 + .. versionchanged:: 1.10.0 + Add ``lloyd``. + + strength : {1, 2}, optional + Strength of the LHS. ``strength=1`` produces a plain LHS while + ``strength=2`` produces an orthogonal array based LHS of strength 2 + [7]_, [8]_. In that case, only ``n=p**2`` points can be sampled, + with ``p`` a prime number. It also constrains ``d <= p + 1``. + Default is 1. + + .. versionadded:: 1.8.0 + + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Notes + ----- + + When LHS is used for integrating a function :math:`f` over :math:`n`, + LHS is extremely effective on integrands that are nearly additive [2]_. + With a LHS of :math:`n` points, the variance of the integral is always + lower than plain MC on :math:`n-1` points [3]_. There is a central limit + theorem for LHS on the mean and variance of the integral [4]_, but not + necessarily for optimized LHS due to the randomization. + + :math:`A` is called an orthogonal array of strength :math:`t` if in each + n-row-by-t-column submatrix of :math:`A`: all :math:`p^t` possible + distinct rows occur the same number of times. The elements of :math:`A` + are in the set :math:`\{0, 1, ..., p-1\}`, also called symbols. + The constraint that :math:`p` must be a prime number is to allow modular + arithmetic. Increasing strength adds some symmetry to the sub-projections + of a sample. With strength 2, samples are symmetric along the diagonals of + 2D sub-projections. This may be undesirable, but on the other hand, the + sample dispersion is improved. + + Strength 1 (plain LHS) brings an advantage over strength 0 (MC) and + strength 2 is a useful increment over strength 1. Going to strength 3 is + a smaller increment and scrambled QMC like Sobol', Halton are more + performant [7]_. + + To create a LHS of strength 2, the orthogonal array :math:`A` is + randomized by applying a random, bijective map of the set of symbols onto + itself. For example, in column 0, all 0s might become 2; in column 1, + all 0s might become 1, etc. + Then, for each column :math:`i` and symbol :math:`j`, we add a plain, + one-dimensional LHS of size :math:`p` to the subarray where + :math:`A^i = j`. The resulting matrix is finally divided by :math:`p`. + + References + ---------- + .. [1] Mckay et al., "A Comparison of Three Methods for Selecting Values + of Input Variables in the Analysis of Output from a Computer Code." + Technometrics, 1979. + .. [2] M. Stein, "Large sample properties of simulations using Latin + hypercube sampling." Technometrics 29, no. 2: 143-151, 1987. + .. [3] A. B. Owen, "Monte Carlo variance of scrambled net quadrature." + SIAM Journal on Numerical Analysis 34, no. 5: 1884-1910, 1997 + .. [4] Loh, W.-L. "On Latin hypercube sampling." The annals of statistics + 24, no. 5: 2058-2080, 1996. + .. [5] Fang et al. "Design and modeling for computer experiments". + Computer Science and Data Analysis Series, 2006. + .. [6] Damblin et al., "Numerical studies of space filling designs: + optimization of Latin Hypercube Samples and subprojection properties." + Journal of Simulation, 2013. + .. [7] A. B. Owen , "Orthogonal arrays for computer experiments, + integration and visualization." Statistica Sinica, 1992. + .. [8] B. Tang, "Orthogonal Array-Based Latin Hypercubes." + Journal of the American Statistical Association, 1993. + .. [9] Susan K. Seaholm et al. "Latin hypercube sampling and the + sensitivity analysis of a Monte Carlo epidemic model". + Int J Biomed Comput, 23(1-2), 97-112, + :doi:`10.1016/0020-7101(88)90067-0`, 1988. + + Examples + -------- + In [9]_, a Latin Hypercube sampling strategy was used to sample a + parameter space to study the importance of each parameter of an epidemic + model. Such analysis is also called a sensitivity analysis. + + Since the dimensionality of the problem is high (6), it is computationally + expensive to cover the space. When numerical experiments are costly, + QMC enables analysis that may not be possible if using a grid. + + The six parameters of the model represented the probability of illness, + the probability of withdrawal, and four contact probabilities, + The authors assumed uniform distributions for all parameters and generated + 50 samples. + + Using `scipy.stats.qmc.LatinHypercube` to replicate the protocol, the + first step is to create a sample in the unit hypercube: + + >>> from scipy.stats import qmc + >>> sampler = qmc.LatinHypercube(d=6) + >>> sample = sampler.random(n=50) + + Then the sample can be scaled to the appropriate bounds: + + >>> l_bounds = [0.000125, 0.01, 0.0025, 0.05, 0.47, 0.7] + >>> u_bounds = [0.000375, 0.03, 0.0075, 0.15, 0.87, 0.9] + >>> sample_scaled = qmc.scale(sample, l_bounds, u_bounds) + + Such a sample was used to run the model 50 times, and a polynomial + response surface was constructed. This allowed the authors to study the + relative importance of each parameter across the range of + possibilities of every other parameter. + In this computer experiment, they showed a 14-fold reduction in the number + of samples required to maintain an error below 2% on their response surface + when compared to a grid sampling. + + Below are other examples showing alternative ways to construct LHS + with even better coverage of the space. + + Using a base LHS as a baseline. + + >>> sampler = qmc.LatinHypercube(d=2) + >>> sample = sampler.random(n=5) + >>> qmc.discrepancy(sample) + 0.0196... # random + + Use the `optimization` keyword argument to produce a LHS with + lower discrepancy at higher computational cost. + + >>> sampler = qmc.LatinHypercube(d=2, optimization="random-cd") + >>> sample = sampler.random(n=5) + >>> qmc.discrepancy(sample) + 0.0176... # random + + Use the `strength` keyword argument to produce an orthogonal array based + LHS of strength 2. In this case, the number of sample points must be the + square of a prime number. + + >>> sampler = qmc.LatinHypercube(d=2, strength=2) + >>> sample = sampler.random(n=9) + >>> qmc.discrepancy(sample) + 0.00526... # random + + Options could be combined to produce an optimized centered + orthogonal array based LHS. After optimization, the result would not + be guaranteed to be of strength 2. + + """ + + def __init__( + self, d: IntNumber, *, + scramble: bool = True, + strength: int = 1, + optimization: Literal["random-cd", "lloyd"] | None = None, + seed: SeedType = None + ) -> None: + # Used in `scipy.integrate.qmc_quad` + self._init_quad = {'d': d, 'scramble': True, 'strength': strength, + 'optimization': optimization} + super().__init__(d=d, seed=seed, optimization=optimization) + self.scramble = scramble + + lhs_method_strength = { + 1: self._random_lhs, + 2: self._random_oa_lhs + } + + try: + self.lhs_method: Callable = lhs_method_strength[strength] + except KeyError as exc: + message = (f"{strength!r} is not a valid strength. It must be one" + f" of {set(lhs_method_strength)!r}") + raise ValueError(message) from exc + + def _random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + lhs = self.lhs_method(n) + return lhs + + def _random_lhs(self, n: IntNumber = 1) -> np.ndarray: + """Base LHS algorithm.""" + if not self.scramble: + samples: np.ndarray | float = 0.5 + else: + samples = self.rng.uniform(size=(n, self.d)) + + perms = np.tile(np.arange(1, n + 1), + (self.d, 1)) # type: ignore[arg-type] + for i in range(self.d): + self.rng.shuffle(perms[i, :]) + perms = perms.T + + samples = (perms - samples) / n + return samples + + def _random_oa_lhs(self, n: IntNumber = 4) -> np.ndarray: + """Orthogonal array based LHS of strength 2.""" + p = np.sqrt(n).astype(int) + n_row = p**2 + n_col = p + 1 + + primes = primes_from_2_to(p + 1) + if p not in primes or n != n_row: + raise ValueError( + "n is not the square of a prime number. Close" + f" values are {primes[-2:]**2}" + ) + if self.d > p + 1: + raise ValueError("n is too small for d. Must be n > (d-1)**2") + + oa_sample = np.zeros(shape=(n_row, n_col), dtype=int) + + # OA of strength 2 + arrays = np.tile(np.arange(p), (2, 1)) + oa_sample[:, :2] = np.stack(np.meshgrid(*arrays), + axis=-1).reshape(-1, 2) + for p_ in range(1, p): + oa_sample[:, 2+p_-1] = np.mod(oa_sample[:, 0] + + p_*oa_sample[:, 1], p) + + # scramble the OA + oa_sample_ = np.empty(shape=(n_row, n_col), dtype=int) + for j in range(n_col): + perms = self.rng.permutation(p) + oa_sample_[:, j] = perms[oa_sample[:, j]] + + # following is making a scrambled OA into an OA-LHS + oa_lhs_sample = np.zeros(shape=(n_row, n_col)) + lhs_engine = LatinHypercube(d=1, scramble=self.scramble, strength=1, + seed=self.rng) # type: QMCEngine + for j in range(n_col): + for k in range(p): + idx = oa_sample[:, j] == k + lhs = lhs_engine.random(p).flatten() + oa_lhs_sample[:, j][idx] = lhs + oa_sample[:, j][idx] + + lhs_engine = lhs_engine.reset() + + oa_lhs_sample /= p + + return oa_lhs_sample[:, :self.d] # type: ignore + + +class Sobol(QMCEngine): + """Engine for generating (scrambled) Sobol' sequences. + + Sobol' sequences are low-discrepancy, quasi-random numbers. Points + can be drawn using two methods: + + * `random_base2`: safely draw :math:`n=2^m` points. This method + guarantees the balance properties of the sequence. + * `random`: draw an arbitrary number of points from the + sequence. See warning below. + + Parameters + ---------- + d : int + Dimensionality of the sequence. Max dimensionality is 21201. + scramble : bool, optional + If True, use LMS+shift scrambling. Otherwise, no scrambling is done. + Default is True. + bits : int, optional + Number of bits of the generator. Control the maximum number of points + that can be generated, which is ``2**bits``. Maximal value is 64. + It does not correspond to the return type, which is always + ``np.float64`` to prevent points from repeating themselves. + Default is None, which for backward compatibility, corresponds to 30. + + .. versionadded:: 1.9.0 + optimization : {None, "random-cd", "lloyd"}, optional + Whether to use an optimization scheme to improve the quality after + sampling. Note that this is a post-processing step that does not + guarantee that all properties of the sample will be conserved. + Default is None. + + * ``random-cd``: random permutations of coordinates to lower the + centered discrepancy. The best sample based on the centered + discrepancy is constantly updated. Centered discrepancy-based + sampling shows better space-filling robustness toward 2D and 3D + subprojections compared to using other discrepancy measures. + * ``lloyd``: Perturb samples using a modified Lloyd-Max algorithm. + The process converges to equally spaced samples. + + .. versionadded:: 1.10.0 + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Notes + ----- + Sobol' sequences [1]_ provide :math:`n=2^m` low discrepancy points in + :math:`[0,1)^{d}`. Scrambling them [3]_ makes them suitable for singular + integrands, provides a means of error estimation, and can improve their + rate of convergence. The scrambling strategy which is implemented is a + (left) linear matrix scramble (LMS) followed by a digital random shift + (LMS+shift) [2]_. + + There are many versions of Sobol' sequences depending on their + 'direction numbers'. This code uses direction numbers from [4]_. Hence, + the maximum number of dimension is 21201. The direction numbers have been + precomputed with search criterion 6 and can be retrieved at + https://web.maths.unsw.edu.au/~fkuo/sobol/. + + .. warning:: + + Sobol' sequences are a quadrature rule and they lose their balance + properties if one uses a sample size that is not a power of 2, or skips + the first point, or thins the sequence [5]_. + + If :math:`n=2^m` points are not enough then one should take :math:`2^M` + points for :math:`M>m`. When scrambling, the number R of independent + replicates does not have to be a power of 2. + + Sobol' sequences are generated to some number :math:`B` of bits. + After :math:`2^B` points have been generated, the sequence would + repeat. Hence, an error is raised. + The number of bits can be controlled with the parameter `bits`. + + References + ---------- + .. [1] I. M. Sobol', "The distribution of points in a cube and the accurate + evaluation of integrals." Zh. Vychisl. Mat. i Mat. Phys., 7:784-802, + 1967. + .. [2] J. Matousek, "On the L2-discrepancy for anchored boxes." + J. of Complexity 14, 527-556, 1998. + .. [3] Art B. Owen, "Scrambling Sobol and Niederreiter-Xing points." + Journal of Complexity, 14(4):466-489, December 1998. + .. [4] S. Joe and F. Y. Kuo, "Constructing sobol sequences with better + two-dimensional projections." SIAM Journal on Scientific Computing, + 30(5):2635-2654, 2008. + .. [5] Art B. Owen, "On dropping the first Sobol' point." + :arxiv:`2008.08051`, 2020. + + Examples + -------- + Generate samples from a low discrepancy sequence of Sobol'. + + >>> from scipy.stats import qmc + >>> sampler = qmc.Sobol(d=2, scramble=False) + >>> sample = sampler.random_base2(m=3) + >>> sample + array([[0. , 0. ], + [0.5 , 0.5 ], + [0.75 , 0.25 ], + [0.25 , 0.75 ], + [0.375, 0.375], + [0.875, 0.875], + [0.625, 0.125], + [0.125, 0.625]]) + + Compute the quality of the sample using the discrepancy criterion. + + >>> qmc.discrepancy(sample) + 0.013882107204860938 + + To continue an existing design, extra points can be obtained + by calling again `random_base2`. Alternatively, you can skip some + points like: + + >>> _ = sampler.reset() + >>> _ = sampler.fast_forward(4) + >>> sample_continued = sampler.random_base2(m=2) + >>> sample_continued + array([[0.375, 0.375], + [0.875, 0.875], + [0.625, 0.125], + [0.125, 0.625]]) + + Finally, samples can be scaled to bounds. + + >>> l_bounds = [0, 2] + >>> u_bounds = [10, 5] + >>> qmc.scale(sample_continued, l_bounds, u_bounds) + array([[3.75 , 3.125], + [8.75 , 4.625], + [6.25 , 2.375], + [1.25 , 3.875]]) + + """ + + MAXDIM: ClassVar[int] = _MAXDIM + + def __init__( + self, d: IntNumber, *, scramble: bool = True, + bits: IntNumber | None = None, seed: SeedType = None, + optimization: Literal["random-cd", "lloyd"] | None = None + ) -> None: + # Used in `scipy.integrate.qmc_quad` + self._init_quad = {'d': d, 'scramble': True, 'bits': bits, + 'optimization': optimization} + + super().__init__(d=d, optimization=optimization, seed=seed) + if d > self.MAXDIM: + raise ValueError( + f"Maximum supported dimensionality is {self.MAXDIM}." + ) + + self.bits = bits + self.dtype_i: type + + if self.bits is None: + self.bits = 30 + + if self.bits <= 32: + self.dtype_i = np.uint32 + elif 32 < self.bits <= 64: + self.dtype_i = np.uint64 + else: + raise ValueError("Maximum supported 'bits' is 64") + + self.maxn = 2**self.bits + + # v is d x maxbit matrix + self._sv: np.ndarray = np.zeros((d, self.bits), dtype=self.dtype_i) + _initialize_v(self._sv, dim=d, bits=self.bits) + + if not scramble: + self._shift: np.ndarray = np.zeros(d, dtype=self.dtype_i) + else: + # scramble self._shift and self._sv + self._scramble() + + self._quasi = self._shift.copy() + + # normalization constant with the largest possible number + # calculate in Python to not overflow int with 2**64 + self._scale = 1.0 / 2 ** self.bits + + self._first_point = (self._quasi * self._scale).reshape(1, -1) + # explicit casting to float64 + self._first_point = self._first_point.astype(np.float64) + + def _scramble(self) -> None: + """Scramble the sequence using LMS+shift.""" + # Generate shift vector + self._shift = np.dot( + rng_integers(self.rng, 2, size=(self.d, self.bits), + dtype=self.dtype_i), + 2 ** np.arange(self.bits, dtype=self.dtype_i), + ) + # Generate lower triangular matrices (stacked across dimensions) + ltm = np.tril(rng_integers(self.rng, 2, + size=(self.d, self.bits, self.bits), + dtype=self.dtype_i)) + _cscramble( + dim=self.d, bits=self.bits, # type: ignore[arg-type] + ltm=ltm, sv=self._sv + ) + + def _random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + """Draw next point(s) in the Sobol' sequence. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + + Returns + ------- + sample : array_like (n, d) + Sobol' sample. + + """ + sample: np.ndarray = np.empty((n, self.d), dtype=np.float64) + + if n == 0: + return sample + + total_n = self.num_generated + n + if total_n > self.maxn: + msg = ( + f"At most 2**{self.bits}={self.maxn} distinct points can be " + f"generated. {self.num_generated} points have been previously " + f"generated, then: n={self.num_generated}+{n}={total_n}. " + ) + if self.bits != 64: + msg += "Consider increasing `bits`." + raise ValueError(msg) + + if self.num_generated == 0: + # verify n is 2**n + if not (n & (n - 1) == 0): + warnings.warn("The balance properties of Sobol' points require" + " n to be a power of 2.", stacklevel=2) + + if n == 1: + sample = self._first_point + else: + _draw( + n=n - 1, num_gen=self.num_generated, dim=self.d, + scale=self._scale, sv=self._sv, quasi=self._quasi, + sample=sample + ) + sample = np.concatenate( + [self._first_point, sample] + )[:n] # type: ignore[misc] + else: + _draw( + n=n, num_gen=self.num_generated - 1, dim=self.d, + scale=self._scale, sv=self._sv, quasi=self._quasi, + sample=sample + ) + + return sample + + def random_base2(self, m: IntNumber) -> np.ndarray: + """Draw point(s) from the Sobol' sequence. + + This function draws :math:`n=2^m` points in the parameter space + ensuring the balance properties of the sequence. + + Parameters + ---------- + m : int + Logarithm in base 2 of the number of samples; i.e., n = 2^m. + + Returns + ------- + sample : array_like (n, d) + Sobol' sample. + + """ + n = 2 ** m + + total_n = self.num_generated + n + if not (total_n & (total_n - 1) == 0): + raise ValueError("The balance properties of Sobol' points require " + "n to be a power of 2. {0} points have been " + "previously generated, then: n={0}+2**{1}={2}. " + "If you still want to do this, the function " + "'Sobol.random()' can be used." + .format(self.num_generated, m, total_n)) + + return self.random(n) + + def reset(self) -> Sobol: + """Reset the engine to base state. + + Returns + ------- + engine : Sobol + Engine reset to its base state. + + """ + super().reset() + self._quasi = self._shift.copy() + return self + + def fast_forward(self, n: IntNumber) -> Sobol: + """Fast-forward the sequence by `n` positions. + + Parameters + ---------- + n : int + Number of points to skip in the sequence. + + Returns + ------- + engine : Sobol + The fast-forwarded engine. + + """ + if self.num_generated == 0: + _fast_forward( + n=n - 1, num_gen=self.num_generated, dim=self.d, + sv=self._sv, quasi=self._quasi + ) + else: + _fast_forward( + n=n, num_gen=self.num_generated - 1, dim=self.d, + sv=self._sv, quasi=self._quasi + ) + self.num_generated += n + return self + + +class PoissonDisk(QMCEngine): + """Poisson disk sampling. + + Parameters + ---------- + d : int + Dimension of the parameter space. + radius : float + Minimal distance to keep between points when sampling new candidates. + hypersphere : {"volume", "surface"}, optional + Sampling strategy to generate potential candidates to be added in the + final sample. Default is "volume". + + * ``volume``: original Bridson algorithm as described in [1]_. + New candidates are sampled *within* the hypersphere. + * ``surface``: only sample the surface of the hypersphere. + ncandidates : int + Number of candidates to sample per iteration. More candidates result + in a denser sampling as more candidates can be accepted per iteration. + optimization : {None, "random-cd", "lloyd"}, optional + Whether to use an optimization scheme to improve the quality after + sampling. Note that this is a post-processing step that does not + guarantee that all properties of the sample will be conserved. + Default is None. + + * ``random-cd``: random permutations of coordinates to lower the + centered discrepancy. The best sample based on the centered + discrepancy is constantly updated. Centered discrepancy-based + sampling shows better space-filling robustness toward 2D and 3D + subprojections compared to using other discrepancy measures. + * ``lloyd``: Perturb samples using a modified Lloyd-Max algorithm. + The process converges to equally spaced samples. + + .. versionadded:: 1.10.0 + seed : {None, int, `numpy.random.Generator`}, optional + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Notes + ----- + Poisson disk sampling is an iterative sampling strategy. Starting from + a seed sample, `ncandidates` are sampled in the hypersphere + surrounding the seed. Candidates below a certain `radius` or outside the + domain are rejected. New samples are added in a pool of sample seed. The + process stops when the pool is empty or when the number of required + samples is reached. + + The maximum number of point that a sample can contain is directly linked + to the `radius`. As the dimension of the space increases, a higher radius + spreads the points further and help overcome the curse of dimensionality. + See the :ref:`quasi monte carlo tutorial ` for more + details. + + .. warning:: + + The algorithm is more suitable for low dimensions and sampling size + due to its iterative nature and memory requirements. + Selecting a small radius with a high dimension would + mean that the space could contain more samples than using lower + dimension or a bigger radius. + + Some code taken from [2]_, written consent given on 31.03.2021 + by the original author, Shamis, for free use in SciPy under + the 3-clause BSD. + + References + ---------- + .. [1] Robert Bridson, "Fast Poisson Disk Sampling in Arbitrary + Dimensions." SIGGRAPH, 2007. + .. [2] `StackOverflow `__. + + Examples + -------- + Generate a 2D sample using a `radius` of 0.2. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from matplotlib.collections import PatchCollection + >>> from scipy.stats import qmc + >>> + >>> rng = np.random.default_rng() + >>> radius = 0.2 + >>> engine = qmc.PoissonDisk(d=2, radius=radius, seed=rng) + >>> sample = engine.random(20) + + Visualizing the 2D sample and showing that no points are closer than + `radius`. ``radius/2`` is used to visualize non-intersecting circles. + If two samples are exactly at `radius` from each other, then their circle + of radius ``radius/2`` will touch. + + >>> fig, ax = plt.subplots() + >>> _ = ax.scatter(sample[:, 0], sample[:, 1]) + >>> circles = [plt.Circle((xi, yi), radius=radius/2, fill=False) + ... for xi, yi in sample] + >>> collection = PatchCollection(circles, match_original=True) + >>> ax.add_collection(collection) + >>> _ = ax.set(aspect='equal', xlabel=r'$x_1$', ylabel=r'$x_2$', + ... xlim=[0, 1], ylim=[0, 1]) + >>> plt.show() + + Such visualization can be seen as circle packing: how many circle can + we put in the space. It is a np-hard problem. The method `fill_space` + can be used to add samples until no more samples can be added. This is + a hard problem and parameters may need to be adjusted manually. Beware of + the dimension: as the dimensionality increases, the number of samples + required to fill the space increases exponentially + (curse-of-dimensionality). + + """ + + def __init__( + self, + d: IntNumber, + *, + radius: DecimalNumber = 0.05, + hypersphere: Literal["volume", "surface"] = "volume", + ncandidates: IntNumber = 30, + optimization: Literal["random-cd", "lloyd"] | None = None, + seed: SeedType = None + ) -> None: + # Used in `scipy.integrate.qmc_quad` + self._init_quad = {'d': d, 'radius': radius, + 'hypersphere': hypersphere, + 'ncandidates': ncandidates, + 'optimization': optimization} + super().__init__(d=d, optimization=optimization, seed=seed) + + hypersphere_sample = { + "volume": self._hypersphere_volume_sample, + "surface": self._hypersphere_surface_sample + } + + try: + self.hypersphere_method = hypersphere_sample[hypersphere] + except KeyError as exc: + message = ( + f"{hypersphere!r} is not a valid hypersphere sampling" + f" method. It must be one of {set(hypersphere_sample)!r}") + raise ValueError(message) from exc + + # size of the sphere from which the samples are drawn relative to the + # size of a disk (radius) + # for the surface sampler, all new points are almost exactly 1 radius + # away from at least one existing sample +eps to avoid rejection + self.radius_factor = 2 if hypersphere == "volume" else 1.001 + self.radius = radius + self.radius_squared = self.radius**2 + + # sample to generate per iteration in the hypersphere around center + self.ncandidates = ncandidates + + with np.errstate(divide='ignore'): + self.cell_size = self.radius / np.sqrt(self.d) + self.grid_size = ( + np.ceil(np.ones(self.d) / self.cell_size) + ).astype(int) + + self._initialize_grid_pool() + + def _initialize_grid_pool(self): + """Sampling pool and sample grid.""" + self.sample_pool = [] + # Positions of cells + # n-dim value for each grid cell + self.sample_grid = np.empty( + np.append(self.grid_size, self.d), + dtype=np.float32 + ) + # Initialise empty cells with NaNs + self.sample_grid.fill(np.nan) + + def _random( + self, n: IntNumber = 1, *, workers: IntNumber = 1 + ) -> np.ndarray: + """Draw `n` in the interval ``[0, 1]``. + + Note that it can return fewer samples if the space is full. + See the note section of the class. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + + Returns + ------- + sample : array_like (n, d) + QMC sample. + + """ + if n == 0 or self.d == 0: + return np.empty((n, self.d)) + + def in_limits(sample: np.ndarray) -> bool: + return (sample.max() <= 1.) and (sample.min() >= 0.) + + def in_neighborhood(candidate: np.ndarray, n: int = 2) -> bool: + """ + Check if there are samples closer than ``radius_squared`` to the + `candidate` sample. + """ + indices = (candidate / self.cell_size).astype(int) + ind_min = np.maximum(indices - n, np.zeros(self.d, dtype=int)) + ind_max = np.minimum(indices + n + 1, self.grid_size) + + # Check if the center cell is empty + if not np.isnan(self.sample_grid[tuple(indices)][0]): + return True + + a = [slice(ind_min[i], ind_max[i]) for i in range(self.d)] + + # guards against: invalid value encountered in less as we are + # comparing with nan and returns False. Which is wanted. + with np.errstate(invalid='ignore'): + if np.any( + np.sum( + np.square(candidate - self.sample_grid[tuple(a)]), + axis=self.d + ) < self.radius_squared + ): + return True + + return False + + def add_sample(candidate: np.ndarray) -> None: + self.sample_pool.append(candidate) + indices = (candidate / self.cell_size).astype(int) + self.sample_grid[tuple(indices)] = candidate + curr_sample.append(candidate) + + curr_sample: list[np.ndarray] = [] + + if len(self.sample_pool) == 0: + # the pool is being initialized with a single random sample + add_sample(self.rng.random(self.d)) + num_drawn = 1 + else: + num_drawn = 0 + + # exhaust sample pool to have up to n sample + while len(self.sample_pool) and num_drawn < n: + # select a sample from the available pool + idx_center = rng_integers(self.rng, len(self.sample_pool)) + center = self.sample_pool[idx_center] + del self.sample_pool[idx_center] + + # generate candidates around the center sample + candidates = self.hypersphere_method( + center, self.radius * self.radius_factor, self.ncandidates + ) + + # keep candidates that satisfy some conditions + for candidate in candidates: + if in_limits(candidate) and not in_neighborhood(candidate): + add_sample(candidate) + + num_drawn += 1 + if num_drawn >= n: + break + + self.num_generated += num_drawn + return np.array(curr_sample) + + def fill_space(self) -> np.ndarray: + """Draw ``n`` samples in the interval ``[0, 1]``. + + Unlike `random`, this method will try to add points until + the space is full. Depending on ``candidates`` (and to a lesser extent + other parameters), some empty areas can still be present in the sample. + + .. warning:: + + This can be extremely slow in high dimensions or if the + ``radius`` is very small-with respect to the dimensionality. + + Returns + ------- + sample : array_like (n, d) + QMC sample. + + """ + return self.random(np.inf) # type: ignore[arg-type] + + def reset(self) -> PoissonDisk: + """Reset the engine to base state. + + Returns + ------- + engine : PoissonDisk + Engine reset to its base state. + + """ + super().reset() + self._initialize_grid_pool() + return self + + def _hypersphere_volume_sample( + self, center: np.ndarray, radius: DecimalNumber, + candidates: IntNumber = 1 + ) -> np.ndarray: + """Uniform sampling within hypersphere.""" + # should remove samples within r/2 + x = self.rng.standard_normal(size=(candidates, self.d)) + ssq = np.sum(x**2, axis=1) + fr = radius * gammainc(self.d/2, ssq/2)**(1/self.d) / np.sqrt(ssq) + fr_tiled = np.tile( + fr.reshape(-1, 1), (1, self.d) # type: ignore[arg-type] + ) + p = center + np.multiply(x, fr_tiled) + return p + + def _hypersphere_surface_sample( + self, center: np.ndarray, radius: DecimalNumber, + candidates: IntNumber = 1 + ) -> np.ndarray: + """Uniform sampling on the hypersphere's surface.""" + vec = self.rng.standard_normal(size=(candidates, self.d)) + vec /= np.linalg.norm(vec, axis=1)[:, None] + p = center + np.multiply(vec, radius) + return p + + +class MultivariateNormalQMC: + r"""QMC sampling from a multivariate Normal :math:`N(\mu, \Sigma)`. + + Parameters + ---------- + mean : array_like (d,) + The mean vector. Where ``d`` is the dimension. + cov : array_like (d, d), optional + The covariance matrix. If omitted, use `cov_root` instead. + If both `cov` and `cov_root` are omitted, use the identity matrix. + cov_root : array_like (d, d'), optional + A root decomposition of the covariance matrix, where ``d'`` may be less + than ``d`` if the covariance is not full rank. If omitted, use `cov`. + inv_transform : bool, optional + If True, use inverse transform instead of Box-Muller. Default is True. + engine : QMCEngine, optional + Quasi-Monte Carlo engine sampler. If None, `Sobol` is used. + seed : {None, int, `numpy.random.Generator`}, optional + Used only if `engine` is None. + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Examples + -------- + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import qmc + >>> dist = qmc.MultivariateNormalQMC(mean=[0, 5], cov=[[1, 0], [0, 1]]) + >>> sample = dist.random(512) + >>> _ = plt.scatter(sample[:, 0], sample[:, 1]) + >>> plt.show() + + """ + + def __init__( + self, mean: npt.ArrayLike, cov: npt.ArrayLike | None = None, *, + cov_root: npt.ArrayLike | None = None, + inv_transform: bool = True, + engine: QMCEngine | None = None, + seed: SeedType = None + ) -> None: + mean = np.asarray(np.atleast_1d(mean)) + d = mean.shape[0] + if cov is not None: + # covariance matrix provided + cov = np.asarray(np.atleast_2d(cov)) + # check for square/symmetric cov matrix and mean vector has the + # same d + if not mean.shape[0] == cov.shape[0]: + raise ValueError("Dimension mismatch between mean and " + "covariance.") + if not np.allclose(cov, cov.transpose()): + raise ValueError("Covariance matrix is not symmetric.") + # compute Cholesky decomp; if it fails, do the eigen decomposition + try: + cov_root = np.linalg.cholesky(cov).transpose() + except np.linalg.LinAlgError: + eigval, eigvec = np.linalg.eigh(cov) + if not np.all(eigval >= -1.0e-8): + raise ValueError("Covariance matrix not PSD.") + eigval = np.clip(eigval, 0.0, None) + cov_root = (eigvec * np.sqrt(eigval)).transpose() + elif cov_root is not None: + # root decomposition provided + cov_root = np.atleast_2d(cov_root) + if not mean.shape[0] == cov_root.shape[0]: + raise ValueError("Dimension mismatch between mean and " + "covariance.") + else: + # corresponds to identity covariance matrix + cov_root = None + + self._inv_transform = inv_transform + + if not inv_transform: + # to apply Box-Muller, we need an even number of dimensions + engine_dim = 2 * math.ceil(d / 2) + else: + engine_dim = d + if engine is None: + self.engine = Sobol( + d=engine_dim, scramble=True, bits=30, seed=seed + ) # type: QMCEngine + elif isinstance(engine, QMCEngine): + if engine.d != engine_dim: + raise ValueError("Dimension of `engine` must be consistent" + " with dimensions of mean and covariance." + " If `inv_transform` is False, it must be" + " an even number.") + self.engine = engine + else: + raise ValueError("`engine` must be an instance of " + "`scipy.stats.qmc.QMCEngine` or `None`.") + + self._mean = mean + self._corr_matrix = cov_root + + self._d = d + + def random(self, n: IntNumber = 1) -> np.ndarray: + """Draw `n` QMC samples from the multivariate Normal. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + + Returns + ------- + sample : array_like (n, d) + Sample. + + """ + base_samples = self._standard_normal_samples(n) + return self._correlate(base_samples) + + def _correlate(self, base_samples: np.ndarray) -> np.ndarray: + if self._corr_matrix is not None: + return base_samples @ self._corr_matrix + self._mean + else: + # avoid multiplying with identity here + return base_samples + self._mean + + def _standard_normal_samples(self, n: IntNumber = 1) -> np.ndarray: + """Draw `n` QMC samples from the standard Normal :math:`N(0, I_d)`. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + + Returns + ------- + sample : array_like (n, d) + Sample. + + """ + # get base samples + samples = self.engine.random(n) + if self._inv_transform: + # apply inverse transform + # (values to close to 0/1 result in inf values) + return stats.norm.ppf(0.5 + (1 - 1e-10) * (samples - 0.5)) # type: ignore[attr-defined] # noqa: E501 + else: + # apply Box-Muller transform (note: indexes starting from 1) + even = np.arange(0, samples.shape[-1], 2) + Rs = np.sqrt(-2 * np.log(samples[:, even])) + thetas = 2 * math.pi * samples[:, 1 + even] + cos = np.cos(thetas) + sin = np.sin(thetas) + transf_samples = np.stack([Rs * cos, Rs * sin], + -1).reshape(n, -1) + # make sure we only return the number of dimension requested + return transf_samples[:, : self._d] + + +class MultinomialQMC: + r"""QMC sampling from a multinomial distribution. + + Parameters + ---------- + pvals : array_like (k,) + Vector of probabilities of size ``k``, where ``k`` is the number + of categories. Elements must be non-negative and sum to 1. + n_trials : int + Number of trials. + engine : QMCEngine, optional + Quasi-Monte Carlo engine sampler. If None, `Sobol` is used. + seed : {None, int, `numpy.random.Generator`}, optional + Used only if `engine` is None. + If `seed` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(seed)``. + If `seed` is already a ``Generator`` instance, then the provided + instance is used. + + Examples + -------- + Let's define 3 categories and for a given sample, the sum of the trials + of each category is 8. The number of trials per category is determined + by the `pvals` associated to each category. + Then, we sample this distribution 64 times. + + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import qmc + >>> dist = qmc.MultinomialQMC( + ... pvals=[0.2, 0.4, 0.4], n_trials=10, engine=qmc.Halton(d=1) + ... ) + >>> sample = dist.random(64) + + We can plot the sample and verify that the median of number of trials + for each category is following the `pvals`. That would be + ``pvals * n_trials = [2, 4, 4]``. + + >>> fig, ax = plt.subplots() + >>> ax.yaxis.get_major_locator().set_params(integer=True) + >>> _ = ax.boxplot(sample) + >>> ax.set(xlabel="Categories", ylabel="Trials") + >>> plt.show() + + """ + + def __init__( + self, pvals: npt.ArrayLike, n_trials: IntNumber, + *, engine: QMCEngine | None = None, + seed: SeedType = None + ) -> None: + self.pvals = np.atleast_1d(np.asarray(pvals)) + if np.min(pvals) < 0: + raise ValueError('Elements of pvals must be non-negative.') + if not np.isclose(np.sum(pvals), 1): + raise ValueError('Elements of pvals must sum to 1.') + self.n_trials = n_trials + if engine is None: + self.engine = Sobol( + d=1, scramble=True, bits=30, seed=seed + ) # type: QMCEngine + elif isinstance(engine, QMCEngine): + if engine.d != 1: + raise ValueError("Dimension of `engine` must be 1.") + self.engine = engine + else: + raise ValueError("`engine` must be an instance of " + "`scipy.stats.qmc.QMCEngine` or `None`.") + + def random(self, n: IntNumber = 1) -> np.ndarray: + """Draw `n` QMC samples from the multinomial distribution. + + Parameters + ---------- + n : int, optional + Number of samples to generate in the parameter space. Default is 1. + + Returns + ------- + samples : array_like (n, pvals) + Sample. + + """ + sample = np.empty((n, len(self.pvals))) + for i in range(n): + base_draws = self.engine.random(self.n_trials).ravel() + p_cumulative = np.empty_like(self.pvals, dtype=float) + _fill_p_cumulative(np.array(self.pvals, dtype=float), p_cumulative) + sample_ = np.zeros_like(self.pvals, dtype=np.intp) + _categorize(base_draws, p_cumulative, sample_) + sample[i] = sample_ + return sample + + +def _select_optimizer( + optimization: Literal["random-cd", "lloyd"] | None, config: dict +) -> Callable | None: + """A factory for optimization methods.""" + optimization_method: dict[str, Callable] = { + "random-cd": _random_cd, + "lloyd": _lloyd_centroidal_voronoi_tessellation + } + + optimizer: partial | None + if optimization is not None: + try: + optimization = optimization.lower() # type: ignore[assignment] + optimizer_ = optimization_method[optimization] + except KeyError as exc: + message = (f"{optimization!r} is not a valid optimization" + f" method. It must be one of" + f" {set(optimization_method)!r}") + raise ValueError(message) from exc + + # config + optimizer = partial(optimizer_, **config) + else: + optimizer = None + + return optimizer + + +def _random_cd( + best_sample: np.ndarray, n_iters: int, n_nochange: int, rng: GeneratorType, + **kwargs: dict +) -> np.ndarray: + """Optimal LHS on CD. + + Create a base LHS and do random permutations of coordinates to + lower the centered discrepancy. + Because it starts with a normal LHS, it also works with the + `scramble` keyword argument. + + Two stopping criterion are used to stop the algorithm: at most, + `n_iters` iterations are performed; or if there is no improvement + for `n_nochange` consecutive iterations. + """ + del kwargs # only use keywords which are defined, needed by factory + + n, d = best_sample.shape + + if d == 0 or n == 0: + return np.empty((n, d)) + + if d == 1 or n == 1: + # discrepancy measures are invariant under permuting factors and runs + return best_sample + + best_disc = discrepancy(best_sample) + + bounds = ([0, d - 1], + [0, n - 1], + [0, n - 1]) + + n_nochange_ = 0 + n_iters_ = 0 + while n_nochange_ < n_nochange and n_iters_ < n_iters: + n_iters_ += 1 + + col = rng_integers(rng, *bounds[0], endpoint=True) # type: ignore[misc] + row_1 = rng_integers(rng, *bounds[1], endpoint=True) # type: ignore[misc] + row_2 = rng_integers(rng, *bounds[2], endpoint=True) # type: ignore[misc] + disc = _perturb_discrepancy(best_sample, + row_1, row_2, col, + best_disc) + if disc < best_disc: + best_sample[row_1, col], best_sample[row_2, col] = ( + best_sample[row_2, col], best_sample[row_1, col]) + + best_disc = disc + n_nochange_ = 0 + else: + n_nochange_ += 1 + + return best_sample + + +def _l1_norm(sample: np.ndarray) -> float: + return distance.pdist(sample, 'cityblock').min() + + +def _lloyd_iteration( + sample: np.ndarray, + decay: float, + qhull_options: str +) -> np.ndarray: + """Lloyd-Max algorithm iteration. + + Based on the implementation of Stéfan van der Walt: + + https://github.com/stefanv/lloyd + + which is: + + Copyright (c) 2021-04-21 Stéfan van der Walt + https://github.com/stefanv/lloyd + MIT License + + Parameters + ---------- + sample : array_like (n, d) + The sample to iterate on. + decay : float + Relaxation decay. A positive value would move the samples toward + their centroid, and negative value would move them away. + 1 would move the samples to their centroid. + qhull_options : str + Additional options to pass to Qhull. See Qhull manual + for details. (Default: "Qbb Qc Qz Qj Qx" for ndim > 4 and + "Qbb Qc Qz Qj" otherwise.) + + Returns + ------- + sample : array_like (n, d) + The sample after an iteration of Lloyd's algorithm. + + """ + new_sample = np.empty_like(sample) + + voronoi = Voronoi(sample, qhull_options=qhull_options) + + for ii, idx in enumerate(voronoi.point_region): + # the region is a series of indices into self.voronoi.vertices + # remove samples at infinity, designated by index -1 + region = [i for i in voronoi.regions[idx] if i != -1] + + # get the vertices for this region + verts = voronoi.vertices[region] + + # clipping would be wrong, we need to intersect + # verts = np.clip(verts, 0, 1) + + # move samples towards centroids: + # Centroid in n-D is the mean for uniformly distributed nodes + # of a geometry. + centroid = np.mean(verts, axis=0) + new_sample[ii] = sample[ii] + (centroid - sample[ii]) * decay + + # only update sample to centroid within the region + is_valid = np.all(np.logical_and(new_sample >= 0, new_sample <= 1), axis=1) + sample[is_valid] = new_sample[is_valid] + + return sample + + +def _lloyd_centroidal_voronoi_tessellation( + sample: npt.ArrayLike, + *, + tol: DecimalNumber = 1e-5, + maxiter: IntNumber = 10, + qhull_options: str | None = None, + **kwargs: dict +) -> np.ndarray: + """Approximate Centroidal Voronoi Tessellation. + + Perturb samples in N-dimensions using Lloyd-Max algorithm. + + Parameters + ---------- + sample : array_like (n, d) + The sample to iterate on. With ``n`` the number of samples and ``d`` + the dimension. Samples must be in :math:`[0, 1]^d`, with ``d>=2``. + tol : float, optional + Tolerance for termination. If the min of the L1-norm over the samples + changes less than `tol`, it stops the algorithm. Default is 1e-5. + maxiter : int, optional + Maximum number of iterations. It will stop the algorithm even if + `tol` is above the threshold. + Too many iterations tend to cluster the samples as a hypersphere. + Default is 10. + qhull_options : str, optional + Additional options to pass to Qhull. See Qhull manual + for details. (Default: "Qbb Qc Qz Qj Qx" for ndim > 4 and + "Qbb Qc Qz Qj" otherwise.) + + Returns + ------- + sample : array_like (n, d) + The sample after being processed by Lloyd-Max algorithm. + + Notes + ----- + Lloyd-Max algorithm is an iterative process with the purpose of improving + the dispersion of samples. For given sample: (i) compute a Voronoi + Tessellation; (ii) find the centroid of each Voronoi cell; (iii) move the + samples toward the centroid of their respective cell. See [1]_, [2]_. + + A relaxation factor is used to control how fast samples can move at each + iteration. This factor is starting at 2 and ending at 1 after `maxiter` + following an exponential decay. + + The process converges to equally spaced samples. It implies that measures + like the discrepancy could suffer from too many iterations. On the other + hand, L1 and L2 distances should improve. This is especially true with + QMC methods which tend to favor the discrepancy over other criteria. + + .. note:: + + The current implementation does not intersect the Voronoi Tessellation + with the boundaries. This implies that for a low number of samples, + empirically below 20, no Voronoi cell is touching the boundaries. + Hence, samples cannot be moved close to the boundaries. + + Further improvements could consider the samples at infinity so that + all boundaries are segments of some Voronoi cells. This would fix + the computation of the centroid position. + + .. warning:: + + The Voronoi Tessellation step is expensive and quickly becomes + intractable with dimensions as low as 10 even for a sample + of size as low as 1000. + + .. versionadded:: 1.9.0 + + References + ---------- + .. [1] Lloyd. "Least Squares Quantization in PCM". + IEEE Transactions on Information Theory, 1982. + .. [2] Max J. "Quantizing for minimum distortion". + IEEE Transactions on Information Theory, 1960. + + Examples + -------- + >>> import numpy as np + >>> from scipy.spatial import distance + >>> from scipy.stats._qmc import _lloyd_centroidal_voronoi_tessellation + >>> rng = np.random.default_rng() + >>> sample = rng.random((128, 2)) + + .. note:: + + The samples need to be in :math:`[0, 1]^d`. `scipy.stats.qmc.scale` + can be used to scale the samples from their + original bounds to :math:`[0, 1]^d`. And back to their original bounds. + + Compute the quality of the sample using the L1 criterion. + + >>> def l1_norm(sample): + ... return distance.pdist(sample, 'cityblock').min() + + >>> l1_norm(sample) + 0.00161... # random + + Now process the sample using Lloyd's algorithm and check the improvement + on the L1. The value should increase. + + >>> sample = _lloyd_centroidal_voronoi_tessellation(sample) + >>> l1_norm(sample) + 0.0278... # random + + """ + del kwargs # only use keywords which are defined, needed by factory + + sample = np.asarray(sample).copy() + + if not sample.ndim == 2: + raise ValueError('`sample` is not a 2D array') + + if not sample.shape[1] >= 2: + raise ValueError('`sample` dimension is not >= 2') + + # Checking that sample is within the hypercube + if (sample.max() > 1.) or (sample.min() < 0.): + raise ValueError('`sample` is not in unit hypercube') + + if qhull_options is None: + qhull_options = 'Qbb Qc Qz QJ' + + if sample.shape[1] >= 5: + qhull_options += ' Qx' + + # Fit an exponential to be 2 at 0 and 1 at `maxiter`. + # The decay is used for relaxation. + # analytical solution for y=exp(-maxiter/x) - 0.1 + root = -maxiter / np.log(0.1) + decay = [np.exp(-x / root)+0.9 for x in range(maxiter)] + + l1_old = _l1_norm(sample=sample) + for i in range(maxiter): + sample = _lloyd_iteration( + sample=sample, decay=decay[i], + qhull_options=qhull_options, + ) + + l1_new = _l1_norm(sample=sample) + + if abs(l1_new - l1_old) < tol: + break + else: + l1_old = l1_new + + return sample + + +def _validate_workers(workers: IntNumber = 1) -> IntNumber: + """Validate `workers` based on platform and value. + + Parameters + ---------- + workers : int, optional + Number of workers to use for parallel processing. If -1 is + given all CPU threads are used. Default is 1. + + Returns + ------- + Workers : int + Number of CPU used by the algorithm + + """ + workers = int(workers) + if workers == -1: + workers = os.cpu_count() # type: ignore[assignment] + if workers is None: + raise NotImplementedError( + "Cannot determine the number of cpus using os.cpu_count(), " + "cannot use -1 for the number of workers" + ) + elif workers <= 0: + raise ValueError(f"Invalid number of workers: {workers}, must be -1 " + "or > 0") + + return workers + + +def _validate_bounds( + l_bounds: npt.ArrayLike, u_bounds: npt.ArrayLike, d: int +) -> tuple[np.ndarray, ...]: + """Bounds input validation. + + Parameters + ---------- + l_bounds, u_bounds : array_like (d,) + Lower and upper bounds. + d : int + Dimension to use for broadcasting. + + Returns + ------- + l_bounds, u_bounds : array_like (d,) + Lower and upper bounds. + + """ + try: + lower = np.broadcast_to(l_bounds, d) + upper = np.broadcast_to(u_bounds, d) + except ValueError as exc: + msg = ("'l_bounds' and 'u_bounds' must be broadcastable and respect" + " the sample dimension") + raise ValueError(msg) from exc + + if not np.all(lower < upper): + raise ValueError("Bounds are not consistent 'l_bounds' < 'u_bounds'") + + return lower, upper diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc_cy.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc_cy.pyi new file mode 100644 index 0000000000000000000000000000000000000000..1006385a43179478a9a4a32ae5f825aa5b8b35c4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmc_cy.pyi @@ -0,0 +1,54 @@ +import numpy as np +from scipy._lib._util import DecimalNumber, IntNumber + + +def _cy_wrapper_centered_discrepancy( + sample: np.ndarray, + iterative: bool, + workers: IntNumber, +) -> float: ... + + +def _cy_wrapper_wrap_around_discrepancy( + sample: np.ndarray, + iterative: bool, + workers: IntNumber, +) -> float: ... + + +def _cy_wrapper_mixture_discrepancy( + sample: np.ndarray, + iterative: bool, + workers: IntNumber, +) -> float: ... + + +def _cy_wrapper_l2_star_discrepancy( + sample: np.ndarray, + iterative: bool, + workers: IntNumber, +) -> float: ... + + +def _cy_wrapper_update_discrepancy( + x_new_view: np.ndarray, + sample_view: np.ndarray, + initial_disc: DecimalNumber, +) -> float: ... + + +def _cy_van_der_corput( + n: IntNumber, + base: IntNumber, + start_index: IntNumber, + workers: IntNumber, +) -> np.ndarray: ... + + +def _cy_van_der_corput_scrambled( + n: IntNumber, + base: IntNumber, + start_index: IntNumber, + permutations: np.ndarray, + workers: IntNumber, +) -> np.ndarray: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmvnt.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmvnt.py new file mode 100644 index 0000000000000000000000000000000000000000..a0e9c5ebb3cba91e0bfa5e600a1a04d2459280ad --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_qmvnt.py @@ -0,0 +1,533 @@ +# Integration of multivariate normal and t distributions. + +# Adapted from the MATLAB original implementations by Dr. Alan Genz. + +# http://www.math.wsu.edu/faculty/genz/software/software.html + +# Copyright (C) 2013, Alan Genz, All rights reserved. +# Python implementation is copyright (C) 2022, Robert Kern, All rights +# reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. The contributor name(s) may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import numpy as np + +from scipy.fft import fft, ifft +from scipy.special import gammaincinv, ndtr, ndtri +from scipy.stats._qmc import primes_from_2_to + + +phi = ndtr +phinv = ndtri + + +def _factorize_int(n): + """Return a sorted list of the unique prime factors of a positive integer. + """ + # NOTE: There are lots faster ways to do this, but this isn't terrible. + factors = set() + for p in primes_from_2_to(int(np.sqrt(n)) + 1): + while not (n % p): + factors.add(p) + n //= p + if n == 1: + break + if n != 1: + factors.add(n) + return sorted(factors) + + +def _primitive_root(p): + """Compute a primitive root of the prime number `p`. + + Used in the CBC lattice construction. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Primitive_root_modulo_n + """ + # p is prime + pm = p - 1 + factors = _factorize_int(pm) + n = len(factors) + r = 2 + k = 0 + while k < n: + d = pm // factors[k] + # pow() doesn't like numpy scalar types. + rd = pow(int(r), int(d), int(p)) + if rd == 1: + r += 1 + k = 0 + else: + k += 1 + return r + + +def _cbc_lattice(n_dim, n_qmc_samples): + """Compute a QMC lattice generator using a Fast CBC construction. + + Parameters + ---------- + n_dim : int > 0 + The number of dimensions for the lattice. + n_qmc_samples : int > 0 + The desired number of QMC samples. This will be rounded down to the + nearest prime to enable the CBC construction. + + Returns + ------- + q : float array : shape=(n_dim,) + The lattice generator vector. All values are in the open interval + `(0, 1)`. + actual_n_qmc_samples : int + The prime number of QMC samples that must be used with this lattice, + no more, no less. + + References + ---------- + .. [1] Nuyens, D. and Cools, R. "Fast Component-by-Component Construction, + a Reprise for Different Kernels", In H. Niederreiter and D. Talay, + editors, Monte-Carlo and Quasi-Monte Carlo Methods 2004, + Springer-Verlag, 2006, 371-385. + """ + # Round down to the nearest prime number. + primes = primes_from_2_to(n_qmc_samples + 1) + n_qmc_samples = primes[-1] + + bt = np.ones(n_dim) + gm = np.hstack([1.0, 0.8 ** np.arange(n_dim - 1)]) + q = 1 + w = 0 + z = np.arange(1, n_dim + 1) + m = (n_qmc_samples - 1) // 2 + g = _primitive_root(n_qmc_samples) + # Slightly faster way to compute perm[j] = pow(g, j, n_qmc_samples) + # Shame that we don't have modulo pow() implemented as a ufunc. + perm = np.ones(m, dtype=int) + for j in range(m - 1): + perm[j + 1] = (g * perm[j]) % n_qmc_samples + perm = np.minimum(n_qmc_samples - perm, perm) + pn = perm / n_qmc_samples + c = pn * pn - pn + 1.0 / 6 + fc = fft(c) + for s in range(1, n_dim): + reordered = np.hstack([ + c[:w+1][::-1], + c[w+1:m][::-1], + ]) + q = q * (bt[s-1] + gm[s-1] * reordered) + w = ifft(fc * fft(q)).real.argmin() + z[s] = perm[w] + q = z / n_qmc_samples + return q, n_qmc_samples + + +# Note: this function is not currently used or tested by any SciPy code. It is +# included in this file to facilitate the development of a parameter for users +# to set the desired CDF accuracy, but must be reviewed and tested before use. +def _qauto(func, covar, low, high, rng, error=1e-3, limit=10_000, **kwds): + """Automatically rerun the integration to get the required error bound. + + Parameters + ---------- + func : callable + Either :func:`_qmvn` or :func:`_qmvt`. + covar, low, high : array + As specified in :func:`_qmvn` and :func:`_qmvt`. + rng : Generator, optional + default_rng(), yada, yada + error : float > 0 + The desired error bound. + limit : int > 0: + The rough limit of the number of integration points to consider. The + integration will stop looping once this limit has been *exceeded*. + **kwds : + Other keyword arguments to pass to `func`. When using :func:`_qmvt`, be + sure to include ``nu=`` as one of these. + + Returns + ------- + prob : float + The estimated probability mass within the bounds. + est_error : float + 3 times the standard error of the batch estimates. + n_samples : int + The number of integration points actually used. + """ + n = len(covar) + n_samples = 0 + if n == 1: + prob = phi(high) - phi(low) + # More or less + est_error = 1e-15 + else: + mi = min(limit, n * 1000) + prob = 0.0 + est_error = 1.0 + ei = 0.0 + while est_error > error and n_samples < limit: + mi = round(np.sqrt(2) * mi) + pi, ei, ni = func(mi, covar, low, high, rng=rng, **kwds) + n_samples += ni + wt = 1.0 / (1 + (ei / est_error)**2) + prob += wt * (pi - prob) + est_error = np.sqrt(wt) * ei + return prob, est_error, n_samples + + +# Note: this function is not currently used or tested by any SciPy code. It is +# included in this file to facilitate the resolution of gh-8367, gh-16142, and +# possibly gh-14286, but must be reviewed and tested before use. +def _qmvn(m, covar, low, high, rng, lattice='cbc', n_batches=10): + """Multivariate normal integration over box bounds. + + Parameters + ---------- + m : int > n_batches + The number of points to sample. This number will be divided into + `n_batches` batches that apply random offsets of the sampling lattice + for each batch in order to estimate the error. + covar : (n, n) float array + Possibly singular, positive semidefinite symmetric covariance matrix. + low, high : (n,) float array + The low and high integration bounds. + rng : Generator, optional + default_rng(), yada, yada + lattice : 'cbc' or callable + The type of lattice rule to use to construct the integration points. + n_batches : int > 0, optional + The number of QMC batches to apply. + + Returns + ------- + prob : float + The estimated probability mass within the bounds. + est_error : float + 3 times the standard error of the batch estimates. + """ + cho, lo, hi = _permuted_cholesky(covar, low, high) + n = cho.shape[0] + ct = cho[0, 0] + c = phi(lo[0] / ct) + d = phi(hi[0] / ct) + ci = c + dci = d - ci + prob = 0.0 + error_var = 0.0 + q, n_qmc_samples = _cbc_lattice(n - 1, max(m // n_batches, 1)) + y = np.zeros((n - 1, n_qmc_samples)) + i_samples = np.arange(n_qmc_samples) + 1 + for j in range(n_batches): + c = np.full(n_qmc_samples, ci) + dc = np.full(n_qmc_samples, dci) + pv = dc.copy() + for i in range(1, n): + # Pseudorandomly-shifted lattice coordinate. + z = q[i - 1] * i_samples + rng.random() + # Fast remainder(z, 1.0) + z -= z.astype(int) + # Tent periodization transform. + x = abs(2 * z - 1) + y[i - 1, :] = phinv(c + x * dc) + s = cho[i, :i] @ y[:i, :] + ct = cho[i, i] + c = phi((lo[i] - s) / ct) + d = phi((hi[i] - s) / ct) + dc = d - c + pv = pv * dc + # Accumulate the mean and error variances with online formulations. + d = (pv.mean() - prob) / (j + 1) + prob += d + error_var = (j - 1) * error_var / (j + 1) + d * d + # Error bounds are 3 times the standard error of the estimates. + est_error = 3 * np.sqrt(error_var) + n_samples = n_qmc_samples * n_batches + return prob, est_error, n_samples + + +# Note: this function is not currently used or tested by any SciPy code. It is +# included in this file to facilitate the resolution of gh-8367, gh-16142, and +# possibly gh-14286, but must be reviewed and tested before use. +def _mvn_qmc_integrand(covar, low, high, use_tent=False): + """Transform the multivariate normal integration into a QMC integrand over + a unit hypercube. + + The dimensionality of the resulting hypercube integration domain is one + less than the dimensionality of the original integrand. Note that this + transformation subsumes the integration bounds in order to account for + infinite bounds. The QMC integration one does with the returned integrand + should be on the unit hypercube. + + Parameters + ---------- + covar : (n, n) float array + Possibly singular, positive semidefinite symmetric covariance matrix. + low, high : (n,) float array + The low and high integration bounds. + use_tent : bool, optional + If True, then use tent periodization. Only helpful for lattice rules. + + Returns + ------- + integrand : Callable[[NDArray], NDArray] + The QMC-integrable integrand. It takes an + ``(n_qmc_samples, ndim_integrand)`` array of QMC samples in the unit + hypercube and returns the ``(n_qmc_samples,)`` evaluations of at these + QMC points. + ndim_integrand : int + The dimensionality of the integrand. Equal to ``n-1``. + """ + cho, lo, hi = _permuted_cholesky(covar, low, high) + n = cho.shape[0] + ndim_integrand = n - 1 + ct = cho[0, 0] + c = phi(lo[0] / ct) + d = phi(hi[0] / ct) + ci = c + dci = d - ci + + def integrand(*zs): + ndim_qmc = len(zs) + n_qmc_samples = len(np.atleast_1d(zs[0])) + assert ndim_qmc == ndim_integrand + y = np.zeros((ndim_qmc, n_qmc_samples)) + c = np.full(n_qmc_samples, ci) + dc = np.full(n_qmc_samples, dci) + pv = dc.copy() + for i in range(1, n): + if use_tent: + # Tent periodization transform. + x = abs(2 * zs[i-1] - 1) + else: + x = zs[i-1] + y[i - 1, :] = phinv(c + x * dc) + s = cho[i, :i] @ y[:i, :] + ct = cho[i, i] + c = phi((lo[i] - s) / ct) + d = phi((hi[i] - s) / ct) + dc = d - c + pv = pv * dc + return pv + + return integrand, ndim_integrand + + +def _qmvt(m, nu, covar, low, high, rng, lattice='cbc', n_batches=10): + """Multivariate t integration over box bounds. + + Parameters + ---------- + m : int > n_batches + The number of points to sample. This number will be divided into + `n_batches` batches that apply random offsets of the sampling lattice + for each batch in order to estimate the error. + nu : float >= 0 + The shape parameter of the multivariate t distribution. + covar : (n, n) float array + Possibly singular, positive semidefinite symmetric covariance matrix. + low, high : (n,) float array + The low and high integration bounds. + rng : Generator, optional + default_rng(), yada, yada + lattice : 'cbc' or callable + The type of lattice rule to use to construct the integration points. + n_batches : int > 0, optional + The number of QMC batches to apply. + + Returns + ------- + prob : float + The estimated probability mass within the bounds. + est_error : float + 3 times the standard error of the batch estimates. + n_samples : int + The number of samples actually used. + """ + sn = max(1.0, np.sqrt(nu)) + low = np.asarray(low, dtype=np.float64) + high = np.asarray(high, dtype=np.float64) + cho, lo, hi = _permuted_cholesky(covar, low / sn, high / sn) + n = cho.shape[0] + prob = 0.0 + error_var = 0.0 + q, n_qmc_samples = _cbc_lattice(n, max(m // n_batches, 1)) + i_samples = np.arange(n_qmc_samples) + 1 + for j in range(n_batches): + pv = np.ones(n_qmc_samples) + s = np.zeros((n, n_qmc_samples)) + for i in range(n): + # Pseudorandomly-shifted lattice coordinate. + z = q[i] * i_samples + rng.random() + # Fast remainder(z, 1.0) + z -= z.astype(int) + # Tent periodization transform. + x = abs(2 * z - 1) + # FIXME: Lift the i==0 case out of the loop to make the logic + # easier to follow. + if i == 0: + # We'll use one of the QR variates to pull out the + # t-distribution scaling. + if nu > 0: + r = np.sqrt(2 * gammaincinv(nu / 2, x)) + else: + r = np.ones_like(x) + else: + y = phinv(c + x * dc) # noqa: F821 + with np.errstate(invalid='ignore'): + s[i:, :] += cho[i:, i - 1][:, np.newaxis] * y + si = s[i, :] + + c = np.ones(n_qmc_samples) + d = np.ones(n_qmc_samples) + with np.errstate(invalid='ignore'): + lois = lo[i] * r - si + hiis = hi[i] * r - si + c[lois < -9] = 0.0 + d[hiis < -9] = 0.0 + lo_mask = abs(lois) < 9 + hi_mask = abs(hiis) < 9 + c[lo_mask] = phi(lois[lo_mask]) + d[hi_mask] = phi(hiis[hi_mask]) + + dc = d - c + pv *= dc + + # Accumulate the mean and error variances with online formulations. + d = (pv.mean() - prob) / (j + 1) + prob += d + error_var = (j - 1) * error_var / (j + 1) + d * d + # Error bounds are 3 times the standard error of the estimates. + est_error = 3 * np.sqrt(error_var) + n_samples = n_qmc_samples * n_batches + return prob, est_error, n_samples + + +def _permuted_cholesky(covar, low, high, tol=1e-10): + """Compute a scaled, permuted Cholesky factor, with integration bounds. + + The scaling and permuting of the dimensions accomplishes part of the + transformation of the original integration problem into a more numerically + tractable form. The lower-triangular Cholesky factor will then be used in + the subsequent integration. The integration bounds will be scaled and + permuted as well. + + Parameters + ---------- + covar : (n, n) float array + Possibly singular, positive semidefinite symmetric covariance matrix. + low, high : (n,) float array + The low and high integration bounds. + tol : float, optional + The singularity tolerance. + + Returns + ------- + cho : (n, n) float array + Lower Cholesky factor, scaled and permuted. + new_low, new_high : (n,) float array + The scaled and permuted low and high integration bounds. + """ + # Make copies for outputting. + cho = np.array(covar, dtype=np.float64) + new_lo = np.array(low, dtype=np.float64) + new_hi = np.array(high, dtype=np.float64) + n = cho.shape[0] + if cho.shape != (n, n): + raise ValueError("expected a square symmetric array") + if new_lo.shape != (n,) or new_hi.shape != (n,): + raise ValueError( + "expected integration boundaries the same dimensions " + "as the covariance matrix" + ) + # Scale by the sqrt of the diagonal. + dc = np.sqrt(np.maximum(np.diag(cho), 0.0)) + # But don't divide by 0. + dc[dc == 0.0] = 1.0 + new_lo /= dc + new_hi /= dc + cho /= dc + cho /= dc[:, np.newaxis] + + y = np.zeros(n) + sqtp = np.sqrt(2 * np.pi) + for k in range(n): + epk = (k + 1) * tol + im = k + ck = 0.0 + dem = 1.0 + s = 0.0 + lo_m = 0.0 + hi_m = 0.0 + for i in range(k, n): + if cho[i, i] > tol: + ci = np.sqrt(cho[i, i]) + if i > 0: + s = cho[i, :k] @ y[:k] + lo_i = (new_lo[i] - s) / ci + hi_i = (new_hi[i] - s) / ci + de = phi(hi_i) - phi(lo_i) + if de <= dem: + ck = ci + dem = de + lo_m = lo_i + hi_m = hi_i + im = i + if im > k: + # Swap im and k + cho[im, im] = cho[k, k] + _swap_slices(cho, np.s_[im, :k], np.s_[k, :k]) + _swap_slices(cho, np.s_[im + 1:, im], np.s_[im + 1:, k]) + _swap_slices(cho, np.s_[k + 1:im, k], np.s_[im, k + 1:im]) + _swap_slices(new_lo, k, im) + _swap_slices(new_hi, k, im) + if ck > epk: + cho[k, k] = ck + cho[k, k + 1:] = 0.0 + for i in range(k + 1, n): + cho[i, k] /= ck + cho[i, k + 1:i + 1] -= cho[i, k] * cho[k + 1:i + 1, k] + if abs(dem) > tol: + y[k] = ((np.exp(-lo_m * lo_m / 2) - np.exp(-hi_m * hi_m / 2)) / + (sqtp * dem)) + else: + y[k] = (lo_m + hi_m) / 2 + if lo_m < -10: + y[k] = hi_m + elif hi_m > 10: + y[k] = lo_m + cho[k, :k + 1] /= ck + new_lo[k] /= ck + new_hi[k] /= ck + else: + cho[k:, k] = 0.0 + y[k] = (new_lo[k] + new_hi[k]) / 2 + return cho, new_lo, new_hi + + +def _swap_slices(x, slc1, slc2): + t = x[slc1].copy() + x[slc1] = x[slc2].copy() + x[slc2] = t diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rcont/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rcont/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..25728ead5f7ea277e6e94c359d5a5603b99eeb38 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rcont/__init__.py @@ -0,0 +1,4 @@ +# +from .rcont import rvs_rcont1, rvs_rcont2 + +__all__ = ["rvs_rcont1", "rvs_rcont2"] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_relative_risk.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_relative_risk.py new file mode 100644 index 0000000000000000000000000000000000000000..51525fd28adb37c72b12106450e4178c786091b2 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_relative_risk.py @@ -0,0 +1,263 @@ +import operator +from dataclasses import dataclass +import numpy as np +from scipy.special import ndtri +from ._common import ConfidenceInterval + + +def _validate_int(n, bound, name): + msg = f'{name} must be an integer not less than {bound}, but got {n!r}' + try: + n = operator.index(n) + except TypeError: + raise TypeError(msg) from None + if n < bound: + raise ValueError(msg) + return n + + +@dataclass +class RelativeRiskResult: + """ + Result of `scipy.stats.contingency.relative_risk`. + + Attributes + ---------- + relative_risk : float + This is:: + + (exposed_cases/exposed_total) / (control_cases/control_total) + + exposed_cases : int + The number of "cases" (i.e. occurrence of disease or other event + of interest) among the sample of "exposed" individuals. + exposed_total : int + The total number of "exposed" individuals in the sample. + control_cases : int + The number of "cases" among the sample of "control" or non-exposed + individuals. + control_total : int + The total number of "control" individuals in the sample. + + Methods + ------- + confidence_interval : + Compute the confidence interval for the relative risk estimate. + """ + + relative_risk: float + exposed_cases: int + exposed_total: int + control_cases: int + control_total: int + + def confidence_interval(self, confidence_level=0.95): + """ + Compute the confidence interval for the relative risk. + + The confidence interval is computed using the Katz method + (i.e. "Method C" of [1]_; see also [2]_, section 3.1.2). + + Parameters + ---------- + confidence_level : float, optional + The confidence level to use for the confidence interval. + Default is 0.95. + + Returns + ------- + ci : ConfidenceInterval instance + The return value is an object with attributes ``low`` and + ``high`` that hold the confidence interval. + + References + ---------- + .. [1] D. Katz, J. Baptista, S. P. Azen and M. C. Pike, "Obtaining + confidence intervals for the risk ratio in cohort studies", + Biometrics, 34, 469-474 (1978). + .. [2] Hardeo Sahai and Anwer Khurshid, Statistics in Epidemiology, + CRC Press LLC, Boca Raton, FL, USA (1996). + + + Examples + -------- + >>> from scipy.stats.contingency import relative_risk + >>> result = relative_risk(exposed_cases=10, exposed_total=75, + ... control_cases=12, control_total=225) + >>> result.relative_risk + 2.5 + >>> result.confidence_interval() + ConfidenceInterval(low=1.1261564003469628, high=5.549850800541033) + """ + if not 0 <= confidence_level <= 1: + raise ValueError('confidence_level must be in the interval ' + '[0, 1].') + + # Handle edge cases where either exposed_cases or control_cases + # is zero. We follow the convention of the R function riskratio + # from the epitools library. + if self.exposed_cases == 0 and self.control_cases == 0: + # relative risk is nan. + return ConfidenceInterval(low=np.nan, high=np.nan) + elif self.exposed_cases == 0: + # relative risk is 0. + return ConfidenceInterval(low=0.0, high=np.nan) + elif self.control_cases == 0: + # relative risk is inf + return ConfidenceInterval(low=np.nan, high=np.inf) + + alpha = 1 - confidence_level + z = ndtri(1 - alpha/2) + rr = self.relative_risk + + # Estimate of the variance of log(rr) is + # var(log(rr)) = 1/exposed_cases - 1/exposed_total + + # 1/control_cases - 1/control_total + # and the standard error is the square root of that. + se = np.sqrt(1/self.exposed_cases - 1/self.exposed_total + + 1/self.control_cases - 1/self.control_total) + delta = z*se + katz_lo = rr*np.exp(-delta) + katz_hi = rr*np.exp(delta) + return ConfidenceInterval(low=katz_lo, high=katz_hi) + + +def relative_risk(exposed_cases, exposed_total, control_cases, control_total): + """ + Compute the relative risk (also known as the risk ratio). + + This function computes the relative risk associated with a 2x2 + contingency table ([1]_, section 2.2.3; [2]_, section 3.1.2). Instead + of accepting a table as an argument, the individual numbers that are + used to compute the relative risk are given as separate parameters. + This is to avoid the ambiguity of which row or column of the contingency + table corresponds to the "exposed" cases and which corresponds to the + "control" cases. Unlike, say, the odds ratio, the relative risk is not + invariant under an interchange of the rows or columns. + + Parameters + ---------- + exposed_cases : nonnegative int + The number of "cases" (i.e. occurrence of disease or other event + of interest) among the sample of "exposed" individuals. + exposed_total : positive int + The total number of "exposed" individuals in the sample. + control_cases : nonnegative int + The number of "cases" among the sample of "control" or non-exposed + individuals. + control_total : positive int + The total number of "control" individuals in the sample. + + Returns + ------- + result : instance of `~scipy.stats._result_classes.RelativeRiskResult` + The object has the float attribute ``relative_risk``, which is:: + + rr = (exposed_cases/exposed_total) / (control_cases/control_total) + + The object also has the method ``confidence_interval`` to compute + the confidence interval of the relative risk for a given confidence + level. + + See Also + -------- + odds_ratio + + Notes + ----- + The R package epitools has the function `riskratio`, which accepts + a table with the following layout:: + + disease=0 disease=1 + exposed=0 (ref) n00 n01 + exposed=1 n10 n11 + + With a 2x2 table in the above format, the estimate of the CI is + computed by `riskratio` when the argument method="wald" is given, + or with the function `riskratio.wald`. + + For example, in a test of the incidence of lung cancer among a + sample of smokers and nonsmokers, the "exposed" category would + correspond to "is a smoker" and the "disease" category would + correspond to "has or had lung cancer". + + To pass the same data to ``relative_risk``, use:: + + relative_risk(n11, n10 + n11, n01, n00 + n01) + + .. versionadded:: 1.7.0 + + References + ---------- + .. [1] Alan Agresti, An Introduction to Categorical Data Analysis + (second edition), Wiley, Hoboken, NJ, USA (2007). + .. [2] Hardeo Sahai and Anwer Khurshid, Statistics in Epidemiology, + CRC Press LLC, Boca Raton, FL, USA (1996). + + Examples + -------- + >>> from scipy.stats.contingency import relative_risk + + This example is from Example 3.1 of [2]_. The results of a heart + disease study are summarized in the following table:: + + High CAT Low CAT Total + -------- ------- ----- + CHD 27 44 71 + No CHD 95 443 538 + + Total 122 487 609 + + CHD is coronary heart disease, and CAT refers to the level of + circulating catecholamine. CAT is the "exposure" variable, and + high CAT is the "exposed" category. So the data from the table + to be passed to ``relative_risk`` is:: + + exposed_cases = 27 + exposed_total = 122 + control_cases = 44 + control_total = 487 + + >>> result = relative_risk(27, 122, 44, 487) + >>> result.relative_risk + 2.4495156482861398 + + Find the confidence interval for the relative risk. + + >>> result.confidence_interval(confidence_level=0.95) + ConfidenceInterval(low=1.5836990926700116, high=3.7886786315466354) + + The interval does not contain 1, so the data supports the statement + that high CAT is associated with greater risk of CHD. + """ + # Relative risk is a trivial calculation. The nontrivial part is in the + # `confidence_interval` method of the RelativeRiskResult class. + + exposed_cases = _validate_int(exposed_cases, 0, "exposed_cases") + exposed_total = _validate_int(exposed_total, 1, "exposed_total") + control_cases = _validate_int(control_cases, 0, "control_cases") + control_total = _validate_int(control_total, 1, "control_total") + + if exposed_cases > exposed_total: + raise ValueError('exposed_cases must not exceed exposed_total.') + if control_cases > control_total: + raise ValueError('control_cases must not exceed control_total.') + + if exposed_cases == 0 and control_cases == 0: + # relative risk is 0/0. + rr = np.nan + elif exposed_cases == 0: + # relative risk is 0/nonzero + rr = 0.0 + elif control_cases == 0: + # relative risk is nonzero/0. + rr = np.inf + else: + p1 = exposed_cases / exposed_total + p2 = control_cases / control_total + rr = p1 / p2 + return RelativeRiskResult(relative_risk=rr, + exposed_cases=exposed_cases, + exposed_total=exposed_total, + control_cases=control_cases, + control_total=control_total) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_resampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_resampling.py new file mode 100644 index 0000000000000000000000000000000000000000..2e8184ea1686abbcf79f39cc1314b060e2bb5ed9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_resampling.py @@ -0,0 +1,1870 @@ +from __future__ import annotations + +import warnings +import numpy as np +from itertools import combinations, permutations, product +from collections.abc import Sequence +import inspect + +from scipy._lib._util import check_random_state, _rename_parameter +from scipy.special import ndtr, ndtri, comb, factorial +from scipy._lib._util import rng_integers +from dataclasses import dataclass +from ._common import ConfidenceInterval +from ._axis_nan_policy import _broadcast_concatenate, _broadcast_arrays +from ._warnings_errors import DegenerateDataWarning + +__all__ = ['bootstrap', 'monte_carlo_test', 'permutation_test'] + + +def _vectorize_statistic(statistic): + """Vectorize an n-sample statistic""" + # This is a little cleaner than np.nditer at the expense of some data + # copying: concatenate samples together, then use np.apply_along_axis + def stat_nd(*data, axis=0): + lengths = [sample.shape[axis] for sample in data] + split_indices = np.cumsum(lengths)[:-1] + z = _broadcast_concatenate(data, axis) + + # move working axis to position 0 so that new dimensions in the output + # of `statistic` are _prepended_. ("This axis is removed, and replaced + # with new dimensions...") + z = np.moveaxis(z, axis, 0) + + def stat_1d(z): + data = np.split(z, split_indices) + return statistic(*data) + + return np.apply_along_axis(stat_1d, 0, z)[()] + return stat_nd + + +def _jackknife_resample(sample, batch=None): + """Jackknife resample the sample. Only one-sample stats for now.""" + n = sample.shape[-1] + batch_nominal = batch or n + + for k in range(0, n, batch_nominal): + # col_start:col_end are the observations to remove + batch_actual = min(batch_nominal, n-k) + + # jackknife - each row leaves out one observation + j = np.ones((batch_actual, n), dtype=bool) + np.fill_diagonal(j[:, k:k+batch_actual], False) + i = np.arange(n) + i = np.broadcast_to(i, (batch_actual, n)) + i = i[j].reshape((batch_actual, n-1)) + + resamples = sample[..., i] + yield resamples + + +def _bootstrap_resample(sample, n_resamples=None, random_state=None): + """Bootstrap resample the sample.""" + n = sample.shape[-1] + + # bootstrap - each row is a random resample of original observations + i = rng_integers(random_state, 0, n, (n_resamples, n)) + + resamples = sample[..., i] + return resamples + + +def _percentile_of_score(a, score, axis): + """Vectorized, simplified `scipy.stats.percentileofscore`. + Uses logic of the 'mean' value of percentileofscore's kind parameter. + + Unlike `stats.percentileofscore`, the percentile returned is a fraction + in [0, 1]. + """ + B = a.shape[axis] + return ((a < score).sum(axis=axis) + (a <= score).sum(axis=axis)) / (2 * B) + + +def _percentile_along_axis(theta_hat_b, alpha): + """`np.percentile` with different percentile for each slice.""" + # the difference between _percentile_along_axis and np.percentile is that + # np.percentile gets _all_ the qs for each axis slice, whereas + # _percentile_along_axis gets the q corresponding with each axis slice + shape = theta_hat_b.shape[:-1] + alpha = np.broadcast_to(alpha, shape) + percentiles = np.zeros_like(alpha, dtype=np.float64) + for indices, alpha_i in np.ndenumerate(alpha): + if np.isnan(alpha_i): + # e.g. when bootstrap distribution has only one unique element + msg = ( + "The BCa confidence interval cannot be calculated." + " This problem is known to occur when the distribution" + " is degenerate or the statistic is np.min." + ) + warnings.warn(DegenerateDataWarning(msg), stacklevel=3) + percentiles[indices] = np.nan + else: + theta_hat_b_i = theta_hat_b[indices] + percentiles[indices] = np.percentile(theta_hat_b_i, alpha_i) + return percentiles[()] # return scalar instead of 0d array + + +def _bca_interval(data, statistic, axis, alpha, theta_hat_b, batch): + """Bias-corrected and accelerated interval.""" + # closely follows [1] 14.3 and 15.4 (Eq. 15.36) + + # calculate z0_hat + theta_hat = np.asarray(statistic(*data, axis=axis))[..., None] + percentile = _percentile_of_score(theta_hat_b, theta_hat, axis=-1) + z0_hat = ndtri(percentile) + + # calculate a_hat + theta_hat_ji = [] # j is for sample of data, i is for jackknife resample + for j, sample in enumerate(data): + # _jackknife_resample will add an axis prior to the last axis that + # corresponds with the different jackknife resamples. Do the same for + # each sample of the data to ensure broadcastability. We need to + # create a copy of the list containing the samples anyway, so do this + # in the loop to simplify the code. This is not the bottleneck... + samples = [np.expand_dims(sample, -2) for sample in data] + theta_hat_i = [] + for jackknife_sample in _jackknife_resample(sample, batch): + samples[j] = jackknife_sample + broadcasted = _broadcast_arrays(samples, axis=-1) + theta_hat_i.append(statistic(*broadcasted, axis=-1)) + theta_hat_ji.append(theta_hat_i) + + theta_hat_ji = [np.concatenate(theta_hat_i, axis=-1) + for theta_hat_i in theta_hat_ji] + + n_j = [theta_hat_i.shape[-1] for theta_hat_i in theta_hat_ji] + + theta_hat_j_dot = [theta_hat_i.mean(axis=-1, keepdims=True) + for theta_hat_i in theta_hat_ji] + + U_ji = [(n - 1) * (theta_hat_dot - theta_hat_i) + for theta_hat_dot, theta_hat_i, n + in zip(theta_hat_j_dot, theta_hat_ji, n_j)] + + nums = [(U_i**3).sum(axis=-1)/n**3 for U_i, n in zip(U_ji, n_j)] + dens = [(U_i**2).sum(axis=-1)/n**2 for U_i, n in zip(U_ji, n_j)] + a_hat = 1/6 * sum(nums) / sum(dens)**(3/2) + + # calculate alpha_1, alpha_2 + z_alpha = ndtri(alpha) + z_1alpha = -z_alpha + num1 = z0_hat + z_alpha + alpha_1 = ndtr(z0_hat + num1/(1 - a_hat*num1)) + num2 = z0_hat + z_1alpha + alpha_2 = ndtr(z0_hat + num2/(1 - a_hat*num2)) + return alpha_1, alpha_2, a_hat # return a_hat for testing + + +def _bootstrap_iv(data, statistic, vectorized, paired, axis, confidence_level, + alternative, n_resamples, batch, method, bootstrap_result, + random_state): + """Input validation and standardization for `bootstrap`.""" + + if vectorized not in {True, False, None}: + raise ValueError("`vectorized` must be `True`, `False`, or `None`.") + + if vectorized is None: + vectorized = 'axis' in inspect.signature(statistic).parameters + + if not vectorized: + statistic = _vectorize_statistic(statistic) + + axis_int = int(axis) + if axis != axis_int: + raise ValueError("`axis` must be an integer.") + + n_samples = 0 + try: + n_samples = len(data) + except TypeError: + raise ValueError("`data` must be a sequence of samples.") + + if n_samples == 0: + raise ValueError("`data` must contain at least one sample.") + + data_iv = [] + for sample in data: + sample = np.atleast_1d(sample) + if sample.shape[axis_int] <= 1: + raise ValueError("each sample in `data` must contain two or more " + "observations along `axis`.") + sample = np.moveaxis(sample, axis_int, -1) + data_iv.append(sample) + + if paired not in {True, False}: + raise ValueError("`paired` must be `True` or `False`.") + + if paired: + n = data_iv[0].shape[-1] + for sample in data_iv[1:]: + if sample.shape[-1] != n: + message = ("When `paired is True`, all samples must have the " + "same length along `axis`") + raise ValueError(message) + + # to generate the bootstrap distribution for paired-sample statistics, + # resample the indices of the observations + def statistic(i, axis=-1, data=data_iv, unpaired_statistic=statistic): + data = [sample[..., i] for sample in data] + return unpaired_statistic(*data, axis=axis) + + data_iv = [np.arange(n)] + + confidence_level_float = float(confidence_level) + + alternative = alternative.lower() + alternatives = {'two-sided', 'less', 'greater'} + if alternative not in alternatives: + raise ValueError(f"`alternative` must be one of {alternatives}") + + n_resamples_int = int(n_resamples) + if n_resamples != n_resamples_int or n_resamples_int < 0: + raise ValueError("`n_resamples` must be a non-negative integer.") + + if batch is None: + batch_iv = batch + else: + batch_iv = int(batch) + if batch != batch_iv or batch_iv <= 0: + raise ValueError("`batch` must be a positive integer or None.") + + methods = {'percentile', 'basic', 'bca'} + method = method.lower() + if method not in methods: + raise ValueError(f"`method` must be in {methods}") + + message = "`bootstrap_result` must have attribute `bootstrap_distribution'" + if (bootstrap_result is not None + and not hasattr(bootstrap_result, "bootstrap_distribution")): + raise ValueError(message) + + message = ("Either `bootstrap_result.bootstrap_distribution.size` or " + "`n_resamples` must be positive.") + if ((not bootstrap_result or + not bootstrap_result.bootstrap_distribution.size) + and n_resamples_int == 0): + raise ValueError(message) + + random_state = check_random_state(random_state) + + return (data_iv, statistic, vectorized, paired, axis_int, + confidence_level_float, alternative, n_resamples_int, batch_iv, + method, bootstrap_result, random_state) + + +@dataclass +class BootstrapResult: + """Result object returned by `scipy.stats.bootstrap`. + + Attributes + ---------- + confidence_interval : ConfidenceInterval + The bootstrap confidence interval as an instance of + `collections.namedtuple` with attributes `low` and `high`. + bootstrap_distribution : ndarray + The bootstrap distribution, that is, the value of `statistic` for + each resample. The last dimension corresponds with the resamples + (e.g. ``res.bootstrap_distribution.shape[-1] == n_resamples``). + standard_error : float or ndarray + The bootstrap standard error, that is, the sample standard + deviation of the bootstrap distribution. + + """ + confidence_interval: ConfidenceInterval + bootstrap_distribution: np.ndarray + standard_error: float | np.ndarray + + +def bootstrap(data, statistic, *, n_resamples=9999, batch=None, + vectorized=None, paired=False, axis=0, confidence_level=0.95, + alternative='two-sided', method='BCa', bootstrap_result=None, + random_state=None): + r""" + Compute a two-sided bootstrap confidence interval of a statistic. + + When `method` is ``'percentile'`` and `alternative` is ``'two-sided'``, + a bootstrap confidence interval is computed according to the following + procedure. + + 1. Resample the data: for each sample in `data` and for each of + `n_resamples`, take a random sample of the original sample + (with replacement) of the same size as the original sample. + + 2. Compute the bootstrap distribution of the statistic: for each set of + resamples, compute the test statistic. + + 3. Determine the confidence interval: find the interval of the bootstrap + distribution that is + + - symmetric about the median and + - contains `confidence_level` of the resampled statistic values. + + While the ``'percentile'`` method is the most intuitive, it is rarely + used in practice. Two more common methods are available, ``'basic'`` + ('reverse percentile') and ``'BCa'`` ('bias-corrected and accelerated'); + they differ in how step 3 is performed. + + If the samples in `data` are taken at random from their respective + distributions :math:`n` times, the confidence interval returned by + `bootstrap` will contain the true value of the statistic for those + distributions approximately `confidence_level`:math:`\, \times \, n` times. + + Parameters + ---------- + data : sequence of array-like + Each element of data is a sample from an underlying distribution. + statistic : callable + Statistic for which the confidence interval is to be calculated. + `statistic` must be a callable that accepts ``len(data)`` samples + as separate arguments and returns the resulting statistic. + If `vectorized` is set ``True``, + `statistic` must also accept a keyword argument `axis` and be + vectorized to compute the statistic along the provided `axis`. + n_resamples : int, default: ``9999`` + The number of resamples performed to form the bootstrap distribution + of the statistic. + batch : int, optional + The number of resamples to process in each vectorized call to + `statistic`. Memory usage is O( `batch` * ``n`` ), where ``n`` is the + sample size. Default is ``None``, in which case ``batch = n_resamples`` + (or ``batch = max(n_resamples, n)`` for ``method='BCa'``). + vectorized : bool, optional + If `vectorized` is set ``False``, `statistic` will not be passed + keyword argument `axis` and is expected to calculate the statistic + only for 1D samples. If ``True``, `statistic` will be passed keyword + argument `axis` and is expected to calculate the statistic along `axis` + when passed an ND sample array. If ``None`` (default), `vectorized` + will be set ``True`` if ``axis`` is a parameter of `statistic`. Use of + a vectorized statistic typically reduces computation time. + paired : bool, default: ``False`` + Whether the statistic treats corresponding elements of the samples + in `data` as paired. + axis : int, default: ``0`` + The axis of the samples in `data` along which the `statistic` is + calculated. + confidence_level : float, default: ``0.95`` + The confidence level of the confidence interval. + alternative : {'two-sided', 'less', 'greater'}, default: ``'two-sided'`` + Choose ``'two-sided'`` (default) for a two-sided confidence interval, + ``'less'`` for a one-sided confidence interval with the lower bound + at ``-np.inf``, and ``'greater'`` for a one-sided confidence interval + with the upper bound at ``np.inf``. The other bound of the one-sided + confidence intervals is the same as that of a two-sided confidence + interval with `confidence_level` twice as far from 1.0; e.g. the upper + bound of a 95% ``'less'`` confidence interval is the same as the upper + bound of a 90% ``'two-sided'`` confidence interval. + method : {'percentile', 'basic', 'bca'}, default: ``'BCa'`` + Whether to return the 'percentile' bootstrap confidence interval + (``'percentile'``), the 'basic' (AKA 'reverse') bootstrap confidence + interval (``'basic'``), or the bias-corrected and accelerated bootstrap + confidence interval (``'BCa'``). + bootstrap_result : BootstrapResult, optional + Provide the result object returned by a previous call to `bootstrap` + to include the previous bootstrap distribution in the new bootstrap + distribution. This can be used, for example, to change + `confidence_level`, change `method`, or see the effect of performing + additional resampling without repeating computations. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + res : BootstrapResult + An object with attributes: + + confidence_interval : ConfidenceInterval + The bootstrap confidence interval as an instance of + `collections.namedtuple` with attributes `low` and `high`. + bootstrap_distribution : ndarray + The bootstrap distribution, that is, the value of `statistic` for + each resample. The last dimension corresponds with the resamples + (e.g. ``res.bootstrap_distribution.shape[-1] == n_resamples``). + standard_error : float or ndarray + The bootstrap standard error, that is, the sample standard + deviation of the bootstrap distribution. + + Warns + ----- + `~scipy.stats.DegenerateDataWarning` + Generated when ``method='BCa'`` and the bootstrap distribution is + degenerate (e.g. all elements are identical). + + Notes + ----- + Elements of the confidence interval may be NaN for ``method='BCa'`` if + the bootstrap distribution is degenerate (e.g. all elements are identical). + In this case, consider using another `method` or inspecting `data` for + indications that other analysis may be more appropriate (e.g. all + observations are identical). + + References + ---------- + .. [1] B. Efron and R. J. Tibshirani, An Introduction to the Bootstrap, + Chapman & Hall/CRC, Boca Raton, FL, USA (1993) + .. [2] Nathaniel E. Helwig, "Bootstrap Confidence Intervals", + http://users.stat.umn.edu/~helwig/notes/bootci-Notes.pdf + .. [3] Bootstrapping (statistics), Wikipedia, + https://en.wikipedia.org/wiki/Bootstrapping_%28statistics%29 + + Examples + -------- + Suppose we have sampled data from an unknown distribution. + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> from scipy.stats import norm + >>> dist = norm(loc=2, scale=4) # our "unknown" distribution + >>> data = dist.rvs(size=100, random_state=rng) + + We are interested in the standard deviation of the distribution. + + >>> std_true = dist.std() # the true value of the statistic + >>> print(std_true) + 4.0 + >>> std_sample = np.std(data) # the sample statistic + >>> print(std_sample) + 3.9460644295563863 + + The bootstrap is used to approximate the variability we would expect if we + were to repeatedly sample from the unknown distribution and calculate the + statistic of the sample each time. It does this by repeatedly resampling + values *from the original sample* with replacement and calculating the + statistic of each resample. This results in a "bootstrap distribution" of + the statistic. + + >>> import matplotlib.pyplot as plt + >>> from scipy.stats import bootstrap + >>> data = (data,) # samples must be in a sequence + >>> res = bootstrap(data, np.std, confidence_level=0.9, + ... random_state=rng) + >>> fig, ax = plt.subplots() + >>> ax.hist(res.bootstrap_distribution, bins=25) + >>> ax.set_title('Bootstrap Distribution') + >>> ax.set_xlabel('statistic value') + >>> ax.set_ylabel('frequency') + >>> plt.show() + + The standard error quantifies this variability. It is calculated as the + standard deviation of the bootstrap distribution. + + >>> res.standard_error + 0.24427002125829136 + >>> res.standard_error == np.std(res.bootstrap_distribution, ddof=1) + True + + The bootstrap distribution of the statistic is often approximately normal + with scale equal to the standard error. + + >>> x = np.linspace(3, 5) + >>> pdf = norm.pdf(x, loc=std_sample, scale=res.standard_error) + >>> fig, ax = plt.subplots() + >>> ax.hist(res.bootstrap_distribution, bins=25, density=True) + >>> ax.plot(x, pdf) + >>> ax.set_title('Normal Approximation of the Bootstrap Distribution') + >>> ax.set_xlabel('statistic value') + >>> ax.set_ylabel('pdf') + >>> plt.show() + + This suggests that we could construct a 90% confidence interval on the + statistic based on quantiles of this normal distribution. + + >>> norm.interval(0.9, loc=std_sample, scale=res.standard_error) + (3.5442759991341726, 4.3478528599786) + + Due to central limit theorem, this normal approximation is accurate for a + variety of statistics and distributions underlying the samples; however, + the approximation is not reliable in all cases. Because `bootstrap` is + designed to work with arbitrary underlying distributions and statistics, + it uses more advanced techniques to generate an accurate confidence + interval. + + >>> print(res.confidence_interval) + ConfidenceInterval(low=3.57655333533867, high=4.382043696342881) + + If we sample from the original distribution 1000 times and form a bootstrap + confidence interval for each sample, the confidence interval + contains the true value of the statistic approximately 90% of the time. + + >>> n_trials = 1000 + >>> ci_contains_true_std = 0 + >>> for i in range(n_trials): + ... data = (dist.rvs(size=100, random_state=rng),) + ... ci = bootstrap(data, np.std, confidence_level=0.9, n_resamples=1000, + ... random_state=rng).confidence_interval + ... if ci[0] < std_true < ci[1]: + ... ci_contains_true_std += 1 + >>> print(ci_contains_true_std) + 875 + + Rather than writing a loop, we can also determine the confidence intervals + for all 1000 samples at once. + + >>> data = (dist.rvs(size=(n_trials, 100), random_state=rng),) + >>> res = bootstrap(data, np.std, axis=-1, confidence_level=0.9, + ... n_resamples=1000, random_state=rng) + >>> ci_l, ci_u = res.confidence_interval + + Here, `ci_l` and `ci_u` contain the confidence interval for each of the + ``n_trials = 1000`` samples. + + >>> print(ci_l[995:]) + [3.77729695 3.75090233 3.45829131 3.34078217 3.48072829] + >>> print(ci_u[995:]) + [4.88316666 4.86924034 4.32032996 4.2822427 4.59360598] + + And again, approximately 90% contain the true value, ``std_true = 4``. + + >>> print(np.sum((ci_l < std_true) & (std_true < ci_u))) + 900 + + `bootstrap` can also be used to estimate confidence intervals of + multi-sample statistics, including those calculated by hypothesis + tests. `scipy.stats.mood` perform's Mood's test for equal scale parameters, + and it returns two outputs: a statistic, and a p-value. To get a + confidence interval for the test statistic, we first wrap + `scipy.stats.mood` in a function that accepts two sample arguments, + accepts an `axis` keyword argument, and returns only the statistic. + + >>> from scipy.stats import mood + >>> def my_statistic(sample1, sample2, axis): + ... statistic, _ = mood(sample1, sample2, axis=-1) + ... return statistic + + Here, we use the 'percentile' method with the default 95% confidence level. + + >>> sample1 = norm.rvs(scale=1, size=100, random_state=rng) + >>> sample2 = norm.rvs(scale=2, size=100, random_state=rng) + >>> data = (sample1, sample2) + >>> res = bootstrap(data, my_statistic, method='basic', random_state=rng) + >>> print(mood(sample1, sample2)[0]) # element 0 is the statistic + -5.521109549096542 + >>> print(res.confidence_interval) + ConfidenceInterval(low=-7.255994487314675, high=-4.016202624747605) + + The bootstrap estimate of the standard error is also available. + + >>> print(res.standard_error) + 0.8344963846318795 + + Paired-sample statistics work, too. For example, consider the Pearson + correlation coefficient. + + >>> from scipy.stats import pearsonr + >>> n = 100 + >>> x = np.linspace(0, 10, n) + >>> y = x + rng.uniform(size=n) + >>> print(pearsonr(x, y)[0]) # element 0 is the statistic + 0.9962357936065914 + + We wrap `pearsonr` so that it returns only the statistic. + + >>> def my_statistic(x, y): + ... return pearsonr(x, y)[0] + + We call `bootstrap` using ``paired=True``. + Also, since ``my_statistic`` isn't vectorized to calculate the statistic + along a given axis, we pass in ``vectorized=False``. + + >>> res = bootstrap((x, y), my_statistic, vectorized=False, paired=True, + ... random_state=rng) + >>> print(res.confidence_interval) + ConfidenceInterval(low=0.9950085825848624, high=0.9971212407917498) + + The result object can be passed back into `bootstrap` to perform additional + resampling: + + >>> len(res.bootstrap_distribution) + 9999 + >>> res = bootstrap((x, y), my_statistic, vectorized=False, paired=True, + ... n_resamples=1001, random_state=rng, + ... bootstrap_result=res) + >>> len(res.bootstrap_distribution) + 11000 + + or to change the confidence interval options: + + >>> res2 = bootstrap((x, y), my_statistic, vectorized=False, paired=True, + ... n_resamples=0, random_state=rng, bootstrap_result=res, + ... method='percentile', confidence_level=0.9) + >>> np.testing.assert_equal(res2.bootstrap_distribution, + ... res.bootstrap_distribution) + >>> res.confidence_interval + ConfidenceInterval(low=0.9950035351407804, high=0.9971170323404578) + + without repeating computation of the original bootstrap distribution. + + """ + # Input validation + args = _bootstrap_iv(data, statistic, vectorized, paired, axis, + confidence_level, alternative, n_resamples, batch, + method, bootstrap_result, random_state) + (data, statistic, vectorized, paired, axis, confidence_level, + alternative, n_resamples, batch, method, bootstrap_result, + random_state) = args + + theta_hat_b = ([] if bootstrap_result is None + else [bootstrap_result.bootstrap_distribution]) + + batch_nominal = batch or n_resamples or 1 + + for k in range(0, n_resamples, batch_nominal): + batch_actual = min(batch_nominal, n_resamples-k) + # Generate resamples + resampled_data = [] + for sample in data: + resample = _bootstrap_resample(sample, n_resamples=batch_actual, + random_state=random_state) + resampled_data.append(resample) + + # Compute bootstrap distribution of statistic + theta_hat_b.append(statistic(*resampled_data, axis=-1)) + theta_hat_b = np.concatenate(theta_hat_b, axis=-1) + + # Calculate percentile interval + alpha = ((1 - confidence_level)/2 if alternative == 'two-sided' + else (1 - confidence_level)) + if method == 'bca': + interval = _bca_interval(data, statistic, axis=-1, alpha=alpha, + theta_hat_b=theta_hat_b, batch=batch)[:2] + percentile_fun = _percentile_along_axis + else: + interval = alpha, 1-alpha + + def percentile_fun(a, q): + return np.percentile(a=a, q=q, axis=-1) + + # Calculate confidence interval of statistic + ci_l = percentile_fun(theta_hat_b, interval[0]*100) + ci_u = percentile_fun(theta_hat_b, interval[1]*100) + if method == 'basic': # see [3] + theta_hat = statistic(*data, axis=-1) + ci_l, ci_u = 2*theta_hat - ci_u, 2*theta_hat - ci_l + + if alternative == 'less': + ci_l = np.full_like(ci_l, -np.inf) + elif alternative == 'greater': + ci_u = np.full_like(ci_u, np.inf) + + return BootstrapResult(confidence_interval=ConfidenceInterval(ci_l, ci_u), + bootstrap_distribution=theta_hat_b, + standard_error=np.std(theta_hat_b, ddof=1, axis=-1)) + + +def _monte_carlo_test_iv(data, rvs, statistic, vectorized, n_resamples, + batch, alternative, axis): + """Input validation for `monte_carlo_test`.""" + + axis_int = int(axis) + if axis != axis_int: + raise ValueError("`axis` must be an integer.") + + if vectorized not in {True, False, None}: + raise ValueError("`vectorized` must be `True`, `False`, or `None`.") + + if not isinstance(rvs, Sequence): + rvs = (rvs,) + data = (data,) + for rvs_i in rvs: + if not callable(rvs_i): + raise TypeError("`rvs` must be callable or sequence of callables.") + + if not len(rvs) == len(data): + message = "If `rvs` is a sequence, `len(rvs)` must equal `len(data)`." + raise ValueError(message) + + if not callable(statistic): + raise TypeError("`statistic` must be callable.") + + if vectorized is None: + vectorized = 'axis' in inspect.signature(statistic).parameters + + if not vectorized: + statistic_vectorized = _vectorize_statistic(statistic) + else: + statistic_vectorized = statistic + + data = _broadcast_arrays(data, axis) + data_iv = [] + for sample in data: + sample = np.atleast_1d(sample) + sample = np.moveaxis(sample, axis_int, -1) + data_iv.append(sample) + + n_resamples_int = int(n_resamples) + if n_resamples != n_resamples_int or n_resamples_int <= 0: + raise ValueError("`n_resamples` must be a positive integer.") + + if batch is None: + batch_iv = batch + else: + batch_iv = int(batch) + if batch != batch_iv or batch_iv <= 0: + raise ValueError("`batch` must be a positive integer or None.") + + alternatives = {'two-sided', 'greater', 'less'} + alternative = alternative.lower() + if alternative not in alternatives: + raise ValueError(f"`alternative` must be in {alternatives}") + + return (data_iv, rvs, statistic_vectorized, vectorized, n_resamples_int, + batch_iv, alternative, axis_int) + + +@dataclass +class MonteCarloTestResult: + """Result object returned by `scipy.stats.monte_carlo_test`. + + Attributes + ---------- + statistic : float or ndarray + The observed test statistic of the sample. + pvalue : float or ndarray + The p-value for the given alternative. + null_distribution : ndarray + The values of the test statistic generated under the null + hypothesis. + """ + statistic: float | np.ndarray + pvalue: float | np.ndarray + null_distribution: np.ndarray + + +@_rename_parameter('sample', 'data') +def monte_carlo_test(data, rvs, statistic, *, vectorized=None, + n_resamples=9999, batch=None, alternative="two-sided", + axis=0): + r"""Perform a Monte Carlo hypothesis test. + + `data` contains a sample or a sequence of one or more samples. `rvs` + specifies the distribution(s) of the sample(s) in `data` under the null + hypothesis. The value of `statistic` for the given `data` is compared + against a Monte Carlo null distribution: the value of the statistic for + each of `n_resamples` sets of samples generated using `rvs`. This gives + the p-value, the probability of observing such an extreme value of the + test statistic under the null hypothesis. + + Parameters + ---------- + data : array-like or sequence of array-like + An array or sequence of arrays of observations. + rvs : callable or tuple of callables + A callable or sequence of callables that generates random variates + under the null hypothesis. Each element of `rvs` must be a callable + that accepts keyword argument ``size`` (e.g. ``rvs(size=(m, n))``) and + returns an N-d array sample of that shape. If `rvs` is a sequence, the + number of callables in `rvs` must match the number of samples in + `data`, i.e. ``len(rvs) == len(data)``. If `rvs` is a single callable, + `data` is treated as a single sample. + statistic : callable + Statistic for which the p-value of the hypothesis test is to be + calculated. `statistic` must be a callable that accepts a sample + (e.g. ``statistic(sample)``) or ``len(rvs)`` separate samples (e.g. + ``statistic(samples1, sample2)`` if `rvs` contains two callables and + `data` contains two samples) and returns the resulting statistic. + If `vectorized` is set ``True``, `statistic` must also accept a keyword + argument `axis` and be vectorized to compute the statistic along the + provided `axis` of the samples in `data`. + vectorized : bool, optional + If `vectorized` is set ``False``, `statistic` will not be passed + keyword argument `axis` and is expected to calculate the statistic + only for 1D samples. If ``True``, `statistic` will be passed keyword + argument `axis` and is expected to calculate the statistic along `axis` + when passed ND sample arrays. If ``None`` (default), `vectorized` + will be set ``True`` if ``axis`` is a parameter of `statistic`. Use of + a vectorized statistic typically reduces computation time. + n_resamples : int, default: 9999 + Number of samples drawn from each of the callables of `rvs`. + Equivalently, the number statistic values under the null hypothesis + used as the Monte Carlo null distribution. + batch : int, optional + The number of Monte Carlo samples to process in each call to + `statistic`. Memory usage is O( `batch` * ``sample.size[axis]`` ). Default + is ``None``, in which case `batch` equals `n_resamples`. + alternative : {'two-sided', 'less', 'greater'} + The alternative hypothesis for which the p-value is calculated. + For each alternative, the p-value is defined as follows. + + - ``'greater'`` : the percentage of the null distribution that is + greater than or equal to the observed value of the test statistic. + - ``'less'`` : the percentage of the null distribution that is + less than or equal to the observed value of the test statistic. + - ``'two-sided'`` : twice the smaller of the p-values above. + + axis : int, default: 0 + The axis of `data` (or each sample within `data`) over which to + calculate the statistic. + + Returns + ------- + res : MonteCarloTestResult + An object with attributes: + + statistic : float or ndarray + The test statistic of the observed `data`. + pvalue : float or ndarray + The p-value for the given alternative. + null_distribution : ndarray + The values of the test statistic generated under the null + hypothesis. + + .. warning:: + The p-value is calculated by counting the elements of the null + distribution that are as extreme or more extreme than the observed + value of the statistic. Due to the use of finite precision arithmetic, + some statistic functions return numerically distinct values when the + theoretical values would be exactly equal. In some cases, this could + lead to a large error in the calculated p-value. `monte_carlo_test` + guards against this by considering elements in the null distribution + that are "close" (within a relative tolerance of 100 times the + floating point epsilon of inexact dtypes) to the observed + value of the test statistic as equal to the observed value of the + test statistic. However, the user is advised to inspect the null + distribution to assess whether this method of comparison is + appropriate, and if not, calculate the p-value manually. + + References + ---------- + + .. [1] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly Drawn." + Statistical Applications in Genetics and Molecular Biology 9.1 (2010). + + Examples + -------- + + Suppose we wish to test whether a small sample has been drawn from a normal + distribution. We decide that we will use the skew of the sample as a + test statistic, and we will consider a p-value of 0.05 to be statistically + significant. + + >>> import numpy as np + >>> from scipy import stats + >>> def statistic(x, axis): + ... return stats.skew(x, axis) + + After collecting our data, we calculate the observed value of the test + statistic. + + >>> rng = np.random.default_rng() + >>> x = stats.skewnorm.rvs(a=1, size=50, random_state=rng) + >>> statistic(x, axis=0) + 0.12457412450240658 + + To determine the probability of observing such an extreme value of the + skewness by chance if the sample were drawn from the normal distribution, + we can perform a Monte Carlo hypothesis test. The test will draw many + samples at random from their normal distribution, calculate the skewness + of each sample, and compare our original skewness against this + distribution to determine an approximate p-value. + + >>> from scipy.stats import monte_carlo_test + >>> # because our statistic is vectorized, we pass `vectorized=True` + >>> rvs = lambda size: stats.norm.rvs(size=size, random_state=rng) + >>> res = monte_carlo_test(x, rvs, statistic, vectorized=True) + >>> print(res.statistic) + 0.12457412450240658 + >>> print(res.pvalue) + 0.7012 + + The probability of obtaining a test statistic less than or equal to the + observed value under the null hypothesis is ~70%. This is greater than + our chosen threshold of 5%, so we cannot consider this to be significant + evidence against the null hypothesis. + + Note that this p-value essentially matches that of + `scipy.stats.skewtest`, which relies on an asymptotic distribution of a + test statistic based on the sample skewness. + + >>> stats.skewtest(x).pvalue + 0.6892046027110614 + + This asymptotic approximation is not valid for small sample sizes, but + `monte_carlo_test` can be used with samples of any size. + + >>> x = stats.skewnorm.rvs(a=1, size=7, random_state=rng) + >>> # stats.skewtest(x) would produce an error due to small sample + >>> res = monte_carlo_test(x, rvs, statistic, vectorized=True) + + The Monte Carlo distribution of the test statistic is provided for + further investigation. + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.hist(res.null_distribution, bins=50) + >>> ax.set_title("Monte Carlo distribution of test statistic") + >>> ax.set_xlabel("Value of Statistic") + >>> ax.set_ylabel("Frequency") + >>> plt.show() + + """ + args = _monte_carlo_test_iv(data, rvs, statistic, vectorized, + n_resamples, batch, alternative, axis) + (data, rvs, statistic, vectorized, + n_resamples, batch, alternative, axis) = args + + # Some statistics return plain floats; ensure they're at least a NumPy float + observed = np.asarray(statistic(*data, axis=-1))[()] + + n_observations = [sample.shape[-1] for sample in data] + batch_nominal = batch or n_resamples + null_distribution = [] + for k in range(0, n_resamples, batch_nominal): + batch_actual = min(batch_nominal, n_resamples - k) + resamples = [rvs_i(size=(batch_actual, n_observations_i)) + for rvs_i, n_observations_i in zip(rvs, n_observations)] + null_distribution.append(statistic(*resamples, axis=-1)) + null_distribution = np.concatenate(null_distribution) + null_distribution = null_distribution.reshape([-1] + [1]*observed.ndim) + + # relative tolerance for detecting numerically distinct but + # theoretically equal values in the null distribution + eps = (0 if not np.issubdtype(observed.dtype, np.inexact) + else np.finfo(observed.dtype).eps*100) + gamma = np.abs(eps * observed) + + def less(null_distribution, observed): + cmps = null_distribution <= observed + gamma + pvalues = (cmps.sum(axis=0) + 1) / (n_resamples + 1) # see [1] + return pvalues + + def greater(null_distribution, observed): + cmps = null_distribution >= observed - gamma + pvalues = (cmps.sum(axis=0) + 1) / (n_resamples + 1) # see [1] + return pvalues + + def two_sided(null_distribution, observed): + pvalues_less = less(null_distribution, observed) + pvalues_greater = greater(null_distribution, observed) + pvalues = np.minimum(pvalues_less, pvalues_greater) * 2 + return pvalues + + compare = {"less": less, + "greater": greater, + "two-sided": two_sided} + + pvalues = compare[alternative](null_distribution, observed) + pvalues = np.clip(pvalues, 0, 1) + + return MonteCarloTestResult(observed, pvalues, null_distribution) + + +@dataclass +class PermutationTestResult: + """Result object returned by `scipy.stats.permutation_test`. + + Attributes + ---------- + statistic : float or ndarray + The observed test statistic of the data. + pvalue : float or ndarray + The p-value for the given alternative. + null_distribution : ndarray + The values of the test statistic generated under the null + hypothesis. + """ + statistic: float | np.ndarray + pvalue: float | np.ndarray + null_distribution: np.ndarray + + +def _all_partitions_concatenated(ns): + """ + Generate all partitions of indices of groups of given sizes, concatenated + + `ns` is an iterable of ints. + """ + def all_partitions(z, n): + for c in combinations(z, n): + x0 = set(c) + x1 = z - x0 + yield [x0, x1] + + def all_partitions_n(z, ns): + if len(ns) == 0: + yield [z] + return + for c in all_partitions(z, ns[0]): + for d in all_partitions_n(c[1], ns[1:]): + yield c[0:1] + d + + z = set(range(np.sum(ns))) + for partitioning in all_partitions_n(z, ns[:]): + x = np.concatenate([list(partition) + for partition in partitioning]).astype(int) + yield x + + +def _batch_generator(iterable, batch): + """A generator that yields batches of elements from an iterable""" + iterator = iter(iterable) + if batch <= 0: + raise ValueError("`batch` must be positive.") + z = [item for i, item in zip(range(batch), iterator)] + while z: # we don't want StopIteration without yielding an empty list + yield z + z = [item for i, item in zip(range(batch), iterator)] + + +def _pairings_permutations_gen(n_permutations, n_samples, n_obs_sample, batch, + random_state): + # Returns a generator that yields arrays of size + # `(batch, n_samples, n_obs_sample)`. + # Each row is an independent permutation of indices 0 to `n_obs_sample`. + batch = min(batch, n_permutations) + + if hasattr(random_state, 'permuted'): + def batched_perm_generator(): + indices = np.arange(n_obs_sample) + indices = np.tile(indices, (batch, n_samples, 1)) + for k in range(0, n_permutations, batch): + batch_actual = min(batch, n_permutations-k) + # Don't permute in place, otherwise results depend on `batch` + permuted_indices = random_state.permuted(indices, axis=-1) + yield permuted_indices[:batch_actual] + else: # RandomState and early Generators don't have `permuted` + def batched_perm_generator(): + for k in range(0, n_permutations, batch): + batch_actual = min(batch, n_permutations-k) + size = (batch_actual, n_samples, n_obs_sample) + x = random_state.random(size=size) + yield np.argsort(x, axis=-1)[:batch_actual] + + return batched_perm_generator() + + +def _calculate_null_both(data, statistic, n_permutations, batch, + random_state=None): + """ + Calculate null distribution for independent sample tests. + """ + n_samples = len(data) + + # compute number of permutations + # (distinct partitions of data into samples of these sizes) + n_obs_i = [sample.shape[-1] for sample in data] # observations per sample + n_obs_ic = np.cumsum(n_obs_i) + n_obs = n_obs_ic[-1] # total number of observations + n_max = np.prod([comb(n_obs_ic[i], n_obs_ic[i-1]) + for i in range(n_samples-1, 0, -1)]) + + # perm_generator is an iterator that produces permutations of indices + # from 0 to n_obs. We'll concatenate the samples, use these indices to + # permute the data, then split the samples apart again. + if n_permutations >= n_max: + exact_test = True + n_permutations = n_max + perm_generator = _all_partitions_concatenated(n_obs_i) + else: + exact_test = False + # Neither RandomState.permutation nor Generator.permutation + # can permute axis-slices independently. If this feature is + # added in the future, batches of the desired size should be + # generated in a single call. + perm_generator = (random_state.permutation(n_obs) + for i in range(n_permutations)) + + batch = batch or int(n_permutations) + null_distribution = [] + + # First, concatenate all the samples. In batches, permute samples with + # indices produced by the `perm_generator`, split them into new samples of + # the original sizes, compute the statistic for each batch, and add these + # statistic values to the null distribution. + data = np.concatenate(data, axis=-1) + for indices in _batch_generator(perm_generator, batch=batch): + indices = np.array(indices) + + # `indices` is 2D: each row is a permutation of the indices. + # We use it to index `data` along its last axis, which corresponds + # with observations. + # After indexing, the second to last axis of `data_batch` corresponds + # with permutations, and the last axis corresponds with observations. + data_batch = data[..., indices] + + # Move the permutation axis to the front: we'll concatenate a list + # of batched statistic values along this zeroth axis to form the + # null distribution. + data_batch = np.moveaxis(data_batch, -2, 0) + data_batch = np.split(data_batch, n_obs_ic[:-1], axis=-1) + null_distribution.append(statistic(*data_batch, axis=-1)) + null_distribution = np.concatenate(null_distribution, axis=0) + + return null_distribution, n_permutations, exact_test + + +def _calculate_null_pairings(data, statistic, n_permutations, batch, + random_state=None): + """ + Calculate null distribution for association tests. + """ + n_samples = len(data) + + # compute number of permutations (factorial(n) permutations of each sample) + n_obs_sample = data[0].shape[-1] # observations per sample; same for each + n_max = factorial(n_obs_sample)**n_samples + + # `perm_generator` is an iterator that produces a list of permutations of + # indices from 0 to n_obs_sample, one for each sample. + if n_permutations >= n_max: + exact_test = True + n_permutations = n_max + batch = batch or int(n_permutations) + # cartesian product of the sets of all permutations of indices + perm_generator = product(*(permutations(range(n_obs_sample)) + for i in range(n_samples))) + batched_perm_generator = _batch_generator(perm_generator, batch=batch) + else: + exact_test = False + batch = batch or int(n_permutations) + # Separate random permutations of indices for each sample. + # Again, it would be nice if RandomState/Generator.permutation + # could permute each axis-slice separately. + args = n_permutations, n_samples, n_obs_sample, batch, random_state + batched_perm_generator = _pairings_permutations_gen(*args) + + null_distribution = [] + + for indices in batched_perm_generator: + indices = np.array(indices) + + # `indices` is 3D: the zeroth axis is for permutations, the next is + # for samples, and the last is for observations. Swap the first two + # to make the zeroth axis correspond with samples, as it does for + # `data`. + indices = np.swapaxes(indices, 0, 1) + + # When we're done, `data_batch` will be a list of length `n_samples`. + # Each element will be a batch of random permutations of one sample. + # The zeroth axis of each batch will correspond with permutations, + # and the last will correspond with observations. (This makes it + # easy to pass into `statistic`.) + data_batch = [None]*n_samples + for i in range(n_samples): + data_batch[i] = data[i][..., indices[i]] + data_batch[i] = np.moveaxis(data_batch[i], -2, 0) + + null_distribution.append(statistic(*data_batch, axis=-1)) + null_distribution = np.concatenate(null_distribution, axis=0) + + return null_distribution, n_permutations, exact_test + + +def _calculate_null_samples(data, statistic, n_permutations, batch, + random_state=None): + """ + Calculate null distribution for paired-sample tests. + """ + n_samples = len(data) + + # By convention, the meaning of the "samples" permutations type for + # data with only one sample is to flip the sign of the observations. + # Achieve this by adding a second sample - the negative of the original. + if n_samples == 1: + data = [data[0], -data[0]] + + # The "samples" permutation strategy is the same as the "pairings" + # strategy except the roles of samples and observations are flipped. + # So swap these axes, then we'll use the function for the "pairings" + # strategy to do all the work! + data = np.swapaxes(data, 0, -1) + + # (Of course, the user's statistic doesn't know what we've done here, + # so we need to pass it what it's expecting.) + def statistic_wrapped(*data, axis): + data = np.swapaxes(data, 0, -1) + if n_samples == 1: + data = data[0:1] + return statistic(*data, axis=axis) + + return _calculate_null_pairings(data, statistic_wrapped, n_permutations, + batch, random_state) + + +def _permutation_test_iv(data, statistic, permutation_type, vectorized, + n_resamples, batch, alternative, axis, random_state): + """Input validation for `permutation_test`.""" + + axis_int = int(axis) + if axis != axis_int: + raise ValueError("`axis` must be an integer.") + + permutation_types = {'samples', 'pairings', 'independent'} + permutation_type = permutation_type.lower() + if permutation_type not in permutation_types: + raise ValueError(f"`permutation_type` must be in {permutation_types}.") + + if vectorized not in {True, False, None}: + raise ValueError("`vectorized` must be `True`, `False`, or `None`.") + + if vectorized is None: + vectorized = 'axis' in inspect.signature(statistic).parameters + + if not vectorized: + statistic = _vectorize_statistic(statistic) + + message = "`data` must be a tuple containing at least two samples" + try: + if len(data) < 2 and permutation_type == 'independent': + raise ValueError(message) + except TypeError: + raise TypeError(message) + + data = _broadcast_arrays(data, axis) + data_iv = [] + for sample in data: + sample = np.atleast_1d(sample) + if sample.shape[axis] <= 1: + raise ValueError("each sample in `data` must contain two or more " + "observations along `axis`.") + sample = np.moveaxis(sample, axis_int, -1) + data_iv.append(sample) + + n_resamples_int = (int(n_resamples) if not np.isinf(n_resamples) + else np.inf) + if n_resamples != n_resamples_int or n_resamples_int <= 0: + raise ValueError("`n_resamples` must be a positive integer.") + + if batch is None: + batch_iv = batch + else: + batch_iv = int(batch) + if batch != batch_iv or batch_iv <= 0: + raise ValueError("`batch` must be a positive integer or None.") + + alternatives = {'two-sided', 'greater', 'less'} + alternative = alternative.lower() + if alternative not in alternatives: + raise ValueError(f"`alternative` must be in {alternatives}") + + random_state = check_random_state(random_state) + + return (data_iv, statistic, permutation_type, vectorized, n_resamples_int, + batch_iv, alternative, axis_int, random_state) + + +def permutation_test(data, statistic, *, permutation_type='independent', + vectorized=None, n_resamples=9999, batch=None, + alternative="two-sided", axis=0, random_state=None): + r""" + Performs a permutation test of a given statistic on provided data. + + For independent sample statistics, the null hypothesis is that the data are + randomly sampled from the same distribution. + For paired sample statistics, two null hypothesis can be tested: + that the data are paired at random or that the data are assigned to samples + at random. + + Parameters + ---------- + data : iterable of array-like + Contains the samples, each of which is an array of observations. + Dimensions of sample arrays must be compatible for broadcasting except + along `axis`. + statistic : callable + Statistic for which the p-value of the hypothesis test is to be + calculated. `statistic` must be a callable that accepts samples + as separate arguments (e.g. ``statistic(*data)``) and returns the + resulting statistic. + If `vectorized` is set ``True``, `statistic` must also accept a keyword + argument `axis` and be vectorized to compute the statistic along the + provided `axis` of the sample arrays. + permutation_type : {'independent', 'samples', 'pairings'}, optional + The type of permutations to be performed, in accordance with the + null hypothesis. The first two permutation types are for paired sample + statistics, in which all samples contain the same number of + observations and observations with corresponding indices along `axis` + are considered to be paired; the third is for independent sample + statistics. + + - ``'samples'`` : observations are assigned to different samples + but remain paired with the same observations from other samples. + This permutation type is appropriate for paired sample hypothesis + tests such as the Wilcoxon signed-rank test and the paired t-test. + - ``'pairings'`` : observations are paired with different observations, + but they remain within the same sample. This permutation type is + appropriate for association/correlation tests with statistics such + as Spearman's :math:`\rho`, Kendall's :math:`\tau`, and Pearson's + :math:`r`. + - ``'independent'`` (default) : observations are assigned to different + samples. Samples may contain different numbers of observations. This + permutation type is appropriate for independent sample hypothesis + tests such as the Mann-Whitney :math:`U` test and the independent + sample t-test. + + Please see the Notes section below for more detailed descriptions + of the permutation types. + + vectorized : bool, optional + If `vectorized` is set ``False``, `statistic` will not be passed + keyword argument `axis` and is expected to calculate the statistic + only for 1D samples. If ``True``, `statistic` will be passed keyword + argument `axis` and is expected to calculate the statistic along `axis` + when passed an ND sample array. If ``None`` (default), `vectorized` + will be set ``True`` if ``axis`` is a parameter of `statistic`. Use + of a vectorized statistic typically reduces computation time. + n_resamples : int or np.inf, default: 9999 + Number of random permutations (resamples) used to approximate the null + distribution. If greater than or equal to the number of distinct + permutations, the exact null distribution will be computed. + Note that the number of distinct permutations grows very rapidly with + the sizes of samples, so exact tests are feasible only for very small + data sets. + batch : int, optional + The number of permutations to process in each call to `statistic`. + Memory usage is O( `batch` * ``n`` ), where ``n`` is the total size + of all samples, regardless of the value of `vectorized`. Default is + ``None``, in which case ``batch`` is the number of permutations. + alternative : {'two-sided', 'less', 'greater'}, optional + The alternative hypothesis for which the p-value is calculated. + For each alternative, the p-value is defined for exact tests as + follows. + + - ``'greater'`` : the percentage of the null distribution that is + greater than or equal to the observed value of the test statistic. + - ``'less'`` : the percentage of the null distribution that is + less than or equal to the observed value of the test statistic. + - ``'two-sided'`` (default) : twice the smaller of the p-values above. + + Note that p-values for randomized tests are calculated according to the + conservative (over-estimated) approximation suggested in [2]_ and [3]_ + rather than the unbiased estimator suggested in [4]_. That is, when + calculating the proportion of the randomized null distribution that is + as extreme as the observed value of the test statistic, the values in + the numerator and denominator are both increased by one. An + interpretation of this adjustment is that the observed value of the + test statistic is always included as an element of the randomized + null distribution. + The convention used for two-sided p-values is not universal; + the observed test statistic and null distribution are returned in + case a different definition is preferred. + + axis : int, default: 0 + The axis of the (broadcasted) samples over which to calculate the + statistic. If samples have a different number of dimensions, + singleton dimensions are prepended to samples with fewer dimensions + before `axis` is considered. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate permutations. + + If `random_state` is ``None`` (default), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + res : PermutationTestResult + An object with attributes: + + statistic : float or ndarray + The observed test statistic of the data. + pvalue : float or ndarray + The p-value for the given alternative. + null_distribution : ndarray + The values of the test statistic generated under the null + hypothesis. + + Notes + ----- + + The three types of permutation tests supported by this function are + described below. + + **Unpaired statistics** (``permutation_type='independent'``): + + The null hypothesis associated with this permutation type is that all + observations are sampled from the same underlying distribution and that + they have been assigned to one of the samples at random. + + Suppose ``data`` contains two samples; e.g. ``a, b = data``. + When ``1 < n_resamples < binom(n, k)``, where + + * ``k`` is the number of observations in ``a``, + * ``n`` is the total number of observations in ``a`` and ``b``, and + * ``binom(n, k)`` is the binomial coefficient (``n`` choose ``k``), + + the data are pooled (concatenated), randomly assigned to either the first + or second sample, and the statistic is calculated. This process is + performed repeatedly, `permutation` times, generating a distribution of the + statistic under the null hypothesis. The statistic of the original + data is compared to this distribution to determine the p-value. + + When ``n_resamples >= binom(n, k)``, an exact test is performed: the data + are *partitioned* between the samples in each distinct way exactly once, + and the exact null distribution is formed. + Note that for a given partitioning of the data between the samples, + only one ordering/permutation of the data *within* each sample is + considered. For statistics that do not depend on the order of the data + within samples, this dramatically reduces computational cost without + affecting the shape of the null distribution (because the frequency/count + of each value is affected by the same factor). + + For ``a = [a1, a2, a3, a4]`` and ``b = [b1, b2, b3]``, an example of this + permutation type is ``x = [b3, a1, a2, b2]`` and ``y = [a4, b1, a3]``. + Because only one ordering/permutation of the data *within* each sample + is considered in an exact test, a resampling like ``x = [b3, a1, b2, a2]`` + and ``y = [a4, a3, b1]`` would *not* be considered distinct from the + example above. + + ``permutation_type='independent'`` does not support one-sample statistics, + but it can be applied to statistics with more than two samples. In this + case, if ``n`` is an array of the number of observations within each + sample, the number of distinct partitions is:: + + np.prod([binom(sum(n[i:]), sum(n[i+1:])) for i in range(len(n)-1)]) + + **Paired statistics, permute pairings** (``permutation_type='pairings'``): + + The null hypothesis associated with this permutation type is that + observations within each sample are drawn from the same underlying + distribution and that pairings with elements of other samples are + assigned at random. + + Suppose ``data`` contains only one sample; e.g. ``a, = data``, and we + wish to consider all possible pairings of elements of ``a`` with elements + of a second sample, ``b``. Let ``n`` be the number of observations in + ``a``, which must also equal the number of observations in ``b``. + + When ``1 < n_resamples < factorial(n)``, the elements of ``a`` are + randomly permuted. The user-supplied statistic accepts one data argument, + say ``a_perm``, and calculates the statistic considering ``a_perm`` and + ``b``. This process is performed repeatedly, `permutation` times, + generating a distribution of the statistic under the null hypothesis. + The statistic of the original data is compared to this distribution to + determine the p-value. + + When ``n_resamples >= factorial(n)``, an exact test is performed: + ``a`` is permuted in each distinct way exactly once. Therefore, the + `statistic` is computed for each unique pairing of samples between ``a`` + and ``b`` exactly once. + + For ``a = [a1, a2, a3]`` and ``b = [b1, b2, b3]``, an example of this + permutation type is ``a_perm = [a3, a1, a2]`` while ``b`` is left + in its original order. + + ``permutation_type='pairings'`` supports ``data`` containing any number + of samples, each of which must contain the same number of observations. + All samples provided in ``data`` are permuted *independently*. Therefore, + if ``m`` is the number of samples and ``n`` is the number of observations + within each sample, then the number of permutations in an exact test is:: + + factorial(n)**m + + Note that if a two-sample statistic, for example, does not inherently + depend on the order in which observations are provided - only on the + *pairings* of observations - then only one of the two samples should be + provided in ``data``. This dramatically reduces computational cost without + affecting the shape of the null distribution (because the frequency/count + of each value is affected by the same factor). + + **Paired statistics, permute samples** (``permutation_type='samples'``): + + The null hypothesis associated with this permutation type is that + observations within each pair are drawn from the same underlying + distribution and that the sample to which they are assigned is random. + + Suppose ``data`` contains two samples; e.g. ``a, b = data``. + Let ``n`` be the number of observations in ``a``, which must also equal + the number of observations in ``b``. + + When ``1 < n_resamples < 2**n``, the elements of ``a`` are ``b`` are + randomly swapped between samples (maintaining their pairings) and the + statistic is calculated. This process is performed repeatedly, + `permutation` times, generating a distribution of the statistic under the + null hypothesis. The statistic of the original data is compared to this + distribution to determine the p-value. + + When ``n_resamples >= 2**n``, an exact test is performed: the observations + are assigned to the two samples in each distinct way (while maintaining + pairings) exactly once. + + For ``a = [a1, a2, a3]`` and ``b = [b1, b2, b3]``, an example of this + permutation type is ``x = [b1, a2, b3]`` and ``y = [a1, b2, a3]``. + + ``permutation_type='samples'`` supports ``data`` containing any number + of samples, each of which must contain the same number of observations. + If ``data`` contains more than one sample, paired observations within + ``data`` are exchanged between samples *independently*. Therefore, if ``m`` + is the number of samples and ``n`` is the number of observations within + each sample, then the number of permutations in an exact test is:: + + factorial(m)**n + + Several paired-sample statistical tests, such as the Wilcoxon signed rank + test and paired-sample t-test, can be performed considering only the + *difference* between two paired elements. Accordingly, if ``data`` contains + only one sample, then the null distribution is formed by independently + changing the *sign* of each observation. + + .. warning:: + The p-value is calculated by counting the elements of the null + distribution that are as extreme or more extreme than the observed + value of the statistic. Due to the use of finite precision arithmetic, + some statistic functions return numerically distinct values when the + theoretical values would be exactly equal. In some cases, this could + lead to a large error in the calculated p-value. `permutation_test` + guards against this by considering elements in the null distribution + that are "close" (within a relative tolerance of 100 times the + floating point epsilon of inexact dtypes) to the observed + value of the test statistic as equal to the observed value of the + test statistic. However, the user is advised to inspect the null + distribution to assess whether this method of comparison is + appropriate, and if not, calculate the p-value manually. See example + below. + + References + ---------- + + .. [1] R. A. Fisher. The Design of Experiments, 6th Ed (1951). + .. [2] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly Drawn." + Statistical Applications in Genetics and Molecular Biology 9.1 (2010). + .. [3] M. D. Ernst. "Permutation Methods: A Basis for Exact Inference". + Statistical Science (2004). + .. [4] B. Efron and R. J. Tibshirani. An Introduction to the Bootstrap + (1993). + + Examples + -------- + + Suppose we wish to test whether two samples are drawn from the same + distribution. Assume that the underlying distributions are unknown to us, + and that before observing the data, we hypothesized that the mean of the + first sample would be less than that of the second sample. We decide that + we will use the difference between the sample means as a test statistic, + and we will consider a p-value of 0.05 to be statistically significant. + + For efficiency, we write the function defining the test statistic in a + vectorized fashion: the samples ``x`` and ``y`` can be ND arrays, and the + statistic will be calculated for each axis-slice along `axis`. + + >>> import numpy as np + >>> def statistic(x, y, axis): + ... return np.mean(x, axis=axis) - np.mean(y, axis=axis) + + After collecting our data, we calculate the observed value of the test + statistic. + + >>> from scipy.stats import norm + >>> rng = np.random.default_rng() + >>> x = norm.rvs(size=5, random_state=rng) + >>> y = norm.rvs(size=6, loc = 3, random_state=rng) + >>> statistic(x, y, 0) + -3.5411688580987266 + + Indeed, the test statistic is negative, suggesting that the true mean of + the distribution underlying ``x`` is less than that of the distribution + underlying ``y``. To determine the probability of this occurring by chance + if the two samples were drawn from the same distribution, we perform + a permutation test. + + >>> from scipy.stats import permutation_test + >>> # because our statistic is vectorized, we pass `vectorized=True` + >>> # `n_resamples=np.inf` indicates that an exact test is to be performed + >>> res = permutation_test((x, y), statistic, vectorized=True, + ... n_resamples=np.inf, alternative='less') + >>> print(res.statistic) + -3.5411688580987266 + >>> print(res.pvalue) + 0.004329004329004329 + + The probability of obtaining a test statistic less than or equal to the + observed value under the null hypothesis is 0.4329%. This is less than our + chosen threshold of 5%, so we consider this to be significant evidence + against the null hypothesis in favor of the alternative. + + Because the size of the samples above was small, `permutation_test` could + perform an exact test. For larger samples, we resort to a randomized + permutation test. + + >>> x = norm.rvs(size=100, random_state=rng) + >>> y = norm.rvs(size=120, loc=0.3, random_state=rng) + >>> res = permutation_test((x, y), statistic, n_resamples=100000, + ... vectorized=True, alternative='less', + ... random_state=rng) + >>> print(res.statistic) + -0.5230459671240913 + >>> print(res.pvalue) + 0.00016999830001699983 + + The approximate probability of obtaining a test statistic less than or + equal to the observed value under the null hypothesis is 0.0225%. This is + again less than our chosen threshold of 5%, so again we have significant + evidence to reject the null hypothesis in favor of the alternative. + + For large samples and number of permutations, the result is comparable to + that of the corresponding asymptotic test, the independent sample t-test. + + >>> from scipy.stats import ttest_ind + >>> res_asymptotic = ttest_ind(x, y, alternative='less') + >>> print(res_asymptotic.pvalue) + 0.00012688101537979522 + + The permutation distribution of the test statistic is provided for + further investigation. + + >>> import matplotlib.pyplot as plt + >>> plt.hist(res.null_distribution, bins=50) + >>> plt.title("Permutation distribution of test statistic") + >>> plt.xlabel("Value of Statistic") + >>> plt.ylabel("Frequency") + >>> plt.show() + + Inspection of the null distribution is essential if the statistic suffers + from inaccuracy due to limited machine precision. Consider the following + case: + + >>> from scipy.stats import pearsonr + >>> x = [1, 2, 4, 3] + >>> y = [2, 4, 6, 8] + >>> def statistic(x, y): + ... return pearsonr(x, y).statistic + >>> res = permutation_test((x, y), statistic, vectorized=False, + ... permutation_type='pairings', + ... alternative='greater') + >>> r, pvalue, null = res.statistic, res.pvalue, res.null_distribution + + In this case, some elements of the null distribution differ from the + observed value of the correlation coefficient ``r`` due to numerical noise. + We manually inspect the elements of the null distribution that are nearly + the same as the observed value of the test statistic. + + >>> r + 0.8 + >>> unique = np.unique(null) + >>> unique + array([-1. , -0.8, -0.8, -0.6, -0.4, -0.2, -0.2, 0. , 0.2, 0.2, 0.4, + 0.6, 0.8, 0.8, 1. ]) # may vary + >>> unique[np.isclose(r, unique)].tolist() + [0.7999999999999999, 0.8] + + If `permutation_test` were to perform the comparison naively, the + elements of the null distribution with value ``0.7999999999999999`` would + not be considered as extreme or more extreme as the observed value of the + statistic, so the calculated p-value would be too small. + + >>> incorrect_pvalue = np.count_nonzero(null >= r) / len(null) + >>> incorrect_pvalue + 0.1111111111111111 # may vary + + Instead, `permutation_test` treats elements of the null distribution that + are within ``max(1e-14, abs(r)*1e-14)`` of the observed value of the + statistic ``r`` to be equal to ``r``. + + >>> correct_pvalue = np.count_nonzero(null >= r - 1e-14) / len(null) + >>> correct_pvalue + 0.16666666666666666 + >>> res.pvalue == correct_pvalue + True + + This method of comparison is expected to be accurate in most practical + situations, but the user is advised to assess this by inspecting the + elements of the null distribution that are close to the observed value + of the statistic. Also, consider the use of statistics that can be + calculated using exact arithmetic (e.g. integer statistics). + + """ + args = _permutation_test_iv(data, statistic, permutation_type, vectorized, + n_resamples, batch, alternative, axis, + random_state) + (data, statistic, permutation_type, vectorized, n_resamples, batch, + alternative, axis, random_state) = args + + observed = statistic(*data, axis=-1) + + null_calculators = {"pairings": _calculate_null_pairings, + "samples": _calculate_null_samples, + "independent": _calculate_null_both} + null_calculator_args = (data, statistic, n_resamples, + batch, random_state) + calculate_null = null_calculators[permutation_type] + null_distribution, n_resamples, exact_test = ( + calculate_null(*null_calculator_args)) + + # See References [2] and [3] + adjustment = 0 if exact_test else 1 + + # relative tolerance for detecting numerically distinct but + # theoretically equal values in the null distribution + eps = (0 if not np.issubdtype(observed.dtype, np.inexact) + else np.finfo(observed.dtype).eps*100) + gamma = np.abs(eps * observed) + + def less(null_distribution, observed): + cmps = null_distribution <= observed + gamma + pvalues = (cmps.sum(axis=0) + adjustment) / (n_resamples + adjustment) + return pvalues + + def greater(null_distribution, observed): + cmps = null_distribution >= observed - gamma + pvalues = (cmps.sum(axis=0) + adjustment) / (n_resamples + adjustment) + return pvalues + + def two_sided(null_distribution, observed): + pvalues_less = less(null_distribution, observed) + pvalues_greater = greater(null_distribution, observed) + pvalues = np.minimum(pvalues_less, pvalues_greater) * 2 + return pvalues + + compare = {"less": less, + "greater": greater, + "two-sided": two_sided} + + pvalues = compare[alternative](null_distribution, observed) + pvalues = np.clip(pvalues, 0, 1) + + return PermutationTestResult(observed, pvalues, null_distribution) + + +@dataclass +class ResamplingMethod: + """Configuration information for a statistical resampling method. + + Instances of this class can be passed into the `method` parameter of some + hypothesis test functions to perform a resampling or Monte Carlo version + of the hypothesis test. + + Attributes + ---------- + n_resamples : int + The number of resamples to perform or Monte Carlo samples to draw. + batch : int, optional + The number of resamples to process in each vectorized call to + the statistic. Batch sizes >>1 tend to be faster when the statistic + is vectorized, but memory usage scales linearly with the batch size. + Default is ``None``, which processes all resamples in a single batch. + """ + n_resamples: int = 9999 + batch: int = None # type: ignore[assignment] + + +@dataclass +class MonteCarloMethod(ResamplingMethod): + """Configuration information for a Monte Carlo hypothesis test. + + Instances of this class can be passed into the `method` parameter of some + hypothesis test functions to perform a Monte Carlo version of the + hypothesis tests. + + Attributes + ---------- + n_resamples : int, optional + The number of Monte Carlo samples to draw. Default is 9999. + batch : int, optional + The number of Monte Carlo samples to process in each vectorized call to + the statistic. Batch sizes >>1 tend to be faster when the statistic + is vectorized, but memory usage scales linearly with the batch size. + Default is ``None``, which processes all samples in a single batch. + rvs : callable or tuple of callables, optional + A callable or sequence of callables that generates random variates + under the null hypothesis. Each element of `rvs` must be a callable + that accepts keyword argument ``size`` (e.g. ``rvs(size=(m, n))``) and + returns an N-d array sample of that shape. If `rvs` is a sequence, the + number of callables in `rvs` must match the number of samples passed + to the hypothesis test in which the `MonteCarloMethod` is used. Default + is ``None``, in which case the hypothesis test function chooses values + to match the standard version of the hypothesis test. For example, + the null hypothesis of `scipy.stats.pearsonr` is typically that the + samples are drawn from the standard normal distribution, so + ``rvs = (rng.normal, rng.normal)`` where + ``rng = np.random.default_rng()``. + """ + rvs: object = None + + def _asdict(self): + # `dataclasses.asdict` deepcopies; we don't want that. + return dict(n_resamples=self.n_resamples, batch=self.batch, + rvs=self.rvs) + + +@dataclass +class PermutationMethod(ResamplingMethod): + """Configuration information for a permutation hypothesis test. + + Instances of this class can be passed into the `method` parameter of some + hypothesis test functions to perform a permutation version of the + hypothesis tests. + + Attributes + ---------- + n_resamples : int, optional + The number of resamples to perform. Default is 9999. + batch : int, optional + The number of resamples to process in each vectorized call to + the statistic. Batch sizes >>1 tend to be faster when the statistic + is vectorized, but memory usage scales linearly with the batch size. + Default is ``None``, which processes all resamples in a single batch. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, then that instance is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is ``None`` (default), the + `numpy.random.RandomState` singleton is used. + """ + random_state: object = None + + def _asdict(self): + # `dataclasses.asdict` deepcopies; we don't want that. + return dict(n_resamples=self.n_resamples, batch=self.batch, + random_state=self.random_state) + + +@dataclass +class BootstrapMethod(ResamplingMethod): + """Configuration information for a bootstrap confidence interval. + + Instances of this class can be passed into the `method` parameter of some + confidence interval methods to generate a bootstrap confidence interval. + + Attributes + ---------- + n_resamples : int, optional + The number of resamples to perform. Default is 9999. + batch : int, optional + The number of resamples to process in each vectorized call to + the statistic. Batch sizes >>1 tend to be faster when the statistic + is vectorized, but memory usage scales linearly with the batch size. + Default is ``None``, which processes all resamples in a single batch. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is already a ``Generator`` or ``RandomState`` + instance, then that instance is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is ``None`` (default), the + `numpy.random.RandomState` singleton is used. + + method : {'bca', 'percentile', 'basic'} + Whether to use the 'percentile' bootstrap ('percentile'), the 'basic' + (AKA 'reverse') bootstrap ('basic'), or the bias-corrected and + accelerated bootstrap ('BCa', default). + """ + random_state: object = None + method: str = 'BCa' + + def _asdict(self): + # `dataclasses.asdict` deepcopies; we don't want that. + return dict(n_resamples=self.n_resamples, batch=self.batch, + random_state=self.random_state, method=self.method) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_result_classes.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_result_classes.py new file mode 100644 index 0000000000000000000000000000000000000000..975af9310efb0c9a414439fd8d531fb95c988951 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_result_classes.py @@ -0,0 +1,40 @@ +# This module exists only to allow Sphinx to generate docs +# for the result objects returned by some functions in stats +# _without_ adding them to the main stats documentation page. + +""" +Result classes +-------------- + +.. currentmodule:: scipy.stats._result_classes + +.. autosummary:: + :toctree: generated/ + + RelativeRiskResult + BinomTestResult + TukeyHSDResult + DunnettResult + PearsonRResult + FitResult + OddsRatioResult + TtestResult + ECDFResult + EmpiricalDistributionFunction + +""" + +__all__ = ['BinomTestResult', 'RelativeRiskResult', 'TukeyHSDResult', + 'PearsonRResult', 'FitResult', 'OddsRatioResult', + 'TtestResult', 'DunnettResult', 'ECDFResult', + 'EmpiricalDistributionFunction'] + + +from ._binomtest import BinomTestResult +from ._odds_ratio import OddsRatioResult +from ._relative_risk import RelativeRiskResult +from ._hypotests import TukeyHSDResult +from ._multicomp import DunnettResult +from ._stats_py import PearsonRResult, TtestResult +from ._fit import FitResult +from ._survival import ECDFResult, EmpiricalDistributionFunction diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rvs_sampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rvs_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..86adb251c3e5ced6896b498bc28c5b6b144db7af --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_rvs_sampling.py @@ -0,0 +1,56 @@ +import warnings +from scipy.stats.sampling import RatioUniforms + +def rvs_ratio_uniforms(pdf, umax, vmin, vmax, size=1, c=0, random_state=None): + """ + Generate random samples from a probability density function using the + ratio-of-uniforms method. + + .. deprecated:: 1.12.0 + `rvs_ratio_uniforms` is deprecated in favour of + `scipy.stats.sampling.RatioUniforms` from version 1.12.0 and will + be removed in SciPy 1.15.0 + + Parameters + ---------- + pdf : callable + A function with signature `pdf(x)` that is proportional to the + probability density function of the distribution. + umax : float + The upper bound of the bounding rectangle in the u-direction. + vmin : float + The lower bound of the bounding rectangle in the v-direction. + vmax : float + The upper bound of the bounding rectangle in the v-direction. + size : int or tuple of ints, optional + Defining number of random variates (default is 1). + c : float, optional. + Shift parameter of ratio-of-uniforms method, see Notes. Default is 0. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + rvs : ndarray + The random variates distributed according to the probability + distribution defined by the pdf. + + Notes + ----- + Please refer to `scipy.stats.sampling.RatioUniforms` for the documentation. + """ + warnings.warn("Please use `RatioUniforms` from the " + "`scipy.stats.sampling` namespace. The " + "`scipy.stats.rvs_ratio_uniforms` namespace is deprecated " + "and will be removed in SciPy 1.15.0", + category=DeprecationWarning, stacklevel=2) + gen = RatioUniforms(pdf, umax=umax, vmin=vmin, vmax=vmax, + c=c, random_state=random_state) + return gen.rvs(size) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..44143985a88738c43984347b7787279348fac7f4 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sampling.py @@ -0,0 +1,1314 @@ +import math +import numbers +import numpy as np +from scipy import stats +from scipy import special as sc +from ._qmc import (check_random_state as check_random_state_qmc, + Halton, QMCEngine) +from ._unuran.unuran_wrapper import NumericalInversePolynomial +from scipy._lib._util import check_random_state + + +__all__ = ['FastGeneratorInversion', 'RatioUniforms'] + + +# define pdfs and other helper functions to create the generators + +def argus_pdf(x, chi): + # approach follows Baumgarten/Hoermann: Generating ARGUS random variates + # for chi > 5, use relationship of the ARGUS distribution to Gamma(1.5) + if chi <= 5: + y = 1 - x * x + return x * math.sqrt(y) * math.exp(-0.5 * chi**2 * y) + return math.sqrt(x) * math.exp(-x) + + +def argus_gamma_trf(x, chi): + if chi <= 5: + return x + return np.sqrt(1.0 - 2 * x / chi**2) + + +def argus_gamma_inv_trf(x, chi): + if chi <= 5: + return x + return 0.5 * chi**2 * (1 - x**2) + + +def betaprime_pdf(x, a, b): + if x > 0: + logf = (a - 1) * math.log(x) - (a + b) * math.log1p(x) - sc.betaln(a, b) + return math.exp(logf) + else: + # return pdf at x == 0 separately to avoid runtime warnings + if a > 1: + return 0 + elif a < 1: + return np.inf + else: + return 1 / sc.beta(a, b) + + +def beta_valid_params(a, b): + return (min(a, b) >= 0.1) and (max(a, b) <= 700) + + +def gamma_pdf(x, a): + if x > 0: + return math.exp(-math.lgamma(a) + (a - 1.0) * math.log(x) - x) + else: + return 0 if a >= 1 else np.inf + + +def invgamma_pdf(x, a): + if x > 0: + return math.exp(-(a + 1.0) * math.log(x) - math.lgamma(a) - 1 / x) + else: + return 0 if a >= 1 else np.inf + + +def burr_pdf(x, cc, dd): + # note: we use np.exp instead of math.exp, otherwise an overflow + # error can occur in the setup, e.g., for parameters + # 1.89128135, 0.30195177, see test test_burr_overflow + if x > 0: + lx = math.log(x) + return np.exp(-(cc + 1) * lx - (dd + 1) * math.log1p(np.exp(-cc * lx))) + else: + return 0 + + +def burr12_pdf(x, cc, dd): + if x > 0: + lx = math.log(x) + logterm = math.log1p(math.exp(cc * lx)) + return math.exp((cc - 1) * lx - (dd + 1) * logterm + math.log(cc * dd)) + else: + return 0 + + +def chi_pdf(x, a): + if x > 0: + return math.exp( + (a - 1) * math.log(x) + - 0.5 * (x * x) + - (a / 2 - 1) * math.log(2) + - math.lgamma(0.5 * a) + ) + else: + return 0 if a >= 1 else np.inf + + +def chi2_pdf(x, df): + if x > 0: + return math.exp( + (df / 2 - 1) * math.log(x) + - 0.5 * x + - (df / 2) * math.log(2) + - math.lgamma(0.5 * df) + ) + else: + return 0 if df >= 1 else np.inf + + +def alpha_pdf(x, a): + if x > 0: + return math.exp(-2.0 * math.log(x) - 0.5 * (a - 1.0 / x) ** 2) + return 0.0 + + +def bradford_pdf(x, c): + if 0 <= x <= 1: + return 1.0 / (1.0 + c * x) + return 0.0 + + +def crystalball_pdf(x, b, m): + if x > -b: + return math.exp(-0.5 * x * x) + return math.exp(m * math.log(m / b) - 0.5 * b * b - m * math.log(m / b - b - x)) + + +def weibull_min_pdf(x, c): + if x > 0: + return c * math.exp((c - 1) * math.log(x) - x**c) + return 0.0 + + +def weibull_max_pdf(x, c): + if x < 0: + return c * math.exp((c - 1) * math.log(-x) - ((-x) ** c)) + return 0.0 + + +def invweibull_pdf(x, c): + if x > 0: + return c * math.exp(-(c + 1) * math.log(x) - x ** (-c)) + return 0.0 + + +def wald_pdf(x): + if x > 0: + return math.exp(-((x - 1) ** 2) / (2 * x)) / math.sqrt(x**3) + return 0.0 + + +def geninvgauss_mode(p, b): + if p > 1: # equivalent mode formulas numerical more stable versions + return (math.sqrt((1 - p) ** 2 + b**2) - (1 - p)) / b + return b / (math.sqrt((1 - p) ** 2 + b**2) + (1 - p)) + + +def geninvgauss_pdf(x, p, b): + m = geninvgauss_mode(p, b) + lfm = (p - 1) * math.log(m) - 0.5 * b * (m + 1 / m) + if x > 0: + return math.exp((p - 1) * math.log(x) - 0.5 * b * (x + 1 / x) - lfm) + return 0.0 + + +def invgauss_mode(mu): + return 1.0 / (math.sqrt(1.5 * 1.5 + 1 / (mu * mu)) + 1.5) + + +def invgauss_pdf(x, mu): + m = invgauss_mode(mu) + lfm = -1.5 * math.log(m) - (m - mu) ** 2 / (2 * m * mu**2) + if x > 0: + return math.exp(-1.5 * math.log(x) - (x - mu) ** 2 / (2 * x * mu**2) - lfm) + return 0.0 + + +def powerlaw_pdf(x, a): + if x > 0: + return x ** (a - 1) + return 0.0 + + +# Define a dictionary: for a given distribution (keys), another dictionary +# (values) specifies the parameters for NumericalInversePolynomial (PINV). +# The keys of the latter dictionary are: +# - pdf: the pdf of the distribution (callable). The signature of the pdf +# is float -> float (i.e., the function does not have to be vectorized). +# If possible, functions like log or exp from the module math should be +# preferred over functions from numpy since the PINV setup will be faster +# in that case. +# - check_pinv_params: callable f that returns true if the shape parameters +# (args) are recommended parameters for PINV (i.e., the u-error does +# not exceed the default tolerance) +# - center: scalar if the center does not depend on args, otherwise +# callable that returns the center as a function of the shape parameters +# - rvs_transform: a callable that can be used to transform the rvs that +# are distributed according to the pdf to the target distribution +# (as an example, see the entry for the beta distribution) +# - rvs_transform_inv: the inverse of rvs_transform (it is required +# for the transformed ppf) +# - mirror_uniform: boolean or a callable that returns true or false +# depending on the shape parameters. If True, the ppf is applied +# to 1-u instead of u to generate rvs, where u is a uniform rv. +# While both u and 1-u are uniform, it can be required to use 1-u +# to compute the u-error correctly. This is only relevant for the argus +# distribution. +# The only required keys are "pdf" and "check_pinv_params". +# All other keys are optional. + +PINV_CONFIG = { + "alpha": { + "pdf": alpha_pdf, + "check_pinv_params": lambda a: 1.0e-11 <= a < 2.1e5, + "center": lambda a: 0.25 * (math.sqrt(a * a + 8.0) - a), + }, + "anglit": { + "pdf": lambda x: math.cos(2 * x) + 1.0e-13, + # +1.e-13 is necessary, otherwise PINV has strange problems as + # f(upper border) is very close to 0 + "center": 0, + }, + "argus": { + "pdf": argus_pdf, + "center": lambda chi: 0.7 if chi <= 5 else 0.5, + "check_pinv_params": lambda chi: 1e-20 < chi < 901, + "rvs_transform": argus_gamma_trf, + "rvs_transform_inv": argus_gamma_inv_trf, + "mirror_uniform": lambda chi: chi > 5, + }, + "beta": { + "pdf": betaprime_pdf, + "center": lambda a, b: max(0.1, (a - 1) / (b + 1)), + "check_pinv_params": beta_valid_params, + "rvs_transform": lambda x, *args: x / (1 + x), + "rvs_transform_inv": lambda x, *args: x / (1 - x) if x < 1 else np.inf, + }, + "betaprime": { + "pdf": betaprime_pdf, + "center": lambda a, b: max(0.1, (a - 1) / (b + 1)), + "check_pinv_params": beta_valid_params, + }, + "bradford": { + "pdf": bradford_pdf, + "check_pinv_params": lambda a: 1.0e-6 <= a <= 1e9, + "center": 0.5, + }, + "burr": { + "pdf": burr_pdf, + "center": lambda a, b: (2 ** (1 / b) - 1) ** (-1 / a), + "check_pinv_params": lambda a, b: (min(a, b) >= 0.3) and (max(a, b) <= 50), + }, + "burr12": { + "pdf": burr12_pdf, + "center": lambda a, b: (2 ** (1 / b) - 1) ** (1 / a), + "check_pinv_params": lambda a, b: (min(a, b) >= 0.2) and (max(a, b) <= 50), + }, + "cauchy": { + "pdf": lambda x: 1 / (1 + (x * x)), + "center": 0, + }, + "chi": { + "pdf": chi_pdf, + "check_pinv_params": lambda df: 0.05 <= df <= 1.0e6, + "center": lambda a: math.sqrt(a), + }, + "chi2": { + "pdf": chi2_pdf, + "check_pinv_params": lambda df: 0.07 <= df <= 1e6, + "center": lambda a: a, + }, + "cosine": { + "pdf": lambda x: 1 + math.cos(x), + "center": 0, + }, + "crystalball": { + "pdf": crystalball_pdf, + "check_pinv_params": lambda b, m: (0.01 <= b <= 5.5) + and (1.1 <= m <= 75.1), + "center": 0.0, + }, + "expon": { + "pdf": lambda x: math.exp(-x), + "center": 1.0, + }, + "gamma": { + "pdf": gamma_pdf, + "check_pinv_params": lambda a: 0.04 <= a <= 1e6, + "center": lambda a: a, + }, + "gennorm": { + "pdf": lambda x, b: math.exp(-abs(x) ** b), + "check_pinv_params": lambda b: 0.081 <= b <= 45.0, + "center": 0.0, + }, + "geninvgauss": { + "pdf": geninvgauss_pdf, + "check_pinv_params": lambda p, b: (abs(p) <= 1200.0) + and (1.0e-10 <= b <= 1200.0), + "center": geninvgauss_mode, + }, + "gumbel_l": { + "pdf": lambda x: math.exp(x - math.exp(x)), + "center": -0.6, + }, + "gumbel_r": { + "pdf": lambda x: math.exp(-x - math.exp(-x)), + "center": 0.6, + }, + "hypsecant": { + "pdf": lambda x: 1.0 / (math.exp(x) + math.exp(-x)), + "center": 0.0, + }, + "invgamma": { + "pdf": invgamma_pdf, + "check_pinv_params": lambda a: 0.04 <= a <= 1e6, + "center": lambda a: 1 / a, + }, + "invgauss": { + "pdf": invgauss_pdf, + "check_pinv_params": lambda mu: 1.0e-10 <= mu <= 1.0e9, + "center": invgauss_mode, + }, + "invweibull": { + "pdf": invweibull_pdf, + "check_pinv_params": lambda a: 0.12 <= a <= 512, + "center": 1.0, + }, + "laplace": { + "pdf": lambda x: math.exp(-abs(x)), + "center": 0.0, + }, + "logistic": { + "pdf": lambda x: math.exp(-x) / (1 + math.exp(-x)) ** 2, + "center": 0.0, + }, + "maxwell": { + "pdf": lambda x: x * x * math.exp(-0.5 * x * x), + "center": 1.41421, + }, + "moyal": { + "pdf": lambda x: math.exp(-(x + math.exp(-x)) / 2), + "center": 1.2, + }, + "norm": { + "pdf": lambda x: math.exp(-x * x / 2), + "center": 0.0, + }, + "pareto": { + "pdf": lambda x, b: x ** -(b + 1), + "center": lambda b: b / (b - 1) if b > 2 else 1.5, + "check_pinv_params": lambda b: 0.08 <= b <= 400000, + }, + "powerlaw": { + "pdf": powerlaw_pdf, + "center": 1.0, + "check_pinv_params": lambda a: 0.06 <= a <= 1.0e5, + }, + "t": { + "pdf": lambda x, df: (1 + x * x / df) ** (-0.5 * (df + 1)), + "check_pinv_params": lambda a: 0.07 <= a <= 1e6, + "center": 0.0, + }, + "rayleigh": { + "pdf": lambda x: x * math.exp(-0.5 * (x * x)), + "center": 1.0, + }, + "semicircular": { + "pdf": lambda x: math.sqrt(1.0 - (x * x)), + "center": 0, + }, + "wald": { + "pdf": wald_pdf, + "center": 1.0, + }, + "weibull_max": { + "pdf": weibull_max_pdf, + "check_pinv_params": lambda a: 0.25 <= a <= 512, + "center": -1.0, + }, + "weibull_min": { + "pdf": weibull_min_pdf, + "check_pinv_params": lambda a: 0.25 <= a <= 512, + "center": 1.0, + }, +} + + +def _validate_qmc_input(qmc_engine, d, seed): + # Input validation for `qmc_engine` and `d` + # Error messages for invalid `d` are raised by QMCEngine + # we could probably use a stats.qmc.check_qrandom_state + if isinstance(qmc_engine, QMCEngine): + if d is not None and qmc_engine.d != d: + message = "`d` must be consistent with dimension of `qmc_engine`." + raise ValueError(message) + d = qmc_engine.d if d is None else d + elif qmc_engine is None: + d = 1 if d is None else d + qmc_engine = Halton(d, seed=seed) + else: + message = ( + "`qmc_engine` must be an instance of " + "`scipy.stats.qmc.QMCEngine` or `None`." + ) + raise ValueError(message) + + return qmc_engine, d + + +class CustomDistPINV: + def __init__(self, pdf, args): + self._pdf = lambda x: pdf(x, *args) + + def pdf(self, x): + return self._pdf(x) + + +class FastGeneratorInversion: + """ + Fast sampling by numerical inversion of the CDF for a large class of + continuous distributions in `scipy.stats`. + + Parameters + ---------- + dist : rv_frozen object + Frozen distribution object from `scipy.stats`. The list of supported + distributions can be found in the Notes section. The shape parameters, + `loc` and `scale` used to create the distributions must be scalars. + For example, for the Gamma distribution with shape parameter `p`, + `p` has to be a float, and for the beta distribution with shape + parameters (a, b), both a and b have to be floats. + domain : tuple of floats, optional + If one wishes to sample from a truncated/conditional distribution, + the domain has to be specified. + The default is None. In that case, the random variates are not + truncated, and the domain is inferred from the support of the + distribution. + ignore_shape_range : boolean, optional. + If False, shape parameters that are outside of the valid range + of values to ensure that the numerical accuracy (see Notes) is + high, raise a ValueError. If True, any shape parameters that are valid + for the distribution are accepted. This can be useful for testing. + The default is False. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + A NumPy random number generator or seed for the underlying NumPy + random number generator used to generate the stream of uniform + random numbers. + If `random_state` is None, it uses ``self.random_state``. + If `random_state` is an int, + ``np.random.default_rng(random_state)`` is used. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Attributes + ---------- + loc : float + The location parameter. + random_state : {`numpy.random.Generator`, `numpy.random.RandomState`} + The random state used in relevant methods like `rvs` (unless + another `random_state` is passed as an argument to these methods). + scale : float + The scale parameter. + + Methods + ------- + cdf + evaluate_error + ppf + qrvs + rvs + support + + Notes + ----- + The class creates an object for continuous distributions specified + by `dist`. The method `rvs` uses a generator from + `scipy.stats.sampling` that is created when the object is instantiated. + In addition, the methods `qrvs` and `ppf` are added. + `qrvs` generate samples based on quasi-random numbers from + `scipy.stats.qmc`. `ppf` is the PPF based on the + numerical inversion method in [1]_ (`NumericalInversePolynomial`) that is + used to generate random variates. + + Supported distributions (`distname`) are: + ``alpha``, ``anglit``, ``argus``, ``beta``, ``betaprime``, ``bradford``, + ``burr``, ``burr12``, ``cauchy``, ``chi``, ``chi2``, ``cosine``, + ``crystalball``, ``expon``, ``gamma``, ``gennorm``, ``geninvgauss``, + ``gumbel_l``, ``gumbel_r``, ``hypsecant``, ``invgamma``, ``invgauss``, + ``invweibull``, ``laplace``, ``logistic``, ``maxwell``, ``moyal``, + ``norm``, ``pareto``, ``powerlaw``, ``t``, ``rayleigh``, ``semicircular``, + ``wald``, ``weibull_max``, ``weibull_min``. + + `rvs` relies on the accuracy of the numerical inversion. If very extreme + shape parameters are used, the numerical inversion might not work. However, + for all implemented distributions, the admissible shape parameters have + been tested, and an error will be raised if the user supplies values + outside of the allowed range. The u-error should not exceed 1e-10 for all + valid parameters. Note that warnings might be raised even if parameters + are within the valid range when the object is instantiated. + To check numerical accuracy, the method `evaluate_error` can be used. + + Note that all implemented distributions are also part of `scipy.stats`, and + the object created by `FastGeneratorInversion` relies on methods like + `ppf`, `cdf` and `pdf` from `rv_frozen`. The main benefit of using this + class can be summarized as follows: Once the generator to sample random + variates is created in the setup step, sampling and evaluation of + the PPF using `ppf` are very fast, + and performance is essentially independent of the distribution. Therefore, + a substantial speed-up can be achieved for many distributions if large + numbers of random variates are required. It is important to know that this + fast sampling is achieved by inversion of the CDF. Thus, one uniform + random variate is transformed into a non-uniform variate, which is an + advantage for several simulation methods, e.g., when + the variance reduction methods of common random variates or + antithetic variates are be used ([2]_). + + In addition, inversion makes it possible to + - to use a QMC generator from `scipy.stats.qmc` (method `qrvs`), + - to generate random variates truncated to an interval. For example, if + one aims to sample standard normal random variates from + the interval (2, 4), this can be easily achieved by using the parameter + `domain`. + + The location and scale that are initially defined by `dist` + can be reset without having to rerun the setup + step to create the generator that is used for sampling. The relation + of the distribution `Y` with `loc` and `scale` to the standard + distribution `X` (i.e., ``loc=0`` and ``scale=1``) is given by + ``Y = loc + scale * X``. + + References + ---------- + .. [1] Derflinger, Gerhard, Wolfgang Hörmann, and Josef Leydold. + "Random variate generation by numerical inversion when only the + density is known." ACM Transactions on Modeling and Computer + Simulation (TOMACS) 20.4 (2010): 1-25. + .. [2] Hörmann, Wolfgang, Josef Leydold and Gerhard Derflinger. + "Automatic nonuniform random number generation." + Springer, 2004. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> from scipy.stats.sampling import FastGeneratorInversion + + Let's start with a simple example to illustrate the main features: + + >>> gamma_frozen = stats.gamma(1.5) + >>> gamma_dist = FastGeneratorInversion(gamma_frozen) + >>> r = gamma_dist.rvs(size=1000) + + The mean should be approximately equal to the shape parameter 1.5: + + >>> r.mean() + 1.52423591130436 # may vary + + Similarly, we can draw a sample based on quasi-random numbers: + + >>> r = gamma_dist.qrvs(size=1000) + >>> r.mean() + 1.4996639255942914 # may vary + + Compare the PPF against approximation `ppf`. + + >>> q = [0.001, 0.2, 0.5, 0.8, 0.999] + >>> np.max(np.abs(gamma_frozen.ppf(q) - gamma_dist.ppf(q))) + 4.313394796895409e-08 + + To confirm that the numerical inversion is accurate, we evaluate the + approximation error (u-error), which should be below 1e-10 (for more + details, refer to the documentation of `evaluate_error`): + + >>> gamma_dist.evaluate_error() + (7.446320551265581e-11, nan) # may vary + + Note that the location and scale can be changed without instantiating a + new generator: + + >>> gamma_dist.loc = 2 + >>> gamma_dist.scale = 3 + >>> r = gamma_dist.rvs(size=1000) + + The mean should be approximately 2 + 3*1.5 = 6.5. + + >>> r.mean() + 6.399549295242894 # may vary + + Let us also illustrate how truncation can be applied: + + >>> trunc_norm = FastGeneratorInversion(stats.norm(), domain=(3, 4)) + >>> r = trunc_norm.rvs(size=1000) + >>> 3 < r.min() < r.max() < 4 + True + + Check the mean: + + >>> r.mean() + 3.250433367078603 # may vary + + >>> stats.norm.expect(lb=3, ub=4, conditional=True) + 3.260454285589997 + + In this particular, case, `scipy.stats.truncnorm` could also be used to + generate truncated normal random variates. + + """ + + def __init__( + self, + dist, + *, + domain=None, + ignore_shape_range=False, + random_state=None, + ): + + if isinstance(dist, stats.distributions.rv_frozen): + distname = dist.dist.name + if distname not in PINV_CONFIG.keys(): + raise ValueError( + f"Distribution '{distname}' is not supported." + f"It must be one of {list(PINV_CONFIG.keys())}" + ) + else: + raise ValueError("`dist` must be a frozen distribution object") + + loc = dist.kwds.get("loc", 0) + scale = dist.kwds.get("scale", 1) + args = dist.args + if not np.isscalar(loc): + raise ValueError("loc must be scalar.") + if not np.isscalar(scale): + raise ValueError("scale must be scalar.") + + self._frozendist = getattr(stats, distname)( + *args, + loc=loc, + scale=scale, + ) + self._distname = distname + + nargs = np.broadcast_arrays(args)[0].size + nargs_expected = self._frozendist.dist.numargs + if nargs != nargs_expected: + raise ValueError( + f"Each of the {nargs_expected} shape parameters must be a " + f"scalar, but {nargs} values are provided." + ) + + self.random_state = random_state + + if domain is None: + self._domain = self._frozendist.support() + self._p_lower = 0.0 + self._p_domain = 1.0 + else: + self._domain = domain + self._p_lower = self._frozendist.cdf(self._domain[0]) + _p_domain = self._frozendist.cdf(self._domain[1]) - self._p_lower + self._p_domain = _p_domain + self._set_domain_adj() + self._ignore_shape_range = ignore_shape_range + + # the domain to be passed to NumericalInversePolynomial + # define a separate variable since in case of a transformation, + # domain_pinv will not be the same as self._domain + self._domain_pinv = self._domain + + # get information about the distribution from the config to set up + # the generator + dist = self._process_config(distname, args) + + if self._rvs_transform_inv is not None: + d0 = self._rvs_transform_inv(self._domain[0], *args) + d1 = self._rvs_transform_inv(self._domain[1], *args) + if d0 > d1: + # swap values if transformation if decreasing + d0, d1 = d1, d0 + # only update _domain_pinv and not _domain + # _domain refers to the original distribution, _domain_pinv + # to the transformed distribution + self._domain_pinv = d0, d1 + + # self._center has been set by the call self._process_config + # check if self._center is inside the transformed domain + # _domain_pinv, otherwise move it to the endpoint that is closer + if self._center is not None: + if self._center < self._domain_pinv[0]: + self._center = self._domain_pinv[0] + elif self._center > self._domain_pinv[1]: + self._center = self._domain_pinv[1] + + self._rng = NumericalInversePolynomial( + dist, + random_state=self.random_state, + domain=self._domain_pinv, + center=self._center, + ) + + @property + def random_state(self): + return self._random_state + + @random_state.setter + def random_state(self, random_state): + self._random_state = check_random_state_qmc(random_state) + + @property + def loc(self): + return self._frozendist.kwds.get("loc", 0) + + @loc.setter + def loc(self, loc): + if not np.isscalar(loc): + raise ValueError("loc must be scalar.") + self._frozendist.kwds["loc"] = loc + # update the adjusted domain that depends on loc and scale + self._set_domain_adj() + + @property + def scale(self): + return self._frozendist.kwds.get("scale", 0) + + @scale.setter + def scale(self, scale): + if not np.isscalar(scale): + raise ValueError("scale must be scalar.") + self._frozendist.kwds["scale"] = scale + # update the adjusted domain that depends on loc and scale + self._set_domain_adj() + + def _set_domain_adj(self): + """ Adjust the domain based on loc and scale. """ + loc = self.loc + scale = self.scale + lb = self._domain[0] * scale + loc + ub = self._domain[1] * scale + loc + self._domain_adj = (lb, ub) + + def _process_config(self, distname, args): + cfg = PINV_CONFIG[distname] + if "check_pinv_params" in cfg: + if not self._ignore_shape_range: + if not cfg["check_pinv_params"](*args): + msg = ("No generator is defined for the shape parameters " + f"{args}. Use ignore_shape_range to proceed " + "with the selected values.") + raise ValueError(msg) + + if "center" in cfg.keys(): + if not np.isscalar(cfg["center"]): + self._center = cfg["center"](*args) + else: + self._center = cfg["center"] + else: + self._center = None + self._rvs_transform = cfg.get("rvs_transform", None) + self._rvs_transform_inv = cfg.get("rvs_transform_inv", None) + _mirror_uniform = cfg.get("mirror_uniform", None) + if _mirror_uniform is None: + self._mirror_uniform = False + else: + self._mirror_uniform = _mirror_uniform(*args) + + return CustomDistPINV(cfg["pdf"], args) + + def rvs(self, size=None): + """ + Sample from the distribution by inversion. + + Parameters + ---------- + size : int or tuple, optional + The shape of samples. Default is ``None`` in which case a scalar + sample is returned. + + Returns + ------- + rvs : array_like + A NumPy array of random variates. + + Notes + ----- + Random variates are generated by numerical inversion of the CDF, i.e., + `ppf` computed by `NumericalInversePolynomial` when the class + is instantiated. Note that the + default ``rvs`` method of the rv_continuous class is + overwritten. Hence, a different stream of random numbers is generated + even if the same seed is used. + """ + # note: we cannot use self._rng.rvs directly in case + # self._mirror_uniform is true + u = self.random_state.uniform(size=size) + if self._mirror_uniform: + u = 1 - u + r = self._rng.ppf(u) + if self._rvs_transform is not None: + r = self._rvs_transform(r, *self._frozendist.args) + return self.loc + self.scale * r + + def ppf(self, q): + """ + Very fast PPF (inverse CDF) of the distribution which + is a very close approximation of the exact PPF values. + + Parameters + ---------- + u : array_like + Array with probabilities. + + Returns + ------- + ppf : array_like + Quantiles corresponding to the values in `u`. + + Notes + ----- + The evaluation of the PPF is very fast but it may have a large + relative error in the far tails. The numerical precision of the PPF + is controlled by the u-error, that is, + ``max |u - CDF(PPF(u))|`` where the max is taken over points in + the interval [0,1], see `evaluate_error`. + + Note that this PPF is designed to generate random samples. + """ + q = np.asarray(q) + if self._mirror_uniform: + x = self._rng.ppf(1 - q) + else: + x = self._rng.ppf(q) + if self._rvs_transform is not None: + x = self._rvs_transform(x, *self._frozendist.args) + return self.scale * x + self.loc + + def qrvs(self, size=None, d=None, qmc_engine=None): + """ + Quasi-random variates of the given distribution. + + The `qmc_engine` is used to draw uniform quasi-random variates, and + these are converted to quasi-random variates of the given distribution + using inverse transform sampling. + + Parameters + ---------- + size : int, tuple of ints, or None; optional + Defines shape of random variates array. Default is ``None``. + d : int or None, optional + Defines dimension of uniform quasi-random variates to be + transformed. Default is ``None``. + qmc_engine : scipy.stats.qmc.QMCEngine(d=1), optional + Defines the object to use for drawing + quasi-random variates. Default is ``None``, which uses + `scipy.stats.qmc.Halton(1)`. + + Returns + ------- + rvs : ndarray or scalar + Quasi-random variates. See Notes for shape information. + + Notes + ----- + The shape of the output array depends on `size`, `d`, and `qmc_engine`. + The intent is for the interface to be natural, but the detailed rules + to achieve this are complicated. + + - If `qmc_engine` is ``None``, a `scipy.stats.qmc.Halton` instance is + created with dimension `d`. If `d` is not provided, ``d=1``. + - If `qmc_engine` is not ``None`` and `d` is ``None``, `d` is + determined from the dimension of the `qmc_engine`. + - If `qmc_engine` is not ``None`` and `d` is not ``None`` but the + dimensions are inconsistent, a ``ValueError`` is raised. + - After `d` is determined according to the rules above, the output + shape is ``tuple_shape + d_shape``, where: + + - ``tuple_shape = tuple()`` if `size` is ``None``, + - ``tuple_shape = (size,)`` if `size` is an ``int``, + - ``tuple_shape = size`` if `size` is a sequence, + - ``d_shape = tuple()`` if `d` is ``None`` or `d` is 1, and + - ``d_shape = (d,)`` if `d` is greater than 1. + + The elements of the returned array are part of a low-discrepancy + sequence. If `d` is 1, this means that none of the samples are truly + independent. If `d` > 1, each slice ``rvs[..., i]`` will be of a + quasi-independent sequence; see `scipy.stats.qmc.QMCEngine` for + details. Note that when `d` > 1, the samples returned are still those + of the provided univariate distribution, not a multivariate + generalization of that distribution. + + """ + qmc_engine, d = _validate_qmc_input(qmc_engine, d, self.random_state) + # mainly copied from unuran_wrapper.pyx.templ + # `rvs` is flexible about whether `size` is an int or tuple, so this + # should be, too. + try: + if size is None: + tuple_size = (1,) + else: + tuple_size = tuple(size) + except TypeError: + tuple_size = (size,) + # we do not use rng.qrvs directly since we need to be + # able to apply the ppf to 1 - u + N = 1 if size is None else np.prod(size) + u = qmc_engine.random(N) + if self._mirror_uniform: + u = 1 - u + qrvs = self._ppf(u) + if self._rvs_transform is not None: + qrvs = self._rvs_transform(qrvs, *self._frozendist.args) + if size is None: + qrvs = qrvs.squeeze()[()] + else: + if d == 1: + qrvs = qrvs.reshape(tuple_size) + else: + qrvs = qrvs.reshape(tuple_size + (d,)) + return self.loc + self.scale * qrvs + + def evaluate_error(self, size=100000, random_state=None, x_error=False): + """ + Evaluate the numerical accuracy of the inversion (u- and x-error). + + Parameters + ---------- + size : int, optional + The number of random points over which the error is estimated. + Default is ``100000``. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + A NumPy random number generator or seed for the underlying NumPy + random number generator used to generate the stream of uniform + random numbers. + If `random_state` is None, use ``self.random_state``. + If `random_state` is an int, + ``np.random.default_rng(random_state)`` is used. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + u_error, x_error : tuple of floats + A NumPy array of random variates. + + Notes + ----- + The numerical precision of the inverse CDF `ppf` is controlled by + the u-error. It is computed as follows: + ``max |u - CDF(PPF(u))|`` where the max is taken `size` random + points in the interval [0,1]. `random_state` determines the random + sample. Note that if `ppf` was exact, the u-error would be zero. + + The x-error measures the direct distance between the exact PPF + and `ppf`. If ``x_error`` is set to ``True`, it is + computed as the maximum of the minimum of the relative and absolute + x-error: + ``max(min(x_error_abs[i], x_error_rel[i]))`` where + ``x_error_abs[i] = |PPF(u[i]) - PPF_fast(u[i])|``, + ``x_error_rel[i] = max |(PPF(u[i]) - PPF_fast(u[i])) / PPF(u[i])|``. + Note that it is important to consider the relative x-error in the case + that ``PPF(u)`` is close to zero or very large. + + By default, only the u-error is evaluated and the x-error is set to + ``np.nan``. Note that the evaluation of the x-error will be very slow + if the implementation of the PPF is slow. + + Further information about these error measures can be found in [1]_. + + References + ---------- + .. [1] Derflinger, Gerhard, Wolfgang Hörmann, and Josef Leydold. + "Random variate generation by numerical inversion when only the + density is known." ACM Transactions on Modeling and Computer + Simulation (TOMACS) 20.4 (2010): 1-25. + + Examples + -------- + + >>> import numpy as np + >>> from scipy import stats + >>> from scipy.stats.sampling import FastGeneratorInversion + + Create an object for the normal distribution: + + >>> d_norm_frozen = stats.norm() + >>> d_norm = FastGeneratorInversion(d_norm_frozen) + + To confirm that the numerical inversion is accurate, we evaluate the + approximation error (u-error and x-error). + + >>> u_error, x_error = d_norm.evaluate_error(x_error=True) + + The u-error should be below 1e-10: + + >>> u_error + 8.785783212061915e-11 # may vary + + Compare the PPF against approximation `ppf`: + + >>> q = [0.001, 0.2, 0.4, 0.6, 0.8, 0.999] + >>> diff = np.abs(d_norm_frozen.ppf(q) - d_norm.ppf(q)) + >>> x_error_abs = np.max(diff) + >>> x_error_abs + 1.2937954707581412e-08 + + This is the absolute x-error evaluated at the points q. The relative + error is given by + + >>> x_error_rel = np.max(diff / np.abs(d_norm_frozen.ppf(q))) + >>> x_error_rel + 4.186725600453555e-09 + + The x_error computed above is derived in a very similar way over a + much larger set of random values q. At each value q[i], the minimum + of the relative and absolute error is taken. The final value is then + derived as the maximum of these values. In our example, we get the + following value: + + >>> x_error + 4.507068014335139e-07 # may vary + + """ + if not isinstance(size, (numbers.Integral, np.integer)): + raise ValueError("size must be an integer.") + # urng will be used to draw the samples for testing the error + # it must not interfere with self.random_state. therefore, do not + # call self.rvs, but draw uniform random numbers and apply + # self.ppf (note: like in rvs, consider self._mirror_uniform) + urng = check_random_state_qmc(random_state) + u = urng.uniform(size=size) + if self._mirror_uniform: + u = 1 - u + x = self.ppf(u) + uerr = np.max(np.abs(self._cdf(x) - u)) + if not x_error: + return uerr, np.nan + ppf_u = self._ppf(u) + x_error_abs = np.abs(self.ppf(u)-ppf_u) + x_error_rel = x_error_abs / np.abs(ppf_u) + x_error_combined = np.array([x_error_abs, x_error_rel]).min(axis=0) + return uerr, np.max(x_error_combined) + + def support(self): + """Support of the distribution. + + Returns + ------- + a, b : float + end-points of the distribution's support. + + Notes + ----- + + Note that the support of the distribution depends on `loc`, + `scale` and `domain`. + + Examples + -------- + + >>> from scipy import stats + >>> from scipy.stats.sampling import FastGeneratorInversion + + Define a truncated normal distribution: + + >>> d_norm = FastGeneratorInversion(stats.norm(), domain=(0, 1)) + >>> d_norm.support() + (0, 1) + + Shift the distribution: + + >>> d_norm.loc = 2.5 + >>> d_norm.support() + (2.5, 3.5) + + """ + return self._domain_adj + + def _cdf(self, x): + """Cumulative distribution function (CDF) + + Parameters + ---------- + x : array_like + The values where the CDF is evaluated + + Returns + ------- + y : ndarray + CDF evaluated at x + + """ + y = self._frozendist.cdf(x) + if self._p_domain == 1.0: + return y + return np.clip((y - self._p_lower) / self._p_domain, 0, 1) + + def _ppf(self, q): + """Percent point function (inverse of `cdf`) + + Parameters + ---------- + q : array_like + lower tail probability + + Returns + ------- + x : array_like + quantile corresponding to the lower tail probability q. + + """ + if self._p_domain == 1.0: + return self._frozendist.ppf(q) + x = self._frozendist.ppf(self._p_domain * np.array(q) + self._p_lower) + return np.clip(x, self._domain_adj[0], self._domain_adj[1]) + + +class RatioUniforms: + """ + Generate random samples from a probability density function using the + ratio-of-uniforms method. + + Parameters + ---------- + pdf : callable + A function with signature `pdf(x)` that is proportional to the + probability density function of the distribution. + umax : float + The upper bound of the bounding rectangle in the u-direction. + vmin : float + The lower bound of the bounding rectangle in the v-direction. + vmax : float + The upper bound of the bounding rectangle in the v-direction. + c : float, optional. + Shift parameter of ratio-of-uniforms method, see Notes. Default is 0. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Methods + ------- + rvs + + Notes + ----- + Given a univariate probability density function `pdf` and a constant `c`, + define the set ``A = {(u, v) : 0 < u <= sqrt(pdf(v/u + c))}``. + If ``(U, V)`` is a random vector uniformly distributed over ``A``, + then ``V/U + c`` follows a distribution according to `pdf`. + + The above result (see [1]_, [2]_) can be used to sample random variables + using only the PDF, i.e. no inversion of the CDF is required. Typical + choices of `c` are zero or the mode of `pdf`. The set ``A`` is a subset of + the rectangle ``R = [0, umax] x [vmin, vmax]`` where + + - ``umax = sup sqrt(pdf(x))`` + - ``vmin = inf (x - c) sqrt(pdf(x))`` + - ``vmax = sup (x - c) sqrt(pdf(x))`` + + In particular, these values are finite if `pdf` is bounded and + ``x**2 * pdf(x)`` is bounded (i.e. subquadratic tails). + One can generate ``(U, V)`` uniformly on ``R`` and return + ``V/U + c`` if ``(U, V)`` are also in ``A`` which can be directly + verified. + + The algorithm is not changed if one replaces `pdf` by k * `pdf` for any + constant k > 0. Thus, it is often convenient to work with a function + that is proportional to the probability density function by dropping + unnecessary normalization factors. + + Intuitively, the method works well if ``A`` fills up most of the + enclosing rectangle such that the probability is high that ``(U, V)`` + lies in ``A`` whenever it lies in ``R`` as the number of required + iterations becomes too large otherwise. To be more precise, note that + the expected number of iterations to draw ``(U, V)`` uniformly + distributed on ``R`` such that ``(U, V)`` is also in ``A`` is given by + the ratio ``area(R) / area(A) = 2 * umax * (vmax - vmin) / area(pdf)``, + where `area(pdf)` is the integral of `pdf` (which is equal to one if the + probability density function is used but can take on other values if a + function proportional to the density is used). The equality holds since + the area of ``A`` is equal to ``0.5 * area(pdf)`` (Theorem 7.1 in [1]_). + If the sampling fails to generate a single random variate after 50000 + iterations (i.e. not a single draw is in ``A``), an exception is raised. + + If the bounding rectangle is not correctly specified (i.e. if it does not + contain ``A``), the algorithm samples from a distribution different from + the one given by `pdf`. It is therefore recommended to perform a + test such as `~scipy.stats.kstest` as a check. + + References + ---------- + .. [1] L. Devroye, "Non-Uniform Random Variate Generation", + Springer-Verlag, 1986. + + .. [2] W. Hoermann and J. Leydold, "Generating generalized inverse Gaussian + random variates", Statistics and Computing, 24(4), p. 547--557, 2014. + + .. [3] A.J. Kinderman and J.F. Monahan, "Computer Generation of Random + Variables Using the Ratio of Uniform Deviates", + ACM Transactions on Mathematical Software, 3(3), p. 257--260, 1977. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + + >>> from scipy.stats.sampling import RatioUniforms + >>> rng = np.random.default_rng() + + Simulate normally distributed random variables. It is easy to compute the + bounding rectangle explicitly in that case. For simplicity, we drop the + normalization factor of the density. + + >>> f = lambda x: np.exp(-x**2 / 2) + >>> v = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) + >>> umax = np.sqrt(f(0)) + >>> gen = RatioUniforms(f, umax=umax, vmin=-v, vmax=v, random_state=rng) + >>> r = gen.rvs(size=2500) + + The K-S test confirms that the random variates are indeed normally + distributed (normality is not rejected at 5% significance level): + + >>> stats.kstest(r, 'norm')[1] + 0.250634764150542 + + The exponential distribution provides another example where the bounding + rectangle can be determined explicitly. + + >>> gen = RatioUniforms(lambda x: np.exp(-x), umax=1, vmin=0, + ... vmax=2*np.exp(-1), random_state=rng) + >>> r = gen.rvs(1000) + >>> stats.kstest(r, 'expon')[1] + 0.21121052054580314 + + """ + + def __init__(self, pdf, *, umax, vmin, vmax, c=0, random_state=None): + if vmin >= vmax: + raise ValueError("vmin must be smaller than vmax.") + + if umax <= 0: + raise ValueError("umax must be positive.") + + self._pdf = pdf + self._umax = umax + self._vmin = vmin + self._vmax = vmax + self._c = c + self._rng = check_random_state(random_state) + + def rvs(self, size=1): + """Sampling of random variates + + Parameters + ---------- + size : int or tuple of ints, optional + Number of random variates to be generated (default is 1). + + Returns + ------- + rvs : ndarray + The random variates distributed according to the probability + distribution defined by the pdf. + + """ + size1d = tuple(np.atleast_1d(size)) + N = np.prod(size1d) # number of rvs needed, reshape upon return + + # start sampling using ratio of uniforms method + x = np.zeros(N) + simulated, i = 0, 1 + + # loop until N rvs have been generated: expected runtime is finite. + # to avoid infinite loop, raise exception if not a single rv has been + # generated after 50000 tries. even if the expected number of iterations + # is 1000, the probability of this event is (1-1/1000)**50000 + # which is of order 10e-22 + while simulated < N: + k = N - simulated + # simulate uniform rvs on [0, umax] and [vmin, vmax] + u1 = self._umax * self._rng.uniform(size=k) + v1 = self._rng.uniform(self._vmin, self._vmax, size=k) + # apply rejection method + rvs = v1 / u1 + self._c + accept = (u1**2 <= self._pdf(rvs)) + num_accept = np.sum(accept) + if num_accept > 0: + x[simulated:(simulated + num_accept)] = rvs[accept] + simulated += num_accept + + if (simulated == 0) and (i*N >= 50000): + msg = ( + f"Not a single random variate could be generated in {i*N} " + "attempts. The ratio of uniforms method does not appear " + "to work for the provided parameters. Please check the " + "pdf and the bounds." + ) + raise RuntimeError(msg) + i += 1 + + return np.reshape(x, size1d) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sensitivity_analysis.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sensitivity_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..7baffcbdccb58aa453f5ddf6b83da4730c979e06 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sensitivity_analysis.py @@ -0,0 +1,712 @@ +from __future__ import annotations + +import inspect +from dataclasses import dataclass +from typing import ( + Callable, Literal, Protocol, TYPE_CHECKING +) + +import numpy as np + +from scipy.stats._common import ConfidenceInterval +from scipy.stats._qmc import check_random_state +from scipy.stats._resampling import BootstrapResult +from scipy.stats import qmc, bootstrap + + +if TYPE_CHECKING: + import numpy.typing as npt + from scipy._lib._util import DecimalNumber, IntNumber, SeedType + + +__all__ = [ + 'sobol_indices' +] + + +def f_ishigami(x: npt.ArrayLike) -> np.ndarray: + r"""Ishigami function. + + .. math:: + + Y(\mathbf{x}) = \sin x_1 + 7 \sin^2 x_2 + 0.1 x_3^4 \sin x_1 + + with :math:`\mathbf{x} \in [-\pi, \pi]^3`. + + Parameters + ---------- + x : array_like ([x1, x2, x3], n) + + Returns + ------- + f : array_like (n,) + Function evaluation. + + References + ---------- + .. [1] Ishigami, T. and T. Homma. "An importance quantification technique + in uncertainty analysis for computer models." IEEE, + :doi:`10.1109/ISUMA.1990.151285`, 1990. + """ + x = np.atleast_2d(x) + f_eval = ( + np.sin(x[0]) + + 7 * np.sin(x[1])**2 + + 0.1 * (x[2]**4) * np.sin(x[0]) + ) + return f_eval + + +def sample_A_B( + n: IntNumber, + dists: list[PPFDist], + random_state: SeedType = None +) -> np.ndarray: + """Sample two matrices A and B. + + Uses a Sobol' sequence with 2`d` columns to have 2 uncorrelated matrices. + This is more efficient than using 2 random draw of Sobol'. + See sec. 5 from [1]_. + + Output shape is (d, n). + + References + ---------- + .. [1] Saltelli, A., P. Annoni, I. Azzini, F. Campolongo, M. Ratto, and + S. Tarantola. "Variance based sensitivity analysis of model + output. Design and estimator for the total sensitivity index." + Computer Physics Communications, 181(2):259-270, + :doi:`10.1016/j.cpc.2009.09.018`, 2010. + """ + d = len(dists) + A_B = qmc.Sobol(d=2*d, seed=random_state, bits=64).random(n).T + A_B = A_B.reshape(2, d, -1) + try: + for d_, dist in enumerate(dists): + A_B[:, d_] = dist.ppf(A_B[:, d_]) + except AttributeError as exc: + message = "Each distribution in `dists` must have method `ppf`." + raise ValueError(message) from exc + return A_B + + +def sample_AB(A: np.ndarray, B: np.ndarray) -> np.ndarray: + """AB matrix. + + AB: rows of B into A. Shape (d, d, n). + - Copy A into d "pages" + - In the first page, replace 1st rows of A with 1st row of B. + ... + - In the dth page, replace dth row of A with dth row of B. + - return the stack of pages + """ + d, n = A.shape + AB = np.tile(A, (d, 1, 1)) + i = np.arange(d) + AB[i, i] = B[i] + return AB + + +def saltelli_2010( + f_A: np.ndarray, f_B: np.ndarray, f_AB: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: + r"""Saltelli2010 formulation. + + .. math:: + + S_i = \frac{1}{N} \sum_{j=1}^N + f(\mathbf{B})_j (f(\mathbf{AB}^{(i)})_j - f(\mathbf{A})_j) + + .. math:: + + S_{T_i} = \frac{1}{N} \sum_{j=1}^N + (f(\mathbf{A})_j - f(\mathbf{AB}^{(i)})_j)^2 + + Parameters + ---------- + f_A, f_B : array_like (s, n) + Function values at A and B, respectively + f_AB : array_like (d, s, n) + Function values at each of the AB pages + + Returns + ------- + s, st : array_like (s, d) + First order and total order Sobol' indices. + + References + ---------- + .. [1] Saltelli, A., P. Annoni, I. Azzini, F. Campolongo, M. Ratto, and + S. Tarantola. "Variance based sensitivity analysis of model + output. Design and estimator for the total sensitivity index." + Computer Physics Communications, 181(2):259-270, + :doi:`10.1016/j.cpc.2009.09.018`, 2010. + """ + # Empirical variance calculated using output from A and B which are + # independent. Output of AB is not independent and cannot be used + var = np.var([f_A, f_B], axis=(0, -1)) + + # We divide by the variance to have a ratio of variance + # this leads to eq. 2 + s = np.mean(f_B * (f_AB - f_A), axis=-1) / var # Table 2 (b) + st = 0.5 * np.mean((f_A - f_AB) ** 2, axis=-1) / var # Table 2 (f) + + return s.T, st.T + + +@dataclass +class BootstrapSobolResult: + first_order: BootstrapResult + total_order: BootstrapResult + + +@dataclass +class SobolResult: + first_order: np.ndarray + total_order: np.ndarray + _indices_method: Callable + _f_A: np.ndarray + _f_B: np.ndarray + _f_AB: np.ndarray + _A: np.ndarray | None = None + _B: np.ndarray | None = None + _AB: np.ndarray | None = None + _bootstrap_result: BootstrapResult | None = None + + def bootstrap( + self, + confidence_level: DecimalNumber = 0.95, + n_resamples: IntNumber = 999 + ) -> BootstrapSobolResult: + """Bootstrap Sobol' indices to provide confidence intervals. + + Parameters + ---------- + confidence_level : float, default: ``0.95`` + The confidence level of the confidence intervals. + n_resamples : int, default: ``999`` + The number of resamples performed to form the bootstrap + distribution of the indices. + + Returns + ------- + res : BootstrapSobolResult + Bootstrap result containing the confidence intervals and the + bootstrap distribution of the indices. + + An object with attributes: + + first_order : BootstrapResult + Bootstrap result of the first order indices. + total_order : BootstrapResult + Bootstrap result of the total order indices. + See `BootstrapResult` for more details. + + """ + def statistic(idx): + f_A_ = self._f_A[:, idx] + f_B_ = self._f_B[:, idx] + f_AB_ = self._f_AB[..., idx] + return self._indices_method(f_A_, f_B_, f_AB_) + + n = self._f_A.shape[1] + + res = bootstrap( + [np.arange(n)], statistic=statistic, method="BCa", + n_resamples=n_resamples, + confidence_level=confidence_level, + bootstrap_result=self._bootstrap_result + ) + self._bootstrap_result = res + + first_order = BootstrapResult( + confidence_interval=ConfidenceInterval( + res.confidence_interval.low[0], res.confidence_interval.high[0] + ), + bootstrap_distribution=res.bootstrap_distribution[0], + standard_error=res.standard_error[0], + ) + total_order = BootstrapResult( + confidence_interval=ConfidenceInterval( + res.confidence_interval.low[1], res.confidence_interval.high[1] + ), + bootstrap_distribution=res.bootstrap_distribution[1], + standard_error=res.standard_error[1], + ) + + return BootstrapSobolResult( + first_order=first_order, total_order=total_order + ) + + +class PPFDist(Protocol): + @property + def ppf(self) -> Callable[..., float]: + ... + + +def sobol_indices( + *, + func: Callable[[np.ndarray], npt.ArrayLike] | + dict[Literal['f_A', 'f_B', 'f_AB'], np.ndarray], + n: IntNumber, + dists: list[PPFDist] | None = None, + method: Callable | Literal['saltelli_2010'] = 'saltelli_2010', + random_state: SeedType = None +) -> SobolResult: + r"""Global sensitivity indices of Sobol'. + + Parameters + ---------- + func : callable or dict(str, array_like) + If `func` is a callable, function to compute the Sobol' indices from. + Its signature must be:: + + func(x: ArrayLike) -> ArrayLike + + with ``x`` of shape ``(d, n)`` and output of shape ``(s, n)`` where: + + - ``d`` is the input dimensionality of `func` + (number of input variables), + - ``s`` is the output dimensionality of `func` + (number of output variables), and + - ``n`` is the number of samples (see `n` below). + + Function evaluation values must be finite. + + If `func` is a dictionary, contains the function evaluations from three + different arrays. Keys must be: ``f_A``, ``f_B`` and ``f_AB``. + ``f_A`` and ``f_B`` should have a shape ``(s, n)`` and ``f_AB`` + should have a shape ``(d, s, n)``. + This is an advanced feature and misuse can lead to wrong analysis. + n : int + Number of samples used to generate the matrices ``A`` and ``B``. + Must be a power of 2. The total number of points at which `func` is + evaluated will be ``n*(d+2)``. + dists : list(distributions), optional + List of each parameter's distribution. The distribution of parameters + depends on the application and should be carefully chosen. + Parameters are assumed to be independently distributed, meaning there + is no constraint nor relationship between their values. + + Distributions must be an instance of a class with a ``ppf`` + method. + + Must be specified if `func` is a callable, and ignored otherwise. + method : Callable or str, default: 'saltelli_2010' + Method used to compute the first and total Sobol' indices. + + If a callable, its signature must be:: + + func(f_A: np.ndarray, f_B: np.ndarray, f_AB: np.ndarray) + -> Tuple[np.ndarray, np.ndarray] + + with ``f_A, f_B`` of shape ``(s, n)`` and ``f_AB`` of shape + ``(d, s, n)``. + These arrays contain the function evaluations from three different sets + of samples. + The output is a tuple of the first and total indices with + shape ``(s, d)``. + This is an advanced feature and misuse can lead to wrong analysis. + random_state : {None, int, `numpy.random.Generator`}, optional + If `random_state` is an int or None, a new `numpy.random.Generator` is + created using ``np.random.default_rng(random_state)``. + If `random_state` is already a ``Generator`` instance, then the + provided instance is used. + + Returns + ------- + res : SobolResult + An object with attributes: + + first_order : ndarray of shape (s, d) + First order Sobol' indices. + total_order : ndarray of shape (s, d) + Total order Sobol' indices. + + And method: + + bootstrap(confidence_level: float, n_resamples: int) + -> BootstrapSobolResult + + A method providing confidence intervals on the indices. + See `scipy.stats.bootstrap` for more details. + + The bootstrapping is done on both first and total order indices, + and they are available in `BootstrapSobolResult` as attributes + ``first_order`` and ``total_order``. + + Notes + ----- + The Sobol' method [1]_, [2]_ is a variance-based Sensitivity Analysis which + obtains the contribution of each parameter to the variance of the + quantities of interest (QoIs; i.e., the outputs of `func`). + Respective contributions can be used to rank the parameters and + also gauge the complexity of the model by computing the + model's effective (or mean) dimension. + + .. note:: + + Parameters are assumed to be independently distributed. Each + parameter can still follow any distribution. In fact, the distribution + is very important and should match the real distribution of the + parameters. + + It uses a functional decomposition of the variance of the function to + explore + + .. math:: + + \mathbb{V}(Y) = \sum_{i}^{d} \mathbb{V}_i (Y) + \sum_{i= 2**12``. The more complex the model is, + the more samples will be needed. + + Even for a purely addiditive model, the indices may not sum to 1 due + to numerical noise. + + References + ---------- + .. [1] Sobol, I. M.. "Sensitivity analysis for nonlinear mathematical + models." Mathematical Modeling and Computational Experiment, 1:407-414, + 1993. + .. [2] Sobol, I. M. (2001). "Global sensitivity indices for nonlinear + mathematical models and their Monte Carlo estimates." Mathematics + and Computers in Simulation, 55(1-3):271-280, + :doi:`10.1016/S0378-4754(00)00270-6`, 2001. + .. [3] Saltelli, A. "Making best use of model evaluations to + compute sensitivity indices." Computer Physics Communications, + 145(2):280-297, :doi:`10.1016/S0010-4655(02)00280-1`, 2002. + .. [4] Saltelli, A., M. Ratto, T. Andres, F. Campolongo, J. Cariboni, + D. Gatelli, M. Saisana, and S. Tarantola. "Global Sensitivity Analysis. + The Primer." 2007. + .. [5] Saltelli, A., P. Annoni, I. Azzini, F. Campolongo, M. Ratto, and + S. Tarantola. "Variance based sensitivity analysis of model + output. Design and estimator for the total sensitivity index." + Computer Physics Communications, 181(2):259-270, + :doi:`10.1016/j.cpc.2009.09.018`, 2010. + .. [6] Ishigami, T. and T. Homma. "An importance quantification technique + in uncertainty analysis for computer models." IEEE, + :doi:`10.1109/ISUMA.1990.151285`, 1990. + + Examples + -------- + The following is an example with the Ishigami function [6]_ + + .. math:: + + Y(\mathbf{x}) = \sin x_1 + 7 \sin^2 x_2 + 0.1 x_3^4 \sin x_1, + + with :math:`\mathbf{x} \in [-\pi, \pi]^3`. This function exhibits strong + non-linearity and non-monotonicity. + + Remember, Sobol' indices assumes that samples are independently + distributed. In this case we use a uniform distribution on each marginals. + + >>> import numpy as np + >>> from scipy.stats import sobol_indices, uniform + >>> rng = np.random.default_rng() + >>> def f_ishigami(x): + ... f_eval = ( + ... np.sin(x[0]) + ... + 7 * np.sin(x[1])**2 + ... + 0.1 * (x[2]**4) * np.sin(x[0]) + ... ) + ... return f_eval + >>> indices = sobol_indices( + ... func=f_ishigami, n=1024, + ... dists=[ + ... uniform(loc=-np.pi, scale=2*np.pi), + ... uniform(loc=-np.pi, scale=2*np.pi), + ... uniform(loc=-np.pi, scale=2*np.pi) + ... ], + ... random_state=rng + ... ) + >>> indices.first_order + array([0.31637954, 0.43781162, 0.00318825]) + >>> indices.total_order + array([0.56122127, 0.44287857, 0.24229595]) + + Confidence interval can be obtained using bootstrapping. + + >>> boot = indices.bootstrap() + + Then, this information can be easily visualized. + + >>> import matplotlib.pyplot as plt + >>> fig, axs = plt.subplots(1, 2, figsize=(9, 4)) + >>> _ = axs[0].errorbar( + ... [1, 2, 3], indices.first_order, fmt='o', + ... yerr=[ + ... indices.first_order - boot.first_order.confidence_interval.low, + ... boot.first_order.confidence_interval.high - indices.first_order + ... ], + ... ) + >>> axs[0].set_ylabel("First order Sobol' indices") + >>> axs[0].set_xlabel('Input parameters') + >>> axs[0].set_xticks([1, 2, 3]) + >>> _ = axs[1].errorbar( + ... [1, 2, 3], indices.total_order, fmt='o', + ... yerr=[ + ... indices.total_order - boot.total_order.confidence_interval.low, + ... boot.total_order.confidence_interval.high - indices.total_order + ... ], + ... ) + >>> axs[1].set_ylabel("Total order Sobol' indices") + >>> axs[1].set_xlabel('Input parameters') + >>> axs[1].set_xticks([1, 2, 3]) + >>> plt.tight_layout() + >>> plt.show() + + .. note:: + + By default, `scipy.stats.uniform` has support ``[0, 1]``. + Using the parameters ``loc`` and ``scale``, one obtains the uniform + distribution on ``[loc, loc + scale]``. + + This result is particularly interesting because the first order index + :math:`S_{x_3} = 0` whereas its total order is :math:`S_{T_{x_3}} = 0.244`. + This means that higher order interactions with :math:`x_3` are responsible + for the difference. Almost 25% of the observed variance + on the QoI is due to the correlations between :math:`x_3` and :math:`x_1`, + although :math:`x_3` by itself has no impact on the QoI. + + The following gives a visual explanation of Sobol' indices on this + function. Let's generate 1024 samples in :math:`[-\pi, \pi]^3` and + calculate the value of the output. + + >>> from scipy.stats import qmc + >>> n_dim = 3 + >>> p_labels = ['$x_1$', '$x_2$', '$x_3$'] + >>> sample = qmc.Sobol(d=n_dim, seed=rng).random(1024) + >>> sample = qmc.scale( + ... sample=sample, + ... l_bounds=[-np.pi, -np.pi, -np.pi], + ... u_bounds=[np.pi, np.pi, np.pi] + ... ) + >>> output = f_ishigami(sample.T) + + Now we can do scatter plots of the output with respect to each parameter. + This gives a visual way to understand how each parameter impacts the + output of the function. + + >>> fig, ax = plt.subplots(1, n_dim, figsize=(12, 4)) + >>> for i in range(n_dim): + ... xi = sample[:, i] + ... ax[i].scatter(xi, output, marker='+') + ... ax[i].set_xlabel(p_labels[i]) + >>> ax[0].set_ylabel('Y') + >>> plt.tight_layout() + >>> plt.show() + + Now Sobol' goes a step further: + by conditioning the output value by given values of the parameter + (black lines), the conditional output mean is computed. It corresponds to + the term :math:`\mathbb{E}(Y|x_i)`. Taking the variance of this term gives + the numerator of the Sobol' indices. + + >>> mini = np.min(output) + >>> maxi = np.max(output) + >>> n_bins = 10 + >>> bins = np.linspace(-np.pi, np.pi, num=n_bins, endpoint=False) + >>> dx = bins[1] - bins[0] + >>> fig, ax = plt.subplots(1, n_dim, figsize=(12, 4)) + >>> for i in range(n_dim): + ... xi = sample[:, i] + ... ax[i].scatter(xi, output, marker='+') + ... ax[i].set_xlabel(p_labels[i]) + ... for bin_ in bins: + ... idx = np.where((bin_ <= xi) & (xi <= bin_ + dx)) + ... xi_ = xi[idx] + ... y_ = output[idx] + ... ave_y_ = np.mean(y_) + ... ax[i].plot([bin_ + dx/2] * 2, [mini, maxi], c='k') + ... ax[i].scatter(bin_ + dx/2, ave_y_, c='r') + >>> ax[0].set_ylabel('Y') + >>> plt.tight_layout() + >>> plt.show() + + Looking at :math:`x_3`, the variance + of the mean is zero leading to :math:`S_{x_3} = 0`. But we can further + observe that the variance of the output is not constant along the parameter + values of :math:`x_3`. This heteroscedasticity is explained by higher order + interactions. Moreover, an heteroscedasticity is also noticeable on + :math:`x_1` leading to an interaction between :math:`x_3` and :math:`x_1`. + On :math:`x_2`, the variance seems to be constant and thus null interaction + with this parameter can be supposed. + + This case is fairly simple to analyse visually---although it is only a + qualitative analysis. Nevertheless, when the number of input parameters + increases such analysis becomes unrealistic as it would be difficult to + conclude on high-order terms. Hence the benefit of using Sobol' indices. + + """ + random_state = check_random_state(random_state) + + n_ = int(n) + if not (n_ & (n_ - 1) == 0) or n != n_: + raise ValueError( + "The balance properties of Sobol' points require 'n' " + "to be a power of 2." + ) + n = n_ + + if not callable(method): + indices_methods: dict[str, Callable] = { + "saltelli_2010": saltelli_2010, + } + try: + method = method.lower() # type: ignore[assignment] + indices_method_ = indices_methods[method] + except KeyError as exc: + message = ( + f"{method!r} is not a valid 'method'. It must be one of" + f" {set(indices_methods)!r} or a callable." + ) + raise ValueError(message) from exc + else: + indices_method_ = method + sig = inspect.signature(indices_method_) + + if set(sig.parameters) != {'f_A', 'f_B', 'f_AB'}: + message = ( + "If 'method' is a callable, it must have the following" + f" signature: {inspect.signature(saltelli_2010)}" + ) + raise ValueError(message) + + def indices_method(f_A, f_B, f_AB): + """Wrap indices method to ensure proper output dimension. + + 1D when single output, 2D otherwise. + """ + return np.squeeze(indices_method_(f_A=f_A, f_B=f_B, f_AB=f_AB)) + + if callable(func): + if dists is None: + raise ValueError( + "'dists' must be defined when 'func' is a callable." + ) + + def wrapped_func(x): + return np.atleast_2d(func(x)) + + A, B = sample_A_B(n=n, dists=dists, random_state=random_state) + AB = sample_AB(A=A, B=B) + + f_A = wrapped_func(A) + + if f_A.shape[1] != n: + raise ValueError( + "'func' output should have a shape ``(s, -1)`` with ``s`` " + "the number of output." + ) + + def funcAB(AB): + d, d, n = AB.shape + AB = np.moveaxis(AB, 0, -1).reshape(d, n*d) + f_AB = wrapped_func(AB) + return np.moveaxis(f_AB.reshape((-1, n, d)), -1, 0) + + f_B = wrapped_func(B) + f_AB = funcAB(AB) + else: + message = ( + "When 'func' is a dictionary, it must contain the following " + "keys: 'f_A', 'f_B' and 'f_AB'." + "'f_A' and 'f_B' should have a shape ``(s, n)`` and 'f_AB' " + "should have a shape ``(d, s, n)``." + ) + try: + f_A, f_B, f_AB = np.atleast_2d( + func['f_A'], func['f_B'], func['f_AB'] + ) + except KeyError as exc: + raise ValueError(message) from exc + + if f_A.shape[1] != n or f_A.shape != f_B.shape or \ + f_AB.shape == f_A.shape or f_AB.shape[-1] % n != 0: + raise ValueError(message) + + # Normalization by mean + # Sobol', I. and Levitan, Y. L. (1999). On the use of variance reducing + # multipliers in monte carlo computations of a global sensitivity index. + # Computer Physics Communications, 117(1) :52-61. + mean = np.mean([f_A, f_B], axis=(0, -1)).reshape(-1, 1) + f_A -= mean + f_B -= mean + f_AB -= mean + + # Compute indices + # Filter warnings for constant output as var = 0 + with np.errstate(divide='ignore', invalid='ignore'): + first_order, total_order = indices_method(f_A=f_A, f_B=f_B, f_AB=f_AB) + + # null variance means null indices + first_order[~np.isfinite(first_order)] = 0 + total_order[~np.isfinite(total_order)] = 0 + + res = dict( + first_order=first_order, + total_order=total_order, + _indices_method=indices_method, + _f_A=f_A, + _f_B=f_B, + _f_AB=f_AB + ) + + if callable(func): + res.update( + dict( + _A=A, + _B=B, + _AB=AB, + ) + ) + + return SobolResult(**res) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sobol.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sobol.pyi new file mode 100644 index 0000000000000000000000000000000000000000..7ca5e3a9c1a142b25ac26401e9ab1cb6726c877f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_sobol.pyi @@ -0,0 +1,54 @@ +import numpy as np +from scipy._lib._util import IntNumber +from typing import Literal + +def _initialize_v( + v : np.ndarray, + dim : IntNumber, + bits: IntNumber +) -> None: ... + +def _cscramble ( + dim : IntNumber, + bits: IntNumber, + ltm : np.ndarray, + sv: np.ndarray +) -> None: ... + +def _fill_p_cumulative( + p: np.ndarray, + p_cumulative: np.ndarray +) -> None: ... + +def _draw( + n : IntNumber, + num_gen: IntNumber, + dim: IntNumber, + scale: float, + sv: np.ndarray, + quasi: np.ndarray, + sample: np.ndarray + ) -> None: ... + +def _fast_forward( + n: IntNumber, + num_gen: IntNumber, + dim: IntNumber, + sv: np.ndarray, + quasi: np.ndarray + ) -> None: ... + +def _categorize( + draws: np.ndarray, + p_cumulative: np.ndarray, + result: np.ndarray + ) -> None: ... + +_MAXDIM: Literal[21201] +_MAXDEG: Literal[18] + +def _test_find_index( + p_cumulative: np.ndarray, + size: int, + value: float + ) -> int: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats.pxd b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats.pxd new file mode 100644 index 0000000000000000000000000000000000000000..a7085943001b0b8fd2891f504c01c35ded7f7fb0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats.pxd @@ -0,0 +1,9 @@ +# destined to be used in a LowLevelCallable +cdef double _geninvgauss_pdf(double x, void *user_data) except * nogil +cdef double _studentized_range_cdf(int n, double[2] x, void *user_data) noexcept nogil +cdef double _studentized_range_cdf_asymptotic(double z, void *user_data) noexcept nogil +cdef double _studentized_range_pdf(int n, double[2] x, void *user_data) noexcept nogil +cdef double _studentized_range_pdf_asymptotic(double z, void *user_data) noexcept nogil +cdef double _studentized_range_moment(int n, double[3] x_arg, void *user_data) noexcept nogil +cdef double _genhyperbolic_pdf(double x, void *user_data) except * nogil +cdef double _genhyperbolic_logpdf(double x, void *user_data) except * nogil diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_mstats_common.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_mstats_common.py new file mode 100644 index 0000000000000000000000000000000000000000..35e426ec2e31611793a45e817964cfe79d75173d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_mstats_common.py @@ -0,0 +1,499 @@ +import warnings +import numpy as np +from . import distributions +from .._lib._bunch import _make_tuple_bunch +from ._stats_pythran import siegelslopes as siegelslopes_pythran +from . import _mstats_basic + +__all__ = ['_find_repeats', 'linregress', 'theilslopes', 'siegelslopes'] + +# This is not a namedtuple for backwards compatibility. See PR #12983 +LinregressResult = _make_tuple_bunch('LinregressResult', + ['slope', 'intercept', 'rvalue', + 'pvalue', 'stderr'], + extra_field_names=['intercept_stderr']) +TheilslopesResult = _make_tuple_bunch('TheilslopesResult', + ['slope', 'intercept', + 'low_slope', 'high_slope']) +SiegelslopesResult = _make_tuple_bunch('SiegelslopesResult', + ['slope', 'intercept']) + + +def linregress(x, y=None, alternative='two-sided'): + """ + Calculate a linear least-squares regression for two sets of measurements. + + Parameters + ---------- + x, y : array_like + Two sets of measurements. Both arrays should have the same length. If + only `x` is given (and ``y=None``), then it must be a two-dimensional + array where one dimension has length 2. The two sets of measurements + are then found by splitting the array along the length-2 dimension. In + the case where ``y=None`` and `x` is a 2x2 array, ``linregress(x)`` is + equivalent to ``linregress(x[0], x[1])``. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the slope of the regression line is nonzero + * 'less': the slope of the regression line is less than zero + * 'greater': the slope of the regression line is greater than zero + + .. versionadded:: 1.7.0 + + Returns + ------- + result : ``LinregressResult`` instance + The return value is an object with the following attributes: + + slope : float + Slope of the regression line. + intercept : float + Intercept of the regression line. + rvalue : float + The Pearson correlation coefficient. The square of ``rvalue`` + is equal to the coefficient of determination. + pvalue : float + The p-value for a hypothesis test whose null hypothesis is + that the slope is zero, using Wald Test with t-distribution of + the test statistic. See `alternative` above for alternative + hypotheses. + stderr : float + Standard error of the estimated slope (gradient), under the + assumption of residual normality. + intercept_stderr : float + Standard error of the estimated intercept, under the assumption + of residual normality. + + See Also + -------- + scipy.optimize.curve_fit : + Use non-linear least squares to fit a function to data. + scipy.optimize.leastsq : + Minimize the sum of squares of a set of equations. + + Notes + ----- + Missing values are considered pair-wise: if a value is missing in `x`, + the corresponding value in `y` is masked. + + For compatibility with older versions of SciPy, the return value acts + like a ``namedtuple`` of length 5, with fields ``slope``, ``intercept``, + ``rvalue``, ``pvalue`` and ``stderr``, so one can continue to write:: + + slope, intercept, r, p, se = linregress(x, y) + + With that style, however, the standard error of the intercept is not + available. To have access to all the computed values, including the + standard error of the intercept, use the return value as an object + with attributes, e.g.:: + + result = linregress(x, y) + print(result.intercept, result.intercept_stderr) + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy import stats + >>> rng = np.random.default_rng() + + Generate some data: + + >>> x = rng.random(10) + >>> y = 1.6*x + rng.random(10) + + Perform the linear regression: + + >>> res = stats.linregress(x, y) + + Coefficient of determination (R-squared): + + >>> print(f"R-squared: {res.rvalue**2:.6f}") + R-squared: 0.717533 + + Plot the data along with the fitted line: + + >>> plt.plot(x, y, 'o', label='original data') + >>> plt.plot(x, res.intercept + res.slope*x, 'r', label='fitted line') + >>> plt.legend() + >>> plt.show() + + Calculate 95% confidence interval on slope and intercept: + + >>> # Two-sided inverse Students t-distribution + >>> # p - probability, df - degrees of freedom + >>> from scipy.stats import t + >>> tinv = lambda p, df: abs(t.ppf(p/2, df)) + + >>> ts = tinv(0.05, len(x)-2) + >>> print(f"slope (95%): {res.slope:.6f} +/- {ts*res.stderr:.6f}") + slope (95%): 1.453392 +/- 0.743465 + >>> print(f"intercept (95%): {res.intercept:.6f}" + ... f" +/- {ts*res.intercept_stderr:.6f}") + intercept (95%): 0.616950 +/- 0.544475 + + """ + TINY = 1.0e-20 + if y is None: # x is a (2, N) or (N, 2) shaped array_like + x = np.asarray(x) + if x.shape[0] == 2: + x, y = x + elif x.shape[1] == 2: + x, y = x.T + else: + raise ValueError("If only `x` is given as input, it has to " + "be of shape (2, N) or (N, 2); provided shape " + f"was {x.shape}.") + else: + x = np.asarray(x) + y = np.asarray(y) + + if x.size == 0 or y.size == 0: + raise ValueError("Inputs must not be empty.") + + if np.amax(x) == np.amin(x) and len(x) > 1: + raise ValueError("Cannot calculate a linear regression " + "if all x values are identical") + + n = len(x) + xmean = np.mean(x, None) + ymean = np.mean(y, None) + + # Average sums of square differences from the mean + # ssxm = mean( (x-mean(x))^2 ) + # ssxym = mean( (x-mean(x)) * (y-mean(y)) ) + ssxm, ssxym, _, ssym = np.cov(x, y, bias=1).flat + + # R-value + # r = ssxym / sqrt( ssxm * ssym ) + if ssxm == 0.0 or ssym == 0.0: + # If the denominator was going to be 0 + r = 0.0 + else: + r = ssxym / np.sqrt(ssxm * ssym) + # Test for numerical error propagation (make sure -1 < r < 1) + if r > 1.0: + r = 1.0 + elif r < -1.0: + r = -1.0 + + slope = ssxym / ssxm + intercept = ymean - slope*xmean + if n == 2: + # handle case when only two points are passed in + if y[0] == y[1]: + prob = 1.0 + else: + prob = 0.0 + slope_stderr = 0.0 + intercept_stderr = 0.0 + else: + df = n - 2 # Number of degrees of freedom + # n-2 degrees of freedom because 2 has been used up + # to estimate the mean and standard deviation + t = r * np.sqrt(df / ((1.0 - r + TINY)*(1.0 + r + TINY))) + t, prob = _mstats_basic._ttest_finish(df, t, alternative) + + slope_stderr = np.sqrt((1 - r**2) * ssym / ssxm / df) + + # Also calculate the standard error of the intercept + # The following relationship is used: + # ssxm = mean( (x-mean(x))^2 ) + # = ssx - sx*sx + # = mean( x^2 ) - mean(x)^2 + intercept_stderr = slope_stderr * np.sqrt(ssxm + xmean**2) + + return LinregressResult(slope=slope, intercept=intercept, rvalue=r, + pvalue=prob, stderr=slope_stderr, + intercept_stderr=intercept_stderr) + + +def theilslopes(y, x=None, alpha=0.95, method='separate'): + r""" + Computes the Theil-Sen estimator for a set of points (x, y). + + `theilslopes` implements a method for robust linear regression. It + computes the slope as the median of all slopes between paired values. + + Parameters + ---------- + y : array_like + Dependent variable. + x : array_like or None, optional + Independent variable. If None, use ``arange(len(y))`` instead. + alpha : float, optional + Confidence degree between 0 and 1. Default is 95% confidence. + Note that `alpha` is symmetric around 0.5, i.e. both 0.1 and 0.9 are + interpreted as "find the 90% confidence interval". + method : {'joint', 'separate'}, optional + Method to be used for computing estimate for intercept. + Following methods are supported, + + * 'joint': Uses np.median(y - slope * x) as intercept. + * 'separate': Uses np.median(y) - slope * np.median(x) + as intercept. + + The default is 'separate'. + + .. versionadded:: 1.8.0 + + Returns + ------- + result : ``TheilslopesResult`` instance + The return value is an object with the following attributes: + + slope : float + Theil slope. + intercept : float + Intercept of the Theil line. + low_slope : float + Lower bound of the confidence interval on `slope`. + high_slope : float + Upper bound of the confidence interval on `slope`. + + See Also + -------- + siegelslopes : a similar technique using repeated medians + + Notes + ----- + The implementation of `theilslopes` follows [1]_. The intercept is + not defined in [1]_, and here it is defined as ``median(y) - + slope*median(x)``, which is given in [3]_. Other definitions of + the intercept exist in the literature such as ``median(y - slope*x)`` + in [4]_. The approach to compute the intercept can be determined by the + parameter ``method``. A confidence interval for the intercept is not + given as this question is not addressed in [1]_. + + For compatibility with older versions of SciPy, the return value acts + like a ``namedtuple`` of length 4, with fields ``slope``, ``intercept``, + ``low_slope``, and ``high_slope``, so one can continue to write:: + + slope, intercept, low_slope, high_slope = theilslopes(y, x) + + References + ---------- + .. [1] P.K. Sen, "Estimates of the regression coefficient based on + Kendall's tau", J. Am. Stat. Assoc., Vol. 63, pp. 1379-1389, 1968. + .. [2] H. Theil, "A rank-invariant method of linear and polynomial + regression analysis I, II and III", Nederl. Akad. Wetensch., Proc. + 53:, pp. 386-392, pp. 521-525, pp. 1397-1412, 1950. + .. [3] W.L. Conover, "Practical nonparametric statistics", 2nd ed., + John Wiley and Sons, New York, pp. 493. + .. [4] https://en.wikipedia.org/wiki/Theil%E2%80%93Sen_estimator + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + >>> x = np.linspace(-5, 5, num=150) + >>> y = x + np.random.normal(size=x.size) + >>> y[11:15] += 10 # add outliers + >>> y[-5:] -= 7 + + Compute the slope, intercept and 90% confidence interval. For comparison, + also compute the least-squares fit with `linregress`: + + >>> res = stats.theilslopes(y, x, 0.90, method='separate') + >>> lsq_res = stats.linregress(x, y) + + Plot the results. The Theil-Sen regression line is shown in red, with the + dashed red lines illustrating the confidence interval of the slope (note + that the dashed red lines are not the confidence interval of the regression + as the confidence interval of the intercept is not included). The green + line shows the least-squares fit for comparison. + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(x, y, 'b.') + >>> ax.plot(x, res[1] + res[0] * x, 'r-') + >>> ax.plot(x, res[1] + res[2] * x, 'r--') + >>> ax.plot(x, res[1] + res[3] * x, 'r--') + >>> ax.plot(x, lsq_res[1] + lsq_res[0] * x, 'g-') + >>> plt.show() + + """ + if method not in ['joint', 'separate']: + raise ValueError("method must be either 'joint' or 'separate'." + f"'{method}' is invalid.") + # We copy both x and y so we can use _find_repeats. + y = np.array(y, dtype=float, copy=True).ravel() + if x is None: + x = np.arange(len(y), dtype=float) + else: + x = np.array(x, dtype=float, copy=True).ravel() + if len(x) != len(y): + raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") + + # Compute sorted slopes only when deltax > 0 + deltax = x[:, np.newaxis] - x + deltay = y[:, np.newaxis] - y + slopes = deltay[deltax > 0] / deltax[deltax > 0] + if not slopes.size: + msg = "All `x` coordinates are identical." + warnings.warn(msg, RuntimeWarning, stacklevel=2) + slopes.sort() + medslope = np.median(slopes) + if method == 'joint': + medinter = np.median(y - medslope * x) + else: + medinter = np.median(y) - medslope * np.median(x) + # Now compute confidence intervals + if alpha > 0.5: + alpha = 1. - alpha + + z = distributions.norm.ppf(alpha / 2.) + # This implements (2.6) from Sen (1968) + _, nxreps = _find_repeats(x) + _, nyreps = _find_repeats(y) + nt = len(slopes) # N in Sen (1968) + ny = len(y) # n in Sen (1968) + # Equation 2.6 in Sen (1968): + sigsq = 1/18. * (ny * (ny-1) * (2*ny+5) - + sum(k * (k-1) * (2*k + 5) for k in nxreps) - + sum(k * (k-1) * (2*k + 5) for k in nyreps)) + # Find the confidence interval indices in `slopes` + try: + sigma = np.sqrt(sigsq) + Ru = min(int(np.round((nt - z*sigma)/2.)), len(slopes)-1) + Rl = max(int(np.round((nt + z*sigma)/2.)) - 1, 0) + delta = slopes[[Rl, Ru]] + except (ValueError, IndexError): + delta = (np.nan, np.nan) + + return TheilslopesResult(slope=medslope, intercept=medinter, + low_slope=delta[0], high_slope=delta[1]) + + +def _find_repeats(arr): + # This function assumes it may clobber its input. + if len(arr) == 0: + return np.array(0, np.float64), np.array(0, np.intp) + + # XXX This cast was previously needed for the Fortran implementation, + # should we ditch it? + arr = np.asarray(arr, np.float64).ravel() + arr.sort() + + # Taken from NumPy 1.9's np.unique. + change = np.concatenate(([True], arr[1:] != arr[:-1])) + unique = arr[change] + change_idx = np.concatenate(np.nonzero(change) + ([arr.size],)) + freq = np.diff(change_idx) + atleast2 = freq > 1 + return unique[atleast2], freq[atleast2] + + +def siegelslopes(y, x=None, method="hierarchical"): + r""" + Computes the Siegel estimator for a set of points (x, y). + + `siegelslopes` implements a method for robust linear regression + using repeated medians (see [1]_) to fit a line to the points (x, y). + The method is robust to outliers with an asymptotic breakdown point + of 50%. + + Parameters + ---------- + y : array_like + Dependent variable. + x : array_like or None, optional + Independent variable. If None, use ``arange(len(y))`` instead. + method : {'hierarchical', 'separate'} + If 'hierarchical', estimate the intercept using the estimated + slope ``slope`` (default option). + If 'separate', estimate the intercept independent of the estimated + slope. See Notes for details. + + Returns + ------- + result : ``SiegelslopesResult`` instance + The return value is an object with the following attributes: + + slope : float + Estimate of the slope of the regression line. + intercept : float + Estimate of the intercept of the regression line. + + See Also + -------- + theilslopes : a similar technique without repeated medians + + Notes + ----- + With ``n = len(y)``, compute ``m_j`` as the median of + the slopes from the point ``(x[j], y[j])`` to all other `n-1` points. + ``slope`` is then the median of all slopes ``m_j``. + Two ways are given to estimate the intercept in [1]_ which can be chosen + via the parameter ``method``. + The hierarchical approach uses the estimated slope ``slope`` + and computes ``intercept`` as the median of ``y - slope*x``. + The other approach estimates the intercept separately as follows: for + each point ``(x[j], y[j])``, compute the intercepts of all the `n-1` + lines through the remaining points and take the median ``i_j``. + ``intercept`` is the median of the ``i_j``. + + The implementation computes `n` times the median of a vector of size `n` + which can be slow for large vectors. There are more efficient algorithms + (see [2]_) which are not implemented here. + + For compatibility with older versions of SciPy, the return value acts + like a ``namedtuple`` of length 2, with fields ``slope`` and + ``intercept``, so one can continue to write:: + + slope, intercept = siegelslopes(y, x) + + References + ---------- + .. [1] A. Siegel, "Robust Regression Using Repeated Medians", + Biometrika, Vol. 69, pp. 242-244, 1982. + + .. [2] A. Stein and M. Werman, "Finding the repeated median regression + line", Proceedings of the Third Annual ACM-SIAM Symposium on + Discrete Algorithms, pp. 409-413, 1992. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> import matplotlib.pyplot as plt + + >>> x = np.linspace(-5, 5, num=150) + >>> y = x + np.random.normal(size=x.size) + >>> y[11:15] += 10 # add outliers + >>> y[-5:] -= 7 + + Compute the slope and intercept. For comparison, also compute the + least-squares fit with `linregress`: + + >>> res = stats.siegelslopes(y, x) + >>> lsq_res = stats.linregress(x, y) + + Plot the results. The Siegel regression line is shown in red. The green + line shows the least-squares fit for comparison. + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(111) + >>> ax.plot(x, y, 'b.') + >>> ax.plot(x, res[1] + res[0] * x, 'r-') + >>> ax.plot(x, lsq_res[1] + lsq_res[0] * x, 'g-') + >>> plt.show() + + """ + if method not in ['hierarchical', 'separate']: + raise ValueError("method can only be 'hierarchical' or 'separate'") + y = np.asarray(y).ravel() + if x is None: + x = np.arange(len(y), dtype=float) + else: + x = np.asarray(x, dtype=float).ravel() + if len(x) != len(y): + raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") + dtype = np.result_type(x, y, np.float32) # use at least float32 + y, x = y.astype(dtype), x.astype(dtype) + medslope, medinter = siegelslopes_pythran(y, x, method) + return SiegelslopesResult(slope=medslope, intercept=medinter) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_py.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_py.py new file mode 100644 index 0000000000000000000000000000000000000000..a0bcb13c74b2b1b9bb75dd1ecad59dbab08ac249 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_stats_py.py @@ -0,0 +1,11053 @@ +# Copyright 2002 Gary Strangman. All rights reserved +# Copyright 2002-2016 The SciPy Developers +# +# The original code from Gary Strangman was heavily adapted for +# use in SciPy by Travis Oliphant. The original code came with the +# following disclaimer: +# +# This software is provided "as-is". There are no expressed or implied +# warranties of any kind, including, but not limited to, the warranties +# of merchantability and fitness for a given application. In no event +# shall Gary Strangman be liable for any direct, indirect, incidental, +# special, exemplary or consequential damages (including, but not limited +# to, loss of use, data or profits, or business interruption) however +# caused and on any theory of liability, whether in contract, strict +# liability or tort (including negligence or otherwise) arising in any way +# out of the use of this software, even if advised of the possibility of +# such damage. + +""" +A collection of basic statistical functions for Python. + +References +---------- +.. [CRCProbStat2000] Zwillinger, D. and Kokoska, S. (2000). CRC Standard + Probability and Statistics Tables and Formulae. Chapman & Hall: New + York. 2000. + +""" +import warnings +import math +from math import gcd +from collections import namedtuple + +import numpy as np +from numpy import array, asarray, ma + +from scipy import sparse +from scipy.spatial.distance import cdist +from scipy.spatial import distance_matrix + +from scipy.ndimage import _measurements +from scipy.optimize import milp, LinearConstraint +from scipy._lib._util import (check_random_state, MapWrapper, _get_nan, + rng_integers, _rename_parameter, _contains_nan, + AxisError) + +import scipy.special as special +from scipy import linalg +from . import distributions +from . import _mstats_basic as mstats_basic +from ._stats_mstats_common import (_find_repeats, linregress, theilslopes, + siegelslopes) +from ._stats import (_kendall_dis, _toint64, _weightedrankedtau, + _local_correlations) +from dataclasses import dataclass, field +from ._hypotests import _all_partitions +from ._stats_pythran import _compute_outer_prob_inside_method +from ._resampling import (MonteCarloMethod, PermutationMethod, BootstrapMethod, + monte_carlo_test, permutation_test, bootstrap, + _batch_generator) +from ._axis_nan_policy import (_axis_nan_policy_factory, + _broadcast_concatenate) +from ._binomtest import _binary_search_for_binom_tst as _binary_search +from scipy._lib._bunch import _make_tuple_bunch +from scipy import stats +from scipy.optimize import root_scalar +from scipy._lib.deprecation import _NoValue, _deprecate_positional_args +from scipy._lib._util import normalize_axis_index + +# In __all__ but deprecated for removal in SciPy 1.13.0 +from scipy._lib._util import float_factorial # noqa: F401 +from scipy.stats._mstats_basic import ( # noqa: F401 + PointbiserialrResult, Ttest_1sampResult, Ttest_relResult +) + + +# Functions/classes in other files should be added in `__init__.py`, not here +__all__ = ['find_repeats', 'gmean', 'hmean', 'pmean', 'mode', 'tmean', 'tvar', + 'tmin', 'tmax', 'tstd', 'tsem', 'moment', + 'skew', 'kurtosis', 'describe', 'skewtest', 'kurtosistest', + 'normaltest', 'jarque_bera', + 'scoreatpercentile', 'percentileofscore', + 'cumfreq', 'relfreq', 'obrientransform', + 'sem', 'zmap', 'zscore', 'gzscore', 'iqr', 'gstd', + 'median_abs_deviation', + 'sigmaclip', 'trimboth', 'trim1', 'trim_mean', + 'f_oneway', 'pearsonr', 'fisher_exact', + 'spearmanr', 'pointbiserialr', + 'kendalltau', 'weightedtau', 'multiscale_graphcorr', + 'linregress', 'siegelslopes', 'theilslopes', 'ttest_1samp', + 'ttest_ind', 'ttest_ind_from_stats', 'ttest_rel', + 'kstest', 'ks_1samp', 'ks_2samp', + 'chisquare', 'power_divergence', + 'tiecorrect', 'ranksums', 'kruskal', 'friedmanchisquare', + 'rankdata', 'combine_pvalues', 'quantile_test', + 'wasserstein_distance', 'wasserstein_distance_nd', 'energy_distance', + 'brunnermunzel', 'alexandergovern', + 'expectile'] + + +def _chk_asarray(a, axis): + if axis is None: + a = np.ravel(a) + outaxis = 0 + else: + a = np.asarray(a) + outaxis = axis + + if a.ndim == 0: + a = np.atleast_1d(a) + + return a, outaxis + + +def _chk2_asarray(a, b, axis): + if axis is None: + a = np.ravel(a) + b = np.ravel(b) + outaxis = 0 + else: + a = np.asarray(a) + b = np.asarray(b) + outaxis = axis + + if a.ndim == 0: + a = np.atleast_1d(a) + if b.ndim == 0: + b = np.atleast_1d(b) + + return a, b, outaxis + + +SignificanceResult = _make_tuple_bunch('SignificanceResult', + ['statistic', 'pvalue'], []) + + +# note that `weights` are paired with `x` +@_axis_nan_policy_factory( + lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, + result_to_tuple=lambda x: (x,), kwd_samples=['weights']) +def gmean(a, axis=0, dtype=None, weights=None): + r"""Compute the weighted geometric mean along the specified axis. + + The weighted geometric mean of the array :math:`a_i` associated to weights + :math:`w_i` is: + + .. math:: + + \exp \left( \frac{ \sum_{i=1}^n w_i \ln a_i }{ \sum_{i=1}^n w_i } + \right) \, , + + and, with equal weights, it gives: + + .. math:: + + \sqrt[n]{ \prod_{i=1}^n a_i } \, . + + Parameters + ---------- + a : array_like + Input array or object that can be converted to an array. + axis : int or None, optional + Axis along which the geometric mean is computed. Default is 0. + If None, compute over the whole array `a`. + dtype : dtype, optional + Type to which the input arrays are cast before the calculation is + performed. + weights : array_like, optional + The `weights` array must be broadcastable to the same shape as `a`. + Default is None, which gives each value a weight of 1.0. + + Returns + ------- + gmean : ndarray + See `dtype` parameter above. + + See Also + -------- + numpy.mean : Arithmetic average + numpy.average : Weighted average + hmean : Harmonic mean + + References + ---------- + .. [1] "Weighted Geometric Mean", *Wikipedia*, + https://en.wikipedia.org/wiki/Weighted_geometric_mean. + .. [2] Grossman, J., Grossman, M., Katz, R., "Averages: A New Approach", + Archimedes Foundation, 1983 + + Examples + -------- + >>> from scipy.stats import gmean + >>> gmean([1, 4]) + 2.0 + >>> gmean([1, 2, 3, 4, 5, 6, 7]) + 3.3800151591412964 + >>> gmean([1, 4, 7], weights=[3, 1, 3]) + 2.80668351922014 + + """ + + a = np.asarray(a, dtype=dtype) + + if weights is not None: + weights = np.asarray(weights, dtype=dtype) + + with np.errstate(divide='ignore'): + log_a = np.log(a) + + return np.exp(np.average(log_a, axis=axis, weights=weights)) + + +@_axis_nan_policy_factory( + lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, + result_to_tuple=lambda x: (x,), kwd_samples=['weights']) +def hmean(a, axis=0, dtype=None, *, weights=None): + r"""Calculate the weighted harmonic mean along the specified axis. + + The weighted harmonic mean of the array :math:`a_i` associated to weights + :math:`w_i` is: + + .. math:: + + \frac{ \sum_{i=1}^n w_i }{ \sum_{i=1}^n \frac{w_i}{a_i} } \, , + + and, with equal weights, it gives: + + .. math:: + + \frac{ n }{ \sum_{i=1}^n \frac{1}{a_i} } \, . + + Parameters + ---------- + a : array_like + Input array, masked array or object that can be converted to an array. + axis : int or None, optional + Axis along which the harmonic mean is computed. Default is 0. + If None, compute over the whole array `a`. + dtype : dtype, optional + Type of the returned array and of the accumulator in which the + elements are summed. If `dtype` is not specified, it defaults to the + dtype of `a`, unless `a` has an integer `dtype` with a precision less + than that of the default platform integer. In that case, the default + platform integer is used. + weights : array_like, optional + The weights array can either be 1-D (in which case its length must be + the size of `a` along the given `axis`) or of the same shape as `a`. + Default is None, which gives each value a weight of 1.0. + + .. versionadded:: 1.9 + + Returns + ------- + hmean : ndarray + See `dtype` parameter above. + + See Also + -------- + numpy.mean : Arithmetic average + numpy.average : Weighted average + gmean : Geometric mean + + Notes + ----- + The harmonic mean is computed over a single dimension of the input + array, axis=0 by default, or all values in the array if axis=None. + float64 intermediate and return values are used for integer inputs. + + References + ---------- + .. [1] "Weighted Harmonic Mean", *Wikipedia*, + https://en.wikipedia.org/wiki/Harmonic_mean#Weighted_harmonic_mean + .. [2] Ferger, F., "The nature and use of the harmonic mean", Journal of + the American Statistical Association, vol. 26, pp. 36-40, 1931 + + Examples + -------- + >>> from scipy.stats import hmean + >>> hmean([1, 4]) + 1.6000000000000001 + >>> hmean([1, 2, 3, 4, 5, 6, 7]) + 2.6997245179063363 + >>> hmean([1, 4, 7], weights=[3, 1, 3]) + 1.9029126213592233 + + """ + if not isinstance(a, np.ndarray): + a = np.array(a, dtype=dtype) + elif dtype: + # Must change the default dtype allowing array type + if isinstance(a, np.ma.MaskedArray): + a = np.ma.asarray(a, dtype=dtype) + else: + a = np.asarray(a, dtype=dtype) + + if np.all(a >= 0): + # Harmonic mean only defined if greater than or equal to zero. + if weights is not None: + weights = np.asanyarray(weights, dtype=dtype) + + with np.errstate(divide='ignore'): + return 1.0 / np.average(1.0 / a, axis=axis, weights=weights) + else: + raise ValueError("Harmonic mean only defined if all elements greater " + "than or equal to zero") + + +@_axis_nan_policy_factory( + lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, + result_to_tuple=lambda x: (x,), kwd_samples=['weights']) +def pmean(a, p, *, axis=0, dtype=None, weights=None): + r"""Calculate the weighted power mean along the specified axis. + + The weighted power mean of the array :math:`a_i` associated to weights + :math:`w_i` is: + + .. math:: + + \left( \frac{ \sum_{i=1}^n w_i a_i^p }{ \sum_{i=1}^n w_i } + \right)^{ 1 / p } \, , + + and, with equal weights, it gives: + + .. math:: + + \left( \frac{ 1 }{ n } \sum_{i=1}^n a_i^p \right)^{ 1 / p } \, . + + When ``p=0``, it returns the geometric mean. + + This mean is also called generalized mean or Hölder mean, and must not be + confused with the Kolmogorov generalized mean, also called + quasi-arithmetic mean or generalized f-mean [3]_. + + Parameters + ---------- + a : array_like + Input array, masked array or object that can be converted to an array. + p : int or float + Exponent. + axis : int or None, optional + Axis along which the power mean is computed. Default is 0. + If None, compute over the whole array `a`. + dtype : dtype, optional + Type of the returned array and of the accumulator in which the + elements are summed. If `dtype` is not specified, it defaults to the + dtype of `a`, unless `a` has an integer `dtype` with a precision less + than that of the default platform integer. In that case, the default + platform integer is used. + weights : array_like, optional + The weights array can either be 1-D (in which case its length must be + the size of `a` along the given `axis`) or of the same shape as `a`. + Default is None, which gives each value a weight of 1.0. + + Returns + ------- + pmean : ndarray, see `dtype` parameter above. + Output array containing the power mean values. + + See Also + -------- + numpy.average : Weighted average + gmean : Geometric mean + hmean : Harmonic mean + + Notes + ----- + The power mean is computed over a single dimension of the input + array, ``axis=0`` by default, or all values in the array if ``axis=None``. + float64 intermediate and return values are used for integer inputs. + + .. versionadded:: 1.9 + + References + ---------- + .. [1] "Generalized Mean", *Wikipedia*, + https://en.wikipedia.org/wiki/Generalized_mean + .. [2] Norris, N., "Convexity properties of generalized mean value + functions", The Annals of Mathematical Statistics, vol. 8, + pp. 118-120, 1937 + .. [3] Bullen, P.S., Handbook of Means and Their Inequalities, 2003 + + Examples + -------- + >>> from scipy.stats import pmean, hmean, gmean + >>> pmean([1, 4], 1.3) + 2.639372938300652 + >>> pmean([1, 2, 3, 4, 5, 6, 7], 1.3) + 4.157111214492084 + >>> pmean([1, 4, 7], -2, weights=[3, 1, 3]) + 1.4969684896631954 + + For p=-1, power mean is equal to harmonic mean: + + >>> pmean([1, 4, 7], -1, weights=[3, 1, 3]) + 1.9029126213592233 + >>> hmean([1, 4, 7], weights=[3, 1, 3]) + 1.9029126213592233 + + For p=0, power mean is defined as the geometric mean: + + >>> pmean([1, 4, 7], 0, weights=[3, 1, 3]) + 2.80668351922014 + >>> gmean([1, 4, 7], weights=[3, 1, 3]) + 2.80668351922014 + + """ + if not isinstance(p, (int, float)): + raise ValueError("Power mean only defined for exponent of type int or " + "float.") + if p == 0: + return gmean(a, axis=axis, dtype=dtype, weights=weights) + + if not isinstance(a, np.ndarray): + a = np.array(a, dtype=dtype) + elif dtype: + # Must change the default dtype allowing array type + if isinstance(a, np.ma.MaskedArray): + a = np.ma.asarray(a, dtype=dtype) + else: + a = np.asarray(a, dtype=dtype) + + if np.all(a >= 0): + # Power mean only defined if greater than or equal to zero + if weights is not None: + weights = np.asanyarray(weights, dtype=dtype) + + with np.errstate(divide='ignore'): + return np.float_power( + np.average(np.float_power(a, p), axis=axis, weights=weights), + 1/p) + else: + raise ValueError("Power mean only defined if all elements greater " + "than or equal to zero") + + +ModeResult = namedtuple('ModeResult', ('mode', 'count')) + + +def _mode_result(mode, count): + # When a slice is empty, `_axis_nan_policy` automatically produces + # NaN for `mode` and `count`. This is a reasonable convention for `mode`, + # but `count` should not be NaN; it should be zero. + i = np.isnan(count) + if i.shape == (): + count = count.dtype(0) if i else count + else: + count[i] = 0 + return ModeResult(mode, count) + + +@_axis_nan_policy_factory(_mode_result, override={'vectorization': True, + 'nan_propagation': False}) +def mode(a, axis=0, nan_policy='propagate', keepdims=False): + r"""Return an array of the modal (most common) value in the passed array. + + If there is more than one such value, only one is returned. + The bin-count for the modal bins is also returned. + + Parameters + ---------- + a : array_like + Numeric, n-dimensional array of which to find mode(s). + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': treats nan as it would treat any other value + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + keepdims : bool, optional + If set to ``False``, the `axis` over which the statistic is taken + is consumed (eliminated from the output array). If set to ``True``, + the `axis` is retained with size one, and the result will broadcast + correctly against the input array. + + Returns + ------- + mode : ndarray + Array of modal values. + count : ndarray + Array of counts for each mode. + + Notes + ----- + The mode is calculated using `numpy.unique`. + In NumPy versions 1.21 and after, all NaNs - even those with different + binary representations - are treated as equivalent and counted as separate + instances of the same value. + + By convention, the mode of an empty array is NaN, and the associated count + is zero. + + Examples + -------- + >>> import numpy as np + >>> a = np.array([[3, 0, 3, 7], + ... [3, 2, 6, 2], + ... [1, 7, 2, 8], + ... [3, 0, 6, 1], + ... [3, 2, 5, 5]]) + >>> from scipy import stats + >>> stats.mode(a, keepdims=True) + ModeResult(mode=array([[3, 0, 6, 1]]), count=array([[4, 2, 2, 1]])) + + To get mode of whole array, specify ``axis=None``: + + >>> stats.mode(a, axis=None, keepdims=True) + ModeResult(mode=[[3]], count=[[5]]) + >>> stats.mode(a, axis=None, keepdims=False) + ModeResult(mode=3, count=5) + + """ + # `axis`, `nan_policy`, and `keepdims` are handled by `_axis_nan_policy` + if not np.issubdtype(a.dtype, np.number): + message = ("Argument `a` is not recognized as numeric. " + "Support for input that cannot be coerced to a numeric " + "array was deprecated in SciPy 1.9.0 and removed in SciPy " + "1.11.0. Please consider `np.unique`.") + raise TypeError(message) + + if a.size == 0: + NaN = _get_nan(a) + return ModeResult(*np.array([NaN, 0], dtype=NaN.dtype)) + + vals, cnts = np.unique(a, return_counts=True) + modes, counts = vals[cnts.argmax()], cnts.max() + return ModeResult(modes[()], counts[()]) + + +def _put_nan_to_limits(a, limits, inclusive): + """Put NaNs in an array for values outside of given limits. + + This is primarily a utility function. + + Parameters + ---------- + a : array + limits : (float or None, float or None) + A tuple consisting of the (lower limit, upper limit). Values in the + input array less than the lower limit or greater than the upper limit + will be replaced with `np.nan`. None implies no limit. + inclusive : (bool, bool) + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to lower or upper are allowed. + + """ + if limits is None: + return a + mask = np.full_like(a, False, dtype=np.bool_) + lower_limit, upper_limit = limits + lower_include, upper_include = inclusive + if lower_limit is not None: + mask |= (a < lower_limit) if lower_include else a <= lower_limit + if upper_limit is not None: + mask |= (a > upper_limit) if upper_include else a >= upper_limit + if np.all(mask): + raise ValueError("No array values within given limits") + if np.any(mask): + a = a.copy() if np.issubdtype(a.dtype, np.inexact) else a.astype(np.float64) + a[mask] = np.nan + return a + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, default_axis=None, + result_to_tuple=lambda x: (x,) +) +def tmean(a, limits=None, inclusive=(True, True), axis=None): + """Compute the trimmed mean. + + This function finds the arithmetic mean of given values, ignoring values + outside the given `limits`. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None (default), then all + values are used. Either of the limit values in the tuple can also be + None representing a half-open interval. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to compute test. Default is None. + + Returns + ------- + tmean : ndarray + Trimmed mean. + + See Also + -------- + trim_mean : Returns mean after trimming a proportion from both tails. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tmean(x) + 9.5 + >>> stats.tmean(x, (3,17)) + 10.0 + + """ + a = _put_nan_to_limits(a, limits, inclusive) + return np.nanmean(a, axis=axis) + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def tvar(a, limits=None, inclusive=(True, True), axis=0, ddof=1): + """Compute the trimmed variance. + + This function computes the sample variance of an array of values, + while ignoring values which are outside of given `limits`. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None, then all values are + used. Either of the limit values in the tuple can also be None + representing a half-open interval. The default value is None. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + ddof : int, optional + Delta degrees of freedom. Default is 1. + + Returns + ------- + tvar : float + Trimmed variance. + + Notes + ----- + `tvar` computes the unbiased sample variance, i.e. it uses a correction + factor ``n / (n - 1)``. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tvar(x) + 35.0 + >>> stats.tvar(x, (3,17)) + 20.0 + + """ + a = _put_nan_to_limits(a, limits, inclusive) + return np.nanvar(a, ddof=ddof, axis=axis) + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def tmin(a, lowerlimit=None, axis=0, inclusive=True, nan_policy='propagate'): + """Compute the trimmed minimum. + + This function finds the minimum value of an array `a` along the + specified axis, but only considering values greater than a specified + lower limit. + + Parameters + ---------- + a : array_like + Array of values. + lowerlimit : None or float, optional + Values in the input array less than the given limit will be ignored. + When lowerlimit is None, then all values are used. The default value + is None. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + inclusive : {True, False}, optional + This flag determines whether values exactly equal to the lower limit + are included. The default value is True. + + Returns + ------- + tmin : float, int or ndarray + Trimmed minimum. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tmin(x) + 0 + + >>> stats.tmin(x, 13) + 13 + + >>> stats.tmin(x, 13, inclusive=False) + 14 + + """ + dtype = a.dtype + a = _put_nan_to_limits(a, (lowerlimit, None), (inclusive, None)) + res = np.nanmin(a, axis=axis) + if not np.any(np.isnan(res)): + # needed if input is of integer dtype + return res.astype(dtype, copy=False) + return res + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def tmax(a, upperlimit=None, axis=0, inclusive=True, nan_policy='propagate'): + """Compute the trimmed maximum. + + This function computes the maximum value of an array along a given axis, + while ignoring values larger than a specified upper limit. + + Parameters + ---------- + a : array_like + Array of values. + upperlimit : None or float, optional + Values in the input array greater than the given limit will be ignored. + When upperlimit is None, then all values are used. The default value + is None. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + inclusive : {True, False}, optional + This flag determines whether values exactly equal to the upper limit + are included. The default value is True. + + Returns + ------- + tmax : float, int or ndarray + Trimmed maximum. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tmax(x) + 19 + + >>> stats.tmax(x, 13) + 13 + + >>> stats.tmax(x, 13, inclusive=False) + 12 + + """ + dtype = a.dtype + a = _put_nan_to_limits(a, (None, upperlimit), (None, inclusive)) + res = np.nanmax(a, axis=axis) + if not np.any(np.isnan(res)): + # needed if input is of integer dtype + return res.astype(dtype, copy=False) + return res + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def tstd(a, limits=None, inclusive=(True, True), axis=0, ddof=1): + """Compute the trimmed sample standard deviation. + + This function finds the sample standard deviation of given values, + ignoring values outside the given `limits`. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None, then all values are + used. Either of the limit values in the tuple can also be None + representing a half-open interval. The default value is None. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + ddof : int, optional + Delta degrees of freedom. Default is 1. + + Returns + ------- + tstd : float + Trimmed sample standard deviation. + + Notes + ----- + `tstd` computes the unbiased sample standard deviation, i.e. it uses a + correction factor ``n / (n - 1)``. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tstd(x) + 5.9160797830996161 + >>> stats.tstd(x, (3,17)) + 4.4721359549995796 + + """ + return np.sqrt(tvar(a, limits, inclusive, axis, ddof, _no_deco=True)) + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def tsem(a, limits=None, inclusive=(True, True), axis=0, ddof=1): + """Compute the trimmed standard error of the mean. + + This function finds the standard error of the mean for given + values, ignoring values outside the given `limits`. + + Parameters + ---------- + a : array_like + Array of values. + limits : None or (lower limit, upper limit), optional + Values in the input array less than the lower limit or greater than the + upper limit will be ignored. When limits is None, then all values are + used. Either of the limit values in the tuple can also be None + representing a half-open interval. The default value is None. + inclusive : (bool, bool), optional + A tuple consisting of the (lower flag, upper flag). These flags + determine whether values exactly equal to the lower or upper limits + are included. The default value is (True, True). + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over the + whole array `a`. + ddof : int, optional + Delta degrees of freedom. Default is 1. + + Returns + ------- + tsem : float + Trimmed standard error of the mean. + + Notes + ----- + `tsem` uses unbiased sample standard deviation, i.e. it uses a + correction factor ``n / (n - 1)``. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = np.arange(20) + >>> stats.tsem(x) + 1.3228756555322954 + >>> stats.tsem(x, (3,17)) + 1.1547005383792515 + + """ + a = _put_nan_to_limits(a, limits, inclusive) + sd = np.sqrt(np.nanvar(a, ddof=ddof, axis=axis)) + n_obs = (~np.isnan(a)).sum(axis=axis) + return sd / np.sqrt(n_obs, dtype=sd.dtype) + + +##################################### +# MOMENTS # +##################################### + + +def _moment_outputs(kwds): + moment = np.atleast_1d(kwds.get('order', 1)) + if moment.size == 0: + raise ValueError("'order' must be a scalar or a non-empty 1D " + "list/array.") + return len(moment) + + +def _moment_result_object(*args): + if len(args) == 1: + return args[0] + return np.asarray(args) + +# `moment` fits into the `_axis_nan_policy` pattern, but it is a bit unusual +# because the number of outputs is variable. Specifically, +# `result_to_tuple=lambda x: (x,)` may be surprising for a function that +# can produce more than one output, but it is intended here. +# When `moment is called to produce the output: +# - `result_to_tuple` packs the returned array into a single-element tuple, +# - `_moment_result_object` extracts and returns that single element. +# However, when the input array is empty, `moment` is never called. Instead, +# - `_check_empty_inputs` is used to produce an empty array with the +# appropriate dimensions. +# - A list comprehension creates the appropriate number of copies of this +# array, depending on `n_outputs`. +# - This list - which may have multiple elements - is passed into +# `_moment_result_object`. +# - If there is a single output, `_moment_result_object` extracts and returns +# the single output from the list. +# - If there are multiple outputs, and therefore multiple elements in the list, +# `_moment_result_object` converts the list of arrays to a single array and +# returns it. +# Currently this leads to a slight inconsistency: when the input array is +# empty, there is no distinction between the `moment` function being called +# with parameter `order=1` and `order=[1]`; the latter *should* produce +# the same as the former but with a singleton zeroth dimension. +@_rename_parameter('moment', 'order') +@_axis_nan_policy_factory( # noqa: E302 + _moment_result_object, n_samples=1, result_to_tuple=lambda x: (x,), + n_outputs=_moment_outputs +) +def moment(a, order=1, axis=0, nan_policy='propagate', *, center=None): + r"""Calculate the nth moment about the mean for a sample. + + A moment is a specific quantitative measure of the shape of a set of + points. It is often used to calculate coefficients of skewness and kurtosis + due to its close relationship with them. + + Parameters + ---------- + a : array_like + Input array. + order : int or array_like of ints, optional + Order of central moment that is returned. Default is 1. + axis : int or None, optional + Axis along which the central moment is computed. Default is 0. + If None, compute over the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + center : float or None, optional + The point about which moments are taken. This can be the sample mean, + the origin, or any other be point. If `None` (default) compute the + center as the sample mean. + + Returns + ------- + n-th moment about the `center` : ndarray or float + The appropriate moment along the given axis or over all values if axis + is None. The denominator for the moment calculation is the number of + observations, no degrees of freedom correction is done. + + See Also + -------- + kurtosis, skew, describe + + Notes + ----- + The k-th moment of a data sample is: + + .. math:: + + m_k = \frac{1}{n} \sum_{i = 1}^n (x_i - c)^k + + Where `n` is the number of samples, and `c` is the center around which the + moment is calculated. This function uses exponentiation by squares [1]_ for + efficiency. + + Note that, if `a` is an empty array (``a.size == 0``), array `moment` with + one element (`moment.size == 1`) is treated the same as scalar `moment` + (``np.isscalar(moment)``). This might produce arrays of unexpected shape. + + References + ---------- + .. [1] https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms + + Examples + -------- + >>> from scipy.stats import moment + >>> moment([1, 2, 3, 4, 5], order=1) + 0.0 + >>> moment([1, 2, 3, 4, 5], order=2) + 2.0 + + """ + moment = order # parameter was renamed + a, axis = _chk_asarray(a, axis) + + # for array_like moment input, return a value for each. + if not np.isscalar(moment): + # Calculated the mean once at most, and only if it will be used + calculate_mean = center is None and np.any(np.asarray(moment) > 1) + mean = a.mean(axis, keepdims=True) if calculate_mean else None + mmnt = [] + for i in moment: + if center is None and i > 1: + mmnt.append(_moment(a, i, axis, mean=mean)) + else: + mmnt.append(_moment(a, i, axis, mean=center)) + return np.array(mmnt) + else: + return _moment(a, moment, axis, mean=center) + + +# Moment with optional pre-computed mean, equal to a.mean(axis, keepdims=True) +def _moment(a, moment, axis, *, mean=None): + if np.abs(moment - np.round(moment)) > 0: + raise ValueError("All moment parameters must be integers") + + # moment of empty array is the same regardless of order + if a.size == 0: + return np.mean(a, axis=axis) + + dtype = a.dtype.type if a.dtype.kind in 'fc' else np.float64 + + if moment == 0 or (moment == 1 and mean is None): + # By definition the zeroth moment is always 1, and the first *central* + # moment is 0. + shape = list(a.shape) + del shape[axis] + + if len(shape) == 0: + return dtype(1.0 if moment == 0 else 0.0) + else: + return (np.ones(shape, dtype=dtype) if moment == 0 + else np.zeros(shape, dtype=dtype)) + else: + # Exponentiation by squares: form exponent sequence + n_list = [moment] + current_n = moment + while current_n > 2: + if current_n % 2: + current_n = (current_n - 1) / 2 + else: + current_n /= 2 + n_list.append(current_n) + + # Starting point for exponentiation by squares + mean = (a.mean(axis, keepdims=True) if mean is None + else np.asarray(mean, dtype=dtype)[()]) + a_zero_mean = a - mean + + eps = np.finfo(a_zero_mean.dtype).resolution * 10 + with np.errstate(divide='ignore', invalid='ignore'): + rel_diff = np.max(np.abs(a_zero_mean), axis=axis, + keepdims=True) / np.abs(mean) + with np.errstate(invalid='ignore'): + precision_loss = np.any(rel_diff < eps) + n = a.shape[axis] if axis is not None else a.size + if precision_loss and n > 1: + message = ("Precision loss occurred in moment calculation due to " + "catastrophic cancellation. This occurs when the data " + "are nearly identical. Results may be unreliable.") + warnings.warn(message, RuntimeWarning, stacklevel=4) + + if n_list[-1] == 1: + s = a_zero_mean.copy() + else: + s = a_zero_mean**2 + + # Perform multiplications + for n in n_list[-2::-1]: + s = s**2 + if n % 2: + s *= a_zero_mean + return np.mean(s, axis) + + +def _var(x, axis=0, ddof=0, mean=None): + # Calculate variance of sample, warning if precision is lost + var = _moment(x, 2, axis, mean=mean) + if ddof != 0: + n = x.shape[axis] if axis is not None else x.size + var *= np.divide(n, n-ddof) # to avoid error on division by zero + return var + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1 +) +def skew(a, axis=0, bias=True, nan_policy='propagate'): + r"""Compute the sample skewness of a data set. + + For normally distributed data, the skewness should be about zero. For + unimodal continuous distributions, a skewness value greater than zero means + that there is more weight in the right tail of the distribution. The + function `skewtest` can be used to determine if the skewness value + is close enough to zero, statistically speaking. + + Parameters + ---------- + a : ndarray + Input array. + axis : int or None, optional + Axis along which skewness is calculated. Default is 0. + If None, compute over the whole array `a`. + bias : bool, optional + If False, then the calculations are corrected for statistical bias. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + skewness : ndarray + The skewness of values along an axis, returning NaN where all values + are equal. + + Notes + ----- + The sample skewness is computed as the Fisher-Pearson coefficient + of skewness, i.e. + + .. math:: + + g_1=\frac{m_3}{m_2^{3/2}} + + where + + .. math:: + + m_i=\frac{1}{N}\sum_{n=1}^N(x[n]-\bar{x})^i + + is the biased sample :math:`i\texttt{th}` central moment, and + :math:`\bar{x}` is + the sample mean. If ``bias`` is False, the calculations are + corrected for bias and the value computed is the adjusted + Fisher-Pearson standardized moment coefficient, i.e. + + .. math:: + + G_1=\frac{k_3}{k_2^{3/2}}= + \frac{\sqrt{N(N-1)}}{N-2}\frac{m_3}{m_2^{3/2}}. + + References + ---------- + .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard + Probability and Statistics Tables and Formulae. Chapman & Hall: New + York. 2000. + Section 2.2.24.1 + + Examples + -------- + >>> from scipy.stats import skew + >>> skew([1, 2, 3, 4, 5]) + 0.0 + >>> skew([2, 8, 0, 4, 1, 9, 9, 0]) + 0.2650554122698573 + + """ + a, axis = _chk_asarray(a, axis) + n = a.shape[axis] + + contains_nan, nan_policy = _contains_nan(a, nan_policy) + + if contains_nan and nan_policy == 'omit': + a = ma.masked_invalid(a) + return mstats_basic.skew(a, axis, bias) + + mean = a.mean(axis, keepdims=True) + m2 = _moment(a, 2, axis, mean=mean) + m3 = _moment(a, 3, axis, mean=mean) + with np.errstate(all='ignore'): + zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) + vals = np.where(zero, np.nan, m3 / m2**1.5) + if not bias: + can_correct = ~zero & (n > 2) + if can_correct.any(): + m2 = np.extract(can_correct, m2) + m3 = np.extract(can_correct, m3) + nval = np.sqrt((n - 1.0) * n) / (n - 2.0) * m3 / m2**1.5 + np.place(vals, can_correct, nval) + + return vals[()] + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1 +) +def kurtosis(a, axis=0, fisher=True, bias=True, nan_policy='propagate'): + """Compute the kurtosis (Fisher or Pearson) of a dataset. + + Kurtosis is the fourth central moment divided by the square of the + variance. If Fisher's definition is used, then 3.0 is subtracted from + the result to give 0.0 for a normal distribution. + + If bias is False then the kurtosis is calculated using k statistics to + eliminate bias coming from biased moment estimators + + Use `kurtosistest` to see if result is close enough to normal. + + Parameters + ---------- + a : array + Data for which the kurtosis is calculated. + axis : int or None, optional + Axis along which the kurtosis is calculated. Default is 0. + If None, compute over the whole array `a`. + fisher : bool, optional + If True, Fisher's definition is used (normal ==> 0.0). If False, + Pearson's definition is used (normal ==> 3.0). + bias : bool, optional + If False, then the calculations are corrected for statistical bias. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. 'propagate' returns nan, + 'raise' throws an error, 'omit' performs the calculations ignoring nan + values. Default is 'propagate'. + + Returns + ------- + kurtosis : array + The kurtosis of values along an axis, returning NaN where all values + are equal. + + References + ---------- + .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard + Probability and Statistics Tables and Formulae. Chapman & Hall: New + York. 2000. + + Examples + -------- + In Fisher's definition, the kurtosis of the normal distribution is zero. + In the following example, the kurtosis is close to zero, because it was + calculated from the dataset, not from the continuous distribution. + + >>> import numpy as np + >>> from scipy.stats import norm, kurtosis + >>> data = norm.rvs(size=1000, random_state=3) + >>> kurtosis(data) + -0.06928694200380558 + + The distribution with a higher kurtosis has a heavier tail. + The zero valued kurtosis of the normal distribution in Fisher's definition + can serve as a reference point. + + >>> import matplotlib.pyplot as plt + >>> import scipy.stats as stats + >>> from scipy.stats import kurtosis + + >>> x = np.linspace(-5, 5, 100) + >>> ax = plt.subplot() + >>> distnames = ['laplace', 'norm', 'uniform'] + + >>> for distname in distnames: + ... if distname == 'uniform': + ... dist = getattr(stats, distname)(loc=-2, scale=4) + ... else: + ... dist = getattr(stats, distname) + ... data = dist.rvs(size=1000) + ... kur = kurtosis(data, fisher=True) + ... y = dist.pdf(x) + ... ax.plot(x, y, label="{}, {}".format(distname, round(kur, 3))) + ... ax.legend() + + The Laplace distribution has a heavier tail than the normal distribution. + The uniform distribution (which has negative kurtosis) has the thinnest + tail. + + """ + a, axis = _chk_asarray(a, axis) + + contains_nan, nan_policy = _contains_nan(a, nan_policy) + + if contains_nan and nan_policy == 'omit': + a = ma.masked_invalid(a) + return mstats_basic.kurtosis(a, axis, fisher, bias) + + n = a.shape[axis] + mean = a.mean(axis, keepdims=True) + m2 = _moment(a, 2, axis, mean=mean) + m4 = _moment(a, 4, axis, mean=mean) + with np.errstate(all='ignore'): + zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) + vals = np.where(zero, np.nan, m4 / m2**2.0) + + if not bias: + can_correct = ~zero & (n > 3) + if can_correct.any(): + m2 = np.extract(can_correct, m2) + m4 = np.extract(can_correct, m4) + nval = 1.0/(n-2)/(n-3) * ((n**2-1.0)*m4/m2**2.0 - 3*(n-1)**2.0) + np.place(vals, can_correct, nval + 3.0) + + return vals[()] - 3 if fisher else vals[()] + + +DescribeResult = namedtuple('DescribeResult', + ('nobs', 'minmax', 'mean', 'variance', 'skewness', + 'kurtosis')) + + +def describe(a, axis=0, ddof=1, bias=True, nan_policy='propagate'): + """Compute several descriptive statistics of the passed array. + + Parameters + ---------- + a : array_like + Input data. + axis : int or None, optional + Axis along which statistics are calculated. Default is 0. + If None, compute over the whole array `a`. + ddof : int, optional + Delta degrees of freedom (only for variance). Default is 1. + bias : bool, optional + If False, then the skewness and kurtosis calculations are corrected + for statistical bias. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + nobs : int or ndarray of ints + Number of observations (length of data along `axis`). + When 'omit' is chosen as nan_policy, the length along each axis + slice is counted separately. + minmax: tuple of ndarrays or floats + Minimum and maximum value of `a` along the given axis. + mean : ndarray or float + Arithmetic mean of `a` along the given axis. + variance : ndarray or float + Unbiased variance of `a` along the given axis; denominator is number + of observations minus one. + skewness : ndarray or float + Skewness of `a` along the given axis, based on moment calculations + with denominator equal to the number of observations, i.e. no degrees + of freedom correction. + kurtosis : ndarray or float + Kurtosis (Fisher) of `a` along the given axis. The kurtosis is + normalized so that it is zero for the normal distribution. No + degrees of freedom are used. + + See Also + -------- + skew, kurtosis + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> a = np.arange(10) + >>> stats.describe(a) + DescribeResult(nobs=10, minmax=(0, 9), mean=4.5, + variance=9.166666666666666, skewness=0.0, + kurtosis=-1.2242424242424244) + >>> b = [[1, 2], [3, 4]] + >>> stats.describe(b) + DescribeResult(nobs=2, minmax=(array([1, 2]), array([3, 4])), + mean=array([2., 3.]), variance=array([2., 2.]), + skewness=array([0., 0.]), kurtosis=array([-2., -2.])) + + """ + a, axis = _chk_asarray(a, axis) + + contains_nan, nan_policy = _contains_nan(a, nan_policy) + + if contains_nan and nan_policy == 'omit': + a = ma.masked_invalid(a) + return mstats_basic.describe(a, axis, ddof, bias) + + if a.size == 0: + raise ValueError("The input must not be empty.") + n = a.shape[axis] + mm = (np.min(a, axis=axis), np.max(a, axis=axis)) + m = np.mean(a, axis=axis) + v = _var(a, axis=axis, ddof=ddof) + sk = skew(a, axis, bias=bias) + kurt = kurtosis(a, axis, bias=bias) + + return DescribeResult(n, mm, m, v, sk, kurt) + +##################################### +# NORMALITY TESTS # +##################################### + + +def _get_pvalue(statistic, distribution, alternative, symmetric=True): + """Get p-value given the statistic, (continuous) distribution, and alternative""" + + if alternative == 'less': + pvalue = distribution.cdf(statistic) + elif alternative == 'greater': + pvalue = distribution.sf(statistic) + elif alternative == 'two-sided': + pvalue = 2 * (distribution.sf(np.abs(statistic)) if symmetric + else np.minimum(distribution.cdf(statistic), + distribution.sf(statistic))) + else: + message = "`alternative` must be 'less', 'greater', or 'two-sided'." + raise ValueError(message) + + return pvalue + + +SkewtestResult = namedtuple('SkewtestResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(SkewtestResult, n_samples=1, too_small=7) +def skewtest(a, axis=0, nan_policy='propagate', alternative='two-sided'): + r"""Test whether the skew is different from the normal distribution. + + This function tests the null hypothesis that the skewness of + the population that the sample was drawn from is the same + as that of a corresponding normal distribution. + + Parameters + ---------- + a : array + The data to be tested. + axis : int or None, optional + Axis along which statistics are calculated. Default is 0. + If None, compute over the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the skewness of the distribution underlying the sample + is different from that of the normal distribution (i.e. 0) + * 'less': the skewness of the distribution underlying the sample + is less than that of the normal distribution + * 'greater': the skewness of the distribution underlying the sample + is greater than that of the normal distribution + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float + The computed z-score for this test. + pvalue : float + The p-value for the hypothesis test. + + Notes + ----- + The sample size must be at least 8. + + References + ---------- + .. [1] R. B. D'Agostino, A. J. Belanger and R. B. D'Agostino Jr., + "A suggestion for using powerful and informative tests of + normality", American Statistician 44, pp. 316-321, 1990. + .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test + for normality (complete samples). Biometrika, 52(3/4), 591-611. + .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + + Examples + -------- + Suppose we wish to infer from measurements whether the weights of adult + human males in a medical study are not normally distributed [2]_. + The weights (lbs) are recorded in the array ``x`` below. + + >>> import numpy as np + >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) + + The skewness test from [1]_ begins by computing a statistic based on the + sample skewness. + + >>> from scipy import stats + >>> res = stats.skewtest(x) + >>> res.statistic + 2.7788579769903414 + + Because normal distributions have zero skewness, the magnitude of this + statistic tends to be low for samples drawn from a normal distribution. + + The test is performed by comparing the observed value of the + statistic against the null distribution: the distribution of statistic + values derived under the null hypothesis that the weights were drawn from + a normal distribution. + + For this test, the null distribution of the statistic for very large + samples is the standard normal distribution. + + >>> import matplotlib.pyplot as plt + >>> dist = stats.norm() + >>> st_val = np.linspace(-5, 5, 100) + >>> pdf = dist.pdf(st_val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def st_plot(ax): # we'll reuse this + ... ax.plot(st_val, pdf) + ... ax.set_title("Skew Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> st_plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution as extreme or more extreme than the observed + value of the statistic. In a two-sided test, elements of the null + distribution greater than the observed statistic and elements of the null + distribution less than the negative of the observed statistic are both + considered "more extreme". + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> st_plot(ax) + >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props) + >>> i = st_val >= res.statistic + >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0') + >>> i = st_val <= -res.statistic + >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0') + >>> ax.set_xlim(-5, 5) + >>> ax.set_ylim(0, 0.1) + >>> plt.show() + >>> res.pvalue + 0.005455036974740185 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from a normally distributed population that produces such an + extreme value of the statistic - this may be taken as evidence against + the null hypothesis in favor of the alternative: the weights were not + drawn from a normal distribution. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [3]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + + Note that the standard normal distribution provides an asymptotic + approximation of the null distribution; it is only accurate for samples + with many observations. For small samples like ours, + `scipy.stats.monte_carlo_test` may provide a more accurate, albeit + stochastic, approximation of the exact p-value. + + >>> def statistic(x, axis): + ... # get just the skewtest statistic; ignore the p-value + ... return stats.skewtest(x, axis=axis).statistic + >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> st_plot(ax) + >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50), + ... density=True) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'Monte Carlo approximation\n(11 observations)']) + >>> plt.show() + >>> res.pvalue + 0.0062 # may vary + + In this case, the asymptotic approximation and Monte Carlo approximation + agree fairly closely, even for our small sample. + + """ + b2 = skew(a, axis) + n = a.shape[axis] + if n < 8: + raise ValueError( + "skewtest is not valid with less than 8 samples; %i samples" + " were given." % int(n)) + y = b2 * math.sqrt(((n + 1) * (n + 3)) / (6.0 * (n - 2))) + beta2 = (3.0 * (n**2 + 27*n - 70) * (n+1) * (n+3) / + ((n-2.0) * (n+5) * (n+7) * (n+9))) + W2 = -1 + math.sqrt(2 * (beta2 - 1)) + delta = 1 / math.sqrt(0.5 * math.log(W2)) + alpha = math.sqrt(2.0 / (W2 - 1)) + y = np.where(y == 0, 1, y) + Z = delta * np.log(y / alpha + np.sqrt((y / alpha)**2 + 1)) + + pvalue = _get_pvalue(Z, distributions.norm, alternative) + return SkewtestResult(Z[()], pvalue[()]) + + +KurtosistestResult = namedtuple('KurtosistestResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(KurtosistestResult, n_samples=1, too_small=4) +def kurtosistest(a, axis=0, nan_policy='propagate', alternative='two-sided'): + r"""Test whether a dataset has normal kurtosis. + + This function tests the null hypothesis that the kurtosis + of the population from which the sample was drawn is that + of the normal distribution. + + Parameters + ---------- + a : array + Array of the sample data. + axis : int or None, optional + Axis along which to compute test. Default is 0. If None, + compute over the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the kurtosis of the distribution underlying the sample + is different from that of the normal distribution + * 'less': the kurtosis of the distribution underlying the sample + is less than that of the normal distribution + * 'greater': the kurtosis of the distribution underlying the sample + is greater than that of the normal distribution + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float + The computed z-score for this test. + pvalue : float + The p-value for the hypothesis test. + + Notes + ----- + Valid only for n>20. This function uses the method described in [1]_. + + References + ---------- + .. [1] see e.g. F. J. Anscombe, W. J. Glynn, "Distribution of the kurtosis + statistic b2 for normal samples", Biometrika, vol. 70, pp. 227-234, 1983. + .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test + for normality (complete samples). Biometrika, 52(3/4), 591-611. + .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical + research. The open cardiovascular medicine journal, 2, 97. + + Examples + -------- + Suppose we wish to infer from measurements whether the weights of adult + human males in a medical study are not normally distributed [2]_. + The weights (lbs) are recorded in the array ``x`` below. + + >>> import numpy as np + >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) + + The kurtosis test from [1]_ begins by computing a statistic based on the + sample (excess/Fisher) kurtosis. + + >>> from scipy import stats + >>> res = stats.kurtosistest(x) + >>> res.statistic + 2.3048235214240873 + + (The test warns that our sample has too few observations to perform the + test. We'll return to this at the end of the example.) + Because normal distributions have zero excess kurtosis (by definition), + the magnitude of this statistic tends to be low for samples drawn from a + normal distribution. + + The test is performed by comparing the observed value of the + statistic against the null distribution: the distribution of statistic + values derived under the null hypothesis that the weights were drawn from + a normal distribution. + + For this test, the null distribution of the statistic for very large + samples is the standard normal distribution. + + >>> import matplotlib.pyplot as plt + >>> dist = stats.norm() + >>> kt_val = np.linspace(-5, 5, 100) + >>> pdf = dist.pdf(kt_val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def kt_plot(ax): # we'll reuse this + ... ax.plot(kt_val, pdf) + ... ax.set_title("Kurtosis Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> kt_plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution as extreme or more extreme than the observed + value of the statistic. In a two-sided test in which the statistic is + positive, elements of the null distribution greater than the observed + statistic and elements of the null distribution less than the negative of + the observed statistic are both considered "more extreme". + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> kt_plot(ax) + >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props) + >>> i = kt_val >= res.statistic + >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0') + >>> i = kt_val <= -res.statistic + >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0') + >>> ax.set_xlim(-5, 5) + >>> ax.set_ylim(0, 0.1) + >>> plt.show() + >>> res.pvalue + 0.0211764592113868 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from a normally distributed population that produces such an + extreme value of the statistic - this may be taken as evidence against + the null hypothesis in favor of the alternative: the weights were not + drawn from a normal distribution. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [3]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + + Note that the standard normal distribution provides an asymptotic + approximation of the null distribution; it is only accurate for samples + with many observations. This is the reason we received a warning at the + beginning of the example; our sample is quite small. In this case, + `scipy.stats.monte_carlo_test` may provide a more accurate, albeit + stochastic, approximation of the exact p-value. + + >>> def statistic(x, axis): + ... # get just the skewtest statistic; ignore the p-value + ... return stats.kurtosistest(x, axis=axis).statistic + >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> kt_plot(ax) + >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50), + ... density=True) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'Monte Carlo approximation\n(11 observations)']) + >>> plt.show() + >>> res.pvalue + 0.0272 # may vary + + Furthermore, despite their stochastic nature, p-values computed in this way + can be used to exactly control the rate of false rejections of the null + hypothesis [4]_. + + """ + n = a.shape[axis] + if n < 5: + raise ValueError( + "kurtosistest requires at least 5 observations; %i observations" + " were given." % int(n)) + if n < 20: + warnings.warn("kurtosistest only valid for n>=20 ... continuing " + "anyway, n=%i" % int(n), + stacklevel=2) + b2 = kurtosis(a, axis, fisher=False) + + E = 3.0*(n-1) / (n+1) + varb2 = 24.0*n*(n-2)*(n-3) / ((n+1)*(n+1.)*(n+3)*(n+5)) # [1]_ Eq. 1 + x = (b2-E) / np.sqrt(varb2) # [1]_ Eq. 4 + # [1]_ Eq. 2: + sqrtbeta1 = 6.0*(n*n-5*n+2)/((n+7)*(n+9)) * np.sqrt((6.0*(n+3)*(n+5)) / + (n*(n-2)*(n-3))) + # [1]_ Eq. 3: + A = 6.0 + 8.0/sqrtbeta1 * (2.0/sqrtbeta1 + np.sqrt(1+4.0/(sqrtbeta1**2))) + term1 = 1 - 2/(9.0*A) + denom = 1 + x*np.sqrt(2/(A-4.0)) + term2 = np.sign(denom) * np.where(denom == 0.0, np.nan, + np.power((1-2.0/A)/np.abs(denom), 1/3.0)) + if np.any(denom == 0): + msg = ("Test statistic not defined in some cases due to division by " + "zero. Return nan in that case...") + warnings.warn(msg, RuntimeWarning, stacklevel=2) + + Z = (term1 - term2) / np.sqrt(2/(9.0*A)) # [1]_ Eq. 5 + + pvalue = _get_pvalue(Z, distributions.norm, alternative) + return KurtosistestResult(Z[()], pvalue[()]) + + +NormaltestResult = namedtuple('NormaltestResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(NormaltestResult, n_samples=1, too_small=7) +def normaltest(a, axis=0, nan_policy='propagate'): + r"""Test whether a sample differs from a normal distribution. + + This function tests the null hypothesis that a sample comes + from a normal distribution. It is based on D'Agostino and + Pearson's [1]_, [2]_ test that combines skew and kurtosis to + produce an omnibus test of normality. + + Parameters + ---------- + a : array_like + The array containing the sample to be tested. + axis : int or None, optional + Axis along which to compute test. Default is 0. If None, + compute over the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + statistic : float or array + ``s^2 + k^2``, where ``s`` is the z-score returned by `skewtest` and + ``k`` is the z-score returned by `kurtosistest`. + pvalue : float or array + A 2-sided chi squared probability for the hypothesis test. + + References + ---------- + .. [1] D'Agostino, R. B. (1971), "An omnibus test of normality for + moderate and large sample size", Biometrika, 58, 341-348 + .. [2] D'Agostino, R. and Pearson, E. S. (1973), "Tests for departure from + normality", Biometrika, 60, 613-622 + .. [3] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test + for normality (complete samples). Biometrika, 52(3/4), 591-611. + .. [4] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [5] Panagiotakos, D. B. (2008). The value of p-value in biomedical + research. The open cardiovascular medicine journal, 2, 97. + + Examples + -------- + Suppose we wish to infer from measurements whether the weights of adult + human males in a medical study are not normally distributed [3]_. + The weights (lbs) are recorded in the array ``x`` below. + + >>> import numpy as np + >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) + + The normality test of [1]_ and [2]_ begins by computing a statistic based + on the sample skewness and kurtosis. + + >>> from scipy import stats + >>> res = stats.normaltest(x) + >>> res.statistic + 13.034263121192582 + + (The test warns that our sample has too few observations to perform the + test. We'll return to this at the end of the example.) + Because the normal distribution has zero skewness and zero + ("excess" or "Fisher") kurtosis, the value of this statistic tends to be + low for samples drawn from a normal distribution. + + The test is performed by comparing the observed value of the statistic + against the null distribution: the distribution of statistic values derived + under the null hypothesis that the weights were drawn from a normal + distribution. + For this normality test, the null distribution for very large samples is + the chi-squared distribution with two degrees of freedom. + + >>> import matplotlib.pyplot as plt + >>> dist = stats.chi2(df=2) + >>> stat_vals = np.linspace(0, 16, 100) + >>> pdf = dist.pdf(stat_vals) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(stat_vals, pdf) + ... ax.set_title("Normality Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution greater than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (13.5, 5e-4), (14, 5e-3), arrowprops=props) + >>> i = stat_vals >= res.statistic # index more extreme statistic values + >>> ax.fill_between(stat_vals[i], y1=0, y2=pdf[i]) + >>> ax.set_xlim(8, 16) + >>> ax.set_ylim(0, 0.01) + >>> plt.show() + >>> res.pvalue + 0.0014779023013100172 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from a normally distributed population that produces such an + extreme value of the statistic - this may be taken as evidence against + the null hypothesis in favor of the alternative: the weights were not + drawn from a normal distribution. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [4]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + + Note that the chi-squared distribution provides an asymptotic + approximation of the null distribution; it is only accurate for samples + with many observations. This is the reason we received a warning at the + beginning of the example; our sample is quite small. In this case, + `scipy.stats.monte_carlo_test` may provide a more accurate, albeit + stochastic, approximation of the exact p-value. + + >>> def statistic(x, axis): + ... # Get only the `normaltest` statistic; ignore approximate p-value + ... return stats.normaltest(x, axis=axis).statistic + >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic, + ... alternative='greater') + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> ax.hist(res.null_distribution, np.linspace(0, 25, 50), + ... density=True) + >>> ax.legend(['aymptotic approximation (many observations)', + ... 'Monte Carlo approximation (11 observations)']) + >>> ax.set_xlim(0, 14) + >>> plt.show() + >>> res.pvalue + 0.0082 # may vary + + Furthermore, despite their stochastic nature, p-values computed in this way + can be used to exactly control the rate of false rejections of the null + hypothesis [5]_. + + """ + s, _ = skewtest(a, axis) + k, _ = kurtosistest(a, axis) + k2 = s*s + k*k + + return NormaltestResult(k2, distributions.chi2.sf(k2, 2)) + + +@_axis_nan_policy_factory(SignificanceResult, default_axis=None) +def jarque_bera(x, *, axis=None): + r"""Perform the Jarque-Bera goodness of fit test on sample data. + + The Jarque-Bera test tests whether the sample data has the skewness and + kurtosis matching a normal distribution. + + Note that this test only works for a large enough number of data samples + (>2000) as the test statistic asymptotically has a Chi-squared distribution + with 2 degrees of freedom. + + Parameters + ---------- + x : array_like + Observations of a random variable. + axis : int or None, default: 0 + If an int, the axis of the input along which to compute the statistic. + The statistic of each axis-slice (e.g. row) of the input will appear in + a corresponding element of the output. + If ``None``, the input will be raveled before computing the statistic. + + Returns + ------- + result : SignificanceResult + An object with the following attributes: + + statistic : float + The test statistic. + pvalue : float + The p-value for the hypothesis test. + + References + ---------- + .. [1] Jarque, C. and Bera, A. (1980) "Efficient tests for normality, + homoscedasticity and serial independence of regression residuals", + 6 Econometric Letters 255-259. + .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test + for normality (complete samples). Biometrika, 52(3/4), 591-611. + .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical + research. The open cardiovascular medicine journal, 2, 97. + + Examples + -------- + Suppose we wish to infer from measurements whether the weights of adult + human males in a medical study are not normally distributed [2]_. + The weights (lbs) are recorded in the array ``x`` below. + + >>> import numpy as np + >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) + + The Jarque-Bera test begins by computing a statistic based on the sample + skewness and kurtosis. + + >>> from scipy import stats + >>> res = stats.jarque_bera(x) + >>> res.statistic + 6.982848237344646 + + Because the normal distribution has zero skewness and zero + ("excess" or "Fisher") kurtosis, the value of this statistic tends to be + low for samples drawn from a normal distribution. + + The test is performed by comparing the observed value of the statistic + against the null distribution: the distribution of statistic values derived + under the null hypothesis that the weights were drawn from a normal + distribution. + For the Jarque-Bera test, the null distribution for very large samples is + the chi-squared distribution with two degrees of freedom. + + >>> import matplotlib.pyplot as plt + >>> dist = stats.chi2(df=2) + >>> jb_val = np.linspace(0, 11, 100) + >>> pdf = dist.pdf(jb_val) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def jb_plot(ax): # we'll reuse this + ... ax.plot(jb_val, pdf) + ... ax.set_title("Jarque-Bera Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> jb_plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution greater than or equal to the observed value of the + statistic. + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> jb_plot(ax) + >>> pvalue = dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (7.5, 0.01), (8, 0.05), arrowprops=props) + >>> i = jb_val >= res.statistic # indices of more extreme statistic values + >>> ax.fill_between(jb_val[i], y1=0, y2=pdf[i]) + >>> ax.set_xlim(0, 11) + >>> ax.set_ylim(0, 0.3) + >>> plt.show() + >>> res.pvalue + 0.03045746622458189 + + If the p-value is "small" - that is, if there is a low probability of + sampling data from a normally distributed population that produces such an + extreme value of the statistic - this may be taken as evidence against + the null hypothesis in favor of the alternative: the weights were not + drawn from a normal distribution. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [3]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + + Note that the chi-squared distribution provides an asymptotic approximation + of the null distribution; it is only accurate for samples with many + observations. For small samples like ours, `scipy.stats.monte_carlo_test` + may provide a more accurate, albeit stochastic, approximation of the + exact p-value. + + >>> def statistic(x, axis): + ... # underlying calculation of the Jarque Bera statistic + ... s = stats.skew(x, axis=axis) + ... k = stats.kurtosis(x, axis=axis) + ... return x.shape[axis]/6 * (s**2 + k**2/4) + >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic, + ... alternative='greater') + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> jb_plot(ax) + >>> ax.hist(res.null_distribution, np.linspace(0, 10, 50), + ... density=True) + >>> ax.legend(['aymptotic approximation (many observations)', + ... 'Monte Carlo approximation (11 observations)']) + >>> plt.show() + >>> res.pvalue + 0.0097 # may vary + + Furthermore, despite their stochastic nature, p-values computed in this way + can be used to exactly control the rate of false rejections of the null + hypothesis [4]_. + + """ + x = np.asarray(x) + if axis is None: + x = x.ravel() + axis = 0 + + n = x.shape[axis] + if n == 0: + raise ValueError('At least one observation is required.') + + mu = x.mean(axis=axis, keepdims=True) + diffx = x - mu + s = skew(diffx, axis=axis, _no_deco=True) + k = kurtosis(diffx, axis=axis, _no_deco=True) + statistic = n / 6 * (s**2 + k**2 / 4) + pvalue = distributions.chi2.sf(statistic, df=2) + + return SignificanceResult(statistic, pvalue) + + +##################################### +# FREQUENCY FUNCTIONS # +##################################### + + +def scoreatpercentile(a, per, limit=(), interpolation_method='fraction', + axis=None): + """Calculate the score at a given percentile of the input sequence. + + For example, the score at `per=50` is the median. If the desired quantile + lies between two data points, we interpolate between them, according to + the value of `interpolation`. If the parameter `limit` is provided, it + should be a tuple (lower, upper) of two values. + + Parameters + ---------- + a : array_like + A 1-D array of values from which to extract score. + per : array_like + Percentile(s) at which to extract score. Values should be in range + [0,100]. + limit : tuple, optional + Tuple of two scalars, the lower and upper limits within which to + compute the percentile. Values of `a` outside + this (closed) interval will be ignored. + interpolation_method : {'fraction', 'lower', 'higher'}, optional + Specifies the interpolation method to use, + when the desired quantile lies between two data points `i` and `j` + The following options are available (default is 'fraction'): + + * 'fraction': ``i + (j - i) * fraction`` where ``fraction`` is the + fractional part of the index surrounded by ``i`` and ``j`` + * 'lower': ``i`` + * 'higher': ``j`` + + axis : int, optional + Axis along which the percentiles are computed. Default is None. If + None, compute over the whole array `a`. + + Returns + ------- + score : float or ndarray + Score at percentile(s). + + See Also + -------- + percentileofscore, numpy.percentile + + Notes + ----- + This function will become obsolete in the future. + For NumPy 1.9 and higher, `numpy.percentile` provides all the functionality + that `scoreatpercentile` provides. And it's significantly faster. + Therefore it's recommended to use `numpy.percentile` for users that have + numpy >= 1.9. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> a = np.arange(100) + >>> stats.scoreatpercentile(a, 50) + 49.5 + + """ + # adapted from NumPy's percentile function. When we require numpy >= 1.8, + # the implementation of this function can be replaced by np.percentile. + a = np.asarray(a) + if a.size == 0: + # empty array, return nan(s) with shape matching `per` + if np.isscalar(per): + return np.nan + else: + return np.full(np.asarray(per).shape, np.nan, dtype=np.float64) + + if limit: + a = a[(limit[0] <= a) & (a <= limit[1])] + + sorted_ = np.sort(a, axis=axis) + if axis is None: + axis = 0 + + return _compute_qth_percentile(sorted_, per, interpolation_method, axis) + + +# handle sequence of per's without calling sort multiple times +def _compute_qth_percentile(sorted_, per, interpolation_method, axis): + if not np.isscalar(per): + score = [_compute_qth_percentile(sorted_, i, + interpolation_method, axis) + for i in per] + return np.array(score) + + if not (0 <= per <= 100): + raise ValueError("percentile must be in the range [0, 100]") + + indexer = [slice(None)] * sorted_.ndim + idx = per / 100. * (sorted_.shape[axis] - 1) + + if int(idx) != idx: + # round fractional indices according to interpolation method + if interpolation_method == 'lower': + idx = int(np.floor(idx)) + elif interpolation_method == 'higher': + idx = int(np.ceil(idx)) + elif interpolation_method == 'fraction': + pass # keep idx as fraction and interpolate + else: + raise ValueError("interpolation_method can only be 'fraction', " + "'lower' or 'higher'") + + i = int(idx) + if i == idx: + indexer[axis] = slice(i, i + 1) + weights = array(1) + sumval = 1.0 + else: + indexer[axis] = slice(i, i + 2) + j = i + 1 + weights = array([(j - idx), (idx - i)], float) + wshape = [1] * sorted_.ndim + wshape[axis] = 2 + weights.shape = wshape + sumval = weights.sum() + + # Use np.add.reduce (== np.sum but a little faster) to coerce data type + return np.add.reduce(sorted_[tuple(indexer)] * weights, axis=axis) / sumval + + +def percentileofscore(a, score, kind='rank', nan_policy='propagate'): + """Compute the percentile rank of a score relative to a list of scores. + + A `percentileofscore` of, for example, 80% means that 80% of the + scores in `a` are below the given score. In the case of gaps or + ties, the exact definition depends on the optional keyword, `kind`. + + Parameters + ---------- + a : array_like + A 1-D array to which `score` is compared. + score : array_like + Scores to compute percentiles for. + kind : {'rank', 'weak', 'strict', 'mean'}, optional + Specifies the interpretation of the resulting score. + The following options are available (default is 'rank'): + + * 'rank': Average percentage ranking of score. In case of multiple + matches, average the percentage rankings of all matching scores. + * 'weak': This kind corresponds to the definition of a cumulative + distribution function. A percentileofscore of 80% means that 80% + of values are less than or equal to the provided score. + * 'strict': Similar to "weak", except that only values that are + strictly less than the given score are counted. + * 'mean': The average of the "weak" and "strict" scores, often used + in testing. See https://en.wikipedia.org/wiki/Percentile_rank + nan_policy : {'propagate', 'raise', 'omit'}, optional + Specifies how to treat `nan` values in `a`. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan (for each value in `score`). + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + pcos : float + Percentile-position of score (0-100) relative to `a`. + + See Also + -------- + numpy.percentile + scipy.stats.scoreatpercentile, scipy.stats.rankdata + + Examples + -------- + Three-quarters of the given values lie below a given score: + + >>> import numpy as np + >>> from scipy import stats + >>> stats.percentileofscore([1, 2, 3, 4], 3) + 75.0 + + With multiple matches, note how the scores of the two matches, 0.6 + and 0.8 respectively, are averaged: + + >>> stats.percentileofscore([1, 2, 3, 3, 4], 3) + 70.0 + + Only 2/5 values are strictly less than 3: + + >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='strict') + 40.0 + + But 4/5 values are less than or equal to 3: + + >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='weak') + 80.0 + + The average between the weak and the strict scores is: + + >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='mean') + 60.0 + + Score arrays (of any dimensionality) are supported: + + >>> stats.percentileofscore([1, 2, 3, 3, 4], [2, 3]) + array([40., 70.]) + + The inputs can be infinite: + + >>> stats.percentileofscore([-np.inf, 0, 1, np.inf], [1, 2, np.inf]) + array([75., 75., 100.]) + + If `a` is empty, then the resulting percentiles are all `nan`: + + >>> stats.percentileofscore([], [1, 2]) + array([nan, nan]) + """ + + a = np.asarray(a) + n = len(a) + score = np.asarray(score) + + # Nan treatment + cna, npa = _contains_nan(a, nan_policy, use_summation=False) + cns, nps = _contains_nan(score, nan_policy, use_summation=False) + + if (cna or cns) and nan_policy == 'raise': + raise ValueError("The input contains nan values") + + if cns: + # If a score is nan, then the output should be nan + # (also if nan_policy is "omit", because it only applies to `a`) + score = ma.masked_where(np.isnan(score), score) + + if cna: + if nan_policy == "omit": + # Don't count nans + a = ma.masked_where(np.isnan(a), a) + n = a.count() + + if nan_policy == "propagate": + # All outputs should be nans + n = 0 + + # Cannot compare to empty list ==> nan + if n == 0: + perct = np.full_like(score, np.nan, dtype=np.float64) + + else: + # Prepare broadcasting + score = score[..., None] + + def count(x): + return np.count_nonzero(x, -1) + + # Main computations/logic + if kind == 'rank': + left = count(a < score) + right = count(a <= score) + plus1 = left < right + perct = (left + right + plus1) * (50.0 / n) + elif kind == 'strict': + perct = count(a < score) * (100.0 / n) + elif kind == 'weak': + perct = count(a <= score) * (100.0 / n) + elif kind == 'mean': + left = count(a < score) + right = count(a <= score) + perct = (left + right) * (50.0 / n) + else: + raise ValueError( + "kind can only be 'rank', 'strict', 'weak' or 'mean'") + + # Re-insert nan values + perct = ma.filled(perct, np.nan) + + if perct.ndim == 0: + return perct[()] + return perct + + +HistogramResult = namedtuple('HistogramResult', + ('count', 'lowerlimit', 'binsize', 'extrapoints')) + + +def _histogram(a, numbins=10, defaultlimits=None, weights=None, + printextras=False): + """Create a histogram. + + Separate the range into several bins and return the number of instances + in each bin. + + Parameters + ---------- + a : array_like + Array of scores which will be put into bins. + numbins : int, optional + The number of bins to use for the histogram. Default is 10. + defaultlimits : tuple (lower, upper), optional + The lower and upper values for the range of the histogram. + If no value is given, a range slightly larger than the range of the + values in a is used. Specifically ``(a.min() - s, a.max() + s)``, + where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. + weights : array_like, optional + The weights for each value in `a`. Default is None, which gives each + value a weight of 1.0 + printextras : bool, optional + If True, if there are extra points (i.e. the points that fall outside + the bin limits) a warning is raised saying how many of those points + there are. Default is False. + + Returns + ------- + count : ndarray + Number of points (or sum of weights) in each bin. + lowerlimit : float + Lowest value of histogram, the lower limit of the first bin. + binsize : float + The size of the bins (all bins have the same size). + extrapoints : int + The number of points outside the range of the histogram. + + See Also + -------- + numpy.histogram + + Notes + ----- + This histogram is based on numpy's histogram but has a larger range by + default if default limits is not set. + + """ + a = np.ravel(a) + if defaultlimits is None: + if a.size == 0: + # handle empty arrays. Undetermined range, so use 0-1. + defaultlimits = (0, 1) + else: + # no range given, so use values in `a` + data_min = a.min() + data_max = a.max() + # Have bins extend past min and max values slightly + s = (data_max - data_min) / (2. * (numbins - 1.)) + defaultlimits = (data_min - s, data_max + s) + + # use numpy's histogram method to compute bins + hist, bin_edges = np.histogram(a, bins=numbins, range=defaultlimits, + weights=weights) + # hist are not always floats, convert to keep with old output + hist = np.array(hist, dtype=float) + # fixed width for bins is assumed, as numpy's histogram gives + # fixed width bins for int values for 'bins' + binsize = bin_edges[1] - bin_edges[0] + # calculate number of extra points + extrapoints = len([v for v in a + if defaultlimits[0] > v or v > defaultlimits[1]]) + if extrapoints > 0 and printextras: + warnings.warn("Points outside given histogram range = %s" % extrapoints, + stacklevel=3,) + + return HistogramResult(hist, defaultlimits[0], binsize, extrapoints) + + +CumfreqResult = namedtuple('CumfreqResult', + ('cumcount', 'lowerlimit', 'binsize', + 'extrapoints')) + + +def cumfreq(a, numbins=10, defaultreallimits=None, weights=None): + """Return a cumulative frequency histogram, using the histogram function. + + A cumulative histogram is a mapping that counts the cumulative number of + observations in all of the bins up to the specified bin. + + Parameters + ---------- + a : array_like + Input array. + numbins : int, optional + The number of bins to use for the histogram. Default is 10. + defaultreallimits : tuple (lower, upper), optional + The lower and upper values for the range of the histogram. + If no value is given, a range slightly larger than the range of the + values in `a` is used. Specifically ``(a.min() - s, a.max() + s)``, + where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. + weights : array_like, optional + The weights for each value in `a`. Default is None, which gives each + value a weight of 1.0 + + Returns + ------- + cumcount : ndarray + Binned values of cumulative frequency. + lowerlimit : float + Lower real limit + binsize : float + Width of each bin. + extrapoints : int + Extra points. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> x = [1, 4, 2, 1, 3, 1] + >>> res = stats.cumfreq(x, numbins=4, defaultreallimits=(1.5, 5)) + >>> res.cumcount + array([ 1., 2., 3., 3.]) + >>> res.extrapoints + 3 + + Create a normal distribution with 1000 random values + + >>> samples = stats.norm.rvs(size=1000, random_state=rng) + + Calculate cumulative frequencies + + >>> res = stats.cumfreq(samples, numbins=25) + + Calculate space of values for x + + >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.cumcount.size, + ... res.cumcount.size) + + Plot histogram and cumulative histogram + + >>> fig = plt.figure(figsize=(10, 4)) + >>> ax1 = fig.add_subplot(1, 2, 1) + >>> ax2 = fig.add_subplot(1, 2, 2) + >>> ax1.hist(samples, bins=25) + >>> ax1.set_title('Histogram') + >>> ax2.bar(x, res.cumcount, width=res.binsize) + >>> ax2.set_title('Cumulative histogram') + >>> ax2.set_xlim([x.min(), x.max()]) + + >>> plt.show() + + """ + h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights) + cumhist = np.cumsum(h * 1, axis=0) + return CumfreqResult(cumhist, l, b, e) + + +RelfreqResult = namedtuple('RelfreqResult', + ('frequency', 'lowerlimit', 'binsize', + 'extrapoints')) + + +def relfreq(a, numbins=10, defaultreallimits=None, weights=None): + """Return a relative frequency histogram, using the histogram function. + + A relative frequency histogram is a mapping of the number of + observations in each of the bins relative to the total of observations. + + Parameters + ---------- + a : array_like + Input array. + numbins : int, optional + The number of bins to use for the histogram. Default is 10. + defaultreallimits : tuple (lower, upper), optional + The lower and upper values for the range of the histogram. + If no value is given, a range slightly larger than the range of the + values in a is used. Specifically ``(a.min() - s, a.max() + s)``, + where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. + weights : array_like, optional + The weights for each value in `a`. Default is None, which gives each + value a weight of 1.0 + + Returns + ------- + frequency : ndarray + Binned values of relative frequency. + lowerlimit : float + Lower real limit. + binsize : float + Width of each bin. + extrapoints : int + Extra points. + + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> a = np.array([2, 4, 1, 2, 3, 2]) + >>> res = stats.relfreq(a, numbins=4) + >>> res.frequency + array([ 0.16666667, 0.5 , 0.16666667, 0.16666667]) + >>> np.sum(res.frequency) # relative frequencies should add up to 1 + 1.0 + + Create a normal distribution with 1000 random values + + >>> samples = stats.norm.rvs(size=1000, random_state=rng) + + Calculate relative frequencies + + >>> res = stats.relfreq(samples, numbins=25) + + Calculate space of values for x + + >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.frequency.size, + ... res.frequency.size) + + Plot relative frequency histogram + + >>> fig = plt.figure(figsize=(5, 4)) + >>> ax = fig.add_subplot(1, 1, 1) + >>> ax.bar(x, res.frequency, width=res.binsize) + >>> ax.set_title('Relative frequency histogram') + >>> ax.set_xlim([x.min(), x.max()]) + + >>> plt.show() + + """ + a = np.asanyarray(a) + h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights) + h = h / a.shape[0] + + return RelfreqResult(h, l, b, e) + + +##################################### +# VARIABILITY FUNCTIONS # +##################################### + +def obrientransform(*samples): + """Compute the O'Brien transform on input data (any number of arrays). + + Used to test for homogeneity of variance prior to running one-way stats. + Each array in ``*samples`` is one level of a factor. + If `f_oneway` is run on the transformed data and found significant, + the variances are unequal. From Maxwell and Delaney [1]_, p.112. + + Parameters + ---------- + sample1, sample2, ... : array_like + Any number of arrays. + + Returns + ------- + obrientransform : ndarray + Transformed data for use in an ANOVA. The first dimension + of the result corresponds to the sequence of transformed + arrays. If the arrays given are all 1-D of the same length, + the return value is a 2-D array; otherwise it is a 1-D array + of type object, with each element being an ndarray. + + References + ---------- + .. [1] S. E. Maxwell and H. D. Delaney, "Designing Experiments and + Analyzing Data: A Model Comparison Perspective", Wadsworth, 1990. + + Examples + -------- + We'll test the following data sets for differences in their variance. + + >>> x = [10, 11, 13, 9, 7, 12, 12, 9, 10] + >>> y = [13, 21, 5, 10, 8, 14, 10, 12, 7, 15] + + Apply the O'Brien transform to the data. + + >>> from scipy.stats import obrientransform + >>> tx, ty = obrientransform(x, y) + + Use `scipy.stats.f_oneway` to apply a one-way ANOVA test to the + transformed data. + + >>> from scipy.stats import f_oneway + >>> F, p = f_oneway(tx, ty) + >>> p + 0.1314139477040335 + + If we require that ``p < 0.05`` for significance, we cannot conclude + that the variances are different. + + """ + TINY = np.sqrt(np.finfo(float).eps) + + # `arrays` will hold the transformed arguments. + arrays = [] + sLast = None + + for sample in samples: + a = np.asarray(sample) + n = len(a) + mu = np.mean(a) + sq = (a - mu)**2 + sumsq = sq.sum() + + # The O'Brien transform. + t = ((n - 1.5) * n * sq - 0.5 * sumsq) / ((n - 1) * (n - 2)) + + # Check that the mean of the transformed data is equal to the + # original variance. + var = sumsq / (n - 1) + if abs(var - np.mean(t)) > TINY: + raise ValueError('Lack of convergence in obrientransform.') + + arrays.append(t) + sLast = a.shape + + if sLast: + for arr in arrays[:-1]: + if sLast != arr.shape: + return np.array(arrays, dtype=object) + return np.array(arrays) + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, too_small=1 +) +def sem(a, axis=0, ddof=1, nan_policy='propagate'): + """Compute standard error of the mean. + + Calculate the standard error of the mean (or standard error of + measurement) of the values in the input array. + + Parameters + ---------- + a : array_like + An array containing the values for which the standard error is + returned. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + ddof : int, optional + Delta degrees-of-freedom. How many degrees of freedom to adjust + for bias in limited samples relative to the population estimate + of variance. Defaults to 1. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + s : ndarray or float + The standard error of the mean in the sample(s), along the input axis. + + Notes + ----- + The default value for `ddof` is different to the default (0) used by other + ddof containing routines, such as np.std and np.nanstd. + + Examples + -------- + Find standard error along the first axis: + + >>> import numpy as np + >>> from scipy import stats + >>> a = np.arange(20).reshape(5,4) + >>> stats.sem(a) + array([ 2.8284, 2.8284, 2.8284, 2.8284]) + + Find standard error across the whole array, using n degrees of freedom: + + >>> stats.sem(a, axis=None, ddof=0) + 1.2893796958227628 + + """ + n = a.shape[axis] + s = np.std(a, axis=axis, ddof=ddof) / np.sqrt(n) + return s + + +def _isconst(x): + """ + Check if all values in x are the same. nans are ignored. + + x must be a 1d array. + + The return value is a 1d array with length 1, so it can be used + in np.apply_along_axis. + """ + y = x[~np.isnan(x)] + if y.size == 0: + return np.array([True]) + else: + return (y[0] == y).all(keepdims=True) + + +def _quiet_nanmean(x): + """ + Compute nanmean for the 1d array x, but quietly return nan if x is all nan. + + The return value is a 1d array with length 1, so it can be used + in np.apply_along_axis. + """ + y = x[~np.isnan(x)] + if y.size == 0: + return np.array([np.nan]) + else: + return np.mean(y, keepdims=True) + + +def _quiet_nanstd(x, ddof=0): + """ + Compute nanstd for the 1d array x, but quietly return nan if x is all nan. + + The return value is a 1d array with length 1, so it can be used + in np.apply_along_axis. + """ + y = x[~np.isnan(x)] + if y.size == 0: + return np.array([np.nan]) + else: + return np.std(y, keepdims=True, ddof=ddof) + + +def zscore(a, axis=0, ddof=0, nan_policy='propagate'): + """ + Compute the z score. + + Compute the z score of each value in the sample, relative to the + sample mean and standard deviation. + + Parameters + ---------- + a : array_like + An array like object containing the sample data. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + ddof : int, optional + Degrees of freedom correction in the calculation of the + standard deviation. Default is 0. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. 'propagate' returns nan, + 'raise' throws an error, 'omit' performs the calculations ignoring nan + values. Default is 'propagate'. Note that when the value is 'omit', + nans in the input also propagate to the output, but they do not affect + the z-scores computed for the non-nan values. + + Returns + ------- + zscore : array_like + The z-scores, standardized by mean and standard deviation of + input array `a`. + + See Also + -------- + numpy.mean : Arithmetic average + numpy.std : Arithmetic standard deviation + scipy.stats.gzscore : Geometric standard score + + Notes + ----- + This function preserves ndarray subclasses, and works also with + matrices and masked arrays (it uses `asanyarray` instead of + `asarray` for parameters). + + References + ---------- + .. [1] "Standard score", *Wikipedia*, + https://en.wikipedia.org/wiki/Standard_score. + .. [2] Huck, S. W., Cross, T. L., Clark, S. B, "Overcoming misconceptions + about Z-scores", Teaching Statistics, vol. 8, pp. 38-40, 1986 + + Examples + -------- + >>> import numpy as np + >>> a = np.array([ 0.7972, 0.0767, 0.4383, 0.7866, 0.8091, + ... 0.1954, 0.6307, 0.6599, 0.1065, 0.0508]) + >>> from scipy import stats + >>> stats.zscore(a) + array([ 1.1273, -1.247 , -0.0552, 1.0923, 1.1664, -0.8559, 0.5786, + 0.6748, -1.1488, -1.3324]) + + Computing along a specified axis, using n-1 degrees of freedom + (``ddof=1``) to calculate the standard deviation: + + >>> b = np.array([[ 0.3148, 0.0478, 0.6243, 0.4608], + ... [ 0.7149, 0.0775, 0.6072, 0.9656], + ... [ 0.6341, 0.1403, 0.9759, 0.4064], + ... [ 0.5918, 0.6948, 0.904 , 0.3721], + ... [ 0.0921, 0.2481, 0.1188, 0.1366]]) + >>> stats.zscore(b, axis=1, ddof=1) + array([[-0.19264823, -1.28415119, 1.07259584, 0.40420358], + [ 0.33048416, -1.37380874, 0.04251374, 1.00081084], + [ 0.26796377, -1.12598418, 1.23283094, -0.37481053], + [-0.22095197, 0.24468594, 1.19042819, -1.21416216], + [-0.82780366, 1.4457416 , -0.43867764, -0.1792603 ]]) + + An example with `nan_policy='omit'`: + + >>> x = np.array([[25.11, 30.10, np.nan, 32.02, 43.15], + ... [14.95, 16.06, 121.25, 94.35, 29.81]]) + >>> stats.zscore(x, axis=1, nan_policy='omit') + array([[-1.13490897, -0.37830299, nan, -0.08718406, 1.60039602], + [-0.91611681, -0.89090508, 1.4983032 , 0.88731639, -0.5785977 ]]) + """ + return zmap(a, a, axis=axis, ddof=ddof, nan_policy=nan_policy) + + +def gzscore(a, *, axis=0, ddof=0, nan_policy='propagate'): + """ + Compute the geometric standard score. + + Compute the geometric z score of each strictly positive value in the + sample, relative to the geometric mean and standard deviation. + Mathematically the geometric z score can be evaluated as:: + + gzscore = log(a/gmu) / log(gsigma) + + where ``gmu`` (resp. ``gsigma``) is the geometric mean (resp. standard + deviation). + + Parameters + ---------- + a : array_like + Sample data. + axis : int or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + ddof : int, optional + Degrees of freedom correction in the calculation of the + standard deviation. Default is 0. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. 'propagate' returns nan, + 'raise' throws an error, 'omit' performs the calculations ignoring nan + values. Default is 'propagate'. Note that when the value is 'omit', + nans in the input also propagate to the output, but they do not affect + the geometric z scores computed for the non-nan values. + + Returns + ------- + gzscore : array_like + The geometric z scores, standardized by geometric mean and geometric + standard deviation of input array `a`. + + See Also + -------- + gmean : Geometric mean + gstd : Geometric standard deviation + zscore : Standard score + + Notes + ----- + This function preserves ndarray subclasses, and works also with + matrices and masked arrays (it uses ``asanyarray`` instead of + ``asarray`` for parameters). + + .. versionadded:: 1.8 + + References + ---------- + .. [1] "Geometric standard score", *Wikipedia*, + https://en.wikipedia.org/wiki/Geometric_standard_deviation#Geometric_standard_score. + + Examples + -------- + Draw samples from a log-normal distribution: + + >>> import numpy as np + >>> from scipy.stats import zscore, gzscore + >>> import matplotlib.pyplot as plt + + >>> rng = np.random.default_rng() + >>> mu, sigma = 3., 1. # mean and standard deviation + >>> x = rng.lognormal(mu, sigma, size=500) + + Display the histogram of the samples: + + >>> fig, ax = plt.subplots() + >>> ax.hist(x, 50) + >>> plt.show() + + Display the histogram of the samples standardized by the classical zscore. + Distribution is rescaled but its shape is unchanged. + + >>> fig, ax = plt.subplots() + >>> ax.hist(zscore(x), 50) + >>> plt.show() + + Demonstrate that the distribution of geometric zscores is rescaled and + quasinormal: + + >>> fig, ax = plt.subplots() + >>> ax.hist(gzscore(x), 50) + >>> plt.show() + + """ + a = np.asanyarray(a) + log = ma.log if isinstance(a, ma.MaskedArray) else np.log + + return zscore(log(a), axis=axis, ddof=ddof, nan_policy=nan_policy) + + +def zmap(scores, compare, axis=0, ddof=0, nan_policy='propagate'): + """ + Calculate the relative z-scores. + + Return an array of z-scores, i.e., scores that are standardized to + zero mean and unit variance, where mean and variance are calculated + from the comparison array. + + Parameters + ---------- + scores : array_like + The input for which z-scores are calculated. + compare : array_like + The input from which the mean and standard deviation of the + normalization are taken; assumed to have the same dimension as + `scores`. + axis : int or None, optional + Axis over which mean and variance of `compare` are calculated. + Default is 0. If None, compute over the whole array `scores`. + ddof : int, optional + Degrees of freedom correction in the calculation of the + standard deviation. Default is 0. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle the occurrence of nans in `compare`. + 'propagate' returns nan, 'raise' raises an exception, 'omit' + performs the calculations ignoring nan values. Default is + 'propagate'. Note that when the value is 'omit', nans in `scores` + also propagate to the output, but they do not affect the z-scores + computed for the non-nan values. + + Returns + ------- + zscore : array_like + Z-scores, in the same shape as `scores`. + + Notes + ----- + This function preserves ndarray subclasses, and works also with + matrices and masked arrays (it uses `asanyarray` instead of + `asarray` for parameters). + + Examples + -------- + >>> from scipy.stats import zmap + >>> a = [0.5, 2.0, 2.5, 3] + >>> b = [0, 1, 2, 3, 4] + >>> zmap(a, b) + array([-1.06066017, 0. , 0.35355339, 0.70710678]) + + """ + a = np.asanyarray(compare) + + if a.size == 0: + return np.empty(a.shape) + + contains_nan, nan_policy = _contains_nan(a, nan_policy) + + if contains_nan and nan_policy == 'omit': + if axis is None: + mn = _quiet_nanmean(a.ravel()) + std = _quiet_nanstd(a.ravel(), ddof=ddof) + isconst = _isconst(a.ravel()) + else: + mn = np.apply_along_axis(_quiet_nanmean, axis, a) + std = np.apply_along_axis(_quiet_nanstd, axis, a, ddof=ddof) + isconst = np.apply_along_axis(_isconst, axis, a) + else: + mn = a.mean(axis=axis, keepdims=True) + std = a.std(axis=axis, ddof=ddof, keepdims=True) + # The intent is to check whether all elements of `a` along `axis` are + # identical. Due to finite precision arithmetic, comparing elements + # against `mn` doesn't work. Previously, this compared elements to + # `_first`, but that extracts the element at index 0 regardless of + # whether it is masked. As a simple fix, compare against `min`. + a0 = a.min(axis=axis, keepdims=True) + isconst = (a == a0).all(axis=axis, keepdims=True) + + # Set std deviations that are 0 to 1 to avoid division by 0. + std[isconst] = 1.0 + z = (scores - mn) / std + # Set the outputs associated with a constant input to nan. + z[np.broadcast_to(isconst, z.shape)] = np.nan + return z + + +def gstd(a, axis=0, ddof=1): + """ + Calculate the geometric standard deviation of an array. + + The geometric standard deviation describes the spread of a set of numbers + where the geometric mean is preferred. It is a multiplicative factor, and + so a dimensionless quantity. + + It is defined as the exponent of the standard deviation of ``log(a)``. + Mathematically the population geometric standard deviation can be + evaluated as:: + + gstd = exp(std(log(a))) + + .. versionadded:: 1.3.0 + + Parameters + ---------- + a : array_like + An array like object containing the sample data. + axis : int, tuple or None, optional + Axis along which to operate. Default is 0. If None, compute over + the whole array `a`. + ddof : int, optional + Degree of freedom correction in the calculation of the + geometric standard deviation. Default is 1. + + Returns + ------- + gstd : ndarray or float + An array of the geometric standard deviation. If `axis` is None or `a` + is a 1d array a float is returned. + + See Also + -------- + gmean : Geometric mean + numpy.std : Standard deviation + gzscore : Geometric standard score + + Notes + ----- + As the calculation requires the use of logarithms the geometric standard + deviation only supports strictly positive values. Any non-positive or + infinite values will raise a `ValueError`. + The geometric standard deviation is sometimes confused with the exponent of + the standard deviation, ``exp(std(a))``. Instead the geometric standard + deviation is ``exp(std(log(a)))``. + The default value for `ddof` is different to the default value (0) used + by other ddof containing functions, such as ``np.std`` and ``np.nanstd``. + + References + ---------- + .. [1] "Geometric standard deviation", *Wikipedia*, + https://en.wikipedia.org/wiki/Geometric_standard_deviation. + .. [2] Kirkwood, T. B., "Geometric means and measures of dispersion", + Biometrics, vol. 35, pp. 908-909, 1979 + + Examples + -------- + Find the geometric standard deviation of a log-normally distributed sample. + Note that the standard deviation of the distribution is one, on a + log scale this evaluates to approximately ``exp(1)``. + + >>> import numpy as np + >>> from scipy.stats import gstd + >>> rng = np.random.default_rng() + >>> sample = rng.lognormal(mean=0, sigma=1, size=1000) + >>> gstd(sample) + 2.810010162475324 + + Compute the geometric standard deviation of a multidimensional array and + of a given axis. + + >>> a = np.arange(1, 25).reshape(2, 3, 4) + >>> gstd(a, axis=None) + 2.2944076136018947 + >>> gstd(a, axis=2) + array([[1.82424757, 1.22436866, 1.13183117], + [1.09348306, 1.07244798, 1.05914985]]) + >>> gstd(a, axis=(1,2)) + array([2.12939215, 1.22120169]) + + The geometric standard deviation further handles masked arrays. + + >>> a = np.arange(1, 25).reshape(2, 3, 4) + >>> ma = np.ma.masked_where(a > 16, a) + >>> ma + masked_array( + data=[[[1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12]], + [[13, 14, 15, 16], + [--, --, --, --], + [--, --, --, --]]], + mask=[[[False, False, False, False], + [False, False, False, False], + [False, False, False, False]], + [[False, False, False, False], + [ True, True, True, True], + [ True, True, True, True]]], + fill_value=999999) + >>> gstd(ma, axis=2) + masked_array( + data=[[1.8242475707663655, 1.2243686572447428, 1.1318311657788478], + [1.0934830582350938, --, --]], + mask=[[False, False, False], + [False, True, True]], + fill_value=999999) + + """ + a = np.asanyarray(a) + log = ma.log if isinstance(a, ma.MaskedArray) else np.log + + try: + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + return np.exp(np.std(log(a), axis=axis, ddof=ddof)) + except RuntimeWarning as w: + if np.isinf(a).any(): + raise ValueError( + 'Infinite value encountered. The geometric standard deviation ' + 'is defined for strictly positive values only.' + ) from w + a_nan = np.isnan(a) + a_nan_any = a_nan.any() + # exclude NaN's from negativity check, but + # avoid expensive masking for arrays with no NaN + if ((a_nan_any and np.less_equal(np.nanmin(a), 0)) or + (not a_nan_any and np.less_equal(a, 0).any())): + raise ValueError( + 'Non positive value encountered. The geometric standard ' + 'deviation is defined for strictly positive values only.' + ) from w + elif 'Degrees of freedom <= 0 for slice' == str(w): + raise ValueError(w) from w + else: + # Remaining warnings don't need to be exceptions. + return np.exp(np.std(log(a, where=~a_nan), axis=axis, ddof=ddof)) + except TypeError as e: + raise ValueError( + 'Invalid array input. The inputs could not be ' + 'safely coerced to any supported types') from e + + +# Private dictionary initialized only once at module level +# See https://en.wikipedia.org/wiki/Robust_measures_of_scale +_scale_conversions = {'normal': special.erfinv(0.5) * 2.0 * math.sqrt(2.0)} + + +@_axis_nan_policy_factory( + lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, + default_axis=None, override={'nan_propagation': False} +) +def iqr(x, axis=None, rng=(25, 75), scale=1.0, nan_policy='propagate', + interpolation='linear', keepdims=False): + r""" + Compute the interquartile range of the data along the specified axis. + + The interquartile range (IQR) is the difference between the 75th and + 25th percentile of the data. It is a measure of the dispersion + similar to standard deviation or variance, but is much more robust + against outliers [2]_. + + The ``rng`` parameter allows this function to compute other + percentile ranges than the actual IQR. For example, setting + ``rng=(0, 100)`` is equivalent to `numpy.ptp`. + + The IQR of an empty array is `np.nan`. + + .. versionadded:: 0.18.0 + + Parameters + ---------- + x : array_like + Input array or object that can be converted to an array. + axis : int or sequence of int, optional + Axis along which the range is computed. The default is to + compute the IQR for the entire array. + rng : Two-element sequence containing floats in range of [0,100] optional + Percentiles over which to compute the range. Each must be + between 0 and 100, inclusive. The default is the true IQR: + ``(25, 75)``. The order of the elements is not important. + scale : scalar or str or array_like of reals, optional + The numerical value of scale will be divided out of the final + result. The following string value is also recognized: + + * 'normal' : Scale by + :math:`2 \sqrt{2} erf^{-1}(\frac{1}{2}) \approx 1.349`. + + The default is 1.0. + Array-like `scale` of real dtype is also allowed, as long + as it broadcasts correctly to the output such that + ``out / scale`` is a valid operation. The output dimensions + depend on the input array, `x`, the `axis` argument, and the + `keepdims` flag. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + interpolation : str, optional + + Specifies the interpolation method to use when the percentile + boundaries lie between two data points ``i`` and ``j``. + The following options are available (default is 'linear'): + + * 'linear': ``i + (j - i)*fraction``, where ``fraction`` is the + fractional part of the index surrounded by ``i`` and ``j``. + * 'lower': ``i``. + * 'higher': ``j``. + * 'nearest': ``i`` or ``j`` whichever is nearest. + * 'midpoint': ``(i + j)/2``. + + For NumPy >= 1.22.0, the additional options provided by the ``method`` + keyword of `numpy.percentile` are also valid. + + keepdims : bool, optional + If this is set to True, the reduced axes are left in the + result as dimensions with size one. With this option, the result + will broadcast correctly against the original array `x`. + + Returns + ------- + iqr : scalar or ndarray + If ``axis=None``, a scalar is returned. If the input contains + integers or floats of smaller precision than ``np.float64``, then the + output data-type is ``np.float64``. Otherwise, the output data-type is + the same as that of the input. + + See Also + -------- + numpy.std, numpy.var + + References + ---------- + .. [1] "Interquartile range" https://en.wikipedia.org/wiki/Interquartile_range + .. [2] "Robust measures of scale" https://en.wikipedia.org/wiki/Robust_measures_of_scale + .. [3] "Quantile" https://en.wikipedia.org/wiki/Quantile + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import iqr + >>> x = np.array([[10, 7, 4], [3, 2, 1]]) + >>> x + array([[10, 7, 4], + [ 3, 2, 1]]) + >>> iqr(x) + 4.0 + >>> iqr(x, axis=0) + array([ 3.5, 2.5, 1.5]) + >>> iqr(x, axis=1) + array([ 3., 1.]) + >>> iqr(x, axis=1, keepdims=True) + array([[ 3.], + [ 1.]]) + + """ + x = asarray(x) + + # This check prevents percentile from raising an error later. Also, it is + # consistent with `np.var` and `np.std`. + if not x.size: + return _get_nan(x) + + # An error may be raised here, so fail-fast, before doing lengthy + # computations, even though `scale` is not used until later + if isinstance(scale, str): + scale_key = scale.lower() + if scale_key not in _scale_conversions: + raise ValueError(f"{scale} not a valid scale for `iqr`") + scale = _scale_conversions[scale_key] + + # Select the percentile function to use based on nans and policy + contains_nan, nan_policy = _contains_nan(x, nan_policy) + + if contains_nan and nan_policy == 'omit': + percentile_func = np.nanpercentile + else: + percentile_func = np.percentile + + if len(rng) != 2: + raise TypeError("quantile range must be two element sequence") + + if np.isnan(rng).any(): + raise ValueError("range must not contain NaNs") + + rng = sorted(rng) + pct = percentile_func(x, rng, axis=axis, method=interpolation, + keepdims=keepdims) + out = np.subtract(pct[1], pct[0]) + + if scale != 1.0: + out /= scale + + return out + + +def _mad_1d(x, center, nan_policy): + # Median absolute deviation for 1-d array x. + # This is a helper function for `median_abs_deviation`; it assumes its + # arguments have been validated already. In particular, x must be a + # 1-d numpy array, center must be callable, and if nan_policy is not + # 'propagate', it is assumed to be 'omit', because 'raise' is handled + # in `median_abs_deviation`. + # No warning is generated if x is empty or all nan. + isnan = np.isnan(x) + if isnan.any(): + if nan_policy == 'propagate': + return np.nan + x = x[~isnan] + if x.size == 0: + # MAD of an empty array is nan. + return np.nan + # Edge cases have been handled, so do the basic MAD calculation. + med = center(x) + mad = np.median(np.abs(x - med)) + return mad + + +def median_abs_deviation(x, axis=0, center=np.median, scale=1.0, + nan_policy='propagate'): + r""" + Compute the median absolute deviation of the data along the given axis. + + The median absolute deviation (MAD, [1]_) computes the median over the + absolute deviations from the median. It is a measure of dispersion + similar to the standard deviation but more robust to outliers [2]_. + + The MAD of an empty array is ``np.nan``. + + .. versionadded:: 1.5.0 + + Parameters + ---------- + x : array_like + Input array or object that can be converted to an array. + axis : int or None, optional + Axis along which the range is computed. Default is 0. If None, compute + the MAD over the entire array. + center : callable, optional + A function that will return the central value. The default is to use + np.median. Any user defined function used will need to have the + function signature ``func(arr, axis)``. + scale : scalar or str, optional + The numerical value of scale will be divided out of the final + result. The default is 1.0. The string "normal" is also accepted, + and results in `scale` being the inverse of the standard normal + quantile function at 0.75, which is approximately 0.67449. + Array-like scale is also allowed, as long as it broadcasts correctly + to the output such that ``out / scale`` is a valid operation. The + output dimensions depend on the input array, `x`, and the `axis` + argument. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + mad : scalar or ndarray + If ``axis=None``, a scalar is returned. If the input contains + integers or floats of smaller precision than ``np.float64``, then the + output data-type is ``np.float64``. Otherwise, the output data-type is + the same as that of the input. + + See Also + -------- + numpy.std, numpy.var, numpy.median, scipy.stats.iqr, scipy.stats.tmean, + scipy.stats.tstd, scipy.stats.tvar + + Notes + ----- + The `center` argument only affects the calculation of the central value + around which the MAD is calculated. That is, passing in ``center=np.mean`` + will calculate the MAD around the mean - it will not calculate the *mean* + absolute deviation. + + The input array may contain `inf`, but if `center` returns `inf`, the + corresponding MAD for that data will be `nan`. + + References + ---------- + .. [1] "Median absolute deviation", + https://en.wikipedia.org/wiki/Median_absolute_deviation + .. [2] "Robust measures of scale", + https://en.wikipedia.org/wiki/Robust_measures_of_scale + + Examples + -------- + When comparing the behavior of `median_abs_deviation` with ``np.std``, + the latter is affected when we change a single value of an array to have an + outlier value while the MAD hardly changes: + + >>> import numpy as np + >>> from scipy import stats + >>> x = stats.norm.rvs(size=100, scale=1, random_state=123456) + >>> x.std() + 0.9973906394005013 + >>> stats.median_abs_deviation(x) + 0.82832610097857 + >>> x[0] = 345.6 + >>> x.std() + 34.42304872314415 + >>> stats.median_abs_deviation(x) + 0.8323442311590675 + + Axis handling example: + + >>> x = np.array([[10, 7, 4], [3, 2, 1]]) + >>> x + array([[10, 7, 4], + [ 3, 2, 1]]) + >>> stats.median_abs_deviation(x) + array([3.5, 2.5, 1.5]) + >>> stats.median_abs_deviation(x, axis=None) + 2.0 + + Scale normal example: + + >>> x = stats.norm.rvs(size=1000000, scale=2, random_state=123456) + >>> stats.median_abs_deviation(x) + 1.3487398527041636 + >>> stats.median_abs_deviation(x, scale='normal') + 1.9996446978061115 + + """ + if not callable(center): + raise TypeError("The argument 'center' must be callable. The given " + f"value {repr(center)} is not callable.") + + # An error may be raised here, so fail-fast, before doing lengthy + # computations, even though `scale` is not used until later + if isinstance(scale, str): + if scale.lower() == 'normal': + scale = 0.6744897501960817 # special.ndtri(0.75) + else: + raise ValueError(f"{scale} is not a valid scale value.") + + x = asarray(x) + + # Consistent with `np.var` and `np.std`. + if not x.size: + if axis is None: + return np.nan + nan_shape = tuple(item for i, item in enumerate(x.shape) if i != axis) + if nan_shape == (): + # Return nan, not array(nan) + return np.nan + return np.full(nan_shape, np.nan) + + contains_nan, nan_policy = _contains_nan(x, nan_policy) + + if contains_nan: + if axis is None: + mad = _mad_1d(x.ravel(), center, nan_policy) + else: + mad = np.apply_along_axis(_mad_1d, axis, x, center, nan_policy) + else: + if axis is None: + med = center(x, axis=None) + mad = np.median(np.abs(x - med)) + else: + # Wrap the call to center() in expand_dims() so it acts like + # keepdims=True was used. + med = np.expand_dims(center(x, axis=axis), axis) + mad = np.median(np.abs(x - med), axis=axis) + + return mad / scale + + +##################################### +# TRIMMING FUNCTIONS # +##################################### + + +SigmaclipResult = namedtuple('SigmaclipResult', ('clipped', 'lower', 'upper')) + + +def sigmaclip(a, low=4., high=4.): + """Perform iterative sigma-clipping of array elements. + + Starting from the full sample, all elements outside the critical range are + removed, i.e. all elements of the input array `c` that satisfy either of + the following conditions:: + + c < mean(c) - std(c)*low + c > mean(c) + std(c)*high + + The iteration continues with the updated sample until no + elements are outside the (updated) range. + + Parameters + ---------- + a : array_like + Data array, will be raveled if not 1-D. + low : float, optional + Lower bound factor of sigma clipping. Default is 4. + high : float, optional + Upper bound factor of sigma clipping. Default is 4. + + Returns + ------- + clipped : ndarray + Input array with clipped elements removed. + lower : float + Lower threshold value use for clipping. + upper : float + Upper threshold value use for clipping. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import sigmaclip + >>> a = np.concatenate((np.linspace(9.5, 10.5, 31), + ... np.linspace(0, 20, 5))) + >>> fact = 1.5 + >>> c, low, upp = sigmaclip(a, fact, fact) + >>> c + array([ 9.96666667, 10. , 10.03333333, 10. ]) + >>> c.var(), c.std() + (0.00055555555555555165, 0.023570226039551501) + >>> low, c.mean() - fact*c.std(), c.min() + (9.9646446609406727, 9.9646446609406727, 9.9666666666666668) + >>> upp, c.mean() + fact*c.std(), c.max() + (10.035355339059327, 10.035355339059327, 10.033333333333333) + + >>> a = np.concatenate((np.linspace(9.5, 10.5, 11), + ... np.linspace(-100, -50, 3))) + >>> c, low, upp = sigmaclip(a, 1.8, 1.8) + >>> (c == np.linspace(9.5, 10.5, 11)).all() + True + + """ + c = np.asarray(a).ravel() + delta = 1 + while delta: + c_std = c.std() + c_mean = c.mean() + size = c.size + critlower = c_mean - c_std * low + critupper = c_mean + c_std * high + c = c[(c >= critlower) & (c <= critupper)] + delta = size - c.size + + return SigmaclipResult(c, critlower, critupper) + + +def trimboth(a, proportiontocut, axis=0): + """Slice off a proportion of items from both ends of an array. + + Slice off the passed proportion of items from both ends of the passed + array (i.e., with `proportiontocut` = 0.1, slices leftmost 10% **and** + rightmost 10% of scores). The trimmed values are the lowest and + highest ones. + Slice off less if proportion results in a non-integer slice index (i.e. + conservatively slices off `proportiontocut`). + + Parameters + ---------- + a : array_like + Data to trim. + proportiontocut : float + Proportion (in range 0-1) of total data set to trim of each end. + axis : int or None, optional + Axis along which to trim data. Default is 0. If None, compute over + the whole array `a`. + + Returns + ------- + out : ndarray + Trimmed version of array `a`. The order of the trimmed content + is undefined. + + See Also + -------- + trim_mean + + Examples + -------- + Create an array of 10 values and trim 10% of those values from each end: + + >>> import numpy as np + >>> from scipy import stats + >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> stats.trimboth(a, 0.1) + array([1, 3, 2, 4, 5, 6, 7, 8]) + + Note that the elements of the input array are trimmed by value, but the + output array is not necessarily sorted. + + The proportion to trim is rounded down to the nearest integer. For + instance, trimming 25% of the values from each end of an array of 10 + values will return an array of 6 values: + + >>> b = np.arange(10) + >>> stats.trimboth(b, 1/4).shape + (6,) + + Multidimensional arrays can be trimmed along any axis or across the entire + array: + + >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9] + >>> d = np.array([a, b, c]) + >>> stats.trimboth(d, 0.4, axis=0).shape + (1, 10) + >>> stats.trimboth(d, 0.4, axis=1).shape + (3, 2) + >>> stats.trimboth(d, 0.4, axis=None).shape + (6,) + + """ + a = np.asarray(a) + + if a.size == 0: + return a + + if axis is None: + a = a.ravel() + axis = 0 + + nobs = a.shape[axis] + lowercut = int(proportiontocut * nobs) + uppercut = nobs - lowercut + if (lowercut >= uppercut): + raise ValueError("Proportion too big.") + + atmp = np.partition(a, (lowercut, uppercut - 1), axis) + + sl = [slice(None)] * atmp.ndim + sl[axis] = slice(lowercut, uppercut) + return atmp[tuple(sl)] + + +def trim1(a, proportiontocut, tail='right', axis=0): + """Slice off a proportion from ONE end of the passed array distribution. + + If `proportiontocut` = 0.1, slices off 'leftmost' or 'rightmost' + 10% of scores. The lowest or highest values are trimmed (depending on + the tail). + Slice off less if proportion results in a non-integer slice index + (i.e. conservatively slices off `proportiontocut` ). + + Parameters + ---------- + a : array_like + Input array. + proportiontocut : float + Fraction to cut off of 'left' or 'right' of distribution. + tail : {'left', 'right'}, optional + Defaults to 'right'. + axis : int or None, optional + Axis along which to trim data. Default is 0. If None, compute over + the whole array `a`. + + Returns + ------- + trim1 : ndarray + Trimmed version of array `a`. The order of the trimmed content is + undefined. + + Examples + -------- + Create an array of 10 values and trim 20% of its lowest values: + + >>> import numpy as np + >>> from scipy import stats + >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> stats.trim1(a, 0.2, 'left') + array([2, 4, 3, 5, 6, 7, 8, 9]) + + Note that the elements of the input array are trimmed by value, but the + output array is not necessarily sorted. + + The proportion to trim is rounded down to the nearest integer. For + instance, trimming 25% of the values from an array of 10 values will + return an array of 8 values: + + >>> b = np.arange(10) + >>> stats.trim1(b, 1/4).shape + (8,) + + Multidimensional arrays can be trimmed along any axis or across the entire + array: + + >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9] + >>> d = np.array([a, b, c]) + >>> stats.trim1(d, 0.8, axis=0).shape + (1, 10) + >>> stats.trim1(d, 0.8, axis=1).shape + (3, 2) + >>> stats.trim1(d, 0.8, axis=None).shape + (6,) + + """ + a = np.asarray(a) + if axis is None: + a = a.ravel() + axis = 0 + + nobs = a.shape[axis] + + # avoid possible corner case + if proportiontocut >= 1: + return [] + + if tail.lower() == 'right': + lowercut = 0 + uppercut = nobs - int(proportiontocut * nobs) + + elif tail.lower() == 'left': + lowercut = int(proportiontocut * nobs) + uppercut = nobs + + atmp = np.partition(a, (lowercut, uppercut - 1), axis) + + sl = [slice(None)] * atmp.ndim + sl[axis] = slice(lowercut, uppercut) + return atmp[tuple(sl)] + + +def trim_mean(a, proportiontocut, axis=0): + """Return mean of array after trimming a specified fraction of extreme values + + Removes the specified proportion of elements from *each* end of the + sorted array, then computes the mean of the remaining elements. + + Parameters + ---------- + a : array_like + Input array. + proportiontocut : float + Fraction of the most positive and most negative elements to remove. + When the specified proportion does not result in an integer number of + elements, the number of elements to trim is rounded down. + axis : int or None, default: 0 + Axis along which the trimmed means are computed. + If None, compute over the raveled array. + + Returns + ------- + trim_mean : ndarray + Mean of trimmed array. + + See Also + -------- + trimboth : Remove a proportion of elements from each end of an array. + tmean : Compute the mean after trimming values outside specified limits. + + Notes + ----- + For 1-D array `a`, `trim_mean` is approximately equivalent to the following + calculation:: + + import numpy as np + a = np.sort(a) + m = int(proportiontocut * len(a)) + np.mean(a[m: len(a) - m]) + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = [1, 2, 3, 5] + >>> stats.trim_mean(x, 0.25) + 2.5 + + When the specified proportion does not result in an integer number of + elements, the number of elements to trim is rounded down. + + >>> stats.trim_mean(x, 0.24999) == np.mean(x) + True + + Use `axis` to specify the axis along which the calculation is performed. + + >>> x2 = [[1, 2, 3, 5], + ... [10, 20, 30, 50]] + >>> stats.trim_mean(x2, 0.25) + array([ 5.5, 11. , 16.5, 27.5]) + >>> stats.trim_mean(x2, 0.25, axis=1) + array([ 2.5, 25. ]) + + """ + a = np.asarray(a) + + if a.size == 0: + return np.nan + + if axis is None: + a = a.ravel() + axis = 0 + + nobs = a.shape[axis] + lowercut = int(proportiontocut * nobs) + uppercut = nobs - lowercut + if (lowercut > uppercut): + raise ValueError("Proportion too big.") + + atmp = np.partition(a, (lowercut, uppercut - 1), axis) + + sl = [slice(None)] * atmp.ndim + sl[axis] = slice(lowercut, uppercut) + return np.mean(atmp[tuple(sl)], axis=axis) + + +F_onewayResult = namedtuple('F_onewayResult', ('statistic', 'pvalue')) + + +def _create_f_oneway_nan_result(shape, axis, samples): + """ + This is a helper function for f_oneway for creating the return values + in certain degenerate conditions. It creates return values that are + all nan with the appropriate shape for the given `shape` and `axis`. + """ + axis = normalize_axis_index(axis, len(shape)) + shp = shape[:axis] + shape[axis+1:] + f = np.full(shp, fill_value=_get_nan(*samples)) + prob = f.copy() + return F_onewayResult(f[()], prob[()]) + + +def _first(arr, axis): + """Return arr[..., 0:1, ...] where 0:1 is in the `axis` position.""" + return np.take_along_axis(arr, np.array(0, ndmin=arr.ndim), axis) + + +def _f_oneway_is_too_small(samples, kwargs={}, axis=-1): + # Check this after forming alldata, so shape errors are detected + # and reported before checking for 0 length inputs. + if any(sample.shape[axis] == 0 for sample in samples): + msg = 'at least one input has length 0' + warnings.warn(stats.DegenerateDataWarning(msg), stacklevel=2) + return True + + # Must have at least one group with length greater than 1. + if all(sample.shape[axis] == 1 for sample in samples): + msg = ('all input arrays have length 1. f_oneway requires that at ' + 'least one input has length greater than 1.') + warnings.warn(stats.DegenerateDataWarning(msg), stacklevel=2) + return True + + return False + + +@_axis_nan_policy_factory( + F_onewayResult, n_samples=None, too_small=_f_oneway_is_too_small +) +def f_oneway(*samples, axis=0): + """Perform one-way ANOVA. + + The one-way ANOVA tests the null hypothesis that two or more groups have + the same population mean. The test is applied to samples from two or + more groups, possibly with differing sizes. + + Parameters + ---------- + sample1, sample2, ... : array_like + The sample measurements for each group. There must be at least + two arguments. If the arrays are multidimensional, then all the + dimensions of the array must be the same except for `axis`. + axis : int, optional + Axis of the input arrays along which the test is applied. + Default is 0. + + Returns + ------- + statistic : float + The computed F statistic of the test. + pvalue : float + The associated p-value from the F distribution. + + Warns + ----- + `~scipy.stats.ConstantInputWarning` + Raised if all values within each of the input arrays are identical. + In this case the F statistic is either infinite or isn't defined, + so ``np.inf`` or ``np.nan`` is returned. + + `~scipy.stats.DegenerateDataWarning` + Raised if the length of any input array is 0, or if all the input + arrays have length 1. ``np.nan`` is returned for the F statistic + and the p-value in these cases. + + Notes + ----- + The ANOVA test has important assumptions that must be satisfied in order + for the associated p-value to be valid. + + 1. The samples are independent. + 2. Each sample is from a normally distributed population. + 3. The population standard deviations of the groups are all equal. This + property is known as homoscedasticity. + + If these assumptions are not true for a given set of data, it may still + be possible to use the Kruskal-Wallis H-test (`scipy.stats.kruskal`) or + the Alexander-Govern test (`scipy.stats.alexandergovern`) although with + some loss of power. + + The length of each group must be at least one, and there must be at + least one group with length greater than one. If these conditions + are not satisfied, a warning is generated and (``np.nan``, ``np.nan``) + is returned. + + If all values in each group are identical, and there exist at least two + groups with different values, the function generates a warning and + returns (``np.inf``, 0). + + If all values in all groups are the same, function generates a warning + and returns (``np.nan``, ``np.nan``). + + The algorithm is from Heiman [2]_, pp.394-7. + + References + ---------- + .. [1] R. Lowry, "Concepts and Applications of Inferential Statistics", + Chapter 14, 2014, http://vassarstats.net/textbook/ + + .. [2] G.W. Heiman, "Understanding research methods and statistics: An + integrated introduction for psychology", Houghton, Mifflin and + Company, 2001. + + .. [3] G.H. McDonald, "Handbook of Biological Statistics", One-way ANOVA. + http://www.biostathandbook.com/onewayanova.html + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import f_oneway + + Here are some data [3]_ on a shell measurement (the length of the anterior + adductor muscle scar, standardized by dividing by length) in the mussel + Mytilus trossulus from five locations: Tillamook, Oregon; Newport, Oregon; + Petersburg, Alaska; Magadan, Russia; and Tvarminne, Finland, taken from a + much larger data set used in McDonald et al. (1991). + + >>> tillamook = [0.0571, 0.0813, 0.0831, 0.0976, 0.0817, 0.0859, 0.0735, + ... 0.0659, 0.0923, 0.0836] + >>> newport = [0.0873, 0.0662, 0.0672, 0.0819, 0.0749, 0.0649, 0.0835, + ... 0.0725] + >>> petersburg = [0.0974, 0.1352, 0.0817, 0.1016, 0.0968, 0.1064, 0.105] + >>> magadan = [0.1033, 0.0915, 0.0781, 0.0685, 0.0677, 0.0697, 0.0764, + ... 0.0689] + >>> tvarminne = [0.0703, 0.1026, 0.0956, 0.0973, 0.1039, 0.1045] + >>> f_oneway(tillamook, newport, petersburg, magadan, tvarminne) + F_onewayResult(statistic=7.121019471642447, pvalue=0.0002812242314534544) + + `f_oneway` accepts multidimensional input arrays. When the inputs + are multidimensional and `axis` is not given, the test is performed + along the first axis of the input arrays. For the following data, the + test is performed three times, once for each column. + + >>> a = np.array([[9.87, 9.03, 6.81], + ... [7.18, 8.35, 7.00], + ... [8.39, 7.58, 7.68], + ... [7.45, 6.33, 9.35], + ... [6.41, 7.10, 9.33], + ... [8.00, 8.24, 8.44]]) + >>> b = np.array([[6.35, 7.30, 7.16], + ... [6.65, 6.68, 7.63], + ... [5.72, 7.73, 6.72], + ... [7.01, 9.19, 7.41], + ... [7.75, 7.87, 8.30], + ... [6.90, 7.97, 6.97]]) + >>> c = np.array([[3.31, 8.77, 1.01], + ... [8.25, 3.24, 3.62], + ... [6.32, 8.81, 5.19], + ... [7.48, 8.83, 8.91], + ... [8.59, 6.01, 6.07], + ... [3.07, 9.72, 7.48]]) + >>> F, p = f_oneway(a, b, c) + >>> F + array([1.75676344, 0.03701228, 3.76439349]) + >>> p + array([0.20630784, 0.96375203, 0.04733157]) + + """ + if len(samples) < 2: + raise TypeError('at least two inputs are required;' + f' got {len(samples)}.') + + # ANOVA on N groups, each in its own array + num_groups = len(samples) + + # We haven't explicitly validated axis, but if it is bad, this call of + # np.concatenate will raise np.exceptions.AxisError. The call will raise + # ValueError if the dimensions of all the arrays, except the axis + # dimension, are not the same. + alldata = np.concatenate(samples, axis=axis) + bign = alldata.shape[axis] + + # Check if the inputs are too small + if _f_oneway_is_too_small(samples): + return _create_f_oneway_nan_result(alldata.shape, axis, samples) + + # Check if all values within each group are identical, and if the common + # value in at least one group is different from that in another group. + # Based on https://github.com/scipy/scipy/issues/11669 + + # If axis=0, say, and the groups have shape (n0, ...), (n1, ...), ..., + # then is_const is a boolean array with shape (num_groups, ...). + # It is True if the values within the groups along the axis slice are + # identical. In the typical case where each input array is 1-d, is_const is + # a 1-d array with length num_groups. + is_const = np.concatenate( + [(_first(sample, axis) == sample).all(axis=axis, + keepdims=True) + for sample in samples], + axis=axis + ) + + # all_const is a boolean array with shape (...) (see previous comment). + # It is True if the values within each group along the axis slice are + # the same (e.g. [[3, 3, 3], [5, 5, 5, 5], [4, 4, 4]]). + all_const = is_const.all(axis=axis) + if all_const.any(): + msg = ("Each of the input arrays is constant; " + "the F statistic is not defined or infinite") + warnings.warn(stats.ConstantInputWarning(msg), stacklevel=2) + + # all_same_const is True if all the values in the groups along the axis=0 + # slice are the same (e.g. [[3, 3, 3], [3, 3, 3, 3], [3, 3, 3]]). + all_same_const = (_first(alldata, axis) == alldata).all(axis=axis) + + # Determine the mean of the data, and subtract that from all inputs to a + # variance (via sum_of_sq / sq_of_sum) calculation. Variance is invariant + # to a shift in location, and centering all data around zero vastly + # improves numerical stability. + offset = alldata.mean(axis=axis, keepdims=True) + alldata = alldata - offset + + normalized_ss = _square_of_sums(alldata, axis=axis) / bign + + sstot = _sum_of_squares(alldata, axis=axis) - normalized_ss + + ssbn = 0 + for sample in samples: + smo_ss = _square_of_sums(sample - offset, axis=axis) + ssbn = ssbn + smo_ss / sample.shape[axis] + + # Naming: variables ending in bn/b are for "between treatments", wn/w are + # for "within treatments" + ssbn = ssbn - normalized_ss + sswn = sstot - ssbn + dfbn = num_groups - 1 + dfwn = bign - num_groups + msb = ssbn / dfbn + msw = sswn / dfwn + with np.errstate(divide='ignore', invalid='ignore'): + f = msb / msw + + prob = special.fdtrc(dfbn, dfwn, f) # equivalent to stats.f.sf + + # Fix any f values that should be inf or nan because the corresponding + # inputs were constant. + if np.isscalar(f): + if all_same_const: + f = np.nan + prob = np.nan + elif all_const: + f = np.inf + prob = 0.0 + else: + f[all_const] = np.inf + prob[all_const] = 0.0 + f[all_same_const] = np.nan + prob[all_same_const] = np.nan + + return F_onewayResult(f, prob) + + +@dataclass +class AlexanderGovernResult: + statistic: float + pvalue: float + + +@_axis_nan_policy_factory( + AlexanderGovernResult, n_samples=None, + result_to_tuple=lambda x: (x.statistic, x.pvalue), + too_small=1 +) +def alexandergovern(*samples, nan_policy='propagate'): + """Performs the Alexander Govern test. + + The Alexander-Govern approximation tests the equality of k independent + means in the face of heterogeneity of variance. The test is applied to + samples from two or more groups, possibly with differing sizes. + + Parameters + ---------- + sample1, sample2, ... : array_like + The sample measurements for each group. There must be at least + two samples. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + res : AlexanderGovernResult + An object with attributes: + + statistic : float + The computed A statistic of the test. + pvalue : float + The associated p-value from the chi-squared distribution. + + Warns + ----- + `~scipy.stats.ConstantInputWarning` + Raised if an input is a constant array. The statistic is not defined + in this case, so ``np.nan`` is returned. + + See Also + -------- + f_oneway : one-way ANOVA + + Notes + ----- + The use of this test relies on several assumptions. + + 1. The samples are independent. + 2. Each sample is from a normally distributed population. + 3. Unlike `f_oneway`, this test does not assume on homoscedasticity, + instead relaxing the assumption of equal variances. + + Input samples must be finite, one dimensional, and with size greater than + one. + + References + ---------- + .. [1] Alexander, Ralph A., and Diane M. Govern. "A New and Simpler + Approximation for ANOVA under Variance Heterogeneity." Journal + of Educational Statistics, vol. 19, no. 2, 1994, pp. 91-101. + JSTOR, www.jstor.org/stable/1165140. Accessed 12 Sept. 2020. + + Examples + -------- + >>> from scipy.stats import alexandergovern + + Here are some data on annual percentage rate of interest charged on + new car loans at nine of the largest banks in four American cities + taken from the National Institute of Standards and Technology's + ANOVA dataset. + + We use `alexandergovern` to test the null hypothesis that all cities + have the same mean APR against the alternative that the cities do not + all have the same mean APR. We decide that a significance level of 5% + is required to reject the null hypothesis in favor of the alternative. + + >>> atlanta = [13.75, 13.75, 13.5, 13.5, 13.0, 13.0, 13.0, 12.75, 12.5] + >>> chicago = [14.25, 13.0, 12.75, 12.5, 12.5, 12.4, 12.3, 11.9, 11.9] + >>> houston = [14.0, 14.0, 13.51, 13.5, 13.5, 13.25, 13.0, 12.5, 12.5] + >>> memphis = [15.0, 14.0, 13.75, 13.59, 13.25, 12.97, 12.5, 12.25, + ... 11.89] + >>> alexandergovern(atlanta, chicago, houston, memphis) + AlexanderGovernResult(statistic=4.65087071883494, + pvalue=0.19922132490385214) + + The p-value is 0.1992, indicating a nearly 20% chance of observing + such an extreme value of the test statistic under the null hypothesis. + This exceeds 5%, so we do not reject the null hypothesis in favor of + the alternative. + + """ + samples = _alexandergovern_input_validation(samples, nan_policy) + + if np.any([(sample == sample[0]).all() for sample in samples]): + msg = "An input array is constant; the statistic is not defined." + warnings.warn(stats.ConstantInputWarning(msg), stacklevel=2) + return AlexanderGovernResult(np.nan, np.nan) + + # The following formula numbers reference the equation described on + # page 92 by Alexander, Govern. Formulas 5, 6, and 7 describe other + # tests that serve as the basis for equation (8) but are not needed + # to perform the test. + + # precalculate mean and length of each sample + lengths = np.array([len(sample) for sample in samples]) + means = np.array([np.mean(sample) for sample in samples]) + + # (1) determine standard error of the mean for each sample + standard_errors = [np.std(sample, ddof=1) / np.sqrt(length) + for sample, length in zip(samples, lengths)] + + # (2) define a weight for each sample + inv_sq_se = 1 / np.square(standard_errors) + weights = inv_sq_se / np.sum(inv_sq_se) + + # (3) determine variance-weighted estimate of the common mean + var_w = np.sum(weights * means) + + # (4) determine one-sample t statistic for each group + t_stats = (means - var_w)/standard_errors + + # calculate parameters to be used in transformation + v = lengths - 1 + a = v - .5 + b = 48 * a**2 + c = (a * np.log(1 + (t_stats ** 2)/v))**.5 + + # (8) perform a normalizing transformation on t statistic + z = (c + ((c**3 + 3*c)/b) - + ((4*c**7 + 33*c**5 + 240*c**3 + 855*c) / + (b**2*10 + 8*b*c**4 + 1000*b))) + + # (9) calculate statistic + A = np.sum(np.square(z)) + + # "[the p value is determined from] central chi-square random deviates + # with k - 1 degrees of freedom". Alexander, Govern (94) + p = distributions.chi2.sf(A, len(samples) - 1) + return AlexanderGovernResult(A, p) + + +def _alexandergovern_input_validation(samples, nan_policy): + if len(samples) < 2: + raise TypeError(f"2 or more inputs required, got {len(samples)}") + + for sample in samples: + if np.size(sample) <= 1: + raise ValueError("Input sample size must be greater than one.") + if np.isinf(sample).any(): + raise ValueError("Input samples must be finite.") + + return samples + + +def _pearsonr_fisher_ci(r, n, confidence_level, alternative): + """ + Compute the confidence interval for Pearson's R. + + Fisher's transformation is used to compute the confidence interval + (https://en.wikipedia.org/wiki/Fisher_transformation). + """ + if r == 1: + zr = np.inf + elif r == -1: + zr = -np.inf + else: + zr = np.arctanh(r) + + if n > 3: + se = np.sqrt(1 / (n - 3)) + if alternative == "two-sided": + h = special.ndtri(0.5 + confidence_level/2) + zlo = zr - h*se + zhi = zr + h*se + rlo = np.tanh(zlo) + rhi = np.tanh(zhi) + elif alternative == "less": + h = special.ndtri(confidence_level) + zhi = zr + h*se + rhi = np.tanh(zhi) + rlo = -1.0 + else: + # alternative == "greater": + h = special.ndtri(confidence_level) + zlo = zr - h*se + rlo = np.tanh(zlo) + rhi = 1.0 + else: + rlo, rhi = -1.0, 1.0 + + return ConfidenceInterval(low=rlo, high=rhi) + + +def _pearsonr_bootstrap_ci(confidence_level, method, x, y, alternative): + """ + Compute the confidence interval for Pearson's R using the bootstrap. + """ + def statistic(x, y): + statistic, _ = pearsonr(x, y) + return statistic + + res = bootstrap((x, y), statistic, confidence_level=confidence_level, + paired=True, alternative=alternative, **method._asdict()) + # for one-sided confidence intervals, bootstrap gives +/- inf on one side + res.confidence_interval = np.clip(res.confidence_interval, -1, 1) + + return ConfidenceInterval(*res.confidence_interval) + + +ConfidenceInterval = namedtuple('ConfidenceInterval', ['low', 'high']) + +PearsonRResultBase = _make_tuple_bunch('PearsonRResultBase', + ['statistic', 'pvalue'], []) + + +class PearsonRResult(PearsonRResultBase): + """ + Result of `scipy.stats.pearsonr` + + Attributes + ---------- + statistic : float + Pearson product-moment correlation coefficient. + pvalue : float + The p-value associated with the chosen alternative. + + Methods + ------- + confidence_interval + Computes the confidence interval of the correlation + coefficient `statistic` for the given confidence level. + + """ + def __init__(self, statistic, pvalue, alternative, n, x, y): + super().__init__(statistic, pvalue) + self._alternative = alternative + self._n = n + self._x = x + self._y = y + + # add alias for consistency with other correlation functions + self.correlation = statistic + + def confidence_interval(self, confidence_level=0.95, method=None): + """ + The confidence interval for the correlation coefficient. + + Compute the confidence interval for the correlation coefficient + ``statistic`` with the given confidence level. + + If `method` is not provided, + The confidence interval is computed using the Fisher transformation + F(r) = arctanh(r) [1]_. When the sample pairs are drawn from a + bivariate normal distribution, F(r) approximately follows a normal + distribution with standard error ``1/sqrt(n - 3)``, where ``n`` is the + length of the original samples along the calculation axis. When + ``n <= 3``, this approximation does not yield a finite, real standard + error, so we define the confidence interval to be -1 to 1. + + If `method` is an instance of `BootstrapMethod`, the confidence + interval is computed using `scipy.stats.bootstrap` with the provided + configuration options and other appropriate settings. In some cases, + confidence limits may be NaN due to a degenerate resample, and this is + typical for very small samples (~6 observations). + + Parameters + ---------- + confidence_level : float + The confidence level for the calculation of the correlation + coefficient confidence interval. Default is 0.95. + + method : BootstrapMethod, optional + Defines the method used to compute the confidence interval. See + method description for details. + + .. versionadded:: 1.11.0 + + Returns + ------- + ci : namedtuple + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. + + References + ---------- + .. [1] "Pearson correlation coefficient", Wikipedia, + https://en.wikipedia.org/wiki/Pearson_correlation_coefficient + """ + if isinstance(method, BootstrapMethod): + ci = _pearsonr_bootstrap_ci(confidence_level, method, + self._x, self._y, self._alternative) + elif method is None: + ci = _pearsonr_fisher_ci(self.statistic, self._n, confidence_level, + self._alternative) + else: + message = ('`method` must be an instance of `BootstrapMethod` ' + 'or None.') + raise ValueError(message) + return ci + +def pearsonr(x, y, *, alternative='two-sided', method=None): + r""" + Pearson correlation coefficient and p-value for testing non-correlation. + + The Pearson correlation coefficient [1]_ measures the linear relationship + between two datasets. Like other correlation + coefficients, this one varies between -1 and +1 with 0 implying no + correlation. Correlations of -1 or +1 imply an exact linear relationship. + Positive correlations imply that as x increases, so does y. Negative + correlations imply that as x increases, y decreases. + + This function also performs a test of the null hypothesis that the + distributions underlying the samples are uncorrelated and normally + distributed. (See Kowalski [3]_ + for a discussion of the effects of non-normality of the input on the + distribution of the correlation coefficient.) + The p-value roughly indicates the probability of an uncorrelated system + producing datasets that have a Pearson correlation at least as extreme + as the one computed from these datasets. + + Parameters + ---------- + x : (N,) array_like + Input array. + y : (N,) array_like + Input array. + alternative : {'two-sided', 'greater', 'less'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the correlation is nonzero + * 'less': the correlation is negative (less than zero) + * 'greater': the correlation is positive (greater than zero) + + .. versionadded:: 1.9.0 + method : ResamplingMethod, optional + Defines the method used to compute the p-value. If `method` is an + instance of `PermutationMethod`/`MonteCarloMethod`, the p-value is + computed using + `scipy.stats.permutation_test`/`scipy.stats.monte_carlo_test` with the + provided configuration options and other appropriate settings. + Otherwise, the p-value is computed as documented in the notes. + + .. versionadded:: 1.11.0 + + Returns + ------- + result : `~scipy.stats._result_classes.PearsonRResult` + An object with the following attributes: + + statistic : float + Pearson product-moment correlation coefficient. + pvalue : float + The p-value associated with the chosen alternative. + + The object has the following method: + + confidence_interval(confidence_level, method) + This computes the confidence interval of the correlation + coefficient `statistic` for the given confidence level. + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. If `method` is not provided, the + confidence interval is computed using the Fisher transformation + [1]_. If `method` is an instance of `BootstrapMethod`, the + confidence interval is computed using `scipy.stats.bootstrap` with + the provided configuration options and other appropriate settings. + In some cases, confidence limits may be NaN due to a degenerate + resample, and this is typical for very small samples (~6 + observations). + + Warns + ----- + `~scipy.stats.ConstantInputWarning` + Raised if an input is a constant array. The correlation coefficient + is not defined in this case, so ``np.nan`` is returned. + + `~scipy.stats.NearConstantInputWarning` + Raised if an input is "nearly" constant. The array ``x`` is considered + nearly constant if ``norm(x - mean(x)) < 1e-13 * abs(mean(x))``. + Numerical errors in the calculation ``x - mean(x)`` in this case might + result in an inaccurate calculation of r. + + See Also + -------- + spearmanr : Spearman rank-order correlation coefficient. + kendalltau : Kendall's tau, a correlation measure for ordinal data. + + Notes + ----- + The correlation coefficient is calculated as follows: + + .. math:: + + r = \frac{\sum (x - m_x) (y - m_y)} + {\sqrt{\sum (x - m_x)^2 \sum (y - m_y)^2}} + + where :math:`m_x` is the mean of the vector x and :math:`m_y` is + the mean of the vector y. + + Under the assumption that x and y are drawn from + independent normal distributions (so the population correlation coefficient + is 0), the probability density function of the sample correlation + coefficient r is ([1]_, [2]_): + + .. math:: + f(r) = \frac{{(1-r^2)}^{n/2-2}}{\mathrm{B}(\frac{1}{2},\frac{n}{2}-1)} + + where n is the number of samples, and B is the beta function. This + is sometimes referred to as the exact distribution of r. This is + the distribution that is used in `pearsonr` to compute the p-value when + the `method` parameter is left at its default value (None). + The distribution is a beta distribution on the interval [-1, 1], + with equal shape parameters a = b = n/2 - 1. In terms of SciPy's + implementation of the beta distribution, the distribution of r is:: + + dist = scipy.stats.beta(n/2 - 1, n/2 - 1, loc=-1, scale=2) + + The default p-value returned by `pearsonr` is a two-sided p-value. For a + given sample with correlation coefficient r, the p-value is + the probability that abs(r') of a random sample x' and y' drawn from + the population with zero correlation would be greater than or equal + to abs(r). In terms of the object ``dist`` shown above, the p-value + for a given r and length n can be computed as:: + + p = 2*dist.cdf(-abs(r)) + + When n is 2, the above continuous distribution is not well-defined. + One can interpret the limit of the beta distribution as the shape + parameters a and b approach a = b = 0 as a discrete distribution with + equal probability masses at r = 1 and r = -1. More directly, one + can observe that, given the data x = [x1, x2] and y = [y1, y2], and + assuming x1 != x2 and y1 != y2, the only possible values for r are 1 + and -1. Because abs(r') for any sample x' and y' with length 2 will + be 1, the two-sided p-value for a sample of length 2 is always 1. + + For backwards compatibility, the object that is returned also behaves + like a tuple of length two that holds the statistic and the p-value. + + References + ---------- + .. [1] "Pearson correlation coefficient", Wikipedia, + https://en.wikipedia.org/wiki/Pearson_correlation_coefficient + .. [2] Student, "Probable error of a correlation coefficient", + Biometrika, Volume 6, Issue 2-3, 1 September 1908, pp. 302-310. + .. [3] C. J. Kowalski, "On the Effects of Non-Normality on the Distribution + of the Sample Product-Moment Correlation Coefficient" + Journal of the Royal Statistical Society. Series C (Applied + Statistics), Vol. 21, No. 1 (1972), pp. 1-12. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x, y = [1, 2, 3, 4, 5, 6, 7], [10, 9, 2.5, 6, 4, 3, 2] + >>> res = stats.pearsonr(x, y) + >>> res + PearsonRResult(statistic=-0.828503883588428, pvalue=0.021280260007523286) + + To perform an exact permutation version of the test: + + >>> rng = np.random.default_rng(7796654889291491997) + >>> method = stats.PermutationMethod(n_resamples=np.inf, random_state=rng) + >>> stats.pearsonr(x, y, method=method) + PearsonRResult(statistic=-0.828503883588428, pvalue=0.028174603174603175) + + To perform the test under the null hypothesis that the data were drawn from + *uniform* distributions: + + >>> method = stats.MonteCarloMethod(rvs=(rng.uniform, rng.uniform)) + >>> stats.pearsonr(x, y, method=method) + PearsonRResult(statistic=-0.828503883588428, pvalue=0.0188) + + To produce an asymptotic 90% confidence interval: + + >>> res.confidence_interval(confidence_level=0.9) + ConfidenceInterval(low=-0.9644331982722841, high=-0.3460237473272273) + + And for a bootstrap confidence interval: + + >>> method = stats.BootstrapMethod(method='BCa', random_state=rng) + >>> res.confidence_interval(confidence_level=0.9, method=method) + ConfidenceInterval(low=-0.9983163756488651, high=-0.22771001702132443) # may vary + + There is a linear dependence between x and y if y = a + b*x + e, where + a,b are constants and e is a random error term, assumed to be independent + of x. For simplicity, assume that x is standard normal, a=0, b=1 and let + e follow a normal distribution with mean zero and standard deviation s>0. + + >>> rng = np.random.default_rng() + >>> s = 0.5 + >>> x = stats.norm.rvs(size=500, random_state=rng) + >>> e = stats.norm.rvs(scale=s, size=500, random_state=rng) + >>> y = x + e + >>> stats.pearsonr(x, y).statistic + 0.9001942438244763 + + This should be close to the exact value given by + + >>> 1/np.sqrt(1 + s**2) + 0.8944271909999159 + + For s=0.5, we observe a high level of correlation. In general, a large + variance of the noise reduces the correlation, while the correlation + approaches one as the variance of the error goes to zero. + + It is important to keep in mind that no correlation does not imply + independence unless (x, y) is jointly normal. Correlation can even be zero + when there is a very simple dependence structure: if X follows a + standard normal distribution, let y = abs(x). Note that the correlation + between x and y is zero. Indeed, since the expectation of x is zero, + cov(x, y) = E[x*y]. By definition, this equals E[x*abs(x)] which is zero + by symmetry. The following lines of code illustrate this observation: + + >>> y = np.abs(x) + >>> stats.pearsonr(x, y) + PearsonRResult(statistic=-0.05444919272687482, pvalue=0.22422294836207743) + + A non-zero correlation coefficient can be misleading. For example, if X has + a standard normal distribution, define y = x if x < 0 and y = 0 otherwise. + A simple calculation shows that corr(x, y) = sqrt(2/Pi) = 0.797..., + implying a high level of correlation: + + >>> y = np.where(x < 0, x, 0) + >>> stats.pearsonr(x, y) + PearsonRResult(statistic=0.861985781588, pvalue=4.813432002751103e-149) + + This is unintuitive since there is no dependence of x and y if x is larger + than zero which happens in about half of the cases if we sample x and y. + + """ + n = len(x) + if n != len(y): + raise ValueError('x and y must have the same length.') + + if n < 2: + raise ValueError('x and y must have length at least 2.') + + x = np.asarray(x) + y = np.asarray(y) + + if (np.issubdtype(x.dtype, np.complexfloating) + or np.issubdtype(y.dtype, np.complexfloating)): + raise ValueError('This function does not support complex data') + + # If an input is constant, the correlation coefficient is not defined. + if (x == x[0]).all() or (y == y[0]).all(): + msg = ("An input array is constant; the correlation coefficient " + "is not defined.") + warnings.warn(stats.ConstantInputWarning(msg), stacklevel=2) + result = PearsonRResult(statistic=np.nan, pvalue=np.nan, n=n, + alternative=alternative, x=x, y=y) + return result + + if isinstance(method, PermutationMethod): + def statistic(y): + statistic, _ = pearsonr(x, y, alternative=alternative) + return statistic + + res = permutation_test((y,), statistic, permutation_type='pairings', + alternative=alternative, **method._asdict()) + + return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n, + alternative=alternative, x=x, y=y) + elif isinstance(method, MonteCarloMethod): + def statistic(x, y): + statistic, _ = pearsonr(x, y, alternative=alternative) + return statistic + + if method.rvs is None: + rng = np.random.default_rng() + method.rvs = rng.normal, rng.normal + + res = monte_carlo_test((x, y,), statistic=statistic, + alternative=alternative, **method._asdict()) + + return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n, + alternative=alternative, x=x, y=y) + elif method is not None: + message = ('`method` must be an instance of `PermutationMethod`,' + '`MonteCarloMethod`, or None.') + raise ValueError(message) + + # dtype is the data type for the calculations. This expression ensures + # that the data type is at least 64 bit floating point. It might have + # more precision if the input is, for example, np.longdouble. + dtype = type(1.0 + x[0] + y[0]) + + if n == 2: + r = dtype(np.sign(x[1] - x[0])*np.sign(y[1] - y[0])) + result = PearsonRResult(statistic=r, pvalue=1.0, n=n, + alternative=alternative, x=x, y=y) + return result + + xmean = x.mean(dtype=dtype) + ymean = y.mean(dtype=dtype) + + # By using `astype(dtype)`, we ensure that the intermediate calculations + # use at least 64 bit floating point. + xm = x.astype(dtype) - xmean + ym = y.astype(dtype) - ymean + + # Unlike np.linalg.norm or the expression sqrt((xm*xm).sum()), + # scipy.linalg.norm(xm) does not overflow if xm is, for example, + # [-5e210, 5e210, 3e200, -3e200] + normxm = linalg.norm(xm) + normym = linalg.norm(ym) + + threshold = 1e-13 + if normxm < threshold*abs(xmean) or normym < threshold*abs(ymean): + # If all the values in x (likewise y) are very close to the mean, + # the loss of precision that occurs in the subtraction xm = x - xmean + # might result in large errors in r. + msg = ("An input array is nearly constant; the computed " + "correlation coefficient may be inaccurate.") + warnings.warn(stats.NearConstantInputWarning(msg), stacklevel=2) + + r = np.dot(xm/normxm, ym/normym) + + # Presumably, if abs(r) > 1, then it is only some small artifact of + # floating point arithmetic. + r = max(min(r, 1.0), -1.0) + + # As explained in the docstring, the distribution of `r` under the null + # hypothesis is the beta distribution on (-1, 1) with a = b = n/2 - 1. + ab = n/2 - 1 + dist = stats.beta(ab, ab, loc=-1, scale=2) + pvalue = _get_pvalue(r, dist, alternative) + + return PearsonRResult(statistic=r, pvalue=pvalue, n=n, + alternative=alternative, x=x, y=y) + + +def fisher_exact(table, alternative='two-sided'): + """Perform a Fisher exact test on a 2x2 contingency table. + + The null hypothesis is that the true odds ratio of the populations + underlying the observations is one, and the observations were sampled + from these populations under a condition: the marginals of the + resulting table must equal those of the observed table. The statistic + returned is the unconditional maximum likelihood estimate of the odds + ratio, and the p-value is the probability under the null hypothesis of + obtaining a table at least as extreme as the one that was actually + observed. There are other possible choices of statistic and two-sided + p-value definition associated with Fisher's exact test; please see the + Notes for more information. + + Parameters + ---------- + table : array_like of ints + A 2x2 contingency table. Elements must be non-negative integers. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the odds ratio of the underlying population is not one + * 'less': the odds ratio of the underlying population is less than one + * 'greater': the odds ratio of the underlying population is greater + than one + + See the Notes for more details. + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float + This is the prior odds ratio, not a posterior estimate. + pvalue : float + The probability under the null hypothesis of obtaining a + table at least as extreme as the one that was actually observed. + + See Also + -------- + chi2_contingency : Chi-square test of independence of variables in a + contingency table. This can be used as an alternative to + `fisher_exact` when the numbers in the table are large. + contingency.odds_ratio : Compute the odds ratio (sample or conditional + MLE) for a 2x2 contingency table. + barnard_exact : Barnard's exact test, which is a more powerful alternative + than Fisher's exact test for 2x2 contingency tables. + boschloo_exact : Boschloo's exact test, which is a more powerful + alternative than Fisher's exact test for 2x2 contingency tables. + + Notes + ----- + *Null hypothesis and p-values* + + The null hypothesis is that the true odds ratio of the populations + underlying the observations is one, and the observations were sampled at + random from these populations under a condition: the marginals of the + resulting table must equal those of the observed table. Equivalently, + the null hypothesis is that the input table is from the hypergeometric + distribution with parameters (as used in `hypergeom`) + ``M = a + b + c + d``, ``n = a + b`` and ``N = a + c``, where the + input table is ``[[a, b], [c, d]]``. This distribution has support + ``max(0, N + n - M) <= x <= min(N, n)``, or, in terms of the values + in the input table, ``min(0, a - d) <= x <= a + min(b, c)``. ``x`` + can be interpreted as the upper-left element of a 2x2 table, so the + tables in the distribution have form:: + + [ x n - x ] + [N - x M - (n + N) + x] + + For example, if:: + + table = [6 2] + [1 4] + + then the support is ``2 <= x <= 7``, and the tables in the distribution + are:: + + [2 6] [3 5] [4 4] [5 3] [6 2] [7 1] + [5 0] [4 1] [3 2] [2 3] [1 4] [0 5] + + The probability of each table is given by the hypergeometric distribution + ``hypergeom.pmf(x, M, n, N)``. For this example, these are (rounded to + three significant digits):: + + x 2 3 4 5 6 7 + p 0.0163 0.163 0.408 0.326 0.0816 0.00466 + + These can be computed with:: + + >>> import numpy as np + >>> from scipy.stats import hypergeom + >>> table = np.array([[6, 2], [1, 4]]) + >>> M = table.sum() + >>> n = table[0].sum() + >>> N = table[:, 0].sum() + >>> start, end = hypergeom.support(M, n, N) + >>> hypergeom.pmf(np.arange(start, end+1), M, n, N) + array([0.01631702, 0.16317016, 0.40792541, 0.32634033, 0.08158508, + 0.004662 ]) + + The two-sided p-value is the probability that, under the null hypothesis, + a random table would have a probability equal to or less than the + probability of the input table. For our example, the probability of + the input table (where ``x = 6``) is 0.0816. The x values where the + probability does not exceed this are 2, 6 and 7, so the two-sided p-value + is ``0.0163 + 0.0816 + 0.00466 ~= 0.10256``:: + + >>> from scipy.stats import fisher_exact + >>> res = fisher_exact(table, alternative='two-sided') + >>> res.pvalue + 0.10256410256410257 + + The one-sided p-value for ``alternative='greater'`` is the probability + that a random table has ``x >= a``, which in our example is ``x >= 6``, + or ``0.0816 + 0.00466 ~= 0.08626``:: + + >>> res = fisher_exact(table, alternative='greater') + >>> res.pvalue + 0.08624708624708627 + + This is equivalent to computing the survival function of the + distribution at ``x = 5`` (one less than ``x`` from the input table, + because we want to include the probability of ``x = 6`` in the sum):: + + >>> hypergeom.sf(5, M, n, N) + 0.08624708624708627 + + For ``alternative='less'``, the one-sided p-value is the probability + that a random table has ``x <= a``, (i.e. ``x <= 6`` in our example), + or ``0.0163 + 0.163 + 0.408 + 0.326 + 0.0816 ~= 0.9949``:: + + >>> res = fisher_exact(table, alternative='less') + >>> res.pvalue + 0.9953379953379957 + + This is equivalent to computing the cumulative distribution function + of the distribution at ``x = 6``: + + >>> hypergeom.cdf(6, M, n, N) + 0.9953379953379957 + + *Odds ratio* + + The calculated odds ratio is different from the value computed by the + R function ``fisher.test``. This implementation returns the "sample" + or "unconditional" maximum likelihood estimate, while ``fisher.test`` + in R uses the conditional maximum likelihood estimate. To compute the + conditional maximum likelihood estimate of the odds ratio, use + `scipy.stats.contingency.odds_ratio`. + + References + ---------- + .. [1] Fisher, Sir Ronald A, "The Design of Experiments: + Mathematics of a Lady Tasting Tea." ISBN 978-0-486-41151-4, 1935. + .. [2] "Fisher's exact test", + https://en.wikipedia.org/wiki/Fisher's_exact_test + .. [3] Emma V. Low et al. "Identifying the lowest effective dose of + acetazolamide for the prophylaxis of acute mountain sickness: + systematic review and meta-analysis." + BMJ, 345, :doi:`10.1136/bmj.e6779`, 2012. + + Examples + -------- + In [3]_, the effective dose of acetazolamide for the prophylaxis of acute + mountain sickness was investigated. The study notably concluded: + + Acetazolamide 250 mg, 500 mg, and 750 mg daily were all efficacious for + preventing acute mountain sickness. Acetazolamide 250 mg was the lowest + effective dose with available evidence for this indication. + + The following table summarizes the results of the experiment in which + some participants took a daily dose of acetazolamide 250 mg while others + took a placebo. + Cases of acute mountain sickness were recorded:: + + Acetazolamide Control/Placebo + Acute mountain sickness 7 17 + No 15 5 + + + Is there evidence that the acetazolamide 250 mg reduces the risk of + acute mountain sickness? + We begin by formulating a null hypothesis :math:`H_0`: + + The odds of experiencing acute mountain sickness are the same with + the acetazolamide treatment as they are with placebo. + + Let's assess the plausibility of this hypothesis with + Fisher's test. + + >>> from scipy.stats import fisher_exact + >>> res = fisher_exact([[7, 17], [15, 5]], alternative='less') + >>> res.statistic + 0.13725490196078433 + >>> res.pvalue + 0.0028841933752349743 + + Using a significance level of 5%, we would reject the null hypothesis in + favor of the alternative hypothesis: "The odds of experiencing acute + mountain sickness with acetazolamide treatment are less than the odds of + experiencing acute mountain sickness with placebo." + + .. note:: + + Because the null distribution of Fisher's exact test is formed under + the assumption that both row and column sums are fixed, the result of + the test are conservative when applied to an experiment in which the + row sums are not fixed. + + In this case, the column sums are fixed; there are 22 subjects in each + group. But the number of cases of acute mountain sickness is not + (and cannot be) fixed before conducting the experiment. It is a + consequence. + + Boschloo's test does not depend on the assumption that the row sums + are fixed, and consequently, it provides a more powerful test in this + situation. + + >>> from scipy.stats import boschloo_exact + >>> res = boschloo_exact([[7, 17], [15, 5]], alternative='less') + >>> res.statistic + 0.0028841933752349743 + >>> res.pvalue + 0.0015141406667567101 + + We verify that the p-value is less than with `fisher_exact`. + + """ + hypergeom = distributions.hypergeom + # int32 is not enough for the algorithm + c = np.asarray(table, dtype=np.int64) + if not c.shape == (2, 2): + raise ValueError("The input `table` must be of shape (2, 2).") + + if np.any(c < 0): + raise ValueError("All values in `table` must be nonnegative.") + + if 0 in c.sum(axis=0) or 0 in c.sum(axis=1): + # If both values in a row or column are zero, the p-value is 1 and + # the odds ratio is NaN. + return SignificanceResult(np.nan, 1.0) + + if c[1, 0] > 0 and c[0, 1] > 0: + oddsratio = c[0, 0] * c[1, 1] / (c[1, 0] * c[0, 1]) + else: + oddsratio = np.inf + + n1 = c[0, 0] + c[0, 1] + n2 = c[1, 0] + c[1, 1] + n = c[0, 0] + c[1, 0] + + def pmf(x): + return hypergeom.pmf(x, n1 + n2, n1, n) + + if alternative == 'less': + pvalue = hypergeom.cdf(c[0, 0], n1 + n2, n1, n) + elif alternative == 'greater': + # Same formula as the 'less' case, but with the second column. + pvalue = hypergeom.cdf(c[0, 1], n1 + n2, n1, c[0, 1] + c[1, 1]) + elif alternative == 'two-sided': + mode = int((n + 1) * (n1 + 1) / (n1 + n2 + 2)) + pexact = hypergeom.pmf(c[0, 0], n1 + n2, n1, n) + pmode = hypergeom.pmf(mode, n1 + n2, n1, n) + + epsilon = 1e-14 + gamma = 1 + epsilon + + if np.abs(pexact - pmode) / np.maximum(pexact, pmode) <= epsilon: + return SignificanceResult(oddsratio, 1.) + + elif c[0, 0] < mode: + plower = hypergeom.cdf(c[0, 0], n1 + n2, n1, n) + if hypergeom.pmf(n, n1 + n2, n1, n) > pexact * gamma: + return SignificanceResult(oddsratio, plower) + + guess = _binary_search(lambda x: -pmf(x), -pexact * gamma, mode, n) + pvalue = plower + hypergeom.sf(guess, n1 + n2, n1, n) + else: + pupper = hypergeom.sf(c[0, 0] - 1, n1 + n2, n1, n) + if hypergeom.pmf(0, n1 + n2, n1, n) > pexact * gamma: + return SignificanceResult(oddsratio, pupper) + + guess = _binary_search(pmf, pexact * gamma, 0, mode) + pvalue = pupper + hypergeom.cdf(guess, n1 + n2, n1, n) + else: + msg = "`alternative` should be one of {'two-sided', 'less', 'greater'}" + raise ValueError(msg) + + pvalue = min(pvalue, 1.0) + + return SignificanceResult(oddsratio, pvalue) + + +def spearmanr(a, b=None, axis=0, nan_policy='propagate', + alternative='two-sided'): + r"""Calculate a Spearman correlation coefficient with associated p-value. + + The Spearman rank-order correlation coefficient is a nonparametric measure + of the monotonicity of the relationship between two datasets. + Like other correlation coefficients, + this one varies between -1 and +1 with 0 implying no correlation. + Correlations of -1 or +1 imply an exact monotonic relationship. Positive + correlations imply that as x increases, so does y. Negative correlations + imply that as x increases, y decreases. + + The p-value roughly indicates the probability of an uncorrelated system + producing datasets that have a Spearman correlation at least as extreme + as the one computed from these datasets. Although calculation of the + p-value does not make strong assumptions about the distributions underlying + the samples, it is only accurate for very large samples (>500 + observations). For smaller sample sizes, consider a permutation test (see + Examples section below). + + Parameters + ---------- + a, b : 1D or 2D array_like, b is optional + One or two 1-D or 2-D arrays containing multiple variables and + observations. When these are 1-D, each represents a vector of + observations of a single variable. For the behavior in the 2-D case, + see under ``axis``, below. + Both arrays need to have the same length in the ``axis`` dimension. + axis : int or None, optional + If axis=0 (default), then each column represents a variable, with + observations in the rows. If axis=1, the relationship is transposed: + each row represents a variable, while the columns contain observations. + If axis=None, then both arrays will be raveled. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the correlation is nonzero + * 'less': the correlation is negative (less than zero) + * 'greater': the correlation is positive (greater than zero) + + .. versionadded:: 1.7.0 + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float or ndarray (2-D square) + Spearman correlation matrix or correlation coefficient (if only 2 + variables are given as parameters). Correlation matrix is square + with length equal to total number of variables (columns or rows) in + ``a`` and ``b`` combined. + pvalue : float + The p-value for a hypothesis test whose null hypothesis + is that two samples have no ordinal correlation. See + `alternative` above for alternative hypotheses. `pvalue` has the + same shape as `statistic`. + + Warns + ----- + `~scipy.stats.ConstantInputWarning` + Raised if an input is a constant array. The correlation coefficient + is not defined in this case, so ``np.nan`` is returned. + + References + ---------- + .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard + Probability and Statistics Tables and Formulae. Chapman & Hall: New + York. 2000. + Section 14.7 + .. [2] Kendall, M. G. and Stuart, A. (1973). + The Advanced Theory of Statistics, Volume 2: Inference and Relationship. + Griffin. 1973. + Section 31.18 + .. [3] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The + relationship between the free pool of proline and collagen content in + human liver cirrhosis. The Journal of Clinical Investigation, 49(12), + 2246-2249. + .. [4] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric + statistical methods. John Wiley & Sons. + .. [5] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly Drawn." + Statistical Applications in Genetics and Molecular Biology 9.1 (2010). + .. [6] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are + superior to t and F tests in biomedical research. The American + Statistician, 52(2), 127-132. + + Examples + -------- + Consider the following data from [3]_, which studied the relationship + between free proline (an amino acid) and total collagen (a protein often + found in connective tissue) in unhealthy human livers. + + The ``x`` and ``y`` arrays below record measurements of the two compounds. + The observations are paired: each free proline measurement was taken from + the same liver as the total collagen measurement at the same index. + + >>> import numpy as np + >>> # total collagen (mg/g dry weight of liver) + >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4]) + >>> # free proline (μ mole/g dry weight of liver) + >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0]) + + These data were analyzed in [4]_ using Spearman's correlation coefficient, + a statistic sensitive to monotonic correlation between the samples. + + >>> from scipy import stats + >>> res = stats.spearmanr(x, y) + >>> res.statistic + 0.7000000000000001 + + The value of this statistic tends to be high (close to 1) for samples with + a strongly positive ordinal correlation, low (close to -1) for samples with + a strongly negative ordinal correlation, and small in magnitude (close to + zero) for samples with weak ordinal correlation. + + The test is performed by comparing the observed value of the + statistic against the null distribution: the distribution of statistic + values derived under the null hypothesis that total collagen and free + proline measurements are independent. + + For this test, the statistic can be transformed such that the null + distribution for large samples is Student's t distribution with + ``len(x) - 2`` degrees of freedom. + + >>> import matplotlib.pyplot as plt + >>> dof = len(x)-2 # len(x) == len(y) + >>> dist = stats.t(df=dof) + >>> t_vals = np.linspace(-5, 5, 100) + >>> pdf = dist.pdf(t_vals) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(t_vals, pdf) + ... ax.set_title("Spearman's Rho Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution as extreme or more extreme than the observed + value of the statistic. In a two-sided test in which the statistic is + positive, elements of the null distribution greater than the transformed + statistic and elements of the null distribution less than the negative of + the observed statistic are both considered "more extreme". + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> rs = res.statistic # original statistic + >>> transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs))) + >>> pvalue = dist.cdf(-transformed) + dist.sf(transformed) + >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (2.7, 0.025), (3, 0.03), arrowprops=props) + >>> i = t_vals >= transformed + >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') + >>> i = t_vals <= -transformed + >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') + >>> ax.set_xlim(-5, 5) + >>> ax.set_ylim(0, 0.1) + >>> plt.show() + >>> res.pvalue + 0.07991669030889909 # two-sided p-value + + If the p-value is "small" - that is, if there is a low probability of + sampling data from independent distributions that produces such an extreme + value of the statistic - this may be taken as evidence against the null + hypothesis in favor of the alternative: the distribution of total collagen + and free proline are *not* independent. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [5]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + - Small p-values are not evidence for a *large* effect; rather, they can + only provide evidence for a "significant" effect, meaning that they are + unlikely to have occurred under the null hypothesis. + + Suppose that before performing the experiment, the authors had reason + to predict a positive correlation between the total collagen and free + proline measurements, and that they had chosen to assess the plausibility + of the null hypothesis against a one-sided alternative: free proline has a + positive ordinal correlation with total collagen. In this case, only those + values in the null distribution that are as great or greater than the + observed statistic are considered to be more extreme. + + >>> res = stats.spearmanr(x, y, alternative='greater') + >>> res.statistic + 0.7000000000000001 # same statistic + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.sf(transformed) + >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (3, 0.018), (3.5, 0.03), arrowprops=props) + >>> i = t_vals >= transformed + >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') + >>> ax.set_xlim(1, 5) + >>> ax.set_ylim(0, 0.1) + >>> plt.show() + >>> res.pvalue + 0.03995834515444954 # one-sided p-value; half of the two-sided p-value + + Note that the t-distribution provides an asymptotic approximation of the + null distribution; it is only accurate for samples with many observations. + For small samples, it may be more appropriate to perform a permutation + test: Under the null hypothesis that total collagen and free proline are + independent, each of the free proline measurements were equally likely to + have been observed with any of the total collagen measurements. Therefore, + we can form an *exact* null distribution by calculating the statistic under + each possible pairing of elements between ``x`` and ``y``. + + >>> def statistic(x): # explore all possible pairings by permuting `x` + ... rs = stats.spearmanr(x, y).statistic # ignore pvalue + ... transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs))) + ... return transformed + >>> ref = stats.permutation_test((x,), statistic, alternative='greater', + ... permutation_type='pairings') + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> ax.hist(ref.null_distribution, np.linspace(-5, 5, 26), + ... density=True) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... f'exact \n({len(ref.null_distribution)} permutations)']) + >>> plt.show() + >>> ref.pvalue + 0.04563492063492063 # exact one-sided p-value + + """ + if axis is not None and axis > 1: + raise ValueError("spearmanr only handles 1-D or 2-D arrays, " + f"supplied axis argument {axis}, please use only " + "values 0, 1 or None for axis") + + a, axisout = _chk_asarray(a, axis) + if a.ndim > 2: + raise ValueError("spearmanr only handles 1-D or 2-D arrays") + + if b is None: + if a.ndim < 2: + raise ValueError("`spearmanr` needs at least 2 " + "variables to compare") + else: + # Concatenate a and b, so that we now only have to handle the case + # of a 2-D `a`. + b, _ = _chk_asarray(b, axis) + if axisout == 0: + a = np.column_stack((a, b)) + else: + a = np.vstack((a, b)) + + n_vars = a.shape[1 - axisout] + n_obs = a.shape[axisout] + if n_obs <= 1: + # Handle empty arrays or single observations. + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + warn_msg = ("An input array is constant; the correlation coefficient " + "is not defined.") + if axisout == 0: + if (a[:, 0][0] == a[:, 0]).all() or (a[:, 1][0] == a[:, 1]).all(): + # If an input is constant, the correlation coefficient + # is not defined. + warnings.warn(stats.ConstantInputWarning(warn_msg), stacklevel=2) + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + else: # case when axisout == 1 b/c a is 2 dim only + if (a[0, :][0] == a[0, :]).all() or (a[1, :][0] == a[1, :]).all(): + # If an input is constant, the correlation coefficient + # is not defined. + warnings.warn(stats.ConstantInputWarning(warn_msg), stacklevel=2) + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + a_contains_nan, nan_policy = _contains_nan(a, nan_policy) + variable_has_nan = np.zeros(n_vars, dtype=bool) + if a_contains_nan: + if nan_policy == 'omit': + return mstats_basic.spearmanr(a, axis=axis, nan_policy=nan_policy, + alternative=alternative) + elif nan_policy == 'propagate': + if a.ndim == 1 or n_vars <= 2: + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + else: + # Keep track of variables with NaNs, set the outputs to NaN + # only for those variables + variable_has_nan = np.isnan(a).any(axis=axisout) + + a_ranked = np.apply_along_axis(rankdata, axisout, a) + rs = np.corrcoef(a_ranked, rowvar=axisout) + dof = n_obs - 2 # degrees of freedom + + # rs can have elements equal to 1, so avoid zero division warnings + with np.errstate(divide='ignore'): + # clip the small negative values possibly caused by rounding + # errors before taking the square root + t = rs * np.sqrt((dof/((rs+1.0)*(1.0-rs))).clip(0)) + + prob = _get_pvalue(t, distributions.t(dof), alternative) + + # For backwards compatibility, return scalars when comparing 2 columns + if rs.shape == (2, 2): + res = SignificanceResult(rs[1, 0], prob[1, 0]) + res.correlation = rs[1, 0] + return res + else: + rs[variable_has_nan, :] = np.nan + rs[:, variable_has_nan] = np.nan + res = SignificanceResult(rs[()], prob[()]) + res.correlation = rs + return res + + +def pointbiserialr(x, y): + r"""Calculate a point biserial correlation coefficient and its p-value. + + The point biserial correlation is used to measure the relationship + between a binary variable, x, and a continuous variable, y. Like other + correlation coefficients, this one varies between -1 and +1 with 0 + implying no correlation. Correlations of -1 or +1 imply a determinative + relationship. + + This function may be computed using a shortcut formula but produces the + same result as `pearsonr`. + + Parameters + ---------- + x : array_like of bools + Input array. + y : array_like + Input array. + + Returns + ------- + res: SignificanceResult + An object containing attributes: + + statistic : float + The R value. + pvalue : float + The two-sided p-value. + + Notes + ----- + `pointbiserialr` uses a t-test with ``n-1`` degrees of freedom. + It is equivalent to `pearsonr`. + + The value of the point-biserial correlation can be calculated from: + + .. math:: + + r_{pb} = \frac{\overline{Y_1} - \overline{Y_0}} + {s_y} + \sqrt{\frac{N_0 N_1} + {N (N - 1)}} + + Where :math:`\overline{Y_{0}}` and :math:`\overline{Y_{1}}` are means + of the metric observations coded 0 and 1 respectively; :math:`N_{0}` and + :math:`N_{1}` are number of observations coded 0 and 1 respectively; + :math:`N` is the total number of observations and :math:`s_{y}` is the + standard deviation of all the metric observations. + + A value of :math:`r_{pb}` that is significantly different from zero is + completely equivalent to a significant difference in means between the two + groups. Thus, an independent groups t Test with :math:`N-2` degrees of + freedom may be used to test whether :math:`r_{pb}` is nonzero. The + relation between the t-statistic for comparing two independent groups and + :math:`r_{pb}` is given by: + + .. math:: + + t = \sqrt{N - 2}\frac{r_{pb}}{\sqrt{1 - r^{2}_{pb}}} + + References + ---------- + .. [1] J. Lev, "The Point Biserial Coefficient of Correlation", Ann. Math. + Statist., Vol. 20, no.1, pp. 125-126, 1949. + + .. [2] R.F. Tate, "Correlation Between a Discrete and a Continuous + Variable. Point-Biserial Correlation.", Ann. Math. Statist., Vol. 25, + np. 3, pp. 603-607, 1954. + + .. [3] D. Kornbrot "Point Biserial Correlation", In Wiley StatsRef: + Statistics Reference Online (eds N. Balakrishnan, et al.), 2014. + :doi:`10.1002/9781118445112.stat06227` + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> a = np.array([0, 0, 0, 1, 1, 1, 1]) + >>> b = np.arange(7) + >>> stats.pointbiserialr(a, b) + (0.8660254037844386, 0.011724811003954652) + >>> stats.pearsonr(a, b) + (0.86602540378443871, 0.011724811003954626) + >>> np.corrcoef(a, b) + array([[ 1. , 0.8660254], + [ 0.8660254, 1. ]]) + + """ + rpb, prob = pearsonr(x, y) + # create result object with alias for backward compatibility + res = SignificanceResult(rpb, prob) + res.correlation = rpb + return res + + +@_deprecate_positional_args(version="1.14") +def kendalltau(x, y, *, initial_lexsort=_NoValue, nan_policy='propagate', + method='auto', variant='b', alternative='two-sided'): + r"""Calculate Kendall's tau, a correlation measure for ordinal data. + + Kendall's tau is a measure of the correspondence between two rankings. + Values close to 1 indicate strong agreement, and values close to -1 + indicate strong disagreement. This implements two variants of Kendall's + tau: tau-b (the default) and tau-c (also known as Stuart's tau-c). These + differ only in how they are normalized to lie within the range -1 to 1; + the hypothesis tests (their p-values) are identical. Kendall's original + tau-a is not implemented separately because both tau-b and tau-c reduce + to tau-a in the absence of ties. + + Parameters + ---------- + x, y : array_like + Arrays of rankings, of the same shape. If arrays are not 1-D, they + will be flattened to 1-D. + initial_lexsort : bool, optional, deprecated + This argument is unused. + + .. deprecated:: 1.10.0 + `kendalltau` keyword argument `initial_lexsort` is deprecated as it + is unused and will be removed in SciPy 1.14.0. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + method : {'auto', 'asymptotic', 'exact'}, optional + Defines which method is used to calculate the p-value [5]_. + The following options are available (default is 'auto'): + + * 'auto': selects the appropriate method based on a trade-off + between speed and accuracy + * 'asymptotic': uses a normal approximation valid for large samples + * 'exact': computes the exact p-value, but can only be used if no ties + are present. As the sample size increases, the 'exact' computation + time may grow and the result may lose some precision. + variant : {'b', 'c'}, optional + Defines which variant of Kendall's tau is returned. Default is 'b'. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': the rank correlation is nonzero + * 'less': the rank correlation is negative (less than zero) + * 'greater': the rank correlation is positive (greater than zero) + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float + The tau statistic. + pvalue : float + The p-value for a hypothesis test whose null hypothesis is + an absence of association, tau = 0. + + See Also + -------- + spearmanr : Calculates a Spearman rank-order correlation coefficient. + theilslopes : Computes the Theil-Sen estimator for a set of points (x, y). + weightedtau : Computes a weighted version of Kendall's tau. + + Notes + ----- + The definition of Kendall's tau that is used is [2]_:: + + tau_b = (P - Q) / sqrt((P + Q + T) * (P + Q + U)) + + tau_c = 2 (P - Q) / (n**2 * (m - 1) / m) + + where P is the number of concordant pairs, Q the number of discordant + pairs, T the number of ties only in `x`, and U the number of ties only in + `y`. If a tie occurs for the same pair in both `x` and `y`, it is not + added to either T or U. n is the total number of samples, and m is the + number of unique values in either `x` or `y`, whichever is smaller. + + References + ---------- + .. [1] Maurice G. Kendall, "A New Measure of Rank Correlation", Biometrika + Vol. 30, No. 1/2, pp. 81-93, 1938. + .. [2] Maurice G. Kendall, "The treatment of ties in ranking problems", + Biometrika Vol. 33, No. 3, pp. 239-251. 1945. + .. [3] Gottfried E. Noether, "Elements of Nonparametric Statistics", John + Wiley & Sons, 1967. + .. [4] Peter M. Fenwick, "A new data structure for cumulative frequency + tables", Software: Practice and Experience, Vol. 24, No. 3, + pp. 327-336, 1994. + .. [5] Maurice G. Kendall, "Rank Correlation Methods" (4th Edition), + Charles Griffin & Co., 1970. + .. [6] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The + relationship between the free pool of proline and collagen content + in human liver cirrhosis. The Journal of Clinical Investigation, + 49(12), 2246-2249. + .. [7] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric + statistical methods. John Wiley & Sons. + .. [8] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be + Zero: Calculating Exact P-values When Permutations Are Randomly + Drawn." Statistical Applications in Genetics and Molecular Biology + 9.1 (2010). + + Examples + -------- + Consider the following data from [6]_, which studied the relationship + between free proline (an amino acid) and total collagen (a protein often + found in connective tissue) in unhealthy human livers. + + The ``x`` and ``y`` arrays below record measurements of the two compounds. + The observations are paired: each free proline measurement was taken from + the same liver as the total collagen measurement at the same index. + + >>> import numpy as np + >>> # total collagen (mg/g dry weight of liver) + >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4]) + >>> # free proline (μ mole/g dry weight of liver) + >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0]) + + These data were analyzed in [7]_ using Spearman's correlation coefficient, + a statistic similar to to Kendall's tau in that it is also sensitive to + ordinal correlation between the samples. Let's perform an analogous study + using Kendall's tau. + + >>> from scipy import stats + >>> res = stats.kendalltau(x, y) + >>> res.statistic + 0.5499999999999999 + + The value of this statistic tends to be high (close to 1) for samples with + a strongly positive ordinal correlation, low (close to -1) for samples with + a strongly negative ordinal correlation, and small in magnitude (close to + zero) for samples with weak ordinal correlation. + + The test is performed by comparing the observed value of the + statistic against the null distribution: the distribution of statistic + values derived under the null hypothesis that total collagen and free + proline measurements are independent. + + For this test, the null distribution for large samples without ties is + approximated as the normal distribution with variance + ``(2*(2*n + 5))/(9*n*(n - 1))``, where ``n = len(x)``. + + >>> import matplotlib.pyplot as plt + >>> n = len(x) # len(x) == len(y) + >>> var = (2*(2*n + 5))/(9*n*(n - 1)) + >>> dist = stats.norm(scale=np.sqrt(var)) + >>> z_vals = np.linspace(-1.25, 1.25, 100) + >>> pdf = dist.pdf(z_vals) + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> def plot(ax): # we'll reuse this + ... ax.plot(z_vals, pdf) + ... ax.set_title("Kendall Tau Test Null Distribution") + ... ax.set_xlabel("statistic") + ... ax.set_ylabel("probability density") + >>> plot(ax) + >>> plt.show() + + The comparison is quantified by the p-value: the proportion of values in + the null distribution as extreme or more extreme than the observed + value of the statistic. In a two-sided test in which the statistic is + positive, elements of the null distribution greater than the transformed + statistic and elements of the null distribution less than the negative of + the observed statistic are both considered "more extreme". + + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) + >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)') + >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) + >>> _ = ax.annotate(annotation, (0.65, 0.15), (0.8, 0.3), arrowprops=props) + >>> i = z_vals >= res.statistic + >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0') + >>> i = z_vals <= -res.statistic + >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0') + >>> ax.set_xlim(-1.25, 1.25) + >>> ax.set_ylim(0, 0.5) + >>> plt.show() + >>> res.pvalue + 0.09108705741631495 # approximate p-value + + Note that there is slight disagreement between the shaded area of the curve + and the p-value returned by `kendalltau`. This is because our data has + ties, and we have neglected a tie correction to the null distribution + variance that `kendalltau` performs. For samples without ties, the shaded + areas of our plot and p-value returned by `kendalltau` would match exactly. + + If the p-value is "small" - that is, if there is a low probability of + sampling data from independent distributions that produces such an extreme + value of the statistic - this may be taken as evidence against the null + hypothesis in favor of the alternative: the distribution of total collagen + and free proline are *not* independent. Note that: + + - The inverse is not true; that is, the test is not used to provide + evidence for the null hypothesis. + - The threshold for values that will be considered "small" is a choice that + should be made before the data is analyzed [8]_ with consideration of the + risks of both false positives (incorrectly rejecting the null hypothesis) + and false negatives (failure to reject a false null hypothesis). + - Small p-values are not evidence for a *large* effect; rather, they can + only provide evidence for a "significant" effect, meaning that they are + unlikely to have occurred under the null hypothesis. + + For samples without ties of moderate size, `kendalltau` can compute the + p-value exactly. However, in the presence of ties, `kendalltau` resorts + to an asymptotic approximation. Nonetheles, we can use a permutation test + to compute the null distribution exactly: Under the null hypothesis that + total collagen and free proline are independent, each of the free proline + measurements were equally likely to have been observed with any of the + total collagen measurements. Therefore, we can form an *exact* null + distribution by calculating the statistic under each possible pairing of + elements between ``x`` and ``y``. + + >>> def statistic(x): # explore all possible pairings by permuting `x` + ... return stats.kendalltau(x, y).statistic # ignore pvalue + >>> ref = stats.permutation_test((x,), statistic, + ... permutation_type='pairings') + >>> fig, ax = plt.subplots(figsize=(8, 5)) + >>> plot(ax) + >>> bins = np.linspace(-1.25, 1.25, 25) + >>> ax.hist(ref.null_distribution, bins=bins, density=True) + >>> ax.legend(['aymptotic approximation\n(many observations)', + ... 'exact null distribution']) + >>> plot(ax) + >>> plt.show() + >>> ref.pvalue + 0.12222222222222222 # exact p-value + + Note that there is significant disagreement between the exact p-value + calculated here and the approximation returned by `kendalltau` above. For + small samples with ties, consider performing a permutation test for more + accurate results. + + """ + if initial_lexsort is not _NoValue: + msg = ("'kendalltau' keyword argument 'initial_lexsort' is deprecated" + " as it is unused and will be removed in SciPy 1.12.0.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + x = np.asarray(x).ravel() + y = np.asarray(y).ravel() + + if x.size != y.size: + raise ValueError("All inputs to `kendalltau` must be of the same " + f"size, found x-size {x.size} and y-size {y.size}") + elif not x.size or not y.size: + # Return NaN if arrays are empty + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + # check both x and y + cnx, npx = _contains_nan(x, nan_policy) + cny, npy = _contains_nan(y, nan_policy) + contains_nan = cnx or cny + if npx == 'omit' or npy == 'omit': + nan_policy = 'omit' + + if contains_nan and nan_policy == 'propagate': + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + elif contains_nan and nan_policy == 'omit': + x = ma.masked_invalid(x) + y = ma.masked_invalid(y) + if variant == 'b': + return mstats_basic.kendalltau(x, y, method=method, use_ties=True, + alternative=alternative) + else: + message = ("nan_policy='omit' is currently compatible only with " + "variant='b'.") + raise ValueError(message) + + def count_rank_tie(ranks): + cnt = np.bincount(ranks).astype('int64', copy=False) + cnt = cnt[cnt > 1] + # Python ints to avoid overflow down the line + return (int((cnt * (cnt - 1) // 2).sum()), + int((cnt * (cnt - 1.) * (cnt - 2)).sum()), + int((cnt * (cnt - 1.) * (2*cnt + 5)).sum())) + + size = x.size + perm = np.argsort(y) # sort on y and convert y to dense ranks + x, y = x[perm], y[perm] + y = np.r_[True, y[1:] != y[:-1]].cumsum(dtype=np.intp) + + # stable sort on x and convert x to dense ranks + perm = np.argsort(x, kind='mergesort') + x, y = x[perm], y[perm] + x = np.r_[True, x[1:] != x[:-1]].cumsum(dtype=np.intp) + + dis = _kendall_dis(x, y) # discordant pairs + + obs = np.r_[True, (x[1:] != x[:-1]) | (y[1:] != y[:-1]), True] + cnt = np.diff(np.nonzero(obs)[0]).astype('int64', copy=False) + + ntie = int((cnt * (cnt - 1) // 2).sum()) # joint ties + xtie, x0, x1 = count_rank_tie(x) # ties in x, stats + ytie, y0, y1 = count_rank_tie(y) # ties in y, stats + + tot = (size * (size - 1)) // 2 + + if xtie == tot or ytie == tot: + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + # Note that tot = con + dis + (xtie - ntie) + (ytie - ntie) + ntie + # = con + dis + xtie + ytie - ntie + con_minus_dis = tot - xtie - ytie + ntie - 2 * dis + if variant == 'b': + tau = con_minus_dis / np.sqrt(tot - xtie) / np.sqrt(tot - ytie) + elif variant == 'c': + minclasses = min(len(set(x)), len(set(y))) + tau = 2*con_minus_dis / (size**2 * (minclasses-1)/minclasses) + else: + raise ValueError(f"Unknown variant of the method chosen: {variant}. " + "variant must be 'b' or 'c'.") + + # Limit range to fix computational errors + tau = np.minimum(1., max(-1., tau)) + + # The p-value calculation is the same for all variants since the p-value + # depends only on con_minus_dis. + if method == 'exact' and (xtie != 0 or ytie != 0): + raise ValueError("Ties found, exact method cannot be used.") + + if method == 'auto': + if (xtie == 0 and ytie == 0) and (size <= 33 or + min(dis, tot-dis) <= 1): + method = 'exact' + else: + method = 'asymptotic' + + if xtie == 0 and ytie == 0 and method == 'exact': + pvalue = mstats_basic._kendall_p_exact(size, tot-dis, alternative) + elif method == 'asymptotic': + # con_minus_dis is approx normally distributed with this variance [3]_ + m = size * (size - 1.) + var = ((m * (2*size + 5) - x1 - y1) / 18 + + (2 * xtie * ytie) / m + x0 * y0 / (9 * m * (size - 2))) + z = con_minus_dis / np.sqrt(var) + pvalue = _get_pvalue(z, distributions.norm, alternative) + else: + raise ValueError(f"Unknown method {method} specified. Use 'auto', " + "'exact' or 'asymptotic'.") + + # create result object with alias for backward compatibility + res = SignificanceResult(tau[()], pvalue[()]) + res.correlation = tau[()] + return res + + +def weightedtau(x, y, rank=True, weigher=None, additive=True): + r"""Compute a weighted version of Kendall's :math:`\tau`. + + The weighted :math:`\tau` is a weighted version of Kendall's + :math:`\tau` in which exchanges of high weight are more influential than + exchanges of low weight. The default parameters compute the additive + hyperbolic version of the index, :math:`\tau_\mathrm h`, which has + been shown to provide the best balance between important and + unimportant elements [1]_. + + The weighting is defined by means of a rank array, which assigns a + nonnegative rank to each element (higher importance ranks being + associated with smaller values, e.g., 0 is the highest possible rank), + and a weigher function, which assigns a weight based on the rank to + each element. The weight of an exchange is then the sum or the product + of the weights of the ranks of the exchanged elements. The default + parameters compute :math:`\tau_\mathrm h`: an exchange between + elements with rank :math:`r` and :math:`s` (starting from zero) has + weight :math:`1/(r+1) + 1/(s+1)`. + + Specifying a rank array is meaningful only if you have in mind an + external criterion of importance. If, as it usually happens, you do + not have in mind a specific rank, the weighted :math:`\tau` is + defined by averaging the values obtained using the decreasing + lexicographical rank by (`x`, `y`) and by (`y`, `x`). This is the + behavior with default parameters. Note that the convention used + here for ranking (lower values imply higher importance) is opposite + to that used by other SciPy statistical functions. + + Parameters + ---------- + x, y : array_like + Arrays of scores, of the same shape. If arrays are not 1-D, they will + be flattened to 1-D. + rank : array_like of ints or bool, optional + A nonnegative rank assigned to each element. If it is None, the + decreasing lexicographical rank by (`x`, `y`) will be used: elements of + higher rank will be those with larger `x`-values, using `y`-values to + break ties (in particular, swapping `x` and `y` will give a different + result). If it is False, the element indices will be used + directly as ranks. The default is True, in which case this + function returns the average of the values obtained using the + decreasing lexicographical rank by (`x`, `y`) and by (`y`, `x`). + weigher : callable, optional + The weigher function. Must map nonnegative integers (zero + representing the most important element) to a nonnegative weight. + The default, None, provides hyperbolic weighing, that is, + rank :math:`r` is mapped to weight :math:`1/(r+1)`. + additive : bool, optional + If True, the weight of an exchange is computed by adding the + weights of the ranks of the exchanged elements; otherwise, the weights + are multiplied. The default is True. + + Returns + ------- + res: SignificanceResult + An object containing attributes: + + statistic : float + The weighted :math:`\tau` correlation index. + pvalue : float + Presently ``np.nan``, as the null distribution of the statistic is + unknown (even in the additive hyperbolic case). + + See Also + -------- + kendalltau : Calculates Kendall's tau. + spearmanr : Calculates a Spearman rank-order correlation coefficient. + theilslopes : Computes the Theil-Sen estimator for a set of points (x, y). + + Notes + ----- + This function uses an :math:`O(n \log n)`, mergesort-based algorithm + [1]_ that is a weighted extension of Knight's algorithm for Kendall's + :math:`\tau` [2]_. It can compute Shieh's weighted :math:`\tau` [3]_ + between rankings without ties (i.e., permutations) by setting + `additive` and `rank` to False, as the definition given in [1]_ is a + generalization of Shieh's. + + NaNs are considered the smallest possible score. + + .. versionadded:: 0.19.0 + + References + ---------- + .. [1] Sebastiano Vigna, "A weighted correlation index for rankings with + ties", Proceedings of the 24th international conference on World + Wide Web, pp. 1166-1176, ACM, 2015. + .. [2] W.R. Knight, "A Computer Method for Calculating Kendall's Tau with + Ungrouped Data", Journal of the American Statistical Association, + Vol. 61, No. 314, Part 1, pp. 436-439, 1966. + .. [3] Grace S. Shieh. "A weighted Kendall's tau statistic", Statistics & + Probability Letters, Vol. 39, No. 1, pp. 17-24, 1998. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> x = [12, 2, 1, 12, 2] + >>> y = [1, 4, 7, 1, 0] + >>> res = stats.weightedtau(x, y) + >>> res.statistic + -0.56694968153682723 + >>> res.pvalue + nan + >>> res = stats.weightedtau(x, y, additive=False) + >>> res.statistic + -0.62205716951801038 + + NaNs are considered the smallest possible score: + + >>> x = [12, 2, 1, 12, 2] + >>> y = [1, 4, 7, 1, np.nan] + >>> res = stats.weightedtau(x, y) + >>> res.statistic + -0.56694968153682723 + + This is exactly Kendall's tau: + + >>> x = [12, 2, 1, 12, 2] + >>> y = [1, 4, 7, 1, 0] + >>> res = stats.weightedtau(x, y, weigher=lambda x: 1) + >>> res.statistic + -0.47140452079103173 + + >>> x = [12, 2, 1, 12, 2] + >>> y = [1, 4, 7, 1, 0] + >>> stats.weightedtau(x, y, rank=None) + SignificanceResult(statistic=-0.4157652301037516, pvalue=nan) + >>> stats.weightedtau(y, x, rank=None) + SignificanceResult(statistic=-0.7181341329699028, pvalue=nan) + + """ + x = np.asarray(x).ravel() + y = np.asarray(y).ravel() + + if x.size != y.size: + raise ValueError("All inputs to `weightedtau` must be " + "of the same size, " + f"found x-size {x.size} and y-size {y.size}") + if not x.size: + # Return NaN if arrays are empty + res = SignificanceResult(np.nan, np.nan) + res.correlation = np.nan + return res + + # If there are NaNs we apply _toint64() + if np.isnan(np.sum(x)): + x = _toint64(x) + if np.isnan(np.sum(y)): + y = _toint64(y) + + # Reduce to ranks unsupported types + if x.dtype != y.dtype: + if x.dtype != np.int64: + x = _toint64(x) + if y.dtype != np.int64: + y = _toint64(y) + else: + if x.dtype not in (np.int32, np.int64, np.float32, np.float64): + x = _toint64(x) + y = _toint64(y) + + if rank is True: + tau = ( + _weightedrankedtau(x, y, None, weigher, additive) + + _weightedrankedtau(y, x, None, weigher, additive) + ) / 2 + res = SignificanceResult(tau, np.nan) + res.correlation = tau + return res + + if rank is False: + rank = np.arange(x.size, dtype=np.intp) + elif rank is not None: + rank = np.asarray(rank).ravel() + if rank.size != x.size: + raise ValueError( + "All inputs to `weightedtau` must be of the same size, " + f"found x-size {x.size} and rank-size {rank.size}" + ) + + tau = _weightedrankedtau(x, y, rank, weigher, additive) + res = SignificanceResult(tau, np.nan) + res.correlation = tau + return res + + +# FROM MGCPY: https://github.com/neurodata/mgcpy + + +class _ParallelP: + """Helper function to calculate parallel p-value.""" + + def __init__(self, x, y, random_states): + self.x = x + self.y = y + self.random_states = random_states + + def __call__(self, index): + order = self.random_states[index].permutation(self.y.shape[0]) + permy = self.y[order][:, order] + + # calculate permuted stats, store in null distribution + perm_stat = _mgc_stat(self.x, permy)[0] + + return perm_stat + + +def _perm_test(x, y, stat, reps=1000, workers=-1, random_state=None): + r"""Helper function that calculates the p-value. See below for uses. + + Parameters + ---------- + x, y : ndarray + `x` and `y` have shapes `(n, p)` and `(n, q)`. + stat : float + The sample test statistic. + reps : int, optional + The number of replications used to estimate the null when using the + permutation test. The default is 1000 replications. + workers : int or map-like callable, optional + If `workers` is an int the population is subdivided into `workers` + sections and evaluated in parallel (uses + `multiprocessing.Pool `). Supply `-1` to use all cores + available to the Process. Alternatively supply a map-like callable, + such as `multiprocessing.Pool.map` for evaluating the population in + parallel. This evaluation is carried out as `workers(func, iterable)`. + Requires that `func` be pickleable. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + pvalue : float + The sample test p-value. + null_dist : list + The approximated null distribution. + + """ + # generate seeds for each rep (change to new parallel random number + # capabilities in numpy >= 1.17+) + random_state = check_random_state(random_state) + random_states = [np.random.RandomState(rng_integers(random_state, 1 << 32, + size=4, dtype=np.uint32)) for _ in range(reps)] + + # parallelizes with specified workers over number of reps and set seeds + parallelp = _ParallelP(x=x, y=y, random_states=random_states) + with MapWrapper(workers) as mapwrapper: + null_dist = np.array(list(mapwrapper(parallelp, range(reps)))) + + # calculate p-value and significant permutation map through list + pvalue = (1 + (null_dist >= stat).sum()) / (1 + reps) + + return pvalue, null_dist + + +def _euclidean_dist(x): + return cdist(x, x) + + +MGCResult = _make_tuple_bunch('MGCResult', + ['statistic', 'pvalue', 'mgc_dict'], []) + + +def multiscale_graphcorr(x, y, compute_distance=_euclidean_dist, reps=1000, + workers=1, is_twosamp=False, random_state=None): + r"""Computes the Multiscale Graph Correlation (MGC) test statistic. + + Specifically, for each point, MGC finds the :math:`k`-nearest neighbors for + one property (e.g. cloud density), and the :math:`l`-nearest neighbors for + the other property (e.g. grass wetness) [1]_. This pair :math:`(k, l)` is + called the "scale". A priori, however, it is not know which scales will be + most informative. So, MGC computes all distance pairs, and then efficiently + computes the distance correlations for all scales. The local correlations + illustrate which scales are relatively informative about the relationship. + The key, therefore, to successfully discover and decipher relationships + between disparate data modalities is to adaptively determine which scales + are the most informative, and the geometric implication for the most + informative scales. Doing so not only provides an estimate of whether the + modalities are related, but also provides insight into how the + determination was made. This is especially important in high-dimensional + data, where simple visualizations do not reveal relationships to the + unaided human eye. Characterizations of this implementation in particular + have been derived from and benchmarked within in [2]_. + + Parameters + ---------- + x, y : ndarray + If ``x`` and ``y`` have shapes ``(n, p)`` and ``(n, q)`` where `n` is + the number of samples and `p` and `q` are the number of dimensions, + then the MGC independence test will be run. Alternatively, ``x`` and + ``y`` can have shapes ``(n, n)`` if they are distance or similarity + matrices, and ``compute_distance`` must be sent to ``None``. If ``x`` + and ``y`` have shapes ``(n, p)`` and ``(m, p)``, an unpaired + two-sample MGC test will be run. + compute_distance : callable, optional + A function that computes the distance or similarity among the samples + within each data matrix. Set to ``None`` if ``x`` and ``y`` are + already distance matrices. The default uses the euclidean norm metric. + If you are calling a custom function, either create the distance + matrix before-hand or create a function of the form + ``compute_distance(x)`` where `x` is the data matrix for which + pairwise distances are calculated. + reps : int, optional + The number of replications used to estimate the null when using the + permutation test. The default is ``1000``. + workers : int or map-like callable, optional + If ``workers`` is an int the population is subdivided into ``workers`` + sections and evaluated in parallel (uses ``multiprocessing.Pool + ``). Supply ``-1`` to use all cores available to the + Process. Alternatively supply a map-like callable, such as + ``multiprocessing.Pool.map`` for evaluating the p-value in parallel. + This evaluation is carried out as ``workers(func, iterable)``. + Requires that `func` be pickleable. The default is ``1``. + is_twosamp : bool, optional + If `True`, a two sample test will be run. If ``x`` and ``y`` have + shapes ``(n, p)`` and ``(m, p)``, this optional will be overridden and + set to ``True``. Set to ``True`` if ``x`` and ``y`` both have shapes + ``(n, p)`` and a two sample test is desired. The default is ``False``. + Note that this will not run if inputs are distance matrices. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + res : MGCResult + An object containing attributes: + + statistic : float + The sample MGC test statistic within `[-1, 1]`. + pvalue : float + The p-value obtained via permutation. + mgc_dict : dict + Contains additional useful results: + + - mgc_map : ndarray + A 2D representation of the latent geometry of the + relationship. + - opt_scale : (int, int) + The estimated optimal scale as a `(x, y)` pair. + - null_dist : list + The null distribution derived from the permuted matrices. + + See Also + -------- + pearsonr : Pearson correlation coefficient and p-value for testing + non-correlation. + kendalltau : Calculates Kendall's tau. + spearmanr : Calculates a Spearman rank-order correlation coefficient. + + Notes + ----- + A description of the process of MGC and applications on neuroscience data + can be found in [1]_. It is performed using the following steps: + + #. Two distance matrices :math:`D^X` and :math:`D^Y` are computed and + modified to be mean zero columnwise. This results in two + :math:`n \times n` distance matrices :math:`A` and :math:`B` (the + centering and unbiased modification) [3]_. + + #. For all values :math:`k` and :math:`l` from :math:`1, ..., n`, + + * The :math:`k`-nearest neighbor and :math:`l`-nearest neighbor graphs + are calculated for each property. Here, :math:`G_k (i, j)` indicates + the :math:`k`-smallest values of the :math:`i`-th row of :math:`A` + and :math:`H_l (i, j)` indicates the :math:`l` smallested values of + the :math:`i`-th row of :math:`B` + + * Let :math:`\circ` denotes the entry-wise matrix product, then local + correlations are summed and normalized using the following statistic: + + .. math:: + + c^{kl} = \frac{\sum_{ij} A G_k B H_l} + {\sqrt{\sum_{ij} A^2 G_k \times \sum_{ij} B^2 H_l}} + + #. The MGC test statistic is the smoothed optimal local correlation of + :math:`\{ c^{kl} \}`. Denote the smoothing operation as :math:`R(\cdot)` + (which essentially set all isolated large correlations) as 0 and + connected large correlations the same as before, see [3]_.) MGC is, + + .. math:: + + MGC_n (x, y) = \max_{(k, l)} R \left(c^{kl} \left( x_n, y_n \right) + \right) + + The test statistic returns a value between :math:`(-1, 1)` since it is + normalized. + + The p-value returned is calculated using a permutation test. This process + is completed by first randomly permuting :math:`y` to estimate the null + distribution and then calculating the probability of observing a test + statistic, under the null, at least as extreme as the observed test + statistic. + + MGC requires at least 5 samples to run with reliable results. It can also + handle high-dimensional data sets. + In addition, by manipulating the input data matrices, the two-sample + testing problem can be reduced to the independence testing problem [4]_. + Given sample data :math:`U` and :math:`V` of sizes :math:`p \times n` + :math:`p \times m`, data matrix :math:`X` and :math:`Y` can be created as + follows: + + .. math:: + + X = [U | V] \in \mathcal{R}^{p \times (n + m)} + Y = [0_{1 \times n} | 1_{1 \times m}] \in \mathcal{R}^{(n + m)} + + Then, the MGC statistic can be calculated as normal. This methodology can + be extended to similar tests such as distance correlation [4]_. + + .. versionadded:: 1.4.0 + + References + ---------- + .. [1] Vogelstein, J. T., Bridgeford, E. W., Wang, Q., Priebe, C. E., + Maggioni, M., & Shen, C. (2019). Discovering and deciphering + relationships across disparate data modalities. ELife. + .. [2] Panda, S., Palaniappan, S., Xiong, J., Swaminathan, A., + Ramachandran, S., Bridgeford, E. W., ... Vogelstein, J. T. (2019). + mgcpy: A Comprehensive High Dimensional Independence Testing Python + Package. :arXiv:`1907.02088` + .. [3] Shen, C., Priebe, C.E., & Vogelstein, J. T. (2019). From distance + correlation to multiscale graph correlation. Journal of the American + Statistical Association. + .. [4] Shen, C. & Vogelstein, J. T. (2018). The Exact Equivalence of + Distance and Kernel Methods for Hypothesis Testing. + :arXiv:`1806.05514` + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import multiscale_graphcorr + >>> x = np.arange(100) + >>> y = x + >>> res = multiscale_graphcorr(x, y) + >>> res.statistic, res.pvalue + (1.0, 0.001) + + To run an unpaired two-sample test, + + >>> x = np.arange(100) + >>> y = np.arange(79) + >>> res = multiscale_graphcorr(x, y) + >>> res.statistic, res.pvalue # doctest: +SKIP + (0.033258146255703246, 0.023) + + or, if shape of the inputs are the same, + + >>> x = np.arange(100) + >>> y = x + >>> res = multiscale_graphcorr(x, y, is_twosamp=True) + >>> res.statistic, res.pvalue # doctest: +SKIP + (-0.008021809890200488, 1.0) + + """ + if not isinstance(x, np.ndarray) or not isinstance(y, np.ndarray): + raise ValueError("x and y must be ndarrays") + + # convert arrays of type (n,) to (n, 1) + if x.ndim == 1: + x = x[:, np.newaxis] + elif x.ndim != 2: + raise ValueError(f"Expected a 2-D array `x`, found shape {x.shape}") + if y.ndim == 1: + y = y[:, np.newaxis] + elif y.ndim != 2: + raise ValueError(f"Expected a 2-D array `y`, found shape {y.shape}") + + nx, px = x.shape + ny, py = y.shape + + # check for NaNs + _contains_nan(x, nan_policy='raise') + _contains_nan(y, nan_policy='raise') + + # check for positive or negative infinity and raise error + if np.sum(np.isinf(x)) > 0 or np.sum(np.isinf(y)) > 0: + raise ValueError("Inputs contain infinities") + + if nx != ny: + if px == py: + # reshape x and y for two sample testing + is_twosamp = True + else: + raise ValueError("Shape mismatch, x and y must have shape [n, p] " + "and [n, q] or have shape [n, p] and [m, p].") + + if nx < 5 or ny < 5: + raise ValueError("MGC requires at least 5 samples to give reasonable " + "results.") + + # convert x and y to float + x = x.astype(np.float64) + y = y.astype(np.float64) + + # check if compute_distance_matrix if a callable() + if not callable(compute_distance) and compute_distance is not None: + raise ValueError("Compute_distance must be a function.") + + # check if number of reps exists, integer, or > 0 (if under 1000 raises + # warning) + if not isinstance(reps, int) or reps < 0: + raise ValueError("Number of reps must be an integer greater than 0.") + elif reps < 1000: + msg = ("The number of replications is low (under 1000), and p-value " + "calculations may be unreliable. Use the p-value result, with " + "caution!") + warnings.warn(msg, RuntimeWarning, stacklevel=2) + + if is_twosamp: + if compute_distance is None: + raise ValueError("Cannot run if inputs are distance matrices") + x, y = _two_sample_transform(x, y) + + if compute_distance is not None: + # compute distance matrices for x and y + x = compute_distance(x) + y = compute_distance(y) + + # calculate MGC stat + stat, stat_dict = _mgc_stat(x, y) + stat_mgc_map = stat_dict["stat_mgc_map"] + opt_scale = stat_dict["opt_scale"] + + # calculate permutation MGC p-value + pvalue, null_dist = _perm_test(x, y, stat, reps=reps, workers=workers, + random_state=random_state) + + # save all stats (other than stat/p-value) in dictionary + mgc_dict = {"mgc_map": stat_mgc_map, + "opt_scale": opt_scale, + "null_dist": null_dist} + + # create result object with alias for backward compatibility + res = MGCResult(stat, pvalue, mgc_dict) + res.stat = stat + return res + + +def _mgc_stat(distx, disty): + r"""Helper function that calculates the MGC stat. See above for use. + + Parameters + ---------- + distx, disty : ndarray + `distx` and `disty` have shapes `(n, p)` and `(n, q)` or + `(n, n)` and `(n, n)` + if distance matrices. + + Returns + ------- + stat : float + The sample MGC test statistic within `[-1, 1]`. + stat_dict : dict + Contains additional useful additional returns containing the following + keys: + + - stat_mgc_map : ndarray + MGC-map of the statistics. + - opt_scale : (float, float) + The estimated optimal scale as a `(x, y)` pair. + + """ + # calculate MGC map and optimal scale + stat_mgc_map = _local_correlations(distx, disty, global_corr='mgc') + + n, m = stat_mgc_map.shape + if m == 1 or n == 1: + # the global scale at is the statistic calculated at maximial nearest + # neighbors. There is not enough local scale to search over, so + # default to global scale + stat = stat_mgc_map[m - 1][n - 1] + opt_scale = m * n + else: + samp_size = len(distx) - 1 + + # threshold to find connected region of significant local correlations + sig_connect = _threshold_mgc_map(stat_mgc_map, samp_size) + + # maximum within the significant region + stat, opt_scale = _smooth_mgc_map(sig_connect, stat_mgc_map) + + stat_dict = {"stat_mgc_map": stat_mgc_map, + "opt_scale": opt_scale} + + return stat, stat_dict + + +def _threshold_mgc_map(stat_mgc_map, samp_size): + r""" + Finds a connected region of significance in the MGC-map by thresholding. + + Parameters + ---------- + stat_mgc_map : ndarray + All local correlations within `[-1,1]`. + samp_size : int + The sample size of original data. + + Returns + ------- + sig_connect : ndarray + A binary matrix with 1's indicating the significant region. + + """ + m, n = stat_mgc_map.shape + + # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05 + # with varying levels of performance. Threshold is based on a beta + # approximation. + per_sig = 1 - (0.02 / samp_size) # Percentile to consider as significant + threshold = samp_size * (samp_size - 3)/4 - 1/2 # Beta approximation + threshold = distributions.beta.ppf(per_sig, threshold, threshold) * 2 - 1 + + # the global scale at is the statistic calculated at maximial nearest + # neighbors. Threshold is the maximum on the global and local scales + threshold = max(threshold, stat_mgc_map[m - 1][n - 1]) + + # find the largest connected component of significant correlations + sig_connect = stat_mgc_map > threshold + if np.sum(sig_connect) > 0: + sig_connect, _ = _measurements.label(sig_connect) + _, label_counts = np.unique(sig_connect, return_counts=True) + + # skip the first element in label_counts, as it is count(zeros) + max_label = np.argmax(label_counts[1:]) + 1 + sig_connect = sig_connect == max_label + else: + sig_connect = np.array([[False]]) + + return sig_connect + + +def _smooth_mgc_map(sig_connect, stat_mgc_map): + """Finds the smoothed maximal within the significant region R. + + If area of R is too small it returns the last local correlation. Otherwise, + returns the maximum within significant_connected_region. + + Parameters + ---------- + sig_connect : ndarray + A binary matrix with 1's indicating the significant region. + stat_mgc_map : ndarray + All local correlations within `[-1, 1]`. + + Returns + ------- + stat : float + The sample MGC statistic within `[-1, 1]`. + opt_scale: (float, float) + The estimated optimal scale as an `(x, y)` pair. + + """ + m, n = stat_mgc_map.shape + + # the global scale at is the statistic calculated at maximial nearest + # neighbors. By default, statistic and optimal scale are global. + stat = stat_mgc_map[m - 1][n - 1] + opt_scale = [m, n] + + if np.linalg.norm(sig_connect) != 0: + # proceed only when the connected region's area is sufficiently large + # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05 + # with varying levels of performance + if np.sum(sig_connect) >= np.ceil(0.02 * max(m, n)) * min(m, n): + max_corr = max(stat_mgc_map[sig_connect]) + + # find all scales within significant_connected_region that maximize + # the local correlation + max_corr_index = np.where((stat_mgc_map >= max_corr) & sig_connect) + + if max_corr >= stat: + stat = max_corr + + k, l = max_corr_index + one_d_indices = k * n + l # 2D to 1D indexing + k = np.max(one_d_indices) // n + l = np.max(one_d_indices) % n + opt_scale = [k+1, l+1] # adding 1s to match R indexing + + return stat, opt_scale + + +def _two_sample_transform(u, v): + """Helper function that concatenates x and y for two sample MGC stat. + + See above for use. + + Parameters + ---------- + u, v : ndarray + `u` and `v` have shapes `(n, p)` and `(m, p)`. + + Returns + ------- + x : ndarray + Concatenate `u` and `v` along the `axis = 0`. `x` thus has shape + `(2n, p)`. + y : ndarray + Label matrix for `x` where 0 refers to samples that comes from `u` and + 1 refers to samples that come from `v`. `y` thus has shape `(2n, 1)`. + + """ + nx = u.shape[0] + ny = v.shape[0] + x = np.concatenate([u, v], axis=0) + y = np.concatenate([np.zeros(nx), np.ones(ny)], axis=0).reshape(-1, 1) + return x, y + + +##################################### +# INFERENTIAL STATISTICS # +##################################### + +TtestResultBase = _make_tuple_bunch('TtestResultBase', + ['statistic', 'pvalue'], ['df']) + + +class TtestResult(TtestResultBase): + """ + Result of a t-test. + + See the documentation of the particular t-test function for more + information about the definition of the statistic and meaning of + the confidence interval. + + Attributes + ---------- + statistic : float or array + The t-statistic of the sample. + pvalue : float or array + The p-value associated with the given alternative. + df : float or array + The number of degrees of freedom used in calculation of the + t-statistic; this is one less than the size of the sample + (``a.shape[axis]-1`` if there are no masked elements or omitted NaNs). + + Methods + ------- + confidence_interval + Computes a confidence interval around the population statistic + for the given confidence level. + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. + + """ + + def __init__(self, statistic, pvalue, df, # public + alternative, standard_error, estimate): # private + super().__init__(statistic, pvalue, df=df) + self._alternative = alternative + self._standard_error = standard_error # denominator of t-statistic + self._estimate = estimate # point estimate of sample mean + + def confidence_interval(self, confidence_level=0.95): + """ + Parameters + ---------- + confidence_level : float + The confidence level for the calculation of the population mean + confidence interval. Default is 0.95. + + Returns + ------- + ci : namedtuple + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. + + """ + low, high = _t_confidence_interval(self.df, self.statistic, + confidence_level, self._alternative) + low = low * self._standard_error + self._estimate + high = high * self._standard_error + self._estimate + return ConfidenceInterval(low=low, high=high) + + +def pack_TtestResult(statistic, pvalue, df, alternative, standard_error, + estimate): + # this could be any number of dimensions (including 0d), but there is + # at most one unique non-NaN value + alternative = np.atleast_1d(alternative) # can't index 0D object + alternative = alternative[np.isfinite(alternative)] + alternative = alternative[0] if alternative.size else np.nan + return TtestResult(statistic, pvalue, df=df, alternative=alternative, + standard_error=standard_error, estimate=estimate) + + +def unpack_TtestResult(res): + return (res.statistic, res.pvalue, res.df, res._alternative, + res._standard_error, res._estimate) + + +@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, + result_to_tuple=unpack_TtestResult, n_outputs=6) +def ttest_1samp(a, popmean, axis=0, nan_policy='propagate', + alternative="two-sided"): + """Calculate the T-test for the mean of ONE group of scores. + + This is a test for the null hypothesis that the expected value + (mean) of a sample of independent observations `a` is equal to the given + population mean, `popmean`. + + Parameters + ---------- + a : array_like + Sample observations. + popmean : float or array_like + Expected value in null hypothesis. If array_like, then its length along + `axis` must equal 1, and it must otherwise be broadcastable with `a`. + axis : int or None, optional + Axis along which to compute test; default is 0. If None, compute over + the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the mean of the underlying distribution of the sample + is different than the given population mean (`popmean`) + * 'less': the mean of the underlying distribution of the sample is + less than the given population mean (`popmean`) + * 'greater': the mean of the underlying distribution of the sample is + greater than the given population mean (`popmean`) + + Returns + ------- + result : `~scipy.stats._result_classes.TtestResult` + An object with the following attributes: + + statistic : float or array + The t-statistic. + pvalue : float or array + The p-value associated with the given alternative. + df : float or array + The number of degrees of freedom used in calculation of the + t-statistic; this is one less than the size of the sample + (``a.shape[axis]``). + + .. versionadded:: 1.10.0 + + The object also has the following method: + + confidence_interval(confidence_level=0.95) + Computes a confidence interval around the population + mean for the given confidence level. + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. + + .. versionadded:: 1.10.0 + + Notes + ----- + The statistic is calculated as ``(np.mean(a) - popmean)/se``, where + ``se`` is the standard error. Therefore, the statistic will be positive + when the sample mean is greater than the population mean and negative when + the sample mean is less than the population mean. + + Examples + -------- + Suppose we wish to test the null hypothesis that the mean of a population + is equal to 0.5. We choose a confidence level of 99%; that is, we will + reject the null hypothesis in favor of the alternative if the p-value is + less than 0.01. + + When testing random variates from the standard uniform distribution, which + has a mean of 0.5, we expect the data to be consistent with the null + hypothesis most of the time. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> rvs = stats.uniform.rvs(size=50, random_state=rng) + >>> stats.ttest_1samp(rvs, popmean=0.5) + TtestResult(statistic=2.456308468440, pvalue=0.017628209047638, df=49) + + As expected, the p-value of 0.017 is not below our threshold of 0.01, so + we cannot reject the null hypothesis. + + When testing data from the standard *normal* distribution, which has a mean + of 0, we would expect the null hypothesis to be rejected. + + >>> rvs = stats.norm.rvs(size=50, random_state=rng) + >>> stats.ttest_1samp(rvs, popmean=0.5) + TtestResult(statistic=-7.433605518875, pvalue=1.416760157221e-09, df=49) + + Indeed, the p-value is lower than our threshold of 0.01, so we reject the + null hypothesis in favor of the default "two-sided" alternative: the mean + of the population is *not* equal to 0.5. + + However, suppose we were to test the null hypothesis against the + one-sided alternative that the mean of the population is *greater* than + 0.5. Since the mean of the standard normal is less than 0.5, we would not + expect the null hypothesis to be rejected. + + >>> stats.ttest_1samp(rvs, popmean=0.5, alternative='greater') + TtestResult(statistic=-7.433605518875, pvalue=0.99999999929, df=49) + + Unsurprisingly, with a p-value greater than our threshold, we would not + reject the null hypothesis. + + Note that when working with a confidence level of 99%, a true null + hypothesis will be rejected approximately 1% of the time. + + >>> rvs = stats.uniform.rvs(size=(100, 50), random_state=rng) + >>> res = stats.ttest_1samp(rvs, popmean=0.5, axis=1) + >>> np.sum(res.pvalue < 0.01) + 1 + + Indeed, even though all 100 samples above were drawn from the standard + uniform distribution, which *does* have a population mean of 0.5, we would + mistakenly reject the null hypothesis for one of them. + + `ttest_1samp` can also compute a confidence interval around the population + mean. + + >>> rvs = stats.norm.rvs(size=50, random_state=rng) + >>> res = stats.ttest_1samp(rvs, popmean=0) + >>> ci = res.confidence_interval(confidence_level=0.95) + >>> ci + ConfidenceInterval(low=-0.3193887540880017, high=0.2898583388980972) + + The bounds of the 95% confidence interval are the + minimum and maximum values of the parameter `popmean` for which the + p-value of the test would be 0.05. + + >>> res = stats.ttest_1samp(rvs, popmean=ci.low) + >>> np.testing.assert_allclose(res.pvalue, 0.05) + >>> res = stats.ttest_1samp(rvs, popmean=ci.high) + >>> np.testing.assert_allclose(res.pvalue, 0.05) + + Under certain assumptions about the population from which a sample + is drawn, the confidence interval with confidence level 95% is expected + to contain the true population mean in 95% of sample replications. + + >>> rvs = stats.norm.rvs(size=(50, 1000), loc=1, random_state=rng) + >>> res = stats.ttest_1samp(rvs, popmean=0) + >>> ci = res.confidence_interval() + >>> contains_pop_mean = (ci.low < 1) & (ci.high > 1) + >>> contains_pop_mean.sum() + 953 + + """ + a, axis = _chk_asarray(a, axis) + + n = a.shape[axis] + df = n - 1 + + mean = np.mean(a, axis) + try: + popmean = np.squeeze(popmean, axis=axis) + except ValueError as e: + raise ValueError("`popmean.shape[axis]` must equal 1.") from e + d = mean - popmean + v = _var(a, axis, ddof=1) + denom = np.sqrt(v / n) + + with np.errstate(divide='ignore', invalid='ignore'): + t = np.divide(d, denom)[()] + prob = _get_pvalue(t, distributions.t(df), alternative) + + # when nan_policy='omit', `df` can be different for different axis-slices + df = np.broadcast_to(df, t.shape)[()] + # _axis_nan_policy decorator doesn't play well with strings + alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] + return TtestResult(t, prob, df=df, alternative=alternative_num, + standard_error=denom, estimate=mean) + + +def _t_confidence_interval(df, t, confidence_level, alternative): + # Input validation on `alternative` is already done + # We just need IV on confidence_level + if confidence_level < 0 or confidence_level > 1: + message = "`confidence_level` must be a number between 0 and 1." + raise ValueError(message) + + if alternative < 0: # 'less' + p = confidence_level + low, high = np.broadcast_arrays(-np.inf, special.stdtrit(df, p)) + elif alternative > 0: # 'greater' + p = 1 - confidence_level + low, high = np.broadcast_arrays(special.stdtrit(df, p), np.inf) + elif alternative == 0: # 'two-sided' + tail_probability = (1 - confidence_level)/2 + p = tail_probability, 1-tail_probability + # axis of p must be the zeroth and orthogonal to all the rest + p = np.reshape(p, [2] + [1]*np.asarray(df).ndim) + low, high = special.stdtrit(df, p) + else: # alternative is NaN when input is empty (see _axis_nan_policy) + p, nans = np.broadcast_arrays(t, np.nan) + low, high = nans, nans + + return low[()], high[()] + +def _ttest_ind_from_stats(mean1, mean2, denom, df, alternative): + + d = mean1 - mean2 + with np.errstate(divide='ignore', invalid='ignore'): + t = np.divide(d, denom)[()] + prob = _get_pvalue(t, distributions.t(df), alternative) + + return (t, prob) + + +def _unequal_var_ttest_denom(v1, n1, v2, n2): + vn1 = v1 / n1 + vn2 = v2 / n2 + with np.errstate(divide='ignore', invalid='ignore'): + df = (vn1 + vn2)**2 / (vn1**2 / (n1 - 1) + vn2**2 / (n2 - 1)) + + # If df is undefined, variances are zero (assumes n1 > 0 & n2 > 0). + # Hence it doesn't matter what df is as long as it's not NaN. + df = np.where(np.isnan(df), 1, df) + denom = np.sqrt(vn1 + vn2) + return df, denom + + +def _equal_var_ttest_denom(v1, n1, v2, n2): + # If there is a single observation in one sample, this formula for pooled + # variance breaks down because the variance of that sample is undefined. + # The pooled variance is still defined, though, because the (n-1) in the + # numerator should cancel with the (n-1) in the denominator, leaving only + # the sum of squared differences from the mean: zero. + v1 = np.where(n1 == 1, 0, v1)[()] + v2 = np.where(n2 == 1, 0, v2)[()] + + df = n1 + n2 - 2.0 + svar = ((n1 - 1) * v1 + (n2 - 1) * v2) / df + denom = np.sqrt(svar * (1.0 / n1 + 1.0 / n2)) + return df, denom + + +Ttest_indResult = namedtuple('Ttest_indResult', ('statistic', 'pvalue')) + + +def ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2, + equal_var=True, alternative="two-sided"): + r""" + T-test for means of two independent samples from descriptive statistics. + + This is a test for the null hypothesis that two independent + samples have identical average (expected) values. + + Parameters + ---------- + mean1 : array_like + The mean(s) of sample 1. + std1 : array_like + The corrected sample standard deviation of sample 1 (i.e. ``ddof=1``). + nobs1 : array_like + The number(s) of observations of sample 1. + mean2 : array_like + The mean(s) of sample 2. + std2 : array_like + The corrected sample standard deviation of sample 2 (i.e. ``ddof=1``). + nobs2 : array_like + The number(s) of observations of sample 2. + equal_var : bool, optional + If True (default), perform a standard independent 2 sample test + that assumes equal population variances [1]_. + If False, perform Welch's t-test, which does not assume equal + population variance [2]_. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions are unequal. + * 'less': the mean of the first distribution is less than the + mean of the second distribution. + * 'greater': the mean of the first distribution is greater than the + mean of the second distribution. + + .. versionadded:: 1.6.0 + + Returns + ------- + statistic : float or array + The calculated t-statistics. + pvalue : float or array + The two-tailed p-value. + + See Also + -------- + scipy.stats.ttest_ind + + Notes + ----- + The statistic is calculated as ``(mean1 - mean2)/se``, where ``se`` is the + standard error. Therefore, the statistic will be positive when `mean1` is + greater than `mean2` and negative when `mean1` is less than `mean2`. + + This method does not check whether any of the elements of `std1` or `std2` + are negative. If any elements of the `std1` or `std2` parameters are + negative in a call to this method, this method will return the same result + as if it were passed ``numpy.abs(std1)`` and ``numpy.abs(std2)``, + respectively, instead; no exceptions or warnings will be emitted. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test + + .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test + + Examples + -------- + Suppose we have the summary data for two samples, as follows (with the + Sample Variance being the corrected sample variance):: + + Sample Sample + Size Mean Variance + Sample 1 13 15.0 87.5 + Sample 2 11 12.0 39.0 + + Apply the t-test to this data (with the assumption that the population + variances are equal): + + >>> import numpy as np + >>> from scipy.stats import ttest_ind_from_stats + >>> ttest_ind_from_stats(mean1=15.0, std1=np.sqrt(87.5), nobs1=13, + ... mean2=12.0, std2=np.sqrt(39.0), nobs2=11) + Ttest_indResult(statistic=0.9051358093310269, pvalue=0.3751996797581487) + + For comparison, here is the data from which those summary statistics + were taken. With this data, we can compute the same result using + `scipy.stats.ttest_ind`: + + >>> a = np.array([1, 3, 4, 6, 11, 13, 15, 19, 22, 24, 25, 26, 26]) + >>> b = np.array([2, 4, 6, 9, 11, 13, 14, 15, 18, 19, 21]) + >>> from scipy.stats import ttest_ind + >>> ttest_ind(a, b) + Ttest_indResult(statistic=0.905135809331027, pvalue=0.3751996797581486) + + Suppose we instead have binary data and would like to apply a t-test to + compare the proportion of 1s in two independent groups:: + + Number of Sample Sample + Size ones Mean Variance + Sample 1 150 30 0.2 0.161073 + Sample 2 200 45 0.225 0.175251 + + The sample mean :math:`\hat{p}` is the proportion of ones in the sample + and the variance for a binary observation is estimated by + :math:`\hat{p}(1-\hat{p})`. + + >>> ttest_ind_from_stats(mean1=0.2, std1=np.sqrt(0.161073), nobs1=150, + ... mean2=0.225, std2=np.sqrt(0.175251), nobs2=200) + Ttest_indResult(statistic=-0.5627187905196761, pvalue=0.5739887114209541) + + For comparison, we could compute the t statistic and p-value using + arrays of 0s and 1s and `scipy.stat.ttest_ind`, as above. + + >>> group1 = np.array([1]*30 + [0]*(150-30)) + >>> group2 = np.array([1]*45 + [0]*(200-45)) + >>> ttest_ind(group1, group2) + Ttest_indResult(statistic=-0.5627179589855622, pvalue=0.573989277115258) + + """ + mean1 = np.asarray(mean1) + std1 = np.asarray(std1) + mean2 = np.asarray(mean2) + std2 = np.asarray(std2) + if equal_var: + df, denom = _equal_var_ttest_denom(std1**2, nobs1, std2**2, nobs2) + else: + df, denom = _unequal_var_ttest_denom(std1**2, nobs1, + std2**2, nobs2) + + res = _ttest_ind_from_stats(mean1, mean2, denom, df, alternative) + return Ttest_indResult(*res) + + +@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, + result_to_tuple=unpack_TtestResult, n_outputs=6) +def ttest_ind(a, b, axis=0, equal_var=True, nan_policy='propagate', + permutations=None, random_state=None, alternative="two-sided", + trim=0): + """ + Calculate the T-test for the means of *two independent* samples of scores. + + This is a test for the null hypothesis that 2 independent samples + have identical average (expected) values. This test assumes that the + populations have identical variances by default. + + Parameters + ---------- + a, b : array_like + The arrays must have the same shape, except in the dimension + corresponding to `axis` (the first, by default). + axis : int or None, optional + Axis along which to compute test. If None, compute over the whole + arrays, `a`, and `b`. + equal_var : bool, optional + If True (default), perform a standard independent 2 sample test + that assumes equal population variances [1]_. + If False, perform Welch's t-test, which does not assume equal + population variance [2]_. + + .. versionadded:: 0.11.0 + + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + The 'omit' option is not currently available for permutation tests or + one-sided asympyotic tests. + + permutations : non-negative int, np.inf, or None (default), optional + If 0 or None (default), use the t-distribution to calculate p-values. + Otherwise, `permutations` is the number of random permutations that + will be used to estimate p-values using a permutation test. If + `permutations` equals or exceeds the number of distinct partitions of + the pooled data, an exact test is performed instead (i.e. each + distinct partition is used exactly once). See Notes for details. + + .. versionadded:: 1.7.0 + + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Pseudorandom number generator state used to generate permutations + (used only when `permutations` is not None). + + .. versionadded:: 1.7.0 + + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions underlying the samples + are unequal. + * 'less': the mean of the distribution underlying the first sample + is less than the mean of the distribution underlying the second + sample. + * 'greater': the mean of the distribution underlying the first + sample is greater than the mean of the distribution underlying + the second sample. + + .. versionadded:: 1.6.0 + + trim : float, optional + If nonzero, performs a trimmed (Yuen's) t-test. + Defines the fraction of elements to be trimmed from each end of the + input samples. If 0 (default), no elements will be trimmed from either + side. The number of trimmed elements from each tail is the floor of the + trim times the number of elements. Valid range is [0, .5). + + .. versionadded:: 1.7 + + Returns + ------- + result : `~scipy.stats._result_classes.TtestResult` + An object with the following attributes: + + statistic : float or ndarray + The t-statistic. + pvalue : float or ndarray + The p-value associated with the given alternative. + df : float or ndarray + The number of degrees of freedom used in calculation of the + t-statistic. This is always NaN for a permutation t-test. + + .. versionadded:: 1.11.0 + + The object also has the following method: + + confidence_interval(confidence_level=0.95) + Computes a confidence interval around the difference in + population means for the given confidence level. + The confidence interval is returned in a ``namedtuple`` with + fields ``low`` and ``high``. + When a permutation t-test is performed, the confidence interval + is not computed, and fields ``low`` and ``high`` contain NaN. + + .. versionadded:: 1.11.0 + + Notes + ----- + Suppose we observe two independent samples, e.g. flower petal lengths, and + we are considering whether the two samples were drawn from the same + population (e.g. the same species of flower or two species with similar + petal characteristics) or two different populations. + + The t-test quantifies the difference between the arithmetic means + of the two samples. The p-value quantifies the probability of observing + as or more extreme values assuming the null hypothesis, that the + samples are drawn from populations with the same population means, is true. + A p-value larger than a chosen threshold (e.g. 5% or 1%) indicates that + our observation is not so unlikely to have occurred by chance. Therefore, + we do not reject the null hypothesis of equal population means. + If the p-value is smaller than our threshold, then we have evidence + against the null hypothesis of equal population means. + + By default, the p-value is determined by comparing the t-statistic of the + observed data against a theoretical t-distribution. + When ``1 < permutations < binom(n, k)``, where + + * ``k`` is the number of observations in `a`, + * ``n`` is the total number of observations in `a` and `b`, and + * ``binom(n, k)`` is the binomial coefficient (``n`` choose ``k``), + + the data are pooled (concatenated), randomly assigned to either group `a` + or `b`, and the t-statistic is calculated. This process is performed + repeatedly (`permutation` times), generating a distribution of the + t-statistic under the null hypothesis, and the t-statistic of the observed + data is compared to this distribution to determine the p-value. + Specifically, the p-value reported is the "achieved significance level" + (ASL) as defined in 4.4 of [3]_. Note that there are other ways of + estimating p-values using randomized permutation tests; for other + options, see the more general `permutation_test`. + + When ``permutations >= binom(n, k)``, an exact test is performed: the data + are partitioned between the groups in each distinct way exactly once. + + The permutation test can be computationally expensive and not necessarily + more accurate than the analytical test, but it does not make strong + assumptions about the shape of the underlying distribution. + + Use of trimming is commonly referred to as the trimmed t-test. At times + called Yuen's t-test, this is an extension of Welch's t-test, with the + difference being the use of winsorized means in calculation of the variance + and the trimmed sample size in calculation of the statistic. Trimming is + recommended if the underlying distribution is long-tailed or contaminated + with outliers [4]_. + + The statistic is calculated as ``(np.mean(a) - np.mean(b))/se``, where + ``se`` is the standard error. Therefore, the statistic will be positive + when the sample mean of `a` is greater than the sample mean of `b` and + negative when the sample mean of `a` is less than the sample mean of + `b`. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test + + .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test + + .. [3] B. Efron and T. Hastie. Computer Age Statistical Inference. (2016). + + .. [4] Yuen, Karen K. "The Two-Sample Trimmed t for Unequal Population + Variances." Biometrika, vol. 61, no. 1, 1974, pp. 165-170. JSTOR, + www.jstor.org/stable/2334299. Accessed 30 Mar. 2021. + + .. [5] Yuen, Karen K., and W. J. Dixon. "The Approximate Behaviour and + Performance of the Two-Sample Trimmed t." Biometrika, vol. 60, + no. 2, 1973, pp. 369-374. JSTOR, www.jstor.org/stable/2334550. + Accessed 30 Mar. 2021. + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + + Test with sample with identical means: + + >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) + >>> rvs2 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) + >>> stats.ttest_ind(rvs1, rvs2) + Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952038870015) + >>> stats.ttest_ind(rvs1, rvs2, equal_var=False) + Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952553131064) + + `ttest_ind` underestimates p for unequal variances: + + >>> rvs3 = stats.norm.rvs(loc=5, scale=20, size=500, random_state=rng) + >>> stats.ttest_ind(rvs1, rvs3) + Ttest_indResult(statistic=-1.6370984482905417, pvalue=0.1019251574705033) + >>> stats.ttest_ind(rvs1, rvs3, equal_var=False) + Ttest_indResult(statistic=-1.637098448290542, pvalue=0.10202110497954867) + + When ``n1 != n2``, the equal variance t-statistic is no longer equal to the + unequal variance t-statistic: + + >>> rvs4 = stats.norm.rvs(loc=5, scale=20, size=100, random_state=rng) + >>> stats.ttest_ind(rvs1, rvs4) + Ttest_indResult(statistic=-1.9481646859513422, pvalue=0.05186270935842703) + >>> stats.ttest_ind(rvs1, rvs4, equal_var=False) + Ttest_indResult(statistic=-1.3146566100751664, pvalue=0.1913495266513811) + + T-test with different means, variance, and n: + + >>> rvs5 = stats.norm.rvs(loc=8, scale=20, size=100, random_state=rng) + >>> stats.ttest_ind(rvs1, rvs5) + Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0046418707568707885) + >>> stats.ttest_ind(rvs1, rvs5, equal_var=False) + Ttest_indResult(statistic=-1.8686598649188084, pvalue=0.06434714193919686) + + When performing a permutation test, more permutations typically yields + more accurate results. Use a ``np.random.Generator`` to ensure + reproducibility: + + >>> stats.ttest_ind(rvs1, rvs5, permutations=10000, + ... random_state=rng) + Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0052994700529947) + + Take these two samples, one of which has an extreme tail. + + >>> a = (56, 128.6, 12, 123.8, 64.34, 78, 763.3) + >>> b = (1.1, 2.9, 4.2) + + Use the `trim` keyword to perform a trimmed (Yuen) t-test. For example, + using 20% trimming, ``trim=.2``, the test will reduce the impact of one + (``np.floor(trim*len(a))``) element from each tail of sample `a`. It will + have no effect on sample `b` because ``np.floor(trim*len(b))`` is 0. + + >>> stats.ttest_ind(a, b, trim=.2) + Ttest_indResult(statistic=3.4463884028073513, + pvalue=0.01369338726499547) + """ + if not (0 <= trim < .5): + raise ValueError("Trimming percentage should be 0 <= `trim` < .5.") + + NaN = _get_nan(a, b) + + if a.size == 0 or b.size == 0: + # _axis_nan_policy decorator ensures this only happens with 1d input + return TtestResult(NaN, NaN, df=NaN, alternative=NaN, + standard_error=NaN, estimate=NaN) + + if permutations is not None and permutations != 0: + if trim != 0: + raise ValueError("Permutations are currently not supported " + "with trimming.") + if permutations < 0 or (np.isfinite(permutations) and + int(permutations) != permutations): + raise ValueError("Permutations must be a non-negative integer.") + + t, prob = _permutation_ttest(a, b, permutations=permutations, + axis=axis, equal_var=equal_var, + nan_policy=nan_policy, + random_state=random_state, + alternative=alternative) + df, denom, estimate = NaN, NaN, NaN + + else: + n1 = a.shape[axis] + n2 = b.shape[axis] + + if trim == 0: + if equal_var: + old_errstate = np.geterr() + np.seterr(divide='ignore', invalid='ignore') + v1 = _var(a, axis, ddof=1) + v2 = _var(b, axis, ddof=1) + if equal_var: + np.seterr(**old_errstate) + m1 = np.mean(a, axis) + m2 = np.mean(b, axis) + else: + v1, m1, n1 = _ttest_trim_var_mean_len(a, trim, axis) + v2, m2, n2 = _ttest_trim_var_mean_len(b, trim, axis) + + if equal_var: + df, denom = _equal_var_ttest_denom(v1, n1, v2, n2) + else: + df, denom = _unequal_var_ttest_denom(v1, n1, v2, n2) + t, prob = _ttest_ind_from_stats(m1, m2, denom, df, alternative) + + # when nan_policy='omit', `df` can be different for different axis-slices + df = np.broadcast_to(df, t.shape)[()] + estimate = m1-m2 + + # _axis_nan_policy decorator doesn't play well with strings + alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] + return TtestResult(t, prob, df=df, alternative=alternative_num, + standard_error=denom, estimate=estimate) + + +def _ttest_trim_var_mean_len(a, trim, axis): + """Variance, mean, and length of winsorized input along specified axis""" + # for use with `ttest_ind` when trimming. + # further calculations in this test assume that the inputs are sorted. + # From [4] Section 1 "Let x_1, ..., x_n be n ordered observations..." + a = np.sort(a, axis=axis) + + # `g` is the number of elements to be replaced on each tail, converted + # from a percentage amount of trimming + n = a.shape[axis] + g = int(n * trim) + + # Calculate the Winsorized variance of the input samples according to + # specified `g` + v = _calculate_winsorized_variance(a, g, axis) + + # the total number of elements in the trimmed samples + n -= 2 * g + + # calculate the g-times trimmed mean, as defined in [4] (1-1) + m = trim_mean(a, trim, axis=axis) + return v, m, n + + +def _calculate_winsorized_variance(a, g, axis): + """Calculates g-times winsorized variance along specified axis""" + # it is expected that the input `a` is sorted along the correct axis + if g == 0: + return _var(a, ddof=1, axis=axis) + # move the intended axis to the end that way it is easier to manipulate + a_win = np.moveaxis(a, axis, -1) + + # save where NaNs are for later use. + nans_indices = np.any(np.isnan(a_win), axis=-1) + + # Winsorization and variance calculation are done in one step in [4] + # (1-3), but here winsorization is done first; replace the left and + # right sides with the repeating value. This can be see in effect in ( + # 1-3) in [4], where the leftmost and rightmost tails are replaced with + # `(g + 1) * x_{g + 1}` on the left and `(g + 1) * x_{n - g}` on the + # right. Zero-indexing turns `g + 1` to `g`, and `n - g` to `- g - 1` in + # array indexing. + a_win[..., :g] = a_win[..., [g]] + a_win[..., -g:] = a_win[..., [-g - 1]] + + # Determine the variance. In [4], the degrees of freedom is expressed as + # `h - 1`, where `h = n - 2g` (unnumbered equations in Section 1, end of + # page 369, beginning of page 370). This is converted to NumPy's format, + # `n - ddof` for use with `np.var`. The result is converted to an + # array to accommodate indexing later. + var_win = np.asarray(_var(a_win, ddof=(2 * g + 1), axis=-1)) + + # with `nan_policy='propagate'`, NaNs may be completely trimmed out + # because they were sorted into the tail of the array. In these cases, + # replace computed variances with `np.nan`. + var_win[nans_indices] = np.nan + return var_win + + +def _permutation_distribution_t(data, permutations, size_a, equal_var, + random_state=None): + """Generation permutation distribution of t statistic""" + + random_state = check_random_state(random_state) + + # prepare permutation indices + size = data.shape[-1] + # number of distinct combinations + n_max = special.comb(size, size_a) + + if permutations < n_max: + perm_generator = (random_state.permutation(size) + for i in range(permutations)) + else: + permutations = n_max + perm_generator = (np.concatenate(z) + for z in _all_partitions(size_a, size-size_a)) + + t_stat = [] + for indices in _batch_generator(perm_generator, batch=50): + # get one batch from perm_generator at a time as a list + indices = np.array(indices) + # generate permutations + data_perm = data[..., indices] + # move axis indexing permutations to position 0 to broadcast + # nicely with t_stat_observed, which doesn't have this dimension + data_perm = np.moveaxis(data_perm, -2, 0) + + a = data_perm[..., :size_a] + b = data_perm[..., size_a:] + t_stat.append(_calc_t_stat(a, b, equal_var)) + + t_stat = np.concatenate(t_stat, axis=0) + + return t_stat, permutations, n_max + + +def _calc_t_stat(a, b, equal_var, axis=-1): + """Calculate the t statistic along the given dimension.""" + na = a.shape[axis] + nb = b.shape[axis] + avg_a = np.mean(a, axis=axis) + avg_b = np.mean(b, axis=axis) + var_a = _var(a, axis=axis, ddof=1) + var_b = _var(b, axis=axis, ddof=1) + + if not equal_var: + denom = _unequal_var_ttest_denom(var_a, na, var_b, nb)[1] + else: + denom = _equal_var_ttest_denom(var_a, na, var_b, nb)[1] + + return (avg_a-avg_b)/denom + + +def _permutation_ttest(a, b, permutations, axis=0, equal_var=True, + nan_policy='propagate', random_state=None, + alternative="two-sided"): + """ + Calculates the T-test for the means of TWO INDEPENDENT samples of scores + using permutation methods. + + This test is similar to `stats.ttest_ind`, except it doesn't rely on an + approximate normality assumption since it uses a permutation test. + This function is only called from ttest_ind when permutations is not None. + + Parameters + ---------- + a, b : array_like + The arrays must be broadcastable, except along the dimension + corresponding to `axis` (the zeroth, by default). + axis : int, optional + The axis over which to operate on a and b. + permutations : int, optional + Number of permutations used to calculate p-value. If greater than or + equal to the number of distinct permutations, perform an exact test. + equal_var : bool, optional + If False, an equal variance (Welch's) t-test is conducted. Otherwise, + an ordinary t-test is conducted. + random_state : {None, int, `numpy.random.Generator`}, optional + If `seed` is None the `numpy.random.Generator` singleton is used. + If `seed` is an int, a new ``Generator`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` instance then that instance is + used. + Pseudorandom number generator state used for generating random + permutations. + + Returns + ------- + statistic : float or array + The calculated t-statistic. + pvalue : float or array + The p-value. + + """ + random_state = check_random_state(random_state) + + t_stat_observed = _calc_t_stat(a, b, equal_var, axis=axis) + + na = a.shape[axis] + mat = _broadcast_concatenate((a, b), axis=axis) + mat = np.moveaxis(mat, axis, -1) + + t_stat, permutations, n_max = _permutation_distribution_t( + mat, permutations, size_a=na, equal_var=equal_var, + random_state=random_state) + + compare = {"less": np.less_equal, + "greater": np.greater_equal, + "two-sided": lambda x, y: (x <= -np.abs(y)) | (x >= np.abs(y))} + + # Calculate the p-values + cmps = compare[alternative](t_stat, t_stat_observed) + # Randomized test p-value calculation should use biased estimate; see e.g. + # https://www.degruyter.com/document/doi/10.2202/1544-6115.1585/ + adjustment = 1 if n_max > permutations else 0 + pvalues = (cmps.sum(axis=0) + adjustment) / (permutations + adjustment) + + # nans propagate naturally in statistic calculation, but need to be + # propagated manually into pvalues + if nan_policy == 'propagate' and np.isnan(t_stat_observed).any(): + if np.ndim(pvalues) == 0: + pvalues = np.float64(np.nan) + else: + pvalues[np.isnan(t_stat_observed)] = np.nan + + return (t_stat_observed, pvalues) + + +def _get_len(a, axis, msg): + try: + n = a.shape[axis] + except IndexError: + raise AxisError(axis, a.ndim, msg) from None + return n + + +@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, + result_to_tuple=unpack_TtestResult, n_outputs=6, + paired=True) +def ttest_rel(a, b, axis=0, nan_policy='propagate', alternative="two-sided"): + """Calculate the t-test on TWO RELATED samples of scores, a and b. + + This is a test for the null hypothesis that two related or + repeated samples have identical average (expected) values. + + Parameters + ---------- + a, b : array_like + The arrays must have the same shape. + axis : int or None, optional + Axis along which to compute test. If None, compute over the whole + arrays, `a`, and `b`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the means of the distributions underlying the samples + are unequal. + * 'less': the mean of the distribution underlying the first sample + is less than the mean of the distribution underlying the second + sample. + * 'greater': the mean of the distribution underlying the first + sample is greater than the mean of the distribution underlying + the second sample. + + .. versionadded:: 1.6.0 + + Returns + ------- + result : `~scipy.stats._result_classes.TtestResult` + An object with the following attributes: + + statistic : float or array + The t-statistic. + pvalue : float or array + The p-value associated with the given alternative. + df : float or array + The number of degrees of freedom used in calculation of the + t-statistic; this is one less than the size of the sample + (``a.shape[axis]``). + + .. versionadded:: 1.10.0 + + The object also has the following method: + + confidence_interval(confidence_level=0.95) + Computes a confidence interval around the difference in + population means for the given confidence level. + The confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. + + .. versionadded:: 1.10.0 + + Notes + ----- + Examples for use are scores of the same set of student in + different exams, or repeated sampling from the same units. The + test measures whether the average score differs significantly + across samples (e.g. exams). If we observe a large p-value, for + example greater than 0.05 or 0.1 then we cannot reject the null + hypothesis of identical average scores. If the p-value is smaller + than the threshold, e.g. 1%, 5% or 10%, then we reject the null + hypothesis of equal averages. Small p-values are associated with + large t-statistics. + + The t-statistic is calculated as ``np.mean(a - b)/se``, where ``se`` is the + standard error. Therefore, the t-statistic will be positive when the sample + mean of ``a - b`` is greater than zero and negative when the sample mean of + ``a - b`` is less than zero. + + References + ---------- + https://en.wikipedia.org/wiki/T-test#Dependent_t-test_for_paired_samples + + Examples + -------- + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + + >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) + >>> rvs2 = (stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) + ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng)) + >>> stats.ttest_rel(rvs1, rvs2) + TtestResult(statistic=-0.4549717054410304, pvalue=0.6493274702088672, df=499) + >>> rvs3 = (stats.norm.rvs(loc=8, scale=10, size=500, random_state=rng) + ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng)) + >>> stats.ttest_rel(rvs1, rvs3) + TtestResult(statistic=-5.879467544540889, pvalue=7.540777129099917e-09, df=499) + + """ + a, b, axis = _chk2_asarray(a, b, axis) + + na = _get_len(a, axis, "first argument") + nb = _get_len(b, axis, "second argument") + if na != nb: + raise ValueError('unequal length arrays') + + if na == 0 or nb == 0: + # _axis_nan_policy decorator ensures this only happens with 1d input + NaN = _get_nan(a, b) + return TtestResult(NaN, NaN, df=NaN, alternative=NaN, + standard_error=NaN, estimate=NaN) + + n = a.shape[axis] + df = n - 1 + + d = (a - b).astype(np.float64) + v = _var(d, axis, ddof=1) + dm = np.mean(d, axis) + denom = np.sqrt(v / n) + + with np.errstate(divide='ignore', invalid='ignore'): + t = np.divide(dm, denom)[()] + prob = _get_pvalue(t, distributions.t(df), alternative) + + # when nan_policy='omit', `df` can be different for different axis-slices + df = np.broadcast_to(df, t.shape)[()] + + # _axis_nan_policy decorator doesn't play well with strings + alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] + return TtestResult(t, prob, df=df, alternative=alternative_num, + standard_error=denom, estimate=dm) + + +# Map from names to lambda_ values used in power_divergence(). +_power_div_lambda_names = { + "pearson": 1, + "log-likelihood": 0, + "freeman-tukey": -0.5, + "mod-log-likelihood": -1, + "neyman": -2, + "cressie-read": 2/3, +} + + +def _count(a, axis=None): + """Count the number of non-masked elements of an array. + + This function behaves like `np.ma.count`, but is much faster + for ndarrays. + """ + if hasattr(a, 'count'): + num = a.count(axis=axis) + if isinstance(num, np.ndarray) and num.ndim == 0: + # In some cases, the `count` method returns a scalar array (e.g. + # np.array(3)), but we want a plain integer. + num = int(num) + else: + if axis is None: + num = a.size + else: + num = a.shape[axis] + return num + + +def _m_broadcast_to(a, shape): + if np.ma.isMaskedArray(a): + return np.ma.masked_array(np.broadcast_to(a, shape), + mask=np.broadcast_to(a.mask, shape)) + return np.broadcast_to(a, shape, subok=True) + + +Power_divergenceResult = namedtuple('Power_divergenceResult', + ('statistic', 'pvalue')) + + +def power_divergence(f_obs, f_exp=None, ddof=0, axis=0, lambda_=None): + """Cressie-Read power divergence statistic and goodness of fit test. + + This function tests the null hypothesis that the categorical data + has the given frequencies, using the Cressie-Read power divergence + statistic. + + Parameters + ---------- + f_obs : array_like + Observed frequencies in each category. + f_exp : array_like, optional + Expected frequencies in each category. By default the categories are + assumed to be equally likely. + ddof : int, optional + "Delta degrees of freedom": adjustment to the degrees of freedom + for the p-value. The p-value is computed using a chi-squared + distribution with ``k - 1 - ddof`` degrees of freedom, where `k` + is the number of observed frequencies. The default value of `ddof` + is 0. + axis : int or None, optional + The axis of the broadcast result of `f_obs` and `f_exp` along which to + apply the test. If axis is None, all values in `f_obs` are treated + as a single data set. Default is 0. + lambda_ : float or str, optional + The power in the Cressie-Read power divergence statistic. The default + is 1. For convenience, `lambda_` may be assigned one of the following + strings, in which case the corresponding numerical value is used: + + * ``"pearson"`` (value 1) + Pearson's chi-squared statistic. In this case, the function is + equivalent to `chisquare`. + * ``"log-likelihood"`` (value 0) + Log-likelihood ratio. Also known as the G-test [3]_. + * ``"freeman-tukey"`` (value -1/2) + Freeman-Tukey statistic. + * ``"mod-log-likelihood"`` (value -1) + Modified log-likelihood ratio. + * ``"neyman"`` (value -2) + Neyman's statistic. + * ``"cressie-read"`` (value 2/3) + The power recommended in [5]_. + + Returns + ------- + res: Power_divergenceResult + An object containing attributes: + + statistic : float or ndarray + The Cressie-Read power divergence test statistic. The value is + a float if `axis` is None or if` `f_obs` and `f_exp` are 1-D. + pvalue : float or ndarray + The p-value of the test. The value is a float if `ddof` and the + return value `stat` are scalars. + + See Also + -------- + chisquare + + Notes + ----- + This test is invalid when the observed or expected frequencies in each + category are too small. A typical rule is that all of the observed + and expected frequencies should be at least 5. + + Also, the sum of the observed and expected frequencies must be the same + for the test to be valid; `power_divergence` raises an error if the sums + do not agree within a relative tolerance of ``1e-8``. + + When `lambda_` is less than zero, the formula for the statistic involves + dividing by `f_obs`, so a warning or error may be generated if any value + in `f_obs` is 0. + + Similarly, a warning or error may be generated if any value in `f_exp` is + zero when `lambda_` >= 0. + + The default degrees of freedom, k-1, are for the case when no parameters + of the distribution are estimated. If p parameters are estimated by + efficient maximum likelihood then the correct degrees of freedom are + k-1-p. If the parameters are estimated in a different way, then the + dof can be between k-1-p and k-1. However, it is also possible that + the asymptotic distribution is not a chisquare, in which case this + test is not appropriate. + + This function handles masked arrays. If an element of `f_obs` or `f_exp` + is masked, then data at that position is ignored, and does not count + towards the size of the data set. + + .. versionadded:: 0.13.0 + + References + ---------- + .. [1] Lowry, Richard. "Concepts and Applications of Inferential + Statistics". Chapter 8. + https://web.archive.org/web/20171015035606/http://faculty.vassar.edu/lowry/ch8pt1.html + .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test + .. [3] "G-test", https://en.wikipedia.org/wiki/G-test + .. [4] Sokal, R. R. and Rohlf, F. J. "Biometry: the principles and + practice of statistics in biological research", New York: Freeman + (1981) + .. [5] Cressie, N. and Read, T. R. C., "Multinomial Goodness-of-Fit + Tests", J. Royal Stat. Soc. Series B, Vol. 46, No. 3 (1984), + pp. 440-464. + + Examples + -------- + (See `chisquare` for more examples.) + + When just `f_obs` is given, it is assumed that the expected frequencies + are uniform and given by the mean of the observed frequencies. Here we + perform a G-test (i.e. use the log-likelihood ratio statistic): + + >>> import numpy as np + >>> from scipy.stats import power_divergence + >>> power_divergence([16, 18, 16, 14, 12, 12], lambda_='log-likelihood') + (2.006573162632538, 0.84823476779463769) + + The expected frequencies can be given with the `f_exp` argument: + + >>> power_divergence([16, 18, 16, 14, 12, 12], + ... f_exp=[16, 16, 16, 16, 16, 8], + ... lambda_='log-likelihood') + (3.3281031458963746, 0.6495419288047497) + + When `f_obs` is 2-D, by default the test is applied to each column. + + >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T + >>> obs.shape + (6, 2) + >>> power_divergence(obs, lambda_="log-likelihood") + (array([ 2.00657316, 6.77634498]), array([ 0.84823477, 0.23781225])) + + By setting ``axis=None``, the test is applied to all data in the array, + which is equivalent to applying the test to the flattened array. + + >>> power_divergence(obs, axis=None) + (23.31034482758621, 0.015975692534127565) + >>> power_divergence(obs.ravel()) + (23.31034482758621, 0.015975692534127565) + + `ddof` is the change to make to the default degrees of freedom. + + >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=1) + (2.0, 0.73575888234288467) + + The calculation of the p-values is done by broadcasting the + test statistic with `ddof`. + + >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=[0,1,2]) + (2.0, array([ 0.84914504, 0.73575888, 0.5724067 ])) + + `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has + shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting + `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared + statistics, we must use ``axis=1``: + + >>> power_divergence([16, 18, 16, 14, 12, 12], + ... f_exp=[[16, 16, 16, 16, 16, 8], + ... [8, 20, 20, 16, 12, 12]], + ... axis=1) + (array([ 3.5 , 9.25]), array([ 0.62338763, 0.09949846])) + + """ + # Convert the input argument `lambda_` to a numerical value. + if isinstance(lambda_, str): + if lambda_ not in _power_div_lambda_names: + names = repr(list(_power_div_lambda_names.keys()))[1:-1] + raise ValueError(f"invalid string for lambda_: {lambda_!r}. " + f"Valid strings are {names}") + lambda_ = _power_div_lambda_names[lambda_] + elif lambda_ is None: + lambda_ = 1 + + f_obs = np.asanyarray(f_obs) + f_obs_float = f_obs.astype(np.float64) + + if f_exp is not None: + f_exp = np.asanyarray(f_exp) + bshape = np.broadcast_shapes(f_obs_float.shape, f_exp.shape) + f_obs_float = _m_broadcast_to(f_obs_float, bshape) + f_exp = _m_broadcast_to(f_exp, bshape) + rtol = 1e-8 # to pass existing tests + with np.errstate(invalid='ignore'): + f_obs_sum = f_obs_float.sum(axis=axis) + f_exp_sum = f_exp.sum(axis=axis) + relative_diff = (np.abs(f_obs_sum - f_exp_sum) / + np.minimum(f_obs_sum, f_exp_sum)) + diff_gt_tol = (relative_diff > rtol).any() + if diff_gt_tol: + msg = (f"For each axis slice, the sum of the observed " + f"frequencies must agree with the sum of the " + f"expected frequencies to a relative tolerance " + f"of {rtol}, but the percent differences are:\n" + f"{relative_diff}") + raise ValueError(msg) + + else: + # Ignore 'invalid' errors so the edge case of a data set with length 0 + # is handled without spurious warnings. + with np.errstate(invalid='ignore'): + f_exp = f_obs.mean(axis=axis, keepdims=True) + + # `terms` is the array of terms that are summed along `axis` to create + # the test statistic. We use some specialized code for a few special + # cases of lambda_. + if lambda_ == 1: + # Pearson's chi-squared statistic + terms = (f_obs_float - f_exp)**2 / f_exp + elif lambda_ == 0: + # Log-likelihood ratio (i.e. G-test) + terms = 2.0 * special.xlogy(f_obs, f_obs / f_exp) + elif lambda_ == -1: + # Modified log-likelihood ratio + terms = 2.0 * special.xlogy(f_exp, f_exp / f_obs) + else: + # General Cressie-Read power divergence. + terms = f_obs * ((f_obs / f_exp)**lambda_ - 1) + terms /= 0.5 * lambda_ * (lambda_ + 1) + + stat = terms.sum(axis=axis) + + num_obs = _count(terms, axis=axis) + ddof = asarray(ddof) + p = distributions.chi2.sf(stat, num_obs - 1 - ddof) + + return Power_divergenceResult(stat, p) + + +def chisquare(f_obs, f_exp=None, ddof=0, axis=0): + """Calculate a one-way chi-square test. + + The chi-square test tests the null hypothesis that the categorical data + has the given frequencies. + + Parameters + ---------- + f_obs : array_like + Observed frequencies in each category. + f_exp : array_like, optional + Expected frequencies in each category. By default the categories are + assumed to be equally likely. + ddof : int, optional + "Delta degrees of freedom": adjustment to the degrees of freedom + for the p-value. The p-value is computed using a chi-squared + distribution with ``k - 1 - ddof`` degrees of freedom, where `k` + is the number of observed frequencies. The default value of `ddof` + is 0. + axis : int or None, optional + The axis of the broadcast result of `f_obs` and `f_exp` along which to + apply the test. If axis is None, all values in `f_obs` are treated + as a single data set. Default is 0. + + Returns + ------- + res: Power_divergenceResult + An object containing attributes: + + statistic : float or ndarray + The chi-squared test statistic. The value is a float if `axis` is + None or `f_obs` and `f_exp` are 1-D. + pvalue : float or ndarray + The p-value of the test. The value is a float if `ddof` and the + result attribute `statistic` are scalars. + + See Also + -------- + scipy.stats.power_divergence + scipy.stats.fisher_exact : Fisher exact test on a 2x2 contingency table. + scipy.stats.barnard_exact : An unconditional exact test. An alternative + to chi-squared test for small sample sizes. + + Notes + ----- + This test is invalid when the observed or expected frequencies in each + category are too small. A typical rule is that all of the observed + and expected frequencies should be at least 5. According to [3]_, the + total number of samples is recommended to be greater than 13, + otherwise exact tests (such as Barnard's Exact test) should be used + because they do not overreject. + + Also, the sum of the observed and expected frequencies must be the same + for the test to be valid; `chisquare` raises an error if the sums do not + agree within a relative tolerance of ``1e-8``. + + The default degrees of freedom, k-1, are for the case when no parameters + of the distribution are estimated. If p parameters are estimated by + efficient maximum likelihood then the correct degrees of freedom are + k-1-p. If the parameters are estimated in a different way, then the + dof can be between k-1-p and k-1. However, it is also possible that + the asymptotic distribution is not chi-square, in which case this test + is not appropriate. + + References + ---------- + .. [1] Lowry, Richard. "Concepts and Applications of Inferential + Statistics". Chapter 8. + https://web.archive.org/web/20171022032306/http://vassarstats.net:80/textbook/ch8pt1.html + .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test + .. [3] Pearson, Karl. "On the criterion that a given system of deviations from the probable + in the case of a correlated system of variables is such that it can be reasonably + supposed to have arisen from random sampling", Philosophical Magazine. Series 5. 50 + (1900), pp. 157-175. + .. [4] Mannan, R. William and E. Charles. Meslow. "Bird populations and + vegetation characteristics in managed and old-growth forests, + northeastern Oregon." Journal of Wildlife Management + 48, 1219-1238, :doi:`10.2307/3801783`, 1984. + + Examples + -------- + In [4]_, bird foraging behavior was investigated in an old-growth forest + of Oregon. + In the forest, 44% of the canopy volume was Douglas fir, + 24% was ponderosa pine, 29% was grand fir, and 3% was western larch. + The authors observed the behavior of several species of birds, one of + which was the red-breasted nuthatch. They made 189 observations of this + species foraging, recording 43 ("23%") of observations in Douglas fir, + 52 ("28%") in ponderosa pine, 54 ("29%") in grand fir, and 40 ("21%") in + western larch. + + Using a chi-square test, we can test the null hypothesis that the + proportions of foraging events are equal to the proportions of canopy + volume. The authors of the paper considered a p-value less than 1% to be + significant. + + Using the above proportions of canopy volume and observed events, we can + infer expected frequencies. + + >>> import numpy as np + >>> f_exp = np.array([44, 24, 29, 3]) / 100 * 189 + + The observed frequencies of foraging were: + + >>> f_obs = np.array([43, 52, 54, 40]) + + We can now compare the observed frequencies with the expected frequencies. + + >>> from scipy.stats import chisquare + >>> chisquare(f_obs=f_obs, f_exp=f_exp) + Power_divergenceResult(statistic=228.23515947653874, pvalue=3.3295585338846486e-49) + + The p-value is well below the chosen significance level. Hence, the + authors considered the difference to be significant and concluded + that the relative proportions of foraging events were not the same + as the relative proportions of tree canopy volume. + + Following are other generic examples to demonstrate how the other + parameters can be used. + + When just `f_obs` is given, it is assumed that the expected frequencies + are uniform and given by the mean of the observed frequencies. + + >>> chisquare([16, 18, 16, 14, 12, 12]) + Power_divergenceResult(statistic=2.0, pvalue=0.84914503608460956) + + With `f_exp` the expected frequencies can be given. + + >>> chisquare([16, 18, 16, 14, 12, 12], f_exp=[16, 16, 16, 16, 16, 8]) + Power_divergenceResult(statistic=3.5, pvalue=0.62338762774958223) + + When `f_obs` is 2-D, by default the test is applied to each column. + + >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T + >>> obs.shape + (6, 2) + >>> chisquare(obs) + Power_divergenceResult(statistic=array([2. , 6.66666667]), pvalue=array([0.84914504, 0.24663415])) + + By setting ``axis=None``, the test is applied to all data in the array, + which is equivalent to applying the test to the flattened array. + + >>> chisquare(obs, axis=None) + Power_divergenceResult(statistic=23.31034482758621, pvalue=0.015975692534127565) + >>> chisquare(obs.ravel()) + Power_divergenceResult(statistic=23.310344827586206, pvalue=0.01597569253412758) + + `ddof` is the change to make to the default degrees of freedom. + + >>> chisquare([16, 18, 16, 14, 12, 12], ddof=1) + Power_divergenceResult(statistic=2.0, pvalue=0.7357588823428847) + + The calculation of the p-values is done by broadcasting the + chi-squared statistic with `ddof`. + + >>> chisquare([16, 18, 16, 14, 12, 12], ddof=[0,1,2]) + Power_divergenceResult(statistic=2.0, pvalue=array([0.84914504, 0.73575888, 0.5724067 ])) + + `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has + shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting + `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared + statistics, we use ``axis=1``: + + >>> chisquare([16, 18, 16, 14, 12, 12], + ... f_exp=[[16, 16, 16, 16, 16, 8], [8, 20, 20, 16, 12, 12]], + ... axis=1) + Power_divergenceResult(statistic=array([3.5 , 9.25]), pvalue=array([0.62338763, 0.09949846])) + + """ # noqa: E501 + return power_divergence(f_obs, f_exp=f_exp, ddof=ddof, axis=axis, + lambda_="pearson") + + +KstestResult = _make_tuple_bunch('KstestResult', ['statistic', 'pvalue'], + ['statistic_location', 'statistic_sign']) + + +def _compute_dplus(cdfvals, x): + """Computes D+ as used in the Kolmogorov-Smirnov test. + + Parameters + ---------- + cdfvals : array_like + Sorted array of CDF values between 0 and 1 + x: array_like + Sorted array of the stochastic variable itself + + Returns + ------- + res: Pair with the following elements: + - The maximum distance of the CDF values below Uniform(0, 1). + - The location at which the maximum is reached. + + """ + n = len(cdfvals) + dplus = (np.arange(1.0, n + 1) / n - cdfvals) + amax = dplus.argmax() + loc_max = x[amax] + return (dplus[amax], loc_max) + + +def _compute_dminus(cdfvals, x): + """Computes D- as used in the Kolmogorov-Smirnov test. + + Parameters + ---------- + cdfvals : array_like + Sorted array of CDF values between 0 and 1 + x: array_like + Sorted array of the stochastic variable itself + + Returns + ------- + res: Pair with the following elements: + - Maximum distance of the CDF values above Uniform(0, 1) + - The location at which the maximum is reached. + """ + n = len(cdfvals) + dminus = (cdfvals - np.arange(0.0, n)/n) + amax = dminus.argmax() + loc_max = x[amax] + return (dminus[amax], loc_max) + + +def _tuple_to_KstestResult(statistic, pvalue, + statistic_location, statistic_sign): + return KstestResult(statistic, pvalue, + statistic_location=statistic_location, + statistic_sign=statistic_sign) + + +def _KstestResult_to_tuple(res): + return *res, res.statistic_location, res.statistic_sign + + +@_axis_nan_policy_factory(_tuple_to_KstestResult, n_samples=1, n_outputs=4, + result_to_tuple=_KstestResult_to_tuple) +@_rename_parameter("mode", "method") +def ks_1samp(x, cdf, args=(), alternative='two-sided', method='auto'): + """ + Performs the one-sample Kolmogorov-Smirnov test for goodness of fit. + + This test compares the underlying distribution F(x) of a sample + against a given continuous distribution G(x). See Notes for a description + of the available null and alternative hypotheses. + + Parameters + ---------- + x : array_like + a 1-D array of observations of iid random variables. + cdf : callable + callable used to calculate the cdf. + args : tuple, sequence, optional + Distribution parameters, used with `cdf`. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the null and alternative hypotheses. Default is 'two-sided'. + Please see explanations in the Notes below. + method : {'auto', 'exact', 'approx', 'asymp'}, optional + Defines the distribution used for calculating the p-value. + The following options are available (default is 'auto'): + + * 'auto' : selects one of the other options. + * 'exact' : uses the exact distribution of test statistic. + * 'approx' : approximates the two-sided probability with twice + the one-sided probability + * 'asymp': uses asymptotic distribution of test statistic + + Returns + ------- + res: KstestResult + An object containing attributes: + + statistic : float + KS test statistic, either D+, D-, or D (the maximum of the two) + pvalue : float + One-tailed or two-tailed p-value. + statistic_location : float + Value of `x` corresponding with the KS statistic; i.e., the + distance between the empirical distribution function and the + hypothesized cumulative distribution function is measured at this + observation. + statistic_sign : int + +1 if the KS statistic is the maximum positive difference between + the empirical distribution function and the hypothesized cumulative + distribution function (D+); -1 if the KS statistic is the maximum + negative difference (D-). + + + See Also + -------- + ks_2samp, kstest + + Notes + ----- + There are three options for the null and corresponding alternative + hypothesis that can be selected using the `alternative` parameter. + + - `two-sided`: The null hypothesis is that the two distributions are + identical, F(x)=G(x) for all x; the alternative is that they are not + identical. + + - `less`: The null hypothesis is that F(x) >= G(x) for all x; the + alternative is that F(x) < G(x) for at least one x. + + - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the + alternative is that F(x) > G(x) for at least one x. + + Note that the alternative hypotheses describe the *CDFs* of the + underlying distributions, not the observed values. For example, + suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in + x1 tend to be less than those in x2. + + Examples + -------- + Suppose we wish to test the null hypothesis that a sample is distributed + according to the standard normal. + We choose a confidence level of 95%; that is, we will reject the null + hypothesis in favor of the alternative if the p-value is less than 0.05. + + When testing uniformly distributed data, we would expect the + null hypothesis to be rejected. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> stats.ks_1samp(stats.uniform.rvs(size=100, random_state=rng), + ... stats.norm.cdf) + KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23) + + Indeed, the p-value is lower than our threshold of 0.05, so we reject the + null hypothesis in favor of the default "two-sided" alternative: the data + are *not* distributed according to the standard normal. + + When testing random variates from the standard normal distribution, we + expect the data to be consistent with the null hypothesis most of the time. + + >>> x = stats.norm.rvs(size=100, random_state=rng) + >>> stats.ks_1samp(x, stats.norm.cdf) + KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717) + + As expected, the p-value of 0.92 is not below our threshold of 0.05, so + we cannot reject the null hypothesis. + + Suppose, however, that the random variates are distributed according to + a normal distribution that is shifted toward greater values. In this case, + the cumulative density function (CDF) of the underlying distribution tends + to be *less* than the CDF of the standard normal. Therefore, we would + expect the null hypothesis to be rejected with ``alternative='less'``: + + >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng) + >>> stats.ks_1samp(x, stats.norm.cdf, alternative='less') + KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) + + and indeed, with p-value smaller than our threshold, we reject the null + hypothesis in favor of the alternative. + + """ + mode = method + + alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( + alternative.lower()[0], alternative) + if alternative not in ['two-sided', 'greater', 'less']: + raise ValueError("Unexpected alternative %s" % alternative) + + N = len(x) + x = np.sort(x) + cdfvals = cdf(x, *args) + np_one = np.int8(1) + + if alternative == 'greater': + Dplus, d_location = _compute_dplus(cdfvals, x) + return KstestResult(Dplus, distributions.ksone.sf(Dplus, N), + statistic_location=d_location, + statistic_sign=np_one) + + if alternative == 'less': + Dminus, d_location = _compute_dminus(cdfvals, x) + return KstestResult(Dminus, distributions.ksone.sf(Dminus, N), + statistic_location=d_location, + statistic_sign=-np_one) + + # alternative == 'two-sided': + Dplus, dplus_location = _compute_dplus(cdfvals, x) + Dminus, dminus_location = _compute_dminus(cdfvals, x) + if Dplus > Dminus: + D = Dplus + d_location = dplus_location + d_sign = np_one + else: + D = Dminus + d_location = dminus_location + d_sign = -np_one + + if mode == 'auto': # Always select exact + mode = 'exact' + if mode == 'exact': + prob = distributions.kstwo.sf(D, N) + elif mode == 'asymp': + prob = distributions.kstwobign.sf(D * np.sqrt(N)) + else: + # mode == 'approx' + prob = 2 * distributions.ksone.sf(D, N) + prob = np.clip(prob, 0, 1) + return KstestResult(D, prob, + statistic_location=d_location, + statistic_sign=d_sign) + + +Ks_2sampResult = KstestResult + + +def _compute_prob_outside_square(n, h): + """ + Compute the proportion of paths that pass outside the two diagonal lines. + + Parameters + ---------- + n : integer + n > 0 + h : integer + 0 <= h <= n + + Returns + ------- + p : float + The proportion of paths that pass outside the lines x-y = +/-h. + + """ + # Compute Pr(D_{n,n} >= h/n) + # Prob = 2 * ( binom(2n, n-h) - binom(2n, n-2a) + binom(2n, n-3a) - ... ) + # / binom(2n, n) + # This formulation exhibits subtractive cancellation. + # Instead divide each term by binom(2n, n), then factor common terms + # and use a Horner-like algorithm + # P = 2 * A0 * (1 - A1*(1 - A2*(1 - A3*(1 - A4*(...))))) + + P = 0.0 + k = int(np.floor(n / h)) + while k >= 0: + p1 = 1.0 + # Each of the Ai terms has numerator and denominator with + # h simple terms. + for j in range(h): + p1 = (n - k * h - j) * p1 / (n + k * h + j + 1) + P = p1 * (1.0 - P) + k -= 1 + return 2 * P + + +def _count_paths_outside_method(m, n, g, h): + """Count the number of paths that pass outside the specified diagonal. + + Parameters + ---------- + m : integer + m > 0 + n : integer + n > 0 + g : integer + g is greatest common divisor of m and n + h : integer + 0 <= h <= lcm(m,n) + + Returns + ------- + p : float + The number of paths that go low. + The calculation may overflow - check for a finite answer. + + Notes + ----- + Count the integer lattice paths from (0, 0) to (m, n), which at some + point (x, y) along the path, satisfy: + m*y <= n*x - h*g + The paths make steps of size +1 in either positive x or positive y + directions. + + We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk. + Hodges, J.L. Jr., + "The Significance Probability of the Smirnov Two-Sample Test," + Arkiv fiur Matematik, 3, No. 43 (1958), 469-86. + + """ + # Compute #paths which stay lower than x/m-y/n = h/lcm(m,n) + # B(x, y) = #{paths from (0,0) to (x,y) without + # previously crossing the boundary} + # = binom(x, y) - #{paths which already reached the boundary} + # Multiply by the number of path extensions going from (x, y) to (m, n) + # Sum. + + # Probability is symmetrical in m, n. Computation below assumes m >= n. + if m < n: + m, n = n, m + mg = m // g + ng = n // g + + # Not every x needs to be considered. + # xj holds the list of x values to be checked. + # Wherever n*x/m + ng*h crosses an integer + lxj = n + (mg-h)//mg + xj = [(h + mg * j + ng-1)//ng for j in range(lxj)] + # B is an array just holding a few values of B(x,y), the ones needed. + # B[j] == B(x_j, j) + if lxj == 0: + return special.binom(m + n, n) + B = np.zeros(lxj) + B[0] = 1 + # Compute the B(x, y) terms + for j in range(1, lxj): + Bj = special.binom(xj[j] + j, j) + for i in range(j): + bin = special.binom(xj[j] - xj[i] + j - i, j-i) + Bj -= bin * B[i] + B[j] = Bj + # Compute the number of path extensions... + num_paths = 0 + for j in range(lxj): + bin = special.binom((m-xj[j]) + (n - j), n-j) + term = B[j] * bin + num_paths += term + return num_paths + + +def _attempt_exact_2kssamp(n1, n2, g, d, alternative): + """Attempts to compute the exact 2sample probability. + + n1, n2 are the sample sizes + g is the gcd(n1, n2) + d is the computed max difference in ECDFs + + Returns (success, d, probability) + """ + lcm = (n1 // g) * n2 + h = int(np.round(d * lcm)) + d = h * 1.0 / lcm + if h == 0: + return True, d, 1.0 + saw_fp_error, prob = False, np.nan + try: + with np.errstate(invalid="raise", over="raise"): + if alternative == 'two-sided': + if n1 == n2: + prob = _compute_prob_outside_square(n1, h) + else: + prob = _compute_outer_prob_inside_method(n1, n2, g, h) + else: + if n1 == n2: + # prob = binom(2n, n-h) / binom(2n, n) + # Evaluating in that form incurs roundoff errors + # from special.binom. Instead calculate directly + jrange = np.arange(h) + prob = np.prod((n1 - jrange) / (n1 + jrange + 1.0)) + else: + with np.errstate(over='raise'): + num_paths = _count_paths_outside_method(n1, n2, g, h) + bin = special.binom(n1 + n2, n1) + if num_paths > bin or np.isinf(bin): + saw_fp_error = True + else: + prob = num_paths / bin + + except (FloatingPointError, OverflowError): + saw_fp_error = True + + if saw_fp_error: + return False, d, np.nan + if not (0 <= prob <= 1): + return False, d, prob + return True, d, prob + + +@_axis_nan_policy_factory(_tuple_to_KstestResult, n_samples=2, n_outputs=4, + result_to_tuple=_KstestResult_to_tuple) +@_rename_parameter("mode", "method") +def ks_2samp(data1, data2, alternative='two-sided', method='auto'): + """ + Performs the two-sample Kolmogorov-Smirnov test for goodness of fit. + + This test compares the underlying continuous distributions F(x) and G(x) + of two independent samples. See Notes for a description of the available + null and alternative hypotheses. + + Parameters + ---------- + data1, data2 : array_like, 1-Dimensional + Two arrays of sample observations assumed to be drawn from a continuous + distribution, sample sizes can be different. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the null and alternative hypotheses. Default is 'two-sided'. + Please see explanations in the Notes below. + method : {'auto', 'exact', 'asymp'}, optional + Defines the method used for calculating the p-value. + The following options are available (default is 'auto'): + + * 'auto' : use 'exact' for small size arrays, 'asymp' for large + * 'exact' : use exact distribution of test statistic + * 'asymp' : use asymptotic distribution of test statistic + + Returns + ------- + res: KstestResult + An object containing attributes: + + statistic : float + KS test statistic. + pvalue : float + One-tailed or two-tailed p-value. + statistic_location : float + Value from `data1` or `data2` corresponding with the KS statistic; + i.e., the distance between the empirical distribution functions is + measured at this observation. + statistic_sign : int + +1 if the empirical distribution function of `data1` exceeds + the empirical distribution function of `data2` at + `statistic_location`, otherwise -1. + + See Also + -------- + kstest, ks_1samp, epps_singleton_2samp, anderson_ksamp + + Notes + ----- + There are three options for the null and corresponding alternative + hypothesis that can be selected using the `alternative` parameter. + + - `less`: The null hypothesis is that F(x) >= G(x) for all x; the + alternative is that F(x) < G(x) for at least one x. The statistic + is the magnitude of the minimum (most negative) difference between the + empirical distribution functions of the samples. + + - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the + alternative is that F(x) > G(x) for at least one x. The statistic + is the maximum (most positive) difference between the empirical + distribution functions of the samples. + + - `two-sided`: The null hypothesis is that the two distributions are + identical, F(x)=G(x) for all x; the alternative is that they are not + identical. The statistic is the maximum absolute difference between the + empirical distribution functions of the samples. + + Note that the alternative hypotheses describe the *CDFs* of the + underlying distributions, not the observed values of the data. For example, + suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in + x1 tend to be less than those in x2. + + If the KS statistic is large, then the p-value will be small, and this may + be taken as evidence against the null hypothesis in favor of the + alternative. + + If ``method='exact'``, `ks_2samp` attempts to compute an exact p-value, + that is, the probability under the null hypothesis of obtaining a test + statistic value as extreme as the value computed from the data. + If ``method='asymp'``, the asymptotic Kolmogorov-Smirnov distribution is + used to compute an approximate p-value. + If ``method='auto'``, an exact p-value computation is attempted if both + sample sizes are less than 10000; otherwise, the asymptotic method is used. + In any case, if an exact p-value calculation is attempted and fails, a + warning will be emitted, and the asymptotic p-value will be returned. + + The 'two-sided' 'exact' computation computes the complementary probability + and then subtracts from 1. As such, the minimum probability it can return + is about 1e-16. While the algorithm itself is exact, numerical + errors may accumulate for large sample sizes. It is most suited to + situations in which one of the sample sizes is only a few thousand. + + We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk [1]_. + + References + ---------- + .. [1] Hodges, J.L. Jr., "The Significance Probability of the Smirnov + Two-Sample Test," Arkiv fiur Matematik, 3, No. 43 (1958), 469-486. + + Examples + -------- + Suppose we wish to test the null hypothesis that two samples were drawn + from the same distribution. + We choose a confidence level of 95%; that is, we will reject the null + hypothesis in favor of the alternative if the p-value is less than 0.05. + + If the first sample were drawn from a uniform distribution and the second + were drawn from the standard normal, we would expect the null hypothesis + to be rejected. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> sample1 = stats.uniform.rvs(size=100, random_state=rng) + >>> sample2 = stats.norm.rvs(size=110, random_state=rng) + >>> stats.ks_2samp(sample1, sample2) + KstestResult(statistic=0.5454545454545454, pvalue=7.37417839555191e-15) + + Indeed, the p-value is lower than our threshold of 0.05, so we reject the + null hypothesis in favor of the default "two-sided" alternative: the data + were *not* drawn from the same distribution. + + When both samples are drawn from the same distribution, we expect the data + to be consistent with the null hypothesis most of the time. + + >>> sample1 = stats.norm.rvs(size=105, random_state=rng) + >>> sample2 = stats.norm.rvs(size=95, random_state=rng) + >>> stats.ks_2samp(sample1, sample2) + KstestResult(statistic=0.10927318295739348, pvalue=0.5438289009927495) + + As expected, the p-value of 0.54 is not below our threshold of 0.05, so + we cannot reject the null hypothesis. + + Suppose, however, that the first sample were drawn from + a normal distribution shifted toward greater values. In this case, + the cumulative density function (CDF) of the underlying distribution tends + to be *less* than the CDF underlying the second sample. Therefore, we would + expect the null hypothesis to be rejected with ``alternative='less'``: + + >>> sample1 = stats.norm.rvs(size=105, loc=0.5, random_state=rng) + >>> stats.ks_2samp(sample1, sample2, alternative='less') + KstestResult(statistic=0.4055137844611529, pvalue=3.5474563068855554e-08) + + and indeed, with p-value smaller than our threshold, we reject the null + hypothesis in favor of the alternative. + + """ + mode = method + + if mode not in ['auto', 'exact', 'asymp']: + raise ValueError(f'Invalid value for mode: {mode}') + alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( + alternative.lower()[0], alternative) + if alternative not in ['two-sided', 'less', 'greater']: + raise ValueError(f'Invalid value for alternative: {alternative}') + MAX_AUTO_N = 10000 # 'auto' will attempt to be exact if n1,n2 <= MAX_AUTO_N + if np.ma.is_masked(data1): + data1 = data1.compressed() + if np.ma.is_masked(data2): + data2 = data2.compressed() + data1 = np.sort(data1) + data2 = np.sort(data2) + n1 = data1.shape[0] + n2 = data2.shape[0] + if min(n1, n2) == 0: + raise ValueError('Data passed to ks_2samp must not be empty') + + data_all = np.concatenate([data1, data2]) + # using searchsorted solves equal data problem + cdf1 = np.searchsorted(data1, data_all, side='right') / n1 + cdf2 = np.searchsorted(data2, data_all, side='right') / n2 + cddiffs = cdf1 - cdf2 + + # Identify the location of the statistic + argminS = np.argmin(cddiffs) + argmaxS = np.argmax(cddiffs) + loc_minS = data_all[argminS] + loc_maxS = data_all[argmaxS] + + # Ensure sign of minS is not negative. + minS = np.clip(-cddiffs[argminS], 0, 1) + maxS = cddiffs[argmaxS] + + if alternative == 'less' or (alternative == 'two-sided' and minS > maxS): + d = minS + d_location = loc_minS + d_sign = -1 + else: + d = maxS + d_location = loc_maxS + d_sign = 1 + g = gcd(n1, n2) + n1g = n1 // g + n2g = n2 // g + prob = -np.inf + if mode == 'auto': + mode = 'exact' if max(n1, n2) <= MAX_AUTO_N else 'asymp' + elif mode == 'exact': + # If lcm(n1, n2) is too big, switch from exact to asymp + if n1g >= np.iinfo(np.int32).max / n2g: + mode = 'asymp' + warnings.warn( + f"Exact ks_2samp calculation not possible with samples sizes " + f"{n1} and {n2}. Switching to 'asymp'.", RuntimeWarning, + stacklevel=3) + + if mode == 'exact': + success, d, prob = _attempt_exact_2kssamp(n1, n2, g, d, alternative) + if not success: + mode = 'asymp' + warnings.warn(f"ks_2samp: Exact calculation unsuccessful. " + f"Switching to method={mode}.", RuntimeWarning, + stacklevel=3) + + if mode == 'asymp': + # The product n1*n2 is large. Use Smirnov's asymptoptic formula. + # Ensure float to avoid overflow in multiplication + # sorted because the one-sided formula is not symmetric in n1, n2 + m, n = sorted([float(n1), float(n2)], reverse=True) + en = m * n / (m + n) + if alternative == 'two-sided': + prob = distributions.kstwo.sf(d, np.round(en)) + else: + z = np.sqrt(en) * d + # Use Hodges' suggested approximation Eqn 5.3 + # Requires m to be the larger of (n1, n2) + expt = -2 * z**2 - 2 * z * (m + 2*n)/np.sqrt(m*n*(m+n))/3.0 + prob = np.exp(expt) + + prob = np.clip(prob, 0, 1) + # Currently, `d` is a Python float. We want it to be a NumPy type, so + # float64 is appropriate. An enhancement would be for `d` to respect the + # dtype of the input. + return KstestResult(np.float64(d), prob, statistic_location=d_location, + statistic_sign=np.int8(d_sign)) + + +def _parse_kstest_args(data1, data2, args, N): + # kstest allows many different variations of arguments. + # Pull out the parsing into a separate function + # (xvals, yvals, ) # 2sample + # (xvals, cdf function,..) + # (xvals, name of distribution, ...) + # (name of distribution, name of distribution, ...) + + # Returns xvals, yvals, cdf + # where cdf is a cdf function, or None + # and yvals is either an array_like of values, or None + # and xvals is array_like. + rvsfunc, cdf = None, None + if isinstance(data1, str): + rvsfunc = getattr(distributions, data1).rvs + elif callable(data1): + rvsfunc = data1 + + if isinstance(data2, str): + cdf = getattr(distributions, data2).cdf + data2 = None + elif callable(data2): + cdf = data2 + data2 = None + + data1 = np.sort(rvsfunc(*args, size=N) if rvsfunc else data1) + return data1, data2, cdf + + +def _kstest_n_samples(kwargs): + cdf = kwargs['cdf'] + return 1 if (isinstance(cdf, str) or callable(cdf)) else 2 + + +@_axis_nan_policy_factory(_tuple_to_KstestResult, n_samples=_kstest_n_samples, + n_outputs=4, result_to_tuple=_KstestResult_to_tuple) +@_rename_parameter("mode", "method") +def kstest(rvs, cdf, args=(), N=20, alternative='two-sided', method='auto'): + """ + Performs the (one-sample or two-sample) Kolmogorov-Smirnov test for + goodness of fit. + + The one-sample test compares the underlying distribution F(x) of a sample + against a given distribution G(x). The two-sample test compares the + underlying distributions of two independent samples. Both tests are valid + only for continuous distributions. + + Parameters + ---------- + rvs : str, array_like, or callable + If an array, it should be a 1-D array of observations of random + variables. + If a callable, it should be a function to generate random variables; + it is required to have a keyword argument `size`. + If a string, it should be the name of a distribution in `scipy.stats`, + which will be used to generate random variables. + cdf : str, array_like or callable + If array_like, it should be a 1-D array of observations of random + variables, and the two-sample test is performed + (and rvs must be array_like). + If a callable, that callable is used to calculate the cdf. + If a string, it should be the name of a distribution in `scipy.stats`, + which will be used as the cdf function. + args : tuple, sequence, optional + Distribution parameters, used if `rvs` or `cdf` are strings or + callables. + N : int, optional + Sample size if `rvs` is string or callable. Default is 20. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the null and alternative hypotheses. Default is 'two-sided'. + Please see explanations in the Notes below. + method : {'auto', 'exact', 'approx', 'asymp'}, optional + Defines the distribution used for calculating the p-value. + The following options are available (default is 'auto'): + + * 'auto' : selects one of the other options. + * 'exact' : uses the exact distribution of test statistic. + * 'approx' : approximates the two-sided probability with twice the + one-sided probability + * 'asymp': uses asymptotic distribution of test statistic + + Returns + ------- + res: KstestResult + An object containing attributes: + + statistic : float + KS test statistic, either D+, D-, or D (the maximum of the two) + pvalue : float + One-tailed or two-tailed p-value. + statistic_location : float + In a one-sample test, this is the value of `rvs` + corresponding with the KS statistic; i.e., the distance between + the empirical distribution function and the hypothesized cumulative + distribution function is measured at this observation. + + In a two-sample test, this is the value from `rvs` or `cdf` + corresponding with the KS statistic; i.e., the distance between + the empirical distribution functions is measured at this + observation. + statistic_sign : int + In a one-sample test, this is +1 if the KS statistic is the + maximum positive difference between the empirical distribution + function and the hypothesized cumulative distribution function + (D+); it is -1 if the KS statistic is the maximum negative + difference (D-). + + In a two-sample test, this is +1 if the empirical distribution + function of `rvs` exceeds the empirical distribution + function of `cdf` at `statistic_location`, otherwise -1. + + See Also + -------- + ks_1samp, ks_2samp + + Notes + ----- + There are three options for the null and corresponding alternative + hypothesis that can be selected using the `alternative` parameter. + + - `two-sided`: The null hypothesis is that the two distributions are + identical, F(x)=G(x) for all x; the alternative is that they are not + identical. + + - `less`: The null hypothesis is that F(x) >= G(x) for all x; the + alternative is that F(x) < G(x) for at least one x. + + - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the + alternative is that F(x) > G(x) for at least one x. + + Note that the alternative hypotheses describe the *CDFs* of the + underlying distributions, not the observed values. For example, + suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in + x1 tend to be less than those in x2. + + + Examples + -------- + Suppose we wish to test the null hypothesis that a sample is distributed + according to the standard normal. + We choose a confidence level of 95%; that is, we will reject the null + hypothesis in favor of the alternative if the p-value is less than 0.05. + + When testing uniformly distributed data, we would expect the + null hypothesis to be rejected. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng() + >>> stats.kstest(stats.uniform.rvs(size=100, random_state=rng), + ... stats.norm.cdf) + KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23) + + Indeed, the p-value is lower than our threshold of 0.05, so we reject the + null hypothesis in favor of the default "two-sided" alternative: the data + are *not* distributed according to the standard normal. + + When testing random variates from the standard normal distribution, we + expect the data to be consistent with the null hypothesis most of the time. + + >>> x = stats.norm.rvs(size=100, random_state=rng) + >>> stats.kstest(x, stats.norm.cdf) + KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717) + + As expected, the p-value of 0.92 is not below our threshold of 0.05, so + we cannot reject the null hypothesis. + + Suppose, however, that the random variates are distributed according to + a normal distribution that is shifted toward greater values. In this case, + the cumulative density function (CDF) of the underlying distribution tends + to be *less* than the CDF of the standard normal. Therefore, we would + expect the null hypothesis to be rejected with ``alternative='less'``: + + >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng) + >>> stats.kstest(x, stats.norm.cdf, alternative='less') + KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) + + and indeed, with p-value smaller than our threshold, we reject the null + hypothesis in favor of the alternative. + + For convenience, the previous test can be performed using the name of the + distribution as the second argument. + + >>> stats.kstest(x, "norm", alternative='less') + KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) + + The examples above have all been one-sample tests identical to those + performed by `ks_1samp`. Note that `kstest` can also perform two-sample + tests identical to those performed by `ks_2samp`. For example, when two + samples are drawn from the same distribution, we expect the data to be + consistent with the null hypothesis most of the time. + + >>> sample1 = stats.laplace.rvs(size=105, random_state=rng) + >>> sample2 = stats.laplace.rvs(size=95, random_state=rng) + >>> stats.kstest(sample1, sample2) + KstestResult(statistic=0.11779448621553884, pvalue=0.4494256912629795) + + As expected, the p-value of 0.45 is not below our threshold of 0.05, so + we cannot reject the null hypothesis. + + """ + # to not break compatibility with existing code + if alternative == 'two_sided': + alternative = 'two-sided' + if alternative not in ['two-sided', 'greater', 'less']: + raise ValueError("Unexpected alternative %s" % alternative) + xvals, yvals, cdf = _parse_kstest_args(rvs, cdf, args, N) + if cdf: + return ks_1samp(xvals, cdf, args=args, alternative=alternative, + method=method, _no_deco=True) + return ks_2samp(xvals, yvals, alternative=alternative, method=method, + _no_deco=True) + + +def tiecorrect(rankvals): + """Tie correction factor for Mann-Whitney U and Kruskal-Wallis H tests. + + Parameters + ---------- + rankvals : array_like + A 1-D sequence of ranks. Typically this will be the array + returned by `~scipy.stats.rankdata`. + + Returns + ------- + factor : float + Correction factor for U or H. + + See Also + -------- + rankdata : Assign ranks to the data + mannwhitneyu : Mann-Whitney rank test + kruskal : Kruskal-Wallis H test + + References + ---------- + .. [1] Siegel, S. (1956) Nonparametric Statistics for the Behavioral + Sciences. New York: McGraw-Hill. + + Examples + -------- + >>> from scipy.stats import tiecorrect, rankdata + >>> tiecorrect([1, 2.5, 2.5, 4]) + 0.9 + >>> ranks = rankdata([1, 3, 2, 4, 5, 7, 2, 8, 4]) + >>> ranks + array([ 1. , 4. , 2.5, 5.5, 7. , 8. , 2.5, 9. , 5.5]) + >>> tiecorrect(ranks) + 0.9833333333333333 + + """ + arr = np.sort(rankvals) + idx = np.nonzero(np.r_[True, arr[1:] != arr[:-1], True])[0] + cnt = np.diff(idx).astype(np.float64) + + size = np.float64(arr.size) + return 1.0 if size < 2 else 1.0 - (cnt**3 - cnt).sum() / (size**3 - size) + + +RanksumsResult = namedtuple('RanksumsResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(RanksumsResult, n_samples=2) +def ranksums(x, y, alternative='two-sided'): + """Compute the Wilcoxon rank-sum statistic for two samples. + + The Wilcoxon rank-sum test tests the null hypothesis that two sets + of measurements are drawn from the same distribution. The alternative + hypothesis is that values in one sample are more likely to be + larger than the values in the other sample. + + This test should be used to compare two samples from continuous + distributions. It does not handle ties between measurements + in x and y. For tie-handling and an optional continuity correction + see `scipy.stats.mannwhitneyu`. + + Parameters + ---------- + x,y : array_like + The data from the two samples. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. Default is 'two-sided'. + The following options are available: + + * 'two-sided': one of the distributions (underlying `x` or `y`) is + stochastically greater than the other. + * 'less': the distribution underlying `x` is stochastically less + than the distribution underlying `y`. + * 'greater': the distribution underlying `x` is stochastically greater + than the distribution underlying `y`. + + .. versionadded:: 1.7.0 + + Returns + ------- + statistic : float + The test statistic under the large-sample approximation that the + rank sum statistic is normally distributed. + pvalue : float + The p-value of the test. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Wilcoxon_rank-sum_test + + Examples + -------- + We can test the hypothesis that two independent unequal-sized samples are + drawn from the same distribution with computing the Wilcoxon rank-sum + statistic. + + >>> import numpy as np + >>> from scipy.stats import ranksums + >>> rng = np.random.default_rng() + >>> sample1 = rng.uniform(-1, 1, 200) + >>> sample2 = rng.uniform(-0.5, 1.5, 300) # a shifted distribution + >>> ranksums(sample1, sample2) + RanksumsResult(statistic=-7.887059, + pvalue=3.09390448e-15) # may vary + >>> ranksums(sample1, sample2, alternative='less') + RanksumsResult(statistic=-7.750585297581713, + pvalue=4.573497606342543e-15) # may vary + >>> ranksums(sample1, sample2, alternative='greater') + RanksumsResult(statistic=-7.750585297581713, + pvalue=0.9999999999999954) # may vary + + The p-value of less than ``0.05`` indicates that this test rejects the + hypothesis at the 5% significance level. + + """ + x, y = map(np.asarray, (x, y)) + n1 = len(x) + n2 = len(y) + alldata = np.concatenate((x, y)) + ranked = rankdata(alldata) + x = ranked[:n1] + s = np.sum(x, axis=0) + expected = n1 * (n1+n2+1) / 2.0 + z = (s - expected) / np.sqrt(n1*n2*(n1+n2+1)/12.0) + pvalue = _get_pvalue(z, distributions.norm, alternative) + + return RanksumsResult(z[()], pvalue[()]) + + +KruskalResult = namedtuple('KruskalResult', ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(KruskalResult, n_samples=None) +def kruskal(*samples, nan_policy='propagate'): + """Compute the Kruskal-Wallis H-test for independent samples. + + The Kruskal-Wallis H-test tests the null hypothesis that the population + median of all of the groups are equal. It is a non-parametric version of + ANOVA. The test works on 2 or more independent samples, which may have + different sizes. Note that rejecting the null hypothesis does not + indicate which of the groups differs. Post hoc comparisons between + groups are required to determine which groups are different. + + Parameters + ---------- + sample1, sample2, ... : array_like + Two or more arrays with the sample measurements can be given as + arguments. Samples must be one-dimensional. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + statistic : float + The Kruskal-Wallis H statistic, corrected for ties. + pvalue : float + The p-value for the test using the assumption that H has a chi + square distribution. The p-value returned is the survival function of + the chi square distribution evaluated at H. + + See Also + -------- + f_oneway : 1-way ANOVA. + mannwhitneyu : Mann-Whitney rank test on two samples. + friedmanchisquare : Friedman test for repeated measurements. + + Notes + ----- + Due to the assumption that H has a chi square distribution, the number + of samples in each group must not be too small. A typical rule is + that each sample must have at least 5 measurements. + + References + ---------- + .. [1] W. H. Kruskal & W. W. Wallis, "Use of Ranks in + One-Criterion Variance Analysis", Journal of the American Statistical + Association, Vol. 47, Issue 260, pp. 583-621, 1952. + .. [2] https://en.wikipedia.org/wiki/Kruskal-Wallis_one-way_analysis_of_variance + + Examples + -------- + >>> from scipy import stats + >>> x = [1, 3, 5, 7, 9] + >>> y = [2, 4, 6, 8, 10] + >>> stats.kruskal(x, y) + KruskalResult(statistic=0.2727272727272734, pvalue=0.6015081344405895) + + >>> x = [1, 1, 1] + >>> y = [2, 2, 2] + >>> z = [2, 2] + >>> stats.kruskal(x, y, z) + KruskalResult(statistic=7.0, pvalue=0.0301973834223185) + + """ + samples = list(map(np.asarray, samples)) + + num_groups = len(samples) + if num_groups < 2: + raise ValueError("Need at least two groups in stats.kruskal()") + + for sample in samples: + if sample.size == 0: + NaN = _get_nan(*samples) + return KruskalResult(NaN, NaN) + elif sample.ndim != 1: + raise ValueError("Samples must be one-dimensional.") + + n = np.asarray(list(map(len, samples))) + + if nan_policy not in ('propagate', 'raise', 'omit'): + raise ValueError("nan_policy must be 'propagate', 'raise' or 'omit'") + + contains_nan = False + for sample in samples: + cn = _contains_nan(sample, nan_policy) + if cn[0]: + contains_nan = True + break + + if contains_nan and nan_policy == 'omit': + for sample in samples: + sample = ma.masked_invalid(sample) + return mstats_basic.kruskal(*samples) + + if contains_nan and nan_policy == 'propagate': + return KruskalResult(np.nan, np.nan) + + alldata = np.concatenate(samples) + ranked = rankdata(alldata) + ties = tiecorrect(ranked) + if ties == 0: + raise ValueError('All numbers are identical in kruskal') + + # Compute sum^2/n for each group and sum + j = np.insert(np.cumsum(n), 0, 0) + ssbn = 0 + for i in range(num_groups): + ssbn += _square_of_sums(ranked[j[i]:j[i+1]]) / n[i] + + totaln = np.sum(n, dtype=float) + h = 12.0 / (totaln * (totaln + 1)) * ssbn - 3 * (totaln + 1) + df = num_groups - 1 + h /= ties + + return KruskalResult(h, distributions.chi2.sf(h, df)) + + +FriedmanchisquareResult = namedtuple('FriedmanchisquareResult', + ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(FriedmanchisquareResult, n_samples=None, paired=True) +def friedmanchisquare(*samples): + """Compute the Friedman test for repeated samples. + + The Friedman test tests the null hypothesis that repeated samples of + the same individuals have the same distribution. It is often used + to test for consistency among samples obtained in different ways. + For example, if two sampling techniques are used on the same set of + individuals, the Friedman test can be used to determine if the two + sampling techniques are consistent. + + Parameters + ---------- + sample1, sample2, sample3... : array_like + Arrays of observations. All of the arrays must have the same number + of elements. At least three samples must be given. + + Returns + ------- + statistic : float + The test statistic, correcting for ties. + pvalue : float + The associated p-value assuming that the test statistic has a chi + squared distribution. + + Notes + ----- + Due to the assumption that the test statistic has a chi squared + distribution, the p-value is only reliable for n > 10 and more than + 6 repeated samples. + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Friedman_test + .. [2] P. Sprent and N.C. Smeeton, "Applied Nonparametric Statistical + Methods, Third Edition". Chapter 6, Section 6.3.2. + + Examples + -------- + In [2]_, the pulse rate (per minute) of a group of seven students was + measured before exercise, immediately after exercise and 5 minutes + after exercise. Is there evidence to suggest that the pulse rates on + these three occasions are similar? + + We begin by formulating a null hypothesis :math:`H_0`: + + The pulse rates are identical on these three occasions. + + Let's assess the plausibility of this hypothesis with a Friedman test. + + >>> from scipy.stats import friedmanchisquare + >>> before = [72, 96, 88, 92, 74, 76, 82] + >>> immediately_after = [120, 120, 132, 120, 101, 96, 112] + >>> five_min_after = [76, 95, 104, 96, 84, 72, 76] + >>> res = friedmanchisquare(before, immediately_after, five_min_after) + >>> res.statistic + 10.57142857142857 + >>> res.pvalue + 0.005063414171757498 + + Using a significance level of 5%, we would reject the null hypothesis in + favor of the alternative hypothesis: "the pulse rates are different on + these three occasions". + + """ + k = len(samples) + if k < 3: + raise ValueError('At least 3 sets of samples must be given ' + f'for Friedman test, got {k}.') + + n = len(samples[0]) + for i in range(1, k): + if len(samples[i]) != n: + raise ValueError('Unequal N in friedmanchisquare. Aborting.') + + # Rank data + data = np.vstack(samples).T + data = data.astype(float) + for i in range(len(data)): + data[i] = rankdata(data[i]) + + # Handle ties + ties = 0 + for d in data: + replist, repnum = find_repeats(array(d)) + for t in repnum: + ties += t * (t*t - 1) + c = 1 - ties / (k*(k*k - 1)*n) + + ssbn = np.sum(data.sum(axis=0)**2) + chisq = (12.0 / (k*n*(k+1)) * ssbn - 3*n*(k+1)) / c + + return FriedmanchisquareResult(chisq, distributions.chi2.sf(chisq, k - 1)) + + +BrunnerMunzelResult = namedtuple('BrunnerMunzelResult', + ('statistic', 'pvalue')) + + +@_axis_nan_policy_factory(BrunnerMunzelResult, n_samples=2) +def brunnermunzel(x, y, alternative="two-sided", distribution="t", + nan_policy='propagate'): + """Compute the Brunner-Munzel test on samples x and y. + + The Brunner-Munzel test is a nonparametric test of the null hypothesis that + when values are taken one by one from each group, the probabilities of + getting large values in both groups are equal. + Unlike the Wilcoxon-Mann-Whitney's U test, this does not require the + assumption of equivariance of two groups. Note that this does not assume + the distributions are same. This test works on two independent samples, + which may have different sizes. + + Parameters + ---------- + x, y : array_like + Array of samples, should be one-dimensional. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided' + * 'less': one-sided + * 'greater': one-sided + distribution : {'t', 'normal'}, optional + Defines how to get the p-value. + The following options are available (default is 't'): + + * 't': get the p-value by t-distribution + * 'normal': get the p-value by standard normal distribution. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': returns nan + * 'raise': throws an error + * 'omit': performs the calculations ignoring nan values + + Returns + ------- + statistic : float + The Brunner-Munzer W statistic. + pvalue : float + p-value assuming an t distribution. One-sided or + two-sided, depending on the choice of `alternative` and `distribution`. + + See Also + -------- + mannwhitneyu : Mann-Whitney rank test on two samples. + + Notes + ----- + Brunner and Munzel recommended to estimate the p-value by t-distribution + when the size of data is 50 or less. If the size is lower than 10, it would + be better to use permuted Brunner Munzel test (see [2]_). + + References + ---------- + .. [1] Brunner, E. and Munzel, U. "The nonparametric Benhrens-Fisher + problem: Asymptotic theory and a small-sample approximation". + Biometrical Journal. Vol. 42(2000): 17-25. + .. [2] Neubert, K. and Brunner, E. "A studentized permutation test for the + non-parametric Behrens-Fisher problem". Computational Statistics and + Data Analysis. Vol. 51(2007): 5192-5204. + + Examples + -------- + >>> from scipy import stats + >>> x1 = [1,2,1,1,1,1,1,1,1,1,2,4,1,1] + >>> x2 = [3,3,4,3,1,2,3,1,1,5,4] + >>> w, p_value = stats.brunnermunzel(x1, x2) + >>> w + 3.1374674823029505 + >>> p_value + 0.0057862086661515377 + + """ + + nx = len(x) + ny = len(y) + if nx == 0 or ny == 0: + NaN = _get_nan(x, y) + return BrunnerMunzelResult(NaN, NaN) + rankc = rankdata(np.concatenate((x, y))) + rankcx = rankc[0:nx] + rankcy = rankc[nx:nx+ny] + rankcx_mean = np.mean(rankcx) + rankcy_mean = np.mean(rankcy) + rankx = rankdata(x) + ranky = rankdata(y) + rankx_mean = np.mean(rankx) + ranky_mean = np.mean(ranky) + + Sx = np.sum(np.power(rankcx - rankx - rankcx_mean + rankx_mean, 2.0)) + Sx /= nx - 1 + Sy = np.sum(np.power(rankcy - ranky - rankcy_mean + ranky_mean, 2.0)) + Sy /= ny - 1 + + wbfn = nx * ny * (rankcy_mean - rankcx_mean) + wbfn /= (nx + ny) * np.sqrt(nx * Sx + ny * Sy) + + if distribution == "t": + df_numer = np.power(nx * Sx + ny * Sy, 2.0) + df_denom = np.power(nx * Sx, 2.0) / (nx - 1) + df_denom += np.power(ny * Sy, 2.0) / (ny - 1) + df = df_numer / df_denom + + if (df_numer == 0) and (df_denom == 0): + message = ("p-value cannot be estimated with `distribution='t' " + "because degrees of freedom parameter is undefined " + "(0/0). Try using `distribution='normal'") + warnings.warn(message, RuntimeWarning, stacklevel=2) + + distribution = distributions.t(df) + elif distribution == "normal": + distribution = distributions.norm() + else: + raise ValueError( + "distribution should be 't' or 'normal'") + + p = _get_pvalue(-wbfn, distribution, alternative) + + return BrunnerMunzelResult(wbfn, p) + + +@_axis_nan_policy_factory(SignificanceResult, kwd_samples=['weights'], paired=True) +def combine_pvalues(pvalues, method='fisher', weights=None): + """ + Combine p-values from independent tests that bear upon the same hypothesis. + + These methods are intended only for combining p-values from hypothesis + tests based upon continuous distributions. + + Each method assumes that under the null hypothesis, the p-values are + sampled independently and uniformly from the interval [0, 1]. A test + statistic (different for each method) is computed and a combined + p-value is calculated based upon the distribution of this test statistic + under the null hypothesis. + + Parameters + ---------- + pvalues : array_like + Array of p-values assumed to come from independent tests based on + continuous distributions. + method : {'fisher', 'pearson', 'tippett', 'stouffer', 'mudholkar_george'} + + Name of method to use to combine p-values. + + The available methods are (see Notes for details): + + * 'fisher': Fisher's method (Fisher's combined probability test) + * 'pearson': Pearson's method + * 'mudholkar_george': Mudholkar's and George's method + * 'tippett': Tippett's method + * 'stouffer': Stouffer's Z-score method + weights : array_like, optional + Optional array of weights used only for Stouffer's Z-score method. + Ignored by other methods. + + Returns + ------- + res : SignificanceResult + An object containing attributes: + + statistic : float + The statistic calculated by the specified method. + pvalue : float + The combined p-value. + + Examples + -------- + Suppose we wish to combine p-values from four independent tests + of the same null hypothesis using Fisher's method (default). + + >>> from scipy.stats import combine_pvalues + >>> pvalues = [0.1, 0.05, 0.02, 0.3] + >>> combine_pvalues(pvalues) + SignificanceResult(statistic=20.828626352604235, pvalue=0.007616871850449092) + + When the individual p-values carry different weights, consider Stouffer's + method. + + >>> weights = [1, 2, 3, 4] + >>> res = combine_pvalues(pvalues, method='stouffer', weights=weights) + >>> res.pvalue + 0.009578891494533616 + + Notes + ----- + If this function is applied to tests with a discrete statistics such as + any rank test or contingency-table test, it will yield systematically + wrong results, e.g. Fisher's method will systematically overestimate the + p-value [1]_. This problem becomes less severe for large sample sizes + when the discrete distributions become approximately continuous. + + The differences between the methods can be best illustrated by their + statistics and what aspects of a combination of p-values they emphasise + when considering significance [2]_. For example, methods emphasising large + p-values are more sensitive to strong false and true negatives; conversely + methods focussing on small p-values are sensitive to positives. + + * The statistics of Fisher's method (also known as Fisher's combined + probability test) [3]_ is :math:`-2\\sum_i \\log(p_i)`, which is + equivalent (as a test statistics) to the product of individual p-values: + :math:`\\prod_i p_i`. Under the null hypothesis, this statistics follows + a :math:`\\chi^2` distribution. This method emphasises small p-values. + * Pearson's method uses :math:`-2\\sum_i\\log(1-p_i)`, which is equivalent + to :math:`\\prod_i \\frac{1}{1-p_i}` [2]_. + It thus emphasises large p-values. + * Mudholkar and George compromise between Fisher's and Pearson's method by + averaging their statistics [4]_. Their method emphasises extreme + p-values, both close to 1 and 0. + * Stouffer's method [5]_ uses Z-scores and the statistic: + :math:`\\sum_i \\Phi^{-1} (p_i)`, where :math:`\\Phi` is the CDF of the + standard normal distribution. The advantage of this method is that it is + straightforward to introduce weights, which can make Stouffer's method + more powerful than Fisher's method when the p-values are from studies + of different size [6]_ [7]_. + * Tippett's method uses the smallest p-value as a statistic. + (Mind that this minimum is not the combined p-value.) + + Fisher's method may be extended to combine p-values from dependent tests + [8]_. Extensions such as Brown's method and Kost's method are not currently + implemented. + + .. versionadded:: 0.15.0 + + References + ---------- + .. [1] Kincaid, W. M., "The Combination of Tests Based on Discrete + Distributions." Journal of the American Statistical Association 57, + no. 297 (1962), 10-19. + .. [2] Heard, N. and Rubin-Delanchey, P. "Choosing between methods of + combining p-values." Biometrika 105.1 (2018): 239-246. + .. [3] https://en.wikipedia.org/wiki/Fisher%27s_method + .. [4] George, E. O., and G. S. Mudholkar. "On the convolution of logistic + random variables." Metrika 30.1 (1983): 1-13. + .. [5] https://en.wikipedia.org/wiki/Fisher%27s_method#Relation_to_Stouffer.27s_Z-score_method + .. [6] Whitlock, M. C. "Combining probability from independent tests: the + weighted Z-method is superior to Fisher's approach." Journal of + Evolutionary Biology 18, no. 5 (2005): 1368-1373. + .. [7] Zaykin, Dmitri V. "Optimally weighted Z-test is a powerful method + for combining probabilities in meta-analysis." Journal of + Evolutionary Biology 24, no. 8 (2011): 1836-1841. + .. [8] https://en.wikipedia.org/wiki/Extensions_of_Fisher%27s_method + + """ + if pvalues.size == 0: + NaN = _get_nan(pvalues) + return SignificanceResult(NaN, NaN) + + if method == 'fisher': + statistic = -2 * np.sum(np.log(pvalues)) + pval = distributions.chi2.sf(statistic, 2 * len(pvalues)) + elif method == 'pearson': + statistic = 2 * np.sum(np.log1p(-pvalues)) + pval = distributions.chi2.cdf(-statistic, 2 * len(pvalues)) + elif method == 'mudholkar_george': + normalizing_factor = np.sqrt(3/len(pvalues))/np.pi + statistic = -np.sum(np.log(pvalues)) + np.sum(np.log1p(-pvalues)) + nu = 5 * len(pvalues) + 4 + approx_factor = np.sqrt(nu / (nu - 2)) + pval = distributions.t.sf(statistic * normalizing_factor + * approx_factor, nu) + elif method == 'tippett': + statistic = np.min(pvalues) + pval = distributions.beta.cdf(statistic, 1, len(pvalues)) + elif method == 'stouffer': + if weights is None: + weights = np.ones_like(pvalues) + elif len(weights) != len(pvalues): + raise ValueError("pvalues and weights must be of the same size.") + + Zi = distributions.norm.isf(pvalues) + statistic = np.dot(weights, Zi) / np.linalg.norm(weights) + pval = distributions.norm.sf(statistic) + + else: + raise ValueError( + f"Invalid method {method!r}. Valid methods are 'fisher', " + "'pearson', 'mudholkar_george', 'tippett', and 'stouffer'" + ) + + return SignificanceResult(statistic, pval) + + +@dataclass +class QuantileTestResult: + r""" + Result of `scipy.stats.quantile_test`. + + Attributes + ---------- + statistic: float + The statistic used to calculate the p-value; either ``T1``, the + number of observations less than or equal to the hypothesized quantile, + or ``T2``, the number of observations strictly less than the + hypothesized quantile. Two test statistics are required to handle the + possibility the data was generated from a discrete or mixed + distribution. + + statistic_type : int + ``1`` or ``2`` depending on which of ``T1`` or ``T2`` was used to + calculate the p-value respectively. ``T1`` corresponds to the + ``"greater"`` alternative hypothesis and ``T2`` to the ``"less"``. For + the ``"two-sided"`` case, the statistic type that leads to smallest + p-value is used. For significant tests, ``statistic_type = 1`` means + there is evidence that the population quantile is significantly greater + than the hypothesized value and ``statistic_type = 2`` means there is + evidence that it is significantly less than the hypothesized value. + + pvalue : float + The p-value of the hypothesis test. + """ + statistic: float + statistic_type: int + pvalue: float + _alternative: list[str] = field(repr=False) + _x : np.ndarray = field(repr=False) + _p : float = field(repr=False) + + def confidence_interval(self, confidence_level=0.95): + """ + Compute the confidence interval of the quantile. + + Parameters + ---------- + confidence_level : float, default: 0.95 + Confidence level for the computed confidence interval + of the quantile. Default is 0.95. + + Returns + ------- + ci : ``ConfidenceInterval`` object + The object has attributes ``low`` and ``high`` that hold the + lower and upper bounds of the confidence interval. + + Examples + -------- + >>> import numpy as np + >>> import scipy.stats as stats + >>> p = 0.75 # quantile of interest + >>> q = 0 # hypothesized value of the quantile + >>> x = np.exp(np.arange(0, 1.01, 0.01)) + >>> res = stats.quantile_test(x, q=q, p=p, alternative='less') + >>> lb, ub = res.confidence_interval() + >>> lb, ub + (-inf, 2.293318740264183) + >>> res = stats.quantile_test(x, q=q, p=p, alternative='two-sided') + >>> lb, ub = res.confidence_interval(0.9) + >>> lb, ub + (1.9542373206359396, 2.293318740264183) + """ + + alternative = self._alternative + p = self._p + x = np.sort(self._x) + n = len(x) + bd = stats.binom(n, p) + + if confidence_level <= 0 or confidence_level >= 1: + message = "`confidence_level` must be a number between 0 and 1." + raise ValueError(message) + + low_index = np.nan + high_index = np.nan + + if alternative == 'less': + p = 1 - confidence_level + low = -np.inf + high_index = int(bd.isf(p)) + high = x[high_index] if high_index < n else np.nan + elif alternative == 'greater': + p = 1 - confidence_level + low_index = int(bd.ppf(p)) - 1 + low = x[low_index] if low_index >= 0 else np.nan + high = np.inf + elif alternative == 'two-sided': + p = (1 - confidence_level) / 2 + low_index = int(bd.ppf(p)) - 1 + low = x[low_index] if low_index >= 0 else np.nan + high_index = int(bd.isf(p)) + high = x[high_index] if high_index < n else np.nan + + return ConfidenceInterval(low, high) + + +def quantile_test_iv(x, q, p, alternative): + + x = np.atleast_1d(x) + message = '`x` must be a one-dimensional array of numbers.' + if x.ndim != 1 or not np.issubdtype(x.dtype, np.number): + raise ValueError(message) + + q = np.array(q)[()] + message = "`q` must be a scalar." + if q.ndim != 0 or not np.issubdtype(q.dtype, np.number): + raise ValueError(message) + + p = np.array(p)[()] + message = "`p` must be a float strictly between 0 and 1." + if p.ndim != 0 or p >= 1 or p <= 0: + raise ValueError(message) + + alternatives = {'two-sided', 'less', 'greater'} + message = f"`alternative` must be one of {alternatives}" + if alternative not in alternatives: + raise ValueError(message) + + return x, q, p, alternative + + +def quantile_test(x, *, q=0, p=0.5, alternative='two-sided'): + r""" + Perform a quantile test and compute a confidence interval of the quantile. + + This function tests the null hypothesis that `q` is the value of the + quantile associated with probability `p` of the population underlying + sample `x`. For example, with default parameters, it tests that the + median of the population underlying `x` is zero. The function returns an + object including the test statistic, a p-value, and a method for computing + the confidence interval around the quantile. + + Parameters + ---------- + x : array_like + A one-dimensional sample. + q : float, default: 0 + The hypothesized value of the quantile. + p : float, default: 0.5 + The probability associated with the quantile; i.e. the proportion of + the population less than `q` is `p`. Must be strictly between 0 and + 1. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + The following options are available (default is 'two-sided'): + + * 'two-sided': the quantile associated with the probability `p` + is not `q`. + * 'less': the quantile associated with the probability `p` is less + than `q`. + * 'greater': the quantile associated with the probability `p` is + greater than `q`. + + Returns + ------- + result : QuantileTestResult + An object with the following attributes: + + statistic : float + One of two test statistics that may be used in the quantile test. + The first test statistic, ``T1``, is the proportion of samples in + `x` that are less than or equal to the hypothesized quantile + `q`. The second test statistic, ``T2``, is the proportion of + samples in `x` that are strictly less than the hypothesized + quantile `q`. + + When ``alternative = 'greater'``, ``T1`` is used to calculate the + p-value and ``statistic`` is set to ``T1``. + + When ``alternative = 'less'``, ``T2`` is used to calculate the + p-value and ``statistic`` is set to ``T2``. + + When ``alternative = 'two-sided'``, both ``T1`` and ``T2`` are + considered, and the one that leads to the smallest p-value is used. + + statistic_type : int + Either `1` or `2` depending on which of ``T1`` or ``T2`` was + used to calculate the p-value. + + pvalue : float + The p-value associated with the given alternative. + + The object also has the following method: + + confidence_interval(confidence_level=0.95) + Computes a confidence interval around the the + population quantile associated with the probability `p`. The + confidence interval is returned in a ``namedtuple`` with + fields `low` and `high`. Values are `nan` when there are + not enough observations to compute the confidence interval at + the desired confidence. + + Notes + ----- + This test and its method for computing confidence intervals are + non-parametric. They are valid if and only if the observations are i.i.d. + + The implementation of the test follows Conover [1]_. Two test statistics + are considered. + + ``T1``: The number of observations in `x` less than or equal to `q`. + + ``T1 = (x <= q).sum()`` + + ``T2``: The number of observations in `x` strictly less than `q`. + + ``T2 = (x < q).sum()`` + + The use of two test statistics is necessary to handle the possibility that + `x` was generated from a discrete or mixed distribution. + + The null hypothesis for the test is: + + H0: The :math:`p^{\mathrm{th}}` population quantile is `q`. + + and the null distribution for each test statistic is + :math:`\mathrm{binom}\left(n, p\right)`. When ``alternative='less'``, + the alternative hypothesis is: + + H1: The :math:`p^{\mathrm{th}}` population quantile is less than `q`. + + and the p-value is the probability that the binomial random variable + + .. math:: + Y \sim \mathrm{binom}\left(n, p\right) + + is greater than or equal to the observed value ``T2``. + + When ``alternative='greater'``, the alternative hypothesis is: + + H1: The :math:`p^{\mathrm{th}}` population quantile is greater than `q` + + and the p-value is the probability that the binomial random variable Y + is less than or equal to the observed value ``T1``. + + When ``alternative='two-sided'``, the alternative hypothesis is + + H1: `q` is not the :math:`p^{\mathrm{th}}` population quantile. + + and the p-value is twice the smaller of the p-values for the ``'less'`` + and ``'greater'`` cases. Both of these p-values can exceed 0.5 for the same + data, so the value is clipped into the interval :math:`[0, 1]`. + + The approach for confidence intervals is attributed to Thompson [2]_ and + later proven to be applicable to any set of i.i.d. samples [3]_. The + computation is based on the observation that the probability of a quantile + :math:`q` to be larger than any observations :math:`x_m (1\leq m \leq N)` + can be computed as + + .. math:: + + \mathbb{P}(x_m \leq q) = 1 - \sum_{k=0}^{m-1} \binom{N}{k} + q^k(1-q)^{N-k} + + By default, confidence intervals are computed for a 95% confidence level. + A common interpretation of a 95% confidence intervals is that if i.i.d. + samples are drawn repeatedly from the same population and confidence + intervals are formed each time, the confidence interval will contain the + true value of the specified quantile in approximately 95% of trials. + + A similar function is available in the QuantileNPCI R package [4]_. The + foundation is the same, but it computes the confidence interval bounds by + doing interpolations between the sample values, whereas this function uses + only sample values as bounds. Thus, ``quantile_test.confidence_interval`` + returns more conservative intervals (i.e., larger). + + The same computation of confidence intervals for quantiles is included in + the confintr package [5]_. + + Two-sided confidence intervals are not guaranteed to be optimal; i.e., + there may exist a tighter interval that may contain the quantile of + interest with probability larger than the confidence level. + Without further assumption on the samples (e.g., the nature of the + underlying distribution), the one-sided intervals are optimally tight. + + References + ---------- + .. [1] W. J. Conover. Practical Nonparametric Statistics, 3rd Ed. 1999. + .. [2] W. R. Thompson, "On Confidence Ranges for the Median and Other + Expectation Distributions for Populations of Unknown Distribution + Form," The Annals of Mathematical Statistics, vol. 7, no. 3, + pp. 122-128, 1936, Accessed: Sep. 18, 2019. [Online]. Available: + https://www.jstor.org/stable/2957563. + .. [3] H. A. David and H. N. Nagaraja, "Order Statistics in Nonparametric + Inference" in Order Statistics, John Wiley & Sons, Ltd, 2005, pp. + 159-170. Available: + https://onlinelibrary.wiley.com/doi/10.1002/0471722162.ch7. + .. [4] N. Hutson, A. Hutson, L. Yan, "QuantileNPCI: Nonparametric + Confidence Intervals for Quantiles," R package, + https://cran.r-project.org/package=QuantileNPCI + .. [5] M. Mayer, "confintr: Confidence Intervals," R package, + https://cran.r-project.org/package=confintr + + + Examples + -------- + + Suppose we wish to test the null hypothesis that the median of a population + is equal to 0.5. We choose a confidence level of 99%; that is, we will + reject the null hypothesis in favor of the alternative if the p-value is + less than 0.01. + + When testing random variates from the standard uniform distribution, which + has a median of 0.5, we expect the data to be consistent with the null + hypothesis most of the time. + + >>> import numpy as np + >>> from scipy import stats + >>> rng = np.random.default_rng(6981396440634228121) + >>> rvs = stats.uniform.rvs(size=100, random_state=rng) + >>> stats.quantile_test(rvs, q=0.5, p=0.5) + QuantileTestResult(statistic=45, statistic_type=1, pvalue=0.36820161732669576) + + As expected, the p-value is not below our threshold of 0.01, so + we cannot reject the null hypothesis. + + When testing data from the standard *normal* distribution, which has a + median of 0, we would expect the null hypothesis to be rejected. + + >>> rvs = stats.norm.rvs(size=100, random_state=rng) + >>> stats.quantile_test(rvs, q=0.5, p=0.5) + QuantileTestResult(statistic=67, statistic_type=2, pvalue=0.0008737198369123724) + + Indeed, the p-value is lower than our threshold of 0.01, so we reject the + null hypothesis in favor of the default "two-sided" alternative: the median + of the population is *not* equal to 0.5. + + However, suppose we were to test the null hypothesis against the + one-sided alternative that the median of the population is *greater* than + 0.5. Since the median of the standard normal is less than 0.5, we would not + expect the null hypothesis to be rejected. + + >>> stats.quantile_test(rvs, q=0.5, p=0.5, alternative='greater') + QuantileTestResult(statistic=67, statistic_type=1, pvalue=0.9997956114162866) + + Unsurprisingly, with a p-value greater than our threshold, we would not + reject the null hypothesis in favor of the chosen alternative. + + The quantile test can be used for any quantile, not only the median. For + example, we can test whether the third quartile of the distribution + underlying the sample is greater than 0.6. + + >>> rvs = stats.uniform.rvs(size=100, random_state=rng) + >>> stats.quantile_test(rvs, q=0.6, p=0.75, alternative='greater') + QuantileTestResult(statistic=64, statistic_type=1, pvalue=0.00940696592998271) + + The p-value is lower than the threshold. We reject the null hypothesis in + favor of the alternative: the third quartile of the distribution underlying + our sample is greater than 0.6. + + `quantile_test` can also compute confidence intervals for any quantile. + + >>> rvs = stats.norm.rvs(size=100, random_state=rng) + >>> res = stats.quantile_test(rvs, q=0.6, p=0.75) + >>> ci = res.confidence_interval(confidence_level=0.95) + >>> ci + ConfidenceInterval(low=0.284491604437432, high=0.8912531024914844) + + When testing a one-sided alternative, the confidence interval contains + all observations such that if passed as `q`, the p-value of the + test would be greater than 0.05, and therefore the null hypothesis + would not be rejected. For example: + + >>> rvs.sort() + >>> q, p, alpha = 0.6, 0.75, 0.95 + >>> res = stats.quantile_test(rvs, q=q, p=p, alternative='less') + >>> ci = res.confidence_interval(confidence_level=alpha) + >>> for x in rvs[rvs <= ci.high]: + ... res = stats.quantile_test(rvs, q=x, p=p, alternative='less') + ... assert res.pvalue > 1-alpha + >>> for x in rvs[rvs > ci.high]: + ... res = stats.quantile_test(rvs, q=x, p=p, alternative='less') + ... assert res.pvalue < 1-alpha + + Also, if a 95% confidence interval is repeatedly generated for random + samples, the confidence interval will contain the true quantile value in + approximately 95% of replications. + + >>> dist = stats.rayleigh() # our "unknown" distribution + >>> p = 0.2 + >>> true_stat = dist.ppf(p) # the true value of the statistic + >>> n_trials = 1000 + >>> quantile_ci_contains_true_stat = 0 + >>> for i in range(n_trials): + ... data = dist.rvs(size=100, random_state=rng) + ... res = stats.quantile_test(data, p=p) + ... ci = res.confidence_interval(0.95) + ... if ci[0] < true_stat < ci[1]: + ... quantile_ci_contains_true_stat += 1 + >>> quantile_ci_contains_true_stat >= 950 + True + + This works with any distribution and any quantile, as long as the samples + are i.i.d. + """ + # Implementation carefully follows [1] 3.2 + # "H0: the p*th quantile of X is x*" + # To facilitate comparison with [1], we'll use variable names that + # best match Conover's notation + X, x_star, p_star, H1 = quantile_test_iv(x, q, p, alternative) + + # "We will use two test statistics in this test. Let T1 equal " + # "the number of observations less than or equal to x*, and " + # "let T2 equal the number of observations less than x*." + T1 = (X <= x_star).sum() + T2 = (X < x_star).sum() + + # "The null distribution of the test statistics T1 and T2 is " + # "the binomial distribution, with parameters n = sample size, and " + # "p = p* as given in the null hypothesis.... Y has the binomial " + # "distribution with parameters n and p*." + n = len(X) + Y = stats.binom(n=n, p=p_star) + + # "H1: the p* population quantile is less than x*" + if H1 == 'less': + # "The p-value is the probability that a binomial random variable Y " + # "is greater than *or equal to* the observed value of T2...using p=p*" + pvalue = Y.sf(T2-1) # Y.pmf(T2) + Y.sf(T2) + statistic = T2 + statistic_type = 2 + # "H1: the p* population quantile is greater than x*" + elif H1 == 'greater': + # "The p-value is the probability that a binomial random variable Y " + # "is less than or equal to the observed value of T1... using p = p*" + pvalue = Y.cdf(T1) + statistic = T1 + statistic_type = 1 + # "H1: x* is not the p*th population quantile" + elif H1 == 'two-sided': + # "The p-value is twice the smaller of the probabilities that a + # binomial random variable Y is less than or equal to the observed + # value of T1 or greater than or equal to the observed value of T2 + # using p=p*." + # Note: both one-sided p-values can exceed 0.5 for the same data, so + # `clip` + pvalues = [Y.cdf(T1), Y.sf(T2 - 1)] # [greater, less] + sorted_idx = np.argsort(pvalues) + pvalue = np.clip(2*pvalues[sorted_idx[0]], 0, 1) + if sorted_idx[0]: + statistic, statistic_type = T2, 2 + else: + statistic, statistic_type = T1, 1 + + return QuantileTestResult( + statistic=statistic, + statistic_type=statistic_type, + pvalue=pvalue, + _alternative=H1, + _x=X, + _p=p_star + ) + + +##################################### +# STATISTICAL DISTANCES # +##################################### + + +def wasserstein_distance_nd(u_values, v_values, u_weights=None, v_weights=None): + r""" + Compute the Wasserstein-1 distance between two N-D discrete distributions. + + The Wasserstein distance, also called the Earth mover's distance or the + optimal transport distance, is a similarity metric between two probability + distributions [1]_. In the discrete case, the Wasserstein distance can be + understood as the cost of an optimal transport plan to convert one + distribution into the other. The cost is calculated as the product of the + amount of probability mass being moved and the distance it is being moved. + A brief and intuitive introduction can be found at [2]_. + + .. versionadded:: 1.13.0 + + Parameters + ---------- + u_values : 2d array_like + A sample from a probability distribution or the support (set of all + possible values) of a probability distribution. Each element along + axis 0 is an observation or possible value, and axis 1 represents the + dimensionality of the distribution; i.e., each row is a vector + observation or possible value. + + v_values : 2d array_like + A sample from or the support of a second distribution. + + u_weights, v_weights : 1d array_like, optional + Weights or counts corresponding with the sample or probability masses + corresponding with the support values. Sum of elements must be positive + and finite. If unspecified, each value is assigned the same weight. + + Returns + ------- + distance : float + The computed distance between the distributions. + + Notes + ----- + Given two probability mass functions, :math:`u` + and :math:`v`, the first Wasserstein distance between the distributions + using the Euclidean norm is: + + .. math:: + + l_1 (u, v) = \inf_{\pi \in \Gamma (u, v)} \int \| x-y \|_2 \mathrm{d} \pi (x, y) + + where :math:`\Gamma (u, v)` is the set of (probability) distributions on + :math:`\mathbb{R}^n \times \mathbb{R}^n` whose marginals are :math:`u` and + :math:`v` on the first and second factors respectively. For a given value + :math:`x`, :math:`u(x)` gives the probabilty of :math:`u` at position + :math:`x`, and the same for :math:`v(x)`. + + This is also called the optimal transport problem or the Monge problem. + Let the finite point sets :math:`\{x_i\}` and :math:`\{y_j\}` denote + the support set of probability mass function :math:`u` and :math:`v` + respectively. The Monge problem can be expressed as follows, + + Let :math:`\Gamma` denote the transport plan, :math:`D` denote the + distance matrix and, + + .. math:: + + x = \text{vec}(\Gamma) \\ + c = \text{vec}(D) \\ + b = \begin{bmatrix} + u\\ + v\\ + \end{bmatrix} + + The :math:`\text{vec}()` function denotes the Vectorization function + that transforms a matrix into a column vector by vertically stacking + the columns of the matrix. + The tranport plan :math:`\Gamma` is a matrix :math:`[\gamma_{ij}]` in + which :math:`\gamma_{ij}` is a positive value representing the amount of + probability mass transported from :math:`u(x_i)` to :math:`v(y_i)`. + Summing over the rows of :math:`\Gamma` should give the source distribution + :math:`u` : :math:`\sum_j \gamma_{ij} = u(x_i)` holds for all :math:`i` + and summing over the columns of :math:`\Gamma` should give the target + distribution :math:`v`: :math:`\sum_i \gamma_{ij} = v(y_j)` holds for all + :math:`j`. + The distance matrix :math:`D` is a matrix :math:`[d_{ij}]`, in which + :math:`d_{ij} = d(x_i, y_j)`. + + Given :math:`\Gamma`, :math:`D`, :math:`b`, the Monge problem can be + tranformed into a linear programming problem by + taking :math:`A x = b` as constraints and :math:`z = c^T x` as minimization + target (sum of costs) , where matrix :math:`A` has the form + + .. math:: + + \begin{array} {rrrr|rrrr|r|rrrr} + 1 & 1 & \dots & 1 & 0 & 0 & \dots & 0 & \dots & 0 & 0 & \dots & + 0 \cr + 0 & 0 & \dots & 0 & 1 & 1 & \dots & 1 & \dots & 0 & 0 &\dots & + 0 \cr + \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \ddots + & \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \cr + 0 & 0 & \dots & 0 & 0 & 0 & \dots & 0 & \dots & 1 & 1 & \dots & + 1 \cr \hline + + 1 & 0 & \dots & 0 & 1 & 0 & \dots & \dots & \dots & 1 & 0 & \dots & + 0 \cr + 0 & 1 & \dots & 0 & 0 & 1 & \dots & \dots & \dots & 0 & 1 & \dots & + 0 \cr + \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \ddots & + \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \cr + 0 & 0 & \dots & 1 & 0 & 0 & \dots & 1 & \dots & 0 & 0 & \dots & 1 + \end{array} + + By solving the dual form of the above linear programming problem (with + solution :math:`y^*`), the Wasserstein distance :math:`l_1 (u, v)` can + be computed as :math:`b^T y^*`. + + The above solution is inspired by Vincent Herrmann's blog [3]_ . For a + more thorough explanation, see [4]_ . + + The input distributions can be empirical, therefore coming from samples + whose values are effectively inputs of the function, or they can be seen as + generalized functions, in which case they are weighted sums of Dirac delta + functions located at the specified values. + + References + ---------- + .. [1] "Wasserstein metric", + https://en.wikipedia.org/wiki/Wasserstein_metric + .. [2] Lili Weng, "What is Wasserstein distance?", Lil'log, + https://lilianweng.github.io/posts/2017-08-20-gan/#what-is-wasserstein-distance. + .. [3] Hermann, Vincent. "Wasserstein GAN and the Kantorovich-Rubinstein + Duality". https://vincentherrmann.github.io/blog/wasserstein/. + .. [4] Peyré, Gabriel, and Marco Cuturi. "Computational optimal + transport." Center for Research in Economics and Statistics + Working Papers 2017-86 (2017). + + See Also + -------- + wasserstein_distance: Compute the Wasserstein-1 distance between two + 1D discrete distributions. + + Examples + -------- + Compute the Wasserstein distance between two three-dimensional samples, + each with two observations. + + >>> from scipy.stats import wasserstein_distance_nd + >>> wasserstein_distance_nd([[0, 2, 3], [1, 2, 5]], [[3, 2, 3], [4, 2, 5]]) + 3.0 + + Compute the Wasserstein distance between two two-dimensional distributions + with three and two weighted observations, respectively. + + >>> wasserstein_distance_nd([[0, 2.75], [2, 209.3], [0, 0]], + ... [[0.2, 0.322], [4.5, 25.1808]], + ... [0.4, 5.2, 0.114], [0.8, 1.5]) + 174.15840245217169 + """ + m, n = len(u_values), len(v_values) + u_values = asarray(u_values) + v_values = asarray(v_values) + + if u_values.ndim > 2 or v_values.ndim > 2: + raise ValueError('Invalid input values. The inputs must have either ' + 'one or two dimensions.') + # if dimensions are not equal throw error + if u_values.ndim != v_values.ndim: + raise ValueError('Invalid input values. Dimensions of inputs must be ' + 'equal.') + # if data is 1D then call the cdf_distance function + if u_values.ndim == 1 and v_values.ndim == 1: + return _cdf_distance(1, u_values, v_values, u_weights, v_weights) + + u_values, u_weights = _validate_distribution(u_values, u_weights) + v_values, v_weights = _validate_distribution(v_values, v_weights) + # if number of columns is not equal throw error + if u_values.shape[1] != v_values.shape[1]: + raise ValueError('Invalid input values. If two-dimensional, ' + '`u_values` and `v_values` must have the same ' + 'number of columns.') + + # if data contains np.inf then return inf or nan + if np.any(np.isinf(u_values)) ^ np.any(np.isinf(v_values)): + return np.inf + elif np.any(np.isinf(u_values)) and np.any(np.isinf(v_values)): + return np.nan + + # create constraints + A_upper_part = sparse.block_diag((np.ones((1, n)), ) * m) + A_lower_part = sparse.hstack((sparse.eye(n), ) * m) + # sparse constraint matrix of size (m + n)*(m * n) + A = sparse.vstack((A_upper_part, A_lower_part)) + A = sparse.coo_array(A) + + # get cost matrix + D = distance_matrix(u_values, v_values, p=2) + cost = D.ravel() + + # create the minimization target + p_u = np.full(m, 1/m) if u_weights is None else u_weights/np.sum(u_weights) + p_v = np.full(n, 1/n) if v_weights is None else v_weights/np.sum(v_weights) + b = np.concatenate((p_u, p_v), axis=0) + + # solving LP + constraints = LinearConstraint(A=A.T, ub=cost) + opt_res = milp(c=-b, constraints=constraints, bounds=(-np.inf, np.inf)) + return -opt_res.fun + + +def wasserstein_distance(u_values, v_values, u_weights=None, v_weights=None): + r""" + Compute the Wasserstein-1 distance between two 1D discrete distributions. + + The Wasserstein distance, also called the Earth mover's distance or the + optimal transport distance, is a similarity metric between two probability + distributions [1]_. In the discrete case, the Wasserstein distance can be + understood as the cost of an optimal transport plan to convert one + distribution into the other. The cost is calculated as the product of the + amount of probability mass being moved and the distance it is being moved. + A brief and intuitive introduction can be found at [2]_. + + .. versionadded:: 1.0.0 + + Parameters + ---------- + u_values : 1d array_like + A sample from a probability distribution or the support (set of all + possible values) of a probability distribution. Each element is an + observation or possible value. + + v_values : 1d array_like + A sample from or the support of a second distribution. + + u_weights, v_weights : 1d array_like, optional + Weights or counts corresponding with the sample or probability masses + corresponding with the support values. Sum of elements must be positive + and finite. If unspecified, each value is assigned the same weight. + + Returns + ------- + distance : float + The computed distance between the distributions. + + Notes + ----- + Given two 1D probability mass functions, :math:`u` and :math:`v`, the first + Wasserstein distance between the distributions is: + + .. math:: + + l_1 (u, v) = \inf_{\pi \in \Gamma (u, v)} \int_{\mathbb{R} \times + \mathbb{R}} |x-y| \mathrm{d} \pi (x, y) + + where :math:`\Gamma (u, v)` is the set of (probability) distributions on + :math:`\mathbb{R} \times \mathbb{R}` whose marginals are :math:`u` and + :math:`v` on the first and second factors respectively. For a given value + :math:`x`, :math:`u(x)` gives the probabilty of :math:`u` at position + :math:`x`, and the same for :math:`v(x)`. + + If :math:`U` and :math:`V` are the respective CDFs of :math:`u` and + :math:`v`, this distance also equals to: + + .. math:: + + l_1(u, v) = \int_{-\infty}^{+\infty} |U-V| + + See [3]_ for a proof of the equivalence of both definitions. + + The input distributions can be empirical, therefore coming from samples + whose values are effectively inputs of the function, or they can be seen as + generalized functions, in which case they are weighted sums of Dirac delta + functions located at the specified values. + + References + ---------- + .. [1] "Wasserstein metric", https://en.wikipedia.org/wiki/Wasserstein_metric + .. [2] Lili Weng, "What is Wasserstein distance?", Lil'log, + https://lilianweng.github.io/posts/2017-08-20-gan/#what-is-wasserstein-distance. + .. [3] Ramdas, Garcia, Cuturi "On Wasserstein Two Sample Testing and Related + Families of Nonparametric Tests" (2015). :arXiv:`1509.02237`. + + See Also + -------- + wasserstein_distance_nd: Compute the Wasserstein-1 distance between two N-D + discrete distributions. + + Examples + -------- + >>> from scipy.stats import wasserstein_distance + >>> wasserstein_distance([0, 1, 3], [5, 6, 8]) + 5.0 + >>> wasserstein_distance([0, 1], [0, 1], [3, 1], [2, 2]) + 0.25 + >>> wasserstein_distance([3.4, 3.9, 7.5, 7.8], [4.5, 1.4], + ... [1.4, 0.9, 3.1, 7.2], [3.2, 3.5]) + 4.0781331438047861 + + """ + return _cdf_distance(1, u_values, v_values, u_weights, v_weights) + + +def energy_distance(u_values, v_values, u_weights=None, v_weights=None): + r"""Compute the energy distance between two 1D distributions. + + .. versionadded:: 1.0.0 + + Parameters + ---------- + u_values, v_values : array_like + Values observed in the (empirical) distribution. + u_weights, v_weights : array_like, optional + Weight for each value. If unspecified, each value is assigned the same + weight. + `u_weights` (resp. `v_weights`) must have the same length as + `u_values` (resp. `v_values`). If the weight sum differs from 1, it + must still be positive and finite so that the weights can be normalized + to sum to 1. + + Returns + ------- + distance : float + The computed distance between the distributions. + + Notes + ----- + The energy distance between two distributions :math:`u` and :math:`v`, whose + respective CDFs are :math:`U` and :math:`V`, equals to: + + .. math:: + + D(u, v) = \left( 2\mathbb E|X - Y| - \mathbb E|X - X'| - + \mathbb E|Y - Y'| \right)^{1/2} + + where :math:`X` and :math:`X'` (resp. :math:`Y` and :math:`Y'`) are + independent random variables whose probability distribution is :math:`u` + (resp. :math:`v`). + + Sometimes the square of this quantity is referred to as the "energy + distance" (e.g. in [2]_, [4]_), but as noted in [1]_ and [3]_, only the + definition above satisfies the axioms of a distance function (metric). + + As shown in [2]_, for one-dimensional real-valued variables, the energy + distance is linked to the non-distribution-free version of the Cramér-von + Mises distance: + + .. math:: + + D(u, v) = \sqrt{2} l_2(u, v) = \left( 2 \int_{-\infty}^{+\infty} (U-V)^2 + \right)^{1/2} + + Note that the common Cramér-von Mises criterion uses the distribution-free + version of the distance. See [2]_ (section 2), for more details about both + versions of the distance. + + The input distributions can be empirical, therefore coming from samples + whose values are effectively inputs of the function, or they can be seen as + generalized functions, in which case they are weighted sums of Dirac delta + functions located at the specified values. + + References + ---------- + .. [1] Rizzo, Szekely "Energy distance." Wiley Interdisciplinary Reviews: + Computational Statistics, 8(1):27-38 (2015). + .. [2] Szekely "E-statistics: The energy of statistical samples." Bowling + Green State University, Department of Mathematics and Statistics, + Technical Report 02-16 (2002). + .. [3] "Energy distance", https://en.wikipedia.org/wiki/Energy_distance + .. [4] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer, + Munos "The Cramer Distance as a Solution to Biased Wasserstein + Gradients" (2017). :arXiv:`1705.10743`. + + Examples + -------- + >>> from scipy.stats import energy_distance + >>> energy_distance([0], [2]) + 2.0000000000000004 + >>> energy_distance([0, 8], [0, 8], [3, 1], [2, 2]) + 1.0000000000000002 + >>> energy_distance([0.7, 7.4, 2.4, 6.8], [1.4, 8. ], + ... [2.1, 4.2, 7.4, 8. ], [7.6, 8.8]) + 0.88003340976158217 + + """ + return np.sqrt(2) * _cdf_distance(2, u_values, v_values, + u_weights, v_weights) + + +def _cdf_distance(p, u_values, v_values, u_weights=None, v_weights=None): + r""" + Compute, between two one-dimensional distributions :math:`u` and + :math:`v`, whose respective CDFs are :math:`U` and :math:`V`, the + statistical distance that is defined as: + + .. math:: + + l_p(u, v) = \left( \int_{-\infty}^{+\infty} |U-V|^p \right)^{1/p} + + p is a positive parameter; p = 1 gives the Wasserstein distance, p = 2 + gives the energy distance. + + Parameters + ---------- + u_values, v_values : array_like + Values observed in the (empirical) distribution. + u_weights, v_weights : array_like, optional + Weight for each value. If unspecified, each value is assigned the same + weight. + `u_weights` (resp. `v_weights`) must have the same length as + `u_values` (resp. `v_values`). If the weight sum differs from 1, it + must still be positive and finite so that the weights can be normalized + to sum to 1. + + Returns + ------- + distance : float + The computed distance between the distributions. + + Notes + ----- + The input distributions can be empirical, therefore coming from samples + whose values are effectively inputs of the function, or they can be seen as + generalized functions, in which case they are weighted sums of Dirac delta + functions located at the specified values. + + References + ---------- + .. [1] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer, + Munos "The Cramer Distance as a Solution to Biased Wasserstein + Gradients" (2017). :arXiv:`1705.10743`. + + """ + u_values, u_weights = _validate_distribution(u_values, u_weights) + v_values, v_weights = _validate_distribution(v_values, v_weights) + + u_sorter = np.argsort(u_values) + v_sorter = np.argsort(v_values) + + all_values = np.concatenate((u_values, v_values)) + all_values.sort(kind='mergesort') + + # Compute the differences between pairs of successive values of u and v. + deltas = np.diff(all_values) + + # Get the respective positions of the values of u and v among the values of + # both distributions. + u_cdf_indices = u_values[u_sorter].searchsorted(all_values[:-1], 'right') + v_cdf_indices = v_values[v_sorter].searchsorted(all_values[:-1], 'right') + + # Calculate the CDFs of u and v using their weights, if specified. + if u_weights is None: + u_cdf = u_cdf_indices / u_values.size + else: + u_sorted_cumweights = np.concatenate(([0], + np.cumsum(u_weights[u_sorter]))) + u_cdf = u_sorted_cumweights[u_cdf_indices] / u_sorted_cumweights[-1] + + if v_weights is None: + v_cdf = v_cdf_indices / v_values.size + else: + v_sorted_cumweights = np.concatenate(([0], + np.cumsum(v_weights[v_sorter]))) + v_cdf = v_sorted_cumweights[v_cdf_indices] / v_sorted_cumweights[-1] + + # Compute the value of the integral based on the CDFs. + # If p = 1 or p = 2, we avoid using np.power, which introduces an overhead + # of about 15%. + if p == 1: + return np.sum(np.multiply(np.abs(u_cdf - v_cdf), deltas)) + if p == 2: + return np.sqrt(np.sum(np.multiply(np.square(u_cdf - v_cdf), deltas))) + return np.power(np.sum(np.multiply(np.power(np.abs(u_cdf - v_cdf), p), + deltas)), 1/p) + + +def _validate_distribution(values, weights): + """ + Validate the values and weights from a distribution input of `cdf_distance` + and return them as ndarray objects. + + Parameters + ---------- + values : array_like + Values observed in the (empirical) distribution. + weights : array_like + Weight for each value. + + Returns + ------- + values : ndarray + Values as ndarray. + weights : ndarray + Weights as ndarray. + + """ + # Validate the value array. + values = np.asarray(values, dtype=float) + if len(values) == 0: + raise ValueError("Distribution can't be empty.") + + # Validate the weight array, if specified. + if weights is not None: + weights = np.asarray(weights, dtype=float) + if len(weights) != len(values): + raise ValueError('Value and weight array-likes for the same ' + 'empirical distribution must be of the same size.') + if np.any(weights < 0): + raise ValueError('All weights must be non-negative.') + if not 0 < np.sum(weights) < np.inf: + raise ValueError('Weight array-like sum must be positive and ' + 'finite. Set as None for an equal distribution of ' + 'weight.') + + return values, weights + + return values, None + + +##################################### +# SUPPORT FUNCTIONS # +##################################### + +RepeatedResults = namedtuple('RepeatedResults', ('values', 'counts')) + + +def find_repeats(arr): + """Find repeats and repeat counts. + + Parameters + ---------- + arr : array_like + Input array. This is cast to float64. + + Returns + ------- + values : ndarray + The unique values from the (flattened) input that are repeated. + + counts : ndarray + Number of times the corresponding 'value' is repeated. + + Notes + ----- + In numpy >= 1.9 `numpy.unique` provides similar functionality. The main + difference is that `find_repeats` only returns repeated values. + + Examples + -------- + >>> from scipy import stats + >>> stats.find_repeats([2, 1, 2, 3, 2, 2, 5]) + RepeatedResults(values=array([2.]), counts=array([4])) + + >>> stats.find_repeats([[10, 20, 1, 2], [5, 5, 4, 4]]) + RepeatedResults(values=array([4., 5.]), counts=array([2, 2])) + + """ + # Note: always copies. + return RepeatedResults(*_find_repeats(np.array(arr, dtype=np.float64))) + + +def _sum_of_squares(a, axis=0): + """Square each element of the input array, and return the sum(s) of that. + + Parameters + ---------- + a : array_like + Input array. + axis : int or None, optional + Axis along which to calculate. Default is 0. If None, compute over + the whole array `a`. + + Returns + ------- + sum_of_squares : ndarray + The sum along the given axis for (a**2). + + See Also + -------- + _square_of_sums : The square(s) of the sum(s) (the opposite of + `_sum_of_squares`). + + """ + a, axis = _chk_asarray(a, axis) + return np.sum(a*a, axis) + + +def _square_of_sums(a, axis=0): + """Sum elements of the input array, and return the square(s) of that sum. + + Parameters + ---------- + a : array_like + Input array. + axis : int or None, optional + Axis along which to calculate. Default is 0. If None, compute over + the whole array `a`. + + Returns + ------- + square_of_sums : float or ndarray + The square of the sum over `axis`. + + See Also + -------- + _sum_of_squares : The sum of squares (the opposite of `square_of_sums`). + + """ + a, axis = _chk_asarray(a, axis) + s = np.sum(a, axis) + if not np.isscalar(s): + return s.astype(float) * s + else: + return float(s) * s + + +def rankdata(a, method='average', *, axis=None, nan_policy='propagate'): + """Assign ranks to data, dealing with ties appropriately. + + By default (``axis=None``), the data array is first flattened, and a flat + array of ranks is returned. Separately reshape the rank array to the + shape of the data array if desired (see Examples). + + Ranks begin at 1. The `method` argument controls how ranks are assigned + to equal values. See [1]_ for further discussion of ranking methods. + + Parameters + ---------- + a : array_like + The array of values to be ranked. + method : {'average', 'min', 'max', 'dense', 'ordinal'}, optional + The method used to assign ranks to tied elements. + The following methods are available (default is 'average'): + + * 'average': The average of the ranks that would have been assigned to + all the tied values is assigned to each value. + * 'min': The minimum of the ranks that would have been assigned to all + the tied values is assigned to each value. (This is also + referred to as "competition" ranking.) + * 'max': The maximum of the ranks that would have been assigned to all + the tied values is assigned to each value. + * 'dense': Like 'min', but the rank of the next highest element is + assigned the rank immediately after those assigned to the tied + elements. + * 'ordinal': All values are given a distinct rank, corresponding to + the order that the values occur in `a`. + axis : {None, int}, optional + Axis along which to perform the ranking. If ``None``, the data array + is first flattened. + nan_policy : {'propagate', 'omit', 'raise'}, optional + Defines how to handle when input contains nan. + The following options are available (default is 'propagate'): + + * 'propagate': propagates nans through the rank calculation + * 'omit': performs the calculations ignoring nan values + * 'raise': raises an error + + .. note:: + + When `nan_policy` is 'propagate', the output is an array of *all* + nans because ranks relative to nans in the input are undefined. + When `nan_policy` is 'omit', nans in `a` are ignored when ranking + the other values, and the corresponding locations of the output + are nan. + + .. versionadded:: 1.10 + + Returns + ------- + ranks : ndarray + An array of size equal to the size of `a`, containing rank + scores. + + References + ---------- + .. [1] "Ranking", https://en.wikipedia.org/wiki/Ranking + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import rankdata + >>> rankdata([0, 2, 3, 2]) + array([ 1. , 2.5, 4. , 2.5]) + >>> rankdata([0, 2, 3, 2], method='min') + array([ 1, 2, 4, 2]) + >>> rankdata([0, 2, 3, 2], method='max') + array([ 1, 3, 4, 3]) + >>> rankdata([0, 2, 3, 2], method='dense') + array([ 1, 2, 3, 2]) + >>> rankdata([0, 2, 3, 2], method='ordinal') + array([ 1, 2, 4, 3]) + >>> rankdata([[0, 2], [3, 2]]).reshape(2,2) + array([[1. , 2.5], + [4. , 2.5]]) + >>> rankdata([[0, 2, 2], [3, 2, 5]], axis=1) + array([[1. , 2.5, 2.5], + [2. , 1. , 3. ]]) + >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="propagate") + array([nan, nan, nan, nan, nan, nan]) + >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="omit") + array([ 2., 3., 4., nan, 1., nan]) + + """ + methods = ('average', 'min', 'max', 'dense', 'ordinal') + if method not in methods: + raise ValueError(f'unknown method "{method}"') + + x = np.asarray(a) + + if axis is None: + x = x.ravel() + axis = -1 + + if x.size == 0: + dtype = float if method == 'average' else np.dtype("long") + return np.empty(x.shape, dtype=dtype) + + contains_nan, nan_policy = _contains_nan(x, nan_policy) + + x = np.swapaxes(x, axis, -1) + ranks = _rankdata(x, method) + + if contains_nan: + i_nan = (np.isnan(x) if nan_policy == 'omit' + else np.isnan(x).any(axis=-1)) + ranks = ranks.astype(float, copy=False) + ranks[i_nan] = np.nan + + ranks = np.swapaxes(ranks, axis, -1) + return ranks + + +def _order_ranks(ranks, j): + # Reorder ascending order `ranks` according to `j` + ordered_ranks = np.empty(j.shape, dtype=ranks.dtype) + np.put_along_axis(ordered_ranks, j, ranks, axis=-1) + return ordered_ranks + + +def _rankdata(x, method, return_ties=False): + # Rank data `x` by desired `method`; `return_ties` if desired + shape = x.shape + + # Get sort order + kind = 'mergesort' if method == 'ordinal' else 'quicksort' + j = np.argsort(x, axis=-1, kind=kind) + ordinal_ranks = np.broadcast_to(np.arange(1, shape[-1]+1, dtype=int), shape) + + # Ordinal ranks is very easy because ties don't matter. We're done. + if method == 'ordinal': + return _order_ranks(ordinal_ranks, j) # never return ties + + # Sort array + y = np.take_along_axis(x, j, axis=-1) + # Logical indices of unique elements + i = np.concatenate([np.ones(shape[:-1] + (1,), dtype=np.bool_), + y[..., :-1] != y[..., 1:]], axis=-1) + + # Integer indices of unique elements + indices = np.arange(y.size)[i.ravel()] + # Counts of unique elements + counts = np.diff(indices, append=y.size) + + # Compute `'min'`, `'max'`, and `'mid'` ranks of unique elements + if method == 'min': + ranks = ordinal_ranks[i] + elif method == 'max': + ranks = ordinal_ranks[i] + counts - 1 + elif method == 'average': + ranks = ordinal_ranks[i] + (counts - 1)/2 + elif method == 'dense': + ranks = np.cumsum(i, axis=-1)[i] + + ranks = np.repeat(ranks, counts).reshape(shape) + ranks = _order_ranks(ranks, j) + + if return_ties: + # Tie information is returned in a format that is useful to functions that + # rely on this (private) function. Example: + # >>> x = np.asarray([3, 2, 1, 2, 2, 2, 1]) + # >>> _, t = _rankdata(x, 'average', return_ties=True) + # >>> t # array([2., 0., 4., 0., 0., 0., 1.]) # two 1s, four 2s, and one 3 + # Unlike ranks, tie counts are *not* reordered to correspond with the order of + # the input; e.g. the number of appearances of the lowest rank element comes + # first. This is a useful format because: + # - The shape of the result is the shape of the input. Different slices can + # have different numbers of tied elements but not result in a ragged array. + # - Functions that use `t` usually don't need to which each element of the + # original array is associated with each tie count; they perform a reduction + # over the tie counts onnly. The tie counts are naturally computed in a + # sorted order, so this does not unnecesarily reorder them. + # - One exception is `wilcoxon`, which needs the number of zeros. Zeros always + # have the lowest rank, so it is easy to find them at the zeroth index. + t = np.zeros(shape, dtype=float) + t[i] = counts + return ranks, t + return ranks + + +def expectile(a, alpha=0.5, *, weights=None): + r"""Compute the expectile at the specified level. + + Expectiles are a generalization of the expectation in the same way as + quantiles are a generalization of the median. The expectile at level + `alpha = 0.5` is the mean (average). See Notes for more details. + + Parameters + ---------- + a : array_like + Array containing numbers whose expectile is desired. + alpha : float, default: 0.5 + The level of the expectile; e.g., `alpha=0.5` gives the mean. + weights : array_like, optional + An array of weights associated with the values in `a`. + The `weights` must be broadcastable to the same shape as `a`. + Default is None, which gives each value a weight of 1.0. + An integer valued weight element acts like repeating the corresponding + observation in `a` that many times. See Notes for more details. + + Returns + ------- + expectile : ndarray + The empirical expectile at level `alpha`. + + See Also + -------- + numpy.mean : Arithmetic average + numpy.quantile : Quantile + + Notes + ----- + In general, the expectile at level :math:`\alpha` of a random variable + :math:`X` with cumulative distribution function (CDF) :math:`F` is given + by the unique solution :math:`t` of: + + .. math:: + + \alpha E((X - t)_+) = (1 - \alpha) E((t - X)_+) \,. + + Here, :math:`(x)_+ = \max(0, x)` is the positive part of :math:`x`. + This equation can be equivalently written as: + + .. math:: + + \alpha \int_t^\infty (x - t)\mathrm{d}F(x) + = (1 - \alpha) \int_{-\infty}^t (t - x)\mathrm{d}F(x) \,. + + The empirical expectile at level :math:`\alpha` (`alpha`) of a sample + :math:`a_i` (the array `a`) is defined by plugging in the empirical CDF of + `a`. Given sample or case weights :math:`w` (the array `weights`), it + reads :math:`F_a(x) = \frac{1}{\sum_i w_i} \sum_i w_i 1_{a_i \leq x}` + with indicator function :math:`1_{A}`. This leads to the definition of the + empirical expectile at level `alpha` as the unique solution :math:`t` of: + + .. math:: + + \alpha \sum_{i=1}^n w_i (a_i - t)_+ = + (1 - \alpha) \sum_{i=1}^n w_i (t - a_i)_+ \,. + + For :math:`\alpha=0.5`, this simplifies to the weighted average. + Furthermore, the larger :math:`\alpha`, the larger the value of the + expectile. + + As a final remark, the expectile at level :math:`\alpha` can also be + written as a minimization problem. One often used choice is + + .. math:: + + \operatorname{argmin}_t + E(\lvert 1_{t\geq X} - \alpha\rvert(t - X)^2) \,. + + References + ---------- + .. [1] W. K. Newey and J. L. Powell (1987), "Asymmetric Least Squares + Estimation and Testing," Econometrica, 55, 819-847. + .. [2] T. Gneiting (2009). "Making and Evaluating Point Forecasts," + Journal of the American Statistical Association, 106, 746 - 762. + :doi:`10.48550/arXiv.0912.0902` + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import expectile + >>> a = [1, 4, 2, -1] + >>> expectile(a, alpha=0.5) == np.mean(a) + True + >>> expectile(a, alpha=0.2) + 0.42857142857142855 + >>> expectile(a, alpha=0.8) + 2.5714285714285716 + >>> weights = [1, 3, 1, 1] + + """ + if alpha < 0 or alpha > 1: + raise ValueError( + "The expectile level alpha must be in the range [0, 1]." + ) + a = np.asarray(a) + + if weights is not None: + weights = np.broadcast_to(weights, a.shape) + + # This is the empirical equivalent of Eq. (13) with identification + # function from Table 9 (omitting a factor of 2) in [2] (their y is our + # data a, their x is our t) + def first_order(t): + return np.average(np.abs((a <= t) - alpha) * (t - a), weights=weights) + + if alpha >= 0.5: + x0 = np.average(a, weights=weights) + x1 = np.amax(a) + else: + x1 = np.average(a, weights=weights) + x0 = np.amin(a) + + if x0 == x1: + # a has a single unique element + return x0 + + # Note that the expectile is the unique solution, so no worries about + # finding a wrong root. + res = root_scalar(first_order, x0=x0, x1=x1) + return res.root diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_survival.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_survival.py new file mode 100644 index 0000000000000000000000000000000000000000..82aad05ab8e052e65f58059338761ae047279b7e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_survival.py @@ -0,0 +1,686 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING +import warnings + +import numpy as np +from scipy import special, interpolate, stats +from scipy.stats._censored_data import CensoredData +from scipy.stats._common import ConfidenceInterval +from scipy.stats import norm # type: ignore[attr-defined] + +if TYPE_CHECKING: + from typing import Literal + import numpy.typing as npt + + +__all__ = ['ecdf', 'logrank'] + + +@dataclass +class EmpiricalDistributionFunction: + """An empirical distribution function produced by `scipy.stats.ecdf` + + Attributes + ---------- + quantiles : ndarray + The unique values of the sample from which the + `EmpiricalDistributionFunction` was estimated. + probabilities : ndarray + The point estimates of the cumulative distribution function (CDF) or + its complement, the survival function (SF), corresponding with + `quantiles`. + """ + quantiles: np.ndarray + probabilities: np.ndarray + # Exclude these from __str__ + _n: np.ndarray = field(repr=False) # number "at risk" + _d: np.ndarray = field(repr=False) # number of "deaths" + _sf: np.ndarray = field(repr=False) # survival function for var estimate + _kind: str = field(repr=False) # type of function: "cdf" or "sf" + + def __init__(self, q, p, n, d, kind): + self.probabilities = p + self.quantiles = q + self._n = n + self._d = d + self._sf = p if kind == 'sf' else 1 - p + self._kind = kind + + f0 = 1 if kind == 'sf' else 0 # leftmost function value + f1 = 1 - f0 + # fill_value can't handle edge cases at infinity + x = np.insert(q, [0, len(q)], [-np.inf, np.inf]) + y = np.insert(p, [0, len(p)], [f0, f1]) + # `or` conditions handle the case of empty x, points + self._f = interpolate.interp1d(x, y, kind='previous', + assume_sorted=True) + + def evaluate(self, x): + """Evaluate the empirical CDF/SF function at the input. + + Parameters + ---------- + x : ndarray + Argument to the CDF/SF + + Returns + ------- + y : ndarray + The CDF/SF evaluated at the input + """ + return self._f(x) + + def plot(self, ax=None, **matplotlib_kwargs): + """Plot the empirical distribution function + + Available only if ``matplotlib`` is installed. + + Parameters + ---------- + ax : matplotlib.axes.Axes + Axes object to draw the plot onto, otherwise uses the current Axes. + + **matplotlib_kwargs : dict, optional + Keyword arguments passed directly to `matplotlib.axes.Axes.step`. + Unless overridden, ``where='post'``. + + Returns + ------- + lines : list of `matplotlib.lines.Line2D` + Objects representing the plotted data + """ + try: + import matplotlib # noqa: F401 + except ModuleNotFoundError as exc: + message = "matplotlib must be installed to use method `plot`." + raise ModuleNotFoundError(message) from exc + + if ax is None: + import matplotlib.pyplot as plt + ax = plt.gca() + + kwargs = {'where': 'post'} + kwargs.update(matplotlib_kwargs) + + delta = np.ptp(self.quantiles)*0.05 # how far past sample edge to plot + q = self.quantiles + q = [q[0] - delta] + list(q) + [q[-1] + delta] + + return ax.step(q, self.evaluate(q), **kwargs) + + def confidence_interval(self, confidence_level=0.95, *, method='linear'): + """Compute a confidence interval around the CDF/SF point estimate + + Parameters + ---------- + confidence_level : float, default: 0.95 + Confidence level for the computed confidence interval + + method : str, {"linear", "log-log"} + Method used to compute the confidence interval. Options are + "linear" for the conventional Greenwood confidence interval + (default) and "log-log" for the "exponential Greenwood", + log-negative-log-transformed confidence interval. + + Returns + ------- + ci : ``ConfidenceInterval`` + An object with attributes ``low`` and ``high``, instances of + `~scipy.stats._result_classes.EmpiricalDistributionFunction` that + represent the lower and upper bounds (respectively) of the + confidence interval. + + Notes + ----- + Confidence intervals are computed according to the Greenwood formula + (``method='linear'``) or the more recent "exponential Greenwood" + formula (``method='log-log'``) as described in [1]_. The conventional + Greenwood formula can result in lower confidence limits less than 0 + and upper confidence limits greater than 1; these are clipped to the + unit interval. NaNs may be produced by either method; these are + features of the formulas. + + References + ---------- + .. [1] Sawyer, Stanley. "The Greenwood and Exponential Greenwood + Confidence Intervals in Survival Analysis." + https://www.math.wustl.edu/~sawyer/handouts/greenwood.pdf + + """ + message = ("Confidence interval bounds do not implement a " + "`confidence_interval` method.") + if self._n is None: + raise NotImplementedError(message) + + methods = {'linear': self._linear_ci, + 'log-log': self._loglog_ci} + + message = f"`method` must be one of {set(methods)}." + if method.lower() not in methods: + raise ValueError(message) + + message = "`confidence_level` must be a scalar between 0 and 1." + confidence_level = np.asarray(confidence_level)[()] + if confidence_level.shape or not (0 <= confidence_level <= 1): + raise ValueError(message) + + method_fun = methods[method.lower()] + low, high = method_fun(confidence_level) + + message = ("The confidence interval is undefined at some observations." + " This is a feature of the mathematical formula used, not" + " an error in its implementation.") + if np.any(np.isnan(low) | np.isnan(high)): + warnings.warn(message, RuntimeWarning, stacklevel=2) + + low, high = np.clip(low, 0, 1), np.clip(high, 0, 1) + low = EmpiricalDistributionFunction(self.quantiles, low, None, None, + self._kind) + high = EmpiricalDistributionFunction(self.quantiles, high, None, None, + self._kind) + return ConfidenceInterval(low, high) + + def _linear_ci(self, confidence_level): + sf, d, n = self._sf, self._d, self._n + # When n == d, Greenwood's formula divides by zero. + # When s != 0, this can be ignored: var == inf, and CI is [0, 1] + # When s == 0, this results in NaNs. Produce an informative warning. + with np.errstate(divide='ignore', invalid='ignore'): + var = sf ** 2 * np.cumsum(d / (n * (n - d))) + + se = np.sqrt(var) + z = special.ndtri(1 / 2 + confidence_level / 2) + + z_se = z * se + low = self.probabilities - z_se + high = self.probabilities + z_se + + return low, high + + def _loglog_ci(self, confidence_level): + sf, d, n = self._sf, self._d, self._n + + with np.errstate(divide='ignore', invalid='ignore'): + var = 1 / np.log(sf) ** 2 * np.cumsum(d / (n * (n - d))) + + se = np.sqrt(var) + z = special.ndtri(1 / 2 + confidence_level / 2) + + with np.errstate(divide='ignore'): + lnl_points = np.log(-np.log(sf)) + + z_se = z * se + low = np.exp(-np.exp(lnl_points + z_se)) + high = np.exp(-np.exp(lnl_points - z_se)) + if self._kind == "cdf": + low, high = 1-high, 1-low + + return low, high + + +@dataclass +class ECDFResult: + """ Result object returned by `scipy.stats.ecdf` + + Attributes + ---------- + cdf : `~scipy.stats._result_classes.EmpiricalDistributionFunction` + An object representing the empirical cumulative distribution function. + sf : `~scipy.stats._result_classes.EmpiricalDistributionFunction` + An object representing the complement of the empirical cumulative + distribution function. + """ + cdf: EmpiricalDistributionFunction + sf: EmpiricalDistributionFunction + + def __init__(self, q, cdf, sf, n, d): + self.cdf = EmpiricalDistributionFunction(q, cdf, n, d, "cdf") + self.sf = EmpiricalDistributionFunction(q, sf, n, d, "sf") + + +def _iv_CensoredData( + sample: npt.ArrayLike | CensoredData, param_name: str = 'sample' +) -> CensoredData: + """Attempt to convert `sample` to `CensoredData`.""" + if not isinstance(sample, CensoredData): + try: # takes care of input standardization/validation + sample = CensoredData(uncensored=sample) + except ValueError as e: + message = str(e).replace('uncensored', param_name) + raise type(e)(message) from e + return sample + + +def ecdf(sample: npt.ArrayLike | CensoredData) -> ECDFResult: + """Empirical cumulative distribution function of a sample. + + The empirical cumulative distribution function (ECDF) is a step function + estimate of the CDF of the distribution underlying a sample. This function + returns objects representing both the empirical distribution function and + its complement, the empirical survival function. + + Parameters + ---------- + sample : 1D array_like or `scipy.stats.CensoredData` + Besides array_like, instances of `scipy.stats.CensoredData` containing + uncensored and right-censored observations are supported. Currently, + other instances of `scipy.stats.CensoredData` will result in a + ``NotImplementedError``. + + Returns + ------- + res : `~scipy.stats._result_classes.ECDFResult` + An object with the following attributes. + + cdf : `~scipy.stats._result_classes.EmpiricalDistributionFunction` + An object representing the empirical cumulative distribution + function. + sf : `~scipy.stats._result_classes.EmpiricalDistributionFunction` + An object representing the empirical survival function. + + The `cdf` and `sf` attributes themselves have the following attributes. + + quantiles : ndarray + The unique values in the sample that defines the empirical CDF/SF. + probabilities : ndarray + The point estimates of the probabilities corresponding with + `quantiles`. + + And the following methods: + + evaluate(x) : + Evaluate the CDF/SF at the argument. + + plot(ax) : + Plot the CDF/SF on the provided axes. + + confidence_interval(confidence_level=0.95) : + Compute the confidence interval around the CDF/SF at the values in + `quantiles`. + + Notes + ----- + When each observation of the sample is a precise measurement, the ECDF + steps up by ``1/len(sample)`` at each of the observations [1]_. + + When observations are lower bounds, upper bounds, or both upper and lower + bounds, the data is said to be "censored", and `sample` may be provided as + an instance of `scipy.stats.CensoredData`. + + For right-censored data, the ECDF is given by the Kaplan-Meier estimator + [2]_; other forms of censoring are not supported at this time. + + Confidence intervals are computed according to the Greenwood formula or the + more recent "Exponential Greenwood" formula as described in [4]_. + + References + ---------- + .. [1] Conover, William Jay. Practical nonparametric statistics. Vol. 350. + John Wiley & Sons, 1999. + + .. [2] Kaplan, Edward L., and Paul Meier. "Nonparametric estimation from + incomplete observations." Journal of the American statistical + association 53.282 (1958): 457-481. + + .. [3] Goel, Manish Kumar, Pardeep Khanna, and Jugal Kishore. + "Understanding survival analysis: Kaplan-Meier estimate." + International journal of Ayurveda research 1.4 (2010): 274. + + .. [4] Sawyer, Stanley. "The Greenwood and Exponential Greenwood Confidence + Intervals in Survival Analysis." + https://www.math.wustl.edu/~sawyer/handouts/greenwood.pdf + + Examples + -------- + **Uncensored Data** + + As in the example from [1]_ page 79, five boys were selected at random from + those in a single high school. Their one-mile run times were recorded as + follows. + + >>> sample = [6.23, 5.58, 7.06, 6.42, 5.20] # one-mile run times (minutes) + + The empirical distribution function, which approximates the distribution + function of one-mile run times of the population from which the boys were + sampled, is calculated as follows. + + >>> from scipy import stats + >>> res = stats.ecdf(sample) + >>> res.cdf.quantiles + array([5.2 , 5.58, 6.23, 6.42, 7.06]) + >>> res.cdf.probabilities + array([0.2, 0.4, 0.6, 0.8, 1. ]) + + To plot the result as a step function: + + >>> import matplotlib.pyplot as plt + >>> ax = plt.subplot() + >>> res.cdf.plot(ax) + >>> ax.set_xlabel('One-Mile Run Time (minutes)') + >>> ax.set_ylabel('Empirical CDF') + >>> plt.show() + + **Right-censored Data** + + As in the example from [1]_ page 91, the lives of ten car fanbelts were + tested. Five tests concluded because the fanbelt being tested broke, but + the remaining tests concluded for other reasons (e.g. the study ran out of + funding, but the fanbelt was still functional). The mileage driven + with the fanbelts were recorded as follows. + + >>> broken = [77, 47, 81, 56, 80] # in thousands of miles driven + >>> unbroken = [62, 60, 43, 71, 37] + + Precise survival times of the fanbelts that were still functional at the + end of the tests are unknown, but they are known to exceed the values + recorded in ``unbroken``. Therefore, these observations are said to be + "right-censored", and the data is represented using + `scipy.stats.CensoredData`. + + >>> sample = stats.CensoredData(uncensored=broken, right=unbroken) + + The empirical survival function is calculated as follows. + + >>> res = stats.ecdf(sample) + >>> res.sf.quantiles + array([37., 43., 47., 56., 60., 62., 71., 77., 80., 81.]) + >>> res.sf.probabilities + array([1. , 1. , 0.875, 0.75 , 0.75 , 0.75 , 0.75 , 0.5 , 0.25 , 0. ]) + + To plot the result as a step function: + + >>> ax = plt.subplot() + >>> res.cdf.plot(ax) + >>> ax.set_xlabel('Fanbelt Survival Time (thousands of miles)') + >>> ax.set_ylabel('Empirical SF') + >>> plt.show() + + """ + sample = _iv_CensoredData(sample) + + if sample.num_censored() == 0: + res = _ecdf_uncensored(sample._uncensor()) + elif sample.num_censored() == sample._right.size: + res = _ecdf_right_censored(sample) + else: + # Support additional censoring options in follow-up PRs + message = ("Currently, only uncensored and right-censored data is " + "supported.") + raise NotImplementedError(message) + + t, cdf, sf, n, d = res + return ECDFResult(t, cdf, sf, n, d) + + +def _ecdf_uncensored(sample): + sample = np.sort(sample) + x, counts = np.unique(sample, return_counts=True) + + # [1].81 "the fraction of [observations] that are less than or equal to x + events = np.cumsum(counts) + n = sample.size + cdf = events / n + + # [1].89 "the relative frequency of the sample that exceeds x in value" + sf = 1 - cdf + + at_risk = np.concatenate(([n], n - events[:-1])) + return x, cdf, sf, at_risk, counts + + +def _ecdf_right_censored(sample): + # It is conventional to discuss right-censored data in terms of + # "survival time", "death", and "loss" (e.g. [2]). We'll use that + # terminology here. + # This implementation was influenced by the references cited and also + # https://www.youtube.com/watch?v=lxoWsVco_iM + # https://en.wikipedia.org/wiki/Kaplan%E2%80%93Meier_estimator + # In retrospect it is probably most easily compared against [3]. + # Ultimately, the data needs to be sorted, so this implementation is + # written to avoid a separate call to `unique` after sorting. In hope of + # better performance on large datasets, it also computes survival + # probabilities at unique times only rather than at each observation. + tod = sample._uncensored # time of "death" + tol = sample._right # time of "loss" + times = np.concatenate((tod, tol)) + died = np.asarray([1]*tod.size + [0]*tol.size) + + # sort by times + i = np.argsort(times) + times = times[i] + died = died[i] + at_risk = np.arange(times.size, 0, -1) + + # logical indices of unique times + j = np.diff(times, prepend=-np.inf, append=np.inf) > 0 + j_l = j[:-1] # first instances of unique times + j_r = j[1:] # last instances of unique times + + # get number at risk and deaths at each unique time + t = times[j_l] # unique times + n = at_risk[j_l] # number at risk at each unique time + cd = np.cumsum(died)[j_r] # cumulative deaths up to/including unique times + d = np.diff(cd, prepend=0) # deaths at each unique time + + # compute survival function + sf = np.cumprod((n - d) / n) + cdf = 1 - sf + return t, cdf, sf, n, d + + +@dataclass +class LogRankResult: + """Result object returned by `scipy.stats.logrank`. + + Attributes + ---------- + statistic : float ndarray + The computed statistic (defined below). Its magnitude is the + square root of the magnitude returned by most other logrank test + implementations. + pvalue : float ndarray + The computed p-value of the test. + """ + statistic: np.ndarray + pvalue: np.ndarray + + +def logrank( + x: npt.ArrayLike | CensoredData, + y: npt.ArrayLike | CensoredData, + alternative: Literal['two-sided', 'less', 'greater'] = "two-sided" +) -> LogRankResult: + r"""Compare the survival distributions of two samples via the logrank test. + + Parameters + ---------- + x, y : array_like or CensoredData + Samples to compare based on their empirical survival functions. + alternative : {'two-sided', 'less', 'greater'}, optional + Defines the alternative hypothesis. + + The null hypothesis is that the survival distributions of the two + groups, say *X* and *Y*, are identical. + + The following alternative hypotheses [4]_ are available (default is + 'two-sided'): + + * 'two-sided': the survival distributions of the two groups are not + identical. + * 'less': survival of group *X* is favored: the group *X* failure rate + function is less than the group *Y* failure rate function at some + times. + * 'greater': survival of group *Y* is favored: the group *X* failure + rate function is greater than the group *Y* failure rate function at + some times. + + Returns + ------- + res : `~scipy.stats._result_classes.LogRankResult` + An object containing attributes: + + statistic : float ndarray + The computed statistic (defined below). Its magnitude is the + square root of the magnitude returned by most other logrank test + implementations. + pvalue : float ndarray + The computed p-value of the test. + + See Also + -------- + scipy.stats.ecdf + + Notes + ----- + The logrank test [1]_ compares the observed number of events to + the expected number of events under the null hypothesis that the two + samples were drawn from the same distribution. The statistic is + + .. math:: + + Z_i = \frac{\sum_{j=1}^J(O_{i,j}-E_{i,j})}{\sqrt{\sum_{j=1}^J V_{i,j}}} + \rightarrow \mathcal{N}(0,1) + + where + + .. math:: + + E_{i,j} = O_j \frac{N_{i,j}}{N_j}, + \qquad + V_{i,j} = E_{i,j} \left(\frac{N_j-O_j}{N_j}\right) + \left(\frac{N_j-N_{i,j}}{N_j-1}\right), + + :math:`i` denotes the group (i.e. it may assume values :math:`x` or + :math:`y`, or it may be omitted to refer to the combined sample) + :math:`j` denotes the time (at which an event occurred), + :math:`N` is the number of subjects at risk just before an event occurred, + and :math:`O` is the observed number of events at that time. + + The ``statistic`` :math:`Z_x` returned by `logrank` is the (signed) square + root of the statistic returned by many other implementations. Under the + null hypothesis, :math:`Z_x**2` is asymptotically distributed according to + the chi-squared distribution with one degree of freedom. Consequently, + :math:`Z_x` is asymptotically distributed according to the standard normal + distribution. The advantage of using :math:`Z_x` is that the sign + information (i.e. whether the observed number of events tends to be less + than or greater than the number expected under the null hypothesis) is + preserved, allowing `scipy.stats.logrank` to offer one-sided alternative + hypotheses. + + References + ---------- + .. [1] Mantel N. "Evaluation of survival data and two new rank order + statistics arising in its consideration." + Cancer Chemotherapy Reports, 50(3):163-170, PMID: 5910392, 1966 + .. [2] Bland, Altman, "The logrank test", BMJ, 328:1073, + :doi:`10.1136/bmj.328.7447.1073`, 2004 + .. [3] "Logrank test", Wikipedia, + https://en.wikipedia.org/wiki/Logrank_test + .. [4] Brown, Mark. "On the choice of variance for the log rank test." + Biometrika 71.1 (1984): 65-74. + .. [5] Klein, John P., and Melvin L. Moeschberger. Survival analysis: + techniques for censored and truncated data. Vol. 1230. New York: + Springer, 2003. + + Examples + -------- + Reference [2]_ compared the survival times of patients with two different + types of recurrent malignant gliomas. The samples below record the time + (number of weeks) for which each patient participated in the study. The + `scipy.stats.CensoredData` class is used because the data is + right-censored: the uncensored observations correspond with observed deaths + whereas the censored observations correspond with the patient leaving the + study for another reason. + + >>> from scipy import stats + >>> x = stats.CensoredData( + ... uncensored=[6, 13, 21, 30, 37, 38, 49, 50, + ... 63, 79, 86, 98, 202, 219], + ... right=[31, 47, 80, 82, 82, 149] + ... ) + >>> y = stats.CensoredData( + ... uncensored=[10, 10, 12, 13, 14, 15, 16, 17, 18, 20, 24, 24, + ... 25, 28,30, 33, 35, 37, 40, 40, 46, 48, 76, 81, + ... 82, 91, 112, 181], + ... right=[34, 40, 70] + ... ) + + We can calculate and visualize the empirical survival functions + of both groups as follows. + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> ax = plt.subplot() + >>> ecdf_x = stats.ecdf(x) + >>> ecdf_x.sf.plot(ax, label='Astrocytoma') + >>> ecdf_y = stats.ecdf(y) + >>> ecdf_y.sf.plot(ax, label='Glioblastoma') + >>> ax.set_xlabel('Time to death (weeks)') + >>> ax.set_ylabel('Empirical SF') + >>> plt.legend() + >>> plt.show() + + Visual inspection of the empirical survival functions suggests that the + survival times tend to be different between the two groups. To formally + assess whether the difference is significant at the 1% level, we use the + logrank test. + + >>> res = stats.logrank(x=x, y=y) + >>> res.statistic + -2.73799... + >>> res.pvalue + 0.00618... + + The p-value is less than 1%, so we can consider the data to be evidence + against the null hypothesis in favor of the alternative that there is a + difference between the two survival functions. + + """ + # Input validation. `alternative` IV handled in `_get_pvalue` below. + x = _iv_CensoredData(sample=x, param_name='x') + y = _iv_CensoredData(sample=y, param_name='y') + + # Combined sample. (Under H0, the two groups are identical.) + xy = CensoredData( + uncensored=np.concatenate((x._uncensored, y._uncensored)), + right=np.concatenate((x._right, y._right)) + ) + + # Extract data from the combined sample + res = ecdf(xy) + idx = res.sf._d.astype(bool) # indices of observed events + times_xy = res.sf.quantiles[idx] # unique times of observed events + at_risk_xy = res.sf._n[idx] # combined number of subjects at risk + deaths_xy = res.sf._d[idx] # combined number of events + + # Get the number at risk within each sample. + # First compute the number at risk in group X at each of the `times_xy`. + # Could use `interpolate_1d`, but this is more compact. + res_x = ecdf(x) + i = np.searchsorted(res_x.sf.quantiles, times_xy) + at_risk_x = np.append(res_x.sf._n, 0)[i] # 0 at risk after last time + # Subtract from the combined number at risk to get number at risk in Y + at_risk_y = at_risk_xy - at_risk_x + + # Compute the variance. + num = at_risk_x * at_risk_y * deaths_xy * (at_risk_xy - deaths_xy) + den = at_risk_xy**2 * (at_risk_xy - 1) + # Note: when `at_risk_xy == 1`, we would have `at_risk_xy - 1 == 0` in the + # numerator and denominator. Simplifying the fraction symbolically, we + # would always find the overall quotient to be zero, so don't compute it. + i = at_risk_xy > 1 + sum_var = np.sum(num[i]/den[i]) + + # Get the observed and expected number of deaths in group X + n_died_x = x._uncensored.size + sum_exp_deaths_x = np.sum(at_risk_x * (deaths_xy/at_risk_xy)) + + # Compute the statistic. This is the square root of that in references. + statistic = (n_died_x - sum_exp_deaths_x)/np.sqrt(sum_var) + + # Equivalent to chi2(df=1).sf(statistic**2) when alternative='two-sided' + pvalue = stats._stats_py._get_pvalue(statistic, norm, alternative) + + return LogRankResult(statistic=statistic[()], pvalue=pvalue[()]) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_tukeylambda_stats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_tukeylambda_stats.py new file mode 100644 index 0000000000000000000000000000000000000000..b77173c136d80eb57f5c993108b7408653acad13 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_tukeylambda_stats.py @@ -0,0 +1,199 @@ +import numpy as np +from numpy import poly1d +from scipy.special import beta + + +# The following code was used to generate the Pade coefficients for the +# Tukey Lambda variance function. Version 0.17 of mpmath was used. +#--------------------------------------------------------------------------- +# import mpmath as mp +# +# mp.mp.dps = 60 +# +# one = mp.mpf(1) +# two = mp.mpf(2) +# +# def mpvar(lam): +# if lam == 0: +# v = mp.pi**2 / three +# else: +# v = (two / lam**2) * (one / (one + two*lam) - +# mp.beta(lam + one, lam + one)) +# return v +# +# t = mp.taylor(mpvar, 0, 8) +# p, q = mp.pade(t, 4, 4) +# print("p =", [mp.fp.mpf(c) for c in p]) +# print("q =", [mp.fp.mpf(c) for c in q]) +#--------------------------------------------------------------------------- + +# Pade coefficients for the Tukey Lambda variance function. +_tukeylambda_var_pc = [3.289868133696453, 0.7306125098871127, + -0.5370742306855439, 0.17292046290190008, + -0.02371146284628187] +_tukeylambda_var_qc = [1.0, 3.683605511659861, 4.184152498888124, + 1.7660926747377275, 0.2643989311168465] + +# numpy.poly1d instances for the numerator and denominator of the +# Pade approximation to the Tukey Lambda variance. +_tukeylambda_var_p = poly1d(_tukeylambda_var_pc[::-1]) +_tukeylambda_var_q = poly1d(_tukeylambda_var_qc[::-1]) + + +def tukeylambda_variance(lam): + """Variance of the Tukey Lambda distribution. + + Parameters + ---------- + lam : array_like + The lambda values at which to compute the variance. + + Returns + ------- + v : ndarray + The variance. For lam < -0.5, the variance is not defined, so + np.nan is returned. For lam = 0.5, np.inf is returned. + + Notes + ----- + In an interval around lambda=0, this function uses the [4,4] Pade + approximation to compute the variance. Otherwise it uses the standard + formula (https://en.wikipedia.org/wiki/Tukey_lambda_distribution). The + Pade approximation is used because the standard formula has a removable + discontinuity at lambda = 0, and does not produce accurate numerical + results near lambda = 0. + """ + lam = np.asarray(lam) + shp = lam.shape + lam = np.atleast_1d(lam).astype(np.float64) + + # For absolute values of lam less than threshold, use the Pade + # approximation. + threshold = 0.075 + + # Play games with masks to implement the conditional evaluation of + # the distribution. + # lambda < -0.5: var = nan + low_mask = lam < -0.5 + # lambda == -0.5: var = inf + neghalf_mask = lam == -0.5 + # abs(lambda) < threshold: use Pade approximation + small_mask = np.abs(lam) < threshold + # else the "regular" case: use the explicit formula. + reg_mask = ~(low_mask | neghalf_mask | small_mask) + + # Get the 'lam' values for the cases where they are needed. + small = lam[small_mask] + reg = lam[reg_mask] + + # Compute the function for each case. + v = np.empty_like(lam) + v[low_mask] = np.nan + v[neghalf_mask] = np.inf + if small.size > 0: + # Use the Pade approximation near lambda = 0. + v[small_mask] = _tukeylambda_var_p(small) / _tukeylambda_var_q(small) + if reg.size > 0: + v[reg_mask] = (2.0 / reg**2) * (1.0 / (1.0 + 2 * reg) - + beta(reg + 1, reg + 1)) + v.shape = shp + return v + + +# The following code was used to generate the Pade coefficients for the +# Tukey Lambda kurtosis function. Version 0.17 of mpmath was used. +#--------------------------------------------------------------------------- +# import mpmath as mp +# +# mp.mp.dps = 60 +# +# one = mp.mpf(1) +# two = mp.mpf(2) +# three = mp.mpf(3) +# four = mp.mpf(4) +# +# def mpkurt(lam): +# if lam == 0: +# k = mp.mpf(6)/5 +# else: +# numer = (one/(four*lam+one) - four*mp.beta(three*lam+one, lam+one) + +# three*mp.beta(two*lam+one, two*lam+one)) +# denom = two*(one/(two*lam+one) - mp.beta(lam+one,lam+one))**2 +# k = numer / denom - three +# return k +# +# # There is a bug in mpmath 0.17: when we use the 'method' keyword of the +# # taylor function and we request a degree 9 Taylor polynomial, we actually +# # get degree 8. +# t = mp.taylor(mpkurt, 0, 9, method='quad', radius=0.01) +# t = [mp.chop(c, tol=1e-15) for c in t] +# p, q = mp.pade(t, 4, 4) +# print("p =", [mp.fp.mpf(c) for c in p]) +# print("q =", [mp.fp.mpf(c) for c in q]) +#--------------------------------------------------------------------------- + +# Pade coefficients for the Tukey Lambda kurtosis function. +_tukeylambda_kurt_pc = [1.2, -5.853465139719495, -22.653447381131077, + 0.20601184383406815, 4.59796302262789] +_tukeylambda_kurt_qc = [1.0, 7.171149192233599, 12.96663094361842, + 0.43075235247853005, -2.789746758009912] + +# numpy.poly1d instances for the numerator and denominator of the +# Pade approximation to the Tukey Lambda kurtosis. +_tukeylambda_kurt_p = poly1d(_tukeylambda_kurt_pc[::-1]) +_tukeylambda_kurt_q = poly1d(_tukeylambda_kurt_qc[::-1]) + + +def tukeylambda_kurtosis(lam): + """Kurtosis of the Tukey Lambda distribution. + + Parameters + ---------- + lam : array_like + The lambda values at which to compute the variance. + + Returns + ------- + v : ndarray + The variance. For lam < -0.25, the variance is not defined, so + np.nan is returned. For lam = 0.25, np.inf is returned. + + """ + lam = np.asarray(lam) + shp = lam.shape + lam = np.atleast_1d(lam).astype(np.float64) + + # For absolute values of lam less than threshold, use the Pade + # approximation. + threshold = 0.055 + + # Use masks to implement the conditional evaluation of the kurtosis. + # lambda < -0.25: kurtosis = nan + low_mask = lam < -0.25 + # lambda == -0.25: kurtosis = inf + negqrtr_mask = lam == -0.25 + # lambda near 0: use Pade approximation + small_mask = np.abs(lam) < threshold + # else the "regular" case: use the explicit formula. + reg_mask = ~(low_mask | negqrtr_mask | small_mask) + + # Get the 'lam' values for the cases where they are needed. + small = lam[small_mask] + reg = lam[reg_mask] + + # Compute the function for each case. + k = np.empty_like(lam) + k[low_mask] = np.nan + k[negqrtr_mask] = np.inf + if small.size > 0: + k[small_mask] = _tukeylambda_kurt_p(small) / _tukeylambda_kurt_q(small) + if reg.size > 0: + numer = (1.0 / (4 * reg + 1) - 4 * beta(3 * reg + 1, reg + 1) + + 3 * beta(2 * reg + 1, 2 * reg + 1)) + denom = 2 * (1.0/(2 * reg + 1) - beta(reg + 1, reg + 1))**2 + k[reg_mask] = numer / denom - 3 + + # The return value will be a numpy array; resetting the shape ensures that + # if `lam` was a scalar, the return value is a 0-d array. + k.shape = shp + return k diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_unuran/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_unuran/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_unuran/unuran_wrapper.pyi b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_unuran/unuran_wrapper.pyi new file mode 100644 index 0000000000000000000000000000000000000000..96b6d4e65edfd8e7264ef6905d4706303e552702 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_unuran/unuran_wrapper.pyi @@ -0,0 +1,179 @@ +from __future__ import annotations +import numpy as np +from typing import (overload, Callable, NamedTuple, Protocol) +import numpy.typing as npt +from scipy._lib._util import SeedType +import scipy.stats as stats + + +ArrayLike0D = bool | int | float | complex | str | bytes | np.generic + + +__all__: list[str] + + +class UNURANError(RuntimeError): + ... + + +class Method: + @overload + def rvs(self, size: None = ...) -> float | int: ... # type: ignore[misc] + @overload + def rvs(self, size: int | tuple[int, ...] = ...) -> np.ndarray: ... + def set_random_state(self, random_state: SeedType) -> None: ... + + +class TDRDist(Protocol): + @property + def pdf(self) -> Callable[..., float]: ... + @property + def dpdf(self) -> Callable[..., float]: ... + @property + def support(self) -> tuple[float, float]: ... + + +class TransformedDensityRejection(Method): + def __init__(self, + dist: TDRDist, + *, + mode: None | float = ..., + center: None | float = ..., + domain: None | tuple[float, float] = ..., + c: float = ..., + construction_points: int | npt.ArrayLike = ..., + use_dars: bool = ..., + max_squeeze_hat_ratio: float = ..., + random_state: SeedType = ...) -> None: ... + @property + def squeeze_hat_ratio(self) -> float: ... + @property + def squeeze_area(self) -> float: ... + @overload + def ppf_hat(self, u: ArrayLike0D) -> float: ... # type: ignore[misc] + @overload + def ppf_hat(self, u: npt.ArrayLike) -> np.ndarray: ... + + +class SROUDist(Protocol): + @property + def pdf(self) -> Callable[..., float]: ... + @property + def support(self) -> tuple[float, float]: ... + + +class SimpleRatioUniforms(Method): + def __init__(self, + dist: SROUDist, + *, + mode: None | float = ..., + pdf_area: float = ..., + domain: None | tuple[float, float] = ..., + cdf_at_mode: float = ..., + random_state: SeedType = ...) -> None: ... + + +class UError(NamedTuple): + max_error: float + mean_absolute_error: float + +class PINVDist(Protocol): + @property + def pdf(self) -> Callable[..., float]: ... + @property + def cdf(self) -> Callable[..., float]: ... + @property + def logpdf(self) -> Callable[..., float]: ... + + +class NumericalInversePolynomial(Method): + def __init__(self, + dist: PINVDist, + *, + mode: None | float = ..., + center: None | float = ..., + domain: None | tuple[float, float] = ..., + order: int = ..., + u_resolution: float = ..., + random_state: SeedType = ...) -> None: ... + @property + def intervals(self) -> int: ... + @overload + def ppf(self, u: ArrayLike0D) -> float: ... # type: ignore[misc] + @overload + def ppf(self, u: npt.ArrayLike) -> np.ndarray: ... + @overload + def cdf(self, x: ArrayLike0D) -> float: ... # type: ignore[misc] + @overload + def cdf(self, x: npt.ArrayLike) -> np.ndarray: ... + def u_error(self, sample_size: int = ...) -> UError: ... + def qrvs(self, + size: None | int | tuple[int, ...] = ..., + d: None | int = ..., + qmc_engine: None | stats.qmc.QMCEngine = ...) -> npt.ArrayLike: ... + + +class HINVDist(Protocol): + @property + def pdf(self) -> Callable[..., float]: ... + @property + def cdf(self) -> Callable[..., float]: ... + @property + def support(self) -> tuple[float, float]: ... + + +class NumericalInverseHermite(Method): + def __init__(self, + dist: HINVDist, + *, + domain: None | tuple[float, float] = ..., + order: int= ..., + u_resolution: float = ..., + construction_points: None | npt.ArrayLike = ..., + max_intervals: int = ..., + random_state: SeedType = ...) -> None: ... + @property + def intervals(self) -> int: ... + @overload + def ppf(self, u: ArrayLike0D) -> float: ... # type: ignore[misc] + @overload + def ppf(self, u: npt.ArrayLike) -> np.ndarray: ... + def qrvs(self, + size: None | int | tuple[int, ...] = ..., + d: None | int = ..., + qmc_engine: None | stats.qmc.QMCEngine = ...) -> npt.ArrayLike: ... + def u_error(self, sample_size: int = ...) -> UError: ... + + +class DAUDist(Protocol): + @property + def pmf(self) -> Callable[..., float]: ... + @property + def support(self) -> tuple[float, float]: ... + +class DiscreteAliasUrn(Method): + def __init__(self, + dist: npt.ArrayLike | DAUDist, + *, + domain: None | tuple[float, float] = ..., + urn_factor: float = ..., + random_state: SeedType = ...) -> None: ... + + +class DGTDist(Protocol): + @property + def pmf(self) -> Callable[..., float]: ... + @property + def support(self) -> tuple[float, float]: ... + +class DiscreteGuideTable(Method): + def __init__(self, + dist: npt.ArrayLike | DGTDist, + *, + domain: None | tuple[float, float] = ..., + guide_factor: float = ..., + random_state: SeedType = ...) -> None: ... + @overload + def ppf(self, u: ArrayLike0D) -> float: ... # type: ignore[misc] + @overload + def ppf(self, u: npt.ArrayLike) -> np.ndarray: ... diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_variation.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_variation.py new file mode 100644 index 0000000000000000000000000000000000000000..b51cb856e213af829eeee03da7ace7e41c23b5c0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_variation.py @@ -0,0 +1,121 @@ +import numpy as np +from scipy._lib._util import _get_nan +from ._axis_nan_policy import _axis_nan_policy_factory + + +@_axis_nan_policy_factory( + lambda x: x, n_outputs=1, result_to_tuple=lambda x: (x,) +) +def variation(a, axis=0, nan_policy='propagate', ddof=0, *, keepdims=False): + """ + Compute the coefficient of variation. + + The coefficient of variation is the standard deviation divided by the + mean. This function is equivalent to:: + + np.std(x, axis=axis, ddof=ddof) / np.mean(x) + + The default for ``ddof`` is 0, but many definitions of the coefficient + of variation use the square root of the unbiased sample variance + for the sample standard deviation, which corresponds to ``ddof=1``. + + The function does not take the absolute value of the mean of the data, + so the return value is negative if the mean is negative. + + Parameters + ---------- + a : array_like + Input array. + axis : int or None, optional + Axis along which to calculate the coefficient of variation. + Default is 0. If None, compute over the whole array `a`. + nan_policy : {'propagate', 'raise', 'omit'}, optional + Defines how to handle when input contains ``nan``. + The following options are available: + + * 'propagate': return ``nan`` + * 'raise': raise an exception + * 'omit': perform the calculation with ``nan`` values omitted + + The default is 'propagate'. + ddof : int, optional + Gives the "Delta Degrees Of Freedom" used when computing the + standard deviation. The divisor used in the calculation of the + standard deviation is ``N - ddof``, where ``N`` is the number of + elements. `ddof` must be less than ``N``; if it isn't, the result + will be ``nan`` or ``inf``, depending on ``N`` and the values in + the array. By default `ddof` is zero for backwards compatibility, + but it is recommended to use ``ddof=1`` to ensure that the sample + standard deviation is computed as the square root of the unbiased + sample variance. + + Returns + ------- + variation : ndarray + The calculated variation along the requested axis. + + Notes + ----- + There are several edge cases that are handled without generating a + warning: + + * If both the mean and the standard deviation are zero, ``nan`` + is returned. + * If the mean is zero and the standard deviation is nonzero, ``inf`` + is returned. + * If the input has length zero (either because the array has zero + length, or all the input values are ``nan`` and ``nan_policy`` is + ``'omit'``), ``nan`` is returned. + * If the input contains ``inf``, ``nan`` is returned. + + References + ---------- + .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard + Probability and Statistics Tables and Formulae. Chapman & Hall: New + York. 2000. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats import variation + >>> variation([1, 2, 3, 4, 5], ddof=1) + 0.5270462766947299 + + Compute the variation along a given dimension of an array that contains + a few ``nan`` values: + + >>> x = np.array([[ 10.0, np.nan, 11.0, 19.0, 23.0, 29.0, 98.0], + ... [ 29.0, 30.0, 32.0, 33.0, 35.0, 56.0, 57.0], + ... [np.nan, np.nan, 12.0, 13.0, 16.0, 16.0, 17.0]]) + >>> variation(x, axis=1, ddof=1, nan_policy='omit') + array([1.05109361, 0.31428986, 0.146483 ]) + + """ + # `nan_policy` and `keepdims` are handled by `_axis_nan_policy` + n = a.shape[axis] + NaN = _get_nan(a) + + if a.size == 0 or ddof > n: + # Handle as a special case to avoid spurious warnings. + # The return values, if any, are all nan. + shp = np.asarray(a.shape) + shp = np.delete(shp, axis) + result = np.full(shp, fill_value=NaN) + return result[()] + + mean_a = a.mean(axis) + + if ddof == n: + # Another special case. Result is either inf or nan. + std_a = a.std(axis=axis, ddof=0) + result = np.full_like(std_a, fill_value=NaN) + i = std_a > 0 + result[i] = np.inf + result[i] = np.copysign(result[i], mean_a[i]) + return result[()] + + with np.errstate(divide='ignore', invalid='ignore'): + std_a = a.std(axis, ddof=ddof) + result = std_a / mean_a + + return result[()] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_warnings_errors.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_warnings_errors.py new file mode 100644 index 0000000000000000000000000000000000000000..38385b862c9d642b41af8d74279f98c6a427208a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_warnings_errors.py @@ -0,0 +1,38 @@ +# Warnings + + +class DegenerateDataWarning(RuntimeWarning): + """Warns when data is degenerate and results may not be reliable.""" + def __init__(self, msg=None): + if msg is None: + msg = ("Degenerate data encountered; results may not be reliable.") + self.args = (msg,) + + +class ConstantInputWarning(DegenerateDataWarning): + """Warns when all values in data are exactly equal.""" + def __init__(self, msg=None): + if msg is None: + msg = ("All values in data are exactly equal; " + "results may not be reliable.") + self.args = (msg,) + + +class NearConstantInputWarning(DegenerateDataWarning): + """Warns when all values in data are nearly equal.""" + def __init__(self, msg=None): + if msg is None: + msg = ("All values in data are nearly equal; " + "results may not be reliable.") + self.args = (msg,) + + +# Errors + + +class FitError(RuntimeError): + """Represents an error condition when fitting a distribution to data.""" + def __init__(self, msg=None): + if msg is None: + msg = ("An error occurred when fitting a distribution to data.") + self.args = (msg,) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_wilcoxon.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_wilcoxon.py new file mode 100644 index 0000000000000000000000000000000000000000..2fbdfe14803125fccdcba00a2cd7be1c1f619270 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/_wilcoxon.py @@ -0,0 +1,238 @@ +import warnings +import numpy as np + +from scipy import stats +from ._stats_py import _get_pvalue, _rankdata +from . import _morestats +from ._axis_nan_policy import _broadcast_arrays +from ._hypotests import _get_wilcoxon_distr +from scipy._lib._util import _lazywhere, _get_nan + + +class WilcoxonDistribution: + + def __init__(self, n): + n = np.asarray(n).astype(int, copy=False) + self.n = n + self._dists = {ni: _get_wilcoxon_distr(ni) for ni in np.unique(n)} + + def _cdf1(self, k, n): + pmfs = self._dists[n] + return pmfs[:k + 1].sum() + + def _cdf(self, k, n): + return np.vectorize(self._cdf1, otypes=[float])(k, n) + + def _sf1(self, k, n): + pmfs = self._dists[n] + return pmfs[k:].sum() + + def _sf(self, k, n): + return np.vectorize(self._sf1, otypes=[float])(k, n) + + def mean(self): + return self.n * (self.n + 1) / 4 + + def _prep(self, k): + k = np.asarray(k).astype(int, copy=False) + mn = self.mean() + out = np.empty(k.shape, dtype=np.float64) + return k, mn, out + + def cdf(self, k): + k, mn, out = self._prep(k) + return _lazywhere(k <= mn, (k, self.n), self._cdf, + f2=lambda k, n: 1 - self._sf(k+1, n))[()] + + def sf(self, k): + k, mn, out = self._prep(k) + return _lazywhere(k <= mn, (k, self.n), self._sf, + f2=lambda k, n: 1 - self._cdf(k-1, n))[()] + + +def _wilcoxon_iv(x, y, zero_method, correction, alternative, method, axis): + + axis = np.asarray(axis)[()] + message = "`axis` must be an integer." + if not np.issubdtype(axis.dtype, np.integer) or axis.ndim != 0: + raise ValueError(message) + + message = '`axis` must be compatible with the shape(s) of `x` (and `y`)' + try: + if y is None: + x = np.asarray(x) + d = x + else: + x, y = _broadcast_arrays((x, y), axis=axis) + d = x - y + d = np.moveaxis(d, axis, -1) + except np.AxisError as e: + raise ValueError(message) from e + + message = "`x` and `y` must have the same length along `axis`." + if y is not None and x.shape[axis] != y.shape[axis]: + raise ValueError(message) + + message = "`x` (and `y`, if provided) must be an array of real numbers." + if np.issubdtype(d.dtype, np.integer): + d = d.astype(np.float64) + if not np.issubdtype(d.dtype, np.floating): + raise ValueError(message) + + zero_method = str(zero_method).lower() + zero_methods = {"wilcox", "pratt", "zsplit"} + message = f"`zero_method` must be one of {zero_methods}." + if zero_method not in zero_methods: + raise ValueError(message) + + corrections = {True, False} + message = f"`correction` must be one of {corrections}." + if correction not in corrections: + raise ValueError(message) + + alternative = str(alternative).lower() + alternatives = {"two-sided", "less", "greater"} + message = f"`alternative` must be one of {alternatives}." + if alternative not in alternatives: + raise ValueError(message) + + if not isinstance(method, stats.PermutationMethod): + methods = {"auto", "approx", "exact"} + message = (f"`method` must be one of {methods} or " + "an instance of `stats.PermutationMethod`.") + if method not in methods: + raise ValueError(message) + output_z = True if method == 'approx' else False + + # logic unchanged here for backward compatibility + n_zero = np.sum(d == 0, axis=-1) + has_zeros = np.any(n_zero > 0) + if method == "auto": + if d.shape[-1] <= 50 and not has_zeros: + method = "exact" + else: + method = "approx" + + n_zero = np.sum(d == 0) + if n_zero > 0 and method == "exact": + method = "approx" + warnings.warn("Exact p-value calculation does not work if there are " + "zeros. Switching to normal approximation.", + stacklevel=2) + + if (method == "approx" and zero_method in ["wilcox", "pratt"] + and n_zero == d.size and d.size > 0 and d.ndim == 1): + raise ValueError("zero_method 'wilcox' and 'pratt' do not " + "work if x - y is zero for all elements.") + + if 0 < d.shape[-1] < 10 and method == "approx": + warnings.warn("Sample size too small for normal approximation.", stacklevel=2) + + return d, zero_method, correction, alternative, method, axis, output_z + + +def _wilcoxon_statistic(d, zero_method='wilcox'): + + i_zeros = (d == 0) + + if zero_method == 'wilcox': + # Wilcoxon's method for treating zeros was to remove them from + # the calculation. We do this by replacing 0s with NaNs, which + # are ignored anyway. + if not d.flags['WRITEABLE']: + d = d.copy() + d[i_zeros] = np.nan + + i_nan = np.isnan(d) + n_nan = np.sum(i_nan, axis=-1) + count = d.shape[-1] - n_nan + + r, t = _rankdata(abs(d), 'average', return_ties=True) + + r_plus = np.sum((d > 0) * r, axis=-1) + r_minus = np.sum((d < 0) * r, axis=-1) + + if zero_method == "zsplit": + # The "zero-split" method for treating zeros is to add half their contribution + # to r_plus and half to r_minus. + # See gh-2263 for the origin of this method. + r_zero_2 = np.sum(i_zeros * r, axis=-1) / 2 + r_plus += r_zero_2 + r_minus += r_zero_2 + + mn = count * (count + 1.) * 0.25 + se = count * (count + 1.) * (2. * count + 1.) + + if zero_method == "pratt": + # Pratt's method for treating zeros was just to modify the z-statistic. + + # normal approximation needs to be adjusted, see Cureton (1967) + n_zero = i_zeros.sum(axis=-1) + mn -= n_zero * (n_zero + 1.) * 0.25 + se -= n_zero * (n_zero + 1.) * (2. * n_zero + 1.) + + # zeros are not to be included in tie-correction. + # any tie counts corresponding with zeros are in the 0th column + t[i_zeros.any(axis=-1), 0] = 0 + + tie_correct = (t**3 - t).sum(axis=-1) + se -= tie_correct/2 + se = np.sqrt(se / 24) + + z = (r_plus - mn) / se + + return r_plus, r_minus, se, z, count + + +def _correction_sign(z, alternative): + if alternative == 'greater': + return 1 + elif alternative == 'less': + return -1 + else: + return np.sign(z) + + +def _wilcoxon_nd(x, y=None, zero_method='wilcox', correction=True, + alternative='two-sided', method='auto', axis=0): + + temp = _wilcoxon_iv(x, y, zero_method, correction, alternative, method, axis) + d, zero_method, correction, alternative, method, axis, output_z = temp + + if d.size == 0: + NaN = _get_nan(d) + res = _morestats.WilcoxonResult(statistic=NaN, pvalue=NaN) + if method == 'approx': + res.zstatistic = NaN + return res + + r_plus, r_minus, se, z, count = _wilcoxon_statistic(d, zero_method) + + if method == 'approx': + if correction: + sign = _correction_sign(z, alternative) + z -= sign * 0.5 / se + p = _get_pvalue(z, stats.norm, alternative) + elif method == 'exact': + dist = WilcoxonDistribution(count) + if alternative == 'less': + p = dist.cdf(r_plus) + elif alternative == 'greater': + p = dist.sf(r_plus) + else: + p = 2 * np.minimum(dist.sf(r_plus), dist.cdf(r_plus)) + p = np.clip(p, 0, 1) + else: # `PermutationMethod` instance (already validated) + p = stats.permutation_test( + (d,), lambda d: _wilcoxon_statistic(d, zero_method)[0], + permutation_type='samples', **method._asdict(), + alternative=alternative, axis=-1).pvalue + + # for backward compatibility... + statistic = np.minimum(r_plus, r_minus) if alternative=='two-sided' else r_plus + z = -np.abs(z) if (alternative == 'two-sided' and method == 'approx') else z + + res = _morestats.WilcoxonResult(statistic=statistic, pvalue=p[()]) + if output_z: + res.zstatistic = z[()] + return res diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/biasedurn.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/biasedurn.py new file mode 100644 index 0000000000000000000000000000000000000000..f5e1cd5c84897ed9e65db1cf20d3281479d07a1f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/biasedurn.py @@ -0,0 +1,20 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + '_PyFishersNCHypergeometric', + '_PyWalleniusNCHypergeometric', + '_PyStochasticLib3' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="biasedurn", + private_modules=["_biasedurn"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/contingency.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/contingency.py new file mode 100644 index 0000000000000000000000000000000000000000..399322475b08953a26a23135d0244d87890467bc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/contingency.py @@ -0,0 +1,468 @@ +""" +Contingency table functions (:mod:`scipy.stats.contingency`) +============================================================ + +Functions for creating and analyzing contingency tables. + +.. currentmodule:: scipy.stats.contingency + +.. autosummary:: + :toctree: generated/ + + chi2_contingency + relative_risk + odds_ratio + crosstab + association + + expected_freq + margins + +""" + + +from functools import reduce +import math +import numpy as np +from ._stats_py import power_divergence +from ._relative_risk import relative_risk +from ._crosstab import crosstab +from ._odds_ratio import odds_ratio +from scipy._lib._bunch import _make_tuple_bunch + + +__all__ = ['margins', 'expected_freq', 'chi2_contingency', 'crosstab', + 'association', 'relative_risk', 'odds_ratio'] + + +def margins(a): + """Return a list of the marginal sums of the array `a`. + + Parameters + ---------- + a : ndarray + The array for which to compute the marginal sums. + + Returns + ------- + margsums : list of ndarrays + A list of length `a.ndim`. `margsums[k]` is the result + of summing `a` over all axes except `k`; it has the same + number of dimensions as `a`, but the length of each axis + except axis `k` will be 1. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.contingency import margins + + >>> a = np.arange(12).reshape(2, 6) + >>> a + array([[ 0, 1, 2, 3, 4, 5], + [ 6, 7, 8, 9, 10, 11]]) + >>> m0, m1 = margins(a) + >>> m0 + array([[15], + [51]]) + >>> m1 + array([[ 6, 8, 10, 12, 14, 16]]) + + >>> b = np.arange(24).reshape(2,3,4) + >>> m0, m1, m2 = margins(b) + >>> m0 + array([[[ 66]], + [[210]]]) + >>> m1 + array([[[ 60], + [ 92], + [124]]]) + >>> m2 + array([[[60, 66, 72, 78]]]) + """ + margsums = [] + ranged = list(range(a.ndim)) + for k in ranged: + marg = np.apply_over_axes(np.sum, a, [j for j in ranged if j != k]) + margsums.append(marg) + return margsums + + +def expected_freq(observed): + """ + Compute the expected frequencies from a contingency table. + + Given an n-dimensional contingency table of observed frequencies, + compute the expected frequencies for the table based on the marginal + sums under the assumption that the groups associated with each + dimension are independent. + + Parameters + ---------- + observed : array_like + The table of observed frequencies. (While this function can handle + a 1-D array, that case is trivial. Generally `observed` is at + least 2-D.) + + Returns + ------- + expected : ndarray of float64 + The expected frequencies, based on the marginal sums of the table. + Same shape as `observed`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.stats.contingency import expected_freq + >>> observed = np.array([[10, 10, 20],[20, 20, 20]]) + >>> expected_freq(observed) + array([[ 12., 12., 16.], + [ 18., 18., 24.]]) + + """ + # Typically `observed` is an integer array. If `observed` has a large + # number of dimensions or holds large values, some of the following + # computations may overflow, so we first switch to floating point. + observed = np.asarray(observed, dtype=np.float64) + + # Create a list of the marginal sums. + margsums = margins(observed) + + # Create the array of expected frequencies. The shapes of the + # marginal sums returned by apply_over_axes() are just what we + # need for broadcasting in the following product. + d = observed.ndim + expected = reduce(np.multiply, margsums) / observed.sum() ** (d - 1) + return expected + + +Chi2ContingencyResult = _make_tuple_bunch( + 'Chi2ContingencyResult', + ['statistic', 'pvalue', 'dof', 'expected_freq'], [] +) + + +def chi2_contingency(observed, correction=True, lambda_=None): + """Chi-square test of independence of variables in a contingency table. + + This function computes the chi-square statistic and p-value for the + hypothesis test of independence of the observed frequencies in the + contingency table [1]_ `observed`. The expected frequencies are computed + based on the marginal sums under the assumption of independence; see + `scipy.stats.contingency.expected_freq`. The number of degrees of + freedom is (expressed using numpy functions and attributes):: + + dof = observed.size - sum(observed.shape) + observed.ndim - 1 + + + Parameters + ---------- + observed : array_like + The contingency table. The table contains the observed frequencies + (i.e. number of occurrences) in each category. In the two-dimensional + case, the table is often described as an "R x C table". + correction : bool, optional + If True, *and* the degrees of freedom is 1, apply Yates' correction + for continuity. The effect of the correction is to adjust each + observed value by 0.5 towards the corresponding expected value. + lambda_ : float or str, optional + By default, the statistic computed in this test is Pearson's + chi-squared statistic [2]_. `lambda_` allows a statistic from the + Cressie-Read power divergence family [3]_ to be used instead. See + `scipy.stats.power_divergence` for details. + + Returns + ------- + res : Chi2ContingencyResult + An object containing attributes: + + statistic : float + The test statistic. + pvalue : float + The p-value of the test. + dof : int + The degrees of freedom. + expected_freq : ndarray, same shape as `observed` + The expected frequencies, based on the marginal sums of the table. + + See Also + -------- + scipy.stats.contingency.expected_freq + scipy.stats.fisher_exact + scipy.stats.chisquare + scipy.stats.power_divergence + scipy.stats.barnard_exact + scipy.stats.boschloo_exact + + Notes + ----- + An often quoted guideline for the validity of this calculation is that + the test should be used only if the observed and expected frequencies + in each cell are at least 5. + + This is a test for the independence of different categories of a + population. The test is only meaningful when the dimension of + `observed` is two or more. Applying the test to a one-dimensional + table will always result in `expected` equal to `observed` and a + chi-square statistic equal to 0. + + This function does not handle masked arrays, because the calculation + does not make sense with missing values. + + Like `scipy.stats.chisquare`, this function computes a chi-square + statistic; the convenience this function provides is to figure out the + expected frequencies and degrees of freedom from the given contingency + table. If these were already known, and if the Yates' correction was not + required, one could use `scipy.stats.chisquare`. That is, if one calls:: + + res = chi2_contingency(obs, correction=False) + + then the following is true:: + + (res.statistic, res.pvalue) == stats.chisquare(obs.ravel(), + f_exp=ex.ravel(), + ddof=obs.size - 1 - dof) + + The `lambda_` argument was added in version 0.13.0 of scipy. + + References + ---------- + .. [1] "Contingency table", + https://en.wikipedia.org/wiki/Contingency_table + .. [2] "Pearson's chi-squared test", + https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test + .. [3] Cressie, N. and Read, T. R. C., "Multinomial Goodness-of-Fit + Tests", J. Royal Stat. Soc. Series B, Vol. 46, No. 3 (1984), + pp. 440-464. + .. [4] Berger, Jeffrey S. et al. "Aspirin for the Primary Prevention of + Cardiovascular Events in Women and Men: A Sex-Specific + Meta-analysis of Randomized Controlled Trials." + JAMA, 295(3):306-313, :doi:`10.1001/jama.295.3.306`, 2006. + + Examples + -------- + In [4]_, the use of aspirin to prevent cardiovascular events in women + and men was investigated. The study notably concluded: + + ...aspirin therapy reduced the risk of a composite of + cardiovascular events due to its effect on reducing the risk of + ischemic stroke in women [...] + + The article lists studies of various cardiovascular events. Let's + focus on the ischemic stoke in women. + + The following table summarizes the results of the experiment in which + participants took aspirin or a placebo on a regular basis for several + years. Cases of ischemic stroke were recorded:: + + Aspirin Control/Placebo + Ischemic stroke 176 230 + No stroke 21035 21018 + + Is there evidence that the aspirin reduces the risk of ischemic stroke? + We begin by formulating a null hypothesis :math:`H_0`: + + The effect of aspirin is equivalent to that of placebo. + + Let's assess the plausibility of this hypothesis with + a chi-square test. + + >>> import numpy as np + >>> from scipy.stats import chi2_contingency + >>> table = np.array([[176, 230], [21035, 21018]]) + >>> res = chi2_contingency(table) + >>> res.statistic + 6.892569132546561 + >>> res.pvalue + 0.008655478161175739 + + Using a significance level of 5%, we would reject the null hypothesis in + favor of the alternative hypothesis: "the effect of aspirin + is not equivalent to the effect of placebo". + Because `scipy.stats.contingency.chi2_contingency` performs a two-sided + test, the alternative hypothesis does not indicate the direction of the + effect. We can use `stats.contingency.odds_ratio` to support the + conclusion that aspirin *reduces* the risk of ischemic stroke. + + Below are further examples showing how larger contingency tables can be + tested. + + A two-way example (2 x 3): + + >>> obs = np.array([[10, 10, 20], [20, 20, 20]]) + >>> res = chi2_contingency(obs) + >>> res.statistic + 2.7777777777777777 + >>> res.pvalue + 0.24935220877729619 + >>> res.dof + 2 + >>> res.expected_freq + array([[ 12., 12., 16.], + [ 18., 18., 24.]]) + + Perform the test using the log-likelihood ratio (i.e. the "G-test") + instead of Pearson's chi-squared statistic. + + >>> res = chi2_contingency(obs, lambda_="log-likelihood") + >>> res.statistic + 2.7688587616781319 + >>> res.pvalue + 0.25046668010954165 + + A four-way example (2 x 2 x 2 x 2): + + >>> obs = np.array( + ... [[[[12, 17], + ... [11, 16]], + ... [[11, 12], + ... [15, 16]]], + ... [[[23, 15], + ... [30, 22]], + ... [[14, 17], + ... [15, 16]]]]) + >>> res = chi2_contingency(obs) + >>> res.statistic + 8.7584514426741897 + >>> res.pvalue + 0.64417725029295503 + """ + observed = np.asarray(observed) + if np.any(observed < 0): + raise ValueError("All values in `observed` must be nonnegative.") + if observed.size == 0: + raise ValueError("No data; `observed` has size 0.") + + expected = expected_freq(observed) + if np.any(expected == 0): + # Include one of the positions where expected is zero in + # the exception message. + zeropos = list(zip(*np.nonzero(expected == 0)))[0] + raise ValueError("The internally computed table of expected " + f"frequencies has a zero element at {zeropos}.") + + # The degrees of freedom + dof = expected.size - sum(expected.shape) + expected.ndim - 1 + + if dof == 0: + # Degenerate case; this occurs when `observed` is 1D (or, more + # generally, when it has only one nontrivial dimension). In this + # case, we also have observed == expected, so chi2 is 0. + chi2 = 0.0 + p = 1.0 + else: + if dof == 1 and correction: + # Adjust `observed` according to Yates' correction for continuity. + # Magnitude of correction no bigger than difference; see gh-13875 + diff = expected - observed + direction = np.sign(diff) + magnitude = np.minimum(0.5, np.abs(diff)) + observed = observed + magnitude * direction + + chi2, p = power_divergence(observed, expected, + ddof=observed.size - 1 - dof, axis=None, + lambda_=lambda_) + + return Chi2ContingencyResult(chi2, p, dof, expected) + + +def association(observed, method="cramer", correction=False, lambda_=None): + """Calculates degree of association between two nominal variables. + + The function provides the option for computing one of three measures of + association between two nominal variables from the data given in a 2d + contingency table: Tschuprow's T, Pearson's Contingency Coefficient + and Cramer's V. + + Parameters + ---------- + observed : array-like + The array of observed values + method : {"cramer", "tschuprow", "pearson"} (default = "cramer") + The association test statistic. + correction : bool, optional + Inherited from `scipy.stats.contingency.chi2_contingency()` + lambda_ : float or str, optional + Inherited from `scipy.stats.contingency.chi2_contingency()` + + Returns + ------- + statistic : float + Value of the test statistic + + Notes + ----- + Cramer's V, Tschuprow's T and Pearson's Contingency Coefficient, all + measure the degree to which two nominal or ordinal variables are related, + or the level of their association. This differs from correlation, although + many often mistakenly consider them equivalent. Correlation measures in + what way two variables are related, whereas, association measures how + related the variables are. As such, association does not subsume + independent variables, and is rather a test of independence. A value of + 1.0 indicates perfect association, and 0.0 means the variables have no + association. + + Both the Cramer's V and Tschuprow's T are extensions of the phi + coefficient. Moreover, due to the close relationship between the + Cramer's V and Tschuprow's T the returned values can often be similar + or even equivalent. They are likely to diverge more as the array shape + diverges from a 2x2. + + References + ---------- + .. [1] "Tschuprow's T", + https://en.wikipedia.org/wiki/Tschuprow's_T + .. [2] Tschuprow, A. A. (1939) + Principles of the Mathematical Theory of Correlation; + translated by M. Kantorowitsch. W. Hodge & Co. + .. [3] "Cramer's V", https://en.wikipedia.org/wiki/Cramer's_V + .. [4] "Nominal Association: Phi and Cramer's V", + http://www.people.vcu.edu/~pdattalo/702SuppRead/MeasAssoc/NominalAssoc.html + .. [5] Gingrich, Paul, "Association Between Variables", + http://uregina.ca/~gingrich/ch11a.pdf + + Examples + -------- + An example with a 4x2 contingency table: + + >>> import numpy as np + >>> from scipy.stats.contingency import association + >>> obs4x2 = np.array([[100, 150], [203, 322], [420, 700], [320, 210]]) + + Pearson's contingency coefficient + + >>> association(obs4x2, method="pearson") + 0.18303298140595667 + + Cramer's V + + >>> association(obs4x2, method="cramer") + 0.18617813077483678 + + Tschuprow's T + + >>> association(obs4x2, method="tschuprow") + 0.14146478765062995 + """ + arr = np.asarray(observed) + if not np.issubdtype(arr.dtype, np.integer): + raise ValueError("`observed` must be an integer array.") + + if len(arr.shape) != 2: + raise ValueError("method only accepts 2d arrays") + + chi2_stat = chi2_contingency(arr, correction=correction, + lambda_=lambda_) + + phi2 = chi2_stat.statistic / arr.sum() + n_rows, n_cols = arr.shape + if method == "cramer": + value = phi2 / min(n_cols - 1, n_rows - 1) + elif method == "tschuprow": + value = phi2 / math.sqrt((n_rows - 1) * (n_cols - 1)) + elif method == 'pearson': + value = phi2 / (1 + phi2) + else: + raise ValueError("Invalid argument value: 'method' argument must " + "be 'cramer', 'tschuprow', or 'pearson'") + + return math.sqrt(value) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/distributions.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/distributions.py new file mode 100644 index 0000000000000000000000000000000000000000..ac9c37aa98c9545b2616c8d32e8f676d8d49289e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/distributions.py @@ -0,0 +1,24 @@ +# +# Author: Travis Oliphant 2002-2011 with contributions from +# SciPy Developers 2004-2011 +# +# NOTE: To look at history using `git blame`, use `git blame -M -C -C` +# instead of `git blame -Lxxx,+x`. +# +from ._distn_infrastructure import (rv_discrete, rv_continuous, rv_frozen) # noqa: F401 + +from . import _continuous_distns +from . import _discrete_distns + +from ._continuous_distns import * # noqa: F403 +from ._levy_stable import levy_stable +from ._discrete_distns import * # noqa: F403 +from ._entropy import entropy + +# For backwards compatibility e.g. pymc expects distributions.__all__. +__all__ = ['rv_discrete', 'rv_continuous', 'rv_histogram', 'entropy'] # noqa: F405 + +# Add only the distribution names, not the *_gen names. +__all__ += _continuous_distns._distn_names +__all__ += ['levy_stable'] +__all__ += _discrete_distns._distn_names diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/kde.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/kde.py new file mode 100644 index 0000000000000000000000000000000000000000..08e299b5137c44d70c4db841a0b53060d552d50d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/kde.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'gaussian_kde', 'linalg', 'logsumexp', 'check_random_state', + 'atleast_2d', 'reshape', 'newaxis', 'exp', 'ravel', 'power', + 'atleast_1d', 'squeeze', 'sum', 'transpose', 'cov', + 'gaussian_kernel_estimate' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="kde", + private_modules=["_kde"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/morestats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/morestats.py new file mode 100644 index 0000000000000000000000000000000000000000..fce0b989d8b5ec583a517c7998d55b1336937bf6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/morestats.py @@ -0,0 +1,34 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'mvsdist', + 'bayes_mvs', 'kstat', 'kstatvar', 'probplot', 'ppcc_max', 'ppcc_plot', + 'boxcox_llf', 'boxcox', 'boxcox_normmax', 'boxcox_normplot', + 'shapiro', 'anderson', 'ansari', 'bartlett', 'levene', + 'fligner', 'mood', 'wilcoxon', 'median_test', + 'circmean', 'circvar', 'circstd', 'anderson_ksamp', + 'yeojohnson_llf', 'yeojohnson', 'yeojohnson_normmax', + 'yeojohnson_normplot', 'annotations', 'namedtuple', 'isscalar', 'log', + 'around', 'unique', 'arange', 'sort', 'amin', 'amax', 'atleast_1d', + 'array', 'compress', 'exp', 'ravel', 'count_nonzero', 'arctan2', + 'hypot', 'optimize', 'find_repeats', + 'chi2_contingency', 'distributions', 'rv_generic', 'Mean', + 'Variance', 'Std_dev', 'ShapiroResult', 'AndersonResult', + 'Anderson_ksampResult', 'AnsariResult', 'BartlettResult', + 'LeveneResult', 'FlignerResult', 'WilcoxonResult' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="morestats", + private_modules=["_morestats"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats.py new file mode 100644 index 0000000000000000000000000000000000000000..88016af71803dc5c4ebadba168f22cdcd8273dbb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats.py @@ -0,0 +1,140 @@ +""" +=================================================================== +Statistical functions for masked arrays (:mod:`scipy.stats.mstats`) +=================================================================== + +.. currentmodule:: scipy.stats.mstats + +This module contains a large number of statistical functions that can +be used with masked arrays. + +Most of these functions are similar to those in `scipy.stats` but might +have small differences in the API or in the algorithm used. Since this +is a relatively new package, some API changes are still possible. + +Summary statistics +================== + +.. autosummary:: + :toctree: generated/ + + describe + gmean + hmean + kurtosis + mode + mquantiles + hdmedian + hdquantiles + hdquantiles_sd + idealfourths + plotting_positions + meppf + moment + skew + tmean + tvar + tmin + tmax + tsem + variation + find_repeats + sem + trimmed_mean + trimmed_mean_ci + trimmed_std + trimmed_var + +Frequency statistics +==================== + +.. autosummary:: + :toctree: generated/ + + scoreatpercentile + +Correlation functions +===================== + +.. autosummary:: + :toctree: generated/ + + f_oneway + pearsonr + spearmanr + pointbiserialr + kendalltau + kendalltau_seasonal + linregress + siegelslopes + theilslopes + sen_seasonal_slopes + +Statistical tests +================= + +.. autosummary:: + :toctree: generated/ + + ttest_1samp + ttest_onesamp + ttest_ind + ttest_rel + chisquare + kstest + ks_2samp + ks_1samp + ks_twosamp + mannwhitneyu + rankdata + kruskal + kruskalwallis + friedmanchisquare + brunnermunzel + skewtest + kurtosistest + normaltest + +Transformations +=============== + +.. autosummary:: + :toctree: generated/ + + obrientransform + trim + trima + trimmed_stde + trimr + trimtail + trimboth + winsorize + zmap + zscore + +Other +===== + +.. autosummary:: + :toctree: generated/ + + argstoarray + count_tied_groups + msign + compare_medians_ms + median_cihs + mjci + mquantiles_cimj + rsh + +""" +from . import _mstats_basic +from . import _mstats_extras +from ._mstats_basic import * # noqa: F403 +from ._mstats_extras import * # noqa: F403 +# Functions that support masked array input in stats but need to be kept in the +# mstats namespace for backwards compatibility: +from scipy.stats import gmean, hmean, zmap, zscore, chisquare + +__all__ = _mstats_basic.__all__ + _mstats_extras.__all__ +__all__ += ['gmean', 'hmean', 'zmap', 'zscore', 'chisquare'] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..f79f07bed7a01a49e3005496db27c1a2c971e06a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_basic.py @@ -0,0 +1,50 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'argstoarray', + 'count_tied_groups', + 'describe', + 'f_oneway', 'find_repeats','friedmanchisquare', + 'kendalltau','kendalltau_seasonal','kruskal','kruskalwallis', + 'ks_twosamp', 'ks_2samp', 'kurtosis', 'kurtosistest', + 'ks_1samp', 'kstest', + 'linregress', + 'mannwhitneyu', 'meppf','mode','moment','mquantiles','msign', + 'normaltest', + 'obrientransform', + 'pearsonr','plotting_positions','pointbiserialr', + 'rankdata', + 'scoreatpercentile','sem', + 'sen_seasonal_slopes','skew','skewtest','spearmanr', + 'siegelslopes', 'theilslopes', + 'tmax','tmean','tmin','trim','trimboth', + 'trimtail','trima','trimr','trimmed_mean','trimmed_std', + 'trimmed_stde','trimmed_var','tsem','ttest_1samp','ttest_onesamp', + 'ttest_ind','ttest_rel','tvar', + 'variation', + 'winsorize', + 'brunnermunzel', 'ma', 'masked', 'nomask', 'namedtuple', + 'distributions', 'stats_linregress', 'stats_LinregressResult', + 'stats_theilslopes', 'stats_siegelslopes', 'ModeResult', + 'PointbiserialrResult', + 'Ttest_1sampResult', 'Ttest_indResult', 'Ttest_relResult', + 'MannwhitneyuResult', 'KruskalResult', 'trimdoc', 'trim1', + 'DescribeResult', 'stde_median', 'SkewtestResult', 'KurtosistestResult', + 'NormaltestResult', 'F_onewayResult', 'FriedmanchisquareResult', + 'BrunnerMunzelResult' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="mstats_basic", + private_modules=["_mstats_basic"], all=__all__, + attribute=name, correct_module="mstats") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_extras.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_extras.py new file mode 100644 index 0000000000000000000000000000000000000000..01a19f22b257d537762838f86dcfa10146c9a4a5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mstats_extras.py @@ -0,0 +1,26 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'compare_medians_ms', + 'hdquantiles', 'hdmedian', 'hdquantiles_sd', + 'idealfourths', + 'median_cihs','mjci','mquantiles_cimj', + 'rsh', + 'trimmed_mean_ci', 'ma', 'MaskedArray', 'mstats', + 'norm', 'beta', 't', 'binom' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="mstats_extras", + private_modules=["_mstats_extras"], all=__all__, + attribute=name, correct_module="mstats") diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mvn.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mvn.py new file mode 100644 index 0000000000000000000000000000000000000000..832993d4702946c19cd7e2a35326a074dac64625 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/mvn.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'mvnun', + 'mvnun_weighted', + 'mvndst', + 'dkblck' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="mvn", + private_modules=["_mvn"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/qmc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/qmc.py new file mode 100644 index 0000000000000000000000000000000000000000..4f3c8182857bee5e25433e154ec72c9f05c0fc5d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/qmc.py @@ -0,0 +1,235 @@ +r""" +==================================================== +Quasi-Monte Carlo submodule (:mod:`scipy.stats.qmc`) +==================================================== + +.. currentmodule:: scipy.stats.qmc + +This module provides Quasi-Monte Carlo generators and associated helper +functions. + + +Quasi-Monte Carlo +================= + +Engines +------- + +.. autosummary:: + :toctree: generated/ + + QMCEngine + Sobol + Halton + LatinHypercube + PoissonDisk + MultinomialQMC + MultivariateNormalQMC + +Helpers +------- + +.. autosummary:: + :toctree: generated/ + + discrepancy + geometric_discrepancy + update_discrepancy + scale + + +Introduction to Quasi-Monte Carlo +================================= + +Quasi-Monte Carlo (QMC) methods [1]_, [2]_, [3]_ provide an +:math:`n \times d` array of numbers in :math:`[0,1]`. They can be used in +place of :math:`n` points from the :math:`U[0,1]^{d}` distribution. Compared to +random points, QMC points are designed to have fewer gaps and clumps. This is +quantified by discrepancy measures [4]_. From the Koksma-Hlawka +inequality [5]_ we know that low discrepancy reduces a bound on +integration error. Averaging a function :math:`f` over :math:`n` QMC points +can achieve an integration error close to :math:`O(n^{-1})` for well +behaved functions [2]_. + +Most QMC constructions are designed for special values of :math:`n` +such as powers of 2 or large primes. Changing the sample +size by even one can degrade their performance, even their +rate of convergence [6]_. For instance :math:`n=100` points may give less +accuracy than :math:`n=64` if the method was designed for :math:`n=2^m`. + +Some QMC constructions are extensible in :math:`n`: we can find +another special sample size :math:`n' > n` and often an infinite +sequence of increasing special sample sizes. Some QMC +constructions are extensible in :math:`d`: we can increase the dimension, +possibly to some upper bound, and typically without requiring +special values of :math:`d`. Some QMC methods are extensible in +both :math:`n` and :math:`d`. + +QMC points are deterministic. That makes it hard to estimate the accuracy of +integrals estimated by averages over QMC points. Randomized QMC (RQMC) [7]_ +points are constructed so that each point is individually :math:`U[0,1]^{d}` +while collectively the :math:`n` points retain their low discrepancy. +One can make :math:`R` independent replications of RQMC points to +see how stable a computation is. From :math:`R` independent values, +a t-test (or bootstrap t-test [8]_) then gives approximate confidence +intervals on the mean value. Some RQMC methods produce a +root mean squared error that is actually :math:`o(1/n)` and smaller than +the rate seen in unrandomized QMC. An intuitive explanation is +that the error is a sum of many small ones and random errors +cancel in a way that deterministic ones do not. RQMC also +has advantages on integrands that are singular or, for other +reasons, fail to be Riemann integrable. + +(R)QMC cannot beat Bahkvalov's curse of dimension (see [9]_). For +any random or deterministic method, there are worst case functions +that will give it poor performance in high dimensions. A worst +case function for QMC might be 0 at all n points but very +large elsewhere. Worst case analyses get very pessimistic +in high dimensions. (R)QMC can bring a great improvement over +MC when the functions on which it is used are not worst case. +For instance (R)QMC can be especially effective on integrands +that are well approximated by sums of functions of +some small number of their input variables at a time [10]_, [11]_. +That property is often a surprising finding about those functions. + +Also, to see an improvement over IID MC, (R)QMC requires a bit of smoothness of +the integrand, roughly the mixed first order derivative in each direction, +:math:`\partial^d f/\partial x_1 \cdots \partial x_d`, must be integral. +For instance, a function that is 1 inside the hypersphere and 0 outside of it +has infinite variation in the sense of Hardy and Krause for any dimension +:math:`d = 2`. + +Scrambled nets are a kind of RQMC that have some valuable robustness +properties [12]_. If the integrand is square integrable, they give variance +:math:`var_{SNET} = o(1/n)`. There is a finite upper bound on +:math:`var_{SNET} / var_{MC}` that holds simultaneously for every square +integrable integrand. Scrambled nets satisfy a strong law of large numbers +for :math:`f` in :math:`L^p` when :math:`p>1`. In some +special cases there is a central limit theorem [13]_. For smooth enough +integrands they can achieve RMSE nearly :math:`O(n^{-3})`. See [12]_ +for references about these properties. + +The main kinds of QMC methods are lattice rules [14]_ and digital +nets and sequences [2]_, [15]_. The theories meet up in polynomial +lattice rules [16]_ which can produce digital nets. Lattice rules +require some form of search for good constructions. For digital +nets there are widely used default constructions. + +The most widely used QMC methods are Sobol' sequences [17]_. +These are digital nets. They are extensible in both :math:`n` and :math:`d`. +They can be scrambled. The special sample sizes are powers +of 2. Another popular method are Halton sequences [18]_. +The constructions resemble those of digital nets. The earlier +dimensions have much better equidistribution properties than +later ones. There are essentially no special sample sizes. +They are not thought to be as accurate as Sobol' sequences. +They can be scrambled. The nets of Faure [19]_ are also widely +used. All dimensions are equally good, but the special sample +sizes grow rapidly with dimension :math:`d`. They can be scrambled. +The nets of Niederreiter and Xing [20]_ have the best asymptotic +properties but have not shown good empirical performance [21]_. + +Higher order digital nets are formed by a digit interleaving process +in the digits of the constructed points. They can achieve higher +levels of asymptotic accuracy given higher smoothness conditions on :math:`f` +and they can be scrambled [22]_. There is little or no empirical work +showing the improved rate to be attained. + +Using QMC is like using the entire period of a small random +number generator. The constructions are similar and so +therefore are the computational costs [23]_. + +(R)QMC is sometimes improved by passing the points through +a baker's transformation (tent function) prior to using them. +That function has the form :math:`1-2|x-1/2|`. As :math:`x` goes from 0 to +1, this function goes from 0 to 1 and then back. It is very +useful to produce a periodic function for lattice rules [14]_, +and sometimes it improves the convergence rate [24]_. + +It is not straightforward to apply QMC methods to Markov +chain Monte Carlo (MCMC). We can think of MCMC as using +:math:`n=1` point in :math:`[0,1]^{d}` for very large :math:`d`, with +ergodic results corresponding to :math:`d \to \infty`. One proposal is +in [25]_ and under strong conditions an improved rate of convergence +has been shown [26]_. + +Returning to Sobol' points: there are many versions depending +on what are called direction numbers. Those are the result of +searches and are tabulated. A very widely used set of direction +numbers come from [27]_. It is extensible in dimension up to +:math:`d=21201`. + +References +---------- +.. [1] Owen, Art B. "Monte Carlo Book: the Quasi-Monte Carlo parts." 2019. +.. [2] Niederreiter, Harald. "Random number generation and quasi-Monte Carlo + methods." Society for Industrial and Applied Mathematics, 1992. +.. [3] Dick, Josef, Frances Y. Kuo, and Ian H. Sloan. "High-dimensional + integration: the quasi-Monte Carlo way." Acta Numerica no. 22: 133, 2013. +.. [4] Aho, A. V., C. Aistleitner, T. Anderson, K. Appel, V. Arnol'd, N. + Aronszajn, D. Asotsky et al. "W. Chen et al.(eds.), "A Panorama of + Discrepancy Theory", Sringer International Publishing, + Switzerland: 679, 2014. +.. [5] Hickernell, Fred J. "Koksma-Hlawka Inequality." Wiley StatsRef: + Statistics Reference Online, 2014. +.. [6] Owen, Art B. "On dropping the first Sobol' point." :arxiv:`2008.08051`, + 2020. +.. [7] L'Ecuyer, Pierre, and Christiane Lemieux. "Recent advances in randomized + quasi-Monte Carlo methods." In Modeling uncertainty, pp. 419-474. Springer, + New York, NY, 2002. +.. [8] DiCiccio, Thomas J., and Bradley Efron. "Bootstrap confidence + intervals." Statistical science: 189-212, 1996. +.. [9] Dimov, Ivan T. "Monte Carlo methods for applied scientists." World + Scientific, 2008. +.. [10] Caflisch, Russel E., William J. Morokoff, and Art B. Owen. "Valuation + of mortgage backed securities using Brownian bridges to reduce effective + dimension." Journal of Computational Finance: no. 1 27-46, 1997. +.. [11] Sloan, Ian H., and Henryk Wozniakowski. "When are quasi-Monte Carlo + algorithms efficient for high dimensional integrals?." Journal of Complexity + 14, no. 1 (1998): 1-33. +.. [12] Owen, Art B., and Daniel Rudolf, "A strong law of large numbers for + scrambled net integration." SIAM Review, to appear. +.. [13] Loh, Wei-Liem. "On the asymptotic distribution of scrambled net + quadrature." The Annals of Statistics 31, no. 4: 1282-1324, 2003. +.. [14] Sloan, Ian H. and S. Joe. "Lattice methods for multiple integration." + Oxford University Press, 1994. +.. [15] Dick, Josef, and Friedrich Pillichshammer. "Digital nets and sequences: + discrepancy theory and quasi-Monte Carlo integration." Cambridge University + Press, 2010. +.. [16] Dick, Josef, F. Kuo, Friedrich Pillichshammer, and I. Sloan. + "Construction algorithms for polynomial lattice rules for multivariate + integration." Mathematics of computation 74, no. 252: 1895-1921, 2005. +.. [17] Sobol', Il'ya Meerovich. "On the distribution of points in a cube and + the approximate evaluation of integrals." Zhurnal Vychislitel'noi Matematiki + i Matematicheskoi Fiziki 7, no. 4: 784-802, 1967. +.. [18] Halton, John H. "On the efficiency of certain quasi-random sequences of + points in evaluating multi-dimensional integrals." Numerische Mathematik 2, + no. 1: 84-90, 1960. +.. [19] Faure, Henri. "Discrepance de suites associees a un systeme de + numeration (en dimension s)." Acta arithmetica 41, no. 4: 337-351, 1982. +.. [20] Niederreiter, Harold, and Chaoping Xing. "Low-discrepancy sequences and + global function fields with many rational places." Finite Fields and their + applications 2, no. 3: 241-273, 1996. +.. [21] Hong, Hee Sun, and Fred J. Hickernell. "Algorithm 823: Implementing + scrambled digital sequences." ACM Transactions on Mathematical Software + (TOMS) 29, no. 2: 95-109, 2003. +.. [22] Dick, Josef. "Higher order scrambled digital nets achieve the optimal + rate of the root mean square error for smooth integrands." The Annals of + Statistics 39, no. 3: 1372-1398, 2011. +.. [23] Niederreiter, Harald. "Multidimensional numerical integration using + pseudorandom numbers." In Stochastic Programming 84 Part I, pp. 17-38. + Springer, Berlin, Heidelberg, 1986. +.. [24] Hickernell, Fred J. "Obtaining O (N-2+e) Convergence for Lattice + Quadrature Rules." In Monte Carlo and Quasi-Monte Carlo Methods 2000, + pp. 274-289. Springer, Berlin, Heidelberg, 2002. +.. [25] Owen, Art B., and Seth D. Tribble. "A quasi-Monte Carlo Metropolis + algorithm." Proceedings of the National Academy of Sciences 102, + no. 25: 8844-8849, 2005. +.. [26] Chen, Su. "Consistency and convergence rate of Markov chain quasi Monte + Carlo with examples." PhD diss., Stanford University, 2011. +.. [27] Joe, Stephen, and Frances Y. Kuo. "Constructing Sobol sequences with + better two-dimensional projections." SIAM Journal on Scientific Computing + 30, no. 5: 2635-2654, 2008. + +""" +from ._qmc import * # noqa: F403 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/sampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..a699221ba77bb8320023d70c3e47f562c08eab18 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/sampling.py @@ -0,0 +1,68 @@ +""" +====================================================== +Random Number Generators (:mod:`scipy.stats.sampling`) +====================================================== + +.. currentmodule:: scipy.stats.sampling + +This module contains a collection of random number generators to sample +from univariate continuous and discrete distributions. It uses the +implementation of a C library called "UNU.RAN". The only exception is +RatioUniforms, which is a pure Python implementation of the +Ratio-of-Uniforms method. + +Generators Wrapped +================== + +For continuous distributions +---------------------------- + +.. autosummary:: + :toctree: generated/ + + NumericalInverseHermite + NumericalInversePolynomial + TransformedDensityRejection + SimpleRatioUniforms + RatioUniforms + +For discrete distributions +-------------------------- + +.. autosummary:: + :toctree: generated/ + + DiscreteAliasUrn + DiscreteGuideTable + +Warnings / Errors used in :mod:`scipy.stats.sampling` +----------------------------------------------------- + +.. autosummary:: + :toctree: generated/ + + UNURANError + + +Generators for pre-defined distributions +======================================== + +To easily apply the above methods for some of the continuous distributions +in :mod:`scipy.stats`, the following functionality can be used: + +.. autosummary:: + :toctree: generated/ + + FastGeneratorInversion + +""" +from ._sampling import FastGeneratorInversion, RatioUniforms # noqa: F401 +from ._unuran.unuran_wrapper import ( # noqa: F401 + TransformedDensityRejection, + DiscreteAliasUrn, + DiscreteGuideTable, + NumericalInversePolynomial, + NumericalInverseHermite, + SimpleRatioUniforms, + UNURANError +) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/stats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/stats.py new file mode 100644 index 0000000000000000000000000000000000000000..142686e08ab56e8cd2a734ef3e327b0f50a10f54 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/stats.py @@ -0,0 +1,52 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.stats` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'find_repeats', 'gmean', 'hmean', 'pmean', 'mode', 'tmean', 'tvar', + 'tmin', 'tmax', 'tstd', 'tsem', 'moment', + 'skew', 'kurtosis', 'describe', 'skewtest', 'kurtosistest', + 'normaltest', 'jarque_bera', + 'scoreatpercentile', 'percentileofscore', + 'cumfreq', 'relfreq', 'obrientransform', + 'sem', 'zmap', 'zscore', 'gzscore', 'iqr', 'gstd', + 'median_abs_deviation', + 'sigmaclip', 'trimboth', 'trim1', 'trim_mean', + 'f_oneway', + 'pearsonr', 'fisher_exact', + 'spearmanr', 'pointbiserialr', + 'kendalltau', 'weightedtau', 'multiscale_graphcorr', + 'linregress', 'siegelslopes', 'theilslopes', 'ttest_1samp', + 'ttest_ind', 'ttest_ind_from_stats', 'ttest_rel', + 'kstest', 'ks_1samp', 'ks_2samp', + 'chisquare', 'power_divergence', + 'tiecorrect', 'ranksums', 'kruskal', 'friedmanchisquare', + 'rankdata', + 'combine_pvalues', 'wasserstein_distance', 'energy_distance', + 'brunnermunzel', 'alexandergovern', 'gcd', 'namedtuple', 'array', + 'ma', 'cdist', 'check_random_state', 'MapWrapper', + 'rng_integers', 'float_factorial', 'linalg', 'distributions', + 'mstats_basic', 'ModeResult', 'DescribeResult', + 'SkewtestResult', 'KurtosistestResult', 'NormaltestResult', + 'HistogramResult', 'CumfreqResult', + 'RelfreqResult', 'SigmaclipResult', 'F_onewayResult', + 'AlexanderGovernResult', + 'PointbiserialrResult', + 'MGCResult', 'Ttest_1sampResult', 'Ttest_indResult', + 'Ttest_relResult', 'Power_divergenceResult', 'KstestResult', + 'Ks_2sampResult', 'RanksumsResult', 'KruskalResult', + 'FriedmanchisquareResult', 'BrunnerMunzelResult', 'RepeatedResults' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="stats", module="stats", + private_modules=["_stats_py"], all=__all__, + attribute=name) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/__init__.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/common_tests.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/common_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..4260eb97f2a2c1d9a839e2f586bd562f28c1ac85 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/common_tests.py @@ -0,0 +1,351 @@ +import pickle + +import numpy as np +import numpy.testing as npt +from numpy.testing import assert_allclose, assert_equal +from pytest import raises as assert_raises + +import numpy.ma.testutils as ma_npt + +from scipy._lib._util import ( + getfullargspec_no_self as _getfullargspec, np_long +) +from scipy import stats + + +def check_named_results(res, attributes, ma=False): + for i, attr in enumerate(attributes): + if ma: + ma_npt.assert_equal(res[i], getattr(res, attr)) + else: + npt.assert_equal(res[i], getattr(res, attr)) + + +def check_normalization(distfn, args, distname): + norm_moment = distfn.moment(0, *args) + npt.assert_allclose(norm_moment, 1.0) + + if distname == "rv_histogram_instance": + atol, rtol = 1e-5, 0 + else: + atol, rtol = 1e-7, 1e-7 + + normalization_expect = distfn.expect(lambda x: 1, args=args) + npt.assert_allclose(normalization_expect, 1.0, atol=atol, rtol=rtol, + err_msg=distname, verbose=True) + + _a, _b = distfn.support(*args) + normalization_cdf = distfn.cdf(_b, *args) + npt.assert_allclose(normalization_cdf, 1.0) + + +def check_moment(distfn, arg, m, v, msg): + m1 = distfn.moment(1, *arg) + m2 = distfn.moment(2, *arg) + if not np.isinf(m): + npt.assert_almost_equal(m1, m, decimal=10, + err_msg=msg + ' - 1st moment') + else: # or np.isnan(m1), + npt.assert_(np.isinf(m1), + msg + ' - 1st moment -infinite, m1=%s' % str(m1)) + + if not np.isinf(v): + npt.assert_almost_equal(m2 - m1 * m1, v, decimal=10, + err_msg=msg + ' - 2ndt moment') + else: # or np.isnan(m2), + npt.assert_(np.isinf(m2), msg + f' - 2nd moment -infinite, {m2=}') + + +def check_mean_expect(distfn, arg, m, msg): + if np.isfinite(m): + m1 = distfn.expect(lambda x: x, arg) + npt.assert_almost_equal(m1, m, decimal=5, + err_msg=msg + ' - 1st moment (expect)') + + +def check_var_expect(distfn, arg, m, v, msg): + dist_looser_tolerances = {"rv_histogram_instance" , "ksone"} + kwargs = {'rtol': 5e-6} if msg in dist_looser_tolerances else {} + if np.isfinite(v): + m2 = distfn.expect(lambda x: x*x, arg) + npt.assert_allclose(m2, v + m*m, **kwargs) + + +def check_skew_expect(distfn, arg, m, v, s, msg): + if np.isfinite(s): + m3e = distfn.expect(lambda x: np.power(x-m, 3), arg) + npt.assert_almost_equal(m3e, s * np.power(v, 1.5), + decimal=5, err_msg=msg + ' - skew') + else: + npt.assert_(np.isnan(s)) + + +def check_kurt_expect(distfn, arg, m, v, k, msg): + if np.isfinite(k): + m4e = distfn.expect(lambda x: np.power(x-m, 4), arg) + npt.assert_allclose(m4e, (k + 3.) * np.power(v, 2), + atol=1e-5, rtol=1e-5, + err_msg=msg + ' - kurtosis') + elif not np.isposinf(k): + npt.assert_(np.isnan(k)) + + +def check_munp_expect(dist, args, msg): + # If _munp is overridden, test a higher moment. (Before gh-18634, some + # distributions had issues with moments 5 and higher.) + if dist._munp.__func__ != stats.rv_continuous._munp: + res = dist.moment(5, *args) # shouldn't raise an error + ref = dist.expect(lambda x: x ** 5, args, lb=-np.inf, ub=np.inf) + if not np.isfinite(res): # could be valid; automated test can't know + return + # loose tolerance, mostly to see whether _munp returns *something* + assert_allclose(res, ref, atol=1e-10, rtol=1e-4, + err_msg=msg + ' - higher moment / _munp') + + +def check_entropy(distfn, arg, msg): + ent = distfn.entropy(*arg) + npt.assert_(not np.isnan(ent), msg + 'test Entropy is nan') + + +def check_private_entropy(distfn, args, superclass): + # compare a generic _entropy with the distribution-specific implementation + npt.assert_allclose(distfn._entropy(*args), + superclass._entropy(distfn, *args)) + + +def check_entropy_vect_scale(distfn, arg): + # check 2-d + sc = np.asarray([[1, 2], [3, 4]]) + v_ent = distfn.entropy(*arg, scale=sc) + s_ent = [distfn.entropy(*arg, scale=s) for s in sc.ravel()] + s_ent = np.asarray(s_ent).reshape(v_ent.shape) + assert_allclose(v_ent, s_ent, atol=1e-14) + + # check invalid value, check cast + sc = [1, 2, -3] + v_ent = distfn.entropy(*arg, scale=sc) + s_ent = [distfn.entropy(*arg, scale=s) for s in sc] + s_ent = np.asarray(s_ent).reshape(v_ent.shape) + assert_allclose(v_ent, s_ent, atol=1e-14) + + +def check_edge_support(distfn, args): + # Make sure that x=self.a and self.b are handled correctly. + x = distfn.support(*args) + if isinstance(distfn, stats.rv_discrete): + x = x[0]-1, x[1] + + npt.assert_equal(distfn.cdf(x, *args), [0.0, 1.0]) + npt.assert_equal(distfn.sf(x, *args), [1.0, 0.0]) + + if distfn.name not in ('skellam', 'dlaplace'): + # with a = -inf, log(0) generates warnings + npt.assert_equal(distfn.logcdf(x, *args), [-np.inf, 0.0]) + npt.assert_equal(distfn.logsf(x, *args), [0.0, -np.inf]) + + npt.assert_equal(distfn.ppf([0.0, 1.0], *args), x) + npt.assert_equal(distfn.isf([0.0, 1.0], *args), x[::-1]) + + # out-of-bounds for isf & ppf + npt.assert_(np.isnan(distfn.isf([-1, 2], *args)).all()) + npt.assert_(np.isnan(distfn.ppf([-1, 2], *args)).all()) + + +def check_named_args(distfn, x, shape_args, defaults, meths): + ## Check calling w/ named arguments. + + # check consistency of shapes, numargs and _parse signature + signature = _getfullargspec(distfn._parse_args) + npt.assert_(signature.varargs is None) + npt.assert_(signature.varkw is None) + npt.assert_(not signature.kwonlyargs) + npt.assert_(list(signature.defaults) == list(defaults)) + + shape_argnames = signature.args[:-len(defaults)] # a, b, loc=0, scale=1 + if distfn.shapes: + shapes_ = distfn.shapes.replace(',', ' ').split() + else: + shapes_ = '' + npt.assert_(len(shapes_) == distfn.numargs) + npt.assert_(len(shapes_) == len(shape_argnames)) + + # check calling w/ named arguments + shape_args = list(shape_args) + + vals = [meth(x, *shape_args) for meth in meths] + npt.assert_(np.all(np.isfinite(vals))) + + names, a, k = shape_argnames[:], shape_args[:], {} + while names: + k.update({names.pop(): a.pop()}) + v = [meth(x, *a, **k) for meth in meths] + npt.assert_array_equal(vals, v) + if 'n' not in k.keys(): + # `n` is first parameter of moment(), so can't be used as named arg + npt.assert_equal(distfn.moment(1, *a, **k), + distfn.moment(1, *shape_args)) + + # unknown arguments should not go through: + k.update({'kaboom': 42}) + assert_raises(TypeError, distfn.cdf, x, **k) + + +def check_random_state_property(distfn, args): + # check the random_state attribute of a distribution *instance* + + # This test fiddles with distfn.random_state. This breaks other tests, + # hence need to save it and then restore. + rndm = distfn.random_state + + # baseline: this relies on the global state + np.random.seed(1234) + distfn.random_state = None + r0 = distfn.rvs(*args, size=8) + + # use an explicit instance-level random_state + distfn.random_state = 1234 + r1 = distfn.rvs(*args, size=8) + npt.assert_equal(r0, r1) + + distfn.random_state = np.random.RandomState(1234) + r2 = distfn.rvs(*args, size=8) + npt.assert_equal(r0, r2) + + # check that np.random.Generator can be used (numpy >= 1.17) + if hasattr(np.random, 'default_rng'): + # obtain a np.random.Generator object + rng = np.random.default_rng(1234) + distfn.rvs(*args, size=1, random_state=rng) + + # can override the instance-level random_state for an individual .rvs call + distfn.random_state = 2 + orig_state = distfn.random_state.get_state() + + r3 = distfn.rvs(*args, size=8, random_state=np.random.RandomState(1234)) + npt.assert_equal(r0, r3) + + # ... and that does not alter the instance-level random_state! + npt.assert_equal(distfn.random_state.get_state(), orig_state) + + # finally, restore the random_state + distfn.random_state = rndm + + +def check_meth_dtype(distfn, arg, meths): + q0 = [0.25, 0.5, 0.75] + x0 = distfn.ppf(q0, *arg) + x_cast = [x0.astype(tp) for tp in (np_long, np.float16, np.float32, + np.float64)] + + for x in x_cast: + # casting may have clipped the values, exclude those + distfn._argcheck(*arg) + x = x[(distfn.a < x) & (x < distfn.b)] + for meth in meths: + val = meth(x, *arg) + npt.assert_(val.dtype == np.float64) + + +def check_ppf_dtype(distfn, arg): + q0 = np.asarray([0.25, 0.5, 0.75]) + q_cast = [q0.astype(tp) for tp in (np.float16, np.float32, np.float64)] + for q in q_cast: + for meth in [distfn.ppf, distfn.isf]: + val = meth(q, *arg) + npt.assert_(val.dtype == np.float64) + + +def check_cmplx_deriv(distfn, arg): + # Distributions allow complex arguments. + def deriv(f, x, *arg): + x = np.asarray(x) + h = 1e-10 + return (f(x + h*1j, *arg)/h).imag + + x0 = distfn.ppf([0.25, 0.51, 0.75], *arg) + x_cast = [x0.astype(tp) for tp in (np_long, np.float16, np.float32, + np.float64)] + + for x in x_cast: + # casting may have clipped the values, exclude those + distfn._argcheck(*arg) + x = x[(distfn.a < x) & (x < distfn.b)] + + pdf, cdf, sf = distfn.pdf(x, *arg), distfn.cdf(x, *arg), distfn.sf(x, *arg) + assert_allclose(deriv(distfn.cdf, x, *arg), pdf, rtol=1e-5) + assert_allclose(deriv(distfn.logcdf, x, *arg), pdf/cdf, rtol=1e-5) + + assert_allclose(deriv(distfn.sf, x, *arg), -pdf, rtol=1e-5) + assert_allclose(deriv(distfn.logsf, x, *arg), -pdf/sf, rtol=1e-5) + + assert_allclose(deriv(distfn.logpdf, x, *arg), + deriv(distfn.pdf, x, *arg) / distfn.pdf(x, *arg), + rtol=1e-5) + + +def check_pickling(distfn, args): + # check that a distribution instance pickles and unpickles + # pay special attention to the random_state property + + # save the random_state (restore later) + rndm = distfn.random_state + + # check unfrozen + distfn.random_state = 1234 + distfn.rvs(*args, size=8) + s = pickle.dumps(distfn) + r0 = distfn.rvs(*args, size=8) + + unpickled = pickle.loads(s) + r1 = unpickled.rvs(*args, size=8) + npt.assert_equal(r0, r1) + + # also smoke test some methods + medians = [distfn.ppf(0.5, *args), unpickled.ppf(0.5, *args)] + npt.assert_equal(medians[0], medians[1]) + npt.assert_equal(distfn.cdf(medians[0], *args), + unpickled.cdf(medians[1], *args)) + + # check frozen pickling/unpickling with rvs + frozen_dist = distfn(*args) + pkl = pickle.dumps(frozen_dist) + unpickled = pickle.loads(pkl) + + r0 = frozen_dist.rvs(size=8) + r1 = unpickled.rvs(size=8) + npt.assert_equal(r0, r1) + + # check pickling/unpickling of .fit method + if hasattr(distfn, "fit"): + fit_function = distfn.fit + pickled_fit_function = pickle.dumps(fit_function) + unpickled_fit_function = pickle.loads(pickled_fit_function) + assert fit_function.__name__ == unpickled_fit_function.__name__ == "fit" + + # restore the random_state + distfn.random_state = rndm + + +def check_freezing(distfn, args): + # regression test for gh-11089: freezing a distribution fails + # if loc and/or scale are specified + if isinstance(distfn, stats.rv_continuous): + locscale = {'loc': 1, 'scale': 2} + else: + locscale = {'loc': 1} + + rv = distfn(*args, **locscale) + assert rv.a == distfn(*args).a + assert rv.b == distfn(*args).b + + +def check_rvs_broadcast(distfunc, distname, allargs, shape, shape_only, otype): + np.random.seed(123) + sample = distfunc.rvs(*allargs) + assert_equal(sample.shape, shape, "%s: rvs failed to broadcast" % distname) + if not shape_only: + rvs = np.vectorize(lambda *allargs: distfunc.rvs(*allargs), otypes=otype) + np.random.seed(123) + expected = rvs(*allargs) + assert_allclose(sample, expected, rtol=1e-13) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/_mvt.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/_mvt.py new file mode 100644 index 0000000000000000000000000000000000000000..c346d0daded4b6e734718742cc8950b84ed333f7 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/_mvt.py @@ -0,0 +1,171 @@ +import math +import numpy as np +from scipy import special +from scipy.stats._qmc import primes_from_2_to + + +def _primes(n): + # Defined to facilitate comparison between translation and source + # In Matlab, primes(10.5) -> first four primes, primes(11.5) -> first five + return primes_from_2_to(math.ceil(n)) + + +def _gaminv(a, b): + # Defined to facilitate comparison between translation and source + # Matlab's `gaminv` is like `special.gammaincinv` but args are reversed + return special.gammaincinv(b, a) + + +def _qsimvtv(m, nu, sigma, a, b, rng): + """Estimates the multivariate t CDF using randomized QMC + + Parameters + ---------- + m : int + The number of points + nu : float + Degrees of freedom + sigma : ndarray + A 2D positive semidefinite covariance matrix + a : ndarray + Lower integration limits + b : ndarray + Upper integration limits. + rng : Generator + Pseudorandom number generator + + Returns + ------- + p : float + The estimated CDF. + e : float + An absolute error estimate. + + """ + # _qsimvtv is a Python translation of the Matlab function qsimvtv, + # semicolons and all. + # + # This function uses an algorithm given in the paper + # "Comparison of Methods for the Numerical Computation of + # Multivariate t Probabilities", in + # J. of Computational and Graphical Stat., 11(2002), pp. 950-971, by + # Alan Genz and Frank Bretz + # + # The primary references for the numerical integration are + # "On a Number-Theoretical Integration Method" + # H. Niederreiter, Aequationes Mathematicae, 8(1972), pp. 304-11. + # and + # "Randomization of Number Theoretic Methods for Multiple Integration" + # R. Cranley & T.N.L. Patterson, SIAM J Numer Anal, 13(1976), pp. 904-14. + # + # Alan Genz is the author of this function and following Matlab functions. + # Alan Genz, WSU Math, PO Box 643113, Pullman, WA 99164-3113 + # Email : alangenz@wsu.edu + # + # Copyright (C) 2013, Alan Genz, All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided the following conditions are met: + # 1. Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # 2. Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in + # the documentation and/or other materials provided with the + # distribution. + # 3. The contributor name(s) may not be used to endorse or promote + # products derived from this software without specific prior + # written permission. + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + # Initialization + sn = max(1, math.sqrt(nu)); ch, az, bz = _chlrps(sigma, a/sn, b/sn) + n = len(sigma); N = 10; P = math.ceil(m/N); on = np.ones(P); p = 0; e = 0 + ps = np.sqrt(_primes(5*n*math.log(n+4)/4)); q = ps[:, np.newaxis] # Richtmyer gens. + + # Randomization loop for ns samples + c = None; dc = None + for S in range(N): + vp = on.copy(); s = np.zeros((n, P)) + for i in range(n): + x = np.abs(2*np.mod(q[i]*np.arange(1, P+1) + rng.random(), 1)-1) # periodizing transform + if i == 0: + r = on + if nu > 0: + r = np.sqrt(2*_gaminv(x, nu/2)) + else: + y = _Phinv(c + x*dc) + s[i:] += ch[i:, i-1:i] * y + si = s[i, :]; c = on.copy(); ai = az[i]*r - si; d = on.copy(); bi = bz[i]*r - si + c[ai <= -9] = 0; tl = abs(ai) < 9; c[tl] = _Phi(ai[tl]) + d[bi <= -9] = 0; tl = abs(bi) < 9; d[tl] = _Phi(bi[tl]) + dc = d - c; vp = vp * dc + d = (np.mean(vp) - p)/(S + 1); p = p + d; e = (S - 1)*e/(S + 1) + d**2 + e = math.sqrt(e) # error estimate is 3 times std error with N samples. + return p, e + + +# Standard statistical normal distribution functions +def _Phi(z): + return special.ndtr(z) + + +def _Phinv(p): + return special.ndtri(p) + + +def _chlrps(R, a, b): + """ + Computes permuted and scaled lower Cholesky factor c for R which may be + singular, also permuting and scaling integration limit vectors a and b. + """ + ep = 1e-10 # singularity tolerance + eps = np.finfo(R.dtype).eps + + n = len(R); c = R.copy(); ap = a.copy(); bp = b.copy(); d = np.sqrt(np.maximum(np.diag(c), 0)) + for i in range(n): + if d[i] > 0: + c[:, i] /= d[i]; c[i, :] /= d[i] + ap[i] /= d[i]; bp[i] /= d[i] + y = np.zeros((n, 1)); sqtp = math.sqrt(2*math.pi) + + for k in range(n): + im = k; ckk = 0; dem = 1; s = 0 + for i in range(k, n): + if c[i, i] > eps: + cii = math.sqrt(max(c[i, i], 0)) + if i > 0: s = c[i, :k] @ y[:k] + ai = (ap[i]-s)/cii; bi = (bp[i]-s)/cii; de = _Phi(bi)-_Phi(ai) + if de <= dem: + ckk = cii; dem = de; am = ai; bm = bi; im = i + if im > k: + ap[[im, k]] = ap[[k, im]]; bp[[im, k]] = bp[[k, im]]; c[im, im] = c[k, k] + t = c[im, :k].copy(); c[im, :k] = c[k, :k]; c[k, :k] = t + t = c[im+1:, im].copy(); c[im+1:, im] = c[im+1:, k]; c[im+1:, k] = t + t = c[k+1:im, k].copy(); c[k+1:im, k] = c[im, k+1:im].T; c[im, k+1:im] = t.T + if ckk > ep*(k+1): + c[k, k] = ckk; c[k, k+1:] = 0 + for i in range(k+1, n): + c[i, k] = c[i, k]/ckk; c[i, k+1:i+1] = c[i, k+1:i+1] - c[i, k]*c[k+1:i+1, k].T + if abs(dem) > ep: + y[k] = (np.exp(-am**2/2) - np.exp(-bm**2/2)) / (sqtp*dem) + else: + y[k] = (am + bm) / 2 + if am < -10: + y[k] = bm + elif bm > 10: + y[k] = am + c[k, :k+1] /= ckk; ap[k] /= ckk; bp[k] /= ckk + else: + c[k:, k] = 0; y[k] = (ap[k] + bp[k])/2 + pass + return c, ap, bp diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/fisher_exact_results_from_r.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/fisher_exact_results_from_r.py new file mode 100644 index 0000000000000000000000000000000000000000..b7dd8936018eae2f74cc6f5966235a86fa821793 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/fisher_exact_results_from_r.py @@ -0,0 +1,607 @@ +# DO NOT EDIT THIS FILE! +# This file was generated by the R script +# generate_fisher_exact_results_from_r.R +# The script was run with R version 3.6.2 (2019-12-12) at 2020-11-09 06:16:09 + + +from collections import namedtuple +import numpy as np + + +Inf = np.inf + +Parameters = namedtuple('Parameters', + ['table', 'confidence_level', 'alternative']) +RResults = namedtuple('RResults', + ['pvalue', 'conditional_odds_ratio', + 'conditional_odds_ratio_ci']) +data = [ + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.1300759363430016, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0.04035202926536294, + 2.662846672960251))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.02301413756522116, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0.004668988338943325, + 0.895792956493601))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.1973244147157191, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0.4153910882532168, + 259.2593661129417))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.09580440012477633, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0.08056337526385809, + 1.22704788545557))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.2697004098849359, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0.1176691231650079, + 1.787463657995973))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.1973244147157192, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0.003857141267422399, + 2.407369893767229))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.06126482213438735, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 1.451643573543705))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.04761904761904762, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(1.024822256141754, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 39.00054996869288))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.04761904761904761, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(1.024822256141754, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 39.00054996869287))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=2.005657880389071e-122, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(349.2595113327733, + 3630.382605689872))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=5.728437460831947e-44, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(152.4166024390096, + 1425.700792178893))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.95, + alternative='two.sided'), + RResults(pvalue=0.574111858126088, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0.8520462587912048, + 1.340148950273938))), + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.1300759363430016, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0.02502345007115455, + 6.304424772117853))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.02301413756522116, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0.001923034001462487, + 1.53670836950172))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.1973244147157191, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0.2397970951413721, + 1291.342011095509))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.09580440012477633, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0.05127576113762925, + 1.717176678806983))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.2697004098849359, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0.07498546954483619, + 2.506969905199901))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.1973244147157192, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0.0007743881879531337, + 4.170192301163831))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.06126482213438735, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 2.642491011905582))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.04761904761904762, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0.496935393325443, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 198.019801980198))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.04761904761904761, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0.496935393325443, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 198.019801980198))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=2.005657880389071e-122, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(270.0334165523604, + 5461.333333326708))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=5.728437460831947e-44, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(116.7944750275836, + 1931.995993191814))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.99, + alternative='two.sided'), + RResults(pvalue=0.574111858126088, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0.7949398282935892, + 1.436229679394333))), + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.1300759363430016, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0, + 1.797867027270803))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.0185217259520665, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0, + 0.6785254803404526))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.9782608695652173, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0, + 127.8497388102893))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.05625775074399956, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0, + 1.032332939718425))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.1808979350599346, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0, + 1.502407513296985))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.1652173913043479, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0, + 1.820421051562392))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.0565217391304348, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 1.06224603077045))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.5, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 19.00192394479939))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.4999999999999999, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 19.00192394479939))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(0, + 3045.460216525746))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(0, + 1186.440170942579))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.95, + alternative='less'), + RResults(pvalue=0.7416227010368963, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0, + 1.293551891610822))), + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.1300759363430016, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0, + 4.375946050832565))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.0185217259520665, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0, + 1.235282118191202))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.9782608695652173, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0, + 657.2063583945989))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.05625775074399956, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0, + 1.498867660683128))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.1808979350599346, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0, + 2.186159386716762))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.1652173913043479, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0, + 3.335351451901569))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.0565217391304348, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 2.075407697450433))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.5, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 99.00009507969122))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.4999999999999999, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + 99.00009507969123))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(0, + 4503.078257659934))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=1, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(0, + 1811.766127544222))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.99, + alternative='less'), + RResults(pvalue=0.7416227010368963, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0, + 1.396522811516685))), + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.979790445314723, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0.05119649909830196, + Inf))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.9990149169715733, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0.007163749169069961, + Inf))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.1652173913043478, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0.5493234651081089, + Inf))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.9849086665340765, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0.1003538933958604, + Inf))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.9330176609214881, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0.146507416280863, + Inf))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.9782608695652174, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0.007821681994077808, + Inf))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.02380952380952382, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(1.487678929918272, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.0238095238095238, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(1.487678929918272, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=2.005657880388915e-122, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(397.784359748113, + Inf))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=5.728437460831983e-44, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(174.7148056880929, + Inf))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.95, + alternative='greater'), + RResults(pvalue=0.2959825901308897, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0.8828406663967776, + Inf))), + (Parameters(table=[[100, 2], [1000, 5]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.979790445314723, + conditional_odds_ratio=0.25055839934223, + conditional_odds_ratio_ci=(0.03045407081240429, + Inf))), + (Parameters(table=[[2, 7], [8, 2]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.9990149169715733, + conditional_odds_ratio=0.0858623513573622, + conditional_odds_ratio_ci=(0.002768053063547901, + Inf))), + (Parameters(table=[[5, 1], [10, 10]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.1652173913043478, + conditional_odds_ratio=4.725646047336587, + conditional_odds_ratio_ci=(0.2998184792279909, + Inf))), + (Parameters(table=[[5, 15], [20, 20]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.9849086665340765, + conditional_odds_ratio=0.3394396617440851, + conditional_odds_ratio_ci=(0.06180414342643172, + Inf))), + (Parameters(table=[[5, 16], [16, 25]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.9330176609214881, + conditional_odds_ratio=0.4937791394540491, + conditional_odds_ratio_ci=(0.09037094010066403, + Inf))), + (Parameters(table=[[10, 5], [10, 1]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.9782608695652174, + conditional_odds_ratio=0.2116112781158479, + conditional_odds_ratio_ci=(0.001521592095430679, + Inf))), + (Parameters(table=[[10, 5], [10, 0]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[5, 0], [1, 4]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.02380952380952382, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0.6661157890359722, + Inf))), + (Parameters(table=[[0, 5], [1, 4]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[5, 1], [0, 4]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.0238095238095238, + conditional_odds_ratio=Inf, + conditional_odds_ratio_ci=(0.6661157890359725, + Inf))), + (Parameters(table=[[0, 1], [3, 2]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=1, + conditional_odds_ratio=0, + conditional_odds_ratio_ci=(0, + Inf))), + (Parameters(table=[[200, 7], [8, 300]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=2.005657880388915e-122, + conditional_odds_ratio=977.7866978606228, + conditional_odds_ratio_ci=(297.9619252357688, + Inf))), + (Parameters(table=[[28, 21], [6, 1957]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=5.728437460831983e-44, + conditional_odds_ratio=425.2403028434684, + conditional_odds_ratio_ci=(130.3213490295859, + Inf))), + (Parameters(table=[[190, 800], [200, 900]], + confidence_level=0.99, + alternative='greater'), + RResults(pvalue=0.2959825901308897, + conditional_odds_ratio=1.068697577856801, + conditional_odds_ratio_ci=(0.8176272148267533, + Inf))), +] diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/AtmWtAg.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/AtmWtAg.dat new file mode 100644 index 0000000000000000000000000000000000000000..30537565fe8c47f74da0e63a39f4b46600f7768f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/AtmWtAg.dat @@ -0,0 +1,108 @@ +NIST/ITL StRD +Dataset Name: AtmWtAg (AtmWtAg.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 108) + + +Procedure: Analysis of Variance + + +Reference: Powell, L.J., Murphy, T.J. and Gramlich, J.W. (1982). + "The Absolute Isotopic Abundance & Atomic Weight + of a Reference Sample of Silver". + NBS Journal of Research, 87, pp. 9-19. + + +Data: 1 Factor + 2 Treatments + 24 Replicates/Cell + 48 Observations + 7 Constant Leading Digits + Average Level of Difficulty + Observed Data + + +Model: 3 Parameters (mu, tau_1, tau_2) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + + +Between Instrument 1 3.63834187500000E-09 3.63834187500000E-09 1.59467335677930E+01 +Within Instrument 46 1.04951729166667E-08 2.28155932971014E-10 + + Certified R-Squared 2.57426544538321E-01 + + Certified Residual + Standard Deviation 1.51048314446410E-05 + + + + + + + + + + + +Data: Instrument AgWt + 1 107.8681568 + 1 107.8681465 + 1 107.8681572 + 1 107.8681785 + 1 107.8681446 + 1 107.8681903 + 1 107.8681526 + 1 107.8681494 + 1 107.8681616 + 1 107.8681587 + 1 107.8681519 + 1 107.8681486 + 1 107.8681419 + 1 107.8681569 + 1 107.8681508 + 1 107.8681672 + 1 107.8681385 + 1 107.8681518 + 1 107.8681662 + 1 107.8681424 + 1 107.8681360 + 1 107.8681333 + 1 107.8681610 + 1 107.8681477 + 2 107.8681079 + 2 107.8681344 + 2 107.8681513 + 2 107.8681197 + 2 107.8681604 + 2 107.8681385 + 2 107.8681642 + 2 107.8681365 + 2 107.8681151 + 2 107.8681082 + 2 107.8681517 + 2 107.8681448 + 2 107.8681198 + 2 107.8681482 + 2 107.8681334 + 2 107.8681609 + 2 107.8681101 + 2 107.8681512 + 2 107.8681469 + 2 107.8681360 + 2 107.8681254 + 2 107.8681261 + 2 107.8681450 + 2 107.8681368 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SiRstv.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SiRstv.dat new file mode 100644 index 0000000000000000000000000000000000000000..18ea8971fd7a4d67800dafe98ac5ea5acef53025 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SiRstv.dat @@ -0,0 +1,85 @@ +NIST/ITL StRD +Dataset Name: SiRstv (SiRstv.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 85) + + +Procedure: Analysis of Variance + + +Reference: Ehrstein, James and Croarkin, M. Carroll. + Unpublished NIST dataset. + + +Data: 1 Factor + 5 Treatments + 5 Replicates/Cell + 25 Observations + 3 Constant Leading Digits + Lower Level of Difficulty + Observed Data + + +Model: 6 Parameters (mu,tau_1, ... , tau_5) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Instrument 4 5.11462616000000E-02 1.27865654000000E-02 1.18046237440255E+00 +Within Instrument 20 2.16636560000000E-01 1.08318280000000E-02 + + Certified R-Squared 1.90999039051129E-01 + + Certified Residual + Standard Deviation 1.04076068334656E-01 + + + + + + + + + + + + +Data: Instrument Resistance + 1 196.3052 + 1 196.1240 + 1 196.1890 + 1 196.2569 + 1 196.3403 + 2 196.3042 + 2 196.3825 + 2 196.1669 + 2 196.3257 + 2 196.0422 + 3 196.1303 + 3 196.2005 + 3 196.2889 + 3 196.0343 + 3 196.1811 + 4 196.2795 + 4 196.1748 + 4 196.1494 + 4 196.1485 + 4 195.9885 + 5 196.2119 + 5 196.1051 + 5 196.1850 + 5 196.0052 + 5 196.2090 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs01.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs01.dat new file mode 100644 index 0000000000000000000000000000000000000000..945b24bf35422152a5faba73ed054ab78fda1bdf --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs01.dat @@ -0,0 +1,249 @@ +NIST/ITL StRD +Dataset Name: SmLs01 (SmLs01.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 249) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 21 Replicates/Cell + 189 Observations + 1 Constant Leading Digit + Lower Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.68000000000000E+00 2.10000000000000E-01 2.10000000000000E+01 +Within Treatment 180 1.80000000000000E+00 1.00000000000000E-02 + + Certified R-Squared 4.82758620689655E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1.4 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 2 1.3 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 3 1.5 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 4 1.3 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 5 1.5 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 6 1.3 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 7 1.5 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 8 1.3 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 9 1.5 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs02.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs02.dat new file mode 100644 index 0000000000000000000000000000000000000000..ee76633a660a48225064bbb86a25f6a2f36c6d9a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs02.dat @@ -0,0 +1,1869 @@ +NIST/ITL StRD +Dataset Name: SmLs02 (SmLs02.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 1869) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 201 Replicates/Cell + 1809 Observations + 1 Constant Leading Digit + Lower Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60800000000000E+01 2.01000000000000E+00 2.01000000000000E+02 +Within Treatment 1800 1.80000000000000E+01 1.00000000000000E-02 + + Certified R-Squared 4.71830985915493E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1.4 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 2 1.3 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 3 1.5 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 4 1.3 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 5 1.5 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 6 1.3 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 7 1.5 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 8 1.3 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 9 1.5 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs03.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs03.dat new file mode 100644 index 0000000000000000000000000000000000000000..55dfa2313ffb152709c58b47c0058567b710d903 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs03.dat @@ -0,0 +1,18069 @@ +NIST/ITL StRD +Dataset Name: SmLs03 (SmLs03.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 18069) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 2001 Replicates/Cell + 18009 Observations + 1 Constant Leading Digit + Lower Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60080000000000E+02 2.00100000000000E+01 2.00100000000000E+03 +Within Treatment 18000 1.80000000000000E+02 1.00000000000000E-02 + + Certified R-Squared 4.70712773465067E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1.4 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 1 1.3 + 1 1.5 + 2 1.3 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 2 1.2 + 2 1.4 + 3 1.5 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 3 1.4 + 3 1.6 + 4 1.3 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 4 1.2 + 4 1.4 + 5 1.5 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 5 1.4 + 5 1.6 + 6 1.3 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 6 1.2 + 6 1.4 + 7 1.5 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 7 1.4 + 7 1.6 + 8 1.3 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 8 1.2 + 8 1.4 + 9 1.5 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 + 9 1.4 + 9 1.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs04.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs04.dat new file mode 100644 index 0000000000000000000000000000000000000000..6a2a9fc935a56989b166de9b23f3df3bc4f64879 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs04.dat @@ -0,0 +1,249 @@ +NIST/ITL StRD +Dataset Name: SmLs04 (SmLs04.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 249) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 21 Replicates/Cell + 189 Observations + 7 Constant Leading Digits + Average Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.68000000000000E+00 2.10000000000000E-01 2.10000000000000E+01 +Within Treatment 180 1.80000000000000E+00 1.00000000000000E-02 + + Certified R-Squared 4.82758620689655E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000.4 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 2 1000000.3 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 3 1000000.5 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 4 1000000.3 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 5 1000000.5 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 6 1000000.3 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 7 1000000.5 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 8 1000000.3 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 9 1000000.5 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs05.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs05.dat new file mode 100644 index 0000000000000000000000000000000000000000..fe11c40b5f51aefc81d4d1501a74e627f2b2d992 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs05.dat @@ -0,0 +1,1869 @@ +NIST/ITL StRD +Dataset Name: SmLs05 (SmLs05.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 1869) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 201 Replicates/Cell + 1809 Observations + 7 Constant Leading Digits + Average Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60800000000000E+01 2.01000000000000E+00 2.01000000000000E+02 +Within Treatment 1800 1.80000000000000E+01 1.00000000000000E-02 + + Certified R-Squared 4.71830985915493E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000.4 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 2 1000000.3 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 3 1000000.5 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 4 1000000.3 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 5 1000000.5 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 6 1000000.3 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 7 1000000.5 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 8 1000000.3 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 9 1000000.5 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs06.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs06.dat new file mode 100644 index 0000000000000000000000000000000000000000..602e4fbdaa26bbb8d95ce78d1f48dbbfa883e7e9 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs06.dat @@ -0,0 +1,18069 @@ +NIST/ITL StRD +Dataset Name: SmLs06 (SmLs06.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 18069) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 2001 Replicates/Cell + 18009 Observations + 7 Constant Leading Digits + Average Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60080000000000E+02 2.00100000000000E+01 2.00100000000000E+03 +Within Treatment 18000 1.80000000000000E+02 1.00000000000000E-02 + + Certified R-Squared 4.70712773465067E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000.4 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 1 1000000.3 + 1 1000000.5 + 2 1000000.3 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 2 1000000.2 + 2 1000000.4 + 3 1000000.5 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 3 1000000.4 + 3 1000000.6 + 4 1000000.3 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 4 1000000.2 + 4 1000000.4 + 5 1000000.5 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 5 1000000.4 + 5 1000000.6 + 6 1000000.3 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 6 1000000.2 + 6 1000000.4 + 7 1000000.5 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 7 1000000.4 + 7 1000000.6 + 8 1000000.3 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 8 1000000.2 + 8 1000000.4 + 9 1000000.5 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 + 9 1000000.4 + 9 1000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs07.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs07.dat new file mode 100644 index 0000000000000000000000000000000000000000..deeac955e65ffaf55838568baa54951efaf2662b --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs07.dat @@ -0,0 +1,249 @@ +NIST/ITL StRD +Dataset Name: SmLs07 (SmLs07.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 249) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 21 Replicates/Cell + 189 Observations + 13 Constant Leading Digits + Higher Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.68000000000000E+00 2.10000000000000E-01 2.10000000000000E+01 +Within Treatment 180 1.80000000000000E+00 1.00000000000000E-02 + + Certified R-Squared 4.82758620689655E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000000000.4 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 2 1000000000000.3 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 3 1000000000000.5 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 4 1000000000000.3 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 5 1000000000000.5 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 6 1000000000000.3 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 7 1000000000000.5 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 8 1000000000000.3 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 9 1000000000000.5 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs08.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs08.dat new file mode 100644 index 0000000000000000000000000000000000000000..c5ee643fb8c6ef849ab8e34352bc60f15c715a45 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs08.dat @@ -0,0 +1,1869 @@ +NIST/ITL StRD +Dataset Name: SmLs08 (SmLs08.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 1869) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 201 Replicates/Cell + 1809 Observations + 13 Constant Leading Digits + Higher Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60800000000000E+01 2.01000000000000E+00 2.01000000000000E+02 +Within Treatment 1800 1.80000000000000E+01 1.00000000000000E-02 + + Certified R-Squared 4.71830985915493E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000000000.4 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 2 1000000000000.3 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 3 1000000000000.5 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 4 1000000000000.3 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 5 1000000000000.5 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 6 1000000000000.3 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 7 1000000000000.5 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 8 1000000000000.3 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 9 1000000000000.5 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs09.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs09.dat new file mode 100644 index 0000000000000000000000000000000000000000..887905e355a2a13801f1b004187631f2301f7eef --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_anova/SmLs09.dat @@ -0,0 +1,18069 @@ +NIST/ITL StRD +Dataset Name: SmLs09 (SmLs09.dat) + + +File Format: ASCII + Certified Values (lines 41 to 47) + Data (lines 61 to 18069) + + +Procedure: Analysis of Variance + + +Reference: Simon, Stephen D. and Lesage, James P. (1989). + "Assessing the Accuracy of ANOVA Calculations in + Statistical Software". + Computational Statistics & Data Analysis, 8, pp. 325-332. + + +Data: 1 Factor + 9 Treatments + 2001 Replicates/Cell + 18009 Observations + 13 Constant Leading Digits + Higher Level of Difficulty + Generated Data + + +Model: 10 Parameters (mu,tau_1, ... , tau_9) + y_{ij} = mu + tau_i + epsilon_{ij} + + + + + + +Certified Values: + +Source of Sums of Mean +Variation df Squares Squares F Statistic + +Between Treatment 8 1.60080000000000E+02 2.00100000000000E+01 2.00100000000000E+03 +Within Treatment 18000 1.80000000000000E+02 1.00000000000000E-02 + + Certified R-Squared 4.70712773465067E-01 + + Certified Residual + Standard Deviation 1.00000000000000E-01 + + + + + + + + + + + + +Data: Treatment Response + 1 1000000000000.4 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 1 1000000000000.3 + 1 1000000000000.5 + 2 1000000000000.3 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 2 1000000000000.2 + 2 1000000000000.4 + 3 1000000000000.5 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 3 1000000000000.4 + 3 1000000000000.6 + 4 1000000000000.3 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 4 1000000000000.2 + 4 1000000000000.4 + 5 1000000000000.5 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 5 1000000000000.4 + 5 1000000000000.6 + 6 1000000000000.3 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 6 1000000000000.2 + 6 1000000000000.4 + 7 1000000000000.5 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 7 1000000000000.4 + 7 1000000000000.6 + 8 1000000000000.3 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 8 1000000000000.2 + 8 1000000000000.4 + 9 1000000000000.5 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 + 9 1000000000000.4 + 9 1000000000000.6 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_linregress/Norris.dat b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_linregress/Norris.dat new file mode 100644 index 0000000000000000000000000000000000000000..4bf8ed911cae75824b27e5f5d5e444e17fa8eae8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/nist_linregress/Norris.dat @@ -0,0 +1,97 @@ +NIST/ITL StRD +Dataset Name: Norris (Norris.dat) + +File Format: ASCII + Certified Values (lines 31 to 46) + Data (lines 61 to 96) + +Procedure: Linear Least Squares Regression + +Reference: Norris, J., NIST. + Calibration of Ozone Monitors. + +Data: 1 Response Variable (y) + 1 Predictor Variable (x) + 36 Observations + Lower Level of Difficulty + Observed Data + +Model: Linear Class + 2 Parameters (B0,B1) + + y = B0 + B1*x + e + + + + Certified Regression Statistics + + Standard Deviation + Parameter Estimate of Estimate + + B0 -0.262323073774029 0.232818234301152 + B1 1.00211681802045 0.429796848199937E-03 + + Residual + Standard Deviation 0.884796396144373 + + R-Squared 0.999993745883712 + + + Certified Analysis of Variance Table + +Source of Degrees of Sums of Mean +Variation Freedom Squares Squares F Statistic + +Regression 1 4255954.13232369 4255954.13232369 5436385.54079785 +Residual 34 26.6173985294224 0.782864662630069 + + + + + + + + + + + + + +Data: y x + 0.1 0.2 + 338.8 337.4 + 118.1 118.2 + 888.0 884.6 + 9.2 10.1 + 228.1 226.5 + 668.5 666.3 + 998.5 996.3 + 449.1 448.6 + 778.9 777.0 + 559.2 558.2 + 0.3 0.4 + 0.1 0.6 + 778.1 775.5 + 668.8 666.9 + 339.3 338.0 + 448.9 447.5 + 10.8 11.6 + 557.7 556.0 + 228.3 228.1 + 998.0 995.8 + 888.8 887.6 + 119.6 120.2 + 0.3 0.3 + 0.6 0.3 + 557.6 556.8 + 339.3 339.1 + 888.0 887.2 + 998.5 999.0 + 778.9 779.0 + 10.2 11.1 + 117.6 118.3 + 228.9 229.2 + 668.4 669.1 + 449.2 448.9 + 0.2 0.5 + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/studentized_range_mpmath_ref.json b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/studentized_range_mpmath_ref.json new file mode 100644 index 0000000000000000000000000000000000000000..bb971286cf85b28738a80bacececfb90c2566782 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/data/studentized_range_mpmath_ref.json @@ -0,0 +1,1499 @@ +{ + "COMMENT": "!!!!!! THIS FILE WAS AUTOGENERATED BY RUNNING `python studentized_range_mpmath_ref.py` !!!!!!", + "moment_data": [ + { + "src_case": { + "m": 0, + "k": 3, + "v": 10, + "expected_atol": 1e-09, + "expected_rtol": 1e-09 + }, + "mp_result": 1.0 + }, + { + "src_case": { + "m": 1, + "k": 3, + "v": 10, + "expected_atol": 1e-09, + "expected_rtol": 1e-09 + }, + "mp_result": 1.8342745127927962 + }, + { + "src_case": { + "m": 2, + "k": 3, + "v": 10, + "expected_atol": 1e-09, + "expected_rtol": 1e-09 + }, + "mp_result": 4.567483357831711 + }, + { + "src_case": { + "m": 3, + "k": 3, + "v": 10, + "expected_atol": 1e-09, + "expected_rtol": 1e-09 + }, + "mp_result": 14.412156886227011 + }, + { + "src_case": { + "m": 4, + "k": 3, + "v": 10, + "expected_atol": 1e-09, + "expected_rtol": 1e-09 + }, + "mp_result": 56.012250366720444 + } + ], + "cdf_data": [ + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0027502772229359594 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.8544145010066327e-12 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0027520560662338336 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.39089126131273e-13 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.002752437649536182 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.0862189999210748e-12 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.002752755744313648 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0027527430186246545 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.002752666667812431 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.505275157135514e-24 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.8546698113384126e-25 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.7362668562706085e-11 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 5.571947730052616e-26 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.032619249089036e-27 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.539763646681808e-22 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.618313512511099e-12 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 4.919231733354114e-28 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.159348906295542e-13 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.22331624289542043 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.2395624637676257 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23510918942128056 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23786536230099864 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.000651656693149116 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.2401356460422021 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.003971273224673166 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0008732969319364606 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.24023154593376422 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.001300816146573152 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.5682573722040226e-07 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0005841098057517027 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.2267674885784e-05 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0005731712496327297 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.746798012658064e-06 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 5.807700350854172e-07 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.147637957472628e-08 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 8.306675539750552e-08 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.8711786295203324 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9818862781476212 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9566506502400175 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9849546621386962 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9731488893573804 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.8450530667988544 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.6164875232404174 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9845292772767739 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.8079691517949077 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.7573606942645745 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.8587525248147736 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.8611036193280976 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.46523135355387657 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.6318042819232383 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.5574947140294286 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.5970517763141937 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.6493671527818267 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.6466699776044968 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9881335633712994 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999861266821 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.999908236635449 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999978467928313 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999996690216 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999993640496 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9570401457077894 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999997977351971 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9991738325963548 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999730883609333 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999905199205 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999950566264 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9312318042339768 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999991743904675 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9977643922032399 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999054426012515 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999602948055 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.9999999792458618 + } + ], + "pdf_data": [ + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.05487847613526332 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.564099684606509e-10 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.05494947290360002 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 8.442593793786411e-11 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.054964710604860405 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.764441961563576e-11 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.05497690690332341 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.05497385731702228 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 4.758021225803992e-22 + }, + { + "src_case": { + "q": 0.1, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.054977415200879516 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.8004731453548083e-19 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.5564176176604816e-09 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.342768070688728e-24 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.454372265306114e-10 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.9138464398429654e-25 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 5.266341131767418e-23 + }, + { + "src_case": { + "q": 0.1, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 8.234556126446594e-11 + }, + { + "src_case": { + "q": 0.1, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.32929780487562e-26 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.36083736990527154 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.4137959132282269 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.4080239698771056 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.398772020275752 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.4160873922094346 + }, + { + "src_case": { + "q": 1, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.4157583991350054 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.005210720148451848 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.02575314059867804 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.009782573637596617 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.006818708302379005 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0047089182958790715 + }, + { + "src_case": { + "q": 1, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.004627085294166373 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0010886280311369462 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.630674470916427e-06 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 4.121713278199428e-05 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 9.319506007252685e-06 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.5585754418789747e-06 + }, + { + "src_case": { + "q": 1, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.4190335899441991e-06 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.07185383302009114 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.050268901219386576 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.03321056847176124 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.04044172384981084 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.030571365659999617 + }, + { + "src_case": { + "q": 4, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.030120779149073032 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.17501664247670937 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.22374394725370736 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23246597521020534 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23239043677504484 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23057775622748988 + }, + { + "src_case": { + "q": 4, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.23012666145240815 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.2073676639537027 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.3245990542431859 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0033733228559870584 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 7.728665739003835e-05 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.38244500549096866 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.45434978340834464 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.43334135870667473 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.159522630228393e-09 + }, + { + "src_case": { + "q": 4, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.45807877248528855 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.5303467191175695e-08 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.121281850105421e-06 + }, + { + "src_case": { + "q": 10, + "k": 3, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.1901591191700855e-09 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0006784051704217357 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.011845582636101885 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.844183552674918e-05 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 3.215093171597309e-08 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 5.125792577534542e-07 + }, + { + "src_case": { + "q": 10, + "k": 10, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.7759015355532446e-08 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 10, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.0017957646258393628 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 3, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.018534407764819284 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 20, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 0.00013316083413164858 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 50, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 2.082489228991225e-06 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 100, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 1.3444226792257012e-07 + }, + { + "src_case": { + "q": 10, + "k": 20, + "v": 120, + "expected_atol": 1e-11, + "expected_rtol": 1e-11 + }, + "mp_result": 7.446912854228521e-08 + } + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_axis_nan_policy.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_axis_nan_policy.py new file mode 100644 index 0000000000000000000000000000000000000000..b4a0b30f8374eea644f80c63aa2c3efadbf4695f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_axis_nan_policy.py @@ -0,0 +1,1188 @@ +# Many scipy.stats functions support `axis` and `nan_policy` parameters. +# When the two are combined, it can be tricky to get all the behavior just +# right. This file contains a suite of common tests for scipy.stats functions +# that support `axis` and `nan_policy` and additional tests for some associated +# functions in stats._util. + +from itertools import product, combinations_with_replacement, permutations +import re +import pickle +import pytest + +import numpy as np +from numpy.testing import assert_allclose, assert_equal, suppress_warnings +from scipy import stats +from scipy.stats import norm # type: ignore[attr-defined] +from scipy.stats._axis_nan_policy import _masked_arrays_2_sentinel_arrays +from scipy._lib._util import AxisError + + +def unpack_ttest_result(res): + low, high = res.confidence_interval() + return (res.statistic, res.pvalue, res.df, res._standard_error, + res._estimate, low, high) + + +def _get_ttest_ci(ttest): + # get a function that returns the CI bounds of provided `ttest` + def ttest_ci(*args, **kwargs): + res = ttest(*args, **kwargs) + return res.confidence_interval() + return ttest_ci + + +axis_nan_policy_cases = [ + # function, args, kwds, number of samples, number of outputs, + # ... paired, unpacker function + # args, kwds typically aren't needed; just showing that they work + (stats.kruskal, tuple(), dict(), 3, 2, False, None), # 4 samples is slow + (stats.ranksums, ('less',), dict(), 2, 2, False, None), + (stats.mannwhitneyu, tuple(), {'method': 'asymptotic'}, 2, 2, False, None), + (stats.wilcoxon, ('pratt',), {'mode': 'auto'}, 2, 2, True, + lambda res: (res.statistic, res.pvalue)), + (stats.wilcoxon, tuple(), dict(), 1, 2, True, + lambda res: (res.statistic, res.pvalue)), + (stats.wilcoxon, tuple(), {'mode': 'approx'}, 1, 3, True, + lambda res: (res.statistic, res.pvalue, res.zstatistic)), + (stats.gmean, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.hmean, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.pmean, (1.42,), dict(), 1, 1, False, lambda x: (x,)), + (stats.sem, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.iqr, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.kurtosis, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.skew, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.kstat, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.kstatvar, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.moment, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.moment, tuple(), dict(order=[1, 2]), 1, 2, False, None), + (stats.jarque_bera, tuple(), dict(), 1, 2, False, None), + (stats.ttest_1samp, (np.array([0]),), dict(), 1, 7, False, + unpack_ttest_result), + (stats.ttest_rel, tuple(), dict(), 2, 7, True, unpack_ttest_result), + (stats.ttest_ind, tuple(), dict(), 2, 7, False, unpack_ttest_result), + (_get_ttest_ci(stats.ttest_1samp), (0,), dict(), 1, 2, False, None), + (_get_ttest_ci(stats.ttest_rel), tuple(), dict(), 2, 2, True, None), + (_get_ttest_ci(stats.ttest_ind), tuple(), dict(), 2, 2, False, None), + (stats.mode, tuple(), dict(), 1, 2, True, lambda x: (x.mode, x.count)), + (stats.differential_entropy, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.variation, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.friedmanchisquare, tuple(), dict(), 3, 2, True, None), + (stats.brunnermunzel, tuple(), dict(), 2, 2, False, None), + (stats.mood, tuple(), {}, 2, 2, False, None), + (stats.shapiro, tuple(), {}, 1, 2, False, None), + (stats.ks_1samp, (norm().cdf,), dict(), 1, 4, False, + lambda res: (*res, res.statistic_location, res.statistic_sign)), + (stats.ks_2samp, tuple(), dict(), 2, 4, False, + lambda res: (*res, res.statistic_location, res.statistic_sign)), + (stats.kstest, (norm().cdf,), dict(), 1, 4, False, + lambda res: (*res, res.statistic_location, res.statistic_sign)), + (stats.kstest, tuple(), dict(), 2, 4, False, + lambda res: (*res, res.statistic_location, res.statistic_sign)), + (stats.levene, tuple(), {}, 2, 2, False, None), + (stats.fligner, tuple(), {'center': 'trimmed', 'proportiontocut': 0.01}, + 2, 2, False, None), + (stats.ansari, tuple(), {}, 2, 2, False, None), + (stats.entropy, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.entropy, tuple(), dict(), 2, 1, True, lambda x: (x,)), + (stats.skewtest, tuple(), dict(), 1, 2, False, None), + (stats.kurtosistest, tuple(), dict(), 1, 2, False, None), + (stats.normaltest, tuple(), dict(), 1, 2, False, None), + (stats.cramervonmises, ("norm",), dict(), 1, 2, False, + lambda res: (res.statistic, res.pvalue)), + (stats.cramervonmises_2samp, tuple(), dict(), 2, 2, False, + lambda res: (res.statistic, res.pvalue)), + (stats.epps_singleton_2samp, tuple(), dict(), 2, 2, False, None), + (stats.bartlett, tuple(), {}, 2, 2, False, None), + (stats.tmean, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.tvar, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.tmin, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.tmax, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.tstd, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.tsem, tuple(), {}, 1, 1, False, lambda x: (x,)), + (stats.circmean, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.circvar, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.circstd, tuple(), dict(), 1, 1, False, lambda x: (x,)), + (stats.f_oneway, tuple(), {}, 2, 2, False, None), + (stats.alexandergovern, tuple(), {}, 2, 2, False, + lambda res: (res.statistic, res.pvalue)), + (stats.combine_pvalues, tuple(), {}, 1, 2, False, None), +] + +# If the message is one of those expected, put nans in +# appropriate places of `statistics` and `pvalues` +too_small_messages = {"The input contains nan", # for nan_policy="raise" + "Degrees of freedom <= 0 for slice", + "x and y should have at least 5 elements", + "Data must be at least length 3", + "The sample must contain at least two", + "x and y must contain at least two", + "division by zero", + "Mean of empty slice", + "Data passed to ks_2samp must not be empty", + "Not enough test observations", + "Not enough other observations", + "Not enough observations.", + "At least one observation is required", + "zero-size array to reduction operation maximum", + "`x` and `y` must be of nonzero size.", + "The exact distribution of the Wilcoxon test", + "Data input must not be empty", + "Window length (0) must be positive and less", + "Window length (1) must be positive and less", + "Window length (2) must be positive and less", + "skewtest is not valid with less than", + "kurtosistest requires at least 5", + "attempt to get argmax of an empty sequence", + "No array values within given limits", + "Input sample size must be greater than one.",} + +# If the message is one of these, results of the function may be inaccurate, +# but NaNs are not to be placed +inaccuracy_messages = {"Precision loss occurred in moment calculation", + "Sample size too small for normal approximation."} + +# For some functions, nan_policy='propagate' should not just return NaNs +override_propagate_funcs = {stats.mode} + +# For some functions, empty arrays produce non-NaN results +empty_special_case_funcs = {stats.entropy} + +def _mixed_data_generator(n_samples, n_repetitions, axis, rng, + paired=False): + # generate random samples to check the response of hypothesis tests to + # samples with different (but broadcastable) shapes and various + # nan patterns (e.g. all nans, some nans, no nans) along axis-slices + + data = [] + for i in range(n_samples): + n_patterns = 6 # number of distinct nan patterns + n_obs = 20 if paired else 20 + i # observations per axis-slice + x = np.ones((n_repetitions, n_patterns, n_obs)) * np.nan + + for j in range(n_repetitions): + samples = x[j, :, :] + + # case 0: axis-slice with all nans (0 reals) + # cases 1-3: axis-slice with 1-3 reals (the rest nans) + # case 4: axis-slice with mostly (all but two) reals + # case 5: axis slice with all reals + for k, n_reals in enumerate([0, 1, 2, 3, n_obs-2, n_obs]): + # for cases 1-3, need paired nansw to be in the same place + indices = rng.permutation(n_obs)[:n_reals] + samples[k, indices] = rng.random(size=n_reals) + + # permute the axis-slices just to show that order doesn't matter + samples[:] = rng.permutation(samples, axis=0) + + # For multi-sample tests, we want to test broadcasting and check + # that nan policy works correctly for each nan pattern for each input. + # This takes care of both simultaneously. + new_shape = [n_repetitions] + [1]*n_samples + [n_obs] + new_shape[1 + i] = 6 + x = x.reshape(new_shape) + + x = np.moveaxis(x, -1, axis) + data.append(x) + return data + + +def _homogeneous_data_generator(n_samples, n_repetitions, axis, rng, + paired=False, all_nans=True): + # generate random samples to check the response of hypothesis tests to + # samples with different (but broadcastable) shapes and homogeneous + # data (all nans or all finite) + data = [] + for i in range(n_samples): + n_obs = 20 if paired else 20 + i # observations per axis-slice + shape = [n_repetitions] + [1]*n_samples + [n_obs] + shape[1 + i] = 2 + x = np.ones(shape) * np.nan if all_nans else rng.random(shape) + x = np.moveaxis(x, -1, axis) + data.append(x) + return data + + +def nan_policy_1d(hypotest, data1d, unpacker, *args, n_outputs=2, + nan_policy='raise', paired=False, _no_deco=True, **kwds): + # Reference implementation for how `nan_policy` should work for 1d samples + + if nan_policy == 'raise': + for sample in data1d: + if np.any(np.isnan(sample)): + raise ValueError("The input contains nan values") + + elif (nan_policy == 'propagate' + and hypotest not in override_propagate_funcs): + # For all hypothesis tests tested, returning nans is the right thing. + # But many hypothesis tests don't propagate correctly (e.g. they treat + # np.nan the same as np.inf, which doesn't make sense when ranks are + # involved) so override that behavior here. + for sample in data1d: + if np.any(np.isnan(sample)): + return np.full(n_outputs, np.nan) + + elif nan_policy == 'omit': + # manually omit nans (or pairs in which at least one element is nan) + if not paired: + data1d = [sample[~np.isnan(sample)] for sample in data1d] + else: + nan_mask = np.isnan(data1d[0]) + for sample in data1d[1:]: + nan_mask = np.logical_or(nan_mask, np.isnan(sample)) + data1d = [sample[~nan_mask] for sample in data1d] + + return unpacker(hypotest(*data1d, *args, _no_deco=_no_deco, **kwds)) + + +@pytest.mark.filterwarnings('ignore::RuntimeWarning') +@pytest.mark.filterwarnings('ignore::UserWarning') +@pytest.mark.parametrize(("hypotest", "args", "kwds", "n_samples", "n_outputs", + "paired", "unpacker"), axis_nan_policy_cases) +@pytest.mark.parametrize(("nan_policy"), ("propagate", "omit", "raise")) +@pytest.mark.parametrize(("axis"), (1,)) +@pytest.mark.parametrize(("data_generator"), ("mixed",)) +def test_axis_nan_policy_fast(hypotest, args, kwds, n_samples, n_outputs, + paired, unpacker, nan_policy, axis, + data_generator): + _axis_nan_policy_test(hypotest, args, kwds, n_samples, n_outputs, paired, + unpacker, nan_policy, axis, data_generator) + + +@pytest.mark.slow +@pytest.mark.filterwarnings('ignore::RuntimeWarning') +@pytest.mark.filterwarnings('ignore::UserWarning') +@pytest.mark.parametrize(("hypotest", "args", "kwds", "n_samples", "n_outputs", + "paired", "unpacker"), axis_nan_policy_cases) +@pytest.mark.parametrize(("nan_policy"), ("propagate", "omit", "raise")) +@pytest.mark.parametrize(("axis"), range(-3, 3)) +@pytest.mark.parametrize(("data_generator"), + ("all_nans", "all_finite", "mixed")) +def test_axis_nan_policy_full(hypotest, args, kwds, n_samples, n_outputs, + paired, unpacker, nan_policy, axis, + data_generator): + _axis_nan_policy_test(hypotest, args, kwds, n_samples, n_outputs, paired, + unpacker, nan_policy, axis, data_generator) + + +def _axis_nan_policy_test(hypotest, args, kwds, n_samples, n_outputs, paired, + unpacker, nan_policy, axis, data_generator): + # Tests the 1D and vectorized behavior of hypothesis tests against a + # reference implementation (nan_policy_1d with np.ndenumerate) + + # Some hypothesis tests return a non-iterable that needs an `unpacker` to + # extract the statistic and p-value. For those that don't: + if not unpacker: + def unpacker(res): + return res + + rng = np.random.default_rng(0) + + # Generate multi-dimensional test data with all important combinations + # of patterns of nans along `axis` + n_repetitions = 3 # number of repetitions of each pattern + data_gen_kwds = {'n_samples': n_samples, 'n_repetitions': n_repetitions, + 'axis': axis, 'rng': rng, 'paired': paired} + if data_generator == 'mixed': + inherent_size = 6 # number of distinct types of patterns + data = _mixed_data_generator(**data_gen_kwds) + elif data_generator == 'all_nans': + inherent_size = 2 # hard-coded in _homogeneous_data_generator + data_gen_kwds['all_nans'] = True + data = _homogeneous_data_generator(**data_gen_kwds) + elif data_generator == 'all_finite': + inherent_size = 2 # hard-coded in _homogeneous_data_generator + data_gen_kwds['all_nans'] = False + data = _homogeneous_data_generator(**data_gen_kwds) + + output_shape = [n_repetitions] + [inherent_size]*n_samples + + # To generate reference behavior to compare against, loop over the axis- + # slices in data. Make indexing easier by moving `axis` to the end and + # broadcasting all samples to the same shape. + data_b = [np.moveaxis(sample, axis, -1) for sample in data] + data_b = [np.broadcast_to(sample, output_shape + [sample.shape[-1]]) + for sample in data_b] + statistics = np.zeros(output_shape) + pvalues = np.zeros(output_shape) + + for i, _ in np.ndenumerate(statistics): + data1d = [sample[i] for sample in data_b] + with np.errstate(divide='ignore', invalid='ignore'): + try: + res1d = nan_policy_1d(hypotest, data1d, unpacker, *args, + n_outputs=n_outputs, + nan_policy=nan_policy, + paired=paired, _no_deco=True, **kwds) + + # Eventually we'll check the results of a single, vectorized + # call of `hypotest` against the arrays `statistics` and + # `pvalues` populated using the reference `nan_policy_1d`. + # But while we're at it, check the results of a 1D call to + # `hypotest` against the reference `nan_policy_1d`. + res1db = unpacker(hypotest(*data1d, *args, + nan_policy=nan_policy, **kwds)) + assert_equal(res1db[0], res1d[0]) + if len(res1db) == 2: + assert_equal(res1db[1], res1d[1]) + + # When there is not enough data in 1D samples, many existing + # hypothesis tests raise errors instead of returning nans . + # For vectorized calls, we put nans in the corresponding elements + # of the output. + except (RuntimeWarning, UserWarning, ValueError, + ZeroDivisionError) as e: + + # whatever it is, make sure same error is raised by both + # `nan_policy_1d` and `hypotest` + with pytest.raises(type(e), match=re.escape(str(e))): + nan_policy_1d(hypotest, data1d, unpacker, *args, + n_outputs=n_outputs, nan_policy=nan_policy, + paired=paired, _no_deco=True, **kwds) + with pytest.raises(type(e), match=re.escape(str(e))): + hypotest(*data1d, *args, nan_policy=nan_policy, **kwds) + + if any([str(e).startswith(message) + for message in too_small_messages]): + res1d = np.full(n_outputs, np.nan) + elif any([str(e).startswith(message) + for message in inaccuracy_messages]): + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + sup.filter(UserWarning) + res1d = nan_policy_1d(hypotest, data1d, unpacker, + *args, n_outputs=n_outputs, + nan_policy=nan_policy, + paired=paired, _no_deco=True, + **kwds) + else: + raise e + statistics[i] = res1d[0] + if len(res1d) == 2: + pvalues[i] = res1d[1] + + # Perform a vectorized call to the hypothesis test. + # If `nan_policy == 'raise'`, check that it raises the appropriate error. + # If not, compare against the output against `statistics` and `pvalues` + if nan_policy == 'raise' and not data_generator == "all_finite": + message = 'The input contains nan values' + with pytest.raises(ValueError, match=message): + hypotest(*data, axis=axis, nan_policy=nan_policy, *args, **kwds) + + else: + with suppress_warnings() as sup, \ + np.errstate(divide='ignore', invalid='ignore'): + sup.filter(RuntimeWarning, "Precision loss occurred in moment") + sup.filter(UserWarning, "Sample size too small for normal " + "approximation.") + res = unpacker(hypotest(*data, axis=axis, nan_policy=nan_policy, + *args, **kwds)) + assert_allclose(res[0], statistics, rtol=1e-15) + assert_equal(res[0].dtype, statistics.dtype) + + if len(res) == 2: + assert_allclose(res[1], pvalues, rtol=1e-15) + assert_equal(res[1].dtype, pvalues.dtype) + + +@pytest.mark.filterwarnings('ignore::RuntimeWarning') +@pytest.mark.parametrize(("hypotest", "args", "kwds", "n_samples", "n_outputs", + "paired", "unpacker"), axis_nan_policy_cases) +@pytest.mark.parametrize(("nan_policy"), ("propagate", "omit", "raise")) +@pytest.mark.parametrize(("data_generator"), + ("all_nans", "all_finite", "mixed", "empty")) +def test_axis_nan_policy_axis_is_None(hypotest, args, kwds, n_samples, + n_outputs, paired, unpacker, nan_policy, + data_generator): + # check for correct behavior when `axis=None` + + if not unpacker: + def unpacker(res): + return res + + rng = np.random.default_rng(0) + + if data_generator == "empty": + data = [rng.random((2, 0)) for i in range(n_samples)] + else: + data = [rng.random((2, 20)) for i in range(n_samples)] + + if data_generator == "mixed": + masks = [rng.random((2, 20)) > 0.9 for i in range(n_samples)] + for sample, mask in zip(data, masks): + sample[mask] = np.nan + elif data_generator == "all_nans": + data = [sample * np.nan for sample in data] + + data_raveled = [sample.ravel() for sample in data] + + if nan_policy == 'raise' and data_generator not in {"all_finite", "empty"}: + message = 'The input contains nan values' + + # check for correct behavior whether or not data is 1d to begin with + with pytest.raises(ValueError, match=message): + hypotest(*data, axis=None, nan_policy=nan_policy, + *args, **kwds) + with pytest.raises(ValueError, match=message): + hypotest(*data_raveled, axis=None, nan_policy=nan_policy, + *args, **kwds) + + else: + # behavior of reference implementation with 1d input, hypotest with 1d + # input, and hypotest with Nd input should match, whether that means + # that outputs are equal or they raise the same exception + + ea_str, eb_str, ec_str = None, None, None + with np.errstate(divide='ignore', invalid='ignore'): + try: + res1da = nan_policy_1d(hypotest, data_raveled, unpacker, *args, + n_outputs=n_outputs, + nan_policy=nan_policy, paired=paired, + _no_deco=True, **kwds) + except (RuntimeWarning, ValueError, ZeroDivisionError) as ea: + ea_str = str(ea) + + try: + res1db = unpacker(hypotest(*data_raveled, *args, + nan_policy=nan_policy, **kwds)) + except (RuntimeWarning, ValueError, ZeroDivisionError) as eb: + eb_str = str(eb) + + try: + res1dc = unpacker(hypotest(*data, *args, axis=None, + nan_policy=nan_policy, **kwds)) + except (RuntimeWarning, ValueError, ZeroDivisionError) as ec: + ec_str = str(ec) + + if ea_str or eb_str or ec_str: + assert any([str(ea_str).startswith(message) + for message in too_small_messages]) + assert ea_str == eb_str == ec_str + else: + assert_equal(res1db, res1da) + assert_equal(res1dc, res1da) + for item in list(res1da) + list(res1db) + list(res1dc): + # Most functions naturally return NumPy numbers, which + # are drop-in replacements for the Python versions but with + # desirable attributes. Make sure this is consistent. + assert np.issubdtype(item.dtype, np.number) + +# Test keepdims for: +# - single-output and multi-output functions (gmean and mannwhitneyu) +# - Axis negative, positive, None, and tuple +# - 1D with no NaNs +# - 1D with NaN propagation +# - Zero-sized output +@pytest.mark.parametrize("nan_policy", ("omit", "propagate")) +@pytest.mark.parametrize( + ("hypotest", "args", "kwds", "n_samples", "unpacker"), + ((stats.gmean, tuple(), dict(), 1, lambda x: (x,)), + (stats.mannwhitneyu, tuple(), {'method': 'asymptotic'}, 2, None)) +) +@pytest.mark.parametrize( + ("sample_shape", "axis_cases"), + (((2, 3, 3, 4), (None, 0, -1, (0, 2), (1, -1), (3, 1, 2, 0))), + ((10, ), (0, -1)), + ((20, 0), (0, 1))) +) +def test_keepdims(hypotest, args, kwds, n_samples, unpacker, + sample_shape, axis_cases, nan_policy): + # test if keepdims parameter works correctly + if not unpacker: + def unpacker(res): + return res + rng = np.random.default_rng(0) + data = [rng.random(sample_shape) for _ in range(n_samples)] + nan_data = [sample.copy() for sample in data] + nan_mask = [rng.random(sample_shape) < 0.2 for _ in range(n_samples)] + for sample, mask in zip(nan_data, nan_mask): + sample[mask] = np.nan + for axis in axis_cases: + expected_shape = list(sample_shape) + if axis is None: + expected_shape = np.ones(len(sample_shape)) + else: + if isinstance(axis, int): + expected_shape[axis] = 1 + else: + for ax in axis: + expected_shape[ax] = 1 + expected_shape = tuple(expected_shape) + res = unpacker(hypotest(*data, *args, axis=axis, keepdims=True, + **kwds)) + res_base = unpacker(hypotest(*data, *args, axis=axis, keepdims=False, + **kwds)) + nan_res = unpacker(hypotest(*nan_data, *args, axis=axis, + keepdims=True, nan_policy=nan_policy, + **kwds)) + nan_res_base = unpacker(hypotest(*nan_data, *args, axis=axis, + keepdims=False, + nan_policy=nan_policy, **kwds)) + for r, r_base, rn, rn_base in zip(res, res_base, nan_res, + nan_res_base): + assert r.shape == expected_shape + r = np.squeeze(r, axis=axis) + assert_equal(r, r_base) + assert rn.shape == expected_shape + rn = np.squeeze(rn, axis=axis) + assert_equal(rn, rn_base) + + +@pytest.mark.parametrize(("fun", "nsamp"), + [(stats.kstat, 1), + (stats.kstatvar, 1)]) +def test_hypotest_back_compat_no_axis(fun, nsamp): + m, n = 8, 9 + + rng = np.random.default_rng(0) + x = rng.random((nsamp, m, n)) + res = fun(*x) + res2 = fun(*x, _no_deco=True) + res3 = fun([xi.ravel() for xi in x]) + assert_equal(res, res2) + assert_equal(res, res3) + + +@pytest.mark.parametrize(("axis"), (0, 1, 2)) +def test_axis_nan_policy_decorated_positional_axis(axis): + # Test for correct behavior of function decorated with + # _axis_nan_policy_decorator whether `axis` is provided as positional or + # keyword argument + + shape = (8, 9, 10) + rng = np.random.default_rng(0) + x = rng.random(shape) + y = rng.random(shape) + res1 = stats.mannwhitneyu(x, y, True, 'two-sided', axis) + res2 = stats.mannwhitneyu(x, y, True, 'two-sided', axis=axis) + assert_equal(res1, res2) + + message = "mannwhitneyu() got multiple values for argument 'axis'" + with pytest.raises(TypeError, match=re.escape(message)): + stats.mannwhitneyu(x, y, True, 'two-sided', axis, axis=axis) + + +def test_axis_nan_policy_decorated_positional_args(): + # Test for correct behavior of function decorated with + # _axis_nan_policy_decorator when function accepts *args + + shape = (3, 8, 9, 10) + rng = np.random.default_rng(0) + x = rng.random(shape) + x[0, 0, 0, 0] = np.nan + stats.kruskal(*x) + + message = "kruskal() got an unexpected keyword argument 'samples'" + with pytest.raises(TypeError, match=re.escape(message)): + stats.kruskal(samples=x) + + with pytest.raises(TypeError, match=re.escape(message)): + stats.kruskal(*x, samples=x) + + +def test_axis_nan_policy_decorated_keyword_samples(): + # Test for correct behavior of function decorated with + # _axis_nan_policy_decorator whether samples are provided as positional or + # keyword arguments + + shape = (2, 8, 9, 10) + rng = np.random.default_rng(0) + x = rng.random(shape) + x[0, 0, 0, 0] = np.nan + res1 = stats.mannwhitneyu(*x) + res2 = stats.mannwhitneyu(x=x[0], y=x[1]) + assert_equal(res1, res2) + + message = "mannwhitneyu() got multiple values for argument" + with pytest.raises(TypeError, match=re.escape(message)): + stats.mannwhitneyu(*x, x=x[0], y=x[1]) + + +@pytest.mark.parametrize(("hypotest", "args", "kwds", "n_samples", "n_outputs", + "paired", "unpacker"), axis_nan_policy_cases) +def test_axis_nan_policy_decorated_pickled(hypotest, args, kwds, n_samples, + n_outputs, paired, unpacker): + if "ttest_ci" in hypotest.__name__: + pytest.skip("Can't pickle functions defined within functions.") + + rng = np.random.default_rng(0) + + # Some hypothesis tests return a non-iterable that needs an `unpacker` to + # extract the statistic and p-value. For those that don't: + if not unpacker: + def unpacker(res): + return res + + data = rng.uniform(size=(n_samples, 2, 30)) + pickled_hypotest = pickle.dumps(hypotest) + unpickled_hypotest = pickle.loads(pickled_hypotest) + res1 = unpacker(hypotest(*data, *args, axis=-1, **kwds)) + res2 = unpacker(unpickled_hypotest(*data, *args, axis=-1, **kwds)) + assert_allclose(res1, res2, rtol=1e-12) + + +def test_check_empty_inputs(): + # Test that _check_empty_inputs is doing its job, at least for single- + # sample inputs. (Multi-sample functionality is tested below.) + # If the input sample is not empty, it should return None. + # If the input sample is empty, it should return an array of NaNs or an + # empty array of appropriate shape. np.mean is used as a reference for the + # output because, like the statistics calculated by these functions, + # it works along and "consumes" `axis` but preserves the other axes. + for i in range(5): + for combo in combinations_with_replacement([0, 1, 2], i): + for axis in range(len(combo)): + samples = (np.zeros(combo),) + output = stats._axis_nan_policy._check_empty_inputs(samples, + axis) + if output is not None: + with np.testing.suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Mean of empty slice.") + sup.filter(RuntimeWarning, "invalid value encountered") + reference = samples[0].mean(axis=axis) + np.testing.assert_equal(output, reference) + + +def _check_arrays_broadcastable(arrays, axis): + # https://numpy.org/doc/stable/user/basics.broadcasting.html + # "When operating on two arrays, NumPy compares their shapes element-wise. + # It starts with the trailing (i.e. rightmost) dimensions and works its + # way left. + # Two dimensions are compatible when + # 1. they are equal, or + # 2. one of them is 1 + # ... + # Arrays do not need to have the same number of dimensions." + # (Clarification: if the arrays are compatible according to the criteria + # above and an array runs out of dimensions, it is still compatible.) + # Below, we follow the rules above except ignoring `axis` + + n_dims = max([arr.ndim for arr in arrays]) + if axis is not None: + # convert to negative axis + axis = (-n_dims + axis) if axis >= 0 else axis + + for dim in range(1, n_dims+1): # we'll index from -1 to -n_dims, inclusive + if -dim == axis: + continue # ignore lengths along `axis` + + dim_lengths = set() + for arr in arrays: + if dim <= arr.ndim and arr.shape[-dim] != 1: + dim_lengths.add(arr.shape[-dim]) + + if len(dim_lengths) > 1: + return False + return True + + +@pytest.mark.slow +@pytest.mark.parametrize(("hypotest", "args", "kwds", "n_samples", "n_outputs", + "paired", "unpacker"), axis_nan_policy_cases) +def test_empty(hypotest, args, kwds, n_samples, n_outputs, paired, unpacker): + # test for correct output shape when at least one input is empty + + if hypotest in override_propagate_funcs: + reason = "Doesn't follow the usual pattern. Tested separately." + pytest.skip(reason=reason) + + if unpacker is None: + unpacker = lambda res: (res[0], res[1]) # noqa: E731 + + def small_data_generator(n_samples, n_dims): + + def small_sample_generator(n_dims): + # return all possible "small" arrays in up to n_dim dimensions + for i in n_dims: + # "small" means with size along dimension either 0 or 1 + for combo in combinations_with_replacement([0, 1, 2], i): + yield np.zeros(combo) + + # yield all possible combinations of small samples + gens = [small_sample_generator(n_dims) for i in range(n_samples)] + yield from product(*gens) + + n_dims = [2, 3] + for samples in small_data_generator(n_samples, n_dims): + + # this test is only for arrays of zero size + if not any(sample.size == 0 for sample in samples): + continue + + max_axis = max(sample.ndim for sample in samples) + + # need to test for all valid values of `axis` parameter, too + for axis in range(-max_axis, max_axis): + + try: + # After broadcasting, all arrays are the same shape, so + # the shape of the output should be the same as a single- + # sample statistic. Use np.mean as a reference. + concat = stats._stats_py._broadcast_concatenate(samples, axis) + with np.testing.suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Mean of empty slice.") + sup.filter(RuntimeWarning, "invalid value encountered") + expected = np.mean(concat, axis=axis) * np.nan + + if hypotest in empty_special_case_funcs: + empty_val = hypotest(*([[]]*len(samples)), *args, **kwds) + mask = np.isnan(expected) + expected[mask] = empty_val + + with np.testing.suppress_warnings() as sup: + # generated by f_oneway for too_small inputs + sup.filter(stats.DegenerateDataWarning) + res = hypotest(*samples, *args, axis=axis, **kwds) + res = unpacker(res) + + for i in range(n_outputs): + assert_equal(res[i], expected) + + except ValueError: + # confirm that the arrays truly are not broadcastable + assert not _check_arrays_broadcastable(samples, + None if paired else axis) + + # confirm that _both_ `_broadcast_concatenate` and `hypotest` + # produce this information. + message = "Array shapes are incompatible for broadcasting." + with pytest.raises(ValueError, match=message): + stats._stats_py._broadcast_concatenate(samples, axis, paired) + with pytest.raises(ValueError, match=message): + hypotest(*samples, *args, axis=axis, **kwds) + + +def test_masked_array_2_sentinel_array(): + # prepare arrays + np.random.seed(0) + A = np.random.rand(10, 11, 12) + B = np.random.rand(12) + mask = A < 0.5 + A = np.ma.masked_array(A, mask) + + # set arbitrary elements to special values + # (these values might have been considered for use as sentinel values) + max_float = np.finfo(np.float64).max + max_float2 = np.nextafter(max_float, -np.inf) + max_float3 = np.nextafter(max_float2, -np.inf) + A[3, 4, 1] = np.nan + A[4, 5, 2] = np.inf + A[5, 6, 3] = max_float + B[8] = np.nan + B[7] = np.inf + B[6] = max_float2 + + # convert masked A to array with sentinel value, don't modify B + out_arrays, sentinel = _masked_arrays_2_sentinel_arrays([A, B]) + A_out, B_out = out_arrays + + # check that good sentinel value was chosen (according to intended logic) + assert (sentinel != max_float) and (sentinel != max_float2) + assert sentinel == max_float3 + + # check that output arrays are as intended + A_reference = A.data + A_reference[A.mask] = sentinel + np.testing.assert_array_equal(A_out, A_reference) + assert B_out is B + + +def test_masked_dtype(): + # When _masked_arrays_2_sentinel_arrays was first added, it always + # upcast the arrays to np.float64. After gh16662, check expected promotion + # and that the expected sentinel is found. + + # these are important because the max of the promoted dtype is the first + # candidate to be the sentinel value + max16 = np.iinfo(np.int16).max + max128c = np.finfo(np.complex128).max + + # a is a regular array, b has masked elements, and c has no masked elements + a = np.array([1, 2, max16], dtype=np.int16) + b = np.ma.array([1, 2, 1], dtype=np.int8, mask=[0, 1, 0]) + c = np.ma.array([1, 2, 1], dtype=np.complex128, mask=[0, 0, 0]) + + # check integer masked -> sentinel conversion + out_arrays, sentinel = _masked_arrays_2_sentinel_arrays([a, b]) + a_out, b_out = out_arrays + assert sentinel == max16-1 # not max16 because max16 was in the data + assert b_out.dtype == np.int16 # check expected promotion + assert_allclose(b_out, [b[0], sentinel, b[-1]]) # check sentinel placement + assert a_out is a # not a masked array, so left untouched + assert not isinstance(b_out, np.ma.MaskedArray) # b became regular array + + # similarly with complex + out_arrays, sentinel = _masked_arrays_2_sentinel_arrays([b, c]) + b_out, c_out = out_arrays + assert sentinel == max128c # max128c was not in the data + assert b_out.dtype == np.complex128 # b got promoted + assert_allclose(b_out, [b[0], sentinel, b[-1]]) # check sentinel placement + assert not isinstance(b_out, np.ma.MaskedArray) # b became regular array + assert not isinstance(c_out, np.ma.MaskedArray) # c became regular array + + # Also, check edge case when a sentinel value cannot be found in the data + min8, max8 = np.iinfo(np.int8).min, np.iinfo(np.int8).max + a = np.arange(min8, max8+1, dtype=np.int8) # use all possible values + mask1 = np.zeros_like(a, dtype=bool) + mask0 = np.zeros_like(a, dtype=bool) + + # a masked value can be used as the sentinel + mask1[1] = True + a1 = np.ma.array(a, mask=mask1) + out_arrays, sentinel = _masked_arrays_2_sentinel_arrays([a1]) + assert sentinel == min8+1 + + # unless it's the smallest possible; skipped for simiplicity (see code) + mask0[0] = True + a0 = np.ma.array(a, mask=mask0) + message = "This function replaces masked elements with sentinel..." + with pytest.raises(ValueError, match=message): + _masked_arrays_2_sentinel_arrays([a0]) + + # test that dtype is preserved in functions + a = np.ma.array([1, 2, 3], mask=[0, 1, 0], dtype=np.float32) + assert stats.gmean(a).dtype == np.float32 + + +def test_masked_stat_1d(): + # basic test of _axis_nan_policy_factory with 1D masked sample + males = [19, 22, 16, 29, 24] + females = [20, 11, 17, 12] + res = stats.mannwhitneyu(males, females) + + # same result when extra nan is omitted + females2 = [20, 11, 17, np.nan, 12] + res2 = stats.mannwhitneyu(males, females2, nan_policy='omit') + np.testing.assert_array_equal(res2, res) + + # same result when extra element is masked + females3 = [20, 11, 17, 1000, 12] + mask3 = [False, False, False, True, False] + females3 = np.ma.masked_array(females3, mask=mask3) + res3 = stats.mannwhitneyu(males, females3) + np.testing.assert_array_equal(res3, res) + + # same result when extra nan is omitted and additional element is masked + females4 = [20, 11, 17, np.nan, 1000, 12] + mask4 = [False, False, False, False, True, False] + females4 = np.ma.masked_array(females4, mask=mask4) + res4 = stats.mannwhitneyu(males, females4, nan_policy='omit') + np.testing.assert_array_equal(res4, res) + + # same result when extra elements, including nan, are masked + females5 = [20, 11, 17, np.nan, 1000, 12] + mask5 = [False, False, False, True, True, False] + females5 = np.ma.masked_array(females5, mask=mask5) + res5 = stats.mannwhitneyu(males, females5, nan_policy='propagate') + res6 = stats.mannwhitneyu(males, females5, nan_policy='raise') + np.testing.assert_array_equal(res5, res) + np.testing.assert_array_equal(res6, res) + + +@pytest.mark.parametrize(("axis"), range(-3, 3)) +def test_masked_stat_3d(axis): + # basic test of _axis_nan_policy_factory with 3D masked sample + np.random.seed(0) + a = np.random.rand(3, 4, 5) + b = np.random.rand(4, 5) + c = np.random.rand(4, 1) + + mask_a = a < 0.1 + mask_c = [False, False, False, True] + a_masked = np.ma.masked_array(a, mask=mask_a) + c_masked = np.ma.masked_array(c, mask=mask_c) + + a_nans = a.copy() + a_nans[mask_a] = np.nan + c_nans = c.copy() + c_nans[mask_c] = np.nan + + res = stats.kruskal(a_nans, b, c_nans, nan_policy='omit', axis=axis) + res2 = stats.kruskal(a_masked, b, c_masked, axis=axis) + np.testing.assert_array_equal(res, res2) + + +def test_mixed_mask_nan_1(): + # targeted test of _axis_nan_policy_factory with 2D masked sample: + # omitting samples with masks and nan_policy='omit' are equivalent + # also checks paired-sample sentinel value removal + m, n = 3, 20 + axis = -1 + + np.random.seed(0) + a = np.random.rand(m, n) + b = np.random.rand(m, n) + mask_a1 = np.random.rand(m, n) < 0.2 + mask_a2 = np.random.rand(m, n) < 0.1 + mask_b1 = np.random.rand(m, n) < 0.15 + mask_b2 = np.random.rand(m, n) < 0.15 + mask_a1[2, :] = True + + a_nans = a.copy() + b_nans = b.copy() + a_nans[mask_a1 | mask_a2] = np.nan + b_nans[mask_b1 | mask_b2] = np.nan + + a_masked1 = np.ma.masked_array(a, mask=mask_a1) + b_masked1 = np.ma.masked_array(b, mask=mask_b1) + a_masked1[mask_a2] = np.nan + b_masked1[mask_b2] = np.nan + + a_masked2 = np.ma.masked_array(a, mask=mask_a2) + b_masked2 = np.ma.masked_array(b, mask=mask_b2) + a_masked2[mask_a1] = np.nan + b_masked2[mask_b1] = np.nan + + a_masked3 = np.ma.masked_array(a, mask=(mask_a1 | mask_a2)) + b_masked3 = np.ma.masked_array(b, mask=(mask_b1 | mask_b2)) + + res = stats.wilcoxon(a_nans, b_nans, nan_policy='omit', axis=axis) + res1 = stats.wilcoxon(a_masked1, b_masked1, nan_policy='omit', axis=axis) + res2 = stats.wilcoxon(a_masked2, b_masked2, nan_policy='omit', axis=axis) + res3 = stats.wilcoxon(a_masked3, b_masked3, nan_policy='raise', axis=axis) + res4 = stats.wilcoxon(a_masked3, b_masked3, + nan_policy='propagate', axis=axis) + + np.testing.assert_array_equal(res1, res) + np.testing.assert_array_equal(res2, res) + np.testing.assert_array_equal(res3, res) + np.testing.assert_array_equal(res4, res) + + +def test_mixed_mask_nan_2(): + # targeted test of _axis_nan_policy_factory with 2D masked sample: + # check for expected interaction between masks and nans + + # Cases here are + # [mixed nan/mask, all nans, all masked, + # unmasked nan, masked nan, unmasked non-nan] + a = [[1, np.nan, 2], [np.nan, np.nan, np.nan], [1, 2, 3], + [1, np.nan, 3], [1, np.nan, 3], [1, 2, 3]] + mask = [[1, 0, 1], [0, 0, 0], [1, 1, 1], + [0, 0, 0], [0, 1, 0], [0, 0, 0]] + a_masked = np.ma.masked_array(a, mask=mask) + b = [[4, 5, 6]] + ref1 = stats.ranksums([1, 3], [4, 5, 6]) + ref2 = stats.ranksums([1, 2, 3], [4, 5, 6]) + + # nan_policy = 'omit' + # all elements are removed from first three rows + # middle element is removed from fourth and fifth rows + # no elements removed from last row + res = stats.ranksums(a_masked, b, nan_policy='omit', axis=-1) + stat_ref = [np.nan, np.nan, np.nan, + ref1.statistic, ref1.statistic, ref2.statistic] + p_ref = [np.nan, np.nan, np.nan, + ref1.pvalue, ref1.pvalue, ref2.pvalue] + np.testing.assert_array_equal(res.statistic, stat_ref) + np.testing.assert_array_equal(res.pvalue, p_ref) + + # nan_policy = 'propagate' + # nans propagate in first, second, and fourth row + # all elements are removed by mask from third row + # middle element is removed from fifth row + # no elements removed from last row + res = stats.ranksums(a_masked, b, nan_policy='propagate', axis=-1) + stat_ref = [np.nan, np.nan, np.nan, + np.nan, ref1.statistic, ref2.statistic] + p_ref = [np.nan, np.nan, np.nan, + np.nan, ref1.pvalue, ref2.pvalue] + np.testing.assert_array_equal(res.statistic, stat_ref) + np.testing.assert_array_equal(res.pvalue, p_ref) + + +def test_axis_None_vs_tuple(): + # `axis` `None` should be equivalent to tuple with all axes + shape = (3, 8, 9, 10) + rng = np.random.default_rng(0) + x = rng.random(shape) + res = stats.kruskal(*x, axis=None) + res2 = stats.kruskal(*x, axis=(0, 1, 2)) + np.testing.assert_array_equal(res, res2) + + +def test_axis_None_vs_tuple_with_broadcasting(): + # `axis` `None` should be equivalent to tuple with all axes, + # which should be equivalent to raveling the arrays before passing them + rng = np.random.default_rng(0) + x = rng.random((5, 1)) + y = rng.random((1, 5)) + x2, y2 = np.broadcast_arrays(x, y) + + res0 = stats.mannwhitneyu(x.ravel(), y.ravel()) + res1 = stats.mannwhitneyu(x, y, axis=None) + res2 = stats.mannwhitneyu(x, y, axis=(0, 1)) + res3 = stats.mannwhitneyu(x2.ravel(), y2.ravel()) + + assert res1 == res0 + assert res2 == res0 + assert res3 != res0 + + +@pytest.mark.parametrize(("axis"), + list(permutations(range(-3, 3), 2)) + [(-4, 1)]) +def test_other_axis_tuples(axis): + # Check that _axis_nan_policy_factory treats all `axis` tuples as expected + rng = np.random.default_rng(0) + shape_x = (4, 5, 6) + shape_y = (1, 6) + x = rng.random(shape_x) + y = rng.random(shape_y) + axis_original = axis + + # convert axis elements to positive + axis = tuple([(i if i >= 0 else 3 + i) for i in axis]) + axis = sorted(axis) + + if len(set(axis)) != len(axis): + message = "`axis` must contain only distinct elements" + with pytest.raises(AxisError, match=re.escape(message)): + stats.mannwhitneyu(x, y, axis=axis_original) + return + + if axis[0] < 0 or axis[-1] > 2: + message = "`axis` is out of bounds for array of dimension 3" + with pytest.raises(AxisError, match=re.escape(message)): + stats.mannwhitneyu(x, y, axis=axis_original) + return + + res = stats.mannwhitneyu(x, y, axis=axis_original) + + # reference behavior + not_axis = {0, 1, 2} - set(axis) # which axis is not part of `axis` + not_axis = next(iter(not_axis)) # take it out of the set + + x2 = x + shape_y_broadcasted = [1, 1, 6] + shape_y_broadcasted[not_axis] = shape_x[not_axis] + y2 = np.broadcast_to(y, shape_y_broadcasted) + + m = x2.shape[not_axis] + x2 = np.moveaxis(x2, axis, (1, 2)) + y2 = np.moveaxis(y2, axis, (1, 2)) + x2 = np.reshape(x2, (m, -1)) + y2 = np.reshape(y2, (m, -1)) + res2 = stats.mannwhitneyu(x2, y2, axis=1) + + np.testing.assert_array_equal(res, res2) + + +@pytest.mark.parametrize( + ("weighted_fun_name, unpacker"), + [ + ("gmean", lambda x: x), + ("hmean", lambda x: x), + ("pmean", lambda x: x), + ("combine_pvalues", lambda x: (x.pvalue, x.statistic)), + ], +) +def test_mean_mixed_mask_nan_weights(weighted_fun_name, unpacker): + # targeted test of _axis_nan_policy_factory with 2D masked sample: + # omitting samples with masks and nan_policy='omit' are equivalent + # also checks paired-sample sentinel value removal + + if weighted_fun_name == 'pmean': + def weighted_fun(a, **kwargs): + return stats.pmean(a, p=0.42, **kwargs) + else: + weighted_fun = getattr(stats, weighted_fun_name) + + def func(*args, **kwargs): + return unpacker(weighted_fun(*args, **kwargs)) + + m, n = 3, 20 + axis = -1 + + rng = np.random.default_rng(6541968121) + a = rng.uniform(size=(m, n)) + b = rng.uniform(size=(m, n)) + mask_a1 = rng.uniform(size=(m, n)) < 0.2 + mask_a2 = rng.uniform(size=(m, n)) < 0.1 + mask_b1 = rng.uniform(size=(m, n)) < 0.15 + mask_b2 = rng.uniform(size=(m, n)) < 0.15 + mask_a1[2, :] = True + + a_nans = a.copy() + b_nans = b.copy() + a_nans[mask_a1 | mask_a2] = np.nan + b_nans[mask_b1 | mask_b2] = np.nan + + a_masked1 = np.ma.masked_array(a, mask=mask_a1) + b_masked1 = np.ma.masked_array(b, mask=mask_b1) + a_masked1[mask_a2] = np.nan + b_masked1[mask_b2] = np.nan + + a_masked2 = np.ma.masked_array(a, mask=mask_a2) + b_masked2 = np.ma.masked_array(b, mask=mask_b2) + a_masked2[mask_a1] = np.nan + b_masked2[mask_b1] = np.nan + + a_masked3 = np.ma.masked_array(a, mask=(mask_a1 | mask_a2)) + b_masked3 = np.ma.masked_array(b, mask=(mask_b1 | mask_b2)) + + mask_all = (mask_a1 | mask_a2 | mask_b1 | mask_b2) + a_masked4 = np.ma.masked_array(a, mask=mask_all) + b_masked4 = np.ma.masked_array(b, mask=mask_all) + + with np.testing.suppress_warnings() as sup: + message = 'invalid value encountered' + sup.filter(RuntimeWarning, message) + res = func(a_nans, weights=b_nans, nan_policy="omit", axis=axis) + res1 = func(a_masked1, weights=b_masked1, nan_policy="omit", axis=axis) + res2 = func(a_masked2, weights=b_masked2, nan_policy="omit", axis=axis) + res3 = func(a_masked3, weights=b_masked3, nan_policy="raise", axis=axis) + res4 = func(a_masked3, weights=b_masked3, nan_policy="propagate", axis=axis) + # Would test with a_masked3/b_masked3, but there is a bug in np.average + # that causes a bug in _no_deco mean with masked weights. Would use + # np.ma.average, but that causes other problems. See numpy/numpy#7330. + if weighted_fun_name in {"hmean"}: + weighted_fun_ma = getattr(stats.mstats, weighted_fun_name) + res5 = weighted_fun_ma(a_masked4, weights=b_masked4, + axis=axis, _no_deco=True) + + np.testing.assert_array_equal(res1, res) + np.testing.assert_array_equal(res2, res) + np.testing.assert_array_equal(res3, res) + np.testing.assert_array_equal(res4, res) + if weighted_fun_name in {"hmean"}: + # _no_deco mean returns masked array, last element was masked + np.testing.assert_allclose(res5.compressed(), res[~np.isnan(res)]) + + +def test_raise_invalid_args_g17713(): + # other cases are handled in: + # test_axis_nan_policy_decorated_positional_axis - multiple values for arg + # test_axis_nan_policy_decorated_positional_args - unexpected kwd arg + message = "got an unexpected keyword argument" + with pytest.raises(TypeError, match=message): + stats.gmean([1, 2, 3], invalid_arg=True) + + message = " got multiple values for argument" + with pytest.raises(TypeError, match=message): + stats.gmean([1, 2, 3], a=True) + + message = "missing 1 required positional argument" + with pytest.raises(TypeError, match=message): + stats.gmean() + + message = "takes from 1 to 4 positional arguments but 5 were given" + with pytest.raises(TypeError, match=message): + stats.gmean([1, 2, 3], 0, float, [1, 1, 1], 10) + + +@pytest.mark.parametrize('dtype', [np.int16, np.float32, np.complex128]) +def test_array_like_input(dtype): + # Check that `_axis_nan_policy`-decorated functions work with custom + # containers that are coercible to numeric arrays + + class ArrLike: + def __init__(self, x, dtype): + self._x = x + self._dtype = dtype + + def __array__(self, dtype=None, copy=None): + return np.asarray(x, dtype=self._dtype) + + x = [1]*2 + [3, 4, 5] + res = stats.mode(ArrLike(x, dtype=dtype)) + assert res.mode == 1 + assert res.count == 2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_binned_statistic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_binned_statistic.py new file mode 100644 index 0000000000000000000000000000000000000000..932df07f2e489c137b378841fd749272ae0bcc89 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_binned_statistic.py @@ -0,0 +1,568 @@ +import numpy as np +from numpy.testing import assert_allclose +import pytest +from pytest import raises as assert_raises +from scipy.stats import (binned_statistic, binned_statistic_2d, + binned_statistic_dd) +from scipy._lib._util import check_random_state + +from .common_tests import check_named_results + + +class TestBinnedStatistic: + + @classmethod + def setup_class(cls): + rng = check_random_state(9865) + cls.x = rng.uniform(size=100) + cls.y = rng.uniform(size=100) + cls.v = rng.uniform(size=100) + cls.X = rng.uniform(size=(100, 3)) + cls.w = rng.uniform(size=100) + cls.u = rng.uniform(size=100) + 1e6 + + def test_1d_count(self): + x = self.x + v = self.v + + count1, edges1, bc = binned_statistic(x, v, 'count', bins=10) + count2, edges2 = np.histogram(x, bins=10) + + assert_allclose(count1, count2) + assert_allclose(edges1, edges2) + + def test_gh5927(self): + # smoke test for gh5927 - binned_statistic was using `is` for string + # comparison + x = self.x + v = self.v + statistics = ['mean', 'median', 'count', 'sum'] + for statistic in statistics: + binned_statistic(x, v, statistic, bins=10) + + def test_big_number_std(self): + # tests for numerical stability of std calculation + # see issue gh-10126 for more + x = self.x + u = self.u + stat1, edges1, bc = binned_statistic(x, u, 'std', bins=10) + stat2, edges2, bc = binned_statistic(x, u, np.std, bins=10) + + assert_allclose(stat1, stat2) + + def test_empty_bins_std(self): + # tests that std returns gives nan for empty bins + x = self.x + u = self.u + print(binned_statistic(x, u, 'count', bins=1000)) + stat1, edges1, bc = binned_statistic(x, u, 'std', bins=1000) + stat2, edges2, bc = binned_statistic(x, u, np.std, bins=1000) + + assert_allclose(stat1, stat2) + + def test_non_finite_inputs_and_int_bins(self): + # if either `values` or `sample` contain np.inf or np.nan throw + # see issue gh-9010 for more + x = self.x + u = self.u + orig = u[0] + u[0] = np.inf + assert_raises(ValueError, binned_statistic, u, x, 'std', bins=10) + # need to test for non-python specific ints, e.g. np.int8, np.int64 + assert_raises(ValueError, binned_statistic, u, x, 'std', + bins=np.int64(10)) + u[0] = np.nan + assert_raises(ValueError, binned_statistic, u, x, 'count', bins=10) + # replace original value, u belongs the class + u[0] = orig + + def test_1d_result_attributes(self): + x = self.x + v = self.v + + res = binned_statistic(x, v, 'count', bins=10) + attributes = ('statistic', 'bin_edges', 'binnumber') + check_named_results(res, attributes) + + def test_1d_sum(self): + x = self.x + v = self.v + + sum1, edges1, bc = binned_statistic(x, v, 'sum', bins=10) + sum2, edges2 = np.histogram(x, bins=10, weights=v) + + assert_allclose(sum1, sum2) + assert_allclose(edges1, edges2) + + def test_1d_mean(self): + x = self.x + v = self.v + + stat1, edges1, bc = binned_statistic(x, v, 'mean', bins=10) + stat2, edges2, bc = binned_statistic(x, v, np.mean, bins=10) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_1d_std(self): + x = self.x + v = self.v + + stat1, edges1, bc = binned_statistic(x, v, 'std', bins=10) + stat2, edges2, bc = binned_statistic(x, v, np.std, bins=10) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_1d_min(self): + x = self.x + v = self.v + + stat1, edges1, bc = binned_statistic(x, v, 'min', bins=10) + stat2, edges2, bc = binned_statistic(x, v, np.min, bins=10) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_1d_max(self): + x = self.x + v = self.v + + stat1, edges1, bc = binned_statistic(x, v, 'max', bins=10) + stat2, edges2, bc = binned_statistic(x, v, np.max, bins=10) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_1d_median(self): + x = self.x + v = self.v + + stat1, edges1, bc = binned_statistic(x, v, 'median', bins=10) + stat2, edges2, bc = binned_statistic(x, v, np.median, bins=10) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_1d_bincode(self): + x = self.x[:20] + v = self.v[:20] + + count1, edges1, bc = binned_statistic(x, v, 'count', bins=3) + bc2 = np.array([3, 2, 1, 3, 2, 3, 3, 3, 3, 1, 1, 3, 3, 1, 2, 3, 1, + 1, 2, 1]) + + bcount = [(bc == i).sum() for i in np.unique(bc)] + + assert_allclose(bc, bc2) + assert_allclose(bcount, count1) + + def test_1d_range_keyword(self): + # Regression test for gh-3063, range can be (min, max) or [(min, max)] + np.random.seed(9865) + x = np.arange(30) + data = np.random.random(30) + + mean, bins, _ = binned_statistic(x[:15], data[:15]) + mean_range, bins_range, _ = binned_statistic(x, data, range=[(0, 14)]) + mean_range2, bins_range2, _ = binned_statistic(x, data, range=(0, 14)) + + assert_allclose(mean, mean_range) + assert_allclose(bins, bins_range) + assert_allclose(mean, mean_range2) + assert_allclose(bins, bins_range2) + + def test_1d_multi_values(self): + x = self.x + v = self.v + w = self.w + + stat1v, edges1v, bc1v = binned_statistic(x, v, 'mean', bins=10) + stat1w, edges1w, bc1w = binned_statistic(x, w, 'mean', bins=10) + stat2, edges2, bc2 = binned_statistic(x, [v, w], 'mean', bins=10) + + assert_allclose(stat2[0], stat1v) + assert_allclose(stat2[1], stat1w) + assert_allclose(edges1v, edges2) + assert_allclose(bc1v, bc2) + + def test_2d_count(self): + x = self.x + y = self.y + v = self.v + + count1, binx1, biny1, bc = binned_statistic_2d( + x, y, v, 'count', bins=5) + count2, binx2, biny2 = np.histogram2d(x, y, bins=5) + + assert_allclose(count1, count2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_result_attributes(self): + x = self.x + y = self.y + v = self.v + + res = binned_statistic_2d(x, y, v, 'count', bins=5) + attributes = ('statistic', 'x_edge', 'y_edge', 'binnumber') + check_named_results(res, attributes) + + def test_2d_sum(self): + x = self.x + y = self.y + v = self.v + + sum1, binx1, biny1, bc = binned_statistic_2d(x, y, v, 'sum', bins=5) + sum2, binx2, biny2 = np.histogram2d(x, y, bins=5, weights=v) + + assert_allclose(sum1, sum2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_mean(self): + x = self.x + y = self.y + v = self.v + + stat1, binx1, biny1, bc = binned_statistic_2d(x, y, v, 'mean', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d(x, y, v, np.mean, bins=5) + + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_mean_unicode(self): + x = self.x + y = self.y + v = self.v + stat1, binx1, biny1, bc = binned_statistic_2d( + x, y, v, 'mean', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d(x, y, v, np.mean, bins=5) + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_std(self): + x = self.x + y = self.y + v = self.v + + stat1, binx1, biny1, bc = binned_statistic_2d(x, y, v, 'std', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d(x, y, v, np.std, bins=5) + + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_min(self): + x = self.x + y = self.y + v = self.v + + stat1, binx1, biny1, bc = binned_statistic_2d(x, y, v, 'min', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d(x, y, v, np.min, bins=5) + + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_max(self): + x = self.x + y = self.y + v = self.v + + stat1, binx1, biny1, bc = binned_statistic_2d(x, y, v, 'max', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d(x, y, v, np.max, bins=5) + + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_median(self): + x = self.x + y = self.y + v = self.v + + stat1, binx1, biny1, bc = binned_statistic_2d( + x, y, v, 'median', bins=5) + stat2, binx2, biny2, bc = binned_statistic_2d( + x, y, v, np.median, bins=5) + + assert_allclose(stat1, stat2) + assert_allclose(binx1, binx2) + assert_allclose(biny1, biny2) + + def test_2d_bincode(self): + x = self.x[:20] + y = self.y[:20] + v = self.v[:20] + + count1, binx1, biny1, bc = binned_statistic_2d( + x, y, v, 'count', bins=3) + bc2 = np.array([17, 11, 6, 16, 11, 17, 18, 17, 17, 7, 6, 18, 16, + 6, 11, 16, 6, 6, 11, 8]) + + bcount = [(bc == i).sum() for i in np.unique(bc)] + + assert_allclose(bc, bc2) + count1adj = count1[count1.nonzero()] + assert_allclose(bcount, count1adj) + + def test_2d_multi_values(self): + x = self.x + y = self.y + v = self.v + w = self.w + + stat1v, binx1v, biny1v, bc1v = binned_statistic_2d( + x, y, v, 'mean', bins=8) + stat1w, binx1w, biny1w, bc1w = binned_statistic_2d( + x, y, w, 'mean', bins=8) + stat2, binx2, biny2, bc2 = binned_statistic_2d( + x, y, [v, w], 'mean', bins=8) + + assert_allclose(stat2[0], stat1v) + assert_allclose(stat2[1], stat1w) + assert_allclose(binx1v, binx2) + assert_allclose(biny1w, biny2) + assert_allclose(bc1v, bc2) + + def test_2d_binnumbers_unraveled(self): + x = self.x + y = self.y + v = self.v + + stat, edgesx, bcx = binned_statistic(x, v, 'mean', bins=20) + stat, edgesy, bcy = binned_statistic(y, v, 'mean', bins=10) + + stat2, edgesx2, edgesy2, bc2 = binned_statistic_2d( + x, y, v, 'mean', bins=(20, 10), expand_binnumbers=True) + + bcx3 = np.searchsorted(edgesx, x, side='right') + bcy3 = np.searchsorted(edgesy, y, side='right') + + # `numpy.searchsorted` is non-inclusive on right-edge, compensate + bcx3[x == x.max()] -= 1 + bcy3[y == y.max()] -= 1 + + assert_allclose(bcx, bc2[0]) + assert_allclose(bcy, bc2[1]) + assert_allclose(bcx3, bc2[0]) + assert_allclose(bcy3, bc2[1]) + + def test_dd_count(self): + X = self.X + v = self.v + + count1, edges1, bc = binned_statistic_dd(X, v, 'count', bins=3) + count2, edges2 = np.histogramdd(X, bins=3) + + assert_allclose(count1, count2) + assert_allclose(edges1, edges2) + + def test_dd_result_attributes(self): + X = self.X + v = self.v + + res = binned_statistic_dd(X, v, 'count', bins=3) + attributes = ('statistic', 'bin_edges', 'binnumber') + check_named_results(res, attributes) + + def test_dd_sum(self): + X = self.X + v = self.v + + sum1, edges1, bc = binned_statistic_dd(X, v, 'sum', bins=3) + sum2, edges2 = np.histogramdd(X, bins=3, weights=v) + sum3, edges3, bc = binned_statistic_dd(X, v, np.sum, bins=3) + + assert_allclose(sum1, sum2) + assert_allclose(edges1, edges2) + assert_allclose(sum1, sum3) + assert_allclose(edges1, edges3) + + def test_dd_mean(self): + X = self.X + v = self.v + + stat1, edges1, bc = binned_statistic_dd(X, v, 'mean', bins=3) + stat2, edges2, bc = binned_statistic_dd(X, v, np.mean, bins=3) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_dd_std(self): + X = self.X + v = self.v + + stat1, edges1, bc = binned_statistic_dd(X, v, 'std', bins=3) + stat2, edges2, bc = binned_statistic_dd(X, v, np.std, bins=3) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_dd_min(self): + X = self.X + v = self.v + + stat1, edges1, bc = binned_statistic_dd(X, v, 'min', bins=3) + stat2, edges2, bc = binned_statistic_dd(X, v, np.min, bins=3) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_dd_max(self): + X = self.X + v = self.v + + stat1, edges1, bc = binned_statistic_dd(X, v, 'max', bins=3) + stat2, edges2, bc = binned_statistic_dd(X, v, np.max, bins=3) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_dd_median(self): + X = self.X + v = self.v + + stat1, edges1, bc = binned_statistic_dd(X, v, 'median', bins=3) + stat2, edges2, bc = binned_statistic_dd(X, v, np.median, bins=3) + + assert_allclose(stat1, stat2) + assert_allclose(edges1, edges2) + + def test_dd_bincode(self): + X = self.X[:20] + v = self.v[:20] + + count1, edges1, bc = binned_statistic_dd(X, v, 'count', bins=3) + bc2 = np.array([63, 33, 86, 83, 88, 67, 57, 33, 42, 41, 82, 83, 92, + 32, 36, 91, 43, 87, 81, 81]) + + bcount = [(bc == i).sum() for i in np.unique(bc)] + + assert_allclose(bc, bc2) + count1adj = count1[count1.nonzero()] + assert_allclose(bcount, count1adj) + + def test_dd_multi_values(self): + X = self.X + v = self.v + w = self.w + + for stat in ["count", "sum", "mean", "std", "min", "max", "median", + np.std]: + stat1v, edges1v, bc1v = binned_statistic_dd(X, v, stat, bins=8) + stat1w, edges1w, bc1w = binned_statistic_dd(X, w, stat, bins=8) + stat2, edges2, bc2 = binned_statistic_dd(X, [v, w], stat, bins=8) + assert_allclose(stat2[0], stat1v) + assert_allclose(stat2[1], stat1w) + assert_allclose(edges1v, edges2) + assert_allclose(edges1w, edges2) + assert_allclose(bc1v, bc2) + + def test_dd_binnumbers_unraveled(self): + X = self.X + v = self.v + + stat, edgesx, bcx = binned_statistic(X[:, 0], v, 'mean', bins=15) + stat, edgesy, bcy = binned_statistic(X[:, 1], v, 'mean', bins=20) + stat, edgesz, bcz = binned_statistic(X[:, 2], v, 'mean', bins=10) + + stat2, edges2, bc2 = binned_statistic_dd( + X, v, 'mean', bins=(15, 20, 10), expand_binnumbers=True) + + assert_allclose(bcx, bc2[0]) + assert_allclose(bcy, bc2[1]) + assert_allclose(bcz, bc2[2]) + + def test_dd_binned_statistic_result(self): + # NOTE: tests the reuse of bin_edges from previous call + x = np.random.random((10000, 3)) + v = np.random.random(10000) + bins = np.linspace(0, 1, 10) + bins = (bins, bins, bins) + + result = binned_statistic_dd(x, v, 'mean', bins=bins) + stat = result.statistic + + result = binned_statistic_dd(x, v, 'mean', + binned_statistic_result=result) + stat2 = result.statistic + + assert_allclose(stat, stat2) + + def test_dd_zero_dedges(self): + x = np.random.random((10000, 3)) + v = np.random.random(10000) + bins = np.linspace(0, 1, 10) + bins = np.append(bins, 1) + bins = (bins, bins, bins) + with assert_raises(ValueError, match='difference is numerically 0'): + binned_statistic_dd(x, v, 'mean', bins=bins) + + def test_dd_range_errors(self): + # Test that descriptive exceptions are raised as appropriate for bad + # values of the `range` argument. (See gh-12996) + with assert_raises(ValueError, + match='In range, start must be <= stop'): + binned_statistic_dd([self.y], self.v, + range=[[1, 0]]) + with assert_raises( + ValueError, + match='In dimension 1 of range, start must be <= stop'): + binned_statistic_dd([self.x, self.y], self.v, + range=[[1, 0], [0, 1]]) + with assert_raises( + ValueError, + match='In dimension 2 of range, start must be <= stop'): + binned_statistic_dd([self.x, self.y], self.v, + range=[[0, 1], [1, 0]]) + with assert_raises( + ValueError, + match='range given for 1 dimensions; 2 required'): + binned_statistic_dd([self.x, self.y], self.v, + range=[[0, 1]]) + + def test_binned_statistic_float32(self): + X = np.array([0, 0.42358226], dtype=np.float32) + stat, _, _ = binned_statistic(X, None, 'count', bins=5) + assert_allclose(stat, np.array([1, 0, 0, 0, 1], dtype=np.float64)) + + def test_gh14332(self): + # Test the wrong output when the `sample` is close to bin edge + x = [] + size = 20 + for i in range(size): + x += [1-0.1**i] + + bins = np.linspace(0,1,11) + sum1, edges1, bc = binned_statistic_dd(x, np.ones(len(x)), + bins=[bins], statistic='sum') + sum2, edges2 = np.histogram(x, bins=bins) + + assert_allclose(sum1, sum2) + assert_allclose(edges1[0], edges2) + + @pytest.mark.parametrize("dtype", [np.float64, np.complex128]) + @pytest.mark.parametrize("statistic", [np.mean, np.median, np.sum, np.std, + np.min, np.max, 'count', + lambda x: (x**2).sum(), + lambda x: (x**2).sum() * 1j]) + def test_dd_all(self, dtype, statistic): + def ref_statistic(x): + return len(x) if statistic == 'count' else statistic(x) + + rng = np.random.default_rng(3704743126639371) + n = 10 + x = rng.random(size=n) + i = x >= 0.5 + v = rng.random(size=n) + if dtype is np.complex128: + v = v + rng.random(size=n)*1j + + stat, _, _ = binned_statistic_dd(x, v, statistic, bins=2) + ref = np.array([ref_statistic(v[~i]), ref_statistic(v[i])]) + assert_allclose(stat, ref) + assert stat.dtype == np.result_type(ref.dtype, np.float64) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_boost_ufuncs.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_boost_ufuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..89b7558899f35571d9e7e415b68f78f80a76caac --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_boost_ufuncs.py @@ -0,0 +1,47 @@ +import pytest +import numpy as np +from numpy.testing import assert_allclose +from scipy.stats import _boost + + +type_char_to_type_tol = {'f': (np.float32, 32*np.finfo(np.float32).eps), + 'd': (np.float64, 32*np.finfo(np.float64).eps)} + + +# Each item in this list is +# (func, args, expected_value) +# All the values can be represented exactly, even with np.float32. +# +# This is not an exhaustive test data set of all the functions! +# It is a spot check of several functions, primarily for +# checking that the different data types are handled correctly. +test_data = [ + (_boost._beta_cdf, (0.5, 2, 3), 0.6875), + (_boost._beta_ppf, (0.6875, 2, 3), 0.5), + (_boost._beta_pdf, (0.5, 2, 3), 1.5), + (_boost._beta_pdf, (0, 1, 5), 5.0), + (_boost._beta_pdf, (1, 5, 1), 5.0), + (_boost._beta_sf, (0.5, 2, 1), 0.75), + (_boost._beta_isf, (0.75, 2, 1), 0.5), + (_boost._binom_cdf, (1, 3, 0.5), 0.5), + (_boost._binom_pdf, (1, 4, 0.5), 0.25), + (_boost._hypergeom_cdf, (2, 3, 5, 6), 0.5), + (_boost._nbinom_cdf, (1, 4, 0.25), 0.015625), + (_boost._ncf_mean, (10, 12, 2.5), 1.5), +] + + +@pytest.mark.parametrize('func, args, expected', test_data) +def test_stats_boost_ufunc(func, args, expected): + type_sigs = func.types + type_chars = [sig.split('->')[-1] for sig in type_sigs] + for type_char in type_chars: + typ, rtol = type_char_to_type_tol[type_char] + args = [typ(arg) for arg in args] + # Harmless overflow warnings are a "feature" of some wrappers on some + # platforms. This test is about dtype and accuracy, so let's avoid false + # test failures cause by these warnings. See gh-17432. + with np.errstate(over='ignore'): + value = func(*args) + assert isinstance(value, typ) + assert_allclose(value, expected, rtol=rtol) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_censored_data.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_censored_data.py new file mode 100644 index 0000000000000000000000000000000000000000..ae71dcfaccf899645051287f8944131ec48a1eee --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_censored_data.py @@ -0,0 +1,152 @@ +# Tests for the CensoredData class. + +import pytest +import numpy as np +from numpy.testing import assert_equal, assert_array_equal +from scipy.stats import CensoredData + + +class TestCensoredData: + + def test_basic(self): + uncensored = [1] + left = [0] + right = [2, 5] + interval = [[2, 3]] + data = CensoredData(uncensored, left=left, right=right, + interval=interval) + assert_equal(data._uncensored, uncensored) + assert_equal(data._left, left) + assert_equal(data._right, right) + assert_equal(data._interval, interval) + + udata = data._uncensor() + assert_equal(udata, np.concatenate((uncensored, left, right, + np.mean(interval, axis=1)))) + + def test_right_censored(self): + x = np.array([0, 3, 2.5]) + is_censored = np.array([0, 1, 0], dtype=bool) + data = CensoredData.right_censored(x, is_censored) + assert_equal(data._uncensored, x[~is_censored]) + assert_equal(data._right, x[is_censored]) + assert_equal(data._left, []) + assert_equal(data._interval, np.empty((0, 2))) + + def test_left_censored(self): + x = np.array([0, 3, 2.5]) + is_censored = np.array([0, 1, 0], dtype=bool) + data = CensoredData.left_censored(x, is_censored) + assert_equal(data._uncensored, x[~is_censored]) + assert_equal(data._left, x[is_censored]) + assert_equal(data._right, []) + assert_equal(data._interval, np.empty((0, 2))) + + def test_interval_censored_basic(self): + a = [0.5, 2.0, 3.0, 5.5] + b = [1.0, 2.5, 3.5, 7.0] + data = CensoredData.interval_censored(low=a, high=b) + assert_array_equal(data._interval, np.array(list(zip(a, b)))) + assert data._uncensored.shape == (0,) + assert data._left.shape == (0,) + assert data._right.shape == (0,) + + def test_interval_censored_mixed(self): + # This is actually a mix of uncensored, left-censored, right-censored + # and interval-censored data. Check that when the `interval_censored` + # class method is used, the data is correctly separated into the + # appropriate arrays. + a = [0.5, -np.inf, -13.0, 2.0, 1.0, 10.0, -1.0] + b = [0.5, 2500.0, np.inf, 3.0, 1.0, 11.0, np.inf] + data = CensoredData.interval_censored(low=a, high=b) + assert_array_equal(data._interval, [[2.0, 3.0], [10.0, 11.0]]) + assert_array_equal(data._uncensored, [0.5, 1.0]) + assert_array_equal(data._left, [2500.0]) + assert_array_equal(data._right, [-13.0, -1.0]) + + def test_interval_to_other_types(self): + # The interval parameter can represent uncensored and + # left- or right-censored data. Test the conversion of such + # an example to the canonical form in which the different + # types have been split into the separate arrays. + interval = np.array([[0, 1], # interval-censored + [2, 2], # not censored + [3, 3], # not censored + [9, np.inf], # right-censored + [8, np.inf], # right-censored + [-np.inf, 0], # left-censored + [1, 2]]) # interval-censored + data = CensoredData(interval=interval) + assert_equal(data._uncensored, [2, 3]) + assert_equal(data._left, [0]) + assert_equal(data._right, [9, 8]) + assert_equal(data._interval, [[0, 1], [1, 2]]) + + def test_empty_arrays(self): + data = CensoredData(uncensored=[], left=[], right=[], interval=[]) + assert data._uncensored.shape == (0,) + assert data._left.shape == (0,) + assert data._right.shape == (0,) + assert data._interval.shape == (0, 2) + assert len(data) == 0 + + def test_invalid_constructor_args(self): + with pytest.raises(ValueError, match='must be a one-dimensional'): + CensoredData(uncensored=[[1, 2, 3]]) + with pytest.raises(ValueError, match='must be a one-dimensional'): + CensoredData(left=[[1, 2, 3]]) + with pytest.raises(ValueError, match='must be a one-dimensional'): + CensoredData(right=[[1, 2, 3]]) + with pytest.raises(ValueError, match='must be a two-dimensional'): + CensoredData(interval=[[1, 2, 3]]) + + with pytest.raises(ValueError, match='must not contain nan'): + CensoredData(uncensored=[1, np.nan, 2]) + with pytest.raises(ValueError, match='must not contain nan'): + CensoredData(left=[1, np.nan, 2]) + with pytest.raises(ValueError, match='must not contain nan'): + CensoredData(right=[1, np.nan, 2]) + with pytest.raises(ValueError, match='must not contain nan'): + CensoredData(interval=[[1, np.nan], [2, 3]]) + + with pytest.raises(ValueError, + match='both values must not be infinite'): + CensoredData(interval=[[1, 3], [2, 9], [np.inf, np.inf]]) + + with pytest.raises(ValueError, + match='left value must not exceed the right'): + CensoredData(interval=[[1, 0], [2, 2]]) + + @pytest.mark.parametrize('func', [CensoredData.left_censored, + CensoredData.right_censored]) + def test_invalid_left_right_censored_args(self, func): + with pytest.raises(ValueError, + match='`x` must be one-dimensional'): + func([[1, 2, 3]], [0, 1, 1]) + with pytest.raises(ValueError, + match='`censored` must be one-dimensional'): + func([1, 2, 3], [[0, 1, 1]]) + with pytest.raises(ValueError, match='`x` must not contain'): + func([1, 2, np.nan], [0, 1, 1]) + with pytest.raises(ValueError, match='must have the same length'): + func([1, 2, 3], [0, 0, 1, 1]) + + def test_invalid_censored_args(self): + with pytest.raises(ValueError, + match='`low` must be a one-dimensional'): + CensoredData.interval_censored(low=[[3]], high=[4, 5]) + with pytest.raises(ValueError, + match='`high` must be a one-dimensional'): + CensoredData.interval_censored(low=[3], high=[[4, 5]]) + with pytest.raises(ValueError, match='`low` must not contain'): + CensoredData.interval_censored([1, 2, np.nan], [0, 1, 1]) + with pytest.raises(ValueError, match='must have the same length'): + CensoredData.interval_censored([1, 2, 3], [0, 0, 1, 1]) + + def test_count_censored(self): + x = [1, 2, 3] + # data1 has no censored data. + data1 = CensoredData(x) + assert data1.num_censored() == 0 + data2 = CensoredData(uncensored=[2.5], left=[10], interval=[[0, 1]]) + assert data2.num_censored() == 2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_contingency.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_contingency.py new file mode 100644 index 0000000000000000000000000000000000000000..ea7fad58b0d6eee2815cfced77d293f10551343f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_contingency.py @@ -0,0 +1,241 @@ +import numpy as np +from numpy.testing import (assert_equal, assert_array_equal, + assert_array_almost_equal, assert_approx_equal, + assert_allclose) +import pytest +from pytest import raises as assert_raises +from scipy.special import xlogy +from scipy.stats.contingency import (margins, expected_freq, + chi2_contingency, association) + + +def test_margins(): + a = np.array([1]) + m = margins(a) + assert_equal(len(m), 1) + m0 = m[0] + assert_array_equal(m0, np.array([1])) + + a = np.array([[1]]) + m0, m1 = margins(a) + expected0 = np.array([[1]]) + expected1 = np.array([[1]]) + assert_array_equal(m0, expected0) + assert_array_equal(m1, expected1) + + a = np.arange(12).reshape(2, 6) + m0, m1 = margins(a) + expected0 = np.array([[15], [51]]) + expected1 = np.array([[6, 8, 10, 12, 14, 16]]) + assert_array_equal(m0, expected0) + assert_array_equal(m1, expected1) + + a = np.arange(24).reshape(2, 3, 4) + m0, m1, m2 = margins(a) + expected0 = np.array([[[66]], [[210]]]) + expected1 = np.array([[[60], [92], [124]]]) + expected2 = np.array([[[60, 66, 72, 78]]]) + assert_array_equal(m0, expected0) + assert_array_equal(m1, expected1) + assert_array_equal(m2, expected2) + + +def test_expected_freq(): + assert_array_equal(expected_freq([1]), np.array([1.0])) + + observed = np.array([[[2, 0], [0, 2]], [[0, 2], [2, 0]], [[1, 1], [1, 1]]]) + e = expected_freq(observed) + assert_array_equal(e, np.ones_like(observed)) + + observed = np.array([[10, 10, 20], [20, 20, 20]]) + e = expected_freq(observed) + correct = np.array([[12., 12., 16.], [18., 18., 24.]]) + assert_array_almost_equal(e, correct) + + +def test_chi2_contingency_trivial(): + # Some very simple tests for chi2_contingency. + + # A trivial case + obs = np.array([[1, 2], [1, 2]]) + chi2, p, dof, expected = chi2_contingency(obs, correction=False) + assert_equal(chi2, 0.0) + assert_equal(p, 1.0) + assert_equal(dof, 1) + assert_array_equal(obs, expected) + + # A *really* trivial case: 1-D data. + obs = np.array([1, 2, 3]) + chi2, p, dof, expected = chi2_contingency(obs, correction=False) + assert_equal(chi2, 0.0) + assert_equal(p, 1.0) + assert_equal(dof, 0) + assert_array_equal(obs, expected) + + +def test_chi2_contingency_R(): + # Some test cases that were computed independently, using R. + + # Rcode = \ + # """ + # # Data vector. + # data <- c( + # 12, 34, 23, 4, 47, 11, + # 35, 31, 11, 34, 10, 18, + # 12, 32, 9, 18, 13, 19, + # 12, 12, 14, 9, 33, 25 + # ) + # + # # Create factor tags:r=rows, c=columns, t=tiers + # r <- factor(gl(4, 2*3, 2*3*4, labels=c("r1", "r2", "r3", "r4"))) + # c <- factor(gl(3, 1, 2*3*4, labels=c("c1", "c2", "c3"))) + # t <- factor(gl(2, 3, 2*3*4, labels=c("t1", "t2"))) + # + # # 3-way Chi squared test of independence + # s = summary(xtabs(data~r+c+t)) + # print(s) + # """ + # Routput = \ + # """ + # Call: xtabs(formula = data ~ r + c + t) + # Number of cases in table: 478 + # Number of factors: 3 + # Test for independence of all factors: + # Chisq = 102.17, df = 17, p-value = 3.514e-14 + # """ + obs = np.array( + [[[12, 34, 23], + [35, 31, 11], + [12, 32, 9], + [12, 12, 14]], + [[4, 47, 11], + [34, 10, 18], + [18, 13, 19], + [9, 33, 25]]]) + chi2, p, dof, expected = chi2_contingency(obs) + assert_approx_equal(chi2, 102.17, significant=5) + assert_approx_equal(p, 3.514e-14, significant=4) + assert_equal(dof, 17) + + # Rcode = \ + # """ + # # Data vector. + # data <- c( + # # + # 12, 17, + # 11, 16, + # # + # 11, 12, + # 15, 16, + # # + # 23, 15, + # 30, 22, + # # + # 14, 17, + # 15, 16 + # ) + # + # # Create factor tags:r=rows, c=columns, d=depths(?), t=tiers + # r <- factor(gl(2, 2, 2*2*2*2, labels=c("r1", "r2"))) + # c <- factor(gl(2, 1, 2*2*2*2, labels=c("c1", "c2"))) + # d <- factor(gl(2, 4, 2*2*2*2, labels=c("d1", "d2"))) + # t <- factor(gl(2, 8, 2*2*2*2, labels=c("t1", "t2"))) + # + # # 4-way Chi squared test of independence + # s = summary(xtabs(data~r+c+d+t)) + # print(s) + # """ + # Routput = \ + # """ + # Call: xtabs(formula = data ~ r + c + d + t) + # Number of cases in table: 262 + # Number of factors: 4 + # Test for independence of all factors: + # Chisq = 8.758, df = 11, p-value = 0.6442 + # """ + obs = np.array( + [[[[12, 17], + [11, 16]], + [[11, 12], + [15, 16]]], + [[[23, 15], + [30, 22]], + [[14, 17], + [15, 16]]]]) + chi2, p, dof, expected = chi2_contingency(obs) + assert_approx_equal(chi2, 8.758, significant=4) + assert_approx_equal(p, 0.6442, significant=4) + assert_equal(dof, 11) + + +def test_chi2_contingency_g(): + c = np.array([[15, 60], [15, 90]]) + g, p, dof, e = chi2_contingency(c, lambda_='log-likelihood', + correction=False) + assert_allclose(g, 2*xlogy(c, c/e).sum()) + + g, p, dof, e = chi2_contingency(c, lambda_='log-likelihood', + correction=True) + c_corr = c + np.array([[-0.5, 0.5], [0.5, -0.5]]) + assert_allclose(g, 2*xlogy(c_corr, c_corr/e).sum()) + + c = np.array([[10, 12, 10], [12, 10, 10]]) + g, p, dof, e = chi2_contingency(c, lambda_='log-likelihood') + assert_allclose(g, 2*xlogy(c, c/e).sum()) + + +def test_chi2_contingency_bad_args(): + # Test that "bad" inputs raise a ValueError. + + # Negative value in the array of observed frequencies. + obs = np.array([[-1, 10], [1, 2]]) + assert_raises(ValueError, chi2_contingency, obs) + + # The zeros in this will result in zeros in the array + # of expected frequencies. + obs = np.array([[0, 1], [0, 1]]) + assert_raises(ValueError, chi2_contingency, obs) + + # A degenerate case: `observed` has size 0. + obs = np.empty((0, 8)) + assert_raises(ValueError, chi2_contingency, obs) + + +def test_chi2_contingency_yates_gh13875(): + # Magnitude of Yates' continuity correction should not exceed difference + # between expected and observed value of the statistic; see gh-13875 + observed = np.array([[1573, 3], [4, 0]]) + p = chi2_contingency(observed)[1] + assert_allclose(p, 1, rtol=1e-12) + + +@pytest.mark.parametrize("correction", [False, True]) +def test_result(correction): + obs = np.array([[1, 2], [1, 2]]) + res = chi2_contingency(obs, correction=correction) + assert_equal((res.statistic, res.pvalue, res.dof, res.expected_freq), res) + + +def test_bad_association_args(): + # Invalid Test Statistic + assert_raises(ValueError, association, [[1, 2], [3, 4]], "X") + # Invalid array shape + assert_raises(ValueError, association, [[[1, 2]], [[3, 4]]], "cramer") + # chi2_contingency exception + assert_raises(ValueError, association, [[-1, 10], [1, 2]], 'cramer') + # Invalid Array Item Data Type + assert_raises(ValueError, association, + np.array([[1, 2], ["dd", 4]], dtype=object), 'cramer') + + +@pytest.mark.parametrize('stat, expected', + [('cramer', 0.09222412010290792), + ('tschuprow', 0.0775509319944633), + ('pearson', 0.12932925727138758)]) +def test_assoc(stat, expected): + # 2d Array + obs1 = np.array([[12, 13, 14, 15, 16], + [17, 16, 18, 19, 11], + [9, 15, 14, 12, 11]]) + a = association(observed=obs1, method=stat) + assert_allclose(a, expected) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..7afee32a52b7f7d0fbeb4997cc1961e6783ced8c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_basic.py @@ -0,0 +1,1016 @@ +import sys +import numpy as np +import numpy.testing as npt +import pytest +from pytest import raises as assert_raises +from scipy.integrate import IntegrationWarning +import itertools + +from scipy import stats +from .common_tests import (check_normalization, check_moment, + check_mean_expect, + check_var_expect, check_skew_expect, + check_kurt_expect, check_entropy, + check_private_entropy, check_entropy_vect_scale, + check_edge_support, check_named_args, + check_random_state_property, + check_meth_dtype, check_ppf_dtype, + check_cmplx_deriv, + check_pickling, check_rvs_broadcast, + check_freezing, check_munp_expect,) +from scipy.stats._distr_params import distcont +from scipy.stats._distn_infrastructure import rv_continuous_frozen + +""" +Test all continuous distributions. + +Parameters were chosen for those distributions that pass the +Kolmogorov-Smirnov test. This provides safe parameters for each +distributions so that we can perform further testing of class methods. + +These tests currently check only/mostly for serious errors and exceptions, +not for numerically exact results. +""" + +# Note that you need to add new distributions you want tested +# to _distr_params + +DECIMAL = 5 # specify the precision of the tests # increased from 0 to 5 +_IS_32BIT = (sys.maxsize < 2**32) + +# For skipping test_cont_basic +distslow = ['recipinvgauss', 'vonmises', 'kappa4', 'vonmises_line', + 'gausshyper', 'norminvgauss', 'geninvgauss', 'genhyperbolic', + 'truncnorm', 'truncweibull_min'] + +# distxslow are sorted by speed (very slow to slow) +distxslow = ['studentized_range', 'kstwo', 'ksone', 'wrapcauchy', 'genexpon'] + +# For skipping test_moments, which is already marked slow +distxslow_test_moments = ['studentized_range', 'vonmises', 'vonmises_line', + 'ksone', 'kstwo', 'recipinvgauss', 'genexpon'] + +# skip check_fit_args (test is slow) +skip_fit_test_mle = ['exponpow', 'exponweib', 'gausshyper', 'genexpon', + 'halfgennorm', 'gompertz', 'johnsonsb', 'johnsonsu', + 'kappa4', 'ksone', 'kstwo', 'kstwobign', 'mielke', 'ncf', + 'nct', 'powerlognorm', 'powernorm', 'recipinvgauss', + 'trapezoid', 'vonmises', 'vonmises_line', 'levy_stable', + 'rv_histogram_instance', 'studentized_range'] + +# these were really slow in `test_fit`.py. +# note that this list is used to skip both fit_test and fit_fix tests +slow_fit_test_mm = ['argus', 'exponpow', 'exponweib', 'gausshyper', 'genexpon', + 'genhalflogistic', 'halfgennorm', 'gompertz', 'johnsonsb', + 'kappa4', 'kstwobign', 'recipinvgauss', + 'trapezoid', 'truncexpon', 'vonmises', 'vonmises_line', + 'studentized_range'] +# pearson3 fails due to something weird +# the first list fails due to non-finite distribution moments encountered +# most of the rest fail due to integration warnings +# pearson3 is overridden as not implemented due to gh-11746 +fail_fit_test_mm = (['alpha', 'betaprime', 'bradford', 'burr', 'burr12', + 'cauchy', 'crystalball', 'f', 'fisk', 'foldcauchy', + 'genextreme', 'genpareto', 'halfcauchy', 'invgamma', + 'jf_skew_t', 'kappa3', 'levy', 'levy_l', 'loglaplace', + 'lomax', 'mielke', 'nakagami', 'ncf', 'skewcauchy', 't', + 'tukeylambda', 'invweibull', 'rel_breitwigner'] + + ['genhyperbolic', 'johnsonsu', 'ksone', 'kstwo', + 'nct', 'pareto', 'powernorm', 'powerlognorm'] + + ['pearson3']) + +skip_fit_test = {"MLE": skip_fit_test_mle, + "MM": slow_fit_test_mm + fail_fit_test_mm} + +# skip check_fit_args_fix (test is slow) +skip_fit_fix_test_mle = ['burr', 'exponpow', 'exponweib', 'gausshyper', + 'genexpon', 'halfgennorm', 'gompertz', 'johnsonsb', + 'johnsonsu', 'kappa4', 'ksone', 'kstwo', 'kstwobign', + 'levy_stable', 'mielke', 'ncf', 'ncx2', + 'powerlognorm', 'powernorm', 'rdist', 'recipinvgauss', + 'trapezoid', 'truncpareto', 'vonmises', 'vonmises_line', + 'studentized_range'] +# the first list fails due to non-finite distribution moments encountered +# most of the rest fail due to integration warnings +# pearson3 is overridden as not implemented due to gh-11746 +fail_fit_fix_test_mm = (['alpha', 'betaprime', 'burr', 'burr12', 'cauchy', + 'crystalball', 'f', 'fisk', 'foldcauchy', + 'genextreme', 'genpareto', 'halfcauchy', 'invgamma', + 'jf_skew_t', 'kappa3', 'levy', 'levy_l', 'loglaplace', + 'lomax', 'mielke', 'nakagami', 'ncf', 'nct', + 'skewcauchy', 't', 'truncpareto', 'invweibull'] + + ['genhyperbolic', 'johnsonsu', 'ksone', 'kstwo', + 'pareto', 'powernorm', 'powerlognorm'] + + ['pearson3']) +skip_fit_fix_test = {"MLE": skip_fit_fix_test_mle, + "MM": slow_fit_test_mm + fail_fit_fix_test_mm} + +# These distributions fail the complex derivative test below. +# Here 'fail' mean produce wrong results and/or raise exceptions, depending +# on the implementation details of corresponding special functions. +# cf https://github.com/scipy/scipy/pull/4979 for a discussion. +fails_cmplx = {'argus', 'beta', 'betaprime', 'chi', 'chi2', 'cosine', + 'dgamma', 'dweibull', 'erlang', 'f', 'foldcauchy', 'gamma', + 'gausshyper', 'gengamma', 'genhyperbolic', + 'geninvgauss', 'gennorm', 'genpareto', + 'halfcauchy', 'halfgennorm', 'invgamma', 'jf_skew_t', + 'ksone', 'kstwo', 'kstwobign', 'levy_l', 'loggamma', + 'logistic', 'loguniform', 'maxwell', 'nakagami', + 'ncf', 'nct', 'ncx2', 'norminvgauss', 'pearson3', + 'powerlaw', 'rdist', 'reciprocal', 'rice', + 'skewnorm', 't', 'truncweibull_min', + 'tukeylambda', 'vonmises', 'vonmises_line', + 'rv_histogram_instance', 'truncnorm', 'studentized_range', + 'johnsonsb', 'halflogistic', 'rel_breitwigner'} + + +# rv_histogram instances, with uniform and non-uniform bins; +# stored as (dist, arg) tuples for cases_test_cont_basic +# and cases_test_moments. +histogram_test_instances = [] +case1 = {'a': [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 8, 8, 9], 'bins': 8} # equal width bins +case2 = {'a': [1, 1], 'bins': [0, 1, 10]} # unequal width bins +for case, density in itertools.product([case1, case2], [True, False]): + _hist = np.histogram(**case, density=density) + _rv_hist = stats.rv_histogram(_hist, density=density) + histogram_test_instances.append((_rv_hist, tuple())) + + +def cases_test_cont_basic(): + for distname, arg in distcont[:] + histogram_test_instances: + if distname == 'levy_stable': + continue + elif distname in distslow: + yield pytest.param(distname, arg, marks=pytest.mark.slow) + elif distname in distxslow: + yield pytest.param(distname, arg, marks=pytest.mark.xslow) + else: + yield distname, arg + + +@pytest.mark.parametrize('distname,arg', cases_test_cont_basic()) +@pytest.mark.parametrize('sn, n_fit_samples', [(500, 200)]) +def test_cont_basic(distname, arg, sn, n_fit_samples): + # this test skips slow distributions + + try: + distfn = getattr(stats, distname) + except TypeError: + distfn = distname + distname = 'rv_histogram_instance' + + rng = np.random.RandomState(765456) + rvs = distfn.rvs(size=sn, *arg, random_state=rng) + m, v = distfn.stats(*arg) + + if distname not in {'laplace_asymmetric'}: + check_sample_meanvar_(m, v, rvs) + check_cdf_ppf(distfn, arg, distname) + check_sf_isf(distfn, arg, distname) + check_cdf_sf(distfn, arg, distname) + check_ppf_isf(distfn, arg, distname) + check_pdf(distfn, arg, distname) + check_pdf_logpdf(distfn, arg, distname) + check_pdf_logpdf_at_endpoints(distfn, arg, distname) + check_cdf_logcdf(distfn, arg, distname) + check_sf_logsf(distfn, arg, distname) + check_ppf_broadcast(distfn, arg, distname) + + alpha = 0.01 + if distname == 'rv_histogram_instance': + check_distribution_rvs(distfn.cdf, arg, alpha, rvs) + elif distname != 'geninvgauss': + # skip kstest for geninvgauss since cdf is too slow; see test for + # rv generation in TestGenInvGauss in test_distributions.py + check_distribution_rvs(distname, arg, alpha, rvs) + + locscale_defaults = (0, 1) + meths = [distfn.pdf, distfn.logpdf, distfn.cdf, distfn.logcdf, + distfn.logsf] + # make sure arguments are within support + spec_x = {'weibull_max': -0.5, 'levy_l': -0.5, + 'pareto': 1.5, 'truncpareto': 3.2, 'tukeylambda': 0.3, + 'rv_histogram_instance': 5.0} + x = spec_x.get(distname, 0.5) + if distname == 'invweibull': + arg = (1,) + elif distname == 'ksone': + arg = (3,) + + check_named_args(distfn, x, arg, locscale_defaults, meths) + check_random_state_property(distfn, arg) + + if distname in ['rel_breitwigner'] and _IS_32BIT: + # gh18414 + pytest.skip("fails on Linux 32-bit") + else: + check_pickling(distfn, arg) + check_freezing(distfn, arg) + + # Entropy + if distname not in ['kstwobign', 'kstwo', 'ncf']: + check_entropy(distfn, arg, distname) + + if distfn.numargs == 0: + check_vecentropy(distfn, arg) + + if (distfn.__class__._entropy != stats.rv_continuous._entropy + and distname != 'vonmises'): + check_private_entropy(distfn, arg, stats.rv_continuous) + + with npt.suppress_warnings() as sup: + sup.filter(IntegrationWarning, "The occurrence of roundoff error") + sup.filter(IntegrationWarning, "Extremely bad integrand") + sup.filter(RuntimeWarning, "invalid value") + check_entropy_vect_scale(distfn, arg) + + check_retrieving_support(distfn, arg) + check_edge_support(distfn, arg) + + check_meth_dtype(distfn, arg, meths) + check_ppf_dtype(distfn, arg) + + if distname not in fails_cmplx: + check_cmplx_deriv(distfn, arg) + + if distname != 'truncnorm': + check_ppf_private(distfn, arg, distname) + + for method in ["MLE", "MM"]: + if distname not in skip_fit_test[method]: + check_fit_args(distfn, arg, rvs[:n_fit_samples], method) + + if distname not in skip_fit_fix_test[method]: + check_fit_args_fix(distfn, arg, rvs[:n_fit_samples], method) + + +@pytest.mark.parametrize('distname,arg', cases_test_cont_basic()) +def test_rvs_scalar(distname, arg): + # rvs should return a scalar when given scalar arguments (gh-12428) + try: + distfn = getattr(stats, distname) + except TypeError: + distfn = distname + distname = 'rv_histogram_instance' + + assert np.isscalar(distfn.rvs(*arg)) + assert np.isscalar(distfn.rvs(*arg, size=())) + assert np.isscalar(distfn.rvs(*arg, size=None)) + + +def test_levy_stable_random_state_property(): + # levy_stable only implements rvs(), so it is skipped in the + # main loop in test_cont_basic(). Here we apply just the test + # check_random_state_property to levy_stable. + check_random_state_property(stats.levy_stable, (0.5, 0.1)) + + +def cases_test_moments(): + fail_normalization = set() + fail_higher = {'ncf'} + fail_moment = {'johnsonsu'} # generic `munp` is inaccurate for johnsonsu + + for distname, arg in distcont[:] + histogram_test_instances: + if distname == 'levy_stable': + continue + + if distname in distxslow_test_moments: + yield pytest.param(distname, arg, True, True, True, True, + marks=pytest.mark.xslow(reason="too slow")) + continue + + cond1 = distname not in fail_normalization + cond2 = distname not in fail_higher + cond3 = distname not in fail_moment + + marks = list() + # Currently unused, `marks` can be used to add a timeout to a test of + # a specific distribution. For example, this shows how a timeout could + # be added for the 'skewnorm' distribution: + # + # marks = list() + # if distname == 'skewnorm': + # marks.append(pytest.mark.timeout(300)) + + yield pytest.param(distname, arg, cond1, cond2, cond3, + False, marks=marks) + + if not cond1 or not cond2 or not cond3: + # Run the distributions that have issues twice, once skipping the + # not_ok parts, once with the not_ok parts but marked as knownfail + yield pytest.param(distname, arg, True, True, True, True, + marks=[pytest.mark.xfail] + marks) + + +@pytest.mark.slow +@pytest.mark.parametrize('distname,arg,normalization_ok,higher_ok,moment_ok,' + 'is_xfailing', + cases_test_moments()) +def test_moments(distname, arg, normalization_ok, higher_ok, moment_ok, + is_xfailing): + try: + distfn = getattr(stats, distname) + except TypeError: + distfn = distname + distname = 'rv_histogram_instance' + + with npt.suppress_warnings() as sup: + sup.filter(IntegrationWarning, + "The integral is probably divergent, or slowly convergent.") + sup.filter(IntegrationWarning, + "The maximum number of subdivisions.") + sup.filter(IntegrationWarning, + "The algorithm does not converge.") + + if is_xfailing: + sup.filter(IntegrationWarning) + + m, v, s, k = distfn.stats(*arg, moments='mvsk') + + with np.errstate(all="ignore"): + if normalization_ok: + check_normalization(distfn, arg, distname) + + if higher_ok: + check_mean_expect(distfn, arg, m, distname) + check_skew_expect(distfn, arg, m, v, s, distname) + check_var_expect(distfn, arg, m, v, distname) + check_kurt_expect(distfn, arg, m, v, k, distname) + check_munp_expect(distfn, arg, distname) + + check_loc_scale(distfn, arg, m, v, distname) + + if moment_ok: + check_moment(distfn, arg, m, v, distname) + + +@pytest.mark.parametrize('dist,shape_args', distcont) +def test_rvs_broadcast(dist, shape_args): + if dist in ['gausshyper', 'studentized_range']: + pytest.skip("too slow") + + if dist in ['rel_breitwigner'] and _IS_32BIT: + # gh18414 + pytest.skip("fails on Linux 32-bit") + + # If shape_only is True, it means the _rvs method of the + # distribution uses more than one random number to generate a random + # variate. That means the result of using rvs with broadcasting or + # with a nontrivial size will not necessarily be the same as using the + # numpy.vectorize'd version of rvs(), so we can only compare the shapes + # of the results, not the values. + # Whether or not a distribution is in the following list is an + # implementation detail of the distribution, not a requirement. If + # the implementation the rvs() method of a distribution changes, this + # test might also have to be changed. + shape_only = dist in ['argus', 'betaprime', 'dgamma', 'dweibull', + 'exponnorm', 'genhyperbolic', 'geninvgauss', + 'levy_stable', 'nct', 'norminvgauss', 'rice', + 'skewnorm', 'semicircular', 'gennorm', 'loggamma'] + + distfunc = getattr(stats, dist) + loc = np.zeros(2) + scale = np.ones((3, 1)) + nargs = distfunc.numargs + allargs = [] + bshape = [3, 2] + # Generate shape parameter arguments... + for k in range(nargs): + shp = (k + 4,) + (1,)*(k + 2) + allargs.append(shape_args[k]*np.ones(shp)) + bshape.insert(0, k + 4) + allargs.extend([loc, scale]) + # bshape holds the expected shape when loc, scale, and the shape + # parameters are all broadcast together. + + check_rvs_broadcast(distfunc, dist, allargs, bshape, shape_only, 'd') + + +# Expected values of the SF, CDF, PDF were computed using +# mpmath with mpmath.mp.dps = 50 and output at 20: +# +# def ks(x, n): +# x = mpmath.mpf(x) +# logp = -mpmath.power(6.0*n*x+1.0, 2)/18.0/n +# sf, cdf = mpmath.exp(logp), -mpmath.expm1(logp) +# pdf = (6.0*n*x+1.0) * 2 * sf/3 +# print(mpmath.nstr(sf, 20), mpmath.nstr(cdf, 20), mpmath.nstr(pdf, 20)) +# +# Tests use 1/n < x < 1-1/n and n > 1e6 to use the asymptotic computation. +# Larger x has a smaller sf. +@pytest.mark.parametrize('x,n,sf,cdf,pdf,rtol', + [(2.0e-5, 1000000000, + 0.44932297307934442379, 0.55067702692065557621, + 35946.137394996276407, 5e-15), + (2.0e-9, 1000000000, + 0.99999999061111115519, 9.3888888448132728224e-9, + 8.6666665852962971765, 5e-14), + (5.0e-4, 1000000000, + 7.1222019433090374624e-218, 1.0, + 1.4244408634752704094e-211, 5e-14)]) +def test_gh17775_regression(x, n, sf, cdf, pdf, rtol): + # Regression test for gh-17775. In scipy 1.9.3 and earlier, + # these test would fail. + # + # KS one asymptotic sf ~ e^(-(6nx+1)^2 / 18n) + # Given a large 32-bit integer n, 6n will overflow in the c implementation. + # Example of broken behaviour: + # ksone.sf(2.0e-5, 1000000000) == 0.9374359693473666 + ks = stats.ksone + vals = np.array([ks.sf(x, n), ks.cdf(x, n), ks.pdf(x, n)]) + expected = np.array([sf, cdf, pdf]) + npt.assert_allclose(vals, expected, rtol=rtol) + # The sf+cdf must sum to 1.0. + npt.assert_equal(vals[0] + vals[1], 1.0) + # Check inverting the (potentially very small) sf (uses a lower tolerance) + npt.assert_allclose([ks.isf(sf, n)], [x], rtol=1e-8) + + +def test_rvs_gh2069_regression(): + # Regression tests for gh-2069. In scipy 0.17 and earlier, + # these tests would fail. + # + # A typical example of the broken behavior: + # >>> norm.rvs(loc=np.zeros(5), scale=np.ones(5)) + # array([-2.49613705, -2.49613705, -2.49613705, -2.49613705, -2.49613705]) + rng = np.random.RandomState(123) + vals = stats.norm.rvs(loc=np.zeros(5), scale=1, random_state=rng) + d = np.diff(vals) + npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!") + vals = stats.norm.rvs(loc=0, scale=np.ones(5), random_state=rng) + d = np.diff(vals) + npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!") + vals = stats.norm.rvs(loc=np.zeros(5), scale=np.ones(5), random_state=rng) + d = np.diff(vals) + npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!") + vals = stats.norm.rvs(loc=np.array([[0], [0]]), scale=np.ones(5), + random_state=rng) + d = np.diff(vals.ravel()) + npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!") + + assert_raises(ValueError, stats.norm.rvs, [[0, 0], [0, 0]], + [[1, 1], [1, 1]], 1) + assert_raises(ValueError, stats.gamma.rvs, [2, 3, 4, 5], 0, 1, (2, 2)) + assert_raises(ValueError, stats.gamma.rvs, [1, 1, 1, 1], [0, 0, 0, 0], + [[1], [2]], (4,)) + + +def test_nomodify_gh9900_regression(): + # Regression test for gh-9990 + # Prior to gh-9990, calls to stats.truncnorm._cdf() use what ever was + # set inside the stats.truncnorm instance during stats.truncnorm.cdf(). + # This could cause issues with multi-threaded code. + # Since then, the calls to cdf() are not permitted to modify the global + # stats.truncnorm instance. + tn = stats.truncnorm + # Use the right-half truncated normal + # Check that the cdf and _cdf return the same result. + npt.assert_almost_equal(tn.cdf(1, 0, np.inf), + 0.6826894921370859) + npt.assert_almost_equal(tn._cdf([1], [0], [np.inf]), + 0.6826894921370859) + + # Now use the left-half truncated normal + npt.assert_almost_equal(tn.cdf(-1, -np.inf, 0), + 0.31731050786291415) + npt.assert_almost_equal(tn._cdf([-1], [-np.inf], [0]), + 0.31731050786291415) + + # Check that the right-half truncated normal _cdf hasn't changed + npt.assert_almost_equal(tn._cdf([1], [0], [np.inf]), + 0.6826894921370859) # Not 1.6826894921370859 + npt.assert_almost_equal(tn.cdf(1, 0, np.inf), + 0.6826894921370859) + + # Check that the left-half truncated normal _cdf hasn't changed + npt.assert_almost_equal(tn._cdf([-1], [-np.inf], [0]), + 0.31731050786291415) # Not -0.6826894921370859 + npt.assert_almost_equal(tn.cdf(1, -np.inf, 0), + 1) # Not 1.6826894921370859 + npt.assert_almost_equal(tn.cdf(-1, -np.inf, 0), + 0.31731050786291415) # Not -0.6826894921370859 + + +def test_broadcast_gh9990_regression(): + # Regression test for gh-9990 + # The x-value 7 only lies within the support of 4 of the supplied + # distributions. Prior to 9990, one array passed to + # stats.reciprocal._cdf would have 4 elements, but an array + # previously stored by stats.reciprocal_argcheck() would have 6, leading + # to a broadcast error. + a = np.array([1, 2, 3, 4, 5, 6]) + b = np.array([8, 16, 1, 32, 1, 48]) + ans = [stats.reciprocal.cdf(7, _a, _b) for _a, _b in zip(a,b)] + npt.assert_array_almost_equal(stats.reciprocal.cdf(7, a, b), ans) + + ans = [stats.reciprocal.cdf(1, _a, _b) for _a, _b in zip(a,b)] + npt.assert_array_almost_equal(stats.reciprocal.cdf(1, a, b), ans) + + ans = [stats.reciprocal.cdf(_a, _a, _b) for _a, _b in zip(a,b)] + npt.assert_array_almost_equal(stats.reciprocal.cdf(a, a, b), ans) + + ans = [stats.reciprocal.cdf(_b, _a, _b) for _a, _b in zip(a,b)] + npt.assert_array_almost_equal(stats.reciprocal.cdf(b, a, b), ans) + + +def test_broadcast_gh7933_regression(): + # Check broadcast works + stats.truncnorm.logpdf( + np.array([3.0, 2.0, 1.0]), + a=(1.5 - np.array([6.0, 5.0, 4.0])) / 3.0, + b=np.inf, + loc=np.array([6.0, 5.0, 4.0]), + scale=3.0 + ) + + +def test_gh2002_regression(): + # Add a check that broadcast works in situations where only some + # x-values are compatible with some of the shape arguments. + x = np.r_[-2:2:101j] + a = np.r_[-np.ones(50), np.ones(51)] + expected = [stats.truncnorm.pdf(_x, _a, np.inf) for _x, _a in zip(x, a)] + ans = stats.truncnorm.pdf(x, a, np.inf) + npt.assert_array_almost_equal(ans, expected) + + +def test_gh1320_regression(): + # Check that the first example from gh-1320 now works. + c = 2.62 + stats.genextreme.ppf(0.5, np.array([[c], [c + 0.5]])) + # The other examples in gh-1320 appear to have stopped working + # some time ago. + # ans = stats.genextreme.moment(2, np.array([c, c + 0.5])) + # expected = np.array([25.50105963, 115.11191437]) + # stats.genextreme.moment(5, np.array([[c], [c + 0.5]])) + # stats.genextreme.moment(5, np.array([c, c + 0.5])) + + +def test_method_of_moments(): + # example from https://en.wikipedia.org/wiki/Method_of_moments_(statistics) + np.random.seed(1234) + x = [0, 0, 0, 0, 1] + a = 1/5 - 2*np.sqrt(3)/5 + b = 1/5 + 2*np.sqrt(3)/5 + # force use of method of moments (uniform.fit is overridden) + loc, scale = super(type(stats.uniform), stats.uniform).fit(x, method="MM") + npt.assert_almost_equal(loc, a, decimal=4) + npt.assert_almost_equal(loc+scale, b, decimal=4) + + +def check_sample_meanvar_(popmean, popvar, sample): + if np.isfinite(popmean): + check_sample_mean(sample, popmean) + if np.isfinite(popvar): + check_sample_var(sample, popvar) + + +def check_sample_mean(sample, popmean): + # Checks for unlikely difference between sample mean and population mean + prob = stats.ttest_1samp(sample, popmean).pvalue + assert prob > 0.01 + + +def check_sample_var(sample, popvar): + # check that population mean lies within the CI bootstrapped from the + # sample. This used to be a chi-squared test for variance, but there were + # too many false positives + res = stats.bootstrap( + (sample,), + lambda x, axis: x.var(ddof=1, axis=axis), + confidence_level=0.995, + ) + conf = res.confidence_interval + low, high = conf.low, conf.high + assert low <= popvar <= high + + +def check_cdf_ppf(distfn, arg, msg): + values = [0.001, 0.5, 0.999] + npt.assert_almost_equal(distfn.cdf(distfn.ppf(values, *arg), *arg), + values, decimal=DECIMAL, err_msg=msg + + ' - cdf-ppf roundtrip') + + +def check_sf_isf(distfn, arg, msg): + npt.assert_almost_equal(distfn.sf(distfn.isf([0.1, 0.5, 0.9], *arg), *arg), + [0.1, 0.5, 0.9], decimal=DECIMAL, err_msg=msg + + ' - sf-isf roundtrip') + + +def check_cdf_sf(distfn, arg, msg): + npt.assert_almost_equal(distfn.cdf([0.1, 0.9], *arg), + 1.0 - distfn.sf([0.1, 0.9], *arg), + decimal=DECIMAL, err_msg=msg + + ' - cdf-sf relationship') + + +def check_ppf_isf(distfn, arg, msg): + p = np.array([0.1, 0.9]) + npt.assert_almost_equal(distfn.isf(p, *arg), distfn.ppf(1-p, *arg), + decimal=DECIMAL, err_msg=msg + + ' - ppf-isf relationship') + + +def check_pdf(distfn, arg, msg): + # compares pdf at median with numerical derivative of cdf + median = distfn.ppf(0.5, *arg) + eps = 1e-6 + pdfv = distfn.pdf(median, *arg) + if (pdfv < 1e-4) or (pdfv > 1e4): + # avoid checking a case where pdf is close to zero or + # huge (singularity) + median = median + 0.1 + pdfv = distfn.pdf(median, *arg) + cdfdiff = (distfn.cdf(median + eps, *arg) - + distfn.cdf(median - eps, *arg))/eps/2.0 + # replace with better diff and better test (more points), + # actually, this works pretty well + msg += ' - cdf-pdf relationship' + npt.assert_almost_equal(pdfv, cdfdiff, decimal=DECIMAL, err_msg=msg) + + +def check_pdf_logpdf(distfn, args, msg): + # compares pdf at several points with the log of the pdf + points = np.array([0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]) + vals = distfn.ppf(points, *args) + vals = vals[np.isfinite(vals)] + pdf = distfn.pdf(vals, *args) + logpdf = distfn.logpdf(vals, *args) + pdf = pdf[(pdf != 0) & np.isfinite(pdf)] + logpdf = logpdf[np.isfinite(logpdf)] + msg += " - logpdf-log(pdf) relationship" + npt.assert_almost_equal(np.log(pdf), logpdf, decimal=7, err_msg=msg) + + +def check_pdf_logpdf_at_endpoints(distfn, args, msg): + # compares pdf with the log of the pdf at the (finite) end points + points = np.array([0, 1]) + vals = distfn.ppf(points, *args) + vals = vals[np.isfinite(vals)] + pdf = distfn.pdf(vals, *args) + logpdf = distfn.logpdf(vals, *args) + pdf = pdf[(pdf != 0) & np.isfinite(pdf)] + logpdf = logpdf[np.isfinite(logpdf)] + msg += " - logpdf-log(pdf) relationship" + npt.assert_almost_equal(np.log(pdf), logpdf, decimal=7, err_msg=msg) + + +def check_sf_logsf(distfn, args, msg): + # compares sf at several points with the log of the sf + points = np.array([0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0]) + vals = distfn.ppf(points, *args) + vals = vals[np.isfinite(vals)] + sf = distfn.sf(vals, *args) + logsf = distfn.logsf(vals, *args) + sf = sf[sf != 0] + logsf = logsf[np.isfinite(logsf)] + msg += " - logsf-log(sf) relationship" + npt.assert_almost_equal(np.log(sf), logsf, decimal=7, err_msg=msg) + + +def check_cdf_logcdf(distfn, args, msg): + # compares cdf at several points with the log of the cdf + points = np.array([0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0]) + vals = distfn.ppf(points, *args) + vals = vals[np.isfinite(vals)] + cdf = distfn.cdf(vals, *args) + logcdf = distfn.logcdf(vals, *args) + cdf = cdf[cdf != 0] + logcdf = logcdf[np.isfinite(logcdf)] + msg += " - logcdf-log(cdf) relationship" + npt.assert_almost_equal(np.log(cdf), logcdf, decimal=7, err_msg=msg) + + +def check_ppf_broadcast(distfn, arg, msg): + # compares ppf for multiple argsets. + num_repeats = 5 + args = [] * num_repeats + if arg: + args = [np.array([_] * num_repeats) for _ in arg] + + median = distfn.ppf(0.5, *arg) + medians = distfn.ppf(0.5, *args) + msg += " - ppf multiple" + npt.assert_almost_equal(medians, [median] * num_repeats, decimal=7, err_msg=msg) + + +def check_distribution_rvs(dist, args, alpha, rvs): + # dist is either a cdf function or name of a distribution in scipy.stats. + # args are the args for scipy.stats.dist(*args) + # alpha is a significance level, ~0.01 + # rvs is array_like of random variables + # test from scipy.stats.tests + # this version reuses existing random variables + D, pval = stats.kstest(rvs, dist, args=args, N=1000) + if (pval < alpha): + # The rvs passed in failed the K-S test, which _could_ happen + # but is unlikely if alpha is small enough. + # Repeat the test with a new sample of rvs. + # Generate 1000 rvs, perform a K-S test that the new sample of rvs + # are distributed according to the distribution. + D, pval = stats.kstest(dist, dist, args=args, N=1000) + npt.assert_(pval > alpha, "D = " + str(D) + "; pval = " + str(pval) + + "; alpha = " + str(alpha) + "\nargs = " + str(args)) + + +def check_vecentropy(distfn, args): + npt.assert_equal(distfn.vecentropy(*args), distfn._entropy(*args)) + + +def check_loc_scale(distfn, arg, m, v, msg): + # Make `loc` and `scale` arrays to catch bugs like gh-13580 where + # `loc` and `scale` arrays improperly broadcast with shapes. + loc, scale = np.array([10.0, 20.0]), np.array([10.0, 20.0]) + mt, vt = distfn.stats(*arg, loc=loc, scale=scale) + npt.assert_allclose(m*scale + loc, mt) + npt.assert_allclose(v*scale*scale, vt) + + +def check_ppf_private(distfn, arg, msg): + # fails by design for truncnorm self.nb not defined + ppfs = distfn._ppf(np.array([0.1, 0.5, 0.9]), *arg) + npt.assert_(not np.any(np.isnan(ppfs)), msg + 'ppf private is nan') + + +def check_retrieving_support(distfn, args): + loc, scale = 1, 2 + supp = distfn.support(*args) + supp_loc_scale = distfn.support(*args, loc=loc, scale=scale) + npt.assert_almost_equal(np.array(supp)*scale + loc, + np.array(supp_loc_scale)) + + +def check_fit_args(distfn, arg, rvs, method): + with np.errstate(all='ignore'), npt.suppress_warnings() as sup: + sup.filter(category=RuntimeWarning, + message="The shape parameter of the erlang") + sup.filter(category=RuntimeWarning, + message="floating point number truncated") + vals = distfn.fit(rvs, method=method) + vals2 = distfn.fit(rvs, optimizer='powell', method=method) + # Only check the length of the return; accuracy tested in test_fit.py + npt.assert_(len(vals) == 2+len(arg)) + npt.assert_(len(vals2) == 2+len(arg)) + + +def check_fit_args_fix(distfn, arg, rvs, method): + with np.errstate(all='ignore'), npt.suppress_warnings() as sup: + sup.filter(category=RuntimeWarning, + message="The shape parameter of the erlang") + + vals = distfn.fit(rvs, floc=0, method=method) + vals2 = distfn.fit(rvs, fscale=1, method=method) + npt.assert_(len(vals) == 2+len(arg)) + npt.assert_(vals[-2] == 0) + npt.assert_(vals2[-1] == 1) + npt.assert_(len(vals2) == 2+len(arg)) + if len(arg) > 0: + vals3 = distfn.fit(rvs, f0=arg[0], method=method) + npt.assert_(len(vals3) == 2+len(arg)) + npt.assert_(vals3[0] == arg[0]) + if len(arg) > 1: + vals4 = distfn.fit(rvs, f1=arg[1], method=method) + npt.assert_(len(vals4) == 2+len(arg)) + npt.assert_(vals4[1] == arg[1]) + if len(arg) > 2: + vals5 = distfn.fit(rvs, f2=arg[2], method=method) + npt.assert_(len(vals5) == 2+len(arg)) + npt.assert_(vals5[2] == arg[2]) + + +@pytest.mark.parametrize('method', ['pdf', 'logpdf', 'cdf', 'logcdf', + 'sf', 'logsf', 'ppf', 'isf']) +@pytest.mark.parametrize('distname, args', distcont) +def test_methods_with_lists(method, distname, args): + # Test that the continuous distributions can accept Python lists + # as arguments. + dist = getattr(stats, distname) + f = getattr(dist, method) + if distname == 'invweibull' and method.startswith('log'): + x = [1.5, 2] + else: + x = [0.1, 0.2] + + shape2 = [[a]*2 for a in args] + loc = [0, 0.1] + scale = [1, 1.01] + result = f(x, *shape2, loc=loc, scale=scale) + npt.assert_allclose(result, + [f(*v) for v in zip(x, *shape2, loc, scale)], + rtol=1e-14, atol=5e-14) + + +def test_burr_fisk_moment_gh13234_regression(): + vals0 = stats.burr.moment(1, 5, 4) + assert isinstance(vals0, float) + + vals1 = stats.fisk.moment(1, 8) + assert isinstance(vals1, float) + + +def test_moments_with_array_gh12192_regression(): + # array loc and scalar scale + vals0 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=1) + expected0 = np.array([1., 2., 3.]) + npt.assert_equal(vals0, expected0) + + # array loc and invalid scalar scale + vals1 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=-1) + expected1 = np.array([np.nan, np.nan, np.nan]) + npt.assert_equal(vals1, expected1) + + # array loc and array scale with invalid entries + vals2 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), + scale=[-3, 1, 0]) + expected2 = np.array([np.nan, 2., np.nan]) + npt.assert_equal(vals2, expected2) + + # (loc == 0) & (scale < 0) + vals3 = stats.norm.moment(order=2, loc=0, scale=-4) + expected3 = np.nan + npt.assert_equal(vals3, expected3) + assert isinstance(vals3, expected3.__class__) + + # array loc with 0 entries and scale with invalid entries + vals4 = stats.norm.moment(order=2, loc=[1, 0, 2], scale=[3, -4, -5]) + expected4 = np.array([10., np.nan, np.nan]) + npt.assert_equal(vals4, expected4) + + # all(loc == 0) & (array scale with invalid entries) + vals5 = stats.norm.moment(order=2, loc=[0, 0, 0], scale=[5., -2, 100.]) + expected5 = np.array([25., np.nan, 10000.]) + npt.assert_equal(vals5, expected5) + + # all( (loc == 0) & (scale < 0) ) + vals6 = stats.norm.moment(order=2, loc=[0, 0, 0], scale=[-5., -2, -100.]) + expected6 = np.array([np.nan, np.nan, np.nan]) + npt.assert_equal(vals6, expected6) + + # scalar args, loc, and scale + vals7 = stats.chi.moment(order=2, df=1, loc=0, scale=0) + expected7 = np.nan + npt.assert_equal(vals7, expected7) + assert isinstance(vals7, expected7.__class__) + + # array args, scalar loc, and scalar scale + vals8 = stats.chi.moment(order=2, df=[1, 2, 3], loc=0, scale=0) + expected8 = np.array([np.nan, np.nan, np.nan]) + npt.assert_equal(vals8, expected8) + + # array args, array loc, and array scale + vals9 = stats.chi.moment(order=2, df=[1, 2, 3], loc=[1., 0., 2.], + scale=[1., -3., 0.]) + expected9 = np.array([3.59576912, np.nan, np.nan]) + npt.assert_allclose(vals9, expected9, rtol=1e-8) + + # (n > 4), all(loc != 0), and all(scale != 0) + vals10 = stats.norm.moment(5, [1., 2.], [1., 2.]) + expected10 = np.array([26., 832.]) + npt.assert_allclose(vals10, expected10, rtol=1e-13) + + # test broadcasting and more + a = [-1.1, 0, 1, 2.2, np.pi] + b = [-1.1, 0, 1, 2.2, np.pi] + loc = [-1.1, 0, np.sqrt(2)] + scale = [-2.1, 0, 1, 2.2, np.pi] + + a = np.array(a).reshape((-1, 1, 1, 1)) + b = np.array(b).reshape((-1, 1, 1)) + loc = np.array(loc).reshape((-1, 1)) + scale = np.array(scale) + + vals11 = stats.beta.moment(order=2, a=a, b=b, loc=loc, scale=scale) + + a, b, loc, scale = np.broadcast_arrays(a, b, loc, scale) + + for i in np.ndenumerate(a): + with np.errstate(invalid='ignore', divide='ignore'): + i = i[0] # just get the index + # check against same function with scalar input + expected = stats.beta.moment(order=2, a=a[i], b=b[i], + loc=loc[i], scale=scale[i]) + np.testing.assert_equal(vals11[i], expected) + + +def test_broadcasting_in_moments_gh12192_regression(): + vals0 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=[[1]]) + expected0 = np.array([[1., 2., 3.]]) + npt.assert_equal(vals0, expected0) + assert vals0.shape == expected0.shape + + vals1 = stats.norm.moment(order=1, loc=np.array([[1], [2], [3]]), + scale=[1, 2, 3]) + expected1 = np.array([[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]) + npt.assert_equal(vals1, expected1) + assert vals1.shape == expected1.shape + + vals2 = stats.chi.moment(order=1, df=[1., 2., 3.], loc=0., scale=1.) + expected2 = np.array([0.79788456, 1.25331414, 1.59576912]) + npt.assert_allclose(vals2, expected2, rtol=1e-8) + assert vals2.shape == expected2.shape + + vals3 = stats.chi.moment(order=1, df=[[1.], [2.], [3.]], loc=[0., 1., 2.], + scale=[-1., 0., 3.]) + expected3 = np.array([[np.nan, np.nan, 4.39365368], + [np.nan, np.nan, 5.75994241], + [np.nan, np.nan, 6.78730736]]) + npt.assert_allclose(vals3, expected3, rtol=1e-8) + assert vals3.shape == expected3.shape + + +def test_kappa3_array_gh13582(): + # https://github.com/scipy/scipy/pull/15140#issuecomment-994958241 + shapes = [0.5, 1.5, 2.5, 3.5, 4.5] + moments = 'mvsk' + res = np.array([[stats.kappa3.stats(shape, moments=moment) + for shape in shapes] for moment in moments]) + res2 = np.array(stats.kappa3.stats(shapes, moments=moments)) + npt.assert_allclose(res, res2) + + +@pytest.mark.xslow +def test_kappa4_array_gh13582(): + h = np.array([-0.5, 2.5, 3.5, 4.5, -3]) + k = np.array([-0.5, 1, -1.5, 0, 3.5]) + moments = 'mvsk' + res = np.array([[stats.kappa4.stats(h[i], k[i], moments=moment) + for i in range(5)] for moment in moments]) + res2 = np.array(stats.kappa4.stats(h, k, moments=moments)) + npt.assert_allclose(res, res2) + + # https://github.com/scipy/scipy/pull/15250#discussion_r775112913 + h = np.array([-1, -1/4, -1/4, 1, -1, 0]) + k = np.array([1, 1, 1/2, -1/3, -1, 0]) + res = np.array([[stats.kappa4.stats(h[i], k[i], moments=moment) + for i in range(6)] for moment in moments]) + res2 = np.array(stats.kappa4.stats(h, k, moments=moments)) + npt.assert_allclose(res, res2) + + # https://github.com/scipy/scipy/pull/15250#discussion_r775115021 + h = np.array([-1, -0.5, 1]) + k = np.array([-1, -0.5, 0, 1])[:, None] + res2 = np.array(stats.kappa4.stats(h, k, moments=moments)) + assert res2.shape == (4, 4, 3) + + +def test_frozen_attributes(): + # gh-14827 reported that all frozen distributions had both pmf and pdf + # attributes; continuous should have pdf and discrete should have pmf. + message = "'rv_continuous_frozen' object has no attribute" + with pytest.raises(AttributeError, match=message): + stats.norm().pmf + with pytest.raises(AttributeError, match=message): + stats.norm().logpmf + stats.norm.pmf = "herring" + frozen_norm = stats.norm() + assert isinstance(frozen_norm, rv_continuous_frozen) + delattr(stats.norm, 'pmf') + + +def test_skewnorm_pdf_gh16038(): + rng = np.random.default_rng(0) + x, a = -np.inf, 0 + npt.assert_equal(stats.skewnorm.pdf(x, a), stats.norm.pdf(x)) + x, a = rng.random(size=(3, 3)), rng.random(size=(3, 3)) + mask = rng.random(size=(3, 3)) < 0.5 + a[mask] = 0 + x_norm = x[mask] + res = stats.skewnorm.pdf(x, a) + npt.assert_equal(res[mask], stats.norm.pdf(x_norm)) + npt.assert_equal(res[~mask], stats.skewnorm.pdf(x[~mask], a[~mask])) + + +# for scalar input, these functions should return scalar output +scalar_out = [['rvs', []], ['pdf', [0]], ['logpdf', [0]], ['cdf', [0]], + ['logcdf', [0]], ['sf', [0]], ['logsf', [0]], ['ppf', [0]], + ['isf', [0]], ['moment', [1]], ['entropy', []], ['expect', []], + ['median', []], ['mean', []], ['std', []], ['var', []]] +scalars_out = [['interval', [0.95]], ['support', []], ['stats', ['mv']]] + + +@pytest.mark.parametrize('case', scalar_out + scalars_out) +def test_scalar_for_scalar(case): + # Some rv_continuous functions returned 0d array instead of NumPy scalar + # Guard against regression + method_name, args = case + method = getattr(stats.norm(), method_name) + res = method(*args) + if case in scalar_out: + assert isinstance(res, np.number) + else: + assert isinstance(res[0], np.number) + assert isinstance(res[1], np.number) + + +def test_scalar_for_scalar2(): + # test methods that are not attributes of frozen distributions + res = stats.norm.fit([1, 2, 3]) + assert isinstance(res[0], np.number) + assert isinstance(res[1], np.number) + res = stats.norm.fit_loc_scale([1, 2, 3]) + assert isinstance(res[0], np.number) + assert isinstance(res[1], np.number) + res = stats.norm.nnlf((0, 1), [1, 2, 3]) + assert isinstance(res, np.number) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_fit_censored.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_fit_censored.py new file mode 100644 index 0000000000000000000000000000000000000000..4508b49712e5bc8975bf2f9b2681ccc6504b0ae0 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_continuous_fit_censored.py @@ -0,0 +1,683 @@ +# Tests for fitting specific distributions to censored data. + +import numpy as np +from numpy.testing import assert_allclose + +from scipy.optimize import fmin +from scipy.stats import (CensoredData, beta, cauchy, chi2, expon, gamma, + gumbel_l, gumbel_r, invgauss, invweibull, laplace, + logistic, lognorm, nct, ncx2, norm, weibull_max, + weibull_min) + + +# In some tests, we'll use this optimizer for improved accuracy. +def optimizer(func, x0, args=(), disp=0): + return fmin(func, x0, args=args, disp=disp, xtol=1e-12, ftol=1e-12) + + +def test_beta(): + """ + Test fitting beta shape parameters to interval-censored data. + + Calculation in R: + + > library(fitdistrplus) + > data <- data.frame(left=c(0.10, 0.50, 0.75, 0.80), + + right=c(0.20, 0.55, 0.90, 0.95)) + > result = fitdistcens(data, 'beta', control=list(reltol=1e-14)) + + > result + Fitting of the distribution ' beta ' on censored data by maximum likelihood + Parameters: + estimate + shape1 1.419941 + shape2 1.027066 + > result$sd + shape1 shape2 + 0.9914177 0.6866565 + """ + data = CensoredData(interval=[[0.10, 0.20], + [0.50, 0.55], + [0.75, 0.90], + [0.80, 0.95]]) + + # For this test, fit only the shape parameters; loc and scale are fixed. + a, b, loc, scale = beta.fit(data, floc=0, fscale=1, optimizer=optimizer) + + assert_allclose(a, 1.419941, rtol=5e-6) + assert_allclose(b, 1.027066, rtol=5e-6) + assert loc == 0 + assert scale == 1 + + +def test_cauchy_right_censored(): + """ + Test fitting the Cauchy distribution to right-censored data. + + Calculation in R, with two values not censored [1, 10] and + one right-censored value [30]. + + > library(fitdistrplus) + > data <- data.frame(left=c(1, 10, 30), right=c(1, 10, NA)) + > result = fitdistcens(data, 'cauchy', control=list(reltol=1e-14)) + > result + Fitting of the distribution ' cauchy ' on censored data by maximum + likelihood + Parameters: + estimate + location 7.100001 + scale 7.455866 + """ + data = CensoredData(uncensored=[1, 10], right=[30]) + loc, scale = cauchy.fit(data, optimizer=optimizer) + assert_allclose(loc, 7.10001, rtol=5e-6) + assert_allclose(scale, 7.455866, rtol=5e-6) + + +def test_cauchy_mixed(): + """ + Test fitting the Cauchy distribution to data with mixed censoring. + + Calculation in R, with: + * two values not censored [1, 10], + * one left-censored [1], + * one right-censored [30], and + * one interval-censored [[4, 8]]. + + > library(fitdistrplus) + > data <- data.frame(left=c(NA, 1, 4, 10, 30), right=c(1, 1, 8, 10, NA)) + > result = fitdistcens(data, 'cauchy', control=list(reltol=1e-14)) + > result + Fitting of the distribution ' cauchy ' on censored data by maximum + likelihood + Parameters: + estimate + location 4.605150 + scale 5.900852 + """ + data = CensoredData(uncensored=[1, 10], left=[1], right=[30], + interval=[[4, 8]]) + loc, scale = cauchy.fit(data, optimizer=optimizer) + assert_allclose(loc, 4.605150, rtol=5e-6) + assert_allclose(scale, 5.900852, rtol=5e-6) + + +def test_chi2_mixed(): + """ + Test fitting just the shape parameter (df) of chi2 to mixed data. + + Calculation in R, with: + * two values not censored [1, 10], + * one left-censored [1], + * one right-censored [30], and + * one interval-censored [[4, 8]]. + + > library(fitdistrplus) + > data <- data.frame(left=c(NA, 1, 4, 10, 30), right=c(1, 1, 8, 10, NA)) + > result = fitdistcens(data, 'chisq', control=list(reltol=1e-14)) + > result + Fitting of the distribution ' chisq ' on censored data by maximum + likelihood + Parameters: + estimate + df 5.060329 + """ + data = CensoredData(uncensored=[1, 10], left=[1], right=[30], + interval=[[4, 8]]) + df, loc, scale = chi2.fit(data, floc=0, fscale=1, optimizer=optimizer) + assert_allclose(df, 5.060329, rtol=5e-6) + assert loc == 0 + assert scale == 1 + + +def test_expon_right_censored(): + """ + For the exponential distribution with loc=0, the exact solution for + fitting n uncensored points x[0]...x[n-1] and m right-censored points + x[n]..x[n+m-1] is + + scale = sum(x)/n + + That is, divide the sum of all the values (not censored and + right-censored) by the number of uncensored values. (See, for example, + https://en.wikipedia.org/wiki/Censoring_(statistics)#Likelihood.) + + The second derivative of the log-likelihood function is + + n/scale**2 - 2*sum(x)/scale**3 + + from which the estimate of the standard error can be computed. + + ----- + + Calculation in R, for reference only. The R results are not + used in the test. + + > library(fitdistrplus) + > dexps <- function(x, scale) { + + return(dexp(x, 1/scale)) + + } + > pexps <- function(q, scale) { + + return(pexp(q, 1/scale)) + + } + > left <- c(1, 2.5, 3, 6, 7.5, 10, 12, 12, 14.5, 15, + + 16, 16, 20, 20, 21, 22) + > right <- c(1, 2.5, 3, 6, 7.5, 10, 12, 12, 14.5, 15, + + NA, NA, NA, NA, NA, NA) + > result = fitdistcens(data, 'exps', start=list(scale=mean(data$left)), + + control=list(reltol=1e-14)) + > result + Fitting of the distribution ' exps ' on censored data by maximum likelihood + Parameters: + estimate + scale 19.85 + > result$sd + scale + 6.277119 + """ + # This data has 10 uncensored values and 6 right-censored values. + obs = [1, 2.5, 3, 6, 7.5, 10, 12, 12, 14.5, 15, 16, 16, 20, 20, 21, 22] + cens = [False]*10 + [True]*6 + data = CensoredData.right_censored(obs, cens) + + loc, scale = expon.fit(data, floc=0, optimizer=optimizer) + + assert loc == 0 + # Use the analytical solution to compute the expected value. This + # is the sum of the observed values divided by the number of uncensored + # values. + n = len(data) - data.num_censored() + total = data._uncensored.sum() + data._right.sum() + expected = total / n + assert_allclose(scale, expected, 1e-8) + + +def test_gamma_right_censored(): + """ + Fit gamma shape and scale to data with one right-censored value. + + Calculation in R: + + > library(fitdistrplus) + > data <- data.frame(left=c(2.5, 2.9, 3.8, 9.1, 9.3, 12.0, 23.0, 25.0), + + right=c(2.5, 2.9, 3.8, 9.1, 9.3, 12.0, 23.0, NA)) + > result = fitdistcens(data, 'gamma', start=list(shape=1, scale=10), + + control=list(reltol=1e-13)) + > result + Fitting of the distribution ' gamma ' on censored data by maximum + likelihood + Parameters: + estimate + shape 1.447623 + scale 8.360197 + > result$sd + shape scale + 0.7053086 5.1016531 + """ + # The last value is right-censored. + x = CensoredData.right_censored([2.5, 2.9, 3.8, 9.1, 9.3, 12.0, 23.0, + 25.0], + [0]*7 + [1]) + + a, loc, scale = gamma.fit(x, floc=0, optimizer=optimizer) + + assert_allclose(a, 1.447623, rtol=5e-6) + assert loc == 0 + assert_allclose(scale, 8.360197, rtol=5e-6) + + +def test_gumbel(): + """ + Fit gumbel_l and gumbel_r to censored data. + + This R calculation should match gumbel_r. + + > library(evd) + > library(fitdistrplus) + > data = data.frame(left=c(0, 2, 3, 9, 10, 10), + + right=c(1, 2, 3, 9, NA, NA)) + > result = fitdistcens(data, 'gumbel', + + control=list(reltol=1e-14), + + start=list(loc=4, scale=5)) + > result + Fitting of the distribution ' gumbel ' on censored data by maximum + likelihood + Parameters: + estimate + loc 4.487853 + scale 4.843640 + """ + # First value is interval-censored. Last two are right-censored. + uncensored = np.array([2, 3, 9]) + right = np.array([10, 10]) + interval = np.array([[0, 1]]) + data = CensoredData(uncensored, right=right, interval=interval) + loc, scale = gumbel_r.fit(data, optimizer=optimizer) + assert_allclose(loc, 4.487853, rtol=5e-6) + assert_allclose(scale, 4.843640, rtol=5e-6) + + # Negate the data and reverse the intervals, and test with gumbel_l. + data2 = CensoredData(-uncensored, left=-right, + interval=-interval[:, ::-1]) + # Fitting gumbel_l to data2 should give the same result as above, but + # with loc negated. + loc2, scale2 = gumbel_l.fit(data2, optimizer=optimizer) + assert_allclose(loc2, -4.487853, rtol=5e-6) + assert_allclose(scale2, 4.843640, rtol=5e-6) + + +def test_invgauss(): + """ + Fit just the shape parameter of invgauss to data with one value + left-censored and one value right-censored. + + Calculation in R; using a fixed dispersion parameter amounts to fixing + the scale to be 1. + + > library(statmod) + > library(fitdistrplus) + > left <- c(NA, 0.4813096, 0.5571880, 0.5132463, 0.3801414, 0.5904386, + + 0.4822340, 0.3478597, 3, 0.7191797, 1.5810902, 0.4442299) + > right <- c(0.15, 0.4813096, 0.5571880, 0.5132463, 0.3801414, 0.5904386, + + 0.4822340, 0.3478597, NA, 0.7191797, 1.5810902, 0.4442299) + > data <- data.frame(left=left, right=right) + > result = fitdistcens(data, 'invgauss', control=list(reltol=1e-12), + + fix.arg=list(dispersion=1), start=list(mean=3)) + > result + Fitting of the distribution ' invgauss ' on censored data by maximum + likelihood + Parameters: + estimate + mean 0.853469 + Fixed parameters: + value + dispersion 1 + > result$sd + mean + 0.247636 + + Here's the R calculation with the dispersion as a free parameter to + be fit. + + > result = fitdistcens(data, 'invgauss', control=list(reltol=1e-12), + + start=list(mean=3, dispersion=1)) + > result + Fitting of the distribution ' invgauss ' on censored data by maximum + likelihood + Parameters: + estimate + mean 0.8699819 + dispersion 1.2261362 + + The parametrization of the inverse Gaussian distribution in the + `statmod` package is not the same as in SciPy (see + https://arxiv.org/abs/1603.06687 + for details). The translation from R to SciPy is + + scale = 1/dispersion + mu = mean * dispersion + + > 1/result$estimate['dispersion'] # 1/dispersion + dispersion + 0.8155701 + > result$estimate['mean'] * result$estimate['dispersion'] + mean + 1.066716 + + Those last two values are the SciPy scale and shape parameters. + """ + # One point is left-censored, and one is right-censored. + x = [0.4813096, 0.5571880, 0.5132463, 0.3801414, + 0.5904386, 0.4822340, 0.3478597, 0.7191797, + 1.5810902, 0.4442299] + data = CensoredData(uncensored=x, left=[0.15], right=[3]) + + # Fit only the shape parameter. + mu, loc, scale = invgauss.fit(data, floc=0, fscale=1, optimizer=optimizer) + + assert_allclose(mu, 0.853469, rtol=5e-5) + assert loc == 0 + assert scale == 1 + + # Fit the shape and scale. + mu, loc, scale = invgauss.fit(data, floc=0, optimizer=optimizer) + + assert_allclose(mu, 1.066716, rtol=5e-5) + assert loc == 0 + assert_allclose(scale, 0.8155701, rtol=5e-5) + + +def test_invweibull(): + """ + Fit invweibull to censored data. + + Here is the calculation in R. The 'frechet' distribution from the evd + package matches SciPy's invweibull distribution. The `loc` parameter + is fixed at 0. + + > library(evd) + > library(fitdistrplus) + > data = data.frame(left=c(0, 2, 3, 9, 10, 10), + + right=c(1, 2, 3, 9, NA, NA)) + > result = fitdistcens(data, 'frechet', + + control=list(reltol=1e-14), + + start=list(loc=4, scale=5)) + > result + Fitting of the distribution ' frechet ' on censored data by maximum + likelihood + Parameters: + estimate + scale 2.7902200 + shape 0.6379845 + Fixed parameters: + value + loc 0 + """ + # In the R data, the first value is interval-censored, and the last + # two are right-censored. The rest are not censored. + data = CensoredData(uncensored=[2, 3, 9], right=[10, 10], + interval=[[0, 1]]) + c, loc, scale = invweibull.fit(data, floc=0, optimizer=optimizer) + assert_allclose(c, 0.6379845, rtol=5e-6) + assert loc == 0 + assert_allclose(scale, 2.7902200, rtol=5e-6) + + +def test_laplace(): + """ + Fir the Laplace distribution to left- and right-censored data. + + Calculation in R: + + > library(fitdistrplus) + > dlaplace <- function(x, location=0, scale=1) { + + return(0.5*exp(-abs((x - location)/scale))/scale) + + } + > plaplace <- function(q, location=0, scale=1) { + + z <- (q - location)/scale + + s <- sign(z) + + f <- -s*0.5*exp(-abs(z)) + (s+1)/2 + + return(f) + + } + > left <- c(NA, -41.564, 50.0, 15.7384, 50.0, 10.0452, -2.0684, + + -19.5399, 50.0, 9.0005, 27.1227, 4.3113, -3.7372, + + 25.3111, 14.7987, 34.0887, 50.0, 42.8496, 18.5862, + + 32.8921, 9.0448, -27.4591, NA, 19.5083, -9.7199) + > right <- c(-50.0, -41.564, NA, 15.7384, NA, 10.0452, -2.0684, + + -19.5399, NA, 9.0005, 27.1227, 4.3113, -3.7372, + + 25.3111, 14.7987, 34.0887, NA, 42.8496, 18.5862, + + 32.8921, 9.0448, -27.4591, -50.0, 19.5083, -9.7199) + > data <- data.frame(left=left, right=right) + > result <- fitdistcens(data, 'laplace', start=list(location=10, scale=10), + + control=list(reltol=1e-13)) + > result + Fitting of the distribution ' laplace ' on censored data by maximum + likelihood + Parameters: + estimate + location 14.79870 + scale 30.93601 + > result$sd + location scale + 0.1758864 7.0972125 + """ + # The value -50 is left-censored, and the value 50 is right-censored. + obs = np.array([-50.0, -41.564, 50.0, 15.7384, 50.0, 10.0452, -2.0684, + -19.5399, 50.0, 9.0005, 27.1227, 4.3113, -3.7372, + 25.3111, 14.7987, 34.0887, 50.0, 42.8496, 18.5862, + 32.8921, 9.0448, -27.4591, -50.0, 19.5083, -9.7199]) + x = obs[(obs != -50.0) & (obs != 50)] + left = obs[obs == -50.0] + right = obs[obs == 50.0] + data = CensoredData(uncensored=x, left=left, right=right) + loc, scale = laplace.fit(data, loc=10, scale=10, optimizer=optimizer) + assert_allclose(loc, 14.79870, rtol=5e-6) + assert_allclose(scale, 30.93601, rtol=5e-6) + + +def test_logistic(): + """ + Fit the logistic distribution to left-censored data. + + Calculation in R: + > library(fitdistrplus) + > left = c(13.5401, 37.4235, 11.906 , 13.998 , NA , 0.4023, NA , + + 10.9044, 21.0629, 9.6985, NA , 12.9016, 39.164 , 34.6396, + + NA , 20.3665, 16.5889, 18.0952, 45.3818, 35.3306, 8.4949, + + 3.4041, NA , 7.2828, 37.1265, 6.5969, 17.6868, 17.4977, + + 16.3391, 36.0541) + > right = c(13.5401, 37.4235, 11.906 , 13.998 , 0. , 0.4023, 0. , + + 10.9044, 21.0629, 9.6985, 0. , 12.9016, 39.164 , 34.6396, + + 0. , 20.3665, 16.5889, 18.0952, 45.3818, 35.3306, 8.4949, + + 3.4041, 0. , 7.2828, 37.1265, 6.5969, 17.6868, 17.4977, + + 16.3391, 36.0541) + > data = data.frame(left=left, right=right) + > result = fitdistcens(data, 'logis', control=list(reltol=1e-14)) + > result + Fitting of the distribution ' logis ' on censored data by maximum + likelihood + Parameters: + estimate + location 14.633459 + scale 9.232736 + > result$sd + location scale + 2.931505 1.546879 + """ + # Values that are zero are left-censored; the true values are less than 0. + x = np.array([13.5401, 37.4235, 11.906, 13.998, 0.0, 0.4023, 0.0, 10.9044, + 21.0629, 9.6985, 0.0, 12.9016, 39.164, 34.6396, 0.0, 20.3665, + 16.5889, 18.0952, 45.3818, 35.3306, 8.4949, 3.4041, 0.0, + 7.2828, 37.1265, 6.5969, 17.6868, 17.4977, 16.3391, + 36.0541]) + data = CensoredData.left_censored(x, censored=(x == 0)) + loc, scale = logistic.fit(data, optimizer=optimizer) + assert_allclose(loc, 14.633459, rtol=5e-7) + assert_allclose(scale, 9.232736, rtol=5e-6) + + +def test_lognorm(): + """ + Ref: https://math.montana.edu/jobo/st528/documents/relc.pdf + + The data is the locomotive control time to failure example that starts + on page 8. That's the 8th page in the PDF; the page number shown in + the text is 270). + The document includes SAS output for the data. + """ + # These are the uncensored measurements. There are also 59 right-censored + # measurements where the lower bound is 135. + miles_to_fail = [22.5, 37.5, 46.0, 48.5, 51.5, 53.0, 54.5, 57.5, 66.5, + 68.0, 69.5, 76.5, 77.0, 78.5, 80.0, 81.5, 82.0, 83.0, + 84.0, 91.5, 93.5, 102.5, 107.0, 108.5, 112.5, 113.5, + 116.0, 117.0, 118.5, 119.0, 120.0, 122.5, 123.0, 127.5, + 131.0, 132.5, 134.0] + + data = CensoredData.right_censored(miles_to_fail + [135]*59, + [0]*len(miles_to_fail) + [1]*59) + sigma, loc, scale = lognorm.fit(data, floc=0) + + assert loc == 0 + # Convert the lognorm parameters to the mu and sigma of the underlying + # normal distribution. + mu = np.log(scale) + # The expected results are from the 17th page of the PDF document + # (labeled page 279), in the SAS output on the right side of the page. + assert_allclose(mu, 5.1169, rtol=5e-4) + assert_allclose(sigma, 0.7055, rtol=5e-3) + + +def test_nct(): + """ + Test fitting the noncentral t distribution to censored data. + + Calculation in R: + + > library(fitdistrplus) + > data <- data.frame(left=c(1, 2, 3, 5, 8, 10, 25, 25), + + right=c(1, 2, 3, 5, 8, 10, NA, NA)) + > result = fitdistcens(data, 't', control=list(reltol=1e-14), + + start=list(df=1, ncp=2)) + > result + Fitting of the distribution ' t ' on censored data by maximum likelihood + Parameters: + estimate + df 0.5432336 + ncp 2.8893565 + + """ + data = CensoredData.right_censored([1, 2, 3, 5, 8, 10, 25, 25], + [0, 0, 0, 0, 0, 0, 1, 1]) + # Fit just the shape parameter df and nc; loc and scale are fixed. + with np.errstate(over='ignore'): # remove context when gh-14901 is closed + df, nc, loc, scale = nct.fit(data, floc=0, fscale=1, + optimizer=optimizer) + assert_allclose(df, 0.5432336, rtol=5e-6) + assert_allclose(nc, 2.8893565, rtol=5e-6) + assert loc == 0 + assert scale == 1 + + +def test_ncx2(): + """ + Test fitting the shape parameters (df, ncp) of ncx2 to mixed data. + + Calculation in R, with + * 5 not censored values [2.7, 0.2, 6.5, 0.4, 0.1], + * 1 interval-censored value [[0.6, 1.0]], and + * 2 right-censored values [8, 8]. + + > library(fitdistrplus) + > data <- data.frame(left=c(2.7, 0.2, 6.5, 0.4, 0.1, 0.6, 8, 8), + + right=c(2.7, 0.2, 6.5, 0.4, 0.1, 1.0, NA, NA)) + > result = fitdistcens(data, 'chisq', control=list(reltol=1e-14), + + start=list(df=1, ncp=2)) + > result + Fitting of the distribution ' chisq ' on censored data by maximum + likelihood + Parameters: + estimate + df 1.052871 + ncp 2.362934 + """ + data = CensoredData(uncensored=[2.7, 0.2, 6.5, 0.4, 0.1], right=[8, 8], + interval=[[0.6, 1.0]]) + with np.errstate(over='ignore'): # remove context when gh-14901 is closed + df, ncp, loc, scale = ncx2.fit(data, floc=0, fscale=1, + optimizer=optimizer) + assert_allclose(df, 1.052871, rtol=5e-6) + assert_allclose(ncp, 2.362934, rtol=5e-6) + assert loc == 0 + assert scale == 1 + + +def test_norm(): + """ + Test fitting the normal distribution to interval-censored data. + + Calculation in R: + + > library(fitdistrplus) + > data <- data.frame(left=c(0.10, 0.50, 0.75, 0.80), + + right=c(0.20, 0.55, 0.90, 0.95)) + > result = fitdistcens(data, 'norm', control=list(reltol=1e-14)) + + > result + Fitting of the distribution ' norm ' on censored data by maximum likelihood + Parameters: + estimate + mean 0.5919990 + sd 0.2868042 + > result$sd + mean sd + 0.1444432 0.1029451 + """ + data = CensoredData(interval=[[0.10, 0.20], + [0.50, 0.55], + [0.75, 0.90], + [0.80, 0.95]]) + + loc, scale = norm.fit(data, optimizer=optimizer) + + assert_allclose(loc, 0.5919990, rtol=5e-6) + assert_allclose(scale, 0.2868042, rtol=5e-6) + + +def test_weibull_censored1(): + # Ref: http://www.ams.sunysb.edu/~zhu/ams588/Lecture_3_likelihood.pdf + + # Survival times; '*' indicates right-censored. + s = "3,5,6*,8,10*,11*,15,20*,22,23,27*,29,32,35,40,26,28,33*,21,24*" + + times, cens = zip(*[(float(t[0]), len(t) == 2) + for t in [w.split('*') for w in s.split(',')]]) + data = CensoredData.right_censored(times, cens) + + c, loc, scale = weibull_min.fit(data, floc=0) + + # Expected values are from the reference. + assert_allclose(c, 2.149, rtol=1e-3) + assert loc == 0 + assert_allclose(scale, 28.99, rtol=1e-3) + + # Flip the sign of the data, and make the censored values + # left-censored. We should get the same parameters when we fit + # weibull_max to the flipped data. + data2 = CensoredData.left_censored(-np.array(times), cens) + + c2, loc2, scale2 = weibull_max.fit(data2, floc=0) + + assert_allclose(c2, 2.149, rtol=1e-3) + assert loc2 == 0 + assert_allclose(scale2, 28.99, rtol=1e-3) + + +def test_weibull_min_sas1(): + # Data and SAS results from + # https://support.sas.com/documentation/cdl/en/qcug/63922/HTML/default/ + # viewer.htm#qcug_reliability_sect004.htm + + text = """ + 450 0 460 1 1150 0 1150 0 1560 1 + 1600 0 1660 1 1850 1 1850 1 1850 1 + 1850 1 1850 1 2030 1 2030 1 2030 1 + 2070 0 2070 0 2080 0 2200 1 3000 1 + 3000 1 3000 1 3000 1 3100 0 3200 1 + 3450 0 3750 1 3750 1 4150 1 4150 1 + 4150 1 4150 1 4300 1 4300 1 4300 1 + 4300 1 4600 0 4850 1 4850 1 4850 1 + 4850 1 5000 1 5000 1 5000 1 6100 1 + 6100 0 6100 1 6100 1 6300 1 6450 1 + 6450 1 6700 1 7450 1 7800 1 7800 1 + 8100 1 8100 1 8200 1 8500 1 8500 1 + 8500 1 8750 1 8750 0 8750 1 9400 1 + 9900 1 10100 1 10100 1 10100 1 11500 1 + """ + + life, cens = np.array([int(w) for w in text.split()]).reshape(-1, 2).T + life = life/1000.0 + + data = CensoredData.right_censored(life, cens) + + c, loc, scale = weibull_min.fit(data, floc=0, optimizer=optimizer) + assert_allclose(c, 1.0584, rtol=1e-4) + assert_allclose(scale, 26.2968, rtol=1e-5) + assert loc == 0 + + +def test_weibull_min_sas2(): + # http://support.sas.com/documentation/cdl/en/ormpug/67517/HTML/default/ + # viewer.htm#ormpug_nlpsolver_examples06.htm + + # The last two values are right-censored. + days = np.array([143, 164, 188, 188, 190, 192, 206, 209, 213, 216, 220, + 227, 230, 234, 246, 265, 304, 216, 244]) + + data = CensoredData.right_censored(days, [0]*(len(days) - 2) + [1]*2) + + c, loc, scale = weibull_min.fit(data, 1, loc=100, scale=100, + optimizer=optimizer) + + assert_allclose(c, 2.7112, rtol=5e-4) + assert_allclose(loc, 122.03, rtol=5e-4) + assert_allclose(scale, 108.37, rtol=5e-4) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_crosstab.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_crosstab.py new file mode 100644 index 0000000000000000000000000000000000000000..35eda2de983654f0eb7d41aaccf3bd1a2e93505a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_crosstab.py @@ -0,0 +1,115 @@ +import pytest +import numpy as np +from numpy.testing import assert_array_equal, assert_equal +from scipy.stats.contingency import crosstab + + +@pytest.mark.parametrize('sparse', [False, True]) +def test_crosstab_basic(sparse): + a = [0, 0, 9, 9, 0, 0, 9] + b = [2, 1, 3, 1, 2, 3, 3] + expected_avals = [0, 9] + expected_bvals = [1, 2, 3] + expected_count = np.array([[1, 2, 1], + [1, 0, 2]]) + (avals, bvals), count = crosstab(a, b, sparse=sparse) + assert_array_equal(avals, expected_avals) + assert_array_equal(bvals, expected_bvals) + if sparse: + assert_array_equal(count.A, expected_count) + else: + assert_array_equal(count, expected_count) + + +def test_crosstab_basic_1d(): + # Verify that a single input sequence works as expected. + x = [1, 2, 3, 1, 2, 3, 3] + expected_xvals = [1, 2, 3] + expected_count = np.array([2, 2, 3]) + (xvals,), count = crosstab(x) + assert_array_equal(xvals, expected_xvals) + assert_array_equal(count, expected_count) + + +def test_crosstab_basic_3d(): + # Verify the function for three input sequences. + a = 'a' + b = 'b' + x = [0, 0, 9, 9, 0, 0, 9, 9] + y = [a, a, a, a, b, b, b, a] + z = [1, 2, 3, 1, 2, 3, 3, 1] + expected_xvals = [0, 9] + expected_yvals = [a, b] + expected_zvals = [1, 2, 3] + expected_count = np.array([[[1, 1, 0], + [0, 1, 1]], + [[2, 0, 1], + [0, 0, 1]]]) + (xvals, yvals, zvals), count = crosstab(x, y, z) + assert_array_equal(xvals, expected_xvals) + assert_array_equal(yvals, expected_yvals) + assert_array_equal(zvals, expected_zvals) + assert_array_equal(count, expected_count) + + +@pytest.mark.parametrize('sparse', [False, True]) +def test_crosstab_levels(sparse): + a = [0, 0, 9, 9, 0, 0, 9] + b = [1, 2, 3, 1, 2, 3, 3] + expected_avals = [0, 9] + expected_bvals = [0, 1, 2, 3] + expected_count = np.array([[0, 1, 2, 1], + [0, 1, 0, 2]]) + (avals, bvals), count = crosstab(a, b, levels=[None, [0, 1, 2, 3]], + sparse=sparse) + assert_array_equal(avals, expected_avals) + assert_array_equal(bvals, expected_bvals) + if sparse: + assert_array_equal(count.A, expected_count) + else: + assert_array_equal(count, expected_count) + + +@pytest.mark.parametrize('sparse', [False, True]) +def test_crosstab_extra_levels(sparse): + # The pair of values (-1, 3) will be ignored, because we explicitly + # request the counted `a` values to be [0, 9]. + a = [0, 0, 9, 9, 0, 0, 9, -1] + b = [1, 2, 3, 1, 2, 3, 3, 3] + expected_avals = [0, 9] + expected_bvals = [0, 1, 2, 3] + expected_count = np.array([[0, 1, 2, 1], + [0, 1, 0, 2]]) + (avals, bvals), count = crosstab(a, b, levels=[[0, 9], [0, 1, 2, 3]], + sparse=sparse) + assert_array_equal(avals, expected_avals) + assert_array_equal(bvals, expected_bvals) + if sparse: + assert_array_equal(count.A, expected_count) + else: + assert_array_equal(count, expected_count) + + +def test_validation_at_least_one(): + with pytest.raises(TypeError, match='At least one'): + crosstab() + + +def test_validation_same_lengths(): + with pytest.raises(ValueError, match='must have the same length'): + crosstab([1, 2], [1, 2, 3, 4]) + + +def test_validation_sparse_only_two_args(): + with pytest.raises(ValueError, match='only two input sequences'): + crosstab([0, 1, 1], [8, 8, 9], [1, 3, 3], sparse=True) + + +def test_validation_len_levels_matches_args(): + with pytest.raises(ValueError, match='number of input sequences'): + crosstab([0, 1, 1], [8, 8, 9], levels=([0, 1, 2, 3],)) + + +def test_result(): + res = crosstab([0, 1], [1, 2]) + assert_equal((res.elements, res.count), res) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..012e68a8da2d3eb72e8e2e3d73b9ef66b6fc2388 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_basic.py @@ -0,0 +1,548 @@ +import numpy.testing as npt +from numpy.testing import assert_allclose + +import numpy as np +import pytest + +from scipy import stats +from .common_tests import (check_normalization, check_moment, + check_mean_expect, + check_var_expect, check_skew_expect, + check_kurt_expect, check_entropy, + check_private_entropy, check_edge_support, + check_named_args, check_random_state_property, + check_pickling, check_rvs_broadcast, + check_freezing,) +from scipy.stats._distr_params import distdiscrete, invdistdiscrete +from scipy.stats._distn_infrastructure import rv_discrete_frozen + +vals = ([1, 2, 3, 4], [0.1, 0.2, 0.3, 0.4]) +distdiscrete += [[stats.rv_discrete(values=vals), ()]] + +# For these distributions, test_discrete_basic only runs with test mode full +distslow = {'zipfian', 'nhypergeom'} + + +def cases_test_discrete_basic(): + seen = set() + for distname, arg in distdiscrete: + if distname in distslow: + yield pytest.param(distname, arg, distname, marks=pytest.mark.slow) + else: + yield distname, arg, distname not in seen + seen.add(distname) + + +@pytest.mark.parametrize('distname,arg,first_case', cases_test_discrete_basic()) +def test_discrete_basic(distname, arg, first_case): + try: + distfn = getattr(stats, distname) + except TypeError: + distfn = distname + distname = 'sample distribution' + np.random.seed(9765456) + rvs = distfn.rvs(size=2000, *arg) + supp = np.unique(rvs) + m, v = distfn.stats(*arg) + check_cdf_ppf(distfn, arg, supp, distname + ' cdf_ppf') + + check_pmf_cdf(distfn, arg, distname) + check_oth(distfn, arg, supp, distname + ' oth') + check_edge_support(distfn, arg) + + alpha = 0.01 + check_discrete_chisquare(distfn, arg, rvs, alpha, + distname + ' chisquare') + + if first_case: + locscale_defaults = (0,) + meths = [distfn.pmf, distfn.logpmf, distfn.cdf, distfn.logcdf, + distfn.logsf] + # make sure arguments are within support + # for some distributions, this needs to be overridden + spec_k = {'randint': 11, 'hypergeom': 4, 'bernoulli': 0, + 'nchypergeom_wallenius': 6} + k = spec_k.get(distname, 1) + check_named_args(distfn, k, arg, locscale_defaults, meths) + if distname != 'sample distribution': + check_scale_docstring(distfn) + check_random_state_property(distfn, arg) + check_pickling(distfn, arg) + check_freezing(distfn, arg) + + # Entropy + check_entropy(distfn, arg, distname) + if distfn.__class__._entropy != stats.rv_discrete._entropy: + check_private_entropy(distfn, arg, stats.rv_discrete) + + +@pytest.mark.parametrize('distname,arg', distdiscrete) +def test_moments(distname, arg): + try: + distfn = getattr(stats, distname) + except TypeError: + distfn = distname + distname = 'sample distribution' + m, v, s, k = distfn.stats(*arg, moments='mvsk') + check_normalization(distfn, arg, distname) + + # compare `stats` and `moment` methods + check_moment(distfn, arg, m, v, distname) + check_mean_expect(distfn, arg, m, distname) + check_var_expect(distfn, arg, m, v, distname) + check_skew_expect(distfn, arg, m, v, s, distname) + with np.testing.suppress_warnings() as sup: + if distname in ['zipf', 'betanbinom']: + sup.filter(RuntimeWarning) + check_kurt_expect(distfn, arg, m, v, k, distname) + + # frozen distr moments + check_moment_frozen(distfn, arg, m, 1) + check_moment_frozen(distfn, arg, v+m*m, 2) + + +@pytest.mark.parametrize('dist,shape_args', distdiscrete) +def test_rvs_broadcast(dist, shape_args): + # If shape_only is True, it means the _rvs method of the + # distribution uses more than one random number to generate a random + # variate. That means the result of using rvs with broadcasting or + # with a nontrivial size will not necessarily be the same as using the + # numpy.vectorize'd version of rvs(), so we can only compare the shapes + # of the results, not the values. + # Whether or not a distribution is in the following list is an + # implementation detail of the distribution, not a requirement. If + # the implementation the rvs() method of a distribution changes, this + # test might also have to be changed. + shape_only = dist in ['betabinom', 'betanbinom', 'skellam', 'yulesimon', + 'dlaplace', 'nchypergeom_fisher', + 'nchypergeom_wallenius'] + + try: + distfunc = getattr(stats, dist) + except TypeError: + distfunc = dist + dist = f'rv_discrete(values=({dist.xk!r}, {dist.pk!r}))' + loc = np.zeros(2) + nargs = distfunc.numargs + allargs = [] + bshape = [] + # Generate shape parameter arguments... + for k in range(nargs): + shp = (k + 3,) + (1,)*(k + 1) + param_val = shape_args[k] + allargs.append(np.full(shp, param_val)) + bshape.insert(0, shp[0]) + allargs.append(loc) + bshape.append(loc.size) + # bshape holds the expected shape when loc, scale, and the shape + # parameters are all broadcast together. + check_rvs_broadcast( + distfunc, dist, allargs, bshape, shape_only, [np.dtype(int)] + ) + + +@pytest.mark.parametrize('dist,args', distdiscrete) +def test_ppf_with_loc(dist, args): + try: + distfn = getattr(stats, dist) + except TypeError: + distfn = dist + #check with a negative, no and positive relocation. + np.random.seed(1942349) + re_locs = [np.random.randint(-10, -1), 0, np.random.randint(1, 10)] + _a, _b = distfn.support(*args) + for loc in re_locs: + npt.assert_array_equal( + [_a-1+loc, _b+loc], + [distfn.ppf(0.0, *args, loc=loc), distfn.ppf(1.0, *args, loc=loc)] + ) + + +@pytest.mark.parametrize('dist, args', distdiscrete) +def test_isf_with_loc(dist, args): + try: + distfn = getattr(stats, dist) + except TypeError: + distfn = dist + # check with a negative, no and positive relocation. + np.random.seed(1942349) + re_locs = [np.random.randint(-10, -1), 0, np.random.randint(1, 10)] + _a, _b = distfn.support(*args) + for loc in re_locs: + expected = _b + loc, _a - 1 + loc + res = distfn.isf(0., *args, loc=loc), distfn.isf(1., *args, loc=loc) + npt.assert_array_equal(expected, res) + # test broadcasting behaviour + re_locs = [np.random.randint(-10, -1, size=(5, 3)), + np.zeros((5, 3)), + np.random.randint(1, 10, size=(5, 3))] + _a, _b = distfn.support(*args) + for loc in re_locs: + expected = _b + loc, _a - 1 + loc + res = distfn.isf(0., *args, loc=loc), distfn.isf(1., *args, loc=loc) + npt.assert_array_equal(expected, res) + + +def check_cdf_ppf(distfn, arg, supp, msg): + # supp is assumed to be an array of integers in the support of distfn + # (but not necessarily all the integers in the support). + # This test assumes that the PMF of any value in the support of the + # distribution is greater than 1e-8. + + # cdf is a step function, and ppf(q) = min{k : cdf(k) >= q, k integer} + cdf_supp = distfn.cdf(supp, *arg) + # In very rare cases, the finite precision calculation of ppf(cdf(supp)) + # can produce an array in which an element is off by one. We nudge the + # CDF values down by 15 ULPs help to avoid this. + cdf_supp0 = cdf_supp - 15*np.spacing(cdf_supp) + npt.assert_array_equal(distfn.ppf(cdf_supp0, *arg), + supp, msg + '-roundtrip') + # Repeat the same calculation, but with the CDF values decreased by 1e-8. + npt.assert_array_equal(distfn.ppf(distfn.cdf(supp, *arg) - 1e-8, *arg), + supp, msg + '-roundtrip') + + if not hasattr(distfn, 'xk'): + _a, _b = distfn.support(*arg) + supp1 = supp[supp < _b] + npt.assert_array_equal(distfn.ppf(distfn.cdf(supp1, *arg) + 1e-8, *arg), + supp1 + distfn.inc, msg + ' ppf-cdf-next') + + +def check_pmf_cdf(distfn, arg, distname): + if hasattr(distfn, 'xk'): + index = distfn.xk + else: + startind = int(distfn.ppf(0.01, *arg) - 1) + index = list(range(startind, startind + 10)) + cdfs = distfn.cdf(index, *arg) + pmfs_cum = distfn.pmf(index, *arg).cumsum() + + atol, rtol = 1e-10, 1e-10 + if distname == 'skellam': # ncx2 accuracy + atol, rtol = 1e-5, 1e-5 + npt.assert_allclose(cdfs - cdfs[0], pmfs_cum - pmfs_cum[0], + atol=atol, rtol=rtol) + + # also check that pmf at non-integral k is zero + k = np.asarray(index) + k_shifted = k[:-1] + np.diff(k)/2 + npt.assert_equal(distfn.pmf(k_shifted, *arg), 0) + + # better check frozen distributions, and also when loc != 0 + loc = 0.5 + dist = distfn(loc=loc, *arg) + npt.assert_allclose(dist.pmf(k[1:] + loc), np.diff(dist.cdf(k + loc))) + npt.assert_equal(dist.pmf(k_shifted + loc), 0) + + +def check_moment_frozen(distfn, arg, m, k): + npt.assert_allclose(distfn(*arg).moment(k), m, + atol=1e-10, rtol=1e-10) + + +def check_oth(distfn, arg, supp, msg): + # checking other methods of distfn + npt.assert_allclose(distfn.sf(supp, *arg), 1. - distfn.cdf(supp, *arg), + atol=1e-10, rtol=1e-10) + + q = np.linspace(0.01, 0.99, 20) + npt.assert_allclose(distfn.isf(q, *arg), distfn.ppf(1. - q, *arg), + atol=1e-10, rtol=1e-10) + + median_sf = distfn.isf(0.5, *arg) + npt.assert_(distfn.sf(median_sf - 1, *arg) > 0.5) + npt.assert_(distfn.cdf(median_sf + 1, *arg) > 0.5) + + +def check_discrete_chisquare(distfn, arg, rvs, alpha, msg): + """Perform chisquare test for random sample of a discrete distribution + + Parameters + ---------- + distname : string + name of distribution function + arg : sequence + parameters of distribution + alpha : float + significance level, threshold for p-value + + Returns + ------- + result : bool + 0 if test passes, 1 if test fails + + """ + wsupp = 0.05 + + # construct intervals with minimum mass `wsupp`. + # intervals are left-half-open as in a cdf difference + _a, _b = distfn.support(*arg) + lo = int(max(_a, -1000)) + high = int(min(_b, 1000)) + 1 + distsupport = range(lo, high) + last = 0 + distsupp = [lo] + distmass = [] + for ii in distsupport: + current = distfn.cdf(ii, *arg) + if current - last >= wsupp - 1e-14: + distsupp.append(ii) + distmass.append(current - last) + last = current + if current > (1 - wsupp): + break + if distsupp[-1] < _b: + distsupp.append(_b) + distmass.append(1 - last) + distsupp = np.array(distsupp) + distmass = np.array(distmass) + + # convert intervals to right-half-open as required by histogram + histsupp = distsupp + 1e-8 + histsupp[0] = _a + + # find sample frequencies and perform chisquare test + freq, hsupp = np.histogram(rvs, histsupp) + chis, pval = stats.chisquare(np.array(freq), len(rvs)*distmass) + + npt.assert_( + pval > alpha, + f'chisquare - test for {msg} at arg = {str(arg)} with pval = {str(pval)}' + ) + + +def check_scale_docstring(distfn): + if distfn.__doc__ is not None: + # Docstrings can be stripped if interpreter is run with -OO + npt.assert_('scale' not in distfn.__doc__) + + +@pytest.mark.parametrize('method', ['pmf', 'logpmf', 'cdf', 'logcdf', + 'sf', 'logsf', 'ppf', 'isf']) +@pytest.mark.parametrize('distname, args', distdiscrete) +def test_methods_with_lists(method, distname, args): + # Test that the discrete distributions can accept Python lists + # as arguments. + try: + dist = getattr(stats, distname) + except TypeError: + return + if method in ['ppf', 'isf']: + z = [0.1, 0.2] + else: + z = [0, 1] + p2 = [[p]*2 for p in args] + loc = [0, 1] + result = dist.pmf(z, *p2, loc=loc) + npt.assert_allclose(result, + [dist.pmf(*v) for v in zip(z, *p2, loc)], + rtol=1e-15, atol=1e-15) + + +@pytest.mark.parametrize('distname, args', invdistdiscrete) +def test_cdf_gh13280_regression(distname, args): + # Test for nan output when shape parameters are invalid + dist = getattr(stats, distname) + x = np.arange(-2, 15) + vals = dist.cdf(x, *args) + expected = np.nan + npt.assert_equal(vals, expected) + + +def cases_test_discrete_integer_shapes(): + # distributions parameters that are only allowed to be integral when + # fitting, but are allowed to be real as input to PDF, etc. + integrality_exceptions = {'nbinom': {'n'}, 'betanbinom': {'n'}} + + seen = set() + for distname, shapes in distdiscrete: + if distname in seen: + continue + seen.add(distname) + + try: + dist = getattr(stats, distname) + except TypeError: + continue + + shape_info = dist._shape_info() + + for i, shape in enumerate(shape_info): + if (shape.name in integrality_exceptions.get(distname, set()) or + not shape.integrality): + continue + + yield distname, shape.name, shapes + + +@pytest.mark.parametrize('distname, shapename, shapes', + cases_test_discrete_integer_shapes()) +def test_integer_shapes(distname, shapename, shapes): + dist = getattr(stats, distname) + shape_info = dist._shape_info() + shape_names = [shape.name for shape in shape_info] + i = shape_names.index(shapename) # this element of params must be integral + + shapes_copy = list(shapes) + + valid_shape = shapes[i] + invalid_shape = valid_shape - 0.5 # arbitrary non-integral value + new_valid_shape = valid_shape - 1 + shapes_copy[i] = [[valid_shape], [invalid_shape], [new_valid_shape]] + + a, b = dist.support(*shapes) + x = np.round(np.linspace(a, b, 5)) + + pmf = dist.pmf(x, *shapes_copy) + assert not np.any(np.isnan(pmf[0, :])) + assert np.all(np.isnan(pmf[1, :])) + assert not np.any(np.isnan(pmf[2, :])) + + +def test_frozen_attributes(): + # gh-14827 reported that all frozen distributions had both pmf and pdf + # attributes; continuous should have pdf and discrete should have pmf. + message = "'rv_discrete_frozen' object has no attribute" + with pytest.raises(AttributeError, match=message): + stats.binom(10, 0.5).pdf + with pytest.raises(AttributeError, match=message): + stats.binom(10, 0.5).logpdf + stats.binom.pdf = "herring" + frozen_binom = stats.binom(10, 0.5) + assert isinstance(frozen_binom, rv_discrete_frozen) + delattr(stats.binom, 'pdf') + + +@pytest.mark.parametrize('distname, shapes', distdiscrete) +def test_interval(distname, shapes): + # gh-11026 reported that `interval` returns incorrect values when + # `confidence=1`. The values were not incorrect, but it was not intuitive + # that the left end of the interval should extend beyond the support of the + # distribution. Confirm that this is the behavior for all distributions. + if isinstance(distname, str): + dist = getattr(stats, distname) + else: + dist = distname + a, b = dist.support(*shapes) + npt.assert_equal(dist.ppf([0, 1], *shapes), (a-1, b)) + npt.assert_equal(dist.isf([1, 0], *shapes), (a-1, b)) + npt.assert_equal(dist.interval(1, *shapes), (a-1, b)) + + +@pytest.mark.xfail_on_32bit("Sensible to machine precision") +def test_rv_sample(): + # Thoroughly test rv_sample and check that gh-3758 is resolved + + # Generate a random discrete distribution + rng = np.random.default_rng(98430143469) + xk = np.sort(rng.random(10) * 10) + pk = rng.random(10) + pk /= np.sum(pk) + dist = stats.rv_discrete(values=(xk, pk)) + + # Generate points to the left and right of xk + xk_left = (np.array([0] + xk[:-1].tolist()) + xk)/2 + xk_right = (np.array(xk[1:].tolist() + [xk[-1]+1]) + xk)/2 + + # Generate points to the left and right of cdf + cdf2 = np.cumsum(pk) + cdf2_left = (np.array([0] + cdf2[:-1].tolist()) + cdf2)/2 + cdf2_right = (np.array(cdf2[1:].tolist() + [1]) + cdf2)/2 + + # support - leftmost and rightmost xk + a, b = dist.support() + assert_allclose(a, xk[0]) + assert_allclose(b, xk[-1]) + + # pmf - supported only on the xk + assert_allclose(dist.pmf(xk), pk) + assert_allclose(dist.pmf(xk_right), 0) + assert_allclose(dist.pmf(xk_left), 0) + + # logpmf is log of the pmf; log(0) = -np.inf + with np.errstate(divide='ignore'): + assert_allclose(dist.logpmf(xk), np.log(pk)) + assert_allclose(dist.logpmf(xk_right), -np.inf) + assert_allclose(dist.logpmf(xk_left), -np.inf) + + # cdf - the cumulative sum of the pmf + assert_allclose(dist.cdf(xk), cdf2) + assert_allclose(dist.cdf(xk_right), cdf2) + assert_allclose(dist.cdf(xk_left), [0]+cdf2[:-1].tolist()) + + with np.errstate(divide='ignore'): + assert_allclose(dist.logcdf(xk), np.log(dist.cdf(xk)), + atol=1e-15) + assert_allclose(dist.logcdf(xk_right), np.log(dist.cdf(xk_right)), + atol=1e-15) + assert_allclose(dist.logcdf(xk_left), np.log(dist.cdf(xk_left)), + atol=1e-15) + + # sf is 1-cdf + assert_allclose(dist.sf(xk), 1-dist.cdf(xk)) + assert_allclose(dist.sf(xk_right), 1-dist.cdf(xk_right)) + assert_allclose(dist.sf(xk_left), 1-dist.cdf(xk_left)) + + with np.errstate(divide='ignore'): + assert_allclose(dist.logsf(xk), np.log(dist.sf(xk)), + atol=1e-15) + assert_allclose(dist.logsf(xk_right), np.log(dist.sf(xk_right)), + atol=1e-15) + assert_allclose(dist.logsf(xk_left), np.log(dist.sf(xk_left)), + atol=1e-15) + + # ppf + assert_allclose(dist.ppf(cdf2), xk) + assert_allclose(dist.ppf(cdf2_left), xk) + assert_allclose(dist.ppf(cdf2_right)[:-1], xk[1:]) + assert_allclose(dist.ppf(0), a - 1) + assert_allclose(dist.ppf(1), b) + + # isf + sf2 = dist.sf(xk) + assert_allclose(dist.isf(sf2), xk) + assert_allclose(dist.isf(1-cdf2_left), dist.ppf(cdf2_left)) + assert_allclose(dist.isf(1-cdf2_right), dist.ppf(cdf2_right)) + assert_allclose(dist.isf(0), b) + assert_allclose(dist.isf(1), a - 1) + + # interval is (ppf(alpha/2), isf(alpha/2)) + ps = np.linspace(0.01, 0.99, 10) + int2 = dist.ppf(ps/2), dist.isf(ps/2) + assert_allclose(dist.interval(1-ps), int2) + assert_allclose(dist.interval(0), dist.median()) + assert_allclose(dist.interval(1), (a-1, b)) + + # median is simply ppf(0.5) + med2 = dist.ppf(0.5) + assert_allclose(dist.median(), med2) + + # all four stats (mean, var, skew, and kurtosis) from the definitions + mean2 = np.sum(xk*pk) + var2 = np.sum((xk - mean2)**2 * pk) + skew2 = np.sum((xk - mean2)**3 * pk) / var2**(3/2) + kurt2 = np.sum((xk - mean2)**4 * pk) / var2**2 - 3 + assert_allclose(dist.mean(), mean2) + assert_allclose(dist.std(), np.sqrt(var2)) + assert_allclose(dist.var(), var2) + assert_allclose(dist.stats(moments='mvsk'), (mean2, var2, skew2, kurt2)) + + # noncentral moment against definition + mom3 = np.sum((xk**3) * pk) + assert_allclose(dist.moment(3), mom3) + + # expect - check against moments + assert_allclose(dist.expect(lambda x: 1), 1) + assert_allclose(dist.expect(), mean2) + assert_allclose(dist.expect(lambda x: x**3), mom3) + + # entropy is the negative of the expected value of log(p) + with np.errstate(divide='ignore'): + assert_allclose(-dist.expect(lambda x: dist.logpmf(x)), dist.entropy()) + + # RVS is just ppf of uniform random variates + rng = np.random.default_rng(98430143469) + rvs = dist.rvs(size=100, random_state=rng) + rng = np.random.default_rng(98430143469) + rvs0 = dist.ppf(rng.random(size=100)) + assert_allclose(rvs, rvs0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_distns.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_distns.py new file mode 100644 index 0000000000000000000000000000000000000000..3741cf79c96d623c32413356ca19248fc8f97ddc --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_discrete_distns.py @@ -0,0 +1,648 @@ +import pytest +import itertools + +from scipy.stats import (betabinom, betanbinom, hypergeom, nhypergeom, + bernoulli, boltzmann, skellam, zipf, zipfian, binom, + nbinom, nchypergeom_fisher, nchypergeom_wallenius, + randint) + +import numpy as np +from numpy.testing import ( + assert_almost_equal, assert_equal, assert_allclose, suppress_warnings +) +from scipy.special import binom as special_binom +from scipy.optimize import root_scalar +from scipy.integrate import quad + + +# The expected values were computed with Wolfram Alpha, using +# the expression CDF[HypergeometricDistribution[N, n, M], k]. +@pytest.mark.parametrize('k, M, n, N, expected, rtol', + [(3, 10, 4, 5, + 0.9761904761904762, 1e-15), + (107, 10000, 3000, 215, + 0.9999999997226765, 1e-15), + (10, 10000, 3000, 215, + 2.681682217692179e-21, 5e-11)]) +def test_hypergeom_cdf(k, M, n, N, expected, rtol): + p = hypergeom.cdf(k, M, n, N) + assert_allclose(p, expected, rtol=rtol) + + +# The expected values were computed with Wolfram Alpha, using +# the expression SurvivalFunction[HypergeometricDistribution[N, n, M], k]. +@pytest.mark.parametrize('k, M, n, N, expected, rtol', + [(25, 10000, 3000, 215, + 0.9999999999052958, 1e-15), + (125, 10000, 3000, 215, + 1.4416781705752128e-18, 5e-11)]) +def test_hypergeom_sf(k, M, n, N, expected, rtol): + p = hypergeom.sf(k, M, n, N) + assert_allclose(p, expected, rtol=rtol) + + +def test_hypergeom_logpmf(): + # symmetries test + # f(k,N,K,n) = f(n-k,N,N-K,n) = f(K-k,N,K,N-n) = f(k,N,n,K) + k = 5 + N = 50 + K = 10 + n = 5 + logpmf1 = hypergeom.logpmf(k, N, K, n) + logpmf2 = hypergeom.logpmf(n - k, N, N - K, n) + logpmf3 = hypergeom.logpmf(K - k, N, K, N - n) + logpmf4 = hypergeom.logpmf(k, N, n, K) + assert_almost_equal(logpmf1, logpmf2, decimal=12) + assert_almost_equal(logpmf1, logpmf3, decimal=12) + assert_almost_equal(logpmf1, logpmf4, decimal=12) + + # test related distribution + # Bernoulli distribution if n = 1 + k = 1 + N = 10 + K = 7 + n = 1 + hypergeom_logpmf = hypergeom.logpmf(k, N, K, n) + bernoulli_logpmf = bernoulli.logpmf(k, K/N) + assert_almost_equal(hypergeom_logpmf, bernoulli_logpmf, decimal=12) + + +def test_nhypergeom_pmf(): + # test with hypergeom + M, n, r = 45, 13, 8 + k = 6 + NHG = nhypergeom.pmf(k, M, n, r) + HG = hypergeom.pmf(k, M, n, k+r-1) * (M - n - (r-1)) / (M - (k+r-1)) + assert_allclose(HG, NHG, rtol=1e-10) + + +def test_nhypergeom_pmfcdf(): + # test pmf and cdf with arbitrary values. + M = 8 + n = 3 + r = 4 + support = np.arange(n+1) + pmf = nhypergeom.pmf(support, M, n, r) + cdf = nhypergeom.cdf(support, M, n, r) + assert_allclose(pmf, [1/14, 3/14, 5/14, 5/14], rtol=1e-13) + assert_allclose(cdf, [1/14, 4/14, 9/14, 1.0], rtol=1e-13) + + +def test_nhypergeom_r0(): + # test with `r = 0`. + M = 10 + n = 3 + r = 0 + pmf = nhypergeom.pmf([[0, 1, 2, 0], [1, 2, 0, 3]], M, n, r) + assert_allclose(pmf, [[1, 0, 0, 1], [0, 0, 1, 0]], rtol=1e-13) + + +def test_nhypergeom_rvs_shape(): + # Check that when given a size with more dimensions than the + # dimensions of the broadcast parameters, rvs returns an array + # with the correct shape. + x = nhypergeom.rvs(22, [7, 8, 9], [[12], [13]], size=(5, 1, 2, 3)) + assert x.shape == (5, 1, 2, 3) + + +def test_nhypergeom_accuracy(): + # Check that nhypergeom.rvs post-gh-13431 gives the same values as + # inverse transform sampling + np.random.seed(0) + x = nhypergeom.rvs(22, 7, 11, size=100) + np.random.seed(0) + p = np.random.uniform(size=100) + y = nhypergeom.ppf(p, 22, 7, 11) + assert_equal(x, y) + + +def test_boltzmann_upper_bound(): + k = np.arange(-3, 5) + + N = 1 + p = boltzmann.pmf(k, 0.123, N) + expected = k == 0 + assert_equal(p, expected) + + lam = np.log(2) + N = 3 + p = boltzmann.pmf(k, lam, N) + expected = [0, 0, 0, 4/7, 2/7, 1/7, 0, 0] + assert_allclose(p, expected, rtol=1e-13) + + c = boltzmann.cdf(k, lam, N) + expected = [0, 0, 0, 4/7, 6/7, 1, 1, 1] + assert_allclose(c, expected, rtol=1e-13) + + +def test_betabinom_a_and_b_unity(): + # test limiting case that betabinom(n, 1, 1) is a discrete uniform + # distribution from 0 to n + n = 20 + k = np.arange(n + 1) + p = betabinom(n, 1, 1).pmf(k) + expected = np.repeat(1 / (n + 1), n + 1) + assert_almost_equal(p, expected) + + +@pytest.mark.parametrize('dtypes', itertools.product(*[(int, float)]*3)) +def test_betabinom_stats_a_and_b_integers_gh18026(dtypes): + # gh-18026 reported that `betabinom` kurtosis calculation fails when some + # parameters are integers. Check that this is resolved. + n_type, a_type, b_type = dtypes + n, a, b = n_type(10), a_type(2), b_type(3) + assert_allclose(betabinom.stats(n, a, b, moments='k'), -0.6904761904761907) + + +def test_betabinom_bernoulli(): + # test limiting case that betabinom(1, a, b) = bernoulli(a / (a + b)) + a = 2.3 + b = 0.63 + k = np.arange(2) + p = betabinom(1, a, b).pmf(k) + expected = bernoulli(a / (a + b)).pmf(k) + assert_almost_equal(p, expected) + + +def test_issue_10317(): + alpha, n, p = 0.9, 10, 1 + assert_equal(nbinom.interval(confidence=alpha, n=n, p=p), (0, 0)) + + +def test_issue_11134(): + alpha, n, p = 0.95, 10, 0 + assert_equal(binom.interval(confidence=alpha, n=n, p=p), (0, 0)) + + +def test_issue_7406(): + np.random.seed(0) + assert_equal(binom.ppf(np.random.rand(10), 0, 0.5), 0) + + # Also check that endpoints (q=0, q=1) are correct + assert_equal(binom.ppf(0, 0, 0.5), -1) + assert_equal(binom.ppf(1, 0, 0.5), 0) + + +def test_issue_5122(): + p = 0 + n = np.random.randint(100, size=10) + + x = 0 + ppf = binom.ppf(x, n, p) + assert_equal(ppf, -1) + + x = np.linspace(0.01, 0.99, 10) + ppf = binom.ppf(x, n, p) + assert_equal(ppf, 0) + + x = 1 + ppf = binom.ppf(x, n, p) + assert_equal(ppf, n) + + +def test_issue_1603(): + assert_equal(binom(1000, np.logspace(-3, -100)).ppf(0.01), 0) + + +def test_issue_5503(): + p = 0.5 + x = np.logspace(3, 14, 12) + assert_allclose(binom.cdf(x, 2*x, p), 0.5, atol=1e-2) + + +@pytest.mark.parametrize('x, n, p, cdf_desired', [ + (300, 1000, 3/10, 0.51559351981411995636), + (3000, 10000, 3/10, 0.50493298381929698016), + (30000, 100000, 3/10, 0.50156000591726422864), + (300000, 1000000, 3/10, 0.50049331906666960038), + (3000000, 10000000, 3/10, 0.50015600124585261196), + (30000000, 100000000, 3/10, 0.50004933192735230102), + (30010000, 100000000, 3/10, 0.98545384016570790717), + (29990000, 100000000, 3/10, 0.01455017177985268670), + (29950000, 100000000, 3/10, 5.02250963487432024943e-28), +]) +def test_issue_5503pt2(x, n, p, cdf_desired): + assert_allclose(binom.cdf(x, n, p), cdf_desired) + + +def test_issue_5503pt3(): + # From Wolfram Alpha: CDF[BinomialDistribution[1e12, 1e-12], 2] + assert_allclose(binom.cdf(2, 10**12, 10**-12), 0.91969860292869777384) + + +def test_issue_6682(): + # Reference value from R: + # options(digits=16) + # print(pnbinom(250, 50, 32/63, lower.tail=FALSE)) + assert_allclose(nbinom.sf(250, 50, 32./63.), 1.460458510976452e-35) + + +def test_issue_19747(): + # test that negative k does not raise an error in nbinom.logcdf + result = nbinom.logcdf([5, -1, 1], 5, 0.5) + reference = [-0.47313352, -np.inf, -2.21297293] + assert_allclose(result, reference) + + +def test_boost_divide_by_zero_issue_15101(): + n = 1000 + p = 0.01 + k = 996 + assert_allclose(binom.pmf(k, n, p), 0.0) + + +def test_skellam_gh11474(): + # test issue reported in gh-11474 caused by `cdfchn` + mu = [1, 10, 100, 1000, 5000, 5050, 5100, 5250, 6000] + cdf = skellam.cdf(0, mu, mu) + # generated in R + # library(skellam) + # options(digits = 16) + # mu = c(1, 10, 100, 1000, 5000, 5050, 5100, 5250, 6000) + # pskellam(0, mu, mu, TRUE) + cdf_expected = [0.6542541612768356, 0.5448901559424127, 0.5141135799745580, + 0.5044605891382528, 0.5019947363350450, 0.5019848365953181, + 0.5019750827993392, 0.5019466621805060, 0.5018209330219539] + assert_allclose(cdf, cdf_expected) + + +class TestZipfian: + def test_zipfian_asymptotic(self): + # test limiting case that zipfian(a, n) -> zipf(a) as n-> oo + a = 6.5 + N = 10000000 + k = np.arange(1, 21) + assert_allclose(zipfian.pmf(k, a, N), zipf.pmf(k, a)) + assert_allclose(zipfian.cdf(k, a, N), zipf.cdf(k, a)) + assert_allclose(zipfian.sf(k, a, N), zipf.sf(k, a)) + assert_allclose(zipfian.stats(a, N, moments='msvk'), + zipf.stats(a, moments='msvk')) + + def test_zipfian_continuity(self): + # test that zipfian(0.999999, n) ~ zipfian(1.000001, n) + # (a = 1 switches between methods of calculating harmonic sum) + alt1, agt1 = 0.99999999, 1.00000001 + N = 30 + k = np.arange(1, N + 1) + assert_allclose(zipfian.pmf(k, alt1, N), zipfian.pmf(k, agt1, N), + rtol=5e-7) + assert_allclose(zipfian.cdf(k, alt1, N), zipfian.cdf(k, agt1, N), + rtol=5e-7) + assert_allclose(zipfian.sf(k, alt1, N), zipfian.sf(k, agt1, N), + rtol=5e-7) + assert_allclose(zipfian.stats(alt1, N, moments='msvk'), + zipfian.stats(agt1, N, moments='msvk'), rtol=5e-7) + + def test_zipfian_R(self): + # test against R VGAM package + # library(VGAM) + # k <- c(13, 16, 1, 4, 4, 8, 10, 19, 5, 7) + # a <- c(1.56712977, 3.72656295, 5.77665117, 9.12168729, 5.79977172, + # 4.92784796, 9.36078764, 4.3739616 , 7.48171872, 4.6824154) + # n <- c(70, 80, 48, 65, 83, 89, 50, 30, 20, 20) + # pmf <- dzipf(k, N = n, shape = a) + # cdf <- pzipf(k, N = n, shape = a) + # print(pmf) + # print(cdf) + np.random.seed(0) + k = np.random.randint(1, 20, size=10) + a = np.random.rand(10)*10 + 1 + n = np.random.randint(1, 100, size=10) + pmf = [8.076972e-03, 2.950214e-05, 9.799333e-01, 3.216601e-06, + 3.158895e-04, 3.412497e-05, 4.350472e-10, 2.405773e-06, + 5.860662e-06, 1.053948e-04] + cdf = [0.8964133, 0.9998666, 0.9799333, 0.9999995, 0.9998584, + 0.9999458, 1.0000000, 0.9999920, 0.9999977, 0.9998498] + # skip the first point; zipUC is not accurate for low a, n + assert_allclose(zipfian.pmf(k, a, n)[1:], pmf[1:], rtol=1e-6) + assert_allclose(zipfian.cdf(k, a, n)[1:], cdf[1:], rtol=5e-5) + + np.random.seed(0) + naive_tests = np.vstack((np.logspace(-2, 1, 10), + np.random.randint(2, 40, 10))).T + + @pytest.mark.parametrize("a, n", naive_tests) + def test_zipfian_naive(self, a, n): + # test against bare-bones implementation + + @np.vectorize + def Hns(n, s): + """Naive implementation of harmonic sum""" + return (1/np.arange(1, n+1)**s).sum() + + @np.vectorize + def pzip(k, a, n): + """Naive implementation of zipfian pmf""" + if k < 1 or k > n: + return 0. + else: + return 1 / k**a / Hns(n, a) + + k = np.arange(n+1) + pmf = pzip(k, a, n) + cdf = np.cumsum(pmf) + mean = np.average(k, weights=pmf) + var = np.average((k - mean)**2, weights=pmf) + std = var**0.5 + skew = np.average(((k-mean)/std)**3, weights=pmf) + kurtosis = np.average(((k-mean)/std)**4, weights=pmf) - 3 + assert_allclose(zipfian.pmf(k, a, n), pmf) + assert_allclose(zipfian.cdf(k, a, n), cdf) + assert_allclose(zipfian.stats(a, n, moments="mvsk"), + [mean, var, skew, kurtosis]) + + def test_pmf_integer_k(self): + k = np.arange(0, 1000) + k_int32 = k.astype(np.int32) + dist = zipfian(111, 22) + pmf = dist.pmf(k) + pmf_k_int32 = dist.pmf(k_int32) + assert_equal(pmf, pmf_k_int32) + + +class TestNCH: + np.random.seed(2) # seeds 0 and 1 had some xl = xu; randint failed + shape = (2, 4, 3) + max_m = 100 + m1 = np.random.randint(1, max_m, size=shape) # red balls + m2 = np.random.randint(1, max_m, size=shape) # white balls + N = m1 + m2 # total balls + n = randint.rvs(0, N, size=N.shape) # number of draws + xl = np.maximum(0, n-m2) # lower bound of support + xu = np.minimum(n, m1) # upper bound of support + x = randint.rvs(xl, xu, size=xl.shape) + odds = np.random.rand(*x.shape)*2 + + # test output is more readable when function names (strings) are passed + @pytest.mark.parametrize('dist_name', + ['nchypergeom_fisher', 'nchypergeom_wallenius']) + def test_nch_hypergeom(self, dist_name): + # Both noncentral hypergeometric distributions reduce to the + # hypergeometric distribution when odds = 1 + dists = {'nchypergeom_fisher': nchypergeom_fisher, + 'nchypergeom_wallenius': nchypergeom_wallenius} + dist = dists[dist_name] + x, N, m1, n = self.x, self.N, self.m1, self.n + assert_allclose(dist.pmf(x, N, m1, n, odds=1), + hypergeom.pmf(x, N, m1, n)) + + def test_nchypergeom_fisher_naive(self): + # test against a very simple implementation + x, N, m1, n, odds = self.x, self.N, self.m1, self.n, self.odds + + @np.vectorize + def pmf_mean_var(x, N, m1, n, w): + # simple implementation of nchypergeom_fisher pmf + m2 = N - m1 + xl = np.maximum(0, n-m2) + xu = np.minimum(n, m1) + + def f(x): + t1 = special_binom(m1, x) + t2 = special_binom(m2, n - x) + return t1 * t2 * w**x + + def P(k): + return sum(f(y)*y**k for y in range(xl, xu + 1)) + + P0 = P(0) + P1 = P(1) + P2 = P(2) + pmf = f(x) / P0 + mean = P1 / P0 + var = P2 / P0 - (P1 / P0)**2 + return pmf, mean, var + + pmf, mean, var = pmf_mean_var(x, N, m1, n, odds) + assert_allclose(nchypergeom_fisher.pmf(x, N, m1, n, odds), pmf) + assert_allclose(nchypergeom_fisher.stats(N, m1, n, odds, moments='m'), + mean) + assert_allclose(nchypergeom_fisher.stats(N, m1, n, odds, moments='v'), + var) + + def test_nchypergeom_wallenius_naive(self): + # test against a very simple implementation + + np.random.seed(2) + shape = (2, 4, 3) + max_m = 100 + m1 = np.random.randint(1, max_m, size=shape) + m2 = np.random.randint(1, max_m, size=shape) + N = m1 + m2 + n = randint.rvs(0, N, size=N.shape) + xl = np.maximum(0, n-m2) + xu = np.minimum(n, m1) + x = randint.rvs(xl, xu, size=xl.shape) + w = np.random.rand(*x.shape)*2 + + def support(N, m1, n, w): + m2 = N - m1 + xl = np.maximum(0, n-m2) + xu = np.minimum(n, m1) + return xl, xu + + @np.vectorize + def mean(N, m1, n, w): + m2 = N - m1 + xl, xu = support(N, m1, n, w) + + def fun(u): + return u/m1 + (1 - (n-u)/m2)**w - 1 + + return root_scalar(fun, bracket=(xl, xu)).root + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, + message="invalid value encountered in mean") + assert_allclose(nchypergeom_wallenius.mean(N, m1, n, w), + mean(N, m1, n, w), rtol=2e-2) + + @np.vectorize + def variance(N, m1, n, w): + m2 = N - m1 + u = mean(N, m1, n, w) + a = u * (m1 - u) + b = (n-u)*(u + m2 - n) + return N*a*b / ((N-1) * (m1*b + m2*a)) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, + message="invalid value encountered in mean") + assert_allclose( + nchypergeom_wallenius.stats(N, m1, n, w, moments='v'), + variance(N, m1, n, w), + rtol=5e-2 + ) + + @np.vectorize + def pmf(x, N, m1, n, w): + m2 = N - m1 + xl, xu = support(N, m1, n, w) + + def integrand(t): + D = w*(m1 - x) + (m2 - (n-x)) + res = (1-t**(w/D))**x * (1-t**(1/D))**(n-x) + return res + + def f(x): + t1 = special_binom(m1, x) + t2 = special_binom(m2, n - x) + the_integral = quad(integrand, 0, 1, + epsrel=1e-16, epsabs=1e-16) + return t1 * t2 * the_integral[0] + + return f(x) + + pmf0 = pmf(x, N, m1, n, w) + pmf1 = nchypergeom_wallenius.pmf(x, N, m1, n, w) + + atol, rtol = 1e-6, 1e-6 + i = np.abs(pmf1 - pmf0) < atol + rtol*np.abs(pmf0) + assert i.sum() > np.prod(shape) / 2 # works at least half the time + + # for those that fail, discredit the naive implementation + for N, m1, n, w in zip(N[~i], m1[~i], n[~i], w[~i]): + # get the support + m2 = N - m1 + xl, xu = support(N, m1, n, w) + x = np.arange(xl, xu + 1) + + # calculate sum of pmf over the support + # the naive implementation is very wrong in these cases + assert pmf(x, N, m1, n, w).sum() < .5 + assert_allclose(nchypergeom_wallenius.pmf(x, N, m1, n, w).sum(), 1) + + def test_wallenius_against_mpmath(self): + # precompute data with mpmath since naive implementation above + # is not reliable. See source code in gh-13330. + M = 50 + n = 30 + N = 20 + odds = 2.25 + # Expected results, computed with mpmath. + sup = np.arange(21) + pmf = np.array([3.699003068656875e-20, + 5.89398584245431e-17, + 2.1594437742911123e-14, + 3.221458044649955e-12, + 2.4658279241205077e-10, + 1.0965862603981212e-08, + 3.057890479665704e-07, + 5.622818831643761e-06, + 7.056482841531681e-05, + 0.000618899425358671, + 0.003854172932571669, + 0.01720592676256026, + 0.05528844897093792, + 0.12772363313574242, + 0.21065898367825722, + 0.24465958845359234, + 0.1955114898110033, + 0.10355390084949237, + 0.03414490375225675, + 0.006231989845775931, + 0.0004715577304677075]) + mean = 14.808018384813426 + var = 2.6085975877923717 + + # nchypergeom_wallenius.pmf returns 0 for pmf(0) and pmf(1), and pmf(2) + # has only three digits of accuracy (~ 2.1511e-14). + assert_allclose(nchypergeom_wallenius.pmf(sup, M, n, N, odds), pmf, + rtol=1e-13, atol=1e-13) + assert_allclose(nchypergeom_wallenius.mean(M, n, N, odds), + mean, rtol=1e-13) + assert_allclose(nchypergeom_wallenius.var(M, n, N, odds), + var, rtol=1e-11) + + @pytest.mark.parametrize('dist_name', + ['nchypergeom_fisher', 'nchypergeom_wallenius']) + def test_rvs_shape(self, dist_name): + # Check that when given a size with more dimensions than the + # dimensions of the broadcast parameters, rvs returns an array + # with the correct shape. + dists = {'nchypergeom_fisher': nchypergeom_fisher, + 'nchypergeom_wallenius': nchypergeom_wallenius} + dist = dists[dist_name] + x = dist.rvs(50, 30, [[10], [20]], [0.5, 1.0, 2.0], size=(5, 1, 2, 3)) + assert x.shape == (5, 1, 2, 3) + + +@pytest.mark.parametrize("mu, q, expected", + [[10, 120, -1.240089881791596e-38], + [1500, 0, -86.61466680572661]]) +def test_nbinom_11465(mu, q, expected): + # test nbinom.logcdf at extreme tails + size = 20 + n, p = size, size/(size+mu) + # In R: + # options(digits=16) + # pnbinom(mu=10, size=20, q=120, log.p=TRUE) + assert_allclose(nbinom.logcdf(q, n, p), expected) + + +def test_gh_17146(): + # Check that discrete distributions return PMF of zero at non-integral x. + # See gh-17146. + x = np.linspace(0, 1, 11) + p = 0.8 + pmf = bernoulli(p).pmf(x) + i = (x % 1 == 0) + assert_allclose(pmf[-1], p) + assert_allclose(pmf[0], 1-p) + assert_equal(pmf[~i], 0) + + +class TestBetaNBinom: + @pytest.mark.parametrize('x, n, a, b, ref', + [[5, 5e6, 5, 20, 1.1520944824139114e-107], + [100, 50, 5, 20, 0.002855762954310226], + [10000, 1000, 5, 20, 1.9648515726019154e-05]]) + def test_betanbinom_pmf(self, x, n, a, b, ref): + # test that PMF stays accurate in the distribution tails + # reference values computed with mpmath + # from mpmath import mp + # mp.dps = 500 + # def betanbinom_pmf(k, n, a, b): + # k = mp.mpf(k) + # a = mp.mpf(a) + # b = mp.mpf(b) + # n = mp.mpf(n) + # return float(mp.binomial(n + k - mp.one, k) + # * mp.beta(a + n, b + k) / mp.beta(a, b)) + assert_allclose(betanbinom.pmf(x, n, a, b), ref, rtol=1e-10) + + + @pytest.mark.parametrize('n, a, b, ref', + [[10000, 5000, 50, 0.12841520515722202], + [10, 9, 9, 7.9224400871459695], + [100, 1000, 10, 1.5849602176622748]]) + def test_betanbinom_kurtosis(self, n, a, b, ref): + # reference values were computed via mpmath + # from mpmath import mp + # def kurtosis_betanegbinom(n, a, b): + # n = mp.mpf(n) + # a = mp.mpf(a) + # b = mp.mpf(b) + # four = mp.mpf(4.) + # mean = n * b / (a - mp.one) + # var = (n * b * (n + a - 1.) * (a + b - 1.) + # / ((a - 2.) * (a - 1.)**2.)) + # def f(k): + # return (mp.binomial(n + k - mp.one, k) + # * mp.beta(a + n, b + k) / mp.beta(a, b) + # * (k - mean)**four) + # fourth_moment = mp.nsum(f, [0, mp.inf]) + # return float(fourth_moment/var**2 - 3.) + assert_allclose(betanbinom.stats(n, a, b, moments="k"), + ref, rtol=3e-15) + + +class TestZipf: + def test_gh20692(self): + # test that int32 data for k generates same output as double + k = np.arange(0, 1000) + k_int32 = k.astype(np.int32) + dist = zipf(9) + pmf = dist.pmf(k) + pmf_k_int32 = dist.pmf(k_int32) + assert_equal(pmf, pmf_k_int32) \ No newline at end of file diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_distributions.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_distributions.py new file mode 100644 index 0000000000000000000000000000000000000000..4623540b81a95d0eff35af8a228e90a43927d26e --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_distributions.py @@ -0,0 +1,9676 @@ +""" +Test functions for stats module +""" +import warnings +import re +import sys +import pickle +from pathlib import Path +import os +import json +import platform + +from numpy.testing import (assert_equal, assert_array_equal, + assert_almost_equal, assert_array_almost_equal, + assert_allclose, assert_, assert_warns, + assert_array_less, suppress_warnings) +import pytest +from pytest import raises as assert_raises + +import numpy +import numpy as np +from numpy import typecodes, array +from numpy.lib.recfunctions import rec_append_fields +from scipy import special +from scipy._lib._util import check_random_state +from scipy.integrate import (IntegrationWarning, quad, trapezoid, + cumulative_trapezoid) +import scipy.stats as stats +from scipy.stats._distn_infrastructure import argsreduce +import scipy.stats.distributions + +from scipy.special import xlogy, polygamma, entr +from scipy.stats._distr_params import distcont, invdistcont +from .test_discrete_basic import distdiscrete, invdistdiscrete +from scipy.stats._continuous_distns import FitDataError, _argus_phi +from scipy.optimize import root, fmin, differential_evolution +from itertools import product + +# python -OO strips docstrings +DOCSTRINGS_STRIPPED = sys.flags.optimize > 1 + +# Failing on macOS 11, Intel CPUs. See gh-14901 +MACOS_INTEL = (sys.platform == 'darwin') and (platform.machine() == 'x86_64') + + +# distributions to skip while testing the fix for the support method +# introduced in gh-13294. These distributions are skipped as they +# always return a non-nan support for every parametrization. +skip_test_support_gh13294_regression = ['tukeylambda', 'pearson3'] + + +def _assert_hasattr(a, b, msg=None): + if msg is None: + msg = f'{a} does not have attribute {b}' + assert_(hasattr(a, b), msg=msg) + + +def test_api_regression(): + # https://github.com/scipy/scipy/issues/3802 + _assert_hasattr(scipy.stats.distributions, 'f_gen') + + +def test_distributions_submodule(): + actual = set(scipy.stats.distributions.__all__) + continuous = [dist[0] for dist in distcont] # continuous dist names + discrete = [dist[0] for dist in distdiscrete] # discrete dist names + other = ['rv_discrete', 'rv_continuous', 'rv_histogram', + 'entropy', 'trapz'] + expected = continuous + discrete + other + + # need to remove, e.g., + # + expected = set(filter(lambda s: not str(s).startswith('<'), expected)) + + assert actual == expected + + +class TestVonMises: + @pytest.mark.parametrize('k', [0.1, 1, 101]) + @pytest.mark.parametrize('x', [0, 1, np.pi, 10, 100]) + def test_vonmises_periodic(self, k, x): + def check_vonmises_pdf_periodic(k, L, s, x): + vm = stats.vonmises(k, loc=L, scale=s) + assert_almost_equal(vm.pdf(x), vm.pdf(x % (2 * np.pi * s))) + + def check_vonmises_cdf_periodic(k, L, s, x): + vm = stats.vonmises(k, loc=L, scale=s) + assert_almost_equal(vm.cdf(x) % 1, + vm.cdf(x % (2 * np.pi * s)) % 1) + + check_vonmises_pdf_periodic(k, 0, 1, x) + check_vonmises_pdf_periodic(k, 1, 1, x) + check_vonmises_pdf_periodic(k, 0, 10, x) + + check_vonmises_cdf_periodic(k, 0, 1, x) + check_vonmises_cdf_periodic(k, 1, 1, x) + check_vonmises_cdf_periodic(k, 0, 10, x) + + def test_vonmises_line_support(self): + assert_equal(stats.vonmises_line.a, -np.pi) + assert_equal(stats.vonmises_line.b, np.pi) + + def test_vonmises_numerical(self): + vm = stats.vonmises(800) + assert_almost_equal(vm.cdf(0), 0.5) + + # Expected values of the vonmises PDF were computed using + # mpmath with 50 digits of precision: + # + # def vmpdf_mp(x, kappa): + # x = mpmath.mpf(x) + # kappa = mpmath.mpf(kappa) + # num = mpmath.exp(kappa*mpmath.cos(x)) + # den = 2 * mpmath.pi * mpmath.besseli(0, kappa) + # return num/den + + @pytest.mark.parametrize('x, kappa, expected_pdf', + [(0.1, 0.01, 0.16074242744907072), + (0.1, 25.0, 1.7515464099118245), + (0.1, 800, 0.2073272544458798), + (2.0, 0.01, 0.15849003875385817), + (2.0, 25.0, 8.356882934278192e-16), + (2.0, 800, 0.0)]) + def test_vonmises_pdf(self, x, kappa, expected_pdf): + pdf = stats.vonmises.pdf(x, kappa) + assert_allclose(pdf, expected_pdf, rtol=1e-15) + + # Expected values of the vonmises entropy were computed using + # mpmath with 50 digits of precision: + # + # def vonmises_entropy(kappa): + # kappa = mpmath.mpf(kappa) + # return (-kappa * mpmath.besseli(1, kappa) / + # mpmath.besseli(0, kappa) + mpmath.log(2 * mpmath.pi * + # mpmath.besseli(0, kappa))) + # >>> float(vonmises_entropy(kappa)) + + @pytest.mark.parametrize('kappa, expected_entropy', + [(1, 1.6274014590199897), + (5, 0.6756431570114528), + (100, -0.8811275441649473), + (1000, -2.03468891852547), + (2000, -2.3813876496587847)]) + def test_vonmises_entropy(self, kappa, expected_entropy): + entropy = stats.vonmises.entropy(kappa) + assert_allclose(entropy, expected_entropy, rtol=1e-13) + + def test_vonmises_rvs_gh4598(self): + # check that random variates wrap around as discussed in gh-4598 + seed = 30899520 + rng1 = np.random.default_rng(seed) + rng2 = np.random.default_rng(seed) + rng3 = np.random.default_rng(seed) + rvs1 = stats.vonmises(1, loc=0, scale=1).rvs(random_state=rng1) + rvs2 = stats.vonmises(1, loc=2*np.pi, scale=1).rvs(random_state=rng2) + rvs3 = stats.vonmises(1, loc=0, + scale=(2*np.pi/abs(rvs1)+1)).rvs(random_state=rng3) + assert_allclose(rvs1, rvs2, atol=1e-15) + assert_allclose(rvs1, rvs3, atol=1e-15) + + # Expected values of the vonmises LOGPDF were computed + # using wolfram alpha: + # kappa * cos(x) - log(2*pi*I0(kappa)) + @pytest.mark.parametrize('x, kappa, expected_logpdf', + [(0.1, 0.01, -1.8279520246003170), + (0.1, 25.0, 0.5604990605420549), + (0.1, 800, -1.5734567947337514), + (2.0, 0.01, -1.8420635346185686), + (2.0, 25.0, -34.7182759850871489), + (2.0, 800, -1130.4942582548682739)]) + def test_vonmises_logpdf(self, x, kappa, expected_logpdf): + logpdf = stats.vonmises.logpdf(x, kappa) + assert_allclose(logpdf, expected_logpdf, rtol=1e-15) + + def test_vonmises_expect(self): + """ + Test that the vonmises expectation values are + computed correctly. This test checks that the + numeric integration estimates the correct normalization + (1) and mean angle (loc). These expectations are + independent of the chosen 2pi interval. + """ + rng = np.random.default_rng(6762668991392531563) + + loc, kappa, lb = rng.random(3) * 10 + res = stats.vonmises(loc=loc, kappa=kappa).expect(lambda x: 1) + assert_allclose(res, 1) + assert np.issubdtype(res.dtype, np.floating) + + bounds = lb, lb + 2 * np.pi + res = stats.vonmises(loc=loc, kappa=kappa).expect(lambda x: 1, *bounds) + assert_allclose(res, 1) + assert np.issubdtype(res.dtype, np.floating) + + bounds = lb, lb + 2 * np.pi + res = stats.vonmises(loc=loc, kappa=kappa).expect(lambda x: np.exp(1j*x), + *bounds, complex_func=1) + assert_allclose(np.angle(res), loc % (2*np.pi)) + assert np.issubdtype(res.dtype, np.complexfloating) + + @pytest.mark.xslow + @pytest.mark.parametrize("rvs_loc", [0, 2]) + @pytest.mark.parametrize("rvs_shape", [1, 100, 1e8]) + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_shape', [True, False]) + def test_fit_MLE_comp_optimizer(self, rvs_loc, rvs_shape, + fix_loc, fix_shape): + if fix_shape and fix_loc: + pytest.skip("Nothing to fit.") + + rng = np.random.default_rng(6762668991392531563) + data = stats.vonmises.rvs(rvs_shape, size=1000, loc=rvs_loc, + random_state=rng) + + kwds = {'fscale': 1} + if fix_loc: + kwds['floc'] = rvs_loc + if fix_shape: + kwds['f0'] = rvs_shape + + _assert_less_or_close_loglike(stats.vonmises, data, + stats.vonmises.nnlf, **kwds) + + def test_vonmises_fit_bad_floc(self): + data = [-0.92923506, -0.32498224, 0.13054989, -0.97252014, 2.79658071, + -0.89110948, 1.22520295, 1.44398065, 2.49163859, 1.50315096, + 3.05437696, -2.73126329, -3.06272048, 1.64647173, 1.94509247, + -1.14328023, 0.8499056, 2.36714682, -1.6823179, -0.88359996] + data = np.asarray(data) + loc = -0.5 * np.pi + kappa_fit, loc_fit, scale_fit = stats.vonmises.fit(data, floc=loc) + assert kappa_fit == np.finfo(float).tiny + _assert_less_or_close_loglike(stats.vonmises, data, + stats.vonmises.nnlf, fscale=1, floc=loc) + + @pytest.mark.parametrize('sign', [-1, 1]) + def test_vonmises_fit_unwrapped_data(self, sign): + rng = np.random.default_rng(6762668991392531563) + data = stats.vonmises(loc=sign*0.5*np.pi, kappa=10).rvs(100000, + random_state=rng) + shifted_data = data + 4*np.pi + kappa_fit, loc_fit, scale_fit = stats.vonmises.fit(data) + kappa_fit_shifted, loc_fit_shifted, _ = stats.vonmises.fit(shifted_data) + assert_allclose(loc_fit, loc_fit_shifted) + assert_allclose(kappa_fit, kappa_fit_shifted) + assert scale_fit == 1 + assert -np.pi < loc_fit < np.pi + + def test_vonmises_kappa_0_gh18166(self): + # Check that kappa = 0 is supported. + dist = stats.vonmises(0) + assert_allclose(dist.pdf(0), 1 / (2 * np.pi), rtol=1e-15) + assert_allclose(dist.cdf(np.pi/2), 0.75, rtol=1e-15) + assert_allclose(dist.sf(-np.pi/2), 0.75, rtol=1e-15) + assert_allclose(dist.ppf(0.9), np.pi*0.8, rtol=1e-15) + assert_allclose(dist.mean(), 0, atol=1e-15) + assert_allclose(dist.expect(), 0, atol=1e-15) + assert np.all(np.abs(dist.rvs(size=10, random_state=1234)) <= np.pi) + + def test_vonmises_fit_equal_data(self): + # When all data are equal, expect kappa = 1e16. + kappa, loc, scale = stats.vonmises.fit([0]) + assert kappa == 1e16 and loc == 0 and scale == 1 + + def test_vonmises_fit_bounds(self): + # For certain input data, the root bracket is violated numerically. + # Test that this situation is handled. The input data below are + # crafted to trigger the bound violation for the current choice of + # bounds and the specific way the bounds and the objective function + # are computed. + + # Test that no exception is raised when the lower bound is violated. + scipy.stats.vonmises.fit([0, 3.7e-08], floc=0) + + # Test that no exception is raised when the upper bound is violated. + scipy.stats.vonmises.fit([np.pi/2*(1-4.86e-9)], floc=0) + + +def _assert_less_or_close_loglike(dist, data, func=None, maybe_identical=False, + **kwds): + """ + This utility function checks that the negative log-likelihood function + (or `func`) of the result computed using dist.fit() is less than or equal + to the result computed using the generic fit method. Because of + normal numerical imprecision, the "equality" check is made using + `np.allclose` with a relative tolerance of 1e-15. + """ + if func is None: + func = dist.nnlf + + mle_analytical = dist.fit(data, **kwds) + numerical_opt = super(type(dist), dist).fit(data, **kwds) + + # Sanity check that the analytical MLE is actually executed. + # Due to floating point arithmetic, the generic MLE is unlikely + # to produce the exact same result as the analytical MLE. + if not maybe_identical: + assert np.any(mle_analytical != numerical_opt) + + ll_mle_analytical = func(mle_analytical, data) + ll_numerical_opt = func(numerical_opt, data) + assert (ll_mle_analytical <= ll_numerical_opt or + np.allclose(ll_mle_analytical, ll_numerical_opt, rtol=1e-15)) + + # Ideally we'd check that shapes are correctly fixed, too, but that is + # complicated by the many ways of fixing them (e.g. f0, fix_a, fa). + if 'floc' in kwds: + assert mle_analytical[-2] == kwds['floc'] + if 'fscale' in kwds: + assert mle_analytical[-1] == kwds['fscale'] + + +def assert_fit_warnings(dist): + param = ['floc', 'fscale'] + if dist.shapes: + nshapes = len(dist.shapes.split(",")) + param += ['f0', 'f1', 'f2'][:nshapes] + all_fixed = dict(zip(param, np.arange(len(param)))) + data = [1, 2, 3] + with pytest.raises(RuntimeError, + match="All parameters fixed. There is nothing " + "to optimize."): + dist.fit(data, **all_fixed) + with pytest.raises(ValueError, + match="The data contains non-finite values"): + dist.fit([np.nan]) + with pytest.raises(ValueError, + match="The data contains non-finite values"): + dist.fit([np.inf]) + with pytest.raises(TypeError, match="Unknown keyword arguments:"): + dist.fit(data, extra_keyword=2) + with pytest.raises(TypeError, match="Too many positional arguments."): + dist.fit(data, *[1]*(len(param) - 1)) + + +@pytest.mark.parametrize('dist', + ['alpha', 'betaprime', + 'fatiguelife', 'invgamma', 'invgauss', 'invweibull', + 'johnsonsb', 'levy', 'levy_l', 'lognorm', 'gibrat', + 'powerlognorm', 'rayleigh', 'wald']) +def test_support(dist): + """gh-6235""" + dct = dict(distcont) + args = dct[dist] + + dist = getattr(stats, dist) + + assert_almost_equal(dist.pdf(dist.a, *args), 0) + assert_equal(dist.logpdf(dist.a, *args), -np.inf) + assert_almost_equal(dist.pdf(dist.b, *args), 0) + assert_equal(dist.logpdf(dist.b, *args), -np.inf) + + +class TestRandInt: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.randint.rvs(5, 30, size=100) + assert_(numpy.all(vals < 30) & numpy.all(vals >= 5)) + assert_(len(vals) == 100) + vals = stats.randint.rvs(5, 30, size=(2, 50)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.randint.rvs(15, 46) + assert_((val >= 15) & (val < 46)) + assert_(isinstance(val, numpy.ScalarType), msg=repr(type(val))) + val = stats.randint(15, 46).rvs(3) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_pdf(self): + k = numpy.r_[0:36] + out = numpy.where((k >= 5) & (k < 30), 1.0/(30-5), 0) + vals = stats.randint.pmf(k, 5, 30) + assert_array_almost_equal(vals, out) + + def test_cdf(self): + x = np.linspace(0, 36, 100) + k = numpy.floor(x) + out = numpy.select([k >= 30, k >= 5], [1.0, (k-5.0+1)/(30-5.0)], 0) + vals = stats.randint.cdf(x, 5, 30) + assert_array_almost_equal(vals, out, decimal=12) + + +class TestBinom: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.binom.rvs(10, 0.75, size=(2, 50)) + assert_(numpy.all(vals >= 0) & numpy.all(vals <= 10)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.binom.rvs(10, 0.75) + assert_(isinstance(val, int)) + val = stats.binom(10, 0.75).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_pmf(self): + # regression test for Ticket #1842 + vals1 = stats.binom.pmf(100, 100, 1) + vals2 = stats.binom.pmf(0, 100, 0) + assert_allclose(vals1, 1.0, rtol=1e-15, atol=0) + assert_allclose(vals2, 1.0, rtol=1e-15, atol=0) + + def test_entropy(self): + # Basic entropy tests. + b = stats.binom(2, 0.5) + expected_p = np.array([0.25, 0.5, 0.25]) + expected_h = -sum(xlogy(expected_p, expected_p)) + h = b.entropy() + assert_allclose(h, expected_h) + + b = stats.binom(2, 0.0) + h = b.entropy() + assert_equal(h, 0.0) + + b = stats.binom(2, 1.0) + h = b.entropy() + assert_equal(h, 0.0) + + def test_warns_p0(self): + # no spurious warnings are generated for p=0; gh-3817 + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + assert_equal(stats.binom(n=2, p=0).mean(), 0) + assert_equal(stats.binom(n=2, p=0).std(), 0) + + def test_ppf_p1(self): + # Check that gh-17388 is resolved: PPF == n when p = 1 + n = 4 + assert stats.binom.ppf(q=0.3, n=n, p=1.0) == n + + def test_pmf_poisson(self): + # Check that gh-17146 is resolved: binom -> poisson + n = 1541096362225563.0 + p = 1.0477878413173978e-18 + x = np.arange(3) + res = stats.binom.pmf(x, n=n, p=p) + ref = stats.poisson.pmf(x, n * p) + assert_allclose(res, ref, atol=1e-16) + + def test_pmf_cdf(self): + # Check that gh-17809 is resolved: binom.pmf(0) ~ binom.cdf(0) + n = 25.0 * 10 ** 21 + p = 1.0 * 10 ** -21 + r = 0 + res = stats.binom.pmf(r, n, p) + ref = stats.binom.cdf(r, n, p) + assert_allclose(res, ref, atol=1e-16) + + def test_pmf_gh15101(self): + # Check that gh-15101 is resolved (no divide warnings when p~1, n~oo) + res = stats.binom.pmf(3, 2000, 0.999) + assert_allclose(res, 0, atol=1e-16) + + +class TestArcsine: + + def test_endpoints(self): + # Regression test for gh-13697. The following calculation + # should not generate a warning. + p = stats.arcsine.pdf([0, 1]) + assert_equal(p, [np.inf, np.inf]) + + +class TestBernoulli: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.bernoulli.rvs(0.75, size=(2, 50)) + assert_(numpy.all(vals >= 0) & numpy.all(vals <= 1)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.bernoulli.rvs(0.75) + assert_(isinstance(val, int)) + val = stats.bernoulli(0.75).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_entropy(self): + # Simple tests of entropy. + b = stats.bernoulli(0.25) + expected_h = -0.25*np.log(0.25) - 0.75*np.log(0.75) + h = b.entropy() + assert_allclose(h, expected_h) + + b = stats.bernoulli(0.0) + h = b.entropy() + assert_equal(h, 0.0) + + b = stats.bernoulli(1.0) + h = b.entropy() + assert_equal(h, 0.0) + + +class TestBradford: + # gh-6216 + def test_cdf_ppf(self): + c = 0.1 + x = np.logspace(-20, -4) + q = stats.bradford.cdf(x, c) + xx = stats.bradford.ppf(q, c) + assert_allclose(x, xx) + + +class TestChi: + + # "Exact" value of chi.sf(10, 4), as computed by Wolfram Alpha with + # 1 - CDF[ChiDistribution[4], 10] + CHI_SF_10_4 = 9.83662422461598e-21 + # "Exact" value of chi.mean(df=1000) as computed by Wolfram Alpha with + # Mean[ChiDistribution[1000]] + CHI_MEAN_1000 = 31.614871896980 + + def test_sf(self): + s = stats.chi.sf(10, 4) + assert_allclose(s, self.CHI_SF_10_4, rtol=1e-15) + + def test_isf(self): + x = stats.chi.isf(self.CHI_SF_10_4, 4) + assert_allclose(x, 10, rtol=1e-15) + + # reference value for 1e14 was computed via mpmath + # from mpmath import mp + # mp.dps = 500 + # df = mp.mpf(1e14) + # float(mp.rf(mp.mpf(0.5) * df, mp.mpf(0.5)) * mp.sqrt(2.)) + + @pytest.mark.parametrize('df, ref', + [(1e3, CHI_MEAN_1000), + (1e14, 9999999.999999976)] + ) + def test_mean(self, df, ref): + assert_allclose(stats.chi.mean(df), ref, rtol=1e-12) + + # Entropy references values were computed with the following mpmath code + # from mpmath import mp + # mp.dps = 50 + # def chi_entropy_mpmath(df): + # df = mp.mpf(df) + # half_df = 0.5 * df + # entropy = mp.log(mp.gamma(half_df)) + 0.5 * \ + # (df - mp.log(2) - (df - mp.one) * mp.digamma(half_df)) + # return float(entropy) + + @pytest.mark.parametrize('df, ref', + [(1e-4, -9989.7316027504), + (1, 0.7257913526447274), + (1e3, 1.0721981095025448), + (1e10, 1.0723649429080335), + (1e100, 1.0723649429247002)]) + def test_entropy(self, df, ref): + assert_allclose(stats.chi(df).entropy(), ref, rtol=1e-15) + + +class TestNBinom: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.nbinom.rvs(10, 0.75, size=(2, 50)) + assert_(numpy.all(vals >= 0)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.nbinom.rvs(10, 0.75) + assert_(isinstance(val, int)) + val = stats.nbinom(10, 0.75).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_pmf(self): + # regression test for ticket 1779 + assert_allclose(np.exp(stats.nbinom.logpmf(700, 721, 0.52)), + stats.nbinom.pmf(700, 721, 0.52)) + # logpmf(0,1,1) shouldn't return nan (regression test for gh-4029) + val = scipy.stats.nbinom.logpmf(0, 1, 1) + assert_equal(val, 0) + + def test_logcdf_gh16159(self): + # check that gh16159 is resolved. + vals = stats.nbinom.logcdf([0, 5, 0, 5], n=4.8, p=0.45) + ref = np.log(stats.nbinom.cdf([0, 5, 0, 5], n=4.8, p=0.45)) + assert_allclose(vals, ref) + + +class TestGenInvGauss: + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.slow + def test_rvs_with_mode_shift(self): + # ratio_unif w/ mode shift + gig = stats.geninvgauss(2.3, 1.5) + _, p = stats.kstest(gig.rvs(size=1500, random_state=1234), gig.cdf) + assert_equal(p > 0.05, True) + + @pytest.mark.slow + def test_rvs_without_mode_shift(self): + # ratio_unif w/o mode shift + gig = stats.geninvgauss(0.9, 0.75) + _, p = stats.kstest(gig.rvs(size=1500, random_state=1234), gig.cdf) + assert_equal(p > 0.05, True) + + @pytest.mark.slow + def test_rvs_new_method(self): + # new algorithm of Hoermann / Leydold + gig = stats.geninvgauss(0.1, 0.2) + _, p = stats.kstest(gig.rvs(size=1500, random_state=1234), gig.cdf) + assert_equal(p > 0.05, True) + + @pytest.mark.slow + def test_rvs_p_zero(self): + def my_ks_check(p, b): + gig = stats.geninvgauss(p, b) + rvs = gig.rvs(size=1500, random_state=1234) + return stats.kstest(rvs, gig.cdf)[1] > 0.05 + # boundary cases when p = 0 + assert_equal(my_ks_check(0, 0.2), True) # new algo + assert_equal(my_ks_check(0, 0.9), True) # ratio_unif w/o shift + assert_equal(my_ks_check(0, 1.5), True) # ratio_unif with shift + + def test_rvs_negative_p(self): + # if p negative, return inverse + assert_equal( + stats.geninvgauss(-1.5, 2).rvs(size=10, random_state=1234), + 1 / stats.geninvgauss(1.5, 2).rvs(size=10, random_state=1234)) + + def test_invgauss(self): + # test that invgauss is special case + ig = stats.geninvgauss.rvs(size=1500, p=-0.5, b=1, random_state=1234) + assert_equal(stats.kstest(ig, 'invgauss', args=[1])[1] > 0.15, True) + # test pdf and cdf + mu, x = 100, np.linspace(0.01, 1, 10) + pdf_ig = stats.geninvgauss.pdf(x, p=-0.5, b=1 / mu, scale=mu) + assert_allclose(pdf_ig, stats.invgauss(mu).pdf(x)) + cdf_ig = stats.geninvgauss.cdf(x, p=-0.5, b=1 / mu, scale=mu) + assert_allclose(cdf_ig, stats.invgauss(mu).cdf(x)) + + def test_pdf_R(self): + # test against R package GIGrvg + # x <- seq(0.01, 5, length.out = 10) + # GIGrvg::dgig(x, 0.5, 1, 1) + vals_R = np.array([2.081176820e-21, 4.488660034e-01, 3.747774338e-01, + 2.693297528e-01, 1.905637275e-01, 1.351476913e-01, + 9.636538981e-02, 6.909040154e-02, 4.978006801e-02, + 3.602084467e-02]) + x = np.linspace(0.01, 5, 10) + assert_allclose(vals_R, stats.geninvgauss.pdf(x, 0.5, 1)) + + def test_pdf_zero(self): + # pdf at 0 is 0, needs special treatment to avoid 1/x in pdf + assert_equal(stats.geninvgauss.pdf(0, 0.5, 0.5), 0) + # if x is large and p is moderate, make sure that pdf does not + # overflow because of x**(p-1); exp(-b*x) forces pdf to zero + assert_equal(stats.geninvgauss.pdf(2e6, 50, 2), 0) + + +class TestGenHyperbolic: + def setup_method(self): + np.random.seed(1234) + + def test_pdf_r(self): + # test against R package GeneralizedHyperbolic + # x <- seq(-10, 10, length.out = 10) + # GeneralizedHyperbolic::dghyp( + # x = x, lambda = 2, alpha = 2, beta = 1, delta = 1.5, mu = 0.5 + # ) + vals_R = np.array([ + 2.94895678275316e-13, 1.75746848647696e-10, 9.48149804073045e-08, + 4.17862521692026e-05, 0.0103947630463822, 0.240864958986839, + 0.162833527161649, 0.0374609592899472, 0.00634894847327781, + 0.000941920705790324 + ]) + + lmbda, alpha, beta = 2, 2, 1 + mu, delta = 0.5, 1.5 + args = (lmbda, alpha*delta, beta*delta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + x = np.linspace(-10, 10, 10) + + assert_allclose(gh.pdf(x), vals_R, atol=0, rtol=1e-13) + + def test_cdf_r(self): + # test against R package GeneralizedHyperbolic + # q <- seq(-10, 10, length.out = 10) + # GeneralizedHyperbolic::pghyp( + # q = q, lambda = 2, alpha = 2, beta = 1, delta = 1.5, mu = 0.5 + # ) + vals_R = np.array([ + 1.01881590921421e-13, 6.13697274983578e-11, 3.37504977637992e-08, + 1.55258698166181e-05, 0.00447005453832497, 0.228935323956347, + 0.755759458895243, 0.953061062884484, 0.992598013917513, + 0.998942646586662 + ]) + + lmbda, alpha, beta = 2, 2, 1 + mu, delta = 0.5, 1.5 + args = (lmbda, alpha*delta, beta*delta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + x = np.linspace(-10, 10, 10) + + assert_allclose(gh.cdf(x), vals_R, atol=0, rtol=1e-6) + + # The reference values were computed by implementing the PDF with mpmath + # and integrating it with mp.quad. The values were computed with + # mp.dps=250, and then again with mp.dps=400 to ensure the full 64 bit + # precision was computed. + @pytest.mark.parametrize( + 'x, p, a, b, loc, scale, ref', + [(-15, 2, 3, 1.5, 0.5, 1.5, 4.770036428808252e-20), + (-15, 10, 1.5, 0.25, 1, 5, 0.03282964575089294), + (-15, 10, 1.5, 1.375, 0, 1, 3.3711159600215594e-23), + (-15, 0.125, 1.5, 1.49995, 0, 1, 4.729401428898605e-23), + (-1, 0.125, 1.5, 1.49995, 0, 1, 0.0003565725914786859), + (5, -0.125, 1.5, 1.49995, 0, 1, 0.2600651974023352), + (5, -0.125, 1000, 999, 0, 1, 5.923270556517253e-28), + (20, -0.125, 1000, 999, 0, 1, 0.23452293711665634), + (40, -0.125, 1000, 999, 0, 1, 0.9999648749561968), + (60, -0.125, 1000, 999, 0, 1, 0.9999999999975475)] + ) + def test_cdf_mpmath(self, x, p, a, b, loc, scale, ref): + cdf = stats.genhyperbolic.cdf(x, p, a, b, loc=loc, scale=scale) + assert_allclose(cdf, ref, rtol=5e-12) + + # The reference values were computed by implementing the PDF with mpmath + # and integrating it with mp.quad. The values were computed with + # mp.dps=250, and then again with mp.dps=400 to ensure the full 64 bit + # precision was computed. + @pytest.mark.parametrize( + 'x, p, a, b, loc, scale, ref', + [(0, 1e-6, 12, -1, 0, 1, 0.38520358671350524), + (-1, 3, 2.5, 2.375, 1, 3, 0.9999901774267577), + (-20, 3, 2.5, 2.375, 1, 3, 1.0), + (25, 2, 3, 1.5, 0.5, 1.5, 8.593419916523976e-10), + (300, 10, 1.5, 0.25, 1, 5, 6.137415609872158e-24), + (60, -0.125, 1000, 999, 0, 1, 2.4524915075944173e-12), + (75, -0.125, 1000, 999, 0, 1, 2.9435194886214633e-18)] + ) + def test_sf_mpmath(self, x, p, a, b, loc, scale, ref): + sf = stats.genhyperbolic.sf(x, p, a, b, loc=loc, scale=scale) + assert_allclose(sf, ref, rtol=5e-12) + + def test_moments_r(self): + # test against R package GeneralizedHyperbolic + # sapply(1:4, + # function(x) GeneralizedHyperbolic::ghypMom( + # order = x, lambda = 2, alpha = 2, + # beta = 1, delta = 1.5, mu = 0.5, + # momType = 'raw') + # ) + + vals_R = [2.36848366948115, 8.4739346779246, + 37.8870502710066, 205.76608511485] + + lmbda, alpha, beta = 2, 2, 1 + mu, delta = 0.5, 1.5 + args = (lmbda, alpha*delta, beta*delta) + + vals_us = [ + stats.genhyperbolic(*args, loc=mu, scale=delta).moment(i) + for i in range(1, 5) + ] + + assert_allclose(vals_us, vals_R, atol=0, rtol=1e-13) + + def test_rvs(self): + # Kolmogorov-Smirnov test to ensure alignment + # of analytical and empirical cdfs + + lmbda, alpha, beta = 2, 2, 1 + mu, delta = 0.5, 1.5 + args = (lmbda, alpha*delta, beta*delta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + _, p = stats.kstest(gh.rvs(size=1500, random_state=1234), gh.cdf) + + assert_equal(p > 0.05, True) + + def test_pdf_t(self): + # Test Against T-Student with 1 - 30 df + df = np.linspace(1, 30, 10) + + # in principle alpha should be zero in practice for big lmbdas + # alpha cannot be too small else pdf does not integrate + alpha, beta = np.float_power(df, 2)*np.finfo(np.float32).eps, 0 + mu, delta = 0, np.sqrt(df) + args = (-df/2, alpha, beta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + x = np.linspace(gh.ppf(0.01), gh.ppf(0.99), 50)[:, np.newaxis] + + assert_allclose( + gh.pdf(x), stats.t.pdf(x, df), + atol=0, rtol=1e-6 + ) + + def test_pdf_cauchy(self): + # Test Against Cauchy distribution + + # in principle alpha should be zero in practice for big lmbdas + # alpha cannot be too small else pdf does not integrate + lmbda, alpha, beta = -0.5, np.finfo(np.float32).eps, 0 + mu, delta = 0, 1 + args = (lmbda, alpha, beta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + x = np.linspace(gh.ppf(0.01), gh.ppf(0.99), 50)[:, np.newaxis] + + assert_allclose( + gh.pdf(x), stats.cauchy.pdf(x), + atol=0, rtol=1e-6 + ) + + def test_pdf_laplace(self): + # Test Against Laplace with location param [-10, 10] + loc = np.linspace(-10, 10, 10) + + # in principle delta should be zero in practice for big loc delta + # cannot be too small else pdf does not integrate + delta = np.finfo(np.float32).eps + + lmbda, alpha, beta = 1, 1, 0 + args = (lmbda, alpha*delta, beta*delta) + + # ppf does not integrate for scale < 5e-4 + # therefore using simple linspace to define the support + gh = stats.genhyperbolic(*args, loc=loc, scale=delta) + x = np.linspace(-20, 20, 50)[:, np.newaxis] + + assert_allclose( + gh.pdf(x), stats.laplace.pdf(x, loc=loc, scale=1), + atol=0, rtol=1e-11 + ) + + def test_pdf_norminvgauss(self): + # Test Against NIG with varying alpha/beta/delta/mu + + alpha, beta, delta, mu = ( + np.linspace(1, 20, 10), + np.linspace(0, 19, 10)*np.float_power(-1, range(10)), + np.linspace(1, 1, 10), + np.linspace(-100, 100, 10) + ) + + lmbda = - 0.5 + args = (lmbda, alpha * delta, beta * delta) + + gh = stats.genhyperbolic(*args, loc=mu, scale=delta) + x = np.linspace(gh.ppf(0.01), gh.ppf(0.99), 50)[:, np.newaxis] + + assert_allclose( + gh.pdf(x), stats.norminvgauss.pdf( + x, a=alpha, b=beta, loc=mu, scale=delta), + atol=0, rtol=1e-13 + ) + + +class TestHypSecant: + + # Reference values were computed with the mpmath expression + # float((2/mp.pi)*mp.atan(mp.exp(-x))) + # and mp.dps = 50. + @pytest.mark.parametrize('x, reference', + [(30, 5.957247804324683e-14), + (50, 1.2278802891647964e-22)]) + def test_sf(self, x, reference): + sf = stats.hypsecant.sf(x) + assert_allclose(sf, reference, rtol=5e-15) + + # Reference values were computed with the mpmath expression + # float(-mp.log(mp.tan((mp.pi/2)*p))) + # and mp.dps = 50. + @pytest.mark.parametrize('p, reference', + [(1e-6, 13.363927852673998), + (1e-12, 27.179438410639094)]) + def test_isf(self, p, reference): + x = stats.hypsecant.isf(p) + assert_allclose(x, reference, rtol=5e-15) + + +class TestNormInvGauss: + def setup_method(self): + np.random.seed(1234) + + def test_cdf_R(self): + # test pdf and cdf vals against R + # require("GeneralizedHyperbolic") + # x_test <- c(-7, -5, 0, 8, 15) + # r_cdf <- GeneralizedHyperbolic::pnig(x_test, mu = 0, a = 1, b = 0.5) + # r_pdf <- GeneralizedHyperbolic::dnig(x_test, mu = 0, a = 1, b = 0.5) + r_cdf = np.array([8.034920282e-07, 2.512671945e-05, 3.186661051e-01, + 9.988650664e-01, 9.999848769e-01]) + x_test = np.array([-7, -5, 0, 8, 15]) + vals_cdf = stats.norminvgauss.cdf(x_test, a=1, b=0.5) + assert_allclose(vals_cdf, r_cdf, atol=1e-9) + + def test_pdf_R(self): + # values from R as defined in test_cdf_R + r_pdf = np.array([1.359600783e-06, 4.413878805e-05, 4.555014266e-01, + 7.450485342e-04, 8.917889931e-06]) + x_test = np.array([-7, -5, 0, 8, 15]) + vals_pdf = stats.norminvgauss.pdf(x_test, a=1, b=0.5) + assert_allclose(vals_pdf, r_pdf, atol=1e-9) + + @pytest.mark.parametrize('x, a, b, sf, rtol', + [(-1, 1, 0, 0.8759652211005315, 1e-13), + (25, 1, 0, 1.1318690184042579e-13, 1e-4), + (1, 5, -1.5, 0.002066711134653577, 1e-12), + (10, 5, -1.5, 2.308435233930669e-29, 1e-9)]) + def test_sf_isf_mpmath(self, x, a, b, sf, rtol): + # Reference data generated with `reference_distributions.NormInvGauss`, + # e.g. `NormInvGauss(alpha=1, beta=0).sf(-1)` with mp.dps = 50 + s = stats.norminvgauss.sf(x, a, b) + assert_allclose(s, sf, rtol=rtol) + i = stats.norminvgauss.isf(sf, a, b) + assert_allclose(i, x, rtol=rtol) + + def test_sf_isf_mpmath_vectorized(self): + x = [-1, 25] + a = [1, 1] + b = 0 + sf = [0.8759652211005315, 1.1318690184042579e-13] # see previous test + s = stats.norminvgauss.sf(x, a, b) + assert_allclose(s, sf, rtol=1e-13, atol=1e-16) + i = stats.norminvgauss.isf(sf, a, b) + # Not perfect, but better than it was. See gh-13338. + assert_allclose(i, x, rtol=1e-6) + + def test_gh8718(self): + # Add test that gh-13338 resolved gh-8718 + dst = stats.norminvgauss(1, 0) + x = np.arange(0, 20, 2) + sf = dst.sf(x) + isf = dst.isf(sf) + assert_allclose(isf, x) + + def test_stats(self): + a, b = 1, 0.5 + gamma = np.sqrt(a**2 - b**2) + v_stats = (b / gamma, a**2 / gamma**3, 3.0 * b / (a * np.sqrt(gamma)), + 3.0 * (1 + 4 * b**2 / a**2) / gamma) + assert_equal(v_stats, stats.norminvgauss.stats(a, b, moments='mvsk')) + + def test_ppf(self): + a, b = 1, 0.5 + x_test = np.array([0.001, 0.5, 0.999]) + vals = stats.norminvgauss.ppf(x_test, a, b) + assert_allclose(x_test, stats.norminvgauss.cdf(vals, a, b)) + + +class TestGeom: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.geom.rvs(0.75, size=(2, 50)) + assert_(numpy.all(vals >= 0)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.geom.rvs(0.75) + assert_(isinstance(val, int)) + val = stats.geom(0.75).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_rvs_9313(self): + # previously, RVS were converted to `np.int32` on some platforms, + # causing overflow for moderately large integer output (gh-9313). + # Check that this is resolved to the extent possible w/ `np.int64`. + rng = np.random.default_rng(649496242618848) + rvs = stats.geom.rvs(np.exp(-35), size=5, random_state=rng) + assert rvs.dtype == np.int64 + assert np.all(rvs > np.iinfo(np.int32).max) + + def test_pmf(self): + vals = stats.geom.pmf([1, 2, 3], 0.5) + assert_array_almost_equal(vals, [0.5, 0.25, 0.125]) + + def test_logpmf(self): + # regression test for ticket 1793 + vals1 = np.log(stats.geom.pmf([1, 2, 3], 0.5)) + vals2 = stats.geom.logpmf([1, 2, 3], 0.5) + assert_allclose(vals1, vals2, rtol=1e-15, atol=0) + + # regression test for gh-4028 + val = stats.geom.logpmf(1, 1) + assert_equal(val, 0.0) + + def test_cdf_sf(self): + vals = stats.geom.cdf([1, 2, 3], 0.5) + vals_sf = stats.geom.sf([1, 2, 3], 0.5) + expected = array([0.5, 0.75, 0.875]) + assert_array_almost_equal(vals, expected) + assert_array_almost_equal(vals_sf, 1-expected) + + def test_logcdf_logsf(self): + vals = stats.geom.logcdf([1, 2, 3], 0.5) + vals_sf = stats.geom.logsf([1, 2, 3], 0.5) + expected = array([0.5, 0.75, 0.875]) + assert_array_almost_equal(vals, np.log(expected)) + assert_array_almost_equal(vals_sf, np.log1p(-expected)) + + def test_ppf(self): + vals = stats.geom.ppf([0.5, 0.75, 0.875], 0.5) + expected = array([1.0, 2.0, 3.0]) + assert_array_almost_equal(vals, expected) + + def test_ppf_underflow(self): + # this should not underflow + assert_allclose(stats.geom.ppf(1e-20, 1e-20), 1.0, atol=1e-14) + + def test_entropy_gh18226(self): + # gh-18226 reported that `geom.entropy` produced a warning and + # inaccurate output for small p. Check that this is resolved. + h = stats.geom(0.0146).entropy() + assert_allclose(h, 5.219397961962308, rtol=1e-15) + + +class TestPlanck: + def setup_method(self): + np.random.seed(1234) + + def test_sf(self): + vals = stats.planck.sf([1, 2, 3], 5.) + expected = array([4.5399929762484854e-05, + 3.0590232050182579e-07, + 2.0611536224385579e-09]) + assert_array_almost_equal(vals, expected) + + def test_logsf(self): + vals = stats.planck.logsf([1000., 2000., 3000.], 1000.) + expected = array([-1001000., -2001000., -3001000.]) + assert_array_almost_equal(vals, expected) + + +class TestGennorm: + def test_laplace(self): + # test against Laplace (special case for beta=1) + points = [1, 2, 3] + pdf1 = stats.gennorm.pdf(points, 1) + pdf2 = stats.laplace.pdf(points) + assert_almost_equal(pdf1, pdf2) + + def test_norm(self): + # test against normal (special case for beta=2) + points = [1, 2, 3] + pdf1 = stats.gennorm.pdf(points, 2) + pdf2 = stats.norm.pdf(points, scale=2**-.5) + assert_almost_equal(pdf1, pdf2) + + def test_rvs(self): + np.random.seed(0) + # 0 < beta < 1 + dist = stats.gennorm(0.5) + rvs = dist.rvs(size=1000) + assert stats.kstest(rvs, dist.cdf).pvalue > 0.1 + # beta = 1 + dist = stats.gennorm(1) + rvs = dist.rvs(size=1000) + rvs_laplace = stats.laplace.rvs(size=1000) + assert stats.ks_2samp(rvs, rvs_laplace).pvalue > 0.1 + # beta = 2 + dist = stats.gennorm(2) + rvs = dist.rvs(size=1000) + rvs_norm = stats.norm.rvs(scale=1/2**0.5, size=1000) + assert stats.ks_2samp(rvs, rvs_norm).pvalue > 0.1 + + def test_rvs_broadcasting(self): + np.random.seed(0) + dist = stats.gennorm([[0.5, 1.], [2., 5.]]) + rvs = dist.rvs(size=[1000, 2, 2]) + assert stats.kstest(rvs[:, 0, 0], stats.gennorm(0.5).cdf)[1] > 0.1 + assert stats.kstest(rvs[:, 0, 1], stats.gennorm(1.0).cdf)[1] > 0.1 + assert stats.kstest(rvs[:, 1, 0], stats.gennorm(2.0).cdf)[1] > 0.1 + assert stats.kstest(rvs[:, 1, 1], stats.gennorm(5.0).cdf)[1] > 0.1 + + +class TestGibrat: + + # sfx is sf(x). The values were computed with mpmath: + # + # from mpmath import mp + # mp.dps = 100 + # def gibrat_sf(x): + # return 1 - mp.ncdf(mp.log(x)) + # + # E.g. + # + # >>> float(gibrat_sf(1.5)) + # 0.3425678305148459 + # + @pytest.mark.parametrize('x, sfx', [(1.5, 0.3425678305148459), + (5000, 8.173334352522493e-18)]) + def test_sf_isf(self, x, sfx): + assert_allclose(stats.gibrat.sf(x), sfx, rtol=2e-14) + assert_allclose(stats.gibrat.isf(sfx), x, rtol=2e-14) + + +class TestGompertz: + + def test_gompertz_accuracy(self): + # Regression test for gh-4031 + p = stats.gompertz.ppf(stats.gompertz.cdf(1e-100, 1), 1) + assert_allclose(p, 1e-100) + + # sfx is sf(x). The values were computed with mpmath: + # + # from mpmath import mp + # mp.dps = 100 + # def gompertz_sf(x, c): + # return mp.exp(-c*mp.expm1(x)) + # + # E.g. + # + # >>> float(gompertz_sf(1, 2.5)) + # 0.013626967146253437 + # + @pytest.mark.parametrize('x, c, sfx', [(1, 2.5, 0.013626967146253437), + (3, 2.5, 1.8973243273704087e-21), + (0.05, 5, 0.7738668242570479), + (2.25, 5, 3.707795833465481e-19)]) + def test_sf_isf(self, x, c, sfx): + assert_allclose(stats.gompertz.sf(x, c), sfx, rtol=1e-14) + assert_allclose(stats.gompertz.isf(sfx, c), x, rtol=1e-14) + + # reference values were computed with mpmath + # from mpmath import mp + # mp.dps = 100 + # def gompertz_entropy(c): + # c = mp.mpf(c) + # return float(mp.one - mp.log(c) - mp.exp(c)*mp.e1(c)) + + @pytest.mark.parametrize('c, ref', [(1e-4, 1.5762523017634573), + (1, 0.4036526376768059), + (1000, -5.908754280976161), + (1e10, -22.025850930040455)]) + def test_entropy(self, c, ref): + assert_allclose(stats.gompertz.entropy(c), ref, rtol=1e-14) + + +class TestFoldNorm: + + # reference values were computed with mpmath with 50 digits of precision + # from mpmath import mp + # mp.dps = 50 + # mp.mpf(0.5) * (mp.erf((x - c)/mp.sqrt(2)) + mp.erf((x + c)/mp.sqrt(2))) + + @pytest.mark.parametrize('x, c, ref', [(1e-4, 1e-8, 7.978845594730578e-05), + (1e-4, 1e-4, 7.97884555483635e-05)]) + def test_cdf(self, x, c, ref): + assert_allclose(stats.foldnorm.cdf(x, c), ref, rtol=1e-15) + + +class TestHalfNorm: + + # sfx is sf(x). The values were computed with mpmath: + # + # from mpmath import mp + # mp.dps = 100 + # def halfnorm_sf(x): + # return 2*(1 - mp.ncdf(x)) + # + # E.g. + # + # >>> float(halfnorm_sf(1)) + # 0.3173105078629141 + # + @pytest.mark.parametrize('x, sfx', [(1, 0.3173105078629141), + (10, 1.523970604832105e-23)]) + def test_sf_isf(self, x, sfx): + assert_allclose(stats.halfnorm.sf(x), sfx, rtol=1e-14) + assert_allclose(stats.halfnorm.isf(sfx), x, rtol=1e-14) + + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 100 + # def halfnorm_cdf_mpmath(x): + # x = mp.mpf(x) + # return float(mp.erf(x/mp.sqrt(2.))) + + @pytest.mark.parametrize('x, ref', [(1e-40, 7.978845608028653e-41), + (1e-18, 7.978845608028654e-19), + (8, 0.9999999999999988)]) + def test_cdf(self, x, ref): + assert_allclose(stats.halfnorm.cdf(x), ref, rtol=1e-15) + + @pytest.mark.parametrize("rvs_loc", [1e-5, 1e10]) + @pytest.mark.parametrize("rvs_scale", [1e-2, 100, 1e8]) + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_scale', [True, False]) + def test_fit_MLE_comp_optimizer(self, rvs_loc, rvs_scale, + fix_loc, fix_scale): + + rng = np.random.default_rng(6762668991392531563) + data = stats.halfnorm.rvs(loc=rvs_loc, scale=rvs_scale, size=1000, + random_state=rng) + + if fix_loc and fix_scale: + error_msg = ("All parameters fixed. There is nothing to " + "optimize.") + with pytest.raises(RuntimeError, match=error_msg): + stats.halflogistic.fit(data, floc=rvs_loc, fscale=rvs_scale) + return + + kwds = {} + if fix_loc: + kwds['floc'] = rvs_loc + if fix_scale: + kwds['fscale'] = rvs_scale + + # Numerical result may equal analytical result if the initial guess + # computed from moment condition is already optimal. + _assert_less_or_close_loglike(stats.halfnorm, data, **kwds, + maybe_identical=True) + + def test_fit_error(self): + # `floc` bigger than the minimal data point + with pytest.raises(FitDataError): + stats.halfnorm.fit([1, 2, 3], floc=2) + + +class TestHalfCauchy: + + @pytest.mark.parametrize("rvs_loc", [1e-5, 1e10]) + @pytest.mark.parametrize("rvs_scale", [1e-2, 1e8]) + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_scale', [True, False]) + def test_fit_MLE_comp_optimizer(self, rvs_loc, rvs_scale, + fix_loc, fix_scale): + + rng = np.random.default_rng(6762668991392531563) + data = stats.halfnorm.rvs(loc=rvs_loc, scale=rvs_scale, size=1000, + random_state=rng) + + if fix_loc and fix_scale: + error_msg = ("All parameters fixed. There is nothing to " + "optimize.") + with pytest.raises(RuntimeError, match=error_msg): + stats.halfcauchy.fit(data, floc=rvs_loc, fscale=rvs_scale) + return + + kwds = {} + if fix_loc: + kwds['floc'] = rvs_loc + if fix_scale: + kwds['fscale'] = rvs_scale + + _assert_less_or_close_loglike(stats.halfcauchy, data, **kwds) + + def test_fit_error(self): + # `floc` bigger than the minimal data point + with pytest.raises(FitDataError): + stats.halfcauchy.fit([1, 2, 3], floc=2) + + +class TestHalfLogistic: + # survival function reference values were computed with mpmath + # from mpmath import mp + # mp.dps = 50 + # def sf_mpmath(x): + # x = mp.mpf(x) + # return float(mp.mpf(2.)/(mp.exp(x) + mp.one)) + + @pytest.mark.parametrize('x, ref', [(100, 7.440151952041672e-44), + (200, 2.767793053473475e-87)]) + def test_sf(self, x, ref): + assert_allclose(stats.halflogistic.sf(x), ref, rtol=1e-15) + + # inverse survival function reference values were computed with mpmath + # from mpmath import mp + # mp.dps = 200 + # def isf_mpmath(x): + # halfx = mp.mpf(x)/2 + # return float(-mp.log(halfx/(mp.one - halfx))) + + @pytest.mark.parametrize('q, ref', [(7.440151952041672e-44, 100), + (2.767793053473475e-87, 200), + (1-1e-9, 1.999999943436137e-09), + (1-1e-15, 1.9984014443252818e-15)]) + def test_isf(self, q, ref): + assert_allclose(stats.halflogistic.isf(q), ref, rtol=1e-15) + + @pytest.mark.parametrize("rvs_loc", [1e-5, 1e10]) + @pytest.mark.parametrize("rvs_scale", [1e-2, 100, 1e8]) + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_scale', [True, False]) + def test_fit_MLE_comp_optimizer(self, rvs_loc, rvs_scale, + fix_loc, fix_scale): + + rng = np.random.default_rng(6762668991392531563) + data = stats.halflogistic.rvs(loc=rvs_loc, scale=rvs_scale, size=1000, + random_state=rng) + + kwds = {} + if fix_loc and fix_scale: + error_msg = ("All parameters fixed. There is nothing to " + "optimize.") + with pytest.raises(RuntimeError, match=error_msg): + stats.halflogistic.fit(data, floc=rvs_loc, fscale=rvs_scale) + return + + if fix_loc: + kwds['floc'] = rvs_loc + if fix_scale: + kwds['fscale'] = rvs_scale + + # Numerical result may equal analytical result if the initial guess + # computed from moment condition is already optimal. + _assert_less_or_close_loglike(stats.halflogistic, data, **kwds, + maybe_identical=True) + + def test_fit_bad_floc(self): + msg = r" Maximum likelihood estimation with 'halflogistic' requires" + with assert_raises(FitDataError, match=msg): + stats.halflogistic.fit([0, 2, 4], floc=1) + + +class TestHalfgennorm: + def test_expon(self): + # test against exponential (special case for beta=1) + points = [1, 2, 3] + pdf1 = stats.halfgennorm.pdf(points, 1) + pdf2 = stats.expon.pdf(points) + assert_almost_equal(pdf1, pdf2) + + def test_halfnorm(self): + # test against half normal (special case for beta=2) + points = [1, 2, 3] + pdf1 = stats.halfgennorm.pdf(points, 2) + pdf2 = stats.halfnorm.pdf(points, scale=2**-.5) + assert_almost_equal(pdf1, pdf2) + + def test_gennorm(self): + # test against generalized normal + points = [1, 2, 3] + pdf1 = stats.halfgennorm.pdf(points, .497324) + pdf2 = stats.gennorm.pdf(points, .497324) + assert_almost_equal(pdf1, 2*pdf2) + + +class TestLaplaceasymmetric: + def test_laplace(self): + # test against Laplace (special case for kappa=1) + points = np.array([1, 2, 3]) + pdf1 = stats.laplace_asymmetric.pdf(points, 1) + pdf2 = stats.laplace.pdf(points) + assert_allclose(pdf1, pdf2) + + def test_asymmetric_laplace_pdf(self): + # test asymmetric Laplace + points = np.array([1, 2, 3]) + kappa = 2 + kapinv = 1/kappa + pdf1 = stats.laplace_asymmetric.pdf(points, kappa) + pdf2 = stats.laplace_asymmetric.pdf(points*(kappa**2), kapinv) + assert_allclose(pdf1, pdf2) + + def test_asymmetric_laplace_log_10_16(self): + # test asymmetric Laplace + points = np.array([-np.log(16), np.log(10)]) + kappa = 2 + pdf1 = stats.laplace_asymmetric.pdf(points, kappa) + cdf1 = stats.laplace_asymmetric.cdf(points, kappa) + sf1 = stats.laplace_asymmetric.sf(points, kappa) + pdf2 = np.array([1/10, 1/250]) + cdf2 = np.array([1/5, 1 - 1/500]) + sf2 = np.array([4/5, 1/500]) + ppf1 = stats.laplace_asymmetric.ppf(cdf2, kappa) + ppf2 = points + isf1 = stats.laplace_asymmetric.isf(sf2, kappa) + isf2 = points + assert_allclose(np.concatenate((pdf1, cdf1, sf1, ppf1, isf1)), + np.concatenate((pdf2, cdf2, sf2, ppf2, isf2))) + + +class TestTruncnorm: + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.parametrize("a, b, ref", + [(0, 100, 0.7257913526447274), + (0.6, 0.7, -2.3027610681852573), + (1e-06, 2e-06, -13.815510557964274)]) + def test_entropy(self, a, b, ref): + # All reference values were calculated with mpmath: + # import numpy as np + # from mpmath import mp + # mp.dps = 50 + # def entropy_trun(a, b): + # a, b = mp.mpf(a), mp.mpf(b) + # Z = mp.ncdf(b) - mp.ncdf(a) + # + # def pdf(x): + # return mp.npdf(x) / Z + # + # res = -mp.quad(lambda t: pdf(t) * mp.log(pdf(t)), [a, b]) + # return np.float64(res) + assert_allclose(stats.truncnorm.entropy(a, b), ref, rtol=1e-10) + + @pytest.mark.parametrize("a, b, ref", + [(1e-11, 10000000000.0, 0.725791352640738), + (1e-100, 1e+100, 0.7257913526447274), + (-1e-100, 1e+100, 0.7257913526447274), + (-1e+100, 1e+100, 1.4189385332046727)]) + def test_extreme_entropy(self, a, b, ref): + # The reference values were calculated with mpmath + # import numpy as np + # from mpmath import mp + # mp.dps = 50 + # def trunc_norm_entropy(a, b): + # a, b = mp.mpf(a), mp.mpf(b) + # Z = mp.ncdf(b) - mp.ncdf(a) + # A = mp.log(mp.sqrt(2 * mp.pi * mp.e) * Z) + # B = (a * mp.npdf(a) - b * mp.npdf(b)) / (2 * Z) + # return np.float64(A + B) + assert_allclose(stats.truncnorm.entropy(a, b), ref, rtol=1e-14) + + def test_ppf_ticket1131(self): + vals = stats.truncnorm.ppf([-0.5, 0, 1e-4, 0.5, 1-1e-4, 1, 2], -1., 1., + loc=[3]*7, scale=2) + expected = np.array([np.nan, 1, 1.00056419, 3, 4.99943581, 5, np.nan]) + assert_array_almost_equal(vals, expected) + + def test_isf_ticket1131(self): + vals = stats.truncnorm.isf([-0.5, 0, 1e-4, 0.5, 1-1e-4, 1, 2], -1., 1., + loc=[3]*7, scale=2) + expected = np.array([np.nan, 5, 4.99943581, 3, 1.00056419, 1, np.nan]) + assert_array_almost_equal(vals, expected) + + def test_gh_2477_small_values(self): + # Check a case that worked in the original issue. + low, high = -11, -10 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + # Check a case that failed in the original issue. + low, high = 10, 11 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + + def test_gh_2477_large_values(self): + # Check a case that used to fail because of extreme tailness. + low, high = 100, 101 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low <= x.min() <= x.max() <= high), str([low, high, x]) + + # Check some additional extreme tails + low, high = 1000, 1001 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + + low, high = 10000, 10001 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + + low, high = -10001, -10000 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + + def test_gh_9403_nontail_values(self): + for low, high in [[3, 4], [-4, -3]]: + xvals = np.array([-np.inf, low, high, np.inf]) + xmid = (high+low)/2.0 + cdfs = stats.truncnorm.cdf(xvals, low, high) + sfs = stats.truncnorm.sf(xvals, low, high) + pdfs = stats.truncnorm.pdf(xvals, low, high) + expected_cdfs = np.array([0, 0, 1, 1]) + expected_sfs = np.array([1.0, 1.0, 0.0, 0.0]) + expected_pdfs = np.array([0, 3.3619772, 0.1015229, 0]) + if low < 0: + expected_pdfs = np.array([0, 0.1015229, 3.3619772, 0]) + assert_almost_equal(cdfs, expected_cdfs) + assert_almost_equal(sfs, expected_sfs) + assert_almost_equal(pdfs, expected_pdfs) + assert_almost_equal(np.log(expected_pdfs[1]/expected_pdfs[2]), + low + 0.5) + pvals = np.array([0, 0.5, 1.0]) + ppfs = stats.truncnorm.ppf(pvals, low, high) + expected_ppfs = np.array([low, np.sign(low)*3.1984741, high]) + assert_almost_equal(ppfs, expected_ppfs) + + if low < 0: + assert_almost_equal(stats.truncnorm.sf(xmid, low, high), + 0.8475544278436675) + assert_almost_equal(stats.truncnorm.cdf(xmid, low, high), + 0.1524455721563326) + else: + assert_almost_equal(stats.truncnorm.cdf(xmid, low, high), + 0.8475544278436675) + assert_almost_equal(stats.truncnorm.sf(xmid, low, high), + 0.1524455721563326) + pdf = stats.truncnorm.pdf(xmid, low, high) + assert_almost_equal(np.log(pdf/expected_pdfs[2]), (xmid+0.25)/2) + + def test_gh_9403_medium_tail_values(self): + for low, high in [[39, 40], [-40, -39]]: + xvals = np.array([-np.inf, low, high, np.inf]) + xmid = (high+low)/2.0 + cdfs = stats.truncnorm.cdf(xvals, low, high) + sfs = stats.truncnorm.sf(xvals, low, high) + pdfs = stats.truncnorm.pdf(xvals, low, high) + expected_cdfs = np.array([0, 0, 1, 1]) + expected_sfs = np.array([1.0, 1.0, 0.0, 0.0]) + expected_pdfs = np.array([0, 3.90256074e+01, 2.73349092e-16, 0]) + if low < 0: + expected_pdfs = np.array([0, 2.73349092e-16, + 3.90256074e+01, 0]) + assert_almost_equal(cdfs, expected_cdfs) + assert_almost_equal(sfs, expected_sfs) + assert_almost_equal(pdfs, expected_pdfs) + assert_almost_equal(np.log(expected_pdfs[1]/expected_pdfs[2]), + low + 0.5) + pvals = np.array([0, 0.5, 1.0]) + ppfs = stats.truncnorm.ppf(pvals, low, high) + expected_ppfs = np.array([low, np.sign(low)*39.01775731, high]) + assert_almost_equal(ppfs, expected_ppfs) + cdfs = stats.truncnorm.cdf(ppfs, low, high) + assert_almost_equal(cdfs, pvals) + + if low < 0: + assert_almost_equal(stats.truncnorm.sf(xmid, low, high), + 0.9999999970389126) + assert_almost_equal(stats.truncnorm.cdf(xmid, low, high), + 2.961048103554866e-09) + else: + assert_almost_equal(stats.truncnorm.cdf(xmid, low, high), + 0.9999999970389126) + assert_almost_equal(stats.truncnorm.sf(xmid, low, high), + 2.961048103554866e-09) + pdf = stats.truncnorm.pdf(xmid, low, high) + assert_almost_equal(np.log(pdf/expected_pdfs[2]), (xmid+0.25)/2) + + xvals = np.linspace(low, high, 11) + xvals2 = -xvals[::-1] + assert_almost_equal(stats.truncnorm.cdf(xvals, low, high), + stats.truncnorm.sf(xvals2, -high, -low)[::-1]) + assert_almost_equal(stats.truncnorm.sf(xvals, low, high), + stats.truncnorm.cdf(xvals2, -high, -low)[::-1]) + assert_almost_equal(stats.truncnorm.pdf(xvals, low, high), + stats.truncnorm.pdf(xvals2, -high, -low)[::-1]) + + def test_cdf_tail_15110_14753(self): + # Check accuracy issues reported in gh-14753 and gh-155110 + # Ground truth values calculated using Wolfram Alpha, e.g. + # (CDF[NormalDistribution[0,1],83/10]-CDF[NormalDistribution[0,1],8])/ + # (1 - CDF[NormalDistribution[0,1],8]) + assert_allclose(stats.truncnorm(13., 15.).cdf(14.), + 0.9999987259565643) + assert_allclose(stats.truncnorm(8, np.inf).cdf(8.3), + 0.9163220907327540) + + # Test data for the truncnorm stats() method. + # The data in each row is: + # a, b, mean, variance, skewness, excess kurtosis. Generated using + # https://gist.github.com/WarrenWeckesser/636b537ee889679227d53543d333a720 + _truncnorm_stats_data = [ + [-30, 30, + 0.0, 1.0, 0.0, 0.0], + [-10, 10, + 0.0, 1.0, 0.0, -1.4927521335810455e-19], + [-3, 3, + 0.0, 0.9733369246625415, 0.0, -0.17111443639774404], + [-2, 2, + 0.0, 0.7737413035499232, 0.0, -0.6344632828703505], + [0, np.inf, + 0.7978845608028654, + 0.3633802276324187, + 0.995271746431156, + 0.8691773036059741], + [-np.inf, 0, + -0.7978845608028654, + 0.3633802276324187, + -0.995271746431156, + 0.8691773036059741], + [-1, 3, + 0.282786110727154, + 0.6161417353578293, + 0.5393018494027877, + -0.20582065135274694], + [-3, 1, + -0.282786110727154, + 0.6161417353578293, + -0.5393018494027877, + -0.20582065135274694], + [-10, -9, + -9.108456288012409, + 0.011448805821636248, + -1.8985607290949496, + 5.0733461105025075], + ] + _truncnorm_stats_data = np.array(_truncnorm_stats_data) + + @pytest.mark.parametrize("case", _truncnorm_stats_data) + def test_moments(self, case): + a, b, m0, v0, s0, k0 = case + m, v, s, k = stats.truncnorm.stats(a, b, moments='mvsk') + assert_allclose([m, v, s, k], [m0, v0, s0, k0], atol=1e-17) + + def test_9902_moments(self): + m, v = stats.truncnorm.stats(0, np.inf, moments='mv') + assert_almost_equal(m, 0.79788456) + assert_almost_equal(v, 0.36338023) + + def test_gh_1489_trac_962_rvs(self): + # Check the original example. + low, high = 10, 15 + x = stats.truncnorm.rvs(low, high, 0, 1, size=10) + assert_(low < x.min() < x.max() < high) + + def test_gh_11299_rvs(self): + # Arose from investigating gh-11299 + # Test multiple shape parameters simultaneously. + low = [-10, 10, -np.inf, -5, -np.inf, -np.inf, -45, -45, 40, -10, 40] + high = [-5, 11, 5, np.inf, 40, -40, 40, -40, 45, np.inf, np.inf] + x = stats.truncnorm.rvs(low, high, size=(5, len(low))) + assert np.shape(x) == (5, len(low)) + assert_(np.all(low <= x.min(axis=0))) + assert_(np.all(x.max(axis=0) <= high)) + + def test_rvs_Generator(self): + # check that rvs can use a Generator + if hasattr(np.random, "default_rng"): + stats.truncnorm.rvs(-10, -5, size=5, + random_state=np.random.default_rng()) + + def test_logcdf_gh17064(self): + # regression test for gh-17064 - avoid roundoff error for logcdfs ~0 + a = np.array([-np.inf, -np.inf, -8, -np.inf, 10]) + b = np.array([np.inf, np.inf, 8, 10, np.inf]) + x = np.array([10, 7.5, 7.5, 9, 20]) + expected = [-7.619853024160525e-24, -3.190891672910947e-14, + -3.128682067168231e-14, -1.1285122074235991e-19, + -3.61374964828753e-66] + assert_allclose(stats.truncnorm(a, b).logcdf(x), expected) + assert_allclose(stats.truncnorm(-b, -a).logsf(-x), expected) + + def test_moments_gh18634(self): + # gh-18634 reported that moments 5 and higher didn't work; check that + # this is resolved + res = stats.truncnorm(-2, 3).moment(5) + # From Mathematica: + # Moment[TruncatedDistribution[{-2, 3}, NormalDistribution[]], 5] + ref = 1.645309620208361 + assert_allclose(res, ref) + + +class TestGenLogistic: + + # Expected values computed with mpmath with 50 digits of precision. + @pytest.mark.parametrize('x, expected', [(-1000, -1499.5945348918917), + (-125, -187.09453489189184), + (0, -1.3274028432916989), + (100, -99.59453489189184), + (1000, -999.5945348918918)]) + def test_logpdf(self, x, expected): + c = 1.5 + logp = stats.genlogistic.logpdf(x, c) + assert_allclose(logp, expected, rtol=1e-13) + + # Expected values computed with mpmath with 50 digits of precision + # from mpmath import mp + # mp.dps = 50 + # def entropy_mp(c): + # c = mp.mpf(c) + # return float(-mp.log(c)+mp.one+mp.digamma(c + mp.one) + mp.euler) + + @pytest.mark.parametrize('c, ref', [(1e-100, 231.25850929940458), + (1e-4, 10.21050485336338), + (1e8, 1.577215669901533), + (1e100, 1.5772156649015328)]) + def test_entropy(self, c, ref): + assert_allclose(stats.genlogistic.entropy(c), ref, rtol=5e-15) + + # Expected values computed with mpmath with 50 digits of precision + # from mpmath import mp + # mp.dps = 1000 + # + # def genlogistic_cdf_mp(x, c): + # x = mp.mpf(x) + # c = mp.mpf(c) + # return (mp.one + mp.exp(-x)) ** (-c) + # + # def genlogistic_sf_mp(x, c): + # return mp.one - genlogistic_cdf_mp(x, c) + # + # x, c, ref = 100, 0.02, -7.440151952041672e-466 + # print(float(mp.log(genlogistic_cdf_mp(x, c)))) + # ppf/isf reference values generated by passing in `ref` (`q` is produced) + + @pytest.mark.parametrize('x, c, ref', [(200, 10, 1.3838965267367375e-86), + (500, 20, 1.424915281348257e-216)]) + def test_sf(self, x, c, ref): + assert_allclose(stats.genlogistic.sf(x, c), ref, rtol=1e-14) + + @pytest.mark.parametrize('q, c, ref', [(0.01, 200, 9.898441467379765), + (0.001, 2, 7.600152115573173)]) + def test_isf(self, q, c, ref): + assert_allclose(stats.genlogistic.isf(q, c), ref, rtol=5e-16) + + @pytest.mark.parametrize('q, c, ref', [(0.5, 200, 5.6630969187064615), + (0.99, 20, 7.595630231412436)]) + def test_ppf(self, q, c, ref): + assert_allclose(stats.genlogistic.ppf(q, c), ref, rtol=5e-16) + + @pytest.mark.parametrize('x, c, ref', [(100, 0.02, -7.440151952041672e-46), + (50, 20, -3.857499695927835e-21)]) + def test_logcdf(self, x, c, ref): + assert_allclose(stats.genlogistic.logcdf(x, c), ref, rtol=1e-15) + + +class TestHypergeom: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.hypergeom.rvs(20, 10, 3, size=(2, 50)) + assert_(numpy.all(vals >= 0) & + numpy.all(vals <= 3)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.hypergeom.rvs(20, 3, 10) + assert_(isinstance(val, int)) + val = stats.hypergeom(20, 3, 10).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_precision(self): + # comparison number from mpmath + M = 2500 + n = 50 + N = 500 + tot = M + good = n + hgpmf = stats.hypergeom.pmf(2, tot, good, N) + assert_almost_equal(hgpmf, 0.0010114963068932233, 11) + + def test_args(self): + # test correct output for corner cases of arguments + # see gh-2325 + assert_almost_equal(stats.hypergeom.pmf(0, 2, 1, 0), 1.0, 11) + assert_almost_equal(stats.hypergeom.pmf(1, 2, 1, 0), 0.0, 11) + + assert_almost_equal(stats.hypergeom.pmf(0, 2, 0, 2), 1.0, 11) + assert_almost_equal(stats.hypergeom.pmf(1, 2, 1, 0), 0.0, 11) + + def test_cdf_above_one(self): + # for some values of parameters, hypergeom cdf was >1, see gh-2238 + assert_(0 <= stats.hypergeom.cdf(30, 13397950, 4363, 12390) <= 1.0) + + def test_precision2(self): + # Test hypergeom precision for large numbers. See #1218. + # Results compared with those from R. + oranges = 9.9e4 + pears = 1.1e5 + fruits_eaten = np.array([3, 3.8, 3.9, 4, 4.1, 4.2, 5]) * 1e4 + quantile = 2e4 + res = [stats.hypergeom.sf(quantile, oranges + pears, oranges, eaten) + for eaten in fruits_eaten] + expected = np.array([0, 1.904153e-114, 2.752693e-66, 4.931217e-32, + 8.265601e-11, 0.1237904, 1]) + assert_allclose(res, expected, atol=0, rtol=5e-7) + + # Test with array_like first argument + quantiles = [1.9e4, 2e4, 2.1e4, 2.15e4] + res2 = stats.hypergeom.sf(quantiles, oranges + pears, oranges, 4.2e4) + expected2 = [1, 0.1237904, 6.511452e-34, 3.277667e-69] + assert_allclose(res2, expected2, atol=0, rtol=5e-7) + + def test_entropy(self): + # Simple tests of entropy. + hg = stats.hypergeom(4, 1, 1) + h = hg.entropy() + expected_p = np.array([0.75, 0.25]) + expected_h = -np.sum(xlogy(expected_p, expected_p)) + assert_allclose(h, expected_h) + + hg = stats.hypergeom(1, 1, 1) + h = hg.entropy() + assert_equal(h, 0.0) + + def test_logsf(self): + # Test logsf for very large numbers. See issue #4982 + # Results compare with those from R (v3.2.0): + # phyper(k, n, M-n, N, lower.tail=FALSE, log.p=TRUE) + # -2239.771 + + k = 1e4 + M = 1e7 + n = 1e6 + N = 5e4 + + result = stats.hypergeom.logsf(k, M, n, N) + expected = -2239.771 # From R + assert_almost_equal(result, expected, decimal=3) + + k = 1 + M = 1600 + n = 600 + N = 300 + + result = stats.hypergeom.logsf(k, M, n, N) + expected = -2.566567e-68 # From R + assert_almost_equal(result, expected, decimal=15) + + def test_logcdf(self): + # Test logcdf for very large numbers. See issue #8692 + # Results compare with those from R (v3.3.2): + # phyper(k, n, M-n, N, lower.tail=TRUE, log.p=TRUE) + # -5273.335 + + k = 1 + M = 1e7 + n = 1e6 + N = 5e4 + + result = stats.hypergeom.logcdf(k, M, n, N) + expected = -5273.335 # From R + assert_almost_equal(result, expected, decimal=3) + + # Same example as in issue #8692 + k = 40 + M = 1600 + n = 50 + N = 300 + + result = stats.hypergeom.logcdf(k, M, n, N) + expected = -7.565148879229e-23 # From R + assert_almost_equal(result, expected, decimal=15) + + k = 125 + M = 1600 + n = 250 + N = 500 + + result = stats.hypergeom.logcdf(k, M, n, N) + expected = -4.242688e-12 # From R + assert_almost_equal(result, expected, decimal=15) + + # test broadcasting robustness based on reviewer + # concerns in PR 9603; using an array version of + # the example from issue #8692 + k = np.array([40, 40, 40]) + M = 1600 + n = 50 + N = 300 + + result = stats.hypergeom.logcdf(k, M, n, N) + expected = np.full(3, -7.565148879229e-23) # filled from R result + assert_almost_equal(result, expected, decimal=15) + + def test_mean_gh18511(self): + # gh-18511 reported that the `mean` was incorrect for large arguments; + # check that this is resolved + M = 390_000 + n = 370_000 + N = 12_000 + + hm = stats.hypergeom.mean(M, n, N) + rm = n / M * N + assert_allclose(hm, rm) + + def test_sf_gh18506(self): + # gh-18506 reported that `sf` was incorrect for large population; + # check that this is resolved + n = 10 + N = 10**5 + i = np.arange(5, 15) + population_size = 10.**i + p = stats.hypergeom.sf(n - 1, population_size, N, n) + assert np.all(p > 0) + assert np.all(np.diff(p) < 0) + + +class TestLoggamma: + + # Expected cdf values were computed with mpmath. For given x and c, + # x = mpmath.mpf(x) + # c = mpmath.mpf(c) + # cdf = mpmath.gammainc(c, 0, mpmath.exp(x), + # regularized=True) + @pytest.mark.parametrize('x, c, cdf', + [(1, 2, 0.7546378854206702), + (-1, 14, 6.768116452566383e-18), + (-745.1, 0.001, 0.4749605142005238), + (-800, 0.001, 0.44958802911019136), + (-725, 0.1, 3.4301205868273265e-32), + (-740, 0.75, 1.0074360436599631e-241)]) + def test_cdf_ppf(self, x, c, cdf): + p = stats.loggamma.cdf(x, c) + assert_allclose(p, cdf, rtol=1e-13) + y = stats.loggamma.ppf(cdf, c) + assert_allclose(y, x, rtol=1e-13) + + # Expected sf values were computed with mpmath. For given x and c, + # x = mpmath.mpf(x) + # c = mpmath.mpf(c) + # sf = mpmath.gammainc(c, mpmath.exp(x), mpmath.inf, + # regularized=True) + @pytest.mark.parametrize('x, c, sf', + [(4, 1.5, 1.6341528919488565e-23), + (6, 100, 8.23836829202024e-74), + (-800, 0.001, 0.5504119708898086), + (-743, 0.0025, 0.8437131370024089)]) + def test_sf_isf(self, x, c, sf): + s = stats.loggamma.sf(x, c) + assert_allclose(s, sf, rtol=1e-13) + y = stats.loggamma.isf(sf, c) + assert_allclose(y, x, rtol=1e-13) + + def test_logpdf(self): + # Test logpdf with x=-500, c=2. ln(gamma(2)) = 0, and + # exp(-500) ~= 7e-218, which is far smaller than the ULP + # of c*x=-1000, so logpdf(-500, 2) = c*x - exp(x) - ln(gamma(2)) + # should give -1000.0. + lp = stats.loggamma.logpdf(-500, 2) + assert_allclose(lp, -1000.0, rtol=1e-14) + + def test_stats(self): + # The following precomputed values are from the table in section 2.2 + # of "A Statistical Study of Log-Gamma Distribution", by Ping Shing + # Chan (thesis, McMaster University, 1993). + table = np.array([ + # c, mean, var, skew, exc. kurt. + 0.5, -1.9635, 4.9348, -1.5351, 4.0000, + 1.0, -0.5772, 1.6449, -1.1395, 2.4000, + 12.0, 2.4427, 0.0869, -0.2946, 0.1735, + ]).reshape(-1, 5) + for c, mean, var, skew, kurt in table: + computed = stats.loggamma.stats(c, moments='msvk') + assert_array_almost_equal(computed, [mean, var, skew, kurt], + decimal=4) + + @pytest.mark.parametrize('c', [0.1, 0.001]) + def test_rvs(self, c): + # Regression test for gh-11094. + x = stats.loggamma.rvs(c, size=100000) + # Before gh-11094 was fixed, the case with c=0.001 would + # generate many -inf values. + assert np.isfinite(x).all() + # Crude statistical test. About half the values should be + # less than the median and half greater than the median. + med = stats.loggamma.median(c) + btest = stats.binomtest(np.count_nonzero(x < med), len(x)) + ci = btest.proportion_ci(confidence_level=0.999) + assert ci.low < 0.5 < ci.high + + @pytest.mark.parametrize("c, ref", + [(1e-8, 19.420680753952364), + (1, 1.5772156649015328), + (1e4, -3.186214986116763), + (1e10, -10.093986931748889), + (1e100, -113.71031611649761)]) + def test_entropy(self, c, ref): + + # Reference values were calculated with mpmath + # from mpmath import mp + # mp.dps = 500 + # def loggamma_entropy_mpmath(c): + # c = mp.mpf(c) + # return float(mp.log(mp.gamma(c)) + c * (mp.one - mp.digamma(c))) + + assert_allclose(stats.loggamma.entropy(c), ref, rtol=1e-14) + + +class TestJohnsonsu: + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 50 + # def johnsonsu_sf(x, a, b): + # x = mp.mpf(x) + # a = mp.mpf(a) + # b = mp.mpf(b) + # return float(mp.ncdf(-(a + b * mp.log(x + mp.sqrt(x*x + 1))))) + # Order is x, a, b, sf, isf tol + # (Can't expect full precision when the ISF input is very nearly 1) + cases = [(-500, 1, 1, 0.9999999982660072, 1e-8), + (2000, 1, 1, 7.426351000595343e-21, 5e-14), + (100000, 1, 1, 4.046923979269977e-40, 5e-14)] + + @pytest.mark.parametrize("case", cases) + def test_sf_isf(self, case): + x, a, b, sf, tol = case + assert_allclose(stats.johnsonsu.sf(x, a, b), sf, rtol=5e-14) + assert_allclose(stats.johnsonsu.isf(sf, a, b), x, rtol=tol) + + +class TestJohnsonb: + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 50 + # def johnsonb_sf(x, a, b): + # x = mp.mpf(x) + # a = mp.mpf(a) + # b = mp.mpf(b) + # return float(mp.ncdf(-(a + b * mp.log(x/(mp.one - x))))) + # Order is x, a, b, sf, isf atol + # (Can't expect full precision when the ISF input is very nearly 1) + cases = [(1e-4, 1, 1, 0.9999999999999999, 1e-7), + (0.9999, 1, 1, 8.921114313932308e-25, 5e-14), + (0.999999, 1, 1, 5.815197487181902e-50, 5e-14)] + + @pytest.mark.parametrize("case", cases) + def test_sf_isf(self, case): + x, a, b, sf, tol = case + assert_allclose(stats.johnsonsb.sf(x, a, b), sf, rtol=5e-14) + assert_allclose(stats.johnsonsb.isf(sf, a, b), x, atol=tol) + + +class TestLogistic: + # gh-6226 + def test_cdf_ppf(self): + x = np.linspace(-20, 20) + y = stats.logistic.cdf(x) + xx = stats.logistic.ppf(y) + assert_allclose(x, xx) + + def test_sf_isf(self): + x = np.linspace(-20, 20) + y = stats.logistic.sf(x) + xx = stats.logistic.isf(y) + assert_allclose(x, xx) + + def test_extreme_values(self): + # p is chosen so that 1 - (1 - p) == p in double precision + p = 9.992007221626409e-16 + desired = 34.53957599234088 + assert_allclose(stats.logistic.ppf(1 - p), desired) + assert_allclose(stats.logistic.isf(p), desired) + + def test_logpdf_basic(self): + logp = stats.logistic.logpdf([-15, 0, 10]) + # Expected values computed with mpmath with 50 digits of precision. + expected = [-15.000000611804547, + -1.3862943611198906, + -10.000090797798434] + assert_allclose(logp, expected, rtol=1e-13) + + def test_logpdf_extreme_values(self): + logp = stats.logistic.logpdf([800, -800]) + # For such large arguments, logpdf(x) = -abs(x) when computed + # with 64 bit floating point. + assert_equal(logp, [-800, -800]) + + @pytest.mark.parametrize("loc_rvs,scale_rvs", [(0.4484955, 0.10216821), + (0.62918191, 0.74367064)]) + def test_fit(self, loc_rvs, scale_rvs): + data = stats.logistic.rvs(size=100, loc=loc_rvs, scale=scale_rvs) + + # test that result of fit method is the same as optimization + def func(input, data): + a, b = input + n = len(data) + x1 = np.sum(np.exp((data - a) / b) / + (1 + np.exp((data - a) / b))) - n / 2 + x2 = np.sum(((data - a) / b) * + ((np.exp((data - a) / b) - 1) / + (np.exp((data - a) / b) + 1))) - n + return x1, x2 + + expected_solution = root(func, stats.logistic._fitstart(data), args=( + data,)).x + fit_method = stats.logistic.fit(data) + + # other than computational variances, the fit method and the solution + # to this system of equations are equal + assert_allclose(fit_method, expected_solution, atol=1e-30) + + def test_fit_comp_optimizer(self): + data = stats.logistic.rvs(size=100, loc=0.5, scale=2) + _assert_less_or_close_loglike(stats.logistic, data) + _assert_less_or_close_loglike(stats.logistic, data, floc=1) + _assert_less_or_close_loglike(stats.logistic, data, fscale=1) + + @pytest.mark.parametrize('testlogcdf', [True, False]) + def test_logcdfsf_tails(self, testlogcdf): + # Test either logcdf or logsf. By symmetry, we can use the same + # expected values for both by switching the sign of x for logsf. + x = np.array([-10000, -800, 17, 50, 500]) + if testlogcdf: + y = stats.logistic.logcdf(x) + else: + y = stats.logistic.logsf(-x) + # The expected values were computed with mpmath. + expected = [-10000.0, -800.0, -4.139937633089748e-08, + -1.9287498479639178e-22, -7.124576406741286e-218] + assert_allclose(y, expected, rtol=2e-15) + + def test_fit_gh_18176(self): + # logistic.fit returned `scale < 0` for this data. Check that this has + # been fixed. + data = np.array([-459, 37, 43, 45, 45, 48, 54, 55, 58] + + [59] * 3 + [61] * 9) + # If scale were negative, NLLF would be infinite, so this would fail + _assert_less_or_close_loglike(stats.logistic, data) + + +class TestLogser: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.logser.rvs(0.75, size=(2, 50)) + assert_(numpy.all(vals >= 1)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.logser.rvs(0.75) + assert_(isinstance(val, int)) + val = stats.logser(0.75).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_pmf_small_p(self): + m = stats.logser.pmf(4, 1e-20) + # The expected value was computed using mpmath: + # >>> import mpmath + # >>> mpmath.mp.dps = 64 + # >>> k = 4 + # >>> p = mpmath.mpf('1e-20') + # >>> float(-(p**k)/k/mpmath.log(1-p)) + # 2.5e-61 + # It is also clear from noticing that for very small p, + # log(1-p) is approximately -p, and the formula becomes + # p**(k-1) / k + assert_allclose(m, 2.5e-61) + + def test_mean_small_p(self): + m = stats.logser.mean(1e-8) + # The expected mean was computed using mpmath: + # >>> import mpmath + # >>> mpmath.dps = 60 + # >>> p = mpmath.mpf('1e-8') + # >>> float(-p / ((1 - p)*mpmath.log(1 - p))) + # 1.000000005 + assert_allclose(m, 1.000000005) + + +class TestGumbel_r_l: + @pytest.fixture(scope='function') + def rng(self): + return np.random.default_rng(1234) + + @pytest.mark.parametrize("dist", [stats.gumbel_r, stats.gumbel_l]) + @pytest.mark.parametrize("loc_rvs", [-1, 0, 1]) + @pytest.mark.parametrize("scale_rvs", [.1, 1, 5]) + @pytest.mark.parametrize('fix_loc, fix_scale', + ([True, False], [False, True])) + def test_fit_comp_optimizer(self, dist, loc_rvs, scale_rvs, + fix_loc, fix_scale, rng): + data = dist.rvs(size=100, loc=loc_rvs, scale=scale_rvs, + random_state=rng) + + kwds = dict() + # the fixed location and scales are arbitrarily modified to not be + # close to the true value. + if fix_loc: + kwds['floc'] = loc_rvs * 2 + if fix_scale: + kwds['fscale'] = scale_rvs * 2 + + # test that the gumbel_* fit method is better than super method + _assert_less_or_close_loglike(dist, data, **kwds) + + @pytest.mark.parametrize("dist, sgn", [(stats.gumbel_r, 1), + (stats.gumbel_l, -1)]) + def test_fit(self, dist, sgn): + z = sgn*np.array([3, 3, 3, 3, 3, 3, 3, 3.00000001]) + loc, scale = dist.fit(z) + # The expected values were computed with mpmath with 60 digits + # of precision. + assert_allclose(loc, sgn*3.0000000001667906) + assert_allclose(scale, 1.2495222465145514e-09, rtol=1e-6) + + +class TestPareto: + def test_stats(self): + # Check the stats() method with some simple values. Also check + # that the calculations do not trigger RuntimeWarnings. + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + + m, v, s, k = stats.pareto.stats(0.5, moments='mvsk') + assert_equal(m, np.inf) + assert_equal(v, np.inf) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(1.0, moments='mvsk') + assert_equal(m, np.inf) + assert_equal(v, np.inf) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(1.5, moments='mvsk') + assert_equal(m, 3.0) + assert_equal(v, np.inf) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(2.0, moments='mvsk') + assert_equal(m, 2.0) + assert_equal(v, np.inf) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(2.5, moments='mvsk') + assert_allclose(m, 2.5 / 1.5) + assert_allclose(v, 2.5 / (1.5*1.5*0.5)) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(3.0, moments='mvsk') + assert_allclose(m, 1.5) + assert_allclose(v, 0.75) + assert_equal(s, np.nan) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(3.5, moments='mvsk') + assert_allclose(m, 3.5 / 2.5) + assert_allclose(v, 3.5 / (2.5*2.5*1.5)) + assert_allclose(s, (2*4.5/0.5)*np.sqrt(1.5/3.5)) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(4.0, moments='mvsk') + assert_allclose(m, 4.0 / 3.0) + assert_allclose(v, 4.0 / 18.0) + assert_allclose(s, 2*(1+4.0)/(4.0-3) * np.sqrt((4.0-2)/4.0)) + assert_equal(k, np.nan) + + m, v, s, k = stats.pareto.stats(4.5, moments='mvsk') + assert_allclose(m, 4.5 / 3.5) + assert_allclose(v, 4.5 / (3.5*3.5*2.5)) + assert_allclose(s, (2*5.5/1.5) * np.sqrt(2.5/4.5)) + assert_allclose(k, 6*(4.5**3 + 4.5**2 - 6*4.5 - 2)/(4.5*1.5*0.5)) + + def test_sf(self): + x = 1e9 + b = 2 + scale = 1.5 + p = stats.pareto.sf(x, b, loc=0, scale=scale) + expected = (scale/x)**b # 2.25e-18 + assert_allclose(p, expected) + + @pytest.fixture(scope='function') + def rng(self): + return np.random.default_rng(1234) + + @pytest.mark.filterwarnings("ignore:invalid value encountered in " + "double_scalars") + @pytest.mark.parametrize("rvs_shape", [1, 2]) + @pytest.mark.parametrize("rvs_loc", [0, 2]) + @pytest.mark.parametrize("rvs_scale", [1, 5]) + def test_fit(self, rvs_shape, rvs_loc, rvs_scale, rng): + data = stats.pareto.rvs(size=100, b=rvs_shape, scale=rvs_scale, + loc=rvs_loc, random_state=rng) + + # shape can still be fixed with multiple names + shape_mle_analytical1 = stats.pareto.fit(data, floc=0, f0=1.04)[0] + shape_mle_analytical2 = stats.pareto.fit(data, floc=0, fix_b=1.04)[0] + shape_mle_analytical3 = stats.pareto.fit(data, floc=0, fb=1.04)[0] + assert (shape_mle_analytical1 == shape_mle_analytical2 == + shape_mle_analytical3 == 1.04) + + # data can be shifted with changes to `loc` + data = stats.pareto.rvs(size=100, b=rvs_shape, scale=rvs_scale, + loc=(rvs_loc + 2), random_state=rng) + shape_mle_a, loc_mle_a, scale_mle_a = stats.pareto.fit(data, floc=2) + assert_equal(scale_mle_a + 2, data.min()) + + data_shift = data - 2 + ndata = data_shift.shape[0] + assert_equal(shape_mle_a, + ndata / np.sum(np.log(data_shift/data_shift.min()))) + assert_equal(loc_mle_a, 2) + + @pytest.mark.parametrize("rvs_shape", [.1, 2]) + @pytest.mark.parametrize("rvs_loc", [0, 2]) + @pytest.mark.parametrize("rvs_scale", [1, 5]) + @pytest.mark.parametrize('fix_shape, fix_loc, fix_scale', + [p for p in product([True, False], repeat=3) + if False in p]) + @np.errstate(invalid="ignore") + def test_fit_MLE_comp_optimizer(self, rvs_shape, rvs_loc, rvs_scale, + fix_shape, fix_loc, fix_scale, rng): + data = stats.pareto.rvs(size=100, b=rvs_shape, scale=rvs_scale, + loc=rvs_loc, random_state=rng) + + kwds = {} + if fix_shape: + kwds['f0'] = rvs_shape + if fix_loc: + kwds['floc'] = rvs_loc + if fix_scale: + kwds['fscale'] = rvs_scale + + _assert_less_or_close_loglike(stats.pareto, data, **kwds) + + @np.errstate(invalid="ignore") + def test_fit_known_bad_seed(self): + # Tests a known seed and set of parameters that would produce a result + # would violate the support of Pareto if the fit method did not check + # the constraint `fscale + floc < min(data)`. + shape, location, scale = 1, 0, 1 + data = stats.pareto.rvs(shape, location, scale, size=100, + random_state=np.random.default_rng(2535619)) + _assert_less_or_close_loglike(stats.pareto, data) + + def test_fit_warnings(self): + assert_fit_warnings(stats.pareto) + # `floc` that causes invalid negative data + assert_raises(FitDataError, stats.pareto.fit, [1, 2, 3], floc=2) + # `floc` and `fscale` combination causes invalid data + assert_raises(FitDataError, stats.pareto.fit, [5, 2, 3], floc=1, + fscale=3) + + def test_negative_data(self, rng): + data = stats.pareto.rvs(loc=-130, b=1, size=100, random_state=rng) + assert_array_less(data, 0) + # The purpose of this test is to make sure that no runtime warnings are + # raised for all negative data, not the output of the fit method. Other + # methods test the output but have to silence warnings from the super + # method. + _ = stats.pareto.fit(data) + + +class TestGenpareto: + def test_ab(self): + # c >= 0: a, b = [0, inf] + for c in [1., 0.]: + c = np.asarray(c) + a, b = stats.genpareto._get_support(c) + assert_equal(a, 0.) + assert_(np.isposinf(b)) + + # c < 0: a=0, b=1/|c| + c = np.asarray(-2.) + a, b = stats.genpareto._get_support(c) + assert_allclose([a, b], [0., 0.5]) + + def test_c0(self): + # with c=0, genpareto reduces to the exponential distribution + # rv = stats.genpareto(c=0.) + rv = stats.genpareto(c=0.) + x = np.linspace(0, 10., 30) + assert_allclose(rv.pdf(x), stats.expon.pdf(x)) + assert_allclose(rv.cdf(x), stats.expon.cdf(x)) + assert_allclose(rv.sf(x), stats.expon.sf(x)) + + q = np.linspace(0., 1., 10) + assert_allclose(rv.ppf(q), stats.expon.ppf(q)) + + def test_cm1(self): + # with c=-1, genpareto reduces to the uniform distr on [0, 1] + rv = stats.genpareto(c=-1.) + x = np.linspace(0, 10., 30) + assert_allclose(rv.pdf(x), stats.uniform.pdf(x)) + assert_allclose(rv.cdf(x), stats.uniform.cdf(x)) + assert_allclose(rv.sf(x), stats.uniform.sf(x)) + + q = np.linspace(0., 1., 10) + assert_allclose(rv.ppf(q), stats.uniform.ppf(q)) + + # logpdf(1., c=-1) should be zero + assert_allclose(rv.logpdf(1), 0) + + def test_x_inf(self): + # make sure x=inf is handled gracefully + rv = stats.genpareto(c=0.1) + assert_allclose([rv.pdf(np.inf), rv.cdf(np.inf)], [0., 1.]) + assert_(np.isneginf(rv.logpdf(np.inf))) + + rv = stats.genpareto(c=0.) + assert_allclose([rv.pdf(np.inf), rv.cdf(np.inf)], [0., 1.]) + assert_(np.isneginf(rv.logpdf(np.inf))) + + rv = stats.genpareto(c=-1.) + assert_allclose([rv.pdf(np.inf), rv.cdf(np.inf)], [0., 1.]) + assert_(np.isneginf(rv.logpdf(np.inf))) + + def test_c_continuity(self): + # pdf is continuous at c=0, -1 + x = np.linspace(0, 10, 30) + for c in [0, -1]: + pdf0 = stats.genpareto.pdf(x, c) + for dc in [1e-14, -1e-14]: + pdfc = stats.genpareto.pdf(x, c + dc) + assert_allclose(pdf0, pdfc, atol=1e-12) + + cdf0 = stats.genpareto.cdf(x, c) + for dc in [1e-14, 1e-14]: + cdfc = stats.genpareto.cdf(x, c + dc) + assert_allclose(cdf0, cdfc, atol=1e-12) + + def test_c_continuity_ppf(self): + q = np.r_[np.logspace(1e-12, 0.01, base=0.1), + np.linspace(0.01, 1, 30, endpoint=False), + 1. - np.logspace(1e-12, 0.01, base=0.1)] + for c in [0., -1.]: + ppf0 = stats.genpareto.ppf(q, c) + for dc in [1e-14, -1e-14]: + ppfc = stats.genpareto.ppf(q, c + dc) + assert_allclose(ppf0, ppfc, atol=1e-12) + + def test_c_continuity_isf(self): + q = np.r_[np.logspace(1e-12, 0.01, base=0.1), + np.linspace(0.01, 1, 30, endpoint=False), + 1. - np.logspace(1e-12, 0.01, base=0.1)] + for c in [0., -1.]: + isf0 = stats.genpareto.isf(q, c) + for dc in [1e-14, -1e-14]: + isfc = stats.genpareto.isf(q, c + dc) + assert_allclose(isf0, isfc, atol=1e-12) + + def test_cdf_ppf_roundtrip(self): + # this should pass with machine precision. hat tip @pbrod + q = np.r_[np.logspace(1e-12, 0.01, base=0.1), + np.linspace(0.01, 1, 30, endpoint=False), + 1. - np.logspace(1e-12, 0.01, base=0.1)] + for c in [1e-8, -1e-18, 1e-15, -1e-15]: + assert_allclose(stats.genpareto.cdf(stats.genpareto.ppf(q, c), c), + q, atol=1e-15) + + def test_logsf(self): + logp = stats.genpareto.logsf(1e10, .01, 0, 1) + assert_allclose(logp, -1842.0680753952365) + + # Values in 'expected_stats' are + # [mean, variance, skewness, excess kurtosis]. + @pytest.mark.parametrize( + 'c, expected_stats', + [(0, [1, 1, 2, 6]), + (1/4, [4/3, 32/9, 10/np.sqrt(2), np.nan]), + (1/9, [9/8, (81/64)*(9/7), (10/9)*np.sqrt(7), 754/45]), + (-1, [1/2, 1/12, 0, -6/5])]) + def test_stats(self, c, expected_stats): + result = stats.genpareto.stats(c, moments='mvsk') + assert_allclose(result, expected_stats, rtol=1e-13, atol=1e-15) + + def test_var(self): + # Regression test for gh-11168. + v = stats.genpareto.var(1e-8) + assert_allclose(v, 1.000000040000001, rtol=1e-13) + + +class TestPearson3: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.pearson3.rvs(0.1, size=(2, 50)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllFloat']) + val = stats.pearson3.rvs(0.5) + assert_(isinstance(val, float)) + val = stats.pearson3(0.5).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllFloat']) + assert_(len(val) == 3) + + def test_pdf(self): + vals = stats.pearson3.pdf(2, [0.0, 0.1, 0.2]) + assert_allclose(vals, np.array([0.05399097, 0.05555481, 0.05670246]), + atol=1e-6) + vals = stats.pearson3.pdf(-3, 0.1) + assert_allclose(vals, np.array([0.00313791]), atol=1e-6) + vals = stats.pearson3.pdf([-3, -2, -1, 0, 1], 0.1) + assert_allclose(vals, np.array([0.00313791, 0.05192304, 0.25028092, + 0.39885918, 0.23413173]), atol=1e-6) + + def test_cdf(self): + vals = stats.pearson3.cdf(2, [0.0, 0.1, 0.2]) + assert_allclose(vals, np.array([0.97724987, 0.97462004, 0.97213626]), + atol=1e-6) + vals = stats.pearson3.cdf(-3, 0.1) + assert_allclose(vals, [0.00082256], atol=1e-6) + vals = stats.pearson3.cdf([-3, -2, -1, 0, 1], 0.1) + assert_allclose(vals, [8.22563821e-04, 1.99860448e-02, 1.58550710e-01, + 5.06649130e-01, 8.41442111e-01], atol=1e-6) + + def test_negative_cdf_bug_11186(self): + # incorrect CDFs for negative skews in gh-11186; fixed in gh-12640 + # Also check vectorization w/ negative, zero, and positive skews + skews = [-3, -1, 0, 0.5] + x_eval = 0.5 + neg_inf = -30 # avoid RuntimeWarning caused by np.log(0) + cdfs = stats.pearson3.cdf(x_eval, skews) + int_pdfs = [quad(stats.pearson3(skew).pdf, neg_inf, x_eval)[0] + for skew in skews] + assert_allclose(cdfs, int_pdfs) + + def test_return_array_bug_11746(self): + # pearson3.moment was returning size 0 or 1 array instead of float + # The first moment is equal to the loc, which defaults to zero + moment = stats.pearson3.moment(1, 2) + assert_equal(moment, 0) + assert isinstance(moment, np.number) + + moment = stats.pearson3.moment(1, 0.000001) + assert_equal(moment, 0) + assert isinstance(moment, np.number) + + def test_ppf_bug_17050(self): + # incorrect PPF for negative skews were reported in gh-17050 + # Check that this is fixed (even in the array case) + skews = [-3, -1, 0, 0.5] + x_eval = 0.5 + res = stats.pearson3.ppf(stats.pearson3.cdf(x_eval, skews), skews) + assert_allclose(res, x_eval) + + # Negation of the skew flips the distribution about the origin, so + # the following should hold + skew = np.array([[-0.5], [1.5]]) + x = np.linspace(-2, 2) + assert_allclose(stats.pearson3.pdf(x, skew), + stats.pearson3.pdf(-x, -skew)) + assert_allclose(stats.pearson3.cdf(x, skew), + stats.pearson3.sf(-x, -skew)) + assert_allclose(stats.pearson3.ppf(x, skew), + -stats.pearson3.isf(x, -skew)) + + def test_sf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 50; Pearson3(skew=skew).sf(x). Check positive, negative, + # and zero skew due to branching. + skew = [0.1, 0.5, 1.0, -0.1] + x = [5.0, 10.0, 50.0, 8.0] + ref = [1.64721926440872e-06, 8.271911573556123e-11, + 1.3149506021756343e-40, 2.763057937820296e-21] + assert_allclose(stats.pearson3.sf(x, skew), ref, rtol=2e-14) + assert_allclose(stats.pearson3.sf(x, 0), stats.norm.sf(x), rtol=2e-14) + + +class TestKappa4: + def test_cdf_genpareto(self): + # h = 1 and k != 0 is generalized Pareto + x = [0.0, 0.1, 0.2, 0.5] + h = 1.0 + for k in [-1.9, -1.0, -0.5, -0.2, -0.1, 0.1, 0.2, 0.5, 1.0, + 1.9]: + vals = stats.kappa4.cdf(x, h, k) + # shape parameter is opposite what is expected + vals_comp = stats.genpareto.cdf(x, -k) + assert_allclose(vals, vals_comp) + + def test_cdf_genextreme(self): + # h = 0 and k != 0 is generalized extreme value + x = np.linspace(-5, 5, 10) + h = 0.0 + k = np.linspace(-3, 3, 10) + vals = stats.kappa4.cdf(x, h, k) + vals_comp = stats.genextreme.cdf(x, k) + assert_allclose(vals, vals_comp) + + def test_cdf_expon(self): + # h = 1 and k = 0 is exponential + x = np.linspace(0, 10, 10) + h = 1.0 + k = 0.0 + vals = stats.kappa4.cdf(x, h, k) + vals_comp = stats.expon.cdf(x) + assert_allclose(vals, vals_comp) + + def test_cdf_gumbel_r(self): + # h = 0 and k = 0 is gumbel_r + x = np.linspace(-5, 5, 10) + h = 0.0 + k = 0.0 + vals = stats.kappa4.cdf(x, h, k) + vals_comp = stats.gumbel_r.cdf(x) + assert_allclose(vals, vals_comp) + + def test_cdf_logistic(self): + # h = -1 and k = 0 is logistic + x = np.linspace(-5, 5, 10) + h = -1.0 + k = 0.0 + vals = stats.kappa4.cdf(x, h, k) + vals_comp = stats.logistic.cdf(x) + assert_allclose(vals, vals_comp) + + def test_cdf_uniform(self): + # h = 1 and k = 1 is uniform + x = np.linspace(-5, 5, 10) + h = 1.0 + k = 1.0 + vals = stats.kappa4.cdf(x, h, k) + vals_comp = stats.uniform.cdf(x) + assert_allclose(vals, vals_comp) + + def test_integers_ctor(self): + # regression test for gh-7416: _argcheck fails for integer h and k + # in numpy 1.12 + stats.kappa4(1, 2) + + +class TestPoisson: + def setup_method(self): + np.random.seed(1234) + + def test_pmf_basic(self): + # Basic case + ln2 = np.log(2) + vals = stats.poisson.pmf([0, 1, 2], ln2) + expected = [0.5, ln2/2, ln2**2/4] + assert_allclose(vals, expected) + + def test_mu0(self): + # Edge case: mu=0 + vals = stats.poisson.pmf([0, 1, 2], 0) + expected = [1, 0, 0] + assert_array_equal(vals, expected) + + interval = stats.poisson.interval(0.95, 0) + assert_equal(interval, (0, 0)) + + def test_rvs(self): + vals = stats.poisson.rvs(0.5, size=(2, 50)) + assert_(numpy.all(vals >= 0)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.poisson.rvs(0.5) + assert_(isinstance(val, int)) + val = stats.poisson(0.5).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_stats(self): + mu = 16.0 + result = stats.poisson.stats(mu, moments='mvsk') + assert_allclose(result, [mu, mu, np.sqrt(1.0/mu), 1.0/mu]) + + mu = np.array([0.0, 1.0, 2.0]) + result = stats.poisson.stats(mu, moments='mvsk') + expected = (mu, mu, [np.inf, 1, 1/np.sqrt(2)], [np.inf, 1, 0.5]) + assert_allclose(result, expected) + + +class TestKSTwo: + def setup_method(self): + np.random.seed(1234) + + def test_cdf(self): + for n in [1, 2, 3, 10, 100, 1000]: + # Test x-values: + # 0, 1/2n, where the cdf should be 0 + # 1/n, where the cdf should be n!/n^n + # 0.5, where the cdf should match ksone.cdf + # 1-1/n, where cdf = 1-2/n^n + # 1, where cdf == 1 + # (E.g. Exact values given by Eqn 1 in Simard / L'Ecuyer) + x = np.array([0, 0.5/n, 1/n, 0.5, 1-1.0/n, 1]) + v1 = (1.0/n)**n + lg = scipy.special.gammaln(n+1) + elg = (np.exp(lg) if v1 != 0 else 0) + expected = np.array([0, 0, v1 * elg, + 1 - 2*stats.ksone.sf(0.5, n), + max(1 - 2*v1, 0.0), + 1.0]) + vals_cdf = stats.kstwo.cdf(x, n) + assert_allclose(vals_cdf, expected) + + def test_sf(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + # Same x values as in test_cdf, and use sf = 1 - cdf + x = np.array([0, 0.5/n, 1/n, 0.5, 1-1.0/n, 1]) + v1 = (1.0/n)**n + lg = scipy.special.gammaln(n+1) + elg = (np.exp(lg) if v1 != 0 else 0) + expected = np.array([1.0, 1.0, + 1 - v1 * elg, + 2*stats.ksone.sf(0.5, n), + min(2*v1, 1.0), 0]) + vals_sf = stats.kstwo.sf(x, n) + assert_allclose(vals_sf, expected) + + def test_cdf_sqrtn(self): + # For fixed a, cdf(a/sqrt(n), n) -> kstwobign(a) as n->infinity + # cdf(a/sqrt(n), n) is an increasing function of n (and a) + # Check that the function is indeed increasing (allowing for some + # small floating point and algorithm differences.) + x = np.linspace(0, 2, 11)[1:] + ns = [50, 100, 200, 400, 1000, 2000] + for _x in x: + xn = _x / np.sqrt(ns) + probs = stats.kstwo.cdf(xn, ns) + diffs = np.diff(probs) + assert_array_less(diffs, 1e-8) + + def test_cdf_sf(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + vals_cdf = stats.kstwo.cdf(x, n) + vals_sf = stats.kstwo.sf(x, n) + assert_array_almost_equal(vals_cdf, 1 - vals_sf) + + def test_cdf_sf_sqrtn(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + xn = x / np.sqrt(n) + vals_cdf = stats.kstwo.cdf(xn, n) + vals_sf = stats.kstwo.sf(xn, n) + assert_array_almost_equal(vals_cdf, 1 - vals_sf) + + def test_ppf_of_cdf(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + xn = x[x > 0.5/n] + vals_cdf = stats.kstwo.cdf(xn, n) + # CDFs close to 1 are better dealt with using the SF + cond = (0 < vals_cdf) & (vals_cdf < 0.99) + vals = stats.kstwo.ppf(vals_cdf, n) + assert_allclose(vals[cond], xn[cond], rtol=1e-4) + + def test_isf_of_sf(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + xn = x[x > 0.5/n] + vals_isf = stats.kstwo.isf(xn, n) + cond = (0 < vals_isf) & (vals_isf < 1.0) + vals = stats.kstwo.sf(vals_isf, n) + assert_allclose(vals[cond], xn[cond], rtol=1e-4) + + def test_ppf_of_cdf_sqrtn(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + xn = (x / np.sqrt(n))[x > 0.5/n] + vals_cdf = stats.kstwo.cdf(xn, n) + cond = (0 < vals_cdf) & (vals_cdf < 1.0) + vals = stats.kstwo.ppf(vals_cdf, n) + assert_allclose(vals[cond], xn[cond]) + + def test_isf_of_sf_sqrtn(self): + x = np.linspace(0, 1, 11) + for n in [1, 2, 3, 10, 100, 1000]: + xn = (x / np.sqrt(n))[x > 0.5/n] + vals_sf = stats.kstwo.sf(xn, n) + # SFs close to 1 are better dealt with using the CDF + cond = (0 < vals_sf) & (vals_sf < 0.95) + vals = stats.kstwo.isf(vals_sf, n) + assert_allclose(vals[cond], xn[cond]) + + def test_ppf(self): + probs = np.linspace(0, 1, 11)[1:] + for n in [1, 2, 3, 10, 100, 1000]: + xn = stats.kstwo.ppf(probs, n) + vals_cdf = stats.kstwo.cdf(xn, n) + assert_allclose(vals_cdf, probs) + + def test_simard_lecuyer_table1(self): + # Compute the cdf for values near the mean of the distribution. + # The mean u ~ log(2)*sqrt(pi/(2n)) + # Compute for x in [u/4, u/3, u/2, u, 2u, 3u] + # This is the computation of Table 1 of Simard, R., L'Ecuyer, P. (2011) + # "Computing the Two-Sided Kolmogorov-Smirnov Distribution". + # Except that the values below are not from the published table, but + # were generated using an independent SageMath implementation of + # Durbin's algorithm (with the exponentiation and scaling of + # Marsaglia/Tsang/Wang's version) using 500 bit arithmetic. + # Some of the values in the published table have relative + # errors greater than 1e-4. + ns = [10, 50, 100, 200, 500, 1000] + ratios = np.array([1.0/4, 1.0/3, 1.0/2, 1, 2, 3]) + expected = np.array([ + [1.92155292e-08, 5.72933228e-05, 2.15233226e-02, 6.31566589e-01, + 9.97685592e-01, 9.99999942e-01], + [2.28096224e-09, 1.99142563e-05, 1.42617934e-02, 5.95345542e-01, + 9.96177701e-01, 9.99998662e-01], + [1.00201886e-09, 1.32673079e-05, 1.24608594e-02, 5.86163220e-01, + 9.95866877e-01, 9.99998240e-01], + [4.93313022e-10, 9.52658029e-06, 1.12123138e-02, 5.79486872e-01, + 9.95661824e-01, 9.99997964e-01], + [2.37049293e-10, 6.85002458e-06, 1.01309221e-02, 5.73427224e-01, + 9.95491207e-01, 9.99997750e-01], + [1.56990874e-10, 5.71738276e-06, 9.59725430e-03, 5.70322692e-01, + 9.95409545e-01, 9.99997657e-01] + ]) + for idx, n in enumerate(ns): + x = ratios * np.log(2) * np.sqrt(np.pi/2/n) + vals_cdf = stats.kstwo.cdf(x, n) + assert_allclose(vals_cdf, expected[idx], rtol=1e-5) + + +class TestZipf: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.zipf.rvs(1.5, size=(2, 50)) + assert_(numpy.all(vals >= 1)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.zipf.rvs(1.5) + assert_(isinstance(val, int)) + val = stats.zipf(1.5).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + + def test_moments(self): + # n-th moment is finite iff a > n + 1 + m, v = stats.zipf.stats(a=2.8) + assert_(np.isfinite(m)) + assert_equal(v, np.inf) + + s, k = stats.zipf.stats(a=4.8, moments='sk') + assert_(not np.isfinite([s, k]).all()) + + +class TestDLaplace: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + vals = stats.dlaplace.rvs(1.5, size=(2, 50)) + assert_(numpy.shape(vals) == (2, 50)) + assert_(vals.dtype.char in typecodes['AllInteger']) + val = stats.dlaplace.rvs(1.5) + assert_(isinstance(val, int)) + val = stats.dlaplace(1.5).rvs(3) + assert_(isinstance(val, numpy.ndarray)) + assert_(val.dtype.char in typecodes['AllInteger']) + assert_(stats.dlaplace.rvs(0.8) is not None) + + def test_stats(self): + # compare the explicit formulas w/ direct summation using pmf + a = 1. + dl = stats.dlaplace(a) + m, v, s, k = dl.stats('mvsk') + + N = 37 + xx = np.arange(-N, N+1) + pp = dl.pmf(xx) + m2, m4 = np.sum(pp*xx**2), np.sum(pp*xx**4) + assert_equal((m, s), (0, 0)) + assert_allclose((v, k), (m2, m4/m2**2 - 3.), atol=1e-14, rtol=1e-8) + + def test_stats2(self): + a = np.log(2.) + dl = stats.dlaplace(a) + m, v, s, k = dl.stats('mvsk') + assert_equal((m, s), (0., 0.)) + assert_allclose((v, k), (4., 3.25)) + + +class TestInvgauss: + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.parametrize("rvs_mu,rvs_loc,rvs_scale", + [(2, 0, 1), (4.635, 4.362, 6.303)]) + def test_fit(self, rvs_mu, rvs_loc, rvs_scale): + data = stats.invgauss.rvs(size=100, mu=rvs_mu, + loc=rvs_loc, scale=rvs_scale) + # Analytical MLEs are calculated with formula when `floc` is fixed + mu, loc, scale = stats.invgauss.fit(data, floc=rvs_loc) + + data = data - rvs_loc + mu_temp = np.mean(data) + scale_mle = len(data) / (np.sum(data**(-1) - mu_temp**(-1))) + mu_mle = mu_temp/scale_mle + + # `mu` and `scale` match analytical formula + assert_allclose(mu_mle, mu, atol=1e-15, rtol=1e-15) + assert_allclose(scale_mle, scale, atol=1e-15, rtol=1e-15) + assert_equal(loc, rvs_loc) + data = stats.invgauss.rvs(size=100, mu=rvs_mu, + loc=rvs_loc, scale=rvs_scale) + # fixed parameters are returned + mu, loc, scale = stats.invgauss.fit(data, floc=rvs_loc - 1, + fscale=rvs_scale + 1) + assert_equal(rvs_scale + 1, scale) + assert_equal(rvs_loc - 1, loc) + + # shape can still be fixed with multiple names + shape_mle1 = stats.invgauss.fit(data, fmu=1.04)[0] + shape_mle2 = stats.invgauss.fit(data, fix_mu=1.04)[0] + shape_mle3 = stats.invgauss.fit(data, f0=1.04)[0] + assert shape_mle1 == shape_mle2 == shape_mle3 == 1.04 + + @pytest.mark.parametrize("rvs_mu,rvs_loc,rvs_scale", + [(2, 0, 1), (6.311, 3.225, 4.520)]) + def test_fit_MLE_comp_optimizer(self, rvs_mu, rvs_loc, rvs_scale): + rng = np.random.RandomState(1234) + data = stats.invgauss.rvs(size=100, mu=rvs_mu, + loc=rvs_loc, scale=rvs_scale, random_state=rng) + + super_fit = super(type(stats.invgauss), stats.invgauss).fit + # fitting without `floc` uses superclass fit method + super_fitted = super_fit(data) + invgauss_fit = stats.invgauss.fit(data) + assert_equal(super_fitted, invgauss_fit) + + # fitting with `fmu` is uses superclass fit method + super_fitted = super_fit(data, floc=0, fmu=2) + invgauss_fit = stats.invgauss.fit(data, floc=0, fmu=2) + assert_equal(super_fitted, invgauss_fit) + + # fixed `floc` uses analytical formula and provides better fit than + # super method + _assert_less_or_close_loglike(stats.invgauss, data, floc=rvs_loc) + + # fixed `floc` not resulting in invalid data < 0 uses analytical + # formulas and provides a better fit than the super method + assert np.all((data - (rvs_loc - 1)) > 0) + _assert_less_or_close_loglike(stats.invgauss, data, floc=rvs_loc - 1) + + # fixed `floc` to an arbitrary number, 0, still provides a better fit + # than the super method + _assert_less_or_close_loglike(stats.invgauss, data, floc=0) + + # fixed `fscale` to an arbitrary number still provides a better fit + # than the super method + _assert_less_or_close_loglike(stats.invgauss, data, floc=rvs_loc, + fscale=np.random.rand(1)[0]) + + def test_fit_raise_errors(self): + assert_fit_warnings(stats.invgauss) + # FitDataError is raised when negative invalid data + with pytest.raises(FitDataError): + stats.invgauss.fit([1, 2, 3], floc=2) + + def test_cdf_sf(self): + # Regression tests for gh-13614. + # Ground truth from R's statmod library (pinvgauss), e.g. + # library(statmod) + # options(digits=15) + # mu = c(4.17022005e-04, 7.20324493e-03, 1.14374817e-06, + # 3.02332573e-03, 1.46755891e-03) + # print(pinvgauss(5, mu, 1)) + + # make sure a finite value is returned when mu is very small. see + # GH-13614 + mu = [4.17022005e-04, 7.20324493e-03, 1.14374817e-06, + 3.02332573e-03, 1.46755891e-03] + expected = [1, 1, 1, 1, 1] + actual = stats.invgauss.cdf(0.4, mu=mu) + assert_equal(expected, actual) + + # test if the function can distinguish small left/right tail + # probabilities from zero. + cdf_actual = stats.invgauss.cdf(0.001, mu=1.05) + assert_allclose(cdf_actual, 4.65246506892667e-219) + sf_actual = stats.invgauss.sf(110, mu=1.05) + assert_allclose(sf_actual, 4.12851625944048e-25) + + # test if x does not cause numerical issues when mu is very small + # and x is close to mu in value. + + # slightly smaller than mu + actual = stats.invgauss.cdf(0.00009, 0.0001) + assert_allclose(actual, 2.9458022894924e-26) + + # slightly bigger than mu + actual = stats.invgauss.cdf(0.000102, 0.0001) + assert_allclose(actual, 0.976445540507925) + + def test_logcdf_logsf(self): + # Regression tests for improvements made in gh-13616. + # Ground truth from R's statmod library (pinvgauss), e.g. + # library(statmod) + # options(digits=15) + # print(pinvgauss(0.001, 1.05, 1, log.p=TRUE, lower.tail=FALSE)) + + # test if logcdf and logsf can compute values too small to + # be represented on the unlogged scale. See: gh-13616 + logcdf = stats.invgauss.logcdf(0.0001, mu=1.05) + assert_allclose(logcdf, -5003.87872590367) + logcdf = stats.invgauss.logcdf(110, 1.05) + assert_allclose(logcdf, -4.12851625944087e-25) + logsf = stats.invgauss.logsf(0.001, mu=1.05) + assert_allclose(logsf, -4.65246506892676e-219) + logsf = stats.invgauss.logsf(110, 1.05) + assert_allclose(logsf, -56.1467092416426) + + # from mpmath import mp + # mp.dps = 100 + # mu = mp.mpf(1e-2) + # ref = (1/2 * mp.log(2 * mp.pi * mp.e * mu**3) + # - 3/2* mp.exp(2/mu) * mp.e1(2/mu)) + @pytest.mark.parametrize("mu, ref", [(2e-8, -25.172361826883957), + (1e-3, -8.943444010642972), + (1e-2, -5.4962796152622335), + (1e8, 3.3244822568873476), + (1e100, 3.32448280139689)]) + def test_entropy(self, mu, ref): + assert_allclose(stats.invgauss.entropy(mu), ref, rtol=5e-14) + + +class TestLaplace: + @pytest.mark.parametrize("rvs_loc", [-5, 0, 1, 2]) + @pytest.mark.parametrize("rvs_scale", [1, 2, 3, 10]) + def test_fit(self, rvs_loc, rvs_scale): + # tests that various inputs follow expected behavior + # for a variety of `loc` and `scale`. + rng = np.random.RandomState(1234) + data = stats.laplace.rvs(size=100, loc=rvs_loc, scale=rvs_scale, + random_state=rng) + + # MLE estimates are given by + loc_mle = np.median(data) + scale_mle = np.sum(np.abs(data - loc_mle)) / len(data) + + # standard outputs should match analytical MLE formulas + loc, scale = stats.laplace.fit(data) + assert_allclose(loc, loc_mle, atol=1e-15, rtol=1e-15) + assert_allclose(scale, scale_mle, atol=1e-15, rtol=1e-15) + + # fixed parameter should use analytical formula for other + loc, scale = stats.laplace.fit(data, floc=loc_mle) + assert_allclose(scale, scale_mle, atol=1e-15, rtol=1e-15) + loc, scale = stats.laplace.fit(data, fscale=scale_mle) + assert_allclose(loc, loc_mle) + + # test with non-mle fixed parameter + # create scale with non-median loc + loc = rvs_loc * 2 + scale_mle = np.sum(np.abs(data - loc)) / len(data) + + # fixed loc to non median, scale should match + # scale calculation with modified loc + loc, scale = stats.laplace.fit(data, floc=loc) + assert_equal(scale_mle, scale) + + # fixed scale created with non median loc, + # loc output should still be the data median. + loc, scale = stats.laplace.fit(data, fscale=scale_mle) + assert_equal(loc_mle, loc) + + # error raised when both `floc` and `fscale` are fixed + assert_raises(RuntimeError, stats.laplace.fit, data, floc=loc_mle, + fscale=scale_mle) + + # error is raised with non-finite values + assert_raises(ValueError, stats.laplace.fit, [np.nan]) + assert_raises(ValueError, stats.laplace.fit, [np.inf]) + + @pytest.mark.parametrize("rvs_loc,rvs_scale", [(-5, 10), + (10, 5), + (0.5, 0.2)]) + def test_fit_MLE_comp_optimizer(self, rvs_loc, rvs_scale): + rng = np.random.RandomState(1234) + data = stats.laplace.rvs(size=1000, loc=rvs_loc, scale=rvs_scale, + random_state=rng) + + # the log-likelihood function for laplace is given by + def ll(loc, scale, data): + return -1 * (- (len(data)) * np.log(2*scale) - + (1/scale)*np.sum(np.abs(data - loc))) + + # test that the objective function result of the analytical MLEs is + # less than or equal to that of the numerically optimized estimate + loc, scale = stats.laplace.fit(data) + loc_opt, scale_opt = super(type(stats.laplace), + stats.laplace).fit(data) + ll_mle = ll(loc, scale, data) + ll_opt = ll(loc_opt, scale_opt, data) + assert ll_mle < ll_opt or np.allclose(ll_mle, ll_opt, + atol=1e-15, rtol=1e-15) + + def test_fit_simple_non_random_data(self): + data = np.array([1.0, 1.0, 3.0, 5.0, 8.0, 14.0]) + # with `floc` fixed to 6, scale should be 4. + loc, scale = stats.laplace.fit(data, floc=6) + assert_allclose(scale, 4, atol=1e-15, rtol=1e-15) + # with `fscale` fixed to 6, loc should be 4. + loc, scale = stats.laplace.fit(data, fscale=6) + assert_allclose(loc, 4, atol=1e-15, rtol=1e-15) + + def test_sf_cdf_extremes(self): + # These calculations should not generate warnings. + x = 1000 + p0 = stats.laplace.cdf(-x) + # The exact value is smaller than can be represented with + # 64 bit floating point, so the expected result is 0. + assert p0 == 0.0 + # The closest 64 bit floating point representation of the + # exact value is 1.0. + p1 = stats.laplace.cdf(x) + assert p1 == 1.0 + + p0 = stats.laplace.sf(x) + # The exact value is smaller than can be represented with + # 64 bit floating point, so the expected result is 0. + assert p0 == 0.0 + # The closest 64 bit floating point representation of the + # exact value is 1.0. + p1 = stats.laplace.sf(-x) + assert p1 == 1.0 + + def test_sf(self): + x = 200 + p = stats.laplace.sf(x) + assert_allclose(p, np.exp(-x)/2, rtol=1e-13) + + def test_isf(self): + p = 1e-25 + x = stats.laplace.isf(p) + assert_allclose(x, -np.log(2*p), rtol=1e-13) + + +class TestLogLaplace: + + def test_sf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 100; LogLaplace(c=c).sf(x). + c = np.array([2.0, 3.0, 5.0]) + x = np.array([1e-5, 1e10, 1e15]) + ref = [0.99999999995, 5e-31, 5e-76] + assert_allclose(stats.loglaplace.sf(x, c), ref, rtol=1e-15) + + def test_isf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 100; LogLaplace(c=c).isf(q). + c = 3.25 + q = [0.8, 0.1, 1e-10, 1e-20, 1e-40] + ref = [0.7543222539245642, 1.6408455124660906, 964.4916294395846, + 1151387.578354072, 1640845512466.0906] + assert_allclose(stats.loglaplace.isf(q, c), ref, rtol=1e-14) + + @pytest.mark.parametrize('r', [1, 2, 3, 4]) + def test_moments_stats(self, r): + mom = 'mvsk'[r - 1] + c = np.arange(0.5, r + 0.5, 0.5) + + # r-th non-central moment is infinite if |r| >= c. + assert_allclose(stats.loglaplace.moment(r, c), np.inf) + + # r-th non-central moment is non-finite (inf or nan) if r >= c. + assert not np.any(np.isfinite(stats.loglaplace.stats(c, moments=mom))) + + @pytest.mark.parametrize("c", [0.5, 1.0, 2.0]) + @pytest.mark.parametrize("loc, scale", [(-1.2, 3.45)]) + @pytest.mark.parametrize("fix_c", [True, False]) + @pytest.mark.parametrize("fix_scale", [True, False]) + def test_fit_analytic_mle(self, c, loc, scale, fix_c, fix_scale): + # Test that the analytical MLE produces no worse result than the + # generic (numerical) MLE. + + rng = np.random.default_rng(6762668991392531563) + data = stats.loglaplace.rvs(c, loc=loc, scale=scale, size=100, + random_state=rng) + + kwds = {'floc': loc} + if fix_c: + kwds['fc'] = c + if fix_scale: + kwds['fscale'] = scale + nfree = 3 - len(kwds) + + if nfree == 0: + error_msg = "All parameters fixed. There is nothing to optimize." + with pytest.raises((RuntimeError, ValueError), match=error_msg): + stats.loglaplace.fit(data, **kwds) + return + + _assert_less_or_close_loglike(stats.loglaplace, data, **kwds) + +class TestPowerlaw: + + # In the following data, `sf` was computed with mpmath. + @pytest.mark.parametrize('x, a, sf', + [(0.25, 2.0, 0.9375), + (0.99609375, 1/256, 1.528855235208108e-05)]) + def test_sf(self, x, a, sf): + assert_allclose(stats.powerlaw.sf(x, a), sf, rtol=1e-15) + + @pytest.fixture(scope='function') + def rng(self): + return np.random.default_rng(1234) + + @pytest.mark.parametrize("rvs_shape", [.1, .5, .75, 1, 2]) + @pytest.mark.parametrize("rvs_loc", [-1, 0, 1]) + @pytest.mark.parametrize("rvs_scale", [.1, 1, 5]) + @pytest.mark.parametrize('fix_shape, fix_loc, fix_scale', + [p for p in product([True, False], repeat=3) + if False in p]) + def test_fit_MLE_comp_optimizer(self, rvs_shape, rvs_loc, rvs_scale, + fix_shape, fix_loc, fix_scale, rng): + data = stats.powerlaw.rvs(size=250, a=rvs_shape, loc=rvs_loc, + scale=rvs_scale, random_state=rng) + + kwds = dict() + if fix_shape: + kwds['f0'] = rvs_shape + if fix_loc: + kwds['floc'] = np.nextafter(data.min(), -np.inf) + if fix_scale: + kwds['fscale'] = rvs_scale + + # Numerical result may equal analytical result if some code path + # of the analytical routine makes use of numerical optimization. + _assert_less_or_close_loglike(stats.powerlaw, data, **kwds, + maybe_identical=True) + + def test_problem_case(self): + # An observed problem with the test method indicated that some fixed + # scale values could cause bad results, this is now corrected. + a = 2.50002862645130604506 + location = 0.0 + scale = 35.249023299873095 + + data = stats.powerlaw.rvs(a=a, loc=location, scale=scale, size=100, + random_state=np.random.default_rng(5)) + + kwds = {'fscale': np.ptp(data) * 2} + + _assert_less_or_close_loglike(stats.powerlaw, data, **kwds) + + def test_fit_warnings(self): + assert_fit_warnings(stats.powerlaw) + # test for error when `fscale + floc <= np.max(data)` is not satisfied + msg = r" Maximum likelihood estimation with 'powerlaw' requires" + with assert_raises(FitDataError, match=msg): + stats.powerlaw.fit([1, 2, 4], floc=0, fscale=3) + + # test for error when `data - floc >= 0` is not satisfied + msg = r" Maximum likelihood estimation with 'powerlaw' requires" + with assert_raises(FitDataError, match=msg): + stats.powerlaw.fit([1, 2, 4], floc=2) + + # test for fixed location not less than `min(data)`. + msg = r" Maximum likelihood estimation with 'powerlaw' requires" + with assert_raises(FitDataError, match=msg): + stats.powerlaw.fit([1, 2, 4], floc=1) + + # test for when fixed scale is less than or equal to range of data + msg = r"Negative or zero `fscale` is outside" + with assert_raises(ValueError, match=msg): + stats.powerlaw.fit([1, 2, 4], fscale=-3) + + # test for when fixed scale is less than or equal to range of data + msg = r"`fscale` must be greater than the range of data." + with assert_raises(ValueError, match=msg): + stats.powerlaw.fit([1, 2, 4], fscale=3) + + def test_minimum_data_zero_gh17801(self): + # gh-17801 reported an overflow error when the minimum value of the + # data is zero. Check that this problem is resolved. + data = [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6] + dist = stats.powerlaw + with np.errstate(over='ignore'): + _assert_less_or_close_loglike(dist, data) + + +class TestPowerLogNorm: + + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 80 + # def powerlognorm_sf_mp(x, c, s): + # x = mp.mpf(x) + # c = mp.mpf(c) + # s = mp.mpf(s) + # return mp.ncdf(-mp.log(x) / s)**c + # + # def powerlognormal_cdf_mp(x, c, s): + # return mp.one - powerlognorm_sf_mp(x, c, s) + # + # x, c, s = 100, 20, 1 + # print(float(powerlognorm_sf_mp(x, c, s))) + + @pytest.mark.parametrize("x, c, s, ref", + [(100, 20, 1, 1.9057100820561928e-114), + (1e-3, 20, 1, 0.9999999999507617), + (1e-3, 0.02, 1, 0.9999999999999508), + (1e22, 0.02, 1, 6.50744044621611e-12)]) + def test_sf(self, x, c, s, ref): + assert_allclose(stats.powerlognorm.sf(x, c, s), ref, rtol=1e-13) + + # reference values were computed via mpmath using the survival + # function above (passing in `ref` and getting `q`). + @pytest.mark.parametrize("q, c, s, ref", + [(0.9999999587870905, 0.02, 1, 0.01), + (6.690376686108851e-233, 20, 1, 1000)]) + def test_isf(self, q, c, s, ref): + assert_allclose(stats.powerlognorm.isf(q, c, s), ref, rtol=5e-11) + + @pytest.mark.parametrize("x, c, s, ref", + [(1e25, 0.02, 1, 0.9999999999999963), + (1e-6, 0.02, 1, 2.054921078040843e-45), + (1e-6, 200, 1, 2.0549210780408428e-41), + (0.3, 200, 1, 0.9999999999713368)]) + def test_cdf(self, x, c, s, ref): + assert_allclose(stats.powerlognorm.cdf(x, c, s), ref, rtol=3e-14) + + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 50 + # def powerlognorm_pdf_mpmath(x, c, s): + # x = mp.mpf(x) + # c = mp.mpf(c) + # s = mp.mpf(s) + # res = (c/(x * s) * mp.npdf(mp.log(x)/s) * + # mp.ncdf(-mp.log(x)/s)**(c - mp.one)) + # return float(res) + + @pytest.mark.parametrize("x, c, s, ref", + [(1e22, 0.02, 1, 6.5954987852335016e-34), + (1e20, 1e-3, 1, 1.588073750563988e-22), + (1e40, 1e-3, 1, 1.3179391812506349e-43)]) + def test_pdf(self, x, c, s, ref): + assert_allclose(stats.powerlognorm.pdf(x, c, s), ref, rtol=3e-12) + + +class TestPowerNorm: + + # survival function references were computed with mpmath via + # from mpmath import mp + # x = mp.mpf(x) + # c = mp.mpf(x) + # float(mp.ncdf(-x)**c) + + @pytest.mark.parametrize("x, c, ref", + [(9, 1, 1.1285884059538405e-19), + (20, 2, 7.582445786569958e-178), + (100, 0.02, 3.330957891903866e-44), + (200, 0.01, 1.3004759092324774e-87)]) + def test_sf(self, x, c, ref): + assert_allclose(stats.powernorm.sf(x, c), ref, rtol=1e-13) + + # inverse survival function references were computed with mpmath via + # from mpmath import mp + # def isf_mp(q, c): + # q = mp.mpf(q) + # c = mp.mpf(c) + # arg = q**(mp.one / c) + # return float(-mp.sqrt(2) * mp.erfinv(mp.mpf(2.) * arg - mp.one)) + + @pytest.mark.parametrize("q, c, ref", + [(1e-5, 20, -0.15690800666514138), + (0.99999, 100, -5.19933666203545), + (0.9999, 0.02, -2.576676052143387), + (5e-2, 0.02, 17.089518110222244), + (1e-18, 2, 5.9978070150076865), + (1e-50, 5, 6.361340902404057)]) + def test_isf(self, q, c, ref): + assert_allclose(stats.powernorm.isf(q, c), ref, rtol=5e-12) + + # CDF reference values were computed with mpmath via + # from mpmath import mp + # def cdf_mp(x, c): + # x = mp.mpf(x) + # c = mp.mpf(c) + # return float(mp.one - mp.ncdf(-x)**c) + + @pytest.mark.parametrize("x, c, ref", + [(-12, 9, 1.598833900869911e-32), + (2, 9, 0.9999999999999983), + (-20, 9, 2.4782617067456103e-88), + (-5, 0.02, 5.733032242841443e-09), + (-20, 0.02, 5.507248237212467e-91)]) + def test_cdf(self, x, c, ref): + assert_allclose(stats.powernorm.cdf(x, c), ref, rtol=5e-14) + + +class TestInvGamma: + def test_invgamma_inf_gh_1866(self): + # invgamma's moments are only finite for a>n + # specific numbers checked w/ boost 1.54 + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + mvsk = stats.invgamma.stats(a=19.31, moments='mvsk') + expected = [0.05461496450, 0.0001723162534, 1.020362676, + 2.055616582] + assert_allclose(mvsk, expected) + + a = [1.1, 3.1, 5.6] + mvsk = stats.invgamma.stats(a=a, moments='mvsk') + expected = ([10., 0.476190476, 0.2173913043], # mmm + [np.inf, 0.2061430632, 0.01312749422], # vvv + [np.nan, 41.95235392, 2.919025532], # sss + [np.nan, np.nan, 24.51923076]) # kkk + for x, y in zip(mvsk, expected): + assert_almost_equal(x, y) + + def test_cdf_ppf(self): + # gh-6245 + x = np.logspace(-2.6, 0) + y = stats.invgamma.cdf(x, 1) + xx = stats.invgamma.ppf(y, 1) + assert_allclose(x, xx) + + def test_sf_isf(self): + # gh-6245 + if sys.maxsize > 2**32: + x = np.logspace(2, 100) + else: + # Invgamme roundtrip on 32-bit systems has relative accuracy + # ~1e-15 until x=1e+15, and becomes inf above x=1e+18 + x = np.logspace(2, 18) + + y = stats.invgamma.sf(x, 1) + xx = stats.invgamma.isf(y, 1) + assert_allclose(x, xx, rtol=1.0) + + @pytest.mark.parametrize("a, ref", + [(100000000.0, -26.21208257605721), + (1e+100, -343.9688254159022)]) + def test_large_entropy(self, a, ref): + # The reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + + # def invgamma_entropy(a): + # a = mp.mpf(a) + # h = a + mp.loggamma(a) - (mp.one + a) * mp.digamma(a) + # return float(h) + assert_allclose(stats.invgamma.entropy(a), ref, rtol=1e-15) + + +class TestF: + def test_endpoints(self): + # Compute the pdf at the left endpoint dst.a. + data = [[stats.f, (2, 1), 1.0]] + for _f, _args, _correct in data: + ans = _f.pdf(_f.a, *_args) + + ans = [_f.pdf(_f.a, *_args) for _f, _args, _ in data] + correct = [_correct_ for _f, _args, _correct_ in data] + assert_array_almost_equal(ans, correct) + + def test_f_moments(self): + # n-th moment of F distributions is only finite for n < dfd / 2 + m, v, s, k = stats.f.stats(11, 6.5, moments='mvsk') + assert_(np.isfinite(m)) + assert_(np.isfinite(v)) + assert_(np.isfinite(s)) + assert_(not np.isfinite(k)) + + def test_moments_warnings(self): + # no warnings should be generated for dfd = 2, 4, 6, 8 (div by zero) + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + stats.f.stats(dfn=[11]*4, dfd=[2, 4, 6, 8], moments='mvsk') + + def test_stats_broadcast(self): + dfn = np.array([[3], [11]]) + dfd = np.array([11, 12]) + m, v, s, k = stats.f.stats(dfn=dfn, dfd=dfd, moments='mvsk') + m2 = [dfd / (dfd - 2)]*2 + assert_allclose(m, m2) + v2 = 2 * dfd**2 * (dfn + dfd - 2) / dfn / (dfd - 2)**2 / (dfd - 4) + assert_allclose(v, v2) + s2 = ((2*dfn + dfd - 2) * np.sqrt(8*(dfd - 4)) / + ((dfd - 6) * np.sqrt(dfn*(dfn + dfd - 2)))) + assert_allclose(s, s2) + k2num = 12 * (dfn * (5*dfd - 22) * (dfn + dfd - 2) + + (dfd - 4) * (dfd - 2)**2) + k2den = dfn * (dfd - 6) * (dfd - 8) * (dfn + dfd - 2) + k2 = k2num / k2den + assert_allclose(k, k2) + + +class TestStudentT: + def test_rvgeneric_std(self): + # Regression test for #1191 + assert_array_almost_equal(stats.t.std([5, 6]), [1.29099445, 1.22474487]) + + def test_moments_t(self): + # regression test for #8786 + assert_equal(stats.t.stats(df=1, moments='mvsk'), + (np.inf, np.nan, np.nan, np.nan)) + assert_equal(stats.t.stats(df=1.01, moments='mvsk'), + (0.0, np.inf, np.nan, np.nan)) + assert_equal(stats.t.stats(df=2, moments='mvsk'), + (0.0, np.inf, np.nan, np.nan)) + assert_equal(stats.t.stats(df=2.01, moments='mvsk'), + (0.0, 2.01/(2.01-2.0), np.nan, np.inf)) + assert_equal(stats.t.stats(df=3, moments='sk'), (np.nan, np.inf)) + assert_equal(stats.t.stats(df=3.01, moments='sk'), (0.0, np.inf)) + assert_equal(stats.t.stats(df=4, moments='sk'), (0.0, np.inf)) + assert_equal(stats.t.stats(df=4.01, moments='sk'), (0.0, 6.0/(4.01 - 4.0))) + + def test_t_entropy(self): + df = [1, 2, 25, 100] + # Expected values were computed with mpmath. + expected = [2.5310242469692907, 1.9602792291600821, + 1.459327578078393, 1.4289633653182439] + assert_allclose(stats.t.entropy(df), expected, rtol=1e-13) + + @pytest.mark.parametrize("v, ref", + [(100, 1.4289633653182439), + (1e+100, 1.4189385332046727)]) + def test_t_extreme_entropy(self, v, ref): + # Reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + # + # def t_entropy(v): + # v = mp.mpf(v) + # C = (v + mp.one) / 2 + # A = C * (mp.digamma(C) - mp.digamma(v / 2)) + # B = 0.5 * mp.log(v) + mp.log(mp.beta(v / 2, mp.one / 2)) + # h = A + B + # return float(h) + assert_allclose(stats.t.entropy(v), ref, rtol=1e-14) + + @pytest.mark.parametrize("methname", ["pdf", "logpdf", "cdf", + "ppf", "sf", "isf"]) + @pytest.mark.parametrize("df_infmask", [[0, 0], [1, 1], [0, 1], + [[0, 1, 0], [1, 1, 1]], + [[1, 0], [0, 1]], + [[0], [1]]]) + def test_t_inf_df(self, methname, df_infmask): + np.random.seed(0) + df_infmask = np.asarray(df_infmask, dtype=bool) + df = np.random.uniform(0, 10, size=df_infmask.shape) + x = np.random.randn(*df_infmask.shape) + df[df_infmask] = np.inf + t_dist = stats.t(df=df, loc=3, scale=1) + t_dist_ref = stats.t(df=df[~df_infmask], loc=3, scale=1) + norm_dist = stats.norm(loc=3, scale=1) + t_meth = getattr(t_dist, methname) + t_meth_ref = getattr(t_dist_ref, methname) + norm_meth = getattr(norm_dist, methname) + res = t_meth(x) + assert_equal(res[df_infmask], norm_meth(x[df_infmask])) + assert_equal(res[~df_infmask], t_meth_ref(x[~df_infmask])) + + @pytest.mark.parametrize("df_infmask", [[0, 0], [1, 1], [0, 1], + [[0, 1, 0], [1, 1, 1]], + [[1, 0], [0, 1]], + [[0], [1]]]) + def test_t_inf_df_stats_entropy(self, df_infmask): + np.random.seed(0) + df_infmask = np.asarray(df_infmask, dtype=bool) + df = np.random.uniform(0, 10, size=df_infmask.shape) + df[df_infmask] = np.inf + res = stats.t.stats(df=df, loc=3, scale=1, moments='mvsk') + res_ex_inf = stats.norm.stats(loc=3, scale=1, moments='mvsk') + res_ex_noinf = stats.t.stats(df=df[~df_infmask], loc=3, scale=1, + moments='mvsk') + for i in range(4): + assert_equal(res[i][df_infmask], res_ex_inf[i]) + assert_equal(res[i][~df_infmask], res_ex_noinf[i]) + + res = stats.t.entropy(df=df, loc=3, scale=1) + res_ex_inf = stats.norm.entropy(loc=3, scale=1) + res_ex_noinf = stats.t.entropy(df=df[~df_infmask], loc=3, scale=1) + assert_equal(res[df_infmask], res_ex_inf) + assert_equal(res[~df_infmask], res_ex_noinf) + + def test_logpdf_pdf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 500; StudentT(df=df).logpdf(x), StudentT(df=df).pdf(x) + x = [1, 1e3, 10, 1] + df = [1e100, 1e50, 1e20, 1] + logpdf_ref = [-1.4189385332046727, -500000.9189385332, + -50.918938533204674, -1.8378770664093456] + pdf_ref = [0.24197072451914334, 0, + 7.69459862670642e-23, 0.15915494309189535] + assert_allclose(stats.t.logpdf(x, df), logpdf_ref, rtol=1e-14) + assert_allclose(stats.t.pdf(x, df), pdf_ref, rtol=1e-14) + + +class TestRvDiscrete: + def setup_method(self): + np.random.seed(1234) + + def test_rvs(self): + states = [-1, 0, 1, 2, 3, 4] + probability = [0.0, 0.3, 0.4, 0.0, 0.3, 0.0] + samples = 1000 + r = stats.rv_discrete(name='sample', values=(states, probability)) + x = r.rvs(size=samples) + assert_(isinstance(x, numpy.ndarray)) + + for s, p in zip(states, probability): + assert_(abs(sum(x == s)/float(samples) - p) < 0.05) + + x = r.rvs() + assert np.issubdtype(type(x), np.integer) + + def test_entropy(self): + # Basic tests of entropy. + pvals = np.array([0.25, 0.45, 0.3]) + p = stats.rv_discrete(values=([0, 1, 2], pvals)) + expected_h = -sum(xlogy(pvals, pvals)) + h = p.entropy() + assert_allclose(h, expected_h) + + p = stats.rv_discrete(values=([0, 1, 2], [1.0, 0, 0])) + h = p.entropy() + assert_equal(h, 0.0) + + def test_pmf(self): + xk = [1, 2, 4] + pk = [0.5, 0.3, 0.2] + rv = stats.rv_discrete(values=(xk, pk)) + + x = [[1., 4.], + [3., 2]] + assert_allclose(rv.pmf(x), + [[0.5, 0.2], + [0., 0.3]], atol=1e-14) + + def test_cdf(self): + xk = [1, 2, 4] + pk = [0.5, 0.3, 0.2] + rv = stats.rv_discrete(values=(xk, pk)) + + x_values = [-2, 1., 1.1, 1.5, 2.0, 3.0, 4, 5] + expected = [0, 0.5, 0.5, 0.5, 0.8, 0.8, 1, 1] + assert_allclose(rv.cdf(x_values), expected, atol=1e-14) + + # also check scalar arguments + assert_allclose([rv.cdf(xx) for xx in x_values], + expected, atol=1e-14) + + def test_ppf(self): + xk = [1, 2, 4] + pk = [0.5, 0.3, 0.2] + rv = stats.rv_discrete(values=(xk, pk)) + + q_values = [0.1, 0.5, 0.6, 0.8, 0.9, 1.] + expected = [1, 1, 2, 2, 4, 4] + assert_allclose(rv.ppf(q_values), expected, atol=1e-14) + + # also check scalar arguments + assert_allclose([rv.ppf(q) for q in q_values], + expected, atol=1e-14) + + def test_cdf_ppf_next(self): + # copied and special cased from test_discrete_basic + vals = ([1, 2, 4, 7, 8], [0.1, 0.2, 0.3, 0.3, 0.1]) + rv = stats.rv_discrete(values=vals) + + assert_array_equal(rv.ppf(rv.cdf(rv.xk[:-1]) + 1e-8), + rv.xk[1:]) + + def test_multidimension(self): + xk = np.arange(12).reshape((3, 4)) + pk = np.array([[0.1, 0.1, 0.15, 0.05], + [0.1, 0.1, 0.05, 0.05], + [0.1, 0.1, 0.05, 0.05]]) + rv = stats.rv_discrete(values=(xk, pk)) + + assert_allclose(rv.expect(), np.sum(rv.xk * rv.pk), atol=1e-14) + + def test_bad_input(self): + xk = [1, 2, 3] + pk = [0.5, 0.5] + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + pk = [1, 2, 3] + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + xk = [1, 2, 3] + pk = [0.5, 1.2, -0.7] + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + xk = [1, 2, 3, 4, 5] + pk = [0.3, 0.3, 0.3, 0.3, -0.2] + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + xk = [1, 1] + pk = [0.5, 0.5] + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + def test_shape_rv_sample(self): + # tests added for gh-9565 + + # mismatch of 2d inputs + xk, pk = np.arange(4).reshape((2, 2)), np.full((2, 3), 1/6) + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + # same number of elements, but shapes not compatible + xk, pk = np.arange(6).reshape((3, 2)), np.full((2, 3), 1/6) + assert_raises(ValueError, stats.rv_discrete, **dict(values=(xk, pk))) + + # same shapes => no error + xk, pk = np.arange(6).reshape((3, 2)), np.full((3, 2), 1/6) + assert_equal(stats.rv_discrete(values=(xk, pk)).pmf(0), 1/6) + + def test_expect1(self): + xk = [1, 2, 4, 6, 7, 11] + pk = [0.1, 0.2, 0.2, 0.2, 0.2, 0.1] + rv = stats.rv_discrete(values=(xk, pk)) + + assert_allclose(rv.expect(), np.sum(rv.xk * rv.pk), atol=1e-14) + + def test_expect2(self): + # rv_sample should override _expect. Bug report from + # https://stackoverflow.com/questions/63199792 + y = [200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, + 1100.0, 1200.0, 1300.0, 1400.0, 1500.0, 1600.0, 1700.0, 1800.0, + 1900.0, 2000.0, 2100.0, 2200.0, 2300.0, 2400.0, 2500.0, 2600.0, + 2700.0, 2800.0, 2900.0, 3000.0, 3100.0, 3200.0, 3300.0, 3400.0, + 3500.0, 3600.0, 3700.0, 3800.0, 3900.0, 4000.0, 4100.0, 4200.0, + 4300.0, 4400.0, 4500.0, 4600.0, 4700.0, 4800.0] + + py = [0.0004, 0.0, 0.0033, 0.006500000000000001, 0.0, 0.0, + 0.004399999999999999, 0.6862, 0.0, 0.0, 0.0, + 0.00019999999999997797, 0.0006000000000000449, + 0.024499999999999966, 0.006400000000000072, + 0.0043999999999999595, 0.019499999999999962, + 0.03770000000000007, 0.01759999999999995, 0.015199999999999991, + 0.018100000000000005, 0.04500000000000004, 0.0025999999999999357, + 0.0, 0.0041000000000001036, 0.005999999999999894, + 0.0042000000000000925, 0.0050000000000000044, + 0.0041999999999999815, 0.0004999999999999449, + 0.009199999999999986, 0.008200000000000096, + 0.0, 0.0, 0.0046999999999999265, 0.0019000000000000128, + 0.0006000000000000449, 0.02510000000000001, 0.0, + 0.007199999999999984, 0.0, 0.012699999999999934, 0.0, 0.0, + 0.008199999999999985, 0.005600000000000049, 0.0] + + rv = stats.rv_discrete(values=(y, py)) + + # check the mean + assert_allclose(rv.expect(), rv.mean(), atol=1e-14) + assert_allclose(rv.expect(), + sum(v * w for v, w in zip(y, py)), atol=1e-14) + + # also check the second moment + assert_allclose(rv.expect(lambda x: x**2), + sum(v**2 * w for v, w in zip(y, py)), atol=1e-14) + + +class TestSkewCauchy: + def test_cauchy(self): + x = np.linspace(-5, 5, 100) + assert_array_almost_equal(stats.skewcauchy.pdf(x, a=0), + stats.cauchy.pdf(x)) + assert_array_almost_equal(stats.skewcauchy.cdf(x, a=0), + stats.cauchy.cdf(x)) + assert_array_almost_equal(stats.skewcauchy.ppf(x, a=0), + stats.cauchy.ppf(x)) + + def test_skewcauchy_R(self): + # options(digits=16) + # library(sgt) + # # lmbda, x contain the values generated for a, x below + # lmbda <- c(0.0976270078546495, 0.430378732744839, 0.2055267521432877, + # 0.0897663659937937, -0.15269040132219, 0.2917882261333122, + # -0.12482557747462, 0.7835460015641595, 0.9273255210020589, + # -0.2331169623484446) + # x <- c(2.917250380826646, 0.2889491975290444, 0.6804456109393229, + # 4.25596638292661, -4.289639418021131, -4.1287070029845925, + # -4.797816025596743, 3.32619845547938, 2.7815675094985046, + # 3.700121482468191) + # pdf = dsgt(x, mu=0, lambda=lambda, sigma=1, q=1/2, mean.cent=FALSE, + # var.adj = sqrt(2)) + # cdf = psgt(x, mu=0, lambda=lambda, sigma=1, q=1/2, mean.cent=FALSE, + # var.adj = sqrt(2)) + # qsgt(cdf, mu=0, lambda=lambda, sigma=1, q=1/2, mean.cent=FALSE, + # var.adj = sqrt(2)) + + np.random.seed(0) + a = np.random.rand(10) * 2 - 1 + x = np.random.rand(10) * 10 - 5 + pdf = [0.039473975217333909, 0.305829714049903223, 0.24140158118994162, + 0.019585772402693054, 0.021436553695989482, 0.00909817103867518, + 0.01658423410016873, 0.071083288030394126, 0.103250045941454524, + 0.013110230778426242] + cdf = [0.87426677718213752, 0.37556468910780882, 0.59442096496538066, + 0.91304659850890202, 0.09631964100300605, 0.03829624330921733, + 0.08245240578402535, 0.72057062945510386, 0.62826415852515449, + 0.95011308463898292] + assert_allclose(stats.skewcauchy.pdf(x, a), pdf) + assert_allclose(stats.skewcauchy.cdf(x, a), cdf) + assert_allclose(stats.skewcauchy.ppf(cdf, a), x) + + +class TestJFSkewT: + def test_compare_t(self): + # Verify that jf_skew_t with a=b recovers the t distribution with 2a + # degrees of freedom + a = b = 5 + df = a * 2 + x = [-1.0, 0.0, 1.0, 2.0] + q = [0.0, 0.1, 0.25, 0.75, 0.90, 1.0] + + jf = stats.jf_skew_t(a, b) + t = stats.t(df) + + assert_allclose(jf.pdf(x), t.pdf(x)) + assert_allclose(jf.cdf(x), t.cdf(x)) + assert_allclose(jf.ppf(q), t.ppf(q)) + assert_allclose(jf.stats('mvsk'), t.stats('mvsk')) + + @pytest.fixture + def gamlss_pdf_data(self): + """Sample data points computed using the `ST5` distribution from the + GAMLSS package in R. The pdf has been calculated for (a,b)=(2,3), + (a,b)=(8,4), and (a,b)=(12,13) for x in `np.linspace(-10, 10, 41)`. + + N.B. the `ST5` distribution in R uses an alternative parameterization + in terms of nu and tau, where: + - nu = (a - b) / (a * b * (a + b)) ** 0.5 + - tau = 2 / (a + b) + """ + data = np.load( + Path(__file__).parent / "data/jf_skew_t_gamlss_pdf_data.npy" + ) + return np.rec.fromarrays(data, names="x,pdf,a,b") + + @pytest.mark.parametrize("a,b", [(2, 3), (8, 4), (12, 13)]) + def test_compare_with_gamlss_r(self, gamlss_pdf_data, a, b): + """Compare the pdf with a table of reference values. The table of + reference values was produced using R, where the Jones and Faddy skew + t distribution is available in the GAMLSS package as `ST5`. + """ + data = gamlss_pdf_data[ + (gamlss_pdf_data["a"] == a) & (gamlss_pdf_data["b"] == b) + ] + x, pdf = data["x"], data["pdf"] + assert_allclose(pdf, stats.jf_skew_t(a, b).pdf(x), rtol=1e-12) + +# Test data for TestSkewNorm.test_noncentral_moments() +# The expected noncentral moments were computed by Wolfram Alpha. +# In Wolfram Alpha, enter +# SkewNormalDistribution[0, 1, a] moment +# with `a` replaced by the desired shape parameter. In the results, there +# should be a table of the first four moments. Click on "More" to get more +# moments. The expected moments start with the first moment (order = 1). +_skewnorm_noncentral_moments = [ + (2, [2*np.sqrt(2/(5*np.pi)), + 1, + 22/5*np.sqrt(2/(5*np.pi)), + 3, + 446/25*np.sqrt(2/(5*np.pi)), + 15, + 2682/25*np.sqrt(2/(5*np.pi)), + 105, + 107322/125*np.sqrt(2/(5*np.pi))]), + (0.1, [np.sqrt(2/(101*np.pi)), + 1, + 302/101*np.sqrt(2/(101*np.pi)), + 3, + (152008*np.sqrt(2/(101*np.pi)))/10201, + 15, + (107116848*np.sqrt(2/(101*np.pi)))/1030301, + 105, + (97050413184*np.sqrt(2/(101*np.pi)))/104060401]), + (-3, [-3/np.sqrt(5*np.pi), + 1, + -63/(10*np.sqrt(5*np.pi)), + 3, + -2529/(100*np.sqrt(5*np.pi)), + 15, + -30357/(200*np.sqrt(5*np.pi)), + 105, + -2428623/(2000*np.sqrt(5*np.pi)), + 945, + -242862867/(20000*np.sqrt(5*np.pi)), + 10395, + -29143550277/(200000*np.sqrt(5*np.pi)), + 135135]), +] + + +class TestSkewNorm: + def setup_method(self): + self.rng = check_random_state(1234) + + def test_normal(self): + # When the skewness is 0 the distribution is normal + x = np.linspace(-5, 5, 100) + assert_array_almost_equal(stats.skewnorm.pdf(x, a=0), + stats.norm.pdf(x)) + + def test_rvs(self): + shape = (3, 4, 5) + x = stats.skewnorm.rvs(a=0.75, size=shape, random_state=self.rng) + assert_equal(shape, x.shape) + + x = stats.skewnorm.rvs(a=-3, size=shape, random_state=self.rng) + assert_equal(shape, x.shape) + + def test_moments(self): + X = stats.skewnorm.rvs(a=4, size=int(1e6), loc=5, scale=2, + random_state=self.rng) + expected = [np.mean(X), np.var(X), stats.skew(X), stats.kurtosis(X)] + computed = stats.skewnorm.stats(a=4, loc=5, scale=2, moments='mvsk') + assert_array_almost_equal(computed, expected, decimal=2) + + X = stats.skewnorm.rvs(a=-4, size=int(1e6), loc=5, scale=2, + random_state=self.rng) + expected = [np.mean(X), np.var(X), stats.skew(X), stats.kurtosis(X)] + computed = stats.skewnorm.stats(a=-4, loc=5, scale=2, moments='mvsk') + assert_array_almost_equal(computed, expected, decimal=2) + + def test_pdf_large_x(self): + # Triples are [x, a, logpdf(x, a)]. These values were computed + # using Log[PDF[SkewNormalDistribution[0, 1, a], x]] in Wolfram Alpha. + logpdfvals = [ + [40, -1, -1604.834233366398515598970], + [40, -1/2, -1004.142946723741991369168], + [40, 0, -800.9189385332046727417803], + [40, 1/2, -800.2257913526447274323631], + [-40, -1/2, -800.2257913526447274323631], + [-2, 1e7, -2.000000000000199559727173e14], + [2, -1e7, -2.000000000000199559727173e14], + ] + for x, a, logpdfval in logpdfvals: + logp = stats.skewnorm.logpdf(x, a) + assert_allclose(logp, logpdfval, rtol=1e-8) + + def test_cdf_large_x(self): + # Regression test for gh-7746. + # The x values are large enough that the closest 64 bit floating + # point representation of the exact CDF is 1.0. + p = stats.skewnorm.cdf([10, 20, 30], -1) + assert_allclose(p, np.ones(3), rtol=1e-14) + p = stats.skewnorm.cdf(25, 2.5) + assert_allclose(p, 1.0, rtol=1e-14) + + def test_cdf_sf_small_values(self): + # Triples are [x, a, cdf(x, a)]. These values were computed + # using CDF[SkewNormalDistribution[0, 1, a], x] in Wolfram Alpha. + cdfvals = [ + [-8, 1, 3.870035046664392611e-31], + [-4, 2, 8.1298399188811398e-21], + [-2, 5, 1.55326826787106273e-26], + [-9, -1, 2.257176811907681295e-19], + [-10, -4, 1.523970604832105213e-23], + ] + for x, a, cdfval in cdfvals: + p = stats.skewnorm.cdf(x, a) + assert_allclose(p, cdfval, rtol=1e-8) + # For the skew normal distribution, sf(-x, -a) = cdf(x, a). + p = stats.skewnorm.sf(-x, -a) + assert_allclose(p, cdfval, rtol=1e-8) + + @pytest.mark.parametrize('a, moments', _skewnorm_noncentral_moments) + def test_noncentral_moments(self, a, moments): + for order, expected in enumerate(moments, start=1): + mom = stats.skewnorm.moment(order, a) + assert_allclose(mom, expected, rtol=1e-14) + + def test_fit(self): + rng = np.random.default_rng(4609813989115202851) + + a, loc, scale = -2, 3.5, 0.5 # arbitrary, valid parameters + dist = stats.skewnorm(a, loc, scale) + rvs = dist.rvs(size=100, random_state=rng) + + # test that MLE still honors guesses and fixed parameters + a2, loc2, scale2 = stats.skewnorm.fit(rvs, -1.5, floc=3) + a3, loc3, scale3 = stats.skewnorm.fit(rvs, -1.6, floc=3) + assert loc2 == loc3 == 3 # fixed parameter is respected + assert a2 != a3 # different guess -> (slightly) different outcome + # quality of fit is tested elsewhere + + # test that MoM honors fixed parameters, accepts (but ignores) guesses + a4, loc4, scale4 = stats.skewnorm.fit(rvs, 3, fscale=3, method='mm') + assert scale4 == 3 + # because scale was fixed, only the mean and skewness will be matched + dist4 = stats.skewnorm(a4, loc4, scale4) + res = dist4.stats(moments='ms') + ref = np.mean(rvs), stats.skew(rvs) + assert_allclose(res, ref) + + # Test behavior when skew of data is beyond maximum of skewnorm + rvs2 = stats.pareto.rvs(1, size=100, random_state=rng) + + # MLE still works + res = stats.skewnorm.fit(rvs2) + assert np.all(np.isfinite(res)) + + # MoM fits variance and skewness + a5, loc5, scale5 = stats.skewnorm.fit(rvs2, method='mm') + assert np.isinf(a5) + # distribution infrastruction doesn't allow infinite shape parameters + # into _stats; it just bypasses it and produces NaNs. Calculate + # moments manually. + m, v = np.mean(rvs2), np.var(rvs2) + assert_allclose(m, loc5 + scale5 * np.sqrt(2/np.pi)) + assert_allclose(v, scale5**2 * (1 - 2 / np.pi)) + + # test that MLE and MoM behave as expected under sign changes + a6p, loc6p, scale6p = stats.skewnorm.fit(rvs, method='mle') + a6m, loc6m, scale6m = stats.skewnorm.fit(-rvs, method='mle') + assert_allclose([a6m, loc6m, scale6m], [-a6p, -loc6p, scale6p]) + a7p, loc7p, scale7p = stats.skewnorm.fit(rvs, method='mm') + a7m, loc7m, scale7m = stats.skewnorm.fit(-rvs, method='mm') + assert_allclose([a7m, loc7m, scale7m], [-a7p, -loc7p, scale7p]) + + def test_fit_gh19332(self): + # When the skewness of the data was high, `skewnorm.fit` fell back on + # generic `fit` behavior with a bad guess of the skewness parameter. + # Test that this is improved; `skewnorm.fit` is now better at finding + # the global optimum when the sample is highly skewed. See gh-19332. + x = np.array([-5, -1, 1 / 100_000] + 12 * [1] + [5]) + + params = stats.skewnorm.fit(x) + res = stats.skewnorm.nnlf(params, x) + + # Compare overridden fit against generic fit. + # res should be about 32.01, and generic fit is worse at 32.64. + # In case the generic fit improves, remove this assertion (see gh-19333). + params_super = stats.skewnorm.fit(x, superfit=True) + ref = stats.skewnorm.nnlf(params_super, x) + assert res < ref - 0.5 + + # Compare overridden fit against stats.fit + rng = np.random.default_rng(9842356982345693637) + bounds = {'a': (-5, 5), 'loc': (-10, 10), 'scale': (1e-16, 10)} + def optimizer(fun, bounds): + return differential_evolution(fun, bounds, seed=rng) + + fit_result = stats.fit(stats.skewnorm, x, bounds, optimizer=optimizer) + np.testing.assert_allclose(params, fit_result.params, rtol=1e-4) + + +class TestExpon: + def test_zero(self): + assert_equal(stats.expon.pdf(0), 1) + + def test_tail(self): # Regression test for ticket 807 + assert_equal(stats.expon.cdf(1e-18), 1e-18) + assert_equal(stats.expon.isf(stats.expon.sf(40)), 40) + + def test_nan_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.nan]) + assert_raises(ValueError, stats.expon.fit, x) + + def test_inf_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.inf]) + assert_raises(ValueError, stats.expon.fit, x) + + +class TestNorm: + def test_nan_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.nan]) + assert_raises(ValueError, stats.norm.fit, x) + + def test_inf_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.inf]) + assert_raises(ValueError, stats.norm.fit, x) + + def test_bad_keyword_arg(self): + x = [1, 2, 3] + assert_raises(TypeError, stats.norm.fit, x, plate="shrimp") + + @pytest.mark.parametrize('loc', [0, 1]) + def test_delta_cdf(self, loc): + # The expected value is computed with mpmath: + # >>> import mpmath + # >>> mpmath.mp.dps = 60 + # >>> float(mpmath.ncdf(12) - mpmath.ncdf(11)) + # 1.910641809677555e-28 + expected = 1.910641809677555e-28 + delta = stats.norm._delta_cdf(11+loc, 12+loc, loc=loc) + assert_allclose(delta, expected, rtol=1e-13) + delta = stats.norm._delta_cdf(-(12+loc), -(11+loc), loc=-loc) + assert_allclose(delta, expected, rtol=1e-13) + + +class TestUniform: + """gh-10300""" + def test_nan_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.nan]) + assert_raises(ValueError, stats.uniform.fit, x) + + def test_inf_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.inf]) + assert_raises(ValueError, stats.uniform.fit, x) + + +class TestExponNorm: + def test_moments(self): + # Some moment test cases based on non-loc/scaled formula + def get_moms(lam, sig, mu): + # See wikipedia for these formulae + # where it is listed as an exponentially modified gaussian + opK2 = 1.0 + 1 / (lam*sig)**2 + exp_skew = 2 / (lam * sig)**3 * opK2**(-1.5) + exp_kurt = 6.0 * (1 + (lam * sig)**2)**(-2) + return [mu + 1/lam, sig*sig + 1.0/(lam*lam), exp_skew, exp_kurt] + + mu, sig, lam = 0, 1, 1 + K = 1.0 / (lam * sig) + sts = stats.exponnorm.stats(K, loc=mu, scale=sig, moments='mvsk') + assert_almost_equal(sts, get_moms(lam, sig, mu)) + mu, sig, lam = -3, 2, 0.1 + K = 1.0 / (lam * sig) + sts = stats.exponnorm.stats(K, loc=mu, scale=sig, moments='mvsk') + assert_almost_equal(sts, get_moms(lam, sig, mu)) + mu, sig, lam = 0, 3, 1 + K = 1.0 / (lam * sig) + sts = stats.exponnorm.stats(K, loc=mu, scale=sig, moments='mvsk') + assert_almost_equal(sts, get_moms(lam, sig, mu)) + mu, sig, lam = -5, 11, 3.5 + K = 1.0 / (lam * sig) + sts = stats.exponnorm.stats(K, loc=mu, scale=sig, moments='mvsk') + assert_almost_equal(sts, get_moms(lam, sig, mu)) + + def test_nan_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.nan]) + assert_raises(ValueError, stats.exponnorm.fit, x, floc=0, fscale=1) + + def test_inf_raises_error(self): + # see gh-issue 10300 + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.inf]) + assert_raises(ValueError, stats.exponnorm.fit, x, floc=0, fscale=1) + + def test_extremes_x(self): + # Test for extreme values against overflows + assert_almost_equal(stats.exponnorm.pdf(-900, 1), 0.0) + assert_almost_equal(stats.exponnorm.pdf(+900, 1), 0.0) + assert_almost_equal(stats.exponnorm.pdf(-900, 0.01), 0.0) + assert_almost_equal(stats.exponnorm.pdf(+900, 0.01), 0.0) + + # Expected values for the PDF were computed with mpmath, with + # the following function, and with mpmath.mp.dps = 50. + # + # def exponnorm_stdpdf(x, K): + # x = mpmath.mpf(x) + # K = mpmath.mpf(K) + # t1 = mpmath.exp(1/(2*K**2) - x/K) + # erfcarg = -(x - 1/K)/mpmath.sqrt(2) + # t2 = mpmath.erfc(erfcarg) + # return t1 * t2 / (2*K) + # + @pytest.mark.parametrize('x, K, expected', + [(20, 0.01, 6.90010764753618e-88), + (1, 0.01, 0.24438994313247364), + (-1, 0.01, 0.23955149623472075), + (-20, 0.01, 4.6004708690125477e-88), + (10, 1, 7.48518298877006e-05), + (10, 10000, 9.990005048283775e-05)]) + def test_std_pdf(self, x, K, expected): + assert_allclose(stats.exponnorm.pdf(x, K), expected, rtol=5e-12) + + # Expected values for the CDF were computed with mpmath using + # the following function and with mpmath.mp.dps = 60: + # + # def mp_exponnorm_cdf(x, K, loc=0, scale=1): + # x = mpmath.mpf(x) + # K = mpmath.mpf(K) + # loc = mpmath.mpf(loc) + # scale = mpmath.mpf(scale) + # z = (x - loc)/scale + # return (mpmath.ncdf(z) + # - mpmath.exp((1/(2*K) - z)/K)*mpmath.ncdf(z - 1/K)) + # + @pytest.mark.parametrize('x, K, scale, expected', + [[0, 0.01, 1, 0.4960109760186432], + [-5, 0.005, 1, 2.7939945412195734e-07], + [-1e4, 0.01, 100, 0.0], + [-1e4, 0.01, 1000, 6.920401854427357e-24], + [5, 0.001, 1, 0.9999997118542392]]) + def test_cdf_small_K(self, x, K, scale, expected): + p = stats.exponnorm.cdf(x, K, scale=scale) + if expected == 0.0: + assert p == 0.0 + else: + assert_allclose(p, expected, rtol=1e-13) + + # Expected values for the SF were computed with mpmath using + # the following function and with mpmath.mp.dps = 60: + # + # def mp_exponnorm_sf(x, K, loc=0, scale=1): + # x = mpmath.mpf(x) + # K = mpmath.mpf(K) + # loc = mpmath.mpf(loc) + # scale = mpmath.mpf(scale) + # z = (x - loc)/scale + # return (mpmath.ncdf(-z) + # + mpmath.exp((1/(2*K) - z)/K)*mpmath.ncdf(z - 1/K)) + # + @pytest.mark.parametrize('x, K, scale, expected', + [[10, 0.01, 1, 8.474702916146657e-24], + [2, 0.005, 1, 0.02302280664231312], + [5, 0.005, 0.5, 8.024820681931086e-24], + [10, 0.005, 0.5, 3.0603340062892486e-89], + [20, 0.005, 0.5, 0.0], + [-3, 0.001, 1, 0.9986545205566117]]) + def test_sf_small_K(self, x, K, scale, expected): + p = stats.exponnorm.sf(x, K, scale=scale) + if expected == 0.0: + assert p == 0.0 + else: + assert_allclose(p, expected, rtol=5e-13) + + +class TestGenExpon: + def test_pdf_unity_area(self): + from scipy.integrate import simpson + # PDF should integrate to one + p = stats.genexpon.pdf(numpy.arange(0, 10, 0.01), 0.5, 0.5, 2.0) + assert_almost_equal(simpson(p, dx=0.01), 1, 1) + + def test_cdf_bounds(self): + # CDF should always be positive + cdf = stats.genexpon.cdf(numpy.arange(0, 10, 0.01), 0.5, 0.5, 2.0) + assert_(numpy.all((0 <= cdf) & (cdf <= 1))) + + # The values of p in the following data were computed with mpmath. + # E.g. the script + # from mpmath import mp + # mp.dps = 80 + # x = mp.mpf('15.0') + # a = mp.mpf('1.0') + # b = mp.mpf('2.0') + # c = mp.mpf('1.5') + # print(float(mp.exp((-a-b)*x + (b/c)*-mp.expm1(-c*x)))) + # prints + # 1.0859444834514553e-19 + @pytest.mark.parametrize('x, p, a, b, c', + [(15, 1.0859444834514553e-19, 1, 2, 1.5), + (0.25, 0.7609068232534623, 0.5, 2, 3), + (0.25, 0.09026661397565876, 9.5, 2, 0.5), + (0.01, 0.9753038265071597, 2.5, 0.25, 0.5), + (3.25, 0.0001962824553094492, 2.5, 0.25, 0.5), + (0.125, 0.9508674287164001, 0.25, 5, 0.5)]) + def test_sf_isf(self, x, p, a, b, c): + sf = stats.genexpon.sf(x, a, b, c) + assert_allclose(sf, p, rtol=2e-14) + isf = stats.genexpon.isf(p, a, b, c) + assert_allclose(isf, x, rtol=2e-14) + + # The values of p in the following data were computed with mpmath. + @pytest.mark.parametrize('x, p, a, b, c', + [(0.25, 0.2390931767465377, 0.5, 2, 3), + (0.25, 0.9097333860243412, 9.5, 2, 0.5), + (0.01, 0.0246961734928403, 2.5, 0.25, 0.5), + (3.25, 0.9998037175446906, 2.5, 0.25, 0.5), + (0.125, 0.04913257128359998, 0.25, 5, 0.5)]) + def test_cdf_ppf(self, x, p, a, b, c): + cdf = stats.genexpon.cdf(x, a, b, c) + assert_allclose(cdf, p, rtol=2e-14) + ppf = stats.genexpon.ppf(p, a, b, c) + assert_allclose(ppf, x, rtol=2e-14) + + +class TestTruncexpon: + + def test_sf_isf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 50; TruncExpon(b=b).sf(x) + b = [20, 100] + x = [19.999999, 99.999999] + ref = [2.0611546593828472e-15, 3.7200778266671455e-50] + assert_allclose(stats.truncexpon.sf(x, b), ref, rtol=1.5e-10) + assert_allclose(stats.truncexpon.isf(ref, b), x, rtol=1e-12) + + +class TestExponpow: + def test_tail(self): + assert_almost_equal(stats.exponpow.cdf(1e-10, 2.), 1e-20) + assert_almost_equal(stats.exponpow.isf(stats.exponpow.sf(5, .8), .8), + 5) + + +class TestSkellam: + def test_pmf(self): + # comparison to R + k = numpy.arange(-10, 15) + mu1, mu2 = 10, 5 + skpmfR = numpy.array( + [4.2254582961926893e-005, 1.1404838449648488e-004, + 2.8979625801752660e-004, 6.9177078182101231e-004, + 1.5480716105844708e-003, 3.2412274963433889e-003, + 6.3373707175123292e-003, 1.1552351566696643e-002, + 1.9606152375042644e-002, 3.0947164083410337e-002, + 4.5401737566767360e-002, 6.1894328166820688e-002, + 7.8424609500170578e-002, 9.2418812533573133e-002, + 1.0139793148019728e-001, 1.0371927988298846e-001, + 9.9076583077406091e-002, 8.8546660073089561e-002, + 7.4187842052486810e-002, 5.8392772862200251e-002, + 4.3268692953013159e-002, 3.0248159818374226e-002, + 1.9991434305603021e-002, 1.2516877303301180e-002, + 7.4389876226229707e-003]) + + assert_almost_equal(stats.skellam.pmf(k, mu1, mu2), skpmfR, decimal=15) + + def test_cdf(self): + # comparison to R, only 5 decimals + k = numpy.arange(-10, 15) + mu1, mu2 = 10, 5 + skcdfR = numpy.array( + [6.4061475386192104e-005, 1.7810985988267694e-004, + 4.6790611790020336e-004, 1.1596768997212152e-003, + 2.7077485103056847e-003, 5.9489760066490718e-003, + 1.2286346724161398e-002, 2.3838698290858034e-002, + 4.3444850665900668e-002, 7.4392014749310995e-002, + 1.1979375231607835e-001, 1.8168808048289900e-001, + 2.6011268998306952e-001, 3.5253150251664261e-001, + 4.5392943399683988e-001, 5.5764871387982828e-001, + 6.5672529695723436e-001, 7.4527195703032389e-001, + 8.1945979908281064e-001, 8.7785257194501087e-001, + 9.2112126489802404e-001, 9.5136942471639818e-001, + 9.7136085902200120e-001, 9.8387773632530240e-001, + 9.9131672394792536e-001]) + + assert_almost_equal(stats.skellam.cdf(k, mu1, mu2), skcdfR, decimal=5) + + def test_extreme_mu2(self): + # check that crash reported by gh-17916 large mu2 is resolved + x, mu1, mu2 = 0, 1, 4820232647677555.0 + assert_allclose(stats.skellam.pmf(x, mu1, mu2), 0, atol=1e-16) + assert_allclose(stats.skellam.cdf(x, mu1, mu2), 1, atol=1e-16) + + +class TestLognorm: + def test_pdf(self): + # Regression test for Ticket #1471: avoid nan with 0/0 situation + # Also make sure there are no warnings at x=0, cf gh-5202 + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + pdf = stats.lognorm.pdf([0, 0.5, 1], 1) + assert_array_almost_equal(pdf, [0.0, 0.62749608, 0.39894228]) + + def test_logcdf(self): + # Regression test for gh-5940: sf et al would underflow too early + x2, mu, sigma = 201.68, 195, 0.149 + assert_allclose(stats.lognorm.sf(x2-mu, s=sigma), + stats.norm.sf(np.log(x2-mu)/sigma)) + assert_allclose(stats.lognorm.logsf(x2-mu, s=sigma), + stats.norm.logsf(np.log(x2-mu)/sigma)) + + @pytest.fixture(scope='function') + def rng(self): + return np.random.default_rng(1234) + + @pytest.mark.parametrize("rvs_shape", [.1, 2]) + @pytest.mark.parametrize("rvs_loc", [-2, 0, 2]) + @pytest.mark.parametrize("rvs_scale", [.2, 1, 5]) + @pytest.mark.parametrize('fix_shape, fix_loc, fix_scale', + [e for e in product((False, True), repeat=3) + if False in e]) + @np.errstate(invalid="ignore") + def test_fit_MLE_comp_optimizer(self, rvs_shape, rvs_loc, rvs_scale, + fix_shape, fix_loc, fix_scale, rng): + data = stats.lognorm.rvs(size=100, s=rvs_shape, scale=rvs_scale, + loc=rvs_loc, random_state=rng) + + kwds = {} + if fix_shape: + kwds['f0'] = rvs_shape + if fix_loc: + kwds['floc'] = rvs_loc + if fix_scale: + kwds['fscale'] = rvs_scale + + # Numerical result may equal analytical result if some code path + # of the analytical routine makes use of numerical optimization. + _assert_less_or_close_loglike(stats.lognorm, data, **kwds, + maybe_identical=True) + + def test_isf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 100; + # LogNormal(s=s).isf(q=0.1, guess=0) + # LogNormal(s=s).isf(q=2e-10, guess=100) + s = 0.954 + q = [0.1, 2e-10, 5e-20, 6e-40] + ref = [3.3960065375794937, 390.07632793595974, 5830.5020828128445, + 287872.84087457904] + assert_allclose(stats.lognorm.isf(q, s), ref, rtol=1e-14) + + +class TestBeta: + def test_logpdf(self): + # Regression test for Ticket #1326: avoid nan with 0*log(0) situation + logpdf = stats.beta.logpdf(0, 1, 0.5) + assert_almost_equal(logpdf, -0.69314718056) + logpdf = stats.beta.logpdf(0, 0.5, 1) + assert_almost_equal(logpdf, np.inf) + + def test_logpdf_ticket_1866(self): + alpha, beta = 267, 1472 + x = np.array([0.2, 0.5, 0.6]) + b = stats.beta(alpha, beta) + assert_allclose(b.logpdf(x).sum(), -1201.699061824062) + assert_allclose(b.pdf(x), np.exp(b.logpdf(x))) + + def test_fit_bad_keyword_args(self): + x = [0.1, 0.5, 0.6] + assert_raises(TypeError, stats.beta.fit, x, floc=0, fscale=1, + plate="shrimp") + + def test_fit_duplicated_fixed_parameter(self): + # At most one of 'f0', 'fa' or 'fix_a' can be given to the fit method. + # More than one raises a ValueError. + x = [0.1, 0.5, 0.6] + assert_raises(ValueError, stats.beta.fit, x, fa=0.5, fix_a=0.5) + + @pytest.mark.skipif(MACOS_INTEL, reason="Overflow, see gh-14901") + def test_issue_12635(self): + # Confirm that Boost's beta distribution resolves gh-12635. + # Check against R: + # options(digits=16) + # p = 0.9999999999997369 + # a = 75.0 + # b = 66334470.0 + # print(qbeta(p, a, b)) + p, a, b = 0.9999999999997369, 75.0, 66334470.0 + assert_allclose(stats.beta.ppf(p, a, b), 2.343620802982393e-06) + + @pytest.mark.skipif(MACOS_INTEL, reason="Overflow, see gh-14901") + def test_issue_12794(self): + # Confirm that Boost's beta distribution resolves gh-12794. + # Check against R. + # options(digits=16) + # p = 1e-11 + # count_list = c(10,100,1000) + # print(qbeta(1-p, count_list + 1, 100000 - count_list)) + inv_R = np.array([0.0004944464889611935, + 0.0018360586912635726, + 0.0122663919942518351]) + count_list = np.array([10, 100, 1000]) + p = 1e-11 + inv = stats.beta.isf(p, count_list + 1, 100000 - count_list) + assert_allclose(inv, inv_R) + res = stats.beta.sf(inv, count_list + 1, 100000 - count_list) + assert_allclose(res, p) + + @pytest.mark.skipif(MACOS_INTEL, reason="Overflow, see gh-14901") + def test_issue_12796(self): + # Confirm that Boost's beta distribution succeeds in the case + # of gh-12796 + alpha_2 = 5e-6 + count_ = np.arange(1, 20) + nobs = 100000 + q, a, b = 1 - alpha_2, count_ + 1, nobs - count_ + inv = stats.beta.ppf(q, a, b) + res = stats.beta.cdf(inv, a, b) + assert_allclose(res, 1 - alpha_2) + + def test_endpoints(self): + # Confirm that boost's beta distribution returns inf at x=1 + # when b<1 + a, b = 1, 0.5 + assert_equal(stats.beta.pdf(1, a, b), np.inf) + + # Confirm that boost's beta distribution returns inf at x=0 + # when a<1 + a, b = 0.2, 3 + assert_equal(stats.beta.pdf(0, a, b), np.inf) + + # Confirm that boost's beta distribution returns 5 at x=0 + # when a=1, b=5 + a, b = 1, 5 + assert_equal(stats.beta.pdf(0, a, b), 5) + assert_equal(stats.beta.pdf(1e-310, a, b), 5) + + # Confirm that boost's beta distribution returns 5 at x=1 + # when a=5, b=1 + a, b = 5, 1 + assert_equal(stats.beta.pdf(1, a, b), 5) + assert_equal(stats.beta.pdf(1-1e-310, a, b), 5) + + @pytest.mark.xfail(reason="Does not warn on special codepath") + def test_boost_eval_issue_14606(self): + q, a, b = 0.995, 1.0e11, 1.0e13 + with pytest.warns(RuntimeWarning): + stats.beta.ppf(q, a, b) + + @pytest.mark.parametrize('method', [stats.beta.ppf, stats.beta.isf]) + @pytest.mark.parametrize('a, b', [(1e-310, 12.5), (12.5, 1e-310)]) + def test_beta_ppf_with_subnormal_a_b(self, method, a, b): + # Regression test for gh-17444: beta.ppf(p, a, b) and beta.isf(p, a, b) + # would result in a segmentation fault if either a or b was subnormal. + p = 0.9 + # Depending on the version of Boost that we have vendored and + # our setting of the Boost double promotion policy, the call + # `stats.beta.ppf(p, a, b)` might raise an OverflowError or + # return a value. We'll accept either behavior (and not care about + # the value), because our goal here is to verify that the call does + # not trigger a segmentation fault. + try: + method(p, a, b) + except OverflowError: + # The OverflowError exception occurs with Boost 1.80 or earlier + # when Boost's double promotion policy is false; see + # https://github.com/boostorg/math/issues/882 + # and + # https://github.com/boostorg/math/pull/883 + # Once we have vendored the fixed version of Boost, we can drop + # this try-except wrapper and just call the function. + pass + + # entropy accuracy was confirmed using the following mpmath function + # from mpmath import mp + # mp.dps = 50 + # def beta_entropy_mpmath(a, b): + # a = mp.mpf(a) + # b = mp.mpf(b) + # entropy = mp.log(mp.beta(a, b)) - (a - 1) * mp.digamma(a) -\ + # (b - 1) * mp.digamma(b) + (a + b -2) * mp.digamma(a + b) + # return float(entropy) + + @pytest.mark.parametrize('a, b, ref', + [(0.5, 0.5, -0.24156447527049044), + (0.001, 1, -992.0922447210179), + (1, 10000, -8.210440371976183), + (100000, 100000, -5.377247470132859)]) + def test_entropy(self, a, b, ref): + assert_allclose(stats.beta(a, b).entropy(), ref) + + @pytest.mark.parametrize( + "a, b, ref, tol", + [ + (1, 10, -1.4025850929940458, 1e-14), + (10, 20, -1.0567887388936708, 1e-13), + (4e6, 4e6+20, -7.221686009678741, 1e-9), + (5e6, 5e6+10, -7.333257022834638, 1e-8), + (1e10, 1e10+20, -11.133707703130474, 1e-11), + (1e50, 1e50+20, -57.185409562486385, 1e-15), + (2, 1e10, -21.448635265288925, 1e-11), + (2, 1e20, -44.47448619497938, 1e-14), + (2, 1e50, -113.55203898480075, 1e-14), + (5, 1e10, -20.87226777401971, 1e-10), + (5, 1e20, -43.89811870326017, 1e-14), + (5, 1e50, -112.97567149308153, 1e-14), + (10, 1e10, -20.489796752909477, 1e-9), + (10, 1e20, -43.51564768139993, 1e-14), + (10, 1e50, -112.59320047122131, 1e-14), + (1e20, 2, -44.47448619497938, 1e-14), + (1e20, 5, -43.89811870326017, 1e-14), + (1e50, 10, -112.59320047122131, 1e-14), + ] + ) + def test_extreme_entropy(self, a, b, ref, tol): + # Reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + # + # def beta_entropy_mpmath(a, b): + # a = mp.mpf(a) + # b = mp.mpf(b) + # entropy = ( + # mp.log(mp.beta(a, b)) - (a - 1) * mp.digamma(a) + # - (b - 1) * mp.digamma(b) + (a + b - 2) * mp.digamma(a + b) + # ) + # return float(entropy) + assert_allclose(stats.beta(a, b).entropy(), ref, rtol=tol) + + +class TestBetaPrime: + # the test values are used in test_cdf_gh_17631 / test_ppf_gh_17631 + # They are computed with mpmath. Example: + # from mpmath import mp + # mp.dps = 50 + # a, b = mp.mpf(0.05), mp.mpf(0.1) + # x = mp.mpf(1e22) + # float(mp.betainc(a, b, 0.0, x/(1+x), regularized=True)) + # note: we use the values computed by the cdf to test whether + # ppf(cdf(x)) == x (up to a small tolerance) + # since the ppf can be very sensitive to small variations of the input, + # it can be required to generate the test case for the ppf separately, + # see self.test_ppf + cdf_vals = [ + (1e22, 100.0, 0.05, 0.8973027435427167), + (1e10, 100.0, 0.05, 0.5911548582766262), + (1e8, 0.05, 0.1, 0.9467768090820048), + (1e8, 100.0, 0.05, 0.4852944858726726), + (1e-10, 0.05, 0.1, 0.21238845427095), + (1e-10, 1.5, 1.5, 1.697652726007973e-15), + (1e-10, 0.05, 100.0, 0.40884514172337383), + (1e-22, 0.05, 0.1, 0.053349567649287326), + (1e-22, 1.5, 1.5, 1.6976527263135503e-33), + (1e-22, 0.05, 100.0, 0.10269725645728331), + (1e-100, 0.05, 0.1, 6.7163126421919795e-06), + (1e-100, 1.5, 1.5, 1.6976527263135503e-150), + (1e-100, 0.05, 100.0, 1.2928818587561651e-05), + ] + + def test_logpdf(self): + alpha, beta = 267, 1472 + x = np.array([0.2, 0.5, 0.6]) + b = stats.betaprime(alpha, beta) + assert_(np.isfinite(b.logpdf(x)).all()) + assert_allclose(b.pdf(x), np.exp(b.logpdf(x))) + + def test_cdf(self): + # regression test for gh-4030: Implementation of + # scipy.stats.betaprime.cdf() + x = stats.betaprime.cdf(0, 0.2, 0.3) + assert_equal(x, 0.0) + + alpha, beta = 267, 1472 + x = np.array([0.2, 0.5, 0.6]) + cdfs = stats.betaprime.cdf(x, alpha, beta) + assert_(np.isfinite(cdfs).all()) + + # check the new cdf implementation vs generic one: + gen_cdf = stats.rv_continuous._cdf_single + cdfs_g = [gen_cdf(stats.betaprime, val, alpha, beta) for val in x] + assert_allclose(cdfs, cdfs_g, atol=0, rtol=2e-12) + + # The expected values for test_ppf() were computed with mpmath, e.g. + # + # from mpmath import mp + # mp.dps = 125 + # p = 0.01 + # a, b = 1.25, 2.5 + # x = mp.findroot(lambda t: mp.betainc(a, b, x1=0, x2=t/(1+t), + # regularized=True) - p, + # x0=(0.01, 0.011), method='secant') + # print(float(x)) + # + # prints + # + # 0.01080162700956614 + # + @pytest.mark.parametrize( + 'p, a, b, expected', + [(0.010, 1.25, 2.5, 0.01080162700956614), + (1e-12, 1.25, 2.5, 1.0610141996279122e-10), + (1e-18, 1.25, 2.5, 1.6815941817974941e-15), + (1e-17, 0.25, 7.0, 1.0179194531881782e-69), + (0.375, 0.25, 7.0, 0.002036820346115211), + (0.9978811466052919, 0.05, 0.1, 1.0000000000001218e22),] + ) + def test_ppf(self, p, a, b, expected): + x = stats.betaprime.ppf(p, a, b) + assert_allclose(x, expected, rtol=1e-14) + + @pytest.mark.parametrize('x, a, b, p', cdf_vals) + def test_ppf_gh_17631(self, x, a, b, p): + assert_allclose(stats.betaprime.ppf(p, a, b), x, rtol=1e-14) + + @pytest.mark.parametrize( + 'x, a, b, expected', + cdf_vals + [ + (1e10, 1.5, 1.5, 0.9999999999999983), + (1e10, 0.05, 0.1, 0.9664184367890859), + (1e22, 0.05, 0.1, 0.9978811466052919), + ]) + def test_cdf_gh_17631(self, x, a, b, expected): + assert_allclose(stats.betaprime.cdf(x, a, b), expected, rtol=1e-14) + + @pytest.mark.parametrize( + 'x, a, b, expected', + [(1e50, 0.05, 0.1, 0.9999966641709545), + (1e50, 100.0, 0.05, 0.995925162631006)]) + def test_cdf_extreme_tails(self, x, a, b, expected): + # for even more extreme values, we only get a few correct digits + # results are still < 1 + y = stats.betaprime.cdf(x, a, b) + assert y < 1.0 + assert_allclose(y, expected, rtol=2e-5) + + def test_sf(self): + # reference values were computed via the reference distribution, + # e.g. + # mp.dps = 50 + # a, b = 5, 3 + # x = 1e10 + # BetaPrime(a=a, b=b).sf(x); returns 3.4999999979e-29 + a = [5, 4, 2, 0.05, 0.05, 0.05, 0.05, 100.0, 100.0, 0.05, 0.05, + 0.05, 1.5, 1.5] + b = [3, 2, 1, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 100.0, 100.0, + 100.0, 1.5, 1.5] + x = [1e10, 1e20, 1e30, 1e22, 1e-10, 1e-22, 1e-100, 1e22, 1e10, + 1e-10, 1e-22, 1e-100, 1e10, 1e-10] + ref = [3.4999999979e-29, 9.999999999994357e-40, 1.9999999999999998e-30, + 0.0021188533947081017, 0.78761154572905, 0.9466504323507127, + 0.9999932836873578, 0.10269725645728331, 0.40884514172337383, + 0.5911548582766262, 0.8973027435427167, 0.9999870711814124, + 1.6976527260079727e-15, 0.9999999999999983] + sf_values = stats.betaprime.sf(x, a, b) + assert_allclose(sf_values, ref, rtol=1e-12) + + def test_fit_stats_gh18274(self): + # gh-18274 reported spurious warning emitted when fitting `betaprime` + # to data. Some of these were emitted by stats, too. Check that the + # warnings are no longer emitted. + stats.betaprime.fit([0.1, 0.25, 0.3, 1.2, 1.6], floc=0, fscale=1) + stats.betaprime(a=1, b=1).stats('mvsk') + + def test_moment_gh18634(self): + # Testing for gh-18634 revealed that `betaprime` raised a + # NotImplementedError for higher moments. Check that this is + # resolved. Parameters are arbitrary but lie on either side of the + # moment order (5) to test both branches of `_lazywhere`. Reference + # values produced with Mathematica, e.g. + # `Moment[BetaPrimeDistribution[2,7],5]` + ref = [np.inf, 0.867096912929055] + res = stats.betaprime(2, [4.2, 7.1]).moment(5) + assert_allclose(res, ref) + + +class TestGamma: + def test_pdf(self): + # a few test cases to compare with R + pdf = stats.gamma.pdf(90, 394, scale=1./5) + assert_almost_equal(pdf, 0.002312341) + + pdf = stats.gamma.pdf(3, 10, scale=1./5) + assert_almost_equal(pdf, 0.1620358) + + def test_logpdf(self): + # Regression test for Ticket #1326: cornercase avoid nan with 0*log(0) + # situation + logpdf = stats.gamma.logpdf(0, 1) + assert_almost_equal(logpdf, 0) + + def test_fit_bad_keyword_args(self): + x = [0.1, 0.5, 0.6] + assert_raises(TypeError, stats.gamma.fit, x, floc=0, plate="shrimp") + + def test_isf(self): + # Test cases for when the probability is very small. See gh-13664. + # The expected values can be checked with mpmath. With mpmath, + # the survival function sf(x, k) can be computed as + # + # mpmath.gammainc(k, x, mpmath.inf, regularized=True) + # + # Here we have: + # + # >>> mpmath.mp.dps = 60 + # >>> float(mpmath.gammainc(1, 39.14394658089878, mpmath.inf, + # ... regularized=True)) + # 9.99999999999999e-18 + # >>> float(mpmath.gammainc(100, 330.6557590436547, mpmath.inf, + # regularized=True)) + # 1.000000000000028e-50 + # + assert np.isclose(stats.gamma.isf(1e-17, 1), + 39.14394658089878, atol=1e-14) + assert np.isclose(stats.gamma.isf(1e-50, 100), + 330.6557590436547, atol=1e-13) + + @pytest.mark.parametrize('scale', [1.0, 5.0]) + def test_delta_cdf(self, scale): + # Expected value computed with mpmath: + # + # >>> import mpmath + # >>> mpmath.mp.dps = 150 + # >>> cdf1 = mpmath.gammainc(3, 0, 245, regularized=True) + # >>> cdf2 = mpmath.gammainc(3, 0, 250, regularized=True) + # >>> float(cdf2 - cdf1) + # 1.1902609356171962e-102 + # + delta = stats.gamma._delta_cdf(scale*245, scale*250, 3, scale=scale) + assert_allclose(delta, 1.1902609356171962e-102, rtol=1e-13) + + @pytest.mark.parametrize('a, ref, rtol', + [(1e-4, -9990.366610819761, 1e-15), + (2, 1.5772156649015328, 1e-15), + (100, 3.7181819485047463, 1e-13), + (1e4, 6.024075385026086, 1e-15), + (1e18, 22.142204370151084, 1e-15), + (1e100, 116.54819318290696, 1e-15)]) + def test_entropy(self, a, ref, rtol): + # expected value computed with mpmath: + # from mpmath import mp + # mp.dps = 500 + # def gamma_entropy_reference(x): + # x = mp.mpf(x) + # return float(mp.digamma(x) * (mp.one - x) + x + mp.loggamma(x)) + + assert_allclose(stats.gamma.entropy(a), ref, rtol=rtol) + + @pytest.mark.parametrize("a", [1e-2, 1, 1e2]) + @pytest.mark.parametrize("loc", [1e-2, 0, 1e2]) + @pytest.mark.parametrize('scale', [1e-2, 1, 1e2]) + @pytest.mark.parametrize('fix_a', [True, False]) + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_scale', [True, False]) + def test_fit_mm(self, a, loc, scale, fix_a, fix_loc, fix_scale): + rng = np.random.default_rng(6762668991392531563) + data = stats.gamma.rvs(a, loc=loc, scale=scale, size=100, + random_state=rng) + + kwds = {} + if fix_a: + kwds['fa'] = a + if fix_loc: + kwds['floc'] = loc + if fix_scale: + kwds['fscale'] = scale + nfree = 3 - len(kwds) + + if nfree == 0: + error_msg = "All parameters fixed. There is nothing to optimize." + with pytest.raises(ValueError, match=error_msg): + stats.gamma.fit(data, method='mm', **kwds) + return + + theta = stats.gamma.fit(data, method='mm', **kwds) + dist = stats.gamma(*theta) + if nfree >= 1: + assert_allclose(dist.mean(), np.mean(data)) + if nfree >= 2: + assert_allclose(dist.moment(2), np.mean(data**2)) + if nfree >= 3: + assert_allclose(dist.moment(3), np.mean(data**3)) + +def test_pdf_overflow_gh19616(): + # Confirm that gh19616 (intermediate over/underflows in PDF) is resolved + # Reference value from R GeneralizedHyperbolic library + # library(GeneralizedHyperbolic) + # options(digits=16) + # jitter = 1e-3 + # dnig(1, a=2**0.5 / jitter**2, b=1 / jitter**2) + jitter = 1e-3 + Z = stats.norminvgauss(2**0.5 / jitter**2, 1 / jitter**2, loc=0, scale=1) + assert_allclose(Z.pdf(1.0), 282.0948446666433) + + +class TestDgamma: + def test_pdf(self): + rng = np.random.default_rng(3791303244302340058) + size = 10 # number of points to check + x = rng.normal(scale=10, size=size) + a = rng.uniform(high=10, size=size) + res = stats.dgamma.pdf(x, a) + ref = stats.gamma.pdf(np.abs(x), a) / 2 + assert_allclose(res, ref) + + dist = stats.dgamma(a) + # There was an intermittent failure with assert_equal on Linux - 32 bit + assert_allclose(dist.pdf(x), res, rtol=5e-16) + + # mpmath was used to compute the expected values. + # For x < 0, cdf(x, a) is mp.gammainc(a, -x, mp.inf, regularized=True)/2 + # For x > 0, cdf(x, a) is (1 + mp.gammainc(a, 0, x, regularized=True))/2 + # E.g. + # from mpmath import mp + # mp.dps = 50 + # print(float(mp.gammainc(1, 20, mp.inf, regularized=True)/2)) + # prints + # 1.030576811219279e-09 + @pytest.mark.parametrize('x, a, expected', + [(-20, 1, 1.030576811219279e-09), + (-40, 1, 2.1241771276457944e-18), + (-50, 5, 2.7248509914602648e-17), + (-25, 0.125, 5.333071920958156e-14), + (5, 1, 0.9966310265004573)]) + def test_cdf_ppf_sf_isf_tail(self, x, a, expected): + cdf = stats.dgamma.cdf(x, a) + assert_allclose(cdf, expected, rtol=5e-15) + ppf = stats.dgamma.ppf(expected, a) + assert_allclose(ppf, x, rtol=5e-15) + sf = stats.dgamma.sf(-x, a) + assert_allclose(sf, expected, rtol=5e-15) + isf = stats.dgamma.isf(expected, a) + assert_allclose(isf, -x, rtol=5e-15) + + @pytest.mark.parametrize("a, ref", + [(1.5, 2.0541199559354117), + (1.3, 1.9357296377121247), + (1.1, 1.7856502333412134)]) + def test_entropy(self, a, ref): + # The reference values were calculated with mpmath: + # def entropy_dgamma(a): + # def pdf(x): + # A = mp.one / (mp.mpf(2.) * mp.gamma(a)) + # B = mp.fabs(x) ** (a - mp.one) + # C = mp.exp(-mp.fabs(x)) + # h = A * B * C + # return h + # + # return -mp.quad(lambda t: pdf(t) * mp.log(pdf(t)), + # [-mp.inf, mp.inf]) + assert_allclose(stats.dgamma.entropy(a), ref, rtol=1e-14) + + @pytest.mark.parametrize("a, ref", + [(1e-100, -1e+100), + (1e-10, -9999999975.858217), + (1e-5, -99987.37111657023), + (1e4, 6.717222565586032), + (1000000000000000.0, 19.38147391121996), + (1e+100, 117.2413403634669)]) + def test_entropy_entreme_values(self, a, ref): + # The reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + # def second_dgamma(a): + # a = mp.mpf(a) + # x_1 = a + mp.log(2) + mp.loggamma(a) + # x_2 = (mp.one - a) * mp.digamma(a) + # h = x_1 + x_2 + # return h + assert_allclose(stats.dgamma.entropy(a), ref, rtol=1e-10) + + def test_entropy_array_input(self): + x = np.array([1, 5, 1e20, 1e-5]) + y = stats.dgamma.entropy(x) + for i in range(len(y)): + assert y[i] == stats.dgamma.entropy(x[i]) + + +class TestChi2: + # regression tests after precision improvements, ticket:1041, not verified + def test_precision(self): + assert_almost_equal(stats.chi2.pdf(1000, 1000), 8.919133934753128e-003, + decimal=14) + assert_almost_equal(stats.chi2.pdf(100, 100), 0.028162503162596778, + decimal=14) + + def test_ppf(self): + # Expected values computed with mpmath. + df = 4.8 + x = stats.chi2.ppf(2e-47, df) + assert_allclose(x, 1.098472479575179840604902808e-19, rtol=1e-10) + x = stats.chi2.ppf(0.5, df) + assert_allclose(x, 4.15231407598589358660093156, rtol=1e-10) + + df = 13 + x = stats.chi2.ppf(2e-77, df) + assert_allclose(x, 1.0106330688195199050507943e-11, rtol=1e-10) + x = stats.chi2.ppf(0.1, df) + assert_allclose(x, 7.041504580095461859307179763, rtol=1e-10) + + # Entropy references values were computed with the following mpmath code + # from mpmath import mp + # mp.dps = 50 + # def chisq_entropy_mpmath(df): + # df = mp.mpf(df) + # half_df = 0.5 * df + # entropy = (half_df + mp.log(2) + mp.log(mp.gamma(half_df)) + + # (mp.one - half_df) * mp.digamma(half_df)) + # return float(entropy) + + @pytest.mark.parametrize('df, ref', + [(1e-4, -19988.980448690163), + (1, 0.7837571104739337), + (100, 4.061397128938114), + (251, 4.525577254045129), + (1e15, 19.034900320939986)]) + def test_entropy(self, df, ref): + assert_allclose(stats.chi2(df).entropy(), ref, rtol=1e-13) + + +class TestGumbelL: + # gh-6228 + def test_cdf_ppf(self): + x = np.linspace(-100, -4) + y = stats.gumbel_l.cdf(x) + xx = stats.gumbel_l.ppf(y) + assert_allclose(x, xx) + + def test_logcdf_logsf(self): + x = np.linspace(-100, -4) + y = stats.gumbel_l.logcdf(x) + z = stats.gumbel_l.logsf(x) + u = np.exp(y) + v = -special.expm1(z) + assert_allclose(u, v) + + def test_sf_isf(self): + x = np.linspace(-20, 5) + y = stats.gumbel_l.sf(x) + xx = stats.gumbel_l.isf(y) + assert_allclose(x, xx) + + @pytest.mark.parametrize('loc', [-1, 1]) + def test_fit_fixed_param(self, loc): + # ensure fixed location is correctly reflected from `gumbel_r.fit` + # See comments at end of gh-12737. + data = stats.gumbel_l.rvs(size=100, loc=loc) + fitted_loc, _ = stats.gumbel_l.fit(data, floc=loc) + assert_equal(fitted_loc, loc) + + +class TestGumbelR: + + def test_sf(self): + # Expected value computed with mpmath: + # >>> import mpmath + # >>> mpmath.mp.dps = 40 + # >>> float(mpmath.mp.one - mpmath.exp(-mpmath.exp(-50))) + # 1.9287498479639178e-22 + assert_allclose(stats.gumbel_r.sf(50), 1.9287498479639178e-22, + rtol=1e-14) + + def test_isf(self): + # Expected value computed with mpmath: + # >>> import mpmath + # >>> mpmath.mp.dps = 40 + # >>> float(-mpmath.log(-mpmath.log(mpmath.mp.one - 1e-17))) + # 39.14394658089878 + assert_allclose(stats.gumbel_r.isf(1e-17), 39.14394658089878, + rtol=1e-14) + + +class TestLevyStable: + @pytest.fixture(autouse=True) + def reset_levy_stable_params(self): + """Setup default parameters for levy_stable generator""" + stats.levy_stable.parameterization = "S1" + stats.levy_stable.cdf_default_method = "piecewise" + stats.levy_stable.pdf_default_method = "piecewise" + stats.levy_stable.quad_eps = stats._levy_stable._QUAD_EPS + + @pytest.fixture + def nolan_pdf_sample_data(self): + """Sample data points for pdf computed with Nolan's stablec + + See - http://fs2.american.edu/jpnolan/www/stable/stable.html + + There's a known limitation of Nolan's executable for alpha < 0.2. + + The data table loaded below is generated from Nolan's stablec + with the following parameter space: + + alpha = 0.1, 0.2, ..., 2.0 + beta = -1.0, -0.9, ..., 1.0 + p = 0.01, 0.05, 0.1, 0.25, 0.35, 0.5, + and the equivalent for the right tail + + Typically inputs for stablec: + + stablec.exe << + 1 # pdf + 1 # Nolan S equivalent to S0 in scipy + .25,2,.25 # alpha + -1,-1,0 # beta + -10,10,1 # x + 1,0 # gamma, delta + 2 # output file + """ + data = np.load( + Path(__file__).parent / + 'data/levy_stable/stable-Z1-pdf-sample-data.npy' + ) + data = np.rec.fromarrays(data.T, names='x,p,alpha,beta,pct') + return data + + @pytest.fixture + def nolan_cdf_sample_data(self): + """Sample data points for cdf computed with Nolan's stablec + + See - http://fs2.american.edu/jpnolan/www/stable/stable.html + + There's a known limitation of Nolan's executable for alpha < 0.2. + + The data table loaded below is generated from Nolan's stablec + with the following parameter space: + + alpha = 0.1, 0.2, ..., 2.0 + beta = -1.0, -0.9, ..., 1.0 + p = 0.01, 0.05, 0.1, 0.25, 0.35, 0.5, + + and the equivalent for the right tail + + Ideally, Nolan's output for CDF values should match the percentile + from where they have been sampled from. Even more so as we extract + percentile x positions from stablec too. However, we note at places + Nolan's stablec will produce absolute errors in order of 1e-5. We + compare against his calculations here. In future, once we less + reliant on Nolan's paper we might switch to comparing directly at + percentiles (those x values being produced from some alternative + means). + + Typically inputs for stablec: + + stablec.exe << + 2 # cdf + 1 # Nolan S equivalent to S0 in scipy + .25,2,.25 # alpha + -1,-1,0 # beta + -10,10,1 # x + 1,0 # gamma, delta + 2 # output file + """ + data = np.load( + Path(__file__).parent / + 'data/levy_stable/stable-Z1-cdf-sample-data.npy' + ) + data = np.rec.fromarrays(data.T, names='x,p,alpha,beta,pct') + return data + + @pytest.fixture + def nolan_loc_scale_sample_data(self): + """Sample data where loc, scale are different from 0, 1 + + Data extracted in similar way to pdf/cdf above using + Nolan's stablec but set to an arbitrary location scale of + (2, 3) for various important parameters alpha, beta and for + parameterisations S0 and S1. + """ + data = np.load( + Path(__file__).parent / + 'data/levy_stable/stable-loc-scale-sample-data.npy' + ) + return data + + @pytest.mark.parametrize( + "sample_size", [ + pytest.param(50), pytest.param(1500, marks=pytest.mark.slow) + ] + ) + @pytest.mark.parametrize("parameterization", ["S0", "S1"]) + @pytest.mark.parametrize( + "alpha,beta", [(1.0, 0), (1.0, -0.5), (1.5, 0), (1.9, 0.5)] + ) + @pytest.mark.parametrize("gamma,delta", [(1, 0), (3, 2)]) + def test_rvs( + self, + parameterization, + alpha, + beta, + gamma, + delta, + sample_size, + ): + stats.levy_stable.parameterization = parameterization + ls = stats.levy_stable( + alpha=alpha, beta=beta, scale=gamma, loc=delta + ) + _, p = stats.kstest( + ls.rvs(size=sample_size, random_state=1234), ls.cdf + ) + assert p > 0.05 + + @pytest.mark.slow + @pytest.mark.parametrize('beta', [0.5, 1]) + def test_rvs_alpha1(self, beta): + """Additional test cases for rvs for alpha equal to 1.""" + np.random.seed(987654321) + alpha = 1.0 + loc = 0.5 + scale = 1.5 + x = stats.levy_stable.rvs(alpha, beta, loc=loc, scale=scale, + size=5000) + stat, p = stats.kstest(x, 'levy_stable', + args=(alpha, beta, loc, scale)) + assert p > 0.01 + + def test_fit(self): + # construct data to have percentiles that match + # example in McCulloch 1986. + x = [ + -.05413, -.05413, 0., 0., 0., 0., .00533, .00533, .00533, .00533, + .00533, .03354, .03354, .03354, .03354, .03354, .05309, .05309, + .05309, .05309, .05309 + ] + alpha1, beta1, loc1, scale1 = stats.levy_stable._fitstart(x) + assert_allclose(alpha1, 1.48, rtol=0, atol=0.01) + assert_almost_equal(beta1, -.22, 2) + assert_almost_equal(scale1, 0.01717, 4) + assert_almost_equal( + loc1, 0.00233, 2 + ) # to 2 dps due to rounding error in McCulloch86 + + # cover alpha=2 scenario + x2 = x + [.05309, .05309, .05309, .05309, .05309] + alpha2, beta2, loc2, scale2 = stats.levy_stable._fitstart(x2) + assert_equal(alpha2, 2) + assert_equal(beta2, -1) + assert_almost_equal(scale2, .02503, 4) + assert_almost_equal(loc2, .03354, 4) + + @pytest.mark.xfail(reason="Unknown problem with fitstart.") + @pytest.mark.parametrize( + "alpha,beta,delta,gamma", + [ + (1.5, 0.4, 2, 3), + (1.0, 0.4, 2, 3), + ] + ) + @pytest.mark.parametrize( + "parametrization", ["S0", "S1"] + ) + def test_fit_rvs(self, alpha, beta, delta, gamma, parametrization): + """Test that fit agrees with rvs for each parametrization.""" + stats.levy_stable.parametrization = parametrization + data = stats.levy_stable.rvs( + alpha, beta, loc=delta, scale=gamma, size=10000, random_state=1234 + ) + fit = stats.levy_stable._fitstart(data) + alpha_obs, beta_obs, delta_obs, gamma_obs = fit + assert_allclose( + [alpha, beta, delta, gamma], + [alpha_obs, beta_obs, delta_obs, gamma_obs], + rtol=0.01, + ) + + def test_fit_beta_flip(self): + # Confirm that sign of beta affects loc, not alpha or scale. + x = np.array([1, 1, 3, 3, 10, 10, 10, 30, 30, 100, 100]) + alpha1, beta1, loc1, scale1 = stats.levy_stable._fitstart(x) + alpha2, beta2, loc2, scale2 = stats.levy_stable._fitstart(-x) + assert_equal(beta1, 1) + assert loc1 != 0 + assert_almost_equal(alpha2, alpha1) + assert_almost_equal(beta2, -beta1) + assert_almost_equal(loc2, -loc1) + assert_almost_equal(scale2, scale1) + + def test_fit_delta_shift(self): + # Confirm that loc slides up and down if data shifts. + SHIFT = 1 + x = np.array([1, 1, 3, 3, 10, 10, 10, 30, 30, 100, 100]) + alpha1, beta1, loc1, scale1 = stats.levy_stable._fitstart(-x) + alpha2, beta2, loc2, scale2 = stats.levy_stable._fitstart(-x + SHIFT) + assert_almost_equal(alpha2, alpha1) + assert_almost_equal(beta2, beta1) + assert_almost_equal(loc2, loc1 + SHIFT) + assert_almost_equal(scale2, scale1) + + def test_fit_loc_extrap(self): + # Confirm that loc goes out of sample for alpha close to 1. + x = [1, 1, 3, 3, 10, 10, 10, 30, 30, 140, 140] + alpha1, beta1, loc1, scale1 = stats.levy_stable._fitstart(x) + assert alpha1 < 1, f"Expected alpha < 1, got {alpha1}" + assert loc1 < min(x), f"Expected loc < {min(x)}, got {loc1}" + + x2 = [1, 1, 3, 3, 10, 10, 10, 30, 30, 130, 130] + alpha2, beta2, loc2, scale2 = stats.levy_stable._fitstart(x2) + assert alpha2 > 1, f"Expected alpha > 1, got {alpha2}" + assert loc2 > max(x2), f"Expected loc > {max(x2)}, got {loc2}" + + @pytest.mark.parametrize( + "pct_range,alpha_range,beta_range", [ + pytest.param( + [.01, .5, .99], + [.1, 1, 2], + [-1, 0, .8], + ), + pytest.param( + [.01, .05, .5, .95, .99], + [.1, .5, 1, 1.5, 2], + [-.9, -.5, 0, .3, .6, 1], + marks=pytest.mark.slow + ), + pytest.param( + [.01, .05, .1, .25, .35, .5, .65, .75, .9, .95, .99], + np.linspace(0.1, 2, 20), + np.linspace(-1, 1, 21), + marks=pytest.mark.xslow, + ), + ] + ) + def test_pdf_nolan_samples( + self, nolan_pdf_sample_data, pct_range, alpha_range, beta_range + ): + """Test pdf values against Nolan's stablec.exe output""" + data = nolan_pdf_sample_data + + # some tests break on linux 32 bit + uname = platform.uname() + is_linux_32 = uname.system == 'Linux' and uname.machine == 'i686' + platform_desc = "/".join( + [uname.system, uname.machine, uname.processor]) + + # fmt: off + # There are a number of cases which fail on some but not all platforms. + # These are excluded by the filters below. TODO: Rewrite tests so that + # the now filtered out test cases are still run but marked in pytest as + # expected to fail. + tests = [ + [ + 'dni', 1e-7, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + ~( + ( + (r['beta'] == 0) & + (r['pct'] == 0.5) + ) | + ( + (r['beta'] >= 0.9) & + (r['alpha'] >= 1.6) & + (r['pct'] == 0.5) + ) | + ( + (r['alpha'] <= 0.4) & + np.isin(r['pct'], [.01, .99]) + ) | + ( + (r['alpha'] <= 0.3) & + np.isin(r['pct'], [.05, .95]) + ) | + ( + (r['alpha'] <= 0.2) & + np.isin(r['pct'], [.1, .9]) + ) | + ( + (r['alpha'] == 0.1) & + np.isin(r['pct'], [.25, .75]) & + np.isin(np.abs(r['beta']), [.5, .6, .7]) + ) | + ( + (r['alpha'] == 0.1) & + np.isin(r['pct'], [.5]) & + np.isin(np.abs(r['beta']), [.1]) + ) | + ( + (r['alpha'] == 0.1) & + np.isin(r['pct'], [.35, .65]) & + np.isin(np.abs(r['beta']), [-.4, -.3, .3, .4, .5]) + ) | + ( + (r['alpha'] == 0.2) & + (r['beta'] == 0.5) & + (r['pct'] == 0.25) + ) | + ( + (r['alpha'] == 0.2) & + (r['beta'] == -0.3) & + (r['pct'] == 0.65) + ) | + ( + (r['alpha'] == 0.2) & + (r['beta'] == 0.3) & + (r['pct'] == 0.35) + ) | + ( + (r['alpha'] == 1.) & + np.isin(r['pct'], [.5]) & + np.isin(np.abs(r['beta']), [.1, .2, .3, .4]) + ) | + ( + (r['alpha'] == 1.) & + np.isin(r['pct'], [.35, .65]) & + np.isin(np.abs(r['beta']), [.8, .9, 1.]) + ) | + ( + (r['alpha'] == 1.) & + np.isin(r['pct'], [.01, .99]) & + np.isin(np.abs(r['beta']), [-.1, .1]) + ) | + # various points ok but too sparse to list + (r['alpha'] >= 1.1) + ) + ) + ], + # piecewise generally good accuracy + [ + 'piecewise', 1e-11, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 0.2) & + (r['alpha'] != 1.) + ) + ], + # for alpha = 1. for linux 32 bit optimize.bisect + # has some issues for .01 and .99 percentile + [ + 'piecewise', 1e-11, lambda r: ( + (r['alpha'] == 1.) & + (not is_linux_32) & + np.isin(r['pct'], pct_range) & + (1. in alpha_range) & + np.isin(r['beta'], beta_range) + ) + ], + # for small alpha very slightly reduced accuracy + [ + 'piecewise', 2.5e-10, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] <= 0.2) + ) + ], + # fft accuracy reduces as alpha decreases + [ + 'fft-simpson', 1e-5, lambda r: ( + (r['alpha'] >= 1.9) & + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) + ), + ], + [ + 'fft-simpson', 1e-6, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 1) & + (r['alpha'] < 1.9) + ) + ], + # fft relative errors for alpha < 1, will raise if enabled + # ['fft-simpson', 1e-4, lambda r: r['alpha'] == 0.9], + # ['fft-simpson', 1e-3, lambda r: r['alpha'] == 0.8], + # ['fft-simpson', 1e-2, lambda r: r['alpha'] == 0.7], + # ['fft-simpson', 1e-1, lambda r: r['alpha'] == 0.6], + ] + # fmt: on + for ix, (default_method, rtol, + filter_func) in enumerate(tests): + stats.levy_stable.pdf_default_method = default_method + subdata = data[filter_func(data) + ] if filter_func is not None else data + with suppress_warnings() as sup: + # occurs in FFT methods only + sup.record( + RuntimeWarning, + "Density calculations experimental for FFT method.*" + ) + p = stats.levy_stable.pdf( + subdata['x'], + subdata['alpha'], + subdata['beta'], + scale=1, + loc=0 + ) + with np.errstate(over="ignore"): + subdata2 = rec_append_fields( + subdata, + ['calc', 'abserr', 'relerr'], + [ + p, + np.abs(p - subdata['p']), + np.abs(p - subdata['p']) / np.abs(subdata['p']) + ] + ) + failures = subdata2[ + (subdata2['relerr'] >= rtol) | + np.isnan(p) + ] + message = ( + f"pdf test {ix} failed with method '{default_method}' " + f"[platform: {platform_desc}]\n{failures.dtype.names}\n{failures}" + ) + assert_allclose( + p, + subdata['p'], + rtol, + err_msg=message, + verbose=False + ) + + @pytest.mark.parametrize( + "pct_range,alpha_range,beta_range", [ + pytest.param( + [.01, .5, .99], + [.1, 1, 2], + [-1, 0, .8], + ), + pytest.param( + [.01, .05, .5, .95, .99], + [.1, .5, 1, 1.5, 2], + [-.9, -.5, 0, .3, .6, 1], + marks=pytest.mark.slow + ), + pytest.param( + [.01, .05, .1, .25, .35, .5, .65, .75, .9, .95, .99], + np.linspace(0.1, 2, 20), + np.linspace(-1, 1, 21), + marks=pytest.mark.xslow, + ), + ] + ) + def test_cdf_nolan_samples( + self, nolan_cdf_sample_data, pct_range, alpha_range, beta_range + ): + """ Test cdf values against Nolan's stablec.exe output.""" + data = nolan_cdf_sample_data + tests = [ + # piecewise generally good accuracy + [ + 'piecewise', 2e-12, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + ~( + ( + (r['alpha'] == 1.) & + np.isin(r['beta'], [-0.3, -0.2, -0.1]) & + (r['pct'] == 0.01) + ) | + ( + (r['alpha'] == 1.) & + np.isin(r['beta'], [0.1, 0.2, 0.3]) & + (r['pct'] == 0.99) + ) + ) + ) + ], + # for some points with alpha=1, Nolan's STABLE clearly + # loses accuracy + [ + 'piecewise', 5e-2, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + ( + (r['alpha'] == 1.) & + np.isin(r['beta'], [-0.3, -0.2, -0.1]) & + (r['pct'] == 0.01) + ) | + ( + (r['alpha'] == 1.) & + np.isin(r['beta'], [0.1, 0.2, 0.3]) & + (r['pct'] == 0.99) + ) + ) + ], + # fft accuracy poor, very poor alpha < 1 + [ + 'fft-simpson', 1e-5, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 1.7) + ) + ], + [ + 'fft-simpson', 1e-4, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 1.5) & + (r['alpha'] <= 1.7) + ) + ], + [ + 'fft-simpson', 1e-3, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 1.3) & + (r['alpha'] <= 1.5) + ) + ], + [ + 'fft-simpson', 1e-2, lambda r: ( + np.isin(r['pct'], pct_range) & + np.isin(r['alpha'], alpha_range) & + np.isin(r['beta'], beta_range) & + (r['alpha'] > 1.0) & + (r['alpha'] <= 1.3) + ) + ], + ] + for ix, (default_method, rtol, + filter_func) in enumerate(tests): + stats.levy_stable.cdf_default_method = default_method + subdata = data[filter_func(data) + ] if filter_func is not None else data + with suppress_warnings() as sup: + sup.record( + RuntimeWarning, + 'Cumulative density calculations experimental for FFT' + + ' method. Use piecewise method instead.*' + ) + p = stats.levy_stable.cdf( + subdata['x'], + subdata['alpha'], + subdata['beta'], + scale=1, + loc=0 + ) + with np.errstate(over="ignore"): + subdata2 = rec_append_fields( + subdata, + ['calc', 'abserr', 'relerr'], + [ + p, + np.abs(p - subdata['p']), + np.abs(p - subdata['p']) / np.abs(subdata['p']) + ] + ) + failures = subdata2[ + (subdata2['relerr'] >= rtol) | + np.isnan(p) + ] + message = (f"cdf test {ix} failed with method '{default_method}'\n" + f"{failures.dtype.names}\n{failures}") + assert_allclose( + p, + subdata['p'], + rtol, + err_msg=message, + verbose=False + ) + + @pytest.mark.parametrize("param", [0, 1]) + @pytest.mark.parametrize("case", ["pdf", "cdf"]) + def test_location_scale( + self, nolan_loc_scale_sample_data, param, case + ): + """Tests for pdf and cdf where loc, scale are different from 0, 1 + """ + + uname = platform.uname() + is_linux_32 = uname.system == 'Linux' and "32bit" in platform.architecture()[0] + # Test seems to be unstable (see gh-17839 for a bug report on Debian + # i386), so skip it. + if is_linux_32 and case == 'pdf': + pytest.skip("Test unstable on some platforms; see gh-17839, 17859") + + data = nolan_loc_scale_sample_data + # We only test against piecewise as location/scale transforms + # are same for other methods. + stats.levy_stable.cdf_default_method = "piecewise" + stats.levy_stable.pdf_default_method = "piecewise" + + subdata = data[data["param"] == param] + stats.levy_stable.parameterization = f"S{param}" + + assert case in ["pdf", "cdf"] + function = ( + stats.levy_stable.pdf if case == "pdf" else stats.levy_stable.cdf + ) + + v1 = function( + subdata['x'], subdata['alpha'], subdata['beta'], scale=2, loc=3 + ) + assert_allclose(v1, subdata[case], 1e-5) + + @pytest.mark.parametrize( + "method,decimal_places", + [ + ['dni', 4], + ['piecewise', 4], + ] + ) + def test_pdf_alpha_equals_one_beta_non_zero(self, method, decimal_places): + """ sample points extracted from Tables and Graphs of Stable + Probability Density Functions - Donald R Holt - 1973 - p 187. + """ + xs = np.array( + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4] + ) + density = np.array( + [ + .3183, .3096, .2925, .2622, .1591, .1587, .1599, .1635, .0637, + .0729, .0812, .0955, .0318, .0390, .0458, .0586, .0187, .0236, + .0285, .0384 + ] + ) + betas = np.array( + [ + 0, .25, .5, 1, 0, .25, .5, 1, 0, .25, .5, 1, 0, .25, .5, 1, 0, + .25, .5, 1 + ] + ) + with np.errstate(all='ignore'), suppress_warnings() as sup: + sup.filter( + category=RuntimeWarning, + message="Density calculation unstable.*" + ) + stats.levy_stable.pdf_default_method = method + # stats.levy_stable.fft_grid_spacing = 0.0001 + pdf = stats.levy_stable.pdf(xs, 1, betas, scale=1, loc=0) + assert_almost_equal( + pdf, density, decimal_places, method + ) + + @pytest.mark.parametrize( + "params,expected", + [ + [(1.48, -.22, 0, 1), (0, np.inf, np.nan, np.nan)], + [(2, .9, 10, 1.5), (10, 4.5, 0, 0)] + ] + ) + def test_stats(self, params, expected): + observed = stats.levy_stable.stats( + params[0], params[1], loc=params[2], scale=params[3], + moments='mvsk' + ) + assert_almost_equal(observed, expected) + + @pytest.mark.parametrize('alpha', [0.25, 0.5, 0.75]) + @pytest.mark.parametrize( + 'function,beta,points,expected', + [ + ( + stats.levy_stable.cdf, + 1.0, + np.linspace(-25, 0, 10), + 0.0, + ), + ( + stats.levy_stable.pdf, + 1.0, + np.linspace(-25, 0, 10), + 0.0, + ), + ( + stats.levy_stable.cdf, + -1.0, + np.linspace(0, 25, 10), + 1.0, + ), + ( + stats.levy_stable.pdf, + -1.0, + np.linspace(0, 25, 10), + 0.0, + ) + ] + ) + def test_distribution_outside_support( + self, alpha, function, beta, points, expected + ): + """Ensure the pdf/cdf routines do not return nan outside support. + + This distribution's support becomes truncated in a few special cases: + support is [mu, infty) if alpha < 1 and beta = 1 + support is (-infty, mu] if alpha < 1 and beta = -1 + Otherwise, the support is all reals. Here, mu is zero by default. + """ + assert 0 < alpha < 1 + assert_almost_equal( + function(points, alpha=alpha, beta=beta), + np.full(len(points), expected) + ) + + @pytest.mark.parametrize( + 'x,alpha,beta,expected', + # Reference values from Matlab + # format long + # alphas = [1.7720732804618808, 1.9217001522410235, 1.5654806051633634, + # 1.7420803447784388, 1.5748002527689913]; + # betas = [0.5059373136902996, -0.8779442746685926, -0.4016220341911392, + # -0.38180029468259247, -0.25200194914153684]; + # x0s = [0, 1e-4, -1e-4]; + # for x0 = x0s + # disp("x0 = " + x0) + # for ii = 1:5 + # alpha = alphas(ii); + # beta = betas(ii); + # pd = makedist('Stable','alpha',alpha,'beta',beta,'gam',1,'delta',0); + # % we need to adjust x. It is the same as x = 0 In scipy. + # x = x0 - beta * tan(pi * alpha / 2); + # disp(pd.pdf(x)) + # end + # end + [ + (0, 1.7720732804618808, 0.5059373136902996, 0.278932636798268), + (0, 1.9217001522410235, -0.8779442746685926, 0.281054757202316), + (0, 1.5654806051633634, -0.4016220341911392, 0.271282133194204), + (0, 1.7420803447784388, -0.38180029468259247, 0.280202199244247), + (0, 1.5748002527689913, -0.25200194914153684, 0.280136576218665), + ] + ) + def test_x_equal_zeta( + self, x, alpha, beta, expected + ): + """Test pdf for x equal to zeta. + + With S1 parametrization: x0 = x + zeta if alpha != 1 So, for x = 0, x0 + will be close to zeta. + + When case "x equal zeta" is not handled properly and quad_eps is not + low enough: - pdf may be less than 0 - logpdf is nan + + The points from the parametrize block are found randomly so that PDF is + less than 0. + + Reference values taken from MATLAB + https://www.mathworks.com/help/stats/stable-distribution.html + """ + stats.levy_stable.quad_eps = 1.2e-11 + + assert_almost_equal( + stats.levy_stable.pdf(x, alpha=alpha, beta=beta), + expected, + ) + + @pytest.mark.xfail + @pytest.mark.parametrize( + # See comment for test_x_equal_zeta for script for reference values + 'x,alpha,beta,expected', + [ + (1e-4, 1.7720732804618808, 0.5059373136902996, 0.278929165340670), + (1e-4, 1.9217001522410235, -0.8779442746685926, 0.281056564327953), + (1e-4, 1.5654806051633634, -0.4016220341911392, 0.271252432161167), + (1e-4, 1.7420803447784388, -0.38180029468259247, 0.280205311264134), + (1e-4, 1.5748002527689913, -0.25200194914153684, 0.280140965235426), + (-1e-4, 1.7720732804618808, 0.5059373136902996, 0.278936106741754), + (-1e-4, 1.9217001522410235, -0.8779442746685926, 0.281052948629429), + (-1e-4, 1.5654806051633634, -0.4016220341911392, 0.271275394392385), + (-1e-4, 1.7420803447784388, -0.38180029468259247, 0.280199085645099), + (-1e-4, 1.5748002527689913, -0.25200194914153684, 0.280132185432842), + ] + ) + def test_x_near_zeta( + self, x, alpha, beta, expected + ): + """Test pdf for x near zeta. + + With S1 parametrization: x0 = x + zeta if alpha != 1 So, for x = 0, x0 + will be close to zeta. + + When case "x near zeta" is not handled properly and quad_eps is not + low enough: - pdf may be less than 0 - logpdf is nan + + The points from the parametrize block are found randomly so that PDF is + less than 0. + + Reference values taken from MATLAB + https://www.mathworks.com/help/stats/stable-distribution.html + """ + stats.levy_stable.quad_eps = 1.2e-11 + + assert_almost_equal( + stats.levy_stable.pdf(x, alpha=alpha, beta=beta), + expected, + ) + + +class TestArrayArgument: # test for ticket:992 + def setup_method(self): + np.random.seed(1234) + + def test_noexception(self): + rvs = stats.norm.rvs(loc=(np.arange(5)), scale=np.ones(5), + size=(10, 5)) + assert_equal(rvs.shape, (10, 5)) + + +class TestDocstring: + def test_docstrings(self): + # See ticket #761 + if stats.rayleigh.__doc__ is not None: + assert_("rayleigh" in stats.rayleigh.__doc__.lower()) + if stats.bernoulli.__doc__ is not None: + assert_("bernoulli" in stats.bernoulli.__doc__.lower()) + + def test_no_name_arg(self): + # If name is not given, construction shouldn't fail. See #1508. + stats.rv_continuous() + stats.rv_discrete() + + +def test_args_reduce(): + a = array([1, 3, 2, 1, 2, 3, 3]) + b, c = argsreduce(a > 1, a, 2) + + assert_array_equal(b, [3, 2, 2, 3, 3]) + assert_array_equal(c, [2]) + + b, c = argsreduce(2 > 1, a, 2) + assert_array_equal(b, a) + assert_array_equal(c, [2] * np.size(a)) + + b, c = argsreduce(a > 0, a, 2) + assert_array_equal(b, a) + assert_array_equal(c, [2] * np.size(a)) + + +class TestFitMethod: + skip = ['ncf', 'ksone', 'kstwo'] + + def setup_method(self): + np.random.seed(1234) + + # skip these b/c deprecated, or only loc and scale arguments + fitSkipNonFinite = ['expon', 'norm', 'uniform'] + + @pytest.mark.parametrize('dist,args', distcont) + def test_fit_w_non_finite_data_values(self, dist, args): + """gh-10300""" + if dist in self.fitSkipNonFinite: + pytest.skip("%s fit known to fail or deprecated" % dist) + x = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.nan]) + y = np.array([1.6483, 2.7169, 2.4667, 1.1791, 3.5433, np.inf]) + distfunc = getattr(stats, dist) + assert_raises(ValueError, distfunc.fit, x, fscale=1) + assert_raises(ValueError, distfunc.fit, y, fscale=1) + + def test_fix_fit_2args_lognorm(self): + # Regression test for #1551. + np.random.seed(12345) + with np.errstate(all='ignore'): + x = stats.lognorm.rvs(0.25, 0., 20.0, size=20) + expected_shape = np.sqrt(((np.log(x) - np.log(20))**2).mean()) + assert_allclose(np.array(stats.lognorm.fit(x, floc=0, fscale=20)), + [expected_shape, 0, 20], atol=1e-8) + + def test_fix_fit_norm(self): + x = np.arange(1, 6) + + loc, scale = stats.norm.fit(x) + assert_almost_equal(loc, 3) + assert_almost_equal(scale, np.sqrt(2)) + + loc, scale = stats.norm.fit(x, floc=2) + assert_equal(loc, 2) + assert_equal(scale, np.sqrt(3)) + + loc, scale = stats.norm.fit(x, fscale=2) + assert_almost_equal(loc, 3) + assert_equal(scale, 2) + + def test_fix_fit_gamma(self): + x = np.arange(1, 6) + meanlog = np.log(x).mean() + + # A basic test of gamma.fit with floc=0. + floc = 0 + a, loc, scale = stats.gamma.fit(x, floc=floc) + s = np.log(x.mean()) - meanlog + assert_almost_equal(np.log(a) - special.digamma(a), s, decimal=5) + assert_equal(loc, floc) + assert_almost_equal(scale, x.mean()/a, decimal=8) + + # Regression tests for gh-2514. + # The problem was that if `floc=0` was given, any other fixed + # parameters were ignored. + f0 = 1 + floc = 0 + a, loc, scale = stats.gamma.fit(x, f0=f0, floc=floc) + assert_equal(a, f0) + assert_equal(loc, floc) + assert_almost_equal(scale, x.mean()/a, decimal=8) + + f0 = 2 + floc = 0 + a, loc, scale = stats.gamma.fit(x, f0=f0, floc=floc) + assert_equal(a, f0) + assert_equal(loc, floc) + assert_almost_equal(scale, x.mean()/a, decimal=8) + + # loc and scale fixed. + floc = 0 + fscale = 2 + a, loc, scale = stats.gamma.fit(x, floc=floc, fscale=fscale) + assert_equal(loc, floc) + assert_equal(scale, fscale) + c = meanlog - np.log(fscale) + assert_almost_equal(special.digamma(a), c) + + def test_fix_fit_beta(self): + # Test beta.fit when both floc and fscale are given. + + def mlefunc(a, b, x): + # Zeros of this function are critical points of + # the maximum likelihood function. + n = len(x) + s1 = np.log(x).sum() + s2 = np.log(1-x).sum() + psiab = special.psi(a + b) + func = [s1 - n * (-psiab + special.psi(a)), + s2 - n * (-psiab + special.psi(b))] + return func + + # Basic test with floc and fscale given. + x = np.array([0.125, 0.25, 0.5]) + a, b, loc, scale = stats.beta.fit(x, floc=0, fscale=1) + assert_equal(loc, 0) + assert_equal(scale, 1) + assert_allclose(mlefunc(a, b, x), [0, 0], atol=1e-6) + + # Basic test with f0, floc and fscale given. + # This is also a regression test for gh-2514. + x = np.array([0.125, 0.25, 0.5]) + a, b, loc, scale = stats.beta.fit(x, f0=2, floc=0, fscale=1) + assert_equal(a, 2) + assert_equal(loc, 0) + assert_equal(scale, 1) + da, db = mlefunc(a, b, x) + assert_allclose(db, 0, atol=1e-5) + + # Same floc and fscale values as above, but reverse the data + # and fix b (f1). + x2 = 1 - x + a2, b2, loc2, scale2 = stats.beta.fit(x2, f1=2, floc=0, fscale=1) + assert_equal(b2, 2) + assert_equal(loc2, 0) + assert_equal(scale2, 1) + da, db = mlefunc(a2, b2, x2) + assert_allclose(da, 0, atol=1e-5) + # a2 of this test should equal b from above. + assert_almost_equal(a2, b) + + # Check for detection of data out of bounds when floc and fscale + # are given. + assert_raises(ValueError, stats.beta.fit, x, floc=0.5, fscale=1) + y = np.array([0, .5, 1]) + assert_raises(ValueError, stats.beta.fit, y, floc=0, fscale=1) + assert_raises(ValueError, stats.beta.fit, y, floc=0, fscale=1, f0=2) + assert_raises(ValueError, stats.beta.fit, y, floc=0, fscale=1, f1=2) + + # Check that attempting to fix all the parameters raises a ValueError. + assert_raises(ValueError, stats.beta.fit, y, f0=0, f1=1, + floc=2, fscale=3) + + def test_expon_fit(self): + x = np.array([2, 2, 4, 4, 4, 4, 4, 8]) + + loc, scale = stats.expon.fit(x) + assert_equal(loc, 2) # x.min() + assert_equal(scale, 2) # x.mean() - x.min() + + loc, scale = stats.expon.fit(x, fscale=3) + assert_equal(loc, 2) # x.min() + assert_equal(scale, 3) # fscale + + loc, scale = stats.expon.fit(x, floc=0) + assert_equal(loc, 0) # floc + assert_equal(scale, 4) # x.mean() - loc + + def test_lognorm_fit(self): + x = np.array([1.5, 3, 10, 15, 23, 59]) + lnxm1 = np.log(x - 1) + + shape, loc, scale = stats.lognorm.fit(x, floc=1) + assert_allclose(shape, lnxm1.std(), rtol=1e-12) + assert_equal(loc, 1) + assert_allclose(scale, np.exp(lnxm1.mean()), rtol=1e-12) + + shape, loc, scale = stats.lognorm.fit(x, floc=1, fscale=6) + assert_allclose(shape, np.sqrt(((lnxm1 - np.log(6))**2).mean()), + rtol=1e-12) + assert_equal(loc, 1) + assert_equal(scale, 6) + + shape, loc, scale = stats.lognorm.fit(x, floc=1, fix_s=0.75) + assert_equal(shape, 0.75) + assert_equal(loc, 1) + assert_allclose(scale, np.exp(lnxm1.mean()), rtol=1e-12) + + def test_uniform_fit(self): + x = np.array([1.0, 1.1, 1.2, 9.0]) + + loc, scale = stats.uniform.fit(x) + assert_equal(loc, x.min()) + assert_equal(scale, np.ptp(x)) + + loc, scale = stats.uniform.fit(x, floc=0) + assert_equal(loc, 0) + assert_equal(scale, x.max()) + + loc, scale = stats.uniform.fit(x, fscale=10) + assert_equal(loc, 0) + assert_equal(scale, 10) + + assert_raises(ValueError, stats.uniform.fit, x, floc=2.0) + assert_raises(ValueError, stats.uniform.fit, x, fscale=5.0) + + @pytest.mark.slow + @pytest.mark.parametrize("method", ["MLE", "MM"]) + def test_fshapes(self, method): + # take a beta distribution, with shapes='a, b', and make sure that + # fa is equivalent to f0, and fb is equivalent to f1 + a, b = 3., 4. + x = stats.beta.rvs(a, b, size=100, random_state=1234) + res_1 = stats.beta.fit(x, f0=3., method=method) + res_2 = stats.beta.fit(x, fa=3., method=method) + assert_allclose(res_1, res_2, atol=1e-12, rtol=1e-12) + + res_2 = stats.beta.fit(x, fix_a=3., method=method) + assert_allclose(res_1, res_2, atol=1e-12, rtol=1e-12) + + res_3 = stats.beta.fit(x, f1=4., method=method) + res_4 = stats.beta.fit(x, fb=4., method=method) + assert_allclose(res_3, res_4, atol=1e-12, rtol=1e-12) + + res_4 = stats.beta.fit(x, fix_b=4., method=method) + assert_allclose(res_3, res_4, atol=1e-12, rtol=1e-12) + + # cannot specify both positional and named args at the same time + assert_raises(ValueError, stats.beta.fit, x, fa=1, f0=2, method=method) + + # check that attempting to fix all parameters raises a ValueError + assert_raises(ValueError, stats.beta.fit, x, fa=0, f1=1, + floc=2, fscale=3, method=method) + + # check that specifying floc, fscale and fshapes works for + # beta and gamma which override the generic fit method + res_5 = stats.beta.fit(x, fa=3., floc=0, fscale=1, method=method) + aa, bb, ll, ss = res_5 + assert_equal([aa, ll, ss], [3., 0, 1]) + + # gamma distribution + a = 3. + data = stats.gamma.rvs(a, size=100) + aa, ll, ss = stats.gamma.fit(data, fa=a, method=method) + assert_equal(aa, a) + + @pytest.mark.parametrize("method", ["MLE", "MM"]) + def test_extra_params(self, method): + # unknown parameters should raise rather than be silently ignored + dist = stats.exponnorm + data = dist.rvs(K=2, size=100) + dct = dict(enikibeniki=-101) + assert_raises(TypeError, dist.fit, data, **dct, method=method) + + +class TestFrozen: + def setup_method(self): + np.random.seed(1234) + + # Test that a frozen distribution gives the same results as the original + # object. + # + # Only tested for the normal distribution (with loc and scale specified) + # and for the gamma distribution (with a shape parameter specified). + def test_norm(self): + dist = stats.norm + frozen = stats.norm(loc=10.0, scale=3.0) + + result_f = frozen.pdf(20.0) + result = dist.pdf(20.0, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.cdf(20.0) + result = dist.cdf(20.0, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.ppf(0.25) + result = dist.ppf(0.25, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.isf(0.25) + result = dist.isf(0.25, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.sf(10.0) + result = dist.sf(10.0, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.median() + result = dist.median(loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.mean() + result = dist.mean(loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.var() + result = dist.var(loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.std() + result = dist.std(loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.entropy() + result = dist.entropy(loc=10.0, scale=3.0) + assert_equal(result_f, result) + + result_f = frozen.moment(2) + result = dist.moment(2, loc=10.0, scale=3.0) + assert_equal(result_f, result) + + assert_equal(frozen.a, dist.a) + assert_equal(frozen.b, dist.b) + + def test_gamma(self): + a = 2.0 + dist = stats.gamma + frozen = stats.gamma(a) + + result_f = frozen.pdf(20.0) + result = dist.pdf(20.0, a) + assert_equal(result_f, result) + + result_f = frozen.cdf(20.0) + result = dist.cdf(20.0, a) + assert_equal(result_f, result) + + result_f = frozen.ppf(0.25) + result = dist.ppf(0.25, a) + assert_equal(result_f, result) + + result_f = frozen.isf(0.25) + result = dist.isf(0.25, a) + assert_equal(result_f, result) + + result_f = frozen.sf(10.0) + result = dist.sf(10.0, a) + assert_equal(result_f, result) + + result_f = frozen.median() + result = dist.median(a) + assert_equal(result_f, result) + + result_f = frozen.mean() + result = dist.mean(a) + assert_equal(result_f, result) + + result_f = frozen.var() + result = dist.var(a) + assert_equal(result_f, result) + + result_f = frozen.std() + result = dist.std(a) + assert_equal(result_f, result) + + result_f = frozen.entropy() + result = dist.entropy(a) + assert_equal(result_f, result) + + result_f = frozen.moment(2) + result = dist.moment(2, a) + assert_equal(result_f, result) + + assert_equal(frozen.a, frozen.dist.a) + assert_equal(frozen.b, frozen.dist.b) + + def test_regression_ticket_1293(self): + # Create a frozen distribution. + frozen = stats.lognorm(1) + # Call one of its methods that does not take any keyword arguments. + m1 = frozen.moment(2) + # Now call a method that takes a keyword argument. + frozen.stats(moments='mvsk') + # Call moment(2) again. + # After calling stats(), the following was raising an exception. + # So this test passes if the following does not raise an exception. + m2 = frozen.moment(2) + # The following should also be true, of course. But it is not + # the focus of this test. + assert_equal(m1, m2) + + def test_ab(self): + # test that the support of a frozen distribution + # (i) remains frozen even if it changes for the original one + # (ii) is actually correct if the shape parameters are such that + # the values of [a, b] are not the default [0, inf] + # take a genpareto as an example where the support + # depends on the value of the shape parameter: + # for c > 0: a, b = 0, inf + # for c < 0: a, b = 0, -1/c + + c = -0.1 + rv = stats.genpareto(c=c) + a, b = rv.dist._get_support(c) + assert_equal([a, b], [0., 10.]) + + c = 0.1 + stats.genpareto.pdf(0, c=c) + assert_equal(rv.dist._get_support(c), [0, np.inf]) + + c = -0.1 + rv = stats.genpareto(c=c) + a, b = rv.dist._get_support(c) + assert_equal([a, b], [0., 10.]) + + c = 0.1 + stats.genpareto.pdf(0, c) # this should NOT change genpareto.b + assert_equal((rv.dist.a, rv.dist.b), stats.genpareto._get_support(c)) + + rv1 = stats.genpareto(c=0.1) + assert_(rv1.dist is not rv.dist) + + # c >= 0: a, b = [0, inf] + for c in [1., 0.]: + c = np.asarray(c) + rv = stats.genpareto(c=c) + a, b = rv.a, rv.b + assert_equal(a, 0.) + assert_(np.isposinf(b)) + + # c < 0: a=0, b=1/|c| + c = np.asarray(-2.) + a, b = stats.genpareto._get_support(c) + assert_allclose([a, b], [0., 0.5]) + + def test_rv_frozen_in_namespace(self): + # Regression test for gh-3522 + assert_(hasattr(stats.distributions, 'rv_frozen')) + + def test_random_state(self): + # only check that the random_state attribute exists, + frozen = stats.norm() + assert_(hasattr(frozen, 'random_state')) + + # ... that it can be set, + frozen.random_state = 42 + assert_equal(frozen.random_state.get_state(), + np.random.RandomState(42).get_state()) + + # ... and that .rvs method accepts it as an argument + rndm = np.random.RandomState(1234) + frozen.rvs(size=8, random_state=rndm) + + def test_pickling(self): + # test that a frozen instance pickles and unpickles + # (this method is a clone of common_tests.check_pickling) + beta = stats.beta(2.3098496451481823, 0.62687954300963677) + poiss = stats.poisson(3.) + sample = stats.rv_discrete(values=([0, 1, 2, 3], + [0.1, 0.2, 0.3, 0.4])) + + for distfn in [beta, poiss, sample]: + distfn.random_state = 1234 + distfn.rvs(size=8) + s = pickle.dumps(distfn) + r0 = distfn.rvs(size=8) + + unpickled = pickle.loads(s) + r1 = unpickled.rvs(size=8) + assert_equal(r0, r1) + + # also smoke test some methods + medians = [distfn.ppf(0.5), unpickled.ppf(0.5)] + assert_equal(medians[0], medians[1]) + assert_equal(distfn.cdf(medians[0]), + unpickled.cdf(medians[1])) + + def test_expect(self): + # smoke test the expect method of the frozen distribution + # only take a gamma w/loc and scale and poisson with loc specified + def func(x): + return x + + gm = stats.gamma(a=2, loc=3, scale=4) + with np.errstate(invalid="ignore", divide="ignore"): + gm_val = gm.expect(func, lb=1, ub=2, conditional=True) + gamma_val = stats.gamma.expect(func, args=(2,), loc=3, scale=4, + lb=1, ub=2, conditional=True) + assert_allclose(gm_val, gamma_val) + + p = stats.poisson(3, loc=4) + p_val = p.expect(func) + poisson_val = stats.poisson.expect(func, args=(3,), loc=4) + assert_allclose(p_val, poisson_val) + + +class TestExpect: + # Test for expect method. + # + # Uses normal distribution and beta distribution for finite bounds, and + # hypergeom for discrete distribution with finite support + def test_norm(self): + v = stats.norm.expect(lambda x: (x-5)*(x-5), loc=5, scale=2) + assert_almost_equal(v, 4, decimal=14) + + m = stats.norm.expect(lambda x: (x), loc=5, scale=2) + assert_almost_equal(m, 5, decimal=14) + + lb = stats.norm.ppf(0.05, loc=5, scale=2) + ub = stats.norm.ppf(0.95, loc=5, scale=2) + prob90 = stats.norm.expect(lambda x: 1, loc=5, scale=2, lb=lb, ub=ub) + assert_almost_equal(prob90, 0.9, decimal=14) + + prob90c = stats.norm.expect(lambda x: 1, loc=5, scale=2, lb=lb, ub=ub, + conditional=True) + assert_almost_equal(prob90c, 1., decimal=14) + + def test_beta(self): + # case with finite support interval + v = stats.beta.expect(lambda x: (x-19/3.)*(x-19/3.), args=(10, 5), + loc=5, scale=2) + assert_almost_equal(v, 1./18., decimal=13) + + m = stats.beta.expect(lambda x: x, args=(10, 5), loc=5., scale=2.) + assert_almost_equal(m, 19/3., decimal=13) + + ub = stats.beta.ppf(0.95, 10, 10, loc=5, scale=2) + lb = stats.beta.ppf(0.05, 10, 10, loc=5, scale=2) + prob90 = stats.beta.expect(lambda x: 1., args=(10, 10), loc=5., + scale=2., lb=lb, ub=ub, conditional=False) + assert_almost_equal(prob90, 0.9, decimal=13) + + prob90c = stats.beta.expect(lambda x: 1, args=(10, 10), loc=5, + scale=2, lb=lb, ub=ub, conditional=True) + assert_almost_equal(prob90c, 1., decimal=13) + + def test_hypergeom(self): + # test case with finite bounds + + # without specifying bounds + m_true, v_true = stats.hypergeom.stats(20, 10, 8, loc=5.) + m = stats.hypergeom.expect(lambda x: x, args=(20, 10, 8), loc=5.) + assert_almost_equal(m, m_true, decimal=13) + + v = stats.hypergeom.expect(lambda x: (x-9.)**2, args=(20, 10, 8), + loc=5.) + assert_almost_equal(v, v_true, decimal=14) + + # with bounds, bounds equal to shifted support + v_bounds = stats.hypergeom.expect(lambda x: (x-9.)**2, + args=(20, 10, 8), + loc=5., lb=5, ub=13) + assert_almost_equal(v_bounds, v_true, decimal=14) + + # drop boundary points + prob_true = 1-stats.hypergeom.pmf([5, 13], 20, 10, 8, loc=5).sum() + prob_bounds = stats.hypergeom.expect(lambda x: 1, args=(20, 10, 8), + loc=5., lb=6, ub=12) + assert_almost_equal(prob_bounds, prob_true, decimal=13) + + # conditional + prob_bc = stats.hypergeom.expect(lambda x: 1, args=(20, 10, 8), loc=5., + lb=6, ub=12, conditional=True) + assert_almost_equal(prob_bc, 1, decimal=14) + + # check simple integral + prob_b = stats.hypergeom.expect(lambda x: 1, args=(20, 10, 8), + lb=0, ub=8) + assert_almost_equal(prob_b, 1, decimal=13) + + def test_poisson(self): + # poisson, use lower bound only + prob_bounds = stats.poisson.expect(lambda x: 1, args=(2,), lb=3, + conditional=False) + prob_b_true = 1-stats.poisson.cdf(2, 2) + assert_almost_equal(prob_bounds, prob_b_true, decimal=14) + + prob_lb = stats.poisson.expect(lambda x: 1, args=(2,), lb=2, + conditional=True) + assert_almost_equal(prob_lb, 1, decimal=14) + + def test_genhalflogistic(self): + # genhalflogistic, changes upper bound of support in _argcheck + # regression test for gh-2622 + halflog = stats.genhalflogistic + # check consistency when calling expect twice with the same input + res1 = halflog.expect(args=(1.5,)) + halflog.expect(args=(0.5,)) + res2 = halflog.expect(args=(1.5,)) + assert_almost_equal(res1, res2, decimal=14) + + def test_rice_overflow(self): + # rice.pdf(999, 0.74) was inf since special.i0 silentyly overflows + # check that using i0e fixes it + assert_(np.isfinite(stats.rice.pdf(999, 0.74))) + + assert_(np.isfinite(stats.rice.expect(lambda x: 1, args=(0.74,)))) + assert_(np.isfinite(stats.rice.expect(lambda x: 2, args=(0.74,)))) + assert_(np.isfinite(stats.rice.expect(lambda x: 3, args=(0.74,)))) + + def test_logser(self): + # test a discrete distribution with infinite support and loc + p, loc = 0.3, 3 + res_0 = stats.logser.expect(lambda k: k, args=(p,)) + # check against the correct answer (sum of a geom series) + assert_allclose(res_0, + p / (p - 1.) / np.log(1. - p), atol=1e-15) + + # now check it with `loc` + res_l = stats.logser.expect(lambda k: k, args=(p,), loc=loc) + assert_allclose(res_l, res_0 + loc, atol=1e-15) + + def test_skellam(self): + # Use a discrete distribution w/ bi-infinite support. Compute two first + # moments and compare to known values (cf skellam.stats) + p1, p2 = 18, 22 + m1 = stats.skellam.expect(lambda x: x, args=(p1, p2)) + m2 = stats.skellam.expect(lambda x: x**2, args=(p1, p2)) + assert_allclose(m1, p1 - p2, atol=1e-12) + assert_allclose(m2 - m1**2, p1 + p2, atol=1e-12) + + def test_randint(self): + # Use a discrete distribution w/ parameter-dependent support, which + # is larger than the default chunksize + lo, hi = 0, 113 + res = stats.randint.expect(lambda x: x, (lo, hi)) + assert_allclose(res, + sum(_ for _ in range(lo, hi)) / (hi - lo), atol=1e-15) + + def test_zipf(self): + # Test that there is no infinite loop even if the sum diverges + assert_warns(RuntimeWarning, stats.zipf.expect, + lambda x: x**2, (2,)) + + def test_discrete_kwds(self): + # check that discrete expect accepts keywords to control the summation + n0 = stats.poisson.expect(lambda x: 1, args=(2,)) + n1 = stats.poisson.expect(lambda x: 1, args=(2,), + maxcount=1001, chunksize=32, tolerance=1e-8) + assert_almost_equal(n0, n1, decimal=14) + + def test_moment(self): + # test the .moment() method: compute a higher moment and compare to + # a known value + def poiss_moment5(mu): + return mu**5 + 10*mu**4 + 25*mu**3 + 15*mu**2 + mu + + for mu in [5, 7]: + m5 = stats.poisson.moment(5, mu) + assert_allclose(m5, poiss_moment5(mu), rtol=1e-10) + + def test_challenging_cases_gh8928(self): + # Several cases where `expect` failed to produce a correct result were + # reported in gh-8928. Check that these cases have been resolved. + assert_allclose(stats.norm.expect(loc=36, scale=1.0), 36) + assert_allclose(stats.norm.expect(loc=40, scale=1.0), 40) + assert_allclose(stats.norm.expect(loc=10, scale=0.1), 10) + assert_allclose(stats.gamma.expect(args=(148,)), 148) + assert_allclose(stats.logistic.expect(loc=85), 85) + + def test_lb_ub_gh15855(self): + # Make sure changes to `expect` made in gh15855 treat lb/ub correctly + dist = stats.uniform + ref = dist.mean(loc=10, scale=5) # 12.5 + # moment over whole distribution + assert_allclose(dist.expect(loc=10, scale=5), ref) + # moment over whole distribution, lb and ub outside of support + assert_allclose(dist.expect(loc=10, scale=5, lb=9, ub=16), ref) + # moment over 60% of distribution, [lb, ub] centered within support + assert_allclose(dist.expect(loc=10, scale=5, lb=11, ub=14), ref*0.6) + # moment over truncated distribution, essentially + assert_allclose(dist.expect(loc=10, scale=5, lb=11, ub=14, + conditional=True), ref) + # moment over 40% of distribution, [lb, ub] not centered within support + assert_allclose(dist.expect(loc=10, scale=5, lb=11, ub=13), 12*0.4) + # moment with lb > ub + assert_allclose(dist.expect(loc=10, scale=5, lb=13, ub=11), -12*0.4) + # moment with lb > ub, conditional + assert_allclose(dist.expect(loc=10, scale=5, lb=13, ub=11, + conditional=True), 12) + + +class TestNct: + def test_nc_parameter(self): + # Parameter values c<=0 were not enabled (gh-2402). + # For negative values c and for c=0 results of rv.cdf(0) below were nan + rv = stats.nct(5, 0) + assert_equal(rv.cdf(0), 0.5) + rv = stats.nct(5, -1) + assert_almost_equal(rv.cdf(0), 0.841344746069, decimal=10) + + def test_broadcasting(self): + res = stats.nct.pdf(5, np.arange(4, 7)[:, None], + np.linspace(0.1, 1, 4)) + expected = array([[0.00321886, 0.00557466, 0.00918418, 0.01442997], + [0.00217142, 0.00395366, 0.00683888, 0.01126276], + [0.00153078, 0.00291093, 0.00525206, 0.00900815]]) + assert_allclose(res, expected, rtol=1e-5) + + def test_variance_gh_issue_2401(self): + # Computation of the variance of a non-central t-distribution resulted + # in a TypeError: ufunc 'isinf' not supported for the input types, + # and the inputs could not be safely coerced to any supported types + # according to the casting rule 'safe' + rv = stats.nct(4, 0) + assert_equal(rv.var(), 2.0) + + def test_nct_inf_moments(self): + # n-th moment of nct only exists for df > n + m, v, s, k = stats.nct.stats(df=0.9, nc=0.3, moments='mvsk') + assert_equal([m, v, s, k], [np.nan, np.nan, np.nan, np.nan]) + + m, v, s, k = stats.nct.stats(df=1.9, nc=0.3, moments='mvsk') + assert_(np.isfinite(m)) + assert_equal([v, s, k], [np.nan, np.nan, np.nan]) + + m, v, s, k = stats.nct.stats(df=3.1, nc=0.3, moments='mvsk') + assert_(np.isfinite([m, v, s]).all()) + assert_equal(k, np.nan) + + def test_nct_stats_large_df_values(self): + # previously gamma function was used which lost precision at df=345 + # cf. https://github.com/scipy/scipy/issues/12919 for details + nct_mean_df_1000 = stats.nct.mean(1000, 2) + nct_stats_df_1000 = stats.nct.stats(1000, 2) + # These expected values were computed with mpmath. They were also + # verified with the Wolfram Alpha expressions: + # Mean[NoncentralStudentTDistribution[1000, 2]] + # Var[NoncentralStudentTDistribution[1000, 2]] + expected_stats_df_1000 = [2.0015015641422464, 1.0040115288163005] + assert_allclose(nct_mean_df_1000, expected_stats_df_1000[0], + rtol=1e-10) + assert_allclose(nct_stats_df_1000, expected_stats_df_1000, + rtol=1e-10) + # and a bigger df value + nct_mean = stats.nct.mean(100000, 2) + nct_stats = stats.nct.stats(100000, 2) + # These expected values were computed with mpmath. + expected_stats = [2.0000150001562518, 1.0000400011500288] + assert_allclose(nct_mean, expected_stats[0], rtol=1e-10) + assert_allclose(nct_stats, expected_stats, rtol=1e-9) + + def test_cdf_large_nc(self): + # gh-17916 reported a crash with large `nc` values + assert_allclose(stats.nct.cdf(2, 2, float(2**16)), 0) + + +class TestRecipInvGauss: + + def test_pdf_endpoint(self): + p = stats.recipinvgauss.pdf(0, 0.6) + assert p == 0.0 + + def test_logpdf_endpoint(self): + logp = stats.recipinvgauss.logpdf(0, 0.6) + assert logp == -np.inf + + def test_cdf_small_x(self): + # The expected value was computer with mpmath: + # + # import mpmath + # + # mpmath.mp.dps = 100 + # + # def recipinvgauss_cdf_mp(x, mu): + # x = mpmath.mpf(x) + # mu = mpmath.mpf(mu) + # trm1 = 1/mu - x + # trm2 = 1/mu + x + # isqx = 1/mpmath.sqrt(x) + # return (mpmath.ncdf(-isqx*trm1) + # - mpmath.exp(2/mu)*mpmath.ncdf(-isqx*trm2)) + # + p = stats.recipinvgauss.cdf(0.05, 0.5) + expected = 6.590396159501331e-20 + assert_allclose(p, expected, rtol=1e-14) + + def test_sf_large_x(self): + # The expected value was computed with mpmath; see test_cdf_small. + p = stats.recipinvgauss.sf(80, 0.5) + expected = 2.699819200556787e-18 + assert_allclose(p, expected, 5e-15) + + +class TestRice: + def test_rice_zero_b(self): + # rice distribution should work with b=0, cf gh-2164 + x = [0.2, 1., 5.] + assert_(np.isfinite(stats.rice.pdf(x, b=0.)).all()) + assert_(np.isfinite(stats.rice.logpdf(x, b=0.)).all()) + assert_(np.isfinite(stats.rice.cdf(x, b=0.)).all()) + assert_(np.isfinite(stats.rice.logcdf(x, b=0.)).all()) + + q = [0.1, 0.1, 0.5, 0.9] + assert_(np.isfinite(stats.rice.ppf(q, b=0.)).all()) + + mvsk = stats.rice.stats(0, moments='mvsk') + assert_(np.isfinite(mvsk).all()) + + # furthermore, pdf is continuous as b\to 0 + # rice.pdf(x, b\to 0) = x exp(-x^2/2) + O(b^2) + # see e.g. Abramovich & Stegun 9.6.7 & 9.6.10 + b = 1e-8 + assert_allclose(stats.rice.pdf(x, 0), stats.rice.pdf(x, b), + atol=b, rtol=0) + + def test_rice_rvs(self): + rvs = stats.rice.rvs + assert_equal(rvs(b=3.).size, 1) + assert_equal(rvs(b=3., size=(3, 5)).shape, (3, 5)) + + def test_rice_gh9836(self): + # test that gh-9836 is resolved; previously jumped to 1 at the end + + cdf = stats.rice.cdf(np.arange(10, 160, 10), np.arange(10, 160, 10)) + # Generated in R + # library(VGAM) + # options(digits=16) + # x = seq(10, 150, 10) + # print(price(x, sigma=1, vee=x)) + cdf_exp = [0.4800278103504522, 0.4900233218590353, 0.4933500379379548, + 0.4950128317658719, 0.4960103776798502, 0.4966753655438764, + 0.4971503395812474, 0.4975065620443196, 0.4977836197921638, + 0.4980052636649550, 0.4981866072661382, 0.4983377260666599, + 0.4984655952615694, 0.4985751970541413, 0.4986701850071265] + assert_allclose(cdf, cdf_exp) + + probabilities = np.arange(0.1, 1, 0.1) + ppf = stats.rice.ppf(probabilities, 500/4, scale=4) + # Generated in R + # library(VGAM) + # options(digits=16) + # p = seq(0.1, .9, by = .1) + # print(qrice(p, vee = 500, sigma = 4)) + ppf_exp = [494.8898762347361, 496.6495690858350, 497.9184315188069, + 499.0026277378915, 500.0159999146250, 501.0293721352668, + 502.1135684981884, 503.3824312270405, 505.1421247157822] + assert_allclose(ppf, ppf_exp) + + ppf = scipy.stats.rice.ppf(0.5, np.arange(10, 150, 10)) + # Generated in R + # library(VGAM) + # options(digits=16) + # b <- seq(10, 140, 10) + # print(qrice(0.5, vee = b, sigma = 1)) + ppf_exp = [10.04995862522287, 20.02499480078302, 30.01666512465732, + 40.01249934924363, 50.00999966676032, 60.00833314046875, + 70.00714273568241, 80.00624991862573, 90.00555549840364, + 100.00499995833597, 110.00454542324384, 120.00416664255323, + 130.00384613488120, 140.00357141338748] + assert_allclose(ppf, ppf_exp) + + +class TestErlang: + def setup_method(self): + np.random.seed(1234) + + def test_erlang_runtimewarning(self): + # erlang should generate a RuntimeWarning if a non-integer + # shape parameter is used. + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + + # The non-integer shape parameter 1.3 should trigger a + # RuntimeWarning + assert_raises(RuntimeWarning, + stats.erlang.rvs, 1.3, loc=0, scale=1, size=4) + + # Calling the fit method with `f0` set to an integer should + # *not* trigger a RuntimeWarning. It should return the same + # values as gamma.fit(...). + data = [0.5, 1.0, 2.0, 4.0] + result_erlang = stats.erlang.fit(data, f0=1) + result_gamma = stats.gamma.fit(data, f0=1) + assert_allclose(result_erlang, result_gamma, rtol=1e-3) + + def test_gh_pr_10949_argcheck(self): + assert_equal(stats.erlang.pdf(0.5, a=[1, -1]), + stats.gamma.pdf(0.5, a=[1, -1])) + + +class TestRayleigh: + def setup_method(self): + np.random.seed(987654321) + + # gh-6227 + def test_logpdf(self): + y = stats.rayleigh.logpdf(50) + assert_allclose(y, -1246.0879769945718) + + def test_logsf(self): + y = stats.rayleigh.logsf(50) + assert_allclose(y, -1250) + + @pytest.mark.parametrize("rvs_loc,rvs_scale", [(0.85373171, 0.86932204), + (0.20558821, 0.61621008)]) + def test_fit(self, rvs_loc, rvs_scale): + data = stats.rayleigh.rvs(size=250, loc=rvs_loc, scale=rvs_scale) + + def scale_mle(data, floc): + return (np.sum((data - floc) ** 2) / (2 * len(data))) ** .5 + + # when `floc` is provided, `scale` is found with an analytical formula + scale_expect = scale_mle(data, rvs_loc) + loc, scale = stats.rayleigh.fit(data, floc=rvs_loc) + assert_equal(loc, rvs_loc) + assert_equal(scale, scale_expect) + + # when `fscale` is fixed, superclass fit is used to determine `loc`. + loc, scale = stats.rayleigh.fit(data, fscale=.6) + assert_equal(scale, .6) + + # with both parameters free, one dimensional optimization is done + # over a new function that takes into account the dependent relation + # of `scale` to `loc`. + loc, scale = stats.rayleigh.fit(data) + # test that `scale` is defined by its relation to `loc` + assert_equal(scale, scale_mle(data, loc)) + + @pytest.mark.parametrize("rvs_loc,rvs_scale", [[0.74, 0.01], + [0.08464463, 0.12069025]]) + def test_fit_comparison_super_method(self, rvs_loc, rvs_scale): + # test that the objective function result of the analytical MLEs is + # less than or equal to that of the numerically optimized estimate + data = stats.rayleigh.rvs(size=250, loc=rvs_loc, scale=rvs_scale) + _assert_less_or_close_loglike(stats.rayleigh, data) + + def test_fit_warnings(self): + assert_fit_warnings(stats.rayleigh) + + def test_fit_gh17088(self): + # `rayleigh.fit` could return a location that was inconsistent with + # the data. See gh-17088. + rng = np.random.default_rng(456) + loc, scale, size = 50, 600, 500 + rvs = stats.rayleigh.rvs(loc, scale, size=size, random_state=rng) + loc_fit, _ = stats.rayleigh.fit(rvs) + assert loc_fit < np.min(rvs) + loc_fit, scale_fit = stats.rayleigh.fit(rvs, fscale=scale) + assert loc_fit < np.min(rvs) + assert scale_fit == scale + + +class TestExponWeib: + + def test_pdf_logpdf(self): + # Regression test for gh-3508. + x = 0.1 + a = 1.0 + c = 100.0 + p = stats.exponweib.pdf(x, a, c) + logp = stats.exponweib.logpdf(x, a, c) + # Expected values were computed with mpmath. + assert_allclose([p, logp], + [1.0000000000000054e-97, -223.35075402042244]) + + def test_a_is_1(self): + # For issue gh-3508. + # Check that when a=1, the pdf and logpdf methods of exponweib are the + # same as those of weibull_min. + x = np.logspace(-4, -1, 4) + a = 1 + c = 100 + + p = stats.exponweib.pdf(x, a, c) + expected = stats.weibull_min.pdf(x, c) + assert_allclose(p, expected) + + logp = stats.exponweib.logpdf(x, a, c) + expected = stats.weibull_min.logpdf(x, c) + assert_allclose(logp, expected) + + def test_a_is_1_c_is_1(self): + # When a = 1 and c = 1, the distribution is exponential. + x = np.logspace(-8, 1, 10) + a = 1 + c = 1 + + p = stats.exponweib.pdf(x, a, c) + expected = stats.expon.pdf(x) + assert_allclose(p, expected) + + logp = stats.exponweib.logpdf(x, a, c) + expected = stats.expon.logpdf(x) + assert_allclose(logp, expected) + + # Reference values were computed with mpmath, e.g: + # + # from mpmath import mp + # + # def mp_sf(x, a, c): + # x = mp.mpf(x) + # a = mp.mpf(a) + # c = mp.mpf(c) + # return -mp.powm1(-mp.expm1(-x**c)), a) + # + # mp.dps = 100 + # print(float(mp_sf(1, 2.5, 0.75))) + # + # prints + # + # 0.6823127476985246 + # + @pytest.mark.parametrize( + 'x, a, c, ref', + [(1, 2.5, 0.75, 0.6823127476985246), + (50, 2.5, 0.75, 1.7056666054719663e-08), + (125, 2.5, 0.75, 1.4534393150714602e-16), + (250, 2.5, 0.75, 1.2391389689773512e-27), + (250, 0.03125, 0.75, 1.548923711221689e-29), + (3, 0.03125, 3.0, 5.873527551689983e-14), + (2e80, 10.0, 0.02, 2.9449084156902135e-17)] + ) + def test_sf(self, x, a, c, ref): + sf = stats.exponweib.sf(x, a, c) + assert_allclose(sf, ref, rtol=1e-14) + + # Reference values were computed with mpmath, e.g. + # + # from mpmath import mp + # + # def mp_isf(p, a, c): + # p = mp.mpf(p) + # a = mp.mpf(a) + # c = mp.mpf(c) + # return (-mp.log(-mp.expm1(mp.log1p(-p)/a)))**(1/c) + # + # mp.dps = 100 + # print(float(mp_isf(0.25, 2.5, 0.75))) + # + # prints + # + # 2.8946008178158924 + # + @pytest.mark.parametrize( + 'p, a, c, ref', + [(0.25, 2.5, 0.75, 2.8946008178158924), + (3e-16, 2.5, 0.75, 121.77966713102938), + (1e-12, 1, 2, 5.256521769756932), + (2e-13, 0.03125, 3, 2.953915059484589), + (5e-14, 10.0, 0.02, 7.57094886384687e+75)] + ) + def test_isf(self, p, a, c, ref): + isf = stats.exponweib.isf(p, a, c) + assert_allclose(isf, ref, rtol=5e-14) + + +class TestFatigueLife: + + def test_sf_tail(self): + # Expected value computed with mpmath: + # import mpmath + # mpmath.mp.dps = 80 + # x = mpmath.mpf(800.0) + # c = mpmath.mpf(2.5) + # s = float(1 - mpmath.ncdf(1/c * (mpmath.sqrt(x) + # - 1/mpmath.sqrt(x)))) + # print(s) + # Output: + # 6.593376447038406e-30 + s = stats.fatiguelife.sf(800.0, 2.5) + assert_allclose(s, 6.593376447038406e-30, rtol=1e-13) + + def test_isf_tail(self): + # See test_sf_tail for the mpmath code. + p = 6.593376447038406e-30 + q = stats.fatiguelife.isf(p, 2.5) + assert_allclose(q, 800.0, rtol=1e-13) + + +class TestWeibull: + + def test_logpdf(self): + # gh-6217 + y = stats.weibull_min.logpdf(0, 1) + assert_equal(y, 0) + + def test_with_maxima_distrib(self): + # Tests for weibull_min and weibull_max. + # The expected values were computed using the symbolic algebra + # program 'maxima' with the package 'distrib', which has + # 'pdf_weibull' and 'cdf_weibull'. The mapping between the + # scipy and maxima functions is as follows: + # ----------------------------------------------------------------- + # scipy maxima + # --------------------------------- ------------------------------ + # weibull_min.pdf(x, a, scale=b) pdf_weibull(x, a, b) + # weibull_min.logpdf(x, a, scale=b) log(pdf_weibull(x, a, b)) + # weibull_min.cdf(x, a, scale=b) cdf_weibull(x, a, b) + # weibull_min.logcdf(x, a, scale=b) log(cdf_weibull(x, a, b)) + # weibull_min.sf(x, a, scale=b) 1 - cdf_weibull(x, a, b) + # weibull_min.logsf(x, a, scale=b) log(1 - cdf_weibull(x, a, b)) + # + # weibull_max.pdf(x, a, scale=b) pdf_weibull(-x, a, b) + # weibull_max.logpdf(x, a, scale=b) log(pdf_weibull(-x, a, b)) + # weibull_max.cdf(x, a, scale=b) 1 - cdf_weibull(-x, a, b) + # weibull_max.logcdf(x, a, scale=b) log(1 - cdf_weibull(-x, a, b)) + # weibull_max.sf(x, a, scale=b) cdf_weibull(-x, a, b) + # weibull_max.logsf(x, a, scale=b) log(cdf_weibull(-x, a, b)) + # ----------------------------------------------------------------- + x = 1.5 + a = 2.0 + b = 3.0 + + # weibull_min + + p = stats.weibull_min.pdf(x, a, scale=b) + assert_allclose(p, np.exp(-0.25)/3) + + lp = stats.weibull_min.logpdf(x, a, scale=b) + assert_allclose(lp, -0.25 - np.log(3)) + + c = stats.weibull_min.cdf(x, a, scale=b) + assert_allclose(c, -special.expm1(-0.25)) + + lc = stats.weibull_min.logcdf(x, a, scale=b) + assert_allclose(lc, np.log(-special.expm1(-0.25))) + + s = stats.weibull_min.sf(x, a, scale=b) + assert_allclose(s, np.exp(-0.25)) + + ls = stats.weibull_min.logsf(x, a, scale=b) + assert_allclose(ls, -0.25) + + # Also test using a large value x, for which computing the survival + # function using the CDF would result in 0. + s = stats.weibull_min.sf(30, 2, scale=3) + assert_allclose(s, np.exp(-100)) + + ls = stats.weibull_min.logsf(30, 2, scale=3) + assert_allclose(ls, -100) + + # weibull_max + x = -1.5 + + p = stats.weibull_max.pdf(x, a, scale=b) + assert_allclose(p, np.exp(-0.25)/3) + + lp = stats.weibull_max.logpdf(x, a, scale=b) + assert_allclose(lp, -0.25 - np.log(3)) + + c = stats.weibull_max.cdf(x, a, scale=b) + assert_allclose(c, np.exp(-0.25)) + + lc = stats.weibull_max.logcdf(x, a, scale=b) + assert_allclose(lc, -0.25) + + s = stats.weibull_max.sf(x, a, scale=b) + assert_allclose(s, -special.expm1(-0.25)) + + ls = stats.weibull_max.logsf(x, a, scale=b) + assert_allclose(ls, np.log(-special.expm1(-0.25))) + + # Also test using a value of x close to 0, for which computing the + # survival function using the CDF would result in 0. + s = stats.weibull_max.sf(-1e-9, 2, scale=3) + assert_allclose(s, -special.expm1(-1/9000000000000000000)) + + ls = stats.weibull_max.logsf(-1e-9, 2, scale=3) + assert_allclose(ls, np.log(-special.expm1(-1/9000000000000000000))) + + @pytest.mark.parametrize('scale', [1.0, 0.1]) + def test_delta_cdf(self, scale): + # Expected value computed with mpmath: + # + # def weibull_min_sf(x, k, scale): + # x = mpmath.mpf(x) + # k = mpmath.mpf(k) + # scale =mpmath.mpf(scale) + # return mpmath.exp(-(x/scale)**k) + # + # >>> import mpmath + # >>> mpmath.mp.dps = 60 + # >>> sf1 = weibull_min_sf(7.5, 3, 1) + # >>> sf2 = weibull_min_sf(8.0, 3, 1) + # >>> float(sf1 - sf2) + # 6.053624060118734e-184 + # + delta = stats.weibull_min._delta_cdf(scale*7.5, scale*8, 3, + scale=scale) + assert_allclose(delta, 6.053624060118734e-184) + + def test_fit_min(self): + rng = np.random.default_rng(5985959307161735394) + + c, loc, scale = 2, 3.5, 0.5 # arbitrary, valid parameters + dist = stats.weibull_min(c, loc, scale) + rvs = dist.rvs(size=100, random_state=rng) + + # test that MLE still honors guesses and fixed parameters + c2, loc2, scale2 = stats.weibull_min.fit(rvs, 1.5, floc=3) + c3, loc3, scale3 = stats.weibull_min.fit(rvs, 1.6, floc=3) + assert loc2 == loc3 == 3 # fixed parameter is respected + assert c2 != c3 # different guess -> (slightly) different outcome + # quality of fit is tested elsewhere + + # test that MoM honors fixed parameters, accepts (but ignores) guesses + c4, loc4, scale4 = stats.weibull_min.fit(rvs, 3, fscale=3, method='mm') + assert scale4 == 3 + # because scale was fixed, only the mean and skewness will be matched + dist4 = stats.weibull_min(c4, loc4, scale4) + res = dist4.stats(moments='ms') + ref = np.mean(rvs), stats.skew(rvs) + assert_allclose(res, ref) + + # reference values were computed via mpmath + # from mpmath import mp + # def weibull_sf_mpmath(x, c): + # x = mp.mpf(x) + # c = mp.mpf(c) + # return float(mp.exp(-x**c)) + + @pytest.mark.parametrize('x, c, ref', [(50, 1, 1.9287498479639178e-22), + (1000, 0.8, + 8.131269637872743e-110)]) + def test_sf_isf(self, x, c, ref): + assert_allclose(stats.weibull_min.sf(x, c), ref, rtol=5e-14) + assert_allclose(stats.weibull_min.isf(ref, c), x, rtol=5e-14) + + +class TestDweibull: + def test_entropy(self): + # Test that dweibull entropy follows that of weibull_min. + # (Generic tests check that the dweibull entropy is consistent + # with its PDF. As for accuracy, dweibull entropy should be just + # as accurate as weibull_min entropy. Checks of accuracy against + # a reference need only be applied to the fundamental distribution - + # weibull_min.) + rng = np.random.default_rng(8486259129157041777) + c = 10**rng.normal(scale=100, size=10) + res = stats.dweibull.entropy(c) + ref = stats.weibull_min.entropy(c) - np.log(0.5) + assert_allclose(res, ref, rtol=1e-15) + + def test_sf(self): + # test that for positive values the dweibull survival function is half + # the weibull_min survival function + rng = np.random.default_rng(8486259129157041777) + c = 10**rng.normal(scale=1, size=10) + x = 10 * rng.uniform() + res = stats.dweibull.sf(x, c) + ref = 0.5 * stats.weibull_min.sf(x, c) + assert_allclose(res, ref, rtol=1e-15) + + +class TestTruncWeibull: + + def test_pdf_bounds(self): + # test bounds + y = stats.truncweibull_min.pdf([0.1, 2.0], 2.0, 0.11, 1.99) + assert_equal(y, [0.0, 0.0]) + + def test_logpdf(self): + y = stats.truncweibull_min.logpdf(2.0, 1.0, 2.0, np.inf) + assert_equal(y, 0.0) + + # hand calculation + y = stats.truncweibull_min.logpdf(2.0, 1.0, 2.0, 4.0) + assert_allclose(y, 0.14541345786885884) + + def test_ppf_bounds(self): + # test bounds + y = stats.truncweibull_min.ppf([0.0, 1.0], 2.0, 0.1, 2.0) + assert_equal(y, [0.1, 2.0]) + + def test_cdf_to_ppf(self): + q = [0., 0.1, .25, 0.50, 0.75, 0.90, 1.] + x = stats.truncweibull_min.ppf(q, 2., 0., 3.) + q_out = stats.truncweibull_min.cdf(x, 2., 0., 3.) + assert_allclose(q, q_out) + + def test_sf_to_isf(self): + q = [0., 0.1, .25, 0.50, 0.75, 0.90, 1.] + x = stats.truncweibull_min.isf(q, 2., 0., 3.) + q_out = stats.truncweibull_min.sf(x, 2., 0., 3.) + assert_allclose(q, q_out) + + def test_munp(self): + c = 2. + a = 1. + b = 3. + + def xnpdf(x, n): + return x**n*stats.truncweibull_min.pdf(x, c, a, b) + + m0 = stats.truncweibull_min.moment(0, c, a, b) + assert_equal(m0, 1.) + + m1 = stats.truncweibull_min.moment(1, c, a, b) + m1_expected, _ = quad(lambda x: xnpdf(x, 1), a, b) + assert_allclose(m1, m1_expected) + + m2 = stats.truncweibull_min.moment(2, c, a, b) + m2_expected, _ = quad(lambda x: xnpdf(x, 2), a, b) + assert_allclose(m2, m2_expected) + + m3 = stats.truncweibull_min.moment(3, c, a, b) + m3_expected, _ = quad(lambda x: xnpdf(x, 3), a, b) + assert_allclose(m3, m3_expected) + + m4 = stats.truncweibull_min.moment(4, c, a, b) + m4_expected, _ = quad(lambda x: xnpdf(x, 4), a, b) + assert_allclose(m4, m4_expected) + + def test_reference_values(self): + a = 1. + b = 3. + c = 2. + x_med = np.sqrt(1 - np.log(0.5 + np.exp(-(8. + np.log(2.))))) + + cdf = stats.truncweibull_min.cdf(x_med, c, a, b) + assert_allclose(cdf, 0.5) + + lc = stats.truncweibull_min.logcdf(x_med, c, a, b) + assert_allclose(lc, -np.log(2.)) + + ppf = stats.truncweibull_min.ppf(0.5, c, a, b) + assert_allclose(ppf, x_med) + + sf = stats.truncweibull_min.sf(x_med, c, a, b) + assert_allclose(sf, 0.5) + + ls = stats.truncweibull_min.logsf(x_med, c, a, b) + assert_allclose(ls, -np.log(2.)) + + isf = stats.truncweibull_min.isf(0.5, c, a, b) + assert_allclose(isf, x_med) + + def test_compare_weibull_min(self): + # Verify that the truncweibull_min distribution gives the same results + # as the original weibull_min + x = 1.5 + c = 2.0 + a = 0.0 + b = np.inf + scale = 3.0 + + p = stats.weibull_min.pdf(x, c, scale=scale) + p_trunc = stats.truncweibull_min.pdf(x, c, a, b, scale=scale) + assert_allclose(p, p_trunc) + + lp = stats.weibull_min.logpdf(x, c, scale=scale) + lp_trunc = stats.truncweibull_min.logpdf(x, c, a, b, scale=scale) + assert_allclose(lp, lp_trunc) + + cdf = stats.weibull_min.cdf(x, c, scale=scale) + cdf_trunc = stats.truncweibull_min.cdf(x, c, a, b, scale=scale) + assert_allclose(cdf, cdf_trunc) + + lc = stats.weibull_min.logcdf(x, c, scale=scale) + lc_trunc = stats.truncweibull_min.logcdf(x, c, a, b, scale=scale) + assert_allclose(lc, lc_trunc) + + s = stats.weibull_min.sf(x, c, scale=scale) + s_trunc = stats.truncweibull_min.sf(x, c, a, b, scale=scale) + assert_allclose(s, s_trunc) + + ls = stats.weibull_min.logsf(x, c, scale=scale) + ls_trunc = stats.truncweibull_min.logsf(x, c, a, b, scale=scale) + assert_allclose(ls, ls_trunc) + + # # Also test using a large value x, for which computing the survival + # # function using the CDF would result in 0. + s = stats.truncweibull_min.sf(30, 2, a, b, scale=3) + assert_allclose(s, np.exp(-100)) + + ls = stats.truncweibull_min.logsf(30, 2, a, b, scale=3) + assert_allclose(ls, -100) + + def test_compare_weibull_min2(self): + # Verify that the truncweibull_min distribution PDF and CDF results + # are the same as those calculated from truncating weibull_min + c, a, b = 2.5, 0.25, 1.25 + x = np.linspace(a, b, 100) + + pdf1 = stats.truncweibull_min.pdf(x, c, a, b) + cdf1 = stats.truncweibull_min.cdf(x, c, a, b) + + norm = stats.weibull_min.cdf(b, c) - stats.weibull_min.cdf(a, c) + pdf2 = stats.weibull_min.pdf(x, c) / norm + cdf2 = (stats.weibull_min.cdf(x, c) - stats.weibull_min.cdf(a, c))/norm + + np.testing.assert_allclose(pdf1, pdf2) + np.testing.assert_allclose(cdf1, cdf2) + + +class TestRdist: + def test_rdist_cdf_gh1285(self): + # check workaround in rdist._cdf for issue gh-1285. + distfn = stats.rdist + values = [0.001, 0.5, 0.999] + assert_almost_equal(distfn.cdf(distfn.ppf(values, 541.0), 541.0), + values, decimal=5) + + def test_rdist_beta(self): + # rdist is a special case of stats.beta + x = np.linspace(-0.99, 0.99, 10) + c = 2.7 + assert_almost_equal(0.5*stats.beta(c/2, c/2).pdf((x + 1)/2), + stats.rdist(c).pdf(x)) + + # reference values were computed via mpmath + # from mpmath import mp + # mp.dps = 200 + # def rdist_sf_mpmath(x, c): + # x = mp.mpf(x) + # c = mp.mpf(c) + # return float(mp.betainc(c/2, c/2, (x+1)/2, mp.one, regularized=True)) + @pytest.mark.parametrize( + "x, c, ref", + [ + (0.0001, 541, 0.49907251345565845), + (0.1, 241, 0.06000788166249205), + (0.5, 441, 1.0655898106047832e-29), + (0.8, 341, 6.025478373732215e-78), + ] + ) + def test_rdist_sf(self, x, c, ref): + assert_allclose(stats.rdist.sf(x, c), ref, rtol=5e-14) + + +class TestTrapezoid: + def test_reduces_to_triang(self): + modes = [0, 0.3, 0.5, 1] + for mode in modes: + x = [0, mode, 1] + assert_almost_equal(stats.trapezoid.pdf(x, mode, mode), + stats.triang.pdf(x, mode)) + assert_almost_equal(stats.trapezoid.cdf(x, mode, mode), + stats.triang.cdf(x, mode)) + + def test_reduces_to_uniform(self): + x = np.linspace(0, 1, 10) + assert_almost_equal(stats.trapezoid.pdf(x, 0, 1), stats.uniform.pdf(x)) + assert_almost_equal(stats.trapezoid.cdf(x, 0, 1), stats.uniform.cdf(x)) + + def test_cases(self): + # edge cases + assert_almost_equal(stats.trapezoid.pdf(0, 0, 0), 2) + assert_almost_equal(stats.trapezoid.pdf(1, 1, 1), 2) + assert_almost_equal(stats.trapezoid.pdf(0.5, 0, 0.8), + 1.11111111111111111) + assert_almost_equal(stats.trapezoid.pdf(0.5, 0.2, 1.0), + 1.11111111111111111) + + # straightforward case + assert_almost_equal(stats.trapezoid.pdf(0.1, 0.2, 0.8), 0.625) + assert_almost_equal(stats.trapezoid.pdf(0.5, 0.2, 0.8), 1.25) + assert_almost_equal(stats.trapezoid.pdf(0.9, 0.2, 0.8), 0.625) + + assert_almost_equal(stats.trapezoid.cdf(0.1, 0.2, 0.8), 0.03125) + assert_almost_equal(stats.trapezoid.cdf(0.2, 0.2, 0.8), 0.125) + assert_almost_equal(stats.trapezoid.cdf(0.5, 0.2, 0.8), 0.5) + assert_almost_equal(stats.trapezoid.cdf(0.9, 0.2, 0.8), 0.96875) + assert_almost_equal(stats.trapezoid.cdf(1.0, 0.2, 0.8), 1.0) + + def test_moments_and_entropy(self): + # issue #11795: improve precision of trapezoid stats + # Apply formulas from Wikipedia for the following parameters: + a, b, c, d = -3, -1, 2, 3 # => 1/3, 5/6, -3, 6 + p1, p2, loc, scale = (b-a) / (d-a), (c-a) / (d-a), a, d-a + h = 2 / (d+c-b-a) + + def moment(n): + return (h * ((d**(n+2) - c**(n+2)) / (d-c) + - (b**(n+2) - a**(n+2)) / (b-a)) / + (n+1) / (n+2)) + + mean = moment(1) + var = moment(2) - mean**2 + entropy = 0.5 * (d-c+b-a) / (d+c-b-a) + np.log(0.5 * (d+c-b-a)) + assert_almost_equal(stats.trapezoid.mean(p1, p2, loc, scale), + mean, decimal=13) + assert_almost_equal(stats.trapezoid.var(p1, p2, loc, scale), + var, decimal=13) + assert_almost_equal(stats.trapezoid.entropy(p1, p2, loc, scale), + entropy, decimal=13) + + # Check boundary cases where scipy d=0 or d=1. + assert_almost_equal(stats.trapezoid.mean(0, 0, -3, 6), -1, decimal=13) + assert_almost_equal(stats.trapezoid.mean(0, 1, -3, 6), 0, decimal=13) + assert_almost_equal(stats.trapezoid.var(0, 1, -3, 6), 3, decimal=13) + + def test_trapezoid_vect(self): + # test that array-valued shapes and arguments are handled + c = np.array([0.1, 0.2, 0.3]) + d = np.array([0.5, 0.6])[:, None] + x = np.array([0.15, 0.25, 0.9]) + v = stats.trapezoid.pdf(x, c, d) + + cc, dd, xx = np.broadcast_arrays(c, d, x) + + res = np.empty(xx.size, dtype=xx.dtype) + ind = np.arange(xx.size) + for i, x1, c1, d1 in zip(ind, xx.ravel(), cc.ravel(), dd.ravel()): + res[i] = stats.trapezoid.pdf(x1, c1, d1) + + assert_allclose(v, res.reshape(v.shape), atol=1e-15) + + # Check that the stats() method supports vector arguments. + v = np.asarray(stats.trapezoid.stats(c, d, moments="mvsk")) + cc, dd = np.broadcast_arrays(c, d) + res = np.empty((cc.size, 4)) # 4 stats returned per value + ind = np.arange(cc.size) + for i, c1, d1 in zip(ind, cc.ravel(), dd.ravel()): + res[i] = stats.trapezoid.stats(c1, d1, moments="mvsk") + + assert_allclose(v, res.T.reshape(v.shape), atol=1e-15) + + def test_trapz(self): + # Basic test for alias + x = np.linspace(0, 1, 10) + assert_almost_equal(stats.trapz.pdf(x, 0, 1), stats.uniform.pdf(x)) + + +class TestTriang: + def test_edge_cases(self): + with np.errstate(all='raise'): + assert_equal(stats.triang.pdf(0, 0), 2.) + assert_equal(stats.triang.pdf(0.5, 0), 1.) + assert_equal(stats.triang.pdf(1, 0), 0.) + + assert_equal(stats.triang.pdf(0, 1), 0) + assert_equal(stats.triang.pdf(0.5, 1), 1.) + assert_equal(stats.triang.pdf(1, 1), 2) + + assert_equal(stats.triang.cdf(0., 0.), 0.) + assert_equal(stats.triang.cdf(0.5, 0.), 0.75) + assert_equal(stats.triang.cdf(1.0, 0.), 1.0) + + assert_equal(stats.triang.cdf(0., 1.), 0.) + assert_equal(stats.triang.cdf(0.5, 1.), 0.25) + assert_equal(stats.triang.cdf(1., 1.), 1) + + +class TestMaxwell: + + # reference values were computed with wolfram alpha + # erfc(x/sqrt(2)) + sqrt(2/pi) * x * e^(-x^2/2) + + @pytest.mark.parametrize("x, ref", + [(20, 2.2138865931011177e-86), + (0.01, 0.999999734046458435)]) + def test_sf(self, x, ref): + assert_allclose(stats.maxwell.sf(x), ref, rtol=1e-14) + + # reference values were computed with wolfram alpha + # sqrt(2) * sqrt(Q^(-1)(3/2, q)) + + @pytest.mark.parametrize("q, ref", + [(0.001, 4.033142223656157022), + (0.9999847412109375, 0.0385743284050381), + (2**-55, 8.95564974719481)]) + def test_isf(self, q, ref): + assert_allclose(stats.maxwell.isf(q), ref, rtol=1e-15) + + +class TestMielke: + def test_moments(self): + k, s = 4.642, 0.597 + # n-th moment exists only if n < s + assert_equal(stats.mielke(k, s).moment(1), np.inf) + assert_equal(stats.mielke(k, 1.0).moment(1), np.inf) + assert_(np.isfinite(stats.mielke(k, 1.01).moment(1))) + + def test_burr_equivalence(self): + x = np.linspace(0.01, 100, 50) + k, s = 2.45, 5.32 + assert_allclose(stats.burr.pdf(x, s, k/s), stats.mielke.pdf(x, k, s)) + + +class TestBurr: + def test_endpoints_7491(self): + # gh-7491 + # Compute the pdf at the left endpoint dst.a. + data = [ + [stats.fisk, (1,), 1], + [stats.burr, (0.5, 2), 1], + [stats.burr, (1, 1), 1], + [stats.burr, (2, 0.5), 1], + [stats.burr12, (1, 0.5), 0.5], + [stats.burr12, (1, 1), 1.0], + [stats.burr12, (1, 2), 2.0]] + + ans = [_f.pdf(_f.a, *_args) for _f, _args, _ in data] + correct = [_correct_ for _f, _args, _correct_ in data] + assert_array_almost_equal(ans, correct) + + ans = [_f.logpdf(_f.a, *_args) for _f, _args, _ in data] + correct = [np.log(_correct_) for _f, _args, _correct_ in data] + assert_array_almost_equal(ans, correct) + + def test_burr_stats_9544(self): + # gh-9544. Test from gh-9978 + c, d = 5.0, 3 + mean, variance = stats.burr(c, d).stats() + # mean = sc.beta(3 + 1/5, 1. - 1/5) * 3 = 1.4110263... + # var = sc.beta(3 + 2 / 5, 1. - 2 / 5) * 3 - + # (sc.beta(3 + 1 / 5, 1. - 1 / 5) * 3) ** 2 + mean_hc, variance_hc = 1.4110263183925857, 0.22879948026191643 + assert_allclose(mean, mean_hc) + assert_allclose(variance, variance_hc) + + def test_burr_nan_mean_var_9544(self): + # gh-9544. Test from gh-9978 + c, d = 0.5, 3 + mean, variance = stats.burr(c, d).stats() + assert_(np.isnan(mean)) + assert_(np.isnan(variance)) + c, d = 1.5, 3 + mean, variance = stats.burr(c, d).stats() + assert_(np.isfinite(mean)) + assert_(np.isnan(variance)) + + c, d = 0.5, 3 + e1, e2, e3, e4 = stats.burr._munp(np.array([1, 2, 3, 4]), c, d) + assert_(np.isnan(e1)) + assert_(np.isnan(e2)) + assert_(np.isnan(e3)) + assert_(np.isnan(e4)) + c, d = 1.5, 3 + e1, e2, e3, e4 = stats.burr._munp([1, 2, 3, 4], c, d) + assert_(np.isfinite(e1)) + assert_(np.isnan(e2)) + assert_(np.isnan(e3)) + assert_(np.isnan(e4)) + c, d = 2.5, 3 + e1, e2, e3, e4 = stats.burr._munp([1, 2, 3, 4], c, d) + assert_(np.isfinite(e1)) + assert_(np.isfinite(e2)) + assert_(np.isnan(e3)) + assert_(np.isnan(e4)) + c, d = 3.5, 3 + e1, e2, e3, e4 = stats.burr._munp([1, 2, 3, 4], c, d) + assert_(np.isfinite(e1)) + assert_(np.isfinite(e2)) + assert_(np.isfinite(e3)) + assert_(np.isnan(e4)) + c, d = 4.5, 3 + e1, e2, e3, e4 = stats.burr._munp([1, 2, 3, 4], c, d) + assert_(np.isfinite(e1)) + assert_(np.isfinite(e2)) + assert_(np.isfinite(e3)) + assert_(np.isfinite(e4)) + + def test_burr_isf(self): + # reference values were computed via the reference distribution, e.g. + # mp.dps = 100 + # Burr(c=5, d=3).isf([0.1, 1e-10, 1e-20, 1e-40]) + c, d = 5.0, 3.0 + q = [0.1, 1e-10, 1e-20, 1e-40] + ref = [1.9469686558286508, 124.57309395989076, 12457.309396155173, + 124573093.96155174] + assert_allclose(stats.burr.isf(q, c, d), ref, rtol=1e-14) + + +class TestBurr12: + + @pytest.mark.parametrize('scale, expected', + [(1.0, 2.3283064359965952e-170), + (3.5, 5.987114417447875e-153)]) + def test_delta_cdf(self, scale, expected): + # Expected value computed with mpmath: + # + # def burr12sf(x, c, d, scale): + # x = mpmath.mpf(x) + # c = mpmath.mpf(c) + # d = mpmath.mpf(d) + # scale = mpmath.mpf(scale) + # return (mpmath.mp.one + (x/scale)**c)**(-d) + # + # >>> import mpmath + # >>> mpmath.mp.dps = 60 + # >>> float(burr12sf(2e5, 4, 8, 1) - burr12sf(4e5, 4, 8, 1)) + # 2.3283064359965952e-170 + # >>> float(burr12sf(2e5, 4, 8, 3.5) - burr12sf(4e5, 4, 8, 3.5)) + # 5.987114417447875e-153 + # + delta = stats.burr12._delta_cdf(2e5, 4e5, 4, 8, scale=scale) + assert_allclose(delta, expected, rtol=1e-13) + + def test_moments_edge(self): + # gh-18838 reported that burr12 moments could be invalid; see above. + # Check that this is resolved in an edge case where c*d == n, and + # compare the results against those produced by Mathematica, e.g. + # `SinghMaddalaDistribution[2, 2, 1]` at Wolfram Alpha. + c, d = 2, 2 + mean = np.pi/4 + var = 1 - np.pi**2/16 + skew = np.pi**3/(32*var**1.5) + kurtosis = np.nan + ref = [mean, var, skew, kurtosis] + res = stats.burr12(c, d).stats('mvsk') + assert_allclose(res, ref, rtol=1e-14) + + +class TestStudentizedRange: + # For alpha = .05, .01, and .001, and for each value of + # v = [1, 3, 10, 20, 120, inf], a Q was picked from each table for + # k = [2, 8, 14, 20]. + + # these arrays are written with `k` as column, and `v` as rows. + # Q values are taken from table 3: + # https://www.jstor.org/stable/2237810 + q05 = [17.97, 45.40, 54.33, 59.56, + 4.501, 8.853, 10.35, 11.24, + 3.151, 5.305, 6.028, 6.467, + 2.950, 4.768, 5.357, 5.714, + 2.800, 4.363, 4.842, 5.126, + 2.772, 4.286, 4.743, 5.012] + q01 = [90.03, 227.2, 271.8, 298.0, + 8.261, 15.64, 18.22, 19.77, + 4.482, 6.875, 7.712, 8.226, + 4.024, 5.839, 6.450, 6.823, + 3.702, 5.118, 5.562, 5.827, + 3.643, 4.987, 5.400, 5.645] + q001 = [900.3, 2272, 2718, 2980, + 18.28, 34.12, 39.69, 43.05, + 6.487, 9.352, 10.39, 11.03, + 5.444, 7.313, 7.966, 8.370, + 4.772, 6.039, 6.448, 6.695, + 4.654, 5.823, 6.191, 6.411] + qs = np.concatenate((q05, q01, q001)) + ps = [.95, .99, .999] + vs = [1, 3, 10, 20, 120, np.inf] + ks = [2, 8, 14, 20] + + data = list(zip(product(ps, vs, ks), qs)) + + # A small selection of large-v cases generated with R's `ptukey` + # Each case is in the format (q, k, v, r_result) + r_data = [ + (0.1, 3, 9001, 0.002752818526842), + (1, 10, 1000, 0.000526142388912), + (1, 3, np.inf, 0.240712641229283), + (4, 3, np.inf, 0.987012338626815), + (1, 10, np.inf, 0.000519869467083), + ] + + def test_cdf_against_tables(self): + for pvk, q in self.data: + p_expected, v, k = pvk + res_p = stats.studentized_range.cdf(q, k, v) + assert_allclose(res_p, p_expected, rtol=1e-4) + + @pytest.mark.slow + def test_ppf_against_tables(self): + for pvk, q_expected in self.data: + p, v, k = pvk + res_q = stats.studentized_range.ppf(p, k, v) + assert_allclose(res_q, q_expected, rtol=5e-4) + + path_prefix = os.path.dirname(__file__) + relative_path = "data/studentized_range_mpmath_ref.json" + with open(os.path.join(path_prefix, relative_path)) as file: + pregenerated_data = json.load(file) + + @pytest.mark.parametrize("case_result", pregenerated_data["cdf_data"]) + def test_cdf_against_mp(self, case_result): + src_case = case_result["src_case"] + mp_result = case_result["mp_result"] + qkv = src_case["q"], src_case["k"], src_case["v"] + res = stats.studentized_range.cdf(*qkv) + + assert_allclose(res, mp_result, + atol=src_case["expected_atol"], + rtol=src_case["expected_rtol"]) + + @pytest.mark.parametrize("case_result", pregenerated_data["pdf_data"]) + def test_pdf_against_mp(self, case_result): + src_case = case_result["src_case"] + mp_result = case_result["mp_result"] + qkv = src_case["q"], src_case["k"], src_case["v"] + res = stats.studentized_range.pdf(*qkv) + + assert_allclose(res, mp_result, + atol=src_case["expected_atol"], + rtol=src_case["expected_rtol"]) + + @pytest.mark.slow + @pytest.mark.xfail_on_32bit("intermittent RuntimeWarning: invalid value.") + @pytest.mark.parametrize("case_result", pregenerated_data["moment_data"]) + def test_moment_against_mp(self, case_result): + src_case = case_result["src_case"] + mp_result = case_result["mp_result"] + mkv = src_case["m"], src_case["k"], src_case["v"] + + # Silence invalid value encountered warnings. Actual problems will be + # caught by the result comparison. + with np.errstate(invalid='ignore'): + res = stats.studentized_range.moment(*mkv) + + assert_allclose(res, mp_result, + atol=src_case["expected_atol"], + rtol=src_case["expected_rtol"]) + + def test_pdf_integration(self): + k, v = 3, 10 + # Test whether PDF integration is 1 like it should be. + res = quad(stats.studentized_range.pdf, 0, np.inf, args=(k, v)) + assert_allclose(res[0], 1) + + @pytest.mark.xslow + def test_pdf_against_cdf(self): + k, v = 3, 10 + + # Test whether the integrated PDF matches the CDF using cumulative + # integration. Use a small step size to reduce error due to the + # summation. This is slow, but tests the results well. + x = np.arange(0, 10, step=0.01) + + y_cdf = stats.studentized_range.cdf(x, k, v)[1:] + y_pdf_raw = stats.studentized_range.pdf(x, k, v) + y_pdf_cumulative = cumulative_trapezoid(y_pdf_raw, x) + + # Because of error caused by the summation, use a relatively large rtol + assert_allclose(y_pdf_cumulative, y_cdf, rtol=1e-4) + + @pytest.mark.parametrize("r_case_result", r_data) + def test_cdf_against_r(self, r_case_result): + # Test large `v` values using R + q, k, v, r_res = r_case_result + with np.errstate(invalid='ignore'): + res = stats.studentized_range.cdf(q, k, v) + assert_allclose(res, r_res) + + @pytest.mark.slow + @pytest.mark.xfail_on_32bit("intermittent RuntimeWarning: invalid value.") + def test_moment_vectorization(self): + # Test moment broadcasting. Calls `_munp` directly because + # `rv_continuous.moment` is broken at time of writing. See gh-12192 + + # Silence invalid value encountered warnings. Actual problems will be + # caught by the result comparison. + with np.errstate(invalid='ignore'): + m = stats.studentized_range._munp([1, 2], [4, 5], [10, 11]) + + assert_allclose(m.shape, (2,)) + + with pytest.raises(ValueError, match="...could not be broadcast..."): + stats.studentized_range._munp(1, [4, 5], [10, 11, 12]) + + @pytest.mark.xslow + def test_fitstart_valid(self): + with suppress_warnings() as sup, np.errstate(invalid="ignore"): + # the integration warning message may differ + sup.filter(IntegrationWarning) + k, df, _, _ = stats.studentized_range._fitstart([1, 2, 3]) + assert_(stats.studentized_range._argcheck(k, df)) + + def test_infinite_df(self): + # Check that the CDF and PDF infinite and normal integrators + # roughly match for a high df case + res = stats.studentized_range.pdf(3, 10, np.inf) + res_finite = stats.studentized_range.pdf(3, 10, 99999) + assert_allclose(res, res_finite, atol=1e-4, rtol=1e-4) + + res = stats.studentized_range.cdf(3, 10, np.inf) + res_finite = stats.studentized_range.cdf(3, 10, 99999) + assert_allclose(res, res_finite, atol=1e-4, rtol=1e-4) + + def test_df_cutoff(self): + # Test that the CDF and PDF properly switch integrators at df=100,000. + # The infinite integrator should be different enough that it fails + # an allclose assertion. Also sanity check that using the same + # integrator does pass the allclose with a 1-df difference, which + # should be tiny. + + res = stats.studentized_range.pdf(3, 10, 100000) + res_finite = stats.studentized_range.pdf(3, 10, 99999) + res_sanity = stats.studentized_range.pdf(3, 10, 99998) + assert_raises(AssertionError, assert_allclose, res, res_finite, + atol=1e-6, rtol=1e-6) + assert_allclose(res_finite, res_sanity, atol=1e-6, rtol=1e-6) + + res = stats.studentized_range.cdf(3, 10, 100000) + res_finite = stats.studentized_range.cdf(3, 10, 99999) + res_sanity = stats.studentized_range.cdf(3, 10, 99998) + assert_raises(AssertionError, assert_allclose, res, res_finite, + atol=1e-6, rtol=1e-6) + assert_allclose(res_finite, res_sanity, atol=1e-6, rtol=1e-6) + + def test_clipping(self): + # The result of this computation was -9.9253938401489e-14 on some + # systems. The correct result is very nearly zero, but should not be + # negative. + q, k, v = 34.6413996195345746, 3, 339 + p = stats.studentized_range.sf(q, k, v) + assert_allclose(p, 0, atol=1e-10) + assert p >= 0 + + +def test_540_567(): + # test for nan returned in tickets 540, 567 + assert_almost_equal(stats.norm.cdf(-1.7624320982), 0.03899815971089126, + decimal=10, err_msg='test_540_567') + assert_almost_equal(stats.norm.cdf(-1.7624320983), 0.038998159702449846, + decimal=10, err_msg='test_540_567') + assert_almost_equal(stats.norm.cdf(1.38629436112, loc=0.950273420309, + scale=0.204423758009), + 0.98353464004309321, + decimal=10, err_msg='test_540_567') + + +def test_regression_ticket_1326(): + # adjust to avoid nan with 0*log(0) + assert_almost_equal(stats.chi2.pdf(0.0, 2), 0.5, 14) + + +def test_regression_tukey_lambda(): + # Make sure that Tukey-Lambda distribution correctly handles + # non-positive lambdas. + x = np.linspace(-5.0, 5.0, 101) + + with np.errstate(divide='ignore'): + for lam in [0.0, -1.0, -2.0, np.array([[-1.0], [0.0], [-2.0]])]: + p = stats.tukeylambda.pdf(x, lam) + assert_((p != 0.0).all()) + assert_(~np.isnan(p).all()) + + lam = np.array([[-1.0], [0.0], [2.0]]) + p = stats.tukeylambda.pdf(x, lam) + + assert_(~np.isnan(p).all()) + assert_((p[0] != 0.0).all()) + assert_((p[1] != 0.0).all()) + assert_((p[2] != 0.0).any()) + assert_((p[2] == 0.0).any()) + + +@pytest.mark.skipif(DOCSTRINGS_STRIPPED, reason="docstrings stripped") +def test_regression_ticket_1421(): + assert_('pdf(x, mu, loc=0, scale=1)' not in stats.poisson.__doc__) + assert_('pmf(x,' in stats.poisson.__doc__) + + +def test_nan_arguments_gh_issue_1362(): + with np.errstate(invalid='ignore'): + assert_(np.isnan(stats.t.logcdf(1, np.nan))) + assert_(np.isnan(stats.t.cdf(1, np.nan))) + assert_(np.isnan(stats.t.logsf(1, np.nan))) + assert_(np.isnan(stats.t.sf(1, np.nan))) + assert_(np.isnan(stats.t.pdf(1, np.nan))) + assert_(np.isnan(stats.t.logpdf(1, np.nan))) + assert_(np.isnan(stats.t.ppf(1, np.nan))) + assert_(np.isnan(stats.t.isf(1, np.nan))) + + assert_(np.isnan(stats.bernoulli.logcdf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.cdf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.logsf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.sf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.pmf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.logpmf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.ppf(np.nan, 0.5))) + assert_(np.isnan(stats.bernoulli.isf(np.nan, 0.5))) + + +def test_frozen_fit_ticket_1536(): + np.random.seed(5678) + true = np.array([0.25, 0., 0.5]) + x = stats.lognorm.rvs(true[0], true[1], true[2], size=100) + + with np.errstate(divide='ignore'): + params = np.array(stats.lognorm.fit(x, floc=0.)) + + assert_almost_equal(params, true, decimal=2) + + params = np.array(stats.lognorm.fit(x, fscale=0.5, loc=0)) + assert_almost_equal(params, true, decimal=2) + + params = np.array(stats.lognorm.fit(x, f0=0.25, loc=0)) + assert_almost_equal(params, true, decimal=2) + + params = np.array(stats.lognorm.fit(x, f0=0.25, floc=0)) + assert_almost_equal(params, true, decimal=2) + + np.random.seed(5678) + loc = 1 + floc = 0.9 + x = stats.norm.rvs(loc, 2., size=100) + params = np.array(stats.norm.fit(x, floc=floc)) + expected = np.array([floc, np.sqrt(((x-floc)**2).mean())]) + assert_almost_equal(params, expected, decimal=4) + + +def test_regression_ticket_1530(): + # Check the starting value works for Cauchy distribution fit. + np.random.seed(654321) + rvs = stats.cauchy.rvs(size=100) + params = stats.cauchy.fit(rvs) + expected = (0.045, 1.142) + assert_almost_equal(params, expected, decimal=1) + + +def test_gh_pr_4806(): + # Check starting values for Cauchy distribution fit. + np.random.seed(1234) + x = np.random.randn(42) + for offset in 10000.0, 1222333444.0: + loc, scale = stats.cauchy.fit(x + offset) + assert_allclose(loc, offset, atol=1.0) + assert_allclose(scale, 0.6, atol=1.0) + + +def test_tukeylambda_stats_ticket_1545(): + # Some test for the variance and kurtosis of the Tukey Lambda distr. + # See test_tukeylamdba_stats.py for more tests. + + mv = stats.tukeylambda.stats(0, moments='mvsk') + # Known exact values: + expected = [0, np.pi**2/3, 0, 1.2] + assert_almost_equal(mv, expected, decimal=10) + + mv = stats.tukeylambda.stats(3.13, moments='mvsk') + # 'expected' computed with mpmath. + expected = [0, 0.0269220858861465102, 0, -0.898062386219224104] + assert_almost_equal(mv, expected, decimal=10) + + mv = stats.tukeylambda.stats(0.14, moments='mvsk') + # 'expected' computed with mpmath. + expected = [0, 2.11029702221450250, 0, -0.02708377353223019456] + assert_almost_equal(mv, expected, decimal=10) + + +def test_poisson_logpmf_ticket_1436(): + assert_(np.isfinite(stats.poisson.logpmf(1500, 200))) + + +def test_powerlaw_stats(): + """Test the powerlaw stats function. + + This unit test is also a regression test for ticket 1548. + + The exact values are: + mean: + mu = a / (a + 1) + variance: + sigma**2 = a / ((a + 2) * (a + 1) ** 2) + skewness: + One formula (see https://en.wikipedia.org/wiki/Skewness) is + gamma_1 = (E[X**3] - 3*mu*E[X**2] + 2*mu**3) / sigma**3 + A short calculation shows that E[X**k] is a / (a + k), so gamma_1 + can be implemented as + n = a/(a+3) - 3*(a/(a+1))*a/(a+2) + 2*(a/(a+1))**3 + d = sqrt(a/((a+2)*(a+1)**2)) ** 3 + gamma_1 = n/d + Either by simplifying, or by a direct calculation of mu_3 / sigma**3, + one gets the more concise formula: + gamma_1 = -2.0 * ((a - 1) / (a + 3)) * sqrt((a + 2) / a) + kurtosis: (See https://en.wikipedia.org/wiki/Kurtosis) + The excess kurtosis is + gamma_2 = mu_4 / sigma**4 - 3 + A bit of calculus and algebra (sympy helps) shows that + mu_4 = 3*a*(3*a**2 - a + 2) / ((a+1)**4 * (a+2) * (a+3) * (a+4)) + so + gamma_2 = 3*(3*a**2 - a + 2) * (a+2) / (a*(a+3)*(a+4)) - 3 + which can be rearranged to + gamma_2 = 6 * (a**3 - a**2 - 6*a + 2) / (a*(a+3)*(a+4)) + """ + cases = [(1.0, (0.5, 1./12, 0.0, -1.2)), + (2.0, (2./3, 2./36, -0.56568542494924734, -0.6))] + for a, exact_mvsk in cases: + mvsk = stats.powerlaw.stats(a, moments="mvsk") + assert_array_almost_equal(mvsk, exact_mvsk) + + +def test_powerlaw_edge(): + # Regression test for gh-3986. + p = stats.powerlaw.logpdf(0, 1) + assert_equal(p, 0.0) + + +def test_exponpow_edge(): + # Regression test for gh-3982. + p = stats.exponpow.logpdf(0, 1) + assert_equal(p, 0.0) + + # Check pdf and logpdf at x = 0 for other values of b. + p = stats.exponpow.pdf(0, [0.25, 1.0, 1.5]) + assert_equal(p, [np.inf, 1.0, 0.0]) + p = stats.exponpow.logpdf(0, [0.25, 1.0, 1.5]) + assert_equal(p, [np.inf, 0.0, -np.inf]) + + +def test_gengamma_edge(): + # Regression test for gh-3985. + p = stats.gengamma.pdf(0, 1, 1) + assert_equal(p, 1.0) + + +@pytest.mark.parametrize("a, c, ref, tol", + [(1500000.0, 1, 8.529426144018633, 1e-15), + (1e+30, 1, 35.95771492811536, 1e-15), + (1e+100, 1, 116.54819318290696, 1e-15), + (3e3, 1, 5.422011196659015, 1e-13), + (3e6, -1e100, -236.29663213396054, 1e-15), + (3e60, 1e-100, 1.3925371786831085e+102, 1e-15)]) +def test_gengamma_extreme_entropy(a, c, ref, tol): + # The reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + # + # def gen_entropy(a, c): + # a, c = mp.mpf(a), mp.mpf(c) + # val = mp.digamma(a) + # h = (a * (mp.one - val) + val/c + mp.loggamma(a) - mp.log(abs(c))) + # return float(h) + assert_allclose(stats.gengamma.entropy(a, c), ref, rtol=tol) + + +def test_gengamma_endpoint_with_neg_c(): + p = stats.gengamma.pdf(0, 1, -1) + assert p == 0.0 + logp = stats.gengamma.logpdf(0, 1, -1) + assert logp == -np.inf + + +def test_gengamma_munp(): + # Regression tests for gh-4724. + p = stats.gengamma._munp(-2, 200, 1.) + assert_almost_equal(p, 1./199/198) + + p = stats.gengamma._munp(-2, 10, 1.) + assert_almost_equal(p, 1./9/8) + + +def test_ksone_fit_freeze(): + # Regression test for ticket #1638. + d = np.array( + [-0.18879233, 0.15734249, 0.18695107, 0.27908787, -0.248649, + -0.2171497, 0.12233512, 0.15126419, 0.03119282, 0.4365294, + 0.08930393, -0.23509903, 0.28231224, -0.09974875, -0.25196048, + 0.11102028, 0.1427649, 0.10176452, 0.18754054, 0.25826724, + 0.05988819, 0.0531668, 0.21906056, 0.32106729, 0.2117662, + 0.10886442, 0.09375789, 0.24583286, -0.22968366, -0.07842391, + -0.31195432, -0.21271196, 0.1114243, -0.13293002, 0.01331725, + -0.04330977, -0.09485776, -0.28434547, 0.22245721, -0.18518199, + -0.10943985, -0.35243174, 0.06897665, -0.03553363, -0.0701746, + -0.06037974, 0.37670779, -0.21684405]) + + with np.errstate(invalid='ignore'): + with suppress_warnings() as sup: + sup.filter(IntegrationWarning, + "The maximum number of subdivisions .50. has been " + "achieved.") + sup.filter(RuntimeWarning, + "floating point number truncated to an integer") + stats.ksone.fit(d) + + +def test_norm_logcdf(): + # Test precision of the logcdf of the normal distribution. + # This precision was enhanced in ticket 1614. + x = -np.asarray(list(range(0, 120, 4))) + # Values from R + expected = [-0.69314718, -10.36010149, -35.01343716, -75.41067300, + -131.69539607, -203.91715537, -292.09872100, -396.25241451, + -516.38564863, -652.50322759, -804.60844201, -972.70364403, + -1156.79057310, -1356.87055173, -1572.94460885, -1805.01356068, + -2053.07806561, -2317.13866238, -2597.19579746, -2893.24984493, + -3205.30112136, -3533.34989701, -3877.39640444, -4237.44084522, + -4613.48339520, -5005.52420869, -5413.56342187, -5837.60115548, + -6277.63751711, -6733.67260303] + + assert_allclose(stats.norm().logcdf(x), expected, atol=1e-8) + + # also test the complex-valued code path + assert_allclose(stats.norm().logcdf(x + 1e-14j).real, expected, atol=1e-8) + + # test the accuracy: d(logcdf)/dx = pdf / cdf \equiv exp(logpdf - logcdf) + deriv = (stats.norm.logcdf(x + 1e-10j)/1e-10).imag + deriv_expected = np.exp(stats.norm.logpdf(x) - stats.norm.logcdf(x)) + assert_allclose(deriv, deriv_expected, atol=1e-10) + + +def test_levy_cdf_ppf(): + # Test levy.cdf, including small arguments. + x = np.array([1000, 1.0, 0.5, 0.1, 0.01, 0.001]) + + # Expected values were calculated separately with mpmath. + # E.g. + # >>> mpmath.mp.dps = 100 + # >>> x = mpmath.mp.mpf('0.01') + # >>> cdf = mpmath.erfc(mpmath.sqrt(1/(2*x))) + expected = np.array([0.9747728793699604, + 0.3173105078629141, + 0.1572992070502851, + 0.0015654022580025495, + 1.523970604832105e-23, + 1.795832784800726e-219]) + + y = stats.levy.cdf(x) + assert_allclose(y, expected, rtol=1e-10) + + # ppf(expected) should get us back to x. + xx = stats.levy.ppf(expected) + assert_allclose(xx, x, rtol=1e-13) + + +def test_levy_sf(): + # Large values, far into the tail of the distribution. + x = np.array([1e15, 1e25, 1e35, 1e50]) + # Expected values were calculated with mpmath. + expected = np.array([2.5231325220201597e-08, + 2.52313252202016e-13, + 2.52313252202016e-18, + 7.978845608028653e-26]) + y = stats.levy.sf(x) + assert_allclose(y, expected, rtol=1e-14) + + +# The expected values for levy.isf(p) were calculated with mpmath. +# For loc=0 and scale=1, the inverse SF can be computed with +# +# import mpmath +# +# def levy_invsf(p): +# return 1/(2*mpmath.erfinv(p)**2) +# +# For example, with mpmath.mp.dps set to 60, float(levy_invsf(1e-20)) +# returns 6.366197723675814e+39. +# +@pytest.mark.parametrize('p, expected_isf', + [(1e-20, 6.366197723675814e+39), + (1e-8, 6366197723675813.0), + (0.375, 4.185810119346273), + (0.875, 0.42489442055310134), + (0.999, 0.09235685880262713), + (0.9999999962747097, 0.028766845244146945)]) +def test_levy_isf(p, expected_isf): + x = stats.levy.isf(p) + assert_allclose(x, expected_isf, atol=5e-15) + + +def test_levy_l_sf(): + # Test levy_l.sf for small arguments. + x = np.array([-0.016, -0.01, -0.005, -0.0015]) + # Expected values were calculated with mpmath. + expected = np.array([2.6644463892359302e-15, + 1.523970604832107e-23, + 2.0884875837625492e-45, + 5.302850374626878e-147]) + y = stats.levy_l.sf(x) + assert_allclose(y, expected, rtol=1e-13) + + +def test_levy_l_isf(): + # Test roundtrip sf(isf(p)), including a small input value. + p = np.array([3.0e-15, 0.25, 0.99]) + x = stats.levy_l.isf(p) + q = stats.levy_l.sf(x) + assert_allclose(q, p, rtol=5e-14) + + +def test_hypergeom_interval_1802(): + # these two had endless loops + assert_equal(stats.hypergeom.interval(.95, 187601, 43192, 757), + (152.0, 197.0)) + assert_equal(stats.hypergeom.interval(.945, 187601, 43192, 757), + (152.0, 197.0)) + # this was working also before + assert_equal(stats.hypergeom.interval(.94, 187601, 43192, 757), + (153.0, 196.0)) + + # degenerate case .a == .b + assert_equal(stats.hypergeom.ppf(0.02, 100, 100, 8), 8) + assert_equal(stats.hypergeom.ppf(1, 100, 100, 8), 8) + + +def test_distribution_too_many_args(): + np.random.seed(1234) + + # Check that a TypeError is raised when too many args are given to a method + # Regression test for ticket 1815. + x = np.linspace(0.1, 0.7, num=5) + assert_raises(TypeError, stats.gamma.pdf, x, 2, 3, loc=1.0) + assert_raises(TypeError, stats.gamma.pdf, x, 2, 3, 4, loc=1.0) + assert_raises(TypeError, stats.gamma.pdf, x, 2, 3, 4, 5) + assert_raises(TypeError, stats.gamma.pdf, x, 2, 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.rvs, 2., 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.cdf, x, 2., 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.ppf, x, 2., 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.stats, 2., 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.entropy, 2., 3, loc=1.0, scale=0.5) + assert_raises(TypeError, stats.gamma.fit, x, 2., 3, loc=1.0, scale=0.5) + + # These should not give errors + stats.gamma.pdf(x, 2, 3) # loc=3 + stats.gamma.pdf(x, 2, 3, 4) # loc=3, scale=4 + stats.gamma.stats(2., 3) + stats.gamma.stats(2., 3, 4) + stats.gamma.stats(2., 3, 4, 'mv') + stats.gamma.rvs(2., 3, 4, 5) + stats.gamma.fit(stats.gamma.rvs(2., size=7), 2.) + + # Also for a discrete distribution + stats.geom.pmf(x, 2, loc=3) # no error, loc=3 + assert_raises(TypeError, stats.geom.pmf, x, 2, 3, 4) + assert_raises(TypeError, stats.geom.pmf, x, 2, 3, loc=4) + + # And for distributions with 0, 2 and 3 args respectively + assert_raises(TypeError, stats.expon.pdf, x, 3, loc=1.0) + assert_raises(TypeError, stats.exponweib.pdf, x, 3, 4, 5, loc=1.0) + assert_raises(TypeError, stats.exponweib.pdf, x, 3, 4, 5, 0.1, 0.1) + assert_raises(TypeError, stats.ncf.pdf, x, 3, 4, 5, 6, loc=1.0) + assert_raises(TypeError, stats.ncf.pdf, x, 3, 4, 5, 6, 1.0, scale=0.5) + stats.ncf.pdf(x, 3, 4, 5, 6, 1.0) # 3 args, plus loc/scale + + +def test_ncx2_tails_ticket_955(): + # Trac #955 -- check that the cdf computed by special functions + # matches the integrated pdf + a = stats.ncx2.cdf(np.arange(20, 25, 0.2), 2, 1.07458615e+02) + b = stats.ncx2._cdfvec(np.arange(20, 25, 0.2), 2, 1.07458615e+02) + assert_allclose(a, b, rtol=1e-3, atol=0) + + +def test_ncx2_tails_pdf(): + # ncx2.pdf does not return nans in extreme tails(example from gh-1577) + # NB: this is to check that nan_to_num is not needed in ncx2.pdf + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + assert_equal(stats.ncx2.pdf(1, np.arange(340, 350), 2), 0) + logval = stats.ncx2.logpdf(1, np.arange(340, 350), 2) + + assert_(np.isneginf(logval).all()) + + # Verify logpdf has extended precision when pdf underflows to 0 + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + assert_equal(stats.ncx2.pdf(10000, 3, 12), 0) + assert_allclose(stats.ncx2.logpdf(10000, 3, 12), -4662.444377524883) + + +@pytest.mark.parametrize('method, expected', [ + ('cdf', np.array([2.497951336e-09, 3.437288941e-10])), + ('pdf', np.array([1.238579980e-07, 1.710041145e-08])), + ('logpdf', np.array([-15.90413011, -17.88416331])), + ('ppf', np.array([4.865182052, 7.017182271])) +]) +def test_ncx2_zero_nc(method, expected): + # gh-5441 + # ncx2 with nc=0 is identical to chi2 + # Comparison to R (v3.5.1) + # > options(digits=10) + # > pchisq(0.1, df=10, ncp=c(0,4)) + # > dchisq(0.1, df=10, ncp=c(0,4)) + # > dchisq(0.1, df=10, ncp=c(0,4), log=TRUE) + # > qchisq(0.1, df=10, ncp=c(0,4)) + + result = getattr(stats.ncx2, method)(0.1, nc=[0, 4], df=10) + assert_allclose(result, expected, atol=1e-15) + + +def test_ncx2_zero_nc_rvs(): + # gh-5441 + # ncx2 with nc=0 is identical to chi2 + result = stats.ncx2.rvs(df=10, nc=0, random_state=1) + expected = stats.chi2.rvs(df=10, random_state=1) + assert_allclose(result, expected, atol=1e-15) + + +def test_ncx2_gh12731(): + # test that gh-12731 is resolved; previously these were all 0.5 + nc = 10**np.arange(5, 10) + assert_equal(stats.ncx2.cdf(1e4, df=1, nc=nc), 0) + + +def test_ncx2_gh8665(): + # test that gh-8665 is resolved; previously this tended to nonzero value + x = np.array([4.99515382e+00, 1.07617327e+01, 2.31854502e+01, + 4.99515382e+01, 1.07617327e+02, 2.31854502e+02, + 4.99515382e+02, 1.07617327e+03, 2.31854502e+03, + 4.99515382e+03, 1.07617327e+04, 2.31854502e+04, + 4.99515382e+04]) + nu, lam = 20, 499.51538166556196 + + sf = stats.ncx2.sf(x, df=nu, nc=lam) + # computed in R. Couldn't find a survival function implementation + # options(digits=16) + # x <- c(4.99515382e+00, 1.07617327e+01, 2.31854502e+01, 4.99515382e+01, + # 1.07617327e+02, 2.31854502e+02, 4.99515382e+02, 1.07617327e+03, + # 2.31854502e+03, 4.99515382e+03, 1.07617327e+04, 2.31854502e+04, + # 4.99515382e+04) + # nu <- 20 + # lam <- 499.51538166556196 + # 1 - pchisq(x, df = nu, ncp = lam) + sf_expected = [1.0000000000000000, 1.0000000000000000, 1.0000000000000000, + 1.0000000000000000, 1.0000000000000000, 0.9999999999999888, + 0.6646525582135460, 0.0000000000000000, 0.0000000000000000, + 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, + 0.0000000000000000] + assert_allclose(sf, sf_expected, atol=1e-12) + + +def test_ncx2_gh11777(): + # regression test for gh-11777: + # At high values of degrees of freedom df, ensure the pdf of ncx2 does + # not get clipped to zero when the non-centrality parameter is + # sufficiently less than df + df = 6700 + nc = 5300 + x = np.linspace(stats.ncx2.ppf(0.001, df, nc), + stats.ncx2.ppf(0.999, df, nc), num=10000) + ncx2_pdf = stats.ncx2.pdf(x, df, nc) + gauss_approx = stats.norm.pdf(x, df + nc, np.sqrt(2 * df + 4 * nc)) + # use huge tolerance as we're only looking for obvious discrepancy + assert_allclose(ncx2_pdf, gauss_approx, atol=1e-4) + + +# Expected values for foldnorm.sf were computed with mpmath: +# +# from mpmath import mp +# mp.dps = 60 +# def foldcauchy_sf(x, c): +# x = mp.mpf(x) +# c = mp.mpf(c) +# return mp.one - (mp.atan(x - c) + mp.atan(x + c))/mp.pi +# +# E.g. +# +# >>> float(foldcauchy_sf(2, 1)) +# 0.35241638234956674 +# +@pytest.mark.parametrize('x, c, expected', + [(2, 1, 0.35241638234956674), + (2, 2, 0.5779791303773694), + (1e13, 1, 6.366197723675813e-14), + (2e16, 1, 3.183098861837907e-17), + (1e13, 2e11, 6.368745221764519e-14), + (0.125, 200, 0.999998010612169)]) +def test_foldcauchy_sf(x, c, expected): + sf = stats.foldcauchy.sf(x, c) + assert_allclose(sf, expected, 2e-15) + + +# The same mpmath code shown in the comments above test_foldcauchy_sf() +# is used to create these expected values. +@pytest.mark.parametrize('x, expected', + [(2, 0.2951672353008665), + (1e13, 6.366197723675813e-14), + (2e16, 3.183098861837907e-17), + (5e80, 1.2732395447351629e-81)]) +def test_halfcauchy_sf(x, expected): + sf = stats.halfcauchy.sf(x) + assert_allclose(sf, expected, 2e-15) + + +# Expected value computed with mpmath: +# expected = mp.cot(mp.pi*p/2) +@pytest.mark.parametrize('p, expected', + [(0.9999995, 7.853981633329977e-07), + (0.975, 0.039290107007669675), + (0.5, 1.0), + (0.01, 63.65674116287158), + (1e-14, 63661977236758.13), + (5e-80, 1.2732395447351627e+79)]) +def test_halfcauchy_isf(p, expected): + x = stats.halfcauchy.isf(p) + assert_allclose(x, expected) + + +def test_foldnorm_zero(): + # Parameter value c=0 was not enabled, see gh-2399. + rv = stats.foldnorm(0, scale=1) + assert_equal(rv.cdf(0), 0) # rv.cdf(0) previously resulted in: nan + + +# Expected values for foldnorm.sf were computed with mpmath: +# +# from mpmath import mp +# mp.dps = 60 +# def foldnorm_sf(x, c): +# x = mp.mpf(x) +# c = mp.mpf(c) +# return mp.ncdf(-x+c) + mp.ncdf(-x-c) +# +# E.g. +# +# >>> float(foldnorm_sf(2, 1)) +# 0.16000515196308715 +# +@pytest.mark.parametrize('x, c, expected', + [(2, 1, 0.16000515196308715), + (20, 1, 8.527223952630977e-81), + (10, 15, 0.9999997133484281), + (25, 15, 7.619853024160525e-24)]) +def test_foldnorm_sf(x, c, expected): + sf = stats.foldnorm.sf(x, c) + assert_allclose(sf, expected, 1e-14) + + +def test_stats_shapes_argcheck(): + # stats method was failing for vector shapes if some of the values + # were outside of the allowed range, see gh-2678 + mv3 = stats.invgamma.stats([0.0, 0.5, 1.0], 1, 0.5) # 0 is not a legal `a` + mv2 = stats.invgamma.stats([0.5, 1.0], 1, 0.5) + mv2_augmented = tuple(np.r_[np.nan, _] for _ in mv2) + assert_equal(mv2_augmented, mv3) + + # -1 is not a legal shape parameter + mv3 = stats.lognorm.stats([2, 2.4, -1]) + mv2 = stats.lognorm.stats([2, 2.4]) + mv2_augmented = tuple(np.r_[_, np.nan] for _ in mv2) + assert_equal(mv2_augmented, mv3) + + # FIXME: this is only a quick-and-dirty test of a quick-and-dirty bugfix. + # stats method with multiple shape parameters is not properly vectorized + # anyway, so some distributions may or may not fail. + + +# Test subclassing distributions w/ explicit shapes + +class _distr_gen(stats.rv_continuous): + def _pdf(self, x, a): + return 42 + + +class _distr2_gen(stats.rv_continuous): + def _cdf(self, x, a): + return 42 * a + x + + +class _distr3_gen(stats.rv_continuous): + def _pdf(self, x, a, b): + return a + b + + def _cdf(self, x, a): + # Different # of shape params from _pdf, to be able to check that + # inspection catches the inconsistency. + return 42 * a + x + + +class _distr6_gen(stats.rv_continuous): + # Two shape parameters (both _pdf and _cdf defined, consistent shapes.) + def _pdf(self, x, a, b): + return a*x + b + + def _cdf(self, x, a, b): + return 42 * a + x + + +class TestSubclassingExplicitShapes: + # Construct a distribution w/ explicit shapes parameter and test it. + + def test_correct_shapes(self): + dummy_distr = _distr_gen(name='dummy', shapes='a') + assert_equal(dummy_distr.pdf(1, a=1), 42) + + def test_wrong_shapes_1(self): + dummy_distr = _distr_gen(name='dummy', shapes='A') + assert_raises(TypeError, dummy_distr.pdf, 1, **dict(a=1)) + + def test_wrong_shapes_2(self): + dummy_distr = _distr_gen(name='dummy', shapes='a, b, c') + dct = dict(a=1, b=2, c=3) + assert_raises(TypeError, dummy_distr.pdf, 1, **dct) + + def test_shapes_string(self): + # shapes must be a string + dct = dict(name='dummy', shapes=42) + assert_raises(TypeError, _distr_gen, **dct) + + def test_shapes_identifiers_1(self): + # shapes must be a comma-separated list of valid python identifiers + dct = dict(name='dummy', shapes='(!)') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_identifiers_2(self): + dct = dict(name='dummy', shapes='4chan') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_identifiers_3(self): + dct = dict(name='dummy', shapes='m(fti)') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_identifiers_nodefaults(self): + dct = dict(name='dummy', shapes='a=2') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_args(self): + dct = dict(name='dummy', shapes='*args') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_kwargs(self): + dct = dict(name='dummy', shapes='**kwargs') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_keywords(self): + # python keywords cannot be used for shape parameters + dct = dict(name='dummy', shapes='a, b, c, lambda') + assert_raises(SyntaxError, _distr_gen, **dct) + + def test_shapes_signature(self): + # test explicit shapes which agree w/ the signature of _pdf + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, a): + return stats.norm._pdf(x) * a + + dist = _dist_gen(shapes='a') + assert_equal(dist.pdf(0.5, a=2), stats.norm.pdf(0.5)*2) + + def test_shapes_signature_inconsistent(self): + # test explicit shapes which do not agree w/ the signature of _pdf + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, a): + return stats.norm._pdf(x) * a + + dist = _dist_gen(shapes='a, b') + assert_raises(TypeError, dist.pdf, 0.5, **dict(a=1, b=2)) + + def test_star_args(self): + # test _pdf with only starargs + # NB: **kwargs of pdf will never reach _pdf + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, *args): + extra_kwarg = args[0] + return stats.norm._pdf(x) * extra_kwarg + + dist = _dist_gen(shapes='extra_kwarg') + assert_equal(dist.pdf(0.5, extra_kwarg=33), stats.norm.pdf(0.5)*33) + assert_equal(dist.pdf(0.5, 33), stats.norm.pdf(0.5)*33) + assert_raises(TypeError, dist.pdf, 0.5, **dict(xxx=33)) + + def test_star_args_2(self): + # test _pdf with named & starargs + # NB: **kwargs of pdf will never reach _pdf + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, offset, *args): + extra_kwarg = args[0] + return stats.norm._pdf(x) * extra_kwarg + offset + + dist = _dist_gen(shapes='offset, extra_kwarg') + assert_equal(dist.pdf(0.5, offset=111, extra_kwarg=33), + stats.norm.pdf(0.5)*33 + 111) + assert_equal(dist.pdf(0.5, 111, 33), + stats.norm.pdf(0.5)*33 + 111) + + def test_extra_kwarg(self): + # **kwargs to _pdf are ignored. + # this is a limitation of the framework (_pdf(x, *goodargs)) + class _distr_gen(stats.rv_continuous): + def _pdf(self, x, *args, **kwargs): + # _pdf should handle *args, **kwargs itself. Here "handling" + # is ignoring *args and looking for ``extra_kwarg`` and using + # that. + extra_kwarg = kwargs.pop('extra_kwarg', 1) + return stats.norm._pdf(x) * extra_kwarg + + dist = _distr_gen(shapes='extra_kwarg') + assert_equal(dist.pdf(1, extra_kwarg=3), stats.norm.pdf(1)) + + def test_shapes_empty_string(self): + # shapes='' is equivalent to shapes=None + class _dist_gen(stats.rv_continuous): + def _pdf(self, x): + return stats.norm.pdf(x) + + dist = _dist_gen(shapes='') + assert_equal(dist.pdf(0.5), stats.norm.pdf(0.5)) + + +class TestSubclassingNoShapes: + # Construct a distribution w/o explicit shapes parameter and test it. + + def test_only__pdf(self): + dummy_distr = _distr_gen(name='dummy') + assert_equal(dummy_distr.pdf(1, a=1), 42) + + def test_only__cdf(self): + # _pdf is determined from _cdf by taking numerical derivative + dummy_distr = _distr2_gen(name='dummy') + assert_almost_equal(dummy_distr.pdf(1, a=1), 1) + + @pytest.mark.skipif(DOCSTRINGS_STRIPPED, reason="docstring stripped") + def test_signature_inspection(self): + # check that _pdf signature inspection works correctly, and is used in + # the class docstring + dummy_distr = _distr_gen(name='dummy') + assert_equal(dummy_distr.numargs, 1) + assert_equal(dummy_distr.shapes, 'a') + res = re.findall(r'logpdf\(x, a, loc=0, scale=1\)', + dummy_distr.__doc__) + assert_(len(res) == 1) + + @pytest.mark.skipif(DOCSTRINGS_STRIPPED, reason="docstring stripped") + def test_signature_inspection_2args(self): + # same for 2 shape params and both _pdf and _cdf defined + dummy_distr = _distr6_gen(name='dummy') + assert_equal(dummy_distr.numargs, 2) + assert_equal(dummy_distr.shapes, 'a, b') + res = re.findall(r'logpdf\(x, a, b, loc=0, scale=1\)', + dummy_distr.__doc__) + assert_(len(res) == 1) + + def test_signature_inspection_2args_incorrect_shapes(self): + # both _pdf and _cdf defined, but shapes are inconsistent: raises + assert_raises(TypeError, _distr3_gen, name='dummy') + + def test_defaults_raise(self): + # default arguments should raise + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, a=42): + return 42 + assert_raises(TypeError, _dist_gen, **dict(name='dummy')) + + def test_starargs_raise(self): + # without explicit shapes, *args are not allowed + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, a, *args): + return 42 + assert_raises(TypeError, _dist_gen, **dict(name='dummy')) + + def test_kwargs_raise(self): + # without explicit shapes, **kwargs are not allowed + class _dist_gen(stats.rv_continuous): + def _pdf(self, x, a, **kwargs): + return 42 + assert_raises(TypeError, _dist_gen, **dict(name='dummy')) + + +@pytest.mark.skipif(DOCSTRINGS_STRIPPED, reason="docstring stripped") +def test_docstrings(): + badones = [r',\s*,', r'\(\s*,', r'^\s*:'] + for distname in stats.__all__: + dist = getattr(stats, distname) + if isinstance(dist, (stats.rv_discrete, stats.rv_continuous)): + for regex in badones: + assert_(re.search(regex, dist.__doc__) is None) + + +def test_infinite_input(): + assert_almost_equal(stats.skellam.sf(np.inf, 10, 11), 0) + assert_almost_equal(stats.ncx2._cdf(np.inf, 8, 0.1), 1) + + +def test_lomax_accuracy(): + # regression test for gh-4033 + p = stats.lomax.ppf(stats.lomax.cdf(1e-100, 1), 1) + assert_allclose(p, 1e-100) + + +def test_truncexpon_accuracy(): + # regression test for gh-4035 + p = stats.truncexpon.ppf(stats.truncexpon.cdf(1e-100, 1), 1) + assert_allclose(p, 1e-100) + + +def test_rayleigh_accuracy(): + # regression test for gh-4034 + p = stats.rayleigh.isf(stats.rayleigh.sf(9, 1), 1) + assert_almost_equal(p, 9.0, decimal=15) + + +def test_genextreme_give_no_warnings(): + """regression test for gh-6219""" + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + + stats.genextreme.cdf(.5, 0) + stats.genextreme.pdf(.5, 0) + stats.genextreme.ppf(.5, 0) + stats.genextreme.logpdf(-np.inf, 0.0) + number_of_warnings_thrown = len(w) + assert_equal(number_of_warnings_thrown, 0) + + +def test_genextreme_entropy(): + # regression test for gh-5181 + euler_gamma = 0.5772156649015329 + + h = stats.genextreme.entropy(-1.0) + assert_allclose(h, 2*euler_gamma + 1, rtol=1e-14) + + h = stats.genextreme.entropy(0) + assert_allclose(h, euler_gamma + 1, rtol=1e-14) + + h = stats.genextreme.entropy(1.0) + assert_equal(h, 1) + + h = stats.genextreme.entropy(-2.0, scale=10) + assert_allclose(h, euler_gamma*3 + np.log(10) + 1, rtol=1e-14) + + h = stats.genextreme.entropy(10) + assert_allclose(h, -9*euler_gamma + 1, rtol=1e-14) + + h = stats.genextreme.entropy(-10) + assert_allclose(h, 11*euler_gamma + 1, rtol=1e-14) + + +def test_genextreme_sf_isf(): + # Expected values were computed using mpmath: + # + # import mpmath + # + # def mp_genextreme_sf(x, xi, mu=0, sigma=1): + # # Formula from wikipedia, which has a sign convention for xi that + # # is the opposite of scipy's shape parameter. + # if xi != 0: + # t = mpmath.power(1 + ((x - mu)/sigma)*xi, -1/xi) + # else: + # t = mpmath.exp(-(x - mu)/sigma) + # return 1 - mpmath.exp(-t) + # + # >>> mpmath.mp.dps = 1000 + # >>> s = mp_genextreme_sf(mpmath.mp.mpf("1e8"), mpmath.mp.mpf("0.125")) + # >>> float(s) + # 1.6777205262585625e-57 + # >>> s = mp_genextreme_sf(mpmath.mp.mpf("7.98"), mpmath.mp.mpf("-0.125")) + # >>> float(s) + # 1.52587890625e-21 + # >>> s = mp_genextreme_sf(mpmath.mp.mpf("7.98"), mpmath.mp.mpf("0")) + # >>> float(s) + # 0.00034218086528426593 + + x = 1e8 + s = stats.genextreme.sf(x, -0.125) + assert_allclose(s, 1.6777205262585625e-57) + x2 = stats.genextreme.isf(s, -0.125) + assert_allclose(x2, x) + + x = 7.98 + s = stats.genextreme.sf(x, 0.125) + assert_allclose(s, 1.52587890625e-21) + x2 = stats.genextreme.isf(s, 0.125) + assert_allclose(x2, x) + + x = 7.98 + s = stats.genextreme.sf(x, 0) + assert_allclose(s, 0.00034218086528426593) + x2 = stats.genextreme.isf(s, 0) + assert_allclose(x2, x) + + +def test_burr12_ppf_small_arg(): + prob = 1e-16 + quantile = stats.burr12.ppf(prob, 2, 3) + # The expected quantile was computed using mpmath: + # >>> import mpmath + # >>> mpmath.mp.dps = 100 + # >>> prob = mpmath.mpf('1e-16') + # >>> c = mpmath.mpf(2) + # >>> d = mpmath.mpf(3) + # >>> float(((1-prob)**(-1/d) - 1)**(1/c)) + # 5.7735026918962575e-09 + assert_allclose(quantile, 5.7735026918962575e-09) + + +def test_crystalball_function(): + """ + All values are calculated using the independent implementation of the + ROOT framework (see https://root.cern.ch/). + Corresponding ROOT code is given in the comments. + """ + X = np.linspace(-5.0, 5.0, 21)[:-1] + + # for(float x = -5.0; x < 5.0; x+=0.5) + # std::cout << ROOT::Math::crystalball_pdf(x, 1.0, 2.0, 1.0) << ", "; + calculated = stats.crystalball.pdf(X, beta=1.0, m=2.0) + expected = np.array([0.0202867, 0.0241428, 0.0292128, 0.0360652, 0.045645, + 0.059618, 0.0811467, 0.116851, 0.18258, 0.265652, + 0.301023, 0.265652, 0.18258, 0.097728, 0.0407391, + 0.013226, 0.00334407, 0.000658486, 0.000100982, + 1.20606e-05]) + assert_allclose(expected, calculated, rtol=0.001) + + # for(float x = -5.0; x < 5.0; x+=0.5) + # std::cout << ROOT::Math::crystalball_pdf(x, 2.0, 3.0, 1.0) << ", "; + calculated = stats.crystalball.pdf(X, beta=2.0, m=3.0) + expected = np.array([0.0019648, 0.00279754, 0.00417592, 0.00663121, + 0.0114587, 0.0223803, 0.0530497, 0.12726, 0.237752, + 0.345928, 0.391987, 0.345928, 0.237752, 0.12726, + 0.0530497, 0.0172227, 0.00435458, 0.000857469, + 0.000131497, 1.57051e-05]) + assert_allclose(expected, calculated, rtol=0.001) + + # for(float x = -5.0; x < 5.0; x+=0.5) { + # std::cout << ROOT::Math::crystalball_pdf(x, 2.0, 3.0, 2.0, 0.5); + # std::cout << ", "; + # } + calculated = stats.crystalball.pdf(X, beta=2.0, m=3.0, loc=0.5, scale=2.0) + expected = np.array([0.00785921, 0.0111902, 0.0167037, 0.0265249, + 0.0423866, 0.0636298, 0.0897324, 0.118876, 0.147944, + 0.172964, 0.189964, 0.195994, 0.189964, 0.172964, + 0.147944, 0.118876, 0.0897324, 0.0636298, 0.0423866, + 0.0265249]) + assert_allclose(expected, calculated, rtol=0.001) + + # for(float x = -5.0; x < 5.0; x+=0.5) + # std::cout << ROOT::Math::crystalball_cdf(x, 1.0, 2.0, 1.0) << ", "; + calculated = stats.crystalball.cdf(X, beta=1.0, m=2.0) + expected = np.array([0.12172, 0.132785, 0.146064, 0.162293, 0.18258, + 0.208663, 0.24344, 0.292128, 0.36516, 0.478254, + 0.622723, 0.767192, 0.880286, 0.94959, 0.982834, + 0.995314, 0.998981, 0.999824, 0.999976, 0.999997]) + assert_allclose(expected, calculated, rtol=0.001) + + # for(float x = -5.0; x < 5.0; x+=0.5) + # std::cout << ROOT::Math::crystalball_cdf(x, 2.0, 3.0, 1.0) << ", "; + calculated = stats.crystalball.cdf(X, beta=2.0, m=3.0) + expected = np.array([0.00442081, 0.00559509, 0.00730787, 0.00994682, + 0.0143234, 0.0223803, 0.0397873, 0.0830763, 0.173323, + 0.320592, 0.508717, 0.696841, 0.844111, 0.934357, + 0.977646, 0.993899, 0.998674, 0.999771, 0.999969, + 0.999997]) + assert_allclose(expected, calculated, rtol=0.001) + + # for(float x = -5.0; x < 5.0; x+=0.5) { + # std::cout << ROOT::Math::crystalball_cdf(x, 2.0, 3.0, 2.0, 0.5); + # std::cout << ", "; + # } + calculated = stats.crystalball.cdf(X, beta=2.0, m=3.0, loc=0.5, scale=2.0) + expected = np.array([0.0176832, 0.0223803, 0.0292315, 0.0397873, 0.0567945, + 0.0830763, 0.121242, 0.173323, 0.24011, 0.320592, + 0.411731, 0.508717, 0.605702, 0.696841, 0.777324, + 0.844111, 0.896192, 0.934357, 0.960639, 0.977646]) + assert_allclose(expected, calculated, rtol=0.001) + + +def test_crystalball_function_moments(): + """ + All values are calculated using the pdf formula and the integrate function + of Mathematica + """ + # The Last two (alpha, n) pairs test the special case n == alpha**2 + beta = np.array([2.0, 1.0, 3.0, 2.0, 3.0]) + m = np.array([3.0, 3.0, 2.0, 4.0, 9.0]) + + # The distribution should be correctly normalised + expected_0th_moment = np.array([1.0, 1.0, 1.0, 1.0, 1.0]) + calculated_0th_moment = stats.crystalball._munp(0, beta, m) + assert_allclose(expected_0th_moment, calculated_0th_moment, rtol=0.001) + + # calculated using wolframalpha.com + # e.g. for beta = 2 and m = 3 we calculate the norm like this: + # integrate exp(-x^2/2) from -2 to infinity + + # integrate (3/2)^3*exp(-2^2/2)*(3/2-2-x)^(-3) from -infinity to -2 + norm = np.array([2.5511, 3.01873, 2.51065, 2.53983, 2.507410455]) + + a = np.array([-0.21992, -3.03265, np.inf, -0.135335, -0.003174]) + expected_1th_moment = a / norm + calculated_1th_moment = stats.crystalball._munp(1, beta, m) + assert_allclose(expected_1th_moment, calculated_1th_moment, rtol=0.001) + + a = np.array([np.inf, np.inf, np.inf, 3.2616, 2.519908]) + expected_2th_moment = a / norm + calculated_2th_moment = stats.crystalball._munp(2, beta, m) + assert_allclose(expected_2th_moment, calculated_2th_moment, rtol=0.001) + + a = np.array([np.inf, np.inf, np.inf, np.inf, -0.0577668]) + expected_3th_moment = a / norm + calculated_3th_moment = stats.crystalball._munp(3, beta, m) + assert_allclose(expected_3th_moment, calculated_3th_moment, rtol=0.001) + + a = np.array([np.inf, np.inf, np.inf, np.inf, 7.78468]) + expected_4th_moment = a / norm + calculated_4th_moment = stats.crystalball._munp(4, beta, m) + assert_allclose(expected_4th_moment, calculated_4th_moment, rtol=0.001) + + a = np.array([np.inf, np.inf, np.inf, np.inf, -1.31086]) + expected_5th_moment = a / norm + calculated_5th_moment = stats.crystalball._munp(5, beta, m) + assert_allclose(expected_5th_moment, calculated_5th_moment, rtol=0.001) + + +def test_crystalball_entropy(): + # regression test for gh-13602 + cb = stats.crystalball(2, 3) + res1 = cb.entropy() + # -20000 and 30 are negative and positive infinity, respectively + lo, hi, N = -20000, 30, 200000 + x = np.linspace(lo, hi, N) + res2 = trapezoid(entr(cb.pdf(x)), x) + assert_allclose(res1, res2, rtol=1e-7) + + +def test_invweibull_fit(): + """ + Test fitting invweibull to data. + + Here is a the same calculation in R: + + > library(evd) + > library(fitdistrplus) + > x = c(1, 1.25, 2, 2.5, 2.8, 3, 3.8, 4, 5, 8, 10, 12, 64, 99) + > result = fitdist(x, 'frechet', control=list(reltol=1e-13), + + fix.arg=list(loc=0), start=list(shape=2, scale=3)) + > result + Fitting of the distribution ' frechet ' by maximum likelihood + Parameters: + estimate Std. Error + shape 1.048482 0.2261815 + scale 3.099456 0.8292887 + Fixed parameters: + value + loc 0 + + """ + + def optimizer(func, x0, args=(), disp=0): + return fmin(func, x0, args=args, disp=disp, xtol=1e-12, ftol=1e-12) + + x = np.array([1, 1.25, 2, 2.5, 2.8, 3, 3.8, 4, 5, 8, 10, 12, 64, 99]) + c, loc, scale = stats.invweibull.fit(x, floc=0, optimizer=optimizer) + assert_allclose(c, 1.048482, rtol=5e-6) + assert loc == 0 + assert_allclose(scale, 3.099456, rtol=5e-6) + + +# Expected values were computed with mpmath. +@pytest.mark.parametrize('x, c, expected', + [(3, 1.5, 0.175064510070713299327), + (2000, 1.5, 1.11802773877318715787e-5), + (2000, 9.25, 2.92060308832269637092e-31), + (1e15, 1.5, 3.16227766016837933199884e-23)]) +def test_invweibull_sf(x, c, expected): + computed = stats.invweibull.sf(x, c) + assert_allclose(computed, expected, rtol=1e-15) + + +# Expected values were computed with mpmath. +@pytest.mark.parametrize('p, c, expected', + [(0.5, 2.5, 1.15789669836468183976), + (3e-18, 5, 3195.77171838060906447)]) +def test_invweibull_isf(p, c, expected): + computed = stats.invweibull.isf(p, c) + assert_allclose(computed, expected, rtol=1e-15) + + +@pytest.mark.parametrize( + 'df1,df2,x', + [(2, 2, [-0.5, 0.2, 1.0, 2.3]), + (4, 11, [-0.5, 0.2, 1.0, 2.3]), + (7, 17, [1, 2, 3, 4, 5])] +) +def test_ncf_edge_case(df1, df2, x): + # Test for edge case described in gh-11660. + # Non-central Fisher distribution when nc = 0 + # should be the same as Fisher distribution. + nc = 0 + expected_cdf = stats.f.cdf(x, df1, df2) + calculated_cdf = stats.ncf.cdf(x, df1, df2, nc) + assert_allclose(expected_cdf, calculated_cdf, rtol=1e-14) + + # when ncf_gen._skip_pdf will be used instead of generic pdf, + # this additional test will be useful. + expected_pdf = stats.f.pdf(x, df1, df2) + calculated_pdf = stats.ncf.pdf(x, df1, df2, nc) + assert_allclose(expected_pdf, calculated_pdf, rtol=1e-6) + + +def test_ncf_variance(): + # Regression test for gh-10658 (incorrect variance formula for ncf). + # The correct value of ncf.var(2, 6, 4), 42.75, can be verified with, for + # example, Wolfram Alpha with the expression + # Variance[NoncentralFRatioDistribution[2, 6, 4]] + # or with the implementation of the noncentral F distribution in the C++ + # library Boost. + v = stats.ncf.var(2, 6, 4) + assert_allclose(v, 42.75, rtol=1e-14) + + +def test_ncf_cdf_spotcheck(): + # Regression test for gh-15582 testing against values from R/MATLAB + # Generate check_val from R or MATLAB as follows: + # R: pf(20, df1 = 6, df2 = 33, ncp = 30.4) = 0.998921 + # MATLAB: ncfcdf(20, 6, 33, 30.4) = 0.998921 + scipy_val = stats.ncf.cdf(20, 6, 33, 30.4) + check_val = 0.998921 + assert_allclose(check_val, np.round(scipy_val, decimals=6)) + + +@pytest.mark.skipif(sys.maxsize <= 2**32, + reason="On some 32-bit the warning is not raised") +def test_ncf_ppf_issue_17026(): + # Regression test for gh-17026 + x = np.linspace(0, 1, 600) + x[0] = 1e-16 + par = (0.1, 2, 5, 0, 1) + with pytest.warns(RuntimeWarning): + q = stats.ncf.ppf(x, *par) + q0 = [stats.ncf.ppf(xi, *par) for xi in x] + assert_allclose(q, q0) + + +class TestHistogram: + def setup_method(self): + np.random.seed(1234) + + # We have 8 bins + # [1,2), [2,3), [3,4), [4,5), [5,6), [6,7), [7,8), [8,9) + # But actually np.histogram will put the last 9 also in the [8,9) bin! + # Therefore there is a slight difference below for the last bin, from + # what you might have expected. + histogram = np.histogram([1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 9], bins=8) + self.template = stats.rv_histogram(histogram) + + data = stats.norm.rvs(loc=1.0, scale=2.5, size=10000, random_state=123) + norm_histogram = np.histogram(data, bins=50) + self.norm_template = stats.rv_histogram(norm_histogram) + + def test_pdf(self): + values = np.array([0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, + 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5]) + pdf_values = np.asarray([0.0/25.0, 0.0/25.0, 1.0/25.0, 1.0/25.0, + 2.0/25.0, 2.0/25.0, 3.0/25.0, 3.0/25.0, + 4.0/25.0, 4.0/25.0, 5.0/25.0, 5.0/25.0, + 4.0/25.0, 4.0/25.0, 3.0/25.0, 3.0/25.0, + 3.0/25.0, 3.0/25.0, 0.0/25.0, 0.0/25.0]) + assert_allclose(self.template.pdf(values), pdf_values) + + # Test explicitly the corner cases: + # As stated above the pdf in the bin [8,9) is greater than + # one would naively expect because np.histogram putted the 9 + # into the [8,9) bin. + assert_almost_equal(self.template.pdf(8.0), 3.0/25.0) + assert_almost_equal(self.template.pdf(8.5), 3.0/25.0) + # 9 is outside our defined bins [8,9) hence the pdf is already 0 + # for a continuous distribution this is fine, because a single value + # does not have a finite probability! + assert_almost_equal(self.template.pdf(9.0), 0.0/25.0) + assert_almost_equal(self.template.pdf(10.0), 0.0/25.0) + + x = np.linspace(-2, 2, 10) + assert_allclose(self.norm_template.pdf(x), + stats.norm.pdf(x, loc=1.0, scale=2.5), rtol=0.1) + + def test_cdf_ppf(self): + values = np.array([0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, + 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5]) + cdf_values = np.asarray([0.0/25.0, 0.0/25.0, 0.0/25.0, 0.5/25.0, + 1.0/25.0, 2.0/25.0, 3.0/25.0, 4.5/25.0, + 6.0/25.0, 8.0/25.0, 10.0/25.0, 12.5/25.0, + 15.0/25.0, 17.0/25.0, 19.0/25.0, 20.5/25.0, + 22.0/25.0, 23.5/25.0, 25.0/25.0, 25.0/25.0]) + assert_allclose(self.template.cdf(values), cdf_values) + # First three and last two values in cdf_value are not unique + assert_allclose(self.template.ppf(cdf_values[2:-1]), values[2:-1]) + + # Test of cdf and ppf are inverse functions + x = np.linspace(1.0, 9.0, 100) + assert_allclose(self.template.ppf(self.template.cdf(x)), x) + x = np.linspace(0.0, 1.0, 100) + assert_allclose(self.template.cdf(self.template.ppf(x)), x) + + x = np.linspace(-2, 2, 10) + assert_allclose(self.norm_template.cdf(x), + stats.norm.cdf(x, loc=1.0, scale=2.5), rtol=0.1) + + def test_rvs(self): + N = 10000 + sample = self.template.rvs(size=N, random_state=123) + assert_equal(np.sum(sample < 1.0), 0.0) + assert_allclose(np.sum(sample <= 2.0), 1.0/25.0 * N, rtol=0.2) + assert_allclose(np.sum(sample <= 2.5), 2.0/25.0 * N, rtol=0.2) + assert_allclose(np.sum(sample <= 3.0), 3.0/25.0 * N, rtol=0.1) + assert_allclose(np.sum(sample <= 3.5), 4.5/25.0 * N, rtol=0.1) + assert_allclose(np.sum(sample <= 4.0), 6.0/25.0 * N, rtol=0.1) + assert_allclose(np.sum(sample <= 4.5), 8.0/25.0 * N, rtol=0.1) + assert_allclose(np.sum(sample <= 5.0), 10.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 5.5), 12.5/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 6.0), 15.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 6.5), 17.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 7.0), 19.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 7.5), 20.5/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 8.0), 22.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 8.5), 23.5/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 9.0), 25.0/25.0 * N, rtol=0.05) + assert_allclose(np.sum(sample <= 9.0), 25.0/25.0 * N, rtol=0.05) + assert_equal(np.sum(sample > 9.0), 0.0) + + def test_munp(self): + for n in range(4): + assert_allclose(self.norm_template._munp(n), + stats.norm(1.0, 2.5).moment(n), rtol=0.05) + + def test_entropy(self): + assert_allclose(self.norm_template.entropy(), + stats.norm.entropy(loc=1.0, scale=2.5), rtol=0.05) + + +def test_histogram_non_uniform(): + # Tests rv_histogram works even for non-uniform bin widths + counts, bins = ([1, 1], [0, 1, 1001]) + + dist = stats.rv_histogram((counts, bins), density=False) + np.testing.assert_allclose(dist.pdf([0.5, 200]), [0.5, 0.0005]) + assert dist.median() == 1 + + dist = stats.rv_histogram((counts, bins), density=True) + np.testing.assert_allclose(dist.pdf([0.5, 200]), 1/1001) + assert dist.median() == 1001/2 + + # Omitting density produces a warning for non-uniform bins... + message = "Bin widths are not constant. Assuming..." + with pytest.warns(RuntimeWarning, match=message): + dist = stats.rv_histogram((counts, bins)) + assert dist.median() == 1001/2 # default is like `density=True` + + # ... but not for uniform bins + dist = stats.rv_histogram((counts, [0, 1, 2])) + assert dist.median() == 1 + + +class TestLogUniform: + def test_alias(self): + # This test makes sure that "reciprocal" and "loguniform" are + # aliases of the same distribution and that both are log-uniform + rng = np.random.default_rng(98643218961) + rv = stats.loguniform(10 ** -3, 10 ** 0) + rvs = rv.rvs(size=10000, random_state=rng) + + rng = np.random.default_rng(98643218961) + rv2 = stats.reciprocal(10 ** -3, 10 ** 0) + rvs2 = rv2.rvs(size=10000, random_state=rng) + + assert_allclose(rvs2, rvs) + + vals, _ = np.histogram(np.log10(rvs), bins=10) + assert 900 <= vals.min() <= vals.max() <= 1100 + assert np.abs(np.median(vals) - 1000) <= 10 + + @pytest.mark.parametrize("method", ['mle', 'mm']) + def test_fit_override(self, method): + # loguniform is overparameterized, so check that fit override enforces + # scale=1 unless fscale is provided by the user + rng = np.random.default_rng(98643218961) + rvs = stats.loguniform.rvs(0.1, 1, size=1000, random_state=rng) + + a, b, loc, scale = stats.loguniform.fit(rvs, method=method) + assert scale == 1 + + a, b, loc, scale = stats.loguniform.fit(rvs, fscale=2, method=method) + assert scale == 2 + + def test_overflow(self): + # original formulation had overflow issues; check that this is resolved + # Extensive accuracy tests elsewhere, no need to test all methods + rng = np.random.default_rng(7136519550773909093) + a, b = 1e-200, 1e200 + dist = stats.loguniform(a, b) + + # test roundtrip error + cdf = rng.uniform(0, 1, size=1000) + assert_allclose(dist.cdf(dist.ppf(cdf)), cdf) + rvs = dist.rvs(size=1000) + assert_allclose(dist.ppf(dist.cdf(rvs)), rvs) + + # test a property of the pdf (and that there is no overflow) + x = 10.**np.arange(-200, 200) + pdf = dist.pdf(x) # no overflow + assert_allclose(pdf[:-1]/pdf[1:], 10) + + # check munp against wikipedia reference + mean = (b - a)/(np.log(b) - np.log(a)) + assert_allclose(dist.mean(), mean) + + +class TestArgus: + def test_argus_rvs_large_chi(self): + # test that the algorithm can handle large values of chi + x = stats.argus.rvs(50, size=500, random_state=325) + assert_almost_equal(stats.argus(50).mean(), x.mean(), decimal=4) + + @pytest.mark.parametrize('chi, random_state', [ + [0.1, 325], # chi <= 0.5: rejection method case 1 + [1.3, 155], # 0.5 < chi <= 1.8: rejection method case 2 + [3.5, 135] # chi > 1.8: transform conditional Gamma distribution + ]) + def test_rvs(self, chi, random_state): + x = stats.argus.rvs(chi, size=500, random_state=random_state) + _, p = stats.kstest(x, "argus", (chi, )) + assert_(p > 0.05) + + @pytest.mark.parametrize('chi', [1e-9, 1e-6]) + def test_rvs_small_chi(self, chi): + # test for gh-11699 => rejection method case 1 can even handle chi=0 + # the CDF of the distribution for chi=0 is 1 - (1 - x**2)**(3/2) + # test rvs against distribution of limit chi=0 + r = stats.argus.rvs(chi, size=500, random_state=890981) + _, p = stats.kstest(r, lambda x: 1 - (1 - x**2)**(3/2)) + assert_(p > 0.05) + + # Expected values were computed with mpmath. + @pytest.mark.parametrize('chi, expected_mean', + [(1, 0.6187026683551835), + (10, 0.984805536783744), + (40, 0.9990617659702923), + (60, 0.9995831885165300), + (99, 0.9998469348663028)]) + def test_mean(self, chi, expected_mean): + m = stats.argus.mean(chi, scale=1) + assert_allclose(m, expected_mean, rtol=1e-13) + + # Expected values were computed with mpmath. + @pytest.mark.parametrize('chi, expected_var, rtol', + [(1, 0.05215651254197807, 1e-13), + (10, 0.00015805472008165595, 1e-11), + (40, 5.877763210262901e-07, 1e-8), + (60, 1.1590179389611416e-07, 1e-8), + (99, 1.5623277006064666e-08, 1e-8)]) + def test_var(self, chi, expected_var, rtol): + v = stats.argus.var(chi, scale=1) + assert_allclose(v, expected_var, rtol=rtol) + + # Expected values were computed with mpmath (code: see gh-13370). + @pytest.mark.parametrize('chi, expected, rtol', + [(0.9, 0.07646314974436118, 1e-14), + (0.5, 0.015429797891863365, 1e-14), + (0.1, 0.0001325825293278049, 1e-14), + (0.01, 1.3297677078224565e-07, 1e-15), + (1e-3, 1.3298072023958999e-10, 1e-14), + (1e-4, 1.3298075973486862e-13, 1e-14), + (1e-6, 1.32980760133771e-19, 1e-14), + (1e-9, 1.329807601338109e-28, 1e-15)]) + def test_argus_phi_small_chi(self, chi, expected, rtol): + assert_allclose(_argus_phi(chi), expected, rtol=rtol) + + # Expected values were computed with mpmath (code: see gh-13370). + @pytest.mark.parametrize( + 'chi, expected', + [(0.5, (0.28414073302940573, 1.2742227939992954, 1.2381254688255896)), + (0.2, (0.296172952995264, 1.2951290588110516, 1.1865767100877576)), + (0.1, (0.29791447523536274, 1.29806307956989, 1.1793168289857412)), + (0.01, (0.2984904104866452, 1.2990283628160553, 1.1769268414080531)), + (1e-3, (0.298496172925224, 1.2990380082487925, 1.176902956021053)), + (1e-4, (0.29849623054991836, 1.2990381047023793, 1.1769027171686324)), + (1e-6, (0.2984962311319278, 1.2990381056765605, 1.1769027147562232)), + (1e-9, (0.298496231131986, 1.299038105676658, 1.1769027147559818))]) + def test_pdf_small_chi(self, chi, expected): + x = np.array([0.1, 0.5, 0.9]) + assert_allclose(stats.argus.pdf(x, chi), expected, rtol=1e-13) + + # Expected values were computed with mpmath (code: see gh-13370). + @pytest.mark.parametrize( + 'chi, expected', + [(0.5, (0.9857660526895221, 0.6616565930168475, 0.08796070398429937)), + (0.2, (0.9851555052359501, 0.6514666238985464, 0.08362690023746594)), + (0.1, (0.9850670974995661, 0.6500061310508574, 0.08302050640683846)), + (0.01, (0.9850378582451867, 0.6495239242251358, 0.08282109244852445)), + (1e-3, (0.9850375656906663, 0.6495191015522573, 0.08281910005231098)), + (1e-4, (0.9850375627651049, 0.6495190533254682, 0.08281908012852317)), + (1e-6, (0.9850375627355568, 0.6495190528383777, 0.08281907992729293)), + (1e-9, (0.9850375627355538, 0.649519052838329, 0.0828190799272728))]) + def test_sf_small_chi(self, chi, expected): + x = np.array([0.1, 0.5, 0.9]) + assert_allclose(stats.argus.sf(x, chi), expected, rtol=1e-14) + + # Expected values were computed with mpmath (code: see gh-13370). + @pytest.mark.parametrize( + 'chi, expected', + [(0.5, (0.0142339473104779, 0.3383434069831524, 0.9120392960157007)), + (0.2, (0.014844494764049919, 0.34853337610145363, 0.916373099762534)), + (0.1, (0.014932902500433911, 0.34999386894914264, 0.9169794935931616)), + (0.01, (0.014962141754813293, 0.35047607577486417, 0.9171789075514756)), + (1e-3, (0.01496243430933372, 0.35048089844774266, 0.917180899947689)), + (1e-4, (0.014962437234895118, 0.3504809466745317, 0.9171809198714769)), + (1e-6, (0.01496243726444329, 0.3504809471616223, 0.9171809200727071)), + (1e-9, (0.014962437264446245, 0.350480947161671, 0.9171809200727272))]) + def test_cdf_small_chi(self, chi, expected): + x = np.array([0.1, 0.5, 0.9]) + assert_allclose(stats.argus.cdf(x, chi), expected, rtol=1e-12) + + # Expected values were computed with mpmath (code: see gh-13370). + @pytest.mark.parametrize( + 'chi, expected, rtol', + [(0.5, (0.5964284712757741, 0.052890651988588604), 1e-12), + (0.101, (0.5893490968089076, 0.053017469847275685), 1e-11), + (0.1, (0.5893431757009437, 0.05301755449499372), 1e-13), + (0.01, (0.5890515677940915, 0.05302167905837031), 1e-13), + (1e-3, (0.5890486520005177, 0.053021719862088104), 1e-13), + (1e-4, (0.5890486228426105, 0.0530217202700811), 1e-13), + (1e-6, (0.5890486225481156, 0.05302172027420182), 1e-13), + (1e-9, (0.5890486225480862, 0.05302172027420224), 1e-13)]) + def test_stats_small_chi(self, chi, expected, rtol): + val = stats.argus.stats(chi, moments='mv') + assert_allclose(val, expected, rtol=rtol) + + +class TestNakagami: + + def test_logpdf(self): + # Test nakagami logpdf for an input where the PDF is smaller + # than can be represented with 64 bit floating point. + # The expected value of logpdf was computed with mpmath: + # + # def logpdf(x, nu): + # x = mpmath.mpf(x) + # nu = mpmath.mpf(nu) + # return (mpmath.log(2) + nu*mpmath.log(nu) - + # mpmath.loggamma(nu) + (2*nu - 1)*mpmath.log(x) - + # nu*x**2) + # + nu = 2.5 + x = 25 + logp = stats.nakagami.logpdf(x, nu) + assert_allclose(logp, -1546.9253055607549) + + def test_sf_isf(self): + # Test nakagami sf and isf when the survival function + # value is very small. + # The expected value of the survival function was computed + # with mpmath: + # + # def sf(x, nu): + # x = mpmath.mpf(x) + # nu = mpmath.mpf(nu) + # return mpmath.gammainc(nu, nu*x*x, regularized=True) + # + nu = 2.5 + x0 = 5.0 + sf = stats.nakagami.sf(x0, nu) + assert_allclose(sf, 2.736273158588307e-25, rtol=1e-13) + # Check round trip back to x0. + x1 = stats.nakagami.isf(sf, nu) + assert_allclose(x1, x0, rtol=1e-13) + + @pytest.mark.parametrize("m, ref", + [(5, -0.097341814372152), + (0.5, 0.7257913526447274), + (10, -0.43426184310934907)]) + def test_entropy(self, m, ref): + # from sympy import * + # from mpmath import mp + # import numpy as np + # v, x = symbols('v, x', real=True, positive=True) + # pdf = 2 * v ** v / gamma(v) * x ** (2 * v - 1) * exp(-v * x ** 2) + # h = simplify(simplify(integrate(-pdf * log(pdf), (x, 0, oo)))) + # entropy = lambdify(v, h, 'mpmath') + # mp.dps = 200 + # nu = 5 + # ref = np.float64(entropy(mp.mpf(nu))) + # print(ref) + assert_allclose(stats.nakagami.entropy(m), ref, rtol=1.1e-14) + + @pytest.mark.parametrize("m, ref", + [(1e-100, -5.0e+99), (1e-10, -4999999965.442979), + (9.999e6, -7.333206478668433), (1.001e7, -7.3337562313259825), + (1e10, -10.787134112333835), (1e100, -114.40346329705756)]) + def test_extreme_nu(self, m, ref): + assert_allclose(stats.nakagami.entropy(m), ref) + + def test_entropy_overflow(self): + assert np.isfinite(stats.nakagami._entropy(1e100)) + assert np.isfinite(stats.nakagami._entropy(1e-100)) + + @pytest.mark.parametrize("nu, ref", + [(1e10, 0.9999999999875), + (1e3, 0.9998750078173821), + (1e-10, 1.772453850659802e-05)]) + def test_mean(self, nu, ref): + # reference values were computed with mpmath + # from mpmath import mp + # mp.dps = 500 + # nu = mp.mpf(1e10) + # float(mp.rf(nu, mp.mpf(0.5))/mp.sqrt(nu)) + assert_allclose(stats.nakagami.mean(nu), ref, rtol=1e-12) + + @pytest.mark.xfail(reason="Fit of nakagami not reliable, see gh-10908.") + @pytest.mark.parametrize('nu', [1.6, 2.5, 3.9]) + @pytest.mark.parametrize('loc', [25.0, 10, 35]) + @pytest.mark.parametrize('scale', [13, 5, 20]) + def test_fit(self, nu, loc, scale): + # Regression test for gh-13396 (21/27 cases failed previously) + # The first tuple of the parameters' values is discussed in gh-10908 + N = 100 + samples = stats.nakagami.rvs(size=N, nu=nu, loc=loc, + scale=scale, random_state=1337) + nu_est, loc_est, scale_est = stats.nakagami.fit(samples) + assert_allclose(nu_est, nu, rtol=0.2) + assert_allclose(loc_est, loc, rtol=0.2) + assert_allclose(scale_est, scale, rtol=0.2) + + def dlogl_dnu(nu, loc, scale): + return ((-2*nu + 1) * np.sum(1/(samples - loc)) + + 2*nu/scale**2 * np.sum(samples - loc)) + + def dlogl_dloc(nu, loc, scale): + return (N * (1 + np.log(nu) - polygamma(0, nu)) + + 2 * np.sum(np.log((samples - loc) / scale)) + - np.sum(((samples - loc) / scale)**2)) + + def dlogl_dscale(nu, loc, scale): + return (- 2 * N * nu / scale + + 2 * nu / scale ** 3 * np.sum((samples - loc) ** 2)) + + assert_allclose(dlogl_dnu(nu_est, loc_est, scale_est), 0, atol=1e-3) + assert_allclose(dlogl_dloc(nu_est, loc_est, scale_est), 0, atol=1e-3) + assert_allclose(dlogl_dscale(nu_est, loc_est, scale_est), 0, atol=1e-3) + + @pytest.mark.parametrize('loc', [25.0, 10, 35]) + @pytest.mark.parametrize('scale', [13, 5, 20]) + def test_fit_nu(self, loc, scale): + # For nu = 0.5, we have analytical values for + # the MLE of the loc and the scale + nu = 0.5 + n = 100 + samples = stats.nakagami.rvs(size=n, nu=nu, loc=loc, + scale=scale, random_state=1337) + nu_est, loc_est, scale_est = stats.nakagami.fit(samples, f0=nu) + + # Analytical values + loc_theo = np.min(samples) + scale_theo = np.sqrt(np.mean((samples - loc_est) ** 2)) + + assert_allclose(nu_est, nu, rtol=1e-7) + assert_allclose(loc_est, loc_theo, rtol=1e-7) + assert_allclose(scale_est, scale_theo, rtol=1e-7) + + +class TestWrapCauchy: + + def test_cdf_shape_broadcasting(self): + # Regression test for gh-13791. + # Check that wrapcauchy.cdf broadcasts the shape parameter + # correctly. + c = np.array([[0.03, 0.25], [0.5, 0.75]]) + x = np.array([[1.0], [4.0]]) + p = stats.wrapcauchy.cdf(x, c) + assert p.shape == (2, 2) + scalar_values = [stats.wrapcauchy.cdf(x1, c1) + for (x1, c1) in np.nditer((x, c))] + assert_allclose(p.ravel(), scalar_values, rtol=1e-13) + + def test_cdf_center(self): + p = stats.wrapcauchy.cdf(np.pi, 0.03) + assert_allclose(p, 0.5, rtol=1e-14) + + def test_cdf(self): + x1 = 1.0 # less than pi + x2 = 4.0 # greater than pi + c = 0.75 + p = stats.wrapcauchy.cdf([x1, x2], c) + cr = (1 + c)/(1 - c) + assert_allclose(p[0], np.arctan(cr*np.tan(x1/2))/np.pi) + assert_allclose(p[1], 1 - np.arctan(cr*np.tan(np.pi - x2/2))/np.pi) + + +def test_rvs_no_size_error(): + # _rvs methods must have parameter `size`; see gh-11394 + class rvs_no_size_gen(stats.rv_continuous): + def _rvs(self): + return 1 + + rvs_no_size = rvs_no_size_gen(name='rvs_no_size') + + with assert_raises(TypeError, match=r"_rvs\(\) got (an|\d) unexpected"): + rvs_no_size.rvs() + + +@pytest.mark.parametrize('distname, args', invdistdiscrete + invdistcont) +def test_support_gh13294_regression(distname, args): + if distname in skip_test_support_gh13294_regression: + pytest.skip(f"skipping test for the support method for " + f"distribution {distname}.") + dist = getattr(stats, distname) + # test support method with invalid arguments + if isinstance(dist, stats.rv_continuous): + # test with valid scale + if len(args) != 0: + a0, b0 = dist.support(*args) + assert_equal(a0, np.nan) + assert_equal(b0, np.nan) + # test with invalid scale + # For some distributions, that take no parameters, + # the case of only invalid scale occurs and hence, + # it is implicitly tested in this test case. + loc1, scale1 = 0, -1 + a1, b1 = dist.support(*args, loc1, scale1) + assert_equal(a1, np.nan) + assert_equal(b1, np.nan) + else: + a, b = dist.support(*args) + assert_equal(a, np.nan) + assert_equal(b, np.nan) + + +def test_support_broadcasting_gh13294_regression(): + a0, b0 = stats.norm.support([0, 0, 0, 1], [1, 1, 1, -1]) + ex_a0 = np.array([-np.inf, -np.inf, -np.inf, np.nan]) + ex_b0 = np.array([np.inf, np.inf, np.inf, np.nan]) + assert_equal(a0, ex_a0) + assert_equal(b0, ex_b0) + assert a0.shape == ex_a0.shape + assert b0.shape == ex_b0.shape + + a1, b1 = stats.norm.support([], []) + ex_a1, ex_b1 = np.array([]), np.array([]) + assert_equal(a1, ex_a1) + assert_equal(b1, ex_b1) + assert a1.shape == ex_a1.shape + assert b1.shape == ex_b1.shape + + a2, b2 = stats.norm.support([0, 0, 0, 1], [-1]) + ex_a2 = np.array(4*[np.nan]) + ex_b2 = np.array(4*[np.nan]) + assert_equal(a2, ex_a2) + assert_equal(b2, ex_b2) + assert a2.shape == ex_a2.shape + assert b2.shape == ex_b2.shape + + +def test_stats_broadcasting_gh14953_regression(): + # test case in gh14953 + loc = [0., 0.] + scale = [[1.], [2.], [3.]] + assert_equal(stats.norm.var(loc, scale), [[1., 1.], [4., 4.], [9., 9.]]) + # test some edge cases + loc = np.empty((0, )) + scale = np.empty((1, 0)) + assert stats.norm.var(loc, scale).shape == (1, 0) + + +# Check a few values of the cosine distribution's cdf, sf, ppf and +# isf methods. Expected values were computed with mpmath. + +@pytest.mark.parametrize('x, expected', + [(-3.14159, 4.956444476505336e-19), + (3.14, 0.9999999998928399)]) +def test_cosine_cdf_sf(x, expected): + assert_allclose(stats.cosine.cdf(x), expected) + assert_allclose(stats.cosine.sf(-x), expected) + + +@pytest.mark.parametrize('p, expected', + [(1e-6, -3.1080612413765905), + (1e-17, -3.141585429601399), + (0.975, 2.1447547020964923)]) +def test_cosine_ppf_isf(p, expected): + assert_allclose(stats.cosine.ppf(p), expected) + assert_allclose(stats.cosine.isf(p), -expected) + + +def test_cosine_logpdf_endpoints(): + logp = stats.cosine.logpdf([-np.pi, np.pi]) + # reference value calculated using mpmath assuming `np.cos(-1)` is four + # floating point numbers too high. See gh-18382. + assert_array_less(logp, -37.18838327496655) + + +def test_distr_params_lists(): + # distribution objects are extra distributions added in + # test_discrete_basic. All other distributions are strings (names) + # and so we only choose those to compare whether both lists match. + discrete_distnames = {name for name, _ in distdiscrete + if isinstance(name, str)} + invdiscrete_distnames = {name for name, _ in invdistdiscrete} + assert discrete_distnames == invdiscrete_distnames + + cont_distnames = {name for name, _ in distcont} + invcont_distnames = {name for name, _ in invdistcont} + assert cont_distnames == invcont_distnames + + +def test_moment_order_4(): + # gh-13655 reported that if a distribution has a `_stats` method that + # accepts the `moments` parameter, then if the distribution's `moment` + # method is called with `order=4`, the faster/more accurate`_stats` gets + # called, but the results aren't used, and the generic `_munp` method is + # called to calculate the moment anyway. This tests that the issue has + # been fixed. + # stats.skewnorm._stats accepts the `moments` keyword + stats.skewnorm._stats(a=0, moments='k') # no failure = has `moments` + # When `moment` is called, `_stats` is used, so the moment is very accurate + # (exactly equal to Pearson's kurtosis of the normal distribution, 3) + assert stats.skewnorm.moment(order=4, a=0) == 3.0 + # At the time of gh-13655, skewnorm._munp() used the generic method + # to compute its result, which was inefficient and not very accurate. + # At that time, the following assertion would fail. skewnorm._munp() + # has since been made more accurate and efficient, so now this test + # is expected to pass. + assert stats.skewnorm._munp(4, 0) == 3.0 + + +class TestRelativisticBW: + @pytest.fixture + def ROOT_pdf_sample_data(self): + """Sample data points for pdf computed with CERN's ROOT + + See - https://root.cern/ + + Uses ROOT.TMath.BreitWignerRelativistic, available in ROOT + versions 6.27+ + + pdf calculated for Z0 Boson, W Boson, and Higgs Boson for + x in `np.linspace(0, 200, 401)`. + """ + data = np.load( + Path(__file__).parent / + 'data/rel_breitwigner_pdf_sample_data_ROOT.npy' + ) + data = np.rec.fromarrays(data.T, names='x,pdf,rho,gamma') + return data + + @pytest.mark.parametrize( + "rho,gamma,rtol", [ + (36.545206797050334, 2.4952, 5e-14), # Z0 Boson + (38.55107913669065, 2.085, 1e-14), # W Boson + (96292.3076923077, 0.0013, 5e-13), # Higgs Boson + ] + ) + def test_pdf_against_ROOT(self, ROOT_pdf_sample_data, rho, gamma, rtol): + data = ROOT_pdf_sample_data[ + (ROOT_pdf_sample_data['rho'] == rho) + & (ROOT_pdf_sample_data['gamma'] == gamma) + ] + x, pdf = data['x'], data['pdf'] + assert_allclose( + pdf, stats.rel_breitwigner.pdf(x, rho, scale=gamma), rtol=rtol + ) + + @pytest.mark.parametrize("rho, Gamma, rtol", [ + (36.545206797050334, 2.4952, 5e-13), # Z0 Boson + (38.55107913669065, 2.085, 5e-13), # W Boson + (96292.3076923077, 0.0013, 5e-10), # Higgs Boson + ] + ) + def test_pdf_against_simple_implementation(self, rho, Gamma, rtol): + # reference implementation straight from formulas on Wikipedia [1] + def pdf(E, M, Gamma): + gamma = np.sqrt(M**2 * (M**2 + Gamma**2)) + k = (2 * np.sqrt(2) * M * Gamma * gamma + / (np.pi * np.sqrt(M**2 + gamma))) + return k / ((E**2 - M**2)**2 + M**2*Gamma**2) + # get reasonable values at which to evaluate the CDF + p = np.linspace(0.05, 0.95, 10) + x = stats.rel_breitwigner.ppf(p, rho, scale=Gamma) + res = stats.rel_breitwigner.pdf(x, rho, scale=Gamma) + ref = pdf(x, rho*Gamma, Gamma) + assert_allclose(res, ref, rtol=rtol) + + @pytest.mark.parametrize( + "rho,gamma", [ + pytest.param( + 36.545206797050334, 2.4952, marks=pytest.mark.slow + ), # Z0 Boson + pytest.param( + 38.55107913669065, 2.085, marks=pytest.mark.xslow + ), # W Boson + pytest.param( + 96292.3076923077, 0.0013, marks=pytest.mark.xslow + ), # Higgs Boson + ] + ) + def test_fit_floc(self, rho, gamma): + """Tests fit for cases where floc is set. + + `rel_breitwigner` has special handling for these cases. + """ + seed = 6936804688480013683 + rng = np.random.default_rng(seed) + data = stats.rel_breitwigner.rvs( + rho, scale=gamma, size=1000, random_state=rng + ) + fit = stats.rel_breitwigner.fit(data, floc=0) + assert_allclose((fit[0], fit[2]), (rho, gamma), rtol=2e-1) + assert fit[1] == 0 + # Check again with fscale set. + fit = stats.rel_breitwigner.fit(data, floc=0, fscale=gamma) + assert_allclose(fit[0], rho, rtol=1e-2) + assert (fit[1], fit[2]) == (0, gamma) + + +class TestJohnsonSU: + @pytest.mark.parametrize("case", [ # a, b, loc, scale, m1, m2, g1, g2 + (-0.01, 1.1, 0.02, 0.0001, 0.02000137427557091, + 2.1112742956578063e-08, 0.05989781342460999, 20.36324408592951-3), + (2.554395574161155, 2.2482281679651965, 0, 1, -1.54215386737391, + 0.7629882028469993, -1.256656139406788, 6.303058419339775-3)]) + def test_moment_gh18071(self, case): + # gh-18071 reported an IntegrationWarning emitted by johnsonsu.stats + # Check that the warning is no longer emitted and that the values + # are accurate compared against results from Mathematica. + # Reference values from Mathematica, e.g. + # Mean[JohnsonDistribution["SU",-0.01, 1.1, 0.02, 0.0001]] + res = stats.johnsonsu.stats(*case[:4], moments='mvsk') + assert_allclose(res, case[4:], rtol=1e-14) + + +class TestTruncPareto: + def test_pdf(self): + # PDF is that of the truncated pareto distribution + b, c = 1.8, 5.3 + x = np.linspace(1.8, 5.3) + res = stats.truncpareto(b, c).pdf(x) + ref = stats.pareto(b).pdf(x) / stats.pareto(b).cdf(c) + assert_allclose(res, ref) + + @pytest.mark.parametrize('fix_loc', [True, False]) + @pytest.mark.parametrize('fix_scale', [True, False]) + @pytest.mark.parametrize('fix_b', [True, False]) + @pytest.mark.parametrize('fix_c', [True, False]) + def test_fit(self, fix_loc, fix_scale, fix_b, fix_c): + + rng = np.random.default_rng(6747363148258237171) + b, c, loc, scale = 1.8, 5.3, 1, 2.5 + dist = stats.truncpareto(b, c, loc=loc, scale=scale) + data = dist.rvs(size=500, random_state=rng) + + kwds = {} + if fix_loc: + kwds['floc'] = loc + if fix_scale: + kwds['fscale'] = scale + if fix_b: + kwds['f0'] = b + if fix_c: + kwds['f1'] = c + + if fix_loc and fix_scale and fix_b and fix_c: + message = "All parameters fixed. There is nothing to optimize." + with pytest.raises(RuntimeError, match=message): + stats.truncpareto.fit(data, **kwds) + else: + _assert_less_or_close_loglike(stats.truncpareto, data, **kwds) + + +class TestKappa3: + def test_sf(self): + # During development of gh-18822, we found that the override of + # kappa3.sf could experience overflow where the version in main did + # not. Check that this does not happen in final implementation. + sf0 = 1 - stats.kappa3.cdf(0.5, 1e5) + sf1 = stats.kappa3.sf(0.5, 1e5) + assert_allclose(sf1, sf0) + + +# Cases are (distribution name, log10 of smallest probability mass to test, +# log10 of the complement of the largest probability mass to test, atol, +# rtol). None uses default values. +@pytest.mark.parametrize("case", [("kappa3", None, None, None, None), + ("loglaplace", None, None, None, None), + ("lognorm", None, None, None, None), + ("lomax", None, None, None, None), + ("pareto", None, None, None, None),]) +def test_sf_isf_overrides(case): + # Test that SF is the inverse of ISF. Supplements + # `test_continuous_basic.check_sf_isf` for distributions with overridden + # `sf` and `isf` methods. + distname, lp1, lp2, atol, rtol = case + + lpm = np.log10(0.5) # log10 of the probability mass at the median + lp1 = lp1 or -290 + lp2 = lp2 or -14 + atol = atol or 0 + rtol = rtol or 1e-12 + dist = getattr(stats, distname) + params = dict(distcont)[distname] + dist_frozen = dist(*params) + + # Test (very deep) right tail to median. We can benchmark with random + # (loguniform) points, but strictly logspaced points are fine for tests. + ref = np.logspace(lp1, lpm) + res = dist_frozen.sf(dist_frozen.isf(ref)) + assert_allclose(res, ref, atol=atol, rtol=rtol) + + # test median to left tail + ref = 1 - np.logspace(lp2, lpm, 20) + res = dist_frozen.sf(dist_frozen.isf(ref)) + assert_allclose(res, ref, atol=atol, rtol=rtol) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_entropy.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_entropy.py new file mode 100644 index 0000000000000000000000000000000000000000..a25da83952b702da117ffbe11d0a9ded84a49f7a --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_entropy.py @@ -0,0 +1,286 @@ +import numpy as np +from numpy.testing import assert_equal, assert_allclose +# avoid new uses of the following; prefer assert/np.testing.assert_allclose +from numpy.testing import (assert_, assert_almost_equal, + assert_array_almost_equal) + +import pytest +from pytest import raises as assert_raises +import scipy.stats as stats + + +class TestEntropy: + def test_entropy_positive(self): + # See ticket #497 + pk = [0.5, 0.2, 0.3] + qk = [0.1, 0.25, 0.65] + eself = stats.entropy(pk, pk) + edouble = stats.entropy(pk, qk) + assert_(0.0 == eself) + assert_(edouble >= 0.0) + + def test_entropy_base(self): + pk = np.ones(16, float) + S = stats.entropy(pk, base=2.) + assert_(abs(S - 4.) < 1.e-5) + + qk = np.ones(16, float) + qk[:8] = 2. + S = stats.entropy(pk, qk) + S2 = stats.entropy(pk, qk, base=2.) + assert_(abs(S/S2 - np.log(2.)) < 1.e-5) + + def test_entropy_zero(self): + # Test for PR-479 + assert_almost_equal(stats.entropy([0, 1, 2]), 0.63651416829481278, + decimal=12) + + def test_entropy_2d(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + qk = [[0.2, 0.1], [0.3, 0.6], [0.5, 0.3]] + assert_array_almost_equal(stats.entropy(pk, qk), + [0.1933259, 0.18609809]) + + def test_entropy_2d_zero(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + qk = [[0.0, 0.1], [0.3, 0.6], [0.5, 0.3]] + assert_array_almost_equal(stats.entropy(pk, qk), + [np.inf, 0.18609809]) + + pk[0][0] = 0.0 + assert_array_almost_equal(stats.entropy(pk, qk), + [0.17403988, 0.18609809]) + + def test_entropy_base_2d_nondefault_axis(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + assert_array_almost_equal(stats.entropy(pk, axis=1), + [0.63651417, 0.63651417, 0.66156324]) + + def test_entropy_2d_nondefault_axis(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + qk = [[0.2, 0.1], [0.3, 0.6], [0.5, 0.3]] + assert_array_almost_equal(stats.entropy(pk, qk, axis=1), + [0.231049, 0.231049, 0.127706]) + + def test_entropy_raises_value_error(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + qk = [[0.1, 0.2], [0.6, 0.3]] + assert_raises(ValueError, stats.entropy, pk, qk) + + def test_base_entropy_with_axis_0_is_equal_to_default(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + assert_array_almost_equal(stats.entropy(pk, axis=0), + stats.entropy(pk)) + + def test_entropy_with_axis_0_is_equal_to_default(self): + pk = [[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]] + qk = [[0.2, 0.1], [0.3, 0.6], [0.5, 0.3]] + assert_array_almost_equal(stats.entropy(pk, qk, axis=0), + stats.entropy(pk, qk)) + + def test_base_entropy_transposed(self): + pk = np.array([[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]]) + assert_array_almost_equal(stats.entropy(pk.T).T, + stats.entropy(pk, axis=1)) + + def test_entropy_transposed(self): + pk = np.array([[0.1, 0.2], [0.6, 0.3], [0.3, 0.5]]) + qk = np.array([[0.2, 0.1], [0.3, 0.6], [0.5, 0.3]]) + assert_array_almost_equal(stats.entropy(pk.T, qk.T).T, + stats.entropy(pk, qk, axis=1)) + + def test_entropy_broadcasting(self): + np.random.rand(0) + x = np.random.rand(3) + y = np.random.rand(2, 1) + res = stats.entropy(x, y, axis=-1) + assert_equal(res[0], stats.entropy(x, y[0])) + assert_equal(res[1], stats.entropy(x, y[1])) + + def test_entropy_shape_mismatch(self): + x = np.random.rand(10, 1, 12) + y = np.random.rand(11, 2) + message = "Array shapes are incompatible for broadcasting." + with pytest.raises(ValueError, match=message): + stats.entropy(x, y) + + def test_input_validation(self): + x = np.random.rand(10) + message = "`base` must be a positive number." + with pytest.raises(ValueError, match=message): + stats.entropy(x, base=-2) + + +class TestDifferentialEntropy: + """ + Vasicek results are compared with the R package vsgoftest. + + # library(vsgoftest) + # + # samp <- c() + # entropy.estimate(x = samp, window = ) + + """ + + def test_differential_entropy_vasicek(self): + + random_state = np.random.RandomState(0) + values = random_state.standard_normal(100) + + entropy = stats.differential_entropy(values, method='vasicek') + assert_allclose(entropy, 1.342551, rtol=1e-6) + + entropy = stats.differential_entropy(values, window_length=1, + method='vasicek') + assert_allclose(entropy, 1.122044, rtol=1e-6) + + entropy = stats.differential_entropy(values, window_length=8, + method='vasicek') + assert_allclose(entropy, 1.349401, rtol=1e-6) + + def test_differential_entropy_vasicek_2d_nondefault_axis(self): + random_state = np.random.RandomState(0) + values = random_state.standard_normal((3, 100)) + + entropy = stats.differential_entropy(values, axis=1, method='vasicek') + assert_allclose( + entropy, + [1.342551, 1.341826, 1.293775], + rtol=1e-6, + ) + + entropy = stats.differential_entropy(values, axis=1, window_length=1, + method='vasicek') + assert_allclose( + entropy, + [1.122044, 1.102944, 1.129616], + rtol=1e-6, + ) + + entropy = stats.differential_entropy(values, axis=1, window_length=8, + method='vasicek') + assert_allclose( + entropy, + [1.349401, 1.338514, 1.292332], + rtol=1e-6, + ) + + def test_differential_entropy_raises_value_error(self): + random_state = np.random.RandomState(0) + values = random_state.standard_normal((3, 100)) + + error_str = ( + r"Window length \({window_length}\) must be positive and less " + r"than half the sample size \({sample_size}\)." + ) + + sample_size = values.shape[1] + + for window_length in {-1, 0, sample_size//2, sample_size}: + + formatted_error_str = error_str.format( + window_length=window_length, + sample_size=sample_size, + ) + + with assert_raises(ValueError, match=formatted_error_str): + stats.differential_entropy( + values, + window_length=window_length, + axis=1, + ) + + def test_base_differential_entropy_with_axis_0_is_equal_to_default(self): + random_state = np.random.RandomState(0) + values = random_state.standard_normal((100, 3)) + + entropy = stats.differential_entropy(values, axis=0) + default_entropy = stats.differential_entropy(values) + assert_allclose(entropy, default_entropy) + + def test_base_differential_entropy_transposed(self): + random_state = np.random.RandomState(0) + values = random_state.standard_normal((3, 100)) + + assert_allclose( + stats.differential_entropy(values.T).T, + stats.differential_entropy(values, axis=1), + ) + + def test_input_validation(self): + x = np.random.rand(10) + + message = "`base` must be a positive number or `None`." + with pytest.raises(ValueError, match=message): + stats.differential_entropy(x, base=-2) + + message = "`method` must be one of..." + with pytest.raises(ValueError, match=message): + stats.differential_entropy(x, method='ekki-ekki') + + @pytest.mark.parametrize('method', ['vasicek', 'van es', + 'ebrahimi', 'correa']) + def test_consistency(self, method): + # test that method is a consistent estimator + n = 10000 if method == 'correa' else 1000000 + rvs = stats.norm.rvs(size=n, random_state=0) + expected = stats.norm.entropy() + res = stats.differential_entropy(rvs, method=method) + assert_allclose(res, expected, rtol=0.005) + + # values from differential_entropy reference [6], table 1, n=50, m=7 + norm_rmse_std_cases = { # method: (RMSE, STD) + 'vasicek': (0.198, 0.109), + 'van es': (0.212, 0.110), + 'correa': (0.135, 0.112), + 'ebrahimi': (0.128, 0.109) + } + + @pytest.mark.parametrize('method, expected', + list(norm_rmse_std_cases.items())) + def test_norm_rmse_std(self, method, expected): + # test that RMSE and standard deviation of estimators matches values + # given in differential_entropy reference [6]. Incidentally, also + # tests vectorization. + reps, n, m = 10000, 50, 7 + rmse_expected, std_expected = expected + rvs = stats.norm.rvs(size=(reps, n), random_state=0) + true_entropy = stats.norm.entropy() + res = stats.differential_entropy(rvs, window_length=m, + method=method, axis=-1) + assert_allclose(np.sqrt(np.mean((res - true_entropy)**2)), + rmse_expected, atol=0.005) + assert_allclose(np.std(res), std_expected, atol=0.002) + + # values from differential_entropy reference [6], table 2, n=50, m=7 + expon_rmse_std_cases = { # method: (RMSE, STD) + 'vasicek': (0.194, 0.148), + 'van es': (0.179, 0.149), + 'correa': (0.155, 0.152), + 'ebrahimi': (0.151, 0.148) + } + + @pytest.mark.parametrize('method, expected', + list(expon_rmse_std_cases.items())) + def test_expon_rmse_std(self, method, expected): + # test that RMSE and standard deviation of estimators matches values + # given in differential_entropy reference [6]. Incidentally, also + # tests vectorization. + reps, n, m = 10000, 50, 7 + rmse_expected, std_expected = expected + rvs = stats.expon.rvs(size=(reps, n), random_state=0) + true_entropy = stats.expon.entropy() + res = stats.differential_entropy(rvs, window_length=m, + method=method, axis=-1) + assert_allclose(np.sqrt(np.mean((res - true_entropy)**2)), + rmse_expected, atol=0.005) + assert_allclose(np.std(res), std_expected, atol=0.002) + + @pytest.mark.parametrize('n, method', [(8, 'van es'), + (12, 'ebrahimi'), + (1001, 'vasicek')]) + def test_method_auto(self, n, method): + rvs = stats.norm.rvs(size=(n,), random_state=0) + res1 = stats.differential_entropy(rvs) + res2 = stats.differential_entropy(rvs, method=method) + assert res1 == res2 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fast_gen_inversion.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fast_gen_inversion.py new file mode 100644 index 0000000000000000000000000000000000000000..3369af9691501fcf5b0fb1bde06d82afceacae40 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fast_gen_inversion.py @@ -0,0 +1,430 @@ +import pytest +import warnings +import numpy as np +from numpy.testing import (assert_array_equal, assert_allclose, + suppress_warnings) +from copy import deepcopy +from scipy.stats.sampling import FastGeneratorInversion +from scipy import stats + + +def test_bad_args(): + # loc and scale must be scalar + with pytest.raises(ValueError, match="loc must be scalar"): + FastGeneratorInversion(stats.norm(loc=(1.2, 1.3))) + with pytest.raises(ValueError, match="scale must be scalar"): + FastGeneratorInversion(stats.norm(scale=[1.5, 5.7])) + + with pytest.raises(ValueError, match="'test' cannot be used to seed"): + FastGeneratorInversion(stats.norm(), random_state="test") + + msg = "Each of the 1 shape parameters must be a scalar" + with pytest.raises(ValueError, match=msg): + FastGeneratorInversion(stats.gamma([1.3, 2.5])) + + with pytest.raises(ValueError, match="`dist` must be a frozen"): + FastGeneratorInversion("xy") + + with pytest.raises(ValueError, match="Distribution 'truncnorm' is not"): + FastGeneratorInversion(stats.truncnorm(1.3, 4.5)) + + +def test_random_state(): + # fixed seed + gen = FastGeneratorInversion(stats.norm(), random_state=68734509) + x1 = gen.rvs(size=10) + gen.random_state = 68734509 + x2 = gen.rvs(size=10) + assert_array_equal(x1, x2) + + # Generator + urng = np.random.default_rng(20375857) + gen = FastGeneratorInversion(stats.norm(), random_state=urng) + x1 = gen.rvs(size=10) + gen.random_state = np.random.default_rng(20375857) + x2 = gen.rvs(size=10) + assert_array_equal(x1, x2) + + # RandomState + urng = np.random.RandomState(2364) + gen = FastGeneratorInversion(stats.norm(), random_state=urng) + x1 = gen.rvs(size=10) + gen.random_state = np.random.RandomState(2364) + x2 = gen.rvs(size=10) + assert_array_equal(x1, x2) + + # if evaluate_error is called, it must not interfere with the random_state + # used by rvs + gen = FastGeneratorInversion(stats.norm(), random_state=68734509) + x1 = gen.rvs(size=10) + _ = gen.evaluate_error(size=5) # this will generate 5 uniform rvs + x2 = gen.rvs(size=10) + gen.random_state = 68734509 + x3 = gen.rvs(size=20) + assert_array_equal(x2, x3[10:]) + + +dists_with_params = [ + ("alpha", (3.5,)), + ("anglit", ()), + ("argus", (3.5,)), + ("argus", (5.1,)), + ("beta", (1.5, 0.9)), + ("cosine", ()), + ("betaprime", (2.5, 3.3)), + ("bradford", (1.2,)), + ("burr", (1.3, 2.4)), + ("burr12", (0.7, 1.2)), + ("cauchy", ()), + ("chi2", (3.5,)), + ("chi", (4.5,)), + ("crystalball", (0.7, 1.2)), + ("expon", ()), + ("gamma", (1.5,)), + ("gennorm", (2.7,)), + ("gumbel_l", ()), + ("gumbel_r", ()), + ("hypsecant", ()), + ("invgauss", (3.1,)), + ("invweibull", (1.5,)), + ("laplace", ()), + ("logistic", ()), + ("maxwell", ()), + ("moyal", ()), + ("norm", ()), + ("pareto", (1.3,)), + ("powerlaw", (7.6,)), + ("rayleigh", ()), + ("semicircular", ()), + ("t", (5.7,)), + ("wald", ()), + ("weibull_max", (2.4,)), + ("weibull_min", (1.2,)), +] + + +@pytest.mark.parametrize(("distname, args"), dists_with_params) +def test_rvs_and_ppf(distname, args): + # check sample against rvs generated by rv_continuous + urng = np.random.default_rng(9807324628097097) + rng1 = getattr(stats, distname)(*args) + rvs1 = rng1.rvs(size=500, random_state=urng) + rng2 = FastGeneratorInversion(rng1, random_state=urng) + rvs2 = rng2.rvs(size=500) + assert stats.cramervonmises_2samp(rvs1, rvs2).pvalue > 0.01 + + # check ppf + q = [0.001, 0.1, 0.5, 0.9, 0.999] + assert_allclose(rng1.ppf(q), rng2.ppf(q), atol=1e-10) + + +@pytest.mark.parametrize(("distname, args"), dists_with_params) +def test_u_error(distname, args): + # check sample against rvs generated by rv_continuous + dist = getattr(stats, distname)(*args) + with suppress_warnings() as sup: + # filter the warnings thrown by UNU.RAN + sup.filter(RuntimeWarning) + rng = FastGeneratorInversion(dist) + u_error, x_error = rng.evaluate_error( + size=10_000, random_state=9807324628097097, x_error=False + ) + assert u_error <= 1e-10 + + +@pytest.mark.xfail(reason="geninvgauss CDF is not accurate") +def test_geninvgauss_uerror(): + dist = stats.geninvgauss(3.2, 1.5) + rng = FastGeneratorInversion(dist) + err = rng.evaluate_error(size=10_000, random_state=67982) + assert err[0] < 1e-10 + +# TODO: add more distributions +@pytest.mark.parametrize(("distname, args"), [("beta", (0.11, 0.11))]) +def test_error_extreme_params(distname, args): + # take extreme parameters where u-error might not be below the tolerance + # due to limitations of floating point arithmetic + with suppress_warnings() as sup: + # filter the warnings thrown by UNU.RAN for such extreme parameters + sup.filter(RuntimeWarning) + dist = getattr(stats, distname)(*args) + rng = FastGeneratorInversion(dist) + u_error, x_error = rng.evaluate_error( + size=10_000, random_state=980732462809709732623, x_error=True + ) + if u_error >= 2.5 * 1e-10: + assert x_error < 1e-9 + + +def test_evaluate_error_inputs(): + gen = FastGeneratorInversion(stats.norm()) + with pytest.raises(ValueError, match="size must be an integer"): + gen.evaluate_error(size=3.5) + with pytest.raises(ValueError, match="size must be an integer"): + gen.evaluate_error(size=(3, 3)) + + +def test_rvs_ppf_loc_scale(): + loc, scale = 3.5, 2.3 + dist = stats.norm(loc=loc, scale=scale) + rng = FastGeneratorInversion(dist, random_state=1234) + r = rng.rvs(size=1000) + r_rescaled = (r - loc) / scale + assert stats.cramervonmises(r_rescaled, "norm").pvalue > 0.01 + q = [0.001, 0.1, 0.5, 0.9, 0.999] + assert_allclose(rng._ppf(q), rng.ppf(q), atol=1e-10) + + +def test_domain(): + # only a basic check that the domain argument is passed to the + # UNU.RAN generators + rng = FastGeneratorInversion(stats.norm(), domain=(-1, 1)) + r = rng.rvs(size=100) + assert -1 <= r.min() < r.max() <= 1 + + # if loc and scale are used, new domain is loc + scale*domain + loc, scale = 3.5, 1.3 + dist = stats.norm(loc=loc, scale=scale) + rng = FastGeneratorInversion(dist, domain=(-1.5, 2)) + r = rng.rvs(size=100) + lb, ub = loc - scale * 1.5, loc + scale * 2 + assert lb <= r.min() < r.max() <= ub + + +@pytest.mark.parametrize(("distname, args, expected"), + [("beta", (3.5, 2.5), (0, 1)), + ("norm", (), (-np.inf, np.inf))]) +def test_support(distname, args, expected): + # test that the support is updated if truncation and loc/scale are applied + # use beta distribution since it is a transformed betaprime distribution, + # so it is important that the correct support is considered + # (i.e., the support of beta is (0,1), while betaprime is (0, inf)) + dist = getattr(stats, distname)(*args) + rng = FastGeneratorInversion(dist) + assert_array_equal(rng.support(), expected) + rng.loc = 1 + rng.scale = 2 + assert_array_equal(rng.support(), 1 + 2*np.array(expected)) + + +@pytest.mark.parametrize(("distname, args"), + [("beta", (3.5, 2.5)), ("norm", ())]) +def test_support_truncation(distname, args): + # similar test for truncation + dist = getattr(stats, distname)(*args) + rng = FastGeneratorInversion(dist, domain=(0.5, 0.7)) + assert_array_equal(rng.support(), (0.5, 0.7)) + rng.loc = 1 + rng.scale = 2 + assert_array_equal(rng.support(), (1 + 2 * 0.5, 1 + 2 * 0.7)) + + +def test_domain_shift_truncation(): + # center of norm is zero, it should be shifted to the left endpoint of + # domain. if this was not the case, PINV in UNURAN would raise a warning + # as the center is not inside the domain + with warnings.catch_warnings(): + warnings.simplefilter("error") + rng = FastGeneratorInversion(stats.norm(), domain=(1, 2)) + r = rng.rvs(size=100) + assert 1 <= r.min() < r.max() <= 2 + + +def test_non_rvs_methods_with_domain(): + # as a first step, compare truncated normal against stats.truncnorm + rng = FastGeneratorInversion(stats.norm(), domain=(2.3, 3.2)) + trunc_norm = stats.truncnorm(2.3, 3.2) + # take values that are inside and outside the domain + x = (2.0, 2.4, 3.0, 3.4) + p = (0.01, 0.5, 0.99) + assert_allclose(rng._cdf(x), trunc_norm.cdf(x)) + assert_allclose(rng._ppf(p), trunc_norm.ppf(p)) + loc, scale = 2, 3 + rng.loc = 2 + rng.scale = 3 + trunc_norm = stats.truncnorm(2.3, 3.2, loc=loc, scale=scale) + x = np.array(x) * scale + loc + assert_allclose(rng._cdf(x), trunc_norm.cdf(x)) + assert_allclose(rng._ppf(p), trunc_norm.ppf(p)) + + # do another sanity check with beta distribution + # in that case, it is important to use the correct domain since beta + # is a transformation of betaprime which has a different support + rng = FastGeneratorInversion(stats.beta(2.5, 3.5), domain=(0.3, 0.7)) + rng.loc = 2 + rng.scale = 2.5 + # the support is 2.75, , 3.75 (2 + 2.5 * 0.3, 2 + 2.5 * 0.7) + assert_array_equal(rng.support(), (2.75, 3.75)) + x = np.array([2.74, 2.76, 3.74, 3.76]) + # the cdf needs to be zero outside of the domain + y_cdf = rng._cdf(x) + assert_array_equal((y_cdf[0], y_cdf[3]), (0, 1)) + assert np.min(y_cdf[1:3]) > 0 + # ppf needs to map 0 and 1 to the boundaries + assert_allclose(rng._ppf(y_cdf), (2.75, 2.76, 3.74, 3.75)) + + +def test_non_rvs_methods_without_domain(): + norm_dist = stats.norm() + rng = FastGeneratorInversion(norm_dist) + x = np.linspace(-3, 3, num=10) + p = (0.01, 0.5, 0.99) + assert_allclose(rng._cdf(x), norm_dist.cdf(x)) + assert_allclose(rng._ppf(p), norm_dist.ppf(p)) + loc, scale = 0.5, 1.3 + rng.loc = loc + rng.scale = scale + norm_dist = stats.norm(loc=loc, scale=scale) + assert_allclose(rng._cdf(x), norm_dist.cdf(x)) + assert_allclose(rng._ppf(p), norm_dist.ppf(p)) + +@pytest.mark.parametrize(("domain, x"), + [(None, 0.5), + ((0, 1), 0.5), + ((0, 1), 1.5)]) +def test_scalar_inputs(domain, x): + """ pdf, cdf etc should map scalar values to scalars. check with and + w/o domain since domain impacts pdf, cdf etc + Take x inside and outside of domain """ + rng = FastGeneratorInversion(stats.norm(), domain=domain) + assert np.isscalar(rng._cdf(x)) + assert np.isscalar(rng._ppf(0.5)) + + +def test_domain_argus_large_chi(): + # for large chi, the Gamma distribution is used and the domain has to be + # transformed. this is a test to ensure that the transformation works + chi, lb, ub = 5.5, 0.25, 0.75 + rng = FastGeneratorInversion(stats.argus(chi), domain=(lb, ub)) + rng.random_state = 4574 + r = rng.rvs(size=500) + assert lb <= r.min() < r.max() <= ub + # perform goodness of fit test with conditional cdf + cdf = stats.argus(chi).cdf + prob = cdf(ub) - cdf(lb) + assert stats.cramervonmises(r, lambda x: cdf(x) / prob).pvalue > 0.05 + + +def test_setting_loc_scale(): + rng = FastGeneratorInversion(stats.norm(), random_state=765765864) + r1 = rng.rvs(size=1000) + rng.loc = 3.0 + rng.scale = 2.5 + r2 = rng.rvs(1000) + # rescaled r2 should be again standard normal + assert stats.cramervonmises_2samp(r1, (r2 - 3) / 2.5).pvalue > 0.05 + # reset values to default loc=0, scale=1 + rng.loc = 0 + rng.scale = 1 + r2 = rng.rvs(1000) + assert stats.cramervonmises_2samp(r1, r2).pvalue > 0.05 + + +def test_ignore_shape_range(): + msg = "No generator is defined for the shape parameters" + with pytest.raises(ValueError, match=msg): + rng = FastGeneratorInversion(stats.t(0.03)) + rng = FastGeneratorInversion(stats.t(0.03), ignore_shape_range=True) + # we can ignore the recommended range of shape parameters + # but u-error can be expected to be too large in that case + u_err, _ = rng.evaluate_error(size=1000, random_state=234) + assert u_err >= 1e-6 + +@pytest.mark.xfail_on_32bit( + "NumericalInversePolynomial.qrvs fails for Win 32-bit" +) +class TestQRVS: + def test_input_validation(self): + gen = FastGeneratorInversion(stats.norm()) + + match = "`qmc_engine` must be an instance of..." + with pytest.raises(ValueError, match=match): + gen.qrvs(qmc_engine=0) + + match = "`d` must be consistent with dimension of `qmc_engine`." + with pytest.raises(ValueError, match=match): + gen.qrvs(d=3, qmc_engine=stats.qmc.Halton(2)) + + qrngs = [None, stats.qmc.Sobol(1, seed=0), stats.qmc.Halton(3, seed=0)] + # `size=None` should not add anything to the shape, `size=1` should + sizes = [ + (None, tuple()), + (1, (1,)), + (4, (4,)), + ((4,), (4,)), + ((2, 4), (2, 4)), + ] + # Neither `d=None` nor `d=1` should add anything to the shape + ds = [(None, tuple()), (1, tuple()), (3, (3,))] + + @pytest.mark.parametrize("qrng", qrngs) + @pytest.mark.parametrize("size_in, size_out", sizes) + @pytest.mark.parametrize("d_in, d_out", ds) + def test_QRVS_shape_consistency(self, qrng, size_in, size_out, + d_in, d_out): + gen = FastGeneratorInversion(stats.norm()) + + # If d and qrng.d are inconsistent, an error is raised + if d_in is not None and qrng is not None and qrng.d != d_in: + match = "`d` must be consistent with dimension of `qmc_engine`." + with pytest.raises(ValueError, match=match): + gen.qrvs(size_in, d=d_in, qmc_engine=qrng) + return + + # Sometimes d is really determined by qrng + if d_in is None and qrng is not None and qrng.d != 1: + d_out = (qrng.d,) + + shape_expected = size_out + d_out + + qrng2 = deepcopy(qrng) + qrvs = gen.qrvs(size=size_in, d=d_in, qmc_engine=qrng) + if size_in is not None: + assert qrvs.shape == shape_expected + + if qrng2 is not None: + uniform = qrng2.random(np.prod(size_in) or 1) + qrvs2 = stats.norm.ppf(uniform).reshape(shape_expected) + assert_allclose(qrvs, qrvs2, atol=1e-12) + + def test_QRVS_size_tuple(self): + # QMCEngine samples are always of shape (n, d). When `size` is a tuple, + # we set `n = prod(size)` in the call to qmc_engine.random, transform + # the sample, and reshape it to the final dimensions. When we reshape, + # we need to be careful, because the _columns_ of the sample returned + # by a QMCEngine are "independent"-ish, but the elements within the + # columns are not. We need to make sure that this doesn't get mixed up + # by reshaping: qrvs[..., i] should remain "independent"-ish of + # qrvs[..., i+1], but the elements within qrvs[..., i] should be + # transformed from the same low-discrepancy sequence. + + gen = FastGeneratorInversion(stats.norm()) + + size = (3, 4) + d = 5 + qrng = stats.qmc.Halton(d, seed=0) + qrng2 = stats.qmc.Halton(d, seed=0) + + uniform = qrng2.random(np.prod(size)) + + qrvs = gen.qrvs(size=size, d=d, qmc_engine=qrng) + qrvs2 = stats.norm.ppf(uniform) + + for i in range(d): + sample = qrvs[..., i] + sample2 = qrvs2[:, i].reshape(size) + assert_allclose(sample, sample2, atol=1e-12) + + +def test_burr_overflow(): + # this case leads to an overflow error if math.exp is used + # in the definition of the burr pdf instead of np.exp + # a direct implementation of the PDF as x**(-c-1) / (1+x**(-c))**(d+1) + # also leads to an overflow error in the setup + args = (1.89128135, 0.30195177) + with suppress_warnings() as sup: + # filter potential overflow warning + sup.filter(RuntimeWarning) + gen = FastGeneratorInversion(stats.burr(*args)) + u_error, _ = gen.evaluate_error(random_state=4326) + assert u_error <= 1e-10 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fit.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fit.py new file mode 100644 index 0000000000000000000000000000000000000000..bcb776f71e35c5e603c3d54a34a22768c0bf637d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_fit.py @@ -0,0 +1,1027 @@ +import os +import numpy as np +import numpy.testing as npt +from numpy.testing import assert_allclose, assert_equal +import pytest +from scipy import stats +from scipy.optimize import differential_evolution + +from .test_continuous_basic import distcont +from scipy.stats._distn_infrastructure import FitError +from scipy.stats._distr_params import distdiscrete +from scipy.stats import goodness_of_fit + + +# this is not a proper statistical test for convergence, but only +# verifies that the estimate and true values don't differ by too much + +fit_sizes = [1000, 5000, 10000] # sample sizes to try + +thresh_percent = 0.25 # percent of true parameters for fail cut-off +thresh_min = 0.75 # minimum difference estimate - true to fail test + +mle_failing_fits = [ + 'gausshyper', + 'genexpon', + 'gengamma', + 'kappa4', + 'ksone', + 'kstwo', + 'ncf', + 'ncx2', + 'truncexpon', + 'tukeylambda', + 'vonmises', + 'levy_stable', + 'trapezoid', + 'truncweibull_min', + 'studentized_range', +] + +# The MLE fit method of these distributions doesn't perform well when all +# parameters are fit, so test them with the location fixed at 0. +mle_use_floc0 = [ + 'burr', + 'chi', + 'chi2', + 'mielke', + 'pearson3', + 'genhalflogistic', + 'rdist', + 'pareto', + 'powerlaw', # distfn.nnlf(est2, rvs) > distfn.nnlf(est1, rvs) otherwise + 'powerlognorm', + 'wrapcauchy', + 'rel_breitwigner', +] + +mm_failing_fits = ['alpha', 'betaprime', 'burr', 'burr12', 'cauchy', 'chi', + 'chi2', 'crystalball', 'dgamma', 'dweibull', 'f', + 'fatiguelife', 'fisk', 'foldcauchy', 'genextreme', + 'gengamma', 'genhyperbolic', 'gennorm', 'genpareto', + 'halfcauchy', 'invgamma', 'invweibull', 'jf_skew_t', + 'johnsonsu', 'kappa3', 'ksone', 'kstwo', 'levy', 'levy_l', + 'levy_stable', 'loglaplace', 'lomax', 'mielke', 'nakagami', + 'ncf', 'nct', 'ncx2', 'pareto', 'powerlognorm', 'powernorm', + 'rel_breitwigner', 'skewcauchy', 't', 'trapezoid', 'triang', + 'truncpareto', 'truncweibull_min', 'tukeylambda', + 'studentized_range'] + +# not sure if these fail, but they caused my patience to fail +mm_slow_fits = ['argus', 'exponpow', 'exponweib', 'gausshyper', 'genexpon', + 'genhalflogistic', 'halfgennorm', 'gompertz', 'johnsonsb', + 'kappa4', 'kstwobign', 'recipinvgauss', + 'truncexpon', 'vonmises', 'vonmises_line'] + +failing_fits = {"MM": mm_failing_fits + mm_slow_fits, "MLE": mle_failing_fits} +fail_interval_censored = {"truncpareto"} + +# Don't run the fit test on these: +skip_fit = [ + 'erlang', # Subclass of gamma, generates a warning. + 'genhyperbolic', # too slow +] + + +def cases_test_cont_fit(): + # this tests the closeness of the estimated parameters to the true + # parameters with fit method of continuous distributions + # Note: is slow, some distributions don't converge with sample + # size <= 10000 + for distname, arg in distcont: + if distname not in skip_fit: + yield distname, arg + + +@pytest.mark.slow +@pytest.mark.parametrize('distname,arg', cases_test_cont_fit()) +@pytest.mark.parametrize('method', ["MLE", "MM"]) +def test_cont_fit(distname, arg, method): + if distname in failing_fits[method]: + # Skip failing fits unless overridden + try: + xfail = not int(os.environ['SCIPY_XFAIL']) + except Exception: + xfail = True + if xfail: + msg = "Fitting %s doesn't work reliably yet" % distname + msg += (" [Set environment variable SCIPY_XFAIL=1 to run this" + " test nevertheless.]") + pytest.xfail(msg) + + distfn = getattr(stats, distname) + + truearg = np.hstack([arg, [0.0, 1.0]]) + diffthreshold = np.max(np.vstack([truearg*thresh_percent, + np.full(distfn.numargs+2, thresh_min)]), + 0) + + for fit_size in fit_sizes: + # Note that if a fit succeeds, the other fit_sizes are skipped + np.random.seed(1234) + + with np.errstate(all='ignore'): + rvs = distfn.rvs(size=fit_size, *arg) + if method == 'MLE' and distfn.name in mle_use_floc0: + kwds = {'floc': 0} + else: + kwds = {} + # start with default values + est = distfn.fit(rvs, method=method, **kwds) + if method == 'MLE': + # Trivial test of the use of CensoredData. The fit() method + # will check that data contains no actual censored data, and + # do a regular uncensored fit. + data1 = stats.CensoredData(rvs) + est1 = distfn.fit(data1, **kwds) + msg = ('Different results fitting uncensored data wrapped as' + f' CensoredData: {distfn.name}: est={est} est1={est1}') + assert_allclose(est1, est, rtol=1e-10, err_msg=msg) + if method == 'MLE' and distname not in fail_interval_censored: + # Convert the first `nic` values in rvs to interval-censored + # values. The interval is small, so est2 should be close to + # est. + nic = 15 + interval = np.column_stack((rvs, rvs)) + interval[:nic, 0] *= 0.99 + interval[:nic, 1] *= 1.01 + interval.sort(axis=1) + data2 = stats.CensoredData(interval=interval) + est2 = distfn.fit(data2, **kwds) + msg = ('Different results fitting interval-censored' + f' data: {distfn.name}: est={est} est2={est2}') + assert_allclose(est2, est, rtol=0.05, err_msg=msg) + + diff = est - truearg + + # threshold for location + diffthreshold[-2] = np.max([np.abs(rvs.mean())*thresh_percent, + thresh_min]) + + if np.any(np.isnan(est)): + raise AssertionError('nan returned in fit') + else: + if np.all(np.abs(diff) <= diffthreshold): + break + else: + txt = 'parameter: %s\n' % str(truearg) + txt += 'estimated: %s\n' % str(est) + txt += 'diff : %s\n' % str(diff) + raise AssertionError('fit not very good in %s\n' % distfn.name + txt) + + +def _check_loc_scale_mle_fit(name, data, desired, atol=None): + d = getattr(stats, name) + actual = d.fit(data)[-2:] + assert_allclose(actual, desired, atol=atol, + err_msg='poor mle fit of (loc, scale) in %s' % name) + + +def test_non_default_loc_scale_mle_fit(): + data = np.array([1.01, 1.78, 1.78, 1.78, 1.88, 1.88, 1.88, 2.00]) + _check_loc_scale_mle_fit('uniform', data, [1.01, 0.99], 1e-3) + _check_loc_scale_mle_fit('expon', data, [1.01, 0.73875], 1e-3) + + +def test_expon_fit(): + """gh-6167""" + data = [0, 0, 0, 0, 2, 2, 2, 2] + phat = stats.expon.fit(data, floc=0) + assert_allclose(phat, [0, 1.0], atol=1e-3) + + +def test_fit_error(): + data = np.concatenate([np.zeros(29), np.ones(21)]) + message = "Optimization converged to parameters that are..." + with pytest.raises(FitError, match=message), \ + pytest.warns(RuntimeWarning): + stats.beta.fit(data) + + +@pytest.mark.parametrize("dist, params", + [(stats.norm, (0.5, 2.5)), # type: ignore[attr-defined] + (stats.binom, (10, 0.3, 2))]) # type: ignore[attr-defined] +def test_nnlf_and_related_methods(dist, params): + rng = np.random.default_rng(983459824) + + if hasattr(dist, 'pdf'): + logpxf = dist.logpdf + else: + logpxf = dist.logpmf + + x = dist.rvs(*params, size=100, random_state=rng) + ref = -logpxf(x, *params).sum() + res1 = dist.nnlf(params, x) + res2 = dist._penalized_nnlf(params, x) + assert_allclose(res1, ref) + assert_allclose(res2, ref) + + +def cases_test_fit_mle(): + # These fail default test or hang + skip_basic_fit = {'argus', 'foldnorm', 'truncpareto', 'truncweibull_min', + 'ksone', 'levy_stable', 'studentized_range', 'kstwo', + 'arcsine'} + + # Please keep this list in alphabetical order... + slow_basic_fit = {'alpha', + 'betaprime', 'binom', 'bradford', 'burr12', + 'chi', 'crystalball', 'dweibull', 'exponpow', + 'f', 'fatiguelife', 'fisk', 'foldcauchy', + 'genexpon', 'genextreme', 'gennorm', 'genpareto', + 'gompertz', 'halfgennorm', 'invgauss', 'invweibull', + 'jf_skew_t', 'johnsonsb', 'johnsonsu', 'kappa3', + 'kstwobign', 'loglaplace', 'lognorm', 'lomax', 'mielke', + 'nakagami', 'nbinom', 'norminvgauss', + 'pareto', 'pearson3', 'powerlaw', 'powernorm', + 'randint', 'rdist', 'recipinvgauss', 'rice', + 't', 'uniform', 'weibull_max', 'wrapcauchy'} + + # Please keep this list in alphabetical order... + xslow_basic_fit = {'beta', 'betabinom', 'burr', 'exponweib', + 'gausshyper', 'gengamma', 'genhalflogistic', + 'genhyperbolic', 'geninvgauss', + 'hypergeom', 'kappa4', 'loguniform', + 'ncf', 'nchypergeom_fisher', 'nchypergeom_wallenius', + 'nct', 'ncx2', 'nhypergeom', + 'powerlognorm', 'reciprocal', 'rel_breitwigner', + 'skellam', 'trapezoid', 'triang', 'truncnorm', + 'tukeylambda', 'zipfian'} + + for dist in dict(distdiscrete + distcont): + if dist in skip_basic_fit or not isinstance(dist, str): + reason = "tested separately" + yield pytest.param(dist, marks=pytest.mark.skip(reason=reason)) + elif dist in slow_basic_fit: + reason = "too slow (>= 0.25s)" + yield pytest.param(dist, marks=pytest.mark.slow(reason=reason)) + elif dist in xslow_basic_fit: + reason = "too slow (>= 1.0s)" + yield pytest.param(dist, marks=pytest.mark.xslow(reason=reason)) + else: + yield dist + + +def cases_test_fit_mse(): + # the first four are so slow that I'm not sure whether they would pass + skip_basic_fit = {'levy_stable', 'studentized_range', 'ksone', 'skewnorm', + 'norminvgauss', # super slow (~1 hr) but passes + 'kstwo', # very slow (~25 min) but passes + 'geninvgauss', # quite slow (~4 minutes) but passes + 'gausshyper', 'genhyperbolic', # integration warnings + 'tukeylambda', # close, but doesn't meet tolerance + 'vonmises'} # can have negative CDF; doesn't play nice + + # Please keep this list in alphabetical order... + slow_basic_fit = {'alpha', 'anglit', 'arcsine', 'betabinom', 'bradford', + 'chi', 'chi2', 'crystalball', 'dgamma', 'dweibull', + 'erlang', 'exponnorm', 'exponpow', 'exponweib', + 'fatiguelife', 'fisk', 'foldcauchy', 'foldnorm', + 'gamma', 'genexpon', 'genextreme', 'genhalflogistic', + 'genlogistic', 'genpareto', 'gompertz', + 'hypergeom', 'invweibull', 'jf_skew_t', 'johnsonsb', + 'johnsonsu', 'kappa3', 'kstwobign', + 'laplace_asymmetric', 'loggamma', 'loglaplace', + 'lognorm', 'lomax', + 'maxwell', 'mielke', 'nakagami', 'nhypergeom', + 'pareto', 'powernorm', 'randint', 'recipinvgauss', + 'semicircular', + 't', 'triang', 'truncexpon', 'truncpareto', + 'truncweibull_min', + 'uniform', 'vonmises_line', + 'wald', 'weibull_max', 'weibull_min', 'wrapcauchy'} + + # Please keep this list in alphabetical order... + xslow_basic_fit = {'beta', 'betaprime', 'burr', 'burr12', + 'f', 'gengamma', 'gennorm', + 'halfgennorm', 'invgamma', 'invgauss', + 'kappa4', 'loguniform', + 'ncf', 'nchypergeom_fisher', 'nchypergeom_wallenius', + 'nct', 'ncx2', + 'pearson3', 'powerlaw', 'powerlognorm', + 'rdist', 'reciprocal', 'rel_breitwigner', 'rice', + 'trapezoid', 'truncnorm', + 'zipfian'} + + warns_basic_fit = {'skellam'} # can remove mark after gh-14901 is resolved + + for dist in dict(distdiscrete + distcont): + if dist in skip_basic_fit or not isinstance(dist, str): + reason = "Fails. Oh well." + yield pytest.param(dist, marks=pytest.mark.skip(reason=reason)) + elif dist in slow_basic_fit: + reason = "too slow (>= 0.25s)" + yield pytest.param(dist, marks=pytest.mark.slow(reason=reason)) + elif dist in xslow_basic_fit: + reason = "too slow (>= 1.0s)" + yield pytest.param(dist, marks=pytest.mark.xslow(reason=reason)) + elif dist in warns_basic_fit: + mark = pytest.mark.filterwarnings('ignore::RuntimeWarning') + yield pytest.param(dist, marks=mark) + else: + yield dist + + +def cases_test_fitstart(): + for distname, shapes in dict(distcont).items(): + if (not isinstance(distname, str) or + distname in {'studentized_range', 'recipinvgauss'}): # slow + continue + yield distname, shapes + + +@pytest.mark.parametrize('distname, shapes', cases_test_fitstart()) +def test_fitstart(distname, shapes): + dist = getattr(stats, distname) + rng = np.random.default_rng(216342614) + data = rng.random(10) + + with np.errstate(invalid='ignore', divide='ignore'): # irrelevant to test + guess = dist._fitstart(data) + + assert dist._argcheck(*guess[:-2]) + + +def assert_nlff_less_or_close(dist, data, params1, params0, rtol=1e-7, atol=0, + nlff_name='nnlf'): + nlff = getattr(dist, nlff_name) + nlff1 = nlff(params1, data) + nlff0 = nlff(params0, data) + if not (nlff1 < nlff0): + np.testing.assert_allclose(nlff1, nlff0, rtol=rtol, atol=atol) + + +class TestFit: + dist = stats.binom # type: ignore[attr-defined] + seed = 654634816187 + rng = np.random.default_rng(seed) + data = stats.binom.rvs(5, 0.5, size=100, random_state=rng) # type: ignore[attr-defined] # noqa: E501 + shape_bounds_a = [(1, 10), (0, 1)] + shape_bounds_d = {'n': (1, 10), 'p': (0, 1)} + atol = 5e-2 + rtol = 1e-2 + tols = {'atol': atol, 'rtol': rtol} + + def opt(self, *args, **kwds): + return differential_evolution(*args, seed=0, **kwds) + + def test_dist_iv(self): + message = "`dist` must be an instance of..." + with pytest.raises(ValueError, match=message): + stats.fit(10, self.data, self.shape_bounds_a) + + def test_data_iv(self): + message = "`data` must be exactly one-dimensional." + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, [[1, 2, 3]], self.shape_bounds_a) + + message = "All elements of `data` must be finite numbers." + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, [1, 2, 3, np.nan], self.shape_bounds_a) + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, [1, 2, 3, np.inf], self.shape_bounds_a) + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, ['1', '2', '3'], self.shape_bounds_a) + + def test_bounds_iv(self): + message = "Bounds provided for the following unrecognized..." + shape_bounds = {'n': (1, 10), 'p': (0, 1), '1': (0, 10)} + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "Each element of a `bounds` sequence must be a tuple..." + shape_bounds = [(1, 10, 3), (0, 1)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "Each element of `bounds` must be a tuple specifying..." + shape_bounds = [(1, 10, 3), (0, 1, 0.5)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + shape_bounds = [1, 0] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "A `bounds` sequence must contain at least 2 elements..." + shape_bounds = [(1, 10)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "A `bounds` sequence may not contain more than 3 elements..." + bounds = [(1, 10), (1, 10), (1, 10), (1, 10)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, bounds) + + message = "There are no values for `p` on the interval..." + shape_bounds = {'n': (1, 10), 'p': (1, 0)} + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "There are no values for `n` on the interval..." + shape_bounds = [(10, 1), (0, 1)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "There are no integer values for `n` on the interval..." + shape_bounds = [(1.4, 1.6), (0, 1)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + message = "The intersection of user-provided bounds for `n`" + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data) + shape_bounds = [(-np.inf, np.inf), (0, 1)] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, shape_bounds) + + def test_guess_iv(self): + message = "Guesses provided for the following unrecognized..." + guess = {'n': 1, 'p': 0.5, '1': 255} + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "Each element of `guess` must be a scalar..." + guess = {'n': 1, 'p': 'hi'} + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + guess = [1, 'f'] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + guess = [[1, 2]] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "A `guess` sequence must contain at least 2..." + guess = [1] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "A `guess` sequence may not contain more than 3..." + guess = [1, 2, 3, 4] + with pytest.raises(ValueError, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "Guess for parameter `n` rounded.*|Guess for parameter `p` clipped.*" + guess = {'n': 4.5, 'p': -0.5} + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "Guess for parameter `loc` rounded..." + guess = [5, 0.5, 0.5] + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "Guess for parameter `p` clipped..." + guess = {'n': 5, 'p': -0.5} + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + message = "Guess for parameter `loc` clipped..." + guess = [5, 0.5, 1] + with pytest.warns(RuntimeWarning, match=message): + stats.fit(self.dist, self.data, self.shape_bounds_d, guess=guess) + + def basic_fit_test(self, dist_name, method): + + N = 5000 + dist_data = dict(distcont + distdiscrete) + rng = np.random.default_rng(self.seed) + dist = getattr(stats, dist_name) + shapes = np.array(dist_data[dist_name]) + bounds = np.empty((len(shapes) + 2, 2), dtype=np.float64) + bounds[:-2, 0] = shapes/10.**np.sign(shapes) + bounds[:-2, 1] = shapes*10.**np.sign(shapes) + bounds[-2] = (0, 10) + bounds[-1] = (1e-16, 10) + loc = rng.uniform(*bounds[-2]) + scale = rng.uniform(*bounds[-1]) + ref = list(dist_data[dist_name]) + [loc, scale] + + if getattr(dist, 'pmf', False): + ref = ref[:-1] + ref[-1] = np.floor(loc) + data = dist.rvs(*ref, size=N, random_state=rng) + bounds = bounds[:-1] + if getattr(dist, 'pdf', False): + data = dist.rvs(*ref, size=N, random_state=rng) + + with npt.suppress_warnings() as sup: + sup.filter(RuntimeWarning, "overflow encountered") + res = stats.fit(dist, data, bounds, method=method, + optimizer=self.opt) + + nlff_names = {'mle': 'nnlf', 'mse': '_penalized_nlpsf'} + nlff_name = nlff_names[method] + assert_nlff_less_or_close(dist, data, res.params, ref, **self.tols, + nlff_name=nlff_name) + + @pytest.mark.parametrize("dist_name", cases_test_fit_mle()) + def test_basic_fit_mle(self, dist_name): + self.basic_fit_test(dist_name, "mle") + + @pytest.mark.parametrize("dist_name", cases_test_fit_mse()) + def test_basic_fit_mse(self, dist_name): + self.basic_fit_test(dist_name, "mse") + + def test_arcsine(self): + # Can't guarantee that all distributions will fit all data with + # arbitrary bounds. This distribution just happens to fail above. + # Try something slightly different. + N = 1000 + rng = np.random.default_rng(self.seed) + dist = stats.arcsine + shapes = (1., 2.) + data = dist.rvs(*shapes, size=N, random_state=rng) + shape_bounds = {'loc': (0.1, 10), 'scale': (0.1, 10)} + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + assert_nlff_less_or_close(dist, data, res.params, shapes, **self.tols) + + def test_argus(self): + # Can't guarantee that all distributions will fit all data with + # arbitrary bounds. This distribution just happens to fail above. + # Try something slightly different. + N = 1000 + rng = np.random.default_rng(self.seed) + dist = stats.argus + shapes = (1., 2., 3.) + data = dist.rvs(*shapes, size=N, random_state=rng) + shape_bounds = {'chi': (0.1, 10), 'loc': (0.1, 10), 'scale': (0.1, 10)} + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + + assert_nlff_less_or_close(dist, data, res.params, shapes, **self.tols) + + def test_foldnorm(self): + # Can't guarantee that all distributions will fit all data with + # arbitrary bounds. This distribution just happens to fail above. + # Try something slightly different. + N = 1000 + rng = np.random.default_rng(self.seed) + dist = stats.foldnorm + shapes = (1.952125337355587, 2., 3.) + data = dist.rvs(*shapes, size=N, random_state=rng) + shape_bounds = {'c': (0.1, 10), 'loc': (0.1, 10), 'scale': (0.1, 10)} + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + + assert_nlff_less_or_close(dist, data, res.params, shapes, **self.tols) + + def test_truncpareto(self): + # Can't guarantee that all distributions will fit all data with + # arbitrary bounds. This distribution just happens to fail above. + # Try something slightly different. + N = 1000 + rng = np.random.default_rng(self.seed) + dist = stats.truncpareto + shapes = (1.8, 5.3, 2.3, 4.1) + data = dist.rvs(*shapes, size=N, random_state=rng) + shape_bounds = [(0.1, 10)]*4 + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + + assert_nlff_less_or_close(dist, data, res.params, shapes, **self.tols) + + def test_truncweibull_min(self): + # Can't guarantee that all distributions will fit all data with + # arbitrary bounds. This distribution just happens to fail above. + # Try something slightly different. + N = 1000 + rng = np.random.default_rng(self.seed) + dist = stats.truncweibull_min + shapes = (2.5, 0.25, 1.75, 2., 3.) + data = dist.rvs(*shapes, size=N, random_state=rng) + shape_bounds = [(0.1, 10)]*5 + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + + assert_nlff_less_or_close(dist, data, res.params, shapes, **self.tols) + + def test_missing_shape_bounds(self): + # some distributions have a small domain w.r.t. a parameter, e.g. + # $p \in [0, 1]$ for binomial distribution + # User does not need to provide these because the intersection of the + # user's bounds (none) and the distribution's domain is finite + N = 1000 + rng = np.random.default_rng(self.seed) + + dist = stats.binom + n, p, loc = 10, 0.65, 0 + data = dist.rvs(n, p, loc=loc, size=N, random_state=rng) + shape_bounds = {'n': np.array([0, 20])} # check arrays are OK, too + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + assert_allclose(res.params, (n, p, loc), **self.tols) + + dist = stats.bernoulli + p, loc = 0.314159, 0 + data = dist.rvs(p, loc=loc, size=N, random_state=rng) + res = stats.fit(dist, data, optimizer=self.opt) + assert_allclose(res.params, (p, loc), **self.tols) + + def test_fit_only_loc_scale(self): + # fit only loc + N = 5000 + rng = np.random.default_rng(self.seed) + + dist = stats.norm + loc, scale = 1.5, 1 + data = dist.rvs(loc=loc, size=N, random_state=rng) + loc_bounds = (0, 5) + bounds = {'loc': loc_bounds} + res = stats.fit(dist, data, bounds, optimizer=self.opt) + assert_allclose(res.params, (loc, scale), **self.tols) + + # fit only scale + loc, scale = 0, 2.5 + data = dist.rvs(scale=scale, size=N, random_state=rng) + scale_bounds = (0.01, 5) + bounds = {'scale': scale_bounds} + res = stats.fit(dist, data, bounds, optimizer=self.opt) + assert_allclose(res.params, (loc, scale), **self.tols) + + # fit only loc and scale + dist = stats.norm + loc, scale = 1.5, 2.5 + data = dist.rvs(loc=loc, scale=scale, size=N, random_state=rng) + bounds = {'loc': loc_bounds, 'scale': scale_bounds} + res = stats.fit(dist, data, bounds, optimizer=self.opt) + assert_allclose(res.params, (loc, scale), **self.tols) + + def test_everything_fixed(self): + N = 5000 + rng = np.random.default_rng(self.seed) + + dist = stats.norm + loc, scale = 1.5, 2.5 + data = dist.rvs(loc=loc, scale=scale, size=N, random_state=rng) + + # loc, scale fixed to 0, 1 by default + res = stats.fit(dist, data) + assert_allclose(res.params, (0, 1), **self.tols) + + # loc, scale explicitly fixed + bounds = {'loc': (loc, loc), 'scale': (scale, scale)} + res = stats.fit(dist, data, bounds) + assert_allclose(res.params, (loc, scale), **self.tols) + + # `n` gets fixed during polishing + dist = stats.binom + n, p, loc = 10, 0.65, 0 + data = dist.rvs(n, p, loc=loc, size=N, random_state=rng) + shape_bounds = {'n': (0, 20), 'p': (0.65, 0.65)} + res = stats.fit(dist, data, shape_bounds, optimizer=self.opt) + assert_allclose(res.params, (n, p, loc), **self.tols) + + def test_failure(self): + N = 5000 + rng = np.random.default_rng(self.seed) + + dist = stats.nbinom + shapes = (5, 0.5) + data = dist.rvs(*shapes, size=N, random_state=rng) + + assert data.min() == 0 + # With lower bounds on location at 0.5, likelihood is zero + bounds = [(0, 30), (0, 1), (0.5, 10)] + res = stats.fit(dist, data, bounds) + message = "Optimization converged to parameter values that are" + assert res.message.startswith(message) + assert res.success is False + + @pytest.mark.xslow + def test_guess(self): + # Test that guess helps DE find the desired solution + N = 2000 + # With some seeds, `fit` doesn't need a guess + rng = np.random.default_rng(1963904448561) + dist = stats.nhypergeom + params = (20, 7, 12, 0) + bounds = [(2, 200), (0.7, 70), (1.2, 120), (0, 10)] + + data = dist.rvs(*params, size=N, random_state=rng) + + res = stats.fit(dist, data, bounds, optimizer=self.opt) + assert not np.allclose(res.params, params, **self.tols) + + res = stats.fit(dist, data, bounds, guess=params, optimizer=self.opt) + assert_allclose(res.params, params, **self.tols) + + def test_mse_accuracy_1(self): + # Test maximum spacing estimation against example from Wikipedia + # https://en.wikipedia.org/wiki/Maximum_spacing_estimation#Examples + data = [2, 4] + dist = stats.expon + bounds = {'loc': (0, 0), 'scale': (1e-8, 10)} + res_mle = stats.fit(dist, data, bounds=bounds, method='mle') + assert_allclose(res_mle.params.scale, 3, atol=1e-3) + res_mse = stats.fit(dist, data, bounds=bounds, method='mse') + assert_allclose(res_mse.params.scale, 3.915, atol=1e-3) + + def test_mse_accuracy_2(self): + # Test maximum spacing estimation against example from Wikipedia + # https://en.wikipedia.org/wiki/Maximum_spacing_estimation#Examples + rng = np.random.default_rng(9843212616816518964) + + dist = stats.uniform + n = 10 + data = dist(3, 6).rvs(size=n, random_state=rng) + bounds = {'loc': (0, 10), 'scale': (1e-8, 10)} + res = stats.fit(dist, data, bounds=bounds, method='mse') + # (loc=3.608118420015416, scale=5.509323262055043) + + x = np.sort(data) + a = (n*x[0] - x[-1])/(n - 1) + b = (n*x[-1] - x[0])/(n - 1) + ref = a, b-a # (3.6081133632151503, 5.509328130317254) + assert_allclose(res.params, ref, rtol=1e-4) + + +# Data from Matlab: https://www.mathworks.com/help/stats/lillietest.html +examgrades = [65, 61, 81, 88, 69, 89, 55, 84, 86, 84, 71, 81, 84, 81, 78, 67, + 96, 66, 73, 75, 59, 71, 69, 63, 79, 76, 63, 85, 87, 88, 80, 71, + 65, 84, 71, 75, 81, 79, 64, 65, 84, 77, 70, 75, 84, 75, 73, 92, + 90, 79, 80, 71, 73, 71, 58, 79, 73, 64, 77, 82, 81, 59, 54, 82, + 57, 79, 79, 73, 74, 82, 63, 64, 73, 69, 87, 68, 81, 73, 83, 73, + 80, 73, 73, 71, 66, 78, 64, 74, 68, 67, 75, 75, 80, 85, 74, 76, + 80, 77, 93, 70, 86, 80, 81, 83, 68, 60, 85, 64, 74, 82, 81, 77, + 66, 85, 75, 81, 69, 60, 83, 72] + + +class TestGoodnessOfFit: + + def test_gof_iv(self): + dist = stats.norm + x = [1, 2, 3] + + message = r"`dist` must be a \(non-frozen\) instance of..." + with pytest.raises(TypeError, match=message): + goodness_of_fit(stats.norm(), x) + + message = "`data` must be a one-dimensional array of numbers." + with pytest.raises(ValueError, match=message): + goodness_of_fit(dist, [[1, 2, 3]]) + + message = "`statistic` must be one of..." + with pytest.raises(ValueError, match=message): + goodness_of_fit(dist, x, statistic='mm') + + message = "`n_mc_samples` must be an integer." + with pytest.raises(TypeError, match=message): + goodness_of_fit(dist, x, n_mc_samples=1000.5) + + message = "'herring' cannot be used to seed a" + with pytest.raises(ValueError, match=message): + goodness_of_fit(dist, x, random_state='herring') + + def test_against_ks(self): + rng = np.random.default_rng(8517426291317196949) + x = examgrades + known_params = {'loc': np.mean(x), 'scale': np.std(x, ddof=1)} + res = goodness_of_fit(stats.norm, x, known_params=known_params, + statistic='ks', random_state=rng) + ref = stats.kstest(x, stats.norm(**known_params).cdf, method='exact') + assert_allclose(res.statistic, ref.statistic) # ~0.0848 + assert_allclose(res.pvalue, ref.pvalue, atol=5e-3) # ~0.335 + + def test_against_lilliefors(self): + rng = np.random.default_rng(2291803665717442724) + x = examgrades + res = goodness_of_fit(stats.norm, x, statistic='ks', random_state=rng) + known_params = {'loc': np.mean(x), 'scale': np.std(x, ddof=1)} + ref = stats.kstest(x, stats.norm(**known_params).cdf, method='exact') + assert_allclose(res.statistic, ref.statistic) # ~0.0848 + assert_allclose(res.pvalue, 0.0348, atol=5e-3) + + def test_against_cvm(self): + rng = np.random.default_rng(8674330857509546614) + x = examgrades + known_params = {'loc': np.mean(x), 'scale': np.std(x, ddof=1)} + res = goodness_of_fit(stats.norm, x, known_params=known_params, + statistic='cvm', random_state=rng) + ref = stats.cramervonmises(x, stats.norm(**known_params).cdf) + assert_allclose(res.statistic, ref.statistic) # ~0.090 + assert_allclose(res.pvalue, ref.pvalue, atol=5e-3) # ~0.636 + + def test_against_anderson_case_0(self): + # "Case 0" is where loc and scale are known [1] + rng = np.random.default_rng(7384539336846690410) + x = np.arange(1, 101) + # loc that produced critical value of statistic found w/ root_scalar + known_params = {'loc': 45.01575354024957, 'scale': 30} + res = goodness_of_fit(stats.norm, x, known_params=known_params, + statistic='ad', random_state=rng) + assert_allclose(res.statistic, 2.492) # See [1] Table 1A 1.0 + assert_allclose(res.pvalue, 0.05, atol=5e-3) + + def test_against_anderson_case_1(self): + # "Case 1" is where scale is known and loc is fit [1] + rng = np.random.default_rng(5040212485680146248) + x = np.arange(1, 101) + # scale that produced critical value of statistic found w/ root_scalar + known_params = {'scale': 29.957112639101933} + res = goodness_of_fit(stats.norm, x, known_params=known_params, + statistic='ad', random_state=rng) + assert_allclose(res.statistic, 0.908) # See [1] Table 1B 1.1 + assert_allclose(res.pvalue, 0.1, atol=5e-3) + + def test_against_anderson_case_2(self): + # "Case 2" is where loc is known and scale is fit [1] + rng = np.random.default_rng(726693985720914083) + x = np.arange(1, 101) + # loc that produced critical value of statistic found w/ root_scalar + known_params = {'loc': 44.5680212261933} + res = goodness_of_fit(stats.norm, x, known_params=known_params, + statistic='ad', random_state=rng) + assert_allclose(res.statistic, 2.904) # See [1] Table 1B 1.2 + assert_allclose(res.pvalue, 0.025, atol=5e-3) + + def test_against_anderson_case_3(self): + # "Case 3" is where both loc and scale are fit [1] + rng = np.random.default_rng(6763691329830218206) + # c that produced critical value of statistic found w/ root_scalar + x = stats.skewnorm.rvs(1.4477847789132101, loc=1, scale=2, size=100, + random_state=rng) + res = goodness_of_fit(stats.norm, x, statistic='ad', random_state=rng) + assert_allclose(res.statistic, 0.559) # See [1] Table 1B 1.2 + assert_allclose(res.pvalue, 0.15, atol=5e-3) + + @pytest.mark.slow + def test_against_anderson_gumbel_r(self): + rng = np.random.default_rng(7302761058217743) + # c that produced critical value of statistic found w/ root_scalar + x = stats.genextreme(0.051896837188595134, loc=0.5, + scale=1.5).rvs(size=1000, random_state=rng) + res = goodness_of_fit(stats.gumbel_r, x, statistic='ad', + random_state=rng) + ref = stats.anderson(x, dist='gumbel_r') + assert_allclose(res.statistic, ref.critical_values[0]) + assert_allclose(res.pvalue, ref.significance_level[0]/100, atol=5e-3) + + def test_against_filliben_norm(self): + # Test against `stats.fit` ref. [7] Section 8 "Example" + rng = np.random.default_rng(8024266430745011915) + y = [6, 1, -4, 8, -2, 5, 0] + known_params = {'loc': 0, 'scale': 1} + res = stats.goodness_of_fit(stats.norm, y, known_params=known_params, + statistic="filliben", random_state=rng) + # Slight discrepancy presumably due to roundoff in Filliben's + # calculation. Using exact order statistic medians instead of + # Filliben's approximation doesn't account for it. + assert_allclose(res.statistic, 0.98538, atol=1e-4) + assert 0.75 < res.pvalue < 0.9 + + # Using R's ppcc library: + # library(ppcc) + # options(digits=16) + # x < - c(6, 1, -4, 8, -2, 5, 0) + # set.seed(100) + # ppccTest(x, "qnorm", ppos="Filliben") + # Discrepancy with + assert_allclose(res.statistic, 0.98540957187084, rtol=2e-5) + assert_allclose(res.pvalue, 0.8875, rtol=2e-3) + + def test_filliben_property(self): + # Filliben's statistic should be independent of data location and scale + rng = np.random.default_rng(8535677809395478813) + x = rng.normal(loc=10, scale=0.5, size=100) + res = stats.goodness_of_fit(stats.norm, x, + statistic="filliben", random_state=rng) + known_params = {'loc': 0, 'scale': 1} + ref = stats.goodness_of_fit(stats.norm, x, known_params=known_params, + statistic="filliben", random_state=rng) + assert_allclose(res.statistic, ref.statistic, rtol=1e-15) + + @pytest.mark.parametrize('case', [(25, [.928, .937, .950, .958, .966]), + (50, [.959, .965, .972, .977, .981]), + (95, [.977, .979, .983, .986, .989])]) + def test_against_filliben_norm_table(self, case): + # Test against `stats.fit` ref. [7] Table 1 + rng = np.random.default_rng(504569995557928957) + n, ref = case + x = rng.random(n) + known_params = {'loc': 0, 'scale': 1} + res = stats.goodness_of_fit(stats.norm, x, known_params=known_params, + statistic="filliben", random_state=rng) + percentiles = np.array([0.005, 0.01, 0.025, 0.05, 0.1]) + res = stats.scoreatpercentile(res.null_distribution, percentiles*100) + assert_allclose(res, ref, atol=2e-3) + + @pytest.mark.slow + @pytest.mark.parametrize('case', [(5, 0.95772790260469, 0.4755), + (6, 0.95398832257958, 0.3848), + (7, 0.9432692889277, 0.2328)]) + def test_against_ppcc(self, case): + # Test against R ppcc, e.g. + # library(ppcc) + # options(digits=16) + # x < - c(0.52325412, 1.06907699, -0.36084066, 0.15305959, 0.99093194) + # set.seed(100) + # ppccTest(x, "qrayleigh", ppos="Filliben") + n, ref_statistic, ref_pvalue = case + rng = np.random.default_rng(7777775561439803116) + x = rng.normal(size=n) + res = stats.goodness_of_fit(stats.rayleigh, x, statistic="filliben", + random_state=rng) + assert_allclose(res.statistic, ref_statistic, rtol=1e-4) + assert_allclose(res.pvalue, ref_pvalue, atol=1.5e-2) + + def test_params_effects(self): + # Ensure that `guessed_params`, `fit_params`, and `known_params` have + # the intended effects. + rng = np.random.default_rng(9121950977643805391) + x = stats.skewnorm.rvs(-5.044559778383153, loc=1, scale=2, size=50, + random_state=rng) + + # Show that `guessed_params` don't fit to the guess, + # but `fit_params` and `known_params` respect the provided fit + guessed_params = {'c': 13.4} + fit_params = {'scale': 13.73} + known_params = {'loc': -13.85} + rng = np.random.default_rng(9121950977643805391) + res1 = goodness_of_fit(stats.weibull_min, x, n_mc_samples=2, + guessed_params=guessed_params, + fit_params=fit_params, + known_params=known_params, random_state=rng) + assert not np.allclose(res1.fit_result.params.c, 13.4) + assert_equal(res1.fit_result.params.scale, 13.73) + assert_equal(res1.fit_result.params.loc, -13.85) + + # Show that changing the guess changes the parameter that gets fit, + # and it changes the null distribution + guessed_params = {'c': 2} + rng = np.random.default_rng(9121950977643805391) + res2 = goodness_of_fit(stats.weibull_min, x, n_mc_samples=2, + guessed_params=guessed_params, + fit_params=fit_params, + known_params=known_params, random_state=rng) + assert not np.allclose(res2.fit_result.params.c, + res1.fit_result.params.c, rtol=1e-8) + assert not np.allclose(res2.null_distribution, + res1.null_distribution, rtol=1e-8) + assert_equal(res2.fit_result.params.scale, 13.73) + assert_equal(res2.fit_result.params.loc, -13.85) + + # If we set all parameters as fit_params and known_params, + # they're all fixed to those values, but the null distribution + # varies. + fit_params = {'c': 13.4, 'scale': 13.73} + rng = np.random.default_rng(9121950977643805391) + res3 = goodness_of_fit(stats.weibull_min, x, n_mc_samples=2, + guessed_params=guessed_params, + fit_params=fit_params, + known_params=known_params, random_state=rng) + assert_equal(res3.fit_result.params.c, 13.4) + assert_equal(res3.fit_result.params.scale, 13.73) + assert_equal(res3.fit_result.params.loc, -13.85) + assert not np.allclose(res3.null_distribution, res1.null_distribution) + + def test_custom_statistic(self): + # Test support for custom statistic function. + + # References: + # [1] Pyke, R. (1965). "Spacings". Journal of the Royal Statistical + # Society: Series B (Methodological), 27(3): 395-436. + # [2] Burrows, P. M. (1979). "Selected Percentage Points of + # Greenwood's Statistics". Journal of the Royal Statistical + # Society. Series A (General), 142(2): 256-258. + + # Use the Greenwood statistic for illustration; see [1, p.402]. + def greenwood(dist, data, *, axis): + x = np.sort(data, axis=axis) + y = dist.cdf(x) + d = np.diff(y, axis=axis, prepend=0, append=1) + return np.sum(d ** 2, axis=axis) + + # Run the Monte Carlo test with sample size = 5 on a fully specified + # null distribution, and compare the simulated quantiles to the exact + # ones given in [2, Table 1, column (n = 5)]. + rng = np.random.default_rng(9121950977643805391) + data = stats.expon.rvs(size=5, random_state=rng) + result = goodness_of_fit(stats.expon, data, + known_params={'loc': 0, 'scale': 1}, + statistic=greenwood, random_state=rng) + p = [.01, .05, .1, .2, .3, .4, .5, .6, .7, .8, .9, .95, .99] + exact_quantiles = [ + .183863, .199403, .210088, .226040, .239947, .253677, .268422, + .285293, .306002, .334447, .382972, .432049, .547468] + simulated_quantiles = np.quantile(result.null_distribution, p) + assert_allclose(simulated_quantiles, exact_quantiles, atol=0.005) + +class TestFitResult: + def test_plot_iv(self): + rng = np.random.default_rng(1769658657308472721) + data = stats.norm.rvs(0, 1, size=100, random_state=rng) + + def optimizer(*args, **kwargs): + return differential_evolution(*args, **kwargs, seed=rng) + + bounds = [(0, 30), (0, 1)] + res = stats.fit(stats.norm, data, bounds, optimizer=optimizer) + try: + import matplotlib # noqa: F401 + message = r"`plot_type` must be one of \{'..." + with pytest.raises(ValueError, match=message): + res.plot(plot_type='llama') + except (ModuleNotFoundError, ImportError): + # Avoid trying to call MPL with numpy 2.0-dev, because that fails + # too often due to ABI mismatches and is hard to avoid. This test + # will work fine again once MPL has done a 2.0-compatible release. + if not np.__version__.startswith('2.0.0.dev0'): + message = r"matplotlib must be installed to use method `plot`." + with pytest.raises(ModuleNotFoundError, match=message): + res.plot(plot_type='llama') diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_hypotests.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_hypotests.py new file mode 100644 index 0000000000000000000000000000000000000000..d9204f0a360d24dc8d4dc776f1ea11ab12788e1d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_hypotests.py @@ -0,0 +1,1879 @@ +from itertools import product + +import numpy as np +import random +import functools +import pytest +from numpy.testing import (assert_, assert_equal, assert_allclose, + assert_almost_equal) # avoid new uses +from pytest import raises as assert_raises + +import scipy.stats as stats +from scipy.stats import distributions +from scipy.stats._hypotests import (epps_singleton_2samp, cramervonmises, + _cdf_cvm, cramervonmises_2samp, + _pval_cvm_2samp_exact, barnard_exact, + boschloo_exact) +from scipy.stats._mannwhitneyu import mannwhitneyu, _mwu_state +from .common_tests import check_named_results +from scipy._lib._testutils import _TestPythranFunc + + +class TestEppsSingleton: + def test_statistic_1(self): + # first example in Goerg & Kaiser, also in original paper of + # Epps & Singleton. Note: values do not match exactly, the + # value of the interquartile range varies depending on how + # quantiles are computed + x = np.array([-0.35, 2.55, 1.73, 0.73, 0.35, + 2.69, 0.46, -0.94, -0.37, 12.07]) + y = np.array([-1.15, -0.15, 2.48, 3.25, 3.71, + 4.29, 5.00, 7.74, 8.38, 8.60]) + w, p = epps_singleton_2samp(x, y) + assert_almost_equal(w, 15.14, decimal=1) + assert_almost_equal(p, 0.00442, decimal=3) + + def test_statistic_2(self): + # second example in Goerg & Kaiser, again not a perfect match + x = np.array((0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 5, 5, 5, 5, 6, 10, + 10, 10, 10)) + y = np.array((10, 4, 0, 5, 10, 10, 0, 5, 6, 7, 10, 3, 1, 7, 0, 8, 1, + 5, 8, 10)) + w, p = epps_singleton_2samp(x, y) + assert_allclose(w, 8.900, atol=0.001) + assert_almost_equal(p, 0.06364, decimal=3) + + def test_epps_singleton_array_like(self): + np.random.seed(1234) + x, y = np.arange(30), np.arange(28) + + w1, p1 = epps_singleton_2samp(list(x), list(y)) + w2, p2 = epps_singleton_2samp(tuple(x), tuple(y)) + w3, p3 = epps_singleton_2samp(x, y) + + assert_(w1 == w2 == w3) + assert_(p1 == p2 == p3) + + def test_epps_singleton_size(self): + # raise error if less than 5 elements + x, y = (1, 2, 3, 4), np.arange(10) + assert_raises(ValueError, epps_singleton_2samp, x, y) + + def test_epps_singleton_nonfinite(self): + # raise error if there are non-finite values + x, y = (1, 2, 3, 4, 5, np.inf), np.arange(10) + assert_raises(ValueError, epps_singleton_2samp, x, y) + + def test_names(self): + x, y = np.arange(20), np.arange(30) + res = epps_singleton_2samp(x, y) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + +class TestCvm: + # the expected values of the cdfs are taken from Table 1 in + # Csorgo / Faraway: The Exact and Asymptotic Distribution of + # Cramér-von Mises Statistics, 1996. + def test_cdf_4(self): + assert_allclose( + _cdf_cvm([0.02983, 0.04111, 0.12331, 0.94251], 4), + [0.01, 0.05, 0.5, 0.999], + atol=1e-4) + + def test_cdf_10(self): + assert_allclose( + _cdf_cvm([0.02657, 0.03830, 0.12068, 0.56643], 10), + [0.01, 0.05, 0.5, 0.975], + atol=1e-4) + + def test_cdf_1000(self): + assert_allclose( + _cdf_cvm([0.02481, 0.03658, 0.11889, 1.16120], 1000), + [0.01, 0.05, 0.5, 0.999], + atol=1e-4) + + def test_cdf_inf(self): + assert_allclose( + _cdf_cvm([0.02480, 0.03656, 0.11888, 1.16204]), + [0.01, 0.05, 0.5, 0.999], + atol=1e-4) + + def test_cdf_support(self): + # cdf has support on [1/(12*n), n/3] + assert_equal(_cdf_cvm([1/(12*533), 533/3], 533), [0, 1]) + assert_equal(_cdf_cvm([1/(12*(27 + 1)), (27 + 1)/3], 27), [0, 1]) + + def test_cdf_large_n(self): + # test that asymptotic cdf and cdf for large samples are close + assert_allclose( + _cdf_cvm([0.02480, 0.03656, 0.11888, 1.16204, 100], 10000), + _cdf_cvm([0.02480, 0.03656, 0.11888, 1.16204, 100]), + atol=1e-4) + + def test_large_x(self): + # for large values of x and n, the series used to compute the cdf + # converges slowly. + # this leads to bug in R package goftest and MAPLE code that is + # the basis of the implementation in scipy + # note: cdf = 1 for x >= 1000/3 and n = 1000 + assert_(0.99999 < _cdf_cvm(333.3, 1000) < 1.0) + assert_(0.99999 < _cdf_cvm(333.3) < 1.0) + + def test_low_p(self): + # _cdf_cvm can return values larger than 1. In that case, we just + # return a p-value of zero. + n = 12 + res = cramervonmises(np.ones(n)*0.8, 'norm') + assert_(_cdf_cvm(res.statistic, n) > 1.0) + assert_equal(res.pvalue, 0) + + def test_invalid_input(self): + assert_raises(ValueError, cramervonmises, [1.5], "norm") + assert_raises(ValueError, cramervonmises, (), "norm") + + def test_values_R(self): + # compared against R package goftest, version 1.1.1 + # goftest::cvm.test(c(-1.7, 2, 0, 1.3, 4, 0.1, 0.6), "pnorm") + res = cramervonmises([-1.7, 2, 0, 1.3, 4, 0.1, 0.6], "norm") + assert_allclose(res.statistic, 0.288156, atol=1e-6) + assert_allclose(res.pvalue, 0.1453465, atol=1e-6) + + # goftest::cvm.test(c(-1.7, 2, 0, 1.3, 4, 0.1, 0.6), + # "pnorm", mean = 3, sd = 1.5) + res = cramervonmises([-1.7, 2, 0, 1.3, 4, 0.1, 0.6], "norm", (3, 1.5)) + assert_allclose(res.statistic, 0.9426685, atol=1e-6) + assert_allclose(res.pvalue, 0.002026417, atol=1e-6) + + # goftest::cvm.test(c(1, 2, 5, 1.4, 0.14, 11, 13, 0.9, 7.5), "pexp") + res = cramervonmises([1, 2, 5, 1.4, 0.14, 11, 13, 0.9, 7.5], "expon") + assert_allclose(res.statistic, 0.8421854, atol=1e-6) + assert_allclose(res.pvalue, 0.004433406, atol=1e-6) + + def test_callable_cdf(self): + x, args = np.arange(5), (1.4, 0.7) + r1 = cramervonmises(x, distributions.expon.cdf) + r2 = cramervonmises(x, "expon") + assert_equal((r1.statistic, r1.pvalue), (r2.statistic, r2.pvalue)) + + r1 = cramervonmises(x, distributions.beta.cdf, args) + r2 = cramervonmises(x, "beta", args) + assert_equal((r1.statistic, r1.pvalue), (r2.statistic, r2.pvalue)) + + +class TestMannWhitneyU: + def setup_method(self): + _mwu_state._recursive = True + + # All magic numbers are from R wilcox.test unless otherwise specified + # https://rdrr.io/r/stats/wilcox.test.html + + # --- Test Input Validation --- + + def test_input_validation(self): + x = np.array([1, 2]) # generic, valid inputs + y = np.array([3, 4]) + with assert_raises(ValueError, match="`x` and `y` must be of nonzero"): + mannwhitneyu([], y) + with assert_raises(ValueError, match="`x` and `y` must be of nonzero"): + mannwhitneyu(x, []) + with assert_raises(ValueError, match="`use_continuity` must be one"): + mannwhitneyu(x, y, use_continuity='ekki') + with assert_raises(ValueError, match="`alternative` must be one of"): + mannwhitneyu(x, y, alternative='ekki') + with assert_raises(ValueError, match="`axis` must be an integer"): + mannwhitneyu(x, y, axis=1.5) + with assert_raises(ValueError, match="`method` must be one of"): + mannwhitneyu(x, y, method='ekki') + + def test_auto(self): + # Test that default method ('auto') chooses intended method + + np.random.seed(1) + n = 8 # threshold to switch from exact to asymptotic + + # both inputs are smaller than threshold; should use exact + x = np.random.rand(n-1) + y = np.random.rand(n-1) + auto = mannwhitneyu(x, y) + asymptotic = mannwhitneyu(x, y, method='asymptotic') + exact = mannwhitneyu(x, y, method='exact') + assert auto.pvalue == exact.pvalue + assert auto.pvalue != asymptotic.pvalue + + # one input is smaller than threshold; should use exact + x = np.random.rand(n-1) + y = np.random.rand(n+1) + auto = mannwhitneyu(x, y) + asymptotic = mannwhitneyu(x, y, method='asymptotic') + exact = mannwhitneyu(x, y, method='exact') + assert auto.pvalue == exact.pvalue + assert auto.pvalue != asymptotic.pvalue + + # other input is smaller than threshold; should use exact + auto = mannwhitneyu(y, x) + asymptotic = mannwhitneyu(x, y, method='asymptotic') + exact = mannwhitneyu(x, y, method='exact') + assert auto.pvalue == exact.pvalue + assert auto.pvalue != asymptotic.pvalue + + # both inputs are larger than threshold; should use asymptotic + x = np.random.rand(n+1) + y = np.random.rand(n+1) + auto = mannwhitneyu(x, y) + asymptotic = mannwhitneyu(x, y, method='asymptotic') + exact = mannwhitneyu(x, y, method='exact') + assert auto.pvalue != exact.pvalue + assert auto.pvalue == asymptotic.pvalue + + # both inputs are smaller than threshold, but there is a tie + # should use asymptotic + x = np.random.rand(n-1) + y = np.random.rand(n-1) + y[3] = x[3] + auto = mannwhitneyu(x, y) + asymptotic = mannwhitneyu(x, y, method='asymptotic') + exact = mannwhitneyu(x, y, method='exact') + assert auto.pvalue != exact.pvalue + assert auto.pvalue == asymptotic.pvalue + + # --- Test Basic Functionality --- + + x = [210.052110, 110.190630, 307.918612] + y = [436.08811482466416, 416.37397329768191, 179.96975939463582, + 197.8118754228619, 34.038757281225756, 138.54220550921517, + 128.7769351470246, 265.92721427951852, 275.6617533155341, + 592.34083395416258, 448.73177590617018, 300.61495185038905, + 187.97508449019588] + + # This test was written for mann_whitney_u in gh-4933. + # Originally, the p-values for alternatives were swapped; + # this has been corrected and the tests have been refactored for + # compactness, but otherwise the tests are unchanged. + # R code for comparison, e.g.: + # options(digits = 16) + # x = c(210.052110, 110.190630, 307.918612) + # y = c(436.08811482466416, 416.37397329768191, 179.96975939463582, + # 197.8118754228619, 34.038757281225756, 138.54220550921517, + # 128.7769351470246, 265.92721427951852, 275.6617533155341, + # 592.34083395416258, 448.73177590617018, 300.61495185038905, + # 187.97508449019588) + # wilcox.test(x, y, alternative="g", exact=TRUE) + cases_basic = [[{"alternative": 'two-sided', "method": "asymptotic"}, + (16, 0.6865041817876)], + [{"alternative": 'less', "method": "asymptotic"}, + (16, 0.3432520908938)], + [{"alternative": 'greater', "method": "asymptotic"}, + (16, 0.7047591913255)], + [{"alternative": 'two-sided', "method": "exact"}, + (16, 0.7035714285714)], + [{"alternative": 'less', "method": "exact"}, + (16, 0.3517857142857)], + [{"alternative": 'greater', "method": "exact"}, + (16, 0.6946428571429)]] + + @pytest.mark.parametrize(("kwds", "expected"), cases_basic) + def test_basic(self, kwds, expected): + res = mannwhitneyu(self.x, self.y, **kwds) + assert_allclose(res, expected) + + cases_continuity = [[{"alternative": 'two-sided', "use_continuity": True}, + (23, 0.6865041817876)], + [{"alternative": 'less', "use_continuity": True}, + (23, 0.7047591913255)], + [{"alternative": 'greater', "use_continuity": True}, + (23, 0.3432520908938)], + [{"alternative": 'two-sided', "use_continuity": False}, + (23, 0.6377328900502)], + [{"alternative": 'less', "use_continuity": False}, + (23, 0.6811335549749)], + [{"alternative": 'greater', "use_continuity": False}, + (23, 0.3188664450251)]] + + @pytest.mark.parametrize(("kwds", "expected"), cases_continuity) + def test_continuity(self, kwds, expected): + # When x and y are interchanged, less and greater p-values should + # swap (compare to above). This wouldn't happen if the continuity + # correction were applied in the wrong direction. Note that less and + # greater p-values do not sum to 1 when continuity correction is on, + # which is what we'd expect. Also check that results match R when + # continuity correction is turned off. + # Note that method='asymptotic' -> exact=FALSE + # and use_continuity=False -> correct=FALSE, e.g.: + # wilcox.test(x, y, alternative="t", exact=FALSE, correct=FALSE) + res = mannwhitneyu(self.y, self.x, method='asymptotic', **kwds) + assert_allclose(res, expected) + + def test_tie_correct(self): + # Test tie correction against R's wilcox.test + # options(digits = 16) + # x = c(1, 2, 3, 4) + # y = c(1, 2, 3, 4, 5) + # wilcox.test(x, y, exact=FALSE) + x = [1, 2, 3, 4] + y0 = np.array([1, 2, 3, 4, 5]) + dy = np.array([0, 1, 0, 1, 0])*0.01 + dy2 = np.array([0, 0, 1, 0, 0])*0.01 + y = [y0-0.01, y0-dy, y0-dy2, y0, y0+dy2, y0+dy, y0+0.01] + res = mannwhitneyu(x, y, axis=-1, method="asymptotic") + U_expected = [10, 9, 8.5, 8, 7.5, 7, 6] + p_expected = [1, 0.9017048037317, 0.804080657472, 0.7086240584439, + 0.6197963884941, 0.5368784563079, 0.3912672792826] + assert_equal(res.statistic, U_expected) + assert_allclose(res.pvalue, p_expected) + + # --- Test Exact Distribution of U --- + + # These are tabulated values of the CDF of the exact distribution of + # the test statistic from pg 52 of reference [1] (Mann-Whitney Original) + pn3 = {1: [0.25, 0.5, 0.75], 2: [0.1, 0.2, 0.4, 0.6], + 3: [0.05, .1, 0.2, 0.35, 0.5, 0.65]} + pn4 = {1: [0.2, 0.4, 0.6], 2: [0.067, 0.133, 0.267, 0.4, 0.6], + 3: [0.028, 0.057, 0.114, 0.2, .314, 0.429, 0.571], + 4: [0.014, 0.029, 0.057, 0.1, 0.171, 0.243, 0.343, 0.443, 0.557]} + pm5 = {1: [0.167, 0.333, 0.5, 0.667], + 2: [0.047, 0.095, 0.19, 0.286, 0.429, 0.571], + 3: [0.018, 0.036, 0.071, 0.125, 0.196, 0.286, 0.393, 0.5, 0.607], + 4: [0.008, 0.016, 0.032, 0.056, 0.095, 0.143, + 0.206, 0.278, 0.365, 0.452, 0.548], + 5: [0.004, 0.008, 0.016, 0.028, 0.048, 0.075, 0.111, + 0.155, 0.21, 0.274, 0.345, .421, 0.5, 0.579]} + pm6 = {1: [0.143, 0.286, 0.428, 0.571], + 2: [0.036, 0.071, 0.143, 0.214, 0.321, 0.429, 0.571], + 3: [0.012, 0.024, 0.048, 0.083, 0.131, + 0.19, 0.274, 0.357, 0.452, 0.548], + 4: [0.005, 0.01, 0.019, 0.033, 0.057, 0.086, 0.129, + 0.176, 0.238, 0.305, 0.381, 0.457, 0.543], # the last element + # of the previous list, 0.543, has been modified from 0.545; + # I assume it was a typo + 5: [0.002, 0.004, 0.009, 0.015, 0.026, 0.041, 0.063, 0.089, + 0.123, 0.165, 0.214, 0.268, 0.331, 0.396, 0.465, 0.535], + 6: [0.001, 0.002, 0.004, 0.008, 0.013, 0.021, 0.032, 0.047, + 0.066, 0.09, 0.12, 0.155, 0.197, 0.242, 0.294, 0.350, + 0.409, 0.469, 0.531]} + + def test_exact_distribution(self): + # I considered parametrize. I decided against it. + p_tables = {3: self.pn3, 4: self.pn4, 5: self.pm5, 6: self.pm6} + for n, table in p_tables.items(): + for m, p in table.items(): + # check p-value against table + u = np.arange(0, len(p)) + assert_allclose(_mwu_state.cdf(k=u, m=m, n=n), p, atol=1e-3) + + # check identity CDF + SF - PMF = 1 + # ( In this implementation, SF(U) includes PMF(U) ) + u2 = np.arange(0, m*n+1) + assert_allclose(_mwu_state.cdf(k=u2, m=m, n=n) + + _mwu_state.sf(k=u2, m=m, n=n) + - _mwu_state.pmf(k=u2, m=m, n=n), 1) + + # check symmetry about mean of U, i.e. pmf(U) = pmf(m*n-U) + pmf = _mwu_state.pmf(k=u2, m=m, n=n) + assert_allclose(pmf, pmf[::-1]) + + # check symmetry w.r.t. interchange of m, n + pmf2 = _mwu_state.pmf(k=u2, m=n, n=m) + assert_allclose(pmf, pmf2) + + def test_asymptotic_behavior(self): + np.random.seed(0) + + # for small samples, the asymptotic test is not very accurate + x = np.random.rand(5) + y = np.random.rand(5) + res1 = mannwhitneyu(x, y, method="exact") + res2 = mannwhitneyu(x, y, method="asymptotic") + assert res1.statistic == res2.statistic + assert np.abs(res1.pvalue - res2.pvalue) > 1e-2 + + # for large samples, they agree reasonably well + x = np.random.rand(40) + y = np.random.rand(40) + res1 = mannwhitneyu(x, y, method="exact") + res2 = mannwhitneyu(x, y, method="asymptotic") + assert res1.statistic == res2.statistic + assert np.abs(res1.pvalue - res2.pvalue) < 1e-3 + + # --- Test Corner Cases --- + + def test_exact_U_equals_mean(self): + # Test U == m*n/2 with exact method + # Without special treatment, two-sided p-value > 1 because both + # one-sided p-values are > 0.5 + res_l = mannwhitneyu([1, 2, 3], [1.5, 2.5], alternative="less", + method="exact") + res_g = mannwhitneyu([1, 2, 3], [1.5, 2.5], alternative="greater", + method="exact") + assert_equal(res_l.pvalue, res_g.pvalue) + assert res_l.pvalue > 0.5 + + res = mannwhitneyu([1, 2, 3], [1.5, 2.5], alternative="two-sided", + method="exact") + assert_equal(res, (3, 1)) + # U == m*n/2 for asymptotic case tested in test_gh_2118 + # The reason it's tricky for the asymptotic test has to do with + # continuity correction. + + cases_scalar = [[{"alternative": 'two-sided', "method": "asymptotic"}, + (0, 1)], + [{"alternative": 'less', "method": "asymptotic"}, + (0, 0.5)], + [{"alternative": 'greater', "method": "asymptotic"}, + (0, 0.977249868052)], + [{"alternative": 'two-sided', "method": "exact"}, (0, 1)], + [{"alternative": 'less', "method": "exact"}, (0, 0.5)], + [{"alternative": 'greater', "method": "exact"}, (0, 1)]] + + @pytest.mark.parametrize(("kwds", "result"), cases_scalar) + def test_scalar_data(self, kwds, result): + # just making sure scalars work + assert_allclose(mannwhitneyu(1, 2, **kwds), result) + + def test_equal_scalar_data(self): + # when two scalars are equal, there is an -0.5/0 in the asymptotic + # approximation. R gives pvalue=1.0 for alternatives 'less' and + # 'greater' but NA for 'two-sided'. I don't see why, so I don't + # see a need for a special case to match that behavior. + assert_equal(mannwhitneyu(1, 1, method="exact"), (0.5, 1)) + assert_equal(mannwhitneyu(1, 1, method="asymptotic"), (0.5, 1)) + + # without continuity correction, this becomes 0/0, which really + # is undefined + assert_equal(mannwhitneyu(1, 1, method="asymptotic", + use_continuity=False), (0.5, np.nan)) + + # --- Test Enhancements / Bug Reports --- + + @pytest.mark.parametrize("method", ["asymptotic", "exact"]) + def test_gh_12837_11113(self, method): + # Test that behavior for broadcastable nd arrays is appropriate: + # output shape is correct and all values are equal to when the test + # is performed on one pair of samples at a time. + # Tests that gh-12837 and gh-11113 (requests for n-d input) + # are resolved + np.random.seed(0) + + # arrays are broadcastable except for axis = -3 + axis = -3 + m, n = 7, 10 # sample sizes + x = np.random.rand(m, 3, 8) + y = np.random.rand(6, n, 1, 8) + 0.1 + res = mannwhitneyu(x, y, method=method, axis=axis) + + shape = (6, 3, 8) # appropriate shape of outputs, given inputs + assert res.pvalue.shape == shape + assert res.statistic.shape == shape + + # move axis of test to end for simplicity + x, y = np.moveaxis(x, axis, -1), np.moveaxis(y, axis, -1) + + x = x[None, ...] # give x a zeroth dimension + assert x.ndim == y.ndim + + x = np.broadcast_to(x, shape + (m,)) + y = np.broadcast_to(y, shape + (n,)) + assert x.shape[:-1] == shape + assert y.shape[:-1] == shape + + # loop over pairs of samples + statistics = np.zeros(shape) + pvalues = np.zeros(shape) + for indices in product(*[range(i) for i in shape]): + xi = x[indices] + yi = y[indices] + temp = mannwhitneyu(xi, yi, method=method) + statistics[indices] = temp.statistic + pvalues[indices] = temp.pvalue + + np.testing.assert_equal(res.pvalue, pvalues) + np.testing.assert_equal(res.statistic, statistics) + + def test_gh_11355(self): + # Test for correct behavior with NaN/Inf in input + x = [1, 2, 3, 4] + y = [3, 6, 7, 8, 9, 3, 2, 1, 4, 4, 5] + res1 = mannwhitneyu(x, y) + + # Inf is not a problem. This is a rank test, and it's the largest value + y[4] = np.inf + res2 = mannwhitneyu(x, y) + + assert_equal(res1.statistic, res2.statistic) + assert_equal(res1.pvalue, res2.pvalue) + + # NaNs should propagate by default. + y[4] = np.nan + res3 = mannwhitneyu(x, y) + assert_equal(res3.statistic, np.nan) + assert_equal(res3.pvalue, np.nan) + + cases_11355 = [([1, 2, 3, 4], + [3, 6, 7, 8, np.inf, 3, 2, 1, 4, 4, 5], + 10, 0.1297704873477), + ([1, 2, 3, 4], + [3, 6, 7, 8, np.inf, np.inf, 2, 1, 4, 4, 5], + 8.5, 0.08735617507695), + ([1, 2, np.inf, 4], + [3, 6, 7, 8, np.inf, 3, 2, 1, 4, 4, 5], + 17.5, 0.5988856695752), + ([1, 2, np.inf, 4], + [3, 6, 7, 8, np.inf, np.inf, 2, 1, 4, 4, 5], + 16, 0.4687165824462), + ([1, np.inf, np.inf, 4], + [3, 6, 7, 8, np.inf, np.inf, 2, 1, 4, 4, 5], + 24.5, 0.7912517950119)] + + @pytest.mark.parametrize(("x", "y", "statistic", "pvalue"), cases_11355) + def test_gh_11355b(self, x, y, statistic, pvalue): + # Test for correct behavior with NaN/Inf in input + res = mannwhitneyu(x, y, method='asymptotic') + assert_allclose(res.statistic, statistic, atol=1e-12) + assert_allclose(res.pvalue, pvalue, atol=1e-12) + + cases_9184 = [[True, "less", "asymptotic", 0.900775348204], + [True, "greater", "asymptotic", 0.1223118025635], + [True, "two-sided", "asymptotic", 0.244623605127], + [False, "less", "asymptotic", 0.8896643190401], + [False, "greater", "asymptotic", 0.1103356809599], + [False, "two-sided", "asymptotic", 0.2206713619198], + [True, "less", "exact", 0.8967698967699], + [True, "greater", "exact", 0.1272061272061], + [True, "two-sided", "exact", 0.2544122544123]] + + @pytest.mark.parametrize(("use_continuity", "alternative", + "method", "pvalue_exp"), cases_9184) + def test_gh_9184(self, use_continuity, alternative, method, pvalue_exp): + # gh-9184 might be considered a doc-only bug. Please see the + # documentation to confirm that mannwhitneyu correctly notes + # that the output statistic is that of the first sample (x). In any + # case, check the case provided there against output from R. + # R code: + # options(digits=16) + # x <- c(0.80, 0.83, 1.89, 1.04, 1.45, 1.38, 1.91, 1.64, 0.73, 1.46) + # y <- c(1.15, 0.88, 0.90, 0.74, 1.21) + # wilcox.test(x, y, alternative = "less", exact = FALSE) + # wilcox.test(x, y, alternative = "greater", exact = FALSE) + # wilcox.test(x, y, alternative = "two.sided", exact = FALSE) + # wilcox.test(x, y, alternative = "less", exact = FALSE, + # correct=FALSE) + # wilcox.test(x, y, alternative = "greater", exact = FALSE, + # correct=FALSE) + # wilcox.test(x, y, alternative = "two.sided", exact = FALSE, + # correct=FALSE) + # wilcox.test(x, y, alternative = "less", exact = TRUE) + # wilcox.test(x, y, alternative = "greater", exact = TRUE) + # wilcox.test(x, y, alternative = "two.sided", exact = TRUE) + statistic_exp = 35 + x = (0.80, 0.83, 1.89, 1.04, 1.45, 1.38, 1.91, 1.64, 0.73, 1.46) + y = (1.15, 0.88, 0.90, 0.74, 1.21) + res = mannwhitneyu(x, y, use_continuity=use_continuity, + alternative=alternative, method=method) + assert_equal(res.statistic, statistic_exp) + assert_allclose(res.pvalue, pvalue_exp) + + def test_gh_6897(self): + # Test for correct behavior with empty input + with assert_raises(ValueError, match="`x` and `y` must be of nonzero"): + mannwhitneyu([], []) + + def test_gh_4067(self): + # Test for correct behavior with all NaN input - default is propagate + a = np.array([np.nan, np.nan, np.nan, np.nan, np.nan]) + b = np.array([np.nan, np.nan, np.nan, np.nan, np.nan]) + res = mannwhitneyu(a, b) + assert_equal(res.statistic, np.nan) + assert_equal(res.pvalue, np.nan) + + # All cases checked against R wilcox.test, e.g. + # options(digits=16) + # x = c(1, 2, 3) + # y = c(1.5, 2.5) + # wilcox.test(x, y, exact=FALSE, alternative='less') + + cases_2118 = [[[1, 2, 3], [1.5, 2.5], "greater", (3, 0.6135850036578)], + [[1, 2, 3], [1.5, 2.5], "less", (3, 0.6135850036578)], + [[1, 2, 3], [1.5, 2.5], "two-sided", (3, 1.0)], + [[1, 2, 3], [2], "greater", (1.5, 0.681324055883)], + [[1, 2, 3], [2], "less", (1.5, 0.681324055883)], + [[1, 2, 3], [2], "two-sided", (1.5, 1)], + [[1, 2], [1, 2], "greater", (2, 0.667497228949)], + [[1, 2], [1, 2], "less", (2, 0.667497228949)], + [[1, 2], [1, 2], "two-sided", (2, 1)]] + + @pytest.mark.parametrize(["x", "y", "alternative", "expected"], cases_2118) + def test_gh_2118(self, x, y, alternative, expected): + # test cases in which U == m*n/2 when method is asymptotic + # applying continuity correction could result in p-value > 1 + res = mannwhitneyu(x, y, use_continuity=True, alternative=alternative, + method="asymptotic") + assert_allclose(res, expected, rtol=1e-12) + + def test_gh19692_smaller_table(self): + # In gh-19692, we noted that the shape of the cache used in calculating + # p-values was dependent on the order of the inputs because the sample + # sizes n1 and n2 changed. This was indicative of unnecessary cache + # growth and redundant calculation. Check that this is resolved. + rng = np.random.default_rng(7600451795963068007) + x = rng.random(size=5) + y = rng.random(size=11) + _mwu_state._fmnks = -np.ones((1, 1, 1)) # reset cache + stats.mannwhitneyu(x, y, method='exact') + shape = _mwu_state._fmnks.shape + assert shape[0] <= 6 and shape[1] <= 12 # one more than sizes + stats.mannwhitneyu(y, x, method='exact') + assert shape == _mwu_state._fmnks.shape # unchanged when sizes are reversed + + # Also, we weren't exploiting the symmmetry of the null distribution + # to its full potential. Ensure that the null distribution is not + # evaluated explicitly for `k > m*n/2`. + _mwu_state._fmnks = -np.ones((1, 1, 1)) # reset cache + stats.mannwhitneyu(x, 0*y, method='exact', alternative='greater') + shape = _mwu_state._fmnks.shape + assert shape[-1] == 1 # k is smallest possible + stats.mannwhitneyu(0*x, y, method='exact', alternative='greater') + assert shape == _mwu_state._fmnks.shape + + @pytest.mark.parametrize('alternative', ['less', 'greater', 'two-sided']) + def test_permutation_method(self, alternative): + rng = np.random.default_rng(7600451795963068007) + x = rng.random(size=(2, 5)) + y = rng.random(size=(2, 6)) + res = stats.mannwhitneyu(x, y, method=stats.PermutationMethod(), + alternative=alternative, axis=1) + res2 = stats.mannwhitneyu(x, y, method='exact', + alternative=alternative, axis=1) + assert_allclose(res.statistic, res2.statistic, rtol=1e-15) + assert_allclose(res.pvalue, res2.pvalue, rtol=1e-15) + + def teardown_method(self): + _mwu_state._recursive = None + + +class TestMannWhitneyU_iterative(TestMannWhitneyU): + def setup_method(self): + _mwu_state._recursive = False + + def teardown_method(self): + _mwu_state._recursive = None + + +@pytest.mark.xslow +def test_mann_whitney_u_switch(): + # Check that mannwhiteneyu switches between recursive and iterative + # implementations at n = 500 + + # ensure that recursion is not enforced + _mwu_state._recursive = None + _mwu_state._fmnks = -np.ones((1, 1, 1)) + + rng = np.random.default_rng(9546146887652) + x = rng.random(5) + + # use iterative algorithm because n > 500 + y = rng.random(501) + stats.mannwhitneyu(x, y, method='exact') + # iterative algorithm doesn't modify _mwu_state._fmnks + assert np.all(_mwu_state._fmnks == -1) + + # use recursive algorithm because n <= 500 + y = rng.random(500) + stats.mannwhitneyu(x, y, method='exact') + + # recursive algorithm has modified _mwu_state._fmnks + assert not np.all(_mwu_state._fmnks == -1) + + +class TestSomersD(_TestPythranFunc): + def setup_method(self): + self.dtypes = self.ALL_INTEGER + self.ALL_FLOAT + self.arguments = {0: (np.arange(10), + self.ALL_INTEGER + self.ALL_FLOAT), + 1: (np.arange(10), + self.ALL_INTEGER + self.ALL_FLOAT)} + input_array = [self.arguments[idx][0] for idx in self.arguments] + # In this case, self.partialfunc can simply be stats.somersd, + # since `alternative` is an optional argument. If it is required, + # we can use functools.partial to freeze the value, because + # we only mainly test various array inputs, not str, etc. + self.partialfunc = functools.partial(stats.somersd, + alternative='two-sided') + self.expected = self.partialfunc(*input_array) + + def pythranfunc(self, *args): + res = self.partialfunc(*args) + assert_allclose(res.statistic, self.expected.statistic, atol=1e-15) + assert_allclose(res.pvalue, self.expected.pvalue, atol=1e-15) + + def test_pythranfunc_keywords(self): + # Not specifying the optional keyword args + table = [[27, 25, 14, 7, 0], [7, 14, 18, 35, 12], [1, 3, 2, 7, 17]] + res1 = stats.somersd(table) + # Specifying the optional keyword args with default value + optional_args = self.get_optional_args(stats.somersd) + res2 = stats.somersd(table, **optional_args) + # Check if the results are the same in two cases + assert_allclose(res1.statistic, res2.statistic, atol=1e-15) + assert_allclose(res1.pvalue, res2.pvalue, atol=1e-15) + + def test_like_kendalltau(self): + # All tests correspond with one in test_stats.py `test_kendalltau` + + # case without ties, con-dis equal zero + x = [5, 2, 1, 3, 6, 4, 7, 8] + y = [5, 2, 6, 3, 1, 8, 7, 4] + # Cross-check with result from SAS FREQ: + expected = (0.000000000000000, 1.000000000000000) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # case without ties, con-dis equal zero + x = [0, 5, 2, 1, 3, 6, 4, 7, 8] + y = [5, 2, 0, 6, 3, 1, 8, 7, 4] + # Cross-check with result from SAS FREQ: + expected = (0.000000000000000, 1.000000000000000) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # case without ties, con-dis close to zero + x = [5, 2, 1, 3, 6, 4, 7] + y = [5, 2, 6, 3, 1, 7, 4] + # Cross-check with result from SAS FREQ: + expected = (-0.142857142857140, 0.630326953157670) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # simple case without ties + x = np.arange(10) + y = np.arange(10) + # Cross-check with result from SAS FREQ: + # SAS p value is not provided. + expected = (1.000000000000000, 0) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # swap a couple values and a couple more + x = np.arange(10) + y = np.array([0, 2, 1, 3, 4, 6, 5, 7, 8, 9]) + # Cross-check with result from SAS FREQ: + expected = (0.911111111111110, 0.000000000000000) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # same in opposite direction + x = np.arange(10) + y = np.arange(10)[::-1] + # Cross-check with result from SAS FREQ: + # SAS p value is not provided. + expected = (-1.000000000000000, 0) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # swap a couple values and a couple more + x = np.arange(10) + y = np.array([9, 7, 8, 6, 5, 3, 4, 2, 1, 0]) + # Cross-check with result from SAS FREQ: + expected = (-0.9111111111111111, 0.000000000000000) + res = stats.somersd(x, y) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # with some ties + x1 = [12, 2, 1, 12, 2] + x2 = [1, 4, 7, 1, 0] + # Cross-check with result from SAS FREQ: + expected = (-0.500000000000000, 0.304901788178780) + res = stats.somersd(x1, x2) + assert_allclose(res.statistic, expected[0], atol=1e-15) + assert_allclose(res.pvalue, expected[1], atol=1e-15) + + # with only ties in one or both inputs + # SAS will not produce an output for these: + # NOTE: No statistics are computed for x * y because x has fewer + # than 2 nonmissing levels. + # WARNING: No OUTPUT data set is produced for this table because a + # row or column variable has fewer than 2 nonmissing levels and no + # statistics are computed. + + res = stats.somersd([2, 2, 2], [2, 2, 2]) + assert_allclose(res.statistic, np.nan) + assert_allclose(res.pvalue, np.nan) + + res = stats.somersd([2, 0, 2], [2, 2, 2]) + assert_allclose(res.statistic, np.nan) + assert_allclose(res.pvalue, np.nan) + + res = stats.somersd([2, 2, 2], [2, 0, 2]) + assert_allclose(res.statistic, np.nan) + assert_allclose(res.pvalue, np.nan) + + res = stats.somersd([0], [0]) + assert_allclose(res.statistic, np.nan) + assert_allclose(res.pvalue, np.nan) + + # empty arrays provided as input + res = stats.somersd([], []) + assert_allclose(res.statistic, np.nan) + assert_allclose(res.pvalue, np.nan) + + # test unequal length inputs + x = np.arange(10.) + y = np.arange(20.) + assert_raises(ValueError, stats.somersd, x, y) + + def test_asymmetry(self): + # test that somersd is asymmetric w.r.t. input order and that + # convention is as described: first input is row variable & independent + # data is from Wikipedia: + # https://en.wikipedia.org/wiki/Somers%27_D + # but currently that example contradicts itself - it says X is + # independent yet take D_XY + + x = [1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 1, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3] + y = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + # Cross-check with result from SAS FREQ: + d_cr = 0.272727272727270 + d_rc = 0.342857142857140 + p = 0.092891940883700 # same p-value for either direction + res = stats.somersd(x, y) + assert_allclose(res.statistic, d_cr, atol=1e-15) + assert_allclose(res.pvalue, p, atol=1e-4) + assert_equal(res.table.shape, (3, 2)) + res = stats.somersd(y, x) + assert_allclose(res.statistic, d_rc, atol=1e-15) + assert_allclose(res.pvalue, p, atol=1e-15) + assert_equal(res.table.shape, (2, 3)) + + def test_somers_original(self): + # test against Somers' original paper [1] + + # Table 5A + # Somers' convention was column IV + table = np.array([[8, 2], [6, 5], [3, 4], [1, 3], [2, 3]]) + # Our convention (and that of SAS FREQ) is row IV + table = table.T + dyx = 129/340 + assert_allclose(stats.somersd(table).statistic, dyx) + + # table 7A - d_yx = 1 + table = np.array([[25, 0], [85, 0], [0, 30]]) + dxy, dyx = 3300/5425, 3300/3300 + assert_allclose(stats.somersd(table).statistic, dxy) + assert_allclose(stats.somersd(table.T).statistic, dyx) + + # table 7B - d_yx < 0 + table = np.array([[25, 0], [0, 30], [85, 0]]) + dyx = -1800/3300 + assert_allclose(stats.somersd(table.T).statistic, dyx) + + def test_contingency_table_with_zero_rows_cols(self): + # test that zero rows/cols in contingency table don't affect result + + N = 100 + shape = 4, 6 + size = np.prod(shape) + + np.random.seed(0) + s = stats.multinomial.rvs(N, p=np.ones(size)/size).reshape(shape) + res = stats.somersd(s) + + s2 = np.insert(s, 2, np.zeros(shape[1]), axis=0) + res2 = stats.somersd(s2) + + s3 = np.insert(s, 2, np.zeros(shape[0]), axis=1) + res3 = stats.somersd(s3) + + s4 = np.insert(s2, 2, np.zeros(shape[0]+1), axis=1) + res4 = stats.somersd(s4) + + # Cross-check with result from SAS FREQ: + assert_allclose(res.statistic, -0.116981132075470, atol=1e-15) + assert_allclose(res.statistic, res2.statistic) + assert_allclose(res.statistic, res3.statistic) + assert_allclose(res.statistic, res4.statistic) + + assert_allclose(res.pvalue, 0.156376448188150, atol=1e-15) + assert_allclose(res.pvalue, res2.pvalue) + assert_allclose(res.pvalue, res3.pvalue) + assert_allclose(res.pvalue, res4.pvalue) + + def test_invalid_contingency_tables(self): + N = 100 + shape = 4, 6 + size = np.prod(shape) + + np.random.seed(0) + # start with a valid contingency table + s = stats.multinomial.rvs(N, p=np.ones(size)/size).reshape(shape) + + s5 = s - 2 + message = "All elements of the contingency table must be non-negative" + with assert_raises(ValueError, match=message): + stats.somersd(s5) + + s6 = s + 0.01 + message = "All elements of the contingency table must be integer" + with assert_raises(ValueError, match=message): + stats.somersd(s6) + + message = ("At least two elements of the contingency " + "table must be nonzero.") + with assert_raises(ValueError, match=message): + stats.somersd([[]]) + + with assert_raises(ValueError, match=message): + stats.somersd([[1]]) + + s7 = np.zeros((3, 3)) + with assert_raises(ValueError, match=message): + stats.somersd(s7) + + s7[0, 1] = 1 + with assert_raises(ValueError, match=message): + stats.somersd(s7) + + def test_only_ranks_matter(self): + # only ranks of input data should matter + x = [1, 2, 3] + x2 = [-1, 2.1, np.inf] + y = [3, 2, 1] + y2 = [0, -0.5, -np.inf] + res = stats.somersd(x, y) + res2 = stats.somersd(x2, y2) + assert_equal(res.statistic, res2.statistic) + assert_equal(res.pvalue, res2.pvalue) + + def test_contingency_table_return(self): + # check that contingency table is returned + x = np.arange(10) + y = np.arange(10) + res = stats.somersd(x, y) + assert_equal(res.table, np.eye(10)) + + def test_somersd_alternative(self): + # Test alternative parameter, asymptotic method (due to tie) + + # Based on scipy.stats.test_stats.TestCorrSpearman2::test_alternative + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 7] + + # strong positive correlation + expected = stats.somersd(x1, x2, alternative="two-sided") + assert expected.statistic > 0 + + # rank correlation > 0 -> large "less" p-value + res = stats.somersd(x1, x2, alternative="less") + assert_equal(res.statistic, expected.statistic) + assert_allclose(res.pvalue, 1 - (expected.pvalue / 2)) + + # rank correlation > 0 -> small "greater" p-value + res = stats.somersd(x1, x2, alternative="greater") + assert_equal(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue / 2) + + # reverse the direction of rank correlation + x2.reverse() + + # strong negative correlation + expected = stats.somersd(x1, x2, alternative="two-sided") + assert expected.statistic < 0 + + # rank correlation < 0 -> large "greater" p-value + res = stats.somersd(x1, x2, alternative="greater") + assert_equal(res.statistic, expected.statistic) + assert_allclose(res.pvalue, 1 - (expected.pvalue / 2)) + + # rank correlation < 0 -> small "less" p-value + res = stats.somersd(x1, x2, alternative="less") + assert_equal(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue / 2) + + with pytest.raises(ValueError, match="`alternative` must be..."): + stats.somersd(x1, x2, alternative="ekki-ekki") + + @pytest.mark.parametrize("positive_correlation", (False, True)) + def test_somersd_perfect_correlation(self, positive_correlation): + # Before the addition of `alternative`, perfect correlation was + # treated as a special case. Now it is treated like any other case, but + # make sure there are no divide by zero warnings or associated errors + + x1 = np.arange(10) + x2 = x1 if positive_correlation else np.flip(x1) + expected_statistic = 1 if positive_correlation else -1 + + # perfect correlation -> small "two-sided" p-value (0) + res = stats.somersd(x1, x2, alternative="two-sided") + assert res.statistic == expected_statistic + assert res.pvalue == 0 + + # rank correlation > 0 -> large "less" p-value (1) + res = stats.somersd(x1, x2, alternative="less") + assert res.statistic == expected_statistic + assert res.pvalue == (1 if positive_correlation else 0) + + # rank correlation > 0 -> small "greater" p-value (0) + res = stats.somersd(x1, x2, alternative="greater") + assert res.statistic == expected_statistic + assert res.pvalue == (0 if positive_correlation else 1) + + def test_somersd_large_inputs_gh18132(self): + # Test that large inputs where potential overflows could occur give + # the expected output. This is tested in the case of binary inputs. + # See gh-18126. + + # generate lists of random classes 1-2 (binary) + classes = [1, 2] + n_samples = 10 ** 6 + random.seed(6272161) + x = random.choices(classes, k=n_samples) + y = random.choices(classes, k=n_samples) + + # get value to compare with: sklearn output + # from sklearn import metrics + # val_auc_sklearn = metrics.roc_auc_score(x, y) + # # convert to the Gini coefficient (Gini = (AUC*2)-1) + # val_sklearn = 2 * val_auc_sklearn - 1 + val_sklearn = -0.001528138777036947 + + # calculate the Somers' D statistic, which should be equal to the + # result of val_sklearn until approximately machine precision + val_scipy = stats.somersd(x, y).statistic + assert_allclose(val_sklearn, val_scipy, atol=1e-15) + + +class TestBarnardExact: + """Some tests to show that barnard_exact() works correctly.""" + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[43, 40], [10, 39]], (3.555406779643, 0.000362832367)), + ([[100, 2], [1000, 5]], (-1.776382925679, 0.135126970878)), + ([[2, 7], [8, 2]], (-2.518474945157, 0.019210815430)), + ([[5, 1], [10, 10]], (1.449486150679, 0.156277546306)), + ([[5, 15], [20, 20]], (-1.851640199545, 0.066363501421)), + ([[5, 16], [20, 25]], (-1.609639949352, 0.116984852192)), + ([[10, 5], [10, 1]], (-1.449486150679, 0.177536588915)), + ([[5, 0], [1, 4]], (2.581988897472, 0.013671875000)), + ([[0, 1], [3, 2]], (-1.095445115010, 0.509667991877)), + ([[0, 2], [6, 4]], (-1.549193338483, 0.197019618792)), + ([[2, 7], [8, 2]], (-2.518474945157, 0.019210815430)), + ], + ) + def test_precise(self, input_sample, expected): + """The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-6 : + ```R + library(Barnard) + options(digits=10) + barnard.test(43, 40, 10, 39, dp=1e-6, pooled=TRUE) + ``` + """ + res = barnard_exact(input_sample) + statistic, pvalue = res.statistic, res.pvalue + assert_allclose([statistic, pvalue], expected) + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[43, 40], [10, 39]], (3.920362887717, 0.000289470662)), + ([[100, 2], [1000, 5]], (-1.139432816087, 0.950272080594)), + ([[2, 7], [8, 2]], (-3.079373904042, 0.020172119141)), + ([[5, 1], [10, 10]], (1.622375939458, 0.150599922226)), + ([[5, 15], [20, 20]], (-1.974771239528, 0.063038448651)), + ([[5, 16], [20, 25]], (-1.722122973346, 0.133329494287)), + ([[10, 5], [10, 1]], (-1.765469659009, 0.250566655215)), + ([[5, 0], [1, 4]], (5.477225575052, 0.007812500000)), + ([[0, 1], [3, 2]], (-1.224744871392, 0.509667991877)), + ([[0, 2], [6, 4]], (-1.732050807569, 0.197019618792)), + ([[2, 7], [8, 2]], (-3.079373904042, 0.020172119141)), + ], + ) + def test_pooled_param(self, input_sample, expected): + """The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-6 : + ```R + library(Barnard) + options(digits=10) + barnard.test(43, 40, 10, 39, dp=1e-6, pooled=FALSE) + ``` + """ + res = barnard_exact(input_sample, pooled=False) + statistic, pvalue = res.statistic, res.pvalue + assert_allclose([statistic, pvalue], expected) + + def test_raises(self): + # test we raise an error for wrong input number of nuisances. + error_msg = ( + "Number of points `n` must be strictly positive, found 0" + ) + with assert_raises(ValueError, match=error_msg): + barnard_exact([[1, 2], [3, 4]], n=0) + + # test we raise an error for wrong shape of input. + error_msg = "The input `table` must be of shape \\(2, 2\\)." + with assert_raises(ValueError, match=error_msg): + barnard_exact(np.arange(6).reshape(2, 3)) + + # Test all values must be positives + error_msg = "All values in `table` must be nonnegative." + with assert_raises(ValueError, match=error_msg): + barnard_exact([[-1, 2], [3, 4]]) + + # Test value error on wrong alternative param + error_msg = ( + "`alternative` should be one of {'two-sided', 'less', 'greater'}," + " found .*" + ) + with assert_raises(ValueError, match=error_msg): + barnard_exact([[1, 2], [3, 4]], "not-correct") + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[0, 0], [4, 3]], (1.0, 0)), + ], + ) + def test_edge_cases(self, input_sample, expected): + res = barnard_exact(input_sample) + statistic, pvalue = res.statistic, res.pvalue + assert_equal(pvalue, expected[0]) + assert_equal(statistic, expected[1]) + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[0, 5], [0, 10]], (1.0, np.nan)), + ([[5, 0], [10, 0]], (1.0, np.nan)), + ], + ) + def test_row_or_col_zero(self, input_sample, expected): + res = barnard_exact(input_sample) + statistic, pvalue = res.statistic, res.pvalue + assert_equal(pvalue, expected[0]) + assert_equal(statistic, expected[1]) + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[2, 7], [8, 2]], (-2.518474945157, 0.009886140845)), + ([[7, 200], [300, 8]], (-21.320036698460, 0.0)), + ([[21, 28], [1957, 6]], (-30.489638143953, 0.0)), + ], + ) + @pytest.mark.parametrize("alternative", ["greater", "less"]) + def test_less_greater(self, input_sample, expected, alternative): + """ + "The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-6 : + ```R + library(Barnard) + options(digits=10) + a = barnard.test(2, 7, 8, 2, dp=1e-6, pooled=TRUE) + a$p.value[1] + ``` + In this test, we are using the "one-sided" return value `a$p.value[1]` + to test our pvalue. + """ + expected_stat, less_pvalue_expect = expected + + if alternative == "greater": + input_sample = np.array(input_sample)[:, ::-1] + expected_stat = -expected_stat + + res = barnard_exact(input_sample, alternative=alternative) + statistic, pvalue = res.statistic, res.pvalue + assert_allclose( + [statistic, pvalue], [expected_stat, less_pvalue_expect], atol=1e-7 + ) + + +class TestBoschlooExact: + """Some tests to show that boschloo_exact() works correctly.""" + + ATOL = 1e-7 + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[2, 7], [8, 2]], (0.01852173, 0.009886142)), + ([[5, 1], [10, 10]], (0.9782609, 0.9450994)), + ([[5, 16], [20, 25]], (0.08913823, 0.05827348)), + ([[10, 5], [10, 1]], (0.1652174, 0.08565611)), + ([[5, 0], [1, 4]], (1, 1)), + ([[0, 1], [3, 2]], (0.5, 0.34375)), + ([[2, 7], [8, 2]], (0.01852173, 0.009886142)), + ([[7, 12], [8, 3]], (0.06406797, 0.03410916)), + ([[10, 24], [25, 37]], (0.2009359, 0.1512882)), + ], + ) + def test_less(self, input_sample, expected): + """The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-8 : + ```R + library(Exact) + options(digits=10) + data <- matrix(c(43, 10, 40, 39), 2, 2, byrow=TRUE) + a = exact.test(data, method="Boschloo", alternative="less", + tsmethod="central", np.interval=TRUE, beta=1e-8) + ``` + """ + res = boschloo_exact(input_sample, alternative="less") + statistic, pvalue = res.statistic, res.pvalue + assert_allclose([statistic, pvalue], expected, atol=self.ATOL) + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[43, 40], [10, 39]], (0.0002875544, 0.0001615562)), + ([[2, 7], [8, 2]], (0.9990149, 0.9918327)), + ([[5, 1], [10, 10]], (0.1652174, 0.09008534)), + ([[5, 15], [20, 20]], (0.9849087, 0.9706997)), + ([[5, 16], [20, 25]], (0.972349, 0.9524124)), + ([[5, 0], [1, 4]], (0.02380952, 0.006865367)), + ([[0, 1], [3, 2]], (1, 1)), + ([[0, 2], [6, 4]], (1, 1)), + ([[2, 7], [8, 2]], (0.9990149, 0.9918327)), + ([[7, 12], [8, 3]], (0.9895302, 0.9771215)), + ([[10, 24], [25, 37]], (0.9012936, 0.8633275)), + ], + ) + def test_greater(self, input_sample, expected): + """The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-8 : + ```R + library(Exact) + options(digits=10) + data <- matrix(c(43, 10, 40, 39), 2, 2, byrow=TRUE) + a = exact.test(data, method="Boschloo", alternative="greater", + tsmethod="central", np.interval=TRUE, beta=1e-8) + ``` + """ + res = boschloo_exact(input_sample, alternative="greater") + statistic, pvalue = res.statistic, res.pvalue + assert_allclose([statistic, pvalue], expected, atol=self.ATOL) + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[43, 40], [10, 39]], (0.0002875544, 0.0003231115)), + ([[2, 7], [8, 2]], (0.01852173, 0.01977228)), + ([[5, 1], [10, 10]], (0.1652174, 0.1801707)), + ([[5, 16], [20, 25]], (0.08913823, 0.116547)), + ([[5, 0], [1, 4]], (0.02380952, 0.01373073)), + ([[0, 1], [3, 2]], (0.5, 0.6875)), + ([[2, 7], [8, 2]], (0.01852173, 0.01977228)), + ([[7, 12], [8, 3]], (0.06406797, 0.06821831)), + ], + ) + def test_two_sided(self, input_sample, expected): + """The expected values have been generated by R, using a resolution + for the nuisance parameter of 1e-8 : + ```R + library(Exact) + options(digits=10) + data <- matrix(c(43, 10, 40, 39), 2, 2, byrow=TRUE) + a = exact.test(data, method="Boschloo", alternative="two.sided", + tsmethod="central", np.interval=TRUE, beta=1e-8) + ``` + """ + res = boschloo_exact(input_sample, alternative="two-sided", n=64) + # Need n = 64 for python 32-bit + statistic, pvalue = res.statistic, res.pvalue + assert_allclose([statistic, pvalue], expected, atol=self.ATOL) + + def test_raises(self): + # test we raise an error for wrong input number of nuisances. + error_msg = ( + "Number of points `n` must be strictly positive, found 0" + ) + with assert_raises(ValueError, match=error_msg): + boschloo_exact([[1, 2], [3, 4]], n=0) + + # test we raise an error for wrong shape of input. + error_msg = "The input `table` must be of shape \\(2, 2\\)." + with assert_raises(ValueError, match=error_msg): + boschloo_exact(np.arange(6).reshape(2, 3)) + + # Test all values must be positives + error_msg = "All values in `table` must be nonnegative." + with assert_raises(ValueError, match=error_msg): + boschloo_exact([[-1, 2], [3, 4]]) + + # Test value error on wrong alternative param + error_msg = ( + r"`alternative` should be one of \('two-sided', 'less', " + r"'greater'\), found .*" + ) + with assert_raises(ValueError, match=error_msg): + boschloo_exact([[1, 2], [3, 4]], "not-correct") + + @pytest.mark.parametrize( + "input_sample,expected", + [ + ([[0, 5], [0, 10]], (np.nan, np.nan)), + ([[5, 0], [10, 0]], (np.nan, np.nan)), + ], + ) + def test_row_or_col_zero(self, input_sample, expected): + res = boschloo_exact(input_sample) + statistic, pvalue = res.statistic, res.pvalue + assert_equal(pvalue, expected[0]) + assert_equal(statistic, expected[1]) + + def test_two_sided_gt_1(self): + # Check that returned p-value does not exceed 1 even when twice + # the minimum of the one-sided p-values does. See gh-15345. + tbl = [[1, 1], [13, 12]] + pl = boschloo_exact(tbl, alternative='less').pvalue + pg = boschloo_exact(tbl, alternative='greater').pvalue + assert 2*min(pl, pg) > 1 + pt = boschloo_exact(tbl, alternative='two-sided').pvalue + assert pt == 1.0 + + @pytest.mark.parametrize("alternative", ("less", "greater")) + def test_against_fisher_exact(self, alternative): + # Check that the statistic of `boschloo_exact` is the same as the + # p-value of `fisher_exact` (for one-sided tests). See gh-15345. + tbl = [[2, 7], [8, 2]] + boschloo_stat = boschloo_exact(tbl, alternative=alternative).statistic + fisher_p = stats.fisher_exact(tbl, alternative=alternative)[1] + assert_allclose(boschloo_stat, fisher_p) + + +class TestCvm_2samp: + def test_invalid_input(self): + y = np.arange(5) + msg = 'x and y must contain at least two observations.' + with pytest.raises(ValueError, match=msg): + cramervonmises_2samp([], y) + with pytest.raises(ValueError, match=msg): + cramervonmises_2samp(y, [1]) + msg = 'method must be either auto, exact or asymptotic' + with pytest.raises(ValueError, match=msg): + cramervonmises_2samp(y, y, 'xyz') + + def test_list_input(self): + x = [2, 3, 4, 7, 6] + y = [0.2, 0.7, 12, 18] + r1 = cramervonmises_2samp(x, y) + r2 = cramervonmises_2samp(np.array(x), np.array(y)) + assert_equal((r1.statistic, r1.pvalue), (r2.statistic, r2.pvalue)) + + def test_example_conover(self): + # Example 2 in Section 6.2 of W.J. Conover: Practical Nonparametric + # Statistics, 1971. + x = [7.6, 8.4, 8.6, 8.7, 9.3, 9.9, 10.1, 10.6, 11.2] + y = [5.2, 5.7, 5.9, 6.5, 6.8, 8.2, 9.1, 9.8, 10.8, 11.3, 11.5, 12.3, + 12.5, 13.4, 14.6] + r = cramervonmises_2samp(x, y) + assert_allclose(r.statistic, 0.262, atol=1e-3) + assert_allclose(r.pvalue, 0.18, atol=1e-2) + + @pytest.mark.parametrize('statistic, m, n, pval', + [(710, 5, 6, 48./462), + (1897, 7, 7, 117./1716), + (576, 4, 6, 2./210), + (1764, 6, 7, 2./1716)]) + def test_exact_pvalue(self, statistic, m, n, pval): + # the exact values are taken from Anderson: On the distribution of the + # two-sample Cramer-von-Mises criterion, 1962. + # The values are taken from Table 2, 3, 4 and 5 + assert_equal(_pval_cvm_2samp_exact(statistic, m, n), pval) + + def test_large_sample(self): + # for large samples, the statistic U gets very large + # do a sanity check that p-value is not 0, 1 or nan + np.random.seed(4367) + x = distributions.norm.rvs(size=1000000) + y = distributions.norm.rvs(size=900000) + r = cramervonmises_2samp(x, y) + assert_(0 < r.pvalue < 1) + r = cramervonmises_2samp(x, y+0.1) + assert_(0 < r.pvalue < 1) + + def test_exact_vs_asymptotic(self): + np.random.seed(0) + x = np.random.rand(7) + y = np.random.rand(8) + r1 = cramervonmises_2samp(x, y, method='exact') + r2 = cramervonmises_2samp(x, y, method='asymptotic') + assert_equal(r1.statistic, r2.statistic) + assert_allclose(r1.pvalue, r2.pvalue, atol=1e-2) + + def test_method_auto(self): + x = np.arange(20) + y = [0.5, 4.7, 13.1] + r1 = cramervonmises_2samp(x, y, method='exact') + r2 = cramervonmises_2samp(x, y, method='auto') + assert_equal(r1.pvalue, r2.pvalue) + # switch to asymptotic if one sample has more than 20 observations + x = np.arange(21) + r1 = cramervonmises_2samp(x, y, method='asymptotic') + r2 = cramervonmises_2samp(x, y, method='auto') + assert_equal(r1.pvalue, r2.pvalue) + + def test_same_input(self): + # make sure trivial edge case can be handled + # note that _cdf_cvm_inf(0) = nan. implementation avoids nan by + # returning pvalue=1 for very small values of the statistic + x = np.arange(15) + res = cramervonmises_2samp(x, x) + assert_equal((res.statistic, res.pvalue), (0.0, 1.0)) + # check exact p-value + res = cramervonmises_2samp(x[:4], x[:4]) + assert_equal((res.statistic, res.pvalue), (0.0, 1.0)) + + +class TestTukeyHSD: + + data_same_size = ([24.5, 23.5, 26.4, 27.1, 29.9], + [28.4, 34.2, 29.5, 32.2, 30.1], + [26.1, 28.3, 24.3, 26.2, 27.8]) + data_diff_size = ([24.5, 23.5, 26.28, 26.4, 27.1, 29.9, 30.1, 30.1], + [28.4, 34.2, 29.5, 32.2, 30.1], + [26.1, 28.3, 24.3, 26.2, 27.8]) + extreme_size = ([24.5, 23.5, 26.4], + [28.4, 34.2, 29.5, 32.2, 30.1, 28.4, 34.2, 29.5, 32.2, + 30.1], + [26.1, 28.3, 24.3, 26.2, 27.8]) + + sas_same_size = """ + Comparison LowerCL Difference UpperCL Significance + 2 - 3 0.6908830568 4.34 7.989116943 1 + 2 - 1 0.9508830568 4.6 8.249116943 1 + 3 - 2 -7.989116943 -4.34 -0.6908830568 1 + 3 - 1 -3.389116943 0.26 3.909116943 0 + 1 - 2 -8.249116943 -4.6 -0.9508830568 1 + 1 - 3 -3.909116943 -0.26 3.389116943 0 + """ + + sas_diff_size = """ + Comparison LowerCL Difference UpperCL Significance + 2 - 1 0.2679292645 3.645 7.022070736 1 + 2 - 3 0.5934764007 4.34 8.086523599 1 + 1 - 2 -7.022070736 -3.645 -0.2679292645 1 + 1 - 3 -2.682070736 0.695 4.072070736 0 + 3 - 2 -8.086523599 -4.34 -0.5934764007 1 + 3 - 1 -4.072070736 -0.695 2.682070736 0 + """ + + sas_extreme = """ + Comparison LowerCL Difference UpperCL Significance + 2 - 3 1.561605075 4.34 7.118394925 1 + 2 - 1 2.740784879 6.08 9.419215121 1 + 3 - 2 -7.118394925 -4.34 -1.561605075 1 + 3 - 1 -1.964526566 1.74 5.444526566 0 + 1 - 2 -9.419215121 -6.08 -2.740784879 1 + 1 - 3 -5.444526566 -1.74 1.964526566 0 + """ + + @pytest.mark.parametrize("data,res_expect_str,atol", + ((data_same_size, sas_same_size, 1e-4), + (data_diff_size, sas_diff_size, 1e-4), + (extreme_size, sas_extreme, 1e-10), + ), + ids=["equal size sample", + "unequal sample size", + "extreme sample size differences"]) + def test_compare_sas(self, data, res_expect_str, atol): + ''' + SAS code used to generate results for each sample: + DATA ACHE; + INPUT BRAND RELIEF; + CARDS; + 1 24.5 + ... + 3 27.8 + ; + ods graphics on; ODS RTF;ODS LISTING CLOSE; + PROC ANOVA DATA=ACHE; + CLASS BRAND; + MODEL RELIEF=BRAND; + MEANS BRAND/TUKEY CLDIFF; + TITLE 'COMPARE RELIEF ACROSS MEDICINES - ANOVA EXAMPLE'; + ods output CLDiffs =tc; + proc print data=tc; + format LowerCL 17.16 UpperCL 17.16 Difference 17.16; + title "Output with many digits"; + RUN; + QUIT; + ODS RTF close; + ODS LISTING; + ''' + res_expect = np.asarray(res_expect_str.replace(" - ", " ").split()[5:], + dtype=float).reshape((6, 6)) + res_tukey = stats.tukey_hsd(*data) + conf = res_tukey.confidence_interval() + # loop over the comparisons + for i, j, l, s, h, sig in res_expect: + i, j = int(i) - 1, int(j) - 1 + assert_allclose(conf.low[i, j], l, atol=atol) + assert_allclose(res_tukey.statistic[i, j], s, atol=atol) + assert_allclose(conf.high[i, j], h, atol=atol) + assert_allclose((res_tukey.pvalue[i, j] <= .05), sig == 1) + + matlab_sm_siz = """ + 1 2 -8.2491590248597 -4.6 -0.9508409751403 0.0144483269098 + 1 3 -3.9091590248597 -0.26 3.3891590248597 0.9803107240900 + 2 3 0.6908409751403 4.34 7.9891590248597 0.0203311368795 + """ + + matlab_diff_sz = """ + 1 2 -7.02207069748501 -3.645 -0.26792930251500 0.03371498443080 + 1 3 -2.68207069748500 0.695 4.07207069748500 0.85572267328807 + 2 3 0.59347644287720 4.34 8.08652355712281 0.02259047020620 + """ + + @pytest.mark.parametrize("data,res_expect_str,atol", + ((data_same_size, matlab_sm_siz, 1e-12), + (data_diff_size, matlab_diff_sz, 1e-7)), + ids=["equal size sample", + "unequal size sample"]) + def test_compare_matlab(self, data, res_expect_str, atol): + """ + vals = [24.5, 23.5, 26.4, 27.1, 29.9, 28.4, 34.2, 29.5, 32.2, 30.1, + 26.1, 28.3, 24.3, 26.2, 27.8] + names = {'zero', 'zero', 'zero', 'zero', 'zero', 'one', 'one', 'one', + 'one', 'one', 'two', 'two', 'two', 'two', 'two'} + [p,t,stats] = anova1(vals,names,"off"); + [c,m,h,nms] = multcompare(stats, "CType","hsd"); + """ + res_expect = np.asarray(res_expect_str.split(), + dtype=float).reshape((3, 6)) + res_tukey = stats.tukey_hsd(*data) + conf = res_tukey.confidence_interval() + # loop over the comparisons + for i, j, l, s, h, p in res_expect: + i, j = int(i) - 1, int(j) - 1 + assert_allclose(conf.low[i, j], l, atol=atol) + assert_allclose(res_tukey.statistic[i, j], s, atol=atol) + assert_allclose(conf.high[i, j], h, atol=atol) + assert_allclose(res_tukey.pvalue[i, j], p, atol=atol) + + def test_compare_r(self): + """ + Testing against results and p-values from R: + from: https://www.rdocumentation.org/packages/stats/versions/3.6.2/ + topics/TukeyHSD + > require(graphics) + > summary(fm1 <- aov(breaks ~ tension, data = warpbreaks)) + > TukeyHSD(fm1, "tension", ordered = TRUE) + > plot(TukeyHSD(fm1, "tension")) + Tukey multiple comparisons of means + 95% family-wise confidence level + factor levels have been ordered + Fit: aov(formula = breaks ~ tension, data = warpbreaks) + $tension + """ + str_res = """ + diff lwr upr p adj + 2 - 3 4.722222 -4.8376022 14.28205 0.4630831 + 1 - 3 14.722222 5.1623978 24.28205 0.0014315 + 1 - 2 10.000000 0.4401756 19.55982 0.0384598 + """ + res_expect = np.asarray(str_res.replace(" - ", " ").split()[5:], + dtype=float).reshape((3, 6)) + data = ([26, 30, 54, 25, 70, 52, 51, 26, 67, + 27, 14, 29, 19, 29, 31, 41, 20, 44], + [18, 21, 29, 17, 12, 18, 35, 30, 36, + 42, 26, 19, 16, 39, 28, 21, 39, 29], + [36, 21, 24, 18, 10, 43, 28, 15, 26, + 20, 21, 24, 17, 13, 15, 15, 16, 28]) + + res_tukey = stats.tukey_hsd(*data) + conf = res_tukey.confidence_interval() + # loop over the comparisons + for i, j, s, l, h, p in res_expect: + i, j = int(i) - 1, int(j) - 1 + # atols are set to the number of digits present in the r result. + assert_allclose(conf.low[i, j], l, atol=1e-7) + assert_allclose(res_tukey.statistic[i, j], s, atol=1e-6) + assert_allclose(conf.high[i, j], h, atol=1e-5) + assert_allclose(res_tukey.pvalue[i, j], p, atol=1e-7) + + def test_engineering_stat_handbook(self): + ''' + Example sourced from: + https://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm + ''' + group1 = [6.9, 5.4, 5.8, 4.6, 4.0] + group2 = [8.3, 6.8, 7.8, 9.2, 6.5] + group3 = [8.0, 10.5, 8.1, 6.9, 9.3] + group4 = [5.8, 3.8, 6.1, 5.6, 6.2] + res = stats.tukey_hsd(group1, group2, group3, group4) + conf = res.confidence_interval() + lower = np.asarray([ + [0, 0, 0, -2.25], + [.29, 0, -2.93, .13], + [1.13, 0, 0, .97], + [0, 0, 0, 0]]) + upper = np.asarray([ + [0, 0, 0, 1.93], + [4.47, 0, 1.25, 4.31], + [5.31, 0, 0, 5.15], + [0, 0, 0, 0]]) + + for (i, j) in [(1, 0), (2, 0), (0, 3), (1, 2), (2, 3)]: + assert_allclose(conf.low[i, j], lower[i, j], atol=1e-2) + assert_allclose(conf.high[i, j], upper[i, j], atol=1e-2) + + def test_rand_symm(self): + # test some expected identities of the results + np.random.seed(1234) + data = np.random.rand(3, 100) + res = stats.tukey_hsd(*data) + conf = res.confidence_interval() + # the confidence intervals should be negated symmetric of each other + assert_equal(conf.low, -conf.high.T) + # the `high` and `low` center diagonals should be the same since the + # mean difference in a self comparison is 0. + assert_equal(np.diagonal(conf.high), conf.high[0, 0]) + assert_equal(np.diagonal(conf.low), conf.low[0, 0]) + # statistic array should be antisymmetric with zeros on the diagonal + assert_equal(res.statistic, -res.statistic.T) + assert_equal(np.diagonal(res.statistic), 0) + # p-values should be symmetric and 1 when compared to itself + assert_equal(res.pvalue, res.pvalue.T) + assert_equal(np.diagonal(res.pvalue), 1) + + def test_no_inf(self): + with assert_raises(ValueError, match="...must be finite."): + stats.tukey_hsd([1, 2, 3], [2, np.inf], [6, 7, 3]) + + def test_is_1d(self): + with assert_raises(ValueError, match="...must be one-dimensional"): + stats.tukey_hsd([[1, 2], [2, 3]], [2, 5], [5, 23, 6]) + + def test_no_empty(self): + with assert_raises(ValueError, match="...must be greater than one"): + stats.tukey_hsd([], [2, 5], [4, 5, 6]) + + @pytest.mark.parametrize("nargs", (0, 1)) + def test_not_enough_treatments(self, nargs): + with assert_raises(ValueError, match="...more than 1 treatment."): + stats.tukey_hsd(*([[23, 7, 3]] * nargs)) + + @pytest.mark.parametrize("cl", [-.5, 0, 1, 2]) + def test_conf_level_invalid(self, cl): + with assert_raises(ValueError, match="must be between 0 and 1"): + r = stats.tukey_hsd([23, 7, 3], [3, 4], [9, 4]) + r.confidence_interval(cl) + + def test_2_args_ttest(self): + # that with 2 treatments the `pvalue` is equal to that of `ttest_ind` + res_tukey = stats.tukey_hsd(*self.data_diff_size[:2]) + res_ttest = stats.ttest_ind(*self.data_diff_size[:2]) + assert_allclose(res_ttest.pvalue, res_tukey.pvalue[0, 1]) + assert_allclose(res_ttest.pvalue, res_tukey.pvalue[1, 0]) + + +class TestPoissonMeansTest: + @pytest.mark.parametrize("c1, n1, c2, n2, p_expect", ( + # example from [1], 6. Illustrative examples: Example 1 + [0, 100, 3, 100, 0.0884], + [2, 100, 6, 100, 0.1749] + )) + def test_paper_examples(self, c1, n1, c2, n2, p_expect): + res = stats.poisson_means_test(c1, n1, c2, n2) + assert_allclose(res.pvalue, p_expect, atol=1e-4) + + @pytest.mark.parametrize("c1, n1, c2, n2, p_expect, alt, d", ( + # These test cases are produced by the wrapped fortran code from the + # original authors. Using a slightly modified version of this fortran, + # found here, https://github.com/nolanbconaway/poisson-etest, + # additional tests were created. + [20, 10, 20, 10, 0.9999997568929630, 'two-sided', 0], + [10, 10, 10, 10, 0.9999998403241203, 'two-sided', 0], + [50, 15, 1, 1, 0.09920321053409643, 'two-sided', .05], + [3, 100, 20, 300, 0.12202725450896404, 'two-sided', 0], + [3, 12, 4, 20, 0.40416087318539173, 'greater', 0], + [4, 20, 3, 100, 0.008053640402974236, 'greater', 0], + # publishing paper does not include a `less` alternative, + # so it was calculated with switched argument order and + # alternative="greater" + [4, 20, 3, 10, 0.3083216325432898, 'less', 0], + [1, 1, 50, 15, 0.09322998607245102, 'less', 0] + )) + def test_fortran_authors(self, c1, n1, c2, n2, p_expect, alt, d): + res = stats.poisson_means_test(c1, n1, c2, n2, alternative=alt, diff=d) + assert_allclose(res.pvalue, p_expect, atol=2e-6, rtol=1e-16) + + def test_different_results(self): + # The implementation in Fortran is known to break down at higher + # counts and observations, so we expect different results. By + # inspection we can infer the p-value to be near one. + count1, count2 = 10000, 10000 + nobs1, nobs2 = 10000, 10000 + res = stats.poisson_means_test(count1, nobs1, count2, nobs2) + assert_allclose(res.pvalue, 1) + + def test_less_than_zero_lambda_hat2(self): + # demonstrates behavior that fixes a known fault from original Fortran. + # p-value should clearly be near one. + count1, count2 = 0, 0 + nobs1, nobs2 = 1, 1 + res = stats.poisson_means_test(count1, nobs1, count2, nobs2) + assert_allclose(res.pvalue, 1) + + def test_input_validation(self): + count1, count2 = 0, 0 + nobs1, nobs2 = 1, 1 + + # test non-integral events + message = '`k1` and `k2` must be integers.' + with assert_raises(TypeError, match=message): + stats.poisson_means_test(.7, nobs1, count2, nobs2) + with assert_raises(TypeError, match=message): + stats.poisson_means_test(count1, nobs1, .7, nobs2) + + # test negative events + message = '`k1` and `k2` must be greater than or equal to 0.' + with assert_raises(ValueError, match=message): + stats.poisson_means_test(-1, nobs1, count2, nobs2) + with assert_raises(ValueError, match=message): + stats.poisson_means_test(count1, nobs1, -1, nobs2) + + # test negative sample size + message = '`n1` and `n2` must be greater than 0.' + with assert_raises(ValueError, match=message): + stats.poisson_means_test(count1, -1, count2, nobs2) + with assert_raises(ValueError, match=message): + stats.poisson_means_test(count1, nobs1, count2, -1) + + # test negative difference + message = 'diff must be greater than or equal to 0.' + with assert_raises(ValueError, match=message): + stats.poisson_means_test(count1, nobs1, count2, nobs2, diff=-1) + + # test invalid alternatvie + message = 'Alternative must be one of ...' + with assert_raises(ValueError, match=message): + stats.poisson_means_test(1, 2, 1, 2, alternative='error') + + +class TestBWSTest: + + def test_bws_input_validation(self): + rng = np.random.default_rng(4571775098104213308) + + x, y = rng.random(size=(2, 7)) + + message = '`x` and `y` must be exactly one-dimensional.' + with pytest.raises(ValueError, match=message): + stats.bws_test([x, x], [y, y]) + + message = '`x` and `y` must not contain NaNs.' + with pytest.raises(ValueError, match=message): + stats.bws_test([np.nan], y) + + message = '`x` and `y` must be of nonzero size.' + with pytest.raises(ValueError, match=message): + stats.bws_test(x, []) + + message = 'alternative` must be one of...' + with pytest.raises(ValueError, match=message): + stats.bws_test(x, y, alternative='ekki-ekki') + + message = 'method` must be an instance of...' + with pytest.raises(ValueError, match=message): + stats.bws_test(x, y, method=42) + + + def test_against_published_reference(self): + # Test against Example 2 in bws_test Reference [1], pg 9 + # https://link.springer.com/content/pdf/10.1007/BF02762032.pdf + x = [1, 2, 3, 4, 6, 7, 8] + y = [5, 9, 10, 11, 12, 13, 14] + res = stats.bws_test(x, y, alternative='two-sided') + assert_allclose(res.statistic, 5.132, atol=1e-3) + assert_equal(res.pvalue, 10/3432) + + + @pytest.mark.parametrize(('alternative', 'statistic', 'pvalue'), + [('two-sided', 1.7510204081633, 0.1264422777777), + ('less', -1.7510204081633, 0.05754662004662), + ('greater', -1.7510204081633, 0.9424533799534)]) + def test_against_R(self, alternative, statistic, pvalue): + # Test against R library BWStest function bws_test + # library(BWStest) + # options(digits=16) + # x = c(...) + # y = c(...) + # bws_test(x, y, alternative='two.sided') + rng = np.random.default_rng(4571775098104213308) + x, y = rng.random(size=(2, 7)) + res = stats.bws_test(x, y, alternative=alternative) + assert_allclose(res.statistic, statistic, rtol=1e-13) + assert_allclose(res.pvalue, pvalue, atol=1e-2, rtol=1e-1) + + @pytest.mark.parametrize(('alternative', 'statistic', 'pvalue'), + [('two-sided', 1.142629265891, 0.2903950180801), + ('less', 0.99629665877411, 0.8545660222131), + ('greater', 0.99629665877411, 0.1454339777869)]) + def test_against_R_imbalanced(self, alternative, statistic, pvalue): + # Test against R library BWStest function bws_test + # library(BWStest) + # options(digits=16) + # x = c(...) + # y = c(...) + # bws_test(x, y, alternative='two.sided') + rng = np.random.default_rng(5429015622386364034) + x = rng.random(size=9) + y = rng.random(size=8) + res = stats.bws_test(x, y, alternative=alternative) + assert_allclose(res.statistic, statistic, rtol=1e-13) + assert_allclose(res.pvalue, pvalue, atol=1e-2, rtol=1e-1) + + def test_method(self): + # Test that `method` parameter has the desired effect + rng = np.random.default_rng(1520514347193347862) + x, y = rng.random(size=(2, 10)) + + rng = np.random.default_rng(1520514347193347862) + method = stats.PermutationMethod(n_resamples=10, random_state=rng) + res1 = stats.bws_test(x, y, method=method) + + assert len(res1.null_distribution) == 10 + + rng = np.random.default_rng(1520514347193347862) + method = stats.PermutationMethod(n_resamples=10, random_state=rng) + res2 = stats.bws_test(x, y, method=method) + + assert_allclose(res1.null_distribution, res2.null_distribution) + + rng = np.random.default_rng(5205143471933478621) + method = stats.PermutationMethod(n_resamples=10, random_state=rng) + res3 = stats.bws_test(x, y, method=method) + + assert not np.allclose(res3.null_distribution, res1.null_distribution) + + def test_directions(self): + # Sanity check of the sign of the one-sided statistic + rng = np.random.default_rng(1520514347193347862) + x = rng.random(size=5) + y = x - 1 + + res = stats.bws_test(x, y, alternative='greater') + assert res.statistic > 0 + assert_equal(res.pvalue, 1 / len(res.null_distribution)) + + res = stats.bws_test(x, y, alternative='less') + assert res.statistic > 0 + assert_equal(res.pvalue, 1) + + res = stats.bws_test(y, x, alternative='less') + assert res.statistic < 0 + assert_equal(res.pvalue, 1 / len(res.null_distribution)) + + res = stats.bws_test(y, x, alternative='greater') + assert res.statistic < 0 + assert_equal(res.pvalue, 1) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_kdeoth.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_kdeoth.py new file mode 100644 index 0000000000000000000000000000000000000000..dec6fd65a19d0b8ac17456a172d6dba324445353 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_kdeoth.py @@ -0,0 +1,608 @@ +from scipy import stats, linalg, integrate +import numpy as np +from numpy.testing import (assert_almost_equal, assert_, assert_equal, + assert_array_almost_equal, + assert_array_almost_equal_nulp, assert_allclose) +import pytest +from pytest import raises as assert_raises + + +def test_kde_1d(): + #some basic tests comparing to normal distribution + np.random.seed(8765678) + n_basesample = 500 + xn = np.random.randn(n_basesample) + xnmean = xn.mean() + xnstd = xn.std(ddof=1) + + # get kde for original sample + gkde = stats.gaussian_kde(xn) + + # evaluate the density function for the kde for some points + xs = np.linspace(-7,7,501) + kdepdf = gkde.evaluate(xs) + normpdf = stats.norm.pdf(xs, loc=xnmean, scale=xnstd) + intervall = xs[1] - xs[0] + + assert_(np.sum((kdepdf - normpdf)**2)*intervall < 0.01) + prob1 = gkde.integrate_box_1d(xnmean, np.inf) + prob2 = gkde.integrate_box_1d(-np.inf, xnmean) + assert_almost_equal(prob1, 0.5, decimal=1) + assert_almost_equal(prob2, 0.5, decimal=1) + assert_almost_equal(gkde.integrate_box(xnmean, np.inf), prob1, decimal=13) + assert_almost_equal(gkde.integrate_box(-np.inf, xnmean), prob2, decimal=13) + + assert_almost_equal(gkde.integrate_kde(gkde), + (kdepdf**2).sum()*intervall, decimal=2) + assert_almost_equal(gkde.integrate_gaussian(xnmean, xnstd**2), + (kdepdf*normpdf).sum()*intervall, decimal=2) + + +def test_kde_1d_weighted(): + #some basic tests comparing to normal distribution + np.random.seed(8765678) + n_basesample = 500 + xn = np.random.randn(n_basesample) + wn = np.random.rand(n_basesample) + xnmean = np.average(xn, weights=wn) + xnstd = np.sqrt(np.average((xn-xnmean)**2, weights=wn)) + + # get kde for original sample + gkde = stats.gaussian_kde(xn, weights=wn) + + # evaluate the density function for the kde for some points + xs = np.linspace(-7,7,501) + kdepdf = gkde.evaluate(xs) + normpdf = stats.norm.pdf(xs, loc=xnmean, scale=xnstd) + intervall = xs[1] - xs[0] + + assert_(np.sum((kdepdf - normpdf)**2)*intervall < 0.01) + prob1 = gkde.integrate_box_1d(xnmean, np.inf) + prob2 = gkde.integrate_box_1d(-np.inf, xnmean) + assert_almost_equal(prob1, 0.5, decimal=1) + assert_almost_equal(prob2, 0.5, decimal=1) + assert_almost_equal(gkde.integrate_box(xnmean, np.inf), prob1, decimal=13) + assert_almost_equal(gkde.integrate_box(-np.inf, xnmean), prob2, decimal=13) + + assert_almost_equal(gkde.integrate_kde(gkde), + (kdepdf**2).sum()*intervall, decimal=2) + assert_almost_equal(gkde.integrate_gaussian(xnmean, xnstd**2), + (kdepdf*normpdf).sum()*intervall, decimal=2) + + +@pytest.mark.slow +def test_kde_2d(): + #some basic tests comparing to normal distribution + np.random.seed(8765678) + n_basesample = 500 + + mean = np.array([1.0, 3.0]) + covariance = np.array([[1.0, 2.0], [2.0, 6.0]]) + + # Need transpose (shape (2, 500)) for kde + xn = np.random.multivariate_normal(mean, covariance, size=n_basesample).T + + # get kde for original sample + gkde = stats.gaussian_kde(xn) + + # evaluate the density function for the kde for some points + x, y = np.mgrid[-7:7:500j, -7:7:500j] + grid_coords = np.vstack([x.ravel(), y.ravel()]) + kdepdf = gkde.evaluate(grid_coords) + kdepdf = kdepdf.reshape(500, 500) + + normpdf = stats.multivariate_normal.pdf(np.dstack([x, y]), + mean=mean, cov=covariance) + intervall = y.ravel()[1] - y.ravel()[0] + + assert_(np.sum((kdepdf - normpdf)**2) * (intervall**2) < 0.01) + + small = -1e100 + large = 1e100 + prob1 = gkde.integrate_box([small, mean[1]], [large, large]) + prob2 = gkde.integrate_box([small, small], [large, mean[1]]) + + assert_almost_equal(prob1, 0.5, decimal=1) + assert_almost_equal(prob2, 0.5, decimal=1) + assert_almost_equal(gkde.integrate_kde(gkde), + (kdepdf**2).sum()*(intervall**2), decimal=2) + assert_almost_equal(gkde.integrate_gaussian(mean, covariance), + (kdepdf*normpdf).sum()*(intervall**2), decimal=2) + + +@pytest.mark.slow +def test_kde_2d_weighted(): + #some basic tests comparing to normal distribution + np.random.seed(8765678) + n_basesample = 500 + + mean = np.array([1.0, 3.0]) + covariance = np.array([[1.0, 2.0], [2.0, 6.0]]) + + # Need transpose (shape (2, 500)) for kde + xn = np.random.multivariate_normal(mean, covariance, size=n_basesample).T + wn = np.random.rand(n_basesample) + + # get kde for original sample + gkde = stats.gaussian_kde(xn, weights=wn) + + # evaluate the density function for the kde for some points + x, y = np.mgrid[-7:7:500j, -7:7:500j] + grid_coords = np.vstack([x.ravel(), y.ravel()]) + kdepdf = gkde.evaluate(grid_coords) + kdepdf = kdepdf.reshape(500, 500) + + normpdf = stats.multivariate_normal.pdf(np.dstack([x, y]), + mean=mean, cov=covariance) + intervall = y.ravel()[1] - y.ravel()[0] + + assert_(np.sum((kdepdf - normpdf)**2) * (intervall**2) < 0.01) + + small = -1e100 + large = 1e100 + prob1 = gkde.integrate_box([small, mean[1]], [large, large]) + prob2 = gkde.integrate_box([small, small], [large, mean[1]]) + + assert_almost_equal(prob1, 0.5, decimal=1) + assert_almost_equal(prob2, 0.5, decimal=1) + assert_almost_equal(gkde.integrate_kde(gkde), + (kdepdf**2).sum()*(intervall**2), decimal=2) + assert_almost_equal(gkde.integrate_gaussian(mean, covariance), + (kdepdf*normpdf).sum()*(intervall**2), decimal=2) + + +def test_kde_bandwidth_method(): + def scotts_factor(kde_obj): + """Same as default, just check that it works.""" + return np.power(kde_obj.n, -1./(kde_obj.d+4)) + + np.random.seed(8765678) + n_basesample = 50 + xn = np.random.randn(n_basesample) + + # Default + gkde = stats.gaussian_kde(xn) + # Supply a callable + gkde2 = stats.gaussian_kde(xn, bw_method=scotts_factor) + # Supply a scalar + gkde3 = stats.gaussian_kde(xn, bw_method=gkde.factor) + + xs = np.linspace(-7,7,51) + kdepdf = gkde.evaluate(xs) + kdepdf2 = gkde2.evaluate(xs) + assert_almost_equal(kdepdf, kdepdf2) + kdepdf3 = gkde3.evaluate(xs) + assert_almost_equal(kdepdf, kdepdf3) + + assert_raises(ValueError, stats.gaussian_kde, xn, bw_method='wrongstring') + + +def test_kde_bandwidth_method_weighted(): + def scotts_factor(kde_obj): + """Same as default, just check that it works.""" + return np.power(kde_obj.neff, -1./(kde_obj.d+4)) + + np.random.seed(8765678) + n_basesample = 50 + xn = np.random.randn(n_basesample) + + # Default + gkde = stats.gaussian_kde(xn) + # Supply a callable + gkde2 = stats.gaussian_kde(xn, bw_method=scotts_factor) + # Supply a scalar + gkde3 = stats.gaussian_kde(xn, bw_method=gkde.factor) + + xs = np.linspace(-7,7,51) + kdepdf = gkde.evaluate(xs) + kdepdf2 = gkde2.evaluate(xs) + assert_almost_equal(kdepdf, kdepdf2) + kdepdf3 = gkde3.evaluate(xs) + assert_almost_equal(kdepdf, kdepdf3) + + assert_raises(ValueError, stats.gaussian_kde, xn, bw_method='wrongstring') + + +# Subclasses that should stay working (extracted from various sources). +# Unfortunately the earlier design of gaussian_kde made it necessary for users +# to create these kinds of subclasses, or call _compute_covariance() directly. + +class _kde_subclass1(stats.gaussian_kde): + def __init__(self, dataset): + self.dataset = np.atleast_2d(dataset) + self.d, self.n = self.dataset.shape + self.covariance_factor = self.scotts_factor + self._compute_covariance() + + +class _kde_subclass2(stats.gaussian_kde): + def __init__(self, dataset): + self.covariance_factor = self.scotts_factor + super().__init__(dataset) + + +class _kde_subclass4(stats.gaussian_kde): + def covariance_factor(self): + return 0.5 * self.silverman_factor() + + +def test_gaussian_kde_subclassing(): + x1 = np.array([-7, -5, 1, 4, 5], dtype=float) + xs = np.linspace(-10, 10, num=50) + + # gaussian_kde itself + kde = stats.gaussian_kde(x1) + ys = kde(xs) + + # subclass 1 + kde1 = _kde_subclass1(x1) + y1 = kde1(xs) + assert_array_almost_equal_nulp(ys, y1, nulp=10) + + # subclass 2 + kde2 = _kde_subclass2(x1) + y2 = kde2(xs) + assert_array_almost_equal_nulp(ys, y2, nulp=10) + + # subclass 3 was removed because we have no obligation to maintain support + # for user invocation of private methods + + # subclass 4 + kde4 = _kde_subclass4(x1) + y4 = kde4(x1) + y_expected = [0.06292987, 0.06346938, 0.05860291, 0.08657652, 0.07904017] + + assert_array_almost_equal(y_expected, y4, decimal=6) + + # Not a subclass, but check for use of _compute_covariance() + kde5 = kde + kde5.covariance_factor = lambda: kde.factor + kde5._compute_covariance() + y5 = kde5(xs) + assert_array_almost_equal_nulp(ys, y5, nulp=10) + + +def test_gaussian_kde_covariance_caching(): + x1 = np.array([-7, -5, 1, 4, 5], dtype=float) + xs = np.linspace(-10, 10, num=5) + # These expected values are from scipy 0.10, before some changes to + # gaussian_kde. They were not compared with any external reference. + y_expected = [0.02463386, 0.04689208, 0.05395444, 0.05337754, 0.01664475] + + # Set the bandwidth, then reset it to the default. + kde = stats.gaussian_kde(x1) + kde.set_bandwidth(bw_method=0.5) + kde.set_bandwidth(bw_method='scott') + y2 = kde(xs) + + assert_array_almost_equal(y_expected, y2, decimal=7) + + +def test_gaussian_kde_monkeypatch(): + """Ugly, but people may rely on this. See scipy pull request 123, + specifically the linked ML thread "Width of the Gaussian in stats.kde". + If it is necessary to break this later on, that is to be discussed on ML. + """ + x1 = np.array([-7, -5, 1, 4, 5], dtype=float) + xs = np.linspace(-10, 10, num=50) + + # The old monkeypatched version to get at Silverman's Rule. + kde = stats.gaussian_kde(x1) + kde.covariance_factor = kde.silverman_factor + kde._compute_covariance() + y1 = kde(xs) + + # The new saner version. + kde2 = stats.gaussian_kde(x1, bw_method='silverman') + y2 = kde2(xs) + + assert_array_almost_equal_nulp(y1, y2, nulp=10) + + +def test_kde_integer_input(): + """Regression test for #1181.""" + x1 = np.arange(5) + kde = stats.gaussian_kde(x1) + y_expected = [0.13480721, 0.18222869, 0.19514935, 0.18222869, 0.13480721] + assert_array_almost_equal(kde(x1), y_expected, decimal=6) + + +_ftypes = ['float32', 'float64', 'float96', 'float128', 'int32', 'int64'] + + +@pytest.mark.parametrize("bw_type", _ftypes + ["scott", "silverman"]) +@pytest.mark.parametrize("dtype", _ftypes) +def test_kde_output_dtype(dtype, bw_type): + # Check whether the datatypes are available + dtype = getattr(np, dtype, None) + + if bw_type in ["scott", "silverman"]: + bw = bw_type + else: + bw_type = getattr(np, bw_type, None) + bw = bw_type(3) if bw_type else None + + if any(dt is None for dt in [dtype, bw]): + pytest.skip() + + weights = np.arange(5, dtype=dtype) + dataset = np.arange(5, dtype=dtype) + k = stats.gaussian_kde(dataset, bw_method=bw, weights=weights) + points = np.arange(5, dtype=dtype) + result = k(points) + # weights are always cast to float64 + assert result.dtype == np.result_type(dataset, points, np.float64(weights), + k.factor) + + +def test_pdf_logpdf_validation(): + rng = np.random.default_rng(64202298293133848336925499069837723291) + xn = rng.standard_normal((2, 10)) + gkde = stats.gaussian_kde(xn) + xs = rng.standard_normal((3, 10)) + + msg = "points have dimension 3, dataset has dimension 2" + with pytest.raises(ValueError, match=msg): + gkde.logpdf(xs) + + +def test_pdf_logpdf(): + np.random.seed(1) + n_basesample = 50 + xn = np.random.randn(n_basesample) + + # Default + gkde = stats.gaussian_kde(xn) + + xs = np.linspace(-15, 12, 25) + pdf = gkde.evaluate(xs) + pdf2 = gkde.pdf(xs) + assert_almost_equal(pdf, pdf2, decimal=12) + + logpdf = np.log(pdf) + logpdf2 = gkde.logpdf(xs) + assert_almost_equal(logpdf, logpdf2, decimal=12) + + # There are more points than data + gkde = stats.gaussian_kde(xs) + pdf = np.log(gkde.evaluate(xn)) + pdf2 = gkde.logpdf(xn) + assert_almost_equal(pdf, pdf2, decimal=12) + + +def test_pdf_logpdf_weighted(): + np.random.seed(1) + n_basesample = 50 + xn = np.random.randn(n_basesample) + wn = np.random.rand(n_basesample) + + # Default + gkde = stats.gaussian_kde(xn, weights=wn) + + xs = np.linspace(-15, 12, 25) + pdf = gkde.evaluate(xs) + pdf2 = gkde.pdf(xs) + assert_almost_equal(pdf, pdf2, decimal=12) + + logpdf = np.log(pdf) + logpdf2 = gkde.logpdf(xs) + assert_almost_equal(logpdf, logpdf2, decimal=12) + + # There are more points than data + gkde = stats.gaussian_kde(xs, weights=np.random.rand(len(xs))) + pdf = np.log(gkde.evaluate(xn)) + pdf2 = gkde.logpdf(xn) + assert_almost_equal(pdf, pdf2, decimal=12) + + +def test_marginal_1_axis(): + rng = np.random.default_rng(6111799263660870475) + n_data = 50 + n_dim = 10 + dataset = rng.normal(size=(n_dim, n_data)) + points = rng.normal(size=(n_dim, 3)) + + dimensions = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) # dimensions to keep + + kde = stats.gaussian_kde(dataset) + marginal = kde.marginal(dimensions) + pdf = marginal.pdf(points[dimensions]) + + def marginal_pdf_single(point): + def f(x): + x = np.concatenate(([x], point[dimensions])) + return kde.pdf(x)[0] + return integrate.quad(f, -np.inf, np.inf)[0] + + def marginal_pdf(points): + return np.apply_along_axis(marginal_pdf_single, axis=0, arr=points) + + ref = marginal_pdf(points) + + assert_allclose(pdf, ref, rtol=1e-6) + + +@pytest.mark.xslow +def test_marginal_2_axis(): + rng = np.random.default_rng(6111799263660870475) + n_data = 30 + n_dim = 4 + dataset = rng.normal(size=(n_dim, n_data)) + points = rng.normal(size=(n_dim, 3)) + + dimensions = np.array([1, 3]) # dimensions to keep + + kde = stats.gaussian_kde(dataset) + marginal = kde.marginal(dimensions) + pdf = marginal.pdf(points[dimensions]) + + def marginal_pdf(points): + def marginal_pdf_single(point): + def f(y, x): + w, z = point[dimensions] + x = np.array([x, w, y, z]) + return kde.pdf(x)[0] + return integrate.dblquad(f, -np.inf, np.inf, -np.inf, np.inf)[0] + + return np.apply_along_axis(marginal_pdf_single, axis=0, arr=points) + + ref = marginal_pdf(points) + + assert_allclose(pdf, ref, rtol=1e-6) + + +def test_marginal_iv(): + # test input validation + rng = np.random.default_rng(6111799263660870475) + n_data = 30 + n_dim = 4 + dataset = rng.normal(size=(n_dim, n_data)) + points = rng.normal(size=(n_dim, 3)) + + kde = stats.gaussian_kde(dataset) + + # check that positive and negative indices are equivalent + dimensions1 = [-1, 1] + marginal1 = kde.marginal(dimensions1) + pdf1 = marginal1.pdf(points[dimensions1]) + + dimensions2 = [3, -3] + marginal2 = kde.marginal(dimensions2) + pdf2 = marginal2.pdf(points[dimensions2]) + + assert_equal(pdf1, pdf2) + + # IV for non-integer dimensions + message = "Elements of `dimensions` must be integers..." + with pytest.raises(ValueError, match=message): + kde.marginal([1, 2.5]) + + # IV for uniquenes + message = "All elements of `dimensions` must be unique." + with pytest.raises(ValueError, match=message): + kde.marginal([1, 2, 2]) + + # IV for non-integer dimensions + message = (r"Dimensions \[-5 6\] are invalid for a distribution in 4...") + with pytest.raises(ValueError, match=message): + kde.marginal([1, -5, 6]) + + +@pytest.mark.xslow +def test_logpdf_overflow(): + # regression test for gh-12988; testing against linalg instability for + # very high dimensionality kde + np.random.seed(1) + n_dimensions = 2500 + n_samples = 5000 + xn = np.array([np.random.randn(n_samples) + (n) for n in range( + 0, n_dimensions)]) + + # Default + gkde = stats.gaussian_kde(xn) + + logpdf = gkde.logpdf(np.arange(0, n_dimensions)) + np.testing.assert_equal(np.isneginf(logpdf[0]), False) + np.testing.assert_equal(np.isnan(logpdf[0]), False) + + +def test_weights_intact(): + # regression test for gh-9709: weights are not modified + np.random.seed(12345) + vals = np.random.lognormal(size=100) + weights = np.random.choice([1.0, 10.0, 100], size=vals.size) + orig_weights = weights.copy() + + stats.gaussian_kde(np.log10(vals), weights=weights) + assert_allclose(weights, orig_weights, atol=1e-14, rtol=1e-14) + + +def test_weights_integer(): + # integer weights are OK, cf gh-9709 (comment) + np.random.seed(12345) + values = [0.2, 13.5, 21.0, 75.0, 99.0] + weights = [1, 2, 4, 8, 16] # a list of integers + pdf_i = stats.gaussian_kde(values, weights=weights) + pdf_f = stats.gaussian_kde(values, weights=np.float64(weights)) + + xn = [0.3, 11, 88] + assert_allclose(pdf_i.evaluate(xn), + pdf_f.evaluate(xn), atol=1e-14, rtol=1e-14) + + +def test_seed(): + # Test the seed option of the resample method + def test_seed_sub(gkde_trail): + n_sample = 200 + # The results should be different without using seed + samp1 = gkde_trail.resample(n_sample) + samp2 = gkde_trail.resample(n_sample) + assert_raises( + AssertionError, assert_allclose, samp1, samp2, atol=1e-13 + ) + # Use integer seed + seed = 831 + samp1 = gkde_trail.resample(n_sample, seed=seed) + samp2 = gkde_trail.resample(n_sample, seed=seed) + assert_allclose(samp1, samp2, atol=1e-13) + # Use RandomState + rstate1 = np.random.RandomState(seed=138) + samp1 = gkde_trail.resample(n_sample, seed=rstate1) + rstate2 = np.random.RandomState(seed=138) + samp2 = gkde_trail.resample(n_sample, seed=rstate2) + assert_allclose(samp1, samp2, atol=1e-13) + + # check that np.random.Generator can be used (numpy >= 1.17) + if hasattr(np.random, 'default_rng'): + # obtain a np.random.Generator object + rng = np.random.default_rng(1234) + gkde_trail.resample(n_sample, seed=rng) + + np.random.seed(8765678) + n_basesample = 500 + wn = np.random.rand(n_basesample) + # Test 1D case + xn_1d = np.random.randn(n_basesample) + + gkde_1d = stats.gaussian_kde(xn_1d) + test_seed_sub(gkde_1d) + gkde_1d_weighted = stats.gaussian_kde(xn_1d, weights=wn) + test_seed_sub(gkde_1d_weighted) + + # Test 2D case + mean = np.array([1.0, 3.0]) + covariance = np.array([[1.0, 2.0], [2.0, 6.0]]) + xn_2d = np.random.multivariate_normal(mean, covariance, size=n_basesample).T + + gkde_2d = stats.gaussian_kde(xn_2d) + test_seed_sub(gkde_2d) + gkde_2d_weighted = stats.gaussian_kde(xn_2d, weights=wn) + test_seed_sub(gkde_2d_weighted) + + +def test_singular_data_covariance_gh10205(): + # When the data lie in a lower-dimensional subspace and this causes + # and exception, check that the error message is informative. + rng = np.random.default_rng(2321583144339784787) + mu = np.array([1, 10, 20]) + sigma = np.array([[4, 10, 0], [10, 25, 0], [0, 0, 100]]) + data = rng.multivariate_normal(mu, sigma, 1000) + try: # doesn't raise any error on some platforms, and that's OK + stats.gaussian_kde(data.T) + except linalg.LinAlgError: + msg = "The data appears to lie in a lower-dimensional subspace..." + with assert_raises(linalg.LinAlgError, match=msg): + stats.gaussian_kde(data.T) + + +def test_fewer_points_than_dimensions_gh17436(): + # When the number of points is fewer than the number of dimensions, the + # the covariance matrix would be singular, and the exception tested in + # test_singular_data_covariance_gh10205 would occur. However, sometimes + # this occurs when the user passes in the transpose of what `gaussian_kde` + # expects. This can result in a huge covariance matrix, so bail early. + rng = np.random.default_rng(2046127537594925772) + rvs = rng.multivariate_normal(np.zeros(3), np.eye(3), size=5) + message = "Number of dimensions is greater than number of samples..." + with pytest.raises(ValueError, match=message): + stats.gaussian_kde(rvs) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_morestats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_morestats.py new file mode 100644 index 0000000000000000000000000000000000000000..86853655a94dc6b0bf3caa52375b08704511f9e1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_morestats.py @@ -0,0 +1,2986 @@ +# Author: Travis Oliphant, 2002 +# +# Further enhancements and tests added by numerous SciPy developers. +# +import warnings +import sys +from functools import partial + +import numpy as np +from numpy.random import RandomState +from numpy.testing import (assert_array_equal, assert_almost_equal, + assert_array_less, assert_array_almost_equal, + assert_, assert_allclose, assert_equal, + suppress_warnings) +import pytest +from pytest import raises as assert_raises +import re +from scipy import optimize, stats, special +from scipy.stats._morestats import _abw_state, _get_As_weibull, _Avals_weibull +from .common_tests import check_named_results +from .._hypotests import _get_wilcoxon_distr, _get_wilcoxon_distr2 +from scipy.stats._binomtest import _binary_search_for_binom_tst +from scipy.stats._distr_params import distcont + +distcont = dict(distcont) # type: ignore + +# Matplotlib is not a scipy dependency but is optionally used in probplot, so +# check if it's available +try: + import matplotlib + matplotlib.rcParams['backend'] = 'Agg' + import matplotlib.pyplot as plt + have_matplotlib = True +except Exception: + have_matplotlib = False + + +# test data gear.dat from NIST for Levene and Bartlett test +# https://www.itl.nist.gov/div898/handbook/eda/section3/eda3581.htm +g1 = [1.006, 0.996, 0.998, 1.000, 0.992, 0.993, 1.002, 0.999, 0.994, 1.000] +g2 = [0.998, 1.006, 1.000, 1.002, 0.997, 0.998, 0.996, 1.000, 1.006, 0.988] +g3 = [0.991, 0.987, 0.997, 0.999, 0.995, 0.994, 1.000, 0.999, 0.996, 0.996] +g4 = [1.005, 1.002, 0.994, 1.000, 0.995, 0.994, 0.998, 0.996, 1.002, 0.996] +g5 = [0.998, 0.998, 0.982, 0.990, 1.002, 0.984, 0.996, 0.993, 0.980, 0.996] +g6 = [1.009, 1.013, 1.009, 0.997, 0.988, 1.002, 0.995, 0.998, 0.981, 0.996] +g7 = [0.990, 1.004, 0.996, 1.001, 0.998, 1.000, 1.018, 1.010, 0.996, 1.002] +g8 = [0.998, 1.000, 1.006, 1.000, 1.002, 0.996, 0.998, 0.996, 1.002, 1.006] +g9 = [1.002, 0.998, 0.996, 0.995, 0.996, 1.004, 1.004, 0.998, 0.999, 0.991] +g10 = [0.991, 0.995, 0.984, 0.994, 0.997, 0.997, 0.991, 0.998, 1.004, 0.997] + + +# The loggamma RVS stream is changing due to gh-13349; this version +# preserves the old stream so that tests don't change. +def _old_loggamma_rvs(*args, **kwargs): + return np.log(stats.gamma.rvs(*args, **kwargs)) + + +class TestBayes_mvs: + def test_basic(self): + # Expected values in this test simply taken from the function. For + # some checks regarding correctness of implementation, see review in + # gh-674 + data = [6, 9, 12, 7, 8, 8, 13] + mean, var, std = stats.bayes_mvs(data) + assert_almost_equal(mean.statistic, 9.0) + assert_allclose(mean.minmax, (7.103650222492964, 10.896349777507034), + rtol=1e-6) + + assert_almost_equal(var.statistic, 10.0) + assert_allclose(var.minmax, (3.1767242068607087, 24.45910381334018), + rtol=1e-09) + + assert_almost_equal(std.statistic, 2.9724954732045084, decimal=14) + assert_allclose(std.minmax, (1.7823367265645145, 4.9456146050146312), + rtol=1e-14) + + def test_empty_input(self): + assert_raises(ValueError, stats.bayes_mvs, []) + + def test_result_attributes(self): + x = np.arange(15) + attributes = ('statistic', 'minmax') + res = stats.bayes_mvs(x) + + for i in res: + check_named_results(i, attributes) + + +class TestMvsdist: + def test_basic(self): + data = [6, 9, 12, 7, 8, 8, 13] + mean, var, std = stats.mvsdist(data) + assert_almost_equal(mean.mean(), 9.0) + assert_allclose(mean.interval(0.9), (7.103650222492964, + 10.896349777507034), rtol=1e-14) + + assert_almost_equal(var.mean(), 10.0) + assert_allclose(var.interval(0.9), (3.1767242068607087, + 24.45910381334018), rtol=1e-09) + + assert_almost_equal(std.mean(), 2.9724954732045084, decimal=14) + assert_allclose(std.interval(0.9), (1.7823367265645145, + 4.9456146050146312), rtol=1e-14) + + def test_empty_input(self): + assert_raises(ValueError, stats.mvsdist, []) + + def test_bad_arg(self): + # Raise ValueError if fewer than two data points are given. + data = [1] + assert_raises(ValueError, stats.mvsdist, data) + + def test_warns(self): + # regression test for gh-5270 + # make sure there are no spurious divide-by-zero warnings + with warnings.catch_warnings(): + warnings.simplefilter('error', RuntimeWarning) + [x.mean() for x in stats.mvsdist([1, 2, 3])] + [x.mean() for x in stats.mvsdist([1, 2, 3, 4, 5])] + + +class TestShapiro: + def test_basic(self): + x1 = [0.11, 7.87, 4.61, 10.14, 7.95, 3.14, 0.46, + 4.43, 0.21, 4.75, 0.71, 1.52, 3.24, + 0.93, 0.42, 4.97, 9.53, 4.55, 0.47, 6.66] + w, pw = stats.shapiro(x1) + shapiro_test = stats.shapiro(x1) + assert_almost_equal(w, 0.90047299861907959, decimal=6) + assert_almost_equal(shapiro_test.statistic, 0.90047299861907959, decimal=6) + assert_almost_equal(pw, 0.042089745402336121, decimal=6) + assert_almost_equal(shapiro_test.pvalue, 0.042089745402336121, decimal=6) + + x2 = [1.36, 1.14, 2.92, 2.55, 1.46, 1.06, 5.27, -1.11, + 3.48, 1.10, 0.88, -0.51, 1.46, 0.52, 6.20, 1.69, + 0.08, 3.67, 2.81, 3.49] + w, pw = stats.shapiro(x2) + shapiro_test = stats.shapiro(x2) + assert_almost_equal(w, 0.9590270, decimal=6) + assert_almost_equal(shapiro_test.statistic, 0.9590270, decimal=6) + assert_almost_equal(pw, 0.52460, decimal=3) + assert_almost_equal(shapiro_test.pvalue, 0.52460, decimal=3) + + # Verified against R + x3 = stats.norm.rvs(loc=5, scale=3, size=100, random_state=12345678) + w, pw = stats.shapiro(x3) + shapiro_test = stats.shapiro(x3) + assert_almost_equal(w, 0.9772805571556091, decimal=6) + assert_almost_equal(shapiro_test.statistic, 0.9772805571556091, decimal=6) + assert_almost_equal(pw, 0.08144091814756393, decimal=3) + assert_almost_equal(shapiro_test.pvalue, 0.08144091814756393, decimal=3) + + # Extracted from original paper + x4 = [0.139, 0.157, 0.175, 0.256, 0.344, 0.413, 0.503, 0.577, 0.614, + 0.655, 0.954, 1.392, 1.557, 1.648, 1.690, 1.994, 2.174, 2.206, + 3.245, 3.510, 3.571, 4.354, 4.980, 6.084, 8.351] + W_expected = 0.83467 + p_expected = 0.000914 + w, pw = stats.shapiro(x4) + shapiro_test = stats.shapiro(x4) + assert_almost_equal(w, W_expected, decimal=4) + assert_almost_equal(shapiro_test.statistic, W_expected, decimal=4) + assert_almost_equal(pw, p_expected, decimal=5) + assert_almost_equal(shapiro_test.pvalue, p_expected, decimal=5) + + def test_2d(self): + x1 = [[0.11, 7.87, 4.61, 10.14, 7.95, 3.14, 0.46, + 4.43, 0.21, 4.75], [0.71, 1.52, 3.24, + 0.93, 0.42, 4.97, 9.53, 4.55, 0.47, 6.66]] + w, pw = stats.shapiro(x1) + shapiro_test = stats.shapiro(x1) + assert_almost_equal(w, 0.90047299861907959, decimal=6) + assert_almost_equal(shapiro_test.statistic, 0.90047299861907959, decimal=6) + assert_almost_equal(pw, 0.042089745402336121, decimal=6) + assert_almost_equal(shapiro_test.pvalue, 0.042089745402336121, decimal=6) + + x2 = [[1.36, 1.14, 2.92, 2.55, 1.46, 1.06, 5.27, -1.11, + 3.48, 1.10], [0.88, -0.51, 1.46, 0.52, 6.20, 1.69, + 0.08, 3.67, 2.81, 3.49]] + w, pw = stats.shapiro(x2) + shapiro_test = stats.shapiro(x2) + assert_almost_equal(w, 0.9590270, decimal=6) + assert_almost_equal(shapiro_test.statistic, 0.9590270, decimal=6) + assert_almost_equal(pw, 0.52460, decimal=3) + assert_almost_equal(shapiro_test.pvalue, 0.52460, decimal=3) + + def test_empty_input(self): + assert_raises(ValueError, stats.shapiro, []) + assert_raises(ValueError, stats.shapiro, [[], [], []]) + + def test_not_enough_values(self): + assert_raises(ValueError, stats.shapiro, [1, 2]) + assert_raises(ValueError, stats.shapiro, np.array([[], [2]], dtype=object)) + + def test_bad_arg(self): + # Length of x is less than 3. + x = [1] + assert_raises(ValueError, stats.shapiro, x) + + def test_nan_input(self): + x = np.arange(10.) + x[9] = np.nan + + w, pw = stats.shapiro(x) + shapiro_test = stats.shapiro(x) + assert_equal(w, np.nan) + assert_equal(shapiro_test.statistic, np.nan) + # Originally, shapiro returned a p-value of 1 in this case, + # but there is no way to produce a numerical p-value if the + # statistic is not a number. NaN is more appropriate. + assert_almost_equal(pw, np.nan) + assert_almost_equal(shapiro_test.pvalue, np.nan) + + def test_gh14462(self): + # shapiro is theoretically location-invariant, but when the magnitude + # of the values is much greater than the variance, there can be + # numerical issues. Fixed by subtracting median from the data. + # See gh-14462. + + trans_val, maxlog = stats.boxcox([122500, 474400, 110400]) + res = stats.shapiro(trans_val) + + # Reference from R: + # options(digits=16) + # x = c(0.00000000e+00, 3.39996924e-08, -6.35166875e-09) + # shapiro.test(x) + ref = (0.86468431705371, 0.2805581751566) + + assert_allclose(res, ref, rtol=1e-5) + + def test_length_3_gh18322(self): + # gh-18322 reported that the p-value could be negative for input of + # length 3. Check that this is resolved. + res = stats.shapiro([0.6931471805599453, 0.0, 0.0]) + assert res.pvalue >= 0 + + # R `shapiro.test` doesn't produce an accurate p-value in the case + # above. Check that the formula used in `stats.shapiro` is not wrong. + # options(digits=16) + # x = c(-0.7746653110021126, -0.4344432067942129, 1.8157053280290931) + # shapiro.test(x) + x = [-0.7746653110021126, -0.4344432067942129, 1.8157053280290931] + res = stats.shapiro(x) + assert_allclose(res.statistic, 0.84658770645509) + assert_allclose(res.pvalue, 0.2313666489882, rtol=1e-6) + + +class TestAnderson: + def test_normal(self): + rs = RandomState(1234567890) + x1 = rs.standard_exponential(size=50) + x2 = rs.standard_normal(size=50) + A, crit, sig = stats.anderson(x1) + assert_array_less(crit[:-1], A) + A, crit, sig = stats.anderson(x2) + assert_array_less(A, crit[-2:]) + + v = np.ones(10) + v[0] = 0 + A, crit, sig = stats.anderson(v) + # The expected statistic 3.208057 was computed independently of scipy. + # For example, in R: + # > library(nortest) + # > v <- rep(1, 10) + # > v[1] <- 0 + # > result <- ad.test(v) + # > result$statistic + # A + # 3.208057 + assert_allclose(A, 3.208057) + + def test_expon(self): + rs = RandomState(1234567890) + x1 = rs.standard_exponential(size=50) + x2 = rs.standard_normal(size=50) + A, crit, sig = stats.anderson(x1, 'expon') + assert_array_less(A, crit[-2:]) + with np.errstate(all='ignore'): + A, crit, sig = stats.anderson(x2, 'expon') + assert_(A > crit[-1]) + + def test_gumbel(self): + # Regression test for gh-6306. Before that issue was fixed, + # this case would return a2=inf. + v = np.ones(100) + v[0] = 0.0 + a2, crit, sig = stats.anderson(v, 'gumbel') + # A brief reimplementation of the calculation of the statistic. + n = len(v) + xbar, s = stats.gumbel_l.fit(v) + logcdf = stats.gumbel_l.logcdf(v, xbar, s) + logsf = stats.gumbel_l.logsf(v, xbar, s) + i = np.arange(1, n+1) + expected_a2 = -n - np.mean((2*i - 1) * (logcdf + logsf[::-1])) + + assert_allclose(a2, expected_a2) + + def test_bad_arg(self): + assert_raises(ValueError, stats.anderson, [1], dist='plate_of_shrimp') + + def test_result_attributes(self): + rs = RandomState(1234567890) + x = rs.standard_exponential(size=50) + res = stats.anderson(x) + attributes = ('statistic', 'critical_values', 'significance_level') + check_named_results(res, attributes) + + def test_gumbel_l(self): + # gh-2592, gh-6337 + # Adds support to 'gumbel_r' and 'gumbel_l' as valid inputs for dist. + rs = RandomState(1234567890) + x = rs.gumbel(size=100) + A1, crit1, sig1 = stats.anderson(x, 'gumbel') + A2, crit2, sig2 = stats.anderson(x, 'gumbel_l') + + assert_allclose(A2, A1) + + def test_gumbel_r(self): + # gh-2592, gh-6337 + # Adds support to 'gumbel_r' and 'gumbel_l' as valid inputs for dist. + rs = RandomState(1234567890) + x1 = rs.gumbel(size=100) + x2 = np.ones(100) + # A constant array is a degenerate case and breaks gumbel_r.fit, so + # change one value in x2. + x2[0] = 0.996 + A1, crit1, sig1 = stats.anderson(x1, 'gumbel_r') + A2, crit2, sig2 = stats.anderson(x2, 'gumbel_r') + + assert_array_less(A1, crit1[-2:]) + assert_(A2 > crit2[-1]) + + def test_weibull_min_case_A(self): + # data and reference values from `anderson` reference [7] + x = np.array([225, 171, 198, 189, 189, 135, 162, 135, 117, 162]) + res = stats.anderson(x, 'weibull_min') + m, loc, scale = res.fit_result.params + assert_allclose((m, loc, scale), (2.38, 99.02, 78.23), rtol=2e-3) + assert_allclose(res.statistic, 0.260, rtol=1e-3) + assert res.statistic < res.critical_values[0] + + c = 1 / m # ~0.42 + assert_allclose(c, 1/2.38, rtol=2e-3) + # interpolate between rows for c=0.4 and c=0.45, indices -3 and -2 + As40 = _Avals_weibull[-3] + As45 = _Avals_weibull[-2] + As_ref = As40 + (c - 0.4)/(0.45 - 0.4) * (As45 - As40) + # atol=1e-3 because results are rounded up to the next third decimal + assert np.all(res.critical_values > As_ref) + assert_allclose(res.critical_values, As_ref, atol=1e-3) + + def test_weibull_min_case_B(self): + # From `anderson` reference [7] + x = np.array([74, 57, 48, 29, 502, 12, 70, 21, + 29, 386, 59, 27, 153, 26, 326]) + message = "Maximum likelihood estimation has converged to " + with pytest.raises(ValueError, match=message): + stats.anderson(x, 'weibull_min') + + def test_weibull_warning_error(self): + # Check for warning message when there are too few observations + # This is also an example in which an error occurs during fitting + x = -np.array([225, 75, 57, 168, 107, 12, 61, 43, 29]) + wmessage = "Critical values of the test statistic are given for the..." + emessage = "An error occurred while fitting the Weibull distribution..." + wcontext = pytest.warns(UserWarning, match=wmessage) + econtext = pytest.raises(ValueError, match=emessage) + with wcontext, econtext: + stats.anderson(x, 'weibull_min') + + @pytest.mark.parametrize('distname', + ['norm', 'expon', 'gumbel_l', 'extreme1', + 'gumbel', 'gumbel_r', 'logistic', 'weibull_min']) + def test_anderson_fit_params(self, distname): + # check that anderson now returns a FitResult + rng = np.random.default_rng(330691555377792039) + real_distname = ('gumbel_l' if distname in {'extreme1', 'gumbel'} + else distname) + dist = getattr(stats, real_distname) + params = distcont[real_distname] + x = dist.rvs(*params, size=1000, random_state=rng) + res = stats.anderson(x, distname) + assert res.fit_result.success + + def test_anderson_weibull_As(self): + m = 1 # "when mi < 2, so that c > 0.5, the last line...should be used" + assert_equal(_get_As_weibull(1/m), _Avals_weibull[-1]) + m = np.inf + assert_equal(_get_As_weibull(1/m), _Avals_weibull[0]) + + +class TestAndersonKSamp: + def test_example1a(self): + # Example data from Scholz & Stephens (1987), originally + # published in Lehmann (1995, Nonparametrics, Statistical + # Methods Based on Ranks, p. 309) + # Pass a mixture of lists and arrays + t1 = [38.7, 41.5, 43.8, 44.5, 45.5, 46.0, 47.7, 58.0] + t2 = np.array([39.2, 39.3, 39.7, 41.4, 41.8, 42.9, 43.3, 45.8]) + t3 = np.array([34.0, 35.0, 39.0, 40.0, 43.0, 43.0, 44.0, 45.0]) + t4 = np.array([34.0, 34.8, 34.8, 35.4, 37.2, 37.8, 41.2, 42.8]) + + Tk, tm, p = stats.anderson_ksamp((t1, t2, t3, t4), midrank=False) + + assert_almost_equal(Tk, 4.449, 3) + assert_array_almost_equal([0.4985, 1.3237, 1.9158, 2.4930, 3.2459], + tm[0:5], 4) + assert_allclose(p, 0.0021, atol=0.00025) + + def test_example1b(self): + # Example data from Scholz & Stephens (1987), originally + # published in Lehmann (1995, Nonparametrics, Statistical + # Methods Based on Ranks, p. 309) + # Pass arrays + t1 = np.array([38.7, 41.5, 43.8, 44.5, 45.5, 46.0, 47.7, 58.0]) + t2 = np.array([39.2, 39.3, 39.7, 41.4, 41.8, 42.9, 43.3, 45.8]) + t3 = np.array([34.0, 35.0, 39.0, 40.0, 43.0, 43.0, 44.0, 45.0]) + t4 = np.array([34.0, 34.8, 34.8, 35.4, 37.2, 37.8, 41.2, 42.8]) + Tk, tm, p = stats.anderson_ksamp((t1, t2, t3, t4), midrank=True) + + assert_almost_equal(Tk, 4.480, 3) + assert_array_almost_equal([0.4985, 1.3237, 1.9158, 2.4930, 3.2459], + tm[0:5], 4) + assert_allclose(p, 0.0020, atol=0.00025) + + @pytest.mark.slow + def test_example2a(self): + # Example data taken from an earlier technical report of + # Scholz and Stephens + # Pass lists instead of arrays + t1 = [194, 15, 41, 29, 33, 181] + t2 = [413, 14, 58, 37, 100, 65, 9, 169, 447, 184, 36, 201, 118] + t3 = [34, 31, 18, 18, 67, 57, 62, 7, 22, 34] + t4 = [90, 10, 60, 186, 61, 49, 14, 24, 56, 20, 79, 84, 44, 59, 29, + 118, 25, 156, 310, 76, 26, 44, 23, 62] + t5 = [130, 208, 70, 101, 208] + t6 = [74, 57, 48, 29, 502, 12, 70, 21, 29, 386, 59, 27] + t7 = [55, 320, 56, 104, 220, 239, 47, 246, 176, 182, 33] + t8 = [23, 261, 87, 7, 120, 14, 62, 47, 225, 71, 246, 21, 42, 20, 5, + 12, 120, 11, 3, 14, 71, 11, 14, 11, 16, 90, 1, 16, 52, 95] + t9 = [97, 51, 11, 4, 141, 18, 142, 68, 77, 80, 1, 16, 106, 206, 82, + 54, 31, 216, 46, 111, 39, 63, 18, 191, 18, 163, 24] + t10 = [50, 44, 102, 72, 22, 39, 3, 15, 197, 188, 79, 88, 46, 5, 5, 36, + 22, 139, 210, 97, 30, 23, 13, 14] + t11 = [359, 9, 12, 270, 603, 3, 104, 2, 438] + t12 = [50, 254, 5, 283, 35, 12] + t13 = [487, 18, 100, 7, 98, 5, 85, 91, 43, 230, 3, 130] + t14 = [102, 209, 14, 57, 54, 32, 67, 59, 134, 152, 27, 14, 230, 66, + 61, 34] + + samples = (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) + Tk, tm, p = stats.anderson_ksamp(samples, midrank=False) + assert_almost_equal(Tk, 3.288, 3) + assert_array_almost_equal([0.5990, 1.3269, 1.8052, 2.2486, 2.8009], + tm[0:5], 4) + assert_allclose(p, 0.0041, atol=0.00025) + + rng = np.random.default_rng(6989860141921615054) + method = stats.PermutationMethod(n_resamples=9999, random_state=rng) + res = stats.anderson_ksamp(samples, midrank=False, method=method) + assert_array_equal(res.statistic, Tk) + assert_array_equal(res.critical_values, tm) + assert_allclose(res.pvalue, p, atol=6e-4) + + def test_example2b(self): + # Example data taken from an earlier technical report of + # Scholz and Stephens + t1 = [194, 15, 41, 29, 33, 181] + t2 = [413, 14, 58, 37, 100, 65, 9, 169, 447, 184, 36, 201, 118] + t3 = [34, 31, 18, 18, 67, 57, 62, 7, 22, 34] + t4 = [90, 10, 60, 186, 61, 49, 14, 24, 56, 20, 79, 84, 44, 59, 29, + 118, 25, 156, 310, 76, 26, 44, 23, 62] + t5 = [130, 208, 70, 101, 208] + t6 = [74, 57, 48, 29, 502, 12, 70, 21, 29, 386, 59, 27] + t7 = [55, 320, 56, 104, 220, 239, 47, 246, 176, 182, 33] + t8 = [23, 261, 87, 7, 120, 14, 62, 47, 225, 71, 246, 21, 42, 20, 5, + 12, 120, 11, 3, 14, 71, 11, 14, 11, 16, 90, 1, 16, 52, 95] + t9 = [97, 51, 11, 4, 141, 18, 142, 68, 77, 80, 1, 16, 106, 206, 82, + 54, 31, 216, 46, 111, 39, 63, 18, 191, 18, 163, 24] + t10 = [50, 44, 102, 72, 22, 39, 3, 15, 197, 188, 79, 88, 46, 5, 5, 36, + 22, 139, 210, 97, 30, 23, 13, 14] + t11 = [359, 9, 12, 270, 603, 3, 104, 2, 438] + t12 = [50, 254, 5, 283, 35, 12] + t13 = [487, 18, 100, 7, 98, 5, 85, 91, 43, 230, 3, 130] + t14 = [102, 209, 14, 57, 54, 32, 67, 59, 134, 152, 27, 14, 230, 66, + 61, 34] + + Tk, tm, p = stats.anderson_ksamp((t1, t2, t3, t4, t5, t6, t7, t8, + t9, t10, t11, t12, t13, t14), + midrank=True) + + assert_almost_equal(Tk, 3.294, 3) + assert_array_almost_equal([0.5990, 1.3269, 1.8052, 2.2486, 2.8009], + tm[0:5], 4) + assert_allclose(p, 0.0041, atol=0.00025) + + def test_R_kSamples(self): + # test values generates with R package kSamples + # package version 1.2-6 (2017-06-14) + # r1 = 1:100 + # continuous case (no ties) --> version 1 + # res <- kSamples::ad.test(r1, r1 + 40.5) + # res$ad[1, "T.AD"] # 41.105 + # res$ad[1, " asympt. P-value"] # 5.8399e-18 + # + # discrete case (ties allowed) --> version 2 (here: midrank=True) + # res$ad[2, "T.AD"] # 41.235 + # + # res <- kSamples::ad.test(r1, r1 + .5) + # res$ad[1, "T.AD"] # -1.2824 + # res$ad[1, " asympt. P-value"] # 1 + # res$ad[2, "T.AD"] # -1.2944 + # + # res <- kSamples::ad.test(r1, r1 + 7.5) + # res$ad[1, "T.AD"] # 1.4923 + # res$ad[1, " asympt. P-value"] # 0.077501 + # + # res <- kSamples::ad.test(r1, r1 + 6) + # res$ad[2, "T.AD"] # 0.63892 + # res$ad[2, " asympt. P-value"] # 0.17981 + # + # res <- kSamples::ad.test(r1, r1 + 11.5) + # res$ad[1, "T.AD"] # 4.5042 + # res$ad[1, " asympt. P-value"] # 0.00545 + # + # res <- kSamples::ad.test(r1, r1 + 13.5) + # res$ad[1, "T.AD"] # 6.2982 + # res$ad[1, " asympt. P-value"] # 0.00118 + + x1 = np.linspace(1, 100, 100) + # test case: different distributions;p-value floored at 0.001 + # test case for issue #5493 / #8536 + with suppress_warnings() as sup: + sup.filter(UserWarning, message='p-value floored') + s, _, p = stats.anderson_ksamp([x1, x1 + 40.5], midrank=False) + assert_almost_equal(s, 41.105, 3) + assert_equal(p, 0.001) + + with suppress_warnings() as sup: + sup.filter(UserWarning, message='p-value floored') + s, _, p = stats.anderson_ksamp([x1, x1 + 40.5]) + assert_almost_equal(s, 41.235, 3) + assert_equal(p, 0.001) + + # test case: similar distributions --> p-value capped at 0.25 + with suppress_warnings() as sup: + sup.filter(UserWarning, message='p-value capped') + s, _, p = stats.anderson_ksamp([x1, x1 + .5], midrank=False) + assert_almost_equal(s, -1.2824, 4) + assert_equal(p, 0.25) + + with suppress_warnings() as sup: + sup.filter(UserWarning, message='p-value capped') + s, _, p = stats.anderson_ksamp([x1, x1 + .5]) + assert_almost_equal(s, -1.2944, 4) + assert_equal(p, 0.25) + + # test case: check interpolated p-value in [0.01, 0.25] (no ties) + s, _, p = stats.anderson_ksamp([x1, x1 + 7.5], midrank=False) + assert_almost_equal(s, 1.4923, 4) + assert_allclose(p, 0.0775, atol=0.005, rtol=0) + + # test case: check interpolated p-value in [0.01, 0.25] (w/ ties) + s, _, p = stats.anderson_ksamp([x1, x1 + 6]) + assert_almost_equal(s, 0.6389, 4) + assert_allclose(p, 0.1798, atol=0.005, rtol=0) + + # test extended critical values for p=0.001 and p=0.005 + s, _, p = stats.anderson_ksamp([x1, x1 + 11.5], midrank=False) + assert_almost_equal(s, 4.5042, 4) + assert_allclose(p, 0.00545, atol=0.0005, rtol=0) + + s, _, p = stats.anderson_ksamp([x1, x1 + 13.5], midrank=False) + assert_almost_equal(s, 6.2982, 4) + assert_allclose(p, 0.00118, atol=0.0001, rtol=0) + + def test_not_enough_samples(self): + assert_raises(ValueError, stats.anderson_ksamp, np.ones(5)) + + def test_no_distinct_observations(self): + assert_raises(ValueError, stats.anderson_ksamp, + (np.ones(5), np.ones(5))) + + def test_empty_sample(self): + assert_raises(ValueError, stats.anderson_ksamp, (np.ones(5), [])) + + def test_result_attributes(self): + # Pass a mixture of lists and arrays + t1 = [38.7, 41.5, 43.8, 44.5, 45.5, 46.0, 47.7, 58.0] + t2 = np.array([39.2, 39.3, 39.7, 41.4, 41.8, 42.9, 43.3, 45.8]) + res = stats.anderson_ksamp((t1, t2), midrank=False) + + attributes = ('statistic', 'critical_values', 'significance_level') + check_named_results(res, attributes) + + assert_equal(res.significance_level, res.pvalue) + + +class TestAnsari: + + def test_small(self): + x = [1, 2, 3, 3, 4] + y = [3, 2, 6, 1, 6, 1, 4, 1] + with suppress_warnings() as sup: + sup.filter(UserWarning, "Ties preclude use of exact statistic.") + W, pval = stats.ansari(x, y) + assert_almost_equal(W, 23.5, 11) + assert_almost_equal(pval, 0.13499256881897437, 11) + + def test_approx(self): + ramsay = np.array((111, 107, 100, 99, 102, 106, 109, 108, 104, 99, + 101, 96, 97, 102, 107, 113, 116, 113, 110, 98)) + parekh = np.array((107, 108, 106, 98, 105, 103, 110, 105, 104, + 100, 96, 108, 103, 104, 114, 114, 113, 108, + 106, 99)) + + with suppress_warnings() as sup: + sup.filter(UserWarning, "Ties preclude use of exact statistic.") + W, pval = stats.ansari(ramsay, parekh) + + assert_almost_equal(W, 185.5, 11) + assert_almost_equal(pval, 0.18145819972867083, 11) + + def test_exact(self): + W, pval = stats.ansari([1, 2, 3, 4], [15, 5, 20, 8, 10, 12]) + assert_almost_equal(W, 10.0, 11) + assert_almost_equal(pval, 0.533333333333333333, 7) + + def test_bad_arg(self): + assert_raises(ValueError, stats.ansari, [], [1]) + assert_raises(ValueError, stats.ansari, [1], []) + + def test_result_attributes(self): + x = [1, 2, 3, 3, 4] + y = [3, 2, 6, 1, 6, 1, 4, 1] + with suppress_warnings() as sup: + sup.filter(UserWarning, "Ties preclude use of exact statistic.") + res = stats.ansari(x, y) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_bad_alternative(self): + # invalid value for alternative must raise a ValueError + x1 = [1, 2, 3, 4] + x2 = [5, 6, 7, 8] + match = "'alternative' must be 'two-sided'" + with assert_raises(ValueError, match=match): + stats.ansari(x1, x2, alternative='foo') + + def test_alternative_exact(self): + x1 = [-5, 1, 5, 10, 15, 20, 25] # high scale, loc=10 + x2 = [7.5, 8.5, 9.5, 10.5, 11.5, 12.5] # low scale, loc=10 + # ratio of scales is greater than 1. So, the + # p-value must be high when `alternative='less'` + # and low when `alternative='greater'`. + statistic, pval = stats.ansari(x1, x2) + pval_l = stats.ansari(x1, x2, alternative='less').pvalue + pval_g = stats.ansari(x1, x2, alternative='greater').pvalue + assert pval_l > 0.95 + assert pval_g < 0.05 # level of significance. + # also check if the p-values sum up to 1 plus the probability + # mass under the calculated statistic. + prob = _abw_state.pmf(statistic, len(x1), len(x2)) + assert_allclose(pval_g + pval_l, 1 + prob, atol=1e-12) + # also check if one of the one-sided p-value equals half the + # two-sided p-value and the other one-sided p-value is its + # compliment. + assert_allclose(pval_g, pval/2, atol=1e-12) + assert_allclose(pval_l, 1+prob-pval/2, atol=1e-12) + # sanity check. The result should flip if + # we exchange x and y. + pval_l_reverse = stats.ansari(x2, x1, alternative='less').pvalue + pval_g_reverse = stats.ansari(x2, x1, alternative='greater').pvalue + assert pval_l_reverse < 0.05 + assert pval_g_reverse > 0.95 + + @pytest.mark.parametrize( + 'x, y, alternative, expected', + # the tests are designed in such a way that the + # if else statement in ansari test for exact + # mode is covered. + [([1, 2, 3, 4], [5, 6, 7, 8], 'less', 0.6285714285714), + ([1, 2, 3, 4], [5, 6, 7, 8], 'greater', 0.6285714285714), + ([1, 2, 3], [4, 5, 6, 7, 8], 'less', 0.8928571428571), + ([1, 2, 3], [4, 5, 6, 7, 8], 'greater', 0.2857142857143), + ([1, 2, 3, 4, 5], [6, 7, 8], 'less', 0.2857142857143), + ([1, 2, 3, 4, 5], [6, 7, 8], 'greater', 0.8928571428571)] + ) + def test_alternative_exact_with_R(self, x, y, alternative, expected): + # testing with R on arbitrary data + # Sample R code used for the third test case above: + # ```R + # > options(digits=16) + # > x <- c(1,2,3) + # > y <- c(4,5,6,7,8) + # > ansari.test(x, y, alternative='less', exact=TRUE) + # + # Ansari-Bradley test + # + # data: x and y + # AB = 6, p-value = 0.8928571428571 + # alternative hypothesis: true ratio of scales is less than 1 + # + # ``` + pval = stats.ansari(x, y, alternative=alternative).pvalue + assert_allclose(pval, expected, atol=1e-12) + + def test_alternative_approx(self): + # intuitive tests for approximation + x1 = stats.norm.rvs(0, 5, size=100, random_state=123) + x2 = stats.norm.rvs(0, 2, size=100, random_state=123) + # for m > 55 or n > 55, the test should automatically + # switch to approximation. + pval_l = stats.ansari(x1, x2, alternative='less').pvalue + pval_g = stats.ansari(x1, x2, alternative='greater').pvalue + assert_allclose(pval_l, 1.0, atol=1e-12) + assert_allclose(pval_g, 0.0, atol=1e-12) + # also check if one of the one-sided p-value equals half the + # two-sided p-value and the other one-sided p-value is its + # compliment. + x1 = stats.norm.rvs(0, 2, size=60, random_state=123) + x2 = stats.norm.rvs(0, 1.5, size=60, random_state=123) + pval = stats.ansari(x1, x2).pvalue + pval_l = stats.ansari(x1, x2, alternative='less').pvalue + pval_g = stats.ansari(x1, x2, alternative='greater').pvalue + assert_allclose(pval_g, pval/2, atol=1e-12) + assert_allclose(pval_l, 1-pval/2, atol=1e-12) + + +class TestBartlett: + + def test_data(self): + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda357.htm + args = [g1, g2, g3, g4, g5, g6, g7, g8, g9, g10] + T, pval = stats.bartlett(*args) + assert_almost_equal(T, 20.78587342806484, 7) + assert_almost_equal(pval, 0.0136358632781, 7) + + def test_bad_arg(self): + # Too few args raises ValueError. + assert_raises(ValueError, stats.bartlett, [1]) + + def test_result_attributes(self): + args = [g1, g2, g3, g4, g5, g6, g7, g8, g9, g10] + res = stats.bartlett(*args) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_empty_arg(self): + args = (g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, []) + assert_equal((np.nan, np.nan), stats.bartlett(*args)) + + # temporary fix for issue #9252: only accept 1d input + def test_1d_input(self): + x = np.array([[1, 2], [3, 4]]) + assert_raises(ValueError, stats.bartlett, g1, x) + + +class TestLevene: + + def test_data(self): + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda35a.htm + args = [g1, g2, g3, g4, g5, g6, g7, g8, g9, g10] + W, pval = stats.levene(*args) + assert_almost_equal(W, 1.7059176930008939, 7) + assert_almost_equal(pval, 0.0990829755522, 7) + + def test_trimmed1(self): + # Test that center='trimmed' gives the same result as center='mean' + # when proportiontocut=0. + W1, pval1 = stats.levene(g1, g2, g3, center='mean') + W2, pval2 = stats.levene(g1, g2, g3, center='trimmed', + proportiontocut=0.0) + assert_almost_equal(W1, W2) + assert_almost_equal(pval1, pval2) + + def test_trimmed2(self): + x = [1.2, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 100.0] + y = [0.0, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 200.0] + np.random.seed(1234) + x2 = np.random.permutation(x) + + # Use center='trimmed' + W0, pval0 = stats.levene(x, y, center='trimmed', + proportiontocut=0.125) + W1, pval1 = stats.levene(x2, y, center='trimmed', + proportiontocut=0.125) + # Trim the data here, and use center='mean' + W2, pval2 = stats.levene(x[1:-1], y[1:-1], center='mean') + # Result should be the same. + assert_almost_equal(W0, W2) + assert_almost_equal(W1, W2) + assert_almost_equal(pval1, pval2) + + def test_equal_mean_median(self): + x = np.linspace(-1, 1, 21) + np.random.seed(1234) + x2 = np.random.permutation(x) + y = x**3 + W1, pval1 = stats.levene(x, y, center='mean') + W2, pval2 = stats.levene(x2, y, center='median') + assert_almost_equal(W1, W2) + assert_almost_equal(pval1, pval2) + + def test_bad_keyword(self): + x = np.linspace(-1, 1, 21) + assert_raises(TypeError, stats.levene, x, x, portiontocut=0.1) + + def test_bad_center_value(self): + x = np.linspace(-1, 1, 21) + assert_raises(ValueError, stats.levene, x, x, center='trim') + + def test_too_few_args(self): + assert_raises(ValueError, stats.levene, [1]) + + def test_result_attributes(self): + args = [g1, g2, g3, g4, g5, g6, g7, g8, g9, g10] + res = stats.levene(*args) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + # temporary fix for issue #9252: only accept 1d input + def test_1d_input(self): + x = np.array([[1, 2], [3, 4]]) + assert_raises(ValueError, stats.levene, g1, x) + + +class TestBinomTest: + """Tests for stats.binomtest.""" + + # Expected results here are from R binom.test, e.g. + # options(digits=16) + # binom.test(484, 967, p=0.48) + # + def test_two_sided_pvalues1(self): + # `tol` could be stricter on most architectures, but the value + # here is limited by accuracy of `binom.cdf` for large inputs on + # Linux_Python_37_32bit_full and aarch64 + rtol = 1e-10 # aarch64 observed rtol: 1.5e-11 + res = stats.binomtest(10079999, 21000000, 0.48) + assert_allclose(res.pvalue, 1.0, rtol=rtol) + res = stats.binomtest(10079990, 21000000, 0.48) + assert_allclose(res.pvalue, 0.9966892187965, rtol=rtol) + res = stats.binomtest(10080009, 21000000, 0.48) + assert_allclose(res.pvalue, 0.9970377203856, rtol=rtol) + res = stats.binomtest(10080017, 21000000, 0.48) + assert_allclose(res.pvalue, 0.9940754817328, rtol=1e-9) + + def test_two_sided_pvalues2(self): + rtol = 1e-10 # no aarch64 failure with 1e-15, preemptive bump + res = stats.binomtest(9, n=21, p=0.48) + assert_allclose(res.pvalue, 0.6689672431939, rtol=rtol) + res = stats.binomtest(4, 21, 0.48) + assert_allclose(res.pvalue, 0.008139563452106, rtol=rtol) + res = stats.binomtest(11, 21, 0.48) + assert_allclose(res.pvalue, 0.8278629664608, rtol=rtol) + res = stats.binomtest(7, 21, 0.48) + assert_allclose(res.pvalue, 0.1966772901718, rtol=rtol) + res = stats.binomtest(3, 10, .5) + assert_allclose(res.pvalue, 0.34375, rtol=rtol) + res = stats.binomtest(2, 2, .4) + assert_allclose(res.pvalue, 0.16, rtol=rtol) + res = stats.binomtest(2, 4, .3) + assert_allclose(res.pvalue, 0.5884, rtol=rtol) + + def test_edge_cases(self): + rtol = 1e-10 # aarch64 observed rtol: 1.33e-15 + res = stats.binomtest(484, 967, 0.5) + assert_allclose(res.pvalue, 1, rtol=rtol) + res = stats.binomtest(3, 47, 3/47) + assert_allclose(res.pvalue, 1, rtol=rtol) + res = stats.binomtest(13, 46, 13/46) + assert_allclose(res.pvalue, 1, rtol=rtol) + res = stats.binomtest(15, 44, 15/44) + assert_allclose(res.pvalue, 1, rtol=rtol) + res = stats.binomtest(7, 13, 0.5) + assert_allclose(res.pvalue, 1, rtol=rtol) + res = stats.binomtest(6, 11, 0.5) + assert_allclose(res.pvalue, 1, rtol=rtol) + + def test_binary_srch_for_binom_tst(self): + # Test that old behavior of binomtest is maintained + # by the new binary search method in cases where d + # exactly equals the input on one side. + n = 10 + p = 0.5 + k = 3 + # First test for the case where k > mode of PMF + i = np.arange(np.ceil(p * n), n+1) + d = stats.binom.pmf(k, n, p) + # Old way of calculating y, probably consistent with R. + y1 = np.sum(stats.binom.pmf(i, n, p) <= d, axis=0) + # New way with binary search. + ix = _binary_search_for_binom_tst(lambda x1: + -stats.binom.pmf(x1, n, p), + -d, np.ceil(p * n), n) + y2 = n - ix + int(d == stats.binom.pmf(ix, n, p)) + assert_allclose(y1, y2, rtol=1e-9) + # Now test for the other side. + k = 7 + i = np.arange(np.floor(p * n) + 1) + d = stats.binom.pmf(k, n, p) + # Old way of calculating y. + y1 = np.sum(stats.binom.pmf(i, n, p) <= d, axis=0) + # New way with binary search. + ix = _binary_search_for_binom_tst(lambda x1: + stats.binom.pmf(x1, n, p), + d, 0, np.floor(p * n)) + y2 = ix + 1 + assert_allclose(y1, y2, rtol=1e-9) + + # Expected results here are from R 3.6.2 binom.test + @pytest.mark.parametrize('alternative, pval, ci_low, ci_high', + [('less', 0.148831050443, + 0.0, 0.2772002496709138), + ('greater', 0.9004695898947, + 0.1366613252458672, 1.0), + ('two-sided', 0.2983720970096, + 0.1266555521019559, 0.2918426890886281)]) + def test_confidence_intervals1(self, alternative, pval, ci_low, ci_high): + res = stats.binomtest(20, n=100, p=0.25, alternative=alternative) + assert_allclose(res.pvalue, pval, rtol=1e-12) + assert_equal(res.statistic, 0.2) + ci = res.proportion_ci(confidence_level=0.95) + assert_allclose((ci.low, ci.high), (ci_low, ci_high), rtol=1e-12) + + # Expected results here are from R 3.6.2 binom.test. + @pytest.mark.parametrize('alternative, pval, ci_low, ci_high', + [('less', + 0.005656361, 0.0, 0.1872093), + ('greater', + 0.9987146, 0.008860761, 1.0), + ('two-sided', + 0.01191714, 0.006872485, 0.202706269)]) + def test_confidence_intervals2(self, alternative, pval, ci_low, ci_high): + res = stats.binomtest(3, n=50, p=0.2, alternative=alternative) + assert_allclose(res.pvalue, pval, rtol=1e-6) + assert_equal(res.statistic, 0.06) + ci = res.proportion_ci(confidence_level=0.99) + assert_allclose((ci.low, ci.high), (ci_low, ci_high), rtol=1e-6) + + # Expected results here are from R 3.6.2 binom.test. + @pytest.mark.parametrize('alternative, pval, ci_high', + [('less', 0.05631351, 0.2588656), + ('greater', 1.0, 1.0), + ('two-sided', 0.07604122, 0.3084971)]) + def test_confidence_interval_exact_k0(self, alternative, pval, ci_high): + # Test with k=0, n = 10. + res = stats.binomtest(0, 10, p=0.25, alternative=alternative) + assert_allclose(res.pvalue, pval, rtol=1e-6) + ci = res.proportion_ci(confidence_level=0.95) + assert_equal(ci.low, 0.0) + assert_allclose(ci.high, ci_high, rtol=1e-6) + + # Expected results here are from R 3.6.2 binom.test. + @pytest.mark.parametrize('alternative, pval, ci_low', + [('less', 1.0, 0.0), + ('greater', 9.536743e-07, 0.7411344), + ('two-sided', 9.536743e-07, 0.6915029)]) + def test_confidence_interval_exact_k_is_n(self, alternative, pval, ci_low): + # Test with k = n = 10. + res = stats.binomtest(10, 10, p=0.25, alternative=alternative) + assert_allclose(res.pvalue, pval, rtol=1e-6) + ci = res.proportion_ci(confidence_level=0.95) + assert_equal(ci.high, 1.0) + assert_allclose(ci.low, ci_low, rtol=1e-6) + + # Expected results are from the prop.test function in R 3.6.2. + @pytest.mark.parametrize( + 'k, alternative, corr, conf, ci_low, ci_high', + [[3, 'two-sided', True, 0.95, 0.08094782, 0.64632928], + [3, 'two-sided', True, 0.99, 0.0586329, 0.7169416], + [3, 'two-sided', False, 0.95, 0.1077913, 0.6032219], + [3, 'two-sided', False, 0.99, 0.07956632, 0.6799753], + [3, 'less', True, 0.95, 0.0, 0.6043476], + [3, 'less', True, 0.99, 0.0, 0.6901811], + [3, 'less', False, 0.95, 0.0, 0.5583002], + [3, 'less', False, 0.99, 0.0, 0.6507187], + [3, 'greater', True, 0.95, 0.09644904, 1.0], + [3, 'greater', True, 0.99, 0.06659141, 1.0], + [3, 'greater', False, 0.95, 0.1268766, 1.0], + [3, 'greater', False, 0.99, 0.08974147, 1.0], + + [0, 'two-sided', True, 0.95, 0.0, 0.3445372], + [0, 'two-sided', False, 0.95, 0.0, 0.2775328], + [0, 'less', True, 0.95, 0.0, 0.2847374], + [0, 'less', False, 0.95, 0.0, 0.212942], + [0, 'greater', True, 0.95, 0.0, 1.0], + [0, 'greater', False, 0.95, 0.0, 1.0], + + [10, 'two-sided', True, 0.95, 0.6554628, 1.0], + [10, 'two-sided', False, 0.95, 0.7224672, 1.0], + [10, 'less', True, 0.95, 0.0, 1.0], + [10, 'less', False, 0.95, 0.0, 1.0], + [10, 'greater', True, 0.95, 0.7152626, 1.0], + [10, 'greater', False, 0.95, 0.787058, 1.0]] + ) + def test_ci_wilson_method(self, k, alternative, corr, conf, + ci_low, ci_high): + res = stats.binomtest(k, n=10, p=0.1, alternative=alternative) + if corr: + method = 'wilsoncc' + else: + method = 'wilson' + ci = res.proportion_ci(confidence_level=conf, method=method) + assert_allclose((ci.low, ci.high), (ci_low, ci_high), rtol=1e-6) + + def test_estimate_equals_hypothesized_prop(self): + # Test the special case where the estimated proportion equals + # the hypothesized proportion. When alternative is 'two-sided', + # the p-value is 1. + res = stats.binomtest(4, 16, 0.25) + assert_equal(res.statistic, 0.25) + assert_equal(res.pvalue, 1.0) + + @pytest.mark.parametrize('k, n', [(0, 0), (-1, 2)]) + def test_invalid_k_n(self, k, n): + with pytest.raises(ValueError, + match="must be an integer not less than"): + stats.binomtest(k, n) + + def test_invalid_k_too_big(self): + with pytest.raises(ValueError, + match=r"k \(11\) must not be greater than n \(10\)."): + stats.binomtest(11, 10, 0.25) + + def test_invalid_k_wrong_type(self): + with pytest.raises(TypeError, + match="k must be an integer."): + stats.binomtest([10, 11], 21, 0.25) + + def test_invalid_p_range(self): + message = r'p \(-0.5\) must be in range...' + with pytest.raises(ValueError, match=message): + stats.binomtest(50, 150, p=-0.5) + message = r'p \(1.5\) must be in range...' + with pytest.raises(ValueError, match=message): + stats.binomtest(50, 150, p=1.5) + + def test_invalid_confidence_level(self): + res = stats.binomtest(3, n=10, p=0.1) + message = r"confidence_level \(-1\) must be in the interval" + with pytest.raises(ValueError, match=message): + res.proportion_ci(confidence_level=-1) + + def test_invalid_ci_method(self): + res = stats.binomtest(3, n=10, p=0.1) + with pytest.raises(ValueError, match=r"method \('plate of shrimp'\) must be"): + res.proportion_ci(method="plate of shrimp") + + def test_invalid_alternative(self): + with pytest.raises(ValueError, match=r"alternative \('ekki'\) not..."): + stats.binomtest(3, n=10, p=0.1, alternative='ekki') + + def test_alias(self): + res = stats.binomtest(3, n=10, p=0.1) + assert_equal(res.proportion_estimate, res.statistic) + + @pytest.mark.skipif(sys.maxsize <= 2**32, reason="32-bit does not overflow") + def test_boost_overflow_raises(self): + # Boost.Math error policy should raise exceptions in Python + with pytest.raises(OverflowError, match='Error in function...'): + stats.binomtest(5, 6, p=sys.float_info.min) + + +class TestFligner: + + def test_data(self): + # numbers from R: fligner.test in package stats + x1 = np.arange(5) + assert_array_almost_equal(stats.fligner(x1, x1**2), + (3.2282229927203536, 0.072379187848207877), + 11) + + def test_trimmed1(self): + # Perturb input to break ties in the transformed data + # See https://github.com/scipy/scipy/pull/8042 for more details + rs = np.random.RandomState(123) + + def _perturb(g): + return (np.asarray(g) + 1e-10 * rs.randn(len(g))).tolist() + + g1_ = _perturb(g1) + g2_ = _perturb(g2) + g3_ = _perturb(g3) + # Test that center='trimmed' gives the same result as center='mean' + # when proportiontocut=0. + Xsq1, pval1 = stats.fligner(g1_, g2_, g3_, center='mean') + Xsq2, pval2 = stats.fligner(g1_, g2_, g3_, center='trimmed', + proportiontocut=0.0) + assert_almost_equal(Xsq1, Xsq2) + assert_almost_equal(pval1, pval2) + + def test_trimmed2(self): + x = [1.2, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 100.0] + y = [0.0, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 200.0] + # Use center='trimmed' + Xsq1, pval1 = stats.fligner(x, y, center='trimmed', + proportiontocut=0.125) + # Trim the data here, and use center='mean' + Xsq2, pval2 = stats.fligner(x[1:-1], y[1:-1], center='mean') + # Result should be the same. + assert_almost_equal(Xsq1, Xsq2) + assert_almost_equal(pval1, pval2) + + # The following test looks reasonable at first, but fligner() uses the + # function stats.rankdata(), and in one of the cases in this test, + # there are ties, while in the other (because of normal rounding + # errors) there are not. This difference leads to differences in the + # third significant digit of W. + # + #def test_equal_mean_median(self): + # x = np.linspace(-1,1,21) + # y = x**3 + # W1, pval1 = stats.fligner(x, y, center='mean') + # W2, pval2 = stats.fligner(x, y, center='median') + # assert_almost_equal(W1, W2) + # assert_almost_equal(pval1, pval2) + + def test_bad_keyword(self): + x = np.linspace(-1, 1, 21) + assert_raises(TypeError, stats.fligner, x, x, portiontocut=0.1) + + def test_bad_center_value(self): + x = np.linspace(-1, 1, 21) + assert_raises(ValueError, stats.fligner, x, x, center='trim') + + def test_bad_num_args(self): + # Too few args raises ValueError. + assert_raises(ValueError, stats.fligner, [1]) + + def test_empty_arg(self): + x = np.arange(5) + assert_equal((np.nan, np.nan), stats.fligner(x, x**2, [])) + + +def mood_cases_with_ties(): + # Generate random `x` and `y` arrays with ties both between and within the + # samples. Expected results are (statistic, pvalue) from SAS. + expected_results = [(-1.76658511464992, .0386488678399305), + (-.694031428192304, .2438312498647250), + (-1.15093525352151, .1248794365836150)] + seeds = [23453254, 1298352315, 987234597] + for si, seed in enumerate(seeds): + rng = np.random.default_rng(seed) + xy = rng.random(100) + # Generate random indices to make ties + tie_ind = rng.integers(low=0, high=99, size=5) + # Generate a random number of ties for each index. + num_ties_per_ind = rng.integers(low=1, high=5, size=5) + # At each `tie_ind`, mark the next `n` indices equal to that value. + for i, n in zip(tie_ind, num_ties_per_ind): + for j in range(i + 1, i + n): + xy[j] = xy[i] + # scramble order of xy before splitting into `x, y` + rng.shuffle(xy) + x, y = np.split(xy, 2) + yield x, y, 'less', *expected_results[si] + + +class TestMood: + @pytest.mark.parametrize("x,y,alternative,stat_expect,p_expect", + mood_cases_with_ties()) + def test_against_SAS(self, x, y, alternative, stat_expect, p_expect): + """ + Example code used to generate SAS output: + DATA myData; + INPUT X Y; + CARDS; + 1 0 + 1 1 + 1 2 + 1 3 + 1 4 + 2 0 + 2 1 + 2 4 + 2 9 + 2 16 + ods graphics on; + proc npar1way mood data=myData ; + class X; + ods output MoodTest=mt; + proc contents data=mt; + proc print data=mt; + format Prob1 17.16 Prob2 17.16 Statistic 17.16 Z 17.16 ; + title "Mood Two-Sample Test"; + proc print data=myData; + title "Data for above results"; + run; + """ + statistic, pvalue = stats.mood(x, y, alternative=alternative) + assert_allclose(stat_expect, statistic, atol=1e-16) + assert_allclose(p_expect, pvalue, atol=1e-16) + + @pytest.mark.parametrize("alternative, expected", + [('two-sided', (1.019938533549930, + .3077576129778760)), + ('less', (1.019938533549930, + 1 - .1538788064889380)), + ('greater', (1.019938533549930, + .1538788064889380))]) + def test_against_SAS_2(self, alternative, expected): + # Code to run in SAS in above function + x = [111, 107, 100, 99, 102, 106, 109, 108, 104, 99, + 101, 96, 97, 102, 107, 113, 116, 113, 110, 98] + y = [107, 108, 106, 98, 105, 103, 110, 105, 104, 100, + 96, 108, 103, 104, 114, 114, 113, 108, 106, 99] + res = stats.mood(x, y, alternative=alternative) + assert_allclose(res, expected) + + def test_mood_order_of_args(self): + # z should change sign when the order of arguments changes, pvalue + # should not change + np.random.seed(1234) + x1 = np.random.randn(10, 1) + x2 = np.random.randn(15, 1) + z1, p1 = stats.mood(x1, x2) + z2, p2 = stats.mood(x2, x1) + assert_array_almost_equal([z1, p1], [-z2, p2]) + + def test_mood_with_axis_none(self): + # Test with axis = None, compare with results from R + x1 = [-0.626453810742332, 0.183643324222082, -0.835628612410047, + 1.59528080213779, 0.329507771815361, -0.820468384118015, + 0.487429052428485, 0.738324705129217, 0.575781351653492, + -0.305388387156356, 1.51178116845085, 0.389843236411431, + -0.621240580541804, -2.2146998871775, 1.12493091814311, + -0.0449336090152309, -0.0161902630989461, 0.943836210685299, + 0.821221195098089, 0.593901321217509] + + x2 = [-0.896914546624981, 0.184849184646742, 1.58784533120882, + -1.13037567424629, -0.0802517565509893, 0.132420284381094, + 0.707954729271733, -0.23969802417184, 1.98447393665293, + -0.138787012119665, 0.417650750792556, 0.981752777463662, + -0.392695355503813, -1.03966897694891, 1.78222896030858, + -2.31106908460517, 0.878604580921265, 0.035806718015226, + 1.01282869212708, 0.432265154539617, 2.09081920524915, + -1.19992581964387, 1.58963820029007, 1.95465164222325, + 0.00493777682814261, -2.45170638784613, 0.477237302613617, + -0.596558168631403, 0.792203270299649, 0.289636710177348] + + x1 = np.array(x1) + x2 = np.array(x2) + x1.shape = (10, 2) + x2.shape = (15, 2) + assert_array_almost_equal(stats.mood(x1, x2, axis=None), + [-1.31716607555, 0.18778296257]) + + def test_mood_2d(self): + # Test if the results of mood test in 2-D case are consistent with the + # R result for the same inputs. Numbers from R mood.test(). + ny = 5 + np.random.seed(1234) + x1 = np.random.randn(10, ny) + x2 = np.random.randn(15, ny) + z_vectest, pval_vectest = stats.mood(x1, x2) + + for j in range(ny): + assert_array_almost_equal([z_vectest[j], pval_vectest[j]], + stats.mood(x1[:, j], x2[:, j])) + + # inverse order of dimensions + x1 = x1.transpose() + x2 = x2.transpose() + z_vectest, pval_vectest = stats.mood(x1, x2, axis=1) + + for i in range(ny): + # check axis handling is self consistent + assert_array_almost_equal([z_vectest[i], pval_vectest[i]], + stats.mood(x1[i, :], x2[i, :])) + + def test_mood_3d(self): + shape = (10, 5, 6) + np.random.seed(1234) + x1 = np.random.randn(*shape) + x2 = np.random.randn(*shape) + + for axis in range(3): + z_vectest, pval_vectest = stats.mood(x1, x2, axis=axis) + # Tests that result for 3-D arrays is equal to that for the + # same calculation on a set of 1-D arrays taken from the + # 3-D array + axes_idx = ([1, 2], [0, 2], [0, 1]) # the two axes != axis + for i in range(shape[axes_idx[axis][0]]): + for j in range(shape[axes_idx[axis][1]]): + if axis == 0: + slice1 = x1[:, i, j] + slice2 = x2[:, i, j] + elif axis == 1: + slice1 = x1[i, :, j] + slice2 = x2[i, :, j] + else: + slice1 = x1[i, j, :] + slice2 = x2[i, j, :] + + assert_array_almost_equal([z_vectest[i, j], + pval_vectest[i, j]], + stats.mood(slice1, slice2)) + + def test_mood_bad_arg(self): + # Raise ValueError when the sum of the lengths of the args is + # less than 3 + assert_raises(ValueError, stats.mood, [1], []) + + def test_mood_alternative(self): + + np.random.seed(0) + x = stats.norm.rvs(scale=0.75, size=100) + y = stats.norm.rvs(scale=1.25, size=100) + + stat1, p1 = stats.mood(x, y, alternative='two-sided') + stat2, p2 = stats.mood(x, y, alternative='less') + stat3, p3 = stats.mood(x, y, alternative='greater') + + assert stat1 == stat2 == stat3 + assert_allclose(p1, 0, atol=1e-7) + assert_allclose(p2, p1/2) + assert_allclose(p3, 1 - p1/2) + + with pytest.raises(ValueError, match="`alternative` must be..."): + stats.mood(x, y, alternative='ekki-ekki') + + @pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater']) + def test_result(self, alternative): + rng = np.random.default_rng(265827767938813079281100964083953437622) + x1 = rng.standard_normal((10, 1)) + x2 = rng.standard_normal((15, 1)) + + res = stats.mood(x1, x2, alternative=alternative) + assert_equal((res.statistic, res.pvalue), res) + + +class TestProbplot: + + def test_basic(self): + x = stats.norm.rvs(size=20, random_state=12345) + osm, osr = stats.probplot(x, fit=False) + osm_expected = [-1.8241636, -1.38768012, -1.11829229, -0.91222575, + -0.73908135, -0.5857176, -0.44506467, -0.31273668, + -0.18568928, -0.06158146, 0.06158146, 0.18568928, + 0.31273668, 0.44506467, 0.5857176, 0.73908135, + 0.91222575, 1.11829229, 1.38768012, 1.8241636] + assert_allclose(osr, np.sort(x)) + assert_allclose(osm, osm_expected) + + res, res_fit = stats.probplot(x, fit=True) + res_fit_expected = [1.05361841, 0.31297795, 0.98741609] + assert_allclose(res_fit, res_fit_expected) + + def test_sparams_keyword(self): + x = stats.norm.rvs(size=100, random_state=123456) + # Check that None, () and 0 (loc=0, for normal distribution) all work + # and give the same results + osm1, osr1 = stats.probplot(x, sparams=None, fit=False) + osm2, osr2 = stats.probplot(x, sparams=0, fit=False) + osm3, osr3 = stats.probplot(x, sparams=(), fit=False) + assert_allclose(osm1, osm2) + assert_allclose(osm1, osm3) + assert_allclose(osr1, osr2) + assert_allclose(osr1, osr3) + # Check giving (loc, scale) params for normal distribution + osm, osr = stats.probplot(x, sparams=(), fit=False) + + def test_dist_keyword(self): + x = stats.norm.rvs(size=20, random_state=12345) + osm1, osr1 = stats.probplot(x, fit=False, dist='t', sparams=(3,)) + osm2, osr2 = stats.probplot(x, fit=False, dist=stats.t, sparams=(3,)) + assert_allclose(osm1, osm2) + assert_allclose(osr1, osr2) + + assert_raises(ValueError, stats.probplot, x, dist='wrong-dist-name') + assert_raises(AttributeError, stats.probplot, x, dist=[]) + + class custom_dist: + """Some class that looks just enough like a distribution.""" + def ppf(self, q): + return stats.norm.ppf(q, loc=2) + + osm1, osr1 = stats.probplot(x, sparams=(2,), fit=False) + osm2, osr2 = stats.probplot(x, dist=custom_dist(), fit=False) + assert_allclose(osm1, osm2) + assert_allclose(osr1, osr2) + + @pytest.mark.skipif(not have_matplotlib, reason="no matplotlib") + def test_plot_kwarg(self): + fig = plt.figure() + fig.add_subplot(111) + x = stats.t.rvs(3, size=100, random_state=7654321) + res1, fitres1 = stats.probplot(x, plot=plt) + plt.close() + res2, fitres2 = stats.probplot(x, plot=None) + res3 = stats.probplot(x, fit=False, plot=plt) + plt.close() + res4 = stats.probplot(x, fit=False, plot=None) + # Check that results are consistent between combinations of `fit` and + # `plot` keywords. + assert_(len(res1) == len(res2) == len(res3) == len(res4) == 2) + assert_allclose(res1, res2) + assert_allclose(res1, res3) + assert_allclose(res1, res4) + assert_allclose(fitres1, fitres2) + + # Check that a Matplotlib Axes object is accepted + fig = plt.figure() + ax = fig.add_subplot(111) + stats.probplot(x, fit=False, plot=ax) + plt.close() + + def test_probplot_bad_args(self): + # Raise ValueError when given an invalid distribution. + assert_raises(ValueError, stats.probplot, [1], dist="plate_of_shrimp") + + def test_empty(self): + assert_equal(stats.probplot([], fit=False), + (np.array([]), np.array([]))) + assert_equal(stats.probplot([], fit=True), + ((np.array([]), np.array([])), + (np.nan, np.nan, 0.0))) + + def test_array_of_size_one(self): + with np.errstate(invalid='ignore'): + assert_equal(stats.probplot([1], fit=True), + ((np.array([0.]), np.array([1])), + (np.nan, np.nan, 0.0))) + + +class TestWilcoxon: + def test_wilcoxon_bad_arg(self): + # Raise ValueError when two args of different lengths are given or + # zero_method is unknown. + assert_raises(ValueError, stats.wilcoxon, [1], [1, 2]) + assert_raises(ValueError, stats.wilcoxon, [1, 2], [1, 2], "dummy") + assert_raises(ValueError, stats.wilcoxon, [1, 2], [1, 2], + alternative="dummy") + assert_raises(ValueError, stats.wilcoxon, [1]*10, mode="xyz") + + def test_zero_diff(self): + x = np.arange(20) + # pratt and wilcox do not work if x - y == 0 + assert_raises(ValueError, stats.wilcoxon, x, x, "wilcox", + mode="approx") + assert_raises(ValueError, stats.wilcoxon, x, x, "pratt", + mode="approx") + # ranksum is n*(n+1)/2, split in half if zero_method == "zsplit" + assert_equal(stats.wilcoxon(x, x, "zsplit", mode="approx"), + (20*21/4, 1.0)) + + def test_pratt(self): + # regression test for gh-6805: p-value matches value from R package + # coin (wilcoxsign_test) reported in the issue + x = [1, 2, 3, 4] + y = [1, 2, 3, 5] + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Sample size too small") + res = stats.wilcoxon(x, y, zero_method="pratt", mode="approx") + assert_allclose(res, (0.0, 0.31731050786291415)) + + def test_wilcoxon_arg_type(self): + # Should be able to accept list as arguments. + # Address issue 6070. + arr = [1, 2, 3, 0, -1, 3, 1, 2, 1, 1, 2] + + _ = stats.wilcoxon(arr, zero_method="pratt", mode="approx") + _ = stats.wilcoxon(arr, zero_method="zsplit", mode="approx") + _ = stats.wilcoxon(arr, zero_method="wilcox", mode="approx") + + def test_accuracy_wilcoxon(self): + freq = [1, 4, 16, 15, 8, 4, 5, 1, 2] + nums = range(-4, 5) + x = np.concatenate([[u] * v for u, v in zip(nums, freq)]) + y = np.zeros(x.size) + + T, p = stats.wilcoxon(x, y, "pratt", mode="approx") + assert_allclose(T, 423) + assert_allclose(p, 0.0031724568006762576) + + T, p = stats.wilcoxon(x, y, "zsplit", mode="approx") + assert_allclose(T, 441) + assert_allclose(p, 0.0032145343172473055) + + T, p = stats.wilcoxon(x, y, "wilcox", mode="approx") + assert_allclose(T, 327) + assert_allclose(p, 0.00641346115861) + + # Test the 'correction' option, using values computed in R with: + # > wilcox.test(x, y, paired=TRUE, exact=FALSE, correct={FALSE,TRUE}) + x = np.array([120, 114, 181, 188, 180, 146, 121, 191, 132, 113, 127, 112]) + y = np.array([133, 143, 119, 189, 112, 199, 198, 113, 115, 121, 142, 187]) + T, p = stats.wilcoxon(x, y, correction=False, mode="approx") + assert_equal(T, 34) + assert_allclose(p, 0.6948866, rtol=1e-6) + T, p = stats.wilcoxon(x, y, correction=True, mode="approx") + assert_equal(T, 34) + assert_allclose(p, 0.7240817, rtol=1e-6) + + def test_wilcoxon_result_attributes(self): + x = np.array([120, 114, 181, 188, 180, 146, 121, 191, 132, 113, 127, 112]) + y = np.array([133, 143, 119, 189, 112, 199, 198, 113, 115, 121, 142, 187]) + res = stats.wilcoxon(x, y, correction=False, mode="approx") + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_wilcoxon_has_zstatistic(self): + rng = np.random.default_rng(89426135444) + x, y = rng.random(15), rng.random(15) + + res = stats.wilcoxon(x, y, mode="approx") + ref = stats.norm.ppf(res.pvalue/2) + assert_allclose(res.zstatistic, ref) + + res = stats.wilcoxon(x, y, mode="exact") + assert not hasattr(res, 'zstatistic') + + res = stats.wilcoxon(x, y) + assert not hasattr(res, 'zstatistic') + + def test_wilcoxon_tie(self): + # Regression test for gh-2391. + # Corresponding R code is: + # > result = wilcox.test(rep(0.1, 10), exact=FALSE, correct=FALSE) + # > result$p.value + # [1] 0.001565402 + # > result = wilcox.test(rep(0.1, 10), exact=FALSE, correct=TRUE) + # > result$p.value + # [1] 0.001904195 + stat, p = stats.wilcoxon([0.1] * 10, mode="approx") + expected_p = 0.001565402 + assert_equal(stat, 0) + assert_allclose(p, expected_p, rtol=1e-6) + + stat, p = stats.wilcoxon([0.1] * 10, correction=True, mode="approx") + expected_p = 0.001904195 + assert_equal(stat, 0) + assert_allclose(p, expected_p, rtol=1e-6) + + def test_onesided(self): + # tested against "R version 3.4.1 (2017-06-30)" + # x <- c(125, 115, 130, 140, 140, 115, 140, 125, 140, 135) + # y <- c(110, 122, 125, 120, 140, 124, 123, 137, 135, 145) + # cfg <- list(x = x, y = y, paired = TRUE, exact = FALSE) + # do.call(wilcox.test, c(cfg, list(alternative = "less", correct = FALSE))) + # do.call(wilcox.test, c(cfg, list(alternative = "less", correct = TRUE))) + # do.call(wilcox.test, c(cfg, list(alternative = "greater", correct = FALSE))) + # do.call(wilcox.test, c(cfg, list(alternative = "greater", correct = TRUE))) + x = [125, 115, 130, 140, 140, 115, 140, 125, 140, 135] + y = [110, 122, 125, 120, 140, 124, 123, 137, 135, 145] + + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Sample size too small") + w, p = stats.wilcoxon(x, y, alternative="less", mode="approx") + assert_equal(w, 27) + assert_almost_equal(p, 0.7031847, decimal=6) + + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Sample size too small") + w, p = stats.wilcoxon(x, y, alternative="less", correction=True, + mode="approx") + assert_equal(w, 27) + assert_almost_equal(p, 0.7233656, decimal=6) + + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Sample size too small") + w, p = stats.wilcoxon(x, y, alternative="greater", mode="approx") + assert_equal(w, 27) + assert_almost_equal(p, 0.2968153, decimal=6) + + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Sample size too small") + w, p = stats.wilcoxon(x, y, alternative="greater", correction=True, + mode="approx") + assert_equal(w, 27) + assert_almost_equal(p, 0.3176447, decimal=6) + + def test_exact_basic(self): + for n in range(1, 51): + pmf1 = _get_wilcoxon_distr(n) + pmf2 = _get_wilcoxon_distr2(n) + assert_equal(n*(n+1)/2 + 1, len(pmf1)) + assert_equal(sum(pmf1), 1) + assert_array_almost_equal(pmf1, pmf2) + + def test_exact_pval(self): + # expected values computed with "R version 3.4.1 (2017-06-30)" + x = np.array([1.81, 0.82, 1.56, -0.48, 0.81, 1.28, -1.04, 0.23, + -0.75, 0.14]) + y = np.array([0.71, 0.65, -0.2, 0.85, -1.1, -0.45, -0.84, -0.24, + -0.68, -0.76]) + _, p = stats.wilcoxon(x, y, alternative="two-sided", mode="exact") + assert_almost_equal(p, 0.1054688, decimal=6) + _, p = stats.wilcoxon(x, y, alternative="less", mode="exact") + assert_almost_equal(p, 0.9580078, decimal=6) + _, p = stats.wilcoxon(x, y, alternative="greater", mode="exact") + assert_almost_equal(p, 0.05273438, decimal=6) + + x = np.arange(0, 20) + 0.5 + y = np.arange(20, 0, -1) + _, p = stats.wilcoxon(x, y, alternative="two-sided", mode="exact") + assert_almost_equal(p, 0.8694878, decimal=6) + _, p = stats.wilcoxon(x, y, alternative="less", mode="exact") + assert_almost_equal(p, 0.4347439, decimal=6) + _, p = stats.wilcoxon(x, y, alternative="greater", mode="exact") + assert_almost_equal(p, 0.5795889, decimal=6) + + # These inputs were chosen to give a W statistic that is either the + # center of the distribution (when the length of the support is odd), or + # the value to the left of the center (when the length of the support is + # even). Also, the numbers are chosen so that the W statistic is the + # sum of the positive values. + + @pytest.mark.parametrize('x', [[-1, -2, 3], + [-1, 2, -3, -4, 5], + [-1, -2, 3, -4, -5, -6, 7, 8]]) + def test_exact_p_1(self, x): + w, p = stats.wilcoxon(x) + x = np.array(x) + wtrue = x[x > 0].sum() + assert_equal(w, wtrue) + assert_equal(p, 1) + + def test_auto(self): + # auto default to exact if there are no ties and n<= 25 + x = np.arange(0, 25) + 0.5 + y = np.arange(25, 0, -1) + assert_equal(stats.wilcoxon(x, y), + stats.wilcoxon(x, y, mode="exact")) + + # if there are ties (i.e. zeros in d = x-y), then switch to approx + d = np.arange(0, 13) + with suppress_warnings() as sup: + sup.filter(UserWarning, message="Exact p-value calculation") + w, p = stats.wilcoxon(d) + assert_equal(stats.wilcoxon(d, mode="approx"), (w, p)) + + # use approximation for samples > 25 + d = np.arange(1, 52) + assert_equal(stats.wilcoxon(d), stats.wilcoxon(d, mode="approx")) + + @pytest.mark.parametrize('size', [3, 5, 10]) + def test_permutation_method(self, size): + rng = np.random.default_rng(92348034828501345) + x = rng.random(size=size) + res = stats.wilcoxon(x, method=stats.PermutationMethod()) + ref = stats.wilcoxon(x, method='exact') + assert_equal(res.statistic, ref.statistic) + assert_equal(res.pvalue, ref.pvalue) + + x = rng.random(size=size*10) + rng = np.random.default_rng(59234803482850134) + pm = stats.PermutationMethod(n_resamples=99, random_state=rng) + ref = stats.wilcoxon(x, method=pm) + rng = np.random.default_rng(59234803482850134) + pm = stats.PermutationMethod(n_resamples=99, random_state=rng) + res = stats.wilcoxon(x, method=pm) + + assert_equal(np.round(res.pvalue, 2), res.pvalue) # n_resamples used + assert_equal(res.pvalue, ref.pvalue) # random_state used + + def test_method_auto_nan_propagate_ND_length_gt_50_gh20591(self): + # When method!='approx', nan_policy='propagate', and a slice of + # a >1 dimensional array input contained NaN, the result object of + # `wilcoxon` could (under yet other conditions) return `zstatistic` + # for some slices but not others. This resulted in an error because + # `apply_along_axis` would have to create a ragged array. + # Check that this is resolved. + rng = np.random.default_rng(235889269872456) + A = rng.normal(size=(51, 2)) # length along slice > exact threshold + A[5, 1] = np.nan + res = stats.wilcoxon(A) + ref = stats.wilcoxon(A, method='approx') + assert_allclose(res, ref) + assert hasattr(ref, 'zstatistic') + assert not hasattr(res, 'zstatistic') + + +class TestKstat: + def test_moments_normal_distribution(self): + np.random.seed(32149) + data = np.random.randn(12345) + moments = [stats.kstat(data, n) for n in [1, 2, 3, 4]] + + expected = [0.011315, 1.017931, 0.05811052, 0.0754134] + assert_allclose(moments, expected, rtol=1e-4) + + # test equivalence with `stats.moment` + m1 = stats.moment(data, order=1) + m2 = stats.moment(data, order=2) + m3 = stats.moment(data, order=3) + assert_allclose((m1, m2, m3), expected[:-1], atol=0.02, rtol=1e-2) + + def test_empty_input(self): + assert_raises(ValueError, stats.kstat, []) + + def test_nan_input(self): + data = np.arange(10.) + data[6] = np.nan + + assert_equal(stats.kstat(data), np.nan) + + def test_kstat_bad_arg(self): + # Raise ValueError if n > 4 or n < 1. + data = np.arange(10) + for n in [0, 4.001]: + assert_raises(ValueError, stats.kstat, data, n=n) + + +class TestKstatVar: + def test_empty_input(self): + assert_raises(ValueError, stats.kstatvar, []) + + def test_nan_input(self): + data = np.arange(10.) + data[6] = np.nan + + assert_equal(stats.kstat(data), np.nan) + + def test_bad_arg(self): + # Raise ValueError is n is not 1 or 2. + data = [1] + n = 10 + assert_raises(ValueError, stats.kstatvar, data, n=n) + + +class TestPpccPlot: + def setup_method(self): + self.x = _old_loggamma_rvs(5, size=500, random_state=7654321) + 5 + + def test_basic(self): + N = 5 + svals, ppcc = stats.ppcc_plot(self.x, -10, 10, N=N) + ppcc_expected = [0.21139644, 0.21384059, 0.98766719, 0.97980182, + 0.93519298] + assert_allclose(svals, np.linspace(-10, 10, num=N)) + assert_allclose(ppcc, ppcc_expected) + + def test_dist(self): + # Test that we can specify distributions both by name and as objects. + svals1, ppcc1 = stats.ppcc_plot(self.x, -10, 10, dist='tukeylambda') + svals2, ppcc2 = stats.ppcc_plot(self.x, -10, 10, + dist=stats.tukeylambda) + assert_allclose(svals1, svals2, rtol=1e-20) + assert_allclose(ppcc1, ppcc2, rtol=1e-20) + # Test that 'tukeylambda' is the default dist + svals3, ppcc3 = stats.ppcc_plot(self.x, -10, 10) + assert_allclose(svals1, svals3, rtol=1e-20) + assert_allclose(ppcc1, ppcc3, rtol=1e-20) + + @pytest.mark.skipif(not have_matplotlib, reason="no matplotlib") + def test_plot_kwarg(self): + # Check with the matplotlib.pyplot module + fig = plt.figure() + ax = fig.add_subplot(111) + stats.ppcc_plot(self.x, -20, 20, plot=plt) + fig.delaxes(ax) + + # Check that a Matplotlib Axes object is accepted + ax = fig.add_subplot(111) + stats.ppcc_plot(self.x, -20, 20, plot=ax) + plt.close() + + def test_invalid_inputs(self): + # `b` has to be larger than `a` + assert_raises(ValueError, stats.ppcc_plot, self.x, 1, 0) + + # Raise ValueError when given an invalid distribution. + assert_raises(ValueError, stats.ppcc_plot, [1, 2, 3], 0, 1, + dist="plate_of_shrimp") + + def test_empty(self): + # For consistency with probplot return for one empty array, + # ppcc contains all zeros and svals is the same as for normal array + # input. + svals, ppcc = stats.ppcc_plot([], 0, 1) + assert_allclose(svals, np.linspace(0, 1, num=80)) + assert_allclose(ppcc, np.zeros(80, dtype=float)) + + +class TestPpccMax: + def test_ppcc_max_bad_arg(self): + # Raise ValueError when given an invalid distribution. + data = [1] + assert_raises(ValueError, stats.ppcc_max, data, dist="plate_of_shrimp") + + def test_ppcc_max_basic(self): + x = stats.tukeylambda.rvs(-0.7, loc=2, scale=0.5, size=10000, + random_state=1234567) + 1e4 + assert_almost_equal(stats.ppcc_max(x), -0.71215366521264145, decimal=7) + + def test_dist(self): + x = stats.tukeylambda.rvs(-0.7, loc=2, scale=0.5, size=10000, + random_state=1234567) + 1e4 + + # Test that we can specify distributions both by name and as objects. + max1 = stats.ppcc_max(x, dist='tukeylambda') + max2 = stats.ppcc_max(x, dist=stats.tukeylambda) + assert_almost_equal(max1, -0.71215366521264145, decimal=5) + assert_almost_equal(max2, -0.71215366521264145, decimal=5) + + # Test that 'tukeylambda' is the default dist + max3 = stats.ppcc_max(x) + assert_almost_equal(max3, -0.71215366521264145, decimal=5) + + def test_brack(self): + x = stats.tukeylambda.rvs(-0.7, loc=2, scale=0.5, size=10000, + random_state=1234567) + 1e4 + assert_raises(ValueError, stats.ppcc_max, x, brack=(0.0, 1.0, 0.5)) + + assert_almost_equal(stats.ppcc_max(x, brack=(0, 1)), + -0.71215366521264145, decimal=7) + + assert_almost_equal(stats.ppcc_max(x, brack=(-2, 2)), + -0.71215366521264145, decimal=7) + + +class TestBoxcox_llf: + + def test_basic(self): + x = stats.norm.rvs(size=10000, loc=10, random_state=54321) + lmbda = 1 + llf = stats.boxcox_llf(lmbda, x) + llf_expected = -x.size / 2. * np.log(np.sum(x.std()**2)) + assert_allclose(llf, llf_expected) + + def test_array_like(self): + x = stats.norm.rvs(size=100, loc=10, random_state=54321) + lmbda = 1 + llf = stats.boxcox_llf(lmbda, x) + llf2 = stats.boxcox_llf(lmbda, list(x)) + assert_allclose(llf, llf2, rtol=1e-12) + + def test_2d_input(self): + # Note: boxcox_llf() was already working with 2-D input (sort of), so + # keep it like that. boxcox() doesn't work with 2-D input though, due + # to brent() returning a scalar. + x = stats.norm.rvs(size=100, loc=10, random_state=54321) + lmbda = 1 + llf = stats.boxcox_llf(lmbda, x) + llf2 = stats.boxcox_llf(lmbda, np.vstack([x, x]).T) + assert_allclose([llf, llf], llf2, rtol=1e-12) + + def test_empty(self): + assert_(np.isnan(stats.boxcox_llf(1, []))) + + def test_gh_6873(self): + # Regression test for gh-6873. + # This example was taken from gh-7534, a duplicate of gh-6873. + data = [198.0, 233.0, 233.0, 392.0] + llf = stats.boxcox_llf(-8, data) + # The expected value was computed with mpmath. + assert_allclose(llf, -17.93934208579061) + + def test_instability_gh20021(self): + data = [2003, 1950, 1997, 2000, 2009] + llf = stats.boxcox_llf(1e-8, data) + # The expected value was computed with mpsci, set mpmath.mp.dps=100 + assert_allclose(llf, -15.32401272869016598) + + +# This is the data from github user Qukaiyi, given as an example +# of a data set that caused boxcox to fail. +_boxcox_data = [ + 15957, 112079, 1039553, 711775, 173111, 307382, 183155, 53366, 760875, + 207500, 160045, 473714, 40194, 440319, 133261, 265444, 155590, 36660, + 904939, 55108, 138391, 339146, 458053, 63324, 1377727, 1342632, 41575, + 68685, 172755, 63323, 368161, 199695, 538214, 167760, 388610, 398855, + 1001873, 364591, 1320518, 194060, 194324, 2318551, 196114, 64225, 272000, + 198668, 123585, 86420, 1925556, 695798, 88664, 46199, 759135, 28051, + 345094, 1977752, 51778, 82746, 638126, 2560910, 45830, 140576, 1603787, + 57371, 548730, 5343629, 2298913, 998813, 2156812, 423966, 68350, 145237, + 131935, 1600305, 342359, 111398, 1409144, 281007, 60314, 242004, 113418, + 246211, 61940, 95858, 957805, 40909, 307955, 174159, 124278, 241193, + 872614, 304180, 146719, 64361, 87478, 509360, 167169, 933479, 620561, + 483333, 97416, 143518, 286905, 597837, 2556043, 89065, 69944, 196858, + 88883, 49379, 916265, 1527392, 626954, 54415, 89013, 2883386, 106096, + 402697, 45578, 349852, 140379, 34648, 757343, 1305442, 2054757, 121232, + 606048, 101492, 51426, 1820833, 83412, 136349, 1379924, 505977, 1303486, + 95853, 146451, 285422, 2205423, 259020, 45864, 684547, 182014, 784334, + 174793, 563068, 170745, 1195531, 63337, 71833, 199978, 2330904, 227335, + 898280, 75294, 2011361, 116771, 157489, 807147, 1321443, 1148635, 2456524, + 81839, 1228251, 97488, 1051892, 75397, 3009923, 2732230, 90923, 39735, + 132433, 225033, 337555, 1204092, 686588, 1062402, 40362, 1361829, 1497217, + 150074, 551459, 2019128, 39581, 45349, 1117187, 87845, 1877288, 164448, + 10338362, 24942, 64737, 769946, 2469124, 2366997, 259124, 2667585, 29175, + 56250, 74450, 96697, 5920978, 838375, 225914, 119494, 206004, 430907, + 244083, 219495, 322239, 407426, 618748, 2087536, 2242124, 4736149, 124624, + 406305, 240921, 2675273, 4425340, 821457, 578467, 28040, 348943, 48795, + 145531, 52110, 1645730, 1768364, 348363, 85042, 2673847, 81935, 169075, + 367733, 135474, 383327, 1207018, 93481, 5934183, 352190, 636533, 145870, + 55659, 146215, 73191, 248681, 376907, 1606620, 169381, 81164, 246390, + 236093, 885778, 335969, 49266, 381430, 307437, 350077, 34346, 49340, + 84715, 527120, 40163, 46898, 4609439, 617038, 2239574, 159905, 118337, + 120357, 430778, 3799158, 3516745, 54198, 2970796, 729239, 97848, 6317375, + 887345, 58198, 88111, 867595, 210136, 1572103, 1420760, 574046, 845988, + 509743, 397927, 1119016, 189955, 3883644, 291051, 126467, 1239907, 2556229, + 411058, 657444, 2025234, 1211368, 93151, 577594, 4842264, 1531713, 305084, + 479251, 20591, 1466166, 137417, 897756, 594767, 3606337, 32844, 82426, + 1294831, 57174, 290167, 322066, 813146, 5671804, 4425684, 895607, 450598, + 1048958, 232844, 56871, 46113, 70366, 701618, 97739, 157113, 865047, + 194810, 1501615, 1765727, 38125, 2733376, 40642, 437590, 127337, 106310, + 4167579, 665303, 809250, 1210317, 45750, 1853687, 348954, 156786, 90793, + 1885504, 281501, 3902273, 359546, 797540, 623508, 3672775, 55330, 648221, + 266831, 90030, 7118372, 735521, 1009925, 283901, 806005, 2434897, 94321, + 309571, 4213597, 2213280, 120339, 64403, 8155209, 1686948, 4327743, + 1868312, 135670, 3189615, 1569446, 706058, 58056, 2438625, 520619, 105201, + 141961, 179990, 1351440, 3148662, 2804457, 2760144, 70775, 33807, 1926518, + 2362142, 186761, 240941, 97860, 1040429, 1431035, 78892, 484039, 57845, + 724126, 3166209, 175913, 159211, 1182095, 86734, 1921472, 513546, 326016, + 1891609 +] + + +class TestBoxcox: + + def test_fixed_lmbda(self): + x = _old_loggamma_rvs(5, size=50, random_state=12345) + 5 + xt = stats.boxcox(x, lmbda=1) + assert_allclose(xt, x - 1) + xt = stats.boxcox(x, lmbda=-1) + assert_allclose(xt, 1 - 1/x) + + xt = stats.boxcox(x, lmbda=0) + assert_allclose(xt, np.log(x)) + + # Also test that array_like input works + xt = stats.boxcox(list(x), lmbda=0) + assert_allclose(xt, np.log(x)) + + # test that constant input is accepted; see gh-12225 + xt = stats.boxcox(np.ones(10), 2) + assert_equal(xt, np.zeros(10)) + + def test_lmbda_None(self): + # Start from normal rv's, do inverse transform to check that + # optimization function gets close to the right answer. + lmbda = 2.5 + x = stats.norm.rvs(loc=10, size=50000, random_state=1245) + x_inv = (x * lmbda + 1)**(-lmbda) + xt, maxlog = stats.boxcox(x_inv) + + assert_almost_equal(maxlog, -1 / lmbda, decimal=2) + + def test_alpha(self): + rng = np.random.RandomState(1234) + x = _old_loggamma_rvs(5, size=50, random_state=rng) + 5 + + # Some regular values for alpha, on a small sample size + _, _, interval = stats.boxcox(x, alpha=0.75) + assert_allclose(interval, [4.004485780226041, 5.138756355035744]) + _, _, interval = stats.boxcox(x, alpha=0.05) + assert_allclose(interval, [1.2138178554857557, 8.209033272375663]) + + # Try some extreme values, see we don't hit the N=500 limit + x = _old_loggamma_rvs(7, size=500, random_state=rng) + 15 + _, _, interval = stats.boxcox(x, alpha=0.001) + assert_allclose(interval, [0.3988867, 11.40553131]) + _, _, interval = stats.boxcox(x, alpha=0.999) + assert_allclose(interval, [5.83316246, 5.83735292]) + + def test_boxcox_bad_arg(self): + # Raise ValueError if any data value is negative. + x = np.array([-1, 2]) + assert_raises(ValueError, stats.boxcox, x) + # Raise ValueError if data is constant. + assert_raises(ValueError, stats.boxcox, np.array([1])) + # Raise ValueError if data is not 1-dimensional. + assert_raises(ValueError, stats.boxcox, np.array([[1], [2]])) + + def test_empty(self): + assert_(stats.boxcox([]).shape == (0,)) + + def test_gh_6873(self): + # Regression test for gh-6873. + y, lam = stats.boxcox(_boxcox_data) + # The expected value of lam was computed with the function + # powerTransform in the R library 'car'. I trust that value + # to only about five significant digits. + assert_allclose(lam, -0.051654, rtol=1e-5) + + @pytest.mark.parametrize("bounds", [(-1, 1), (1.1, 2), (-2, -1.1)]) + def test_bounded_optimizer_within_bounds(self, bounds): + # Define custom optimizer with bounds. + def optimizer(fun): + return optimize.minimize_scalar(fun, bounds=bounds, + method="bounded") + + _, lmbda = stats.boxcox(_boxcox_data, lmbda=None, optimizer=optimizer) + assert bounds[0] < lmbda < bounds[1] + + def test_bounded_optimizer_against_unbounded_optimizer(self): + # Test whether setting bounds on optimizer excludes solution from + # unbounded optimizer. + + # Get unbounded solution. + _, lmbda = stats.boxcox(_boxcox_data, lmbda=None) + + # Set tolerance and bounds around solution. + bounds = (lmbda + 0.1, lmbda + 1) + options = {'xatol': 1e-12} + + def optimizer(fun): + return optimize.minimize_scalar(fun, bounds=bounds, + method="bounded", options=options) + + # Check bounded solution. Lower bound should be active. + _, lmbda_bounded = stats.boxcox(_boxcox_data, lmbda=None, + optimizer=optimizer) + assert lmbda_bounded != lmbda + assert_allclose(lmbda_bounded, bounds[0]) + + @pytest.mark.parametrize("optimizer", ["str", (1, 2), 0.1]) + def test_bad_optimizer_type_raises_error(self, optimizer): + # Check if error is raised if string, tuple or float is passed + with pytest.raises(ValueError, match="`optimizer` must be a callable"): + stats.boxcox(_boxcox_data, lmbda=None, optimizer=optimizer) + + def test_bad_optimizer_value_raises_error(self): + # Check if error is raised if `optimizer` function does not return + # `OptimizeResult` object + + # Define test function that always returns 1 + def optimizer(fun): + return 1 + + message = "return an object containing the optimal `lmbda`" + with pytest.raises(ValueError, match=message): + stats.boxcox(_boxcox_data, lmbda=None, optimizer=optimizer) + + @pytest.mark.parametrize( + "bad_x", [np.array([1, -42, 12345.6]), np.array([np.nan, 42, 1])] + ) + def test_negative_x_value_raises_error(self, bad_x): + """Test boxcox_normmax raises ValueError if x contains non-positive values.""" + message = "only positive, finite, real numbers" + with pytest.raises(ValueError, match=message): + stats.boxcox_normmax(bad_x) + + @pytest.mark.parametrize('x', [ + # Attempt to trigger overflow in power expressions. + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0, + 2009.0, 1980.0, 1999.0, 2007.0, 1991.0]), + # Attempt to trigger overflow with a large optimal lambda. + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0]), + # Attempt to trigger overflow with large data. + np.array([2003.0e200, 1950.0e200, 1997.0e200, 2000.0e200, 2009.0e200]) + ]) + def test_overflow(self, x): + with pytest.warns(UserWarning, match="The optimal lambda is"): + xt_bc, lam_bc = stats.boxcox(x) + assert np.all(np.isfinite(xt_bc)) + + +class TestBoxcoxNormmax: + def setup_method(self): + self.x = _old_loggamma_rvs(5, size=50, random_state=12345) + 5 + + def test_pearsonr(self): + maxlog = stats.boxcox_normmax(self.x) + assert_allclose(maxlog, 1.804465, rtol=1e-6) + + def test_mle(self): + maxlog = stats.boxcox_normmax(self.x, method='mle') + assert_allclose(maxlog, 1.758101, rtol=1e-6) + + # Check that boxcox() uses 'mle' + _, maxlog_boxcox = stats.boxcox(self.x) + assert_allclose(maxlog_boxcox, maxlog) + + def test_all(self): + maxlog_all = stats.boxcox_normmax(self.x, method='all') + assert_allclose(maxlog_all, [1.804465, 1.758101], rtol=1e-6) + + @pytest.mark.parametrize("method", ["mle", "pearsonr", "all"]) + @pytest.mark.parametrize("bounds", [(-1, 1), (1.1, 2), (-2, -1.1)]) + def test_bounded_optimizer_within_bounds(self, method, bounds): + + def optimizer(fun): + return optimize.minimize_scalar(fun, bounds=bounds, + method="bounded") + + maxlog = stats.boxcox_normmax(self.x, method=method, + optimizer=optimizer) + assert np.all(bounds[0] < maxlog) + assert np.all(maxlog < bounds[1]) + + def test_user_defined_optimizer(self): + # tests an optimizer that is not based on scipy.optimize.minimize + lmbda = stats.boxcox_normmax(self.x) + lmbda_rounded = np.round(lmbda, 5) + lmbda_range = np.linspace(lmbda_rounded-0.01, lmbda_rounded+0.01, 1001) + + class MyResult: + pass + + def optimizer(fun): + # brute force minimum over the range + objs = [] + for lmbda in lmbda_range: + objs.append(fun(lmbda)) + res = MyResult() + res.x = lmbda_range[np.argmin(objs)] + return res + + lmbda2 = stats.boxcox_normmax(self.x, optimizer=optimizer) + assert lmbda2 != lmbda # not identical + assert_allclose(lmbda2, lmbda, 1e-5) # but as close as it should be + + def test_user_defined_optimizer_and_brack_raises_error(self): + optimizer = optimize.minimize_scalar + + # Using default `brack=None` with user-defined `optimizer` works as + # expected. + stats.boxcox_normmax(self.x, brack=None, optimizer=optimizer) + + # Using user-defined `brack` with user-defined `optimizer` is expected + # to throw an error. Instead, users should specify + # optimizer-specific parameters in the optimizer function itself. + with pytest.raises(ValueError, match="`brack` must be None if " + "`optimizer` is given"): + + stats.boxcox_normmax(self.x, brack=(-2.0, 2.0), + optimizer=optimizer) + + @pytest.mark.parametrize( + 'x', ([2003.0, 1950.0, 1997.0, 2000.0, 2009.0], + [0.50000471, 0.50004979, 0.50005902, 0.50009312, 0.50001632])) + def test_overflow(self, x): + message = "The optimal lambda is..." + with pytest.warns(UserWarning, match=message): + lmbda = stats.boxcox_normmax(x, method='mle') + assert np.isfinite(special.boxcox(x, lmbda)).all() + # 10000 is safety factor used in boxcox_normmax + ymax = np.finfo(np.float64).max / 10000 + x_treme = np.max(x) if lmbda > 0 else np.min(x) + y_extreme = special.boxcox(x_treme, lmbda) + assert_allclose(y_extreme, ymax * np.sign(lmbda)) + + def test_negative_ymax(self): + with pytest.raises(ValueError, match="`ymax` must be strictly positive"): + stats.boxcox_normmax(self.x, ymax=-1) + + @pytest.mark.parametrize("x", [ + # positive overflow in float64 + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0], + dtype=np.float64), + # negative overflow in float64 + np.array([0.50000471, 0.50004979, 0.50005902, 0.50009312, 0.50001632], + dtype=np.float64), + # positive overflow in float32 + np.array([200.3, 195.0, 199.7, 200.0, 200.9], + dtype=np.float32), + # negative overflow in float32 + np.array([2e-30, 1e-30, 1e-30, 1e-30, 1e-30, 1e-30], + dtype=np.float32), + ]) + @pytest.mark.parametrize("ymax", [1e10, 1e30, None]) + # TODO: add method "pearsonr" after fix overflow issue + @pytest.mark.parametrize("method", ["mle"]) + def test_user_defined_ymax_input_float64_32(self, x, ymax, method): + # Test the maximum of the transformed data close to ymax + with pytest.warns(UserWarning, match="The optimal lambda is"): + kwarg = {'ymax': ymax} if ymax is not None else {} + lmb = stats.boxcox_normmax(x, method=method, **kwarg) + x_treme = [np.min(x), np.max(x)] + ymax_res = max(abs(stats.boxcox(x_treme, lmb))) + if ymax is None: + # 10000 is safety factor used in boxcox_normmax + ymax = np.finfo(x.dtype).max / 10000 + assert_allclose(ymax, ymax_res, rtol=1e-5) + + @pytest.mark.parametrize("x", [ + # positive overflow in float32 but not float64 + [200.3, 195.0, 199.7, 200.0, 200.9], + # negative overflow in float32 but not float64 + [2e-30, 1e-30, 1e-30, 1e-30, 1e-30, 1e-30], + ]) + # TODO: add method "pearsonr" after fix overflow issue + @pytest.mark.parametrize("method", ["mle"]) + def test_user_defined_ymax_inf(self, x, method): + x_32 = np.asarray(x, dtype=np.float32) + x_64 = np.asarray(x, dtype=np.float64) + + # assert overflow with float32 but not float64 + with pytest.warns(UserWarning, match="The optimal lambda is"): + stats.boxcox_normmax(x_32, method=method) + stats.boxcox_normmax(x_64, method=method) + + # compute the true optimal lambda then compare them + lmb_32 = stats.boxcox_normmax(x_32, ymax=np.inf, method=method) + lmb_64 = stats.boxcox_normmax(x_64, ymax=np.inf, method=method) + assert_allclose(lmb_32, lmb_64, rtol=1e-2) + + +class TestBoxcoxNormplot: + def setup_method(self): + self.x = _old_loggamma_rvs(5, size=500, random_state=7654321) + 5 + + def test_basic(self): + N = 5 + lmbdas, ppcc = stats.boxcox_normplot(self.x, -10, 10, N=N) + ppcc_expected = [0.57783375, 0.83610988, 0.97524311, 0.99756057, + 0.95843297] + assert_allclose(lmbdas, np.linspace(-10, 10, num=N)) + assert_allclose(ppcc, ppcc_expected) + + @pytest.mark.skipif(not have_matplotlib, reason="no matplotlib") + def test_plot_kwarg(self): + # Check with the matplotlib.pyplot module + fig = plt.figure() + ax = fig.add_subplot(111) + stats.boxcox_normplot(self.x, -20, 20, plot=plt) + fig.delaxes(ax) + + # Check that a Matplotlib Axes object is accepted + ax = fig.add_subplot(111) + stats.boxcox_normplot(self.x, -20, 20, plot=ax) + plt.close() + + def test_invalid_inputs(self): + # `lb` has to be larger than `la` + assert_raises(ValueError, stats.boxcox_normplot, self.x, 1, 0) + # `x` can not contain negative values + assert_raises(ValueError, stats.boxcox_normplot, [-1, 1], 0, 1) + + def test_empty(self): + assert_(stats.boxcox_normplot([], 0, 1).size == 0) + + +class TestYeojohnson_llf: + + def test_array_like(self): + x = stats.norm.rvs(size=100, loc=0, random_state=54321) + lmbda = 1 + llf = stats.yeojohnson_llf(lmbda, x) + llf2 = stats.yeojohnson_llf(lmbda, list(x)) + assert_allclose(llf, llf2, rtol=1e-12) + + def test_2d_input(self): + x = stats.norm.rvs(size=100, loc=10, random_state=54321) + lmbda = 1 + llf = stats.yeojohnson_llf(lmbda, x) + llf2 = stats.yeojohnson_llf(lmbda, np.vstack([x, x]).T) + assert_allclose([llf, llf], llf2, rtol=1e-12) + + def test_empty(self): + assert_(np.isnan(stats.yeojohnson_llf(1, []))) + + +class TestYeojohnson: + + def test_fixed_lmbda(self): + rng = np.random.RandomState(12345) + + # Test positive input + x = _old_loggamma_rvs(5, size=50, random_state=rng) + 5 + assert np.all(x > 0) + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt, x) + xt = stats.yeojohnson(x, lmbda=-1) + assert_allclose(xt, 1 - 1 / (x + 1)) + xt = stats.yeojohnson(x, lmbda=0) + assert_allclose(xt, np.log(x + 1)) + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt, x) + + # Test negative input + x = _old_loggamma_rvs(5, size=50, random_state=rng) - 5 + assert np.all(x < 0) + xt = stats.yeojohnson(x, lmbda=2) + assert_allclose(xt, -np.log(-x + 1)) + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt, x) + xt = stats.yeojohnson(x, lmbda=3) + assert_allclose(xt, 1 / (-x + 1) - 1) + + # test both positive and negative input + x = _old_loggamma_rvs(5, size=50, random_state=rng) - 2 + assert not np.all(x < 0) + assert not np.all(x >= 0) + pos = x >= 0 + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt[pos], x[pos]) + xt = stats.yeojohnson(x, lmbda=-1) + assert_allclose(xt[pos], 1 - 1 / (x[pos] + 1)) + xt = stats.yeojohnson(x, lmbda=0) + assert_allclose(xt[pos], np.log(x[pos] + 1)) + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt[pos], x[pos]) + + neg = ~pos + xt = stats.yeojohnson(x, lmbda=2) + assert_allclose(xt[neg], -np.log(-x[neg] + 1)) + xt = stats.yeojohnson(x, lmbda=1) + assert_allclose(xt[neg], x[neg]) + xt = stats.yeojohnson(x, lmbda=3) + assert_allclose(xt[neg], 1 / (-x[neg] + 1) - 1) + + @pytest.mark.parametrize('lmbda', [0, .1, .5, 2]) + def test_lmbda_None(self, lmbda): + # Start from normal rv's, do inverse transform to check that + # optimization function gets close to the right answer. + + def _inverse_transform(x, lmbda): + x_inv = np.zeros(x.shape, dtype=x.dtype) + pos = x >= 0 + + # when x >= 0 + if abs(lmbda) < np.spacing(1.): + x_inv[pos] = np.exp(x[pos]) - 1 + else: # lmbda != 0 + x_inv[pos] = np.power(x[pos] * lmbda + 1, 1 / lmbda) - 1 + + # when x < 0 + if abs(lmbda - 2) > np.spacing(1.): + x_inv[~pos] = 1 - np.power(-(2 - lmbda) * x[~pos] + 1, + 1 / (2 - lmbda)) + else: # lmbda == 2 + x_inv[~pos] = 1 - np.exp(-x[~pos]) + + return x_inv + + n_samples = 20000 + np.random.seed(1234567) + x = np.random.normal(loc=0, scale=1, size=(n_samples)) + + x_inv = _inverse_transform(x, lmbda) + xt, maxlog = stats.yeojohnson(x_inv) + + assert_allclose(maxlog, lmbda, atol=1e-2) + + assert_almost_equal(0, np.linalg.norm(x - xt) / n_samples, decimal=2) + assert_almost_equal(0, xt.mean(), decimal=1) + assert_almost_equal(1, xt.std(), decimal=1) + + def test_empty(self): + assert_(stats.yeojohnson([]).shape == (0,)) + + def test_array_like(self): + x = stats.norm.rvs(size=100, loc=0, random_state=54321) + xt1, _ = stats.yeojohnson(x) + xt2, _ = stats.yeojohnson(list(x)) + assert_allclose(xt1, xt2, rtol=1e-12) + + @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) + def test_input_dtype_complex(self, dtype): + x = np.arange(6, dtype=dtype) + err_msg = ('Yeo-Johnson transformation is not defined for complex ' + 'numbers.') + with pytest.raises(ValueError, match=err_msg): + stats.yeojohnson(x) + + @pytest.mark.parametrize('dtype', [np.int8, np.uint8, np.int16, np.int32]) + def test_input_dtype_integer(self, dtype): + x_int = np.arange(8, dtype=dtype) + x_float = np.arange(8, dtype=np.float64) + xt_int, lmbda_int = stats.yeojohnson(x_int) + xt_float, lmbda_float = stats.yeojohnson(x_float) + assert_allclose(xt_int, xt_float, rtol=1e-7) + assert_allclose(lmbda_int, lmbda_float, rtol=1e-7) + + def test_input_high_variance(self): + # non-regression test for gh-10821 + x = np.array([3251637.22, 620695.44, 11642969.00, 2223468.22, + 85307500.00, 16494389.89, 917215.88, 11642969.00, + 2145773.87, 4962000.00, 620695.44, 651234.50, + 1907876.71, 4053297.88, 3251637.22, 3259103.08, + 9547969.00, 20631286.23, 12807072.08, 2383819.84, + 90114500.00, 17209575.46, 12852969.00, 2414609.99, + 2170368.23]) + xt_yeo, lam_yeo = stats.yeojohnson(x) + xt_box, lam_box = stats.boxcox(x + 1) + assert_allclose(xt_yeo, xt_box, rtol=1e-6) + assert_allclose(lam_yeo, lam_box, rtol=1e-6) + + @pytest.mark.parametrize('x', [ + np.array([1.0, float("nan"), 2.0]), + np.array([1.0, float("inf"), 2.0]), + np.array([1.0, -float("inf"), 2.0]), + np.array([-1.0, float("nan"), float("inf"), -float("inf"), 1.0]) + ]) + def test_nonfinite_input(self, x): + with pytest.raises(ValueError, match='Yeo-Johnson input must be finite'): + xt_yeo, lam_yeo = stats.yeojohnson(x) + + @pytest.mark.parametrize('x', [ + # Attempt to trigger overflow in power expressions. + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0, + 2009.0, 1980.0, 1999.0, 2007.0, 1991.0]), + # Attempt to trigger overflow with a large optimal lambda. + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0]), + # Attempt to trigger overflow with large data. + np.array([2003.0e200, 1950.0e200, 1997.0e200, 2000.0e200, 2009.0e200]) + ]) + def test_overflow(self, x): + # non-regression test for gh-18389 + + def optimizer(fun, lam_yeo): + out = optimize.fminbound(fun, -lam_yeo, lam_yeo, xtol=1.48e-08) + result = optimize.OptimizeResult() + result.x = out + return result + + with np.errstate(all="raise"): + xt_yeo, lam_yeo = stats.yeojohnson(x) + xt_box, lam_box = stats.boxcox( + x + 1, optimizer=partial(optimizer, lam_yeo=lam_yeo)) + assert np.isfinite(np.var(xt_yeo)) + assert np.isfinite(np.var(xt_box)) + assert_allclose(lam_yeo, lam_box, rtol=1e-6) + assert_allclose(xt_yeo, xt_box, rtol=1e-4) + + @pytest.mark.parametrize('x', [ + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0, + 2009.0, 1980.0, 1999.0, 2007.0, 1991.0]), + np.array([2003.0, 1950.0, 1997.0, 2000.0, 2009.0]) + ]) + @pytest.mark.parametrize('scale', [1, 1e-12, 1e-32, 1e-150, 1e32, 1e200]) + @pytest.mark.parametrize('sign', [1, -1]) + def test_overflow_underflow_signed_data(self, x, scale, sign): + # non-regression test for gh-18389 + with np.errstate(all="raise"): + xt_yeo, lam_yeo = stats.yeojohnson(sign * x * scale) + assert np.all(np.sign(sign * x) == np.sign(xt_yeo)) + assert np.isfinite(lam_yeo) + assert np.isfinite(np.var(xt_yeo)) + + @pytest.mark.parametrize('x', [ + np.array([0, 1, 2, 3]), + np.array([0, -1, 2, -3]), + np.array([0, 0, 0]) + ]) + @pytest.mark.parametrize('sign', [1, -1]) + @pytest.mark.parametrize('brack', [None, (-2, 2)]) + def test_integer_signed_data(self, x, sign, brack): + with np.errstate(all="raise"): + x_int = sign * x + x_float = x_int.astype(np.float64) + lam_yeo_int = stats.yeojohnson_normmax(x_int, brack=brack) + xt_yeo_int = stats.yeojohnson(x_int, lmbda=lam_yeo_int) + lam_yeo_float = stats.yeojohnson_normmax(x_float, brack=brack) + xt_yeo_float = stats.yeojohnson(x_float, lmbda=lam_yeo_float) + assert np.all(np.sign(x_int) == np.sign(xt_yeo_int)) + assert np.isfinite(lam_yeo_int) + assert np.isfinite(np.var(xt_yeo_int)) + assert lam_yeo_int == lam_yeo_float + assert np.all(xt_yeo_int == xt_yeo_float) + + +class TestYeojohnsonNormmax: + def setup_method(self): + self.x = _old_loggamma_rvs(5, size=50, random_state=12345) + 5 + + def test_mle(self): + maxlog = stats.yeojohnson_normmax(self.x) + assert_allclose(maxlog, 1.876393, rtol=1e-6) + + def test_darwin_example(self): + # test from original paper "A new family of power transformations to + # improve normality or symmetry" by Yeo and Johnson. + x = [6.1, -8.4, 1.0, 2.0, 0.7, 2.9, 3.5, 5.1, 1.8, 3.6, 7.0, 3.0, 9.3, + 7.5, -6.0] + lmbda = stats.yeojohnson_normmax(x) + assert np.allclose(lmbda, 1.305, atol=1e-3) + + +class TestCircFuncs: + # In gh-5747, the R package `circular` was used to calculate reference + # values for the circular variance, e.g.: + # library(circular) + # options(digits=16) + # x = c(0, 2*pi/3, 5*pi/3) + # var.circular(x) + @pytest.mark.parametrize("test_func,expected", + [(stats.circmean, 0.167690146), + (stats.circvar, 0.006455174270186603), + (stats.circstd, 6.520702116)]) + def test_circfuncs(self, test_func, expected): + x = np.array([355, 5, 2, 359, 10, 350]) + assert_allclose(test_func(x, high=360), expected, rtol=1e-7) + + def test_circfuncs_small(self): + x = np.array([20, 21, 22, 18, 19, 20.5, 19.2]) + M1 = x.mean() + M2 = stats.circmean(x, high=360) + assert_allclose(M2, M1, rtol=1e-5) + + V1 = (x*np.pi/180).var() + # for small variations, circvar is approximately half the + # linear variance + V1 = V1 / 2. + V2 = stats.circvar(x, high=360) + assert_allclose(V2, V1, rtol=1e-4) + + S1 = x.std() + S2 = stats.circstd(x, high=360) + assert_allclose(S2, S1, rtol=1e-4) + + @pytest.mark.parametrize("test_func, numpy_func", + [(stats.circmean, np.mean), + (stats.circvar, np.var), + (stats.circstd, np.std)]) + def test_circfuncs_close(self, test_func, numpy_func): + # circfuncs should handle very similar inputs (gh-12740) + x = np.array([0.12675364631578953] * 10 + [0.12675365920187928] * 100) + circstat = test_func(x) + normal = numpy_func(x) + assert_allclose(circstat, normal, atol=2e-8) + + def test_circmean_axis(self): + x = np.array([[355, 5, 2, 359, 10, 350], + [351, 7, 4, 352, 9, 349], + [357, 9, 8, 358, 4, 356]]) + M1 = stats.circmean(x, high=360) + M2 = stats.circmean(x.ravel(), high=360) + assert_allclose(M1, M2, rtol=1e-14) + + M1 = stats.circmean(x, high=360, axis=1) + M2 = [stats.circmean(x[i], high=360) for i in range(x.shape[0])] + assert_allclose(M1, M2, rtol=1e-14) + + M1 = stats.circmean(x, high=360, axis=0) + M2 = [stats.circmean(x[:, i], high=360) for i in range(x.shape[1])] + assert_allclose(M1, M2, rtol=1e-14) + + def test_circvar_axis(self): + x = np.array([[355, 5, 2, 359, 10, 350], + [351, 7, 4, 352, 9, 349], + [357, 9, 8, 358, 4, 356]]) + + V1 = stats.circvar(x, high=360) + V2 = stats.circvar(x.ravel(), high=360) + assert_allclose(V1, V2, rtol=1e-11) + + V1 = stats.circvar(x, high=360, axis=1) + V2 = [stats.circvar(x[i], high=360) for i in range(x.shape[0])] + assert_allclose(V1, V2, rtol=1e-11) + + V1 = stats.circvar(x, high=360, axis=0) + V2 = [stats.circvar(x[:, i], high=360) for i in range(x.shape[1])] + assert_allclose(V1, V2, rtol=1e-11) + + def test_circstd_axis(self): + x = np.array([[355, 5, 2, 359, 10, 350], + [351, 7, 4, 352, 9, 349], + [357, 9, 8, 358, 4, 356]]) + + S1 = stats.circstd(x, high=360) + S2 = stats.circstd(x.ravel(), high=360) + assert_allclose(S1, S2, rtol=1e-11) + + S1 = stats.circstd(x, high=360, axis=1) + S2 = [stats.circstd(x[i], high=360) for i in range(x.shape[0])] + assert_allclose(S1, S2, rtol=1e-11) + + S1 = stats.circstd(x, high=360, axis=0) + S2 = [stats.circstd(x[:, i], high=360) for i in range(x.shape[1])] + assert_allclose(S1, S2, rtol=1e-11) + + @pytest.mark.parametrize("test_func,expected", + [(stats.circmean, 0.167690146), + (stats.circvar, 0.006455174270186603), + (stats.circstd, 6.520702116)]) + def test_circfuncs_array_like(self, test_func, expected): + x = [355, 5, 2, 359, 10, 350] + assert_allclose(test_func(x, high=360), expected, rtol=1e-7) + + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_empty(self, test_func): + assert_(np.isnan(test_func([]))) + + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_nan_propagate(self, test_func): + x = [355, 5, 2, 359, 10, 350, np.nan] + assert_(np.isnan(test_func(x, high=360))) + + @pytest.mark.parametrize("test_func,expected", + [(stats.circmean, + {None: np.nan, 0: 355.66582264, 1: 0.28725053}), + (stats.circvar, + {None: np.nan, + 0: 0.002570671054089924, + 1: 0.005545914017677123}), + (stats.circstd, + {None: np.nan, 0: 4.11093193, 1: 6.04265394})]) + def test_nan_propagate_array(self, test_func, expected): + x = np.array([[355, 5, 2, 359, 10, 350, 1], + [351, 7, 4, 352, 9, 349, np.nan], + [1, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]]) + for axis in expected.keys(): + out = test_func(x, high=360, axis=axis) + if axis is None: + assert_(np.isnan(out)) + else: + assert_allclose(out[0], expected[axis], rtol=1e-7) + assert_(np.isnan(out[1:]).all()) + + @pytest.mark.parametrize("test_func,expected", + [(stats.circmean, + {None: 359.4178026893944, + 0: np.array([353.0, 6.0, 3.0, 355.5, 9.5, + 349.5]), + 1: np.array([0.16769015, 358.66510252])}), + (stats.circvar, + {None: 0.008396678483192477, + 0: np.array([1.9997969, 0.4999873, 0.4999873, + 6.1230956, 0.1249992, 0.1249992] + )*(np.pi/180)**2, + 1: np.array([0.006455174270186603, + 0.01016767581393285])}), + (stats.circstd, + {None: 7.440570778057074, + 0: np.array([2.00020313, 1.00002539, 1.00002539, + 3.50108929, 0.50000317, + 0.50000317]), + 1: np.array([6.52070212, 8.19138093])})]) + def test_nan_omit_array(self, test_func, expected): + x = np.array([[355, 5, 2, 359, 10, 350, np.nan], + [351, 7, 4, 352, 9, 349, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]]) + for axis in expected.keys(): + out = test_func(x, high=360, nan_policy='omit', axis=axis) + if axis is None: + assert_allclose(out, expected[axis], rtol=1e-7) + else: + assert_allclose(out[:-1], expected[axis], rtol=1e-7) + assert_(np.isnan(out[-1])) + + @pytest.mark.parametrize("test_func,expected", + [(stats.circmean, 0.167690146), + (stats.circvar, 0.006455174270186603), + (stats.circstd, 6.520702116)]) + def test_nan_omit(self, test_func, expected): + x = [355, 5, 2, 359, 10, 350, np.nan] + assert_allclose(test_func(x, high=360, nan_policy='omit'), + expected, rtol=1e-7) + + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_nan_omit_all(self, test_func): + x = [np.nan, np.nan, np.nan, np.nan, np.nan] + assert_(np.isnan(test_func(x, nan_policy='omit'))) + + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_nan_omit_all_axis(self, test_func): + x = np.array([[np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan]]) + out = test_func(x, nan_policy='omit', axis=1) + assert_(np.isnan(out).all()) + assert_(len(out) == 2) + + @pytest.mark.parametrize("x", + [[355, 5, 2, 359, 10, 350, np.nan], + np.array([[355, 5, 2, 359, 10, 350, np.nan], + [351, 7, 4, 352, np.nan, 9, 349]])]) + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_nan_raise(self, test_func, x): + assert_raises(ValueError, test_func, x, high=360, nan_policy='raise') + + @pytest.mark.parametrize("x", + [[355, 5, 2, 359, 10, 350, np.nan], + np.array([[355, 5, 2, 359, 10, 350, np.nan], + [351, 7, 4, 352, np.nan, 9, 349]])]) + @pytest.mark.parametrize("test_func", [stats.circmean, stats.circvar, + stats.circstd]) + def test_bad_nan_policy(self, test_func, x): + assert_raises(ValueError, test_func, x, high=360, nan_policy='foobar') + + def test_circmean_scalar(self): + x = 1. + M1 = x + M2 = stats.circmean(x) + assert_allclose(M2, M1, rtol=1e-5) + + def test_circmean_range(self): + # regression test for gh-6420: circmean(..., high, low) must be + # between `high` and `low` + m = stats.circmean(np.arange(0, 2, 0.1), np.pi, -np.pi) + assert_(m < np.pi) + assert_(m > -np.pi) + + def test_circfuncs_uint8(self): + # regression test for gh-7255: overflow when working with + # numpy uint8 data type + x = np.array([150, 10], dtype='uint8') + assert_equal(stats.circmean(x, high=180), 170.0) + assert_allclose(stats.circvar(x, high=180), 0.2339555554617, rtol=1e-7) + assert_allclose(stats.circstd(x, high=180), 20.91551378, rtol=1e-7) + + +class TestMedianTest: + + def test_bad_n_samples(self): + # median_test requires at least two samples. + assert_raises(ValueError, stats.median_test, [1, 2, 3]) + + def test_empty_sample(self): + # Each sample must contain at least one value. + assert_raises(ValueError, stats.median_test, [], [1, 2, 3]) + + def test_empty_when_ties_ignored(self): + # The grand median is 1, and all values in the first argument are + # equal to the grand median. With ties="ignore", those values are + # ignored, which results in the first sample being (in effect) empty. + # This should raise a ValueError. + assert_raises(ValueError, stats.median_test, + [1, 1, 1, 1], [2, 0, 1], [2, 0], ties="ignore") + + def test_empty_contingency_row(self): + # The grand median is 1, and with the default ties="below", all the + # values in the samples are counted as being below the grand median. + # This would result a row of zeros in the contingency table, which is + # an error. + assert_raises(ValueError, stats.median_test, [1, 1, 1], [1, 1, 1]) + + # With ties="above", all the values are counted as above the + # grand median. + assert_raises(ValueError, stats.median_test, [1, 1, 1], [1, 1, 1], + ties="above") + + def test_bad_ties(self): + assert_raises(ValueError, stats.median_test, [1, 2, 3], [4, 5], + ties="foo") + + def test_bad_nan_policy(self): + assert_raises(ValueError, stats.median_test, [1, 2, 3], [4, 5], + nan_policy='foobar') + + def test_bad_keyword(self): + assert_raises(TypeError, stats.median_test, [1, 2, 3], [4, 5], + foo="foo") + + def test_simple(self): + x = [1, 2, 3] + y = [1, 2, 3] + stat, p, med, tbl = stats.median_test(x, y) + + # The median is floating point, but this equality test should be safe. + assert_equal(med, 2.0) + + assert_array_equal(tbl, [[1, 1], [2, 2]]) + + # The expected values of the contingency table equal the contingency + # table, so the statistic should be 0 and the p-value should be 1. + assert_equal(stat, 0) + assert_equal(p, 1) + + def test_ties_options(self): + # Test the contingency table calculation. + x = [1, 2, 3, 4] + y = [5, 6] + z = [7, 8, 9] + # grand median is 5. + + # Default 'ties' option is "below". + stat, p, m, tbl = stats.median_test(x, y, z) + assert_equal(m, 5) + assert_equal(tbl, [[0, 1, 3], [4, 1, 0]]) + + stat, p, m, tbl = stats.median_test(x, y, z, ties="ignore") + assert_equal(m, 5) + assert_equal(tbl, [[0, 1, 3], [4, 0, 0]]) + + stat, p, m, tbl = stats.median_test(x, y, z, ties="above") + assert_equal(m, 5) + assert_equal(tbl, [[0, 2, 3], [4, 0, 0]]) + + def test_nan_policy_options(self): + x = [1, 2, np.nan] + y = [4, 5, 6] + mt1 = stats.median_test(x, y, nan_policy='propagate') + s, p, m, t = stats.median_test(x, y, nan_policy='omit') + + assert_equal(mt1, (np.nan, np.nan, np.nan, None)) + assert_allclose(s, 0.31250000000000006) + assert_allclose(p, 0.57615012203057869) + assert_equal(m, 4.0) + assert_equal(t, np.array([[0, 2], [2, 1]])) + assert_raises(ValueError, stats.median_test, x, y, nan_policy='raise') + + def test_basic(self): + # median_test calls chi2_contingency to compute the test statistic + # and p-value. Make sure it hasn't screwed up the call... + + x = [1, 2, 3, 4, 5] + y = [2, 4, 6, 8] + + stat, p, m, tbl = stats.median_test(x, y) + assert_equal(m, 4) + assert_equal(tbl, [[1, 2], [4, 2]]) + + exp_stat, exp_p, dof, e = stats.chi2_contingency(tbl) + assert_allclose(stat, exp_stat) + assert_allclose(p, exp_p) + + stat, p, m, tbl = stats.median_test(x, y, lambda_=0) + assert_equal(m, 4) + assert_equal(tbl, [[1, 2], [4, 2]]) + + exp_stat, exp_p, dof, e = stats.chi2_contingency(tbl, lambda_=0) + assert_allclose(stat, exp_stat) + assert_allclose(p, exp_p) + + stat, p, m, tbl = stats.median_test(x, y, correction=False) + assert_equal(m, 4) + assert_equal(tbl, [[1, 2], [4, 2]]) + + exp_stat, exp_p, dof, e = stats.chi2_contingency(tbl, correction=False) + assert_allclose(stat, exp_stat) + assert_allclose(p, exp_p) + + @pytest.mark.parametrize("correction", [False, True]) + def test_result(self, correction): + x = [1, 2, 3] + y = [1, 2, 3] + + res = stats.median_test(x, y, correction=correction) + assert_equal((res.statistic, res.pvalue, res.median, res.table), res) + + +class TestDirectionalStats: + # Reference implementations are not available + def test_directional_stats_correctness(self): + # Data from Fisher: Dispersion on a sphere, 1953 and + # Mardia and Jupp, Directional Statistics. + + decl = -np.deg2rad(np.array([343.2, 62., 36.9, 27., 359., + 5.7, 50.4, 357.6, 44.])) + incl = -np.deg2rad(np.array([66.1, 68.7, 70.1, 82.1, 79.5, + 73., 69.3, 58.8, 51.4])) + data = np.stack((np.cos(incl) * np.cos(decl), + np.cos(incl) * np.sin(decl), + np.sin(incl)), + axis=1) + + dirstats = stats.directional_stats(data) + directional_mean = dirstats.mean_direction + mean_rounded = np.round(directional_mean, 4) + + reference_mean = np.array([0.2984, -0.1346, -0.9449]) + assert_allclose(mean_rounded, reference_mean) + + @pytest.mark.parametrize('angles, ref', [ + ([-np.pi/2, np.pi/2], 1.), + ([0, 2*np.pi], 0.) + ]) + def test_directional_stats_2d_special_cases(self, angles, ref): + if callable(ref): + ref = ref(angles) + data = np.stack([np.cos(angles), np.sin(angles)], axis=1) + res = 1 - stats.directional_stats(data).mean_resultant_length + assert_allclose(res, ref) + + def test_directional_stats_2d(self): + # Test that for circular data directional_stats + # yields the same result as circmean/circvar + rng = np.random.default_rng(0xec9a6899d5a2830e0d1af479dbe1fd0c) + testdata = 2 * np.pi * rng.random((1000, )) + testdata_vector = np.stack((np.cos(testdata), + np.sin(testdata)), + axis=1) + dirstats = stats.directional_stats(testdata_vector) + directional_mean = dirstats.mean_direction + directional_mean_angle = np.arctan2(directional_mean[1], + directional_mean[0]) + directional_mean_angle = directional_mean_angle % (2*np.pi) + circmean = stats.circmean(testdata) + assert_allclose(circmean, directional_mean_angle) + + directional_var = 1 - dirstats.mean_resultant_length + circular_var = stats.circvar(testdata) + assert_allclose(directional_var, circular_var) + + def test_directional_mean_higher_dim(self): + # test that directional_stats works for higher dimensions + # here a 4D array is reduced over axis = 2 + data = np.array([[0.8660254, 0.5, 0.], + [0.8660254, -0.5, 0.]]) + full_array = np.tile(data, (2, 2, 2, 1)) + expected = np.array([[[1., 0., 0.], + [1., 0., 0.]], + [[1., 0., 0.], + [1., 0., 0.]]]) + dirstats = stats.directional_stats(full_array, axis=2) + assert_allclose(expected, dirstats.mean_direction) + + def test_directional_stats_list_ndarray_input(self): + # test that list and numpy array inputs yield same results + data = [[0.8660254, 0.5, 0.], [0.8660254, -0.5, 0]] + data_array = np.asarray(data) + res = stats.directional_stats(data) + ref = stats.directional_stats(data_array) + assert_allclose(res.mean_direction, ref.mean_direction) + assert_allclose(res.mean_resultant_length, + res.mean_resultant_length) + + def test_directional_stats_1d_error(self): + # test that one-dimensional data raises ValueError + data = np.ones((5, )) + message = (r"samples must at least be two-dimensional. " + r"Instead samples has shape: (5,)") + with pytest.raises(ValueError, match=re.escape(message)): + stats.directional_stats(data) + + def test_directional_stats_normalize(self): + # test that directional stats calculations yield same results + # for unnormalized input with normalize=True and normalized + # input with normalize=False + data = np.array([[0.8660254, 0.5, 0.], + [1.7320508, -1., 0.]]) + res = stats.directional_stats(data, normalize=True) + normalized_data = data / np.linalg.norm(data, axis=-1, + keepdims=True) + ref = stats.directional_stats(normalized_data, + normalize=False) + assert_allclose(res.mean_direction, ref.mean_direction) + assert_allclose(res.mean_resultant_length, + ref.mean_resultant_length) + + +class TestFDRControl: + def test_input_validation(self): + message = "`ps` must include only numbers between 0 and 1" + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([-1, 0.5, 0.7]) + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([0.5, 0.7, 2]) + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([0.5, 0.7, np.nan]) + + message = "Unrecognized `method` 'YAK'" + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([0.5, 0.7, 0.9], method='YAK') + + message = "`axis` must be an integer or `None`" + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([0.5, 0.7, 0.9], axis=1.5) + with pytest.raises(ValueError, match=message): + stats.false_discovery_control([0.5, 0.7, 0.9], axis=(1, 2)) + + def test_against_TileStats(self): + # See reference [3] of false_discovery_control + ps = [0.005, 0.009, 0.019, 0.022, 0.051, 0.101, 0.361, 0.387] + res = stats.false_discovery_control(ps) + ref = [0.036, 0.036, 0.044, 0.044, 0.082, 0.135, 0.387, 0.387] + assert_allclose(res, ref, atol=1e-3) + + @pytest.mark.parametrize("case", + [([0.24617028, 0.01140030, 0.05652047, 0.06841983, + 0.07989886, 0.01841490, 0.17540784, 0.06841983, + 0.06841983, 0.25464082], 'bh'), + ([0.72102493, 0.03339112, 0.16554665, 0.20039952, + 0.23402122, 0.05393666, 0.51376399, 0.20039952, + 0.20039952, 0.74583488], 'by')]) + def test_against_R(self, case): + # Test against p.adjust, e.g. + # p = c(0.22155325, 0.00114003,..., 0.0364813 , 0.25464082) + # p.adjust(p, "BY") + ref, method = case + rng = np.random.default_rng(6134137338861652935) + ps = stats.loguniform.rvs(1e-3, 0.5, size=10, random_state=rng) + ps[3] = ps[7] # force a tie + res = stats.false_discovery_control(ps, method=method) + assert_allclose(res, ref, atol=1e-6) + + def test_axis_None(self): + rng = np.random.default_rng(6134137338861652935) + ps = stats.loguniform.rvs(1e-3, 0.5, size=(3, 4, 5), random_state=rng) + res = stats.false_discovery_control(ps, axis=None) + ref = stats.false_discovery_control(ps.ravel()) + assert_equal(res, ref) + + @pytest.mark.parametrize("axis", [0, 1, -1]) + def test_axis(self, axis): + rng = np.random.default_rng(6134137338861652935) + ps = stats.loguniform.rvs(1e-3, 0.5, size=(3, 4, 5), random_state=rng) + res = stats.false_discovery_control(ps, axis=axis) + ref = np.apply_along_axis(stats.false_discovery_control, axis, ps) + assert_equal(res, ref) + + def test_edge_cases(self): + assert_array_equal(stats.false_discovery_control([0.25]), [0.25]) + assert_array_equal(stats.false_discovery_control(0.25), 0.25) + assert_array_equal(stats.false_discovery_control([]), []) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_basic.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..5250e1e3ca2497bcf376a709aa965c9a6a6628b8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_basic.py @@ -0,0 +1,2053 @@ +""" +Tests for the stats.mstats module (support for masked arrays) +""" +import warnings +import platform + +import numpy as np +from numpy import nan +import numpy.ma as ma +from numpy.ma import masked, nomask + +import scipy.stats.mstats as mstats +from scipy import stats +from .common_tests import check_named_results +import pytest +from pytest import raises as assert_raises +from numpy.ma.testutils import (assert_equal, assert_almost_equal, + assert_array_almost_equal, + assert_array_almost_equal_nulp, assert_, + assert_allclose, assert_array_equal) +from numpy.testing import suppress_warnings +from scipy.stats import _mstats_basic + + +class TestMquantiles: + def test_mquantiles_limit_keyword(self): + # Regression test for Trac ticket #867 + data = np.array([[6., 7., 1.], + [47., 15., 2.], + [49., 36., 3.], + [15., 39., 4.], + [42., 40., -999.], + [41., 41., -999.], + [7., -999., -999.], + [39., -999., -999.], + [43., -999., -999.], + [40., -999., -999.], + [36., -999., -999.]]) + desired = [[19.2, 14.6, 1.45], + [40.0, 37.5, 2.5], + [42.8, 40.05, 3.55]] + quants = mstats.mquantiles(data, axis=0, limit=(0, 50)) + assert_almost_equal(quants, desired) + + +def check_equal_gmean(array_like, desired, axis=None, dtype=None, rtol=1e-7): + # Note this doesn't test when axis is not specified + x = mstats.gmean(array_like, axis=axis, dtype=dtype) + assert_allclose(x, desired, rtol=rtol) + assert_equal(x.dtype, dtype) + + +def check_equal_hmean(array_like, desired, axis=None, dtype=None, rtol=1e-7): + x = stats.hmean(array_like, axis=axis, dtype=dtype) + assert_allclose(x, desired, rtol=rtol) + assert_equal(x.dtype, dtype) + + +class TestGeoMean: + def test_1d(self): + a = [1, 2, 3, 4] + desired = np.power(1*2*3*4, 1./4.) + check_equal_gmean(a, desired, rtol=1e-14) + + def test_1d_ma(self): + # Test a 1d masked array + a = ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + desired = 45.2872868812 + check_equal_gmean(a, desired) + + a = ma.array([1, 2, 3, 4], mask=[0, 0, 0, 1]) + desired = np.power(1*2*3, 1./3.) + check_equal_gmean(a, desired, rtol=1e-14) + + def test_1d_ma_value(self): + # Test a 1d masked array with a masked value + a = np.ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100], + mask=[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) + desired = 41.4716627439 + check_equal_gmean(a, desired) + + def test_1d_ma0(self): + # Test a 1d masked array with zero element + a = np.ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 0]) + desired = 0 + check_equal_gmean(a, desired) + + def test_1d_ma_inf(self): + # Test a 1d masked array with negative element + a = np.ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, -1]) + desired = np.nan + with np.errstate(invalid='ignore'): + check_equal_gmean(a, desired) + + @pytest.mark.skipif(not hasattr(np, 'float96'), + reason='cannot find float96 so skipping') + def test_1d_float96(self): + a = ma.array([1, 2, 3, 4], mask=[0, 0, 0, 1]) + desired_dt = np.power(1*2*3, 1./3.).astype(np.float96) + check_equal_gmean(a, desired_dt, dtype=np.float96, rtol=1e-14) + + def test_2d_ma(self): + a = ma.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], + mask=[[0, 0, 0, 0], [1, 0, 0, 1], [0, 1, 1, 0]]) + desired = np.array([1, 2, 3, 4]) + check_equal_gmean(a, desired, axis=0, rtol=1e-14) + + desired = ma.array([np.power(1*2*3*4, 1./4.), + np.power(2*3, 1./2.), + np.power(1*4, 1./2.)]) + check_equal_gmean(a, desired, axis=-1, rtol=1e-14) + + # Test a 2d masked array + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 52.8885199 + check_equal_gmean(np.ma.array(a), desired) + + +class TestHarMean: + def test_1d(self): + a = ma.array([1, 2, 3, 4], mask=[0, 0, 0, 1]) + desired = 3. / (1./1 + 1./2 + 1./3) + check_equal_hmean(a, desired, rtol=1e-14) + + a = np.ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + desired = 34.1417152147 + check_equal_hmean(a, desired) + + a = np.ma.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100], + mask=[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) + desired = 31.8137186141 + check_equal_hmean(a, desired) + + @pytest.mark.skipif(not hasattr(np, 'float96'), + reason='cannot find float96 so skipping') + def test_1d_float96(self): + a = ma.array([1, 2, 3, 4], mask=[0, 0, 0, 1]) + desired_dt = np.asarray(3. / (1./1 + 1./2 + 1./3), dtype=np.float96) + check_equal_hmean(a, desired_dt, dtype=np.float96) + + def test_2d(self): + a = ma.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], + mask=[[0, 0, 0, 0], [1, 0, 0, 1], [0, 1, 1, 0]]) + desired = ma.array([1, 2, 3, 4]) + check_equal_hmean(a, desired, axis=0, rtol=1e-14) + + desired = [4./(1/1.+1/2.+1/3.+1/4.), 2./(1/2.+1/3.), 2./(1/1.+1/4.)] + check_equal_hmean(a, desired, axis=-1, rtol=1e-14) + + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 38.6696271841 + check_equal_hmean(np.ma.array(a), desired) + + +class TestRanking: + def test_ranking(self): + x = ma.array([0,1,1,1,2,3,4,5,5,6,]) + assert_almost_equal(mstats.rankdata(x), + [1,3,3,3,5,6,7,8.5,8.5,10]) + x[[3,4]] = masked + assert_almost_equal(mstats.rankdata(x), + [1,2.5,2.5,0,0,4,5,6.5,6.5,8]) + assert_almost_equal(mstats.rankdata(x, use_missing=True), + [1,2.5,2.5,4.5,4.5,4,5,6.5,6.5,8]) + x = ma.array([0,1,5,1,2,4,3,5,1,6,]) + assert_almost_equal(mstats.rankdata(x), + [1,3,8.5,3,5,7,6,8.5,3,10]) + x = ma.array([[0,1,1,1,2], [3,4,5,5,6,]]) + assert_almost_equal(mstats.rankdata(x), + [[1,3,3,3,5], [6,7,8.5,8.5,10]]) + assert_almost_equal(mstats.rankdata(x, axis=1), + [[1,3,3,3,5], [1,2,3.5,3.5,5]]) + assert_almost_equal(mstats.rankdata(x,axis=0), + [[1,1,1,1,1], [2,2,2,2,2,]]) + + +class TestCorr: + def test_pearsonr(self): + # Tests some computations of Pearson's r + x = ma.arange(10) + with warnings.catch_warnings(): + # The tests in this context are edge cases, with perfect + # correlation or anticorrelation, or totally masked data. + # None of these should trigger a RuntimeWarning. + warnings.simplefilter("error", RuntimeWarning) + + assert_almost_equal(mstats.pearsonr(x, x)[0], 1.0) + assert_almost_equal(mstats.pearsonr(x, x[::-1])[0], -1.0) + + x = ma.array(x, mask=True) + pr = mstats.pearsonr(x, x) + assert_(pr[0] is masked) + assert_(pr[1] is masked) + + x1 = ma.array([-1.0, 0.0, 1.0]) + y1 = ma.array([0, 0, 3]) + r, p = mstats.pearsonr(x1, y1) + assert_almost_equal(r, np.sqrt(3)/2) + assert_almost_equal(p, 1.0/3) + + # (x2, y2) have the same unmasked data as (x1, y1). + mask = [False, False, False, True] + x2 = ma.array([-1.0, 0.0, 1.0, 99.0], mask=mask) + y2 = ma.array([0, 0, 3, -1], mask=mask) + r, p = mstats.pearsonr(x2, y2) + assert_almost_equal(r, np.sqrt(3)/2) + assert_almost_equal(p, 1.0/3) + + def test_pearsonr_misaligned_mask(self): + mx = np.ma.masked_array([1, 2, 3, 4, 5, 6], mask=[0, 1, 0, 0, 0, 0]) + my = np.ma.masked_array([9, 8, 7, 6, 5, 9], mask=[0, 0, 1, 0, 0, 0]) + x = np.array([1, 4, 5, 6]) + y = np.array([9, 6, 5, 9]) + mr, mp = mstats.pearsonr(mx, my) + r, p = stats.pearsonr(x, y) + assert_equal(mr, r) + assert_equal(mp, p) + + def test_spearmanr(self): + # Tests some computations of Spearman's rho + (x, y) = ([5.05,6.75,3.21,2.66], [1.65,2.64,2.64,6.95]) + assert_almost_equal(mstats.spearmanr(x,y)[0], -0.6324555) + (x, y) = ([5.05,6.75,3.21,2.66,np.nan],[1.65,2.64,2.64,6.95,np.nan]) + (x, y) = (ma.fix_invalid(x), ma.fix_invalid(y)) + assert_almost_equal(mstats.spearmanr(x,y)[0], -0.6324555) + + x = [2.0, 47.4, 42.0, 10.8, 60.1, 1.7, 64.0, 63.1, + 1.0, 1.4, 7.9, 0.3, 3.9, 0.3, 6.7] + y = [22.6, 8.3, 44.4, 11.9, 24.6, 0.6, 5.7, 41.6, + 0.0, 0.6, 6.7, 3.8, 1.0, 1.2, 1.4] + assert_almost_equal(mstats.spearmanr(x,y)[0], 0.6887299) + x = [2.0, 47.4, 42.0, 10.8, 60.1, 1.7, 64.0, 63.1, + 1.0, 1.4, 7.9, 0.3, 3.9, 0.3, 6.7, np.nan] + y = [22.6, 8.3, 44.4, 11.9, 24.6, 0.6, 5.7, 41.6, + 0.0, 0.6, 6.7, 3.8, 1.0, 1.2, 1.4, np.nan] + (x, y) = (ma.fix_invalid(x), ma.fix_invalid(y)) + assert_almost_equal(mstats.spearmanr(x,y)[0], 0.6887299) + # Next test is to make sure calculation uses sufficient precision. + # The denominator's value is ~n^3 and used to be represented as an + # int. 2000**3 > 2**32 so these arrays would cause overflow on + # some machines. + x = list(range(2000)) + y = list(range(2000)) + y[0], y[9] = y[9], y[0] + y[10], y[434] = y[434], y[10] + y[435], y[1509] = y[1509], y[435] + # rho = 1 - 6 * (2 * (9^2 + 424^2 + 1074^2))/(2000 * (2000^2 - 1)) + # = 1 - (1 / 500) + # = 0.998 + assert_almost_equal(mstats.spearmanr(x,y)[0], 0.998) + + # test for namedtuple attributes + res = mstats.spearmanr(x, y) + attributes = ('correlation', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_spearmanr_alternative(self): + # check against R + # options(digits=16) + # cor.test(c(2.0, 47.4, 42.0, 10.8, 60.1, 1.7, 64.0, 63.1, + # 1.0, 1.4, 7.9, 0.3, 3.9, 0.3, 6.7), + # c(22.6, 8.3, 44.4, 11.9, 24.6, 0.6, 5.7, 41.6, + # 0.0, 0.6, 6.7, 3.8, 1.0, 1.2, 1.4), + # alternative='two.sided', method='spearman') + x = [2.0, 47.4, 42.0, 10.8, 60.1, 1.7, 64.0, 63.1, + 1.0, 1.4, 7.9, 0.3, 3.9, 0.3, 6.7] + y = [22.6, 8.3, 44.4, 11.9, 24.6, 0.6, 5.7, 41.6, + 0.0, 0.6, 6.7, 3.8, 1.0, 1.2, 1.4] + + r_exp = 0.6887298747763864 # from cor.test + + r, p = mstats.spearmanr(x, y) + assert_allclose(r, r_exp) + assert_allclose(p, 0.004519192910756) + + r, p = mstats.spearmanr(x, y, alternative='greater') + assert_allclose(r, r_exp) + assert_allclose(p, 0.002259596455378) + + r, p = mstats.spearmanr(x, y, alternative='less') + assert_allclose(r, r_exp) + assert_allclose(p, 0.9977404035446) + + # intuitive test (with obvious positive correlation) + n = 100 + x = np.linspace(0, 5, n) + y = 0.1*x + np.random.rand(n) # y is positively correlated w/ x + + stat1, p1 = mstats.spearmanr(x, y) + + stat2, p2 = mstats.spearmanr(x, y, alternative="greater") + assert_allclose(p2, p1 / 2) # positive correlation -> small p + + stat3, p3 = mstats.spearmanr(x, y, alternative="less") + assert_allclose(p3, 1 - p1 / 2) # positive correlation -> large p + + assert stat1 == stat2 == stat3 + + with pytest.raises(ValueError, match="alternative must be 'less'..."): + mstats.spearmanr(x, y, alternative="ekki-ekki") + + @pytest.mark.skipif(platform.machine() == 'ppc64le', + reason="fails/crashes on ppc64le") + def test_kendalltau(self): + # check case with maximum disorder and p=1 + x = ma.array(np.array([9, 2, 5, 6])) + y = ma.array(np.array([4, 7, 9, 11])) + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [0.0, 1.0] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # simple case without ties + x = ma.array(np.arange(10)) + y = ma.array(np.arange(10)) + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [1.0, 5.511463844797e-07] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # check exception in case of invalid method keyword + assert_raises(ValueError, mstats.kendalltau, x, y, method='banana') + + # swap a couple of values + b = y[1] + y[1] = y[2] + y[2] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [0.9555555555555556, 5.511463844797e-06] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # swap a couple more + b = y[5] + y[5] = y[6] + y[6] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [0.9111111111111111, 2.976190476190e-05] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # same in opposite direction + x = ma.array(np.arange(10)) + y = ma.array(np.arange(10)[::-1]) + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [-1.0, 5.511463844797e-07] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # swap a couple of values + b = y[1] + y[1] = y[2] + y[2] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [-0.9555555555555556, 5.511463844797e-06] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # swap a couple more + b = y[5] + y[5] = y[6] + y[6] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = [-0.9111111111111111, 2.976190476190e-05] + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), expected) + + # Tests some computations of Kendall's tau + x = ma.fix_invalid([5.05, 6.75, 3.21, 2.66, np.nan]) + y = ma.fix_invalid([1.65, 26.5, -5.93, 7.96, np.nan]) + z = ma.fix_invalid([1.65, 2.64, 2.64, 6.95, np.nan]) + assert_almost_equal(np.asarray(mstats.kendalltau(x, y)), + [+0.3333333, 0.75]) + assert_almost_equal(np.asarray(mstats.kendalltau(x, y, method='asymptotic')), + [+0.3333333, 0.4969059]) + assert_almost_equal(np.asarray(mstats.kendalltau(x, z)), + [-0.5477226, 0.2785987]) + # + x = ma.fix_invalid([0, 0, 0, 0, 20, 20, 0, 60, 0, 20, + 10, 10, 0, 40, 0, 20, 0, 0, 0, 0, 0, np.nan]) + y = ma.fix_invalid([0, 80, 80, 80, 10, 33, 60, 0, 67, 27, + 25, 80, 80, 80, 80, 80, 80, 0, 10, 45, np.nan, 0]) + result = mstats.kendalltau(x, y) + assert_almost_equal(np.asarray(result), [-0.1585188, 0.4128009]) + + # test for namedtuple attributes + attributes = ('correlation', 'pvalue') + check_named_results(result, attributes, ma=True) + + @pytest.mark.skipif(platform.machine() == 'ppc64le', + reason="fails/crashes on ppc64le") + @pytest.mark.slow + def test_kendalltau_large(self): + # make sure internal variable use correct precision with + # larger arrays + x = np.arange(2000, dtype=float) + x = ma.masked_greater(x, 1995) + y = np.arange(2000, dtype=float) + y = np.concatenate((y[1000:], y[:1000])) + assert_(np.isfinite(mstats.kendalltau(x, y)[1])) + + def test_kendalltau_seasonal(self): + # Tests the seasonal Kendall tau. + x = [[nan, nan, 4, 2, 16, 26, 5, 1, 5, 1, 2, 3, 1], + [4, 3, 5, 3, 2, 7, 3, 1, 1, 2, 3, 5, 3], + [3, 2, 5, 6, 18, 4, 9, 1, 1, nan, 1, 1, nan], + [nan, 6, 11, 4, 17, nan, 6, 1, 1, 2, 5, 1, 1]] + x = ma.fix_invalid(x).T + output = mstats.kendalltau_seasonal(x) + assert_almost_equal(output['global p-value (indep)'], 0.008, 3) + assert_almost_equal(output['seasonal p-value'].round(2), + [0.18,0.53,0.20,0.04]) + + @pytest.mark.parametrize("method", ("exact", "asymptotic")) + @pytest.mark.parametrize("alternative", ("two-sided", "greater", "less")) + def test_kendalltau_mstats_vs_stats(self, method, alternative): + # Test that mstats.kendalltau and stats.kendalltau with + # nan_policy='omit' matches behavior of stats.kendalltau + # Accuracy of the alternatives is tested in stats/tests/test_stats.py + + np.random.seed(0) + n = 50 + x = np.random.rand(n) + y = np.random.rand(n) + mask = np.random.rand(n) > 0.5 + + x_masked = ma.array(x, mask=mask) + y_masked = ma.array(y, mask=mask) + res_masked = mstats.kendalltau( + x_masked, y_masked, method=method, alternative=alternative) + + x_compressed = x_masked.compressed() + y_compressed = y_masked.compressed() + res_compressed = stats.kendalltau( + x_compressed, y_compressed, method=method, alternative=alternative) + + x[mask] = np.nan + y[mask] = np.nan + res_nan = stats.kendalltau( + x, y, method=method, nan_policy='omit', alternative=alternative) + + assert_allclose(res_masked, res_compressed) + assert_allclose(res_nan, res_compressed) + + def test_kendall_p_exact_medium(self): + # Test for the exact method with medium samples (some n >= 171) + # expected values generated using SymPy + expectations = {(100, 2393): 0.62822615287956040664, + (101, 2436): 0.60439525773513602669, + (170, 0): 2.755801935583541e-307, + (171, 0): 0.0, + (171, 1): 2.755801935583541e-307, + (172, 1): 0.0, + (200, 9797): 0.74753983745929675209, + (201, 9656): 0.40959218958120363618} + for nc, expected in expectations.items(): + res = _mstats_basic._kendall_p_exact(nc[0], nc[1]) + assert_almost_equal(res, expected) + + @pytest.mark.xslow + def test_kendall_p_exact_large(self): + # Test for the exact method with large samples (n >= 171) + # expected values generated using SymPy + expectations = {(400, 38965): 0.48444283672113314099, + (401, 39516): 0.66363159823474837662, + (800, 156772): 0.42265448483120932055, + (801, 157849): 0.53437553412194416236, + (1600, 637472): 0.84200727400323538419, + (1601, 630304): 0.34465255088058593946} + + for nc, expected in expectations.items(): + res = _mstats_basic._kendall_p_exact(nc[0], nc[1]) + assert_almost_equal(res, expected) + + def test_pointbiserial(self): + x = [1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, -1] + y = [14.8, 13.8, 12.4, 10.1, 7.1, 6.1, 5.8, 4.6, 4.3, 3.5, 3.3, 3.2, + 3.0, 2.8, 2.8, 2.5, 2.4, 2.3, 2.1, 1.7, 1.7, 1.5, 1.3, 1.3, 1.2, + 1.2, 1.1, 0.8, 0.7, 0.6, 0.5, 0.2, 0.2, 0.1, np.nan] + assert_almost_equal(mstats.pointbiserialr(x, y)[0], 0.36149, 5) + + # test for namedtuple attributes + res = mstats.pointbiserialr(x, y) + attributes = ('correlation', 'pvalue') + check_named_results(res, attributes, ma=True) + + +class TestTrimming: + + def test_trim(self): + a = ma.arange(10) + assert_equal(mstats.trim(a), [0,1,2,3,4,5,6,7,8,9]) + a = ma.arange(10) + assert_equal(mstats.trim(a,(2,8)), [None,None,2,3,4,5,6,7,8,None]) + a = ma.arange(10) + assert_equal(mstats.trim(a,limits=(2,8),inclusive=(False,False)), + [None,None,None,3,4,5,6,7,None,None]) + a = ma.arange(10) + assert_equal(mstats.trim(a,limits=(0.1,0.2),relative=True), + [None,1,2,3,4,5,6,7,None,None]) + + a = ma.arange(12) + a[[0,-1]] = a[5] = masked + assert_equal(mstats.trim(a, (2,8)), + [None, None, 2, 3, 4, None, 6, 7, 8, None, None, None]) + + x = ma.arange(100).reshape(10, 10) + expected = [1]*10 + [0]*70 + [1]*20 + trimx = mstats.trim(x, (0.1,0.2), relative=True, axis=None) + assert_equal(trimx._mask.ravel(), expected) + trimx = mstats.trim(x, (0.1,0.2), relative=True, axis=0) + assert_equal(trimx._mask.ravel(), expected) + trimx = mstats.trim(x, (0.1,0.2), relative=True, axis=-1) + assert_equal(trimx._mask.T.ravel(), expected) + + # same as above, but with an extra masked row inserted + x = ma.arange(110).reshape(11, 10) + x[1] = masked + expected = [1]*20 + [0]*70 + [1]*20 + trimx = mstats.trim(x, (0.1,0.2), relative=True, axis=None) + assert_equal(trimx._mask.ravel(), expected) + trimx = mstats.trim(x, (0.1,0.2), relative=True, axis=0) + assert_equal(trimx._mask.ravel(), expected) + trimx = mstats.trim(x.T, (0.1,0.2), relative=True, axis=-1) + assert_equal(trimx.T._mask.ravel(), expected) + + def test_trim_old(self): + x = ma.arange(100) + assert_equal(mstats.trimboth(x).count(), 60) + assert_equal(mstats.trimtail(x,tail='r').count(), 80) + x[50:70] = masked + trimx = mstats.trimboth(x) + assert_equal(trimx.count(), 48) + assert_equal(trimx._mask, [1]*16 + [0]*34 + [1]*20 + [0]*14 + [1]*16) + x._mask = nomask + x.shape = (10,10) + assert_equal(mstats.trimboth(x).count(), 60) + assert_equal(mstats.trimtail(x).count(), 80) + + def test_trimr(self): + x = ma.arange(10) + result = mstats.trimr(x, limits=(0.15, 0.14), inclusive=(False, False)) + expected = ma.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + mask=[1, 1, 0, 0, 0, 0, 0, 0, 0, 1]) + assert_equal(result, expected) + assert_equal(result.mask, expected.mask) + + def test_trimmedmean(self): + data = ma.array([77, 87, 88,114,151,210,219,246,253,262, + 296,299,306,376,428,515,666,1310,2611]) + assert_almost_equal(mstats.trimmed_mean(data,0.1), 343, 0) + assert_almost_equal(mstats.trimmed_mean(data,(0.1,0.1)), 343, 0) + assert_almost_equal(mstats.trimmed_mean(data,(0.2,0.2)), 283, 0) + + def test_trimmedvar(self): + # Basic test. Additional tests of all arguments, edge cases, + # input validation, and proper treatment of masked arrays are needed. + rng = np.random.default_rng(3262323289434724460) + data_orig = rng.random(size=20) + data = np.sort(data_orig) + data = ma.array(data, mask=[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]) + assert_allclose(mstats.trimmed_var(data_orig, 0.1), data.var()) + + def test_trimmedstd(self): + # Basic test. Additional tests of all arguments, edge cases, + # input validation, and proper treatment of masked arrays are needed. + rng = np.random.default_rng(7121029245207162780) + data_orig = rng.random(size=20) + data = np.sort(data_orig) + data = ma.array(data, mask=[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]) + assert_allclose(mstats.trimmed_std(data_orig, 0.1), data.std()) + + def test_trimmed_stde(self): + data = ma.array([77, 87, 88,114,151,210,219,246,253,262, + 296,299,306,376,428,515,666,1310,2611]) + assert_almost_equal(mstats.trimmed_stde(data,(0.2,0.2)), 56.13193, 5) + assert_almost_equal(mstats.trimmed_stde(data,0.2), 56.13193, 5) + + def test_winsorization(self): + data = ma.array([77, 87, 88,114,151,210,219,246,253,262, + 296,299,306,376,428,515,666,1310,2611]) + assert_almost_equal(mstats.winsorize(data,(0.2,0.2)).var(ddof=1), + 21551.4, 1) + assert_almost_equal( + mstats.winsorize(data, (0.2,0.2),(False,False)).var(ddof=1), + 11887.3, 1) + data[5] = masked + winsorized = mstats.winsorize(data) + assert_equal(winsorized.mask, data.mask) + + def test_winsorization_nan(self): + data = ma.array([np.nan, np.nan, 0, 1, 2]) + assert_raises(ValueError, mstats.winsorize, data, (0.05, 0.05), + nan_policy='raise') + # Testing propagate (default behavior) + assert_equal(mstats.winsorize(data, (0.4, 0.4)), + ma.array([2, 2, 2, 2, 2])) + assert_equal(mstats.winsorize(data, (0.8, 0.8)), + ma.array([np.nan, np.nan, np.nan, np.nan, np.nan])) + assert_equal(mstats.winsorize(data, (0.4, 0.4), nan_policy='omit'), + ma.array([np.nan, np.nan, 2, 2, 2])) + assert_equal(mstats.winsorize(data, (0.8, 0.8), nan_policy='omit'), + ma.array([np.nan, np.nan, 2, 2, 2])) + + +class TestMoments: + # Comparison numbers are found using R v.1.5.1 + # note that length(testcase) = 4 + # testmathworks comes from documentation for the + # Statistics Toolbox for Matlab and can be found at both + # https://www.mathworks.com/help/stats/kurtosis.html + # https://www.mathworks.com/help/stats/skewness.html + # Note that both test cases came from here. + testcase = [1,2,3,4] + testmathworks = ma.fix_invalid([1.165, 0.6268, 0.0751, 0.3516, -0.6965, + np.nan]) + testcase_2d = ma.array( + np.array([[0.05245846, 0.50344235, 0.86589117, 0.36936353, 0.46961149], + [0.11574073, 0.31299969, 0.45925772, 0.72618805, 0.75194407], + [0.67696689, 0.91878127, 0.09769044, 0.04645137, 0.37615733], + [0.05903624, 0.29908861, 0.34088298, 0.66216337, 0.83160998], + [0.64619526, 0.94894632, 0.27855892, 0.0706151, 0.39962917]]), + mask=np.array([[True, False, False, True, False], + [True, True, True, False, True], + [False, False, False, False, False], + [True, True, True, True, True], + [False, False, True, False, False]], dtype=bool)) + + def _assert_equal(self, actual, expect, *, shape=None, dtype=None): + expect = np.asarray(expect) + if shape is not None: + expect = np.broadcast_to(expect, shape) + assert_array_equal(actual, expect) + if dtype is None: + dtype = expect.dtype + assert actual.dtype == dtype + + def test_moment(self): + y = mstats.moment(self.testcase,1) + assert_almost_equal(y,0.0,10) + y = mstats.moment(self.testcase,2) + assert_almost_equal(y,1.25) + y = mstats.moment(self.testcase,3) + assert_almost_equal(y,0.0) + y = mstats.moment(self.testcase,4) + assert_almost_equal(y,2.5625) + + # check array_like input for moment + y = mstats.moment(self.testcase, [1, 2, 3, 4]) + assert_allclose(y, [0, 1.25, 0, 2.5625]) + + # check moment input consists only of integers + y = mstats.moment(self.testcase, 0.0) + assert_allclose(y, 1.0) + assert_raises(ValueError, mstats.moment, self.testcase, 1.2) + y = mstats.moment(self.testcase, [1.0, 2, 3, 4.0]) + assert_allclose(y, [0, 1.25, 0, 2.5625]) + + # test empty input + y = mstats.moment([]) + self._assert_equal(y, np.nan, dtype=np.float64) + y = mstats.moment(np.array([], dtype=np.float32)) + self._assert_equal(y, np.nan, dtype=np.float32) + y = mstats.moment(np.zeros((1, 0)), axis=0) + self._assert_equal(y, [], shape=(0,), dtype=np.float64) + y = mstats.moment([[]], axis=1) + self._assert_equal(y, np.nan, shape=(1,), dtype=np.float64) + y = mstats.moment([[]], moment=[0, 1], axis=0) + self._assert_equal(y, [], shape=(2, 0)) + + x = np.arange(10.) + x[9] = np.nan + assert_equal(mstats.moment(x, 2), ma.masked) # NaN value is ignored + + def test_variation(self): + y = mstats.variation(self.testcase) + assert_almost_equal(y,0.44721359549996, 10) + + def test_variation_ddof(self): + # test variation with delta degrees of freedom + # regression test for gh-13341 + a = np.array([1, 2, 3, 4, 5]) + y = mstats.variation(a, ddof=1) + assert_almost_equal(y, 0.5270462766947299) + + def test_skewness(self): + y = mstats.skew(self.testmathworks) + assert_almost_equal(y,-0.29322304336607,10) + y = mstats.skew(self.testmathworks,bias=0) + assert_almost_equal(y,-0.437111105023940,10) + y = mstats.skew(self.testcase) + assert_almost_equal(y,0.0,10) + + # test that skew works on multidimensional masked arrays + correct_2d = ma.array( + np.array([0.6882870394455785, 0, 0.2665647526856708, + 0, -0.05211472114254485]), + mask=np.array([False, False, False, True, False], dtype=bool) + ) + assert_allclose(mstats.skew(self.testcase_2d, 1), correct_2d) + for i, row in enumerate(self.testcase_2d): + assert_almost_equal(mstats.skew(row), correct_2d[i]) + + correct_2d_bias_corrected = ma.array( + np.array([1.685952043212545, 0.0, 0.3973712716070531, 0, + -0.09026534484117164]), + mask=np.array([False, False, False, True, False], dtype=bool) + ) + assert_allclose(mstats.skew(self.testcase_2d, 1, bias=False), + correct_2d_bias_corrected) + for i, row in enumerate(self.testcase_2d): + assert_almost_equal(mstats.skew(row, bias=False), + correct_2d_bias_corrected[i]) + + # Check consistency between stats and mstats implementations + assert_allclose(mstats.skew(self.testcase_2d[2, :]), + stats.skew(self.testcase_2d[2, :])) + + def test_kurtosis(self): + # Set flags for axis = 0 and fisher=0 (Pearson's definition of kurtosis + # for compatibility with Matlab) + y = mstats.kurtosis(self.testmathworks, 0, fisher=0, bias=1) + assert_almost_equal(y, 2.1658856802973, 10) + # Note that MATLAB has confusing docs for the following case + # kurtosis(x,0) gives an unbiased estimate of Pearson's skewness + # kurtosis(x) gives a biased estimate of Fisher's skewness (Pearson-3) + # The MATLAB docs imply that both should give Fisher's + y = mstats.kurtosis(self.testmathworks, fisher=0, bias=0) + assert_almost_equal(y, 3.663542721189047, 10) + y = mstats.kurtosis(self.testcase, 0, 0) + assert_almost_equal(y, 1.64) + + # test that kurtosis works on multidimensional masked arrays + correct_2d = ma.array(np.array([-1.5, -3., -1.47247052385, 0., + -1.26979517952]), + mask=np.array([False, False, False, True, + False], dtype=bool)) + assert_array_almost_equal(mstats.kurtosis(self.testcase_2d, 1), + correct_2d) + for i, row in enumerate(self.testcase_2d): + assert_almost_equal(mstats.kurtosis(row), correct_2d[i]) + + correct_2d_bias_corrected = ma.array( + np.array([-1.5, -3., -1.88988209538, 0., -0.5234638463918877]), + mask=np.array([False, False, False, True, False], dtype=bool)) + assert_array_almost_equal(mstats.kurtosis(self.testcase_2d, 1, + bias=False), + correct_2d_bias_corrected) + for i, row in enumerate(self.testcase_2d): + assert_almost_equal(mstats.kurtosis(row, bias=False), + correct_2d_bias_corrected[i]) + + # Check consistency between stats and mstats implementations + assert_array_almost_equal_nulp(mstats.kurtosis(self.testcase_2d[2, :]), + stats.kurtosis(self.testcase_2d[2, :]), + nulp=4) + + +class TestMode: + def test_mode(self): + a1 = [0,0,0,1,1,1,2,3,3,3,3,4,5,6,7] + a2 = np.reshape(a1, (3,5)) + a3 = np.array([1,2,3,4,5,6]) + a4 = np.reshape(a3, (3,2)) + ma1 = ma.masked_where(ma.array(a1) > 2, a1) + ma2 = ma.masked_where(a2 > 2, a2) + ma3 = ma.masked_where(a3 < 2, a3) + ma4 = ma.masked_where(ma.array(a4) < 2, a4) + assert_equal(mstats.mode(a1, axis=None), (3,4)) + assert_equal(mstats.mode(a1, axis=0), (3,4)) + assert_equal(mstats.mode(ma1, axis=None), (0,3)) + assert_equal(mstats.mode(a2, axis=None), (3,4)) + assert_equal(mstats.mode(ma2, axis=None), (0,3)) + assert_equal(mstats.mode(a3, axis=None), (1,1)) + assert_equal(mstats.mode(ma3, axis=None), (2,1)) + assert_equal(mstats.mode(a2, axis=0), ([[0,0,0,1,1]], [[1,1,1,1,1]])) + assert_equal(mstats.mode(ma2, axis=0), ([[0,0,0,1,1]], [[1,1,1,1,1]])) + assert_equal(mstats.mode(a2, axis=-1), ([[0],[3],[3]], [[3],[3],[1]])) + assert_equal(mstats.mode(ma2, axis=-1), ([[0],[1],[0]], [[3],[1],[0]])) + assert_equal(mstats.mode(ma4, axis=0), ([[3,2]], [[1,1]])) + assert_equal(mstats.mode(ma4, axis=-1), ([[2],[3],[5]], [[1],[1],[1]])) + + a1_res = mstats.mode(a1, axis=None) + + # test for namedtuple attributes + attributes = ('mode', 'count') + check_named_results(a1_res, attributes, ma=True) + + def test_mode_modifies_input(self): + # regression test for gh-6428: mode(..., axis=None) may not modify + # the input array + im = np.zeros((100, 100)) + im[:50, :] += 1 + im[:, :50] += 1 + cp = im.copy() + mstats.mode(im, None) + assert_equal(im, cp) + + +class TestPercentile: + def setup_method(self): + self.a1 = [3, 4, 5, 10, -3, -5, 6] + self.a2 = [3, -6, -2, 8, 7, 4, 2, 1] + self.a3 = [3., 4, 5, 10, -3, -5, -6, 7.0] + + def test_percentile(self): + x = np.arange(8) * 0.5 + assert_equal(mstats.scoreatpercentile(x, 0), 0.) + assert_equal(mstats.scoreatpercentile(x, 100), 3.5) + assert_equal(mstats.scoreatpercentile(x, 50), 1.75) + + def test_2D(self): + x = ma.array([[1, 1, 1], + [1, 1, 1], + [4, 4, 3], + [1, 1, 1], + [1, 1, 1]]) + assert_equal(mstats.scoreatpercentile(x, 50), [1, 1, 1]) + + +class TestVariability: + """ Comparison numbers are found using R v.1.5.1 + note that length(testcase) = 4 + """ + testcase = ma.fix_invalid([1,2,3,4,np.nan]) + + def test_sem(self): + # This is not in R, so used: sqrt(var(testcase)*3/4) / sqrt(3) + y = mstats.sem(self.testcase) + assert_almost_equal(y, 0.6454972244) + n = self.testcase.count() + assert_allclose(mstats.sem(self.testcase, ddof=0) * np.sqrt(n/(n-2)), + mstats.sem(self.testcase, ddof=2)) + + def test_zmap(self): + # This is not in R, so tested by using: + # (testcase[i]-mean(testcase,axis=0)) / sqrt(var(testcase)*3/4) + y = mstats.zmap(self.testcase, self.testcase) + desired_unmaskedvals = ([-1.3416407864999, -0.44721359549996, + 0.44721359549996, 1.3416407864999]) + assert_array_almost_equal(desired_unmaskedvals, + y.data[y.mask == False], decimal=12) # noqa: E712 + + def test_zscore(self): + # This is not in R, so tested by using: + # (testcase[i]-mean(testcase,axis=0)) / sqrt(var(testcase)*3/4) + y = mstats.zscore(self.testcase) + desired = ma.fix_invalid([-1.3416407864999, -0.44721359549996, + 0.44721359549996, 1.3416407864999, np.nan]) + assert_almost_equal(desired, y, decimal=12) + + +class TestMisc: + + def test_obrientransform(self): + args = [[5]*5+[6]*11+[7]*9+[8]*3+[9]*2+[10]*2, + [6]+[7]*2+[8]*4+[9]*9+[10]*16] + result = [5*[3.1828]+11*[0.5591]+9*[0.0344]+3*[1.6086]+2*[5.2817]+2*[11.0538], + [10.4352]+2*[4.8599]+4*[1.3836]+9*[0.0061]+16*[0.7277]] + assert_almost_equal(np.round(mstats.obrientransform(*args).T, 4), + result, 4) + + def test_ks_2samp(self): + x = [[nan,nan, 4, 2, 16, 26, 5, 1, 5, 1, 2, 3, 1], + [4, 3, 5, 3, 2, 7, 3, 1, 1, 2, 3, 5, 3], + [3, 2, 5, 6, 18, 4, 9, 1, 1, nan, 1, 1, nan], + [nan, 6, 11, 4, 17, nan, 6, 1, 1, 2, 5, 1, 1]] + x = ma.fix_invalid(x).T + (winter, spring, summer, fall) = x.T + + assert_almost_equal(np.round(mstats.ks_2samp(winter, spring), 4), + (0.1818, 0.9628)) + assert_almost_equal(np.round(mstats.ks_2samp(winter, spring, 'g'), 4), + (0.1469, 0.6886)) + assert_almost_equal(np.round(mstats.ks_2samp(winter, spring, 'l'), 4), + (0.1818, 0.6011)) + + def test_friedmanchisq(self): + # No missing values + args = ([9.0,9.5,5.0,7.5,9.5,7.5,8.0,7.0,8.5,6.0], + [7.0,6.5,7.0,7.5,5.0,8.0,6.0,6.5,7.0,7.0], + [6.0,8.0,4.0,6.0,7.0,6.5,6.0,4.0,6.5,3.0]) + result = mstats.friedmanchisquare(*args) + assert_almost_equal(result[0], 10.4737, 4) + assert_almost_equal(result[1], 0.005317, 6) + # Missing values + x = [[nan,nan, 4, 2, 16, 26, 5, 1, 5, 1, 2, 3, 1], + [4, 3, 5, 3, 2, 7, 3, 1, 1, 2, 3, 5, 3], + [3, 2, 5, 6, 18, 4, 9, 1, 1,nan, 1, 1,nan], + [nan, 6, 11, 4, 17,nan, 6, 1, 1, 2, 5, 1, 1]] + x = ma.fix_invalid(x) + result = mstats.friedmanchisquare(*x) + assert_almost_equal(result[0], 2.0156, 4) + assert_almost_equal(result[1], 0.5692, 4) + + # test for namedtuple attributes + attributes = ('statistic', 'pvalue') + check_named_results(result, attributes, ma=True) + + +def test_regress_simple(): + # Regress a line with sinusoidal noise. Test for #1273. + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + + result = mstats.linregress(x, y) + + # Result is of a correct class and with correct fields + lr = stats._stats_mstats_common.LinregressResult + assert_(isinstance(result, lr)) + attributes = ('slope', 'intercept', 'rvalue', 'pvalue', 'stderr') + check_named_results(result, attributes, ma=True) + assert 'intercept_stderr' in dir(result) + + # Slope and intercept are estimated correctly + assert_almost_equal(result.slope, 0.19644990055858422) + assert_almost_equal(result.intercept, 10.211269918932341) + assert_almost_equal(result.stderr, 0.002395781449783862) + assert_almost_equal(result.intercept_stderr, 0.13866936078570702) + + +def test_linregress_identical_x(): + x = np.zeros(10) + y = np.random.random(10) + msg = "Cannot calculate a linear regression if all x values are identical" + with assert_raises(ValueError, match=msg): + mstats.linregress(x, y) + + +class TestTheilslopes: + def test_theilslopes(self): + # Test for basic slope and intercept. + slope, intercept, lower, upper = mstats.theilslopes([0, 1, 1]) + assert_almost_equal(slope, 0.5) + assert_almost_equal(intercept, 0.5) + + slope, intercept, lower, upper = mstats.theilslopes([0, 1, 1], + method='joint') + assert_almost_equal(slope, 0.5) + assert_almost_equal(intercept, 0.0) + + # Test for correct masking. + y = np.ma.array([0, 1, 100, 1], mask=[False, False, True, False]) + slope, intercept, lower, upper = mstats.theilslopes(y) + assert_almost_equal(slope, 1./3) + assert_almost_equal(intercept, 2./3) + + slope, intercept, lower, upper = mstats.theilslopes(y, + method='joint') + assert_almost_equal(slope, 1./3) + assert_almost_equal(intercept, 0.0) + + # Test of confidence intervals from example in Sen (1968). + x = [1, 2, 3, 4, 10, 12, 18] + y = [9, 15, 19, 20, 45, 55, 78] + slope, intercept, lower, upper = mstats.theilslopes(y, x, 0.07) + assert_almost_equal(slope, 4) + assert_almost_equal(intercept, 4.0) + assert_almost_equal(upper, 4.38, decimal=2) + assert_almost_equal(lower, 3.71, decimal=2) + + slope, intercept, lower, upper = mstats.theilslopes(y, x, 0.07, + method='joint') + assert_almost_equal(slope, 4) + assert_almost_equal(intercept, 6.0) + assert_almost_equal(upper, 4.38, decimal=2) + assert_almost_equal(lower, 3.71, decimal=2) + + + def test_theilslopes_warnings(self): + # Test `theilslopes` with degenerate input; see gh-15943 + msg = "All `x` coordinates.*|Mean of empty slice.|invalid value encountered.*" + with pytest.warns(RuntimeWarning, match=msg): + res = mstats.theilslopes([0, 1], [0, 0]) + assert np.all(np.isnan(res)) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered...") + res = mstats.theilslopes([0, 0, 0], [0, 1, 0]) + assert_allclose(res, (0, 0, np.nan, np.nan)) + + + def test_theilslopes_namedtuple_consistency(self): + """ + Simple test to ensure tuple backwards-compatibility of the returned + TheilslopesResult object + """ + y = [1, 2, 4] + x = [4, 6, 8] + slope, intercept, low_slope, high_slope = mstats.theilslopes(y, x) + result = mstats.theilslopes(y, x) + + # note all four returned values are distinct here + assert_equal(slope, result.slope) + assert_equal(intercept, result.intercept) + assert_equal(low_slope, result.low_slope) + assert_equal(high_slope, result.high_slope) + + def test_gh19678_uint8(self): + # `theilslopes` returned unexpected results when `y` was an unsigned type. + # Check that this is resolved. + rng = np.random.default_rng(2549824598234528) + y = rng.integers(0, 255, size=10, dtype=np.uint8) + res = stats.theilslopes(y, y) + np.testing.assert_allclose(res.slope, 1) + + +def test_siegelslopes(): + # method should be exact for straight line + y = 2 * np.arange(10) + 0.5 + assert_equal(mstats.siegelslopes(y), (2.0, 0.5)) + assert_equal(mstats.siegelslopes(y, method='separate'), (2.0, 0.5)) + + x = 2 * np.arange(10) + y = 5 * x - 3.0 + assert_equal(mstats.siegelslopes(y, x), (5.0, -3.0)) + assert_equal(mstats.siegelslopes(y, x, method='separate'), (5.0, -3.0)) + + # method is robust to outliers: brekdown point of 50% + y[:4] = 1000 + assert_equal(mstats.siegelslopes(y, x), (5.0, -3.0)) + + # if there are no outliers, results should be comparble to linregress + x = np.arange(10) + y = -2.3 + 0.3*x + stats.norm.rvs(size=10, random_state=231) + slope_ols, intercept_ols, _, _, _ = stats.linregress(x, y) + + slope, intercept = mstats.siegelslopes(y, x) + assert_allclose(slope, slope_ols, rtol=0.1) + assert_allclose(intercept, intercept_ols, rtol=0.1) + + slope, intercept = mstats.siegelslopes(y, x, method='separate') + assert_allclose(slope, slope_ols, rtol=0.1) + assert_allclose(intercept, intercept_ols, rtol=0.1) + + +def test_siegelslopes_namedtuple_consistency(): + """ + Simple test to ensure tuple backwards-compatibility of the returned + SiegelslopesResult object. + """ + y = [1, 2, 4] + x = [4, 6, 8] + slope, intercept = mstats.siegelslopes(y, x) + result = mstats.siegelslopes(y, x) + + # note both returned values are distinct here + assert_equal(slope, result.slope) + assert_equal(intercept, result.intercept) + + +def test_sen_seasonal_slopes(): + rng = np.random.default_rng(5765986256978575148) + x = rng.random(size=(100, 4)) + intra_slope, inter_slope = mstats.sen_seasonal_slopes(x) + + # reference implementation from the `sen_seasonal_slopes` documentation + def dijk(yi): + n = len(yi) + x = np.arange(n) + dy = yi - yi[:, np.newaxis] + dx = x - x[:, np.newaxis] + mask = np.triu(np.ones((n, n), dtype=bool), k=1) + return dy[mask]/dx[mask] + + for i in range(4): + assert_allclose(np.median(dijk(x[:, i])), intra_slope[i]) + + all_slopes = np.concatenate([dijk(x[:, i]) for i in range(x.shape[1])]) + assert_allclose(np.median(all_slopes), inter_slope) + + +def test_plotting_positions(): + # Regression test for #1256 + pos = mstats.plotting_positions(np.arange(3), 0, 0) + assert_array_almost_equal(pos.data, np.array([0.25, 0.5, 0.75])) + + +class TestNormalitytests: + + def test_vs_nonmasked(self): + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + assert_array_almost_equal(mstats.normaltest(x), + stats.normaltest(x)) + assert_array_almost_equal(mstats.skewtest(x), + stats.skewtest(x)) + assert_array_almost_equal(mstats.kurtosistest(x), + stats.kurtosistest(x)) + + funcs = [stats.normaltest, stats.skewtest, stats.kurtosistest] + mfuncs = [mstats.normaltest, mstats.skewtest, mstats.kurtosistest] + x = [1, 2, 3, 4] + for func, mfunc in zip(funcs, mfuncs): + assert_raises(ValueError, func, x) + assert_raises(ValueError, mfunc, x) + + def test_axis_None(self): + # Test axis=None (equal to axis=0 for 1-D input) + x = np.array((-2,-1,0,1,2,3)*4)**2 + assert_allclose(mstats.normaltest(x, axis=None), mstats.normaltest(x)) + assert_allclose(mstats.skewtest(x, axis=None), mstats.skewtest(x)) + assert_allclose(mstats.kurtosistest(x, axis=None), + mstats.kurtosistest(x)) + + def test_maskedarray_input(self): + # Add some masked values, test result doesn't change + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + xm = np.ma.array(np.r_[np.inf, x, 10], + mask=np.r_[True, [False] * x.size, True]) + assert_allclose(mstats.normaltest(xm), stats.normaltest(x)) + assert_allclose(mstats.skewtest(xm), stats.skewtest(x)) + assert_allclose(mstats.kurtosistest(xm), stats.kurtosistest(x)) + + def test_nd_input(self): + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + x_2d = np.vstack([x] * 2).T + for func in [mstats.normaltest, mstats.skewtest, mstats.kurtosistest]: + res_1d = func(x) + res_2d = func(x_2d) + assert_allclose(res_2d[0], [res_1d[0]] * 2) + assert_allclose(res_2d[1], [res_1d[1]] * 2) + + def test_normaltest_result_attributes(self): + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + res = mstats.normaltest(x) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_kurtosistest_result_attributes(self): + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + res = mstats.kurtosistest(x) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_regression_9033(self): + # x clearly non-normal but power of negative denom needs + # to be handled correctly to reject normality + counts = [128, 0, 58, 7, 0, 41, 16, 0, 0, 167] + x = np.hstack([np.full(c, i) for i, c in enumerate(counts)]) + assert_equal(mstats.kurtosistest(x)[1] < 0.01, True) + + @pytest.mark.parametrize("test", ["skewtest", "kurtosistest"]) + @pytest.mark.parametrize("alternative", ["less", "greater"]) + def test_alternative(self, test, alternative): + x = stats.norm.rvs(loc=10, scale=2.5, size=30, random_state=123) + + stats_test = getattr(stats, test) + mstats_test = getattr(mstats, test) + + z_ex, p_ex = stats_test(x, alternative=alternative) + z, p = mstats_test(x, alternative=alternative) + assert_allclose(z, z_ex, atol=1e-12) + assert_allclose(p, p_ex, atol=1e-12) + + # test with masked arrays + x[1:5] = np.nan + x = np.ma.masked_array(x, mask=np.isnan(x)) + z_ex, p_ex = stats_test(x.compressed(), alternative=alternative) + z, p = mstats_test(x, alternative=alternative) + assert_allclose(z, z_ex, atol=1e-12) + assert_allclose(p, p_ex, atol=1e-12) + + def test_bad_alternative(self): + x = stats.norm.rvs(size=20, random_state=123) + msg = r"`alternative` must be..." + + with pytest.raises(ValueError, match=msg): + mstats.skewtest(x, alternative='error') + + with pytest.raises(ValueError, match=msg): + mstats.kurtosistest(x, alternative='error') + + +class TestFOneway: + def test_result_attributes(self): + a = np.array([655, 788], dtype=np.uint16) + b = np.array([789, 772], dtype=np.uint16) + res = mstats.f_oneway(a, b) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + +class TestMannwhitneyu: + # data from gh-1428 + x = np.array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 2., 1., 1., 1., 1., 2., 1., 1., 2., 1., 1., 2., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 3., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1.]) + + y = np.array([1., 1., 1., 1., 1., 1., 1., 2., 1., 2., 1., 1., 1., 1., + 2., 1., 1., 1., 2., 1., 1., 1., 1., 1., 2., 1., 1., 3., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 1., 2., 1., + 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 2., + 2., 1., 1., 2., 1., 1., 2., 1., 2., 1., 1., 1., 1., 2., + 2., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 2., 1., 1., 1., 1., 1., 2., 2., 2., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 2., 1., 1., 2., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 2., 1., 1., + 1., 1., 1., 1.]) + + def test_result_attributes(self): + res = mstats.mannwhitneyu(self.x, self.y) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_against_stats(self): + # gh-4641 reported that stats.mannwhitneyu returned half the p-value + # of mstats.mannwhitneyu. Default alternative of stats.mannwhitneyu + # is now two-sided, so they match. + res1 = mstats.mannwhitneyu(self.x, self.y) + res2 = stats.mannwhitneyu(self.x, self.y) + assert res1.statistic == res2.statistic + assert_allclose(res1.pvalue, res2.pvalue) + + +class TestKruskal: + def test_result_attributes(self): + x = [1, 3, 5, 7, 9] + y = [2, 4, 6, 8, 10] + + res = mstats.kruskal(x, y) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + +# TODO: for all ttest functions, add tests with masked array inputs +class TestTtest_rel: + def test_vs_nonmasked(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + # 1-D inputs + res1 = stats.ttest_rel(outcome[:, 0], outcome[:, 1]) + res2 = mstats.ttest_rel(outcome[:, 0], outcome[:, 1]) + assert_allclose(res1, res2) + + # 2-D inputs + res1 = stats.ttest_rel(outcome[:, 0], outcome[:, 1], axis=None) + res2 = mstats.ttest_rel(outcome[:, 0], outcome[:, 1], axis=None) + assert_allclose(res1, res2) + res1 = stats.ttest_rel(outcome[:, :2], outcome[:, 2:], axis=0) + res2 = mstats.ttest_rel(outcome[:, :2], outcome[:, 2:], axis=0) + assert_allclose(res1, res2) + + # Check default is axis=0 + res3 = mstats.ttest_rel(outcome[:, :2], outcome[:, 2:]) + assert_allclose(res2, res3) + + def test_fully_masked(self): + np.random.seed(1234567) + outcome = ma.masked_array(np.random.randn(3, 2), + mask=[[1, 1, 1], [0, 0, 0]]) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + for pair in [(outcome[:, 0], outcome[:, 1]), + ([np.nan, np.nan], [1.0, 2.0])]: + t, p = mstats.ttest_rel(*pair) + assert_array_equal(t, (np.nan, np.nan)) + assert_array_equal(p, (np.nan, np.nan)) + + def test_result_attributes(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + res = mstats.ttest_rel(outcome[:, 0], outcome[:, 1]) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_invalid_input_size(self): + assert_raises(ValueError, mstats.ttest_rel, + np.arange(10), np.arange(11)) + x = np.arange(24) + assert_raises(ValueError, mstats.ttest_rel, + x.reshape(2, 3, 4), x.reshape(2, 4, 3), axis=1) + assert_raises(ValueError, mstats.ttest_rel, + x.reshape(2, 3, 4), x.reshape(2, 4, 3), axis=2) + + def test_empty(self): + res1 = mstats.ttest_rel([], []) + assert_(np.all(np.isnan(res1))) + + def test_zero_division(self): + t, p = mstats.ttest_ind([0, 0, 0], [1, 1, 1]) + assert_equal((np.abs(t), p), (np.inf, 0)) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + t, p = mstats.ttest_ind([0, 0, 0], [0, 0, 0]) + assert_array_equal(t, np.array([np.nan, np.nan])) + assert_array_equal(p, np.array([np.nan, np.nan])) + + def test_bad_alternative(self): + msg = r"alternative must be 'less', 'greater' or 'two-sided'" + with pytest.raises(ValueError, match=msg): + mstats.ttest_ind([1, 2, 3], [4, 5, 6], alternative='foo') + + @pytest.mark.parametrize("alternative", ["less", "greater"]) + def test_alternative(self, alternative): + x = stats.norm.rvs(loc=10, scale=5, size=25, random_state=42) + y = stats.norm.rvs(loc=8, scale=2, size=25, random_state=42) + + t_ex, p_ex = stats.ttest_rel(x, y, alternative=alternative) + t, p = mstats.ttest_rel(x, y, alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + # test with masked arrays + x[1:10] = np.nan + y[1:10] = np.nan + x = np.ma.masked_array(x, mask=np.isnan(x)) + y = np.ma.masked_array(y, mask=np.isnan(y)) + t, p = mstats.ttest_rel(x, y, alternative=alternative) + t_ex, p_ex = stats.ttest_rel(x.compressed(), y.compressed(), + alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + +class TestTtest_ind: + def test_vs_nonmasked(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + # 1-D inputs + res1 = stats.ttest_ind(outcome[:, 0], outcome[:, 1]) + res2 = mstats.ttest_ind(outcome[:, 0], outcome[:, 1]) + assert_allclose(res1, res2) + + # 2-D inputs + res1 = stats.ttest_ind(outcome[:, 0], outcome[:, 1], axis=None) + res2 = mstats.ttest_ind(outcome[:, 0], outcome[:, 1], axis=None) + assert_allclose(res1, res2) + res1 = stats.ttest_ind(outcome[:, :2], outcome[:, 2:], axis=0) + res2 = mstats.ttest_ind(outcome[:, :2], outcome[:, 2:], axis=0) + assert_allclose(res1, res2) + + # Check default is axis=0 + res3 = mstats.ttest_ind(outcome[:, :2], outcome[:, 2:]) + assert_allclose(res2, res3) + + # Check equal_var + res4 = stats.ttest_ind(outcome[:, 0], outcome[:, 1], equal_var=True) + res5 = mstats.ttest_ind(outcome[:, 0], outcome[:, 1], equal_var=True) + assert_allclose(res4, res5) + res4 = stats.ttest_ind(outcome[:, 0], outcome[:, 1], equal_var=False) + res5 = mstats.ttest_ind(outcome[:, 0], outcome[:, 1], equal_var=False) + assert_allclose(res4, res5) + + def test_fully_masked(self): + np.random.seed(1234567) + outcome = ma.masked_array(np.random.randn(3, 2), mask=[[1, 1, 1], [0, 0, 0]]) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + for pair in [(outcome[:, 0], outcome[:, 1]), + ([np.nan, np.nan], [1.0, 2.0])]: + t, p = mstats.ttest_ind(*pair) + assert_array_equal(t, (np.nan, np.nan)) + assert_array_equal(p, (np.nan, np.nan)) + + def test_result_attributes(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + res = mstats.ttest_ind(outcome[:, 0], outcome[:, 1]) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_empty(self): + res1 = mstats.ttest_ind([], []) + assert_(np.all(np.isnan(res1))) + + def test_zero_division(self): + t, p = mstats.ttest_ind([0, 0, 0], [1, 1, 1]) + assert_equal((np.abs(t), p), (np.inf, 0)) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + t, p = mstats.ttest_ind([0, 0, 0], [0, 0, 0]) + assert_array_equal(t, (np.nan, np.nan)) + assert_array_equal(p, (np.nan, np.nan)) + + t, p = mstats.ttest_ind([0, 0, 0], [1, 1, 1], equal_var=False) + assert_equal((np.abs(t), p), (np.inf, 0)) + assert_array_equal(mstats.ttest_ind([0, 0, 0], [0, 0, 0], + equal_var=False), (np.nan, np.nan)) + + def test_bad_alternative(self): + msg = r"alternative must be 'less', 'greater' or 'two-sided'" + with pytest.raises(ValueError, match=msg): + mstats.ttest_ind([1, 2, 3], [4, 5, 6], alternative='foo') + + @pytest.mark.parametrize("alternative", ["less", "greater"]) + def test_alternative(self, alternative): + x = stats.norm.rvs(loc=10, scale=2, size=100, random_state=123) + y = stats.norm.rvs(loc=8, scale=2, size=100, random_state=123) + + t_ex, p_ex = stats.ttest_ind(x, y, alternative=alternative) + t, p = mstats.ttest_ind(x, y, alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + # test with masked arrays + x[1:10] = np.nan + y[80:90] = np.nan + x = np.ma.masked_array(x, mask=np.isnan(x)) + y = np.ma.masked_array(y, mask=np.isnan(y)) + t_ex, p_ex = stats.ttest_ind(x.compressed(), y.compressed(), + alternative=alternative) + t, p = mstats.ttest_ind(x, y, alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + +class TestTtest_1samp: + def test_vs_nonmasked(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + # 1-D inputs + res1 = stats.ttest_1samp(outcome[:, 0], 1) + res2 = mstats.ttest_1samp(outcome[:, 0], 1) + assert_allclose(res1, res2) + + def test_fully_masked(self): + np.random.seed(1234567) + outcome = ma.masked_array(np.random.randn(3), mask=[1, 1, 1]) + expected = (np.nan, np.nan) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + for pair in [((np.nan, np.nan), 0.0), (outcome, 0.0)]: + t, p = mstats.ttest_1samp(*pair) + assert_array_equal(p, expected) + assert_array_equal(t, expected) + + def test_result_attributes(self): + np.random.seed(1234567) + outcome = np.random.randn(20, 4) + [0, 0, 1, 2] + + res = mstats.ttest_1samp(outcome[:, 0], 1) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_empty(self): + res1 = mstats.ttest_1samp([], 1) + assert_(np.all(np.isnan(res1))) + + def test_zero_division(self): + t, p = mstats.ttest_1samp([0, 0, 0], 1) + assert_equal((np.abs(t), p), (np.inf, 0)) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in absolute") + t, p = mstats.ttest_1samp([0, 0, 0], 0) + assert_(np.isnan(t)) + assert_array_equal(p, (np.nan, np.nan)) + + def test_bad_alternative(self): + msg = r"alternative must be 'less', 'greater' or 'two-sided'" + with pytest.raises(ValueError, match=msg): + mstats.ttest_1samp([1, 2, 3], 4, alternative='foo') + + @pytest.mark.parametrize("alternative", ["less", "greater"]) + def test_alternative(self, alternative): + x = stats.norm.rvs(loc=10, scale=2, size=100, random_state=123) + + t_ex, p_ex = stats.ttest_1samp(x, 9, alternative=alternative) + t, p = mstats.ttest_1samp(x, 9, alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + # test with masked arrays + x[1:10] = np.nan + x = np.ma.masked_array(x, mask=np.isnan(x)) + t_ex, p_ex = stats.ttest_1samp(x.compressed(), 9, + alternative=alternative) + t, p = mstats.ttest_1samp(x, 9, alternative=alternative) + assert_allclose(t, t_ex, rtol=1e-14) + assert_allclose(p, p_ex, rtol=1e-14) + + +class TestDescribe: + """ + Tests for mstats.describe. + + Note that there are also tests for `mstats.describe` in the + class TestCompareWithStats. + """ + def test_basic_with_axis(self): + # This is a basic test that is also a regression test for gh-7303. + a = np.ma.masked_array([[0, 1, 2, 3, 4, 9], + [5, 5, 0, 9, 3, 3]], + mask=[[0, 0, 0, 0, 0, 1], + [0, 0, 1, 1, 0, 0]]) + result = mstats.describe(a, axis=1) + assert_equal(result.nobs, [5, 4]) + amin, amax = result.minmax + assert_equal(amin, [0, 3]) + assert_equal(amax, [4, 5]) + assert_equal(result.mean, [2.0, 4.0]) + assert_equal(result.variance, [2.0, 1.0]) + assert_equal(result.skewness, [0.0, 0.0]) + assert_allclose(result.kurtosis, [-1.3, -2.0]) + + +class TestCompareWithStats: + """ + Class to compare mstats results with stats results. + + It is in general assumed that scipy.stats is at a more mature stage than + stats.mstats. If a routine in mstats results in similar results like in + scipy.stats, this is considered also as a proper validation of scipy.mstats + routine. + + Different sample sizes are used for testing, as some problems between stats + and mstats are dependent on sample size. + + Author: Alexander Loew + + NOTE that some tests fail. This might be caused by + a) actual differences or bugs between stats and mstats + b) numerical inaccuracies + c) different definitions of routine interfaces + + These failures need to be checked. Current workaround is to have disabled these + tests, but issuing reports on scipy-dev + + """ + def get_n(self): + """ Returns list of sample sizes to be used for comparison. """ + return [1000, 100, 10, 5] + + def generate_xy_sample(self, n): + # This routine generates numpy arrays and corresponding masked arrays + # with the same data, but additional masked values + np.random.seed(1234567) + x = np.random.randn(n) + y = x + np.random.randn(n) + xm = np.full(len(x) + 5, 1e16) + ym = np.full(len(y) + 5, 1e16) + xm[0:len(x)] = x + ym[0:len(y)] = y + mask = xm > 9e15 + xm = np.ma.array(xm, mask=mask) + ym = np.ma.array(ym, mask=mask) + return x, y, xm, ym + + def generate_xy_sample2D(self, n, nx): + x = np.full((n, nx), np.nan) + y = np.full((n, nx), np.nan) + xm = np.full((n+5, nx), np.nan) + ym = np.full((n+5, nx), np.nan) + + for i in range(nx): + x[:, i], y[:, i], dx, dy = self.generate_xy_sample(n) + + xm[0:n, :] = x[0:n] + ym[0:n, :] = y[0:n] + xm = np.ma.array(xm, mask=np.isnan(xm)) + ym = np.ma.array(ym, mask=np.isnan(ym)) + return x, y, xm, ym + + def test_linregress(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + result1 = stats.linregress(x, y) + result2 = stats.mstats.linregress(xm, ym) + assert_allclose(np.asarray(result1), np.asarray(result2)) + + def test_pearsonr(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r, p = stats.pearsonr(x, y) + rm, pm = stats.mstats.pearsonr(xm, ym) + + assert_almost_equal(r, rm, decimal=14) + assert_almost_equal(p, pm, decimal=14) + + def test_spearmanr(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r, p = stats.spearmanr(x, y) + rm, pm = stats.mstats.spearmanr(xm, ym) + assert_almost_equal(r, rm, 14) + assert_almost_equal(p, pm, 14) + + def test_spearmanr_backcompat_useties(self): + # A regression test to ensure we don't break backwards compat + # more than we have to (see gh-9204). + x = np.arange(6) + assert_raises(ValueError, mstats.spearmanr, x, x, False) + + def test_gmean(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.gmean(abs(x)) + rm = stats.mstats.gmean(abs(xm)) + assert_allclose(r, rm, rtol=1e-13) + + r = stats.gmean(abs(y)) + rm = stats.mstats.gmean(abs(ym)) + assert_allclose(r, rm, rtol=1e-13) + + def test_hmean(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + + r = stats.hmean(abs(x)) + rm = stats.mstats.hmean(abs(xm)) + assert_almost_equal(r, rm, 10) + + r = stats.hmean(abs(y)) + rm = stats.mstats.hmean(abs(ym)) + assert_almost_equal(r, rm, 10) + + def test_skew(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + + r = stats.skew(x) + rm = stats.mstats.skew(xm) + assert_almost_equal(r, rm, 10) + + r = stats.skew(y) + rm = stats.mstats.skew(ym) + assert_almost_equal(r, rm, 10) + + def test_moment(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + + r = stats.moment(x) + rm = stats.mstats.moment(xm) + assert_almost_equal(r, rm, 10) + + r = stats.moment(y) + rm = stats.mstats.moment(ym) + assert_almost_equal(r, rm, 10) + + def test_zscore(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + + # reference solution + zx = (x - x.mean()) / x.std() + zy = (y - y.mean()) / y.std() + + # validate stats + assert_allclose(stats.zscore(x), zx, rtol=1e-10) + assert_allclose(stats.zscore(y), zy, rtol=1e-10) + + # compare stats and mstats + assert_allclose(stats.zscore(x), stats.mstats.zscore(xm[0:len(x)]), + rtol=1e-10) + assert_allclose(stats.zscore(y), stats.mstats.zscore(ym[0:len(y)]), + rtol=1e-10) + + def test_kurtosis(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.kurtosis(x) + rm = stats.mstats.kurtosis(xm) + assert_almost_equal(r, rm, 10) + + r = stats.kurtosis(y) + rm = stats.mstats.kurtosis(ym) + assert_almost_equal(r, rm, 10) + + def test_sem(self): + # example from stats.sem doc + a = np.arange(20).reshape(5, 4) + am = np.ma.array(a) + r = stats.sem(a, ddof=1) + rm = stats.mstats.sem(am, ddof=1) + + assert_allclose(r, 2.82842712, atol=1e-5) + assert_allclose(rm, 2.82842712, atol=1e-5) + + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.mstats.sem(xm, axis=None, ddof=0), + stats.sem(x, axis=None, ddof=0), decimal=13) + assert_almost_equal(stats.mstats.sem(ym, axis=None, ddof=0), + stats.sem(y, axis=None, ddof=0), decimal=13) + assert_almost_equal(stats.mstats.sem(xm, axis=None, ddof=1), + stats.sem(x, axis=None, ddof=1), decimal=13) + assert_almost_equal(stats.mstats.sem(ym, axis=None, ddof=1), + stats.sem(y, axis=None, ddof=1), decimal=13) + + def test_describe(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.describe(x, ddof=1) + rm = stats.mstats.describe(xm, ddof=1) + for ii in range(6): + assert_almost_equal(np.asarray(r[ii]), + np.asarray(rm[ii]), + decimal=12) + + def test_describe_result_attributes(self): + actual = mstats.describe(np.arange(5)) + attributes = ('nobs', 'minmax', 'mean', 'variance', 'skewness', + 'kurtosis') + check_named_results(actual, attributes, ma=True) + + def test_rankdata(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.rankdata(x) + rm = stats.mstats.rankdata(x) + assert_allclose(r, rm) + + def test_tmean(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.tmean(x),stats.mstats.tmean(xm), 14) + assert_almost_equal(stats.tmean(y),stats.mstats.tmean(ym), 14) + + def test_tmax(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.tmax(x,2.), + stats.mstats.tmax(xm,2.), 10) + assert_almost_equal(stats.tmax(y,2.), + stats.mstats.tmax(ym,2.), 10) + + assert_almost_equal(stats.tmax(x, upperlimit=3.), + stats.mstats.tmax(xm, upperlimit=3.), 10) + assert_almost_equal(stats.tmax(y, upperlimit=3.), + stats.mstats.tmax(ym, upperlimit=3.), 10) + + def test_tmin(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_equal(stats.tmin(x), stats.mstats.tmin(xm)) + assert_equal(stats.tmin(y), stats.mstats.tmin(ym)) + + assert_almost_equal(stats.tmin(x, lowerlimit=-1.), + stats.mstats.tmin(xm, lowerlimit=-1.), 10) + assert_almost_equal(stats.tmin(y, lowerlimit=-1.), + stats.mstats.tmin(ym, lowerlimit=-1.), 10) + + def test_zmap(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + z = stats.zmap(x, y) + zm = stats.mstats.zmap(xm, ym) + assert_allclose(z, zm[0:len(z)], atol=1e-10) + + def test_variation(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.variation(x), stats.mstats.variation(xm), + decimal=12) + assert_almost_equal(stats.variation(y), stats.mstats.variation(ym), + decimal=12) + + def test_tvar(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.tvar(x), stats.mstats.tvar(xm), + decimal=12) + assert_almost_equal(stats.tvar(y), stats.mstats.tvar(ym), + decimal=12) + + def test_trimboth(self): + a = np.arange(20) + b = stats.trimboth(a, 0.1) + bm = stats.mstats.trimboth(a, 0.1) + assert_allclose(np.sort(b), bm.data[~bm.mask]) + + def test_tsem(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + assert_almost_equal(stats.tsem(x), stats.mstats.tsem(xm), + decimal=14) + assert_almost_equal(stats.tsem(y), stats.mstats.tsem(ym), + decimal=14) + assert_almost_equal(stats.tsem(x, limits=(-2., 2.)), + stats.mstats.tsem(xm, limits=(-2., 2.)), + decimal=14) + + def test_skewtest(self): + # this test is for 1D data + for n in self.get_n(): + if n > 8: + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.skewtest(x) + rm = stats.mstats.skewtest(xm) + assert_allclose(r, rm) + + def test_skewtest_result_attributes(self): + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + res = mstats.skewtest(x) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes, ma=True) + + def test_skewtest_2D_notmasked(self): + # a normal ndarray is passed to the masked function + x = np.random.random((20, 2)) * 20. + r = stats.skewtest(x) + rm = stats.mstats.skewtest(x) + assert_allclose(np.asarray(r), np.asarray(rm)) + + def test_skewtest_2D_WithMask(self): + nx = 2 + for n in self.get_n(): + if n > 8: + x, y, xm, ym = self.generate_xy_sample2D(n, nx) + r = stats.skewtest(x) + rm = stats.mstats.skewtest(xm) + + assert_allclose(r[0][0], rm[0][0], rtol=1e-14) + assert_allclose(r[0][1], rm[0][1], rtol=1e-14) + + def test_normaltest(self): + with np.errstate(over='raise'), suppress_warnings() as sup: + sup.filter(UserWarning, "kurtosistest only valid for n>=20") + for n in self.get_n(): + if n > 8: + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.normaltest(x) + rm = stats.mstats.normaltest(xm) + assert_allclose(np.asarray(r), np.asarray(rm)) + + def test_find_repeats(self): + x = np.asarray([1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4]).astype('float') + tmp = np.asarray([1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5]).astype('float') + mask = (tmp == 5.) + xm = np.ma.array(tmp, mask=mask) + x_orig, xm_orig = x.copy(), xm.copy() + + r = stats.find_repeats(x) + rm = stats.mstats.find_repeats(xm) + + assert_equal(r, rm) + assert_equal(x, x_orig) + assert_equal(xm, xm_orig) + + # This crazy behavior is expected by count_tied_groups, but is not + # in the docstring... + _, counts = stats.mstats.find_repeats([]) + assert_equal(counts, np.array(0, dtype=np.intp)) + + def test_kendalltau(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.kendalltau(x, y) + rm = stats.mstats.kendalltau(xm, ym) + assert_almost_equal(r[0], rm[0], decimal=10) + assert_almost_equal(r[1], rm[1], decimal=7) + + def test_obrientransform(self): + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + r = stats.obrientransform(x) + rm = stats.mstats.obrientransform(xm) + assert_almost_equal(r.T, rm[0:len(x)]) + + def test_ks_1samp(self): + """Checks that mstats.ks_1samp and stats.ks_1samp agree on masked arrays.""" + for mode in ['auto', 'exact', 'asymp']: + with suppress_warnings(): + for alternative in ['less', 'greater', 'two-sided']: + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + res1 = stats.ks_1samp(x, stats.norm.cdf, + alternative=alternative, mode=mode) + res2 = stats.mstats.ks_1samp(xm, stats.norm.cdf, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res2)) + res3 = stats.ks_1samp(xm, stats.norm.cdf, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res3)) + + def test_kstest_1samp(self): + """ + Checks that 1-sample mstats.kstest and stats.kstest agree on masked arrays. + """ + for mode in ['auto', 'exact', 'asymp']: + with suppress_warnings(): + for alternative in ['less', 'greater', 'two-sided']: + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + res1 = stats.kstest(x, 'norm', + alternative=alternative, mode=mode) + res2 = stats.mstats.kstest(xm, 'norm', + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res2)) + res3 = stats.kstest(xm, 'norm', + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res3)) + + def test_ks_2samp(self): + """Checks that mstats.ks_2samp and stats.ks_2samp agree on masked arrays. + gh-8431""" + for mode in ['auto', 'exact', 'asymp']: + with suppress_warnings() as sup: + if mode in ['auto', 'exact']: + message = "ks_2samp: Exact calculation unsuccessful." + sup.filter(RuntimeWarning, message) + for alternative in ['less', 'greater', 'two-sided']: + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + res1 = stats.ks_2samp(x, y, + alternative=alternative, mode=mode) + res2 = stats.mstats.ks_2samp(xm, ym, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res2)) + res3 = stats.ks_2samp(xm, y, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res3)) + + def test_kstest_2samp(self): + """ + Checks that 2-sample mstats.kstest and stats.kstest agree on masked arrays. + """ + for mode in ['auto', 'exact', 'asymp']: + with suppress_warnings() as sup: + if mode in ['auto', 'exact']: + message = "ks_2samp: Exact calculation unsuccessful." + sup.filter(RuntimeWarning, message) + for alternative in ['less', 'greater', 'two-sided']: + for n in self.get_n(): + x, y, xm, ym = self.generate_xy_sample(n) + res1 = stats.kstest(x, y, + alternative=alternative, mode=mode) + res2 = stats.mstats.kstest(xm, ym, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res2)) + res3 = stats.kstest(xm, y, + alternative=alternative, mode=mode) + assert_equal(np.asarray(res1), np.asarray(res3)) + + +class TestBrunnerMunzel: + # Data from (Lumley, 1996) + X = np.ma.masked_invalid([1, 2, 1, 1, 1, np.nan, 1, 1, + 1, 1, 1, 2, 4, 1, 1, np.nan]) + Y = np.ma.masked_invalid([3, 3, 4, 3, np.nan, 1, 2, 3, 1, 1, 5, 4]) + significant = 14 + + def test_brunnermunzel_one_sided(self): + # Results are compared with R's lawstat package. + u1, p1 = mstats.brunnermunzel(self.X, self.Y, alternative='less') + u2, p2 = mstats.brunnermunzel(self.Y, self.X, alternative='greater') + u3, p3 = mstats.brunnermunzel(self.X, self.Y, alternative='greater') + u4, p4 = mstats.brunnermunzel(self.Y, self.X, alternative='less') + + assert_almost_equal(p1, p2, decimal=self.significant) + assert_almost_equal(p3, p4, decimal=self.significant) + assert_(p1 != p3) + assert_almost_equal(u1, 3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u2, -3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u3, 3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u4, -3.1374674823029505, + decimal=self.significant) + assert_almost_equal(p1, 0.0028931043330757342, + decimal=self.significant) + assert_almost_equal(p3, 0.99710689566692423, + decimal=self.significant) + + def test_brunnermunzel_two_sided(self): + # Results are compared with R's lawstat package. + u1, p1 = mstats.brunnermunzel(self.X, self.Y, alternative='two-sided') + u2, p2 = mstats.brunnermunzel(self.Y, self.X, alternative='two-sided') + + assert_almost_equal(p1, p2, decimal=self.significant) + assert_almost_equal(u1, 3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u2, -3.1374674823029505, + decimal=self.significant) + assert_almost_equal(p1, 0.0057862086661515377, + decimal=self.significant) + + def test_brunnermunzel_default(self): + # The default value for alternative is two-sided + u1, p1 = mstats.brunnermunzel(self.X, self.Y) + u2, p2 = mstats.brunnermunzel(self.Y, self.X) + + assert_almost_equal(p1, p2, decimal=self.significant) + assert_almost_equal(u1, 3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u2, -3.1374674823029505, + decimal=self.significant) + assert_almost_equal(p1, 0.0057862086661515377, + decimal=self.significant) + + def test_brunnermunzel_alternative_error(self): + alternative = "error" + distribution = "t" + assert_(alternative not in ["two-sided", "greater", "less"]) + assert_raises(ValueError, + mstats.brunnermunzel, + self.X, + self.Y, + alternative, + distribution) + + def test_brunnermunzel_distribution_norm(self): + u1, p1 = mstats.brunnermunzel(self.X, self.Y, distribution="normal") + u2, p2 = mstats.brunnermunzel(self.Y, self.X, distribution="normal") + assert_almost_equal(p1, p2, decimal=self.significant) + assert_almost_equal(u1, 3.1374674823029505, + decimal=self.significant) + assert_almost_equal(u2, -3.1374674823029505, + decimal=self.significant) + assert_almost_equal(p1, 0.0017041417600383024, + decimal=self.significant) + + def test_brunnermunzel_distribution_error(self): + alternative = "two-sided" + distribution = "error" + assert_(alternative not in ["t", "normal"]) + assert_raises(ValueError, + mstats.brunnermunzel, + self.X, + self.Y, + alternative, + distribution) + + def test_brunnermunzel_empty_imput(self): + u1, p1 = mstats.brunnermunzel(self.X, []) + u2, p2 = mstats.brunnermunzel([], self.Y) + u3, p3 = mstats.brunnermunzel([], []) + + assert_(np.isnan(u1)) + assert_(np.isnan(p1)) + assert_(np.isnan(u2)) + assert_(np.isnan(p2)) + assert_(np.isnan(u3)) + assert_(np.isnan(p3)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_extras.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_extras.py new file mode 100644 index 0000000000000000000000000000000000000000..4b9fd0d80a6e05652c5151f5dfece2c5979dbfe5 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_mstats_extras.py @@ -0,0 +1,172 @@ +import numpy as np +import numpy.ma as ma +import scipy.stats.mstats as ms + +from numpy.testing import (assert_equal, assert_almost_equal, assert_, + assert_allclose) + + +def test_compare_medians_ms(): + x = np.arange(7) + y = x + 10 + assert_almost_equal(ms.compare_medians_ms(x, y), 0) + + y2 = np.linspace(0, 1, num=10) + assert_almost_equal(ms.compare_medians_ms(x, y2), 0.017116406778) + + +def test_hdmedian(): + # 1-D array + x = ma.arange(11) + assert_allclose(ms.hdmedian(x), 5, rtol=1e-14) + x.mask = ma.make_mask(x) + x.mask[:7] = False + assert_allclose(ms.hdmedian(x), 3, rtol=1e-14) + + # Check that `var` keyword returns a value. TODO: check whether returned + # value is actually correct. + assert_(ms.hdmedian(x, var=True).size == 2) + + # 2-D array + x2 = ma.arange(22).reshape((11, 2)) + assert_allclose(ms.hdmedian(x2, axis=0), [10, 11]) + x2.mask = ma.make_mask(x2) + x2.mask[:7, :] = False + assert_allclose(ms.hdmedian(x2, axis=0), [6, 7]) + + +def test_rsh(): + np.random.seed(132345) + x = np.random.randn(100) + res = ms.rsh(x) + # Just a sanity check that the code runs and output shape is correct. + # TODO: check that implementation is correct. + assert_(res.shape == x.shape) + + # Check points keyword + res = ms.rsh(x, points=[0, 1.]) + assert_(res.size == 2) + + +def test_mjci(): + # Tests the Marits-Jarrett estimator + data = ma.array([77, 87, 88,114,151,210,219,246,253,262, + 296,299,306,376,428,515,666,1310,2611]) + assert_almost_equal(ms.mjci(data),[55.76819,45.84028,198.87875],5) + + +def test_trimmed_mean_ci(): + # Tests the confidence intervals of the trimmed mean. + data = ma.array([545,555,558,572,575,576,578,580, + 594,605,635,651,653,661,666]) + assert_almost_equal(ms.trimmed_mean(data,0.2), 596.2, 1) + assert_equal(np.round(ms.trimmed_mean_ci(data,(0.2,0.2)),1), + [561.8, 630.6]) + + +def test_idealfourths(): + # Tests ideal-fourths + test = np.arange(100) + assert_almost_equal(np.asarray(ms.idealfourths(test)), + [24.416667,74.583333],6) + test_2D = test.repeat(3).reshape(-1,3) + assert_almost_equal(ms.idealfourths(test_2D, axis=0), + [[24.416667,24.416667,24.416667], + [74.583333,74.583333,74.583333]],6) + assert_almost_equal(ms.idealfourths(test_2D, axis=1), + test.repeat(2).reshape(-1,2)) + test = [0, 0] + _result = ms.idealfourths(test) + assert_(np.isnan(_result).all()) + + +class TestQuantiles: + data = [0.706560797,0.727229578,0.990399276,0.927065621,0.158953014, + 0.887764025,0.239407086,0.349638551,0.972791145,0.149789972, + 0.936947700,0.132359948,0.046041972,0.641675031,0.945530547, + 0.224218684,0.771450991,0.820257774,0.336458052,0.589113496, + 0.509736129,0.696838829,0.491323573,0.622767425,0.775189248, + 0.641461450,0.118455200,0.773029450,0.319280007,0.752229111, + 0.047841438,0.466295911,0.583850781,0.840581845,0.550086491, + 0.466470062,0.504765074,0.226855960,0.362641207,0.891620942, + 0.127898691,0.490094097,0.044882048,0.041441695,0.317976349, + 0.504135618,0.567353033,0.434617473,0.636243375,0.231803616, + 0.230154113,0.160011327,0.819464108,0.854706985,0.438809221, + 0.487427267,0.786907310,0.408367937,0.405534192,0.250444460, + 0.995309248,0.144389588,0.739947527,0.953543606,0.680051621, + 0.388382017,0.863530727,0.006514031,0.118007779,0.924024803, + 0.384236354,0.893687694,0.626534881,0.473051932,0.750134705, + 0.241843555,0.432947602,0.689538104,0.136934797,0.150206859, + 0.474335206,0.907775349,0.525869295,0.189184225,0.854284286, + 0.831089744,0.251637345,0.587038213,0.254475554,0.237781276, + 0.827928620,0.480283781,0.594514455,0.213641488,0.024194386, + 0.536668589,0.699497811,0.892804071,0.093835427,0.731107772] + + def test_hdquantiles(self): + data = self.data + assert_almost_equal(ms.hdquantiles(data,[0., 1.]), + [0.006514031, 0.995309248]) + hdq = ms.hdquantiles(data,[0.25, 0.5, 0.75]) + assert_almost_equal(hdq, [0.253210762, 0.512847491, 0.762232442,]) + + data = np.array(data).reshape(10,10) + hdq = ms.hdquantiles(data,[0.25,0.5,0.75],axis=0) + assert_almost_equal(hdq[:,0], ms.hdquantiles(data[:,0],[0.25,0.5,0.75])) + assert_almost_equal(hdq[:,-1], ms.hdquantiles(data[:,-1],[0.25,0.5,0.75])) + hdq = ms.hdquantiles(data,[0.25,0.5,0.75],axis=0,var=True) + assert_almost_equal(hdq[...,0], + ms.hdquantiles(data[:,0],[0.25,0.5,0.75],var=True)) + assert_almost_equal(hdq[...,-1], + ms.hdquantiles(data[:,-1],[0.25,0.5,0.75], var=True)) + + def test_hdquantiles_sd(self): + # Standard deviation is a jackknife estimator, so we can check if + # the efficient version (hdquantiles_sd) matches a rudimentary, + # but clear version here. + + hd_std_errs = ms.hdquantiles_sd(self.data) + + # jacknnife standard error, Introduction to the Bootstrap Eq. 11.5 + n = len(self.data) + jdata = np.broadcast_to(self.data, (n, n)) + jselector = np.logical_not(np.eye(n)) # leave out one sample each row + jdata = jdata[jselector].reshape(n, n-1) + jdist = ms.hdquantiles(jdata, axis=1) + jdist_mean = np.mean(jdist, axis=0) + jstd = ((n-1)/n * np.sum((jdist - jdist_mean)**2, axis=0))**.5 + + assert_almost_equal(hd_std_errs, jstd) + # Test actual values for good measure + assert_almost_equal(hd_std_errs, [0.0379258, 0.0380656, 0.0380013]) + + two_data_points = ms.hdquantiles_sd([1, 2]) + assert_almost_equal(two_data_points, [0.5, 0.5, 0.5]) + + def test_mquantiles_cimj(self): + # Only test that code runs, implementation not checked for correctness + ci_lower, ci_upper = ms.mquantiles_cimj(self.data) + assert_(ci_lower.size == ci_upper.size == 3) + + +def test_median_cihs(): + # Basic test against R library EnvStats function `eqnpar`, e.g. + # library(EnvStats) + # options(digits=8) + # x = c(0.88612955, 0.35242375, 0.66240904, 0.94617974, 0.10929913, + # 0.76699506, 0.88550655, 0.62763754, 0.76818588, 0.68506508, + # 0.88043148, 0.03911248, 0.93805564, 0.95326961, 0.25291112, + # 0.16128487, 0.49784577, 0.24588924, 0.6597, 0.92239679) + # eqnpar(x, p=0.5, + # ci.method = "interpolate", approx.conf.level = 0.95, ci = TRUE) + rng = np.random.default_rng(8824288259505800535) + x = rng.random(size=20) + assert_allclose(ms.median_cihs(x), (0.38663198, 0.88431272)) + + # SciPy's 90% CI upper limit doesn't match that of EnvStats eqnpar. SciPy + # doesn't look wrong, and it agrees with a different reference, + # `median_confint_hs` from `hoehleatsu/quantileCI`. + # In (e.g.) Colab with R runtime: + # devtools::install_github("hoehleatsu/quantileCI") + # library(quantileCI) + # median_confint_hs(x=x, conf.level=0.90, interpolate=TRUE) + assert_allclose(ms.median_cihs(x, 0.1), (0.48319773366, 0.88094268050)) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multicomp.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multicomp.py new file mode 100644 index 0000000000000000000000000000000000000000..c85d95ebbb1609c861a338fa1c4c2b6169b8ce00 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multicomp.py @@ -0,0 +1,404 @@ +import copy + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from scipy import stats +from scipy.stats._multicomp import _pvalue_dunnett, DunnettResult + + +class TestDunnett: + # For the following tests, p-values were computed using Matlab, e.g. + # sample = [18. 15. 18. 16. 17. 15. 14. 14. 14. 15. 15.... + # 14. 15. 14. 22. 18. 21. 21. 10. 10. 11. 9.... + # 25. 26. 17.5 16. 15.5 14.5 22. 22. 24. 22.5 29.... + # 24.5 20. 18. 18.5 17.5 26.5 13. 16.5 13. 13. 13.... + # 28. 27. 34. 31. 29. 27. 24. 23. 38. 36. 25.... + # 38. 26. 22. 36. 27. 27. 32. 28. 31.... + # 24. 27. 33. 32. 28. 19. 37. 31. 36. 36.... + # 34. 38. 32. 38. 32.... + # 26. 24. 26. 25. 29. 29.5 16.5 36. 44.... + # 25. 27. 19.... + # 25. 20.... + # 28.]; + # j = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... + # 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... + # 0 0 0 0... + # 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1... + # 2 2 2 2 2 2 2 2 2... + # 3 3 3... + # 4 4... + # 5]; + # [~, ~, stats] = anova1(sample, j, "off"); + # [results, ~, ~, gnames] = multcompare(stats, ... + # "CriticalValueType", "dunnett", ... + # "Approximate", false); + # tbl = array2table(results, "VariableNames", ... + # ["Group", "Control Group", "Lower Limit", ... + # "Difference", "Upper Limit", "P-value"]); + # tbl.("Group") = gnames(tbl.("Group")); + # tbl.("Control Group") = gnames(tbl.("Control Group")) + + # Matlab doesn't report the statistic, so the statistics were + # computed using R multcomp `glht`, e.g.: + # library(multcomp) + # options(digits=16) + # control < - c(18.0, 15.0, 18.0, 16.0, 17.0, 15.0, 14.0, 14.0, 14.0, + # 15.0, 15.0, 14.0, 15.0, 14.0, 22.0, 18.0, 21.0, 21.0, + # 10.0, 10.0, 11.0, 9.0, 25.0, 26.0, 17.5, 16.0, 15.5, + # 14.5, 22.0, 22.0, 24.0, 22.5, 29.0, 24.5, 20.0, 18.0, + # 18.5, 17.5, 26.5, 13.0, 16.5, 13.0, 13.0, 13.0, 28.0, + # 27.0, 34.0, 31.0, 29.0, 27.0, 24.0, 23.0, 38.0, 36.0, + # 25.0, 38.0, 26.0, 22.0, 36.0, 27.0, 27.0, 32.0, 28.0, + # 31.0) + # t < - c(24.0, 27.0, 33.0, 32.0, 28.0, 19.0, 37.0, 31.0, 36.0, 36.0, + # 34.0, 38.0, 32.0, 38.0, 32.0) + # w < - c(26.0, 24.0, 26.0, 25.0, 29.0, 29.5, 16.5, 36.0, 44.0) + # x < - c(25.0, 27.0, 19.0) + # y < - c(25.0, 20.0) + # z < - c(28.0) + # + # groups = factor(rep(c("control", "t", "w", "x", "y", "z"), + # times=c(length(control), length(t), length(w), + # length(x), length(y), length(z)))) + # df < - data.frame(response=c(control, t, w, x, y, z), + # group=groups) + # model < - aov(response + # ~group, data = df) + # test < - glht(model=model, + # linfct=mcp(group="Dunnett"), + # alternative="g") + # summary(test) + # confint(test) + # p-values agreed with those produced by Matlab to at least atol=1e-3 + + # From Matlab's documentation on multcompare + samples_1 = [ + [ + 24.0, 27.0, 33.0, 32.0, 28.0, 19.0, 37.0, 31.0, 36.0, 36.0, + 34.0, 38.0, 32.0, 38.0, 32.0 + ], + [26.0, 24.0, 26.0, 25.0, 29.0, 29.5, 16.5, 36.0, 44.0], + [25.0, 27.0, 19.0], + [25.0, 20.0], + [28.0] + ] + control_1 = [ + 18.0, 15.0, 18.0, 16.0, 17.0, 15.0, 14.0, 14.0, 14.0, 15.0, 15.0, + 14.0, 15.0, 14.0, 22.0, 18.0, 21.0, 21.0, 10.0, 10.0, 11.0, 9.0, + 25.0, 26.0, 17.5, 16.0, 15.5, 14.5, 22.0, 22.0, 24.0, 22.5, 29.0, + 24.5, 20.0, 18.0, 18.5, 17.5, 26.5, 13.0, 16.5, 13.0, 13.0, 13.0, + 28.0, 27.0, 34.0, 31.0, 29.0, 27.0, 24.0, 23.0, 38.0, 36.0, 25.0, + 38.0, 26.0, 22.0, 36.0, 27.0, 27.0, 32.0, 28.0, 31.0 + ] + pvalue_1 = [4.727e-06, 0.022346, 0.97912, 0.99953, 0.86579] # Matlab + # Statistic, alternative p-values, and CIs computed with R multcomp `glht` + p_1_twosided = [1e-4, 0.02237, 0.97913, 0.99953, 0.86583] + p_1_greater = [1e-4, 0.011217, 0.768500, 0.896991, 0.577211] + p_1_less = [1, 1, 0.99660, 0.98398, .99953] + statistic_1 = [5.27356, 2.91270, 0.60831, 0.27002, 0.96637] + ci_1_twosided = [[5.3633917835622, 0.7296142201217, -8.3879817106607, + -11.9090753452911, -11.7655021543469], + [15.9709832164378, 13.8936496687672, 13.4556900439941, + 14.6434503452911, 25.4998771543469]] + ci_1_greater = [5.9036402398526, 1.4000632918725, -7.2754756323636, + -10.5567456382391, -9.8675629499576] + ci_1_less = [15.4306165948619, 13.2230539537359, 12.3429406339544, + 13.2908248513211, 23.6015228251660] + pvalues_1 = dict(twosided=p_1_twosided, less=p_1_less, greater=p_1_greater) + cis_1 = dict(twosided=ci_1_twosided, less=ci_1_less, greater=ci_1_greater) + case_1 = dict(samples=samples_1, control=control_1, statistic=statistic_1, + pvalues=pvalues_1, cis=cis_1) + + # From Dunnett1955 comparing with R's DescTools: DunnettTest + samples_2 = [[9.76, 8.80, 7.68, 9.36], [12.80, 9.68, 12.16, 9.20, 10.55]] + control_2 = [7.40, 8.50, 7.20, 8.24, 9.84, 8.32] + pvalue_2 = [0.6201, 0.0058] + # Statistic, alternative p-values, and CIs computed with R multcomp `glht` + p_2_twosided = [0.6201020, 0.0058254] + p_2_greater = [0.3249776, 0.0029139] + p_2_less = [0.91676, 0.99984] + statistic_2 = [0.85703, 3.69375] + ci_2_twosided = [[-1.2564116462124, 0.8396273539789], + [2.5564116462124, 4.4163726460211]] + ci_2_greater = [-0.9588591188156, 1.1187563667543] + ci_2_less = [2.2588591188156, 4.1372436332457] + pvalues_2 = dict(twosided=p_2_twosided, less=p_2_less, greater=p_2_greater) + cis_2 = dict(twosided=ci_2_twosided, less=ci_2_less, greater=ci_2_greater) + case_2 = dict(samples=samples_2, control=control_2, statistic=statistic_2, + pvalues=pvalues_2, cis=cis_2) + + samples_3 = [[55, 64, 64], [55, 49, 52], [50, 44, 41]] + control_3 = [55, 47, 48] + pvalue_3 = [0.0364, 0.8966, 0.4091] + # Statistic, alternative p-values, and CIs computed with R multcomp `glht` + p_3_twosided = [0.036407, 0.896539, 0.409295] + p_3_greater = [0.018277, 0.521109, 0.981892] + p_3_less = [0.99944, 0.90054, 0.20974] + statistic_3 = [3.09073, 0.56195, -1.40488] + ci_3_twosided = [[0.7529028025053, -8.2470971974947, -15.2470971974947], + [21.2470971974947, 12.2470971974947, 5.2470971974947]] + ci_3_greater = [2.4023682323149, -6.5976317676851, -13.5976317676851] + ci_3_less = [19.5984402363662, 10.5984402363662, 3.5984402363662] + pvalues_3 = dict(twosided=p_3_twosided, less=p_3_less, greater=p_3_greater) + cis_3 = dict(twosided=ci_3_twosided, less=ci_3_less, greater=ci_3_greater) + case_3 = dict(samples=samples_3, control=control_3, statistic=statistic_3, + pvalues=pvalues_3, cis=cis_3) + + # From Thomson and Short, + # Mucociliary function in health, chronic obstructive airway disease, + # and asbestosis, Journal of Applied Physiology, 1969. Table 1 + # Comparing with R's DescTools: DunnettTest + samples_4 = [[3.8, 2.7, 4.0, 2.4], [2.8, 3.4, 3.7, 2.2, 2.0]] + control_4 = [2.9, 3.0, 2.5, 2.6, 3.2] + pvalue_4 = [0.5832, 0.9982] + # Statistic, alternative p-values, and CIs computed with R multcomp `glht` + p_4_twosided = [0.58317, 0.99819] + p_4_greater = [0.30225, 0.69115] + p_4_less = [0.91929, 0.65212] + statistic_4 = [0.90875, -0.05007] + ci_4_twosided = [[-0.6898153448579, -1.0333456251632], + [1.4598153448579, 0.9933456251632]] + ci_4_greater = [-0.5186459268412, -0.8719655502147 ] + ci_4_less = [1.2886459268412, 0.8319655502147] + pvalues_4 = dict(twosided=p_4_twosided, less=p_4_less, greater=p_4_greater) + cis_4 = dict(twosided=ci_4_twosided, less=ci_4_less, greater=ci_4_greater) + case_4 = dict(samples=samples_4, control=control_4, statistic=statistic_4, + pvalues=pvalues_4, cis=cis_4) + + @pytest.mark.parametrize( + 'rho, n_groups, df, statistic, pvalue, alternative', + [ + # From Dunnett1955 + # Tables 1a and 1b pages 1117-1118 + (0.5, 1, 10, 1.81, 0.05, "greater"), # different than two-sided + (0.5, 3, 10, 2.34, 0.05, "greater"), + (0.5, 2, 30, 1.99, 0.05, "greater"), + (0.5, 5, 30, 2.33, 0.05, "greater"), + (0.5, 4, 12, 3.32, 0.01, "greater"), + (0.5, 7, 12, 3.56, 0.01, "greater"), + (0.5, 2, 60, 2.64, 0.01, "greater"), + (0.5, 4, 60, 2.87, 0.01, "greater"), + (0.5, 4, 60, [2.87, 2.21], [0.01, 0.05], "greater"), + # Tables 2a and 2b pages 1119-1120 + (0.5, 1, 10, 2.23, 0.05, "two-sided"), # two-sided + (0.5, 3, 10, 2.81, 0.05, "two-sided"), + (0.5, 2, 30, 2.32, 0.05, "two-sided"), + (0.5, 3, 20, 2.57, 0.05, "two-sided"), + (0.5, 4, 12, 3.76, 0.01, "two-sided"), + (0.5, 7, 12, 4.08, 0.01, "two-sided"), + (0.5, 2, 60, 2.90, 0.01, "two-sided"), + (0.5, 4, 60, 3.14, 0.01, "two-sided"), + (0.5, 4, 60, [3.14, 2.55], [0.01, 0.05], "two-sided"), + ], + ) + def test_critical_values( + self, rho, n_groups, df, statistic, pvalue, alternative + ): + rng = np.random.default_rng(165250594791731684851746311027739134893) + rho = np.full((n_groups, n_groups), rho) + np.fill_diagonal(rho, 1) + + statistic = np.array(statistic) + res = _pvalue_dunnett( + rho=rho, df=df, statistic=statistic, + alternative=alternative, + rng=rng + ) + assert_allclose(res, pvalue, atol=5e-3) + + @pytest.mark.parametrize( + 'samples, control, pvalue, statistic', + [ + (samples_1, control_1, pvalue_1, statistic_1), + (samples_2, control_2, pvalue_2, statistic_2), + (samples_3, control_3, pvalue_3, statistic_3), + (samples_4, control_4, pvalue_4, statistic_4), + ] + ) + def test_basic(self, samples, control, pvalue, statistic): + rng = np.random.default_rng(11681140010308601919115036826969764808) + + res = stats.dunnett(*samples, control=control, random_state=rng) + + assert isinstance(res, DunnettResult) + assert_allclose(res.statistic, statistic, rtol=5e-5) + assert_allclose(res.pvalue, pvalue, rtol=1e-2, atol=1e-4) + + @pytest.mark.parametrize( + 'alternative', + ['two-sided', 'less', 'greater'] + ) + def test_ttest_ind(self, alternative): + # check that `dunnett` agrees with `ttest_ind` + # when there are only two groups + rng = np.random.default_rng(114184017807316971636137493526995620351) + + for _ in range(10): + sample = rng.integers(-100, 100, size=(10,)) + control = rng.integers(-100, 100, size=(10,)) + + res = stats.dunnett( + sample, control=control, + alternative=alternative, random_state=rng + ) + ref = stats.ttest_ind( + sample, control, + alternative=alternative, random_state=rng + ) + + assert_allclose(res.statistic, ref.statistic, rtol=1e-3, atol=1e-5) + assert_allclose(res.pvalue, ref.pvalue, rtol=1e-3, atol=1e-5) + + @pytest.mark.parametrize( + 'alternative, pvalue', + [ + ('less', [0, 1]), + ('greater', [1, 0]), + ('two-sided', [0, 0]), + ] + ) + def test_alternatives(self, alternative, pvalue): + rng = np.random.default_rng(114184017807316971636137493526995620351) + + # width of 20 and min diff between samples/control is 60 + # and maximal diff would be 100 + sample_less = rng.integers(0, 20, size=(10,)) + control = rng.integers(80, 100, size=(10,)) + sample_greater = rng.integers(160, 180, size=(10,)) + + res = stats.dunnett( + sample_less, sample_greater, control=control, + alternative=alternative, random_state=rng + ) + assert_allclose(res.pvalue, pvalue, atol=1e-7) + + ci = res.confidence_interval() + # two-sided is comparable for high/low + if alternative == 'less': + assert np.isneginf(ci.low).all() + assert -100 < ci.high[0] < -60 + assert 60 < ci.high[1] < 100 + elif alternative == 'greater': + assert -100 < ci.low[0] < -60 + assert 60 < ci.low[1] < 100 + assert np.isposinf(ci.high).all() + elif alternative == 'two-sided': + assert -100 < ci.low[0] < -60 + assert 60 < ci.low[1] < 100 + assert -100 < ci.high[0] < -60 + assert 60 < ci.high[1] < 100 + + @pytest.mark.parametrize("case", [case_1, case_2, case_3, case_4]) + @pytest.mark.parametrize("alternative", ['less', 'greater', 'two-sided']) + def test_against_R_multicomp_glht(self, case, alternative): + rng = np.random.default_rng(189117774084579816190295271136455278291) + samples = case['samples'] + control = case['control'] + alternatives = {'less': 'less', 'greater': 'greater', + 'two-sided': 'twosided'} + p_ref = case['pvalues'][alternative.replace('-', '')] + + res = stats.dunnett(*samples, control=control, alternative=alternative, + random_state=rng) + # atol can't be tighter because R reports some pvalues as "< 1e-4" + assert_allclose(res.pvalue, p_ref, rtol=5e-3, atol=1e-4) + + ci_ref = case['cis'][alternatives[alternative]] + if alternative == "greater": + ci_ref = [ci_ref, np.inf] + elif alternative == "less": + ci_ref = [-np.inf, ci_ref] + assert res._ci is None + assert res._ci_cl is None + ci = res.confidence_interval(confidence_level=0.95) + assert_allclose(ci.low, ci_ref[0], rtol=5e-3, atol=1e-5) + assert_allclose(ci.high, ci_ref[1], rtol=5e-3, atol=1e-5) + + # re-run to use the cached value "is" to check id as same object + assert res._ci is ci + assert res._ci_cl == 0.95 + ci_ = res.confidence_interval(confidence_level=0.95) + assert ci_ is ci + + @pytest.mark.parametrize('alternative', ["two-sided", "less", "greater"]) + def test_str(self, alternative): + rng = np.random.default_rng(189117774084579816190295271136455278291) + + res = stats.dunnett( + *self.samples_3, control=self.control_3, alternative=alternative, + random_state=rng + ) + + # check some str output + res_str = str(res) + assert '(Sample 2 - Control)' in res_str + assert '95.0%' in res_str + + if alternative == 'less': + assert '-inf' in res_str + assert '19.' in res_str + elif alternative == 'greater': + assert 'inf' in res_str + assert '-13.' in res_str + else: + assert 'inf' not in res_str + assert '21.' in res_str + + def test_warnings(self): + rng = np.random.default_rng(189117774084579816190295271136455278291) + + res = stats.dunnett( + *self.samples_3, control=self.control_3, random_state=rng + ) + msg = r"Computation of the confidence interval did not converge" + with pytest.warns(UserWarning, match=msg): + res._allowance(tol=1e-5) + + def test_raises(self): + samples, control = self.samples_3, self.control_3 + + # alternative + with pytest.raises(ValueError, match="alternative must be"): + stats.dunnett(*samples, control=control, alternative='bob') + + # 2D for a sample + samples_ = copy.deepcopy(samples) + samples_[0] = [samples_[0]] + with pytest.raises(ValueError, match="must be 1D arrays"): + stats.dunnett(*samples_, control=control) + + # 2D for control + control_ = copy.deepcopy(control) + control_ = [control_] + with pytest.raises(ValueError, match="must be 1D arrays"): + stats.dunnett(*samples, control=control_) + + # No obs in a sample + samples_ = copy.deepcopy(samples) + samples_[1] = [] + with pytest.raises(ValueError, match="at least 1 observation"): + stats.dunnett(*samples_, control=control) + + # No obs in control + control_ = [] + with pytest.raises(ValueError, match="at least 1 observation"): + stats.dunnett(*samples, control=control_) + + res = stats.dunnett(*samples, control=control) + with pytest.raises(ValueError, match="Confidence level must"): + res.confidence_interval(confidence_level=3) + + @pytest.mark.filterwarnings("ignore:Computation of the confidence") + @pytest.mark.parametrize('n_samples', [1, 2, 3]) + def test_shapes(self, n_samples): + rng = np.random.default_rng(689448934110805334) + samples = rng.normal(size=(n_samples, 10)) + control = rng.normal(size=10) + res = stats.dunnett(*samples, control=control, random_state=rng) + assert res.statistic.shape == (n_samples,) + assert res.pvalue.shape == (n_samples,) + ci = res.confidence_interval() + assert ci.low.shape == (n_samples,) + assert ci.high.shape == (n_samples,) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multivariate.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multivariate.py new file mode 100644 index 0000000000000000000000000000000000000000..824660923144abf3fba427abada2ff1603ed5bc8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_multivariate.py @@ -0,0 +1,3859 @@ +""" +Test functions for multivariate normal distributions. + +""" +import pickle + +from numpy.testing import (assert_allclose, assert_almost_equal, + assert_array_almost_equal, assert_equal, + assert_array_less, assert_) +import pytest +from pytest import raises as assert_raises + +from .test_continuous_basic import check_distribution_rvs + +import numpy +import numpy as np + +import scipy.linalg +from scipy.stats._multivariate import (_PSD, + _lnB, + multivariate_normal_frozen) +from scipy.stats import (multivariate_normal, multivariate_hypergeom, + matrix_normal, special_ortho_group, ortho_group, + random_correlation, unitary_group, dirichlet, + beta, wishart, multinomial, invwishart, chi2, + invgamma, norm, uniform, ks_2samp, kstest, binom, + hypergeom, multivariate_t, cauchy, normaltest, + random_table, uniform_direction, vonmises_fisher, + dirichlet_multinomial, vonmises) + +from scipy.stats import _covariance, Covariance +from scipy import stats + +from scipy.integrate import romb, qmc_quad, tplquad +from scipy.special import multigammaln +from scipy._lib._pep440 import Version + +from .common_tests import check_random_state_property +from .data._mvt import _qsimvtv + +from unittest.mock import patch + + +def assert_close(res, ref, *args, **kwargs): + res, ref = np.asarray(res), np.asarray(ref) + assert_allclose(res, ref, *args, **kwargs) + assert_equal(res.shape, ref.shape) + + +class TestCovariance: + + def test_input_validation(self): + + message = "The input `precision` must be a square, two-dimensional..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaPrecision(np.ones(2)) + + message = "`precision.shape` must equal `covariance.shape`." + with pytest.raises(ValueError, match=message): + _covariance.CovViaPrecision(np.eye(3), covariance=np.eye(2)) + + message = "The input `diagonal` must be a one-dimensional array..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaDiagonal("alpaca") + + message = "The input `cholesky` must be a square, two-dimensional..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaCholesky(np.ones(2)) + + message = "The input `eigenvalues` must be a one-dimensional..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaEigendecomposition(("alpaca", np.eye(2))) + + message = "The input `eigenvectors` must be a square..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaEigendecomposition((np.ones(2), "alpaca")) + + message = "The shapes of `eigenvalues` and `eigenvectors` must be..." + with pytest.raises(ValueError, match=message): + _covariance.CovViaEigendecomposition(([1, 2, 3], np.eye(2))) + + _covariance_preprocessing = {"Diagonal": np.diag, + "Precision": np.linalg.inv, + "Cholesky": np.linalg.cholesky, + "Eigendecomposition": np.linalg.eigh, + "PSD": lambda x: + _PSD(x, allow_singular=True)} + _all_covariance_types = np.array(list(_covariance_preprocessing)) + _matrices = {"diagonal full rank": np.diag([1, 2, 3]), + "general full rank": [[5, 1, 3], [1, 6, 4], [3, 4, 7]], + "diagonal singular": np.diag([1, 0, 3]), + "general singular": [[5, -1, 0], [-1, 5, 0], [0, 0, 0]]} + _cov_types = {"diagonal full rank": _all_covariance_types, + "general full rank": _all_covariance_types[1:], + "diagonal singular": _all_covariance_types[[0, -2, -1]], + "general singular": _all_covariance_types[-2:]} + + @pytest.mark.parametrize("cov_type_name", _all_covariance_types[:-1]) + def test_factories(self, cov_type_name): + A = np.diag([1, 2, 3]) + x = [-4, 2, 5] + + cov_type = getattr(_covariance, f"CovVia{cov_type_name}") + preprocessing = self._covariance_preprocessing[cov_type_name] + factory = getattr(Covariance, f"from_{cov_type_name.lower()}") + + res = factory(preprocessing(A)) + ref = cov_type(preprocessing(A)) + assert type(res) == type(ref) + assert_allclose(res.whiten(x), ref.whiten(x)) + + @pytest.mark.parametrize("matrix_type", list(_matrices)) + @pytest.mark.parametrize("cov_type_name", _all_covariance_types) + def test_covariance(self, matrix_type, cov_type_name): + message = (f"CovVia{cov_type_name} does not support {matrix_type} " + "matrices") + if cov_type_name not in self._cov_types[matrix_type]: + pytest.skip(message) + + A = self._matrices[matrix_type] + cov_type = getattr(_covariance, f"CovVia{cov_type_name}") + preprocessing = self._covariance_preprocessing[cov_type_name] + + psd = _PSD(A, allow_singular=True) + + # test properties + cov_object = cov_type(preprocessing(A)) + assert_close(cov_object.log_pdet, psd.log_pdet) + assert_equal(cov_object.rank, psd.rank) + assert_equal(cov_object.shape, np.asarray(A).shape) + assert_close(cov_object.covariance, np.asarray(A)) + + # test whitening/coloring 1D x + rng = np.random.default_rng(5292808890472453840) + x = rng.random(size=3) + res = cov_object.whiten(x) + ref = x @ psd.U + # res != ref in general; but res @ res == ref @ ref + assert_close(res @ res, ref @ ref) + if hasattr(cov_object, "_colorize") and "singular" not in matrix_type: + # CovViaPSD does not have _colorize + assert_close(cov_object.colorize(res), x) + + # test whitening/coloring 3D x + x = rng.random(size=(2, 4, 3)) + res = cov_object.whiten(x) + ref = x @ psd.U + assert_close((res**2).sum(axis=-1), (ref**2).sum(axis=-1)) + if hasattr(cov_object, "_colorize") and "singular" not in matrix_type: + assert_close(cov_object.colorize(res), x) + + # gh-19197 reported that multivariate normal `rvs` produced incorrect + # results when a singular Covariance object was produce using + # `from_eigenvalues`. This was due to an issue in `colorize` with + # singular covariance matrices. Check this edge case, which is skipped + # in the previous tests. + if hasattr(cov_object, "_colorize"): + res = cov_object.colorize(np.eye(len(A))) + assert_close(res.T @ res, A) + + @pytest.mark.parametrize("size", [None, tuple(), 1, (2, 4, 3)]) + @pytest.mark.parametrize("matrix_type", list(_matrices)) + @pytest.mark.parametrize("cov_type_name", _all_covariance_types) + def test_mvn_with_covariance(self, size, matrix_type, cov_type_name): + message = (f"CovVia{cov_type_name} does not support {matrix_type} " + "matrices") + if cov_type_name not in self._cov_types[matrix_type]: + pytest.skip(message) + + A = self._matrices[matrix_type] + cov_type = getattr(_covariance, f"CovVia{cov_type_name}") + preprocessing = self._covariance_preprocessing[cov_type_name] + + mean = [0.1, 0.2, 0.3] + cov_object = cov_type(preprocessing(A)) + mvn = multivariate_normal + dist0 = multivariate_normal(mean, A, allow_singular=True) + dist1 = multivariate_normal(mean, cov_object, allow_singular=True) + + rng = np.random.default_rng(5292808890472453840) + x = rng.multivariate_normal(mean, A, size=size) + rng = np.random.default_rng(5292808890472453840) + x1 = mvn.rvs(mean, cov_object, size=size, random_state=rng) + rng = np.random.default_rng(5292808890472453840) + x2 = mvn(mean, cov_object, seed=rng).rvs(size=size) + if isinstance(cov_object, _covariance.CovViaPSD): + assert_close(x1, np.squeeze(x)) # for backward compatibility + assert_close(x2, np.squeeze(x)) + else: + assert_equal(x1.shape, x.shape) + assert_equal(x2.shape, x.shape) + assert_close(x2, x1) + + assert_close(mvn.pdf(x, mean, cov_object), dist0.pdf(x)) + assert_close(dist1.pdf(x), dist0.pdf(x)) + assert_close(mvn.logpdf(x, mean, cov_object), dist0.logpdf(x)) + assert_close(dist1.logpdf(x), dist0.logpdf(x)) + assert_close(mvn.entropy(mean, cov_object), dist0.entropy()) + assert_close(dist1.entropy(), dist0.entropy()) + + @pytest.mark.parametrize("size", [tuple(), (2, 4, 3)]) + @pytest.mark.parametrize("cov_type_name", _all_covariance_types) + def test_mvn_with_covariance_cdf(self, size, cov_type_name): + # This is split from the test above because it's slow to be running + # with all matrix types, and there's no need because _mvn.mvnun + # does the calculation. All Covariance needs to do is pass is + # provide the `covariance` attribute. + matrix_type = "diagonal full rank" + A = self._matrices[matrix_type] + cov_type = getattr(_covariance, f"CovVia{cov_type_name}") + preprocessing = self._covariance_preprocessing[cov_type_name] + + mean = [0.1, 0.2, 0.3] + cov_object = cov_type(preprocessing(A)) + mvn = multivariate_normal + dist0 = multivariate_normal(mean, A, allow_singular=True) + dist1 = multivariate_normal(mean, cov_object, allow_singular=True) + + rng = np.random.default_rng(5292808890472453840) + x = rng.multivariate_normal(mean, A, size=size) + + assert_close(mvn.cdf(x, mean, cov_object), dist0.cdf(x)) + assert_close(dist1.cdf(x), dist0.cdf(x)) + assert_close(mvn.logcdf(x, mean, cov_object), dist0.logcdf(x)) + assert_close(dist1.logcdf(x), dist0.logcdf(x)) + + def test_covariance_instantiation(self): + message = "The `Covariance` class cannot be instantiated directly." + with pytest.raises(NotImplementedError, match=message): + Covariance() + + @pytest.mark.filterwarnings("ignore::RuntimeWarning") # matrix not PSD + def test_gh9942(self): + # Originally there was a mistake in the `multivariate_normal_frozen` + # `rvs` method that caused all covariance objects to be processed as + # a `_CovViaPSD`. Ensure that this is resolved. + A = np.diag([1, 2, -1e-8]) + n = A.shape[0] + mean = np.zeros(n) + + # Error if the matrix is processed as a `_CovViaPSD` + with pytest.raises(ValueError, match="The input matrix must be..."): + multivariate_normal(mean, A).rvs() + + # No error if it is provided as a `CovViaEigendecomposition` + seed = 3562050283508273023 + rng1 = np.random.default_rng(seed) + rng2 = np.random.default_rng(seed) + cov = Covariance.from_eigendecomposition(np.linalg.eigh(A)) + rv = multivariate_normal(mean, cov) + res = rv.rvs(random_state=rng1) + ref = multivariate_normal.rvs(mean, cov, random_state=rng2) + assert_equal(res, ref) + + def test_gh19197(self): + # gh-19197 reported that multivariate normal `rvs` produced incorrect + # results when a singular Covariance object was produce using + # `from_eigenvalues`. Check that this specific issue is resolved; + # a more general test is included in `test_covariance`. + mean = np.ones(2) + cov = Covariance.from_eigendecomposition((np.zeros(2), np.eye(2))) + dist = scipy.stats.multivariate_normal(mean=mean, cov=cov) + rvs = dist.rvs(size=None) + assert_equal(rvs, mean) + + cov = scipy.stats.Covariance.from_eigendecomposition( + (np.array([1., 0.]), np.array([[1., 0.], [0., 400.]]))) + dist = scipy.stats.multivariate_normal(mean=mean, cov=cov) + rvs = dist.rvs(size=None) + assert rvs[0] != mean[0] + assert rvs[1] == mean[1] + + +def _random_covariance(dim, evals, rng, singular=False): + # Generates random covariance matrix with dimensionality `dim` and + # eigenvalues `evals` using provided Generator `rng`. Randomly sets + # some evals to zero if `singular` is True. + A = rng.random((dim, dim)) + A = A @ A.T + _, v = np.linalg.eigh(A) + if singular: + zero_eigs = rng.normal(size=dim) > 0 + evals[zero_eigs] = 0 + cov = v @ np.diag(evals) @ v.T + return cov + + +def _sample_orthonormal_matrix(n): + M = np.random.randn(n, n) + u, s, v = scipy.linalg.svd(M) + return u + + +class TestMultivariateNormal: + def test_input_shape(self): + mu = np.arange(3) + cov = np.identity(2) + assert_raises(ValueError, multivariate_normal.pdf, (0, 1), mu, cov) + assert_raises(ValueError, multivariate_normal.pdf, (0, 1, 2), mu, cov) + assert_raises(ValueError, multivariate_normal.cdf, (0, 1), mu, cov) + assert_raises(ValueError, multivariate_normal.cdf, (0, 1, 2), mu, cov) + + def test_scalar_values(self): + np.random.seed(1234) + + # When evaluated on scalar data, the pdf should return a scalar + x, mean, cov = 1.5, 1.7, 2.5 + pdf = multivariate_normal.pdf(x, mean, cov) + assert_equal(pdf.ndim, 0) + + # When evaluated on a single vector, the pdf should return a scalar + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.abs(np.random.randn(5)) # Diagonal values for cov. matrix + pdf = multivariate_normal.pdf(x, mean, cov) + assert_equal(pdf.ndim, 0) + + # When evaluated on scalar data, the cdf should return a scalar + x, mean, cov = 1.5, 1.7, 2.5 + cdf = multivariate_normal.cdf(x, mean, cov) + assert_equal(cdf.ndim, 0) + + # When evaluated on a single vector, the cdf should return a scalar + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.abs(np.random.randn(5)) # Diagonal values for cov. matrix + cdf = multivariate_normal.cdf(x, mean, cov) + assert_equal(cdf.ndim, 0) + + def test_logpdf(self): + # Check that the log of the pdf is in fact the logpdf + np.random.seed(1234) + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.abs(np.random.randn(5)) + d1 = multivariate_normal.logpdf(x, mean, cov) + d2 = multivariate_normal.pdf(x, mean, cov) + assert_allclose(d1, np.log(d2)) + + def test_logpdf_default_values(self): + # Check that the log of the pdf is in fact the logpdf + # with default parameters Mean=None and cov = 1 + np.random.seed(1234) + x = np.random.randn(5) + d1 = multivariate_normal.logpdf(x) + d2 = multivariate_normal.pdf(x) + # check whether default values are being used + d3 = multivariate_normal.logpdf(x, None, 1) + d4 = multivariate_normal.pdf(x, None, 1) + assert_allclose(d1, np.log(d2)) + assert_allclose(d3, np.log(d4)) + + def test_logcdf(self): + # Check that the log of the cdf is in fact the logcdf + np.random.seed(1234) + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.abs(np.random.randn(5)) + d1 = multivariate_normal.logcdf(x, mean, cov) + d2 = multivariate_normal.cdf(x, mean, cov) + assert_allclose(d1, np.log(d2)) + + def test_logcdf_default_values(self): + # Check that the log of the cdf is in fact the logcdf + # with default parameters Mean=None and cov = 1 + np.random.seed(1234) + x = np.random.randn(5) + d1 = multivariate_normal.logcdf(x) + d2 = multivariate_normal.cdf(x) + # check whether default values are being used + d3 = multivariate_normal.logcdf(x, None, 1) + d4 = multivariate_normal.cdf(x, None, 1) + assert_allclose(d1, np.log(d2)) + assert_allclose(d3, np.log(d4)) + + def test_rank(self): + # Check that the rank is detected correctly. + np.random.seed(1234) + n = 4 + mean = np.random.randn(n) + for expected_rank in range(1, n + 1): + s = np.random.randn(n, expected_rank) + cov = np.dot(s, s.T) + distn = multivariate_normal(mean, cov, allow_singular=True) + assert_equal(distn.cov_object.rank, expected_rank) + + def test_degenerate_distributions(self): + + for n in range(1, 5): + z = np.random.randn(n) + for k in range(1, n): + # Sample a small covariance matrix. + s = np.random.randn(k, k) + cov_kk = np.dot(s, s.T) + + # Embed the small covariance matrix into a larger singular one. + cov_nn = np.zeros((n, n)) + cov_nn[:k, :k] = cov_kk + + # Embed part of the vector in the same way + x = np.zeros(n) + x[:k] = z[:k] + + # Define a rotation of the larger low rank matrix. + u = _sample_orthonormal_matrix(n) + cov_rr = np.dot(u, np.dot(cov_nn, u.T)) + y = np.dot(u, x) + + # Check some identities. + distn_kk = multivariate_normal(np.zeros(k), cov_kk, + allow_singular=True) + distn_nn = multivariate_normal(np.zeros(n), cov_nn, + allow_singular=True) + distn_rr = multivariate_normal(np.zeros(n), cov_rr, + allow_singular=True) + assert_equal(distn_kk.cov_object.rank, k) + assert_equal(distn_nn.cov_object.rank, k) + assert_equal(distn_rr.cov_object.rank, k) + pdf_kk = distn_kk.pdf(x[:k]) + pdf_nn = distn_nn.pdf(x) + pdf_rr = distn_rr.pdf(y) + assert_allclose(pdf_kk, pdf_nn) + assert_allclose(pdf_kk, pdf_rr) + logpdf_kk = distn_kk.logpdf(x[:k]) + logpdf_nn = distn_nn.logpdf(x) + logpdf_rr = distn_rr.logpdf(y) + assert_allclose(logpdf_kk, logpdf_nn) + assert_allclose(logpdf_kk, logpdf_rr) + + # Add an orthogonal component and find the density + y_orth = y + u[:, -1] + pdf_rr_orth = distn_rr.pdf(y_orth) + logpdf_rr_orth = distn_rr.logpdf(y_orth) + + # Ensure that this has zero probability + assert_equal(pdf_rr_orth, 0.0) + assert_equal(logpdf_rr_orth, -np.inf) + + def test_degenerate_array(self): + # Test that we can generate arrays of random variate from a degenerate + # multivariate normal, and that the pdf for these samples is non-zero + # (i.e. samples from the distribution lie on the subspace) + k = 10 + for n in range(2, 6): + for r in range(1, n): + mn = np.zeros(n) + u = _sample_orthonormal_matrix(n)[:, :r] + vr = np.dot(u, u.T) + X = multivariate_normal.rvs(mean=mn, cov=vr, size=k) + + pdf = multivariate_normal.pdf(X, mean=mn, cov=vr, + allow_singular=True) + assert_equal(pdf.size, k) + assert np.all(pdf > 0.0) + + logpdf = multivariate_normal.logpdf(X, mean=mn, cov=vr, + allow_singular=True) + assert_equal(logpdf.size, k) + assert np.all(logpdf > -np.inf) + + def test_large_pseudo_determinant(self): + # Check that large pseudo-determinants are handled appropriately. + + # Construct a singular diagonal covariance matrix + # whose pseudo determinant overflows double precision. + large_total_log = 1000.0 + npos = 100 + nzero = 2 + large_entry = np.exp(large_total_log / npos) + n = npos + nzero + cov = np.zeros((n, n), dtype=float) + np.fill_diagonal(cov, large_entry) + cov[-nzero:, -nzero:] = 0 + + # Check some determinants. + assert_equal(scipy.linalg.det(cov), 0) + assert_equal(scipy.linalg.det(cov[:npos, :npos]), np.inf) + assert_allclose(np.linalg.slogdet(cov[:npos, :npos]), + (1, large_total_log)) + + # Check the pseudo-determinant. + psd = _PSD(cov) + assert_allclose(psd.log_pdet, large_total_log) + + def test_broadcasting(self): + np.random.seed(1234) + n = 4 + + # Construct a random covariance matrix. + data = np.random.randn(n, n) + cov = np.dot(data, data.T) + mean = np.random.randn(n) + + # Construct an ndarray which can be interpreted as + # a 2x3 array whose elements are random data vectors. + X = np.random.randn(2, 3, n) + + # Check that multiple data points can be evaluated at once. + desired_pdf = multivariate_normal.pdf(X, mean, cov) + desired_cdf = multivariate_normal.cdf(X, mean, cov) + for i in range(2): + for j in range(3): + actual = multivariate_normal.pdf(X[i, j], mean, cov) + assert_allclose(actual, desired_pdf[i,j]) + # Repeat for cdf + actual = multivariate_normal.cdf(X[i, j], mean, cov) + assert_allclose(actual, desired_cdf[i,j], rtol=1e-3) + + def test_normal_1D(self): + # The probability density function for a 1D normal variable should + # agree with the standard normal distribution in scipy.stats.distributions + x = np.linspace(0, 2, 10) + mean, cov = 1.2, 0.9 + scale = cov**0.5 + d1 = norm.pdf(x, mean, scale) + d2 = multivariate_normal.pdf(x, mean, cov) + assert_allclose(d1, d2) + # The same should hold for the cumulative distribution function + d1 = norm.cdf(x, mean, scale) + d2 = multivariate_normal.cdf(x, mean, cov) + assert_allclose(d1, d2) + + def test_marginalization(self): + # Integrating out one of the variables of a 2D Gaussian should + # yield a 1D Gaussian + mean = np.array([2.5, 3.5]) + cov = np.array([[.5, 0.2], [0.2, .6]]) + n = 2 ** 8 + 1 # Number of samples + delta = 6 / (n - 1) # Grid spacing + + v = np.linspace(0, 6, n) + xv, yv = np.meshgrid(v, v) + pos = np.empty((n, n, 2)) + pos[:, :, 0] = xv + pos[:, :, 1] = yv + pdf = multivariate_normal.pdf(pos, mean, cov) + + # Marginalize over x and y axis + margin_x = romb(pdf, delta, axis=0) + margin_y = romb(pdf, delta, axis=1) + + # Compare with standard normal distribution + gauss_x = norm.pdf(v, loc=mean[0], scale=cov[0, 0] ** 0.5) + gauss_y = norm.pdf(v, loc=mean[1], scale=cov[1, 1] ** 0.5) + assert_allclose(margin_x, gauss_x, rtol=1e-2, atol=1e-2) + assert_allclose(margin_y, gauss_y, rtol=1e-2, atol=1e-2) + + def test_frozen(self): + # The frozen distribution should agree with the regular one + np.random.seed(1234) + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.abs(np.random.randn(5)) + norm_frozen = multivariate_normal(mean, cov) + assert_allclose(norm_frozen.pdf(x), multivariate_normal.pdf(x, mean, cov)) + assert_allclose(norm_frozen.logpdf(x), + multivariate_normal.logpdf(x, mean, cov)) + assert_allclose(norm_frozen.cdf(x), multivariate_normal.cdf(x, mean, cov)) + assert_allclose(norm_frozen.logcdf(x), + multivariate_normal.logcdf(x, mean, cov)) + + @pytest.mark.parametrize( + 'covariance', + [ + np.eye(2), + Covariance.from_diagonal([1, 1]), + ] + ) + def test_frozen_multivariate_normal_exposes_attributes(self, covariance): + mean = np.ones((2,)) + cov_should_be = np.eye(2) + norm_frozen = multivariate_normal(mean, covariance) + assert np.allclose(norm_frozen.mean, mean) + assert np.allclose(norm_frozen.cov, cov_should_be) + + def test_pseudodet_pinv(self): + # Make sure that pseudo-inverse and pseudo-det agree on cutoff + + # Assemble random covariance matrix with large and small eigenvalues + np.random.seed(1234) + n = 7 + x = np.random.randn(n, n) + cov = np.dot(x, x.T) + s, u = scipy.linalg.eigh(cov) + s = np.full(n, 0.5) + s[0] = 1.0 + s[-1] = 1e-7 + cov = np.dot(u, np.dot(np.diag(s), u.T)) + + # Set cond so that the lowest eigenvalue is below the cutoff + cond = 1e-5 + psd = _PSD(cov, cond=cond) + psd_pinv = _PSD(psd.pinv, cond=cond) + + # Check that the log pseudo-determinant agrees with the sum + # of the logs of all but the smallest eigenvalue + assert_allclose(psd.log_pdet, np.sum(np.log(s[:-1]))) + # Check that the pseudo-determinant of the pseudo-inverse + # agrees with 1 / pseudo-determinant + assert_allclose(-psd.log_pdet, psd_pinv.log_pdet) + + def test_exception_nonsquare_cov(self): + cov = [[1, 2, 3], [4, 5, 6]] + assert_raises(ValueError, _PSD, cov) + + def test_exception_nonfinite_cov(self): + cov_nan = [[1, 0], [0, np.nan]] + assert_raises(ValueError, _PSD, cov_nan) + cov_inf = [[1, 0], [0, np.inf]] + assert_raises(ValueError, _PSD, cov_inf) + + def test_exception_non_psd_cov(self): + cov = [[1, 0], [0, -1]] + assert_raises(ValueError, _PSD, cov) + + def test_exception_singular_cov(self): + np.random.seed(1234) + x = np.random.randn(5) + mean = np.random.randn(5) + cov = np.ones((5, 5)) + e = np.linalg.LinAlgError + assert_raises(e, multivariate_normal, mean, cov) + assert_raises(e, multivariate_normal.pdf, x, mean, cov) + assert_raises(e, multivariate_normal.logpdf, x, mean, cov) + assert_raises(e, multivariate_normal.cdf, x, mean, cov) + assert_raises(e, multivariate_normal.logcdf, x, mean, cov) + + # Message used to be "singular matrix", but this is more accurate. + # See gh-15508 + cov = [[1., 0.], [1., 1.]] + msg = "When `allow_singular is False`, the input matrix" + with pytest.raises(np.linalg.LinAlgError, match=msg): + multivariate_normal(cov=cov) + + def test_R_values(self): + # Compare the multivariate pdf with some values precomputed + # in R version 3.0.1 (2013-05-16) on Mac OS X 10.6. + + # The values below were generated by the following R-script: + # > library(mnormt) + # > x <- seq(0, 2, length=5) + # > y <- 3*x - 2 + # > z <- x + cos(y) + # > mu <- c(1, 3, 2) + # > Sigma <- matrix(c(1,2,0,2,5,0.5,0,0.5,3), 3, 3) + # > r_pdf <- dmnorm(cbind(x,y,z), mu, Sigma) + r_pdf = np.array([0.0002214706, 0.0013819953, 0.0049138692, + 0.0103803050, 0.0140250800]) + + x = np.linspace(0, 2, 5) + y = 3 * x - 2 + z = x + np.cos(y) + r = np.array([x, y, z]).T + + mean = np.array([1, 3, 2], 'd') + cov = np.array([[1, 2, 0], [2, 5, .5], [0, .5, 3]], 'd') + + pdf = multivariate_normal.pdf(r, mean, cov) + assert_allclose(pdf, r_pdf, atol=1e-10) + + # Compare the multivariate cdf with some values precomputed + # in R version 3.3.2 (2016-10-31) on Debian GNU/Linux. + + # The values below were generated by the following R-script: + # > library(mnormt) + # > x <- seq(0, 2, length=5) + # > y <- 3*x - 2 + # > z <- x + cos(y) + # > mu <- c(1, 3, 2) + # > Sigma <- matrix(c(1,2,0,2,5,0.5,0,0.5,3), 3, 3) + # > r_cdf <- pmnorm(cbind(x,y,z), mu, Sigma) + r_cdf = np.array([0.0017866215, 0.0267142892, 0.0857098761, + 0.1063242573, 0.2501068509]) + + cdf = multivariate_normal.cdf(r, mean, cov) + assert_allclose(cdf, r_cdf, atol=2e-5) + + # Also test bivariate cdf with some values precomputed + # in R version 3.3.2 (2016-10-31) on Debian GNU/Linux. + + # The values below were generated by the following R-script: + # > library(mnormt) + # > x <- seq(0, 2, length=5) + # > y <- 3*x - 2 + # > mu <- c(1, 3) + # > Sigma <- matrix(c(1,2,2,5), 2, 2) + # > r_cdf2 <- pmnorm(cbind(x,y), mu, Sigma) + r_cdf2 = np.array([0.01262147, 0.05838989, 0.18389571, + 0.40696599, 0.66470577]) + + r2 = np.array([x, y]).T + + mean2 = np.array([1, 3], 'd') + cov2 = np.array([[1, 2], [2, 5]], 'd') + + cdf2 = multivariate_normal.cdf(r2, mean2, cov2) + assert_allclose(cdf2, r_cdf2, atol=1e-5) + + def test_multivariate_normal_rvs_zero_covariance(self): + mean = np.zeros(2) + covariance = np.zeros((2, 2)) + model = multivariate_normal(mean, covariance, allow_singular=True) + sample = model.rvs() + assert_equal(sample, [0, 0]) + + def test_rvs_shape(self): + # Check that rvs parses the mean and covariance correctly, and returns + # an array of the right shape + N = 300 + d = 4 + sample = multivariate_normal.rvs(mean=np.zeros(d), cov=1, size=N) + assert_equal(sample.shape, (N, d)) + + sample = multivariate_normal.rvs(mean=None, + cov=np.array([[2, .1], [.1, 1]]), + size=N) + assert_equal(sample.shape, (N, 2)) + + u = multivariate_normal(mean=0, cov=1) + sample = u.rvs(N) + assert_equal(sample.shape, (N, )) + + def test_large_sample(self): + # Generate large sample and compare sample mean and sample covariance + # with mean and covariance matrix. + + np.random.seed(2846) + + n = 3 + mean = np.random.randn(n) + M = np.random.randn(n, n) + cov = np.dot(M, M.T) + size = 5000 + + sample = multivariate_normal.rvs(mean, cov, size) + + assert_allclose(numpy.cov(sample.T), cov, rtol=1e-1) + assert_allclose(sample.mean(0), mean, rtol=1e-1) + + def test_entropy(self): + np.random.seed(2846) + + n = 3 + mean = np.random.randn(n) + M = np.random.randn(n, n) + cov = np.dot(M, M.T) + + rv = multivariate_normal(mean, cov) + + # Check that frozen distribution agrees with entropy function + assert_almost_equal(rv.entropy(), multivariate_normal.entropy(mean, cov)) + # Compare entropy with manually computed expression involving + # the sum of the logs of the eigenvalues of the covariance matrix + eigs = np.linalg.eig(cov)[0] + desired = 1 / 2 * (n * (np.log(2 * np.pi) + 1) + np.sum(np.log(eigs))) + assert_almost_equal(desired, rv.entropy()) + + def test_lnB(self): + alpha = np.array([1, 1, 1]) + desired = .5 # e^lnB = 1/2 for [1, 1, 1] + + assert_almost_equal(np.exp(_lnB(alpha)), desired) + + def test_cdf_with_lower_limit_arrays(self): + # test CDF with lower limit in several dimensions + rng = np.random.default_rng(2408071309372769818) + mean = [0, 0] + cov = np.eye(2) + a = rng.random((4, 3, 2))*6 - 3 + b = rng.random((4, 3, 2))*6 - 3 + + cdf1 = multivariate_normal.cdf(b, mean, cov, lower_limit=a) + + cdf2a = multivariate_normal.cdf(b, mean, cov) + cdf2b = multivariate_normal.cdf(a, mean, cov) + ab1 = np.concatenate((a[..., 0:1], b[..., 1:2]), axis=-1) + ab2 = np.concatenate((a[..., 1:2], b[..., 0:1]), axis=-1) + cdf2ab1 = multivariate_normal.cdf(ab1, mean, cov) + cdf2ab2 = multivariate_normal.cdf(ab2, mean, cov) + cdf2 = cdf2a + cdf2b - cdf2ab1 - cdf2ab2 + + assert_allclose(cdf1, cdf2) + + def test_cdf_with_lower_limit_consistency(self): + # check that multivariate normal CDF functions are consistent + rng = np.random.default_rng(2408071309372769818) + mean = rng.random(3) + cov = rng.random((3, 3)) + cov = cov @ cov.T + a = rng.random((2, 3))*6 - 3 + b = rng.random((2, 3))*6 - 3 + + cdf1 = multivariate_normal.cdf(b, mean, cov, lower_limit=a) + cdf2 = multivariate_normal(mean, cov).cdf(b, lower_limit=a) + cdf3 = np.exp(multivariate_normal.logcdf(b, mean, cov, lower_limit=a)) + cdf4 = np.exp(multivariate_normal(mean, cov).logcdf(b, lower_limit=a)) + + assert_allclose(cdf2, cdf1, rtol=1e-4) + assert_allclose(cdf3, cdf1, rtol=1e-4) + assert_allclose(cdf4, cdf1, rtol=1e-4) + + def test_cdf_signs(self): + # check that sign of output is correct when np.any(lower > x) + mean = np.zeros(3) + cov = np.eye(3) + b = [[1, 1, 1], [0, 0, 0], [1, 0, 1], [0, 1, 0]] + a = [[0, 0, 0], [1, 1, 1], [0, 1, 0], [1, 0, 1]] + # when odd number of elements of b < a, output is negative + expected_signs = np.array([1, -1, -1, 1]) + cdf = multivariate_normal.cdf(b, mean, cov, lower_limit=a) + assert_allclose(cdf, cdf[0]*expected_signs) + + def test_mean_cov(self): + # test the interaction between a Covariance object and mean + P = np.diag(1 / np.array([1, 2, 3])) + cov_object = _covariance.CovViaPrecision(P) + + message = "`cov` represents a covariance matrix in 3 dimensions..." + with pytest.raises(ValueError, match=message): + multivariate_normal.entropy([0, 0], cov_object) + + with pytest.raises(ValueError, match=message): + multivariate_normal([0, 0], cov_object) + + x = [0.5, 0.5, 0.5] + ref = multivariate_normal.pdf(x, [0, 0, 0], cov_object) + assert_equal(multivariate_normal.pdf(x, cov=cov_object), ref) + + ref = multivariate_normal.pdf(x, [1, 1, 1], cov_object) + assert_equal(multivariate_normal.pdf(x, 1, cov=cov_object), ref) + + def test_fit_wrong_fit_data_shape(self): + data = [1, 3] + error_msg = "`x` must be two-dimensional." + with pytest.raises(ValueError, match=error_msg): + multivariate_normal.fit(data) + + @pytest.mark.parametrize('dim', (3, 5)) + def test_fit_correctness(self, dim): + rng = np.random.default_rng(4385269356937404) + x = rng.random((100, dim)) + mean_est, cov_est = multivariate_normal.fit(x) + mean_ref, cov_ref = np.mean(x, axis=0), np.cov(x.T, ddof=0) + assert_allclose(mean_est, mean_ref, atol=1e-15) + assert_allclose(cov_est, cov_ref, rtol=1e-15) + + def test_fit_both_parameters_fixed(self): + data = np.full((2, 1), 3) + mean_fixed = 1. + cov_fixed = np.atleast_2d(1.) + mean, cov = multivariate_normal.fit(data, fix_mean=mean_fixed, + fix_cov=cov_fixed) + assert_equal(mean, mean_fixed) + assert_equal(cov, cov_fixed) + + @pytest.mark.parametrize('fix_mean', [np.zeros((2, 2)), + np.zeros((3, ))]) + def test_fit_fix_mean_input_validation(self, fix_mean): + msg = ("`fix_mean` must be a one-dimensional array the same " + "length as the dimensionality of the vectors `x`.") + with pytest.raises(ValueError, match=msg): + multivariate_normal.fit(np.eye(2), fix_mean=fix_mean) + + @pytest.mark.parametrize('fix_cov', [np.zeros((2, )), + np.zeros((3, 2)), + np.zeros((4, 4))]) + def test_fit_fix_cov_input_validation_dimension(self, fix_cov): + msg = ("`fix_cov` must be a two-dimensional square array " + "of same side length as the dimensionality of the " + "vectors `x`.") + with pytest.raises(ValueError, match=msg): + multivariate_normal.fit(np.eye(3), fix_cov=fix_cov) + + def test_fit_fix_cov_not_positive_semidefinite(self): + error_msg = "`fix_cov` must be symmetric positive semidefinite." + with pytest.raises(ValueError, match=error_msg): + fix_cov = np.array([[1., 0.], [0., -1.]]) + multivariate_normal.fit(np.eye(2), fix_cov=fix_cov) + + def test_fit_fix_mean(self): + rng = np.random.default_rng(4385269356937404) + loc = rng.random(3) + A = rng.random((3, 3)) + cov = np.dot(A, A.T) + samples = multivariate_normal.rvs(mean=loc, cov=cov, size=100, + random_state=rng) + mean_free, cov_free = multivariate_normal.fit(samples) + logp_free = multivariate_normal.logpdf(samples, mean=mean_free, + cov=cov_free).sum() + mean_fix, cov_fix = multivariate_normal.fit(samples, fix_mean=loc) + assert_equal(mean_fix, loc) + logp_fix = multivariate_normal.logpdf(samples, mean=mean_fix, + cov=cov_fix).sum() + # test that fixed parameters result in lower likelihood than free + # parameters + assert logp_fix < logp_free + # test that a small perturbation of the resulting parameters + # has lower likelihood than the estimated parameters + A = rng.random((3, 3)) + m = 1e-8 * np.dot(A, A.T) + cov_perturbed = cov_fix + m + logp_perturbed = (multivariate_normal.logpdf(samples, + mean=mean_fix, + cov=cov_perturbed) + ).sum() + assert logp_perturbed < logp_fix + + + def test_fit_fix_cov(self): + rng = np.random.default_rng(4385269356937404) + loc = rng.random(3) + A = rng.random((3, 3)) + cov = np.dot(A, A.T) + samples = multivariate_normal.rvs(mean=loc, cov=cov, + size=100, random_state=rng) + mean_free, cov_free = multivariate_normal.fit(samples) + logp_free = multivariate_normal.logpdf(samples, mean=mean_free, + cov=cov_free).sum() + mean_fix, cov_fix = multivariate_normal.fit(samples, fix_cov=cov) + assert_equal(mean_fix, np.mean(samples, axis=0)) + assert_equal(cov_fix, cov) + logp_fix = multivariate_normal.logpdf(samples, mean=mean_fix, + cov=cov_fix).sum() + # test that fixed parameters result in lower likelihood than free + # parameters + assert logp_fix < logp_free + # test that a small perturbation of the resulting parameters + # has lower likelihood than the estimated parameters + mean_perturbed = mean_fix + 1e-8 * rng.random(3) + logp_perturbed = (multivariate_normal.logpdf(samples, + mean=mean_perturbed, + cov=cov_fix) + ).sum() + assert logp_perturbed < logp_fix + + +class TestMatrixNormal: + + def test_bad_input(self): + # Check that bad inputs raise errors + num_rows = 4 + num_cols = 3 + M = np.full((num_rows,num_cols), 0.3) + U = 0.5 * np.identity(num_rows) + np.full((num_rows, num_rows), 0.5) + V = 0.7 * np.identity(num_cols) + np.full((num_cols, num_cols), 0.3) + + # Incorrect dimensions + assert_raises(ValueError, matrix_normal, np.zeros((5,4,3))) + assert_raises(ValueError, matrix_normal, M, np.zeros(10), V) + assert_raises(ValueError, matrix_normal, M, U, np.zeros(10)) + assert_raises(ValueError, matrix_normal, M, U, U) + assert_raises(ValueError, matrix_normal, M, V, V) + assert_raises(ValueError, matrix_normal, M.T, U, V) + + e = np.linalg.LinAlgError + # Singular covariance for the rvs method of a non-frozen instance + assert_raises(e, matrix_normal.rvs, + M, U, np.ones((num_cols, num_cols))) + assert_raises(e, matrix_normal.rvs, + M, np.ones((num_rows, num_rows)), V) + # Singular covariance for a frozen instance + assert_raises(e, matrix_normal, M, U, np.ones((num_cols, num_cols))) + assert_raises(e, matrix_normal, M, np.ones((num_rows, num_rows)), V) + + def test_default_inputs(self): + # Check that default argument handling works + num_rows = 4 + num_cols = 3 + M = np.full((num_rows,num_cols), 0.3) + U = 0.5 * np.identity(num_rows) + np.full((num_rows, num_rows), 0.5) + V = 0.7 * np.identity(num_cols) + np.full((num_cols, num_cols), 0.3) + Z = np.zeros((num_rows, num_cols)) + Zr = np.zeros((num_rows, 1)) + Zc = np.zeros((1, num_cols)) + Ir = np.identity(num_rows) + Ic = np.identity(num_cols) + I1 = np.identity(1) + + assert_equal(matrix_normal.rvs(mean=M, rowcov=U, colcov=V).shape, + (num_rows, num_cols)) + assert_equal(matrix_normal.rvs(mean=M).shape, + (num_rows, num_cols)) + assert_equal(matrix_normal.rvs(rowcov=U).shape, + (num_rows, 1)) + assert_equal(matrix_normal.rvs(colcov=V).shape, + (1, num_cols)) + assert_equal(matrix_normal.rvs(mean=M, colcov=V).shape, + (num_rows, num_cols)) + assert_equal(matrix_normal.rvs(mean=M, rowcov=U).shape, + (num_rows, num_cols)) + assert_equal(matrix_normal.rvs(rowcov=U, colcov=V).shape, + (num_rows, num_cols)) + + assert_equal(matrix_normal(mean=M).rowcov, Ir) + assert_equal(matrix_normal(mean=M).colcov, Ic) + assert_equal(matrix_normal(rowcov=U).mean, Zr) + assert_equal(matrix_normal(rowcov=U).colcov, I1) + assert_equal(matrix_normal(colcov=V).mean, Zc) + assert_equal(matrix_normal(colcov=V).rowcov, I1) + assert_equal(matrix_normal(mean=M, rowcov=U).colcov, Ic) + assert_equal(matrix_normal(mean=M, colcov=V).rowcov, Ir) + assert_equal(matrix_normal(rowcov=U, colcov=V).mean, Z) + + def test_covariance_expansion(self): + # Check that covariance can be specified with scalar or vector + num_rows = 4 + num_cols = 3 + M = np.full((num_rows, num_cols), 0.3) + Uv = np.full(num_rows, 0.2) + Us = 0.2 + Vv = np.full(num_cols, 0.1) + Vs = 0.1 + + Ir = np.identity(num_rows) + Ic = np.identity(num_cols) + + assert_equal(matrix_normal(mean=M, rowcov=Uv, colcov=Vv).rowcov, + 0.2*Ir) + assert_equal(matrix_normal(mean=M, rowcov=Uv, colcov=Vv).colcov, + 0.1*Ic) + assert_equal(matrix_normal(mean=M, rowcov=Us, colcov=Vs).rowcov, + 0.2*Ir) + assert_equal(matrix_normal(mean=M, rowcov=Us, colcov=Vs).colcov, + 0.1*Ic) + + def test_frozen_matrix_normal(self): + for i in range(1,5): + for j in range(1,5): + M = np.full((i,j), 0.3) + U = 0.5 * np.identity(i) + np.full((i,i), 0.5) + V = 0.7 * np.identity(j) + np.full((j,j), 0.3) + + frozen = matrix_normal(mean=M, rowcov=U, colcov=V) + + rvs1 = frozen.rvs(random_state=1234) + rvs2 = matrix_normal.rvs(mean=M, rowcov=U, colcov=V, + random_state=1234) + assert_equal(rvs1, rvs2) + + X = frozen.rvs(random_state=1234) + + pdf1 = frozen.pdf(X) + pdf2 = matrix_normal.pdf(X, mean=M, rowcov=U, colcov=V) + assert_equal(pdf1, pdf2) + + logpdf1 = frozen.logpdf(X) + logpdf2 = matrix_normal.logpdf(X, mean=M, rowcov=U, colcov=V) + assert_equal(logpdf1, logpdf2) + + def test_matches_multivariate(self): + # Check that the pdfs match those obtained by vectorising and + # treating as a multivariate normal. + for i in range(1,5): + for j in range(1,5): + M = np.full((i,j), 0.3) + U = 0.5 * np.identity(i) + np.full((i,i), 0.5) + V = 0.7 * np.identity(j) + np.full((j,j), 0.3) + + frozen = matrix_normal(mean=M, rowcov=U, colcov=V) + X = frozen.rvs(random_state=1234) + pdf1 = frozen.pdf(X) + logpdf1 = frozen.logpdf(X) + entropy1 = frozen.entropy() + + vecX = X.T.flatten() + vecM = M.T.flatten() + cov = np.kron(V,U) + pdf2 = multivariate_normal.pdf(vecX, mean=vecM, cov=cov) + logpdf2 = multivariate_normal.logpdf(vecX, mean=vecM, cov=cov) + entropy2 = multivariate_normal.entropy(mean=vecM, cov=cov) + + assert_allclose(pdf1, pdf2, rtol=1E-10) + assert_allclose(logpdf1, logpdf2, rtol=1E-10) + assert_allclose(entropy1, entropy2) + + def test_array_input(self): + # Check array of inputs has the same output as the separate entries. + num_rows = 4 + num_cols = 3 + M = np.full((num_rows,num_cols), 0.3) + U = 0.5 * np.identity(num_rows) + np.full((num_rows, num_rows), 0.5) + V = 0.7 * np.identity(num_cols) + np.full((num_cols, num_cols), 0.3) + N = 10 + + frozen = matrix_normal(mean=M, rowcov=U, colcov=V) + X1 = frozen.rvs(size=N, random_state=1234) + X2 = frozen.rvs(size=N, random_state=4321) + X = np.concatenate((X1[np.newaxis,:,:,:],X2[np.newaxis,:,:,:]), axis=0) + assert_equal(X.shape, (2, N, num_rows, num_cols)) + + array_logpdf = frozen.logpdf(X) + assert_equal(array_logpdf.shape, (2, N)) + for i in range(2): + for j in range(N): + separate_logpdf = matrix_normal.logpdf(X[i,j], mean=M, + rowcov=U, colcov=V) + assert_allclose(separate_logpdf, array_logpdf[i,j], 1E-10) + + def test_moments(self): + # Check that the sample moments match the parameters + num_rows = 4 + num_cols = 3 + M = np.full((num_rows,num_cols), 0.3) + U = 0.5 * np.identity(num_rows) + np.full((num_rows, num_rows), 0.5) + V = 0.7 * np.identity(num_cols) + np.full((num_cols, num_cols), 0.3) + N = 1000 + + frozen = matrix_normal(mean=M, rowcov=U, colcov=V) + X = frozen.rvs(size=N, random_state=1234) + + sample_mean = np.mean(X,axis=0) + assert_allclose(sample_mean, M, atol=0.1) + + sample_colcov = np.cov(X.reshape(N*num_rows,num_cols).T) + assert_allclose(sample_colcov, V, atol=0.1) + + sample_rowcov = np.cov(np.swapaxes(X,1,2).reshape( + N*num_cols,num_rows).T) + assert_allclose(sample_rowcov, U, atol=0.1) + + def test_samples(self): + # Regression test to ensure that we always generate the same stream of + # random variates. + actual = matrix_normal.rvs( + mean=np.array([[1, 2], [3, 4]]), + rowcov=np.array([[4, -1], [-1, 2]]), + colcov=np.array([[5, 1], [1, 10]]), + random_state=np.random.default_rng(0), + size=2 + ) + expected = np.array( + [[[1.56228264238181, -1.24136424071189], + [2.46865788392114, 6.22964440489445]], + [[3.86405716144353, 10.73714311429529], + [2.59428444080606, 5.79987854490876]]] + ) + assert_allclose(actual, expected) + + +class TestDirichlet: + + def test_frozen_dirichlet(self): + np.random.seed(2846) + + n = np.random.randint(1, 32) + alpha = np.random.uniform(10e-10, 100, n) + + d = dirichlet(alpha) + + assert_equal(d.var(), dirichlet.var(alpha)) + assert_equal(d.mean(), dirichlet.mean(alpha)) + assert_equal(d.entropy(), dirichlet.entropy(alpha)) + num_tests = 10 + for i in range(num_tests): + x = np.random.uniform(10e-10, 100, n) + x /= np.sum(x) + assert_equal(d.pdf(x[:-1]), dirichlet.pdf(x[:-1], alpha)) + assert_equal(d.logpdf(x[:-1]), dirichlet.logpdf(x[:-1], alpha)) + + def test_numpy_rvs_shape_compatibility(self): + np.random.seed(2846) + alpha = np.array([1.0, 2.0, 3.0]) + x = np.random.dirichlet(alpha, size=7) + assert_equal(x.shape, (7, 3)) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + dirichlet.pdf(x.T, alpha) + dirichlet.pdf(x.T[:-1], alpha) + dirichlet.logpdf(x.T, alpha) + dirichlet.logpdf(x.T[:-1], alpha) + + def test_alpha_with_zeros(self): + np.random.seed(2846) + alpha = [1.0, 0.0, 3.0] + # don't pass invalid alpha to np.random.dirichlet + x = np.random.dirichlet(np.maximum(1e-9, alpha), size=7).T + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_alpha_with_negative_entries(self): + np.random.seed(2846) + alpha = [1.0, -2.0, 3.0] + # don't pass invalid alpha to np.random.dirichlet + x = np.random.dirichlet(np.maximum(1e-9, alpha), size=7).T + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_with_zeros(self): + alpha = np.array([1.0, 2.0, 3.0, 4.0]) + x = np.array([0.1, 0.0, 0.2, 0.7]) + dirichlet.pdf(x, alpha) + dirichlet.logpdf(x, alpha) + alpha = np.array([1.0, 1.0, 1.0, 1.0]) + assert_almost_equal(dirichlet.pdf(x, alpha), 6) + assert_almost_equal(dirichlet.logpdf(x, alpha), np.log(6)) + + def test_data_with_zeros_and_small_alpha(self): + alpha = np.array([1.0, 0.5, 3.0, 4.0]) + x = np.array([0.1, 0.0, 0.2, 0.7]) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_with_negative_entries(self): + alpha = np.array([1.0, 2.0, 3.0, 4.0]) + x = np.array([0.1, -0.1, 0.3, 0.7]) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_with_too_large_entries(self): + alpha = np.array([1.0, 2.0, 3.0, 4.0]) + x = np.array([0.1, 1.1, 0.3, 0.7]) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_too_deep_c(self): + alpha = np.array([1.0, 2.0, 3.0]) + x = np.full((2, 7, 7), 1 / 14) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_alpha_too_deep(self): + alpha = np.array([[1.0, 2.0], [3.0, 4.0]]) + x = np.full((2, 2, 7), 1 / 4) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_alpha_correct_depth(self): + alpha = np.array([1.0, 2.0, 3.0]) + x = np.full((3, 7), 1 / 3) + dirichlet.pdf(x, alpha) + dirichlet.logpdf(x, alpha) + + def test_non_simplex_data(self): + alpha = np.array([1.0, 2.0, 3.0]) + x = np.full((3, 7), 1 / 2) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_vector_too_short(self): + alpha = np.array([1.0, 2.0, 3.0, 4.0]) + x = np.full((2, 7), 1 / 2) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_data_vector_too_long(self): + alpha = np.array([1.0, 2.0, 3.0, 4.0]) + x = np.full((5, 7), 1 / 5) + assert_raises(ValueError, dirichlet.pdf, x, alpha) + assert_raises(ValueError, dirichlet.logpdf, x, alpha) + + def test_mean_var_cov(self): + # Reference values calculated by hand and confirmed with Mathematica, e.g. + # `Covariance[DirichletDistribution[{ 1, 0.8, 0.2, 10^-300}]]` + alpha = np.array([1., 0.8, 0.2]) + d = dirichlet(alpha) + + expected_mean = [0.5, 0.4, 0.1] + expected_var = [1. / 12., 0.08, 0.03] + expected_cov = [ + [ 1. / 12, -1. / 15, -1. / 60], + [-1. / 15, 2. / 25, -1. / 75], + [-1. / 60, -1. / 75, 3. / 100], + ] + + assert_array_almost_equal(d.mean(), expected_mean) + assert_array_almost_equal(d.var(), expected_var) + assert_array_almost_equal(d.cov(), expected_cov) + + def test_scalar_values(self): + alpha = np.array([0.2]) + d = dirichlet(alpha) + + # For alpha of length 1, mean and var should be scalar instead of array + assert_equal(d.mean().ndim, 0) + assert_equal(d.var().ndim, 0) + + assert_equal(d.pdf([1.]).ndim, 0) + assert_equal(d.logpdf([1.]).ndim, 0) + + def test_K_and_K_minus_1_calls_equal(self): + # Test that calls with K and K-1 entries yield the same results. + + np.random.seed(2846) + + n = np.random.randint(1, 32) + alpha = np.random.uniform(10e-10, 100, n) + + d = dirichlet(alpha) + num_tests = 10 + for i in range(num_tests): + x = np.random.uniform(10e-10, 100, n) + x /= np.sum(x) + assert_almost_equal(d.pdf(x[:-1]), d.pdf(x)) + + def test_multiple_entry_calls(self): + # Test that calls with multiple x vectors as matrix work + np.random.seed(2846) + + n = np.random.randint(1, 32) + alpha = np.random.uniform(10e-10, 100, n) + d = dirichlet(alpha) + + num_tests = 10 + num_multiple = 5 + xm = None + for i in range(num_tests): + for m in range(num_multiple): + x = np.random.uniform(10e-10, 100, n) + x /= np.sum(x) + if xm is not None: + xm = np.vstack((xm, x)) + else: + xm = x + rm = d.pdf(xm.T) + rs = None + for xs in xm: + r = d.pdf(xs) + if rs is not None: + rs = np.append(rs, r) + else: + rs = r + assert_array_almost_equal(rm, rs) + + def test_2D_dirichlet_is_beta(self): + np.random.seed(2846) + + alpha = np.random.uniform(10e-10, 100, 2) + d = dirichlet(alpha) + b = beta(alpha[0], alpha[1]) + + num_tests = 10 + for i in range(num_tests): + x = np.random.uniform(10e-10, 100, 2) + x /= np.sum(x) + assert_almost_equal(b.pdf(x), d.pdf([x])) + + assert_almost_equal(b.mean(), d.mean()[0]) + assert_almost_equal(b.var(), d.var()[0]) + + +def test_multivariate_normal_dimensions_mismatch(): + # Regression test for GH #3493. Check that setting up a PDF with a mean of + # length M and a covariance matrix of size (N, N), where M != N, raises a + # ValueError with an informative error message. + mu = np.array([0.0, 0.0]) + sigma = np.array([[1.0]]) + + assert_raises(ValueError, multivariate_normal, mu, sigma) + + # A simple check that the right error message was passed along. Checking + # that the entire message is there, word for word, would be somewhat + # fragile, so we just check for the leading part. + try: + multivariate_normal(mu, sigma) + except ValueError as e: + msg = "Dimension mismatch" + assert_equal(str(e)[:len(msg)], msg) + + +class TestWishart: + def test_scale_dimensions(self): + # Test that we can call the Wishart with various scale dimensions + + # Test case: dim=1, scale=1 + true_scale = np.array(1, ndmin=2) + scales = [ + 1, # scalar + [1], # iterable + np.array(1), # 0-dim + np.r_[1], # 1-dim + np.array(1, ndmin=2) # 2-dim + ] + for scale in scales: + w = wishart(1, scale) + assert_equal(w.scale, true_scale) + assert_equal(w.scale.shape, true_scale.shape) + + # Test case: dim=2, scale=[[1,0] + # [0,2] + true_scale = np.array([[1,0], + [0,2]]) + scales = [ + [1,2], # iterable + np.r_[1,2], # 1-dim + np.array([[1,0], # 2-dim + [0,2]]) + ] + for scale in scales: + w = wishart(2, scale) + assert_equal(w.scale, true_scale) + assert_equal(w.scale.shape, true_scale.shape) + + # We cannot call with a df < dim - 1 + assert_raises(ValueError, wishart, 1, np.eye(2)) + + # But we can call with dim - 1 < df < dim + wishart(1.1, np.eye(2)) # no error + # see gh-5562 + + # We cannot call with a 3-dimension array + scale = np.array(1, ndmin=3) + assert_raises(ValueError, wishart, 1, scale) + + def test_quantile_dimensions(self): + # Test that we can call the Wishart rvs with various quantile dimensions + + # If dim == 1, consider x.shape = [1,1,1] + X = [ + 1, # scalar + [1], # iterable + np.array(1), # 0-dim + np.r_[1], # 1-dim + np.array(1, ndmin=2), # 2-dim + np.array([1], ndmin=3) # 3-dim + ] + + w = wishart(1,1) + density = w.pdf(np.array(1, ndmin=3)) + for x in X: + assert_equal(w.pdf(x), density) + + # If dim == 1, consider x.shape = [1,1,*] + X = [ + [1,2,3], # iterable + np.r_[1,2,3], # 1-dim + np.array([1,2,3], ndmin=3) # 3-dim + ] + + w = wishart(1,1) + density = w.pdf(np.array([1,2,3], ndmin=3)) + for x in X: + assert_equal(w.pdf(x), density) + + # If dim == 2, consider x.shape = [2,2,1] + # where x[:,:,*] = np.eye(1)*2 + X = [ + 2, # scalar + [2,2], # iterable + np.array(2), # 0-dim + np.r_[2,2], # 1-dim + np.array([[2,0], + [0,2]]), # 2-dim + np.array([[2,0], + [0,2]])[:,:,np.newaxis] # 3-dim + ] + + w = wishart(2,np.eye(2)) + density = w.pdf(np.array([[2,0], + [0,2]])[:,:,np.newaxis]) + for x in X: + assert_equal(w.pdf(x), density) + + def test_frozen(self): + # Test that the frozen and non-frozen Wishart gives the same answers + + # Construct an arbitrary positive definite scale matrix + dim = 4 + scale = np.diag(np.arange(dim)+1) + scale[np.tril_indices(dim, k=-1)] = np.arange(dim * (dim-1) // 2) + scale = np.dot(scale.T, scale) + + # Construct a collection of positive definite matrices to test the PDF + X = [] + for i in range(5): + x = np.diag(np.arange(dim)+(i+1)**2) + x[np.tril_indices(dim, k=-1)] = np.arange(dim * (dim-1) // 2) + x = np.dot(x.T, x) + X.append(x) + X = np.array(X).T + + # Construct a 1D and 2D set of parameters + parameters = [ + (10, 1, np.linspace(0.1, 10, 5)), # 1D case + (10, scale, X) + ] + + for (df, scale, x) in parameters: + w = wishart(df, scale) + assert_equal(w.var(), wishart.var(df, scale)) + assert_equal(w.mean(), wishart.mean(df, scale)) + assert_equal(w.mode(), wishart.mode(df, scale)) + assert_equal(w.entropy(), wishart.entropy(df, scale)) + assert_equal(w.pdf(x), wishart.pdf(x, df, scale)) + + def test_wishart_2D_rvs(self): + dim = 3 + df = 10 + + # Construct a simple non-diagonal positive definite matrix + scale = np.eye(dim) + scale[0,1] = 0.5 + scale[1,0] = 0.5 + + # Construct frozen Wishart random variables + w = wishart(df, scale) + + # Get the generated random variables from a known seed + np.random.seed(248042) + w_rvs = wishart.rvs(df, scale) + np.random.seed(248042) + frozen_w_rvs = w.rvs() + + # Manually calculate what it should be, based on the Bartlett (1933) + # decomposition of a Wishart into D A A' D', where D is the Cholesky + # factorization of the scale matrix and A is the lower triangular matrix + # with the square root of chi^2 variates on the diagonal and N(0,1) + # variates in the lower triangle. + np.random.seed(248042) + covariances = np.random.normal(size=3) + variances = np.r_[ + np.random.chisquare(df), + np.random.chisquare(df-1), + np.random.chisquare(df-2), + ]**0.5 + + # Construct the lower-triangular A matrix + A = np.diag(variances) + A[np.tril_indices(dim, k=-1)] = covariances + + # Wishart random variate + D = np.linalg.cholesky(scale) + DA = D.dot(A) + manual_w_rvs = np.dot(DA, DA.T) + + # Test for equality + assert_allclose(w_rvs, manual_w_rvs) + assert_allclose(frozen_w_rvs, manual_w_rvs) + + def test_1D_is_chisquared(self): + # The 1-dimensional Wishart with an identity scale matrix is just a + # chi-squared distribution. + # Test variance, mean, entropy, pdf + # Kolgomorov-Smirnov test for rvs + np.random.seed(482974) + + sn = 500 + dim = 1 + scale = np.eye(dim) + + df_range = np.arange(1, 10, 2, dtype=float) + X = np.linspace(0.1,10,num=10) + for df in df_range: + w = wishart(df, scale) + c = chi2(df) + + # Statistics + assert_allclose(w.var(), c.var()) + assert_allclose(w.mean(), c.mean()) + assert_allclose(w.entropy(), c.entropy()) + + # PDF + assert_allclose(w.pdf(X), c.pdf(X)) + + # rvs + rvs = w.rvs(size=sn) + args = (df,) + alpha = 0.01 + check_distribution_rvs('chi2', args, alpha, rvs) + + def test_is_scaled_chisquared(self): + # The 2-dimensional Wishart with an arbitrary scale matrix can be + # transformed to a scaled chi-squared distribution. + # For :math:`S \sim W_p(V,n)` and :math:`\lambda \in \mathbb{R}^p` we have + # :math:`\lambda' S \lambda \sim \lambda' V \lambda \times \chi^2(n)` + np.random.seed(482974) + + sn = 500 + df = 10 + dim = 4 + # Construct an arbitrary positive definite matrix + scale = np.diag(np.arange(4)+1) + scale[np.tril_indices(4, k=-1)] = np.arange(6) + scale = np.dot(scale.T, scale) + # Use :math:`\lambda = [1, \dots, 1]'` + lamda = np.ones((dim,1)) + sigma_lamda = lamda.T.dot(scale).dot(lamda).squeeze() + w = wishart(df, sigma_lamda) + c = chi2(df, scale=sigma_lamda) + + # Statistics + assert_allclose(w.var(), c.var()) + assert_allclose(w.mean(), c.mean()) + assert_allclose(w.entropy(), c.entropy()) + + # PDF + X = np.linspace(0.1,10,num=10) + assert_allclose(w.pdf(X), c.pdf(X)) + + # rvs + rvs = w.rvs(size=sn) + args = (df,0,sigma_lamda) + alpha = 0.01 + check_distribution_rvs('chi2', args, alpha, rvs) + +class TestMultinomial: + def test_logpmf(self): + vals1 = multinomial.logpmf((3,4), 7, (0.3, 0.7)) + assert_allclose(vals1, -1.483270127243324, rtol=1e-8) + + vals2 = multinomial.logpmf([3, 4], 0, [.3, .7]) + assert vals2 == -np.inf + + vals3 = multinomial.logpmf([0, 0], 0, [.3, .7]) + assert vals3 == 0 + + vals4 = multinomial.logpmf([3, 4], 0, [-2, 3]) + assert_allclose(vals4, np.nan, rtol=1e-8) + + def test_reduces_binomial(self): + # test that the multinomial pmf reduces to the binomial pmf in the 2d + # case + val1 = multinomial.logpmf((3, 4), 7, (0.3, 0.7)) + val2 = binom.logpmf(3, 7, 0.3) + assert_allclose(val1, val2, rtol=1e-8) + + val1 = multinomial.pmf((6, 8), 14, (0.1, 0.9)) + val2 = binom.pmf(6, 14, 0.1) + assert_allclose(val1, val2, rtol=1e-8) + + def test_R(self): + # test against the values produced by this R code + # (https://stat.ethz.ch/R-manual/R-devel/library/stats/html/Multinom.html) + # X <- t(as.matrix(expand.grid(0:3, 0:3))); X <- X[, colSums(X) <= 3] + # X <- rbind(X, 3:3 - colSums(X)); dimnames(X) <- list(letters[1:3], NULL) + # X + # apply(X, 2, function(x) dmultinom(x, prob = c(1,2,5))) + + n, p = 3, [1./8, 2./8, 5./8] + r_vals = {(0, 0, 3): 0.244140625, (1, 0, 2): 0.146484375, + (2, 0, 1): 0.029296875, (3, 0, 0): 0.001953125, + (0, 1, 2): 0.292968750, (1, 1, 1): 0.117187500, + (2, 1, 0): 0.011718750, (0, 2, 1): 0.117187500, + (1, 2, 0): 0.023437500, (0, 3, 0): 0.015625000} + for x in r_vals: + assert_allclose(multinomial.pmf(x, n, p), r_vals[x], atol=1e-14) + + @pytest.mark.parametrize("n", [0, 3]) + def test_rvs_np(self, n): + # test that .rvs agrees w/numpy + sc_rvs = multinomial.rvs(n, [1/4.]*3, size=7, random_state=123) + rndm = np.random.RandomState(123) + np_rvs = rndm.multinomial(n, [1/4.]*3, size=7) + assert_equal(sc_rvs, np_rvs) + + def test_pmf(self): + vals0 = multinomial.pmf((5,), 5, (1,)) + assert_allclose(vals0, 1, rtol=1e-8) + + vals1 = multinomial.pmf((3,4), 7, (.3, .7)) + assert_allclose(vals1, .22689449999999994, rtol=1e-8) + + vals2 = multinomial.pmf([[[3,5],[0,8]], [[-1, 9], [1, 1]]], 8, + (.1, .9)) + assert_allclose(vals2, [[.03306744, .43046721], [0, 0]], rtol=1e-8) + + x = np.empty((0,2), dtype=np.float64) + vals3 = multinomial.pmf(x, 4, (.3, .7)) + assert_equal(vals3, np.empty([], dtype=np.float64)) + + vals4 = multinomial.pmf([1,2], 4, (.3, .7)) + assert_allclose(vals4, 0, rtol=1e-8) + + vals5 = multinomial.pmf([3, 3, 0], 6, [2/3.0, 1/3.0, 0]) + assert_allclose(vals5, 0.219478737997, rtol=1e-8) + + vals5 = multinomial.pmf([0, 0, 0], 0, [2/3.0, 1/3.0, 0]) + assert vals5 == 1 + + vals6 = multinomial.pmf([2, 1, 0], 0, [2/3.0, 1/3.0, 0]) + assert vals6 == 0 + + def test_pmf_broadcasting(self): + vals0 = multinomial.pmf([1, 2], 3, [[.1, .9], [.2, .8]]) + assert_allclose(vals0, [.243, .384], rtol=1e-8) + + vals1 = multinomial.pmf([1, 2], [3, 4], [.1, .9]) + assert_allclose(vals1, [.243, 0], rtol=1e-8) + + vals2 = multinomial.pmf([[[1, 2], [1, 1]]], 3, [.1, .9]) + assert_allclose(vals2, [[.243, 0]], rtol=1e-8) + + vals3 = multinomial.pmf([1, 2], [[[3], [4]]], [.1, .9]) + assert_allclose(vals3, [[[.243], [0]]], rtol=1e-8) + + vals4 = multinomial.pmf([[1, 2], [1,1]], [[[[3]]]], [.1, .9]) + assert_allclose(vals4, [[[[.243, 0]]]], rtol=1e-8) + + @pytest.mark.parametrize("n", [0, 5]) + def test_cov(self, n): + cov1 = multinomial.cov(n, (.2, .3, .5)) + cov2 = [[n*.2*.8, -n*.2*.3, -n*.2*.5], + [-n*.3*.2, n*.3*.7, -n*.3*.5], + [-n*.5*.2, -n*.5*.3, n*.5*.5]] + assert_allclose(cov1, cov2, rtol=1e-8) + + def test_cov_broadcasting(self): + cov1 = multinomial.cov(5, [[.1, .9], [.2, .8]]) + cov2 = [[[.45, -.45],[-.45, .45]], [[.8, -.8], [-.8, .8]]] + assert_allclose(cov1, cov2, rtol=1e-8) + + cov3 = multinomial.cov([4, 5], [.1, .9]) + cov4 = [[[.36, -.36], [-.36, .36]], [[.45, -.45], [-.45, .45]]] + assert_allclose(cov3, cov4, rtol=1e-8) + + cov5 = multinomial.cov([4, 5], [[.3, .7], [.4, .6]]) + cov6 = [[[4*.3*.7, -4*.3*.7], [-4*.3*.7, 4*.3*.7]], + [[5*.4*.6, -5*.4*.6], [-5*.4*.6, 5*.4*.6]]] + assert_allclose(cov5, cov6, rtol=1e-8) + + @pytest.mark.parametrize("n", [0, 2]) + def test_entropy(self, n): + # this is equivalent to a binomial distribution with n=2, so the + # entropy .77899774929 is easily computed "by hand" + ent0 = multinomial.entropy(n, [.2, .8]) + assert_allclose(ent0, binom.entropy(n, .2), rtol=1e-8) + + def test_entropy_broadcasting(self): + ent0 = multinomial.entropy([2, 3], [.2, .3]) + assert_allclose(ent0, [binom.entropy(2, .2), binom.entropy(3, .2)], + rtol=1e-8) + + ent1 = multinomial.entropy([7, 8], [[.3, .7], [.4, .6]]) + assert_allclose(ent1, [binom.entropy(7, .3), binom.entropy(8, .4)], + rtol=1e-8) + + ent2 = multinomial.entropy([[7], [8]], [[.3, .7], [.4, .6]]) + assert_allclose(ent2, + [[binom.entropy(7, .3), binom.entropy(7, .4)], + [binom.entropy(8, .3), binom.entropy(8, .4)]], + rtol=1e-8) + + @pytest.mark.parametrize("n", [0, 5]) + def test_mean(self, n): + mean1 = multinomial.mean(n, [.2, .8]) + assert_allclose(mean1, [n*.2, n*.8], rtol=1e-8) + + def test_mean_broadcasting(self): + mean1 = multinomial.mean([5, 6], [.2, .8]) + assert_allclose(mean1, [[5*.2, 5*.8], [6*.2, 6*.8]], rtol=1e-8) + + def test_frozen(self): + # The frozen distribution should agree with the regular one + np.random.seed(1234) + n = 12 + pvals = (.1, .2, .3, .4) + x = [[0,0,0,12],[0,0,1,11],[0,1,1,10],[1,1,1,9],[1,1,2,8]] + x = np.asarray(x, dtype=np.float64) + mn_frozen = multinomial(n, pvals) + assert_allclose(mn_frozen.pmf(x), multinomial.pmf(x, n, pvals)) + assert_allclose(mn_frozen.logpmf(x), multinomial.logpmf(x, n, pvals)) + assert_allclose(mn_frozen.entropy(), multinomial.entropy(n, pvals)) + + def test_gh_11860(self): + # gh-11860 reported cases in which the adjustments made by multinomial + # to the last element of `p` can cause `nan`s even when the input is + # essentially valid. Check that a pathological case returns a finite, + # nonzero result. (This would fail in main before the PR.) + n = 88 + rng = np.random.default_rng(8879715917488330089) + p = rng.random(n) + p[-1] = 1e-30 + p /= np.sum(p) + x = np.ones(n) + logpmf = multinomial.logpmf(x, n, p) + assert np.isfinite(logpmf) + +class TestInvwishart: + def test_frozen(self): + # Test that the frozen and non-frozen inverse Wishart gives the same + # answers + + # Construct an arbitrary positive definite scale matrix + dim = 4 + scale = np.diag(np.arange(dim)+1) + scale[np.tril_indices(dim, k=-1)] = np.arange(dim*(dim-1)/2) + scale = np.dot(scale.T, scale) + + # Construct a collection of positive definite matrices to test the PDF + X = [] + for i in range(5): + x = np.diag(np.arange(dim)+(i+1)**2) + x[np.tril_indices(dim, k=-1)] = np.arange(dim*(dim-1)/2) + x = np.dot(x.T, x) + X.append(x) + X = np.array(X).T + + # Construct a 1D and 2D set of parameters + parameters = [ + (10, 1, np.linspace(0.1, 10, 5)), # 1D case + (10, scale, X) + ] + + for (df, scale, x) in parameters: + iw = invwishart(df, scale) + assert_equal(iw.var(), invwishart.var(df, scale)) + assert_equal(iw.mean(), invwishart.mean(df, scale)) + assert_equal(iw.mode(), invwishart.mode(df, scale)) + assert_allclose(iw.pdf(x), invwishart.pdf(x, df, scale)) + + def test_1D_is_invgamma(self): + # The 1-dimensional inverse Wishart with an identity scale matrix is + # just an inverse gamma distribution. + # Test variance, mean, pdf, entropy + # Kolgomorov-Smirnov test for rvs + np.random.seed(482974) + + sn = 500 + dim = 1 + scale = np.eye(dim) + + df_range = np.arange(5, 20, 2, dtype=float) + X = np.linspace(0.1,10,num=10) + for df in df_range: + iw = invwishart(df, scale) + ig = invgamma(df/2, scale=1./2) + + # Statistics + assert_allclose(iw.var(), ig.var()) + assert_allclose(iw.mean(), ig.mean()) + + # PDF + assert_allclose(iw.pdf(X), ig.pdf(X)) + + # rvs + rvs = iw.rvs(size=sn) + args = (df/2, 0, 1./2) + alpha = 0.01 + check_distribution_rvs('invgamma', args, alpha, rvs) + + # entropy + assert_allclose(iw.entropy(), ig.entropy()) + + def test_invwishart_2D_rvs(self): + dim = 3 + df = 10 + + # Construct a simple non-diagonal positive definite matrix + scale = np.eye(dim) + scale[0,1] = 0.5 + scale[1,0] = 0.5 + + # Construct frozen inverse-Wishart random variables + iw = invwishart(df, scale) + + # Get the generated random variables from a known seed + np.random.seed(608072) + iw_rvs = invwishart.rvs(df, scale) + np.random.seed(608072) + frozen_iw_rvs = iw.rvs() + + # Manually calculate what it should be, based on the decomposition in + # https://arxiv.org/abs/2310.15884 of an invers-Wishart into L L', + # where L A = D, D is the Cholesky factorization of the scale matrix, + # and A is the lower triangular matrix with the square root of chi^2 + # variates on the diagonal and N(0,1) variates in the lower triangle. + # the diagonal chi^2 variates in this A are reversed compared to those + # in the Bartlett decomposition A for Wishart rvs. + np.random.seed(608072) + covariances = np.random.normal(size=3) + variances = np.r_[ + np.random.chisquare(df-2), + np.random.chisquare(df-1), + np.random.chisquare(df), + ]**0.5 + + # Construct the lower-triangular A matrix + A = np.diag(variances) + A[np.tril_indices(dim, k=-1)] = covariances + + # inverse-Wishart random variate + D = np.linalg.cholesky(scale) + L = np.linalg.solve(A.T, D.T).T + manual_iw_rvs = np.dot(L, L.T) + + # Test for equality + assert_allclose(iw_rvs, manual_iw_rvs) + assert_allclose(frozen_iw_rvs, manual_iw_rvs) + + def test_sample_mean(self): + """Test that sample mean consistent with known mean.""" + # Construct an arbitrary positive definite scale matrix + df = 10 + sample_size = 20_000 + for dim in [1, 5]: + scale = np.diag(np.arange(dim) + 1) + scale[np.tril_indices(dim, k=-1)] = np.arange(dim * (dim - 1) / 2) + scale = np.dot(scale.T, scale) + + dist = invwishart(df, scale) + Xmean_exp = dist.mean() + Xvar_exp = dist.var() + Xmean_std = (Xvar_exp / sample_size)**0.5 # asymptotic SE of mean estimate + + X = dist.rvs(size=sample_size, random_state=1234) + Xmean_est = X.mean(axis=0) + + ntests = dim*(dim + 1)//2 + fail_rate = 0.01 / ntests # correct for multiple tests + max_diff = norm.ppf(1 - fail_rate / 2) + assert np.allclose( + (Xmean_est - Xmean_exp) / Xmean_std, + 0, + atol=max_diff, + ) + + def test_logpdf_4x4(self): + """Regression test for gh-8844.""" + X = np.array([[2, 1, 0, 0.5], + [1, 2, 0.5, 0.5], + [0, 0.5, 3, 1], + [0.5, 0.5, 1, 2]]) + Psi = np.array([[9, 7, 3, 1], + [7, 9, 5, 1], + [3, 5, 8, 2], + [1, 1, 2, 9]]) + nu = 6 + prob = invwishart.logpdf(X, nu, Psi) + # Explicit calculation from the formula on wikipedia. + p = X.shape[0] + sig, logdetX = np.linalg.slogdet(X) + sig, logdetPsi = np.linalg.slogdet(Psi) + M = np.linalg.solve(X, Psi) + expected = ((nu/2)*logdetPsi + - (nu*p/2)*np.log(2) + - multigammaln(nu/2, p) + - (nu + p + 1)/2*logdetX + - 0.5*M.trace()) + assert_allclose(prob, expected) + + +class TestSpecialOrthoGroup: + def test_reproducibility(self): + np.random.seed(514) + x = special_ortho_group.rvs(3) + expected = np.array([[-0.99394515, -0.04527879, 0.10011432], + [0.04821555, -0.99846897, 0.02711042], + [0.09873351, 0.03177334, 0.99460653]]) + assert_array_almost_equal(x, expected) + + random_state = np.random.RandomState(seed=514) + x = special_ortho_group.rvs(3, random_state=random_state) + assert_array_almost_equal(x, expected) + + def test_invalid_dim(self): + assert_raises(ValueError, special_ortho_group.rvs, None) + assert_raises(ValueError, special_ortho_group.rvs, (2, 2)) + assert_raises(ValueError, special_ortho_group.rvs, 1) + assert_raises(ValueError, special_ortho_group.rvs, 2.5) + + def test_frozen_matrix(self): + dim = 7 + frozen = special_ortho_group(dim) + + rvs1 = frozen.rvs(random_state=1234) + rvs2 = special_ortho_group.rvs(dim, random_state=1234) + + assert_equal(rvs1, rvs2) + + def test_det_and_ortho(self): + xs = [special_ortho_group.rvs(dim) + for dim in range(2,12) + for i in range(3)] + + # Test that determinants are always +1 + dets = [np.linalg.det(x) for x in xs] + assert_allclose(dets, [1.]*30, rtol=1e-13) + + # Test that these are orthogonal matrices + for x in xs: + assert_array_almost_equal(np.dot(x, x.T), + np.eye(x.shape[0])) + + def test_haar(self): + # Test that the distribution is constant under rotation + # Every column should have the same distribution + # Additionally, the distribution should be invariant under another rotation + + # Generate samples + dim = 5 + samples = 1000 # Not too many, or the test takes too long + ks_prob = .05 + np.random.seed(514) + xs = special_ortho_group.rvs(dim, size=samples) + + # Dot a few rows (0, 1, 2) with unit vectors (0, 2, 4, 3), + # effectively picking off entries in the matrices of xs. + # These projections should all have the same distribution, + # establishing rotational invariance. We use the two-sided + # KS test to confirm this. + # We could instead test that angles between random vectors + # are uniformly distributed, but the below is sufficient. + # It is not feasible to consider all pairs, so pick a few. + els = ((0,0), (0,2), (1,4), (2,3)) + #proj = {(er, ec): [x[er][ec] for x in xs] for er, ec in els} + proj = {(er, ec): sorted([x[er][ec] for x in xs]) for er, ec in els} + pairs = [(e0, e1) for e0 in els for e1 in els if e0 > e1] + ks_tests = [ks_2samp(proj[p0], proj[p1])[1] for (p0, p1) in pairs] + assert_array_less([ks_prob]*len(pairs), ks_tests) + + +class TestOrthoGroup: + def test_reproducibility(self): + seed = 514 + np.random.seed(seed) + x = ortho_group.rvs(3) + x2 = ortho_group.rvs(3, random_state=seed) + # Note this matrix has det -1, distinguishing O(N) from SO(N) + assert_almost_equal(np.linalg.det(x), -1) + expected = np.array([[0.381686, -0.090374, 0.919863], + [0.905794, -0.161537, -0.391718], + [-0.183993, -0.98272, -0.020204]]) + assert_array_almost_equal(x, expected) + assert_array_almost_equal(x2, expected) + + def test_invalid_dim(self): + assert_raises(ValueError, ortho_group.rvs, None) + assert_raises(ValueError, ortho_group.rvs, (2, 2)) + assert_raises(ValueError, ortho_group.rvs, 1) + assert_raises(ValueError, ortho_group.rvs, 2.5) + + def test_frozen_matrix(self): + dim = 7 + frozen = ortho_group(dim) + frozen_seed = ortho_group(dim, seed=1234) + + rvs1 = frozen.rvs(random_state=1234) + rvs2 = ortho_group.rvs(dim, random_state=1234) + rvs3 = frozen_seed.rvs(size=1) + + assert_equal(rvs1, rvs2) + assert_equal(rvs1, rvs3) + + def test_det_and_ortho(self): + xs = [[ortho_group.rvs(dim) + for i in range(10)] + for dim in range(2,12)] + + # Test that abs determinants are always +1 + dets = np.array([[np.linalg.det(x) for x in xx] for xx in xs]) + assert_allclose(np.fabs(dets), np.ones(dets.shape), rtol=1e-13) + + # Test that these are orthogonal matrices + for xx in xs: + for x in xx: + assert_array_almost_equal(np.dot(x, x.T), + np.eye(x.shape[0])) + + @pytest.mark.parametrize("dim", [2, 5, 10, 20]) + def test_det_distribution_gh18272(self, dim): + # Test that positive and negative determinants are equally likely. + rng = np.random.default_rng(6796248956179332344) + dist = ortho_group(dim=dim) + rvs = dist.rvs(size=5000, random_state=rng) + dets = scipy.linalg.det(rvs) + k = np.sum(dets > 0) + n = len(dets) + res = stats.binomtest(k, n) + low, high = res.proportion_ci(confidence_level=0.95) + assert low < 0.5 < high + + def test_haar(self): + # Test that the distribution is constant under rotation + # Every column should have the same distribution + # Additionally, the distribution should be invariant under another rotation + + # Generate samples + dim = 5 + samples = 1000 # Not too many, or the test takes too long + ks_prob = .05 + np.random.seed(518) # Note that the test is sensitive to seed too + xs = ortho_group.rvs(dim, size=samples) + + # Dot a few rows (0, 1, 2) with unit vectors (0, 2, 4, 3), + # effectively picking off entries in the matrices of xs. + # These projections should all have the same distribution, + # establishing rotational invariance. We use the two-sided + # KS test to confirm this. + # We could instead test that angles between random vectors + # are uniformly distributed, but the below is sufficient. + # It is not feasible to consider all pairs, so pick a few. + els = ((0,0), (0,2), (1,4), (2,3)) + #proj = {(er, ec): [x[er][ec] for x in xs] for er, ec in els} + proj = {(er, ec): sorted([x[er][ec] for x in xs]) for er, ec in els} + pairs = [(e0, e1) for e0 in els for e1 in els if e0 > e1] + ks_tests = [ks_2samp(proj[p0], proj[p1])[1] for (p0, p1) in pairs] + assert_array_less([ks_prob]*len(pairs), ks_tests) + + @pytest.mark.slow + def test_pairwise_distances(self): + # Test that the distribution of pairwise distances is close to correct. + np.random.seed(514) + + def random_ortho(dim): + u, _s, v = np.linalg.svd(np.random.normal(size=(dim, dim))) + return np.dot(u, v) + + for dim in range(2, 6): + def generate_test_statistics(rvs, N=1000, eps=1e-10): + stats = np.array([ + np.sum((rvs(dim=dim) - rvs(dim=dim))**2) + for _ in range(N) + ]) + # Add a bit of noise to account for numeric accuracy. + stats += np.random.uniform(-eps, eps, size=stats.shape) + return stats + + expected = generate_test_statistics(random_ortho) + actual = generate_test_statistics(scipy.stats.ortho_group.rvs) + + _D, p = scipy.stats.ks_2samp(expected, actual) + + assert_array_less(.05, p) + + +class TestRandomCorrelation: + def test_reproducibility(self): + np.random.seed(514) + eigs = (.5, .8, 1.2, 1.5) + x = random_correlation.rvs(eigs) + x2 = random_correlation.rvs(eigs, random_state=514) + expected = np.array([[1., -0.184851, 0.109017, -0.227494], + [-0.184851, 1., 0.231236, 0.326669], + [0.109017, 0.231236, 1., -0.178912], + [-0.227494, 0.326669, -0.178912, 1.]]) + assert_array_almost_equal(x, expected) + assert_array_almost_equal(x2, expected) + + def test_invalid_eigs(self): + assert_raises(ValueError, random_correlation.rvs, None) + assert_raises(ValueError, random_correlation.rvs, 'test') + assert_raises(ValueError, random_correlation.rvs, 2.5) + assert_raises(ValueError, random_correlation.rvs, [2.5]) + assert_raises(ValueError, random_correlation.rvs, [[1,2],[3,4]]) + assert_raises(ValueError, random_correlation.rvs, [2.5, -.5]) + assert_raises(ValueError, random_correlation.rvs, [1, 2, .1]) + + def test_frozen_matrix(self): + eigs = (.5, .8, 1.2, 1.5) + frozen = random_correlation(eigs) + frozen_seed = random_correlation(eigs, seed=514) + + rvs1 = random_correlation.rvs(eigs, random_state=514) + rvs2 = frozen.rvs(random_state=514) + rvs3 = frozen_seed.rvs() + + assert_equal(rvs1, rvs2) + assert_equal(rvs1, rvs3) + + def test_definition(self): + # Test the definition of a correlation matrix in several dimensions: + # + # 1. Det is product of eigenvalues (and positive by construction + # in examples) + # 2. 1's on diagonal + # 3. Matrix is symmetric + + def norm(i, e): + return i*e/sum(e) + + np.random.seed(123) + + eigs = [norm(i, np.random.uniform(size=i)) for i in range(2, 6)] + eigs.append([4,0,0,0]) + + ones = [[1.]*len(e) for e in eigs] + xs = [random_correlation.rvs(e) for e in eigs] + + # Test that determinants are products of eigenvalues + # These are positive by construction + # Could also test that the eigenvalues themselves are correct, + # but this seems sufficient. + dets = [np.fabs(np.linalg.det(x)) for x in xs] + dets_known = [np.prod(e) for e in eigs] + assert_allclose(dets, dets_known, rtol=1e-13, atol=1e-13) + + # Test for 1's on the diagonal + diags = [np.diag(x) for x in xs] + for a, b in zip(diags, ones): + assert_allclose(a, b, rtol=1e-13) + + # Correlation matrices are symmetric + for x in xs: + assert_allclose(x, x.T, rtol=1e-13) + + def test_to_corr(self): + # Check some corner cases in to_corr + + # ajj == 1 + m = np.array([[0.1, 0], [0, 1]], dtype=float) + m = random_correlation._to_corr(m) + assert_allclose(m, np.array([[1, 0], [0, 0.1]])) + + # Floating point overflow; fails to compute the correct + # rotation, but should still produce some valid rotation + # rather than infs/nans + with np.errstate(over='ignore'): + g = np.array([[0, 1], [-1, 0]]) + + m0 = np.array([[1e300, 0], [0, np.nextafter(1, 0)]], dtype=float) + m = random_correlation._to_corr(m0.copy()) + assert_allclose(m, g.T.dot(m0).dot(g)) + + m0 = np.array([[0.9, 1e300], [1e300, 1.1]], dtype=float) + m = random_correlation._to_corr(m0.copy()) + assert_allclose(m, g.T.dot(m0).dot(g)) + + # Zero discriminant; should set the first diag entry to 1 + m0 = np.array([[2, 1], [1, 2]], dtype=float) + m = random_correlation._to_corr(m0.copy()) + assert_allclose(m[0,0], 1) + + # Slightly negative discriminant; should be approx correct still + m0 = np.array([[2 + 1e-7, 1], [1, 2]], dtype=float) + m = random_correlation._to_corr(m0.copy()) + assert_allclose(m[0,0], 1) + + +class TestUniformDirection: + @pytest.mark.parametrize("dim", [1, 3]) + @pytest.mark.parametrize("size", [None, 1, 5, (5, 4)]) + def test_samples(self, dim, size): + # test that samples have correct shape and norm 1 + rng = np.random.default_rng(2777937887058094419) + uniform_direction_dist = uniform_direction(dim, seed=rng) + samples = uniform_direction_dist.rvs(size) + mean, cov = np.zeros(dim), np.eye(dim) + expected_shape = rng.multivariate_normal(mean, cov, size=size).shape + assert samples.shape == expected_shape + norms = np.linalg.norm(samples, axis=-1) + assert_allclose(norms, 1.) + + @pytest.mark.parametrize("dim", [None, 0, (2, 2), 2.5]) + def test_invalid_dim(self, dim): + message = ("Dimension of vector must be specified, " + "and must be an integer greater than 0.") + with pytest.raises(ValueError, match=message): + uniform_direction.rvs(dim) + + def test_frozen_distribution(self): + dim = 5 + frozen = uniform_direction(dim) + frozen_seed = uniform_direction(dim, seed=514) + + rvs1 = frozen.rvs(random_state=514) + rvs2 = uniform_direction.rvs(dim, random_state=514) + rvs3 = frozen_seed.rvs() + + assert_equal(rvs1, rvs2) + assert_equal(rvs1, rvs3) + + @pytest.mark.parametrize("dim", [2, 5, 8]) + def test_uniform(self, dim): + rng = np.random.default_rng(1036978481269651776) + spherical_dist = uniform_direction(dim, seed=rng) + # generate random, orthogonal vectors + v1, v2 = spherical_dist.rvs(size=2) + v2 -= v1 @ v2 * v1 + v2 /= np.linalg.norm(v2) + assert_allclose(v1 @ v2, 0, atol=1e-14) # orthogonal + # generate data and project onto orthogonal vectors + samples = spherical_dist.rvs(size=10000) + s1 = samples @ v1 + s2 = samples @ v2 + angles = np.arctan2(s1, s2) + # test that angles follow a uniform distribution + # normalize angles to range [0, 1] + angles += np.pi + angles /= 2*np.pi + # perform KS test + uniform_dist = uniform() + kstest_result = kstest(angles, uniform_dist.cdf) + assert kstest_result.pvalue > 0.05 + + +class TestUnitaryGroup: + def test_reproducibility(self): + np.random.seed(514) + x = unitary_group.rvs(3) + x2 = unitary_group.rvs(3, random_state=514) + + expected = np.array( + [[0.308771+0.360312j, 0.044021+0.622082j, 0.160327+0.600173j], + [0.732757+0.297107j, 0.076692-0.4614j, -0.394349+0.022613j], + [-0.148844+0.357037j, -0.284602-0.557949j, 0.607051+0.299257j]] + ) + + assert_array_almost_equal(x, expected) + assert_array_almost_equal(x2, expected) + + def test_invalid_dim(self): + assert_raises(ValueError, unitary_group.rvs, None) + assert_raises(ValueError, unitary_group.rvs, (2, 2)) + assert_raises(ValueError, unitary_group.rvs, 1) + assert_raises(ValueError, unitary_group.rvs, 2.5) + + def test_frozen_matrix(self): + dim = 7 + frozen = unitary_group(dim) + frozen_seed = unitary_group(dim, seed=514) + + rvs1 = frozen.rvs(random_state=514) + rvs2 = unitary_group.rvs(dim, random_state=514) + rvs3 = frozen_seed.rvs(size=1) + + assert_equal(rvs1, rvs2) + assert_equal(rvs1, rvs3) + + def test_unitarity(self): + xs = [unitary_group.rvs(dim) + for dim in range(2,12) + for i in range(3)] + + # Test that these are unitary matrices + for x in xs: + assert_allclose(np.dot(x, x.conj().T), np.eye(x.shape[0]), atol=1e-15) + + def test_haar(self): + # Test that the eigenvalues, which lie on the unit circle in + # the complex plane, are uncorrelated. + + # Generate samples + dim = 5 + samples = 1000 # Not too many, or the test takes too long + np.random.seed(514) # Note that the test is sensitive to seed too + xs = unitary_group.rvs(dim, size=samples) + + # The angles "x" of the eigenvalues should be uniformly distributed + # Overall this seems to be a necessary but weak test of the distribution. + eigs = np.vstack([scipy.linalg.eigvals(x) for x in xs]) + x = np.arctan2(eigs.imag, eigs.real) + res = kstest(x.ravel(), uniform(-np.pi, 2*np.pi).cdf) + assert_(res.pvalue > 0.05) + + +class TestMultivariateT: + + # These tests were created by running vpa(mvtpdf(...)) in MATLAB. The + # function takes no `mu` parameter. The tests were run as + # + # >> ans = vpa(mvtpdf(x - mu, shape, df)); + # + PDF_TESTS = [( + # x + [ + [1, 2], + [4, 1], + [2, 1], + [2, 4], + [1, 4], + [4, 1], + [3, 2], + [3, 3], + [4, 4], + [5, 1], + ], + # loc + [0, 0], + # shape + [ + [1, 0], + [0, 1] + ], + # df + 4, + # ans + [ + 0.013972450422333741737457302178882, + 0.0010998721906793330026219646100571, + 0.013972450422333741737457302178882, + 0.00073682844024025606101402363634634, + 0.0010998721906793330026219646100571, + 0.0010998721906793330026219646100571, + 0.0020732579600816823488240725481546, + 0.00095660371505271429414668515889275, + 0.00021831953784896498569831346792114, + 0.00037725616140301147447000396084604 + ] + + ), ( + # x + [ + [0.9718, 0.1298, 0.8134], + [0.4922, 0.5522, 0.7185], + [0.3010, 0.1491, 0.5008], + [0.5971, 0.2585, 0.8940], + [0.5434, 0.5287, 0.9507], + ], + # loc + [-1, 1, 50], + # shape + [ + [1.0000, 0.5000, 0.2500], + [0.5000, 1.0000, -0.1000], + [0.2500, -0.1000, 1.0000], + ], + # df + 8, + # ans + [ + 0.00000000000000069609279697467772867405511133763, + 0.00000000000000073700739052207366474839369535934, + 0.00000000000000069522909962669171512174435447027, + 0.00000000000000074212293557998314091880208889767, + 0.00000000000000077039675154022118593323030449058, + ] + )] + + @pytest.mark.parametrize("x, loc, shape, df, ans", PDF_TESTS) + def test_pdf_correctness(self, x, loc, shape, df, ans): + dist = multivariate_t(loc, shape, df, seed=0) + val = dist.pdf(x) + assert_array_almost_equal(val, ans) + + @pytest.mark.parametrize("x, loc, shape, df, ans", PDF_TESTS) + def test_logpdf_correct(self, x, loc, shape, df, ans): + dist = multivariate_t(loc, shape, df, seed=0) + val1 = dist.pdf(x) + val2 = dist.logpdf(x) + assert_array_almost_equal(np.log(val1), val2) + + # https://github.com/scipy/scipy/issues/10042#issuecomment-576795195 + def test_mvt_with_df_one_is_cauchy(self): + x = [9, 7, 4, 1, -3, 9, 0, -3, -1, 3] + val = multivariate_t.pdf(x, df=1) + ans = cauchy.pdf(x) + assert_array_almost_equal(val, ans) + + def test_mvt_with_high_df_is_approx_normal(self): + # `normaltest` returns the chi-squared statistic and the associated + # p-value. The null hypothesis is that `x` came from a normal + # distribution, so a low p-value represents rejecting the null, i.e. + # that it is unlikely that `x` came a normal distribution. + P_VAL_MIN = 0.1 + + dist = multivariate_t(0, 1, df=100000, seed=1) + samples = dist.rvs(size=100000) + _, p = normaltest(samples) + assert (p > P_VAL_MIN) + + dist = multivariate_t([-2, 3], [[10, -1], [-1, 10]], df=100000, + seed=42) + samples = dist.rvs(size=100000) + _, p = normaltest(samples) + assert ((p > P_VAL_MIN).all()) + + @patch('scipy.stats.multivariate_normal._logpdf') + def test_mvt_with_inf_df_calls_normal(self, mock): + dist = multivariate_t(0, 1, df=np.inf, seed=7) + assert isinstance(dist, multivariate_normal_frozen) + multivariate_t.pdf(0, df=np.inf) + assert mock.call_count == 1 + multivariate_t.logpdf(0, df=np.inf) + assert mock.call_count == 2 + + def test_shape_correctness(self): + # pdf and logpdf should return scalar when the + # number of samples in x is one. + dim = 4 + loc = np.zeros(dim) + shape = np.eye(dim) + df = 4.5 + x = np.zeros(dim) + res = multivariate_t(loc, shape, df).pdf(x) + assert np.isscalar(res) + res = multivariate_t(loc, shape, df).logpdf(x) + assert np.isscalar(res) + + # pdf() and logpdf() should return probabilities of shape + # (n_samples,) when x has n_samples. + n_samples = 7 + x = np.random.random((n_samples, dim)) + res = multivariate_t(loc, shape, df).pdf(x) + assert (res.shape == (n_samples,)) + res = multivariate_t(loc, shape, df).logpdf(x) + assert (res.shape == (n_samples,)) + + # rvs() should return scalar unless a size argument is applied. + res = multivariate_t(np.zeros(1), np.eye(1), 1).rvs() + assert np.isscalar(res) + + # rvs() should return vector of shape (size,) if size argument + # is applied. + size = 7 + res = multivariate_t(np.zeros(1), np.eye(1), 1).rvs(size=size) + assert (res.shape == (size,)) + + def test_default_arguments(self): + dist = multivariate_t() + assert_equal(dist.loc, [0]) + assert_equal(dist.shape, [[1]]) + assert (dist.df == 1) + + DEFAULT_ARGS_TESTS = [ + (None, None, None, 0, 1, 1), + (None, None, 7, 0, 1, 7), + (None, [[7, 0], [0, 7]], None, [0, 0], [[7, 0], [0, 7]], 1), + (None, [[7, 0], [0, 7]], 7, [0, 0], [[7, 0], [0, 7]], 7), + ([7, 7], None, None, [7, 7], [[1, 0], [0, 1]], 1), + ([7, 7], None, 7, [7, 7], [[1, 0], [0, 1]], 7), + ([7, 7], [[7, 0], [0, 7]], None, [7, 7], [[7, 0], [0, 7]], 1), + ([7, 7], [[7, 0], [0, 7]], 7, [7, 7], [[7, 0], [0, 7]], 7) + ] + + @pytest.mark.parametrize("loc, shape, df, loc_ans, shape_ans, df_ans", + DEFAULT_ARGS_TESTS) + def test_default_args(self, loc, shape, df, loc_ans, shape_ans, df_ans): + dist = multivariate_t(loc=loc, shape=shape, df=df) + assert_equal(dist.loc, loc_ans) + assert_equal(dist.shape, shape_ans) + assert (dist.df == df_ans) + + ARGS_SHAPES_TESTS = [ + (-1, 2, 3, [-1], [[2]], 3), + ([-1], [2], 3, [-1], [[2]], 3), + (np.array([-1]), np.array([2]), 3, [-1], [[2]], 3) + ] + + @pytest.mark.parametrize("loc, shape, df, loc_ans, shape_ans, df_ans", + ARGS_SHAPES_TESTS) + def test_scalar_list_and_ndarray_arguments(self, loc, shape, df, loc_ans, + shape_ans, df_ans): + dist = multivariate_t(loc, shape, df) + assert_equal(dist.loc, loc_ans) + assert_equal(dist.shape, shape_ans) + assert_equal(dist.df, df_ans) + + def test_argument_error_handling(self): + # `loc` should be a one-dimensional vector. + loc = [[1, 1]] + assert_raises(ValueError, + multivariate_t, + **dict(loc=loc)) + + # `shape` should be scalar or square matrix. + shape = [[1, 1], [2, 2], [3, 3]] + assert_raises(ValueError, + multivariate_t, + **dict(loc=loc, shape=shape)) + + # `df` should be greater than zero. + loc = np.zeros(2) + shape = np.eye(2) + df = -1 + assert_raises(ValueError, + multivariate_t, + **dict(loc=loc, shape=shape, df=df)) + df = 0 + assert_raises(ValueError, + multivariate_t, + **dict(loc=loc, shape=shape, df=df)) + + def test_reproducibility(self): + rng = np.random.RandomState(4) + loc = rng.uniform(size=3) + shape = np.eye(3) + dist1 = multivariate_t(loc, shape, df=3, seed=2) + dist2 = multivariate_t(loc, shape, df=3, seed=2) + samples1 = dist1.rvs(size=10) + samples2 = dist2.rvs(size=10) + assert_equal(samples1, samples2) + + def test_allow_singular(self): + # Make shape singular and verify error was raised. + args = dict(loc=[0,0], shape=[[0,0],[0,1]], df=1, allow_singular=False) + assert_raises(np.linalg.LinAlgError, multivariate_t, **args) + + @pytest.mark.parametrize("size", [(10, 3), (5, 6, 4, 3)]) + @pytest.mark.parametrize("dim", [2, 3, 4, 5]) + @pytest.mark.parametrize("df", [1., 2., np.inf]) + def test_rvs(self, size, dim, df): + dist = multivariate_t(np.zeros(dim), np.eye(dim), df) + rvs = dist.rvs(size=size) + assert rvs.shape == size + (dim, ) + + def test_cdf_signs(self): + # check that sign of output is correct when np.any(lower > x) + mean = np.zeros(3) + cov = np.eye(3) + df = 10 + b = [[1, 1, 1], [0, 0, 0], [1, 0, 1], [0, 1, 0]] + a = [[0, 0, 0], [1, 1, 1], [0, 1, 0], [1, 0, 1]] + # when odd number of elements of b < a, output is negative + expected_signs = np.array([1, -1, -1, 1]) + cdf = multivariate_normal.cdf(b, mean, cov, df, lower_limit=a) + assert_allclose(cdf, cdf[0]*expected_signs) + + @pytest.mark.parametrize('dim', [1, 2, 5, 10]) + def test_cdf_against_multivariate_normal(self, dim): + # Check accuracy against MVN randomly-generated cases + self.cdf_against_mvn_test(dim) + + @pytest.mark.parametrize('dim', [3, 6, 9]) + def test_cdf_against_multivariate_normal_singular(self, dim): + # Check accuracy against MVN for randomly-generated singular cases + self.cdf_against_mvn_test(3, True) + + def cdf_against_mvn_test(self, dim, singular=False): + # Check for accuracy in the limit that df -> oo and MVT -> MVN + rng = np.random.default_rng(413722918996573) + n = 3 + + w = 10**rng.uniform(-2, 1, size=dim) + cov = _random_covariance(dim, w, rng, singular) + + mean = 10**rng.uniform(-1, 2, size=dim) * np.sign(rng.normal(size=dim)) + a = -10**rng.uniform(-1, 2, size=(n, dim)) + mean + b = 10**rng.uniform(-1, 2, size=(n, dim)) + mean + + res = stats.multivariate_t.cdf(b, mean, cov, df=10000, lower_limit=a, + allow_singular=True, random_state=rng) + ref = stats.multivariate_normal.cdf(b, mean, cov, allow_singular=True, + lower_limit=a) + assert_allclose(res, ref, atol=5e-4) + + def test_cdf_against_univariate_t(self): + rng = np.random.default_rng(413722918996573) + cov = 2 + mean = 0 + x = rng.normal(size=10, scale=np.sqrt(cov)) + df = 3 + + res = stats.multivariate_t.cdf(x, mean, cov, df, lower_limit=-np.inf, + random_state=rng) + ref = stats.t.cdf(x, df, mean, np.sqrt(cov)) + incorrect = stats.norm.cdf(x, mean, np.sqrt(cov)) + + assert_allclose(res, ref, atol=5e-4) # close to t + assert np.all(np.abs(res - incorrect) > 1e-3) # not close to normal + + @pytest.mark.parametrize("dim", [2, 3, 5, 10]) + @pytest.mark.parametrize("seed", [3363958638, 7891119608, 3887698049, + 5013150848, 1495033423, 6170824608]) + @pytest.mark.parametrize("singular", [False, True]) + def test_cdf_against_qsimvtv(self, dim, seed, singular): + if singular and seed != 3363958638: + pytest.skip('Agreement with qsimvtv is not great in singular case') + rng = np.random.default_rng(seed) + w = 10**rng.uniform(-2, 2, size=dim) + cov = _random_covariance(dim, w, rng, singular) + mean = rng.random(dim) + a = -rng.random(dim) + b = rng.random(dim) + df = rng.random() * 5 + + # no lower limit + res = stats.multivariate_t.cdf(b, mean, cov, df, random_state=rng, + allow_singular=True) + with np.errstate(invalid='ignore'): + ref = _qsimvtv(20000, df, cov, np.inf*a, b - mean, rng)[0] + assert_allclose(res, ref, atol=2e-4, rtol=1e-3) + + # with lower limit + res = stats.multivariate_t.cdf(b, mean, cov, df, lower_limit=a, + random_state=rng, allow_singular=True) + with np.errstate(invalid='ignore'): + ref = _qsimvtv(20000, df, cov, a - mean, b - mean, rng)[0] + assert_allclose(res, ref, atol=1e-4, rtol=1e-3) + + def test_cdf_against_generic_integrators(self): + # Compare result against generic numerical integrators + dim = 3 + rng = np.random.default_rng(41372291899657) + w = 10 ** rng.uniform(-1, 1, size=dim) + cov = _random_covariance(dim, w, rng, singular=True) + mean = rng.random(dim) + a = -rng.random(dim) + b = rng.random(dim) + df = rng.random() * 5 + + res = stats.multivariate_t.cdf(b, mean, cov, df, random_state=rng, + lower_limit=a) + + def integrand(x): + return stats.multivariate_t.pdf(x.T, mean, cov, df) + + ref = qmc_quad(integrand, a, b, qrng=stats.qmc.Halton(d=dim, seed=rng)) + assert_allclose(res, ref.integral, rtol=1e-3) + + def integrand(*zyx): + return stats.multivariate_t.pdf(zyx[::-1], mean, cov, df) + + ref = tplquad(integrand, a[0], b[0], a[1], b[1], a[2], b[2]) + assert_allclose(res, ref[0], rtol=1e-3) + + def test_against_matlab(self): + # Test against matlab mvtcdf: + # C = [6.21786909 0.2333667 7.95506077; + # 0.2333667 29.67390923 16.53946426; + # 7.95506077 16.53946426 19.17725252] + # df = 1.9559939787727658 + # mvtcdf([0, 0, 0], C, df) % 0.2523 + rng = np.random.default_rng(2967390923) + cov = np.array([[ 6.21786909, 0.2333667 , 7.95506077], + [ 0.2333667 , 29.67390923, 16.53946426], + [ 7.95506077, 16.53946426, 19.17725252]]) + df = 1.9559939787727658 + dist = stats.multivariate_t(shape=cov, df=df) + res = dist.cdf([0, 0, 0], random_state=rng) + ref = 0.2523 + assert_allclose(res, ref, rtol=1e-3) + + def test_frozen(self): + seed = 4137229573 + rng = np.random.default_rng(seed) + loc = rng.uniform(size=3) + x = rng.uniform(size=3) + loc + shape = np.eye(3) + df = rng.random() + args = (loc, shape, df) + + rng_frozen = np.random.default_rng(seed) + rng_unfrozen = np.random.default_rng(seed) + dist = stats.multivariate_t(*args, seed=rng_frozen) + assert_equal(dist.cdf(x), + multivariate_t.cdf(x, *args, random_state=rng_unfrozen)) + + def test_vectorized(self): + dim = 4 + n = (2, 3) + rng = np.random.default_rng(413722918996573) + A = rng.random(size=(dim, dim)) + cov = A @ A.T + mean = rng.random(dim) + x = rng.random(n + (dim,)) + df = rng.random() * 5 + + res = stats.multivariate_t.cdf(x, mean, cov, df, random_state=rng) + + def _cdf_1d(x): + return _qsimvtv(10000, df, cov, -np.inf*x, x-mean, rng)[0] + + ref = np.apply_along_axis(_cdf_1d, -1, x) + assert_allclose(res, ref, atol=1e-4, rtol=1e-3) + + @pytest.mark.parametrize("dim", (3, 7)) + def test_against_analytical(self, dim): + rng = np.random.default_rng(413722918996573) + A = scipy.linalg.toeplitz(c=[1] + [0.5] * (dim - 1)) + res = stats.multivariate_t(shape=A).cdf([0] * dim, random_state=rng) + ref = 1 / (dim + 1) + assert_allclose(res, ref, rtol=5e-5) + + def test_entropy_inf_df(self): + cov = np.eye(3, 3) + df = np.inf + mvt_entropy = stats.multivariate_t.entropy(shape=cov, df=df) + mvn_entropy = stats.multivariate_normal.entropy(None, cov) + assert mvt_entropy == mvn_entropy + + @pytest.mark.parametrize("df", [1, 10, 100]) + def test_entropy_1d(self, df): + mvt_entropy = stats.multivariate_t.entropy(shape=1., df=df) + t_entropy = stats.t.entropy(df=df) + assert_allclose(mvt_entropy, t_entropy, rtol=1e-13) + + # entropy reference values were computed via numerical integration + # + # def integrand(x, y, mvt): + # vec = np.array([x, y]) + # return mvt.logpdf(vec) * mvt.pdf(vec) + + # def multivariate_t_entropy_quad_2d(df, cov): + # dim = cov.shape[0] + # loc = np.zeros((dim, )) + # mvt = stats.multivariate_t(loc, cov, df) + # limit = 100 + # return -integrate.dblquad(integrand, -limit, limit, -limit, limit, + # args=(mvt, ))[0] + + @pytest.mark.parametrize("df, cov, ref, tol", + [(10, np.eye(2, 2), 3.0378770664093313, 1e-14), + (100, np.array([[0.5, 1], [1, 10]]), + 3.55102424550609, 1e-8)]) + def test_entropy_vs_numerical_integration(self, df, cov, ref, tol): + loc = np.zeros((2, )) + mvt = stats.multivariate_t(loc, cov, df) + assert_allclose(mvt.entropy(), ref, rtol=tol) + + @pytest.mark.parametrize( + "df, dim, ref, tol", + [ + (10, 1, 1.5212624929756808, 1e-15), + (100, 1, 1.4289633653182439, 1e-13), + (500, 1, 1.420939531869349, 1e-14), + (1e20, 1, 1.4189385332046727, 1e-15), + (1e100, 1, 1.4189385332046727, 1e-15), + (10, 10, 15.069150450832911, 1e-15), + (1000, 10, 14.19936546446673, 1e-13), + (1e20, 10, 14.189385332046728, 1e-15), + (1e100, 10, 14.189385332046728, 1e-15), + (10, 100, 148.28902883192654, 1e-15), + (1000, 100, 141.99155538003762, 1e-14), + (1e20, 100, 141.8938533204673, 1e-15), + (1e100, 100, 141.8938533204673, 1e-15), + ] + ) + def test_extreme_entropy(self, df, dim, ref, tol): + # Reference values were calculated with mpmath: + # from mpmath import mp + # mp.dps = 500 + # + # def mul_t_mpmath_entropy(dim, df=1): + # dim = mp.mpf(dim) + # df = mp.mpf(df) + # halfsum = (dim + df)/2 + # half_df = df/2 + # + # return float( + # -mp.loggamma(halfsum) + mp.loggamma(half_df) + # + dim / 2 * mp.log(df * mp.pi) + # + halfsum * (mp.digamma(halfsum) - mp.digamma(half_df)) + # + 0.0 + # ) + mvt = stats.multivariate_t(shape=np.eye(dim), df=df) + assert_allclose(mvt.entropy(), ref, rtol=tol) + + def test_entropy_with_covariance(self): + # Generated using np.randn(5, 5) and then rounding + # to two decimal places + _A = np.array([ + [1.42, 0.09, -0.49, 0.17, 0.74], + [-1.13, -0.01, 0.71, 0.4, -0.56], + [1.07, 0.44, -0.28, -0.44, 0.29], + [-1.5, -0.94, -0.67, 0.73, -1.1], + [0.17, -0.08, 1.46, -0.32, 1.36] + ]) + # Set cov to be a symmetric positive semi-definite matrix + cov = _A @ _A.T + + # Test the asymptotic case. For large degrees of freedom + # the entropy approaches the multivariate normal entropy. + df = 1e20 + mul_t_entropy = stats.multivariate_t.entropy(shape=cov, df=df) + mul_norm_entropy = multivariate_normal(None, cov=cov).entropy() + assert_allclose(mul_t_entropy, mul_norm_entropy, rtol=1e-15) + + # Test the regular case. For a dim of 5 the threshold comes out + # to be approximately 766.45. So using slightly + # different dfs on each site of the threshold, the entropies + # are being compared. + df1 = 765 + df2 = 768 + _entropy1 = stats.multivariate_t.entropy(shape=cov, df=df1) + _entropy2 = stats.multivariate_t.entropy(shape=cov, df=df2) + assert_allclose(_entropy1, _entropy2, rtol=1e-5) + + +class TestMultivariateHypergeom: + @pytest.mark.parametrize( + "x, m, n, expected", + [ + # Ground truth value from R dmvhyper + ([3, 4], [5, 10], 7, -1.119814), + # test for `n=0` + ([3, 4], [5, 10], 0, -np.inf), + # test for `x < 0` + ([-3, 4], [5, 10], 7, -np.inf), + # test for `m < 0` (RuntimeWarning issue) + ([3, 4], [-5, 10], 7, np.nan), + # test for all `m < 0` and `x.sum() != n` + ([[1, 2], [3, 4]], [[-4, -6], [-5, -10]], + [3, 7], [np.nan, np.nan]), + # test for `x < 0` and `m < 0` (RuntimeWarning issue) + ([-3, 4], [-5, 10], 1, np.nan), + # test for `x > m` + ([1, 11], [10, 1], 12, np.nan), + # test for `m < 0` (RuntimeWarning issue) + ([1, 11], [10, -1], 12, np.nan), + # test for `n < 0` + ([3, 4], [5, 10], -7, np.nan), + # test for `x.sum() != n` + ([3, 3], [5, 10], 7, -np.inf) + ] + ) + def test_logpmf(self, x, m, n, expected): + vals = multivariate_hypergeom.logpmf(x, m, n) + assert_allclose(vals, expected, rtol=1e-6) + + def test_reduces_hypergeom(self): + # test that the multivariate_hypergeom pmf reduces to the + # hypergeom pmf in the 2d case. + val1 = multivariate_hypergeom.pmf(x=[3, 1], m=[10, 5], n=4) + val2 = hypergeom.pmf(k=3, M=15, n=4, N=10) + assert_allclose(val1, val2, rtol=1e-8) + + val1 = multivariate_hypergeom.pmf(x=[7, 3], m=[15, 10], n=10) + val2 = hypergeom.pmf(k=7, M=25, n=10, N=15) + assert_allclose(val1, val2, rtol=1e-8) + + def test_rvs(self): + # test if `rvs` is unbiased and large sample size converges + # to the true mean. + rv = multivariate_hypergeom(m=[3, 5], n=4) + rvs = rv.rvs(size=1000, random_state=123) + assert_allclose(rvs.mean(0), rv.mean(), rtol=1e-2) + + def test_rvs_broadcasting(self): + rv = multivariate_hypergeom(m=[[3, 5], [5, 10]], n=[4, 9]) + rvs = rv.rvs(size=(1000, 2), random_state=123) + assert_allclose(rvs.mean(0), rv.mean(), rtol=1e-2) + + @pytest.mark.parametrize('m, n', ( + ([0, 0, 20, 0, 0], 5), ([0, 0, 0, 0, 0], 0), + ([0, 0], 0), ([0], 0) + )) + def test_rvs_gh16171(self, m, n): + res = multivariate_hypergeom.rvs(m, n) + m = np.asarray(m) + res_ex = m.copy() + res_ex[m != 0] = n + assert_equal(res, res_ex) + + @pytest.mark.parametrize( + "x, m, n, expected", + [ + ([5], [5], 5, 1), + ([3, 4], [5, 10], 7, 0.3263403), + # Ground truth value from R dmvhyper + ([[[3, 5], [0, 8]], [[-1, 9], [1, 1]]], + [5, 10], [[8, 8], [8, 2]], + [[0.3916084, 0.006993007], [0, 0.4761905]]), + # test with empty arrays. + (np.array([], dtype=int), np.array([], dtype=int), 0, []), + ([1, 2], [4, 5], 5, 0), + # Ground truth value from R dmvhyper + ([3, 3, 0], [5, 6, 7], 6, 0.01077354) + ] + ) + def test_pmf(self, x, m, n, expected): + vals = multivariate_hypergeom.pmf(x, m, n) + assert_allclose(vals, expected, rtol=1e-7) + + @pytest.mark.parametrize( + "x, m, n, expected", + [ + ([3, 4], [[5, 10], [10, 15]], 7, [0.3263403, 0.3407531]), + ([[1], [2]], [[3], [4]], [1, 3], [1., 0.]), + ([[[1], [2]]], [[3], [4]], [1, 3], [[1., 0.]]), + ([[1], [2]], [[[[3]]]], [1, 3], [[[1., 0.]]]) + ] + ) + def test_pmf_broadcasting(self, x, m, n, expected): + vals = multivariate_hypergeom.pmf(x, m, n) + assert_allclose(vals, expected, rtol=1e-7) + + def test_cov(self): + cov1 = multivariate_hypergeom.cov(m=[3, 7, 10], n=12) + cov2 = [[0.64421053, -0.26526316, -0.37894737], + [-0.26526316, 1.14947368, -0.88421053], + [-0.37894737, -0.88421053, 1.26315789]] + assert_allclose(cov1, cov2, rtol=1e-8) + + def test_cov_broadcasting(self): + cov1 = multivariate_hypergeom.cov(m=[[7, 9], [10, 15]], n=[8, 12]) + cov2 = [[[1.05, -1.05], [-1.05, 1.05]], + [[1.56, -1.56], [-1.56, 1.56]]] + assert_allclose(cov1, cov2, rtol=1e-8) + + cov3 = multivariate_hypergeom.cov(m=[[4], [5]], n=[4, 5]) + cov4 = [[[0.]], [[0.]]] + assert_allclose(cov3, cov4, rtol=1e-8) + + cov5 = multivariate_hypergeom.cov(m=[7, 9], n=[8, 12]) + cov6 = [[[1.05, -1.05], [-1.05, 1.05]], + [[0.7875, -0.7875], [-0.7875, 0.7875]]] + assert_allclose(cov5, cov6, rtol=1e-8) + + def test_var(self): + # test with hypergeom + var0 = multivariate_hypergeom.var(m=[10, 5], n=4) + var1 = hypergeom.var(M=15, n=4, N=10) + assert_allclose(var0, var1, rtol=1e-8) + + def test_var_broadcasting(self): + var0 = multivariate_hypergeom.var(m=[10, 5], n=[4, 8]) + var1 = multivariate_hypergeom.var(m=[10, 5], n=4) + var2 = multivariate_hypergeom.var(m=[10, 5], n=8) + assert_allclose(var0[0], var1, rtol=1e-8) + assert_allclose(var0[1], var2, rtol=1e-8) + + var3 = multivariate_hypergeom.var(m=[[10, 5], [10, 14]], n=[4, 8]) + var4 = [[0.6984127, 0.6984127], [1.352657, 1.352657]] + assert_allclose(var3, var4, rtol=1e-8) + + var5 = multivariate_hypergeom.var(m=[[5], [10]], n=[5, 10]) + var6 = [[0.], [0.]] + assert_allclose(var5, var6, rtol=1e-8) + + def test_mean(self): + # test with hypergeom + mean0 = multivariate_hypergeom.mean(m=[10, 5], n=4) + mean1 = hypergeom.mean(M=15, n=4, N=10) + assert_allclose(mean0[0], mean1, rtol=1e-8) + + mean2 = multivariate_hypergeom.mean(m=[12, 8], n=10) + mean3 = [12.*10./20., 8.*10./20.] + assert_allclose(mean2, mean3, rtol=1e-8) + + def test_mean_broadcasting(self): + mean0 = multivariate_hypergeom.mean(m=[[3, 5], [10, 5]], n=[4, 8]) + mean1 = [[3.*4./8., 5.*4./8.], [10.*8./15., 5.*8./15.]] + assert_allclose(mean0, mean1, rtol=1e-8) + + def test_mean_edge_cases(self): + mean0 = multivariate_hypergeom.mean(m=[0, 0, 0], n=0) + assert_equal(mean0, [0., 0., 0.]) + + mean1 = multivariate_hypergeom.mean(m=[1, 0, 0], n=2) + assert_equal(mean1, [np.nan, np.nan, np.nan]) + + mean2 = multivariate_hypergeom.mean(m=[[1, 0, 0], [1, 0, 1]], n=2) + assert_allclose(mean2, [[np.nan, np.nan, np.nan], [1., 0., 1.]], + rtol=1e-17) + + mean3 = multivariate_hypergeom.mean(m=np.array([], dtype=int), n=0) + assert_equal(mean3, []) + assert_(mean3.shape == (0, )) + + def test_var_edge_cases(self): + var0 = multivariate_hypergeom.var(m=[0, 0, 0], n=0) + assert_allclose(var0, [0., 0., 0.], rtol=1e-16) + + var1 = multivariate_hypergeom.var(m=[1, 0, 0], n=2) + assert_equal(var1, [np.nan, np.nan, np.nan]) + + var2 = multivariate_hypergeom.var(m=[[1, 0, 0], [1, 0, 1]], n=2) + assert_allclose(var2, [[np.nan, np.nan, np.nan], [0., 0., 0.]], + rtol=1e-17) + + var3 = multivariate_hypergeom.var(m=np.array([], dtype=int), n=0) + assert_equal(var3, []) + assert_(var3.shape == (0, )) + + def test_cov_edge_cases(self): + cov0 = multivariate_hypergeom.cov(m=[1, 0, 0], n=1) + cov1 = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] + assert_allclose(cov0, cov1, rtol=1e-17) + + cov3 = multivariate_hypergeom.cov(m=[0, 0, 0], n=0) + cov4 = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] + assert_equal(cov3, cov4) + + cov5 = multivariate_hypergeom.cov(m=np.array([], dtype=int), n=0) + cov6 = np.array([], dtype=np.float64).reshape(0, 0) + assert_allclose(cov5, cov6, rtol=1e-17) + assert_(cov5.shape == (0, 0)) + + def test_frozen(self): + # The frozen distribution should agree with the regular one + np.random.seed(1234) + n = 12 + m = [7, 9, 11, 13] + x = [[0, 0, 0, 12], [0, 0, 1, 11], [0, 1, 1, 10], + [1, 1, 1, 9], [1, 1, 2, 8]] + x = np.asarray(x, dtype=int) + mhg_frozen = multivariate_hypergeom(m, n) + assert_allclose(mhg_frozen.pmf(x), + multivariate_hypergeom.pmf(x, m, n)) + assert_allclose(mhg_frozen.logpmf(x), + multivariate_hypergeom.logpmf(x, m, n)) + assert_allclose(mhg_frozen.var(), multivariate_hypergeom.var(m, n)) + assert_allclose(mhg_frozen.cov(), multivariate_hypergeom.cov(m, n)) + + def test_invalid_params(self): + assert_raises(ValueError, multivariate_hypergeom.pmf, 5, 10, 5) + assert_raises(ValueError, multivariate_hypergeom.pmf, 5, [10], 5) + assert_raises(ValueError, multivariate_hypergeom.pmf, [5, 4], [10], 5) + assert_raises(TypeError, multivariate_hypergeom.pmf, [5.5, 4.5], + [10, 15], 5) + assert_raises(TypeError, multivariate_hypergeom.pmf, [5, 4], + [10.5, 15.5], 5) + assert_raises(TypeError, multivariate_hypergeom.pmf, [5, 4], + [10, 15], 5.5) + + +class TestRandomTable: + def get_rng(self): + return np.random.default_rng(628174795866951638) + + def test_process_parameters(self): + message = "`row` must be one-dimensional" + with pytest.raises(ValueError, match=message): + random_table([[1, 2]], [1, 2]) + + message = "`col` must be one-dimensional" + with pytest.raises(ValueError, match=message): + random_table([1, 2], [[1, 2]]) + + message = "each element of `row` must be non-negative" + with pytest.raises(ValueError, match=message): + random_table([1, -1], [1, 2]) + + message = "each element of `col` must be non-negative" + with pytest.raises(ValueError, match=message): + random_table([1, 2], [1, -2]) + + message = "sums over `row` and `col` must be equal" + with pytest.raises(ValueError, match=message): + random_table([1, 2], [1, 0]) + + message = "each element of `row` must be an integer" + with pytest.raises(ValueError, match=message): + random_table([2.1, 2.1], [1, 1, 2]) + + message = "each element of `col` must be an integer" + with pytest.raises(ValueError, match=message): + random_table([1, 2], [1.1, 1.1, 1]) + + row = [1, 3] + col = [2, 1, 1] + r, c, n = random_table._process_parameters([1, 3], [2, 1, 1]) + assert_equal(row, r) + assert_equal(col, c) + assert n == np.sum(row) + + @pytest.mark.parametrize("scale,method", + ((1, "boyett"), (100, "patefield"))) + def test_process_rvs_method_on_None(self, scale, method): + row = np.array([1, 3]) * scale + col = np.array([2, 1, 1]) * scale + + ct = random_table + expected = ct.rvs(row, col, method=method, random_state=1) + got = ct.rvs(row, col, method=None, random_state=1) + + assert_equal(expected, got) + + def test_process_rvs_method_bad_argument(self): + row = [1, 3] + col = [2, 1, 1] + + # order of items in set is random, so cannot check that + message = "'foo' not recognized, must be one of" + with pytest.raises(ValueError, match=message): + random_table.rvs(row, col, method="foo") + + @pytest.mark.parametrize('frozen', (True, False)) + @pytest.mark.parametrize('log', (True, False)) + def test_pmf_logpmf(self, frozen, log): + # The pmf is tested through random sample generation + # with Boyett's algorithm, whose implementation is simple + # enough to verify manually for correctness. + rng = self.get_rng() + row = [2, 6] + col = [1, 3, 4] + rvs = random_table.rvs(row, col, size=1000, + method="boyett", random_state=rng) + + obj = random_table(row, col) if frozen else random_table + method = getattr(obj, "logpmf" if log else "pmf") + if not frozen: + original_method = method + + def method(x): + return original_method(x, row, col) + pmf = (lambda x: np.exp(method(x))) if log else method + + unique_rvs, counts = np.unique(rvs, axis=0, return_counts=True) + + # rough accuracy check + p = pmf(unique_rvs) + assert_allclose(p * len(rvs), counts, rtol=0.1) + + # accept any iterable + p2 = pmf(list(unique_rvs[0])) + assert_equal(p2, p[0]) + + # accept high-dimensional input and 2d input + rvs_nd = rvs.reshape((10, 100) + rvs.shape[1:]) + p = pmf(rvs_nd) + assert p.shape == (10, 100) + for i in range(p.shape[0]): + for j in range(p.shape[1]): + pij = p[i, j] + rvij = rvs_nd[i, j] + qij = pmf(rvij) + assert_equal(pij, qij) + + # probability is zero if column marginal does not match + x = [[0, 1, 1], [2, 1, 3]] + assert_equal(np.sum(x, axis=-1), row) + p = pmf(x) + assert p == 0 + + # probability is zero if row marginal does not match + x = [[0, 1, 2], [1, 2, 2]] + assert_equal(np.sum(x, axis=-2), col) + p = pmf(x) + assert p == 0 + + # response to invalid inputs + message = "`x` must be at least two-dimensional" + with pytest.raises(ValueError, match=message): + pmf([1]) + + message = "`x` must contain only integral values" + with pytest.raises(ValueError, match=message): + pmf([[1.1]]) + + message = "`x` must contain only integral values" + with pytest.raises(ValueError, match=message): + pmf([[np.nan]]) + + message = "`x` must contain only non-negative values" + with pytest.raises(ValueError, match=message): + pmf([[-1]]) + + message = "shape of `x` must agree with `row`" + with pytest.raises(ValueError, match=message): + pmf([[1, 2, 3]]) + + message = "shape of `x` must agree with `col`" + with pytest.raises(ValueError, match=message): + pmf([[1, 2], + [3, 4]]) + + @pytest.mark.parametrize("method", ("boyett", "patefield")) + def test_rvs_mean(self, method): + # test if `rvs` is unbiased and large sample size converges + # to the true mean. + rng = self.get_rng() + row = [2, 6] + col = [1, 3, 4] + rvs = random_table.rvs(row, col, size=1000, method=method, + random_state=rng) + mean = random_table.mean(row, col) + assert_equal(np.sum(mean), np.sum(row)) + assert_allclose(rvs.mean(0), mean, atol=0.05) + assert_equal(rvs.sum(axis=-1), np.broadcast_to(row, (1000, 2))) + assert_equal(rvs.sum(axis=-2), np.broadcast_to(col, (1000, 3))) + + def test_rvs_cov(self): + # test if `rvs` generated with patefield and boyett algorithms + # produce approximately the same covariance matrix + rng = self.get_rng() + row = [2, 6] + col = [1, 3, 4] + rvs1 = random_table.rvs(row, col, size=10000, method="boyett", + random_state=rng) + rvs2 = random_table.rvs(row, col, size=10000, method="patefield", + random_state=rng) + cov1 = np.var(rvs1, axis=0) + cov2 = np.var(rvs2, axis=0) + assert_allclose(cov1, cov2, atol=0.02) + + @pytest.mark.parametrize("method", ("boyett", "patefield")) + def test_rvs_size(self, method): + row = [2, 6] + col = [1, 3, 4] + + # test size `None` + rv = random_table.rvs(row, col, method=method, + random_state=self.get_rng()) + assert rv.shape == (2, 3) + + # test size 1 + rv2 = random_table.rvs(row, col, size=1, method=method, + random_state=self.get_rng()) + assert rv2.shape == (1, 2, 3) + assert_equal(rv, rv2[0]) + + # test size 0 + rv3 = random_table.rvs(row, col, size=0, method=method, + random_state=self.get_rng()) + assert rv3.shape == (0, 2, 3) + + # test other valid size + rv4 = random_table.rvs(row, col, size=20, method=method, + random_state=self.get_rng()) + assert rv4.shape == (20, 2, 3) + + rv5 = random_table.rvs(row, col, size=(4, 5), method=method, + random_state=self.get_rng()) + assert rv5.shape == (4, 5, 2, 3) + + assert_allclose(rv5.reshape(20, 2, 3), rv4, rtol=1e-15) + + # test invalid size + message = "`size` must be a non-negative integer or `None`" + with pytest.raises(ValueError, match=message): + random_table.rvs(row, col, size=-1, method=method, + random_state=self.get_rng()) + + with pytest.raises(ValueError, match=message): + random_table.rvs(row, col, size=np.nan, method=method, + random_state=self.get_rng()) + + @pytest.mark.parametrize("method", ("boyett", "patefield")) + def test_rvs_method(self, method): + # This test assumes that pmf is correct and checks that random samples + # follow this probability distribution. This seems like a circular + # argument, since pmf is checked in test_pmf_logpmf with random samples + # generated with the rvs method. This test is not redundant, because + # test_pmf_logpmf intentionally uses rvs generation with Boyett only, + # but here we test both Boyett and Patefield. + row = [2, 6] + col = [1, 3, 4] + + ct = random_table + rvs = ct.rvs(row, col, size=100000, method=method, + random_state=self.get_rng()) + + unique_rvs, counts = np.unique(rvs, axis=0, return_counts=True) + + # generated frequencies should match expected frequencies + p = ct.pmf(unique_rvs, row, col) + assert_allclose(p * len(rvs), counts, rtol=0.02) + + @pytest.mark.parametrize("method", ("boyett", "patefield")) + def test_rvs_with_zeros_in_col_row(self, method): + row = [0, 1, 0] + col = [1, 0, 0, 0] + d = random_table(row, col) + rv = d.rvs(1000, method=method, random_state=self.get_rng()) + expected = np.zeros((1000, len(row), len(col))) + expected[...] = [[0, 0, 0, 0], + [1, 0, 0, 0], + [0, 0, 0, 0]] + assert_equal(rv, expected) + + @pytest.mark.parametrize("method", (None, "boyett", "patefield")) + @pytest.mark.parametrize("col", ([], [0])) + @pytest.mark.parametrize("row", ([], [0])) + def test_rvs_with_edge_cases(self, method, row, col): + d = random_table(row, col) + rv = d.rvs(10, method=method, random_state=self.get_rng()) + expected = np.zeros((10, len(row), len(col))) + assert_equal(rv, expected) + + @pytest.mark.parametrize('v', (1, 2)) + def test_rvs_rcont(self, v): + # This test checks the internal low-level interface. + # It is implicitly also checked by the other test_rvs* calls. + import scipy.stats._rcont as _rcont + + row = np.array([1, 3], dtype=np.int64) + col = np.array([2, 1, 1], dtype=np.int64) + + rvs = getattr(_rcont, f"rvs_rcont{v}") + + ntot = np.sum(row) + result = rvs(row, col, ntot, 1, self.get_rng()) + + assert result.shape == (1, len(row), len(col)) + assert np.sum(result) == ntot + + def test_frozen(self): + row = [2, 6] + col = [1, 3, 4] + d = random_table(row, col, seed=self.get_rng()) + + sample = d.rvs() + + expected = random_table.mean(row, col) + assert_equal(expected, d.mean()) + + expected = random_table.pmf(sample, row, col) + assert_equal(expected, d.pmf(sample)) + + expected = random_table.logpmf(sample, row, col) + assert_equal(expected, d.logpmf(sample)) + + @pytest.mark.parametrize("method", ("boyett", "patefield")) + def test_rvs_frozen(self, method): + row = [2, 6] + col = [1, 3, 4] + d = random_table(row, col, seed=self.get_rng()) + + expected = random_table.rvs(row, col, size=10, method=method, + random_state=self.get_rng()) + got = d.rvs(size=10, method=method) + assert_equal(expected, got) + + +def check_pickling(distfn, args): + # check that a distribution instance pickles and unpickles + # pay special attention to the random_state property + + # save the random_state (restore later) + rndm = distfn.random_state + + distfn.random_state = 1234 + distfn.rvs(*args, size=8) + s = pickle.dumps(distfn) + r0 = distfn.rvs(*args, size=8) + + unpickled = pickle.loads(s) + r1 = unpickled.rvs(*args, size=8) + assert_equal(r0, r1) + + # restore the random_state + distfn.random_state = rndm + + +def test_random_state_property(): + scale = np.eye(3) + scale[0, 1] = 0.5 + scale[1, 0] = 0.5 + dists = [ + [multivariate_normal, ()], + [dirichlet, (np.array([1.]), )], + [wishart, (10, scale)], + [invwishart, (10, scale)], + [multinomial, (5, [0.5, 0.4, 0.1])], + [ortho_group, (2,)], + [special_ortho_group, (2,)] + ] + for distfn, args in dists: + check_random_state_property(distfn, args) + check_pickling(distfn, args) + + +class TestVonMises_Fisher: + @pytest.mark.parametrize("dim", [2, 3, 4, 6]) + @pytest.mark.parametrize("size", [None, 1, 5, (5, 4)]) + def test_samples(self, dim, size): + # test that samples have correct shape and norm 1 + rng = np.random.default_rng(2777937887058094419) + mu = np.full((dim, ), 1/np.sqrt(dim)) + vmf_dist = vonmises_fisher(mu, 1, seed=rng) + samples = vmf_dist.rvs(size) + mean, cov = np.zeros(dim), np.eye(dim) + expected_shape = rng.multivariate_normal(mean, cov, size=size).shape + assert samples.shape == expected_shape + norms = np.linalg.norm(samples, axis=-1) + assert_allclose(norms, 1.) + + @pytest.mark.parametrize("dim", [5, 8]) + @pytest.mark.parametrize("kappa", [1e15, 1e20, 1e30]) + def test_sampling_high_concentration(self, dim, kappa): + # test that no warnings are encountered for high values + rng = np.random.default_rng(2777937887058094419) + mu = np.full((dim, ), 1/np.sqrt(dim)) + vmf_dist = vonmises_fisher(mu, kappa, seed=rng) + vmf_dist.rvs(10) + + def test_two_dimensional_mu(self): + mu = np.ones((2, 2)) + msg = "'mu' must have one-dimensional shape." + with pytest.raises(ValueError, match=msg): + vonmises_fisher(mu, 1) + + def test_wrong_norm_mu(self): + mu = np.ones((2, )) + msg = "'mu' must be a unit vector of norm 1." + with pytest.raises(ValueError, match=msg): + vonmises_fisher(mu, 1) + + def test_one_entry_mu(self): + mu = np.ones((1, )) + msg = "'mu' must have at least two entries." + with pytest.raises(ValueError, match=msg): + vonmises_fisher(mu, 1) + + @pytest.mark.parametrize("kappa", [-1, (5, 3)]) + def test_kappa_validation(self, kappa): + msg = "'kappa' must be a positive scalar." + with pytest.raises(ValueError, match=msg): + vonmises_fisher([1, 0], kappa) + + @pytest.mark.parametrize("kappa", [0, 0.]) + def test_kappa_zero(self, kappa): + msg = ("For 'kappa=0' the von Mises-Fisher distribution " + "becomes the uniform distribution on the sphere " + "surface. Consider using 'scipy.stats.uniform_direction' " + "instead.") + with pytest.raises(ValueError, match=msg): + vonmises_fisher([1, 0], kappa) + + + @pytest.mark.parametrize("method", [vonmises_fisher.pdf, + vonmises_fisher.logpdf]) + def test_invalid_shapes_pdf_logpdf(self, method): + x = np.array([1., 0., 0]) + msg = ("The dimensionality of the last axis of 'x' must " + "match the dimensionality of the von Mises Fisher " + "distribution.") + with pytest.raises(ValueError, match=msg): + method(x, [1, 0], 1) + + @pytest.mark.parametrize("method", [vonmises_fisher.pdf, + vonmises_fisher.logpdf]) + def test_unnormalized_input(self, method): + x = np.array([0.5, 0.]) + msg = "'x' must be unit vectors of norm 1 along last dimension." + with pytest.raises(ValueError, match=msg): + method(x, [1, 0], 1) + + # Expected values of the vonmises-fisher logPDF were computed via mpmath + # from mpmath import mp + # import numpy as np + # mp.dps = 50 + # def logpdf_mpmath(x, mu, kappa): + # dim = mu.size + # halfdim = mp.mpf(0.5 * dim) + # kappa = mp.mpf(kappa) + # const = (kappa**(halfdim - mp.one)/((2*mp.pi)**halfdim * \ + # mp.besseli(halfdim -mp.one, kappa))) + # return float(const * mp.exp(kappa*mp.fdot(x, mu))) + + @pytest.mark.parametrize('x, mu, kappa, reference', + [(np.array([1., 0., 0.]), np.array([1., 0., 0.]), + 1e-4, 0.0795854295583605), + (np.array([1., 0., 0]), np.array([0., 0., 1.]), + 1e-4, 0.07957747141331854), + (np.array([1., 0., 0.]), np.array([1., 0., 0.]), + 100, 15.915494309189533), + (np.array([1., 0., 0]), np.array([0., 0., 1.]), + 100, 5.920684802611232e-43), + (np.array([1., 0., 0.]), + np.array([np.sqrt(0.98), np.sqrt(0.02), 0.]), + 2000, 5.930499050746588e-07), + (np.array([1., 0., 0]), np.array([1., 0., 0.]), + 2000, 318.3098861837907), + (np.array([1., 0., 0., 0., 0.]), + np.array([1., 0., 0., 0., 0.]), + 2000, 101371.86957712633), + (np.array([1., 0., 0., 0., 0.]), + np.array([np.sqrt(0.98), np.sqrt(0.02), 0., + 0, 0.]), + 2000, 0.00018886808182653578), + (np.array([1., 0., 0., 0., 0.]), + np.array([np.sqrt(0.8), np.sqrt(0.2), 0., + 0, 0.]), + 2000, 2.0255393314603194e-87)]) + def test_pdf_accuracy(self, x, mu, kappa, reference): + pdf = vonmises_fisher(mu, kappa).pdf(x) + assert_allclose(pdf, reference, rtol=1e-13) + + # Expected values of the vonmises-fisher logPDF were computed via mpmath + # from mpmath import mp + # import numpy as np + # mp.dps = 50 + # def logpdf_mpmath(x, mu, kappa): + # dim = mu.size + # halfdim = mp.mpf(0.5 * dim) + # kappa = mp.mpf(kappa) + # two = mp.mpf(2.) + # const = (kappa**(halfdim - mp.one)/((two*mp.pi)**halfdim * \ + # mp.besseli(halfdim - mp.one, kappa))) + # return float(mp.log(const * mp.exp(kappa*mp.fdot(x, mu)))) + + @pytest.mark.parametrize('x, mu, kappa, reference', + [(np.array([1., 0., 0.]), np.array([1., 0., 0.]), + 1e-4, -2.5309242486359573), + (np.array([1., 0., 0]), np.array([0., 0., 1.]), + 1e-4, -2.5310242486359575), + (np.array([1., 0., 0.]), np.array([1., 0., 0.]), + 100, 2.767293119578746), + (np.array([1., 0., 0]), np.array([0., 0., 1.]), + 100, -97.23270688042125), + (np.array([1., 0., 0.]), + np.array([np.sqrt(0.98), np.sqrt(0.02), 0.]), + 2000, -14.337987284534103), + (np.array([1., 0., 0]), np.array([1., 0., 0.]), + 2000, 5.763025393132737), + (np.array([1., 0., 0., 0., 0.]), + np.array([1., 0., 0., 0., 0.]), + 2000, 11.526550911307156), + (np.array([1., 0., 0., 0., 0.]), + np.array([np.sqrt(0.98), np.sqrt(0.02), 0., + 0, 0.]), + 2000, -8.574461766359684), + (np.array([1., 0., 0., 0., 0.]), + np.array([np.sqrt(0.8), np.sqrt(0.2), 0., + 0, 0.]), + 2000, -199.61906708886113)]) + def test_logpdf_accuracy(self, x, mu, kappa, reference): + logpdf = vonmises_fisher(mu, kappa).logpdf(x) + assert_allclose(logpdf, reference, rtol=1e-14) + + # Expected values of the vonmises-fisher entropy were computed via mpmath + # from mpmath import mp + # import numpy as np + # mp.dps = 50 + # def entropy_mpmath(dim, kappa): + # mu = np.full((dim, ), 1/np.sqrt(dim)) + # kappa = mp.mpf(kappa) + # halfdim = mp.mpf(0.5 * dim) + # logconstant = (mp.log(kappa**(halfdim - mp.one) + # /((2*mp.pi)**halfdim + # * mp.besseli(halfdim -mp.one, kappa))) + # return float(-logconstant - kappa * mp.besseli(halfdim, kappa)/ + # mp.besseli(halfdim -1, kappa)) + + @pytest.mark.parametrize('dim, kappa, reference', + [(3, 1e-4, 2.531024245302624), + (3, 100, -1.7672931195787458), + (5, 5000, -11.359032310024453), + (8, 1, 3.4189526482545527)]) + def test_entropy_accuracy(self, dim, kappa, reference): + mu = np.full((dim, ), 1/np.sqrt(dim)) + entropy = vonmises_fisher(mu, kappa).entropy() + assert_allclose(entropy, reference, rtol=2e-14) + + @pytest.mark.parametrize("method", [vonmises_fisher.pdf, + vonmises_fisher.logpdf]) + def test_broadcasting(self, method): + # test that pdf and logpdf values are correctly broadcasted + testshape = (2, 2) + rng = np.random.default_rng(2777937887058094419) + x = uniform_direction(3).rvs(testshape, random_state=rng) + mu = np.full((3, ), 1/np.sqrt(3)) + kappa = 5 + result_all = method(x, mu, kappa) + assert result_all.shape == testshape + for i in range(testshape[0]): + for j in range(testshape[1]): + current_val = method(x[i, j, :], mu, kappa) + assert_allclose(current_val, result_all[i, j], rtol=1e-15) + + def test_vs_vonmises_2d(self): + # test that in 2D, von Mises-Fisher yields the same results + # as the von Mises distribution + rng = np.random.default_rng(2777937887058094419) + mu = np.array([0, 1]) + mu_angle = np.arctan2(mu[1], mu[0]) + kappa = 20 + vmf = vonmises_fisher(mu, kappa) + vonmises_dist = vonmises(loc=mu_angle, kappa=kappa) + vectors = uniform_direction(2).rvs(10, random_state=rng) + angles = np.arctan2(vectors[:, 1], vectors[:, 0]) + assert_allclose(vonmises_dist.entropy(), vmf.entropy()) + assert_allclose(vonmises_dist.pdf(angles), vmf.pdf(vectors)) + assert_allclose(vonmises_dist.logpdf(angles), vmf.logpdf(vectors)) + + @pytest.mark.parametrize("dim", [2, 3, 6]) + @pytest.mark.parametrize("kappa, mu_tol, kappa_tol", + [(1, 5e-2, 5e-2), + (10, 1e-2, 1e-2), + (100, 5e-3, 2e-2), + (1000, 1e-3, 2e-2)]) + def test_fit_accuracy(self, dim, kappa, mu_tol, kappa_tol): + mu = np.full((dim, ), 1/np.sqrt(dim)) + vmf_dist = vonmises_fisher(mu, kappa) + rng = np.random.default_rng(2777937887058094419) + n_samples = 10000 + samples = vmf_dist.rvs(n_samples, random_state=rng) + mu_fit, kappa_fit = vonmises_fisher.fit(samples) + angular_error = np.arccos(mu.dot(mu_fit)) + assert_allclose(angular_error, 0., atol=mu_tol, rtol=0) + assert_allclose(kappa, kappa_fit, rtol=kappa_tol) + + def test_fit_error_one_dimensional_data(self): + x = np.zeros((3, )) + msg = "'x' must be two dimensional." + with pytest.raises(ValueError, match=msg): + vonmises_fisher.fit(x) + + def test_fit_error_unnormalized_data(self): + x = np.ones((3, 3)) + msg = "'x' must be unit vectors of norm 1 along last dimension." + with pytest.raises(ValueError, match=msg): + vonmises_fisher.fit(x) + + def test_frozen_distribution(self): + mu = np.array([0, 0, 1]) + kappa = 5 + frozen = vonmises_fisher(mu, kappa) + frozen_seed = vonmises_fisher(mu, kappa, seed=514) + + rvs1 = frozen.rvs(random_state=514) + rvs2 = vonmises_fisher.rvs(mu, kappa, random_state=514) + rvs3 = frozen_seed.rvs() + + assert_equal(rvs1, rvs2) + assert_equal(rvs1, rvs3) + + +class TestDirichletMultinomial: + @classmethod + def get_params(self, m): + rng = np.random.default_rng(28469824356873456) + alpha = rng.uniform(0, 100, size=2) + x = rng.integers(1, 20, size=(m, 2)) + n = x.sum(axis=-1) + return rng, m, alpha, n, x + + def test_frozen(self): + rng = np.random.default_rng(28469824356873456) + + alpha = rng.uniform(0, 100, 10) + x = rng.integers(0, 10, 10) + n = np.sum(x, axis=-1) + + d = dirichlet_multinomial(alpha, n) + assert_equal(d.logpmf(x), dirichlet_multinomial.logpmf(x, alpha, n)) + assert_equal(d.pmf(x), dirichlet_multinomial.pmf(x, alpha, n)) + assert_equal(d.mean(), dirichlet_multinomial.mean(alpha, n)) + assert_equal(d.var(), dirichlet_multinomial.var(alpha, n)) + assert_equal(d.cov(), dirichlet_multinomial.cov(alpha, n)) + + def test_pmf_logpmf_against_R(self): + # # Compare PMF against R's extraDistr ddirmnon + # # library(extraDistr) + # # options(digits=16) + # ddirmnom(c(1, 2, 3), 6, c(3, 4, 5)) + x = np.array([1, 2, 3]) + n = np.sum(x) + alpha = np.array([3, 4, 5]) + res = dirichlet_multinomial.pmf(x, alpha, n) + logres = dirichlet_multinomial.logpmf(x, alpha, n) + ref = 0.08484162895927638 + assert_allclose(res, ref) + assert_allclose(logres, np.log(ref)) + assert res.shape == logres.shape == () + + # library(extraDistr) + # options(digits=16) + # ddirmnom(c(4, 3, 2, 0, 2, 3, 5, 7, 4, 7), 37, + # c(45.01025314, 21.98739582, 15.14851365, 80.21588671, + # 52.84935481, 25.20905262, 53.85373737, 4.88568118, + # 89.06440654, 20.11359466)) + rng = np.random.default_rng(28469824356873456) + alpha = rng.uniform(0, 100, 10) + x = rng.integers(0, 10, 10) + n = np.sum(x, axis=-1) + res = dirichlet_multinomial(alpha, n).pmf(x) + logres = dirichlet_multinomial.logpmf(x, alpha, n) + ref = 3.65409306285992e-16 + assert_allclose(res, ref) + assert_allclose(logres, np.log(ref)) + + def test_pmf_logpmf_support(self): + # when the sum of the category counts does not equal the number of + # trials, the PMF is zero + rng, m, alpha, n, x = self.get_params(1) + n += 1 + assert_equal(dirichlet_multinomial(alpha, n).pmf(x), 0) + assert_equal(dirichlet_multinomial(alpha, n).logpmf(x), -np.inf) + + rng, m, alpha, n, x = self.get_params(10) + i = rng.random(size=10) > 0.5 + x[i] = np.round(x[i] * 2) # sum of these x does not equal n + assert_equal(dirichlet_multinomial(alpha, n).pmf(x)[i], 0) + assert_equal(dirichlet_multinomial(alpha, n).logpmf(x)[i], -np.inf) + assert np.all(dirichlet_multinomial(alpha, n).pmf(x)[~i] > 0) + assert np.all(dirichlet_multinomial(alpha, n).logpmf(x)[~i] > -np.inf) + + def test_dimensionality_one(self): + # if the dimensionality is one, there is only one possible outcome + n = 6 # number of trials + alpha = [10] # concentration parameters + x = np.asarray([n]) # counts + dist = dirichlet_multinomial(alpha, n) + + assert_equal(dist.pmf(x), 1) + assert_equal(dist.pmf(x+1), 0) + assert_equal(dist.logpmf(x), 0) + assert_equal(dist.logpmf(x+1), -np.inf) + assert_equal(dist.mean(), n) + assert_equal(dist.var(), 0) + assert_equal(dist.cov(), 0) + + @pytest.mark.parametrize('method_name', ['pmf', 'logpmf']) + def test_against_betabinom_pmf(self, method_name): + rng, m, alpha, n, x = self.get_params(100) + + method = getattr(dirichlet_multinomial(alpha, n), method_name) + ref_method = getattr(stats.betabinom(n, *alpha.T), method_name) + + res = method(x) + ref = ref_method(x.T[0]) + assert_allclose(res, ref) + + @pytest.mark.parametrize('method_name', ['mean', 'var']) + def test_against_betabinom_moments(self, method_name): + rng, m, alpha, n, x = self.get_params(100) + + method = getattr(dirichlet_multinomial(alpha, n), method_name) + ref_method = getattr(stats.betabinom(n, *alpha.T), method_name) + + res = method()[:, 0] + ref = ref_method() + assert_allclose(res, ref) + + def test_moments(self): + message = 'Needs NumPy 1.22.0 for multinomial broadcasting' + if Version(np.__version__) < Version("1.22.0"): + pytest.skip(reason=message) + + rng = np.random.default_rng(28469824356873456) + dim = 5 + n = rng.integers(1, 100) + alpha = rng.random(size=dim) * 10 + dist = dirichlet_multinomial(alpha, n) + + # Generate a random sample from the distribution using NumPy + m = 100000 + p = rng.dirichlet(alpha, size=m) + x = rng.multinomial(n, p, size=m) + + assert_allclose(dist.mean(), np.mean(x, axis=0), rtol=5e-3) + assert_allclose(dist.var(), np.var(x, axis=0), rtol=1e-2) + assert dist.mean().shape == dist.var().shape == (dim,) + + cov = dist.cov() + assert cov.shape == (dim, dim) + assert_allclose(cov, np.cov(x.T), rtol=2e-2) + assert_equal(np.diag(cov), dist.var()) + assert np.all(scipy.linalg.eigh(cov)[0] > 0) # positive definite + + def test_input_validation(self): + # valid inputs + x0 = np.array([1, 2, 3]) + n0 = np.sum(x0) + alpha0 = np.array([3, 4, 5]) + + text = "`x` must contain only non-negative integers." + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf([1, -1, 3], alpha0, n0) + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf([1, 2.1, 3], alpha0, n0) + + text = "`alpha` must contain only positive values." + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf(x0, [3, 0, 4], n0) + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf(x0, [3, -1, 4], n0) + + text = "`n` must be a positive integer." + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf(x0, alpha0, 49.1) + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf(x0, alpha0, 0) + + x = np.array([1, 2, 3, 4]) + alpha = np.array([3, 4, 5]) + text = "`x` and `alpha` must be broadcastable." + with assert_raises(ValueError, match=text): + dirichlet_multinomial.logpmf(x, alpha, x.sum()) + + @pytest.mark.parametrize('method', ['pmf', 'logpmf']) + def test_broadcasting_pmf(self, method): + alpha = np.array([[3, 4, 5], [4, 5, 6], [5, 5, 7], [8, 9, 10]]) + n = np.array([[6], [7], [8]]) + x = np.array([[1, 2, 3], [2, 2, 3]]).reshape((2, 1, 1, 3)) + method = getattr(dirichlet_multinomial, method) + res = method(x, alpha, n) + assert res.shape == (2, 3, 4) + for i in range(len(x)): + for j in range(len(n)): + for k in range(len(alpha)): + res_ijk = res[i, j, k] + ref = method(x[i].squeeze(), alpha[k].squeeze(), n[j].squeeze()) + assert_allclose(res_ijk, ref) + + @pytest.mark.parametrize('method_name', ['mean', 'var', 'cov']) + def test_broadcasting_moments(self, method_name): + alpha = np.array([[3, 4, 5], [4, 5, 6], [5, 5, 7], [8, 9, 10]]) + n = np.array([[6], [7], [8]]) + method = getattr(dirichlet_multinomial, method_name) + res = method(alpha, n) + assert res.shape == (3, 4, 3) if method_name != 'cov' else (3, 4, 3, 3) + for j in range(len(n)): + for k in range(len(alpha)): + res_ijk = res[j, k] + ref = method(alpha[k].squeeze(), n[j].squeeze()) + assert_allclose(res_ijk, ref) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_odds_ratio.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_odds_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..ffb38a05c8df2e0bd4d7336ef344aa2f73bd0d6c --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_odds_ratio.py @@ -0,0 +1,147 @@ +import pytest +import numpy as np +from numpy.testing import assert_equal, assert_allclose +from .._discrete_distns import nchypergeom_fisher, hypergeom +from scipy.stats._odds_ratio import odds_ratio +from .data.fisher_exact_results_from_r import data + + +class TestOddsRatio: + + @pytest.mark.parametrize('parameters, rresult', data) + def test_results_from_r(self, parameters, rresult): + alternative = parameters.alternative.replace('.', '-') + result = odds_ratio(parameters.table) + # The results computed by R are not very accurate. + if result.statistic < 400: + or_rtol = 5e-4 + ci_rtol = 2e-2 + else: + or_rtol = 5e-2 + ci_rtol = 1e-1 + assert_allclose(result.statistic, + rresult.conditional_odds_ratio, rtol=or_rtol) + ci = result.confidence_interval(parameters.confidence_level, + alternative) + assert_allclose((ci.low, ci.high), rresult.conditional_odds_ratio_ci, + rtol=ci_rtol) + + # Also do a self-check for the conditional odds ratio. + # With the computed conditional odds ratio as the noncentrality + # parameter of the noncentral hypergeometric distribution with + # parameters table.sum(), table[0].sum(), and table[:,0].sum() as + # total, ngood and nsample, respectively, the mean of the distribution + # should equal table[0, 0]. + cor = result.statistic + table = np.array(parameters.table) + total = table.sum() + ngood = table[0].sum() + nsample = table[:, 0].sum() + # nchypergeom_fisher does not allow the edge cases where the + # noncentrality parameter is 0 or inf, so handle those values + # separately here. + if cor == 0: + nchg_mean = hypergeom.support(total, ngood, nsample)[0] + elif cor == np.inf: + nchg_mean = hypergeom.support(total, ngood, nsample)[1] + else: + nchg_mean = nchypergeom_fisher.mean(total, ngood, nsample, cor) + assert_allclose(nchg_mean, table[0, 0], rtol=1e-13) + + # Check that the confidence interval is correct. + alpha = 1 - parameters.confidence_level + if alternative == 'two-sided': + if ci.low > 0: + sf = nchypergeom_fisher.sf(table[0, 0] - 1, + total, ngood, nsample, ci.low) + assert_allclose(sf, alpha/2, rtol=1e-11) + if np.isfinite(ci.high): + cdf = nchypergeom_fisher.cdf(table[0, 0], + total, ngood, nsample, ci.high) + assert_allclose(cdf, alpha/2, rtol=1e-11) + elif alternative == 'less': + if np.isfinite(ci.high): + cdf = nchypergeom_fisher.cdf(table[0, 0], + total, ngood, nsample, ci.high) + assert_allclose(cdf, alpha, rtol=1e-11) + else: + # alternative == 'greater' + if ci.low > 0: + sf = nchypergeom_fisher.sf(table[0, 0] - 1, + total, ngood, nsample, ci.low) + assert_allclose(sf, alpha, rtol=1e-11) + + @pytest.mark.parametrize('table', [ + [[0, 0], [5, 10]], + [[5, 10], [0, 0]], + [[0, 5], [0, 10]], + [[5, 0], [10, 0]], + ]) + def test_row_or_col_zero(self, table): + result = odds_ratio(table) + assert_equal(result.statistic, np.nan) + ci = result.confidence_interval() + assert_equal((ci.low, ci.high), (0, np.inf)) + + @pytest.mark.parametrize("case", + [[0.95, 'two-sided', 0.4879913, 2.635883], + [0.90, 'two-sided', 0.5588516, 2.301663]]) + def test_sample_odds_ratio_ci(self, case): + # Compare the sample odds ratio confidence interval to the R function + # oddsratio.wald from the epitools package, e.g. + # > library(epitools) + # > table = matrix(c(10, 20, 41, 93), nrow=2, ncol=2, byrow=TRUE) + # > result = oddsratio.wald(table) + # > result$measure + # odds ratio with 95% C.I. + # Predictor estimate lower upper + # Exposed1 1.000000 NA NA + # Exposed2 1.134146 0.4879913 2.635883 + + confidence_level, alternative, ref_low, ref_high = case + table = [[10, 20], [41, 93]] + result = odds_ratio(table, kind='sample') + assert_allclose(result.statistic, 1.134146, rtol=1e-6) + ci = result.confidence_interval(confidence_level, alternative) + assert_allclose([ci.low, ci.high], [ref_low, ref_high], rtol=1e-6) + + @pytest.mark.parametrize('alternative', ['less', 'greater', 'two-sided']) + def test_sample_odds_ratio_one_sided_ci(self, alternative): + # can't find a good reference for one-sided CI, so bump up the sample + # size and compare against the conditional odds ratio CI + table = [[1000, 2000], [4100, 9300]] + res = odds_ratio(table, kind='sample') + ref = odds_ratio(table, kind='conditional') + assert_allclose(res.statistic, ref.statistic, atol=1e-5) + assert_allclose(res.confidence_interval(alternative=alternative), + ref.confidence_interval(alternative=alternative), + atol=2e-3) + + @pytest.mark.parametrize('kind', ['sample', 'conditional']) + @pytest.mark.parametrize('bad_table', [123, "foo", [10, 11, 12]]) + def test_invalid_table_shape(self, kind, bad_table): + with pytest.raises(ValueError, match="Invalid shape"): + odds_ratio(bad_table, kind=kind) + + def test_invalid_table_type(self): + with pytest.raises(ValueError, match='must be an array of integers'): + odds_ratio([[1.0, 3.4], [5.0, 9.9]]) + + def test_negative_table_values(self): + with pytest.raises(ValueError, match='must be nonnegative'): + odds_ratio([[1, 2], [3, -4]]) + + def test_invalid_kind(self): + with pytest.raises(ValueError, match='`kind` must be'): + odds_ratio([[10, 20], [30, 14]], kind='magnetoreluctance') + + def test_invalid_alternative(self): + result = odds_ratio([[5, 10], [2, 32]]) + with pytest.raises(ValueError, match='`alternative` must be'): + result.confidence_interval(alternative='depleneration') + + @pytest.mark.parametrize('level', [-0.5, 1.5]) + def test_invalid_confidence_level(self, level): + result = odds_ratio([[5, 10], [2, 32]]) + with pytest.raises(ValueError, match='must be between 0 and 1'): + result.confidence_interval(confidence_level=level) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_qmc.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_qmc.py new file mode 100644 index 0000000000000000000000000000000000000000..968e45c8196671470dd69271cf6cbac206b24f40 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_qmc.py @@ -0,0 +1,1410 @@ +import os +from collections import Counter +from itertools import combinations, product + +import pytest +import numpy as np +from numpy.testing import assert_allclose, assert_equal, assert_array_equal + +from scipy.spatial import distance +from scipy.stats import shapiro +from scipy.stats._sobol import _test_find_index +from scipy.stats import qmc +from scipy.stats._qmc import ( + van_der_corput, n_primes, primes_from_2_to, + update_discrepancy, QMCEngine, _l1_norm, + _perturb_discrepancy, _lloyd_centroidal_voronoi_tessellation +) + + +class TestUtils: + def test_scale(self): + # 1d scalar + space = [[0], [1], [0.5]] + out = [[-2], [6], [2]] + scaled_space = qmc.scale(space, l_bounds=-2, u_bounds=6) + + assert_allclose(scaled_space, out) + + # 2d space + space = [[0, 0], [1, 1], [0.5, 0.5]] + bounds = np.array([[-2, 0], [6, 5]]) + out = [[-2, 0], [6, 5], [2, 2.5]] + + scaled_space = qmc.scale(space, l_bounds=bounds[0], u_bounds=bounds[1]) + + assert_allclose(scaled_space, out) + + scaled_back_space = qmc.scale(scaled_space, l_bounds=bounds[0], + u_bounds=bounds[1], reverse=True) + assert_allclose(scaled_back_space, space) + + # broadcast + space = [[0, 0, 0], [1, 1, 1], [0.5, 0.5, 0.5]] + l_bounds, u_bounds = 0, [6, 5, 3] + out = [[0, 0, 0], [6, 5, 3], [3, 2.5, 1.5]] + + scaled_space = qmc.scale(space, l_bounds=l_bounds, u_bounds=u_bounds) + + assert_allclose(scaled_space, out) + + def test_scale_random(self): + rng = np.random.default_rng(317589836511269190194010915937762468165) + sample = rng.random((30, 10)) + a = -rng.random(10) * 10 + b = rng.random(10) * 10 + scaled = qmc.scale(sample, a, b, reverse=False) + unscaled = qmc.scale(scaled, a, b, reverse=True) + assert_allclose(unscaled, sample) + + def test_scale_errors(self): + with pytest.raises(ValueError, match=r"Sample is not a 2D array"): + space = [0, 1, 0.5] + qmc.scale(space, l_bounds=-2, u_bounds=6) + + with pytest.raises(ValueError, match=r"Bounds are not consistent"): + space = [[0, 0], [1, 1], [0.5, 0.5]] + bounds = np.array([[-2, 6], [6, 5]]) + qmc.scale(space, l_bounds=bounds[0], u_bounds=bounds[1]) + + with pytest.raises(ValueError, match=r"'l_bounds' and 'u_bounds'" + r" must be broadcastable"): + space = [[0, 0], [1, 1], [0.5, 0.5]] + l_bounds, u_bounds = [-2, 0, 2], [6, 5] + qmc.scale(space, l_bounds=l_bounds, u_bounds=u_bounds) + + with pytest.raises(ValueError, match=r"'l_bounds' and 'u_bounds'" + r" must be broadcastable"): + space = [[0, 0], [1, 1], [0.5, 0.5]] + bounds = np.array([[-2, 0, 2], [6, 5, 5]]) + qmc.scale(space, l_bounds=bounds[0], u_bounds=bounds[1]) + + with pytest.raises(ValueError, match=r"Sample is not in unit " + r"hypercube"): + space = [[0, 0], [1, 1.5], [0.5, 0.5]] + bounds = np.array([[-2, 0], [6, 5]]) + qmc.scale(space, l_bounds=bounds[0], u_bounds=bounds[1]) + + with pytest.raises(ValueError, match=r"Sample is out of bounds"): + out = [[-2, 0], [6, 5], [8, 2.5]] + bounds = np.array([[-2, 0], [6, 5]]) + qmc.scale(out, l_bounds=bounds[0], u_bounds=bounds[1], + reverse=True) + + def test_discrepancy(self): + space_1 = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + space_1 = (2.0 * space_1 - 1.0) / (2.0 * 6.0) + space_2 = np.array([[1, 5], [2, 4], [3, 3], [4, 2], [5, 1], [6, 6]]) + space_2 = (2.0 * space_2 - 1.0) / (2.0 * 6.0) + + # From Fang et al. Design and modeling for computer experiments, 2006 + assert_allclose(qmc.discrepancy(space_1), 0.0081, atol=1e-4) + assert_allclose(qmc.discrepancy(space_2), 0.0105, atol=1e-4) + + # From Zhou Y.-D. et al. Mixture discrepancy for quasi-random point + # sets. Journal of Complexity, 29 (3-4), pp. 283-301, 2013. + # Example 4 on Page 298 + sample = np.array([[2, 1, 1, 2, 2, 2], + [1, 2, 2, 2, 2, 2], + [2, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 2, 2], + [1, 2, 2, 2, 1, 1], + [2, 2, 2, 2, 1, 1], + [2, 2, 2, 1, 2, 2]]) + sample = (2.0 * sample - 1.0) / (2.0 * 2.0) + + assert_allclose(qmc.discrepancy(sample, method='MD'), 2.5000, + atol=1e-4) + assert_allclose(qmc.discrepancy(sample, method='WD'), 1.3680, + atol=1e-4) + assert_allclose(qmc.discrepancy(sample, method='CD'), 0.3172, + atol=1e-4) + + # From Tim P. et al. Minimizing the L2 and Linf star discrepancies + # of a single point in the unit hypercube. JCAM, 2005 + # Table 1 on Page 283 + for dim in [2, 4, 8, 16, 32, 64]: + ref = np.sqrt(3**(-dim)) + assert_allclose(qmc.discrepancy(np.array([[1]*dim]), + method='L2-star'), ref) + + def test_discrepancy_errors(self): + sample = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + + with pytest.raises( + ValueError, match=r"Sample is not in unit hypercube" + ): + qmc.discrepancy(sample) + + with pytest.raises(ValueError, match=r"Sample is not a 2D array"): + qmc.discrepancy([1, 3]) + + sample = [[0, 0], [1, 1], [0.5, 0.5]] + with pytest.raises(ValueError, match=r"'toto' is not a valid ..."): + qmc.discrepancy(sample, method="toto") + + def test_discrepancy_parallel(self, monkeypatch): + sample = np.array([[2, 1, 1, 2, 2, 2], + [1, 2, 2, 2, 2, 2], + [2, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 2, 2], + [1, 2, 2, 2, 1, 1], + [2, 2, 2, 2, 1, 1], + [2, 2, 2, 1, 2, 2]]) + sample = (2.0 * sample - 1.0) / (2.0 * 2.0) + + assert_allclose(qmc.discrepancy(sample, method='MD', workers=8), + 2.5000, + atol=1e-4) + assert_allclose(qmc.discrepancy(sample, method='WD', workers=8), + 1.3680, + atol=1e-4) + assert_allclose(qmc.discrepancy(sample, method='CD', workers=8), + 0.3172, + atol=1e-4) + + # From Tim P. et al. Minimizing the L2 and Linf star discrepancies + # of a single point in the unit hypercube. JCAM, 2005 + # Table 1 on Page 283 + for dim in [2, 4, 8, 16, 32, 64]: + ref = np.sqrt(3 ** (-dim)) + assert_allclose(qmc.discrepancy(np.array([[1] * dim]), + method='L2-star', workers=-1), ref) + + monkeypatch.setattr(os, 'cpu_count', lambda: None) + with pytest.raises(NotImplementedError, match="Cannot determine the"): + qmc.discrepancy(sample, workers=-1) + + with pytest.raises(ValueError, match="Invalid number of workers..."): + qmc.discrepancy(sample, workers=-2) + + def test_geometric_discrepancy_errors(self): + sample = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + + with pytest.raises(ValueError, match=r"Sample is not in unit hypercube"): + qmc.geometric_discrepancy(sample) + + with pytest.raises(ValueError, match=r"Sample is not a 2D array"): + qmc.geometric_discrepancy([1, 3]) + + sample = [[0, 0], [1, 1], [0.5, 0.5]] + with pytest.raises(ValueError, match=r"'toto' is not a valid ..."): + qmc.geometric_discrepancy(sample, method="toto") + + sample = np.array([[0, 0], [0, 0], [0, 1]]) + with pytest.warns(UserWarning, match="Sample contains duplicate points."): + qmc.geometric_discrepancy(sample) + + sample = np.array([[0.5, 0.5]]) + with pytest.raises(ValueError, match="Sample must contain at least two points"): + qmc.geometric_discrepancy(sample) + + def test_geometric_discrepancy(self): + sample = np.array([[0, 0], [1, 1]]) + assert_allclose(qmc.geometric_discrepancy(sample), np.sqrt(2)) + assert_allclose(qmc.geometric_discrepancy(sample, method="mst"), np.sqrt(2)) + + sample = np.array([[0, 0], [0, 1], [0.5, 1]]) + assert_allclose(qmc.geometric_discrepancy(sample), 0.5) + assert_allclose(qmc.geometric_discrepancy(sample, method="mst"), 0.75) + + sample = np.array([[0, 0], [0.25, 0.25], [1, 1]]) + assert_allclose(qmc.geometric_discrepancy(sample), np.sqrt(2) / 4) + assert_allclose(qmc.geometric_discrepancy(sample, method="mst"), np.sqrt(2) / 2) + assert_allclose(qmc.geometric_discrepancy(sample, metric="chebyshev"), 0.25) + assert_allclose( + qmc.geometric_discrepancy(sample, method="mst", metric="chebyshev"), 0.5 + ) + + rng = np.random.default_rng(191468432622931918890291693003068437394) + sample = qmc.LatinHypercube(d=3, seed=rng).random(50) + assert_allclose(qmc.geometric_discrepancy(sample), 0.05106012076093356) + assert_allclose( + qmc.geometric_discrepancy(sample, method='mst'), 0.19704396643366182 + ) + + @pytest.mark.xfail( + reason="minimum_spanning_tree ignores zero distances (#18892)", + strict=True, + ) + def test_geometric_discrepancy_mst_with_zero_distances(self): + sample = np.array([[0, 0], [0, 0], [0, 1]]) + assert_allclose(qmc.geometric_discrepancy(sample, method='mst'), 0.5) + + def test_update_discrepancy(self): + # From Fang et al. Design and modeling for computer experiments, 2006 + space_1 = np.array([[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]) + space_1 = (2.0 * space_1 - 1.0) / (2.0 * 6.0) + + disc_init = qmc.discrepancy(space_1[:-1], iterative=True) + disc_iter = update_discrepancy(space_1[-1], space_1[:-1], disc_init) + + assert_allclose(disc_iter, 0.0081, atol=1e-4) + + # n QMCEngine: + if self.can_scramble: + return self.qmce(scramble=scramble, seed=seed, **kwargs) + else: + if scramble: + pytest.skip() + else: + return self.qmce(seed=seed, **kwargs) + + def reference(self, scramble: bool) -> np.ndarray: + return self.scramble_nd if scramble else self.unscramble_nd + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_0dim(self, scramble): + engine = self.engine(d=0, scramble=scramble) + sample = engine.random(4) + assert_array_equal(np.empty((4, 0)), sample) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_0sample(self, scramble): + engine = self.engine(d=2, scramble=scramble) + sample = engine.random(0) + assert_array_equal(np.empty((0, 2)), sample) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_1sample(self, scramble): + engine = self.engine(d=2, scramble=scramble) + sample = engine.random(1) + assert (1, 2) == sample.shape + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_bounds(self, scramble): + engine = self.engine(d=100, scramble=scramble) + sample = engine.random(512) + assert np.all(sample >= 0) + assert np.all(sample <= 1) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_sample(self, scramble): + ref_sample = self.reference(scramble=scramble) + engine = self.engine(d=2, scramble=scramble) + sample = engine.random(n=len(ref_sample)) + + assert_allclose(sample, ref_sample, atol=1e-1) + assert engine.num_generated == len(ref_sample) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_continuing(self, scramble): + engine = self.engine(d=2, scramble=scramble) + ref_sample = engine.random(n=8) + + engine = self.engine(d=2, scramble=scramble) + + n_half = len(ref_sample) // 2 + + _ = engine.random(n=n_half) + sample = engine.random(n=n_half) + assert_allclose(sample, ref_sample[n_half:], atol=1e-1) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + @pytest.mark.parametrize( + "seed", + ( + 170382760648021597650530316304495310428, + np.random.default_rng(170382760648021597650530316304495310428), + None, + ), + ) + def test_reset(self, scramble, seed): + engine = self.engine(d=2, scramble=scramble, seed=seed) + ref_sample = engine.random(n=8) + + engine.reset() + assert engine.num_generated == 0 + + sample = engine.random(n=8) + assert_allclose(sample, ref_sample) + + @pytest.mark.parametrize("scramble", scramble, ids=ids) + def test_fast_forward(self, scramble): + engine = self.engine(d=2, scramble=scramble) + ref_sample = engine.random(n=8) + + engine = self.engine(d=2, scramble=scramble) + + engine.fast_forward(4) + sample = engine.random(n=4) + + assert_allclose(sample, ref_sample[4:], atol=1e-1) + + # alternate fast forwarding with sampling + engine.reset() + even_draws = [] + for i in range(8): + if i % 2 == 0: + even_draws.append(engine.random()) + else: + engine.fast_forward(1) + assert_allclose( + ref_sample[[i for i in range(8) if i % 2 == 0]], + np.concatenate(even_draws), + atol=1e-5 + ) + + @pytest.mark.parametrize("scramble", [True]) + def test_distribution(self, scramble): + d = 50 + engine = self.engine(d=d, scramble=scramble) + sample = engine.random(1024) + assert_allclose( + np.mean(sample, axis=0), np.repeat(0.5, d), atol=1e-2 + ) + assert_allclose( + np.percentile(sample, 25, axis=0), np.repeat(0.25, d), atol=1e-2 + ) + assert_allclose( + np.percentile(sample, 75, axis=0), np.repeat(0.75, d), atol=1e-2 + ) + + def test_raises_optimizer(self): + message = r"'toto' is not a valid optimization method" + with pytest.raises(ValueError, match=message): + self.engine(d=1, scramble=False, optimization="toto") + + @pytest.mark.parametrize( + "optimization,metric", + [ + ("random-CD", qmc.discrepancy), + ("lloyd", lambda sample: -_l1_norm(sample))] + ) + def test_optimizers(self, optimization, metric): + engine = self.engine(d=2, scramble=False) + sample_ref = engine.random(n=64) + metric_ref = metric(sample_ref) + + optimal_ = self.engine(d=2, scramble=False, optimization=optimization) + sample_ = optimal_.random(n=64) + metric_ = metric(sample_) + + assert metric_ < metric_ref + + def test_consume_prng_state(self): + rng = np.random.default_rng(0xa29cabb11cfdf44ff6cac8bec254c2a0) + sample = [] + for i in range(3): + engine = self.engine(d=2, scramble=True, seed=rng) + sample.append(engine.random(4)) + + with pytest.raises(AssertionError, match="Arrays are not equal"): + assert_equal(sample[0], sample[1]) + with pytest.raises(AssertionError, match="Arrays are not equal"): + assert_equal(sample[0], sample[2]) + + +class TestHalton(QMCEngineTests): + qmce = qmc.Halton + can_scramble = True + # theoretical values known from Van der Corput + unscramble_nd = np.array([[0, 0], [1 / 2, 1 / 3], + [1 / 4, 2 / 3], [3 / 4, 1 / 9], + [1 / 8, 4 / 9], [5 / 8, 7 / 9], + [3 / 8, 2 / 9], [7 / 8, 5 / 9]]) + # theoretical values unknown: convergence properties checked + scramble_nd = np.array([[0.50246036, 0.93382481], + [0.00246036, 0.26715815], + [0.75246036, 0.60049148], + [0.25246036, 0.8227137 ], + [0.62746036, 0.15604704], + [0.12746036, 0.48938037], + [0.87746036, 0.71160259], + [0.37746036, 0.04493592]]) + + def test_workers(self): + ref_sample = self.reference(scramble=True) + engine = self.engine(d=2, scramble=True) + sample = engine.random(n=len(ref_sample), workers=8) + + assert_allclose(sample, ref_sample, atol=1e-3) + + # worker + integers + engine.reset() + ref_sample = engine.integers(10) + engine.reset() + sample = engine.integers(10, workers=8) + assert_equal(sample, ref_sample) + + +class TestLHS(QMCEngineTests): + qmce = qmc.LatinHypercube + can_scramble = True + + def test_continuing(self, *args): + pytest.skip("Not applicable: not a sequence.") + + def test_fast_forward(self, *args): + pytest.skip("Not applicable: not a sequence.") + + def test_sample(self, *args): + pytest.skip("Not applicable: the value of reference sample is" + " implementation dependent.") + + @pytest.mark.parametrize("strength", [1, 2]) + @pytest.mark.parametrize("scramble", [False, True]) + @pytest.mark.parametrize("optimization", [None, "random-CD"]) + def test_sample_stratified(self, optimization, scramble, strength): + seed = np.random.default_rng(37511836202578819870665127532742111260) + p = 5 + n = p**2 + d = 6 + + engine = qmc.LatinHypercube(d=d, scramble=scramble, + strength=strength, + optimization=optimization, + seed=seed) + sample = engine.random(n=n) + assert sample.shape == (n, d) + assert engine.num_generated == n + + # centering stratifies samples in the middle of equal segments: + # * inter-sample distance is constant in 1D sub-projections + # * after ordering, columns are equal + expected1d = (np.arange(n) + 0.5) / n + expected = np.broadcast_to(expected1d, (d, n)).T + assert np.any(sample != expected) + + sorted_sample = np.sort(sample, axis=0) + tol = 0.5 / n if scramble else 0 + + assert_allclose(sorted_sample, expected, atol=tol) + assert np.any(sample - expected > tol) + + if strength == 2 and optimization is None: + unique_elements = np.arange(p) + desired = set(product(unique_elements, unique_elements)) + + for i, j in combinations(range(engine.d), 2): + samples_2d = sample[:, [i, j]] + res = (samples_2d * p).astype(int) + res_set = {tuple(row) for row in res} + assert_equal(res_set, desired) + + def test_optimizer_1d(self): + # discrepancy measures are invariant under permuting factors and runs + engine = self.engine(d=1, scramble=False) + sample_ref = engine.random(n=64) + + optimal_ = self.engine(d=1, scramble=False, optimization="random-CD") + sample_ = optimal_.random(n=64) + + assert_array_equal(sample_ref, sample_) + + def test_raises(self): + message = r"not a valid strength" + with pytest.raises(ValueError, match=message): + qmc.LatinHypercube(1, strength=3) + + message = r"n is not the square of a prime number" + with pytest.raises(ValueError, match=message): + engine = qmc.LatinHypercube(d=2, strength=2) + engine.random(16) + + message = r"n is not the square of a prime number" + with pytest.raises(ValueError, match=message): + engine = qmc.LatinHypercube(d=2, strength=2) + engine.random(5) # because int(sqrt(5)) would result in 2 + + message = r"n is too small for d" + with pytest.raises(ValueError, match=message): + engine = qmc.LatinHypercube(d=5, strength=2) + engine.random(9) + + +class TestSobol(QMCEngineTests): + qmce = qmc.Sobol + can_scramble = True + # theoretical values from Joe Kuo2010 + unscramble_nd = np.array([[0., 0.], + [0.5, 0.5], + [0.75, 0.25], + [0.25, 0.75], + [0.375, 0.375], + [0.875, 0.875], + [0.625, 0.125], + [0.125, 0.625]]) + + # theoretical values unknown: convergence properties checked + scramble_nd = np.array([[0.25331921, 0.41371179], + [0.8654213, 0.9821167], + [0.70097554, 0.03664616], + [0.18027647, 0.60895735], + [0.10521339, 0.21897069], + [0.53019685, 0.66619033], + [0.91122276, 0.34580743], + [0.45337471, 0.78912079]]) + + def test_warning(self): + with pytest.warns(UserWarning, match=r"The balance properties of " + r"Sobol' points"): + engine = qmc.Sobol(1) + engine.random(10) + + def test_random_base2(self): + engine = qmc.Sobol(2, scramble=False) + sample = engine.random_base2(2) + assert_array_equal(self.unscramble_nd[:4], sample) + + # resampling still having N=2**n + sample = engine.random_base2(2) + assert_array_equal(self.unscramble_nd[4:8], sample) + + # resampling again but leading to N!=2**n + with pytest.raises(ValueError, match=r"The balance properties of " + r"Sobol' points"): + engine.random_base2(2) + + def test_raise(self): + with pytest.raises(ValueError, match=r"Maximum supported " + r"dimensionality"): + qmc.Sobol(qmc.Sobol.MAXDIM + 1) + + with pytest.raises(ValueError, match=r"Maximum supported " + r"'bits' is 64"): + qmc.Sobol(1, bits=65) + + def test_high_dim(self): + engine = qmc.Sobol(1111, scramble=False) + count1 = Counter(engine.random().flatten().tolist()) + count2 = Counter(engine.random().flatten().tolist()) + assert_equal(count1, Counter({0.0: 1111})) + assert_equal(count2, Counter({0.5: 1111})) + + @pytest.mark.parametrize("bits", [2, 3]) + def test_bits(self, bits): + engine = qmc.Sobol(2, scramble=False, bits=bits) + ns = 2**bits + sample = engine.random(ns) + assert_array_equal(self.unscramble_nd[:ns], sample) + + with pytest.raises(ValueError, match="increasing `bits`"): + engine.random() + + def test_64bits(self): + engine = qmc.Sobol(2, scramble=False, bits=64) + sample = engine.random(8) + assert_array_equal(self.unscramble_nd, sample) + + +class TestPoisson(QMCEngineTests): + qmce = qmc.PoissonDisk + can_scramble = False + + def test_bounds(self, *args): + pytest.skip("Too costly in memory.") + + def test_fast_forward(self, *args): + pytest.skip("Not applicable: recursive process.") + + def test_sample(self, *args): + pytest.skip("Not applicable: the value of reference sample is" + " implementation dependent.") + + def test_continuing(self, *args): + # can continue a sampling, but will not preserve the same order + # because candidates are lost, so we will not select the same center + radius = 0.05 + ns = 6 + engine = self.engine(d=2, radius=radius, scramble=False) + + sample_init = engine.random(n=ns) + assert len(sample_init) <= ns + assert l2_norm(sample_init) >= radius + + sample_continued = engine.random(n=ns) + assert len(sample_continued) <= ns + assert l2_norm(sample_continued) >= radius + + sample = np.concatenate([sample_init, sample_continued], axis=0) + assert len(sample) <= ns * 2 + assert l2_norm(sample) >= radius + + def test_mindist(self): + rng = np.random.default_rng(132074951149370773672162394161442690287) + ns = 50 + + low, high = 0.08, 0.2 + radii = (high - low) * rng.random(5) + low + + dimensions = [1, 3, 4] + hypersphere_methods = ["volume", "surface"] + + gen = product(dimensions, radii, hypersphere_methods) + + for d, radius, hypersphere in gen: + engine = self.qmce( + d=d, radius=radius, hypersphere=hypersphere, seed=rng + ) + sample = engine.random(ns) + + assert len(sample) <= ns + assert l2_norm(sample) >= radius + + def test_fill_space(self): + radius = 0.2 + engine = self.qmce(d=2, radius=radius) + + sample = engine.fill_space() + # circle packing problem is np complex + assert l2_norm(sample) >= radius + + def test_raises(self): + message = r"'toto' is not a valid hypersphere sampling" + with pytest.raises(ValueError, match=message): + qmc.PoissonDisk(1, hypersphere="toto") + + +class TestMultinomialQMC: + def test_validations(self): + # negative Ps + p = np.array([0.12, 0.26, -0.05, 0.35, 0.22]) + with pytest.raises(ValueError, match=r"Elements of pvals must " + r"be non-negative."): + qmc.MultinomialQMC(p, n_trials=10) + + # sum of P too large + p = np.array([0.12, 0.26, 0.1, 0.35, 0.22]) + message = r"Elements of pvals must sum to 1." + with pytest.raises(ValueError, match=message): + qmc.MultinomialQMC(p, n_trials=10) + + p = np.array([0.12, 0.26, 0.05, 0.35, 0.22]) + + message = r"Dimension of `engine` must be 1." + with pytest.raises(ValueError, match=message): + qmc.MultinomialQMC(p, n_trials=10, engine=qmc.Sobol(d=2)) + + message = r"`engine` must be an instance of..." + with pytest.raises(ValueError, match=message): + qmc.MultinomialQMC(p, n_trials=10, engine=np.random.default_rng()) + + @pytest.mark.filterwarnings('ignore::UserWarning') + def test_MultinomialBasicDraw(self): + seed = np.random.default_rng(6955663962957011631562466584467607969) + p = np.array([0.12, 0.26, 0.05, 0.35, 0.22]) + n_trials = 100 + expected = np.atleast_2d(n_trials * p).astype(int) + engine = qmc.MultinomialQMC(p, n_trials=n_trials, seed=seed) + assert_allclose(engine.random(1), expected, atol=1) + + def test_MultinomialDistribution(self): + seed = np.random.default_rng(77797854505813727292048130876699859000) + p = np.array([0.12, 0.26, 0.05, 0.35, 0.22]) + engine = qmc.MultinomialQMC(p, n_trials=8192, seed=seed) + draws = engine.random(1) + assert_allclose(draws / np.sum(draws), np.atleast_2d(p), atol=1e-4) + + def test_FindIndex(self): + p_cumulative = np.array([0.1, 0.4, 0.45, 0.6, 0.75, 0.9, 0.99, 1.0]) + size = len(p_cumulative) + assert_equal(_test_find_index(p_cumulative, size, 0.0), 0) + assert_equal(_test_find_index(p_cumulative, size, 0.4), 2) + assert_equal(_test_find_index(p_cumulative, size, 0.44999), 2) + assert_equal(_test_find_index(p_cumulative, size, 0.45001), 3) + assert_equal(_test_find_index(p_cumulative, size, 1.0), size - 1) + + @pytest.mark.filterwarnings('ignore::UserWarning') + def test_other_engine(self): + # same as test_MultinomialBasicDraw with different engine + seed = np.random.default_rng(283753519042773243071753037669078065412) + p = np.array([0.12, 0.26, 0.05, 0.35, 0.22]) + n_trials = 100 + expected = np.atleast_2d(n_trials * p).astype(int) + base_engine = qmc.Sobol(1, scramble=True, seed=seed) + engine = qmc.MultinomialQMC(p, n_trials=n_trials, engine=base_engine, + seed=seed) + assert_allclose(engine.random(1), expected, atol=1) + + +class TestNormalQMC: + def test_NormalQMC(self): + # d = 1 + engine = qmc.MultivariateNormalQMC(mean=np.zeros(1)) + samples = engine.random() + assert_equal(samples.shape, (1, 1)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 1)) + # d = 2 + engine = qmc.MultivariateNormalQMC(mean=np.zeros(2)) + samples = engine.random() + assert_equal(samples.shape, (1, 2)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 2)) + + def test_NormalQMCInvTransform(self): + # d = 1 + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(1), inv_transform=True) + samples = engine.random() + assert_equal(samples.shape, (1, 1)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 1)) + # d = 2 + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(2), inv_transform=True) + samples = engine.random() + assert_equal(samples.shape, (1, 2)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 2)) + + def test_NormalQMCSeeded(self): + # test even dimension + seed = np.random.default_rng(274600237797326520096085022671371676017) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(2), inv_transform=False, seed=seed) + samples = engine.random(n=2) + samples_expected = np.array([[-0.932001, -0.522923], + [-1.477655, 0.846851]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + # test odd dimension + seed = np.random.default_rng(274600237797326520096085022671371676017) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(3), inv_transform=False, seed=seed) + samples = engine.random(n=2) + samples_expected = np.array([[-0.932001, -0.522923, 0.036578], + [-1.778011, 0.912428, -0.065421]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + # same test with another engine + seed = np.random.default_rng(274600237797326520096085022671371676017) + base_engine = qmc.Sobol(4, scramble=True, seed=seed) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(3), inv_transform=False, + engine=base_engine, seed=seed + ) + samples = engine.random(n=2) + samples_expected = np.array([[-0.932001, -0.522923, 0.036578], + [-1.778011, 0.912428, -0.065421]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + def test_NormalQMCSeededInvTransform(self): + # test even dimension + seed = np.random.default_rng(288527772707286126646493545351112463929) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(2), seed=seed, inv_transform=True) + samples = engine.random(n=2) + samples_expected = np.array([[-0.913237, -0.964026], + [0.255904, 0.003068]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + # test odd dimension + seed = np.random.default_rng(288527772707286126646493545351112463929) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(3), seed=seed, inv_transform=True) + samples = engine.random(n=2) + samples_expected = np.array([[-0.913237, -0.964026, 0.355501], + [0.699261, 2.90213 , -0.6418]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + def test_other_engine(self): + for d in (0, 1, 2): + base_engine = qmc.Sobol(d=d, scramble=False) + engine = qmc.MultivariateNormalQMC(mean=np.zeros(d), + engine=base_engine, + inv_transform=True) + samples = engine.random() + assert_equal(samples.shape, (1, d)) + + def test_NormalQMCShapiro(self): + rng = np.random.default_rng(13242) + engine = qmc.MultivariateNormalQMC(mean=np.zeros(2), seed=rng) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0)) < 1e-2) + assert all(np.abs(samples.std(axis=0) - 1) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # make sure samples are uncorrelated + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1]) < 1e-2 + + def test_NormalQMCShapiroInvTransform(self): + rng = np.random.default_rng(32344554) + engine = qmc.MultivariateNormalQMC( + mean=np.zeros(2), inv_transform=True, seed=rng) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0)) < 1e-2) + assert all(np.abs(samples.std(axis=0) - 1) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # make sure samples are uncorrelated + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1]) < 1e-2 + + +class TestMultivariateNormalQMC: + + def test_validations(self): + + message = r"Dimension of `engine` must be consistent" + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0], engine=qmc.Sobol(d=2)) + + message = r"Dimension of `engine` must be consistent" + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0, 0, 0], engine=qmc.Sobol(d=4)) + + message = r"`engine` must be an instance of..." + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0, 0], engine=np.random.default_rng()) + + message = r"Covariance matrix not PSD." + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0, 0], [[1, 2], [2, 1]]) + + message = r"Covariance matrix is not symmetric." + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0, 0], [[1, 0], [2, 1]]) + + message = r"Dimension mismatch between mean and covariance." + with pytest.raises(ValueError, match=message): + qmc.MultivariateNormalQMC([0], [[1, 0], [0, 1]]) + + def test_MultivariateNormalQMCNonPD(self): + # try with non-pd but psd cov; should work + engine = qmc.MultivariateNormalQMC( + [0, 0, 0], [[1, 0, 1], [0, 1, 1], [1, 1, 2]], + ) + assert engine._corr_matrix is not None + + def test_MultivariateNormalQMC(self): + # d = 1 scalar + engine = qmc.MultivariateNormalQMC(mean=0, cov=5) + samples = engine.random() + assert_equal(samples.shape, (1, 1)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 1)) + + # d = 2 list + engine = qmc.MultivariateNormalQMC(mean=[0, 1], cov=[[1, 0], [0, 1]]) + samples = engine.random() + assert_equal(samples.shape, (1, 2)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 2)) + + # d = 3 np.array + mean = np.array([0, 1, 2]) + cov = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + engine = qmc.MultivariateNormalQMC(mean, cov) + samples = engine.random() + assert_equal(samples.shape, (1, 3)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 3)) + + def test_MultivariateNormalQMCInvTransform(self): + # d = 1 scalar + engine = qmc.MultivariateNormalQMC(mean=0, cov=5, inv_transform=True) + samples = engine.random() + assert_equal(samples.shape, (1, 1)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 1)) + + # d = 2 list + engine = qmc.MultivariateNormalQMC( + mean=[0, 1], cov=[[1, 0], [0, 1]], inv_transform=True, + ) + samples = engine.random() + assert_equal(samples.shape, (1, 2)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 2)) + + # d = 3 np.array + mean = np.array([0, 1, 2]) + cov = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + engine = qmc.MultivariateNormalQMC(mean, cov, inv_transform=True) + samples = engine.random() + assert_equal(samples.shape, (1, 3)) + samples = engine.random(n=5) + assert_equal(samples.shape, (5, 3)) + + def test_MultivariateNormalQMCSeeded(self): + # test even dimension + rng = np.random.default_rng(180182791534511062935571481899241825000) + a = rng.standard_normal((2, 2)) + A = a @ a.transpose() + np.diag(rng.random(2)) + engine = qmc.MultivariateNormalQMC(np.array([0, 0]), A, + inv_transform=False, seed=rng) + samples = engine.random(n=2) + samples_expected = np.array([[-0.64419, -0.882413], + [0.837199, 2.045301]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + # test odd dimension + rng = np.random.default_rng(180182791534511062935571481899241825000) + a = rng.standard_normal((3, 3)) + A = a @ a.transpose() + np.diag(rng.random(3)) + engine = qmc.MultivariateNormalQMC(np.array([0, 0, 0]), A, + inv_transform=False, seed=rng) + samples = engine.random(n=2) + samples_expected = np.array([[-0.693853, -1.265338, -0.088024], + [1.620193, 2.679222, 0.457343]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + def test_MultivariateNormalQMCSeededInvTransform(self): + # test even dimension + rng = np.random.default_rng(224125808928297329711992996940871155974) + a = rng.standard_normal((2, 2)) + A = a @ a.transpose() + np.diag(rng.random(2)) + engine = qmc.MultivariateNormalQMC( + np.array([0, 0]), A, seed=rng, inv_transform=True + ) + samples = engine.random(n=2) + samples_expected = np.array([[0.682171, -3.114233], + [-0.098463, 0.668069]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + # test odd dimension + rng = np.random.default_rng(224125808928297329711992996940871155974) + a = rng.standard_normal((3, 3)) + A = a @ a.transpose() + np.diag(rng.random(3)) + engine = qmc.MultivariateNormalQMC( + np.array([0, 0, 0]), A, seed=rng, inv_transform=True + ) + samples = engine.random(n=2) + samples_expected = np.array([[0.988061, -1.644089, -0.877035], + [-1.771731, 1.096988, 2.024744]]) + assert_allclose(samples, samples_expected, atol=1e-4) + + def test_MultivariateNormalQMCShapiro(self): + # test the standard case + seed = np.random.default_rng(188960007281846377164494575845971640) + engine = qmc.MultivariateNormalQMC( + mean=[0, 0], cov=[[1, 0], [0, 1]], seed=seed + ) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0)) < 1e-2) + assert all(np.abs(samples.std(axis=0) - 1) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # make sure samples are uncorrelated + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1]) < 1e-2 + + # test the correlated, non-zero mean case + engine = qmc.MultivariateNormalQMC( + mean=[1.0, 2.0], cov=[[1.5, 0.5], [0.5, 1.5]], seed=seed + ) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0) - [1, 2]) < 1e-2) + assert all(np.abs(samples.std(axis=0) - np.sqrt(1.5)) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # check covariance + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1] - 0.5) < 1e-2 + + def test_MultivariateNormalQMCShapiroInvTransform(self): + # test the standard case + seed = np.random.default_rng(200089821034563288698994840831440331329) + engine = qmc.MultivariateNormalQMC( + mean=[0, 0], cov=[[1, 0], [0, 1]], seed=seed, inv_transform=True + ) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0)) < 1e-2) + assert all(np.abs(samples.std(axis=0) - 1) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # make sure samples are uncorrelated + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1]) < 1e-2 + + # test the correlated, non-zero mean case + engine = qmc.MultivariateNormalQMC( + mean=[1.0, 2.0], + cov=[[1.5, 0.5], [0.5, 1.5]], + seed=seed, + inv_transform=True, + ) + samples = engine.random(n=256) + assert all(np.abs(samples.mean(axis=0) - [1, 2]) < 1e-2) + assert all(np.abs(samples.std(axis=0) - np.sqrt(1.5)) < 1e-2) + # perform Shapiro-Wilk test for normality + for i in (0, 1): + _, pval = shapiro(samples[:, i]) + assert pval > 0.9 + # check covariance + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1] - 0.5) < 1e-2 + + def test_MultivariateNormalQMCDegenerate(self): + # X, Y iid standard Normal and Z = X + Y, random vector (X, Y, Z) + seed = np.random.default_rng(16320637417581448357869821654290448620) + engine = qmc.MultivariateNormalQMC( + mean=[0.0, 0.0, 0.0], + cov=[[1.0, 0.0, 1.0], [0.0, 1.0, 1.0], [1.0, 1.0, 2.0]], + seed=seed, + ) + samples = engine.random(n=512) + assert all(np.abs(samples.mean(axis=0)) < 1e-2) + assert np.abs(np.std(samples[:, 0]) - 1) < 1e-2 + assert np.abs(np.std(samples[:, 1]) - 1) < 1e-2 + assert np.abs(np.std(samples[:, 2]) - np.sqrt(2)) < 1e-2 + for i in (0, 1, 2): + _, pval = shapiro(samples[:, i]) + assert pval > 0.8 + cov = np.cov(samples.transpose()) + assert np.abs(cov[0, 1]) < 1e-2 + assert np.abs(cov[0, 2] - 1) < 1e-2 + # check to see if X + Y = Z almost exactly + assert all(np.abs(samples[:, 0] + samples[:, 1] - samples[:, 2]) + < 1e-5) + + +class TestLloyd: + def test_lloyd(self): + # quite sensible seed as it can go up before going further down + rng = np.random.RandomState(1809831) + sample = rng.uniform(0, 1, size=(128, 2)) + base_l1 = _l1_norm(sample) + base_l2 = l2_norm(sample) + + for _ in range(4): + sample_lloyd = _lloyd_centroidal_voronoi_tessellation( + sample, maxiter=1, + ) + curr_l1 = _l1_norm(sample_lloyd) + curr_l2 = l2_norm(sample_lloyd) + + # higher is better for the distance measures + assert base_l1 < curr_l1 + assert base_l2 < curr_l2 + + base_l1 = curr_l1 + base_l2 = curr_l2 + + sample = sample_lloyd + + def test_lloyd_non_mutating(self): + """ + Verify that the input samples are not mutated in place and that they do + not share memory with the output. + """ + sample_orig = np.array([[0.1, 0.1], + [0.1, 0.2], + [0.2, 0.1], + [0.2, 0.2]]) + sample_copy = sample_orig.copy() + new_sample = _lloyd_centroidal_voronoi_tessellation( + sample=sample_orig + ) + assert_allclose(sample_orig, sample_copy) + assert not np.may_share_memory(sample_orig, new_sample) + + def test_lloyd_errors(self): + with pytest.raises(ValueError, match=r"`sample` is not a 2D array"): + sample = [0, 1, 0.5] + _lloyd_centroidal_voronoi_tessellation(sample) + + msg = r"`sample` dimension is not >= 2" + with pytest.raises(ValueError, match=msg): + sample = [[0], [0.4], [1]] + _lloyd_centroidal_voronoi_tessellation(sample) + + msg = r"`sample` is not in unit hypercube" + with pytest.raises(ValueError, match=msg): + sample = [[-1.1, 0], [0.1, 0.4], [1, 2]] + _lloyd_centroidal_voronoi_tessellation(sample) + + +# mindist +def l2_norm(sample): + return distance.pdist(sample).min() diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_rank.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_rank.py new file mode 100644 index 0000000000000000000000000000000000000000..2d65902425fd2ac808299fe3ef4bbb8c05b260c6 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_rank.py @@ -0,0 +1,336 @@ +import numpy as np +from numpy.testing import assert_equal, assert_array_equal +import pytest + +from scipy.stats import rankdata, tiecorrect +from scipy._lib._util import np_long + + +class TestTieCorrect: + + def test_empty(self): + """An empty array requires no correction, should return 1.0.""" + ranks = np.array([], dtype=np.float64) + c = tiecorrect(ranks) + assert_equal(c, 1.0) + + def test_one(self): + """A single element requires no correction, should return 1.0.""" + ranks = np.array([1.0], dtype=np.float64) + c = tiecorrect(ranks) + assert_equal(c, 1.0) + + def test_no_correction(self): + """Arrays with no ties require no correction.""" + ranks = np.arange(2.0) + c = tiecorrect(ranks) + assert_equal(c, 1.0) + ranks = np.arange(3.0) + c = tiecorrect(ranks) + assert_equal(c, 1.0) + + def test_basic(self): + """Check a few basic examples of the tie correction factor.""" + # One tie of two elements + ranks = np.array([1.0, 2.5, 2.5]) + c = tiecorrect(ranks) + T = 2.0 + N = ranks.size + expected = 1.0 - (T**3 - T) / (N**3 - N) + assert_equal(c, expected) + + # One tie of two elements (same as above, but tie is not at the end) + ranks = np.array([1.5, 1.5, 3.0]) + c = tiecorrect(ranks) + T = 2.0 + N = ranks.size + expected = 1.0 - (T**3 - T) / (N**3 - N) + assert_equal(c, expected) + + # One tie of three elements + ranks = np.array([1.0, 3.0, 3.0, 3.0]) + c = tiecorrect(ranks) + T = 3.0 + N = ranks.size + expected = 1.0 - (T**3 - T) / (N**3 - N) + assert_equal(c, expected) + + # Two ties, lengths 2 and 3. + ranks = np.array([1.5, 1.5, 4.0, 4.0, 4.0]) + c = tiecorrect(ranks) + T1 = 2.0 + T2 = 3.0 + N = ranks.size + expected = 1.0 - ((T1**3 - T1) + (T2**3 - T2)) / (N**3 - N) + assert_equal(c, expected) + + def test_overflow(self): + ntie, k = 2000, 5 + a = np.repeat(np.arange(k), ntie) + n = a.size # ntie * k + out = tiecorrect(rankdata(a)) + assert_equal(out, 1.0 - k * (ntie**3 - ntie) / float(n**3 - n)) + + +class TestRankData: + + def test_empty(self): + """stats.rankdata([]) should return an empty array.""" + a = np.array([], dtype=int) + r = rankdata(a) + assert_array_equal(r, np.array([], dtype=np.float64)) + r = rankdata([]) + assert_array_equal(r, np.array([], dtype=np.float64)) + + @pytest.mark.parametrize("shape", [(0, 1, 2)]) + @pytest.mark.parametrize("axis", [None, *range(3)]) + def test_empty_multidim(self, shape, axis): + a = np.empty(shape, dtype=int) + r = rankdata(a, axis=axis) + expected_shape = (0,) if axis is None else shape + assert_equal(r.shape, expected_shape) + assert_equal(r.dtype, np.float64) + + def test_one(self): + """Check stats.rankdata with an array of length 1.""" + data = [100] + a = np.array(data, dtype=int) + r = rankdata(a) + assert_array_equal(r, np.array([1.0], dtype=np.float64)) + r = rankdata(data) + assert_array_equal(r, np.array([1.0], dtype=np.float64)) + + def test_basic(self): + """Basic tests of stats.rankdata.""" + data = [100, 10, 50] + expected = np.array([3.0, 1.0, 2.0], dtype=np.float64) + a = np.array(data, dtype=int) + r = rankdata(a) + assert_array_equal(r, expected) + r = rankdata(data) + assert_array_equal(r, expected) + + data = [40, 10, 30, 10, 50] + expected = np.array([4.0, 1.5, 3.0, 1.5, 5.0], dtype=np.float64) + a = np.array(data, dtype=int) + r = rankdata(a) + assert_array_equal(r, expected) + r = rankdata(data) + assert_array_equal(r, expected) + + data = [20, 20, 20, 10, 10, 10] + expected = np.array([5.0, 5.0, 5.0, 2.0, 2.0, 2.0], dtype=np.float64) + a = np.array(data, dtype=int) + r = rankdata(a) + assert_array_equal(r, expected) + r = rankdata(data) + assert_array_equal(r, expected) + # The docstring states explicitly that the argument is flattened. + a2d = a.reshape(2, 3) + r = rankdata(a2d) + assert_array_equal(r, expected) + + def test_rankdata_object_string(self): + + def min_rank(a): + return [1 + sum(i < j for i in a) for j in a] + + def max_rank(a): + return [sum(i <= j for i in a) for j in a] + + def ordinal_rank(a): + return min_rank([(x, i) for i, x in enumerate(a)]) + + def average_rank(a): + return [(i + j) / 2.0 for i, j in zip(min_rank(a), max_rank(a))] + + def dense_rank(a): + b = np.unique(a) + return [1 + sum(i < j for i in b) for j in a] + + rankf = dict(min=min_rank, max=max_rank, ordinal=ordinal_rank, + average=average_rank, dense=dense_rank) + + def check_ranks(a): + for method in 'min', 'max', 'dense', 'ordinal', 'average': + out = rankdata(a, method=method) + assert_array_equal(out, rankf[method](a)) + + val = ['foo', 'bar', 'qux', 'xyz', 'abc', 'efg', 'ace', 'qwe', 'qaz'] + check_ranks(np.random.choice(val, 200)) + check_ranks(np.random.choice(val, 200).astype('object')) + + val = np.array([0, 1, 2, 2.718, 3, 3.141], dtype='object') + check_ranks(np.random.choice(val, 200).astype('object')) + + def test_large_int(self): + data = np.array([2**60, 2**60+1], dtype=np.uint64) + r = rankdata(data) + assert_array_equal(r, [1.0, 2.0]) + + data = np.array([2**60, 2**60+1], dtype=np.int64) + r = rankdata(data) + assert_array_equal(r, [1.0, 2.0]) + + data = np.array([2**60, -2**60+1], dtype=np.int64) + r = rankdata(data) + assert_array_equal(r, [2.0, 1.0]) + + def test_big_tie(self): + for n in [10000, 100000, 1000000]: + data = np.ones(n, dtype=int) + r = rankdata(data) + expected_rank = 0.5 * (n + 1) + assert_array_equal(r, expected_rank * data, + "test failed with n=%d" % n) + + def test_axis(self): + data = [[0, 2, 1], + [4, 2, 2]] + expected0 = [[1., 1.5, 1.], + [2., 1.5, 2.]] + r0 = rankdata(data, axis=0) + assert_array_equal(r0, expected0) + expected1 = [[1., 3., 2.], + [3., 1.5, 1.5]] + r1 = rankdata(data, axis=1) + assert_array_equal(r1, expected1) + + methods = ["average", "min", "max", "dense", "ordinal"] + dtypes = [np.float64] + [np_long]*4 + + @pytest.mark.parametrize("axis", [0, 1]) + @pytest.mark.parametrize("method, dtype", zip(methods, dtypes)) + def test_size_0_axis(self, axis, method, dtype): + shape = (3, 0) + data = np.zeros(shape) + r = rankdata(data, method=method, axis=axis) + assert_equal(r.shape, shape) + assert_equal(r.dtype, dtype) + + @pytest.mark.parametrize('axis', range(3)) + @pytest.mark.parametrize('method', methods) + def test_nan_policy_omit_3d(self, axis, method): + shape = (20, 21, 22) + rng = np.random.RandomState(23983242) + + a = rng.random(size=shape) + i = rng.random(size=shape) < 0.4 + j = rng.random(size=shape) < 0.1 + k = rng.random(size=shape) < 0.1 + a[i] = np.nan + a[j] = -np.inf + a[k] - np.inf + + def rank_1d_omit(a, method): + out = np.zeros_like(a) + i = np.isnan(a) + a_compressed = a[~i] + res = rankdata(a_compressed, method) + out[~i] = res + out[i] = np.nan + return out + + def rank_omit(a, method, axis): + return np.apply_along_axis(lambda a: rank_1d_omit(a, method), + axis, a) + + res = rankdata(a, method, axis=axis, nan_policy='omit') + res0 = rank_omit(a, method, axis=axis) + + assert_array_equal(res, res0) + + def test_nan_policy_2d_axis_none(self): + # 2 2d-array test with axis=None + data = [[0, np.nan, 3], + [4, 2, np.nan], + [1, 2, 2]] + assert_array_equal(rankdata(data, axis=None, nan_policy='omit'), + [1., np.nan, 6., 7., 4., np.nan, 2., 4., 4.]) + assert_array_equal(rankdata(data, axis=None, nan_policy='propagate'), + [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, + np.nan, np.nan, np.nan]) + + def test_nan_policy_raise(self): + # 1 1d-array test + data = [0, 2, 3, -2, np.nan, np.nan] + with pytest.raises(ValueError, match="The input contains nan"): + rankdata(data, nan_policy='raise') + + # 2 2d-array test + data = [[0, np.nan, 3], + [4, 2, np.nan], + [np.nan, 2, 2]] + + with pytest.raises(ValueError, match="The input contains nan"): + rankdata(data, axis=0, nan_policy="raise") + + with pytest.raises(ValueError, match="The input contains nan"): + rankdata(data, axis=1, nan_policy="raise") + + def test_nan_policy_propagate(self): + # 1 1d-array test + data = [0, 2, 3, -2, np.nan, np.nan] + assert_array_equal(rankdata(data, nan_policy='propagate'), + [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]) + + # 2 2d-array test + data = [[0, np.nan, 3], + [4, 2, np.nan], + [1, 2, 2]] + assert_array_equal(rankdata(data, axis=0, nan_policy='propagate'), + [[1, np.nan, np.nan], + [3, np.nan, np.nan], + [2, np.nan, np.nan]]) + assert_array_equal(rankdata(data, axis=1, nan_policy='propagate'), + [[np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan], + [1, 2.5, 2.5]]) + + +_cases = ( + # values, method, expected + ([], 'average', []), + ([], 'min', []), + ([], 'max', []), + ([], 'dense', []), + ([], 'ordinal', []), + # + ([100], 'average', [1.0]), + ([100], 'min', [1.0]), + ([100], 'max', [1.0]), + ([100], 'dense', [1.0]), + ([100], 'ordinal', [1.0]), + # + ([100, 100, 100], 'average', [2.0, 2.0, 2.0]), + ([100, 100, 100], 'min', [1.0, 1.0, 1.0]), + ([100, 100, 100], 'max', [3.0, 3.0, 3.0]), + ([100, 100, 100], 'dense', [1.0, 1.0, 1.0]), + ([100, 100, 100], 'ordinal', [1.0, 2.0, 3.0]), + # + ([100, 300, 200], 'average', [1.0, 3.0, 2.0]), + ([100, 300, 200], 'min', [1.0, 3.0, 2.0]), + ([100, 300, 200], 'max', [1.0, 3.0, 2.0]), + ([100, 300, 200], 'dense', [1.0, 3.0, 2.0]), + ([100, 300, 200], 'ordinal', [1.0, 3.0, 2.0]), + # + ([100, 200, 300, 200], 'average', [1.0, 2.5, 4.0, 2.5]), + ([100, 200, 300, 200], 'min', [1.0, 2.0, 4.0, 2.0]), + ([100, 200, 300, 200], 'max', [1.0, 3.0, 4.0, 3.0]), + ([100, 200, 300, 200], 'dense', [1.0, 2.0, 3.0, 2.0]), + ([100, 200, 300, 200], 'ordinal', [1.0, 2.0, 4.0, 3.0]), + # + ([100, 200, 300, 200, 100], 'average', [1.5, 3.5, 5.0, 3.5, 1.5]), + ([100, 200, 300, 200, 100], 'min', [1.0, 3.0, 5.0, 3.0, 1.0]), + ([100, 200, 300, 200, 100], 'max', [2.0, 4.0, 5.0, 4.0, 2.0]), + ([100, 200, 300, 200, 100], 'dense', [1.0, 2.0, 3.0, 2.0, 1.0]), + ([100, 200, 300, 200, 100], 'ordinal', [1.0, 3.0, 5.0, 4.0, 2.0]), + # + ([10] * 30, 'ordinal', np.arange(1.0, 31.0)), +) + + +def test_cases(): + for values, method, expected in _cases: + r = rankdata(values, method=method) + assert_array_equal(r, expected) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_relative_risk.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_relative_risk.py new file mode 100644 index 0000000000000000000000000000000000000000..b75e64d929f319465b1f1d62af4fb2096c2ab2ac --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_relative_risk.py @@ -0,0 +1,95 @@ +import pytest +import numpy as np +from numpy.testing import assert_allclose, assert_equal +from scipy.stats.contingency import relative_risk + + +# Test just the calculation of the relative risk, including edge +# cases that result in a relative risk of 0, inf or nan. +@pytest.mark.parametrize( + 'exposed_cases, exposed_total, control_cases, control_total, expected_rr', + [(1, 4, 3, 8, 0.25 / 0.375), + (0, 10, 5, 20, 0), + (0, 10, 0, 20, np.nan), + (5, 15, 0, 20, np.inf)] +) +def test_relative_risk(exposed_cases, exposed_total, + control_cases, control_total, expected_rr): + result = relative_risk(exposed_cases, exposed_total, + control_cases, control_total) + assert_allclose(result.relative_risk, expected_rr, rtol=1e-13) + + +def test_relative_risk_confidence_interval(): + result = relative_risk(exposed_cases=16, exposed_total=128, + control_cases=24, control_total=256) + rr = result.relative_risk + ci = result.confidence_interval(confidence_level=0.95) + # The corresponding calculation in R using the epitools package. + # + # > library(epitools) + # > c <- matrix(c(232, 112, 24, 16), nrow=2) + # > result <- riskratio(c) + # > result$measure + # risk ratio with 95% C.I. + # Predictor estimate lower upper + # Exposed1 1.000000 NA NA + # Exposed2 1.333333 0.7347317 2.419628 + # + # The last line is the result that we want. + assert_allclose(rr, 4/3) + assert_allclose((ci.low, ci.high), (0.7347317, 2.419628), rtol=5e-7) + + +def test_relative_risk_ci_conflevel0(): + result = relative_risk(exposed_cases=4, exposed_total=12, + control_cases=5, control_total=30) + rr = result.relative_risk + assert_allclose(rr, 2.0, rtol=1e-14) + ci = result.confidence_interval(0) + assert_allclose((ci.low, ci.high), (2.0, 2.0), rtol=1e-12) + + +def test_relative_risk_ci_conflevel1(): + result = relative_risk(exposed_cases=4, exposed_total=12, + control_cases=5, control_total=30) + ci = result.confidence_interval(1) + assert_equal((ci.low, ci.high), (0, np.inf)) + + +def test_relative_risk_ci_edge_cases_00(): + result = relative_risk(exposed_cases=0, exposed_total=12, + control_cases=0, control_total=30) + assert_equal(result.relative_risk, np.nan) + ci = result.confidence_interval() + assert_equal((ci.low, ci.high), (np.nan, np.nan)) + + +def test_relative_risk_ci_edge_cases_01(): + result = relative_risk(exposed_cases=0, exposed_total=12, + control_cases=1, control_total=30) + assert_equal(result.relative_risk, 0) + ci = result.confidence_interval() + assert_equal((ci.low, ci.high), (0.0, np.nan)) + + +def test_relative_risk_ci_edge_cases_10(): + result = relative_risk(exposed_cases=1, exposed_total=12, + control_cases=0, control_total=30) + assert_equal(result.relative_risk, np.inf) + ci = result.confidence_interval() + assert_equal((ci.low, ci.high), (np.nan, np.inf)) + + +@pytest.mark.parametrize('ec, et, cc, ct', [(0, 0, 10, 20), + (-1, 10, 1, 5), + (1, 10, 0, 0), + (1, 10, -1, 4)]) +def test_relative_risk_bad_value(ec, et, cc, ct): + with pytest.raises(ValueError, match="must be an integer not less than"): + relative_risk(ec, et, cc, ct) + + +def test_relative_risk_bad_type(): + with pytest.raises(TypeError, match="must be an integer"): + relative_risk(1, 10, 2.0, 40) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_resampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_resampling.py new file mode 100644 index 0000000000000000000000000000000000000000..cdb216b61495272bdceca31ea1b3b804a26437ac --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_resampling.py @@ -0,0 +1,1748 @@ +import numpy as np +import pytest +from scipy.stats import bootstrap, monte_carlo_test, permutation_test +from numpy.testing import assert_allclose, assert_equal, suppress_warnings +from scipy import stats +from scipy import special +from .. import _resampling as _resampling +from scipy._lib._util import rng_integers +from scipy.optimize import root + + +def test_bootstrap_iv(): + + message = "`data` must be a sequence of samples." + with pytest.raises(ValueError, match=message): + bootstrap(1, np.mean) + + message = "`data` must contain at least one sample." + with pytest.raises(ValueError, match=message): + bootstrap(tuple(), np.mean) + + message = "each sample in `data` must contain two or more observations..." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3], [1]), np.mean) + + message = ("When `paired is True`, all samples must have the same length ") + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3], [1, 2, 3, 4]), np.mean, paired=True) + + message = "`vectorized` must be `True`, `False`, or `None`." + with pytest.raises(ValueError, match=message): + bootstrap(1, np.mean, vectorized='ekki') + + message = "`axis` must be an integer." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, axis=1.5) + + message = "could not convert string to float" + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, confidence_level='ni') + + message = "`n_resamples` must be a non-negative integer." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, n_resamples=-1000) + + message = "`n_resamples` must be a non-negative integer." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, n_resamples=1000.5) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, batch=-1000) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, batch=1000.5) + + message = "`method` must be in" + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, method='ekki') + + message = "`bootstrap_result` must have attribute `bootstrap_distribution'" + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, bootstrap_result=10) + + message = "Either `bootstrap_result.bootstrap_distribution.size`" + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, n_resamples=0) + + message = "'herring' cannot be used to seed a" + with pytest.raises(ValueError, match=message): + bootstrap(([1, 2, 3],), np.mean, random_state='herring') + + +@pytest.mark.parametrize("method", ['basic', 'percentile', 'BCa']) +@pytest.mark.parametrize("axis", [0, 1, 2]) +def test_bootstrap_batch(method, axis): + # for one-sample statistics, batch size shouldn't affect the result + np.random.seed(0) + + x = np.random.rand(10, 11, 12) + res1 = bootstrap((x,), np.mean, batch=None, method=method, + random_state=0, axis=axis, n_resamples=100) + res2 = bootstrap((x,), np.mean, batch=10, method=method, + random_state=0, axis=axis, n_resamples=100) + + assert_equal(res2.confidence_interval.low, res1.confidence_interval.low) + assert_equal(res2.confidence_interval.high, res1.confidence_interval.high) + assert_equal(res2.standard_error, res1.standard_error) + + +@pytest.mark.parametrize("method", ['basic', 'percentile', 'BCa']) +def test_bootstrap_paired(method): + # test that `paired` works as expected + np.random.seed(0) + n = 100 + x = np.random.rand(n) + y = np.random.rand(n) + + def my_statistic(x, y, axis=-1): + return ((x-y)**2).mean(axis=axis) + + def my_paired_statistic(i, axis=-1): + a = x[i] + b = y[i] + res = my_statistic(a, b) + return res + + i = np.arange(len(x)) + + res1 = bootstrap((i,), my_paired_statistic, random_state=0) + res2 = bootstrap((x, y), my_statistic, paired=True, random_state=0) + + assert_allclose(res1.confidence_interval, res2.confidence_interval) + assert_allclose(res1.standard_error, res2.standard_error) + + +@pytest.mark.parametrize("method", ['basic', 'percentile', 'BCa']) +@pytest.mark.parametrize("axis", [0, 1, 2]) +@pytest.mark.parametrize("paired", [True, False]) +def test_bootstrap_vectorized(method, axis, paired): + # test that paired is vectorized as expected: when samples are tiled, + # CI and standard_error of each axis-slice is the same as those of the + # original 1d sample + + np.random.seed(0) + + def my_statistic(x, y, z, axis=-1): + return x.mean(axis=axis) + y.mean(axis=axis) + z.mean(axis=axis) + + shape = 10, 11, 12 + n_samples = shape[axis] + + x = np.random.rand(n_samples) + y = np.random.rand(n_samples) + z = np.random.rand(n_samples) + res1 = bootstrap((x, y, z), my_statistic, paired=paired, method=method, + random_state=0, axis=0, n_resamples=100) + assert (res1.bootstrap_distribution.shape + == res1.standard_error.shape + (100,)) + + reshape = [1, 1, 1] + reshape[axis] = n_samples + x = np.broadcast_to(x.reshape(reshape), shape) + y = np.broadcast_to(y.reshape(reshape), shape) + z = np.broadcast_to(z.reshape(reshape), shape) + res2 = bootstrap((x, y, z), my_statistic, paired=paired, method=method, + random_state=0, axis=axis, n_resamples=100) + + assert_allclose(res2.confidence_interval.low, + res1.confidence_interval.low) + assert_allclose(res2.confidence_interval.high, + res1.confidence_interval.high) + assert_allclose(res2.standard_error, res1.standard_error) + + result_shape = list(shape) + result_shape.pop(axis) + + assert_equal(res2.confidence_interval.low.shape, result_shape) + assert_equal(res2.confidence_interval.high.shape, result_shape) + assert_equal(res2.standard_error.shape, result_shape) + + +@pytest.mark.xfail_on_32bit("MemoryError with BCa observed in CI") +@pytest.mark.parametrize("method", ['basic', 'percentile', 'BCa']) +def test_bootstrap_against_theory(method): + # based on https://www.statology.org/confidence-intervals-python/ + rng = np.random.default_rng(2442101192988600726) + data = stats.norm.rvs(loc=5, scale=2, size=5000, random_state=rng) + alpha = 0.95 + dist = stats.t(df=len(data)-1, loc=np.mean(data), scale=stats.sem(data)) + expected_interval = dist.interval(confidence=alpha) + expected_se = dist.std() + + config = dict(data=(data,), statistic=np.mean, n_resamples=5000, + method=method, random_state=rng) + res = bootstrap(**config, confidence_level=alpha) + assert_allclose(res.confidence_interval, expected_interval, rtol=5e-4) + assert_allclose(res.standard_error, expected_se, atol=3e-4) + + config.update(dict(n_resamples=0, bootstrap_result=res)) + res = bootstrap(**config, confidence_level=alpha, alternative='less') + assert_allclose(res.confidence_interval.high, dist.ppf(alpha), rtol=5e-4) + + config.update(dict(n_resamples=0, bootstrap_result=res)) + res = bootstrap(**config, confidence_level=alpha, alternative='greater') + assert_allclose(res.confidence_interval.low, dist.ppf(1-alpha), rtol=5e-4) + + +tests_R = {"basic": (23.77, 79.12), + "percentile": (28.86, 84.21), + "BCa": (32.31, 91.43)} + + +@pytest.mark.parametrize("method, expected", tests_R.items()) +def test_bootstrap_against_R(method, expected): + # Compare against R's "boot" library + # library(boot) + + # stat <- function (x, a) { + # mean(x[a]) + # } + + # x <- c(10, 12, 12.5, 12.5, 13.9, 15, 21, 22, + # 23, 34, 50, 81, 89, 121, 134, 213) + + # # Use a large value so we get a few significant digits for the CI. + # n = 1000000 + # bootresult = boot(x, stat, n) + # result <- boot.ci(bootresult) + # print(result) + x = np.array([10, 12, 12.5, 12.5, 13.9, 15, 21, 22, + 23, 34, 50, 81, 89, 121, 134, 213]) + res = bootstrap((x,), np.mean, n_resamples=1000000, method=method, + random_state=0) + assert_allclose(res.confidence_interval, expected, rtol=0.005) + + +tests_against_itself_1samp = {"basic": 1780, + "percentile": 1784, + "BCa": 1784} + + +def test_multisample_BCa_against_R(): + # Because bootstrap is stochastic, it's tricky to test against reference + # behavior. Here, we show that SciPy's BCa CI matches R wboot's BCa CI + # much more closely than the other SciPy CIs do. + + # arbitrary skewed data + x = [0.75859206, 0.5910282, -0.4419409, -0.36654601, + 0.34955357, -1.38835871, 0.76735821] + y = [1.41186073, 0.49775975, 0.08275588, 0.24086388, + 0.03567057, 0.52024419, 0.31966611, 1.32067634] + + # a multi-sample statistic for which the BCa CI tends to be different + # from the other CIs + def statistic(x, y, axis): + s1 = stats.skew(x, axis=axis) + s2 = stats.skew(y, axis=axis) + return s1 - s2 + + # compute confidence intervals using each method + rng = np.random.default_rng(468865032284792692) + + res_basic = stats.bootstrap((x, y), statistic, method='basic', + batch=100, random_state=rng) + res_percent = stats.bootstrap((x, y), statistic, method='percentile', + batch=100, random_state=rng) + res_bca = stats.bootstrap((x, y), statistic, method='bca', + batch=100, random_state=rng) + + # compute midpoints so we can compare just one number for each + mid_basic = np.mean(res_basic.confidence_interval) + mid_percent = np.mean(res_percent.confidence_interval) + mid_bca = np.mean(res_bca.confidence_interval) + + # reference for BCA CI computed using R wboot package: + # library(wBoot) + # library(moments) + + # x = c(0.75859206, 0.5910282, -0.4419409, -0.36654601, + # 0.34955357, -1.38835871, 0.76735821) + # y = c(1.41186073, 0.49775975, 0.08275588, 0.24086388, + # 0.03567057, 0.52024419, 0.31966611, 1.32067634) + + # twoskew <- function(x1, y1) {skewness(x1) - skewness(y1)} + # boot.two.bca(x, y, skewness, conf.level = 0.95, + # R = 9999, stacked = FALSE) + mid_wboot = -1.5519 + + # compute percent difference relative to wboot BCA method + diff_basic = (mid_basic - mid_wboot)/abs(mid_wboot) + diff_percent = (mid_percent - mid_wboot)/abs(mid_wboot) + diff_bca = (mid_bca - mid_wboot)/abs(mid_wboot) + + # SciPy's BCa CI midpoint is much closer than that of the other methods + assert diff_basic < -0.15 + assert diff_percent > 0.15 + assert abs(diff_bca) < 0.03 + + +def test_BCa_acceleration_against_reference(): + # Compare the (deterministic) acceleration parameter for a multi-sample + # problem against a reference value. The example is from [1], but Efron's + # value seems inaccurate. Straightorward code for computing the + # reference acceleration (0.011008228344026734) is available at: + # https://github.com/scipy/scipy/pull/16455#issuecomment-1193400981 + + y = np.array([10, 27, 31, 40, 46, 50, 52, 104, 146]) + z = np.array([16, 23, 38, 94, 99, 141, 197]) + + def statistic(z, y, axis=0): + return np.mean(z, axis=axis) - np.mean(y, axis=axis) + + data = [z, y] + res = stats.bootstrap(data, statistic) + + axis = -1 + alpha = 0.95 + theta_hat_b = res.bootstrap_distribution + batch = 100 + _, _, a_hat = _resampling._bca_interval(data, statistic, axis, alpha, + theta_hat_b, batch) + assert_allclose(a_hat, 0.011008228344026734) + + +@pytest.mark.parametrize("method, expected", + tests_against_itself_1samp.items()) +def test_bootstrap_against_itself_1samp(method, expected): + # The expected values in this test were generated using bootstrap + # to check for unintended changes in behavior. The test also makes sure + # that bootstrap works with multi-sample statistics and that the + # `axis` argument works as expected / function is vectorized. + np.random.seed(0) + + n = 100 # size of sample + n_resamples = 999 # number of bootstrap resamples used to form each CI + confidence_level = 0.9 + + # The true mean is 5 + dist = stats.norm(loc=5, scale=1) + stat_true = dist.mean() + + # Do the same thing 2000 times. (The code is fully vectorized.) + n_replications = 2000 + data = dist.rvs(size=(n_replications, n)) + res = bootstrap((data,), + statistic=np.mean, + confidence_level=confidence_level, + n_resamples=n_resamples, + batch=50, + method=method, + axis=-1) + ci = res.confidence_interval + + # ci contains vectors of lower and upper confidence interval bounds + ci_contains_true = np.sum((ci[0] < stat_true) & (stat_true < ci[1])) + assert ci_contains_true == expected + + # ci_contains_true is not inconsistent with confidence_level + pvalue = stats.binomtest(ci_contains_true, n_replications, + confidence_level).pvalue + assert pvalue > 0.1 + + +tests_against_itself_2samp = {"basic": 892, + "percentile": 890} + + +@pytest.mark.parametrize("method, expected", + tests_against_itself_2samp.items()) +def test_bootstrap_against_itself_2samp(method, expected): + # The expected values in this test were generated using bootstrap + # to check for unintended changes in behavior. The test also makes sure + # that bootstrap works with multi-sample statistics and that the + # `axis` argument works as expected / function is vectorized. + np.random.seed(0) + + n1 = 100 # size of sample 1 + n2 = 120 # size of sample 2 + n_resamples = 999 # number of bootstrap resamples used to form each CI + confidence_level = 0.9 + + # The statistic we're interested in is the difference in means + def my_stat(data1, data2, axis=-1): + mean1 = np.mean(data1, axis=axis) + mean2 = np.mean(data2, axis=axis) + return mean1 - mean2 + + # The true difference in the means is -0.1 + dist1 = stats.norm(loc=0, scale=1) + dist2 = stats.norm(loc=0.1, scale=1) + stat_true = dist1.mean() - dist2.mean() + + # Do the same thing 1000 times. (The code is fully vectorized.) + n_replications = 1000 + data1 = dist1.rvs(size=(n_replications, n1)) + data2 = dist2.rvs(size=(n_replications, n2)) + res = bootstrap((data1, data2), + statistic=my_stat, + confidence_level=confidence_level, + n_resamples=n_resamples, + batch=50, + method=method, + axis=-1) + ci = res.confidence_interval + + # ci contains vectors of lower and upper confidence interval bounds + ci_contains_true = np.sum((ci[0] < stat_true) & (stat_true < ci[1])) + assert ci_contains_true == expected + + # ci_contains_true is not inconsistent with confidence_level + pvalue = stats.binomtest(ci_contains_true, n_replications, + confidence_level).pvalue + assert pvalue > 0.1 + + +@pytest.mark.parametrize("method", ["basic", "percentile"]) +@pytest.mark.parametrize("axis", [0, 1]) +def test_bootstrap_vectorized_3samp(method, axis): + def statistic(*data, axis=0): + # an arbitrary, vectorized statistic + return sum(sample.mean(axis) for sample in data) + + def statistic_1d(*data): + # the same statistic, not vectorized + for sample in data: + assert sample.ndim == 1 + return statistic(*data, axis=0) + + np.random.seed(0) + x = np.random.rand(4, 5) + y = np.random.rand(4, 5) + z = np.random.rand(4, 5) + res1 = bootstrap((x, y, z), statistic, vectorized=True, + axis=axis, n_resamples=100, method=method, random_state=0) + res2 = bootstrap((x, y, z), statistic_1d, vectorized=False, + axis=axis, n_resamples=100, method=method, random_state=0) + assert_allclose(res1.confidence_interval, res2.confidence_interval) + assert_allclose(res1.standard_error, res2.standard_error) + + +@pytest.mark.xfail_on_32bit("Failure is not concerning; see gh-14107") +@pytest.mark.parametrize("method", ["basic", "percentile", "BCa"]) +@pytest.mark.parametrize("axis", [0, 1]) +def test_bootstrap_vectorized_1samp(method, axis): + def statistic(x, axis=0): + # an arbitrary, vectorized statistic + return x.mean(axis=axis) + + def statistic_1d(x): + # the same statistic, not vectorized + assert x.ndim == 1 + return statistic(x, axis=0) + + np.random.seed(0) + x = np.random.rand(4, 5) + res1 = bootstrap((x,), statistic, vectorized=True, axis=axis, + n_resamples=100, batch=None, method=method, + random_state=0) + res2 = bootstrap((x,), statistic_1d, vectorized=False, axis=axis, + n_resamples=100, batch=10, method=method, + random_state=0) + assert_allclose(res1.confidence_interval, res2.confidence_interval) + assert_allclose(res1.standard_error, res2.standard_error) + + +@pytest.mark.parametrize("method", ["basic", "percentile", "BCa"]) +def test_bootstrap_degenerate(method): + data = 35 * [10000.] + if method == "BCa": + with np.errstate(invalid='ignore'): + msg = "The BCa confidence interval cannot be calculated" + with pytest.warns(stats.DegenerateDataWarning, match=msg): + res = bootstrap([data, ], np.mean, method=method) + assert_equal(res.confidence_interval, (np.nan, np.nan)) + else: + res = bootstrap([data, ], np.mean, method=method) + assert_equal(res.confidence_interval, (10000., 10000.)) + assert_equal(res.standard_error, 0) + + +@pytest.mark.parametrize("method", ["basic", "percentile", "BCa"]) +def test_bootstrap_gh15678(method): + # Check that gh-15678 is fixed: when statistic function returned a Python + # float, method="BCa" failed when trying to add a dimension to the float + rng = np.random.default_rng(354645618886684) + dist = stats.norm(loc=2, scale=4) + data = dist.rvs(size=100, random_state=rng) + data = (data,) + res = bootstrap(data, stats.skew, method=method, n_resamples=100, + random_state=np.random.default_rng(9563)) + # this always worked because np.apply_along_axis returns NumPy data type + ref = bootstrap(data, stats.skew, method=method, n_resamples=100, + random_state=np.random.default_rng(9563), vectorized=False) + assert_allclose(res.confidence_interval, ref.confidence_interval) + assert_allclose(res.standard_error, ref.standard_error) + assert isinstance(res.standard_error, np.float64) + + +def test_bootstrap_min(): + # Check that gh-15883 is fixed: percentileofscore should + # behave according to the 'mean' behavior and not trigger nan for BCa + rng = np.random.default_rng(1891289180021102) + dist = stats.norm(loc=2, scale=4) + data = dist.rvs(size=100, random_state=rng) + true_min = np.min(data) + data = (data,) + res = bootstrap(data, np.min, method="BCa", n_resamples=100, + random_state=np.random.default_rng(3942)) + assert true_min == res.confidence_interval.low + res2 = bootstrap(-np.array(data), np.max, method="BCa", n_resamples=100, + random_state=np.random.default_rng(3942)) + assert_allclose(-res.confidence_interval.low, + res2.confidence_interval.high) + assert_allclose(-res.confidence_interval.high, + res2.confidence_interval.low) + + +@pytest.mark.parametrize("additional_resamples", [0, 1000]) +def test_re_bootstrap(additional_resamples): + # Test behavior of parameter `bootstrap_result` + rng = np.random.default_rng(8958153316228384) + x = rng.random(size=100) + + n1 = 1000 + n2 = additional_resamples + n3 = n1 + additional_resamples + + rng = np.random.default_rng(296689032789913033) + res = stats.bootstrap((x,), np.mean, n_resamples=n1, random_state=rng, + confidence_level=0.95, method='percentile') + res = stats.bootstrap((x,), np.mean, n_resamples=n2, random_state=rng, + confidence_level=0.90, method='BCa', + bootstrap_result=res) + + rng = np.random.default_rng(296689032789913033) + ref = stats.bootstrap((x,), np.mean, n_resamples=n3, random_state=rng, + confidence_level=0.90, method='BCa') + + assert_allclose(res.standard_error, ref.standard_error, rtol=1e-14) + assert_allclose(res.confidence_interval, ref.confidence_interval, + rtol=1e-14) + + +@pytest.mark.xfail_on_32bit("Sensible to machine precision") +@pytest.mark.parametrize("method", ['basic', 'percentile', 'BCa']) +def test_bootstrap_alternative(method): + rng = np.random.default_rng(5894822712842015040) + dist = stats.norm(loc=2, scale=4) + data = (dist.rvs(size=(100), random_state=rng),) + + config = dict(data=data, statistic=np.std, random_state=rng, axis=-1) + t = stats.bootstrap(**config, confidence_level=0.9) + + config.update(dict(n_resamples=0, bootstrap_result=t)) + l = stats.bootstrap(**config, confidence_level=0.95, alternative='less') + g = stats.bootstrap(**config, confidence_level=0.95, alternative='greater') + + assert_allclose(l.confidence_interval.high, t.confidence_interval.high, + rtol=1e-14) + assert_allclose(g.confidence_interval.low, t.confidence_interval.low, + rtol=1e-14) + assert np.isneginf(l.confidence_interval.low) + assert np.isposinf(g.confidence_interval.high) + + with pytest.raises(ValueError, match='`alternative` must be one of'): + stats.bootstrap(**config, alternative='ekki-ekki') + + +def test_jackknife_resample(): + shape = 3, 4, 5, 6 + np.random.seed(0) + x = np.random.rand(*shape) + y = next(_resampling._jackknife_resample(x)) + + for i in range(shape[-1]): + # each resample is indexed along second to last axis + # (last axis is the one the statistic will be taken over / consumed) + slc = y[..., i, :] + expected = np.delete(x, i, axis=-1) + + assert np.array_equal(slc, expected) + + y2 = np.concatenate(list(_resampling._jackknife_resample(x, batch=2)), + axis=-2) + assert np.array_equal(y2, y) + + +@pytest.mark.parametrize("rng_name", ["RandomState", "default_rng"]) +def test_bootstrap_resample(rng_name): + rng = getattr(np.random, rng_name, None) + if rng is None: + pytest.skip(f"{rng_name} not available.") + rng1 = rng(0) + rng2 = rng(0) + + n_resamples = 10 + shape = 3, 4, 5, 6 + + np.random.seed(0) + x = np.random.rand(*shape) + y = _resampling._bootstrap_resample(x, n_resamples, random_state=rng1) + + for i in range(n_resamples): + # each resample is indexed along second to last axis + # (last axis is the one the statistic will be taken over / consumed) + slc = y[..., i, :] + + js = rng_integers(rng2, 0, shape[-1], shape[-1]) + expected = x[..., js] + + assert np.array_equal(slc, expected) + + +@pytest.mark.parametrize("score", [0, 0.5, 1]) +@pytest.mark.parametrize("axis", [0, 1, 2]) +def test_percentile_of_score(score, axis): + shape = 10, 20, 30 + np.random.seed(0) + x = np.random.rand(*shape) + p = _resampling._percentile_of_score(x, score, axis=-1) + + def vectorized_pos(a, score, axis): + return np.apply_along_axis(stats.percentileofscore, axis, a, score) + + p2 = vectorized_pos(x, score, axis=-1)/100 + + assert_allclose(p, p2, 1e-15) + + +def test_percentile_along_axis(): + # the difference between _percentile_along_axis and np.percentile is that + # np.percentile gets _all_ the qs for each axis slice, whereas + # _percentile_along_axis gets the q corresponding with each axis slice + + shape = 10, 20 + np.random.seed(0) + x = np.random.rand(*shape) + q = np.random.rand(*shape[:-1]) * 100 + y = _resampling._percentile_along_axis(x, q) + + for i in range(shape[0]): + res = y[i] + expected = np.percentile(x[i], q[i], axis=-1) + assert_allclose(res, expected, 1e-15) + + +@pytest.mark.parametrize("axis", [0, 1, 2]) +def test_vectorize_statistic(axis): + # test that _vectorize_statistic vectorizes a statistic along `axis` + + def statistic(*data, axis): + # an arbitrary, vectorized statistic + return sum(sample.mean(axis) for sample in data) + + def statistic_1d(*data): + # the same statistic, not vectorized + for sample in data: + assert sample.ndim == 1 + return statistic(*data, axis=0) + + # vectorize the non-vectorized statistic + statistic2 = _resampling._vectorize_statistic(statistic_1d) + + np.random.seed(0) + x = np.random.rand(4, 5, 6) + y = np.random.rand(4, 1, 6) + z = np.random.rand(1, 5, 6) + + res1 = statistic(x, y, z, axis=axis) + res2 = statistic2(x, y, z, axis=axis) + assert_allclose(res1, res2) + + +@pytest.mark.parametrize("method", ["basic", "percentile", "BCa"]) +def test_vector_valued_statistic(method): + # Generate 95% confidence interval around MLE of normal distribution + # parameters. Repeat 100 times, each time on sample of size 100. + # Check that confidence interval contains true parameters ~95 times. + # Confidence intervals are estimated and stochastic; a test failure + # does not necessarily indicate that something is wrong. More important + # than values of `counts` below is that the shapes of the outputs are + # correct. + + rng = np.random.default_rng(2196847219) + params = 1, 0.5 + sample = stats.norm.rvs(*params, size=(100, 100), random_state=rng) + + def statistic(data, axis): + return np.asarray([np.mean(data, axis), + np.std(data, axis, ddof=1)]) + + res = bootstrap((sample,), statistic, method=method, axis=-1, + n_resamples=9999, batch=200) + + counts = np.sum((res.confidence_interval.low.T < params) + & (res.confidence_interval.high.T > params), + axis=0) + assert np.all(counts >= 90) + assert np.all(counts <= 100) + assert res.confidence_interval.low.shape == (2, 100) + assert res.confidence_interval.high.shape == (2, 100) + assert res.standard_error.shape == (2, 100) + assert res.bootstrap_distribution.shape == (2, 100, 9999) + + +@pytest.mark.slow +@pytest.mark.filterwarnings('ignore::RuntimeWarning') +def test_vector_valued_statistic_gh17715(): + # gh-17715 reported a mistake introduced in the extension of BCa to + # multi-sample statistics; a `len` should have been `.shape[-1]`. Check + # that this is resolved. + + rng = np.random.default_rng(141921000979291141) + + def concordance(x, y, axis): + xm = x.mean(axis) + ym = y.mean(axis) + cov = ((x - xm[..., None]) * (y - ym[..., None])).mean(axis) + return (2 * cov) / (x.var(axis) + y.var(axis) + (xm - ym) ** 2) + + def statistic(tp, tn, fp, fn, axis): + actual = tp + fp + expected = tp + fn + return np.nan_to_num(concordance(actual, expected, axis)) + + def statistic_extradim(*args, axis): + return statistic(*args, axis)[np.newaxis, ...] + + data = [[4, 0, 0, 2], # (tp, tn, fp, fn) + [2, 1, 2, 1], + [0, 6, 0, 0], + [0, 6, 3, 0], + [0, 8, 1, 0]] + data = np.array(data).T + + res = bootstrap(data, statistic_extradim, random_state=rng, paired=True) + ref = bootstrap(data, statistic, random_state=rng, paired=True) + assert_allclose(res.confidence_interval.low[0], + ref.confidence_interval.low, atol=1e-15) + assert_allclose(res.confidence_interval.high[0], + ref.confidence_interval.high, atol=1e-15) + + +# --- Test Monte Carlo Hypothesis Test --- # + +class TestMonteCarloHypothesisTest: + atol = 2.5e-2 # for comparing p-value + + def rvs(self, rvs_in, rs): + return lambda *args, **kwds: rvs_in(*args, random_state=rs, **kwds) + + def test_input_validation(self): + # test that the appropriate error messages are raised for invalid input + + def stat(x): + return stats.skewnorm(x).statistic + + message = "Array shapes are incompatible for broadcasting." + data = (np.zeros((2, 5)), np.zeros((3, 5))) + rvs = (stats.norm.rvs, stats.norm.rvs) + with pytest.raises(ValueError, match=message): + monte_carlo_test(data, rvs, lambda x, y: 1, axis=-1) + + message = "`axis` must be an integer." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, axis=1.5) + + message = "`vectorized` must be `True`, `False`, or `None`." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, vectorized=1.5) + + message = "`rvs` must be callable or sequence of callables." + with pytest.raises(TypeError, match=message): + monte_carlo_test([1, 2, 3], None, stat) + with pytest.raises(TypeError, match=message): + monte_carlo_test([[1, 2], [3, 4]], [lambda x: x, None], stat) + + message = "If `rvs` is a sequence..." + with pytest.raises(ValueError, match=message): + monte_carlo_test([[1, 2, 3]], [lambda x: x, lambda x: x], stat) + + message = "`statistic` must be callable." + with pytest.raises(TypeError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, None) + + message = "`n_resamples` must be a positive integer." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, + n_resamples=-1000) + + message = "`n_resamples` must be a positive integer." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, + n_resamples=1000.5) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, batch=-1000) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, batch=1000.5) + + message = "`alternative` must be in..." + with pytest.raises(ValueError, match=message): + monte_carlo_test([1, 2, 3], stats.norm.rvs, stat, + alternative='ekki') + + + def test_batch(self): + # make sure that the `batch` parameter is respected by checking the + # maximum batch size provided in calls to `statistic` + rng = np.random.default_rng(23492340193) + x = rng.random(10) + + def statistic(x, axis): + batch_size = 1 if x.ndim == 1 else len(x) + statistic.batch_size = max(batch_size, statistic.batch_size) + statistic.counter += 1 + return stats.skewtest(x, axis=axis).statistic + statistic.counter = 0 + statistic.batch_size = 0 + + kwds = {'sample': x, 'statistic': statistic, + 'n_resamples': 1000, 'vectorized': True} + + kwds['rvs'] = self.rvs(stats.norm.rvs, np.random.default_rng(32842398)) + res1 = monte_carlo_test(batch=1, **kwds) + assert_equal(statistic.counter, 1001) + assert_equal(statistic.batch_size, 1) + + kwds['rvs'] = self.rvs(stats.norm.rvs, np.random.default_rng(32842398)) + statistic.counter = 0 + res2 = monte_carlo_test(batch=50, **kwds) + assert_equal(statistic.counter, 21) + assert_equal(statistic.batch_size, 50) + + kwds['rvs'] = self.rvs(stats.norm.rvs, np.random.default_rng(32842398)) + statistic.counter = 0 + res3 = monte_carlo_test(**kwds) + assert_equal(statistic.counter, 2) + assert_equal(statistic.batch_size, 1000) + + assert_equal(res1.pvalue, res3.pvalue) + assert_equal(res2.pvalue, res3.pvalue) + + @pytest.mark.parametrize('axis', range(-3, 3)) + def test_axis(self, axis): + # test that Nd-array samples are handled correctly for valid values + # of the `axis` parameter + rng = np.random.default_rng(2389234) + norm_rvs = self.rvs(stats.norm.rvs, rng) + + size = [2, 3, 4] + size[axis] = 100 + x = norm_rvs(size=size) + expected = stats.skewtest(x, axis=axis) + + def statistic(x, axis): + return stats.skewtest(x, axis=axis).statistic + + res = monte_carlo_test(x, norm_rvs, statistic, vectorized=True, + n_resamples=20000, axis=axis) + + assert_allclose(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue, atol=self.atol) + + @pytest.mark.parametrize('alternative', ("less", "greater")) + @pytest.mark.parametrize('a', np.linspace(-0.5, 0.5, 5)) # skewness + def test_against_ks_1samp(self, alternative, a): + # test that monte_carlo_test can reproduce pvalue of ks_1samp + rng = np.random.default_rng(65723433) + + x = stats.skewnorm.rvs(a=a, size=30, random_state=rng) + expected = stats.ks_1samp(x, stats.norm.cdf, alternative=alternative) + + def statistic1d(x): + return stats.ks_1samp(x, stats.norm.cdf, mode='asymp', + alternative=alternative).statistic + + norm_rvs = self.rvs(stats.norm.rvs, rng) + res = monte_carlo_test(x, norm_rvs, statistic1d, + n_resamples=1000, vectorized=False, + alternative=alternative) + + assert_allclose(res.statistic, expected.statistic) + if alternative == 'greater': + assert_allclose(res.pvalue, expected.pvalue, atol=self.atol) + elif alternative == 'less': + assert_allclose(1-res.pvalue, expected.pvalue, atol=self.atol) + + @pytest.mark.parametrize('hypotest', (stats.skewtest, stats.kurtosistest)) + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + @pytest.mark.parametrize('a', np.linspace(-2, 2, 5)) # skewness + def test_against_normality_tests(self, hypotest, alternative, a): + # test that monte_carlo_test can reproduce pvalue of normality tests + rng = np.random.default_rng(85723405) + + x = stats.skewnorm.rvs(a=a, size=150, random_state=rng) + expected = hypotest(x, alternative=alternative) + + def statistic(x, axis): + return hypotest(x, axis=axis).statistic + + norm_rvs = self.rvs(stats.norm.rvs, rng) + res = monte_carlo_test(x, norm_rvs, statistic, vectorized=True, + alternative=alternative) + + assert_allclose(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue, atol=self.atol) + + @pytest.mark.parametrize('a', np.arange(-2, 3)) # skewness parameter + def test_against_normaltest(self, a): + # test that monte_carlo_test can reproduce pvalue of normaltest + rng = np.random.default_rng(12340513) + + x = stats.skewnorm.rvs(a=a, size=150, random_state=rng) + expected = stats.normaltest(x) + + def statistic(x, axis): + return stats.normaltest(x, axis=axis).statistic + + norm_rvs = self.rvs(stats.norm.rvs, rng) + res = monte_carlo_test(x, norm_rvs, statistic, vectorized=True, + alternative='greater') + + assert_allclose(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue, atol=self.atol) + + @pytest.mark.parametrize('a', np.linspace(-0.5, 0.5, 5)) # skewness + def test_against_cramervonmises(self, a): + # test that monte_carlo_test can reproduce pvalue of cramervonmises + rng = np.random.default_rng(234874135) + + x = stats.skewnorm.rvs(a=a, size=30, random_state=rng) + expected = stats.cramervonmises(x, stats.norm.cdf) + + def statistic1d(x): + return stats.cramervonmises(x, stats.norm.cdf).statistic + + norm_rvs = self.rvs(stats.norm.rvs, rng) + res = monte_carlo_test(x, norm_rvs, statistic1d, + n_resamples=1000, vectorized=False, + alternative='greater') + + assert_allclose(res.statistic, expected.statistic) + assert_allclose(res.pvalue, expected.pvalue, atol=self.atol) + + @pytest.mark.parametrize('dist_name', ('norm', 'logistic')) + @pytest.mark.parametrize('i', range(5)) + def test_against_anderson(self, dist_name, i): + # test that monte_carlo_test can reproduce results of `anderson`. Note: + # `anderson` does not provide a p-value; it provides a list of + # significance levels and the associated critical value of the test + # statistic. `i` used to index this list. + + # find the skewness for which the sample statistic matches one of the + # critical values provided by `stats.anderson` + + def fun(a): + rng = np.random.default_rng(394295467) + x = stats.tukeylambda.rvs(a, size=100, random_state=rng) + expected = stats.anderson(x, dist_name) + return expected.statistic - expected.critical_values[i] + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + sol = root(fun, x0=0) + assert sol.success + + # get the significance level (p-value) associated with that critical + # value + a = sol.x[0] + rng = np.random.default_rng(394295467) + x = stats.tukeylambda.rvs(a, size=100, random_state=rng) + expected = stats.anderson(x, dist_name) + expected_stat = expected.statistic + expected_p = expected.significance_level[i]/100 + + # perform equivalent Monte Carlo test and compare results + def statistic1d(x): + return stats.anderson(x, dist_name).statistic + + dist_rvs = self.rvs(getattr(stats, dist_name).rvs, rng) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + res = monte_carlo_test(x, dist_rvs, + statistic1d, n_resamples=1000, + vectorized=False, alternative='greater') + + assert_allclose(res.statistic, expected_stat) + assert_allclose(res.pvalue, expected_p, atol=2*self.atol) + + def test_p_never_zero(self): + # Use biased estimate of p-value to ensure that p-value is never zero + # per monte_carlo_test reference [1] + rng = np.random.default_rng(2190176673029737545) + x = np.zeros(100) + res = monte_carlo_test(x, rng.random, np.mean, + vectorized=True, alternative='less') + assert res.pvalue == 0.0001 + + def test_against_ttest_ind(self): + # test that `monte_carlo_test` can reproduce results of `ttest_ind`. + rng = np.random.default_rng(219017667302737545) + data = rng.random(size=(2, 5)), rng.random(size=7) # broadcastable + rvs = rng.normal, rng.normal + def statistic(x, y, axis): + return stats.ttest_ind(x, y, axis).statistic + + res = stats.monte_carlo_test(data, rvs, statistic, axis=-1) + ref = stats.ttest_ind(data[0], [data[1]], axis=-1) + assert_allclose(res.statistic, ref.statistic) + assert_allclose(res.pvalue, ref.pvalue, rtol=2e-2) + + def test_against_f_oneway(self): + # test that `monte_carlo_test` can reproduce results of `f_oneway`. + rng = np.random.default_rng(219017667302737545) + data = (rng.random(size=(2, 100)), rng.random(size=(2, 101)), + rng.random(size=(2, 102)), rng.random(size=(2, 103))) + rvs = rng.normal, rng.normal, rng.normal, rng.normal + + def statistic(*args, axis): + return stats.f_oneway(*args, axis=axis).statistic + + res = stats.monte_carlo_test(data, rvs, statistic, axis=-1, + alternative='greater') + ref = stats.f_oneway(*data, axis=-1) + + assert_allclose(res.statistic, ref.statistic) + assert_allclose(res.pvalue, ref.pvalue, atol=1e-2) + + @pytest.mark.xfail_on_32bit("Statistic may not depend on sample order on 32-bit") + def test_finite_precision_statistic(self): + # Some statistics return numerically distinct values when the values + # should be equal in theory. Test that `monte_carlo_test` accounts + # for this in some way. + rng = np.random.default_rng(2549824598234528) + n_resamples = 9999 + def rvs(size): + return 1. * stats.bernoulli(p=0.333).rvs(size=size, random_state=rng) + + x = rvs(100) + res = stats.monte_carlo_test(x, rvs, np.var, alternative='less', + n_resamples=n_resamples) + # show that having a tolerance matters + c0 = np.sum(res.null_distribution <= res.statistic) + c1 = np.sum(res.null_distribution <= res.statistic*(1+1e-15)) + assert c0 != c1 + assert res.pvalue == (c1 + 1)/(n_resamples + 1) + + +class TestPermutationTest: + + rtol = 1e-14 + + def setup_method(self): + self.rng = np.random.default_rng(7170559330470561044) + + # -- Input validation -- # + + def test_permutation_test_iv(self): + + def stat(x, y, axis): + return stats.ttest_ind((x, y), axis).statistic + + message = "each sample in `data` must contain two or more ..." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1]), stat) + + message = "`data` must be a tuple containing at least two samples" + with pytest.raises(ValueError, match=message): + permutation_test((1,), stat) + with pytest.raises(TypeError, match=message): + permutation_test(1, stat) + + message = "`axis` must be an integer." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, axis=1.5) + + message = "`permutation_type` must be in..." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, + permutation_type="ekki") + + message = "`vectorized` must be `True`, `False`, or `None`." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, vectorized=1.5) + + message = "`n_resamples` must be a positive integer." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, n_resamples=-1000) + + message = "`n_resamples` must be a positive integer." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, n_resamples=1000.5) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, batch=-1000) + + message = "`batch` must be a positive integer or None." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, batch=1000.5) + + message = "`alternative` must be in..." + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, alternative='ekki') + + message = "'herring' cannot be used to seed a" + with pytest.raises(ValueError, match=message): + permutation_test(([1, 2, 3], [1, 2, 3]), stat, + random_state='herring') + + # -- Test Parameters -- # + @pytest.mark.parametrize('random_state', [np.random.RandomState, + np.random.default_rng]) + @pytest.mark.parametrize('permutation_type', + ['pairings', 'samples', 'independent']) + def test_batch(self, permutation_type, random_state): + # make sure that the `batch` parameter is respected by checking the + # maximum batch size provided in calls to `statistic` + x = self.rng.random(10) + y = self.rng.random(10) + + def statistic(x, y, axis): + batch_size = 1 if x.ndim == 1 else len(x) + statistic.batch_size = max(batch_size, statistic.batch_size) + statistic.counter += 1 + return np.mean(x, axis=axis) - np.mean(y, axis=axis) + statistic.counter = 0 + statistic.batch_size = 0 + + kwds = {'n_resamples': 1000, 'permutation_type': permutation_type, + 'vectorized': True} + res1 = stats.permutation_test((x, y), statistic, batch=1, + random_state=random_state(0), **kwds) + assert_equal(statistic.counter, 1001) + assert_equal(statistic.batch_size, 1) + + statistic.counter = 0 + res2 = stats.permutation_test((x, y), statistic, batch=50, + random_state=random_state(0), **kwds) + assert_equal(statistic.counter, 21) + assert_equal(statistic.batch_size, 50) + + statistic.counter = 0 + res3 = stats.permutation_test((x, y), statistic, batch=1000, + random_state=random_state(0), **kwds) + assert_equal(statistic.counter, 2) + assert_equal(statistic.batch_size, 1000) + + assert_equal(res1.pvalue, res3.pvalue) + assert_equal(res2.pvalue, res3.pvalue) + + @pytest.mark.parametrize('random_state', [np.random.RandomState, + np.random.default_rng]) + @pytest.mark.parametrize('permutation_type, exact_size', + [('pairings', special.factorial(3)**2), + ('samples', 2**3), + ('independent', special.binom(6, 3))]) + def test_permutations(self, permutation_type, exact_size, random_state): + # make sure that the `permutations` parameter is respected by checking + # the size of the null distribution + x = self.rng.random(3) + y = self.rng.random(3) + + def statistic(x, y, axis): + return np.mean(x, axis=axis) - np.mean(y, axis=axis) + + kwds = {'permutation_type': permutation_type, + 'vectorized': True} + res = stats.permutation_test((x, y), statistic, n_resamples=3, + random_state=random_state(0), **kwds) + assert_equal(res.null_distribution.size, 3) + + res = stats.permutation_test((x, y), statistic, **kwds) + assert_equal(res.null_distribution.size, exact_size) + + # -- Randomized Permutation Tests -- # + + # To get reasonable accuracy, these next three tests are somewhat slow. + # Originally, I had them passing for all combinations of permutation type, + # alternative, and RNG, but that takes too long for CI. Instead, split + # into three tests, each testing a particular combination of the three + # parameters. + + def test_randomized_test_against_exact_both(self): + # check that the randomized and exact tests agree to reasonable + # precision for permutation_type='both + + alternative, rng = 'less', 0 + + nx, ny, permutations = 8, 9, 24000 + assert special.binom(nx + ny, nx) > permutations + + x = stats.norm.rvs(size=nx) + y = stats.norm.rvs(size=ny) + data = x, y + + def statistic(x, y, axis): + return np.mean(x, axis=axis) - np.mean(y, axis=axis) + + kwds = {'vectorized': True, 'permutation_type': 'independent', + 'batch': 100, 'alternative': alternative, 'random_state': rng} + res = permutation_test(data, statistic, n_resamples=permutations, + **kwds) + res2 = permutation_test(data, statistic, n_resamples=np.inf, **kwds) + + assert res.statistic == res2.statistic + assert_allclose(res.pvalue, res2.pvalue, atol=1e-2) + + @pytest.mark.slow() + def test_randomized_test_against_exact_samples(self): + # check that the randomized and exact tests agree to reasonable + # precision for permutation_type='samples' + + alternative, rng = 'greater', None + + nx, ny, permutations = 15, 15, 32000 + assert 2**nx > permutations + + x = stats.norm.rvs(size=nx) + y = stats.norm.rvs(size=ny) + data = x, y + + def statistic(x, y, axis): + return np.mean(x - y, axis=axis) + + kwds = {'vectorized': True, 'permutation_type': 'samples', + 'batch': 100, 'alternative': alternative, 'random_state': rng} + res = permutation_test(data, statistic, n_resamples=permutations, + **kwds) + res2 = permutation_test(data, statistic, n_resamples=np.inf, **kwds) + + assert res.statistic == res2.statistic + assert_allclose(res.pvalue, res2.pvalue, atol=1e-2) + + def test_randomized_test_against_exact_pairings(self): + # check that the randomized and exact tests agree to reasonable + # precision for permutation_type='pairings' + + alternative, rng = 'two-sided', self.rng + + nx, ny, permutations = 8, 8, 40000 + assert special.factorial(nx) > permutations + + x = stats.norm.rvs(size=nx) + y = stats.norm.rvs(size=ny) + data = [x] + + def statistic1d(x): + return stats.pearsonr(x, y)[0] + + statistic = _resampling._vectorize_statistic(statistic1d) + + kwds = {'vectorized': True, 'permutation_type': 'samples', + 'batch': 100, 'alternative': alternative, 'random_state': rng} + res = permutation_test(data, statistic, n_resamples=permutations, + **kwds) + res2 = permutation_test(data, statistic, n_resamples=np.inf, **kwds) + + assert res.statistic == res2.statistic + assert_allclose(res.pvalue, res2.pvalue, atol=1e-2) + + @pytest.mark.parametrize('alternative', ('less', 'greater')) + # Different conventions for two-sided p-value here VS ttest_ind. + # Eventually, we can add multiple options for the two-sided alternative + # here in permutation_test. + @pytest.mark.parametrize('permutations', (30, 1e9)) + @pytest.mark.parametrize('axis', (0, 1, 2)) + def test_against_permutation_ttest(self, alternative, permutations, axis): + # check that this function and ttest_ind with permutations give + # essentially identical results. + + x = np.arange(3*4*5).reshape(3, 4, 5) + y = np.moveaxis(np.arange(4)[:, None, None], 0, axis) + + rng1 = np.random.default_rng(4337234444626115331) + res1 = stats.ttest_ind(x, y, permutations=permutations, axis=axis, + random_state=rng1, alternative=alternative) + + def statistic(x, y, axis): + return stats.ttest_ind(x, y, axis=axis).statistic + + rng2 = np.random.default_rng(4337234444626115331) + res2 = permutation_test((x, y), statistic, vectorized=True, + n_resamples=permutations, + alternative=alternative, axis=axis, + random_state=rng2) + + assert_allclose(res1.statistic, res2.statistic, rtol=self.rtol) + assert_allclose(res1.pvalue, res2.pvalue, rtol=self.rtol) + + # -- Independent (Unpaired) Sample Tests -- # + + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + def test_against_ks_2samp(self, alternative): + + x = self.rng.normal(size=4, scale=1) + y = self.rng.normal(size=5, loc=3, scale=3) + + expected = stats.ks_2samp(x, y, alternative=alternative, mode='exact') + + def statistic1d(x, y): + return stats.ks_2samp(x, y, mode='asymp', + alternative=alternative).statistic + + # ks_2samp is always a one-tailed 'greater' test + # it's the statistic that changes (D+ vs D- vs max(D+, D-)) + res = permutation_test((x, y), statistic1d, n_resamples=np.inf, + alternative='greater', random_state=self.rng) + + assert_allclose(res.statistic, expected.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + def test_against_ansari(self, alternative): + + x = self.rng.normal(size=4, scale=1) + y = self.rng.normal(size=5, scale=3) + + # ansari has a different convention for 'alternative' + alternative_correspondence = {"less": "greater", + "greater": "less", + "two-sided": "two-sided"} + alternative_scipy = alternative_correspondence[alternative] + expected = stats.ansari(x, y, alternative=alternative_scipy) + + def statistic1d(x, y): + return stats.ansari(x, y).statistic + + res = permutation_test((x, y), statistic1d, n_resamples=np.inf, + alternative=alternative, random_state=self.rng) + + assert_allclose(res.statistic, expected.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + def test_against_mannwhitneyu(self, alternative): + + x = stats.uniform.rvs(size=(3, 5, 2), loc=0, random_state=self.rng) + y = stats.uniform.rvs(size=(3, 5, 2), loc=0.05, random_state=self.rng) + + expected = stats.mannwhitneyu(x, y, axis=1, alternative=alternative) + + def statistic(x, y, axis): + return stats.mannwhitneyu(x, y, axis=axis).statistic + + res = permutation_test((x, y), statistic, vectorized=True, + n_resamples=np.inf, alternative=alternative, + axis=1, random_state=self.rng) + + assert_allclose(res.statistic, expected.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + def test_against_cvm(self): + + x = stats.norm.rvs(size=4, scale=1, random_state=self.rng) + y = stats.norm.rvs(size=5, loc=3, scale=3, random_state=self.rng) + + expected = stats.cramervonmises_2samp(x, y, method='exact') + + def statistic1d(x, y): + return stats.cramervonmises_2samp(x, y, + method='asymptotic').statistic + + # cramervonmises_2samp has only one alternative, greater + res = permutation_test((x, y), statistic1d, n_resamples=np.inf, + alternative='greater', random_state=self.rng) + + assert_allclose(res.statistic, expected.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + @pytest.mark.xslow() + @pytest.mark.parametrize('axis', (-1, 2)) + def test_vectorized_nsamp_ptype_both(self, axis): + # Test that permutation_test with permutation_type='independent' works + # properly for a 3-sample statistic with nd array samples of different + # (but compatible) shapes and ndims. Show that exact permutation test + # and random permutation tests approximate SciPy's asymptotic pvalues + # and that exact and random permutation test results are even closer + # to one another (than they are to the asymptotic results). + + # Three samples, different (but compatible) shapes with different ndims + rng = np.random.default_rng(6709265303529651545) + x = rng.random(size=(3)) + y = rng.random(size=(1, 3, 2)) + z = rng.random(size=(2, 1, 4)) + data = (x, y, z) + + # Define the statistic (and pvalue for comparison) + def statistic1d(*data): + return stats.kruskal(*data).statistic + + def pvalue1d(*data): + return stats.kruskal(*data).pvalue + + statistic = _resampling._vectorize_statistic(statistic1d) + pvalue = _resampling._vectorize_statistic(pvalue1d) + + # Calculate the expected results + x2 = np.broadcast_to(x, (2, 3, 3)) # broadcast manually because + y2 = np.broadcast_to(y, (2, 3, 2)) # _vectorize_statistic doesn't + z2 = np.broadcast_to(z, (2, 3, 4)) + expected_statistic = statistic(x2, y2, z2, axis=axis) + expected_pvalue = pvalue(x2, y2, z2, axis=axis) + + # Calculate exact and randomized permutation results + kwds = {'vectorized': False, 'axis': axis, 'alternative': 'greater', + 'permutation_type': 'independent', 'random_state': self.rng} + res = permutation_test(data, statistic1d, n_resamples=np.inf, **kwds) + res2 = permutation_test(data, statistic1d, n_resamples=1000, **kwds) + + # Check results + assert_allclose(res.statistic, expected_statistic, rtol=self.rtol) + assert_allclose(res.statistic, res2.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected_pvalue, atol=6e-2) + assert_allclose(res.pvalue, res2.pvalue, atol=3e-2) + + # -- Paired-Sample Tests -- # + + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + def test_against_wilcoxon(self, alternative): + + x = stats.uniform.rvs(size=(3, 6, 2), loc=0, random_state=self.rng) + y = stats.uniform.rvs(size=(3, 6, 2), loc=0.05, random_state=self.rng) + + # We'll check both 1- and 2-sample versions of the same test; + # we expect identical results to wilcoxon in all cases. + def statistic_1samp_1d(z): + # 'less' ensures we get the same of two statistics every time + return stats.wilcoxon(z, alternative='less').statistic + + def statistic_2samp_1d(x, y): + return stats.wilcoxon(x, y, alternative='less').statistic + + def test_1d(x, y): + return stats.wilcoxon(x, y, alternative=alternative) + + test = _resampling._vectorize_statistic(test_1d) + + expected = test(x, y, axis=1) + expected_stat = expected[0] + expected_p = expected[1] + + kwds = {'vectorized': False, 'axis': 1, 'alternative': alternative, + 'permutation_type': 'samples', 'random_state': self.rng, + 'n_resamples': np.inf} + res1 = permutation_test((x-y,), statistic_1samp_1d, **kwds) + res2 = permutation_test((x, y), statistic_2samp_1d, **kwds) + + # `wilcoxon` returns a different statistic with 'two-sided' + assert_allclose(res1.statistic, res2.statistic, rtol=self.rtol) + if alternative != 'two-sided': + assert_allclose(res2.statistic, expected_stat, rtol=self.rtol) + + assert_allclose(res2.pvalue, expected_p, rtol=self.rtol) + assert_allclose(res1.pvalue, res2.pvalue, rtol=self.rtol) + + @pytest.mark.parametrize('alternative', ("less", "greater", "two-sided")) + def test_against_binomtest(self, alternative): + + x = self.rng.integers(0, 2, size=10) + x[x == 0] = -1 + # More naturally, the test would flip elements between 0 and one. + # However, permutation_test will flip the _signs_ of the elements. + # So we have to work with +1/-1 instead of 1/0. + + def statistic(x, axis=0): + return np.sum(x > 0, axis=axis) + + k, n, p = statistic(x), 10, 0.5 + expected = stats.binomtest(k, n, p, alternative=alternative) + + res = stats.permutation_test((x,), statistic, vectorized=True, + permutation_type='samples', + n_resamples=np.inf, random_state=self.rng, + alternative=alternative) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + # -- Exact Association Tests -- # + + def test_against_kendalltau(self): + + x = self.rng.normal(size=6) + y = x + self.rng.normal(size=6) + + expected = stats.kendalltau(x, y, method='exact') + + def statistic1d(x): + return stats.kendalltau(x, y, method='asymptotic').statistic + + # kendalltau currently has only one alternative, two-sided + res = permutation_test((x,), statistic1d, permutation_type='pairings', + n_resamples=np.inf, random_state=self.rng) + + assert_allclose(res.statistic, expected.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected.pvalue, rtol=self.rtol) + + @pytest.mark.parametrize('alternative', ('less', 'greater', 'two-sided')) + def test_against_fisher_exact(self, alternative): + + def statistic(x,): + return np.sum((x == 1) & (y == 1)) + + # x and y are binary random variables with some dependence + rng = np.random.default_rng(6235696159000529929) + x = (rng.random(7) > 0.6).astype(float) + y = (rng.random(7) + 0.25*x > 0.6).astype(float) + tab = stats.contingency.crosstab(x, y)[1] + + res = permutation_test((x,), statistic, permutation_type='pairings', + n_resamples=np.inf, alternative=alternative, + random_state=rng) + res2 = stats.fisher_exact(tab, alternative=alternative) + + assert_allclose(res.pvalue, res2[1]) + + @pytest.mark.xslow() + @pytest.mark.parametrize('axis', (-2, 1)) + def test_vectorized_nsamp_ptype_samples(self, axis): + # Test that permutation_test with permutation_type='samples' works + # properly for a 3-sample statistic with nd array samples of different + # (but compatible) shapes and ndims. Show that exact permutation test + # reproduces SciPy's exact pvalue and that random permutation test + # approximates it. + + x = self.rng.random(size=(2, 4, 3)) + y = self.rng.random(size=(1, 4, 3)) + z = self.rng.random(size=(2, 4, 1)) + x = stats.rankdata(x, axis=axis) + y = stats.rankdata(y, axis=axis) + z = stats.rankdata(z, axis=axis) + y = y[0] # to check broadcast with different ndim + data = (x, y, z) + + def statistic1d(*data): + return stats.page_trend_test(data, ranked=True, + method='asymptotic').statistic + + def pvalue1d(*data): + return stats.page_trend_test(data, ranked=True, + method='exact').pvalue + + statistic = _resampling._vectorize_statistic(statistic1d) + pvalue = _resampling._vectorize_statistic(pvalue1d) + + expected_statistic = statistic(*np.broadcast_arrays(*data), axis=axis) + expected_pvalue = pvalue(*np.broadcast_arrays(*data), axis=axis) + + # Let's forgive this use of an integer seed, please. + kwds = {'vectorized': False, 'axis': axis, 'alternative': 'greater', + 'permutation_type': 'pairings', 'random_state': 0} + res = permutation_test(data, statistic1d, n_resamples=np.inf, **kwds) + res2 = permutation_test(data, statistic1d, n_resamples=5000, **kwds) + + assert_allclose(res.statistic, expected_statistic, rtol=self.rtol) + assert_allclose(res.statistic, res2.statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected_pvalue, rtol=self.rtol) + assert_allclose(res.pvalue, res2.pvalue, atol=3e-2) + + # -- Test Against External References -- # + + tie_case_1 = {'x': [1, 2, 3, 4], 'y': [1.5, 2, 2.5], + 'expected_less': 0.2000000000, + 'expected_2sided': 0.4, # 2*expected_less + 'expected_Pr_gte_S_mean': 0.3428571429, # see note below + 'expected_statistic': 7.5, + 'expected_avg': 9.142857, 'expected_std': 1.40698} + tie_case_2 = {'x': [111, 107, 100, 99, 102, 106, 109, 108], + 'y': [107, 108, 106, 98, 105, 103, 110, 105, 104], + 'expected_less': 0.1555738379, + 'expected_2sided': 0.3111476758, + 'expected_Pr_gte_S_mean': 0.2969971205, # see note below + 'expected_statistic': 32.5, + 'expected_avg': 38.117647, 'expected_std': 5.172124} + + @pytest.mark.xslow() # only the second case is slow, really + @pytest.mark.parametrize('case', (tie_case_1, tie_case_2)) + def test_with_ties(self, case): + """ + Results above from SAS PROC NPAR1WAY, e.g. + + DATA myData; + INPUT X Y; + CARDS; + 1 1 + 1 2 + 1 3 + 1 4 + 2 1.5 + 2 2 + 2 2.5 + ods graphics on; + proc npar1way AB data=myData; + class X; + EXACT; + run; + ods graphics off; + + Note: SAS provides Pr >= |S-Mean|, which is different from our + definition of a two-sided p-value. + + """ + + x = case['x'] + y = case['y'] + + expected_statistic = case['expected_statistic'] + expected_less = case['expected_less'] + expected_2sided = case['expected_2sided'] + expected_Pr_gte_S_mean = case['expected_Pr_gte_S_mean'] + expected_avg = case['expected_avg'] + expected_std = case['expected_std'] + + def statistic1d(x, y): + return stats.ansari(x, y).statistic + + with np.testing.suppress_warnings() as sup: + sup.filter(UserWarning, "Ties preclude use of exact statistic") + res = permutation_test((x, y), statistic1d, n_resamples=np.inf, + alternative='less') + res2 = permutation_test((x, y), statistic1d, n_resamples=np.inf, + alternative='two-sided') + + assert_allclose(res.statistic, expected_statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected_less, atol=1e-10) + assert_allclose(res2.pvalue, expected_2sided, atol=1e-10) + assert_allclose(res2.null_distribution.mean(), expected_avg, rtol=1e-6) + assert_allclose(res2.null_distribution.std(), expected_std, rtol=1e-6) + + # SAS provides Pr >= |S-Mean|; might as well check against that, too + S = res.statistic + mean = res.null_distribution.mean() + n = len(res.null_distribution) + Pr_gte_S_mean = np.sum(np.abs(res.null_distribution-mean) + >= np.abs(S-mean))/n + assert_allclose(expected_Pr_gte_S_mean, Pr_gte_S_mean) + + @pytest.mark.parametrize('alternative, expected_pvalue', + (('less', 0.9708333333333), + ('greater', 0.05138888888889), + ('two-sided', 0.1027777777778))) + def test_against_spearmanr_in_R(self, alternative, expected_pvalue): + """ + Results above from R cor.test, e.g. + + options(digits=16) + x <- c(1.76405235, 0.40015721, 0.97873798, + 2.2408932, 1.86755799, -0.97727788) + y <- c(2.71414076, 0.2488, 0.87551913, + 2.6514917, 2.01160156, 0.47699563) + cor.test(x, y, method = "spearm", alternative = "t") + """ + # data comes from + # np.random.seed(0) + # x = stats.norm.rvs(size=6) + # y = x + stats.norm.rvs(size=6) + x = [1.76405235, 0.40015721, 0.97873798, + 2.2408932, 1.86755799, -0.97727788] + y = [2.71414076, 0.2488, 0.87551913, + 2.6514917, 2.01160156, 0.47699563] + expected_statistic = 0.7714285714285715 + + def statistic1d(x): + return stats.spearmanr(x, y).statistic + + res = permutation_test((x,), statistic1d, permutation_type='pairings', + n_resamples=np.inf, alternative=alternative) + + assert_allclose(res.statistic, expected_statistic, rtol=self.rtol) + assert_allclose(res.pvalue, expected_pvalue, atol=1e-13) + + @pytest.mark.parametrize("batch", (-1, 0)) + def test_batch_generator_iv(self, batch): + with pytest.raises(ValueError, match="`batch` must be positive."): + list(_resampling._batch_generator([1, 2, 3], batch)) + + batch_generator_cases = [(range(0), 3, []), + (range(6), 3, [[0, 1, 2], [3, 4, 5]]), + (range(8), 3, [[0, 1, 2], [3, 4, 5], [6, 7]])] + + @pytest.mark.parametrize("iterable, batch, expected", + batch_generator_cases) + def test_batch_generator(self, iterable, batch, expected): + got = list(_resampling._batch_generator(iterable, batch)) + assert got == expected + + def test_finite_precision_statistic(self): + # Some statistics return numerically distinct values when the values + # should be equal in theory. Test that `permutation_test` accounts + # for this in some way. + x = [1, 2, 4, 3] + y = [2, 4, 6, 8] + + def statistic(x, y): + return stats.pearsonr(x, y)[0] + + res = stats.permutation_test((x, y), statistic, vectorized=False, + permutation_type='pairings') + r, pvalue, null = res.statistic, res.pvalue, res.null_distribution + + correct_p = 2 * np.sum(null >= r - 1e-14) / len(null) + assert pvalue == correct_p == 1/3 + # Compare against other exact correlation tests using R corr.test + # options(digits=16) + # x = c(1, 2, 4, 3) + # y = c(2, 4, 6, 8) + # cor.test(x, y, alternative = "t", method = "spearman") # 0.333333333 + # cor.test(x, y, alternative = "t", method = "kendall") # 0.333333333 + + +def test_all_partitions_concatenated(): + # make sure that _all_paritions_concatenated produces the correct number + # of partitions of the data into samples of the given sizes and that + # all are unique + n = np.array([3, 2, 4], dtype=int) + nc = np.cumsum(n) + + all_partitions = set() + counter = 0 + for partition_concatenated in _resampling._all_partitions_concatenated(n): + counter += 1 + partitioning = np.split(partition_concatenated, nc[:-1]) + all_partitions.add(tuple([frozenset(i) for i in partitioning])) + + expected = np.prod([special.binom(sum(n[i:]), sum(n[i+1:])) + for i in range(len(n)-1)]) + + assert_equal(counter, expected) + assert_equal(len(all_partitions), expected) + + +@pytest.mark.parametrize('fun_name', + ['bootstrap', 'permutation_test', 'monte_carlo_test']) +def test_parameter_vectorized(fun_name): + # Check that parameter `vectorized` is working as desired for all + # resampling functions. Results don't matter; just don't fail asserts. + rng = np.random.default_rng(75245098234592) + sample = rng.random(size=10) + + def rvs(size): # needed by `monte_carlo_test` + return stats.norm.rvs(size=size, random_state=rng) + + fun_options = {'bootstrap': {'data': (sample,), 'random_state': rng, + 'method': 'percentile'}, + 'permutation_test': {'data': (sample,), 'random_state': rng, + 'permutation_type': 'samples'}, + 'monte_carlo_test': {'sample': sample, 'rvs': rvs}} + common_options = {'n_resamples': 100} + + fun = getattr(stats, fun_name) + options = fun_options[fun_name] + options.update(common_options) + + def statistic(x, axis): + assert x.ndim > 1 or np.array_equal(x, sample) + return np.mean(x, axis=axis) + fun(statistic=statistic, vectorized=None, **options) + fun(statistic=statistic, vectorized=True, **options) + + def statistic(x): + assert x.ndim == 1 + return np.mean(x) + fun(statistic=statistic, vectorized=None, **options) + fun(statistic=statistic, vectorized=False, **options) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sampling.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..10b4e5af96ec070ef10ccd8e5d3e7db78f3a03e1 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sampling.py @@ -0,0 +1,1445 @@ +import threading +import pickle +import pytest +from copy import deepcopy +import platform +import sys +import math +import numpy as np +from numpy.testing import assert_allclose, assert_equal, suppress_warnings +from scipy.stats.sampling import ( + TransformedDensityRejection, + DiscreteAliasUrn, + DiscreteGuideTable, + NumericalInversePolynomial, + NumericalInverseHermite, + RatioUniforms, + SimpleRatioUniforms, + UNURANError +) +from pytest import raises as assert_raises +from scipy import stats +from scipy import special +from scipy.stats import chisquare, cramervonmises +from scipy.stats._distr_params import distdiscrete, distcont +from scipy._lib._util import check_random_state + + +# common test data: this data can be shared between all the tests. + + +# Normal distribution shared between all the continuous methods +class StandardNormal: + def pdf(self, x): + # normalization constant needed for NumericalInverseHermite + return 1./np.sqrt(2.*np.pi) * np.exp(-0.5 * x*x) + + def dpdf(self, x): + return 1./np.sqrt(2.*np.pi) * -x * np.exp(-0.5 * x*x) + + def cdf(self, x): + return special.ndtr(x) + + +all_methods = [ + ("TransformedDensityRejection", {"dist": StandardNormal()}), + ("DiscreteAliasUrn", {"dist": [0.02, 0.18, 0.8]}), + ("DiscreteGuideTable", {"dist": [0.02, 0.18, 0.8]}), + ("NumericalInversePolynomial", {"dist": StandardNormal()}), + ("NumericalInverseHermite", {"dist": StandardNormal()}), + ("SimpleRatioUniforms", {"dist": StandardNormal(), "mode": 0}) +] + +if (sys.implementation.name == 'pypy' + and sys.implementation.version < (7, 3, 10)): + # changed in PyPy for v7.3.10 + floaterr = r"unsupported operand type for float\(\): 'list'" +else: + floaterr = r"must be real number, not list" +# Make sure an internal error occurs in UNU.RAN when invalid callbacks are +# passed. Moreover, different generators throw different error messages. +# So, in case of an `UNURANError`, we do not validate the error message. +bad_pdfs_common = [ + # Negative PDF + (lambda x: -x, UNURANError, r"..."), + # Returning wrong type + (lambda x: [], TypeError, floaterr), + # Undefined name inside the function + (lambda x: foo, NameError, r"name 'foo' is not defined"), # type: ignore[name-defined] # noqa: F821, E501 + # Infinite value returned => Overflow error. + (lambda x: np.inf, UNURANError, r"..."), + # NaN value => internal error in UNU.RAN + (lambda x: np.nan, UNURANError, r"..."), + # signature of PDF wrong + (lambda: 1.0, TypeError, r"takes 0 positional arguments but 1 was given") +] + + +# same approach for dpdf +bad_dpdf_common = [ + # Infinite value returned. + (lambda x: np.inf, UNURANError, r"..."), + # NaN value => internal error in UNU.RAN + (lambda x: np.nan, UNURANError, r"..."), + # Returning wrong type + (lambda x: [], TypeError, floaterr), + # Undefined name inside the function + (lambda x: foo, NameError, r"name 'foo' is not defined"), # type: ignore[name-defined] # noqa: F821, E501 + # signature of dPDF wrong + (lambda: 1.0, TypeError, r"takes 0 positional arguments but 1 was given") +] + + +# same approach for logpdf +bad_logpdfs_common = [ + # Returning wrong type + (lambda x: [], TypeError, floaterr), + # Undefined name inside the function + (lambda x: foo, NameError, r"name 'foo' is not defined"), # type: ignore[name-defined] # noqa: F821, E501 + # Infinite value returned => Overflow error. + (lambda x: np.inf, UNURANError, r"..."), + # NaN value => internal error in UNU.RAN + (lambda x: np.nan, UNURANError, r"..."), + # signature of logpdf wrong + (lambda: 1.0, TypeError, r"takes 0 positional arguments but 1 was given") +] + + +bad_pv_common = [ + ([], r"must contain at least one element"), + ([[1.0, 0.0]], r"wrong number of dimensions \(expected 1, got 2\)"), + ([0.2, 0.4, np.nan, 0.8], r"must contain only finite / non-nan values"), + ([0.2, 0.4, np.inf, 0.8], r"must contain only finite / non-nan values"), + ([0.0, 0.0], r"must contain at least one non-zero value"), +] + + +# size of the domains is incorrect +bad_sized_domains = [ + # > 2 elements in the domain + ((1, 2, 3), ValueError, r"must be a length 2 tuple"), + # empty domain + ((), ValueError, r"must be a length 2 tuple") +] + +# domain values are incorrect +bad_domains = [ + ((2, 1), UNURANError, r"left >= right"), + ((1, 1), UNURANError, r"left >= right"), +] + +# infinite and nan values present in domain. +inf_nan_domains = [ + # left >= right + ((10, 10), UNURANError, r"left >= right"), + ((np.inf, np.inf), UNURANError, r"left >= right"), + ((-np.inf, -np.inf), UNURANError, r"left >= right"), + ((np.inf, -np.inf), UNURANError, r"left >= right"), + # Also include nans in some of the domains. + ((-np.inf, np.nan), ValueError, r"only non-nan values"), + ((np.nan, np.inf), ValueError, r"only non-nan values") +] + +# `nan` values present in domain. Some distributions don't support +# infinite tails, so don't mix the nan values with infinities. +nan_domains = [ + ((0, np.nan), ValueError, r"only non-nan values"), + ((np.nan, np.nan), ValueError, r"only non-nan values") +] + + +# all the methods should throw errors for nan, bad sized, and bad valued +# domains. +@pytest.mark.parametrize("domain, err, msg", + bad_domains + bad_sized_domains + + nan_domains) # type: ignore[operator] +@pytest.mark.parametrize("method, kwargs", all_methods) +def test_bad_domain(domain, err, msg, method, kwargs): + Method = getattr(stats.sampling, method) + with pytest.raises(err, match=msg): + Method(**kwargs, domain=domain) + + +@pytest.mark.parametrize("method, kwargs", all_methods) +def test_random_state(method, kwargs): + Method = getattr(stats.sampling, method) + + # simple seed that works for any version of NumPy + seed = 123 + rng1 = Method(**kwargs, random_state=seed) + rng2 = Method(**kwargs, random_state=seed) + assert_equal(rng1.rvs(100), rng2.rvs(100)) + + # global seed + np.random.seed(123) + rng1 = Method(**kwargs) + rvs1 = rng1.rvs(100) + np.random.seed(None) + rng2 = Method(**kwargs, random_state=123) + rvs2 = rng2.rvs(100) + assert_equal(rvs1, rvs2) + + # Generator seed for new NumPy + # when a RandomState is given, it should take the bitgen_t + # member of the class and create a Generator instance. + seed1 = np.random.RandomState(np.random.MT19937(123)) + seed2 = np.random.Generator(np.random.MT19937(123)) + rng1 = Method(**kwargs, random_state=seed1) + rng2 = Method(**kwargs, random_state=seed2) + assert_equal(rng1.rvs(100), rng2.rvs(100)) + + +def test_set_random_state(): + rng1 = TransformedDensityRejection(StandardNormal(), random_state=123) + rng2 = TransformedDensityRejection(StandardNormal()) + rng2.set_random_state(123) + assert_equal(rng1.rvs(100), rng2.rvs(100)) + rng = TransformedDensityRejection(StandardNormal(), random_state=123) + rvs1 = rng.rvs(100) + rng.set_random_state(123) + rvs2 = rng.rvs(100) + assert_equal(rvs1, rvs2) + + +def test_threading_behaviour(): + # Test if the API is thread-safe. + # This verifies if the lock mechanism and the use of `PyErr_Occurred` + # is correct. + errors = {"err1": None, "err2": None} + + class Distribution: + def __init__(self, pdf_msg): + self.pdf_msg = pdf_msg + + def pdf(self, x): + if 49.9 < x < 50.0: + raise ValueError(self.pdf_msg) + return x + + def dpdf(self, x): + return 1 + + def func1(): + dist = Distribution('foo') + rng = TransformedDensityRejection(dist, domain=(10, 100), + random_state=12) + try: + rng.rvs(100000) + except ValueError as e: + errors['err1'] = e.args[0] + + def func2(): + dist = Distribution('bar') + rng = TransformedDensityRejection(dist, domain=(10, 100), + random_state=2) + try: + rng.rvs(100000) + except ValueError as e: + errors['err2'] = e.args[0] + + t1 = threading.Thread(target=func1) + t2 = threading.Thread(target=func2) + + t1.start() + t2.start() + + t1.join() + t2.join() + + assert errors['err1'] == 'foo' + assert errors['err2'] == 'bar' + + +@pytest.mark.parametrize("method, kwargs", all_methods) +def test_pickle(method, kwargs): + Method = getattr(stats.sampling, method) + rng1 = Method(**kwargs, random_state=123) + obj = pickle.dumps(rng1) + rng2 = pickle.loads(obj) + assert_equal(rng1.rvs(100), rng2.rvs(100)) + + +@pytest.mark.parametrize("size", [None, 0, (0, ), 1, (10, 3), (2, 3, 4, 5), + (0, 0), (0, 1)]) +def test_rvs_size(size): + # As the `rvs` method is present in the base class and shared between + # all the classes, we can just test with one of the methods. + rng = TransformedDensityRejection(StandardNormal()) + if size is None: + assert np.isscalar(rng.rvs(size)) + else: + if np.isscalar(size): + size = (size, ) + assert rng.rvs(size).shape == size + + +def test_with_scipy_distribution(): + # test if the setup works with SciPy's rv_frozen distributions + dist = stats.norm() + urng = np.random.default_rng(0) + rng = NumericalInverseHermite(dist, random_state=urng) + u = np.linspace(0, 1, num=100) + check_cont_samples(rng, dist, dist.stats()) + assert_allclose(dist.ppf(u), rng.ppf(u)) + # test if it works with `loc` and `scale` + dist = stats.norm(loc=10., scale=5.) + rng = NumericalInverseHermite(dist, random_state=urng) + check_cont_samples(rng, dist, dist.stats()) + assert_allclose(dist.ppf(u), rng.ppf(u)) + # check for discrete distributions + dist = stats.binom(10, 0.2) + rng = DiscreteAliasUrn(dist, random_state=urng) + domain = dist.support() + pv = dist.pmf(np.arange(domain[0], domain[1]+1)) + check_discr_samples(rng, pv, dist.stats()) + + +def check_cont_samples(rng, dist, mv_ex, rtol=1e-7, atol=1e-1): + rvs = rng.rvs(100000) + mv = rvs.mean(), rvs.var() + # test the moments only if the variance is finite + if np.isfinite(mv_ex[1]): + assert_allclose(mv, mv_ex, rtol=rtol, atol=atol) + # Cramer Von Mises test for goodness-of-fit + rvs = rng.rvs(500) + dist.cdf = np.vectorize(dist.cdf) + pval = cramervonmises(rvs, dist.cdf).pvalue + assert pval > 0.1 + + +def check_discr_samples(rng, pv, mv_ex, rtol=1e-3, atol=1e-1): + rvs = rng.rvs(100000) + # test if the first few moments match + mv = rvs.mean(), rvs.var() + assert_allclose(mv, mv_ex, rtol=rtol, atol=atol) + # normalize + pv = pv / pv.sum() + # chi-squared test for goodness-of-fit + obs_freqs = np.zeros_like(pv) + _, freqs = np.unique(rvs, return_counts=True) + freqs = freqs / freqs.sum() + obs_freqs[:freqs.size] = freqs + pval = chisquare(obs_freqs, pv).pvalue + assert pval > 0.1 + + +def test_warning_center_not_in_domain(): + # UNURAN will warn if the center provided or the one computed w/o the + # domain is outside of the domain + msg = "102 : center moved into domain of distribution" + with pytest.warns(RuntimeWarning, match=msg): + NumericalInversePolynomial(StandardNormal(), center=0, domain=(3, 5)) + with pytest.warns(RuntimeWarning, match=msg): + NumericalInversePolynomial(StandardNormal(), domain=(3, 5)) + + +@pytest.mark.parametrize('method', ["SimpleRatioUniforms", + "NumericalInversePolynomial", + "TransformedDensityRejection"]) +def test_error_mode_not_in_domain(method): + # UNURAN raises an error if the mode is not in the domain + # the behavior is different compared to the case that center is not in the + # domain. mode is supposed to be the exact value, center can be an + # approximate value + Method = getattr(stats.sampling, method) + msg = "17 : mode not in domain" + with pytest.raises(UNURANError, match=msg): + Method(StandardNormal(), mode=0, domain=(3, 5)) + + +@pytest.mark.parametrize('method', ["NumericalInverseHermite", + "NumericalInversePolynomial"]) +class TestQRVS: + def test_input_validation(self, method): + match = "`qmc_engine` must be an instance of..." + with pytest.raises(ValueError, match=match): + Method = getattr(stats.sampling, method) + gen = Method(StandardNormal()) + gen.qrvs(qmc_engine=0) + + # issues with QMCEngines and old NumPy + Method = getattr(stats.sampling, method) + gen = Method(StandardNormal()) + + match = "`d` must be consistent with dimension of `qmc_engine`." + with pytest.raises(ValueError, match=match): + gen.qrvs(d=3, qmc_engine=stats.qmc.Halton(2)) + + qrngs = [None, stats.qmc.Sobol(1, seed=0), stats.qmc.Halton(3, seed=0)] + # `size=None` should not add anything to the shape, `size=1` should + sizes = [(None, tuple()), (1, (1,)), (4, (4,)), + ((4,), (4,)), ((2, 4), (2, 4))] # type: ignore + # Neither `d=None` nor `d=1` should add anything to the shape + ds = [(None, tuple()), (1, tuple()), (3, (3,))] + + @pytest.mark.parametrize('qrng', qrngs) + @pytest.mark.parametrize('size_in, size_out', sizes) + @pytest.mark.parametrize('d_in, d_out', ds) + def test_QRVS_shape_consistency(self, qrng, size_in, size_out, + d_in, d_out, method): + w32 = sys.platform == "win32" and platform.architecture()[0] == "32bit" + if w32 and method == "NumericalInversePolynomial": + pytest.xfail("NumericalInversePolynomial.qrvs fails for Win " + "32-bit") + + dist = StandardNormal() + Method = getattr(stats.sampling, method) + gen = Method(dist) + + # If d and qrng.d are inconsistent, an error is raised + if d_in is not None and qrng is not None and qrng.d != d_in: + match = "`d` must be consistent with dimension of `qmc_engine`." + with pytest.raises(ValueError, match=match): + gen.qrvs(size_in, d=d_in, qmc_engine=qrng) + return + + # Sometimes d is really determined by qrng + if d_in is None and qrng is not None and qrng.d != 1: + d_out = (qrng.d,) + + shape_expected = size_out + d_out + + qrng2 = deepcopy(qrng) + qrvs = gen.qrvs(size=size_in, d=d_in, qmc_engine=qrng) + if size_in is not None: + assert qrvs.shape == shape_expected + + if qrng2 is not None: + uniform = qrng2.random(np.prod(size_in) or 1) + qrvs2 = stats.norm.ppf(uniform).reshape(shape_expected) + assert_allclose(qrvs, qrvs2, atol=1e-12) + + def test_QRVS_size_tuple(self, method): + # QMCEngine samples are always of shape (n, d). When `size` is a tuple, + # we set `n = prod(size)` in the call to qmc_engine.random, transform + # the sample, and reshape it to the final dimensions. When we reshape, + # we need to be careful, because the _columns_ of the sample returned + # by a QMCEngine are "independent"-ish, but the elements within the + # columns are not. We need to make sure that this doesn't get mixed up + # by reshaping: qrvs[..., i] should remain "independent"-ish of + # qrvs[..., i+1], but the elements within qrvs[..., i] should be + # transformed from the same low-discrepancy sequence. + + dist = StandardNormal() + Method = getattr(stats.sampling, method) + gen = Method(dist) + + size = (3, 4) + d = 5 + qrng = stats.qmc.Halton(d, seed=0) + qrng2 = stats.qmc.Halton(d, seed=0) + + uniform = qrng2.random(np.prod(size)) + + qrvs = gen.qrvs(size=size, d=d, qmc_engine=qrng) + qrvs2 = stats.norm.ppf(uniform) + + for i in range(d): + sample = qrvs[..., i] + sample2 = qrvs2[:, i].reshape(size) + assert_allclose(sample, sample2, atol=1e-12) + + +class TestTransformedDensityRejection: + # Simple Custom Distribution + class dist0: + def pdf(self, x): + return 3/4 * (1-x*x) + + def dpdf(self, x): + return 3/4 * (-2*x) + + def cdf(self, x): + return 3/4 * (x - x**3/3 + 2/3) + + def support(self): + return -1, 1 + + # Standard Normal Distribution + class dist1: + def pdf(self, x): + return stats.norm._pdf(x / 0.1) + + def dpdf(self, x): + return -x / 0.01 * stats.norm._pdf(x / 0.1) + + def cdf(self, x): + return stats.norm._cdf(x / 0.1) + + # pdf with piecewise linear function as transformed density + # with T = -1/sqrt with shift. Taken from UNU.RAN test suite + # (from file t_tdr_ps.c) + class dist2: + def __init__(self, shift): + self.shift = shift + + def pdf(self, x): + x -= self.shift + y = 1. / (abs(x) + 1.) + return 0.5 * y * y + + def dpdf(self, x): + x -= self.shift + y = 1. / (abs(x) + 1.) + y = y * y * y + return y if (x < 0.) else -y + + def cdf(self, x): + x -= self.shift + if x <= 0.: + return 0.5 / (1. - x) + else: + return 1. - 0.5 / (1. + x) + + dists = [dist0(), dist1(), dist2(0.), dist2(10000.)] + + # exact mean and variance of the distributions in the list dists + mv0 = [0., 4./15.] + mv1 = [0., 0.01] + mv2 = [0., np.inf] + mv3 = [10000., np.inf] + mvs = [mv0, mv1, mv2, mv3] + + @pytest.mark.parametrize("dist, mv_ex", + zip(dists, mvs)) + def test_basic(self, dist, mv_ex): + with suppress_warnings() as sup: + # filter the warnings thrown by UNU.RAN + sup.filter(RuntimeWarning) + rng = TransformedDensityRejection(dist, random_state=42) + check_cont_samples(rng, dist, mv_ex) + + # PDF 0 everywhere => bad construction points + bad_pdfs = [(lambda x: 0, UNURANError, r"50 : bad construction points.")] + bad_pdfs += bad_pdfs_common # type: ignore[arg-type] + + @pytest.mark.parametrize("pdf, err, msg", bad_pdfs) + def test_bad_pdf(self, pdf, err, msg): + class dist: + pass + dist.pdf = pdf + dist.dpdf = lambda x: 1 # an arbitrary dPDF + with pytest.raises(err, match=msg): + TransformedDensityRejection(dist) + + @pytest.mark.parametrize("dpdf, err, msg", bad_dpdf_common) + def test_bad_dpdf(self, dpdf, err, msg): + class dist: + pass + dist.pdf = lambda x: x + dist.dpdf = dpdf + with pytest.raises(err, match=msg): + TransformedDensityRejection(dist, domain=(1, 10)) + + # test domains with inf + nan in them. need to write a custom test for + # this because not all methods support infinite tails. + @pytest.mark.parametrize("domain, err, msg", inf_nan_domains) + def test_inf_nan_domains(self, domain, err, msg): + with pytest.raises(err, match=msg): + TransformedDensityRejection(StandardNormal(), domain=domain) + + @pytest.mark.parametrize("construction_points", [-1, 0, 0.1]) + def test_bad_construction_points_scalar(self, construction_points): + with pytest.raises(ValueError, match=r"`construction_points` must be " + r"a positive integer."): + TransformedDensityRejection( + StandardNormal(), construction_points=construction_points + ) + + def test_bad_construction_points_array(self): + # empty array + construction_points = [] + with pytest.raises(ValueError, match=r"`construction_points` must " + r"either be a " + r"scalar or a non-empty array."): + TransformedDensityRejection( + StandardNormal(), construction_points=construction_points + ) + + # construction_points not monotonically increasing + construction_points = [1, 1, 1, 1, 1, 1] + with pytest.warns(RuntimeWarning, match=r"33 : starting points not " + r"strictly monotonically " + r"increasing"): + TransformedDensityRejection( + StandardNormal(), construction_points=construction_points + ) + + # construction_points containing nans + construction_points = [np.nan, np.nan, np.nan] + with pytest.raises(UNURANError, match=r"50 : bad construction " + r"points."): + TransformedDensityRejection( + StandardNormal(), construction_points=construction_points + ) + + # construction_points out of domain + construction_points = [-10, 10] + with pytest.warns(RuntimeWarning, match=r"50 : starting point out of " + r"domain"): + TransformedDensityRejection( + StandardNormal(), domain=(-3, 3), + construction_points=construction_points + ) + + @pytest.mark.parametrize("c", [-1., np.nan, np.inf, 0.1, 1.]) + def test_bad_c(self, c): + msg = r"`c` must either be -0.5 or 0." + with pytest.raises(ValueError, match=msg): + TransformedDensityRejection(StandardNormal(), c=-1.) + + u = [np.linspace(0, 1, num=1000), [], [[]], [np.nan], + [-np.inf, np.nan, np.inf], 0, + [[np.nan, 0.5, 0.1], [0.2, 0.4, np.inf], [-2, 3, 4]]] + + @pytest.mark.parametrize("u", u) + def test_ppf_hat(self, u): + # Increase the `max_squeeze_hat_ratio` so the ppf_hat is more + # accurate. + rng = TransformedDensityRejection(StandardNormal(), + max_squeeze_hat_ratio=0.9999) + # Older versions of NumPy throw RuntimeWarnings for comparisons + # with nan. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in greater") + sup.filter(RuntimeWarning, "invalid value encountered in " + "greater_equal") + sup.filter(RuntimeWarning, "invalid value encountered in less") + sup.filter(RuntimeWarning, "invalid value encountered in " + "less_equal") + res = rng.ppf_hat(u) + expected = stats.norm.ppf(u) + assert_allclose(res, expected, rtol=1e-3, atol=1e-5) + assert res.shape == expected.shape + + def test_bad_dist(self): + # Empty distribution + class dist: + ... + + msg = r"`pdf` required but not found." + with pytest.raises(ValueError, match=msg): + TransformedDensityRejection(dist) + + # dPDF not present in dist + class dist: + pdf = lambda x: 1-x*x # noqa: E731 + + msg = r"`dpdf` required but not found." + with pytest.raises(ValueError, match=msg): + TransformedDensityRejection(dist) + + +class TestDiscreteAliasUrn: + # DAU fails on these probably because of large domains and small + # computation errors in PMF. Mean/SD match but chi-squared test fails. + basic_fail_dists = { + 'nchypergeom_fisher', # numerical errors on tails + 'nchypergeom_wallenius', # numerical errors on tails + 'randint' # fails on 32-bit ubuntu + } + + @pytest.mark.parametrize("distname, params", distdiscrete) + def test_basic(self, distname, params): + if distname in self.basic_fail_dists: + msg = ("DAU fails on these probably because of large domains " + "and small computation errors in PMF.") + pytest.skip(msg) + if not isinstance(distname, str): + dist = distname + else: + dist = getattr(stats, distname) + dist = dist(*params) + domain = dist.support() + if not np.isfinite(domain[1] - domain[0]): + # DAU only works with finite domain. So, skip the distributions + # with infinite tails. + pytest.skip("DAU only works with a finite domain.") + k = np.arange(domain[0], domain[1]+1) + pv = dist.pmf(k) + mv_ex = dist.stats('mv') + rng = DiscreteAliasUrn(dist, random_state=42) + check_discr_samples(rng, pv, mv_ex) + + # Can't use bad_pmf_common here as we evaluate PMF early on to avoid + # unhelpful errors from UNU.RAN. + bad_pmf = [ + # inf returned + (lambda x: np.inf, ValueError, + r"must contain only finite / non-nan values"), + # nan returned + (lambda x: np.nan, ValueError, + r"must contain only finite / non-nan values"), + # all zeros + (lambda x: 0.0, ValueError, + r"must contain at least one non-zero value"), + # Undefined name inside the function + (lambda x: foo, NameError, # type: ignore[name-defined] # noqa: F821 + r"name 'foo' is not defined"), + # Returning wrong type. + (lambda x: [], ValueError, + r"setting an array element with a sequence."), + # probabilities < 0 + (lambda x: -x, UNURANError, + r"50 : probability < 0"), + # signature of PMF wrong + (lambda: 1.0, TypeError, + r"takes 0 positional arguments but 1 was given") + ] + + @pytest.mark.parametrize("pmf, err, msg", bad_pmf) + def test_bad_pmf(self, pmf, err, msg): + class dist: + pass + dist.pmf = pmf + with pytest.raises(err, match=msg): + DiscreteAliasUrn(dist, domain=(1, 10)) + + @pytest.mark.parametrize("pv", [[0.18, 0.02, 0.8], + [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]]) + def test_sampling_with_pv(self, pv): + pv = np.asarray(pv, dtype=np.float64) + rng = DiscreteAliasUrn(pv, random_state=123) + rng.rvs(100_000) + pv = pv / pv.sum() + variates = np.arange(0, len(pv)) + # test if the first few moments match + m_expected = np.average(variates, weights=pv) + v_expected = np.average((variates - m_expected) ** 2, weights=pv) + mv_expected = m_expected, v_expected + check_discr_samples(rng, pv, mv_expected) + + @pytest.mark.parametrize("pv, msg", bad_pv_common) + def test_bad_pv(self, pv, msg): + with pytest.raises(ValueError, match=msg): + DiscreteAliasUrn(pv) + + # DAU doesn't support infinite tails. So, it should throw an error when + # inf is present in the domain. + inf_domain = [(-np.inf, np.inf), (np.inf, np.inf), (-np.inf, -np.inf), + (0, np.inf), (-np.inf, 0)] + + @pytest.mark.parametrize("domain", inf_domain) + def test_inf_domain(self, domain): + with pytest.raises(ValueError, match=r"must be finite"): + DiscreteAliasUrn(stats.binom(10, 0.2), domain=domain) + + def test_bad_urn_factor(self): + with pytest.warns(RuntimeWarning, match=r"relative urn size < 1."): + DiscreteAliasUrn([0.5, 0.5], urn_factor=-1) + + def test_bad_args(self): + msg = (r"`domain` must be provided when the " + r"probability vector is not available.") + + class dist: + def pmf(self, x): + return x + + with pytest.raises(ValueError, match=msg): + DiscreteAliasUrn(dist) + + def test_gh19359(self): + pv = special.softmax(np.ones((1533,))) + rng = DiscreteAliasUrn(pv, random_state=42) + # check the correctness + check_discr_samples(rng, pv, (1532 / 2, (1532**2 - 1) / 12), + rtol=5e-3) + + +class TestNumericalInversePolynomial: + # Simple Custom Distribution + class dist0: + def pdf(self, x): + return 3/4 * (1-x*x) + + def cdf(self, x): + return 3/4 * (x - x**3/3 + 2/3) + + def support(self): + return -1, 1 + + # Standard Normal Distribution + class dist1: + def pdf(self, x): + return stats.norm._pdf(x / 0.1) + + def cdf(self, x): + return stats.norm._cdf(x / 0.1) + + # Sin 2 distribution + # / 0.05 + 0.45*(1 +sin(2 Pi x)) if |x| <= 1 + # f(x) = < + # \ 0 otherwise + # Taken from UNU.RAN test suite (from file t_pinv.c) + class dist2: + def pdf(self, x): + return 0.05 + 0.45 * (1 + np.sin(2*np.pi*x)) + + def cdf(self, x): + return (0.05*(x + 1) + + 0.9*(1. + 2.*np.pi*(1 + x) - np.cos(2.*np.pi*x)) / + (4.*np.pi)) + + def support(self): + return -1, 1 + + # Sin 10 distribution + # / 0.05 + 0.45*(1 +sin(2 Pi x)) if |x| <= 5 + # f(x) = < + # \ 0 otherwise + # Taken from UNU.RAN test suite (from file t_pinv.c) + class dist3: + def pdf(self, x): + return 0.2 * (0.05 + 0.45 * (1 + np.sin(2*np.pi*x))) + + def cdf(self, x): + return x/10. + 0.5 + 0.09/(2*np.pi) * (np.cos(10*np.pi) - + np.cos(2*np.pi*x)) + + def support(self): + return -5, 5 + + dists = [dist0(), dist1(), dist2(), dist3()] + + # exact mean and variance of the distributions in the list dists + mv0 = [0., 4./15.] + mv1 = [0., 0.01] + mv2 = [-0.45/np.pi, 2/3*0.5 - 0.45**2/np.pi**2] + mv3 = [-0.45/np.pi, 0.2 * 250/3 * 0.5 - 0.45**2/np.pi**2] + mvs = [mv0, mv1, mv2, mv3] + + @pytest.mark.parametrize("dist, mv_ex", + zip(dists, mvs)) + def test_basic(self, dist, mv_ex): + rng = NumericalInversePolynomial(dist, random_state=42) + check_cont_samples(rng, dist, mv_ex) + + @pytest.mark.xslow + @pytest.mark.parametrize("distname, params", distcont) + def test_basic_all_scipy_dists(self, distname, params): + + very_slow_dists = ['anglit', 'gausshyper', 'kappa4', + 'ksone', 'kstwo', 'levy_l', + 'levy_stable', 'studentized_range', + 'trapezoid', 'triang', 'vonmises'] + # for these distributions, some assertions fail due to minor + # numerical differences. They can be avoided either by changing + # the seed or by increasing the u_resolution. + fail_dists = ['chi2', 'fatiguelife', 'gibrat', + 'halfgennorm', 'lognorm', 'ncf', + 'ncx2', 'pareto', 't'] + # for these distributions, skip the check for agreement between sample + # moments and true moments. We cannot expect them to pass due to the + # high variance of sample moments. + skip_sample_moment_check = ['rel_breitwigner'] + + if distname in very_slow_dists: + pytest.skip(f"PINV too slow for {distname}") + if distname in fail_dists: + pytest.skip(f"PINV fails for {distname}") + dist = (getattr(stats, distname) + if isinstance(distname, str) + else distname) + dist = dist(*params) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + rng = NumericalInversePolynomial(dist, random_state=42) + if distname in skip_sample_moment_check: + return + check_cont_samples(rng, dist, [dist.mean(), dist.var()]) + + @pytest.mark.parametrize("pdf, err, msg", bad_pdfs_common) + def test_bad_pdf(self, pdf, err, msg): + class dist: + pass + dist.pdf = pdf + with pytest.raises(err, match=msg): + NumericalInversePolynomial(dist, domain=[0, 5]) + + @pytest.mark.parametrize("logpdf, err, msg", bad_logpdfs_common) + def test_bad_logpdf(self, logpdf, err, msg): + class dist: + pass + dist.logpdf = logpdf + with pytest.raises(err, match=msg): + NumericalInversePolynomial(dist, domain=[0, 5]) + + # test domains with inf + nan in them. need to write a custom test for + # this because not all methods support infinite tails. + @pytest.mark.parametrize("domain, err, msg", inf_nan_domains) + def test_inf_nan_domains(self, domain, err, msg): + with pytest.raises(err, match=msg): + NumericalInversePolynomial(StandardNormal(), domain=domain) + + u = [ + # test if quantile 0 and 1 return -inf and inf respectively and check + # the correctness of the PPF for equidistant points between 0 and 1. + np.linspace(0, 1, num=10000), + # test the PPF method for empty arrays + [], [[]], + # test if nans and infs return nan result. + [np.nan], [-np.inf, np.nan, np.inf], + # test if a scalar is returned for a scalar input. + 0, + # test for arrays with nans, values greater than 1 and less than 0, + # and some valid values. + [[np.nan, 0.5, 0.1], [0.2, 0.4, np.inf], [-2, 3, 4]] + ] + + @pytest.mark.parametrize("u", u) + def test_ppf(self, u): + dist = StandardNormal() + rng = NumericalInversePolynomial(dist, u_resolution=1e-14) + # Older versions of NumPy throw RuntimeWarnings for comparisons + # with nan. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in greater") + sup.filter(RuntimeWarning, "invalid value encountered in " + "greater_equal") + sup.filter(RuntimeWarning, "invalid value encountered in less") + sup.filter(RuntimeWarning, "invalid value encountered in " + "less_equal") + res = rng.ppf(u) + expected = stats.norm.ppf(u) + assert_allclose(res, expected, rtol=1e-11, atol=1e-11) + assert res.shape == expected.shape + + x = [np.linspace(-10, 10, num=10000), [], [[]], [np.nan], + [-np.inf, np.nan, np.inf], 0, + [[np.nan, 0.5, 0.1], [0.2, 0.4, np.inf], [-np.inf, 3, 4]]] + + @pytest.mark.parametrize("x", x) + def test_cdf(self, x): + dist = StandardNormal() + rng = NumericalInversePolynomial(dist, u_resolution=1e-14) + # Older versions of NumPy throw RuntimeWarnings for comparisons + # with nan. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in greater") + sup.filter(RuntimeWarning, "invalid value encountered in " + "greater_equal") + sup.filter(RuntimeWarning, "invalid value encountered in less") + sup.filter(RuntimeWarning, "invalid value encountered in " + "less_equal") + res = rng.cdf(x) + expected = stats.norm.cdf(x) + assert_allclose(res, expected, rtol=1e-11, atol=1e-11) + assert res.shape == expected.shape + + def test_u_error(self): + dist = StandardNormal() + rng = NumericalInversePolynomial(dist, u_resolution=1e-10) + max_error, mae = rng.u_error() + assert max_error < 1e-10 + assert mae <= max_error + rng = NumericalInversePolynomial(dist, u_resolution=1e-14) + max_error, mae = rng.u_error() + assert max_error < 1e-14 + assert mae <= max_error + + bad_orders = [1, 4.5, 20, np.inf, np.nan] + bad_u_resolution = [1e-20, 1e-1, np.inf, np.nan] + + @pytest.mark.parametrize("order", bad_orders) + def test_bad_orders(self, order): + dist = StandardNormal() + + msg = r"`order` must be an integer in the range \[3, 17\]." + with pytest.raises(ValueError, match=msg): + NumericalInversePolynomial(dist, order=order) + + @pytest.mark.parametrize("u_resolution", bad_u_resolution) + def test_bad_u_resolution(self, u_resolution): + msg = r"`u_resolution` must be between 1e-15 and 1e-5." + with pytest.raises(ValueError, match=msg): + NumericalInversePolynomial(StandardNormal(), + u_resolution=u_resolution) + + def test_bad_args(self): + + class BadDist: + def cdf(self, x): + return stats.norm._cdf(x) + + dist = BadDist() + msg = r"Either of the methods `pdf` or `logpdf` must be specified" + with pytest.raises(ValueError, match=msg): + rng = NumericalInversePolynomial(dist) + + dist = StandardNormal() + rng = NumericalInversePolynomial(dist) + msg = r"`sample_size` must be greater than or equal to 1000." + with pytest.raises(ValueError, match=msg): + rng.u_error(10) + + class Distribution: + def pdf(self, x): + return np.exp(-0.5 * x*x) + + dist = Distribution() + rng = NumericalInversePolynomial(dist) + msg = r"Exact CDF required but not found." + with pytest.raises(ValueError, match=msg): + rng.u_error() + + def test_logpdf_pdf_consistency(self): + # 1. check that PINV works with pdf and logpdf only + # 2. check that generated ppf is the same (up to a small tolerance) + + class MyDist: + pass + + # create generator from dist with only pdf + dist_pdf = MyDist() + dist_pdf.pdf = lambda x: math.exp(-x*x/2) + rng1 = NumericalInversePolynomial(dist_pdf) + + # create dist with only logpdf + dist_logpdf = MyDist() + dist_logpdf.logpdf = lambda x: -x*x/2 + rng2 = NumericalInversePolynomial(dist_logpdf) + + q = np.linspace(1e-5, 1-1e-5, num=100) + assert_allclose(rng1.ppf(q), rng2.ppf(q)) + + +class TestNumericalInverseHermite: + # / (1 +sin(2 Pi x))/2 if |x| <= 1 + # f(x) = < + # \ 0 otherwise + # Taken from UNU.RAN test suite (from file t_hinv.c) + class dist0: + def pdf(self, x): + return 0.5*(1. + np.sin(2.*np.pi*x)) + + def dpdf(self, x): + return np.pi*np.cos(2.*np.pi*x) + + def cdf(self, x): + return (1. + 2.*np.pi*(1 + x) - np.cos(2.*np.pi*x)) / (4.*np.pi) + + def support(self): + return -1, 1 + + # / Max(sin(2 Pi x)),0)Pi/2 if -1 < x <0.5 + # f(x) = < + # \ 0 otherwise + # Taken from UNU.RAN test suite (from file t_hinv.c) + class dist1: + def pdf(self, x): + if (x <= -0.5): + return np.sin((2. * np.pi) * x) * 0.5 * np.pi + if (x < 0.): + return 0. + if (x <= 0.5): + return np.sin((2. * np.pi) * x) * 0.5 * np.pi + + def dpdf(self, x): + if (x <= -0.5): + return np.cos((2. * np.pi) * x) * np.pi * np.pi + if (x < 0.): + return 0. + if (x <= 0.5): + return np.cos((2. * np.pi) * x) * np.pi * np.pi + + def cdf(self, x): + if (x <= -0.5): + return 0.25 * (1 - np.cos((2. * np.pi) * x)) + if (x < 0.): + return 0.5 + if (x <= 0.5): + return 0.75 - 0.25 * np.cos((2. * np.pi) * x) + + def support(self): + return -1, 0.5 + + dists = [dist0(), dist1()] + + # exact mean and variance of the distributions in the list dists + mv0 = [-1/(2*np.pi), 1/3 - 1/(4*np.pi*np.pi)] + mv1 = [-1/4, 3/8-1/(2*np.pi*np.pi) - 1/16] + mvs = [mv0, mv1] + + @pytest.mark.parametrize("dist, mv_ex", + zip(dists, mvs)) + @pytest.mark.parametrize("order", [3, 5]) + def test_basic(self, dist, mv_ex, order): + rng = NumericalInverseHermite(dist, order=order, random_state=42) + check_cont_samples(rng, dist, mv_ex) + + # test domains with inf + nan in them. need to write a custom test for + # this because not all methods support infinite tails. + @pytest.mark.parametrize("domain, err, msg", inf_nan_domains) + def test_inf_nan_domains(self, domain, err, msg): + with pytest.raises(err, match=msg): + NumericalInverseHermite(StandardNormal(), domain=domain) + + def basic_test_all_scipy_dists(self, distname, shapes): + slow_dists = {'ksone', 'kstwo', 'levy_stable', 'skewnorm'} + fail_dists = {'beta', 'gausshyper', 'geninvgauss', 'ncf', 'nct', + 'norminvgauss', 'genhyperbolic', 'studentized_range', + 'vonmises', 'kappa4', 'invgauss', 'wald'} + + if distname in slow_dists: + pytest.skip("Distribution is too slow") + if distname in fail_dists: + # specific reasons documented in gh-13319 + # https://github.com/scipy/scipy/pull/13319#discussion_r626188955 + pytest.xfail("Fails - usually due to inaccurate CDF/PDF") + + np.random.seed(0) + + dist = getattr(stats, distname)(*shapes) + fni = NumericalInverseHermite(dist) + + x = np.random.rand(10) + p_tol = np.max(np.abs(dist.ppf(x)-fni.ppf(x))/np.abs(dist.ppf(x))) + u_tol = np.max(np.abs(dist.cdf(fni.ppf(x)) - x)) + + assert p_tol < 1e-8 + assert u_tol < 1e-12 + + @pytest.mark.filterwarnings('ignore::RuntimeWarning') + @pytest.mark.xslow + @pytest.mark.parametrize(("distname", "shapes"), distcont) + def test_basic_all_scipy_dists(self, distname, shapes): + # if distname == "truncnorm": + # pytest.skip("Tested separately") + self.basic_test_all_scipy_dists(distname, shapes) + + @pytest.mark.filterwarnings('ignore::RuntimeWarning') + def test_basic_truncnorm_gh17155(self): + self.basic_test_all_scipy_dists("truncnorm", (0.1, 2)) + + def test_input_validation(self): + match = r"`order` must be either 1, 3, or 5." + with pytest.raises(ValueError, match=match): + NumericalInverseHermite(StandardNormal(), order=2) + + match = "`cdf` required but not found" + with pytest.raises(ValueError, match=match): + NumericalInverseHermite("norm") + + match = "could not convert string to float" + with pytest.raises(ValueError, match=match): + NumericalInverseHermite(StandardNormal(), + u_resolution='ekki') + + rngs = [None, 0, np.random.RandomState(0)] + rngs.append(np.random.default_rng(0)) # type: ignore + sizes = [(None, tuple()), (8, (8,)), ((4, 5, 6), (4, 5, 6))] + + @pytest.mark.parametrize('rng', rngs) + @pytest.mark.parametrize('size_in, size_out', sizes) + def test_RVS(self, rng, size_in, size_out): + dist = StandardNormal() + fni = NumericalInverseHermite(dist) + + rng2 = deepcopy(rng) + rvs = fni.rvs(size=size_in, random_state=rng) + if size_in is not None: + assert rvs.shape == size_out + + if rng2 is not None: + rng2 = check_random_state(rng2) + uniform = rng2.uniform(size=size_in) + rvs2 = stats.norm.ppf(uniform) + assert_allclose(rvs, rvs2) + + def test_inaccurate_CDF(self): + # CDF function with inaccurate tail cannot be inverted; see gh-13319 + # https://github.com/scipy/scipy/pull/13319#discussion_r626188955 + shapes = (2.3098496451481823, 0.6268795430096368) + match = ("98 : one or more intervals very short; possibly due to " + "numerical problems with a pole or very flat tail") + + # fails with default tol + with pytest.warns(RuntimeWarning, match=match): + NumericalInverseHermite(stats.beta(*shapes)) + + # no error with coarser tol + NumericalInverseHermite(stats.beta(*shapes), u_resolution=1e-8) + + def test_custom_distribution(self): + dist1 = StandardNormal() + fni1 = NumericalInverseHermite(dist1) + + dist2 = stats.norm() + fni2 = NumericalInverseHermite(dist2) + + assert_allclose(fni1.rvs(random_state=0), fni2.rvs(random_state=0)) + + u = [ + # check the correctness of the PPF for equidistant points between + # 0.02 and 0.98. + np.linspace(0., 1., num=10000), + # test the PPF method for empty arrays + [], [[]], + # test if nans and infs return nan result. + [np.nan], [-np.inf, np.nan, np.inf], + # test if a scalar is returned for a scalar input. + 0, + # test for arrays with nans, values greater than 1 and less than 0, + # and some valid values. + [[np.nan, 0.5, 0.1], [0.2, 0.4, np.inf], [-2, 3, 4]] + ] + + @pytest.mark.parametrize("u", u) + def test_ppf(self, u): + dist = StandardNormal() + rng = NumericalInverseHermite(dist, u_resolution=1e-12) + # Older versions of NumPy throw RuntimeWarnings for comparisons + # with nan. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in greater") + sup.filter(RuntimeWarning, "invalid value encountered in " + "greater_equal") + sup.filter(RuntimeWarning, "invalid value encountered in less") + sup.filter(RuntimeWarning, "invalid value encountered in " + "less_equal") + res = rng.ppf(u) + expected = stats.norm.ppf(u) + assert_allclose(res, expected, rtol=1e-9, atol=3e-10) + assert res.shape == expected.shape + + def test_u_error(self): + dist = StandardNormal() + rng = NumericalInverseHermite(dist, u_resolution=1e-10) + max_error, mae = rng.u_error() + assert max_error < 1e-10 + assert mae <= max_error + with suppress_warnings() as sup: + # ignore warning about u-resolution being too small. + sup.filter(RuntimeWarning) + rng = NumericalInverseHermite(dist, u_resolution=1e-14) + max_error, mae = rng.u_error() + assert max_error < 1e-14 + assert mae <= max_error + + +class TestDiscreteGuideTable: + basic_fail_dists = { + 'nchypergeom_fisher', # numerical errors on tails + 'nchypergeom_wallenius', # numerical errors on tails + 'randint' # fails on 32-bit ubuntu + } + + def test_guide_factor_gt3_raises_warning(self): + pv = [0.1, 0.3, 0.6] + urng = np.random.default_rng() + with pytest.warns(RuntimeWarning): + DiscreteGuideTable(pv, random_state=urng, guide_factor=7) + + def test_guide_factor_zero_raises_warning(self): + pv = [0.1, 0.3, 0.6] + urng = np.random.default_rng() + with pytest.warns(RuntimeWarning): + DiscreteGuideTable(pv, random_state=urng, guide_factor=0) + + def test_negative_guide_factor_raises_warning(self): + # This occurs from the UNU.RAN wrapper automatically. + # however it already gives a useful warning + # Here we just test that a warning is raised. + pv = [0.1, 0.3, 0.6] + urng = np.random.default_rng() + with pytest.warns(RuntimeWarning): + DiscreteGuideTable(pv, random_state=urng, guide_factor=-1) + + @pytest.mark.parametrize("distname, params", distdiscrete) + def test_basic(self, distname, params): + if distname in self.basic_fail_dists: + msg = ("DGT fails on these probably because of large domains " + "and small computation errors in PMF.") + pytest.skip(msg) + + if not isinstance(distname, str): + dist = distname + else: + dist = getattr(stats, distname) + + dist = dist(*params) + domain = dist.support() + + if not np.isfinite(domain[1] - domain[0]): + # DGT only works with finite domain. So, skip the distributions + # with infinite tails. + pytest.skip("DGT only works with a finite domain.") + + k = np.arange(domain[0], domain[1]+1) + pv = dist.pmf(k) + mv_ex = dist.stats('mv') + rng = DiscreteGuideTable(dist, random_state=42) + check_discr_samples(rng, pv, mv_ex) + + u = [ + # the correctness of the PPF for equidistant points between 0 and 1. + np.linspace(0, 1, num=10000), + # test the PPF method for empty arrays + [], [[]], + # test if nans and infs return nan result. + [np.nan], [-np.inf, np.nan, np.inf], + # test if a scalar is returned for a scalar input. + 0, + # test for arrays with nans, values greater than 1 and less than 0, + # and some valid values. + [[np.nan, 0.5, 0.1], [0.2, 0.4, np.inf], [-2, 3, 4]] + ] + + @pytest.mark.parametrize('u', u) + def test_ppf(self, u): + n, p = 4, 0.1 + dist = stats.binom(n, p) + rng = DiscreteGuideTable(dist, random_state=42) + + # Older versions of NumPy throw RuntimeWarnings for comparisons + # with nan. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in greater") + sup.filter(RuntimeWarning, "invalid value encountered in " + "greater_equal") + sup.filter(RuntimeWarning, "invalid value encountered in less") + sup.filter(RuntimeWarning, "invalid value encountered in " + "less_equal") + + res = rng.ppf(u) + expected = stats.binom.ppf(u, n, p) + assert_equal(res.shape, expected.shape) + assert_equal(res, expected) + + @pytest.mark.parametrize("pv, msg", bad_pv_common) + def test_bad_pv(self, pv, msg): + with pytest.raises(ValueError, match=msg): + DiscreteGuideTable(pv) + + # DGT doesn't support infinite tails. So, it should throw an error when + # inf is present in the domain. + inf_domain = [(-np.inf, np.inf), (np.inf, np.inf), (-np.inf, -np.inf), + (0, np.inf), (-np.inf, 0)] + + @pytest.mark.parametrize("domain", inf_domain) + def test_inf_domain(self, domain): + with pytest.raises(ValueError, match=r"must be finite"): + DiscreteGuideTable(stats.binom(10, 0.2), domain=domain) + + +class TestSimpleRatioUniforms: + # pdf with piecewise linear function as transformed density + # with T = -1/sqrt with shift. Taken from UNU.RAN test suite + # (from file t_srou.c) + class dist: + def __init__(self, shift): + self.shift = shift + self.mode = shift + + def pdf(self, x): + x -= self.shift + y = 1. / (abs(x) + 1.) + return 0.5 * y * y + + def cdf(self, x): + x -= self.shift + if x <= 0.: + return 0.5 / (1. - x) + else: + return 1. - 0.5 / (1. + x) + + dists = [dist(0.), dist(10000.)] + + # exact mean and variance of the distributions in the list dists + mv1 = [0., np.inf] + mv2 = [10000., np.inf] + mvs = [mv1, mv2] + + @pytest.mark.parametrize("dist, mv_ex", + zip(dists, mvs)) + def test_basic(self, dist, mv_ex): + rng = SimpleRatioUniforms(dist, mode=dist.mode, random_state=42) + check_cont_samples(rng, dist, mv_ex) + rng = SimpleRatioUniforms(dist, mode=dist.mode, + cdf_at_mode=dist.cdf(dist.mode), + random_state=42) + check_cont_samples(rng, dist, mv_ex) + + # test domains with inf + nan in them. need to write a custom test for + # this because not all methods support infinite tails. + @pytest.mark.parametrize("domain, err, msg", inf_nan_domains) + def test_inf_nan_domains(self, domain, err, msg): + with pytest.raises(err, match=msg): + SimpleRatioUniforms(StandardNormal(), domain=domain) + + def test_bad_args(self): + # pdf_area < 0 + with pytest.raises(ValueError, match=r"`pdf_area` must be > 0"): + SimpleRatioUniforms(StandardNormal(), mode=0, pdf_area=-1) + + +class TestRatioUniforms: + """ Tests for rvs_ratio_uniforms. + """ + + def test_rv_generation(self): + # use KS test to check distribution of rvs + # normal distribution + f = stats.norm.pdf + v = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) + u = np.sqrt(f(0)) + gen = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=12345) + assert_equal(stats.kstest(gen.rvs(2500), 'norm')[1] > 0.25, True) + + # exponential distribution + gen = RatioUniforms(lambda x: np.exp(-x), umax=1, + vmin=0, vmax=2*np.exp(-1), random_state=12345) + assert_equal(stats.kstest(gen.rvs(1000), 'expon')[1] > 0.25, True) + + def test_shape(self): + # test shape of return value depending on size parameter + f = stats.norm.pdf + v = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) + u = np.sqrt(f(0)) + + gen1 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + gen2 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + gen3 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + r1, r2, r3 = gen1.rvs(3), gen2.rvs((3,)), gen3.rvs((3, 1)) + assert_equal(r1, r2) + assert_equal(r2, r3.flatten()) + assert_equal(r1.shape, (3,)) + assert_equal(r3.shape, (3, 1)) + + gen4 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=12) + gen5 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=12) + r4, r5 = gen4.rvs(size=(3, 3, 3)), gen5.rvs(size=27) + assert_equal(r4.flatten(), r5) + assert_equal(r4.shape, (3, 3, 3)) + + gen6 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + gen7 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + gen8 = RatioUniforms(f, umax=u, vmin=-v, vmax=v, random_state=1234) + r6, r7, r8 = gen6.rvs(), gen7.rvs(1), gen8.rvs((1,)) + assert_equal(r6, r7) + assert_equal(r7, r8) + + def test_random_state(self): + f = stats.norm.pdf + v = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) + umax = np.sqrt(f(0)) + gen1 = RatioUniforms(f, umax=umax, vmin=-v, vmax=v, random_state=1234) + r1 = gen1.rvs(10) + np.random.seed(1234) + gen2 = RatioUniforms(f, umax=umax, vmin=-v, vmax=v) + r2 = gen2.rvs(10) + assert_equal(r1, r2) + + def test_exceptions(self): + f = stats.norm.pdf + # need vmin < vmax + with assert_raises(ValueError, match="vmin must be smaller than vmax"): + RatioUniforms(pdf=f, umax=1, vmin=3, vmax=1) + with assert_raises(ValueError, match="vmin must be smaller than vmax"): + RatioUniforms(pdf=f, umax=1, vmin=1, vmax=1) + # need umax > 0 + with assert_raises(ValueError, match="umax must be positive"): + RatioUniforms(pdf=f, umax=-1, vmin=1, vmax=3) + with assert_raises(ValueError, match="umax must be positive"): + RatioUniforms(pdf=f, umax=0, vmin=1, vmax=3) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sensitivity_analysis.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sensitivity_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..ac05c8c9a3f5d84016caf902f028f5cbdb76b889 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_sensitivity_analysis.py @@ -0,0 +1,300 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_array_less +import pytest + +from scipy import stats +from scipy.stats import sobol_indices +from scipy.stats._resampling import BootstrapResult +from scipy.stats._sensitivity_analysis import ( + BootstrapSobolResult, f_ishigami, sample_AB, sample_A_B +) + + +@pytest.fixture(scope='session') +def ishigami_ref_indices(): + """Reference values for Ishigami from Saltelli2007. + + Chapter 4, exercise 5 pages 179-182. + """ + a = 7. + b = 0.1 + + var = 0.5 + a**2/8 + b*np.pi**4/5 + b**2*np.pi**8/18 + v1 = 0.5 + b*np.pi**4/5 + b**2*np.pi**8/50 + v2 = a**2/8 + v3 = 0 + v12 = 0 + # v13: mistake in the book, see other derivations e.g. in 10.1002/nme.4856 + v13 = b**2*np.pi**8*8/225 + v23 = 0 + + s_first = np.array([v1, v2, v3])/var + s_second = np.array([ + [0., 0., v13], + [v12, 0., v23], + [v13, v23, 0.] + ])/var + s_total = s_first + s_second.sum(axis=1) + + return s_first, s_total + + +def f_ishigami_vec(x): + """Output of shape (2, n).""" + res = f_ishigami(x) + return res, res + + +class TestSobolIndices: + + dists = [ + stats.uniform(loc=-np.pi, scale=2*np.pi) # type: ignore[attr-defined] + ] * 3 + + def test_sample_AB(self): + # (d, n) + A = np.array( + [[1, 4, 7, 10], + [2, 5, 8, 11], + [3, 6, 9, 12]] + ) + B = A + 100 + # (d, d, n) + ref = np.array( + [[[101, 104, 107, 110], + [2, 5, 8, 11], + [3, 6, 9, 12]], + [[1, 4, 7, 10], + [102, 105, 108, 111], + [3, 6, 9, 12]], + [[1, 4, 7, 10], + [2, 5, 8, 11], + [103, 106, 109, 112]]] + ) + AB = sample_AB(A=A, B=B) + assert_allclose(AB, ref) + + @pytest.mark.xfail_on_32bit("Can't create large array for test") + @pytest.mark.parametrize( + 'func', + [f_ishigami, pytest.param(f_ishigami_vec, marks=pytest.mark.slow)], + ids=['scalar', 'vector'] + ) + def test_ishigami(self, ishigami_ref_indices, func): + rng = np.random.default_rng(28631265345463262246170309650372465332) + res = sobol_indices( + func=func, n=4096, + dists=self.dists, + random_state=rng + ) + + if func.__name__ == 'f_ishigami_vec': + ishigami_ref_indices = [ + [ishigami_ref_indices[0], ishigami_ref_indices[0]], + [ishigami_ref_indices[1], ishigami_ref_indices[1]] + ] + + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-2) + assert_allclose(res.total_order, ishigami_ref_indices[1], atol=1e-2) + + assert res._bootstrap_result is None + bootstrap_res = res.bootstrap(n_resamples=99) + assert isinstance(bootstrap_res, BootstrapSobolResult) + assert isinstance(res._bootstrap_result, BootstrapResult) + + assert res._bootstrap_result.confidence_interval.low.shape[0] == 2 + assert res._bootstrap_result.confidence_interval.low[1].shape \ + == res.first_order.shape + + assert bootstrap_res.first_order.confidence_interval.low.shape \ + == res.first_order.shape + assert bootstrap_res.total_order.confidence_interval.low.shape \ + == res.total_order.shape + + assert_array_less( + bootstrap_res.first_order.confidence_interval.low, res.first_order + ) + assert_array_less( + res.first_order, bootstrap_res.first_order.confidence_interval.high + ) + assert_array_less( + bootstrap_res.total_order.confidence_interval.low, res.total_order + ) + assert_array_less( + res.total_order, bootstrap_res.total_order.confidence_interval.high + ) + + # call again to use previous results and change a param + assert isinstance( + res.bootstrap(confidence_level=0.9, n_resamples=99), + BootstrapSobolResult + ) + assert isinstance(res._bootstrap_result, BootstrapResult) + + def test_func_dict(self, ishigami_ref_indices): + rng = np.random.default_rng(28631265345463262246170309650372465332) + n = 4096 + dists = [ + stats.uniform(loc=-np.pi, scale=2*np.pi), + stats.uniform(loc=-np.pi, scale=2*np.pi), + stats.uniform(loc=-np.pi, scale=2*np.pi) + ] + + A, B = sample_A_B(n=n, dists=dists, random_state=rng) + AB = sample_AB(A=A, B=B) + + func = { + 'f_A': f_ishigami(A).reshape(1, -1), + 'f_B': f_ishigami(B).reshape(1, -1), + 'f_AB': f_ishigami(AB).reshape((3, 1, -1)) + } + + res = sobol_indices( + func=func, n=n, + dists=dists, + random_state=rng + ) + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-2) + + res = sobol_indices( + func=func, n=n, + random_state=rng + ) + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-2) + + def test_method(self, ishigami_ref_indices): + def jansen_sobol(f_A, f_B, f_AB): + """Jansen for S and Sobol' for St. + + From Saltelli2010, table 2 formulations (c) and (e).""" + var = np.var([f_A, f_B], axis=(0, -1)) + + s = (var - 0.5*np.mean((f_B - f_AB)**2, axis=-1)) / var + st = np.mean(f_A*(f_A - f_AB), axis=-1) / var + + return s.T, st.T + + rng = np.random.default_rng(28631265345463262246170309650372465332) + res = sobol_indices( + func=f_ishigami, n=4096, + dists=self.dists, + method=jansen_sobol, + random_state=rng + ) + + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-2) + assert_allclose(res.total_order, ishigami_ref_indices[1], atol=1e-2) + + def jansen_sobol_typed( + f_A: np.ndarray, f_B: np.ndarray, f_AB: np.ndarray + ) -> tuple[np.ndarray, np.ndarray]: + return jansen_sobol(f_A, f_B, f_AB) + + _ = sobol_indices( + func=f_ishigami, n=8, + dists=self.dists, + method=jansen_sobol_typed, + random_state=rng + ) + + def test_normalization(self, ishigami_ref_indices): + rng = np.random.default_rng(28631265345463262246170309650372465332) + res = sobol_indices( + func=lambda x: f_ishigami(x) + 1000, n=4096, + dists=self.dists, + random_state=rng + ) + + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-2) + assert_allclose(res.total_order, ishigami_ref_indices[1], atol=1e-2) + + def test_constant_function(self, ishigami_ref_indices): + + def f_ishigami_vec_const(x): + """Output of shape (3, n).""" + res = f_ishigami(x) + return res, res * 0 + 10, res + + rng = np.random.default_rng(28631265345463262246170309650372465332) + res = sobol_indices( + func=f_ishigami_vec_const, n=4096, + dists=self.dists, + random_state=rng + ) + + ishigami_vec_indices = [ + [ishigami_ref_indices[0], [0, 0, 0], ishigami_ref_indices[0]], + [ishigami_ref_indices[1], [0, 0, 0], ishigami_ref_indices[1]] + ] + + assert_allclose(res.first_order, ishigami_vec_indices[0], atol=1e-2) + assert_allclose(res.total_order, ishigami_vec_indices[1], atol=1e-2) + + @pytest.mark.xfail_on_32bit("Can't create large array for test") + def test_more_converged(self, ishigami_ref_indices): + rng = np.random.default_rng(28631265345463262246170309650372465332) + res = sobol_indices( + func=f_ishigami, n=2**19, # 524288 + dists=self.dists, + random_state=rng + ) + + assert_allclose(res.first_order, ishigami_ref_indices[0], atol=1e-4) + assert_allclose(res.total_order, ishigami_ref_indices[1], atol=1e-4) + + def test_raises(self): + + message = r"Each distribution in `dists` must have method `ppf`" + with pytest.raises(ValueError, match=message): + sobol_indices(n=0, func=f_ishigami, dists="uniform") + + with pytest.raises(ValueError, match=message): + sobol_indices(n=0, func=f_ishigami, dists=[lambda x: x]) + + message = r"The balance properties of Sobol'" + with pytest.raises(ValueError, match=message): + sobol_indices(n=7, func=f_ishigami, dists=[stats.uniform()]) + + with pytest.raises(ValueError, match=message): + sobol_indices(n=4.1, func=f_ishigami, dists=[stats.uniform()]) + + message = r"'toto' is not a valid 'method'" + with pytest.raises(ValueError, match=message): + sobol_indices(n=0, func=f_ishigami, method='toto') + + message = r"must have the following signature" + with pytest.raises(ValueError, match=message): + sobol_indices(n=0, func=f_ishigami, method=lambda x: x) + + message = r"'dists' must be defined when 'func' is a callable" + with pytest.raises(ValueError, match=message): + sobol_indices(n=0, func=f_ishigami) + + def func_wrong_shape_output(x): + return x.reshape(-1, 1) + + message = r"'func' output should have a shape" + with pytest.raises(ValueError, match=message): + sobol_indices( + n=2, func=func_wrong_shape_output, dists=[stats.uniform()] + ) + + message = r"When 'func' is a dictionary" + with pytest.raises(ValueError, match=message): + sobol_indices( + n=2, func={'f_A': [], 'f_AB': []}, dists=[stats.uniform()] + ) + + with pytest.raises(ValueError, match=message): + # f_B malformed + sobol_indices( + n=2, + func={'f_A': [1, 2], 'f_B': [3], 'f_AB': [5, 6, 7, 8]}, + ) + + with pytest.raises(ValueError, match=message): + # f_AB malformed + sobol_indices( + n=2, + func={'f_A': [1, 2], 'f_B': [3, 4], 'f_AB': [5, 6, 7]}, + ) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_stats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_stats.py new file mode 100644 index 0000000000000000000000000000000000000000..a1600a95e560933438cc3ca245f36f117407fe0f --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_stats.py @@ -0,0 +1,8677 @@ +""" Test functions for stats module + + WRITTEN BY LOUIS LUANGKESORN FOR THE STATS MODULE + BASED ON WILKINSON'S STATISTICS QUIZ + https://www.stanford.edu/~clint/bench/wilk.txt + + Additional tests by a host of SciPy developers. +""" +import os +import re +import warnings +from collections import namedtuple +from itertools import product +import hypothesis.extra.numpy as npst +import hypothesis +import contextlib + +from numpy.testing import (assert_, assert_equal, + assert_almost_equal, assert_array_almost_equal, + assert_array_equal, assert_approx_equal, + assert_allclose, assert_warns, suppress_warnings, + assert_array_less) +import pytest +from pytest import raises as assert_raises +import numpy.ma.testutils as mat +from numpy import array, arange, float32, float64, power +import numpy as np + +import scipy.stats as stats +import scipy.stats.mstats as mstats +import scipy.stats._mstats_basic as mstats_basic +from scipy.stats._ksstats import kolmogn +from scipy.special._testutils import FuncData +from scipy.special import binom +from scipy import optimize +from .common_tests import check_named_results +from scipy.spatial.distance import cdist +from scipy.stats._axis_nan_policy import _broadcast_concatenate +from scipy.stats._stats_py import _permutation_distribution_t +from scipy._lib._util import AxisError + + +""" Numbers in docstrings beginning with 'W' refer to the section numbers + and headings found in the STATISTICS QUIZ of Leland Wilkinson. These are + considered to be essential functionality. True testing and + evaluation of a statistics package requires use of the + NIST Statistical test data. See McCoullough(1999) Assessing The Reliability + of Statistical Software for a test methodology and its + implementation in testing SAS, SPSS, and S-Plus +""" + +# Datasets +# These data sets are from the nasty.dat sets used by Wilkinson +# For completeness, I should write the relevant tests and count them as failures +# Somewhat acceptable, since this is still beta software. It would count as a +# good target for 1.0 status +X = array([1,2,3,4,5,6,7,8,9], float) +ZERO = array([0,0,0,0,0,0,0,0,0], float) +BIG = array([99999991,99999992,99999993,99999994,99999995,99999996,99999997, + 99999998,99999999], float) +LITTLE = array([0.99999991,0.99999992,0.99999993,0.99999994,0.99999995,0.99999996, + 0.99999997,0.99999998,0.99999999], float) +HUGE = array([1e+12,2e+12,3e+12,4e+12,5e+12,6e+12,7e+12,8e+12,9e+12], float) +TINY = array([1e-12,2e-12,3e-12,4e-12,5e-12,6e-12,7e-12,8e-12,9e-12], float) +ROUND = array([0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5], float) + + +class TestTrimmedStats: + # TODO: write these tests to handle missing values properly + dprec = np.finfo(np.float64).precision + + def test_tmean(self): + y = stats.tmean(X, (2, 8), (True, True)) + assert_approx_equal(y, 5.0, significant=self.dprec) + + y1 = stats.tmean(X, limits=(2, 8), inclusive=(False, False)) + y2 = stats.tmean(X, limits=None) + assert_approx_equal(y1, y2, significant=self.dprec) + + x_2d = arange(63, dtype=float64).reshape(9, 7) + y = stats.tmean(x_2d, axis=None) + assert_approx_equal(y, x_2d.mean(), significant=self.dprec) + + y = stats.tmean(x_2d, axis=0) + assert_array_almost_equal(y, x_2d.mean(axis=0), decimal=8) + + y = stats.tmean(x_2d, axis=1) + assert_array_almost_equal(y, x_2d.mean(axis=1), decimal=8) + + y = stats.tmean(x_2d, limits=(2, 61), axis=None) + assert_approx_equal(y, 31.5, significant=self.dprec) + + y = stats.tmean(x_2d, limits=(2, 21), axis=0) + y_true = [14, 11.5, 9, 10, 11, 12, 13] + assert_array_almost_equal(y, y_true, decimal=8) + + y = stats.tmean(x_2d, limits=(2, 21), inclusive=(True, False), axis=0) + y_true = [10.5, 11.5, 9, 10, 11, 12, 13] + assert_array_almost_equal(y, y_true, decimal=8) + + x_2d_with_nan = np.array(x_2d) + x_2d_with_nan[-1, -3:] = np.nan + y = stats.tmean(x_2d_with_nan, limits=(1, 13), axis=0) + y_true = [7, 4.5, 5.5, 6.5, np.nan, np.nan, np.nan] + assert_array_almost_equal(y, y_true, decimal=8) + + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "Mean of empty slice") + + y = stats.tmean(x_2d, limits=(2, 21), axis=1) + y_true = [4, 10, 17, 21, np.nan, np.nan, np.nan, np.nan, np.nan] + assert_array_almost_equal(y, y_true, decimal=8) + + y = stats.tmean(x_2d, limits=(2, 21), + inclusive=(False, True), axis=1) + y_true = [4.5, 10, 17, 21, np.nan, np.nan, np.nan, np.nan, np.nan] + assert_array_almost_equal(y, y_true, decimal=8) + + def test_tvar(self): + y = stats.tvar(X, limits=(2, 8), inclusive=(True, True)) + assert_approx_equal(y, 4.6666666666666661, significant=self.dprec) + + y = stats.tvar(X, limits=None) + assert_approx_equal(y, X.var(ddof=1), significant=self.dprec) + + x_2d = arange(63, dtype=float64).reshape((9, 7)) + y = stats.tvar(x_2d, axis=None) + assert_approx_equal(y, x_2d.var(ddof=1), significant=self.dprec) + + y = stats.tvar(x_2d, axis=0) + assert_array_almost_equal(y[0], np.full((1, 7), 367.50000000), decimal=8) + + y = stats.tvar(x_2d, axis=1) + assert_array_almost_equal(y[0], np.full((1, 9), 4.66666667), decimal=8) + + y = stats.tvar(x_2d[3, :]) + assert_approx_equal(y, 4.666666666666667, significant=self.dprec) + + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "Degrees of freedom <= 0 for slice.") + + # Limiting some values along one axis + y = stats.tvar(x_2d, limits=(1, 5), axis=1, inclusive=(True, True)) + assert_approx_equal(y[0], 2.5, significant=self.dprec) + + # Limiting all values along one axis + y = stats.tvar(x_2d, limits=(0, 6), axis=1, inclusive=(True, True)) + assert_approx_equal(y[0], 4.666666666666667, significant=self.dprec) + assert_equal(y[1], np.nan) + + def test_tstd(self): + y = stats.tstd(X, (2, 8), (True, True)) + assert_approx_equal(y, 2.1602468994692865, significant=self.dprec) + + y = stats.tstd(X, limits=None) + assert_approx_equal(y, X.std(ddof=1), significant=self.dprec) + + def test_tmin(self): + assert_equal(stats.tmin(4), 4) + + x = np.arange(10) + assert_equal(stats.tmin(x), 0) + assert_equal(stats.tmin(x, lowerlimit=0), 0) + assert_equal(stats.tmin(x, lowerlimit=0, inclusive=False), 1) + + x = x.reshape((5, 2)) + assert_equal(stats.tmin(x, lowerlimit=0, inclusive=False), [2, 1]) + assert_equal(stats.tmin(x, axis=1), [0, 2, 4, 6, 8]) + assert_equal(stats.tmin(x, axis=None), 0) + + x = np.arange(10.) + x[9] = np.nan + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "invalid value*") + assert_equal(stats.tmin(x), np.nan) + assert_equal(stats.tmin(x, nan_policy='omit'), 0.) + assert_raises(ValueError, stats.tmin, x, nan_policy='raise') + assert_raises(ValueError, stats.tmin, x, nan_policy='foobar') + msg = "'propagate', 'raise', 'omit'" + with assert_raises(ValueError, match=msg): + stats.tmin(x, nan_policy='foo') + + # check that that if a full slice is masked, the output returns a + # nan instead of a garbage value. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "All-NaN slice encountered") + x = np.arange(16).reshape(4, 4) + res = stats.tmin(x, lowerlimit=4, axis=1) + assert_equal(res, [np.nan, 4, 8, 12]) + + def test_tmax(self): + assert_equal(stats.tmax(4), 4) + + x = np.arange(10) + assert_equal(stats.tmax(x), 9) + assert_equal(stats.tmax(x, upperlimit=9), 9) + assert_equal(stats.tmax(x, upperlimit=9, inclusive=False), 8) + + x = x.reshape((5, 2)) + assert_equal(stats.tmax(x, upperlimit=9, inclusive=False), [8, 7]) + assert_equal(stats.tmax(x, axis=1), [1, 3, 5, 7, 9]) + assert_equal(stats.tmax(x, axis=None), 9) + + x = np.arange(10.) + x[6] = np.nan + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "invalid value*") + assert_equal(stats.tmax(x), np.nan) + assert_equal(stats.tmax(x, nan_policy='omit'), 9.) + assert_raises(ValueError, stats.tmax, x, nan_policy='raise') + assert_raises(ValueError, stats.tmax, x, nan_policy='foobar') + + # check that that if a full slice is masked, the output returns a + # nan instead of a garbage value. + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "All-NaN slice encountered") + x = np.arange(16).reshape(4, 4) + res = stats.tmax(x, upperlimit=11, axis=1) + assert_equal(res, [3, 7, 11, np.nan]) + + def test_tsem(self): + y = stats.tsem(X, limits=(3, 8), inclusive=(False, True)) + y_ref = np.array([4, 5, 6, 7, 8]) + assert_approx_equal(y, y_ref.std(ddof=1) / np.sqrt(y_ref.size), + significant=self.dprec) + + assert_approx_equal(stats.tsem(X, limits=[-1, 10]), + stats.tsem(X, limits=None), + significant=self.dprec) + + +class TestCorrPearsonr: + """ W.II.D. Compute a correlation matrix on all the variables. + + All the correlations, except for ZERO and MISS, should be exactly 1. + ZERO and MISS should have undefined or missing correlations with the + other variables. The same should go for SPEARMAN correlations, if + your program has them. + """ + + def test_pXX(self): + y = stats.pearsonr(X,X) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pXBIG(self): + y = stats.pearsonr(X,BIG) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pXLITTLE(self): + y = stats.pearsonr(X,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pXHUGE(self): + y = stats.pearsonr(X,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pXTINY(self): + y = stats.pearsonr(X,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pXROUND(self): + y = stats.pearsonr(X,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pBIGBIG(self): + y = stats.pearsonr(BIG,BIG) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pBIGLITTLE(self): + y = stats.pearsonr(BIG,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pBIGHUGE(self): + y = stats.pearsonr(BIG,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pBIGTINY(self): + y = stats.pearsonr(BIG,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pBIGROUND(self): + y = stats.pearsonr(BIG,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pLITTLELITTLE(self): + y = stats.pearsonr(LITTLE,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pLITTLEHUGE(self): + y = stats.pearsonr(LITTLE,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pLITTLETINY(self): + y = stats.pearsonr(LITTLE,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pLITTLEROUND(self): + y = stats.pearsonr(LITTLE,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pHUGEHUGE(self): + y = stats.pearsonr(HUGE,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pHUGETINY(self): + y = stats.pearsonr(HUGE,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pHUGEROUND(self): + y = stats.pearsonr(HUGE,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pTINYTINY(self): + y = stats.pearsonr(TINY,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pTINYROUND(self): + y = stats.pearsonr(TINY,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pROUNDROUND(self): + y = stats.pearsonr(ROUND,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_pearsonr_result_attributes(self): + res = stats.pearsonr(X, X) + attributes = ('correlation', 'pvalue') + check_named_results(res, attributes) + assert_equal(res.correlation, res.statistic) + + def test_r_almost_exactly_pos1(self): + a = arange(3.0) + r, prob = stats.pearsonr(a, a) + + assert_allclose(r, 1.0, atol=1e-15) + # With n = len(a) = 3, the error in prob grows like the + # square root of the error in r. + assert_allclose(prob, 0.0, atol=np.sqrt(2*np.spacing(1.0))) + + def test_r_almost_exactly_neg1(self): + a = arange(3.0) + r, prob = stats.pearsonr(a, -a) + + assert_allclose(r, -1.0, atol=1e-15) + # With n = len(a) = 3, the error in prob grows like the + # square root of the error in r. + assert_allclose(prob, 0.0, atol=np.sqrt(2*np.spacing(1.0))) + + def test_basic(self): + # A basic test, with a correlation coefficient + # that is not 1 or -1. + a = array([-1, 0, 1]) + b = array([0, 0, 3]) + r, prob = stats.pearsonr(a, b) + assert_approx_equal(r, np.sqrt(3)/2) + assert_approx_equal(prob, 1/3) + + def test_constant_input(self): + # Zero variance input + # See https://github.com/scipy/scipy/issues/3728 + msg = "An input array is constant" + with pytest.warns(stats.ConstantInputWarning, match=msg): + r, p = stats.pearsonr([0.667, 0.667, 0.667], [0.123, 0.456, 0.789]) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + + def test_near_constant_input(self): + # Near constant input (but not constant): + x = [2, 2, 2 + np.spacing(2)] + y = [3, 3, 3 + 6*np.spacing(3)] + msg = "An input array is nearly constant; the computed" + with pytest.warns(stats.NearConstantInputWarning, match=msg): + # r and p are garbage, so don't bother checking them in this case. + # (The exact value of r would be 1.) + r, p = stats.pearsonr(x, y) + + def test_very_small_input_values(self): + # Very small values in an input. A naive implementation will + # suffer from underflow. + # See https://github.com/scipy/scipy/issues/9353 + x = [0.004434375, 0.004756007, 0.003911996, 0.0038005, 0.003409971] + y = [2.48e-188, 7.41e-181, 4.09e-208, 2.08e-223, 2.66e-245] + r, p = stats.pearsonr(x,y) + + # The expected values were computed using mpmath with 80 digits + # of precision. + assert_allclose(r, 0.7272930540750450) + assert_allclose(p, 0.1637805429533202) + + def test_very_large_input_values(self): + # Very large values in an input. A naive implementation will + # suffer from overflow. + # See https://github.com/scipy/scipy/issues/8980 + x = 1e90*np.array([0, 0, 0, 1, 1, 1, 1]) + y = 1e90*np.arange(7) + + r, p = stats.pearsonr(x, y) + + # The expected values were computed using mpmath with 80 digits + # of precision. + assert_allclose(r, 0.8660254037844386) + assert_allclose(p, 0.011724811003954638) + + def test_extremely_large_input_values(self): + # Extremely large values in x and y. These values would cause the + # product sigma_x * sigma_y to overflow if the two factors were + # computed independently. + x = np.array([2.3e200, 4.5e200, 6.7e200, 8e200]) + y = np.array([1.2e199, 5.5e200, 3.3e201, 1.0e200]) + r, p = stats.pearsonr(x, y) + + # The expected values were computed using mpmath with 80 digits + # of precision. + assert_allclose(r, 0.351312332103289) + assert_allclose(p, 0.648687667896711) + + def test_length_two_pos1(self): + # Inputs with length 2. + # See https://github.com/scipy/scipy/issues/7730 + res = stats.pearsonr([1, 2], [3, 5]) + r, p = res + assert_equal(r, 1) + assert_equal(p, 1) + assert_equal(res.confidence_interval(), (-1, 1)) + + def test_length_two_neg2(self): + # Inputs with length 2. + # See https://github.com/scipy/scipy/issues/7730 + r, p = stats.pearsonr([2, 1], [3, 5]) + assert_equal(r, -1) + assert_equal(p, 1) + + # Expected values computed with R 3.6.2 cor.test, e.g. + # options(digits=16) + # x <- c(1, 2, 3, 4) + # y <- c(0, 1, 0.5, 1) + # cor.test(x, y, method = "pearson", alternative = "g") + # correlation coefficient and p-value for alternative='two-sided' + # calculated with mpmath agree to 16 digits. + @pytest.mark.parametrize('alternative, pval, rlow, rhigh, sign', + [('two-sided', 0.325800137536, -0.814938968841, 0.99230697523, 1), + ('less', 0.8370999312316, -1, 0.985600937290653, 1), + ('greater', 0.1629000687684, -0.6785654158217636, 1, 1), + ('two-sided', 0.325800137536, -0.992306975236, 0.81493896884, -1), + ('less', 0.1629000687684, -1.0, 0.6785654158217636, -1), + ('greater', 0.8370999312316, -0.985600937290653, 1.0, -1)]) + def test_basic_example(self, alternative, pval, rlow, rhigh, sign): + x = [1, 2, 3, 4] + y = np.array([0, 1, 0.5, 1]) * sign + result = stats.pearsonr(x, y, alternative=alternative) + assert_allclose(result.statistic, 0.6741998624632421*sign, rtol=1e-12) + assert_allclose(result.pvalue, pval, rtol=1e-6) + ci = result.confidence_interval() + assert_allclose(ci, (rlow, rhigh), rtol=1e-6) + + def test_negative_correlation_pvalue_gh17795(self): + x = np.arange(10) + y = -x + test_greater = stats.pearsonr(x, y, alternative='greater') + test_less = stats.pearsonr(x, y, alternative='less') + assert_allclose(test_greater.pvalue, 1) + assert_allclose(test_less.pvalue, 0, atol=1e-20) + + def test_length3_r_exactly_negative_one(self): + x = [1, 2, 3] + y = [5, -4, -13] + res = stats.pearsonr(x, y) + + # The expected r and p are exact. + r, p = res + assert_allclose(r, -1.0) + assert_allclose(p, 0.0, atol=1e-7) + + assert_equal(res.confidence_interval(), (-1, 1)) + + def test_unequal_lengths(self): + x = [1, 2, 3] + y = [4, 5] + assert_raises(ValueError, stats.pearsonr, x, y) + + def test_len1(self): + x = [1] + y = [2] + assert_raises(ValueError, stats.pearsonr, x, y) + + def test_complex_data(self): + x = [-1j, -2j, -3.0j] + y = [-1j, -2j, -3.0j] + message = 'This function does not support complex data' + with pytest.raises(ValueError, match=message): + stats.pearsonr(x, y) + + @pytest.mark.xslow + @pytest.mark.parametrize('alternative', ('less', 'greater', 'two-sided')) + @pytest.mark.parametrize('method', ('permutation', 'monte_carlo')) + def test_resampling_pvalue(self, method, alternative): + rng = np.random.default_rng(24623935790378923) + size = 100 if method == 'permutation' else 1000 + x = rng.normal(size=size) + y = rng.normal(size=size) + methods = {'permutation': stats.PermutationMethod(random_state=rng), + 'monte_carlo': stats.MonteCarloMethod(rvs=(rng.normal,)*2)} + method = methods[method] + res = stats.pearsonr(x, y, alternative=alternative, method=method) + ref = stats.pearsonr(x, y, alternative=alternative) + assert_allclose(res.statistic, ref.statistic, rtol=1e-15) + assert_allclose(res.pvalue, ref.pvalue, rtol=1e-2, atol=1e-3) + + @pytest.mark.xslow + @pytest.mark.parametrize('alternative', ('less', 'greater', 'two-sided')) + def test_bootstrap_ci(self, alternative): + rng = np.random.default_rng(24623935790378923) + x = rng.normal(size=100) + y = rng.normal(size=100) + res = stats.pearsonr(x, y, alternative=alternative) + + method = stats.BootstrapMethod(random_state=rng) + res_ci = res.confidence_interval(method=method) + ref_ci = res.confidence_interval() + + assert_allclose(res_ci, ref_ci, atol=1e-2) + + def test_invalid_method(self): + message = "`method` must be an instance of..." + with pytest.raises(ValueError, match=message): + stats.pearsonr([1, 2], [3, 4], method="asymptotic") + + res = stats.pearsonr([1, 2], [3, 4]) + with pytest.raises(ValueError, match=message): + res.confidence_interval(method="exact") + + +class TestFisherExact: + """Some tests to show that fisher_exact() works correctly. + + Note that in SciPy 0.9.0 this was not working well for large numbers due to + inaccuracy of the hypergeom distribution (see #1218). Fixed now. + + Also note that R and SciPy have different argument formats for their + hypergeometric distribution functions. + + R: + > phyper(18999, 99000, 110000, 39000, lower.tail = FALSE) + [1] 1.701815e-09 + """ + + def test_basic(self): + fisher_exact = stats.fisher_exact + + res = fisher_exact([[14500, 20000], [30000, 40000]])[1] + assert_approx_equal(res, 0.01106, significant=4) + res = fisher_exact([[100, 2], [1000, 5]])[1] + assert_approx_equal(res, 0.1301, significant=4) + res = fisher_exact([[2, 7], [8, 2]])[1] + assert_approx_equal(res, 0.0230141, significant=6) + res = fisher_exact([[5, 1], [10, 10]])[1] + assert_approx_equal(res, 0.1973244, significant=6) + res = fisher_exact([[5, 15], [20, 20]])[1] + assert_approx_equal(res, 0.0958044, significant=6) + res = fisher_exact([[5, 16], [20, 25]])[1] + assert_approx_equal(res, 0.1725862, significant=6) + res = fisher_exact([[10, 5], [10, 1]])[1] + assert_approx_equal(res, 0.1973244, significant=6) + res = fisher_exact([[5, 0], [1, 4]])[1] + assert_approx_equal(res, 0.04761904, significant=6) + res = fisher_exact([[0, 1], [3, 2]])[1] + assert_approx_equal(res, 1.0) + res = fisher_exact([[0, 2], [6, 4]])[1] + assert_approx_equal(res, 0.4545454545) + res = fisher_exact([[2, 7], [8, 2]]) + assert_approx_equal(res[1], 0.0230141, significant=6) + assert_approx_equal(res[0], 4.0 / 56) + + def test_precise(self): + # results from R + # + # R defines oddsratio differently (see Notes section of fisher_exact + # docstring), so those will not match. We leave them in anyway, in + # case they will be useful later on. We test only the p-value. + tablist = [ + ([[100, 2], [1000, 5]], (2.505583993422285e-001, 1.300759363430016e-001)), + ([[2, 7], [8, 2]], (8.586235135736206e-002, 2.301413756522114e-002)), + ([[5, 1], [10, 10]], (4.725646047336584e+000, 1.973244147157190e-001)), + ([[5, 15], [20, 20]], (3.394396617440852e-001, 9.580440012477637e-002)), + ([[5, 16], [20, 25]], (3.960558326183334e-001, 1.725864953812994e-001)), + ([[10, 5], [10, 1]], (2.116112781158483e-001, 1.973244147157190e-001)), + ([[10, 5], [10, 0]], (0.000000000000000e+000, 6.126482213438734e-002)), + ([[5, 0], [1, 4]], (np.inf, 4.761904761904762e-002)), + ([[0, 5], [1, 4]], (0.000000000000000e+000, 1.000000000000000e+000)), + ([[5, 1], [0, 4]], (np.inf, 4.761904761904758e-002)), + ([[0, 1], [3, 2]], (0.000000000000000e+000, 1.000000000000000e+000)) + ] + for table, res_r in tablist: + res = stats.fisher_exact(np.asarray(table)) + np.testing.assert_almost_equal(res[1], res_r[1], decimal=11, + verbose=True) + + def test_gh4130(self): + # Previously, a fudge factor used to distinguish between theoeretically + # and numerically different probability masses was 1e-4; it has been + # tightened to fix gh4130. Accuracy checked against R fisher.test. + # options(digits=16) + # table <- matrix(c(6, 108, 37, 200), nrow = 2) + # fisher.test(table, alternative = "t") + x = [[6, 37], [108, 200]] + res = stats.fisher_exact(x) + assert_allclose(res[1], 0.005092697748126) + + # case from https://github.com/brentp/fishers_exact_test/issues/27 + # That package has an (absolute?) fudge factor of 1e-6; too big + x = [[22, 0], [0, 102]] + res = stats.fisher_exact(x) + assert_allclose(res[1], 7.175066786244549e-25) + + # case from https://github.com/brentp/fishers_exact_test/issues/1 + x = [[94, 48], [3577, 16988]] + res = stats.fisher_exact(x) + assert_allclose(res[1], 2.069356340993818e-37) + + def test_gh9231(self): + # Previously, fisher_exact was extremely slow for this table + # As reported in gh-9231, the p-value should be very nearly zero + x = [[5829225, 5692693], [5760959, 5760959]] + res = stats.fisher_exact(x) + assert_allclose(res[1], 0, atol=1e-170) + + @pytest.mark.slow + def test_large_numbers(self): + # Test with some large numbers. Regression test for #1401 + pvals = [5.56e-11, 2.666e-11, 1.363e-11] # from R + for pval, num in zip(pvals, [75, 76, 77]): + res = stats.fisher_exact([[17704, 496], [1065, num]])[1] + assert_approx_equal(res, pval, significant=4) + + res = stats.fisher_exact([[18000, 80000], [20000, 90000]])[1] + assert_approx_equal(res, 0.2751, significant=4) + + def test_raises(self): + # test we raise an error for wrong shape of input. + assert_raises(ValueError, stats.fisher_exact, + np.arange(6).reshape(2, 3)) + + def test_row_or_col_zero(self): + tables = ([[0, 0], [5, 10]], + [[5, 10], [0, 0]], + [[0, 5], [0, 10]], + [[5, 0], [10, 0]]) + for table in tables: + oddsratio, pval = stats.fisher_exact(table) + assert_equal(pval, 1.0) + assert_equal(oddsratio, np.nan) + + def test_less_greater(self): + tables = ( + # Some tables to compare with R: + [[2, 7], [8, 2]], + [[200, 7], [8, 300]], + [[28, 21], [6, 1957]], + [[190, 800], [200, 900]], + # Some tables with simple exact values + # (includes regression test for ticket #1568): + [[0, 2], [3, 0]], + [[1, 1], [2, 1]], + [[2, 0], [1, 2]], + [[0, 1], [2, 3]], + [[1, 0], [1, 4]], + ) + pvals = ( + # from R: + [0.018521725952066501, 0.9990149169715733], + [1.0, 2.0056578803889148e-122], + [1.0, 5.7284374608319831e-44], + [0.7416227, 0.2959826], + # Exact: + [0.1, 1.0], + [0.7, 0.9], + [1.0, 0.3], + [2./3, 1.0], + [1.0, 1./3], + ) + for table, pval in zip(tables, pvals): + res = [] + res.append(stats.fisher_exact(table, alternative="less")[1]) + res.append(stats.fisher_exact(table, alternative="greater")[1]) + assert_allclose(res, pval, atol=0, rtol=1e-7) + + def test_gh3014(self): + # check if issue #3014 has been fixed. + # before, this would have risen a ValueError + odds, pvalue = stats.fisher_exact([[1, 2], [9, 84419233]]) + + @pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater']) + def test_result(self, alternative): + table = np.array([[14500, 20000], [30000, 40000]]) + res = stats.fisher_exact(table, alternative=alternative) + assert_equal((res.statistic, res.pvalue), res) + + +class TestCorrSpearmanr: + """ W.II.D. Compute a correlation matrix on all the variables. + + All the correlations, except for ZERO and MISS, should be exactly 1. + ZERO and MISS should have undefined or missing correlations with the + other variables. The same should go for SPEARMAN correlations, if + your program has them. + """ + + def test_scalar(self): + y = stats.spearmanr(4., 2.) + assert_(np.isnan(y).all()) + + def test_uneven_lengths(self): + assert_raises(ValueError, stats.spearmanr, [1, 2, 1], [8, 9]) + assert_raises(ValueError, stats.spearmanr, [1, 2, 1], 8) + + def test_uneven_2d_shapes(self): + # Different number of columns should work - those just get concatenated. + np.random.seed(232324) + x = np.random.randn(4, 3) + y = np.random.randn(4, 2) + assert stats.spearmanr(x, y).statistic.shape == (5, 5) + assert stats.spearmanr(x.T, y.T, axis=1).pvalue.shape == (5, 5) + + assert_raises(ValueError, stats.spearmanr, x, y, axis=1) + assert_raises(ValueError, stats.spearmanr, x.T, y.T) + + def test_ndim_too_high(self): + np.random.seed(232324) + x = np.random.randn(4, 3, 2) + assert_raises(ValueError, stats.spearmanr, x) + assert_raises(ValueError, stats.spearmanr, x, x) + assert_raises(ValueError, stats.spearmanr, x, None, None) + # But should work with axis=None (raveling axes) for two input arrays + assert_allclose(stats.spearmanr(x, x, axis=None), + stats.spearmanr(x.flatten(), x.flatten(), axis=0)) + + def test_nan_policy(self): + x = np.arange(10.) + x[9] = np.nan + assert_array_equal(stats.spearmanr(x, x), (np.nan, np.nan)) + assert_array_equal(stats.spearmanr(x, x, nan_policy='omit'), + (1.0, 0.0)) + assert_raises(ValueError, stats.spearmanr, x, x, nan_policy='raise') + assert_raises(ValueError, stats.spearmanr, x, x, nan_policy='foobar') + + def test_nan_policy_bug_12458(self): + np.random.seed(5) + x = np.random.rand(5, 10) + k = 6 + x[:, k] = np.nan + y = np.delete(x, k, axis=1) + corx, px = stats.spearmanr(x, nan_policy='omit') + cory, py = stats.spearmanr(y) + corx = np.delete(np.delete(corx, k, axis=1), k, axis=0) + px = np.delete(np.delete(px, k, axis=1), k, axis=0) + assert_allclose(corx, cory, atol=1e-14) + assert_allclose(px, py, atol=1e-14) + + def test_nan_policy_bug_12411(self): + np.random.seed(5) + m = 5 + n = 10 + x = np.random.randn(m, n) + x[1, 0] = np.nan + x[3, -1] = np.nan + corr, pvalue = stats.spearmanr(x, axis=1, nan_policy="propagate") + res = [[stats.spearmanr(x[i, :], x[j, :]).statistic for i in range(m)] + for j in range(m)] + assert_allclose(corr, res) + + def test_sXX(self): + y = stats.spearmanr(X,X) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sXBIG(self): + y = stats.spearmanr(X,BIG) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sXLITTLE(self): + y = stats.spearmanr(X,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sXHUGE(self): + y = stats.spearmanr(X,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sXTINY(self): + y = stats.spearmanr(X,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sXROUND(self): + y = stats.spearmanr(X,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sBIGBIG(self): + y = stats.spearmanr(BIG,BIG) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sBIGLITTLE(self): + y = stats.spearmanr(BIG,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sBIGHUGE(self): + y = stats.spearmanr(BIG,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sBIGTINY(self): + y = stats.spearmanr(BIG,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sBIGROUND(self): + y = stats.spearmanr(BIG,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sLITTLELITTLE(self): + y = stats.spearmanr(LITTLE,LITTLE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sLITTLEHUGE(self): + y = stats.spearmanr(LITTLE,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sLITTLETINY(self): + y = stats.spearmanr(LITTLE,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sLITTLEROUND(self): + y = stats.spearmanr(LITTLE,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sHUGEHUGE(self): + y = stats.spearmanr(HUGE,HUGE) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sHUGETINY(self): + y = stats.spearmanr(HUGE,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sHUGEROUND(self): + y = stats.spearmanr(HUGE,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sTINYTINY(self): + y = stats.spearmanr(TINY,TINY) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sTINYROUND(self): + y = stats.spearmanr(TINY,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_sROUNDROUND(self): + y = stats.spearmanr(ROUND,ROUND) + r = y[0] + assert_approx_equal(r,1.0) + + def test_spearmanr_result_attributes(self): + res = stats.spearmanr(X, X) + attributes = ('correlation', 'pvalue') + check_named_results(res, attributes) + assert_equal(res.correlation, res.statistic) + + def test_1d_vs_2d(self): + x1 = [1, 2, 3, 4, 5, 6] + x2 = [1, 2, 3, 4, 6, 5] + res1 = stats.spearmanr(x1, x2) + res2 = stats.spearmanr(np.asarray([x1, x2]).T) + assert_allclose(res1, res2) + + def test_1d_vs_2d_nans(self): + # Now the same with NaNs present. Regression test for gh-9103. + for nan_policy in ['propagate', 'omit']: + x1 = [1, np.nan, 3, 4, 5, 6] + x2 = [1, 2, 3, 4, 6, np.nan] + res1 = stats.spearmanr(x1, x2, nan_policy=nan_policy) + res2 = stats.spearmanr(np.asarray([x1, x2]).T, nan_policy=nan_policy) + assert_allclose(res1, res2) + + def test_3cols(self): + x1 = np.arange(6) + x2 = -x1 + x3 = np.array([0, 1, 2, 3, 5, 4]) + x = np.asarray([x1, x2, x3]).T + actual = stats.spearmanr(x) + expected_corr = np.array([[1, -1, 0.94285714], + [-1, 1, -0.94285714], + [0.94285714, -0.94285714, 1]]) + expected_pvalue = np.zeros((3, 3), dtype=float) + expected_pvalue[2, 0:2] = 0.00480466472 + expected_pvalue[0:2, 2] = 0.00480466472 + + assert_allclose(actual.statistic, expected_corr) + assert_allclose(actual.pvalue, expected_pvalue) + + def test_gh_9103(self): + # Regression test for gh-9103. + x = np.array([[np.nan, 3.0, 4.0, 5.0, 5.1, 6.0, 9.2], + [5.0, np.nan, 4.1, 4.8, 4.9, 5.0, 4.1], + [0.5, 4.0, 7.1, 3.8, 8.0, 5.1, 7.6]]).T + corr = np.array([[np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan], + [np.nan, np.nan, 1.]]) + assert_allclose(stats.spearmanr(x, nan_policy='propagate').statistic, + corr) + + res = stats.spearmanr(x, nan_policy='omit').statistic + assert_allclose((res[0][1], res[0][2], res[1][2]), + (0.2051957, 0.4857143, -0.4707919), rtol=1e-6) + + def test_gh_8111(self): + # Regression test for gh-8111 (different result for float/int/bool). + n = 100 + np.random.seed(234568) + x = np.random.rand(n) + m = np.random.rand(n) > 0.7 + + # bool against float, no nans + a = (x > .5) + b = np.array(x) + res1 = stats.spearmanr(a, b, nan_policy='omit').statistic + + # bool against float with NaNs + b[m] = np.nan + res2 = stats.spearmanr(a, b, nan_policy='omit').statistic + + # int against float with NaNs + a = a.astype(np.int32) + res3 = stats.spearmanr(a, b, nan_policy='omit').statistic + + expected = [0.865895477, 0.866100381, 0.866100381] + assert_allclose([res1, res2, res3], expected) + + +class TestCorrSpearmanr2: + """Some further tests of the spearmanr function.""" + + def test_spearmanr_vs_r(self): + # Cross-check with R: + # cor.test(c(1,2,3,4,5),c(5,6,7,8,7),method="spearmanr") + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 7] + expected = (0.82078268166812329, 0.088587005313543798) + res = stats.spearmanr(x1, x2) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + def test_empty_arrays(self): + assert_equal(stats.spearmanr([], []), (np.nan, np.nan)) + + def test_normal_draws(self): + np.random.seed(7546) + x = np.array([np.random.normal(loc=1, scale=1, size=500), + np.random.normal(loc=1, scale=1, size=500)]) + corr = [[1.0, 0.3], + [0.3, 1.0]] + x = np.dot(np.linalg.cholesky(corr), x) + expected = (0.28659685838743354, 6.579862219051161e-11) + res = stats.spearmanr(x[0], x[1]) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + def test_corr_1(self): + assert_approx_equal(stats.spearmanr([1, 1, 2], [1, 1, 2])[0], 1.0) + + def test_nan_policies(self): + x = np.arange(10.) + x[9] = np.nan + assert_array_equal(stats.spearmanr(x, x), (np.nan, np.nan)) + assert_allclose(stats.spearmanr(x, x, nan_policy='omit'), + (1.0, 0)) + assert_raises(ValueError, stats.spearmanr, x, x, nan_policy='raise') + assert_raises(ValueError, stats.spearmanr, x, x, nan_policy='foobar') + + def test_unequal_lengths(self): + x = np.arange(10.) + y = np.arange(20.) + assert_raises(ValueError, stats.spearmanr, x, y) + + def test_omit_paired_value(self): + x1 = [1, 2, 3, 4] + x2 = [8, 7, 6, np.nan] + res1 = stats.spearmanr(x1, x2, nan_policy='omit') + res2 = stats.spearmanr(x1[:3], x2[:3], nan_policy='omit') + assert_equal(res1, res2) + + def test_gh_issue_6061_windows_overflow(self): + x = list(range(2000)) + y = list(range(2000)) + y[0], y[9] = y[9], y[0] + y[10], y[434] = y[434], y[10] + y[435], y[1509] = y[1509], y[435] + # rho = 1 - 6 * (2 * (9^2 + 424^2 + 1074^2))/(2000 * (2000^2 - 1)) + # = 1 - (1 / 500) + # = 0.998 + x.append(np.nan) + y.append(3.0) + assert_almost_equal(stats.spearmanr(x, y, nan_policy='omit')[0], 0.998) + + def test_tie0(self): + # with only ties in one or both inputs + warn_msg = "An input array is constant" + with pytest.warns(stats.ConstantInputWarning, match=warn_msg): + r, p = stats.spearmanr([2, 2, 2], [2, 2, 2]) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + r, p = stats.spearmanr([2, 0, 2], [2, 2, 2]) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + r, p = stats.spearmanr([2, 2, 2], [2, 0, 2]) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + + def test_tie1(self): + # Data + x = [1.0, 2.0, 3.0, 4.0] + y = [1.0, 2.0, 2.0, 3.0] + # Ranks of the data, with tie-handling. + xr = [1.0, 2.0, 3.0, 4.0] + yr = [1.0, 2.5, 2.5, 4.0] + # Result of spearmanr should be the same as applying + # pearsonr to the ranks. + sr = stats.spearmanr(x, y) + pr = stats.pearsonr(xr, yr) + assert_almost_equal(sr, pr) + + def test_tie2(self): + # Test tie-handling if inputs contain nan's + # Data without nan's + x1 = [1, 2, 2.5, 2] + y1 = [1, 3, 2.5, 4] + # Same data with nan's + x2 = [1, 2, 2.5, 2, np.nan] + y2 = [1, 3, 2.5, 4, np.nan] + + # Results for two data sets should be the same if nan's are ignored + sr1 = stats.spearmanr(x1, y1) + sr2 = stats.spearmanr(x2, y2, nan_policy='omit') + assert_almost_equal(sr1, sr2) + + def test_ties_axis_1(self): + z1 = np.array([[1, 1, 1, 1], [1, 2, 3, 4]]) + z2 = np.array([[1, 2, 3, 4], [1, 1, 1, 1]]) + z3 = np.array([[1, 1, 1, 1], [1, 1, 1, 1]]) + warn_msg = "An input array is constant" + with pytest.warns(stats.ConstantInputWarning, match=warn_msg): + r, p = stats.spearmanr(z1, axis=1) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + r, p = stats.spearmanr(z2, axis=1) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + r, p = stats.spearmanr(z3, axis=1) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + + def test_gh_11111(self): + x = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) + y = np.array([0, 0.009783728115345005, 0, 0, 0.0019759230121848587, + 0.0007535430349118562, 0.0002661781514710257, 0, 0, + 0.0007835762419683435]) + warn_msg = "An input array is constant" + with pytest.warns(stats.ConstantInputWarning, match=warn_msg): + r, p = stats.spearmanr(x, y) + assert_equal(r, np.nan) + assert_equal(p, np.nan) + + def test_index_error(self): + x = np.array([1.0, 7.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) + y = np.array([0, 0.009783728115345005, 0, 0, 0.0019759230121848587, + 0.0007535430349118562, 0.0002661781514710257, 0, 0, + 0.0007835762419683435]) + assert_raises(ValueError, stats.spearmanr, x, y, axis=2) + + def test_alternative(self): + # Test alternative parameter + + # Simple test - Based on the above ``test_spearmanr_vs_r`` + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 7] + + # strong positive correlation + expected = (0.82078268166812329, 0.088587005313543798) + + # correlation > 0 -> large "less" p-value + res = stats.spearmanr(x1, x2, alternative="less") + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], 1 - (expected[1] / 2)) + + # correlation > 0 -> small "less" p-value + res = stats.spearmanr(x1, x2, alternative="greater") + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1] / 2) + + with pytest.raises(ValueError, match="`alternative` must be 'less'..."): + stats.spearmanr(x1, x2, alternative="ekki-ekki") + + @pytest.mark.parametrize("alternative", ('two-sided', 'less', 'greater')) + def test_alternative_nan_policy(self, alternative): + # Test nan policies + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 7] + x1nan = x1 + [np.nan] + x2nan = x2 + [np.nan] + + # test nan_policy="propagate" + assert_array_equal(stats.spearmanr(x1nan, x2nan), (np.nan, np.nan)) + + # test nan_policy="omit" + res_actual = stats.spearmanr(x1nan, x2nan, nan_policy='omit', + alternative=alternative) + res_expected = stats.spearmanr(x1, x2, alternative=alternative) + assert_allclose(res_actual, res_expected) + + # test nan_policy="raise" + message = 'The input contains nan values' + with pytest.raises(ValueError, match=message): + stats.spearmanr(x1nan, x2nan, nan_policy='raise', + alternative=alternative) + + # test invalid nan_policy + message = "nan_policy must be one of..." + with pytest.raises(ValueError, match=message): + stats.spearmanr(x1nan, x2nan, nan_policy='ekki-ekki', + alternative=alternative) + + +# W.II.E. Tabulate X against X, using BIG as a case weight. The values +# should appear on the diagonal and the total should be 899999955. +# If the table cannot hold these values, forget about working with +# census data. You can also tabulate HUGE against TINY. There is no +# reason a tabulation program should not be able to distinguish +# different values regardless of their magnitude. + +# I need to figure out how to do this one. + + +def test_kendalltau(): + # For the cases without ties, both variants should give the same + # result. + variants = ('b', 'c') + + # case without ties, con-dis equal zero + x = [5, 2, 1, 3, 6, 4, 7, 8] + y = [5, 2, 6, 3, 1, 8, 7, 4] + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (0.0, 1.0) + for taux in variants: + res = stats.kendalltau(x, y) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # case without ties, con-dis equal zero + x = [0, 5, 2, 1, 3, 6, 4, 7, 8] + y = [5, 2, 0, 6, 3, 1, 8, 7, 4] + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (0.0, 1.0) + for taux in variants: + res = stats.kendalltau(x, y) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # case without ties, con-dis close to zero + x = [5, 2, 1, 3, 6, 4, 7] + y = [5, 2, 6, 3, 1, 7, 4] + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (-0.14285714286, 0.77261904762) + for taux in variants: + res = stats.kendalltau(x, y) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # case without ties, con-dis close to zero + x = [2, 1, 3, 6, 4, 7, 8] + y = [2, 6, 3, 1, 8, 7, 4] + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (0.047619047619, 1.0) + for taux in variants: + res = stats.kendalltau(x, y) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # simple case without ties + x = np.arange(10) + y = np.arange(10) + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (1.0, 5.511463844797e-07) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # swap a couple of values + b = y[1] + y[1] = y[2] + y[2] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (0.9555555555555556, 5.511463844797e-06) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # swap a couple more + b = y[5] + y[5] = y[6] + y[6] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (0.9111111111111111, 2.976190476190e-05) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # same in opposite direction + x = np.arange(10) + y = np.arange(10)[::-1] + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (-1.0, 5.511463844797e-07) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # swap a couple of values + b = y[1] + y[1] = y[2] + y[2] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (-0.9555555555555556, 5.511463844797e-06) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # swap a couple more + b = y[5] + y[5] = y[6] + y[6] = b + # Cross-check with exact result from R: + # cor.test(x,y,method="kendall",exact=1) + expected = (-0.9111111111111111, 2.976190476190e-05) + for taux in variants: + res = stats.kendalltau(x, y, variant=taux) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # Check a case where variants are different + # Example values found from Kendall (1970). + # P-value is the same for the both variants + x = array([1, 2, 2, 4, 4, 6, 6, 8, 9, 9]) + y = array([1, 2, 4, 4, 4, 4, 8, 8, 8, 10]) + expected = 0.85895569 + assert_approx_equal(stats.kendalltau(x, y, variant='b')[0], expected) + expected = 0.825 + assert_approx_equal(stats.kendalltau(x, y, variant='c')[0], expected) + + # check exception in case of ties and method='exact' requested + y[2] = y[1] + assert_raises(ValueError, stats.kendalltau, x, y, method='exact') + + # check exception in case of invalid method keyword + assert_raises(ValueError, stats.kendalltau, x, y, method='banana') + + # check exception in case of invalid variant keyword + assert_raises(ValueError, stats.kendalltau, x, y, variant='rms') + + # tau-b with some ties + # Cross-check with R: + # cor.test(c(12,2,1,12,2),c(1,4,7,1,0),method="kendall",exact=FALSE) + x1 = [12, 2, 1, 12, 2] + x2 = [1, 4, 7, 1, 0] + expected = (-0.47140452079103173, 0.28274545993277478) + res = stats.kendalltau(x1, x2) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # test for namedtuple attribute results + attributes = ('correlation', 'pvalue') + for taux in variants: + res = stats.kendalltau(x1, x2, variant=taux) + check_named_results(res, attributes) + assert_equal(res.correlation, res.statistic) + + # with only ties in one or both inputs in tau-b or tau-c + for taux in variants: + assert_equal(stats.kendalltau([2, 2, 2], [2, 2, 2], variant=taux), + (np.nan, np.nan)) + assert_equal(stats.kendalltau([2, 0, 2], [2, 2, 2], variant=taux), + (np.nan, np.nan)) + assert_equal(stats.kendalltau([2, 2, 2], [2, 0, 2], variant=taux), + (np.nan, np.nan)) + + # empty arrays provided as input + assert_equal(stats.kendalltau([], []), (np.nan, np.nan)) + + # check with larger arrays + np.random.seed(7546) + x = np.array([np.random.normal(loc=1, scale=1, size=500), + np.random.normal(loc=1, scale=1, size=500)]) + corr = [[1.0, 0.3], + [0.3, 1.0]] + x = np.dot(np.linalg.cholesky(corr), x) + expected = (0.19291382765531062, 1.1337095377742629e-10) + res = stats.kendalltau(x[0], x[1]) + assert_approx_equal(res[0], expected[0]) + assert_approx_equal(res[1], expected[1]) + + # this should result in 1 for taub but not tau-c + assert_approx_equal(stats.kendalltau([1, 1, 2], [1, 1, 2], variant='b')[0], + 1.0) + assert_approx_equal(stats.kendalltau([1, 1, 2], [1, 1, 2], variant='c')[0], + 0.88888888) + + # test nan_policy + x = np.arange(10.) + x[9] = np.nan + assert_array_equal(stats.kendalltau(x, x), (np.nan, np.nan)) + assert_allclose(stats.kendalltau(x, x, nan_policy='omit'), + (1.0, 5.5114638e-6), rtol=1e-06) + assert_allclose(stats.kendalltau(x, x, nan_policy='omit', method='asymptotic'), + (1.0, 0.00017455009626808976), rtol=1e-06) + assert_raises(ValueError, stats.kendalltau, x, x, nan_policy='raise') + assert_raises(ValueError, stats.kendalltau, x, x, nan_policy='foobar') + + # test unequal length inputs + x = np.arange(10.) + y = np.arange(20.) + assert_raises(ValueError, stats.kendalltau, x, y) + + # test all ties + tau, p_value = stats.kendalltau([], []) + assert_equal(np.nan, tau) + assert_equal(np.nan, p_value) + tau, p_value = stats.kendalltau([0], [0]) + assert_equal(np.nan, tau) + assert_equal(np.nan, p_value) + + # Regression test for GitHub issue #6061 - Overflow on Windows + x = np.arange(2000, dtype=float) + x = np.ma.masked_greater(x, 1995) + y = np.arange(2000, dtype=float) + y = np.concatenate((y[1000:], y[:1000])) + assert_(np.isfinite(stats.kendalltau(x,y)[1])) + + +def test_kendalltau_vs_mstats_basic(): + np.random.seed(42) + for s in range(2,10): + a = [] + # Generate rankings with ties + for i in range(s): + a += [i]*i + b = list(a) + np.random.shuffle(a) + np.random.shuffle(b) + expected = mstats_basic.kendalltau(a, b) + actual = stats.kendalltau(a, b) + assert_approx_equal(actual[0], expected[0]) + assert_approx_equal(actual[1], expected[1]) + + +def test_kendalltau_nan_2nd_arg(): + # regression test for gh-6134: nans in the second arg were not handled + x = [1., 2., 3., 4.] + y = [np.nan, 2.4, 3.4, 3.4] + + r1 = stats.kendalltau(x, y, nan_policy='omit') + r2 = stats.kendalltau(x[1:], y[1:]) + assert_allclose(r1.statistic, r2.statistic, atol=1e-15) + + +def test_kendalltau_deprecations(): + msg_dep = "keyword argument 'initial_lexsort'" + with pytest.deprecated_call(match=msg_dep): + stats.kendalltau([], [], initial_lexsort=True) + with pytest.deprecated_call(match=f"use keyword arguments|{msg_dep}"): + stats.kendalltau([], [], True) + + +def test_kendalltau_gh18139_overflow(): + # gh-18139 reported an overflow in `kendalltau` that appeared after + # SciPy 0.15.1. Check that this particular overflow does not occur. + # (Test would fail if warning were emitted.) + import random + random.seed(6272161) + classes = [1, 2, 3, 4, 5, 6, 7] + n_samples = 2 * 10 ** 5 + x = random.choices(classes, k=n_samples) + y = random.choices(classes, k=n_samples) + res = stats.kendalltau(x, y) + # Reference value from SciPy 0.15.1 + assert_allclose(res.statistic, 0.0011816493905730343) + # Reference p-value from `permutation_test` w/ n_resamples=9999 (default). + # Expected to be accurate to at least two digits. + assert_allclose(res.pvalue, 0.4894, atol=2e-3) + + +class TestKendallTauAlternative: + def test_kendalltau_alternative_asymptotic(self): + # Test alternative parameter, asymptotic method (due to tie) + + # Based on TestCorrSpearman2::test_alternative + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 7] + + # strong positive correlation + expected = stats.kendalltau(x1, x2, alternative="two-sided") + assert expected[0] > 0 + + # rank correlation > 0 -> large "less" p-value + res = stats.kendalltau(x1, x2, alternative="less") + assert_equal(res[0], expected[0]) + assert_allclose(res[1], 1 - (expected[1] / 2)) + + # rank correlation > 0 -> small "greater" p-value + res = stats.kendalltau(x1, x2, alternative="greater") + assert_equal(res[0], expected[0]) + assert_allclose(res[1], expected[1] / 2) + + # reverse the direction of rank correlation + x2.reverse() + + # strong negative correlation + expected = stats.kendalltau(x1, x2, alternative="two-sided") + assert expected[0] < 0 + + # rank correlation < 0 -> large "greater" p-value + res = stats.kendalltau(x1, x2, alternative="greater") + assert_equal(res[0], expected[0]) + assert_allclose(res[1], 1 - (expected[1] / 2)) + + # rank correlation < 0 -> small "less" p-value + res = stats.kendalltau(x1, x2, alternative="less") + assert_equal(res[0], expected[0]) + assert_allclose(res[1], expected[1] / 2) + + with pytest.raises(ValueError, match="`alternative` must be 'less'..."): + stats.kendalltau(x1, x2, alternative="ekki-ekki") + + # There are a lot of special cases considered in the calculation of the + # exact p-value, so we test each separately. We also need to test + # separately when the observed statistic is in the left tail vs the right + # tail because the code leverages symmetry of the null distribution; to + # do that we use the same test case but negate one of the samples. + # Reference values computed using R cor.test, e.g. + # options(digits=16) + # x <- c(44.4, 45.9, 41.9, 53.3, 44.7, 44.1, 50.7, 45.2, 60.1) + # y <- c( 2.6, 3.1, 2.5, 5.0, 3.6, 4.0, 5.2, 2.8, 3.8) + # cor.test(x, y, method = "kendall", alternative = "g") + + alternatives = ('less', 'two-sided', 'greater') + p_n1 = [np.nan, np.nan, np.nan] + p_n2 = [1, 1, 0.5] + p_c0 = [1, 0.3333333333333, 0.1666666666667] + p_c1 = [0.9583333333333, 0.3333333333333, 0.1666666666667] + p_no_correlation = [0.5916666666667, 1, 0.5916666666667] + p_no_correlationb = [0.5475694444444, 1, 0.5475694444444] + p_n_lt_171 = [0.9624118165785, 0.1194389329806, 0.0597194664903] + p_n_lt_171b = [0.246236925303, 0.4924738506059, 0.755634083327] + p_n_lt_171c = [0.9847475308925, 0.03071385306533, 0.01535692653267] + + def exact_test(self, x, y, alternative, rev, stat_expected, p_expected): + if rev: + y = -np.asarray(y) + stat_expected *= -1 + res = stats.kendalltau(x, y, method='exact', alternative=alternative) + res_expected = stat_expected, p_expected + assert_allclose(res, res_expected) + + case_R_n1 = (list(zip(alternatives, p_n1, [False]*3)) + + list(zip(alternatives, reversed(p_n1), [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_n1) + def test_against_R_n1(self, alternative, p_expected, rev): + x, y = [1], [2] + stat_expected = np.nan + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_n2 = (list(zip(alternatives, p_n2, [False]*3)) + + list(zip(alternatives, reversed(p_n2), [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_n2) + def test_against_R_n2(self, alternative, p_expected, rev): + x, y = [1, 2], [3, 4] + stat_expected = 0.9999999999999998 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_c0 = (list(zip(alternatives, p_c0, [False]*3)) + + list(zip(alternatives, reversed(p_c0), [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_c0) + def test_against_R_c0(self, alternative, p_expected, rev): + x, y = [1, 2, 3], [1, 2, 3] + stat_expected = 1 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_c1 = (list(zip(alternatives, p_c1, [False]*3)) + + list(zip(alternatives, reversed(p_c1), [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_c1) + def test_against_R_c1(self, alternative, p_expected, rev): + x, y = [1, 2, 3, 4], [1, 2, 4, 3] + stat_expected = 0.6666666666666667 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_no_corr = (list(zip(alternatives, p_no_correlation, [False]*3)) + + list(zip(alternatives, reversed(p_no_correlation), + [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_no_corr) + def test_against_R_no_correlation(self, alternative, p_expected, rev): + x, y = [1, 2, 3, 4, 5], [1, 5, 4, 2, 3] + stat_expected = 0 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_no_cor_b = (list(zip(alternatives, p_no_correlationb, [False]*3)) + + list(zip(alternatives, reversed(p_no_correlationb), + [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_no_cor_b) + def test_against_R_no_correlationb(self, alternative, p_expected, rev): + x, y = [1, 2, 3, 4, 5, 6, 7, 8], [8, 6, 1, 3, 2, 5, 4, 7] + stat_expected = 0 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_lt_171 = (list(zip(alternatives, p_n_lt_171, [False]*3)) + + list(zip(alternatives, reversed(p_n_lt_171), [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_lt_171) + def test_against_R_lt_171(self, alternative, p_expected, rev): + # Data from Hollander & Wolfe (1973), p. 187f. + # Used from https://rdrr.io/r/stats/cor.test.html + x = [44.4, 45.9, 41.9, 53.3, 44.7, 44.1, 50.7, 45.2, 60.1] + y = [2.6, 3.1, 2.5, 5.0, 3.6, 4.0, 5.2, 2.8, 3.8] + stat_expected = 0.4444444444444445 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_lt_171b = (list(zip(alternatives, p_n_lt_171b, [False]*3)) + + list(zip(alternatives, reversed(p_n_lt_171b), + [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_lt_171b) + def test_against_R_lt_171b(self, alternative, p_expected, rev): + np.random.seed(0) + x = np.random.rand(100) + y = np.random.rand(100) + stat_expected = -0.04686868686868687 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_R_lt_171c = (list(zip(alternatives, p_n_lt_171c, [False]*3)) + + list(zip(alternatives, reversed(p_n_lt_171c), + [True]*3))) + + @pytest.mark.parametrize("alternative, p_expected, rev", case_R_lt_171c) + def test_against_R_lt_171c(self, alternative, p_expected, rev): + np.random.seed(0) + x = np.random.rand(170) + y = np.random.rand(170) + stat_expected = 0.1115906717716673 + self.exact_test(x, y, alternative, rev, stat_expected, p_expected) + + case_gt_171 = (list(zip(alternatives, [False]*3)) + + list(zip(alternatives, [True]*3))) + + @pytest.mark.parametrize("alternative, rev", case_gt_171) + def test_gt_171(self, alternative, rev): + np.random.seed(0) + x = np.random.rand(400) + y = np.random.rand(400) + res0 = stats.kendalltau(x, y, method='exact', + alternative=alternative) + res1 = stats.kendalltau(x, y, method='asymptotic', + alternative=alternative) + assert_equal(res0[0], res1[0]) + assert_allclose(res0[1], res1[1], rtol=1e-3) + + @pytest.mark.parametrize("method", ('exact', 'asymptotic')) + @pytest.mark.parametrize("alternative", ('two-sided', 'less', 'greater')) + def test_nan_policy(self, method, alternative): + # Test nan policies + x1 = [1, 2, 3, 4, 5] + x2 = [5, 6, 7, 8, 9] + x1nan = x1 + [np.nan] + x2nan = x2 + [np.nan] + + # test nan_policy="propagate" + res_actual = stats.kendalltau(x1nan, x2nan, + method=method, alternative=alternative) + res_expected = (np.nan, np.nan) + assert_allclose(res_actual, res_expected) + + # test nan_policy="omit" + res_actual = stats.kendalltau(x1nan, x2nan, nan_policy='omit', + method=method, alternative=alternative) + res_expected = stats.kendalltau(x1, x2, method=method, + alternative=alternative) + assert_allclose(res_actual, res_expected) + + # test nan_policy="raise" + message = 'The input contains nan values' + with pytest.raises(ValueError, match=message): + stats.kendalltau(x1nan, x2nan, nan_policy='raise', + method=method, alternative=alternative) + + # test invalid nan_policy + message = "nan_policy must be one of..." + with pytest.raises(ValueError, match=message): + stats.kendalltau(x1nan, x2nan, nan_policy='ekki-ekki', + method=method, alternative=alternative) + + +def test_weightedtau(): + x = [12, 2, 1, 12, 2] + y = [1, 4, 7, 1, 0] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.56694968153682723) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(x, y, additive=False) + assert_approx_equal(tau, -0.62205716951801038) + assert_equal(np.nan, p_value) + # This must be exactly Kendall's tau + tau, p_value = stats.weightedtau(x, y, weigher=lambda x: 1) + assert_approx_equal(tau, -0.47140452079103173) + assert_equal(np.nan, p_value) + + # test for namedtuple attribute results + res = stats.weightedtau(x, y) + attributes = ('correlation', 'pvalue') + check_named_results(res, attributes) + assert_equal(res.correlation, res.statistic) + + # Asymmetric, ranked version + tau, p_value = stats.weightedtau(x, y, rank=None) + assert_approx_equal(tau, -0.4157652301037516) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(y, x, rank=None) + assert_approx_equal(tau, -0.7181341329699029) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(x, y, rank=None, additive=False) + assert_approx_equal(tau, -0.40644850966246893) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(y, x, rank=None, additive=False) + assert_approx_equal(tau, -0.83766582937355172) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(x, y, rank=False) + assert_approx_equal(tau, -0.51604397940261848) + assert_equal(np.nan, p_value) + # This must be exactly Kendall's tau + tau, p_value = stats.weightedtau(x, y, rank=True, weigher=lambda x: 1) + assert_approx_equal(tau, -0.47140452079103173) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau(y, x, rank=True, weigher=lambda x: 1) + assert_approx_equal(tau, -0.47140452079103173) + assert_equal(np.nan, p_value) + # Test argument conversion + tau, p_value = stats.weightedtau(np.asarray(x, dtype=np.float64), y) + assert_approx_equal(tau, -0.56694968153682723) + tau, p_value = stats.weightedtau(np.asarray(x, dtype=np.int16), y) + assert_approx_equal(tau, -0.56694968153682723) + tau, p_value = stats.weightedtau(np.asarray(x, dtype=np.float64), + np.asarray(y, dtype=np.float64)) + assert_approx_equal(tau, -0.56694968153682723) + # All ties + tau, p_value = stats.weightedtau([], []) + assert_equal(np.nan, tau) + assert_equal(np.nan, p_value) + tau, p_value = stats.weightedtau([0], [0]) + assert_equal(np.nan, tau) + assert_equal(np.nan, p_value) + # Size mismatches + assert_raises(ValueError, stats.weightedtau, [0, 1], [0, 1, 2]) + assert_raises(ValueError, stats.weightedtau, [0, 1], [0, 1], [0]) + # NaNs + x = [12, 2, 1, 12, 2] + y = [1, 4, 7, 1, np.nan] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.56694968153682723) + x = [12, 2, np.nan, 12, 2] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.56694968153682723) + # NaNs when the dtype of x and y are all np.float64 + x = [12.0, 2.0, 1.0, 12.0, 2.0] + y = [1.0, 4.0, 7.0, 1.0, np.nan] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.56694968153682723) + x = [12.0, 2.0, np.nan, 12.0, 2.0] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.56694968153682723) + # NaNs when there are more than one NaN in x or y + x = [12.0, 2.0, 1.0, 12.0, 1.0] + y = [1.0, 4.0, 7.0, 1.0, 1.0] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.6615242347139803) + x = [12.0, 2.0, np.nan, 12.0, np.nan] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.6615242347139803) + y = [np.nan, 4.0, 7.0, np.nan, np.nan] + tau, p_value = stats.weightedtau(x, y) + assert_approx_equal(tau, -0.6615242347139803) + + +def test_segfault_issue_9710(): + # https://github.com/scipy/scipy/issues/9710 + # This test was created to check segfault + # In issue SEGFAULT only repros in optimized builds after calling the function twice + stats.weightedtau([1], [1.0]) + stats.weightedtau([1], [1.0]) + # The code below also caused SEGFAULT + stats.weightedtau([np.nan], [52]) + + +def test_kendall_tau_large(): + n = 172 + # Test omit policy + x = np.arange(n + 1).astype(float) + y = np.arange(n + 1).astype(float) + y[-1] = np.nan + _, pval = stats.kendalltau(x, y, method='exact', nan_policy='omit') + assert_equal(pval, 0.0) + + +def test_weightedtau_vs_quadratic(): + # Trivial quadratic implementation, all parameters mandatory + def wkq(x, y, rank, weigher, add): + tot = conc = disc = u = v = 0 + for (i, j) in product(range(len(x)), range(len(x))): + w = weigher(rank[i]) + weigher(rank[j]) if add \ + else weigher(rank[i]) * weigher(rank[j]) + tot += w + if x[i] == x[j]: + u += w + if y[i] == y[j]: + v += w + if x[i] < x[j] and y[i] < y[j] or x[i] > x[j] and y[i] > y[j]: + conc += w + elif x[i] < x[j] and y[i] > y[j] or x[i] > x[j] and y[i] < y[j]: + disc += w + return (conc - disc) / np.sqrt(tot - u) / np.sqrt(tot - v) + + def weigher(x): + return 1. / (x + 1) + + np.random.seed(42) + for s in range(3,10): + a = [] + # Generate rankings with ties + for i in range(s): + a += [i]*i + b = list(a) + np.random.shuffle(a) + np.random.shuffle(b) + # First pass: use element indices as ranks + rank = np.arange(len(a), dtype=np.intp) + for _ in range(2): + for add in [True, False]: + expected = wkq(a, b, rank, weigher, add) + actual = stats.weightedtau(a, b, rank, weigher, add).statistic + assert_approx_equal(expected, actual) + # Second pass: use a random rank + np.random.shuffle(rank) + + +class TestFindRepeats: + + def test_basic(self): + a = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 5] + res, nums = stats.find_repeats(a) + assert_array_equal(res, [1, 2, 3, 4]) + assert_array_equal(nums, [3, 3, 2, 2]) + + def test_empty_result(self): + # Check that empty arrays are returned when there are no repeats. + for a in [[10, 20, 50, 30, 40], []]: + repeated, counts = stats.find_repeats(a) + assert_array_equal(repeated, []) + assert_array_equal(counts, []) + + +class TestRegression: + + def test_linregressBIGX(self): + # W.II.F. Regress BIG on X. + result = stats.linregress(X, BIG) + assert_almost_equal(result.intercept, 99999990) + assert_almost_equal(result.rvalue, 1.0) + # The uncertainty ought to be almost zero + # since all points lie on a line + assert_almost_equal(result.stderr, 0.0) + assert_almost_equal(result.intercept_stderr, 0.0) + + def test_regressXX(self): + # W.IV.B. Regress X on X. + # The constant should be exactly 0 and the regression coefficient + # should be 1. This is a perfectly valid regression and the + # program should not complain. + result = stats.linregress(X, X) + assert_almost_equal(result.intercept, 0.0) + assert_almost_equal(result.rvalue, 1.0) + # The uncertainly on regression through two points ought to be 0 + assert_almost_equal(result.stderr, 0.0) + assert_almost_equal(result.intercept_stderr, 0.0) + + # W.IV.C. Regress X on BIG and LITTLE (two predictors). The program + # should tell you that this model is "singular" because BIG and + # LITTLE are linear combinations of each other. Cryptic error + # messages are unacceptable here. Singularity is the most + # fundamental regression error. + # + # Need to figure out how to handle multiple linear regression. + # This is not obvious + + def test_regressZEROX(self): + # W.IV.D. Regress ZERO on X. + # The program should inform you that ZERO has no variance or it should + # go ahead and compute the regression and report a correlation and + # total sum of squares of exactly 0. + result = stats.linregress(X, ZERO) + assert_almost_equal(result.intercept, 0.0) + assert_almost_equal(result.rvalue, 0.0) + + def test_regress_simple(self): + # Regress a line with sinusoidal noise. + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + + result = stats.linregress(x, y) + lr = stats._stats_mstats_common.LinregressResult + assert_(isinstance(result, lr)) + assert_almost_equal(result.stderr, 2.3957814497838803e-3) + + def test_regress_alternative(self): + # test alternative parameter + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 # slope is greater than zero + y += np.sin(np.linspace(0, 20, 100)) + + with pytest.raises(ValueError, match="alternative must be 'less'..."): + stats.linregress(x, y, alternative="ekki-ekki") + + res1 = stats.linregress(x, y, alternative="two-sided") + + # slope is greater than zero, so "less" p-value should be large + res2 = stats.linregress(x, y, alternative="less") + assert_allclose(res2.pvalue, 1 - (res1.pvalue / 2)) + + # slope is greater than zero, so "greater" p-value should be small + res3 = stats.linregress(x, y, alternative="greater") + assert_allclose(res3.pvalue, res1.pvalue / 2) + + assert res1.rvalue == res2.rvalue == res3.rvalue + + def test_regress_against_R(self): + # test against R `lm` + # options(digits=16) + # x <- c(151, 174, 138, 186, 128, 136, 179, 163, 152, 131) + # y <- c(63, 81, 56, 91, 47, 57, 76, 72, 62, 48) + # relation <- lm(y~x) + # print(summary(relation)) + + x = [151, 174, 138, 186, 128, 136, 179, 163, 152, 131] + y = [63, 81, 56, 91, 47, 57, 76, 72, 62, 48] + res = stats.linregress(x, y, alternative="two-sided") + # expected values from R's `lm` above + assert_allclose(res.slope, 0.6746104491292) + assert_allclose(res.intercept, -38.4550870760770) + assert_allclose(res.rvalue, np.sqrt(0.95478224775)) + assert_allclose(res.pvalue, 1.16440531074e-06) + assert_allclose(res.stderr, 0.0519051424731) + assert_allclose(res.intercept_stderr, 8.0490133029927) + + def test_regress_simple_onearg_rows(self): + # Regress a line w sinusoidal noise, + # with a single input of shape (2, N) + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + rows = np.vstack((x, y)) + + result = stats.linregress(rows) + assert_almost_equal(result.stderr, 2.3957814497838803e-3) + assert_almost_equal(result.intercept_stderr, 1.3866936078570702e-1) + + def test_regress_simple_onearg_cols(self): + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + columns = np.hstack((np.expand_dims(x, 1), np.expand_dims(y, 1))) + + result = stats.linregress(columns) + assert_almost_equal(result.stderr, 2.3957814497838803e-3) + assert_almost_equal(result.intercept_stderr, 1.3866936078570702e-1) + + def test_regress_shape_error(self): + # Check that a single input argument to linregress with wrong shape + # results in a ValueError. + assert_raises(ValueError, stats.linregress, np.ones((3, 3))) + + def test_linregress(self): + # compared with multivariate ols with pinv + x = np.arange(11) + y = np.arange(5, 16) + y[[(1), (-2)]] -= 1 + y[[(0), (-1)]] += 1 + + result = stats.linregress(x, y) + + # This test used to use 'assert_array_almost_equal' but its + # formualtion got confusing since LinregressResult became + # _lib._bunch._make_tuple_bunch instead of namedtuple + # (for backwards compatibility, see PR #12983) + def assert_ae(x, y): + return assert_almost_equal(x, y, decimal=14) + assert_ae(result.slope, 1.0) + assert_ae(result.intercept, 5.0) + assert_ae(result.rvalue, 0.98229948625750) + assert_ae(result.pvalue, 7.45259691e-008) + assert_ae(result.stderr, 0.063564172616372733) + assert_ae(result.intercept_stderr, 0.37605071654517686) + + def test_regress_simple_negative_cor(self): + # If the slope of the regression is negative the factor R tend + # to -1 not 1. Sometimes rounding errors makes it < -1 + # leading to stderr being NaN. + a, n = 1e-71, 100000 + x = np.linspace(a, 2 * a, n) + y = np.linspace(2 * a, a, n) + result = stats.linregress(x, y) + + # Make sure propagated numerical errors + # did not bring rvalue below -1 (or were coersced) + assert_(result.rvalue >= -1) + assert_almost_equal(result.rvalue, -1) + + # slope and intercept stderror should stay numeric + assert_(not np.isnan(result.stderr)) + assert_(not np.isnan(result.intercept_stderr)) + + def test_linregress_result_attributes(self): + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + result = stats.linregress(x, y) + + # Result is of a correct class + lr = stats._stats_mstats_common.LinregressResult + assert_(isinstance(result, lr)) + + # LinregressResult elements have correct names + attributes = ('slope', 'intercept', 'rvalue', 'pvalue', 'stderr') + check_named_results(result, attributes) + # Also check that the extra attribute (intercept_stderr) is present + assert 'intercept_stderr' in dir(result) + + def test_regress_two_inputs(self): + # Regress a simple line formed by two points. + x = np.arange(2) + y = np.arange(3, 5) + result = stats.linregress(x, y) + + # Non-horizontal line + assert_almost_equal(result.pvalue, 0.0) + + # Zero error through two points + assert_almost_equal(result.stderr, 0.0) + assert_almost_equal(result.intercept_stderr, 0.0) + + def test_regress_two_inputs_horizontal_line(self): + # Regress a horizontal line formed by two points. + x = np.arange(2) + y = np.ones(2) + result = stats.linregress(x, y) + + # Horizontal line + assert_almost_equal(result.pvalue, 1.0) + + # Zero error through two points + assert_almost_equal(result.stderr, 0.0) + assert_almost_equal(result.intercept_stderr, 0.0) + + def test_nist_norris(self): + x = [0.2, 337.4, 118.2, 884.6, 10.1, 226.5, 666.3, 996.3, 448.6, 777.0, + 558.2, 0.4, 0.6, 775.5, 666.9, 338.0, 447.5, 11.6, 556.0, 228.1, + 995.8, 887.6, 120.2, 0.3, 0.3, 556.8, 339.1, 887.2, 999.0, 779.0, + 11.1, 118.3, 229.2, 669.1, 448.9, 0.5] + + y = [0.1, 338.8, 118.1, 888.0, 9.2, 228.1, 668.5, 998.5, 449.1, 778.9, + 559.2, 0.3, 0.1, 778.1, 668.8, 339.3, 448.9, 10.8, 557.7, 228.3, + 998.0, 888.8, 119.6, 0.3, 0.6, 557.6, 339.3, 888.0, 998.5, 778.9, + 10.2, 117.6, 228.9, 668.4, 449.2, 0.2] + + result = stats.linregress(x, y) + + assert_almost_equal(result.slope, 1.00211681802045) + assert_almost_equal(result.intercept, -0.262323073774029) + assert_almost_equal(result.rvalue**2, 0.999993745883712) + assert_almost_equal(result.pvalue, 0.0) + assert_almost_equal(result.stderr, 0.00042979684820) + assert_almost_equal(result.intercept_stderr, 0.23281823430153) + + def test_compare_to_polyfit(self): + x = np.linspace(0, 100, 100) + y = 0.2 * np.linspace(0, 100, 100) + 10 + y += np.sin(np.linspace(0, 20, 100)) + result = stats.linregress(x, y) + poly = np.polyfit(x, y, 1) # Fit 1st degree polynomial + + # Make sure linear regression slope and intercept + # match with results from numpy polyfit + assert_almost_equal(result.slope, poly[0]) + assert_almost_equal(result.intercept, poly[1]) + + def test_empty_input(self): + assert_raises(ValueError, stats.linregress, [], []) + + def test_nan_input(self): + x = np.arange(10.) + x[9] = np.nan + + with np.errstate(invalid="ignore"): + result = stats.linregress(x, x) + + # Make sure the result still comes back as `LinregressResult` + lr = stats._stats_mstats_common.LinregressResult + assert_(isinstance(result, lr)) + assert_array_equal(result, (np.nan,)*5) + assert_equal(result.intercept_stderr, np.nan) + + def test_identical_x(self): + x = np.zeros(10) + y = np.random.random(10) + msg = "Cannot calculate a linear regression" + with assert_raises(ValueError, match=msg): + stats.linregress(x, y) + + +def test_theilslopes(): + # Basic slope test. + slope, intercept, lower, upper = stats.theilslopes([0,1,1]) + assert_almost_equal(slope, 0.5) + assert_almost_equal(intercept, 0.5) + + msg = ("method must be either 'joint' or 'separate'." + "'joint_separate' is invalid.") + with pytest.raises(ValueError, match=msg): + stats.theilslopes([0, 1, 1], method='joint_separate') + + slope, intercept, lower, upper = stats.theilslopes([0, 1, 1], + method='joint') + assert_almost_equal(slope, 0.5) + assert_almost_equal(intercept, 0.0) + + # Test of confidence intervals. + x = [1, 2, 3, 4, 10, 12, 18] + y = [9, 15, 19, 20, 45, 55, 78] + slope, intercept, lower, upper = stats.theilslopes(y, x, 0.07, + method='separate') + assert_almost_equal(slope, 4) + assert_almost_equal(intercept, 4.0) + assert_almost_equal(upper, 4.38, decimal=2) + assert_almost_equal(lower, 3.71, decimal=2) + + slope, intercept, lower, upper = stats.theilslopes(y, x, 0.07, + method='joint') + assert_almost_equal(slope, 4) + assert_almost_equal(intercept, 6.0) + assert_almost_equal(upper, 4.38, decimal=2) + assert_almost_equal(lower, 3.71, decimal=2) + + +def test_cumfreq(): + x = [1, 4, 2, 1, 3, 1] + cumfreqs, lowlim, binsize, extrapoints = stats.cumfreq(x, numbins=4) + assert_array_almost_equal(cumfreqs, np.array([3., 4., 5., 6.])) + cumfreqs, lowlim, binsize, extrapoints = stats.cumfreq( + x, numbins=4, defaultreallimits=(1.5, 5)) + assert_(extrapoints == 3) + + # test for namedtuple attribute results + attributes = ('cumcount', 'lowerlimit', 'binsize', 'extrapoints') + res = stats.cumfreq(x, numbins=4, defaultreallimits=(1.5, 5)) + check_named_results(res, attributes) + + +def test_relfreq(): + a = np.array([1, 4, 2, 1, 3, 1]) + relfreqs, lowlim, binsize, extrapoints = stats.relfreq(a, numbins=4) + assert_array_almost_equal(relfreqs, + array([0.5, 0.16666667, 0.16666667, 0.16666667])) + + # test for namedtuple attribute results + attributes = ('frequency', 'lowerlimit', 'binsize', 'extrapoints') + res = stats.relfreq(a, numbins=4) + check_named_results(res, attributes) + + # check array_like input is accepted + relfreqs2, lowlim, binsize, extrapoints = stats.relfreq([1, 4, 2, 1, 3, 1], + numbins=4) + assert_array_almost_equal(relfreqs, relfreqs2) + + +class TestScoreatpercentile: + def setup_method(self): + self.a1 = [3, 4, 5, 10, -3, -5, 6] + self.a2 = [3, -6, -2, 8, 7, 4, 2, 1] + self.a3 = [3., 4, 5, 10, -3, -5, -6, 7.0] + + def test_basic(self): + x = arange(8) * 0.5 + assert_equal(stats.scoreatpercentile(x, 0), 0.) + assert_equal(stats.scoreatpercentile(x, 100), 3.5) + assert_equal(stats.scoreatpercentile(x, 50), 1.75) + + def test_fraction(self): + scoreatperc = stats.scoreatpercentile + + # Test defaults + assert_equal(scoreatperc(list(range(10)), 50), 4.5) + assert_equal(scoreatperc(list(range(10)), 50, (2,7)), 4.5) + assert_equal(scoreatperc(list(range(100)), 50, limit=(1, 8)), 4.5) + assert_equal(scoreatperc(np.array([1, 10,100]), 50, (10,100)), 55) + assert_equal(scoreatperc(np.array([1, 10,100]), 50, (1,10)), 5.5) + + # explicitly specify interpolation_method 'fraction' (the default) + assert_equal(scoreatperc(list(range(10)), 50, interpolation_method='fraction'), + 4.5) + assert_equal(scoreatperc(list(range(10)), 50, limit=(2, 7), + interpolation_method='fraction'), + 4.5) + assert_equal(scoreatperc(list(range(100)), 50, limit=(1, 8), + interpolation_method='fraction'), + 4.5) + assert_equal(scoreatperc(np.array([1, 10,100]), 50, (10, 100), + interpolation_method='fraction'), + 55) + assert_equal(scoreatperc(np.array([1, 10,100]), 50, (1,10), + interpolation_method='fraction'), + 5.5) + + def test_lower_higher(self): + scoreatperc = stats.scoreatpercentile + + # interpolation_method 'lower'/'higher' + assert_equal(scoreatperc(list(range(10)), 50, + interpolation_method='lower'), 4) + assert_equal(scoreatperc(list(range(10)), 50, + interpolation_method='higher'), 5) + assert_equal(scoreatperc(list(range(10)), 50, (2,7), + interpolation_method='lower'), 4) + assert_equal(scoreatperc(list(range(10)), 50, limit=(2,7), + interpolation_method='higher'), 5) + assert_equal(scoreatperc(list(range(100)), 50, (1,8), + interpolation_method='lower'), 4) + assert_equal(scoreatperc(list(range(100)), 50, (1,8), + interpolation_method='higher'), 5) + assert_equal(scoreatperc(np.array([1, 10, 100]), 50, (10, 100), + interpolation_method='lower'), 10) + assert_equal(scoreatperc(np.array([1, 10, 100]), 50, limit=(10, 100), + interpolation_method='higher'), 100) + assert_equal(scoreatperc(np.array([1, 10, 100]), 50, (1, 10), + interpolation_method='lower'), 1) + assert_equal(scoreatperc(np.array([1, 10, 100]), 50, limit=(1, 10), + interpolation_method='higher'), 10) + + def test_sequence_per(self): + x = arange(8) * 0.5 + expected = np.array([0, 3.5, 1.75]) + res = stats.scoreatpercentile(x, [0, 100, 50]) + assert_allclose(res, expected) + assert_(isinstance(res, np.ndarray)) + # Test with ndarray. Regression test for gh-2861 + assert_allclose(stats.scoreatpercentile(x, np.array([0, 100, 50])), + expected) + # Also test combination of 2-D array, axis not None and array-like per + res2 = stats.scoreatpercentile(np.arange(12).reshape((3,4)), + np.array([0, 1, 100, 100]), axis=1) + expected2 = array([[0, 4, 8], + [0.03, 4.03, 8.03], + [3, 7, 11], + [3, 7, 11]]) + assert_allclose(res2, expected2) + + def test_axis(self): + scoreatperc = stats.scoreatpercentile + x = arange(12).reshape(3, 4) + + assert_equal(scoreatperc(x, (25, 50, 100)), [2.75, 5.5, 11.0]) + + r0 = [[2, 3, 4, 5], [4, 5, 6, 7], [8, 9, 10, 11]] + assert_equal(scoreatperc(x, (25, 50, 100), axis=0), r0) + + r1 = [[0.75, 4.75, 8.75], [1.5, 5.5, 9.5], [3, 7, 11]] + assert_equal(scoreatperc(x, (25, 50, 100), axis=1), r1) + + x = array([[1, 1, 1], + [1, 1, 1], + [4, 4, 3], + [1, 1, 1], + [1, 1, 1]]) + score = stats.scoreatpercentile(x, 50) + assert_equal(score.shape, ()) + assert_equal(score, 1.0) + score = stats.scoreatpercentile(x, 50, axis=0) + assert_equal(score.shape, (3,)) + assert_equal(score, [1, 1, 1]) + + def test_exception(self): + assert_raises(ValueError, stats.scoreatpercentile, [1, 2], 56, + interpolation_method='foobar') + assert_raises(ValueError, stats.scoreatpercentile, [1], 101) + assert_raises(ValueError, stats.scoreatpercentile, [1], -1) + + def test_empty(self): + assert_equal(stats.scoreatpercentile([], 50), np.nan) + assert_equal(stats.scoreatpercentile(np.array([[], []]), 50), np.nan) + assert_equal(stats.scoreatpercentile([], [50, 99]), [np.nan, np.nan]) + + +@pytest.mark.filterwarnings('ignore::FutureWarning') +class TestMode: + + def test_empty(self): + vals, counts = stats.mode([]) + assert_equal(vals, np.array([])) + assert_equal(counts, np.array([])) + + def test_scalar(self): + vals, counts = stats.mode(4.) + assert_equal(vals, np.array([4.])) + assert_equal(counts, np.array([1])) + + def test_basic(self): + data1 = [3, 5, 1, 10, 23, 3, 2, 6, 8, 6, 10, 6] + vals = stats.mode(data1) + assert_equal(vals[0], 6) + assert_equal(vals[1], 3) + + def test_axes(self): + data1 = [10, 10, 30, 40] + data2 = [10, 10, 10, 10] + data3 = [20, 10, 20, 20] + data4 = [30, 30, 30, 30] + data5 = [40, 30, 30, 30] + arr = np.array([data1, data2, data3, data4, data5]) + + vals = stats.mode(arr, axis=None, keepdims=True) + assert_equal(vals[0], np.array([[30]])) + assert_equal(vals[1], np.array([[8]])) + + vals = stats.mode(arr, axis=0, keepdims=True) + assert_equal(vals[0], np.array([[10, 10, 30, 30]])) + assert_equal(vals[1], np.array([[2, 3, 3, 2]])) + + vals = stats.mode(arr, axis=1, keepdims=True) + assert_equal(vals[0], np.array([[10], [10], [20], [30], [30]])) + assert_equal(vals[1], np.array([[2], [4], [3], [4], [3]])) + + @pytest.mark.parametrize('axis', np.arange(-4, 0)) + def test_negative_axes_gh_15375(self, axis): + np.random.seed(984213899) + a = np.random.rand(10, 11, 12, 13) + res0 = stats.mode(a, axis=a.ndim+axis) + res1 = stats.mode(a, axis=axis) + np.testing.assert_array_equal(res0, res1) + + def test_mode_result_attributes(self): + data1 = [3, 5, 1, 10, 23, 3, 2, 6, 8, 6, 10, 6] + data2 = [] + actual = stats.mode(data1) + attributes = ('mode', 'count') + check_named_results(actual, attributes) + actual2 = stats.mode(data2) + check_named_results(actual2, attributes) + + def test_mode_nan(self): + data1 = [3, np.nan, 5, 1, 10, 23, 3, 2, 6, 8, 6, 10, 6] + actual = stats.mode(data1) + assert_equal(actual, (6, 3)) + + actual = stats.mode(data1, nan_policy='omit') + assert_equal(actual, (6, 3)) + assert_raises(ValueError, stats.mode, data1, nan_policy='raise') + assert_raises(ValueError, stats.mode, data1, nan_policy='foobar') + + @pytest.mark.parametrize("data", [ + [3, 5, 1, 1, 3], + [3, np.nan, 5, 1, 1, 3], + [3, 5, 1], + [3, np.nan, 5, 1], + ]) + @pytest.mark.parametrize('keepdims', [False, True]) + def test_smallest_equal(self, data, keepdims): + result = stats.mode(data, nan_policy='omit', keepdims=keepdims) + if keepdims: + assert_equal(result[0][0], 1) + else: + assert_equal(result[0], 1) + + @pytest.mark.parametrize('axis', np.arange(-3, 3)) + def test_mode_shape_gh_9955(self, axis, dtype=np.float64): + rng = np.random.default_rng(984213899) + a = rng.uniform(size=(3, 4, 5)).astype(dtype) + res = stats.mode(a, axis=axis, keepdims=False) + reference_shape = list(a.shape) + reference_shape.pop(axis) + np.testing.assert_array_equal(res.mode.shape, reference_shape) + np.testing.assert_array_equal(res.count.shape, reference_shape) + + def test_nan_policy_propagate_gh_9815(self): + # mode should treat np.nan as it would any other object when + # nan_policy='propagate' + a = [2, np.nan, 1, np.nan] + res = stats.mode(a) + assert np.isnan(res.mode) and res.count == 2 + + def test_keepdims(self): + # test empty arrays (handled by `np.mean`) + a = np.zeros((1, 2, 3, 0)) + + res = stats.mode(a, axis=1, keepdims=False) + assert res.mode.shape == res.count.shape == (1, 3, 0) + + res = stats.mode(a, axis=1, keepdims=True) + assert res.mode.shape == res.count.shape == (1, 1, 3, 0) + + # test nan_policy='propagate' + a = [[1, 3, 3, np.nan], [1, 1, np.nan, 1]] + + res = stats.mode(a, axis=1, keepdims=False) + assert_array_equal(res.mode, [3, 1]) + assert_array_equal(res.count, [2, 3]) + + res = stats.mode(a, axis=1, keepdims=True) + assert_array_equal(res.mode, [[3], [1]]) + assert_array_equal(res.count, [[2], [3]]) + + a = np.array(a) + res = stats.mode(a, axis=None, keepdims=False) + ref = stats.mode(a.ravel(), keepdims=False) + assert_array_equal(res, ref) + assert res.mode.shape == ref.mode.shape == () + + res = stats.mode(a, axis=None, keepdims=True) + ref = stats.mode(a.ravel(), keepdims=True) + assert_equal(res.mode.ravel(), ref.mode.ravel()) + assert res.mode.shape == (1, 1) + assert_equal(res.count.ravel(), ref.count.ravel()) + assert res.count.shape == (1, 1) + + # test nan_policy='omit' + a = [[1, np.nan, np.nan, np.nan, 1], + [np.nan, np.nan, np.nan, np.nan, 2], + [1, 2, np.nan, 5, 5]] + + res = stats.mode(a, axis=1, keepdims=False, nan_policy='omit') + assert_array_equal(res.mode, [1, 2, 5]) + assert_array_equal(res.count, [2, 1, 2]) + + res = stats.mode(a, axis=1, keepdims=True, nan_policy='omit') + assert_array_equal(res.mode, [[1], [2], [5]]) + assert_array_equal(res.count, [[2], [1], [2]]) + + a = np.array(a) + res = stats.mode(a, axis=None, keepdims=False, nan_policy='omit') + ref = stats.mode(a.ravel(), keepdims=False, nan_policy='omit') + assert_array_equal(res, ref) + assert res.mode.shape == ref.mode.shape == () + + res = stats.mode(a, axis=None, keepdims=True, nan_policy='omit') + ref = stats.mode(a.ravel(), keepdims=True, nan_policy='omit') + assert_equal(res.mode.ravel(), ref.mode.ravel()) + assert res.mode.shape == (1, 1) + assert_equal(res.count.ravel(), ref.count.ravel()) + assert res.count.shape == (1, 1) + + @pytest.mark.parametrize("nan_policy", ['propagate', 'omit']) + def test_gh16955(self, nan_policy): + # Check that bug reported in gh-16955 is resolved + shape = (4, 3) + data = np.ones(shape) + data[0, 0] = np.nan + res = stats.mode(a=data, axis=1, keepdims=False, nan_policy=nan_policy) + assert_array_equal(res.mode, [1, 1, 1, 1]) + assert_array_equal(res.count, [2, 3, 3, 3]) + + # Test with input from gh-16595. Support for non-numeric input + # was deprecated, so check for the appropriate error. + my_dtype = np.dtype([('asdf', np.uint8), ('qwer', np.float64, (3,))]) + test = np.zeros(10, dtype=my_dtype) + with pytest.raises(TypeError, match="Argument `a` is not..."): + stats.mode(test, nan_policy=nan_policy) + + def test_gh9955(self): + # The behavior of mode with empty slices (whether the input was empty + # or all elements were omitted) was inconsistent. Test that this is + # resolved: the mode of an empty slice is NaN and the count is zero. + res = stats.mode([]) + ref = (np.nan, 0) + assert_equal(res, ref) + + res = stats.mode([np.nan], nan_policy='omit') + assert_equal(res, ref) + + a = [[10., 20., 20.], [np.nan, np.nan, np.nan]] + res = stats.mode(a, axis=1, nan_policy='omit') + ref = ([20, np.nan], [2, 0]) + assert_equal(res, ref) + + res = stats.mode(a, axis=1, nan_policy='propagate') + ref = ([20, np.nan], [2, 3]) + assert_equal(res, ref) + + z = np.array([[], []]) + res = stats.mode(z, axis=1) + ref = ([np.nan, np.nan], [0, 0]) + assert_equal(res, ref) + + @pytest.mark.filterwarnings('ignore::RuntimeWarning') # np.mean warns + @pytest.mark.parametrize('z', [np.empty((0, 1, 2)), np.empty((1, 1, 2))]) + def test_gh17214(self, z): + res = stats.mode(z, axis=None, keepdims=True) + ref = np.mean(z, axis=None, keepdims=True) + assert res[0].shape == res[1].shape == ref.shape == (1, 1, 1) + + def test_raise_non_numeric_gh18254(self): + message = "Argument `a` is not recognized as numeric." + + class ArrLike: + def __init__(self, x): + self._x = x + + def __array__(self, dtype=None, copy=None): + return self._x.astype(object) + + with pytest.raises(TypeError, match=message): + stats.mode(ArrLike(np.arange(3))) + with pytest.raises(TypeError, match=message): + stats.mode(np.arange(3, dtype=object)) + +class TestSEM: + + testcase = [1, 2, 3, 4] + scalar_testcase = 4. + + def test_sem(self): + # This is not in R, so used: + # sqrt(var(testcase)*3/4)/sqrt(3) + + # y = stats.sem(self.shoes[0]) + # assert_approx_equal(y,0.775177399) + with suppress_warnings() as sup, np.errstate(invalid="ignore"): + sup.filter(RuntimeWarning, "Degrees of freedom <= 0 for slice") + y = stats.sem(self.scalar_testcase) + assert_(np.isnan(y)) + + y = stats.sem(self.testcase) + assert_approx_equal(y, 0.6454972244) + n = len(self.testcase) + assert_allclose(stats.sem(self.testcase, ddof=0) * np.sqrt(n/(n-2)), + stats.sem(self.testcase, ddof=2)) + + x = np.arange(10.) + x[9] = np.nan + assert_equal(stats.sem(x), np.nan) + assert_equal(stats.sem(x, nan_policy='omit'), 0.9128709291752769) + assert_raises(ValueError, stats.sem, x, nan_policy='raise') + assert_raises(ValueError, stats.sem, x, nan_policy='foobar') + + +class TestZmapZscore: + + @pytest.mark.parametrize( + 'x, y', + [([1, 2, 3, 4], [1, 2, 3, 4]), + ([1, 2, 3], [0, 1, 2, 3, 4])] + ) + def test_zmap(self, x, y): + z = stats.zmap(x, y) + # For these simple cases, calculate the expected result directly + # by using the formula for the z-score. + expected = (x - np.mean(y))/np.std(y) + assert_allclose(z, expected, rtol=1e-12) + + def test_zmap_axis(self): + # Test use of 'axis' keyword in zmap. + x = np.array([[0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 2.0], + [2.0, 0.0, 2.0, 0.0]]) + + t1 = 1.0/np.sqrt(2.0/3) + t2 = np.sqrt(3.)/3 + t3 = np.sqrt(2.) + + z0 = stats.zmap(x, x, axis=0) + z1 = stats.zmap(x, x, axis=1) + + z0_expected = [[-t1, -t3/2, -t3/2, 0.0], + [0.0, t3, -t3/2, t1], + [t1, -t3/2, t3, -t1]] + z1_expected = [[-1.0, -1.0, 1.0, 1.0], + [-t2, -t2, -t2, np.sqrt(3.)], + [1.0, -1.0, 1.0, -1.0]] + + assert_array_almost_equal(z0, z0_expected) + assert_array_almost_equal(z1, z1_expected) + + def test_zmap_ddof(self): + # Test use of 'ddof' keyword in zmap. + x = np.array([[0.0, 0.0, 1.0, 1.0], + [0.0, 1.0, 2.0, 3.0]]) + + z = stats.zmap(x, x, axis=1, ddof=1) + + z0_expected = np.array([-0.5, -0.5, 0.5, 0.5])/(1.0/np.sqrt(3)) + z1_expected = np.array([-1.5, -0.5, 0.5, 1.5])/(np.sqrt(5./3)) + assert_array_almost_equal(z[0], z0_expected) + assert_array_almost_equal(z[1], z1_expected) + + @pytest.mark.parametrize('ddof', [0, 2]) + def test_zmap_nan_policy_omit(self, ddof): + # nans in `scores` are propagated, regardless of `nan_policy`. + # `nan_policy` only affects how nans in `compare` are handled. + scores = np.array([-3, -1, 2, np.nan]) + compare = np.array([-8, -3, 2, 7, 12, np.nan]) + z = stats.zmap(scores, compare, ddof=ddof, nan_policy='omit') + assert_allclose(z, stats.zmap(scores, compare[~np.isnan(compare)], + ddof=ddof)) + + @pytest.mark.parametrize('ddof', [0, 2]) + def test_zmap_nan_policy_omit_with_axis(self, ddof): + scores = np.arange(-5.0, 9.0).reshape(2, -1) + compare = np.linspace(-8, 6, 24).reshape(2, -1) + compare[0, 4] = np.nan + compare[0, 6] = np.nan + compare[1, 1] = np.nan + z = stats.zmap(scores, compare, nan_policy='omit', axis=1, ddof=ddof) + expected = np.array([stats.zmap(scores[0], + compare[0][~np.isnan(compare[0])], + ddof=ddof), + stats.zmap(scores[1], + compare[1][~np.isnan(compare[1])], + ddof=ddof)]) + assert_allclose(z, expected, rtol=1e-14) + + def test_zmap_nan_policy_raise(self): + scores = np.array([1, 2, 3]) + compare = np.array([-8, -3, 2, 7, 12, np.nan]) + with pytest.raises(ValueError, match='input contains nan'): + stats.zmap(scores, compare, nan_policy='raise') + + def test_zscore(self): + # not in R, so tested by using: + # (testcase[i] - mean(testcase, axis=0)) / sqrt(var(testcase) * 3/4) + y = stats.zscore([1, 2, 3, 4]) + desired = ([-1.3416407864999, -0.44721359549996, 0.44721359549996, + 1.3416407864999]) + assert_array_almost_equal(desired, y, decimal=12) + + def test_zscore_axis(self): + # Test use of 'axis' keyword in zscore. + x = np.array([[0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 2.0], + [2.0, 0.0, 2.0, 0.0]]) + + t1 = 1.0/np.sqrt(2.0/3) + t2 = np.sqrt(3.)/3 + t3 = np.sqrt(2.) + + z0 = stats.zscore(x, axis=0) + z1 = stats.zscore(x, axis=1) + + z0_expected = [[-t1, -t3/2, -t3/2, 0.0], + [0.0, t3, -t3/2, t1], + [t1, -t3/2, t3, -t1]] + z1_expected = [[-1.0, -1.0, 1.0, 1.0], + [-t2, -t2, -t2, np.sqrt(3.)], + [1.0, -1.0, 1.0, -1.0]] + + assert_array_almost_equal(z0, z0_expected) + assert_array_almost_equal(z1, z1_expected) + + def test_zscore_ddof(self): + # Test use of 'ddof' keyword in zscore. + x = np.array([[0.0, 0.0, 1.0, 1.0], + [0.0, 1.0, 2.0, 3.0]]) + + z = stats.zscore(x, axis=1, ddof=1) + + z0_expected = np.array([-0.5, -0.5, 0.5, 0.5])/(1.0/np.sqrt(3)) + z1_expected = np.array([-1.5, -0.5, 0.5, 1.5])/(np.sqrt(5./3)) + assert_array_almost_equal(z[0], z0_expected) + assert_array_almost_equal(z[1], z1_expected) + + def test_zscore_nan_propagate(self): + x = np.array([1, 2, np.nan, 4, 5]) + z = stats.zscore(x, nan_policy='propagate') + assert all(np.isnan(z)) + + def test_zscore_nan_omit(self): + x = np.array([1, 2, np.nan, 4, 5]) + + z = stats.zscore(x, nan_policy='omit') + + expected = np.array([-1.2649110640673518, + -0.6324555320336759, + np.nan, + 0.6324555320336759, + 1.2649110640673518 + ]) + assert_array_almost_equal(z, expected) + + def test_zscore_nan_omit_with_ddof(self): + x = np.array([np.nan, 1.0, 3.0, 5.0, 7.0, 9.0]) + z = stats.zscore(x, ddof=1, nan_policy='omit') + expected = np.r_[np.nan, stats.zscore(x[1:], ddof=1)] + assert_allclose(z, expected, rtol=1e-13) + + def test_zscore_nan_raise(self): + x = np.array([1, 2, np.nan, 4, 5]) + + assert_raises(ValueError, stats.zscore, x, nan_policy='raise') + + def test_zscore_constant_input_1d(self): + x = [-0.087] * 3 + z = stats.zscore(x) + assert_equal(z, np.full(len(x), np.nan)) + + def test_zscore_constant_input_2d(self): + x = np.array([[10.0, 10.0, 10.0, 10.0], + [10.0, 11.0, 12.0, 13.0]]) + z0 = stats.zscore(x, axis=0) + assert_equal(z0, np.array([[np.nan, -1.0, -1.0, -1.0], + [np.nan, 1.0, 1.0, 1.0]])) + z1 = stats.zscore(x, axis=1) + assert_equal(z1, np.array([[np.nan, np.nan, np.nan, np.nan], + stats.zscore(x[1])])) + z = stats.zscore(x, axis=None) + assert_equal(z, stats.zscore(x.ravel()).reshape(x.shape)) + + y = np.ones((3, 6)) + z = stats.zscore(y, axis=None) + assert_equal(z, np.full(y.shape, np.nan)) + + def test_zscore_constant_input_2d_nan_policy_omit(self): + x = np.array([[10.0, 10.0, 10.0, 10.0], + [10.0, 11.0, 12.0, np.nan], + [10.0, 12.0, np.nan, 10.0]]) + z0 = stats.zscore(x, nan_policy='omit', axis=0) + s = np.sqrt(3/2) + s2 = np.sqrt(2) + assert_allclose(z0, np.array([[np.nan, -s, -1.0, np.nan], + [np.nan, 0, 1.0, np.nan], + [np.nan, s, np.nan, np.nan]])) + z1 = stats.zscore(x, nan_policy='omit', axis=1) + assert_allclose(z1, np.array([[np.nan, np.nan, np.nan, np.nan], + [-s, 0, s, np.nan], + [-s2/2, s2, np.nan, -s2/2]])) + + def test_zscore_2d_all_nan_row(self): + # A row is all nan, and we use axis=1. + x = np.array([[np.nan, np.nan, np.nan, np.nan], + [10.0, 10.0, 12.0, 12.0]]) + z = stats.zscore(x, nan_policy='omit', axis=1) + assert_equal(z, np.array([[np.nan, np.nan, np.nan, np.nan], + [-1.0, -1.0, 1.0, 1.0]])) + + def test_zscore_2d_all_nan(self): + # The entire 2d array is nan, and we use axis=None. + y = np.full((2, 3), np.nan) + z = stats.zscore(y, nan_policy='omit', axis=None) + assert_equal(z, y) + + @pytest.mark.parametrize('x', [np.array([]), np.zeros((3, 0, 5))]) + def test_zscore_empty_input(self, x): + z = stats.zscore(x) + assert_equal(z, x) + + def test_gzscore_normal_array(self): + x = np.array([1, 2, 3, 4]) + z = stats.gzscore(x) + desired = np.log(x / stats.gmean(x)) / np.log(stats.gstd(x, ddof=0)) + assert_allclose(desired, z) + + def test_gzscore_masked_array(self): + x = np.array([1, 2, -1, 3, 4]) + mx = np.ma.masked_array(x, mask=[0, 0, 1, 0, 0]) + z = stats.gzscore(mx) + desired = ([-1.526072095151, -0.194700599824, np.inf, 0.584101799472, + 1.136670895503]) + assert_allclose(desired, z) + + def test_zscore_masked_element_0_gh19039(self): + # zscore returned all NaNs when 0th element was masked. See gh-19039. + rng = np.random.default_rng(8675309) + x = rng.standard_normal(10) + mask = np.zeros_like(x) + y = np.ma.masked_array(x, mask) + y.mask[0] = True + + ref = stats.zscore(x[1:]) # compute reference from non-masked elements + assert not np.any(np.isnan(ref)) + res = stats.zscore(y) + assert_allclose(res[1:], ref) + res = stats.zscore(y, axis=None) + assert_allclose(res[1:], ref) + + y[1:] = y[1] # when non-masked elements are identical, result is nan + res = stats.zscore(y) + assert_equal(res[1:], np.nan) + res = stats.zscore(y, axis=None) + assert_equal(res[1:], np.nan) + +class TestMedianAbsDeviation: + def setup_class(self): + self.dat_nan = np.array([2.20, 2.20, 2.4, 2.4, 2.5, 2.7, 2.8, 2.9, + 3.03, 3.03, 3.10, 3.37, 3.4, 3.4, 3.4, 3.5, + 3.6, 3.7, 3.7, 3.7, 3.7, 3.77, 5.28, np.nan]) + self.dat = np.array([2.20, 2.20, 2.4, 2.4, 2.5, 2.7, 2.8, 2.9, 3.03, + 3.03, 3.10, 3.37, 3.4, 3.4, 3.4, 3.5, 3.6, 3.7, + 3.7, 3.7, 3.7, 3.77, 5.28, 28.95]) + + def test_median_abs_deviation(self): + assert_almost_equal(stats.median_abs_deviation(self.dat, axis=None), + 0.355) + dat = self.dat.reshape(6, 4) + mad = stats.median_abs_deviation(dat, axis=0) + mad_expected = np.asarray([0.435, 0.5, 0.45, 0.4]) + assert_array_almost_equal(mad, mad_expected) + + def test_mad_nan_omit(self): + mad = stats.median_abs_deviation(self.dat_nan, nan_policy='omit') + assert_almost_equal(mad, 0.34) + + def test_axis_and_nan(self): + x = np.array([[1.0, 2.0, 3.0, 4.0, np.nan], + [1.0, 4.0, 5.0, 8.0, 9.0]]) + mad = stats.median_abs_deviation(x, axis=1) + assert_equal(mad, np.array([np.nan, 3.0])) + + def test_nan_policy_omit_with_inf(self): + z = np.array([1, 3, 4, 6, 99, np.nan, np.inf]) + mad = stats.median_abs_deviation(z, nan_policy='omit') + assert_equal(mad, 3.0) + + @pytest.mark.parametrize('axis', [0, 1, 2, None]) + def test_size_zero_with_axis(self, axis): + x = np.zeros((3, 0, 4)) + mad = stats.median_abs_deviation(x, axis=axis) + assert_equal(mad, np.full_like(x.sum(axis=axis), fill_value=np.nan)) + + @pytest.mark.parametrize('nan_policy, expected', + [('omit', np.array([np.nan, 1.5, 1.5])), + ('propagate', np.array([np.nan, np.nan, 1.5]))]) + def test_nan_policy_with_axis(self, nan_policy, expected): + x = np.array([[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], + [1, 5, 3, 6, np.nan, np.nan], + [5, 6, 7, 9, 9, 10]]) + mad = stats.median_abs_deviation(x, nan_policy=nan_policy, axis=1) + assert_equal(mad, expected) + + @pytest.mark.parametrize('axis, expected', + [(1, [2.5, 2.0, 12.0]), (None, 4.5)]) + def test_center_mean_with_nan(self, axis, expected): + x = np.array([[1, 2, 4, 9, np.nan], + [0, 1, 1, 1, 12], + [-10, -10, -10, 20, 20]]) + mad = stats.median_abs_deviation(x, center=np.mean, nan_policy='omit', + axis=axis) + assert_allclose(mad, expected, rtol=1e-15, atol=1e-15) + + def test_center_not_callable(self): + with pytest.raises(TypeError, match='callable'): + stats.median_abs_deviation([1, 2, 3, 5], center=99) + + +def _check_warnings(warn_list, expected_type, expected_len): + """ + Checks that all of the warnings from a list returned by + `warnings.catch_all(record=True)` are of the required type and that the list + contains expected number of warnings. + """ + assert_equal(len(warn_list), expected_len, "number of warnings") + for warn_ in warn_list: + assert_(warn_.category is expected_type) + + +class TestIQR: + + def test_basic(self): + x = np.arange(8) * 0.5 + np.random.shuffle(x) + assert_equal(stats.iqr(x), 1.75) + + def test_api(self): + d = np.ones((5, 5)) + stats.iqr(d) + stats.iqr(d, None) + stats.iqr(d, 1) + stats.iqr(d, (0, 1)) + stats.iqr(d, None, (10, 90)) + stats.iqr(d, None, (30, 20), 1.0) + stats.iqr(d, None, (25, 75), 1.5, 'propagate') + stats.iqr(d, None, (50, 50), 'normal', 'raise', 'linear') + stats.iqr(d, None, (25, 75), -0.4, 'omit', 'lower', True) + + def test_empty(self): + assert_equal(stats.iqr([]), np.nan) + assert_equal(stats.iqr(np.arange(0)), np.nan) + + def test_constant(self): + # Constant array always gives 0 + x = np.ones((7, 4)) + assert_equal(stats.iqr(x), 0.0) + assert_array_equal(stats.iqr(x, axis=0), np.zeros(4)) + assert_array_equal(stats.iqr(x, axis=1), np.zeros(7)) + assert_equal(stats.iqr(x, interpolation='linear'), 0.0) + assert_equal(stats.iqr(x, interpolation='midpoint'), 0.0) + assert_equal(stats.iqr(x, interpolation='nearest'), 0.0) + assert_equal(stats.iqr(x, interpolation='lower'), 0.0) + assert_equal(stats.iqr(x, interpolation='higher'), 0.0) + + # 0 only along constant dimensions + # This also tests much of `axis` + y = np.ones((4, 5, 6)) * np.arange(6) + assert_array_equal(stats.iqr(y, axis=0), np.zeros((5, 6))) + assert_array_equal(stats.iqr(y, axis=1), np.zeros((4, 6))) + assert_array_equal(stats.iqr(y, axis=2), np.full((4, 5), 2.5)) + assert_array_equal(stats.iqr(y, axis=(0, 1)), np.zeros(6)) + assert_array_equal(stats.iqr(y, axis=(0, 2)), np.full(5, 3.)) + assert_array_equal(stats.iqr(y, axis=(1, 2)), np.full(4, 3.)) + + def test_scalarlike(self): + x = np.arange(1) + 7.0 + assert_equal(stats.iqr(x[0]), 0.0) + assert_equal(stats.iqr(x), 0.0) + assert_array_equal(stats.iqr(x, keepdims=True), [0.0]) + + def test_2D(self): + x = np.arange(15).reshape((3, 5)) + assert_equal(stats.iqr(x), 7.0) + assert_array_equal(stats.iqr(x, axis=0), np.full(5, 5.)) + assert_array_equal(stats.iqr(x, axis=1), np.full(3, 2.)) + assert_array_equal(stats.iqr(x, axis=(0, 1)), 7.0) + assert_array_equal(stats.iqr(x, axis=(1, 0)), 7.0) + + def test_axis(self): + # The `axis` keyword is also put through its paces in `test_keepdims`. + o = np.random.normal(size=(71, 23)) + x = np.dstack([o] * 10) # x.shape = (71, 23, 10) + q = stats.iqr(o) + + assert_equal(stats.iqr(x, axis=(0, 1)), q) + x = np.moveaxis(x, -1, 0) # x.shape = (10, 71, 23) + assert_equal(stats.iqr(x, axis=(2, 1)), q) + x = x.swapaxes(0, 1) # x.shape = (71, 10, 23) + assert_equal(stats.iqr(x, axis=(0, 2)), q) + x = x.swapaxes(0, 1) # x.shape = (10, 71, 23) + + assert_equal(stats.iqr(x, axis=(0, 1, 2)), + stats.iqr(x, axis=None)) + assert_equal(stats.iqr(x, axis=(0,)), + stats.iqr(x, axis=0)) + + d = np.arange(3 * 5 * 7 * 11) + # Older versions of numpy only shuffle along axis=0. + # Not sure about newer, don't care. + np.random.shuffle(d) + d = d.reshape((3, 5, 7, 11)) + assert_equal(stats.iqr(d, axis=(0, 1, 2))[0], + stats.iqr(d[:,:,:, 0].ravel())) + assert_equal(stats.iqr(d, axis=(0, 1, 3))[1], + stats.iqr(d[:,:, 1,:].ravel())) + assert_equal(stats.iqr(d, axis=(3, 1, -4))[2], + stats.iqr(d[:,:, 2,:].ravel())) + assert_equal(stats.iqr(d, axis=(3, 1, 2))[2], + stats.iqr(d[2,:,:,:].ravel())) + assert_equal(stats.iqr(d, axis=(3, 2))[2, 1], + stats.iqr(d[2, 1,:,:].ravel())) + assert_equal(stats.iqr(d, axis=(1, -2))[2, 1], + stats.iqr(d[2, :, :, 1].ravel())) + assert_equal(stats.iqr(d, axis=(1, 3))[2, 2], + stats.iqr(d[2, :, 2,:].ravel())) + + assert_raises(AxisError, stats.iqr, d, axis=4) + assert_raises(ValueError, stats.iqr, d, axis=(0, 0)) + + def test_rng(self): + x = np.arange(5) + assert_equal(stats.iqr(x), 2) + assert_equal(stats.iqr(x, rng=(25, 87.5)), 2.5) + assert_equal(stats.iqr(x, rng=(12.5, 75)), 2.5) + assert_almost_equal(stats.iqr(x, rng=(10, 50)), 1.6) # 3-1.4 + + assert_raises(ValueError, stats.iqr, x, rng=(0, 101)) + assert_raises(ValueError, stats.iqr, x, rng=(np.nan, 25)) + assert_raises(TypeError, stats.iqr, x, rng=(0, 50, 60)) + + def test_interpolation(self): + x = np.arange(5) + y = np.arange(4) + # Default + assert_equal(stats.iqr(x), 2) + assert_equal(stats.iqr(y), 1.5) + # Linear + assert_equal(stats.iqr(x, interpolation='linear'), 2) + assert_equal(stats.iqr(y, interpolation='linear'), 1.5) + # Higher + assert_equal(stats.iqr(x, interpolation='higher'), 2) + assert_equal(stats.iqr(x, rng=(25, 80), interpolation='higher'), 3) + assert_equal(stats.iqr(y, interpolation='higher'), 2) + # Lower (will generally, but not always be the same as higher) + assert_equal(stats.iqr(x, interpolation='lower'), 2) + assert_equal(stats.iqr(x, rng=(25, 80), interpolation='lower'), 2) + assert_equal(stats.iqr(y, interpolation='lower'), 2) + # Nearest + assert_equal(stats.iqr(x, interpolation='nearest'), 2) + assert_equal(stats.iqr(y, interpolation='nearest'), 1) + # Midpoint + assert_equal(stats.iqr(x, interpolation='midpoint'), 2) + assert_equal(stats.iqr(x, rng=(25, 80), interpolation='midpoint'), 2.5) + assert_equal(stats.iqr(y, interpolation='midpoint'), 2) + + # Check all method= values new in numpy 1.22.0 are accepted + for method in ('inverted_cdf', 'averaged_inverted_cdf', + 'closest_observation', 'interpolated_inverted_cdf', + 'hazen', 'weibull', 'median_unbiased', + 'normal_unbiased'): + stats.iqr(y, interpolation=method) + + assert_raises(ValueError, stats.iqr, x, interpolation='foobar') + + def test_keepdims(self): + # Also tests most of `axis` + x = np.ones((3, 5, 7, 11)) + assert_equal(stats.iqr(x, axis=None, keepdims=False).shape, ()) + assert_equal(stats.iqr(x, axis=2, keepdims=False).shape, (3, 5, 11)) + assert_equal(stats.iqr(x, axis=(0, 1), keepdims=False).shape, (7, 11)) + assert_equal(stats.iqr(x, axis=(0, 3), keepdims=False).shape, (5, 7)) + assert_equal(stats.iqr(x, axis=(1,), keepdims=False).shape, (3, 7, 11)) + assert_equal(stats.iqr(x, (0, 1, 2, 3), keepdims=False).shape, ()) + assert_equal(stats.iqr(x, axis=(0, 1, 3), keepdims=False).shape, (7,)) + + assert_equal(stats.iqr(x, axis=None, keepdims=True).shape, (1, 1, 1, 1)) + assert_equal(stats.iqr(x, axis=2, keepdims=True).shape, (3, 5, 1, 11)) + assert_equal(stats.iqr(x, axis=(0, 1), keepdims=True).shape, (1, 1, 7, 11)) + assert_equal(stats.iqr(x, axis=(0, 3), keepdims=True).shape, (1, 5, 7, 1)) + assert_equal(stats.iqr(x, axis=(1,), keepdims=True).shape, (3, 1, 7, 11)) + assert_equal(stats.iqr(x, (0, 1, 2, 3), keepdims=True).shape, (1, 1, 1, 1)) + assert_equal(stats.iqr(x, axis=(0, 1, 3), keepdims=True).shape, (1, 1, 7, 1)) + + def test_nanpolicy(self): + x = np.arange(15.0).reshape((3, 5)) + + # No NaNs + assert_equal(stats.iqr(x, nan_policy='propagate'), 7) + assert_equal(stats.iqr(x, nan_policy='omit'), 7) + assert_equal(stats.iqr(x, nan_policy='raise'), 7) + + # Yes NaNs + x[1, 2] = np.nan + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + assert_equal(stats.iqr(x, nan_policy='propagate'), + np.nan) + assert_equal(stats.iqr(x, axis=0, nan_policy='propagate'), + [5, 5, np.nan, 5, 5]) + assert_equal(stats.iqr(x, axis=1, nan_policy='propagate'), + [2, np.nan, 2]) + + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + assert_equal(stats.iqr(x, nan_policy='omit'), 7.5) + assert_equal(stats.iqr(x, axis=0, nan_policy='omit'), np.full(5, 5)) + assert_equal(stats.iqr(x, axis=1, nan_policy='omit'), [2, 2.5, 2]) + + assert_raises(ValueError, stats.iqr, x, nan_policy='raise') + assert_raises(ValueError, stats.iqr, x, axis=0, nan_policy='raise') + assert_raises(ValueError, stats.iqr, x, axis=1, nan_policy='raise') + + # Bad policy + assert_raises(ValueError, stats.iqr, x, nan_policy='barfood') + + def test_scale(self): + x = np.arange(15.0).reshape((3, 5)) + + # No NaNs + assert_equal(stats.iqr(x, scale=1.0), 7) + assert_almost_equal(stats.iqr(x, scale='normal'), 7 / 1.3489795) + assert_equal(stats.iqr(x, scale=2.0), 3.5) + + # Yes NaNs + x[1, 2] = np.nan + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + assert_equal(stats.iqr(x, scale=1.0, nan_policy='propagate'), np.nan) + assert_equal(stats.iqr(x, scale='normal', nan_policy='propagate'), np.nan) + assert_equal(stats.iqr(x, scale=2.0, nan_policy='propagate'), np.nan) + # axis=1 chosen to show behavior with both nans and without + assert_equal(stats.iqr(x, axis=1, scale=1.0, + nan_policy='propagate'), [2, np.nan, 2]) + assert_almost_equal(stats.iqr(x, axis=1, scale='normal', + nan_policy='propagate'), + np.array([2, np.nan, 2]) / 1.3489795) + assert_equal(stats.iqr(x, axis=1, scale=2.0, nan_policy='propagate'), + [1, np.nan, 1]) + # Since NumPy 1.17.0.dev, warnings are no longer emitted by + # np.percentile with nans, so we don't check the number of + # warnings here. See https://github.com/numpy/numpy/pull/12679. + + assert_equal(stats.iqr(x, scale=1.0, nan_policy='omit'), 7.5) + assert_almost_equal(stats.iqr(x, scale='normal', nan_policy='omit'), + 7.5 / 1.3489795) + assert_equal(stats.iqr(x, scale=2.0, nan_policy='omit'), 3.75) + + # Bad scale + assert_raises(ValueError, stats.iqr, x, scale='foobar') + + +class TestMoments: + """ + Comparison numbers are found using R v.1.5.1 + note that length(testcase) = 4 + testmathworks comes from documentation for the + Statistics Toolbox for Matlab and can be found at both + https://www.mathworks.com/help/stats/kurtosis.html + https://www.mathworks.com/help/stats/skewness.html + Note that both test cases came from here. + """ + testcase = [1,2,3,4] + scalar_testcase = 4. + np.random.seed(1234) + testcase_moment_accuracy = np.random.rand(42) + testmathworks = [1.165, 0.6268, 0.0751, 0.3516, -0.6965] + + def _assert_equal(self, actual, expect, *, shape=None, dtype=None): + expect = np.asarray(expect) + if shape is not None: + expect = np.broadcast_to(expect, shape) + assert_array_equal(actual, expect) + if dtype is None: + dtype = expect.dtype + assert actual.dtype == dtype + + @pytest.mark.parametrize('size', [10, (10, 2)]) + @pytest.mark.parametrize('m, c', product((0, 1, 2, 3), (None, 0, 1))) + def test_moment_center_scalar_moment(self, size, m, c): + rng = np.random.default_rng(6581432544381372042) + x = rng.random(size=size) + res = stats.moment(x, m, center=c) + c = np.mean(x, axis=0) if c is None else c + ref = np.sum((x - c)**m, axis=0)/len(x) + assert_allclose(res, ref, atol=1e-16) + + @pytest.mark.parametrize('size', [10, (10, 2)]) + @pytest.mark.parametrize('c', (None, 0, 1)) + def test_moment_center_array_moment(self, size, c): + rng = np.random.default_rng(1706828300224046506) + x = rng.random(size=size) + m = [0, 1, 2, 3] + res = stats.moment(x, m, center=c) + ref = [stats.moment(x, i, center=c) for i in m] + assert_equal(res, ref) + + def test_moment(self): + # mean((testcase-mean(testcase))**power,axis=0),axis=0))**power)) + y = stats.moment(self.scalar_testcase) + assert_approx_equal(y, 0.0) + y = stats.moment(self.testcase, 0) + assert_approx_equal(y, 1.0) + y = stats.moment(self.testcase, 1) + assert_approx_equal(y, 0.0, 10) + y = stats.moment(self.testcase, 2) + assert_approx_equal(y, 1.25) + y = stats.moment(self.testcase, 3) + assert_approx_equal(y, 0.0) + y = stats.moment(self.testcase, 4) + assert_approx_equal(y, 2.5625) + + # check array_like input for moment + y = stats.moment(self.testcase, [1, 2, 3, 4]) + assert_allclose(y, [0, 1.25, 0, 2.5625]) + + # check moment input consists only of integers + y = stats.moment(self.testcase, 0.0) + assert_approx_equal(y, 1.0) + assert_raises(ValueError, stats.moment, self.testcase, 1.2) + y = stats.moment(self.testcase, [1.0, 2, 3, 4.0]) + assert_allclose(y, [0, 1.25, 0, 2.5625]) + + # test empty input + message = r"Mean of empty slice\.|invalid value encountered.*" + with pytest.warns(RuntimeWarning, match=message): + y = stats.moment([]) + self._assert_equal(y, np.nan, dtype=np.float64) + y = stats.moment(np.array([], dtype=np.float32)) + self._assert_equal(y, np.nan, dtype=np.float32) + y = stats.moment(np.zeros((1, 0)), axis=0) + self._assert_equal(y, [], shape=(0,), dtype=np.float64) + y = stats.moment([[]], axis=1) + self._assert_equal(y, np.nan, shape=(1,), dtype=np.float64) + y = stats.moment([[]], order=[0, 1], axis=0) + self._assert_equal(y, [], shape=(2, 0)) + + x = np.arange(10.) + x[9] = np.nan + assert_equal(stats.moment(x, 2), np.nan) + assert_almost_equal(stats.moment(x, nan_policy='omit'), 0.0) + assert_raises(ValueError, stats.moment, x, nan_policy='raise') + assert_raises(ValueError, stats.moment, x, nan_policy='foobar') + + @pytest.mark.parametrize('dtype', [np.float32, np.float64, np.complex128]) + @pytest.mark.parametrize('expect, order', [(0, 1), (1, 0)]) + def test_constant_moments(self, dtype, expect, order): + x = np.random.rand(5).astype(dtype) + y = stats.moment(x, order=order) + self._assert_equal(y, expect, dtype=dtype) + + y = stats.moment(np.broadcast_to(x, (6, 5)), axis=0, order=order) + self._assert_equal(y, expect, shape=(5,), dtype=dtype) + + y = stats.moment(np.broadcast_to(x, (1, 2, 3, 4, 5)), axis=2, + order=order) + self._assert_equal(y, expect, shape=(1, 2, 4, 5), dtype=dtype) + + y = stats.moment(np.broadcast_to(x, (1, 2, 3, 4, 5)), axis=None, + order=order) + self._assert_equal(y, expect, shape=(), dtype=dtype) + + def test_moment_propagate_nan(self): + # Check that the shape of the result is the same for inputs + # with and without nans, cf gh-5817 + a = np.arange(8).reshape(2, -1).astype(float) + a[1, 0] = np.nan + mm = stats.moment(a, 2, axis=1, nan_policy="propagate") + np.testing.assert_allclose(mm, [1.25, np.nan], atol=1e-15) + + def test_moment_empty_order(self): + # tests moment with empty `order` list + with pytest.raises(ValueError, match=r"'order' must be a scalar or a" + r" non-empty 1D list/array."): + stats.moment([1, 2, 3, 4], order=[]) + + def test_rename_moment_order(self): + # Parameter 'order' was formerly known as 'moment'. The old name + # has not been deprecated, so it must continue to work. + x = np.arange(10) + res = stats.moment(x, moment=3) + ref = stats.moment(x, order=3) + np.testing.assert_equal(res, ref) + + def test_skewness(self): + # Scalar test case + y = stats.skew(self.scalar_testcase) + assert np.isnan(y) + # sum((testmathworks-mean(testmathworks,axis=0))**3,axis=0) / + # ((sqrt(var(testmathworks)*4/5))**3)/5 + y = stats.skew(self.testmathworks) + assert_approx_equal(y, -0.29322304336607, 10) + y = stats.skew(self.testmathworks, bias=0) + assert_approx_equal(y, -0.437111105023940, 10) + y = stats.skew(self.testcase) + assert_approx_equal(y, 0.0, 10) + + x = np.arange(10.) + x[9] = np.nan + with np.errstate(invalid='ignore'): + assert_equal(stats.skew(x), np.nan) + assert_equal(stats.skew(x, nan_policy='omit'), 0.) + assert_raises(ValueError, stats.skew, x, nan_policy='raise') + assert_raises(ValueError, stats.skew, x, nan_policy='foobar') + + def test_skewness_scalar(self): + # `skew` must return a scalar for 1-dim input + assert_equal(stats.skew(arange(10)), 0.0) + + def test_skew_propagate_nan(self): + # Check that the shape of the result is the same for inputs + # with and without nans, cf gh-5817 + a = np.arange(8).reshape(2, -1).astype(float) + a[1, 0] = np.nan + with np.errstate(invalid='ignore'): + s = stats.skew(a, axis=1, nan_policy="propagate") + np.testing.assert_allclose(s, [0, np.nan], atol=1e-15) + + def test_skew_constant_value(self): + # Skewness of a constant input should be zero even when the mean is not + # exact (gh-13245) + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + a = np.repeat(-0.27829495, 10) + assert np.isnan(stats.skew(a)) + assert np.isnan(stats.skew(a * float(2**50))) + assert np.isnan(stats.skew(a / float(2**50))) + assert np.isnan(stats.skew(a, bias=False)) + + # similarly, from gh-11086: + assert np.isnan(stats.skew([14.3]*7)) + assert np.isnan(stats.skew(1 + np.arange(-3, 4)*1e-16)) + + def test_kurtosis(self): + # Scalar test case + y = stats.kurtosis(self.scalar_testcase) + assert np.isnan(y) + + # sum((testcase-mean(testcase,axis=0))**4,axis=0) + # / ((sqrt(var(testcase)*3/4))**4) + # / 4 + # + # sum((test2-mean(testmathworks,axis=0))**4,axis=0) + # / ((sqrt(var(testmathworks)*4/5))**4) + # / 5 + # + # Set flags for axis = 0 and + # fisher=0 (Pearson's defn of kurtosis for compatibility with Matlab) + y = stats.kurtosis(self.testmathworks, 0, fisher=0, bias=1) + assert_approx_equal(y, 2.1658856802973, 10) + + # Note that MATLAB has confusing docs for the following case + # kurtosis(x,0) gives an unbiased estimate of Pearson's skewness + # kurtosis(x) gives a biased estimate of Fisher's skewness (Pearson-3) + # The MATLAB docs imply that both should give Fisher's + y = stats.kurtosis(self.testmathworks, fisher=0, bias=0) + assert_approx_equal(y, 3.663542721189047, 10) + y = stats.kurtosis(self.testcase, 0, 0) + assert_approx_equal(y, 1.64) + + x = np.arange(10.) + x[9] = np.nan + assert_equal(stats.kurtosis(x), np.nan) + assert_almost_equal(stats.kurtosis(x, nan_policy='omit'), -1.230000) + assert_raises(ValueError, stats.kurtosis, x, nan_policy='raise') + assert_raises(ValueError, stats.kurtosis, x, nan_policy='foobar') + + def test_kurtosis_array_scalar(self): + assert_equal(type(stats.kurtosis([1, 2, 3])), np.float64) + + def test_kurtosis_propagate_nan(self): + # Check that the shape of the result is the same for inputs + # with and without nans, cf gh-5817 + a = np.arange(8).reshape(2, -1).astype(float) + a[1, 0] = np.nan + k = stats.kurtosis(a, axis=1, nan_policy="propagate") + np.testing.assert_allclose(k, [-1.36, np.nan], atol=1e-15) + + def test_kurtosis_constant_value(self): + # Kurtosis of a constant input should be zero, even when the mean is not + # exact (gh-13245) + a = np.repeat(-0.27829495, 10) + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + assert np.isnan(stats.kurtosis(a, fisher=False)) + assert np.isnan(stats.kurtosis(a * float(2**50), fisher=False)) + assert np.isnan(stats.kurtosis(a / float(2**50), fisher=False)) + assert np.isnan(stats.kurtosis(a, fisher=False, bias=False)) + + def test_moment_accuracy(self): + # 'moment' must have a small enough error compared to the slower + # but very accurate numpy.power() implementation. + tc_no_mean = self.testcase_moment_accuracy - \ + np.mean(self.testcase_moment_accuracy) + assert_allclose(np.power(tc_no_mean, 42).mean(), + stats.moment(self.testcase_moment_accuracy, 42)) + + def test_precision_loss_gh15554(self): + # gh-15554 was one of several issues that have reported problems with + # constant or near-constant input. We can't always fix these, but + # make sure there's a warning. + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + rng = np.random.default_rng(34095309370) + a = rng.random(size=(100, 10)) + a[:, 0] = 1.01 + stats.skew(a)[0] + + def test_empty_1d(self): + message = r"Mean of empty slice\.|invalid value encountered.*" + with pytest.warns(RuntimeWarning, match=message): + stats.skew([]) + with pytest.warns(RuntimeWarning, match=message): + stats.kurtosis([]) + + +@hypothesis.strategies.composite +def ttest_data_axis_strategy(draw): + # draw an array under shape and value constraints + dtype = npst.floating_dtypes() + elements = dict(allow_nan=False, allow_infinity=False) + shape = npst.array_shapes(min_dims=1, min_side=2) + data = draw(npst.arrays(dtype=dtype, elements=elements, shape=shape)) + + # determine axes over which nonzero variance can be computed accurately + ok_axes = [] + # Locally, I don't need catch_warnings or simplefilter, and I can just + # suppress RuntimeWarning. I include all that in hope of getting the same + # behavior on CI. + with warnings.catch_warnings(): + warnings.simplefilter("error") + for axis in range(len(data.shape)): + with contextlib.suppress(Exception): + var = stats.moment(data, order=2, axis=axis) + if np.all(var > 0) and np.all(np.isfinite(var)): + ok_axes.append(axis) + # if there are no valid axes, tell hypothesis to try a different example + hypothesis.assume(ok_axes) + + # draw one of the valid axes + axis = draw(hypothesis.strategies.sampled_from(ok_axes)) + + return data, axis + + +class TestStudentTest: + X1 = np.array([-1, 0, 1]) + X2 = np.array([0, 1, 2]) + T1_0 = 0 + P1_0 = 1 + T1_1 = -1.7320508075 + P1_1 = 0.22540333075 + T1_2 = -3.464102 + P1_2 = 0.0741799 + T2_0 = 1.732051 + P2_0 = 0.2254033 + P1_1_l = P1_1 / 2 + P1_1_g = 1 - (P1_1 / 2) + + def test_onesample(self): + with suppress_warnings() as sup, \ + np.errstate(invalid="ignore", divide="ignore"): + sup.filter(RuntimeWarning, "Degrees of freedom <= 0 for slice") + t, p = stats.ttest_1samp(4., 3.) + assert_(np.isnan(t)) + assert_(np.isnan(p)) + + t, p = stats.ttest_1samp(self.X1, 0) + + assert_array_almost_equal(t, self.T1_0) + assert_array_almost_equal(p, self.P1_0) + + res = stats.ttest_1samp(self.X1, 0) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + t, p = stats.ttest_1samp(self.X2, 0) + + assert_array_almost_equal(t, self.T2_0) + assert_array_almost_equal(p, self.P2_0) + + t, p = stats.ttest_1samp(self.X1, 1) + + assert_array_almost_equal(t, self.T1_1) + assert_array_almost_equal(p, self.P1_1) + + t, p = stats.ttest_1samp(self.X1, 2) + + assert_array_almost_equal(t, self.T1_2) + assert_array_almost_equal(p, self.P1_2) + + # check nan policy + x = stats.norm.rvs(loc=5, scale=10, size=51, random_state=7654567) + x[50] = np.nan + with np.errstate(invalid="ignore"): + assert_array_equal(stats.ttest_1samp(x, 5.0), (np.nan, np.nan)) + + assert_array_almost_equal(stats.ttest_1samp(x, 5.0, nan_policy='omit'), + (-1.6412624074367159, 0.107147027334048005)) + assert_raises(ValueError, stats.ttest_1samp, x, 5.0, nan_policy='raise') + assert_raises(ValueError, stats.ttest_1samp, x, 5.0, + nan_policy='foobar') + + def test_1samp_alternative(self): + assert_raises(ValueError, stats.ttest_1samp, self.X1, 0, + alternative="error") + + t, p = stats.ttest_1samp(self.X1, 1, alternative="less") + assert_allclose(p, self.P1_1_l) + assert_allclose(t, self.T1_1) + + t, p = stats.ttest_1samp(self.X1, 1, alternative="greater") + assert_allclose(p, self.P1_1_g) + assert_allclose(t, self.T1_1) + + @pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater']) + def test_1samp_ci_1d(self, alternative): + # test confidence interval method against reference values + rng = np.random.default_rng(8066178009154342972) + n = 10 + x = rng.normal(size=n, loc=1.5, scale=2) + popmean = rng.normal() # this shouldn't affect confidence interval + # Reference values generated with R t.test: + # options(digits=16) + # x = c(2.75532884, 0.93892217, 0.94835861, 1.49489446, -0.62396595, + # -1.88019867, -1.55684465, 4.88777104, 5.15310979, 4.34656348) + # t.test(x, conf.level=0.85, alternative='l') + + ref = {'two-sided': [0.3594423211709136, 2.9333455028290860], + 'greater': [0.7470806207371626, np.inf], + 'less': [-np.inf, 2.545707203262837]} + res = stats.ttest_1samp(x, popmean=popmean, alternative=alternative) + ci = res.confidence_interval(confidence_level=0.85) + assert_allclose(ci, ref[alternative]) + assert_equal(res.df, n-1) + + def test_1samp_ci_iv(self): + # test `confidence_interval` method input validation + res = stats.ttest_1samp(np.arange(10), 0) + message = '`confidence_level` must be a number between 0 and 1.' + with pytest.raises(ValueError, match=message): + res.confidence_interval(confidence_level=10) + + @pytest.mark.xslow + @hypothesis.given(alpha=hypothesis.strategies.floats(1e-15, 1-1e-15), + data_axis=ttest_data_axis_strategy()) + @pytest.mark.parametrize('alternative', ['less', 'greater']) + def test_pvalue_ci(self, alpha, data_axis, alternative): + # test relationship between one-sided p-values and confidence intervals + data, axis = data_axis + res = stats.ttest_1samp(data, 0, + alternative=alternative, axis=axis) + l, u = res.confidence_interval(confidence_level=alpha) + popmean = l if alternative == 'greater' else u + popmean = np.expand_dims(popmean, axis=axis) + res = stats.ttest_1samp(data, popmean, + alternative=alternative, axis=axis) + np.testing.assert_allclose(res.pvalue, 1-alpha) + + +class TestPercentileOfScore: + + def f(self, *args, **kwargs): + return stats.percentileofscore(*args, **kwargs) + + @pytest.mark.parametrize("kind, result", [("rank", 40), + ("mean", 35), + ("strict", 30), + ("weak", 40)]) + def test_unique(self, kind, result): + a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + assert_equal(self.f(a, 4, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 45), + ("mean", 40), + ("strict", 30), + ("weak", 50)]) + def test_multiple2(self, kind, result): + a = [1, 2, 3, 4, 4, 5, 6, 7, 8, 9] + assert_equal(self.f(a, 4, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 50), + ("mean", 45), + ("strict", 30), + ("weak", 60)]) + def test_multiple3(self, kind, result): + a = [1, 2, 3, 4, 4, 4, 5, 6, 7, 8] + assert_equal(self.f(a, 4, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 30), + ("mean", 30), + ("strict", 30), + ("weak", 30)]) + def test_missing(self, kind, result): + a = [1, 2, 3, 5, 6, 7, 8, 9, 10, 11] + assert_equal(self.f(a, 4, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 40), + ("mean", 35), + ("strict", 30), + ("weak", 40)]) + def test_large_numbers(self, kind, result): + a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] + assert_equal(self.f(a, 40, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 50), + ("mean", 45), + ("strict", 30), + ("weak", 60)]) + def test_large_numbers_multiple3(self, kind, result): + a = [10, 20, 30, 40, 40, 40, 50, 60, 70, 80] + assert_equal(self.f(a, 40, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", 30), + ("mean", 30), + ("strict", 30), + ("weak", 30)]) + def test_large_numbers_missing(self, kind, result): + a = [10, 20, 30, 50, 60, 70, 80, 90, 100, 110] + assert_equal(self.f(a, 40, kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", [0, 10, 100, 100]), + ("mean", [0, 5, 95, 100]), + ("strict", [0, 0, 90, 100]), + ("weak", [0, 10, 100, 100])]) + def test_boundaries(self, kind, result): + a = [10, 20, 30, 50, 60, 70, 80, 90, 100, 110] + assert_equal(self.f(a, [0, 10, 110, 200], kind=kind), result) + + @pytest.mark.parametrize("kind, result", [("rank", [0, 10, 100]), + ("mean", [0, 5, 95]), + ("strict", [0, 0, 90]), + ("weak", [0, 10, 100])]) + def test_inf(self, kind, result): + a = [1, 2, 3, 4, 5, 6, 7, 8, 9, +np.inf] + assert_equal(self.f(a, [-np.inf, 1, +np.inf], kind=kind), result) + + cases = [("propagate", [], 1, np.nan), + ("propagate", [np.nan], 1, np.nan), + ("propagate", [np.nan], [0, 1, 2], [np.nan, np.nan, np.nan]), + ("propagate", [1, 2], [1, 2, np.nan], [50, 100, np.nan]), + ("omit", [1, 2, np.nan], [0, 1, 2], [0, 50, 100]), + ("omit", [1, 2], [0, 1, np.nan], [0, 50, np.nan]), + ("omit", [np.nan, np.nan], [0, 1, 2], [np.nan, np.nan, np.nan])] + + @pytest.mark.parametrize("policy, a, score, result", cases) + def test_nans_ok(self, policy, a, score, result): + assert_equal(self.f(a, score, nan_policy=policy), result) + + cases = [ + ("raise", [1, 2, 3, np.nan], [1, 2, 3], + "The input contains nan values"), + ("raise", [1, 2, 3], [1, 2, 3, np.nan], + "The input contains nan values"), + ] + + @pytest.mark.parametrize("policy, a, score, message", cases) + def test_nans_fail(self, policy, a, score, message): + with assert_raises(ValueError, match=message): + self.f(a, score, nan_policy=policy) + + @pytest.mark.parametrize("shape", [ + (6, ), + (2, 3), + (2, 1, 3), + (2, 1, 1, 3), + ]) + def test_nd(self, shape): + a = np.array([0, 1, 2, 3, 4, 5]) + scores = a.reshape(shape) + results = scores*10 + a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + assert_equal(self.f(a, scores, kind="rank"), results) + + +PowerDivCase = namedtuple('Case', # type: ignore[name-match] + ['f_obs', 'f_exp', 'ddof', 'axis', + 'chi2', # Pearson's + 'log', # G-test (log-likelihood) + 'mod_log', # Modified log-likelihood + 'cr', # Cressie-Read (lambda=2/3) + ]) + +# The details of the first two elements in power_div_1d_cases are used +# in a test in TestPowerDivergence. Check that code before making +# any changes here. +power_div_1d_cases = [ + # Use the default f_exp. + PowerDivCase(f_obs=[4, 8, 12, 8], f_exp=None, ddof=0, axis=None, + chi2=4, + log=2*(4*np.log(4/8) + 12*np.log(12/8)), + mod_log=2*(8*np.log(8/4) + 8*np.log(8/12)), + cr=(4*((4/8)**(2/3) - 1) + 12*((12/8)**(2/3) - 1))/(5/9)), + # Give a non-uniform f_exp. + PowerDivCase(f_obs=[4, 8, 12, 8], f_exp=[2, 16, 12, 2], ddof=0, axis=None, + chi2=24, + log=2*(4*np.log(4/2) + 8*np.log(8/16) + 8*np.log(8/2)), + mod_log=2*(2*np.log(2/4) + 16*np.log(16/8) + 2*np.log(2/8)), + cr=(4*((4/2)**(2/3) - 1) + 8*((8/16)**(2/3) - 1) + + 8*((8/2)**(2/3) - 1))/(5/9)), + # f_exp is a scalar. + PowerDivCase(f_obs=[4, 8, 12, 8], f_exp=8, ddof=0, axis=None, + chi2=4, + log=2*(4*np.log(4/8) + 12*np.log(12/8)), + mod_log=2*(8*np.log(8/4) + 8*np.log(8/12)), + cr=(4*((4/8)**(2/3) - 1) + 12*((12/8)**(2/3) - 1))/(5/9)), + # f_exp equal to f_obs. + PowerDivCase(f_obs=[3, 5, 7, 9], f_exp=[3, 5, 7, 9], ddof=0, axis=0, + chi2=0, log=0, mod_log=0, cr=0), +] + + +power_div_empty_cases = [ + # Shape is (0,)--a data set with length 0. The computed + # test statistic should be 0. + PowerDivCase(f_obs=[], + f_exp=None, ddof=0, axis=0, + chi2=0, log=0, mod_log=0, cr=0), + # Shape is (0, 3). This is 3 data sets, but each data set has + # length 0, so the computed test statistic should be [0, 0, 0]. + PowerDivCase(f_obs=np.array([[],[],[]]).T, + f_exp=None, ddof=0, axis=0, + chi2=[0, 0, 0], + log=[0, 0, 0], + mod_log=[0, 0, 0], + cr=[0, 0, 0]), + # Shape is (3, 0). This represents an empty collection of + # data sets in which each data set has length 3. The test + # statistic should be an empty array. + PowerDivCase(f_obs=np.array([[],[],[]]), + f_exp=None, ddof=0, axis=0, + chi2=[], + log=[], + mod_log=[], + cr=[]), +] + + +class TestPowerDivergence: + + def check_power_divergence(self, f_obs, f_exp, ddof, axis, lambda_, + expected_stat): + f_obs = np.asarray(f_obs) + if axis is None: + num_obs = f_obs.size + else: + b = np.broadcast(f_obs, f_exp) + num_obs = b.shape[axis] + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Mean of empty slice") + stat, p = stats.power_divergence( + f_obs=f_obs, f_exp=f_exp, ddof=ddof, + axis=axis, lambda_=lambda_) + assert_allclose(stat, expected_stat) + + if lambda_ == 1 or lambda_ == "pearson": + # Also test stats.chisquare. + stat, p = stats.chisquare(f_obs=f_obs, f_exp=f_exp, ddof=ddof, + axis=axis) + assert_allclose(stat, expected_stat) + + ddof = np.asarray(ddof) + expected_p = stats.distributions.chi2.sf(expected_stat, + num_obs - 1 - ddof) + assert_allclose(p, expected_p) + + def test_basic(self): + for case in power_div_1d_cases: + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + None, case.chi2) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "pearson", case.chi2) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + 1, case.chi2) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "log-likelihood", case.log) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "mod-log-likelihood", case.mod_log) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "cressie-read", case.cr) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + 2/3, case.cr) + + def test_basic_masked(self): + for case in power_div_1d_cases: + mobs = np.ma.array(case.f_obs) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + None, case.chi2) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + "pearson", case.chi2) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + 1, case.chi2) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + "log-likelihood", case.log) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + "mod-log-likelihood", case.mod_log) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + "cressie-read", case.cr) + self.check_power_divergence( + mobs, case.f_exp, case.ddof, case.axis, + 2/3, case.cr) + + def test_axis(self): + case0 = power_div_1d_cases[0] + case1 = power_div_1d_cases[1] + f_obs = np.vstack((case0.f_obs, case1.f_obs)) + f_exp = np.vstack((np.ones_like(case0.f_obs)*np.mean(case0.f_obs), + case1.f_exp)) + # Check the four computational code paths in power_divergence + # using a 2D array with axis=1. + self.check_power_divergence( + f_obs, f_exp, 0, 1, + "pearson", [case0.chi2, case1.chi2]) + self.check_power_divergence( + f_obs, f_exp, 0, 1, + "log-likelihood", [case0.log, case1.log]) + self.check_power_divergence( + f_obs, f_exp, 0, 1, + "mod-log-likelihood", [case0.mod_log, case1.mod_log]) + self.check_power_divergence( + f_obs, f_exp, 0, 1, + "cressie-read", [case0.cr, case1.cr]) + # Reshape case0.f_obs to shape (2,2), and use axis=None. + # The result should be the same. + self.check_power_divergence( + np.array(case0.f_obs).reshape(2, 2), None, 0, None, + "pearson", case0.chi2) + + def test_ddof_broadcasting(self): + # Test that ddof broadcasts correctly. + # ddof does not affect the test statistic. It is broadcast + # with the computed test statistic for the computation of + # the p value. + + case0 = power_div_1d_cases[0] + case1 = power_div_1d_cases[1] + # Create 4x2 arrays of observed and expected frequencies. + f_obs = np.vstack((case0.f_obs, case1.f_obs)).T + f_exp = np.vstack((np.ones_like(case0.f_obs)*np.mean(case0.f_obs), + case1.f_exp)).T + + expected_chi2 = [case0.chi2, case1.chi2] + + # ddof has shape (2, 1). This is broadcast with the computed + # statistic, so p will have shape (2,2). + ddof = np.array([[0], [1]]) + + stat, p = stats.power_divergence(f_obs, f_exp, ddof=ddof) + assert_allclose(stat, expected_chi2) + + # Compute the p values separately, passing in scalars for ddof. + stat0, p0 = stats.power_divergence(f_obs, f_exp, ddof=ddof[0,0]) + stat1, p1 = stats.power_divergence(f_obs, f_exp, ddof=ddof[1,0]) + + assert_array_equal(p, np.vstack((p0, p1))) + + def test_empty_cases(self): + with warnings.catch_warnings(): + for case in power_div_empty_cases: + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "pearson", case.chi2) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "log-likelihood", case.log) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "mod-log-likelihood", case.mod_log) + self.check_power_divergence( + case.f_obs, case.f_exp, case.ddof, case.axis, + "cressie-read", case.cr) + + def test_power_divergence_result_attributes(self): + f_obs = power_div_1d_cases[0].f_obs + f_exp = power_div_1d_cases[0].f_exp + ddof = power_div_1d_cases[0].ddof + axis = power_div_1d_cases[0].axis + + res = stats.power_divergence(f_obs=f_obs, f_exp=f_exp, ddof=ddof, + axis=axis, lambda_="pearson") + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_power_divergence_gh_12282(self): + # The sums of observed and expected frequencies must match + f_obs = np.array([[10, 20], [30, 20]]) + f_exp = np.array([[5, 15], [35, 25]]) + with assert_raises(ValueError, match='For each axis slice...'): + stats.power_divergence(f_obs=[10, 20], f_exp=[30, 60]) + with assert_raises(ValueError, match='For each axis slice...'): + stats.power_divergence(f_obs=f_obs, f_exp=f_exp, axis=1) + stat, pval = stats.power_divergence(f_obs=f_obs, f_exp=f_exp) + assert_allclose(stat, [5.71428571, 2.66666667]) + assert_allclose(pval, [0.01682741, 0.10247043]) + + +def test_gh_chisquare_12282(): + # Currently `chisquare` is implemented via power_divergence + # in case that ever changes, perform a basic test like + # test_power_divergence_gh_12282 + with assert_raises(ValueError, match='For each axis slice...'): + stats.chisquare(f_obs=[10, 20], f_exp=[30, 60]) + + +@pytest.mark.parametrize("n, dtype", [(200, np.uint8), (1000000, np.int32)]) +def test_chiquare_data_types_attributes(n, dtype): + # Regression test for gh-10159 and gh-18368 + obs = np.array([n, 0], dtype=dtype) + exp = np.array([n // 2, n // 2], dtype=dtype) + res = stats.chisquare(obs, exp) + stat, p = res + assert_allclose(stat, n, rtol=1e-13) + # check that attributes are identical to unpacked outputs - see gh-18368 + assert_equal(res.statistic, stat) + assert_equal(res.pvalue, p) + + +def test_chisquare_masked_arrays(): + # Test masked arrays. + obs = np.array([[8, 8, 16, 32, -1], [-1, -1, 3, 4, 5]]).T + mask = np.array([[0, 0, 0, 0, 1], [1, 1, 0, 0, 0]]).T + mobs = np.ma.masked_array(obs, mask) + expected_chisq = np.array([24.0, 0.5]) + expected_g = np.array([2*(2*8*np.log(0.5) + 32*np.log(2.0)), + 2*(3*np.log(0.75) + 5*np.log(1.25))]) + + chi2 = stats.distributions.chi2 + + chisq, p = stats.chisquare(mobs) + mat.assert_array_equal(chisq, expected_chisq) + mat.assert_array_almost_equal(p, chi2.sf(expected_chisq, + mobs.count(axis=0) - 1)) + + g, p = stats.power_divergence(mobs, lambda_='log-likelihood') + mat.assert_array_almost_equal(g, expected_g, decimal=15) + mat.assert_array_almost_equal(p, chi2.sf(expected_g, + mobs.count(axis=0) - 1)) + + chisq, p = stats.chisquare(mobs.T, axis=1) + mat.assert_array_equal(chisq, expected_chisq) + mat.assert_array_almost_equal(p, chi2.sf(expected_chisq, + mobs.T.count(axis=1) - 1)) + g, p = stats.power_divergence(mobs.T, axis=1, lambda_="log-likelihood") + mat.assert_array_almost_equal(g, expected_g, decimal=15) + mat.assert_array_almost_equal(p, chi2.sf(expected_g, + mobs.count(axis=0) - 1)) + + obs1 = np.ma.array([3, 5, 6, 99, 10], mask=[0, 0, 0, 1, 0]) + exp1 = np.ma.array([2, 4, 8, 10, 99], mask=[0, 0, 0, 0, 1]) + chi2, p = stats.chisquare(obs1, f_exp=exp1) + # Because of the mask at index 3 of obs1 and at index 4 of exp1, + # only the first three elements are included in the calculation + # of the statistic. + mat.assert_array_equal(chi2, 1/2 + 1/4 + 4/8) + + # When axis=None, the two values should have type np.float64. + chisq, p = stats.chisquare(np.ma.array([1,2,3]), axis=None) + assert_(isinstance(chisq, np.float64)) + assert_(isinstance(p, np.float64)) + assert_equal(chisq, 1.0) + assert_almost_equal(p, stats.distributions.chi2.sf(1.0, 2)) + + # Empty arrays: + # A data set with length 0 returns a masked scalar. + with np.errstate(invalid='ignore'): + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Mean of empty slice") + chisq, p = stats.chisquare(np.ma.array([])) + assert_(isinstance(chisq, np.ma.MaskedArray)) + assert_equal(chisq.shape, ()) + assert_(chisq.mask) + + empty3 = np.ma.array([[],[],[]]) + + # empty3 is a collection of 0 data sets (whose lengths would be 3, if + # there were any), so the return value is an array with length 0. + chisq, p = stats.chisquare(empty3) + assert_(isinstance(chisq, np.ma.MaskedArray)) + mat.assert_array_equal(chisq, []) + + # empty3.T is an array containing 3 data sets, each with length 0, + # so an array of size (3,) is returned, with all values masked. + with np.errstate(invalid='ignore'): + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Mean of empty slice") + chisq, p = stats.chisquare(empty3.T) + + assert_(isinstance(chisq, np.ma.MaskedArray)) + assert_equal(chisq.shape, (3,)) + assert_(np.all(chisq.mask)) + + +def test_power_divergence_against_cressie_read_data(): + # Test stats.power_divergence against tables 4 and 5 from + # Cressie and Read, "Multimonial Goodness-of-Fit Tests", + # J. R. Statist. Soc. B (1984), Vol 46, No. 3, pp. 440-464. + # This tests the calculation for several values of lambda. + + # Table 4 data recalculated for greater precision according to: + # Shelby J. Haberman, Analysis of Qualitative Data: Volume 1 + # Introductory Topics, Academic Press, New York, USA (1978). + obs = np.array([15, 11, 14, 17, 5, 11, 10, 4, 8, + 10, 7, 9, 11, 3, 6, 1, 1, 4]) + beta = -0.083769 # Haberman (1978), p. 15 + i = np.arange(1, len(obs) + 1) + alpha = np.log(obs.sum() / np.exp(beta*i).sum()) + expected_counts = np.exp(alpha + beta*i) + + # `table4` holds just the second and third columns from Table 4. + table4 = np.vstack((obs, expected_counts)).T + + table5 = np.array([ + # lambda, statistic + -10.0, 72.2e3, + -5.0, 28.9e1, + -3.0, 65.6, + -2.0, 40.6, + -1.5, 34.0, + -1.0, 29.5, + -0.5, 26.5, + 0.0, 24.6, + 0.5, 23.4, + 0.67, 23.1, + 1.0, 22.7, + 1.5, 22.6, + 2.0, 22.9, + 3.0, 24.8, + 5.0, 35.5, + 10.0, 21.4e1, + ]).reshape(-1, 2) + + for lambda_, expected_stat in table5: + stat, p = stats.power_divergence(table4[:,0], table4[:,1], + lambda_=lambda_) + assert_allclose(stat, expected_stat, rtol=5e-3) + + +def test_friedmanchisquare(): + # see ticket:113 + # verified with matlab and R + # From Demsar "Statistical Comparisons of Classifiers over Multiple Data Sets" + # 2006, Xf=9.28 (no tie handling, tie corrected Xf >=9.28) + x1 = [array([0.763, 0.599, 0.954, 0.628, 0.882, 0.936, 0.661, 0.583, + 0.775, 1.0, 0.94, 0.619, 0.972, 0.957]), + array([0.768, 0.591, 0.971, 0.661, 0.888, 0.931, 0.668, 0.583, + 0.838, 1.0, 0.962, 0.666, 0.981, 0.978]), + array([0.771, 0.590, 0.968, 0.654, 0.886, 0.916, 0.609, 0.563, + 0.866, 1.0, 0.965, 0.614, 0.9751, 0.946]), + array([0.798, 0.569, 0.967, 0.657, 0.898, 0.931, 0.685, 0.625, + 0.875, 1.0, 0.962, 0.669, 0.975, 0.970])] + + # From "Bioestadistica para las ciencias de la salud" Xf=18.95 p<0.001: + x2 = [array([4,3,5,3,5,3,2,5,4,4,4,3]), + array([2,2,1,2,3,1,2,3,2,1,1,3]), + array([2,4,3,3,4,3,3,4,4,1,2,1]), + array([3,5,4,3,4,4,3,3,3,4,4,4])] + + # From Jerrorl H. Zar, "Biostatistical Analysis"(example 12.6), + # Xf=10.68, 0.005 < p < 0.01: + # Probability from this example is inexact + # using Chisquare approximation of Friedman Chisquare. + x3 = [array([7.0,9.9,8.5,5.1,10.3]), + array([5.3,5.7,4.7,3.5,7.7]), + array([4.9,7.6,5.5,2.8,8.4]), + array([8.8,8.9,8.1,3.3,9.1])] + + assert_array_almost_equal(stats.friedmanchisquare(x1[0],x1[1],x1[2],x1[3]), + (10.2283464566929, 0.0167215803284414)) + assert_array_almost_equal(stats.friedmanchisquare(x2[0],x2[1],x2[2],x2[3]), + (18.9428571428571, 0.000280938375189499)) + assert_array_almost_equal(stats.friedmanchisquare(x3[0],x3[1],x3[2],x3[3]), + (10.68, 0.0135882729582176)) + assert_raises(ValueError, stats.friedmanchisquare,x3[0],x3[1]) + + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.friedmanchisquare(*x1) + check_named_results(res, attributes) + + # test using mstats + assert_array_almost_equal(mstats.friedmanchisquare(x1[0], x1[1], + x1[2], x1[3]), + (10.2283464566929, 0.0167215803284414)) + # the following fails + # assert_array_almost_equal(mstats.friedmanchisquare(x2[0],x2[1],x2[2],x2[3]), + # (18.9428571428571, 0.000280938375189499)) + assert_array_almost_equal(mstats.friedmanchisquare(x3[0], x3[1], + x3[2], x3[3]), + (10.68, 0.0135882729582176)) + assert_raises(ValueError, mstats.friedmanchisquare,x3[0],x3[1]) + + +class TestKSTest: + """Tests kstest and ks_1samp agree with K-S various sizes, alternatives, modes.""" + + def _testOne(self, x, alternative, expected_statistic, expected_prob, + mode='auto', decimal=14): + result = stats.kstest(x, 'norm', alternative=alternative, mode=mode) + expected = np.array([expected_statistic, expected_prob]) + assert_array_almost_equal(np.array(result), expected, decimal=decimal) + + def _test_kstest_and_ks1samp(self, x, alternative, mode='auto', decimal=14): + result = stats.kstest(x, 'norm', alternative=alternative, mode=mode) + result_1samp = stats.ks_1samp(x, stats.norm.cdf, + alternative=alternative, mode=mode) + assert_array_almost_equal(np.array(result), result_1samp, decimal=decimal) + + def test_namedtuple_attributes(self): + x = np.linspace(-1, 1, 9) + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.kstest(x, 'norm') + check_named_results(res, attributes) + + def test_agree_with_ks_1samp(self): + x = np.linspace(-1, 1, 9) + self._test_kstest_and_ks1samp(x, 'two-sided') + + x = np.linspace(-15, 15, 9) + self._test_kstest_and_ks1samp(x, 'two-sided') + + x = [-1.23, 0.06, -0.60, 0.17, 0.66, -0.17, -0.08, 0.27, -0.98, -0.99] + self._test_kstest_and_ks1samp(x, 'two-sided') + self._test_kstest_and_ks1samp(x, 'greater', mode='exact') + self._test_kstest_and_ks1samp(x, 'less', mode='exact') + + # missing: no test that uses *args + + +class TestKSOneSample: + """ + Tests kstest and ks_samp 1-samples with K-S various sizes, alternatives, modes. + """ + + def _testOne(self, x, alternative, expected_statistic, expected_prob, + mode='auto', decimal=14): + result = stats.ks_1samp(x, stats.norm.cdf, alternative=alternative, mode=mode) + expected = np.array([expected_statistic, expected_prob]) + assert_array_almost_equal(np.array(result), expected, decimal=decimal) + + def test_namedtuple_attributes(self): + x = np.linspace(-1, 1, 9) + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.ks_1samp(x, stats.norm.cdf) + check_named_results(res, attributes) + + def test_agree_with_r(self): + # comparing with some values from R + x = np.linspace(-1, 1, 9) + self._testOne(x, 'two-sided', 0.15865525393145705, 0.95164069201518386) + + x = np.linspace(-15, 15, 9) + self._testOne(x, 'two-sided', 0.44435602715924361, 0.038850140086788665) + + x = [-1.23, 0.06, -0.60, 0.17, 0.66, -0.17, -0.08, 0.27, -0.98, -0.99] + self._testOne(x, 'two-sided', 0.293580126801961, 0.293408463684361) + self._testOne(x, 'greater', 0.293580126801961, 0.146988835042376, mode='exact') + self._testOne(x, 'less', 0.109348552425692, 0.732768892470675, mode='exact') + + def test_known_examples(self): + # the following tests rely on deterministically replicated rvs + x = stats.norm.rvs(loc=0.2, size=100, random_state=987654321) + self._testOne(x, 'two-sided', 0.12464329735846891, 0.089444888711820769, + mode='asymp') + self._testOne(x, 'less', 0.12464329735846891, 0.040989164077641749) + self._testOne(x, 'greater', 0.0072115233216310994, 0.98531158590396228) + + def test_ks1samp_allpaths(self): + # Check NaN input, output. + assert_(np.isnan(kolmogn(np.nan, 1, True))) + with assert_raises(ValueError, match='n is not integral: 1.5'): + kolmogn(1.5, 1, True) + assert_(np.isnan(kolmogn(-1, 1, True))) + + dataset = np.asarray([ + # Check x out of range + (101, 1, True, 1.0), + (101, 1.1, True, 1.0), + (101, 0, True, 0.0), + (101, -0.1, True, 0.0), + + (32, 1.0 / 64, True, 0.0), # Ruben-Gambino + (32, 1.0 / 64, False, 1.0), # Ruben-Gambino + + # Miller + (32, 0.5, True, 0.9999999363163307), + # Miller 2 * special.smirnov(32, 0.5) + (32, 0.5, False, 6.368366937916623e-08), + + # Check some other paths + (32, 1.0 / 8, True, 0.34624229979775223), + (32, 1.0 / 4, True, 0.9699508336558085), + (1600, 0.49, False, 0.0), + # 2 * special.smirnov(1600, 1/16.0) + (1600, 1 / 16.0, False, 7.0837876229702195e-06), + # _kolmogn_DMTW + (1600, 14 / 1600, False, 0.99962357317602), + # _kolmogn_PelzGood + (1600, 1 / 32, False, 0.08603386296651416), + ]) + FuncData(kolmogn, dataset, (0, 1, 2), 3).check(dtypes=[int, float, bool]) + + @pytest.mark.parametrize("ksfunc", [stats.kstest, stats.ks_1samp]) + @pytest.mark.parametrize("alternative, x6val, ref_location, ref_sign", + [('greater', 6, 6, +1), + ('less', 7, 7, -1), + ('two-sided', 6, 6, +1), + ('two-sided', 7, 7, -1)]) + def test_location_sign(self, ksfunc, alternative, + x6val, ref_location, ref_sign): + # Test that location and sign corresponding with statistic are as + # expected. (Test is designed to be easy to predict.) + x = np.arange(10) + 0.5 + x[6] = x6val + cdf = stats.uniform(scale=10).cdf + res = ksfunc(x, cdf, alternative=alternative) + assert_allclose(res.statistic, 0.1, rtol=1e-15) + assert res.statistic_location == ref_location + assert res.statistic_sign == ref_sign + + # missing: no test that uses *args + + +class TestKSTwoSamples: + """Tests 2-samples with K-S various sizes, alternatives, modes.""" + + def _testOne(self, x1, x2, alternative, expected_statistic, expected_prob, + mode='auto'): + result = stats.ks_2samp(x1, x2, alternative, mode=mode) + expected = np.array([expected_statistic, expected_prob]) + assert_array_almost_equal(np.array(result), expected) + + def testSmall(self): + self._testOne([0], [1], 'two-sided', 1.0/1, 1.0) + self._testOne([0], [1], 'greater', 1.0/1, 0.5) + self._testOne([0], [1], 'less', 0.0/1, 1.0) + self._testOne([1], [0], 'two-sided', 1.0/1, 1.0) + self._testOne([1], [0], 'greater', 0.0/1, 1.0) + self._testOne([1], [0], 'less', 1.0/1, 0.5) + + def testTwoVsThree(self): + data1 = np.array([1.0, 2.0]) + data1p = data1 + 0.01 + data1m = data1 - 0.01 + data2 = np.array([1.0, 2.0, 3.0]) + self._testOne(data1p, data2, 'two-sided', 1.0 / 3, 1.0) + self._testOne(data1p, data2, 'greater', 1.0 / 3, 0.7) + self._testOne(data1p, data2, 'less', 1.0 / 3, 0.7) + self._testOne(data1m, data2, 'two-sided', 2.0 / 3, 0.6) + self._testOne(data1m, data2, 'greater', 2.0 / 3, 0.3) + self._testOne(data1m, data2, 'less', 0, 1.0) + + def testTwoVsFour(self): + data1 = np.array([1.0, 2.0]) + data1p = data1 + 0.01 + data1m = data1 - 0.01 + data2 = np.array([1.0, 2.0, 3.0, 4.0]) + self._testOne(data1p, data2, 'two-sided', 2.0 / 4, 14.0/15) + self._testOne(data1p, data2, 'greater', 2.0 / 4, 8.0/15) + self._testOne(data1p, data2, 'less', 1.0 / 4, 12.0/15) + + self._testOne(data1m, data2, 'two-sided', 3.0 / 4, 6.0/15) + self._testOne(data1m, data2, 'greater', 3.0 / 4, 3.0/15) + self._testOne(data1m, data2, 'less', 0, 1.0) + + def test100_100(self): + x100 = np.linspace(1, 100, 100) + x100_2_p1 = x100 + 2 + 0.1 + x100_2_m1 = x100 + 2 - 0.1 + self._testOne(x100, x100_2_p1, 'two-sided', 3.0 / 100, 0.9999999999962055) + self._testOne(x100, x100_2_p1, 'greater', 3.0 / 100, 0.9143290114276248) + self._testOne(x100, x100_2_p1, 'less', 0, 1.0) + self._testOne(x100, x100_2_m1, 'two-sided', 2.0 / 100, 1.0) + self._testOne(x100, x100_2_m1, 'greater', 2.0 / 100, 0.960978450786184) + self._testOne(x100, x100_2_m1, 'less', 0, 1.0) + + def test100_110(self): + x100 = np.linspace(1, 100, 100) + x110 = np.linspace(1, 100, 110) + x110_20_p1 = x110 + 20 + 0.1 + x110_20_m1 = x110 + 20 - 0.1 + # 100, 110 + self._testOne(x100, x110_20_p1, 'two-sided', 232.0 / 1100, 0.015739183865607353) + self._testOne(x100, x110_20_p1, 'greater', 232.0 / 1100, 0.007869594319053203) + self._testOne(x100, x110_20_p1, 'less', 0, 1) + self._testOne(x100, x110_20_m1, 'two-sided', 229.0 / 1100, 0.017803803861026313) + self._testOne(x100, x110_20_m1, 'greater', 229.0 / 1100, 0.008901905958245056) + self._testOne(x100, x110_20_m1, 'less', 0.0, 1.0) + + def testRepeatedValues(self): + x2233 = np.array([2] * 3 + [3] * 4 + [5] * 5 + [6] * 4, dtype=int) + x3344 = x2233 + 1 + x2356 = np.array([2] * 3 + [3] * 4 + [5] * 10 + [6] * 4, dtype=int) + x3467 = np.array([3] * 10 + [4] * 2 + [6] * 10 + [7] * 4, dtype=int) + self._testOne(x2233, x3344, 'two-sided', 5.0/16, 0.4262934613454952) + self._testOne(x2233, x3344, 'greater', 5.0/16, 0.21465428276573786) + self._testOne(x2233, x3344, 'less', 0.0/16, 1.0) + self._testOne(x2356, x3467, 'two-sided', 190.0/21/26, 0.0919245790168125) + self._testOne(x2356, x3467, 'greater', 190.0/21/26, 0.0459633806858544) + self._testOne(x2356, x3467, 'less', 70.0/21/26, 0.6121593130022775) + + def testEqualSizes(self): + data2 = np.array([1.0, 2.0, 3.0]) + self._testOne(data2, data2+1, 'two-sided', 1.0/3, 1.0) + self._testOne(data2, data2+1, 'greater', 1.0/3, 0.75) + self._testOne(data2, data2+1, 'less', 0.0/3, 1.) + self._testOne(data2, data2+0.5, 'two-sided', 1.0/3, 1.0) + self._testOne(data2, data2+0.5, 'greater', 1.0/3, 0.75) + self._testOne(data2, data2+0.5, 'less', 0.0/3, 1.) + self._testOne(data2, data2-0.5, 'two-sided', 1.0/3, 1.0) + self._testOne(data2, data2-0.5, 'greater', 0.0/3, 1.0) + self._testOne(data2, data2-0.5, 'less', 1.0/3, 0.75) + + @pytest.mark.slow + def testMiddlingBoth(self): + # 500, 600 + n1, n2 = 500, 600 + delta = 1.0/n1/n2/2/2 + x = np.linspace(1, 200, n1) - delta + y = np.linspace(2, 200, n2) + self._testOne(x, y, 'two-sided', 2000.0 / n1 / n2, 1.0, + mode='auto') + self._testOne(x, y, 'two-sided', 2000.0 / n1 / n2, 1.0, + mode='asymp') + self._testOne(x, y, 'greater', 2000.0 / n1 / n2, 0.9697596024683929, + mode='asymp') + self._testOne(x, y, 'less', 500.0 / n1 / n2, 0.9968735843165021, + mode='asymp') + with suppress_warnings() as sup: + message = "ks_2samp: Exact calculation unsuccessful." + sup.filter(RuntimeWarning, message) + self._testOne(x, y, 'greater', 2000.0 / n1 / n2, 0.9697596024683929, + mode='exact') + self._testOne(x, y, 'less', 500.0 / n1 / n2, 0.9968735843165021, + mode='exact') + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + self._testOne(x, y, 'less', 500.0 / n1 / n2, 0.9968735843165021, + mode='exact') + _check_warnings(w, RuntimeWarning, 1) + + @pytest.mark.slow + def testMediumBoth(self): + # 1000, 1100 + n1, n2 = 1000, 1100 + delta = 1.0/n1/n2/2/2 + x = np.linspace(1, 200, n1) - delta + y = np.linspace(2, 200, n2) + self._testOne(x, y, 'two-sided', 6600.0 / n1 / n2, 1.0, + mode='asymp') + self._testOne(x, y, 'two-sided', 6600.0 / n1 / n2, 1.0, + mode='auto') + self._testOne(x, y, 'greater', 6600.0 / n1 / n2, 0.9573185808092622, + mode='asymp') + self._testOne(x, y, 'less', 1000.0 / n1 / n2, 0.9982410869433984, + mode='asymp') + + with suppress_warnings() as sup: + message = "ks_2samp: Exact calculation unsuccessful." + sup.filter(RuntimeWarning, message) + self._testOne(x, y, 'greater', 6600.0 / n1 / n2, 0.9573185808092622, + mode='exact') + self._testOne(x, y, 'less', 1000.0 / n1 / n2, 0.9982410869433984, + mode='exact') + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + self._testOne(x, y, 'less', 1000.0 / n1 / n2, 0.9982410869433984, + mode='exact') + _check_warnings(w, RuntimeWarning, 1) + + def testLarge(self): + # 10000, 110 + n1, n2 = 10000, 110 + lcm = n1*11.0 + delta = 1.0/n1/n2/2/2 + x = np.linspace(1, 200, n1) - delta + y = np.linspace(2, 100, n2) + self._testOne(x, y, 'two-sided', 55275.0 / lcm, 4.2188474935755949e-15) + self._testOne(x, y, 'greater', 561.0 / lcm, 0.99115454582047591) + self._testOne(x, y, 'less', 55275.0 / lcm, 3.1317328311518713e-26) + + def test_gh11184(self): + # 3000, 3001, exact two-sided + np.random.seed(123456) + x = np.random.normal(size=3000) + y = np.random.normal(size=3001) * 1.5 + self._testOne(x, y, 'two-sided', 0.11292880151060758, 2.7755575615628914e-15, + mode='asymp') + self._testOne(x, y, 'two-sided', 0.11292880151060758, 2.7755575615628914e-15, + mode='exact') + + @pytest.mark.xslow + def test_gh11184_bigger(self): + # 10000, 10001, exact two-sided + np.random.seed(123456) + x = np.random.normal(size=10000) + y = np.random.normal(size=10001) * 1.5 + self._testOne(x, y, 'two-sided', 0.10597913208679133, 3.3149311398483503e-49, + mode='asymp') + self._testOne(x, y, 'two-sided', 0.10597913208679133, 2.7755575615628914e-15, + mode='exact') + self._testOne(x, y, 'greater', 0.10597913208679133, 2.7947433906389253e-41, + mode='asymp') + self._testOne(x, y, 'less', 0.09658002199780022, 2.7947433906389253e-41, + mode='asymp') + + @pytest.mark.xslow + def test_gh12999(self): + np.random.seed(123456) + for x in range(1000, 12000, 1000): + vals1 = np.random.normal(size=(x)) + vals2 = np.random.normal(size=(x + 10), loc=0.5) + exact = stats.ks_2samp(vals1, vals2, mode='exact').pvalue + asymp = stats.ks_2samp(vals1, vals2, mode='asymp').pvalue + # these two p-values should be in line with each other + assert_array_less(exact, 3 * asymp) + assert_array_less(asymp, 3 * exact) + + @pytest.mark.slow + def testLargeBoth(self): + # 10000, 11000 + n1, n2 = 10000, 11000 + lcm = n1*11.0 + delta = 1.0/n1/n2/2/2 + x = np.linspace(1, 200, n1) - delta + y = np.linspace(2, 200, n2) + self._testOne(x, y, 'two-sided', 563.0 / lcm, 0.9990660108966576, + mode='asymp') + self._testOne(x, y, 'two-sided', 563.0 / lcm, 0.9990456491488628, + mode='exact') + self._testOne(x, y, 'two-sided', 563.0 / lcm, 0.9990660108966576, + mode='auto') + self._testOne(x, y, 'greater', 563.0 / lcm, 0.7561851877420673) + self._testOne(x, y, 'less', 10.0 / lcm, 0.9998239693191724) + with suppress_warnings() as sup: + message = "ks_2samp: Exact calculation unsuccessful." + sup.filter(RuntimeWarning, message) + self._testOne(x, y, 'greater', 563.0 / lcm, 0.7561851877420673, + mode='exact') + self._testOne(x, y, 'less', 10.0 / lcm, 0.9998239693191724, + mode='exact') + + def testNamedAttributes(self): + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.ks_2samp([1, 2], [3]) + check_named_results(res, attributes) + + @pytest.mark.slow + def test_some_code_paths(self): + # Check that some code paths are executed + from scipy.stats._stats_py import ( + _count_paths_outside_method, + _compute_outer_prob_inside_method + ) + + _compute_outer_prob_inside_method(1, 1, 1, 1) + _count_paths_outside_method(1000, 1, 1, 1001) + + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, _count_paths_outside_method, + 1100, 1099, 1, 1) + assert_raises(FloatingPointError, _count_paths_outside_method, + 2000, 1000, 1, 1) + + def test_argument_checking(self): + # Check that an empty array causes a ValueError + assert_raises(ValueError, stats.ks_2samp, [], [1]) + assert_raises(ValueError, stats.ks_2samp, [1], []) + assert_raises(ValueError, stats.ks_2samp, [], []) + + @pytest.mark.slow + def test_gh12218(self): + """Ensure gh-12218 is fixed.""" + # gh-1228 triggered a TypeError calculating sqrt(n1*n2*(n1+n2)). + # n1, n2 both large integers, the product exceeded 2^64 + np.random.seed(12345678) + n1 = 2097152 # 2*^21 + rvs1 = stats.uniform.rvs(size=n1, loc=0., scale=1) + rvs2 = rvs1 + 1 # Exact value of rvs2 doesn't matter. + stats.ks_2samp(rvs1, rvs2, alternative='greater', mode='asymp') + stats.ks_2samp(rvs1, rvs2, alternative='less', mode='asymp') + stats.ks_2samp(rvs1, rvs2, alternative='two-sided', mode='asymp') + + def test_warnings_gh_14019(self): + # Check that RuntimeWarning is raised when method='auto' and exact + # p-value calculation fails. See gh-14019. + rng = np.random.RandomState(seed=23493549) + # random samples of the same size as in the issue + data1 = rng.random(size=881) + 0.5 + data2 = rng.random(size=369) + message = "ks_2samp: Exact calculation unsuccessful" + with pytest.warns(RuntimeWarning, match=message): + res = stats.ks_2samp(data1, data2, alternative='less') + assert_allclose(res.pvalue, 0, atol=1e-14) + + @pytest.mark.parametrize("ksfunc", [stats.kstest, stats.ks_2samp]) + @pytest.mark.parametrize("alternative, x6val, ref_location, ref_sign", + [('greater', 5.9, 5.9, +1), + ('less', 6.1, 6.0, -1), + ('two-sided', 5.9, 5.9, +1), + ('two-sided', 6.1, 6.0, -1)]) + def test_location_sign(self, ksfunc, alternative, + x6val, ref_location, ref_sign): + # Test that location and sign corresponding with statistic are as + # expected. (Test is designed to be easy to predict.) + x = np.arange(10, dtype=np.float64) + y = x.copy() + x[6] = x6val + res = stats.ks_2samp(x, y, alternative=alternative) + assert res.statistic == 0.1 + assert res.statistic_location == ref_location + assert res.statistic_sign == ref_sign + + +def test_ttest_rel(): + # regression test + tr,pr = 0.81248591389165692, 0.41846234511362157 + tpr = ([tr,-tr],[pr,pr]) + + rvs1 = np.linspace(1,100,100) + rvs2 = np.linspace(1.01,99.989,100) + rvs1_2D = np.array([np.linspace(1,100,100), np.linspace(1.01,99.989,100)]) + rvs2_2D = np.array([np.linspace(1.01,99.989,100), np.linspace(1,100,100)]) + + t,p = stats.ttest_rel(rvs1, rvs2, axis=0) + assert_array_almost_equal([t,p],(tr,pr)) + t,p = stats.ttest_rel(rvs1_2D.T, rvs2_2D.T, axis=0) + assert_array_almost_equal([t,p],tpr) + t,p = stats.ttest_rel(rvs1_2D, rvs2_2D, axis=1) + assert_array_almost_equal([t,p],tpr) + + # test scalars + with suppress_warnings() as sup, \ + np.errstate(invalid="ignore", divide="ignore"): + sup.filter(RuntimeWarning, "Degrees of freedom <= 0 for slice") + t, p = stats.ttest_rel(4., 3.) + assert_(np.isnan(t)) + assert_(np.isnan(p)) + + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.ttest_rel(rvs1, rvs2, axis=0) + check_named_results(res, attributes) + + # test on 3 dimensions + rvs1_3D = np.dstack([rvs1_2D,rvs1_2D,rvs1_2D]) + rvs2_3D = np.dstack([rvs2_2D,rvs2_2D,rvs2_2D]) + t,p = stats.ttest_rel(rvs1_3D, rvs2_3D, axis=1) + assert_array_almost_equal(np.abs(t), tr) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (2, 3)) + + t, p = stats.ttest_rel(np.moveaxis(rvs1_3D, 2, 0), + np.moveaxis(rvs2_3D, 2, 0), + axis=2) + assert_array_almost_equal(np.abs(t), tr) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (3, 2)) + + # test alternative parameter + assert_raises(ValueError, stats.ttest_rel, rvs1, rvs2, alternative="error") + + t, p = stats.ttest_rel(rvs1, rvs2, axis=0, alternative="less") + assert_allclose(p, 1 - pr/2) + assert_allclose(t, tr) + + t, p = stats.ttest_rel(rvs1, rvs2, axis=0, alternative="greater") + assert_allclose(p, pr/2) + assert_allclose(t, tr) + + # check nan policy + rng = np.random.RandomState(12345678) + x = stats.norm.rvs(loc=5, scale=10, size=501, random_state=rng) + x[500] = np.nan + y = (stats.norm.rvs(loc=5, scale=10, size=501, random_state=rng) + + stats.norm.rvs(scale=0.2, size=501, random_state=rng)) + y[500] = np.nan + + with np.errstate(invalid="ignore"): + assert_array_equal(stats.ttest_rel(x, x), (np.nan, np.nan)) + + assert_array_almost_equal(stats.ttest_rel(x, y, nan_policy='omit'), + (0.25299925303978066, 0.8003729814201519)) + assert_raises(ValueError, stats.ttest_rel, x, y, nan_policy='raise') + assert_raises(ValueError, stats.ttest_rel, x, y, nan_policy='foobar') + + # test zero division problem + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + t, p = stats.ttest_rel([0, 0, 0], [1, 1, 1]) + assert_equal((np.abs(t), p), (np.inf, 0)) + with np.errstate(invalid="ignore"): + assert_equal(stats.ttest_rel([0, 0, 0], [0, 0, 0]), (np.nan, np.nan)) + + # check that nan in input array result in nan output + anan = np.array([[1, np.nan], [-1, 1]]) + assert_equal(stats.ttest_rel(anan, np.zeros((2, 2))), + ([0, np.nan], [1, np.nan])) + + # test incorrect input shape raise an error + x = np.arange(24) + assert_raises(ValueError, stats.ttest_rel, x.reshape((8, 3)), + x.reshape((2, 3, 4))) + + # Convert from two-sided p-values to one sided using T result data. + def convert(t, p, alt): + if (t < 0 and alt == "less") or (t > 0 and alt == "greater"): + return p / 2 + return 1 - (p / 2) + converter = np.vectorize(convert) + + rvs1_2D[:, 20:30] = np.nan + rvs2_2D[:, 15:25] = np.nan + + tr, pr = stats.ttest_rel(rvs1_2D, rvs2_2D, 0, nan_policy='omit') + + t, p = stats.ttest_rel(rvs1_2D, rvs2_2D, 0, nan_policy='omit', + alternative='less') + assert_allclose(t, tr, rtol=1e-14) + with np.errstate(invalid='ignore'): + assert_allclose(p, converter(tr, pr, 'less'), rtol=1e-14) + + t, p = stats.ttest_rel(rvs1_2D, rvs2_2D, 0, nan_policy='omit', + alternative='greater') + assert_allclose(t, tr, rtol=1e-14) + with np.errstate(invalid='ignore'): + assert_allclose(p, converter(tr, pr, 'greater'), rtol=1e-14) + + +def test_ttest_rel_nan_2nd_arg(): + # regression test for gh-6134: nans in the second arg were not handled + x = [np.nan, 2.0, 3.0, 4.0] + y = [1.0, 2.0, 1.0, 2.0] + + r1 = stats.ttest_rel(x, y, nan_policy='omit') + r2 = stats.ttest_rel(y, x, nan_policy='omit') + assert_allclose(r2.statistic, -r1.statistic, atol=1e-15) + assert_allclose(r2.pvalue, r1.pvalue, atol=1e-15) + + # NB: arguments are paired when NaNs are dropped + r3 = stats.ttest_rel(y[1:], x[1:]) + assert_allclose(r2, r3, atol=1e-15) + + # .. and this is consistent with R. R code: + # x = c(NA, 2.0, 3.0, 4.0) + # y = c(1.0, 2.0, 1.0, 2.0) + # t.test(x, y, paired=TRUE) + assert_allclose(r2, (-2, 0.1835), atol=1e-4) + + +def test_ttest_rel_empty_1d_returns_nan(): + # Two empty inputs should return a TtestResult containing nan + # for both values. + result = stats.ttest_rel([], []) + assert isinstance(result, stats._stats_py.TtestResult) + assert_equal(result, (np.nan, np.nan)) + + +@pytest.mark.parametrize('b, expected_shape', + [(np.empty((1, 5, 0)), (3, 5)), + (np.empty((1, 0, 0)), (3, 0))]) +def test_ttest_rel_axis_size_zero(b, expected_shape): + # In this test, the length of the axis dimension is zero. + # The results should be arrays containing nan with shape + # given by the broadcast nonaxis dimensions. + a = np.empty((3, 1, 0)) + result = stats.ttest_rel(a, b, axis=-1) + assert isinstance(result, stats._stats_py.TtestResult) + expected_value = np.full(expected_shape, fill_value=np.nan) + assert_equal(result.statistic, expected_value) + assert_equal(result.pvalue, expected_value) + + +def test_ttest_rel_nonaxis_size_zero(): + # In this test, the length of the axis dimension is nonzero, + # but one of the nonaxis dimensions has length 0. Check that + # we still get the correctly broadcast shape, which is (5, 0) + # in this case. + a = np.empty((1, 8, 0)) + b = np.empty((5, 8, 1)) + result = stats.ttest_rel(a, b, axis=1) + assert isinstance(result, stats._stats_py.TtestResult) + assert_equal(result.statistic.shape, (5, 0)) + assert_equal(result.pvalue.shape, (5, 0)) + + +@pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater']) +def test_ttest_rel_ci_1d(alternative): + # test confidence interval method against reference values + rng = np.random.default_rng(3749065329432213059) + n = 10 + x = rng.normal(size=n, loc=1.5, scale=2) + y = rng.normal(size=n, loc=2, scale=2) + # Reference values generated with R t.test: + # options(digits=16) + # x = c(1.22825792, 1.63950485, 4.39025641, 0.68609437, 2.03813481, + # -1.20040109, 1.81997937, 1.86854636, 2.94694282, 3.94291373) + # y = c(3.49961496, 1.53192536, 5.53620083, 2.91687718, 0.04858043, + # 3.78505943, 3.3077496 , 2.30468892, 3.42168074, 0.56797592) + # t.test(x, y, paired=TRUE, conf.level=0.85, alternative='l') + + ref = {'two-sided': [-1.912194489914035, 0.400169725914035], + 'greater': [-1.563944820311475, np.inf], + 'less': [-np.inf, 0.05192005631147523]} + res = stats.ttest_rel(x, y, alternative=alternative) + ci = res.confidence_interval(confidence_level=0.85) + assert_allclose(ci, ref[alternative]) + assert_equal(res.df, n-1) + + +@pytest.mark.parametrize("test_fun, args", + [(stats.ttest_1samp, (np.arange(10), 0)), + (stats.ttest_rel, (np.arange(10), np.arange(10)))]) +def test_ttest_ci_iv(test_fun, args): + # test `confidence_interval` method input validation + res = test_fun(*args) + message = '`confidence_level` must be a number between 0 and 1.' + with pytest.raises(ValueError, match=message): + res.confidence_interval(confidence_level=10) + + +def _desc_stats(x1, x2, axis=0): + def _stats(x, axis=0): + x = np.asarray(x) + mu = np.mean(x, axis=axis) + std = np.std(x, axis=axis, ddof=1) + nobs = x.shape[axis] + return mu, std, nobs + return _stats(x1, axis) + _stats(x2, axis) + + +def test_ttest_ind(): + # regression test + tr = 1.0912746897927283 + pr = 0.27647818616351882 + tpr = ([tr,-tr],[pr,pr]) + + rvs2 = np.linspace(1,100,100) + rvs1 = np.linspace(5,105,100) + rvs1_2D = np.array([rvs1, rvs2]) + rvs2_2D = np.array([rvs2, rvs1]) + + t,p = stats.ttest_ind(rvs1, rvs2, axis=0) + assert_array_almost_equal([t,p],(tr,pr)) + # test from_stats API + assert_array_almost_equal(stats.ttest_ind_from_stats(*_desc_stats(rvs1, + rvs2)), + [t, p]) + t,p = stats.ttest_ind(rvs1_2D.T, rvs2_2D.T, axis=0) + assert_array_almost_equal([t,p],tpr) + args = _desc_stats(rvs1_2D.T, rvs2_2D.T) + assert_array_almost_equal(stats.ttest_ind_from_stats(*args), + [t, p]) + t,p = stats.ttest_ind(rvs1_2D, rvs2_2D, axis=1) + assert_array_almost_equal([t,p],tpr) + args = _desc_stats(rvs1_2D, rvs2_2D, axis=1) + assert_array_almost_equal(stats.ttest_ind_from_stats(*args), + [t, p]) + + # test scalars + with suppress_warnings() as sup, np.errstate(invalid="ignore"): + sup.filter(RuntimeWarning, "Degrees of freedom <= 0 for slice") + t, p = stats.ttest_ind(4., 3.) + assert_(np.isnan(t)) + assert_(np.isnan(p)) + + # test on 3 dimensions + rvs1_3D = np.dstack([rvs1_2D,rvs1_2D,rvs1_2D]) + rvs2_3D = np.dstack([rvs2_2D,rvs2_2D,rvs2_2D]) + t,p = stats.ttest_ind(rvs1_3D, rvs2_3D, axis=1) + assert_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (2, 3)) + + t, p = stats.ttest_ind(np.moveaxis(rvs1_3D, 2, 0), + np.moveaxis(rvs2_3D, 2, 0), + axis=2) + assert_array_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (3, 2)) + + # test alternative parameter + assert_raises(ValueError, stats.ttest_ind, rvs1, rvs2, alternative="error") + assert_raises(ValueError, stats.ttest_ind_from_stats, + *_desc_stats(rvs1_2D.T, rvs2_2D.T), alternative="error") + + t, p = stats.ttest_ind(rvs1, rvs2, alternative="less") + assert_allclose(p, 1 - (pr/2)) + assert_allclose(t, tr) + + t, p = stats.ttest_ind(rvs1, rvs2, alternative="greater") + assert_allclose(p, pr/2) + assert_allclose(t, tr) + + # Below makes sure ttest_ind_from_stats p-val functions identically to + # ttest_ind + t, p = stats.ttest_ind(rvs1_2D.T, rvs2_2D.T, axis=0, alternative="less") + args = _desc_stats(rvs1_2D.T, rvs2_2D.T) + assert_allclose( + stats.ttest_ind_from_stats(*args, alternative="less"), [t, p]) + + t, p = stats.ttest_ind(rvs1_2D.T, rvs2_2D.T, axis=0, alternative="greater") + args = _desc_stats(rvs1_2D.T, rvs2_2D.T) + assert_allclose( + stats.ttest_ind_from_stats(*args, alternative="greater"), [t, p]) + + # check nan policy + rng = np.random.RandomState(12345678) + x = stats.norm.rvs(loc=5, scale=10, size=501, random_state=rng) + x[500] = np.nan + y = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) + + with np.errstate(invalid="ignore"): + assert_array_equal(stats.ttest_ind(x, y), (np.nan, np.nan)) + + assert_array_almost_equal(stats.ttest_ind(x, y, nan_policy='omit'), + (0.24779670949091914, 0.80434267337517906)) + assert_raises(ValueError, stats.ttest_ind, x, y, nan_policy='raise') + assert_raises(ValueError, stats.ttest_ind, x, y, nan_policy='foobar') + + # test zero division problem + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + t, p = stats.ttest_ind([0, 0, 0], [1, 1, 1]) + assert_equal((np.abs(t), p), (np.inf, 0)) + + with np.errstate(invalid="ignore"): + assert_equal(stats.ttest_ind([0, 0, 0], [0, 0, 0]), (np.nan, np.nan)) + + # check that nan in input array result in nan output + anan = np.array([[1, np.nan], [-1, 1]]) + assert_equal(stats.ttest_ind(anan, np.zeros((2, 2))), + ([0, np.nan], [1, np.nan])) + + rvs1_3D[:, :, 10:15] = np.nan + rvs2_3D[:, :, 6:12] = np.nan + + # Convert from two-sided p-values to one sided using T result data. + def convert(t, p, alt): + if (t < 0 and alt == "less") or (t > 0 and alt == "greater"): + return p / 2 + return 1 - (p / 2) + converter = np.vectorize(convert) + + tr, pr = stats.ttest_ind(rvs1_3D, rvs2_3D, 0, nan_policy='omit') + + t, p = stats.ttest_ind(rvs1_3D, rvs2_3D, 0, nan_policy='omit', + alternative='less') + assert_allclose(t, tr, rtol=1e-14) + assert_allclose(p, converter(tr, pr, 'less'), rtol=1e-14) + + t, p = stats.ttest_ind(rvs1_3D, rvs2_3D, 0, nan_policy='omit', + alternative='greater') + assert_allclose(t, tr, rtol=1e-14) + assert_allclose(p, converter(tr, pr, 'greater'), rtol=1e-14) + + +class Test_ttest_ind_permutations: + N = 20 + + # data for most tests + np.random.seed(0) + a = np.vstack((np.arange(3*N//4), np.random.random(3*N//4))) + b = np.vstack((np.arange(N//4) + 100, np.random.random(N//4))) + + # data for equal variance tests + a2 = np.arange(10) + b2 = np.arange(10) + 100 + + # data for exact test + a3 = [1, 2] + b3 = [3, 4] + + # data for bigger test + np.random.seed(0) + rvs1 = stats.norm.rvs(loc=5, scale=10, # type: ignore + size=500).reshape(100, 5).T + rvs2 = stats.norm.rvs(loc=8, scale=20, size=100) # type: ignore + + p_d = [1/1001, (676+1)/1001] # desired pvalues + p_d_gen = [1/1001, (672 + 1)/1001] # desired pvalues for Generator seed + p_d_big = [(993+1)/1001, (685+1)/1001, (840+1)/1001, + (955+1)/1001, (255+1)/1001] + + params = [ + (a, b, {"axis": 1}, p_d), # basic test + (a.T, b.T, {'axis': 0}, p_d), # along axis 0 + (a[0, :], b[0, :], {'axis': None}, p_d[0]), # 1d data + (a[0, :].tolist(), b[0, :].tolist(), {'axis': None}, p_d[0]), + # different seeds + (a, b, {'random_state': 0, "axis": 1}, p_d), + (a, b, {'random_state': np.random.RandomState(0), "axis": 1}, p_d), + (a2, b2, {'equal_var': True}, 1/1001), # equal variances + (rvs1, rvs2, {'axis': -1, 'random_state': 0}, p_d_big), # bigger test + (a3, b3, {}, 1/3), # exact test + (a, b, {'random_state': np.random.default_rng(0), "axis": 1}, p_d_gen), + ] + + @pytest.mark.parametrize("a,b,update,p_d", params) + def test_ttest_ind_permutations(self, a, b, update, p_d): + options_a = {'axis': None, 'equal_var': False} + options_p = {'axis': None, 'equal_var': False, + 'permutations': 1000, 'random_state': 0} + options_a.update(update) + options_p.update(update) + + stat_a, _ = stats.ttest_ind(a, b, **options_a) + stat_p, pvalue = stats.ttest_ind(a, b, **options_p) + assert_array_almost_equal(stat_a, stat_p, 5) + assert_array_almost_equal(pvalue, p_d) + + def test_ttest_ind_exact_alternative(self): + np.random.seed(0) + N = 3 + a = np.random.rand(2, N, 2) + b = np.random.rand(2, N, 2) + + options_p = {'axis': 1, 'permutations': 1000} + + options_p.update(alternative="greater") + res_g_ab = stats.ttest_ind(a, b, **options_p) + res_g_ba = stats.ttest_ind(b, a, **options_p) + + options_p.update(alternative="less") + res_l_ab = stats.ttest_ind(a, b, **options_p) + res_l_ba = stats.ttest_ind(b, a, **options_p) + + options_p.update(alternative="two-sided") + res_2_ab = stats.ttest_ind(a, b, **options_p) + res_2_ba = stats.ttest_ind(b, a, **options_p) + + # Alternative doesn't affect the statistic + assert_equal(res_g_ab.statistic, res_l_ab.statistic) + assert_equal(res_g_ab.statistic, res_2_ab.statistic) + + # Reversing order of inputs negates statistic + assert_equal(res_g_ab.statistic, -res_g_ba.statistic) + assert_equal(res_l_ab.statistic, -res_l_ba.statistic) + assert_equal(res_2_ab.statistic, -res_2_ba.statistic) + + # Reversing order of inputs does not affect p-value of 2-sided test + assert_equal(res_2_ab.pvalue, res_2_ba.pvalue) + + # In exact test, distribution is perfectly symmetric, so these + # identities are exactly satisfied. + assert_equal(res_g_ab.pvalue, res_l_ba.pvalue) + assert_equal(res_l_ab.pvalue, res_g_ba.pvalue) + mask = res_g_ab.pvalue <= 0.5 + assert_equal(res_g_ab.pvalue[mask] + res_l_ba.pvalue[mask], + res_2_ab.pvalue[mask]) + assert_equal(res_l_ab.pvalue[~mask] + res_g_ba.pvalue[~mask], + res_2_ab.pvalue[~mask]) + + def test_ttest_ind_exact_selection(self): + # test the various ways of activating the exact test + np.random.seed(0) + N = 3 + a = np.random.rand(N) + b = np.random.rand(N) + res0 = stats.ttest_ind(a, b) + res1 = stats.ttest_ind(a, b, permutations=1000) + res2 = stats.ttest_ind(a, b, permutations=0) + res3 = stats.ttest_ind(a, b, permutations=np.inf) + assert res1.pvalue != res0.pvalue + assert res2.pvalue == res0.pvalue + assert res3.pvalue == res1.pvalue + + def test_ttest_ind_exact_distribution(self): + # the exact distribution of the test statistic should have + # binom(na + nb, na) elements, all unique. This was not always true + # in gh-4824; fixed by gh-13661. + np.random.seed(0) + a = np.random.rand(3) + b = np.random.rand(4) + + data = np.concatenate((a, b)) + na, nb = len(a), len(b) + + permutations = 100000 + t_stat, _, _ = _permutation_distribution_t(data, permutations, na, + True) + + n_unique = len(set(t_stat)) + assert n_unique == binom(na + nb, na) + assert len(t_stat) == n_unique + + def test_ttest_ind_randperm_alternative(self): + np.random.seed(0) + N = 50 + a = np.random.rand(2, 3, N) + b = np.random.rand(3, N) + options_p = {'axis': -1, 'permutations': 1000, "random_state": 0} + + options_p.update(alternative="greater") + res_g_ab = stats.ttest_ind(a, b, **options_p) + res_g_ba = stats.ttest_ind(b, a, **options_p) + + options_p.update(alternative="less") + res_l_ab = stats.ttest_ind(a, b, **options_p) + res_l_ba = stats.ttest_ind(b, a, **options_p) + + # Alternative doesn't affect the statistic + assert_equal(res_g_ab.statistic, res_l_ab.statistic) + + # Reversing order of inputs negates statistic + assert_equal(res_g_ab.statistic, -res_g_ba.statistic) + assert_equal(res_l_ab.statistic, -res_l_ba.statistic) + + # For random permutations, the chance of ties between the observed + # test statistic and the population is small, so: + assert_equal(res_g_ab.pvalue + res_l_ab.pvalue, + 1 + 1/(options_p['permutations'] + 1)) + assert_equal(res_g_ba.pvalue + res_l_ba.pvalue, + 1 + 1/(options_p['permutations'] + 1)) + + @pytest.mark.slow() + def test_ttest_ind_randperm_alternative2(self): + np.random.seed(0) + N = 50 + a = np.random.rand(N, 4) + b = np.random.rand(N, 4) + options_p = {'permutations': 20000, "random_state": 0} + + options_p.update(alternative="greater") + res_g_ab = stats.ttest_ind(a, b, **options_p) + + options_p.update(alternative="less") + res_l_ab = stats.ttest_ind(a, b, **options_p) + + options_p.update(alternative="two-sided") + res_2_ab = stats.ttest_ind(a, b, **options_p) + + # For random permutations, the chance of ties between the observed + # test statistic and the population is small, so: + assert_equal(res_g_ab.pvalue + res_l_ab.pvalue, + 1 + 1/(options_p['permutations'] + 1)) + + # For for large sample sizes, the distribution should be approximately + # symmetric, so these identities should be approximately satisfied + mask = res_g_ab.pvalue <= 0.5 + assert_allclose(2 * res_g_ab.pvalue[mask], + res_2_ab.pvalue[mask], atol=2e-2) + assert_allclose(2 * (1-res_g_ab.pvalue[~mask]), + res_2_ab.pvalue[~mask], atol=2e-2) + assert_allclose(2 * res_l_ab.pvalue[~mask], + res_2_ab.pvalue[~mask], atol=2e-2) + assert_allclose(2 * (1-res_l_ab.pvalue[mask]), + res_2_ab.pvalue[mask], atol=2e-2) + + def test_ttest_ind_permutation_nanpolicy(self): + np.random.seed(0) + N = 50 + a = np.random.rand(N, 5) + b = np.random.rand(N, 5) + a[5, 1] = np.nan + b[8, 2] = np.nan + a[9, 3] = np.nan + b[9, 3] = np.nan + options_p = {'permutations': 1000, "random_state": 0} + + # Raise + options_p.update(nan_policy="raise") + with assert_raises(ValueError, match="The input contains nan values"): + res = stats.ttest_ind(a, b, **options_p) + + # Propagate + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "invalid value*") + options_p.update(nan_policy="propagate") + res = stats.ttest_ind(a, b, **options_p) + + mask = np.isnan(a).any(axis=0) | np.isnan(b).any(axis=0) + res2 = stats.ttest_ind(a[:, ~mask], b[:, ~mask], **options_p) + + assert_equal(res.pvalue[mask], np.nan) + assert_equal(res.statistic[mask], np.nan) + + assert_allclose(res.pvalue[~mask], res2.pvalue) + assert_allclose(res.statistic[~mask], res2.statistic) + + # Propagate 1d + res = stats.ttest_ind(a.ravel(), b.ravel(), **options_p) + assert np.isnan(res.pvalue) # assert makes sure it's a scalar + assert np.isnan(res.statistic) + + def test_ttest_ind_permutation_check_inputs(self): + with assert_raises(ValueError, match="Permutations must be"): + stats.ttest_ind(self.a2, self.b2, permutations=-3) + with assert_raises(ValueError, match="Permutations must be"): + stats.ttest_ind(self.a2, self.b2, permutations=1.5) + with assert_raises(ValueError, match="'hello' cannot be used"): + stats.ttest_ind(self.a, self.b, permutations=1, + random_state='hello', axis=1) + + def test_ttest_ind_permutation_check_p_values(self): + # p-values should never be exactly zero + N = 10 + a = np.random.rand(N, 20) + b = np.random.rand(N, 20) + p_values = stats.ttest_ind(a, b, permutations=1).pvalue + print(0.0 not in p_values) + assert 0.0 not in p_values + + +class Test_ttest_ind_common: + # for tests that are performed on variations of the t-test such as + # permutations and trimming + @pytest.mark.slow() + @pytest.mark.parametrize("kwds", [{'permutations': 200, 'random_state': 0}, + {'trim': .2}, {}], + ids=["permutations", "trim", "basic"]) + @pytest.mark.parametrize('equal_var', [True, False], + ids=['equal_var', 'unequal_var']) + def test_ttest_many_dims(self, kwds, equal_var): + # Test that test works on many-dimensional arrays + np.random.seed(0) + a = np.random.rand(5, 4, 4, 7, 1, 6) + b = np.random.rand(4, 1, 8, 2, 6) + res = stats.ttest_ind(a, b, axis=-3, **kwds) + + # compare fully-vectorized t-test against t-test on smaller slice + i, j, k = 2, 3, 1 + a2 = a[i, :, j, :, 0, :] + b2 = b[:, 0, :, k, :] + res2 = stats.ttest_ind(a2, b2, axis=-2, **kwds) + assert_equal(res.statistic[i, :, j, k, :], + res2.statistic) + assert_equal(res.pvalue[i, :, j, k, :], + res2.pvalue) + + # compare against t-test on one axis-slice at a time + + # manually broadcast with tile; move axis to end to simplify + x = np.moveaxis(np.tile(a, (1, 1, 1, 1, 2, 1)), -3, -1) + y = np.moveaxis(np.tile(b, (5, 1, 4, 1, 1, 1)), -3, -1) + shape = x.shape[:-1] + statistics = np.zeros(shape) + pvalues = np.zeros(shape) + for indices in product(*(range(i) for i in shape)): + xi = x[indices] # use tuple to index single axis slice + yi = y[indices] + res3 = stats.ttest_ind(xi, yi, axis=-1, **kwds) + statistics[indices] = res3.statistic + pvalues[indices] = res3.pvalue + + assert_allclose(statistics, res.statistic) + assert_allclose(pvalues, res.pvalue) + + @pytest.mark.parametrize("kwds", [{'permutations': 200, 'random_state': 0}, + {'trim': .2}, {}], + ids=["trim", "permutations", "basic"]) + @pytest.mark.parametrize("axis", [-1, 0]) + def test_nans_on_axis(self, kwds, axis): + # confirm that with `nan_policy='propagate'`, NaN results are returned + # on the correct location + a = np.random.randint(10, size=(5, 3, 10)).astype('float') + b = np.random.randint(10, size=(5, 3, 10)).astype('float') + # set some indices in `a` and `b` to be `np.nan`. + a[0][2][3] = np.nan + b[2][0][6] = np.nan + + # arbitrarily use `np.sum` as a baseline for which indices should be + # NaNs + expected = np.isnan(np.sum(a + b, axis=axis)) + # multidimensional inputs to `t.sf(np.abs(t), df)` with NaNs on some + # indices throws an warning. See issue gh-13844 + with suppress_warnings() as sup, np.errstate(invalid="ignore"): + sup.filter(RuntimeWarning, + "invalid value encountered in less_equal") + sup.filter(RuntimeWarning, "Precision loss occurred") + res = stats.ttest_ind(a, b, axis=axis, **kwds) + p_nans = np.isnan(res.pvalue) + assert_array_equal(p_nans, expected) + statistic_nans = np.isnan(res.statistic) + assert_array_equal(statistic_nans, expected) + + +class Test_ttest_trim: + params = [ + [[1, 2, 3], [1.1, 2.9, 4.2], 0.53619490753126731, -0.6864951273557258, + .2], + [[56, 128.6, 12, 123.8, 64.34, 78, 763.3], [1.1, 2.9, 4.2], + 0.00998909252078421, 4.591598691181999, .2], + [[56, 128.6, 12, 123.8, 64.34, 78, 763.3], [1.1, 2.9, 4.2], + 0.10512380092302633, 2.832256715395378, .32], + [[2.7, 2.7, 1.1, 3.0, 1.9, 3.0, 3.8, 3.8, 0.3, 1.9, 1.9], + [6.5, 5.4, 8.1, 3.5, 0.5, 3.8, 6.8, 4.9, 9.5, 6.2, 4.1], + 0.002878909511344, -4.2461168970325, .2], + [[-0.84504783, 0.13366078, 3.53601757, -0.62908581, 0.54119466, + -1.16511574, -0.08836614, 1.18495416, 2.48028757, -1.58925028, + -1.6706357, 0.3090472, -2.12258305, 0.3697304, -1.0415207, + -0.57783497, -0.90997008, 1.09850192, 0.41270579, -1.4927376], + [1.2725522, 1.1657899, 2.7509041, 1.2389013, -0.9490494, -1.0752459, + 1.1038576, 2.9912821, 3.5349111, 0.4171922, 1.0168959, -0.7625041, + -0.4300008, 3.0431921, 1.6035947, 0.5285634, -0.7649405, 1.5575896, + 1.3670797, 1.1726023], 0.005293305834235, -3.0983317739483, .2]] + + @pytest.mark.parametrize("a,b,pr,tr,trim", params) + def test_ttest_compare_r(self, a, b, pr, tr, trim): + ''' + Using PairedData's yuen.t.test method. Something to note is that there + are at least 3 R packages that come with a trimmed t-test method, and + comparisons were made between them. It was found that PairedData's + method's results match this method, SAS, and one of the other R + methods. A notable discrepancy was the DescTools implementation of the + function, which only sometimes agreed with SAS, WRS2, PairedData and + this implementation. For this reason, most comparisons in R are made + against PairedData's method. + + Rather than providing the input and output for all evaluations, here is + a representative example: + > library(PairedData) + > a <- c(1, 2, 3) + > b <- c(1.1, 2.9, 4.2) + > options(digits=16) + > yuen.t.test(a, b, tr=.2) + + Two-sample Yuen test, trim=0.2 + + data: x and y + t = -0.68649512735573, df = 3.4104431643464, p-value = 0.5361949075313 + alternative hypothesis: true difference in trimmed means is not equal + to 0 + 95 percent confidence interval: + -3.912777195645217 2.446110528978550 + sample estimates: + trimmed mean of x trimmed mean of y + 2.000000000000000 2.73333333333333 + ''' + statistic, pvalue = stats.ttest_ind(a, b, trim=trim, equal_var=False) + assert_allclose(statistic, tr, atol=1e-15) + assert_allclose(pvalue, pr, atol=1e-15) + + def test_compare_SAS(self): + # Source of the data used in this test: + # https://support.sas.com/resources/papers/proceedings14/1660-2014.pdf + a = [12, 14, 18, 25, 32, 44, 12, 14, 18, 25, 32, 44] + b = [17, 22, 14, 12, 30, 29, 19, 17, 22, 14, 12, 30, 29, 19] + # In this paper, a trimming percentage of 5% is used. However, + # in their implementation, the number of values trimmed is rounded to + # the nearest whole number. However, consistent with + # `scipy.stats.trimmed_mean`, this test truncates to the lower + # whole number. In this example, the paper notes that 1 value is + # trimmed off of each side. 9% replicates this amount of trimming. + statistic, pvalue = stats.ttest_ind(a, b, trim=.09, equal_var=False) + assert_allclose(pvalue, 0.514522, atol=1e-6) + assert_allclose(statistic, 0.669169, atol=1e-6) + + def test_equal_var(self): + ''' + The PairedData library only supports unequal variances. To compare + samples with equal variances, the multicon library is used. + > library(multicon) + > a <- c(2.7, 2.7, 1.1, 3.0, 1.9, 3.0, 3.8, 3.8, 0.3, 1.9, 1.9) + > b <- c(6.5, 5.4, 8.1, 3.5, 0.5, 3.8, 6.8, 4.9, 9.5, 6.2, 4.1) + > dv = c(a,b) + > iv = c(rep('a', length(a)), rep('b', length(b))) + > yuenContrast(dv~ iv, EQVAR = TRUE) + $Ms + N M wgt + a 11 2.442857142857143 1 + b 11 5.385714285714286 -1 + + $test + stat df crit p + results -4.246116897032513 12 2.178812829667228 0.00113508833897713 + ''' + a = [2.7, 2.7, 1.1, 3.0, 1.9, 3.0, 3.8, 3.8, 0.3, 1.9, 1.9] + b = [6.5, 5.4, 8.1, 3.5, 0.5, 3.8, 6.8, 4.9, 9.5, 6.2, 4.1] + # `equal_var=True` is default + statistic, pvalue = stats.ttest_ind(a, b, trim=.2) + assert_allclose(pvalue, 0.00113508833897713, atol=1e-10) + assert_allclose(statistic, -4.246116897032513, atol=1e-10) + + @pytest.mark.parametrize('alt,pr,tr', + (('greater', 0.9985605452443, -4.2461168970325), + ('less', 0.001439454755672, -4.2461168970325),), + ) + def test_alternatives(self, alt, pr, tr): + ''' + > library(PairedData) + > a <- c(2.7,2.7,1.1,3.0,1.9,3.0,3.8,3.8,0.3,1.9,1.9) + > b <- c(6.5,5.4,8.1,3.5,0.5,3.8,6.8,4.9,9.5,6.2,4.1) + > options(digits=16) + > yuen.t.test(a, b, alternative = 'greater') + ''' + a = [2.7, 2.7, 1.1, 3.0, 1.9, 3.0, 3.8, 3.8, 0.3, 1.9, 1.9] + b = [6.5, 5.4, 8.1, 3.5, 0.5, 3.8, 6.8, 4.9, 9.5, 6.2, 4.1] + + statistic, pvalue = stats.ttest_ind(a, b, trim=.2, equal_var=False, + alternative=alt) + assert_allclose(pvalue, pr, atol=1e-10) + assert_allclose(statistic, tr, atol=1e-10) + + def test_errors_unsupported(self): + # confirm that attempting to trim with NaNs or permutations raises an + # error + match = "Permutations are currently not supported with trimming." + with assert_raises(ValueError, match=match): + stats.ttest_ind([1, 2], [2, 3], trim=.2, permutations=2) + + @pytest.mark.parametrize("trim", [-.2, .5, 1]) + def test_trim_bounds_error(self, trim): + match = "Trimming percentage should be 0 <= `trim` < .5." + with assert_raises(ValueError, match=match): + stats.ttest_ind([1, 2], [2, 1], trim=trim) + + +class Test_ttest_CI: + # indices in order [alternative={two-sided, less, greater}, + # equal_var={False, True}, trim={0, 0.2}] + # reference values in order `statistic, df, pvalue, low, high` + # equal_var=False reference values computed with R PairedData yuen.t.test: + # + # library(PairedData) + # options(digits=16) + # a < - c(0.88236329, 0.97318744, 0.4549262, 0.97893335, 0.0606677, + # 0.44013366, 0.55806018, 0.40151434, 0.14453315, 0.25860601, + # 0.20202162) + # b < - c(0.93455277, 0.42680603, 0.49751939, 0.14152846, 0.711435, + # 0.77669667, 0.20507578, 0.78702772, 0.94691855, 0.32464958, + # 0.3873582, 0.35187468, 0.21731811) + # yuen.t.test(a, b, tr=0, conf.level = 0.9, alternative = 'l') + # + # equal_var=True reference values computed with R multicon yuenContrast: + # + # library(multicon) + # options(digits=16) + # a < - c(0.88236329, 0.97318744, 0.4549262, 0.97893335, 0.0606677, + # 0.44013366, 0.55806018, 0.40151434, 0.14453315, 0.25860601, + # 0.20202162) + # b < - c(0.93455277, 0.42680603, 0.49751939, 0.14152846, 0.711435, + # 0.77669667, 0.20507578, 0.78702772, 0.94691855, 0.32464958, + # 0.3873582, 0.35187468, 0.21731811) + # dv = c(a, b) + # iv = c(rep('a', length(a)), rep('b', length(b))) + # yuenContrast(dv~iv, EQVAR = FALSE, alternative = 'unequal', tr = 0.2) + r = np.empty(shape=(3, 2, 2, 5)) + r[0, 0, 0] = [-0.2314607, 19.894435, 0.8193209, -0.247220294, 0.188729943] + r[1, 0, 0] = [-0.2314607, 19.894435, 0.40966045, -np.inf, 0.1382426469] + r[2, 0, 0] = [-0.2314607, 19.894435, 0.5903395, -0.1967329982, np.inf] + r[0, 0, 1] = [-0.2452886, 11.427896, 0.8105823, -0.34057446, 0.25847383] + r[1, 0, 1] = [-0.2452886, 11.427896, 0.40529115, -np.inf, 0.1865829074] + r[2, 0, 1] = [-0.2452886, 11.427896, 0.5947089, -0.268683541, np.inf] + # confidence interval not available for equal_var=True + r[0, 1, 0] = [-0.2345625322555006, 22, 0.8167175905643815, None, None] + r[1, 1, 0] = [-0.2345625322555006, 22, 0.4083587952821908, None, None] + r[2, 1, 0] = [-0.2345625322555006, 22, 0.5916412047178092, None, None] + r[0, 1, 1] = [-0.2505369406507428, 14, 0.8058115135702835, None, None] + r[1, 1, 1] = [-0.2505369406507428, 14, 0.4029057567851417, None, None] + r[2, 1, 1] = [-0.2505369406507428, 14, 0.5970942432148583, None, None] + @pytest.mark.parametrize('alternative', ['two-sided', 'less', 'greater']) + @pytest.mark.parametrize('equal_var', [False, True]) + @pytest.mark.parametrize('trim', [0, 0.2]) + def test_confidence_interval(self, alternative, equal_var, trim): + if equal_var and trim: + pytest.xfail('Discrepancy in `main`; needs further investigation.') + + rng = np.random.default_rng(3810954496107292580) + x = rng.random(11) + y = rng.random(13) + + res = stats.ttest_ind(x, y, alternative=alternative, + equal_var=equal_var, trim=trim) + + alternatives = {'two-sided': 0, 'less': 1, 'greater': 2} + ref = self.r[alternatives[alternative], int(equal_var), int(np.ceil(trim))] + statistic, df, pvalue, low, high = ref + assert_allclose(res.statistic, statistic) + assert_allclose(res.df, df) + assert_allclose(res.pvalue, pvalue) + if not equal_var: # CI not available when `equal_var is True` + ci = res.confidence_interval(0.9) + assert_allclose(ci.low, low) + assert_allclose(ci.high, high) + + +def test__broadcast_concatenate(): + # test that _broadcast_concatenate properly broadcasts arrays along all + # axes except `axis`, then concatenates along axis + np.random.seed(0) + a = np.random.rand(5, 4, 4, 3, 1, 6) + b = np.random.rand(4, 1, 8, 2, 6) + c = _broadcast_concatenate((a, b), axis=-3) + # broadcast manually as an independent check + a = np.tile(a, (1, 1, 1, 1, 2, 1)) + b = np.tile(b[None, ...], (5, 1, 4, 1, 1, 1)) + for index in product(*(range(i) for i in c.shape)): + i, j, k, l, m, n = index + if l < a.shape[-3]: + assert a[i, j, k, l, m, n] == c[i, j, k, l, m, n] + else: + assert b[i, j, k, l - a.shape[-3], m, n] == c[i, j, k, l, m, n] + + +def test_ttest_ind_with_uneq_var(): + # check vs. R + a = (1, 2, 3) + b = (1.1, 2.9, 4.2) + pr = 0.53619490753126731 + tr = -0.68649512735572582 + t, p = stats.ttest_ind(a, b, equal_var=False) + assert_array_almost_equal([t,p], [tr, pr]) + # test from desc stats API + assert_array_almost_equal(stats.ttest_ind_from_stats(*_desc_stats(a, b), + equal_var=False), + [t, p]) + + a = (1, 2, 3, 4) + pr = 0.84354139131608286 + tr = -0.2108663315950719 + t, p = stats.ttest_ind(a, b, equal_var=False) + assert_array_almost_equal([t,p], [tr, pr]) + assert_array_almost_equal(stats.ttest_ind_from_stats(*_desc_stats(a, b), + equal_var=False), + [t, p]) + + # regression test + tr = 1.0912746897927283 + tr_uneq_n = 0.66745638708050492 + pr = 0.27647831993021388 + pr_uneq_n = 0.50873585065616544 + tpr = ([tr,-tr],[pr,pr]) + + rvs3 = np.linspace(1,100, 25) + rvs2 = np.linspace(1,100,100) + rvs1 = np.linspace(5,105,100) + rvs1_2D = np.array([rvs1, rvs2]) + + rvs2_2D = np.array([rvs2, rvs1]) + + t,p = stats.ttest_ind(rvs1, rvs2, axis=0, equal_var=False) + assert_array_almost_equal([t,p],(tr,pr)) + assert_array_almost_equal(stats.ttest_ind_from_stats(*_desc_stats(rvs1, + rvs2), + equal_var=False), + (t, p)) + + t,p = stats.ttest_ind(rvs1, rvs3, axis=0, equal_var=False) + assert_array_almost_equal([t,p], (tr_uneq_n, pr_uneq_n)) + assert_array_almost_equal(stats.ttest_ind_from_stats(*_desc_stats(rvs1, + rvs3), + equal_var=False), + (t, p)) + + t,p = stats.ttest_ind(rvs1_2D.T, rvs2_2D.T, axis=0, equal_var=False) + assert_array_almost_equal([t,p],tpr) + args = _desc_stats(rvs1_2D.T, rvs2_2D.T) + assert_array_almost_equal(stats.ttest_ind_from_stats(*args, + equal_var=False), + (t, p)) + + t,p = stats.ttest_ind(rvs1_2D, rvs2_2D, axis=1, equal_var=False) + assert_array_almost_equal([t,p],tpr) + args = _desc_stats(rvs1_2D, rvs2_2D, axis=1) + assert_array_almost_equal(stats.ttest_ind_from_stats(*args, + equal_var=False), + (t, p)) + + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.ttest_ind(rvs1, rvs2, axis=0, equal_var=False) + check_named_results(res, attributes) + + # test on 3 dimensions + rvs1_3D = np.dstack([rvs1_2D,rvs1_2D,rvs1_2D]) + rvs2_3D = np.dstack([rvs2_2D,rvs2_2D,rvs2_2D]) + t,p = stats.ttest_ind(rvs1_3D, rvs2_3D, axis=1, equal_var=False) + assert_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (2, 3)) + args = _desc_stats(rvs1_3D, rvs2_3D, axis=1) + t, p = stats.ttest_ind_from_stats(*args, equal_var=False) + assert_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (2, 3)) + + t, p = stats.ttest_ind(np.moveaxis(rvs1_3D, 2, 0), + np.moveaxis(rvs2_3D, 2, 0), + axis=2, equal_var=False) + assert_array_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (3, 2)) + args = _desc_stats(np.moveaxis(rvs1_3D, 2, 0), + np.moveaxis(rvs2_3D, 2, 0), axis=2) + t, p = stats.ttest_ind_from_stats(*args, equal_var=False) + assert_array_almost_equal(np.abs(t), np.abs(tr)) + assert_array_almost_equal(np.abs(p), pr) + assert_equal(t.shape, (3, 2)) + + # test zero division problem + with pytest.warns(RuntimeWarning, match="Precision loss occurred"): + t, p = stats.ttest_ind([0, 0, 0], [1, 1, 1], equal_var=False) + assert_equal((np.abs(t), p), (np.inf, 0)) + with np.errstate(all='ignore'): + assert_equal(stats.ttest_ind([0, 0, 0], [0, 0, 0], equal_var=False), + (np.nan, np.nan)) + + # check that nan in input array result in nan output + anan = np.array([[1, np.nan], [-1, 1]]) + assert_equal(stats.ttest_ind(anan, np.zeros((2, 2)), equal_var=False), + ([0, np.nan], [1, np.nan])) + + +def test_ttest_ind_nan_2nd_arg(): + # regression test for gh-6134: nans in the second arg were not handled + x = [np.nan, 2.0, 3.0, 4.0] + y = [1.0, 2.0, 1.0, 2.0] + + r1 = stats.ttest_ind(x, y, nan_policy='omit') + r2 = stats.ttest_ind(y, x, nan_policy='omit') + assert_allclose(r2.statistic, -r1.statistic, atol=1e-15) + assert_allclose(r2.pvalue, r1.pvalue, atol=1e-15) + + # NB: arguments are not paired when NaNs are dropped + r3 = stats.ttest_ind(y, x[1:]) + assert_allclose(r2, r3, atol=1e-15) + + # .. and this is consistent with R. R code: + # x = c(NA, 2.0, 3.0, 4.0) + # y = c(1.0, 2.0, 1.0, 2.0) + # t.test(x, y, var.equal=TRUE) + assert_allclose(r2, (-2.5354627641855498, 0.052181400457057901), + atol=1e-15) + + +def test_ttest_ind_empty_1d_returns_nan(): + # Two empty inputs should return a TtestResult containing nan + # for both values. + result = stats.ttest_ind([], []) + assert isinstance(result, stats._stats_py.TtestResult) + assert_equal(result, (np.nan, np.nan)) + + +@pytest.mark.parametrize('b, expected_shape', + [(np.empty((1, 5, 0)), (3, 5)), + (np.empty((1, 0, 0)), (3, 0))]) +def test_ttest_ind_axis_size_zero(b, expected_shape): + # In this test, the length of the axis dimension is zero. + # The results should be arrays containing nan with shape + # given by the broadcast nonaxis dimensions. + a = np.empty((3, 1, 0)) + result = stats.ttest_ind(a, b, axis=-1) + assert isinstance(result, stats._stats_py.TtestResult) + expected_value = np.full(expected_shape, fill_value=np.nan) + assert_equal(result.statistic, expected_value) + assert_equal(result.pvalue, expected_value) + + +def test_ttest_ind_nonaxis_size_zero(): + # In this test, the length of the axis dimension is nonzero, + # but one of the nonaxis dimensions has length 0. Check that + # we still get the correctly broadcast shape, which is (5, 0) + # in this case. + a = np.empty((1, 8, 0)) + b = np.empty((5, 8, 1)) + result = stats.ttest_ind(a, b, axis=1) + assert isinstance(result, stats._stats_py.TtestResult) + assert_equal(result.statistic.shape, (5, 0)) + assert_equal(result.pvalue.shape, (5, 0)) + + +def test_ttest_ind_nonaxis_size_zero_different_lengths(): + # In this test, the length of the axis dimension is nonzero, + # and that size is different in the two inputs, + # and one of the nonaxis dimensions has length 0. Check that + # we still get the correctly broadcast shape, which is (5, 0) + # in this case. + a = np.empty((1, 7, 0)) + b = np.empty((5, 8, 1)) + result = stats.ttest_ind(a, b, axis=1) + assert isinstance(result, stats._stats_py.TtestResult) + assert_equal(result.statistic.shape, (5, 0)) + assert_equal(result.pvalue.shape, (5, 0)) + + +def test_gh5686(): + mean1, mean2 = np.array([1, 2]), np.array([3, 4]) + std1, std2 = np.array([5, 3]), np.array([4, 5]) + nobs1, nobs2 = np.array([130, 140]), np.array([100, 150]) + # This will raise a TypeError unless gh-5686 is fixed. + stats.ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2) + + +def test_ttest_ind_from_stats_inputs_zero(): + # Regression test for gh-6409. + result = stats.ttest_ind_from_stats(0, 0, 6, 0, 0, 6, equal_var=False) + assert_equal(result, [np.nan, np.nan]) + + +def test_ttest_single_observation(): + # test that p-values are uniformly distributed under the null hypothesis + rng = np.random.default_rng(246834602926842) + x = rng.normal(size=(10000, 2)) + y = rng.normal(size=(10000, 1)) + q = rng.uniform(size=100) + + res = stats.ttest_ind(x, y, equal_var=True, axis=-1) + assert stats.ks_1samp(res.pvalue, stats.uniform().cdf).pvalue > 0.1 + assert_allclose(np.percentile(res.pvalue, q*100), q, atol=1e-2) + + res = stats.ttest_ind(y, x, equal_var=True, axis=-1) + assert stats.ks_1samp(res.pvalue, stats.uniform().cdf).pvalue > 0.1 + assert_allclose(np.percentile(res.pvalue, q*100), q, atol=1e-2) + + # reference values from R: + # options(digits=16) + # t.test(c(2, 3, 5), c(1.5), var.equal=TRUE) + res = stats.ttest_ind([2, 3, 5], [1.5], equal_var=True) + assert_allclose(res, (1.0394023007754, 0.407779907736), rtol=1e-10) + + +def test_ttest_1samp_new(): + n1, n2, n3 = (10,15,20) + rvn1 = stats.norm.rvs(loc=5,scale=10,size=(n1,n2,n3)) + + # check multidimensional array and correct axis handling + # deterministic rvn1 and rvn2 would be better as in test_ttest_rel + t1,p1 = stats.ttest_1samp(rvn1[:,:,:], np.ones((n2,n3)),axis=0) + t2,p2 = stats.ttest_1samp(rvn1[:,:,:], 1,axis=0) + t3,p3 = stats.ttest_1samp(rvn1[:,0,0], 1) + assert_array_almost_equal(t1,t2, decimal=14) + assert_almost_equal(t1[0,0],t3, decimal=14) + assert_equal(t1.shape, (n2,n3)) + + t1,p1 = stats.ttest_1samp(rvn1[:,:,:], np.ones((n1, 1, n3)),axis=1) + t2,p2 = stats.ttest_1samp(rvn1[:,:,:], 1,axis=1) + t3,p3 = stats.ttest_1samp(rvn1[0,:,0], 1) + assert_array_almost_equal(t1,t2, decimal=14) + assert_almost_equal(t1[0,0],t3, decimal=14) + assert_equal(t1.shape, (n1,n3)) + + t1,p1 = stats.ttest_1samp(rvn1[:,:,:], np.ones((n1,n2,1)),axis=2) + t2,p2 = stats.ttest_1samp(rvn1[:,:,:], 1,axis=2) + t3,p3 = stats.ttest_1samp(rvn1[0,0,:], 1) + assert_array_almost_equal(t1,t2, decimal=14) + assert_almost_equal(t1[0,0],t3, decimal=14) + assert_equal(t1.shape, (n1,n2)) + + # test zero division problem + t, p = stats.ttest_1samp([0, 0, 0], 1) + assert_equal((np.abs(t), p), (np.inf, 0)) + + # test alternative parameter + # Convert from two-sided p-values to one sided using T result data. + def convert(t, p, alt): + if (t < 0 and alt == "less") or (t > 0 and alt == "greater"): + return p / 2 + return 1 - (p / 2) + converter = np.vectorize(convert) + tr, pr = stats.ttest_1samp(rvn1[:, :, :], 1) + + t, p = stats.ttest_1samp(rvn1[:, :, :], 1, alternative="greater") + pc = converter(tr, pr, "greater") + assert_allclose(p, pc) + assert_allclose(t, tr) + + t, p = stats.ttest_1samp(rvn1[:, :, :], 1, alternative="less") + pc = converter(tr, pr, "less") + assert_allclose(p, pc) + assert_allclose(t, tr) + + with np.errstate(all='ignore'): + assert_equal(stats.ttest_1samp([0, 0, 0], 0), (np.nan, np.nan)) + + # check that nan in input array result in nan output + anan = np.array([[1, np.nan],[-1, 1]]) + assert_equal(stats.ttest_1samp(anan, 0), ([0, np.nan], [1, np.nan])) + + rvn1[0:2, 1:3, 4:8] = np.nan + + tr, pr = stats.ttest_1samp(rvn1[:, :, :], 1, nan_policy='omit') + + t, p = stats.ttest_1samp(rvn1[:, :, :], 1, nan_policy='omit', + alternative="greater") + pc = converter(tr, pr, "greater") + assert_allclose(p, pc) + assert_allclose(t, tr) + + t, p = stats.ttest_1samp(rvn1[:, :, :], 1, nan_policy='omit', + alternative="less") + pc = converter(tr, pr, "less") + assert_allclose(p, pc) + assert_allclose(t, tr) + + +def test_ttest_1samp_popmean_array(): + # when popmean.shape[axis] != 1, raise an error + # if the user wants to test multiple null hypotheses simultaneously, + # use standard broadcasting rules + rng = np.random.default_rng(2913300596553337193) + x = rng.random(size=(1, 15, 20)) + + message = r"`popmean.shape\[axis\]` must equal 1." + popmean = rng.random(size=(5, 2, 20)) + with pytest.raises(ValueError, match=message): + stats.ttest_1samp(x, popmean=popmean, axis=-2) + + popmean = rng.random(size=(5, 1, 20)) + res = stats.ttest_1samp(x, popmean=popmean, axis=-2) + assert res.statistic.shape == (5, 20) + + ci = np.expand_dims(res.confidence_interval(), axis=-2) + res = stats.ttest_1samp(x, popmean=ci, axis=-2) + assert_allclose(res.pvalue, 0.05) + + +class TestDescribe: + def test_describe_scalar(self): + with suppress_warnings() as sup, \ + np.errstate(invalid="ignore", divide="ignore"): + sup.filter(RuntimeWarning, "Degrees of freedom <= 0 for slice") + n, mm, m, v, sk, kurt = stats.describe(4.) + assert_equal(n, 1) + assert_equal(mm, (4.0, 4.0)) + assert_equal(m, 4.0) + assert np.isnan(v) + assert np.isnan(sk) + assert np.isnan(kurt) + + def test_describe_numbers(self): + x = np.vstack((np.ones((3,4)), np.full((2, 4), 2))) + nc, mmc = (5, ([1., 1., 1., 1.], [2., 2., 2., 2.])) + mc = np.array([1.4, 1.4, 1.4, 1.4]) + vc = np.array([0.3, 0.3, 0.3, 0.3]) + skc = [0.40824829046386357] * 4 + kurtc = [-1.833333333333333] * 4 + n, mm, m, v, sk, kurt = stats.describe(x) + assert_equal(n, nc) + assert_equal(mm, mmc) + assert_equal(m, mc) + assert_equal(v, vc) + assert_array_almost_equal(sk, skc, decimal=13) + assert_array_almost_equal(kurt, kurtc, decimal=13) + n, mm, m, v, sk, kurt = stats.describe(x.T, axis=1) + assert_equal(n, nc) + assert_equal(mm, mmc) + assert_equal(m, mc) + assert_equal(v, vc) + assert_array_almost_equal(sk, skc, decimal=13) + assert_array_almost_equal(kurt, kurtc, decimal=13) + + x = np.arange(10.) + x[9] = np.nan + + nc, mmc = (9, (0.0, 8.0)) + mc = 4.0 + vc = 7.5 + skc = 0.0 + kurtc = -1.2300000000000002 + n, mm, m, v, sk, kurt = stats.describe(x, nan_policy='omit') + assert_equal(n, nc) + assert_equal(mm, mmc) + assert_equal(m, mc) + assert_equal(v, vc) + assert_array_almost_equal(sk, skc) + assert_array_almost_equal(kurt, kurtc, decimal=13) + + assert_raises(ValueError, stats.describe, x, nan_policy='raise') + assert_raises(ValueError, stats.describe, x, nan_policy='foobar') + + def test_describe_result_attributes(self): + actual = stats.describe(np.arange(5)) + attributes = ('nobs', 'minmax', 'mean', 'variance', 'skewness', + 'kurtosis') + check_named_results(actual, attributes) + + def test_describe_ddof(self): + x = np.vstack((np.ones((3, 4)), np.full((2, 4), 2))) + nc, mmc = (5, ([1., 1., 1., 1.], [2., 2., 2., 2.])) + mc = np.array([1.4, 1.4, 1.4, 1.4]) + vc = np.array([0.24, 0.24, 0.24, 0.24]) + skc = [0.40824829046386357] * 4 + kurtc = [-1.833333333333333] * 4 + n, mm, m, v, sk, kurt = stats.describe(x, ddof=0) + assert_equal(n, nc) + assert_allclose(mm, mmc, rtol=1e-15) + assert_allclose(m, mc, rtol=1e-15) + assert_allclose(v, vc, rtol=1e-15) + assert_array_almost_equal(sk, skc, decimal=13) + assert_array_almost_equal(kurt, kurtc, decimal=13) + + def test_describe_axis_none(self): + x = np.vstack((np.ones((3, 4)), np.full((2, 4), 2))) + + # expected values + e_nobs, e_minmax = (20, (1.0, 2.0)) + e_mean = 1.3999999999999999 + e_var = 0.25263157894736848 + e_skew = 0.4082482904638634 + e_kurt = -1.8333333333333333 + + # actual values + a = stats.describe(x, axis=None) + + assert_equal(a.nobs, e_nobs) + assert_almost_equal(a.minmax, e_minmax) + assert_almost_equal(a.mean, e_mean) + assert_almost_equal(a.variance, e_var) + assert_array_almost_equal(a.skewness, e_skew, decimal=13) + assert_array_almost_equal(a.kurtosis, e_kurt, decimal=13) + + def test_describe_empty(self): + assert_raises(ValueError, stats.describe, []) + + +def test_normalitytests(): + assert_raises(ValueError, stats.skewtest, 4.) + assert_raises(ValueError, stats.kurtosistest, 4.) + assert_raises(ValueError, stats.normaltest, 4.) + + # numbers verified with R: dagoTest in package fBasics + st_normal, st_skew, st_kurt = (3.92371918, 1.98078826, -0.01403734) + pv_normal, pv_skew, pv_kurt = (0.14059673, 0.04761502, 0.98880019) + pv_skew_less, pv_kurt_less = 1 - pv_skew / 2, pv_kurt / 2 + pv_skew_greater, pv_kurt_greater = pv_skew / 2, 1 - pv_kurt / 2 + x = np.array((-2, -1, 0, 1, 2, 3)*4)**2 + attributes = ('statistic', 'pvalue') + + assert_array_almost_equal(stats.normaltest(x), (st_normal, pv_normal)) + check_named_results(stats.normaltest(x), attributes) + assert_array_almost_equal(stats.skewtest(x), (st_skew, pv_skew)) + assert_array_almost_equal(stats.skewtest(x, alternative='less'), + (st_skew, pv_skew_less)) + assert_array_almost_equal(stats.skewtest(x, alternative='greater'), + (st_skew, pv_skew_greater)) + check_named_results(stats.skewtest(x), attributes) + assert_array_almost_equal(stats.kurtosistest(x), (st_kurt, pv_kurt)) + assert_array_almost_equal(stats.kurtosistest(x, alternative='less'), + (st_kurt, pv_kurt_less)) + assert_array_almost_equal(stats.kurtosistest(x, alternative='greater'), + (st_kurt, pv_kurt_greater)) + check_named_results(stats.kurtosistest(x), attributes) + + # some more intuitive tests for kurtosistest and skewtest. + # see gh-13549. + # skew parameter is 1 > 0 + a1 = stats.skewnorm.rvs(a=1, size=10000, random_state=123) + pval = stats.skewtest(a1, alternative='greater').pvalue + assert_almost_equal(pval, 0.0, decimal=5) + # excess kurtosis of laplace is 3 > 0 + a2 = stats.laplace.rvs(size=10000, random_state=123) + pval = stats.kurtosistest(a2, alternative='greater').pvalue + assert_almost_equal(pval, 0.0) + + # Test axis=None (equal to axis=0 for 1-D input) + assert_array_almost_equal(stats.normaltest(x, axis=None), + (st_normal, pv_normal)) + assert_array_almost_equal(stats.skewtest(x, axis=None), + (st_skew, pv_skew)) + assert_array_almost_equal(stats.kurtosistest(x, axis=None), + (st_kurt, pv_kurt)) + + x = np.arange(10.) + x[9] = np.nan + with np.errstate(invalid="ignore"): + assert_array_equal(stats.skewtest(x), (np.nan, np.nan)) + + expected = (1.0184643553962129, 0.30845733195153502) + assert_array_almost_equal(stats.skewtest(x, nan_policy='omit'), expected) + + # test alternative with nan_policy='omit' + a1[10:100] = np.nan + z, p = stats.skewtest(a1, nan_policy='omit') + zl, pl = stats.skewtest(a1, nan_policy='omit', alternative='less') + zg, pg = stats.skewtest(a1, nan_policy='omit', alternative='greater') + assert_allclose(zl, z, atol=1e-15) + assert_allclose(zg, z, atol=1e-15) + assert_allclose(pl, 1 - p/2, atol=1e-15) + assert_allclose(pg, p/2, atol=1e-15) + + with np.errstate(all='ignore'): + assert_raises(ValueError, stats.skewtest, x, nan_policy='raise') + assert_raises(ValueError, stats.skewtest, x, nan_policy='foobar') + assert_raises(ValueError, stats.skewtest, list(range(8)), + alternative='foobar') + + x = np.arange(30.) + x[29] = np.nan + with np.errstate(all='ignore'): + assert_array_equal(stats.kurtosistest(x), (np.nan, np.nan)) + + expected = (-2.2683547379505273, 0.023307594135872967) + assert_array_almost_equal(stats.kurtosistest(x, nan_policy='omit'), + expected) + + # test alternative with nan_policy='omit' + a2[10:20] = np.nan + z, p = stats.kurtosistest(a2[:100], nan_policy='omit') + zl, pl = stats.kurtosistest(a2[:100], nan_policy='omit', + alternative='less') + zg, pg = stats.kurtosistest(a2[:100], nan_policy='omit', + alternative='greater') + assert_allclose(zl, z, atol=1e-15) + assert_allclose(zg, z, atol=1e-15) + assert_allclose(pl, 1 - p/2, atol=1e-15) + assert_allclose(pg, p/2, atol=1e-15) + + assert_raises(ValueError, stats.kurtosistest, x, nan_policy='raise') + assert_raises(ValueError, stats.kurtosistest, x, nan_policy='foobar') + assert_raises(ValueError, stats.kurtosistest, list(range(20)), + alternative='foobar') + + with np.errstate(all='ignore'): + assert_array_equal(stats.normaltest(x), (np.nan, np.nan)) + + expected = (6.2260409514287449, 0.04446644248650191) + assert_array_almost_equal(stats.normaltest(x, nan_policy='omit'), expected) + + assert_raises(ValueError, stats.normaltest, x, nan_policy='raise') + assert_raises(ValueError, stats.normaltest, x, nan_policy='foobar') + + # regression test for issue gh-9033: x clearly non-normal but power of + # negative denom needs to be handled correctly to reject normality + counts = [128, 0, 58, 7, 0, 41, 16, 0, 0, 167] + x = np.hstack([np.full(c, i) for i, c in enumerate(counts)]) + assert_equal(stats.kurtosistest(x)[1] < 0.01, True) + + +class TestRankSums: + + np.random.seed(0) + x, y = np.random.rand(2, 10) + + @pytest.mark.parametrize('alternative', ['less', 'greater', 'two-sided']) + def test_ranksums_result_attributes(self, alternative): + # ranksums pval = mannwhitneyu pval w/out continuity or tie correction + res1 = stats.ranksums(self.x, self.y, + alternative=alternative).pvalue + res2 = stats.mannwhitneyu(self.x, self.y, use_continuity=False, + alternative=alternative).pvalue + assert_allclose(res1, res2) + + def test_ranksums_named_results(self): + res = stats.ranksums(self.x, self.y) + check_named_results(res, ('statistic', 'pvalue')) + + def test_input_validation(self): + with assert_raises(ValueError, match="`alternative` must be 'less'"): + stats.ranksums(self.x, self.y, alternative='foobar') + + +class TestJarqueBera: + def test_jarque_bera_stats(self): + np.random.seed(987654321) + x = np.random.normal(0, 1, 100000) + y = np.random.chisquare(10000, 100000) + z = np.random.rayleigh(1, 100000) + + assert_equal(stats.jarque_bera(x)[0], stats.jarque_bera(x).statistic) + assert_equal(stats.jarque_bera(x)[1], stats.jarque_bera(x).pvalue) + + assert_equal(stats.jarque_bera(y)[0], stats.jarque_bera(y).statistic) + assert_equal(stats.jarque_bera(y)[1], stats.jarque_bera(y).pvalue) + + assert_equal(stats.jarque_bera(z)[0], stats.jarque_bera(z).statistic) + assert_equal(stats.jarque_bera(z)[1], stats.jarque_bera(z).pvalue) + + assert_(stats.jarque_bera(x)[1] > stats.jarque_bera(y)[1]) + assert_(stats.jarque_bera(x).pvalue > stats.jarque_bera(y).pvalue) + + assert_(stats.jarque_bera(x)[1] > stats.jarque_bera(z)[1]) + assert_(stats.jarque_bera(x).pvalue > stats.jarque_bera(z).pvalue) + + assert_(stats.jarque_bera(y)[1] > stats.jarque_bera(z)[1]) + assert_(stats.jarque_bera(y).pvalue > stats.jarque_bera(z).pvalue) + + def test_jarque_bera_array_like(self): + np.random.seed(987654321) + x = np.random.normal(0, 1, 100000) + + jb_test1 = JB1, p1 = stats.jarque_bera(list(x)) + jb_test2 = JB2, p2 = stats.jarque_bera(tuple(x)) + jb_test3 = JB3, p3 = stats.jarque_bera(x.reshape(2, 50000)) + + assert JB1 == JB2 == JB3 == jb_test1.statistic == jb_test2.statistic == jb_test3.statistic # noqa: E501 + assert p1 == p2 == p3 == jb_test1.pvalue == jb_test2.pvalue == jb_test3.pvalue + + def test_jarque_bera_size(self): + assert_raises(ValueError, stats.jarque_bera, []) + + def test_axis(self): + rng = np.random.RandomState(seed=122398129) + x = rng.random(size=(2, 45)) + + assert_equal(stats.jarque_bera(x, axis=None), + stats.jarque_bera(x.ravel())) + + res = stats.jarque_bera(x, axis=1) + s0, p0 = stats.jarque_bera(x[0, :]) + s1, p1 = stats.jarque_bera(x[1, :]) + assert_allclose(res.statistic, [s0, s1]) + assert_allclose(res.pvalue, [p0, p1]) + + resT = stats.jarque_bera(x.T, axis=0) + assert_allclose(res, resT) + + +def test_skewtest_too_few_samples(): + # Regression test for ticket #1492. + # skewtest requires at least 8 samples; 7 should raise a ValueError. + x = np.arange(7.0) + assert_raises(ValueError, stats.skewtest, x) + + +def test_kurtosistest_too_few_samples(): + # Regression test for ticket #1425. + # kurtosistest requires at least 5 samples; 4 should raise a ValueError. + x = np.arange(4.0) + assert_raises(ValueError, stats.kurtosistest, x) + + +class TestMannWhitneyU: + X = [19.8958398126694, 19.5452691647182, 19.0577309166425, 21.716543054589, + 20.3269502208702, 20.0009273294025, 19.3440043632957, 20.4216806548105, + 19.0649894736528, 18.7808043120398, 19.3680942943298, 19.4848044069953, + 20.7514611265663, 19.0894948874598, 19.4975522356628, 18.9971170734274, + 20.3239606288208, 20.6921298083835, 19.0724259532507, 18.9825187935021, + 19.5144462609601, 19.8256857844223, 20.5174677102032, 21.1122407995892, + 17.9490854922535, 18.2847521114727, 20.1072217648826, 18.6439891962179, + 20.4970638083542, 19.5567594734914] + + Y = [19.2790668029091, 16.993808441865, 18.5416338448258, 17.2634018833575, + 19.1577183624616, 18.5119655377495, 18.6068455037221, 18.8358343362655, + 19.0366413269742, 18.1135025515417, 19.2201873866958, 17.8344909022841, + 18.2894380745856, 18.6661374133922, 19.9688601693252, 16.0672254617636, + 19.00596360572, 19.201561539032, 19.0487501090183, 19.0847908674356] + + significant = 14 + + def test_mannwhitneyu_one_sided(self): + u1, p1 = stats.mannwhitneyu(self.X, self.Y, alternative='less') + u2, p2 = stats.mannwhitneyu(self.Y, self.X, alternative='greater') + u3, p3 = stats.mannwhitneyu(self.X, self.Y, alternative='greater') + u4, p4 = stats.mannwhitneyu(self.Y, self.X, alternative='less') + + assert_equal(p1, p2) + assert_equal(p3, p4) + assert_(p1 != p3) + assert_equal(u1, 498) + assert_equal(u2, 102) + assert_equal(u3, 498) + assert_equal(u4, 102) + assert_approx_equal(p1, 0.999957683256589, significant=self.significant) + assert_approx_equal(p3, 4.5941632666275e-05, significant=self.significant) + + def test_mannwhitneyu_two_sided(self): + u1, p1 = stats.mannwhitneyu(self.X, self.Y, alternative='two-sided') + u2, p2 = stats.mannwhitneyu(self.Y, self.X, alternative='two-sided') + + assert_equal(p1, p2) + assert_equal(u1, 498) + assert_equal(u2, 102) + assert_approx_equal(p1, 9.188326533255e-05, + significant=self.significant) + + def test_mannwhitneyu_no_correct_one_sided(self): + u1, p1 = stats.mannwhitneyu(self.X, self.Y, False, + alternative='less') + u2, p2 = stats.mannwhitneyu(self.Y, self.X, False, + alternative='greater') + u3, p3 = stats.mannwhitneyu(self.X, self.Y, False, + alternative='greater') + u4, p4 = stats.mannwhitneyu(self.Y, self.X, False, + alternative='less') + + assert_equal(p1, p2) + assert_equal(p3, p4) + assert_(p1 != p3) + assert_equal(u1, 498) + assert_equal(u2, 102) + assert_equal(u3, 498) + assert_equal(u4, 102) + assert_approx_equal(p1, 0.999955905990004, significant=self.significant) + assert_approx_equal(p3, 4.40940099958089e-05, significant=self.significant) + + def test_mannwhitneyu_no_correct_two_sided(self): + u1, p1 = stats.mannwhitneyu(self.X, self.Y, False, + alternative='two-sided') + u2, p2 = stats.mannwhitneyu(self.Y, self.X, False, + alternative='two-sided') + + assert_equal(p1, p2) + assert_equal(u1, 498) + assert_equal(u2, 102) + assert_approx_equal(p1, 8.81880199916178e-05, + significant=self.significant) + + def test_mannwhitneyu_ones(self): + # test for gh-1428 + x = np.array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 2., 1., 1., 1., 1., 2., 1., 1., 2., 1., 1., 2., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 3., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1.]) + + y = np.array([1., 1., 1., 1., 1., 1., 1., 2., 1., 2., 1., 1., 1., 1., + 2., 1., 1., 1., 2., 1., 1., 1., 1., 1., 2., 1., 1., 3., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 1., 2., 1., + 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., 2., + 2., 1., 1., 2., 1., 1., 2., 1., 2., 1., 1., 1., 1., 2., + 2., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 1., 2., 1., 1., 1., 1., 1., 2., 2., 2., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., + 2., 1., 1., 2., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1., + 1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 2., 1., 1., + 1., 1., 1., 1.]) + + # checked against R wilcox.test + assert_allclose(stats.mannwhitneyu(x, y, alternative='less'), + (16980.5, 2.8214327656317373e-005)) + # p-value from R, e.g. wilcox.test(x, y, alternative="g") + assert_allclose(stats.mannwhitneyu(x, y, alternative='greater'), + (16980.5, 0.9999719954296)) + assert_allclose(stats.mannwhitneyu(x, y, alternative='two-sided'), + (16980.5, 5.642865531266e-05)) + + def test_mannwhitneyu_result_attributes(self): + # test for namedtuple attribute results + attributes = ('statistic', 'pvalue') + res = stats.mannwhitneyu(self.X, self.Y, alternative="less") + check_named_results(res, attributes) + + +def test_pointbiserial(): + # same as mstats test except for the nan + # Test data: https://web.archive.org/web/20060504220742/https://support.sas.com/ctx/samples/index.jsp?sid=490&tab=output + x = [1,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,0, + 0,0,0,0,1] + y = [14.8,13.8,12.4,10.1,7.1,6.1,5.8,4.6,4.3,3.5,3.3,3.2,3.0, + 2.8,2.8,2.5,2.4,2.3,2.1,1.7,1.7,1.5,1.3,1.3,1.2,1.2,1.1, + 0.8,0.7,0.6,0.5,0.2,0.2,0.1] + assert_almost_equal(stats.pointbiserialr(x, y)[0], 0.36149, 5) + + # test for namedtuple attribute results + attributes = ('correlation', 'pvalue') + res = stats.pointbiserialr(x, y) + check_named_results(res, attributes) + assert_equal(res.correlation, res.statistic) + + +def test_obrientransform(): + # A couple tests calculated by hand. + x1 = np.array([0, 2, 4]) + t1 = stats.obrientransform(x1) + expected = [7, -2, 7] + assert_allclose(t1[0], expected) + + x2 = np.array([0, 3, 6, 9]) + t2 = stats.obrientransform(x2) + expected = np.array([30, 0, 0, 30]) + assert_allclose(t2[0], expected) + + # Test two arguments. + a, b = stats.obrientransform(x1, x2) + assert_equal(a, t1[0]) + assert_equal(b, t2[0]) + + # Test three arguments. + a, b, c = stats.obrientransform(x1, x2, x1) + assert_equal(a, t1[0]) + assert_equal(b, t2[0]) + assert_equal(c, t1[0]) + + # This is a regression test to check np.var replacement. + # The author of this test didn't separately verify the numbers. + x1 = np.arange(5) + result = np.array( + [[5.41666667, 1.04166667, -0.41666667, 1.04166667, 5.41666667], + [21.66666667, 4.16666667, -1.66666667, 4.16666667, 21.66666667]]) + assert_array_almost_equal(stats.obrientransform(x1, 2*x1), result, decimal=8) + + # Example from "O'Brien Test for Homogeneity of Variance" + # by Herve Abdi. + values = range(5, 11) + reps = np.array([5, 11, 9, 3, 2, 2]) + data = np.repeat(values, reps) + transformed_values = np.array([3.1828, 0.5591, 0.0344, + 1.6086, 5.2817, 11.0538]) + expected = np.repeat(transformed_values, reps) + result = stats.obrientransform(data) + assert_array_almost_equal(result[0], expected, decimal=4) + + +def check_equal_gmean(array_like, desired, axis=None, dtype=None, rtol=1e-7, + weights=None): + # Note this doesn't test when axis is not specified + x = stats.gmean(array_like, axis=axis, dtype=dtype, weights=weights) + assert_allclose(x, desired, rtol=rtol) + assert_equal(x.dtype, dtype) + + +def check_equal_hmean(array_like, desired, axis=None, dtype=None, rtol=1e-7, + weights=None): + x = stats.hmean(array_like, axis=axis, dtype=dtype, weights=weights) + assert_allclose(x, desired, rtol=rtol) + assert_equal(x.dtype, dtype) + + +def check_equal_pmean(array_like, exp, desired, axis=None, dtype=None, + rtol=1e-7, weights=None): + x = stats.pmean(array_like, exp, axis=axis, dtype=dtype, weights=weights) + assert_allclose(x, desired, rtol=rtol) + assert_equal(x.dtype, dtype) + + +class TestHarMean: + def test_0(self): + a = [1, 0, 2] + desired = 0 + check_equal_hmean(a, desired) + + def test_1d_list(self): + # Test a 1d list + a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] + desired = 34.1417152147 + check_equal_hmean(a, desired) + + a = [1, 2, 3, 4] + desired = 4. / (1. / 1 + 1. / 2 + 1. / 3 + 1. / 4) + check_equal_hmean(a, desired) + + def test_1d_array(self): + # Test a 1d array + a = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + desired = 34.1417152147 + check_equal_hmean(a, desired) + + def test_1d_array_with_zero(self): + a = np.array([1, 0]) + desired = 0.0 + assert_equal(stats.hmean(a), desired) + + def test_1d_array_with_negative_value(self): + a = np.array([1, 0, -1]) + assert_raises(ValueError, stats.hmean, a) + + # Note the next tests use axis=None as default, not axis=0 + def test_2d_list(self): + # Test a 2d list + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 38.6696271841 + check_equal_hmean(a, desired) + + def test_2d_array(self): + # Test a 2d array + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 38.6696271841 + check_equal_hmean(np.array(a), desired) + + def test_2d_axis0(self): + # Test a 2d list with axis=0 + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([22.88135593, 39.13043478, 52.90076336, 65.45454545]) + check_equal_hmean(a, desired, axis=0) + + def test_2d_axis0_with_zero(self): + a = [[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([22.88135593, 0.0, 52.90076336, 65.45454545]) + assert_allclose(stats.hmean(a, axis=0), desired) + + def test_2d_axis1(self): + # Test a 2d list with axis=1 + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([19.2, 63.03939962, 103.80078637]) + check_equal_hmean(a, desired, axis=1) + + def test_2d_axis1_with_zero(self): + a = [[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([0.0, 63.03939962, 103.80078637]) + assert_allclose(stats.hmean(a, axis=1), desired) + + def test_weights_1d_list(self): + # Desired result from: + # https://www.hackmath.net/en/math-problem/35871 + a = [2, 10, 6] + weights = [10, 5, 3] + desired = 3 + check_equal_hmean(a, desired, weights=weights, rtol=1e-5) + + def test_weights_2d_array_axis0(self): + # Desired result from: + # https://www.hackmath.net/en/math-problem/35871 + a = np.array([[2, 5], [10, 5], [6, 5]]) + weights = np.array([[10, 1], [5, 1], [3, 1]]) + desired = np.array([3, 5]) + check_equal_hmean(a, desired, axis=0, weights=weights, rtol=1e-5) + + def test_weights_2d_array_axis1(self): + # Desired result from: + # https://www.hackmath.net/en/math-problem/35871 + a = np.array([[2, 10, 6], [7, 7, 7]]) + weights = np.array([[10, 5, 3], [1, 1, 1]]) + desired = np.array([3, 7]) + check_equal_hmean(a, desired, axis=1, weights=weights, rtol=1e-5) + + def test_weights_masked_1d_array(self): + # Desired result from: + # https://www.hackmath.net/en/math-problem/35871 + a = np.array([2, 10, 6, 42]) + weights = np.ma.array([10, 5, 3, 42], mask=[0, 0, 0, 1]) + desired = 3 + check_equal_hmean(a, desired, weights=weights, rtol=1e-5) + + +class TestGeoMean: + def test_0(self): + a = [1, 0, 2] + desired = 0 + check_equal_gmean(a, desired) + + def test_1d_list(self): + # Test a 1d list + a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] + desired = 45.2872868812 + check_equal_gmean(a, desired) + + a = [1, 2, 3, 4] + desired = power(1 * 2 * 3 * 4, 1. / 4.) + check_equal_gmean(a, desired, rtol=1e-14) + + def test_1d_array(self): + # Test a 1d array + a = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + desired = 45.2872868812 + check_equal_gmean(a, desired) + + a = array([1, 2, 3, 4], float32) + desired = power(1 * 2 * 3 * 4, 1. / 4.) + check_equal_gmean(a, desired, dtype=float32) + + # Note the next tests use axis=None as default, not axis=0 + def test_2d_list(self): + # Test a 2d list + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 52.8885199 + check_equal_gmean(a, desired) + + def test_2d_array(self): + # Test a 2d array + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = 52.8885199 + check_equal_gmean(array(a), desired) + + def test_2d_axis0(self): + # Test a 2d list with axis=0 + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([35.56893304, 49.32424149, 61.3579244, 72.68482371]) + check_equal_gmean(a, desired, axis=0) + + a = array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) + desired = array([1, 2, 3, 4]) + check_equal_gmean(a, desired, axis=0, rtol=1e-14) + + def test_2d_axis1(self): + # Test a 2d list with axis=1 + a = [[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]] + desired = np.array([22.13363839, 64.02171746, 104.40086817]) + check_equal_gmean(a, desired, axis=1) + + a = array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) + v = power(1 * 2 * 3 * 4, 1. / 4.) + desired = array([v, v, v]) + check_equal_gmean(a, desired, axis=1, rtol=1e-14) + + def test_large_values(self): + a = array([1e100, 1e200, 1e300]) + desired = 1e200 + check_equal_gmean(a, desired, rtol=1e-13) + + def test_1d_list0(self): + # Test a 1d list with zero element + a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 0] + desired = 0.0 # due to exp(-inf)=0 + with np.errstate(all='ignore'): + check_equal_gmean(a, desired) + + def test_1d_array0(self): + # Test a 1d array with zero element + a = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 0]) + desired = 0.0 # due to exp(-inf)=0 + with np.errstate(divide='ignore'): + check_equal_gmean(a, desired) + + def test_1d_list_neg(self): + # Test a 1d list with negative element + a = [10, 20, 30, 40, 50, 60, 70, 80, 90, -1] + desired = np.nan # due to log(-1) = nan + with np.errstate(invalid='ignore'): + check_equal_gmean(a, desired) + + def test_weights_1d_list(self): + # Desired result from: + # https://www.dummies.com/education/math/business-statistics/how-to-find-the-weighted-geometric-mean-of-a-data-set/ + a = [1, 2, 3, 4, 5] + weights = [2, 5, 6, 4, 3] + desired = 2.77748 + check_equal_gmean(a, desired, weights=weights, rtol=1e-5) + + def test_weights_1d_array(self): + # Desired result from: + # https://www.dummies.com/education/math/business-statistics/how-to-find-the-weighted-geometric-mean-of-a-data-set/ + a = np.array([1, 2, 3, 4, 5]) + weights = np.array([2, 5, 6, 4, 3]) + desired = 2.77748 + check_equal_gmean(a, desired, weights=weights, rtol=1e-5) + + def test_weights_masked_1d_array(self): + # Desired result from: + # https://www.dummies.com/education/math/business-statistics/how-to-find-the-weighted-geometric-mean-of-a-data-set/ + a = np.array([1, 2, 3, 4, 5, 6]) + weights = np.ma.array([2, 5, 6, 4, 3, 5], mask=[0, 0, 0, 0, 0, 1]) + desired = 2.77748 + check_equal_gmean(a, desired, weights=weights, rtol=1e-5) + + +class TestPowMean: + + def pmean_reference(a, p): + return (np.sum(a**p) / a.size)**(1/p) + + def wpmean_reference(a, p, weights): + return (np.sum(weights * a**p) / np.sum(weights))**(1/p) + + def test_bad_exponent(self): + with pytest.raises(ValueError, match='Power mean only defined for'): + stats.pmean([1, 2, 3], [0]) + with pytest.raises(ValueError, match='Power mean only defined for'): + stats.pmean([1, 2, 3], np.array([0])) + + def test_1d_list(self): + a, p = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], 3.5 + desired = TestPowMean.pmean_reference(np.array(a), p) + check_equal_pmean(a, p, desired) + + a, p = [1, 2, 3, 4], 2 + desired = np.sqrt((1**2 + 2**2 + 3**2 + 4**2) / 4) + check_equal_pmean(a, p, desired) + + def test_1d_array(self): + a, p = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]), -2.5 + desired = TestPowMean.pmean_reference(a, p) + check_equal_pmean(a, p, desired) + + def test_1d_array_with_zero(self): + a, p = np.array([1, 0]), -1 + desired = 0.0 + assert_equal(stats.pmean(a, p), desired) + + def test_1d_array_with_negative_value(self): + a, p = np.array([1, 0, -1]), 1.23 + with pytest.raises(ValueError, match='Power mean only defined if all'): + stats.pmean(a, p) + + @pytest.mark.parametrize( + ("a", "p"), + [([[10, 20], [50, 60], [90, 100]], -0.5), + (np.array([[10, 20], [50, 60], [90, 100]]), 0.5)] + ) + def test_2d_axisnone(self, a, p): + desired = TestPowMean.pmean_reference(np.array(a), p) + check_equal_pmean(a, p, desired) + + @pytest.mark.parametrize( + ("a", "p"), + [([[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]], -0.5), + ([[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]], 0.5)] + ) + def test_2d_list_axis0(self, a, p): + desired = [ + TestPowMean.pmean_reference( + np.array([a[i][j] for i in range(len(a))]), p + ) + for j in range(len(a[0])) + ] + check_equal_pmean(a, p, desired, axis=0) + + @pytest.mark.parametrize( + ("a", "p"), + [([[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]], -0.5), + ([[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]], 0.5)] + ) + def test_2d_list_axis1(self, a, p): + desired = [TestPowMean.pmean_reference(np.array(a_), p) for a_ in a] + check_equal_pmean(a, p, desired, axis=1) + + def test_weights_1d_list(self): + a, p = [2, 10, 6], -1.23456789 + weights = [10, 5, 3] + desired = TestPowMean.wpmean_reference(np.array(a), p, weights) + check_equal_pmean(a, p, desired, weights=weights, rtol=1e-5) + + def test_weights_masked_1d_array(self): + a, p = np.array([2, 10, 6, 42]), 1 + weights = np.ma.array([10, 5, 3, 42], mask=[0, 0, 0, 1]) + desired = np.average(a, weights=weights) + check_equal_pmean(a, p, desired, weights=weights, rtol=1e-5) + + @pytest.mark.parametrize( + ("axis", "fun_name", "p"), + [(None, "wpmean_reference", 9.87654321), + (0, "gmean", 0), + (1, "hmean", -1)] + ) + def test_weights_2d_array(self, axis, fun_name, p): + if fun_name == 'wpmean_reference': + def fun(a, axis, weights): + return TestPowMean.wpmean_reference(a, p, weights) + else: + fun = getattr(stats, fun_name) + a = np.array([[2, 5], [10, 5], [6, 5]]) + weights = np.array([[10, 1], [5, 1], [3, 1]]) + desired = fun(a, axis=axis, weights=weights) + check_equal_pmean(a, p, desired, axis=axis, weights=weights, rtol=1e-5) + + +class TestGeometricStandardDeviation: + # must add 1 as `gstd` is only defined for positive values + array_1d = np.arange(2 * 3 * 4) + 1 + gstd_array_1d = 2.294407613602 + array_3d = array_1d.reshape(2, 3, 4) + + def test_1d_array(self): + gstd_actual = stats.gstd(self.array_1d) + assert_allclose(gstd_actual, self.gstd_array_1d) + + def test_1d_numeric_array_like_input(self): + gstd_actual = stats.gstd(tuple(self.array_1d)) + assert_allclose(gstd_actual, self.gstd_array_1d) + + def test_raises_value_error_non_array_like_input(self): + with pytest.raises(ValueError, match='Invalid array input'): + stats.gstd('This should fail as it can not be cast to an array.') + + def test_raises_value_error_zero_entry(self): + with pytest.raises(ValueError, match='Non positive value'): + stats.gstd(np.append(self.array_1d, [0])) + + def test_raises_value_error_negative_entry(self): + with pytest.raises(ValueError, match='Non positive value'): + stats.gstd(np.append(self.array_1d, [-1])) + + def test_raises_value_error_inf_entry(self): + with pytest.raises(ValueError, match='Infinite value'): + stats.gstd(np.append(self.array_1d, [np.inf])) + + def test_propagates_nan_values(self): + a = array([[1, 1, 1, 16], [np.nan, 1, 2, 3]]) + gstd_actual = stats.gstd(a, axis=1) + assert_allclose(gstd_actual, np.array([4, np.nan])) + + def test_ddof_equal_to_number_of_observations(self): + with pytest.raises(ValueError, match='Degrees of freedom <= 0'): + stats.gstd(self.array_1d, ddof=self.array_1d.size) + + def test_3d_array(self): + gstd_actual = stats.gstd(self.array_3d, axis=None) + assert_allclose(gstd_actual, self.gstd_array_1d) + + def test_3d_array_axis_type_tuple(self): + gstd_actual = stats.gstd(self.array_3d, axis=(1,2)) + assert_allclose(gstd_actual, [2.12939215, 1.22120169]) + + def test_3d_array_axis_0(self): + gstd_actual = stats.gstd(self.array_3d, axis=0) + gstd_desired = np.array([ + [6.1330555493918, 3.958900210120, 3.1206598248344, 2.6651441426902], + [2.3758135028411, 2.174581428192, 2.0260062829505, 1.9115518327308], + [1.8205343606803, 1.746342404566, 1.6846557065742, 1.6325269194382] + ]) + assert_allclose(gstd_actual, gstd_desired) + + def test_3d_array_axis_1(self): + gstd_actual = stats.gstd(self.array_3d, axis=1) + gstd_desired = np.array([ + [3.118993630946, 2.275985934063, 1.933995977619, 1.742896469724], + [1.271693593916, 1.254158641801, 1.238774141609, 1.225164057869] + ]) + assert_allclose(gstd_actual, gstd_desired) + + def test_3d_array_axis_2(self): + gstd_actual = stats.gstd(self.array_3d, axis=2) + gstd_desired = np.array([ + [1.8242475707664, 1.2243686572447, 1.1318311657788], + [1.0934830582351, 1.0724479791887, 1.0591498540749] + ]) + assert_allclose(gstd_actual, gstd_desired) + + def test_masked_3d_array(self): + ma = np.ma.masked_where(self.array_3d > 16, self.array_3d) + gstd_actual = stats.gstd(ma, axis=2) + gstd_desired = stats.gstd(self.array_3d, axis=2) + mask = [[0, 0, 0], [0, 1, 1]] + assert_allclose(gstd_actual, gstd_desired) + assert_equal(gstd_actual.mask, mask) + + +def test_binomtest(): + # precision tests compared to R for ticket:986 + pp = np.concatenate((np.linspace(0.1, 0.2, 5), + np.linspace(0.45, 0.65, 5), + np.linspace(0.85, 0.95, 5))) + n = 501 + x = 450 + results = [0.0, 0.0, 1.0159969301994141e-304, + 2.9752418572150531e-275, 7.7668382922535275e-250, + 2.3381250925167094e-099, 7.8284591587323951e-081, + 9.9155947819961383e-065, 2.8729390725176308e-050, + 1.7175066298388421e-037, 0.0021070691951093692, + 0.12044570587262322, 0.88154763174802508, 0.027120993063129286, + 2.6102587134694721e-006] + + for p, res in zip(pp, results): + assert_approx_equal(stats.binomtest(x, n, p).pvalue, res, + significant=12, err_msg='fail forp=%f' % p) + assert_approx_equal(stats.binomtest(50, 100, 0.1).pvalue, + 5.8320387857343647e-024, + significant=12) + + +def test_binomtest2(): + # test added for issue #2384 + res2 = [ + [1.0, 1.0], + [0.5, 1.0, 0.5], + [0.25, 1.00, 1.00, 0.25], + [0.125, 0.625, 1.000, 0.625, 0.125], + [0.0625, 0.3750, 1.0000, 1.0000, 0.3750, 0.0625], + [0.03125, 0.21875, 0.68750, 1.00000, 0.68750, 0.21875, 0.03125], + [0.015625, 0.125000, 0.453125, 1.000000, 1.000000, 0.453125, 0.125000, + 0.015625], + [0.0078125, 0.0703125, 0.2890625, 0.7265625, 1.0000000, 0.7265625, + 0.2890625, 0.0703125, 0.0078125], + [0.00390625, 0.03906250, 0.17968750, 0.50781250, 1.00000000, + 1.00000000, 0.50781250, 0.17968750, 0.03906250, 0.00390625], + [0.001953125, 0.021484375, 0.109375000, 0.343750000, 0.753906250, + 1.000000000, 0.753906250, 0.343750000, 0.109375000, 0.021484375, + 0.001953125] + ] + for k in range(1, 11): + res1 = [stats.binomtest(v, k, 0.5).pvalue for v in range(k + 1)] + assert_almost_equal(res1, res2[k-1], decimal=10) + + +def test_binomtest3(): + # test added for issue #2384 + # test when x == n*p and neighbors + res3 = [stats.binomtest(v, v*k, 1./k).pvalue + for v in range(1, 11) for k in range(2, 11)] + assert_equal(res3, np.ones(len(res3), int)) + + # > bt=c() + # > for(i in as.single(1:10)) { + # + for(k in as.single(2:10)) { + # + bt = c(bt, binom.test(i-1, k*i,(1/k))$p.value); + # + print(c(i+1, k*i,(1/k))) + # + } + # + } + binom_testm1 = np.array([ + 0.5, 0.5555555555555556, 0.578125, 0.5904000000000003, + 0.5981224279835393, 0.603430543396034, 0.607304096221924, + 0.610255656871054, 0.612579511000001, 0.625, 0.670781893004115, + 0.68853759765625, 0.6980101120000006, 0.703906431368616, + 0.70793209416498, 0.7108561134173507, 0.713076544331419, + 0.714820192935702, 0.6875, 0.7268709038256367, 0.7418963909149174, + 0.74986110468096, 0.7548015520398076, 0.7581671424768577, + 0.760607984787832, 0.762459425024199, 0.7639120677676575, 0.7265625, + 0.761553963657302, 0.774800934828818, 0.7818005980538996, + 0.78613491480358, 0.789084353140195, 0.7912217659828884, + 0.79284214559524, 0.794112956558801, 0.75390625, 0.7856929451142176, + 0.7976688481430754, 0.8039848974727624, 0.807891868948366, + 0.8105487660137676, 0.812473307174702, 0.8139318233591120, + 0.815075399104785, 0.7744140625, 0.8037322594985427, + 0.814742863657656, 0.8205425178645808, 0.8241275984172285, + 0.8265645374416, 0.8283292196088257, 0.829666291102775, + 0.8307144686362666, 0.7905273437499996, 0.8178712053954738, + 0.828116983756619, 0.833508948940494, 0.8368403871552892, + 0.839104213210105, 0.840743186196171, 0.84198481438049, + 0.8429580531563676, 0.803619384765625, 0.829338573944648, + 0.8389591907548646, 0.84401876783902, 0.84714369697889, + 0.8492667010581667, 0.850803474598719, 0.851967542858308, + 0.8528799045949524, 0.8145294189453126, 0.838881732845347, + 0.847979024541911, 0.852760894015685, 0.8557134656773457, + 0.8577190131799202, 0.85917058278431, 0.860270010472127, + 0.861131648404582, 0.823802947998047, 0.846984756807511, + 0.855635653643743, 0.860180994825685, 0.86298688573253, + 0.864892525675245, 0.866271647085603, 0.867316125625004, + 0.8681346531755114 + ]) + + # > bt=c() + # > for(i in as.single(1:10)) { + # + for(k in as.single(2:10)) { + # + bt = c(bt, binom.test(i+1, k*i,(1/k))$p.value); + # + print(c(i+1, k*i,(1/k))) + # + } + # + } + + binom_testp1 = np.array([ + 0.5, 0.259259259259259, 0.26171875, 0.26272, 0.2632244513031551, + 0.2635138663069203, 0.2636951804161073, 0.2638162407564354, + 0.2639010709000002, 0.625, 0.4074074074074074, 0.42156982421875, + 0.4295746560000003, 0.43473045988554, 0.4383309503172684, + 0.4409884859402103, 0.4430309389962837, 0.444649849401104, 0.6875, + 0.4927602499618962, 0.5096031427383425, 0.5189636628480, + 0.5249280070771274, 0.5290623300865124, 0.5320974248125793, + 0.5344204730474308, 0.536255847400756, 0.7265625, 0.5496019313526808, + 0.5669248746708034, 0.576436455045805, 0.5824538812831795, + 0.5866053321547824, 0.589642781414643, 0.5919618019300193, + 0.593790427805202, 0.75390625, 0.590868349763505, 0.607983393277209, + 0.617303847446822, 0.623172512167948, 0.627208862156123, + 0.6301556891501057, 0.632401894928977, 0.6341708982290303, + 0.7744140625, 0.622562037497196, 0.639236102912278, 0.648263335014579, + 0.65392850011132, 0.657816519817211, 0.660650782947676, + 0.662808780346311, 0.6645068560246006, 0.7905273437499996, + 0.6478843304312477, 0.6640468318879372, 0.6727589686071775, + 0.6782129857784873, 0.681950188903695, 0.684671508668418, + 0.686741824999918, 0.688369886732168, 0.803619384765625, + 0.668716055304315, 0.684360013879534, 0.6927642396829181, + 0.6980155964704895, 0.701609591890657, 0.7042244320992127, + 0.7062125081341817, 0.707775152962577, 0.8145294189453126, + 0.686243374488305, 0.7013873696358975, 0.709501223328243, + 0.714563595144314, 0.718024953392931, 0.7205416252126137, + 0.722454130389843, 0.723956813292035, 0.823802947998047, + 0.701255953767043, 0.715928221686075, 0.723772209289768, + 0.7286603031173616, 0.7319999279787631, 0.7344267920995765, + 0.736270323773157, 0.737718376096348 + ]) + + res4_p1 = [stats.binomtest(v+1, v*k, 1./k).pvalue + for v in range(1, 11) for k in range(2, 11)] + res4_m1 = [stats.binomtest(v-1, v*k, 1./k).pvalue + for v in range(1, 11) for k in range(2, 11)] + + assert_almost_equal(res4_p1, binom_testp1, decimal=13) + assert_almost_equal(res4_m1, binom_testm1, decimal=13) + + +class TestTrim: + # test trim functions + def test_trim1(self): + a = np.arange(11) + assert_equal(np.sort(stats.trim1(a, 0.1)), np.arange(10)) + assert_equal(np.sort(stats.trim1(a, 0.2)), np.arange(9)) + assert_equal(np.sort(stats.trim1(a, 0.2, tail='left')), + np.arange(2, 11)) + assert_equal(np.sort(stats.trim1(a, 3/11., tail='left')), + np.arange(3, 11)) + assert_equal(stats.trim1(a, 1.0), []) + assert_equal(stats.trim1(a, 1.0, tail='left'), []) + + # empty input + assert_equal(stats.trim1([], 0.1), []) + assert_equal(stats.trim1([], 3/11., tail='left'), []) + assert_equal(stats.trim1([], 4/6.), []) + + # test axis + a = np.arange(24).reshape(6, 4) + ref = np.arange(4, 24).reshape(5, 4) # first row trimmed + + axis = 0 + trimmed = stats.trim1(a, 0.2, tail='left', axis=axis) + assert_equal(np.sort(trimmed, axis=axis), ref) + + axis = 1 + trimmed = stats.trim1(a.T, 0.2, tail='left', axis=axis) + assert_equal(np.sort(trimmed, axis=axis), ref.T) + + def test_trimboth(self): + a = np.arange(11) + assert_equal(np.sort(stats.trimboth(a, 3/11.)), np.arange(3, 8)) + assert_equal(np.sort(stats.trimboth(a, 0.2)), + np.array([2, 3, 4, 5, 6, 7, 8])) + assert_equal(np.sort(stats.trimboth(np.arange(24).reshape(6, 4), 0.2)), + np.arange(4, 20).reshape(4, 4)) + assert_equal(np.sort(stats.trimboth(np.arange(24).reshape(4, 6).T, + 2/6.)), + np.array([[2, 8, 14, 20], [3, 9, 15, 21]])) + assert_raises(ValueError, stats.trimboth, + np.arange(24).reshape(4, 6).T, 4/6.) + + # empty input + assert_equal(stats.trimboth([], 0.1), []) + assert_equal(stats.trimboth([], 3/11.), []) + assert_equal(stats.trimboth([], 4/6.), []) + + def test_trim_mean(self): + # don't use pre-sorted arrays + a = np.array([4, 8, 2, 0, 9, 5, 10, 1, 7, 3, 6]) + idx = np.array([3, 5, 0, 1, 2, 4]) + a2 = np.arange(24).reshape(6, 4)[idx, :] + a3 = np.arange(24).reshape(6, 4, order='F')[idx, :] + assert_equal(stats.trim_mean(a3, 2/6.), + np.array([2.5, 8.5, 14.5, 20.5])) + assert_equal(stats.trim_mean(a2, 2/6.), + np.array([10., 11., 12., 13.])) + idx4 = np.array([1, 0, 3, 2]) + a4 = np.arange(24).reshape(4, 6)[idx4, :] + assert_equal(stats.trim_mean(a4, 2/6.), + np.array([9., 10., 11., 12., 13., 14.])) + # shuffled arange(24) as array_like + a = [7, 11, 12, 21, 16, 6, 22, 1, 5, 0, 18, 10, 17, 9, 19, 15, 23, + 20, 2, 14, 4, 13, 8, 3] + assert_equal(stats.trim_mean(a, 2/6.), 11.5) + assert_equal(stats.trim_mean([5,4,3,1,2,0], 2/6.), 2.5) + + # check axis argument + np.random.seed(1234) + a = np.random.randint(20, size=(5, 6, 4, 7)) + for axis in [0, 1, 2, 3, -1]: + res1 = stats.trim_mean(a, 2/6., axis=axis) + res2 = stats.trim_mean(np.moveaxis(a, axis, 0), 2/6.) + assert_equal(res1, res2) + + res1 = stats.trim_mean(a, 2/6., axis=None) + res2 = stats.trim_mean(a.ravel(), 2/6.) + assert_equal(res1, res2) + + assert_raises(ValueError, stats.trim_mean, a, 0.6) + + # empty input + assert_equal(stats.trim_mean([], 0.0), np.nan) + assert_equal(stats.trim_mean([], 0.6), np.nan) + + +class TestSigmaClip: + def test_sigmaclip1(self): + a = np.concatenate((np.linspace(9.5, 10.5, 31), np.linspace(0, 20, 5))) + fact = 4 # default + c, low, upp = stats.sigmaclip(a) + assert_(c.min() > low) + assert_(c.max() < upp) + assert_equal(low, c.mean() - fact*c.std()) + assert_equal(upp, c.mean() + fact*c.std()) + assert_equal(c.size, a.size) + + def test_sigmaclip2(self): + a = np.concatenate((np.linspace(9.5, 10.5, 31), np.linspace(0, 20, 5))) + fact = 1.5 + c, low, upp = stats.sigmaclip(a, fact, fact) + assert_(c.min() > low) + assert_(c.max() < upp) + assert_equal(low, c.mean() - fact*c.std()) + assert_equal(upp, c.mean() + fact*c.std()) + assert_equal(c.size, 4) + assert_equal(a.size, 36) # check original array unchanged + + def test_sigmaclip3(self): + a = np.concatenate((np.linspace(9.5, 10.5, 11), + np.linspace(-100, -50, 3))) + fact = 1.8 + c, low, upp = stats.sigmaclip(a, fact, fact) + assert_(c.min() > low) + assert_(c.max() < upp) + assert_equal(low, c.mean() - fact*c.std()) + assert_equal(upp, c.mean() + fact*c.std()) + assert_equal(c, np.linspace(9.5, 10.5, 11)) + + def test_sigmaclip_result_attributes(self): + a = np.concatenate((np.linspace(9.5, 10.5, 11), + np.linspace(-100, -50, 3))) + fact = 1.8 + res = stats.sigmaclip(a, fact, fact) + attributes = ('clipped', 'lower', 'upper') + check_named_results(res, attributes) + + def test_std_zero(self): + # regression test #8632 + x = np.ones(10) + assert_equal(stats.sigmaclip(x)[0], x) + + +class TestAlexanderGovern: + def test_compare_dtypes(self): + args = [[13, 13, 13, 13, 13, 13, 13, 12, 12], + [14, 13, 12, 12, 12, 12, 12, 11, 11], + [14, 14, 13, 13, 13, 13, 13, 12, 12], + [15, 14, 13, 13, 13, 12, 12, 12, 11]] + args_int16 = np.array(args, dtype=np.int16) + args_int32 = np.array(args, dtype=np.int32) + args_uint8 = np.array(args, dtype=np.uint8) + args_float64 = np.array(args, dtype=np.float64) + + res_int16 = stats.alexandergovern(*args_int16) + res_int32 = stats.alexandergovern(*args_int32) + res_unit8 = stats.alexandergovern(*args_uint8) + res_float64 = stats.alexandergovern(*args_float64) + + assert (res_int16.pvalue == res_int32.pvalue == + res_unit8.pvalue == res_float64.pvalue) + assert (res_int16.statistic == res_int32.statistic == + res_unit8.statistic == res_float64.statistic) + + def test_bad_inputs(self): + # input array is of size zero + with assert_raises(ValueError, match="Input sample size must be" + " greater than one."): + stats.alexandergovern([1, 2], []) + # input is a singular non list element + with assert_raises(ValueError, match="Input sample size must be" + " greater than one."): + stats.alexandergovern([1, 2], 2) + # input list is of size 1 + with assert_raises(ValueError, match="Input sample size must be" + " greater than one."): + stats.alexandergovern([1, 2], [2]) + # inputs are not finite (infinity) + with assert_raises(ValueError, match="Input samples must be finite."): + stats.alexandergovern([1, 2], [np.inf, np.inf]) + + def test_compare_r(self): + ''' + Data generated in R with + > set.seed(1) + > library("onewaytests") + > library("tibble") + > y <- c(rnorm(40, sd=10), + + rnorm(30, sd=15), + + rnorm(20, sd=20)) + > x <- c(rep("one", times=40), + + rep("two", times=30), + + rep("eight", times=20)) + > x <- factor(x) + > ag.test(y ~ x, tibble(y,x)) + + Alexander-Govern Test (alpha = 0.05) + ------------------------------------------------------------- + data : y and x + + statistic : 1.359941 + parameter : 2 + p.value : 0.5066321 + + Result : Difference is not statistically significant. + ------------------------------------------------------------- + Example adapted from: + https://eval-serv2.metpsy.uni-jena.de/wiki-metheval-hp/index.php/R_FUN_Alexander-Govern + + ''' + one = [-6.264538107423324, 1.8364332422208225, -8.356286124100471, + 15.952808021377916, 3.295077718153605, -8.204683841180152, + 4.874290524284853, 7.383247051292173, 5.757813516534923, + -3.0538838715635603, 15.11781168450848, 3.898432364114311, + -6.2124058054180376, -22.146998871774997, 11.249309181431082, + -0.4493360901523085, -0.16190263098946087, 9.438362106852992, + 8.212211950980885, 5.939013212175088, 9.189773716082183, + 7.821363007310671, 0.745649833651906, -19.89351695863373, + 6.198257478947102, -0.5612873952900078, -1.557955067053293, + -14.707523838992744, -4.781500551086204, 4.179415601997024, + 13.58679551529044, -1.0278772734299553, 3.876716115593691, + -0.5380504058290512, -13.770595568286065, -4.149945632996798, + -3.942899537103493, -0.5931339671118566, 11.000253719838831, + 7.631757484575442] + + two = [-2.4678539438038034, -3.8004252020476135, 10.454450631071062, + 8.34994798010486, -10.331335418242798, -10.612427354431794, + 5.468729432052455, 11.527993867731237, -1.6851931822534207, + 13.216615896813222, 5.971588205506021, -9.180395898761569, + 5.116795371366372, -16.94044644121189, 21.495355525515556, + 29.7059984775879, -5.508322146997636, -15.662019394747961, + 8.545794411636193, -2.0258190582123654, 36.024266407571645, + -0.5886000409975387, 10.346090436761651, 0.4200323817099909, + -11.14909813323608, 2.8318844927151434, -27.074379433365568, + 21.98332292344329, 2.2988000731784655, 32.58917505543229] + + eight = [9.510190577993251, -14.198928618436291, 12.214527069781099, + -18.68195263288503, -25.07266800478204, 5.828924710349257, + -8.86583746436866, 0.02210703263248262, 1.4868264830332811, + -11.79041892376144, -11.37337465637004, -2.7035723024766414, + 23.56173993146409, -30.47133600859524, 11.878923752568431, + 6.659007424270365, 21.261996745527256, -6.083678472686013, + 7.400376198325763, 5.341975815444621] + soln = stats.alexandergovern(one, two, eight) + assert_allclose(soln.statistic, 1.3599405447999450836) + assert_allclose(soln.pvalue, 0.50663205309676440091) + + def test_compare_scholar(self): + ''' + Data taken from 'The Modification and Evaluation of the + Alexander-Govern Test in Terms of Power' by Kingsley Ochuko, T., + Abdullah, S., Binti Zain, Z., & Soaad Syed Yahaya, S. (2015). + ''' + young = [482.43, 484.36, 488.84, 495.15, 495.24, 502.69, 504.62, + 518.29, 519.1, 524.1, 524.12, 531.18, 548.42, 572.1, 584.68, + 609.09, 609.53, 666.63, 676.4] + middle = [335.59, 338.43, 353.54, 404.27, 437.5, 469.01, 485.85, + 487.3, 493.08, 494.31, 499.1, 886.41] + old = [519.01, 528.5, 530.23, 536.03, 538.56, 538.83, 557.24, 558.61, + 558.95, 565.43, 586.39, 594.69, 629.22, 645.69, 691.84] + soln = stats.alexandergovern(young, middle, old) + assert_allclose(soln.statistic, 5.3237, atol=1e-3) + assert_allclose(soln.pvalue, 0.06982, atol=1e-4) + + # verify with ag.test in r + ''' + > library("onewaytests") + > library("tibble") + > young <- c(482.43, 484.36, 488.84, 495.15, 495.24, 502.69, 504.62, + + 518.29, 519.1, 524.1, 524.12, 531.18, 548.42, 572.1, + + 584.68, 609.09, 609.53, 666.63, 676.4) + > middle <- c(335.59, 338.43, 353.54, 404.27, 437.5, 469.01, 485.85, + + 487.3, 493.08, 494.31, 499.1, 886.41) + > old <- c(519.01, 528.5, 530.23, 536.03, 538.56, 538.83, 557.24, + + 558.61, 558.95, 565.43, 586.39, 594.69, 629.22, + + 645.69, 691.84) + > young_fct <- c(rep("young", times=19)) + > middle_fct <-c(rep("middle", times=12)) + > old_fct <- c(rep("old", times=15)) + > ag.test(a ~ b, tibble(a=c(young, middle, old), b=factor(c(young_fct, + + middle_fct, old_fct)))) + + Alexander-Govern Test (alpha = 0.05) + ------------------------------------------------------------- + data : a and b + + statistic : 5.324629 + parameter : 2 + p.value : 0.06978651 + + Result : Difference is not statistically significant. + ------------------------------------------------------------- + + ''' + assert_allclose(soln.statistic, 5.324629) + assert_allclose(soln.pvalue, 0.06978651) + + def test_compare_scholar3(self): + ''' + Data taken from 'Robustness And Comparative Power Of WelchAspin, + Alexander-Govern And Yuen Tests Under Non-Normality And Variance + Heteroscedasticity', by Ayed A. Almoied. 2017. Page 34-37. + https://digitalcommons.wayne.edu/cgi/viewcontent.cgi?article=2775&context=oa_dissertations + ''' + x1 = [-1.77559, -1.4113, -0.69457, -0.54148, -0.18808, -0.07152, + 0.04696, 0.051183, 0.148695, 0.168052, 0.422561, 0.458555, + 0.616123, 0.709968, 0.839956, 0.857226, 0.929159, 0.981442, + 0.999554, 1.642958] + x2 = [-1.47973, -1.2722, -0.91914, -0.80916, -0.75977, -0.72253, + -0.3601, -0.33273, -0.28859, -0.09637, -0.08969, -0.01824, + 0.260131, 0.289278, 0.518254, 0.683003, 0.877618, 1.172475, + 1.33964, 1.576766] + soln = stats.alexandergovern(x1, x2) + assert_allclose(soln.statistic, 0.713526, atol=1e-5) + assert_allclose(soln.pvalue, 0.398276, atol=1e-5) + + ''' + tested in ag.test in R: + > library("onewaytests") + > library("tibble") + > x1 <- c(-1.77559, -1.4113, -0.69457, -0.54148, -0.18808, -0.07152, + + 0.04696, 0.051183, 0.148695, 0.168052, 0.422561, 0.458555, + + 0.616123, 0.709968, 0.839956, 0.857226, 0.929159, 0.981442, + + 0.999554, 1.642958) + > x2 <- c(-1.47973, -1.2722, -0.91914, -0.80916, -0.75977, -0.72253, + + -0.3601, -0.33273, -0.28859, -0.09637, -0.08969, -0.01824, + + 0.260131, 0.289278, 0.518254, 0.683003, 0.877618, 1.172475, + + 1.33964, 1.576766) + > x1_fact <- c(rep("x1", times=20)) + > x2_fact <- c(rep("x2", times=20)) + > a <- c(x1, x2) + > b <- factor(c(x1_fact, x2_fact)) + > ag.test(a ~ b, tibble(a, b)) + Alexander-Govern Test (alpha = 0.05) + ------------------------------------------------------------- + data : a and b + + statistic : 0.7135182 + parameter : 1 + p.value : 0.3982783 + + Result : Difference is not statistically significant. + ------------------------------------------------------------- + ''' + assert_allclose(soln.statistic, 0.7135182) + assert_allclose(soln.pvalue, 0.3982783) + + def test_nan_policy_propogate(self): + args = [[1, 2, 3, 4], [1, np.nan]] + # default nan_policy is 'propagate' + res = stats.alexandergovern(*args) + assert_equal(res.pvalue, np.nan) + assert_equal(res.statistic, np.nan) + + def test_nan_policy_raise(self): + args = [[1, 2, 3, 4], [1, np.nan]] + with assert_raises(ValueError, match="The input contains nan values"): + stats.alexandergovern(*args, nan_policy='raise') + + def test_nan_policy_omit(self): + args_nan = [[1, 2, 3, np.nan, 4], [1, np.nan, 19, 25]] + args_no_nan = [[1, 2, 3, 4], [1, 19, 25]] + res_nan = stats.alexandergovern(*args_nan, nan_policy='omit') + res_no_nan = stats.alexandergovern(*args_no_nan) + assert_equal(res_nan.pvalue, res_no_nan.pvalue) + assert_equal(res_nan.statistic, res_no_nan.statistic) + + def test_constant_input(self): + # Zero variance input, consistent with `stats.pearsonr` + msg = "An input array is constant; the statistic is not defined." + with pytest.warns(stats.ConstantInputWarning, match=msg): + res = stats.alexandergovern([0.667, 0.667, 0.667], + [0.123, 0.456, 0.789]) + assert_equal(res.statistic, np.nan) + assert_equal(res.pvalue, np.nan) + + +class TestFOneWay: + + def test_trivial(self): + # A trivial test of stats.f_oneway, with F=0. + F, p = stats.f_oneway([0, 2], [0, 2]) + assert_equal(F, 0.0) + assert_equal(p, 1.0) + + def test_basic(self): + # Despite being a floating point calculation, this data should + # result in F being exactly 2.0. + F, p = stats.f_oneway([0, 2], [2, 4]) + assert_equal(F, 2.0) + assert_allclose(p, 1 - np.sqrt(0.5), rtol=1e-14) + + def test_known_exact(self): + # Another trivial dataset for which the exact F and p can be + # calculated. + F, p = stats.f_oneway([2], [2], [2, 3, 4]) + # The use of assert_equal might be too optimistic, but the calculation + # in this case is trivial enough that it is likely to go through with + # no loss of precision. + assert_equal(F, 3/5) + assert_equal(p, 5/8) + + def test_large_integer_array(self): + a = np.array([655, 788], dtype=np.uint16) + b = np.array([789, 772], dtype=np.uint16) + F, p = stats.f_oneway(a, b) + # The expected value was verified by computing it with mpmath with + # 40 digits of precision. + assert_allclose(F, 0.77450216931805540, rtol=1e-14) + + def test_result_attributes(self): + a = np.array([655, 788], dtype=np.uint16) + b = np.array([789, 772], dtype=np.uint16) + res = stats.f_oneway(a, b) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_nist(self): + # These are the nist ANOVA files. They can be found at: + # https://www.itl.nist.gov/div898/strd/anova/anova.html + filenames = ['SiRstv.dat', 'SmLs01.dat', 'SmLs02.dat', 'SmLs03.dat', + 'AtmWtAg.dat', 'SmLs04.dat', 'SmLs05.dat', 'SmLs06.dat', + 'SmLs07.dat', 'SmLs08.dat', 'SmLs09.dat'] + + for test_case in filenames: + rtol = 1e-7 + fname = os.path.abspath(os.path.join(os.path.dirname(__file__), + 'data/nist_anova', test_case)) + with open(fname) as f: + content = f.read().split('\n') + certified = [line.split() for line in content[40:48] + if line.strip()] + dataf = np.loadtxt(fname, skiprows=60) + y, x = dataf.T + y = y.astype(int) + caty = np.unique(y) + f = float(certified[0][-1]) + + xlist = [x[y == i] for i in caty] + res = stats.f_oneway(*xlist) + + # With the hard test cases we relax the tolerance a bit. + hard_tc = ('SmLs07.dat', 'SmLs08.dat', 'SmLs09.dat') + if test_case in hard_tc: + rtol = 1e-4 + + assert_allclose(res[0], f, rtol=rtol, + err_msg='Failing testcase: %s' % test_case) + + @pytest.mark.parametrize("a, b, expected", [ + (np.array([42, 42, 42]), np.array([7, 7, 7]), (np.inf, 0)), + (np.array([42, 42, 42]), np.array([42, 42, 42]), (np.nan, np.nan)) + ]) + def test_constant_input(self, a, b, expected): + # For more details, look on https://github.com/scipy/scipy/issues/11669 + msg = "Each of the input arrays is constant;" + with pytest.warns(stats.ConstantInputWarning, match=msg): + f, p = stats.f_oneway(a, b) + assert f, p == expected + + @pytest.mark.parametrize('axis', [-2, -1, 0, 1]) + def test_2d_inputs(self, axis): + a = np.array([[1, 4, 3, 3], + [2, 5, 3, 3], + [3, 6, 3, 3], + [2, 3, 3, 3], + [1, 4, 3, 3]]) + b = np.array([[3, 1, 5, 3], + [4, 6, 5, 3], + [4, 3, 5, 3], + [1, 5, 5, 3], + [5, 5, 5, 3], + [2, 3, 5, 3], + [8, 2, 5, 3], + [2, 2, 5, 3]]) + c = np.array([[4, 3, 4, 3], + [4, 2, 4, 3], + [5, 4, 4, 3], + [5, 4, 4, 3]]) + + if axis in [-1, 1]: + a = a.T + b = b.T + c = c.T + take_axis = 0 + else: + take_axis = 1 + + warn_msg = "Each of the input arrays is constant;" + with pytest.warns(stats.ConstantInputWarning, match=warn_msg): + f, p = stats.f_oneway(a, b, c, axis=axis) + + # Verify that the result computed with the 2d arrays matches + # the result of calling f_oneway individually on each slice. + for j in [0, 1]: + fj, pj = stats.f_oneway(np.take(a, j, take_axis), + np.take(b, j, take_axis), + np.take(c, j, take_axis)) + assert_allclose(f[j], fj, rtol=1e-14) + assert_allclose(p[j], pj, rtol=1e-14) + for j in [2, 3]: + with pytest.warns(stats.ConstantInputWarning, match=warn_msg): + fj, pj = stats.f_oneway(np.take(a, j, take_axis), + np.take(b, j, take_axis), + np.take(c, j, take_axis)) + assert_equal(f[j], fj) + assert_equal(p[j], pj) + + def test_3d_inputs(self): + # Some 3-d arrays. (There is nothing special about the values.) + a = 1/np.arange(1.0, 4*5*7 + 1).reshape(4, 5, 7) + b = 2/np.arange(1.0, 4*8*7 + 1).reshape(4, 8, 7) + c = np.cos(1/np.arange(1.0, 4*4*7 + 1).reshape(4, 4, 7)) + + f, p = stats.f_oneway(a, b, c, axis=1) + + assert f.shape == (4, 7) + assert p.shape == (4, 7) + + for i in range(a.shape[0]): + for j in range(a.shape[2]): + fij, pij = stats.f_oneway(a[i, :, j], b[i, :, j], c[i, :, j]) + assert_allclose(fij, f[i, j]) + assert_allclose(pij, p[i, j]) + + def test_length0_1d_error(self): + # Require at least one value in each group. + msg = 'at least one input has length 0' + with pytest.warns(stats.DegenerateDataWarning, match=msg): + result = stats.f_oneway([1, 2, 3], [], [4, 5, 6, 7]) + assert_equal(result, (np.nan, np.nan)) + + def test_length0_2d_error(self): + msg = 'at least one input has length 0' + with pytest.warns(stats.DegenerateDataWarning, match=msg): + ncols = 3 + a = np.ones((4, ncols)) + b = np.ones((0, ncols)) + c = np.ones((5, ncols)) + f, p = stats.f_oneway(a, b, c) + nans = np.full((ncols,), fill_value=np.nan) + assert_equal(f, nans) + assert_equal(p, nans) + + def test_all_length_one(self): + msg = 'all input arrays have length 1.' + with pytest.warns(stats.DegenerateDataWarning, match=msg): + result = stats.f_oneway([10], [11], [12], [13]) + assert_equal(result, (np.nan, np.nan)) + + @pytest.mark.parametrize('args', [(), ([1, 2, 3],)]) + def test_too_few_inputs(self, args): + with assert_raises(TypeError): + stats.f_oneway(*args) + + def test_axis_error(self): + a = np.ones((3, 4)) + b = np.ones((5, 4)) + with assert_raises(AxisError): + stats.f_oneway(a, b, axis=2) + + def test_bad_shapes(self): + a = np.ones((3, 4)) + b = np.ones((5, 4)) + with assert_raises(ValueError): + stats.f_oneway(a, b, axis=1) + + +class TestKruskal: + def test_simple(self): + x = [1] + y = [2] + h, p = stats.kruskal(x, y) + assert_equal(h, 1.0) + assert_approx_equal(p, stats.distributions.chi2.sf(h, 1)) + h, p = stats.kruskal(np.array(x), np.array(y)) + assert_equal(h, 1.0) + assert_approx_equal(p, stats.distributions.chi2.sf(h, 1)) + + def test_basic(self): + x = [1, 3, 5, 7, 9] + y = [2, 4, 6, 8, 10] + h, p = stats.kruskal(x, y) + assert_approx_equal(h, 3./11, significant=10) + assert_approx_equal(p, stats.distributions.chi2.sf(3./11, 1)) + h, p = stats.kruskal(np.array(x), np.array(y)) + assert_approx_equal(h, 3./11, significant=10) + assert_approx_equal(p, stats.distributions.chi2.sf(3./11, 1)) + + def test_simple_tie(self): + x = [1] + y = [1, 2] + h_uncorr = 1.5**2 + 2*2.25**2 - 12 + corr = 0.75 + expected = h_uncorr / corr # 0.5 + h, p = stats.kruskal(x, y) + # Since the expression is simple and the exact answer is 0.5, it + # should be safe to use assert_equal(). + assert_equal(h, expected) + + def test_another_tie(self): + x = [1, 1, 1, 2] + y = [2, 2, 2, 2] + h_uncorr = (12. / 8. / 9.) * 4 * (3**2 + 6**2) - 3 * 9 + corr = 1 - float(3**3 - 3 + 5**3 - 5) / (8**3 - 8) + expected = h_uncorr / corr + h, p = stats.kruskal(x, y) + assert_approx_equal(h, expected) + + def test_three_groups(self): + # A test of stats.kruskal with three groups, with ties. + x = [1, 1, 1] + y = [2, 2, 2] + z = [2, 2] + h_uncorr = (12. / 8. / 9.) * (3*2**2 + 3*6**2 + 2*6**2) - 3 * 9 # 5.0 + corr = 1 - float(3**3 - 3 + 5**3 - 5) / (8**3 - 8) + expected = h_uncorr / corr # 7.0 + h, p = stats.kruskal(x, y, z) + assert_approx_equal(h, expected) + assert_approx_equal(p, stats.distributions.chi2.sf(h, 2)) + + def test_empty(self): + # A test of stats.kruskal with three groups, with ties. + x = [1, 1, 1] + y = [2, 2, 2] + z = [] + assert_equal(stats.kruskal(x, y, z), (np.nan, np.nan)) + + def test_kruskal_result_attributes(self): + x = [1, 3, 5, 7, 9] + y = [2, 4, 6, 8, 10] + res = stats.kruskal(x, y) + attributes = ('statistic', 'pvalue') + check_named_results(res, attributes) + + def test_nan_policy(self): + x = np.arange(10.) + x[9] = np.nan + assert_equal(stats.kruskal(x, x), (np.nan, np.nan)) + assert_almost_equal(stats.kruskal(x, x, nan_policy='omit'), (0.0, 1.0)) + assert_raises(ValueError, stats.kruskal, x, x, nan_policy='raise') + assert_raises(ValueError, stats.kruskal, x, x, nan_policy='foobar') + + def test_large_no_samples(self): + # Test to see if large samples are handled correctly. + n = 50000 + x = np.random.randn(n) + y = np.random.randn(n) + 50 + h, p = stats.kruskal(x, y) + expected = 0 + assert_approx_equal(p, expected) + + +class TestCombinePvalues: + + def test_fisher(self): + # Example taken from https://en.wikipedia.org/wiki/Fisher%27s_exact_test#Example + xsq, p = stats.combine_pvalues([.01, .2, .3], method='fisher') + assert_approx_equal(p, 0.02156, significant=4) + + def test_stouffer(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer') + assert_approx_equal(p, 0.01651, significant=4) + + def test_stouffer2(self): + Z, p = stats.combine_pvalues([.5, .5, .5], method='stouffer') + assert_approx_equal(p, 0.5, significant=4) + + def test_weighted_stouffer(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer', + weights=np.ones(3)) + assert_approx_equal(p, 0.01651, significant=4) + + def test_weighted_stouffer2(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer', + weights=np.array((1, 4, 9))) + assert_approx_equal(p, 0.1464, significant=4) + + def test_pearson(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='pearson') + assert_approx_equal(p, 0.02213, significant=4) + + def test_tippett(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='tippett') + assert_approx_equal(p, 0.0297, significant=4) + + def test_mudholkar_george(self): + Z, p = stats.combine_pvalues([.1, .1, .1], method='mudholkar_george') + assert_approx_equal(p, 0.019462, significant=4) + + def test_mudholkar_george_equal_fisher_pearson_average(self): + Z, p = stats.combine_pvalues([.01, .2, .3], method='mudholkar_george') + Z_f, p_f = stats.combine_pvalues([.01, .2, .3], method='fisher') + Z_p, p_p = stats.combine_pvalues([.01, .2, .3], method='pearson') + assert_approx_equal(0.5 * (Z_f+Z_p), Z, significant=4) + + methods = ["fisher", "pearson", "tippett", "stouffer", "mudholkar_george"] + + @pytest.mark.parametrize("variant", ["single", "all", "random"]) + @pytest.mark.parametrize("method", methods) + def test_monotonicity(self, variant, method): + # Test that result increases monotonically with respect to input. + m, n = 10, 7 + rng = np.random.default_rng(278448169958891062669391462690811630763) + + # `pvaluess` is an m × n array of p values. Each row corresponds to + # a set of p values to be combined with p values increasing + # monotonically down one column (single), simultaneously down each + # column (all), or independently down each column (random). + if variant == "single": + pvaluess = np.full((m, n), rng.random(n)) + pvaluess[:, 0] = np.linspace(0.1, 0.9, m) + elif variant == "all": + pvaluess = np.full((n, m), np.linspace(0.1, 0.9, m)).T + elif variant == "random": + pvaluess = np.sort(rng.uniform(0, 1, size=(m, n)), axis=0) + + combined_pvalues = [ + stats.combine_pvalues(pvalues, method=method)[1] + for pvalues in pvaluess + ] + assert np.all(np.diff(combined_pvalues) >= 0) + + @pytest.mark.parametrize("method", methods) + def test_result(self, method): + res = stats.combine_pvalues([.01, .2, .3], method=method) + assert_equal((res.statistic, res.pvalue), res) + + +class TestCdfDistanceValidation: + """ + Test that _cdf_distance() (via wasserstein_distance()) raises ValueErrors + for bad inputs. + """ + + def test_distinct_value_and_weight_lengths(self): + # When the number of weights does not match the number of values, + # a ValueError should be raised. + assert_raises(ValueError, stats.wasserstein_distance, + [1], [2], [4], [3, 1]) + assert_raises(ValueError, stats.wasserstein_distance, [1], [2], [1, 0]) + + def test_zero_weight(self): + # When a distribution is given zero weight, a ValueError should be + # raised. + assert_raises(ValueError, stats.wasserstein_distance, + [0, 1], [2], [0, 0]) + assert_raises(ValueError, stats.wasserstein_distance, + [0, 1], [2], [3, 1], [0]) + + def test_negative_weights(self): + # A ValueError should be raised if there are any negative weights. + assert_raises(ValueError, stats.wasserstein_distance, + [0, 1], [2, 2], [1, 1], [3, -1]) + + def test_empty_distribution(self): + # A ValueError should be raised when trying to measure the distance + # between something and nothing. + assert_raises(ValueError, stats.wasserstein_distance, [], [2, 2]) + assert_raises(ValueError, stats.wasserstein_distance, [1], []) + + def test_inf_weight(self): + # An inf weight is not valid. + assert_raises(ValueError, stats.wasserstein_distance, + [1, 2, 1], [1, 1], [1, np.inf, 1], [1, 1]) + + +class TestWassersteinDistanceND: + """ Tests for wasserstein_distance_nd() output values. + """ + + def test_published_values(self): + # Compare against published values and manually computed results. + # The values and computed result are posted at James D. McCaffrey's blog, + # https://jamesmccaffrey.wordpress.com/2018/03/05/earth-mover-distance + # -wasserstein-metric-example-calculation/ + u = [(1,1), (1,1), (1,1), (1,1), (1,1), (1,1), (1,1), (1,1), (1,1), (1,1), + (4,2), (6,1), (6,1)] + v = [(2,1), (2,1), (3,2), (3,2), (3,2), (5,1), (5,1), (5,1), (5,1), (5,1), + (5,1), (5,1), (7,1)] + + res = stats.wasserstein_distance_nd(u, v) + # In original post, the author kept two decimal places for ease of calculation. + # This test uses the more precise value of distance to get the precise results. + # For comparison, please see the table and figure in the original blog post. + flow = np.array([2., 3., 5., 1., 1., 1.]) + dist = np.array([1.00, 5**0.5, 4.00, 2**0.5, 1.00, 1.00]) + ref = np.sum(flow * dist)/np.sum(flow) + assert_allclose(res, ref) + + @pytest.mark.parametrize('n_value', (4, 15, 35)) + @pytest.mark.parametrize('ndim', (3, 4, 7)) + @pytest.mark.parametrize('max_repeats', (5, 10)) + def test_same_distribution_nD(self, ndim, n_value, max_repeats): + # Any distribution moved to itself should have a Wasserstein distance + # of zero. + rng = np.random.default_rng(363836384995579937222333) + repeats = rng.integers(1, max_repeats, size=n_value, dtype=int) + + u_values = rng.random(size=(n_value, ndim)) + v_values = np.repeat(u_values, repeats, axis=0) + v_weights = rng.random(np.sum(repeats)) + range_repeat = np.repeat(np.arange(len(repeats)), repeats) + u_weights = np.bincount(range_repeat, weights=v_weights) + index = rng.permutation(len(v_weights)) + v_values, v_weights = v_values[index], v_weights[index] + + res = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + assert_allclose(res, 0, atol=1e-15) + + @pytest.mark.parametrize('nu', (8, 9, 38)) + @pytest.mark.parametrize('nv', (8, 12, 17)) + @pytest.mark.parametrize('ndim', (3, 5, 23)) + def test_collapse_nD(self, nu, nv, ndim): + # test collapse for n dimensional values + # Collapsing a n-D distribution to a point distribution at zero + # is equivalent to taking the average of the norm of data. + rng = np.random.default_rng(38573488467338826109) + u_values = rng.random(size=(nu, ndim)) + v_values = np.zeros((nv, ndim)) + u_weights = rng.random(size=nu) + v_weights = rng.random(size=nv) + ref = np.average(np.linalg.norm(u_values, axis=1), weights=u_weights) + res = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + assert_allclose(res, ref) + + @pytest.mark.parametrize('nu', (8, 16, 32)) + @pytest.mark.parametrize('nv', (8, 16, 32)) + @pytest.mark.parametrize('ndim', (1, 2, 6)) + def test_zero_weight_nD(self, nu, nv, ndim): + # Values with zero weight have no impact on the Wasserstein distance. + rng = np.random.default_rng(38573488467338826109) + u_values = rng.random(size=(nu, ndim)) + v_values = rng.random(size=(nv, ndim)) + u_weights = rng.random(size=nu) + v_weights = rng.random(size=nv) + ref = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + + add_row, nrows = rng.integers(0, nu, size=2) + add_value = rng.random(size=(nrows, ndim)) + u_values = np.insert(u_values, add_row, add_value, axis=0) + u_weights = np.insert(u_weights, add_row, np.zeros(nrows), axis=0) + res = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + assert_allclose(res, ref) + + def test_inf_values(self): + # Inf values can lead to an inf distance or trigger a RuntimeWarning + # (and return NaN) if the distance is undefined. + uv, vv, uw = [[1, 1], [2, 1]], [[np.inf, -np.inf]], [1, 1] + distance = stats.wasserstein_distance_nd(uv, vv, uw) + assert_equal(distance, np.inf) + with np.errstate(invalid='ignore'): + uv, vv = [[np.inf, np.inf]], [[np.inf, -np.inf]] + distance = stats.wasserstein_distance_nd(uv, vv) + assert_equal(distance, np.nan) + + @pytest.mark.parametrize('nu', (10, 15, 20)) + @pytest.mark.parametrize('nv', (10, 15, 20)) + @pytest.mark.parametrize('ndim', (1, 3, 5)) + def test_multi_dim_nD(self, nu, nv, ndim): + # Adding dimension on distributions do not affect the result + rng = np.random.default_rng(2736495738494849509) + u_values = rng.random(size=(nu, ndim)) + v_values = rng.random(size=(nv, ndim)) + u_weights = rng.random(size=nu) + v_weights = rng.random(size=nv) + ref = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + + add_dim = rng.integers(0, ndim) + add_value = rng.random() + + u_values = np.insert(u_values, add_dim, add_value, axis=1) + v_values = np.insert(v_values, add_dim, add_value, axis=1) + res = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + assert_allclose(res, ref) + + @pytest.mark.parametrize('nu', (7, 13, 19)) + @pytest.mark.parametrize('nv', (7, 13, 19)) + @pytest.mark.parametrize('ndim', (2, 4, 7)) + def test_orthogonal_nD(self, nu, nv, ndim): + # orthogonal transformations do not affect the result of the + # wasserstein_distance + rng = np.random.default_rng(34746837464536) + u_values = rng.random(size=(nu, ndim)) + v_values = rng.random(size=(nv, ndim)) + u_weights = rng.random(size=nu) + v_weights = rng.random(size=nv) + ref = stats.wasserstein_distance_nd(u_values, v_values, u_weights, v_weights) + + dist = stats.ortho_group(ndim) + transform = dist.rvs(random_state=rng) + shift = rng.random(size=ndim) + res = stats.wasserstein_distance_nd(u_values @ transform + shift, + v_values @ transform + shift, + u_weights, v_weights) + assert_allclose(res, ref) + + def test_error_code(self): + rng = np.random.default_rng(52473644737485644836320101) + with pytest.raises(ValueError, match='Invalid input values. The inputs'): + u_values = rng.random(size=(4, 10, 15)) + v_values = rng.random(size=(6, 2, 7)) + _ = stats.wasserstein_distance_nd(u_values, v_values) + with pytest.raises(ValueError, match='Invalid input values. Dimensions'): + u_values = rng.random(size=(15,)) + v_values = rng.random(size=(3, 15)) + _ = stats.wasserstein_distance_nd(u_values, v_values) + with pytest.raises(ValueError, + match='Invalid input values. If two-dimensional'): + u_values = rng.random(size=(2, 10)) + v_values = rng.random(size=(2, 2)) + _ = stats.wasserstein_distance_nd(u_values, v_values) + + @pytest.mark.parametrize('u_size', [1, 10, 300]) + @pytest.mark.parametrize('v_size', [1, 10, 300]) + def test_optimization_vs_analytical(self, u_size, v_size): + rng = np.random.default_rng(45634745675) + # Test when u_weights = None, v_weights = None + u_values = rng.random(size=(u_size, 1)) + v_values = rng.random(size=(v_size, 1)) + u_values_flat = u_values.ravel() + v_values_flat = v_values.ravel() + # These three calculations are done using different backends + # but they must be equal + d1 = stats.wasserstein_distance(u_values_flat, v_values_flat) + d2 = stats.wasserstein_distance_nd(u_values, v_values) + d3 = stats.wasserstein_distance_nd(u_values_flat, v_values_flat) + assert_allclose(d2, d1) + assert_allclose(d3, d1) + # Test with u_weights and v_weights specified. + u_weights = rng.random(size=u_size) + v_weights = rng.random(size=v_size) + d1 = stats.wasserstein_distance(u_values_flat, v_values_flat, + u_weights, v_weights) + d2 = stats.wasserstein_distance_nd(u_values, v_values, + u_weights, v_weights) + d3 = stats.wasserstein_distance_nd(u_values_flat, v_values_flat, + u_weights, v_weights) + assert_allclose(d2, d1) + assert_allclose(d3, d1) + + +class TestWassersteinDistance: + """ Tests for wasserstein_distance() output values. + """ + + def test_simple(self): + # For basic distributions, the value of the Wasserstein distance is + # straightforward. + assert_allclose( + stats.wasserstein_distance([0, 1], [0], [1, 1], [1]), + .5) + assert_allclose(stats.wasserstein_distance( + [0, 1], [0], [3, 1], [1]), + .25) + assert_allclose(stats.wasserstein_distance( + [0, 2], [0], [1, 1], [1]), + 1) + assert_allclose(stats.wasserstein_distance( + [0, 1, 2], [1, 2, 3]), + 1) + + def test_same_distribution(self): + # Any distribution moved to itself should have a Wasserstein distance + # of zero. + assert_equal(stats.wasserstein_distance([1, 2, 3], [2, 1, 3]), 0) + assert_equal( + stats.wasserstein_distance([1, 1, 1, 4], [4, 1], + [1, 1, 1, 1], [1, 3]), + 0) + + def test_shift(self): + # If the whole distribution is shifted by x, then the Wasserstein + # distance should be the norm of x. + assert_allclose(stats.wasserstein_distance([0], [1]), 1) + assert_allclose(stats.wasserstein_distance([-5], [5]), 10) + assert_allclose( + stats.wasserstein_distance([1, 2, 3, 4, 5], [11, 12, 13, 14, 15]), + 10) + assert_allclose( + stats.wasserstein_distance([4.5, 6.7, 2.1], [4.6, 7, 9.2], + [3, 1, 1], [1, 3, 1]), + 2.5) + + def test_combine_weights(self): + # Assigning a weight w to a value is equivalent to including that value + # w times in the value array with weight of 1. + assert_allclose( + stats.wasserstein_distance( + [0, 0, 1, 1, 1, 1, 5], [0, 3, 3, 3, 3, 4, 4], + [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]), + stats.wasserstein_distance([5, 0, 1], [0, 4, 3], + [1, 2, 4], [1, 2, 4])) + + def test_collapse(self): + # Collapsing a distribution to a point distribution at zero is + # equivalent to taking the average of the absolute values of the + # values. + u = np.arange(-10, 30, 0.3) + v = np.zeros_like(u) + assert_allclose( + stats.wasserstein_distance(u, v), + np.mean(np.abs(u))) + + u_weights = np.arange(len(u)) + v_weights = u_weights[::-1] + assert_allclose( + stats.wasserstein_distance(u, v, u_weights, v_weights), + np.average(np.abs(u), weights=u_weights)) + + def test_zero_weight(self): + # Values with zero weight have no impact on the Wasserstein distance. + assert_allclose( + stats.wasserstein_distance([1, 2, 100000], [1, 1], + [1, 1, 0], [1, 1]), + stats.wasserstein_distance([1, 2], [1, 1], [1, 1], [1, 1])) + + def test_inf_values(self): + # Inf values can lead to an inf distance or trigger a RuntimeWarning + # (and return NaN) if the distance is undefined. + assert_equal( + stats.wasserstein_distance([1, 2, np.inf], [1, 1]), + np.inf) + assert_equal( + stats.wasserstein_distance([1, 2, np.inf], [-np.inf, 1]), + np.inf) + assert_equal( + stats.wasserstein_distance([1, -np.inf, np.inf], [1, 1]), + np.inf) + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "invalid value*") + assert_equal( + stats.wasserstein_distance([1, 2, np.inf], [np.inf, 1]), + np.nan) + + +class TestEnergyDistance: + """ Tests for energy_distance() output values. + """ + + def test_simple(self): + # For basic distributions, the value of the energy distance is + # straightforward. + assert_almost_equal( + stats.energy_distance([0, 1], [0], [1, 1], [1]), + np.sqrt(2) * .5) + assert_almost_equal(stats.energy_distance( + [0, 1], [0], [3, 1], [1]), + np.sqrt(2) * .25) + assert_almost_equal(stats.energy_distance( + [0, 2], [0], [1, 1], [1]), + 2 * .5) + assert_almost_equal( + stats.energy_distance([0, 1, 2], [1, 2, 3]), + np.sqrt(2) * (3*(1./3**2))**.5) + + def test_same_distribution(self): + # Any distribution moved to itself should have a energy distance of + # zero. + assert_equal(stats.energy_distance([1, 2, 3], [2, 1, 3]), 0) + assert_equal( + stats.energy_distance([1, 1, 1, 4], [4, 1], [1, 1, 1, 1], [1, 3]), + 0) + + def test_shift(self): + # If a single-point distribution is shifted by x, then the energy + # distance should be sqrt(2) * sqrt(x). + assert_almost_equal(stats.energy_distance([0], [1]), np.sqrt(2)) + assert_almost_equal( + stats.energy_distance([-5], [5]), + np.sqrt(2) * 10**.5) + + def test_combine_weights(self): + # Assigning a weight w to a value is equivalent to including that value + # w times in the value array with weight of 1. + assert_almost_equal( + stats.energy_distance([0, 0, 1, 1, 1, 1, 5], [0, 3, 3, 3, 3, 4, 4], + [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]), + stats.energy_distance([5, 0, 1], [0, 4, 3], [1, 2, 4], [1, 2, 4])) + + def test_zero_weight(self): + # Values with zero weight have no impact on the energy distance. + assert_almost_equal( + stats.energy_distance([1, 2, 100000], [1, 1], [1, 1, 0], [1, 1]), + stats.energy_distance([1, 2], [1, 1], [1, 1], [1, 1])) + + def test_inf_values(self): + # Inf values can lead to an inf distance or trigger a RuntimeWarning + # (and return NaN) if the distance is undefined. + assert_equal(stats.energy_distance([1, 2, np.inf], [1, 1]), np.inf) + assert_equal( + stats.energy_distance([1, 2, np.inf], [-np.inf, 1]), + np.inf) + assert_equal( + stats.energy_distance([1, -np.inf, np.inf], [1, 1]), + np.inf) + with suppress_warnings() as sup: + sup.record(RuntimeWarning, "invalid value*") + assert_equal( + stats.energy_distance([1, 2, np.inf], [np.inf, 1]), + np.nan) + + +class TestBrunnerMunzel: + # Data from (Lumley, 1996) + X = [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1] + Y = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4] + significant = 13 + + def test_brunnermunzel_one_sided(self): + # Results are compared with R's lawstat package. + u1, p1 = stats.brunnermunzel(self.X, self.Y, alternative='less') + u2, p2 = stats.brunnermunzel(self.Y, self.X, alternative='greater') + u3, p3 = stats.brunnermunzel(self.X, self.Y, alternative='greater') + u4, p4 = stats.brunnermunzel(self.Y, self.X, alternative='less') + + assert_approx_equal(p1, p2, significant=self.significant) + assert_approx_equal(p3, p4, significant=self.significant) + assert_(p1 != p3) + assert_approx_equal(u1, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u2, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(u3, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u4, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(p1, 0.0028931043330757342, + significant=self.significant) + assert_approx_equal(p3, 0.99710689566692423, + significant=self.significant) + + def test_brunnermunzel_two_sided(self): + # Results are compared with R's lawstat package. + u1, p1 = stats.brunnermunzel(self.X, self.Y, alternative='two-sided') + u2, p2 = stats.brunnermunzel(self.Y, self.X, alternative='two-sided') + + assert_approx_equal(p1, p2, significant=self.significant) + assert_approx_equal(u1, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u2, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(p1, 0.0057862086661515377, + significant=self.significant) + + def test_brunnermunzel_default(self): + # The default value for alternative is two-sided + u1, p1 = stats.brunnermunzel(self.X, self.Y) + u2, p2 = stats.brunnermunzel(self.Y, self.X) + + assert_approx_equal(p1, p2, significant=self.significant) + assert_approx_equal(u1, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u2, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(p1, 0.0057862086661515377, + significant=self.significant) + + def test_brunnermunzel_alternative_error(self): + alternative = "error" + distribution = "t" + nan_policy = "propagate" + assert_(alternative not in ["two-sided", "greater", "less"]) + assert_raises(ValueError, + stats.brunnermunzel, + self.X, + self.Y, + alternative, + distribution, + nan_policy) + + def test_brunnermunzel_distribution_norm(self): + u1, p1 = stats.brunnermunzel(self.X, self.Y, distribution="normal") + u2, p2 = stats.brunnermunzel(self.Y, self.X, distribution="normal") + assert_approx_equal(p1, p2, significant=self.significant) + assert_approx_equal(u1, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u2, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(p1, 0.0017041417600383024, + significant=self.significant) + + def test_brunnermunzel_distribution_error(self): + alternative = "two-sided" + distribution = "error" + nan_policy = "propagate" + assert_(alternative not in ["t", "normal"]) + assert_raises(ValueError, + stats.brunnermunzel, + self.X, + self.Y, + alternative, + distribution, + nan_policy) + + def test_brunnermunzel_empty_imput(self): + u1, p1 = stats.brunnermunzel(self.X, []) + u2, p2 = stats.brunnermunzel([], self.Y) + u3, p3 = stats.brunnermunzel([], []) + + assert_equal(u1, np.nan) + assert_equal(p1, np.nan) + assert_equal(u2, np.nan) + assert_equal(p2, np.nan) + assert_equal(u3, np.nan) + assert_equal(p3, np.nan) + + def test_brunnermunzel_nan_input_propagate(self): + X = [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, np.nan] + Y = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4] + u1, p1 = stats.brunnermunzel(X, Y, nan_policy="propagate") + u2, p2 = stats.brunnermunzel(Y, X, nan_policy="propagate") + + assert_equal(u1, np.nan) + assert_equal(p1, np.nan) + assert_equal(u2, np.nan) + assert_equal(p2, np.nan) + + def test_brunnermunzel_nan_input_raise(self): + X = [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, np.nan] + Y = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4] + alternative = "two-sided" + distribution = "t" + nan_policy = "raise" + + assert_raises(ValueError, + stats.brunnermunzel, + X, + Y, + alternative, + distribution, + nan_policy) + assert_raises(ValueError, + stats.brunnermunzel, + Y, + X, + alternative, + distribution, + nan_policy) + + def test_brunnermunzel_nan_input_omit(self): + X = [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, np.nan] + Y = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4] + u1, p1 = stats.brunnermunzel(X, Y, nan_policy="omit") + u2, p2 = stats.brunnermunzel(Y, X, nan_policy="omit") + + assert_approx_equal(p1, p2, significant=self.significant) + assert_approx_equal(u1, 3.1374674823029505, + significant=self.significant) + assert_approx_equal(u2, -3.1374674823029505, + significant=self.significant) + assert_approx_equal(p1, 0.0057862086661515377, + significant=self.significant) + + def test_brunnermunzel_return_nan(self): + """ tests that a warning is emitted when p is nan + p-value with t-distributions can be nan (0/0) (see gh-15843) + """ + x = [1, 2, 3] + y = [5, 6, 7, 8, 9] + + msg = "p-value cannot be estimated|divide by zero|invalid value encountered" + with pytest.warns(RuntimeWarning, match=msg): + stats.brunnermunzel(x, y, distribution="t") + + def test_brunnermunzel_normal_dist(self): + """ tests that a p is 0 for datasets that cause p->nan + when t-distribution is used (see gh-15843) + """ + x = [1, 2, 3] + y = [5, 6, 7, 8, 9] + + with pytest.warns(RuntimeWarning, match='divide by zero'): + _, p = stats.brunnermunzel(x, y, distribution="normal") + assert_equal(p, 0) + + +class TestRatioUniforms: + """ Tests for rvs_ratio_uniforms are in test_sampling.py, + as rvs_ratio_uniforms is deprecated and moved to stats.sampling """ + def test_consistency(self): + f = stats.norm.pdf + v = np.sqrt(f(np.sqrt(2))) * np.sqrt(2) + umax = np.sqrt(f(0)) + gen = stats.sampling.RatioUniforms(f, umax=umax, vmin=-v, vmax=v, + random_state=12345) + r1 = gen.rvs(10) + deprecation_msg = ("Please use `RatioUniforms` from the " + "`scipy.stats.sampling` namespace.") + with pytest.warns(DeprecationWarning, match=deprecation_msg): + r2 = stats.rvs_ratio_uniforms(f, umax, -v, v, size=10, + random_state=12345) + assert_equal(r1, r2) + + +class TestMGCErrorWarnings: + """ Tests errors and warnings derived from MGC. + """ + def test_error_notndarray(self): + # raises error if x or y is not a ndarray + x = np.arange(20) + y = [5] * 20 + assert_raises(ValueError, stats.multiscale_graphcorr, x, y) + assert_raises(ValueError, stats.multiscale_graphcorr, y, x) + + def test_error_shape(self): + # raises error if number of samples different (n) + x = np.arange(100).reshape(25, 4) + y = x.reshape(10, 10) + assert_raises(ValueError, stats.multiscale_graphcorr, x, y) + + def test_error_lowsamples(self): + # raises error if samples are low (< 3) + x = np.arange(3) + y = np.arange(3) + assert_raises(ValueError, stats.multiscale_graphcorr, x, y) + + def test_error_nans(self): + # raises error if inputs contain NaNs + x = np.arange(20, dtype=float) + x[0] = np.nan + assert_raises(ValueError, stats.multiscale_graphcorr, x, x) + + y = np.arange(20) + assert_raises(ValueError, stats.multiscale_graphcorr, x, y) + + def test_error_wrongdisttype(self): + # raises error if metric is not a function + x = np.arange(20) + compute_distance = 0 + assert_raises(ValueError, stats.multiscale_graphcorr, x, x, + compute_distance=compute_distance) + + @pytest.mark.parametrize("reps", [ + -1, # reps is negative + '1', # reps is not integer + ]) + def test_error_reps(self, reps): + # raises error if reps is negative + x = np.arange(20) + assert_raises(ValueError, stats.multiscale_graphcorr, x, x, reps=reps) + + def test_warns_reps(self): + # raises warning when reps is less than 1000 + x = np.arange(20) + reps = 100 + assert_warns(RuntimeWarning, stats.multiscale_graphcorr, x, x, reps=reps) + + def test_error_infty(self): + # raises error if input contains infinities + x = np.arange(20) + y = np.ones(20) * np.inf + assert_raises(ValueError, stats.multiscale_graphcorr, x, y) + + +class TestMGCStat: + """ Test validity of MGC test statistic + """ + def _simulations(self, samps=100, dims=1, sim_type=""): + # linear simulation + if sim_type == "linear": + x = np.random.uniform(-1, 1, size=(samps, 1)) + y = x + 0.3 * np.random.random_sample(size=(x.size, 1)) + + # spiral simulation + elif sim_type == "nonlinear": + unif = np.array(np.random.uniform(0, 5, size=(samps, 1))) + x = unif * np.cos(np.pi * unif) + y = (unif * np.sin(np.pi * unif) + + 0.4*np.random.random_sample(size=(x.size, 1))) + + # independence (tests type I simulation) + elif sim_type == "independence": + u = np.random.normal(0, 1, size=(samps, 1)) + v = np.random.normal(0, 1, size=(samps, 1)) + u_2 = np.random.binomial(1, p=0.5, size=(samps, 1)) + v_2 = np.random.binomial(1, p=0.5, size=(samps, 1)) + x = u/3 + 2*u_2 - 1 + y = v/3 + 2*v_2 - 1 + + # raises error if not approved sim_type + else: + raise ValueError("sim_type must be linear, nonlinear, or " + "independence") + + # add dimensions of noise for higher dimensions + if dims > 1: + dims_noise = np.random.normal(0, 1, size=(samps, dims-1)) + x = np.concatenate((x, dims_noise), axis=1) + + return x, y + + @pytest.mark.slow + @pytest.mark.parametrize("sim_type, obs_stat, obs_pvalue", [ + ("linear", 0.97, 1/1000), # test linear simulation + ("nonlinear", 0.163, 1/1000), # test spiral simulation + ("independence", -0.0094, 0.78) # test independence simulation + ]) + def test_oned(self, sim_type, obs_stat, obs_pvalue): + np.random.seed(12345678) + + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type=sim_type) + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y) + assert_approx_equal(stat, obs_stat, significant=1) + assert_approx_equal(pvalue, obs_pvalue, significant=1) + + @pytest.mark.slow + @pytest.mark.parametrize("sim_type, obs_stat, obs_pvalue", [ + ("linear", 0.184, 1/1000), # test linear simulation + ("nonlinear", 0.0190, 0.117), # test spiral simulation + ]) + def test_fived(self, sim_type, obs_stat, obs_pvalue): + np.random.seed(12345678) + + # generate x and y + x, y = self._simulations(samps=100, dims=5, sim_type=sim_type) + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y) + assert_approx_equal(stat, obs_stat, significant=1) + assert_approx_equal(pvalue, obs_pvalue, significant=1) + + @pytest.mark.xslow + def test_twosamp(self): + np.random.seed(12345678) + + # generate x and y + x = np.random.binomial(100, 0.5, size=(100, 5)) + y = np.random.normal(0, 1, size=(80, 5)) + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y) + assert_approx_equal(stat, 1.0, significant=1) + assert_approx_equal(pvalue, 0.001, significant=1) + + # generate x and y + y = np.random.normal(0, 1, size=(100, 5)) + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y, is_twosamp=True) + assert_approx_equal(stat, 1.0, significant=1) + assert_approx_equal(pvalue, 0.001, significant=1) + + @pytest.mark.slow + def test_workers(self): + np.random.seed(12345678) + + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type="linear") + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y, workers=2) + assert_approx_equal(stat, 0.97, significant=1) + assert_approx_equal(pvalue, 0.001, significant=1) + + @pytest.mark.slow + def test_random_state(self): + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type="linear") + + # test stat and pvalue + stat, pvalue, _ = stats.multiscale_graphcorr(x, y, random_state=1) + assert_approx_equal(stat, 0.97, significant=1) + assert_approx_equal(pvalue, 0.001, significant=1) + + @pytest.mark.slow + def test_dist_perm(self): + np.random.seed(12345678) + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type="nonlinear") + distx = cdist(x, x, metric="euclidean") + disty = cdist(y, y, metric="euclidean") + + stat_dist, pvalue_dist, _ = stats.multiscale_graphcorr(distx, disty, + compute_distance=None, + random_state=1) + assert_approx_equal(stat_dist, 0.163, significant=1) + assert_approx_equal(pvalue_dist, 0.001, significant=1) + + @pytest.mark.slow + def test_pvalue_literature(self): + np.random.seed(12345678) + + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type="linear") + + # test stat and pvalue + _, pvalue, _ = stats.multiscale_graphcorr(x, y, random_state=1) + assert_allclose(pvalue, 1/1001) + + @pytest.mark.slow + def test_alias(self): + np.random.seed(12345678) + + # generate x and y + x, y = self._simulations(samps=100, dims=1, sim_type="linear") + + res = stats.multiscale_graphcorr(x, y, random_state=1) + assert_equal(res.stat, res.statistic) + + +class TestQuantileTest: + r""" Test the non-parametric quantile test, + including the computation of confidence intervals + """ + + def test_quantile_test_iv(self): + x = [1, 2, 3] + + message = "`x` must be a one-dimensional array of numbers." + with pytest.raises(ValueError, match=message): + stats.quantile_test([x]) + + message = "`q` must be a scalar." + with pytest.raises(ValueError, match=message): + stats.quantile_test(x, q=[1, 2]) + + message = "`p` must be a float strictly between 0 and 1." + with pytest.raises(ValueError, match=message): + stats.quantile_test(x, p=[0.5, 0.75]) + with pytest.raises(ValueError, match=message): + stats.quantile_test(x, p=2) + with pytest.raises(ValueError, match=message): + stats.quantile_test(x, p=-0.5) + + message = "`alternative` must be one of..." + with pytest.raises(ValueError, match=message): + stats.quantile_test(x, alternative='one-sided') + + message = "`confidence_level` must be a number between 0 and 1." + with pytest.raises(ValueError, match=message): + stats.quantile_test(x).confidence_interval(1) + + @pytest.mark.parametrize( + 'p, alpha, lb, ub, alternative', + [[0.3, 0.95, 1.221402758160170, 1.476980793882643, 'two-sided'], + [0.5, 0.9, 1.506817785112854, 1.803988415397857, 'two-sided'], + [0.25, 0.95, -np.inf, 1.39096812846378, 'less'], + [0.8, 0.9, 2.117000016612675, np.inf, 'greater']] + ) + def test_R_ci_quantile(self, p, alpha, lb, ub, alternative): + # Test against R library `confintr` function `ci_quantile`, e.g. + # library(confintr) + # options(digits=16) + # x <- exp(seq(0, 1, by = 0.01)) + # ci_quantile(x, q = 0.3)$interval + # ci_quantile(x, q = 0.5, probs = c(0.05, 0.95))$interval + # ci_quantile(x, q = 0.25, probs = c(0, 0.95))$interval + # ci_quantile(x, q = 0.8, probs = c(0.1, 1))$interval + x = np.exp(np.arange(0, 1.01, 0.01)) + res = stats.quantile_test(x, p=p, alternative=alternative) + assert_allclose(res.confidence_interval(alpha), [lb, ub], rtol=1e-15) + + @pytest.mark.parametrize( + 'q, p, alternative, ref', + [[1.2, 0.3, 'two-sided', 0.01515567517648], + [1.8, 0.5, 'two-sided', 0.1109183496606]] + ) + def test_R_pvalue(self, q, p, alternative, ref): + # Test against R library `snpar` function `quant.test`, e.g. + # library(snpar) + # options(digits=16) + # x < - exp(seq(0, 1, by=0.01)) + # quant.test(x, q=1.2, p=0.3, exact=TRUE, alternative='t') + x = np.exp(np.arange(0, 1.01, 0.01)) + res = stats.quantile_test(x, q=q, p=p, alternative=alternative) + assert_allclose(res.pvalue, ref, rtol=1e-12) + + @pytest.mark.parametrize('case', ['continuous', 'discrete']) + @pytest.mark.parametrize('alternative', ['less', 'greater']) + @pytest.mark.parametrize('alpha', [0.9, 0.95]) + def test_pval_ci_match(self, case, alternative, alpha): + # Verify that the following statement holds: + + # The 95% confidence interval corresponding with alternative='less' + # has -inf as its lower bound, and the upper bound `xu` is the greatest + # element from the sample `x` such that: + # `stats.quantile_test(x, q=xu, p=p, alternative='less').pvalue`` + # will be greater than 5%. + + # And the corresponding statement for the alternative='greater' case. + + seed = int((7**len(case) + len(alternative))*alpha) + rng = np.random.default_rng(seed) + if case == 'continuous': + p, q = rng.random(size=2) + rvs = rng.random(size=100) + else: + rvs = rng.integers(1, 11, size=100) + p = rng.random() + q = rng.integers(1, 11) + + res = stats.quantile_test(rvs, q=q, p=p, alternative=alternative) + ci = res.confidence_interval(confidence_level=alpha) + + # select elements inside the confidence interval based on alternative + if alternative == 'less': + i_inside = rvs <= ci.high + else: + i_inside = rvs >= ci.low + + for x in rvs[i_inside]: + res = stats.quantile_test(rvs, q=x, p=p, alternative=alternative) + assert res.pvalue > 1 - alpha + + for x in rvs[~i_inside]: + res = stats.quantile_test(rvs, q=x, p=p, alternative=alternative) + assert res.pvalue < 1 - alpha + + def test_match_conover_examples(self): + # Test against the examples in [1] (Conover Practical Nonparametric + # Statistics Third Edition) pg 139 + + # Example 1 + # Data is [189, 233, 195, 160, 212, 176, 231, 185, 199, 213, 202, 193, + # 174, 166, 248] + # Two-sided test of whether the upper quartile (p=0.75) equals 193 + # (q=193). Conover shows that 7 of the observations are less than or + # equal to 193, and "for the binomial random variable Y, P(Y<=7) = + # 0.0173", so the two-sided p-value is twice that, 0.0346. + x = [189, 233, 195, 160, 212, 176, 231, 185, 199, 213, 202, 193, + 174, 166, 248] + pvalue_expected = 0.0346 + res = stats.quantile_test(x, q=193, p=0.75, alternative='two-sided') + assert_allclose(res.pvalue, pvalue_expected, rtol=1e-5) + + # Example 2 + # Conover doesn't give explicit data, just that 8 out of 112 + # observations are 60 or less. The test is whether the median time is + # equal to 60 against the alternative that the median is greater than + # 60. The p-value is calculated as P(Y<=8), where Y is again a binomial + # distributed random variable, now with p=0.5 and n=112. Conover uses a + # normal approximation, but we can easily calculate the CDF of the + # binomial distribution. + x = [59]*8 + [61]*(112-8) + pvalue_expected = stats.binom(p=0.5, n=112).pmf(k=8) + res = stats.quantile_test(x, q=60, p=0.5, alternative='greater') + assert_allclose(res.pvalue, pvalue_expected, atol=1e-10) + + +class TestPageTrendTest: + # expected statistic and p-values generated using R at + # https://rdrr.io/cran/cultevo/, e.g. + # library(cultevo) + # data = rbind(c(72, 47, 73, 35, 47, 96, 30, 59, 41, 36, 56, 49, 81, 43, + # 70, 47, 28, 28, 62, 20, 61, 20, 80, 24, 50), + # c(68, 52, 60, 34, 44, 20, 65, 88, 21, 81, 48, 31, 31, 67, + # 69, 94, 30, 24, 40, 87, 70, 43, 50, 96, 43), + # c(81, 13, 85, 35, 79, 12, 92, 86, 21, 64, 16, 64, 68, 17, + # 16, 89, 71, 43, 43, 36, 54, 13, 66, 51, 55)) + # result = page.test(data, verbose=FALSE) + # Most test cases generated to achieve common critical p-values so that + # results could be checked (to limited precision) against tables in + # scipy.stats.page_trend_test reference [1] + + np.random.seed(0) + data_3_25 = np.random.rand(3, 25) + data_10_26 = np.random.rand(10, 26) + + ts = [ + (12805, 0.3886487053947608, False, 'asymptotic', data_3_25), + (49140, 0.02888978556179862, False, 'asymptotic', data_10_26), + (12332, 0.7722477197436702, False, 'asymptotic', + [[72, 47, 73, 35, 47, 96, 30, 59, 41, 36, 56, 49, 81, + 43, 70, 47, 28, 28, 62, 20, 61, 20, 80, 24, 50], + [68, 52, 60, 34, 44, 20, 65, 88, 21, 81, 48, 31, 31, + 67, 69, 94, 30, 24, 40, 87, 70, 43, 50, 96, 43], + [81, 13, 85, 35, 79, 12, 92, 86, 21, 64, 16, 64, 68, + 17, 16, 89, 71, 43, 43, 36, 54, 13, 66, 51, 55]]), + (266, 4.121656378600823e-05, False, 'exact', + [[1.5, 4., 8.3, 5, 19, 11], + [5, 4, 3.5, 10, 20, 21], + [8.4, 3.2, 10, 12, 14, 15]]), + (332, 0.9566400920502488, True, 'exact', + [[4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1], + [4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1], + [3, 4, 1, 2], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], + [1, 2, 3, 4], [1, 2, 3, 4]]), + (241, 0.9622210164861476, True, 'exact', + [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], + [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], + [3, 2, 1], [2, 1, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], + [1, 2, 3], [1, 2, 3], [1, 2, 3]]), + (197, 0.9619432897162209, True, 'exact', + [[6, 5, 4, 3, 2, 1], [6, 5, 4, 3, 2, 1], [1, 3, 4, 5, 2, 6]]), + (423, 0.9590458306880073, True, 'exact', + [[5, 4, 3, 2, 1], [5, 4, 3, 2, 1], [5, 4, 3, 2, 1], + [5, 4, 3, 2, 1], [5, 4, 3, 2, 1], [5, 4, 3, 2, 1], + [4, 1, 3, 2, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], + [1, 2, 3, 4, 5]]), + (217, 0.9693058575034678, True, 'exact', + [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], + [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], + [2, 1, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], + [1, 2, 3]]), + (395, 0.991530289351305, True, 'exact', + [[7, 6, 5, 4, 3, 2, 1], [7, 6, 5, 4, 3, 2, 1], + [6, 5, 7, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7]]), + (117, 0.9997817843373017, True, 'exact', + [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], + [3, 2, 1], [3, 2, 1], [3, 2, 1], [2, 1, 3], [1, 2, 3]]), + ] + + @pytest.mark.parametrize("L, p, ranked, method, data", ts) + def test_accuracy(self, L, p, ranked, method, data): + np.random.seed(42) + res = stats.page_trend_test(data, ranked=ranked, method=method) + assert_equal(L, res.statistic) + assert_allclose(p, res.pvalue) + assert_equal(method, res.method) + + ts2 = [ + (542, 0.9481266260876332, True, 'exact', + [[10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + [1, 8, 4, 7, 6, 5, 9, 3, 2, 10]]), + (1322, 0.9993113928199309, True, 'exact', + [[10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [9, 2, 8, 7, 6, 5, 4, 3, 10, 1], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]), + (2286, 0.9908688345484833, True, 'exact', + [[8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1], + [8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1], + [8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1], + [8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1], + [8, 7, 6, 5, 4, 3, 2, 1], [1, 3, 5, 6, 4, 7, 2, 8], + [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8], + [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8], + [1, 2, 3, 4, 5, 6, 7, 8]]), + ] + + # only the first of these appears slow because intermediate data are + # cached and used on the rest + @pytest.mark.parametrize("L, p, ranked, method, data", ts) + @pytest.mark.slow() + def test_accuracy2(self, L, p, ranked, method, data): + np.random.seed(42) + res = stats.page_trend_test(data, ranked=ranked, method=method) + assert_equal(L, res.statistic) + assert_allclose(p, res.pvalue) + assert_equal(method, res.method) + + def test_options(self): + np.random.seed(42) + m, n = 10, 20 + predicted_ranks = np.arange(1, n+1) + perm = np.random.permutation(np.arange(n)) + data = np.random.rand(m, n) + ranks = stats.rankdata(data, axis=1) + res1 = stats.page_trend_test(ranks) + res2 = stats.page_trend_test(ranks, ranked=True) + res3 = stats.page_trend_test(data, ranked=False) + res4 = stats.page_trend_test(ranks, predicted_ranks=predicted_ranks) + res5 = stats.page_trend_test(ranks[:, perm], + predicted_ranks=predicted_ranks[perm]) + assert_equal(res1.statistic, res2.statistic) + assert_equal(res1.statistic, res3.statistic) + assert_equal(res1.statistic, res4.statistic) + assert_equal(res1.statistic, res5.statistic) + + def test_Ames_assay(self): + # test from _page_trend_test.py [2] page 151; data on page 144 + np.random.seed(42) + + data = [[101, 117, 111], [91, 90, 107], [103, 133, 121], + [136, 140, 144], [190, 161, 201], [146, 120, 116]] + data = np.array(data).T + predicted_ranks = np.arange(1, 7) + + res = stats.page_trend_test(data, ranked=False, + predicted_ranks=predicted_ranks, + method="asymptotic") + assert_equal(res.statistic, 257) + assert_almost_equal(res.pvalue, 0.0035, decimal=4) + + res = stats.page_trend_test(data, ranked=False, + predicted_ranks=predicted_ranks, + method="exact") + assert_equal(res.statistic, 257) + assert_almost_equal(res.pvalue, 0.0023, decimal=4) + + def test_input_validation(self): + # test data not a 2d array + with assert_raises(ValueError, match="`data` must be a 2d array."): + stats.page_trend_test(None) + with assert_raises(ValueError, match="`data` must be a 2d array."): + stats.page_trend_test([]) + with assert_raises(ValueError, match="`data` must be a 2d array."): + stats.page_trend_test([1, 2]) + with assert_raises(ValueError, match="`data` must be a 2d array."): + stats.page_trend_test([[[1]]]) + + # test invalid dimensions + with assert_raises(ValueError, match="Page's L is only appropriate"): + stats.page_trend_test(np.random.rand(1, 3)) + with assert_raises(ValueError, match="Page's L is only appropriate"): + stats.page_trend_test(np.random.rand(2, 2)) + + # predicted ranks must include each integer [1, 2, 3] exactly once + message = "`predicted_ranks` must include each integer" + with assert_raises(ValueError, match=message): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + predicted_ranks=[0, 1, 2]) + with assert_raises(ValueError, match=message): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + predicted_ranks=[1.1, 2, 3]) + with assert_raises(ValueError, match=message): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + predicted_ranks=[1, 2, 3, 3]) + with assert_raises(ValueError, match=message): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + predicted_ranks="invalid") + + # test improperly ranked data + with assert_raises(ValueError, match="`data` is not properly ranked"): + stats.page_trend_test([[0, 2, 3], [1, 2, 3]], True) + with assert_raises(ValueError, match="`data` is not properly ranked"): + stats.page_trend_test([[1, 2, 3], [1, 2, 4]], True) + + # various + with assert_raises(ValueError, match="`data` contains NaNs"): + stats.page_trend_test([[1, 2, 3], [1, 2, np.nan]], + ranked=False) + with assert_raises(ValueError, match="`method` must be in"): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + method="ekki") + with assert_raises(TypeError, match="`ranked` must be boolean."): + stats.page_trend_test(data=[[1, 2, 3], [1, 2, 3]], + ranked="ekki") + + +rng = np.random.default_rng(902340982) +x = rng.random(10) +y = rng.random(10) + + +@pytest.mark.parametrize("fun, args", + [(stats.wilcoxon, (x,)), + (stats.ks_1samp, (x, stats.norm.cdf)), # type: ignore[attr-defined] # noqa: E501 + (stats.ks_2samp, (x, y)), + (stats.kstest, (x, y)), + ]) +def test_rename_mode_method(fun, args): + + res = fun(*args, method='exact') + res2 = fun(*args, mode='exact') + assert_equal(res, res2) + + err = rf"{fun.__name__}() got multiple values for argument" + with pytest.raises(TypeError, match=re.escape(err)): + fun(*args, method='exact', mode='exact') + + +class TestExpectile: + def test_same_as_mean(self): + rng = np.random.default_rng(42) + x = rng.random(size=20) + assert_allclose(stats.expectile(x, alpha=0.5), np.mean(x)) + + def test_minimum(self): + rng = np.random.default_rng(42) + x = rng.random(size=20) + assert_allclose(stats.expectile(x, alpha=0), np.amin(x)) + + def test_maximum(self): + rng = np.random.default_rng(42) + x = rng.random(size=20) + assert_allclose(stats.expectile(x, alpha=1), np.amax(x)) + + def test_weights(self): + # expectile should minimize `fun` defined below; see + # F. Sobotka and T. Kneib, "Geoadditive expectile regression", + # Computational Statistics and Data Analysis 56 (2012) 755-767 + # :doi:`10.1016/j.csda.2010.11.015` + rng = np.random.default_rng(1856392524598679138) + + def fun(u, a, alpha, weights): + w = np.full_like(a, fill_value=alpha) + w[a <= u] = 1 - alpha + return np.sum(w * weights * (a - u)**2) + + def expectile2(a, alpha, weights): + bracket = np.min(a), np.max(a) + return optimize.minimize_scalar(fun, bracket=bracket, + args=(a, alpha, weights)).x + + n = 10 + a = rng.random(n) + alpha = rng.random() + weights = rng.random(n) + + res = stats.expectile(a, alpha, weights=weights) + ref = expectile2(a, alpha, weights) + assert_allclose(res, ref) + + @pytest.mark.parametrize( + "alpha", [0.2, 0.5 - 1e-12, 0.5, 0.5 + 1e-12, 0.8] + ) + @pytest.mark.parametrize("n", [20, 2000]) + def test_expectile_properties(self, alpha, n): + """ + See Section 6 of + I. Steinwart, C. Pasin, R.C. Williamson & S. Zhang (2014). + "Elicitation and Identification of Properties". COLT. + http://proceedings.mlr.press/v35/steinwart14.html + + and + + Propositions 5, 6, 7 of + F. Bellini, B. Klar, and A. Müller and E. Rosazza Gianin (2013). + "Generalized Quantiles as Risk Measures" + http://doi.org/10.2139/ssrn.2225751 + """ + rng = np.random.default_rng(42) + x = rng.normal(size=n) + + # 0. definite / constancy + # Let T(X) denote the expectile of rv X ~ F. + # T(c) = c for constant c + for c in [-5, 0, 0.5]: + assert_allclose( + stats.expectile(np.full(shape=n, fill_value=c), alpha=alpha), + c + ) + + # 1. translation equivariance + # T(X + c) = T(X) + c + c = rng.exponential() + assert_allclose( + stats.expectile(x + c, alpha=alpha), + stats.expectile(x, alpha=alpha) + c, + ) + assert_allclose( + stats.expectile(x - c, alpha=alpha), + stats.expectile(x, alpha=alpha) - c, + ) + + # 2. positively homogeneity + # T(cX) = c * T(X) for c > 0 + assert_allclose( + stats.expectile(c * x, alpha=alpha), + c * stats.expectile(x, alpha=alpha), + ) + + # 3. subadditivity + # Note that subadditivity holds for alpha >= 0.5. + # T(X + Y) <= T(X) + T(Y) + # For alpha = 0.5, i.e. the mean, strict equality holds. + # For alpha < 0.5, one can use property 6. to show + # T(X + Y) >= T(X) + T(Y) + y = rng.logistic(size=n, loc=10) # different distribution than x + if alpha == 0.5: + def assert_op(a, b): + assert_allclose(a, b) + + elif alpha > 0.5: + def assert_op(a, b): + assert a < b + + else: + def assert_op(a, b): + assert a > b + + assert_op( + stats.expectile(np.r_[x + y], alpha=alpha), + stats.expectile(x, alpha=alpha) + + stats.expectile(y, alpha=alpha) + ) + + # 4. monotonicity + # This holds for first order stochastic dominance X: + # X >= Y whenever P(X <= x) < P(Y <= x) + # T(X) <= T(Y) whenever X <= Y + y = rng.normal(size=n, loc=5) + assert ( + stats.expectile(x, alpha=alpha) <= stats.expectile(y, alpha=alpha) + ) + + # 5. convexity for alpha > 0.5, concavity for alpha < 0.5 + # convexity is + # T((1 - c) X + c Y) <= (1 - c) T(X) + c T(Y) for 0 <= c <= 1 + y = rng.logistic(size=n, loc=10) + for c in [0.1, 0.5, 0.8]: + assert_op( + stats.expectile((1-c)*x + c*y, alpha=alpha), + (1-c) * stats.expectile(x, alpha=alpha) + + c * stats.expectile(y, alpha=alpha) + ) + + # 6. negative argument + # T_{alpha}(-X) = -T_{1-alpha}(X) + assert_allclose( + stats.expectile(-x, alpha=alpha), + -stats.expectile(x, alpha=1-alpha), + ) + + @pytest.mark.parametrize("n", [20, 2000]) + def test_monotonicity_in_alpha(self, n): + rng = np.random.default_rng(42) + x = rng.pareto(a=2, size=n) + e_list = [] + alpha_seq = np.logspace(-15, np.log10(0.5), 100) + # sorted list of unique alpha values in interval (0, 1) + for alpha in np.r_[0, alpha_seq, 1 - alpha_seq[:-1:-1], 1]: + e_list.append(stats.expectile(x, alpha=alpha)) + assert np.all(np.diff(e_list) > 0) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_survival.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_survival.py new file mode 100644 index 0000000000000000000000000000000000000000..f8360b9139286c130e8a4acd10aa69894c6bf9e8 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_survival.py @@ -0,0 +1,470 @@ +import pytest +import numpy as np +from numpy.testing import assert_equal, assert_allclose +from scipy import stats +from scipy.stats import _survival + + +def _kaplan_meier_reference(times, censored): + # This is a very straightforward implementation of the Kaplan-Meier + # estimator that does almost everything differently from the implementation + # in stats.ecdf. + + # Begin by sorting the raw data. Note that the order of death and loss + # at a given time matters: death happens first. See [2] page 461: + # "These conventions may be paraphrased by saying that deaths recorded as + # of an age t are treated as if they occurred slightly before t, and losses + # recorded as of an age t are treated as occurring slightly after t." + # We implement this by sorting the data first by time, then by `censored`, + # (which is 0 when there is a death and 1 when there is only a loss). + dtype = [('time', float), ('censored', int)] + data = np.array([(t, d) for t, d in zip(times, censored)], dtype=dtype) + data = np.sort(data, order=('time', 'censored')) + times = data['time'] + died = np.logical_not(data['censored']) + + m = times.size + n = np.arange(m, 0, -1) # number at risk + sf = np.cumprod((n - died) / n) + + # Find the indices of the *last* occurrence of unique times. The + # corresponding entries of `times` and `sf` are what we want. + _, indices = np.unique(times[::-1], return_index=True) + ref_times = times[-indices - 1] + ref_sf = sf[-indices - 1] + return ref_times, ref_sf + + +class TestSurvival: + + @staticmethod + def get_random_sample(rng, n_unique): + # generate random sample + unique_times = rng.random(n_unique) + # convert to `np.int32` to resolve `np.repeat` failure in 32-bit CI + repeats = rng.integers(1, 4, n_unique).astype(np.int32) + times = rng.permuted(np.repeat(unique_times, repeats)) + censored = rng.random(size=times.size) > rng.random() + sample = stats.CensoredData.right_censored(times, censored) + return sample, times, censored + + def test_input_validation(self): + message = '`sample` must be a one-dimensional sequence.' + with pytest.raises(ValueError, match=message): + stats.ecdf([[1]]) + with pytest.raises(ValueError, match=message): + stats.ecdf(1) + + message = '`sample` must not contain nan' + with pytest.raises(ValueError, match=message): + stats.ecdf([np.nan]) + + message = 'Currently, only uncensored and right-censored data...' + with pytest.raises(NotImplementedError, match=message): + stats.ecdf(stats.CensoredData.left_censored([1], censored=[True])) + + message = 'method` must be one of...' + res = stats.ecdf([1, 2, 3]) + with pytest.raises(ValueError, match=message): + res.cdf.confidence_interval(method='ekki-ekki') + with pytest.raises(ValueError, match=message): + res.sf.confidence_interval(method='shrubbery') + + message = 'confidence_level` must be a scalar between 0 and 1' + with pytest.raises(ValueError, match=message): + res.cdf.confidence_interval(-1) + with pytest.raises(ValueError, match=message): + res.sf.confidence_interval([0.5, 0.6]) + + message = 'The confidence interval is undefined at some observations.' + with pytest.warns(RuntimeWarning, match=message): + ci = res.cdf.confidence_interval() + + message = 'Confidence interval bounds do not implement...' + with pytest.raises(NotImplementedError, match=message): + ci.low.confidence_interval() + with pytest.raises(NotImplementedError, match=message): + ci.high.confidence_interval() + + def test_edge_cases(self): + res = stats.ecdf([]) + assert_equal(res.cdf.quantiles, []) + assert_equal(res.cdf.probabilities, []) + + res = stats.ecdf([1]) + assert_equal(res.cdf.quantiles, [1]) + assert_equal(res.cdf.probabilities, [1]) + + def test_unique(self): + # Example with unique observations; `stats.ecdf` ref. [1] page 80 + sample = [6.23, 5.58, 7.06, 6.42, 5.20] + res = stats.ecdf(sample) + ref_x = np.sort(np.unique(sample)) + ref_cdf = np.arange(1, 6) / 5 + ref_sf = 1 - ref_cdf + assert_equal(res.cdf.quantiles, ref_x) + assert_equal(res.cdf.probabilities, ref_cdf) + assert_equal(res.sf.quantiles, ref_x) + assert_equal(res.sf.probabilities, ref_sf) + + def test_nonunique(self): + # Example with non-unique observations; `stats.ecdf` ref. [1] page 82 + sample = [0, 2, 1, 2, 3, 4] + res = stats.ecdf(sample) + ref_x = np.sort(np.unique(sample)) + ref_cdf = np.array([1/6, 2/6, 4/6, 5/6, 1]) + ref_sf = 1 - ref_cdf + assert_equal(res.cdf.quantiles, ref_x) + assert_equal(res.cdf.probabilities, ref_cdf) + assert_equal(res.sf.quantiles, ref_x) + assert_equal(res.sf.probabilities, ref_sf) + + def test_evaluate_methods(self): + # Test CDF and SF `evaluate` methods + rng = np.random.default_rng(1162729143302572461) + sample, _, _ = self.get_random_sample(rng, 15) + res = stats.ecdf(sample) + x = res.cdf.quantiles + xr = x + np.diff(x, append=x[-1]+1)/2 # right shifted points + + assert_equal(res.cdf.evaluate(x), res.cdf.probabilities) + assert_equal(res.cdf.evaluate(xr), res.cdf.probabilities) + assert_equal(res.cdf.evaluate(x[0]-1), 0) # CDF starts at 0 + assert_equal(res.cdf.evaluate([-np.inf, np.inf]), [0, 1]) + + assert_equal(res.sf.evaluate(x), res.sf.probabilities) + assert_equal(res.sf.evaluate(xr), res.sf.probabilities) + assert_equal(res.sf.evaluate(x[0]-1), 1) # SF starts at 1 + assert_equal(res.sf.evaluate([-np.inf, np.inf]), [1, 0]) + + # ref. [1] page 91 + t1 = [37, 43, 47, 56, 60, 62, 71, 77, 80, 81] # times + d1 = [0, 0, 1, 1, 0, 0, 0, 1, 1, 1] # 1 means deaths (not censored) + r1 = [1, 1, 0.875, 0.75, 0.75, 0.75, 0.75, 0.5, 0.25, 0] # reference SF + + # https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_survival/BS704_Survival5.html + t2 = [8, 12, 26, 14, 21, 27, 8, 32, 20, 40] + d2 = [1, 1, 1, 1, 1, 1, 0, 0, 0, 0] + r2 = [0.9, 0.788, 0.675, 0.675, 0.54, 0.405, 0.27, 0.27, 0.27] + t3 = [33, 28, 41, 48, 48, 25, 37, 48, 25, 43] + d3 = [1, 1, 1, 0, 0, 0, 0, 0, 0, 0] + r3 = [1, 0.875, 0.75, 0.75, 0.6, 0.6, 0.6] + + # https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_survival/bs704_survival4.html + t4 = [24, 3, 11, 19, 24, 13, 14, 2, 18, 17, + 24, 21, 12, 1, 10, 23, 6, 5, 9, 17] + d4 = [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1] + r4 = [0.95, 0.95, 0.897, 0.844, 0.844, 0.844, 0.844, 0.844, 0.844, + 0.844, 0.76, 0.676, 0.676, 0.676, 0.676, 0.507, 0.507] + + # https://www.real-statistics.com/survival-analysis/kaplan-meier-procedure/confidence-interval-for-the-survival-function/ + t5 = [3, 5, 8, 10, 5, 5, 8, 12, 15, 14, 2, 11, 10, 9, 12, 5, 8, 11] + d5 = [1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1] + r5 = [0.944, 0.889, 0.722, 0.542, 0.542, 0.542, 0.361, 0.181, 0.181, 0.181] + + @pytest.mark.parametrize("case", [(t1, d1, r1), (t2, d2, r2), (t3, d3, r3), + (t4, d4, r4), (t5, d5, r5)]) + def test_right_censored_against_examples(self, case): + # test `ecdf` against other implementations on example problems + times, died, ref = case + sample = stats.CensoredData.right_censored(times, np.logical_not(died)) + res = stats.ecdf(sample) + assert_allclose(res.sf.probabilities, ref, atol=1e-3) + assert_equal(res.sf.quantiles, np.sort(np.unique(times))) + + # test reference implementation against other implementations + res = _kaplan_meier_reference(times, np.logical_not(died)) + assert_equal(res[0], np.sort(np.unique(times))) + assert_allclose(res[1], ref, atol=1e-3) + + @pytest.mark.parametrize('seed', [182746786639392128, 737379171436494115, + 576033618403180168, 308115465002673650]) + def test_right_censored_against_reference_implementation(self, seed): + # test `ecdf` against reference implementation on random problems + rng = np.random.default_rng(seed) + n_unique = rng.integers(10, 100) + sample, times, censored = self.get_random_sample(rng, n_unique) + res = stats.ecdf(sample) + ref = _kaplan_meier_reference(times, censored) + assert_allclose(res.sf.quantiles, ref[0]) + assert_allclose(res.sf.probabilities, ref[1]) + + # If all observations are uncensored, the KM estimate should match + # the usual estimate for uncensored data + sample = stats.CensoredData(uncensored=times) + res = _survival._ecdf_right_censored(sample) # force Kaplan-Meier + ref = stats.ecdf(times) + assert_equal(res[0], ref.sf.quantiles) + assert_allclose(res[1], ref.cdf.probabilities, rtol=1e-14) + assert_allclose(res[2], ref.sf.probabilities, rtol=1e-14) + + def test_right_censored_ci(self): + # test "greenwood" confidence interval against example 4 (URL above). + times, died = self.t4, self.d4 + sample = stats.CensoredData.right_censored(times, np.logical_not(died)) + res = stats.ecdf(sample) + ref_allowance = [0.096, 0.096, 0.135, 0.162, 0.162, 0.162, 0.162, + 0.162, 0.162, 0.162, 0.214, 0.246, 0.246, 0.246, + 0.246, 0.341, 0.341] + + sf_ci = res.sf.confidence_interval() + cdf_ci = res.cdf.confidence_interval() + allowance = res.sf.probabilities - sf_ci.low.probabilities + + assert_allclose(allowance, ref_allowance, atol=1e-3) + assert_allclose(sf_ci.low.probabilities, + np.clip(res.sf.probabilities - allowance, 0, 1)) + assert_allclose(sf_ci.high.probabilities, + np.clip(res.sf.probabilities + allowance, 0, 1)) + assert_allclose(cdf_ci.low.probabilities, + np.clip(res.cdf.probabilities - allowance, 0, 1)) + assert_allclose(cdf_ci.high.probabilities, + np.clip(res.cdf.probabilities + allowance, 0, 1)) + + # test "log-log" confidence interval against Mathematica + # e = {24, 3, 11, 19, 24, 13, 14, 2, 18, 17, 24, 21, 12, 1, 10, 23, 6, 5, + # 9, 17} + # ci = {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0} + # R = EventData[e, ci] + # S = SurvivalModelFit[R] + # S["PointwiseIntervals", ConfidenceLevel->0.95, + # ConfidenceTransform->"LogLog"] + + ref_low = [0.694743, 0.694743, 0.647529, 0.591142, 0.591142, 0.591142, + 0.591142, 0.591142, 0.591142, 0.591142, 0.464605, 0.370359, + 0.370359, 0.370359, 0.370359, 0.160489, 0.160489] + ref_high = [0.992802, 0.992802, 0.973299, 0.947073, 0.947073, 0.947073, + 0.947073, 0.947073, 0.947073, 0.947073, 0.906422, 0.856521, + 0.856521, 0.856521, 0.856521, 0.776724, 0.776724] + sf_ci = res.sf.confidence_interval(method='log-log') + assert_allclose(sf_ci.low.probabilities, ref_low, atol=1e-6) + assert_allclose(sf_ci.high.probabilities, ref_high, atol=1e-6) + + def test_right_censored_ci_example_5(self): + # test "exponential greenwood" confidence interval against example 5 + times, died = self.t5, self.d5 + sample = stats.CensoredData.right_censored(times, np.logical_not(died)) + res = stats.ecdf(sample) + lower = np.array([0.66639, 0.624174, 0.456179, 0.287822, 0.287822, + 0.287822, 0.128489, 0.030957, 0.030957, 0.030957]) + upper = np.array([0.991983, 0.970995, 0.87378, 0.739467, 0.739467, + 0.739467, 0.603133, 0.430365, 0.430365, 0.430365]) + + sf_ci = res.sf.confidence_interval(method='log-log') + cdf_ci = res.cdf.confidence_interval(method='log-log') + + assert_allclose(sf_ci.low.probabilities, lower, atol=1e-5) + assert_allclose(sf_ci.high.probabilities, upper, atol=1e-5) + assert_allclose(cdf_ci.low.probabilities, 1-upper, atol=1e-5) + assert_allclose(cdf_ci.high.probabilities, 1-lower, atol=1e-5) + + # Test against R's `survival` library `survfit` function, 90%CI + # library(survival) + # options(digits=16) + # time = c(3, 5, 8, 10, 5, 5, 8, 12, 15, 14, 2, 11, 10, 9, 12, 5, 8, 11) + # status = c(1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1) + # res = survfit(Surv(time, status) + # ~1, conf.type = "log-log", conf.int = 0.90) + # res$time; res$lower; res$upper + low = [0.74366748406861172, 0.68582332289196246, 0.50596835651480121, + 0.32913131413336727, 0.32913131413336727, 0.32913131413336727, + 0.15986912028781664, 0.04499539918147757, 0.04499539918147757, + 0.04499539918147757] + high = [0.9890291867238429, 0.9638835422144144, 0.8560366823086629, + 0.7130167643978450, 0.7130167643978450, 0.7130167643978450, + 0.5678602982997164, 0.3887616766886558, 0.3887616766886558, + 0.3887616766886558] + sf_ci = res.sf.confidence_interval(method='log-log', + confidence_level=0.9) + assert_allclose(sf_ci.low.probabilities, low) + assert_allclose(sf_ci.high.probabilities, high) + + # And with conf.type = "plain" + low = [0.8556383113628162, 0.7670478794850761, 0.5485720663578469, + 0.3441515412527123, 0.3441515412527123, 0.3441515412527123, + 0.1449184105424544, 0., 0., 0.] + high = [1., 1., 0.8958723780865975, 0.7391817920806210, + 0.7391817920806210, 0.7391817920806210, 0.5773038116797676, + 0.3642270254596720, 0.3642270254596720, 0.3642270254596720] + sf_ci = res.sf.confidence_interval(confidence_level=0.9) + assert_allclose(sf_ci.low.probabilities, low) + assert_allclose(sf_ci.high.probabilities, high) + + def test_right_censored_ci_nans(self): + # test `ecdf` confidence interval on a problem that results in NaNs + times, died = self.t1, self.d1 + sample = stats.CensoredData.right_censored(times, np.logical_not(died)) + res = stats.ecdf(sample) + + # Reference values generated with Matlab + # format long + # t = [37 43 47 56 60 62 71 77 80 81]; + # d = [0 0 1 1 0 0 0 1 1 1]; + # censored = ~d1; + # [f, x, flo, fup] = ecdf(t, 'Censoring', censored, 'Alpha', 0.05); + x = [37, 47, 56, 77, 80, 81] + flo = [np.nan, 0, 0, 0.052701464070711, 0.337611126231790, np.nan] + fup = [np.nan, 0.35417230377, 0.5500569798, 0.9472985359, 1.0, np.nan] + i = np.searchsorted(res.cdf.quantiles, x) + + message = "The confidence interval is undefined at some observations" + with pytest.warns(RuntimeWarning, match=message): + ci = res.cdf.confidence_interval() + + # Matlab gives NaN as the first element of the CIs. Mathematica agrees, + # but R's survfit does not. It makes some sense, but it's not what the + # formula gives, so skip that element. + assert_allclose(ci.low.probabilities[i][1:], flo[1:]) + assert_allclose(ci.high.probabilities[i][1:], fup[1:]) + + # [f, x, flo, fup] = ecdf(t, 'Censoring', censored, 'Function', + # 'survivor', 'Alpha', 0.05); + flo = [np.nan, 0.64582769623, 0.449943020228, 0.05270146407, 0, np.nan] + fup = [np.nan, 1.0, 1.0, 0.947298535929289, 0.662388873768210, np.nan] + i = np.searchsorted(res.cdf.quantiles, x) + + with pytest.warns(RuntimeWarning, match=message): + ci = res.sf.confidence_interval() + + assert_allclose(ci.low.probabilities[i][1:], flo[1:]) + assert_allclose(ci.high.probabilities[i][1:], fup[1:]) + + # With the same data, R's `survival` library `survfit` function + # doesn't produce the leading NaN + # library(survival) + # options(digits=16) + # time = c(37, 43, 47, 56, 60, 62, 71, 77, 80, 81) + # status = c(0, 0, 1, 1, 0, 0, 0, 1, 1, 1) + # res = survfit(Surv(time, status) + # ~1, conf.type = "plain", conf.int = 0.95) + # res$time + # res$lower + # res$upper + low = [1., 1., 0.64582769623233816, 0.44994302022779326, + 0.44994302022779326, 0.44994302022779326, 0.44994302022779326, + 0.05270146407071086, 0., np.nan] + high = [1., 1., 1., 1., 1., 1., 1., 0.9472985359292891, + 0.6623888737682101, np.nan] + assert_allclose(ci.low.probabilities, low) + assert_allclose(ci.high.probabilities, high) + + # It does with conf.type="log-log", as do we + with pytest.warns(RuntimeWarning, match=message): + ci = res.sf.confidence_interval(method='log-log') + low = [np.nan, np.nan, 0.38700001403202522, 0.31480711370551911, + 0.31480711370551911, 0.31480711370551911, 0.31480711370551911, + 0.08048821148507734, 0.01049958986680601, np.nan] + high = [np.nan, np.nan, 0.9813929658789660, 0.9308983170906275, + 0.9308983170906275, 0.9308983170906275, 0.9308983170906275, + 0.8263946341076415, 0.6558775085110887, np.nan] + assert_allclose(ci.low.probabilities, low) + assert_allclose(ci.high.probabilities, high) + + def test_right_censored_against_uncensored(self): + rng = np.random.default_rng(7463952748044886637) + sample = rng.integers(10, 100, size=1000) + censored = np.zeros_like(sample) + censored[np.argmax(sample)] = True + res = stats.ecdf(sample) + ref = stats.ecdf(stats.CensoredData.right_censored(sample, censored)) + assert_equal(res.sf.quantiles, ref.sf.quantiles) + assert_equal(res.sf._n, ref.sf._n) + assert_equal(res.sf._d[:-1], ref.sf._d[:-1]) # difference @ [-1] + assert_allclose(res.sf._sf[:-1], ref.sf._sf[:-1], rtol=1e-14) + + def test_plot_iv(self): + rng = np.random.default_rng(1769658657308472721) + n_unique = rng.integers(10, 100) + sample, _, _ = self.get_random_sample(rng, n_unique) + res = stats.ecdf(sample) + + try: + import matplotlib.pyplot as plt # noqa: F401 + res.sf.plot() # no other errors occur + except (ModuleNotFoundError, ImportError): + # Avoid trying to call MPL with numpy 2.0-dev, because that fails + # too often due to ABI mismatches and is hard to avoid. This test + # will work fine again once MPL has done a 2.0-compatible release. + if not np.__version__.startswith('2.0.0.dev0'): + message = r"matplotlib must be installed to use method `plot`." + with pytest.raises(ModuleNotFoundError, match=message): + res.sf.plot() + + +class TestLogRank: + + @pytest.mark.parametrize( + "x, y, statistic, pvalue", + # Results validate with R + # library(survival) + # options(digits=16) + # + # futime_1 <- c(8, 12, 26, 14, 21, 27, 8, 32, 20, 40) + # fustat_1 <- c(1, 1, 1, 1, 1, 1, 0, 0, 0, 0) + # rx_1 <- c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + # + # futime_2 <- c(33, 28, 41, 48, 48, 25, 37, 48, 25, 43) + # fustat_2 <- c(1, 1, 1, 0, 0, 0, 0, 0, 0, 0) + # rx_2 <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1) + # + # futime <- c(futime_1, futime_2) + # fustat <- c(fustat_1, fustat_2) + # rx <- c(rx_1, rx_2) + # + # survdiff(formula = Surv(futime, fustat) ~ rx) + # + # Also check against another library which handle alternatives + # library(nph) + # logrank.test(futime, fustat, rx, alternative = "two.sided") + # res["test"] + [( + # https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_survival/BS704_Survival5.html + # uncensored, censored + [[8, 12, 26, 14, 21, 27], [8, 32, 20, 40]], + [[33, 28, 41], [48, 48, 25, 37, 48, 25, 43]], + # chi2, ["two-sided", "less", "greater"] + 6.91598157449, + [0.008542873404, 0.9957285632979385, 0.004271436702061537] + ), + ( + # https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_survival/BS704_Survival5.html + [[19, 6, 5, 4], [20, 19, 17, 14]], + [[16, 21, 7], [21, 15, 18, 18, 5]], + 0.835004855038, + [0.3608293039, 0.8195853480676912, 0.1804146519323088] + ), + ( + # Bland, Altman, "The logrank test", BMJ, 2004 + # https://www.bmj.com/content/328/7447/1073.short + [[6, 13, 21, 30, 37, 38, 49, 50, 63, 79, 86, 98, 202, 219], + [31, 47, 80, 82, 82, 149]], + [[10, 10, 12, 13, 14, 15, 16, 17, 18, 20, 24, 24, 25, 28, 30, + 33, 35, 37, 40, 40, 46, 48, 76, 81, 82, 91, 112, 181], + [34, 40, 70]], + 7.49659416854, + [0.006181578637, 0.003090789318730882, 0.9969092106812691] + )] + ) + def test_log_rank(self, x, y, statistic, pvalue): + x = stats.CensoredData(uncensored=x[0], right=x[1]) + y = stats.CensoredData(uncensored=y[0], right=y[1]) + + for i, alternative in enumerate(["two-sided", "less", "greater"]): + res = stats.logrank(x=x, y=y, alternative=alternative) + + # we return z and use the normal distribution while other framework + # return z**2. The p-value are directly comparable, but we have to + # square the statistic + assert_allclose(res.statistic**2, statistic, atol=1e-10) + assert_allclose(res.pvalue, pvalue[i], atol=1e-10) + + def test_raises(self): + sample = stats.CensoredData([1, 2]) + + msg = r"`y` must be" + with pytest.raises(ValueError, match=msg): + stats.logrank(x=sample, y=[[1, 2]]) + + msg = r"`x` must be" + with pytest.raises(ValueError, match=msg): + stats.logrank(x=[[1, 2]], y=sample) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_tukeylambda_stats.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_tukeylambda_stats.py new file mode 100644 index 0000000000000000000000000000000000000000..8041658599a6ed5b2cd70def1a92bffe0d851792 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_tukeylambda_stats.py @@ -0,0 +1,85 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_equal + +from scipy.stats._tukeylambda_stats import (tukeylambda_variance, + tukeylambda_kurtosis) + + +def test_tukeylambda_stats_known_exact(): + """Compare results with some known exact formulas.""" + # Some exact values of the Tukey Lambda variance and kurtosis: + # lambda var kurtosis + # 0 pi**2/3 6/5 (logistic distribution) + # 0.5 4 - pi (5/3 - pi/2)/(pi/4 - 1)**2 - 3 + # 1 1/3 -6/5 (uniform distribution on (-1,1)) + # 2 1/12 -6/5 (uniform distribution on (-1/2, 1/2)) + + # lambda = 0 + var = tukeylambda_variance(0) + assert_allclose(var, np.pi**2 / 3, atol=1e-12) + kurt = tukeylambda_kurtosis(0) + assert_allclose(kurt, 1.2, atol=1e-10) + + # lambda = 0.5 + var = tukeylambda_variance(0.5) + assert_allclose(var, 4 - np.pi, atol=1e-12) + kurt = tukeylambda_kurtosis(0.5) + desired = (5./3 - np.pi/2) / (np.pi/4 - 1)**2 - 3 + assert_allclose(kurt, desired, atol=1e-10) + + # lambda = 1 + var = tukeylambda_variance(1) + assert_allclose(var, 1.0 / 3, atol=1e-12) + kurt = tukeylambda_kurtosis(1) + assert_allclose(kurt, -1.2, atol=1e-10) + + # lambda = 2 + var = tukeylambda_variance(2) + assert_allclose(var, 1.0 / 12, atol=1e-12) + kurt = tukeylambda_kurtosis(2) + assert_allclose(kurt, -1.2, atol=1e-10) + + +def test_tukeylambda_stats_mpmath(): + """Compare results with some values that were computed using mpmath.""" + a10 = dict(atol=1e-10, rtol=0) + a12 = dict(atol=1e-12, rtol=0) + data = [ + # lambda variance kurtosis + [-0.1, 4.78050217874253547, 3.78559520346454510], + [-0.0649, 4.16428023599895777, 2.52019675947435718], + [-0.05, 3.93672267890775277, 2.13129793057777277], + [-0.001, 3.30128380390964882, 1.21452460083542988], + [0.001, 3.27850775649572176, 1.18560634779287585], + [0.03125, 2.95927803254615800, 0.804487555161819980], + [0.05, 2.78281053405464501, 0.611604043886644327], + [0.0649, 2.65282386754100551, 0.476834119532774540], + [1.2, 0.242153920578588346, -1.23428047169049726], + [10.0, 0.00095237579757703597, 2.37810697355144933], + [20.0, 0.00012195121951131043, 7.37654321002709531], + ] + + for lam, var_expected, kurt_expected in data: + var = tukeylambda_variance(lam) + assert_allclose(var, var_expected, **a12) + kurt = tukeylambda_kurtosis(lam) + assert_allclose(kurt, kurt_expected, **a10) + + # Test with vector arguments (most of the other tests are for single + # values). + lam, var_expected, kurt_expected = zip(*data) + var = tukeylambda_variance(lam) + assert_allclose(var, var_expected, **a12) + kurt = tukeylambda_kurtosis(lam) + assert_allclose(kurt, kurt_expected, **a10) + + +def test_tukeylambda_stats_invalid(): + """Test values of lambda outside the domains of the functions.""" + lam = [-1.0, -0.5] + var = tukeylambda_variance(lam) + assert_equal(var, np.array([np.nan, np.inf])) + + lam = [-1.0, -0.25] + kurt = tukeylambda_kurtosis(lam) + assert_equal(kurt, np.array([np.nan, np.inf])) diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_variation.py b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_variation.py new file mode 100644 index 0000000000000000000000000000000000000000..133978d681f09cc71e7b8667a8a0bc1b12b3a395 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/scipy/stats/tests/test_variation.py @@ -0,0 +1,159 @@ +import numpy as np +from numpy.testing import assert_equal, assert_allclose +import pytest +from scipy.stats import variation +from scipy._lib._util import AxisError + + +class TestVariation: + """ + Test class for scipy.stats.variation + """ + + def test_ddof(self): + x = np.arange(9.0) + assert_allclose(variation(x, ddof=1), np.sqrt(60/8)/4) + + @pytest.mark.parametrize('sgn', [1, -1]) + def test_sign(self, sgn): + x = np.array([1, 2, 3, 4, 5]) + v = variation(sgn*x) + expected = sgn*np.sqrt(2)/3 + assert_allclose(v, expected, rtol=1e-10) + + def test_scalar(self): + # A scalar is treated like a 1-d sequence with length 1. + assert_equal(variation(4.0), 0.0) + + @pytest.mark.parametrize('nan_policy, expected', + [('propagate', np.nan), + ('omit', np.sqrt(20/3)/4)]) + def test_variation_nan(self, nan_policy, expected): + x = np.arange(10.) + x[9] = np.nan + assert_allclose(variation(x, nan_policy=nan_policy), expected) + + def test_nan_policy_raise(self): + x = np.array([1.0, 2.0, np.nan, 3.0]) + with pytest.raises(ValueError, match='input contains nan'): + variation(x, nan_policy='raise') + + def test_bad_nan_policy(self): + with pytest.raises(ValueError, match='must be one of'): + variation([1, 2, 3], nan_policy='foobar') + + def test_keepdims(self): + x = np.arange(10).reshape(2, 5) + y = variation(x, axis=1, keepdims=True) + expected = np.array([[np.sqrt(2)/2], + [np.sqrt(2)/7]]) + assert_allclose(y, expected) + + @pytest.mark.parametrize('axis, expected', + [(0, np.empty((1, 0))), + (1, np.full((5, 1), fill_value=np.nan))]) + def test_keepdims_size0(self, axis, expected): + x = np.zeros((5, 0)) + y = variation(x, axis=axis, keepdims=True) + assert_equal(y, expected) + + @pytest.mark.parametrize('incr, expected_fill', [(0, np.inf), (1, np.nan)]) + def test_keepdims_and_ddof_eq_len_plus_incr(self, incr, expected_fill): + x = np.array([[1, 1, 2, 2], [1, 2, 3, 3]]) + y = variation(x, axis=1, ddof=x.shape[1] + incr, keepdims=True) + assert_equal(y, np.full((2, 1), fill_value=expected_fill)) + + def test_propagate_nan(self): + # Check that the shape of the result is the same for inputs + # with and without nans, cf gh-5817 + a = np.arange(8).reshape(2, -1).astype(float) + a[1, 0] = np.nan + v = variation(a, axis=1, nan_policy="propagate") + assert_allclose(v, [np.sqrt(5/4)/1.5, np.nan], atol=1e-15) + + def test_axis_none(self): + # Check that `variation` computes the result on the flattened + # input when axis is None. + y = variation([[0, 1], [2, 3]], axis=None) + assert_allclose(y, np.sqrt(5/4)/1.5) + + def test_bad_axis(self): + # Check that an invalid axis raises np.exceptions.AxisError. + x = np.array([[1, 2, 3], [4, 5, 6]]) + with pytest.raises(AxisError): + variation(x, axis=10) + + def test_mean_zero(self): + # Check that `variation` returns inf for a sequence that is not + # identically zero but whose mean is zero. + x = np.array([10, -3, 1, -4, -4]) + y = variation(x) + assert_equal(y, np.inf) + + x2 = np.array([x, -10*x]) + y2 = variation(x2, axis=1) + assert_equal(y2, [np.inf, np.inf]) + + @pytest.mark.parametrize('x', [np.zeros(5), [], [1, 2, np.inf, 9]]) + def test_return_nan(self, x): + # Test some cases where `variation` returns nan. + y = variation(x) + assert_equal(y, np.nan) + + @pytest.mark.parametrize('axis, expected', + [(0, []), (1, [np.nan]*3), (None, np.nan)]) + def test_2d_size_zero_with_axis(self, axis, expected): + x = np.empty((3, 0)) + y = variation(x, axis=axis) + assert_equal(y, expected) + + def test_neg_inf(self): + # Edge case that produces -inf: ddof equals the number of non-nan + # values, the values are not constant, and the mean is negative. + x1 = np.array([-3, -5]) + assert_equal(variation(x1, ddof=2), -np.inf) + + x2 = np.array([[np.nan, 1, -10, np.nan], + [-20, -3, np.nan, np.nan]]) + assert_equal(variation(x2, axis=1, ddof=2, nan_policy='omit'), + [-np.inf, -np.inf]) + + @pytest.mark.parametrize("nan_policy", ['propagate', 'omit']) + def test_combined_edge_cases(self, nan_policy): + x = np.array([[0, 10, np.nan, 1], + [0, -5, np.nan, 2], + [0, -5, np.nan, 3]]) + y = variation(x, axis=0, nan_policy=nan_policy) + assert_allclose(y, [np.nan, np.inf, np.nan, np.sqrt(2/3)/2]) + + @pytest.mark.parametrize( + 'ddof, expected', + [(0, [np.sqrt(1/6), np.sqrt(5/8), np.inf, 0, np.nan, 0.0, np.nan]), + (1, [0.5, np.sqrt(5/6), np.inf, 0, np.nan, 0, np.nan]), + (2, [np.sqrt(0.5), np.sqrt(5/4), np.inf, np.nan, np.nan, 0, np.nan])] + ) + def test_more_nan_policy_omit_tests(self, ddof, expected): + # The slightly strange formatting in the follow array is my attempt to + # maintain a clean tabular arrangement of the data while satisfying + # the demands of pycodestyle. Currently, E201 and E241 are not + # disabled by the `# noqa` annotation. + nan = np.nan + x = np.array([[1.0, 2.0, nan, 3.0], + [0.0, 4.0, 3.0, 1.0], + [nan, -.5, 0.5, nan], + [nan, 9.0, 9.0, nan], + [nan, nan, nan, nan], + [3.0, 3.0, 3.0, 3.0], + [0.0, 0.0, 0.0, 0.0]]) + v = variation(x, axis=1, ddof=ddof, nan_policy='omit') + assert_allclose(v, expected) + + def test_variation_ddof(self): + # test variation with delta degrees of freedom + # regression test for gh-13341 + a = np.array([1, 2, 3, 4, 5]) + nan_a = np.array([1, 2, 3, np.nan, 4, 5, np.nan]) + y = variation(a, ddof=1) + nan_y = variation(nan_a, nan_policy="omit", ddof=1) + assert_allclose(y, np.sqrt(5/2)/3) + assert y == nan_y diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/INSTALLER b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/LICENSE b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..353924be0e59b9ad7e6c22848c2189398481821d --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/METADATA b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f083e51a604c9d8ac74e20b9964ba22fc68ad8ac --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/METADATA @@ -0,0 +1,119 @@ +Metadata-Version: 2.1 +Name: setuptools +Version: 58.0.4 +Summary: Easily download, build, install, upgrade, and uninstall Python packages +Home-page: https://github.com/pypa/setuptools +Author: Python Packaging Authority +Author-email: distutils-sig@python.org +License: UNKNOWN +Project-URL: Documentation, https://setuptools.readthedocs.io/ +Keywords: CPAN PyPI distutils eggs package management +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Requires-Python: >=3.6 +License-File: LICENSE +Provides-Extra: certs +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs' +Requires-Dist: pygments-github-lexers (==0.0.5) ; extra == 'docs' +Requires-Dist: sphinx-inline-tabs ; extra == 'docs' +Requires-Dist: sphinxcontrib-towncrier ; extra == 'docs' +Requires-Dist: furo ; extra == 'docs' +Provides-Extra: ssl +Provides-Extra: testing +Requires-Dist: pytest (>=4.6) ; extra == 'testing' +Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' +Requires-Dist: pytest-flake8 ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing' +Requires-Dist: mock ; extra == 'testing' +Requires-Dist: flake8-2020 ; extra == 'testing' +Requires-Dist: virtualenv (>=13.0.0) ; extra == 'testing' +Requires-Dist: pytest-virtualenv (>=1.2.7) ; extra == 'testing' +Requires-Dist: wheel ; extra == 'testing' +Requires-Dist: paver ; extra == 'testing' +Requires-Dist: pip (>=19.1) ; extra == 'testing' +Requires-Dist: jaraco.envs ; extra == 'testing' +Requires-Dist: pytest-xdist ; extra == 'testing' +Requires-Dist: sphinx ; extra == 'testing' +Requires-Dist: jaraco.path (>=3.2.0) ; extra == 'testing' +Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/setuptools.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/setuptools + +.. image:: https://github.com/pypa/setuptools/workflows/tests/badge.svg + :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg + :target: https://setuptools.readthedocs.io + +.. image:: https://img.shields.io/badge/skeleton-2021-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white + :target: https://codecov.io/gh/pypa/setuptools + +.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat + :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme + +See the `Installation Instructions +`_ in the Python Packaging +User's Guide for instructions on installing, upgrading, and uninstalling +Setuptools. + +Questions and comments should be directed to the `distutils-sig +mailing list `_. +Bug reports and especially tested patches may be +submitted directly to the `bug tracker +`_. + + +Code of Conduct +=============== + +Everyone interacting in the setuptools project's codebases, issue trackers, +chat rooms, and mailing lists is expected to follow the +`PSF Code of Conduct `_. + + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. + + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/RECORD b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..1e56330ee579c05931c9d70f5da2f75447dd7eda --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/RECORD @@ -0,0 +1,157 @@ +_distutils_hack/__init__.py,sha256=X3RUiA6KBPoEmco_CjACyltyQbFRGVUpZRAbSkPGwMs,3688 +_distutils_hack/override.py,sha256=Eu_s-NF6VIZ4Cqd0tbbA5wtWky2IZPNd8et6GLt1mzo,44 +distutils-precedence.pth,sha256=fqf_7z_ioRfuEsaO1lU2F_DX_S8FkCV8JcSElZo7c3M,152 +pkg_resources/__init__.py,sha256=P3PNN3_m8JJrYMp-i-Sq-3rhK5vuViqqjn1UXKHfe7Q,108202 +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +pkg_resources/_vendor/packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736 +pkg_resources/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +pkg_resources/_vendor/packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128 +pkg_resources/_vendor/packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 +pkg_resources/_vendor/packaging/_typing.py,sha256=x59EhQ57TMT-kTRyLZV25HZvYGGwbucTo6iKh_O0tMw,1812 +pkg_resources/_vendor/packaging/markers.py,sha256=YSntQkMnKyw1_FG6oRNNnGxLL6bAxcGXOtuFE-YTS3k,9518 +pkg_resources/_vendor/packaging/requirements.py,sha256=R8K4H4xX_iD4LvpGw1U3ouuPbGN-wzsFgD7brhAM71Y,4929 +pkg_resources/_vendor/packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944 +pkg_resources/_vendor/packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067 +pkg_resources/_vendor/packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811 +pkg_resources/_vendor/packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470 +pkg_resources/_vendor/pyparsing.py,sha256=mahtkgcp3grNAD0re_9R0DLvBnvjzpeLwgJqT-3H1CE,232056 +pkg_resources/extern/__init__.py,sha256=3PixaT9Tzzd4NoyV6CVhGd7S_9Z-U5yvMWAftZKvC6k,2362 +pkg_resources/tests/data/my-test-package-source/setup.py,sha256=Mrezl3nqxkYkjCYpIxmjhhg4AR8hgi4QZdEYmk-I7R8,104 +setuptools-58.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools-58.0.4.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +setuptools-58.0.4.dist-info/METADATA,sha256=TD4yehxa8Vx7J-J1GRliA0I2uHCibS66C7z4LAHwpyk,4852 +setuptools-58.0.4.dist-info/RECORD,, +setuptools-58.0.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools-58.0.4.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +setuptools-58.0.4.dist-info/entry_points.txt,sha256=wpnhLrbtyk4hZ1qCCw48cCSxoQPzULMhIuaFqsB7GxQ,2636 +setuptools-58.0.4.dist-info/top_level.txt,sha256=d9yL39v_W7qmKDDSH6sT4bE0j_Ls1M3P161OGgdsm4g,41 +setuptools/__init__.py,sha256=l7ULo8jGk-4-8jbacmJ58cYpSRX4swS1ccbJaJVAGdM,7448 +setuptools/_deprecation_warning.py,sha256=jU9-dtfv6cKmtQJOXN8nP1mm7gONw5kKEtiPtbwnZyI,218 +setuptools/_distutils/__init__.py,sha256=lpQAphR_7uhWC2fbSEps4Ja9W4YwezN_IX_LJEt3khU,250 +setuptools/_distutils/_msvccompiler.py,sha256=jR0JM5A1JMnZ6xMDicQzhXWgXTVXs1lWAeUexC1z198,20813 +setuptools/_distutils/archive_util.py,sha256=qW-uiGwYexTvK5e-iSel_31Dshx-CqTanNPK6snwf98,8572 +setuptools/_distutils/bcppcompiler.py,sha256=OJDVpCUmX6H8v_7lV1zifV1fcx92Cr2dhiUh6989UJI,14894 +setuptools/_distutils/ccompiler.py,sha256=G2tn9Q3zQ0VUNfW1LM-nrnLt_6OhtiUunugCv85D1PQ,47607 +setuptools/_distutils/cmd.py,sha256=eco6LAGUtobLuPafuhmgKgkwRRL_WY8KJ4YeDCHpcls,18079 +setuptools/_distutils/command/__init__.py,sha256=2TA-rlNDlzeI-csbWHXFjGD8uOYqALMfyWOhT49nC6g,799 +setuptools/_distutils/command/bdist.py,sha256=2z4eudRl_n7m3lG9leL0IYqes4bsm8c0fxfZuiafjMg,5562 +setuptools/_distutils/command/bdist_dumb.py,sha256=BTur9jcIppyP7Piavjfsk7YjElqvxeYO2npUyPPOekc,4913 +setuptools/_distutils/command/bdist_msi.py,sha256=EVFQYN_X-ExeeP8gmdV9JcINsuUGsLJUz9afMU0Rt8c,35579 +setuptools/_distutils/command/bdist_rpm.py,sha256=gjOw22GhDSbcq0bdq25cTb-n6HWWm0bShLQad_mkJ4k,21537 +setuptools/_distutils/command/bdist_wininst.py,sha256=iGlaI-VfElHOneeczKHWnSN5a10-7IMcJaXuR1mdS3c,16030 +setuptools/_distutils/command/build.py,sha256=1AF-dxN_NlOEyoydBz19AwpeWYPSYCZvOLJSN_PdatY,5773 +setuptools/_distutils/command/build_clib.py,sha256=bgVTHh28eLQA2Gkw68amApd_j7qQBX4MTI-zTvAK_J4,8022 +setuptools/_distutils/command/build_ext.py,sha256=8RSBMLY88t7wlwqwdVScJv7OQMxeQ9q-mmwVPipSA-U,31720 +setuptools/_distutils/command/build_py.py,sha256=hXesMrH_epNj6K8SUtJdipgEis3EdICKeZ8VWe_ndck,16495 +setuptools/_distutils/command/build_scripts.py,sha256=urdn6wPxPMW5dLqpqFkZ8dqaFG1tf9TiAao6U9LCoEI,5963 +setuptools/_distutils/command/check.py,sha256=5qDtI75ccZg3sAItQWeaIu8y3FR314O4rr9Smz4HsEo,5637 +setuptools/_distutils/command/clean.py,sha256=2TCt47ru4hZZM0RfVfUYj5bbpicpGLP4Qhw5jBtvp9k,2776 +setuptools/_distutils/command/config.py,sha256=2aTjww3PwjMB8-ZibCe4P7B-qG1hM1gn_rJXYyxRz6c,13117 +setuptools/_distutils/command/install.py,sha256=drhacfX9QHypgMvMP9C6BjqRYmcGjsH5FPctFcWpqcw,27488 +setuptools/_distutils/command/install_data.py,sha256=YhGOAwh3gJPqF7em5XA0rmpR42z1bLh80ooElzDyUvk,2822 +setuptools/_distutils/command/install_egg_info.py,sha256=0kW0liVMeadkjX0ZcRfMptKFen07Gw6gyw1VHT5KIwc,2603 +setuptools/_distutils/command/install_headers.py,sha256=XQ6idkbIDfr1ljXCOznuVUMvOFpHBn6cK0Wz9gIM2b4,1298 +setuptools/_distutils/command/install_lib.py,sha256=9AofR-MO9lAtjwwuukCptepOaJEKMZW2VHiyR5hU7HA,8397 +setuptools/_distutils/command/install_scripts.py,sha256=_CLUeQwGJRcY2kik7azPMn5IdtDCrjWdUvZ1khlG6ck,2017 +setuptools/_distutils/command/py37compat.py,sha256=qzRhhvTihqx_PZZt2ZYECxh1X3Oj255VqatzelYFAKw,671 +setuptools/_distutils/command/register.py,sha256=2jaq9968rt2puRVDBx1HbNiXv27uOk8idE_4lPf_3VM,11712 +setuptools/_distutils/command/sdist.py,sha256=qotJjAOzyhJjq2-oDImjNFrOtaSneEFDJTB-sEk1wnU,19005 +setuptools/_distutils/command/upload.py,sha256=BLO1w7eSAqsCjCLXtf_CRVSjwF1WmyOByGVGNdcQ8oY,7597 +setuptools/_distutils/config.py,sha256=dtHgblx9JhfyrKx1-J7Jlxw_f7s8ZbPFQii2UWMTZpY,4827 +setuptools/_distutils/core.py,sha256=jbdOkpOK09xi-56vhhwvn3fYdhLb5DJO8q3K1fnQz0Q,8876 +setuptools/_distutils/cygwinccompiler.py,sha256=QpmRAopZOYEKww_iCWTu3KLjs9gggyl90E0fagAxqCM,16938 +setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139 +setuptools/_distutils/dep_util.py,sha256=GuR9Iw_jzZRkyemJ5HX8rB_wRGxkIBcBm1qh54r7zhk,3491 +setuptools/_distutils/dir_util.py,sha256=UwhBOUTcV65GTwce4SPuTXR8Z8q3LYEcmttqcGb0bYo,7778 +setuptools/_distutils/dist.py,sha256=Biuf6ca8uiFfMScRFsYUKtb5neMPtxKxRtXn50_1f3U,50421 +setuptools/_distutils/errors.py,sha256=Yr6tKZGdzBoNi53vBtiq0UJ__X05CmxSdQJqOWaw6SY,3577 +setuptools/_distutils/extension.py,sha256=bTb3Q0CoevGKYv5dX1ls--Ln8tlB0-UEOsi9BwzlZ-s,10515 +setuptools/_distutils/fancy_getopt.py,sha256=OPxp2CxHi1Yp_d1D8JxW4Ueq9fC71tegQFaafh58GGU,17784 +setuptools/_distutils/file_util.py,sha256=0hUqfItN_x2DVihR0MHdA4KCMVCOO8VoByaFp_a6MDg,8148 +setuptools/_distutils/filelist.py,sha256=Z9f5hvepZnpniZ2IFmCnWIjdviWozs8sbARBhWajwoM,13407 +setuptools/_distutils/log.py,sha256=hWBmdUC2K927QcVv3REMW3HMPclxccPQngxLSuUXQl0,1969 +setuptools/_distutils/msvc9compiler.py,sha256=X623B92g0v8A3BEM9qpRf396AEd_hfjkfDUVTKu0hcE,30453 +setuptools/_distutils/msvccompiler.py,sha256=qruALeGRq8-CjtjE2tLQ8W26QnchcYedWzFme8AxZ4Q,23540 +setuptools/_distutils/py35compat.py,sha256=-sk1vBIsOgH-AobjIYbK_OEjdJF_54Ul_D1EiE9XM_c,455 +setuptools/_distutils/py38compat.py,sha256=II7ddBxOijC7uNN4z_46HYUjwYTJYMNiLJoGTormZm0,212 +setuptools/_distutils/spawn.py,sha256=4uE9k3VZWijxy7E_Rlcmh1MoamaPJ8rajdNBagKxjgU,3498 +setuptools/_distutils/sysconfig.py,sha256=5z55MU7gXeceL_G9FK6ex-2OvdeIXJRZJafrtthJcfU,21349 +setuptools/_distutils/text_file.py,sha256=PsuAJeWdKJoLSV_6N6IpB5-0Pa84KzLUucJMFRazw3I,12483 +setuptools/_distutils/unixccompiler.py,sha256=ke1kq16XId1U1RRvAmaUP9iOEiH4cWNB_FXXqm1vzF0,14957 +setuptools/_distutils/util.py,sha256=6UsgxxG3pzfimk2JHa5LBIHHddmBT-YdxoocXLlL6g4,20373 +setuptools/_distutils/version.py,sha256=8NogP6NPPQpp3EUMZcT9czEHia-ehqPo8spo_e7AgUU,12514 +setuptools/_distutils/versionpredicate.py,sha256=ZxpEA-TQv88mUWc6hetUO4qSqA2sa7ipjZ3QEK5evDk,5133 +setuptools/_imp.py,sha256=HmF91IbitRfsD5z-g4_wmcuH-RahyIONbPgiCOFgtzA,2392 +setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools/_vendor/more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82 +setuptools/_vendor/more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968 +setuptools/_vendor/more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256 +setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 +setuptools/_vendor/packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736 +setuptools/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +setuptools/_vendor/packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128 +setuptools/_vendor/packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 +setuptools/_vendor/packaging/_typing.py,sha256=x59EhQ57TMT-kTRyLZV25HZvYGGwbucTo6iKh_O0tMw,1812 +setuptools/_vendor/packaging/markers.py,sha256=BCCxZbt8xgysH8v5pqbLkdtQnRZHIGkJQqlNBGek4nQ,9509 +setuptools/_vendor/packaging/requirements.py,sha256=VHydZdk8m3qFxReomNwKr71cmpjantEV_xOhkEyyINI,4917 +setuptools/_vendor/packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944 +setuptools/_vendor/packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067 +setuptools/_vendor/packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811 +setuptools/_vendor/packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470 +setuptools/_vendor/pyparsing.py,sha256=mahtkgcp3grNAD0re_9R0DLvBnvjzpeLwgJqT-3H1CE,232056 +setuptools/archive_util.py,sha256=maJDbozRbDeSPw53VT0cb_IS3W0Ap73lJR8tX8RZDx0,7077 +setuptools/build_meta.py,sha256=x7FI1UPKCKxBBSopXocfGDnJa98rQO8atKXSwJtdid8,10280 +setuptools/cli-32.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/cli-64.exe,sha256=KLABu5pyrnokJCv6skjXZ6GsXeyYHGcqOUT3oHI3Xpo,74752 +setuptools/cli.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/command/__init__.py,sha256=e-8TJOikUe3St0fw2b2p9u5EDdSxl5zHUBJJKifbcQ8,217 +setuptools/command/alias.py,sha256=1sLQxZcNh6dDQpDmm4G7UGGTol83nY1NTPmNBbm2siI,2381 +setuptools/command/bdist_egg.py,sha256=-upiB6fFtm8cQSQj1LRDVpG1-T143DsXCvV0fh03u7U,16604 +setuptools/command/bdist_rpm.py,sha256=_4Y7tVAzu1zEuDc8tpRdE_sy3zST3h3LPTtzioos5Ck,900 +setuptools/command/build_clib.py,sha256=fWHSFGkk10VCddBWCszvNhowbG9Z9CZXVjQ2uSInoOs,4415 +setuptools/command/build_ext.py,sha256=SNK042HfB2ezlDQbSVRGFqI1IM5A4AsjU1wpV3fgskE,13212 +setuptools/command/build_py.py,sha256=UydjclXl6FSyrPjXOOwZD-gHby0tIKoP-qu5itvyP0g,8276 +setuptools/command/develop.py,sha256=5_Ss7ENd1_B_jVMY1tF5UV_y1Xu6jbVzAPG8oKeluGA,7012 +setuptools/command/dist_info.py,sha256=5t6kOfrdgALT-P3ogss6PF9k-Leyesueycuk3dUyZnI,960 +setuptools/command/easy_install.py,sha256=aNJ21TnKPu5Jx2_301YzW6i8eJkxLqfoEQWI395uX4Q,85341 +setuptools/command/egg_info.py,sha256=se-FhYI1sZMzKd6lndV_-vNkJ31hX4HY4ZcMUu71l9k,25335 +setuptools/command/install.py,sha256=8doMxeQEDoK4Eco0mO2WlXXzzp9QnsGJQ7Z7yWkZPG8,4705 +setuptools/command/install_egg_info.py,sha256=bMgeIeRiXzQ4DAGPV1328kcjwQjHjOWU4FngAWLV78Q,2203 +setuptools/command/install_lib.py,sha256=Uz42McsyHZAjrB6cw9E7Bz0xsaTbzxnM1PI9CBhiPtE,3875 +setuptools/command/install_scripts.py,sha256=o0jN_ex7yYYk8W5clymTFOXwkFMKzW9q_zd9Npcex7M,2593 +setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 +setuptools/command/py36compat.py,sha256=7yLWzQj179Enx3pJ8V1cDDCzeLMFMd9XJXlK-iZTq5Y,4946 +setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468 +setuptools/command/rotate.py,sha256=SvsQPasezIojPjvMnfkqzh8P0U0tCj0daczF8uc3NQM,2128 +setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 +setuptools/command/sdist.py,sha256=pEMF0GMVuaznNK6GFamK4GSXG9_qef0ic8z7jEsPmKo,5967 +setuptools/command/setopt.py,sha256=okxhqD1NM1nQlbSVDCNv6P7Y7g680sc2r-tUW7wPH1Y,5086 +setuptools/command/test.py,sha256=qGY-Hx1RPCndlVh2rsrEs5479CgmxRsrEflVLr98jVA,8088 +setuptools/command/upload.py,sha256=XT3YFVfYPAmA5qhGg0euluU98ftxRUW-PzKcODMLxUs,462 +setuptools/command/upload_docs.py,sha256=ba5kOyedD_u62weinrxqqnvpuQvBIuamXehJG6tAvO0,7218 +setuptools/config.py,sha256=sm9ZbziX9DlOugcVlIbhqttMJwxwznGEsk82D8MVaDM,23123 +setuptools/dep_util.py,sha256=BDx1BkzNQntvAB4alypHbW5UVBzjqths000PrUL4Zqc,949 +setuptools/depends.py,sha256=iHfZdLdlCu2BllSF9bRg7NU0oqbPWMH8ljm4BuwQDY0,5474 +setuptools/dist.py,sha256=cZtPPzGEhSomPH_vXH_DeCFetjJ9B8Hv8VUCG0KbZh8,43087 +setuptools/errors.py,sha256=MVOcv381HNSajDgEUWzOQ4J6B5BHCBMSjHfaWcEwA1o,524 +setuptools/extension.py,sha256=NMM46XjNdVelWemc0x8CyVKA5Ks6Zm3xTWSA2SS6xZM,1684 +setuptools/extern/__init__.py,sha256=Hhf9W73WAitw9TdRJfDIb6YFjmK56CF61afds1Mg0HY,2407 +setuptools/glob.py,sha256=1oZjbfjAHSXbgdhSuR6YGU8jKob9L8NtEmBYqcPTLYk,4873 +setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 +setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/installer.py,sha256=jbhb7ZVkNV_bSUMgfnLcZw0IHr6REFnKF4o7_1Jqxm0,3567 +setuptools/launch.py,sha256=TyPT-Ic1T2EnYvGO26gfNRP4ysBlrhpbRjQxWsiO414,812 +setuptools/monkey.py,sha256=0e3HdVKXHL415O7np-AUqhEFXPPuDdJKbI47chQ_DE4,5217 +setuptools/msvc.py,sha256=3LLt938e6OR7wWPzIvCQu7LCWZSIKqoKV6w3r8jV3kY,50561 +setuptools/namespaces.py,sha256=PMqGVPXPYQgjUTvEg9bGccRAkIODrQ6NmsDg_fwErwI,3093 +setuptools/package_index.py,sha256=zXDqTrW86wWuzenxvx_XpJMgz3kxqhLSkcijlD5sFmo,39897 +setuptools/py34compat.py,sha256=KYOd6ybRxjBW8NJmYD8t_UyyVmysppFXqHpFLdslGXU,245 +setuptools/sandbox.py,sha256=mR83i-mu-ZUU_7TaMgYCeRSyzkqv8loJ_GR9xhS2DDw,14348 +setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 +setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 +setuptools/unicode_utils.py,sha256=aOOFo4JGwAsiBttGYDsqFS7YqWQeZ2j6DWiCuctR_00,941 +setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 +setuptools/wheel.py,sha256=0P8tSk105uF_Ub-30N2HU2X2v7MKDSdjpeQlRRW3SkI,8288 +setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/REQUESTED b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/WHEEL b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..5bad85fdc1cd08553756d0fb2c7be8b5ad6af7fb --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/entry_points.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..9466bf6320157d79e69d6940e2b09fb08f64da51 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/entry_points.txt @@ -0,0 +1,56 @@ +[distutils.commands] +alias = setuptools.command.alias:alias +bdist_egg = setuptools.command.bdist_egg:bdist_egg +bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm +build_clib = setuptools.command.build_clib:build_clib +build_ext = setuptools.command.build_ext:build_ext +build_py = setuptools.command.build_py:build_py +develop = setuptools.command.develop:develop +dist_info = setuptools.command.dist_info:dist_info +easy_install = setuptools.command.easy_install:easy_install +egg_info = setuptools.command.egg_info:egg_info +install = setuptools.command.install:install +install_egg_info = setuptools.command.install_egg_info:install_egg_info +install_lib = setuptools.command.install_lib:install_lib +install_scripts = setuptools.command.install_scripts:install_scripts +rotate = setuptools.command.rotate:rotate +saveopts = setuptools.command.saveopts:saveopts +sdist = setuptools.command.sdist:sdist +setopt = setuptools.command.setopt:setopt +test = setuptools.command.test:test +upload_docs = setuptools.command.upload_docs:upload_docs + +[distutils.setup_keywords] +dependency_links = setuptools.dist:assert_string_list +eager_resources = setuptools.dist:assert_string_list +entry_points = setuptools.dist:check_entry_points +exclude_package_data = setuptools.dist:check_package_data +extras_require = setuptools.dist:check_extras +include_package_data = setuptools.dist:assert_bool +install_requires = setuptools.dist:check_requirements +namespace_packages = setuptools.dist:check_nsp +package_data = setuptools.dist:check_package_data +packages = setuptools.dist:check_packages +python_requires = setuptools.dist:check_specifier +setup_requires = setuptools.dist:check_requirements +test_loader = setuptools.dist:check_importable +test_runner = setuptools.dist:check_importable +test_suite = setuptools.dist:check_test_suite +tests_require = setuptools.dist:check_requirements +use_2to3 = setuptools.dist:invalid_unless_false +zip_safe = setuptools.dist:assert_bool + +[egg_info.writers] +PKG-INFO = setuptools.command.egg_info:write_pkg_info +dependency_links.txt = setuptools.command.egg_info:overwrite_arg +depends.txt = setuptools.command.egg_info:warn_depends_obsolete +eager_resources.txt = setuptools.command.egg_info:overwrite_arg +entry_points.txt = setuptools.command.egg_info:write_entries +namespace_packages.txt = setuptools.command.egg_info:overwrite_arg +requires.txt = setuptools.command.egg_info:write_requirements +top_level.txt = setuptools.command.egg_info:write_toplevel_names + +[setuptools.finalize_distribution_options] +keywords = setuptools.dist:Distribution._finalize_setup_keywords +parent_finalize = setuptools.dist:_Distribution.finalize_options + diff --git a/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/top_level.txt b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..b5ac1070294b478b7cc2ce677207ee08813bfa37 --- /dev/null +++ b/astra_scene_test/Scene_04/.venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/top_level.txt @@ -0,0 +1,3 @@ +_distutils_hack +pkg_resources +setuptools diff --git a/astra_scene_test/Scene_04/assets/black_car_contact.json b/astra_scene_test/Scene_04/assets/black_car_contact.json new file mode 100644 index 0000000000000000000000000000000000000000..ddc888da169e18c6d322790b1245c7b6e2df3fce --- /dev/null +++ b/astra_scene_test/Scene_04/assets/black_car_contact.json @@ -0,0 +1,113 @@ +{ + "parameters": { + "x": -15.531964092033139, + "y": 17.950452298140377, + "scale_x": 1.8999999994018648, + "scale_y": 0.7213880898277021, + "scale_z": 1.3220152359897148, + "yaw": -0.7048189360173098 + }, + "tire_contact_z": -0.08725300557532119, + "observations": { + "front_wheel": { + "1": [ + 242, + 583 + ], + "17": [ + 114, + 599 + ] + }, + "rear_wheel": { + "1": [ + 309, + 570 + ], + "17": [ + 217, + 580 + ], + "33": [ + 69, + 602 + ] + }, + "roof_front": { + "1": [ + 245, + 522 + ], + "17": [ + 147, + 522 + ] + }, + "roof_rear": { + "1": [ + 306, + 516 + ], + "17": [ + 206, + 517 + ], + "33": [ + 65, + 521 + ] + } + }, + "predictions": { + "front_wheel": { + "1": [ + 241.32406595288074, + 573.3415605963328 + ], + "17": [ + 111.9287777581768, + 589.8492386989836 + ] + }, + "rear_wheel": { + "1": [ + 310.8717086394684, + 566.8577877138214 + ], + "17": [ + 218.7975145158942, + 579.6674458434625 + ], + "33": [ + 68.84690460873287, + 599.4260148235435 + ] + }, + "roof_front": { + "1": [ + 261.0693610177214, + 521.9140595859122 + ], + "17": [ + 148.59168173831188, + 521.3518846330073 + ] + }, + "roof_rear": { + "1": [ + 300.8313886399866, + 521.2227841679234 + ], + "17": [ + 208.38026205506856, + 520.687831087386 + ], + "33": [ + 55.92605625947192, + 519.859047978329 + ] + } + }, + "cost": 142.21185372392205, + "note": "Static pose, pitch0 and both tire bottoms on z0. Camera fixed; metric shape remains ambiguous." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/camera_motion.json b/astra_scene_test/Scene_04/assets/camera_motion.json new file mode 100644 index 0000000000000000000000000000000000000000..d4f9145379a1d00b8857a89f9a2c96419775dc12 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/camera_motion.json @@ -0,0 +1,977 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 640.1707869459426, + 514.7179664031315 + ], + "optimizer_success": true, + "optimizer_nfev": 80, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 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, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.008802878977105338, + 0.018049922561147555, + 0.026359950391841133, + 0.034659806714658965, + 0.04327254329396077, + 0.05185140669395555, + 0.06002252695435185, + 0.06818098806171356, + 0.07636963537143969, + 0.08440271418264896, + 0.09323046245654798, + 0.10186810001346316, + 0.11087988568555436, + 0.12019423983009424, + 0.12913288611005386, + 0.13756848658320978, + 0.14512809590276202, + 0.15335421394424342, + 0.16130554758231602, + 0.16897635195394634, + 0.1770232367973462, + 0.18423940066545716, + 0.1915996698294535, + 0.19887779004285744, + 0.20571824485769807, + 0.21289980641837597, + 0.21971259292895534, + 0.22657553773609587, + 0.23358528701912837, + 0.24107258112826913, + 0.2487465357349035, + 0.25710216938241126, + 0.2646146178746809, + 0.273357316077707, + 0.2821914998830155, + 0.29051339408179966, + 0.29901428038231426, + 0.30803816091337044, + 0.31683397815242337, + 0.32595859862392734, + 0.33394999472224235, + 0.34199779634837263, + 0.35010782356486364, + 0.35813393654045206, + 0.36558144966797484, + 0.37261794577795226, + 0.38019217248507076, + 0.38831783381560425, + 0.3954755984880578, + 0.40341715705859726, + 0.4111260521557778, + 0.4189730280635394, + 0.4268682898406424, + 0.4353304914814911, + 0.4439247301639684, + 0.45241538388437325, + 0.4603716190441369, + 0.4687515859830776, + 0.4768854149879135, + 0.4846476375944465, + 0.49194061003410755, + 0.5000059572148312, + 0.5078899465428204, + 0.5153858724278887, + 0.5234472332349355, + 0.5310827233164359, + 0.5387949605443769, + 0.5466763184488568, + 0.5546173953729719, + 0.5624162937184094, + 0.5704564863797402, + 0.5783015694155771, + 0.5859831464314267, + 0.593887880540007, + 0.6019623555952226, + 0.6097046132245869, + 0.6173664539243255, + 0.6253831559700301, + 0.6335348223110313, + 0.6416748670059164, + 0.6497971592054369, + 0.6576087995430274, + 0.6653716666864264, + 0.6733886841975338, + 0.6814958698589496, + 0.689622841944666, + 0.6975194787735914, + 0.705664010997911, + 0.7136295442053926, + 0.7217867063818459, + 0.7299676234268601, + 0.7380504230799666, + 0.7461040306693285, + 0.7542214959487574, + 0.7624604912194031, + 0.7704617097985091, + 0.7783260784458794, + 0.7862920442803105, + 0.7942324240793832, + 0.8021821267951654, + 0.8099710172195204, + 0.817897537301741, + 0.8257831017896716, + 0.8336938907909718, + 0.841385111942113, + 0.8491498884125602, + 0.8569625686000244, + 0.864629449007571, + 0.87223558305206, + 0.8797293636117074, + 0.8872857604617295, + 0.8947980522821005, + 0.9023159668909211, + 0.9097835817806497, + 0.9170625401734516, + 0.9242072038888995, + 0.931372541601249, + 0.9384905519816475, + 0.9455689093454717, + 0.9527437058038144, + 0.9596173495662252, + 0.9665694943526268, + 0.9735491499000904, + 0.9803042881066719, + 0.9867806371001577, + 0.9932856650654416, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + -0.00020943973719140537, + 0.0008611195324010923, + -0.00040989375633570217 + ], + [ + -0.0003550067395339619, + 0.0017914160330369019, + -0.0008066651549705927 + ], + [ + -0.00035854347111521565, + 0.0026132167507584994, + -0.0011566295263596895 + ], + [ + -0.00010921678617277259, + 0.0033359412919149217, + -0.0012020174683988 + ], + [ + 0.0005213716427872679, + 0.0040104128413097385, + -0.00078155818554085 + ], + [ + 0.001213278223099616, + 0.004723641921671793, + -0.00032010073680318197 + ], + [ + 0.0018548900135928127, + 0.005370949192561541, + 0.0002893410896504775 + ], + [ + 0.00224774362252664, + 0.005924292116590759, + 0.001036639327532078 + ], + [ + 0.00230038052223272, + 0.006357633460308338, + 0.00138576150314999 + ], + [ + 0.00228796577867328, + 0.0067208239150738, + 0.0014748200038570754 + ], + [ + 0.0020850950656124204, + 0.007189619853540884, + 0.001657280675078751 + ], + [ + 0.0017952544161472018, + 0.00776324941179226, + 0.002403337789174542 + ], + [ + 0.0012234322395989767, + 0.008430330339027613, + 0.003136106238334495 + ], + [ + 0.0004912200928232764, + 0.00920871018212311, + 0.004015792226603442 + ], + [ + -0.0003018576434170425, + 0.00998497000720928, + 0.00484312618493851 + ], + [ + -0.0011910404166182208, + 0.010684564529596154, + 0.0055015986704040456 + ], + [ + -0.0016795136690813924, + 0.010954496792200253, + 0.006025065216310688 + ], + [ + -0.002078026256812736, + 0.011150124056444584, + 0.0068715895054009 + ], + [ + -0.0019442622822255333, + 0.0111920436258316, + 0.007450659255133659 + ], + [ + -0.0012751693143457162, + 0.011170878274448895, + 0.007597792575330646 + ], + [ + -0.0003657172133730148, + 0.011059861229784402, + 0.007341823087435605 + ], + [ + 0.0009525617855822171, + 0.010948298715698532, + 0.006998710734403429 + ], + [ + 0.0026938980361078833, + 0.010879060176285192, + 0.006640000007910429 + ], + [ + 0.004378268063234446, + 0.010671183472230263, + 0.0058501298063352095 + ], + [ + 0.006386602184111396, + 0.010121237236127763, + 0.004885857552554393 + ], + [ + 0.008263979687207825, + 0.009497824427335086, + 0.0037465198577321403 + ], + [ + 0.010248370215158878, + 0.00891944987235332, + 0.002663139189307138 + ], + [ + 0.01234874690050559, + 0.008489308304006203, + 0.00193126547916533 + ], + [ + 0.014266419222824971, + 0.008347460522798417, + 0.0011040547959791337 + ], + [ + 0.015882240167338045, + 0.008320111135362868, + 0.00018372150314470176 + ], + [ + 0.017018356567272384, + 0.00816085273641958, + -0.0006884180882443881 + ], + [ + 0.01763652280787367, + 0.008012416918407749, + -0.0014764642394765961 + ], + [ + 0.01763410797683712, + 0.007704692550791586, + -0.0026077227432538064 + ], + [ + 0.01736766873609974, + 0.007569553908238818, + -0.003432903701292357 + ], + [ + 0.016801373077826004, + 0.007609386979917657, + -0.00371913474447559 + ], + [ + 0.016158530236097778, + 0.007717638733442985, + -0.0037412839024816872 + ], + [ + 0.015443683754173362, + 0.00788446294758706, + -0.003798301662918307 + ], + [ + 0.014622826792910448, + 0.008198624636637274, + -0.004046844672165718 + ], + [ + 0.013643923288896324, + 0.008482499951652052, + -0.003972536421868014 + ], + [ + 0.012672512318890548, + 0.008782486433992596, + -0.003629059321305456 + ], + [ + 0.011674852147623704, + 0.0089059463612976, + -0.003594446836219208 + ], + [ + 0.010990854546819527, + 0.008974687779897786, + -0.0035324537273584717 + ], + [ + 0.010845710774731508, + 0.009091922315111262, + -0.0035261409760765647 + ], + [ + 0.0110930178133261, + 0.009204630301248833, + -0.003522622808314311 + ], + [ + 0.011845252807042524, + 0.009318539446861476, + -0.0034340664392589733 + ], + [ + 0.012947668230442617, + 0.00953404255218493, + -0.0030207954045265786 + ], + [ + 0.01409709216320152, + 0.009920981575635755, + -0.0023577104333276254 + ], + [ + 0.0152720514738302, + 0.010479784293087971, + -0.0016160593426945268 + ], + [ + 0.01628595538843541, + 0.010941367065369328, + -0.0009746038167461028 + ], + [ + 0.017137921977760213, + 0.011334614692952102, + -0.0005968986638529161 + ], + [ + 0.01757467111738436, + 0.011379949449609126, + -0.0006850197820164679 + ], + [ + 0.017301928399429872, + 0.011269461447619627, + -0.0009653018615329485 + ], + [ + 0.01654798584007431, + 0.011059702788572597, + -0.0012557972358060155 + ], + [ + 0.015472734933379452, + 0.01089011275751993, + -0.0013192612802725857 + ], + [ + 0.013945573109485052, + 0.010879234842203747, + -0.000980883771796475 + ], + [ + 0.012234677607333646, + 0.010968443417469399, + -0.0004865450450693065 + ], + [ + 0.010244881355364752, + 0.011130421420241138, + -0.00010333597388665863 + ], + [ + 0.008304786504793838, + 0.011394669689679497, + 0.0002906440154662258 + ], + [ + 0.006491835320376835, + 0.01169241497907603, + 0.0007321230032834831 + ], + [ + 0.004962740949130399, + 0.011901966919555576, + 0.00087659133183212 + ], + [ + 0.003799508082929694, + 0.012023890201594523, + 0.0014198083787231284 + ], + [ + 0.003107279496734778, + 0.012180137120775914, + 0.0020947421039554646 + ], + [ + 0.002900021389116785, + 0.012464348124945932, + 0.002642148341431348 + ], + [ + 0.002863457859054484, + 0.012750584445496068, + 0.0034520818165749713 + ], + [ + 0.003293798968440998, + 0.013162479122519376, + 0.0036656032948420556 + ], + [ + 0.0037711010322344645, + 0.01349394491371582, + 0.0041716605959196285 + ], + [ + 0.004273690193006163, + 0.013773823866548171, + 0.00454150992225259 + ], + [ + 0.004789018203343772, + 0.013999103830877513, + 0.004939950871607632 + ], + [ + 0.005221509958257419, + 0.014116889662524003, + 0.005043781239317572 + ], + [ + 0.0055463125189956315, + 0.01417969845753781, + 0.005196519582051358 + ], + [ + 0.005708657871658359, + 0.014184714060372926, + 0.0054491250731988385 + ], + [ + 0.005693100730550619, + 0.014236922755496246, + 0.005583381445346449 + ], + [ + 0.005619150606357115, + 0.014250969224822949, + 0.005387475294418588 + ], + [ + 0.005394090337662084, + 0.014242285268622921, + 0.005336939191310482 + ], + [ + 0.004939732420129312, + 0.014232421703819078, + 0.0051183765381283405 + ], + [ + 0.004519171501097994, + 0.01424852123823956, + 0.004319833657084627 + ], + [ + 0.004115801582518156, + 0.014296992350175737, + 0.0033923702731040646 + ], + [ + 0.0037413499785092707, + 0.014387311758723771, + 0.002472868429757525 + ], + [ + 0.003449194931436583, + 0.014440732153533774, + 0.0015955870568848765 + ], + [ + 0.0032532869847308288, + 0.014568374829030799, + 0.000763004216584281 + ], + [ + 0.0036680368928423746, + 0.014779898967876745, + -0.0005788164168664697 + ], + [ + 0.004308702901341574, + 0.015141646204507937, + -0.0013105530036512318 + ], + [ + 0.005057204763893026, + 0.015597171911848473, + -0.0017834480890574764 + ], + [ + 0.005821345548153084, + 0.016076966029129537, + -0.0021276018742495796 + ], + [ + 0.006706580674343547, + 0.016585636746680447, + -0.0023504893711797275 + ], + [ + 0.007703125526741992, + 0.017180409619238005, + -0.002566383353782403 + ], + [ + 0.008749930173653754, + 0.017827581254219266, + -0.0026624831753859694 + ], + [ + 0.009724637560048151, + 0.018447415714245713, + -0.002456395269454268 + ], + [ + 0.010965338976575744, + 0.019153937817699036, + -0.002616092172714449 + ], + [ + 0.01232007238511617, + 0.019912453077559754, + -0.0023766212688304806 + ], + [ + 0.013698396753107031, + 0.02065424632764299, + -0.0018468473913453016 + ], + [ + 0.0149719380870252, + 0.021320186666976694, + -0.0012100733354537458 + ], + [ + 0.015914701969439605, + 0.021947121986267953, + -0.0005331001167641826 + ], + [ + 0.016540292749414848, + 0.022544089308573243, + 5.754334127314092e-05 + ], + [ + 0.016773832137651778, + 0.022975758390590656, + 0.0005417094197838527 + ], + [ + 0.01657872518228831, + 0.023394415244521433, + 0.0009939050380583082 + ], + [ + 0.016374807968436204, + 0.02376941203043557, + 0.001157661535916056 + ], + [ + 0.015878078609295253, + 0.0240107425648432, + 0.0013027021330043794 + ], + [ + 0.01527335582412031, + 0.024198531192760737, + 0.0012180259911261562 + ], + [ + 0.014574741295225804, + 0.024403501767481763, + 0.0012771314474135601 + ], + [ + 0.013837307812012907, + 0.02460079412309688, + 0.001341971266355347 + ], + [ + 0.013139632467955221, + 0.024806944281998658, + 0.0017122253519052284 + ], + [ + 0.012632356381110356, + 0.02511388825715575, + 0.002183008402681762 + ], + [ + 0.012199589517405858, + 0.02533129144158546, + 0.002858604114990466 + ], + [ + 0.01208890994114536, + 0.025572031873596975, + 0.0030823366004223956 + ], + [ + 0.012174200535068418, + 0.025659082498566462, + 0.003429965684394651 + ], + [ + 0.012240144005062152, + 0.025536890027917022, + 0.00382894441099053 + ], + [ + 0.01247784010770409, + 0.025349793734286086, + 0.004147123404976383 + ], + [ + 0.012673093385848037, + 0.02517443228108811, + 0.004511725455951684 + ], + [ + 0.012850456144892454, + 0.02504258813617945, + 0.004789654679724465 + ], + [ + 0.012906142500872261, + 0.024968738837306055, + 0.005010805341839593 + ], + [ + 0.01297107627292627, + 0.024838152587685007, + 0.005258001294290399 + ], + [ + 0.012765685608374409, + 0.024648683128228852, + 0.005611371852758273 + ], + [ + 0.012413887870068252, + 0.02431935347772094, + 0.005960986850837967 + ], + [ + 0.011894791362841363, + 0.023755497050768966, + 0.005991780753745806 + ], + [ + 0.011290168211886218, + 0.023090820938008655, + 0.0057954169073208914 + ], + [ + 0.010762869446809544, + 0.02246673628089443, + 0.00554184152597768 + ], + [ + 0.010250762426635791, + 0.021861890151468794, + 0.005175197666267611 + ], + [ + 0.009952824757429788, + 0.021457563891432656, + 0.004636552122972042 + ], + [ + 0.009737385776637825, + 0.021337103777977807, + 0.004343760768897358 + ], + [ + 0.009881648876112141, + 0.021124208045683757, + 0.004124772095343848 + ], + [ + 0.010028692539991089, + 0.02108435362186473, + 0.004176762871203023 + ], + [ + 0.010239301026452582, + 0.021073716988385983, + 0.004238418867819996 + ], + [ + 0.010556366726739045, + 0.020922265676328124, + 0.004152280218195467 + ], + [ + 0.01098993597280476, + 0.020572560343156937, + 0.004039373036277026 + ], + [ + 0.011337860432733448, + 0.020275336821910704, + 0.0033549803399676044 + ], + [ + 0.011789093761427921, + 0.020360958760674838, + 0.002602562131042079 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2659399244805773, + 2.6948534930745938, + 3.652966722764379, + 2.68590390287027, + 2.018685034835302, + 3.3343229630915614, + 2.229130638539127, + 3.1888415072387786, + 2.569831635740863, + 1.700245339440428, + 2.6668575747314827, + 2.7319867441904115, + 1.523781585706796, + 1.7729100288731323, + 1.2970368078554588, + 1.7051438641295515, + 2.1266169413023075, + 3.0725443851634133, + 2.0936647017552112, + 2.8429376368547863, + 3.084397710654423, + 2.1887027902769978, + 2.0950281835696836, + 2.6968436912447915, + 2.2944323232075217 + ], + "median_reprojection_error_pixels": 0.9941164845058215, + "p90_reprojection_error_pixels": 2.901010989230633, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks.", + "refinement": "All128 source frames; fixed3D landmarks; original17 calibration knots held exact; small isolated tracker fluctuations filtered; no artificial camera shake." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/camera_motion_full.json b/astra_scene_test/Scene_04/assets/camera_motion_full.json new file mode 100644 index 0000000000000000000000000000000000000000..d4f9145379a1d00b8857a89f9a2c96419775dc12 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/camera_motion_full.json @@ -0,0 +1,977 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 640.1707869459426, + 514.7179664031315 + ], + "optimizer_success": true, + "optimizer_nfev": 80, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 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, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.008802878977105338, + 0.018049922561147555, + 0.026359950391841133, + 0.034659806714658965, + 0.04327254329396077, + 0.05185140669395555, + 0.06002252695435185, + 0.06818098806171356, + 0.07636963537143969, + 0.08440271418264896, + 0.09323046245654798, + 0.10186810001346316, + 0.11087988568555436, + 0.12019423983009424, + 0.12913288611005386, + 0.13756848658320978, + 0.14512809590276202, + 0.15335421394424342, + 0.16130554758231602, + 0.16897635195394634, + 0.1770232367973462, + 0.18423940066545716, + 0.1915996698294535, + 0.19887779004285744, + 0.20571824485769807, + 0.21289980641837597, + 0.21971259292895534, + 0.22657553773609587, + 0.23358528701912837, + 0.24107258112826913, + 0.2487465357349035, + 0.25710216938241126, + 0.2646146178746809, + 0.273357316077707, + 0.2821914998830155, + 0.29051339408179966, + 0.29901428038231426, + 0.30803816091337044, + 0.31683397815242337, + 0.32595859862392734, + 0.33394999472224235, + 0.34199779634837263, + 0.35010782356486364, + 0.35813393654045206, + 0.36558144966797484, + 0.37261794577795226, + 0.38019217248507076, + 0.38831783381560425, + 0.3954755984880578, + 0.40341715705859726, + 0.4111260521557778, + 0.4189730280635394, + 0.4268682898406424, + 0.4353304914814911, + 0.4439247301639684, + 0.45241538388437325, + 0.4603716190441369, + 0.4687515859830776, + 0.4768854149879135, + 0.4846476375944465, + 0.49194061003410755, + 0.5000059572148312, + 0.5078899465428204, + 0.5153858724278887, + 0.5234472332349355, + 0.5310827233164359, + 0.5387949605443769, + 0.5466763184488568, + 0.5546173953729719, + 0.5624162937184094, + 0.5704564863797402, + 0.5783015694155771, + 0.5859831464314267, + 0.593887880540007, + 0.6019623555952226, + 0.6097046132245869, + 0.6173664539243255, + 0.6253831559700301, + 0.6335348223110313, + 0.6416748670059164, + 0.6497971592054369, + 0.6576087995430274, + 0.6653716666864264, + 0.6733886841975338, + 0.6814958698589496, + 0.689622841944666, + 0.6975194787735914, + 0.705664010997911, + 0.7136295442053926, + 0.7217867063818459, + 0.7299676234268601, + 0.7380504230799666, + 0.7461040306693285, + 0.7542214959487574, + 0.7624604912194031, + 0.7704617097985091, + 0.7783260784458794, + 0.7862920442803105, + 0.7942324240793832, + 0.8021821267951654, + 0.8099710172195204, + 0.817897537301741, + 0.8257831017896716, + 0.8336938907909718, + 0.841385111942113, + 0.8491498884125602, + 0.8569625686000244, + 0.864629449007571, + 0.87223558305206, + 0.8797293636117074, + 0.8872857604617295, + 0.8947980522821005, + 0.9023159668909211, + 0.9097835817806497, + 0.9170625401734516, + 0.9242072038888995, + 0.931372541601249, + 0.9384905519816475, + 0.9455689093454717, + 0.9527437058038144, + 0.9596173495662252, + 0.9665694943526268, + 0.9735491499000904, + 0.9803042881066719, + 0.9867806371001577, + 0.9932856650654416, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + -0.00020943973719140537, + 0.0008611195324010923, + -0.00040989375633570217 + ], + [ + -0.0003550067395339619, + 0.0017914160330369019, + -0.0008066651549705927 + ], + [ + -0.00035854347111521565, + 0.0026132167507584994, + -0.0011566295263596895 + ], + [ + -0.00010921678617277259, + 0.0033359412919149217, + -0.0012020174683988 + ], + [ + 0.0005213716427872679, + 0.0040104128413097385, + -0.00078155818554085 + ], + [ + 0.001213278223099616, + 0.004723641921671793, + -0.00032010073680318197 + ], + [ + 0.0018548900135928127, + 0.005370949192561541, + 0.0002893410896504775 + ], + [ + 0.00224774362252664, + 0.005924292116590759, + 0.001036639327532078 + ], + [ + 0.00230038052223272, + 0.006357633460308338, + 0.00138576150314999 + ], + [ + 0.00228796577867328, + 0.0067208239150738, + 0.0014748200038570754 + ], + [ + 0.0020850950656124204, + 0.007189619853540884, + 0.001657280675078751 + ], + [ + 0.0017952544161472018, + 0.00776324941179226, + 0.002403337789174542 + ], + [ + 0.0012234322395989767, + 0.008430330339027613, + 0.003136106238334495 + ], + [ + 0.0004912200928232764, + 0.00920871018212311, + 0.004015792226603442 + ], + [ + -0.0003018576434170425, + 0.00998497000720928, + 0.00484312618493851 + ], + [ + -0.0011910404166182208, + 0.010684564529596154, + 0.0055015986704040456 + ], + [ + -0.0016795136690813924, + 0.010954496792200253, + 0.006025065216310688 + ], + [ + -0.002078026256812736, + 0.011150124056444584, + 0.0068715895054009 + ], + [ + -0.0019442622822255333, + 0.0111920436258316, + 0.007450659255133659 + ], + [ + -0.0012751693143457162, + 0.011170878274448895, + 0.007597792575330646 + ], + [ + -0.0003657172133730148, + 0.011059861229784402, + 0.007341823087435605 + ], + [ + 0.0009525617855822171, + 0.010948298715698532, + 0.006998710734403429 + ], + [ + 0.0026938980361078833, + 0.010879060176285192, + 0.006640000007910429 + ], + [ + 0.004378268063234446, + 0.010671183472230263, + 0.0058501298063352095 + ], + [ + 0.006386602184111396, + 0.010121237236127763, + 0.004885857552554393 + ], + [ + 0.008263979687207825, + 0.009497824427335086, + 0.0037465198577321403 + ], + [ + 0.010248370215158878, + 0.00891944987235332, + 0.002663139189307138 + ], + [ + 0.01234874690050559, + 0.008489308304006203, + 0.00193126547916533 + ], + [ + 0.014266419222824971, + 0.008347460522798417, + 0.0011040547959791337 + ], + [ + 0.015882240167338045, + 0.008320111135362868, + 0.00018372150314470176 + ], + [ + 0.017018356567272384, + 0.00816085273641958, + -0.0006884180882443881 + ], + [ + 0.01763652280787367, + 0.008012416918407749, + -0.0014764642394765961 + ], + [ + 0.01763410797683712, + 0.007704692550791586, + -0.0026077227432538064 + ], + [ + 0.01736766873609974, + 0.007569553908238818, + -0.003432903701292357 + ], + [ + 0.016801373077826004, + 0.007609386979917657, + -0.00371913474447559 + ], + [ + 0.016158530236097778, + 0.007717638733442985, + -0.0037412839024816872 + ], + [ + 0.015443683754173362, + 0.00788446294758706, + -0.003798301662918307 + ], + [ + 0.014622826792910448, + 0.008198624636637274, + -0.004046844672165718 + ], + [ + 0.013643923288896324, + 0.008482499951652052, + -0.003972536421868014 + ], + [ + 0.012672512318890548, + 0.008782486433992596, + -0.003629059321305456 + ], + [ + 0.011674852147623704, + 0.0089059463612976, + -0.003594446836219208 + ], + [ + 0.010990854546819527, + 0.008974687779897786, + -0.0035324537273584717 + ], + [ + 0.010845710774731508, + 0.009091922315111262, + -0.0035261409760765647 + ], + [ + 0.0110930178133261, + 0.009204630301248833, + -0.003522622808314311 + ], + [ + 0.011845252807042524, + 0.009318539446861476, + -0.0034340664392589733 + ], + [ + 0.012947668230442617, + 0.00953404255218493, + -0.0030207954045265786 + ], + [ + 0.01409709216320152, + 0.009920981575635755, + -0.0023577104333276254 + ], + [ + 0.0152720514738302, + 0.010479784293087971, + -0.0016160593426945268 + ], + [ + 0.01628595538843541, + 0.010941367065369328, + -0.0009746038167461028 + ], + [ + 0.017137921977760213, + 0.011334614692952102, + -0.0005968986638529161 + ], + [ + 0.01757467111738436, + 0.011379949449609126, + -0.0006850197820164679 + ], + [ + 0.017301928399429872, + 0.011269461447619627, + -0.0009653018615329485 + ], + [ + 0.01654798584007431, + 0.011059702788572597, + -0.0012557972358060155 + ], + [ + 0.015472734933379452, + 0.01089011275751993, + -0.0013192612802725857 + ], + [ + 0.013945573109485052, + 0.010879234842203747, + -0.000980883771796475 + ], + [ + 0.012234677607333646, + 0.010968443417469399, + -0.0004865450450693065 + ], + [ + 0.010244881355364752, + 0.011130421420241138, + -0.00010333597388665863 + ], + [ + 0.008304786504793838, + 0.011394669689679497, + 0.0002906440154662258 + ], + [ + 0.006491835320376835, + 0.01169241497907603, + 0.0007321230032834831 + ], + [ + 0.004962740949130399, + 0.011901966919555576, + 0.00087659133183212 + ], + [ + 0.003799508082929694, + 0.012023890201594523, + 0.0014198083787231284 + ], + [ + 0.003107279496734778, + 0.012180137120775914, + 0.0020947421039554646 + ], + [ + 0.002900021389116785, + 0.012464348124945932, + 0.002642148341431348 + ], + [ + 0.002863457859054484, + 0.012750584445496068, + 0.0034520818165749713 + ], + [ + 0.003293798968440998, + 0.013162479122519376, + 0.0036656032948420556 + ], + [ + 0.0037711010322344645, + 0.01349394491371582, + 0.0041716605959196285 + ], + [ + 0.004273690193006163, + 0.013773823866548171, + 0.00454150992225259 + ], + [ + 0.004789018203343772, + 0.013999103830877513, + 0.004939950871607632 + ], + [ + 0.005221509958257419, + 0.014116889662524003, + 0.005043781239317572 + ], + [ + 0.0055463125189956315, + 0.01417969845753781, + 0.005196519582051358 + ], + [ + 0.005708657871658359, + 0.014184714060372926, + 0.0054491250731988385 + ], + [ + 0.005693100730550619, + 0.014236922755496246, + 0.005583381445346449 + ], + [ + 0.005619150606357115, + 0.014250969224822949, + 0.005387475294418588 + ], + [ + 0.005394090337662084, + 0.014242285268622921, + 0.005336939191310482 + ], + [ + 0.004939732420129312, + 0.014232421703819078, + 0.0051183765381283405 + ], + [ + 0.004519171501097994, + 0.01424852123823956, + 0.004319833657084627 + ], + [ + 0.004115801582518156, + 0.014296992350175737, + 0.0033923702731040646 + ], + [ + 0.0037413499785092707, + 0.014387311758723771, + 0.002472868429757525 + ], + [ + 0.003449194931436583, + 0.014440732153533774, + 0.0015955870568848765 + ], + [ + 0.0032532869847308288, + 0.014568374829030799, + 0.000763004216584281 + ], + [ + 0.0036680368928423746, + 0.014779898967876745, + -0.0005788164168664697 + ], + [ + 0.004308702901341574, + 0.015141646204507937, + -0.0013105530036512318 + ], + [ + 0.005057204763893026, + 0.015597171911848473, + -0.0017834480890574764 + ], + [ + 0.005821345548153084, + 0.016076966029129537, + -0.0021276018742495796 + ], + [ + 0.006706580674343547, + 0.016585636746680447, + -0.0023504893711797275 + ], + [ + 0.007703125526741992, + 0.017180409619238005, + -0.002566383353782403 + ], + [ + 0.008749930173653754, + 0.017827581254219266, + -0.0026624831753859694 + ], + [ + 0.009724637560048151, + 0.018447415714245713, + -0.002456395269454268 + ], + [ + 0.010965338976575744, + 0.019153937817699036, + -0.002616092172714449 + ], + [ + 0.01232007238511617, + 0.019912453077559754, + -0.0023766212688304806 + ], + [ + 0.013698396753107031, + 0.02065424632764299, + -0.0018468473913453016 + ], + [ + 0.0149719380870252, + 0.021320186666976694, + -0.0012100733354537458 + ], + [ + 0.015914701969439605, + 0.021947121986267953, + -0.0005331001167641826 + ], + [ + 0.016540292749414848, + 0.022544089308573243, + 5.754334127314092e-05 + ], + [ + 0.016773832137651778, + 0.022975758390590656, + 0.0005417094197838527 + ], + [ + 0.01657872518228831, + 0.023394415244521433, + 0.0009939050380583082 + ], + [ + 0.016374807968436204, + 0.02376941203043557, + 0.001157661535916056 + ], + [ + 0.015878078609295253, + 0.0240107425648432, + 0.0013027021330043794 + ], + [ + 0.01527335582412031, + 0.024198531192760737, + 0.0012180259911261562 + ], + [ + 0.014574741295225804, + 0.024403501767481763, + 0.0012771314474135601 + ], + [ + 0.013837307812012907, + 0.02460079412309688, + 0.001341971266355347 + ], + [ + 0.013139632467955221, + 0.024806944281998658, + 0.0017122253519052284 + ], + [ + 0.012632356381110356, + 0.02511388825715575, + 0.002183008402681762 + ], + [ + 0.012199589517405858, + 0.02533129144158546, + 0.002858604114990466 + ], + [ + 0.01208890994114536, + 0.025572031873596975, + 0.0030823366004223956 + ], + [ + 0.012174200535068418, + 0.025659082498566462, + 0.003429965684394651 + ], + [ + 0.012240144005062152, + 0.025536890027917022, + 0.00382894441099053 + ], + [ + 0.01247784010770409, + 0.025349793734286086, + 0.004147123404976383 + ], + [ + 0.012673093385848037, + 0.02517443228108811, + 0.004511725455951684 + ], + [ + 0.012850456144892454, + 0.02504258813617945, + 0.004789654679724465 + ], + [ + 0.012906142500872261, + 0.024968738837306055, + 0.005010805341839593 + ], + [ + 0.01297107627292627, + 0.024838152587685007, + 0.005258001294290399 + ], + [ + 0.012765685608374409, + 0.024648683128228852, + 0.005611371852758273 + ], + [ + 0.012413887870068252, + 0.02431935347772094, + 0.005960986850837967 + ], + [ + 0.011894791362841363, + 0.023755497050768966, + 0.005991780753745806 + ], + [ + 0.011290168211886218, + 0.023090820938008655, + 0.0057954169073208914 + ], + [ + 0.010762869446809544, + 0.02246673628089443, + 0.00554184152597768 + ], + [ + 0.010250762426635791, + 0.021861890151468794, + 0.005175197666267611 + ], + [ + 0.009952824757429788, + 0.021457563891432656, + 0.004636552122972042 + ], + [ + 0.009737385776637825, + 0.021337103777977807, + 0.004343760768897358 + ], + [ + 0.009881648876112141, + 0.021124208045683757, + 0.004124772095343848 + ], + [ + 0.010028692539991089, + 0.02108435362186473, + 0.004176762871203023 + ], + [ + 0.010239301026452582, + 0.021073716988385983, + 0.004238418867819996 + ], + [ + 0.010556366726739045, + 0.020922265676328124, + 0.004152280218195467 + ], + [ + 0.01098993597280476, + 0.020572560343156937, + 0.004039373036277026 + ], + [ + 0.011337860432733448, + 0.020275336821910704, + 0.0033549803399676044 + ], + [ + 0.011789093761427921, + 0.020360958760674838, + 0.002602562131042079 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2659399244805773, + 2.6948534930745938, + 3.652966722764379, + 2.68590390287027, + 2.018685034835302, + 3.3343229630915614, + 2.229130638539127, + 3.1888415072387786, + 2.569831635740863, + 1.700245339440428, + 2.6668575747314827, + 2.7319867441904115, + 1.523781585706796, + 1.7729100288731323, + 1.2970368078554588, + 1.7051438641295515, + 2.1266169413023075, + 3.0725443851634133, + 2.0936647017552112, + 2.8429376368547863, + 3.084397710654423, + 2.1887027902769978, + 2.0950281835696836, + 2.6968436912447915, + 2.2944323232075217 + ], + "median_reprojection_error_pixels": 0.9941164845058215, + "p90_reprojection_error_pixels": 2.901010989230633, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks.", + "refinement": "All128 source frames; fixed3D landmarks; original17 calibration knots held exact; small isolated tracker fluctuations filtered; no artificial camera shake." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/config.json b/astra_scene_test/Scene_04/assets/config.json new file mode 100644 index 0000000000000000000000000000000000000000..e2c4ddbcbd673a25a2583593da5599c44cef3604 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/config.json @@ -0,0 +1,135 @@ +{ + "fov": 120.0, + "pitch": 4.726014521736058, + "yaw": 3.83, + "camera_height": 2.966445669913508, + "travel": 27.8, + "road_halfwidth": 10.5, + "sidewalk_width": 18.0, + "intersection": 36.5, + "intersection_width": 12.5, + "exposure": 0.5, + "world_strength": 0.085, + "haze": 0.0025, + "samples": 128, + "resolution_percentage": 100, + "shift_y": 0.09375, + "optics": true, + "world_color": [ + 0.35, + 0.25, + 0.027 + ], + "look": "None", + "rain_displacement": 12, + "glow_strength": 0.45, + "smear_strength": 0.05, + "view_transform": "Standard", + "gamma": 1.06, + "glow_size": 0.86, + "sensor_noise": 0.012, + "smear_region_gain": { + "left": 6.0, + "right": 3.6, + "lower": 4.2, + "opening": 2.5, + "right_lower": 3.0, + "right_upper": 3.9 + }, + "sky_response": { + "1": [ + 1.2605054062517196, + 1.6010563538455556, + 5.981444461917436 + ], + "17": [ + 1.0526570456656525, + 1.4420291016719715, + 6.014321509967891 + ], + "33": [ + 1.0946919130656403, + 1.4779059156328034, + 6.252126070894234 + ], + "49": [ + 1.225149642569033, + 1.7874155565200063, + 8.326589211542156 + ], + "65": [ + 1.2242025797155145, + 2.03143319103788, + 11.878108774815308 + ], + "81": [ + 1.027097816524482, + 1.7479561324369874, + 9.746948941715356 + ], + "97": [ + 0.7570487086400081, + 1.361378012245788, + 7.215861814136287 + ], + "113": [ + 0.5682937110288511, + 1.1020568822706571, + 6.685394646675636 + ], + "128": [ + 0.6520111661770153, + 1.2056391631303078, + 7.768134709770933 + ] + }, + "highlight_ceiling": 0.75, + "highlight_knee": 0.15, + "sensor_correlated": [ + [ + 5, + 12 + ], + [ + 14, + 28 + ], + [ + 36, + 35 + ] + ], + "sensor_noise_color": [ + 1, + 0.88, + 0.95 + ], + "haze_layers": [ + [ + -20, + 25, + 1.0 + ], + [ + 25, + 60, + 0.4 + ], + [ + 60, + 110, + 0.1 + ] + ], + "broad_scatter": 2.0, + "broad_scatter_size": 140, + "optical_overscan": 1.25, + "opening_kernel_scale": 1, + "opening_input_spread": 1, + "opening_ring_angle": 0.48, + "road_roughness": [ + 0.42, + 0.78 + ], + "intersection_fill_watts": 5000 +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/crosswalks.json b/astra_scene_test/Scene_04/assets/crosswalks.json new file mode 100644 index 0000000000000000000000000000000000000000..7f34c1094cd817a41cd9cdffde2666725c721e2b --- /dev/null +++ b/astra_scene_test/Scene_04/assets/crosswalks.json @@ -0,0 +1,486 @@ +{ + "right": { + "observed_end_bounds": [ + [ + 928, + 573 + ], + [ + 1098, + 556 + ], + [ + 1360, + 591 + ], + [ + 1202, + 622 + ] + ], + "plane_world_bounds": [ + [ + 14.147666695812969, + 42.25506597349137, + 0.02308081679303431 + ], + [ + 25.829577790062775, + 45.490655678781394, + 0.2436752179322461 + ], + [ + 21.315051225963373, + 36.766113878431675, + 0.5012816250445447 + ], + [ + 13.691627205459852, + 34.9137103960618, + 0.3454402878031386 + ] + ], + "strips": [ + [ + [ + 14.1425995903646, + 42.17349535596438, + 0.014 + ], + [ + 25.779416383795002, + 45.393716325444174, + 0.014 + ], + [ + 25.533625493082926, + 44.9187134940918, + 0.014 + ], + [ + 14.117770773667598, + 41.7737993300821, + 0.014 + ] + ], + [ + [ + 14.091928535880921, + 41.357789180694425, + 0.014 + ], + [ + 25.277802321117292, + 44.42432279207198, + 0.014 + ], + [ + 25.032011430405213, + 43.94931996071961, + 0.014 + ], + [ + 14.067099719183918, + 40.95809315481215, + 0.014 + ] + ], + [ + [ + 14.04125748139724, + 40.54208300542447, + 0.014 + ], + [ + 24.776188258439582, + 43.45492925869979, + 0.014 + ], + [ + 24.530397367727502, + 42.97992642734742, + 0.014 + ], + [ + 14.01642866470024, + 40.142386979542195, + 0.014 + ] + ], + [ + [ + 13.990586426913563, + 39.72637683015452, + 0.014 + ], + [ + 24.27457419576187, + 42.4855357253276, + 0.014 + ], + [ + 24.028783305049792, + 42.01053289397523, + 0.014 + ], + [ + 13.965757610216558, + 39.32668080427224, + 0.014 + ] + ], + [ + [ + 13.939915372429883, + 38.91067065488457, + 0.014 + ], + [ + 23.77296013308416, + 41.51614219195541, + 0.014 + ], + [ + 23.527169242372082, + 41.04113936060304, + 0.014 + ], + [ + 13.91508655573288, + 38.51097462900229, + 0.014 + ] + ], + [ + [ + 13.889244317946204, + 38.09496447961462, + 0.014 + ], + [ + 23.271346070406445, + 40.546748658583226, + 0.014 + ], + [ + 23.02555517969437, + 40.07174582723084, + 0.014 + ], + [ + 13.8644155012492, + 37.69526845373234, + 0.014 + ] + ], + [ + [ + 13.838573263462521, + 37.279258304344665, + 0.014 + ], + [ + 22.769732007728734, + 39.57735512521103, + 0.014 + ], + [ + 22.523941117016655, + 39.10235229385866, + 0.014 + ], + [ + 13.81374444676552, + 36.87956227846239, + 0.014 + ] + ], + [ + [ + 13.787902208978844, + 36.46355212907471, + 0.014 + ], + [ + 22.268117945051024, + 38.60796159183884, + 0.014 + ], + [ + 22.022327054338945, + 38.13295876048647, + 0.014 + ], + [ + 13.763073392281841, + 36.063856103192435, + 0.014 + ] + ], + [ + [ + 13.737231154495163, + 35.64784595380476, + 0.014 + ], + [ + 21.76650388237331, + 37.638568058466646, + 0.014 + ], + [ + 21.520712991661235, + 37.163565227114276, + 0.014 + ], + [ + 13.712402337798162, + 35.24814992792248, + 0.014 + ] + ] + ] + }, + "left": { + "observed_end_bounds": [ + [ + -100, + 632 + ], + [ + -150, + 595 + ], + [ + 151, + 577 + ], + [ + 218, + 586 + ] + ], + "plane_world_bounds": [ + [ + -17.66525713549598, + 38.938222540714364, + -0.8297024716454495 + ], + [ + -35.48385294715181, + 47.748991211853664, + -1.7967426766866237 + ], + [ + -26.1139267343991, + 51.39163014559495, + -1.6678535988418086 + ], + [ + -16.9376859391618, + 46.15681320534133, + -1.1378530443043848 + ] + ], + "strips": [ + [ + [ + -17.65717301109227, + 39.01842910365467, + 0.014 + ], + [ + -35.37974265589901, + 47.78946497778412, + 0.014 + ], + [ + -34.86960222876024, + 47.987786430843364, + 0.014 + ], + [ + -17.617560801514074, + 39.41144126206213, + 0.014 + ] + ], + [ + [ + -17.576331767055137, + 39.82049473305766, + 0.014 + ], + [ + -34.33863974337092, + 48.19420263708871, + 0.014 + ], + [ + -33.82849931623217, + 48.39252409014796, + 0.014 + ], + [ + -17.536719557476943, + 40.21350689146513, + 0.014 + ] + ], + [ + [ + -17.495490523018002, + 40.62256036246065, + 0.014 + ], + [ + -33.297536830842844, + 48.598940296393295, + 0.014 + ], + [ + -32.787396403704086, + 48.79726174945255, + 0.014 + ], + [ + -17.45587831343981, + 41.015572520868126, + 0.014 + ] + ], + [ + [ + -17.414649278980875, + 41.42462599186365, + 0.014 + ], + [ + -32.25643391831476, + 49.00367795569788, + 0.014 + ], + [ + -31.74629349117601, + 49.20199940875713, + 0.014 + ], + [ + -17.37503706940268, + 41.81763815027112, + 0.014 + ] + ], + [ + [ + -17.333808034943743, + 42.22669162126665, + 0.014 + ], + [ + -31.215331005786688, + 49.40841561500247, + 0.014 + ], + [ + -30.70519057864793, + 49.60673706806172, + 0.014 + ], + [ + -17.29419582536555, + 42.61970377967412, + 0.014 + ] + ], + [ + [ + -17.252966790906612, + 43.02875725066964, + 0.014 + ], + [ + -30.17422809325861, + 49.81315327430706, + 0.014 + ], + [ + -29.664087666119848, + 50.011474727366306, + 0.014 + ], + [ + -17.213354581328417, + 43.421769409077115, + 0.014 + ] + ], + [ + [ + -17.17212554686948, + 43.83082288007264, + 0.014 + ], + [ + -29.13312518073053, + 50.21789093361165, + 0.014 + ], + [ + -28.622984753591773, + 50.41621238667089, + 0.014 + ], + [ + -17.132513337291286, + 44.223835038480104, + 0.014 + ] + ], + [ + [ + -17.09128430283235, + 44.632888509475634, + 0.014 + ], + [ + -28.09202226820245, + 50.622628592916236, + 0.014 + ], + [ + -27.58188184106369, + 50.82095004597548, + 0.014 + ], + [ + -17.051672093254155, + 45.02590066788311, + 0.014 + ] + ], + [ + [ + -17.010443058795218, + 45.43495413887863, + 0.014 + ], + [ + -27.050919355674374, + 51.02736625222082, + 0.014 + ], + [ + -26.540778928535616, + 51.225687705280066, + 0.014 + ], + [ + -16.970830849217023, + 45.8279662972861, + 0.014 + ] + ] + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/distant_lights.json b/astra_scene_test/Scene_04/assets/distant_lights.json new file mode 100644 index 0000000000000000000000000000000000000000..226b6838093c9b24004cd257459efaf1d5efb591 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/distant_lights.json @@ -0,0 +1,747 @@ +{ + "distant_headlight_left": { + "xyz": [ + -7.35898139198819, + 71.47809397744096, + -2.30058224529908 + ], + "observations": { + "1": [ + 576.0, + 539.0 + ], + "9": [ + 576.3914184570312, + 538.5623779296875 + ], + "17": [ + 576.2930297851562, + 540.298095703125 + ], + "25": [ + 574.3419189453125, + 538.5428466796875 + ], + "33": [ + 571.5774536132812, + 534.2451171875 + ], + "41": [ + 570.3427124023438, + 537.163330078125 + ], + "49": [ + 568.6837158203125, + 536.5470581054688 + ], + "57": [ + 566.7459716796875, + 538.6200561523438 + ], + "65": [ + 565.0204467773438, + 543.1527099609375 + ], + "73": [ + 562.9796752929688, + 542.625732421875 + ], + "81": [ + 560.7957763671875, + 545.0585327148438 + ], + "89": [ + 559.5230712890625, + 543.47705078125 + ], + "97": [ + 557.9629516601562, + 541.3145751953125 + ], + "105": [ + 555.3115844726562, + 544.5411376953125 + ], + "113": [ + 551.095703125, + 545.0950317382812 + ], + "121": [ + 545.5712890625, + 548.0756225585938 + ], + "128": [ + 541.04541015625, + 548.66552734375 + ] + }, + "projections": { + "1": [ + 576.1781533091383, + 538.384066864485 + ], + "9": [ + 576.627109797683, + 538.0595213730966 + ], + "17": [ + 576.2367831944781, + 539.7194911885322 + ], + "25": [ + 574.5039117719776, + 538.2300695526071 + ], + "33": [ + 572.2954656990248, + 534.3953705010945 + ], + "41": [ + 570.4621077843385, + 537.230656333056 + ], + "49": [ + 568.90216116284, + 536.8693196552056 + ], + "57": [ + 566.6786176467982, + 538.7812964299692 + ], + "65": [ + 564.5832198403297, + 542.8958445065423 + ], + "73": [ + 562.4908233701947, + 542.5685600948904 + ], + "81": [ + 560.1356406594068, + 544.8894908792836 + ], + "89": [ + 558.9702321861055, + 543.7391223295567 + ], + "97": [ + 557.5454984134551, + 541.9554011923948 + ], + "105": [ + 554.73848486789, + 544.6522637536269 + ], + "113": [ + 550.8956287036239, + 545.4158339185625 + ], + "121": [ + 545.9528428565499, + 548.0841769465898 + ], + "128": [ + 542.6135689019417, + 548.6258324634553 + ] + }, + "mean_error_pixels": 0.5608408373299512, + "max_error_pixels": 1.5686610644785268 + }, + "distant_headlight_center": { + "xyz": [ + -5.166640818071345, + 74.8906401160613, + -2.1968774933027273 + ], + "observations": { + "1": [ + 589.0, + 537.0 + ], + "9": [ + 589.7914428710938, + 536.532958984375 + ], + "17": [ + 590.1503295898438, + 538.204833984375 + ], + "25": [ + 588.5750732421875, + 536.4144287109375 + ], + "33": [ + 586.328857421875, + 531.9990844726562 + ], + "41": [ + 585.3447875976562, + 534.762451171875 + ], + "49": [ + 584.242431640625, + 534.061767578125 + ], + "57": [ + 582.8644409179688, + 536.0819091796875 + ], + "65": [ + 581.7530517578125, + 540.5639038085938 + ], + "73": [ + 580.3848876953125, + 539.9678955078125 + ], + "81": [ + 578.9522094726562, + 542.2881469726562 + ], + "89": [ + 578.4090576171875, + 540.4335327148438 + ], + "97": [ + 577.6299438476562, + 538.175537109375 + ], + "105": [ + 575.9978637695312, + 541.2666015625 + ], + "113": [ + 572.603515625, + 541.677490234375 + ], + "121": [ + 568.5126342773438, + 544.46630859375 + ], + "128": [ + 564.9859619140625, + 544.7620239257812 + ] + }, + "projections": { + "1": [ + 589.0745499526433, + 536.5223328732282 + ], + "9": [ + 589.9217780581538, + 536.145765404874 + ], + "17": [ + 589.9892908053785, + 537.7891385807108 + ], + "25": [ + 588.6760853490683, + 536.226604648976 + ], + "33": [ + 586.8664876526012, + 532.2035113566352 + ], + "41": [ + 585.5855683719942, + 534.8964741883642 + ], + "49": [ + 584.5462500062042, + 534.4652685205119 + ], + "57": [ + 582.9166928262507, + 536.2763793125133 + ], + "65": [ + 581.4683320985137, + 540.3303328247786 + ], + "73": [ + 580.0270506178514, + 539.9134904775809 + ], + "81": [ + 578.3861790710796, + 541.9999479135967 + ], + "89": [ + 577.9446083557641, + 540.6443846063985 + ], + "97": [ + 577.3265146263161, + 538.7700669907289 + ], + "105": [ + 575.4389596713587, + 541.315872036418 + ], + "113": [ + 572.565033533462, + 541.92482212263 + ], + "121": [ + 568.6615683105347, + 544.3393503976336 + ], + "128": [ + 566.1930968922251, + 544.6458110522539 + ] + }, + "mean_error_pixels": 0.4630006274600263, + "max_error_pixels": 1.2127160786750724 + }, + "distant_headlight_far": { + "xyz": [ + -1.3408501070145125, + 78.56457439028985, + -2.0631704148883756 + ], + "observations": { + "1": [ + 611.0, + 537.0 + ], + "9": [ + 612.0967407226562, + 536.3214111328125 + ], + "17": [ + 612.3280639648438, + 537.7880249023438 + ], + "25": [ + 610.8767700195312, + 535.6309204101562 + ], + "33": [ + 608.66259765625, + 530.7155151367188 + ], + "41": [ + 607.9232788085938, + 533.1858520507812 + ], + "49": [ + 607.29833984375, + 532.1372680664062 + ], + "57": [ + 606.4265747070312, + 533.8456420898438 + ], + "65": [ + 605.8465576171875, + 538.3228149414062 + ], + "73": [ + 605.0324096679688, + 537.3521728515625 + ], + "81": [ + 604.3375854492188, + 539.036865234375 + ], + "89": [ + 604.9450073242188, + 536.6976318359375 + ], + "97": [ + 605.3412475585938, + 534.255126953125 + ], + "105": [ + 604.6949462890625, + 537.1362915039062 + ], + "113": [ + 602.6989135742188, + 537.1969604492188 + ], + "121": [ + 599.618408203125, + 539.5076293945312 + ], + "128": [ + 597.2274169921875, + 539.5512084960938 + ] + }, + "projections": { + "1": [ + 608.649284510563, + 534.5721860024094 + ], + "9": [ + 610.0220195720026, + 534.1588460443838 + ], + "17": [ + 610.6871634643762, + 535.8249689646208 + ], + "25": [ + 609.9139727402145, + 534.1962833706403 + ], + "33": [ + 608.6143427261642, + 529.9344808742193 + ], + "41": [ + 608.0381956657178, + 532.4761732701647 + ], + "49": [ + 607.6555498570159, + 531.9976522721279 + ], + "57": [ + 606.7686371840673, + 533.7248721806347 + ], + "65": [ + 606.123149601523, + 537.7603912730182 + ], + "73": [ + 605.4805529272529, + 537.2834945637096 + ], + "81": [ + 604.7111200736371, + 539.1108934336125 + ], + "89": [ + 605.1465546605856, + 537.5422284041786 + ], + "97": [ + 605.4933674721179, + 535.627939224619 + ], + "105": [ + 604.6910736887733, + 538.0624296182407 + ], + "113": [ + 602.9406479538007, + 538.5612576829834 + ], + "121": [ + 600.2240350973775, + 540.7383089533148 + ], + "128": [ + 598.7413537811864, + 540.818020719653 + ] + }, + "mean_error_pixels": 1.3104749025037048, + "max_error_pixels": 3.3793703731873013 + }, + "right_next_lamp": { + "xyz": [ + 12.171372614418908, + 73.32698309400536, + 10.287755236624047 + ], + "observations": { + "1": [ + 676.0, + 474.0 + ], + "9": [ + 679.2293701171875, + 472.12579345703125 + ], + "17": [ + 682.2525634765625, + 472.6588439941406 + ], + "25": [ + 683.0426635742188, + 469.45867919921875 + ], + "33": [ + 682.70703125, + 462.5318908691406 + ], + "41": [ + 684.1465454101562, + 463.30303955078125 + ], + "49": [ + 685.89404296875, + 461.00775146484375 + ], + "57": [ + 687.2223510742188, + 461.00384521484375 + ], + "65": [ + 689.6056518554688, + 464.12689208984375 + ], + "73": [ + 691.504638671875, + 461.5000915527344 + ], + "81": [ + 692.9893798828125, + 460.7843322753906 + ], + "89": [ + 695.825927734375, + 455.9842224121094 + ], + "97": [ + 699.2017822265625, + 451.2179870605469 + ], + "105": [ + 701.9930419921875, + 451.5902099609375 + ], + "113": [ + 703.4578247070312, + 448.8763427734375 + ], + "121": [ + 703.9640502929688, + 447.9381408691406 + ], + "128": [ + 704.8994140625, + 445.02691650390625 + ] + }, + "projections": { + "1": [ + 676.0224894191014, + 474.02737419246273 + ], + "9": [ + 679.1781353090328, + 472.1714102568612 + ], + "17": [ + 681.9945972461533, + 472.48150543567465 + ], + "25": [ + 682.9442083264481, + 469.3815834121774 + ], + "33": [ + 682.8227497095027, + 463.1203522624157 + ], + "41": [ + 684.2332820645654, + 463.54789221300604 + ], + "49": [ + 686.0136830367533, + 461.4045970455758 + ], + "57": [ + 687.4168450830999, + 461.20927022375037 + ], + "65": [ + 689.3861719576702, + 463.43935763124813 + ], + "73": [ + 691.3280317398746, + 460.98869323690974 + ], + "81": [ + 692.7838191446288, + 460.04647916478797 + ], + "89": [ + 695.730084519892, + 455.7304789232318 + ], + "97": [ + 699.3704762523512, + 451.46702325538035 + ], + "105": [ + 701.8727778783019, + 451.235670100558 + ], + "113": [ + 703.557605756206, + 449.0679534841074 + ], + "121": [ + 704.0054552811224, + 448.18727634602976 + ], + "128": [ + 705.171273994033, + 445.54589256007654 + ] + }, + "mean_error_pixels": 0.36050395947295183, + "max_error_pixels": 0.7659519762417646 + }, + "right_smaller_lamp": { + "xyz": [ + 14.926147602214728, + 102.69188314678074, + 8.372225779138235 + ], + "observations": { + "1": [ + 669.0, + 492.0 + ], + "9": [ + 671.7989501953125, + 490.62890625 + ], + "17": [ + 674.040283203125, + 491.7751770019531 + ], + "25": [ + 674.4747924804688, + 488.9952087402344 + ], + "33": [ + 673.739013671875, + 482.8454284667969 + ], + "41": [ + 674.8565673828125, + 484.1058044433594 + ], + "49": [ + 676.0146484375, + 482.53460693359375 + ], + "57": [ + 676.7369995117188, + 483.2787170410156 + ], + "65": [ + 678.2216796875, + 487.1328430175781 + ], + "73": [ + 679.3516235351562, + 485.3456115722656 + ], + "81": [ + 680.3638916015625, + 485.5367431640625 + ], + "89": [ + 682.3519287109375, + 481.94512939453125 + ], + "97": [ + 684.6644897460938, + 478.17315673828125 + ], + "105": [ + 686.397705078125, + 479.7648010253906 + ], + "113": [ + 686.5369262695312, + 478.492919921875 + ], + "121": [ + 685.7559814453125, + 479.2514953613281 + ], + "128": [ + 685.3016967773438, + 477.7740478515625 + ] + }, + "projections": { + "1": [ + 668.6647911929929, + 491.1925476953942 + ], + "9": [ + 671.3847913989883, + 489.961102713132 + ], + "17": [ + 673.6723103506375, + 490.91915071319386 + ], + "25": [ + 674.1804256180307, + 488.4500461889449 + ], + "33": [ + 673.7502214875567, + 482.89457520061734 + ], + "41": [ + 674.6716851944223, + 484.1485370661582 + ], + "49": [ + 675.870941932223, + 482.7741415565 + ], + "57": [ + 676.669113161076, + 483.42357117846916 + ], + "65": [ + 677.9461709336048, + 486.5070199438311 + ], + "73": [ + 679.1498568706313, + 484.99940068527303 + ], + "81": [ + 679.9950965715367, + 485.1569300676617 + ], + "89": [ + 682.1854830805188, + 482.0424806759358 + ], + "97": [ + 684.7968891393401, + 479.01049228456293 + ], + "105": [ + 686.3041535846587, + 480.0642636283342 + ], + "113": [ + 686.9099158368404, + 479.2307364912546 + ], + "121": [ + 686.380228869699, + 479.74440379951676 + ], + "128": [ + 686.6820172779445, + 478.3672701688363 + ] + }, + "mean_error_pixels": 0.58727013196417, + "max_error_pixels": 1.5023972184779937 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/distant_tower_anchors.json b/astra_scene_test/Scene_04/assets/distant_tower_anchors.json new file mode 100644 index 0000000000000000000000000000000000000000..f1482880dad1be964da14a0cedbf057e47a3216f --- /dev/null +++ b/astra_scene_test/Scene_04/assets/distant_tower_anchors.json @@ -0,0 +1,104 @@ +{ + "right_distant_three_windows": { + "xyz": [ + 42.617412637336564, + 209.44558427534503, + 114.72988962862105 + ], + "observations": { + "1": [ + 690, + 322 + ], + "33": [ + 692, + 310 + ], + "65": [ + 698, + 307 + ], + "97": [ + 700, + 293 + ], + "128": [ + 703, + 289 + ] + }, + "projections": { + "1": [ + 687.852849261147, + 322.4624390834458 + ], + "33": [ + 692.6227212523136, + 307.91983166392663 + ], + "65": [ + 696.6294384268816, + 307.7332476569567 + ], + "97": [ + 702.4715206445546, + 293.6340999783804 + ], + "128": [ + 703.2629287452102, + 288.89546375775444 + ] + } + }, + "right_distant_roof_peak": { + "xyz": [ + 53.51081966513235, + 189.1719805826661, + 131.21272448965607 + ], + "observations": { + "1": [ + 712, + 276 + ], + "33": [ + 723, + 259 + ], + "65": [ + 725, + 251 + ], + "97": [ + 734, + 235 + ], + "128": [ + 736, + 234 + ] + }, + "projections": { + "1": [ + 714.4628477083404, + 275.4329280907003 + ], + "33": [ + 720.7678315539995, + 257.4327984904959 + ], + "65": [ + 725.965961708042, + 255.47420523636038 + ], + "97": [ + 733.5380062708937, + 237.32373522674317 + ], + "128": [ + 735.5761860301047, + 230.20613827864662 + ] + } + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/distant_traffic_fit.json b/astra_scene_test/Scene_04/assets/distant_traffic_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..dc0060fa380ce2b272ecfbcb56920f00c6cc4a20 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/distant_traffic_fit.json @@ -0,0 +1,824 @@ +{ + "8": { + "stationary_mean_error_pixels": 5.826549887534254, + "moving_mean_error_pixels": 3.3468962555456927, + "chosen_x_y_velocity": [ + -14.843240600140897, + 203.18433062211895, + -14.999999999999998 + ], + "moving_trial": [ + -14.843240600140897, + 203.18433062211895, + -14.999999999999998 + ], + "observations": { + "1": [ + 589.0, + 537.0 + ], + "5": [ + 589.6947631835938, + 537.3878784179688 + ], + "9": [ + 589.7914428710938, + 536.532958984375 + ], + "13": [ + 589.7700805664062, + 536.8493041992188 + ], + "17": [ + 590.1503295898438, + 538.204833984375 + ], + "21": [ + 589.462646484375, + 538.2354125976562 + ], + "25": [ + 588.5750732421875, + 536.4144287109375 + ], + "29": [ + 587.1988525390625, + 533.6327514648438 + ], + "33": [ + 586.328857421875, + 531.9990844726562 + ], + "37": [ + 585.5714111328125, + 533.0647583007812 + ], + "41": [ + 585.3447875976562, + 534.762451171875 + ], + "45": [ + 584.7625732421875, + 535.7164306640625 + ], + "49": [ + 584.242431640625, + 534.061767578125 + ], + "53": [ + 583.680419921875, + 533.6455078125 + ], + "57": [ + 582.8644409179688, + 536.0819091796875 + ], + "61": [ + 582.4730224609375, + 539.5711059570312 + ], + "65": [ + 581.7530517578125, + 540.5639038085938 + ], + "69": [ + 581.2861938476562, + 540.0653686523438 + ], + "73": [ + 580.3848876953125, + 539.9678955078125 + ], + "77": [ + 579.4817504882812, + 541.0054931640625 + ], + "81": [ + 578.9522094726562, + 542.2881469726562 + ], + "85": [ + 578.6615600585938, + 541.7507934570312 + ], + "89": [ + 578.4090576171875, + 540.4335327148438 + ], + "93": [ + 578.1357421875, + 538.4361572265625 + ], + "97": [ + 577.6299438476562, + 538.175537109375 + ], + "101": [ + 576.8908081054688, + 539.6912841796875 + ], + "105": [ + 575.9978637695312, + 541.2666015625 + ], + "109": [ + 574.3720703125, + 541.5494384765625 + ], + "113": [ + 572.603515625, + 541.677490234375 + ], + "117": [ + 570.50927734375, + 543.0639038085938 + ], + "121": [ + 568.5126342773438, + 544.46630859375 + ], + "125": [ + 566.7442016601562, + 545.0211181640625 + ] + }, + "predictions": { + "1": [ + 587.5234062936316, + 537.7583983681754 + ], + "5": [ + 588.0915029643066, + 537.3448367480603 + ], + "9": [ + 588.6438414533328, + 536.9334501997039 + ], + "13": [ + 588.8332295506663, + 537.5154342082798 + ], + "17": [ + 589.0035857884669, + 538.0985940954783 + ], + "21": [ + 588.4508312286817, + 537.0913707267655 + ], + "25": [ + 587.8808568474608, + 536.0864881949603 + ], + "29": [ + 587.0572921276475, + 533.8475285505256 + ], + "33": [ + 586.2060396120932, + 531.6196517710986 + ], + "37": [ + 585.7186721960611, + 532.6922567344983 + ], + "41": [ + 585.2088127484684, + 533.7695971854147 + ], + "45": [ + 584.7933760675007, + 533.2900427993145 + ], + "49": [ + 584.3551479259022, + 532.8119771158379 + ], + "53": [ + 583.6572946384142, + 533.4317596075682 + ], + "57": [ + 582.9319463912082, + 534.0538674562484 + ], + "61": [ + 582.3196640793326, + 535.7815169220735 + ], + "65": [ + 581.6724533474005, + 537.511488761369 + ], + "69": [ + 581.0502544846896, + 536.9930584469078 + ], + "73": [ + 580.3963027371407, + 536.4764377483536 + ], + "77": [ + 579.6592248402712, + 537.1820591164815 + ], + "81": [ + 578.8867240105404, + 537.8958440580606 + ], + "85": [ + 578.7429199739707, + 536.8661164060036 + ], + "89": [ + 578.5565984359268, + 535.8437348292749 + ], + "93": [ + 578.3335743555185, + 534.5352841745254 + ], + "97": [ + 578.0659367678302, + 533.2309290513754 + ], + "101": [ + 577.1791865373781, + 534.1070051962237 + ], + "105": [ + 576.2377158179195, + 534.9867423778089 + ], + "109": [ + 574.8067129153019, + 534.8864182979581 + ], + "113": [ + 573.3159036178536, + 534.788757915225 + ], + "117": [ + 571.2865049473114, + 535.5747024080047 + ], + "121": [ + 569.1878361715968, + 536.3700283894674 + ], + "125": [ + 567.5771436267083, + 536.138431344066 + ] + } + }, + "33": { + "stationary_mean_error_pixels": 8.983875721903331, + "moving_mean_error_pixels": 5.77230124445649, + "chosen_x_y_velocity": [ + -21.875789274228374, + 203.57341713117074, + -14.999999999974111 + ], + "moving_trial": [ + -21.875789274228374, + 203.57341713117074, + -14.999999999974111 + ], + "observations": { + "1": [ + 576.0, + 539.0 + ], + "5": [ + 576.4158935546875, + 539.4017333984375 + ], + "9": [ + 576.3914184570312, + 538.5623779296875 + ], + "13": [ + 576.2140502929688, + 538.896484375 + ], + "17": [ + 576.2930297851562, + 540.298095703125 + ], + "21": [ + 575.400146484375, + 540.3518676757812 + ], + "25": [ + 574.3419189453125, + 538.5428466796875 + ], + "29": [ + 572.7302856445312, + 535.7682495117188 + ], + "33": [ + 571.5774536132812, + 534.2451171875 + ], + "37": [ + 570.7581176757812, + 535.3529663085938 + ], + "41": [ + 570.3427124023438, + 537.163330078125 + ], + "45": [ + 569.5338134765625, + 538.13037109375 + ], + "49": [ + 568.6837158203125, + 536.5470581054688 + ], + "53": [ + 567.8823852539062, + 536.2171020507812 + ], + "57": [ + 566.7459716796875, + 538.6200561523438 + ], + "61": [ + 566.0570678710938, + 542.096435546875 + ], + "65": [ + 565.0204467773438, + 543.1527099609375 + ], + "69": [ + 564.266357421875, + 542.5944213867188 + ], + "73": [ + 562.9796752929688, + 542.625732421875 + ], + "77": [ + 561.7372436523438, + 543.7108764648438 + ], + "81": [ + 560.7957763671875, + 545.0585327148438 + ], + "85": [ + 560.0787353515625, + 544.6835327148438 + ], + "89": [ + 559.5230712890625, + 543.47705078125 + ], + "93": [ + 558.87890625, + 541.6256713867188 + ], + "97": [ + 557.9629516601562, + 541.3145751953125 + ], + "101": [ + 556.6414794921875, + 542.8955078125 + ], + "105": [ + 555.3115844726562, + 544.5411376953125 + ], + "109": [ + 553.3179321289062, + 544.9266967773438 + ], + "113": [ + 551.095703125, + 545.0950317382812 + ], + "117": [ + 548.3267211914062, + 546.6200561523438 + ], + "121": [ + 545.5712890625, + 548.0756225585938 + ], + "125": [ + 543.2776489257812, + 548.7100830078125 + ] + }, + "predictions": { + "1": [ + 574.4741299827855, + 537.831220838597 + ], + "5": [ + 574.8633712318401, + 537.4057366220565 + ], + "9": [ + 575.2314773968329, + 536.9821326217359 + ], + "13": [ + 575.2213261083941, + 537.5293670064173 + ], + "17": [ + 575.1860726864052, + 538.0766489244204 + ], + "21": [ + 574.4290935597854, + 537.066876723212 + ], + "25": [ + 573.6484762831834, + 536.0593691138932 + ], + "29": [ + 572.6130328145834, + 533.8762714558604 + ], + "33": [ + 571.5427815012273, + 531.7057723640231 + ], + "37": [ + 570.8056324871552, + 532.7949116608866 + ], + "41": [ + 570.0373834927626, + 533.8893481791974 + ], + "45": [ + 569.3734714459304, + 533.3943644250292 + ], + "49": [ + 568.6783366799522, + 532.9003728345369 + ], + "53": [ + 567.7025835050708, + 533.5120277485873 + ], + "57": [ + 566.689384637597, + 534.1257048167703 + ], + "61": [ + 565.7762624309839, + 535.8194751434496 + ], + "65": [ + 564.81696336414, + 537.5141871214988 + ], + "69": [ + 563.886462402089, + 536.9756694149257 + ], + "73": [ + 562.9125130026555, + 536.4382291277777 + ], + "77": [ + 561.8311231328701, + 537.1854235771874 + ], + "81": [ + 560.7006485726143, + 537.9424537368129 + ], + "85": [ + 560.2087857580082, + 536.9382065509232 + ], + "89": [ + 559.6600950136341, + 535.9423902460305 + ], + "93": [ + 559.0620607825987, + 534.5958918391758 + ], + "97": [ + 558.4033142846993, + 533.2521078577444 + ], + "101": [ + 557.0836103603682, + 534.1063653609818 + ], + "105": [ + 555.6898249127457, + 534.963232987264 + ], + "109": [ + 553.7885361903859, + 534.8379911343897 + ], + "113": [ + 551.8053826231514, + 534.7142439176322 + ], + "117": [ + 549.2472057951329, + 535.5139159388891 + ], + "121": [ + 546.593252767997, + 536.3237553954363 + ], + "125": [ + 544.4325093721909, + 536.1162837462894 + ] + } + }, + "60": { + "stationary_mean_error_pixels": 3.2411584257232136, + "moving_mean_error_pixels": 2.0663826434868175, + "chosen_x_y_velocity": [ + -4.171084996792737, + 152.9270068833871, + 0.0 + ], + "moving_trial": [ + -3.8651657218354667, + 181.02352966244334, + -14.999999999999304 + ], + "observations": { + "1": [ + 611.0, + 537.0 + ], + "5": [ + 611.817626953125, + 537.2843017578125 + ], + "9": [ + 612.0967407226562, + 536.3214111328125 + ], + "13": [ + 612.0643310546875, + 536.497314453125 + ], + "17": [ + 612.3280639648438, + 537.7880249023438 + ], + "21": [ + 611.6948852539062, + 537.8065795898438 + ], + "25": [ + 610.8767700195312, + 535.6309204101562 + ], + "29": [ + 609.43603515625, + 532.5975341796875 + ], + "33": [ + 608.66259765625, + 530.7155151367188 + ], + "37": [ + 607.9825439453125, + 531.5700073242188 + ], + "41": [ + 607.9232788085938, + 533.1858520507812 + ], + "45": [ + 607.480712890625, + 533.8895874023438 + ], + "49": [ + 607.29833984375, + 532.1372680664062 + ], + "53": [ + 607.0217895507812, + 531.6180419921875 + ], + "57": [ + 606.4265747070312, + 533.8456420898438 + ], + "61": [ + 606.196044921875, + 537.4027709960938 + ], + "65": [ + 605.8465576171875, + 538.3228149414062 + ], + "69": [ + 605.6130981445312, + 537.6458129882812 + ], + "73": [ + 605.0324096679688, + 537.3521728515625 + ], + "77": [ + 604.5411987304688, + 538.0623168945312 + ], + "81": [ + 604.3375854492188, + 539.036865234375 + ], + "85": [ + 604.528076171875, + 538.2694702148438 + ], + "89": [ + 604.9450073242188, + 536.6976318359375 + ], + "93": [ + 605.3616943359375, + 534.6802978515625 + ], + "97": [ + 605.3412475585938, + 534.255126953125 + ], + "101": [ + 605.007568359375, + 535.6028442382812 + ], + "105": [ + 604.6949462890625, + 537.1362915039062 + ], + "109": [ + 603.7603149414062, + 537.1900024414062 + ], + "113": [ + 602.6989135742188, + 537.1969604492188 + ], + "117": [ + 601.1856689453125, + 538.4503784179688 + ], + "121": [ + 599.618408203125, + 539.5076293945312 + ], + "125": [ + 598.5499877929688, + 539.7424926757812 + ] + }, + "predictions": { + "1": [ + 604.8041246440838, + 536.0828866484186 + ], + "5": [ + 605.6628234598159, + 535.7587528783114 + ], + "9": [ + 606.5183293367365, + 535.437043325908 + ], + "13": [ + 607.0378950312975, + 536.1378467717806 + ], + "17": [ + 607.5523722020496, + 536.84145809814 + ], + "21": [ + 607.3468923258791, + 535.9085274962296 + ], + "25": [ + 607.1390683218217, + 534.9776708929829 + ], + "29": [ + 606.6811053463744, + 532.7329076001137 + ], + "33": [ + 606.2122681278084, + 530.4958391768944 + ], + "37": [ + 606.1565198612531, + 531.6149633377228 + ], + "41": [ + 606.0984562880999, + 532.7372276661592 + ], + "45": [ + 606.1361710786894, + 532.3456310969656 + ], + "49": [ + 606.1713649408246, + 531.9556313991453 + ], + "53": [ + 605.9828496361895, + 532.6518207316486 + ], + "57": [ + 605.790574960591, + 533.349953945737 + ], + "61": [ + 605.743115987282, + 535.1879933451658 + ], + "65": [ + 605.6875806225993, + 537.0298685335952 + ], + "69": [ + 605.662118333579, + 536.6018403822027 + ], + "73": [ + 605.6337900837188, + 536.1755488706424 + ], + "77": [ + 605.567542441064, + 536.8793883416623 + ], + "81": [ + 605.4998209562209, + 537.5863490490049 + ], + "85": [ + 606.066949518401, + 536.5734451767562 + ], + "89": [ + 606.6286108087304, + 535.5633065510526 + ], + "93": [ + 607.1913656440731, + 534.3606440246441 + ], + "97": [ + 607.7514909008027, + 533.1621445115768 + ], + "101": [ + 607.768737483913, + 534.1150768224192 + ], + "105": [ + 607.780865061312, + 535.0706014785204 + ], + "109": [ + 607.346406619297, + 535.0463790693149 + ], + "113": [ + 606.9081044488888, + 535.0235145295508 + ], + "117": [ + 606.0027649329098, + 535.818718996099 + ], + "121": [ + 605.0935888170767, + 536.6174448090546 + ], + "125": [ + 604.694153048999, + 536.3696958119033 + ] + } + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/distant_window_row.json b/astra_scene_test/Scene_04/assets/distant_window_row.json new file mode 100644 index 0000000000000000000000000000000000000000..c8439999b1b77f787e06d976822ffe30a710db3e --- /dev/null +++ b/astra_scene_test/Scene_04/assets/distant_window_row.json @@ -0,0 +1,27 @@ +{ + "x": 41.462958485542764, + "y": 201.97, + "z": 110.73928105813275, + "observations": { + "1": [ + 689.5, + 321.7 + ], + "33": [ + 693.6, + 307.5 + ], + "65": [ + 698.5, + 307.2 + ], + "97": [ + 702.9, + 293.1 + ], + "128": [ + 702.7, + 288.3 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/intersection_vehicle.json b/astra_scene_test/Scene_04/assets/intersection_vehicle.json new file mode 100644 index 0000000000000000000000000000000000000000..f0438723595e192dec90856371e29d4d9fbef4df --- /dev/null +++ b/astra_scene_test/Scene_04/assets/intersection_vehicle.json @@ -0,0 +1,41 @@ +{ + "license_plate_observations": { + "65": [ + 495, + 544 + ], + "97": [ + 453, + 547 + ], + "128": [ + 381, + 563 + ] + }, + "license_plate_xyz": [ + -9.399410767370359, + 45.770513780133484, + -0.048393315593366 + ], + "scale": 1.4, + "root_xy": [ + -9.399410767370359, + 49.204433780133485 + ], + "predictions": { + "65": [ + 493.96545478191047, + 543.4552025962012 + ], + "97": [ + 454.25731603030977, + 547.2743509012653 + ], + "128": [ + 380.70892637228917, + 563.098523777601 + ] + }, + "note": "Static three-view fit. Car body and later row spacing are inferred; terrain contact is shared with the street." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/landmarks.json b/astra_scene_test/Scene_04/assets/landmarks.json new file mode 100644 index 0000000000000000000000000000000000000000..372968336007f5c2784e35317925a274d66379a9 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/landmarks.json @@ -0,0 +1,1023 @@ +{ + "left_near_lamp": { + "xyz": [ + -14.200729466726834, + 46.298022711089374, + 11.536065952944016 + ], + "observations": { + "1": [ + 500, + 441 + ], + "33": [ + 477, + 421 + ], + "65": [ + 444, + 407 + ], + "97": [ + 393, + 373 + ], + "128": [ + 300, + 325 + ] + }, + "projections": { + "1": [ + 500.73402913273384, + 441.17188636667913 + ], + "33": [ + 478.1079348214165, + 421.4161694922248 + ], + "65": [ + 443.8763502321533, + 406.61480424103513 + ], + "97": [ + 390.8930016305568, + 371.9110794914525 + ], + "128": [ + 300.5744684918089, + 325.7419514372331 + ] + }, + "mean_error_pixels": 1.1304121866115637, + "max_error_pixels": 2.3717483008893887 + }, + "right_near_lamp": { + "xyz": [ + 10.392373139610859, + 33.01043668651939, + 13.734346140984389 + ], + "observations": { + "1": [ + 727, + 395 + ], + "33": [ + 752, + 355 + ], + "65": [ + 798, + 307 + ], + "97": [ + 886, + 185 + ] + }, + "projections": { + "1": [ + 727.6843252887536, + 394.5042848124134 + ], + "33": [ + 754.2096025641899, + 355.4829418445371 + ], + "65": [ + 795.9850276848208, + 307.69087640834766 + ], + "97": [ + 885.9783560347623, + 184.52529055234214 + ] + }, + "mean_error_pixels": 1.4280238049928071, + "max_error_pixels": 2.2617639834606154 + }, + "right_secondary_lamp": { + "xyz": [ + 16.40212160660987, + 36.58076186439755, + 13.982917142455664 + ], + "observations": { + "97": [ + 960, + 254 + ], + "128": [ + 1148, + 100 + ] + }, + "projections": { + "97": [ + 959.5418014757051, + 253.4386937439212 + ], + "128": [ + 1148.282253338666, + 100.34542866333624 + ] + }, + "mean_error_pixels": 0.585328380145347, + "max_error_pixels": 0.7245761525051688 + }, + "left_far_lamp": { + "xyz": [ + -9.379595573502398, + 47.13539318211352, + 11.486611306013817 + ], + "observations": { + "1": [ + 541.0, + 443.0 + ], + "9": [ + 539.4916381835938, + 439.3216552734375 + ], + "17": [ + 537.485595703125, + 437.11785888671875 + ], + "25": [ + 532.926513671875, + 431.6917724609375 + ], + "33": [ + 526.0064086914062, + 423.3890380859375 + ], + "41": [ + 520.7630004882812, + 421.8131103515625 + ], + "49": [ + 515.613037109375, + 415.9709167480469 + ], + "57": [ + 509.6747741699219, + 412.7524108886719 + ], + "65": [ + 504.20135498046875, + 411.1305236816406 + ], + "73": [ + 497.11669921875, + 404.0169372558594 + ], + "81": [ + 488.283935546875, + 399.40057373046875 + ], + "89": [ + 479.4316101074219, + 390.09033203125 + ], + "97": [ + 470.0669250488281, + 378.4097900390625 + ], + "105": [ + 458.78826904296875, + 370.81695556640625 + ], + "113": [ + 444.2247314453125, + 359.21331787109375 + ], + "121": [ + 425.7640075683594, + 348.38427734375 + ], + "128": [ + 407.7334289550781, + 335.6137390136719 + ] + }, + "projections": { + "1": [ + 541.5634615197931, + 443.2611123315446 + ], + "9": [ + 539.8045286737156, + 439.3883762426721 + ], + "17": [ + 537.3776407655463, + 436.95380986354076 + ], + "25": [ + 532.9696981195091, + 431.63576805969535 + ], + "33": [ + 526.8264300807299, + 424.145852604538 + ], + "41": [ + 521.2627365819642, + 422.0695715270556 + ], + "49": [ + 516.1942930310026, + 416.66964928602914 + ], + "57": [ + 509.95209362737, + 412.9281749729347 + ], + "65": [ + 504.0632384835899, + 410.8276225193913 + ], + "73": [ + 496.88798352829133, + 403.71161419476834 + ], + "81": [ + 487.84693442257856, + 398.8962032786197 + ], + "89": [ + 478.88493914263563, + 389.69103105774263 + ], + "97": [ + 469.41078371972617, + 378.1502860955218 + ], + "105": [ + 457.67419410852915, + 370.1524245041593 + ], + "113": [ + 443.1841354736833, + 358.7421228024798 + ], + "121": [ + 425.67957260403807, + 348.34063360179294 + ], + "128": [ + 409.80670241361105, + 336.8492867100157 + ] + }, + "mean_error_pixels": 0.6961912416809485, + "max_error_pixels": 2.413512159446805 + }, + "left_near_signal": { + "xyz": [ + -8.109304879175783, + 36.35099002645948, + 6.799730493445938 + ], + "observations": { + "1": [ + 532.0, + 471.0 + ], + "9": [ + 528.5169067382812, + 467.8038024902344 + ], + "17": [ + 524.0791015625, + 466.2242126464844 + ], + "25": [ + 516.974853515625, + 461.2014465332031 + ], + "33": [ + 507.3438415527344, + 453.6877136230469 + ], + "41": [ + 498.4823913574219, + 452.1849365234375 + ], + "49": [ + 489.051025390625, + 446.7339782714844 + ], + "57": [ + 478.26409912109375, + 443.2602233886719 + ], + "65": [ + 466.6575927734375, + 441.1639099121094 + ], + "73": [ + 451.6542053222656, + 433.3653259277344 + ], + "81": [ + 432.9798278808594, + 427.89337158203125 + ], + "89": [ + 411.48309326171875, + 416.65887451171875 + ], + "97": [ + 385.3642578125, + 401.4269104003906 + ], + "105": [ + 351.8008117675781, + 388.5360107421875 + ], + "113": [ + 307.4602966308594, + 369.214111328125 + ], + "121": [ + 248.99717712402344, + 347.3996276855469 + ], + "128": [ + 185.6737823486328, + 321.3771057128906 + ] + }, + "projections": { + "1": [ + 531.7980336313469, + 470.98351907024875 + ], + "9": [ + 528.1871764455223, + 467.6316388403121 + ], + "17": [ + 523.3893996134333, + 465.63431920470106 + ], + "25": [ + 516.6637647163654, + 460.77842097072 + ], + "33": [ + 508.1929231001427, + 453.90101373915894 + ], + "41": [ + 498.9623887553071, + 452.21610832881885 + ], + "49": [ + 489.82038328778435, + 447.0077927794067 + ], + "57": [ + 478.45158428674927, + 443.26800664040155 + ], + "65": [ + 466.2861751811171, + 440.73316411029145 + ], + "73": [ + 451.5001663262537, + 432.96501378553194 + ], + "81": [ + 432.89824929344866, + 427.26377602542584 + ], + "89": [ + 411.65010087705264, + 416.4707600749598 + ], + "97": [ + 385.61225301617776, + 401.69892810673264 + ], + "105": [ + 351.8796606353686, + 388.62800811260314 + ], + "113": [ + 307.46645145262573, + 369.37944863943545 + ], + "121": [ + 248.90261135600764, + 347.48817112101426 + ], + "128": [ + 185.40704488238504, + 321.81770347162796 + ] + }, + "mean_error_pixels": 0.4442014652812221, + "max_error_pixels": 0.9075588417317058 + }, + "right_far_signal": { + "xyz": [ + 8.71915752790315, + 40.75595999522281, + 7.253766295525992 + ], + "observations": { + "1": [ + 694.0, + 471.0 + ], + "9": [ + 698.5577392578125, + 468.45086669921875 + ], + "17": [ + 703.3010864257812, + 468.2178649902344 + ], + "25": [ + 706.0357055664062, + 463.7693786621094 + ], + "33": [ + 707.5382690429688, + 455.1895446777344 + ], + "41": [ + 711.2960815429688, + 454.2938537597656 + ], + "49": [ + 715.709228515625, + 450.38934326171875 + ], + "57": [ + 720.2081909179688, + 448.4042663574219 + ], + "65": [ + 726.0914916992188, + 449.4227294921875 + ], + "73": [ + 732.2811279296875, + 444.2070617675781 + ], + "81": [ + 738.5117797851562, + 439.9429626464844 + ], + "89": [ + 747.1100463867188, + 430.94683837890625 + ], + "97": [ + 757.9595947265625, + 421.08697509765625 + ], + "105": [ + 769.6968994140625, + 415.4256591796875 + ], + "113": [ + 782.4249877929688, + 405.2038879394531 + ], + "121": [ + 796.6268310546875, + 394.6088562011719 + ], + "128": [ + 812.3159790039062, + 380.6214599609375 + ] + }, + "projections": { + "1": [ + 693.2707949991034, + 472.2078247993892 + ], + "9": [ + 698.058081432782, + 469.39917144535747 + ], + "17": [ + 702.7572240316523, + 468.66289622127243 + ], + "25": [ + 705.5580568424793, + 464.42337669711264 + ], + "33": [ + 707.36086469691, + 456.7382817150605 + ], + "41": [ + 711.3724966294412, + 455.39577732802945 + ], + "49": [ + 715.9172776694526, + 451.4880232580769 + ], + "57": [ + 720.5433449094228, + 449.16091818887946 + ], + "65": [ + 726.2015682755222, + 449.04527529278374 + ], + "73": [ + 732.4779227598541, + 443.73114068786265 + ], + "81": [ + 738.8739427037523, + 439.05588368044755 + ], + "89": [ + 747.9382513924168, + 430.18746757145914 + ], + "97": [ + 759.3248090601107, + 420.62646297803184 + ], + "105": [ + 770.7980468096621, + 414.0248203618629 + ], + "113": [ + 783.2382285996143, + 404.20569034890207 + ], + "121": [ + 795.9632887767126, + 394.06071766049104 + ], + "128": [ + 809.6806272148485, + 381.74856498848123 + ] + }, + "mean_error_pixels": 1.166565175051173, + "max_error_pixels": 2.866259722217109 + }, + "tower_window_right": { + "xyz": [ + -29.751068326755114, + 71.85901165324739, + 53.88414167724895 + ], + "observations": { + "1": [ + 467.0, + 254.0 + ], + "9": [ + 464.99371337890625, + 246.5759735107422 + ], + "17": [ + 463.69232177734375, + 240.07489013671875 + ], + "25": [ + 458.90411376953125, + 230.98538208007812 + ], + "33": [ + 450.2771911621094, + 219.12452697753906 + ], + "41": [ + 444.66998291015625, + 213.47528076171875 + ], + "49": [ + 440.4549255371094, + 204.1541748046875 + ], + "57": [ + 435.107421875, + 195.88824462890625 + ], + "65": [ + 432.099365234375, + 190.137939453125 + ], + "73": [ + 426.6878967285156, + 178.90660095214844 + ], + "81": [ + 418.8594055175781, + 170.5100860595703 + ], + "89": [ + 411.3830261230469, + 158.05894470214844 + ], + "97": [ + 405.77960205078125, + 142.83619689941406 + ], + "105": [ + 399.63330078125, + 132.13729858398438 + ], + "113": [ + 391.3387756347656, + 118.40894317626953 + ], + "121": [ + 381.1039733886719, + 106.61896514892578 + ], + "128": [ + 371.6095275878906, + 93.8125228881836 + ] + }, + "projections": { + "1": [ + 467.03759382142596, + 254.29072521676525 + ], + "9": [ + 465.1645096531274, + 246.7589457064068 + ], + "17": [ + 463.9340788417902, + 240.9767536534039 + ], + "25": [ + 458.8868843663472, + 231.1552934216854 + ], + "33": [ + 449.54757580026774, + 218.10707731806912 + ], + "41": [ + 444.29519897193063, + 212.81074546897327 + ], + "49": [ + 439.76700511313044, + 202.95682874621554 + ], + "57": [ + 434.83918051406556, + 195.64096180047306 + ], + "65": [ + 432.2364309205651, + 191.42375411993453 + ], + "73": [ + 426.7255807351526, + 179.80865060658743 + ], + "81": [ + 419.0349317847196, + 172.0799048596901 + ], + "89": [ + 411.33475764513435, + 158.32121626450686 + ], + "97": [ + 405.5061293697758, + 141.864278851054 + ], + "105": [ + 399.76403841057606, + 132.18696267092383 + ], + "113": [ + 391.56684092974945, + 117.88048930625513 + ], + "121": [ + 381.44951898004024, + 106.37660085900973 + ], + "128": [ + 372.11806008891045, + 93.55468474261608 + ] + }, + "mean_error_pixels": 0.7157726888042262, + "max_error_pixels": 1.5796013850545083 + }, + "tower_window_above": { + "xyz": [ + -30.206881997980737, + 73.39670782875702, + 59.10901533956867 + ], + "observations": { + "1": [ + 469.0, + 235.0 + ], + "9": [ + 466.9984436035156, + 227.2169189453125 + ], + "17": [ + 465.7508850097656, + 220.1780548095703 + ], + "25": [ + 461.249267578125, + 210.5689697265625 + ], + "33": [ + 452.6790771484375, + 198.67703247070312 + ], + "41": [ + 447.21307373046875, + 192.49615478515625 + ], + "49": [ + 443.1694030761719, + 182.83709716796875 + ], + "57": [ + 438.1517333984375, + 174.4036865234375 + ], + "65": [ + 435.36932373046875, + 168.04501342773438 + ], + "73": [ + 430.4035339355469, + 156.72047424316406 + ], + "81": [ + 422.7218322753906, + 148.11119079589844 + ], + "89": [ + 415.48175048828125, + 135.50946044921875 + ], + "97": [ + 410.2955627441406, + 120.07831573486328 + ], + "105": [ + 404.6603698730469, + 109.10233306884766 + ], + "113": [ + 396.9137268066406, + 94.90699005126953 + ], + "121": [ + 386.98046875, + 82.55496215820312 + ], + "128": [ + 378.2152099609375, + 69.58731079101562 + ] + }, + "projections": { + "1": [ + 468.7264773990986, + 235.20557349676724 + ], + "9": [ + 467.0075302360989, + 227.3563555272901 + ], + "17": [ + 466.06240070642855, + 221.39007340123965 + ], + "25": [ + 461.1501282371954, + 211.13006414431595 + ], + "33": [ + 451.72774100488874, + 197.36628943922597 + ], + "41": [ + 446.69898828900284, + 191.87137890113905 + ], + "49": [ + 442.40787687887985, + 181.62900222397684 + ], + "57": [ + 437.78428947086695, + 174.0864542912534 + ], + "65": [ + 435.624073769623, + 169.8993539529232 + ], + "73": [ + 430.4298050570293, + 157.88643604363062 + ], + "81": [ + 422.9914558676447, + 149.91623215394105 + ], + "89": [ + 415.50772406488164, + 135.60305350462318 + ], + "97": [ + 410.07601879931485, + 118.58157024076803 + ], + "105": [ + 404.8623446629439, + 108.8056745899558 + ], + "113": [ + 397.20281156490455, + 94.17311424358962 + ], + "121": [ + 387.6399855506237, + 82.52295700462156 + ], + "128": [ + 378.7189959982258, + 69.37530489299064 + ] + }, + "mean_error_pixels": 0.91016418897469, + "max_error_pixels": 1.8717575606383132 + }, + "tower_window_left": { + "xyz": [ + -65.09771227637259, + 84.5620816143931, + 75.10958474060008 + ], + "observations": { + "1": [ + 335.0, + 200.0 + ], + "9": [ + 331.4877014160156, + 191.74758911132812 + ], + "17": [ + 328.3415222167969, + 183.99449157714844 + ], + "25": [ + 321.3124084472656, + 174.48814392089844 + ], + "33": [ + 310.0098571777344, + 163.9500274658203 + ], + "41": [ + 302.093017578125, + 158.16368103027344 + ], + "49": [ + 295.2342224121094, + 148.7094268798828 + ], + "57": [ + 287.56683349609375, + 140.78289794921875 + ], + "65": [ + 282.7301330566406, + 133.88592529296875 + ], + "73": [ + 275.2150573730469, + 122.91220092773438 + ], + "81": [ + 265.39862060546875, + 115.28715515136719 + ], + "89": [ + 255.3653106689453, + 104.95367431640625 + ], + "97": [ + 247.53680419921875, + 90.76495361328125 + ], + "105": [ + 239.53671264648438, + 80.81397247314453 + ], + "113": [ + 228.95188903808594, + 68.32307434082031 + ], + "121": [ + 216.32554626464844, + 58.18230438232422 + ], + "128": [ + 204.4120330810547, + 48.380897521972656 + ] + }, + "projections": { + "1": [ + 335.5897883402013, + 198.4256526101376 + ], + "9": [ + 332.21234069945183, + 191.0782462093203 + ], + "17": [ + 330.1548497185217, + 185.39594751577863 + ], + "25": [ + 322.2321901222826, + 174.9441073595467 + ], + "33": [ + 307.9112107634219, + 161.30077554055032 + ], + "41": [ + 300.8349389663795, + 156.81767653282918 + ], + "49": [ + 293.9321588725419, + 146.7319015695494 + ], + "57": [ + 287.1222530558825, + 139.72837857858946 + ], + "65": [ + 284.18815419430405, + 136.32361099743537 + ], + "73": [ + 276.10818467512746, + 124.72235705804627 + ], + "81": [ + 266.0761014438346, + 118.50991807925334 + ], + "89": [ + 255.31226337590272, + 106.01347280336233 + ], + "97": [ + 246.85864872704497, + 90.17363208664688 + ], + "105": [ + 239.91999599910037, + 82.11298444967377 + ], + "113": [ + 228.91077722127415, + 68.52642261483095 + ], + "121": [ + 215.8937513740791, + 58.19453415596456 + ], + "128": [ + 203.63433624335795, + 46.61628450057083 + ] + }, + "mean_error_pixels": 1.6915025523249403, + "max_error_pixels": 3.3797710774331255 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/materials/asphalt_track_files.json b/astra_scene_test/Scene_04/assets/materials/asphalt_track_files.json new file mode 100644 index 0000000000000000000000000000000000000000..9ed3dfa68e30ca311e9d3e9aab309dd6028e4fa7 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/materials/asphalt_track_files.json @@ -0,0 +1 @@ +{"Diffuse":{"8k":{"jpg":{"size":47987723,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_diff_8k.jpg","md5":"e0e0dbb41ed149b0f4a5a49437b244fa"},"png":{"size":351140287,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_diff_8k.png","md5":"bb895101f59a77924f6fbf8fe139b24d"},"exr":{"size":92188379,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_diff_8k.exr","md5":"5020e01b809cb7ccccb640654e2a8dbc"}},"4k":{"jpg":{"size":14847841,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_diff_4k.jpg","md5":"173f5f31c51f9d432931b3f17c734d3c"},"png":{"size":91057902,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_diff_4k.png","md5":"bf7829c05d44c2604b2140af204fbc6e"},"exr":{"size":30525271,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_diff_4k.exr","md5":"63af0119459adeae8b1d81c28f7779e6"}},"2k":{"jpg":{"size":3779263,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_diff_2k.jpg","md5":"faec159f1f29d231581c32bd256b5ede"},"png":{"size":22995688,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_diff_2k.png","md5":"b7df6b346da0b9faa4a6c2f4b480c002"},"exr":{"size":8531486,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_diff_2k.exr","md5":"a96f4f1ae0c4efd5d839e1c261832c33"}},"1k":{"jpg":{"size":858925,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_diff_1k.jpg","md5":"92e0682878c39430e521cce235ac1f75"},"png":{"size":5694360,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_diff_1k.png","md5":"10ac8b303641094d451807d161bb724a"},"exr":{"size":2078907,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_diff_1k.exr","md5":"e3ffd6cf8978158c459c76dee9e25cda"}}},"nor_dx":{"8k":{"jpg":{"size":61409593,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_nor_dx_8k.jpg","md5":"125c641932aa9c77a7e5e6468f3f3292"},"png":{"size":354764065,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_nor_dx_8k.png","md5":"9990f67d0f2359902385b4dbdf1afec8"},"exr":{"size":129304411,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_nor_dx_8k.exr","md5":"824c39f9547b1e8e6166f16da332b714"}},"4k":{"jpg":{"size":15227810,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_nor_dx_4k.jpg","md5":"22750f3616f292a037af1926f751f557"},"png":{"size":89478134,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_nor_dx_4k.png","md5":"e2fbf996095824e883c919445a6baef8"},"exr":{"size":32357114,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_nor_dx_4k.exr","md5":"5b029be3e9f9f8dd1a162c2f97c60bb3"}},"2k":{"jpg":{"size":3687631,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_nor_dx_2k.jpg","md5":"81fb6473062c9a2de8b59746723a345c"},"png":{"size":22357092,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_nor_dx_2k.png","md5":"82c1b75221147953e206b139e3ed1f44"},"exr":{"size":7977760,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_nor_dx_2k.exr","md5":"ca840156f3f2878fa67a0dedcdd11c7b"}},"1k":{"jpg":{"size":833383,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_nor_dx_1k.jpg","md5":"cb52486599d3c14385e09bdb4fecfb6f"},"png":{"size":5576795,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_nor_dx_1k.png","md5":"63ee6deb2d5221a5bc9489674c4f9cc6"},"exr":{"size":1906325,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_nor_dx_1k.exr","md5":"c492f0fed46b598103f91deb17655dd3"}}},"nor_gl":{"8k":{"jpg":{"size":61578400,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_nor_gl_8k.jpg","md5":"1d61a272d114f1c02ae1232b4d80bd67"},"png":{"size":355641745,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_nor_gl_8k.png","md5":"883c8e5dfe7bc9b36eca5a4f87cbad0f"},"exr":{"size":129393690,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_nor_gl_8k.exr","md5":"76506327724646f9aecf3faf7b25c68c"}},"4k":{"jpg":{"size":15267669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_nor_gl_4k.jpg","md5":"b1330784382948d82765709a1c1390e9"},"png":{"size":89478315,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_nor_gl_4k.png","md5":"15c4515b3ca2621300dc61328194cbe3"},"exr":{"size":32371203,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_nor_gl_4k.exr","md5":"8d75ac89f3836309e03df335a23798c7"}},"2k":{"jpg":{"size":3694762,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_nor_gl_2k.jpg","md5":"5381e78d5deda41e9ff5dd9c317bfa62"},"png":{"size":22358634,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_nor_gl_2k.png","md5":"e4fa33856e0f3146c2de85f3a08dd343"},"exr":{"size":7982651,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_nor_gl_2k.exr","md5":"d7e59eb012c0f37a3c5a0dd51e535a18"}},"1k":{"jpg":{"size":832851,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_nor_gl_1k.jpg","md5":"d61d8e22e24ea0e44873b9c9c97d4e0c"},"png":{"size":5576836,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_nor_gl_1k.png","md5":"af77c53cabdcccd619d85322f7af1535"},"exr":{"size":1905508,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_nor_gl_1k.exr","md5":"abc2203a45666b0a614273e564d93c96"}}},"blend":{"8k":{"blend":{"include":{"textures/asphalt_track_disp_8k.png":{"size":85805855,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_disp_8k.png","md5":"7620fee4adfdbd5e23a05bed6b3823b9"},"textures/asphalt_track_rough_8k.exr":{"size":24430548,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_rough_8k.exr","md5":"511b6581bb522a79fc783a4bfe9d7a18"},"textures/asphalt_track_diff_8k.jpg":{"size":47987723,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_diff_8k.jpg","md5":"e0e0dbb41ed149b0f4a5a49437b244fa"},"textures/asphalt_track_nor_gl_8k.exr":{"size":129393690,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_nor_gl_8k.exr","md5":"76506327724646f9aecf3faf7b25c68c"}},"size":217281,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/8k/asphalt_track/asphalt_track_8k.blend","md5":"ec94b6f3a874572cba4f43bf5e55062c"}},"4k":{"blend":{"include":{"textures/asphalt_track_diff_4k.jpg":{"size":14847841,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_diff_4k.jpg","md5":"173f5f31c51f9d432931b3f17c734d3c"},"textures/asphalt_track_disp_4k.png":{"size":24113156,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_disp_4k.png","md5":"b9c25daad8006e9fb066560ac8cb82e2"},"textures/asphalt_track_nor_gl_4k.exr":{"size":32371203,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_nor_gl_4k.exr","md5":"8d75ac89f3836309e03df335a23798c7"},"textures/asphalt_track_rough_4k.exr":{"size":8329545,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_rough_4k.exr","md5":"4ce96b2a791267ff9305d0730eaefd20"}},"size":217483,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/4k/asphalt_track/asphalt_track_4k.blend","md5":"34493bb39c00fb13f53e468e630c40f0"}},"2k":{"blend":{"include":{"textures/asphalt_track_nor_gl_2k.exr":{"size":7982651,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_nor_gl_2k.exr","md5":"d7e59eb012c0f37a3c5a0dd51e535a18"},"textures/asphalt_track_rough_2k.exr":{"size":2156438,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_rough_2k.exr","md5":"0d7c3bd6a59e7d9853953233855c3d15"},"textures/asphalt_track_disp_2k.png":{"size":6257146,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_disp_2k.png","md5":"799c1ce524ef0cc8353876aa4994e6c5"},"textures/asphalt_track_diff_2k.jpg":{"size":3779263,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_diff_2k.jpg","md5":"faec159f1f29d231581c32bd256b5ede"}},"size":217343,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/2k/asphalt_track/asphalt_track_2k.blend","md5":"c2a4bad9d5cc44775f3b0dd67085b8cb"}},"1k":{"blend":{"include":{"textures/asphalt_track_diff_1k.jpg":{"size":858925,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_diff_1k.jpg","md5":"92e0682878c39430e521cce235ac1f75"},"textures/asphalt_track_disp_1k.png":{"size":1611338,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_disp_1k.png","md5":"a70a3b47069f56629b076beb7fbf69d1"},"textures/asphalt_track_rough_1k.exr":{"size":464541,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_rough_1k.exr","md5":"0bcfe4ce111da735856129f628ca7696"},"textures/asphalt_track_nor_gl_1k.exr":{"size":1905508,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_nor_gl_1k.exr","md5":"abc2203a45666b0a614273e564d93c96"}},"size":217388,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/1k/asphalt_track/asphalt_track_1k.blend","md5":"79280330ecf39f39f101bf4e9e11cbcf"}}},"gltf":{"8k":{"gltf":{"include":{"asphalt_track.bin":{"size":2370708,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/asphalt_track/asphalt_track.bin","md5":"9a58bf9fb3baf83887ff9690abb127ec"},"textures/asphalt_track_arm_8k.jpg":{"size":39077381,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_arm_8k.jpg","md5":"63c11f866cf3f220b31f34f61ee9a7b8"},"textures/asphalt_track_diff_8k.jpg":{"size":47987723,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_diff_8k.jpg","md5":"e0e0dbb41ed149b0f4a5a49437b244fa"},"textures/asphalt_track_nor_gl_8k.jpg":{"size":61578400,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_nor_gl_8k.jpg","md5":"1d61a272d114f1c02ae1232b4d80bd67"}},"size":2789,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/asphalt_track/asphalt_track_8k.gltf","md5":"a9b6215b4afc4eb8d240ee2ec061d6cf"}},"4k":{"gltf":{"include":{"asphalt_track.bin":{"size":2370708,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/asphalt_track/asphalt_track.bin","md5":"9a58bf9fb3baf83887ff9690abb127ec"},"textures/asphalt_track_diff_4k.jpg":{"size":14847841,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_diff_4k.jpg","md5":"173f5f31c51f9d432931b3f17c734d3c"},"textures/asphalt_track_arm_4k.jpg":{"size":10594366,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_arm_4k.jpg","md5":"d428dd6753ba7fe6877852196d641813"},"textures/asphalt_track_nor_gl_4k.jpg":{"size":15267669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_nor_gl_4k.jpg","md5":"b1330784382948d82765709a1c1390e9"}},"size":2789,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/4k/asphalt_track/asphalt_track_4k.gltf","md5":"26c7a6058d711d231dabb8d368103213"}},"2k":{"gltf":{"include":{"asphalt_track.bin":{"size":2370708,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/asphalt_track/asphalt_track.bin","md5":"9a58bf9fb3baf83887ff9690abb127ec"},"textures/asphalt_track_diff_2k.jpg":{"size":3779263,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_diff_2k.jpg","md5":"faec159f1f29d231581c32bd256b5ede"},"textures/asphalt_track_arm_2k.jpg":{"size":2469962,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_arm_2k.jpg","md5":"a528253bac67e8570553c4eac14d97e8"},"textures/asphalt_track_nor_gl_2k.jpg":{"size":3694762,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_nor_gl_2k.jpg","md5":"5381e78d5deda41e9ff5dd9c317bfa62"}},"size":2789,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/2k/asphalt_track/asphalt_track_2k.gltf","md5":"5eb57493df2eabcdf9ef1f5a31ac2683"}},"1k":{"gltf":{"include":{"asphalt_track.bin":{"size":2370708,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/asphalt_track/asphalt_track.bin","md5":"9a58bf9fb3baf83887ff9690abb127ec"},"textures/asphalt_track_arm_1k.jpg":{"size":529896,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_arm_1k.jpg","md5":"f38c5981fb7f8e8c9a8a42726d7fe5a5"},"textures/asphalt_track_diff_1k.jpg":{"size":858925,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_diff_1k.jpg","md5":"92e0682878c39430e521cce235ac1f75"},"textures/asphalt_track_nor_gl_1k.jpg":{"size":832851,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_nor_gl_1k.jpg","md5":"d61d8e22e24ea0e44873b9c9c97d4e0c"}},"size":2789,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/1k/asphalt_track/asphalt_track_1k.gltf","md5":"6360b5017a755187b401eb3c4eb0a5a3"}}},"arm":{"8k":{"jpg":{"size":39077381,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_arm_8k.jpg","md5":"63c11f866cf3f220b31f34f61ee9a7b8"},"png":{"size":245720880,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_arm_8k.png","md5":"42212a45b70d39e52417bbb649411842"},"exr":{"size":66026359,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_arm_8k.exr","md5":"66df4694cbcbe8f68e1df23df0c32f6e"}},"4k":{"jpg":{"size":10594366,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_arm_4k.jpg","md5":"d428dd6753ba7fe6877852196d641813"},"png":{"size":66231113,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_arm_4k.png","md5":"93fc13769c0e0b5be8cb49ca2781ee1a"},"exr":{"size":21063764,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_arm_4k.exr","md5":"f9262b4ed253f3638d3eef9c74b8d7b9"}},"2k":{"jpg":{"size":2469962,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_arm_2k.jpg","md5":"a528253bac67e8570553c4eac14d97e8"},"png":{"size":17082979,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_arm_2k.png","md5":"ba907661fae10c071109392b930d3207"},"exr":{"size":5367706,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_arm_2k.exr","md5":"04a6c7a4b18ff32186f6573d3e93251b"}},"1k":{"jpg":{"size":529896,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_arm_1k.jpg","md5":"f38c5981fb7f8e8c9a8a42726d7fe5a5"},"png":{"size":4202359,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_arm_1k.png","md5":"7e79e55a0261bb50b09283bfcdfd54d8"},"exr":{"size":1071236,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_arm_1k.exr","md5":"008e54cb2c5923d85da0e4085c7deac0"}}},"AO":{"8k":{"jpg":{"size":37686131,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_ao_8k.jpg","md5":"39f265397f46132b319d9d5318ec0256"},"png":{"size":95367135,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_ao_8k.png","md5":"785de799c4ce307a3e8ca4ebcbd5e9d9"},"exr":{"size":35224140,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_ao_8k.exr","md5":"054c4fd7a6dd39a17da8452e551ed120"}},"4k":{"jpg":{"size":9924792,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_ao_4k.jpg","md5":"6f4b7e0e8faf2f043f7e65933695a781"},"png":{"size":27808243,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_ao_4k.png","md5":"90ec429d1f815875cac9385b94ff23f8"},"exr":{"size":9492270,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_ao_4k.exr","md5":"7576a70c0a535481702925a12eaac0e3"}},"2k":{"jpg":{"size":2411966,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_ao_2k.jpg","md5":"1e5ceb618cfa35b6481fdaf2dd1427c1"},"png":{"size":7333205,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_ao_2k.png","md5":"a916b15ac30672e6d2272af7e0f772ec"},"exr":{"size":2341584,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_ao_2k.exr","md5":"37a3848f119e717b3c5b4898b1ee8629"}},"1k":{"jpg":{"size":572610,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_ao_1k.jpg","md5":"c6e83474bcca7fd9307860934ca20918"},"png":{"size":1827130,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_ao_1k.png","md5":"ab027d04f509da36f2098c8fa53a1594"},"exr":{"size":572371,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_ao_1k.exr","md5":"6b667816cbbaaa0d3c15c62f2460b407"}}},"Displacement":{"8k":{"jpg":{"size":9594294,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_disp_8k.jpg","md5":"4739f20ca761e585a0d9270dcef0296a"},"png":{"size":85805855,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_disp_8k.png","md5":"7620fee4adfdbd5e23a05bed6b3823b9"},"exr":{"size":6830453,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_disp_8k.exr","md5":"e858eea408332aa74aeb768efcb39c7e"}},"4k":{"jpg":{"size":2996796,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_disp_4k.jpg","md5":"5030513f53caeae6f2f8fd81f0c9ea5a"},"png":{"size":24113156,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_disp_4k.png","md5":"b9c25daad8006e9fb066560ac8cb82e2"},"exr":{"size":2399037,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_disp_4k.exr","md5":"be8a8cff2e2d9b3040febd9e1ebdaa73"}},"2k":{"jpg":{"size":929202,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_disp_2k.jpg","md5":"1dd05731262bf00e6c691ff9151280d8"},"png":{"size":6257146,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_disp_2k.png","md5":"799c1ce524ef0cc8353876aa4994e6c5"},"exr":{"size":804834,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_disp_2k.exr","md5":"34cc177a575276608c978a9ef84f2516"}},"1k":{"jpg":{"size":280742,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_disp_1k.jpg","md5":"b28970fde2a5c20efa71a8e377ed426f"},"png":{"size":1611338,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_disp_1k.png","md5":"a70a3b47069f56629b076beb7fbf69d1"},"exr":{"size":244015,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_disp_1k.exr","md5":"c5fd1268d85f483ed6fbea16e4b693e2"}}},"Rough":{"8k":{"jpg":{"size":26684611,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_rough_8k.jpg","md5":"232d529d5b522e488529a2b0c3b3d422"},"png":{"size":110546291,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_rough_8k.png","md5":"c99606faeff35f85be950977cb5f6c95"},"exr":{"size":24430548,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_rough_8k.exr","md5":"511b6581bb522a79fc783a4bfe9d7a18"}},"4k":{"jpg":{"size":8806397,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_rough_4k.jpg","md5":"e1c7654e45dffccdb2edc1c48b65a707"},"png":{"size":28902111,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_rough_4k.png","md5":"776debbb78181ca1748aeed3a70b92df"},"exr":{"size":8329545,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_rough_4k.exr","md5":"4ce96b2a791267ff9305d0730eaefd20"}},"2k":{"jpg":{"size":2182047,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_rough_2k.jpg","md5":"213b9f13c03772e24a9cd55c4e43f391"},"png":{"size":7275946,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_rough_2k.png","md5":"57444a326e829d84d9ef6232f15be406"},"exr":{"size":2156438,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_rough_2k.exr","md5":"0d7c3bd6a59e7d9853953233855c3d15"}},"1k":{"jpg":{"size":459197,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_rough_1k.jpg","md5":"c57f7b3a42ebfaf58e44c0ccc83326c3"},"png":{"size":1758717,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_rough_1k.png","md5":"c092a77d75b861a8b3be9f31e7ec5067"},"exr":{"size":464541,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_rough_1k.exr","md5":"0bcfe4ce111da735856129f628ca7696"}}},"mtlx":{"8k":{"mtlx":{"include":{"textures/asphalt_track_disp_8k.png":{"size":85805855,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/asphalt_track/asphalt_track_disp_8k.png","md5":"7620fee4adfdbd5e23a05bed6b3823b9"},"textures/asphalt_track_rough_8k.exr":{"size":24430548,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_rough_8k.exr","md5":"511b6581bb522a79fc783a4bfe9d7a18"},"textures/asphalt_track_diff_8k.jpg":{"size":47987723,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/asphalt_track/asphalt_track_diff_8k.jpg","md5":"e0e0dbb41ed149b0f4a5a49437b244fa"},"textures/asphalt_track_nor_gl_8k.exr":{"size":129393690,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/asphalt_track/asphalt_track_nor_gl_8k.exr","md5":"76506327724646f9aecf3faf7b25c68c"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/8k/asphalt_track/asphalt_track_8k.mtlx","md5":"3ef147f4cf6499a0cfb3f6acfae81994"}},"4k":{"mtlx":{"include":{"textures/asphalt_track_diff_4k.jpg":{"size":14847841,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/asphalt_track/asphalt_track_diff_4k.jpg","md5":"173f5f31c51f9d432931b3f17c734d3c"},"textures/asphalt_track_disp_4k.png":{"size":24113156,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/asphalt_track/asphalt_track_disp_4k.png","md5":"b9c25daad8006e9fb066560ac8cb82e2"},"textures/asphalt_track_nor_gl_4k.exr":{"size":32371203,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_nor_gl_4k.exr","md5":"8d75ac89f3836309e03df335a23798c7"},"textures/asphalt_track_rough_4k.exr":{"size":8329545,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/asphalt_track/asphalt_track_rough_4k.exr","md5":"4ce96b2a791267ff9305d0730eaefd20"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/4k/asphalt_track/asphalt_track_4k.mtlx","md5":"dcf83e19c0a84b773170b8c25cc76455"}},"2k":{"mtlx":{"include":{"textures/asphalt_track_nor_gl_2k.exr":{"size":7982651,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_nor_gl_2k.exr","md5":"d7e59eb012c0f37a3c5a0dd51e535a18"},"textures/asphalt_track_rough_2k.exr":{"size":2156438,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/asphalt_track/asphalt_track_rough_2k.exr","md5":"0d7c3bd6a59e7d9853953233855c3d15"},"textures/asphalt_track_disp_2k.png":{"size":6257146,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_disp_2k.png","md5":"799c1ce524ef0cc8353876aa4994e6c5"},"textures/asphalt_track_diff_2k.jpg":{"size":3779263,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_diff_2k.jpg","md5":"faec159f1f29d231581c32bd256b5ede"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/2k/asphalt_track/asphalt_track_2k.mtlx","md5":"8acc0f64f9f51397c1bcc24d49dfa27c"}},"1k":{"mtlx":{"include":{"textures/asphalt_track_diff_1k.jpg":{"size":858925,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/asphalt_track/asphalt_track_diff_1k.jpg","md5":"92e0682878c39430e521cce235ac1f75"},"textures/asphalt_track_disp_1k.png":{"size":1611338,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/asphalt_track/asphalt_track_disp_1k.png","md5":"a70a3b47069f56629b076beb7fbf69d1"},"textures/asphalt_track_rough_1k.exr":{"size":464541,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_rough_1k.exr","md5":"0bcfe4ce111da735856129f628ca7696"},"textures/asphalt_track_nor_gl_1k.exr":{"size":1905508,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/asphalt_track/asphalt_track_nor_gl_1k.exr","md5":"abc2203a45666b0a614273e564d93c96"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/1k/asphalt_track/asphalt_track_1k.mtlx","md5":"07742ba313308bfa42fdadd0ae22b471"}}}} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/materials/brick_wall_09_files.json b/astra_scene_test/Scene_04/assets/materials/brick_wall_09_files.json new file mode 100644 index 0000000000000000000000000000000000000000..d281fcddfb34eec2082761721be7a530585608ed --- /dev/null +++ b/astra_scene_test/Scene_04/assets/materials/brick_wall_09_files.json @@ -0,0 +1 @@ +{"Diffuse":{"8k":{"jpg":{"size":49298042,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_diff_8k.jpg","md5":"016759ec830a1bc05be68ff4bd2cb9fe"},"png":{"size":332200652,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_diff_8k.png","md5":"a39c587fc604f2d7130387bbcf368cdf"},"exr":{"size":72041425,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_diff_8k.exr","md5":"8f52a97f76a8c0c4b642f634dae2f564"}},"16k":{"jpg":{"size":155495415,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_diff_16k.jpg","md5":"9179a8fca35bba6f3ea7b6861f01d403"},"png":{"size":1206706898,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_diff_16k.png","md5":"5876d9dca9f3650c167be12e54968604"},"exr":{"size":212163861,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_diff_16k.exr","md5":"8a662d9421eafeea9d629c9fc33b8c1e"}},"4k":{"jpg":{"size":12691833,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_diff_4k.jpg","md5":"5b57d70ff2e76ae09b083054f118282f"},"png":{"size":86086698,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_diff_4k.png","md5":"edfe0528915c3e312b18a0e8f555b8c9"},"exr":{"size":20800432,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_diff_4k.exr","md5":"36ad32604a34a5c1da7c4d6b9c7a9c75"}},"2k":{"jpg":{"size":3116669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_diff_2k.jpg","md5":"f675297a70b77f881feb18c012b53b82"},"png":{"size":22071408,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_diff_2k.png","md5":"2ac295b100129f7221df920498d462be"},"exr":{"size":5429958,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_diff_2k.exr","md5":"fd5da95e9800518d830af968c20ac90b"}},"1k":{"jpg":{"size":777371,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_diff_1k.jpg","md5":"d08a1a4be611135d174f051189e00156"},"png":{"size":5495107,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_diff_1k.png","md5":"fa911ea2501ad438ee3715b05d23b977"},"exr":{"size":1391393,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_diff_1k.exr","md5":"5a71c382f6696e4a8e0a6d652119b2c5"}}},"nor_dx":{"8k":{"jpg":{"size":72927862,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_nor_dx_8k.jpg","md5":"a272247bd8d4360ef282e5edf395f33b"},"png":{"size":362198222,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_nor_dx_8k.png","md5":"a296dab0cc36b219ba16c7fde49ab6c4"},"exr":{"size":143963731,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_nor_dx_8k.exr","md5":"2b2c2ceaceea290dd11050667768823e"}},"16k":{"jpg":{"size":238233072,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_nor_dx_16k.jpg","md5":"509c120cf44c17f41cc9dd9313b4bc8b"},"png":{"size":1322132992,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_nor_dx_16k.png","md5":"a53df4dc9c22a22d8d3a94bcbcf0e0bc"},"exr":{"size":449523497,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_nor_dx_16k.exr","md5":"2185ba310acdc1383105a917bf9ae235"}},"4k":{"jpg":{"size":19039005,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_nor_dx_4k.jpg","md5":"9811b8716451f4d07b7db2bb50d43f53"},"png":{"size":93004905,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_nor_dx_4k.png","md5":"da7d2518d3a905d4d2e1ec06dd119a27"},"exr":{"size":37978537,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_nor_dx_4k.exr","md5":"acd363b9cfd17a06f445944e4b71a091"}},"2k":{"jpg":{"size":4506692,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_nor_dx_2k.jpg","md5":"ca757a730cd079c2fee9ff9832102fcb"},"png":{"size":23357919,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_nor_dx_2k.png","md5":"a2e2ef0ea329970bd9a75db5fe3764e7"},"exr":{"size":9276470,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_nor_dx_2k.exr","md5":"2b2a4fe64df91fcfc962f231e2df36e8"}},"1k":{"jpg":{"size":1036781,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_nor_dx_1k.jpg","md5":"b16a16056e2012adb6a5a6f7d34a61ca"},"png":{"size":5755493,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_nor_dx_1k.png","md5":"54cb847b41a9d15a1a29c834cac32fe3"},"exr":{"size":2224723,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_nor_dx_1k.exr","md5":"e9e1369bdacc493156e12af93488de68"}}},"nor_gl":{"8k":{"jpg":{"size":72875273,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_nor_gl_8k.jpg","md5":"e86e02f10cd582a2c664f7a5811fdc1b"},"png":{"size":362195296,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_nor_gl_8k.png","md5":"305c91c4310b82a6c7b7ac33b9c9437f"},"exr":{"size":143777324,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_nor_gl_8k.exr","md5":"36d30bbaf3f23500f69371b5e473ebe3"}},"16k":{"jpg":{"size":238157952,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_nor_gl_16k.jpg","md5":"e6a248bc9e238bc1f67fb79e2770b209"},"png":{"size":1326437253,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_nor_gl_16k.png","md5":"df35660ae66570de27bb4c81a0ec01fb"},"exr":{"size":448776519,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_nor_gl_16k.exr","md5":"11f8235722f7caaebff806ae264c18d3"}},"4k":{"jpg":{"size":19034343,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_nor_gl_4k.jpg","md5":"708479dad1c3d63e5468e016a44d46ac"},"png":{"size":93005762,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_nor_gl_4k.png","md5":"f1bebc6ef51d241669c8228e04656529"},"exr":{"size":37937299,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_nor_gl_4k.exr","md5":"02bc11ba5ff6fbceabf9138c98655d82"}},"2k":{"jpg":{"size":4511408,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_nor_gl_2k.jpg","md5":"4541c3d3354e3f22f96f47a944c9d742"},"png":{"size":23359116,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_nor_gl_2k.png","md5":"bb665bddf097079d6800001bd85d7b65"},"exr":{"size":9264325,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_nor_gl_2k.exr","md5":"3e3a1bf42a79463783ee1871d45ce03e"}},"1k":{"jpg":{"size":1039733,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_nor_gl_1k.jpg","md5":"dabdbe435f6880f41dc7bbdc3f02840a"},"png":{"size":5756520,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_nor_gl_1k.png","md5":"600679747309e94afdd49915a74c2126"},"exr":{"size":2221558,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_nor_gl_1k.exr","md5":"c988ebdc35bfc02f9c8052bd4aac0706"}}},"blend":{"8k":{"blend":{"include":{"textures/brick_wall_09_disp_8k.png":{"size":88096829,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_disp_8k.png","md5":"cda83fc359e02a3885f34d33490938f7"},"textures/brick_wall_09_nor_gl_8k.exr":{"size":143777324,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_nor_gl_8k.exr","md5":"36d30bbaf3f23500f69371b5e473ebe3"},"textures/brick_wall_09_rough_8k.exr":{"size":25304479,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_rough_8k.exr","md5":"7fe7e628a014ff92fb9b26d0b4104ec8"},"textures/brick_wall_09_diff_8k.jpg":{"size":49298042,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_diff_8k.jpg","md5":"016759ec830a1bc05be68ff4bd2cb9fe"},"textures/brick_wall_09_ao_8k.jpg":{"size":44278667,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_ao_8k.jpg","md5":"b50cea2691e71df0109656ac340fcb28"}},"size":216884,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/8k/brick_wall_09/brick_wall_09_8k.blend","md5":"decd3c9ffa14b26c8c62240f65d4c882"}},"16k":{"blend":{"include":{"textures/brick_wall_09_ao_16k.jpg":{"size":140408600,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_ao_16k.jpg","md5":"53342e3792410319ffe397d0033b0040"},"textures/brick_wall_09_diff_16k.jpg":{"size":155495415,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_diff_16k.jpg","md5":"9179a8fca35bba6f3ea7b6861f01d403"},"textures/brick_wall_09_rough_16k.exr":{"size":77965153,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_rough_16k.exr","md5":"366589786c5b93232f82056d655ef06c"},"textures/brick_wall_09_disp_16k.png":{"size":237367010,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_disp_16k.png","md5":"9185ef66c96c134a5ac24af9879e42da"},"textures/brick_wall_09_nor_gl_16k.exr":{"size":448776519,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_nor_gl_16k.exr","md5":"11f8235722f7caaebff806ae264c18d3"}},"size":216932,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/16k/brick_wall_09/brick_wall_09_16k.blend","md5":"3b4c91a02820f97b5461f9274b0dfe89"}},"4k":{"blend":{"include":{"textures/brick_wall_09_rough_4k.exr":{"size":6705597,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_rough_4k.exr","md5":"15caf4f7750374fbf351780216523968"},"textures/brick_wall_09_diff_4k.jpg":{"size":12691833,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_diff_4k.jpg","md5":"5b57d70ff2e76ae09b083054f118282f"},"textures/brick_wall_09_nor_gl_4k.exr":{"size":37937299,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_nor_gl_4k.exr","md5":"02bc11ba5ff6fbceabf9138c98655d82"},"textures/brick_wall_09_disp_4k.png":{"size":24534598,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_disp_4k.png","md5":"b832de2f89d1e0644b6dd995811271e9"},"textures/brick_wall_09_ao_4k.jpg":{"size":11653676,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_ao_4k.jpg","md5":"9f051cc8a5745dbccc8a8e38777413fd"}},"size":216969,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/4k/brick_wall_09/brick_wall_09_4k.blend","md5":"56d8279308611127269c0b47f631eb62"}},"2k":{"blend":{"include":{"textures/brick_wall_09_nor_gl_2k.exr":{"size":9264325,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_nor_gl_2k.exr","md5":"3e3a1bf42a79463783ee1871d45ce03e"},"textures/brick_wall_09_diff_2k.jpg":{"size":3116669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_diff_2k.jpg","md5":"f675297a70b77f881feb18c012b53b82"},"textures/brick_wall_09_disp_2k.png":{"size":6566084,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_disp_2k.png","md5":"d732d48d64c45a0041fc034edf5716d8"},"textures/brick_wall_09_ao_2k.jpg":{"size":2844498,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_ao_2k.jpg","md5":"b0b8c85b5b3ee4764723a660ceb6d811"},"textures/brick_wall_09_rough_2k.exr":{"size":1639454,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_rough_2k.exr","md5":"2267550a6e854e671976bd3b395e33d3"}},"size":217161,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/2k/brick_wall_09/brick_wall_09_2k.blend","md5":"e2493af833b6f8ebb1fee2f2d552a518"}},"1k":{"blend":{"include":{"textures/brick_wall_09_ao_1k.jpg":{"size":674910,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_ao_1k.jpg","md5":"a87c963ee7255179cae3d6f8a7b55294"},"textures/brick_wall_09_rough_1k.exr":{"size":415236,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_rough_1k.exr","md5":"21cf95c4267f53671e813bb08cd65032"},"textures/brick_wall_09_nor_gl_1k.exr":{"size":2221558,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_nor_gl_1k.exr","md5":"c988ebdc35bfc02f9c8052bd4aac0706"},"textures/brick_wall_09_diff_1k.jpg":{"size":777371,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_diff_1k.jpg","md5":"d08a1a4be611135d174f051189e00156"},"textures/brick_wall_09_disp_1k.png":{"size":1691460,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_disp_1k.png","md5":"4d3f93b662970f42471ea50986c6f52d"}},"size":217097,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/blend/1k/brick_wall_09/brick_wall_09_1k.blend","md5":"761a2d28b27aff0dc1b176a66d9efa17"}}},"gltf":{"8k":{"gltf":{"include":{"textures/brick_wall_09_nor_gl_8k.jpg":{"size":72875273,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_nor_gl_8k.jpg","md5":"e86e02f10cd582a2c664f7a5811fdc1b"},"textures/brick_wall_09_diff_8k.jpg":{"size":49298042,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_diff_8k.jpg","md5":"016759ec830a1bc05be68ff4bd2cb9fe"},"brick_wall_09.bin":{"size":2370484,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09.bin","md5":"106fdd40ad9b7e9613c60c18be089e6b"},"textures/brick_wall_09_arm_8k.jpg":{"size":44484274,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_arm_8k.jpg","md5":"7c6c35d629abee063d64d40dffa161eb"}},"size":2788,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09_8k.gltf","md5":"5adab831d3445a0e6bbf223a8773f049"}},"16k":{"gltf":{"include":{"textures/brick_wall_09_arm_16k.jpg":{"size":148428735,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_arm_16k.jpg","md5":"3515ded32065cd02410842d0d529e67f"},"textures/brick_wall_09_diff_16k.jpg":{"size":155495415,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_diff_16k.jpg","md5":"9179a8fca35bba6f3ea7b6861f01d403"},"brick_wall_09.bin":{"size":2370484,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09.bin","md5":"106fdd40ad9b7e9613c60c18be089e6b"},"textures/brick_wall_09_nor_gl_16k.jpg":{"size":238157952,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_nor_gl_16k.jpg","md5":"e6a248bc9e238bc1f67fb79e2770b209"}},"size":2791,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/16k/brick_wall_09/brick_wall_09_16k.gltf","md5":"3afb9cd641ef59f6a8d77b225ab9930f"}},"4k":{"gltf":{"include":{"textures/brick_wall_09_diff_4k.jpg":{"size":12691833,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_diff_4k.jpg","md5":"5b57d70ff2e76ae09b083054f118282f"},"textures/brick_wall_09_nor_gl_4k.jpg":{"size":19034343,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_nor_gl_4k.jpg","md5":"708479dad1c3d63e5468e016a44d46ac"},"brick_wall_09.bin":{"size":2370484,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09.bin","md5":"106fdd40ad9b7e9613c60c18be089e6b"},"textures/brick_wall_09_arm_4k.jpg":{"size":11386232,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_arm_4k.jpg","md5":"dcf393f28258f23cf8d77703e3ebf533"}},"size":2788,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/4k/brick_wall_09/brick_wall_09_4k.gltf","md5":"6eb8708646c2d21101e4eca274d89ef6"}},"2k":{"gltf":{"include":{"textures/brick_wall_09_arm_2k.jpg":{"size":2757145,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_arm_2k.jpg","md5":"199f99ebdca034c57e9e068605bbb51a"},"textures/brick_wall_09_nor_gl_2k.jpg":{"size":4511408,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_nor_gl_2k.jpg","md5":"4541c3d3354e3f22f96f47a944c9d742"},"textures/brick_wall_09_diff_2k.jpg":{"size":3116669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_diff_2k.jpg","md5":"f675297a70b77f881feb18c012b53b82"},"brick_wall_09.bin":{"size":2370484,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09.bin","md5":"106fdd40ad9b7e9613c60c18be089e6b"}},"size":2788,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/2k/brick_wall_09/brick_wall_09_2k.gltf","md5":"cec5f58b61b63b0a6ed86452c32c3a57"}},"1k":{"gltf":{"include":{"textures/brick_wall_09_arm_1k.jpg":{"size":663260,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_arm_1k.jpg","md5":"8cf1a80689ae46cfd9d2da63ff887b58"},"brick_wall_09.bin":{"size":2370484,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/8k/brick_wall_09/brick_wall_09.bin","md5":"106fdd40ad9b7e9613c60c18be089e6b"},"textures/brick_wall_09_nor_gl_1k.jpg":{"size":1039733,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_nor_gl_1k.jpg","md5":"dabdbe435f6880f41dc7bbdc3f02840a"},"textures/brick_wall_09_diff_1k.jpg":{"size":777371,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_diff_1k.jpg","md5":"d08a1a4be611135d174f051189e00156"}},"size":2788,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/gltf/1k/brick_wall_09/brick_wall_09_1k.gltf","md5":"a5b12607b7b1b417036a81762f59ce7d"}}},"arm":{"8k":{"jpg":{"size":44484274,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_arm_8k.jpg","md5":"7c6c35d629abee063d64d40dffa161eb"},"png":{"size":254300806,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_arm_8k.png","md5":"87a3b38d8fa98c5bf7c4b42a5c6e2f36"},"exr":{"size":77156733,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_arm_8k.exr","md5":"7ef34303ee060434137d4b7720d7890a"}},"16k":{"jpg":{"size":148428735,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_arm_16k.jpg","md5":"3515ded32065cd02410842d0d529e67f"},"png":{"size":904971424,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_arm_16k.png","md5":"3b5305a629b37f21aadc84f880a9b389"},"exr":{"size":228254798,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_arm_16k.exr","md5":"cf9fff9a8a21f0b41ad19b9ce037c548"}},"4k":{"jpg":{"size":11386232,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_arm_4k.jpg","md5":"dcf393f28258f23cf8d77703e3ebf533"},"png":{"size":66241957,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_arm_4k.png","md5":"d40d23edc84812ba1e00c86f88df247a"},"exr":{"size":20810638,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_arm_4k.exr","md5":"9ebdd95670ccb44bd22a0c8d57ab8bad"}},"2k":{"jpg":{"size":2757145,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_arm_2k.jpg","md5":"199f99ebdca034c57e9e068605bbb51a"},"png":{"size":16974235,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_arm_2k.png","md5":"e12bd3fdff77351e3f59a1fde34e66fb"},"exr":{"size":5060896,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_arm_2k.exr","md5":"ee70cd48a62a1bc96f45536541d3e74f"}},"1k":{"jpg":{"size":663260,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_arm_1k.jpg","md5":"8cf1a80689ae46cfd9d2da63ff887b58"},"png":{"size":4213285,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_arm_1k.png","md5":"fc86a554b4930ab596213d9485fc591a"},"exr":{"size":1213476,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_arm_1k.exr","md5":"e2e7bee9038059a3f089f99792ecbad6"}}},"AO":{"8k":{"jpg":{"size":44278667,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_ao_8k.jpg","md5":"b50cea2691e71df0109656ac340fcb28"},"png":{"size":109365411,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_ao_8k.png","md5":"e6e41aa08747b3183847a7b32fe58dd3"},"exr":{"size":41348855,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_ao_8k.exr","md5":"e66e4f3e1e0ee3ed71dd100ba556d307"}},"16k":{"jpg":{"size":140408600,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_ao_16k.jpg","md5":"53342e3792410319ffe397d0033b0040"},"png":{"size":362329621,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_ao_16k.png","md5":"6714b31c1334ddfe99d35f1fdd9051c3"},"exr":{"size":129924367,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_ao_16k.exr","md5":"e10d97b17a7ad4292c7d938712cf3933"}},"4k":{"jpg":{"size":11653676,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_ao_4k.jpg","md5":"9f051cc8a5745dbccc8a8e38777413fd"},"png":{"size":29454895,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_ao_4k.png","md5":"ff6d551f2d7aecd0e67eb1319040ab52"},"exr":{"size":10970112,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_ao_4k.exr","md5":"1956a4ce8c1d8b78664be50e5ea91068"}},"2k":{"jpg":{"size":2844498,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_ao_2k.jpg","md5":"b0b8c85b5b3ee4764723a660ceb6d811"},"png":{"size":7564184,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_ao_2k.png","md5":"0f84b2f9918cd63ab728e38802fcbee3"},"exr":{"size":2729852,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_ao_2k.exr","md5":"85b347b45da1754684af446148c40aea"}},"1k":{"jpg":{"size":674910,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_ao_1k.jpg","md5":"a87c963ee7255179cae3d6f8a7b55294"},"png":{"size":1874697,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_ao_1k.png","md5":"f39bc55f643b42d3df2c1c9e7a281c7e"},"exr":{"size":671681,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_ao_1k.exr","md5":"81884049e613e80ffa41ef4072f04a0d"}}},"Displacement":{"8k":{"jpg":{"size":12457366,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_disp_8k.jpg","md5":"c9889976027911224e78779129f8c23f"},"png":{"size":88096829,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_disp_8k.png","md5":"cda83fc359e02a3885f34d33490938f7"},"exr":{"size":10477548,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_disp_8k.exr","md5":"78713841de3fa9302d89a4c00dab7f6b"}},"16k":{"jpg":{"size":38436799,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_disp_16k.jpg","md5":"7de8b3f1711199b6f9959b5d34dfc142"},"png":{"size":237367010,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_disp_16k.png","md5":"9185ef66c96c134a5ac24af9879e42da"},"exr":{"size":27887634,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_disp_16k.exr","md5":"d6f5bd4ad5da7ae24c98eb84c924866e"}},"4k":{"jpg":{"size":4049586,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_disp_4k.jpg","md5":"3aff7396dec3009a51bf52cfa02ec7a6"},"png":{"size":24534598,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_disp_4k.png","md5":"b832de2f89d1e0644b6dd995811271e9"},"exr":{"size":3556458,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_disp_4k.exr","md5":"62dc502564a43fff5ec21dd23465c91e"}},"2k":{"jpg":{"size":1271708,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_disp_2k.jpg","md5":"e9bf6188a8bba36c1471277c79455173"},"png":{"size":6566084,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_disp_2k.png","md5":"d732d48d64c45a0041fc034edf5716d8"},"exr":{"size":1156221,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_disp_2k.exr","md5":"e28a8da1b04bded7f724af2417117468"}},"1k":{"jpg":{"size":389002,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_disp_1k.jpg","md5":"15cb73014427283cd86b99d1af068cc6"},"png":{"size":1691460,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_disp_1k.png","md5":"4d3f93b662970f42471ea50986c6f52d"},"exr":{"size":386861,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_disp_1k.exr","md5":"f32bfe2f8e91b4c6765150be14d73497"}}},"Rough":{"8k":{"jpg":{"size":28016011,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_rough_8k.jpg","md5":"b97c5cd3659976c252b7cbf0d979f322"},"png":{"size":100669227,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_rough_8k.png","md5":"ed6ae24c530e293f442e836bbcbe2d99"},"exr":{"size":25304479,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_rough_8k.exr","md5":"7fe7e628a014ff92fb9b26d0b4104ec8"}},"16k":{"jpg":{"size":86309560,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_rough_16k.jpg","md5":"3b2f44b05d7c74473508b36f946b0d35"},"png":{"size":324000667,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_rough_16k.png","md5":"9776a5f99954682eae761bb3bcacab54"},"exr":{"size":77965153,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_rough_16k.exr","md5":"366589786c5b93232f82056d655ef06c"}},"4k":{"jpg":{"size":7236955,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_rough_4k.jpg","md5":"e375a4da45a6def75e786be3caf8627f"},"png":{"size":26966298,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_rough_4k.png","md5":"d32f8f629da342f078febe4844801b11"},"exr":{"size":6705597,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_rough_4k.exr","md5":"15caf4f7750374fbf351780216523968"}},"2k":{"jpg":{"size":1760306,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_rough_2k.jpg","md5":"e190ee2e1ea9a07874092a49f0401111"},"png":{"size":6904588,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_rough_2k.png","md5":"647ff25dddcb8e245bd1551b00def314"},"exr":{"size":1639454,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_rough_2k.exr","md5":"2267550a6e854e671976bd3b395e33d3"}},"1k":{"jpg":{"size":433465,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_rough_1k.jpg","md5":"fc3b03d624d30064e5f78c82b5b6c0cc"},"png":{"size":1718853,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_rough_1k.png","md5":"82c8bff95ba48fa1e479ac822b2bcc4f"},"exr":{"size":415236,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_rough_1k.exr","md5":"21cf95c4267f53671e813bb08cd65032"}}},"mtlx":{"8k":{"mtlx":{"include":{"textures/brick_wall_09_disp_8k.png":{"size":88096829,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/8k/brick_wall_09/brick_wall_09_disp_8k.png","md5":"cda83fc359e02a3885f34d33490938f7"},"textures/brick_wall_09_nor_gl_8k.exr":{"size":143777324,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_nor_gl_8k.exr","md5":"36d30bbaf3f23500f69371b5e473ebe3"},"textures/brick_wall_09_rough_8k.exr":{"size":25304479,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/8k/brick_wall_09/brick_wall_09_rough_8k.exr","md5":"7fe7e628a014ff92fb9b26d0b4104ec8"},"textures/brick_wall_09_diff_8k.jpg":{"size":49298042,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_diff_8k.jpg","md5":"016759ec830a1bc05be68ff4bd2cb9fe"},"textures/brick_wall_09_ao_8k.jpg":{"size":44278667,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/8k/brick_wall_09/brick_wall_09_ao_8k.jpg","md5":"b50cea2691e71df0109656ac340fcb28"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/8k/brick_wall_09/brick_wall_09_8k.mtlx","md5":"cb5401e7c0803f9acec83c88ab07331f"}},"16k":{"mtlx":{"include":{"textures/brick_wall_09_ao_16k.jpg":{"size":140408600,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_ao_16k.jpg","md5":"53342e3792410319ffe397d0033b0040"},"textures/brick_wall_09_diff_16k.jpg":{"size":155495415,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/16k/brick_wall_09/brick_wall_09_diff_16k.jpg","md5":"9179a8fca35bba6f3ea7b6861f01d403"},"textures/brick_wall_09_rough_16k.exr":{"size":77965153,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_rough_16k.exr","md5":"366589786c5b93232f82056d655ef06c"},"textures/brick_wall_09_disp_16k.png":{"size":237367010,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/16k/brick_wall_09/brick_wall_09_disp_16k.png","md5":"9185ef66c96c134a5ac24af9879e42da"},"textures/brick_wall_09_nor_gl_16k.exr":{"size":448776519,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/16k/brick_wall_09/brick_wall_09_nor_gl_16k.exr","md5":"11f8235722f7caaebff806ae264c18d3"}},"size":4720,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/16k/brick_wall_09/brick_wall_09_16k.mtlx","md5":"98caa8dcb17cfaa6cd1d1f232420bae5"}},"4k":{"mtlx":{"include":{"textures/brick_wall_09_rough_4k.exr":{"size":6705597,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_rough_4k.exr","md5":"15caf4f7750374fbf351780216523968"},"textures/brick_wall_09_diff_4k.jpg":{"size":12691833,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_diff_4k.jpg","md5":"5b57d70ff2e76ae09b083054f118282f"},"textures/brick_wall_09_nor_gl_4k.exr":{"size":37937299,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/4k/brick_wall_09/brick_wall_09_nor_gl_4k.exr","md5":"02bc11ba5ff6fbceabf9138c98655d82"},"textures/brick_wall_09_disp_4k.png":{"size":24534598,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/4k/brick_wall_09/brick_wall_09_disp_4k.png","md5":"b832de2f89d1e0644b6dd995811271e9"},"textures/brick_wall_09_ao_4k.jpg":{"size":11653676,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/4k/brick_wall_09/brick_wall_09_ao_4k.jpg","md5":"9f051cc8a5745dbccc8a8e38777413fd"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/4k/brick_wall_09/brick_wall_09_4k.mtlx","md5":"efe69e584f6c4d59d5f73edc3f697447"}},"2k":{"mtlx":{"include":{"textures/brick_wall_09_nor_gl_2k.exr":{"size":9264325,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_nor_gl_2k.exr","md5":"3e3a1bf42a79463783ee1871d45ce03e"},"textures/brick_wall_09_diff_2k.jpg":{"size":3116669,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_diff_2k.jpg","md5":"f675297a70b77f881feb18c012b53b82"},"textures/brick_wall_09_disp_2k.png":{"size":6566084,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_disp_2k.png","md5":"d732d48d64c45a0041fc034edf5716d8"},"textures/brick_wall_09_ao_2k.jpg":{"size":2844498,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_ao_2k.jpg","md5":"b0b8c85b5b3ee4764723a660ceb6d811"},"textures/brick_wall_09_rough_2k.exr":{"size":1639454,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/2k/brick_wall_09/brick_wall_09_rough_2k.exr","md5":"2267550a6e854e671976bd3b395e33d3"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/2k/brick_wall_09/brick_wall_09_2k.mtlx","md5":"a70e10caeea559ee981bb4ca8825caeb"}},"1k":{"mtlx":{"include":{"textures/brick_wall_09_ao_1k.jpg":{"size":674910,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_ao_1k.jpg","md5":"a87c963ee7255179cae3d6f8a7b55294"},"textures/brick_wall_09_rough_1k.exr":{"size":415236,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_rough_1k.exr","md5":"21cf95c4267f53671e813bb08cd65032"},"textures/brick_wall_09_nor_gl_1k.exr":{"size":2221558,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/exr/1k/brick_wall_09/brick_wall_09_nor_gl_1k.exr","md5":"c988ebdc35bfc02f9c8052bd4aac0706"},"textures/brick_wall_09_diff_1k.jpg":{"size":777371,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/jpg/1k/brick_wall_09/brick_wall_09_diff_1k.jpg","md5":"d08a1a4be611135d174f051189e00156"},"textures/brick_wall_09_disp_1k.png":{"size":1691460,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/brick_wall_09/brick_wall_09_disp_1k.png","md5":"4d3f93b662970f42471ea50986c6f52d"}},"size":4716,"url":"https://dl.polyhaven.org/file/ph-assets/Textures/mtlx/1k/brick_wall_09/brick_wall_09_1k.mtlx","md5":"f996aab194bf02bc3950d39578da16cb"}}}} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/materials/manifest.json b/astra_scene_test/Scene_04/assets/materials/manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..c44a7146fcd43ee6ae6d3693c92eeb0e4f5e64fc --- /dev/null +++ b/astra_scene_test/Scene_04/assets/materials/manifest.json @@ -0,0 +1,62 @@ +{ + "downloaded_utc": "2026-09-11T15:24:30.578061+00:00", + "assets": [ + { + "asset": "asphalt_track", + "source": "https://polyhaven.com/a/asphalt_track", + "license": "CC0", + "author": "Dimitrios Savva", + "physical_width_m": 2.0, + "maps": { + "diff": { + "path": "assets/materials/asphalt_track_diff_2k.jpg", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_diff_2k.jpg", + "bytes": 3779263, + "sha256": "742d1d79560bd661d5871afc857e981e25dcd563bfd24385cbd063ebcaeef06c" + }, + "rough": { + "path": "assets/materials/asphalt_track_rough_2k.jpg", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/asphalt_track/asphalt_track_rough_2k.jpg", + "bytes": 2182047, + "sha256": "b934fb6fbe05770df95fa94fee3a12d45eb121621ce96e9b262f403d49ee493e" + }, + "disp": { + "path": "assets/materials/asphalt_track_disp_2k.png", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/asphalt_track/asphalt_track_disp_2k.png", + "bytes": 6257146, + "sha256": "3a8fff49c110c612bfd640d7d5531d7b197967adc76a6bbd548c671eab583b43" + } + } + }, + { + "asset": "brick_wall_09", + "source": "https://polyhaven.com/a/brick_wall_09", + "license": "CC0", + "authors": [ + "Matterfield", + "Rob Tuytel" + ], + "physical_width_m": 2.0, + "maps": { + "diff": { + "path": "assets/materials/brick_wall_09_diff_2k.jpg", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_diff_2k.jpg", + "bytes": 3116669, + "sha256": "a8d716e62626f1fecdeab31ab7a8fc33f81a4f9f4dc3f23be35171b080db47a9" + }, + "rough": { + "path": "assets/materials/brick_wall_09_rough_2k.jpg", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/jpg/2k/brick_wall_09/brick_wall_09_rough_2k.jpg", + "bytes": 1760306, + "sha256": "fe20265ceafd3b0b93474c9c66efad855bdc029ea2f3c4763fb6315e9670401e" + }, + "disp": { + "path": "assets/materials/brick_wall_09_disp_2k.png", + "url": "https://dl.polyhaven.org/file/ph-assets/Textures/png/2k/brick_wall_09/brick_wall_09_disp_2k.png", + "bytes": 6566084, + "sha256": "a14478ba847341cdb335d0cb974d90fe6184c477cc3809b38959c22a9760e5dd" + } + } + } + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/near_vehicle_landmarks.json b/astra_scene_test/Scene_04/assets/near_vehicle_landmarks.json new file mode 100644 index 0000000000000000000000000000000000000000..4bb54bbaf1e337c7ecab57a06de9d3a65c7f7adc --- /dev/null +++ b/astra_scene_test/Scene_04/assets/near_vehicle_landmarks.json @@ -0,0 +1,244 @@ +{ + "Left black SUV": { + "parameters": { + "x": -15.40583402772641, + "y": 17.689026458088, + "scale_x": 1.3658126531989794, + "scale_y": 0.7254529400923296, + "scale_z": 1.439240796839573, + "yaw": -0.5999999999997535, + "pitch": 0.004614710917425294, + "z": -0.29999999999999993 + }, + "observations": { + "front_wheel": { + "1": [ + 242, + 583 + ], + "17": [ + 114, + 599 + ] + }, + "rear_wheel": { + "1": [ + 309, + 570 + ], + "17": [ + 217, + 580 + ], + "33": [ + 69, + 602 + ] + }, + "roof_front": { + "1": [ + 245, + 522 + ], + "17": [ + 147, + 522 + ] + }, + "roof_rear": { + "1": [ + 306, + 516 + ], + "17": [ + 206, + 517 + ], + "33": [ + 65, + 521 + ] + } + }, + "predictions": { + "front_wheel": { + "1": [ + 241.21008186006156, + 577.0273372129063 + ], + "17": [ + 113.21239688890364, + 594.5420671918232 + ] + }, + "rear_wheel": { + "1": [ + 308.9436012495546, + 569.5166040298791 + ], + "17": [ + 218.2540810958356, + 582.7693887710858 + ], + "33": [ + 72.23341064669683, + 602.9402471122226 + ] + }, + "roof_front": { + "1": [ + 259.88152816959655, + 521.5823994346161 + ], + "17": [ + 147.27626306741416, + 520.9057516365284 + ] + }, + "roof_rear": { + "1": [ + 298.9265786048912, + 520.7329672724297 + ], + "17": [ + 206.74526176687374, + 520.0342978496262 + ], + "33": [ + 55.27533299739173, + 518.9665957301395 + ] + } + }, + "cost": 116.68450426911777 + }, + "Left red sedan": { + "parameters": { + "x": -11.836573935579574, + "y": 18.677237230964867, + "scale_x": 1.0906709901455305, + "scale_y": 0.6000000000000029, + "scale_z": 1.430791188066538, + "yaw": -0.06607431915086959, + "pitch": 0.036772614223542034, + "z": 0.02259027956653785 + }, + "observations": { + "front_wheel": { + "1": [ + 377, + 559 + ], + "17": [ + 289, + 580 + ], + "33": [ + 188, + 591 + ] + }, + "rear_wheel": { + "1": [ + 410, + 553 + ], + "17": [ + 334, + 565 + ], + "33": [ + 257, + 576 + ] + }, + "roof_front": { + "1": [ + 376, + 526 + ], + "17": [ + 292, + 529 + ], + "33": [ + 203, + 534 + ] + }, + "roof_rear": { + "1": [ + 404, + 523 + ], + "17": [ + 323, + 528 + ], + "33": [ + 238, + 530 + ] + } + }, + "predictions": { + "front_wheel": { + "1": [ + 374.5970112404388, + 562.6878244172954 + ], + "17": [ + 302.7728065982664, + 574.5197556815417 + ], + "33": [ + 187.37893660001112, + 590.7382521453769 + ] + }, + "rear_wheel": { + "1": [ + 399.6812566147236, + 556.4634955318185 + ], + "17": [ + 343.06745675780485, + 565.3958202434629 + ], + "33": [ + 256.6724762887043, + 575.4642022259088 + ] + }, + "roof_front": { + "1": [ + 375.2780576334563, + 528.4591413198377 + ], + "17": [ + 306.76309104242733, + 530.4664141911271 + ], + "33": [ + 196.61178181897685, + 531.6386497061818 + ] + }, + "roof_rear": { + "1": [ + 390.4085094450751, + 526.6535331154715 + ], + "17": [ + 330.9054705719508, + 528.0928437216932 + ], + "33": [ + 238.04067613093866, + 527.8063974679002 + ] + } + }, + "cost": 211.068758553369 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/opening_hatch.json b/astra_scene_test/Scene_04/assets/opening_hatch.json new file mode 100644 index 0000000000000000000000000000000000000000..1f11cc9c2551b067e0382fd7f87095a7ff35117e --- /dev/null +++ b/astra_scene_test/Scene_04/assets/opening_hatch.json @@ -0,0 +1,151 @@ +{ + "parameters": { + "x": -15.934376297294063, + "y": 11.605644514590427, + "scale_x": 1.9999999979566179, + "scale_y": 0.6000000000000002, + "scale_z": 1.2282623300122342, + "yaw": -0.3094418565154664 + }, + "tire_contact_z": -0.08106531378080746, + "local_features": { + "rear_wheel": [ + 0.98, + 1.44, + 0.44 + ], + "roof_rear": [ + 0.684, + 1.1, + 1.7 + ], + "rear_bumper": [ + 0.86, + 2.15, + 0.65 + ], + "rear_glass_corner": [ + 0.8, + 1.87, + 1.3 + ] + }, + "observations": { + "rear_wheel": { + "1": [ + 124, + 603 + ], + "5": [ + 89, + 610 + ], + "9": [ + 53, + 616 + ] + }, + "roof_rear": { + "1": [ + 104, + 542 + ], + "5": [ + 69, + 543 + ], + "9": [ + 33, + 545 + ] + }, + "rear_bumper": { + "1": [ + 145, + 601 + ], + "5": [ + 109, + 607 + ], + "9": [ + 75, + 615 + ] + }, + "rear_glass_corner": { + "1": [ + 131, + 560 + ], + "5": [ + 97, + 563 + ], + "9": [ + 63, + 568 + ] + } + }, + "predictions": { + "rear_wheel": { + "1": [ + 134.21909353560892, + 597.9114192483208 + ], + "5": [ + 88.2126585804101, + 606.2356274553185 + ], + "9": [ + 33.65838228042708, + 612.9594420173175 + ] + }, + "roof_rear": { + "1": [ + 116.09956002688716, + 543.7574456528481 + ], + "5": [ + 68.816334495599, + 546.9990447637751 + ], + "9": [ + 12.80371379514338, + 547.756196303031 + ] + }, + "rear_bumper": { + "1": [ + 152.18607656005366, + 585.4669628202355 + ], + "5": [ + 110.0647003461919, + 592.2843814120494 + ], + "9": [ + 60.50467960681465, + 597.2806707476982 + ] + }, + "rear_glass_corner": { + "1": [ + 143.94793731938984, + 559.1701077202015 + ], + "5": [ + 100.79437614976837, + 563.6757271898119 + ], + "9": [ + 49.96050836572158, + 565.9900374061785 + ] + } + }, + "cost": 423.88667883386915, + "note": "Fit the cropped opening hatchback to three measured views, camera fixed.\n\nThe rear wheel is partly obscured by wet glass. These manually placed features\nare approximate, and are retained with their residuals for review.\n" +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/right_warm_windows.json b/astra_scene_test/Scene_04/assets/right_warm_windows.json new file mode 100644 index 0000000000000000000000000000000000000000..a492c642b020dde52293bdd0a009e936cc759682 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/right_warm_windows.json @@ -0,0 +1,72 @@ +{ + "upper_warm_left": { + "xyz": [ + 27.124731577334103, + 29.26344576828372, + 32.061021796888525 + ], + "observations": { + "1": [ + 914, + 184 + ], + "17": [ + 967, + 146 + ], + "33": [ + 1006, + 80 + ] + }, + "predictions": { + "1": [ + 918.6034149519265, + 186.6255490338956 + ], + "17": [ + 962.9376996537841, + 143.73006470629207 + ], + "33": [ + 1006.1911962764011, + 80.05577861788169 + ] + } + }, + "upper_warm_right": { + "xyz": [ + 28.114905993348337, + 28.180852673032906, + 32.542485517819685 + ], + "observations": { + "1": [ + 940, + 172 + ], + "17": [ + 989, + 126 + ], + "33": [ + 1035, + 54 + ] + }, + "predictions": { + "1": [ + 939.0877669497763, + 171.37658672670756 + ], + "17": [ + 987.9092830981837, + 124.75433140280029 + ], + "33": [ + 1036.8208673440329, + 55.6607459327185 + ] + } + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/signal_emitters.json b/astra_scene_test/Scene_04/assets/signal_emitters.json new file mode 100644 index 0000000000000000000000000000000000000000..dd8d87146aa140cb4597d11793ffa9c1feaf3fe1 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/signal_emitters.json @@ -0,0 +1,1058 @@ +{ + "left_near_signal": { + "emitter_xyz": [ + -8.147869306397915, + 36.2293467675215, + 6.692715479200735 + ], + "housing_anchor_xyz": [ + -8.147869306397915, + 36.419346767521496, + 6.692715479200735 + ], + "median_error_pixels": 1.660346582904201, + "p90_error_pixels": 2.5813106684819296, + "observations": { + "1": [ + 530.8274643912322, + 474.50536228809517 + ], + "2": [ + 530.5244857681793, + 474.1591234887042 + ], + "3": [ + 530.219084064053, + 473.94966409587687 + ], + "4": [ + 529.9713402584281, + 473.7927926252329 + ], + "5": [ + 529.3303969609101, + 473.570336829295 + ], + "6": [ + 528.7716424800882, + 472.5985089758267 + ], + "7": [ + 528.5914639908933, + 472.0268717186786 + ], + "8": [ + 527.8804917105366, + 471.55207372624034 + ], + "9": [ + 527.4075111722369, + 471.04445124583805 + ], + "10": [ + 526.6792329286989, + 470.81013299176726 + ], + "11": [ + 526.2004045617715, + 470.458491818242 + ], + "12": [ + 525.4551184575015, + 470.1013633809708 + ], + "13": [ + 524.8790153319486, + 469.8060756876257 + ], + "14": [ + 524.1525622470226, + 469.76809455072276 + ], + "15": [ + 523.8096963572335, + 469.5036953873244 + ], + "16": [ + 523.3038799596918, + 469.4982070379594 + ], + "17": [ + 522.7461435363443, + 469.50030274154517 + ], + "18": [ + 522.17562006899, + 469.3960117210161 + ], + "19": [ + 521.4859533953672, + 469.11429275748606 + ], + "20": [ + 520.4864569282028, + 468.51732418022056 + ], + "21": [ + 519.7849379413818, + 467.77367926241186 + ], + "22": [ + 518.6707567368431, + 466.9392187452801 + ], + "23": [ + 517.6760813951738, + 466.1878646372145 + ], + "24": [ + 516.7382913190811, + 465.11567515845155 + ], + "25": [ + 515.7604650747422, + 464.18746533592065 + ], + "26": [ + 514.5313303541401, + 463.02120337835214 + ], + "27": [ + 512.9936232955174, + 461.82949689701314 + ], + "28": [ + 511.7850046728006, + 460.85714807219983 + ], + "29": [ + 510.49295649599344, + 459.7578654173329 + ], + "30": [ + 509.2951786372752, + 458.6226336185982 + ], + "31": [ + 508.28061161641244, + 457.6213720634277 + ], + "32": [ + 507.03883930065876, + 457.03321567438525 + ], + "33": [ + 505.7775310668184, + 456.5438171787632 + ], + "34": [ + 504.4815550647073, + 456.28012748557325 + ], + "35": [ + 503.5315671759844, + 456.0301614091996 + ], + "36": [ + 502.16510873701674, + 455.9424072760123 + ], + "37": [ + 501.22565715726404, + 455.6817987410751 + ], + "38": [ + 500.0629145496162, + 455.4448594226584 + ], + "39": [ + 499.21082063834524, + 455.16429727111495 + ], + "40": [ + 497.9467468717775, + 455.02328309510835 + ], + "41": [ + 497.0702211747837, + 454.91754681721613 + ], + "42": [ + 496.08541936347814, + 454.93877060613573 + ], + "43": [ + 494.83025605732297, + 454.7105381210494 + ], + "44": [ + 493.5770079125901, + 454.28612884944766 + ], + "45": [ + 492.32805180682135, + 453.6490794844575 + ], + "46": [ + 491.10359710661083, + 453.1438548331015 + ], + "47": [ + 489.98602504320326, + 451.92606342787377 + ], + "48": [ + 488.58448286325296, + 450.6443898978761 + ], + "49": [ + 487.5620242801058, + 449.41114889967537 + ], + "50": [ + 486.3496178345775, + 448.4253221696501 + ], + "51": [ + 485.0915395991696, + 447.2052208912188 + ], + "52": [ + 483.878589353552, + 446.2442784776884 + ], + "53": [ + 482.32935658185374, + 446.27249501771087 + ], + "54": [ + 480.88557379800295, + 446.0355629589287 + ], + "55": [ + 479.46577329174545, + 446.02694885478974 + ], + "56": [ + 477.8755470352215, + 446.0223305563235 + ], + "57": [ + 476.6176345704041, + 446.0197448651874 + ], + "58": [ + 475.24612082107615, + 446.15112920238994 + ], + "59": [ + 473.9064954748311, + 446.22130739786934 + ], + "60": [ + 472.3509836708225, + 446.1293532728193 + ], + "61": [ + 470.9156411669908, + 446.20002730724434 + ], + "62": [ + 469.5193054066985, + 445.6917680654495 + ], + "63": [ + 467.84381504980115, + 445.0360390752064 + ], + "64": [ + 466.3487262992165, + 444.3931620752099 + ], + "65": [ + 464.77210946445894, + 443.5414443555413 + ], + "66": [ + 462.9746935379626, + 442.58125871370265 + ], + "67": [ + 461.2938647688374, + 441.41326073391843 + ], + "68": [ + 459.63322571193055, + 440.49266983702756 + ], + "69": [ + 457.7655534920947, + 439.4314662764563 + ], + "70": [ + 456.07755131752424, + 438.0256136443234 + ], + "71": [ + 453.725664861758, + 436.9761798491169 + ], + "72": [ + 451.6580622824271, + 435.9762142912473 + ], + "73": [ + 449.4785235012257, + 435.1575426685473 + ], + "74": [ + 447.4082128573904, + 434.36775177335 + ], + "75": [ + 445.0586749051566, + 433.59080085422073 + ], + "76": [ + 442.65295669307125, + 432.9326282186534 + ], + "77": [ + 440.44725446121004, + 432.4039529876186 + ], + "78": [ + 438.11860240380867, + 431.58919237808516 + ], + "79": [ + 435.7264857452912, + 430.82134829185696 + ], + "80": [ + 433.340169090518, + 430.06002106901536 + ], + "81": [ + 430.53398041773625, + 429.17289305893706 + ], + "82": [ + 427.7090481878058, + 428.06656071282174 + ], + "83": [ + 425.40267662398355, + 426.9641854682367 + ], + "84": [ + 422.5182008445275, + 425.8505860358808 + ], + "85": [ + 420.0825428684464, + 424.4462877929625 + ], + "86": [ + 417.42828076284445, + 423.1430069322171 + ], + "87": [ + 414.54970761580387, + 421.47925570185896 + ], + "88": [ + 411.3492325782604, + 420.0190395897864 + ], + "89": [ + 408.61792615198505, + 418.30625375336183 + ], + "90": [ + 405.51797197068913, + 416.55405945181195 + ], + "91": [ + 402.82264778879824, + 414.5464971665908 + ], + "92": [ + 398.8068922557439, + 412.4766292070686 + ], + "93": [ + 395.70382645132275, + 410.3623737935808 + ], + "94": [ + 392.47250765611517, + 408.12339947822227 + ], + "95": [ + 389.3843045245399, + 406.0061343005655 + ], + "96": [ + 385.6312903231487, + 404.65124713497335 + ], + "97": [ + 382.0496694128485, + 402.77606119258485 + ], + "98": [ + 378.39429310569227, + 401.1329659239203 + ], + "99": [ + 374.40843743494423, + 399.5829263835591 + ], + "100": [ + 370.42343029489984, + 398.1209723139756 + ], + "101": [ + 366.2344150692428, + 396.7124922285568 + ], + "102": [ + 361.69563971955375, + 395.1471110088826 + ], + "103": [ + 357.4674994750693, + 393.5168763192105 + ], + "104": [ + 353.0573021990487, + 391.51306515937716 + ], + "105": [ + 348.2681298086685, + 389.5859168275093 + ], + "106": [ + 343.50872607859424, + 387.70248329406826 + ], + "107": [ + 338.0795484918839, + 385.2113486863693 + ], + "108": [ + 332.86798785578026, + 382.82364632956296 + ], + "109": [ + 327.31081513831066, + 380.312703419178 + ], + "110": [ + 321.20245910768864, + 377.7912142397787 + ], + "111": [ + 315.2862513746154, + 375.0578027679985 + ], + "112": [ + 309.1869290696538, + 372.55984901773644 + ], + "113": [ + 303.0284726044948, + 369.76249826438607 + ], + "114": [ + 296.44760374275285, + 367.0043816972769 + ], + "115": [ + 289.7451759138377, + 364.2990445780973 + ], + "116": [ + 282.62644809614085, + 361.7078951599764 + ], + "117": [ + 275.36136232139273, + 359.1977092245822 + ], + "118": [ + 267.7098131368736, + 356.35210304250927 + ], + "119": [ + 259.86133586270194, + 353.49979083129773 + ], + "120": [ + 251.74378830178054, + 350.6241591842861 + ], + "121": [ + 243.49235138312363, + 347.50437104409974 + ], + "122": [ + 234.83339121829513, + 344.1076324309318 + ], + "123": [ + 226.22437437588658, + 340.39906349204443 + ], + "124": [ + 217.06327121736237, + 336.4591531399487 + ], + "125": [ + 207.97319702624858, + 332.71444143686256 + ], + "126": [ + 198.16763178490984, + 328.511435857386 + ], + "127": [ + 188.03018679135266, + 325.15225677053144 + ], + "128": [ + 177.8868142744232, + 320.78397563797586 + ] + } + }, + "right_far_signal": { + "emitter_xyz": [ + 8.85874228424147, + 40.75779045404995, + 7.104764052100745 + ], + "housing_anchor_xyz": [ + 8.85874228424147, + 40.94779045404995, + 7.104764052100745 + ], + "median_error_pixels": 1.526466211775741, + "p90_error_pixels": 3.260012789705568, + "observations": { + "1": [ + 696.6114038794435, + 474.7870197078353 + ], + "2": [ + 697.2743251574282, + 474.2548391055962 + ], + "3": [ + 698.3742771758091, + 473.76380876976737 + ], + "4": [ + 699.2751914042923, + 473.5446523064039 + ], + "5": [ + 700.6250958814672, + 472.78462962608154 + ], + "6": [ + 700.8392771284119, + 472.227574203598 + ], + "7": [ + 702.0933445136086, + 471.0208630974202 + ], + "8": [ + 702.894841088713, + 469.86868961655534 + ], + "9": [ + 703.8474118216516, + 468.76067559745854 + ], + "10": [ + 703.6544368556292, + 470.1109761898246 + ], + "11": [ + 704.0589548299523, + 469.70037241903435 + ], + "12": [ + 703.7587982676824, + 470.46613470833375 + ], + "13": [ + 703.561034993854, + 471.0972334823426 + ], + "14": [ + 704.3043772168215, + 471.1352059664128 + ], + "15": [ + 705.2301786061817, + 470.90431219530404 + ], + "16": [ + 706.9819159616712, + 469.744123745171 + ], + "17": [ + 707.4539420382821, + 469.4936586363414 + ], + "18": [ + 707.7494201869096, + 470.0050261074085 + ], + "19": [ + 708.771935499065, + 468.6489559822967 + ], + "20": [ + 708.3158901497764, + 469.24262031467464 + ], + "21": [ + 708.3869746560046, + 469.035976683763 + ], + "22": [ + 707.6259096065401, + 470.20119092206886 + ], + "23": [ + 709.4410794447203, + 467.21767983722714 + ], + "24": [ + 709.0129327503406, + 467.50913858197487 + ], + "25": [ + 708.9809655168734, + 466.80726985490105 + ], + "26": [ + 709.4916002165116, + 465.14151327699017 + ], + "27": [ + 708.3493842006634, + 465.04517727713767 + ], + "28": [ + 708.5346088498272, + 463.01958227910376 + ], + "29": [ + 708.3904499619515, + 462.15856934850405 + ], + "30": [ + 709.202349351871, + 461.0147384342765 + ], + "31": [ + 711.054715825263, + 457.8102578943004 + ], + "32": [ + 710.9209886491392, + 458.16836290787245 + ], + "33": [ + 711.0631672780644, + 457.1560938569029 + ], + "34": [ + 711.7854509081621, + 456.307739237 + ], + "35": [ + 710.4182637041523, + 457.8612535940404 + ], + "36": [ + 710.6208510701682, + 457.4695893067918 + ], + "37": [ + 712.051961368402, + 456.88081973319277 + ], + "38": [ + 712.6912284091536, + 456.2642972566045 + ], + "39": [ + 712.4041165064048, + 457.5740427210542 + ], + "40": [ + 712.7500089258612, + 457.65138216401107 + ], + "41": [ + 713.3890169152669, + 457.8926266651114 + ], + "42": [ + 713.8630208504226, + 457.4365368847405 + ], + "43": [ + 714.4724195682245, + 457.5298925420514 + ], + "44": [ + 714.7348338827144, + 457.40292221872033 + ], + "45": [ + 715.4376772239192, + 456.8044414522113 + ], + "46": [ + 715.6031043080519, + 456.13738866786684 + ], + "47": [ + 716.3656881268463, + 455.08600955593727 + ], + "48": [ + 716.991952010743, + 454.5664957792037 + ], + "49": [ + 717.646381335936, + 454.1673586380901 + ], + "50": [ + 718.3534902807859, + 453.2835220057346 + ], + "51": [ + 719.0969050802481, + 452.4877274057314 + ], + "52": [ + 719.2675689526236, + 451.72994584735784 + ], + "53": [ + 719.8430768332184, + 451.60614994344166 + ], + "54": [ + 720.2779915436101, + 451.5278320108679 + ], + "55": [ + 720.7317148475049, + 451.4911496030879 + ], + "56": [ + 721.2781846829711, + 451.76071047742744 + ], + "57": [ + 721.6898738491989, + 452.14527612032106 + ], + "58": [ + 722.4954051282529, + 452.30323614547575 + ], + "59": [ + 722.9848450808323, + 453.0513253888024 + ], + "60": [ + 723.9151448166137, + 453.2071245964264 + ], + "61": [ + 724.7322890016229, + 453.2988379602866 + ], + "62": [ + 725.3285314601719, + 453.12642889520026 + ], + "63": [ + 725.9275321466084, + 453.0210474858106 + ], + "64": [ + 726.6755508893219, + 452.67445152048106 + ], + "65": [ + 727.6999736830772, + 452.29696014879016 + ], + "66": [ + 728.5762863540634, + 451.4052382867028 + ], + "67": [ + 729.4132878435198, + 450.7637646724363 + ], + "68": [ + 730.152138889176, + 450.12838455629264 + ], + "69": [ + 731.0221762077393, + 449.3811127516026 + ], + "70": [ + 731.8116940433129, + 448.83045666351444 + ], + "71": [ + 732.5619106845392, + 447.99842633191884 + ], + "72": [ + 733.1286558344204, + 447.1828785349978 + ], + "73": [ + 733.8286405734029, + 446.74085975523184 + ], + "74": [ + 734.5451020607171, + 445.75835970300267 + ], + "75": [ + 735.4525141872982, + 445.21365057559063 + ], + "76": [ + 736.133054735223, + 444.78241723907627 + ], + "77": [ + 736.7239249339359, + 444.35918204280426 + ], + "78": [ + 737.6046497843575, + 443.6395852268689 + ], + "79": [ + 738.4379381979555, + 443.18423668604134 + ], + "80": [ + 739.277218715672, + 442.6610789280367 + ], + "81": [ + 739.975941470702, + 441.74584178054624 + ], + "82": [ + 740.8047468952753, + 440.82320154719906 + ], + "83": [ + 741.8349411170525, + 439.8566037444804 + ], + "84": [ + 742.7695157786538, + 438.51469486435104 + ], + "85": [ + 743.7066170366435, + 437.4989589136406 + ], + "86": [ + 744.8104839611929, + 436.2801866048441 + ], + "87": [ + 746.0347745064572, + 435.0584938984965 + ], + "88": [ + 747.2028441739286, + 433.8354240728685 + ], + "89": [ + 748.4028742417985, + 432.3893139000828 + ], + "90": [ + 749.8918018438154, + 431.0897640376876 + ], + "91": [ + 751.197661364478, + 429.5710322354156 + ], + "92": [ + 752.683231169378, + 428.0243277353849 + ], + "93": [ + 754.2065918247346, + 426.78541635443855 + ], + "94": [ + 755.4659316707907, + 425.56788455498815 + ], + "95": [ + 757.0610478539309, + 424.1438429466543 + ], + "96": [ + 758.4809601691386, + 423.4799106212305 + ], + "97": [ + 759.9736566608785, + 422.6928525354665 + ], + "98": [ + 761.5540926454934, + 421.673584206389 + ], + "99": [ + 762.660388382358, + 421.0786745143708 + ], + "100": [ + 764.2304735925098, + 420.3262244155639 + ], + "101": [ + 765.7602340856918, + 419.778498169183 + ], + "102": [ + 767.1296110587597, + 419.12110368085536 + ], + "103": [ + 768.6651422177483, + 418.8094413876304 + ], + "104": [ + 770.788357650283, + 417.93711878979326 + ], + "105": [ + 771.989921736111, + 416.9849249702675 + ], + "106": [ + 773.5257551390818, + 415.96498353664884 + ], + "107": [ + 775.4886422232448, + 414.83599084641094 + ], + "108": [ + 777.269660548192, + 413.6460611332025 + ], + "109": [ + 778.3662108392956, + 412.2132679829246 + ], + "110": [ + 779.8691733598178, + 411.1174853518323 + ], + "111": [ + 781.6120161643012, + 409.52366369158455 + ], + "112": [ + 783.3935176287337, + 408.38499952797525 + ], + "113": [ + 785.1344040427056, + 407.1373252814078 + ], + "114": [ + 787.2579312435365, + 405.95812687596504 + ], + "115": [ + 789.2818919977028, + 405.0785809971002 + ], + "116": [ + 790.7343599668972, + 403.842155430863 + ], + "117": [ + 792.4274916274413, + 402.7655731704221 + ], + "118": [ + 794.2654790236896, + 401.57046321718303 + ], + "119": [ + 796.2230812062309, + 400.253622780149 + ], + "120": [ + 798.3028774507285, + 399.0186442490307 + ], + "121": [ + 800.1960158502851, + 397.31577734473944 + ], + "122": [ + 802.7977141522337, + 395.8810591801129 + ], + "123": [ + 805.2075067705938, + 394.2486364917493 + ], + "124": [ + 807.7271711962715, + 392.57196488797024 + ], + "125": [ + 809.8854777532586, + 390.59742865944946 + ], + "126": [ + 812.9027294777102, + 388.3946356118431 + ], + "127": [ + 814.8142473496994, + 386.57707710314025 + ], + "128": [ + 817.411827949075, + 384.31368288721086 + ] + } + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/signal_response.json b/astra_scene_test/Scene_04/assets/signal_response.json new file mode 100644 index 0000000000000000000000000000000000000000..2ecf340bff8ca23e47c57b1d2512b4605ad15b35 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/signal_response.json @@ -0,0 +1,5 @@ +{ + "note": "Inferred smooth angular LED response, adjusted from three timestamp comparisons. Not a uniquely recovered physical photometric distribution. No frame-dependent emission keys.", + "left_near_signal": {"floor": 8.0, "peak": 115.0, "sharpness": 47.0, "color": [0.002, 0.75, 0.65], "local_glow_watts": 5.0}, + "right_far_signal": {"floor": 28.0, "peak": 1460.0, "sharpness": 54.0, "color": [0.001, 0.75, 0.65], "local_glow_watts": 35.0} +} diff --git a/astra_scene_test/Scene_04/assets/signs_fit.json b/astra_scene_test/Scene_04/assets/signs_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..2c9a533ccdd1e83fa1ab50a0a736ef66b2f028cf --- /dev/null +++ b/astra_scene_test/Scene_04/assets/signs_fit.json @@ -0,0 +1,178 @@ +{ + "park": { + "x": 17.97, + "y": 12.30440409848917, + "z": 10.20496951784451, + "width": 1.8637724437563292, + "height": 2.7423621090751316, + "target_corners": [ + [ + 1056, + 298 + ], + [ + 1118, + 270 + ], + [ + 1130, + 340 + ], + [ + 1067, + 367 + ] + ], + "projected_corners": [ + [ + 1058.0138166606894, + 298.6905590170437 + ], + [ + 1120.12147509248, + 268.97760183205844 + ], + [ + 1128.4737856625475, + 343.05850699690717 + ], + [ + 1064.3776339154674, + 363.97317878054287 + ] + ], + "rms_pixels": 2.1737217425004216 + }, + "park_vertical": { + "x": 15.5, + "y": 13.654836056550943, + "z": 8.902742991524129, + "width": 0.5390861575978192, + "height": 2.8088626084201844, + "target_corners": [ + [ + 981, + 334 + ], + [ + 996, + 330 + ], + [ + 1010, + 394 + ], + [ + 998, + 402 + ] + ], + "projected_corners": [ + [ + 986.8901098531089, + 334.7603957723329 + ], + [ + 1000.2213437928311, + 328.45588259798745 + ], + [ + 1005.8865225935181, + 396.36355414891807 + ], + [ + 992.153877717201, + 400.3769036026191 + ] + ], + "rms_pixels": 3.7880455057634856 + }, + "training": { + "x": -28.25, + "y": 31.04441181217815, + "z": 6.433481838188228, + "width": 6.7329389381709905, + "height": 2.2047989749452777, + "target_corners": [ + [ + 214, + 442 + ], + [ + 294, + 463 + ], + [ + 294, + 492 + ], + [ + 211, + 470 + ] + ], + "projected_corners": [ + [ + 213.44410938271142, + 445.43250293445715 + ], + [ + 295.66293659263334, + 458.7588629069659 + ], + [ + 293.74016371899774, + 483.5976755095154 + ], + [ + 210.4450058255659, + 476.61415242486544 + ] + ], + "rms_pixels": 4.294841313945821 + }, + "hair": { + "x": -27.1, + "y": 36.61507903616481, + "z": 5.503453557874982, + "width": 5.473925911370885, + "height": 1.9969720514675149, + "target_corners": [ + [ + 303, + 466 + ], + [ + 347, + 480 + ], + [ + 348, + 501 + ], + [ + 303, + 488 + ] + ], + "projected_corners": [ + [ + 303.4868841490716, + 469.80669883430386 + ], + [ + 348.58723725128436, + 475.7016714819627 + ], + [ + 347.3070626047482, + 495.3171229544106 + ], + [ + 301.75720881937445, + 492.7273266859726 + ] + ], + "rms_pixels": 3.39835187249128 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/tower_fit.json b/astra_scene_test/Scene_04/assets/tower_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..c53b71f181db9440d0e01eeea897833ad5f7c7af --- /dev/null +++ b/astra_scene_test/Scene_04/assets/tower_fit.json @@ -0,0 +1,35 @@ +{ + "left": [ + -73.8962261694622, + 87.56438052261292, + 95.10580041512942 + ], + "right": [ + -30.345461751623983, + 72.7480551230025, + 95.10580041512942 + ], + "height": 95.10580041512942, + "plane_slope": -0.3402081593208893, + "plane_intercept": 62.42428143674005, + "target_roof_pixels": [ + [ + 307, + 115 + ], + [ + 478, + 85 + ] + ], + "fitted_roof_pixels": [ + [ + 312.3472404939408, + 129.8908610428336 + ], + [ + 473.3578573676606, + 72.07267863253122 + ] + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf.png b/astra_scene_test/Scene_04/assets/wet_glass_psf.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8aa0fea6fc43ba54fbfa5701f802dfff974687 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf.png differ diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf_left.png b/astra_scene_test/Scene_04/assets/wet_glass_psf_left.png new file mode 100644 index 0000000000000000000000000000000000000000..6f31325a3351e3d134a6166481892e3f2fb3d025 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf_left.png differ diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf_lower.png b/astra_scene_test/Scene_04/assets/wet_glass_psf_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..f467e5220d98da0ebcfcbd8529d74f6da8886a24 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf_lower.png differ diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf_opening.png b/astra_scene_test/Scene_04/assets/wet_glass_psf_opening.png new file mode 100644 index 0000000000000000000000000000000000000000..f021eba2c7b848a45eb8da04f215e45bf0e66fd0 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf_opening.png differ diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf_right_lower.png b/astra_scene_test/Scene_04/assets/wet_glass_psf_right_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..7ec12b4e14fb0db1f22ecb7ac1ec6d392ec1a11d Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf_right_lower.png differ diff --git a/astra_scene_test/Scene_04/assets/wet_glass_psf_right_upper.png b/astra_scene_test/Scene_04/assets/wet_glass_psf_right_upper.png new file mode 100644 index 0000000000000000000000000000000000000000..240a1abe778ee33736ce56bcd3ba79c4505aec63 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/wet_glass_psf_right_upper.png differ diff --git a/astra_scene_test/Scene_04/assets/white_suv_landmarks.json b/astra_scene_test/Scene_04/assets/white_suv_landmarks.json new file mode 100644 index 0000000000000000000000000000000000000000..bb11e5c2721476ed70e7dd1e1787b6ea19de0162 --- /dev/null +++ b/astra_scene_test/Scene_04/assets/white_suv_landmarks.json @@ -0,0 +1,151 @@ +{ + "parameters": { + "x": -9.975556758220831, + "y": 24.153402893893198, + "scale_x": 1.3417784327099649, + "scale_y": 0.7223631667696098, + "scale_z": 1.4410294687795644, + "yaw": -0.3538328241534578, + "pitch": 0.009272241405475041, + "z": -0.15096781431132672 + }, + "observations": { + "front_wheel": { + "1": [ + 469, + 554 + ], + "33": [ + 378, + 567 + ], + "65": [ + 132, + 623 + ] + }, + "rear_wheel": { + "1": [ + 488, + 545 + ], + "33": [ + 425, + 555 + ], + "65": [ + 261, + 594 + ] + }, + "roof_front": { + "1": [ + 465, + 516 + ], + "33": [ + 379, + 508 + ], + "65": [ + 172, + 512 + ] + }, + "roof_rear": { + "1": [ + 483, + 518 + ], + "33": [ + 410, + 510 + ], + "65": [ + 241, + 516 + ] + } + }, + "predictions": { + "front_wheel": { + "1": [ + 460.87578499690517, + 553.0760818203138 + ], + "33": [ + 379.3108360352499, + 564.7431737341938 + ], + "65": [ + 132.4492949802186, + 620.7756711590624 + ] + }, + "rear_wheel": { + "1": [ + 485.3016460619449, + 549.1705829456153 + ], + "33": [ + 423.9872659645872, + 557.0281661397516 + ], + "65": [ + 262.014525923232, + 596.9431074576278 + ] + }, + "roof_front": { + "1": [ + 462.7459271307499, + 515.6979733111909 + ], + "33": [ + 385.5366835300524, + 509.7780998951379 + ], + "65": [ + 168.14084065418217, + 514.0867330526124 + ] + }, + "roof_rear": { + "1": [ + 477.28143192434254, + 515.2544652166251 + ], + "33": [ + 411.9233468504648, + 509.09674176874347 + ], + "65": [ + 241.4783625619163, + 513.1224778272925 + ] + } + }, + "cost": 61.328790397131776, + "triangulated": { + "front_wheel": [ + -8.89006399400093, + 22.53808090526565, + 0.45263294676290755 + ], + "rear_wheel": [ + -8.089433587435062, + 24.349948776366407, + 0.6426241609416393 + ], + "roof_front": [ + -9.547532924716826, + 23.59133225707105, + 2.6972381774810623 + ], + "roof_rear": [ + -8.630954604293809, + 24.23893546140849, + 2.5930954008864413 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_left.png b/astra_scene_test/Scene_04/assets/windshield_glare_left.png new file mode 100644 index 0000000000000000000000000000000000000000..62a839a1655194e7d3cc7f4976a5fb15c327d8e3 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_left.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_lower.png b/astra_scene_test/Scene_04/assets/windshield_glare_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..04523705d32663742381ee73f30a4f7179fa7f1d Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_lower.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_opening.png b/astra_scene_test/Scene_04/assets/windshield_glare_opening.png new file mode 100644 index 0000000000000000000000000000000000000000..f5faddf9f46dc21b5ce231c675fc65001a7da685 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_opening.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_right.png b/astra_scene_test/Scene_04/assets/windshield_glare_right.png new file mode 100644 index 0000000000000000000000000000000000000000..343b29371303a382f7ebb325adfd8af03fc03194 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_right.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_right_lower.png b/astra_scene_test/Scene_04/assets/windshield_glare_right_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..90e1065167cad91ce165844b243ce9b95101c1a0 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_right_lower.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_right_upper.png b/astra_scene_test/Scene_04/assets/windshield_glare_right_upper.png new file mode 100644 index 0000000000000000000000000000000000000000..6a108aa9ad130f29e7c302eb8fe5fae5a8e2410c Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_right_upper.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_glare_zones.png b/astra_scene_test/Scene_04/assets/windshield_glare_zones.png new file mode 100644 index 0000000000000000000000000000000000000000..7e17dbca7e7beefcb32b119e890fc0f5db9ae19e Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_glare_zones.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_lower_defocus.png b/astra_scene_test/Scene_04/assets/windshield_lower_defocus.png new file mode 100644 index 0000000000000000000000000000000000000000..661d7d7d933f6ee2e7c75c5f7321836166e4b874 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_lower_defocus.png differ diff --git a/astra_scene_test/Scene_04/assets/windshield_water_height.png b/astra_scene_test/Scene_04/assets/windshield_water_height.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8c7b9de1c600ac5c6cfaf00db81000ac4ac6b9 Binary files /dev/null and b/astra_scene_test/Scene_04/assets/windshield_water_height.png differ diff --git a/astra_scene_test/Scene_04/reference/black_car_contact_trial.json b/astra_scene_test/Scene_04/reference/black_car_contact_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..ddc888da169e18c6d322790b1245c7b6e2df3fce --- /dev/null +++ b/astra_scene_test/Scene_04/reference/black_car_contact_trial.json @@ -0,0 +1,113 @@ +{ + "parameters": { + "x": -15.531964092033139, + "y": 17.950452298140377, + "scale_x": 1.8999999994018648, + "scale_y": 0.7213880898277021, + "scale_z": 1.3220152359897148, + "yaw": -0.7048189360173098 + }, + "tire_contact_z": -0.08725300557532119, + "observations": { + "front_wheel": { + "1": [ + 242, + 583 + ], + "17": [ + 114, + 599 + ] + }, + "rear_wheel": { + "1": [ + 309, + 570 + ], + "17": [ + 217, + 580 + ], + "33": [ + 69, + 602 + ] + }, + "roof_front": { + "1": [ + 245, + 522 + ], + "17": [ + 147, + 522 + ] + }, + "roof_rear": { + "1": [ + 306, + 516 + ], + "17": [ + 206, + 517 + ], + "33": [ + 65, + 521 + ] + } + }, + "predictions": { + "front_wheel": { + "1": [ + 241.32406595288074, + 573.3415605963328 + ], + "17": [ + 111.9287777581768, + 589.8492386989836 + ] + }, + "rear_wheel": { + "1": [ + 310.8717086394684, + 566.8577877138214 + ], + "17": [ + 218.7975145158942, + 579.6674458434625 + ], + "33": [ + 68.84690460873287, + 599.4260148235435 + ] + }, + "roof_front": { + "1": [ + 261.0693610177214, + 521.9140595859122 + ], + "17": [ + 148.59168173831188, + 521.3518846330073 + ] + }, + "roof_rear": { + "1": [ + 300.8313886399866, + 521.2227841679234 + ], + "17": [ + 208.38026205506856, + 520.687831087386 + ], + "33": [ + 55.92605625947192, + 519.859047978329 + ] + } + }, + "cost": 142.21185372392205, + "note": "Static pose, pitch0 and both tire bottoms on z0. Camera fixed; metric shape remains ambiguous." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit.json b/astra_scene_test/Scene_04/reference/camera_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..f7544e4a4fcaede03ca0dfe135161030d904da06 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit.json @@ -0,0 +1,190 @@ +{ + "focal_pixels": 369.5041722813606, + "horizontal_fov_degrees": 120, + "epipole": [ + 640.1042693294204, + 514.8000391855434 + ], + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.07006178239057397, + 0.13724117143684933, + 0.20191503303100267, + 0.2670273389130756, + 0.33083857255726246, + 0.3935919070917752, + 0.45610531288909567, + 0.5155779079699941, + 0.5782822323140315, + 0.6395220998142835, + 0.7042332581935309, + 0.7702508162260968, + 0.8319822466282393, + 0.8933900059366289, + 0.9519801814148795, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.001980510901836688, + 0.0061169630178615365, + 0.0001555391557365775 + ], + [ + -0.0010909770722722892, + 0.011953436783872907, + 0.003506991926534917 + ], + [ + 0.003932905861377334, + 0.01221183478218934, + 0.003933344623087797 + ], + [ + 0.01587412039552616, + 0.008012472864033523, + -0.0013355099666734593 + ], + [ + 0.011481320501435606, + 0.008133995101985913, + -0.004341055849970034 + ], + [ + 0.013923124440554834, + 0.010302859021704695, + -0.002750522808853992 + ], + [ + 0.011135050960559651, + 0.01120404952617484, + -0.0022191217552774254 + ], + [ + 0.0021423337603318224, + 0.014228202497392074, + 0.0009788167626128275 + ], + [ + 0.004860872954617726, + 0.016260153195168443, + 0.002526247178810834 + ], + [ + 0.002594406395370193, + 0.01559145243496378, + -0.001866233451062562 + ], + [ + 0.008723227415838454, + 0.018477391888373367, + -0.005678809614482908 + ], + [ + 0.015338025561376489, + 0.023998422423455904, + -0.003687831921026065 + ], + [ + 0.010835718665576982, + 0.02662285597154286, + -0.002250815760281225 + ], + [ + 0.011811326140716331, + 0.026547929564738414, + -9.304674207834673e-05 + ], + [ + 0.00829740907693274, + 0.02325714350626984, + -0.00032327690597505127 + ], + [ + 0.009362380998476258, + 0.021347752842508137, + -0.0009121756901115025 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2971244600465597, + 2.6004529111987766, + 3.7553202974812248, + 2.680448303979969, + 2.0225718156190013, + 3.476593713476169, + 2.251178543752702, + 3.0706227761051563, + 2.49474559894001, + 1.6827903405576428, + 2.636809607611564, + 2.6989926857949813, + 1.5160806791080441, + 1.7528114567906903, + 1.2939416929711798, + 1.6968221484259578, + 2.088280208367186, + 3.159655023927395, + 2.1015103821369587, + 2.77380874265901, + 3.0396274515384447, + 2.2006341461753314, + 2.112321491024724, + 2.7323989744595267, + 2.311977146860039 + ], + "median_reprojection_error_pixels": 0.9083280111561022, + "p90_reprojection_error_pixels": 2.947230527955634, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_cylindrical_f611_p100.0_cy480.json b/astra_scene_test/Scene_04/reference/camera_fit_cylindrical_f611_p100.0_cy480.json new file mode 100644 index 0000000000000000000000000000000000000000..a45cc21ef1b2fa811e1013a3905060c5420d0d30 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_cylindrical_f611_p100.0_cy480.json @@ -0,0 +1,199 @@ +{ + "projection": "cylindrical", + "focal_pixels": 611.154981, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00000009284939, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "optimizer_success": true, + "optimizer_nfev": 54, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": 0.0, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06448909433199747, + 0.13041677958475553, + 0.19311155377263942, + 0.25672885451843497, + 0.32337212233122015, + 0.3840818924297137, + 0.4486370024770101, + 0.5110345778266316, + 0.572197030689451, + 0.6355474551588773, + 0.6984553600845489, + 0.7620883016482627, + 0.826504790932528, + 0.8907969659211593, + 0.9522084656185318, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + -1.350892057831514e-05, + 0.0022970280041478474, + 0.0016675762728734205 + ], + [ + -0.003658254232070795, + 0.0038947392394224154, + 0.007747766452275231 + ], + [ + -0.0013011501118966973, + 0.0021947511723383046, + 0.008229627681096927 + ], + [ + 0.006153299137093796, + -0.0015995445226267546, + 0.0013853491201155225 + ], + [ + 0.0014697412163254121, + -0.002811628079898395, + -4.049664726999305e-05 + ], + [ + 0.0017565633606380915, + -0.0036684627443785024, + 0.002529845434263831 + ], + [ + -0.001864239108617982, + -0.005295306107256127, + 0.004931730328071736 + ], + [ + -0.01026750344111397, + -0.005862979714342945, + 0.011433854599480466 + ], + [ + -0.010019510169699007, + -0.007231577861717588, + 0.014020528079242565 + ], + [ + -0.013503026014160176, + -0.009418255481908928, + 0.01092780981772223 + ], + [ + -0.01112679374987417, + -0.009792318470681599, + 0.0075703804733615555 + ], + [ + -0.00865659255569807, + -0.009795544179513638, + 0.010559653230084634 + ], + [ + -0.01459979977649427, + -0.011398793670874931, + 0.014851673328906282 + ], + [ + -0.016423545918040777, + -0.015697103250689175, + 0.017472184212502115 + ], + [ + -0.021016601994064212, + -0.021769667970273465, + 0.018609783794941208 + ], + [ + -0.021152940013124046, + -0.026201739165286245, + 0.019464174027082726 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.134855311250984, + 4.980063243547933, + 2.609855068006607, + 2.3970376186642106, + 1.935137910895655, + 2.5812024577132338, + 2.127788931939582, + 7.266096715051088, + 3.341645396033797, + 1.6146354897681678, + 2.4650271676666478, + 2.51788771087051, + 1.6070658680187797, + 1.6934168449554623, + 1.192640407288022, + 1.7444303184126777, + 2.216001645820318, + 2.416168623923266, + 1.9945485553270998, + 3.779563731852287, + 2.4670064750180973, + 2.076979697633324, + 1.9987687023389744, + 2.4155116599812576, + 2.17109973282479 + ], + "median_reprojection_error_pixels": 1.474724697412943, + "p90_reprojection_error_pixels": 6.335623299440346, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p0.03_cy480.json b/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p0.03_cy480.json new file mode 100644 index 0000000000000000000000000000000000000000..49d55401be3783e94878c873104241783ac71304 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p0.03_cy480.json @@ -0,0 +1,199 @@ +{ + "projection": "equirectangular", + "focal_pixels": 611.154981, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00000009284939, + "epipole": [ + 649.4367382839207, + 513.6423446351457 + ], + "optimizer_success": true, + "optimizer_nfev": 76, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": 0.0, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06391180852769861, + 0.12789197887907056, + 0.18954879192895213, + 0.2531274632844839, + 0.31702441945264903, + 0.3780143775936003, + 0.4387359954825423, + 0.49839542970533557, + 0.560331197357472, + 0.6229034164776623, + 0.6864317761193798, + 0.750847948390046, + 0.815290242176217, + 0.8799203224485879, + 0.944833153838353, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0013374796246577527, + 0.004452895334082045, + 0.002760667335183162 + ], + [ + -0.0010647548360292142, + 0.008340583160959382, + 0.008941550477996864 + ], + [ + 0.002637445148300528, + 0.008967796736483291, + 0.010389992877499686 + ], + [ + 0.011612631762323536, + 0.007673841312417227, + 0.005125349176807173 + ], + [ + 0.008407772664701038, + 0.009214127129507955, + 0.003456773514982663 + ], + [ + 0.010243058686913957, + 0.011065536195542094, + 0.007070046679558278 + ], + [ + 0.008287011000277613, + 0.012589873292411463, + 0.009349714594692088 + ], + [ + 0.0014424858699127246, + 0.015239846114754827, + 0.015173247650425489 + ], + [ + 0.003431757816839499, + 0.0173165080829292, + 0.018963471912999096 + ], + [ + 0.001724027683738884, + 0.01889302094472775, + 0.01610218937510642 + ], + [ + 0.006547666724693105, + 0.022645343180123016, + 0.014938125238885398 + ], + [ + 0.011885292637138372, + 0.027247550490681983, + 0.020553115094875493 + ], + [ + 0.008586517042006714, + 0.03067174420160038, + 0.026171419123893865 + ], + [ + 0.010245161914379632, + 0.03199600239350801, + 0.03329956241252277 + ], + [ + 0.00891722360520089, + 0.0317564518278113, + 0.039491392104056174 + ], + [ + 0.011259467677140813, + 0.032247083541883104, + 0.04514825161840656 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.3542995141534293, + 2.4372367987978167, + 4.825678536431219, + 2.7282710510850112, + 1.9407166359910673, + 5.078702826698262, + 2.2659128137686713, + 2.76801620146883, + 2.3761963381990228, + 1.6457565281783677, + 2.212026439563776, + 2.22312213631801, + 1.4583600577507985, + 1.724775412513709, + 1.2382913288500457, + 1.6337225456754338, + 2.050169260619565, + 3.754524726965522, + 2.027640762450462, + 2.5961180335026706, + 2.350734024870892, + 2.1579407947167675, + 2.0404947880870767, + 2.9689643482018124, + 2.301105886378358 + ], + "median_reprojection_error_pixels": 1.3673953314585612, + "p90_reprojection_error_pixels": 6.024593461728612, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p100.0_cy480.json b/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p100.0_cy480.json new file mode 100644 index 0000000000000000000000000000000000000000..cf736fb27c2a1ce00c473af3930f295a9fd5a4af --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_equirectangular_f611_p100.0_cy480.json @@ -0,0 +1,199 @@ +{ + "projection": "equirectangular", + "focal_pixels": 611.154981, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00000009284939, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "optimizer_success": true, + "optimizer_nfev": 37, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": 0.0, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06478329840275224, + 0.13030460771256971, + 0.1928160585867095, + 0.25649607971865745, + 0.3208782361895841, + 0.38193989936853945, + 0.4458720254214668, + 0.5070797892193929, + 0.5685466684894943, + 0.6306952168988338, + 0.6944011294827074, + 0.7597653186981028, + 0.8239118560867335, + 0.8905716900244716, + 0.9524832124671277, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + -2.3044617068495464e-06, + 0.0023271872845185356, + 0.002740664918111743 + ], + [ + -0.003645662313957169, + 0.003951156431908267, + 0.009380954031506718 + ], + [ + -0.0011903730594945243, + 0.0023142081480227875, + 0.011209281476797861 + ], + [ + 0.0064605110956862995, + -0.0013785456497193377, + 0.006326355498606624 + ], + [ + 0.001778321179978206, + -0.0024628280993574335, + 0.004899620949429439 + ], + [ + 0.002192932844232191, + -0.00330552313475627, + 0.008511802101034976 + ], + [ + -0.0013948321981395652, + -0.004862772567339182, + 0.01151471393047822 + ], + [ + -0.009848632712043854, + -0.0053718127142077035, + 0.017840214396753765 + ], + [ + -0.009390132506929938, + -0.006702905988333214, + 0.021978056678855034 + ], + [ + -0.012788908389997126, + -0.00875184846967887, + 0.019830081601858344 + ], + [ + -0.010027294280399059, + -0.009166084479583374, + 0.01831755016974793 + ], + [ + -0.006954686342904779, + -0.009387342626003195, + 0.023037961013288453 + ], + [ + -0.012619600996147574, + -0.011099669967715171, + 0.027678514819469844 + ], + [ + -0.01391366944851682, + -0.015705323937044217, + 0.03127042340700371 + ], + [ + -0.017965338646340978, + -0.0214816152041671, + 0.033962878900011845 + ], + [ + -0.018244026446911452, + -0.02578994626415358, + 0.037677644201401995 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.1176772014576772, + 5.418824282694666, + 2.640156481016188, + 2.418626906983489, + 1.9266075747142444, + 2.5605269201041034, + 2.1134015169604723, + 7.689772744501393, + 3.435099666871854, + 1.6159810125302043, + 2.235333556334498, + 2.2346192376003247, + 1.6064623934271767, + 1.696374884603853, + 1.1897504379385166, + 1.7643095246039386, + 2.235662237999692, + 2.384060906958486, + 1.9830347746839965, + 3.9210032095753684, + 2.492816601333477, + 2.068184020641097, + 1.9836157868671265, + 2.405076345628285, + 2.156876168419635 + ], + "median_reprojection_error_pixels": 1.5916179635125387, + "p90_reprojection_error_pixels": 9.292199081001192, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0.json b/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0.json new file mode 100644 index 0000000000000000000000000000000000000000..dd30ca6038631e68092bbe3ac8b19ee4302c6bde --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0.json @@ -0,0 +1,190 @@ +{ + "focal_pixels": 369.5, + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06962764265628649, + 0.13655150628251467, + 0.20105653749790645, + 0.26575977774149045, + 0.32915197764675214, + 0.3914186270147573, + 0.4542039484237803, + 0.5140969431959234, + 0.576054169594121, + 0.6366744971456203, + 0.7008736322420772, + 0.7666483238611931, + 0.8283429506533235, + 0.8899558858757264, + 0.9496112793717701, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.000343750328709264, + 0.003935068668348956, + 0.0008579555815715451 + ], + [ + -0.0043643170085231955, + 0.007546850031612208, + 0.004978527179798893 + ], + [ + -0.00104082114621088, + 0.0055209475170620815, + 0.006190694422352187 + ], + [ + 0.009042199848436938, + -0.0011481183002156289, + 0.0015498174786575337 + ], + [ + 0.0027052064262942954, + -0.003553392256393048, + -0.000665100206404807 + ], + [ + 0.0031421628795200306, + -0.00403923099637305, + 0.0017334094449435056 + ], + [ + -0.001801978862095022, + -0.005962554623369442, + 0.003253753778014675 + ], + [ + -0.013060676548532288, + -0.005691041629673833, + 0.00776489401665886 + ], + [ + -0.012698014452461605, + -0.0068483546034495005, + 0.010318630285755427 + ], + [ + -0.017575449584673226, + -0.010819622319586853, + 0.006905977442521772 + ], + [ + -0.014449752555135954, + -0.011876911617835332, + 0.003886400069966915 + ], + [ + -0.010983804127672808, + -0.010722693050716704, + 0.00700719590412606 + ], + [ + -0.01868938552334487, + -0.012161725568134907, + 0.010264551373760318 + ], + [ + -0.02127921029635932, + -0.017076236116790083, + 0.013420230030492057 + ], + [ + -0.028929590150316587, + -0.025447713855844753, + 0.013978762548487234 + ], + [ + -0.03199525813597809, + -0.03179505975866488, + 0.01382128174418264 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2175595890366937, + 4.282959992466799, + 2.747295391571148, + 2.494875097228703, + 2.018235940329869, + 2.7257430285991546, + 2.209224123967423, + 6.277892157820448, + 3.224979879918974, + 1.6766036085001206, + 2.68076332974861, + 2.730754458957668, + 1.6444616016376992, + 1.7400607960845846, + 1.255996560654726, + 1.7963367653670674, + 2.2256487641925706, + 2.547420656965834, + 2.0791665664561867, + 3.7332744397891817, + 3.189711194948683, + 2.160554085277853, + 2.077210412986014, + 2.5206329476895384, + 2.2583225634752004 + ], + "median_reprojection_error_pixels": 1.289244890454553, + "p90_reprojection_error_pixels": 5.39878976016502, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0_cy480.json b/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0_cy480.json new file mode 100644 index 0000000000000000000000000000000000000000..7291da8977890f11e930e6741c1416a51122ed12 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_f370_p100.0_cy480.json @@ -0,0 +1,198 @@ +{ + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "optimizer_success": true, + "optimizer_nfev": 58, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": 0.29999999999999993, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.07069726504424696, + 0.14405919533467962, + 0.20505379071145427, + 0.2515667367272918, + 0.32406159025030434, + 0.3851513948094464, + 0.4528987675862592, + 0.524176279576674, + 0.5837789164115477, + 0.6470646867920771, + 0.7079895402728676, + 0.7717253702791266, + 0.8362631609250158, + 0.9005764158298869, + 0.9600390778238594, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.00020609906396746553, + 0.003222502159291887, + 0.0011718030658345424 + ], + [ + -0.004705533851790109, + 0.005028475870144635, + 0.006484550100052919 + ], + [ + -0.001615294918704906, + 0.003025605787954558, + 0.007884740522270367 + ], + [ + 0.009001283031365737, + -0.0004476382053509587, + -0.0014881702974584297 + ], + [ + 0.002255362362020733, + -0.002748163473909014, + -0.0013664171229869782 + ], + [ + 0.0024742014151010266, + -0.0036539880443025342, + 0.0006285521492655154 + ], + [ + -0.0027255370059072275, + -0.0062427817673776234, + 0.004247668788484412 + ], + [ + -0.013915460451191866, + -0.0081723906081978, + 0.01232788623078345 + ], + [ + -0.013509968183776283, + -0.009647781261264402, + 0.01505663052317289 + ], + [ + -0.018140755455666676, + -0.012660781915393257, + 0.011908698371474213 + ], + [ + -0.015151380823796391, + -0.012403198203550247, + 0.007799582299371315 + ], + [ + -0.012379596380027159, + -0.012151241146618107, + 0.007925055465094378 + ], + [ + -0.019122831974060308, + -0.014368764150545946, + 0.014209198086209343 + ], + [ + -0.02224233123762566, + -0.019778616066054817, + 0.01489604692593693 + ], + [ + -0.02831218755753279, + -0.026976437294283197, + 0.015026910864810204 + ], + [ + -0.028900699246245002, + -0.03088292584698631, + 0.01584696004206849 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2720349716885835, + 4.010915095588875, + 2.80618249631085, + 2.5370806379108903, + 2.1332605270501204, + 2.781538840143969, + 2.272837060401338, + 5.718169592313721, + 3.195520286245081, + 1.782116992139487, + 3.3631701070506193, + 3.4664533609847314, + 1.6979034699637894, + 1.8175086541792864, + 1.3613870660195504, + 1.8962724334634593, + 2.274346109989175, + 2.5999239614636025, + 2.191332398728378, + 3.7885541808973975, + 4.417918429345469, + 2.25329776183143, + 2.170157836932337, + 2.582280847381822, + 2.34052477767493 + ], + "median_reprojection_error_pixels": 1.6275975520650472, + "p90_reprojection_error_pixels": 5.47989179179455, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_f370_p5.0.json b/astra_scene_test/Scene_04/reference/camera_fit_f370_p5.0.json new file mode 100644 index 0000000000000000000000000000000000000000..17e293a25c013778f810bc52225a92009eca61d2 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_f370_p5.0.json @@ -0,0 +1,190 @@ +{ + "focal_pixels": 369.5, + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 636.5404903622749, + 515.7843453429374 + ], + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.07019915966550773, + 0.13746887097951688, + 0.2022613992396395, + 0.267527462045243, + 0.33139503864333286, + 0.3942002659119282, + 0.4568296091607401, + 0.516293803363877, + 0.5789330485685902, + 0.6400294072101181, + 0.7046223611860553, + 0.7705557232125081, + 0.8319229970092035, + 0.8933761862202259, + 0.9523944227077171, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0019059270503517624, + 0.005812887214396728, + 0.0002452113251346533 + ], + [ + -0.0012415560202318422, + 0.011335860696906843, + 0.0036781770913165497 + ], + [ + 0.0037026425002792966, + 0.011274369102032913, + 0.004190715503588221 + ], + [ + 0.015556208805829077, + 0.0067321620303064, + -0.0009893525248467257 + ], + [ + 0.011064440987628297, + 0.006493276094330061, + -0.003914961948790108 + ], + [ + 0.013409448089909983, + 0.008282498667212335, + -0.00224396024910795 + ], + [ + 0.010505601902356807, + 0.008781952422823815, + -0.0016232875295092983 + ], + [ + 0.0013874211880624873, + 0.011401783028628916, + 0.00169295209568871 + ], + [ + 0.003983678939154361, + 0.012967870057703867, + 0.0033231405381692167 + ], + [ + 0.0015862379910393954, + 0.011824353753698908, + -0.0009935475970966423 + ], + [ + 0.007582482310770166, + 0.014129318371044175, + -0.004803140195781484 + ], + [ + 0.01405728900632604, + 0.018970408251783755, + -0.002903037569432428 + ], + [ + 0.009368371749226286, + 0.020993768306835227, + -0.001379957071859538 + ], + [ + 0.010251174941177277, + 0.020152339563674536, + 0.0006095012143257522 + ], + [ + 0.006601721559352394, + 0.01622197578489258, + 0.00041453928578887364 + ], + [ + 0.007526099403361045, + 0.01394711549906351, + 0.0003181065721994922 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2838541850329497, + 2.659601571835736, + 3.5081547287090737, + 2.6494468666845785, + 2.021338783444806, + 3.298091447576669, + 2.243497473765977, + 3.190221003679338, + 2.5351217092457774, + 1.6811020622621053, + 2.653036411651938, + 2.7153521605419555, + 1.5308134321888278, + 1.7499568632836198, + 1.2904029376955855, + 1.6960741074387682, + 2.0952908075766987, + 3.0554823578590646, + 2.0975579862953433, + 2.8326165242507435, + 3.0799241135806983, + 2.1939028135472354, + 2.107513048705329, + 2.68844797979481, + 2.3018432825793473 + ], + "median_reprojection_error_pixels": 0.9795913667339933, + "p90_reprojection_error_pixels": 2.9085380045791562, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480.json b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480.json new file mode 100644 index 0000000000000000000000000000000000000000..7887908a51e8893adea0e14b1118b08e99d0bc3f --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480.json @@ -0,0 +1,199 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 639.6776337080299, + 513.9906205242038 + ], + "optimizer_success": true, + "optimizer_nfev": 116, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -0.04273840798292971, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06662809424464981, + 0.1346959922131431, + 0.19536786912083784, + 0.25375973454233436, + 0.3213701617983418, + 0.38321710527580677, + 0.44671797192788926, + 0.5093387935536575, + 0.5718000241238298, + 0.6348477141751818, + 0.6985951469786177, + 0.7632741411860535, + 0.8269109543222969, + 0.8890418531892739, + 0.9494132024708847, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0022186467498330822, + 0.005988365686321537, + 0.0012650252604316252 + ], + [ + -0.0013229141738296877, + 0.010848555681235827, + 0.0061534743523141 + ], + [ + 0.004335471698145356, + 0.010758576660172629, + 0.0064448385573864465 + ], + [ + 0.01806381527831792, + 0.007762696357885902, + -0.0014489780160114665 + ], + [ + 0.013032350034775486, + 0.008598067251883385, + -0.0038436605941292454 + ], + [ + 0.015773502643307746, + 0.010197855621933294, + -0.0018196555508884654 + ], + [ + 0.012640033950764162, + 0.010764895849187795, + -0.000646463134286814 + ], + [ + 0.0027801608588143567, + 0.012728798005763755, + 0.004703580413032486 + ], + [ + 0.0057613702493516964, + 0.014079848553209994, + 0.006539763370895382 + ], + [ + 0.0034002701943823843, + 0.014285463959386072, + 0.001784919137315361 + ], + [ + 0.010319464998343018, + 0.0179642772636026, + -0.0019203699512114869 + ], + [ + 0.01778309503075021, + 0.022698010548954102, + 0.0012403197403231916 + ], + [ + 0.013279426291056903, + 0.02454603596626473, + 0.0035541235686658928 + ], + [ + 0.01460813927313922, + 0.02359281743117832, + 0.0055822829438812805 + ], + [ + 0.01153174614119566, + 0.019593367318522965, + 0.004288003227049923 + ], + [ + 0.014058117427372103, + 0.017362258645149734, + 0.0025730605812523295 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.235401818352547, + 2.6857385504961586, + 3.6151733319603405, + 2.660453066191109, + 1.9813483797346385, + 3.25334907263098, + 2.197505990052404, + 3.1929078305852205, + 2.5544595560565972, + 1.671713901201528, + 2.459376530820359, + 2.49591073355484, + 1.5068318939206307, + 1.7496740445909384, + 1.266788758430158, + 1.67705783848278, + 2.1081956905291355, + 2.9983707194949005, + 2.0561424245479216, + 2.8333839992190386, + 2.540546669289442, + 2.152811829052301, + 2.059042213214702, + 2.650978625815986, + 2.257303087660516 + ], + "median_reprojection_error_pixels": 0.8695204117666346, + "p90_reprojection_error_pixels": 2.6782708890124423, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480_nodist.json b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480_nodist.json new file mode 100644 index 0000000000000000000000000000000000000000..0391f3d56631f7ad1c32781f16f5646b61041442 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f370_p0.03_cy480_nodist.json @@ -0,0 +1,199 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 640.1707869459426, + 514.7179664031315 + ], + "optimizer_success": true, + "optimizer_nfev": 80, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06818098806171356, + 0.13756848658320978, + 0.19887779004285744, + 0.25710216938241126, + 0.32595859862392734, + 0.38831783381560425, + 0.45241538388437325, + 0.5153858724278887, + 0.5783015694155771, + 0.6416748670059164, + 0.705664010997911, + 0.7704617097985091, + 0.8336938907909718, + 0.8947980522821005, + 0.9527437058038144, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.00224774362252664, + 0.005924292116590759, + 0.001036639327532078 + ], + [ + -0.0011910404166182208, + 0.010684564529596154, + 0.0055015986704040456 + ], + [ + 0.004378268063234446, + 0.010671183472230263, + 0.0058501298063352095 + ], + [ + 0.01763652280787367, + 0.008012416918407749, + -0.0014764642394765961 + ], + [ + 0.012672512318890548, + 0.008782486433992596, + -0.003629059321305456 + ], + [ + 0.0152720514738302, + 0.010479784293087971, + -0.0016160593426945268 + ], + [ + 0.012234677607333646, + 0.010968443417469399, + -0.0004865450450693065 + ], + [ + 0.002863457859054484, + 0.012750584445496068, + 0.0034520818165749713 + ], + [ + 0.005693100730550619, + 0.014236922755496246, + 0.005583381445346449 + ], + [ + 0.0032532869847308288, + 0.014568374829030799, + 0.000763004216584281 + ], + [ + 0.009724637560048151, + 0.018447415714245713, + -0.002456395269454268 + ], + [ + 0.01657872518228831, + 0.023394415244521433, + 0.0009939050380583082 + ], + [ + 0.012199589517405858, + 0.02533129144158546, + 0.002858604114990466 + ], + [ + 0.01297107627292627, + 0.024838152587685007, + 0.005258001294290399 + ], + [ + 0.009737385776637825, + 0.021337103777977807, + 0.004343760768897358 + ], + [ + 0.011789093761427921, + 0.020360958760674838, + 0.002602562131042079 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2659399244805773, + 2.6948534930745938, + 3.652966722764379, + 2.68590390287027, + 2.018685034835302, + 3.3343229630915614, + 2.229130638539127, + 3.1888415072387786, + 2.569831635740863, + 1.700245339440428, + 2.6668575747314827, + 2.7319867441904115, + 1.523781585706796, + 1.7729100288731323, + 1.2970368078554588, + 1.7051438641295515, + 2.1266169413023075, + 3.0725443851634133, + 2.0936647017552112, + 2.8429376368547863, + 3.084397710654423, + 2.1887027902769978, + 2.0950281835696836, + 2.6968436912447915, + 2.2944323232075217 + ], + "median_reprojection_error_pixels": 0.9941164845058215, + "p90_reprojection_error_pixels": 2.901010989230633, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_perspective_f650_p0.0_cy480_nodist.json b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f650_p0.0_cy480_nodist.json new file mode 100644 index 0000000000000000000000000000000000000000..1aa8edb12cd2166571ef9547461a153010091574 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_perspective_f650_p0.0_cy480_nodist.json @@ -0,0 +1,199 @@ +{ + "projection": "perspective", + "focal_pixels": 650.0, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 89.11171113377615, + "epipole": [ + 639.8323821846435, + 514.669586321549 + ], + "optimizer_success": true, + "optimizer_nfev": 55, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06701958338484801, + 0.13469642947489233, + 0.19769220946237814, + 0.260657068016453, + 0.3276700958294912, + 0.3902055080566638, + 0.45328512732966164, + 0.5153847085121649, + 0.5782628109291402, + 0.6416584044712467, + 0.7053615590605775, + 0.769655477195789, + 0.8327946814996952, + 0.8940423416872182, + 0.9526329060602159, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0012340511544570044, + 0.0034511227246324183, + 0.00120202283776032 + ], + [ + -0.0009729542148815825, + 0.006342242063079393, + 0.006443620023739859 + ], + [ + 0.00238071539416358, + 0.006178844947546969, + 0.0062966370022444415 + ], + [ + 0.010476525590621674, + 0.004142805509960551, + -0.0015279368554555603 + ], + [ + 0.007514644174757227, + 0.004690176326047511, + -0.0036906793141983727 + ], + [ + 0.00915078357307234, + 0.0055033665396300785, + -0.001774259896241233 + ], + [ + 0.007333844453320958, + 0.00588497906967851, + -0.0006681122594608837 + ], + [ + 0.0012052026212279964, + 0.007271521903676919, + 0.004646037586700862 + ], + [ + 0.0029726737620358367, + 0.007993669509918512, + 0.006438712573541585 + ], + [ + 0.0013409246361203626, + 0.008186915899720341, + 0.0021343185192213526 + ], + [ + 0.005495960951185192, + 0.010236048699031892, + -0.0018840108414187595 + ], + [ + 0.00999427024175968, + 0.012917154448424952, + 0.0007562270733792243 + ], + [ + 0.006769834139492305, + 0.014287578169499422, + 0.003688442838976174 + ], + [ + 0.007458143422264729, + 0.013765028611409618, + 0.005815748897722298 + ], + [ + 0.005383093802379584, + 0.011473255132432822, + 0.00522747039811473 + ], + [ + 0.00707400414960027, + 0.01021961237121139, + 0.0032875171445000025 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2517331547295036, + 2.733897110507583, + 3.60139401210052, + 2.664630759211578, + 1.9979749321976663, + 3.286684474933288, + 2.212786742282675, + 3.2476136625508434, + 2.605833617967712, + 1.7176075550596068, + 2.6812625006560182, + 2.7429545429207574, + 1.5169316321039097, + 1.7893953298145746, + 1.3025787995710383, + 1.7128799855087196, + 2.1497772245147773, + 3.039342105460531, + 2.0734203111835705, + 2.8906094515611986, + 3.146157584084847, + 2.1677645795012666, + 2.0803043870591393, + 2.6654950906131196, + 2.2732306471381905 + ], + "median_reprojection_error_pixels": 0.9082215143012065, + "p90_reprojection_error_pixels": 2.6871043055663826, + "limitation": "89.1117-degree horizontal pinhole trial. Exact intrinsics remain ambiguous under near-forward motion; this small residual improvement does not establish a unique physical focal length." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_perspective_selected.json b/astra_scene_test/Scene_04/reference/camera_fit_perspective_selected.json new file mode 100644 index 0000000000000000000000000000000000000000..28f9ba49c4359ae1a7bd8ea39ac7a902aeff03fe --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_perspective_selected.json @@ -0,0 +1,197 @@ +{ + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "optimizer_success": true, + "optimizer_nfev": 30, + "optimizer_message": "`ftol` termination condition is satisfied.", + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06806794570387358, + 0.13838570865471742, + 0.1998086135376515, + 0.25268692838978535, + 0.3220003628774281, + 0.38265566215065144, + 0.4491100974786929, + 0.5179445999400273, + 0.5781911919236278, + 0.6415275466558922, + 0.7028424397275321, + 0.7662728877472237, + 0.8321484381272545, + 0.8949910759345721, + 0.9568387349356784, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 7.970778437958559e-05, + 0.0034029959711659795, + 0.0013843562742515557 + ], + [ + -0.005389587941039592, + 0.005467733629187528, + 0.006767489531028712 + ], + [ + -0.0019398351802378897, + 0.0029452354005250745, + 0.007754700813419292 + ], + [ + 0.009539690354054348, + -0.001855864519185092, + -0.00031756770471071273 + ], + [ + 0.0021870103200265062, + -0.004159843712941438, + -0.0013727490842938353 + ], + [ + 0.002440803338705285, + -0.005401805115803234, + 0.0008946688709505327 + ], + [ + -0.0033005651309537744, + -0.008247401753660788, + 0.003229309626757449 + ], + [ + -0.01601563011740977, + -0.010138200833504632, + 0.009632697093985277 + ], + [ + -0.01576587458854561, + -0.012111751344382608, + 0.01208851662443387 + ], + [ + -0.021154925139067585, + -0.015620060687691737, + 0.008432119421170826 + ], + [ + -0.017931212774870118, + -0.015780668154091897, + 0.004713672703641766 + ], + [ + -0.014762589539688549, + -0.015571389001831014, + 0.007020628376569298 + ], + [ + -0.02316250789974209, + -0.01847041833849067, + 0.011539355180662372 + ], + [ + -0.026715516358495523, + -0.024744914053527033, + 0.013452664119122586 + ], + [ + -0.03480870514098197, + -0.033931358575548326, + 0.012866470785062641 + ], + [ + -0.03574936893871316, + -0.03920810370805046, + 0.011717812727139709 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.1730617860200803, + 4.800102981583717, + 2.645808139917365, + 2.419783282562076, + 1.9855527863237858, + 2.6321238269999734, + 2.1659667980960653, + 7.101550174591025, + 3.377438780063602, + 1.69010236088028, + 2.638735019280352, + 2.68341792655301, + 1.626795891065523, + 1.754038401748671, + 1.2552626137950078, + 1.7862325143638011, + 2.268001218268813, + 2.469088774854978, + 2.046125826377901, + 3.8501834009050846, + 3.1442549339630395, + 2.1206605824103058, + 2.045076100442462, + 2.455453515842036, + 2.2144591227827917 + ], + "median_reprojection_error_pixels": 1.4285301752976989, + "p90_reprojection_error_pixels": 5.533630663440333, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_radial_rejected.json b/astra_scene_test/Scene_04/reference/camera_fit_radial_rejected.json new file mode 100644 index 0000000000000000000000000000000000000000..7291da8977890f11e930e6741c1416a51122ed12 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_radial_rejected.json @@ -0,0 +1,198 @@ +{ + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 615.0000009999999, + 536.9999990000001 + ], + "optimizer_success": true, + "optimizer_nfev": 58, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": 0.29999999999999993, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.07069726504424696, + 0.14405919533467962, + 0.20505379071145427, + 0.2515667367272918, + 0.32406159025030434, + 0.3851513948094464, + 0.4528987675862592, + 0.524176279576674, + 0.5837789164115477, + 0.6470646867920771, + 0.7079895402728676, + 0.7717253702791266, + 0.8362631609250158, + 0.9005764158298869, + 0.9600390778238594, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.00020609906396746553, + 0.003222502159291887, + 0.0011718030658345424 + ], + [ + -0.004705533851790109, + 0.005028475870144635, + 0.006484550100052919 + ], + [ + -0.001615294918704906, + 0.003025605787954558, + 0.007884740522270367 + ], + [ + 0.009001283031365737, + -0.0004476382053509587, + -0.0014881702974584297 + ], + [ + 0.002255362362020733, + -0.002748163473909014, + -0.0013664171229869782 + ], + [ + 0.0024742014151010266, + -0.0036539880443025342, + 0.0006285521492655154 + ], + [ + -0.0027255370059072275, + -0.0062427817673776234, + 0.004247668788484412 + ], + [ + -0.013915460451191866, + -0.0081723906081978, + 0.01232788623078345 + ], + [ + -0.013509968183776283, + -0.009647781261264402, + 0.01505663052317289 + ], + [ + -0.018140755455666676, + -0.012660781915393257, + 0.011908698371474213 + ], + [ + -0.015151380823796391, + -0.012403198203550247, + 0.007799582299371315 + ], + [ + -0.012379596380027159, + -0.012151241146618107, + 0.007925055465094378 + ], + [ + -0.019122831974060308, + -0.014368764150545946, + 0.014209198086209343 + ], + [ + -0.02224233123762566, + -0.019778616066054817, + 0.01489604692593693 + ], + [ + -0.02831218755753279, + -0.026976437294283197, + 0.015026910864810204 + ], + [ + -0.028900699246245002, + -0.03088292584698631, + 0.01584696004206849 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2720349716885835, + 4.010915095588875, + 2.80618249631085, + 2.5370806379108903, + 2.1332605270501204, + 2.781538840143969, + 2.272837060401338, + 5.718169592313721, + 3.195520286245081, + 1.782116992139487, + 3.3631701070506193, + 3.4664533609847314, + 1.6979034699637894, + 1.8175086541792864, + 1.3613870660195504, + 1.8962724334634593, + 2.274346109989175, + 2.5999239614636025, + 2.191332398728378, + 3.7885541808973975, + 4.417918429345469, + 2.25329776183143, + 2.170157836932337, + 2.582280847381822, + 2.34052477767493 + ], + "median_reprojection_error_pixels": 1.6275975520650472, + "p90_reprojection_error_pixels": 5.47989179179455, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_fit_selected.json b/astra_scene_test/Scene_04/reference/camera_fit_selected.json new file mode 100644 index 0000000000000000000000000000000000000000..0391f3d56631f7ad1c32781f16f5646b61041442 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_fit_selected.json @@ -0,0 +1,199 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 640.1707869459426, + 514.7179664031315 + ], + "optimizer_success": true, + "optimizer_nfev": 80, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06818098806171356, + 0.13756848658320978, + 0.19887779004285744, + 0.25710216938241126, + 0.32595859862392734, + 0.38831783381560425, + 0.45241538388437325, + 0.5153858724278887, + 0.5783015694155771, + 0.6416748670059164, + 0.705664010997911, + 0.7704617097985091, + 0.8336938907909718, + 0.8947980522821005, + 0.9527437058038144, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.00224774362252664, + 0.005924292116590759, + 0.001036639327532078 + ], + [ + -0.0011910404166182208, + 0.010684564529596154, + 0.0055015986704040456 + ], + [ + 0.004378268063234446, + 0.010671183472230263, + 0.0058501298063352095 + ], + [ + 0.01763652280787367, + 0.008012416918407749, + -0.0014764642394765961 + ], + [ + 0.012672512318890548, + 0.008782486433992596, + -0.003629059321305456 + ], + [ + 0.0152720514738302, + 0.010479784293087971, + -0.0016160593426945268 + ], + [ + 0.012234677607333646, + 0.010968443417469399, + -0.0004865450450693065 + ], + [ + 0.002863457859054484, + 0.012750584445496068, + 0.0034520818165749713 + ], + [ + 0.005693100730550619, + 0.014236922755496246, + 0.005583381445346449 + ], + [ + 0.0032532869847308288, + 0.014568374829030799, + 0.000763004216584281 + ], + [ + 0.009724637560048151, + 0.018447415714245713, + -0.002456395269454268 + ], + [ + 0.01657872518228831, + 0.023394415244521433, + 0.0009939050380583082 + ], + [ + 0.012199589517405858, + 0.02533129144158546, + 0.002858604114990466 + ], + [ + 0.01297107627292627, + 0.024838152587685007, + 0.005258001294290399 + ], + [ + 0.009737385776637825, + 0.021337103777977807, + 0.004343760768897358 + ], + [ + 0.011789093761427921, + 0.020360958760674838, + 0.002602562131042079 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2659399244805773, + 2.6948534930745938, + 3.652966722764379, + 2.68590390287027, + 2.018685034835302, + 3.3343229630915614, + 2.229130638539127, + 3.1888415072387786, + 2.569831635740863, + 1.700245339440428, + 2.6668575747314827, + 2.7319867441904115, + 1.523781585706796, + 1.7729100288731323, + 1.2970368078554588, + 1.7051438641295515, + 2.1266169413023075, + 3.0725443851634133, + 2.0936647017552112, + 2.8429376368547863, + 3.084397710654423, + 2.1887027902769978, + 2.0950281835696836, + 2.6968436912447915, + 2.2944323232075217 + ], + "median_reprojection_error_pixels": 0.9941164845058215, + "p90_reprojection_error_pixels": 2.901010989230633, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/camera_motion_before_full_refinement.json b/astra_scene_test/Scene_04/reference/camera_motion_before_full_refinement.json new file mode 100644 index 0000000000000000000000000000000000000000..0391f3d56631f7ad1c32781f16f5646b61041442 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/camera_motion_before_full_refinement.json @@ -0,0 +1,199 @@ +{ + "projection": "perspective", + "focal_pixels": 369.5, + "principal_point": [ + 640.0, + 480.0 + ], + "horizontal_fov_degrees": 120.00056028465872, + "epipole": [ + 640.1707869459426, + 514.7179664031315 + ], + "optimizer_success": true, + "optimizer_nfev": 80, + "optimizer_message": "`ftol` termination condition is satisfied.", + "radial_k1": -9.999999999999999e-09, + "frames": [ + 1, + 9, + 17, + 25, + 33, + 41, + 49, + 57, + 65, + 73, + 81, + 89, + 97, + 105, + 113, + 121, + 128 + ], + "displacement_fraction": [ + 0.0, + 0.06818098806171356, + 0.13756848658320978, + 0.19887779004285744, + 0.25710216938241126, + 0.32595859862392734, + 0.38831783381560425, + 0.45241538388437325, + 0.5153858724278887, + 0.5783015694155771, + 0.6416748670059164, + 0.705664010997911, + 0.7704617097985091, + 0.8336938907909718, + 0.8947980522821005, + 0.9527437058038144, + 1.0 + ], + "rotation_vectors": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.00224774362252664, + 0.005924292116590759, + 0.001036639327532078 + ], + [ + -0.0011910404166182208, + 0.010684564529596154, + 0.0055015986704040456 + ], + [ + 0.004378268063234446, + 0.010671183472230263, + 0.0058501298063352095 + ], + [ + 0.01763652280787367, + 0.008012416918407749, + -0.0014764642394765961 + ], + [ + 0.012672512318890548, + 0.008782486433992596, + -0.003629059321305456 + ], + [ + 0.0152720514738302, + 0.010479784293087971, + -0.0016160593426945268 + ], + [ + 0.012234677607333646, + 0.010968443417469399, + -0.0004865450450693065 + ], + [ + 0.002863457859054484, + 0.012750584445496068, + 0.0034520818165749713 + ], + [ + 0.005693100730550619, + 0.014236922755496246, + 0.005583381445346449 + ], + [ + 0.0032532869847308288, + 0.014568374829030799, + 0.000763004216584281 + ], + [ + 0.009724637560048151, + 0.018447415714245713, + -0.002456395269454268 + ], + [ + 0.01657872518228831, + 0.023394415244521433, + 0.0009939050380583082 + ], + [ + 0.012199589517405858, + 0.02533129144158546, + 0.002858604114990466 + ], + [ + 0.01297107627292627, + 0.024838152587685007, + 0.005258001294290399 + ], + [ + 0.009737385776637825, + 0.021337103777977807, + 0.004343760768897358 + ], + [ + 0.011789093761427921, + 0.020360958760674838, + 0.002602562131042079 + ] + ], + "track_ids": [ + 7, + 8, + 10, + 11, + 13, + 17, + 25, + 31, + 33, + 38, + 74, + 75, + 85, + 97, + 122, + 143, + 149, + 197, + 208, + 212, + 251, + 259, + 365, + 367, + 369 + ], + "depth_in_total_travel_units": [ + 2.2659399244805773, + 2.6948534930745938, + 3.652966722764379, + 2.68590390287027, + 2.018685034835302, + 3.3343229630915614, + 2.229130638539127, + 3.1888415072387786, + 2.569831635740863, + 1.700245339440428, + 2.6668575747314827, + 2.7319867441904115, + 1.523781585706796, + 1.7729100288731323, + 1.2970368078554588, + 1.7051438641295515, + 2.1266169413023075, + 3.0725443851634133, + 2.0936647017552112, + 2.8429376368547863, + 3.084397710654423, + 2.1887027902769978, + 2.0950281835696836, + 2.6968436912447915, + 2.2944323232075217 + ], + "median_reprojection_error_pixels": 0.9941164845058215, + "p90_reprojection_error_pixels": 2.901010989230633, + "limitation": "Metric scale and exact lens distortion are not uniquely constrained. Forward-camera bundle fit uses a 120-degree horizontal pinhole hypothesis and robust tracked landmarks." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/crops/lamp_base_0001.png b/astra_scene_test/Scene_04/reference/crops/lamp_base_0001.png new file mode 100644 index 0000000000000000000000000000000000000000..63174c3b3d81d4699bb1b0a6c7e9d66ac4236519 Binary files /dev/null and b/astra_scene_test/Scene_04/reference/crops/lamp_base_0001.png differ diff --git a/astra_scene_test/Scene_04/reference/crops/lamp_base_0128.png b/astra_scene_test/Scene_04/reference/crops/lamp_base_0128.png new file mode 100644 index 0000000000000000000000000000000000000000..42fb214cca4e4fe80208786c9c009551d38bc2fc Binary files /dev/null and b/astra_scene_test/Scene_04/reference/crops/lamp_base_0128.png differ diff --git a/astra_scene_test/Scene_04/reference/crops/white_suv_1.png b/astra_scene_test/Scene_04/reference/crops/white_suv_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7201bf8dff249393e4a7be5e0b567ab67783b9d7 Binary files /dev/null and b/astra_scene_test/Scene_04/reference/crops/white_suv_1.png differ diff --git a/astra_scene_test/Scene_04/reference/distant_landmarks_trial.json b/astra_scene_test/Scene_04/reference/distant_landmarks_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..226b6838093c9b24004cd257459efaf1d5efb591 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/distant_landmarks_trial.json @@ -0,0 +1,747 @@ +{ + "distant_headlight_left": { + "xyz": [ + -7.35898139198819, + 71.47809397744096, + -2.30058224529908 + ], + "observations": { + "1": [ + 576.0, + 539.0 + ], + "9": [ + 576.3914184570312, + 538.5623779296875 + ], + "17": [ + 576.2930297851562, + 540.298095703125 + ], + "25": [ + 574.3419189453125, + 538.5428466796875 + ], + "33": [ + 571.5774536132812, + 534.2451171875 + ], + "41": [ + 570.3427124023438, + 537.163330078125 + ], + "49": [ + 568.6837158203125, + 536.5470581054688 + ], + "57": [ + 566.7459716796875, + 538.6200561523438 + ], + "65": [ + 565.0204467773438, + 543.1527099609375 + ], + "73": [ + 562.9796752929688, + 542.625732421875 + ], + "81": [ + 560.7957763671875, + 545.0585327148438 + ], + "89": [ + 559.5230712890625, + 543.47705078125 + ], + "97": [ + 557.9629516601562, + 541.3145751953125 + ], + "105": [ + 555.3115844726562, + 544.5411376953125 + ], + "113": [ + 551.095703125, + 545.0950317382812 + ], + "121": [ + 545.5712890625, + 548.0756225585938 + ], + "128": [ + 541.04541015625, + 548.66552734375 + ] + }, + "projections": { + "1": [ + 576.1781533091383, + 538.384066864485 + ], + "9": [ + 576.627109797683, + 538.0595213730966 + ], + "17": [ + 576.2367831944781, + 539.7194911885322 + ], + "25": [ + 574.5039117719776, + 538.2300695526071 + ], + "33": [ + 572.2954656990248, + 534.3953705010945 + ], + "41": [ + 570.4621077843385, + 537.230656333056 + ], + "49": [ + 568.90216116284, + 536.8693196552056 + ], + "57": [ + 566.6786176467982, + 538.7812964299692 + ], + "65": [ + 564.5832198403297, + 542.8958445065423 + ], + "73": [ + 562.4908233701947, + 542.5685600948904 + ], + "81": [ + 560.1356406594068, + 544.8894908792836 + ], + "89": [ + 558.9702321861055, + 543.7391223295567 + ], + "97": [ + 557.5454984134551, + 541.9554011923948 + ], + "105": [ + 554.73848486789, + 544.6522637536269 + ], + "113": [ + 550.8956287036239, + 545.4158339185625 + ], + "121": [ + 545.9528428565499, + 548.0841769465898 + ], + "128": [ + 542.6135689019417, + 548.6258324634553 + ] + }, + "mean_error_pixels": 0.5608408373299512, + "max_error_pixels": 1.5686610644785268 + }, + "distant_headlight_center": { + "xyz": [ + -5.166640818071345, + 74.8906401160613, + -2.1968774933027273 + ], + "observations": { + "1": [ + 589.0, + 537.0 + ], + "9": [ + 589.7914428710938, + 536.532958984375 + ], + "17": [ + 590.1503295898438, + 538.204833984375 + ], + "25": [ + 588.5750732421875, + 536.4144287109375 + ], + "33": [ + 586.328857421875, + 531.9990844726562 + ], + "41": [ + 585.3447875976562, + 534.762451171875 + ], + "49": [ + 584.242431640625, + 534.061767578125 + ], + "57": [ + 582.8644409179688, + 536.0819091796875 + ], + "65": [ + 581.7530517578125, + 540.5639038085938 + ], + "73": [ + 580.3848876953125, + 539.9678955078125 + ], + "81": [ + 578.9522094726562, + 542.2881469726562 + ], + "89": [ + 578.4090576171875, + 540.4335327148438 + ], + "97": [ + 577.6299438476562, + 538.175537109375 + ], + "105": [ + 575.9978637695312, + 541.2666015625 + ], + "113": [ + 572.603515625, + 541.677490234375 + ], + "121": [ + 568.5126342773438, + 544.46630859375 + ], + "128": [ + 564.9859619140625, + 544.7620239257812 + ] + }, + "projections": { + "1": [ + 589.0745499526433, + 536.5223328732282 + ], + "9": [ + 589.9217780581538, + 536.145765404874 + ], + "17": [ + 589.9892908053785, + 537.7891385807108 + ], + "25": [ + 588.6760853490683, + 536.226604648976 + ], + "33": [ + 586.8664876526012, + 532.2035113566352 + ], + "41": [ + 585.5855683719942, + 534.8964741883642 + ], + "49": [ + 584.5462500062042, + 534.4652685205119 + ], + "57": [ + 582.9166928262507, + 536.2763793125133 + ], + "65": [ + 581.4683320985137, + 540.3303328247786 + ], + "73": [ + 580.0270506178514, + 539.9134904775809 + ], + "81": [ + 578.3861790710796, + 541.9999479135967 + ], + "89": [ + 577.9446083557641, + 540.6443846063985 + ], + "97": [ + 577.3265146263161, + 538.7700669907289 + ], + "105": [ + 575.4389596713587, + 541.315872036418 + ], + "113": [ + 572.565033533462, + 541.92482212263 + ], + "121": [ + 568.6615683105347, + 544.3393503976336 + ], + "128": [ + 566.1930968922251, + 544.6458110522539 + ] + }, + "mean_error_pixels": 0.4630006274600263, + "max_error_pixels": 1.2127160786750724 + }, + "distant_headlight_far": { + "xyz": [ + -1.3408501070145125, + 78.56457439028985, + -2.0631704148883756 + ], + "observations": { + "1": [ + 611.0, + 537.0 + ], + "9": [ + 612.0967407226562, + 536.3214111328125 + ], + "17": [ + 612.3280639648438, + 537.7880249023438 + ], + "25": [ + 610.8767700195312, + 535.6309204101562 + ], + "33": [ + 608.66259765625, + 530.7155151367188 + ], + "41": [ + 607.9232788085938, + 533.1858520507812 + ], + "49": [ + 607.29833984375, + 532.1372680664062 + ], + "57": [ + 606.4265747070312, + 533.8456420898438 + ], + "65": [ + 605.8465576171875, + 538.3228149414062 + ], + "73": [ + 605.0324096679688, + 537.3521728515625 + ], + "81": [ + 604.3375854492188, + 539.036865234375 + ], + "89": [ + 604.9450073242188, + 536.6976318359375 + ], + "97": [ + 605.3412475585938, + 534.255126953125 + ], + "105": [ + 604.6949462890625, + 537.1362915039062 + ], + "113": [ + 602.6989135742188, + 537.1969604492188 + ], + "121": [ + 599.618408203125, + 539.5076293945312 + ], + "128": [ + 597.2274169921875, + 539.5512084960938 + ] + }, + "projections": { + "1": [ + 608.649284510563, + 534.5721860024094 + ], + "9": [ + 610.0220195720026, + 534.1588460443838 + ], + "17": [ + 610.6871634643762, + 535.8249689646208 + ], + "25": [ + 609.9139727402145, + 534.1962833706403 + ], + "33": [ + 608.6143427261642, + 529.9344808742193 + ], + "41": [ + 608.0381956657178, + 532.4761732701647 + ], + "49": [ + 607.6555498570159, + 531.9976522721279 + ], + "57": [ + 606.7686371840673, + 533.7248721806347 + ], + "65": [ + 606.123149601523, + 537.7603912730182 + ], + "73": [ + 605.4805529272529, + 537.2834945637096 + ], + "81": [ + 604.7111200736371, + 539.1108934336125 + ], + "89": [ + 605.1465546605856, + 537.5422284041786 + ], + "97": [ + 605.4933674721179, + 535.627939224619 + ], + "105": [ + 604.6910736887733, + 538.0624296182407 + ], + "113": [ + 602.9406479538007, + 538.5612576829834 + ], + "121": [ + 600.2240350973775, + 540.7383089533148 + ], + "128": [ + 598.7413537811864, + 540.818020719653 + ] + }, + "mean_error_pixels": 1.3104749025037048, + "max_error_pixels": 3.3793703731873013 + }, + "right_next_lamp": { + "xyz": [ + 12.171372614418908, + 73.32698309400536, + 10.287755236624047 + ], + "observations": { + "1": [ + 676.0, + 474.0 + ], + "9": [ + 679.2293701171875, + 472.12579345703125 + ], + "17": [ + 682.2525634765625, + 472.6588439941406 + ], + "25": [ + 683.0426635742188, + 469.45867919921875 + ], + "33": [ + 682.70703125, + 462.5318908691406 + ], + "41": [ + 684.1465454101562, + 463.30303955078125 + ], + "49": [ + 685.89404296875, + 461.00775146484375 + ], + "57": [ + 687.2223510742188, + 461.00384521484375 + ], + "65": [ + 689.6056518554688, + 464.12689208984375 + ], + "73": [ + 691.504638671875, + 461.5000915527344 + ], + "81": [ + 692.9893798828125, + 460.7843322753906 + ], + "89": [ + 695.825927734375, + 455.9842224121094 + ], + "97": [ + 699.2017822265625, + 451.2179870605469 + ], + "105": [ + 701.9930419921875, + 451.5902099609375 + ], + "113": [ + 703.4578247070312, + 448.8763427734375 + ], + "121": [ + 703.9640502929688, + 447.9381408691406 + ], + "128": [ + 704.8994140625, + 445.02691650390625 + ] + }, + "projections": { + "1": [ + 676.0224894191014, + 474.02737419246273 + ], + "9": [ + 679.1781353090328, + 472.1714102568612 + ], + "17": [ + 681.9945972461533, + 472.48150543567465 + ], + "25": [ + 682.9442083264481, + 469.3815834121774 + ], + "33": [ + 682.8227497095027, + 463.1203522624157 + ], + "41": [ + 684.2332820645654, + 463.54789221300604 + ], + "49": [ + 686.0136830367533, + 461.4045970455758 + ], + "57": [ + 687.4168450830999, + 461.20927022375037 + ], + "65": [ + 689.3861719576702, + 463.43935763124813 + ], + "73": [ + 691.3280317398746, + 460.98869323690974 + ], + "81": [ + 692.7838191446288, + 460.04647916478797 + ], + "89": [ + 695.730084519892, + 455.7304789232318 + ], + "97": [ + 699.3704762523512, + 451.46702325538035 + ], + "105": [ + 701.8727778783019, + 451.235670100558 + ], + "113": [ + 703.557605756206, + 449.0679534841074 + ], + "121": [ + 704.0054552811224, + 448.18727634602976 + ], + "128": [ + 705.171273994033, + 445.54589256007654 + ] + }, + "mean_error_pixels": 0.36050395947295183, + "max_error_pixels": 0.7659519762417646 + }, + "right_smaller_lamp": { + "xyz": [ + 14.926147602214728, + 102.69188314678074, + 8.372225779138235 + ], + "observations": { + "1": [ + 669.0, + 492.0 + ], + "9": [ + 671.7989501953125, + 490.62890625 + ], + "17": [ + 674.040283203125, + 491.7751770019531 + ], + "25": [ + 674.4747924804688, + 488.9952087402344 + ], + "33": [ + 673.739013671875, + 482.8454284667969 + ], + "41": [ + 674.8565673828125, + 484.1058044433594 + ], + "49": [ + 676.0146484375, + 482.53460693359375 + ], + "57": [ + 676.7369995117188, + 483.2787170410156 + ], + "65": [ + 678.2216796875, + 487.1328430175781 + ], + "73": [ + 679.3516235351562, + 485.3456115722656 + ], + "81": [ + 680.3638916015625, + 485.5367431640625 + ], + "89": [ + 682.3519287109375, + 481.94512939453125 + ], + "97": [ + 684.6644897460938, + 478.17315673828125 + ], + "105": [ + 686.397705078125, + 479.7648010253906 + ], + "113": [ + 686.5369262695312, + 478.492919921875 + ], + "121": [ + 685.7559814453125, + 479.2514953613281 + ], + "128": [ + 685.3016967773438, + 477.7740478515625 + ] + }, + "projections": { + "1": [ + 668.6647911929929, + 491.1925476953942 + ], + "9": [ + 671.3847913989883, + 489.961102713132 + ], + "17": [ + 673.6723103506375, + 490.91915071319386 + ], + "25": [ + 674.1804256180307, + 488.4500461889449 + ], + "33": [ + 673.7502214875567, + 482.89457520061734 + ], + "41": [ + 674.6716851944223, + 484.1485370661582 + ], + "49": [ + 675.870941932223, + 482.7741415565 + ], + "57": [ + 676.669113161076, + 483.42357117846916 + ], + "65": [ + 677.9461709336048, + 486.5070199438311 + ], + "73": [ + 679.1498568706313, + 484.99940068527303 + ], + "81": [ + 679.9950965715367, + 485.1569300676617 + ], + "89": [ + 682.1854830805188, + 482.0424806759358 + ], + "97": [ + 684.7968891393401, + 479.01049228456293 + ], + "105": [ + 686.3041535846587, + 480.0642636283342 + ], + "113": [ + 686.9099158368404, + 479.2307364912546 + ], + "121": [ + 686.380228869699, + 479.74440379951676 + ], + "128": [ + 686.6820172779445, + 478.3672701688363 + ] + }, + "mean_error_pixels": 0.58727013196417, + "max_error_pixels": 1.5023972184779937 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/distant_tower_manual_fit.json b/astra_scene_test/Scene_04/reference/distant_tower_manual_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..07682a9930b521baeaf1d19f3f06b95b5d6fbaf3 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/distant_tower_manual_fit.json @@ -0,0 +1,108 @@ +{ + "right_distant_three_windows": { + "xyz": [ + 42.054236653617394, + 201.03314622787633, + 128.00903034368366 + ], + "observations": { + "1": [ + 690, + 322 + ], + "33": [ + 692, + 310 + ], + "65": [ + 698, + 307 + ], + "97": [ + 700, + 293 + ], + "128": [ + 703, + 289 + ] + }, + "projections": { + "1": [ + 687.8528495382926, + 322.4624381631527 + ], + "33": [ + 692.6227214435469, + 307.919831096012 + ], + "65": [ + 696.6294385146346, + 307.73324751705763 + ], + "97": [ + 702.4715206138236, + 293.63410032091844 + ], + "128": [ + 703.2629285913622, + 288.89546459478254 + ] + }, + "mean_error_pixels": 1.7513311118527632, + "max_error_pixels": 2.5515676282520605 + }, + "right_distant_roof_peak": { + "xyz": [ + 52.87306630732767, + 179.64554780307964, + 143.07199345074264 + ], + "observations": { + "1": [ + 712, + 276 + ], + "33": [ + 723, + 259 + ], + "65": [ + 725, + 251 + ], + "97": [ + 734, + 235 + ], + "128": [ + 736, + 234 + ] + }, + "projections": { + "1": [ + 714.462848021547, + 275.43292750048204 + ], + "33": [ + 720.7678317689918, + 257.43279823625767 + ], + "65": [ + 725.9659618006617, + 255.47420538917297 + ], + "97": [ + 733.5380062224683, + 237.3237358548055 + ], + "128": [ + 735.5761858335604, + 230.20613939099283 + ] + }, + "mean_error_pixels": 3.2037307998570226, + "max_error_pixels": 4.577291345855323 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/facade_fit_trial.json b/astra_scene_test/Scene_04/reference/facade_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..4404aa8f9dfbbe0d06c58e4d29caad80e13354aa --- /dev/null +++ b/astra_scene_test/Scene_04/reference/facade_fit_trial.json @@ -0,0 +1,836 @@ +{ + "left_facade_14": { + "xyz": [ + -27.841737771742938, + 35.67640819611144, + 25.729445669601038 + ], + "observations": { + "1": [ + 339.0, + 298.0 + ], + "9": [ + 325.3019714355469, + 285.2109069824219 + ], + "17": [ + 310.9707336425781, + 271.56524658203125 + ], + "25": [ + 291.9886169433594, + 255.42161560058594 + ], + "33": [ + 266.9227294921875, + 233.75038146972656 + ], + "41": [ + 243.4900360107422, + 216.98995971679688 + ], + "49": [ + 220.01515197753906, + 195.30117797851562 + ], + "57": [ + 194.19801330566406, + 174.2055206298828 + ], + "65": [ + 168.88783264160156, + 152.01251220703125 + ] + }, + "projections": { + "1": [ + 339.0453271621427, + 292.95271618285994 + ], + "9": [ + 326.4840645319435, + 280.65322157175285 + ], + "17": [ + 313.04871304559526, + 268.33183065186836 + ], + "25": [ + 294.33073812763735, + 252.25113683660683 + ], + "33": [ + 268.7013769566705, + 233.3727052057902 + ], + "41": [ + 244.5599030307057, + 218.41418449324584 + ], + "49": [ + 219.50109630441852, + 197.422414064819 + ], + "57": [ + 190.65739042479817, + 176.38841151766712 + ], + "65": [ + 163.05184640447305, + 155.88217940144455 + ] + }, + "mean_error_pixels": 3.8317050393116556, + "max_error_pixels": 7.002360998654086 + }, + "left_facade_153": { + "xyz": [ + -27.765714236818646, + 35.50914466240909, + 27.447082725983854 + ], + "observations": { + "1": [ + 341.0, + 277.0 + ], + "9": [ + 327.4598083496094, + 264.03271484375 + ], + "17": [ + 313.51519775390625, + 250.0844268798828 + ], + "25": [ + 294.87237548828125, + 233.72967529296875 + ], + "33": [ + 270.6798400878906, + 214.92369079589844 + ], + "41": [ + 247.50389099121094, + 197.57298278808594 + ], + "49": [ + 221.70530700683594, + 170.7520294189453 + ], + "57": [ + 196.13729858398438, + 149.1046600341797 + ] + }, + "projections": { + "1": [ + 340.740066253516, + 276.3314043474132 + ], + "9": [ + 328.28513967917087, + 263.216247469357 + ], + "17": [ + 315.1231067946607, + 250.0889749447151 + ], + "25": [ + 296.4862952679571, + 232.94663549140898 + ], + "33": [ + 270.6391663973792, + 212.64392083297912 + ], + "41": [ + 246.753647230215, + 196.49426102238033 + ], + "49": [ + 221.89127097185104, + 174.0739425866572 + ], + "57": [ + 193.4241195040064, + 151.56841676272478 + ] + }, + "mean_error_pixels": 1.9832698303271576, + "max_error_pixels": 3.6648926229128946 + }, + "left_facade_224": { + "xyz": [ + -31.570615544142473, + 33.28003008865258, + 19.73378803209728 + ], + "observations": { + "1": [ + 276.0, + 338.0 + ], + "9": [ + 253.74559020996094, + 324.8104248046875 + ], + "17": [ + 230.70022583007812, + 311.0643310546875 + ], + "25": [ + 201.8429718017578, + 295.34918212890625 + ], + "33": [ + 167.5502471923828, + 278.9259033203125 + ], + "41": [ + 132.8997802734375, + 265.0664367675781 + ], + "49": [ + 96.36368560791016, + 244.71031188964844 + ], + "57": [ + 56.329593658447266, + 225.25418090820312 + ], + "65": [ + 15.354358673095703, + 202.4650115966797 + ] + }, + "projections": { + "1": [ + 268.1291348796826, + 334.17594454572907 + ], + "9": [ + 250.3656086381767, + 323.0001090126973 + ], + "17": [ + 230.5039574399437, + 311.3053963791516 + ], + "25": [ + 204.65238253992663, + 296.4194358035473 + ], + "33": [ + 170.85494320594887, + 279.82958625234284 + ], + "41": [ + 135.91518990950118, + 265.8186493454182 + ], + "49": [ + 99.1025155894688, + 245.50945011628025 + ], + "57": [ + 55.408418860947336, + 224.52257862558702 + ], + "65": [ + 10.69668363813662, + 202.6136727353612 + ] + }, + "mean_error_pixels": 3.45837894262643, + "max_error_pixels": 8.750652424794847 + }, + "left_facade_241": { + "xyz": [ + -31.44149494360135, + 33.638833037939385, + 22.255200243194068 + ], + "observations": { + "1": [ + 284.0, + 313.0 + ], + "9": [ + 263.9388427734375, + 300.0116882324219 + ], + "17": [ + 242.37022399902344, + 285.4534912109375 + ], + "25": [ + 215.34542846679688, + 268.5561218261719 + ], + "33": [ + 181.9423828125, + 250.8788299560547 + ], + "41": [ + 149.00613403320312, + 235.52708435058594 + ], + "49": [ + 114.51394653320312, + 213.73080444335938 + ], + "57": [ + 76.95865631103516, + 192.48257446289062 + ], + "65": [ + 38.58384704589844, + 169.10540771484375 + ] + }, + "projections": { + "1": [ + 277.3717944095918, + 311.0815119926122 + ], + "9": [ + 260.5116555174992, + 298.8910123047399 + ], + "17": [ + 241.9661695400901, + 286.2217930904136 + ], + "25": [ + 217.29355403607735, + 270.0372160984918 + ], + "33": [ + 184.48158329960825, + 251.6516021413199 + ], + "41": [ + 151.65330968797008, + 236.17857045799863 + ], + "49": [ + 117.0458780869402, + 214.239118400549 + ], + "57": [ + 76.41631774005464, + 191.61608462325802 + ], + "65": [ + 35.74889184220069, + 168.45344454732725 + ] + }, + "mean_error_pixels": 2.85725498230652, + "max_error_pixels": 6.900268515297728 + }, + "left_facade_305": { + "xyz": [ + -30.592248948539975, + 36.647279637031595, + 30.058859796798895 + ], + "observations": { + "1": [ + 327.0, + 264.0 + ], + "9": [ + 312.4950256347656, + 249.66595458984375 + ], + "17": [ + 297.2674865722656, + 234.2603302001953 + ], + "25": [ + 277.5322265625, + 216.31361389160156 + ], + "33": [ + 251.56813049316406, + 196.63795471191406 + ], + "41": [ + 227.4759063720703, + 179.9375762939453 + ], + "49": [ + 202.86875915527344, + 156.96546936035156 + ], + "57": [ + 169.35287475585938, + 128.9058837890625 + ], + "65": [ + 142.3964385986328, + 100.19003295898438 + ], + "73": [ + 111.16728973388672, + 71.78462219238281 + ], + "81": [ + 72.69959259033203, + 44.798561096191406 + ], + "89": [ + 34.01161193847656, + 16.70145606994629 + ] + }, + "projections": { + "1": [ + 323.30158230641223, + 259.9916277325066 + ], + "9": [ + 310.6749837738255, + 246.58508927938266 + ], + "17": [ + 297.5595565131833, + 233.21987312077414 + ], + "25": [ + 278.59084562713906, + 215.6587255191169 + ], + "33": [ + 251.9801044754883, + 194.82168913220323 + ], + "41": [ + 228.1848794076701, + 178.6249001645445 + ], + "49": [ + 203.42375852198853, + 156.01942481854445 + ], + "57": [ + 175.4124620849695, + 133.6133389659086 + ], + "65": [ + 149.6365974280116, + 112.36383542715566 + ], + "73": [ + 112.72730008535723, + 79.17904147705588 + ], + "81": [ + 68.30440821508762, + 48.17104182439488 + ], + "89": [ + 14.23914042149147, + 4.381618287372191 + ] + }, + "mean_error_pixels": 6.169990335963384, + "max_error_pixels": 23.296545513850912 + }, + "left_facade_312": { + "xyz": [ + -30.527604568430462, + 33.141117839074326, + 18.70811704208037 + ], + "observations": { + "1": [ + 283.0, + 349.0 + ], + "9": [ + 261.8179016113281, + 336.4425354003906 + ], + "17": [ + 239.73118591308594, + 323.41522216796875 + ], + "25": [ + 212.07040405273438, + 308.0528259277344 + ], + "33": [ + 178.0619354248047, + 291.7078552246094 + ], + "41": [ + 144.02078247070312, + 278.1570739746094 + ], + "49": [ + 108.67957305908203, + 258.6923828125 + ], + "57": [ + 69.52809143066406, + 239.5958709716797 + ], + "65": [ + 28.405357360839844, + 217.02249145507812 + ] + }, + "projections": { + "1": [ + 277.2750392895312, + 344.32791520858075 + ], + "9": [ + 259.8073451640589, + 333.6254668459767 + ], + "17": [ + 240.15375833367153, + 322.4553379258646 + ], + "25": [ + 214.7748893790469, + 308.2045993173465 + ], + "33": [ + 181.8030127670163, + 292.3653527982127 + ], + "41": [ + 147.2795721856274, + 279.0631345000596 + ], + "49": [ + 110.94551412337762, + 259.6064643439719 + ], + "57": [ + 67.64231388704832, + 239.52166552250912 + ], + "65": [ + 22.948371202699946, + 218.44344695932625 + ] + }, + "mean_error_pixels": 3.5286974783429432, + "max_error_pixels": 7.3894215899908176 + }, + "left_facade_319": { + "xyz": [ + -29.723042735599773, + 33.64367293331828, + 18.72088045181945 + ], + "observations": { + "1": [ + 296.0, + 349.0 + ], + "9": [ + 276.9952697753906, + 337.7050476074219 + ], + "17": [ + 256.49615478515625, + 325.91326904296875 + ], + "25": [ + 230.60231018066406, + 311.52423095703125 + ], + "33": [ + 199.1722869873047, + 296.6095886230469 + ], + "41": [ + 167.6732940673828, + 284.0908508300781 + ], + "49": [ + 134.66851806640625, + 265.90399169921875 + ], + "57": [ + 97.01226806640625, + 247.835693359375 + ], + "65": [ + 55.401878356933594, + 225.79563903808594 + ] + }, + "projections": { + "1": [ + 291.5784082524537, + 347.3235634839619 + ], + "9": [ + 275.16378633200344, + 336.94066807577553 + ], + "17": [ + 256.7123383099238, + 326.2043141263571 + ], + "25": [ + 232.84383892870068, + 312.4220588620525 + ], + "33": [ + 201.84097287018614, + 297.04075944247086 + ], + "41": [ + 169.52438699787672, + 284.3789794029889 + ], + "49": [ + 135.6674098788488, + 265.76162129712304 + ], + "57": [ + 95.4252503371813, + 246.71441349403867 + ], + "65": [ + 54.074452203000874, + 226.89856672724514 + ] + }, + "mean_error_pixels": 2.082799112585224, + "max_error_pixels": 4.728732702773075 + }, + "left_facade_342": { + "xyz": [ + -34.10466754191314, + 30.863052139389854, + 23.13512144622352 + ], + "observations": { + "1": [ + 239.0, + 288.0 + ], + "9": [ + 201.36651611328125, + 267.8841552734375 + ], + "17": [ + 166.82066345214844, + 245.6532745361328 + ], + "25": [ + 131.54998779296875, + 224.5261688232422 + ], + "33": [ + 90.62067413330078, + 204.49151611328125 + ], + "41": [ + 49.760948181152344, + 184.81028747558594 + ], + "49": [ + 8.783300399780273, + 160.7051239013672 + ] + }, + "projections": { + "1": [ + 216.34434067688443, + 280.680549139267 + ], + "9": [ + 194.36138891663848, + 265.71962358595465 + ], + "17": [ + 170.14188402300903, + 249.64564393243964 + ], + "25": [ + 137.86634544735045, + 229.41347015830397 + ], + "33": [ + 94.51444352302121, + 206.31498219207833 + ], + "41": [ + 50.59093946816802, + 185.2831865707675 + ], + "49": [ + 3.0399512543182254, + 156.28266071128957 + ] + }, + "mean_error_pixels": 8.117681899043443, + "max_error_pixels": 23.808680355445127 + }, + "left_facade_344": { + "xyz": [ + -31.72924947881222, + 31.06999242053081, + 19.746530468009468 + ], + "observations": { + "1": [ + 255.0, + 329.0 + ], + "9": [ + 223.57339477539062, + 307.66650390625 + ], + "17": [ + 195.84442138671875, + 291.0091857910156 + ], + "25": [ + 163.46841430664062, + 273.5496520996094 + ], + "33": [ + 124.53631591796875, + 255.82064819335938 + ], + "41": [ + 85.69303894042969, + 239.45362854003906 + ], + "49": [ + 44.76420974731445, + 216.94949340820312 + ] + }, + "projections": { + "1": [ + 242.02744493145025, + 319.851288166358 + ], + "9": [ + 221.26912791222895, + 307.0752395212239 + ], + "17": [ + 197.9634481754191, + 293.39128705808946 + ], + "25": [ + 167.70200216946364, + 276.1985058248713 + ], + "33": [ + 127.9278687884364, + 256.93863555618805 + ], + "41": [ + 86.09934038184838, + 239.54512289365644 + ], + "49": [ + 41.19881268005781, + 215.00719252417503 + ] + }, + "mean_error_pixels": 4.926118126606634, + "max_error_pixels": 15.874070467954233 + }, + "left_facade_379": { + "xyz": [ + -29.24601216344421, + 36.23762871295906, + 27.167920134256118 + ], + "observations": { + "1": [ + 330.0, + 288.0 + ], + "9": [ + 316.2759094238281, + 275.235107421875 + ], + "17": [ + 301.9510803222656, + 261.59161376953125 + ], + "25": [ + 282.84442138671875, + 245.40257263183594 + ], + "33": [ + 258.141845703125, + 223.41976928710938 + ], + "41": [ + 234.61839294433594, + 204.76332092285156 + ], + "49": [ + 211.16368103027344, + 182.8395538330078 + ], + "57": [ + 184.9812469482422, + 162.45997619628906 + ], + "65": [ + 160.3619384765625, + 140.65269470214844 + ] + }, + "projections": { + "1": [ + 330.3787393344109, + 283.09523641166277 + ], + "9": [ + 317.73790149107435, + 270.5490041031779 + ], + "17": [ + 304.3471161536146, + 257.9995607828801 + ], + "25": [ + 285.4695171267439, + 241.58490960064188 + ], + "33": [ + 259.438042437187, + 222.28661562887817 + ], + "41": [ + 235.36292152274842, + 207.14366912284402 + ], + "49": [ + 210.3770350518509, + 185.87894413583052 + ], + "57": [ + 181.80414904906553, + 164.6955771965175 + ], + "65": [ + 154.84175728506455, + 144.26231416719304 + ] + }, + "mean_error_pixels": 4.068320348839915, + "max_error_pixels": 6.595585877645492 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/fitted_landmarks.json b/astra_scene_test/Scene_04/reference/fitted_landmarks.json new file mode 100644 index 0000000000000000000000000000000000000000..e2d1c07bfac8e649c318f9748cc2fd27db63e8c2 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/fitted_landmarks.json @@ -0,0 +1,90 @@ +{ + "13": { + "uv": [ + 743, + 512 + ], + "xyz": [ + 17.37393523585375, + 53.567757141225, + 4.631902342837701 + ] + }, + "38": { + "uv": [ + 530, + 537 + ], + "xyz": [ + -9.634018010605121, + 47.197441578486384, + 1.5499998975911986 + ] + }, + "74": { + "uv": [ + 467, + 254 + ], + "xyz": [ + -28.173189393291253, + 54.00520465751647, + 47.88955448187207 + ] + }, + "75": { + "uv": [ + 469, + 235 + ], + "xyz": [ + -28.421819787604488, + 53.474482667145224, + 51.92285183605145 + ] + }, + "85": { + "uv": [ + 694, + 471 + ], + "xyz": [ + 8.588897163826156, + 42.010381452298596, + 8.179405923697072 + ] + }, + "122": { + "uv": [ + 532, + 471 + ], + "xyz": [ + -7.195057597598322, + 32.92574811947026, + 6.613366047012177 + ] + }, + "143": { + "uv": [ + 541, + 443 + ], + "xyz": [ + -9.287145200361358, + 45.55504892875169, + 11.863891384798915 + ] + }, + "251": { + "uv": [ + 335, + 200 + ], + "xyz": [ + -62.29205400794394, + 60.96458058533097, + 67.20808444619212 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/foreground_fit_trial.json b/astra_scene_test/Scene_04/reference/foreground_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..5c2db1f1802f2f09f8c199d84c5cc6adb4d0f65a --- /dev/null +++ b/astra_scene_test/Scene_04/reference/foreground_fit_trial.json @@ -0,0 +1,3344 @@ +{ + "feature_3": { + "xyz": [ + -28.42898301284557, + 28.064415464663377, + 7.547854162155794 + ], + "observations": { + "1": [ + 226.0, + 454.0 + ], + "9": [ + 195.74916076660156, + 447.7850341796875 + ], + "17": [ + 162.09800720214844, + 440.61090087890625 + ], + "25": [ + 121.47499084472656, + 432.4641418457031 + ], + "33": [ + 74.41999053955078, + 425.552734375 + ], + "41": [ + 25.392189025878906, + 420.1846008300781 + ] + }, + "projections": { + "1": [ + 221.7034857707764, + 453.1204104802208 + ], + "9": [ + 195.23799319028439, + 447.36763689049087 + ], + "17": [ + 163.0828139337096, + 441.20105305850615 + ], + "25": [ + 124.85723620951103, + 433.1182690200945 + ], + "33": [ + 78.17470969806197, + 425.57901327307917 + ], + "41": [ + 19.37099321811195, + 420.0562179831773 + ] + }, + "mean_error_pixels": 3.235991610094494, + "max_error_pixels": 6.022564330154452 + }, + "feature_6": { + "xyz": [ + -28.270099904412454, + 29.409077304510205, + 7.573533704614242 + ], + "observations": { + "1": [ + 248.0, + 457.0 + ], + "9": [ + 220.708740234375, + 451.4850158691406 + ], + "17": [ + 189.9171600341797, + 445.33135986328125 + ], + "25": [ + 152.75050354003906, + 438.07720947265625 + ], + "33": [ + 109.20884704589844, + 431.79913330078125 + ], + "41": [ + 63.273780822753906, + 427.6788024902344 + ], + "49": [ + 13.405906677246094, + 417.75860595703125 + ] + }, + "projections": { + "1": [ + 242.59446840339854, + 456.7892006206604 + ], + "9": [ + 218.94296015379535, + 451.5265722833699 + ], + "17": [ + 190.314806286394, + 446.0689989428267 + ], + "25": [ + 156.38828741702582, + 438.705872658958 + ], + "33": [ + 115.26692449689688, + 431.7867210019635 + ], + "41": [ + 64.16432634590763, + 427.37392866044206 + ], + "49": [ + 9.354565277066172, + 417.1464638560527 + ] + }, + "mean_error_pixels": 3.2574730739786912, + "max_error_pixels": 6.058090166666199 + }, + "feature_26": { + "xyz": [ + -28.38895353015113, + 33.60133750904572, + 4.333286477041266 + ], + "observations": { + "1": [ + 289.0, + 505.0 + ], + "9": [ + 268.3883056640625, + 503.0553894042969 + ], + "17": [ + 244.72183227539062, + 501.2847595214844 + ], + "25": [ + 216.33132934570312, + 498.29034423828125 + ], + "33": [ + 183.10202026367188, + 496.08831787109375 + ], + "41": [ + 147.6532745361328, + 497.2365417480469 + ], + "49": [ + 108.5990219116211, + 493.5293273925781 + ], + "57": [ + 64.94026947021484, + 492.5971374511719 + ], + "65": [ + 16.967979431152344, + 489.5366516113281 + ] + }, + "projections": { + "1": [ + 283.4939001328153, + 504.2261124799064 + ], + "9": [ + 265.39940156321677, + 502.19901755982323 + ], + "17": [ + 243.22532301403214, + 500.7760456285473 + ], + "25": [ + 217.6891330867457, + 497.65574123263025 + ], + "33": [ + 188.07210965834958, + 495.1480092546119 + ], + "41": [ + 150.9204966505415, + 496.9298979099913 + ], + "49": [ + 112.71822488246573, + 493.8009371526624 + ], + "57": [ + 64.83350283618438, + 492.863613331027 + ], + "65": [ + 10.234870611421911, + 492.0630712901377 + ] + }, + "mean_error_pixels": 3.521702970855282, + "max_error_pixels": 7.19149155403857 + }, + "feature_29": { + "xyz": [ + -27.361455982255585, + 29.98657925638708, + 6.695791682860995 + ], + "observations": { + "1": [ + 265.0, + 471.0 + ], + "9": [ + 239.87950134277344, + 466.3599548339844 + ], + "17": [ + 211.3734130859375, + 461.1990966796875 + ], + "25": [ + 176.6651611328125, + 454.6192321777344 + ], + "33": [ + 136.9361572265625, + 449.13055419921875 + ], + "41": [ + 94.71565246582031, + 446.0708312988281 + ], + "49": [ + 48.40856170654297, + 437.5699157714844 + ] + }, + "projections": { + "1": [ + 260.72660932587524, + 469.67781907573345 + ], + "9": [ + 238.6514042438463, + 465.33072698196315 + ], + "17": [ + 211.82154392685032, + 461.0322601725424 + ], + "25": [ + 180.27720064806118, + 454.88569477281413 + ], + "33": [ + 142.43980404195207, + 449.2138362510363 + ], + "41": [ + 95.11096926698883, + 446.5659873016639 + ], + "49": [ + 44.71369353880266, + 438.42214779847177 + ] + }, + "mean_error_pixels": 2.872200674425787, + "max_error_pixels": 5.504276897895192 + }, + "feature_30": { + "xyz": [ + -27.519512504221762, + 31.36495338035819, + 6.650725445009571 + ], + "observations": { + "1": [ + 280.0, + 474.0 + ], + "9": [ + 256.5245361328125, + 469.9356689453125 + ], + "17": [ + 230.77224731445312, + 465.5762023925781 + ], + "25": [ + 199.4508514404297, + 460.00372314453125 + ], + "33": [ + 162.55572509765625, + 454.9518127441406 + ], + "41": [ + 123.54429626464844, + 452.730224609375 + ], + "49": [ + 80.6122817993164, + 445.0611572265625 + ], + "57": [ + 32.60860824584961, + 439.4831237792969 + ] + }, + "projections": { + "1": [ + 274.66577108914765, + 473.26697275735216 + ], + "9": [ + 254.6196809871755, + 469.30340364350013 + ], + "17": [ + 230.3093560113636, + 465.5410093751544 + ], + "25": [ + 201.7822247696456, + 459.92824591202367 + ], + "33": [ + 167.78495554625516, + 454.7085751805987 + ], + "41": [ + 125.73505241290547, + 452.8430838587366 + ], + "49": [ + 81.55106408693791, + 445.73404104824107 + ], + "57": [ + 25.854146141349133, + 439.8228361152445 + ] + }, + "mean_error_pixels": 3.1918497977369715, + "max_error_pixels": 6.76299954105632 + }, + "feature_35": { + "xyz": [ + -29.050615878877277, + 28.406457700586518, + 6.259209989160345 + ], + "observations": { + "1": [ + 218.0, + 471.0 + ], + "9": [ + 188.30174255371094, + 466.5295715332031 + ], + "17": [ + 154.3130645751953, + 461.3646545410156 + ], + "25": [ + 113.12709045410156, + 455.3734436035156 + ], + "33": [ + 65.7053451538086, + 451.0679931640625 + ], + "41": [ + 15.152017593383789, + 448.48333740234375 + ] + }, + "projections": { + "1": [ + 214.39391996855267, + 471.40166745214844 + ], + "9": [ + 187.6454147102117, + 466.9099210299172 + ], + "17": [ + 154.94544570401933, + 462.21230275901684 + ], + "25": [ + 116.43153604773204, + 455.82605900348045 + ], + "33": [ + 69.90629493338042, + 450.38426668875763 + ], + "41": [ + 10.511074322978857, + 447.50035506550535 + ] + }, + "mean_error_pixels": 2.9633219764070855, + "max_error_pixels": 4.743902266452495 + }, + "feature_36": { + "xyz": [ + -27.501778858655612, + 33.809929348809014, + 5.275832449028375 + ], + "observations": { + "1": [ + 303.0, + 494.0 + ], + "9": [ + 283.663330078125, + 491.3922119140625 + ], + "17": [ + 261.5282897949219, + 488.9944152832031 + ], + "25": [ + 234.68563842773438, + 485.31610107421875 + ], + "33": [ + 203.22410583496094, + 482.0315246582031 + ], + "41": [ + 170.0732421875, + 482.49713134765625 + ], + "49": [ + 133.45278930664062, + 478.05584716796875 + ], + "57": [ + 92.36991882324219, + 476.4386901855469 + ], + "65": [ + 47.130985260009766, + 472.87255859375 + ] + }, + "projections": { + "1": [ + 298.0590366928667, + 493.66296420625923 + ], + "9": [ + 280.9790598945323, + 491.0739326548035 + ], + "17": [ + 260.1476452630812, + 489.0465773620228 + ], + "25": [ + 235.98274045539972, + 485.2108324492145 + ], + "33": [ + 207.77027437081614, + 481.74513991704305 + ], + "41": [ + 172.8777786042565, + 482.4129255209845 + ], + "49": [ + 137.01291557073938, + 478.2301824024911 + ], + "57": [ + 92.29576024968014, + 475.97130474496066 + ], + "65": [ + 41.73027155651903, + 473.76086670379686 + ] + }, + "mean_error_pixels": 3.0233777920321296, + "max_error_pixels": 5.473280534144734 + }, + "feature_43": { + "xyz": [ + -27.030905426806914, + 32.0729148065258, + 6.591845991065577 + ], + "observations": { + "1": [ + 295.0, + 475.0 + ], + "9": [ + 273.900634765625, + 471.2981872558594 + ], + "17": [ + 249.31129455566406, + 467.5448303222656 + ], + "25": [ + 219.30528259277344, + 462.51947021484375 + ], + "33": [ + 183.8660125732422, + 458.0122985839844 + ], + "41": [ + 146.3266143798828, + 456.68389892578125 + ], + "49": [ + 104.8652572631836, + 450.1318359375 + ], + "57": [ + 58.7099609375, + 445.6868896484375 + ], + "65": [ + 8.685697555541992, + 438.9015808105469 + ] + }, + "projections": { + "1": [ + 288.5888173739528, + 475.4896428755236 + ], + "9": [ + 269.89274401665375, + 471.7413940689869 + ], + "17": [ + 247.225281126328, + 468.29819334849 + ], + "25": [ + 220.65160589051408, + 462.9731662085603 + ], + "33": [ + 189.09669497679795, + 457.9342803756627 + ], + "41": [ + 150.25753596693602, + 456.4510746159221 + ], + "49": [ + 109.7080259868601, + 449.87896227987494 + ], + "57": [ + 58.860021338244565, + 444.647640404775 + ], + "65": [ + 0.9443191780823099, + 438.8417282313429 + ] + }, + "mean_error_pixels": 4.1012055857140854, + "max_error_pixels": 7.741609749544191 + }, + "feature_44": { + "xyz": [ + -25.204720735399285, + 33.11039071795426, + 6.723501209621738 + ], + "observations": { + "1": [ + 323.0, + 476.0 + ], + "9": [ + 305.7037353515625, + 472.4814758300781 + ], + "17": [ + 286.3162536621094, + 469.2692565917969 + ], + "25": [ + 262.3711242675781, + 464.3651428222656 + ], + "33": [ + 234.14956665039062, + 459.4612731933594 + ], + "41": [ + 204.76431274414062, + 458.4093322753906 + ] + }, + "projections": { + "1": [ + 321.8409485978341, + 476.19572142359647 + ], + "9": [ + 305.7367576832458, + 472.62074092590507 + ], + "17": [ + 286.21342251162406, + 469.52495071074463 + ], + "25": [ + 263.289105256742, + 464.4273863080243 + ], + "33": [ + 236.15655394830412, + 459.3049064306774 + ], + "41": [ + 203.09504653903906, + 458.03818313191834 + ] + }, + "mean_error_pixels": 1.0395619827508022, + "max_error_pixels": 2.0130694420356576 + }, + "feature_47": { + "xyz": [ + -23.685556428302345, + 32.665634207592795, + 6.208479052686964 + ], + "observations": { + "1": [ + 336.0, + 484.0 + ], + "9": [ + 320.4140930175781, + 480.82598876953125 + ], + "17": [ + 302.5777893066406, + 477.9319763183594 + ], + "25": [ + 280.2257995605469, + 473.3547668457031 + ], + "33": [ + 253.19427490234375, + 468.50634765625 + ], + "41": [ + 219.51609802246094, + 466.4613342285156 + ], + "49": [ + 178.99612426757812, + 458.361083984375 + ], + "57": [ + 137.1529998779297, + 453.1900634765625 + ], + "65": [ + 96.74473571777344, + 447.68719482421875 + ], + "73": [ + 51.27796936035156, + 439.90325927734375 + ] + }, + "projections": { + "1": [ + 335.8218621745102, + 481.5269928755218 + ], + "9": [ + 320.1812118698498, + 478.2436753418118 + ], + "17": [ + 301.1252722306744, + 475.5327184846515 + ], + "25": [ + 278.8256903625101, + 470.78393751003523 + ], + "33": [ + 252.52552958377316, + 465.932109319296 + ], + "41": [ + 220.15617305280426, + 465.1035674925323 + ], + "49": [ + 186.56292206232803, + 459.53765526987536 + ], + "57": [ + 144.61344180688508, + 455.5031811849375 + ], + "65": [ + 96.98291677969803, + 451.38423765455644 + ], + "73": [ + 38.04197043977058, + 442.21839042077966 + ] + }, + "mean_error_pixels": 4.757520035156363, + "max_error_pixels": 13.436945323879522 + }, + "feature_56": { + "xyz": [ + -27.59038211335012, + 33.88707235750728, + 4.20600955435572 + ], + "observations": { + "1": [ + 301.0, + 507.0 + ], + "9": [ + 281.3951416015625, + 505.0876770019531 + ], + "17": [ + 259.046142578125, + 503.50933837890625 + ], + "25": [ + 232.0009002685547, + 500.6426696777344 + ], + "33": [ + 200.4164276123047, + 498.3479309082031 + ], + "41": [ + 166.93258666992188, + 499.6916198730469 + ], + "49": [ + 129.910400390625, + 496.2977294921875 + ], + "57": [ + 88.43473815917969, + 495.41119384765625 + ], + "65": [ + 42.76484298706055, + 492.9220275878906 + ] + }, + "projections": { + "1": [ + 295.9840733125285, + 506.0365800041416 + ], + "9": [ + 278.74967682741675, + 504.13691415935256 + ], + "17": [ + 257.6024412563933, + 502.91305282747715 + ], + "25": [ + 233.27817027242907, + 499.9421204114154 + ], + "33": [ + 205.14581796607985, + 497.4694465254289 + ], + "41": [ + 169.85948232364694, + 499.4154637358708 + ], + "49": [ + 133.65904786206693, + 496.53810201562135 + ], + "57": [ + 88.3602372271896, + 495.8939733451878 + ], + "65": [ + 36.790519479797695, + 495.5156832079161 + ] + }, + "mean_error_pixels": 3.271729136895116, + "max_error_pixels": 6.513032384743926 + }, + "feature_57": { + "xyz": [ + -27.677496390949248, + 28.004568248457918, + 6.137844974549646 + ], + "observations": { + "1": [ + 231.0, + 472.0 + ], + "9": [ + 202.34840393066406, + 467.611572265625 + ], + "17": [ + 169.6800537109375, + 462.5581970214844 + ], + "25": [ + 130.60629272460938, + 456.6868896484375 + ], + "33": [ + 85.15667724609375, + 452.3750305175781 + ], + "41": [ + 29.37567138671875, + 448.5159912109375 + ] + }, + "projections": { + "1": [ + 228.67156744409675, + 472.31107892721104 + ], + "9": [ + 202.38310513483952, + 467.85180214158584 + ], + "17": [ + 170.1736526209648, + 463.2356673560925 + ], + "25": [ + 132.27529646058395, + 456.87056289434275 + ], + "33": [ + 86.49119453405422, + 451.32211813862517 + ], + "41": [ + 27.679353318630547, + 448.3839459844668 + ] + }, + "mean_error_pixels": 1.4184100501579266, + "max_error_pixels": 2.3491207432452055 + }, + "feature_124": { + "xyz": [ + -28.640813424935974, + 27.36960155125657, + 6.847910645597362 + ], + "observations": { + "1": [ + 208.0, + 461.0 + ], + "9": [ + 177.04502868652344, + 455.6080017089844 + ], + "17": [ + 142.32472229003906, + 448.8991394042969 + ], + "25": [ + 100.62256622314453, + 441.3460388183594 + ], + "33": [ + 52.582393646240234, + 435.1123962402344 + ] + }, + "projections": { + "1": [ + 206.3374836542926, + 460.61027677675565 + ], + "9": [ + 177.86416242401884, + 455.2235993271474 + ], + "17": [ + 143.0534144786214, + 449.3961670306782 + ], + "25": [ + 101.7688239154511, + 441.76126380124293 + ], + "33": [ + 51.368166062733735, + 434.9385001903427 + ] + }, + "mean_error_pixels": 1.1880505227031462, + "max_error_pixels": 1.707584489997555 + }, + "feature_136": { + "xyz": [ + -26.9620817468768, + 33.61986241193773, + 6.668058888603898 + ], + "observations": { + "1": [ + 308.0, + 479.0 + ], + "9": [ + 289.41046142578125, + 475.205810546875 + ], + "17": [ + 268.4305114746094, + 471.5572509765625 + ], + "25": [ + 242.70220947265625, + 466.43377685546875 + ], + "33": [ + 212.5166015625, + 461.4092102050781 + ], + "41": [ + 181.09127807617188, + 459.8489990234375 + ], + "49": [ + 146.3634033203125, + 453.38616943359375 + ], + "57": [ + 107.49575805664062, + 448.89892578125 + ] + }, + "projections": { + "1": [ + 305.0389564651305, + 477.55588302298713 + ], + "9": [ + 288.33262800219603, + 474.0716577752088 + ], + "17": [ + 268.1103430856092, + 471.02527105741655 + ], + "25": [ + 244.38827019456767, + 466.0596940488323 + ], + "33": [ + 216.35226133685188, + 461.24196772402087 + ], + "41": [ + 182.2808485584087, + 460.2441164979255 + ], + "49": [ + 147.15156212051727, + 454.3653140166068 + ], + "57": [ + 103.5432017477475, + 450.0204963869971 + ] + }, + "mean_error_pixels": 2.2081661827776498, + "max_error_pixels": 4.108603412188465 + }, + "feature_144": { + "xyz": [ + -29.44311538926054, + 28.36535998470726, + 8.199263362744203 + ], + "observations": { + "1": [ + 218.0, + 445.0 + ], + "9": [ + 186.73960876464844, + 438.53143310546875 + ], + "17": [ + 152.9450225830078, + 431.11920166015625 + ], + "25": [ + 112.14344787597656, + 422.6999816894531 + ], + "33": [ + 64.47027587890625, + 415.6873474121094 + ], + "41": [ + 14.279901504516602, + 410.2101135253906 + ] + }, + "projections": { + "1": [ + 212.6954138174986, + 445.13605067998583 + ], + "9": [ + 186.08737362396192, + 438.89920156461346 + ], + "17": [ + 153.91279284385683, + 432.14411697067686 + ], + "25": [ + 115.48671368260557, + 423.43197929347895 + ], + "33": [ + 68.37622307591164, + 415.21147350011864 + ], + "41": [ + 9.7334143861126, + 408.81757775453235 + ] + }, + "mean_error_pixels": 3.262830600935717, + "max_error_pixels": 5.306330592331139 + }, + "feature_147": { + "xyz": [ + -27.922350250072412, + 28.39020652223503, + 7.125883807482194 + ], + "observations": { + "1": [ + 236.0, + 461.0 + ], + "9": [ + 207.72755432128906, + 455.3135070800781 + ], + "17": [ + 175.70323181152344, + 448.6668395996094 + ], + "25": [ + 137.21754455566406, + 441.07763671875 + ], + "33": [ + 92.1731185913086, + 434.60797119140625 + ], + "41": [ + 43.65007400512695, + 430.4933166503906 + ] + }, + "projections": { + "1": [ + 232.81972692786212, + 459.86552233339717 + ], + "9": [ + 207.3980663272818, + 454.63175808501916 + ], + "17": [ + 176.4631137098687, + 449.1318584243858 + ], + "25": [ + 139.8447562528048, + 441.7573615548605 + ], + "33": [ + 95.37333442379031, + 434.9496137013968 + ], + "41": [ + 39.22225102646871, + 430.479398278646 + ] + }, + "mean_error_pixels": 2.5640998112025595, + "max_error_pixels": 4.427844854035196 + }, + "feature_194": { + "xyz": [ + -26.252431483374185, + 27.43543178405984, + 4.994571399338847 + ], + "observations": { + "1": [ + 239.0, + 489.0 + ], + "9": [ + 212.03106689453125, + 485.23138427734375 + ], + "17": [ + 181.2407989501953, + 480.85101318359375 + ], + "25": [ + 143.7088165283203, + 475.6732482910156 + ], + "33": [ + 94.15365600585938, + 470.6909484863281 + ], + "41": [ + 36.80189514160156, + 467.0282897949219 + ] + }, + "projections": { + "1": [ + 239.33097451719618, + 487.24160035775117 + ], + "9": [ + 212.9149093686944, + 483.77090133256746 + ], + "17": [ + 180.26269810529902, + 480.3582391936219 + ], + "25": [ + 142.12521022253333, + 475.278814090316 + ], + "33": [ + 96.35305096510933, + 471.1649534335561 + ], + "41": [ + 36.24965649364367, + 470.1604154656388 + ] + }, + "mean_error_pixels": 1.9423192698744636, + "max_error_pixels": 3.180436879024982 + }, + "feature_304": { + "xyz": [ + -22.78289969107923, + 25.24949349027819, + 4.760641330628004 + ], + "observations": { + "1": [ + 260.0, + 489.0 + ], + "9": [ + 235.5068817138672, + 485.5094299316406 + ], + "17": [ + 204.45596313476562, + 481.0455017089844 + ], + "25": [ + 159.7410888671875, + 474.411865234375 + ], + "33": [ + 101.54950714111328, + 467.59454345703125 + ], + "41": [ + 45.85795211791992, + 463.9017639160156 + ] + }, + "projections": { + "1": [ + 262.2291782220796, + 486.8099798920158 + ], + "9": [ + 234.6657314346799, + 483.13772232392176 + ], + "17": [ + 200.21018582210615, + 479.50456432130716 + ], + "25": [ + 159.79002070435996, + 474.04573845159604 + ], + "33": [ + 110.76011321521787, + 469.3251755233888 + ], + "41": [ + 44.36990183556111, + 467.5256882585776 + ] + }, + "mean_error_pixels": 3.969481019358026, + "max_error_pixels": 9.371784867432527 + }, + "feature_311": { + "xyz": [ + -26.552063727496613, + 29.483781117557015, + 4.2871543564988555 + ], + "observations": { + "1": [ + 264.0, + 501.0 + ], + "9": [ + 240.00119018554688, + 498.2234802246094 + ], + "17": [ + 210.3454132080078, + 495.2217102050781 + ], + "25": [ + 169.27650451660156, + 491.8037414550781 + ], + "33": [ + 127.75398254394531, + 489.5896301269531 + ], + "41": [ + 87.60099029541016, + 490.43206787109375 + ], + "49": [ + 43.08454513549805, + 485.4955749511719 + ] + }, + "projections": { + "1": [ + 260.85611964348794, + 500.1756606180715 + ], + "9": [ + 238.00378171419038, + 497.74275296703956 + ], + "17": [ + 209.80249008570343, + 495.6995963356946 + ], + "25": [ + 177.20790291715116, + 492.0246210145468 + ], + "33": [ + 138.7820129463734, + 489.2462758883132 + ], + "41": [ + 88.88709169656124, + 490.34261247074653 + ], + "49": [ + 35.620934872896555, + 486.16992606095096 + ] + }, + "mean_error_pixels": 4.825564905217825, + "max_error_pixels": 11.033374220521594 + }, + "feature_48": { + "xyz": [ + -13.005588480972518, + 24.093163524106807, + 0.45387411867106464 + ], + "observations": { + "1": [ + 404.0, + 555.0 + ], + "9": [ + 385.761962890625, + 557.3265991210938 + ], + "17": [ + 363.6872253417969, + 561.2540283203125 + ], + "25": [ + 335.3769226074219, + 564.05078125 + ], + "33": [ + 299.85760498046875, + 567.2875366210938 + ], + "41": [ + 255.25279235839844, + 577.44921875 + ], + "49": [ + 196.3499755859375, + 585.0567016601562 + ], + "57": [ + 122.29508209228516, + 598.4305419921875 + ], + "65": [ + 25.347532272338867, + 615.2725219726562 + ] + }, + "projections": { + "1": [ + 402.90998745689996, + 555.0980906850443 + ], + "9": [ + 384.7186403641946, + 557.2984735967852 + ], + "17": [ + 360.89108364764945, + 561.4621233971095 + ], + "25": [ + 333.6235096051475, + 563.7163672427805 + ], + "33": [ + 301.88151006951, + 566.43412856195 + ], + "41": [ + 255.30593831550465, + 577.1416594551858 + ], + "49": [ + 201.73971933844848, + 584.5472605510394 + ], + "57": [ + 126.17644657431504, + 597.9927720431928 + ], + "65": [ + 21.674395629185824, + 616.7241944594819 + ] + }, + "mean_error_pixels": 2.500548462495309, + "max_error_pixels": 5.41376652261523 + }, + "feature_59": { + "xyz": [ + -10.63209804488098, + 22.665108172201965, + 0.5949377155275737 + ], + "observations": { + "1": [ + 438.0, + 553.0 + ], + "9": [ + 420.2544250488281, + 555.6448974609375 + ], + "17": [ + 397.55316162109375, + 560.3634033203125 + ], + "25": [ + 368.2631530761719, + 563.668701171875 + ], + "33": [ + 330.8023376464844, + 567.6851196289062 + ], + "41": [ + 285.6253967285156, + 578.7252197265625 + ], + "49": [ + 225.93968200683594, + 587.1826782226562 + ], + "57": [ + 147.3412322998047, + 601.953857421875 + ], + "65": [ + 46.244117736816406, + 619.5076904296875 + ] + }, + "projections": { + "1": [ + 431.68344675592766, + 553.6765025363471 + ], + "9": [ + 414.66551018025723, + 556.04405404226 + ], + "17": [ + 392.06915541387775, + 560.5539377734092 + ], + "25": [ + 365.886266100612, + 563.0765916480547 + ], + "33": [ + 335.0203701162135, + 565.9066956483119 + ], + "41": [ + 288.7813629793727, + 577.2661437878556 + ], + "49": [ + 234.01746438799717, + 585.8671777176596 + ], + "57": [ + 154.29892888119105, + 601.3841283800053 + ], + "65": [ + 38.48416777322586, + 624.0344854844193 + ] + }, + "mean_error_pixels": 5.788466686878544, + "max_error_pixels": 8.983801918173235 + }, + "feature_73": { + "xyz": [ + -9.745717346308233, + 22.42103043636557, + 1.4470466617966313 + ], + "observations": { + "1": [ + 451.0, + 539.0 + ], + "9": [ + 434.7498474121094, + 540.1424560546875 + ], + "17": [ + 414.4917907714844, + 542.808837890625 + ], + "25": [ + 387.4115905761719, + 544.0657348632812 + ], + "33": [ + 352.3484191894531, + 545.446533203125 + ], + "41": [ + 311.28680419921875, + 553.0447998046875 + ], + "49": [ + 255.2911834716797, + 557.6790771484375 + ], + "57": [ + 182.05532836914062, + 567.3294067382812 + ], + "65": [ + 84.02801513671875, + 578.5353393554688 + ] + }, + "projections": { + "1": [ + 446.69328956184165, + 538.8640938321046 + ], + "9": [ + 430.94370136759335, + 539.9179019363052 + ], + "17": [ + 410.0502789505325, + 542.8228333528638 + ], + "25": [ + 385.5727334289936, + 543.540693291884 + ], + "33": [ + 356.4017886796731, + 544.1212041345061 + ], + "41": [ + 313.18804088288044, + 552.1367108979421 + ], + "49": [ + 261.809369166963, + 556.9558797149508 + ], + "57": [ + 187.03292303162812, + 566.9430344949687 + ], + "65": [ + 78.55989519826744, + 581.6486435361533 + ] + }, + "mean_error_pixels": 4.2988950828004455, + "max_error_pixels": 6.558182620665398 + }, + "feature_118": { + "xyz": [ + -21.381099300079303, + 49.31965666236786, + 3.934128306165265 + ], + "observations": { + "1": [ + 483.0, + 516.0 + ], + "9": [ + 473.6402282714844, + 515.5115966796875 + ], + "17": [ + 460.7416076660156, + 516.2073974609375 + ], + "25": [ + 443.39459228515625, + 514.4896850585938 + ], + "33": [ + 420.3373718261719, + 511.5028076171875 + ], + "41": [ + 398.3775329589844, + 514.6508178710938 + ], + "49": [ + 384.6920166015625, + 513.4697265625 + ], + "57": [ + 374.79840087890625, + 514.925048828125 + ], + "65": [ + 364.1363830566406, + 517.4613037109375 + ], + "73": [ + 352.35418701171875, + 516.28076171875 + ], + "81": [ + 340.205322265625, + 518.8330078125 + ], + "89": [ + 327.840087890625, + 517.9929809570312 + ], + "97": [ + 313.4815979003906, + 515.5008544921875 + ], + "105": [ + 298.3309631347656, + 518.5892944335938 + ] + }, + "projections": { + "1": [ + 448.7657965064801, + 518.3372963081401 + ], + "9": [ + 443.6838417291964, + 517.3223389664777 + ], + "17": [ + 437.05073218971137, + 517.7399284137829 + ], + "25": [ + 428.8665906429229, + 515.6964339558259 + ], + "33": [ + 419.5776733295646, + 512.5518619875005 + ], + "41": [ + 408.96015543939427, + 515.0954724733322 + ], + "49": [ + 398.9109180312654, + 513.835691774844 + ], + "57": [ + 386.74941576610973, + 514.9041591325139 + ], + "65": [ + 373.9895047241781, + 517.5264176548659 + ], + "73": [ + 360.0736349016765, + 516.0426186010508 + ], + "81": [ + 343.80943829669985, + 518.5785670247423 + ], + "89": [ + 327.80672417096696, + 517.3836643273848 + ], + "97": [ + 309.76341852981085, + 513.9446446815633 + ], + "105": [ + 287.2643208510232, + 515.1983411318023 + ] + }, + "mean_error_pixels": 12.722158893598701, + "max_error_pixels": 34.31389868359138 + }, + "feature_141": { + "xyz": [ + -9.17185539967833, + 23.601448878437747, + 2.1250016369154565 + ], + "observations": { + "1": [ + 473.0, + 529.0 + ], + "9": [ + 459.5030212402344, + 528.832275390625 + ], + "17": [ + 442.4862060546875, + 530.8295288085938 + ], + "25": [ + 419.18670654296875, + 530.2857666015625 + ], + "33": [ + 389.2955627441406, + 528.2664184570312 + ], + "41": [ + 355.8277893066406, + 531.2675170898438 + ], + "49": [ + 313.8809509277344, + 532.1879272460938 + ], + "57": [ + 254.19155883789062, + 537.69775390625 + ], + "65": [ + 179.15347290039062, + 542.7819213867188 + ], + "73": [ + 118.84210205078125, + 551.7826538085938 + ] + }, + "projections": { + "1": [ + 465.79963247183343, + 527.6218991700146 + ], + "9": [ + 452.79558634628427, + 527.6029241506238 + ], + "17": [ + 435.6363970488188, + 529.2280585844007 + ], + "25": [ + 415.4343708908726, + 528.4341869563431 + ], + "33": [ + 391.4598631952732, + 526.9534639895688 + ], + "41": [ + 357.16005725013315, + 531.9863456256298 + ], + "49": [ + 317.4605088064475, + 533.4047760636352 + ], + "57": [ + 261.63309896772705, + 538.2635337543484 + ], + "65": [ + 184.88500667467963, + 545.5447354069531 + ], + "73": [ + 70.6443499190558, + 551.1425106447933 + ] + }, + "mean_error_pixels": 9.522271438700239, + "max_error_pixels": 48.20200300632128 + }, + "feature_164": { + "xyz": [ + -10.235065829516923, + 25.83608298971255, + 2.9549737665616957 + ], + "observations": { + "1": [ + 466.0, + 517.0 + ], + "9": [ + 452.8934326171875, + 516.212646484375 + ], + "17": [ + 436.3958740234375, + 516.712646484375 + ], + "25": [ + 414.40325927734375, + 514.5435791015625 + ], + "33": [ + 386.62640380859375, + 511.4383850097656 + ], + "41": [ + 359.9572448730469, + 513.0645751953125 + ], + "49": [ + 340.9840393066406, + 511.170654296875 + ], + "57": [ + 303.2931823730469, + 513.329345703125 + ], + "65": [ + 244.8655242919922, + 515.552734375 + ], + "73": [ + 162.6346435546875, + 512.36083984375 + ], + "81": [ + 50.72128677368164, + 513.795166015625 + ] + }, + "projections": { + "1": [ + 463.6634588419696, + 516.0705134070877 + ], + "9": [ + 452.02941646326985, + 515.0382168429691 + ], + "17": [ + 436.9216452543378, + 515.4135894453298 + ], + "25": [ + 419.1390135799242, + 513.287389740512 + ], + "33": [ + 398.18161641912906, + 510.2216477363182 + ], + "41": [ + 369.56577480839724, + 512.8742873691234 + ], + "49": [ + 337.7093391728763, + 511.59384839398297 + ], + "57": [ + 294.8428633167835, + 512.6713099984537 + ], + "65": [ + 239.81866124356117, + 514.888883564939 + ], + "73": [ + 164.36358165907342, + 512.9904394651304 + ], + "81": [ + 51.237640982469316, + 516.8111669332887 + ] + }, + "mean_error_pixels": 4.842833268578775, + "max_error_pixels": 11.619095836908762 + }, + "feature_179": { + "xyz": [ + -10.530007517344144, + 22.45306014150578, + 1.2475757594453663 + ], + "observations": { + "1": [ + 433.0, + 541.0 + ], + "9": [ + 414.8622741699219, + 542.7109985351562 + ], + "17": [ + 392.62530517578125, + 546.360595703125 + ], + "25": [ + 367.7801818847656, + 548.5661010742188 + ], + "33": [ + 335.394287109375, + 551.1338500976562 + ] + }, + "projections": { + "1": [ + 432.6867637723919, + 542.3534594548803 + ], + "9": [ + 415.6484385107624, + 543.7032808079271 + ], + "17": [ + 393.0997349394758, + 546.9289366130665 + ], + "25": [ + 366.7969558669551, + 548.0742820919129 + ], + "33": [ + 335.50091745751536, + 549.3218711066643 + ] + }, + "mean_error_pixels": 1.262004627047612, + "max_error_pixels": 1.815113741598809 + }, + "feature_189": { + "xyz": [ + -26.941091656516384, + 50.1072421376411, + 4.3717461239464415 + ], + "observations": { + "1": [ + 449.0, + 514.0 + ], + "9": [ + 440.5047912597656, + 512.5718994140625 + ], + "17": [ + 427.90765380859375, + 512.153076171875 + ], + "25": [ + 409.42388916015625, + 509.6680603027344 + ], + "33": [ + 386.5289306640625, + 505.89288330078125 + ], + "41": [ + 367.03558349609375, + 507.43792724609375 + ], + "49": [ + 345.6221618652344, + 506.2715148925781 + ], + "57": [ + 313.1564025878906, + 508.43414306640625 + ], + "65": [ + 294.15045166015625, + 512.47119140625 + ], + "73": [ + 276.6644592285156, + 510.7812805175781 + ], + "81": [ + 251.01898193359375, + 514.8050537109375 + ], + "89": [ + 236.2827606201172, + 513.68408203125 + ], + "97": [ + 221.26272583007812, + 509.78448486328125 + ], + "105": [ + 204.6089324951172, + 510.3995056152344 + ], + "113": [ + 185.59765625, + 508.81298828125 + ], + "121": [ + 164.457763671875, + 510.3230895996094 + ], + "128": [ + 145.3196258544922, + 509.6761169433594 + ] + }, + "projections": { + "1": [ + 407.6068968575586, + 515.1186552081401 + ], + "9": [ + 401.2319814933941, + 513.9150651851206 + ], + "17": [ + 393.1290466407121, + 513.974217447497 + ], + "25": [ + 383.32199566389113, + 511.77000568456594 + ], + "33": [ + 372.1939272050182, + 508.83396554152523 + ], + "41": [ + 359.4595393304433, + 511.29503944288905 + ], + "49": [ + 347.4257990098848, + 509.75280641131843 + ], + "57": [ + 332.93856776366636, + 510.55086913367586 + ], + "65": [ + 317.8023803317938, + 512.7112526581363 + ], + "73": [ + 301.28062622430963, + 510.8418767088702 + ], + "81": [ + 281.9680292888437, + 513.3715205941667 + ], + "89": [ + 263.00931282178493, + 512.066443101637 + ], + "97": [ + 241.84822281498168, + 508.0416951649396 + ], + "105": [ + 215.58084808289505, + 508.74214791828956 + ], + "113": [ + 184.5751273527739, + 507.2727618099622 + ], + "121": [ + 147.70149846680863, + 508.8557240392617 + ], + "128": [ + 115.65155343686979, + 508.9239820102703 + ] + }, + "mean_error_pixels": 22.046873307851822, + "max_error_pixels": 41.408216301061465 + }, + "feature_222": { + "xyz": [ + -9.293500409973266, + 23.1416198055647, + 1.3589721348093768 + ], + "observations": { + "1": [ + 469.0, + 541.0 + ], + "9": [ + 455.0341796875, + 541.8815307617188 + ], + "17": [ + 436.7648620605469, + 544.9429321289062 + ], + "25": [ + 411.6676330566406, + 545.88720703125 + ], + "33": [ + 379.3047180175781, + 546.7969360351562 + ], + "41": [ + 340.7491760253906, + 555.21728515625 + ], + "49": [ + 291.2837219238281, + 558.4052124023438 + ], + "57": [ + 226.6394805908203, + 567.9657592773438 + ], + "65": [ + 140.79454040527344, + 580.64111328125 + ], + "73": [ + 21.249683380126953, + 596.2964477539062 + ] + }, + "projections": { + "1": [ + 459.7458529476912, + 540.2901330456187 + ], + "9": [ + 445.7768144472981, + 541.4172258438449 + ], + "17": [ + 427.23088860343387, + 544.4559630464968 + ], + "25": [ + 405.5394076653233, + 545.2162763084964 + ], + "33": [ + 379.9208932983451, + 545.6236361069327 + ], + "41": [ + 342.45583458930287, + 553.4998763576432 + ], + "49": [ + 298.6231912947859, + 558.1846490685913 + ], + "57": [ + 236.03015597220474, + 567.762895797657 + ], + "65": [ + 147.78403107804388, + 581.7410442452599 + ], + "73": [ + 13.1549500384142, + 597.731821537914 + ] + }, + "mean_error_pixels": 7.004019802018453, + "max_error_pixels": 9.546401875479475 + }, + "feature_233": { + "xyz": [ + -12.18564840743272, + 23.38154597928908, + 0.9395511197308821 + ], + "observations": { + "1": [ + 414.0, + 545.0 + ], + "9": [ + 395.34088134765625, + 546.9779052734375 + ], + "17": [ + 372.56341552734375, + 550.5355834960938 + ], + "25": [ + 343.6201477050781, + 553.1769409179688 + ], + "33": [ + 306.5524597167969, + 556.663330078125 + ], + "41": [ + 259.27130126953125, + 566.0368041992188 + ], + "49": [ + 199.33250427246094, + 572.7672119140625 + ], + "57": [ + 124.80342102050781, + 584.8910522460938 + ], + "65": [ + 29.229103088378906, + 600.4054565429688 + ] + }, + "projections": { + "1": [ + 411.2091785123306, + 547.3697566758889 + ], + "9": [ + 393.1065674920114, + 549.0139714405115 + ], + "17": [ + 369.34391139287624, + 552.5177755952761 + ], + "25": [ + 341.92154057861234, + 554.0511048545418 + ], + "33": [ + 309.65027591242693, + 555.9173510797089 + ], + "41": [ + 262.1397697393264, + 565.4393528899221 + ], + "49": [ + 206.75059927451855, + 571.6386791912313 + ], + "57": [ + 127.6282845711097, + 583.5104495473313 + ], + "65": [ + 16.158595643423723, + 600.3211531201446 + ] + }, + "mean_error_pixels": 4.6900012523717685, + "max_error_pixels": 13.070779316311967 + }, + "feature_234": { + "xyz": [ + -9.552058023957903, + 22.45620469168287, + 0.47554329975213494 + ], + "observations": { + "1": [ + 453.0, + 555.0 + ], + "9": [ + 437.1142883300781, + 557.7767944335938 + ], + "17": [ + 417.2198181152344, + 562.7786865234375 + ], + "25": [ + 390.5256042480469, + 566.2493896484375 + ], + "33": [ + 356.150146484375, + 570.4324951171875 + ], + "41": [ + 313.74847412109375, + 582.1493530273438 + ], + "49": [ + 258.585693359375, + 592.0498657226562 + ], + "57": [ + 185.2819061279297, + 608.9874267578125 + ], + "65": [ + 87.06477355957031, + 630.4608764648438 + ] + }, + "projections": { + "1": [ + 449.0840189514946, + 555.886398536837 + ], + "9": [ + 433.45599201346664, + 558.5298604828735 + ], + "17": [ + 412.55669123013786, + 563.46132735955 + ], + "25": [ + 388.2614707853114, + 566.3410930914157 + ], + "33": [ + 359.6412209557135, + 569.3880292615603 + ], + "41": [ + 316.5586485780905, + 581.3465457910324 + ], + "49": [ + 265.2872801896127, + 590.8264315566463 + ], + "57": [ + 190.26712969952467, + 607.6494855221106 + ], + "65": [ + 80.27716034285777, + 632.5317447604549 + ] + }, + "mean_error_pixels": 4.485101532756132, + "max_error_pixels": 7.096491293411007 + }, + "feature_279": { + "xyz": [ + -11.95048739300913, + 23.383815321800682, + 0.3659123332087131 + ], + "observations": { + "1": [ + 419.0, + 557.0 + ], + "9": [ + 400.4272766113281, + 559.69970703125 + ], + "17": [ + 377.81787109375, + 564.1937866210938 + ], + "25": [ + 348.7873229980469, + 567.6510620117188 + ], + "33": [ + 310.0450134277344, + 571.6647338867188 + ], + "41": [ + 262.8382568359375, + 582.7440185546875 + ], + "49": [ + 203.39642333984375, + 591.2234497070312 + ], + "57": [ + 128.0965576171875, + 606.2428588867188 + ], + "65": [ + 29.894895553588867, + 624.0651245117188 + ] + }, + "projections": { + "1": [ + 414.40868762776836, + 557.1119865178771 + ], + "9": [ + 396.50769043685966, + 559.6326020810953 + ], + "17": [ + 372.8974564572368, + 564.2567928168598 + ], + "25": [ + 345.76353776636626, + 566.9669674031122 + ], + "33": [ + 314.03391373700606, + 570.1421291815082 + ], + "41": [ + 266.8830462945454, + 581.7649687870422 + ], + "49": [ + 211.8818461356043, + 590.4116194070106 + ], + "57": [ + 133.05757028193693, + 605.8083275143782 + ], + "65": [ + 21.3062460127245, + 627.7337151783024 + ] + }, + "mean_error_pixels": 5.312066577992072, + "max_error_pixels": 9.33934999958426 + }, + "feature_283": { + "xyz": [ + -9.784734616429734, + 23.62262194654536, + 2.3340409246207505 + ], + "observations": { + "1": [ + 459.0, + 527.0 + ], + "9": [ + 444.93182373046875, + 526.8642578125 + ], + "17": [ + 427.41986083984375, + 528.2420654296875 + ], + "25": [ + 404.4678649902344, + 526.1802368164062 + ], + "33": [ + 374.5221252441406, + 523.8699951171875 + ], + "41": [ + 340.69427490234375, + 526.0284423828125 + ], + "49": [ + 298.37774658203125, + 525.7967529296875 + ], + "57": [ + 236.92662048339844, + 530.5911254882812 + ], + "65": [ + 155.3740692138672, + 535.248291015625 + ], + "73": [ + 43.390830993652344, + 537.377197265625 + ] + }, + "projections": { + "1": [ + 455.9465965897542, + 524.1942200175677 + ], + "9": [ + 442.1303819511379, + 523.8594706037579 + ], + "17": [ + 423.99544358148955, + 525.0538875215682 + ], + "25": [ + 402.6740776569724, + 523.8534964508242 + ], + "33": [ + 377.31247157974326, + 522.0193780076869 + ], + "41": [ + 341.1190668421724, + 526.399355679104 + ], + "49": [ + 299.24662868992476, + 526.992045014517 + ], + "57": [ + 240.432784330488, + 530.7190526721278 + ], + "65": [ + 159.77154364224293, + 536.3508641466307 + ], + "73": [ + 39.663297247138644, + 539.573138330914 + ] + }, + "mean_error_pixels": 3.362990939177976, + "max_error_pixels": 4.678793854524275 + }, + "feature_343": { + "xyz": [ + -8.798196370283364, + 22.71556252842012, + 0.22532996206500866 + ], + "observations": { + "1": [ + 470.0, + 561.0 + ], + "9": [ + 455.2610778808594, + 563.9058227539062 + ], + "17": [ + 436.5084228515625, + 568.93994140625 + ], + "25": [ + 411.9832458496094, + 572.771484375 + ], + "33": [ + 380.0935974121094, + 576.601806640625 + ], + "41": [ + 341.71563720703125, + 588.4218139648438 + ], + "49": [ + 292.5811462402344, + 598.18505859375 + ], + "57": [ + 226.8959503173828, + 615.1868896484375 + ], + "65": [ + 139.92172241210938, + 638.4476318359375 + ] + }, + "projections": { + "1": [ + 464.0063133887446, + 559.9814674384496 + ], + "9": [ + 449.9433250403918, + 562.9826611318763 + ], + "17": [ + 431.0295297319783, + 568.4182418028399 + ], + "25": [ + 409.04973298150173, + 571.7265800174189 + ], + "33": [ + 383.34038385793735, + 575.0421398040182 + ], + "41": [ + 344.7039618315627, + 587.6112557536261 + ], + "49": [ + 298.98253712787374, + 597.861635919623 + ], + "57": [ + 232.52688034405122, + 615.6424556241421 + ], + "65": [ + 135.9705731500017, + 641.8747090957268 + ] + }, + "mean_error_pixels": 4.89801368296472, + "max_error_pixels": 6.40955595361268 + }, + "feature_5": { + "xyz": [ + 10.401715207142567, + 13.572010344242237, + 1.432601948825346 + ], + "observations": { + "1": [ + 890.0, + 540.0 + ], + "9": [ + 932.1524658203125, + 543.8360595703125 + ], + "17": [ + 986.7321166992188, + 552.8622436523438 + ], + "25": [ + 1056.60986328125, + 558.5870361328125 + ], + "33": [ + 1151.8387451171875, + 559.4164428710938 + ] + }, + "projections": { + "1": [ + 889.0704837133853, + 540.193221566914 + ], + "9": [ + 931.0351334618547, + 543.8867167260535 + ], + "17": [ + 988.6635846100933, + 552.7276754817533 + ], + "25": [ + 1057.412751514566, + 558.2535836049822 + ], + "33": [ + 1151.108416286092, + 559.6496536629318 + ] + }, + "mean_error_pixels": 1.1280111828668562, + "max_error_pixels": 1.9361500156945566 + }, + "feature_27": { + "xyz": [ + 11.45098975230415, + 15.956258823488584, + 1.6482448204872064 + ], + "observations": { + "1": [ + 870.0, + 534.0 + ], + "9": [ + 906.3270874023438, + 536.6963500976562 + ], + "17": [ + 948.7317504882812, + 544.0468139648438 + ], + "25": [ + 994.3228149414062, + 546.31005859375 + ], + "33": [ + 1052.7745361328125, + 543.42236328125 + ] + }, + "projections": { + "1": [ + 871.9221958565587, + 534.8440542838312 + ], + "9": [ + 904.9909605589427, + 537.1067264015999 + ], + "17": [ + 947.7684441392923, + 543.5556318766347 + ], + "25": [ + 994.7416470473952, + 545.9656309529228 + ], + "33": [ + 1052.9754825139084, + 543.3566121997906 + ] + }, + "mean_error_pixels": 1.0664150778972066, + "max_error_pixels": 2.099348600167469 + }, + "feature_32": { + "xyz": [ + 10.369121044786528, + 15.860445059930653, + 2.0375978348938935 + ], + "observations": { + "1": [ + 849.0, + 523.0 + ], + "9": [ + 878.5810546875, + 524.1605224609375 + ], + "17": [ + 915.9013061523438, + 530.1353149414062 + ], + "25": [ + 963.0665283203125, + 531.7294921875 + ], + "33": [ + 1022.6788940429688, + 529.4323120117188 + ], + "41": [ + 1110.8033447265625, + 536.6965942382812 + ] + }, + "projections": { + "1": [ + 849.2493462957914, + 525.9524708449966 + ], + "9": [ + 879.4181702796357, + 526.9960694706425 + ], + "17": [ + 918.3856726408262, + 531.7159654287811 + ], + "25": [ + 961.2586292574122, + 532.3174529103401 + ], + "33": [ + 1014.6629849432209, + 527.7866831963017 + ], + "41": [ + 1112.7246372458656, + 533.830748961718 + ] + }, + "mean_error_pixels": 3.733093596108881, + "max_error_pixels": 8.183085780654617 + }, + "feature_62": { + "xyz": [ + 11.40506722920577, + 13.615626165815904, + 1.431903194739286 + ], + "observations": { + "1": [ + 915.0, + 540.0 + ], + "9": [ + 959.942626953125, + 543.5140380859375 + ], + "17": [ + 1018.41455078125, + 552.514892578125 + ], + "25": [ + 1094.6934814453125, + 557.9375610351562 + ], + "33": [ + 1197.463623046875, + 560.1787719726562 + ] + }, + "projections": { + "1": [ + 913.1968962221188, + 540.18759655452 + ], + "9": [ + 958.7855056565884, + 543.9020119583506 + ], + "17": [ + 1021.3714154366598, + 552.8650092424344 + ], + "25": [ + 1095.772580299168, + 558.3432869843024 + ], + "33": [ + 1196.5154520700903, + 559.2462732953934 + ] + }, + "mean_error_pixels": 1.6987042705206559, + "max_error_pixels": 2.9775208259622885 + }, + "feature_69": { + "xyz": [ + 11.703444014714204, + 13.958869374259743, + 0.6760911417636344 + ], + "observations": { + "1": [ + 918.0, + 559.0 + ], + "9": [ + 962.2476196289062, + 565.706787109375 + ], + "17": [ + 1019.024658203125, + 579.151123046875 + ], + "25": [ + 1092.8614501953125, + 589.6774291992188 + ], + "33": [ + 1192.2833251953125, + 598.533447265625 + ] + }, + "projections": { + "1": [ + 915.6408668058655, + 559.753912797014 + ], + "9": [ + 960.7360825004112, + 566.4408691044274 + ], + "17": [ + 1022.3007577699425, + 579.5599978430527 + ], + "25": [ + 1094.616042801571, + 589.9016993752119 + ], + "33": [ + 1191.1266505459625, + 597.1427317429533 + ] + }, + "mean_error_pixels": 2.207255956340491, + "max_error_pixels": 3.301515859518334 + }, + "feature_83": { + "xyz": [ + 10.016433471916091, + 14.684052333478785, + 0.2444911875561735 + ], + "observations": { + "1": [ + 861.0, + 569.0 + ], + "9": [ + 896.8580322265625, + 576.62841796875 + ], + "17": [ + 943.030517578125, + 590.7696533203125 + ], + "25": [ + 1003.5988159179688, + 603.5724487304688 + ], + "33": [ + 1087.0211181640625, + 616.3148193359375 + ], + "41": [ + 1208.4114990234375, + 647.4694213867188 + ] + }, + "projections": { + "1": [ + 862.8947456252729, + 569.7372765545699 + ], + "9": [ + 898.0824472769291, + 577.469719402 + ], + "17": [ + 945.1517902072865, + 591.6561232699106 + ], + "25": [ + 999.1777045559977, + 603.3770368475116 + ], + "33": [ + 1069.773515055981, + 612.9979420278746 + ], + "41": [ + 1210.513870637344, + 648.1515700918551 + ] + }, + "mean_error_pixels": 5.00285229286537, + "max_error_pixels": 17.563641081809955 + }, + "feature_108": { + "xyz": [ + 9.65614369746925, + 13.30889740115065, + 0.676377440846186 + ], + "observations": { + "1": [ + 879.0, + 562.0 + ], + "9": [ + 919.66015625, + 568.7604370117188 + ], + "17": [ + 972.1539916992188, + 582.1739501953125 + ], + "25": [ + 1043.643798828125, + 593.7825927734375 + ], + "33": [ + 1142.873779296875, + 604.8052368164062 + ] + }, + "projections": { + "1": [ + 877.1279078753715, + 561.0378189093293 + ], + "9": [ + 918.3923651953869, + 568.2841896145505 + ], + "17": [ + 975.7022687243243, + 582.2661963440999 + ], + "25": [ + 1045.0938480304126, + 594.3184087349845 + ], + "33": [ + 1141.5747274172807, + 605.0066794485273 + ] + }, + "mean_error_pixels": 1.9738209160122122, + "max_error_pixels": 3.5494759048144107 + }, + "feature_111": { + "xyz": [ + 12.460515874691897, + 14.061172754783492, + 1.4442265188212569 + ], + "observations": { + "1": [ + 932.0, + 541.0 + ], + "9": [ + 976.8059692382812, + 544.1072998046875 + ], + "17": [ + 1035.409423828125, + 552.2792358398438 + ], + "25": [ + 1110.744873046875, + 556.4590454101562 + ], + "33": [ + 1211.393310546875, + 555.7357177734375 + ] + }, + "projections": { + "1": [ + 929.4556655226993, + 539.7649855613191 + ], + "9": [ + 975.9400991282087, + 543.2928720226608 + ], + "17": [ + 1039.003955773456, + 551.9742951553524 + ], + "25": [ + 1112.5601392093627, + 556.8804153142062 + ], + "33": [ + 1209.7701458226966, + 556.6785631084651 + ] + }, + "mean_error_pixels": 2.2730087315486256, + "max_error_pixels": 3.607443516822313 + }, + "feature_161": { + "xyz": [ + 10.326368803124637, + 13.515399491415835, + 0.7316026891265641 + ], + "observations": { + "1": [ + 892.0, + 558.0 + ], + "9": [ + 934.5252685546875, + 564.7433471679688 + ], + "17": [ + 989.2821044921875, + 578.3794555664062 + ], + "25": [ + 1060.8193359375, + 590.1461791992188 + ], + "33": [ + 1159.70458984375, + 601.416259765625 + ] + }, + "projections": { + "1": [ + 890.1805656884179, + 559.108198812835 + ], + "9": [ + 932.8142252825257, + 565.9186232355164 + ], + "17": [ + 991.6799573134867, + 579.2507580172567 + ], + "25": [ + 1062.2318489463212, + 590.27683787799 + ], + "33": [ + 1158.9533612196778, + 599.1581645267656 + ] + }, + "mean_error_pixels": 2.1111460576504557, + "max_error_pixels": 2.551247952173729 + }, + "feature_165": { + "xyz": [ + 11.843399727221945, + 14.409715501303898, + 0.20383938057879347 + ], + "observations": { + "1": [ + 914.0, + 572.0 + ], + "9": [ + 956.5632934570312, + 579.4005737304688 + ], + "17": [ + 1007.5718383789062, + 593.4551391601562 + ], + "25": [ + 1076.148193359375, + 605.6729736328125 + ], + "33": [ + 1170.5911865234375, + 617.9508666992188 + ] + }, + "projections": { + "1": [ + 911.2617175972257, + 571.1101806976185 + ], + "9": [ + 954.3889028920112, + 579.2922636153098 + ], + "17": [ + 1012.6891413896633, + 594.3812137953113 + ], + "25": [ + 1080.0979953527208, + 606.886120278833 + ], + "33": [ + 1168.4411536264652, + 616.8015931943612 + ] + }, + "mean_error_pixels": 3.365314333398429, + "max_error_pixels": 5.200423476389265 + }, + "feature_193": { + "xyz": [ + 12.501125667396309, + 14.162823364749562, + 0.8638891264003197 + ], + "observations": { + "1": [ + 933.0, + 555.0 + ], + "9": [ + 978.0617065429688, + 560.39111328125 + ], + "17": [ + 1035.1885986328125, + 570.9076538085938 + ], + "25": [ + 1110.221923828125, + 580.0338134765625 + ], + "33": [ + 1211.3992919921875, + 586.603759765625 + ] + }, + "projections": { + "1": [ + 930.0217968154856, + 554.536668351355 + ], + "9": [ + 976.4956870225733, + 560.3158522331998 + ], + "17": [ + 1039.5636465405012, + 572.1489145690954 + ], + "25": [ + 1112.937106812985, + 580.7604461856533 + ], + "33": [ + 1209.6252050678827, + 585.3852226611516 + ] + }, + "mean_error_pixels": 2.8185136117285956, + "max_error_pixels": 4.547721679053378 + }, + "feature_226": { + "xyz": [ + 12.769646869023022, + 14.582805472457725, + -0.06778445146523615 + ], + "observations": { + "1": [ + 933.0, + 577.0 + ], + "9": [ + 977.4002685546875, + 585.9940185546875 + ], + "17": [ + 1034.11767578125, + 602.3037109375 + ], + "25": [ + 1104.7193603515625, + 616.4993286132812 + ], + "33": [ + 1200.4796142578125, + 628.7858276367188 + ] + }, + "projections": { + "1": [ + 930.3307631077178, + 577.4587692760499 + ], + "9": [ + 975.8167097449702, + 586.5217693451127 + ], + "17": [ + 1037.1641017240663, + 602.8739705973846 + ], + "25": [ + 1107.5757375775795, + 616.6082065066925 + ], + "33": [ + 1198.920177779864, + 627.6583330772195 + ] + }, + "mean_error_pixels": 2.451938392573289, + "max_error_pixels": 3.099339785302026 + }, + "feature_288": { + "xyz": [ + 11.797672551810905, + 14.051960682499955, + 2.003736817621135 + ], + "observations": { + "1": [ + 913.0, + 523.0 + ], + "9": [ + 957.0250854492188, + 525.1795043945312 + ], + "17": [ + 1011.5255737304688, + 531.875 + ], + "25": [ + 1084.4713134765625, + 533.4840698242188 + ], + "33": [ + 1175.2862548828125, + 532.3221435546875 + ] + }, + "projections": { + "1": [ + 912.2109691099799, + 525.5325825554927 + ], + "9": [ + 955.8964448961724, + 526.843059679431 + ], + "17": [ + 1014.9816075164275, + 532.3470981680683 + ], + "25": [ + 1083.9184326488967, + 533.5932961538633 + ], + "33": [ + 1175.1564197396342, + 528.8446539624667 + ] + }, + "mean_error_pixels": 2.4389084355622392, + "max_error_pixels": 3.4881293281617234 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/ground_camera_fit.json b/astra_scene_test/Scene_04/reference/ground_camera_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..50c71e8fb9a4d3842ab48fa69f0d4c964f39ebbd --- /dev/null +++ b/astra_scene_test/Scene_04/reference/ground_camera_fit.json @@ -0,0 +1,40 @@ +{ + "pitch": 4.726014521736058, + "camera_height": 2.966445669913508, + "lamp_base_targets": { + "1": [ + 519, + 541 + ], + "65": [ + 477, + 545 + ], + "128": [ + 340, + 555 + ] + }, + "predictions": { + "1": [ + 518.9255328375992, + 534.7597792851368 + ], + "65": [ + 469.0404955020731, + 541.8691616745809 + ], + "128": [ + 343.01387000988615, + 559.2713030525597 + ] + }, + "residuals": [ + -0.07446716240076512, + -6.240220714863199, + -7.9595044979269005, + -3.130838325419063, + 3.0138700098861477, + 4.271303052559688 + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/ground_tracks_fit.json b/astra_scene_test/Scene_04/reference/ground_tracks_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..9b59b1657985a9becbef6c5476917034702f9c6c --- /dev/null +++ b/astra_scene_test/Scene_04/reference/ground_tracks_fit.json @@ -0,0 +1,2794 @@ +[ + { + "id": 0, + "xyz": [ + -22.565122072526318, + 46.62576968074306, + -0.947929598738695 + ], + "first_frame": 65, + "mean_error": 4.313831909263332, + "observations": { + "65": [ + 347.787109375, + 543.9345703125 + ], + "69": [ + 336.833984375, + 545.5015869140625 + ], + "73": [ + 322.2336120605469, + 548.317138671875 + ], + "77": [ + 308.6590881347656, + 554.1552124023438 + ], + "81": [ + 294.7225341796875, + 558.8268432617188 + ], + "85": [ + 284.515869140625, + 560.3363647460938 + ], + "89": [ + 273.4637145996094, + 560.6739501953125 + ], + "93": [ + 261.71881103515625, + 560.2540283203125 + ], + "97": [ + 248.69235229492188, + 560.5399169921875 + ], + "101": [ + 234.697265625, + 563.389892578125 + ], + "105": [ + 219.67337036132812, + 565.92822265625 + ], + "109": [ + 202.92176818847656, + 567.5467529296875 + ], + "113": [ + 185.28749084472656, + 569.5127563476562 + ], + "117": [ + 165.82879638671875, + 572.9268798828125 + ], + "121": [ + 145.4898223876953, + 576.8447265625 + ], + "125": [ + 124.43016815185547, + 579.5556030273438 + ] + } + }, + { + "id": 1, + "xyz": [ + 18.723966213333103, + 38.67251158647287, + 0.40438220680132975 + ], + "first_frame": 107, + "mean_error": 0.48390819744020486, + "observations": { + "107": [ + 1029.96435546875, + 562.5318603515625 + ], + "111": [ + 1051.2567138671875, + 565.4083862304688 + ], + "115": [ + 1073.6064453125, + 569.1061401367188 + ], + "119": [ + 1097.7640380859375, + 573.1621704101562 + ], + "123": [ + 1124.955078125, + 576.9346923828125 + ], + "127": [ + 1155.794189453125, + 580.1441040039062 + ] + } + }, + { + "id": 3, + "xyz": [ + -19.07572720595024, + 43.82824866401423, + -1.4123083749905259 + ], + "first_frame": 77, + "mean_error": 2.502774682444302, + "observations": { + "77": [ + 315.746826171875, + 568.4121704101562 + ], + "81": [ + 312.3748779296875, + 571.9600219726562 + ], + "85": [ + 305.460693359375, + 573.6370849609375 + ], + "89": [ + 296.3066101074219, + 574.290283203125 + ], + "93": [ + 282.27191162109375, + 574.3527221679688 + ], + "97": [ + 267.051025390625, + 575.3948364257812 + ], + "101": [ + 250.80018615722656, + 578.7013549804688 + ], + "105": [ + 232.278564453125, + 582.0795288085938 + ], + "109": [ + 211.7564239501953, + 584.9005737304688 + ], + "113": [ + 189.89219665527344, + 588.1817016601562 + ], + "117": [ + 165.91908264160156, + 592.8010864257812 + ], + "121": [ + 142.88043212890625, + 598.1405029296875 + ], + "125": [ + 120.89911651611328, + 602.3465576171875 + ] + } + }, + { + "id": 4, + "xyz": [ + -19.014276683759903, + 42.06507429685914, + -1.4723383232944665 + ], + "first_frame": 83, + "mean_error": 1.0643649960700614, + "observations": { + "83": [ + 284.0224914550781, + 580.0450439453125 + ], + "87": [ + 273.11358642578125, + 581.39111328125 + ], + "91": [ + 258.260986328125, + 581.9945678710938 + ], + "95": [ + 243.56715393066406, + 582.7080688476562 + ], + "99": [ + 226.59007263183594, + 585.661865234375 + ], + "103": [ + 205.87806701660156, + 590.03125 + ], + "107": [ + 184.92926025390625, + 593.4341430664062 + ], + "111": [ + 159.1940460205078, + 596.6100463867188 + ], + "115": [ + 131.72772216796875, + 600.4990844726562 + ], + "119": [ + 101.6380386352539, + 606.5394287109375 + ], + "123": [ + 70.36128997802734, + 612.0038452148438 + ], + "127": [ + 36.78968048095703, + 618.0877075195312 + ] + } + }, + { + "id": 5, + "xyz": [ + 16.77991195120563, + 42.96647496583244, + -0.16669542077123922 + ], + "first_frame": 65, + "mean_error": 2.7291665297043366, + "observations": { + "65": [ + 810.9480590820312, + 549.2361450195312 + ], + "69": [ + 820.7054443359375, + 549.3394775390625 + ], + "73": [ + 831.4295043945312, + 550.0914306640625 + ], + "77": [ + 841.9223022460938, + 551.59228515625 + ], + "81": [ + 847.9094848632812, + 552.3490600585938 + ], + "85": [ + 856.0639038085938, + 552.1538696289062 + ], + "89": [ + 865.5806884765625, + 551.6083374023438 + ], + "93": [ + 873.41845703125, + 550.8474731445312 + ], + "97": [ + 880.2575073242188, + 552.3397827148438 + ], + "101": [ + 891.2372436523438, + 555.37109375 + ], + "105": [ + 901.3931274414062, + 559.258056640625 + ], + "109": [ + 912.41162109375, + 561.7393798828125 + ], + "113": [ + 923.8578491210938, + 564.2930297851562 + ], + "117": [ + 935.6256713867188, + 568.0001831054688 + ], + "121": [ + 949.2911376953125, + 571.4234008789062 + ], + "125": [ + 965.4530029296875, + 575.1049194335938 + ] + } + }, + { + "id": 6, + "xyz": [ + -18.286733095051343, + 42.64992590460731, + -1.3165772331085663 + ], + "first_frame": 78, + "mean_error": 1.7772767311071738, + "observations": { + "78": [ + 312.70660400390625, + 570.9238891601562 + ], + "82": [ + 307.24359130859375, + 574.2720336914062 + ], + "86": [ + 298.59185791015625, + 575.5303955078125 + ], + "90": [ + 286.608154296875, + 576.1497802734375 + ], + "94": [ + 272.98974609375, + 576.6349487304688 + ], + "98": [ + 257.13037109375, + 578.7819213867188 + ], + "102": [ + 240.21176147460938, + 582.6419677734375 + ], + "106": [ + 220.0636749267578, + 586.0458374023438 + ], + "110": [ + 196.75836181640625, + 589.2653198242188 + ], + "114": [ + 172.84996032714844, + 592.7505493164062 + ], + "118": [ + 147.43246459960938, + 598.1160888671875 + ], + "122": [ + 120.55982971191406, + 603.475830078125 + ], + "126": [ + 89.98534393310547, + 608.3961791992188 + ] + } + }, + { + "id": 8, + "xyz": [ + 16.076922921483824, + 37.50135967511251, + 0.47147101741992864 + ], + "first_frame": 98, + "mean_error": 1.0487947231673722, + "observations": { + "98": [ + 955.2408447265625, + 556.7728271484375 + ], + "102": [ + 973.1494140625, + 560.5897216796875 + ], + "106": [ + 993.5687255859375, + 564.9030151367188 + ], + "110": [ + 1011.5039672851562, + 568.4273681640625 + ], + "114": [ + 1034.2132568359375, + 572.0595703125 + ], + "118": [ + 1060.14013671875, + 576.4579467773438 + ], + "122": [ + 1088.239013671875, + 580.5911254882812 + ], + "126": [ + 1119.7435302734375, + 585.1060180664062 + ] + } + }, + { + "id": 9, + "xyz": [ + -18.131547959124017, + 41.98382559249817, + -1.3552490144959723 + ], + "first_frame": 65, + "mean_error": 2.918225206848511, + "observations": { + "65": [ + 362.40533447265625, + 567.1651611328125 + ], + "69": [ + 348.0934753417969, + 568.6539916992188 + ], + "73": [ + 331.78778076171875, + 569.7890625 + ], + "77": [ + 313.9234313964844, + 572.3311157226562 + ], + "81": [ + 297.6807861328125, + 576.4152221679688 + ], + "85": [ + 294.318115234375, + 578.3239135742188 + ], + "89": [ + 280.8163146972656, + 579.5804443359375 + ], + "93": [ + 265.39300537109375, + 580.1239013671875 + ], + "97": [ + 249.79078674316406, + 581.73779296875 + ], + "101": [ + 232.8968048095703, + 585.6143798828125 + ], + "105": [ + 212.3263702392578, + 589.3682250976562 + ], + "109": [ + 189.83897399902344, + 592.5023803710938 + ], + "113": [ + 164.06283569335938, + 595.8429565429688 + ], + "117": [ + 138.12142944335938, + 600.7738037109375 + ], + "121": [ + 108.47491455078125, + 606.7130737304688 + ], + "125": [ + 77.2574691772461, + 611.77783203125 + ] + } + }, + { + "id": 11, + "xyz": [ + -21.767438110397684, + 47.872116016725805, + -1.1861840351534794 + ], + "first_frame": 73, + "mean_error": 1.6809132991803097, + "observations": { + "73": [ + 334.108642578125, + 556.4901123046875 + ], + "77": [ + 326.2308654785156, + 558.6172485351562 + ], + "81": [ + 320.84552001953125, + 560.7366333007812 + ], + "85": [ + 314.185791015625, + 561.34912109375 + ], + "89": [ + 305.734375, + 561.4512329101562 + ], + "93": [ + 295.42694091796875, + 560.5968017578125 + ], + "97": [ + 283.4816589355469, + 561.0346069335938 + ], + "101": [ + 270.2947998046875, + 563.5402221679688 + ], + "105": [ + 256.30364990234375, + 565.629638671875 + ], + "109": [ + 240.99620056152344, + 567.1139526367188 + ], + "113": [ + 224.4208526611328, + 568.7288208007812 + ], + "117": [ + 206.38845825195312, + 571.695556640625 + ], + "121": [ + 187.44773864746094, + 575.107177734375 + ], + "125": [ + 167.26104736328125, + 577.3043212890625 + ] + } + }, + { + "id": 13, + "xyz": [ + 15.602554962576782, + 38.996729055916965, + 0.24413416448436195 + ], + "first_frame": 85, + "mean_error": 0.772430580420983, + "observations": { + "85": [ + 876.4839477539062, + 553.9638061523438 + ], + "89": [ + 888.48095703125, + 553.9783325195312 + ], + "93": [ + 900.599365234375, + 553.7977905273438 + ], + "97": [ + 914.8483276367188, + 555.8133544921875 + ], + "101": [ + 929.5337524414062, + 559.45947265625 + ], + "105": [ + 943.5868530273438, + 563.6421508789062 + ], + "109": [ + 959.0498046875, + 566.67626953125 + ], + "113": [ + 976.8504638671875, + 569.7042846679688 + ], + "117": [ + 995.7496948242188, + 574.1650390625 + ], + "121": [ + 1016.2662963867188, + 578.223388671875 + ], + "125": [ + 1039.066162109375, + 582.5454711914062 + ] + } + }, + { + "id": 16, + "xyz": [ + 15.289143105763445, + 39.370049981401564, + 0.2470077769832015 + ], + "first_frame": 65, + "mean_error": 3.623935694260191, + "observations": { + "65": [ + 812.0540771484375, + 549.2484130859375 + ], + "69": [ + 821.9686889648438, + 549.3880004882812 + ], + "73": [ + 832.9159545898438, + 550.1609497070312 + ], + "77": [ + 845.6369018554688, + 551.8272094726562 + ], + "81": [ + 859.3221435546875, + 553.4158325195312 + ], + "85": [ + 868.822265625, + 553.2572631835938 + ], + "89": [ + 879.9484252929688, + 552.9888916015625 + ], + "93": [ + 891.3121337890625, + 552.5993041992188 + ], + "97": [ + 903.5789794921875, + 554.4908447265625 + ], + "101": [ + 917.5270385742188, + 557.903564453125 + ], + "105": [ + 931.386474609375, + 562.0985107421875 + ], + "109": [ + 946.2037963867188, + 565.0130004882812 + ], + "113": [ + 961.5426025390625, + 567.7120971679688 + ], + "117": [ + 977.8767700195312, + 571.6367797851562 + ], + "121": [ + 995.905029296875, + 575.4518432617188 + ], + "125": [ + 1016.1990966796875, + 579.618896484375 + ] + } + }, + { + "id": 18, + "xyz": [ + -21.143491587365197, + 47.12262562830057, + -1.3819445412606786 + ], + "first_frame": 65, + "mean_error": 1.751217806373846, + "observations": { + "65": [ + 361.2269287109375, + 557.1697998046875 + ], + "69": [ + 347.6336669921875, + 558.0570678710938 + ], + "73": [ + 335.35870361328125, + 559.3075561523438 + ], + "77": [ + 326.57318115234375, + 561.6690063476562 + ], + "81": [ + 320.6318054199219, + 564.3594970703125 + ], + "85": [ + 313.683837890625, + 565.3240356445312 + ], + "89": [ + 304.8467102050781, + 565.5007934570312 + ], + "93": [ + 294.11724853515625, + 564.856201171875 + ], + "97": [ + 282.098388671875, + 565.4846801757812 + ], + "101": [ + 268.6297912597656, + 568.2509765625 + ], + "105": [ + 254.2989959716797, + 570.5496215820312 + ], + "109": [ + 238.75296020507812, + 572.326171875 + ], + "113": [ + 222.1617431640625, + 574.256591796875 + ], + "117": [ + 203.59349060058594, + 578.0504150390625 + ], + "121": [ + 182.94740295410156, + 582.639892578125 + ], + "125": [ + 161.45082092285156, + 586.2760620117188 + ] + } + }, + { + "id": 20, + "xyz": [ + 16.97476664616792, + 42.560330964425454, + -0.2789716435828273 + ], + "first_frame": 98, + "mean_error": 1.2737498386741328, + "observations": { + "98": [ + 893.6885375976562, + 556.2393798828125 + ], + "102": [ + 904.0194702148438, + 559.68701171875 + ], + "106": [ + 914.1578979492188, + 563.2422485351562 + ], + "110": [ + 925.89208984375, + 565.763427734375 + ], + "114": [ + 938.7131958007812, + 568.6204833984375 + ], + "118": [ + 950.08984375, + 572.589111328125 + ], + "122": [ + 965.2464599609375, + 576.26171875 + ], + "126": [ + 981.1144409179688, + 579.7578735351562 + ] + } + }, + { + "id": 21, + "xyz": [ + -20.369106504665137, + 44.57305662607727, + -1.1573328504986973 + ], + "first_frame": 65, + "mean_error": 3.924849979393755, + "observations": { + "65": [ + 358.3122253417969, + 555.1624755859375 + ], + "69": [ + 344.556640625, + 556.1998901367188 + ], + "73": [ + 329.0116271972656, + 558.2887573242188 + ], + "77": [ + 311.52716064453125, + 562.213134765625 + ], + "81": [ + 299.922119140625, + 565.6844482421875 + ], + "85": [ + 289.8446960449219, + 567.2332763671875 + ], + "89": [ + 278.59649658203125, + 567.7047729492188 + ], + "93": [ + 266.5501708984375, + 567.52294921875 + ], + "97": [ + 253.42308044433594, + 568.4410400390625 + ], + "101": [ + 238.78567504882812, + 571.9117431640625 + ], + "105": [ + 223.12867736816406, + 575.2257080078125 + ], + "109": [ + 205.2412567138672, + 577.73046875 + ], + "113": [ + 185.62576293945312, + 580.667724609375 + ], + "117": [ + 162.04782104492188, + 585.2664794921875 + ], + "121": [ + 135.59776306152344, + 590.26318359375 + ], + "125": [ + 108.73532104492188, + 594.3992919921875 + ] + } + }, + { + "id": 22, + "xyz": [ + -18.535511853615727, + 43.121735925611155, + -1.4276953271429393 + ], + "first_frame": 77, + "mean_error": 2.1086748927929246, + "observations": { + "77": [ + 316.40985107421875, + 570.3861083984375 + ], + "81": [ + 311.48284912109375, + 573.731689453125 + ], + "85": [ + 303.584228515625, + 575.503173828125 + ], + "89": [ + 294.4993896484375, + 576.4274291992188 + ], + "93": [ + 280.27056884765625, + 576.8685913085938 + ], + "97": [ + 264.71661376953125, + 578.4840698242188 + ], + "101": [ + 248.46270751953125, + 582.1105346679688 + ], + "105": [ + 229.73451232910156, + 585.5498657226562 + ], + "109": [ + 208.10409545898438, + 588.5380249023438 + ], + "113": [ + 184.74436950683594, + 591.89111328125 + ], + "117": [ + 159.66567993164062, + 596.7078247070312 + ], + "121": [ + 135.19729614257812, + 602.2726440429688 + ], + "125": [ + 108.83543395996094, + 606.9263305664062 + ] + } + }, + { + "id": 23, + "xyz": [ + 16.179799616784923, + 40.601737100001806, + -0.005319851663811536 + ], + "first_frame": 81, + "mean_error": 0.9462747498826589, + "observations": { + "81": [ + 857.3424072265625, + 554.9451904296875 + ], + "85": [ + 867.0308837890625, + 554.8927001953125 + ], + "89": [ + 878.1820068359375, + 554.662841796875 + ], + "93": [ + 889.6114501953125, + 554.3134765625 + ], + "97": [ + 901.5321655273438, + 556.21533203125 + ], + "101": [ + 914.7537231445312, + 559.6041259765625 + ], + "105": [ + 927.8991088867188, + 563.6647338867188 + ], + "109": [ + 941.8404541015625, + 566.4493408203125 + ], + "113": [ + 955.1358032226562, + 568.9769897460938 + ], + "117": [ + 970.153564453125, + 572.9325561523438 + ], + "121": [ + 986.843505859375, + 576.6801147460938 + ], + "125": [ + 1005.8047485351562, + 580.6917724609375 + ] + } + }, + { + "id": 24, + "xyz": [ + -27.373949101611483, + 49.10446861430229, + -1.5863146115131903 + ], + "first_frame": 91, + "mean_error": 0.8635000566394273, + "observations": { + "91": [ + 233.91761779785156, + 563.3776245117188 + ], + "95": [ + 221.58328247070312, + 563.2647094726562 + ], + "99": [ + 207.1542510986328, + 564.9103393554688 + ], + "103": [ + 192.96441650390625, + 567.854248046875 + ], + "107": [ + 177.18804931640625, + 569.5890502929688 + ], + "111": [ + 159.5826873779297, + 571.4511108398438 + ], + "115": [ + 140.3829803466797, + 573.5425415039062 + ], + "119": [ + 119.9801254272461, + 577.5476684570312 + ], + "123": [ + 98.47834777832031, + 580.7399291992188 + ], + "127": [ + 76.15373229980469, + 584.5380249023438 + ] + } + }, + { + "id": 25, + "xyz": [ + 13.79845325718693, + 35.744227614586414, + 0.5338655184747758 + ], + "first_frame": 65, + "mean_error": 10.596633161179799, + "observations": { + "65": [ + 814.2052001953125, + 550.2799072265625 + ], + "69": [ + 824.69287109375, + 550.8704833984375 + ], + "73": [ + 836.1095581054688, + 552.1824340820312 + ], + "77": [ + 849.0587768554688, + 554.493408203125 + ], + "81": [ + 864.0768432617188, + 556.5123901367188 + ], + "85": [ + 881.506103515625, + 557.5201416015625 + ], + "89": [ + 901.6024169921875, + 558.6788330078125 + ], + "93": [ + 924.8521728515625, + 559.0592041015625 + ], + "97": [ + 948.5809936523438, + 561.2715454101562 + ], + "101": [ + 966.1401977539062, + 565.5895385742188 + ], + "105": [ + 985.1783447265625, + 570.4718017578125 + ], + "109": [ + 1004.4823608398438, + 574.2716064453125 + ], + "113": [ + 1026.4189453125, + 578.047119140625 + ], + "117": [ + 1050.884521484375, + 583.1507568359375 + ], + "121": [ + 1078.548095703125, + 588.2022094726562 + ], + "125": [ + 1109.054443359375, + 594.09033203125 + ] + } + }, + { + "id": 26, + "xyz": [ + 18.829374669120043, + 41.40535315805509, + 0.2289926505340913 + ], + "first_frame": 108, + "mean_error": 2.180157276946362, + "observations": { + "108": [ + 977.6544189453125, + 555.98876953125 + ], + "112": [ + 991.0550537109375, + 558.7774047851562 + ], + "116": [ + 1006.205078125, + 562.6766357421875 + ], + "120": [ + 1022.8966674804688, + 566.7128295898438 + ], + "124": [ + 1041.70751953125, + 570.834228515625 + ], + "128": [ + 1064.0, + 574.0 + ] + } + }, + { + "id": 27, + "xyz": [ + 15.137294378771731, + 37.97819809215389, + 0.1859366169797885 + ], + "first_frame": 103, + "mean_error": 0.6912917637841657, + "observations": { + "103": [ + 946.73193359375, + 567.4789428710938 + ], + "107": [ + 963.122314453125, + 571.4569091796875 + ], + "111": [ + 981.5841674804688, + 574.52294921875 + ], + "115": [ + 1001.3455810546875, + 578.6474609375 + ], + "119": [ + 1023.7356567382812, + 583.4292602539062 + ], + "123": [ + 1048.28564453125, + 588.0886840820312 + ], + "127": [ + 1076.3995361328125, + 592.6614379882812 + ] + } + }, + { + "id": 29, + "xyz": [ + -18.37546155313384, + 41.83527930679661, + -0.9235530483208045 + ], + "first_frame": 65, + "mean_error": 3.3166671597513635, + "observations": { + "65": [ + 360.29278564453125, + 559.9125366210938 + ], + "69": [ + 346.0304870605469, + 561.0802612304688 + ], + "73": [ + 329.7626037597656, + 562.7693481445312 + ], + "77": [ + 311.50885009765625, + 566.1133422851562 + ], + "81": [ + 293.3333435058594, + 570.3453979492188 + ], + "85": [ + 284.0111389160156, + 572.00634765625 + ], + "89": [ + 271.93499755859375, + 572.5546264648438 + ], + "93": [ + 259.0560302734375, + 572.5028076171875 + ], + "97": [ + 244.01898193359375, + 573.591064453125 + ], + "101": [ + 225.75167846679688, + 577.2766723632812 + ], + "105": [ + 204.36383056640625, + 580.6864013671875 + ], + "109": [ + 181.2891387939453, + 583.6323852539062 + ], + "113": [ + 155.4034881591797, + 586.8306274414062 + ], + "117": [ + 127.4151611328125, + 591.4039306640625 + ], + "121": [ + 97.27574157714844, + 596.8723754882812 + ], + "125": [ + 65.97064971923828, + 601.3008422851562 + ] + } + }, + { + "id": 30, + "xyz": [ + -31.68172930054719, + 60.27866344900871, + -3.7072632404001795 + ], + "first_frame": 104, + "mean_error": 1.9375821317852264, + "observations": { + "104": [ + 278.2467956542969, + 570.0306396484375 + ], + "108": [ + 267.58734130859375, + 571.4390869140625 + ], + "112": [ + 255.8716583251953, + 572.787353515625 + ], + "116": [ + 244.54464721679688, + 575.2775268554688 + ], + "120": [ + 238.25001525878906, + 578.1229248046875 + ], + "124": [ + 225.58631896972656, + 581.064453125 + ], + "128": [ + 214.0, + 584.0 + ] + } + }, + { + "id": 32, + "xyz": [ + 16.532743532005366, + 41.54996395528177, + -0.008753879815118128 + ], + "first_frame": 65, + "mean_error": 3.733635514900445, + "observations": { + "65": [ + 812.01708984375, + 549.1627807617188 + ], + "69": [ + 821.8255615234375, + 549.2769775390625 + ], + "73": [ + 832.6458129882812, + 550.00341796875 + ], + "77": [ + 845.3294677734375, + 551.5936279296875 + ], + "81": [ + 856.4778442382812, + 552.8328857421875 + ], + "85": [ + 865.744873046875, + 552.6609497070312 + ], + "89": [ + 876.7263793945312, + 552.375 + ], + "93": [ + 887.5360717773438, + 551.8309326171875 + ], + "97": [ + 897.60791015625, + 553.4500732421875 + ], + "101": [ + 908.1300048828125, + 556.2278442382812 + ], + "105": [ + 918.8565673828125, + 559.9405517578125 + ], + "109": [ + 930.6382446289062, + 562.5938110351562 + ], + "113": [ + 944.4397583007812, + 565.3841552734375 + ], + "117": [ + 957.7884521484375, + 569.3570556640625 + ], + "121": [ + 973.2127685546875, + 573.050537109375 + ], + "125": [ + 988.0492553710938, + 576.971923828125 + ] + } + }, + { + "id": 34, + "xyz": [ + -24.53853348569464, + 46.664453349162486, + -1.5307558659578493 + ], + "first_frame": 90, + "mean_error": 2.0640757491987847, + "observations": { + "90": [ + 241.4256591796875, + 567.8672485351562 + ], + "94": [ + 229.20584106445312, + 567.8060302734375 + ], + "98": [ + 214.8823699951172, + 569.3187866210938 + ], + "102": [ + 199.39749145507812, + 572.5191040039062 + ], + "106": [ + 183.22605895996094, + 574.9970703125 + ], + "110": [ + 165.0706787109375, + 577.2527465820312 + ], + "114": [ + 144.54327392578125, + 579.9288940429688 + ], + "118": [ + 121.67289733886719, + 584.3726196289062 + ], + "122": [ + 95.52417755126953, + 589.1506958007812 + ], + "126": [ + 65.50390625, + 592.892822265625 + ] + } + }, + { + "id": 35, + "xyz": [ + 21.47531250608441, + 44.10048171583333, + 0.014151129147903714 + ], + "first_frame": 93, + "mean_error": 2.5792626948527118, + "observations": { + "93": [ + 930.0556030273438, + 544.5899658203125 + ], + "97": [ + 942.380615234375, + 545.9476928710938 + ], + "101": [ + 953.5330810546875, + 548.9091186523438 + ], + "105": [ + 965.6686401367188, + 552.4784545898438 + ], + "109": [ + 977.3023681640625, + 555.157470703125 + ], + "113": [ + 990.6519775390625, + 557.9130859375 + ], + "117": [ + 1005.842041015625, + 561.8584594726562 + ], + "121": [ + 1020.3134765625, + 565.8920288085938 + ], + "125": [ + 1036.23583984375, + 570.3609619140625 + ] + } + }, + { + "id": 36, + "xyz": [ + 17.37783552787301, + 39.89259712114069, + 0.19923637581992693 + ], + "first_frame": 95, + "mean_error": 3.4768944731501406, + "observations": { + "95": [ + 934.1295166015625, + 550.7807006835938 + ], + "99": [ + 944.4943237304688, + 554.1298217773438 + ], + "103": [ + 956.5597534179688, + 558.3230590820312 + ], + "107": [ + 971.0632934570312, + 562.2183227539062 + ], + "111": [ + 986.33544921875, + 565.2484741210938 + ], + "115": [ + 1002.8236083984375, + 569.40771484375 + ], + "119": [ + 1023.4544067382812, + 573.9491577148438 + ], + "123": [ + 1046.4144287109375, + 578.3164672851562 + ], + "127": [ + 1073.50634765625, + 582.0819702148438 + ] + } + }, + { + "id": 37, + "xyz": [ + -18.268602008291833, + 42.36724353687142, + -1.392689332760375 + ], + "first_frame": 65, + "mean_error": 2.5775838631355823, + "observations": { + "65": [ + 362.5631103515625, + 566.3482666015625 + ], + "69": [ + 348.3023986816406, + 567.5873413085938 + ], + "73": [ + 331.9560241699219, + 568.6796875 + ], + "77": [ + 314.319580078125, + 571.373779296875 + ], + "81": [ + 300.6988220214844, + 575.26806640625 + ], + "85": [ + 296.77337646484375, + 577.2816772460938 + ], + "89": [ + 284.1074523925781, + 578.5888061523438 + ], + "93": [ + 269.3548583984375, + 579.1993408203125 + ], + "97": [ + 254.73681640625, + 580.8576049804688 + ], + "101": [ + 239.109375, + 584.775634765625 + ], + "105": [ + 219.17860412597656, + 588.5003662109375 + ], + "109": [ + 196.8900146484375, + 591.5948486328125 + ], + "113": [ + 172.0953369140625, + 595.0863647460938 + ], + "117": [ + 146.24403381347656, + 599.982666015625 + ], + "121": [ + 118.18624114990234, + 605.802978515625 + ], + "125": [ + 87.4591064453125, + 610.8271484375 + ] + } + }, + { + "id": 38, + "xyz": [ + 17.152841359177238, + 44.95729559399675, + -0.33848552487557865 + ], + "first_frame": 65, + "mean_error": 2.0587936098985047, + "observations": { + "65": [ + 811.0684204101562, + 548.7688598632812 + ], + "69": [ + 820.30859375, + 548.7864990234375 + ], + "73": [ + 826.186279296875, + 549.3207397460938 + ], + "77": [ + 830.3638305664062, + 550.4412231445312 + ], + "81": [ + 834.90283203125, + 551.1414794921875 + ], + "85": [ + 840.9566040039062, + 550.8126831054688 + ], + "89": [ + 847.666748046875, + 550.1453857421875 + ], + "93": [ + 855.87646484375, + 549.444091796875 + ], + "97": [ + 864.3452758789062, + 550.8494262695312 + ], + "101": [ + 872.8654174804688, + 553.7958374023438 + ], + "105": [ + 882.84423828125, + 557.41796875 + ], + "109": [ + 891.7713623046875, + 559.5579833984375 + ], + "113": [ + 901.0318603515625, + 561.84912109375 + ], + "117": [ + 911.665283203125, + 565.4140014648438 + ], + "121": [ + 923.5341186523438, + 568.8925170898438 + ], + "125": [ + 937.8157348632812, + 572.1832275390625 + ] + } + }, + { + "id": 39, + "xyz": [ + -22.05623124293088, + 43.76573638087064, + -1.184353266960861 + ], + "first_frame": 92, + "mean_error": 1.1494645599974107, + "observations": { + "92": [ + 227.5913543701172, + 570.8638305664062 + ], + "96": [ + 213.20751953125, + 571.4306030273438 + ], + "100": [ + 197.01976013183594, + 574.2550659179688 + ], + "104": [ + 178.34585571289062, + 577.6053466796875 + ], + "108": [ + 157.3951416015625, + 580.3499755859375 + ], + "112": [ + 132.42396545410156, + 582.9154052734375 + ], + "116": [ + 106.17919921875, + 586.7451782226562 + ], + "120": [ + 76.74308776855469, + 592.3980712890625 + ], + "124": [ + 46.40812301635742, + 596.41943359375 + ], + "128": [ + 14.0, + 601.0 + ] + } + }, + { + "id": 40, + "xyz": [ + -18.45668570296189, + 42.302100722959565, + -1.02159454302906 + ], + "first_frame": 79, + "mean_error": 1.5241066695273082, + "observations": { + "79": [ + 306.10076904296875, + 568.58642578125 + ], + "83": [ + 296.6896667480469, + 571.3020629882812 + ], + "87": [ + 287.2956848144531, + 572.185302734375 + ], + "91": [ + 274.4422912597656, + 572.5901489257812 + ], + "95": [ + 260.634033203125, + 572.674072265625 + ], + "99": [ + 244.45584106445312, + 575.0424194335938 + ], + "103": [ + 225.09632873535156, + 578.9821166992188 + ], + "107": [ + 203.2588653564453, + 581.9393920898438 + ], + "111": [ + 179.02960205078125, + 585.0709228515625 + ], + "115": [ + 153.0006103515625, + 588.6007080078125 + ], + "119": [ + 126.32093048095703, + 594.203857421875 + ], + "123": [ + 96.43804931640625, + 598.9991455078125 + ], + "127": [ + 64.66395568847656, + 604.3619995117188 + ] + } + }, + { + "id": 41, + "xyz": [ + -26.996787985003046, + 49.559817812691996, + -1.5351882044359737 + ], + "first_frame": 88, + "mean_error": 1.3986058728260795, + "observations": { + "88": [ + 253.972900390625, + 560.8399658203125 + ], + "92": [ + 241.8340301513672, + 561.2799072265625 + ], + "96": [ + 229.73680114746094, + 561.1936645507812 + ], + "100": [ + 216.81382751464844, + 563.6595458984375 + ], + "104": [ + 202.790771484375, + 566.1686401367188 + ], + "108": [ + 187.66415405273438, + 567.77734375 + ], + "112": [ + 170.76779174804688, + 569.7195434570312 + ], + "116": [ + 152.1757049560547, + 572.565673828125 + ], + "120": [ + 132.29092407226562, + 576.8200073242188 + ], + "124": [ + 112.38335418701172, + 579.803466796875 + ], + "128": [ + 90.0, + 583.0 + ] + } + }, + { + "id": 42, + "xyz": [ + -21.552168802607415, + 45.03113682214466, + -1.2412944071301397 + ], + "first_frame": 83, + "mean_error": 3.135762100722013, + "observations": { + "83": [ + 283.53216552734375, + 565.8716430664062 + ], + "87": [ + 272.7909851074219, + 566.659912109375 + ], + "91": [ + 261.0198669433594, + 566.924560546875 + ], + "95": [ + 248.49786376953125, + 567.0904541015625 + ], + "99": [ + 234.5011444091797, + 569.2811279296875 + ], + "103": [ + 219.50575256347656, + 573.0025024414062 + ], + "107": [ + 202.74954223632812, + 575.7135620117188 + ], + "111": [ + 184.17251586914062, + 578.5416259765625 + ], + "115": [ + 162.2029571533203, + 581.9345092773438 + ], + "119": [ + 135.9560546875, + 587.1464233398438 + ], + "123": [ + 106.48422241210938, + 591.3782958984375 + ], + "127": [ + 76.5946273803711, + 596.423583984375 + ] + } + }, + { + "id": 44, + "xyz": [ + 18.50081053552883, + 38.12345551173555, + 0.4891120789175395 + ], + "first_frame": 109, + "mean_error": 0.6606397611447258, + "observations": { + "109": [ + 1049.836669921875, + 564.5640869140625 + ], + "113": [ + 1073.6766357421875, + 567.5244140625 + ], + "117": [ + 1099.3975830078125, + 571.5067138671875 + ], + "121": [ + 1127.753662109375, + 575.060546875 + ], + "125": [ + 1156.8223876953125, + 579.3914184570312 + ] + } + }, + { + "id": 45, + "xyz": [ + 20.84836033899293, + 43.35298434299465, + 0.2474882531061079 + ], + "first_frame": 97, + "mean_error": 1.3891706212142467, + "observations": { + "97": [ + 943.51171875, + 545.0374145507812 + ], + "101": [ + 954.704345703125, + 547.9575805664062 + ], + "105": [ + 966.7219848632812, + 551.2891845703125 + ], + "109": [ + 979.4242553710938, + 552.9130249023438 + ], + "113": [ + 993.4901733398438, + 554.674072265625 + ], + "117": [ + 1008.4275512695312, + 557.827392578125 + ], + "121": [ + 1023.4984130859375, + 560.8630981445312 + ], + "125": [ + 1042.49072265625, + 563.694091796875 + ] + } + }, + { + "id": 46, + "xyz": [ + 22.141464399046097, + 41.76515064747096, + 0.1706047118985036 + ], + "first_frame": 101, + "mean_error": 2.185460650863196, + "observations": { + "101": [ + 1005.3900756835938, + 552.142578125 + ], + "105": [ + 1020.20361328125, + 555.568115234375 + ], + "109": [ + 1035.6934814453125, + 558.328369140625 + ], + "113": [ + 1052.750244140625, + 560.943359375 + ], + "117": [ + 1070.615234375, + 564.7277221679688 + ], + "121": [ + 1090.7095947265625, + 568.1715698242188 + ], + "125": [ + 1117.43505859375, + 571.5155029296875 + ] + } + }, + { + "id": 47, + "xyz": [ + -19.598093282036352, + 44.95648532421724, + -1.6078129103174066 + ], + "first_frame": 76, + "mean_error": 3.036357521501476, + "observations": { + "76": [ + 322.10894775390625, + 567.6400756835938 + ], + "80": [ + 318.81024169921875, + 571.1497192382812 + ], + "84": [ + 311.6937255859375, + 573.6598510742188 + ], + "88": [ + 303.56591796875, + 574.4592895507812 + ], + "92": [ + 292.96136474609375, + 574.2608032226562 + ], + "96": [ + 280.2367248535156, + 574.734375 + ], + "100": [ + 266.59210205078125, + 577.2835083007812 + ], + "104": [ + 250.71693420410156, + 580.6236572265625 + ], + "108": [ + 231.06410217285156, + 583.3474731445312 + ], + "112": [ + 210.71189880371094, + 586.1456909179688 + ], + "116": [ + 188.4401397705078, + 590.1130981445312 + ], + "120": [ + 164.59225463867188, + 595.4266967773438 + ], + "124": [ + 143.54054260253906, + 599.41357421875 + ], + "128": [ + 127.0, + 604.0 + ] + } + }, + { + "id": 48, + "xyz": [ + 15.496808821979046, + 38.6087264128368, + 0.21741146534056793 + ], + "first_frame": 87, + "mean_error": 0.6249553828564705, + "observations": { + "87": [ + 886.0328979492188, + 555.8832397460938 + ], + "91": [ + 898.177978515625, + 555.6658325195312 + ], + "95": [ + 911.5476684570312, + 556.5069580078125 + ], + "99": [ + 926.0353393554688, + 559.4392700195312 + ], + "103": [ + 940.3031616210938, + 563.5458984375 + ], + "107": [ + 956.7512817382812, + 567.4309692382812 + ], + "111": [ + 975.3442993164062, + 570.3956298828125 + ], + "115": [ + 993.559814453125, + 574.4577026367188 + ], + "119": [ + 1014.1914672851562, + 578.9932250976562 + ], + "123": [ + 1036.744140625, + 583.2321166992188 + ], + "127": [ + 1062.3948974609375, + 587.150390625 + ] + } + }, + { + "id": 49, + "xyz": [ + -22.65674529628515, + 44.58381963306989, + -1.25014795339236 + ], + "first_frame": 91, + "mean_error": 2.169514492526233, + "observations": { + "91": [ + 233.93797302246094, + 569.5260009765625 + ], + "95": [ + 220.80789184570312, + 569.6355590820312 + ], + "99": [ + 205.41734313964844, + 571.6969604492188 + ], + "103": [ + 189.50274658203125, + 575.3366088867188 + ], + "107": [ + 170.73187255859375, + 577.6971435546875 + ], + "111": [ + 149.11553955078125, + 580.330322265625 + ], + "115": [ + 124.70336151123047, + 583.287841796875 + ], + "119": [ + 96.56758117675781, + 588.3577270507812 + ], + "123": [ + 67.08563232421875, + 592.722412109375 + ], + "127": [ + 35.68373107910156, + 597.3829345703125 + ] + } + }, + { + "id": 50, + "xyz": [ + -28.607552240489625, + 47.701565619563375, + -1.620554560070345 + ], + "first_frame": 100, + "mean_error": 1.3785644736190692, + "observations": { + "100": [ + 161.48500061035156, + 569.8181762695312 + ], + "104": [ + 143.15853881835938, + 572.4514770507812 + ], + "108": [ + 122.48681640625, + 574.59375 + ], + "112": [ + 101.18378448486328, + 576.5155029296875 + ], + "116": [ + 78.25852966308594, + 579.7111206054688 + ], + "120": [ + 51.69721221923828, + 584.6553344726562 + ], + "124": [ + 25.85906982421875, + 587.6212768554688 + ], + "128": [ + 6.0, + 590.0 + ] + } + }, + { + "id": 51, + "xyz": [ + 21.266059280580198, + 42.30099768249127, + 0.23542505943440495 + ], + "first_frame": 98, + "mean_error": 2.3754147239810566, + "observations": { + "98": [ + 966.4677124023438, + 546.5059204101562 + ], + "102": [ + 984.2881469726562, + 549.7020263671875 + ], + "106": [ + 998.01220703125, + 553.3193359375 + ], + "110": [ + 1011.9197387695312, + 555.7745361328125 + ], + "114": [ + 1027.2236328125, + 558.753662109375 + ], + "118": [ + 1043.216064453125, + 562.8291015625 + ], + "122": [ + 1061.99169921875, + 566.4115600585938 + ], + "126": [ + 1084.4708251953125, + 569.9860229492188 + ] + } + }, + { + "id": 52, + "xyz": [ + -17.390569908945043, + 49.58304746278673, + -1.714388422948873 + ], + "first_frame": 65, + "mean_error": 2.831327784919386, + "observations": { + "65": [ + 415.68389892578125, + 557.6670532226562 + ], + "69": [ + 415.4634704589844, + 557.0487670898438 + ], + "73": [ + 408.26922607421875, + 558.22314453125 + ], + "77": [ + 400.1523132324219, + 560.8299560546875 + ], + "81": [ + 392.7802429199219, + 563.6773681640625 + ], + "85": [ + 384.5328674316406, + 564.3917236328125 + ], + "89": [ + 377.0105895996094, + 564.1640014648438 + ], + "93": [ + 368.14178466796875, + 563.141357421875 + ], + "97": [ + 365.7397155761719, + 562.996337890625 + ], + "101": [ + 358.05169677734375, + 565.952880859375 + ], + "105": [ + 349.1123046875, + 568.5451049804688 + ], + "109": [ + 337.2976989746094, + 570.63623046875 + ], + "113": [ + 328.1430358886719, + 572.8989868164062 + ], + "117": [ + 314.9190368652344, + 576.9450073242188 + ], + "121": [ + 298.03662109375, + 581.6646118164062 + ], + "125": [ + 278.1358947753906, + 585.9130249023438 + ] + } + }, + { + "id": 54, + "xyz": [ + 23.17627872574728, + 42.89786672630251, + 0.20684914687030614 + ], + "first_frame": 91, + "mean_error": 2.213366348077136, + "observations": { + "91": [ + 965.462158203125, + 544.0169067382812 + ], + "95": [ + 979.2299194335938, + 543.8377075195312 + ], + "99": [ + 993.0732421875, + 547.315185546875 + ], + "103": [ + 1007.2489013671875, + 550.5166015625 + ], + "107": [ + 1022.1431274414062, + 553.3682250976562 + ], + "111": [ + 1037.7252197265625, + 555.5564575195312 + ], + "115": [ + 1054.6134033203125, + 558.8546142578125 + ], + "119": [ + 1072.30517578125, + 562.5789794921875 + ], + "123": [ + 1092.0574951171875, + 566.1558227539062 + ], + "127": [ + 1113.38671875, + 569.2803955078125 + ] + } + }, + { + "id": 55, + "xyz": [ + -19.34894174402045, + 46.150531989367416, + -1.1294595228266053 + ], + "first_frame": 106, + "mean_error": 1.5254203107433602, + "observations": { + "106": [ + 263.58026123046875, + 569.6804809570312 + ], + "110": [ + 251.69857788085938, + 571.3079833984375 + ], + "114": [ + 233.1816864013672, + 573.4764404296875 + ], + "118": [ + 212.4628448486328, + 577.4923095703125 + ], + "122": [ + 195.22805786132812, + 581.6675415039062 + ], + "126": [ + 172.43150329589844, + 585.2174072265625 + ] + } + }, + { + "id": 56, + "xyz": [ + -18.055258761013164, + 41.741339174936115, + -1.7061669850298624 + ], + "first_frame": 65, + "mean_error": 2.8020670698554144, + "observations": { + "65": [ + 360.6152648925781, + 572.092041015625 + ], + "69": [ + 346.622802734375, + 573.8156127929688 + ], + "73": [ + 330.4238586425781, + 575.5634765625 + ], + "77": [ + 312.5867919921875, + 578.9063110351562 + ], + "81": [ + 294.8673400878906, + 583.4581909179688 + ], + "85": [ + 290.3827819824219, + 585.1128540039062 + ], + "89": [ + 276.5985412597656, + 586.5015258789062 + ], + "93": [ + 261.97479248046875, + 586.8966674804688 + ], + "97": [ + 246.05775451660156, + 588.7883911132812 + ], + "101": [ + 228.5658416748047, + 593.040771484375 + ], + "105": [ + 208.32518005371094, + 597.4329833984375 + ], + "109": [ + 185.66395568847656, + 601.3983764648438 + ], + "113": [ + 158.96295166015625, + 605.6453857421875 + ], + "117": [ + 131.47784423828125, + 612.0332641601562 + ], + "121": [ + 100.41849517822266, + 619.3967895507812 + ], + "125": [ + 68.11421203613281, + 624.739013671875 + ] + } + }, + { + "id": 57, + "xyz": [ + -27.029139462365574, + 54.710817167790424, + -3.0586963153692093 + ], + "first_frame": 103, + "mean_error": 1.6458635592090098, + "observations": { + "103": [ + 275.528564453125, + 573.2672729492188 + ], + "107": [ + 265.3802185058594, + 575.01953125 + ], + "111": [ + 253.25308227539062, + 576.9498901367188 + ], + "115": [ + 239.44607543945312, + 579.2200317382812 + ], + "119": [ + 226.44247436523438, + 583.6234130859375 + ], + "123": [ + 211.07894897460938, + 586.7526245117188 + ], + "127": [ + 203.5115203857422, + 589.7481079101562 + ] + } + }, + { + "id": 58, + "xyz": [ + -21.384231450267094, + 46.129997169115526, + -1.2659309639019607 + ], + "first_frame": 78, + "mean_error": 1.0362775623720994, + "observations": { + "78": [ + 311.07171630859375, + 561.8899536132812 + ], + "82": [ + 305.0684814453125, + 564.544189453125 + ], + "86": [ + 295.50604248046875, + 565.451171875 + ], + "90": [ + 284.6340026855469, + 565.55517578125 + ], + "94": [ + 272.7060241699219, + 565.2236938476562 + ], + "98": [ + 259.4957275390625, + 566.439453125 + ], + "102": [ + 245.46685791015625, + 569.6473999023438 + ], + "106": [ + 229.4947509765625, + 572.2582397460938 + ], + "110": [ + 212.02285766601562, + 574.3248901367188 + ], + "114": [ + 192.371826171875, + 576.8543090820312 + ], + "118": [ + 170.52284240722656, + 581.2698974609375 + ], + "122": [ + 148.89242553710938, + 585.3895263671875 + ], + "126": [ + 126.62742614746094, + 588.94580078125 + ] + } + }, + { + "id": 59, + "xyz": [ + 15.28156782189065, + 39.08251149492431, + 0.13275092050707557 + ], + "first_frame": 82, + "mean_error": 0.6013008060961786, + "observations": { + "82": [ + 861.9283447265625, + 556.2703247070312 + ], + "86": [ + 872.5762939453125, + 556.5128173828125 + ], + "90": [ + 884.04443359375, + 556.05810546875 + ], + "94": [ + 897.1170654296875, + 556.4569091796875 + ], + "98": [ + 910.2999877929688, + 558.9847412109375 + ], + "102": [ + 924.0164184570312, + 562.7703247070312 + ], + "106": [ + 939.1328125, + 566.7780151367188 + ], + "110": [ + 955.291748046875, + 569.7943115234375 + ], + "114": [ + 972.2649536132812, + 573.2603149414062 + ], + "118": [ + 990.192138671875, + 577.4926147460938 + ], + "122": [ + 1010.1213989257812, + 581.4456176757812 + ], + "126": [ + 1032.50732421875, + 585.5142211914062 + ] + } + }, + { + "id": 60, + "xyz": [ + -22.284799094561706, + 47.247721118254745, + -1.143217304123062 + ], + "first_frame": 78, + "mean_error": 0.8277944140343093, + "observations": { + "78": [ + 311.71954345703125, + 558.5755615234375 + ], + "82": [ + 306.283447265625, + 561.13916015625 + ], + "86": [ + 296.9109802246094, + 561.7632446289062 + ], + "90": [ + 286.27398681640625, + 561.726806640625 + ], + "94": [ + 274.5967712402344, + 561.1482543945312 + ], + "98": [ + 261.65228271484375, + 562.2048950195312 + ], + "102": [ + 247.80650329589844, + 565.0404663085938 + ], + "106": [ + 232.9857635498047, + 567.0614624023438 + ], + "110": [ + 216.71505737304688, + 568.680908203125 + ], + "114": [ + 198.52285766601562, + 570.8416137695312 + ], + "118": [ + 179.19493103027344, + 574.3731689453125 + ], + "122": [ + 158.64129638671875, + 578.0240478515625 + ], + "126": [ + 137.65057373046875, + 580.599365234375 + ] + } + } +] \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/intersection_plane_fit.json b/astra_scene_test/Scene_04/reference/intersection_plane_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..8aed1d9e5dd61c6bc42fcc0c0285b7af5fae07ae --- /dev/null +++ b/astra_scene_test/Scene_04/reference/intersection_plane_fit.json @@ -0,0 +1,33 @@ +{ + "coefficients": [ + 0.03158888205030772, + -0.04587235220107175, + 1.5145111108674505 + ], + "ids": [ + 1, + 4, + 6, + 8, + 11, + 13, + 18, + 20, + 23, + 24, + 27, + 39, + 40, + 41, + 44, + 45, + 48, + 50, + 55, + 57, + 58, + 59, + 60 + ], + "rms": 0.30196119282013045 +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/metadata.json b/astra_scene_test/Scene_04/reference/metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..bb0f0b2790f70e959feaafe7e98528770ea7e8ba --- /dev/null +++ b/astra_scene_test/Scene_04/reference/metadata.json @@ -0,0 +1,1421 @@ +{ + "width": 1280, + "height": 720, + "fps_num": 30, + "fps_den": 1, + "frame_count": 128, + "duration_seconds": 4.266666666666667, + "audio_streams": 0, + "source_sha256": "d52093fef79db4c65de71b17b177fce4df5234a30cf52e91ec8814a8b36a386b", + "measurement": "FFmpeg full decode; stream time base 1/15360; constant rate 30/1; no audio stream.", + "frames": [ + { + "frame": 1, + "time_seconds": 0.0, + "path": "reference/frames/0001.png", + "mean_rgb": [ + 70.966, + 68.467, + 52.827 + ], + "sha256": "bd77ab74b3f694e0a80c53f7927faea08079bf5b0b2876f608628bda13ea820a" + }, + { + "frame": 2, + "time_seconds": 0.03333333333333333, + "path": "reference/frames/0002.png", + "mean_rgb": [ + 69.858, + 67.814, + 51.989 + ], + "sha256": "24c579eecc6d430d9dc9ee0d2e56ad681ba8b42672582bfb6a6181b0df222bb4" + }, + { + "frame": 3, + "time_seconds": 0.06666666666666667, + "path": "reference/frames/0003.png", + "mean_rgb": [ + 68.971, + 67.021, + 50.833 + ], + "sha256": "d0194b87a761c6de09a35b5ecf4196a0e938967b1d65bda231695c313355fdc5" + }, + { + "frame": 4, + "time_seconds": 0.1, + "path": "reference/frames/0004.png", + "mean_rgb": [ + 68.539, + 66.634, + 50.282 + ], + "sha256": "18bb3bae82803195f25c3de5f751b136301f3ab86a67d91941914f215ff9fcd9" + }, + { + "frame": 5, + "time_seconds": 0.13333333333333333, + "path": "reference/frames/0005.png", + "mean_rgb": [ + 67.357, + 65.985, + 51.19 + ], + "sha256": "46e29ed536604f1fb3dd9c8c1284a5dc78f957d91ffe59f6fcaf17cfdb5ae833" + }, + { + "frame": 6, + "time_seconds": 0.16666666666666666, + "path": "reference/frames/0006.png", + "mean_rgb": [ + 67.441, + 65.658, + 49.933 + ], + "sha256": "01842c2bef7e91dd58bdad650dbb81810f0b001e36c24bc501f27adc062bd3ad" + }, + { + "frame": 7, + "time_seconds": 0.2, + "path": "reference/frames/0007.png", + "mean_rgb": [ + 66.759, + 64.841, + 49.849 + ], + "sha256": "ee00eeb5bf5295d1c0b74dfbf84d24fabf988c97a28a3d6c9ee31c368d9238c2" + }, + { + "frame": 8, + "time_seconds": 0.23333333333333334, + "path": "reference/frames/0008.png", + "mean_rgb": [ + 65.385, + 63.907, + 48.812 + ], + "sha256": "a94ba00fd7e64be4514a88ec46a80cc7b1f956947c3e128492a13c616746d9a3" + }, + { + "frame": 9, + "time_seconds": 0.26666666666666666, + "path": "reference/frames/0009.png", + "mean_rgb": [ + 64.706, + 62.734, + 46.865 + ], + "sha256": "10d35023a9b09e882a6e2ef21c0d56e2f558e305f131b7b493de688c2fa0649d" + }, + { + "frame": 10, + "time_seconds": 0.3, + "path": "reference/frames/0010.png", + "mean_rgb": [ + 65.215, + 63.14, + 47.819 + ], + "sha256": "453d6bb972a9c7366ed03f5e02181b67480e4bd9537c55380a49038aa37645ce" + }, + { + "frame": 11, + "time_seconds": 0.3333333333333333, + "path": "reference/frames/0011.png", + "mean_rgb": [ + 65.053, + 62.549, + 46.784 + ], + "sha256": "7c0a0a66b53e2ba0b672e6f7d94eedc5193e61c4577622e19c9ab51936cb58e2" + }, + { + "frame": 12, + "time_seconds": 0.36666666666666664, + "path": "reference/frames/0012.png", + "mean_rgb": [ + 63.668, + 62.472, + 47.025 + ], + "sha256": "fdd1553691dbb566b9fb1607e362a76681bd204e7b3dbff171df4dc593d8feaf" + }, + { + "frame": 13, + "time_seconds": 0.4, + "path": "reference/frames/0013.png", + "mean_rgb": [ + 64.897, + 62.404, + 46.576 + ], + "sha256": "cf97b93496af9437b666fc1a18220f0aad8ce06750df88b4a330ba883b3faed2" + }, + { + "frame": 14, + "time_seconds": 0.43333333333333335, + "path": "reference/frames/0014.png", + "mean_rgb": [ + 64.089, + 61.879, + 47.484 + ], + "sha256": "4a8e6969dd57a20718fa0b75c8ef469db3ddfb15265caf2915174478f235cf5a" + }, + { + "frame": 15, + "time_seconds": 0.4666666666666667, + "path": "reference/frames/0015.png", + "mean_rgb": [ + 63.069, + 61.362, + 46.304 + ], + "sha256": "f4c9fedc5e623548b6dfd0375b0d4d655e854738ee694f37f3f2db51a0d96466" + }, + { + "frame": 16, + "time_seconds": 0.5, + "path": "reference/frames/0016.png", + "mean_rgb": [ + 62.484, + 60.114, + 45.486 + ], + "sha256": "547c0c3ebad72e2f328a23640b6cc49917e7e660d54d580c7dab070277e3788e" + }, + { + "frame": 17, + "time_seconds": 0.5333333333333333, + "path": "reference/frames/0017.png", + "mean_rgb": [ + 61.47, + 59.243, + 45.154 + ], + "sha256": "67575874c82c407b9caa9e96641ee366cd9078c5e930408a48908f5be21d8465" + }, + { + "frame": 18, + "time_seconds": 0.5666666666666667, + "path": "reference/frames/0018.png", + "mean_rgb": [ + 61.615, + 59.071, + 45.073 + ], + "sha256": "6d2d9d5542aeb1029d97e5ad7f3a3766ec98fd6e6241f303df4b2b08b5520ee5" + }, + { + "frame": 19, + "time_seconds": 0.6, + "path": "reference/frames/0019.png", + "mean_rgb": [ + 60.521, + 57.812, + 44.055 + ], + "sha256": "6b3ad7c99d58b163e9a7aeaa995b8752464021efad2799e2fae675f83bc41f3e" + }, + { + "frame": 20, + "time_seconds": 0.6333333333333333, + "path": "reference/frames/0020.png", + "mean_rgb": [ + 61.417, + 58.485, + 44.409 + ], + "sha256": "c6a7f7de056ffb6cba7fc7f569a252e2200692850487d1f081ef70840739109d" + }, + { + "frame": 21, + "time_seconds": 0.6666666666666666, + "path": "reference/frames/0021.png", + "mean_rgb": [ + 61.621, + 58.683, + 44.414 + ], + "sha256": "95f8364a01699d9c8dd1b979dcb7d85bdad51b386d3dabc002cfc40039cc9ed8" + }, + { + "frame": 22, + "time_seconds": 0.7, + "path": "reference/frames/0022.png", + "mean_rgb": [ + 62.208, + 59.348, + 45.147 + ], + "sha256": "444df4b9a475724b218c2a341f4187d1e5392158f2a712d8216948ce5c0c4fca" + }, + { + "frame": 23, + "time_seconds": 0.7333333333333333, + "path": "reference/frames/0023.png", + "mean_rgb": [ + 59.649, + 58.057, + 46.896 + ], + "sha256": "4de2c6684eac49d753d61ae3a3946b8b21168b49e4fd4a455f05823ca4ec2b08" + }, + { + "frame": 24, + "time_seconds": 0.7666666666666667, + "path": "reference/frames/0024.png", + "mean_rgb": [ + 60.584, + 57.945, + 44.737 + ], + "sha256": "24d1bbb26fe306da6374d4296db826e9037ac0b5b228ae2d8408e51d921066c1" + }, + { + "frame": 25, + "time_seconds": 0.8, + "path": "reference/frames/0025.png", + "mean_rgb": [ + 61.465, + 58.811, + 46.229 + ], + "sha256": "aea7d5697c83d07033bd8196504ac64f1ef09cde3584dcaf319fd01e98861975" + }, + { + "frame": 26, + "time_seconds": 0.8333333333333334, + "path": "reference/frames/0026.png", + "mean_rgb": [ + 60.402, + 57.923, + 44.473 + ], + "sha256": "4873f4af7a0283fc168ae25f0ae3852e9ec56f4f350f48522558272a820a32a4" + }, + { + "frame": 27, + "time_seconds": 0.8666666666666667, + "path": "reference/frames/0027.png", + "mean_rgb": [ + 61.211, + 59.032, + 45.162 + ], + "sha256": "b945bc341bad0f7e962afd3c4ba382b9d75f5037858e7f9017edc0c6fc0661f9" + }, + { + "frame": 28, + "time_seconds": 0.9, + "path": "reference/frames/0028.png", + "mean_rgb": [ + 61.812, + 59.056, + 43.712 + ], + "sha256": "dc483afb27da57895a0c3ecd3cf070797f2b478e7eafec9beb90d1810e5a83e6" + }, + { + "frame": 29, + "time_seconds": 0.9333333333333333, + "path": "reference/frames/0029.png", + "mean_rgb": [ + 59.831, + 58.738, + 47.664 + ], + "sha256": "39cde1ded354e2f5a69d7788720070b40255c6f12708560104c5c01be8fe3b01" + }, + { + "frame": 30, + "time_seconds": 0.9666666666666667, + "path": "reference/frames/0030.png", + "mean_rgb": [ + 60.652, + 59.156, + 46.425 + ], + "sha256": "f4efa6346e31dc4b1be4889ac0315daaf993c76f8e60583fe3b7986525cb7492" + }, + { + "frame": 31, + "time_seconds": 1.0, + "path": "reference/frames/0031.png", + "mean_rgb": [ + 57.356, + 56.065, + 43.014 + ], + "sha256": "ccb6557fc0f384e1f1ec05ac498973f1261f0c487044074dc501f96dfff83ed4" + }, + { + "frame": 32, + "time_seconds": 1.0333333333333334, + "path": "reference/frames/0032.png", + "mean_rgb": [ + 59.257, + 57.121, + 43.639 + ], + "sha256": "8dcd8abafcea441b739c9a08af940016abb81103eb2ba03d19b49173d221564d" + }, + { + "frame": 33, + "time_seconds": 1.0666666666666667, + "path": "reference/frames/0033.png", + "mean_rgb": [ + 59.298, + 56.554, + 41.98 + ], + "sha256": "53c2489637a3515d206e0a4defaa0dbfbbbdd1e764e27ab9ba5a09873bf184be" + }, + { + "frame": 34, + "time_seconds": 1.1, + "path": "reference/frames/0034.png", + "mean_rgb": [ + 58.137, + 57.374, + 45.011 + ], + "sha256": "ca6bf733f19a2a61ab2c27dea17405fa3db1f4d5219caec954609731cbe7dd9c" + }, + { + "frame": 35, + "time_seconds": 1.1333333333333333, + "path": "reference/frames/0035.png", + "mean_rgb": [ + 62.22, + 59.307, + 44.551 + ], + "sha256": "84150435f6953aae2aa4e65b44baa2471a866f250e2cd29cb38f51fcd47642a6" + }, + { + "frame": 36, + "time_seconds": 1.1666666666666667, + "path": "reference/frames/0036.png", + "mean_rgb": [ + 60.994, + 59.209, + 45.119 + ], + "sha256": "d4ad37dd4ac1997a0b703e3b8ae41dd2412e36d65032b1076bced3bc4e910a86" + }, + { + "frame": 37, + "time_seconds": 1.2, + "path": "reference/frames/0037.png", + "mean_rgb": [ + 58.122, + 57.703, + 45.928 + ], + "sha256": "8c0241dd7c6aa266bce1c6abb3012aa2f27ba151c7a666b7acca0e3150db05e4" + }, + { + "frame": 38, + "time_seconds": 1.2333333333333334, + "path": "reference/frames/0038.png", + "mean_rgb": [ + 58.205, + 56.801, + 42.792 + ], + "sha256": "10913497cc5dedecce56a90326851f706f0f767e4b61fe79456a1f5e4d14c607" + }, + { + "frame": 39, + "time_seconds": 1.2666666666666666, + "path": "reference/frames/0039.png", + "mean_rgb": [ + 62.243, + 59.856, + 44.99 + ], + "sha256": "7449095f800ba25100679f53372da99b70ae8529f96a8a1f8617312edf947894" + }, + { + "frame": 40, + "time_seconds": 1.3, + "path": "reference/frames/0040.png", + "mean_rgb": [ + 61.295, + 59.73, + 45.58 + ], + "sha256": "3bba358fab0cac6a05d6a4588ab428825760beb85e14095d22a3fc27043e10c1" + }, + { + "frame": 41, + "time_seconds": 1.3333333333333333, + "path": "reference/frames/0041.png", + "mean_rgb": [ + 60.803, + 59.627, + 46.136 + ], + "sha256": "b9b4a511ff67ec7965909b3076154f3343f1106faf62c61c0166e93e36a5e395" + }, + { + "frame": 42, + "time_seconds": 1.3666666666666667, + "path": "reference/frames/0042.png", + "mean_rgb": [ + 59.705, + 59.717, + 48.222 + ], + "sha256": "c60ea9202bffc68a32f931319daae17f3700bf3e2b693e235ec0120659564d3f" + }, + { + "frame": 43, + "time_seconds": 1.4, + "path": "reference/frames/0043.png", + "mean_rgb": [ + 60.576, + 59.685, + 46.363 + ], + "sha256": "dfc766ea32a438f7f239fc008d0ff93c1cec14538d34afe76ac68351868b9a5c" + }, + { + "frame": 44, + "time_seconds": 1.4333333333333333, + "path": "reference/frames/0044.png", + "mean_rgb": [ + 60.086, + 59.898, + 47.881 + ], + "sha256": "6aaad1e340e086fbf0136cea68d4f308c41258951da8534026df57452fcdcff9" + }, + { + "frame": 45, + "time_seconds": 1.4666666666666666, + "path": "reference/frames/0045.png", + "mean_rgb": [ + 60.568, + 59.885, + 46.067 + ], + "sha256": "65bbdb23822c64ca03ec3c845be71c38bffd748f14d6f75dc9c4a78a248f1957" + }, + { + "frame": 46, + "time_seconds": 1.5, + "path": "reference/frames/0046.png", + "mean_rgb": [ + 60.649, + 59.984, + 47.301 + ], + "sha256": "0987ef0a7505c51503f269828065fc7e21dbccf33b4ace83133f7c4cdd435dd6" + }, + { + "frame": 47, + "time_seconds": 1.5333333333333334, + "path": "reference/frames/0047.png", + "mean_rgb": [ + 59.217, + 60.033, + 49.369 + ], + "sha256": "31018be6406dbca7ac5fc6975a1b4307b3151c44fcddab71b22bc460318c92d6" + }, + { + "frame": 48, + "time_seconds": 1.5666666666666667, + "path": "reference/frames/0048.png", + "mean_rgb": [ + 60.635, + 60.233, + 48.105 + ], + "sha256": "fd3005d1b78239dc3a6c267b00295cf36b19b6fc88913766bbd3ae4d59ae237f" + }, + { + "frame": 49, + "time_seconds": 1.6, + "path": "reference/frames/0049.png", + "mean_rgb": [ + 60.539, + 60.449, + 47.12 + ], + "sha256": "0fee20eef472714392a7679dfe582bc2516082c74e789eef2bc374eb4c59943d" + }, + { + "frame": 50, + "time_seconds": 1.6333333333333333, + "path": "reference/frames/0050.png", + "mean_rgb": [ + 60.033, + 60.456, + 49.431 + ], + "sha256": "afd2e7890aa727fe9afc56eb05af4f2ed78965c746767556133d9e880cdd9f72" + }, + { + "frame": 51, + "time_seconds": 1.6666666666666667, + "path": "reference/frames/0051.png", + "mean_rgb": [ + 61.123, + 61.039, + 46.575 + ], + "sha256": "85b09ee12dcf9799931fd2f21363f062b845c994c00bcf7fe75542bef3aa2a10" + }, + { + "frame": 52, + "time_seconds": 1.7, + "path": "reference/frames/0052.png", + "mean_rgb": [ + 60.723, + 61.159, + 48.181 + ], + "sha256": "5b06826acc100b3a4e142d19ecd67227faf3dbe0f9ca77d39430cba36b68c4bb" + }, + { + "frame": 53, + "time_seconds": 1.7333333333333334, + "path": "reference/frames/0053.png", + "mean_rgb": [ + 59.966, + 61.197, + 48.221 + ], + "sha256": "c799d727adeb252bacdbab1f0bd786fa1a283bbb937f32d90e1d6ecca9c37f04" + }, + { + "frame": 54, + "time_seconds": 1.7666666666666666, + "path": "reference/frames/0054.png", + "mean_rgb": [ + 60.49, + 61.329, + 48.269 + ], + "sha256": "0ba4e58ca121d6c138783e5a0b2d95e4f95a6135cda5f72e1e2e77bc1828ccb2" + }, + { + "frame": 55, + "time_seconds": 1.8, + "path": "reference/frames/0055.png", + "mean_rgb": [ + 59.555, + 61.457, + 51.276 + ], + "sha256": "ec81b25b8d93dc7c01ad442f6bb49924b1066def4dcd9d71681da17e086190cd" + }, + { + "frame": 56, + "time_seconds": 1.8333333333333333, + "path": "reference/frames/0056.png", + "mean_rgb": [ + 59.375, + 61.603, + 50.238 + ], + "sha256": "6a411f4072ddbfe20351a52b6e94884f8acdaa0136c9479cd1ec37e473c9ef1c" + }, + { + "frame": 57, + "time_seconds": 1.8666666666666667, + "path": "reference/frames/0057.png", + "mean_rgb": [ + 59.611, + 61.708, + 50.663 + ], + "sha256": "b68c83836ce7b9e71d5aada9af93e828978ca919b3ea10d10f9ca4d5142cdd7d" + }, + { + "frame": 58, + "time_seconds": 1.9, + "path": "reference/frames/0058.png", + "mean_rgb": [ + 59.547, + 61.84, + 50.986 + ], + "sha256": "5fa890e0cde37c779cfc2565a7d8418cec24b6e0d046db13aa058b2390ae333b" + }, + { + "frame": 59, + "time_seconds": 1.9333333333333333, + "path": "reference/frames/0059.png", + "mean_rgb": [ + 60.435, + 62.339, + 52.024 + ], + "sha256": "6df4b8f62375a6af416b7475e6f358e98042407d8a076fd669dccccf78a7824a" + }, + { + "frame": 60, + "time_seconds": 1.9666666666666666, + "path": "reference/frames/0060.png", + "mean_rgb": [ + 60.447, + 62.531, + 50.663 + ], + "sha256": "4c4ee690e2ef44ddb36ae4fbc407aa74421e5f3917041605ab6828e2492943a1" + }, + { + "frame": 61, + "time_seconds": 2.0, + "path": "reference/frames/0061.png", + "mean_rgb": [ + 60.865, + 62.887, + 51.533 + ], + "sha256": "c0294d6b2236f420eb31471a805828b813fa55e4b58bcb4898d53a0dd4e40622" + }, + { + "frame": 62, + "time_seconds": 2.033333333333333, + "path": "reference/frames/0062.png", + "mean_rgb": [ + 60.857, + 63.149, + 51.222 + ], + "sha256": "3151ebdc0efac71bf1adc35bd6067e67e3694b592c39a1f19c6cc79f0b150359" + }, + { + "frame": 63, + "time_seconds": 2.066666666666667, + "path": "reference/frames/0063.png", + "mean_rgb": [ + 60.665, + 63.108, + 51.894 + ], + "sha256": "add87a0620d8b222877e0ba59fb8b64a5955107e77d5b30cfaccb1dbfebdc6ec" + }, + { + "frame": 64, + "time_seconds": 2.1, + "path": "reference/frames/0064.png", + "mean_rgb": [ + 60.63, + 63.465, + 52.266 + ], + "sha256": "31c1a86265baa7645908b11f674bcea8616c5d00a9b693612c4a9a219e10b0a5" + }, + { + "frame": 65, + "time_seconds": 2.1333333333333333, + "path": "reference/frames/0065.png", + "mean_rgb": [ + 59.55, + 63.069, + 53.044 + ], + "sha256": "da57055ea3a479642dfe91a55e6da0156bd297cbc0415b95b635f5a8246eef7c" + }, + { + "frame": 66, + "time_seconds": 2.1666666666666665, + "path": "reference/frames/0066.png", + "mean_rgb": [ + 60.101, + 63.674, + 52.45 + ], + "sha256": "07df76df2a2e24c6a5786da8699451d5e5669f3895745f645db2bc3617f96b53" + }, + { + "frame": 67, + "time_seconds": 2.2, + "path": "reference/frames/0067.png", + "mean_rgb": [ + 60.201, + 63.511, + 52.824 + ], + "sha256": "bfe08e80c86ee21a8669e1cfc61b3fbf89c30ce5d7319fe02fab1ac81965b734" + }, + { + "frame": 68, + "time_seconds": 2.2333333333333334, + "path": "reference/frames/0068.png", + "mean_rgb": [ + 60.906, + 63.854, + 53.403 + ], + "sha256": "545517a2d0622bc434dd6d9637cb807de69957f4c2544c4f2a40aca6c0de1380" + }, + { + "frame": 69, + "time_seconds": 2.2666666666666666, + "path": "reference/frames/0069.png", + "mean_rgb": [ + 60.361, + 63.815, + 53.057 + ], + "sha256": "d6d31e029bd62db37ca02b9534838bfec8a139af13026c6e001abcddb5fd0c0c" + }, + { + "frame": 70, + "time_seconds": 2.3, + "path": "reference/frames/0070.png", + "mean_rgb": [ + 60.856, + 64.308, + 53.598 + ], + "sha256": "1bdfa9220d19a44af431e839bc913cfa968111afaaacff16bbc2f46923bc1328" + }, + { + "frame": 71, + "time_seconds": 2.3333333333333335, + "path": "reference/frames/0071.png", + "mean_rgb": [ + 60.769, + 64.053, + 52.625 + ], + "sha256": "f8b516cb34e18feca8c269883586cc5a009ce0be7704f9b00661204780e77ed5" + }, + { + "frame": 72, + "time_seconds": 2.3666666666666667, + "path": "reference/frames/0072.png", + "mean_rgb": [ + 61.279, + 64.668, + 53.609 + ], + "sha256": "f84b084a7d552b63df48f128db322206c784744adcbbbe7ba2f5af6c78ed4c3d" + }, + { + "frame": 73, + "time_seconds": 2.4, + "path": "reference/frames/0073.png", + "mean_rgb": [ + 61.2, + 64.319, + 52.598 + ], + "sha256": "e7b68f497a2306b5c876467c6f5e1496c2c5c41e51f841095cab47d6e7b35257" + }, + { + "frame": 74, + "time_seconds": 2.433333333333333, + "path": "reference/frames/0074.png", + "mean_rgb": [ + 60.537, + 64.464, + 53.708 + ], + "sha256": "797e0995e837f5a835401c40d24b7f37bcd38720b931aae3858eaa3cddad5928" + }, + { + "frame": 75, + "time_seconds": 2.466666666666667, + "path": "reference/frames/0075.png", + "mean_rgb": [ + 60.333, + 64.193, + 53.836 + ], + "sha256": "5ad3f60aff2af82874eea68e9697b53afafc4b9a6a83041c1bf90774c592bafc" + }, + { + "frame": 76, + "time_seconds": 2.5, + "path": "reference/frames/0076.png", + "mean_rgb": [ + 60.601, + 64.768, + 52.835 + ], + "sha256": "425802256682948e3016f2687bca9d840ab9676411101c5b4c12353a0272a619" + }, + { + "frame": 77, + "time_seconds": 2.533333333333333, + "path": "reference/frames/0077.png", + "mean_rgb": [ + 60.659, + 64.871, + 53.67 + ], + "sha256": "2da364f1a30ddc53e00996e80f2b7d3f930a02871a46bf9beca8ad75d649ce24" + }, + { + "frame": 78, + "time_seconds": 2.566666666666667, + "path": "reference/frames/0078.png", + "mean_rgb": [ + 60.661, + 64.834, + 54.041 + ], + "sha256": "c2ee10d923c5bcf9124aa15f6f8c11c9e0ed7c9c4d359e7042a43efbf726b2a2" + }, + { + "frame": 79, + "time_seconds": 2.6, + "path": "reference/frames/0079.png", + "mean_rgb": [ + 60.885, + 64.923, + 53.21 + ], + "sha256": "dc492b3343c48c972652e35dd59817e7667a224ca2ba54fc22763b9085f74aff" + }, + { + "frame": 80, + "time_seconds": 2.6333333333333333, + "path": "reference/frames/0080.png", + "mean_rgb": [ + 61.412, + 64.764, + 53.779 + ], + "sha256": "eed1fba8a9b77b41d780d3ac4da29610cf4f0fd2de99da0d0f6269139a99cfa1" + }, + { + "frame": 81, + "time_seconds": 2.6666666666666665, + "path": "reference/frames/0081.png", + "mean_rgb": [ + 61.558, + 65.305, + 53.165 + ], + "sha256": "79c2153f889612e9bbf3b79cc480d51d95da090f002978a9430ce9c9f87d4eed" + }, + { + "frame": 82, + "time_seconds": 2.7, + "path": "reference/frames/0082.png", + "mean_rgb": [ + 61.349, + 65.096, + 53.444 + ], + "sha256": "03edf019f71d4388278faa4f4141444897b4b2ec7b7e6a3de4c5d7b8e83bf730" + }, + { + "frame": 83, + "time_seconds": 2.7333333333333334, + "path": "reference/frames/0083.png", + "mean_rgb": [ + 61.562, + 65.318, + 53.504 + ], + "sha256": "c17402a973cecc0a9a702d71b64bbf194a580035e301fbdc28c0e4f942f5e770" + }, + { + "frame": 84, + "time_seconds": 2.7666666666666666, + "path": "reference/frames/0084.png", + "mean_rgb": [ + 62.074, + 65.689, + 53.405 + ], + "sha256": "173982be0b59e09448e5c0916f250ab11afeead429f31d1b1a031edeabd431dc" + }, + { + "frame": 85, + "time_seconds": 2.8, + "path": "reference/frames/0085.png", + "mean_rgb": [ + 62.201, + 65.743, + 53.534 + ], + "sha256": "151fd2dc317e700cdd2204819a5243020236c4ecf745e5aa4b1faef7a1155dd4" + }, + { + "frame": 86, + "time_seconds": 2.8333333333333335, + "path": "reference/frames/0086.png", + "mean_rgb": [ + 61.147, + 65.55, + 55.011 + ], + "sha256": "8d84314130bca75c219006604f8025fa82c30ad8abaf5bdfa6f2dc65263f2217" + }, + { + "frame": 87, + "time_seconds": 2.8666666666666667, + "path": "reference/frames/0087.png", + "mean_rgb": [ + 62.027, + 65.914, + 54.118 + ], + "sha256": "423c82a138478b1a3c7cd81637a3e5954d369ec3170fa77a474ac3393b1446a3" + }, + { + "frame": 88, + "time_seconds": 2.9, + "path": "reference/frames/0088.png", + "mean_rgb": [ + 61.963, + 65.667, + 54.17 + ], + "sha256": "2ea8a3feec5e49574835e53f9e6760ac349dd99d278c50a973d9c4fd8adf0d9b" + }, + { + "frame": 89, + "time_seconds": 2.933333333333333, + "path": "reference/frames/0089.png", + "mean_rgb": [ + 61.908, + 65.793, + 54.08 + ], + "sha256": "cd2298643f6d953a6435b826ab3e9481a5200a05ffebfce02d31f0c6100b1981" + }, + { + "frame": 90, + "time_seconds": 2.966666666666667, + "path": "reference/frames/0090.png", + "mean_rgb": [ + 62.798, + 66.354, + 54.52 + ], + "sha256": "ca827466cce7d6c9c6d5e9ac2f30ea08286057339e4fd50ca173d587f09a5ee5" + }, + { + "frame": 91, + "time_seconds": 3.0, + "path": "reference/frames/0091.png", + "mean_rgb": [ + 61.747, + 65.989, + 54.944 + ], + "sha256": "cc8060e28c7b5336042e3f2041904324de12d7b679c805098f6cfb466f762311" + }, + { + "frame": 92, + "time_seconds": 3.033333333333333, + "path": "reference/frames/0092.png", + "mean_rgb": [ + 61.62, + 65.766, + 54.4 + ], + "sha256": "9058dc0c2740bb3e3d4ba24f6d6f1efdb218fa6339fb5bf60db4f6765335ca06" + }, + { + "frame": 93, + "time_seconds": 3.066666666666667, + "path": "reference/frames/0093.png", + "mean_rgb": [ + 62.064, + 65.932, + 54.609 + ], + "sha256": "a6f45ee63606572ab514ca720f8cb92b720b4ec70625c436d5ddd49291a09567" + }, + { + "frame": 94, + "time_seconds": 3.1, + "path": "reference/frames/0094.png", + "mean_rgb": [ + 62.657, + 66.32, + 54.573 + ], + "sha256": "b0b608428ad8f5c04ddab518001054f1b92d0b2b2be2604397e76ecd02078c32" + }, + { + "frame": 95, + "time_seconds": 3.1333333333333333, + "path": "reference/frames/0095.png", + "mean_rgb": [ + 62.432, + 66.256, + 54.439 + ], + "sha256": "900ff6695c144b6f3a73ba39354143d85ad56b2094f03711ed50c006280dfe1e" + }, + { + "frame": 96, + "time_seconds": 3.1666666666666665, + "path": "reference/frames/0096.png", + "mean_rgb": [ + 63.044, + 66.307, + 53.651 + ], + "sha256": "8f4c57ee6f6733751cac22588eb36365c8fe8a8dea36736908bdca5559dd3265" + }, + { + "frame": 97, + "time_seconds": 3.2, + "path": "reference/frames/0097.png", + "mean_rgb": [ + 63.33, + 66.815, + 54.53 + ], + "sha256": "ef3445e0025574a5e875b0852e7439a09d254a4c5f304d56b61b81b190ed916c" + }, + { + "frame": 98, + "time_seconds": 3.2333333333333334, + "path": "reference/frames/0098.png", + "mean_rgb": [ + 62.484, + 66.068, + 54.582 + ], + "sha256": "b32fbab9d85d52a6b1ed9ab8a4085bab479b34f1b69edb1b77f9b414ee72f1c9" + }, + { + "frame": 99, + "time_seconds": 3.2666666666666666, + "path": "reference/frames/0099.png", + "mean_rgb": [ + 62.252, + 65.559, + 53.936 + ], + "sha256": "e7bc9b6f70e477bf8b6c4129f0c43ee9e2d87bc2ac52f399a7f75a17e28d563f" + }, + { + "frame": 100, + "time_seconds": 3.3, + "path": "reference/frames/0100.png", + "mean_rgb": [ + 62.382, + 65.518, + 54.226 + ], + "sha256": "dcae061de907e6166dc997e69e004a9a52dea4bed6bb5b377ea9a36f38157209" + }, + { + "frame": 101, + "time_seconds": 3.3333333333333335, + "path": "reference/frames/0101.png", + "mean_rgb": [ + 61.939, + 65.48, + 54.215 + ], + "sha256": "a8b7badf5f86c764aaa04e5224220b6323a0db2c4d594dfab4df4626611e9562" + }, + { + "frame": 102, + "time_seconds": 3.3666666666666667, + "path": "reference/frames/0102.png", + "mean_rgb": [ + 62.027, + 65.432, + 54.34 + ], + "sha256": "770674fe57a129fde5bcede874a4245f6114224bb11d83a057fa025eda981b26" + }, + { + "frame": 103, + "time_seconds": 3.4, + "path": "reference/frames/0103.png", + "mean_rgb": [ + 61.7, + 65.259, + 54.143 + ], + "sha256": "96ba58d74beab18ab00804a4a67676898483623c1b2c5b927a27c681d6e74310" + }, + { + "frame": 104, + "time_seconds": 3.433333333333333, + "path": "reference/frames/0104.png", + "mean_rgb": [ + 61.446, + 65.134, + 54.114 + ], + "sha256": "220a6898d7a6cf621edbf07a7a4a0430f905cf68b1be047894ceea7608266fb3" + }, + { + "frame": 105, + "time_seconds": 3.466666666666667, + "path": "reference/frames/0105.png", + "mean_rgb": [ + 61.529, + 64.901, + 54.32 + ], + "sha256": "32fcfe515afdc9aa91dfb8417da770235797764f5c458e4091926c747f9f5892" + }, + { + "frame": 106, + "time_seconds": 3.5, + "path": "reference/frames/0106.png", + "mean_rgb": [ + 61.659, + 65.057, + 54.039 + ], + "sha256": "b2e3f257f000e9a0f5166a246af134eb5b92ef3734c67a65b1c0f6c808664636" + }, + { + "frame": 107, + "time_seconds": 3.533333333333333, + "path": "reference/frames/0107.png", + "mean_rgb": [ + 63.232, + 66.557, + 55.281 + ], + "sha256": "341f416d9556e82a6f5579924122603c5c64f7b1b799c10b58ab01d77ad00c3b" + }, + { + "frame": 108, + "time_seconds": 3.566666666666667, + "path": "reference/frames/0108.png", + "mean_rgb": [ + 62.598, + 66.077, + 54.719 + ], + "sha256": "c526ff42953a3ba8e51c991019baf13763f3e5b9f159c4f697c48fa6677068db" + }, + { + "frame": 109, + "time_seconds": 3.6, + "path": "reference/frames/0109.png", + "mean_rgb": [ + 62.868, + 66.292, + 55.104 + ], + "sha256": "aaa1a8010a9c9cc855eb856e891ae07a8ce7032fd719e494ac023002e3f231ad" + }, + { + "frame": 110, + "time_seconds": 3.6333333333333333, + "path": "reference/frames/0110.png", + "mean_rgb": [ + 63.436, + 66.715, + 56.093 + ], + "sha256": "4065308bc07ad1fbf0428bcb7d967b4addf36ec997baadb6add29ee4097ad176" + }, + { + "frame": 111, + "time_seconds": 3.6666666666666665, + "path": "reference/frames/0111.png", + "mean_rgb": [ + 64.228, + 67.09, + 56.454 + ], + "sha256": "636596e85d0ee4ab1d26ae10a7a7adb93026e483321632c262aee2be4fb6cb87" + }, + { + "frame": 112, + "time_seconds": 3.7, + "path": "reference/frames/0112.png", + "mean_rgb": [ + 64.475, + 67.386, + 56.936 + ], + "sha256": "8561ba08871bb5ad0ec5a65edd22f7f43fd3743ac8c74451e7e67559e2151ccd" + }, + { + "frame": 113, + "time_seconds": 3.7333333333333334, + "path": "reference/frames/0113.png", + "mean_rgb": [ + 64.268, + 67.795, + 57.386 + ], + "sha256": "4f84b4ad9c12d57bd0fa3edea9bf8e663b92cd412e1add4a59ed3717002c7437" + }, + { + "frame": 114, + "time_seconds": 3.7666666666666666, + "path": "reference/frames/0114.png", + "mean_rgb": [ + 64.428, + 67.907, + 58.239 + ], + "sha256": "adbbfc295f5a9317d15f08eaf72944ad26c4e7d881a1d762537bbd03c93f6971" + }, + { + "frame": 115, + "time_seconds": 3.8, + "path": "reference/frames/0115.png", + "mean_rgb": [ + 64.778, + 68.283, + 57.719 + ], + "sha256": "42f8fb204095bc6c79b277c73ec41939b86e6d3070f67e6fd18beef041c5f836" + }, + { + "frame": 116, + "time_seconds": 3.8333333333333335, + "path": "reference/frames/0116.png", + "mean_rgb": [ + 64.927, + 68.501, + 58.517 + ], + "sha256": "d5f2a0f4524b1d3b836b1a835f0c5ca255e8670f46c76b0308459efeb5bfa710" + }, + { + "frame": 117, + "time_seconds": 3.8666666666666667, + "path": "reference/frames/0117.png", + "mean_rgb": [ + 65.15, + 68.601, + 58.281 + ], + "sha256": "10b124a0efcbe8dc6adc9565633522eeda3b5a5bfff31b71abae9f156f2b111b" + }, + { + "frame": 118, + "time_seconds": 3.9, + "path": "reference/frames/0118.png", + "mean_rgb": [ + 65.39, + 68.922, + 59.21 + ], + "sha256": "bcd87cd6013ea453f1ab9363a197ef47ef308e36bb4fdfed4fb2d65312b0f16c" + }, + { + "frame": 119, + "time_seconds": 3.933333333333333, + "path": "reference/frames/0119.png", + "mean_rgb": [ + 65.756, + 69.238, + 59.665 + ], + "sha256": "53e59bf696d2e5e0a815af00e6d87f51d33865261cd0cdc9e755f276d8f00e95" + }, + { + "frame": 120, + "time_seconds": 3.966666666666667, + "path": "reference/frames/0120.png", + "mean_rgb": [ + 66.687, + 69.948, + 59.896 + ], + "sha256": "47c73ac6cf4243ac6eb4109beab6be21ca830b8c9c09a79c1548ab360d4ea971" + }, + { + "frame": 121, + "time_seconds": 4.0, + "path": "reference/frames/0121.png", + "mean_rgb": [ + 66.801, + 70.134, + 60.238 + ], + "sha256": "cc3b772dea095940430ad8ce4a5654c24a90d0b8df77e29267c81563f2f64498" + }, + { + "frame": 122, + "time_seconds": 4.033333333333333, + "path": "reference/frames/0122.png", + "mean_rgb": [ + 66.718, + 70.025, + 60.019 + ], + "sha256": "39124f76990ac5cd5b192da4574aac4bc769c217975d97c7bf2e4401ec269750" + }, + { + "frame": 123, + "time_seconds": 4.066666666666666, + "path": "reference/frames/0123.png", + "mean_rgb": [ + 66.48, + 69.987, + 60.363 + ], + "sha256": "48f71f306bbf9c62169d57e3787bfe91b64991043f1e02ef51b9b3226c025f45" + }, + { + "frame": 124, + "time_seconds": 4.1, + "path": "reference/frames/0124.png", + "mean_rgb": [ + 66.864, + 70.303, + 60.085 + ], + "sha256": "2b40465001eab2e91614f4c93b30705666c6ed99f879dafed9e5fc0cc0467f40" + }, + { + "frame": 125, + "time_seconds": 4.133333333333334, + "path": "reference/frames/0125.png", + "mean_rgb": [ + 67.113, + 70.781, + 59.964 + ], + "sha256": "796a8c8447e279ce615bdfda951231dba2b9378075042a3c56d924000bc830a2" + }, + { + "frame": 126, + "time_seconds": 4.166666666666667, + "path": "reference/frames/0126.png", + "mean_rgb": [ + 66.331, + 70.173, + 60.118 + ], + "sha256": "3aa02485be56aee35d7235f9d462fa1337d4615b583bc51310070c3f0cc5aece" + }, + { + "frame": 127, + "time_seconds": 4.2, + "path": "reference/frames/0127.png", + "mean_rgb": [ + 67.903, + 70.519, + 60.702 + ], + "sha256": "7902f4692d69c432a1bfaa539d0a70bf45d2e2fb2244b940673fbb88c7834a6b" + }, + { + "frame": 128, + "time_seconds": 4.233333333333333, + "path": "reference/frames/0128.png", + "mean_rgb": [ + 69.135, + 71.14, + 61.448 + ], + "sha256": "5688d480b473772608ae25c975e70e10e7d9f762c84fcf8d8fde98b6f4ebc597" + } + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/observations.md b/astra_scene_test/Scene_04/reference/observations.md new file mode 100644 index 0000000000000000000000000000000000000000..5a3089e941ad401ee69e3a1c4e1c2f4e5fc11f90 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/observations.md @@ -0,0 +1,127 @@ +# Reference observations and calibration record + +The original `night_front_wide_120fov.mp4` is the sole visual reference. Its bytes +are preserved and identified by SHA-256 in `logs/session.json`. + +FFmpeg decoded 128 frames, 1280 × 720, progressive H.264, 30/1 fps, time base +1/15360, no audio stream. Frame 1 is t=0; frame 128 is t=4.233333 s; the encoded +duration is 128/30 = 4.266667 s. All 128 decoded PNGs are retained with exact +frame-index timestamps in `metadata.json`. + +## Sequence observations + +- Opening: olive-gray humid sky occupies much of the image. A prominent left + apartment tower sits behind the nearer red brick row. The road has restrained, + uneven wet reflections. A red PARK board and silver scaffold dominate the + right foreground. A black Jeep with a rear spare wheel is parked on the right. +- Around 1 s: the PARK board and opening scaffold move out of the right edge. + Left storefront text and the white SUV grow rapidly. The source has a slight + change in camera pitch, not perfectly smooth eased motion. +- Around 2 s: the white SUV occupies the lower left, followed by a white sedan. + Construction fencing and orange/white water barriers approach on the right. + Fixed windshield smears become conspicuous around the brighter passing lamps. +- Around 3 s: the nearer left signal moves rapidly left/up; the farther right + signal remains nearer the center. Their depths cannot be represented by one + cross-street plane. The tall left tower rises out of the top of the frame. +- Ending: the intersection and side-street crosswalks enter the foreground. + The right far-side red brick corner/storefront becomes exposed. Distant + oncoming white-blue lights remain near the road's vanishing region. +- Throughout: a curved black lower windshield seal and muted silver hood sliver + stay near the bottom edge. Rain features are largely attached to the glass; + they are not independent random particles per frame. Glare intensity and shape + change when world-space lights pass behind those water features. + +## Measured motion + +`track_reference.py` uses forward/backward Lucas–Kanade checks to reject unstable +tracks. `feature_tracks.npz` retains the observations; `tracks.jpg` shows the long +trajectories. The camera fit uses a selected subset of static architectural and +signal features, with robust residuals and a continuous forward trajectory. + +The selected 120-degree pinhole fit uses focal length 369.5 pixels, principal +point (640,480), and free translation epipole approximately (640.17,514.72). +Its selected-track median residual is 0.994 px and 90th percentile 2.901 px. +A fit that forced the epipole to the visible distant road center (615,537) was +rejected: its aggregate score concealed more than 100 pixels of near-signal +vertical error late in the sequence. Cylindrical/equirectangular alternatives +and a free radial coefficient were also tested; the simple pinhole model was +retained. These are selected-point fit residuals, **not** whole-image fidelity +scores. Metric scale and exact lens distortion remain underdetermined. + +Ground contacts of the paired left lamp were subsequently used to orient the +world road plane. The current physical pitch is about 4.726 degrees, camera +height 2.966 scene units, and total travel 27.8 units. The world trajectory is +then almost level. This does not change the fitted image-space motion: all +triangulated anchors are transformed into the new world basis. A smooth inferred +downhill grade beyond the intersection brings the distant roadway/headlights +into the lower visible vanishing region. The grade is a reconstruction inference, +not a separately measured physical survey. + +Near facade windows, storefront letters, PARK boards and parked vehicle details +were triangulated separately. They exposed under-estimated facade and scaffold +depth in the initial scene, despite plausible opening-frame positions. Those +depths, roof breaks, window scale, and road contacts were revised before the +whole-sequence preview. The observed illuminated tower panes are actual emissive +mesh faces at their measured world positions. + +## Initial preview findings + +The first CPU preview exposed an inverted Blender pitch convention. Positive +upward pitch requires rotation X = 90° + pitch for a camera looking along +Y. +The original analytical overlay used the correct upward convention; the scene +camera has been corrected before further layout evaluation. The initial tower +wire overlay also shows excessive near perspective; its setback and facade +orientation require correction using the tracked windows. + +## Native appearance review, second full-sequence pass + +The 128-frame motion preview was reviewed in eight synchronized paired sheets. +The revised camera uses all128 recorded frame poses, retaining the original17 +anchor times. Actual signal emitters are now fitted separately from the original +LK feature corners. The reduced near-vehicle lengths improve their exit timing; +the canopy and crosswalk footprints are checked again at native resolution. + +Native frames1,17,33,65,97,128 from appearance_refined exposed remaining overly +plain storefront interiors, undersized PARK letters, square vehicle light +housings, and strong distant headlight points. Those drive the next changes. +The continuous road grid removed the older disconnected reflection bands. +The current lens kernels still need softer internal wipe curves and weaker +secondary copies around paired lamps. Broader glare alone increases highlight +energy too much; color, halo width, and core intensity must be checked together. + +The source's upper sky shifts from brown/olive in the opening to greener, cooler +midtones later. Uniform sky patches also have subtle blue-channel sensor noise. +A global white-balance trial improved sky color but overcooled the roadway, so it +was not selected. Noise strength was reduced after measured blue-channel noise +exceeded the reference. These are camera-response trials, not geometric fixes. + +### Native optical and contact refinement, 21:42 UTC + +The native opening and middle views still showed a clean computer-rendered response after the first complete motion review. Multi-scale source sky measurements found roughly 0.6–1.2 code values of color variation in spatial bands around 2–8 pixels, above the fine-grain model. The added deterministic correlated sensor field is independent of the fixed windshield water. Source lag-one bandpass RGB correlations were about 0.206/0.192/0.151; the field includes a modest previous-frame contribution. + +Camera-visible sky probes with background strength zero proved that the upper homogeneous volume alone exceeded the ending source sky brightness. A fixed three-layer humidity profile reduces that upper in-scattering. The camera-visible background color is then fitted from numeric mean colors at nine timestamps. This remains an inferred appearance response, not a recovered physical exposure curve. + +Parking-bay curb geometry now follows the near vehicle contacts. A three-time license-plate fit places the first dark SUV beyond the left crossing; the later row remains inferred. Its selected point residual is around one to two pixels, which is not a whole-car or whole-image fidelity score. The far-left crown was raised to improve its beginning/end silhouette. + +A bright white pavement patch was traced through native region renders with original, no-caustic, rough, and diffuse material settings. Its persistence ruled out a specular/material cause. The scene light inventory exposed restaurant ceiling lights at the fitted-building transform origin because matrix_world was read before a dependency-graph update. The builder now updates the graph before that transform, and the reopened-scene audit checks room-light height above the road. + +Still approximate: exact car body models, window and facade detail beyond tracked planes, some tree envelopes, far parking spacing, road appearance, and individual windshield caustics. The broad-scatter trials retain the small emitter cores; overly strong trials remain archived as rejected comparisons. + +### Angular signal response correction, 22:47 UTC + +The native 256-sample visor candidate made the left signal progressively too +bright and the right signal too weak at the opening. A localized chroma-energy +measurement at the independently tracked source centroids gave source/candidate +values of 7396/15106, 5833/34646, 24493/165187 for the left signal at frames +1/65/128. Right values were 40905/9873, 77359/52043, 93261/164351. These are +appearance diagnostics affected by background and saturation, not physical +radiometry. A frame65 native96 render with all auxiliary signal point lights +removed reduced left energy to29953 and right to35483; the visible lens response +remained the main cause. Inferred angular emission parameters are now stored in +`assets/signal_response.json`. They use continuous world-direction shading and +no frame-dependent material keys; native comparison remains required. + +A paired native crop of the whiteSUV atframe65 showed the fitted roof and wheel +locations were reasonably aligned, so no additional body-height change was +applied. The source has dense bright silver wheel detail and dark inner window +pillars, which replaced the candidate's sparse dark wheels and white dividers. diff --git a/astra_scene_test/Scene_04/reference/right_facade_fit_trial.json b/astra_scene_test/Scene_04/reference/right_facade_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..81e593635c89315f190562e15a553877ca639611 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/right_facade_fit_trial.json @@ -0,0 +1,570 @@ +{ + "right_68": { + "xyz": [ + 28.164073952500807, + 27.454335437630817, + 31.712728519012344 + ], + "observations": { + "1": [ + 930.0, + 203.0 + ], + "9": [ + 951.3560791015625, + 181.06112670898438 + ], + "17": [ + 975.6013793945312, + 160.9532928466797 + ], + "25": [ + 998.8346557617188, + 134.32972717285156 + ], + "33": [ + 1020.1860961914062, + 98.49620056152344 + ], + "41": [ + 1046.5966796875, + 67.38304138183594 + ], + "49": [ + 1078.5074462890625, + 33.2271728515625 + ] + }, + "projections": { + "1": [ + 929.4197120331812, + 200.78870867974518 + ], + "9": [ + 951.259062287832, + 179.5327814973894 + ], + "17": [ + 974.996497004848, + 160.1114889024342 + ], + "25": [ + 997.172757514137, + 134.70113168225413 + ], + "33": [ + 1019.3931591776368, + 99.54316444012062 + ], + "41": [ + 1047.0306594707035, + 69.71500971405368 + ], + "49": [ + 1082.1926218340823, + 33.10376731977368 + ] + }, + "mean_error_pixels": 1.9899518547375785, + "max_error_pixels": 3.6872412075816077 + }, + "right_81": { + "xyz": [ + 28.30115444344072, + 27.801150959208716, + 34.45506957238043 + ], + "observations": { + "1": [ + 924.0, + 180.0 + ], + "9": [ + 944.8445434570312, + 157.43301391601562 + ], + "17": [ + 968.5233764648438, + 136.40621948242188 + ], + "25": [ + 991.2452392578125, + 108.56942749023438 + ], + "33": [ + 1011.849609375, + 71.45540618896484 + ], + "41": [ + 1036.534912109375, + 40.006202697753906 + ], + "49": [ + 1067.1043701171875, + 5.156663417816162 + ] + }, + "projections": { + "1": [ + 924.150283245417, + 178.4450205626544 + ], + "9": [ + 945.2708485038746, + 156.15412746686854 + ], + "17": [ + 968.1191241331999, + 135.84376504783768 + ], + "25": [ + 989.4407088129258, + 109.30350609761354 + ], + "33": [ + 1010.6616892420786, + 72.65217863080426 + ], + "41": [ + 1036.566464218301, + 42.022222259632315 + ], + "49": [ + 1069.994553646233, + 4.179430896426084 + ] + }, + "mean_error_pixels": 1.7577876456737382, + "max_error_pixels": 3.0509251436947062 + }, + "right_102": { + "xyz": [ + 29.170834269645503, + 26.883622877266156, + 35.59841071701788 + ], + "observations": { + "1": [ + 940.0, + 159.0 + ], + "9": [ + 962.2224731445312, + 134.9095458984375 + ], + "17": [ + 987.5474243164062, + 112.24951171875 + ], + "25": [ + 1012.031494140625, + 82.62113952636719 + ], + "33": [ + 1034.291259765625, + 43.65916442871094 + ], + "41": [ + 1060.844482421875, + 9.848000526428223 + ] + }, + "projections": { + "1": [ + 940.0415471220175, + 158.56034599232225 + ], + "9": [ + 962.7287601378774, + 134.40222846678432 + ], + "17": [ + 987.2865356635257, + 112.21011290253887 + ], + "25": [ + 1010.5141717054051, + 83.39007480459372 + ], + "33": [ + 1033.9845714886237, + 43.61951601769863 + ], + "41": [ + 1062.2367571222849, + 9.95662650782782 + ] + }, + "mean_error_pixels": 0.8048284001091419, + "max_error_pixels": 1.7010375758703349 + }, + "right_105": { + "xyz": [ + 29.5742776844739, + 27.157593093400507, + 32.69628888665872 + ], + "observations": { + "1": [ + 945.0, + 191.0 + ], + "9": [ + 967.8318481445312, + 168.51951599121094 + ], + "17": [ + 993.9000854492188, + 147.25721740722656 + ], + "25": [ + 1019.2359008789062, + 119.6383285522461 + ], + "33": [ + 1042.791015625, + 82.47766876220703 + ], + "41": [ + 1070.8248291015625, + 50.493186950683594 + ], + "49": [ + 1104.236328125, + 16.270078659057617 + ] + }, + "projections": { + "1": [ + 945.5914015027788, + 189.31097688389377 + ], + "9": [ + 968.6681154196949, + 167.1654756121166 + ], + "17": [ + 993.7067926214829, + 146.92026860688952 + ], + "25": [ + 1017.2629637304644, + 120.50979127559498 + ], + "33": [ + 1041.0665535968055, + 83.92810582520951 + ], + "41": [ + 1070.3229698299363, + 52.842684856959636 + ], + "49": [ + 1107.6897858767854, + 14.642169971563249 + ] + }, + "mean_error_pixels": 2.057153403319035, + "max_error_pixels": 3.8179126677003175 + }, + "right_120": { + "xyz": [ + 29.293103965849763, + 26.84076697958025, + 33.98910837106282 + ], + "observations": { + "1": [ + 944.0, + 174.0 + ], + "9": [ + 966.49560546875, + 150.54212951660156 + ], + "17": [ + 992.2581787109375, + 128.60488891601562 + ], + "25": [ + 1017.1861572265625, + 100.16828918457031 + ], + "33": [ + 1040.0406494140625, + 62.25627517700195 + ], + "41": [ + 1067.5091552734375, + 29.437023162841797 + ] + }, + "projections": { + "1": [ + 943.9422407914919, + 173.4249581436335 + ], + "9": [ + 967.0282821110388, + 150.09609847154655 + ], + "17": [ + 992.0703550782074, + 128.66217598550003 + ], + "25": [ + 1015.7346380420024, + 100.80546349229013 + ], + "33": [ + 1039.700696536326, + 62.31903848069072 + ], + "41": [ + 1068.8762077365977, + 29.52289120492776 + ] + }, + "mean_error_pixels": 0.7949526773861596, + "max_error_pixels": 1.5852126171476808 + }, + "right_187": { + "xyz": [ + 27.677596814305723, + 24.509843771235484, + 18.24773604830138 + ], + "observations": { + "1": [ + 973.0, + 319.0 + ], + "9": [ + 1001.975341796875, + 303.50115966796875 + ], + "17": [ + 1035.0302734375, + 289.8284912109375 + ], + "25": [ + 1069.6700439453125, + 269.08013916015625 + ], + "33": [ + 1105.9835205078125, + 239.00192260742188 + ], + "41": [ + 1149.0711669921875, + 213.0996856689453 + ], + "49": [ + 1199.2894287109375, + 184.6190643310547 + ], + "57": [ + 1253.4111328125, + 154.2439727783203 + ] + }, + "projections": { + "1": [ + 976.2867105164453, + 318.3585997067487 + ], + "9": [ + 1004.948829490924, + 302.7888148901767 + ], + "17": [ + 1037.2408482191572, + 288.67702848046304 + ], + "25": [ + 1067.9577781721277, + 269.2201710138815 + ], + "33": [ + 1100.0374984294895, + 241.08238828556227 + ], + "41": [ + 1144.2260014077397, + 215.78139258167073 + ], + "49": [ + 1197.7319073561862, + 185.35679528500805 + ], + "57": [ + 1260.401001929787, + 150.81626657641846 + ] + }, + "mean_error_pixels": 3.995321160871291, + "max_error_pixels": 7.785078039644817 + }, + "right_273": { + "xyz": [ + 28.926562911641405, + 26.031129978599242, + 25.215874260391033 + ], + "observations": { + "1": [ + 958.0, + 258.0 + ], + "9": [ + 983.905029296875, + 238.2399444580078 + ], + "17": [ + 1013.7578125, + 219.835205078125 + ], + "25": [ + 1044.0289306640625, + 194.3040313720703 + ], + "33": [ + 1073.928955078125, + 158.7572784423828 + ], + "41": [ + 1109.5731201171875, + 128.22463989257812 + ], + "49": [ + 1151.25732421875, + 95.4827880859375 + ], + "57": [ + 1195.6387939453125, + 59.72053146362305 + ], + "65": [ + 1246.5206298828125, + 26.361682891845703 + ] + }, + "projections": { + "1": [ + 961.3155858518151, + 253.38672603056693 + ], + "9": [ + 986.8539133951642, + 234.36955707169747 + ], + "17": [ + 1015.021155924569, + 216.9319743623688 + ], + "25": [ + 1041.6034696606982, + 193.87180225370724 + ], + "33": [ + 1068.8890436875026, + 161.52877488418835 + ], + "41": [ + 1104.35291756715, + 133.02328113368065 + ], + "49": [ + 1148.1834784981893, + 98.52345646068045 + ], + "57": [ + 1196.8794430682246, + 61.07564445718725 + ], + "65": [ + 1252.8765680528745, + 23.185664992022794 + ] + }, + "mean_error_pixels": 4.6983738336353325, + "max_error_pixels": 7.105282522296129 + }, + "right_289": { + "xyz": [ + 27.02189649443526, + 24.324746438760688, + 17.20732234107166 + ], + "observations": { + "1": [ + 969.0, + 331.0 + ], + "9": [ + 998.3746948242188, + 315.389404296875 + ], + "17": [ + 1031.39453125, + 301.6976318359375 + ], + "25": [ + 1066.3258056640625, + 281.682861328125 + ], + "33": [ + 1102.835205078125, + 251.97650146484375 + ], + "41": [ + 1146.337646484375, + 227.27981567382812 + ], + "49": [ + 1196.9769287109375, + 200.24212646484375 + ], + "57": [ + 1250.9990234375, + 170.29156494140625 + ] + }, + "projections": { + "1": [ + 972.4574549271849, + 329.18509872301456 + ], + "9": [ + 1001.1080104555126, + 314.2858852330318 + ], + "17": [ + 1033.4977548570887, + 300.8407419569904 + ], + "25": [ + 1064.3293519140573, + 282.0973991547256 + ], + "33": [ + 1096.5822204184697, + 254.8232911769489 + ], + "41": [ + 1141.4358702539184, + 230.32926932226064 + ], + "49": [ + 1195.6224498521601, + 200.85208500333852 + ], + "57": [ + 1259.6224463179383, + 167.16710308843255 + ] + }, + "mean_error_pixels": 4.307945754301022, + "max_error_pixels": 9.17200545385546 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/right_tower_fit_trial.json b/astra_scene_test/Scene_04/reference/right_tower_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..5489368ecc131cfead71ad97c26e65019149dea1 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/right_tower_fit_trial.json @@ -0,0 +1,222 @@ +{ + "right_tower_20": { + "xyz": [ + 63.334365873116475, + 270.9509583926246, + 175.30622935473338 + ], + "observations": { + "1": [ + 696.0, + 319.0 + ], + "9": [ + 698.6965942382812, + 316.6335754394531 + ] + }, + "projections": { + "1": [ + 695.9363346648195, + 318.9818654923959 + ], + "9": [ + 698.7597052558851, + 316.6516302905852 + ] + }, + "mean_error_pixels": 0.06592025299640418, + "max_error_pixels": 0.06619769837156023 + }, + "right_tower_42": { + "xyz": [ + 47.43072001121142, + 243.4415296911511, + 157.2507216286171 + ], + "observations": { + "1": [ + 683.0, + 319.0 + ], + "9": [ + 685.7332763671875, + 316.543212890625 + ] + }, + "projections": { + "1": [ + 682.9972592385708, + 318.9994002895519 + ], + "9": [ + 685.7359921176818, + 316.5438103079563 + ] + }, + "mean_error_pixels": 0.0027931454950384364, + "max_error_pixels": 0.0028056061436729555 + }, + "right_tower_88": { + "xyz": [ + 63.14456605675988, + 249.02486851492836, + 145.99630044543034 + ], + "observations": { + "1": [ + 702.0, + 338.0 + ], + "9": [ + 706.976806640625, + 336.4349670410156 + ] + }, + "projections": { + "1": [ + 703.0434518092047, + 338.35376991236944 + ], + "9": [ + 705.9443548274598, + 336.0832519432736 + ] + }, + "mean_error_pixels": 1.0962535620907232, + "max_error_pixels": 1.101791645017559 + }, + "right_tower_200": { + "xyz": [ + 10.67180579347816, + 32.49477493596642, + 15.01238069590439 + ], + "observations": { + "1": [ + 729.0, + 395.0 + ], + "9": [ + 735.5307006835938, + 387.2533264160156 + ], + "17": [ + 744.096435546875, + 381.1778564453125 + ], + "25": [ + 750.9470825195312, + 370.4839172363281 + ], + "33": [ + 755.5595092773438, + 354.3482360839844 + ], + "41": [ + 763.8697509765625, + 344.31549072265625 + ], + "49": [ + 774.3069458007812, + 330.7001647949219 + ], + "57": [ + 785.5200805664062, + 317.04962158203125 + ], + "65": [ + 800.2605590820312, + 305.1573791503906 + ], + "73": [ + 816.545166015625, + 283.66912841796875 + ], + "81": [ + 834.3521728515625, + 258.90399169921875 + ], + "89": [ + 857.5203857421875, + 224.22889709472656 + ], + "97": [ + 888.4989013671875, + 182.58047485351562 + ], + "105": [ + 925.927978515625, + 137.70187377929688 + ], + "113": [ + 971.2266845703125, + 77.87835693359375 + ] + }, + "projections": { + "1": [ + 730.0075929863251, + 392.44050568268665 + ], + "9": [ + 737.65058245849, + 384.4974153379254 + ], + "17": [ + 745.8297245132167, + 378.1557711015889 + ], + "25": [ + 751.9709047147448, + 367.99011682202865 + ], + "33": [ + 756.8385770273217, + 353.298075740179 + ], + "41": [ + 765.133419000778, + 343.51674296718295 + ], + "49": [ + 775.0840030091995, + 330.51852127489667 + ], + "57": [ + 785.838297585316, + 317.60788838588303 + ], + "65": [ + 798.7625448150222, + 305.8218284070133 + ], + "73": [ + 814.4799937870712, + 285.31230816387074 + ], + "81": [ + 830.9228902377265, + 261.93317056308524 + ], + "89": [ + 854.6878201808837, + 227.71391037743513 + ], + "97": [ + 887.8379016710481, + 184.9707136471534 + ], + "105": [ + 925.3786266761325, + 138.0653615569338 + ], + "113": [ + 974.5131173990476, + 73.37908167747867 + ] + }, + "mean_error_pixels": 2.603503511446887, + "max_error_pixels": 5.571724918557732 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/right_warm_windows_trial.json b/astra_scene_test/Scene_04/reference/right_warm_windows_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..a492c642b020dde52293bdd0a009e936cc759682 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/right_warm_windows_trial.json @@ -0,0 +1,72 @@ +{ + "upper_warm_left": { + "xyz": [ + 27.124731577334103, + 29.26344576828372, + 32.061021796888525 + ], + "observations": { + "1": [ + 914, + 184 + ], + "17": [ + 967, + 146 + ], + "33": [ + 1006, + 80 + ] + }, + "predictions": { + "1": [ + 918.6034149519265, + 186.6255490338956 + ], + "17": [ + 962.9376996537841, + 143.73006470629207 + ], + "33": [ + 1006.1911962764011, + 80.05577861788169 + ] + } + }, + "upper_warm_right": { + "xyz": [ + 28.114905993348337, + 28.180852673032906, + 32.542485517819685 + ], + "observations": { + "1": [ + 940, + 172 + ], + "17": [ + 989, + 126 + ], + "33": [ + 1035, + 54 + ] + }, + "predictions": { + "1": [ + 939.0877669497763, + 171.37658672670756 + ], + "17": [ + 987.9092830981837, + 124.75433140280029 + ], + "33": [ + 1036.8208673440329, + 55.6607459327185 + ] + } + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/roof_fit_trial.json b/astra_scene_test/Scene_04/reference/roof_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..5c6ea882ad31cfa2bf5018407bbfe37fd4e897fa --- /dev/null +++ b/astra_scene_test/Scene_04/reference/roof_fit_trial.json @@ -0,0 +1,105 @@ +{ + "left_red_roof_far": { + "xyz": [ + -36.759071792031946, + 48.2937745907266, + 41.09180880174977 + ], + "observations": { + "1": [ + 350, + 245 + ], + "33": [ + 318, + 225 + ], + "65": [ + 250, + 146 + ] + }, + "projections": { + "1": [ + 352.2000815491876, + 248.4918251772228 + ], + "33": [ + 306.27951141803055, + 197.84171204533192 + ], + "65": [ + 251.26954300986347, + 147.0092962968103 + ] + }, + "mean_error_pixels": 11.776137573272608, + "max_error_pixels": 29.579426249147325 + }, + "right_roof_near": { + "xyz": [ + 32.46600711830408, + 31.67574879264241, + 48.185455586072386 + ], + "observations": { + "1": [ + 917, + 107 + ], + "33": [ + 987, + -2 + ] + }, + "projections": { + "1": [ + 916.2621332006645, + 106.5089388301543 + ], + "33": [ + 987.5842824557445, + -1.609250454833841 + ] + }, + "mean_error_pixels": 0.7946181391020805, + "max_error_pixels": 0.8863342970301022 + }, + "right_roof_far": { + "xyz": [ + 25.660842924049007, + 37.13635965006347, + 36.43352533259185 + ], + "observations": { + "1": [ + 840, + 239 + ], + "33": [ + 882, + 174 + ], + "65": [ + 937, + 96 + ] + }, + "projections": { + "1": [ + 835.7918806934088, + 238.19368645918846 + ], + "33": [ + 881.0689773829666, + 171.53659498553998 + ], + "65": [ + 940.6988146210726, + 98.88881190948808 + ] + }, + "mean_error_pixels": 3.870459345248045, + "max_error_pixels": 4.693235967800995 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/scaffold_corner_fit.json b/astra_scene_test/Scene_04/reference/scaffold_corner_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..7707a459c0cf91a464af5f1da1e728918ad7d8db --- /dev/null +++ b/astra_scene_test/Scene_04/reference/scaffold_corner_fit.json @@ -0,0 +1,28 @@ +{ + "observations": { + "1": [ + 1026, + 404 + ], + "17": [ + 1150, + 378 + ] + }, + "xyz": [ + 18.86208548, + 15.44290225, + 7.75631084 + ], + "projected": { + "1": [ + 1026.4935, + 406.1217 + ], + "17": [ + 1149.6334, + 376.5199 + ] + }, + "note": "Two-time manual canopy corner fit; retained15.5 terminal station within.06worldunits. Other canopy details are inferred." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/scaffold_fit_trial.json b/astra_scene_test/Scene_04/reference/scaffold_fit_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..f9d96e08300e7fceaa0806e2e2e08d3452175d5a --- /dev/null +++ b/astra_scene_test/Scene_04/reference/scaffold_fit_trial.json @@ -0,0 +1,1600 @@ +{ + "feature_295": { + "xyz": [ + 17.98962247251848, + 12.214667649393625, + 10.090136573359569 + ], + "observations": { + "1": [ + 1073.0, + 319.0 + ], + "3": [ + 1090.437744140625, + 311.3988037109375 + ], + "5": [ + 1108.6888427734375, + 302.650390625 + ], + "7": [ + 1128.174560546875, + 293.5535888671875 + ], + "9": [ + 1148.94189453125, + 285.13299560546875 + ], + "11": [ + 1170.5582275390625, + 275.79278564453125 + ], + "13": [ + 1193.4713134765625, + 266.57568359375 + ], + "15": [ + 1217.939697265625, + 257.54241943359375 + ], + "17": [ + 1243.6007080078125, + 248.06011962890625 + ], + "19": [ + 1270.5804443359375, + 238.4796905517578 + ] + }, + "projections": { + "1": [ + 1075.537605871427, + 317.4038034774666 + ], + "3": [ + 1091.664454772997, + 310.3441529661844 + ], + "5": [ + 1108.9489232739788, + 302.77605077519627 + ], + "7": [ + 1127.5246591323094, + 294.64077010200856 + ], + "9": [ + 1147.546706186883, + 285.87018237741995 + ], + "11": [ + 1168.9933587812156, + 277.4439722823299 + ], + "13": [ + 1192.2430598810336, + 268.2693722384183 + ], + "15": [ + 1217.5378581348486, + 258.24586853489984 + ], + "17": [ + 1245.1651471877676, + 247.25411714121927 + ], + "19": [ + 1270.9457521796828, + 235.34411344034868 + ] + }, + "mean_error_pixels": 1.7842935304852579, + "max_error_pixels": 3.156785333578243 + }, + "feature_308": { + "xyz": [ + 17.973461301536837, + 12.124462931194413, + 9.422856201060466 + ], + "observations": { + "1": [ + 1079.0, + 334.0 + ], + "3": [ + 1096.8663330078125, + 326.651123046875 + ], + "5": [ + 1115.5032958984375, + 318.5608825683594 + ], + "7": [ + 1135.580322265625, + 310.2079772949219 + ], + "9": [ + 1156.748779296875, + 301.8352966308594 + ], + "11": [ + 1178.7628173828125, + 293.06829833984375 + ], + "13": [ + 1202.04833984375, + 284.24798583984375 + ], + "15": [ + 1226.8780517578125, + 275.6227722167969 + ], + "17": [ + 1252.736083984375, + 266.79705810546875 + ] + }, + "projections": { + "1": [ + 1081.1033841356207, + 331.85854261798727 + ], + "3": [ + 1097.6305646648075, + 325.23099490418076 + ], + "5": [ + 1115.3610878509994, + 318.1206180584261 + ], + "7": [ + 1134.4358334158674, + 310.47085932537306 + ], + "9": [ + 1155.0185649491214, + 302.21597977700935 + ], + "11": [ + 1177.1168356101584, + 294.32341814489695 + ], + "13": [ + 1201.107394012406, + 285.715562950062 + ], + "15": [ + 1227.2490400492, + 276.29453712617834 + ], + "17": [ + 1255.850011436858, + 265.9437661795869 + ] + }, + "mean_error_pixels": 1.7591440914045036, + "max_error_pixels": 3.228722857431823 + }, + "feature_341": { + "xyz": [ + 17.935187588890667, + 12.174588796800322, + 8.56498847135164 + ], + "observations": { + "1": [ + 1080.0, + 354.0 + ], + "3": [ + 1098.15673828125, + 347.5583190917969 + ], + "5": [ + 1117.26416015625, + 340.69268798828125 + ], + "7": [ + 1137.2806396484375, + 333.71832275390625 + ], + "9": [ + 1158.8089599609375, + 326.8753356933594 + ], + "11": [ + 1180.95947265625, + 318.8797302246094 + ], + "13": [ + 1204.7689208984375, + 311.1215515136719 + ], + "15": [ + 1230.211669921875, + 304.0625 + ], + "17": [ + 1257.08740234375, + 296.5989074707031 + ] + }, + "projections": { + "1": [ + 1082.6268676635427, + 353.14172891154135 + ], + "3": [ + 1099.283022503511, + 347.25395220628866 + ], + "5": [ + 1117.1584348670578, + 340.93677736203176 + ], + "7": [ + 1136.3966116132174, + 334.13963778601715 + ], + "9": [ + 1157.1645000752405, + 326.80370733739954 + ], + "11": [ + 1179.5050433143065, + 319.87893351228445 + ], + "13": [ + 1203.7757672140883, + 312.31775484854273 + ], + "15": [ + 1230.2430702747426, + 304.03228404574907 + ], + "17": [ + 1259.2247288506815, + 294.91768354277735 + ] + }, + "mean_error_pixels": 1.4337507880783962, + "max_error_pixels": 2.763523653426982 + }, + "feature_168": { + "xyz": [ + 14.955577427609336, + 13.217514803426011, + 7.239519701941776 + ], + "observations": { + "1": [ + 987.0, + 395.0 + ], + "3": [ + 1000.113037109375, + 390.6627197265625 + ], + "5": [ + 1013.9102783203125, + 385.85498046875 + ], + "7": [ + 1028.848876953125, + 380.350341796875 + ], + "9": [ + 1044.7587890625, + 374.79443359375 + ], + "11": [ + 1061.2098388671875, + 369.2581787109375 + ], + "13": [ + 1079.350341796875, + 364.4077453613281 + ], + "15": [ + 1098.239990234375, + 359.47760009765625 + ], + "17": [ + 1118.3597412109375, + 354.4563903808594 + ], + "19": [ + 1138.664306640625, + 348.654541015625 + ], + "21": [ + 1160.455322265625, + 341.5187072753906 + ], + "23": [ + 1183.1505126953125, + 332.41973876953125 + ], + "25": [ + 1207.3927001953125, + 321.84259033203125 + ], + "27": [ + 1231.8936767578125, + 309.96258544921875 + ] + }, + "projections": { + "1": [ + 988.4086786101495, + 394.20038832437336 + ], + "3": [ + 1000.9444106829317, + 390.00035446692726 + ], + "5": [ + 1014.3369795158817, + 385.51546722435415 + ], + "7": [ + 1028.6813676502634, + 380.7141246279019 + ], + "9": [ + 1044.087134761508, + 375.55987472853127 + ], + "11": [ + 1060.6311887819734, + 370.8831960146496 + ], + "13": [ + 1078.51062594133, + 365.79400991478684 + ], + "15": [ + 1097.8982828513463, + 360.23887511973464 + ], + "17": [ + 1118.998159100936, + 354.15471446623064 + ], + "19": [ + 1138.4289946247188, + 347.1157227930177 + ], + "21": [ + 1159.5229006123957, + 339.51855209911423 + ], + "23": [ + 1182.5027432535155, + 331.2883799429656 + ], + "25": [ + 1207.6330735578285, + 322.33638949806414 + ], + "27": [ + 1233.1287015661478, + 311.21752102217226 + ] + }, + "mean_error_pixels": 1.207878722671208, + "max_error_pixels": 2.206814643008365 + }, + "feature_249": { + "xyz": [ + 16.128547572231426, + 13.551376728770764, + 7.7529537569667015 + ], + "observations": { + "1": [ + 1004.0, + 386.0 + ], + "3": [ + 1016.9265747070312, + 381.63446044921875 + ], + "5": [ + 1031.080810546875, + 376.7253112792969 + ], + "7": [ + 1045.8756103515625, + 371.49066162109375 + ], + "9": [ + 1061.5927734375, + 366.2908935546875 + ], + "11": [ + 1078.2686767578125, + 360.9084777832031 + ], + "13": [ + 1096.1959228515625, + 355.87994384765625 + ], + "15": [ + 1115.353759765625, + 351.0243835449219 + ], + "17": [ + 1135.5426025390625, + 345.7704772949219 + ], + "19": [ + 1156.646728515625, + 340.1642761230469 + ], + "21": [ + 1178.392822265625, + 332.8139953613281 + ], + "23": [ + 1200.85595703125, + 323.7879638671875 + ], + "25": [ + 1224.433837890625, + 313.14898681640625 + ], + "27": [ + 1248.818359375, + 301.6029968261719 + ], + "29": [ + 1274.96435546875, + 289.3661804199219 + ] + }, + "projections": { + "1": [ + 1005.0092663489024, + 386.2214622546121 + ], + "3": [ + 1017.7583321526004, + 381.88090758565573 + ], + "5": [ + 1031.3537572946097, + 377.254164504316 + ], + "7": [ + 1045.8864861490292, + 372.31045671093545 + ], + "9": [ + 1061.4609765996095, + 367.01443485192556 + ], + "11": [ + 1078.122240652651, + 362.2231913261979 + ], + "13": [ + 1096.0796486092768, + 357.02537217827563 + ], + "15": [ + 1115.4945983844448, + 351.3703014678208 + ], + "17": [ + 1136.5564371474052, + 345.1985274978128 + ], + "19": [ + 1155.866362315815, + 338.0856800586462 + ], + "21": [ + 1176.7575857573645, + 330.43270078377964 + ], + "23": [ + 1199.4324553393794, + 322.1704521370485 + ], + "25": [ + 1224.129382551098, + 313.2174748474629 + ], + "27": [ + 1249.0569403961586, + 302.1020655445752 + ], + "29": [ + 1276.3193362776724, + 290.06901140831127 + ] + }, + "mean_error_pixels": 1.1812134764812579, + "max_error_pixels": 2.888695605807202 + }, + "feature_256": { + "xyz": [ + 15.441638356253783, + 13.366073110108324, + 8.39814724500755 + ], + "observations": { + "1": [ + 990.0, + 369.0 + ], + "3": [ + 1003.1366577148438, + 363.8212890625 + ], + "5": [ + 1016.6925659179688, + 358.0599060058594 + ], + "7": [ + 1031.4591064453125, + 351.9288330078125 + ], + "9": [ + 1046.968994140625, + 346.0878601074219 + ], + "11": [ + 1063.0853271484375, + 339.6114196777344 + ], + "13": [ + 1080.462158203125, + 333.7259826660156 + ], + "15": [ + 1099.4407958984375, + 328.18060302734375 + ], + "17": [ + 1119.183349609375, + 321.9712219238281 + ], + "19": [ + 1139.7694091796875, + 315.2318115234375 + ], + "21": [ + 1160.8485107421875, + 306.912109375 + ], + "23": [ + 1183.1981201171875, + 296.94140625 + ], + "25": [ + 1206.3843994140625, + 285.2774963378906 + ], + "27": [ + 1230.3983154296875, + 272.08026123046875 + ], + "29": [ + 1256.1646728515625, + 259.22796630859375 + ] + }, + "projections": { + "1": [ + 991.7998354450299, + 369.115514749875 + ], + "3": [ + 1004.2005089742629, + 364.1632424005009 + ], + "5": [ + 1017.4297576027955, + 358.8798979926273 + ], + "7": [ + 1031.5773295815934, + 353.22959641543537 + ], + "9": [ + 1046.7464200120162, + 347.17107594710785 + ], + "11": [ + 1062.9750750388175, + 341.55291747329227 + ], + "13": [ + 1080.4746066663367, + 335.4587671509472 + ], + "15": [ + 1099.4044766176225, + 328.8288540433141 + ], + "17": [ + 1119.9519807675638, + 321.59297393188126 + ], + "19": [ + 1138.8656253518207, + 313.45498727290834 + ], + "21": [ + 1159.34979807322, + 304.684153478194 + ], + "23": [ + 1181.6084068740097, + 295.1982862873962 + ], + "25": [ + 1205.882256727109, + 284.90032824779917 + ], + "27": [ + 1230.4635786513736, + 272.3779452015125 + ], + "29": [ + 1257.3962838440982, + 258.7843013107878 + ] + }, + "mean_error_pixels": 1.3932414017941215, + "max_error_pixels": 2.6851307495607055 + }, + "feature_294": { + "xyz": [ + 16.766320053584863, + 13.90633661618298, + 7.497211712082907 + ], + "observations": { + "1": [ + 1010.0, + 397.0 + ], + "3": [ + 1023.2188110351562, + 392.7959899902344 + ], + "5": [ + 1037.414306640625, + 388.0790100097656 + ], + "7": [ + 1052.3636474609375, + 383.2801818847656 + ], + "9": [ + 1068.150390625, + 378.2850341796875 + ], + "11": [ + 1084.7109375, + 373.2574768066406 + ], + "13": [ + 1102.3514404296875, + 368.40557861328125 + ], + "15": [ + 1120.73681640625, + 364.31988525390625 + ], + "17": [ + 1139.6710205078125, + 359.15936279296875 + ], + "19": [ + 1160.00146484375, + 353.9875793457031 + ], + "21": [ + 1181.4205322265625, + 347.1698913574219 + ], + "23": [ + 1203.24609375, + 338.91339111328125 + ], + "25": [ + 1226.1016845703125, + 329.5064697265625 + ], + "27": [ + 1249.3499755859375, + 318.72113037109375 + ] + }, + "projections": { + "1": [ + 1011.2081534759116, + 395.6877469763891 + ], + "3": [ + 1023.8928913810264, + 391.7512133775588 + ], + "5": [ + 1037.4007386538321, + 387.5622297959002 + ], + "7": [ + 1051.818090632885, + 383.094274846513 + ], + "9": [ + 1067.2438717556413, + 378.3169810345037 + ], + "11": [ + 1083.7159503234702, + 374.09432832273075 + ], + "13": [ + 1101.436613427034, + 369.5196251290574 + ], + "15": [ + 1120.5572329129423, + 364.55014238527235 + ], + "17": [ + 1141.254672356907, + 359.13595504822723 + ], + "19": [ + 1160.1317017896088, + 352.76057528915527 + ], + "21": [ + 1180.5040402648533, + 345.9211540708972 + ], + "23": [ + 1202.5565089523093, + 338.56031579772355 + ], + "25": [ + 1226.5056977747226, + 330.6108229809737 + ], + "27": [ + 1250.555455072641, + 320.5696119439869 + ] + }, + "mean_error_pixels": 1.1846645718477824, + "max_error_pixels": 2.2068223576419994 + }, + "feature_309": { + "xyz": [ + 17.2352815104554, + 14.049910023896317, + 7.079769042625244 + ], + "observations": { + "1": [ + 1018.0, + 407.0 + ], + "3": [ + 1031.593505859375, + 403.18646240234375 + ], + "5": [ + 1045.891357421875, + 398.77593994140625 + ], + "7": [ + 1061.0675048828125, + 394.49224853515625 + ], + "9": [ + 1077.058349609375, + 390.16461181640625 + ], + "11": [ + 1093.50634765625, + 385.7275390625 + ], + "13": [ + 1111.3973388671875, + 381.7022399902344 + ], + "15": [ + 1129.854248046875, + 378.1448669433594 + ], + "17": [ + 1149.412109375, + 374.2394104003906 + ], + "19": [ + 1170.020263671875, + 369.8140869140625 + ], + "21": [ + 1191.5225830078125, + 364.104248046875 + ], + "23": [ + 1213.64453125, + 356.4893798828125 + ], + "25": [ + 1236.9324951171875, + 348.13916015625 + ], + "27": [ + 1260.86474609375, + 338.26300048828125 + ] + }, + "projections": { + "1": [ + 1019.3514751492403, + 406.51479940937327 + ], + "3": [ + 1032.2223904020927, + 402.95639420703577 + ], + "5": [ + 1045.9236860387598, + 399.17257856144624 + ], + "7": [ + 1060.5419424562476, + 395.139826354364 + ], + "9": [ + 1076.1762097099, + 390.8312231886298 + ], + "11": [ + 1092.8690703919415, + 387.11760187069615 + ], + "13": [ + 1110.819647288021, + 383.09348693761393 + ], + "15": [ + 1130.1794108880447, + 378.72143541901767 + ], + "17": [ + 1151.125164339639, + 373.9577420092575 + ], + "19": [ + 1170.1727599111337, + 368.19970642375364 + ], + "21": [ + 1190.7127635656925, + 362.03106676687923 + ], + "23": [ + 1212.9275469207123, + 355.401628012489 + ], + "25": [ + 1237.0304588703598, + 348.2526694536947 + ], + "27": [ + 1261.1742870079654, + 339.03683061882 + ] + }, + "mean_error_pixels": 1.1435928990703117, + "max_error_pixels": 2.2257331710158805 + }, + "feature_322": { + "xyz": [ + 15.586794064881122, + 13.402034998778944, + 9.212420477581599 + ], + "observations": { + "1": [ + 990.0, + 352.0 + ], + "3": [ + 1002.904052734375, + 346.3071594238281 + ], + "5": [ + 1016.38427734375, + 339.77484130859375 + ], + "7": [ + 1030.818603515625, + 332.8929443359375 + ], + "9": [ + 1046.192626953125, + 326.33392333984375 + ], + "11": [ + 1061.74169921875, + 319.17449951171875 + ], + "13": [ + 1078.9403076171875, + 312.1969909667969 + ], + "15": [ + 1097.499267578125, + 305.5798645019531 + ], + "17": [ + 1116.78955078125, + 298.36602783203125 + ], + "19": [ + 1137.0286865234375, + 290.6274108886719 + ], + "21": [ + 1157.943359375, + 281.4998779296875 + ], + "23": [ + 1179.5595703125, + 270.3554992675781 + ], + "25": [ + 1202.4652099609375, + 257.9054260253906 + ], + "27": [ + 1225.640380859375, + 243.79165649414062 + ], + "29": [ + 1250.7374267578125, + 229.02772521972656 + ] + }, + "projections": { + "1": [ + 991.3493169953899, + 351.20958278530225 + ], + "3": [ + 1003.6216533595157, + 345.710939214986 + ], + "5": [ + 1016.7051972348813, + 339.8468970945746 + ], + "7": [ + 1030.68687721794, + 333.5782762159606 + ], + "9": [ + 1045.6665020099522, + 326.8600910719625 + ], + "11": [ + 1061.6593159185668, + 320.55444351210934 + ], + "13": [ + 1078.8859181504654, + 313.72529606857387 + ], + "15": [ + 1097.4987822512373, + 306.30791032413697 + ], + "17": [ + 1117.676577258166, + 298.2264253445038 + ], + "19": [ + 1136.2639006693908, + 289.272900575545 + ], + "21": [ + 1156.3743805949694, + 279.62797216635886 + ], + "23": [ + 1178.2030620355927, + 269.2030825444414 + ], + "25": [ + 1201.9797776359476, + 257.89387963328056 + ], + "27": [ + 1226.0528502038405, + 244.30602979727442 + ], + "29": [ + 1252.4021491073868, + 229.55899794307385 + ] + }, + "mean_error_pixels": 1.1650365116898305, + "max_error_pixels": 2.442483490009593 + }, + "feature_355": { + "xyz": [ + 15.36456202192557, + 13.333969994164246, + 7.83176024232291 + ], + "observations": { + "1": [ + 991.0, + 381.0 + ], + "3": [ + 1004.1864013671875, + 376.3884582519531 + ], + "5": [ + 1018.0368041992188, + 371.26580810546875 + ], + "7": [ + 1032.93212890625, + 365.72723388671875 + ], + "9": [ + 1048.5843505859375, + 360.21441650390625 + ], + "11": [ + 1064.942626953125, + 354.5093994140625 + ], + "13": [ + 1082.6279296875, + 349.3219299316406 + ], + "15": [ + 1101.614501953125, + 344.12939453125 + ], + "17": [ + 1121.7669677734375, + 338.8671569824219 + ], + "19": [ + 1142.5640869140625, + 332.734619140625 + ], + "21": [ + 1164.303466796875, + 325.299560546875 + ], + "23": [ + 1186.758544921875, + 315.54986572265625 + ], + "25": [ + 1210.598876953125, + 304.6337585449219 + ], + "27": [ + 1235.084228515625, + 292.4495849609375 + ], + "29": [ + 1261.1170654296875, + 279.5980224609375 + ] + }, + "projections": { + "1": [ + 992.8392401170163, + 381.7512320945179 + ], + "3": [ + 1005.3608322925452, + 377.1898930145328 + ], + "5": [ + 1018.725922542812, + 372.3224013265717 + ], + "7": [ + 1033.0266123931685, + 367.11530343217396 + ], + "9": [ + 1048.3689183227575, + 361.53009148707923 + ], + "11": [ + 1064.8073599489062, + 356.40897284969236 + ], + "13": [ + 1082.547491608916, + 350.84704701572014 + ], + "15": [ + 1101.754486013201, + 344.7883309272443 + ], + "17": [ + 1122.6226871245115, + 338.16695410402144 + ], + "19": [ + 1141.8228130077046, + 330.6175875072206 + ], + "21": [ + 1162.6331072633511, + 322.4784725633957 + ], + "23": [ + 1185.2647970784556, + 313.6722229277996 + ], + "25": [ + 1209.9677464323245, + 304.1072974408563 + ], + "27": [ + 1234.9889225740858, + 292.350458382518 + ], + "29": [ + 1262.4247969248631, + 279.5863827101242 + ] + }, + "mean_error_pixels": 1.5195688910925758, + "max_error_pixels": 3.278511610741274 + }, + "feature_137": { + "xyz": [ + 23.44446695029657, + 15.77928454409526, + 7.774946919416613 + ], + "observations": { + "1": [ + 1095.0, + 408.0 + ], + "3": [ + 1109.7138671875, + 404.543212890625 + ], + "5": [ + 1125.1217041015625, + 400.7590026855469 + ], + "7": [ + 1141.1322021484375, + 397.2959289550781 + ], + "9": [ + 1157.7012939453125, + 394.1653747558594 + ], + "11": [ + 1175.17431640625, + 390.44342041015625 + ], + "13": [ + 1193.7574462890625, + 387.2022399902344 + ], + "15": [ + 1213.427978515625, + 384.568359375 + ], + "17": [ + 1232.89208984375, + 382.4753112792969 + ], + "19": [ + 1253.1497802734375, + 379.9324645996094 + ], + "21": [ + 1270.7276611328125, + 379.1866760253906 + ] + }, + "projections": { + "1": [ + 1097.0944686822104, + 408.1809717873506 + ], + "3": [ + 1110.8585894991277, + 405.08758940640007 + ], + "5": [ + 1125.413034362935, + 401.8218671923516 + ], + "7": [ + 1140.830609773851, + 398.36790863563215 + ], + "9": [ + 1157.1933481018282, + 394.70780291505207 + ], + "11": [ + 1174.4395351708304, + 391.8078938077326 + ], + "13": [ + 1192.814004395355, + 388.69334852238194 + ], + "15": [ + 1212.4340091559804, + 385.3418189043115 + ], + "17": [ + 1233.4336273331544, + 381.7277499462826 + ], + "19": [ + 1252.0778153601555, + 377.05717831258306 + ], + "21": [ + 1271.9458397788324, + 372.1139672702957 + ] + }, + "mean_error_pixels": 2.0064447145646063, + "max_error_pixels": 7.17684947229739 + }, + "feature_214": { + "xyz": [ + 24.607405062277973, + 16.1374396092958, + 7.99456904167137 + ], + "observations": { + "1": [ + 1107.0, + 407.0 + ], + "3": [ + 1121.506591796875, + 403.6138916015625 + ], + "5": [ + 1136.56787109375, + 399.97833251953125 + ], + "7": [ + 1152.442138671875, + 396.6143493652344 + ], + "9": [ + 1168.9974365234375, + 393.41778564453125 + ], + "11": [ + 1186.4063720703125, + 389.7631530761719 + ], + "13": [ + 1204.4112548828125, + 386.7535095214844 + ], + "15": [ + 1223.5069580078125, + 384.5106201171875 + ], + "17": [ + 1242.8184814453125, + 382.5706787109375 + ], + "19": [ + 1262.86376953125, + 379.98773193359375 + ] + }, + "projections": { + "1": [ + 1108.3283472598998, + 406.97272209439603 + ], + "3": [ + 1122.116628346644, + 403.9181704634073 + ], + "5": [ + 1136.6786837105933, + 400.6974536832456 + ], + "7": [ + 1152.0841179259319, + 397.2956048460662 + ], + "9": [ + 1168.4111417195586, + 393.695806539572 + ], + "11": [ + 1185.576647145416, + 390.87851906175473 + ], + "13": [ + 1203.8347943031654, + 387.85780441267667 + ], + "15": [ + 1223.2959692902782, + 384.613069650587 + ], + "17": [ + 1244.0859405059357, + 381.1208520616508 + ], + "19": [ + 1262.465600883454, + 376.5662972727687 + ] + }, + "mean_error_pixels": 1.239706964585737, + "max_error_pixels": 3.4445251356874764 + }, + "feature_223": { + "xyz": [ + 17.747872986961017, + 14.155382580646334, + 6.823738299293413 + ], + "observations": { + "1": [ + 1028.0, + 414.0 + ], + "3": [ + 1041.599609375, + 410.40936279296875 + ], + "5": [ + 1055.94189453125, + 406.30078125 + ], + "7": [ + 1071.159423828125, + 402.2292175292969 + ], + "9": [ + 1087.246337890625, + 398.35650634765625 + ], + "11": [ + 1103.891357421875, + 394.1748962402344 + ], + "13": [ + 1121.903564453125, + 390.56695556640625 + ], + "15": [ + 1141.0228271484375, + 387.23406982421875 + ], + "17": [ + 1161.234130859375, + 383.72625732421875 + ], + "19": [ + 1181.9163818359375, + 379.5663757324219 + ], + "21": [ + 1203.4598388671875, + 374.1496276855469 + ], + "23": [ + 1225.7567138671875, + 366.8321838378906 + ], + "25": [ + 1249.298583984375, + 358.7587585449219 + ], + "27": [ + 1274.2493896484375, + 349.83880615234375 + ] + }, + "projections": { + "1": [ + 1028.6964760530018, + 413.3312307265603 + ], + "3": [ + 1041.8035396772045, + 410.01445947471194 + ], + "5": [ + 1055.75236996083, + 406.4896366035156 + ], + "7": [ + 1070.630248985864, + 402.7351661146745 + ], + "9": [ + 1086.5369575285267, + 398.7263595433052 + ], + "11": [ + 1103.5164263124645, + 395.34361366300095 + ], + "13": [ + 1121.7685283480994, + 391.67754908520556 + ], + "15": [ + 1141.4456297651832, + 387.69419560467907 + ], + "17": [ + 1162.7254232352066, + 383.35393624825974 + ], + "19": [ + 1182.0322292458977, + 377.9927602061562 + ], + "21": [ + 1202.8376759958826, + 372.25561798164364 + ], + "23": [ + 1225.3231216844272, + 366.09699289215433 + ], + "25": [ + 1249.7004185169644, + 359.46371531247496 + ], + "27": [ + 1274.070115835153, + 350.7672780078233 + ] + }, + "mean_error_pixels": 0.992847291438635, + "max_error_pixels": 1.9935795436626036 + }, + "feature_348": { + "xyz": [ + 21.450749678835535, + 15.376224536610186, + 8.167323520880554 + ], + "observations": { + "1": [ + 1068.0, + 396.0 + ], + "3": [ + 1081.5501708984375, + 392.31146240234375 + ], + "5": [ + 1095.8375244140625, + 388.2604064941406 + ], + "7": [ + 1110.1839599609375, + 384.360107421875 + ], + "9": [ + 1125.985595703125, + 380.4214172363281 + ], + "11": [ + 1142.22119140625, + 376.236083984375 + ], + "13": [ + 1159.010986328125, + 372.8094177246094 + ], + "15": [ + 1177.9366455078125, + 369.5097961425781 + ], + "17": [ + 1197.4090576171875, + 365.88433837890625 + ], + "19": [ + 1217.65576171875, + 361.7532958984375 + ], + "21": [ + 1238.5380859375, + 356.3658142089844 + ], + "23": [ + 1259.867919921875, + 349.2958679199219 + ] + }, + "projections": { + "1": [ + 1068.0618744693672, + 396.40192111439944 + ], + "3": [ + 1081.2618879172066, + 392.8821985935425 + ], + "5": [ + 1095.2355445574942, + 389.16026754522255 + ], + "7": [ + 1110.0557756106864, + 385.21704499488044 + ], + "9": [ + 1125.8049763938025, + 381.0309716649276 + ], + "11": [ + 1142.4350491057635, + 377.54062494031683 + ], + "13": [ + 1160.1797711375384, + 373.78955248806665 + ], + "15": [ + 1179.1580360274706, + 369.74990132161577 + ], + "17": [ + 1199.5062638779373, + 365.3897123724754 + ], + "19": [ + 1217.7071673119026, + 360.03300678967076 + ], + "21": [ + 1237.1466994165578, + 354.3469347120081 + ], + "23": [ + 1257.955593198106, + 348.296715837826 + ] + }, + "mean_error_pixels": 1.3506950695263438, + "max_error_pixels": 2.4519035205266375 + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/sky_probe_layered_response_fit.json b/astra_scene_test/Scene_04/reference/sky_probe_layered_response_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..ac30763fa7be4580dd9e65cca924db474d4d9dff --- /dev/null +++ b/astra_scene_test/Scene_04/reference/sky_probe_layered_response_fit.json @@ -0,0 +1,113 @@ +{ + "1": { + "source_srgb_mean": [ + 0.26886695715316794, + 0.2553095134349205, + 0.1619215686273905 + ], + "zero_linear": [ + 0.0020528698805719614, + 0.001900062314234674, + 0.0008688705856911838 + ], + "original_linear": [ + 0.02811931073665619, + 0.02059081941843033, + 0.002912279684096575 + ], + "target_linear": [ + 0.035548978188916634, + 0.03193429512844669, + 0.013092195833721712 + ], + "gain": [ + 1.2850280632513877, + 1.6069029245618758, + 5.981829877217212 + ], + "old_control": [ + 1, + 1, + 1 + ], + "proposed_control": [ + 1.2850280632513877, + 1.6069029245618758, + 5.981829877217212 + ] + }, + "65": { + "source_srgb_mean": [ + 0.2641262164124202, + 0.28641641249085065, + 0.23202178649226193 + ], + "zero_linear": [ + 0.0025316609535366297, + 0.0025382377207279205, + 0.0015589604154229164 + ], + "original_linear": [ + 0.029938995838165283, + 0.030627254396677017, + 0.013537351042032242 + ], + "target_linear": [ + 0.0342579926513042, + 0.04058529804449659, + 0.026269379557944433 + ], + "gain": [ + 1.1575854260108982, + 1.3545173461464046, + 2.0629164562081237 + ], + "old_control": [ + 1.05, + 1.5, + 6.0 + ], + "proposed_control": [ + 1.2154646973114431, + 2.031776019219607, + 12.377498737248743 + ] + }, + "128": { + "source_srgb_mean": [ + 0.21015511982559837, + 0.23531343500350518, + 0.1962235294116668 + ], + "zero_linear": [ + 0.003622727934271097, + 0.003992745652794838, + 0.003103633178398013 + ], + "original_linear": [ + 0.019419824704527855, + 0.022201647982001305, + 0.01016236748546362 + ], + "target_linear": [ + 0.02155382708083712, + 0.02702920815486562, + 0.01884268987815366 + ], + "gain": [ + 1.135088230374114, + 1.2651208779960927, + 2.2297279560657586 + ], + "old_control": [ + 0.6, + 0.97, + 3.5 + ], + "proposed_control": [ + 0.6810529382244684, + 1.22716725165621, + 7.804047846230155 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/sky_response_probe_fit.json b/astra_scene_test/Scene_04/reference/sky_response_probe_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..210efbdc0641f23f208f8df665de36aed516371e --- /dev/null +++ b/astra_scene_test/Scene_04/reference/sky_response_probe_fit.json @@ -0,0 +1,113 @@ +{ + "1": { + "source_srgb_mean": [ + 0.26886695715316794, + 0.2553095134349205, + 0.1619215686273905 + ], + "zero_linear": [ + 0.019171401858329773, + 0.022271208465099335, + 0.016406700015068054 + ], + "original_linear": [ + 0.03514198958873749, + 0.03355832397937775, + 0.017805492505431175 + ], + "target_linear": [ + 0.03505124749877442, + 0.03143818423502139, + 0.012644012193560185 + ], + "gain": [ + 0.9943181746661522, + 0.8121628380896485, + -2.6899542622873893 + ], + "old_control": [ + 1, + 1, + 1 + ], + "proposed_control": [ + 0.9943181746661522, + 0.8121628380896485, + -2.6899542622873893 + ] + }, + "65": { + "source_srgb_mean": [ + 0.2641262164124202, + 0.28641641249085065, + 0.23202178649226193 + ], + "zero_linear": [ + 0.020974431186914444, + 0.0256173275411129, + 0.0235085841268301 + ], + "original_linear": [ + 0.037747710943222046, + 0.042497385293245316, + 0.03086157515645027 + ], + "target_linear": [ + 0.03376068906832274, + 0.04008724816965474, + 0.025779005371493947 + ], + "gain": [ + 0.7622992084538515, + 0.8572198532148915, + 0.30877519576970414 + ], + "old_control": [ + 1.05, + 1.5, + 6.0 + ], + "proposed_control": [ + 0.800414168876544, + 1.2858297798223373, + 1.8526511746182248 + ] + }, + "128": { + "source_srgb_mean": [ + 0.21015511982559837, + 0.23531343500350518, + 0.1962235294116668 + ], + "zero_linear": [ + 0.024533528834581375, + 0.030980871990323067, + 0.03217631205916405 + ], + "original_linear": [ + 0.03410160914063454, + 0.04182152822613716, + 0.03643350675702095 + ], + "target_linear": [ + 0.021072438194005574, + 0.026537788880024723, + 0.01836894358309256 + ], + "gain": [ + -0.3617330258386494, + -0.409853703839423, + -3.2433020935176393 + ], + "old_control": [ + 0.6, + 0.97, + 3.5 + ], + "proposed_control": [ + -0.21703981550318965, + -0.3975580927242403, + -11.351557327311738 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/sky_series_fit.json b/astra_scene_test/Scene_04/reference/sky_series_fit.json new file mode 100644 index 0000000000000000000000000000000000000000..30457bf00a9992595385ab3f74be290c83eab317 --- /dev/null +++ b/astra_scene_test/Scene_04/reference/sky_series_fit.json @@ -0,0 +1,811 @@ +{ + "controls": { + "1": [ + 1.2605054062517196, + 1.6010563538455556, + 5.981444461917436 + ], + "17": [ + 1.0526570456656525, + 1.4420291016719715, + 6.014321509967891 + ], + "33": [ + 1.0946919130656403, + 1.4779059156328034, + 6.252126070894234 + ], + "49": [ + 1.225149642569033, + 1.7874155565200063, + 8.326589211542156 + ], + "65": [ + 1.2242025797155145, + 2.03143319103788, + 11.878108774815308 + ], + "81": [ + 1.027097816524482, + 1.7479561324369874, + 9.746948941715356 + ], + "97": [ + 0.7570487086400081, + 1.361378012245788, + 7.215861814136287 + ], + "113": [ + 0.5682937110288511, + 1.1020568822706571, + 6.685394646675636 + ], + "128": [ + 0.6520111661770153, + 1.2056391631303078, + 7.768134709770933 + ] + }, + "measurements_srgb": [ + [ + 0.26886695715316794, + 0.2553095134349205, + 0.1619215686273905 + ], + [ + 0.2656938271604354, + 0.25413594771232745, + 0.1627182280318917 + ], + [ + 0.2650383442265127, + 0.25521060275954055, + 0.16110907770509558 + ], + [ + 0.26412984749449187, + 0.2543522149599653, + 0.15881452432819215 + ], + [ + 0.2593044299200478, + 0.25335047204057676, + 0.16322251270872504 + ], + [ + 0.26086071169201436, + 0.25303689179367306, + 0.15671227305731703 + ], + [ + 0.2633349310093827, + 0.25189760348573825, + 0.1625413217138096 + ], + [ + 0.2582688453158343, + 0.250243718227919, + 0.16097051561359219 + ], + [ + 0.25487102396506617, + 0.2475679012344513, + 0.15601176470582842 + ], + [ + 0.25384749455328004, + 0.24751590413931956, + 0.15948903413211163 + ], + [ + 0.25489237472756143, + 0.2475267973855117, + 0.1571233115467858 + ], + [ + 0.24524749455325962, + 0.2466809005082232, + 0.15927712418294757 + ], + [ + 0.25294451706597637, + 0.24870936819161785, + 0.1580090050834578 + ], + [ + 0.24845940450242218, + 0.24884008714585462, + 0.16742236746543643 + ], + [ + 0.24561437908485984, + 0.2465763253448359, + 0.1630697167755367 + ], + [ + 0.25070472040659275, + 0.24522440087132893, + 0.16361379811177443 + ], + [ + 0.24381002178637606, + 0.24234175744358719, + 0.1632499636891169 + ], + [ + 0.2449279593317608, + 0.24383776325332018, + 0.16265795206965517 + ], + [ + 0.24560697167744236, + 0.24050748002891625, + 0.16263297022506365 + ], + [ + 0.24530152505434574, + 0.24166811909936248, + 0.16032345679006307 + ], + [ + 0.24376775599116576, + 0.24161931735644548, + 0.16123427741460872 + ], + [ + 0.24692403776314567, + 0.24449251997082663, + 0.16421031227299396 + ], + [ + 0.24074901960771813, + 0.2448742193172374, + 0.17590530137973379 + ], + [ + 0.24509629629617444, + 0.24257923021047392, + 0.16374480755258738 + ], + [ + 0.2525185185184186, + 0.24802875816981654, + 0.17180159767603456 + ], + [ + 0.24957022512697535, + 0.2457673202613285, + 0.16501423384161978 + ], + [ + 0.2519247639795657, + 0.2490893246186329, + 0.16704793028315626 + ], + [ + 0.2578460421204631, + 0.2524078431371603, + 0.15967233115462498 + ], + [ + 0.24762352941165502, + 0.2510530137980035, + 0.1814055192446508 + ], + [ + 0.2528008714595973, + 0.25262193173554687, + 0.17479912854022808 + ], + [ + 0.24671604938258965, + 0.24587567175006556, + 0.1670029048655819 + ], + [ + 0.25005984023227895, + 0.24547639796646975, + 0.1656275962236096 + ], + [ + 0.2514148148147034, + 0.24362962962949797, + 0.16048772694256863 + ], + [ + 0.24437414669559293, + 0.2473657225852079, + 0.17537603485831105 + ], + [ + 0.2626708787217891, + 0.25332578068254064, + 0.16583485838773407 + ], + [ + 0.26008496732018377, + 0.2576416848219946, + 0.17012360203333618 + ], + [ + 0.2563005083513949, + 0.2551305737108699, + 0.17963471314443658 + ], + [ + 0.24593638344214386, + 0.2407677559911519, + 0.1597854756716911 + ], + [ + 0.26504081336231133, + 0.2567747276687489, + 0.16716528685541515 + ], + [ + 0.26575192447342794, + 0.26077066085685763, + 0.17487828612919024 + ], + [ + 0.2627087872185072, + 0.2597905591865541, + 0.17592926652134552 + ], + [ + 0.25576586782852123, + 0.26157211328968705, + 0.18933943355110575 + ], + [ + 0.26255061728386986, + 0.2618573710965129, + 0.18021307189534166 + ], + [ + 0.2598639070442181, + 0.26307334785758335, + 0.18872098765422796 + ], + [ + 0.26480522875810225, + 0.26384517066079494, + 0.17727160493819233 + ], + [ + 0.2636534495278819, + 0.26523151779224224, + 0.18430413943346577 + ], + [ + 0.2575912854029687, + 0.2667487291212123, + 0.1990627450979368 + ], + [ + 0.2656535947711594, + 0.268429339143003, + 0.19040130718945003 + ], + [ + 0.2668198983296358, + 0.2701586056644303, + 0.18656078431363599 + ], + [ + 0.26251474219310594, + 0.26984429920109926, + 0.20036964415385522 + ], + [ + 0.27031735657219846, + 0.27431764705876555, + 0.18478373275227405 + ], + [ + 0.26712592592586193, + 0.27426899055912723, + 0.19566114742183416 + ], + [ + 0.2650784313724821, + 0.27595410312267127, + 0.1972605664487024 + ], + [ + 0.2678872912127086, + 0.2754684095859944, + 0.19577923021050456 + ], + [ + 0.26352534495272245, + 0.2778903413216508, + 0.21380305010881911 + ], + [ + 0.2641279593318073, + 0.2784955700798229, + 0.2080248366011978 + ], + [ + 0.2649580246912833, + 0.27920856935360594, + 0.2108883079156474 + ], + [ + 0.26362062454604424, + 0.280974872912062, + 0.21252912127802756 + ], + [ + 0.2682082788670298, + 0.2819082062453956, + 0.21770631808267196 + ], + [ + 0.2677450980391465, + 0.28254495279586733, + 0.21166942628892055 + ], + [ + 0.26753086419745814, + 0.28253333333326575, + 0.21821220043561698 + ], + [ + 0.26710689905584595, + 0.2846245461146725, + 0.21902977487280006 + ], + [ + 0.2683265068989854, + 0.2861400145242566, + 0.22261176470576868 + ], + [ + 0.26381815541024073, + 0.2871973856208428, + 0.22572389251985087 + ], + [ + 0.2641262164124202, + 0.28641641249085065, + 0.23202178649226193 + ], + [ + 0.26441002178642237, + 0.2864464778503281, + 0.22912810457504548 + ], + [ + 0.2641771968045678, + 0.28577429193892706, + 0.22857937545377105 + ], + [ + 0.26974901960777087, + 0.28657879448068274, + 0.2352291938996642 + ], + [ + 0.26404328249810605, + 0.2869314451705884, + 0.22901902686989203 + ], + [ + 0.26614117647051766, + 0.28686187363827165, + 0.2293802469134619 + ], + [ + 0.2632387799563507, + 0.2857713870732763, + 0.22280595497446645 + ], + [ + 0.26318779956418736, + 0.2846897603485142, + 0.2275651416120881 + ], + [ + 0.2616978939723231, + 0.2796135076252002, + 0.21990370370358744 + ], + [ + 0.2547944807551696, + 0.2784127814087957, + 0.22021336238187642 + ], + [ + 0.25192578068254434, + 0.27502411038482905, + 0.21813914306451646 + ], + [ + 0.251239796659307, + 0.2757901234567274, + 0.21082381989822097 + ], + [ + 0.250890050835058, + 0.27487625272324484, + 0.21469905591855085 + ], + [ + 0.25033144517056566, + 0.2731193899781499, + 0.21543848946975033 + ], + [ + 0.24909978213497752, + 0.2718389251996433, + 0.2117002178648137 + ], + [ + 0.24921626724754256, + 0.2692313725489435, + 0.21205751633975978 + ], + [ + 0.24793071895415061, + 0.27089324618729027, + 0.20881612200424904 + ], + [ + 0.24346027596213732, + 0.26777051561358134, + 0.21014669571521857 + ], + [ + 0.24209949164841074, + 0.2666203340594736, + 0.20574974582415476 + ], + [ + 0.2420620188815276, + 0.26558169934632503, + 0.20123267973846343 + ], + [ + 0.24018373275225724, + 0.2644413943354332, + 0.20126608569343804 + ], + [ + 0.23211314451696083, + 0.2623179375453018, + 0.2073724037762208 + ], + [ + 0.23599883805363975, + 0.26166623093673774, + 0.202475236020234 + ], + [ + 0.23479796659394392, + 0.2598124909222087, + 0.20107131445160706 + ], + [ + 0.23109716775589434, + 0.2575799564269303, + 0.19945504720396678 + ], + [ + 0.23254625998537573, + 0.25928438634704176, + 0.19597792302096442 + ], + [ + 0.22414277414659756, + 0.2548824981843693, + 0.1996050835147894 + ], + [ + 0.22127785039931902, + 0.25193565722575473, + 0.19556993464042963 + ], + [ + 0.2215115468408594, + 0.25077022512698754, + 0.19670718954238783 + ], + [ + 0.22153202614369216, + 0.2500591140158758, + 0.19384691358015194 + ], + [ + 0.22096949891057768, + 0.24885954974572638, + 0.19040348583868916 + ], + [ + 0.22176034858377577, + 0.24664488017419525, + 0.18613725490187225 + ], + [ + 0.21895279593308686, + 0.2453416122003339, + 0.18681307189533478 + ], + [ + 0.21168061002168492, + 0.23850602759611797, + 0.18369978213499033 + ], + [ + 0.21080363108195652, + 0.23454364560627441, + 0.17808685548285288 + ], + [ + 0.2087283950616248, + 0.23200348583865973, + 0.17828598402315768 + ], + [ + 0.20573333333322652, + 0.23102236746538568, + 0.17653173565714736 + ], + [ + 0.20620348583867062, + 0.23059665940438498, + 0.1775636891792951 + ], + [ + 0.20462338416837414, + 0.2296503994189009, + 0.17730689905583846 + ], + [ + 0.20153638344216102, + 0.2293828612925422, + 0.17664705882345072 + ], + [ + 0.20099564270142076, + 0.22736761074787554, + 0.17831895424828545 + ], + [ + 0.19919143064622905, + 0.22741568627437989, + 0.1758861292664436 + ], + [ + 0.20280871459684427, + 0.22988264342761222, + 0.17777618010159027 + ], + [ + 0.1964069716774601, + 0.2250450254174468, + 0.17379215686267022 + ], + [ + 0.19699186637608054, + 0.22435395787932128, + 0.17616819172105563 + ], + [ + 0.19403805373991703, + 0.22363907044286566, + 0.18040856935358507 + ], + [ + 0.19704589687716925, + 0.22310675381251005, + 0.17885025417566347 + ], + [ + 0.19399651416112318, + 0.22365853304272046, + 0.18020261437900223 + ], + [ + 0.19214350036301264, + 0.2240008714595681, + 0.18195381263608038 + ], + [ + 0.19027160493817843, + 0.22233594771229304, + 0.1855828612925772 + ], + [ + 0.1928432824980886, + 0.22345199709500824, + 0.1800177196803831 + ], + [ + 0.1908506899054985, + 0.22291735657213302, + 0.18213013798103428 + ], + [ + 0.19315206971668, + 0.22294074074061535, + 0.1807673202613549 + ], + [ + 0.1911822803194411, + 0.22312912127801557, + 0.18604996368909205 + ], + [ + 0.19406506899046272, + 0.22418416848208195, + 0.18929847494544083 + ], + [ + 0.1990794480754251, + 0.22824008714584043, + 0.18803631082053218 + ], + [ + 0.20094582425552573, + 0.22869832970212228, + 0.1905445170659918 + ], + [ + 0.2009288307914758, + 0.23055323166290276, + 0.19050079883795992 + ], + [ + 0.20135497458232324, + 0.2302915032678423, + 0.1937827160492861 + ], + [ + 0.2029231663034549, + 0.23270312273044558, + 0.19216310820615018 + ], + [ + 0.20371924473482628, + 0.2328204793027041, + 0.1920515613651917 + ], + [ + 0.20261655773410034, + 0.23124371822790404, + 0.1950074074073094 + ], + [ + 0.20624981844579018, + 0.2341525054464929, + 0.19627392883069336 + ], + [ + 0.21015511982559837, + 0.23531343500350518, + 0.1962235294116668 + ] + ], + "fit": [ + { + "frame": 1, + "target_linear": [ + 0.03490975950140683, + 0.031825017734091084, + 0.01309140862077981 + ], + "control": [ + 1.2605054062517196, + 1.6010563538455556, + 5.981444461917436 + ] + }, + { + "frame": 17, + "target_linear": [ + 0.029621007036994617, + 0.029024931170601842, + 0.01326042803046077 + ], + "control": [ + 1.0526570456656525, + 1.4420291016719715, + 6.014321509967891 + ] + }, + { + "frame": 33, + "target_linear": [ + 0.03084657301935047, + 0.029868381566685805, + 0.01384260850971007 + ], + "control": [ + 1.0946919130656403, + 1.4779059156328034, + 6.252126070894234 + ] + }, + { + "frame": 49, + "target_linear": [ + 0.034380133386674054, + 0.03583410405886929, + 0.0181074872667727 + ], + "control": [ + 1.225149642569033, + 1.7874155565200063, + 8.326589211542156 + ] + }, + { + "frame": 65, + "target_linear": [ + 0.03448607054295511, + 0.04057887824015425, + 0.025272398217105193 + ], + "control": [ + 1.2242025797155145, + 2.03143319103788, + 11.878108774815308 + ] + }, + { + "frame": 81, + "target_linear": [ + 0.029677317120858193, + 0.03566032668608801, + 0.02146050778364473 + ], + "control": [ + 1.027097816524482, + 1.7479561324369874, + 9.746948941715356 + ] + }, + { + "frame": 97, + "target_linear": [ + 0.022933517011810737, + 0.0288020606436745, + 0.016823998362596328 + ], + "control": [ + 0.7570487086400081, + 1.361378012245788, + 7.215861814136287 + ] + }, + { + "frame": 113, + "target_linear": [ + 0.01829465172218743, + 0.024322232758808737, + 0.016186387864474967 + ], + "control": [ + 0.5682937110288511, + 1.1020568822706571, + 6.685394646675636 + ] + }, + { + "frame": 128, + "target_linear": [ + 0.020789200413248214, + 0.026625081493728858, + 0.018770261457760073 + ], + "control": [ + 0.6520111661770153, + 1.2056391631303078, + 7.768134709770933 + ] + } + ], + "note": "Camera-visible background response only. Lighting world remains constant. Coefficients inferred from separated layer-scatter/background crops." +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/source_signal_tracks.json b/astra_scene_test/Scene_04/reference/source_signal_tracks.json new file mode 100644 index 0000000000000000000000000000000000000000..b2950e5f8988e572df1c71d7b3aaed3578bd821b --- /dev/null +++ b/astra_scene_test/Scene_04/reference/source_signal_tracks.json @@ -0,0 +1,1030 @@ +{ + "left": { + "1": [ + 530.8274643912322, + 474.50536228809517 + ], + "2": [ + 530.5244857681793, + 474.1591234887042 + ], + "3": [ + 530.219084064053, + 473.94966409587687 + ], + "4": [ + 529.9713402584281, + 473.7927926252329 + ], + "5": [ + 529.3303969609101, + 473.570336829295 + ], + "6": [ + 528.7716424800882, + 472.5985089758267 + ], + "7": [ + 528.5914639908933, + 472.0268717186786 + ], + "8": [ + 527.8804917105366, + 471.55207372624034 + ], + "9": [ + 527.4075111722369, + 471.04445124583805 + ], + "10": [ + 526.6792329286989, + 470.81013299176726 + ], + "11": [ + 526.2004045617715, + 470.458491818242 + ], + "12": [ + 525.4551184575015, + 470.1013633809708 + ], + "13": [ + 524.8790153319486, + 469.8060756876257 + ], + "14": [ + 524.1525622470226, + 469.76809455072276 + ], + "15": [ + 523.8096963572335, + 469.5036953873244 + ], + "16": [ + 523.3038799596918, + 469.4982070379594 + ], + "17": [ + 522.7461435363443, + 469.50030274154517 + ], + "18": [ + 522.17562006899, + 469.3960117210161 + ], + "19": [ + 521.4859533953672, + 469.11429275748606 + ], + "20": [ + 520.4864569282028, + 468.51732418022056 + ], + "21": [ + 519.7849379413818, + 467.77367926241186 + ], + "22": [ + 518.6707567368431, + 466.9392187452801 + ], + "23": [ + 517.6760813951738, + 466.1878646372145 + ], + "24": [ + 516.7382913190811, + 465.11567515845155 + ], + "25": [ + 515.7604650747422, + 464.18746533592065 + ], + "26": [ + 514.5313303541401, + 463.02120337835214 + ], + "27": [ + 512.9936232955174, + 461.82949689701314 + ], + "28": [ + 511.7850046728006, + 460.85714807219983 + ], + "29": [ + 510.49295649599344, + 459.7578654173329 + ], + "30": [ + 509.2951786372752, + 458.6226336185982 + ], + "31": [ + 508.28061161641244, + 457.6213720634277 + ], + "32": [ + 507.03883930065876, + 457.03321567438525 + ], + "33": [ + 505.7775310668184, + 456.5438171787632 + ], + "34": [ + 504.4815550647073, + 456.28012748557325 + ], + "35": [ + 503.5315671759844, + 456.0301614091996 + ], + "36": [ + 502.16510873701674, + 455.9424072760123 + ], + "37": [ + 501.22565715726404, + 455.6817987410751 + ], + "38": [ + 500.0629145496162, + 455.4448594226584 + ], + "39": [ + 499.21082063834524, + 455.16429727111495 + ], + "40": [ + 497.9467468717775, + 455.02328309510835 + ], + "41": [ + 497.0702211747837, + 454.91754681721613 + ], + "42": [ + 496.08541936347814, + 454.93877060613573 + ], + "43": [ + 494.83025605732297, + 454.7105381210494 + ], + "44": [ + 493.5770079125901, + 454.28612884944766 + ], + "45": [ + 492.32805180682135, + 453.6490794844575 + ], + "46": [ + 491.10359710661083, + 453.1438548331015 + ], + "47": [ + 489.98602504320326, + 451.92606342787377 + ], + "48": [ + 488.58448286325296, + 450.6443898978761 + ], + "49": [ + 487.5620242801058, + 449.41114889967537 + ], + "50": [ + 486.3496178345775, + 448.4253221696501 + ], + "51": [ + 485.0915395991696, + 447.2052208912188 + ], + "52": [ + 483.878589353552, + 446.2442784776884 + ], + "53": [ + 482.32935658185374, + 446.27249501771087 + ], + "54": [ + 480.88557379800295, + 446.0355629589287 + ], + "55": [ + 479.46577329174545, + 446.02694885478974 + ], + "56": [ + 477.8755470352215, + 446.0223305563235 + ], + "57": [ + 476.6176345704041, + 446.0197448651874 + ], + "58": [ + 475.24612082107615, + 446.15112920238994 + ], + "59": [ + 473.9064954748311, + 446.22130739786934 + ], + "60": [ + 472.3509836708225, + 446.1293532728193 + ], + "61": [ + 470.9156411669908, + 446.20002730724434 + ], + "62": [ + 469.5193054066985, + 445.6917680654495 + ], + "63": [ + 467.84381504980115, + 445.0360390752064 + ], + "64": [ + 466.3487262992165, + 444.3931620752099 + ], + "65": [ + 464.77210946445894, + 443.5414443555413 + ], + "66": [ + 462.9746935379626, + 442.58125871370265 + ], + "67": [ + 461.2938647688374, + 441.41326073391843 + ], + "68": [ + 459.63322571193055, + 440.49266983702756 + ], + "69": [ + 457.7655534920947, + 439.4314662764563 + ], + "70": [ + 456.07755131752424, + 438.0256136443234 + ], + "71": [ + 453.725664861758, + 436.9761798491169 + ], + "72": [ + 451.6580622824271, + 435.9762142912473 + ], + "73": [ + 449.4785235012257, + 435.1575426685473 + ], + "74": [ + 447.4082128573904, + 434.36775177335 + ], + "75": [ + 445.0586749051566, + 433.59080085422073 + ], + "76": [ + 442.65295669307125, + 432.9326282186534 + ], + "77": [ + 440.44725446121004, + 432.4039529876186 + ], + "78": [ + 438.11860240380867, + 431.58919237808516 + ], + "79": [ + 435.7264857452912, + 430.82134829185696 + ], + "80": [ + 433.340169090518, + 430.06002106901536 + ], + "81": [ + 430.53398041773625, + 429.17289305893706 + ], + "82": [ + 427.7090481878058, + 428.06656071282174 + ], + "83": [ + 425.40267662398355, + 426.9641854682367 + ], + "84": [ + 422.5182008445275, + 425.8505860358808 + ], + "85": [ + 420.0825428684464, + 424.4462877929625 + ], + "86": [ + 417.42828076284445, + 423.1430069322171 + ], + "87": [ + 414.54970761580387, + 421.47925570185896 + ], + "88": [ + 411.3492325782604, + 420.0190395897864 + ], + "89": [ + 408.61792615198505, + 418.30625375336183 + ], + "90": [ + 405.51797197068913, + 416.55405945181195 + ], + "91": [ + 402.82264778879824, + 414.5464971665908 + ], + "92": [ + 398.8068922557439, + 412.4766292070686 + ], + "93": [ + 395.70382645132275, + 410.3623737935808 + ], + "94": [ + 392.47250765611517, + 408.12339947822227 + ], + "95": [ + 389.3843045245399, + 406.0061343005655 + ], + "96": [ + 385.6312903231487, + 404.65124713497335 + ], + "97": [ + 382.0496694128485, + 402.77606119258485 + ], + "98": [ + 378.39429310569227, + 401.1329659239203 + ], + "99": [ + 374.40843743494423, + 399.5829263835591 + ], + "100": [ + 370.42343029489984, + 398.1209723139756 + ], + "101": [ + 366.2344150692428, + 396.7124922285568 + ], + "102": [ + 361.69563971955375, + 395.1471110088826 + ], + "103": [ + 357.4674994750693, + 393.5168763192105 + ], + "104": [ + 353.0573021990487, + 391.51306515937716 + ], + "105": [ + 348.2681298086685, + 389.5859168275093 + ], + "106": [ + 343.50872607859424, + 387.70248329406826 + ], + "107": [ + 338.0795484918839, + 385.2113486863693 + ], + "108": [ + 332.86798785578026, + 382.82364632956296 + ], + "109": [ + 327.31081513831066, + 380.312703419178 + ], + "110": [ + 321.20245910768864, + 377.7912142397787 + ], + "111": [ + 315.2862513746154, + 375.0578027679985 + ], + "112": [ + 309.1869290696538, + 372.55984901773644 + ], + "113": [ + 303.0284726044948, + 369.76249826438607 + ], + "114": [ + 296.44760374275285, + 367.0043816972769 + ], + "115": [ + 289.7451759138377, + 364.2990445780973 + ], + "116": [ + 282.62644809614085, + 361.7078951599764 + ], + "117": [ + 275.36136232139273, + 359.1977092245822 + ], + "118": [ + 267.7098131368736, + 356.35210304250927 + ], + "119": [ + 259.86133586270194, + 353.49979083129773 + ], + "120": [ + 251.74378830178054, + 350.6241591842861 + ], + "121": [ + 243.49235138312363, + 347.50437104409974 + ], + "122": [ + 234.83339121829513, + 344.1076324309318 + ], + "123": [ + 226.22437437588658, + 340.39906349204443 + ], + "124": [ + 217.06327121736237, + 336.4591531399487 + ], + "125": [ + 207.97319702624858, + 332.71444143686256 + ], + "126": [ + 198.16763178490984, + 328.511435857386 + ], + "127": [ + 188.03018679135266, + 325.15225677053144 + ], + "128": [ + 177.8868142744232, + 320.78397563797586 + ] + }, + "right": { + "1": [ + 696.6114038794435, + 474.7870197078353 + ], + "2": [ + 697.2743251574282, + 474.2548391055962 + ], + "3": [ + 698.3742771758091, + 473.76380876976737 + ], + "4": [ + 699.2751914042923, + 473.5446523064039 + ], + "5": [ + 700.6250958814672, + 472.78462962608154 + ], + "6": [ + 700.8392771284119, + 472.227574203598 + ], + "7": [ + 702.0933445136086, + 471.0208630974202 + ], + "8": [ + 702.894841088713, + 469.86868961655534 + ], + "9": [ + 703.8474118216516, + 468.76067559745854 + ], + "10": [ + 703.6544368556292, + 470.1109761898246 + ], + "11": [ + 704.0589548299523, + 469.70037241903435 + ], + "12": [ + 703.7587982676824, + 470.46613470833375 + ], + "13": [ + 703.561034993854, + 471.0972334823426 + ], + "14": [ + 704.3043772168215, + 471.1352059664128 + ], + "15": [ + 705.2301786061817, + 470.90431219530404 + ], + "16": [ + 706.9819159616712, + 469.744123745171 + ], + "17": [ + 707.4539420382821, + 469.4936586363414 + ], + "18": [ + 707.7494201869096, + 470.0050261074085 + ], + "19": [ + 708.771935499065, + 468.6489559822967 + ], + "20": [ + 708.3158901497764, + 469.24262031467464 + ], + "21": [ + 708.3869746560046, + 469.035976683763 + ], + "22": [ + 707.6259096065401, + 470.20119092206886 + ], + "23": [ + 709.4410794447203, + 467.21767983722714 + ], + "24": [ + 709.0129327503406, + 467.50913858197487 + ], + "25": [ + 708.9809655168734, + 466.80726985490105 + ], + "26": [ + 709.4916002165116, + 465.14151327699017 + ], + "27": [ + 708.3493842006634, + 465.04517727713767 + ], + "28": [ + 708.5346088498272, + 463.01958227910376 + ], + "29": [ + 708.3904499619515, + 462.15856934850405 + ], + "30": [ + 709.202349351871, + 461.0147384342765 + ], + "31": [ + 711.054715825263, + 457.8102578943004 + ], + "32": [ + 710.9209886491392, + 458.16836290787245 + ], + "33": [ + 711.0631672780644, + 457.1560938569029 + ], + "34": [ + 711.7854509081621, + 456.307739237 + ], + "35": [ + 710.4182637041523, + 457.8612535940404 + ], + "36": [ + 710.6208510701682, + 457.4695893067918 + ], + "37": [ + 712.051961368402, + 456.88081973319277 + ], + "38": [ + 712.6912284091536, + 456.2642972566045 + ], + "39": [ + 712.4041165064048, + 457.5740427210542 + ], + "40": [ + 712.7500089258612, + 457.65138216401107 + ], + "41": [ + 713.3890169152669, + 457.8926266651114 + ], + "42": [ + 713.8630208504226, + 457.4365368847405 + ], + "43": [ + 714.4724195682245, + 457.5298925420514 + ], + "44": [ + 714.7348338827144, + 457.40292221872033 + ], + "45": [ + 715.4376772239192, + 456.8044414522113 + ], + "46": [ + 715.6031043080519, + 456.13738866786684 + ], + "47": [ + 716.3656881268463, + 455.08600955593727 + ], + "48": [ + 716.991952010743, + 454.5664957792037 + ], + "49": [ + 717.646381335936, + 454.1673586380901 + ], + "50": [ + 718.3534902807859, + 453.2835220057346 + ], + "51": [ + 719.0969050802481, + 452.4877274057314 + ], + "52": [ + 719.2675689526236, + 451.72994584735784 + ], + "53": [ + 719.8430768332184, + 451.60614994344166 + ], + "54": [ + 720.2779915436101, + 451.5278320108679 + ], + "55": [ + 720.7317148475049, + 451.4911496030879 + ], + "56": [ + 721.2781846829711, + 451.76071047742744 + ], + "57": [ + 721.6898738491989, + 452.14527612032106 + ], + "58": [ + 722.4954051282529, + 452.30323614547575 + ], + "59": [ + 722.9848450808323, + 453.0513253888024 + ], + "60": [ + 723.9151448166137, + 453.2071245964264 + ], + "61": [ + 724.7322890016229, + 453.2988379602866 + ], + "62": [ + 725.3285314601719, + 453.12642889520026 + ], + "63": [ + 725.9275321466084, + 453.0210474858106 + ], + "64": [ + 726.6755508893219, + 452.67445152048106 + ], + "65": [ + 727.6999736830772, + 452.29696014879016 + ], + "66": [ + 728.5762863540634, + 451.4052382867028 + ], + "67": [ + 729.4132878435198, + 450.7637646724363 + ], + "68": [ + 730.152138889176, + 450.12838455629264 + ], + "69": [ + 731.0221762077393, + 449.3811127516026 + ], + "70": [ + 731.8116940433129, + 448.83045666351444 + ], + "71": [ + 732.5619106845392, + 447.99842633191884 + ], + "72": [ + 733.1286558344204, + 447.1828785349978 + ], + "73": [ + 733.8286405734029, + 446.74085975523184 + ], + "74": [ + 734.5451020607171, + 445.75835970300267 + ], + "75": [ + 735.4525141872982, + 445.21365057559063 + ], + "76": [ + 736.133054735223, + 444.78241723907627 + ], + "77": [ + 736.7239249339359, + 444.35918204280426 + ], + "78": [ + 737.6046497843575, + 443.6395852268689 + ], + "79": [ + 738.4379381979555, + 443.18423668604134 + ], + "80": [ + 739.277218715672, + 442.6610789280367 + ], + "81": [ + 739.975941470702, + 441.74584178054624 + ], + "82": [ + 740.8047468952753, + 440.82320154719906 + ], + "83": [ + 741.8349411170525, + 439.8566037444804 + ], + "84": [ + 742.7695157786538, + 438.51469486435104 + ], + "85": [ + 743.7066170366435, + 437.4989589136406 + ], + "86": [ + 744.8104839611929, + 436.2801866048441 + ], + "87": [ + 746.0347745064572, + 435.0584938984965 + ], + "88": [ + 747.2028441739286, + 433.8354240728685 + ], + "89": [ + 748.4028742417985, + 432.3893139000828 + ], + "90": [ + 749.8918018438154, + 431.0897640376876 + ], + "91": [ + 751.197661364478, + 429.5710322354156 + ], + "92": [ + 752.683231169378, + 428.0243277353849 + ], + "93": [ + 754.2065918247346, + 426.78541635443855 + ], + "94": [ + 755.4659316707907, + 425.56788455498815 + ], + "95": [ + 757.0610478539309, + 424.1438429466543 + ], + "96": [ + 758.4809601691386, + 423.4799106212305 + ], + "97": [ + 759.9736566608785, + 422.6928525354665 + ], + "98": [ + 761.5540926454934, + 421.673584206389 + ], + "99": [ + 762.660388382358, + 421.0786745143708 + ], + "100": [ + 764.2304735925098, + 420.3262244155639 + ], + "101": [ + 765.7602340856918, + 419.778498169183 + ], + "102": [ + 767.1296110587597, + 419.12110368085536 + ], + "103": [ + 768.6651422177483, + 418.8094413876304 + ], + "104": [ + 770.788357650283, + 417.93711878979326 + ], + "105": [ + 771.989921736111, + 416.9849249702675 + ], + "106": [ + 773.5257551390818, + 415.96498353664884 + ], + "107": [ + 775.4886422232448, + 414.83599084641094 + ], + "108": [ + 777.269660548192, + 413.6460611332025 + ], + "109": [ + 778.3662108392956, + 412.2132679829246 + ], + "110": [ + 779.8691733598178, + 411.1174853518323 + ], + "111": [ + 781.6120161643012, + 409.52366369158455 + ], + "112": [ + 783.3935176287337, + 408.38499952797525 + ], + "113": [ + 785.1344040427056, + 407.1373252814078 + ], + "114": [ + 787.2579312435365, + 405.95812687596504 + ], + "115": [ + 789.2818919977028, + 405.0785809971002 + ], + "116": [ + 790.7343599668972, + 403.842155430863 + ], + "117": [ + 792.4274916274413, + 402.7655731704221 + ], + "118": [ + 794.2654790236896, + 401.57046321718303 + ], + "119": [ + 796.2230812062309, + 400.253622780149 + ], + "120": [ + 798.3028774507285, + 399.0186442490307 + ], + "121": [ + 800.1960158502851, + 397.31577734473944 + ], + "122": [ + 802.7977141522337, + 395.8810591801129 + ], + "123": [ + 805.2075067705938, + 394.2486364917493 + ], + "124": [ + 807.7271711962715, + 392.57196488797024 + ], + "125": [ + 809.8854777532586, + 390.59742865944946 + ], + "126": [ + 812.9027294777102, + 388.3946356118431 + ], + "127": [ + 814.8142473496994, + 386.57707710314025 + ], + "128": [ + 817.411827949075, + 384.31368288721086 + ] + } +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/tracks.jpg b/astra_scene_test/Scene_04/reference/tracks.jpg new file mode 100644 index 0000000000000000000000000000000000000000..81cd82a62b3f46225ceaeb2d3fa26e8e848b41d4 Binary files /dev/null and b/astra_scene_test/Scene_04/reference/tracks.jpg differ diff --git a/astra_scene_test/Scene_04/reference/vertical_segments.json b/astra_scene_test/Scene_04/reference/vertical_segments.json new file mode 100644 index 0000000000000000000000000000000000000000..f72df3c11ab0bd18d98a1803027429c6dc65a9ad --- /dev/null +++ b/astra_scene_test/Scene_04/reference/vertical_segments.json @@ -0,0 +1,324 @@ +[ + { + "p": [ + 1274, + 338, + 1276, + 408 + ], + "line": [ + -0.9995920864606946, + 0.02855977389887699, + 1263.8271145731046 + ], + "length": 70.02856560004639 + }, + { + "p": [ + 1020, + 77, + 1028, + 125 + ], + "line": [ + -0.9863939238321437, + 0.1643989873053573, + 993.4630802862741 + ], + "length": 48.66210024238575 + }, + { + "p": [ + 1257, + 493, + 1257, + 389 + ], + "line": [ + 1.0, + 0.0, + -1257.0 + ], + "length": 104.0 + }, + { + "p": [ + 1258, + 329, + 1272, + 390 + ], + "line": [ + -0.9746597895608373, + 0.22369241071888069, + 1152.5272121410217 + ], + "length": 62.58594091327541 + }, + { + "p": [ + 12, + 380, + 20, + 336 + ], + "line": [ + 0.9838699100999074, + 0.17888543819998318, + -79.7829054371925 + ], + "length": 44.72135954999579 + }, + { + "p": [ + 1259, + 300, + 1261, + 440 + ], + "line": [ + -0.9998979747995099, + 0.014284256782850143, + 1254.586273237728 + ], + "length": 140.01428498549708 + }, + { + "p": [ + 289, + 176, + 306, + 118 + ], + "line": [ + 0.9596285438852136, + 0.28127043527670054, + -326.83624579152604 + ], + "length": 60.4400529450463 + }, + { + "p": [ + 991, + 4, + 1002, + 38 + ], + "line": [ + -0.9514445185544115, + 0.30782028541466255, + 941.6502367457631 + ], + "length": 35.73513677041127 + }, + { + "p": [ + 4, + 227, + 4, + 270 + ], + "line": [ + -1.0, + 0.0, + 4.0 + ], + "length": 43.0 + }, + { + "p": [ + 46, + 353, + 57, + 294 + ], + "line": [ + 0.9830602989442486, + 0.1832824286167243, + -109.91947105313912 + ], + "length": 60.01666435249463 + }, + { + "p": [ + 1270, + 390, + 1270, + 323 + ], + "line": [ + 1.0, + 0.0, + -1270.0 + ], + "length": 67.0 + }, + { + "p": [ + 1258, + 26, + 1260, + 98 + ], + "line": [ + -0.9996144206527183, + 0.027767067240353285, + 1256.7929974328704 + ], + "length": 72.02777242147643 + }, + { + "p": [ + 1254, + 389, + 1254, + 424 + ], + "line": [ + -1.0, + 0.0, + 1254.0 + ], + "length": 35.0 + }, + { + "p": [ + 1271, + 372, + 1271, + 333 + ], + "line": [ + 1.0, + 0.0, + -1271.0 + ], + "length": 39.0 + }, + { + "p": [ + 1272, + 372, + 1272, + 327 + ], + "line": [ + 1.0, + 0.0, + -1272.0 + ], + "length": 45.0 + }, + { + "p": [ + 1256, + 213, + 1271, + 243 + ], + "line": [ + -0.894427190999916, + 0.447213595499958, + 1028.1440560544033 + ], + "length": 33.54101966249685 + }, + { + "p": [ + 1069, + 357, + 1076, + 401 + ], + "line": [ + -0.9875803063351138, + 0.15711504873513174, + 999.6332750737947 + ], + "length": 44.55333881989093 + }, + { + "p": [ + 1124, + 191, + 1135, + 228 + ], + "line": [ + -0.9585363562790536, + 0.2849702680829619, + 1022.9655432538106 + ], + "length": 38.600518131237564 + }, + { + "p": [ + 5, + 393, + 16, + 332 + ], + "line": [ + 0.9841270174450972, + 0.17746552773600113, + -74.66458748747394 + ], + "length": 61.98386886924695 + }, + { + "p": [ + 1249, + 341, + 1249, + 376 + ], + "line": [ + -1.0, + 0.0, + 1249.0 + ], + "length": 35.0 + }, + { + "p": [ + 869, + 178, + 891, + 216 + ], + "line": [ + -0.8654262854811262, + 0.5010362705417046, + 662.8709859266752 + ], + "length": 43.9089968002003 + }, + { + "p": [ + 1273, + 386, + 1273, + 356 + ], + "line": [ + 1.0, + 0.0, + -1273.0 + ], + "length": 30.0 + }, + { + "p": [ + 1255, + 393, + 1263, + 427 + ], + "line": [ + -0.973417168333576, + 0.2290393337255473, + 1131.626088104498 + ], + "length": 34.92849839314596 + } +] \ No newline at end of file diff --git a/astra_scene_test/Scene_04/reference/white_suv_envelope_trial.json b/astra_scene_test/Scene_04/reference/white_suv_envelope_trial.json new file mode 100644 index 0000000000000000000000000000000000000000..0bf809a783cc285218d47a8c3f592a8fdd6189ec --- /dev/null +++ b/astra_scene_test/Scene_04/reference/white_suv_envelope_trial.json @@ -0,0 +1,65 @@ +{ + "parameters": { + "x": -9.665293613337354, + "y": 22.851493165101633, + "width": 2.9912764148336106, + "length": 4.201595855386751, + "height": 2.8338549329445866, + "angle": -0.0434971844242996 + }, + "observations": { + "1": [ + 407, + 514, + 495, + 566 + ], + "33": [ + 277, + 505, + 437, + 585 + ], + "65": [ + 0, + 505, + 280, + 647 + ] + }, + "predictions": { + "1": [ + 403.89897939086484, + 512.6889301765085, + 491.01906415185704, + 563.7818742390907 + ], + "33": [ + 278.47685795367727, + 505.4191828253542, + 433.06458893759384, + 582.48861577046 + ], + "65": [ + 0.0, + 505.2499006274795, + 284.2139382265613, + 666.9284375471518 + ] + }, + "residuals": [ + -3.101020609135162, + -1.3110698234914935, + -3.9809358481429626, + -2.218125760909288, + 1.4768579536772677, + 0.4191828253541985, + -3.9354110624061605, + -2.511384229539999, + 0.0, + 0.24990062747951924, + 4.213938226561311, + 19.928437547151816, + -0.8699436884859919 + ] +} \ No newline at end of file diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/animation_curves.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/animation_curves.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e458a6e611092ad1ae4b785d90c16825296482f0 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/animation_curves.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/atmosphere.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/atmosphere.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25e3b007488b8f18425b2fafd3e0843eff7c1639 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/atmosphere.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/install_optics.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/install_optics.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..348ce2795b282e11b8ef6207994dd2d3b916ef73 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/install_optics.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/install_signal_response.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/install_signal_response.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..917e592ca026daba7278f0bfafb089735f894ec6 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/install_signal_response.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/overscan.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/overscan.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8571030d98834a0169ececa069f9f1c2254fac7 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/overscan.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/photographic_materials.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/photographic_materials.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fda4660c91e55d81fd8911684c3923b98579f21 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/photographic_materials.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/render_device.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/render_device.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..870391db9dfd7c2b48801fba5924eb68045dad45 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/render_device.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/render_settings.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/render_settings.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d93f7690c50e7a347da6b5d4a203f1b3bd936a98 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/render_settings.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/__pycache__/road_surface.cpython-313.pyc b/astra_scene_test/Scene_04/scripts/__pycache__/road_surface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fac62637671587214546cd9d8375005e2980fc2 Binary files /dev/null and b/astra_scene_test/Scene_04/scripts/__pycache__/road_surface.cpython-313.pyc differ diff --git a/astra_scene_test/Scene_04/scripts/animation_curves.py b/astra_scene_test/Scene_04/scripts/animation_curves.py new file mode 100644 index 0000000000000000000000000000000000000000..37e717ea35be6cffb072564eac4b5ac4f5c10819 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/animation_curves.py @@ -0,0 +1,20 @@ +"""Inspect legacy and layered Blender action curves without silently skipping them.""" +def curves(owner): + animation=getattr(owner,'animation_data',None) + action=getattr(animation,'action',None) + if not action:return [] + if hasattr(action,'fcurves'):return list(action.fcurves) + found=[] + for layer in action.layers: + for strip in layer.strips: + for bag in getattr(strip,'channelbags',[]):found.extend(bag.fcurves) + return found + +def linear(owner): + found=curves(owner) + if getattr(getattr(owner,'animation_data',None),'action',None) and not found: + raise RuntimeError('Animated action has no inspectable curves: '+owner.name) + for curve in found: + curve.extrapolation='LINEAR' + for key in curve.keyframe_points:key.interpolation='LINEAR' + return len(found) diff --git a/astra_scene_test/Scene_04/scripts/atmosphere.py b/astra_scene_test/Scene_04/scripts/atmosphere.py new file mode 100644 index 0000000000000000000000000000000000000000..c86ac689513cf76839134b1700aaf27adc294f9a --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/atmosphere.py @@ -0,0 +1,20 @@ +"""Stable homogeneous height layers for low-lying nighttime humidity.""" +import bpy +def install_atmosphere(parameters): + group=bpy.data.collections.get('Atmosphere') + if group is None: + group=bpy.data.collections.new('Atmosphere');bpy.context.scene.collection.children.link(group) + for ob in list(group.objects):bpy.data.objects.remove(ob,do_unlink=True) + layers=parameters.get('haze_layers',[[-20,25,1.0],[25,60,.4],[60,110,.10]]) + for index,(bottom,top,factor) in enumerate(layers): + m=bpy.data.materials.new('Humid air height layer '+str(index));m.use_nodes=True;n=m.node_tree.nodes;n.clear() + out=n.new('ShaderNodeOutputMaterial');v=n.new('ShaderNodeVolumePrincipled') + v.inputs['Density'].default_value=parameters['haze']*factor + v.inputs['Color'].default_value=(*parameters.get('haze_color',[.53,.59,.58]),1) + v.inputs['Anisotropy'].default_value=.28;m.node_tree.links.new(v.outputs['Volume'],out.inputs['Volume']) + bpy.ops.mesh.primitive_cube_add(size=1,location=(0,360,(bottom+top)/2)) + ob=bpy.context.object;ob.name='Stable atmospheric layer '+str(index);ob.dimensions=(440,900,top-bottom) + bpy.ops.object.transform_apply(location=False,rotation=False,scale=True) + for c in list(ob.users_collection):c.objects.unlink(ob) + group.objects.link(ob);ob.data.materials.append(m) + ob['density_profile']='Fixed in world space; no animated random density' diff --git a/astra_scene_test/Scene_04/scripts/audit_blend.py b/astra_scene_test/Scene_04/scripts/audit_blend.py new file mode 100644 index 0000000000000000000000000000000000000000..bbbae48fd7742bd37e77fd2d68f455c193741922 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/audit_blend.py @@ -0,0 +1,49 @@ +"""Read-only scene audit performed after opening the on-disk .blend.""" +import bpy,pathlib,json,hashlib,datetime,sys,struct +ROOT=pathlib.Path(__file__).resolve().parents[1];S=bpy.context.scene +sys.path.insert(0,str(ROOT/'scripts')) +from animation_curves import curves +from road_surface import ground_height +room_lights=[{'name':o.name,'position':list(o.matrix_world.translation),'height_above_road':o.matrix_world.translation.z-ground_height(o.matrix_world.translation.y,o.matrix_world.translation.x)} for o in S.objects if o.type=='LIGHT' and 'room ceiling' in o.name] +camera_curves=[{'path':fc.data_path,'index':fc.array_index,'keyframes':len(fc.keyframe_points),'interpolations':sorted({k.interpolation for k in fc.keyframe_points})} for fc in curves(S.camera)] +source_images=[im.filepath for im in bpy.data.images if im.source in ['MOVIE','SEQUENCE']] +reference_hashes={f['sha256'] for f in json.loads((ROOT/'reference/metadata.json').read_text())['frames']} +images=[] +for im in bpy.data.images: + digest=None;data=None + if im.packed_file:data=im.packed_file.data;digest=hashlib.sha256(data).hexdigest() + elif im.source=='FILE' and im.filepath: + path=pathlib.Path(bpy.path.abspath(im.filepath)) + if path.is_file():data=path.read_bytes();digest=hashlib.sha256(data).hexdigest() + png={'width':struct.unpack('>I',data[16:20])[0],'height':struct.unpack('>I',data[20:24])[0],'bit_depth':data[24]} if data and data[:8]==b'\x89PNG\r\n\x1a\n' else None + images.append({'name':im.name,'source':im.source,'filepath':im.filepath,'packed':bool(im.packed_file),'sha256':digest,'png_header':png}) +fonts=[{'name':font.name,'filepath':font.filepath,'packed':bool(getattr(font,'packed_file',None))} for font in bpy.data.fonts] +camera=[] +for fr in [1,17,33,49,65,81,97,113,128]: + S.frame_set(fr);camera.append({'frame':fr,'location':list(S.camera.location),'rotation':list(S.camera.rotation_euler)}) +scene_path=pathlib.Path(bpy.data.filepath) +crop=S.compositing_node_group.nodes.get('SOURCE_FRAME_CROP') if S.compositing_node_group else None +crop_data=None +if crop: + output=next(n for n in S.compositing_node_group.nodes if n.bl_idname=='NodeGroupOutput') + crop_data={'x':crop.inputs['X'].default_value,'y':crop.inputs['Y'].default_value, + 'width':crop.inputs['Width'].default_value,'height':crop.inputs['Height'].default_value, + 'alpha_crop':crop.inputs['Alpha Crop'].default_value, + 'connected_to_output':bool(output.inputs['Image'].links and output.inputs['Image'].links[0].from_node==crop)} +output_resolution=[crop_data['width'],crop_data['height']] if crop_data else [S.render.resolution_x,S.render.resolution_y] +native=S.compositing_node_group.nodes.get('SOURCE_FRAME_OUTPUT') if S.compositing_node_group else None +native_data={'format':native.format.file_format,'directory':native.directory,'items':[i.name for i in native.file_output_items], + 'connected_to_crop':any(link.from_node==crop for socket in native.inputs for link in socket.links)} if native else None +data={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'blend_sha256':hashlib.sha256(scene_path.read_bytes()).hexdigest(), + 'filepath':str(scene_path),'frame_start':S.frame_start,'frame_end':S.frame_end,'fps':S.render.fps,'fps_base':S.render.fps_base, + 'resolution':[S.render.resolution_x,S.render.resolution_y], 'percentage':S.render.resolution_percentage, + 'output_resolution':output_resolution,'optical_crop':crop_data,'optical_overscan':S.get('optical_overscan',1), + 'native_file_output':native_data,'automatic_output_enabled':S.render.save_output, + 'camera_curves':camera_curves,'camera_samples':camera,'camera_animated':bool(S.camera.animation_data),'camera_lens_mm':S.camera.data.lens,'room_lights':room_lights, + 'mesh_object_count':sum(o.type=='MESH' for o in S.objects),'curve_object_count':sum(o.type=='CURVE' for o in S.objects), + 'material_count':len(bpy.data.materials),'collections':[c.name for c in bpy.data.collections], + 'movie_sequence_images':source_images,'reference_image_matches':[im['name'] for im in images if im['sha256'] in reference_hashes],'samples':S.cycles.samples,'adaptive_min_samples':S.cycles.adaptive_min_samples,'adaptive_threshold':S.cycles.adaptive_threshold,'images':images,'objects':len(S.objects), + 'fonts':fonts,'missing_external_fonts':[font['filepath'] for font in fonts if font['filepath'] and font['filepath']!='' and not font['packed'] and not pathlib.Path(bpy.path.abspath(font['filepath'])).exists()], + 'missing_external_images':[im.filepath for im in bpy.data.images if im.filepath and im.source=='FILE' and not im.packed_file and not pathlib.Path(bpy.path.abspath(im.filepath)).exists()]} +(ROOT/'logs/reopened_scene_audit.json').write_text(json.dumps(data,indent=2)) +print('AUDIT_COMPLETE',data['objects'],data['blend_sha256']) diff --git a/astra_scene_test/Scene_04/scripts/audit_packed_optics.py b/astra_scene_test/Scene_04/scripts/audit_packed_optics.py new file mode 100644 index 0000000000000000000000000000000000000000..2b77e01d871117a679a372b5fbd01082dd1da82c --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/audit_packed_optics.py @@ -0,0 +1,16 @@ +"""Inspect and retain packed numeric optical images from a saved checkpoint.""" +import bpy,hashlib,json,pathlib,struct +ROOT=pathlib.Path(__file__).resolve().parents[1] +folder=ROOT/'renders/optical_baseline_assets' +folder.mkdir(parents=True,exist_ok=True) +rows=[] +for im in bpy.data.images: + if not pathlib.Path(im.filepath).name.startswith(('windshield_','wet_glass_','sensor_')):continue + name=pathlib.Path(im.filepath).name + data=im.packed_file.data if im.packed_file else pathlib.Path(bpy.path.abspath(im.filepath)).read_bytes() + target=folder/name;target.write_bytes(data) + row={'name':name,'image_size':list(im.size),'colorspace':im.colorspace_settings.name,'sha256':hashlib.sha256(data).hexdigest()} + if data[:8]==b'\x89PNG\r\n\x1a\n':row['png_header']={'size':list(struct.unpack('>II',data[16:24])),'bit_depth':data[24],'color_type':data[25]} + rows.append(row) +(ROOT/'logs/packed_optical_baseline.json').write_text(json.dumps(rows,indent=2)) +print('PACKED_OPTICAL_AUDIT_COMPLETE',json.dumps(rows),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/audit_projection.py b/astra_scene_test/Scene_04/scripts/audit_projection.py new file mode 100644 index 0000000000000000000000000000000000000000..1af315b6daebdfec576943a76ca2f35f2a845ffe --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/audit_projection.py @@ -0,0 +1,32 @@ +"""Check projections from the actual saved Blender camera, not the fit code.""" +import bpy,json,pathlib,math +from mathutils import Vector +from bpy_extras.object_utils import world_to_camera_view +ROOT=pathlib.Path(__file__).resolve().parents[1];S=bpy.context.scene +margin=list(S.get('optical_margin_px',[0,0])) +landmarks=json.loads((ROOT/'assets/landmarks.json').read_text());rows={} +for name,data in landmarks.items(): + errors=[];obs=[] + for frame,uv in data['observations'].items(): + S.frame_set(int(frame));q=world_to_camera_view(S,S.camera,Vector(data['xyz'])) + px=[q.x*S.render.resolution_x-margin[0],(1-q.y)*S.render.resolution_y-margin[1]] + err=math.hypot(px[0]-uv[0],px[1]-uv[1]);errors.append(err) + obs.append({'frame':int(frame),'render_projection':px,'source':uv,'error_pixels':err}) + rows[name]={'mean_error_pixels':sum(errors)/len(errors),'max_error_pixels':max(errors),'frames':obs} +emitters=json.loads((ROOT/'assets/signal_emitters.json').read_text());actual={} +for name,data in emitters.items(): + expected=Vector(data['emitter_xyz']) + candidates=[ob for ob in S.objects if ob.name.startswith('Green signal lens')] + obj=min(candidates,key=lambda ob:(ob.matrix_world.translation-expected).length) + observations=[] + for frame,uv in data['observations'].items(): + S.frame_set(int(frame));q=world_to_camera_view(S,S.camera,obj.matrix_world.translation) + px=[q.x*S.render.resolution_x-margin[0],(1-q.y)*S.render.resolution_y-margin[1]] + observations.append({'frame':int(frame),'projection':px,'source':uv,'error_pixels':math.dist(px,uv)}) + actual[name]={'object_name':obj.name,'actual_xyz':list(obj.matrix_world.translation),'expected_xyz':list(expected), + 'mean_error_pixels':sum(r['error_pixels'] for r in observations)/len(observations),'frames':observations} +result={'camera_name':S.camera.name,'lens_mm':S.camera.data.lens,'shift_y':S.camera.data.shift_y,'optical_margin_px':margin, + 'note':'Original selected anchors are abstract fit points. Actual signal emitters below project real saved mesh objects; optical-image centroid checks are separate.', + 'anchors':rows,'actual_signal_emitters':actual} +(ROOT/'logs/blender_projection_audit.json').write_text(json.dumps(result,indent=2)) +for n,r in rows.items():print(n,'mean px',round(r['mean_error_pixels'],3),'max px',round(r['max_error_pixels'],3),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/benchmark_metalrt.py b/astra_scene_test/Scene_04/scripts/benchmark_metalrt.py new file mode 100644 index 0000000000000000000000000000000000000000..e9c5415ca60ce08f45127691234b93ec2f04d080 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/benchmark_metalrt.py @@ -0,0 +1,19 @@ +"""Test hardware traversal under the reduced memory budget; does not save the scene.""" +import bpy,pathlib,sys,json,time,os +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from render_settings import apply_quality +from render_device import configure +args=sys.argv[sys.argv.index('--')+1:] if '--' in sys.argv else [] +percent=int(args[0]) if args else 50;samples=int(args[1]) if len(args)>1 else 64 +tile=int(args[2]) if len(args)>2 else 128;frames=[int(f) for f in args[3].split(',')] if len(args)>3 else [1];mode=args[4] if len(args)>4 else 'ON' +S=bpy.context.scene;apply_quality(S,samples,percent);configure(S);S.cycles.tile_size=tile +prefs=bpy.context.preferences.addons['cycles'].preferences;prefs.metalrt=mode;S.cycles.device='GPU' +S.render.image_settings.file_format='PNG';out=ROOT/'renders/metalrt_benchmark';out.mkdir(parents=True,exist_ok=True) +rows=[] +for fr in frames: + S.frame_set(fr);S.render.filepath=str(out/f'frame{fr}_{percent}_{samples}_{mode}_tile{tile}.png');start=time.monotonic() + print('METALRT_BENCHMARK_START',fr,percent,samples,tile,mode,os.environ.get('CYCLES_METAL_WORKING_SET_PERCENT'),flush=True) + bpy.ops.render.render(write_still=True) + row={'frame':fr,'seconds':time.monotonic()-start,'samples':samples,'percent':percent,'tile':tile,'metalrt':prefs.metalrt,'working_set':os.environ.get('CYCLES_METAL_WORKING_SET_PERCENT')};rows.append(row) + (ROOT/f'logs/metalrt_benchmark_{percent}_{samples}_{mode}_tile{tile}.json').write_text(json.dumps(rows,indent=2));print('METALRT_BENCHMARK_FRAME',row,flush=True) +print('METALRT_BENCHMARK_COMPLETE',len(rows),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/build_scene.py b/astra_scene_test/Scene_04/scripts/build_scene.py new file mode 100644 index 0000000000000000000000000000000000000000..6fb87649c8f4bd03d88a6997a37c82728be0a68d --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/build_scene.py @@ -0,0 +1,1081 @@ +"""Scene 04: deterministic, editable reconstruction. Run using Blender --background. + +All visible world assets are generated as mesh/curve geometry with editable +procedural and CC0 photographed surface materials. Reference frames are used only by the external comparison scripts. +""" +import bpy, bmesh, math, random, json, pathlib, sys, time +from mathutils import Vector, Matrix, Euler, Quaternion +import bisect +from math import sin, cos, pi +ROOT=pathlib.Path(__file__).resolve().parents[1] +random.seed(404) +ARGS=sys.argv[sys.argv.index('--')+1:] if '--' in sys.argv else [] +VARIANT=ARGS[0] if ARGS else 'layout' +PARAMS={ + 'fov':120.0, 'pitch':24.8, 'yaw':4.0, 'camera_height':1.55, + 'travel':27.8, 'road_halfwidth':10.5, 'sidewalk_width':3.6, + 'intersection':36.5, 'intersection_width':12.5, + 'exposure':0.5, 'world_strength':0.28, 'haze':0.0018, + 'samples':96, 'resolution_percentage':60, +} +config=ROOT/'assets/config.json' +if config.exists(): PARAMS.update(json.loads(config.read_text())) +else: config.write_text(json.dumps(PARAMS,indent=2)) +P=PARAMS +bpy.ops.object.select_all(action='SELECT');bpy.ops.object.delete(use_global=False) +for d in list(bpy.data.materials):bpy.data.materials.remove(d) +S=bpy.context.scene +S.render.engine='CYCLES';S.cycles.samples=P['samples'];S.cycles.use_denoising=True +S.cycles.adaptive_threshold=.008 if P['samples']>=512 else .012 if P['samples']>=256 else .035 +S.cycles.adaptive_min_samples=128 if P['samples']>=512 else 64 if P['samples']>=256 else 16 +S.cycles.tile_size=256;S.cycles.debug_use_compact_bvh=True;S.cycles.denoising_use_gpu=False +S.render.use_persistent_data=False +if hasattr(S.render,'compositor_device'):S.render.compositor_device='CPU' +sys.path.insert(0,str(ROOT/'scripts')) +from render_device import configure +print('RENDER_DEVICE',configure(S,'cpu' in ARGS),flush=True) +from render_settings import apply_quality +apply_quality(S,P['samples'],P['resolution_percentage']) +S.cycles.max_bounces=8;S.cycles.transmission_bounces=6;S.cycles.transparent_max_bounces=12 +S.cycles.volume_bounces=1;S.cycles.diffuse_bounces=3;S.cycles.glossy_bounces=4 +S.cycles.sample_clamp_indirect=5 +S.render.resolution_x=1280;S.render.resolution_y=720;S.render.resolution_percentage=P['resolution_percentage'] +S.render.fps=30;S.render.fps_base=1;S.frame_start=1;S.frame_end=128 +S.render.image_settings.file_format='PNG';S.render.image_settings.color_mode='RGB';S.render.image_settings.color_depth='8' +S.render.film_transparent=False;S.render.use_motion_blur=True;S.render.motion_blur_shutter=.45 +S.view_settings.view_transform=P.get('view_transform','AgX');S.view_settings.look=P.get('look','AgX - Medium High Contrast');S.view_settings.gamma=P.get('gamma',1.0) +S.view_settings.exposure=P['exposure'] +S.world.use_nodes=True;world=S.world.node_tree +world.nodes['Background'].inputs['Color'].default_value=(*P.get('world_color',[.24,.27,.24]),1) +world.nodes['Background'].inputs['Strength'].default_value=P['world_strength'] +if P.get('sky_response'): + original=world.nodes['Background'];camera_sky=world.nodes.new('ShaderNodeBackground');camera_sky.label='Gradual calibrated camera response for the visible overcast sky' + camera_sky.inputs['Strength'].default_value=P['world_strength'] + for fr,rgb in P['sky_response'].items(): + camera_sky.inputs['Color'].default_value=(*[v*g for v,g in zip(P['world_color'],rgb)],1) + camera_sky.inputs['Color'].keyframe_insert('default_value',frame=int(fr)) + ray=world.nodes.new('ShaderNodeLightPath');mixsky=world.nodes.new('ShaderNodeMixShader') + world.links.new(ray.outputs['Is Camera Ray'],mixsky.inputs[0]);world.links.new(original.outputs[0],mixsky.inputs[1]);world.links.new(camera_sky.outputs[0],mixsky.inputs[2]) + world.links.new(mixsky.outputs[0],world.nodes['World Output'].inputs['Surface']) + +collections={} +def coll(name): + if name not in collections: + c=bpy.data.collections.new(name);S.collection.children.link(c);collections[name]=c + return collections[name] +def objlink(o,group): + for c in list(o.users_collection):c.objects.unlink(o) + coll(group).objects.link(o) + return o +def material(name,color,rough=.5,metal=0,emission=0): + m=bpy.data.materials.new(name);m.diffuse_color=(*color,1);m.use_nodes=True + p=m.node_tree.nodes.get('Principled BSDF');p.inputs['Base Color'].default_value=(*color,1) + p.inputs['Roughness'].default_value=rough;p.inputs['Metallic'].default_value=metal + if emission: + p.inputs['Emission Color'].default_value=(*color,1);p.inputs['Emission Strength'].default_value=emission + return m +def noise_surface(m,scale,strength,distance,roughrange=None): + n=m.node_tree.nodes;l=m.node_tree.links;p=n.get('Principled BSDF') + t=n.new('ShaderNodeTexNoise');t.inputs['Scale'].default_value=scale;t.inputs['Detail'].default_value=3 + co=n.new('ShaderNodeTexCoord');l.new(co.outputs['Object'],t.inputs['Vector']) + bump=n.new('ShaderNodeBump');bump.inputs['Strength'].default_value=strength;bump.inputs['Distance'].default_value=distance + l.new(t.outputs['Fac'],bump.inputs['Height']);l.new(bump.outputs['Normal'],p.inputs['Normal']) + if roughrange: + r=n.new('ShaderNodeMapRange');r.inputs['From Min'].default_value=.25;r.inputs['From Max'].default_value=.75 + r.inputs['To Min'].default_value=roughrange[0];r.inputs['To Max'].default_value=roughrange[1] + l.new(t.outputs['Fac'],r.inputs['Value']);l.new(r.outputs[0],p.inputs['Roughness']) + return t +M={} +for name,c,r,mt in [ + ('asphalt',(.044,.054,.065),.48,.02),('concrete',(.19,.20,.18),.73,0), + ('curb',(.18,.19,.175),.60,0),('stone',(.29,.27,.21),.72,0), + ('darkbrick',(.10,.064,.043),.67,0),('redbrick',(.23,.092,.05),.71,0), + ('tanbrick',(.25,.23,.16),.70,0),('mortaredstone',(.34,.32,.24),.65,0), + ('metal',(.058,.067,.060),.33,.72),('scaffold',(.39,.43,.44),.24,.8), + ('trim',(.19,.18,.14),.52,.1),('black',(.009,.012,.012),.43,0), + ('glass',(.028,.048,.044),.18,.58),('orange',(.42,.13,.027),.59,0), + ('barrier_white',(.54,.53,.45),.5,0),('roadpaint',(.34,.36,.31),.55,0), + ('signred',(.37,.025,.019),.45,0),('greenmetal',(.029,.070,.052),.45,.35), + ('tire',(.009,.011,.011),.76,0),('chrome',(.47,.50,.50),.2,.9), + ('bark',(.066,.048,.027),.9,0),('leaf',(.031,.090,.029),.73,0), + ('leaflight',(.069,.12,.043),.8,0),('leaffaded',(.1,.13,.05),.8,0), + ('hood',(.14,.16,.15),.27,.55),('rust',(.16,.061,.026),.76,.3), + ('bluebin',(.028,.12,.26),.49,0)]:M[name]=material(name,c,r,mt) +M['shopglass']=material('Actual storefront glazing',(.82,.88,.86),.10,0) +M['shopglass'].node_tree.nodes['Principled BSDF'].inputs['Transmission Weight'].default_value=.92 +M['shopglass'].node_tree.nodes['Principled BSDF'].inputs['IOR'].default_value=1.46 +M['carglass']=material('Tinted automotive glazing',(.065,.095,.072),.085,.15) +M['carglass'].node_tree.nodes['Principled BSDF'].inputs['Transmission Weight'].default_value=.45 +M['carglass'].node_tree.nodes['Principled BSDF'].inputs['IOR'].default_value=1.48 +for name,color in [('leaf',(.043,.076,.009)),('leaflight',(.067,.110,.014)),('leaffaded',(.082,.100,.020))]: + M[name].node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value=(*color,1) +for n in ['concrete','curb','stone','trim','bark']:noise_surface(M[n],6,.25,.03) +paint=M['roadpaint'];pn=paint.node_tree.nodes;pl=paint.node_tree.links;ppaint=pn.get('Principled BSDF') +pc=pn.new('ShaderNodeTexCoord');pw=pn.new('ShaderNodeTexNoise');pw.inputs['Scale'].default_value=24;pw.inputs['Detail'].default_value=3;pl.new(pc.outputs['Object'],pw.inputs['Vector']) +pr=pn.new('ShaderNodeValToRGB');pr.color_ramp.elements[0].position=.33;pr.color_ramp.elements[0].color=(.07,.085,.08,1) +pr.color_ramp.elements[1].position=.58;pr.color_ramp.elements[1].color=(.37,.39,.34,1) +pl.new(pw.outputs['Fac'],pr.inputs[0]);pl.new(pr.outputs[0],ppaint.inputs['Base Color']) +noise_surface(paint,100,.18,.002,(.42,.65)) + +noise_surface(M['asphalt'],145,.20,.003,(.22,.58)) +for name in ['redbrick','darkbrick','tanbrick']: + m=M[name];n=m.node_tree.nodes;l=m.node_tree.links;p=n.get('Principled BSDF') + tex=n.new('ShaderNodeTexBrick');tex.inputs['Color1'].default_value=m.diffuse_color + tex.inputs['Color2'].default_value=tuple(v*.62 for v in m.diffuse_color[:3])+(1,) + tex.inputs['Mortar'].default_value=(.12,.12,.10,1);tex.inputs['Scale'].default_value=1 + tex.inputs['Mortar Size'].default_value=.012;tex.inputs['Brick Width'].default_value=.28;tex.inputs['Row Height'].default_value=.11 + co=n.new('ShaderNodeTexCoord');sep=n.new('ShaderNodeSeparateXYZ');comb=n.new('ShaderNodeCombineXYZ') + l.new(co.outputs['Object'],sep.inputs[0]);l.new(sep.outputs['Y'],comb.inputs['X']);l.new(sep.outputs['Z'],comb.inputs['Y']) + l.new(comb.outputs[0],tex.inputs['Vector']);l.new(tex.outputs['Color'],p.inputs['Base Color']) + b=n.new('ShaderNodeBump');b.inputs['Strength'].default_value=.32;b.inputs['Distance'].default_value=.018 + l.new(tex.outputs['Fac'],b.inputs['Height']);l.new(b.outputs[0],p.inputs['Normal']) +for name,c,power in [('warmwindow',(.66,.40,.16),1.2),('dimwindow',(.22,.24,.16),.08), + ('coolwindow',(.42,.57,.58),1.5),('shopwhite',(.65,.77,.8),3),('shopwarm',(.8,.61,.34),1.8), + ('lamp',(.86,.80,.63),800),('headlight',(.30,.60,1),1100),('tail',(.12,.003,.001),0), + ('signalgreen',(.025,.75,.55),65),('signwhite',(.66,.75,.72),5.0),('signgold',(.60,.65,.16),5.5), + ('redneon',(.60,.015,.02),5)]:M[name]=material(name,c,.32,0,power) +M['escapeiron']=material('Weathered black fire escape iron',(.018,.022,.019),.62,.48) +M['vehiclebadge']=material('Satin gold vehicle grille badge',(.34,.25,.105),.3,.65) +M['silverwheel']=material('Brushed silver wheel backing',(.32,.34,.33),.35,.8) +M['lamp_distant']=material('Dim distant streetlamp lens',(.86,.80,.63),.32,0,70) +M['lamp_secondary']=material('Secondary paired lamp visible lens',(.86,.80,.63),.32,0,180) +M['measuredwindow']=material('Measured warm residential window',(.75,.66,.40),.35,0,2.2) + +sys.path.insert(0,str(ROOT/'scripts')) +from photographic_materials import install_material_maps,add_surface_uv +install_material_maps(M) + +# Mesh batches retain separate named architectural systems and material slots. +class Batch: + def __init__(self,name,group):self.name=name;self.group=group;self.v=[];self.f=[];self.mi=[];self.mats=[] + def face(self,pts,mat): + base=len(self.v);self.v.extend(pts);self.f.append(tuple(range(base,base+len(pts)))) + if mat not in self.mats:self.mats.append(mat) + self.mi.append(self.mats.index(mat)) + def box(self,center,size,mat,rot=0): + x,y,z=center;a,b,c=[s/2 for s in size] + pts=[] + for u,v,w in [(-a,-b,-c),(a,-b,-c),(a,b,-c),(-a,b,-c),(-a,-b,c),(a,-b,c),(a,b,c),(-a,b,c)]: + pts.append((x+u*cos(rot)-v*sin(rot),y+u*sin(rot)+v*cos(rot),z+w)) + for face in [(0,3,2,1),(4,5,6,7),(0,1,5,4),(1,2,6,5),(2,3,7,6),(3,0,4,7)]:self.face([pts[i] for i in face],mat) + def finish(self): + mesh=bpy.data.meshes.new(self.name);mesh.from_pydata(self.v,[],self.f);mesh.update() + if any(m.name in {'asphalt','redbrick','darkbrick','tanbrick'} for m in self.mats):add_surface_uv(mesh) + o=bpy.data.objects.new(self.name,mesh);coll(self.group).objects.link(o) + for m in self.mats:mesh.materials.append(m) + for p,idx in zip(mesh.polygons,self.mi):p.material_index=idx + return o +def box(name,loc,size,mat,group='Street',bevel=0): + b=Batch(name,group);b.box(loc,size,mat);o=b.finish() + if bevel: + mod=o.modifiers.new('Small manufactured edge bevel','BEVEL');mod.width=bevel;mod.segments=2 + o.modifiers.new('Corner normals','WEIGHTED_NORMAL') + return o +def path(name,coords,radius,mat,group='Street',resolution=2): + c=bpy.data.curves.new(name,'CURVE');c.dimensions='3D';c.resolution_u=resolution;c.bevel_depth=radius;c.bevel_resolution=2 + s=c.splines.new('POLY');s.points.add(len(coords)-1) + for p,co in zip(s.points,coords):p.co=(*co,1) + o=bpy.data.objects.new(name,c);coll(group).objects.link(o);c.materials.append(mat);return o +primitive_cache={} +def cylinder(name,a,b,r,mat,group='Street',vertices=12): + a,b=Vector(a),Vector(b);d=b-a;key=('cylinder',vertices,mat.name) + if key not in primitive_cache: + verts=[(cos(j*2*pi/vertices),sin(j*2*pi/vertices),z) for z in [-.5,.5] for j in range(vertices)] + faces=[tuple(reversed(range(vertices))),tuple(range(vertices,2*vertices))] + faces += [(j,(j+1)%vertices,(j+1)%vertices+vertices,j+vertices) for j in range(vertices)] + mesh=bpy.data.meshes.new('Shared '+mat.name+' cylinder');mesh.from_pydata(verts,[],faces);mesh.materials.append(mat) + primitive_cache[key]=mesh + o=bpy.data.objects.new(name,primitive_cache[key]);coll(group).objects.link(o) + o.location=(a+b)/2;o.scale=(r,r,d.length);o.rotation_euler=d.to_track_quat('Z','Y').to_euler() + return o +def sphere(name,loc,scale,mat,group='Street',segments=16,rings=8): + key=('sphere',segments,rings,mat.name) + if key not in primitive_cache: + verts=[(sin(pi*i/rings)*cos(j*2*pi/segments),sin(pi*i/rings)*sin(j*2*pi/segments),cos(pi*i/rings)) for i in range(rings+1) for j in range(segments)] + faces=[(i*segments+j,i*segments+(j+1)%segments,(i+1)*segments+(j+1)%segments,(i+1)*segments+j) for i in range(rings) for j in range(segments)] + mesh=bpy.data.meshes.new('Shared '+mat.name+' sphere');mesh.from_pydata(verts,[],faces);mesh.materials.append(mat) + for p in mesh.polygons:p.use_smooth=True + primitive_cache[key]=mesh + o=bpy.data.objects.new(name,primitive_cache[key]);coll(group).objects.link(o);o.location=loc;o.scale=scale + return o +def text_obj(name,text,loc,size,mat,side=0,group='Signs'): + c=bpy.data.curves.new(name,'FONT');c.body=text;c.size=size;c.align_x='CENTER';c.align_y='CENTER';c.extrude=.001 + o=bpy.data.objects.new(name,c);coll(group).objects.link(o);o.location=loc + # text normal faces road: local x follows street, local y is vertical + o.rotation_euler=(pi/2,0,-side*pi/2);c.materials.append(mat);return o +def wheel_torus(name,loc,major,minor,mat,group='Vehicles'): + key=('wheel_torus',major,minor,mat.name) + if key not in primitive_cache: + vertices=[];faces=[];n=32;m=10 + for j in range(n): + a=j*2*pi/n + for k in range(m): + b=k*2*pi/m;rr=major+minor*cos(b) + vertices.append((minor*sin(b),rr*cos(a),rr*sin(a))) + for j in range(n): + for k in range(m):faces.append((j*m+k,((j+1)%n)*m+k,((j+1)%n)*m+(k+1)%m,j*m+(k+1)%m)) + mesh=bpy.data.meshes.new('Shared rounded wheel section');mesh.from_pydata(vertices,[],faces);mesh.materials.append(mat) + for p in mesh.polygons:p.use_smooth=True + primitive_cache[key]=mesh + o=bpy.data.objects.new(name,primitive_cache[key]);coll(group).objects.link(o);o.location=loc;return o +def light(name,loc,color,power,kind='AREA',size=1,target=None): + d=bpy.data.lights.new(name,kind);d.energy=power;d.color=color + if kind=='AREA':d.shape='DISK';d.size=size + elif kind=='POINT':d.shadow_soft_size=size + elif kind=='SPOT':d.spot_size=math.radians(68);d.spot_blend=.65;d.shadow_soft_size=size + o=bpy.data.objects.new(name,d);coll('Lighting').objects.link(o);o.location=loc + if target is not None:o.rotation_euler=(Vector(target)-o.location).to_track_quat('-Z','Y').to_euler() + return o +def np_linspace(a,b,n):return [a+(b-a)*i/(n-1) for i in range(n)] + +# The visible street falls away beyond the intersection. The near camera path +# remains almost level; the grade explains the lower distant headlight horizon. +from road_surface import ground_height +# Shared grid vertices allow continuous terrain normals. A single wide quad +# per station produced visible reflection bands over the crossfall transition. +road_mesh=bpy.data.meshes.new('Shared continuous road grid') +rx=list(range(-70,71,2));ry=list(range(-75,1002,2));nx=len(rx) +road_mesh.from_pydata([(x,y,-.008) for y in ry for x in rx],[], + [(j*nx+i,j*nx+i+1,(j+1)*nx+i+1,(j+1)*nx+i) for j in range(len(ry)-1) for i in range(nx-1)]) +road_mesh.materials.append(M['asphalt']);road_mesh.update();add_surface_uv(road_mesh) +for poly in road_mesh.polygons:poly.use_smooth=True +road=bpy.data.objects.new('Continuous graded wet asphalt road',road_mesh);coll('Street').objects.link(road) +n=M['asphalt'].node_tree.nodes;l=M['asphalt'].node_tree.links;p=n.get('Principled BSDF') +co=n.new('ShaderNodeTexCoord');mapping=n.new('ShaderNodeVectorMath');mapping.operation='MULTIPLY';mapping.inputs[1].default_value=(.48,.28,1) +l.new(co.outputs['Object'],mapping.inputs[0]);wet=n.new('ShaderNodeTexNoise');wet.inputs['Scale'].default_value=1.6;wet.inputs['Detail'].default_value=5 +l.new(mapping.outputs[0],wet.inputs[0]);ramp=n.new('ShaderNodeValToRGB');ramp.color_ramp.elements[0].position=.3 +road_roughness=P.get('road_roughness',[.25,.58]) +ramp.color_ramp.elements[0].color=(*([road_roughness[0]]*3),1) +ramp.color_ramp.elements[1].position=.70;ramp.color_ramp.elements[1].color=(*([road_roughness[1]]*3),1) +l.new(wet.outputs['Fac'],ramp.inputs[0]);l.new(ramp.outputs[0],p.inputs['Roughness']);p.inputs['Coat Weight'].default_value=.035;p.inputs['Coat Roughness'].default_value=.28 +# Fine longitudinal water rills sit above photographed aggregate; fixed world +# coordinates keep the same patches attached to the moving road surface. +rillmap=n.new('ShaderNodeVectorMath');rillmap.operation='MULTIPLY';rillmap.inputs[1].default_value=(18,.50,2) +l.new(co.outputs['Object'],rillmap.inputs[0]);rill=n.new('ShaderNodeTexNoise');rill.inputs['Scale'].default_value=1;rill.inputs['Detail'].default_value=3 +l.new(rillmap.outputs[0],rill.inputs['Vector']) +under=p.inputs['Normal'].links[0].from_socket +rillbump=n.new('ShaderNodeBump');rillbump.inputs['Strength'].default_value=.32;rillbump.inputs['Distance'].default_value=.008 +l.new(rill.outputs['Fac'],rillbump.inputs['Height']);l.new(under,rillbump.inputs['Normal']);l.new(rillbump.outputs['Normal'],p.inputs['Normal']) +aggregate=p.inputs['Base Color'].links[0].from_socket +rillcolor=n.new('ShaderNodeMapRange');rillcolor.inputs['From Min'].default_value=.25;rillcolor.inputs['From Max'].default_value=.75 +rillcolor.inputs['To Min'].default_value=.68;rillcolor.inputs['To Max'].default_value=1.32;l.new(rill.outputs['Fac'],rillcolor.inputs['Value']) +colorproduct=n.new('ShaderNodeVectorMath');colorproduct.operation='SCALE';l.new(aggregate,colorproduct.inputs[0]);l.new(rillcolor.outputs[0],colorproduct.inputs['Scale']);l.new(colorproduct.outputs[0],p.inputs['Base Color']) + + +I=P['intersection'];IW=P['intersection_width'];RW=P['road_halfwidth'];SW=P['sidewalk_width'] +for side in [-1,1]: + crossing_start,crossing_end=(38.0,51.5) if side<0 else (34.0,46.5) + for start,end in [(-30,crossing_start),(crossing_end,250)]: + for j in range(math.ceil((end-start)/2)): + ya=start+j*2;yb=min(end,ya+2) + box('Raised sidewalk segment',(side*(RW+SW/2),(ya+yb)/2,.06),(SW,yb-ya,.24),M['concrete']) + b=Batch('Individual granite curb stones','Street') + for j in range(int((end-start)/1.1)): + b.box((side*(RW+.12),start+j*1.1+.55,.13),(.26,1.08,.3),M['curb']) + b.finish() + b=Batch('Sidewalk expansion joints','Street') + for y in range(int(start),int(end),2): b.box((side*(RW+SW/2),y,.184),(SW,.018,.005),M['black']) + b.finish() + # Orthogonal sidewalks at the intersection. + for yy in [crossing_start-1.7,crossing_end+1.7]:box('Side street sidewalk',(side*28,yy,.08),(28,3.4,.2),M['concrete']) +# Reference has conspicuous crosswalk stripes across the side streets; the +# traveled carriageway itself is predominantly unmarked in the visible interval. +crossings=json.loads((ROOT/'assets/crosswalks.json').read_text()) +for side,description in crossings.items(): + b=Batch(side.title()+' measured side-street crossing','Road markings') + for pts in description['strips']:b.face(pts,M['roadpaint']) + b.finish() +for y in [61,100]: + bpy.ops.mesh.primitive_cylinder_add(vertices=48,radius=.47,depth=.022,location=(1.8,y,.002));o=bpy.context.object;o.name='Wet cast iron manhole';o.data.materials.append(M['metal']);objlink(o,'Street') + for x in [-.25,-.1,.1,.25]:path('Manhole slots',[(1.8+x,y-.3,.016),(1.8+x,y+.3,.016)],.008,M['black']) +for side in [-1,1]: + for y in [6,23,34,53,72,94]: + box('Storm drain',(side*(RW-.3),y,.014),(.42,.87,.016),M['metal']) + for j in range(7):box('Drain slot',(side*(RW-.3),y-.35+j*.1,.024),(.35,.035,.009),M['black']) +for k in range(24): + x=random.uniform(-10,10);y=random.uniform(-8,100) + path('Irregular asphalt repair seam',[(x+random.uniform(-.15,.15),y+j*.5,.013) for j in range(random.randint(4,15))],.003,M['black'],'Road markings') + +def building(name,side,start,width,height,depth,mat,levels,cols,fire=True,store=True,xoffset=0): + face=side*(RW+SW+xoffset);cx=face+side*depth/2;center=start+width/2 + if store: + body=box(name+' masonry mass',(cx,center,(height+4)/2),(depth,width,height-4),M[mat],'Architecture') + box(name+' ground floor rear wall',(face+side*(depth/2+1.5),center,2),(depth-3,width,4),M[mat],'Architecture') + for yy in [start+.08,start+width-.08]:box(name+' ground floor return wall',(cx,yy,2),(depth,.16,4),M[mat],'Architecture') + else:body=box(name+' masonry mass',(cx,center,height/2),(depth,width,height),M[mat],'Architecture') + details=Batch(name+' recessed windows and masonry trims','Architecture') + floor=(height-4.2)/max(levels,1) + for k in range(levels): + z=4.2+(k+.47)*floor + if k==levels-1:z=min(z,height-1.4) + for j in range(cols): + y=start+(j+.5)*width/cols;tower='prominent residential tower' in name + ww=min(2.2 if tower else 1.22,width/cols*.49);hh=min(3.1 if tower else 1.9,floor*.67 if tower else floor*.58) + # Dark recess surround, glass set behind raised frame. + details.box((face-side*.016,y,z),(.045,ww+.21,hh+.20),M['black']) + wm=random.choices(['glass','dimwindow','warmwindow','coolwindow'],[.969,.03,.001,0] if 'prominent residential tower' in name else [.92,.066,.012,.002])[0] + details.box((face-side*.044,y,z),(.016,ww,hh),M[wm]) + for edge in [-1,1]:details.box((face-side*.11,y+edge*(ww/2+.055),z),(.18,.09,hh+.17),M['trim']) + for zz in [z-hh/2-.08,z+hh/2+.06]:details.box((face-side*.13,y,zz),(.24,ww+.31,.12),M['trim']) + details.box((face-side*.07,y,z),(.06,ww,.065),M['metal']) + details.box((face-side*.07,y,z),(.06,.05,hh),M['metal']) + if random.random()<.22:details.box((face-side*.28,y,z-hh*.32),(.45,ww*.58,.32),M['metal']) + if wm=='warmwindow': + details.box((face-side*.065,y+ww*.22,z),(.018,.10,hh),M['stone']) + if k%2==0:details.box((face-side*.045,center,4.2+k*floor),(.15,width,.11),M['stone']) + for z,dd,h in [(3.7,.30,.22),(height-.35,.25,.20),(height,.52,.32),(height+.28,.35,.15)]: + details.box((face-side*dd/2,center,z),(dd,width+.35,h),M['trim']) + details.box((cx,center,height+.08),(depth,width,.16),M['black']) + for yy in [start+.35,start+width-.35]:details.box((face-side*.10,yy,2),(.24,.5,4),M['stone']) + # Building end elevation faces the approaching camera and carries real windows. + for k in range(levels): + z=4.2+(k+.47)*floor + for j in range(max(2,int(depth/2.4))): + x=face+side*(j+.5)*depth/max(2,int(depth/2.4)) + details.box((x,start-.025,z),(1.1,.04,1.65),M[random.choices(['glass','dimwindow','warmwindow'],[.975,.025,0] if 'tower' in name else [.9,.07,.03])[0]]) + details.box((x,start-.09,z-.91),(1.34,.2,.13),M['trim']) + details.finish() + if store: + shop=Batch(name+' street level shop interiors','Storefronts') + for j in range(max(1,int(width/4))): + if name=='Right far intersection red brick block' and j==0:continue + y=start+(j+.5)*width/max(1,int(width/4));w=width/max(1,int(width/4))-.28 + shop.box((face-side*.15,y,1.88),(.012,w-.18,3.12),M['shopglass']) + for yi in [-w/2,0,w/2]:shop.box((face-side*.18,y+yi,1.86),(.09,.09,3.35),M['trim']) + for z in [.25,3.48]:shop.box((face-side*.16,y,z),(.09,w,.09),M['trim']) + shop.box((face+side*1.6,y,.22),(2.8,w,.05),M['concrete']) + shop.box((face+side*2.4,y,1.75),(.08,w,3.3),M['stone']) + lit_probability=.95 if 'intersection restaurant' in name or name=='Right far intersection red brick block' else .23 + if random.random().5 else 22 + light(name+' room ceiling',(face+side*.85,y,3.45),(.78,.73,.55),room_power,'AREA',1.5,(face+side*.8,y,.6)) + shop.box((face+side*1.35,y,.85),(.7,w*.7,1.3),M['darkbrick']) + for rr in range(5):shop.box((face+side*1.4,y-w*.28+rr*w*.14,1.65),(.22,.13,.35),M['trim']) + shop.finish() + if fire and width<38: + escape=Batch(name+' fire escape landings and iron balusters','Fire escapes') + for fy in [start+width*.33,start+width*.72]: + for k in range(min(levels,8)): + z=4.2+k*floor-.1 + escape.box((face-side*.65,fy,z),(1.3,2.5,.1),M['escapeiron']) + for jj in range(13):escape.box((face-side*1.24,fy-1.2+jj*.2,z+.48),(.035,.035,1),M['escapeiron']) + escape.box((face-side*1.24,fy,z+.99),(.05,2.5,.05),M['escapeiron']) + if k45 else 'glass'],'Architecture') +# The intersection gap is open here; an extra near block incorrectly hid the +# far corner tree and pub during the final second of the reference. +fitted_building('Right far intersection red brick block',1,52,29,30.5,22,'darkbrick',5,10,25,1.85) +for xx,yy,hh in [(28,53,3.0),(31.6,54.8,2.4),(36.5,57.0,3.6)]: + box('Far corner roof chimney stack',(xx,yy,30.5+hh/2),(1.10,1.32,hh),M['darkbrick'],'Architecture') + box('Roof chimney coping',(xx,yy,30.5+hh+.06),(1.36,1.55,.17),M['stone'],'Architecture') + +fitted_building('Right distant narrow stepped tower',1,202,32,126,32,'darkbrick',30,8,40,1.4,False,False) +box('Distant tower setback crown',(56,216,132),(22,23,12),M['darkbrick'],'Architecture') +box('Distant tower rooftop penthouse',(56,219,141),(12,14,8),M['darkbrick'],'Architecture') +# Three narrow illuminated windows are a persistent distant landmark. +dw=json.loads((ROOT/'assets/distant_window_row.json').read_text()) +for dx in [-3.35,0,3.35]: + box('Distant tower measured three-window row',(dw['x']+dx,dw['y']-.03,dw['z']),(1.5,.025,3.2),M['measuredwindow'],'Architecture') +box('Distant tower lower horizontal lit pane',(dw['x']+9.8,dw['y']-.03,dw['z']-11.2),(3.6,.025,1.25),M['dimwindow'],'Architecture') +for side in [-1,1]: + y=62 if side==-1 else 82 + for j in range(9): + w=random.uniform(12,22);h=random.uniform(17,35) + if side==1 and 1850:wn=-wn +middle=(wc[0]+wc[1])/2;middle.z=19.0 +bay=Batch('Right opening shallow chamfered brick bay','Architecture') +front=[middle-wu*2.4+Vector((0,0,z)) for z in [-19,19]]+[middle+wu*2.4+Vector((0,0,z)) for z in [19,-19]] +back=[q-wn*3.2 for q in front] +bay.face(front,M['tanbrick']);bay.face(back[::-1],M['tanbrick']) +for j in range(4):bay.face([front[j],front[(j+1)%4],back[(j+1)%4],back[j]],M['tanbrick']) +bay.finish() +M['observedamberwindow']=material('Observed upper right warm window',(.92,.49,.14),.35,0,.28) +for j,center in enumerate(wc): + width=.92;height=2.45 if j==0 else 3.10 + panel=Batch('Observed upper right amber pane '+str(j),'Architecture') + def corners(w,h,offset):return [center+wn*offset+wu*dx+Vector((0,0,dz)) for dx,dz in [(-w/2,-h/2),(w/2,-h/2),(w/2,h/2),(-w/2,h/2)]] + panel.face(corners(width+.22,height+.22,.035),M['black']);panel.face(corners(width,height,.060),M['observedamberwindow']);panel.finish() + for zz in [-height/2,0,height/2]:path('Tracked amber window horizontal sash',[center+wn*.075-wu*width/2+Vector((0,0,zz)),center+wn*.075+wu*width/2+Vector((0,0,zz))],.033,M['metal'],'Architecture') + path('Tracked amber window central sash',[center+wn*.078+Vector((0,0,-height/2)),center+wn*.078+Vector((0,0,height/2))],.023,M['metal'],'Architecture') + +# Specific recognizable storefront signs on left. +for name,body,key,col in [('Training studio','TRAINING STUDIO','training','signgold'),('Hair decor','HAIR DECOR','hair','signwhite')]: + q=signs[key];yy=q['y'];zz=q['z'];ww=q['width'];hh=q['height'] + box(name+' dark fascia',(q['x'],yy,zz),(.18,ww,hh),M['black'],'Storefronts') + o=text_obj(name+' luminous letters',body,(q['x']+.12,yy,zz-.20),1.30 if key=='training' else 1.0,M[col],-1) + if boldfont:o.data.font=boldfont + if key=='training':o.scale.x=.91;text_obj('Studio small header','ULTIMATE FITNESS',(q['x']+.12,yy,zz+.57),.45,M['signwhite'],-1) + # Genuine inset lit shop bay with mullions, mirrors, counter and ceiling. + box(name+' lit rear wall',(q['x']-4,yy,2.8),(.12,ww*.9,5.5),M['stone'],'Storefronts') + box(name+' rear mirror',(q['x']-3.85,yy,3.0),(.02,ww*.65,3.2),M['chrome'],'Storefronts') + light(name+' interior white light',(q['x']-2,yy,5.6),(.65,.74,.77),600,'AREA',3.5,(q['x']-1.8,yy,.4)) + box(name+' interior ceiling fixture',(q['x']-2,yy,5.9),(2.0,ww*.65,.055),M['shopwhite'],'Storefronts') + for dy in [-ww*.33,0,ww*.33]:box(name+' front window mullion',(q['x']+.05,yy+dy,2.8),(.13,.12,5.5),M['metal'],'Storefronts') + for j,dy in enumerate([-ww*.27,0,ww*.27]): + cy=yy+dy;ix=q['x']-1.65 + if key=='hair': + box('Salon chair upholstered seat',(ix,cy,1.03),(.85,.80,.23),M['black'],'Storefronts',.09) + box('Salon chair upholstered back',(ix-.29,cy,1.66),(.20,.81,1.0),M['black'],'Storefronts',.10) + cylinder('Salon hydraulic chair pedestal',(ix,cy,.18),(ix,cy,.94),.11,M['chrome'],'Storefronts') + box('Salon chair foot plate',(ix,cy,.14),(.69,.67,.08),M['chrome'],'Storefronts',.10) + box('Salon workstation counter',(q['x']-2.8,cy,1.45),(.70,ww*.23,.13),M['stone'],'Storefronts') + for k in range(4):cylinder('Salon product bottle',(q['x']-2.71,cy-.39+k*.25,1.51),(q['x']-2.71,cy-.39+k*.25,1.9),.07,M['shopwhite' if k==0 else 'glass'],'Storefronts',8) + else: + for dz in [.42,1.25,2.08]: + cylinder('Studio equipment shelf',(ix,cy-.65,dz),(ix,cy+.65,dz),.033,M['metal'],'Storefronts') + for k in range(4): + yp=cy-.45+k*.30 + cylinder('Studio dumbbell handle',(ix-.17,yp,dz+.10),(ix+.17,yp,dz+.10),.030,M['chrome'],'Storefronts',8) + for dx in [-.17,.17]:sphere('Studio dark weight',(ix+dx,yp,dz+.10),(.09,.13,.13),M['black'],'Storefronts',8,4) + for dy2 in [-.65,.65]:cylinder('Studio storage upright',(ix,cy+dy2,.18),(ix,cy+dy2,2.3),.035,M['metal'],'Storefronts') + box(name+' interior vertical strip light',(q['x']-3.55,cy-ww*.11,3.6),(.026,.06,2.65),M['shopwhite'],'Storefronts') + box(name+' reception countertop',(q['x']-1.30,yy+ww*.38,1.52),(1.15,ww*.22,.12),M['stone'],'Storefronts') + box(name+' reception dark cabinet',(q['x']-1.35,yy+ww*.38,.84),(1.0,ww*.20,1.25),M['darkbrick'],'Storefronts') + +box('Restaurant canvas awning',(-26.5,46,5.4),(2.8,13,.22),M['signred'],'Storefronts') +text_obj('Restaurant sign','BISTRO • CAFE',(-25.0,46,5.1),.30,M['signwhite'],-1) +for y in [39.7+i*.43 for i in range(30)]:sphere('Restaurant warm festoon bulb',(-25,y,5.3),(.04,.04,.04),M['shopwarm'],'Storefronts',8,4) + +# The far corner has a wrapping black pub awning, glazed front elevation and +# small warm lamps. It becomes fully visible only toward the end of the clip. +for o in list(bpy.data.objects): + if o.name.startswith('Right far intersection red brick block ground floor rear wall'): + bpy.data.objects.remove(o,do_unlink=True) + elif o.name.startswith('Right far intersection red brick block ground floor return wall'): + yy=min((o.matrix_world @ Vector(c)).y for c in o.bound_box) + if yy<52.5:bpy.data.objects.remove(o,do_unlink=True) +M['pubwood']=material('Dark varnished pub wood',(.067,.037,.022),.38) +M['bottlegreen']=material('Dark green bar bottles',(.021,.071,.042),.19,.12) +box('Corner pub floor',(29.8,56.5,.1),(9.6,9,.12),M['pubwood'],'Storefronts') +box('Corner pub ceiling',(29.8,56.5,7.3),(9.6,9,.15),M['stone'],'Storefronts') +box('Corner pub rear wall',(29.8,60.65,3.65),(9.6,.18,7.3),M['darkbrick'],'Storefronts') +box('Corner pub side wall',(34.7,56.5,3.65),(.18,9,7.3),M['darkbrick'],'Storefronts') +box('Corner residential ground elevation',(40.9,52.04,3.65),(12.2,.22,7.3),M['darkbrick'],'Architecture') +for axis in ['front','side']: + for j in range(3): + pos=26.6+j*3.2 if axis=='front' else 53.6+j*3.2 + if axis=='front': + loc=(pos,51.84,3.55);size=(2.94,.02,6.6) + box('Pub front glazed bay',loc,size,M['shopglass'],'Storefronts') + box('Pub lower painted front panel',(pos,51.79,1.0),(2.94,.08,1.65),M['greenmetal'],'Storefronts') + for xx in [pos-1.5,pos+1.5]:box('Pub front stone mullion',(xx,51.73,3.5),(.13,.17,7.0),M['trim'],'Storefronts') + for zz in [.2,1.8,6.45,7.0]:box('Pub front horizontal molding',(pos,51.72,zz),(3.12,.15,.12),M['trim'],'Storefronts') + box('Pub front lower inset border',(pos,51.69,1.0),(2.5,.025,1.30),M['trim'],'Storefronts') + box('Pub front lower inset panel',(pos,51.65,1.0),(2.30,.025,1.08),M['greenmetal'],'Storefronts') + else: + box('Pub side glazed bay',(24.86,pos,3.55),(.02,2.94,6.6),M['shopglass'],'Storefronts') + box('Pub lower painted side panel',(24.81,pos,1.0),(.08,2.94,1.65),M['greenmetal'],'Storefronts') + for yy in [pos-1.5,pos+1.5]:box('Pub side stone mullion',(24.73,yy,3.5),(.17,.13,7.0),M['trim'],'Storefronts') + for zz in [.2,1.8,6.45,7.0]:box('Pub side horizontal molding',(24.72,pos,zz),(.15,3.12,.12),M['trim'],'Storefronts') +box('Pub wrapping front black awning',(29.8,50.95,7.0),(10.0,2.1,.64),M['black'],'Storefronts') +box('Pub wrapping side black awning',(24.05,56.7,7.0),(2.0,10.2,.64),M['black'],'Storefronts') +for zz in [6.69,7.31]: + path('Pub awning pale piping',[(34.8,49.88,zz),(23.04,49.88,zz),(23.04,61.8,zz)],.025,M['trim'],'Storefronts') +for x in [26.6,29.8,33.0]: + cylinder('Pub hanging lamp stem',(x,50.5,6.7),(x,50.5,6.33),.027,M['black'],'Storefronts') + sphere('Pub warm lamp globe',(x,50.5,6.23),(.14,.14,.20),M['shopwhite'],'Storefronts') + light('Pub warm awning light',(x,50.5,6.17),(.91,.73,.45),75,'POINT',.16) +for yy in [54,57,60]: + sphere('Pub side lamp globe',(23.6,yy,6.23),(.14,.14,.20),M['shopwhite'],'Storefronts') + light('Pub side awning light',(23.6,yy,6.17),(.76,.82,.86),65,'POINT',.16) +light('Pub interior warm room',(29.5,56.5,6.8),(.95,.70,.40),1200,'AREA',3.5,(29.5,56.5,1)) +# The camera sees this side wall through the front glass. Furnish the visible +# wall as well as the rear bar, with actual shelves, mirrors and small objects. +for yy in [54.0,57.0,59.5]: + box('Pub side wall timber backing',(34.52,yy,2.7),(.10,2.7,5.0),M['pubwood'],'Storefronts') + box('Pub framed interior mirror',(34.43,yy,3.8),(.04,2.20,2.1),M['chrome'],'Storefronts') + for zz in [2.55,4.95]:box('Pub side display shelf',(34.20,yy,zz),(.50,2.7,.12),M['pubwood'],'Storefronts') + for j in range(7): + cy=yy-1.0+j*.32 + cylinder('Pub window visible bottle',(34.19,cy,2.61),(34.19,cy,3.06),.065,M['bottlegreen'],'Storefronts',8) + cylinder('Pub visible bottle neck',(34.19,cy,3.06),(34.19,cy,3.24),.025,M['bottlegreen'],'Storefronts',8) + light('Pub side display warm light',(33.9,yy,5.6),(.91,.75,.49),120,'AREA',1.2,(34.35,yy,3.0)) +box('Pub bar counter',(29.8,56.8,2.1),(7.7,1.25,.18),M['pubwood'],'Storefronts') +box('Pub bar front',(29.8,56.95,1.05),(7.4,.15,1.95),M['pubwood'],'Storefronts') +for z in [2.6,3.7,4.8]: + box('Pub bottle shelf',(29.8,60.15,z),(8.0,.5,.08),M['pubwood'],'Storefronts') + for j in range(21): + x=26.1+j*.37;hh=.42+.08*(j%3) + cylinder('Bottle body',(x,60.12,z+.05),(x,60.12,z+hh),.09,M['bottlegreen'],'Storefronts',8) + cylinder('Bottle neck',(x,60.12,z+hh),(x,60.12,z+hh+.18),.037,M['bottlegreen'],'Storefronts',8) + box('Bottle pale label',(x,60.025,z+hh*.6),(.11,.005,.13),M['barrier_white'],'Storefronts') +for xx in [38.2,42.5]: + box('Corner ground floor recessed window',(xx,51.90,4.45),(1.15,.025,3.25),M['black'],'Architecture') + box('Corner narrow warm ground window',(xx,51.87,4.45),(.72,.018,2.7),M['dimwindow'],'Architecture') +for index,x in enumerate([38.0,41.0,44.0]): + yy=51.15-.16*index;z=.69;r=.62 + for wx in [x-1.05,x+1.05]: + path('Parked bicycle tire',[(wx+r*cos(a),yy,z+r*sin(a)) for a in np_linspace(0,2*pi,41)],.036,M['tire'],'Street furniture') + path('Bicycle polished rim',[(wx+.565*cos(a),yy-.025,z+.565*sin(a)) for a in np_linspace(0,2*pi,41)],.014,M['chrome'],'Street furniture') + for a in np_linspace(0,2*pi,13):cylinder('Bicycle spoke',(wx,yy-.025,z),(wx+.555*cos(a),yy-.025,z+.555*sin(a)),.006,M['chrome'],'Street furniture',4) + rear=(x-1.05,yy,z);front=(x+1.05,yy,z);pedal=(x-.06,yy,.60);seat=(x-.31,yy,1.58);head=(x+.67,yy,1.52) + for a,b in [(rear,pedal),(rear,seat),(seat,pedal),(seat,head),(head,pedal),(head,front)]:cylinder('Bicycle diamond frame',a,b,.032,M['metal'],'Street furniture',8) + cylinder('Bicycle seatpost',seat,(x-.31,yy,1.78),.025,M['chrome'],'Street furniture',8) + box('Bicycle saddle',(x-.34,yy,1.80),(.40,.19,.10),M['black'],'Street furniture',.035) + path('Bicycle handlebar',[(x+.67,yy,1.52),(x+.79,yy,1.9),(x+.82,yy-.27,1.94)],.026,M['chrome'],'Street furniture') + +def lamp(side,y,height=8.4,arm=1.6,power=650): + x=side*(RW+.65) + cylinder('Streetlight tapered shaft',(x,y,.2),(x,y,height),.075,M['metal'],'Streetlights') + cylinder('Streetlight base',(x,y,.2),(x,y,.7),.17,M['metal'],'Streetlights') + coords=[(x,y,height-1),(x,y,height-.2),(x-side*.28,y,height+.1),(x-side*arm,y,height+.14)] + path('Streetlight curved bracket',coords,.057,M['metal'],'Streetlights') + box('Streetlight luminaire',(x-side*arm,y,height+.1),(.57,.28,.13),M['metal'],'Streetlights') + sphere('Sodium white light lens',(x-side*arm,y,height+.045),(.23,.11,.045),M['lamp_distant'],'Streetlights') + light('Yellow white street light',(x-side*arm,y,height-.02),(.88,.82,.65),power,'POINT',.20) + return (x-side*arm,y,height+.045) +lamp_positions=[] +for side,ys in [(-1,[112,154,202]),(1,[132,172,220])]: + for y in ys:lamp_positions.append(lamp(side,y,8.4 if y<100 else 8,1.65,600 if y<100 else 400)) +# The two following luminaires are specifically triangulated, not a repeated row. +distant_lights=json.loads((ROOT/'assets/distant_lights.json').read_text()) +for key,power in [('right_next_lamp',450),('right_smaller_lamp',220)]: + head=Vector(distant_lights[key]['xyz']);base=head+Vector((1.5,0,0)) + cylinder('Observed '+key+' shaft',(base.x,base.y,ground_height(base.y,base.x)),(base.x,base.y,head.z-.3),.07,M['metal'],'Streetlights') + path('Observed '+key+' arm',[(base.x,base.y,head.z-.6),(base.x,base.y,head.z+.1),tuple(head+Vector((0,0,.1)))],.05,M['metal'],'Streetlights') + sphere('Observed '+key+' lens',head,(.20,.10,.045),M['lamp_secondary' if power>300 else 'lamp_distant'],'Streetlights') + light('Observed '+key+' light',head,(.90,.85,.70),power,'POINT',.16) +anchors=json.loads((ROOT/'assets/landmarks.json').read_text()) +for name,names in [('Left paired streetlamp',['left_near_lamp','left_far_lamp']),('Right paired streetlamp',['right_near_lamp','right_secondary_lamp'])]: + heads=[Vector(anchors[n]['xyz']) for n in names];base=(heads[0]+heads[1])/2;h=min(p.z for p in heads)-.3 + cylinder(name+' shaft',(base.x,base.y,ground_height(base.y,base.x)+.16),(base.x,base.y,h),.095,M['metal'],'Streetlights') + cylinder(name+' fluted pedestal',(base.x,base.y,ground_height(base.y,base.x)+.16),(base.x,base.y,ground_height(base.y,base.x)+.85),.19,M['metal'],'Streetlights') + for j,head in enumerate(heads): + path(name+' swept arm',[(base.x,base.y,h-.9),(base.x,base.y,h-.1),tuple(head+Vector((0,0,.12)))],.063,M['metal'],'Streetlights') + box(name+' fixture '+str(j),tuple(head+Vector((0,0,.075))),(.60,.28,.15),M['metal'],'Streetlights') + secondary=name.startswith('Right') and j==1 + sphere(name+' lamp lens '+str(j),tuple(head),(.12,.06,.025),M['lamp_secondary' if secondary else 'lamp'],'Streetlights') + light(name+' light '+str(j),tuple(head-Vector((0,0,.04))),(.90,.85,.70),1100 if secondary else 2200,'POINT',.19) + lamp_positions.append(tuple(head)) +# The opening upper-left luminaire leaves the field of view immediately. +cb=Euler((math.radians(90+P['pitch']),0,math.radians(-P['yaw'])),'XYZ').to_matrix() @ Matrix.Diagonal((1,-1,-1)) +cornerlight=cb @ Vector(((5-640)/369.5*8,(20-480)/369.5*8,8))+Vector((0,0,P['camera_height'])) +sphere('Opening clipped streetlight lens',cornerlight,(.035,.035,.025),M['lamp'],'Streetlights') +light('Opening clipped streetlight',cornerlight,(.90,.84,.64),650,'POINT',.18) +def signal(side,y,arm=2.8,anchor=None): + x=side*(RW+.65);z=5.5 + if anchor: + emitter_fit=json.loads((ROOT/'assets/signal_emitters.json').read_text()) + head=Vector(emitter_fit[anchor]['housing_anchor_xyz']);z=head.z+.3 + cylinder('Signal pole',(x,y,.2),(x,y,z+.8),.073,M['metal'],'Traffic signals') + path('Signal arm',[(x,y,z+.6),(x-side*.3,y,z+.85),(x-side*arm,y,z+.85)],.063,M['metal'],'Traffic signals') + sx=x-side*arm + if anchor: + sx=head.x + # The far mast reaches over the intersection to its triangulated head. + path('Measured signal cantilever',[(x,y,z+.6),(x,y,z+.85),(sx,head.y,z+.85)],.068,M['metal'],'Traffic signals') + y=head.y + box('Signal ochre housing',(sx,y,z),(.36,.30,1.03),M['stone'],'Traffic signals',.055) + box('Signal black front plate',(sx,y-.165,z),(.31,.025,.96),M['black'],'Traffic signals') + for j in range(3): + zz=z+.30-j*.30 + lens=sphere('Green signal lens' if j==2 else 'Inactive signal lens',(sx,y-.19,zz),(.140,.038,.140),M['signalgreen' if j==2 else 'black'],'Traffic signals') + # An actual projecting visor gives the source's diminishing visible + # lens area as the camera passes obliquely beneath the signal. + hood=Batch('Traffic signal projecting lens visor','Traffic signals') + angles=np_linspace(-.30,pi+.30,25) + for a,b in zip(angles,angles[1:]): + for radius in [.151,.169]: + hood.face([(sx+radius*cos(t),yy,zz+radius*sin(t)) for t,yy in [(a,y-.205),(b,y-.205),(b,y-.50),(a,y-.50)]],M['black']) + hood.face([(sx+r*cos(t),y-.50,zz+r*sin(t)) for t,r in [(a,.151),(b,.151),(b,.169),(a,.169)]],M['black']) + hood.finish() + light('Green signal local glow',(sx,y-.27,z-.3),(.002,1,.57),35,'POINT',.12) + box('Street name plate',(x-side*.65,y-.05,3.7),(1.45,.035,.24),M['greenmetal'],'Signs') + text_obj('Street name','W 73 ST',(x-side*.65,y-.08,3.7),.11,M['signwhite'],0) +signal(-1,I-.5,2.8,'left_near_signal');signal(1,I+IW-.5,2.8,'right_far_signal') +for side,y in [(-1,100),(1,112),(-1,170),(1,180)]:signal(side,y,2) +from install_signal_response import install as install_signal_response +install_signal_response(S) + +def car(name,x,y,color='black',kind='sedan',angle=0): + bodymat=material(name+' paint',color if isinstance(color,tuple) else (.027,.035,.034),.23,.18) + bodymat.node_tree.nodes['Principled BSDF'].inputs['Coat Weight'].default_value=.5 + SUV=kind in ['suv','jeep','van'];L=4.8 if SUV else 4.55;W=1.91 if SUV else 1.8 + H=1.15 if SUV else .96;group='Vehicles' + root=bpy.data.objects.new(name,None);coll(group).objects.link(root);root.location=(x,y,0);root.rotation_euler[2]=angle + parts=[] + # The side shell follows the wheel arches instead of intersecting the tires. + shell=Batch(name+' pressed body panels and wheel openings',group) + ys=[-L/2+j*L/72 for j in range(73)] + def body_width(yy):return W/2*(1-.07*(abs(yy)/(L/2))**5) + def body_top(yy):return (1.45 if SUV else 1.035)-(.20 if SUV else .12)*(abs(yy)/(L/2))**5 + def arch_edge(yy): + z=.36 + for center in [-1.47,1.44]: + if abs(yy-center)<.425:z=max(z,.44+math.sqrt(max(0,.425**2-(yy-center)**2))) + return z + for a,b in zip(ys,ys[1:]): + def skin(yy,t,side): + zz=arch_edge(yy)*(1-t)+body_top(yy)*t;rr=zz/body_top(yy) + return (side*body_width(yy)*(1-.10*(1-rr)**2-.025*rr**8),yy,zz) + for side in [-1,1]: + for t0,t1 in zip([0,.25,.55,.82],[.25,.55,.82,1]): + shell.face([skin(a,t0,side),skin(b,t0,side),skin(b,t1,side),skin(a,t1,side)],bodymat) + shell.face([skin(a,1,-1),skin(a,1,1),skin(b,1,1),skin(b,1,-1)],bodymat) + for yy in [-L/2,L/2]:shell.face([(-body_width(yy),yy,.36),(body_width(yy),yy,.36),(body_width(yy),yy,body_top(yy)),(-body_width(yy),yy,body_top(yy))],bodymat) + body=shell.finish() + bm=bmesh.new();bm.from_mesh(body.data);bmesh.ops.remove_doubles(bm,verts=list(bm.verts),dist=.0001);bm.to_mesh(body.data);bm.free() + for poly in body.data.polygons:poly.use_smooth=True + rounding=body.modifiers.new('Pressed body edge radii','BEVEL');rounding.width=.06;rounding.segments=3;rounding.limit_method='ANGLE';rounding.angle_limit=.45 + body.modifiers.new('Body panel normals','WEIGHTED_NORMAL');parts.append(body) + parts.append(box(name+' dark underbody',(0,0,.34),(W*.86,L*.91,.23),M['black'],group,.06)) + if name in ['Prominent white SUV','Left black SUV']: + # Unlit, exposed lamp assemblies sit ahead of the bumper skin. Their + # reflector pockets and clear cover catch the observed street lighting. + lensmat=material(name+' clear headlamp polycarbonate',(.84,.92,.94),.10,0) + lensmat.node_tree.nodes['Principled BSDF'].inputs['Transmission Weight'].default_value=.91 + lensmat.node_tree.nodes['Principled BSDF'].inputs['IOR'].default_value=1.47 + for side in [-1,1]: + hx=side*.66;hy=-L/2-.059;hz=1.105 + parts.append(box(name+' upper lamp recessed housing',(hx,hy,hz),(.55,.060,.175),M['black'],group,.042)) + for j in [-1,1]: + parts.append(sphere(name+' headlamp reflector',(hx+j*.125,hy-.035,hz),(.115,.028,.057),M['chrome'],group)) + parts.append(sphere(name+' dark projector center',(hx+j*.125,hy-.060,hz),(.040,.011,.040),M['glass'],group)) + parts.append(box(name+' headlamp transparent cover',(hx,hy-.078,hz),(.535,.018,.15),lensmat,group,.036)) + parts.append(path(name+' headlamp narrow chrome brow',[(hx-.255,hy-.082,hz+.073),(hx+.255,hy-.082,hz+.073)],.009,M['chrome'],group)) + parts.append(path(name+' hood front rolled lip',[(-.88,-L/2-.018,1.215),(0,-L/2-.036,1.247),(.88,-L/2-.018,1.215)],.018,bodymat,group)) + + # Trapezoidal cabin has sloped windshield and rear window. + b=Batch(name+' cabin glazing',group) + base=1.45 if SUV else 1.035;top=1.95 if SUV else 1.49 + pts=[(-W*.46,-1.3,base),(W*.46,-1.3,base),(W*.46,1.45,base),(-W*.46,1.45,base), + (-W*.38,-.76,top),(W*.38,-.76,top),(W*.38,1.37 if kind=='jeep' else .98,top),(-W*.38,1.37 if kind=='jeep' else .98,top)] + for idx in [(0,1,5,4),(1,2,6,5),(2,3,7,6),(3,0,4,7)]:b.face([pts[k] for k in idx],M['carglass']) + b.face([pts[k] for k in [4,5,6,7]],bodymat);cabin=b.finish();parts.append(cabin) + cabinround=cabin.modifiers.new('Cabin edge radii','BEVEL');cabinround.width=.085 if SUV else .065;cabinround.segments=5 + cabin.modifiers.new('Glazing panel normals','WEIGHTED_NORMAL') + for side in [-1,1]: + lower=0 if side<0 else 1;upper=4 if side<0 else 5;rear_lower=3 if side<0 else 2;rear_upper=7 if side<0 else 6 + parts.append(path(name+' sculpted A pillar',[pts[lower],pts[upper]],.065 if SUV else .045,bodymat,group)) + parts.append(path(name+' rear roof pillar',[pts[rear_lower],pts[rear_upper]],.085 if SUV else .060,bodymat,group)) + parts.append(path(name+' window surround',[pts[lower],pts[upper],pts[rear_upper],pts[rear_lower],pts[lower]],.018,M['chrome'],group)) + parts.append(box(name+' front seat',(side*.43,-.10,1.25),(.53,.56,.65),M['black'],group,.13)) + parts.append(box(name+' front headrest',(side*.43,.06,1.56),(.30,.20,.25),M['black'],group,.07)) + if SUV:parts.append(path(name+' roof luggage rail',[(side*W*.33,-.56,top+.06),(side*W*.33,.85,top+.06)],.026,M['chrome'],group)) + parts.append(box(name+' dashboard',(0,-.96,1.13),(W*.79,.44,.16),M['black'],group,.07)) + parts.append(box(name+' rear seat bench',(0,.78,1.21),(W*.8,.52,.50),M['black'],group,.10)) + for sx in [-1,1]: + for yy in [-1.47,1.44]: + parts.append(wheel_torus(name+' rounded tire',(sx*(W/2-.01),yy,.44),.285,.089,M['tire'],group)) + parts.append(cylinder(name+' brake disc',(sx*(W/2+.040),yy,.44),(sx*(W/2+.057),yy,.44),.242,M['silverwheel' if name=='Prominent white SUV' else 'metal'],group,24)) + parts.append(wheel_torus(name+' polished rim lip',(sx*(W/2+.080),yy,.44),.244,.015,M['chrome'],group)) + parts.append(cylinder(name+' wheel center',(sx*(W/2+.058),yy,.44),(sx*(W/2+.088),yy,.44),.095 if name=='Prominent white SUV' else .075,M['chrome'],group,16)) + spoke_count=10 if name=='Prominent white SUV' else 5 + for a in range(spoke_count): + aa=a*2*pi/spoke_count + for da in ([-.035,.035] if name=='Prominent white SUV' else [0]): + parts.append(cylinder(name+' alloy wheel spoke',(sx*(W/2+.083),yy+.065*cos(aa),.44+.065*sin(aa)),(sx*(W/2+.083),yy+.23*cos(aa+.10+da),.44+.23*sin(aa+.10+da)),.0175 if name=='Prominent white SUV' else .021,M['chrome'],group,6)) + parts.append(path(name+' wheel arch rolled edge',[(sx*(body_width(yy)+.002),yy+.43*cos(a),.44+.43*sin(a)) for a in np_linspace(0,pi,18)],.012,bodymat,group)) + parts.append(path(name+' side shoulder crease',[(sx*(W/2+.009),-1.95,1.36 if SUV else .94),(sx*(W/2+.009),1.95,1.36 if SUV else .94)],.007,bodymat,group)) + parts.append(box(name+' side mirror',(sx*(W/2+.12),-.73,1.28),(.26,.32,.17),bodymat,group,.06)) + for yy in [-.56,.78]:parts.append(box(name+' door handle',(sx*(W/2+.011),yy,1.31 if SUV else 1.0),(.027,.20,.045),M['chrome'],group,.01)) + parts.append(path(name+' front door shut line',[(sx*(W/2+.004),-.25,base-.025),(sx*(W/2+.004),-.27,.44)],.005,M['black'],group)) + parts.append(path(name+' rear door contour',[(sx*(W/2+.003),.99,base-.03),(sx*(W/2+.003),1.13,.97),(sx*(W/2+.003),1.00,.48)],.004,M['black'],group)) + for yy in [-.23,.99]:parts.append(box(name+' window pillar',(sx*W*.42,yy,(base+top)/2),(.08,.07,top-base),M['black'] if name=='Prominent white SUV' else bodymat,group)) + for yy in [-2.37,2.37]:parts.append(box(name+' light cluster',(sx*.64,yy,.83),(.50,.065,.18),M['glass' if yy<0 else 'tail'],group,.025)) + for yy in [-L/2,L/2]: + parts.append(box(name+' bumper',(0,yy,.48),(W*.94,.09,.17),bodymat,group,.05)) + parts.append(box(name+' license plate',(0,yy*1.022,.65),(.44,.015,.20),M['barrier_white'],group)) + grille_w=W*(.81 if SUV else .47);grille_h=.45 if SUV else .27;grille_z=.86 if SUV else .74 + parts.append(box(name+' front grille',(0,-L/2-.025,grille_z),(grille_w,.025,grille_h),M['black'],group,.035)) + for j in range(6):parts.append(box(name+' grille bright bar',(0,-L/2-.044,grille_z-grille_h/2+(j+.5)*grille_h/6),(grille_w*.95,.022,.016),M['chrome'],group)) + if SUV: + for sx in [-1,1]:parts.append(path(name+' vertical running light surround',[(sx*.81,-L/2-.055,.92),(sx*.81,-L/2-.055,.68),(sx*.64,-L/2-.055,.66)],.018,M['chrome'],group)) + if name=='Prominent white SUV': + badge=Batch('White SUV grille bowtie badge',group) + badge.face([(xx,-L/2-.051,1.0+zz) for xx,zz in [(-.18,-.035),(-.075,-.035),(-.075,-.063),(.075,-.063),(.075,-.035),(.18,-.035),(.18,.035),(.075,.035),(.075,.063),(-.075,.063),(-.075,.035),(-.18,.035)]],M['vehiclebadge']);parts.append(badge.finish()) + if kind=='jeep': + parts.append(cylinder(name+' rear mounted spare tire',(0,2.35,1.3834),(0,2.64,1.3834),.42,M['tire'],group,24)) + parts.append(cylinder(name+' spare dark rim recess',(0,2.64,1.3834),(0,2.66,1.3834),.277,M['black'],group,32)) + parts.append(path(name+' spare polished rim lip',[(.278*cos(a),2.672,1.3834+.278*sin(a)) for a in np_linspace(0,2*pi,49)],.018,M['chrome'],group)) + parts.append(cylinder(name+' spare center hub',(0,2.661,1.3834),(0,2.697,1.3834),.083,M['chrome'],group,16)) + for k in range(5): + a=k*2*pi/5 + for da in [-.045,.045]: + parts.append(cylinder(name+' spare split alloy spoke',(.074*cos(a),2.686,1.3834+.074*sin(a)),(.262*cos(a+da),2.686,1.3834+.262*sin(a+da)),.020,M['chrome'],group,6)) + parts.append(cylinder(name+' spare lug bolt',(.058*cos(a),2.695,1.3834+.058*sin(a)),(.058*cos(a),2.709,1.3834+.058*sin(a)),.013,M['metal'],group,6)) + for o in parts:o.parent=root + return root +def parked(name,x,y,color,kind='sedan',angle=0,scale=1.5): + root=car(name,x,y,color,kind,angle);root.scale=(scale,scale,scale);root.location.z=-.066*scale;return root +parked('Opening cropped left black sedan',-12.4,8.5,(.028,.033,.028),scale=1.5) +blackfit=json.loads((ROOT/'assets/black_car_contact.json').read_text());bp=blackfit['parameters'] +blackcar=parked('Left black SUV',bp['x'],bp['y'],(.022,.032,.028),'suv',angle=bp['yaw'],scale=1) +blackcar.scale=(bp['scale_x'],bp['scale_y'],bp['scale_z']);blackcar.location.z=blackfit['tire_contact_z'] +red_pose=json.loads((ROOT/'assets/near_vehicle_landmarks.json').read_text())['Left red sedan']['parameters'] +red=parked('Left red sedan',red_pose['x'],red_pose['y'],(.24,.045,.019),angle=red_pose['yaw'],scale=1) +red.scale=(red_pose['scale_x'],red_pose['scale_y'],red_pose['scale_z']);red.location.z=red_pose['z'];red.rotation_euler.x=red_pose['pitch'] +ws=json.loads((ROOT/'assets/white_suv_landmarks.json').read_text())['parameters'] +white_suv=parked('Prominent white SUV',ws['x'],ws['y'],(.66,.69,.68),'suv',ws['yaw'],1) +white_suv.scale=(ws['scale_x'],ws['scale_y'],ws['scale_z']);white_suv.location.z=ws['z'];white_suv.rotation_euler.x=ws['pitch'] +white_suv['pose_note']='Joint wheel and roof reprojection fit. Monocular lens/metric proportions are approximate.' +parked('White sedan behind SUV',-10.8,30.6,(.47,.50,.49),scale=1.5) +jeep=parked('Right dark Jeep spare wheel',11.5340,15.7263,(.027,.038,.028),'jeep',pi,1) +jeep.scale=(1.3,.72,1.3) +jeep.location.z=-.066*1.3 +intersection_car=json.loads((ROOT/'assets/intersection_vehicle.json').read_text()) +for side in [-1,1]: + for j,y in enumerate([54,60,66,72,78,85,92,100,108,116,124,133,142,151,160,170,181]): + if side==1:y+=3 + color=random.choice([(.17,.19,.19),(.38,.41,.4),(.03,.042,.049),(.18,.20,.24),(.49,.48,.43)]) + xx=side*11.0 + if side<0: + decay=math.exp(-j/4);y+=(intersection_car['root_xy'][1]-54)*decay;xx+=(intersection_car['root_xy'][0]+11)*decay + if j==0:color=(.035,.044,.05) + parked(f'{side} parked car {j}',xx,y,color,'suv' if j%5==0 else 'sedan',pi if side>0 else 0,1.4) +# Multi-time headlight tracks explain their movement through camera parallax. +# The unconstrained moving fit was not identifiable; the parked/idling solution +# avoids adding an unsupported fourteen-unit independent translation. +traffic=json.loads((ROOT/'assets/distant_lights.json').read_text()) +for i,key in enumerate(['distant_headlight_left','distant_headlight_center','distant_headlight_far']): + xyz=Vector(traffic[key]['xyz']);sc=1.30 if i<2 else 1.18 + xx=xyz.x+.67*sc;yy=xyz.y+2.44*sc + root=car('Distant headlight vehicle '+str(i),xx,yy,(.055,.065,.072));root.scale=(sc,sc,sc) + headlamp_material=material('Measured car '+str(i)+' blue headlamp',(.65,.82,1),.32,0,[45,32,150][i]) + for sx in [-.67,.67]: + o=sphere('Distant measured headlamp',(sx,-2.44,.83),(.12,.04,.05),headlamp_material,'Vehicles');o.parent=root + lo=light('Distant headlight beam',(sx,-2.49,.82),(.33,.60,1),[20,15,80][i],'SPOT',.09,(sx,-22,.1));lo.parent=root + root['motion_evidence']='No resolved independent velocity from 128-frame reference tracks.' + +def tree(name,side,y,height=10,crown=4.3,x_override=None): + x=side*(RW+1.5) if x_override is None else x_override;base=.2 + cylinder(name+' trunk',(x,y,base),(x+.13,y,height*.68),.15,M['bark'],'Trees',10) + # Individual broad leaf clusters plus editable twigs; fixed random seed. + b=Batch(name+' leaves','Trees') + tips=[] + for j in range(24): + angle=random.uniform(0,2*pi);r=random.uniform(.4,crown*.75);z=random.uniform(height*.50,height*.83) + end=(x+r*cos(angle),y+r*sin(angle),z+1.2) + tips.append(end) + cylinder(name+' branch',(x,y,z-1.7),end,.035,M['bark'],'Trees',6) + for j in range(60000 if y<80 else 24000): + tip=random.choice(tips);cx=tip[0]+random.gauss(0,crown*.18);cy=tip[1]+random.gauss(0,crown*.18);cz=tip[2]+random.gauss(0,crown*.19) + if ((cx-x)/crown)**2+((cy-y)/(crown*1.15))**2+((cz-height*.72)/(crown*.95))**2>1.65:continue + a=random.uniform(0,2*pi);tilt=random.uniform(-1.4,1.4);sz=random.uniform(.080,.18) + du=Vector((cos(a)*cos(tilt),sin(a)*cos(tilt),sin(tilt)))*sz;dv=Vector((-sin(a),cos(a),0))*sz*.48;c=Vector((cx,cy,cz)) + b.face([c-du,c+dv,c+du,c-dv],M[random.choices(['leaf','leaflight','leaffaded'],[.7,.25,.05])[0]]) + b.finish() + box(name+' soil pit',(x,y,.19),(1.5,2.3,.025),M['bark'],'Trees') + for xx in [x-.78,x+.78]:path(name+' guard rail',[(xx,y-1.2,.2),(xx,y-1.2,.64),(xx,y+1.2,.64),(xx,y+1.2,.2)],.023,M['metal'],'Trees') +tree_layout=[(-1,17.5,12,4.8,-19),(-1,35,15.5,6.0,-21),(-1,47,17,7.0,-27),(-1,69,27,11.5,-15), + (-1,79,17,6.4,-19),(-1,101,18,6.3,-20),(-1,132,17,5.8,-20), + (1,14,27,10.5,27),(1,29,20,9.0,19.5),(1,72,18,7.0,20), + (1,96,17,6.3,20),(1,125,17,5.8,20),(1,49.73,30,10.5,31.54)] +for j,(side,y,h,c,xo) in enumerate(tree_layout):tree(f'{side} street tree {j}',side,y,h,c,xo) +# A branch extends into the road and hides the secondary right lamp early in +# the clip. Its occlusion changes by camera parallax, with no visibility keys. +branch_center=Vector((13.2,29.0,11.2));cylinder('Right lamp occluding branch',(19.5,29,9.0),branch_center,.055,M['bark'],'Trees',8) +b=Batch('Right lamp occluding leaf cluster','Trees') +for j in range(18000): + u=Vector((random.gauss(0,1.10),random.gauss(0,1.15),random.gauss(0,1.30))) + if u.length>3.5:continue + c=branch_center+u;a=random.uniform(0,2*pi);sz=random.uniform(.06,.12) + du=Vector((cos(a),sin(a),random.uniform(-.8,.8)))*sz;dv=Vector((-sin(a),cos(a),0))*sz*.5 + b.face([c-du,c+dv,c+du,c-dv],M[random.choice(['leaf','leaf','leaflight'])]) +b.finish() + + +# The closest stretch is a dark low wall; colored water barriers and the +# taller pale construction panels are concentrated at the far end. +M['constructionpanel']=material('Rain-streaked pale construction panels',(.26,.29,.27),.76,.08) +noise_surface(M['constructionpanel'],3.5,.25,.012,(.65,.88)) +for j in range(7): + y=18.5+j*2.0;x=RW-.25 + if j<4: + box('Low dark roadside retaining wall',(x+.18,y,.56),(.50,1.98,1.12),M['concrete'],'Construction',.025) + box('Retaining wall weathered cap',(x+.18,y,1.14),(.60,1.98,.13),M['curb'],'Construction',.025) + path('Low fence top rail',[(x+.2,y-.97,1.5),(x+.2,y+.97,1.5)],.03,M['greenmetal'],'Construction') + for yy in [-.95,.95]:cylinder('Low fence post',(x+.2,y+yy,.8),(x+.2,y+yy,1.53),.035,M['greenmetal'],'Construction') + else: + box('Construction barrier foot',(x,y,.20),(.86,1.92,.40),M['orange' if j%3 else 'barrier_white'],'Construction',.08) + box('Construction barrier body',(x,y,.90),(.59,1.86,1.0),M['orange' if j%3 else 'barrier_white'],'Construction',.11) + for yy in [-.59,.59]:box('Barrier molded channel',(x-.31,y+yy,.94),(.018,.12,.62),M['rust'],'Construction') + box('Construction pale temporary panel',(x+.33,y,2.43),(.10,1.91,2.06),M['constructionpanel'],'Construction') + for zz in [1.40,3.46]:box('Fence galvanized rail',(x+.24,y,zz),(.12,1.96,.07),M['scaffold'],'Construction') + cylinder('Fence upright',(x+.29,y-.95,1.15),(x+.29,y-.95,3.54),.045,M['scaffold'],'Construction') +for side,y in [(1,7),(-1,32),(1,56)]: + x=side*(RW+1) + box('Blue sidewalk refuse bin',(x,y,.68),(.52,.52,1.05),M['bluebin'],'Street furniture',.045) + box('Refuse bin lid',(x,y,1.23),(.58,.58,.12),M['metal'],'Street furniture',.03) +for side,y in [(-1,18),(1,11),(-1,54),(1,62)]: + x=side*(RW+1.1);cylinder('Parking regulation signpost',(x,y,.2),(x,y,3.5),.035,M['metal'],'Street furniture') + box('Parking regulation white sign',(x,y-.025,3.2),(.37,.05,.55),M['barrier_white'],'Signs') + text_obj('Parking restrictions','NO\nPARKING',(x,y-.06,3.2),.075,M['signred'],0) +for side,y in [(1,53),(-1,48)]: + x=side*(RW+1.1);cylinder('Fire hydrant barrel',(x,y,.18),(x,y,.76),.13,M['metal'],'Street furniture') + sphere('Hydrant cap',(x,y,.76),(.16,.16,.1),M['metal'],'Street furniture') + cylinder('Hydrant valve',(x-.23,y,.56),(x+.23,y,.56),.085,M['metal'],'Street furniture') + +# Ground contact is applied to the generated street systems once, before the +# camera rig. Calibrated lamp heads and tower tops retain their observed pose. +def near_curb_width(y,side): + # Fixed parking-bay curb geometry, fitted to the near vehicle contacts. + # Tapers into the already measured intersection without moving its corners. + knots=[(-30,18.8),(14,18.8),(21,14.5),(27,12.5),(38,RW)] if side<0 else [(-30,14.2),(22,14.2),(34,RW)] + if y<=knots[0][0]:return knots[0][1] + for (ya,xa),(yb,xb) in zip(knots,knots[1:]): + if y<=yb: + t=(y-ya)/(yb-ya);t=t*t*(3-2*t);return xa+(xb-xa)*t + return RW +bpy.context.view_layer.update() +for o in list(bpy.data.objects): + if o.parent:continue + groups={c.name for c in o.users_collection} + if 'Street' in groups or 'Road markings' in groups: + if o.type=='MESH': + if o.data.users>1:o.data=o.data.copy() + inv=o.matrix_world.inverted() + for vtx in o.data.vertices: + q=o.matrix_world @ vtx.co + if o.name.startswith(('Raised sidewalk segment','Individual granite curb stones','Sidewalk expansion joints')): + side=-1 if q.x<0 else 1;factor=max(0,min(1,(RW+SW-abs(q.x))/SW)) + q.x+=side*(near_curb_width(q.y,side)-RW)*factor + q.z+=ground_height(q.y,q.x);vtx.co=inv@q + continue + fixed_tower=('Left prominent residential tower' in o.name or 'Right distant narrow stepped tower' in o.name or 'Distant tower' in o.name or o.name.startswith('Observed tower_')) + if fixed_tower: + if o.type=='MESH': + inv=o.matrix_world.inverted() + for vtx in o.data.vertices: + q=o.matrix_world @ vtx.co + if q.z<.5:q.z+=ground_height(q.y,q.x);vtx.co=inv@q + continue + if 'paired streetlamp' in o.name or o.name.startswith('Observed right_') or 'Traffic signals' in groups:continue + if o.type=='EMPTY': + x,y=o.location.x,o.location.y;o.location.z+=ground_height(y,x) + if 'Vehicles' in groups and o.name!='Prominent white SUV': + dx=(ground_height(y,x+.1)-ground_height(y,x-.1))/.2;dy=(ground_height(y+.1,x)-ground_height(y-.1,x))/.2 + xx=Vector((1,0,dx)).normalized();normal=Vector((-dx,-dy,1)).normalized();yy=normal.cross(xx).normalized() + terrain=Matrix((xx,yy,normal)).transposed();o.rotation_euler=(terrain@o.rotation_euler.to_matrix()).to_euler() + + else: + center=sum((o.matrix_world @ Vector(c) for c in o.bound_box),Vector())/8 if o.type in {'MESH','CURVE','FONT'} else o.location.copy() + o.location.z+=ground_height(center.y,center.x) + +# Camera: all positions derive from a single continuous trajectory. +bpy.ops.object.camera_add();cam=bpy.context.object;cam.name='Source matched windshield camera';objlink(cam,'Camera rig') +S.camera=cam;cam.data.type='PERSP';cam.data.sensor_width=36;cam.data.lens=18/math.tan(math.radians(P['fov']/2));cam.data.clip_end=1200;cam.data.clip_start=.035 +cam.data.shift_y=P.get('shift_y',0) +cam.data.dof.use_dof=False;cam.data.dof.focus_distance=45;cam.data.dof.aperture_fstop=3.5 +motion_path=ROOT/'assets/camera_motion.json' +motion=json.loads(motion_path.read_text()) if motion_path.exists() else None +base_rotation=Euler((math.radians(90+P['pitch']),0,math.radians(-P['yaw'])),'XYZ').to_matrix() +cv_to_blender=Matrix.Diagonal((1,-1,-1)) +if motion: + ep=motion['epipole'];pp=motion.get('principal_point',[640,360]);focal=motion['focal_pixels'] + translation_direction=base_rotation @ cv_to_blender @ Vector(((ep[0]-pp[0])/focal,(ep[1]-pp[1])/focal,1)) + translation_direction.normalize() +else:translation_direction=Vector((0,1,0)) +def motion_at(fr): + t=(fr-1)/127 + if not motion:return .91*t+.09*t*t,Vector((0,0,0)) + fs=motion['frames'];i=max(0,min(len(fs)-2,bisect.bisect_right(fs,fr)-1));a=(fr-fs[i])/(fs[i+1]-fs[i]) + d=motion['displacement_fraction'][i]*(1-a)+motion['displacement_fraction'][i+1]*a + r=Vector(motion['rotation_vectors'][i]).lerp(Vector(motion['rotation_vectors'][i+1]),a) + return d,r +for fr in range(1,129): + t=(fr-1)/127 + fraction,rotvec=motion_at(fr);distance=P['travel']*fraction + cam.location=Vector((0,0,P['camera_height']))+translation_direction*distance + if motion: + rr=Quaternion(rotvec.normalized(),rotvec.length).to_matrix() if rotvec.length>1e-9 else Matrix.Identity(3) + cam.rotation_euler=(base_rotation @ cv_to_blender @ rr.transposed() @ cv_to_blender).to_euler('XYZ') + else:cam.rotation_euler=(math.radians(90+P['pitch']+.06*sin(t*22)),math.radians(.05*sin(t*18)),math.radians(-P['yaw']+.11*sin(t*8))) + cam.keyframe_insert(data_path='location',frame=fr);cam.keyframe_insert(data_path='rotation_euler',frame=fr) +S.frame_set(1) +for x in [-.73,.73]: + o=light('Camera car low beam',(x,1.2,1.2),(.55,.71,1.0),2300,'SPOT',.12,(x,22,-.7)) + o.data.spot_size=math.radians(88) + # Root translates only; keep headlights directed along the road. + for fr in range(1,129): + o.location=Vector((x,1.2,1.2))+translation_direction*P['travel']*motion_at(fr)[0];o.keyframe_insert(data_path='location',frame=fr) +light('Urban sky bounce across foreground',(0,15,24),(.53,.59,.48),4500,'AREA',35,(0,20,0)) +light('Intersection reflected blue-gray sky fill',(0,44,25),(.40,.57,.90),P.get('intersection_fill_watts',2600),'AREA',28,(0,44,-.5)) +light('Broad reflected city fill along street',(0,72,35),(.49,.57,.59),5000,'AREA',45,(0,65,0)) +light('Right facade reflected warm city light',(20,29,12),(.86,.69,.39),900,'AREA',10,(32,35,21)) +light('Left masonry warm street bounce',(-19,37,8),(.80,.68,.42),650,'AREA',12,(-29,38,18)) + +from animation_curves import linear +for ob in bpy.data.objects: + if ob.animation_data and ob.animation_data.action:linear(ob) + +# Camera-attached lower rubber windshield boundary and sliver of the hood. +# Geometry is authored in normalized camera image coordinates on a near plane. +def screen_mesh(name,pts,mat,depth=.6,group='Windshield'): + f=cam.data.lens/36*1280;ppy=360+cam.data.shift_y*1280;verts=[((u-640)/f*depth,(ppy-v)/f*depth,-depth) for u,v in pts] + b=Batch(name,group);b.face(verts,mat);o=b.finish();o.parent=cam;return o +boundary=[(-80,615),(0,626),(150,670),(340,697),(590,711),(820,720),(1040,708),(1210,681),(1300,641),(1360,800),(-80,800)] +screen_mesh('Curved black windshield lower seal',boundary,M['black'],.40) +M['hood'].node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value=(.27,.27,.21,1) +M['hood'].node_tree.nodes['Principled BSDF'].inputs['Metallic'].default_value=.22 +M['hood'].node_tree.nodes['Principled BSDF'].inputs['Coat Weight'].default_value=.55 +hood=Batch('Curved silver hood surface','Windshield');hf=cam.data.lens/36*1280;hppy=360+cam.data.shift_y*1280 +def hoodpoint(i,j): + u=i/20*2-1;t=j/7;px=700+u*315*(1+.18*t);py=684+70*t+25*u*u+7*u;d=2.3-.7*t + return ((px-640)/hf*d,(hppy-py)/hf*d,-d) +for j in range(7): + for i in range(20):hood.face([hoodpoint(i,j),hoodpoint(i+1,j),hoodpoint(i+1,j+1),hoodpoint(i,j+1)],M['hood']) +ho=hood.finish();ho.parent=cam +for poly in ho.data.polygons:poly.use_smooth=True + +# Millimetre-scale water beads remain attached to the lower glass. Their +# refracted highlights come from the scene, with no emissive painted glints. +water=material('Clear windshield water beads',(.97,.99,1),.025,0) +wp=water.node_tree.nodes['Principled BSDF'];wp.inputs['Transmission Weight'].default_value=1;wp.inputs['IOR'].default_value=1.333 +drop_rng=random.Random(4404);depth=.52;pixel_world=depth/hf +for j in range(76): + u=drop_rng.uniform(95,1190);v=646+34*(1-((u-640)/640)**2)+drop_rng.uniform(-4,20) + radius=drop_rng.uniform(.65,2.6);stretch=drop_rng.uniform(1.1,2.6) + droplet=sphere('Fixed lower-glass water bead',((u-640)*pixel_world,(hppy-v)*pixel_world,-depth),(radius*pixel_world,radius*stretch*pixel_world,.72*pixel_world),water,'Windshield',12,6) + droplet.parent=cam + +# Humidity is fixed in world space and concentrated near the street. +coll('Atmosphere') +from atmosphere import install_atmosphere +install_atmosphere(P) + +# Post lens response: glare from actual rendered lights, fixed subtle distortion. +S.use_nodes=True +nt=S.compositing_node_group if hasattr(S,'compositing_node_group') else S.node_tree +if nt is None: + nt=bpy.data.node_groups.new('Rainy windshield optical response','CompositorNodeTree');S.compositing_node_group=nt +nt.nodes.clear();nodes=nt.nodes;links=nt.links +rl=nodes.new('CompositorNodeRLayers');rl.location=(-500,100) +gl=nodes.new('CompositorNodeGlare');gl.inputs['Type'].default_value='Fog Glow';gl.inputs['Quality'].default_value='High';gl.inputs['Threshold'].default_value=1.2;gl.inputs['Size'].default_value=.6;gl.inputs['Strength'].default_value=.65;gl.location=(-200,100) +links.new(rl.outputs['Image'],gl.inputs['Image']) +try: + out=nodes.new('CompositorNodeComposite');links.new(gl.outputs['Image'],out.inputs['Image']) +except RuntimeError: + nt.interface.new_socket(name='Image',in_out='OUTPUT',socket_type='NodeSocketColor') + out=nodes.new('NodeGroupOutput');links.new(gl.outputs['Image'],out.inputs['Image']) + +S['reconstruction_source']='night_front_wide_120fov.mp4 (reference only, never used by scene nodes)' +S['deterministic_seed']=404;S['camera_parameters']=json.dumps(P) +S['stage']=VARIANT +if P.get('optics',False): + sys.path.insert(0,str(ROOT/'scripts')) + from install_optics import install_optics + install_optics(S,P) +S.frame_set(1);S.render.filepath=str(ROOT/'renders/previews/layout_0001.png') +bpy.ops.file.pack_all() +for resource in list(bpy.data.images)+list(bpy.data.fonts): + if getattr(resource,'filepath',''): + path=pathlib.Path(bpy.path.abspath(resource.filepath)) + if path.is_absolute() and ROOT in path.parents:resource.filepath='//'+path.relative_to(ROOT).as_posix() +S.render.filepath='//renders/final/' +bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'scene_04.blend')) +inventory={'objects':len(bpy.data.objects),'meshes':len(bpy.data.meshes),'materials':len(bpy.data.materials), + 'polygons':sum(len(m.polygons) for m in bpy.data.meshes),'collections':list(collections), 'params':P} +(ROOT/'logs/scene_inventory.json').write_text(json.dumps(inventory,indent=2)) +print('BUILD_COMPLETE',json.dumps(inventory)) +if 'render' in ARGS: + native=S.compositing_node_group.nodes.get('SOURCE_FRAME_OUTPUT') if S.compositing_node_group else None + if native:native.directory=str(ROOT/'renders/previews');native.file_name=VARIANT+'_' + for fr in [1,65,128]: + S.frame_set(fr);S.render.filepath=str(ROOT/f'renders/previews/{VARIANT}_{fr:04d}.png');bpy.ops.render.render(write_still=native is None) diff --git a/astra_scene_test/Scene_04/scripts/calibrate_sky_response.py b/astra_scene_test/Scene_04/scripts/calibrate_sky_response.py new file mode 100644 index 0000000000000000000000000000000000000000..b1a93fa0c796cb9f5f5e0920666631daef552397 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/calibrate_sky_response.py @@ -0,0 +1,18 @@ +"""Infer background control from separate rendered in-scattering/background probes.""" +from pathlib import Path +import os,json,sys,numpy as np +os.environ['OPENCV_IO_ENABLE_OPENEXR']='1' +import cv2 +ROOT=Path(__file__).resolve().parents[1];cv2.setNumThreads(1) +P=json.loads((ROOT/'assets/config.json').read_text());out={};label=sys.argv[1] if len(sys.argv)>1 else 'sky_probe' +def srgb_to_linear(v):return np.where(v<=.04045,v/12.92,((v+.055)/1.055)**2.4) +for fr in [1,65,128]: + zero=cv2.imread(str(ROOT/f'renders/{label}/{fr:04d}_zero.exr'),cv2.IMREAD_UNCHANGED)[:,:,2::-1].mean((0,1)) + orig=cv2.imread(str(ROOT/f'renders/{label}/{fr:04d}_original.exr'),cv2.IMREAD_UNCHANGED)[:,:,2::-1].mean((0,1)) + ref=cv2.imread(str(ROOT/f'reference/frames/{fr:04d}.png'))[30:210,550:700,::-1]/255 + # A saved EXR/PNG round-trip probe confirms display gamma after the Standard view. + # Invert that order, then divide by scene-linear exposure. + target=srgb_to_linear(ref.mean((0,1))**P.get('gamma',1))/2**P['exposure'] + gain=(target-zero)/np.maximum(orig-zero,1e-8) + out[fr]={'source_srgb_mean':ref.mean((0,1)).tolist(),'zero_linear':zero.tolist(),'original_linear':orig.tolist(),'target_linear':target.tolist(),'gain':gain.tolist(),'old_control':P['sky_response'][str(fr)],'proposed_control':(gain*P['sky_response'][str(fr)]).tolist()} +print(json.dumps(out,indent=2));(ROOT/f'reference/{label}_response_fit.json').write_text(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/camera_math.py b/astra_scene_test/Scene_04/scripts/camera_math.py new file mode 100644 index 0000000000000000000000000000000000000000..536108d564446f0cde6a6f1262240ae3592604e5 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/camera_math.py @@ -0,0 +1,21 @@ +"""Standalone projection shared by reference measurements, never by rendering.""" +from pathlib import Path +import json,math,numpy as np +from scipy.spatial.transform import Rotation +ROOT=Path(__file__).resolve().parents[1] +P=json.loads((ROOT/'assets/config.json').read_text());M=json.loads((ROOT/'assets/camera_motion.json').read_text()) +p,y=map(math.radians,[P['pitch'],P['yaw']]);B=np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +f=M['focal_pixels'];pp=np.array(M['principal_point']);d=np.r_[(np.array(M['epipole'])-pp)/f,1];d=d/np.linalg.norm(d)*P['travel'];C0=np.array([0,0,P['camera_height']]) +def camera(fr): + t=np.interp(fr,M['frames'],M['displacement_fraction']);r=np.array([np.interp(fr,M['frames'],np.array(M['rotation_vectors'])[:,i]) for i in range(3)]) + return C0+B@d*t,Rotation.from_rotvec(r).as_matrix()@B.T +def project(q,fr): + C,R=camera(fr);p=R@(np.array(q)-C);return p[:2]/p[2]*f+pp +def triangulate(obs): + from scipy.optimize import least_squares + As=[];bs=[] + for fr,uv in obs.items(): + C,R=camera(int(fr));ray=R.T@np.r_[(np.array(uv)-pp)/f,1];ray/=np.linalg.norm(ray);A=np.eye(3)-np.outer(ray,ray);As.append(A);bs.append(A@C) + q=np.linalg.lstsq(np.concatenate(As),np.concatenate(bs),rcond=None)[0] + fit=least_squares(lambda q:np.concatenate([project(q,int(fr))-uv for fr,uv in obs.items()]),q,loss='soft_l1',f_scale=3) + return fit.x diff --git a/astra_scene_test/Scene_04/scripts/collect_probe_raw.py b/astra_scene_test/Scene_04/scripts/collect_probe_raw.py new file mode 100644 index 0000000000000000000000000000000000000000..33de3a4615e6dde252682e20e8c399f0faa72d04 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/collect_probe_raw.py @@ -0,0 +1,16 @@ +"""Preserve outputs from the first Blender 5.2 single-image File Output probe.""" +from pathlib import Path +import json,time,shutil +ROOT=Path(__file__).resolve().parents[1];folder=ROOT/'renders/optics_probe/linear' +start=time.monotonic();done=set() +while time.monotonic()-start<1800: + for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines(): + row=json.loads(line) + if row['label']=='optics_probe' and row['frame'] in {1,65,128}: + fr=row['frame'];target=folder/f'{fr:04d}_Raw.exr' + if target.exists():done.add(fr);continue + source=folder/'frameRaw.exr' + if source.exists():shutil.copy2(source,target);done.add(fr);print('RAW_PRESERVED',fr,flush=True) + if done=={1,65,128}:break + time.sleep(1) +print('RAW_COLLECTION_COMPLETE',sorted(done),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/collect_usage.py b/astra_scene_test/Scene_04/scripts/collect_usage.py new file mode 100644 index 0000000000000000000000000000000000000000..8c11706b5b223fd6bfb9551bf3c131f44dd5a24f --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/collect_usage.py @@ -0,0 +1,27 @@ +"""Collect task-scoped runtime usage counters, without copying conversation text.""" +import pathlib,json,datetime +ROOT=pathlib.Path(__file__).resolve().parents[1] +runtime=pathlib.Path('/Users/wangpy11/.codex/sessions/2026/09/11') +ids={'rollout-2026-09-11T20-25-40-01a0906e-434d-7f13-9245-c473b8fed82e.jsonl':'primary', + 'rollout-2026-09-11T20-30-16-01a09072-79fb-71f3-a8b0-3d3cc1799507.jsonl':'approval_guardian'} +result={'collected_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(), + 'note':'Raw cumulative runtime counters. Input includes repeated cached context. Approval guardian is host overhead, not a delegated scene-building agent. Billing cost is not available.', + 'billing_cost':None,'sessions':[]} +for filename,role in ids.items(): + path=runtime/filename + if not path.exists():continue + entries=[] + with path.open() as f: + for line in f: + try:row=json.loads(line) + except ValueError:continue + p=row.get('payload',{}) + if row.get('type')=='event_msg' and p.get('type')=='token_count' and p.get('info'): + entries.append({'timestamp':row.get('timestamp'),'total_token_usage':p['info'].get('total_token_usage'), + 'last_token_usage':p['info'].get('last_token_usage')}) + result['sessions'].append({'role':role,'runtime_log':filename,'snapshots':entries}) +(ROOT/'logs/token_usage.json').write_text(json.dumps(result,indent=2)) +session_path=ROOT/'logs/session.json';session=json.loads(session_path.read_text()) +session['token_usage']={r['role']:r['snapshots'][-1] for r in result['sessions'] if r['snapshots']} +session['usage_note']=result['note'];session['cost']=None;session_path.write_text(json.dumps(session,indent=2)) +print('USAGE_SNAPSHOTS_SAVED',[(r['role'],len(r['snapshots'])) for r in result['sessions']]) diff --git a/astra_scene_test/Scene_04/scripts/compare.py b/astra_scene_test/Scene_04/scripts/compare.py new file mode 100644 index 0000000000000000000000000000000000000000..ef886b86df7c5dba6a3ec3f0a911d1de3c95a944 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/compare.py @@ -0,0 +1,27 @@ +"""Timestamp-matched image boards and video assembly; never alters the source.""" +import pathlib,sys,subprocess,json +from PIL import Image,ImageDraw +import numpy as np +ROOT=pathlib.Path(__file__).resolve().parents[1] +label=sys.argv[1] if len(sys.argv)>1 else 'layout' +folder=ROOT/'renders'/label +if not folder.exists():folder=ROOT/'renders/previews' +files=sorted(folder.glob('*.png')) +if folder.name=='previews':files=[p for p in files if p.name.startswith(label+'_')] +out=ROOT/'renders/comparisons';out.mkdir(exist_ok=True) +rows=[] +for p in files: + frame=int(p.stem.split('_')[-1]);a=Image.open(ROOT/f'reference/frames/{frame:04d}.png').convert('RGB');b=Image.open(p).convert('RGB') + b=b.resize(a.size);board=Image.new('RGB',(2560,752),(17,18,18));board.paste(a,(0,32));board.paste(b,(1280,32));d=ImageDraw.Draw(board) + d.text((15,10),f'SOURCE | frame {frame:03d} | {(frame-1)/30:.4f} s',fill='white') + d.text((1295,10),f'BLENDER {label} | frame {frame:03d} | {(frame-1)/30:.4f} s',fill='white') + board.save(out/f'{label}_{frame:04d}.jpg',quality=94) + aa=np.asarray(a,dtype=float);bb=np.asarray(b,dtype=float) + rows.append({'frame':frame,'mae_rgb_255':float(np.abs(aa-bb).mean()),'source_mean_rgb':aa.mean((0,1)).tolist(),'render_mean_rgb':bb.mean((0,1)).tolist()}) +(ROOT/f'logs/comparison_{label}.json').write_text(json.dumps(rows,indent=2)) +if label=='final': + assert [p.name for p in files]==[f'{fr:04d}.png' for fr in range(1,129)],'All128 completed frames required before encoding' + ff=str(ROOT/'tools/ffmpeg') + subprocess.run([ff,'-y','-hide_banner','-loglevel','error','-framerate','30','-start_number','1','-i',str(folder/'%04d.png'),'-frames:v','128','-r','30','-fps_mode','cfr','-video_track_timescale','15360','-an','-c:v','libx264','-preset','slow','-crf','16','-pix_fmt','yuv420p','-movflags','+faststart',str(ROOT/'final.mp4')],check=True) + subprocess.run([ff,'-y','-hide_banner','-loglevel','error','-i',str(ROOT/'night_front_wide_120fov.mp4'),'-i',str(ROOT/'final.mp4'),'-filter_complex','[0:v]setpts=PTS-STARTPTS[a];[1:v]setpts=PTS-STARTPTS[b];[a][b]hstack=inputs=2[v]','-map','[v]','-frames:v','128','-r','30','-fps_mode','cfr','-video_track_timescale','15360','-an','-c:v','libx264','-preset','slow','-crf','17','-pix_fmt','yuv420p','-movflags','+faststart',str(ROOT/'comparison.mp4')],check=True) +print('COMPARISON_COMPLETE',label,len(rows)) diff --git a/astra_scene_test/Scene_04/scripts/correct_initial_camera.py b/astra_scene_test/Scene_04/scripts/correct_initial_camera.py new file mode 100644 index 0000000000000000000000000000000000000000..c8cc7cb6a0117de1f2c8b0276abe89ce531172d1 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/correct_initial_camera.py @@ -0,0 +1,8 @@ +import bpy,math,pathlib,json +R=pathlib.Path(__file__).resolve().parents[1];S=bpy.context.scene;P=json.loads((R/'assets/config.json').read_text()) +for fr in range(1,129): + S.frame_set(fr);t=(fr-1)/127 + S.camera.rotation_euler.x=math.radians(90+P['pitch']+.06*math.sin(t*22)) + S.camera.keyframe_insert(data_path='rotation_euler',frame=fr) +S.frame_set(1);bpy.ops.wm.save_as_mainfile(filepath=str(R/'scene_04.blend')) +print('CAMERA_PITCH_CORRECTED',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/delivery_manifest.py b/astra_scene_test/Scene_04/scripts/delivery_manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..657370f20f4ebe2e32ecc3445b226e96a01ff821 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/delivery_manifest.py @@ -0,0 +1,41 @@ +"""Record final artifact hashes, actual elapsed intervals and render provenance. + +Run only after full rendering, encoding, decoding and reopening checks complete. +This records evidence; it does not replace visual acceptance of the comparison. +""" +import datetime, hashlib, json, pathlib, statistics +from elapsed_time import measure_elapsed +ROOT = pathlib.Path(__file__).resolve().parents[1] +def read(name): return json.loads((ROOT/name).read_text()) +def sha(path): + h=hashlib.sha256() + with path.open('rb') as f: + for block in iter(lambda:f.read(1024*1024),b''):h.update(block) + return h.hexdigest() +now=datetime.datetime.now(datetime.timezone.utc) +session=read('logs/session.json') +start=datetime.datetime.fromisoformat(session['started_utc']) +timing=measure_elapsed(now);excluded=timing['excluded_seconds'] +blend_sha=sha(ROOT/'scene_04.blend') +rows={r['frame']:r for r in (json.loads(line) for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines()) if r['label']=='final'} +assert set(rows)==set(range(1,129)) +assert all(r['blend_sha256']==blend_sha for r in rows.values()) +for fr,r in rows.items():assert sha(ROOT/f'renders/final/{fr:04d}.png')==r['sha256'] +paths=[ROOT/name for name in ['scene_04.blend','final.mp4','comparison.mp4','night_front_wide_120fov.mp4','README.md','GATES.md','reproduce.sh','requirements.txt','review.html']] +paths += [ROOT/name for name in ['logs/final_review.md','logs/iteration_review.md','logs/final_acceptance.json','logs/final_gate_check.log','logs/session.json','logs/token_usage.json','logs/usage_interruption.json','logs/idle_intervals.json','logs/elapsed_time.json','logs/operations.jsonl','logs/render_operations.jsonl','logs/batch_operations.jsonl','logs/delivery_operations.jsonl','logs/automated_delivery_checks.json','logs/reopen_pixel_consistency.json','logs/reopened_scene_audit.json','logs/temporal_review_final.json']] +paths+=sorted((ROOT/'scripts').glob('*.py')) +paths+=sorted(p for p in (ROOT/'assets').rglob('*') if p.is_file()) +paths+=sorted((ROOT/'renders/final').glob('*.png')) +times=[r['seconds'] for r in rows.values()] +report={'recorded_utc':now.isoformat(),'started_utc':start.isoformat(), + 'elapsed_wall_hours':timing['wall_seconds']/3600,'excluded_quota_hours':timing['excluded_by_kind_seconds']['quota']/3600, + 'excluded_idle_hours':timing['excluded_by_kind_seconds']['idle']/3600,'elapsed_excluding_pauses_hours':timing['meaningful_seconds']/3600, + 'timing_note':timing['note'], + 'final_render_seconds_sum':sum(times),'final_render_seconds_median':statistics.median(times), + 'final_frame_count':len(rows),'source_probe':read('logs/probe_night_front_wide_120fov.json'), + 'acceptance':read('logs/final_acceptance.json'), + 'final_probe':read('logs/probe_final.json'),'comparison_probe':read('logs/probe_comparison.json'), + 'usage_latest':session['token_usage'],'billing_cost':session['cost'],'usage_note':session['usage_note'], + 'files':{str(p.relative_to(ROOT)):{'bytes':p.stat().st_size,'sha256':sha(p)} for p in paths}} +(ROOT/'logs/delivery_manifest.json').write_text(json.dumps(report,indent=2)) +print('DELIVERY_MANIFEST_WRITTEN',report['final_frame_count'],round(report['elapsed_excluding_pauses_hours'],3)) diff --git a/astra_scene_test/Scene_04/scripts/elapsed_time.py b/astra_scene_test/Scene_04/scripts/elapsed_time.py new file mode 100644 index 0000000000000000000000000000000000000000..de5018c5c97b8246e7451fed52052c0db9a961eb --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/elapsed_time.py @@ -0,0 +1,32 @@ +"""Actual elapsed time excluding recorded quota and inactive intervals.""" +import datetime,json,pathlib +ROOT=pathlib.Path(__file__).resolve().parents[1] +def measure_elapsed(now=None): + now=now or datetime.datetime.now(datetime.timezone.utc) + session=json.loads((ROOT/'logs/session.json').read_text()) + start=datetime.datetime.fromisoformat(session['started_utc']) + spans=[];by_kind={'quota':0.0,'idle':0.0} + quota=ROOT/'logs/usage_interruption.json' + if quota.exists(): + p=json.loads(quota.read_text()) + spans.append(('quota',p['interruption_after_utc'],p['resumed_utc'])) + idle=ROOT/'logs/idle_intervals.json' + if idle.exists(): + spans.extend(('idle',p['start_utc'],p['end_utc']) for p in json.loads(idle.read_text())) + clipped=[] + for kind,a,b in spans: + a=datetime.datetime.fromisoformat(a);b=datetime.datetime.fromisoformat(b) + assert a.tzinfo and b.tzinfo and b>=a + a=max(start,a);b=min(now,b) + if b>a:clipped.append((a,b));by_kind[kind]+=(b-a).total_seconds() + merged=[] + for a,b in sorted(clipped): + if merged and a<=merged[-1][1]:merged[-1]=(merged[-1][0],max(merged[-1][1],b)) + else:merged.append((a,b)) + excluded=sum((b-a).total_seconds() for a,b in merged) + wall=(now-start).total_seconds() + return {'recorded_utc':now.isoformat(),'started_utc':start.isoformat(), + 'wall_seconds':wall,'excluded_seconds':excluded,'meaningful_seconds':wall-excluded, + 'excluded_by_kind_seconds':by_kind, + 'note':'Elapsed implementation, review and rendering time, excluding recorded pauses. Not a CPU/GPU utilization measurement.'} +if __name__=='__main__':print(json.dumps(measure_elapsed(),indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/encode_preview.py b/astra_scene_test/Scene_04/scripts/encode_preview.py new file mode 100644 index 0000000000000000000000000000000000000000..a0916a4aed3b2d1901222799055d9caba503d4ca --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/encode_preview.py @@ -0,0 +1,9 @@ +"""Encode a complete numbered preview and an exactly indexed comparison.""" +from pathlib import Path +import sys,subprocess +ROOT=Path(__file__).resolve().parents[1];label=sys.argv[1];folder=ROOT/'renders'/label +assert [p.name for p in sorted(folder.glob('*.png'))]==[f'{n:04d}.png' for n in range(1,129)],'All128 ordered preview frames required' +ff=str(ROOT/'tools/ffmpeg') +subprocess.run([ff,'-y','-hide_banner','-loglevel','error','-framerate','30','-start_number','1','-i',str(folder/'%04d.png'),'-frames:v','128','-r','30','-fps_mode','cfr','-video_track_timescale','15360','-an','-c:v','libx264','-preset','slow','-crf','16','-pix_fmt','yuv420p','-movflags','+faststart',str(ROOT/'draft.mp4')],check=True) +subprocess.run([ff,'-y','-hide_banner','-loglevel','error','-i',str(ROOT/'night_front_wide_120fov.mp4'),'-i',str(ROOT/'draft.mp4'),'-filter_complex','[0:v]setpts=PTS-STARTPTS[a];[1:v]scale=1280:720:flags=lanczos,setpts=PTS-STARTPTS[b];[a][b]hstack=inputs=2[v]','-map','[v]','-frames:v','128','-r','30','-fps_mode','cfr','-video_track_timescale','15360','-an','-c:v','libx264','-preset','slow','-crf','17','-pix_fmt','yuv420p','-movflags','+faststart',str(ROOT/'draft_comparison.mp4')],check=True) +print('PREVIEW_VIDEOS_COMPLETE',label) diff --git a/astra_scene_test/Scene_04/scripts/fetch_materials.py b/astra_scene_test/Scene_04/scripts/fetch_materials.py new file mode 100644 index 0000000000000000000000000000000000000000..9ed51b65becdbb06dcc09067cdbc62fb29c05e82 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fetch_materials.py @@ -0,0 +1,18 @@ +"""Download the selected CC0 photographic material maps from Poly Haven.""" +from pathlib import Path +import subprocess,json,hashlib,datetime +ROOT=Path(__file__).resolve().parents[1];folder=ROOT/'assets/materials';folder.mkdir(parents=True,exist_ok=True) +assets={'asphalt_track':{'author':'Dimitrios Savva','physical_width_m':2.0},'brick_wall_09':{'authors':['Matterfield','Rob Tuytel'],'physical_width_m':2.0}} +records=[] +def fetch(url,path): + if not path.exists():subprocess.run(['curl','--fail','--location','--silent','--show-error','--max-time','90',url,'-o',str(path)],check=True) +for name,info in assets.items(): + metadata=folder/f'{name}_files.json';fetch(f'https://api.polyhaven.com/files/{name}',metadata);data=json.loads(metadata.read_text()) + record={'asset':name,'source':f'https://polyhaven.com/a/{name}','license':'CC0',**info,'maps':{}} + for channel,api_key,fmt in [('diff','Diffuse','jpg'),('rough','Rough','jpg'),('disp','Displacement','png')]: + entry=data[api_key]['2k'][fmt];url=entry['url'];path=folder/Path(url.split('?')[0]).name;fetch(url,path) + record['maps'][channel]={'path':str(path.relative_to(ROOT)),'url':url,'bytes':path.stat().st_size,'sha256':hashlib.sha256(path.read_bytes()).hexdigest()} + print('DOWNLOADED',name,channel,path.stat().st_size,flush=True) + records.append(record) +(folder/'manifest.json').write_text(json.dumps({'downloaded_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'assets':records},indent=2)) +print('MATERIAL_ASSETS_READY',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/final_progress.py b/astra_scene_test/Scene_04/scripts/final_progress.py new file mode 100644 index 0000000000000000000000000000000000000000..5a7f3165f36805f7b91d5870f32428a62ff46483 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/final_progress.py @@ -0,0 +1,27 @@ +"""Read current frozen render progress without changing render state.""" +from pathlib import Path +import datetime,hashlib,json,statistics +ROOT=Path(__file__).resolve().parents[1] +blend=hashlib.sha256((ROOT/'scene_04.blend').read_bytes()).hexdigest() +rows={} +for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines(): + r=json.loads(line) + if r.get('label')=='final':rows[r['frame']]=r +current=[] +for fr,r in rows.items(): + p=ROOT/f'renders/final/{fr:04d}.png' + if r.get('blend_sha256')==blend and p.exists() and hashlib.sha256(p.read_bytes()).hexdigest()==r['sha256']:current.append(r) +current.sort(key=lambda r:r['utc']) +records=[] +for line in (ROOT/'logs/final_wrap_manager.log').read_text().splitlines(): + try:records.append(json.loads(line)) + except ValueError:pass +latest=records[-1] if records else {} +seconds=statistics.median([r['seconds'] for r in current[-8:]]) if current else None +report={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'blend_sha256':blend,'completed':len(current),'total':128, + 'latest_frame':current[-1]['frame'] if current else None,'latest_completed_utc':current[-1]['utc'] if current else None, + 'recent_median_seconds':seconds,'estimated_remaining_render_hours':(128-len(current))*seconds/3600 if seconds else None, + 'note':'Estimate excludes remaining batch startup, encoding and reopening checks; shared-machine speed varies.', + 'manager_event':latest.get('event'),'active_frames':latest.get('frames') if latest.get('event')=='batch_start' else None} +(ROOT/'logs/final_progress.json').write_text(json.dumps(report,indent=2)) +print(json.dumps(report)) diff --git a/astra_scene_test/Scene_04/scripts/finalize_delivery.py b/astra_scene_test/Scene_04/scripts/finalize_delivery.py new file mode 100644 index 0000000000000000000000000000000000000000..1bc7bef16802c900e0bd73b8dd0274ffea056e4e --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/finalize_delivery.py @@ -0,0 +1,61 @@ +"""Finish encoding and verification after the current frozen final render. + +This does not mark visual acceptance gates. Whole-sequence visual review remains +separate. Every stage stops on failure or a changed scene. +""" +import datetime, hashlib, json, os, pathlib, subprocess, sys, time +ROOT=pathlib.Path(__file__).resolve().parents[1] +PYTHON=sys.executable +BLENDER=os.environ.get('SCENE04_BLENDER','/Applications/Blender.app/Contents/MacOS/Blender') +def sha(path):return hashlib.sha256(path.read_bytes()).hexdigest() +scene_hash=sha(ROOT/'scene_04.blend') +samples=json.loads((ROOT/'assets/config.json').read_text())['samples'] +env={**os.environ,'OPENBLAS_NUM_THREADS':'1','VECLIB_MAXIMUM_THREADS':'1','OMP_NUM_THREADS':'1'} +records=[] +def event(kind,**kwargs): + row={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'event':kind,'blend_sha256':scene_hash,**kwargs} + records.append(row) + with (ROOT/'logs/delivery_operations.jsonl').open('a') as f:f.write(json.dumps(row)+'\n') + print(json.dumps(row),flush=True) +def stable():assert sha(ROOT/'scene_04.blend')==scene_hash,'Scene changed during delivery processing' +def run(name,args): + stable();event('stage_start',stage=name);start=time.monotonic() + path=ROOT/'logs'/('delivery_'+name+'.log') + with path.open('w') as out:r=subprocess.run(args,cwd=ROOT,env=env,stdout=out,stderr=subprocess.STDOUT) + event('stage_end',stage=name,exit_code=r.returncode,seconds=time.monotonic()-start,log=str(path.relative_to(ROOT))) + assert r.returncode==0,f'{name} failed; see {path}' + stable() +event('waiting_for_verified_final_render') +deadline=time.monotonic()+12*3600 +while True: + stable();ready=False + for line in (ROOT/'logs/batch_operations.jsonl').read_text().splitlines(): + try:row=json.loads(line) + except ValueError:continue + if row.get('label')=='final' and row.get('blend_sha256')==scene_hash and row.get('event')=='sequence_verified' and row.get('count')==128:ready=True + if ready:break + assert time.monotonic()=128 +apply_quality(S,final_samples,100);device=configure(S) +for ob in bpy.data.objects: + if ob.animation_data and ob.animation_data.action:linear(ob) +assert len(curves(S.camera))==6 +S['camera_parameters']=json.dumps(P);S['stage']='final_reference_reconstruction';S.frame_set(1) +S.render.filepath='//renders/final/' +bpy.ops.file.pack_all() +for resource in list(bpy.data.images)+list(bpy.data.fonts): + if getattr(resource,'filepath',''): + path=pathlib.Path(bpy.path.abspath(resource.filepath)) + if path.is_absolute() and ROOT in path.parents:resource.filepath='//'+path.relative_to(ROOT).as_posix() +(ROOT/'assets/config.json').write_text(json.dumps(P,indent=2)) +bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'scene_04.blend')) +shutil.copy2(ROOT/'scene_04.blend',ROOT/'checkpoints/16_final_render_scene.blend') +def sha(p):return hashlib.sha256(p.read_bytes()).hexdigest() +files=[ROOT/'scene_04.blend']+sorted((ROOT/'scripts').glob('*.py'))+sorted(p for p in (ROOT/'assets').rglob('*') if p.is_file()) +manifest={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'device':device,'settings':P, + 'source_sha256':sha(ROOT/'night_front_wide_120fov.mp4'),'files':{str(p.relative_to(ROOT)):sha(p) for p in files}} +(ROOT/'logs/final_scene_manifest.json').write_text(json.dumps(manifest,indent=2)) +print('FINAL_SCENE_FROZEN',manifest['files']['scene_04.blend'],flush=True) diff --git a/astra_scene_test/Scene_04/scripts/fit_black_car_contact.py b/astra_scene_test/Scene_04/scripts/fit_black_car_contact.py new file mode 100644 index 0000000000000000000000000000000000000000..168ee09dc13b4500de7ec2c9355d4489ff832340 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_black_car_contact.py @@ -0,0 +1,19 @@ +"""Near black SUV fit with explicit tire contact, avoiding a buried-wheel solution.""" +from pathlib import Path +import json,sys,numpy as np,math +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import project +obs=json.loads((ROOT/'assets/near_vehicle_landmarks.json').read_text())['Left black SUV']['observations'] +coords={'front_wheel':[1.035,-1.47,.44],'rear_wheel':[1.035,1.44,.44],'roof_front':[.726,-.76,1.95],'roof_rear':[.726,.98,1.95]} +def point(a,key): + x,y,sx,sy,sz,yaw=a;xx,yy,zz=np.array(coords[key])*[sx,sy,sz] + return [x+xx*math.cos(yaw)-yy*math.sin(yaw),y+xx*math.sin(yaw)+yy*math.cos(yaw),zz-.066*sz] +def residual(a): + return np.r_[np.concatenate([project(point(a,key),int(fr))-uv for key,rows in obs.items() for fr,uv in rows.items()]),(a[2]-1.35)*3] +fit=least_squares(residual,[-12.2,13.3,1.35,1.1,1.3,-.2],bounds=([-18,9,.9,.6,.8,-.8],[-7,23,1.9,2.1,1.9,.8]),loss='soft_l1',f_scale=3) +out={'parameters':dict(zip(['x','y','scale_x','scale_y','scale_z','yaw'],fit.x.tolist())),'tire_contact_z':-.066*float(fit.x[4]), + 'observations':obs,'predictions':{key:{fr:project(point(fit.x,key),int(fr)).tolist() for fr in rows} for key,rows in obs.items()},'cost':fit.cost, + 'note':'Static pose, pitch0 and both tire bottoms on z0. Camera fixed; metric shape remains ambiguous.'} +(ROOT/'reference/black_car_contact_trial.json').write_text(json.dumps(out,indent=2)) +print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_camera.py b/astra_scene_test/Scene_04/scripts/fit_camera.py new file mode 100644 index 0000000000000000000000000000000000000000..2159c685bc3bb6176d8e02fa5a562287e4f3313d --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_camera.py @@ -0,0 +1,66 @@ +"""Robust pure-forward bundle fit, with measured camera orientation fluctuations. + +Monocular reconstruction has an arbitrary metric scale; fix total road travel +after fitting. Intrinsics are a tested hypothesis, not recoverable uniquely from +this near-forward four-second sequence alone. +""" +import json,pathlib,sys,numpy as np +from scipy.optimize import least_squares +from scipy.spatial.transform import Rotation +root=pathlib.Path(__file__).resolve().parents[1] +tracks=np.load(root/'reference/feature_tracks.npz')['tracks'] +ids=[7,8,10,11,13,17,25,31,33,38,74,75,85,97,122,143,149,197,208,212,251,259,365,367,369] +samples=np.array(list(range(0,128,8))+[127]);N=len(ids);F=len(samples) +obs=tracks[ids][:,samples];valid=np.isfinite(obs[:,:,0]);f=float(sys.argv[1]) if len(sys.argv)>1 else 640/np.tan(np.deg2rad(60));pp=np.array([640,float(sys.argv[3]) if len(sys.argv)>3 else 360]) +prior=float(sys.argv[2]) if len(sys.argv)>2 else .03 +projection=sys.argv[4] if len(sys.argv)>4 else 'perspective' +nodist=len(sys.argv)>5 and sys.argv[5]=='nodist' +def ray_from_pixels(pixels): + uv=(np.asarray(pixels)-pp)/f + if projection=='equirectangular':return np.stack([np.tan(uv[...,0]),np.tan(uv[...,1])/np.cos(uv[...,0]),np.ones(uv.shape[:-1])],axis=-1) + if projection=='cylindrical':return np.stack([np.tan(uv[...,0]),uv[...,1]/np.cos(uv[...,0]),np.ones(uv.shape[:-1])],axis=-1) + return np.concatenate([uv,np.ones(uv.shape[:-1]+(1,))],axis=-1) +uv0=(obs[:,0]-pp)/f +z0=[] +for tr in obs: + r=np.linalg.norm(tr[-1]-[610,537])/np.linalg.norm(tr[0]-[610,537]);z0.append(np.clip(r/(r-1),1.05,30)) +x0=np.r_[610,537,np.log(z0),np.zeros((F-1)*3),np.linspace(0,1,F)[1:-1],0.0] +def unpack(x): + ep=x[:2];z=np.exp(x[2:2+N]);rot=np.r_[np.zeros(3),x[2+N:2+N+(F-1)*3]].reshape(F,3) + t=np.r_[0,x[2+N+(F-1)*3:-1],1] + return ep,z,rot,t +def project(x): + ep,z,rot,t=unpack(x);k=x[-1];uv=uv0.copy() + for _ in range(12):uv=uv0/(1+k*np.sum(uv*uv,axis=1))[:,None] + d=ray_from_pixels(ep);p=(np.c_[uv,np.ones(N)] if projection=='perspective' else ray_from_pixels(obs[:,0]))*z[:,None] + q=p[:,None,:]-t[None,:,None]*d + R=Rotation.from_rotvec(rot).as_matrix();q=np.einsum('fij,nfj->nfi',R,q) + if projection in ['equirectangular','cylindrical']: + u=np.arctan2(q[:,:,0],q[:,:,2]);hyp=np.hypot(q[:,:,0],q[:,:,2]) + v=np.arctan2(q[:,:,1],hyp) if projection=='equirectangular' else q[:,:,1]/hyp + return np.stack([u,v],axis=2)*f+pp + uv=q[:,:,:2]/q[:,:,2:] + return uv*(1+k*np.sum(uv*uv,axis=2))[:,:,None]*f+pp +def residual(x): + ep,z,rot,t=unpack(x);delta=project(x)-obs + weights=np.array([3 if n==122 else 2 if n==85 else 1.3 if n in [74,75,251] else 1 for n in ids]) + r=(delta*weights[:,None,None])[valid].ravel() + # Weak regularizers prevent a camera wobble from absorbing scene deformation. + return np.r_[r,rot.ravel()*12,np.diff(rot,n=2,axis=0).ravel()*60,np.diff(t,n=2)*4,(ep-[615,537])*prior,x[-1]*.5] +lo=np.full(len(x0),-np.inf);hi=np.full(len(x0),np.inf) +lo[-1]=-.12;hi[-1]=.3 +if projection!='perspective' or nodist:lo[-1]=-1e-8;hi[-1]=1e-8 +if prior>=100: + x0[:2]=[615,537];lo[:2]=[614.999999,536.999999];hi[:2]=[615.000001,537.000001] +fit=least_squares(residual,x0,loss='soft_l1',f_scale=2,max_nfev=700,bounds=(lo,hi)) +ep,z,rot,t=unpack(fit.x);err=np.linalg.norm(project(fit.x)-obs,axis=2) +result={'projection':projection,'focal_pixels':f,'principal_point':pp.tolist(),'horizontal_fov_degrees':float(np.degrees(1280/f) if projection!='perspective' else np.degrees(2*np.arctan(640/f))),'epipole':ep.tolist(), + 'optimizer_success':bool(fit.success),'optimizer_nfev':fit.nfev,'optimizer_message':fit.message, + 'radial_k1':float(fit.x[-1]), + 'frames':(samples+1).tolist(),'displacement_fraction':t.tolist(),'rotation_vectors':rot.tolist(), + 'track_ids':ids,'depth_in_total_travel_units':z.tolist(), + 'median_reprojection_error_pixels':float(np.nanmedian(err)), + 'p90_reprojection_error_pixels':float(np.nanpercentile(err,90)), + 'limitation':'Metric scale and exact lens distortion are not uniquely constrained. Focal length is a tested hypothesis; inspect horizontal_fov_degrees for this trial. Robust forward-motion tracks alone do not establish unique physical intrinsics.'} +(root/('reference/camera_fit.json' if len(sys.argv)<2 else f'reference/camera_fit_{projection}_f{f:.0f}_p{prior}_cy{pp[1]:.0f}'+('_nodist' if nodist else '')+'.json')).write_text(json.dumps(result,indent=2)) +print(json.dumps({k:v for k,v in result.items() if k not in ['rotation_vectors','depth_in_total_travel_units','track_ids']},indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_crosswalks.py b/astra_scene_test/Scene_04/scripts/fit_crosswalks.py new file mode 100644 index 0000000000000000000000000000000000000000..6151d10cc79b0f9aa3200bd92cafd5c0a3e657fc --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_crosswalks.py @@ -0,0 +1,35 @@ +"""Editable paint-strip footprints from the ending view and measured ground plane. +Only annotated coordinates are used, never source pixels in the scene. +""" +from pathlib import Path +import json,sys,numpy as np +from PIL import Image,ImageDraw +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import camera,project,f,pp +from road_surface import ground_height +plane=json.loads((ROOT/'reference/intersection_plane_fit.json').read_text())['coefficients'];a,b,c=plane +C,R=camera(128) +def on_plane(uv): + d=R.T@np.r_[(np.array(uv)-pp)/f,1];t=(a*C[0]+b*C[1]+c-C[2])/(d[2]-a*d[0]-b*d[1]);return C+d*t +# Near and far crossing edges, observed through the wet glass. Extremities +# outside the frame are extrapolated along the visible stripe direction. +obs={'right':[[928,573],[1098,556],[1360,591],[1202,622]],'left':[[-100,632],[-150,595],[151,577],[218,586]]} +out={} +for side,uv in obs.items(): + q=np.array([on_plane(p) for p in uv]);n=9 + # A pair of straight 3D edges preserves one continuous crossing in all frames. + strips=[] + for j in range(n): + t0=(j+.1)/n;t1=(j+.59)/n + pts=[q[0]*(1-t0)+q[3]*t0,q[1]*(1-t0)+q[2]*t0,q[1]*(1-t1)+q[2]*t1,q[0]*(1-t1)+q[3]*t1] + strips.append([[float(p[0]),float(p[1]),.014] for p in pts]) + out[side]={'observed_end_bounds':uv,'plane_world_bounds':q.tolist(),'strips':strips} +(ROOT/'assets/crosswalks.json').write_text(json.dumps(out,indent=2)) +for fr in [65,97,113,128]: + im=Image.open(ROOT/f'reference/frames/{fr:04d}.png');draw=ImageDraw.Draw(im) + for side,d in out.items(): + for strip in d['strips']: + pts=[project([x,y,ground_height(y,x)+z],fr) for x,y,z in strip] + draw.line([tuple(p) for p in pts+[pts[0]]],fill=(255,160,20),width=1) + im.save(ROOT/f'reference/crops/crosswalk_layout_{fr:04d}.png') +print(json.dumps({k:v['plane_world_bounds'] for k,v in out.items()},indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_distant_traffic.py b/astra_scene_test/Scene_04/scripts/fit_distant_traffic.py new file mode 100644 index 0000000000000000000000000000000000000000..dff4904aac98dca4d0688e14c0d4d2c4db86817c --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_distant_traffic.py @@ -0,0 +1,25 @@ +"""Compare stationary and constant-speed explanations for three light tracks.""" +from pathlib import Path +import json,math,numpy as np +from scipy.spatial.transform import Rotation +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];P=json.loads((ROOT/'assets/config.json').read_text());M=json.loads((ROOT/'assets/camera_motion.json').read_text());T=np.load(ROOT/'reference/feature_tracks.npz')['tracks'] +p,y=map(math.radians,[P['pitch'],P['yaw']]);B=np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +f=M['focal_pixels'];pp=np.array(M['principal_point']);d=np.r_[(np.array(M['epipole'])-pp)/f,1];d*=P['travel']/np.linalg.norm(d) +def ground(y):return 0 if y<=44 else (-.085*(y-44)**2/32 if y<60 else -.085*(y-52)) +def project(a,fr): + x,y,velocity=a;yy=y+velocity*(fr-1)/30;pos=np.array([x,yy,ground(yy)+1.15]) + t=np.interp(fr,M['frames'],M['displacement_fraction']);r=np.array([np.interp(fr,M['frames'],np.array(M['rotation_vectors'])[:,i]) for i in range(3)]) + R=Rotation.from_rotvec(r).as_matrix();C=B@d*t+[0,0,P['camera_height']];q=R@B.T@(pos-C);return q[:2]/q[2]*f+pp +out={} +for idx in [8,33,60]: + obs={fr:T[idx,fr-1] for fr in range(1,129,4) if np.isfinite(T[idx,fr-1]).all()} + def residual(a):return np.concatenate([project(a,fr)-uv for fr,uv in obs.items()]) + fixed=least_squares(lambda a:residual([*a,0]),[-5,140],bounds=([-35,60],[15,450]),loss='soft_l1',f_scale=2) + moving=least_squares(lambda a:np.r_[residual(a),a[2]*.25],[*fixed.x,0],bounds=([-35,60,-15],[15,450,4]),loss='soft_l1',f_scale=2) + ef=float(np.mean(np.linalg.norm(residual([*fixed.x,0]).reshape(-1,2),axis=1)));em=float(np.mean(np.linalg.norm(residual(moving.x).reshape(-1,2),axis=1))) + chosen=moving.x if ef-em>1.5 else np.r_[fixed.x,0] + out[str(idx)]={'stationary_mean_error_pixels':ef,'moving_mean_error_pixels':em,'chosen_x_y_velocity':chosen.tolist(),'moving_trial':moving.x.tolist(), + 'observations':{fr:uv.tolist() for fr,uv in obs.items()},'predictions':{fr:project(chosen,fr).tolist() for fr in obs}} +(ROOT/'assets/distant_traffic_fit.json').write_text(json.dumps(out,indent=2)) +for idx,q in out.items():print(idx,q['chosen_x_y_velocity'],'stationary',q['stationary_mean_error_pixels'],'moving',q['moving_mean_error_pixels']) diff --git a/astra_scene_test/Scene_04/scripts/fit_ground.py b/astra_scene_test/Scene_04/scripts/fit_ground.py new file mode 100644 index 0000000000000000000000000000000000000000..5f03c2640a94c9aae8f6e0ef3de3959937c10c95 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_ground.py @@ -0,0 +1,25 @@ +"""Calibrate physical road level with observed bases of the paired left lamp. + +The image-space camera fit is unchanged. This resolves its otherwise arbitrary +world-up orientation and height against ground contact across the sequence. +""" +from pathlib import Path +import numpy as np,json,math +from scipy.spatial.transform import Rotation +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1] +P=json.loads((ROOT/'assets/config.json').read_text());M=json.loads((ROOT/'assets/camera_motion.json').read_text());A=json.loads((ROOT/'assets/landmarks.json').read_text()) +f=M['focal_pixels'];pp=np.array(M['principal_point']);d=np.r_[(np.array(M['epipole'])-pp)/f,1];d=d/np.linalg.norm(d)*P['travel'] +def basis(p): + p=math.radians(p);y=math.radians(P['yaw']) + return np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +B0=basis(P['pitch']);heads=np.array([B0.T@(np.array(A[n]['xyz'])-[0,0,P['camera_height']]) for n in ['left_near_lamp','left_far_lamp']]) +targets={1:[519,541],65:[477,545],128:[340,555]} +def predict(a,fr): + p,h=a;B=basis(p);Q=B@heads.mean(axis=0)+[0,0,h];Q[2]=0 + t=np.interp(fr,M['frames'],M['displacement_fraction']);r=np.array([np.interp(fr,M['frames'],np.array(M['rotation_vectors'])[:,i]) for i in range(3)]) + R=Rotation.from_rotvec(r).as_matrix();C=np.array([0,0,h])+B@d*t;q=R@B.T@(Q-C) + return q[:2]/q[2]*f+pp +fit=least_squares(lambda a:np.concatenate([predict(a,fr)-uv for fr,uv in targets.items()]),[5,3],bounds=([-10,.1],[15,10])) +out={'pitch':fit.x[0],'camera_height':fit.x[1],'lamp_base_targets':targets,'predictions':{fr:predict(fit.x,fr).tolist() for fr in targets},'residuals':fit.fun.tolist()} +(ROOT/'reference/ground_camera_fit.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_intersection_vehicle.py b/astra_scene_test/Scene_04/scripts/fit_intersection_vehicle.py new file mode 100644 index 0000000000000000000000000000000000000000..ff54e14f18488a6f78b3afc22ea864881b6cea34 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_intersection_vehicle.py @@ -0,0 +1,8 @@ +"""Three-time fit of the first dark parked SUV beyond the left crossing.""" +from pathlib import Path +import sys,json +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import triangulate,project +obs={65:[495,544],97:[453,547],128:[381,563]};point=triangulate(obs) +out={'license_plate_observations':obs,'license_plate_xyz':point.tolist(),'scale':1.4,'root_xy':[float(point[0]),float(point[1]+2.4*1.022*1.4)],'predictions':{fr:project(point,fr).tolist() for fr in obs},'note':'Static three-view fit. Car body and later row spacing are inferred; terrain contact is shared with the street.'} +(ROOT/'assets/intersection_vehicle.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_landmarks.py b/astra_scene_test/Scene_04/scripts/fit_landmarks.py new file mode 100644 index 0000000000000000000000000000000000000000..fbba02887c9c6b02e569258aad46fea978b3ba02 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_landmarks.py @@ -0,0 +1,44 @@ +"""Triangulate named scene anchors from the fitted camera and manual observations.""" +from pathlib import Path +import json,numpy as np,math +from scipy.spatial.transform import Rotation +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];fit=json.loads((ROOT/'assets/camera_motion.json').read_text()) +f=fit['focal_pixels'];pp=np.array(fit['principal_point']);ep=np.array(fit['epipole']);travel=27.8 +d=np.r_[(ep-pp)/f,1];scale=travel/np.linalg.norm(d) +config=json.loads((ROOT/'assets/config.json').read_text()) +pitch=math.radians(config['pitch']);yaw=math.radians(config['yaw']) +right=np.array([math.cos(yaw),-math.sin(yaw),0]);down=np.array([math.sin(yaw)*math.sin(pitch),math.cos(yaw)*math.sin(pitch),-math.cos(pitch)]) +forward=np.array([math.sin(yaw)*math.cos(pitch),math.cos(yaw)*math.cos(pitch),math.sin(pitch)]) +B=np.stack([right,down,forward],axis=1) +tracks={ + 'left_near_lamp':{1:[500,441],33:[477,421],65:[444,407],97:[393,373],128:[300,325]}, + 'right_near_lamp':{1:[727,395],33:[752,355],65:[798,307],97:[886,185]}, + 'right_secondary_lamp':{97:[960,254],128:[1148,100]}, +} +raw=np.load(ROOT/'reference/feature_tracks.npz')['tracks'] +for name,idx in [('left_far_lamp',143),('left_near_signal',122),('right_far_signal',85),('tower_window_right',74),('tower_window_above',75),('tower_window_left',251)]: + tracks[name]={fr:raw[idx,fr-1].tolist() for fr in fit['frames'] if np.isfinite(raw[idx,fr-1,0])} +def camera(fr): + t=np.interp(fr,fit['frames'],fit['displacement_fraction']);r=np.array([np.interp(fr,fit['frames'],np.array(fit['rotation_vectors'])[:,i]) for i in range(3)]) + return t*d,Rotation.from_rotvec(r).as_matrix() +out={} +for name,obs in tracks.items(): + As=[];bs=[] + for fr,uv in obs.items(): + C,R=camera(fr);ray=R.T@np.r_[(np.array(uv)-pp)/f,1];ray/=np.linalg.norm(ray) + A=np.eye(3)-np.outer(ray,ray);As.append(A);bs.append(A@C) + point=np.linalg.lstsq(np.concatenate(As),np.concatenate(bs),rcond=None)[0] + def residual(p): + result=[] + for fr,uv in obs.items(): + C,R=camera(fr);q=R@(p-C);xy=q[:2]/q[2]*f+pp;result.extend(xy-uv) + return result + point=least_squares(residual,point,loss='soft_l1',f_scale=3,max_nfev=120).x + errors=[];pred={} + for fr,uv in obs.items(): + C,R=camera(fr);q=R@(point-C);xy=q[:2]/q[2]*f+pp;pred[fr]=xy.tolist();errors.append(float(np.linalg.norm(xy-uv))) + xyz=B@(point*scale)+[0,0,config['camera_height']] + out[name]={'xyz':xyz.tolist(),'observations':obs,'projections':pred,'mean_error_pixels':float(np.mean(errors)),'max_error_pixels':float(np.max(errors))} +(ROOT/'assets/landmarks.json').write_text(json.dumps(out,indent=2)) +for name,row in out.items():print(name,np.round(row['xyz'],2),'mean px',round(row['mean_error_pixels'],2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_near_vehicles.py b/astra_scene_test/Scene_04/scripts/fit_near_vehicles.py new file mode 100644 index 0000000000000000000000000000000000000000..614912c55591997e76c51fda40685a9e0b061731 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_near_vehicles.py @@ -0,0 +1,36 @@ +"""Joint wheel/roof fits for the near parked vehicles, with camera held fixed. +Manual observations refer to reference/crops/near_car_coordinates_*.png. +These are image-space reconstructions; monocular metric dimensions are ambiguous. +""" +from pathlib import Path +import sys,json,math,numpy as np +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import project +DATA={ + 'Left black SUV':{ + 'observations':{'front_wheel':{1:[242,583],17:[114,599]},'rear_wheel':{1:[309,570],17:[217,580],33:[69,602]}, + 'roof_front':{1:[245,522],17:[147,522]},'roof_rear':{1:[306,516],17:[206,517],33:[65,521]}}, + 'coords':{'front_wheel':[1.035,-1.47,.44],'rear_wheel':[1.035,1.44,.44],'roof_front':[.726,-.76,1.95],'roof_rear':[.726,.98,1.95]}, + 'initial':[-12.2,13.3,1.35,1.35,1.35,0,0,0]}, + 'Left red sedan':{ + 'observations':{'front_wheel':{1:[377,559],17:[289,580],33:[188,591]},'rear_wheel':{1:[410,553],17:[334,565],33:[257,576]}, + 'roof_front':{1:[376,526],17:[292,529],33:[203,534]},'roof_rear':{1:[404,523],17:[323,528],33:[238,530]}}, + 'coords':{'front_wheel':[.98,-1.47,.44],'rear_wheel':[.98,1.44,.44],'roof_front':[.684,-.76,1.49],'roof_rear':[.684,.98,1.49]}, + 'initial':[-11.5,18,1.4,1.4,1.4,0,0,0]}} +OUT={} +for name,v in DATA.items(): + def point(a,key): + x,y,sx,sy,sz,yaw,pitch,z=a;xx,yy,zz=np.array(v['coords'][key])*[sx,sy,sz] + yy,zz=yy*math.cos(pitch)-zz*math.sin(pitch),yy*math.sin(pitch)+zz*math.cos(pitch) + return np.array([x+xx*math.cos(yaw)-yy*math.sin(yaw),y+xx*math.sin(yaw)+yy*math.cos(yaw),z+zz]) + def residual(a): + res=[] + for key,frames in v['observations'].items(): + for fr,uv in frames.items():res.extend(project(point(a,key),fr)-uv) + res.extend([(a[2]-1.35)*5,a[6]*12,a[7]*8]) + return res + fit=least_squares(residual,v['initial'],bounds=([-18,9,.9,.6,.8,-.6,-.1,-.3],[-7,23,1.9,2.1,1.9,.6,.1,.3]),loss='soft_l1',f_scale=3) + OUT[name]={'parameters':dict(zip(['x','y','scale_x','scale_y','scale_z','yaw','pitch','z'],fit.x.tolist())), + 'observations':v['observations'],'predictions':{key:{fr:project(point(fit.x,key),fr).tolist() for fr in frames} for key,frames in v['observations'].items()},'cost':fit.cost} +(ROOT/'assets/near_vehicle_landmarks.json').write_text(json.dumps(OUT,indent=2));print(json.dumps(OUT,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_opening_hatch.py b/astra_scene_test/Scene_04/scripts/fit_opening_hatch.py new file mode 100644 index 0000000000000000000000000000000000000000..c88df03679bd8acf1a6f3c12f9a4b44436b103b9 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_opening_hatch.py @@ -0,0 +1,32 @@ +"""Fit the cropped opening hatchback to three measured views, camera fixed. + +The rear wheel is partly obscured by wet glass. These manually placed features +are approximate, and are retained with their residuals for review. +""" +import json, math, pathlib, sys +import numpy as np +from scipy.optimize import least_squares +ROOT=pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import project +observations={ + 'rear_wheel':{1:[124,603],5:[89,610],9:[53,616]}, + 'roof_rear':{1:[104,542],5:[69,543],9:[33,545]}, + 'rear_bumper':{1:[145,601],5:[109,607],9:[75,615]}, + 'rear_glass_corner':{1:[131,560],5:[97,563],9:[63,568]}} +local={'rear_wheel':[.98,1.44,.44], 'roof_rear':[.684,1.10,1.70], + 'rear_bumper':[.86,2.15,.65], 'rear_glass_corner':[.80,1.87,1.30]} +def point(a,key): + x,y,sx,sy,sz,yaw=a + xx,yy,zz=np.array(local[key])*[sx,sy,sz] + return [x+xx*math.cos(yaw)-yy*math.sin(yaw),y+xx*math.sin(yaw)+yy*math.cos(yaw),zz-.066*sz] +def residual(a): + return np.r_[np.concatenate([project(point(a,key),fr)-uv for key,rows in observations.items() for fr,uv in rows.items()]), + (a[2]-1.45)*3,(a[3]-1.1)*2,a[5]*2] +fit=least_squares(residual,[-15,8,1.45,1.1,1.5,0],bounds=([-24,2,.8,.6,.8,-.8],[-9,14,2,1.9,2,.8]),loss='soft_l1',f_scale=3) +out={'parameters':dict(zip(['x','y','scale_x','scale_y','scale_z','yaw'],fit.x.tolist())), + 'tire_contact_z':-.066*float(fit.x[4]),'local_features':local,'observations':observations, + 'predictions':{key:{fr:project(point(fit.x,key),fr).tolist() for fr in rows} for key,rows in observations.items()}, + 'cost':float(fit.cost),'note':__doc__} +(ROOT/'assets/opening_hatch.json').write_text(json.dumps(out,indent=2)) +print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_signal_emitters.py b/astra_scene_test/Scene_04/scripts/fit_signal_emitters.py new file mode 100644 index 0000000000000000000000000000000000000000..8f64bfe7c0f8526c8bbc10679dd942c3c7828e61 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_signal_emitters.py @@ -0,0 +1,12 @@ +"""Fit the visible green light centers, rather than a housing corner.""" +from pathlib import Path +import json,sys,numpy as np +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import triangulate,project +tracks=json.loads((ROOT/'reference/source_signal_tracks.json').read_text());out={} +for side,key in [('left','left_near_signal'),('right','right_far_signal')]: + obs={int(fr):uv for fr,uv in tracks[side].items() if uv is not None};q=triangulate(obs) + errors=[float(np.linalg.norm(project(q,fr)-uv)) for fr,uv in obs.items()] + out[key]={'emitter_xyz':q.tolist(),'housing_anchor_xyz':(q+np.array([0,.19,0])).tolist(),'median_error_pixels':float(np.median(errors)),'p90_error_pixels':float(np.percentile(errors,90)),'observations':obs} +(ROOT/'assets/signal_emitters.json').write_text(json.dumps(out,indent=2)) +print(json.dumps({k:{a:b for a,b in v.items() if a!='observations'} for k,v in out.items()},indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_signs.py b/astra_scene_test/Scene_04/scripts/fit_signs.py new file mode 100644 index 0000000000000000000000000000000000000000..fc5b5734aad6de5663b5b669b40f0d78fb04fc53 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_signs.py @@ -0,0 +1,23 @@ +"""Fit the opening sign rectangles on their 3D mounting plane.""" +from pathlib import Path +import json,math,numpy as np +from scipy.optimize import least_squares +R=Path(__file__).resolve().parents[1];P=json.loads((R/'assets/config.json').read_text());M=json.loads((R/'assets/camera_motion.json').read_text()) +p,y=map(math.radians,[P['pitch'],P['yaw']]);f=M['focal_pixels'];pp=np.array(M['principal_point']) +B=np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +def project(q):q=B.T@(np.array(q)-[0,0,P['camera_height']]);return q[:2]/q[2]*f+pp +specs={'park':{'x':17.97,'target':[[1056,298],[1118,270],[1130,340],[1067,367]],'guess':[12,9,1.8,2.7]}, + 'park_vertical':{'x':15.5,'target':[[981,334],[996,330],[1010,394],[998,402]],'guess':[13,9,.6,3]}, + 'training':{'x':-28.25,'target':[[214,442],[294,463],[294,492],[211,470]],'guess':[30,7,7,2]}, + 'hair':{'x':-27.1,'target':[[303,466],[347,480],[348,501],[303,488]],'guess':[36,7,6,2]}} +out={} +for name,s in specs.items(): + def corners(a): + yy,z,w,h=a;sign=1 if s['x']>0 else -1 + return [(s['x'],yy+sign*w/2,z+h/2),(s['x'],yy-sign*w/2,z+h/2),(s['x'],yy-sign*w/2,z-h/2),(s['x'],yy+sign*w/2,z-h/2)] + def residual(a):return (np.array([project(c) for c in corners(a)])-s['target']).ravel() + fit=least_squares(residual,s['guess'],bounds=([2,1,.1,.1],[40,15,12,5])) + out[name]={'x':s['x'],'y':fit.x[0],'z':fit.x[1],'width':fit.x[2],'height':fit.x[3], + 'target_corners':s['target'],'projected_corners':[project(q).tolist() for q in corners(fit.x)],'rms_pixels':float(np.sqrt(np.mean(fit.fun**2)))} +(R/'assets/signs_fit.json').write_text(json.dumps(out,indent=2)) +for n,s in out.items():print(n,{k:round(v,3) for k,v in s.items() if isinstance(v,float)}) diff --git a/astra_scene_test/Scene_04/scripts/fit_sky_series.py b/astra_scene_test/Scene_04/scripts/fit_sky_series.py new file mode 100644 index 0000000000000000000000000000000000000000..2dd382bfce73814cb79ea73519e84a70d3f2ca41 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_sky_series.py @@ -0,0 +1,21 @@ +"""Calibrate a smooth sky appearance response from numeric mean-color measurements.""" +from pathlib import Path +import json,os,numpy as np +os.environ['OPENCV_IO_ENABLE_OPENEXR']='1' +import cv2 +ROOT=Path(__file__).resolve().parents[1];cv2.setNumThreads(1) +P=json.loads((ROOT/'assets/config.json').read_text()) +fit=json.loads((ROOT/'reference/sky_probe_layered_response_fit.json').read_text()) +anchors=[1,65,128];z=np.array([fit[str(f)]['zero_linear'] for f in anchors]);c=np.array([(np.array(fit[str(f)]['original_linear'])-fit[str(f)]['zero_linear'])/fit[str(f)]['old_control'] for f in anchors]) +def inverse_view(rgb): + v=rgb**P.get('gamma',1.0);return np.where(v<=.04045,v/12.92,((v+.055)/1.055)**2.4)/2**P['exposure'] +measure=[] +for fr in range(1,129): + im=cv2.imread(str(ROOT/f'reference/frames/{fr:04d}.png'))[30:210,550:700,::-1]/255;measure.append(im.mean((0,1))) +controls={};records=[] +for fr in [1,17,33,49,65,81,97,113,128]: + target=inverse_view(np.mean(measure[max(0,fr-3):min(128,fr+2)],axis=0)) + floor=np.array([np.interp(fr,anchors,z[:,j]) for j in range(3)]);coefficient=np.array([np.interp(fr,anchors,c[:,j]) for j in range(3)]) + gain=np.maximum(.01,(target-floor)/coefficient);controls[str(fr)]=gain.tolist();records.append({'frame':fr,'target_linear':target.tolist(),'control':gain.tolist()}) +(ROOT/'reference/sky_series_fit.json').write_text(json.dumps({'controls':controls,'measurements_srgb':np.array(measure).tolist(),'fit':records,'note':'Camera-visible background response only. Lighting world remains constant. Coefficients inferred from separated layer-scatter/background crops.'},indent=2)) +print(json.dumps(controls,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_suv_landmarks.py b/astra_scene_test/Scene_04/scripts/fit_suv_landmarks.py new file mode 100644 index 0000000000000000000000000000000000000000..7af0a22a1ad860bbae30f8f29abe9e9722d7f520 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_suv_landmarks.py @@ -0,0 +1,23 @@ +"""Fit SUV wheel and roof landmarks, keeping the camera trajectory fixed.""" +from pathlib import Path +import json,sys,math,numpy as np +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from camera_math import project,triangulate +obs={'front_wheel':{1:[469,554],33:[378,567],65:[132,623]},'rear_wheel':{1:[488,545],33:[425,555],65:[261,594]}, + 'roof_front':{1:[465,516],33:[379,508],65:[172,512]},'roof_rear':{1:[483,518],33:[410,510],65:[241,516]}} +coords={'front_wheel':[1.035,-1.47,.44],'rear_wheel':[1.035,1.44,.44],'roof_front':[.726,-.76,1.95],'roof_rear':[.726,.98,1.95]} +def point(a,key): + x,y,sx,sy,sz,yaw,pitch,z=a;xx,yy,zz=np.array(coords[key])*[sx,sy,sz] + yy,zz=yy*math.cos(pitch)-zz*math.sin(pitch),yy*math.sin(pitch)+zz*math.cos(pitch) + return np.array([x+xx*math.cos(yaw)-yy*math.sin(yaw),y+xx*math.sin(yaw)+yy*math.cos(yaw),z+zz]) +def residual(a): + res=[] + for key,frames in obs.items(): + q=point(a,key) + for fr,uv in frames.items():res.extend((project(q,fr)-uv)*(.65 if fr==1 else 1)) + res.extend([(a[2]-1.45)*3,a[6]*10,a[7]*5]) + return res +fit=least_squares(residual,[-10.8,23.7,1.57,1.35,1.456,0,0,0],bounds=([-15,19,1,.65,1,-.7,-.15,-.3],[-7,32,2,2.1,1.9,.7,.15,.4]),loss='soft_l1',f_scale=3) +out={'parameters':dict(zip(['x','y','scale_x','scale_y','scale_z','yaw','pitch','z'],fit.x.tolist())),'observations':obs,'predictions':{key:{fr:project(point(fit.x,key),fr).tolist() for fr in frames} for key,frames in obs.items()},'cost':fit.cost,'triangulated':{key:triangulate(frames).tolist() for key,frames in obs.items()}} +(ROOT/'assets/white_suv_landmarks.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_tower.py b/astra_scene_test/Scene_04/scripts/fit_tower.py new file mode 100644 index 0000000000000000000000000000000000000000..e78e95b3afe3b70f8ba495fa7dffa41e0450ba3e --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_tower.py @@ -0,0 +1,16 @@ +"""Fit a level tower roof and facade plane using its triangulated windows.""" +from pathlib import Path +import json,math,numpy as np +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1] +P=json.loads((ROOT/'assets/config.json').read_text());M=json.loads((ROOT/'assets/camera_motion.json').read_text());A=json.loads((ROOT/'assets/landmarks.json').read_text()) +pts=np.array([A[n]['xyz'] for n in ['tower_window_left','tower_window_right','tower_window_above']]) +slope,intercept=np.linalg.lstsq(np.c_[pts[:,0],np.ones(3)],pts[:,1],rcond=None)[0] +p,y=map(math.radians,[P['pitch'],P['yaw']]);B=np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +f=M['focal_pixels'];pp=np.array(M['principal_point']);targets=np.array([[307,115],[478,85]]) +def project(q): + q=B.T@(np.array(q)-[0,0,P['camera_height']]);return q[:2]/q[2]*f+pp +def corners(a):return [(x,slope*x+intercept,a[2]) for x in a[:2]] +fit=least_squares(lambda a:(np.array([project(q) for q in corners(a)])-targets).ravel(),[-76,-31,98]) +out={'left':corners(fit.x)[0],'right':corners(fit.x)[1],'height':fit.x[2],'plane_slope':slope,'plane_intercept':intercept,'target_roof_pixels':targets.tolist(),'fitted_roof_pixels':[project(q).tolist() for q in corners(fit.x)]} +(ROOT/'assets/tower_fit.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/fit_vehicles.py b/astra_scene_test/Scene_04/scripts/fit_vehicles.py new file mode 100644 index 0000000000000000000000000000000000000000..94961e9951d0079af0939a8e8d1d37a8ee462b19 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/fit_vehicles.py @@ -0,0 +1,27 @@ +"""Fit a stationary SUV envelope across several source frames. + +Dimensions are scene units. The camera remains fixed to the independently +measured trajectory; this fit cannot absorb motion by moving the parked SUV. +""" +from pathlib import Path +import json,math,numpy as np +from scipy.spatial.transform import Rotation +from scipy.optimize import least_squares +ROOT=Path(__file__).resolve().parents[1];P=json.loads((ROOT/'assets/config.json').read_text());M=json.loads((ROOT/'assets/camera_motion.json').read_text()) +p,y=map(math.radians,[P['pitch'],P['yaw']]);B=np.array([[math.cos(y),math.sin(y)*math.sin(p),math.sin(y)*math.cos(p)],[-math.sin(y),math.cos(y)*math.sin(p),math.cos(y)*math.cos(p)],[0,-math.cos(p),math.sin(p)]]) +f=M['focal_pixels'];pp=np.array(M['principal_point']);d=np.r_[(np.array(M['epipole'])-pp)/f,1];d*=P['travel']/np.linalg.norm(d) +observations={1:[407,514,495,566],33:[277,505,437,585],65:[0,505,280,647]} +def envelope(a,fr): + x,y,w,length,h,angle=a;ca=math.cos(angle);sa=math.sin(angle);pts=[] + for xx in [-w/2,w/2]: + for yy in [-length/2,length/2]: + for z in [.16*h,.66*h]:pts.append([xx*ca-yy*sa+x,xx*sa+yy*ca+y,z]) + for yy in [-length*.16,length*.205]:pts.append([xx*.8*ca-yy*sa+x,xx*.8*sa+yy*ca+y,h]) + for yy in [-length*.306,length*.30]:pts.append([xx*ca-yy*sa+x,xx*sa+yy*ca+y,.02]) + t=np.interp(fr,M['frames'],M['displacement_fraction']);r=np.array([np.interp(fr,M['frames'],np.array(M['rotation_vectors'])[:,i]) for i in range(3)]) + R=Rotation.from_rotvec(r).as_matrix();C=B@d*t+[0,0,P['camera_height']] + q=(R@B.T@(np.array(pts)-C).T).T;uv=q[:,:2]/q[:,2:]*f+pp + lo=np.maximum(uv.min(0),[0,0]);hi=np.minimum(uv.max(0),[1280,720]);return np.r_[lo,hi] +fit=least_squares(lambda a:np.r_[np.concatenate([envelope(a,fr)-obs for fr,obs in observations.items()]),a[-1]*20],[-11.1,25.4,2.87,7.2,2.93,0],bounds=([-20,19,1.8,4.2,2.0,-.4],[-7,35,5,11,4.2,.4]),loss='soft_l1',f_scale=3) +out={'parameters':dict(zip(['x','y','width','length','height','angle'],fit.x.tolist())),'observations':observations,'predictions':{fr:envelope(fit.x,fr).tolist() for fr in observations},'residuals':fit.fun.tolist()} +(ROOT/'reference/white_suv_envelope_trial.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/gpu_health_probe.py b/astra_scene_test/Scene_04/scripts/gpu_health_probe.py new file mode 100644 index 0000000000000000000000000000000000000000..9e8950ee3f355913957f3ec0e70fd853202e77bd --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/gpu_health_probe.py @@ -0,0 +1,12 @@ +"""Tiny diagnostic render to distinguish a device-wide stall from scene cost.""" +import bpy,sys,pathlib,time +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from render_device import configure +S=bpy.context.scene;S.render.engine='CYCLES';print('DEVICE',configure(S),flush=True) +prefs=bpy.context.preferences.addons['cycles'].preferences +for p in prefs.bl_rna.properties: + if 'metal' in p.identifier:print('METAL_PREF',p.identifier,getattr(prefs,p.identifier),flush=True) +if hasattr(prefs,'metalrt'):prefs.metalrt='OFF' +S.render.resolution_x=64;S.render.resolution_y=64;S.render.resolution_percentage=100;S.cycles.samples=1;S.cycles.use_denoising=False +S.render.filepath=str(ROOT/'renders/gpu_health.png');start=time.monotonic();bpy.ops.render.render(write_still=True) +print('GPU_HEALTH_COMPLETE',time.monotonic()-start,flush=True) diff --git a/astra_scene_test/Scene_04/scripts/inspect_occlusion.py b/astra_scene_test/Scene_04/scripts/inspect_occlusion.py new file mode 100644 index 0000000000000000000000000000000000000000..bdc76f029f70b143770b0640fbd14302b16a007b --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/inspect_occlusion.py @@ -0,0 +1,15 @@ +"""Read-only camera-ray audit for the corner pub and source tree landmarks.""" +import bpy,json,pathlib +from mathutils import Vector +from bpy_extras.object_utils import world_to_camera_view +R=pathlib.Path(__file__).resolve().parents[1];s=bpy.context.scene;s.frame_set(128);bpy.context.view_layer.update();deps=bpy.context.evaluated_depsgraph_get();cam=s.camera +out=[] +for target in [(31.54,49.73,.23),(31.54,49.73,8),(26.6,51.84,3.55),(29.8,51.84,3.55),(33,51.84,3.55),(29.8,56.5,4)]: + q=Vector(target);screen=world_to_camera_view(s,cam,q);origin=cam.matrix_world.translation.copy();direction=(q-origin).normalized();hits=[] + for i in range(10): + ok,loc,normal,idx,ob,matrix=s.ray_cast(deps,origin,direction,distance=200) + if not ok:break + hits.append({'object':ob.name,'point':list(loc),'material':ob.data.materials[ob.data.polygons[idx].material_index].name if ob.type=='MESH' and idx0 + delta=n.new('ShaderNodeMath');delta.operation='SUBTRACT';l.new(maximum,delta.inputs[0]);delta.inputs[1].default_value=knee + positive=n.new('ShaderNodeMath');positive.operation='MAXIMUM';l.new(delta.outputs[0],positive.inputs[0]);positive.inputs[1].default_value=0 + denom=n.new('ShaderNodeMath');denom.operation='ADD';l.new(positive.outputs[0],denom.inputs[0]);denom.inputs[1].default_value=span + fraction=n.new('ShaderNodeMath');fraction.operation='DIVIDE';l.new(positive.outputs[0],fraction.inputs[0]);l.new(denom.outputs[0],fraction.inputs[1]) + scaled=n.new('ShaderNodeMath');scaled.operation='MULTIPLY';l.new(fraction.outputs[0],scaled.inputs[0]);scaled.inputs[1].default_value=span + mapped=n.new('ShaderNodeMath');mapped.operation='ADD';l.new(scaled.outputs[0],mapped.inputs[0]);mapped.inputs[1].default_value=knee + safe=n.new('ShaderNodeMath');safe.operation='MAXIMUM';l.new(maximum,safe.inputs[0]);safe.inputs[1].default_value=1e-6 + ratio=n.new('ShaderNodeMath');ratio.operation='DIVIDE';l.new(mapped.outputs[0],ratio.inputs[0]);l.new(safe.outputs[0],ratio.inputs[1]) + limit=n.new('ShaderNodeMath');limit.operation='MINIMUM';l.new(ratio.outputs[0],limit.inputs[0]);limit.inputs[1].default_value=1 + rolled=n.new('ShaderNodeVectorMath');rolled.operation='SCALE';rolled.label='Camera highlight shoulder preserving LED chroma';l.new(combined,rolled.inputs[0]);l.new(limit.outputs[0],rolled.inputs['Scale']);combined=rolled.outputs[0] + soft=n.new('CompositorNodeBlur');soft.location=(580,-80);soft.inputs['Size'].default_value=(parameters.get('optical_softness',1.6),)*2;soft.inputs['Type'].default_value='Gaussian';l.new(combined,soft.inputs['Image']) + final=mix(combined,soft.outputs['Image'],'MIX',parameters.get('softness_mix',.55),(790,220),'Subpixel optical softness') + mask=image_node('windshield_lower_defocus.png','Near windshield seal focus falloff',(680,-450),True) + lower=n.new('CompositorNodeBlur');lower.inputs['Size'].default_value=(6.0,6.0);lower.inputs['Type'].default_value='Gaussian';l.new(final,lower.inputs['Image']) + merged=mix(final,lower.outputs['Image'],'MIX',1,(980,220),'Near seal is outside the street focus plane') + l.new(mask.outputs['Image'],merged.node.inputs[0]) + if parameters.get('camera_balance'): + response=n.new('ShaderNodeVectorMath');response.operation='MULTIPLY';response.label='Measured gradual camera white-balance response';l.new(merged,response.inputs[0]) + for fr,rgb in parameters['camera_balance'].items(): + response.inputs[1].default_value=rgb;response.inputs[1].keyframe_insert('default_value',frame=int(fr)) + merged=response.outputs[0] + scene.frame_set(scene.frame_current) + if parameters.get('sensor_noise',0)>0: + coordinates=image_node('sensor_coordinates.png','Numeric sensor pixel coordinates',(650,-850),True) + ns=n.new('ShaderNodeVectorMath');ns.operation='SCALE';ns.inputs['Scale'].default_value=1280;l.new(coordinates.outputs['Image'],ns.inputs[0]) + grain=n.new('ShaderNodeTexWhiteNoise');grain.noise_dimensions='4D';grain.label='Deterministic frame-indexed sensor noise, separate from fixed rain';l.new(ns.outputs[0],grain.inputs['Vector']) + driver=grain.inputs['W'].driver_add('default_value');driver.driver.expression='404 + frame * 13.37' + center=n.new('ShaderNodeVectorMath');center.operation='SUBTRACT';center.inputs[1].default_value=(.5,.5,.5);l.new(grain.outputs['Color'],center.inputs[0]) + color=n.new('ShaderNodeVectorMath');color.operation='MULTIPLY';color.inputs[1].default_value=parameters.get('sensor_noise_color',(1.0,.85,1.1));l.new(center.outputs[0],color.inputs[0]) + grain_sum=color.outputs[0] + correlated=color.outputs[0] + if parameters.get('sensor_correlated'): + previous=n.new('ShaderNodeTexWhiteNoise');previous.noise_dimensions='4D';l.new(ns.outputs[0],previous.inputs['Vector']) + previous.inputs['W'].driver_add('default_value').driver.expression='404 + (frame - 1) * 13.37' + prevcenter=n.new('ShaderNodeVectorMath');prevcenter.operation='SUBTRACT';prevcenter.inputs[1].default_value=(.5,.5,.5);l.new(previous.outputs['Color'],prevcenter.inputs[0]) + prevcolor=n.new('ShaderNodeVectorMath');prevcolor.operation='MULTIPLY';prevcolor.inputs[1].default_value=tuple(.22*v for v in parameters.get('sensor_noise_color',(1.0,.85,1.1)));l.new(prevcenter.outputs[0],prevcolor.inputs[0]) + correlated=mix(correlated,prevcolor.outputs[0],'ADD',1,label='Measured modest interframe sensor correlation') + # The source's camera noise has measurable multi-pixel chroma patches. + # Correlate the same deterministic sensor field; the water map stays fixed. + for radius,gain in parameters.get('sensor_correlated',[]): + blurred=n.new('CompositorNodeBlur');blurred.inputs['Size'].default_value=(radius,radius);blurred.inputs['Type'].default_value='Gaussian' + l.new(correlated,blurred.inputs['Image']) + boost=n.new('ShaderNodeVectorMath');boost.operation='SCALE';boost.inputs['Scale'].default_value=gain;l.new(blurred.outputs['Image'],boost.inputs[0]) + grain_sum=mix(grain_sum,boost.outputs[0],'ADD',1,label='Spatially correlated low-light chroma noise') + lum=n.new('ShaderNodeVectorMath');lum.operation='DOT_PRODUCT';lum.inputs[1].default_value=(.2126,.7152,.0722);l.new(merged,lum.inputs[0]) + floor=n.new('ShaderNodeMath');floor.operation='MAXIMUM';floor.inputs[1].default_value=.004;l.new(lum.outputs['Value'],floor.inputs[0]) + shot=n.new('ShaderNodeMath');shot.operation='SQRT';l.new(floor.outputs[0],shot.inputs[0]) + amount=n.new('ShaderNodeMath');amount.operation='MULTIPLY';amount.inputs[1].default_value=parameters['sensor_noise'];l.new(shot.outputs[0],amount.inputs[0]) + amplitude=n.new('ShaderNodeVectorMath');amplitude.operation='SCALE';l.new(grain_sum,amplitude.inputs[0]);l.new(amount.outputs[0],amplitude.inputs['Scale']) + merged=mix(merged,amplitude.outputs[0],'ADD',1,(1130,220),'Low-light photon/read noise, calibrated from uniform source patches') + cropped=install_crop(scene,n,l,merged) + out=n.new('NodeGroupOutput');out.location=(1400,220);l.new(cropped,out.inputs['Image']) + scene['optics_note']='Fixed procedural water map; rendered-light-driven glare kernel; no source-video images or textures.' + return nt +if __name__=='__main__': + p=json.loads((ROOT/'assets/config.json').read_text());install_optics(bpy.context.scene,p) + bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'scene_04.blend')) diff --git a/astra_scene_test/Scene_04/scripts/install_signal_response.py b/astra_scene_test/Scene_04/scripts/install_signal_response.py new file mode 100644 index 0000000000000000000000000000000000000000..572eabe9ce6bc3ff56e5bc29ddde4c0c4d20e03c --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/install_signal_response.py @@ -0,0 +1,45 @@ +"""Smooth world-direction LED emission, with no time-dependent material keys. +Blender Geometry Incoming is a world-space direction toward the viewing point: +https://docs.blender.org/manual/en/5.2/render/shader_nodes/input/geometry.html +""" +import bpy,pathlib,json +from mathutils import Vector +ROOT=pathlib.Path(__file__).resolve().parents[1] +def install(scene): + config=json.loads((ROOT/'assets/signal_response.json').read_text()) + anchors=json.loads((ROOT/'assets/signal_emitters.json').read_text()) + bpy.context.view_layer.update() + lenses=[o for o in scene.objects if o.type=='MESH' and o.name.startswith('Green signal lens')] + glows=[o for o in scene.objects if o.type=='LIGHT' and o.name.startswith('Green signal local glow')] + records=[] + for key in ['left_near_signal','right_far_signal']: + p=config[key];target=Vector(anchors[key]['emitter_xyz']) + lens=min(lenses,key=lambda o:(o.matrix_world.translation-target).length) + assert (lens.matrix_world.translation-target).length<.01 + name='Measured angular LED response '+key + mat=bpy.data.materials.get(name) or bpy.data.materials.new(name) + mat.use_nodes=True;mat.diffuse_color=(*p['color'],1) + nt=mat.node_tree;nt.nodes.clear();n=nt.nodes;l=nt.links + surface=n.new('ShaderNodeBsdfPrincipled');surface.inputs['Base Color'].default_value=(*p['color'],1) + surface.inputs['Roughness'].default_value=.32;surface.inputs['Emission Color'].default_value=(*p['color'],1) + geometry=n.new('ShaderNodeNewGeometry') + dot=n.new('ShaderNodeVectorMath');dot.operation='DOT_PRODUCT';dot.inputs[1].default_value=(0,-1,0);l.new(geometry.outputs['Incoming'],dot.inputs[0]) + cosine=n.new('ShaderNodeMath');cosine.operation='ABSOLUTE';l.new(dot.outputs['Value'],cosine.inputs[0]) + deficit=n.new('ShaderNodeMath');deficit.operation='SUBTRACT';deficit.inputs[0].default_value=1;l.new(cosine.outputs[0],deficit.inputs[1]) + angle=n.new('ShaderNodeMath');angle.operation='MULTIPLY';angle.inputs[1].default_value=-p['sharpness'];l.new(deficit.outputs[0],angle.inputs[0]) + exp=n.new('ShaderNodeMath');exp.operation='EXPONENT';l.new(angle.outputs[0],exp.inputs[0]) + amplitude=n.new('ShaderNodeMath');amplitude.operation='MULTIPLY';amplitude.inputs[1].default_value=p['peak'];l.new(exp.outputs[0],amplitude.inputs[0]) + strength=n.new('ShaderNodeMath');strength.operation='ADD';strength.inputs[1].default_value=p['floor'];l.new(amplitude.outputs[0],strength.inputs[0]) + l.new(strength.outputs[0],surface.inputs['Emission Strength']) + output=n.new('ShaderNodeOutputMaterial');l.new(surface.outputs[0],output.inputs['Surface']) + if lens.data.users>1:lens.data=lens.data.copy() + lens.data.materials.clear();lens.data.materials.append(mat) + glow=min(glows,key=lambda o:(o.matrix_world.translation-target).length) + assert (glow.matrix_world.translation-target).length<.5;glow.data.energy=p['local_glow_watts'] + records.append({'anchor':key,'object':lens.name,'glow':glow.name,'parameters':p}) + scene['signal_response']=json.dumps(config) + print('ANGULAR_SIGNAL_RESPONSE_INSTALLED',json.dumps(records),flush=True) + return records +if __name__=='__main__': + install(bpy.context.scene) + bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'scene_04.blend')) diff --git a/astra_scene_test/Scene_04/scripts/layout_overlay.py b/astra_scene_test/Scene_04/scripts/layout_overlay.py new file mode 100644 index 0000000000000000000000000000000000000000..f7e9997c1f54eec545b91def3ec27edf3743a4b6 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/layout_overlay.py @@ -0,0 +1,27 @@ +"""Analytical wire overlays for quick geometry diagnosis before expensive renders.""" +import json,pathlib,math,numpy as np +from PIL import Image,ImageDraw +ROOT=pathlib.Path(__file__).resolve().parents[1];P=json.loads((ROOT/'assets/config.json').read_text()) +f=640/math.tan(math.radians(P['fov']/2));theta=math.radians(P['pitch']);yaw=math.radians(P['yaw']) +forward=np.array([math.sin(yaw)*math.cos(theta),math.cos(yaw)*math.cos(theta),math.sin(theta)]) +right=np.array([math.cos(yaw),-math.sin(yaw),0]);up=np.cross(right,forward) +def project(p,fr): + t=(fr-1)/127;cam=np.array([0,P['travel']*(.91*t+.09*t*t),P['camera_height']]);q=np.array(p)-cam;depth=q@forward + if depth<.2:return None + return (640+f*(q@right)/depth,360-f*(q@up)/depth) +def wire(d,name,bounds,color,fr): + a,b=bounds;points=[(x,y,z) for x in [a[0],b[0]] for y in [a[1],b[1]] for z in [a[2],b[2]]];p=[project(x,fr) for x in points] + for i in range(8): + for j in range(i+1,8): + if bin(i^j).count('1')==1 and p[i] and p[j]:d.line([p[i],p[j]],fill=color,width=2) + if p[7]:d.text(p[7],name,fill=color) +for fr in [1,65,128]: + im=Image.open(ROOT/f'reference/frames/{fr:04d}.png');d=ImageDraw.Draw(im) + wire(d,'Left brick',((-27.1,-13,0),(-14.1,21,23)),(240,90,50),fr) + wire(d,'Left tower',((-39.3,23,0),(-15.3,46,61)),(80,255,255),fr) + wire(d,'Right start',((14.1,-10,0),(26.1,9,24)),(255,180,100),fr) + wire(d,'Central tower',((14.1,98,0),(32.1,116,55)),(190,100,255),fr) + for y in [P['intersection'],P['intersection']+P['intersection_width']]: + a=project((-30,y,0),fr);b=project((30,y,0),fr) + if a and b:d.line([a,b],fill='yellow',width=2) + im.save(ROOT/f'reference/layout_overlay_{fr:04d}.jpg',quality=94) diff --git a/astra_scene_test/Scene_04/scripts/list_patch_lights.py b/astra_scene_test/Scene_04/scripts/list_patch_lights.py new file mode 100644 index 0000000000000000000000000000000000000000..a276f936c8e4987951770c67520dd0968acc8591 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/list_patch_lights.py @@ -0,0 +1,8 @@ +import bpy,pathlib,json +from mathutils import Vector +S=bpy.context.scene;S.frame_set(128);bpy.context.view_layer.update();q=Vector((11.35,39.30,.06));rows=[] +for o in S.objects: + if o.type!='LIGHT':continue + d=(q-o.matrix_world.translation);length=d.length;direction=o.matrix_world.to_quaternion()@Vector((0,0,-1));angle=d.normalized().angle(direction) + rows.append({'name':o.name,'type':o.data.type,'position':list(o.matrix_world.translation),'distance':length,'power':o.data.energy,'angle':angle,'spot_size':o.data.spot_size if o.data.type=='SPOT' else None,'intensity_proxy':o.data.energy/max(length**2,.001)}) +rows.sort(key=lambda x:-x['intensity_proxy']);root=pathlib.Path(__file__).resolve().parents[1];(root/'logs/patch_light_candidates.json').write_text(json.dumps(rows,indent=2));print(json.dumps(rows[:15],indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/measure_appearance.py b/astra_scene_test/Scene_04/scripts/measure_appearance.py new file mode 100644 index 0000000000000000000000000000000000000000..4d75f93b2bdeb03f3d5c8fa7348e660c9f97893b --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/measure_appearance.py @@ -0,0 +1,42 @@ +"""Independent photometric comparison at identical decoded frame indices. + +These are diagnostic measurements, not a claim of whole-image fidelity. +No source image is ever written into a rendering asset. +""" +from pathlib import Path +import cv2,numpy as np,json,sys +cv2.setNumThreads(1) +ROOT=Path(__file__).resolve().parents[1];label=sys.argv[1] +regions={'sky':(545,55,800,245),'road':(450,585,780,660),'right_pavement':(1050,570,1220,615)} +lights={'left':{1:(500,441),33:(477,421),65:(444,407),97:(393,373),128:(300,325)}, + 'right':{1:(727,395),33:(752,355),65:(798,307),97:(886,185)}} +def analyze(im,frame): + result={'regions':{},'lights':{}} + lum=np.sum(im*np.array([.2126,.7152,.0722]),axis=2) + assert np.isfinite(lum).all() + for name,(x0,y0,x1,y1) in regions.items(): + a=im[y0:y1,x0:x1];g=lum[y0:y1,x0:x1] + result['regions'][name]={'mean_rgb':a.mean((0,1)).tolist(),'luma_std':float(g.std())} + yy,xx=np.mgrid[:720,:1280] + for name,obs in lights.items(): + if frame not in obs:continue + x,y=obs[frame];r=np.hypot(xx-x,yy-y);region=(r<100) + background=np.median(lum[(r>105)&(r<135)]) + energy=np.clip(lum-background-10,0,None)*region + total=energy.sum();radii=np.arange(5,101,5) + cumulative=[float(energy[r1 else ['renders/intersection_refined','renders/appearance_refined']): + for fr in [1,65,128]: + p=ROOT/label/f'{fr:04d}.png' + if not p.exists():continue + im=cv2.imread(str(p)).astype(np.float32)[:,:,::-1] + for name,(x0,y0,x1,y1) in regions.items(): + patch=im[y0:y1,x0:x1];high=patch-cv2.GaussianBlur(patch,(0,0),1.3) + rows.append({'label':label,'frame':fr,'region':name,'mean_rgb':patch.mean((0,1)).tolist(), + 'highpass_std_rgb':high.std((0,1)).tolist(),'highpass_mad_rgb':np.median(np.abs(high-np.median(high,(0,1))),axis=(0,1)).tolist()}) +(ROOT/'logs/noise_measurements.json').write_text(json.dumps(rows,indent=2)) +for r in rows:print(r) diff --git a/astra_scene_test/Scene_04/scripts/measure_signal_energy.py b/astra_scene_test/Scene_04/scripts/measure_signal_energy.py new file mode 100644 index 0000000000000000000000000000000000000000..a25fd6a264f85b6711c01ec2a5f864c9fed0d79e --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/measure_signal_energy.py @@ -0,0 +1,25 @@ +"""Measure localized green-signal appearance at independently tracked positions. +Diagnostics only: background foliage, refraction and saturation affect these +values, so they are not radiometric measurements or whole-image fidelity scores. +""" +from pathlib import Path +import sys,json,numpy as np +from PIL import Image +ROOT=Path(__file__).resolve().parents[1];label=sys.argv[1] +tracks=json.loads((ROOT/'reference/source_signal_tracks.json').read_text()) +yy,xx=np.mgrid[:720,:1280];rows=[] +for path in sorted((ROOT/'renders'/label).glob('[0-9][0-9][0-9][0-9].png')): + fr=int(path.stem) + images={k:np.asarray(Image.open(p).convert('RGB').resize((1280,720)),dtype=float) for k,p in [('source',ROOT/f'reference/frames/{fr:04d}.png'),('render',path)]} + for side in ['left','right']: + if str(fr) not in tracks[side]:continue + x,y=tracks[side][str(fr)];r=np.hypot(xx-x,yy-y);row={'frame':fr,'side':side} + for key,im in images.items(): + v=np.maximum(0,im[:,:,1]-(im[:,:,0]+im[:,:,2])*.5-12)*(r<32) + row[key]={'chroma_energy':float(v.sum()),'radius_pixels':float(np.sqrt((v*r*r).sum()/max(v.sum(),1e-5))), + 'inner_median_rgb':np.median(im[r<8],axis=0).tolist()} + rows.append(row) +(ROOT/f'logs/signal_energy_{label}.json').write_text(json.dumps(rows,indent=2)) +for row in rows: + if row['frame'] in [1,65,128]:print(json.dumps(row)) +print('SIGNAL_ENERGY_MEASURED',label,len(rows)) diff --git a/astra_scene_test/Scene_04/scripts/optical_assets.py b/astra_scene_test/Scene_04/scripts/optical_assets.py new file mode 100644 index 0000000000000000000000000000000000000000..af24c770855d90ec608897b7fc3911a2419d3f39 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/optical_assets.py @@ -0,0 +1,66 @@ +"""Deterministic windshield normal field and a light-spread kernel. + +These are synthetic optical functions, never sampled from the source video. +They remain fixed across frames; rendered world lights supply temporal variation. +""" +from pathlib import Path +import numpy as np,json +from PIL import Image +ROOT=Path(__file__).resolve().parents[1];rng=np.random.default_rng(404) +W,H=1280,720;factor=json.loads((ROOT/'assets/config.json').read_text()).get('optical_overscan',1) +px=round(W*(factor-1)/2);py=round(H*(factor-1)/2) +y,x=np.mgrid[-py:H+py,-px:W+px];height=np.zeros(y.shape,np.float32) +for i in range(240): + cx=rng.uniform(-30,W+30);cy=rng.uniform(-20,H+20) + a=rng.uniform(1.4,5.2);b=a*rng.uniform(1.1,2.5) + if i<25:a*=rng.uniform(2,4);b*=rng.uniform(2,4) + d=((x-cx)/a)**2+((y-cy)/b)**2 + height+=np.maximum(0,1-d)**1.7*rng.uniform(.35,1) +for cx,cy,a,b,ang,amp in [(53,120,95,23,-.9,.20),(704,274,76,30,.35,.25), + (998,222,21,86,.2,.13),(412,473,17,126,-.25,.2), + (807,376,64,88,.45,.13),(1173,62,38,95,-.8,.28), + (635,582,72,24,.2,.09)]: + u=(x-cx)*np.cos(ang)+(y-cy)*np.sin(ang);v=-(x-cx)*np.sin(ang)+(y-cy)*np.cos(ang) + d=(u/a)**2+(v/b)**2;height+=np.maximum(0,1-d)**1.8*amp +# Wind-driven water trails are continuous functions, not frame-random noise. +for j in range(55): + cx=rng.uniform(0,W);cy=rng.uniform(0,H);length=rng.uniform(15,90);wid=rng.uniform(.6,1.6) + center=cx+.07*(y-cy)+2*np.sin((y-cy)/35) + h=np.exp(-((x-center)/wid)**2)*np.clip(1-abs((y-cy)/length),0,1)**2 + height+=h*.05 +dy,dx=np.gradient(height) +normal=np.zeros((*height.shape,4),np.float32);normal[:,:,0]=.5+dx*1.7;normal[:,:,1]=.5-dy*1.7;normal[:,:,2]=.5;normal[:,:,3]=1 +Image.fromarray((np.clip(normal,0,1)*255).astype(np.uint8),'RGBA').save(ROOT/'assets/windshield_displacement.png') +Image.fromarray((np.clip(height,0,1)*255).astype(np.uint8)).save(ROOT/'assets/windshield_water_height.png') +zones=np.zeros(height.shape,np.float32)+.05 +for cx,cy,sx,sy,amp in [(395,350,150,150,.9),(775,280,180,155,.85),(1180,110,165,190,1.0),(60,80,140,160,.25)]: + zones+=amp*np.exp(-((x-cx)/sx)**2-((y-cy)/sy)**2) +zones*=np.clip((690-y)/140,0,1) +Image.fromarray((np.clip(zones,0,1)*255).astype(np.uint8)).save(ROOT/'assets/windshield_glare_zones.png') +left_split=1/(1+np.exp((x-640)/55)) +Image.fromarray((np.clip(zones*left_split,0,1)*255).astype(np.uint8)).save(ROOT/'assets/windshield_glare_left.png') +Image.fromarray((np.clip(zones*(1-left_split),0,1)*255).astype(np.uint8)).save(ROOT/'assets/windshield_glare_right.png') +# Asymmetric, low contrast droplet-caustic point spread kernel. +n=193;v,u=np.mgrid[-96:97,-96:97];r=np.sqrt(u*u+v*v) +k=.60*np.exp(-(r/24)**2)+.25*np.exp(-(r/58)**2) +for cx,cy,a,b,ang,amp in [(-20,16,29,41,.5,.045),(24,-18,28,47,-.8,.055),(6,33,26,16,.2,.025)]: + xx=(u-cx)*np.cos(ang)+(v-cy)*np.sin(ang);yy=-(u-cx)*np.sin(ang)+(v-cy)*np.cos(ang) + rr=np.sqrt((xx/a)**2+(yy/b)**2) + k+=amp*np.exp(-((rr-1)/.11)**2) +for angle,amp in [(.42,.24),(.45,.16),(.49,.10)]: + along=u*np.cos(angle)+v*np.sin(angle);across=-u*np.sin(angle)+v*np.cos(angle) + k+=amp*np.exp(-(across/1.5)**2)*np.exp(-(along/58)**4) +k*=np.clip((97-r)/15,0,1) +rgb=np.stack([k*.98,k,k*.93],axis=2);rgb/=rgb.max() +Image.fromarray((rgb*255).astype(np.uint8),'RGB').save(ROOT/'assets/wet_glass_psf.png') +Image.fromarray((rgb[::-1]*255).astype(np.uint8),'RGB').save(ROOT/'assets/wet_glass_psf_left.png') +# Pillow converts RGB16 PNGs to RGB8 on load. That quantizes this numeric grid +# into multi-pixel blocks and greatly amplifies the correlated sensor noise. +# Preserve all 16 bits when extending the previously calibrated coordinate map. +import cv2 +cv2.setNumThreads(1) +coordinates=cv2.imread(str(ROOT/'assets/sensor_coordinates_base.png'),cv2.IMREAD_UNCHANGED) +assert coordinates.shape==(720,1280,3) and coordinates.dtype==np.uint16 +extended=np.pad(coordinates,((py,py),(px,px),(0,0)),mode='edge') +assert cv2.imwrite(str(ROOT/'assets/sensor_coordinates.png'),extended) +print('OPTICAL_ASSETS_COMPLETE',height.shape) diff --git a/astra_scene_test/Scene_04/scripts/optics_variants.py b/astra_scene_test/Scene_04/scripts/optics_variants.py new file mode 100644 index 0000000000000000000000000000000000000000..ae72905b281c8f560cd5eb03f5853795d7bebd8c --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/optics_variants.py @@ -0,0 +1,57 @@ +"""Recompute lens/camera response from raw 3D scene renders, with no source input.""" +import bpy,pathlib,sys,json,re,time,hashlib +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from install_optics import install_optics +args=sys.argv[sys.argv.index('--')+1:] if '--' in sys.argv else [] +source=ROOT/(args[0] if args else 'renders/optics_probe/linear');label=args[1] if len(args)>1 else 'response' +S=bpy.context.scene +for o in list(bpy.data.objects): + if o.type!='CAMERA':bpy.data.objects.remove(o,do_unlink=True) +if not S.camera: + bpy.ops.object.camera_add();S.camera=bpy.context.object +S.render.engine='CYCLES';S.cycles.device='CPU';S.cycles.samples=1;S.cycles.use_denoising=False +S.render.threads_mode='FIXED';S.render.threads=2;S.render.use_motion_blur=False +S.render.resolution_x=1280;S.render.resolution_y=720;S.render.resolution_percentage=100 +S.render.image_settings.file_format='PNG';S.render.image_settings.color_mode='RGB' +if hasattr(S.render,'compositor_device'):S.render.compositor_device='CPU' +S.world.use_nodes=True;S.world.node_tree.nodes['Background'].inputs['Strength'].default_value=0 +P=json.loads((ROOT/'assets/config.json').read_text());records=[] +for path in sorted(source.glob('[0-9]*.exr')): + match=re.search(r'(\d{4})\D*\.exr$',path.name) + if not match:raise RuntimeError('Cannot identify frame in '+path.name) + fr=int(match.group(1)) + if 'anchors' in args and fr not in [1,65,128]:continue + S.frame_set(fr) + for name,glow,smear in ([('broad2',P['glow_strength'],P['smear_strength']),('broad5',P['glow_strength'],P['smear_strength']),('broad10',P['glow_strength'],P['smear_strength'])] if 'broad' in args else [('scatter035',.35,.05),('scatter100',1.,.05),('scatter200',2.,.05)] if 'scatter' in args else [('cap048',P['glow_strength'],P['smear_strength']),('cap060',P['glow_strength'],P['smear_strength']),('cap085',P['glow_strength'],P['smear_strength'])] if 'shoulder' in args else [('fog_low',1.0,.05),('fog_mid',2.0,.05),('fog_high',3.5,.05)] if 'fogtest' in args else [('wet_low',.35,.035),('wet_mid',.35,.05),('wet_high',.35,.07)] if 'wet' in args else [('current',P['glow_strength'],P['smear_strength'])] if 'current' in args else [('raw',0,0)] if 'raw' in args else [('low',.35,.22),('matched',.65,.45),('medium',1.1,.65)]): + params={**P,'glow_strength':glow,'smear_strength':smear} + if 'broad' in args:params['broad_scatter']={'broad2':2,'broad5':5,'broad10':10}[name] + if 'scatter' in args: + params['sensor_correlated']=[[5,8],[14,18]] + params['smear_region_gain']={**P['smear_region_gain'],'opening':2.5} + if 'shoulder' in args:params['highlight_ceiling']={'cap048':.48,'cap060':.60,'cap085':.85}[name] + if 'fogtest' in args:params['glow_size']={'fog_low':.70,'fog_mid':.74,'fog_high':.77}[name] + if 'sensor' in args:params['sensor_noise']=.012 + if 'balance' in args:params['camera_balance']={'1':[1,1,1],'33':[.96,1.13,1.43],'65':[.90,1.26,1.80],'97':[.72,1.09,1.60],'128':[.57,.84,1.31]} + nt=install_optics(S,params) + rl=next(n for n in nt.nodes if n.bl_idname=='CompositorNodeRLayers');targets=[link.to_socket for link in nt.links if link.from_node==rl] + im=bpy.data.images.load(str(path),check_existing=True);im.colorspace_settings.name='Linear Rec.709' + nd=nt.nodes.new('CompositorNodeImage');nd.image=im;nt.nodes.remove(rl) + for target in targets:nt.links.new(nd.outputs['Image'],target) + S.view_settings.view_transform='AgX';S.view_settings.look='AgX - Medium Low Contrast';S.view_settings.exposure=.7 + start=time.monotonic();bpy.ops.render.render() + variants=[('configured',P.get('view_transform','AgX'),P['look'],P['exposure'])] if 'configured' in args else [('agx','AgX','AgX - Medium Low Contrast',.7)] + if 'views' in args:variants += [('standard_00','Standard','None',0.0),('standard_025','Standard','None',.25),('standard_05','Standard','None',.5),('neutral_025','Khronos PBR Neutral','None',.25)] + if name=='matched':variants += [('standard_05','Standard','None',.5),('standard_10','Standard','None',1.0),('standard_15','Standard','None',1.5),('neutral_10','Khronos PBR Neutral','None',1.0)] + for view_name,view,look,ev in variants: + S.view_settings.view_transform=view;S.view_settings.look=look;S.view_settings.exposure=ev;S.view_settings.gamma=P.get('gamma',1.0) if view_name=='configured' else 1.0 + out=ROOT/'renders'/f'{label}_{name}_{view_name}';out.mkdir(parents=True,exist_ok=True);target=out/f'{fr:04d}.png' + bpy.data.images['Render Result'].save_render(str(target),scene=S) + records.append({'frame':fr,'label':out.name,'glow':glow,'smear':smear,'view':view,'exposure':ev,'sha256':hashlib.sha256(target.read_bytes()).hexdigest()}) + if view_name=='standard_05': + for gamma in [1.06,1.12]: + S.view_settings.gamma=gamma;extra=ROOT/'renders'/f'{label}_{name}_standard_05_gamma{str(gamma).replace(".","")}' + extra.mkdir(parents=True,exist_ok=True);bpy.data.images['Render Result'].save_render(str(extra/f'{fr:04d}.png'),scene=S) + S.view_settings.gamma=1.0 + print('OPTICS_VARIANTS',fr,name,'seconds',time.monotonic()-start,flush=True) +(ROOT/f'logs/{label}_variants.json').write_text(json.dumps(records,indent=2)) +print('OPTICS_VARIANTS_COMPLETE',len(records),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/overscan.py b/astra_scene_test/Scene_04/scripts/overscan.py new file mode 100644 index 0000000000000000000000000000000000000000..84dccbc986082c279741456233b821469d2db2c7 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/overscan.py @@ -0,0 +1,40 @@ +"""Render an optical margin while preserving the calibrated cropped projection.""" +import math +OUTPUT=(1280,720) +def fog_glow_size(value,factor): + """Preserve degrees per pixel in Blender 5.2's Spencer glare kernel. + + The Size socket maps to 180 - 170 * cuberoot(Size) degrees, rather than + scaling a blur radius directly. See Blender's node_composite_glare.cc, + compute_fog_glow_field_of_view(), and fog_glow_kernel.cc. + """ + field_of_view=180-170*max(0,min(1,value))**(1/3) + return max(0,min(1,((180-field_of_view*factor)/170)**3)) +def configure(scene,parameters): + factor=parameters.get('optical_overscan',1.0) + assert 1<=factor<=1.5 + width,height=[round(v*factor) for v in OUTPUT] + scene.render.resolution_x=width;scene.render.resolution_y=height + camera=scene.camera.data + camera.lens=18/math.tan(math.radians(parameters['fov']/2))/factor + camera.shift_y=parameters.get('shift_y',0)/factor + scene['optical_overscan']=factor;scene['output_resolution']=list(OUTPUT) + scene['optical_margin_px']=[(width-OUTPUT[0])//2,(height-OUTPUT[1])//2] + scene.render.save_output=factor<=1 + return scene['optical_margin_px'] +def install_crop(scene,nodes,links,source): + if scene.get('optical_overscan',1)<=1:return source + crop=nodes.new('CompositorNodeCrop');crop.name='SOURCE_FRAME_CROP' + crop.label='Crop optical margin to the source frame';crop.inputs['Alpha Crop'].default_value=False + x,y=scene['optical_margin_px'] + for key,value in [('X',x),('Y',y),('Width',1280),('Height',720)]: + socket=crop.inputs[key];socket.default_value=value + driver=socket.driver_add('default_value').driver;variable=driver.variables.new();variable.name='percentage' + variable.type='SINGLE_PROP';variable.targets[0].id_type='SCENE';variable.targets[0].id=scene + variable.targets[0].data_path='render.resolution_percentage';driver.expression=f'{value}*percentage/100' + links.new(source,crop.inputs['Image']) + file=nodes.new('CompositorNodeOutputFile');file.name='SOURCE_FRAME_OUTPUT' + file.label='Native 1280 by 720 sequence output';file.directory='//renders/final';file.file_name='' + file.format.media_type='IMAGE';file.format.file_format='PNG';file.format.color_mode='RGB';file.format.color_depth='8' + file.file_output_items.new('RGBA','####');links.new(crop.outputs['Image'],file.inputs['####']) + return crop.outputs['Image'] diff --git a/astra_scene_test/Scene_04/scripts/photographic_materials.py b/astra_scene_test/Scene_04/scripts/photographic_materials.py new file mode 100644 index 0000000000000000000000000000000000000000..9529406a90a1eb751e3d8661572cc00b83d7624f --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/photographic_materials.py @@ -0,0 +1,32 @@ +"""CC0 surface microstructure, with scene-calibrated color and roughness. +The maps describe physical materials; none contains reference-video imagery. +""" +import bpy,pathlib +ROOT=pathlib.Path(__file__).resolve().parents[1] +def install_material_maps(materials): + for key,asset,mean,tile in [('asphalt','asphalt_track',.0137373,4.0),('redbrick','brick_wall_09',.255972,2.0),('darkbrick','brick_wall_09',.255972,2.0),('tanbrick','brick_wall_09',.255972,2.0)]: + m=materials[key];n=m.node_tree.nodes;l=m.node_tree.links;p=n.get('Principled BSDF') + co=n.new('ShaderNodeTexCoord');scale=n.new('ShaderNodeVectorMath');scale.operation='SCALE';scale.inputs['Scale'].default_value=1/tile;l.new(co.outputs['UV'],scale.inputs[0]) + textures={} + for channel,ext in [('diff','jpg'),('rough','jpg'),('disp','png')]: + nd=n.new('ShaderNodeTexImage');nd.label='Poly Haven CC0 '+asset+' '+channel + im=bpy.data.images.load(str(ROOT/'assets/materials'/f'{asset}_{channel}_2k.{ext}'),check_existing=True) + im.colorspace_settings.name='sRGB' if channel=='diff' else 'Non-Color';im.pack();nd.image=im;nd.extension='REPEAT';l.new(scale.outputs[0],nd.inputs['Vector']);textures[channel]=nd + bw=n.new('ShaderNodeRGBToBW');l.new(textures['diff'].outputs['Color'],bw.inputs[0]) + normalize=n.new('ShaderNodeMath');normalize.operation='DIVIDE';normalize.inputs[1].default_value=mean;l.new(bw.outputs[0],normalize.inputs[0]) + cl=n.new('ShaderNodeClamp');cl.inputs['Min'].default_value=.35;cl.inputs['Max'].default_value=2.1;l.new(normalize.outputs[0],cl.inputs['Value']) + tint=n.new('ShaderNodeVectorMath');tint.operation='SCALE';tint.inputs[0].default_value=m.diffuse_color[:3];l.new(cl.outputs[0],tint.inputs['Scale']);l.new(tint.outputs[0],p.inputs['Base Color']) + # Broad wetness stays patchy but the aggregate breaks up each reflection. + bump=n.new('ShaderNodeBump');bump.inputs['Strength'].default_value=.65 if key=='asphalt' else .42;bump.inputs['Distance'].default_value=.026 if key=='asphalt' else .025 + l.new(textures['disp'].outputs['Color'],bump.inputs['Height']);l.new(bump.outputs[0],p.inputs['Normal']) + if key!='asphalt': + rough=n.new('ShaderNodeMapRange');rough.inputs['To Min'].default_value=.52;rough.inputs['To Max'].default_value=.82;l.new(textures['rough'].outputs['Color'],rough.inputs['Value']);l.new(rough.outputs[0],p.inputs['Roughness']) + m['source']='https://polyhaven.com/a/'+asset;m['license']='CC0' +def add_surface_uv(mesh): + uv=mesh.uv_layers.new(name='Physical surface coordinates') + for poly in mesh.polygons: + normal=poly.normal;axis=max(range(3),key=lambda i:abs(normal[i])) + axes=(1,2) if axis==0 else ((0,2) if axis==1 else (0,1)) + for loop in poly.loop_indices: + pt=mesh.vertices[mesh.loops[loop].vertex_index].co + uv.data[loop].uv=(pt[axes[0]],pt[axes[1]]) diff --git a/astra_scene_test/Scene_04/scripts/probe_compositor.py b/astra_scene_test/Scene_04/scripts/probe_compositor.py new file mode 100644 index 0000000000000000000000000000000000000000..b30b65df4cf1eb667b6e6a92278bee6d6a9b54c9 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_compositor.py @@ -0,0 +1,8 @@ +import bpy +g=bpy.data.node_groups.new('Compositor API probe','CompositorNodeTree') +for k in ['ShaderNodeMix','CompositorNodeMix','CompositorNodeColorBalance','CompositorNodeLensdist','ShaderNodeMath','CompositorNodeMath','ShaderNodeVectorMath','CompositorNodeImage','CompositorNodeComposite','NodeGroupOutput']: + try: + n=g.nodes.new(k) + print(k,'inputs',[(i.name,i.bl_idname,str(getattr(i,'default_value',None))) for i in n.inputs], + 'properties',[(p.identifier,p.type) for p in n.bl_rna.properties if p.identifier not in ['inputs','outputs']],flush=True) + except Exception as e:print(k,str(e),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/probe_grain.py b/astra_scene_test/Scene_04/scripts/probe_grain.py new file mode 100644 index 0000000000000000000000000000000000000000..762b5e70820f202464bac2e80f151b2f689a841b --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_grain.py @@ -0,0 +1,6 @@ +import bpy +nt=bpy.data.node_groups.new('Sensor grain API investigation','CompositorNodeTree') +for kind in ['CompositorNodeTexture','ShaderNodeTexNoise','ShaderNodeTexWhiteNoise','CompositorNodeInputPosition','ShaderNodeTexCoord','CompositorNodeInputSceneTime']: + try: + n=nt.nodes.new(kind);print(kind,[(s.name,s.bl_idname,str(getattr(s,'default_value',None))) for s in n.inputs],[(s.name,s.bl_idname) for s in n.outputs],flush=True) + except Exception as e:print(kind,type(e).__name__,str(e),flush=True) diff --git a/astra_scene_test/Scene_04/scripts/probe_output.py b/astra_scene_test/Scene_04/scripts/probe_output.py new file mode 100644 index 0000000000000000000000000000000000000000..9b9e9b147a89f6c651d1dde1445e10ba347e5b0d --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_output.py @@ -0,0 +1,15 @@ +import bpy +S=bpy.context.scene +nt=S.compositing_node_group +if nt is None:nt=bpy.data.node_groups.new('Output probe','CompositorNodeTree');S.compositing_node_group=nt +n=nt.nodes.new('CompositorNodeOutputFile') +for p in n.bl_rna.properties: + if p.identifier not in {'rna_type','id_data'}: + print(p.identifier,p.type,str(getattr(n,p.identifier,''))[:500],flush=True) +print('INPUTS',[(i.name,i.bl_idname) for i in n.inputs],flush=True) +if hasattr(n,'file_output_items'): + print('ITEMS',[(p.identifier,p.type) for p in n.file_output_items.bl_rna.properties],flush=True) + print('METHODS',[(f.identifier,[p.identifier for p in f.parameters]) for f in n.file_output_items.bl_rna.functions],flush=True) +print('VIEW_TRANSFORMS',[(i.identifier,i.name) for i in S.view_settings.bl_rna.properties['view_transform'].enum_items],flush=True) +for p in n.format.bl_rna.properties: + if p.identifier!='rna_type':print('FORMAT',p.identifier,p.type,str(getattr(n.format,p.identifier,''))[:300],flush=True) diff --git a/astra_scene_test/Scene_04/scripts/probe_overscan_crop.py b/astra_scene_test/Scene_04/scripts/probe_overscan_crop.py new file mode 100644 index 0000000000000000000000000000000000000000..7f43bd2eaa3a959a63d909c95bf120f533f521dd --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_overscan_crop.py @@ -0,0 +1,21 @@ +"""Verify actual saved compositor dimensions using a synthetic solid image.""" +import bpy,pathlib +ROOT=pathlib.Path(__file__).resolve().parents[1];S=bpy.context.scene +S.render.engine='CYCLES';S.cycles.device='CPU';S.cycles.samples=1 +S.render.threads_mode='FIXED';S.render.threads=2 +S.render.resolution_x=2;S.render.resolution_y=2;S.render.resolution_percentage=100 +if hasattr(S.render,'compositor_device'):S.render.compositor_device='CPU' +S.use_nodes=True;nt=bpy.data.node_groups.new('Synthetic crop proof','CompositorNodeTree');S.compositing_node_group=nt +nt.interface.new_socket(name='Image',in_out='OUTPUT',socket_type='NodeSocketColor') +im=bpy.data.images.new('Synthetic solid calibration field',width=1600,height=900);im.generated_color=(.1,.4,.2,1) +n=nt.nodes.new('CompositorNodeImage');n.image=im +crop=nt.nodes.new('CompositorNodeCrop') +for k,v in [('X',160),('Y',90),('Width',1280),('Height',720),('Alpha Crop',False)]:crop.inputs[k].default_value=v +out=nt.nodes.new('NodeGroupOutput');nt.links.new(n.outputs['Image'],crop.inputs['Image']);nt.links.new(crop.outputs['Image'],out.inputs['Image']) +file=nt.nodes.new('CompositorNodeOutputFile');file.directory=str(ROOT/'renders/comparisons/crop_native_probe');file.file_name='' +file.format.media_type='IMAGE';file.format.file_format='PNG';file.format.color_depth='8';file.file_output_items.new('RGBA','####') +nt.links.new(crop.outputs['Image'],file.inputs['####']) +print('SYNTHETIC_IMAGE_SIZE',list(im.size),flush=True) +S.render.image_settings.file_format='PNG';S.render.filepath=str(ROOT/'renders/comparisons/overscan_crop_proof.png') +S.render.save_output=False;S.frame_set(5);bpy.ops.render.render(write_still=False) +print('OVERSCAN_CROP_PROOF_COMPLETE') diff --git a/astra_scene_test/Scene_04/scripts/probe_road_balance.py b/astra_scene_test/Scene_04/scripts/probe_road_balance.py new file mode 100644 index 0000000000000000000000000000000000000000..e3a6d5a7fb59bad9493f0507055409e2b5a46b34 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_road_balance.py @@ -0,0 +1,46 @@ +"""Compare wet-road roughness and shop/fill illumination in a native-size ROI. + +The saved scene is not changed. The compositing stage is disabled to isolate +surface light transport. Source crop is retained by a separate review command. +""" +import bpy, pathlib, sys, json, time +ROOT=pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0,str(ROOT/'scripts')) +from render_settings import apply_quality +from render_device import configure +S=bpy.context.scene +apply_quality(S,96,100);configure(S) +ending='ending' in sys.argv;frame=128 if ending else 65 +S.frame_set(frame);S.render.use_compositing=False;S.render.save_output=True +S.render.use_border=True;S.render.use_crop_to_border=True +mx,my=S.get('optical_margin_px',[0,0]);w,h=S.render.resolution_x,S.render.resolution_y +S.render.border_min_x=(250+mx)/w;S.render.border_max_x=(850+mx)/w +S.render.border_min_y=1-(670+my)/h;S.render.border_max_y=1-(530+my)/h +S.render.image_settings.file_format='PNG';S.render.image_settings.color_mode='RGB' +p=bpy.data.materials['asphalt'].node_tree.nodes.get('Principled BSDF') +ramp=p.inputs['Roughness'].links[0].from_node.color_ramp +shoplights=[o for o in S.objects if o.type=='LIGHT' and o.name in ['Training studio interior white light','Hair decor interior white light']] +fill=S.objects['Urban sky bounce across foreground'] +intersection_fill=S.objects['Intersection reflected blue-gray sky fill'] +original=[o.data.energy for o in shoplights];fill_power=fill.data.energy +label='road_ending_probe' if ending else 'road_balance_probe' +folder=ROOT/'renders'/label;folder.mkdir(parents=True,exist_ok=True) +records=[] +variants=[ + ('original',(.25,.58),1,1), + ('rougher',(.42,.78),1,1), + ('less_fill',(.25,.58),.45,.67), + ('rougher_less_fill',(.42,.78),.45,.67)] +if ending:variants=[('fill2600',(.42,.78),1,1),('fill5000',(.42,.78),1,1),('fill8000',(.42,.78),1,1)] +for name,rough,shop_scale,fill_scale in variants: + for element,value in zip(ramp.elements,rough):element.color=(value,value,value,1) + for light,energy in zip(shoplights,original):light.data.energy=energy*shop_scale + fill.data.energy=fill_power*fill_scale + if ending:intersection_fill.data.energy=float(name[4:]) + S.render.filepath=str(folder/(name+f'_{frame:04d}.png'));start=time.monotonic() + bpy.ops.render.render(write_still=True) + row={'variant':name,'frame':frame,'roughness':rough,'shop_scale':shop_scale,'fill_scale':fill_scale,'intersection_fill_watts':intersection_fill.data.energy, + 'seconds':time.monotonic()-start,'source_crop':[250,530,850,670]} + records.append(row);print('ROAD_BALANCE_VARIANT',json.dumps(row),flush=True) +(ROOT/'logs'/(label+'.json')).write_text(json.dumps(records,indent=2)) +print('ROAD_BALANCE_PROBE_COMPLETE',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/probe_road_glint.py b/astra_scene_test/Scene_04/scripts/probe_road_glint.py new file mode 100644 index 0000000000000000000000000000000000000000..ce1c399c74d1aa1b2c4ae186903da21d1adc1ee9 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_road_glint.py @@ -0,0 +1,19 @@ +"""Native region light-transport diagnosis for the ending road highlight.""" +import bpy,pathlib,sys +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from render_settings import apply_quality +from render_device import configure +S=bpy.context.scene;S.frame_set(128);apply_quality(S,128,100);configure(S) +S.render.use_border=True;S.render.use_crop_to_border=True +S.render.border_min_x=860/1280;S.render.border_max_x=1010/1280;S.render.border_min_y=1-640/720;S.render.border_max_y=1-540/720 +S.render.use_compositing=False;S.render.use_motion_blur=False;S.render.image_settings.file_format='PNG' +P=bpy.data.materials['asphalt'].node_tree.nodes.get('Principled BSDF');nt=bpy.data.materials['asphalt'].node_tree +out=ROOT/'renders/road_glint_probe';out.mkdir(parents=True,exist_ok=True) +for name in ['original','no_caustics','rough','diffuse']: + if name=='no_caustics':S.cycles.caustics_reflective=False;S.cycles.caustics_refractive=False + if name=='rough': + for link in list(P.inputs['Roughness'].links):nt.links.remove(link) + P.inputs['Roughness'].default_value=.65;P.inputs['Coat Weight'].default_value=0 + if name=='diffuse':P.inputs['Specular IOR Level'].default_value=0;P.inputs['Metallic'].default_value=0 + S.render.filepath=str(out/(name+'.png'));bpy.ops.render.render(write_still=True);print('GLINT_PROBE',name,flush=True) +print('GLINT_PROBE_COMPLETE',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/probe_signal_local_lights.py b/astra_scene_test/Scene_04/scripts/probe_signal_local_lights.py new file mode 100644 index 0000000000000000000000000000000000000000..cdec07a777cf17c68798be10b2822a834f4809fb --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_signal_local_lights.py @@ -0,0 +1,15 @@ +"""Isolate non-geometric signal-light spill; never saves the loaded scene.""" +import bpy,pathlib,sys,json +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from render_settings import apply_quality +from render_device import configure +S=bpy.context.scene;apply_quality(S,96,100);configure(S) +changes=[] +for ob in S.objects: + if ob.type=='LIGHT' and ob.name.startswith('Green signal local glow'): + changes.append({'name':ob.name,'old_energy':ob.data.energy});ob.data.energy=0 +S.frame_set(65);S.render.image_settings.file_format='PNG' +folder=ROOT/'renders/signal_spill_probe';folder.mkdir(parents=True,exist_ok=True) +S.render.filepath=str(folder/'0065.png');bpy.ops.render.render(write_still=True) +(ROOT/'logs/signal_spill_probe.json').write_text(json.dumps({'frame':65,'samples':96,'changes':changes},indent=2)) +print('SIGNAL_SPILL_PROBE_COMPLETE') diff --git a/astra_scene_test/Scene_04/scripts/probe_sky.py b/astra_scene_test/Scene_04/scripts/probe_sky.py new file mode 100644 index 0000000000000000000000000000000000000000..88696403a52f8e7877f4015442c3898844d1ff9e --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/probe_sky.py @@ -0,0 +1,24 @@ +"""Separate the camera-visible sky and in-scattered radiance in small native crops.""" +import bpy,pathlib,sys,json,time +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from render_settings import apply_quality +from render_device import configure +S=bpy.context.scene;apply_quality(S,128,100);configure(S) +layered='layered' in sys.argv +if layered: + from atmosphere import install_atmosphere + install_atmosphere(json.loads((ROOT/'assets/config.json').read_text())) +S.render.use_border=True;S.render.use_crop_to_border=True +S.render.border_min_x=550/1280;S.render.border_max_x=700/1280 +S.render.border_min_y=1-210/720;S.render.border_max_y=1-30/720 +S.render.image_settings.file_format='OPEN_EXR';S.render.image_settings.color_depth='32' +S.render.use_compositing=False;S.render.use_motion_blur=False +sky=next(n for n in S.world.node_tree.nodes if n.label.startswith('Gradual calibrated')) +out=ROOT/('renders/sky_probe_layered' if layered else 'renders/sky_probe');out.mkdir(parents=True,exist_ok=True) +for fr in [1,65,128]: + S.frame_set(fr);strength=sky.inputs['Strength'].default_value + for label,value in [('zero',0),('original',strength)]: + sky.inputs['Strength'].default_value=value;S.render.filepath=str(out/f'{fr:04d}_{label}.exr');t=time.monotonic() + bpy.ops.render.render(write_still=True);print('SKY_PROBE',fr,label,time.monotonic()-t,flush=True) + sky.inputs['Strength'].default_value=strength +print('SKY_PROBE_COMPLETE',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/recompose_overscan.py b/astra_scene_test/Scene_04/scripts/recompose_overscan.py new file mode 100644 index 0000000000000000000000000000000000000000..031c9559f1e50fe78afc51f3c9492d0f4e2e093b --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/recompose_overscan.py @@ -0,0 +1,88 @@ +"""Trial optical response from raw scene radiance, preserving the native crop. + +Run in Blender with -- --source renders/overscan_review/linear --label opening +--frames 1,2,4,6 --opening-gains .5,1.0,1.5,2.5. Never saves the .blend. +""" +import argparse, copy, hashlib, json, pathlib, struct, sys, time +import bpy +ROOT = pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / 'scripts')) +from install_optics import install_optics +p = argparse.ArgumentParser(description=__doc__) +p.add_argument('--source', required=True) +p.add_argument('--label', required=True) +p.add_argument('--frames', required=True) +p.add_argument('--opening-gains', default='2.5') +p.add_argument('--overrides', default='{}', help='JSON object of top-level optical parameter overrides') +a = p.parse_args(sys.argv[sys.argv.index('--') + 1:]) +assert a.label.replace('_', '').isalnum() +frames = [int(f) for f in a.frames.split(',')] +gains = [float(f) for f in a.opening_gains.split(',')] +assert all(1 <= fr <= 128 for fr in frames) and all(g >= 0 for g in gains) +S = bpy.context.scene +for obj in list(bpy.data.objects): + if obj != S.camera: + bpy.data.objects.remove(obj, do_unlink=True) +S.render.engine = 'CYCLES' +S.cycles.device = 'CPU' +S.cycles.samples = 1 +S.cycles.use_denoising = False +S.cycles.use_adaptive_sampling = False +S.render.threads_mode = 'FIXED' +S.render.threads = 2 +S.render.use_motion_blur = False +S.render.resolution_percentage = 100 +S.render.save_output = False +S.render.compositor_device = 'CPU' +S.world.use_nodes = True +S.world.node_tree.nodes.clear() +bg = S.world.node_tree.nodes.new('ShaderNodeBackground') +bg.inputs['Strength'].default_value = 0 +out = S.world.node_tree.nodes.new('ShaderNodeOutputWorld') +S.world.node_tree.links.new(bg.outputs[0], out.inputs[0]) +P = json.loads((ROOT / 'assets/config.json').read_text()) +P.update(json.loads(a.overrides)) +S.view_settings.view_transform = P['view_transform'] +S.view_settings.look = P['look'] +S.view_settings.exposure = P['exposure'] +S.view_settings.gamma = P['gamma'] +records = [] +for gain in gains: + params = copy.deepcopy(P) + params['smear_region_gain']['opening'] = gain + label = a.label + '_gain' + str(gain).replace('.', 'p') + folder = ROOT / 'renders' / label + folder.mkdir(parents=True, exist_ok=True) + for fr in frames: + source = ROOT / a.source / f'{fr:04d}_Raw.exr' + assert source.is_file(), str(source) + nt = install_optics(S, params) + raw = bpy.data.images.load(str(source), check_existing=False) + raw.colorspace_settings.name = 'Linear Rec.709' + assert tuple(raw.size) == (S.render.resolution_x, S.render.resolution_y) + rl = next(n for n in nt.nodes if n.bl_idname == 'CompositorNodeRLayers') + targets = [link.to_socket for link in nt.links if link.from_node == rl] + nd = nt.nodes.new('CompositorNodeImage') + nd.image = raw + nt.nodes.remove(rl) + for socket in targets: + nt.links.new(nd.outputs['Image'], socket) + native = nt.nodes['SOURCE_FRAME_OUTPUT'] + native.directory = str(folder) + native.file_name = '' + S.frame_set(fr) + started = time.monotonic() + bpy.ops.render.render(write_still=False) + target = folder / f'{fr:04d}.png' + data = target.read_bytes() + assert struct.unpack('>II', data[16:24]) == (1280, 720) + row = {'frame': fr, 'label': label, 'opening_gain': gain, + 'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(), + 'sha256': hashlib.sha256(data).hexdigest(), + 'seconds': time.monotonic() - started} + records.append(row) + print('RECOMPOSE_FRAME', json.dumps(row), flush=True) + nd.image = None + bpy.data.images.remove(raw) +(ROOT / 'logs' / (a.label + '_recomposition.json')).write_text(json.dumps(records, indent=2)) +print('OVERSCAN_RECOMPOSITION_COMPLETE', len(records), flush=True) diff --git a/astra_scene_test/Scene_04/scripts/reference.py b/astra_scene_test/Scene_04/scripts/reference.py new file mode 100644 index 0000000000000000000000000000000000000000..95e7a10b2d06492d0eb444dc4f3ff066c8a3fa8a --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/reference.py @@ -0,0 +1,27 @@ +import hashlib, json, pathlib, subprocess +from PIL import Image, ImageDraw +import numpy as np +ROOT = pathlib.Path(__file__).resolve().parents[1] +frames = sorted((ROOT/'reference/frames').glob('*.png')) +assert frames +rows=[] +for i,p in enumerate(frames): + im=Image.open(p).convert('RGB'); a=np.asarray(im) + rows.append({'frame':i+1,'time_seconds':i/30,'path':str(p.relative_to(ROOT)), + 'mean_rgb':a.mean(axis=(0,1)).round(3).tolist(), + 'sha256':hashlib.sha256(p.read_bytes()).hexdigest()}) +metadata={'width':im.width,'height':im.height,'fps_num':30,'fps_den':1,'frame_count':len(frames), + 'duration_seconds':len(frames)/30,'audio_streams':0, + 'source_sha256':hashlib.sha256((ROOT/'night_front_wide_120fov.mp4').read_bytes()).hexdigest(), + 'measurement':'FFmpeg full decode; stream time base 1/15360; constant rate 30/1; no audio stream.', 'frames':rows} +(ROOT/'reference/metadata.json').write_text(json.dumps(metadata,indent=2)) +for page,ids in enumerate([list(range(1,129,8)),list(range(1,129))]): + w=400 if page==0 else 240; h=w*720//1280 + cols=4 if page==0 else 8 + sheet=Image.new('RGB',(cols*w,((len(ids)+cols-1)//cols)*(h+24)),(20,20,20));d=ImageDraw.Draw(sheet) + for j,n in enumerate(ids): + x=j%cols*w; y=j//cols*(h+24) + sheet.paste(Image.open(frames[n-1]).resize((w,h)),(x,y)) + d.text((x+7,y+h+4),f'frame {n:03d} {(n-1)/30:.4f} s',fill='white') + sheet.save(ROOT/f'reference/contact_sheet_{page}.jpg',quality=95) +print('Extracted',len(frames),'timestamped frames') diff --git a/astra_scene_test/Scene_04/scripts/refine_camera_frames.py b/astra_scene_test/Scene_04/scripts/refine_camera_frames.py new file mode 100644 index 0000000000000000000000000000000000000000..bd7616b7f5329f49bdbcf2e3e8f4f12318d5b1b7 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/refine_camera_frames.py @@ -0,0 +1,49 @@ +"""Refine intermediate camera poses from all source frames; keep geometry fixed. +The baseline bundle determines scene points and metric ambiguity. This pass +estimates the source's small inter-frame fluctuations without synthetic shake. +""" +from pathlib import Path +import json,numpy as np +from scipy.optimize import least_squares +from scipy.spatial.transform import Rotation +ROOT=Path(__file__).resolve().parents[1] +M=json.loads((ROOT/'reference/camera_fit_selected.json').read_text()) +tracks=np.load(ROOT/'reference/feature_tracks.npz')['tracks'];ids=M['track_ids'];obs=tracks[ids] +f=M['focal_pixels'];pp=np.array(M['principal_point']);ep=np.array(M['epipole']);d=np.r_[(ep-pp)/f,1] +points=np.c_[(obs[:,0]-pp)/f,np.ones(len(ids))]*np.array(M['depth_in_total_travel_units'])[:,None] +frames=np.arange(1,129);old_t=np.interp(frames,M['frames'],M['displacement_fraction']);old_r=np.array([np.interp(frames,M['frames'],np.array(M['rotation_vectors'])[:,i]) for i in range(3)]).T +weights=np.array([3 if n==122 else 2 if n==85 else 1.3 if n in [74,75,251] else 1 for n in ids]) +def projection(t,r): + q=(Rotation.from_rotvec(r).as_matrix()@(points-t*d).T).T + return q[:,:2]/q[:,2:]*f+pp +new_t=old_t.copy();new_r=old_r.copy();fixed=set(M['frames']) +for j,fr in enumerate(frames): + if fr in fixed:continue + valid=np.isfinite(obs[:,j,0]);initial=np.r_[old_t[j],old_r[j]] + def residual(p): + delta=(projection(p[0],p[1:])-obs[:,j])*weights[:,None] + return np.r_[delta[valid].ravel(),(p-initial)*[12,20,20,20]] + fit=least_squares(residual,initial,loss='soft_l1',f_scale=1.5,max_nfev=60, + bounds=(initial-[.012,.012,.012,.012],initial+[.012,.012,.012,.012])) + new_t[j]=fit.x[0];new_r[j]=fit.x[1:] +# Remove isolated sub-frame tracker noise while retaining measured motion. The +# original calibrated knots and endpoints stay exact. +for arr,old in [(new_t,old_t),(new_r,old_r)]: + delta=arr-old;filtered=delta.copy();filtered[1:-1]=.18*delta[:-2]+.64*delta[1:-1]+.18*delta[2:] + arr[:]=old+filtered + for fr in fixed:arr[fr-1]=old[fr-1] +assert np.all(np.diff(new_t)>0),'Refinement broke continuous forward travel' +rows=[] +for j,fr in enumerate(frames): + before=projection(old_t[j],old_r[j]);after=projection(new_t[j],new_r[j]);valid=np.isfinite(obs[:,j,0]) + rows.append({'frame':int(fr),'median_before':float(np.median(np.linalg.norm(before[valid]-obs[valid,j],axis=1))), + 'median_after':float(np.median(np.linalg.norm(after[valid]-obs[valid,j],axis=1))), + 'max_projected_adjustment':float(np.max(np.linalg.norm(after[valid]-before[valid],axis=1)))}) +result={**M,'frames':frames.tolist(),'displacement_fraction':new_t.tolist(),'rotation_vectors':new_r.tolist(), + 'refinement':'All128 source frames; fixed3D landmarks; original17 calibration knots held exact; small isolated tracker fluctuations filtered; no artificial camera shake.'} +(ROOT/'assets/camera_motion_full.json').write_text(json.dumps(result,indent=2)) +report={'old_median_across_frames':float(np.median([r['median_before'] for r in rows])), + 'new_median_across_frames':float(np.median([r['median_after'] for r in rows])), + 'max_landmark_projection_adjustment':max(r['max_projected_adjustment'] for r in rows),'frames':rows} +(ROOT/'logs/camera_full_frame_refinement.json').write_text(json.dumps(report,indent=2)) +print(json.dumps({k:v for k,v in report.items() if k!='frames'},indent=2)) diff --git a/astra_scene_test/Scene_04/scripts/render.py b/astra_scene_test/Scene_04/scripts/render.py new file mode 100644 index 0000000000000000000000000000000000000000..8eae7b64b2bf517b826219fc2ca9c4fb0d681ae5 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/render.py @@ -0,0 +1,72 @@ +"""Render saved editable scene. Arguments: label, frames|all, samples, percent. +Example: Blender -b scene_04.blend --python scripts/render.py -- final all 768 100 +""" +import bpy,sys,pathlib,json,time,datetime,hashlib,struct +ROOT=pathlib.Path(__file__).resolve().parents[1] +args=sys.argv[sys.argv.index('--')+1:] if '--' in sys.argv else [] +label=args[0] if args else 'preview';selected=args[1] if len(args)>1 else '1,33,65,97,128' +samples=int(args[2]) if len(args)>2 else 96;percent=int(args[3]) if len(args)>3 else 60 +frames=list(range(1,129)) if selected=='all' else [int(f) for f in selected.split(',')] +S=bpy.context.scene;S.cycles.samples=samples;S.cycles.adaptive_threshold=.008 if samples>=512 else .012 if samples>=256 else .035 +S.render.resolution_percentage=percent +sys.path.insert(0,str(ROOT/'scripts')) +from render_device import configure +device_name=configure(S,'cpu' in args) +print('RENDER_DEVICE',device_name,flush=True) +S.render.image_settings.file_format='PNG';S.render.image_settings.color_mode='RGB' +if hasattr(S.render,'compositor_device'):S.render.compositor_device='CPU' +S.cycles.seed=404;S.cycles.use_animated_seed=False +S.cycles.use_auto_tile=True;S.cycles.tile_size=256 +S.cycles.debug_use_compact_bvh=True +S.cycles.denoising_use_gpu=False +S.cycles.adaptive_min_samples=128 if samples>=512 else 64 if samples>=256 else 16 +if 'clear' in args: + for o in bpy.data.collections.get('Atmosphere').objects:o.hide_render=True + S.cycles.max_bounces=4;S.cycles.use_denoising=True +from render_settings import apply_quality +apply_quality(S,samples,percent) +folder=ROOT/'renders'/label;folder.mkdir(parents=True,exist_ok=True) +native=S.compositing_node_group.nodes.get('SOURCE_FRAME_OUTPUT') if S.compositing_node_group else None +if native: + native.directory=str(folder);native.file_name='' + assert native.file_output_items[0].name=='####' +if 'raw' in args: + nt=S.compositing_node_group + raw=nt.nodes.new('CompositorNodeOutputFile');raw.label='Uncomposited scene radiance for optical comparisons' + raw.directory=str(folder/'linear');raw.file_name='frame' + raw.format.media_type='IMAGE';raw.format.file_format='OPEN_EXR';raw.format.color_depth='16';raw.format.exr_codec='ZIP' + raw.file_output_items.new('RGBA','Raw') + source=next(n for n in nt.nodes if n.bl_idname=='CompositorNodeRLayers') + nt.links.new(source.outputs['Image'],raw.inputs['Raw']) +S.render.use_persistent_data=False +scene_hash=hashlib.sha256((ROOT/'scene_04.blend').read_bytes()).hexdigest() +script_hash=hashlib.sha256(pathlib.Path(__file__).read_bytes()).hexdigest() +helper_hashes={name:hashlib.sha256((ROOT/'scripts'/name).read_bytes()).hexdigest() for name in ['render_settings.py','render_device.py']} +previous={} +operations=ROOT/'logs/render_operations.jsonl' +if operations.exists(): + for line in operations.read_text().splitlines(): + try: + row=json.loads(line) + if row.get('label')==label:previous[row['frame']]=row + except (ValueError,KeyError):continue +for frame in frames: + target=folder/f'{frame:04d}.png' + if target.exists() and 'resume' in args: + row=previous.get(frame,{}) + valid=(row.get('render_script_sha256')==script_hash and row.get('render_helper_sha256')==helper_hashes and row.get('samples')==samples and row.get('percent')==percent and row.get('blend_sha256')==scene_hash + and row.get('sha256')==hashlib.sha256(target.read_bytes()).hexdigest()) + if 'raw' in args:valid=valid and (folder/'linear'/f'{frame:04d}_Raw.exr').exists() + if valid: + print('FRAME_RESUME_VERIFIED',label,frame,flush=True);continue + print('FRAME_RESUME_REJECTED',label,frame,'Existing image lacks matching scene/settings/hash evidence',flush=True) + S.frame_set(frame);S.render.filepath=str(target);start=time.monotonic() + if 'raw' in args:raw.file_name=f'{frame:04d}_' + print('FRAME_START',label,frame,'samples',samples,'percent',percent,flush=True) + bpy.ops.render.render(write_still=native is None) + png=target.read_bytes();output_resolution=list(struct.unpack('>II',png[16:24])) + entry={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'label':label,'frame':frame,'source_seconds':(frame-1)/30, + 'samples':samples,'percent':percent,'device':device_name,'tile_size':S.cycles.tile_size,'metal_working_set_percent':S.get('metal_working_set_percent'),'blender_version':bpy.app.version_string,'adaptive_threshold':S.cycles.adaptive_threshold,'adaptive_min_samples':S.cycles.adaptive_min_samples,'render_helper_sha256':helper_hashes,'blend_sha256':scene_hash,'render_script_sha256':script_hash,'internal_resolution':[S.render.resolution_x,S.render.resolution_y],'output_resolution':output_resolution,'seconds':time.monotonic()-start,'sha256':hashlib.sha256(png).hexdigest()} + with (ROOT/'logs/render_operations.jsonl').open('a') as f:f.write(json.dumps(entry)+'\n') + print('FRAME_COMPLETE',json.dumps(entry),flush=True) +print('SEQUENCE_COMPLETE',label,flush=True) diff --git a/astra_scene_test/Scene_04/scripts/render_device.py b/astra_scene_test/Scene_04/scripts/render_device.py new file mode 100644 index 0000000000000000000000000000000000000000..f650babaccd596c56ce56cbef3393dfffbaf16fa --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/render_device.py @@ -0,0 +1,21 @@ +"""Use an available Blender GPU, with a reproducible CPU fallback.""" +import bpy,os +def configure(scene,cpu=False): + os.environ.setdefault('CYCLES_METAL_WORKING_SET_PERCENT','20') + scene['metal_working_set_percent']=os.environ['CYCLES_METAL_WORKING_SET_PERCENT'] + scene.render.threads_mode='FIXED';scene.render.threads=4 + if not cpu: + prefs=bpy.context.preferences.addons['cycles'].preferences + for backend in ['METAL','OPTIX','CUDA','HIP','ONEAPI']: + try: + prefs.compute_device_type=backend + if backend=='METAL' and hasattr(prefs,'metalrt'): + prefs.metalrt='OFF';scene['metal_hardware_raytracing']='OFF' + prefs.get_devices() + available=[d for d in prefs.devices if d.type==backend] + if available: + for d in prefs.devices:d.use=d in available + scene.cycles.device='GPU';return backend + except (TypeError,ValueError,RuntimeError):continue + scene.cycles.device='CPU';scene.render.threads_mode='FIXED';scene.render.threads=4 + return 'CPU' diff --git a/astra_scene_test/Scene_04/scripts/render_sequence.py b/astra_scene_test/Scene_04/scripts/render_sequence.py new file mode 100644 index 0000000000000000000000000000000000000000..053ba2154ed30b906ebf32b5ebc7eac7fe2ecf43 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/render_sequence.py @@ -0,0 +1,78 @@ +"""Render a frozen saved scene in small, restartable Blender batches. + +Each completed frame is accepted only with matching scene/settings/image hashes +by render.py. Separate processes release Blender/Metal memory between batches. +""" +import argparse, datetime, hashlib, json, os, pathlib, subprocess, sys, time +ROOT = pathlib.Path(__file__).resolve().parents[1] +p = argparse.ArgumentParser(description=__doc__) +p.add_argument('--blender', default=os.environ.get('SCENE04_BLENDER', '/Applications/Blender.app/Contents/MacOS/Blender')) +p.add_argument('--label', default='final') +p.add_argument('--frames', default='all') +p.add_argument('--samples', type=int, default=128) +p.add_argument('--percent', type=int, default=100) +p.add_argument('--chunk', type=int, default=4) +p.add_argument('--retries', type=int, default=2) +p.add_argument('--raw', action='store_true') +p.add_argument('--cpu', action='store_true') +a = p.parse_args() +assert a.chunk > 0 and a.samples > 0 and 1 <= a.percent <= 100 +frames = list(range(1,129)) if a.frames == 'all' else [int(f) for f in a.frames.split(',')] +assert frames and len(set(frames)) == len(frames) and all(1 <= f <= 128 for f in frames) +assert a.label.replace('_','').isalnum(), 'Label must be a simple directory name' +scene = ROOT / 'scene_04.blend' +def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest() +scene_hash = sha(scene) +script_hash = sha(ROOT/'scripts/render.py') +helper_hashes = {name:sha(ROOT/'scripts'/name) for name in ['render_settings.py','render_device.py']} +log_root = ROOT/'logs'/('batches_'+a.label) +log_root.mkdir(parents=True, exist_ok=True) +run_id = datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ') +def event(kind, **data): + row = {'utc': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'event': kind, 'run': run_id, + 'label': a.label, 'blend_sha256': scene_hash, **data} + with (ROOT/'logs/batch_operations.jsonl').open('a') as f: f.write(json.dumps(row)+'\n') + print(json.dumps(row), flush=True) +def verified(fr): + records = ROOT/'logs/render_operations.jsonl' + row = None + if records.exists(): + for line in records.read_text().splitlines(): + r = json.loads(line) + if r.get('label') == a.label and r.get('frame') == fr: row = r + target = ROOT/'renders'/a.label/f'{fr:04d}.png' + return bool(row and row.get('render_script_sha256')==script_hash and row.get('render_helper_sha256')==helper_hashes and target.exists() and row.get('blend_sha256') == scene_hash + and row.get('samples') == a.samples and row.get('percent') == a.percent and row.get('sha256') == sha(target) + and (not a.raw or (target.parent/'linear'/f'{fr:04d}_Raw.exr').exists())) +event('sequence_start', frames=frames, samples=a.samples, percent=a.percent, chunk=a.chunk) +for offset in range(0, len(frames), a.chunk): + chunk = frames[offset:offset+a.chunk] + for attempt in range(a.retries+1): + assert sha(ROOT/'scripts/render.py')==script_hash and all(sha(ROOT/'scripts'/name)==digest for name,digest in helper_hashes.items()), 'Render scripts changed during the sequence' + assert sha(scene) == scene_hash, 'Saved scene changed during the render. Stop to avoid mixing versions.' + missing = [fr for fr in chunk if not verified(fr)] + if not missing: break + log_path = log_root/f'{run_id}_{chunk[0]:04d}-{chunk[-1]:04d}_try{attempt+1}.log' + command = [a.blender, '-b', str(scene), '--log', 'render', '--log-level', 'info', '--python-exit-code', '1', '--python', str(ROOT/'scripts/render.py'), + '--', a.label, ','.join(map(str, missing)), str(a.samples), str(a.percent), 'resume'] + if a.raw: command.append('raw') + if a.cpu: command.append('cpu') + event('batch_start', frames=missing, attempt=attempt+1, log=str(log_path.relative_to(ROOT))) + started = time.monotonic() + with log_path.open('w') as log: + child = subprocess.Popen(command, cwd=ROOT, stdout=log, stderr=subprocess.STDOUT) + try: code = child.wait() + except KeyboardInterrupt: + child.terminate() + try: child.wait(timeout=15) + except subprocess.TimeoutExpired: child.kill(); child.wait() + event('sequence_interrupted', pid=child.pid) + raise + remaining = [fr for fr in chunk if not verified(fr)] + event('batch_end', returncode=code, seconds=time.monotonic()-started, missing=remaining) + if not remaining: break + if attempt == a.retries: + raise RuntimeError(f'Batch failed after {a.retries+1} attempts; inspect {log_path}. Missing {remaining}') +assert sha(scene) == scene_hash and all(verified(fr) for fr in frames) +event('sequence_verified', count=len(frames)) +print('BATCH_SEQUENCE_COMPLETE', a.label, len(frames), flush=True) diff --git a/astra_scene_test/Scene_04/scripts/render_settings.py b/astra_scene_test/Scene_04/scripts/render_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..e82cbab549b3f3035a0e9076d3382ca0f2366075 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/render_settings.py @@ -0,0 +1,10 @@ +"""Common sampling settings for the saved scene, renders and verification renders.""" +def apply_quality(scene,samples,percent=100): + scene.cycles.samples=samples + scene.cycles.adaptive_threshold=.008 if samples>=512 else .012 if samples>=256 else .02 if samples>=128 else .035 + scene.cycles.adaptive_min_samples=128 if samples>=512 else 64 if samples>=256 else 32 if samples>=128 else 16 + scene.cycles.seed=404;scene.cycles.use_animated_seed=False + scene.cycles.use_denoising=True;scene.cycles.denoiser='OPENIMAGEDENOISE';scene.cycles.denoising_use_gpu=False + scene.cycles.use_auto_tile=True;scene.cycles.tile_size=512;scene.cycles.debug_use_compact_bvh=True + scene.render.use_persistent_data=False;scene.render.resolution_percentage=percent + if hasattr(scene.render,'compositor_device'):scene.render.compositor_device='CPU' diff --git a/astra_scene_test/Scene_04/scripts/review_completed_final.py b/astra_scene_test/Scene_04/scripts/review_completed_final.py new file mode 100644 index 0000000000000000000000000000000000000000..7e3dc92ce28e6afb0b850d7492034ea20fc36b9d --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/review_completed_final.py @@ -0,0 +1,24 @@ +"""Make incremental review sheets from hash-verified frozen final frames only.""" +from pathlib import Path +import hashlib,json +from PIL import Image,ImageDraw +ROOT=Path(__file__).resolve().parents[1] +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +blend=sha(ROOT/'scene_04.blend');rows={} +for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines(): + r=json.loads(line) + if r.get('label')=='final':rows[r['frame']]=r +valid={fr:r for fr,r in rows.items() if r.get('blend_sha256')==blend and (ROOT/f'renders/final/{fr:04d}.png').exists() and sha(ROOT/f'renders/final/{fr:04d}.png')==r['sha256']} +out=ROOT/'renders/comparisons/final_progress';out.mkdir(exist_ok=True,parents=True);ready=[] +for block in range(8): + frames=list(range(block*16+1,block*16+17)) + if not all(fr in valid for fr in frames):continue + ready.append(block+1);board=Image.new('RGB',(1280,1632),(17,18,19));draw=ImageDraw.Draw(board) + for n,fr in enumerate(frames): + x=(n%2)*640;y=(n//2)*204 + for shift,folder in [(0,'reference/frames'),(320,'renders/final')]: + with Image.open(ROOT/f'{folder}/{fr:04d}.png') as im:board.paste(im.convert('RGB').resize((320,180)),(x+shift,y+24)) + draw.text((x+7,y+6),f'{fr:03d} | {(fr-1)/30:.4f}s | SOURCE / FINAL',fill='white') + board.save(out/f'sequence_{block+1:02d}.jpg',quality=94) +(ROOT/'logs/final_progress_review.json').write_text(json.dumps({'blend_sha256':blend,'ready_blocks':ready,'frame_hashes':{str(fr):r['sha256'] for fr,r in valid.items()},'manual_review':'separate'},indent=2)) +print('VERIFIED_REVIEW_BLOCKS',ready) diff --git a/astra_scene_test/Scene_04/scripts/review_sequence.py b/astra_scene_test/Scene_04/scripts/review_sequence.py new file mode 100644 index 0000000000000000000000000000000000000000..66048d6d2a7eefc62625742fb2786aab202dfa55 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/review_sequence.py @@ -0,0 +1,36 @@ +"""Whole-sequence comparison sheets and independent temporal measurements. +Source and reconstruction are paired at identical frame indices. Optical-flow +warp residuals are diagnostics, not an oracle of visual fidelity. +""" +from pathlib import Path +import sys,json,hashlib,numpy as np,cv2 +from PIL import Image,ImageDraw +cv2.setNumThreads(1) +ROOT=Path(__file__).resolve().parents[1];label=sys.argv[1];folder=ROOT/'renders'/label +files=sorted(folder.glob('[0-9][0-9][0-9][0-9].png'));complete=len(files)==128 +out=ROOT/'renders/comparisons'/label;out.mkdir(parents=True,exist_ok=True) +records=[];prev={};hashes=[] +for n,path in enumerate(files): + fr=int(path.stem);views={key:Image.open(p).convert('RGB') for key,p in [('source',ROOT/f'reference/frames/{fr:04d}.png'),('render',path)]} + if n%16==0:board=Image.new('RGB',(1280,1632),(17,18,19));draw=ImageDraw.Draw(board) + x=(n%2)*640;y=((n%16)//2)*204 + board.paste(views['source'].resize((320,180)),(x,y+24));board.paste(views['render'].resize((320,180)),(x+320,y+24)) + draw.text((x+7,y+6),f'{fr:03d} | {(fr-1)/30:.4f}s | SOURCE / {label}',fill='white') + if n%16==15 or n==len(files)-1:board.save(out/f'sequence_{n//16+1:02d}.jpg',quality=94) + row={'frame':fr};hashes.append(hashlib.sha256(path.read_bytes()).hexdigest()) + for key,im in views.items(): + a=np.asarray(im.resize((640,360)),dtype=np.float32);gray=cv2.cvtColor(a,cv2.COLOR_RGB2GRAY);sky=a[40:100,290:320] + high=sky-cv2.GaussianBlur(sky,(0,0),1.3) + r={'sky_mean_rgb':sky.mean((0,1)).tolist(),'sky_highpass_std':float(high.std())} + if key in prev and fr==prev[key]['frame']+1: + pg=prev[key]['gray'];flow=cv2.calcOpticalFlowFarneback(pg,gray,None,.5,3,19,3,5,1.2,0) + yy,xx=np.mgrid[:360,:640];warped=cv2.remap(gray,(xx+flow[:,:,0]).astype('float32'),(yy+flow[:,:,1]).astype('float32'),cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT) + err=np.abs(warped-pg) + r['flow_warp_mae']=float(np.mean(err[30:335,15:625]));r['sky_mean_change']=float(np.mean(np.abs(sky-prev[key]['sky']))) + r['road_flow_median_xy']=np.median(flow[280:330,220:420],axis=(0,1)).tolist() + row[key]=r;prev[key]={'gray':gray,'sky':sky,'frame':fr} + records.append(row) +summary={'label':label,'frame_count':len(files),'complete_sequence':complete,'duplicate_frame_hash_count':len(hashes)-len(set(hashes)), + 'source_left_render_right':True,'frames':records,'interpretation':'Full indexed frame pairing; flow residuals include noise, water refraction and disocclusions. They do not certify matching geometry or real-time visual playback.'} +(ROOT/f'logs/temporal_review_{label}.json').write_text(json.dumps(summary,indent=2)) +print('SEQUENCE_REVIEW',label,'frames',len(files),'complete',complete,'duplicates',summary['duplicate_frame_hash_count']) diff --git a/astra_scene_test/Scene_04/scripts/road_surface.py b/astra_scene_test/Scene_04/scripts/road_surface.py new file mode 100644 index 0000000000000000000000000000000000000000..43af0f62e10e116c23c24785c7c27ed015bf44d8 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/road_surface.py @@ -0,0 +1,18 @@ +"""Road profile from crosswalk triangulation and distant headlight ground clearance. +Metric units remain approximate under the monocular camera calibration. +""" +def smoothstep(t): + t=max(0,min(1,t));return t*t*(3-2*t) +def hermite(y,y0,y1,z0,z1,m0,m1): + t=(y-y0)/(y1-y0);d=y1-y0 + return (2*t**3-3*t*t+1)*z0+(-2*t**3+3*t*t)*z1+(t**3-2*t*t+t)*d*m0+(t**3-t*t)*d*m1 +def ground_height(y,x=0): + a=.03158888;b=-.04587235;c=1.51451111 + if y<=30:z=0.0 + elif y<35:z=hermite(y,30,35,0,b*35+c,0,b) + elif y<=50:z=b*y+c + elif y<72:z=hermite(y,50,72,b*50+c,-3.14,b,0) + elif y<90:z=hermite(y,72,90,-3.14,-3.18,0,-.01) + elif y<110:z=hermite(y,90,110,-3.18,-3.50,-.01,-.02) + else:z=-3.50-.02*(y-110) + return z+a*x*smoothstep((y-25)/15) diff --git a/astra_scene_test/Scene_04/scripts/search_glare.py b/astra_scene_test/Scene_04/scripts/search_glare.py new file mode 100644 index 0000000000000000000000000000000000000000..aac892cc6851804fb5e77e470202e49fb9ff3723 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/search_glare.py @@ -0,0 +1,23 @@ +"""Controlled glare-energy sweep using raw scene-only radiance.""" +import bpy,pathlib,sys,json +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from install_optics import install_optics +S=bpy.context.scene +for o in list(bpy.data.objects): + if o.type!='CAMERA':bpy.data.objects.remove(o,do_unlink=True) +S.render.engine='CYCLES';S.cycles.device='CPU';S.cycles.samples=1;S.cycles.use_denoising=False;S.render.threads_mode='FIXED';S.render.threads=2 +S.render.resolution_x=1280;S.render.resolution_y=720;S.render.resolution_percentage=100 +S.render.image_settings.file_format='PNG';S.render.image_settings.color_mode='RGB';S.render.use_motion_blur=False +S.view_settings.view_transform='AgX';S.view_settings.look='AgX - Medium Low Contrast';S.view_settings.exposure=.7 +P=json.loads((ROOT/'assets/config.json').read_text()) +for fr in [1,65,128]: + S.frame_set(fr) + for glow,smear in [(0.15,.02),(.35,.05),(.65,.05),(.15,.12),(.35,.12),(.65,.12)]: + nt=install_optics(S,{**P,'glow_strength':glow,'smear_strength':smear});rl=next(n for n in nt.nodes if n.bl_idname=='CompositorNodeRLayers');targets=[l.to_socket for l in nt.links if l.from_node==rl] + im=bpy.data.images.load(str(ROOT/f'renders/optics_probe/linear/{fr:04d}_Raw.exr'),check_existing=True);im.colorspace_settings.name='Linear Rec.709' + nd=nt.nodes.new('CompositorNodeImage');nd.image=im;nt.nodes.remove(rl) + for target in targets:nt.links.new(nd.outputs['Image'],target) + folder=ROOT/'renders'/f'glare_{glow:.2f}_{smear:.2f}';folder.mkdir(exist_ok=True) + S.render.filepath=str(folder/f'{fr:04d}.png');bpy.ops.render.render(write_still=True) + print('GLARE_VARIANT',fr,glow,smear,flush=True) +print('GLARE_SWEEP_COMPLETE',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/session.py b/astra_scene_test/Scene_04/scripts/session.py new file mode 100644 index 0000000000000000000000000000000000000000..d2d9214ad8a7de2576252d3912f32ffcb195ceff --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/session.py @@ -0,0 +1,21 @@ +"""Append real UTC timestamps; never synthesize elapsed time or usage.""" +import datetime, hashlib, json, pathlib, sys, time +ROOT = pathlib.Path(__file__).resolve().parents[1] +LOG = ROOT / 'logs' +LOG.mkdir(exist_ok=True) +session = LOG / 'session.json' +if not session.exists(): + source = ROOT / 'night_front_wide_120fov.mp4' + session.write_text(json.dumps({'started_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(), + 'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(), + 'token_usage': None, 'cost': None, + 'usage_note': 'Host does not expose per-task token/cost counters through available tools.'}, indent=2)) +now = datetime.datetime.now(datetime.timezone.utc) +entry = {'utc': now.isoformat(), 'event': ' '.join(sys.argv[1:])} +with (LOG / 'operations.jsonl').open('a') as f: + f.write(json.dumps(entry) + '\n') +start = datetime.datetime.fromisoformat(json.loads(session.read_text())['started_utc']) +from elapsed_time import measure_elapsed +timing=measure_elapsed(now) +print(json.dumps({**entry, 'elapsed_hours': (now-start).total_seconds()/3600, + 'elapsed_excluding_pauses_hours':timing['meaningful_seconds']/3600})) diff --git a/astra_scene_test/Scene_04/scripts/set_linear_motion.py b/astra_scene_test/Scene_04/scripts/set_linear_motion.py new file mode 100644 index 0000000000000000000000000000000000000000..79f5d4dd787e86677866da92fa7db12ee732422b --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/set_linear_motion.py @@ -0,0 +1,9 @@ +"""Apply the same explicit linear interpolation used by build_scene.py.""" +import bpy,sys,pathlib +ROOT=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts')) +from animation_curves import linear,curves +for ob in bpy.data.objects: + if ob.animation_data and ob.animation_data.action:linear(ob) +for curve in curves(bpy.context.scene.camera):print('CAMERA_CURVE',curve.data_path,curve.array_index,len(curve.keyframe_points),{k.interpolation for k in curve.keyframe_points},flush=True) +bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'scene_04.blend')) +print('LINEAR_MOTION_SAVED',flush=True) diff --git a/astra_scene_test/Scene_04/scripts/track_ground.py b/astra_scene_test/Scene_04/scripts/track_ground.py new file mode 100644 index 0000000000000000000000000000000000000000..e069b33b6b25a203d228384077d480dec1d270aa --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/track_ground.py @@ -0,0 +1,33 @@ +"""Track observed crosswalk paint corners backward to constrain road geometry.""" +from pathlib import Path +import cv2,numpy as np,json,sys +from PIL import Image,ImageDraw +ROOT=Path(__file__).resolve().parents[1];sys.path.insert(0,str(ROOT/'scripts'));from camera_math import triangulate,project +frames=[cv2.imread(str(ROOT/f'reference/frames/{fr:04d}.png'),0) for fr in range(1,129)] +mask=np.zeros((720,1280),np.uint8) +cv2.fillPoly(mask,[np.array([[0,589],[218,573],[271,589],[0,636]]),np.array([[930,572],[1060,563],[1280,589],[1280,620],[1120,599]])],255) +points=cv2.goodFeaturesToTrack(frames[-1],maxCorners=90,qualityLevel=.04,minDistance=11,mask=mask).reshape(-1,2) +tracks=np.full((len(points),128,2),np.nan,np.float32);tracks[:,127]=points;valid=np.ones(len(points),bool) +for idx in range(126,-1,-1): + prev=tracks[:,idx+1].copy();prev[~valid]=0 + new,status,err=cv2.calcOpticalFlowPyrLK(frames[idx+1],frames[idx],prev[:,None,:],None,winSize=(21,21),maxLevel=3,criteria=(3,30,.01)) + back,bstatus,berr=cv2.calcOpticalFlowPyrLK(frames[idx],frames[idx+1],new,None,winSize=(21,21),maxLevel=3,criteria=(3,30,.01)) + valid&=(status.ravel()>0)&(bstatus.ravel()>0)&(np.linalg.norm(back[:,0]-prev,axis=1)<1)&(err.ravel()<26) + valid&=(new[:,0,0]>1)&(new[:,0,0]<1278)&(new[:,0,1]>1)&(new[:,0,1]<718) + tracks[valid,idx]=new[valid,0] +np.savez_compressed(ROOT/'reference/ground_tracks.npz',tracks=tracks) +rows=[] +for i,t in enumerate(tracks): + inds=np.flatnonzero(np.isfinite(t[:,0]));inds=inds[inds>=64][::4] + if len(inds)<5:continue + obs={int(k+1):t[k].tolist() for k in inds};q=triangulate(obs);errs=[float(np.linalg.norm(project(q,fr)-uv)) for fr,uv in obs.items()] + rows.append({'id':i,'xyz':q.tolist(),'first_frame':int(inds[0]+1),'mean_error':float(np.mean(errs)),'observations':obs}) +(ROOT/'reference/ground_tracks_fit.json').write_text(json.dumps(rows,indent=2)) +for fr in [65,97,113,128]: + im=Image.open(ROOT/f'reference/frames/{fr:04d}.png').convert('RGB');d=ImageDraw.Draw(im) + for i,t in enumerate(tracks): + if np.isfinite(t[fr-1,0]): + x,y=t[fr-1];d.ellipse((x-3,y-3,x+3,y+3),outline=(255,255,0));d.text((x+3,y-12),str(i),fill=(255,255,0)) + im.save(ROOT/f'reference/crops/ground_tracks_{fr:04d}.png') +print('GROUND_TRACKS',len(tracks),'fitted',len(rows)) +for r in rows:print(r['id'],r['first_frame'],np.round(r['xyz'],2),round(r['mean_error'],2)) diff --git a/astra_scene_test/Scene_04/scripts/track_reference.py b/astra_scene_test/Scene_04/scripts/track_reference.py new file mode 100644 index 0000000000000000000000000000000000000000..ae1d8e92aca0cf8d8f1d695178f9d5a88cb5c20f --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/track_reference.py @@ -0,0 +1,29 @@ +"""Track real image features for camera fitting; optical overlays are excluded.""" +from pathlib import Path +import cv2, numpy as np, json +from PIL import Image, ImageDraw +root=Path(__file__).resolve().parents[1] +files=sorted((root/'reference/frames').glob('*.png')) +gray=[cv2.imread(str(p),0) for p in files] +mask=np.full(gray[0].shape,255,np.uint8);mask[:50]=0;mask[640:]=0 +pts=cv2.goodFeaturesToTrack(gray[0],maxCorners=500,qualityLevel=.03,minDistance=12,mask=mask) +tracks=np.full((len(pts),len(files),2),np.nan); tracks[:,0]=pts[:,0];good=np.ones(len(pts),bool) +prev=pts.copy() +for i in range(1,len(files)): + new,st,err=cv2.calcOpticalFlowPyrLK(gray[i-1],gray[i],prev,None,winSize=(25,25),maxLevel=4, + criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,40,.005)) + back,bst,_=cv2.calcOpticalFlowPyrLK(gray[i],gray[i-1],new,None,winSize=(25,25),maxLevel=4) + good &= (st[:,0]>0)&(bst[:,0]>0)&(np.linalg.norm(back-prev,axis=(1,2))<1.5)&(err[:,0]<35) + good &= (new[:,0,0]>2)&(new[:,0,0]<1278)&(new[:,0,1]>2)&(new[:,0,1]<690) + tracks[good,i]=new[good,0];prev=new +np.savez_compressed(root/'reference/feature_tracks.npz',tracks=tracks) +im=Image.open(files[0]);d=ImageDraw.Draw(im) +count=0 +for n,tr in enumerate(tracks): + tr=tr[np.isfinite(tr[:,0])] + if len(tr)<100 or np.linalg.norm(tr[-1]-tr[0])<20:continue + count+=1;c=(255,190,30) + d.line([tuple(p) for p in tr],fill=c,width=1) + d.text(tuple(tr[0]),str(n),fill='cyan') +im.save(root/'reference/tracks.jpg') +print('Tracked',len(pts),'features;',count,'long moving tracks') diff --git a/astra_scene_test/Scene_04/scripts/track_signals.py b/astra_scene_test/Scene_04/scripts/track_signals.py new file mode 100644 index 0000000000000000000000000000000000000000..71d830cd0637c1d0faf2e71954bfc9c55903d3c1 --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/track_signals.py @@ -0,0 +1,24 @@ +"""Track the visible green emission, independently of the original LK corner.""" +from pathlib import Path +import cv2,numpy as np,json,sys +cv2.setNumThreads(1) +ROOT=Path(__file__).resolve().parents[1];T=np.load(ROOT/'reference/feature_tracks.npz')['tracks'] +label=sys.argv[1] if len(sys.argv)>1 else 'reference';folder=ROOT/'reference/frames' if label=='reference' else ROOT/'renders'/label +out={} +for name,idx in [('left',122),('right',85)]: + rows={} + for fr in range(1,129): + path=folder/f'{fr:04d}.png' + if not path.exists():continue + im=cv2.resize(cv2.imread(str(path)),(1280,720))[:,:,::-1].astype(float);center=T[idx,fr-1] + xx,yy=map(int,center);x0=max(0,xx-32);x1=min(1280,xx+33);y0=max(0,yy-32);y1=min(720,yy+33) + a=im[y0:y1,x0:x1];r,g,b=np.moveaxis(a,2,0) + score=np.clip(g-r-25,0,None)+np.clip(b-r-20,0,None) + score*=((g-r)>25)&((b-r)>20)&(g>95) + gy,gx=np.mgrid[y0:y1,x0:x1];score*=np.exp(-((gx-center[0])**2+(gy-center[1])**2)/(2*18**2)) + if score.sum()<1:continue + rows[fr]=[float((gx*score).sum()/score.sum()),float((gy*score).sum()/score.sum())] + out[name]=rows +dest=ROOT/'reference/source_signal_tracks.json' if label=='reference' else ROOT/f'logs/render_signal_tracks_{label}.json';dest.write_text(json.dumps(out,indent=2)) +for name,rows in out.items(): + print(name,len(rows),{fr:np.round(rows[fr],2).tolist() for fr in [1,33,65,97,128] if fr in rows}) diff --git a/astra_scene_test/Scene_04/scripts/verify.py b/astra_scene_test/Scene_04/scripts/verify.py new file mode 100644 index 0000000000000000000000000000000000000000..17135f5a2447ccdf9d5630f1b9484a273072145c --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/verify.py @@ -0,0 +1,115 @@ +"""Independent delivery checks. Uses only stdlib and bundled FFmpeg.""" +import pathlib,sys,json,hashlib,subprocess,re,datetime,struct +from fractions import Fraction +ROOT=pathlib.Path(__file__).resolve().parents[1] +def load(p):return json.loads((ROOT/p).read_text()) +def sha(p):return hashlib.sha256((ROOT/p).read_bytes()).hexdigest() +def probe(p): + r=subprocess.run([str(ROOT/'tools/ffmpeg'),'-hide_banner','-nostdin','-xerror','-err_detect','explode','-i',str(ROOT/p),'-map','0:v:0','-vf','showinfo','-f','null','-'],capture_output=True,text=True) + assert r.returncode==0,(p,r.stderr[-3000:]) + frames=re.findall(r'\bn:\s*(\d+)\s+pts:\s*(-?\d+)\s+pts_time:([\d.\-]+).*?\bs:(\d+)x(\d+)',r.stderr) + assert frames,(p,'No decoded video frames') + fps=float(re.search(r'(\d+(?:\.\d+)?) fps',r.stderr).group(1)) + assert len({(f[3],f[4]) for f in frames})==1,'Decoded resolution changes within video' + packets=subprocess.run([str(ROOT/'tools/ffmpeg'),'-v','error','-nostdin','-i',str(ROOT/p),'-map','0:v:0','-c','copy','-f','framecrc','-'],capture_output=True,text=True) + assert packets.returncode==0,packets.stderr + tb=re.search(r'^#tb 0:\s*(\d+/\d+)',packets.stdout,re.M);assert tb,'No packet timebase' + timebase=Fraction(tb.group(1));intervals=[] + for line in packets.stdout.splitlines(): + if not line or line.startswith('#'):continue + fields=[v.strip() for v in line.split(',')] + assert fields[0]=='0' and len(fields)>=6 + intervals.append((int(fields[2])*timebase,int(fields[3])*timebase)) + intervals.sort() + assert len(intervals)==len(frames) and intervals[0][0]==0 + assert all(duration>0 for _,duration in intervals) + assert all(start+duration==following[0] for (start,duration),following in zip(intervals,intervals[1:])), 'Gap or overlap in encoded packet timeline' + result={'count':len(frames),'fps':fps,'width':int(frames[0][3]),'height':int(frames[0][4]),'timestamps':[float(f[2]) for f in frames], + 'audio_streams':len(re.findall(r'Stream #0:\d+.*Audio:',r.stderr)), + 'packet_timebase':str(timebase),'packet_intervals':[[str(t),str(d)] for t,d in intervals], + 'duration_fraction':str(sum(intervals[-1])),'duration_seconds':float(sum(intervals[-1]))} + (ROOT/f'logs/probe_{pathlib.Path(p).stem}.json').write_text(json.dumps(result,indent=2)) + return result +def same_timing(a,b): + assert a['count']==b['count'];assert a['fps']==b['fps'];assert max(abs(x-y) for x,y in zip(a['timestamps'],b['timestamps']))<.0001 + assert a['packet_intervals']==b['packet_intervals'] and a['duration_fraction']==b['duration_fraction'] +def assert_no_movie(data): + assert data['movie_sequence_images']==[],'Movie/sequence texture found' + assert data.get('reference_image_matches',[])==[],'Packed or external source frame used by scene' +def reference(): + m=load('reference/metadata.json');r=probe('night_front_wide_120fov.mp4') + assert sha('night_front_wide_120fov.mp4')==load('logs/session.json')['source_sha256'] + assert r['count']==m['frame_count']==len(m['frames']) + assert [r['width'],r['height']]==[m['width'],m['height']] + assert r['audio_streams']==m['audio_streams']==0 + for f in m['frames']:assert sha(f['path'])==f['sha256'] + assert len(list((ROOT/'reference/frames').glob('*.png')))==r['count'] + print('REFERENCE_VERIFIED') +def scene(): + a=load('logs/reopened_scene_audit.json');assert a['blend_sha256']==sha('scene_04.blend') + assert a['mesh_object_count']>100 and a['curve_object_count']>10 and a['material_count']>15 + assert a['camera_animated'];assert_no_movie(a) + assert a['room_lights'] and min(r['height_above_road'] for r in a['room_lights'])>1.5,'A room ceiling light was misplaced at street level' + cc=a['camera_curves'];assert len(cc)==6 and all(c['keyframes']==128 and c['interpolations']==['LINEAR'] for c in cc);assert not a['missing_external_images'];assert not a.get('missing_external_fonts',[]) + m=load('reference/metadata.json');assert a['frame_start']==1 and a['frame_end']==m['frame_count'] + expected=[m['width'],m['height']] + assert a.get('output_resolution',a['resolution'])==expected and a['percentage']==100 + overscan=a.get('optical_overscan',1) + assert a['resolution']==[round(v*overscan) for v in expected] + if overscan>1: + crop=a['optical_crop'];assert crop and crop['connected_to_output'] and not crop['alpha_crop'] + assert [crop['x'],crop['y']]==[(a['resolution'][i]-expected[i])//2 for i in range(2)] + native=a['native_file_output'];assert native and native['format']=='PNG' and native['connected_to_crop'] + assert native['items']==['####'] and not a['automatic_output_enabled'] + sensor=next(im for im in a['images'] if pathlib.Path(im['filepath']).name=='sensor_coordinates.png') + assert sensor['png_header']['bit_depth']==16,'Numeric sensor map was quantized to 8 bits' + assert [sensor['png_header']['width'],sensor['png_header']['height']]==a['resolution'] + assert a['fps']/a['fps_base']==30 + try:assert_no_movie({'movie_sequence_images':['positive-control.mp4']}) + except AssertionError:pass + else:raise AssertionError('Movie texture negative control did not fail') + try:assert_no_movie({'movie_sequence_images':[],'reference_image_matches':['source-frame.png']}) + except AssertionError:pass + else:raise AssertionError('Static source-frame positive control did not fail') + yy=[k['location'][1] for k in a['camera_samples']];assert all(x10000 + print('SCENE_VERIFIED') +def videos(): + a=probe('night_front_wide_120fov.mp4');b=probe('final.mp4');c=probe('comparison.mp4') + same_timing(a,b);same_timing(a,c) + assert [a['width'],a['height']]==[b['width'],b['height']] + assert [c['width'],c['height']]==[a['width']*2,a['height']] + assert a['audio_streams']==b['audio_streams']==c['audio_streams']==0 + assert len(list((ROOT/'renders/final').glob('*.png')))==a['count'] + scene_hash=sha('scene_04.blend') + config=load('assets/config.json');script_hash=sha('scripts/render.py');helpers={name:sha('scripts/'+name) for name in ['render_settings.py','render_device.py']} + rows={r['frame']:r for r in [json.loads(line) for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines()] if r['label']=='final'} + for fr in range(1,a['count']+1): + row=rows[fr];assert row['blend_sha256']==scene_hash and row['percent']==100 + assert row['samples']==config['samples'] and row['render_script_sha256']==script_hash and row['render_helper_sha256']==helpers + assert row['sha256']==sha(f'renders/final/{fr:04d}.png') + assert row.get('output_resolution')==[a['width'],a['height']] + + print('VIDEOS_VERIFIED') +def reopen(): + a=load('logs/reopened_scene_audit.json');assert a['blend_sha256']==sha('scene_04.blend') + rows={r['frame']:r for r in [json.loads(line) for line in (ROOT/'logs/render_operations.jsonl').read_text().splitlines()] if r['label']=='reopen'} + for fr in [1,65,128]: + row=rows[fr];assert row['blend_sha256']==a['blend_sha256'] and row['percent']==100 + assert row['samples']==a['samples'] and row['render_script_sha256']==sha('scripts/render.py') + assert row['render_helper_sha256']=={name:sha('scripts/'+name) for name in ['render_settings.py','render_device.py']} + assert row['sha256']==sha(f'renders/reopen/{fr:04d}.png') + p=ROOT/f'renders/reopen/{fr:04d}.png';assert p.stat().st_size>20000 + header=p.read_bytes()[:24];assert header[:8]==b'\x89PNG\r\n\x1a\n';assert struct.unpack('>II',header[16:24])==(1280,720) + assert not a['missing_external_images'];assert not a.get('missing_external_fonts',[]);print('REOPEN_VERIFIED') +def provenance(): + from elapsed_time import measure_elapsed + s=load('logs/session.json');timing=measure_elapsed();meaningful=timing['meaningful_seconds'] + assert meaningful>=5*3600,f'Only {meaningful/3600:.3f} hours excluding recorded interruptions; five hours required.' + (ROOT/'logs/elapsed_time.json').write_text(json.dumps(timing,indent=2)) + assert 'token_usage' in s and 'cost' in s + assert len((ROOT/'logs/operations.jsonl').read_text().splitlines())>=8 + assert len(list((ROOT/'checkpoints').glob('*.blend')))>=2 + for p in ['scripts/build_scene.py','scripts/render.py','scripts/compare.py','scripts/verify.py','README.md']:assert (ROOT/p).is_file() + print('PROVENANCE_VERIFIED') +if __name__=='__main__':globals()[sys.argv[1]]() diff --git a/astra_scene_test/Scene_04/scripts/verticals.py b/astra_scene_test/Scene_04/scripts/verticals.py new file mode 100644 index 0000000000000000000000000000000000000000..be528d784e379ed6bb774fe37797d4fef8f5b00f --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/verticals.py @@ -0,0 +1,21 @@ +import pathlib,cv2,numpy as np,json,math +from PIL import Image,ImageDraw +ROOT=pathlib.Path(__file__).resolve().parents[1] +im=cv2.imread(str(ROOT/'reference/frames/0001.png'));g=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) +g=cv2.createCLAHE(clipLimit=2,tileGridSize=(8,8)).apply(g);edges=cv2.Canny(g,45,110) +lines=cv2.HoughLinesP(edges,1,np.pi/720,22,minLineLength=30,maxLineGap=12) +rows=[] +for x1,y1,x2,y2 in lines.reshape(-1,4): + if abs(y1-y2)<30 or abs(y1-y2)400 or max(y1,y2)>590:continue + if not (max(x1,x2)<480 or min(x1,x2)>800):continue + if max(x1,x2)<480 and (x2-x1)*(y2-y1)>0:continue + if min(x1,x2)>800 and (x2-x1)*(y2-y1)<0:continue + a,b=np.cross([x1,y1,1],[x2,y2,1])[:2];line=np.cross([x1,y1,1],[x2,y2,1]);line=line/np.linalg.norm(line[:2]) + rows.append({'p':[int(x1),int(y1),int(x2),int(y2)],'line':line.tolist(),'length':math.hypot(x1-x2,y1-y2)}) +out=Image.fromarray(cv2.cvtColor(im,cv2.COLOR_BGR2RGB));d=ImageDraw.Draw(out) +for i,r in enumerate(rows): + x1,y1,x2,y2=r['p'];d.line([(x1,y1),(x2,y2)],fill='cyan',width=2);d.text((x1,y1),str(i),fill='yellow') +out.save(ROOT/'reference/vertical_segments.jpg',quality=95) +(ROOT/'reference/vertical_segments.json').write_text(json.dumps(rows,indent=2)) +print('Vertical segments',len(rows)) diff --git a/astra_scene_test/Scene_04/scripts/water_caustics.py b/astra_scene_test/Scene_04/scripts/water_caustics.py new file mode 100644 index 0000000000000000000000000000000000000000..94793796e11b45b3a460f411bc428ab851691c1d --- /dev/null +++ b/astra_scene_test/Scene_04/scripts/water_caustics.py @@ -0,0 +1,81 @@ +"""Deterministic asymmetric light-spread fields for separate windshield regions. +Only numeric optical functions are used. Source pixels never enter these maps. +""" +from pathlib import Path +import numpy as np,json +from PIL import Image,ImageDraw,ImageFilter +ROOT=Path(__file__).resolve().parents[1];H,W=720,1280 +P=json.loads((ROOT/'assets/config.json').read_text());factor=P.get('optical_overscan',1) +px=round(W*(factor-1)/2);py=round(H*(factor-1)/2);y,x=np.mgrid[-py:H+py,-px:W+px] +def save_mask(name,m):Image.fromarray((np.clip(m,0,1)*255).astype(np.uint8)).save(ROOT/'assets'/name) +# A localized upper-left water patch becomes prominent as the paired lamp nears it. +left=.015+.95*np.exp(-((x-427)/60)**2-((y-315)/55)**2) +left*=1/(1+np.exp((x-625)/22));left*=np.clip((620-y)/100,0,1) +right=.90*np.exp(-((x-806)/90)**2-((y-306)/105)**2) +right*=1/(1+np.exp((640-x)/30));right*=np.clip((620-y)/100,0,1) +lower=.88*np.exp(-((x-512)/107)**2-((y-440)/57)**2) +opening_spread=P.get('opening_input_spread',1) +opening=np.exp(-((x-3)/(88*opening_spread))**2-((y-35)/(92*opening_spread))**2) +save_mask('windshield_glare_opening.png',opening) +save_mask('windshield_lower_defocus.png',np.clip((y-(600+65*(1-((x-640)/710)**2)))/45,0,1)) +save_mask('windshield_glare_left.png',left);save_mask('windshield_glare_right.png',right);save_mask('windshield_glare_lower.png',lower) +save_mask('windshield_glare_right_lower.png',.95*np.exp(-((x-731)/88)**2-((y-401)/76)**2)) +save_mask('windshield_glare_right_upper.png',np.exp(-((x-1180)/165)**2-((y-115)/160)**2)) +n=385;v,u=np.mgrid[-192:193,-192:193];rad=np.hypot(u,v) +def membrane(points,weight=.08): + im=Image.new('L',(n,n));d=ImageDraw.Draw(im);smooth=[];p=np.array(points,dtype=float) + for j in range(len(p)): + p0,p1,p2,p3=[p[k%len(p)] for k in [j-1,j,j+1,j+2]] + for t in np.linspace(0,1,18,endpoint=False): + q=.5*((2*p1)+(-p0+p2)*t+(2*p0-5*p1+4*p2-p3)*t*t+(-p0+3*p1-3*p2+p3)*t*t*t);smooth.append(tuple(q+192)) + d.polygon(smooth,fill=255) + broad=np.asarray(im.filter(ImageFilter.GaussianBlur(3.2)),dtype=float)/255 + edge=np.maximum(0,broad-np.asarray(im.filter(ImageFilter.GaussianBlur(8.0)),dtype=float)/255) + return broad*weight+edge*.22 +def waterline(points,amp=.12,width=1): + im=Image.new('L',(n,n));d=ImageDraw.Draw(im);p=np.array(points,dtype=float);smooth=[] + for j in range(len(p)-1): + p0,p1,p2,p3=[p[max(0,min(len(p)-1,k))] for k in [j-1,j,j+1,j+2]] + for t in np.linspace(0,1,24,endpoint=False): + q=.5*((2*p1)+(-p0+p2)*t+(2*p0-5*p1+4*p2-p3)*t*t+(-p0+3*p1-3*p2+p3)*t*t*t);smooth.append(tuple(q+192)) + smooth.append(tuple(p[-1]+192));d.line(smooth,fill=255,width=width) + return np.asarray(im.filter(ImageFilter.GaussianBlur(1.5)),dtype=float)/255*amp + +def ring(cx,cy,a,b,angle,amp): + xx=(u-cx)*np.cos(angle)+(v-cy)*np.sin(angle);yy=-(u-cx)*np.sin(angle)+(v-cy)*np.cos(angle) + theta=np.arctan2(yy/b,xx/a);r=np.hypot(xx/a,yy/b) + outline=1+.055*np.sin(theta*5+.7)+.035*np.sin(theta*9-.2) + return amp*(np.exp(-((r-outline)/.17)**2)+.46*np.exp(-r**4)) +def streak(angle,amp,length,offset=0): + along=u*np.cos(angle)+v*np.sin(angle);across=-u*np.sin(angle)+v*np.cos(angle)-offset + return amp*np.exp(-(across/1.45)**2)*np.exp(-((along+length*.28)/(length*.63))**4) +# A light core plus dimmer, displaced meniscus lobes. The asymmetry deliberately +# avoids a symmetric starburst or a solid white disk. +leftk=.08*np.exp(-(rad/19)**2)+.018*np.exp(-(rad/51)**2) +leftk+=membrane([(-66,-45),(-35,-105),(-5,-139),(12,-111),(50,-75),(103,-13),(98,16),(62,37),(15,24),(-27,3)],.05) +for k in range(6): + leftk+=waterline([(-53+k*3,-42-k*13),(-15,-59-k*12),(38,-30-k*13),(92,-6-k*4)],.11-k*.008,2) +leftk+=waterline([(-5,-139),(21,-92),(50,-61),(86,-37),(104,-9),(99,14),(70,29)],.19,2) +for angle,amp,off in [(-.43,.08,0),(-.47,.055,5),(-.51,.04,-5)]:leftk+=streak(angle,amp,120,off) +rightk=.10*np.exp(-(rad/22)**2)+.018*np.exp(-(rad/58)**2) +rightk+=membrane([(-48,13),(-86,45),(-74,100),(-42,133),(-15,89),(-21,51)],.07) +for k in range(4):rightk+=waterline([(-45-k*7,23+k*10),(-25-k*8,50+k*12),(-35-k*7,104+k*5)],.12,2) +for angle,amp,off in [(.39,.10,0),(.43,.075,6),(.48,.045,-7)]:rightk+=streak(angle,amp,148,off) +rightlowerk=.13*np.exp(-(rad/29)**2)+membrane([(9,15),(40,28),(65,73),(41,118),(12,84),(-8,38)],.07) +rightlowerk+=waterline([(15,21),(46,46),(56,80),(39,114)],.18,2) +rightupperk=.10*np.exp(-(rad/32)**2)+membrane([(32,58),(73,82),(102,158),(76,186),(46,149),(20,93)],.07) +for k in range(5):rightupperk+=waterline([(-42+k*8,-68),(2+k*8,-16),(50+k*8,39),(95+k*8,112)],.13-k*.01,2) +lowerk=.25*np.exp(-(rad/18)**2)+.02*np.exp(-(rad/42)**2) +for angle,amp,off in [(.06,.16,-2),(.12,.10,5),(.18,.07,-6)]:lowerk+=streak(angle,amp,110,off) +def save_kernel(name,k,radius): + k*=np.clip((191-radius)/18,0,1);rgb=np.stack([k,k*.995,k*.98],axis=2);rgb/=rgb.max();Image.fromarray((rgb[::-1,::-1]*255).astype(np.uint8)).save(ROOT/'assets'/name) +for name,k in [('wet_glass_psf_right_lower.png',rightlowerk),('wet_glass_psf_right_upper.png',rightupperk),('wet_glass_psf_left.png',leftk),('wet_glass_psf.png',rightk),('wet_glass_psf_lower.png',lowerk)]: + save_kernel(name,k,rad) +# The opening smear spans more sensor pixels than the smaller central caustics. +# Preserve its shape in normalized coordinates while extending physical support. +opening_scale=P.get('opening_kernel_scale',1) +extent=int(np.ceil(192*opening_scale));v,u=np.mgrid[-extent:extent+1,-extent:extent+1] +v=v/opening_scale;u=u/opening_scale;rad=np.hypot(u,v) +openingk=.12*np.exp(-(rad/25)**2)+ring(8,80,28,142,P.get('opening_ring_angle',.48),.075)+streak(-.95,.19,182,0)+streak(-.91,.08,170,7) +save_kernel('wet_glass_psf_opening.png',openingk,rad) +print('WATER_CAUSTICS_COMPLETE') diff --git a/astra_scene_test/X_03/.DS_Store b/astra_scene_test/X_03/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..57bfc816c48128b30112142490031cb8342c87d3 Binary files /dev/null and b/astra_scene_test/X_03/.DS_Store differ diff --git a/astra_scene_test/X_03/.oxlintrc.json b/astra_scene_test/X_03/.oxlintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..b25b8b725eee93da8f12ab5099055ee07b9257c3 --- /dev/null +++ b/astra_scene_test/X_03/.oxlintrc.json @@ -0,0 +1,60 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "eslint", + "typescript", + "unicorn", + "oxc", + "react", + "import", + "jsx-a11y", + "nextjs" + ], + "categories": { + "correctness": "error" + }, + "env": { + "builtin": true, + "browser": true, + "node": true + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "ignorePatterns": [ + ".next/**", + ".vinext/**", + "out/**", + "build/**", + "dist/**", + "coverage/**", + "next-env.d.ts", + "research/**", + "components/ui/**", + "hooks/use-mobile.ts", + "public/ktx-basis/**" + ], + "rules": { + "no-array-constructor": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "import/no-anonymous-default-export": "warn", + "react/display-name": "error", + "react/jsx-no-comment-textnodes": "error", + "react/no-unescaped-entities": "error", + "react/react-compiler": "error", + "react/require-render-return": "error", + "react/rules-of-hooks": "error", + "typescript/ban-ts-comment": "error", + "typescript/no-deprecated": "error", + "typescript/no-empty-object-type": "error", + "typescript/no-explicit-any": "error", + "typescript/no-namespace": "error", + "typescript/no-require-imports": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-function-type": "error" + } +} diff --git a/astra_scene_test/X_03/ASSETS.md b/astra_scene_test/X_03/ASSETS.md new file mode 100644 index 0000000000000000000000000000000000000000..a3b9bf5d98e760a253b49674ad7cb61e6fdcc68e --- /dev/null +++ b/astra_scene_test/X_03/ASSETS.md @@ -0,0 +1,25 @@ +# Asset provenance + +The following source assets are CC0 assets from Poly Haven. Source metadata, download checksums and transformation scripts are retained in the project research records. The forest's procedural meshes and shaders were authored for this build. + +| Source | Use and local derivation | +| --- | --- | +| [Bark Brown 02](https://polyhaven.com/a/bark_brown_02) | Bark color, normal and roughness; species-specific shading | +| [Leaves Forest Ground](https://polyhaven.com/a/leaves_forest_ground) | Forest floor color, normal and roughness with stochastic sampling | +| [Fir Tree 01](https://polyhaven.com/a/fir_tree_01) | Three scanned fir forms; restored source UVs and separate alpha; optimized distant meshes | +| [Tree Small 02](https://polyhaven.com/a/tree_small_02) | Broadleaf tree and leaf atlas; intact connected leaf components retained for both LODs | +| [Rock Moss Set 01](https://polyhaven.com/a/rock_moss_set_01) | Six rock variants, 4K surface detail, three mesh LODs | +| [Dead Tree Trunk](https://polyhaven.com/a/dead_tree_trunk) | Fallen wood, 4K surface detail, three mesh LODs | +| [Tree Stump 01](https://polyhaven.com/a/tree_stump_01) | Weathered stumps, three mesh LODs | +| [Fern 02](https://polyhaven.com/a/fern_02) | Supplementary scanned fern variants | +| [Nettle Plant](https://polyhaven.com/a/nettle_plant) | Scanned understory variation | +| [Weed Plant 02](https://polyhaven.com/a/weed_plant_02) | Small scanned weeds | +| [Moss 01](https://polyhaven.com/a/moss_01) | Curved moss shoots and an orthographic diffuse/normal bake of the same source | + +Relevant transformations include `scripts/export-trees-v2.py`, `scripts/export-fir-blend.py`, `scripts/export-intact-leaves.py`, `scripts/combine-intact-trees.mjs`, `scripts/build-asset-lods.mjs` and `scripts/bake-moss.py`. The fir export retains the source Col mask as a separate bark transition, including its authored object-space projection scale. Geometry simplification uses meshoptimizer (MIT); runtime rendering uses Three.js (MIT). Their license files remain in their installed packages. + +No AI-generated raster scenery or pre-rendered forest background is used in the real-time scene. + +The four 4K rock/deadwood color and normal maps also have full-resolution KTX2 derivatives, encoded with Khronos KTX-Software 4.4.2 (UASTC quality 3, Zstandard level 9, complete mip chains). Original JPEGs remain alongside them. `scripts/encode-prop-textures.py`, `research/compressed-props/encoding.json` and `accepted-assets.json` retain settings, sizes and hashes. The runtime selects a supported GPU texture format without lowering source dimensions. + +`public/ktx-basis/` contains the unchanged Basis Universal JavaScript/WASM decoder supplied with Three.js 0.180.0, licensed under Apache 2.0; its license and upstream reference are included in that folder. Decoder workers are released after photographic assets finish loading. diff --git a/astra_scene_test/X_03/README.md b/astra_scene_test/X_03/README.md new file mode 100644 index 0000000000000000000000000000000000000000..957d4490a97eb1048010fe2cf4208cf119dc92cf --- /dev/null +++ b/astra_scene_test/X_03/README.md @@ -0,0 +1,50 @@ +# Forest + +浏览器中的实时森林,整个画面只有森林。 + +点击画面后用鼠标环顾;W/A/S/D 移动,Q/E 或空格升降,Shift 加速,R 返回起点,Escape 释放鼠标。触屏左侧拖动移动,右侧拖动环顾。 + +需要支持 WebGL 2 的现代浏览器。画面会根据持续帧耗时调整内部渲染分辨率,并以完整输出分辨率累积细节。自由相机可在中央约 210 × 210 米范围内探索,远处森林延伸到视野之外。 + +## 本地运行 + +使用 Node.js 22.13 或更新版本: + +```sh +npm ci +npm run dev +``` + +```sh +npm run build +``` + +静态输出位于 `dist/client`。构建后可用普通静态服务器运行: + +```sh +python3 -m http.server 5174 --bind 127.0.0.1 --directory dist/client +``` + +打开 http://127.0.0.1:5174/。不要直接双击 HTML 文件。 + +本地另存的最终运行包为 `outputs/forest-final.tar.gz`;解压后静态网站位于其中的 `dist/`。运行包、截图和原始研究文件保留在本机,不纳入 Git 源码提交。 + +## 实现与记录 + +树冠、枝条、草、蕨类、灌木、酢浆草、野花、落叶、菌类等使用实际三维网格;树木和植物有多级细节、独立阴影批次与空间裁剪。岩石、枯木和部分植物使用经过优化的 CC0 扫描资产。近处树叶在工作线程中细化,加载完成前保留普通树叶。 + +光照包含树冠遮蔽、接触阴影、随相机移动的太阳阴影、体积散射、薄叶透光、分层雾和细小花粉。没有额外页面、营销文字或操作面板。 + +- [操作日志](log.md) 与 [机器可读操作记录](logs/operations.jsonl) +- [耗时、token 与成本记录](logs/耗时与成本.md) +- [收尾说明与已知限制](logs/closeout.md) +- [验收账本](GATES.md) +- [资产来源](ASSETS.md) + +截图和逐轮视觉评审保存在 `logs/visual/`,自动检查结果保存在 `logs/verification/`。大体积的原始扫描文件与视觉研究保留在本地研究目录,不进入网站运行包。 + +原始 token 计数包含缓存输入,不能把缓存计数再次相加。实际账单成本没有暴露,因此记录为未知;不以估算价格冒充实际费用。墙钟时间与活动覆盖时间分别记录,不能混称纯实现工时。 + +`npm run lint` 检查活动森林实现与验证脚本;本地研究快照、原样保留的第三方解码器和未被页面引用的通用 UI 脚手架不纳入此命令,避免改写历史实验。TypeScript 检查仍可运行 `npx tsc --noEmit`。 + +当前为用户接受并要求收尾的本地版本。最新完整探索的帧耗时未达到原定性能目标,冷启动在高负载机器上可能较慢;具体测量与未发布原因见收尾说明。 diff --git a/astra_scene_test/X_03/next-env.d.ts b/astra_scene_test/X_03/next-env.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c986d49faa6a311fc28b5c66f6b7155975007920 --- /dev/null +++ b/astra_scene_test/X_03/next-env.d.ts @@ -0,0 +1,5 @@ +import "vinext/types"; +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/astra_scene_test/X_03/tsconfig.tsbuildinfo b/astra_scene_test/X_03/tsconfig.tsbuildinfo new file mode 100644 index 0000000000000000000000000000000000000000..c76c235c9ab2b41d2ed5a9fcc8072bd28ca0539c --- /dev/null +++ b/astra_scene_test/X_03/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/styled-jsx/types/css.d.ts","./node_modules/@vinext/types/next/upstream/dist/styled-jsx/types/macro.d.ts","./node_modules/@vinext/types/next/upstream/dist/styled-jsx/types/style.d.ts","./node_modules/@vinext/types/next/upstream/dist/styled-jsx/types/global.d.ts","./node_modules/@vinext/types/next/upstream/dist/styled-jsx/types/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/fallback.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/webpack/webpack.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/entry-constants.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/constants.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/bundler.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/config.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/load-custom-routes.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/image-config.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/body-streams.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/search-params.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/vary-params.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/params.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-kind.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-matches/route-match.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/app-router-headers.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/cache-control.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/app-router-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/cache-handlers/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/constants.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/render-result.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/response-cache/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/static-paths/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/instrumentation/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/setup-exception-listeners.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/worker.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/experimental/ppr.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/page-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/analysis/get-page-static-info.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/require-hook.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-polyfill-crypto.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-baseline.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/random.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/date.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/node-environment.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/page-extensions-type.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/i18n-provider.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/next-url.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/cookies.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/request.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/deep-readonly.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/incremental-cache/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/mitt.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/with-router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/route-loader.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/page-loader.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/bloom-filter.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/router.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/readonly-url-search-params.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/flight-data-helpers.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/cache.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/segment-cache/navigation.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/templates/pages.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/pages/module.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/render.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-matchers/route-matcher.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/normalizer.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/request/suffix.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/request/rsc.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/request/next-data.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/after/builtin-request-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/load-default-error-components.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/base-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/after/after.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/after/after-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/use-cache/cache-life.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/lazy-result.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/create-error-handler.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/async-storage/work-store.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/http.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/hooks-server-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/redirect-status-code.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/redirect-error.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/cache-signal.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/implicit-tags.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/staged-rendering.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/templates/app-route.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-route/module.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/segment-config/app/app-segments.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/get-supported-browsers.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/utils.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/rendering-mode.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/cpu-profile.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/turborepo-access-trace/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/turborepo-access-trace/result.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/turborepo-access-trace/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/export/routes/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/export/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/export/worker.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/worker.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/coalesced-function.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-utils/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/trace/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/trace/trace.d.ts","./node_modules/@vinext/types/next/upstream/dist/trace/shared.d.ts","./node_modules/@vinext/types/next/upstream/dist/trace/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/load-jsconfig.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/@vinext/types/next/upstream/dist/telemetry/storage.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/build-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack-config.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/swc/generated-native.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/define-env.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/swc/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/swc/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/parse-version-info.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/shared/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/parse-stack.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/server/shared.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/debug-channel.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/hot-reloader-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/response.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/base-http/node.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/async-callback-set.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/image-optimizer.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/next-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/lru-cache.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/dev-bundler-service.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/static-paths-worker.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/dev/next-dev-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/next.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/render-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/route-module.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/load-components.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/adapter.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/lib/app-dir-module.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/app-render.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/error-boundary.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/layout-router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/render-from-template-context.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/client-page.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/client-segment.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/extra-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/resolvers.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/types/icons.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/metadata/metadata.d.ts","./node_modules/@vinext/types/next/upstream/dist/lib/framework/boundary-components.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/rsc/preloads.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/rsc/postpone.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/rsc/taint.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/collect-segment-data.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/@vinext/types/next/upstream/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/app-render/entry-base.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/templates/app-page.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/route-modules/app-page/module.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/fallback-params.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/image-response.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/after/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/connection.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/exports/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request-meta.d.ts","./node_modules/@vinext/types/next/upstream/dist/cli/next-test.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/size-limit.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/config-shared.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/base-http/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/api-utils/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/build/adapter/build-complete.d.ts","./node_modules/@vinext/types/next/upstream/dist/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/utils.d.ts","./node_modules/@vinext/types/next/upstream/dist/pages/_app.d.ts","./node_modules/@vinext/types/next/upstream/app.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/use-cache/cache-tag.d.ts","./node_modules/@vinext/types/next/upstream/cache.d.ts","./node_modules/@vinext/types/next/upstream/dist/pages/_document.d.ts","./node_modules/@vinext/types/next/upstream/document.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/dynamic.d.ts","./node_modules/@vinext/types/next/upstream/dynamic.d.ts","./node_modules/@vinext/types/next/upstream/dist/pages/_error.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/catch-error.d.ts","./node_modules/@vinext/types/next/upstream/dist/api/error.d.ts","./node_modules/@vinext/types/next/upstream/error.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/head.d.ts","./node_modules/@vinext/types/next/upstream/head.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/cookies.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/headers.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/request/draft-mode.d.ts","./node_modules/@vinext/types/next/upstream/headers.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/get-img-props.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/image-component.d.ts","./node_modules/@vinext/types/next/upstream/dist/shared/lib/image-external.d.ts","./node_modules/@vinext/types/next/upstream/image.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/link.d.ts","./node_modules/@vinext/types/next/upstream/link.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/unrecognized-action-error.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/redirect.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/not-found.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/forbidden.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/unauthorized.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/unstable-rethrow.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/navigation.react-server.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/components/navigation.d.ts","./node_modules/@vinext/types/next/upstream/navigation.d.ts","./node_modules/@vinext/types/next/upstream/router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/script.d.ts","./node_modules/@vinext/types/next/upstream/script.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@vercel/og/types.d.ts","./node_modules/@vinext/types/next/upstream/server.d.ts","./node_modules/@vinext/types/next/upstream/types/global.d.ts","./node_modules/@vinext/types/next/upstream/types/compiled.d.ts","./node_modules/@vinext/types/next/upstream/types.d.ts","./node_modules/@vinext/types/next/upstream/index.d.ts","./node_modules/@vinext/types/next/vinext/router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/compat/router.d.ts","./node_modules/@vinext/types/next/upstream/compat/router.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/legacy/image.d.ts","./node_modules/@vinext/types/next/upstream/legacy/image.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@next/font/dist/types.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/@vinext/types/next/upstream/font/google/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@next/font/dist/local/index.d.ts","./node_modules/@vinext/types/next/upstream/font/local/index.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/form-shared.d.ts","./node_modules/@vinext/types/next/upstream/dist/client/form.d.ts","./node_modules/@vinext/types/next/upstream/form.d.ts","./node_modules/@vinext/types/next/upstream/dist/compiled/@vercel/og/index.node.d.ts","./node_modules/@vinext/types/next/upstream/dist/server/og/image-response.d.ts","./node_modules/@vinext/types/next/upstream/og.d.ts","./node_modules/@vinext/types/next/upstream/constants.d.ts","./node_modules/@vinext/types/next/next-shims-upstream.generated.d.ts","./node_modules/@vinext/types/next/next-shims-missing-modules-vinext.d.ts","./node_modules/@vinext/types/next/next-shims-vinext.d.ts","./node_modules/@vinext/types/next/next-external-stubs.d.ts","./node_modules/@vinext/types/next/upstream/image-types/global.d.ts","./node_modules/@vinext/types/next/index.d.ts","./node_modules/vinext/dist/shims/next-shims-public.d.ts","./.next/types/routes.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/vinext/dist/shims/constants.d.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/rolldown/dist/shared/logging-c6h4g8da.d.mts","./node_modules/@oxc-project/types/types.d.ts","./node_modules/rolldown/dist/shared/binding-zh1vcmbm.d.mts","./node_modules/@rolldown/pluginutils/dist/filter/index.d.mts","./node_modules/@rolldown/pluginutils/dist/index.d.mts","./node_modules/rolldown/dist/shared/define-config-cekzmics.d.mts","./node_modules/rolldown/dist/index.d.mts","./node_modules/rolldown/dist/parse-ast-index.d.mts","./node_modules/vite/types/internal/rolluptypecompat.d.ts","./node_modules/rolldown/dist/shared/constructors-dansz-_i.d.mts","./node_modules/rolldown/dist/plugins-index.d.mts","./node_modules/rolldown/dist/shared/transform-dgz3pasd.d.mts","./node_modules/rolldown/dist/utils-index.d.mts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/esbuild/lib/main.d.ts","./node_modules/vite/types/internal/esbuildoptions.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts","./node_modules/@jridgewell/trace-mapping/types/types.d.mts","./node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts","./node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts","./node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.mts","./node_modules/@jridgewell/gen-mapping/types/types.d.mts","./node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.mts","./node_modules/@jridgewell/source-map/types/source-map.d.mts","./node_modules/terser/tools/terser.d.ts","./node_modules/vite/types/internal/terseroptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/vite/node_modules/lightningcss/node/ast.d.ts","./node_modules/vite/node_modules/lightningcss/node/targets.d.ts","./node_modules/vite/node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/vite/types/internal/csspreprocessoroptions.d.ts","./node_modules/rolldown/dist/filter-index.d.mts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/vinext/dist/config/next-config.d.ts","./node_modules/vinext/dist/cache/cache-adapters-virtual.d.ts","./node_modules/vinext/dist/config/prerender.d.ts","./node_modules/vinext/dist/routing/file-matcher.d.ts","./node_modules/vinext/dist/routing/route-validation.d.ts","./node_modules/vinext/dist/routing/pages-router.d.ts","./node_modules/vinext/dist/routing/utils.d.ts","./node_modules/vinext/dist/routing/app-route-graph.d.ts","./node_modules/vinext/dist/build/static-export.d.ts","./node_modules/vinext/dist/image/image-adapters-virtual.d.ts","./node_modules/@vitejs/plugin-react/types/optionaltypes.d.ts","./node_modules/@vitejs/plugin-react/dist/index.d.ts","./node_modules/vinext/dist/index.d.ts","./vite.config.ts","./hooks/use-mobile.ts","./node_modules/clsx/clsx.d.mts","./node_modules/tailwind-merge/dist/types.d.ts","./lib/utils.ts","./app/layout.tsx","./lib/forest/static-instance-writer.js","./node_modules/@types/three/src/constants.d.ts","./node_modules/@types/three/src/core/layers.d.ts","./node_modules/@types/three/src/math/vector2.d.ts","./node_modules/@types/three/src/math/matrix3.d.ts","./node_modules/@types/three/src/core/bufferattribute.d.ts","./node_modules/@types/three/src/core/interleavedbuffer.d.ts","./node_modules/@types/three/src/core/interleavedbufferattribute.d.ts","./node_modules/@types/three/src/math/quaternion.d.ts","./node_modules/@types/three/src/math/euler.d.ts","./node_modules/@types/three/src/math/matrix4.d.ts","./node_modules/@types/three/src/math/vector4.d.ts","./node_modules/@types/three/src/cameras/camera.d.ts","./node_modules/@types/three/src/math/colormanagement.d.ts","./node_modules/@types/three/src/math/color.d.ts","./node_modules/@types/three/src/math/cylindrical.d.ts","./node_modules/@types/three/src/math/spherical.d.ts","./node_modules/@types/three/src/math/vector3.d.ts","./node_modules/@types/three/src/objects/bone.d.ts","./node_modules/@types/three/src/math/interpolant.d.ts","./node_modules/@types/three/src/math/interpolants/cubicinterpolant.d.ts","./node_modules/@types/three/src/math/interpolants/discreteinterpolant.d.ts","./node_modules/@types/three/src/math/interpolants/linearinterpolant.d.ts","./node_modules/@types/three/src/animation/keyframetrack.d.ts","./node_modules/@types/three/src/animation/animationclip.d.ts","./node_modules/@types/three/src/extras/core/curve.d.ts","./node_modules/@types/three/src/extras/core/curvepath.d.ts","./node_modules/@types/three/src/extras/core/path.d.ts","./node_modules/@types/three/src/extras/core/shape.d.ts","./node_modules/@types/three/src/math/line3.d.ts","./node_modules/@types/three/src/math/sphere.d.ts","./node_modules/@types/three/src/math/plane.d.ts","./node_modules/@types/three/src/math/triangle.d.ts","./node_modules/@types/three/src/math/box3.d.ts","./node_modules/@types/three/src/renderers/common/storagebufferattribute.d.ts","./node_modules/@types/three/src/renderers/common/indirectstoragebufferattribute.d.ts","./node_modules/@types/three/src/core/eventdispatcher.d.ts","./node_modules/@types/three/src/core/glbufferattribute.d.ts","./node_modules/@types/three/src/core/buffergeometry.d.ts","./node_modules/@types/three/src/objects/group.d.ts","./node_modules/@types/three/src/textures/depthtexture.d.ts","./node_modules/@types/three/src/core/rendertarget.d.ts","./node_modules/@types/three/src/textures/compressedtexture.d.ts","./node_modules/@types/three/src/textures/cubetexture.d.ts","./node_modules/@types/three/src/textures/source.d.ts","./node_modules/@types/three/src/textures/texture.d.ts","./node_modules/@types/three/src/materials/linebasicmaterial.d.ts","./node_modules/@types/three/src/materials/linedashedmaterial.d.ts","./node_modules/@types/three/src/materials/meshbasicmaterial.d.ts","./node_modules/@types/three/src/materials/meshdepthmaterial.d.ts","./node_modules/@types/three/src/materials/meshdistancematerial.d.ts","./node_modules/@types/three/src/materials/meshlambertmaterial.d.ts","./node_modules/@types/three/src/materials/meshmatcapmaterial.d.ts","./node_modules/@types/three/src/materials/meshnormalmaterial.d.ts","./node_modules/@types/three/src/materials/meshphongmaterial.d.ts","./node_modules/@types/three/src/materials/meshstandardmaterial.d.ts","./node_modules/@types/three/src/materials/meshphysicalmaterial.d.ts","./node_modules/@types/three/src/materials/meshtoonmaterial.d.ts","./node_modules/@types/three/src/materials/pointsmaterial.d.ts","./node_modules/@types/three/src/core/uniform.d.ts","./node_modules/@types/three/src/core/uniformsgroup.d.ts","./node_modules/@types/three/src/renderers/shaders/uniformslib.d.ts","./node_modules/@types/three/src/materials/shadermaterial.d.ts","./node_modules/@types/three/src/materials/rawshadermaterial.d.ts","./node_modules/@types/three/src/materials/shadowmaterial.d.ts","./node_modules/@types/three/src/materials/spritematerial.d.ts","./node_modules/@types/three/src/materials/materials.d.ts","./node_modules/@types/three/src/objects/sprite.d.ts","./node_modules/@types/three/src/math/frustum.d.ts","./node_modules/@types/three/src/renderers/webglrendertarget.d.ts","./node_modules/@types/three/src/lights/lightshadow.d.ts","./node_modules/@types/three/src/lights/light.d.ts","./node_modules/@types/three/src/scenes/fog.d.ts","./node_modules/@types/three/src/scenes/fogexp2.d.ts","./node_modules/@types/three/src/scenes/scene.d.ts","./node_modules/@types/three/src/math/box2.d.ts","./node_modules/@types/three/src/renderers/webgl/webglcapabilities.d.ts","./node_modules/@types/three/src/renderers/webgl/webglextensions.d.ts","./node_modules/@types/three/src/renderers/webgl/webglproperties.d.ts","./node_modules/@types/three/src/renderers/webgl/webglstate.d.ts","./node_modules/@types/three/src/renderers/webgl/webglutils.d.ts","./node_modules/@types/three/src/renderers/webgl/webgltextures.d.ts","./node_modules/@types/three/src/renderers/webgl/webgluniforms.d.ts","./node_modules/@types/three/src/renderers/webgl/webglprogram.d.ts","./node_modules/@types/three/src/renderers/webgl/webglinfo.d.ts","./node_modules/@types/three/src/renderers/webgl/webglrenderlists.d.ts","./node_modules/@types/three/src/renderers/webgl/webglobjects.d.ts","./node_modules/@types/three/src/renderers/webgl/webglshadowmap.d.ts","./node_modules/@types/webxr/index.d.ts","./node_modules/@types/three/src/cameras/perspectivecamera.d.ts","./node_modules/@types/three/src/cameras/arraycamera.d.ts","./node_modules/@types/three/src/objects/mesh.d.ts","./node_modules/@types/three/src/textures/externaltexture.d.ts","./node_modules/@types/three/src/renderers/webxr/webxrcontroller.d.ts","./node_modules/@types/three/src/renderers/webxr/webxrmanager.d.ts","./node_modules/@types/three/src/renderers/webglrenderer.d.ts","./node_modules/@types/three/src/renderers/webgl/webglattributes.d.ts","./node_modules/@types/three/src/renderers/webgl/webglbindingstates.d.ts","./node_modules/@types/three/src/renderers/webgl/webglclipping.d.ts","./node_modules/@types/three/src/renderers/webgl/webglcubemaps.d.ts","./node_modules/@types/three/src/renderers/webgl/webgllights.d.ts","./node_modules/@types/three/src/renderers/webgl/webglprograms.d.ts","./node_modules/@types/three/src/materials/material.d.ts","./node_modules/@types/three/src/textures/datatexture.d.ts","./node_modules/@types/three/src/objects/skeleton.d.ts","./node_modules/@types/three/src/math/ray.d.ts","./node_modules/@types/three/src/core/raycaster.d.ts","./node_modules/@types/three/src/core/object3d.d.ts","./node_modules/@types/three/src/animation/animationobjectgroup.d.ts","./node_modules/@types/three/src/animation/animationmixer.d.ts","./node_modules/@types/three/src/animation/animationaction.d.ts","./node_modules/@types/three/src/animation/animationutils.d.ts","./node_modules/@types/three/src/animation/propertybinding.d.ts","./node_modules/@types/three/src/animation/propertymixer.d.ts","./node_modules/@types/three/src/animation/tracks/booleankeyframetrack.d.ts","./node_modules/@types/three/src/animation/tracks/colorkeyframetrack.d.ts","./node_modules/@types/three/src/animation/tracks/numberkeyframetrack.d.ts","./node_modules/@types/three/src/animation/tracks/quaternionkeyframetrack.d.ts","./node_modules/@types/three/src/animation/tracks/stringkeyframetrack.d.ts","./node_modules/@types/three/src/animation/tracks/vectorkeyframetrack.d.ts","./node_modules/@types/three/src/audio/audiocontext.d.ts","./node_modules/@types/three/src/audio/audiolistener.d.ts","./node_modules/@types/three/src/audio/audio.d.ts","./node_modules/@types/three/src/audio/audioanalyser.d.ts","./node_modules/@types/three/src/audio/positionalaudio.d.ts","./node_modules/@types/three/src/renderers/webglcuberendertarget.d.ts","./node_modules/@types/three/src/cameras/cubecamera.d.ts","./node_modules/@types/three/src/cameras/orthographiccamera.d.ts","./node_modules/@types/three/src/cameras/stereocamera.d.ts","./node_modules/@types/three/src/core/clock.d.ts","./node_modules/@types/three/src/core/instancedbufferattribute.d.ts","./node_modules/@types/three/src/core/instancedbuffergeometry.d.ts","./node_modules/@types/three/src/core/instancedinterleavedbuffer.d.ts","./node_modules/@types/three/src/core/rendertarget3d.d.ts","./node_modules/@types/three/src/core/timer.d.ts","./node_modules/@types/three/src/extras/controls.d.ts","./node_modules/@types/three/src/extras/core/shapepath.d.ts","./node_modules/@types/three/src/extras/curves/ellipsecurve.d.ts","./node_modules/@types/three/src/extras/curves/arccurve.d.ts","./node_modules/@types/three/src/extras/curves/catmullromcurve3.d.ts","./node_modules/@types/three/src/extras/curves/cubicbeziercurve.d.ts","./node_modules/@types/three/src/extras/curves/cubicbeziercurve3.d.ts","./node_modules/@types/three/src/extras/curves/linecurve.d.ts","./node_modules/@types/three/src/extras/curves/linecurve3.d.ts","./node_modules/@types/three/src/extras/curves/quadraticbeziercurve.d.ts","./node_modules/@types/three/src/extras/curves/quadraticbeziercurve3.d.ts","./node_modules/@types/three/src/extras/curves/splinecurve.d.ts","./node_modules/@types/three/src/extras/curves/curves.d.ts","./node_modules/@types/three/src/extras/datautils.d.ts","./node_modules/@types/three/src/extras/imageutils.d.ts","./node_modules/@types/three/src/extras/shapeutils.d.ts","./node_modules/@types/three/src/extras/textureutils.d.ts","./node_modules/@types/three/src/geometries/boxgeometry.d.ts","./node_modules/@types/three/src/geometries/capsulegeometry.d.ts","./node_modules/@types/three/src/geometries/circlegeometry.d.ts","./node_modules/@types/three/src/geometries/cylindergeometry.d.ts","./node_modules/@types/three/src/geometries/conegeometry.d.ts","./node_modules/@types/three/src/geometries/polyhedrongeometry.d.ts","./node_modules/@types/three/src/geometries/dodecahedrongeometry.d.ts","./node_modules/@types/three/src/geometries/edgesgeometry.d.ts","./node_modules/@types/three/src/geometries/extrudegeometry.d.ts","./node_modules/@types/three/src/geometries/icosahedrongeometry.d.ts","./node_modules/@types/three/src/geometries/lathegeometry.d.ts","./node_modules/@types/three/src/geometries/octahedrongeometry.d.ts","./node_modules/@types/three/src/geometries/planegeometry.d.ts","./node_modules/@types/three/src/geometries/ringgeometry.d.ts","./node_modules/@types/three/src/geometries/shapegeometry.d.ts","./node_modules/@types/three/src/geometries/spheregeometry.d.ts","./node_modules/@types/three/src/geometries/tetrahedrongeometry.d.ts","./node_modules/@types/three/src/geometries/torusgeometry.d.ts","./node_modules/@types/three/src/geometries/torusknotgeometry.d.ts","./node_modules/@types/three/src/geometries/tubegeometry.d.ts","./node_modules/@types/three/src/geometries/wireframegeometry.d.ts","./node_modules/@types/three/src/geometries/geometries.d.ts","./node_modules/@types/three/src/objects/line.d.ts","./node_modules/@types/three/src/helpers/arrowhelper.d.ts","./node_modules/@types/three/src/objects/linesegments.d.ts","./node_modules/@types/three/src/helpers/axeshelper.d.ts","./node_modules/@types/three/src/helpers/box3helper.d.ts","./node_modules/@types/three/src/helpers/boxhelper.d.ts","./node_modules/@types/three/src/helpers/camerahelper.d.ts","./node_modules/@types/three/src/lights/directionallightshadow.d.ts","./node_modules/@types/three/src/lights/directionallight.d.ts","./node_modules/@types/three/src/helpers/directionallighthelper.d.ts","./node_modules/@types/three/src/helpers/gridhelper.d.ts","./node_modules/@types/three/src/lights/hemispherelight.d.ts","./node_modules/@types/three/src/helpers/hemispherelighthelper.d.ts","./node_modules/@types/three/src/helpers/planehelper.d.ts","./node_modules/@types/three/src/lights/pointlightshadow.d.ts","./node_modules/@types/three/src/lights/pointlight.d.ts","./node_modules/@types/three/src/helpers/pointlighthelper.d.ts","./node_modules/@types/three/src/helpers/polargridhelper.d.ts","./node_modules/@types/three/src/objects/skinnedmesh.d.ts","./node_modules/@types/three/src/helpers/skeletonhelper.d.ts","./node_modules/@types/three/src/helpers/spotlighthelper.d.ts","./node_modules/@types/three/src/lights/ambientlight.d.ts","./node_modules/@types/three/src/math/sphericalharmonics3.d.ts","./node_modules/@types/three/src/lights/lightprobe.d.ts","./node_modules/@types/three/src/lights/rectarealight.d.ts","./node_modules/@types/three/src/lights/spotlightshadow.d.ts","./node_modules/@types/three/src/lights/spotlight.d.ts","./node_modules/@types/three/src/loaders/loadingmanager.d.ts","./node_modules/@types/three/src/loaders/loader.d.ts","./node_modules/@types/three/src/loaders/animationloader.d.ts","./node_modules/@types/three/src/loaders/audioloader.d.ts","./node_modules/@types/three/src/loaders/buffergeometryloader.d.ts","./node_modules/@types/three/src/loaders/cache.d.ts","./node_modules/@types/three/src/loaders/compressedtextureloader.d.ts","./node_modules/@types/three/src/loaders/cubetextureloader.d.ts","./node_modules/@types/three/src/loaders/datatextureloader.d.ts","./node_modules/@types/three/src/loaders/fileloader.d.ts","./node_modules/@types/three/src/loaders/imagebitmaploader.d.ts","./node_modules/@types/three/src/loaders/imageloader.d.ts","./node_modules/@types/three/src/loaders/loaderutils.d.ts","./node_modules/@types/three/src/loaders/materialloader.d.ts","./node_modules/@types/three/src/loaders/objectloader.d.ts","./node_modules/@types/three/src/loaders/textureloader.d.ts","./node_modules/@types/three/src/math/frustumarray.d.ts","./node_modules/@types/three/src/math/interpolants/quaternionlinearinterpolant.d.ts","./node_modules/@types/three/src/math/mathutils.d.ts","./node_modules/@types/three/src/math/matrix2.d.ts","./node_modules/@types/three/src/objects/batchedmesh.d.ts","./node_modules/@types/three/src/objects/instancedmesh.d.ts","./node_modules/@types/three/src/objects/lineloop.d.ts","./node_modules/@types/three/src/objects/lod.d.ts","./node_modules/@types/three/src/objects/points.d.ts","./node_modules/@types/three/src/textures/data3dtexture.d.ts","./node_modules/@types/three/src/renderers/webgl3drendertarget.d.ts","./node_modules/@types/three/src/textures/dataarraytexture.d.ts","./node_modules/@types/three/src/renderers/webglarrayrendertarget.d.ts","./node_modules/@types/three/src/textures/canvastexture.d.ts","./node_modules/@types/three/src/textures/compressedarraytexture.d.ts","./node_modules/@types/three/src/textures/compressedcubetexture.d.ts","./node_modules/@types/three/src/textures/framebuffertexture.d.ts","./node_modules/@types/three/src/textures/videotexture.d.ts","./node_modules/@types/three/src/textures/videoframetexture.d.ts","./node_modules/@types/three/src/utils.d.ts","./node_modules/@types/three/src/three.core.d.ts","./node_modules/@types/three/src/extras/pmremgenerator.d.ts","./node_modules/@types/three/src/renderers/shaders/shaderchunk.d.ts","./node_modules/@types/three/src/renderers/shaders/shaderlib.d.ts","./node_modules/@types/three/src/renderers/shaders/uniformsutils.d.ts","./node_modules/@types/three/src/renderers/webgl/webglbufferrenderer.d.ts","./node_modules/@types/three/src/renderers/webgl/webglcubeuvmaps.d.ts","./node_modules/@types/three/src/renderers/webgl/webglgeometries.d.ts","./node_modules/@types/three/src/renderers/webgl/webglindexedbufferrenderer.d.ts","./node_modules/@types/three/src/renderers/webgl/webglshader.d.ts","./node_modules/@types/three/src/renderers/webxr/webxrdepthsensing.d.ts","./node_modules/@types/three/src/three.d.ts","./node_modules/@types/three/build/three.module.d.ts","./lib/forest/resource-owner.js","./node_modules/@types/three/examples/jsm/utils/buffergeometryutils.d.ts","./lib/forest/math.js","./lib/forest/root-system.js","./lib/forest/canopy-occlusion.js","./lib/forest/branching-trees.js","./node_modules/meshoptimizer/meshopt_decoder.module.d.ts","./node_modules/@types/three/examples/jsm/libs/meshopt_decoder.module.d.ts","./node_modules/@types/three/examples/jsm/loaders/dracoloader.d.ts","./node_modules/@types/three/src/nodes/core/constants.d.ts","./node_modules/@types/three/src/nodes/core/inputnode.d.ts","./node_modules/@types/three/src/nodes/core/constnode.d.ts","./node_modules/@types/three/src/nodes/core/stacknode.d.ts","./node_modules/@types/three/src/nodes/utils/joinnode.d.ts","./node_modules/@types/three/src/nodes/tsl/tslcore.d.ts","./node_modules/@types/three/src/nodes/core/tempnode.d.ts","./node_modules/@types/three/src/nodes/core/arraynode.d.ts","./node_modules/@types/three/src/nodes/core/assignnode.d.ts","./node_modules/@types/three/src/nodes/core/attributenode.d.ts","./node_modules/@types/three/src/nodes/core/bypassnode.d.ts","./node_modules/@types/three/src/nodes/core/contextnode.d.ts","./node_modules/@types/three/src/nodes/core/nodeframe.d.ts","./node_modules/@types/three/src/nodes/core/uniformgroupnode.d.ts","./node_modules/@types/three/src/nodes/core/uniformnode.d.ts","./node_modules/@types/three/src/nodes/accessors/buffernode.d.ts","./node_modules/@types/three/src/nodes/accessors/bufferattributenode.d.ts","./node_modules/@types/three/src/nodes/core/nodeattribute.d.ts","./node_modules/@types/three/src/nodes/core/nodecode.d.ts","./node_modules/@types/three/src/nodes/core/nodeuniform.d.ts","./node_modules/@types/three/src/nodes/core/nodevar.d.ts","./node_modules/@types/three/src/nodes/core/nodevarying.d.ts","./node_modules/@types/three/src/nodes/core/structtypenode.d.ts","./node_modules/@types/three/src/nodes/core/structtype.d.ts","./node_modules/@types/three/src/nodes/core/nodecache.d.ts","./node_modules/@types/three/src/nodes/core/cachenode.d.ts","./node_modules/@types/three/src/nodes/core/indexnode.d.ts","./node_modules/@types/three/src/nodes/lighting/lightingnode.d.ts","./node_modules/@types/three/src/nodes/lighting/lightsnode.d.ts","./node_modules/@types/three/src/nodes/core/lightingmodel.d.ts","./node_modules/@types/three/src/nodes/core/nodefunctioninput.d.ts","./node_modules/@types/three/src/nodes/core/outputstructnode.d.ts","./node_modules/@types/three/src/nodes/core/propertynode.d.ts","./node_modules/@types/three/src/nodes/core/parameternode.d.ts","./node_modules/@types/three/src/nodes/core/structnode.d.ts","./node_modules/@types/three/src/nodes/core/subbuildnode.d.ts","./node_modules/@types/three/src/nodes/core/varnode.d.ts","./node_modules/@types/three/src/nodes/core/varyingnode.d.ts","./node_modules/@types/three/src/nodes/core/nodeutils.d.ts","./node_modules/@types/three/src/nodes/utils/arrayelementnode.d.ts","./node_modules/@types/three/src/nodes/utils/convertnode.d.ts","./node_modules/@types/three/src/nodes/utils/debugnode.d.ts","./node_modules/@types/three/src/nodes/utils/eventnode.d.ts","./node_modules/@types/three/src/nodes/utils/functionoverloadingnode.d.ts","./node_modules/@types/three/src/nodes/utils/loopnode.d.ts","./node_modules/@types/three/src/nodes/accessors/texturenode.d.ts","./node_modules/@types/three/src/nodes/utils/maxmiplevelnode.d.ts","./node_modules/@types/three/src/nodes/utils/membernode.d.ts","./node_modules/@types/three/src/nodes/utils/reflectornode.d.ts","./node_modules/@types/three/src/nodes/utils/remapnode.d.ts","./node_modules/@types/three/src/nodes/utils/rotatenode.d.ts","./node_modules/@types/three/src/nodes/utils/rttnode.d.ts","./node_modules/@types/three/src/nodes/utils/setnode.d.ts","./node_modules/@types/three/src/nodes/utils/splitnode.d.ts","./node_modules/@types/three/src/nodes/utils/spritesheetuvnode.d.ts","./node_modules/@types/three/src/renderers/common/storageinstancedbufferattribute.d.ts","./node_modules/@types/three/src/nodes/accessors/storagebuffernode.d.ts","./node_modules/@types/three/src/nodes/utils/storagearrayelementnode.d.ts","./node_modules/@types/three/src/nodes/math/bitcastnode.d.ts","./node_modules/@types/three/src/nodes/accessors/batchnode.d.ts","./node_modules/@types/three/src/nodes/accessors/cubetexturenode.d.ts","./node_modules/@types/three/src/nodes/accessors/instancenode.d.ts","./node_modules/@types/three/src/nodes/accessors/instancedmeshnode.d.ts","./node_modules/@types/three/src/nodes/accessors/materialnode.d.ts","./node_modules/@types/three/src/nodes/accessors/referencenode.d.ts","./node_modules/@types/three/src/nodes/accessors/materialreferencenode.d.ts","./node_modules/@types/three/src/nodes/accessors/object3dnode.d.ts","./node_modules/@types/three/src/nodes/accessors/modelnode.d.ts","./node_modules/@types/three/src/nodes/accessors/morphnode.d.ts","./node_modules/@types/three/src/nodes/accessors/pointuvnode.d.ts","./node_modules/@types/three/src/nodes/accessors/rendererreferencenode.d.ts","./node_modules/@types/three/src/nodes/accessors/scenenode.d.ts","./node_modules/@types/three/src/nodes/accessors/skinningnode.d.ts","./node_modules/@types/three/src/nodes/accessors/storagetexturenode.d.ts","./node_modules/@types/three/src/nodes/accessors/texture3dnode.d.ts","./node_modules/@types/three/src/nodes/accessors/texturesizenode.d.ts","./node_modules/@types/three/src/nodes/accessors/uniformarraynode.d.ts","./node_modules/@types/three/src/nodes/accessors/userdatanode.d.ts","./node_modules/@types/three/src/nodes/accessors/vertexcolornode.d.ts","./node_modules/@types/three/src/nodes/display/bumpmapnode.d.ts","./node_modules/@types/three/src/nodes/display/colorspacenode.d.ts","./node_modules/@types/three/src/nodes/display/frontfacingnode.d.ts","./node_modules/@types/three/src/nodes/display/normalmapnode.d.ts","./node_modules/@types/three/src/nodes/display/passnode.d.ts","./node_modules/@types/three/src/nodes/display/posterizenode.d.ts","./node_modules/@types/three/src/nodes/display/renderoutputnode.d.ts","./node_modules/@types/three/src/nodes/display/screennode.d.ts","./node_modules/@types/three/src/nodes/display/tonemappingnode.d.ts","./node_modules/@types/three/src/nodes/display/viewportdepthnode.d.ts","./node_modules/@types/three/src/nodes/display/viewporttexturenode.d.ts","./node_modules/@types/three/src/nodes/display/viewportdepthtexturenode.d.ts","./node_modules/@types/three/src/nodes/display/viewportsharedtexturenode.d.ts","./node_modules/@types/three/src/nodes/code/codenode.d.ts","./node_modules/@types/three/src/nodes/code/expressionnode.d.ts","./node_modules/@types/three/src/nodes/core/nodefunction.d.ts","./node_modules/@types/three/src/nodes/code/functionnode.d.ts","./node_modules/@types/three/src/nodes/code/functioncallnode.d.ts","./node_modules/@types/three/src/nodes/code/scriptablenode.d.ts","./node_modules/@types/three/src/nodes/code/scriptablevaluenode.d.ts","./node_modules/@types/three/src/nodes/geometry/rangenode.d.ts","./node_modules/@types/three/src/nodes/gpgpu/computenode.d.ts","./node_modules/@types/three/src/objects/clippinggroup.d.ts","./node_modules/@types/three/src/renderers/common/clippingcontext.d.ts","./node_modules/@types/three/src/nodes/lighting/shadowbasenode.d.ts","./node_modules/@types/three/src/nodes/lighting/shadownode.d.ts","./node_modules/@types/three/src/nodes/lighting/analyticlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/ambientlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/aonode.d.ts","./node_modules/@types/three/src/nodes/lighting/basicenvironmentnode.d.ts","./node_modules/@types/three/src/nodes/lighting/directionallightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/environmentnode.d.ts","./node_modules/@types/three/src/nodes/lighting/hemispherelightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/spotlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/iesspotlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/irradiancenode.d.ts","./node_modules/@types/three/src/nodes/lighting/lightingcontextnode.d.ts","./node_modules/@types/three/src/nodes/lighting/lightprobenode.d.ts","./node_modules/@types/three/src/nodes/lighting/pointshadownode.d.ts","./node_modules/@types/three/src/nodes/lighting/pointlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/projectorlightnode.d.ts","./node_modules/@types/three/src/nodes/lighting/rectarealightnode.d.ts","./node_modules/@types/three/src/nodes/pmrem/pmremnode.d.ts","./node_modules/@types/three/src/nodes/core/nodeparser.d.ts","./node_modules/@types/three/src/nodes/parsers/glslnodefunction.d.ts","./node_modules/@types/three/src/nodes/parsers/glslnodeparser.d.ts","./node_modules/@types/three/src/nodes/functions/basiclightingmodel.d.ts","./node_modules/@types/three/src/nodes/functions/phonglightingmodel.d.ts","./node_modules/@types/three/src/nodes/functions/physicallightingmodel.d.ts","./node_modules/@types/three/src/nodes/nodes.d.ts","./node_modules/@types/three/src/nodes/core/mrtnode.d.ts","./node_modules/@types/three/src/renderers/common/info.d.ts","./node_modules/@types/three/src/nodes/math/hash.d.ts","./node_modules/@types/three/src/nodes/math/operatornode.d.ts","./node_modules/@types/three/src/nodes/math/mathnode.d.ts","./node_modules/@types/three/src/nodes/math/mathutils.d.ts","./node_modules/@types/three/src/nodes/math/trinoise3d.d.ts","./node_modules/@types/three/src/nodes/utils/equirectuv.d.ts","./node_modules/@types/three/src/nodes/utils/matcapuv.d.ts","./node_modules/@types/three/src/nodes/utils/oscillators.d.ts","./node_modules/@types/three/src/nodes/utils/packing.d.ts","./node_modules/@types/three/src/nodes/utils/postprocessingutils.d.ts","./node_modules/@types/three/src/nodes/utils/samplenode.d.ts","./node_modules/@types/three/src/nodes/utils/spriteutils.d.ts","./node_modules/@types/three/src/nodes/utils/timer.d.ts","./node_modules/@types/three/src/nodes/utils/triplanartextures.d.ts","./node_modules/@types/three/src/nodes/utils/uvutils.d.ts","./node_modules/@types/three/src/nodes/utils/viewportutils.d.ts","./node_modules/@types/three/src/nodes/math/conditionalnode.d.ts","./node_modules/@types/three/src/nodes/utils/discard.d.ts","./node_modules/@types/three/src/nodes/tsl/tslbase.d.ts","./node_modules/@types/three/src/nodes/accessors/accessorsutils.d.ts","./node_modules/@types/three/src/nodes/accessors/arrays.d.ts","./node_modules/@types/three/src/nodes/accessors/bitangent.d.ts","./node_modules/@types/three/src/nodes/accessors/builtinnode.d.ts","./node_modules/@types/three/src/nodes/accessors/camera.d.ts","./node_modules/@types/three/src/nodes/accessors/materialproperties.d.ts","./node_modules/@types/three/src/nodes/accessors/modelviewprojectionnode.d.ts","./node_modules/@types/three/src/nodes/accessors/normal.d.ts","./node_modules/@types/three/src/nodes/accessors/position.d.ts","./node_modules/@types/three/src/nodes/accessors/reflectvector.d.ts","./node_modules/@types/three/src/nodes/accessors/tangent.d.ts","./node_modules/@types/three/src/nodes/accessors/texturebicubic.d.ts","./node_modules/@types/three/src/nodes/accessors/uv.d.ts","./node_modules/@types/three/src/nodes/accessors/velocitynode.d.ts","./node_modules/@types/three/src/nodes/display/blendmodes.d.ts","./node_modules/@types/three/src/nodes/display/coloradjustment.d.ts","./node_modules/@types/three/src/nodes/display/toonoutlinepassnode.d.ts","./node_modules/@types/three/src/nodes/display/colorspacefunctions.d.ts","./node_modules/@types/three/src/nodes/display/tonemappingfunctions.d.ts","./node_modules/@types/three/src/nodes/fog/fog.d.ts","./node_modules/@types/three/src/nodes/gpgpu/atomicfunctionnode.d.ts","./node_modules/@types/three/src/nodes/gpgpu/barriernode.d.ts","./node_modules/@types/three/src/nodes/gpgpu/computebuiltinnode.d.ts","./node_modules/@types/three/src/nodes/gpgpu/subgroupfunctionnode.d.ts","./node_modules/@types/three/src/nodes/gpgpu/workgroupinfonode.d.ts","./node_modules/@types/three/src/nodes/accessors/lights.d.ts","./node_modules/@types/three/src/nodes/accessors/clippingnode.d.ts","./node_modules/@types/three/src/renderers/common/binding.d.ts","./node_modules/@types/three/src/renderers/common/uniform.d.ts","./node_modules/@types/three/src/renderers/common/nodes/nodeuniform.d.ts","./node_modules/@types/three/src/renderers/common/buffer.d.ts","./node_modules/@types/three/src/renderers/common/uniformbuffer.d.ts","./node_modules/@types/three/src/renderers/common/uniformsgroup.d.ts","./node_modules/@types/three/src/renderers/common/nodes/nodeuniformsgroup.d.ts","./node_modules/@types/three/src/renderers/common/bindgroup.d.ts","./node_modules/@types/three/src/renderers/common/bundlegroup.d.ts","./node_modules/@types/three/src/renderers/common/backend.d.ts","./node_modules/@types/three/src/renderers/common/constants.d.ts","./node_modules/@types/three/src/renderers/common/datamap.d.ts","./node_modules/@types/three/src/renderers/common/attributes.d.ts","./node_modules/@types/three/src/renderers/common/geometries.d.ts","./node_modules/@types/three/src/renderers/common/nodes/nodebuilderstate.d.ts","./node_modules/@types/three/src/renderers/common/rendercontext.d.ts","./node_modules/@types/three/src/renderers/common/pipeline.d.ts","./node_modules/@types/three/src/renderers/common/programmablestage.d.ts","./node_modules/@types/three/src/renderers/common/renderpipeline.d.ts","./node_modules/@types/three/src/renderers/common/renderobject.d.ts","./node_modules/@types/three/src/materials/nodes/manager/nodematerialobserver.d.ts","./node_modules/@types/three/src/materials/nodes/nodematerial.d.ts","./node_modules/@types/three/src/nodes/lighting/shadowfilternode.d.ts","./node_modules/@types/three/src/nodes/pmrem/pmremutils.d.ts","./node_modules/@types/three/src/nodes/procedural/checker.d.ts","./node_modules/@types/three/src/nodes/shapes/shapes.d.ts","./node_modules/@types/three/src/nodes/materialx/lib/mx_hsv.d.ts","./node_modules/@types/three/src/nodes/materialx/lib/mx_transform_color.d.ts","./node_modules/@types/three/src/nodes/materialx/materialxnodes.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/brdf_ggx.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/brdf_lambert.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/d_ggx.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/dfgapprox.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/f_schlick.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/schlick_to_f0.d.ts","./node_modules/@types/three/src/nodes/functions/bsdf/v_ggx_smithcorrelated.d.ts","./node_modules/@types/three/src/nodes/lighting/lightutils.d.ts","./node_modules/@types/three/src/nodes/functions/material/getgeometryroughness.d.ts","./node_modules/@types/three/src/nodes/functions/material/getparallaxcorrectnormal.d.ts","./node_modules/@types/three/src/nodes/functions/material/getroughness.d.ts","./node_modules/@types/three/src/nodes/functions/material/getshirradianceat.d.ts","./node_modules/@types/three/src/nodes/tsl.d.ts","./node_modules/@types/three/src/renderers/common/chainmap.d.ts","./node_modules/@types/three/src/renderers/common/nodes/nodes.d.ts","./node_modules/@types/three/src/renderers/common/animation.d.ts","./node_modules/@types/three/src/renderers/common/lighting.d.ts","./node_modules/@types/three/src/renderers/common/renderlist.d.ts","./node_modules/@types/three/src/renderers/common/background.d.ts","./node_modules/@types/three/src/renderers/common/computepipeline.d.ts","./node_modules/@types/three/src/renderers/common/pipelines.d.ts","./node_modules/@types/three/src/renderers/common/textures.d.ts","./node_modules/@types/three/src/renderers/common/bindings.d.ts","./node_modules/@types/three/src/renderers/common/color4.d.ts","./node_modules/@types/three/src/renderers/common/nodes/nodelibrary.d.ts","./node_modules/@types/three/src/renderers/common/quadmesh.d.ts","./node_modules/@types/three/src/renderers/common/renderbundle.d.ts","./node_modules/@types/three/src/renderers/common/renderbundles.d.ts","./node_modules/@types/three/src/renderers/common/rendercontexts.d.ts","./node_modules/@types/three/src/renderers/common/renderlists.d.ts","./node_modules/@types/three/src/renderers/common/renderobjects.d.ts","./node_modules/@types/three/src/renderers/common/xrrendertarget.d.ts","./node_modules/@types/three/src/renderers/common/xrmanager.d.ts","./node_modules/@types/three/src/renderers/common/renderer.d.ts","./node_modules/@types/three/src/nodes/core/nodebuilder.d.ts","./node_modules/@types/three/src/nodes/core/node.d.ts","./node_modules/@types/three/src/materials/nodes/line2nodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/linebasicnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/linedashednodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshbasicnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshlambertnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshmatcapnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshnormalnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshphongnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshstandardnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshphysicalnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/meshsssnodematerial.d.ts","./node_modules/@types/three/src/nodes/functions/toonlightingmodel.d.ts","./node_modules/@types/three/src/materials/nodes/meshtoonnodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/spritenodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/pointsnodematerial.d.ts","./node_modules/@types/three/src/nodes/functions/shadowmaskmodel.d.ts","./node_modules/@types/three/src/materials/nodes/shadownodematerial.d.ts","./node_modules/@types/three/src/nodes/functions/volumetriclightingmodel.d.ts","./node_modules/@types/three/src/materials/nodes/volumenodematerial.d.ts","./node_modules/@types/three/src/materials/nodes/nodematerials.d.ts","./node_modules/@types/three/src/renderers/common/extras/pmremgenerator.d.ts","./node_modules/@types/three/src/renderers/common/postprocessing.d.ts","./node_modules/@types/three/src/renderers/webgpu/nodes/standardnodelibrary.d.ts","./node_modules/@webgpu/types/dist/index.d.ts","./node_modules/@types/three/src/renderers/webgpu/webgpubackend.d.ts","./node_modules/@types/three/src/renderers/webgpu/webgpurenderer.d.ts","./node_modules/@types/three/src/renderers/common/rendererutils.d.ts","./node_modules/@types/three/src/lights/webgpu/iesspotlight.d.ts","./node_modules/@types/three/src/lights/webgpu/projectorlight.d.ts","./node_modules/@types/three/src/loaders/nodes/nodeloader.d.ts","./node_modules/@types/three/src/loaders/nodes/nodematerialloader.d.ts","./node_modules/@types/three/src/loaders/nodes/nodeobjectloader.d.ts","./node_modules/@types/three/src/renderers/common/storage3dtexture.d.ts","./node_modules/@types/three/src/renderers/common/storagearraytexture.d.ts","./node_modules/@types/three/src/renderers/common/storagetexture.d.ts","./node_modules/@types/three/src/three.webgpu.d.ts","./node_modules/@types/three/build/three.webgpu.d.ts","./node_modules/@types/three/examples/jsm/utils/workerpool.d.ts","./node_modules/@types/three/examples/jsm/loaders/ktx2loader.d.ts","./node_modules/@types/three/examples/jsm/loaders/gltfloader.d.ts","./lib/forest/compact-geometry.js","./lib/forest/shadow-batches.js","./lib/forest/native-bark-blend.js","./lib/forest/tree-assets.js","./lib/forest/sampled-moss.js","./lib/forest/fungus-layout.js","./lib/forest/surface-query.js","./lib/forest/surface-projector.js","./lib/forest/surface-details.js","./lib/forest/moss-assets.js","./lib/forest/ecology.js","./lib/forest/woodland-flowers.js","./lib/forest/woodland-grass.js","./lib/forest/wood-sorrel.js","./lib/forest/forest-litter.js","./lib/forest/ground-moss.js","./lib/forest/floor-habitat.js","./lib/forest/ground-surface.js","./lib/forest/distant-terrain.js","./lib/forest/distant-forest.js","./lib/forest/particles.js","./lib/forest/close-leaves.js","./lib/forest/close-leaf-layer.js","./lib/forest/placement-visibility.js","./lib/forest/instance-buffers.js","./lib/forest/instance-packer.js","./lib/forest/botanical-shrubs.js","./node_modules/@types/three/examples/jsm/objects/sky.d.ts","./lib/forest/plants.js","./lib/forest/leaf-surfaces.js","./lib/forest/scanned-leaf-surface.js","./lib/forest/forest-floor.js","./lib/forest/moss-coat.js","./lib/forest/bark-surfaces.js","./lib/forest/texture-pool.js","./lib/forest/prop-texture-loader.js","./lib/forest/materials.js","./lib/forest/camera.js","./lib/forest/assets.js","./lib/forest/gpu-profiler.js","./lib/forest/temporal-filter.js","./lib/forest/temporal.js","./lib/forest/atmosphere.js","./lib/forest/render-quality.js","./lib/forest/trees.js","./lib/forest/colonization.js","./lib/forest/tree-study.js","./lib/forest/scene.js","./app/page.tsx","./node_modules/@base-ui/react/internals/reason-parts.d.mts","./node_modules/@base-ui/react/internals/reasons.d.mts","./node_modules/@base-ui/react/internals/createbaseuieventdetails.d.mts","./node_modules/@base-ui/react/types/index.d.mts","./node_modules/@base-ui/react/internals/types.d.mts","./node_modules/@base-ui/react/accordion/root/accordionroot.d.mts","./node_modules/@base-ui/react/internals/usetransitionstatus.d.mts","./node_modules/@base-ui/react/collapsible/root/collapsibleroot.d.mts","./node_modules/@base-ui/react/collapsible/root/usecollapsibleroot.d.mts","./node_modules/@base-ui/react/accordion/item/accordionitem.d.mts","./node_modules/@base-ui/react/accordion/header/accordionheader.d.mts","./node_modules/@base-ui/react/accordion/trigger/accordiontrigger.d.mts","./node_modules/@base-ui/react/accordion/panel/accordionpanel.d.mts","./node_modules/@base-ui/react/accordion/index.parts.d.mts","./node_modules/@base-ui/react/accordion/index.d.mts","./node_modules/lucide-react/dist/lucide-react.d.ts","./components/ui/accordion.tsx","./node_modules/reselect/dist/reselect.d.ts","./node_modules/@base-ui/utils/store/createselector.d.mts","./node_modules/@base-ui/utils/store/createselectormemoized.d.mts","./node_modules/@base-ui/utils/fasthooks.d.mts","./node_modules/@base-ui/utils/store/store.d.mts","./node_modules/@base-ui/utils/store/usestore.d.mts","./node_modules/@base-ui/utils/store/reactstore.d.mts","./node_modules/@base-ui/utils/store/storeinspector.d.mts","./node_modules/@base-ui/utils/store/index.d.mts","./node_modules/@base-ui/utils/useenhancedclickhandler.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.d.mts","./node_modules/@floating-ui/core/dist/floating-ui.core.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.d.mts","./node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts","./node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.d.mts","./node_modules/@base-ui/react/utils/popups/inlinerect.d.mts","./node_modules/@base-ui/react/utils/popups/popuptriggermap.d.mts","./node_modules/@base-ui/react/utils/popups/popuphandle.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingrootstore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingdelaygroup.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingfocusmanager.d.mts","./node_modules/@base-ui/react/internals/getstateattributesprops.d.mts","./node_modules/@base-ui/react/internals/userenderelement.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingportal.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclientpoint.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usedismiss.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefocus.d.mts","./node_modules/@base-ui/react/internals/shadowdom.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/element.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehovershared.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehover.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverfloatinginteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverreferenceinteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/composite.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/gridnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/uselistnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usetypeahead.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/safepolygon.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtree.d.mts","./node_modules/@base-ui/react/floating-ui-react/types.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtreestore.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclick.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloating.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usesyncedfloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/index.d.mts","./node_modules/@base-ui/react/utils/popups/store.d.mts","./node_modules/@base-ui/react/utils/popups/popupstoreutils.d.mts","./node_modules/@base-ui/react/utils/popups/usepopuphandlestore.d.mts","./node_modules/@base-ui/react/utils/popups/index.d.mts","./node_modules/@base-ui/react/internals/resolvevaluelabel.d.mts","./node_modules/@base-ui/react/combobox/root/ariacombobox.d.mts","./node_modules/@base-ui/react/autocomplete/root/autocompleteroot.d.mts","./node_modules/@base-ui/react/autocomplete/value/autocompletevalue.d.mts","./node_modules/@base-ui/react/internals/form-context/formcontext.d.mts","./node_modules/@base-ui/react/form/form.d.mts","./node_modules/@base-ui/react/form/index.d.mts","./node_modules/@base-ui/react/field/root/fieldroot.d.mts","./node_modules/@base-ui/react/internals/useanchorpositioning.d.mts","./node_modules/@base-ui/react/autocomplete/trigger/autocompletetrigger.d.mts","./node_modules/@base-ui/react/combobox/input/comboboxinput.d.mts","./node_modules/@base-ui/react/autocomplete/input-group/autocompleteinputgroup.d.mts","./node_modules/@base-ui/react/combobox/icon/comboboxicon.d.mts","./node_modules/@base-ui/react/combobox/clear/comboboxclear.d.mts","./node_modules/@base-ui/react/combobox/list/comboboxlist.d.mts","./node_modules/@base-ui/react/combobox/status/comboboxstatus.d.mts","./node_modules/@base-ui/react/combobox/portal/comboboxportal.d.mts","./node_modules/@base-ui/react/combobox/backdrop/comboboxbackdrop.d.mts","./node_modules/@base-ui/react/combobox/positioner/comboboxpositioner.d.mts","./node_modules/@base-ui/react/combobox/popup/comboboxpopup.d.mts","./node_modules/@base-ui/react/combobox/arrow/comboboxarrow.d.mts","./node_modules/@base-ui/react/combobox/group/comboboxgroup.d.mts","./node_modules/@base-ui/react/combobox/group-label/comboboxgrouplabel.d.mts","./node_modules/@base-ui/react/autocomplete/item/autocompleteitem.d.mts","./node_modules/@base-ui/react/combobox/row/comboboxrow.d.mts","./node_modules/@base-ui/react/combobox/collection/comboboxcollection.d.mts","./node_modules/@base-ui/react/combobox/empty/comboboxempty.d.mts","./node_modules/@base-ui/react/autocomplete/separator/autocompleteseparator.d.mts","./node_modules/@base-ui/react/internals/filter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefilter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefiltereditems.d.mts","./node_modules/@base-ui/react/autocomplete/index.parts.d.mts","./node_modules/@base-ui/react/autocomplete/index.d.mts","./node_modules/@base-ui/react/avatar/root/avatarroot.d.mts","./node_modules/@base-ui/react/avatar/image/avatarimage.d.mts","./node_modules/@base-ui/react/avatar/fallback/avatarfallback.d.mts","./node_modules/@base-ui/react/avatar/index.parts.d.mts","./node_modules/@base-ui/react/avatar/index.d.mts","./node_modules/@base-ui/react/button/button.d.mts","./node_modules/@base-ui/react/button/index.d.mts","./node_modules/@base-ui/react/checkbox/root/checkboxroot.d.mts","./node_modules/@base-ui/react/checkbox/indicator/checkboxindicator.d.mts","./node_modules/@base-ui/react/checkbox/index.parts.d.mts","./node_modules/@base-ui/react/checkbox/index.d.mts","./node_modules/@base-ui/react/checkbox-group/checkboxgroup.d.mts","./node_modules/@base-ui/react/checkbox-group/index.d.mts","./node_modules/@base-ui/react/collapsible/trigger/collapsibletrigger.d.mts","./node_modules/@base-ui/react/collapsible/panel/collapsiblepanel.d.mts","./node_modules/@base-ui/react/collapsible/index.parts.d.mts","./node_modules/@base-ui/react/collapsible/index.d.mts","./node_modules/@base-ui/react/combobox/root/comboboxroot.d.mts","./node_modules/@base-ui/react/combobox/label/comboboxlabel.d.mts","./node_modules/@base-ui/react/combobox/value/comboboxvalue.d.mts","./node_modules/@base-ui/react/combobox/input-group/comboboxinputgroup.d.mts","./node_modules/@base-ui/react/combobox/trigger/comboboxtrigger.d.mts","./node_modules/@base-ui/react/combobox/item/comboboxitem.d.mts","./node_modules/@base-ui/react/combobox/item-indicator/comboboxitemindicator.d.mts","./node_modules/@base-ui/react/combobox/chips/comboboxchips.d.mts","./node_modules/@base-ui/react/combobox/chip/comboboxchip.d.mts","./node_modules/@base-ui/react/combobox/chip-remove/comboboxchipremove.d.mts","./node_modules/@base-ui/react/combobox/separator/comboboxseparator.d.mts","./node_modules/@base-ui/react/combobox/index.parts.d.mts","./node_modules/@base-ui/react/combobox/index.d.mts","./node_modules/@base-ui/react/menu/arrow/menuarrow.d.mts","./node_modules/@base-ui/react/menu/backdrop/menubackdrop.d.mts","./node_modules/@base-ui/react/utils/adaptiveoriginconstants.d.mts","./node_modules/@base-ui/react/menu/store/menustore.d.mts","./node_modules/@base-ui/react/menu/root/menurootcontext.d.mts","./node_modules/@base-ui/react/menubar/menubarcontext.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/store/menuhandle.d.mts","./node_modules/@base-ui/react/menu/root/menuroot.d.mts","./node_modules/@base-ui/react/menu/checkbox-item/menucheckboxitem.d.mts","./node_modules/@base-ui/react/menu/checkbox-item-indicator/menucheckboxitemindicator.d.mts","./node_modules/@base-ui/react/menu/group/menugroup.d.mts","./node_modules/@base-ui/react/menu/group-label/menugrouplabel.d.mts","./node_modules/@base-ui/react/menu/item/menuitem.d.mts","./node_modules/@base-ui/react/menu/link-item/menulinkitem.d.mts","./node_modules/@base-ui/react/menu/popup/menupopup.d.mts","./node_modules/@base-ui/react/menu/portal/menuportal.d.mts","./node_modules/@base-ui/react/menu/positioner/menupositioner.d.mts","./node_modules/@base-ui/react/menu/radio-group/menuradiogroup.d.mts","./node_modules/@base-ui/react/menu/radio-item/menuradioitem.d.mts","./node_modules/@base-ui/react/menu/radio-item-indicator/menuradioitemindicator.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenuroot.d.mts","./node_modules/@base-ui/react/menu/trigger/menutrigger.d.mts","./node_modules/@base-ui/react/menu/viewport/menuviewport.d.mts","./node_modules/@base-ui/react/separator/separator.d.mts","./node_modules/@base-ui/react/menu/submenu-trigger/menusubmenutrigger.d.mts","./node_modules/@base-ui/react/menu/index.parts.d.mts","./node_modules/@base-ui/react/menu/index.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenuroot.d.mts","./node_modules/@base-ui/react/context-menu/trigger/contextmenutrigger.d.mts","./node_modules/@base-ui/react/context-menu/index.parts.d.mts","./node_modules/@base-ui/react/context-menu/index.d.mts","./node_modules/@base-ui/react/csp-provider/cspprovider.d.mts","./node_modules/@base-ui/react/csp-provider/index.parts.d.mts","./node_modules/@base-ui/react/csp-provider/index.d.mts","./node_modules/@base-ui/react/dialog/backdrop/dialogbackdrop.d.mts","./node_modules/@base-ui/react/dialog/close/dialogclose.d.mts","./node_modules/@base-ui/react/dialog/description/dialogdescription.d.mts","./node_modules/@base-ui/react/dialog/popup/dialogpopup.d.mts","./node_modules/@base-ui/react/dialog/portal/dialogportal.d.mts","./node_modules/@base-ui/react/dialog/viewport/dialogviewport.d.mts","./node_modules/@base-ui/react/dialog/title/dialogtitle.d.mts","./node_modules/@base-ui/react/dialog/trigger/dialogtrigger.d.mts","./node_modules/@base-ui/react/dialog/index.parts.d.mts","./node_modules/@base-ui/react/dialog/index.d.mts","./node_modules/@base-ui/react/internals/direction-context/directioncontext.d.mts","./node_modules/@base-ui/react/direction-provider/directionprovider.d.mts","./node_modules/@base-ui/react/direction-provider/index.parts.d.mts","./node_modules/@base-ui/react/direction-provider/index.d.mts","./node_modules/@base-ui/react/drawer/backdrop/drawerbackdrop.d.mts","./node_modules/@base-ui/react/drawer/close/drawerclose.d.mts","./node_modules/@base-ui/react/drawer/content/drawercontent.d.mts","./node_modules/@base-ui/react/drawer/description/drawerdescription.d.mts","./node_modules/@base-ui/react/drawer/indent/drawerindent.d.mts","./node_modules/@base-ui/react/drawer/indent-background/drawerindentbackground.d.mts","./node_modules/@base-ui/react/utils/useswipedismiss.d.mts","./node_modules/@base-ui/react/drawer/handle.d.mts","./node_modules/@base-ui/react/drawer/root/drawerroot.d.mts","./node_modules/@base-ui/react/drawer/root/drawerrootcontext.d.mts","./node_modules/@base-ui/react/drawer/popup/drawerpopup.d.mts","./node_modules/@base-ui/react/drawer/portal/drawerportal.d.mts","./node_modules/@base-ui/react/drawer/provider/drawerprovider.d.mts","./node_modules/@base-ui/react/drawer/swipe-area/drawerswipearea.d.mts","./node_modules/@base-ui/react/drawer/title/drawertitle.d.mts","./node_modules/@base-ui/react/drawer/trigger/drawertrigger.d.mts","./node_modules/@base-ui/react/drawer/viewport/drawerviewport.d.mts","./node_modules/@base-ui/react/drawer/virtual-keyboard-provider/drawervirtualkeyboardprovider.d.mts","./node_modules/@base-ui/react/drawer/index.parts.d.mts","./node_modules/@base-ui/react/drawer/index.d.mts","./node_modules/@base-ui/react/field/label/fieldlabel.d.mts","./node_modules/@base-ui/react/field/error/fielderror.d.mts","./node_modules/@base-ui/react/field/description/fielddescription.d.mts","./node_modules/@base-ui/react/field/control/fieldcontrol.d.mts","./node_modules/@base-ui/react/field/validity/fieldvalidity.d.mts","./node_modules/@base-ui/react/field/item/fielditem.d.mts","./node_modules/@base-ui/react/field/index.parts.d.mts","./node_modules/@base-ui/react/field/index.d.mts","./node_modules/@base-ui/react/fieldset/root/fieldsetroot.d.mts","./node_modules/@base-ui/react/fieldset/legend/fieldsetlegend.d.mts","./node_modules/@base-ui/react/fieldset/index.parts.d.mts","./node_modules/@base-ui/react/fieldset/index.d.mts","./node_modules/@base-ui/react/input/input.d.mts","./node_modules/@base-ui/react/input/index.d.mts","./node_modules/@base-ui/react/menubar/menubar.d.mts","./node_modules/@base-ui/react/menubar/index.d.mts","./node_modules/@base-ui/react/merge-props/mergeprops.d.mts","./node_modules/@base-ui/react/merge-props/index.d.mts","./node_modules/@base-ui/react/meter/root/meterroot.d.mts","./node_modules/@base-ui/react/meter/track/metertrack.d.mts","./node_modules/@base-ui/react/meter/indicator/meterindicator.d.mts","./node_modules/@base-ui/react/meter/value/metervalue.d.mts","./node_modules/@base-ui/react/meter/label/meterlabel.d.mts","./node_modules/@base-ui/react/meter/index.parts.d.mts","./node_modules/@base-ui/react/meter/index.d.mts","./node_modules/@base-ui/react/navigation-menu/root/navigationmenuroot.d.mts","./node_modules/@base-ui/react/navigation-menu/list/navigationmenulist.d.mts","./node_modules/@base-ui/react/navigation-menu/item/navigationmenuitem.d.mts","./node_modules/@base-ui/react/navigation-menu/content/navigationmenucontent.d.mts","./node_modules/@base-ui/react/navigation-menu/trigger/navigationmenutrigger.d.mts","./node_modules/@base-ui/react/navigation-menu/portal/navigationmenuportal.d.mts","./node_modules/@base-ui/react/navigation-menu/positioner/navigationmenupositioner.d.mts","./node_modules/@base-ui/react/navigation-menu/viewport/navigationmenuviewport.d.mts","./node_modules/@base-ui/react/navigation-menu/backdrop/navigationmenubackdrop.d.mts","./node_modules/@base-ui/react/navigation-menu/popup/navigationmenupopup.d.mts","./node_modules/@base-ui/react/navigation-menu/arrow/navigationmenuarrow.d.mts","./node_modules/@base-ui/react/navigation-menu/link/navigationmenulink.d.mts","./node_modules/@base-ui/react/navigation-menu/icon/navigationmenuicon.d.mts","./node_modules/@base-ui/react/navigation-menu/index.parts.d.mts","./node_modules/@base-ui/react/navigation-menu/index.d.mts","./node_modules/@base-ui/react/number-field/utils/types.d.mts","./node_modules/@base-ui/react/number-field/root/numberfieldroot.d.mts","./node_modules/@base-ui/react/number-field/group/numberfieldgroup.d.mts","./node_modules/@base-ui/react/number-field/increment/numberfieldincrement.d.mts","./node_modules/@base-ui/react/number-field/decrement/numberfielddecrement.d.mts","./node_modules/@base-ui/react/number-field/input/numberfieldinput.d.mts","./node_modules/@base-ui/react/number-field/scrub-area/numberfieldscrubarea.d.mts","./node_modules/@base-ui/react/number-field/scrub-area-cursor/numberfieldscrubareacursor.d.mts","./node_modules/@base-ui/react/number-field/index.parts.d.mts","./node_modules/@base-ui/react/number-field/index.d.mts","./node_modules/@base-ui/react/otp-field/utils/otp.d.mts","./node_modules/@base-ui/react/otp-field/root/otpfieldroot.d.mts","./node_modules/@base-ui/react/otp-field/input/otpfieldinput.d.mts","./node_modules/@base-ui/react/otp-field/index.parts.d.mts","./node_modules/@base-ui/react/otp-field/index.d.mts","./node_modules/@base-ui/utils/usetimeout.d.mts","./node_modules/@base-ui/react/popover/store/popoverstore.d.mts","./node_modules/@base-ui/react/popover/store/popoverhandle.d.mts","./node_modules/@base-ui/react/popover/root/popoverroot.d.mts","./node_modules/@base-ui/react/popover/trigger/popovertrigger.d.mts","./node_modules/@base-ui/react/popover/portal/popoverportal.d.mts","./node_modules/@base-ui/react/popover/positioner/popoverpositioner.d.mts","./node_modules/@base-ui/react/popover/popup/popoverpopup.d.mts","./node_modules/@base-ui/react/popover/arrow/popoverarrow.d.mts","./node_modules/@base-ui/react/popover/backdrop/popoverbackdrop.d.mts","./node_modules/@base-ui/react/popover/title/popovertitle.d.mts","./node_modules/@base-ui/react/popover/description/popoverdescription.d.mts","./node_modules/@base-ui/react/popover/close/popoverclose.d.mts","./node_modules/@base-ui/react/popover/viewport/popoverviewport.d.mts","./node_modules/@base-ui/react/popover/index.parts.d.mts","./node_modules/@base-ui/react/popover/index.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardstore.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardhandle.d.mts","./node_modules/@base-ui/react/preview-card/root/previewcardroot.d.mts","./node_modules/@base-ui/react/preview-card/portal/previewcardportal.d.mts","./node_modules/@base-ui/react/preview-card/trigger/previewcardtrigger.d.mts","./node_modules/@base-ui/react/preview-card/positioner/previewcardpositioner.d.mts","./node_modules/@base-ui/react/preview-card/popup/previewcardpopup.d.mts","./node_modules/@base-ui/react/preview-card/arrow/previewcardarrow.d.mts","./node_modules/@base-ui/react/preview-card/backdrop/previewcardbackdrop.d.mts","./node_modules/@base-ui/react/preview-card/viewport/previewcardviewport.d.mts","./node_modules/@base-ui/react/preview-card/index.parts.d.mts","./node_modules/@base-ui/react/preview-card/index.d.mts","./node_modules/@base-ui/react/progress/root/progressroot.d.mts","./node_modules/@base-ui/react/progress/track/progresstrack.d.mts","./node_modules/@base-ui/react/progress/indicator/progressindicator.d.mts","./node_modules/@base-ui/react/progress/value/progressvalue.d.mts","./node_modules/@base-ui/react/progress/label/progresslabel.d.mts","./node_modules/@base-ui/react/progress/index.parts.d.mts","./node_modules/@base-ui/react/progress/index.d.mts","./node_modules/@base-ui/react/radio/root/radioroot.d.mts","./node_modules/@base-ui/react/radio/indicator/radioindicator.d.mts","./node_modules/@base-ui/react/radio/index.parts.d.mts","./node_modules/@base-ui/react/radio/index.d.mts","./node_modules/@base-ui/react/radio-group/radiogroup.d.mts","./node_modules/@base-ui/react/radio-group/index.d.mts","./node_modules/@base-ui/react/scroll-area/root/scrollarearoot.d.mts","./node_modules/@base-ui/react/scroll-area/viewport/scrollareaviewport.d.mts","./node_modules/@base-ui/react/scroll-area/scrollbar/scrollareascrollbar.d.mts","./node_modules/@base-ui/react/scroll-area/content/scrollareacontent.d.mts","./node_modules/@base-ui/react/scroll-area/thumb/scrollareathumb.d.mts","./node_modules/@base-ui/react/scroll-area/corner/scrollareacorner.d.mts","./node_modules/@base-ui/react/scroll-area/index.parts.d.mts","./node_modules/@base-ui/react/scroll-area/index.d.mts","./node_modules/@base-ui/react/select/root/selectroot.d.mts","./node_modules/@base-ui/react/select/label/selectlabel.d.mts","./node_modules/@base-ui/react/select/trigger/selecttrigger.d.mts","./node_modules/@base-ui/react/select/value/selectvalue.d.mts","./node_modules/@base-ui/react/select/icon/selecticon.d.mts","./node_modules/@base-ui/react/select/portal/selectportal.d.mts","./node_modules/@base-ui/react/select/backdrop/selectbackdrop.d.mts","./node_modules/@base-ui/react/select/positioner/selectpositioner.d.mts","./node_modules/@base-ui/react/select/popup/selectpopup.d.mts","./node_modules/@base-ui/react/select/list/selectlist.d.mts","./node_modules/@base-ui/react/select/item/selectitem.d.mts","./node_modules/@base-ui/react/select/item-indicator/selectitemindicator.d.mts","./node_modules/@base-ui/react/select/item-text/selectitemtext.d.mts","./node_modules/@base-ui/react/select/arrow/selectarrow.d.mts","./node_modules/@base-ui/react/select/scroll-down-arrow/selectscrolldownarrow.d.mts","./node_modules/@base-ui/react/select/scroll-up-arrow/selectscrolluparrow.d.mts","./node_modules/@base-ui/react/select/group/selectgroup.d.mts","./node_modules/@base-ui/react/select/group-label/selectgrouplabel.d.mts","./node_modules/@base-ui/react/select/separator/selectseparator.d.mts","./node_modules/@base-ui/react/select/index.parts.d.mts","./node_modules/@base-ui/react/select/index.d.mts","./node_modules/@base-ui/react/separator/index.d.mts","./node_modules/@base-ui/react/slider/root/sliderroot.d.mts","./node_modules/@base-ui/react/slider/label/sliderlabel.d.mts","./node_modules/@base-ui/react/slider/value/slidervalue.d.mts","./node_modules/@base-ui/react/slider/control/slidercontrol.d.mts","./node_modules/@base-ui/react/slider/track/slidertrack.d.mts","./node_modules/@base-ui/react/internals/labelable-provider/labelablecontext.d.mts","./node_modules/@base-ui/react/slider/thumb/sliderthumb.d.mts","./node_modules/@base-ui/react/slider/indicator/sliderindicator.d.mts","./node_modules/@base-ui/react/slider/index.parts.d.mts","./node_modules/@base-ui/react/slider/index.d.mts","./node_modules/@base-ui/react/switch/root/switchroot.d.mts","./node_modules/@base-ui/react/switch/thumb/switchthumb.d.mts","./node_modules/@base-ui/react/switch/index.parts.d.mts","./node_modules/@base-ui/react/switch/index.d.mts","./node_modules/@base-ui/react/tabs/tab/tabstab.d.mts","./node_modules/@base-ui/react/tabs/root/tabsroot.d.mts","./node_modules/@base-ui/react/tabs/indicator/tabsindicator.d.mts","./node_modules/@base-ui/react/tabs/panel/tabspanel.d.mts","./node_modules/@base-ui/react/tabs/list/tabslist.d.mts","./node_modules/@base-ui/react/tabs/index.parts.d.mts","./node_modules/@base-ui/react/tabs/index.d.mts","./node_modules/@base-ui/react/toast/positioner/toastpositioner.d.mts","./node_modules/@base-ui/react/toast/usetoastmanager.d.mts","./node_modules/@base-ui/react/toast/createtoastmanager.d.mts","./node_modules/@base-ui/react/toast/provider/toastprovider.d.mts","./node_modules/@base-ui/react/toast/viewport/toastviewport.d.mts","./node_modules/@base-ui/react/toast/root/toastroot.d.mts","./node_modules/@base-ui/react/toast/content/toastcontent.d.mts","./node_modules/@base-ui/react/toast/description/toastdescription.d.mts","./node_modules/@base-ui/react/toast/title/toasttitle.d.mts","./node_modules/@base-ui/react/toast/close/toastclose.d.mts","./node_modules/@base-ui/react/toast/action/toastaction.d.mts","./node_modules/@base-ui/react/toast/portal/toastportal.d.mts","./node_modules/@base-ui/react/toast/arrow/toastarrow.d.mts","./node_modules/@base-ui/react/toast/index.parts.d.mts","./node_modules/@base-ui/react/toast/index.d.mts","./node_modules/@base-ui/react/toggle/toggle.d.mts","./node_modules/@base-ui/react/toggle/index.d.mts","./node_modules/@base-ui/react/toggle-group/togglegroup.d.mts","./node_modules/@base-ui/react/toggle-group/index.d.mts","./node_modules/@base-ui/react/toolbar/separator/toolbarseparator.d.mts","./node_modules/@base-ui/react/toolbar/root/toolbarroot.d.mts","./node_modules/@base-ui/react/toolbar/group/toolbargroup.d.mts","./node_modules/@base-ui/react/toolbar/button/toolbarbutton.d.mts","./node_modules/@base-ui/react/toolbar/link/toolbarlink.d.mts","./node_modules/@base-ui/react/toolbar/input/toolbarinput.d.mts","./node_modules/@base-ui/react/toolbar/index.parts.d.mts","./node_modules/@base-ui/react/toolbar/index.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltipstore.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltiphandle.d.mts","./node_modules/@base-ui/react/tooltip/root/tooltiproot.d.mts","./node_modules/@base-ui/react/tooltip/trigger/tooltiptrigger.d.mts","./node_modules/@base-ui/react/tooltip/portal/tooltipportal.d.mts","./node_modules/@base-ui/react/tooltip/positioner/tooltippositioner.d.mts","./node_modules/@base-ui/react/tooltip/popup/tooltippopup.d.mts","./node_modules/@base-ui/react/tooltip/arrow/tooltiparrow.d.mts","./node_modules/@base-ui/react/tooltip/provider/tooltipprovider.d.mts","./node_modules/@base-ui/react/tooltip/viewport/tooltipviewport.d.mts","./node_modules/@base-ui/react/tooltip/index.parts.d.mts","./node_modules/@base-ui/react/tooltip/index.d.mts","./node_modules/@base-ui/react/use-render/userender.d.mts","./node_modules/@base-ui/react/use-render/index.d.mts","./node_modules/@base-ui/react/index.d.mts","./node_modules/@base-ui/react/dialog/store/dialogstore.d.mts","./node_modules/@base-ui/react/dialog/store/dialoghandle.d.mts","./node_modules/@base-ui/react/dialog/root/dialogroot.d.mts","./node_modules/@base-ui/react/alert-dialog/handle.d.mts","./node_modules/@base-ui/react/alert-dialog/root/alertdialogroot.d.mts","./node_modules/@base-ui/react/alert-dialog/trigger/alertdialogtrigger.d.mts","./node_modules/@base-ui/react/alert-dialog/index.parts.d.mts","./node_modules/@base-ui/react/alert-dialog/index.d.mts","./node_modules/class-variance-authority/dist/types.d.ts","./node_modules/class-variance-authority/dist/index.d.ts","./components/ui/button.tsx","./components/ui/alert-dialog.tsx","./components/ui/alert.tsx","./components/ui/aspect-ratio.tsx","./components/ui/attachment.tsx","./components/ui/avatar.tsx","./components/ui/badge.tsx","./components/ui/breadcrumb.tsx","./components/ui/bubble.tsx","./components/ui/separator.tsx","./components/ui/button-group.tsx","./node_modules/react-day-picker/dist/esm/ui.d.ts","./node_modules/date-fns/constants.d.ts","./node_modules/date-fns/locale/types.d.ts","./node_modules/date-fns/fp/types.d.ts","./node_modules/date-fns/types.d.ts","./node_modules/date-fns/add.d.ts","./node_modules/date-fns/addbusinessdays.d.ts","./node_modules/date-fns/adddays.d.ts","./node_modules/date-fns/addhours.d.ts","./node_modules/date-fns/addisoweekyears.d.ts","./node_modules/date-fns/addmilliseconds.d.ts","./node_modules/date-fns/addminutes.d.ts","./node_modules/date-fns/addmonths.d.ts","./node_modules/date-fns/addquarters.d.ts","./node_modules/date-fns/addseconds.d.ts","./node_modules/date-fns/addweeks.d.ts","./node_modules/date-fns/addyears.d.ts","./node_modules/date-fns/areintervalsoverlapping.d.ts","./node_modules/date-fns/clamp.d.ts","./node_modules/date-fns/closestindexto.d.ts","./node_modules/date-fns/closestto.d.ts","./node_modules/date-fns/compareasc.d.ts","./node_modules/date-fns/comparedesc.d.ts","./node_modules/date-fns/constructfrom.d.ts","./node_modules/date-fns/constructnow.d.ts","./node_modules/date-fns/daystoweeks.d.ts","./node_modules/date-fns/differenceinbusinessdays.d.ts","./node_modules/date-fns/differenceincalendardays.d.ts","./node_modules/date-fns/differenceincalendarisoweekyears.d.ts","./node_modules/date-fns/differenceincalendarisoweeks.d.ts","./node_modules/date-fns/differenceincalendarmonths.d.ts","./node_modules/date-fns/differenceincalendarquarters.d.ts","./node_modules/date-fns/differenceincalendarweeks.d.ts","./node_modules/date-fns/differenceincalendaryears.d.ts","./node_modules/date-fns/differenceindays.d.ts","./node_modules/date-fns/differenceinhours.d.ts","./node_modules/date-fns/differenceinisoweekyears.d.ts","./node_modules/date-fns/differenceinmilliseconds.d.ts","./node_modules/date-fns/differenceinminutes.d.ts","./node_modules/date-fns/differenceinmonths.d.ts","./node_modules/date-fns/differenceinquarters.d.ts","./node_modules/date-fns/differenceinseconds.d.ts","./node_modules/date-fns/differenceinweeks.d.ts","./node_modules/date-fns/differenceinyears.d.ts","./node_modules/date-fns/eachdayofinterval.d.ts","./node_modules/date-fns/eachhourofinterval.d.ts","./node_modules/date-fns/eachminuteofinterval.d.ts","./node_modules/date-fns/eachmonthofinterval.d.ts","./node_modules/date-fns/eachquarterofinterval.d.ts","./node_modules/date-fns/eachweekofinterval.d.ts","./node_modules/date-fns/eachweekendofinterval.d.ts","./node_modules/date-fns/eachweekendofmonth.d.ts","./node_modules/date-fns/eachweekendofyear.d.ts","./node_modules/date-fns/eachyearofinterval.d.ts","./node_modules/date-fns/endofday.d.ts","./node_modules/date-fns/endofdecade.d.ts","./node_modules/date-fns/endofhour.d.ts","./node_modules/date-fns/endofisoweek.d.ts","./node_modules/date-fns/endofisoweekyear.d.ts","./node_modules/date-fns/endofminute.d.ts","./node_modules/date-fns/endofmonth.d.ts","./node_modules/date-fns/endofquarter.d.ts","./node_modules/date-fns/endofsecond.d.ts","./node_modules/date-fns/endoftoday.d.ts","./node_modules/date-fns/endoftomorrow.d.ts","./node_modules/date-fns/endofweek.d.ts","./node_modules/date-fns/endofyear.d.ts","./node_modules/date-fns/endofyesterday.d.ts","./node_modules/date-fns/_lib/format/formatters.d.ts","./node_modules/date-fns/_lib/format/longformatters.d.ts","./node_modules/date-fns/format.d.ts","./node_modules/date-fns/formatdistance.d.ts","./node_modules/date-fns/formatdistancestrict.d.ts","./node_modules/date-fns/formatdistancetonow.d.ts","./node_modules/date-fns/formatdistancetonowstrict.d.ts","./node_modules/date-fns/formatduration.d.ts","./node_modules/date-fns/formatiso.d.ts","./node_modules/date-fns/formatiso9075.d.ts","./node_modules/date-fns/formatisoduration.d.ts","./node_modules/date-fns/formatrfc3339.d.ts","./node_modules/date-fns/formatrfc7231.d.ts","./node_modules/date-fns/formatrelative.d.ts","./node_modules/date-fns/fromunixtime.d.ts","./node_modules/date-fns/getdate.d.ts","./node_modules/date-fns/getday.d.ts","./node_modules/date-fns/getdayofyear.d.ts","./node_modules/date-fns/getdaysinmonth.d.ts","./node_modules/date-fns/getdaysinyear.d.ts","./node_modules/date-fns/getdecade.d.ts","./node_modules/date-fns/_lib/defaultoptions.d.ts","./node_modules/date-fns/getdefaultoptions.d.ts","./node_modules/date-fns/gethours.d.ts","./node_modules/date-fns/getisoday.d.ts","./node_modules/date-fns/getisoweek.d.ts","./node_modules/date-fns/getisoweekyear.d.ts","./node_modules/date-fns/getisoweeksinyear.d.ts","./node_modules/date-fns/getmilliseconds.d.ts","./node_modules/date-fns/getminutes.d.ts","./node_modules/date-fns/getmonth.d.ts","./node_modules/date-fns/getoverlappingdaysinintervals.d.ts","./node_modules/date-fns/getquarter.d.ts","./node_modules/date-fns/getseconds.d.ts","./node_modules/date-fns/gettime.d.ts","./node_modules/date-fns/getunixtime.d.ts","./node_modules/date-fns/getweek.d.ts","./node_modules/date-fns/getweekofmonth.d.ts","./node_modules/date-fns/getweekyear.d.ts","./node_modules/date-fns/getweeksinmonth.d.ts","./node_modules/date-fns/getyear.d.ts","./node_modules/date-fns/hourstomilliseconds.d.ts","./node_modules/date-fns/hourstominutes.d.ts","./node_modules/date-fns/hourstoseconds.d.ts","./node_modules/date-fns/interval.d.ts","./node_modules/date-fns/intervaltoduration.d.ts","./node_modules/date-fns/intlformat.d.ts","./node_modules/date-fns/intlformatdistance.d.ts","./node_modules/date-fns/isafter.d.ts","./node_modules/date-fns/isbefore.d.ts","./node_modules/date-fns/isdate.d.ts","./node_modules/date-fns/isequal.d.ts","./node_modules/date-fns/isexists.d.ts","./node_modules/date-fns/isfirstdayofmonth.d.ts","./node_modules/date-fns/isfriday.d.ts","./node_modules/date-fns/isfuture.d.ts","./node_modules/date-fns/islastdayofmonth.d.ts","./node_modules/date-fns/isleapyear.d.ts","./node_modules/date-fns/ismatch.d.ts","./node_modules/date-fns/ismonday.d.ts","./node_modules/date-fns/ispast.d.ts","./node_modules/date-fns/issameday.d.ts","./node_modules/date-fns/issamehour.d.ts","./node_modules/date-fns/issameisoweek.d.ts","./node_modules/date-fns/issameisoweekyear.d.ts","./node_modules/date-fns/issameminute.d.ts","./node_modules/date-fns/issamemonth.d.ts","./node_modules/date-fns/issamequarter.d.ts","./node_modules/date-fns/issamesecond.d.ts","./node_modules/date-fns/issameweek.d.ts","./node_modules/date-fns/issameyear.d.ts","./node_modules/date-fns/issaturday.d.ts","./node_modules/date-fns/issunday.d.ts","./node_modules/date-fns/isthishour.d.ts","./node_modules/date-fns/isthisisoweek.d.ts","./node_modules/date-fns/isthisminute.d.ts","./node_modules/date-fns/isthismonth.d.ts","./node_modules/date-fns/isthisquarter.d.ts","./node_modules/date-fns/isthissecond.d.ts","./node_modules/date-fns/isthisweek.d.ts","./node_modules/date-fns/isthisyear.d.ts","./node_modules/date-fns/isthursday.d.ts","./node_modules/date-fns/istoday.d.ts","./node_modules/date-fns/istomorrow.d.ts","./node_modules/date-fns/istuesday.d.ts","./node_modules/date-fns/isvalid.d.ts","./node_modules/date-fns/iswednesday.d.ts","./node_modules/date-fns/isweekend.d.ts","./node_modules/date-fns/iswithininterval.d.ts","./node_modules/date-fns/isyesterday.d.ts","./node_modules/date-fns/lastdayofdecade.d.ts","./node_modules/date-fns/lastdayofisoweek.d.ts","./node_modules/date-fns/lastdayofisoweekyear.d.ts","./node_modules/date-fns/lastdayofmonth.d.ts","./node_modules/date-fns/lastdayofquarter.d.ts","./node_modules/date-fns/lastdayofweek.d.ts","./node_modules/date-fns/lastdayofyear.d.ts","./node_modules/date-fns/_lib/format/lightformatters.d.ts","./node_modules/date-fns/lightformat.d.ts","./node_modules/date-fns/max.d.ts","./node_modules/date-fns/milliseconds.d.ts","./node_modules/date-fns/millisecondstohours.d.ts","./node_modules/date-fns/millisecondstominutes.d.ts","./node_modules/date-fns/millisecondstoseconds.d.ts","./node_modules/date-fns/min.d.ts","./node_modules/date-fns/minutestohours.d.ts","./node_modules/date-fns/minutestomilliseconds.d.ts","./node_modules/date-fns/minutestoseconds.d.ts","./node_modules/date-fns/monthstoquarters.d.ts","./node_modules/date-fns/monthstoyears.d.ts","./node_modules/date-fns/nextday.d.ts","./node_modules/date-fns/nextfriday.d.ts","./node_modules/date-fns/nextmonday.d.ts","./node_modules/date-fns/nextsaturday.d.ts","./node_modules/date-fns/nextsunday.d.ts","./node_modules/date-fns/nextthursday.d.ts","./node_modules/date-fns/nexttuesday.d.ts","./node_modules/date-fns/nextwednesday.d.ts","./node_modules/date-fns/parse/_lib/types.d.ts","./node_modules/date-fns/parse/_lib/setter.d.ts","./node_modules/date-fns/parse/_lib/parser.d.ts","./node_modules/date-fns/parse/_lib/parsers.d.ts","./node_modules/date-fns/parse.d.ts","./node_modules/date-fns/parseiso.d.ts","./node_modules/date-fns/parsejson.d.ts","./node_modules/date-fns/previousday.d.ts","./node_modules/date-fns/previousfriday.d.ts","./node_modules/date-fns/previousmonday.d.ts","./node_modules/date-fns/previoussaturday.d.ts","./node_modules/date-fns/previoussunday.d.ts","./node_modules/date-fns/previousthursday.d.ts","./node_modules/date-fns/previoustuesday.d.ts","./node_modules/date-fns/previouswednesday.d.ts","./node_modules/date-fns/quarterstomonths.d.ts","./node_modules/date-fns/quarterstoyears.d.ts","./node_modules/date-fns/roundtonearesthours.d.ts","./node_modules/date-fns/roundtonearestminutes.d.ts","./node_modules/date-fns/secondstohours.d.ts","./node_modules/date-fns/secondstomilliseconds.d.ts","./node_modules/date-fns/secondstominutes.d.ts","./node_modules/date-fns/set.d.ts","./node_modules/date-fns/setdate.d.ts","./node_modules/date-fns/setday.d.ts","./node_modules/date-fns/setdayofyear.d.ts","./node_modules/date-fns/setdefaultoptions.d.ts","./node_modules/date-fns/sethours.d.ts","./node_modules/date-fns/setisoday.d.ts","./node_modules/date-fns/setisoweek.d.ts","./node_modules/date-fns/setisoweekyear.d.ts","./node_modules/date-fns/setmilliseconds.d.ts","./node_modules/date-fns/setminutes.d.ts","./node_modules/date-fns/setmonth.d.ts","./node_modules/date-fns/setquarter.d.ts","./node_modules/date-fns/setseconds.d.ts","./node_modules/date-fns/setweek.d.ts","./node_modules/date-fns/setweekyear.d.ts","./node_modules/date-fns/setyear.d.ts","./node_modules/date-fns/startofday.d.ts","./node_modules/date-fns/startofdecade.d.ts","./node_modules/date-fns/startofhour.d.ts","./node_modules/date-fns/startofisoweek.d.ts","./node_modules/date-fns/startofisoweekyear.d.ts","./node_modules/date-fns/startofminute.d.ts","./node_modules/date-fns/startofmonth.d.ts","./node_modules/date-fns/startofquarter.d.ts","./node_modules/date-fns/startofsecond.d.ts","./node_modules/date-fns/startoftoday.d.ts","./node_modules/date-fns/startoftomorrow.d.ts","./node_modules/date-fns/startofweek.d.ts","./node_modules/date-fns/startofweekyear.d.ts","./node_modules/date-fns/startofyear.d.ts","./node_modules/date-fns/startofyesterday.d.ts","./node_modules/date-fns/sub.d.ts","./node_modules/date-fns/subbusinessdays.d.ts","./node_modules/date-fns/subdays.d.ts","./node_modules/date-fns/subhours.d.ts","./node_modules/date-fns/subisoweekyears.d.ts","./node_modules/date-fns/submilliseconds.d.ts","./node_modules/date-fns/subminutes.d.ts","./node_modules/date-fns/submonths.d.ts","./node_modules/date-fns/subquarters.d.ts","./node_modules/date-fns/subseconds.d.ts","./node_modules/date-fns/subweeks.d.ts","./node_modules/date-fns/subyears.d.ts","./node_modules/date-fns/todate.d.ts","./node_modules/date-fns/transpose.d.ts","./node_modules/date-fns/weekstodays.d.ts","./node_modules/date-fns/yearstodays.d.ts","./node_modules/date-fns/yearstomonths.d.ts","./node_modules/date-fns/yearstoquarters.d.ts","./node_modules/date-fns/index.d.ts","./node_modules/date-fns/locale/af.d.ts","./node_modules/date-fns/locale/ar.d.ts","./node_modules/date-fns/locale/ar-dz.d.ts","./node_modules/date-fns/locale/ar-eg.d.ts","./node_modules/date-fns/locale/ar-ma.d.ts","./node_modules/date-fns/locale/ar-sa.d.ts","./node_modules/date-fns/locale/ar-tn.d.ts","./node_modules/date-fns/locale/az.d.ts","./node_modules/date-fns/locale/be.d.ts","./node_modules/date-fns/locale/be-tarask.d.ts","./node_modules/date-fns/locale/bg.d.ts","./node_modules/date-fns/locale/bn.d.ts","./node_modules/date-fns/locale/bs.d.ts","./node_modules/date-fns/locale/ca.d.ts","./node_modules/date-fns/locale/ckb.d.ts","./node_modules/date-fns/locale/cs.d.ts","./node_modules/date-fns/locale/cy.d.ts","./node_modules/date-fns/locale/da.d.ts","./node_modules/date-fns/locale/de.d.ts","./node_modules/date-fns/locale/de-at.d.ts","./node_modules/date-fns/locale/el.d.ts","./node_modules/date-fns/locale/en-au.d.ts","./node_modules/date-fns/locale/en-ca.d.ts","./node_modules/date-fns/locale/en-gb.d.ts","./node_modules/date-fns/locale/en-ie.d.ts","./node_modules/date-fns/locale/en-in.d.ts","./node_modules/date-fns/locale/en-nz.d.ts","./node_modules/date-fns/locale/en-us.d.ts","./node_modules/date-fns/locale/en-za.d.ts","./node_modules/date-fns/locale/eo.d.ts","./node_modules/date-fns/locale/es.d.ts","./node_modules/date-fns/locale/et.d.ts","./node_modules/date-fns/locale/eu.d.ts","./node_modules/date-fns/locale/fa-ir.d.ts","./node_modules/date-fns/locale/fi.d.ts","./node_modules/date-fns/locale/fr.d.ts","./node_modules/date-fns/locale/fr-ca.d.ts","./node_modules/date-fns/locale/fr-ch.d.ts","./node_modules/date-fns/locale/fy.d.ts","./node_modules/date-fns/locale/gd.d.ts","./node_modules/date-fns/locale/gl.d.ts","./node_modules/date-fns/locale/gu.d.ts","./node_modules/date-fns/locale/he.d.ts","./node_modules/date-fns/locale/hi.d.ts","./node_modules/date-fns/locale/hr.d.ts","./node_modules/date-fns/locale/ht.d.ts","./node_modules/date-fns/locale/hu.d.ts","./node_modules/date-fns/locale/hy.d.ts","./node_modules/date-fns/locale/id.d.ts","./node_modules/date-fns/locale/is.d.ts","./node_modules/date-fns/locale/it.d.ts","./node_modules/date-fns/locale/it-ch.d.ts","./node_modules/date-fns/locale/ja.d.ts","./node_modules/date-fns/locale/ja-hira.d.ts","./node_modules/date-fns/locale/ka.d.ts","./node_modules/date-fns/locale/kk.d.ts","./node_modules/date-fns/locale/km.d.ts","./node_modules/date-fns/locale/kn.d.ts","./node_modules/date-fns/locale/ko.d.ts","./node_modules/date-fns/locale/lb.d.ts","./node_modules/date-fns/locale/lt.d.ts","./node_modules/date-fns/locale/lv.d.ts","./node_modules/date-fns/locale/mk.d.ts","./node_modules/date-fns/locale/mn.d.ts","./node_modules/date-fns/locale/ms.d.ts","./node_modules/date-fns/locale/mt.d.ts","./node_modules/date-fns/locale/nb.d.ts","./node_modules/date-fns/locale/nl.d.ts","./node_modules/date-fns/locale/nl-be.d.ts","./node_modules/date-fns/locale/nn.d.ts","./node_modules/date-fns/locale/oc.d.ts","./node_modules/date-fns/locale/pl.d.ts","./node_modules/date-fns/locale/pt.d.ts","./node_modules/date-fns/locale/pt-br.d.ts","./node_modules/date-fns/locale/ro.d.ts","./node_modules/date-fns/locale/ru.d.ts","./node_modules/date-fns/locale/se.d.ts","./node_modules/date-fns/locale/sk.d.ts","./node_modules/date-fns/locale/sl.d.ts","./node_modules/date-fns/locale/sq.d.ts","./node_modules/date-fns/locale/sr.d.ts","./node_modules/date-fns/locale/sr-latn.d.ts","./node_modules/date-fns/locale/sv.d.ts","./node_modules/date-fns/locale/ta.d.ts","./node_modules/date-fns/locale/te.d.ts","./node_modules/date-fns/locale/th.d.ts","./node_modules/date-fns/locale/tr.d.ts","./node_modules/date-fns/locale/ug.d.ts","./node_modules/date-fns/locale/uk.d.ts","./node_modules/date-fns/locale/uz.d.ts","./node_modules/date-fns/locale/uz-cyrl.d.ts","./node_modules/date-fns/locale/vi.d.ts","./node_modules/date-fns/locale/zh-cn.d.ts","./node_modules/date-fns/locale/zh-hk.d.ts","./node_modules/date-fns/locale/zh-tw.d.ts","./node_modules/date-fns/locale.d.ts","./node_modules/react-day-picker/dist/esm/components/button.d.ts","./node_modules/react-day-picker/dist/esm/components/captionlabel.d.ts","./node_modules/react-day-picker/dist/esm/components/chevron.d.ts","./node_modules/react-day-picker/dist/esm/components/day.d.ts","./node_modules/react-day-picker/dist/esm/components/daybutton.d.ts","./node_modules/react-day-picker/dist/esm/components/dropdown.d.ts","./node_modules/react-day-picker/dist/esm/components/dropdownnav.d.ts","./node_modules/react-day-picker/dist/esm/components/footer.d.ts","./node_modules/react-day-picker/dist/esm/classes/calendarweek.d.ts","./node_modules/react-day-picker/dist/esm/classes/calendarmonth.d.ts","./node_modules/react-day-picker/dist/esm/components/month.d.ts","./node_modules/react-day-picker/dist/esm/components/monthgrid.d.ts","./node_modules/react-day-picker/dist/esm/components/months.d.ts","./node_modules/react-day-picker/dist/esm/components/monthsdropdown.d.ts","./node_modules/react-day-picker/dist/esm/components/nav.d.ts","./node_modules/react-day-picker/dist/esm/components/nextmonthbutton.d.ts","./node_modules/react-day-picker/dist/esm/components/option.d.ts","./node_modules/react-day-picker/dist/esm/components/previousmonthbutton.d.ts","./node_modules/react-day-picker/dist/esm/components/root.d.ts","./node_modules/react-day-picker/dist/esm/components/select.d.ts","./node_modules/react-day-picker/dist/esm/components/week.d.ts","./node_modules/react-day-picker/dist/esm/components/weekday.d.ts","./node_modules/react-day-picker/dist/esm/components/weekdays.d.ts","./node_modules/react-day-picker/dist/esm/components/weeknumber.d.ts","./node_modules/react-day-picker/dist/esm/components/weeknumberheader.d.ts","./node_modules/react-day-picker/dist/esm/components/weeks.d.ts","./node_modules/react-day-picker/dist/esm/components/yearsdropdown.d.ts","./node_modules/react-day-picker/dist/esm/components/custom-components.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatcaption.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatday.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatmonthdropdown.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatweeknumber.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatweeknumberheader.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatweekdayname.d.ts","./node_modules/react-day-picker/dist/esm/formatters/formatyeardropdown.d.ts","./node_modules/react-day-picker/dist/esm/formatters/index.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelgrid.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelgridcell.d.ts","./node_modules/react-day-picker/dist/esm/labels/labeldaybutton.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelnav.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelmonthdropdown.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelnext.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelprevious.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelweekday.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelweeknumber.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelweeknumberheader.d.ts","./node_modules/react-day-picker/dist/esm/labels/labelyeardropdown.d.ts","./node_modules/react-day-picker/dist/esm/labels/index.d.ts","./node_modules/react-day-picker/dist/esm/types/shared.d.ts","./node_modules/react-day-picker/dist/esm/classes/datelib.d.ts","./node_modules/react-day-picker/dist/esm/classes/calendarday.d.ts","./node_modules/react-day-picker/dist/esm/classes/index.d.ts","./node_modules/react-day-picker/dist/esm/components/monthcaption.d.ts","./node_modules/react-day-picker/dist/esm/types/props.d.ts","./node_modules/react-day-picker/dist/esm/types/selection.d.ts","./node_modules/react-day-picker/dist/esm/usedaypicker.d.ts","./node_modules/react-day-picker/dist/esm/types/deprecated.d.ts","./node_modules/react-day-picker/dist/esm/types/index.d.ts","./node_modules/react-day-picker/dist/esm/daypicker.d.ts","./node_modules/react-day-picker/dist/esm/helpers/getdefaultclassnames.d.ts","./node_modules/react-day-picker/dist/esm/helpers/index.d.ts","./node_modules/react-day-picker/dist/esm/utils/addtorange.d.ts","./node_modules/react-day-picker/dist/esm/utils/datematchmodifiers.d.ts","./node_modules/react-day-picker/dist/esm/utils/rangecontainsdayofweek.d.ts","./node_modules/react-day-picker/dist/esm/utils/rangecontainsmodifiers.d.ts","./node_modules/react-day-picker/dist/esm/utils/rangeincludesdate.d.ts","./node_modules/react-day-picker/dist/esm/utils/rangeoverlaps.d.ts","./node_modules/react-day-picker/dist/esm/utils/typeguards.d.ts","./node_modules/react-day-picker/dist/esm/utils/index.d.ts","./node_modules/@date-fns/tz/constants/index.d.ts","./node_modules/@date-fns/tz/date/index.d.ts","./node_modules/@date-fns/tz/date/mini.d.ts","./node_modules/@date-fns/tz/tz/index.d.ts","./node_modules/@date-fns/tz/tzoffset/index.d.ts","./node_modules/@date-fns/tz/tzscan/index.d.ts","./node_modules/@date-fns/tz/tzname/index.d.ts","./node_modules/@date-fns/tz/index.d.ts","./node_modules/react-day-picker/dist/esm/index.d.ts","./components/ui/calendar.tsx","./components/ui/card.tsx","./node_modules/embla-carousel/esm/components/alignment.d.ts","./node_modules/embla-carousel/esm/components/noderects.d.ts","./node_modules/embla-carousel/esm/components/axis.d.ts","./node_modules/embla-carousel/esm/components/slidestoscroll.d.ts","./node_modules/embla-carousel/esm/components/limit.d.ts","./node_modules/embla-carousel/esm/components/scrollcontain.d.ts","./node_modules/embla-carousel/esm/components/dragtracker.d.ts","./node_modules/embla-carousel/esm/components/utils.d.ts","./node_modules/embla-carousel/esm/components/animations.d.ts","./node_modules/embla-carousel/esm/components/counter.d.ts","./node_modules/embla-carousel/esm/components/eventhandler.d.ts","./node_modules/embla-carousel/esm/components/eventstore.d.ts","./node_modules/embla-carousel/esm/components/percentofview.d.ts","./node_modules/embla-carousel/esm/components/resizehandler.d.ts","./node_modules/embla-carousel/esm/components/vector1d.d.ts","./node_modules/embla-carousel/esm/components/scrollbody.d.ts","./node_modules/embla-carousel/esm/components/scrollbounds.d.ts","./node_modules/embla-carousel/esm/components/scrolllooper.d.ts","./node_modules/embla-carousel/esm/components/scrollprogress.d.ts","./node_modules/embla-carousel/esm/components/slideregistry.d.ts","./node_modules/embla-carousel/esm/components/scrolltarget.d.ts","./node_modules/embla-carousel/esm/components/scrollto.d.ts","./node_modules/embla-carousel/esm/components/slidefocus.d.ts","./node_modules/embla-carousel/esm/components/translate.d.ts","./node_modules/embla-carousel/esm/components/slidelooper.d.ts","./node_modules/embla-carousel/esm/components/slideshandler.d.ts","./node_modules/embla-carousel/esm/components/slidesinview.d.ts","./node_modules/embla-carousel/esm/components/engine.d.ts","./node_modules/embla-carousel/esm/components/optionshandler.d.ts","./node_modules/embla-carousel/esm/components/plugins.d.ts","./node_modules/embla-carousel/esm/components/emblacarousel.d.ts","./node_modules/embla-carousel/esm/components/draghandler.d.ts","./node_modules/embla-carousel/esm/components/options.d.ts","./node_modules/embla-carousel/esm/index.d.ts","./node_modules/embla-carousel-react/esm/components/useemblacarousel.d.ts","./node_modules/embla-carousel-react/esm/index.d.ts","./components/ui/carousel.tsx","./node_modules/@types/d3-time/index.d.ts","./node_modules/@types/d3-scale/index.d.ts","./node_modules/victory-vendor/d3-scale.d.ts","./node_modules/recharts/types/shape/dot.d.ts","./node_modules/recharts/types/component/text.d.ts","./node_modules/recharts/types/zindex/zindexlayer.d.ts","./node_modules/recharts/types/cartesian/getcartesianposition.d.ts","./node_modules/recharts/types/component/label.d.ts","./node_modules/recharts/types/cartesian/cartesianaxis.d.ts","./node_modules/recharts/types/util/scale/customscaledefinition.d.ts","./node_modules/redux/dist/redux.d.ts","./node_modules/@reduxjs/toolkit/node_modules/immer/dist/immer.d.ts","./node_modules/redux-thunk/dist/redux-thunk.d.ts","./node_modules/@reduxjs/toolkit/dist/uncheckedindexed.ts","./node_modules/@reduxjs/toolkit/dist/index.d.mts","./node_modules/recharts/types/state/cartesianaxisslice.d.ts","./node_modules/recharts/types/synchronisation/types.d.ts","./node_modules/recharts/types/chart/types.d.ts","./node_modules/recharts/types/component/defaulttooltipcontent.d.ts","./node_modules/recharts/types/context/brushupdatecontext.d.ts","./node_modules/recharts/types/state/chartdataslice.d.ts","./node_modules/recharts/types/state/types/linesettings.d.ts","./node_modules/recharts/types/state/types/scattersettings.d.ts","./node_modules/@types/d3-path/index.d.ts","./node_modules/@types/d3-shape/index.d.ts","./node_modules/victory-vendor/d3-shape.d.ts","./node_modules/recharts/types/shape/curve.d.ts","./node_modules/recharts/types/component/labellist.d.ts","./node_modules/recharts/types/component/defaultlegendcontent.d.ts","./node_modules/recharts/types/util/payload/getuniqpayload.d.ts","./node_modules/recharts/types/util/useelementoffset.d.ts","./node_modules/recharts/types/component/legend.d.ts","./node_modules/recharts/types/state/legendslice.d.ts","./node_modules/recharts/types/state/types/stackedgraphicalitem.d.ts","./node_modules/recharts/types/util/stacks/stacktypes.d.ts","./node_modules/recharts/types/util/scale/rechartsscale.d.ts","./node_modules/recharts/types/util/chartutils.d.ts","./node_modules/recharts/types/state/selectors/areaselectors.d.ts","./node_modules/recharts/types/cartesian/area.d.ts","./node_modules/recharts/types/state/types/areasettings.d.ts","./node_modules/recharts/types/animation/easing.d.ts","./node_modules/recharts/types/shape/rectangle.d.ts","./node_modules/recharts/types/cartesian/bar.d.ts","./node_modules/recharts/types/util/barutils.d.ts","./node_modules/recharts/types/state/types/barsettings.d.ts","./node_modules/recharts/types/state/types/radialbarsettings.d.ts","./node_modules/recharts/types/util/svgpropertiesnoevents.d.ts","./node_modules/recharts/types/util/useuniqueid.d.ts","./node_modules/recharts/types/state/types/piesettings.d.ts","./node_modules/recharts/types/state/types/radarsettings.d.ts","./node_modules/recharts/types/state/graphicalitemsslice.d.ts","./node_modules/recharts/types/state/tooltipslice.d.ts","./node_modules/recharts/types/state/optionsslice.d.ts","./node_modules/recharts/types/state/layoutslice.d.ts","./node_modules/immer/dist/immer.d.ts","./node_modules/recharts/types/util/ifoverflow.d.ts","./node_modules/recharts/types/util/resolvedefaultprops.d.ts","./node_modules/recharts/types/cartesian/referenceline.d.ts","./node_modules/recharts/types/state/referenceelementsslice.d.ts","./node_modules/recharts/types/state/brushslice.d.ts","./node_modules/recharts/types/state/rootpropsslice.d.ts","./node_modules/recharts/types/state/polaraxisslice.d.ts","./node_modules/recharts/types/state/polaroptionsslice.d.ts","./node_modules/recharts/types/cartesian/line.d.ts","./node_modules/recharts/types/util/constants.d.ts","./node_modules/recharts/types/util/scatterutils.d.ts","./node_modules/recharts/types/shape/symbols.d.ts","./node_modules/recharts/types/cartesian/scatter.d.ts","./node_modules/recharts/types/cartesian/errorbar.d.ts","./node_modules/recharts/types/state/errorbarslice.d.ts","./node_modules/recharts/types/state/zindexslice.d.ts","./node_modules/recharts/types/state/eventsettingsslice.d.ts","./node_modules/recharts/types/state/renderedticksslice.d.ts","./node_modules/recharts/types/state/store.d.ts","./node_modules/recharts/types/cartesian/getticks.d.ts","./node_modules/recharts/types/cartesian/cartesiangrid.d.ts","./node_modules/recharts/types/state/selectors/combiners/combinedisplayedstackeddata.d.ts","./node_modules/recharts/types/state/selectors/selecttooltipaxistype.d.ts","./node_modules/recharts/types/types.d.ts","./node_modules/recharts/types/hooks.d.ts","./node_modules/recharts/types/state/selectors/axisselectors.d.ts","./node_modules/recharts/types/component/dots.d.ts","./node_modules/recharts/types/util/typeddatakey.d.ts","./node_modules/recharts/types/util/types.d.ts","./node_modules/recharts/types/container/surface.d.ts","./node_modules/recharts/types/container/layer.d.ts","./node_modules/recharts/types/component/cursor.d.ts","./node_modules/recharts/types/component/tooltip.d.ts","./node_modules/recharts/types/component/responsivecontainer.d.ts","./node_modules/recharts/types/component/cell.d.ts","./node_modules/recharts/types/component/customized.d.ts","./node_modules/recharts/types/shape/sector.d.ts","./node_modules/recharts/types/shape/polygon.d.ts","./node_modules/recharts/types/shape/cross.d.ts","./node_modules/recharts/types/polar/polargrid.d.ts","./node_modules/recharts/types/polar/defaultpolarradiusaxisprops.d.ts","./node_modules/recharts/types/polar/polarradiusaxis.d.ts","./node_modules/recharts/types/polar/defaultpolarangleaxisprops.d.ts","./node_modules/recharts/types/polar/polarangleaxis.d.ts","./node_modules/recharts/types/context/tooltipcontext.d.ts","./node_modules/recharts/types/polar/pie.d.ts","./node_modules/recharts/types/polar/radar.d.ts","./node_modules/recharts/types/util/radialbarutils.d.ts","./node_modules/recharts/types/polar/radialbar.d.ts","./node_modules/recharts/types/cartesian/brush.d.ts","./node_modules/recharts/types/cartesian/referencedot.d.ts","./node_modules/recharts/types/util/excludeeventprops.d.ts","./node_modules/recharts/types/util/svgpropertiesandevents.d.ts","./node_modules/recharts/types/cartesian/referencearea.d.ts","./node_modules/recharts/types/cartesian/barstack.d.ts","./node_modules/recharts/types/cartesian/xaxis.d.ts","./node_modules/recharts/types/cartesian/yaxis.d.ts","./node_modules/recharts/types/cartesian/zaxis.d.ts","./node_modules/recharts/types/chart/linechart.d.ts","./node_modules/recharts/types/chart/barchart.d.ts","./node_modules/recharts/types/chart/piechart.d.ts","./node_modules/recharts/types/chart/treemap.d.ts","./node_modules/recharts/types/chart/sankey.d.ts","./node_modules/recharts/types/chart/radarchart.d.ts","./node_modules/recharts/types/chart/scatterchart.d.ts","./node_modules/recharts/types/chart/areachart.d.ts","./node_modules/recharts/types/chart/radialbarchart.d.ts","./node_modules/recharts/types/chart/composedchart.d.ts","./node_modules/recharts/types/chart/sunburstchart.d.ts","./node_modules/recharts/types/shape/trapezoid.d.ts","./node_modules/recharts/types/cartesian/funnel.d.ts","./node_modules/recharts/types/chart/funnelchart.d.ts","./node_modules/recharts/types/util/global.d.ts","./node_modules/recharts/types/zindex/defaultzindexes.d.ts","./node_modules/decimal.js-light/decimal.d.ts","./node_modules/recharts/types/util/scale/getnicetickvalues.d.ts","./node_modules/recharts/types/context/chartlayoutcontext.d.ts","./node_modules/recharts/types/util/getrelativecoordinate.d.ts","./node_modules/recharts/types/util/createcartesiancharts.d.ts","./node_modules/recharts/types/util/createpolarcharts.d.ts","./node_modules/recharts/types/index.d.ts","./components/ui/chart.tsx","./components/ui/checkbox.tsx","./components/ui/collapsible.tsx","./components/ui/input.tsx","./components/ui/textarea.tsx","./components/ui/input-group.tsx","./components/ui/combobox.tsx","./node_modules/@radix-ui/react-context/dist/index.d.mts","./node_modules/@radix-ui/react-primitive/dist/index.d.mts","./node_modules/@radix-ui/react-dismissable-layer/dist/index.d.mts","./node_modules/@radix-ui/react-focus-scope/dist/index.d.mts","./node_modules/@radix-ui/react-portal/dist/index.d.mts","./node_modules/@radix-ui/react-dialog/dist/index.d.mts","./node_modules/cmdk/dist/index.d.ts","./components/ui/dialog.tsx","./components/ui/command.tsx","./components/ui/context-menu.tsx","./components/ui/direction.tsx","./components/ui/drawer.tsx","./components/ui/dropdown-menu.tsx","./components/ui/empty.tsx","./components/ui/label.tsx","./components/ui/field.tsx","./components/ui/hover-card.tsx","./node_modules/input-otp/dist/index.d.ts","./components/ui/input-otp.tsx","./components/ui/item.tsx","./components/ui/kbd.tsx","./components/ui/marker.tsx","./components/ui/menubar.tsx","./node_modules/@shadcn/react/dist/index-dop2hibu.d.ts","./node_modules/@shadcn/react/dist/message-scroller/index.d.ts","./components/ui/message-scroller.tsx","./components/ui/message.tsx","./components/ui/native-select.tsx","./components/ui/navigation-menu.tsx","./components/ui/pagination.tsx","./components/ui/popover.tsx","./components/ui/progress.tsx","./components/ui/radio-group.tsx","./node_modules/react-resizable-panels/dist/react-resizable-panels.d.ts","./components/ui/resizable.tsx","./components/ui/scroll-area.tsx","./components/ui/select.tsx","./components/ui/sheet.tsx","./components/ui/skeleton.tsx","./components/ui/tooltip.tsx","./components/ui/sidebar.tsx","./components/ui/slider.tsx","./components/ui/spinner.tsx","./components/ui/switch.tsx","./components/ui/table.tsx","./components/ui/tabs.tsx","./components/ui/toast.tsx","./components/ui/toggle.tsx","./components/ui/toggle-group.tsx","./node_modules/@cloudflare/workers-types/index.d.ts"],"fileIdsList":[[85,97,145,162,163,549,551],[97,145,162,163,230,528,549,551],[85,97,145,162,163,230,549,551,1226],[97,145,162,163,230,549,551,636,1242,1243],[85,97,145,162,163,230,549,551,636,1624,1627],[85,97,145,162,163,230,549,551,636,1626],[97,145,162,163,230,549,551,636],[85,97,145,162,163,230,549,551,636,1445,1615,1626,1627],[85,97,145,162,163,230,549,551,636,1332],[97,145,162,163,230,549,551,636,1445,1615,1626],[85,97,145,162,163,230,549,551,636,1243,1445,1615],[85,97,145,162,163,230,549,551,636,1445,1615,1626],[97,145,162,163,230,549,551,636,1445,1615,1626,1636],[97,145,162,163,230,549,551,636,1334,1626],[85,97,145,162,163,230,549,551,636,1243,1627,2070],[85,97,145,162,163,230,549,551,636],[85,97,145,162,163,230,549,551,636,1243,1627,2108],[85,97,145,162,163,230,549,551,636,2245],[97,145,162,163,230,549,551,636,1243,1338],[97,145,162,163,230,549,551,1344],[85,97,145,162,163,230,549,551,636,1243,1616,1627,2251],[85,97,145,162,163,230,549,551,636,1243,2251,2259,2260],[85,97,145,162,163,230,549,551,636,1243,1390],[85,97,145,162,163,230,549,551,636,1243,1403,1627],[97,145,162,163,230,549,551,1407],[85,97,145,162,163,230,549,551,636,1427],[85,97,145,162,163,230,549,551,636,1243,1386],[97,145,162,163,230,549,551,636,1626],[85,97,145,162,163,230,549,551,636,1626,1636,2267],[97,145,162,163,230,549,551,636,1510],[85,97,145,162,163,230,549,551,636,1626,1627,2249,2250],[85,97,145,162,163,230,549,551,636,1243,2270],[85,97,145,162,163,230,549,551,636,1441],[85,97,145,162,163,230,549,551,636,1445,1615,1626,1636],[85,97,145,162,163,230,549,551,636,1243,1386,1443,2265],[85,97,145,162,163,230,549,551,636,1243,1627,2277],[85,97,145,162,163,230,549,551,636,1243],[97,145,162,163,230,549,551,636,1243,1467,1626],[85,97,145,162,163,230,549,551,636,1243,1627],[85,97,145,162,163,230,549,551,636,1498],[97,145,162,163,230,549,551,636,1517],[97,145,162,163,230,549,551,636,1521,1523],[97,145,162,163,230,549,551,636,2286],[85,97,145,162,163,230,549,551,636,1531],[85,97,145,162,163,230,549,551,636,1243,1552],[97,145,162,163,230,549,551,636,1553],[85,97,145,162,163,230,549,551,633,636,1243,1445,1615,1626,1627,1636,2249,2290,2291,2292],[97,145,162,163,230,549,551,636,1563],[97,145,162,163,230,549,551,636,1243],[97,145,162,163,230,549,551,636,1567],[97,145,162,163,230,549,551,636,1574,1626],[85,97,145,162,163,230,549,551,636,1243,1589,1627],[85,97,145,162,163,230,549,551,636,1591,1593,1626,2300],[97,145,162,163,230,549,551,636,1591,1626],[97,145,162,163,230,549,551,636,1613],[85,97,145,162,163,230,549,551],[97,145,162,163,230,549,551,887,890,1009,1178,1179,1189,1211],[97,145,162,163,230,549,551,887,1009,1218,1220],[97,145,162,163,230,549,551],[97,145,162,163,230,549,551,890],[97,145,162,163,230,549,551,887,890,891,892,1009],[97,145,162,163,230,549,551,887,890,1009],[97,145,162,163,230,549,551,887,1009],[97,145,162,163,230,549,551,887,890,891,1009],[97,145,162,163,230,549,551,638,887,890,1009],[97,145,162,163,230,549,551,890,1179,1183],[97,145,162,163,230,549,551,887,1009,1202],[97,145,162,163,230,549,551,1203],[97,145,162,163,230,549,551,887,1009,1196,1208,1209,1210,1211,1212,1213,1214],[97,145,162,163,230,549,551,887,1009,1178],[97,145,162,163,230,549,551,1195],[97,145,162,163,230,549,551,887,889,890,1009],[97,145,162,163,230,549,551,1177],[97,145,162,163,230,549,551,638,887,888,889,890,893,1009,1179,1180,1182,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1215,1216,1217,1221,1222,1225],[97,145,162,163,230,549,551,638,887,1009],[97,145,162,163,230,549,551,887,889,890,1009,1179,1183,1184,1185,1186],[97,145,162,163,230,549,551,887,1009,1219],[97,145,162,163,230,549,551,638,887,890,1009,1178,1179,1180,1181],[97,145,162,163,230,549,551,887,893,1009,1200,1212,1223,1224],[97,145,162,163,230,549,551,890,891],[97,145,162,163,230,549,551,634,635],[97,145,162,163,549,551,555,556],[85,97,145,162,163,549,551,1232,1237],[97,145,162,163,549,551,1233,1237,1238,1239,1240,1241],[97,145,162,163,549,551,1233,1237,1238,1239,1240],[85,97,145,162,163,549,551,1229,1230,1232,1233,1236],[85,97,145,162,163,549,551,1232,1233,1234,1237],[85,97,145,162,163,549,551,1229,1230,1232],[97,145,162,163,549,551,1618],[97,145,162,163,549,551,1394,1395,1396,1397,1398,1399,1400,1621,1622,1623],[97,145,162,163,549,551,1394,1395,1396,1397,1398,1399,1400,1620,1621,1622],[85,97,145,162,163,230,549,551,1230,1619,1620],[85,97,145,162,163,549,551,1401,1620],[97,145,162,163,549,551,1297,1298,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1324,1326],[97,145,162,163,549,551,1297,1298,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1324,1325],[85,97,145,162,163,549,551,1232,1302,1303],[85,97,145,162,163,549,551,1232],[85,97,145,162,163,549,551,1296],[85,97,145,162,163,549,551,1232,1328],[85,97,145,162,163,549,551,1232,1234,1328],[97,145,162,163,549,551,1328,1329,1330,1331],[97,145,162,163,549,551,1328,1329,1330],[97,145,162,163,549,551,1333],[85,97,145,162,163,549,551,1229,1230,1232,1302],[97,145,162,163,549,551,1339],[97,145,162,163,549,551,1335,1336,1337],[97,145,162,163,549,551,1335,1336],[85,97,145,162,163,549,551,1232,1234,1335],[97,145,162,163,549,551,1235,1341,1342,1343],[97,145,162,163,549,551,1235,1341,1342],[85,97,145,162,163,549,551,1232,1234,1235],[85,97,145,162,163,549,551,1229,1230,1232,1236],[85,97,145,162,163,549,551,1234,1235],[85,97,145,162,163,549,551,1232,1235],[85,97,145,162,163,549,551,1232,1303],[85,97,145,162,163,549,551,1232,1234],[97,145,162,163,549,551,1305,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1319,1320,1321,1324,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356],[97,145,162,163,549,551,1305,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1319,1320,1321,1324,1325,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355],[85,97,145,162,163,549,551,1232,1302],[85,97,145,162,163,549,551,1232,1234,1254,1303],[85,97,145,162,163,549,551,1229,1230,1295],[97,145,162,163,549,551,1323],[97,145,162,163,549,551],[97,145,162,163,549,551,1358,1359,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1380,1384,1387,1388,1389],[97,145,162,163,549,551,1358,1359,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1380,1383,1384,1387,1388],[85,97,145,162,163,230,549,551,1231,1366,1386],[85,97,145,162,163,549,551,1387],[97,145,162,163,549,551,1391,1392],[97,145,162,163,549,551,1391],[97,145,162,163,549,551,1394,1395,1396,1397,1398,1399,1400,1401,1402,1619],[97,145,162,163,549,551,1394,1395,1396,1397,1398,1399,1400,1401,1618,1619],[85,97,145,162,163,549,551,1232,1234,1254],[85,97,145,162,163,230,549,551,1229,1230,1294,1618],[97,145,162,163,549,551,1262,1617],[85,97,145,162,163,549,551,1234,1253,1254,1263,1290,1294,1616,1619],[85,97,145,162,163,549,551,1232,1618],[85,97,145,162,163,549,551,1404],[97,145,162,163,549,551,1405,1406],[97,145,162,163,549,551,1404,1405],[97,145,162,163,549,551,1408,1409,1410,1411,1412,1413,1416,1418,1419,1420,1421,1422,1423,1424,1425,1426],[97,145,162,163,549,551,1408,1409,1410,1411,1412,1413,1415,1416,1418,1419,1420,1421,1422,1423,1424,1425],[85,97,145,162,163,549,551,1232,1234,1254,1417],[85,97,145,162,163,230,549,551,1229,1230,1294,1415,1417],[85,97,145,162,163,549,551,1414,1416],[85,97,145,162,163,549,551,1232,1414,1417],[85,97,145,162,163,549,551,1232,1415],[85,97,145,162,163,549,551,1232,1234,1302],[97,145,162,163,549,551,1302,1428,1429,1430,1431,1432,1433,1434],[97,145,162,163,549,551,1302,1428,1429,1430,1431,1432,1433],[85,97,145,162,163,549,551,1232,1301],[85,97,145,162,163,549,551,1234,1302],[97,145,162,163,549,551,1436,1437,1438],[97,145,162,163,549,551,1436,1437],[85,97,145,162,163,549,551,1285],[85,97,145,162,163,549,551,1254,1285,1286],[85,97,145,162,163,549,551,1232,1267],[85,97,145,162,163,549,551,1230,1234,1253,1285,1294],[85,97,145,162,163,549,551,1285,1286],[97,145,162,163,549,551,1285],[85,97,145,162,163,549,551,1278],[97,145,162,163,549,551,1229,1285],[97,145,162,163,549,551,1285,1286],[97,145,162,163,549,551,1263,1285],[97,145,162,163,549,551,1230,1263,1285],[97,145,162,163,549,551,1274,1285],[85,97,145,162,163,549,551,1232,1274,1285,1286],[97,145,162,163,549,551,1273,1285],[85,97,145,162,163,549,551,1279,1285,1286],[97,145,162,163,549,551,1231,1253,1263,1294],[85,97,145,162,163,549,551,1278,1285],[97,145,162,163,549,551,1259,1269,1270,1271,1275,1276,1277,1280,1281,1282,1283,1285,1286,1287,1288,1289],[97,145,162,163,549,551,1274],[85,97,145,162,163,549,551,1230,1259,1263,1264,1265,1268,1269,1270,1271,1274,1275,1276,1277,1280,1281,1282,1283,1284,1286,1290],[97,145,162,163,549,551,1272,1294],[85,97,145,162,163,549,551,1229,1230,1232,1299],[97,145,162,163,549,551,1300],[97,145,162,163,549,551,1231,1242,1301,1327,1332,1334,1338,1340,1344,1357,1386,1390,1393,1403,1407,1427,1435,1439,1441,1443,1445,1452,1467,1477,1482,1498,1510,1517,1521,1523,1531,1552,1553,1563,1567,1574,1589,1591,1593,1601,1613,1615,1624],[97,145,162,163,549,551,1440],[85,97,145,162,163,549,551,1232,1435],[97,145,162,163,549,551,1229],[85,97,145,162,163,549,551,1300,1302],[97,145,162,163,549,551,1228],[85,97,145,162,163,549,551,1231],[85,97,145,162,163,549,551,1255,1290],[85,97,145,162,163,549,551,1232,1266],[85,97,145,162,163,549,551,1232,1366],[97,145,162,163,549,551,1358,1359,1366,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1380,1381,1382,1384,1385],[97,145,162,163,549,551,1358,1359,1365,1366,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1380,1381,1382,1383,1384],[85,97,145,162,163,230,549,551,1229,1230,1294,1361,1362,1363,1364,1365],[85,97,145,162,163,549,551,1361,1366],[97,145,162,163,549,551,1262,1361],[85,97,145,162,163,549,551,1232,1234,1253,1254,1263,1286,1290,1294,1360,1366,1386],[85,97,145,162,163,230,549,551,1366,1379],[85,97,145,162,163,549,551,1361],[85,97,145,162,163,549,551,1232,1365],[97,145,162,163,549,551,1442],[85,97,145,162,163,549,551,1366],[97,145,162,163,549,551,1444],[97,145,162,163,549,551,1446,1447,1448,1449,1450,1451],[97,145,162,163,549,551,1446,1447,1448,1449,1450],[85,97,145,162,163,549,551,1232,1446],[97,145,162,163,549,551,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466],[97,145,162,163,549,551,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465],[85,97,145,162,163,549,551,1232,1234,1303],[85,97,145,162,163,549,551,1232,1469],[97,145,162,163,549,551,1469,1470,1471,1472,1473,1474,1475,1476],[97,145,162,163,549,551,1469,1470,1471,1472,1473,1474,1475],[85,97,145,162,163,549,551,1229,1230,1232,1302,1468],[97,145,162,163,549,551,1479,1480,1481],[97,145,162,163,549,551,1383,1479,1480],[85,97,145,162,163,549,551,1232,1479],[85,97,145,162,163,549,551,1229,1230,1232,1302,1478],[97,145,162,163,549,551,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497],[97,145,162,163,549,551,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496],[85,97,145,162,163,230,549,551,1229,1230,1294,1485],[97,145,162,163,549,551,1262,1484],[85,97,145,162,163,549,551,1234,1253,1254,1263,1290,1294,1360,1483,1486,1498,1616],[85,97,145,162,163,549,551,1232,1485],[97,145,162,163,549,551,1501,1502,1503,1504,1505,1506,1507,1508,1509],[97,145,162,163,549,551,1500,1501,1502,1503,1504,1505,1506,1507,1508],[85,97,145,162,163,230,549,551,1229,1230,1294,1500],[97,145,162,163,549,551,1262,1499],[85,97,145,162,163,549,551,1234,1253,1263,1290,1294,1360,1501,1616],[85,97,145,162,163,549,551,1232,1500],[97,145,162,163,549,551,1511,1512,1513,1514,1515,1516],[97,145,162,163,549,551,1511,1512,1513,1514,1515],[85,97,145,162,163,549,551,1232,1511],[97,145,162,163,549,551,1522],[97,145,162,163,549,551,1518,1519,1520],[97,145,162,163,549,551,1518,1519],[85,97,145,162,163,549,551,1232,1234,1518],[85,97,145,162,163,549,551,1232,1524],[97,145,162,163,549,551,1524,1525,1526,1527,1528,1529,1530],[97,145,162,163,549,551,1524,1525,1526,1527,1528,1529],[97,145,162,163,549,551,1532,1533,1534,1535,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551],[97,145,162,163,549,551,1532,1533,1534,1535,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550],[97,145,162,163,549,551,1383],[85,97,145,162,163,549,551,1232,1554],[97,145,162,163,549,551,1554,1555,1556,1557,1558,1560,1561,1562],[97,145,162,163,549,551,1554,1555,1556,1557,1558,1560,1561],[85,97,145,162,163,549,551,1232,1554,1559],[97,145,162,163,549,551,1564,1565,1566],[97,145,162,163,549,551,1564,1565],[85,97,145,162,163,549,551,1229,1231,1232,1302],[85,97,145,162,163,549,551,1232,1564],[97,145,162,163,549,551,1568,1569,1570,1571,1572,1573],[97,145,162,163,549,551,1568,1569,1570,1571,1572],[85,97,145,162,163,549,551,1232,1568,1569],[85,97,145,162,163,549,551,1232,1569],[85,97,145,162,163,549,551,1232,1234,1568,1569],[85,97,145,162,163,549,551,1229,1230,1232,1568],[97,145,162,163,549,551,1576],[97,145,162,163,549,551,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588],[97,145,162,163,549,551,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587],[85,97,145,162,163,549,551,1232,1303,1576],[85,97,145,162,163,549,551,1577],[85,97,145,162,163,549,551,1232,1234,1266,1576],[85,97,145,162,163,549,551,1575],[97,145,162,163,549,551,1592],[97,145,162,163,549,551,1590],[85,97,145,162,163,549,551,1232,1595],[97,145,162,163,549,551,1594,1595,1596,1597,1598,1599,1600],[97,145,162,163,549,551,1232,1594,1595,1596,1597,1598,1599],[85,97,145,162,163,549,551,1232,1553],[97,145,162,163,549,551,1604,1605,1606,1607,1608,1609,1610,1611,1612],[97,145,162,163,549,551,1603,1604,1605,1606,1607,1608,1609,1610,1611],[85,97,145,162,163,230,549,551,1229,1230,1294,1603],[97,145,162,163,549,551,1262,1602],[85,97,145,162,163,549,551,1234,1253,1263,1290,1294,1360,1604,1613,1616],[85,97,145,162,163,549,551,1232,1603],[85,97,145,162,163,549,551,1230],[97,145,162,163,549,551,1232,1614],[97,145,162,163,549,551,1260,1261,1262,1291,1292,1293],[85,97,145,162,163,549,551,1259],[97,145,162,163,549,551,1229,1230,1261],[85,97,145,162,163,549,551,1229,1230,1234,1253,1254,1291],[97,145,162,163,549,551,1232,1234,1253,1261,1263,1290],[97,145,162,163,549,551,1262],[97,145,162,163,549,551,1245],[97,145,162,163,549,551,1246],[97,145,162,163,549,551,1246,1247,1249,1250,1251,1252],[97,145,162,163,549,551,1249],[85,97,145,162,163,230,549,551,1249],[97,145,162,163,549,551,1248,1249],[97,145,162,163,549,551,2062],[97,145,162,163,549,551,2063],[97,145,162,163,549,551,2062,2063,2064,2065,2066,2067,2068],[97,145,162,163,549,551,1255],[97,145,162,163,549,551,1256,1257],[85,97,145,162,163,549,551,1258],[97,145,162,163,549,551,584,586],[97,145,162,163,549,551,585],[97,145,162,163,549,551,584,587],[97,145,162,163,549,551,582,584],[97,145,162,163,549,551,581,582,583],[97,145,162,163,549,551,581,584],[85,97,145,162,163,549,551,2253,2254,2255,2256,2257],[85,97,145,162,163,549,551,2254],[97,145,162,163,549,551,1245,2120,2121,2122,2123],[97,145,162,163,549,551,566],[85,97,145,162,163,230,549,551,2276],[97,145,162,163,549,551,2110],[97,145,162,163,549,551,2133],[97,142,143,145,162,163,549,551],[97,144,145,162,163,549,551],[145,162,163,549,551],[97,145,150,162,163,180,549,551],[97,145,146,151,156,162,163,165,177,188,549,551],[97,145,146,147,156,162,163,165,549,551],[92,93,94,97,145,162,163,549,551],[97,145,148,162,163,189,549,551],[97,145,149,150,157,162,163,166,549,551],[97,145,150,162,163,177,185,549,551],[97,145,151,153,156,162,163,165,549,551],[97,144,145,152,162,163,549,551],[97,145,153,154,162,163,549,551],[97,145,155,156,162,163,549,551],[97,144,145,156,162,163,549,551],[97,145,156,157,158,162,163,177,188,549,551],[97,145,156,157,158,162,163,172,177,180,549,551],[97,138,145,153,156,159,162,163,165,177,188,549,551],[97,145,156,157,159,160,162,163,165,177,185,188,549,551],[97,145,159,161,162,163,177,185,188,549,551],[95,96,97,98,99,100,101,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,549,551],[97,145,156,162,163,549,551],[97,145,162,163,164,188,549,551],[97,145,153,156,162,163,165,177,549,551],[97,145,162,163,166,549,551],[97,145,162,163,167,549,551],[97,144,145,162,163,168,549,551],[97,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,549,551],[97,145,162,163,170,549,551],[97,145,162,163,171,549,551],[97,145,156,162,163,172,173,549,551],[97,145,162,163,172,174,189,191,549,551],[97,145,157,162,163,549,551],[97,145,156,162,163,177,178,180,549,551],[97,145,162,163,179,180,549,551],[97,145,162,163,177,178,549,551],[97,145,162,163,180,549,551],[97,145,162,163,181,549,551],[97,142,145,162,163,177,182,188,549,551],[97,145,156,162,163,183,184,549,551],[97,145,162,163,183,184,549,551],[97,145,150,162,163,165,177,185,549,551],[97,145,162,163,186,549,551],[97,145,162,163,165,187,549,551],[97,145,159,162,163,171,188,549,551],[97,145,150,162,163,189,549,551],[97,145,162,163,177,190,549,551],[97,145,162,163,164,191,549,551],[97,145,162,163,192,549,551],[97,138,145,162,163,549,551],[97,138,145,156,158,162,163,168,177,180,188,190,191,193,549,551],[97,145,162,163,177,194,549,551],[85,89,97,145,162,163,196,197,198,200,481,526,549,551],[85,89,97,145,162,163,196,197,198,199,462,481,526,549,551],[85,89,97,145,162,163,196,197,199,200,481,526,549,551],[85,97,145,162,163,200,462,463,549,551],[85,97,145,162,163,200,462,549,551],[85,89,97,145,162,163,197,198,199,200,481,526,549,551],[85,89,97,145,162,163,196,198,199,200,481,526,549,551],[83,84,97,145,162,163,549,551],[97,145,162,163,549,551,886],[97,145,162,163,549,551,1174],[97,145,162,163,549,551,894],[97,145,162,163,549,551,887,1009],[97,145,162,163,549,551,887,895,896,1009,1177],[97,145,162,163,549,551,887,1009,1175,1176],[97,145,162,163,549,551,639,662,745,747,1083],[97,145,162,163,549,551,639,655,656,661,745,1083],[97,145,162,163,549,551,639,662,674,745,746,748,1083],[97,145,162,163,549,551,745,1083],[97,145,162,163,549,551,643,662],[97,145,162,163,549,551,639,643,658,659,660],[97,145,162,163,549,551,742,745,1083],[97,145,162,163,549,551,750],[97,145,162,163,549,551,661],[97,145,162,163,549,551,639,661],[97,145,162,163,549,551,745,758,759,1083],[97,145,162,163,549,551,760],[97,145,162,163,549,551,745,758,1083],[97,145,162,163,549,551,759,760],[97,145,162,163,549,551,727],[97,145,162,163,549,551,639,640,648,649,655,745,1083],[97,145,162,163,549,551,639,650,679,745,763,1083],[97,145,162,163,549,551,650,745,1083],[97,145,162,163,549,551,641,650,745,1083],[97,145,162,163,549,551,650,727],[97,145,162,163,549,551,639,642,648],[97,145,162,163,549,551,641,643,645,646,648,655,668,671,673,674,675],[97,145,162,163,549,551,643],[97,145,162,163,549,551,676],[97,145,162,163,549,551,643,644],[97,145,162,163,549,551,639,643,645],[97,145,162,163,549,551,642,643,644,648],[97,145,162,163,549,551,640,642,646,647,648,650,655,662,666,674,676,677,682,683,712,733,740,742,744,1117],[97,145,162,163,549,551,640,641,650,655,731,743,745,1083],[97,145,162,163,549,551,649,674,678,683],[97,145,162,163,549,551,679],[97,145,162,163,549,551,639,674,697],[97,145,162,163,549,551,674,745,1083],[97,145,162,163,549,551,641,655],[97,145,162,163,549,551,641,655,663],[97,145,162,163,549,551,641,664],[97,145,162,163,549,551,641,665],[97,145,162,163,549,551,641,652,665,666],[97,145,162,163,549,551,775],[97,145,162,163,549,551,655,663],[97,145,162,163,549,551,641,663],[97,145,162,163,549,551,775,776,777,778,779,780,781,782,783,784],[97,145,162,163,549,551,655,681,683,707,712,733,1117],[97,145,162,163,549,551,641],[97,145,162,163,549,551,639,683],[97,145,162,163,549,551,793],[97,145,162,163,549,551,795],[97,145,162,163,549,551,641,655,663,666,676],[97,145,162,163,549,551,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810],[97,145,162,163,549,551,641,676],[97,145,162,163,549,551,666,676],[97,145,162,163,549,551,655,663,676],[97,145,162,163,549,551,652,655,729,745,812,1083],[97,145,162,163,549,551,652,676,684,814,1161],[97,145,162,163,549,551,652,671,814],[97,145,162,163,549,551,652,676,684,745,814,1083,1161],[97,145,162,163,549,551,648,650,652,814],[97,145,162,163,549,551,648,652,745,812,820,1083],[97,145,162,163,549,551,648,652,686,745,823,1083,1161],[97,145,162,163,549,551,669,814],[97,145,162,163,549,551,648,652,745,827,1083],[97,145,162,163,549,551,652,814],[97,145,162,163,549,551,648,652,656,745,814,830,1083],[97,145,162,163,549,551,648,652,709,745,814,1083],[97,145,162,163,549,551,652,709],[97,145,162,163,549,551,652,655,709,745,819,1083],[97,145,162,163,549,551,708,765,1002],[97,145,162,163,549,551,652,655,709],[97,145,162,163,549,551,652,708,745,1002,1083],[97,145,162,163,549,551,709,834],[97,145,162,163,549,551,639,641,648,649,650,706,707,709,745,1083],[97,145,162,163,549,551,652,709,826],[97,145,162,163,549,551,708,709,727,1002],[97,145,162,163,549,551,652,655,683,709,745,837,1083],[97,145,162,163,549,551,708,727,1002],[97,145,162,163,549,551,683,838],[97,145,162,163,549,551,652,838],[97,145,162,163,549,551,662,839,840],[97,145,162,163,549,551,839,840],[97,145,162,163,549,551,676,769,839,840],[97,145,162,163,549,551,680,839,840],[97,145,162,163,549,551,681,839,840],[97,145,162,163,549,551,741,839,840],[97,145,162,163,549,551,839],[97,145,162,163,549,551,840],[97,145,162,163,549,551,683,740,839,840],[97,145,162,163,549,551,683,839,840,1025],[97,145,162,163,549,551,852,1095,1168],[97,145,162,163,549,551,683,740,839,853,1025,1095,1168],[97,145,162,163,549,551,662,676,682,683,740,745,769,839,840,1083],[97,145,162,163,549,551,683,839,840],[97,145,162,163,549,551,652,683,740],[97,145,162,163,549,551,684,740,1161],[97,145,162,163,549,551,639,641,647,650,652,669,674,676,677,682,683,712,733,739,745,1083,1117],[97,145,162,163,549,551,684,685,686,687,688,689,690,691,692,693,694,695,696,700,701,702,703,740,1161],[97,145,162,163,549,551,639,647,652,683,740],[97,145,162,163,549,551,639,683,740],[97,145,162,163,549,551,683,740],[97,145,162,163,549,551,639,641,647,652,683,740],[97,145,162,163,549,551,639,641,652,683,740],[97,145,162,163,549,551,639,641,683,740],[97,145,162,163,549,551,641,652,683,693,740,1161],[97,145,162,163,549,551,685,740,1095,1138,1161],[97,145,162,163,549,551,684,740,1095,1161],[97,145,162,163,549,551,643,648,709,740,909,925,1093,1136,1137],[97,145,162,163,549,551,686,740,1005,1022,1095,1137,1161],[97,145,162,163,549,551,689,740,1005,1023,1095,1137,1161],[97,145,162,163,549,551,690,740,1095,1137,1161],[97,145,162,163,549,551,691,740,1095,1161],[97,145,162,163,549,551,692,740,1005,1023,1095,1137,1138,1161],[97,145,162,163,549,551,694,740,1137,1138,1147,1161],[97,145,162,163,549,551,694,740,898,926,1024,1137,1138,1148,1161],[97,145,162,163,549,551,693,740,1007,1024,1095,1137,1138,1161],[97,145,162,163,549,551,695,740,1095,1150,1161],[97,145,162,163,549,551,740,902,904,905,907,908,913,922,925,926,933,934,938,946,977,982,984,993,997,1026,1029,1030,1044,1045,1073,1094,1137,1138],[97,145,162,163,549,551,1094,1095,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1151,1152,1153,1155,1157],[97,145,162,163,549,551,696,740,1138,1152,1161],[97,145,162,163,549,551,702,740,1095,1154,1161],[97,145,162,163,549,551,703,740,1095,1137,1138,1161],[97,145,162,163,549,551,740,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1095,1138,1156],[97,145,162,163,549,551,700],[97,145,162,163,549,551,639,641,642,648,649,655,698,699,740,745,1083],[97,145,162,163,549,551,652,740],[97,145,162,163,549,551,643,648,655,668,669,670,745,1083],[97,145,162,163,549,551,642,643,645,651,655],[97,145,162,163,549,551,639,642,652,655],[97,145,162,163,549,551,655],[97,145,162,163,549,551,646,648,655],[97,145,162,163,549,551,639,648,655,668,669,671,705,745,1083],[97,145,162,163,549,551,639,655,668,671,705,728,745,1083],[97,145,162,163,549,551,657],[97,145,162,163,549,551,648,655],[97,145,162,163,549,551,646],[97,145,162,163,549,551,641,648,655],[97,145,162,163,549,551,639,642,646,647,655],[97,145,162,163,549,551,642,648,655,667,668,671],[97,145,162,163,549,551,643,645,647,648,655],[97,145,162,163,549,551,648,655,668,669,671],[97,145,162,163,549,551,648,655,669,671],[97,145,162,163,549,551,641,643,645,649,655,669,671],[97,145,162,163,549,551,642,643],[97,145,162,163,549,551,642,643,645,646,647,648,650,652,653,654],[97,145,162,163,549,551,643,646,648],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,643,902,904,905,907,908,913,922,931,933,934,938,946,953,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,859,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,639,643,644,645,898,902,904,905,907,908,912,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137],[97,145,162,163,549,551,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,642,648,649,655,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,669,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,681,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,860,902,904,905,907,908,913,922,933,934,938,946,958,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,768,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,709,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,641,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,648,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,740,902,904,905,907,908,913,922,933,934,938,946,961,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,648,902,904,905,907,908,913,922,933,934,938,946,963,977,982,984,993,997,1025,1029,1030,1044,1045,1138],[97,145,162,163,549,551,729,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,904,905,906,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,745,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1083,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,961,977,982,984,993,997,1029,1030,1044,1045,1136],[97,145,162,163,549,551,712,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1117,1138],[97,145,162,163,549,551,830,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,672,897,902,904,905,907,908,912,913,919,922,931,933,934,938,946,952,954,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,683,897,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,904,905,906,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,683,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,683,902,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,904,905,907,908,912,913,922,933,934,936,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,961,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,648,902,903,904,905,907,908,911,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,992,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,927,933,934,938,946,977,982,984,989,991,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,904,905,907,908,913,921,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,898,1137],[97,145,162,163,549,551,1137,1138],[97,145,162,163,549,551,925,1137,1138],[97,145,162,163,549,551,683,902,904,905,907,908,913,922,928,933,934,938,946,977,982,984,993,997,1025,1029,1030,1044,1045],[97,145,162,163,549,551,674,897,909,935,1137],[97,145,162,163,549,551,1138],[97,145,162,163,549,551,676,683,740,745,897,900,914,916,917,918,921,925,1019,1083,1136,1138],[97,145,162,163,549,551,643,644,913,914,915,916,917,918,920,1138],[97,145,162,163,549,551,650,712,740,745,1083,1117,1136,1138],[97,145,162,163,549,551,927],[97,145,162,163,549,551,991],[97,145,162,163,549,551,910,911],[97,145,162,163,549,551,641,642,648,649,652,655,1138],[97,145,162,163,549,551,639,917],[97,145,162,163,549,551,902,904,905,907,908,913,922,929,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,902,904,905,907,908,913,919,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,919],[97,145,162,163,549,551,897,898,902,904,905,907,908,909,910,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,639,902,904,905,907,908,913,918,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,639,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,639,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,640,649,650,679,683,712,902,903,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1026,1029,1030,1044,1045,1117,1136,1138],[97,145,162,163,549,551,639,902,903,904,905,907,908,913,922,933,934,938,946,967,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,650,652,712,902,904,905,907,908,913,922,933,934,938,946,977,980,982,984,993,997,1029,1030,1044,1045,1117,1138],[97,145,162,163,549,551,678,902,904,905,907,908,913,922,933,934,938,946,977,982,984,986,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,871,902,904,905,907,908,913,922,933,934,938,946,977,982,984,986,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,679,683,871,897,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,926],[97,145,162,163,549,551,1022],[97,145,162,163,549,551,926,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,926,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,641,649,652,655,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1136,1138],[97,145,162,163,549,551,833,1002],[97,145,162,163,549,551,708,709,902,904,905,907,908,913,922,924,925,933,934,938,946,977,982,984,993,997,1001,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,924,1138],[97,145,162,163,549,551,820,1002],[97,145,162,163,549,551,823,963,1002,1138],[97,145,162,163,549,551,1009],[97,145,162,163,549,551,902,904,905,907,908,913,922,925,926,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,835,973,1002],[97,145,162,163,549,551,709,902,904,905,907,908,913,922,924,933,934,938,946,977,982,984,993,997,1025,1029,1030,1044,1045,1138],[97,145,162,163,549,551,827,902,904,905,907,908,913,922,925,933,934,938,946,977,982,984,993,997,1002,1014,1029,1030,1044,1045,1138],[97,145,162,163,549,551,708,709,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1001,1002,1029,1030,1044,1045,1138],[97,145,162,163,549,551,741,836,1002,1138],[97,145,162,163,549,551,709,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,709,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1095,1138],[97,145,162,163,549,551,650,676,677,708,709,712,740,745,902,904,905,907,908,913,922,925,933,934,938,946,977,982,984,993,997,999,1000,1002,1029,1030,1044,1045,1083,1117,1136],[97,145,162,163,549,551,838,887,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1002,1029,1030,1044,1045,1137,1138],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1100,1101,1138],[97,145,162,163,549,551,655,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,1030],[97,145,162,163,549,551,897,899,900,901,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,992,993,994,995,996,997,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1015,1016,1017,1018,1021,1023,1024,1026,1137,1138],[97,145,162,163,549,551,1019,1020],[97,145,162,163,549,551,683,902,903,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,897,900,905,906,907,908,910,911,912,913,922,923,925,928,929,930,931,934,939,940,941,942,943,945,946,947,948,951,953,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,992,993,994,995,996,997,1000,1001,1012,1014,1015,1018,1026,1028,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1096,1097,1098,1099,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114],[97,145,162,163,549,551,902,904,905,907,908,911,913,922,929,932,933,934,938,946,977,982,984,990,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,641,642,648,649,652,655,858,899,900,901,1137,1138],[97,145,162,163,549,551,1025,1138],[97,145,162,163,549,551,902,904,905,907,908,909,913,922,933,934,938,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,683,902,904,905,907,908,911,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045],[97,145,162,163,549,551,650,679,745,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1083,1138],[97,145,162,163,549,551,639,679,902,904,905,907,908,913,922,933,934,938,942,946,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,903],[97,145,162,163,549,551,902,904,905,907,908,913,922,933,934,936,938,946,953,977,982,984,993,997,1029,1030,1044,1045,1138],[97,145,162,163,549,551,648,650,652,668,671,676,729,740],[97,145,162,163,549,551,669,677],[97,145,162,163,549,551,643,648,652,668,671,676,729,740,741,745,768,1083],[97,145,162,163,549,551,676,740,745,1083],[97,145,162,163,549,551,676,740,745,812,1083],[97,145,162,163,549,551,655,676,740,745,1083],[97,145,162,163,549,551,648,656,741],[97,145,162,163,549,551,639,648,655,668,671,676,729,740,742,745,1083],[97,145,162,163,549,551,641,676,704,745,1083],[97,145,162,163,549,551,1027,1117],[97,145,162,163,549,551,643,644,645,1083,1084,1085],[97,145,162,163,549,551,639,745,1136],[97,145,162,163,549,551,712,729,1085,1089,1117,1120,1136],[97,145,162,163,549,551,1074,1080],[97,145,162,163,549,551,997,1027,1081,1083,1085,1086,1093,1117,1123,1124],[97,145,162,163,549,551,1074],[97,145,162,163,549,551,677],[97,145,162,163,549,551,642,648,649,650,669,712,998,1117],[97,145,162,163,549,551,652],[97,145,162,163,549,551,1090,1091],[97,145,162,163,549,551,655,679,712,1117,1136],[97,145,162,163,549,551,643,644,645,673,676,1027,1084,1085,1086,1093],[97,145,162,163,549,551,643,672],[97,145,162,163,549,551,650,709,745,925,1083,1116],[97,145,162,163,549,551,914,1081,1094,1138],[97,145,162,163,549,551,639,709,740,902,904,905,907,908,913,922,933,934,938,946,977,982,984,993,997,1002,1029,1030,1044,1045,1095,1138],[97,145,162,163,549,551,650,652,681,683,710,711,712,740,745,910,925,997,1025,1080,1083,1085,1088,1093,1115,1116,1117,1136,1137,1138],[97,145,162,163,549,551,641,642,648,649,652,655,858,916,1075],[97,145,162,163,549,551,1025,1079],[97,145,162,163,549,551,997,1074,1083,1085,1090,1091,1092,1093,1117,1122,1125],[97,145,162,163,549,551,1025,1136],[97,145,162,163,549,551,914],[97,145,162,163,549,551,729,740,765,1136],[97,145,162,163,549,551,650,1082],[97,145,162,163,549,551,650,1082,1116,1129],[97,145,162,163,549,551,649,678,679,683,999],[97,145,162,163,549,551,650,679,712,1089,1116,1117],[97,145,162,163,549,551,639,641,643,649,650,652,655,671,676,677,679,683,712,713,740,745,871,925,997,999,1026,1027,1083,1086,1087,1089,1117,1118,1119,1120,1121,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1135],[97,145,162,163,549,551,639,650,652,676,679,681,683,712,740,745,925,1026,1083,1117,1126,1136,1138],[97,145,162,163,549,551,650,676,709,740,745,999,1025,1082,1083,1119],[97,145,162,163,549,551,650,745,1083,1116,1119,1120],[97,145,162,163,549,551,643,644,645,650,673,676,712,740,745,999,1025,1081,1082,1083,1087,1088,1089,1092,1094,1117,1136],[97,145,162,163,549,551,650,712,740,745,925,999,1027,1083,1087,1089,1093,1116,1117,1123,1125,1136],[97,145,162,163,549,551,643,768],[97,145,162,163,549,551,683],[97,145,162,163,549,551,655,678,679,683,1027,1083,1085,1136],[97,145,162,163,549,551,641,642,648,649,652,655,858],[97,145,162,163,549,551,1077],[97,145,162,163,549,551,1076,1078],[97,145,162,163,549,551,641,646,655,674,679,686,727,728,729,731,740,745,793,802,1083,1118,1128,1134,1136,1161],[97,145,162,163,549,551,699],[97,145,162,163,549,551,641,642,652],[97,145,162,163,549,551,698,699],[97,145,162,163,549,551,643,645,675],[97,145,162,163,549,551,643,676,721,734,740,745,1083],[97,145,162,163,549,551,715,722],[97,145,162,163,549,551,639],[97,145,162,163,549,551,650,669,716,740],[97,145,162,163,549,551,733],[97,145,162,163,549,551,683,733],[97,145,162,163,549,551,643,676,722,734,745,1083],[97,145,162,163,549,551,721],[97,145,162,163,549,551,715],[97,145,162,163,549,551,720,733],[97,145,162,163,549,551,639,699,709,712,714,715,721,733,735,736,737,738,740,745,1083,1117],[97,145,162,163,549,551,650,676,677,712,716,721,740,745,1083,1117],[97,145,162,163,549,551,639,650,709,712,714,724,733,1117],[97,145,162,163,549,551,639,649,707,715,740],[97,145,162,163,549,551,714,715,716,717,718,722],[97,145,162,163,549,551,719,721],[97,145,162,163,549,551,639,715],[97,145,162,163,549,551,679,707,864],[97,145,162,163,549,551,679,707,866],[97,145,162,163,549,551,679,681,683,707,733],[97,145,162,163,549,551,639,641,643,649,650,652,655,669,671,676,683,707,712,713,714,715,716,717,721,722,723,725,732,740,745,1083,1117],[97,145,162,163,549,551,679,683],[97,145,162,163,549,551,684,685,686,689,690,691,692,693,694,695,696,702,703,1127,1140,1141,1142,1143,1144,1145,1146,1147,1148,1151,1152,1153,1155],[97,145,162,163,549,551,639,1083,1162],[97,145,162,163,549,551,1136,1161,1163],[97,145,162,163,549,551,655,677,745,1083],[97,145,162,163,549,551,729,730,732],[97,145,162,163,549,551,649,674,726,727,728,729,730,731,733],[97,145,162,163,549,551,647,652,681,683,710,711,740,745,1083],[97,145,162,163,549,551,639,680],[97,145,162,163,549,551,639,643,683],[97,145,162,163,549,551,639,643,683,741],[97,145,162,163,549,551,639,683,864],[97,145,162,163,549,551,639,641,642,674,679,680,681,682],[97,145,162,163,549,551,639,872],[97,145,162,163,549,551,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,674,675,676,677,678,679,680,681,682,683,697,698,704,705,706,707,708,709,710,711,712,713,727,728,729,730,731,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,785,786,787,788,789,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,1002,1083,1117],[97,145,162,163,549,551,699,714,715,716,717,718,719,720,721,722,723,724,725,731,732,733,734,735,736,737,738,739,875,876,877,878,879,880,881,882,883,884,885],[97,145,162,163,549,551,672,673,875,952,998,1025,1082,1115,1119,1128,1136,1158,1159,1160,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173],[97,145,162,163,549,551,552,553],[97,145,162,163,485,490,492,494,498,500,504,508,510,520,523,527,531,532,534,536,539,541,544,547,548,551],[97,145,162,163,300,549,550],[97,145,162,163,484,549,551],[97,145,162,163,486,487,488,489,549,551],[97,145,162,163,533,549,551],[97,145,162,163,205,549,551],[97,145,162,163,432,495,496,549,551],[97,145,162,163,205,206,208,220,244,359,370,477,549,551],[97,145,162,163,208,239,240,241,243,477,549,551],[97,145,162,163,208,376,379,380,382,477,479,549,551,552],[97,145,162,163,208,242,279,477,549,551],[97,145,162,163,206,208,219,220,226,232,237,358,359,360,369,477,479,549,551],[97,145,162,163,477,549,551],[97,145,162,163,215,221,240,260,355,549,551],[97,145,162,163,208,549,551],[97,145,162,163,201,215,221,549,551],[97,145,162,163,386,549,551],[97,145,162,163,383,384,386,549,551],[97,145,162,163,383,385,477,549,551],[97,145,159,162,163,260,456,474,549,551],[97,145,159,162,163,331,334,350,355,474,549,551],[97,145,159,162,163,303,474,549,551],[97,145,162,163,363,549,551],[97,145,162,163,362,363,364,549,551],[97,145,162,163,362,549,551],[91,97,145,159,162,163,201,208,220,226,232,238,240,244,245,258,259,326,356,357,370,477,481,549,551],[97,145,162,163,202,205,208,242,279,376,377,381,477,549,551],[97,145,162,163,202,242,549,551],[97,145,162,163,202,205,259,427,477,549,551],[97,145,162,163,202,549,551],[97,145,162,163,202,208,242,243,549,551],[97,145,162,163,202,378,549,551],[97,145,162,163,245,358,361,368,549,551],[97,145,162,163,276,549,551],[85,97,145,162,163,432,549,551],[97,145,162,163,171,215,230,549,551],[97,145,162,163,215,230,549,551],[85,97,145,162,163,300,549,551],[85,97,145,162,163,221,230,432,549,551],[97,145,162,163,215,286,300,301,511,518,549,551],[97,145,162,163,285,512,513,514,515,517,549,551],[97,145,162,163,336,549,551],[97,145,162,163,336,337,549,551],[97,145,162,163,219,221,288,289,549,551],[97,145,162,163,221,295,296,549,551],[97,145,162,163,221,290,298,549,551],[97,145,162,163,295,549,551],[97,145,162,163,213,221,288,289,290,291,292,293,294,295,298,549,551],[97,145,162,163,221,288,295,296,297,299,549,551],[97,145,162,163,221,289,291,292,549,551],[97,145,162,163,289,291,294,296,549,551],[97,145,162,163,516,549,551],[97,145,162,163,221,549,551],[85,97,145,162,163,542,549,551],[85,97,145,162,163,209,505,549,551],[85,97,145,162,163,188,549,551],[85,97,145,162,163,242,277,549,551],[85,97,145,162,163,242,370,549,551],[97,145,162,163,275,280,549,551],[85,97,145,162,163,276,483,549,551],[97,145,162,163,537,549,551],[85,97,145,159,162,163,177,525,526,549,551],[85,89,97,145,159,162,163,196,197,198,199,200,481,525,549,551],[97,145,159,162,163,221,549,551],[97,145,159,162,163,220,225,306,323,365,366,370,424,426,477,478,549,551],[97,145,162,163,258,367,549,551],[97,145,162,163,481,549,551],[97,145,162,163,207,549,551],[85,97,145,162,163,212,215,429,445,447,549,551],[97,145,162,163,171,215,429,444,445,446,528,549,551],[97,145,162,163,438,439,440,441,442,443,549,551],[97,145,162,163,440,549,551],[97,145,162,163,444,549,551],[97,145,162,163,529,549,551],[97,145,162,163,230,393,394,396,549,551],[85,97,145,162,163,221,387,388,389,390,395,549,551],[97,145,162,163,393,395,549,551],[97,145,162,163,391,549,551],[97,145,162,163,392,549,551],[85,97,145,162,163,230,276,483,549,551],[85,97,145,162,163,230,482,483,549,551],[85,97,145,162,163,230,483,549,551],[97,145,162,163,323,324,549,551],[97,145,162,163,324,549,551],[97,145,159,162,163,478,483,549,551],[97,145,162,163,353,549,551],[97,144,145,162,163,352,549,551],[97,145,162,163,215,221,227,229,331,344,348,350,426,429,466,467,474,478,549,551],[97,145,162,163,221,270,292,549,551],[97,145,162,163,331,342,345,350,549,551],[85,97,145,162,163,212,215,331,334,350,353,386,433,434,435,436,437,448,449,450,451,452,453,454,455,529,549,551],[97,145,162,163,212,215,240,331,338,339,340,343,344,549,551],[97,145,162,163,177,221,240,342,349,429,430,474,549,551],[97,145,162,163,346,549,551],[97,145,159,162,163,171,209,221,225,235,267,268,271,323,326,390,424,425,466,477,478,479,481,529,549,551],[97,145,162,163,212,213,215,549,551],[97,145,162,163,331,549,551],[97,144,145,162,163,240,267,268,325,326,327,328,329,330,478,549,551],[97,145,162,163,350,549,551],[97,144,145,162,163,214,215,225,229,265,331,338,339,340,341,342,345,346,347,348,349,467,549,551],[97,145,159,162,163,265,266,338,478,479,549,551],[97,145,162,163,240,268,323,326,331,426,478,549,551],[97,145,159,162,163,477,479,549,551],[97,145,159,162,163,177,474,478,479,549,551],[97,145,159,162,163,171,201,215,220,227,229,232,235,242,262,267,268,269,270,271,306,307,309,312,314,317,318,319,320,322,370,424,426,474,477,478,479,549,551],[97,145,159,162,163,177,549,551],[97,145,162,163,202,208,209,210,238,474,475,476,481,483,549,551],[97,145,162,163,205,206,477,549,551],[97,145,162,163,398,549,551],[97,145,159,162,163,177,188,202,217,382,386,387,388,389,390,396,397,549,551],[97,145,162,163,171,188,201,215,217,229,232,268,307,312,322,323,376,402,403,404,410,413,414,424,426,474,477,549,551],[97,145,162,163,232,238,245,258,268,326,477,549,551],[97,145,159,162,163,188,209,220,229,268,474,477,549,551,552],[97,145,162,163,428,549,551],[97,145,159,162,163,398,411,412,421,549,551],[97,145,162,163,474,477,549,551],[97,145,162,163,328,467,549,551],[97,145,162,163,229,267,370,483,549,551],[97,145,159,162,163,171,207,312,372,376,404,410,413,416,474,549,551],[97,145,159,162,163,245,258,376,417,549,551],[97,145,162,163,208,269,370,419,477,479,549,551],[97,145,159,162,163,188,390,477,549,551],[97,145,159,162,163,242,269,370,371,372,380,398,418,420,477,549,551],[91,97,145,159,162,163,267,423,481,483,549,551],[97,145,162,163,321,424,549,551],[97,145,159,162,163,171,215,218,220,221,227,229,235,244,245,258,268,271,307,309,319,322,323,370,402,403,404,405,407,409,424,426,474,483,549,551],[97,145,159,162,163,177,245,410,415,421,474,549,551],[97,145,162,163,248,249,250,251,252,253,254,255,256,257,549,551],[97,145,162,163,262,313,549,551],[97,145,162,163,315,549,551],[97,145,162,163,313,549,551],[97,145,162,163,315,316,549,551],[97,145,162,163,545,549,551],[97,145,159,162,163,219,220,221,225,226,478,549,551],[97,145,159,162,163,171,207,209,227,231,267,270,271,305,424,474,479,481,483,549,551],[97,145,159,162,163,171,188,211,218,219,229,231,268,422,467,473,478,549,551],[97,145,162,163,338,549,551],[97,145,162,163,339,549,551],[97,145,162,163,221,232,466,549,551],[97,145,162,163,340,549,551],[97,145,162,163,214,549,551],[97,145,162,163,216,228,549,551],[97,145,159,162,163,216,220,227,549,551],[97,145,162,163,223,228,549,551],[97,145,162,163,224,549,551],[97,145,162,163,216,217,549,551],[97,145,162,163,216,272,549,551],[97,145,162,163,216,549,551],[97,145,162,163,218,262,311,549,551],[97,145,162,163,310,549,551],[97,145,162,163,215,217,218,549,551],[97,145,162,163,218,308,549,551],[97,145,162,163,215,217,549,551],[97,145,162,163,267,370,549,551],[97,145,162,163,466,549,551],[97,145,159,162,163,188,227,229,233,267,370,423,426,429,430,431,457,458,461,465,467,474,478,549,551],[97,145,162,163,281,284,286,287,300,301,549,551],[85,97,145,162,163,198,200,230,459,460,549,551],[85,97,145,162,163,198,200,230,459,460,464,549,551],[97,145,162,163,354,549,551],[97,145,162,163,240,261,266,267,331,332,333,334,335,337,350,351,353,356,423,426,477,479,549,551],[97,145,162,163,300,549,551],[97,145,159,162,163,305,474,549,551],[97,145,162,163,305,549,551],[97,145,159,162,163,227,273,302,304,306,423,474,481,483,549,551],[97,145,162,163,281,282,283,284,286,287,300,301,482,549,551],[91,97,145,159,162,163,171,188,216,217,229,235,267,268,271,370,421,422,424,474,477,478,481,549,551],[97,145,162,163,212,215,222,549,551],[97,145,162,163,266,268,399,402,549,551],[97,145,162,163,266,400,468,469,470,471,472,549,551],[97,145,159,162,163,262,477,549,551],[97,145,159,162,163,549,551],[97,145,162,163,265,350,549,551],[97,145,162,163,264,549,551],[97,145,162,163,266,319,549,551],[97,145,162,163,263,265,477,549,551],[97,145,159,162,163,211,266,399,400,401,474,477,478,549,551],[85,97,145,162,163,215,221,299,549,551],[85,97,145,162,163,213,549,551],[97,145,162,163,203,204,549,551],[85,97,145,162,163,209,549,551],[85,97,145,162,163,215,285,549,551],[85,91,97,145,162,163,267,271,481,483,549,551],[97,145,162,163,209,505,506,549,551],[85,97,145,162,163,280,549,551],[85,97,145,162,163,171,188,207,274,276,278,279,483,549,551],[97,145,162,163,215,242,478,549,551],[97,145,162,163,215,406,549,551],[85,97,145,157,159,162,163,171,205,207,280,481,482,549,551,552],[85,97,145,162,163,196,197,198,199,200,481,526,549,551],[85,86,87,88,89,97,145,162,163,549,551],[97,145,150,162,163,549,551],[97,145,162,163,373,374,375,549,551],[97,145,162,163,373,549,551],[85,89,97,145,159,161,162,163,171,195,196,197,198,199,200,201,207,235,240,416,444,479,480,483,526,549,551],[97,145,162,163,491,549,551],[97,145,162,163,493,549,551],[97,145,162,163,497,549,551],[97,145,162,163,538,549,551],[97,145,162,163,540,549,551],[97,145,162,163,543,549,551],[97,145,162,163,499,549,551],[97,145,162,163,501,502,503,549,551],[97,145,162,163,507,549,551],[90,97,145,162,163,485,490,492,494,498,500,504,508,510,520,521,523,527,528,529,530,549,551],[97,145,162,163,535,549,551],[97,145,162,163,509,549,551],[97,145,162,163,519,549,551],[97,145,162,163,546,549,551],[97,145,162,163,522,549,551],[97,144,145,162,163,266,399,400,402,468,469,471,472,524,526,549,551],[97,145,162,163,195,549,551],[97,145,162,163,521,549,551],[97,145,162,163,549,551,618,629],[97,145,162,163,549,551,634,1625],[97,145,162,163,549,551,634],[85,97,145,162,163,549,551,2258],[97,145,162,163,549,551,1642],[97,145,162,163,549,551,1640,1642],[97,145,162,163,549,551,1640],[97,145,162,163,549,551,1642,1706,1707],[97,145,162,163,549,551,1642,1709],[97,145,162,163,549,551,1642,1710],[97,145,162,163,549,551,1727],[97,145,162,163,549,551,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895],[97,145,162,163,549,551,1642,1803],[97,145,162,163,549,551,1640,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991],[97,145,162,163,549,551,1642,1707,1827],[97,145,162,163,549,551,1640,1824,1825],[97,145,162,163,549,551,1826],[97,145,162,163,549,551,1642,1824],[97,145,162,163,549,551,1639,1640,1641],[97,145,162,163,549,551,2106],[97,145,162,163,549,551,2107],[97,145,162,163,549,551,2080,2100],[97,145,162,163,549,551,2074],[97,145,162,163,549,551,2075,2079,2080,2081,2082,2083,2085,2087,2088,2093,2094,2103],[97,145,162,163,549,551,2075,2080],[97,145,162,163,549,551,2083,2100,2102,2105],[97,145,162,163,549,551,2074,2075,2076,2077,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2104,2105],[97,145,162,163,549,551,2103],[97,145,162,163,549,551,2073,2075,2076,2078,2086,2095,2098,2099,2104],[97,145,162,163,549,551,2080,2105],[97,145,162,163,549,551,2101,2103,2105],[97,145,162,163,549,551,2074,2075,2080,2083,2103],[97,145,162,163,549,551,2087],[97,145,162,163,549,551,2077,2085,2087,2088],[97,145,162,163,549,551,2077],[97,145,162,163,549,551,2077,2087],[97,145,162,163,549,551,2081,2082,2083,2087,2088,2093],[97,145,162,163,549,551,2083,2084,2088,2092,2094,2103],[97,145,162,163,549,551,2075,2087,2096],[97,145,162,163,549,551,2076,2077,2078],[97,145,162,163,549,551,2083,2103],[97,145,162,163,549,551,2083],[97,145,162,163,549,551,2074,2075],[97,145,162,163,549,551,2075],[97,145,162,163,549,551,2079],[97,145,162,163,549,551,2083,2088,2100,2101,2102,2103,2105],[97,145,162,163,549,551,606],[97,145,162,163,549,551,604,606],[97,145,162,163,549,551,595,603,604,605,607,609],[97,145,162,163,549,551,593],[97,145,162,163,549,551,596,601,606,609],[97,145,162,163,549,551,592,609],[97,145,162,163,549,551,596,597,600,601,602,609],[97,145,162,163,549,551,596,597,598,600,601,609],[97,145,162,163,549,551,593,594,595,596,597,601,602,603,605,606,607,609],[97,145,162,163,549,551,609],[97,145,162,163,549,551,591,593,594,595,596,597,598,600,601,602,603,604,605,606,607,608],[97,145,162,163,549,551,591,609],[97,145,162,163,549,551,596,598,599,601,602,609],[97,145,162,163,549,551,600,609],[97,145,162,163,549,551,601,602,606,609],[97,145,162,163,549,551,594,604],[97,145,162,163,549,551,2042],[97,145,162,163,549,551,2001],[97,145,162,163,549,551,2043],[97,145,162,163,549,551,1896,1924,1992,2041],[97,145,162,163,549,551,2001,2002,2042,2043],[97,145,162,163,549,551,1993,1994,1995,1996,1997,1998,1999,2000,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2045],[85,97,145,162,163,549,551,2044,2050],[85,97,145,162,163,549,551,2050],[85,97,145,162,163,549,551,2002],[85,97,145,162,163,549,551,2044],[85,97,145,162,163,549,551,1998],[97,145,162,163,549,551,2021,2022,2023,2024,2025,2026,2027],[97,145,162,163,549,551,2050],[97,145,162,163,549,551,2052],[97,145,162,163,549,551,1638,2020,2028,2040,2044,2048,2050,2051,2053,2061,2069],[97,145,162,163,549,551,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039],[97,145,162,163,549,551,2042,2050],[97,145,162,163,549,551,1638,2013,2040,2041,2045,2046,2048],[97,145,162,163,549,551,2041,2046,2047,2049],[85,97,145,162,163,549,551,1638,2041,2042],[97,145,162,163,549,551,2041,2046],[85,97,145,162,163,549,551,1638,2020,2028,2040],[85,97,145,162,163,549,551,2002,2041,2043,2046,2047],[97,145,162,163,549,551,2054,2055,2056,2057,2058,2059,2060],[85,97,145,162,163,549,551,2115,2125,2130,2136,2137,2144,2146,2147,2149,2190,2193],[85,97,145,162,163,549,551,2115,2125,2130,2135,2137,2146,2150,2151,2153,2154,2190,2193],[85,97,145,162,163,549,551,2146,2151,2195],[85,97,145,162,163,549,551,2129,2193],[85,97,145,162,163,549,551,2114,2115,2117,2125,2193],[85,97,145,162,163,549,551,2115,2125,2146,2184,2193],[85,97,145,162,163,549,551,2115,2152,2173,2177,2193],[85,97,145,162,163,549,551,2137,2160,2161,2193,2234],[97,145,162,163,549,551,2114,2193],[97,145,162,163,549,551,2125,2193],[85,97,145,162,163,549,551,2115,2125,2130,2136,2137,2190,2193],[85,97,145,162,163,549,551,2115,2117,2151,2165,2217],[85,97,145,162,163,549,551,2113,2115,2117,2165],[85,97,145,162,163,549,551,2115,2117,2145,2165,2166,2193],[85,97,145,162,163,549,551,2115,2125,2128,2132,2136,2137,2161,2175,2176,2190,2193],[85,97,145,162,163,549,551,2119,2125,2193],[85,97,145,162,163,549,551,2119,2125,2190,2193],[85,97,145,162,163,549,551,2193],[85,97,145,162,163,549,551,2151,2161,2193],[85,97,145,162,163,549,551,2114,2161,2193],[85,97,145,162,163,549,551,2161,2193],[85,97,145,162,163,549,551,2126],[85,97,145,162,163,549,551,2115,2161,2193],[85,97,145,162,163,549,551,2113,2115,2193],[85,97,145,162,163,549,551,2114,2115,2116,2193],[85,97,145,162,163,549,551,2114,2115,2117,2193,2245],[85,97,145,162,163,549,551,2138,2139,2140],[85,97,145,162,163,549,551,2125,2127,2128,2139,2161,2193,2196],[97,145,162,163,549,551,2183,2193],[97,145,162,163,549,551,2125,2126,2145,2188,2190,2193],[97,145,162,163,549,551,2113,2114,2115,2117,2118,2119,2125,2126,2128,2136,2137,2138,2141,2145,2148,2151,2152,2161,2165,2167,2173,2175,2176,2177,2178,2185,2188,2189,2190,2193,2194,2195,2197,2198,2199,2200,2201,2202,2203,2204,2206,2208,2210,2211,2212,2213,2214,2215,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2240,2241,2242,2243,2244],[85,97,145,162,163,549,551,2115,2130,2137,2156,2158,2193,2209],[85,97,145,162,163,549,551,2115,2119,2125,2166,2193,2207],[85,97,145,162,163,549,551,2115,2125],[85,97,145,162,163,549,551,2115,2119,2125,2166,2193,2205],[85,97,145,162,163,549,551,2115,2137,2145,2157,2166,2193],[85,97,145,162,163,549,551,2115,2125,2130,2135,2137,2146,2190,2193,2201,2209,2212],[85,97,145,162,163,549,551,2135,2193],[85,97,145,162,163,549,551,2150,2193],[97,145,162,163,549,551,2120,2124,2193],[97,145,162,163,549,551,2118,2119,2120,2124,2190,2193],[97,145,162,163,549,551,2120,2124,2129],[97,145,162,163,549,551,2120,2124,2160,2178,2193],[97,145,162,163,549,551,2120,2124,2125,2130,2131,2132,2149,2154,2155,2158,2159,2193],[97,145,162,163,549,551,2120,2124,2138,2141,2193],[97,145,162,163,549,551,2120,2124,2161,2193],[97,145,162,163,549,551,2120,2124,2125],[97,145,162,163,549,551,2120,2124],[97,145,162,163,549,551,2120,2124,2125,2164,2165,2167],[97,145,162,163,549,551,2120,2124,2125,2164,2193],[97,145,162,163,549,551,2120,2124,2126,2148,2193],[97,145,162,163,549,551,2144,2160,2183,2193],[97,145,162,163,549,551,2125,2130,2143,2144,2145,2160,2168,2171,2179,2183,2185,2186,2187,2189,2193],[97,145,162,163,549,551,2125,2130,2143,2144],[97,145,162,163,549,551,2183],[97,145,162,163,549,551,2124,2125,2130,2142,2160,2161,2162,2163,2168,2169,2170,2171,2172,2179,2180,2181,2182],[97,145,162,163,549,551,2120,2124,2125,2127,2128,2160,2193],[97,145,162,163,549,551,2130,2143,2148,2160,2193],[97,145,162,163,549,551,2143,2153,2160],[97,145,162,163,549,551,2130,2160,2193],[85,97,145,162,163,549,551,2128,2156,2157,2160,2193],[97,145,162,163,549,551,2160],[97,145,162,163,549,551,2143,2160],[97,145,162,163,549,551,2128,2130,2160,2193],[97,145,162,163,549,551,2146,2160,2193],[97,145,162,163,549,551,2161,2193],[85,97,145,162,163,549,551,2151,2152,2193],[97,145,162,163,549,551,2128,2135,2142,2144,2145,2161,2190,2193],[85,97,145,162,163,549,551,2148,2152,2173,2177,2193,2220,2221,2222,2235],[85,97,145,162,163,549,551,2193,2206,2208,2210,2211,2213],[97,145,162,163,549,551,2193],[85,97,145,162,163,549,551,2213],[97,145,162,163,549,551,2125,2193,2239],[97,145,162,163,549,551,2119,2193],[85,97,145,162,163,549,551,2160,2174,2177,2193],[97,145,162,163,549,551,2135,2143,2146,2160],[85,97,145,162,163,549,551,2156,2216],[85,97,145,162,163,549,551,2112,2113,2114,2117,2118,2119,2125,2126,2127,2130,2148,2156,2190,2191,2192,2245],[97,145,162,163,549,551,2120],[97,145,162,163,549,551,568],[97,145,162,163,549,551,563,565,568],[97,145,162,163,549,551,564,565],[97,145,162,163,549,551,565,568,572],[97,145,162,163,549,551,564],[97,145,162,163,549,551,565,568],[97,145,162,163,549,551,563,564,565,567],[97,145,162,163,549,551,563,565],[97,145,162,163,549,551,564,565,574],[97,145,162,163,177,195,549,551],[97,145,162,163,549,551,588],[97,110,114,145,162,163,188,549,551],[97,110,145,162,163,177,188,549,551],[97,105,145,162,163,549,551],[97,107,110,145,162,163,185,188,549,551],[97,145,162,163,165,185,549,551],[97,105,145,162,163,195,549,551],[97,107,110,145,162,163,165,188,549,551],[97,102,103,106,109,145,156,162,163,177,188,549,551],[97,110,117,145,162,163,549,551],[97,102,108,145,162,163,549,551],[97,110,131,132,145,162,163,549,551],[97,106,110,145,162,163,180,188,195,549,551],[97,131,145,162,163,195,549,551],[97,104,105,145,162,163,195,549,551],[97,110,145,162,163,549,551],[97,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,145,162,163,549,551],[97,110,125,145,162,163,549,551],[97,110,117,118,145,162,163,549,551],[97,108,110,118,119,145,162,163,549,551],[97,109,145,162,163,549,551],[97,102,105,110,145,162,163,549,551],[97,110,114,118,119,145,162,163,549,551],[97,114,145,162,163,549,551],[97,108,110,113,145,162,163,188,549,551],[97,102,107,110,117,145,162,163,549,551],[97,145,162,163,177,549,551],[97,105,110,131,145,162,163,193,195,549,551],[97,145,162,163,549,551,2111],[97,145,162,163,549,551,2134],[97,145,162,163,549,551,619,624,626],[97,145,162,163,549,551,618],[97,145,162,163,549,551,559,618],[97,145,162,163,549,551,618,620],[97,145,162,163,549,551,618,619,620,621,627,628,630],[97,145,162,163,549,551,622,625],[97,145,162,163,549,551,622,623],[97,145,162,163,549,551,560],[97,145,156,157,159,160,161,162,163,165,177,185,188,194,195,549,551,560,561,562,569,570,571,573,575,577,579,580,590,610,614,615,616,617,618],[97,145,162,163,549,551,560,561,562,576],[97,145,162,163,549,551,611,612],[97,145,162,163,549,551,562],[97,145,162,163,549,551,578],[97,145,162,163,549,551,613],[97,145,162,163,549,551,569,580,618],[97,145,162,163,549,551,589],[97,145,162,163,549,551,569,618],[97,145,162,163,230,549,551,618,631]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"dc0a7f107690ee5cd8afc8dbf05c4df78085471ce16bdd9881642ec738bc81fe","impliedFormat":1},{"version":"a555cd66d7e27bc79c9e9dc35143ecd9c67b16651d190c16541858e4901c1f17","impliedFormat":99},{"version":"47e48fa5bf7f6ef35870b375116fd43bf04e6e509939d486ea3af8544e67e9af","impliedFormat":99},{"version":"2d85fed658bf18d558a95de0bcb7b9faf1d59fb9c60a8744a04fb0e80559b58c","impliedFormat":99},{"version":"5a2db43b355f6d0929e827236039f072c54cbdd1b12b9d1efe1d3dce2afc2853","impliedFormat":99},{"version":"c243e36b099c4f88c48aaf86e5ea24aac91f9aa4ffe52c8e38923b3b2bf75f8e","impliedFormat":99},{"version":"7539ec1337340097915160be1475046b869e94fa559e25767ac41d0f337ae716","impliedFormat":99},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"73b5fa37db36eeac90c4d752e39586f1b57187400c4f5280fd05f16437287a45","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"ea4c0e6a7ed3c61f196e0c6e17548ae6303ca5a63225daed8a65b7242f73a5b0","impliedFormat":99},{"version":"1fc4245b44800eaeb626f71a54be90a2f1110e0bac5210860e6df9346e606141","impliedFormat":99},{"version":"8dd819eb0bb9a0b5d3e7cbc2bf905bb927052070fae97a6655372ac7e4fbdd24","impliedFormat":99},{"version":"1a7c5d19345e1a16ef7c964f34f78d710481d1a08f69e5964ab9eadc268bd020","impliedFormat":99},{"version":"065c4ced0702946eab3f20883da7d36895f9dd76cb34c027e350465dde07ab59","impliedFormat":99},{"version":"6a10b2149a43c6da37c6ff93779458d3bb03a12e59c927339be348f66466e2b3","impliedFormat":99},{"version":"40d575bc7f1ed201ffd94a038fbe4f77c77739cffe4f5917c9743cba440fde1a","impliedFormat":99},{"version":"372e490895535d33c4754c861ed6ad39e2531a00849162dcedd8ed25541632cf","impliedFormat":99},{"version":"b3a85ced809e8bfd37a4b6e89f74fc4ed2307a67c1ced31d7997648aedc07e86","impliedFormat":99},{"version":"224d7e7cb420c30a5f7abb69f7cdc5775f349c723a0e77771256a5bd7889752a","impliedFormat":99},{"version":"8907ae7dee35a6da3991b5c29a3f1d2d42b0bd1ee292b310bfeabc8df690fedc","impliedFormat":99},{"version":"bfde6723658d0577663ca527cb4b6daeabdf599abe4275842c27f28d32caadb0","impliedFormat":99},{"version":"ed3acb493196ebb64e15249ce99de12ebe760417c4e887e944d0400d4f83037e","impliedFormat":99},{"version":"de529d5f9801472b94f9c8132f8e675802366b6bf4474e7cacb9e71dc7c5a8f6","impliedFormat":99},{"version":"e9358d892579183b8fdc1434767886c1158297f611b0873e2bb3794c4886d02c","impliedFormat":99},{"version":"ea20a5d64d18e6dc81631a9e4b4715821ddd1930e47b1d0c37270baf6b030264","impliedFormat":99},{"version":"5fc166639150e57923817490386b22ba418e0e0da48499932da816f1a85a3d17","impliedFormat":99},{"version":"d4d0d29af8c2ba7650fa1b2319eec72e39e56d48ab1ffbd17255e7b5bdb5999c","impliedFormat":99},{"version":"aaabb01b178c3c642633bdcc93d7c86c7be8258cd9964fece9c9bc97bd85cee3","impliedFormat":99},{"version":"6031f9dfcdd1884c8b6669b5c809532f9ac23a763fbb81541b42d839b304835d","impliedFormat":99},{"version":"5f9142498f6963a72d8f07a644bb9825a32523ef634288deef9e791dc915022d","impliedFormat":99},{"version":"b5ecfadf7492b9a4670665f3b8e076b1f11e355318a93b385a9b79a8f4c97693","impliedFormat":99},{"version":"7eedf8248cf6c63bc31979dcf8cc85fac2b81450563e11a645f089a19ad67f31","impliedFormat":99},{"version":"7d465305bf47c8b2e4e54944a1974ead8ba1ac6ad783a5e10ba46947826a9a3d","impliedFormat":99},{"version":"3e1b1f66f9db5d0eccff74e22ae6ec169241612569dc22e17876fbf319f56b5f","impliedFormat":99},{"version":"458c2bd2f4ef842d78f72644ef82d7800a634d9f8953e6374ea98a407d44eaa8","impliedFormat":99},{"version":"e88dfce71d124c1b88c2ab2b1045057a1e446e4d556d3fbe8e742d99d1bde07e","impliedFormat":99},{"version":"669bf12de9b4e0d3c2771d6dfd27e36eefe472d5639150512c1df3ad016f2f61","impliedFormat":99},{"version":"da3f762e95de4a4c9062d8bc0d3dbb3971a6011b26cecd94bf284abe21b9f443","impliedFormat":99},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"3003c9fee1824b9ccbdcf45c0db26fc6b3a92c292e690dbc4fa790d88544e222","impliedFormat":99},{"version":"f4d3e383000be47a71ea451a193ca55b9ccc260348f71159b458393286a26fe4","impliedFormat":99},{"version":"ef391c3c542f2bbcd14cefe9dae2ef8bb34678a0f5405771655fa003fad5ae93","impliedFormat":99},{"version":"a556956c51a5a2627c7a326480cb7dd3980c1344d79b619ac9235a79ead40824","impliedFormat":99},{"version":"36cd5427511b0535f6960f46f69102a7bb4055ef41d109eb84d4fae0fe810677","impliedFormat":99},{"version":"59ff07aae803c0030cee54ef6355d3ae78085005f8e07749e09bd7578d71d033","impliedFormat":99},{"version":"734efc98c48000abc57f0ee3e798deb92e91530748974cb9ec055301184a8b75","impliedFormat":99},{"version":"da245c3fb2d25697acd9136e9ad927b1d96737714e99ebd3494ed852f3ef4f2b","impliedFormat":99},{"version":"7b8ddad473f62a3a211fd3c59dc8f05b6c4115d98b30609a4550ef7387e9779d","impliedFormat":99},{"version":"165731886f97a6b5701410125dca36bd548bb7bd20de8952b14be92601a3a9bc","impliedFormat":99},{"version":"060ce84cce4904d6595d7793f81f9b5cedd9e4681dfc669e93af2a6be9ec804b","impliedFormat":99},{"version":"a37468c7ea5341b1c60c843e0676747c70f20ac2b68827aa302bd1ac9deb7d39","impliedFormat":99},{"version":"013f8df340ed88e6595ee54f4ad15a95f4d5014a010dacb28e354e00a9547b30","impliedFormat":99},{"version":"7f356548dff35e8bb3f57aecd595dd486dfc672753f766ebd8231bb1dc059d02","impliedFormat":99},{"version":"5ebdd0748809b362f6d6b28ba9a5fcf33794a4e129027d12151ef5302cec1a5a","impliedFormat":99},{"version":"98f8f6be93443acd7c5569a9f32cd5407d80156d4b48ec02218ddb1cfedd2c54","impliedFormat":99},{"version":"4e66c9d1b7c9ecea760a26cc92ba8f7e81201dad59a9286557fe78252aaf69cc","impliedFormat":99},{"version":"3440964b4db89e6bfb3dd13cf80e3f3730d0f91ffc2043febacdd3d9e36db2ae","impliedFormat":99},{"version":"d65cd12fa008790a951518943909be07456c3e4f8cedd5ed49e48f66d6734431","impliedFormat":99},{"version":"731c5e962fa986c68c2ff3f1d5ecb041e08456ea69c740efc1ddf05a5f5f64bc","impliedFormat":99},{"version":"6e6c8d86261359c4800467da3c35a52b4750fa56bfbacebf0531101d042b0fef","impliedFormat":99},{"version":"f349c5a14a53dbcdeae5fc83ce6e17dc8eaebb98383f8c886ac1ab173b7e5b66","impliedFormat":99},{"version":"d2ed9b1b5b3e5a355e63f928b07fd2dd574759845217d7658154bf2e327fd382","impliedFormat":99},{"version":"ddca8186f3be6dcf1359641112f504f186d562fddeb3f4b2566c71385deb1e16","impliedFormat":99},{"version":"23046b688a9bcc1cb1fce1b424fe94ed8acffa02392bec0e20721279f1f919ce","impliedFormat":99},{"version":"6ee625908f50a1d83de933c1ee5cb5f97400d634492790ee3d69eada95f48781","impliedFormat":99},{"version":"cb9ffcf4085321463e5ef121470daae1c4755163b5a23844b620cfecb907cb57","impliedFormat":99},{"version":"aca16379e294f23c8dd60ae161494c26afdd3ca6c59218b01c24bc78c8914d0f","impliedFormat":99},{"version":"92207c74204b46f4cb8d6490f025164f5009224f3432c5ba483606bc58001ffe","impliedFormat":99},{"version":"9b9477a0a71e711dbca96c6a9b3124b04b13f476032814f5da24005d9db00462","impliedFormat":99},{"version":"0438c6ba31c9269d9d23a8c0b9978fb3c333a3a4bde9a10c4d934b50aa385908","impliedFormat":99},{"version":"7aa94ed2fad95d28dc4fadc80e11d7eae9aa1a4a1c04e2e41acc45fd0d70afb5","impliedFormat":99},{"version":"ee7da2e03b53faec56bbc55ffce05614e620ba8e2e09d1a03a95773da96ac226","impliedFormat":99},{"version":"952e776ccf4421b1b09a0cb9c3b4635a2c3fe7cda0e61e755e97b4c366bb2df2","impliedFormat":99},{"version":"10eaa7c12922b8cda84c3c824216e7a9b844359a8a6464669edce3f1d53e21f5","impliedFormat":99},{"version":"e390a8b98395e2dbe65c69ccaffbc894efe63bc868ee0c37e4f7679f70bd2317","impliedFormat":99},{"version":"86dfa39a73d2a23e7d7b27ed74fcb4ceb8c118591b30e5835cb368e7eb57498b","impliedFormat":99},{"version":"c02995c5bcea85a7926547c5a3223711a816bcdde8c6890d2ebe7cdc1e32b488","impliedFormat":99},{"version":"ceaf20ac096b25e04bf001d9a5e85ba23f4c59e1f4e6dbcaa35937098bb6a2e9","impliedFormat":99},{"version":"5fca193def948840f510b93b157246c73b8b85571ec133f214cab9577e4dc8d8","impliedFormat":99},{"version":"adfc4e163d986d72d413b0e178ddec1e9dc41750f96eb636046f62af5b9e6d29","impliedFormat":99},{"version":"27c9f2517d9a7b514173797036268688933c4ace95d9e8bde31551741aaa00ed","impliedFormat":99},{"version":"740029a1aaeba00888056d764d3c96a4cd2dd103401c66c0d2e59b4322914f29","impliedFormat":99},{"version":"dfdc8b90dab0bf89e950a8f87e0d36dd342a969dd6d682f4ac7c1e4afbfae092","impliedFormat":99},{"version":"25be9ce21d65c712455b16ca2da936d29ae3dad1fa600fbd54d7bd42e228df88","impliedFormat":99},{"version":"fa1560c57522681946ad07abb032ca104b959a7a0220c57280e2d606e8e1aaab","impliedFormat":99},{"version":"5185978630bbea860ca49f53d4cada0aa9da9a202e00aae2ad51a1e9aed94e7e","affectsGlobalScope":true,"impliedFormat":99},{"version":"c9878628ac9dc7020a09ce157f15993f9d94e3c34a8caaa633415c07d1c4016d","affectsGlobalScope":true,"impliedFormat":99},{"version":"2496512ae0d1aeed4346e9bdd98b6075bbeb987f4422dd57c431a4e3bb59a271","impliedFormat":99},{"version":"6fd4d2dff54344a12ed5272d13617b2103fffda1a5ed9236742b17c5df7122f7","affectsGlobalScope":true,"impliedFormat":99},{"version":"529df9b2a29fe94efcde954aadbb83a08ed4d59751855b86adb7185e493bcfc9","impliedFormat":99},{"version":"c5d65ee720c6e516d983bac3133e33860c8a937a32597ee778d6fc00bf4e4f94","impliedFormat":99},{"version":"25a930d475daf9405009c1ad0375ab95c521cb28c18db7022e4e079115686f5a","affectsGlobalScope":true,"impliedFormat":99},{"version":"1c37c30577045eadd258a653cd4e7e37fc51aad58f880d14f915bea63b446243","impliedFormat":99},{"version":"7189ec2cb372826bffe4ed48e7c901febaca88782d306ba99f91e74e3444ba9c","impliedFormat":99},{"version":"94020f9643cddf4c4638c7c68e90c737ed29eedea0bea4946240318d635be596","impliedFormat":99},{"version":"698493e4cbd82355a030a0d52d0e626c6470d8fdd4e6f94f0fa9b43148bbaa51","impliedFormat":99},{"version":"31f85df58ee3a9739afbd6dc191e68fcc4cad9ff9353ae61d1bb9246f5440574","impliedFormat":99},{"version":"f1d11089e6affadeefa9054a26c63314dc593a11aa5d1688a7fd4363476b2ccb","impliedFormat":99},{"version":"5ba66dc6d498364740007979d481dbbc8b2773faff4bbf0ed9558b6e95d4c1db","impliedFormat":99},{"version":"df1afb0759180d630850f0461178abea0e1a1b7fb2bb6f1c6af7041c4a5ec2e7","impliedFormat":99},{"version":"b91ad03c16a3ab127d8e9f9e625616b0e62d59c013ceee91f946080ccf81a362","impliedFormat":99},{"version":"2055071b3a05b43b0d56927100202b364659aaea914d95cef0e7b0702e910ace","impliedFormat":99},{"version":"eee63cfeb0618fe6ab95b1164c05a52d12df0ea6b8057694a5ecd418ee0d0ffa","impliedFormat":99},{"version":"9c8c2a68da35ae6eeaddbae711d6b9d667deada437bc2f5eb1c6105faf4f04fd","impliedFormat":99},{"version":"bc1c89d2c0dc2bc0ce2b70f09172e3aad7d913b3aafa9fdb41983af49a69229b","impliedFormat":99},{"version":"91cf03ddb6e19fcc5f5b1e0c4471c2bb1595deb1c97444b7c27412f4b3d2ae06","impliedFormat":99},{"version":"25a6b927a88df9da934d9e45f80e5c436a9abfb4c40f35ac9e478a203d4f99b7","impliedFormat":99},{"version":"ac1f7711e76f9abce0edbcc8161b65fcbffb6aec84081c77a0aad9c82e6e2ca8","impliedFormat":99},{"version":"0580db5d4ce0941611d49b38a5fd8047a89025eede67520187a91e6faf4cdcfa","impliedFormat":99},{"version":"aff688e5eb69f99327fce1034984a0b030bb112c63c838a7f176c942bac0301a","impliedFormat":99},{"version":"3bd42a2aa00025ecc0bd58a541d9c92450965fb9fe14e3dde07c2ada857f60e8","impliedFormat":99},{"version":"aae105cf7768838c5c821c23d9263227c8684f5f74b0db696476999e3119728a","impliedFormat":99},{"version":"f319bba8358cf36287ceb54c1b86e15c8d2304d40f7457a2b86d3f4f2f6b784d","impliedFormat":99},{"version":"2b68f2130c248c3f65303e6569ad0c916a6dd61196afef7e8c4f1635cce7825b","impliedFormat":99},{"version":"48ad99a5708f61492047f37f0c1a1743edccfa5e7992c67e16ea8f0fd81f7654","impliedFormat":99},{"version":"cefb91b9a929ce533f403746ae4a47605fef6e292f8ccebb8ce73750f9e08c1a","impliedFormat":99},{"version":"96ed003769b5263006a1d5463c7f2816d37f3b55d7b0233e74110b948d606a72","impliedFormat":99},{"version":"47f6e4498345f1ba370b96b9c6463aa1e174cf9b83a2d919e6eaf40129f11123","impliedFormat":99},{"version":"2df61715b2aaa5c11c47b625e377d7886159c7b89e41db6674f60f501454fa5f","impliedFormat":99},{"version":"2e17e61a94b8f7d35e9949fedc5007e10035dd715d2e355497e05b857ab912c8","impliedFormat":99},{"version":"4a68002953686e54fd6f39324a8e63b5ef5cfe448874ab681e73390962222015","impliedFormat":99},{"version":"8ce387853589e9760f36496db6d40ca8733594e50e4035ca0a06079aad43e525","impliedFormat":99},{"version":"a4be8b24ba3c8ccb6bbcc83c2caa3c85e734c194c88ef17a2be9244571d1990e","impliedFormat":99},{"version":"1d06930a0850fdec2ba393c37709a55bb5dde14fdcbb30e63fe22a91beec15b0","impliedFormat":99},{"version":"4d5ecac7f1bd932a1b5395c79cae1e5e3f61d66b6c6d40cdfb5b1a5c38655906","impliedFormat":99},{"version":"a44d1d552fca3e43739f09ba449b72e9289f6e5c742f75ce630b20773cb14adc","impliedFormat":99},{"version":"0c458f1956ab63ff6e95a9b5a53c05fa4aa91ba2e0b8cb54f88652c6750eb9f0","impliedFormat":99},{"version":"279d460ee7f927ed7125de526a7237105175fdddcef0605e7ce11dfa68f665b9","impliedFormat":99},{"version":"90281050842f5ad59d27d66c9a8bc907224ffb72c77554dd9760ba36217639c0","impliedFormat":99},{"version":"c7daf1a566b28e5f9911bc17202da4e8ec155d01d3e339601f58457d4975f452","impliedFormat":99},{"version":"dac5cb3730d1f696de08644e27fc27b75984ce6781408d9c9ae37a4801ddaf75","impliedFormat":99},{"version":"b064fe0b6e4cc636fc1621a2d6026de3bc79e72e7b5b874a886923e6928d2ad4","impliedFormat":99},{"version":"bda4a152521af374e10a8f4b3a433684be415f6261860c0421f869c558c68c1f","impliedFormat":99},{"version":"dd5924b8454ea83caca77184ce0ff89f83c3279bcf3b664775f24e3c1b866966","impliedFormat":99},{"version":"37cc1def750366d1c73a485a3cf9aa0e506aae8ab24a7e6e3df8e9dc9befdf4e","impliedFormat":99},{"version":"43c49498b01155520426e171bf63b6121a63bd73eb5368e2a973072bc60c1aab","impliedFormat":99},{"version":"77d002edddd09dc5b928a4c415b21fcbaca43b7f965098c066e805f4bdc1ffeb","impliedFormat":99},{"version":"6319dc443c7249a9260394d93490971aab53a7c7f1bf5dbf13fdadfca64dd145","affectsGlobalScope":true,"impliedFormat":99},{"version":"f470982ee325514221c9adba26e9798299b7b3740f14a5b2df46be8bdc1b6017","impliedFormat":99},{"version":"4dcc86e20282dd9435de787f43039719f278273eb85355b98c00533f64f86657","impliedFormat":99},{"version":"f907672bfda02bc86774daa365348ca4403908dba20b133714c06d5ec4accea7","impliedFormat":99},{"version":"87581523e352e98de7d166c6401426a5237978900b377422202f0a8a4011027b","impliedFormat":99},{"version":"86ff7f8a4e06829af80f74cda25b3ea89971b00a683c7fcdfa1b0969fdfb8f00","impliedFormat":99},{"version":"9da9ee1239371d30c46aa686eab8b49f3db11e0631777c26fb4255bbdc043839","impliedFormat":99},{"version":"7f6a4fd1ab7de6d3621cbb1220dcfae7d0b9ec0ec8bae502f070f00d8e82343e","impliedFormat":99},{"version":"4cdb7efb0e2edea0a9dcde107c473989e87759808d627f27d0b47b01e7807b65","impliedFormat":99},{"version":"3450aaf6504c20bcb8ad3b047cdc542516eae10ddad2a9d1e56ff285b2e3ab0f","impliedFormat":99},{"version":"63d1410c0681aa3c320f9a21de83637f7f816d50c3174aa71a0a660034c1035b","impliedFormat":99},{"version":"19b8298aa43f14664cf3399d137f75b0497eafdff29b549034a2514ef8916ec8","impliedFormat":99},{"version":"5a3e9fbcdda81f60c5a91c05b4aff5f5d5251417b48caae401f966ea97513bb9","impliedFormat":99},{"version":"31e4967d9e11f8bff2519eed61d69f7f0c3928f30058342c50d39391eec7048a","impliedFormat":99},{"version":"b87a08a810fc1be967d7822dc61d3809022669e8dffb804adbf96b1255643ca6","impliedFormat":99},{"version":"7f9bb2eed8f9d2f553ead00265d69bde6ed0254237ecad8b7bc1d9cba64c213d","impliedFormat":99},{"version":"d60c78fa40a474c38093b2303f270754b42cc7eb5dbc148a8b2ab7e9d9919e0c","impliedFormat":99},{"version":"06493a11cbbec60f67d4b580181d36b301ddf0a53c80035ca7b6b6b7923f91eb","impliedFormat":99},{"version":"aead6cdf264400f5bfe37c5ce9d993dc145cf220b4e173747a0f625ff224dbaa","impliedFormat":99},{"version":"792174ab1344f68f5deb8bb9b1b8670b0e2dcebab5d7e1f0134dfa5745ab4864","impliedFormat":99},{"version":"6044b4f6ecfff173dee2f72e4bce2f36f773fe04531068db2f4dbbe5c3024a3e","impliedFormat":99},{"version":"0fb9f3ca246459c3e3646c849152b24422aa170b7dfdca0d967dce93c200be73","impliedFormat":99},{"version":"aca2de28c0dab5fbc1555dc3962f395d44928ffb1359dd02799b308a58728cad","impliedFormat":99},{"version":"b6688d5a6ec9182d6fd7a94997c94301aa74f07f160388914a0d61cddea8a9dd","impliedFormat":99},{"version":"21b7ab32d3386d0e858d58ea41b97e56edb46be843b6f96672d91bea0ab564a7","impliedFormat":99},{"version":"ae7fdfdcdb62f6c1556f478415f3cf3b6770ff6f0953c58f4750a1a26cbae550","impliedFormat":99},{"version":"75415b749743762eeb65c7d3866e7d73c559f8094cd3fc4e6e2234f1d5f8fdaa","impliedFormat":99},{"version":"94abe0fba5ada5592299147b968004e0eb000da19b047b8bbdc567b0baa13cc1","impliedFormat":99},{"version":"59744516c4a7b4ff8cc7e9c04304582f5476bfd4336e5d827584bddf4b82bc14","impliedFormat":99},{"version":"07e70f13b99e1bc4341c2bcee4711b59e2176f9ccb185da51ce4fed909708c60","impliedFormat":99},{"version":"a434a06820b11fd5b2d60b6373e927d2795f20f32562b1e8cfe3c8762915a56c","impliedFormat":99},{"version":"56cd7eea4288f8ace225baa23496397d3afd2af6060104a7de6227ee8319a442","impliedFormat":99},{"version":"ef15dc537d62e4c7893f628fbd6d71a8c8ec6a14ba181935948a33475d0f8d89","impliedFormat":99},{"version":"8a9dada96a98d023f7259289802c2573212c69053b293b1b9340044e8767eca1","impliedFormat":99},{"version":"90530a439a435e7133f3f81dd3d00a0c4989a22f0e56ba02bde855dd10ef3ae9","impliedFormat":99},{"version":"5a314b0ff6ff966aa72322af7246efaf37e38d4eb9984d33c3ae7a6125522656","impliedFormat":99},{"version":"806ab3563e1b53bf52620e3e30472d46e904488bd8577d8af046367f571ca408","impliedFormat":99},{"version":"40b1cda7bffbe523cc153efb920936661c37fe95e6e5fd1e74e0d258ca0998e8","impliedFormat":99},{"version":"8df63346396fdb49c76cd5a1c8f355596f60b9cd937147ef7c1f61d6b6c87216","impliedFormat":99},{"version":"83318a562de66a7d512365c1b7278f16031b35504ed53757379094deb1db1d92","impliedFormat":99},{"version":"974a70e013f408e846de038de05b706e492f77d6f12532868fa6bf088e901fcd","impliedFormat":99},{"version":"1070b125d10f2a08c9048d8df09c6b32b80b128d4c0954645c9f1c764ad0f45a","impliedFormat":99},{"version":"70c50958b0f7bdb6350248c9bf8c1ce5b3c56b4a2263697bb6a1a5cabc24869d","impliedFormat":99},{"version":"5f0a54e5e7709d849daa220d499ed01bc2c77559855f6cdcc4b19cd287572a23","impliedFormat":99},{"version":"157a6daa9f5d0355351980bab93e70b679072ac03b82019ef5b93e9065a4de87","impliedFormat":99},{"version":"1cafa1ff54983a053e3d64290837a0a1e0560bf64d1ff49e058f6f4bd09c7847","impliedFormat":99},{"version":"d1bfaf34862c6fff94239df1a170fd5dcc92dd9bb821a1366627d242f7104381","impliedFormat":99},{"version":"18602c7b2f1123cf65c1aebddb1f5d45852fa580a2ceec71df512363eece1e76","impliedFormat":99},{"version":"cd096b7bf9d4da6b75e52d027d677b95e5a74e179510b42c4aafbba98696c450","impliedFormat":99},{"version":"cc6ac5c272ac6582358bef9d7fa2b6a9702b9162dcdebfbb4d8430831edd69d4","impliedFormat":99},{"version":"79b2b89dc77345a256006b255088a80c0ab147e4ed6f9b82fe755cb5f61f57f5","impliedFormat":99},{"version":"db5c22670cf1de95f928be04bc09c2bdc9339bc2724d5558089b112ac21163b7","impliedFormat":99},{"version":"4d6f68b635d32cef5e2594e53db312736b99cdebf86d829bf7a3a38101328dfc","impliedFormat":99},{"version":"558aed1f681606313155b59561cf1452c3e7aab4f06325c8dd4c95a0e01c703c","impliedFormat":99},{"version":"5bc9297a6853378962797df510819c7d64bc10369195e95c7b58bbeddcbea8a8","impliedFormat":99},{"version":"eaf63a74a91894b939511af5c1050f83d305e61194bac7126efd56cd49080bc7","impliedFormat":99},{"version":"f28c291062f55461f15f22eef28039893a2dbc4910870e06e7bd7058d8a94fb3","impliedFormat":99},{"version":"6980ea3d71d22b862c00820953bc42dc5bbc05f7faae209e27e60d01543d6840","impliedFormat":99},{"version":"27f894c6a703b0c59c84b81831f091290c80182004a1b14ceb3ccc11fd1c854e","impliedFormat":99},{"version":"1bdd67539cd8d439ca49b7d211ce3211affa52581192f4f012d92366af071cab","impliedFormat":99},{"version":"aff0bc647dc4aa119a97c2439df1298d56115cf887ae820d29b94cc6fa783bcd","impliedFormat":99},{"version":"d57eb2635f2c233eba01d2e26665e57ae82b2a17614d830e09e8c2cec8117ebf","impliedFormat":99},{"version":"b3f55f822212a6bb4bd3b6767379c47db382330cc8094f033fcb4f1b9b109f1b","impliedFormat":99},{"version":"f2e17374225f371a497c34c70586a55019a7dc4782965d994264d519c4e86158","impliedFormat":99},{"version":"1be4765349431eb007b5f61266ee795285a6a9f01b38e31abc0b2daeb4c0c09b","impliedFormat":99},{"version":"18456b6424fc6b05cbec502d8e6ba9499233faccf0029dcd02675d046dd1beaa","impliedFormat":99},{"version":"ff0967b0890d335b4a5c0c48988ce9300c65878b3c54578251d86c4a916595fa","impliedFormat":99},{"version":"906d8f91ab93fa8e508104086152c90118a5a1efb7233e2839e63b01fee95e28","impliedFormat":99},{"version":"d79cc62b5aedbb27b3a1abced079a461f1bf27997d9881d040e9ff583dc39a5e","impliedFormat":99},{"version":"7384f621f9ffb6c83459bf06cd34f778a8249ebce68279fce95a9aa7f23d847c","impliedFormat":99},{"version":"7e796e15e7f57aae5c3f324669555a418767f8d1e03f9a50c59bdce306f67149","impliedFormat":99},{"version":"bdb380556a29f0068346166e62b2c5b9c9279c356eee4ed78fe1eab8d0f19cc3","impliedFormat":99},{"version":"f04df7615bbe206a446b252540258e286c434aefc277102efb1a022296b8b6c1","impliedFormat":99},{"version":"7230fdc9c9e1d86f099f7d9549774917f9c29bb8ded16fead627d2809b5d6bf2","impliedFormat":99},{"version":"91ce1882b1d2ee147f4ea8b3b6f3a229f6bb9899c033a7e1e7c2a335fcc91aa0","impliedFormat":99},{"version":"6d11156088ac92896eb7314648a3098003e3938330fbdead1d51b9677809cd1e","impliedFormat":99},{"version":"f67ae7d74048ec5aebb82359724c940237211fd44e59c2b3df0d4e04594e0ff7","impliedFormat":99},{"version":"e69c753c28b09d7b9ba45639eac08efcd9266450e777d47dd3d24df08b419421","impliedFormat":99},{"version":"b4b8460555d5f4645d5fb64ed12a982565c9d790787ecf487af0baafa2cdd5d7","impliedFormat":99},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"b566b90a5d97eeaabf688484eac06303fa99dd9c6990d160e7c6fb435b284200","impliedFormat":99},{"version":"9676d88034fd9b84fcfc13bc1661577a8c66ebd6b9200cceafad3597ff726185","impliedFormat":99},{"version":"fa3a64ea36267eff28960a68b20689aa41db56f6d9ed5679e33265d909ce3644","impliedFormat":99},{"version":"fadca6f42bf9789f7f16da85910abaa17554dfb8aded4077abf6ae2f5bcf3ec9","impliedFormat":99},{"version":"44cb01fef58aac0f2a77ccfa7417128e34a6c0ff4112415c4f19e8e5dd25838d","impliedFormat":99},{"version":"bb36693caae6b195d89e33c371f84ed7d37a66c6930da6f8a39f2170ff5afe31","impliedFormat":99},{"version":"bdcfddd90b83b03d030d85895cb60af6af88dbf33513d162ca357ee5e592e65a","impliedFormat":99},{"version":"dda695f30c1715ed5e54d0834656af55b7f48506cbbe51907803e425f4bd6ab1","impliedFormat":99},{"version":"dffb3960e4e26a3a1641ab8e2b7320b2e0ca1f3113677f3c93a7d10c77a036fc","impliedFormat":99},{"version":"d30dbdb4cdf0297fa8f2ba550668db6466db637246dcceebeb356c467bfa65d5","impliedFormat":99},{"version":"52ebd6b123aaeb3825e95c4c00d8c3339c40d9077ffbc8760b2148a83b316adf","impliedFormat":99},{"version":"a5f62c360dab6cd3b317112af223cac1ec5e9e708d591a16b0225cda7d68131d","impliedFormat":99},{"version":"fbb8b39e0931390a74d45f0386b6580882d743e4bd62895be0d0673d41026f22","impliedFormat":99},{"version":"c311014a81ab2ed7bcf0a919e26b260246a0d81a8f7367cedded288cca1b32ed","impliedFormat":99},{"version":"310dc478f8b89ff33fe82bc1cf8e0a5bd4792fd21cbd402268200badfb03186b","impliedFormat":99},{"version":"442e3a7ddb35ec7ea408c70d597c92a0f4cb039838fcc5d93cc4fd0f509b26e0","impliedFormat":99},{"version":"6e20e7c27dcc999c96aac79515bfac701fba269c3fc098f4496b7c281348a7f6","impliedFormat":99},{"version":"8c4e942fe91b6852b292a53c6db5502ea00580120422a5c53226ddeb2a0c390a","impliedFormat":99},{"version":"0f3dcbf106f4fb0c4c50ed31f33f5b8a04ef664fe7a2b657a8156eb82057ac56","impliedFormat":99},{"version":"c0c1c9818bbd471be8c31b9f19128e28a3b6465b9bd93fc95858bab1fc043788","impliedFormat":99},{"version":"803e0b332745c6958093297c93b9eedf7003a24444391b412108ee23dd07425c","impliedFormat":99},{"version":"8104f5e0cd6b984ad2f6752284b6eb2a47c65ee4fed6620f8cbea293099c9ffa","impliedFormat":99},{"version":"f178e9957e2aa76124668024b027231d64a69497863ce6e0345fccf89519677c","impliedFormat":99},{"version":"6ab24be21a48c26c75700aeb08c288b18d9ed53cda95877ee06c3185dc8725e6","impliedFormat":99},{"version":"b887242e79e59100edb277e53fb01194887a2479e49170aed1fedebf0ca7515c","impliedFormat":99},{"version":"1cd122938fd4734fa88b898c79ea194c3ff31807a4f4ad3e36f1c7d8cb145094","impliedFormat":99},{"version":"c4e52d33df11ff0ad0f86322209160f61f27e2fe387fefceb9958e58e6eb7624","impliedFormat":99},{"version":"0be70a1f14362e6bfafa2577221fb22af857a0aec1d2e5e7ed5cee9b256b3735","impliedFormat":99},{"version":"86355922f76e642ec2ba07213c2c3950cabe1b5d44538d6b1791167a5644e7b9","impliedFormat":99},{"version":"c82a90e55bb3c45c5f3a2bd3fbde0bbf772853a43f96f93426e16c59712530b8","impliedFormat":99},{"version":"927ea7d4b00308c33df4dd3ec9a6bdbc72d72a8f3762626d68c2a5b1f1b01b5e","impliedFormat":99},{"version":"956da2ba95442f3892cc2e4fd0c0c627f1d3b62dd586edab00e327689059d599","impliedFormat":99},{"version":"1fef193ba2c371c23f9899950af4dba1ec6f9c5b7fa8dff9113e4bbe8b4a77fc","impliedFormat":99},{"version":"7f9f6119b2de8b368358599c2e1ac0e023d939b8ee01026a8a0a99eb24ef9995","impliedFormat":99},{"version":"69855fb6771278b3e3f3e1bb410ec120fb79c07c2302e76a99ddde95f7a5ca85","impliedFormat":99},{"version":"ff65b9e768b2396404861e76a32d5920a867df2ba9f5ddb2c03796f33d18efee","impliedFormat":99},{"version":"339e4bce52d99951731cf67837bb11c1c9b482a90a40ae9b99d9ab88fd60d5e1","impliedFormat":99},{"version":"2dc893e3c7daef84d6dbd9a830219260d93e25dd7a85e1acd88c440f3d42ca31","impliedFormat":99},{"version":"e30135bb6ef302cbd1b0f0a7e662828952323eea621b8b4f7a335390bde0b2fa","impliedFormat":99},{"version":"e5353ee24e8bd9d3643789a2a5fd67104113231c7f837fb4fdc3e5aedc511c00","impliedFormat":99},{"version":"9596c274b174df87fb686604cf6b1126849a11af90cfe2ab3589895f09d73cb3","impliedFormat":99},{"version":"264038b34da0d6a8b5caec7ba5bb63ae4f83b6583a10e210ace21897b15d6f1a","impliedFormat":99},{"version":"71a506d37985319b26b5915432fc570a0034090af9d3e2234aa21379fd997208","impliedFormat":99},{"version":"506232814def20c2249a0aca107ded5676daaa76b7b97e3dffc1f1be82567fad","impliedFormat":99},{"version":"056745c7982a3ab724d992e1b4d31cb101758169f68317082c3dce49c2347a30","impliedFormat":99},{"version":"9d6c98d89a86dd8ba14b22aacc2d785f5c66b39ebf3237af6c09ce5ff7c07c4a","impliedFormat":99},{"version":"f2d8ff9d6504b017075cfe6c1e75c481776bed73f2f27481c0a2f3e35a44093f","impliedFormat":99},{"version":"c4e8eccf1545cf44ef476a07c10a03641732431f487f3d201eff8934aee2afed","affectsGlobalScope":true,"impliedFormat":99},{"version":"9e197f0fd186d4062051c07ff74e317126b26ccbaecec1f9080c1cf08608f6b9","impliedFormat":99},{"version":"25a4bd45dc6f5bc2b78204ac1aa7992cad1ed5a4f25d917d9029efa41bdc882a","impliedFormat":99},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"c7f2c06fd1863ba864cb9b30a2574d5a218c61538d1e059fe19b015e0b1d7da8","impliedFormat":99},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f5ca0ea6e383272726f2f5d0c1a0c150e3563c40e5321edb3ab2476430dc10a","impliedFormat":99},{"version":"ab3736d9d247ea9860ba916925606277dd22c0dd319e25a50d8be9efe89289d2","impliedFormat":99},{"version":"c3fc5c93ab597755da8776552e7f6e7b169d7a5a0c7e695a21c0970638d82ed8","impliedFormat":99},{"version":"0f01b7b16e429bebebce33325b1ffa8568a02f13299275cd39dcf24ccca2c3c0","impliedFormat":99},{"version":"549ddb9e54c2cb6d096a808116d3647f24e8bfa1f6190a73a0afd028bf1d73bd","impliedFormat":99},{"version":"101b9a573f65a4fb3b68df49faf4afdf242506a97365d8d15ce74f061ef6234e","impliedFormat":99},{"version":"b3b8f22c16b8e6fafc40bd35ba78eafd9a5da0c78c36b590b31783f8226d008b","impliedFormat":99},{"version":"b4e4ef8c0ce24f6d3c93fb6fae57d4d868a29ccaecd9e929d040bf249c2378c3","impliedFormat":99},{"version":"7ba5b75a9473997c0f925a9513530bff03472b04f5ac6f089bf1f2f3950e4ec6","impliedFormat":99},{"version":"c3dc4e1c53490ece2da72896738a56583ea589c4f59d7e08456a91072d323969","impliedFormat":99},{"version":"ac9e99858ae0c5ce5f7ac9781c183e54bb39088772a1454b79fb9b64a22b8b1c","impliedFormat":99},{"version":"12ced30351526e4caefd8c7e9846f7a61a854176d3c67cc4a917c5e9118f607b","impliedFormat":99},{"version":"d5ce9cf2ec67273b265be3ae56365581abebc1a741404229e3857cb62c007e06","impliedFormat":99},{"version":"5fda207e5dc5bea45a94df00d7c054d3fcd7e5263765598ea777d5232678afbc","impliedFormat":99},{"version":"400548ea0dd65b55584ff220479365fad02c3c3d69c2ec85532d9f00f9720d51","impliedFormat":99},{"version":"1be5cfa366271a8f52d4d031d3c4f4b561b38f997408ff3776c9d587af921806","impliedFormat":99},{"version":"5fa84642acf85d3f0f9d1729b7f6e972b877118b490697a80fbed7266a3fbb90","affectsGlobalScope":true,"impliedFormat":99},{"version":"39fdcbc08e370573d33e2b5e628eab435a86f8057d7f2678d06652332173309f","impliedFormat":99},{"version":"557944713c35f7c57585d9fb3bf69bedf52f053ee76f41ccc5ed778089bdf903","impliedFormat":99},{"version":"801e8201896c3817825d76bffbbe6a969f52b8f5608ffd23334d2c73a55c9b44","impliedFormat":99},{"version":"53bb34f4163a9187396201aa5c13786a3b41e0c3ad49f74f8bc54def60883df8","impliedFormat":99},{"version":"7bb01ad3b0d4cec47112389cf7a53b3c4c57dc20b41499aa70cba8cb5c42c6e5","impliedFormat":99},{"version":"0df38d4282042e20b66931d9790f4945c8e71636408d37262e4913991eda8b5b","impliedFormat":99},{"version":"b92798207bb4dde24d73f6ee3b2665f774a95236c68da39762d24f72cd93947c","impliedFormat":99},{"version":"8559ad4069886c42ac5d34494ce6ffb467e5bb450ab4f2d06e4b62000e5fbd79","impliedFormat":99},{"version":"e455610a176446ad7d9f32157d708dbc16842b49134b99123cc2a078a52dd77b","impliedFormat":99},{"version":"bc072c1ac74d1691eab36d002df0ebf21f29c28ebf9f3301ae4868520208dd02","impliedFormat":99},{"version":"f17fd28e02b36399d2242e19f1996a104596e77c8910f8ee68770690edd75963","impliedFormat":99},{"version":"2ad6f61ade0e62879fa3b732eaa09de579ae3b72d5f8b27f957336a73ce82fe9","impliedFormat":99},{"version":"eebc98945a1fed4c8fb484b7f4f15b965dc92666296da463c51b1f1414e9eb9e","impliedFormat":99},{"version":"4693fadb6c7e47f00216f1d6a2c2d1cb3c9b72d496dde67325046b27dfa869b7","impliedFormat":99},{"version":"0fac73479e5038d9f868a13bc1d34a2db4132e96b6c087ffc0b17b84a13e2809","impliedFormat":99},{"version":"b5bd4403dbc1f561034db151dca723f0decc329f8bc47e48b30bd988b661d32e","impliedFormat":99},{"version":"203220af7b209f3859b9793b03b6fae8c458439efde0a1f9b890ecaa2ca7bdd1","impliedFormat":99},{"version":"7ee817cbd31dafe47696c5919fb6da4bcfa94a69afb3fb16e3dcf1ae11e64555","impliedFormat":99},{"version":"7b663fb38c413724650b176992d19ab3e8afa0c4200ea142b1f4b8d00c05aa9d","impliedFormat":99},{"version":"9b04a2c9f24e2ee6ee29e4b79a9e236eac46df6b8c5aca07f584a37dbf067ca3","impliedFormat":99},{"version":"a7348e01355777f92d7806c5859a1b8e79e83f33c257675acb4fb7f614c882bb","impliedFormat":99},{"version":"80fc01011a22b7b6dab7b3b3be2883dead2643189b4f8a753686f364484c2e33","impliedFormat":99},{"version":"f823edad2f36d4ba2cf934818c5537c5b1c871136d5e7fa4eed7f3cfd3cb6551","impliedFormat":99},{"version":"5ddee96c31a163436cca5876a14cecd3a8968251116ed6f0d2e5d067fc3157de","impliedFormat":99},{"version":"3438619c848763c63eb881b6519d259fa04523b1421aa820498eec0eb028bb4e","impliedFormat":99},{"version":"d117eae30714be21337e3dbf16e3b61d5db0e1a761fdcc71f030371afb3d4abf","impliedFormat":99},{"version":"93fcf8710c079696d51883747ba99695b59e9f2a88faba9f273dbf324adf62b7","impliedFormat":99},{"version":"3184fd0943aa88050e4769ee6fccc5fbfb378c5999d6982901b445d072fc1100","impliedFormat":99},{"version":"85ae780d75bfc8a3af1ed4888b29ba20bbbfdf976f211f94776788e876542e33","impliedFormat":99},{"version":"9db173152dd0a34b9d02ddf9368f2cf36b332893d805163daf083d826f6ca0fb","impliedFormat":99},{"version":"9154f4eeb31a9ea717c0f7b17892d86eeed70d2618e9e499f9d5f8e7da856ee6","impliedFormat":99},{"version":"9c9b186a4db6539c91a08da9aba77467a11cd471cf187d13dde3ba67f58f25f8","impliedFormat":99},{"version":"04a0d343ab881126fe6b48b57efd97db1636834912b3be22e628070eb66749ef","impliedFormat":99},{"version":"d05628cedc9a4d0669a2702eba90618834e9e52fd5aac8ca640a32e772b973b8","impliedFormat":99},{"version":"871cdb4602570bcf007ffa893eafd0f9bef2df9c68df0975c89746afe322cf15","impliedFormat":99},{"version":"e340570c7f3fd2130f4dd4338e059d6e07eec5d5c63d9310a085988f92c9c296","impliedFormat":99},{"version":"94821af8816afa9c6cb0c2de14beef5d3885bd76aebd91103eb3f0471f7785a0","impliedFormat":99},{"version":"f4ea6e0577f00ef82bfef8f9f01100a65335a7d8070142d5a77a917aec3ca3a3","impliedFormat":99},{"version":"e9bcedb0803db5b2714ff8b481dc4aae87a85eec07251253eca887881444fc4e","impliedFormat":99},{"version":"504432733cc094e4803cce2eb6d960ccce26ae282aed5924e3e1e07202d6b530","impliedFormat":99},{"version":"bc0007d21554d647803151fc01f8a6eab9e5f0bd971e8c08a85389fb51279329","impliedFormat":99},{"version":"ca3d2544c1778ec16807ca1ce285ef8440fba86557e64595cd9a6bc6b2f8fce2","impliedFormat":99},{"version":"522519a0937c9cc7932fa963e3cfa4c75fa1969539f88b4f44563e34328f34a9","impliedFormat":99},{"version":"fe165b637786214b2a8237caefdfce0bcaf43bc7de3bfacbb512d45d6699af59","impliedFormat":99},{"version":"ae7f3f0a729e40be89c7e6efb534d591579c72b8f898635f90361fae4ad0e26f","impliedFormat":99},{"version":"3babc92a0b3bf9afb0d9d9e8542ce019777e037b14fa6da3b408e0f8c854216a","affectsGlobalScope":true,"impliedFormat":99},{"version":"bcad38ec1f58490154c711c2ac965cf5f84707a55d565bf206be930573aef044","affectsGlobalScope":true,"impliedFormat":99},{"version":"ec14b1424a4d6937f1ae0edeccd0fe6d16ba9b8b4e0c882164dc4a613cf4bc82","impliedFormat":99},{"version":"7a3972f0c9d6515b16a641f926a972e40c18cff857c3ea6fd910873f8b83dbaa","impliedFormat":99},{"version":"eecbd4d34beb3c5828130181d7251f76a0569991ab3a898b34a02cc498af6810","impliedFormat":99},{"version":"da3573bd1f5ebba4b6f9ab71e7c4edd46f178fc9294485b8adb0aa07ff631492","impliedFormat":99},{"version":"94d49b8238355655c25521d6f35e984822af62bb575409a2f0325ad55dfa730a","impliedFormat":99},{"version":"3a6fd8807628ea5f43bb145689cb7c35cbd22fc2bcdda7506fe35031295905c7","impliedFormat":99},{"version":"884e3b6d4162a225adc3c2446f553a12c95151c42ad3e4d2c451b6d84fc2334f","impliedFormat":99},{"version":"c46d622e9c776223c6eb70c1717a192ed2ba53de700f3b5dfef7baf14427c0a2","impliedFormat":99},{"version":"2935eae1a1f526014f666ab4ef14aca3ae85cfbfb185ef1335ea3ae995fd5325","impliedFormat":99},{"version":"0f4cb7ca4826b5071eb0381a9a8949572d088b66d72da5253d494811e4d68ea6","impliedFormat":99},{"version":"8c7978e5ecca0d339bd7c22b9653abff764e425ce5704457e208e3bc076cb921","impliedFormat":99},{"version":"d61df9de67271c5b52784edb1a7fcba714a2aa95cbcb1dd1bcd481d2a3d5755b","impliedFormat":99},{"version":"e32cb04fa7bddfd0a13780c9c892e0f232e43581ebb4fb85661655316c8220f2","impliedFormat":99},{"version":"e9300daf087a7100fcaece5dd806f3ba1a0b03725c2981c63afda2f63e6585c0","impliedFormat":99},{"version":"49a53069e06732832f50a2757ab9235ba88fa8808fbb7dd5cf1e8ed0512adb4a","impliedFormat":99},{"version":"bacddd8ce36e0dfe19dceef84abc2152156177b4f4c1404a0b81af9fa4b70411","impliedFormat":99},{"version":"77f73fd8058e3b539785f9c5f8765082a8c878806e9132006c13586e69808b23","impliedFormat":99},{"version":"a1619a80c775c0db4f3b693b59da5383da82c4256db775a7e1183c08e4f5976b","impliedFormat":99},{"version":"f2a9e54908c2478a7f021d77b2387e39941844b4bbb622824d46e926ef6141fd","impliedFormat":99},{"version":"edc8ad3b052ff78329c61921e6aec0ce972ba9878ce6982727da8e04bbf5f267","impliedFormat":99},{"version":"236c3602f433d5c7cf423f6b98be220678e07f5fe8c5308172d31f28ce6fddad","impliedFormat":99},{"version":"0eeb4d1c6cf4c82e64d289c13ab318ef969fbabb7589b80e1b687844b69846ba","impliedFormat":99},{"version":"dc6c0b2c9f06580e5930e962579e9fe584c4dd01549f9a7288790e583a9cf751","impliedFormat":99},{"version":"862e7ae7fa36853686c3473b64d6a8ca1341e050c684913a4a3b5da7ec374100","affectsGlobalScope":true,"impliedFormat":99},{"version":"c343deed7325ebb392b801ff9abf07e6199d7870edfd6bc33a3dfd7a19a0b9cf","impliedFormat":99},{"version":"07bb06901201fbbf4ccd2da18c3650ec3fc79ee14e7e7123e93d91b946d12485","impliedFormat":99},{"version":"b5a4d41c85acaeb1e1cf1f6ee4a3f6eeaded2a2a6392f41d37b1d9f53a7ec017","impliedFormat":99},{"version":"dcbe5fe927cb4c86ffb9a8f335a196c8754d6c2bcb9797c7a961b05b7dfcb84f","affectsGlobalScope":true},"a291a6b60536fec0a3b9d4ffd6d51179b463e9bc20281d7499d756813b4c07d5","520f5869cb65fe5e669b75255681958ebc48d20f704279a4bfc01c16218f1b27",{"version":"390c0531a9c068f5380e4b0334f2f67d7a04fdd1031cabfce020ee4eb49a4485","impliedFormat":99},{"version":"3b89216a7e38a454985ad17bb2ff85792837dc812f2a89fa5f60ad0a2e216fa7","impliedFormat":99},{"version":"16fe60bb544cfedfd2b5bb2f7d0b3957be7978706d57d9f06edc9c0c8dbdba23","impliedFormat":99},{"version":"de4a612aa8f1704af486f701e21993c786ba7d8cfed55fffa6684026aea2346e","impliedFormat":99},{"version":"c73fdf42528325dd17940937ed787b15ae3445c6a2dae1a2b74bc4d87d337ca2","impliedFormat":99},{"version":"e8e17dfef3cfa9f0847ac93dd535a9896af7fb57c1a1b164484bb1b0ee4a25d8","impliedFormat":99},{"version":"51d2ffea2d1ee4a81c775938588c1e16620281adb60cbc26579a2fc6baa10bd2","impliedFormat":99},{"version":"638a22f8e16b31c37297483fa38cfc760ed309b16d4c606a7b4532f4394c9c0c","impliedFormat":99},{"version":"9394183f4c37b8591156f32e41223c9e0dd211eefe7499155d4cdf15268eaea8","impliedFormat":99},{"version":"4a61989c1daaed8bd7a2c7d87f9fae8d98f1205b3de3b3131478f972d997380b","impliedFormat":99},{"version":"8543104cedbc17231ef94fe02530b612c466d436be7201a8a94f65694cb7875d","impliedFormat":99},{"version":"c074e054c9db79055d37d7d70131e9a3234b8186773b3edb617c13f80bcf8774","impliedFormat":99},{"version":"7d3e062a778b8f5ea4f0cac7e925e31f88e6739812ebc5f827474324a4048f14","impliedFormat":99},{"version":"aa26f7bd84a3ea2b59408071accf147ba8cb0d5991aabf1b9ba22d66ce36086e","impliedFormat":99},{"version":"5c3c3eb0a2b0ac00106e768c4920c56f2629545e9376649eed9671057469586a","impliedFormat":99},{"version":"5a9834c603c65aee5cba0c1d6b3c7aee85cdc7862832a23165c6aa4139c165f2","impliedFormat":99},{"version":"a40c35037b336bca6ffdd1b3e1384b1c1ac867a3f1588f70d1d6d6f821f49385","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"8887e70871f697fa42ad7cdf32168db60ec2d6760c173c97973e35377fe5d83b","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0864480ea083087d705f9405bd6bf59b795e8474c3447f0d6413b2bce535a09","impliedFormat":99},{"version":"e67cbea16f1994af89efd700542dbf3828a46a52b29e4d67e801bd7869dc103c","impliedFormat":99},{"version":"971f12a5fc236419ced0b7b9f23a53c1758233713f565635bbf4b85e2b23f55a","impliedFormat":99},{"version":"76de3321ce519928f1ff7d7a30391c0dc7374af20f81d9167919f038895b5cb0","impliedFormat":99},{"version":"094b9210da23b8711709b0535c59841186267bf6b83c1609aa9b515f830ab274","impliedFormat":99},{"version":"fbfbb4e99c6259ff5ccc4a5a62b3b63c0c8cae6e84737786c4a4c761c9a9de91","impliedFormat":99},{"version":"604887bbd5b0a93234ce882543a465f008636185c52e0f0353330e2bc38b03b6","impliedFormat":99},{"version":"32bf912173e8a9533631f9e9d8dc90a2ac7b52c2355611ddd886beab24dfd182","impliedFormat":99},{"version":"82695324abf7f3278b6d9f0582f4a544e8f7055c8cbe1065ab5cbacde1719c4c","impliedFormat":99},{"version":"43bba542e50e19241ec64bc13cfc0d9273e6198f36563cecad1f4e4b78ad47f3","impliedFormat":99},{"version":"0b215f03e21d3b61e59ec62ae5ffe869b8f8db497ee1999f7aee263aafe2e38c","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":99},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"38cb107048cd8ba54a70014ef9a30cf57bee0d9f10a0ca4cefa974056e1ee460","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"9a195d8476f48523446ece812e5450b5b1ec8c3b6abf99efb8ee2479524f6f56","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"f3fe8197217cb6fd6e07c0eaa0966e4ece77cd779cbfc55767a93704cc692e04","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"617112682fcb55805f0a0e16450397edc016881962eee4298f4f4e3ef795aee8","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":99},{"version":"a0e59ab6a42cc19a9d9fe4bd8eaacb9de9a12e25d4bf5027bc721df36f1a842e","impliedFormat":99},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":99},{"version":"be836d4f889a447168231c052f4fd5f3dc34538fc158d293120106a13dd2c8cb","impliedFormat":99},{"version":"f372433f71ec76d9077985c92edf379a96eb8b90b811a701f4ddbcc22f9e9c26","impliedFormat":99},{"version":"c0079990341b4e479244ec15477cc14e21de5e8da1f11a097fee5c57e891a387","impliedFormat":99},{"version":"76bb58011054685764d831b91388a6536bb5f8785446e21ba3e90de1580ea42a","impliedFormat":99},{"version":"7c27c5b2424fb3d06849c24969e1c900a8f3a0d78a568594397bdbee43560441","impliedFormat":99},{"version":"b846dab4e5d8c6a9ab1a088e7e74b506eca7822eb75aee969c41f617520b38df","impliedFormat":99},{"version":"8872b6cb84ba6412703d45d1fbe704740679019254cc177f9b62956246c69501","impliedFormat":99},{"version":"28ef7d5e83393d432df04c2982b5e8fdaee7f7f374ff55d28907c72068a237ff","impliedFormat":99},{"version":"d2454598d5b8d38a72af67066fee9e667825d11152742b38d85fbde9721e5ac4","impliedFormat":99},{"version":"b00d8f3b23ef3e9654e973a5e8f6716f5a0ca1ab01d742ac9e70ae559d226522","impliedFormat":99},{"version":"b8a02b1535771cd003a24b387285907b915fb616eb3b7c42f69f6ddde4e91f51","impliedFormat":99},{"version":"2faebfa830ae4cfbfb58e48b0ec20a2a63882d776f0ca36ec7155d45cf1b7f2d","impliedFormat":99},{"version":"dc6e3ce502651ecc523590588213bfb986c04b60f5265d37f76662b824425903","impliedFormat":99},{"version":"b3469d4cc21915acbebf7cc33c1d3bf4850a506051dd8f272607bc4adf67a6dc","impliedFormat":99},"e72f98cf1ca3e31504804fc5e3cf5dbb74c437aec29bebe1382df3e8e2a6c696","82bff42ff087bd5dc90a5d17609cc428b8688de92d1333073a839af5ad3c9cbd",{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"ff4bfe3c147eadc1b722a8ac9e4d350e66411b5c9022eabe5f4ada871c9adc41","impliedFormat":1},"9304a861c8673bee09e0f12de31773abbde503b02e59dfd74763ddec2e37cf05","63c6c82f0694cb0a4ddd5ad543015b87ca760133af20ab6dd6f89d6f4b035439",{"version":"0de1a7b1992a925458046470805b16edb4b65660fab170e5e2a575a53f6a33c3","signature":"89bebff43166bd355be3e50869fbd81d4e68d2b9f56352e06a6135d3bb113193"},{"version":"45df11ed45eb1c707ef59206bb0c6daddd348e59803bf26646ad0d85c00eb633","impliedFormat":99},{"version":"ae046314c0651da4a01e9e48ddf370ce9d22ad21f48962f25a12c1c09de9b01a","impliedFormat":99},{"version":"d0e136d6bf3c38be7af296b7e01912b6e8944a428ba7fd1e415a10acd9e687e8","impliedFormat":99},{"version":"7a685305685db7f9d2195ae629df44ae5888c13371a032ebe629a615a177a45b","impliedFormat":99},{"version":"026b28bf8f8c6f88e4e3aee7dd69f2523b91df8310bf6557d71c853144ec0720","impliedFormat":99},{"version":"4bc5ace72e3fcd7da9d8872af098c4b157ad8bd98b1996c097212884dc8e09cb","impliedFormat":99},{"version":"c3aa1b9d09adac7ac5e49aba8e8fa7114c2c842d46c2c5f51da53ec889787bac","impliedFormat":99},{"version":"7cd8fbd00f9608795145d427ff641d7abc485cd485d833ea1d9a90222ee73778","impliedFormat":99},{"version":"0f4f54801406a0a67455a9ad950bed9f4d2921fd66a91682f83a985086d60082","impliedFormat":99},{"version":"7c128cd80303077ca51f3b70b6103f5715048642f5b232cacc02f515ea2c0149","impliedFormat":99},{"version":"8c18a2ccca01e6ec6bb951c9a376d12b08112ee5237826caa913d85b4e3cadb5","impliedFormat":99},{"version":"935c01e5232775764203dce2944dfd69047e66765e6e521fa11d110b74aac76a","impliedFormat":99},{"version":"6aeb63cfffaa8f3274025ba556e6d90d9e90a0b5a664bdcd26fcb23486309efd","impliedFormat":99},{"version":"4f0d9edb39ca115f34bf49e6047d041fa9b589dbe5e652ccec0e61bcc4ceb6a5","impliedFormat":99},{"version":"6e5aa91099e2fe5d1d05f6f3100a90e5a5d9b8aea7b0ea6f4d05a0f192899a64","impliedFormat":99},{"version":"bd85cba544b37cd32e8d02b138c3a2a4075930d01146b3f5e33d713b39dafe77","impliedFormat":99},{"version":"04a7116aece3802e7ee128fed47d31cd18e5660825a62b42a62929f9508b936e","impliedFormat":99},{"version":"20ca05d62223bf6f117925ef8f9b9781e894cb146d30ac491e0763d34e53a5d0","impliedFormat":99},{"version":"4ba733d1a5ff0a0779b714468b13c9089f0d877e6fbd0147fac7c3af54c89fe0","impliedFormat":99},{"version":"697203f3f5a1fea90e40fe660360325090ab36e630dc9422a1909dd4faa2cacc","impliedFormat":99},{"version":"ad1226eba93a65cdccdb1b4f115d67c5469e12705dbe80139c2988d6b296d04d","impliedFormat":99},{"version":"4ea2c94c3a1c87029d10f11c209674d4c6a0c675a97503dc9668d2815ff6ea11","impliedFormat":99},{"version":"2c78675da824686c3541304a927c852a10611b38cdd99b7798e7d923429dc759","impliedFormat":99},{"version":"42ff88c837d04ae4aa8dad82b40613d4f23eddfa674862cf323a4765864f895f","impliedFormat":99},{"version":"94cfe3be66e4a6a1d52eaff0eb03bea21b4cded83428272c28feedfa5f9a152a","impliedFormat":99},{"version":"c2cf5eb33fc641dd321afd12c726ac3e753a81ab1618270ce6cd508f927989c7","impliedFormat":99},{"version":"a7f2f38cd72a96e7678555a1166a4488771b94e5a9c799d1c8943974ada483bd","impliedFormat":99},{"version":"c519327110a82e5eeaad683dc64f36994f19d9893fe69c4ea2b19d41b7e3e45b","impliedFormat":99},{"version":"fa525a25eaf81e3eaef7ca328c352bf4b38e1392ba468aeef117477a5dc42ea7","impliedFormat":99},{"version":"74a3f8babbd6269b402051673c8b255ad31db07539e37bc15aedcf6311fbb53c","impliedFormat":99},{"version":"73c4f628937d4e4a94d5af1c04bf57008a9d2c5f94a8fe6d9da8d51783069e15","impliedFormat":99},{"version":"f8e1fd0e462a1208e7c1e804fa87790112a6ba8c90ad3dc341d7c6430a8b79e1","impliedFormat":99},{"version":"1636e5ef72e41182b6a6a3e62595a3ff60c48f8b6fdb7373b2e7f7eb0f9485d7","impliedFormat":99},{"version":"6fbdecf06e73381e692ae1c2637a93fe2fa21f08e7cfebfac1cd2d50c6c6df6c","impliedFormat":99},{"version":"e437fb52a096addea9cf385b00cadc5fc34b8b8f6a7e63ef02b26cdc495478ab","impliedFormat":99},{"version":"75ad38105b8decc3c60ee068c8d76e3f546b4db1ca55255d0a509f45e4b52990","impliedFormat":99},{"version":"13ce682bb57f9df36d87418dba739412fd47a143f0846ea8a1eb579f85eeed5d","impliedFormat":99},{"version":"d6608a9dd5b11c6386446e415dc53f964f0b39641c161775de537bd964a338da","impliedFormat":99},{"version":"d45218d368df27abcfd0253d4b1287e1b954156f32ff263f31913bad81a80918","impliedFormat":99},{"version":"908ff133f5bddaf93168ffe284031d2ab177c510c2af0846c3a4d0a6e682f068","impliedFormat":99},{"version":"edd454b3d3813b5cc5d87c68ba3c982ad8ec4b22b6ebd5e03a4f6a06f56f6e98","impliedFormat":99},{"version":"4a4ca01f782fb474eb848689f2bf4051b4f9653827202441bd6f045ff46184d4","impliedFormat":99},{"version":"9c82c8b18a4f45b08629f90cd6744224d48c0a861ff938effd848aac2de13ac2","impliedFormat":99},{"version":"093b35cc4b96517fbc829848456a478c790ddb11a509ccc5f9d7b2359440d515","impliedFormat":99},{"version":"354299f6f5ee05fa42d3080c6c4dbbc741bc8cc2d8dd0cf2c8e8b40b76969745","impliedFormat":99},{"version":"ebdc3b72652592040fe10eaaa4ae53621460085eaf70be4b0e560fc30d459877","impliedFormat":99},{"version":"c35b0845639396a86ea5bf1276550dc0db6aadbbfb1d7145fe5974701065f99a","impliedFormat":99},{"version":"7c7dfb0cb2a27eb09a6e6b47566678a13e85de27c244d37d897ecb17399c24ea","impliedFormat":99},{"version":"f5f99c35649b9ad64c6b3dcdd8cfc7c9db3472d27eeb04156b15c17be0e30e5c","impliedFormat":99},{"version":"ecd5b86187507d8dd18df5c1dfdf466533fa0c219f1141874544cad4ee8181d0","impliedFormat":99},{"version":"2071e86ed68b085a66132335eed7e9efff7ba1d783faa2a8eb29767d7e172112","impliedFormat":99},{"version":"a7b87988741b9228692fdbca14817cd068873711c0cb5a65fee4ebcc15bbac05","impliedFormat":99},{"version":"b3f309aab87ae7d8c0b3db432480f23a023204fcd58c9ebba001b53aa3ec313b","impliedFormat":99},{"version":"84adfbf9e964f5f04770aa4f46596690bdc5e710ae67b6fca43ff95d5783bf17","impliedFormat":99},{"version":"d26caccf12d75c60d123c8572c7713d994c62fb4dec56a95bbfe08d8974759e2","impliedFormat":99},{"version":"7e7ddba1b969dd1dbf8c65b24a768a074b09fd704cdc11135215a3b8aaf9ae0f","impliedFormat":99},{"version":"ad42398997e18754aa0441a40d1c73e3a45adef0742ca4b4d4bdc335405f6735","impliedFormat":99},{"version":"dfd76997ada14f21f034dc5ec5bd14c640a2a2220ef73a1e1ce8fd80dc1b5803","impliedFormat":99},{"version":"c91b058ab74323c57dda1cbda7eb8cee56272002249a642deebbbd977c4a0baa","impliedFormat":99},{"version":"cb7f489960477f1f432a3389f691dc243ca075e87f20032a2866321dab05bae2","impliedFormat":99},{"version":"ca885b971dc0c8217ef8aca9f3879c3c2d53415c4dfbe457748045160f6e5205","impliedFormat":99},{"version":"7c1179be1df21576772967091f92a876506420c18607a4186c85de422c5794cb","impliedFormat":99},{"version":"13c2e1798a144acb07b57bc6b66d4eadf6e79f1bbd72472357d303e7b794842a","impliedFormat":99},{"version":"516f5feb685e00a96e4d4c148f9f71f0c388bdc223350c76b7fb97a2750d4d98","impliedFormat":99},{"version":"24c626960973658ff450798d90b9696c53271c2d60192ce73306bd4298dcbd1b","impliedFormat":99},{"version":"7c7a960997d3470573faaaa089e6effd21cd6233d97ba7245974b4adf46597fd","impliedFormat":99},{"version":"560ad98415f922fd0bbe0371224646932d43d3719a5f2b4375817dc3704cb77b","impliedFormat":99},{"version":"69a24ce73bd1a72860582848f778a9404611a2cb05adeb2313c7d13bbc8fbad1","impliedFormat":99},{"version":"017907864b01ae728f5be6be99ea7632e68b2a35c2d7c9606bde20f85f10f838","impliedFormat":99},{"version":"a73fe468accce86f9cd30cb927ae0890fc56e0f5b895bdaa1883a2ea00f2ac52","impliedFormat":99},{"version":"22f98eae982b7f0d26d3dd7849210e033dc1992f594d87c6fe30075eb94b7a24","impliedFormat":99},{"version":"ec47b34311c3c799d1c90a3dcac1651ed23948c064aca4f0617fa253e648ab15","impliedFormat":99},{"version":"761efac4dfd849586e4fe49fc6cda2aba8e708fa8e4eb19ae85373084cba0d51","impliedFormat":99},{"version":"899ed4016a7a722a6224e78139286f1ab7d05f79be50af0a6492b95170e56fab","impliedFormat":99},{"version":"965bfde0433a808a389b80a8e45b717cd2d5a3a0cdf418707cfda3046e33fa5e","impliedFormat":99},{"version":"1d107f6f5f233d9a19c67d423cfdd3cb3aec49f41e338ad09a50cab2a1c94bd2","impliedFormat":99},{"version":"b011f71b5d21579da9f868e56acf3887051fc4027cc7cde7317facb232ed3e95","impliedFormat":99},{"version":"7714308befeeb34cbc1d6715bb650d05e2b4e0516db9e58ef4c399e462d222b1","impliedFormat":99},{"version":"3098f0794f8cecb813ede63e9484a44bb75926c37c9983efc85c9994ebc6e9a6","impliedFormat":99},{"version":"eb8a258495db43e8e4641def32bbbee1b73ecdc680407f948543bd9950668293","impliedFormat":99},{"version":"aa7a83f4acf2686925511ecc32d148062c02984068d563c44f00835fee5b164f","impliedFormat":99},{"version":"d4632bbd2d2afbb1b75163dc7cabab5cc218c2fa933cb8f7d5b7089255faa6fd","impliedFormat":99},{"version":"0cf4827f19c749c5befed9585862c6196a4a5b3d889d20e0f5f4bdb6f734dcc7","impliedFormat":99},{"version":"14d3c7499d1759af5c78eec4f26a6f5b85bdd5b0e41ef3f5e6e813f1ae88c06a","impliedFormat":99},{"version":"0082935dc2cb31cd632eaa6bbdec17f1a9142652e38ede025c0ffab00c50bac4","impliedFormat":99},{"version":"0df7497ada3a4f6459420803ecf7e555f1ad1e7bd43c1e17bdafbc34e19d7162","impliedFormat":99},{"version":"5cccc8d1dd17c789bb6baba06a035e98e378a80d133da3071045c9901bee0094","impliedFormat":99},{"version":"c8a40bb3df60346af02e8d786473985ba53b716bc7caefd21ab838f025ec103b","affectsGlobalScope":true,"impliedFormat":1},{"version":"64da9a17f7cb5d84731607aed8493e4550a3e613cc7b880c87ce82b209d66b96","impliedFormat":99},{"version":"c00cdbfab0c58bb06a3d3931d912428f47925e7f913d8024437ca26a120e4140","impliedFormat":99},{"version":"4ca5b927a7e047f0a0974c7daaeb882230ac08ba3fc165c8e63ddcbd10da5261","impliedFormat":99},{"version":"42fad467293aeebba6546f568a391953df8808086a2b11722b981464c4b93696","impliedFormat":99},{"version":"6d056661e4b636cc04e36c36b24a4eb692499b21fe0b18cb81f8bb655d7a3930","impliedFormat":99},{"version":"e71c5f5440bea23cee6fa272d088930e69694c09ccb89f8811b097feb7c078dc","impliedFormat":99},{"version":"d5de759fa4dc835dd077e4187cb2ff88443990f91dcd606c2ce2a2add88669c2","impliedFormat":99},{"version":"1f957c8657bb868e8cb92e46eac8c8b1877a96708e962015a1ed47fd42c697f6","impliedFormat":99},{"version":"217800577a2c9a7232e5a9d1abd1c1836acbb004e7522a5261299aa867713f96","impliedFormat":99},{"version":"60981ae7c2a8926f7855d8068c42e05a3b1959f0bb795a8bb9773c912a9a6f16","impliedFormat":99},{"version":"4a6de5821d23f5e1781c567ab6550e5357b2c2ae3e8813a277062512f73d4a28","impliedFormat":99},{"version":"618b5aa1f8b9791938f8033f1855238774b555f9dd35f0b8a5443cc066721605","impliedFormat":99},{"version":"760064e691b40768713d8d4d55c8516c402670fed62d189a67d9c9b11ca64cb6","impliedFormat":99},{"version":"f8e6fe15e31c1e050812cecbfa023536971fb2f7766399f8a2d9390d4ab47b5e","impliedFormat":99},{"version":"12530b232edd0c5c33e360bb518fdd2f229100df523f0695de92b97cb97fb7cb","impliedFormat":99},{"version":"68617a52d0596e488c88549c000e964c5f6a241e5361095b2c6203586689b1f3","impliedFormat":99},{"version":"8d4a70e05b1f8450f5fb8997e5bfc336dd0baec3f2c8117f6f260d4eb68de0ac","impliedFormat":99},{"version":"8fa060b55694a9427afa2346181d988302de37181cac7df6e29f252b3741164c","impliedFormat":99},{"version":"e61ce3bbfe37669692af8ac289869baa7b9d01b7e260e5cd0294095a4f6c29a2","impliedFormat":99},{"version":"10f60c4f46231065e5a4815651300d69925049b6d654c141eea7bc3410fa5b4d","impliedFormat":99},{"version":"7b91f1ef3b248dbe1bd3ae0f1b86592d87b66c900b58afe025f9981b961df57b","impliedFormat":99},{"version":"8cc3ab398412f20af6fdd1d307176f933f3a4a6b7eeab11388d3a084b811bec8","impliedFormat":99},{"version":"696116447a588ebeff9d158672b83ce1d26b2be7ffb29acee5925b75c1e29ed4","impliedFormat":99},{"version":"8ca97507cc241216ed30a5c73091a6dd4818dc9cf6dbd3bdab039e40f474202e","impliedFormat":99},{"version":"5676038845e4209868d017df816419f7492d62530eb41bccc2de6783f3df2598","impliedFormat":99},{"version":"cdc154f5e44aa28c4f948ddce70d8cc57acd0992809549761b2f352c409e03b4","impliedFormat":99},{"version":"d7697f915c61a7f7ee03922e9f4e2dd3ef8122a3bcdafc1d7824f2c664b67ad0","impliedFormat":99},{"version":"8ae0357ed41745154782684b1cd3a8b9c84dc92935348d3711b8c949472d6398","impliedFormat":99},{"version":"ece19f08fb075c84c2e22fee2af1991bd2f67f60157b72a2993dc6d1087a7e80","impliedFormat":99},{"version":"4804c3e9ab498d31144a0c9b95defba9f913a4326063d19d8583eb4ba9708a15","impliedFormat":99},{"version":"f7292171fc81d858880863eeea33c85f9522909b6929559f780b5ed697c99020","impliedFormat":99},{"version":"b90f14bca14cdbdd60dc83c451aca97e8df63c8eb8a158a9ed84de4bfb4cad76","impliedFormat":99},{"version":"654fac848dea765dcd6fb3456ab083d6ab20a91b78721928a8d0d691387ae8c2","impliedFormat":99},{"version":"daf3cb7fbb067540163df0a3421e791ebde6bd2e699aad4cdb13366871cb7196","impliedFormat":99},{"version":"98ba4768c426848773fb4a39203aac92e6baa545d93510665cdf207454d0811c","impliedFormat":99},{"version":"f65116ea54fd65813a0d9695249ceaa716487c932247e4aede3e2e3ad3d07316","impliedFormat":99},{"version":"99484c7a277c488a16c49ac1affe465e4fbb5e4d57b8c2190092c5d7b4fe6fca","impliedFormat":99},{"version":"459576a2bc7f798ca767ded6a79cc639a26cb797e5b0c417d0f05eb46f595019","impliedFormat":99},{"version":"0f1ea4f6570d745ee2dfa784baa306ae15c35ff7742566ac5ccc1a893af9a1ba","impliedFormat":99},{"version":"06e727ca4d41b4f549f875d7999d940a392058b1b579846441351ff011a63a31","impliedFormat":99},{"version":"d7e8d8a15b4fdd368720cb7a1ad3e740e2f25b9a5ac24c26839921b8d0b7134b","impliedFormat":99},{"version":"d94acd15b4a3517523756dfeabcb7b4fb8ee853bba680d892ccfd3df4c81edc1","impliedFormat":99},{"version":"0f65f9b61383ffcfa1a409da90c35741cd81ece1a2dc6f2ebd094d81599bc5f6","impliedFormat":99},{"version":"9abd03a84d5473e66b038270dbeae266129ab97261d348a5fbd32ec876161a85","impliedFormat":99},{"version":"884f8073c4687a2058be4f15a8f3d8ad613864a4f2d637bf8523fa52b32cf93f","impliedFormat":99},{"version":"693c4ea033e1d8cb4968972024b972aed022d155a338d67425381446dcea5491","impliedFormat":99},{"version":"e3ac1db377991a0bea76cfcfd60959f9ba94878cf99d141222c8f11470f540ff","impliedFormat":99},{"version":"b6024c6222886b95cb29ab236155a98f8e5dc41151233781815e81a83debf67b","impliedFormat":99},{"version":"94dab3752006a2cd2726462342f1775ef18ff4986404d016d317fe79a9d0a14c","impliedFormat":99},{"version":"727b3a462015bbed74b520861445761ebaecf94e09d95bbf59dfcf22afaccae9","impliedFormat":99},{"version":"2c0300921d8d04b21353c94a8f50a2b6c902feccd1303b6f136bedbb2cec5ed1","impliedFormat":99},{"version":"d496217c7f38f218fc162e8f3e6ed611343aa65615f730f82c494dee6c892bc0","impliedFormat":99},{"version":"282ed4ab5b5c4759d5c917c51a5b2f03ca1df4072275b6bccb936cf60078e973","impliedFormat":99},{"version":"2c96813e14e7edcd8e846f009b24fb1bd842b90e2dcd85481136e52588de7982","impliedFormat":99},{"version":"aa70da8072bb8b6e8fae35c7d394d543be8e5c946dad666225a3475010fd2bf0","impliedFormat":99},{"version":"d2c35cb9836cae1899ae9e7e114410dc128bcff4a79cc26318db285699e0223a","impliedFormat":99},{"version":"f89fbb50fd3736e09b418a2e66b98ff9a04820259856afe54bc67977e1acd05b","impliedFormat":99},{"version":"4c76aceec7002f299d9a57ec8e6623f3573bea208b1ea51cc5ea03bf140adad4","impliedFormat":99},{"version":"a0f217b01453d43058cea514325ac8bd3ac3a184265314429eec8059c62824b6","impliedFormat":99},{"version":"e06bc5a68917139f31f323293f575cf1eb75231ac23ac1b95341079364ef1873","impliedFormat":99},{"version":"31a4b6d0c23346d5fb30b52bd3a8f83113fc928ee6474338d5571361943d58ea","impliedFormat":99},{"version":"aecd83ca7059d21a33fb7ed01dfa06a36c545698dbe0017073dba45532a8487d","impliedFormat":99},{"version":"7fb874c17f3c769961d1b07b6bb0ef07b3ca3d49da344726d8b69608997ef190","impliedFormat":99},{"version":"979e969f86456425e505f6054f5d299f848223d70770a5283fa7c405020b47e1","impliedFormat":99},{"version":"2ad6c5849a68263e12b9f246ffd09b4713cef96d617618076adbe2f7907f3d12","impliedFormat":99},{"version":"acd7f9268858029bcec5eba752515b9351d4435b21f1956461242c706dcc0cf9","impliedFormat":99},{"version":"53e2856f8644978742fae88b3c7f570ab509dc4d13288b3912a4446993fa3bc7","impliedFormat":99},{"version":"ea2b6112bfd326f1075896bf76c9108dfd08ccbae2482ba31f68ca43f0b59ca5","impliedFormat":99},{"version":"3f9368aa15d0cc227a3af7af3e3df431dadf0f7cd9897fcc54507f7eb68761cc","impliedFormat":99},{"version":"0f2d4be859066fc3ea8d04b583cd0774e1f9dce7f60b9890bcc0a10efb9fac33","impliedFormat":99},{"version":"ac09b9131c553c189311d9e94d3853b7942d0097925304fe043220a893701ce9","impliedFormat":99},{"version":"f1b34ea3d64f73fc79ce1f312589134db27aa78ef9e156a8f14f89f768e800ac","impliedFormat":99},{"version":"873da6c837a1ee62b5f9b286845be06dc887290a75c553bed7f431107d25a3b6","impliedFormat":99},{"version":"b2abee3c001c024d4e552c4a3319bf3fcc94a1f48bb0d21f5d300d9b4920bde9","impliedFormat":99},{"version":"f9740d044306830442cac761b593538117f46c5ea57a8dc6d61f0bee12e971b6","impliedFormat":99},{"version":"7cf786964e26f0e2c3a904f93f6e31609e2636723df8c1ce248d39b55055c89f","impliedFormat":99},{"version":"41c6aff52e4289763ea30f0849b712437aaeb420c8448aeb8047ee2eca4549f4","impliedFormat":99},{"version":"f5db101f7d90f614627bcab5f8d06d9ccd144a1735b475637940c54097786b67","impliedFormat":99},{"version":"8c575a8e1b6032e576577f28d74066f73aefa7a35d741d0015be36956bbc30aa","impliedFormat":99},{"version":"1989cb4fb2174c56b15f8b10d18ecb0c053e7b39f94582581d69767d7bfb9b32","impliedFormat":99},{"version":"7d90add559ac0a060d621c722127b9a5880a6ab4c15d512a91c57a7b14a073ca","impliedFormat":99},{"version":"47921880701610e8d8a5930d0c9ea03ee9c13773e6665f4ffc8378d5f8c8c168","impliedFormat":99},{"version":"41cbf6c58f2f4e1e5ee95a829b3f193f83952385fa303062f648040a314f939b","impliedFormat":99},{"version":"bb11cd0d046d21d4ae4a28fc4b0eb5d9336a728f9bd489807a6a313142903bc1","impliedFormat":99},{"version":"a96d6463ab2a5a4cf31b01946f1b0929dc3f8be9f28c7c43da29a9e6b7649db1","impliedFormat":99},{"version":"ec43d6b21fd1ed5a1afeb779ceba99e80fe010458bb0a67d9ef301426b1929e5","impliedFormat":99},{"version":"105bb5317c5212d56f82fd9730322b87f4ad8aea2927ef7684341afad050f49b","impliedFormat":99},{"version":"79ffce57ab318282b29bceb505812c490957124a3a96c7d280a342488b0859bf","impliedFormat":99},{"version":"3631657afc1d7e451e25bd3c2eb7444417b75330963dde464708df353778396c","impliedFormat":99},{"version":"c4b46086b44bb8816d4a995654c00f64b3601eb50a163f2bba4dfe48ae6c6b91","impliedFormat":99},{"version":"32e670209322bd3692e8fc884c63002f6bd565e83f62f1fd23c46729aa335d1b","impliedFormat":99},{"version":"97717d35deb9f6a6127f3abff60c9af080ab0ccba60aa06a5a3486a374747573","impliedFormat":99},{"version":"4d70c89489fdef067b0819f22eec5fd0323a8b488d93075cb7953bbfc636e03e","impliedFormat":99},{"version":"233dc7f3ea55d2375b32c5c19034babec8e1496dc73784f9b091629a5287f2fe","impliedFormat":99},{"version":"e3fbf3f3e99083f8fc21bbde7677c3b1cad0c730fe231599a69911aa66487d01","impliedFormat":99},{"version":"59110c7d72a09bacde4a80f4ba95d9990b352911f0e4ea09bf766804f8d3e44b","impliedFormat":99},{"version":"3d827d1dd689311e57a98e476b3451445d39e573f4855ac265b7ec1747075c4f","impliedFormat":99},{"version":"e0669b0e7c953962035bb39e7fdfd5cc8fc3d9a666a8b167b78417355609be01","impliedFormat":99},{"version":"8495eef8be427c71a2d574e3ead06c537a9a6d437dd669e6786dab3df009f125","impliedFormat":99},{"version":"15741df16deef60b197560d3cfe45e6c1eff69fa7b85a861e3d8aa8a26683b83","impliedFormat":99},{"version":"c1fc3a728bc95e5ae7dbbb3c650247e77bdeccd7c246f76ca917aadc94a8fba7","impliedFormat":99},{"version":"bb77b52bead9b75d7173bec685e5e2136f6c3f226cedae736db63a44f69db679","impliedFormat":99},{"version":"b3f7783d4977af919bdb8db798fe185908083c6f4bd3b07460967c8e093f7312","impliedFormat":99},{"version":"5a6bae49831f960e7f0bc66f49b2c40077b136d9573871f865507fde09580436","impliedFormat":99},{"version":"c8366dba8df08ef5a83995e10faea3ef86d81cd656b18e89e32e043aa7b0f7f1","impliedFormat":99},{"version":"e6295124f95b686a16233c1031d04cd971f9685e3416631f463bde75a5c86ce7","impliedFormat":99},{"version":"00c38bd1fe89fed8d4e8502db4f896aef7415b097ac061c2d65f2b539b6df6a7","impliedFormat":99},{"version":"94a2d7c15538d8e83415299f17fd00ab88c594b6a0a40be1e26c99febbab45f6","impliedFormat":99},{"version":"20bbd68ac2d2e7cdf9f60816ba9b378e13c07f0fdafccf9ae5833c876c6f51bc","impliedFormat":99},{"version":"df109d2490b693bd75105efaae08738ab84102bfdb2eee2372e9e3f369ec5fc2","impliedFormat":99},{"version":"9d5c684e68509dccdc68d5778dd58873138b299cf9f16a16ea9911f04eddce43","impliedFormat":99},{"version":"d411ba0bcd6a51485be855a01cb95f79649fa90039b4f235ba8481dc68edae3e","impliedFormat":99},{"version":"8f47a2e6bd2914f74471a693fc3389f243a97367d8bdd920f27198b6018872ad","impliedFormat":99},{"version":"d6e125557820886c2add872cfb3e9502d4113fd1dd22a1f76ded1f439837f119","impliedFormat":99},{"version":"6e688e8aeba98c268b195f80355a8d163d87ac135ad03c708ceda608e6e269b2","impliedFormat":99},{"version":"802a6978c1b38822934ce43a3505e13b555584848c50bc5db9deb2e896c0940e","impliedFormat":99},{"version":"f502c7d829f5774109007ec2262c23efc941dd1ce42acc140f293a7c5ccfd25b","impliedFormat":99},{"version":"af3444bd00030bae3bef81569f8703ecddc2e569cb6b728ec045f0d73d47572b","impliedFormat":99},{"version":"53102281f8a153bb051e0223a8dc51ff9c4cf92da127d91e3f60e74b4e8f41ca","impliedFormat":99},{"version":"e402e111fadcd36fa26ea1ad74f3defd6ef478f6d278a69c547e664b57770392","impliedFormat":99},{"version":"bf8f4b3b372e92a4e4942ce7f872b2b1e1bd1d3f8698af21627db2dee0dda813","impliedFormat":99},{"version":"0ff08be8d55c47d19f3d6bd79110a2ac67c6c72858250710ba2b689a74149ee2","impliedFormat":99},{"version":"77676a7a58c79c467b6afdb39bed7261a8d3ba510e9fd9b4dbb84a71dd947df3","impliedFormat":99},{"version":"dad5c38d723d08fc0134279b90fac87441ee99b71b0d30814b86954e0111d504","impliedFormat":99},{"version":"dd7510a9a4d30db5ac6418ef1d5381202c6b42c550efeb5fb24dd663eac3f6a2","impliedFormat":99},{"version":"cef653b7f2115c8e2a9b6558bf9a083dbcc37ce8fb6bae0e48cde3b92fdaacb2","impliedFormat":99},{"version":"bb544ec93eab70a6c769cd69c0912742da7c2a8bed7d570e79b8af046a9ca556","impliedFormat":99},{"version":"532bd533a1921eedb9b39fa3559594ab783233867021a7a911db00be5d42fe7a","impliedFormat":99},{"version":"c85f04a8ff65051d2cffc664baa83b70583bd72b9811a50c77f880968c1188ea","impliedFormat":99},{"version":"ad48586787d5e217f4fcc229e3c3d8de8aa12979fdf1f186134e3684d56577ac","impliedFormat":99},{"version":"229d6bca5145c86846793cb3166c83abb256cfdb5c425f25ada8eee49c993e54","impliedFormat":99},{"version":"292856f47dad178fe1cb3401554428b3b0157369a8fa52792587fd2bd06fcbec","impliedFormat":99},{"version":"c7d9ac6cbda9b080656b859f3a05e1b5efa14f82aa7e0c7821b4ba1e129d6240","impliedFormat":99},{"version":"b18c7a06ac51bb77d1baaf6691ea048d4d77445475692d797bf05f777c20a580","impliedFormat":99},{"version":"b8562e5aefa86c069ec1c61dff56ef0492e9fbd731cbcdd4d7fce28a8644e9f6","impliedFormat":99},{"version":"69722e1a7d3aebbbb9d057ff25ae3667abf15218c14e7d8685ddcd8ed64686e3","impliedFormat":99},{"version":"dd6c7d6abb025e7494d02fa9f118af4a5ab0217e03ae54dd836f1160cb7a9201","impliedFormat":99},{"version":"0ec5d4491041a73727e6a7233a84ddf413176335bf81bd57edcb15ad0956a848","impliedFormat":99},{"version":"440c9aba92c41b63d718656bd3758f8f98619dbe827448e47601faa51e7a42fa","impliedFormat":99},{"version":"57410000658f90295210978d18fe2d488daa49287f21d160ba119c8909ff66c5","impliedFormat":99},{"version":"d9cf429fa9667112f53e9bb67bb7b32eeb3697f524d01b9781b65247f1733da4","impliedFormat":99},{"version":"6b8a1a0ee3ab56f43f641206b95e53bfa8a53e6af056415bf7bbf58568cefc83","impliedFormat":99},{"version":"701e25008d343bdd67e02c0ccdce4c2ab41d56645bff646b5dc25e4145e77a3a","impliedFormat":99},{"version":"7a891af63bf06f2be51ed3a67fa974a324d7b917f7b1d10f323ed508a6662354","impliedFormat":99},{"version":"efa0e3dff0199f00eaeb36925776e62419538f7263ec77a56d5612ac5abe9ee2","impliedFormat":99},{"version":"ae6114539394eed7b6305a6d788cb6d2fd94e256d7582f5111a1972ee5a1c455","impliedFormat":99},{"version":"8a6522f0bcdef882a35d055b1dda5c9c8c9c67a5e374c95dcb8a454eb100303e","impliedFormat":99},{"version":"3563a343e025cb849b94da85e8455dd89064dee213bc97bbed559f83d74c98de","impliedFormat":99},{"version":"4dd376ba5ca486993df4c440265bfc95be200fd2ed7e56a26544dcd3f57a9298","impliedFormat":99},{"version":"e67fbc9a974d14cab74cb47b4bed04205886bf534c7e2f17ecb8f7789d297b1c","impliedFormat":99},{"version":"82d76af0a89cd5eb4338771a2a5b27f3cbc689b22be0b840de75be4cfc61f864","impliedFormat":99},{"version":"24e856aec3b5c4228ffed866dcd8e7e692aa86eccaecc4fa8205fadd9737d1af","impliedFormat":99},{"version":"fe395a24df9ffd344cb825575d4b35c1cf69275208c0f99517c715bd7d08ff79","impliedFormat":99},{"version":"39e8edcbd5ac35c6cfdf2b1a794a9693a461a54efb2a475ab7fc08ab13504e26","impliedFormat":99},{"version":"12012b6c28d09a6f1d86b2a30213a92a9e92ad9ee573f94c92a8b237b6422bb7","impliedFormat":99},{"version":"8ee28204ddb2be7d6dfb68891493f654cbf10f5e1667bd33bd62920d9eb9e164","impliedFormat":99},{"version":"b09669391dd3312b8a52242af7823a3c44b50c7dcdc216db8da88b679af46574","impliedFormat":99},{"version":"b71e7f69e72d51d44ad171e6e93aedc2c33c339dab5fa2656e7b1ee5ba19b2ad","impliedFormat":99},{"version":"d17f54b297c4a0ba7be1621b4d696ef657764e3acddcc8380e9bfc66eeb324a3","impliedFormat":99},{"version":"9c61e1d1777ef5ec76a62eb9c66ebc0c1ee5bf1d1037767208693cc3fe61bf9a","impliedFormat":99},{"version":"a715a2786c285a9e27ea2bbaa2ed249d3017e7139782f5ebb8eeedb777b26926","impliedFormat":99},"d7ff913ea354b6e393442e5cf479139db2e33e2665ec72d76949d6cd131449d1",{"version":"49396b69732db938647a2fe60c5fdc99e82db083d8f50f2736dc3926c02d7064","impliedFormat":99},{"version":"515570d41e2a659c169d1ac88ec3a56559a16bf42f69479e08318f061a0a51ad","signature":"b80eda96e94fc4b4e05dca8a408f891592de403b8bb09c32570d3ecd0b66f901"},"3e5855e7f9fdf3e35d6ab84b11b4b18ce37cdf58816505ec8da296c7f109151c","994eed596941de914f3f17a7746df6601f29e3443e9e55f2a48a2106fa0c390a","7bca22fdbe1981aa3e20d31b5b3c2283d813f7b24dbb3aeb722e95e1aef49d32",{"version":"eee89e0f13f68b77c2bff15ce52af6c7a5d2933553b51137c41ee2413be19195","impliedFormat":1},{"version":"09a64e07695f098c539c74ddd1ec94439cce47b75c42910328d393bb039b0d62","impliedFormat":99},{"version":"2a5f7463977c7f86b395bf76f55abaef3f38771e14ff0de8dfd19a7bfa5bf99e","impliedFormat":99},{"version":"0feb4e9ef245e59af0fae6238a4b17213654cccc5111e5823e96aef0d015db34","impliedFormat":99},{"version":"aec199d94cc5a06a838cdd110b2bf8d31bedde4447fdee2d0154f6d49e578dbb","impliedFormat":99},{"version":"f89fdcc14833a9d672efe032c50be46da50642308dde3abb75a66f9301258cb8","impliedFormat":99},{"version":"c695807523750c85e1fe89c233bc953fc8b8924df2a42963c134a83b8b94b874","impliedFormat":99},{"version":"26301b0b384ea59d5429128dda4bbc586960b084799264dbf798e3d9e5d3a3f1","impliedFormat":99},{"version":"d0ca877bd5c8c66387869b794efd8a1469bd31488a866aceb7e600611a546824","impliedFormat":99},{"version":"9118596178e6e467cd6dcdadd4b04ec093a4bcc6fdc2b3525e537936ac09df46","impliedFormat":99},{"version":"5547e9e8755104f201c20cd2c91f978a17dd9caeacaeb7980b233443df303017","impliedFormat":99},{"version":"8a804777ed7b8347f2c520a0bbcf640853156dd65ffc2af845066713cfe8f0ba","impliedFormat":99},{"version":"a71b1bd6159cf35e5206a9a706c6ef49e606b29e825c2d8ca98bedd338ad8974","impliedFormat":99},{"version":"4c90a543d0d336091da2e0c49e8530923025dbcb0d3e5d8b8a658061d207fabd","impliedFormat":99},{"version":"90c438c107c9beff26334d43e4fbc344c7c14ff1d7462ffa986af4c9a0b5d70d","impliedFormat":99},{"version":"08de8f1d972b833791a9782eaee39816eab1138c53319ffcb90ba9defefef6a1","impliedFormat":99},{"version":"f6bac2cf3c5d6043e24f74e200c0ddf6e4dff6e37e0be075db3f474af5ecf7d7","impliedFormat":99},{"version":"7d378b5b9bb7fca01252c3287887f6c315e9b7516c7cdc84cc62953f158d1ba6","impliedFormat":99},{"version":"a0c4b9ee5e73c99740fd0250622bae6835c97330b40f144df6dd0d3d146f38d6","impliedFormat":99},{"version":"c4e55685af7d2eae6c7e945f1a7616a9ec57f6dcdb119c062d91e888ebc29fbb","impliedFormat":99},{"version":"f4eb4d5a36bf1979c196e951dcb20867b1227da80dc2e71be4b5325e3887974d","impliedFormat":99},{"version":"97a51fa3169e333c5aec82f2bfc559e1a14cfe9a6e7b0c3684edbce0481e302c","impliedFormat":99},{"version":"830f6c747d9c21982eb63bb9cca6f8acc9ea12ca825100e848be802ae2ca3275","impliedFormat":99},{"version":"48c8d476fe45690bd3a4bd6357e0572a769483df73118bbbbd2f14531fca7474","impliedFormat":99},{"version":"d1daa7d45aaa263e3ab30ea64e91b19fd7b6a3f0eb0f7e755d497dcbe32dd5ca","impliedFormat":99},{"version":"a4650a1b9921860fd535d9c1b9fb5261fcdea82646d2bcf3f1bb434b0598d77d","impliedFormat":99},{"version":"579b3f09242a0efa3e4b56e077eb5b946280a56dca945d6103c7ef5e14ea447a","impliedFormat":99},{"version":"0108fcdefca63b7943dbd02c6a8452e67c6ec1bfa87e9269f6edc23efeb64ecb","impliedFormat":99},{"version":"48844f9b1ec9ff5da8cdb53184ef30c7ce682be3bb3f609efefbb63d46943fda","impliedFormat":99},{"version":"3e0a5a60b0e9498c4acafe3f24dc72927c959f41dfb37ccc8f9395899ad1d36d","impliedFormat":99},{"version":"1d083ca29e6e874200bab83efd40e5d85c3d4da21b46b8b00799ba03e0f4fb86","impliedFormat":99},{"version":"c5252c1ba8671a29a23e18a92cbad9936a6214e9fab27c020fcdf7d06dd93877","impliedFormat":99},{"version":"bfb2c74ba09559b9ac6b0c21012a72e124c399e7d12eefd0df801acdcaef359d","impliedFormat":99},{"version":"ef1ce13d614f887ac1a4ce2a4a282c2582dc7e321477e87fb15564c5d7755dd5","impliedFormat":99},{"version":"09468947dd33634131cc26f15b0781fc092f74092d9d4835b166167fa54b6c68","impliedFormat":99},{"version":"6864a330077ad818c88de891eb39f9b53904dd37c1827d4255d9ef48b412b90e","impliedFormat":99},{"version":"b89f509dbd2925c8f8baf16bf8c36e77c9b5cbfb1d15541af520ff71276ac8e8","impliedFormat":99},{"version":"4c664b74d638a856ac1b9ce3204e8aedac18a106285510dc12fbdb76cc0fa6b8","impliedFormat":99},{"version":"feacd51331779ef25fb3a83e0c8ccfdc9237a404d357292da5991e63eae2b3c4","impliedFormat":99},{"version":"bc1fe53dc37bd09098aa6805f42c962deebed282688cb549be450aa87cd27124","impliedFormat":99},{"version":"c8076885c7373ccab8c129562723a18e604057ba87c43d0c743759adc023ded8","impliedFormat":99},{"version":"0a9fcfb8fa7ad7b6f62182e153239def6cfc4bdbcd74b97dbf32338a514f1861","impliedFormat":99},{"version":"b11b7538c3787ecf925d69f95c89be1dc445c0fdad3742a54dc14a6eab41ba19","impliedFormat":99},{"version":"4aa24ae79c1523df6c5e7660b3b41c75cf9f82908faf65d66c86c3cab4390d9a","impliedFormat":99},{"version":"a3bfbd4bae528d331ad8cc5f2e21f8c7e109e58df27089e8545ecd9722899a86","impliedFormat":99},{"version":"56b96c036271fd724eb118033b0743e43e557ea8016377b213232567765a0f3d","impliedFormat":99},{"version":"0859bb0abe66d4154c0212e6525e0f02376c4d5cbae84e81b12038707fc71141","impliedFormat":99},{"version":"a0132bc5ad65123d511297552364f2e7b387601f50180f7052f171d81c53dbdb","impliedFormat":99},{"version":"b05874d2e7d215ad1be2e8d0e07fbc646ca3fc5f69980b585656cc4cddf7a9a8","impliedFormat":99},{"version":"be6c7d82aa17f132063ad54cb517bc26b7169a33d3ffaf16f0df350816195c73","impliedFormat":99},{"version":"ec273e29d916d26c4231c3a9b8efb3ddb4ef448243e0bc8919081ed8f057023e","impliedFormat":99},{"version":"18f439f3f9f20f2fb85891bfbd9b21ce966a6fe87167e293109624ee52091a54","impliedFormat":99},{"version":"c4f7102d86e9c64a3f340dbc5e46a6ee01009ab016d2b31008762a87c7539148","impliedFormat":99},{"version":"b8405064c7aed9c17e271f3dfd4c14ce7363363b3f9f01d307e832c3114e9dc3","impliedFormat":99},{"version":"7ba76b8784ce8d238eca83fbefb5533be10b76fe0ed4595491dff2ce682ec1e8","impliedFormat":99},{"version":"46bba6412696454f65b7dbaa75eea9dd12cce24de32b208c3aef5faabf91f3d3","impliedFormat":99},{"version":"290dce045d8d1b4c6ad7706815dc253e0ca693778313abb7ad2d9adda8b7f163","impliedFormat":99},{"version":"ff42c278bdacf9174ad49066cf0f6765d16a1935b25d86ff7a8bd7d2a8438112","impliedFormat":99},{"version":"e76b77b319d694a0a6eaa2083bfff21bc11a95f13c439dda60607d8d66dcec47","impliedFormat":99},{"version":"86e21d5b418aeb170b7db544840210588b7bc31553b491970a055739252308b0","impliedFormat":99},{"version":"c10f663d4a41424c72900f03ef3d0d191e2464896f4fc4c7745e1a01c0f6f7b8","impliedFormat":99},{"version":"381ffa4ec16bbd0d3a0e68f7aa1d8ea0e9a65f415b80dcaf8f2a663f087349ea","impliedFormat":99},{"version":"7316f7b95b1c3143449f5d09d1bf15bc17177a76e628deef15cca33d28466e25","impliedFormat":99},{"version":"245e233d445ee7ff5b893e58b71718afcb6a357825918f05026cdcd9d4165b65","impliedFormat":99},{"version":"bb6c9482c954bb6e8014c58fec79ead37580251e97051f734ffd5481ba8e01a2","impliedFormat":99},{"version":"1ff812c7efab1bf13c08728cc931d0d09dc14f60d70c88c6d5777ca54a1c1680","impliedFormat":99},{"version":"f8655ce25757435f34b1c651cef25a52085950874ef3fe22957a70718f7fc28f","impliedFormat":99},{"version":"e3da491b5c7012af73c373e49a04a1632e3e29b0f2bb24a500440cf5ce113ce2","impliedFormat":99},{"version":"088aaeafe8f56b0b7bf291afcf6530541e8fcd7605b5581fe22a654cfac57c4a","impliedFormat":99},{"version":"c8e88e701934b4224e4722bfb7ef7765435f452c5b055bbe83792e5e532ed9cb","impliedFormat":99},{"version":"4e81f64ef9fa7f12f30f01e998c0165850d713d326918e1c3212cebf4201c069","impliedFormat":99},{"version":"ab11bf9883f2ab0f04272306909891261e64bae8f6279b671e5b4c5a4b9bfce5","impliedFormat":99},{"version":"ae132b5055593d761f74b49a9444e8cb9a45377dec32b3742ec3ecd84598b690","impliedFormat":99},{"version":"9042dccf84a16c6a1c16c30e4b3245e37ef0456996944c818bf19e9f9f63b4e9","impliedFormat":99},{"version":"d6aef76bdf86d5396d715da06f69ca7ba3c059dbeb04523fcbf8f61a99c9145f","impliedFormat":99},{"version":"6c2cf426380bd66a8d0bf8e986204267f053ec3d78fa7b0cdc9fc6be20b0afd6","impliedFormat":99},{"version":"ecf28ce07ad122da187dcec8f51f298526faa935c38df640d50735e2a730f1fe","impliedFormat":99},{"version":"5aa0a922b9327306e9349c91e80d598ea7afe06ac23f42af8dad922a753b36a0","impliedFormat":99},{"version":"f426d5b37e7f813638273cad01dbd40b8e7cc0b9ce5554752a28771b03212d7c","impliedFormat":99},{"version":"8bd3cc91d63b6e8afcd29211e9016b8a9975bf1f7785c5923d60bf5ec74d1bc8","impliedFormat":99},{"version":"3e7edb9d6c60be5c2ee64d48b0e0bedbc844b89ad7d8cff868dcb79de1866092","impliedFormat":99},{"version":"e2fdc0d4f4dc0e16d9184a030d1e79a2ab87ec1c48f937890c3a344f9d7ec426","impliedFormat":99},{"version":"868289e4ff6e43fd2607558e21964b1f50316a1dbce38e5fea50e629993ebe05","impliedFormat":99},{"version":"fb36d0ad91473fbb3c283f30003e78816035c992bb175548c938d44342123779","impliedFormat":99},{"version":"ed4e7031e79189bec515816c321e99464bed40652ca0ddae3bb69645fde4df3e","impliedFormat":99},{"version":"ddf000b95c84f1ea01188de22aee9735606bfa5a93f77fe6edc0566e48dd7ebf","impliedFormat":99},{"version":"b4ec8ef8c78cde70177b5d3d87b813bcc6660a6e2bd88600c918384c78706715","impliedFormat":99},{"version":"2282cd07f3e17e5361564d52f89dd461c7e0437b7099ff42773c3c100cc5890e","impliedFormat":99},{"version":"9135ef504917a56306539e1fbb928bcf4abd0151ca357d66e9493ae19a5adab7","impliedFormat":99},{"version":"19f9bcc109807e987468fdb5a4f0914b04a31e63c71adfd4fd3cdb8965d5534f","impliedFormat":99},{"version":"649962bf23e44a520cd564c62f8754978556af2b46e51707579ff9345d79b054","impliedFormat":99},{"version":"7930bc77456f32cbdbeecb7d10d3288066d63a2dc14f5d0e46c556db2d8bbc9b","impliedFormat":99},{"version":"5d97005f03e3ed6fc25c48bb6a61399cbc800ea3d52ee3b378b99809118702e5","impliedFormat":99},{"version":"9917241aac9fd5469f685918f61ec0f284b72a71964205cd6fab41a1343c7975","impliedFormat":99},{"version":"278e9e34272e36ef66e21f36cfe519bd52d368c8db597aeb4b5ca44754afdc2c","impliedFormat":99},{"version":"217b3f38b7ed1c30356346e20db427ff21719e7112b2d0c99905ee247fccaf94","impliedFormat":99},{"version":"684bd524520afb91ecc09e19f6ca4447c06d82f28478492a91df5cff1466b73e","impliedFormat":99},{"version":"132d7d3bfa9fdabb1988e6c68930db6675e3fc34bbe296e5fa39821936836bdd","impliedFormat":99},{"version":"f206b429869bb2c6494ca9f9e17f3b706c9f27bb6dff53623656793862d02df0","impliedFormat":99},{"version":"bf5172c33c03ef5bf7252e84e42778c402871d78a5dc298d97800b6f28607706","impliedFormat":99},{"version":"7670c8a1d2fcfcef88dbb8573729a49686d5dbe1c8109b0dabf27ac5d5ef1a7a","impliedFormat":99},{"version":"061e72c2b54a86e2316c0718fd9594c480a2665d6b4621042f4d5a8280238641","impliedFormat":99},{"version":"8add92b8aa846d0648ab0abc4072386ace83f593ac9f9b605a44036c39576f19","impliedFormat":99},{"version":"39a7cc770c1ea18cffccd01aa5723bb13b46aa330071dbbbfabdbdb23d697dd7","impliedFormat":99},{"version":"579fa7e0a81dc470473e651382981f18557ade5146e7f88b73e963574cb4dea7","impliedFormat":99},{"version":"1a1bdac70108385878dde644d741cbbff053d59d4569bf53c8f606a21fe5edcf","impliedFormat":99},{"version":"023a1bf4102807e7f3f1adffe0f4b94f297f26675905cf5af8b6701bec4b36b7","impliedFormat":99},{"version":"6d5e2b772db299d18efe4e5155ea5fd59f01f9bc9b13740ab0ddc2100a0ffe67","impliedFormat":99},{"version":"a8db5de2455a14cda9086a0286d5a69b2314727969d1548ce9e6c95d62b37a23","impliedFormat":99},{"version":"5220818fcb21764a4238fb5f6e80c33469da6ffc37312346266b7a4146450c62","impliedFormat":99},{"version":"223092be51660bc7f4d58c5e0d710af4a1d141640062211c79a39b6bd794c833","impliedFormat":99},{"version":"3e85bd0741475d6fd494462a5b2b0583669b24662586dcd84e79b0b57a4f473d","impliedFormat":99},{"version":"7bf2a520da5bcd1e809b5dc2a97c4856b907310d499b7b1afee2e819870376c1","impliedFormat":99},{"version":"3f54f74fd23f4996d3d1e4f13c2f400f984e936f7c2624e66fdfd4dde3e01c74","impliedFormat":99},{"version":"fc8b87930dcf2a5c2e5538cdb12cf714dca43bdcf50303a9cfaff36fe770cc28","impliedFormat":99},{"version":"d2260dc95e03795efc440d4677e9bafc00e8019985a01d5180e1f76193445397","impliedFormat":99},{"version":"0faaac76aaa8aac11ef1a5c7963a4f5f0a6d0bd4f4685a179861f0de5863118b","impliedFormat":99},{"version":"d82f6d8f1886f7b27e0d6d55edf506d6a6bd0c4dd469df07b839368f487f1e46","impliedFormat":99},{"version":"bd00226dd92abd0d6fc097925bd7340844f70fda11c2e51a1309d63d2beed8cd","impliedFormat":99},{"version":"e7e2560c316b1f4cd97c89c07e29d775eed7f784d159685bbd3974673f4a682b","impliedFormat":99},{"version":"92d117911c1afa50b6da9701629986e16b3cbd14e72996b8bf44b919cf8d534b","impliedFormat":99},{"version":"166d6c06a8ca00e14e844691d4b2965541c739d7c2db3005eda8ef320715346b","impliedFormat":99},{"version":"282612c337fafe5695bb3617d1d4d51cfaa11e0c4923af9fb65852c8dd5028db","impliedFormat":99},{"version":"aa5d645ea3ff7c41a3ffc327c6d85c7de11c281a5199426d79d7d9a23fcb7a83","impliedFormat":99},{"version":"63f253ba20509372770aa38957aeaf761a53e5942995c4fb12a69e7f337b1155","impliedFormat":99},{"version":"b9c2d2e49ad179dae80576d25375034a7522f147b1854550550c33fb31719ea5","impliedFormat":99},{"version":"53eaebb4ff9eeb4b93499decc874f630f844612dee2cf7b44c4ae09a1b7cf64f","impliedFormat":99},{"version":"f262f10ff10bf39f760b5f56ed941b496082f840cb34f4ea765aaac84e3cebed","impliedFormat":99},{"version":"e0ac5ac97e881b7dea0bd259c9c824abb1a25fe13f5e15e98eeba9cb88bd5b55","impliedFormat":99},{"version":"f2ec7c52bd4fc835d880524898f1eee0f81d46adaa2e7f99246ab17698b257d3","impliedFormat":99},{"version":"1f2e3cae19b60f35649b109196049e5db1fc553fdccb34c3658dbdb205952932","impliedFormat":99},{"version":"e0d448de21b220d9e6a6042e7a9961799a8ed0b1767f7f0607a51ce122b35435","impliedFormat":99},{"version":"b3f2be5b7d653a3840c0cb7c4432b05d44d3cc671cbf6f5a5f4e3e81daeffb26","impliedFormat":99},{"version":"6dbe87bd8fc16c70bfb0562f25acd19a04e48b3834976f9d3a895ca350fb9fbf","impliedFormat":99},{"version":"f7455625f452f9d40e7f4ec0b7aa882ab05c8eabb580f7b9d8082e167dfe9773","impliedFormat":99},{"version":"f31a6a866b15fc67f11d161d5800718fb285e68e5aba82b650d36a9a50a5883b","impliedFormat":99},{"version":"db3f05b31f99deaefa42faaeccb31b09c6d445d726c5bf6df825124d05039c5d","impliedFormat":99},{"version":"9c05fd0805e08763258721710147a75bd620bdedc6336df47b4be077efc6074f","impliedFormat":99},{"version":"f7b6603c442747f460f2d700cea91baa2b170e4832e73ded9a83fd197e7c58c0","impliedFormat":99},{"version":"15620215abdde663472aec3cb91105c2926214c0c240383f4f1a7b64cc8f281e","impliedFormat":99},{"version":"5bb5fb3cd787ca35aa0bd8642946bbe25778055a6ca811d3049f73394b22b471","impliedFormat":99},{"version":"940ead012f81b7c62af99cdf8db9449f14a6b8e3966e9240391e9d2dc05c654c","impliedFormat":99},{"version":"881a6ee1a6bacae3ed60b31b09a3185cb17bd2b1eef046222be3c9cf27f6be71","impliedFormat":99},{"version":"fc1fa4dc29b3d3c5a6ef0ca10e62b33b792cf8e5dce589c1347622327ee2949d","impliedFormat":99},{"version":"41db2794d180a7c25df0f91bafca07058b74b396f11847320c0e5495d5388557","impliedFormat":99},{"version":"6a000ce1586656702c82704b72694aeef0b20e3277036c0513e9f5e675c1450a","impliedFormat":99},{"version":"e41f17edc4c96e3390e84cf79e022fcb1de260d47111569e5871e1ed664d8631","impliedFormat":99},{"version":"f9c25579789fe368d4e60ecd2e31e26670616e34ca19ed1d4f8f263c881482f8","impliedFormat":99},{"version":"49c1a63ed38e8dbf3965ed24f22b428d9f1ca73583a446a22b161488e4cebf0b","impliedFormat":99},{"version":"4f7d9281c6f27dc0d8fd37230f29269f7c16892846b9599317da3c6ad26edb66","impliedFormat":99},{"version":"89849e8eb6c455a23fa36191253d3af16adeb0a6993679612b0edc5493a282b6","impliedFormat":99},{"version":"4ecd4ffcfc39e1918514c57075461948f3028d7b1713af25e9989e36086ef309","impliedFormat":99},{"version":"ad4149968e6e9b75c57dfe9b3b6286f50b9642ef2196ebcebb627ecd609bf169","impliedFormat":99},{"version":"a79b483b0b1792476964b4c6710f97fe3db7614082a310bad411db5d7b863846","impliedFormat":99},{"version":"5017059f8ff94958ce27f68f74334da441985dd893502f20ab382cd7ff140292","impliedFormat":99},{"version":"d06ae7d9a5679c96756a0e1a12e915c567994e9441125f12cc79687f5605c9c5","impliedFormat":99},{"version":"c6877486439932b61fa004541cdb6b8d08c4952e0da52349f6942c163d64e0b4","impliedFormat":99},{"version":"a7d014ea4721f8a91df7c6493497b561222cb22e788d013d4f09d494d4c7c0f9","impliedFormat":99},{"version":"4114dad763f5731d3857156914e989a154a1603412759d7c2d7911b2ab63f2f1","impliedFormat":99},{"version":"a96faf9bea8e6de9bf5128af2a1033a7a140ab2c44aa4ca4b7c388a940922abd","impliedFormat":99},{"version":"9e1cb68c5e4f66aca186f2b0503c4a9060f97e15a8cd1f48374b94191d2ee3a8","impliedFormat":99},{"version":"fd342276e952d4703dd9f6a761fef6647eac5f8aeb4aeb38282fd2b33e15e9a6","impliedFormat":99},{"version":"38c4061e2ac3d227777ca0b59c52ed9437e84b0a499f67c7e3f84f10702570dd","impliedFormat":99},{"version":"a311cfef0c96adb76f687f2222b9b2e6ba8e5d09c7f3a2754718136ba7391422","impliedFormat":99},{"version":"2600f677b2483edd014887335e5f034cb6d83518135df0d71314469eaf83dd5d","impliedFormat":99},{"version":"ba57013f02639cfd2348bc8f62ba893be899cc3997d0d214befd677c2841599f","impliedFormat":99},{"version":"b4e1dbd53d50f8bb1b7e751d1f281c806117fca659f7479436f4da767c4dcd00","impliedFormat":99},{"version":"8ae4316bbb7f3bc67bd8e38de897d48f4f940b019f141a2b8139c5e921438bea","impliedFormat":99},{"version":"7e1acc54dbf9e7272b75caf39fda909bf6f6649616841cd1a8d5a6a83ab92562","impliedFormat":99},{"version":"6cfbd030617f2318fd2dacb30d36b09db56b6eb73a069e51aca0060749e97ca5","impliedFormat":99},{"version":"f32d8d51edb7b3e14b0f1f4681214da8c77b402ac88eea843b94901b3e713da5","impliedFormat":99},{"version":"686f34a2cca3f20763dd2a1837ebfbdc412f699b5e5a16cc939dc2c2e7ad280f","impliedFormat":99},{"version":"23b453a12ef4ade9a4b7dfa9f764e0f6bad821f293a107bea8c7f1e0ba482032","impliedFormat":99},{"version":"c77f0836ae3caf72dab9f562b1d2f188b7a30f402789c2e72aba39b470f453de","impliedFormat":99},{"version":"831b668aedc9cb9c2533a72f187e1b739d8226b0ae4da681577d0210c7e5ba29","impliedFormat":99},{"version":"839ef015914709f2a6775b2df60bdf2f96e7be1275ed1bbf3fb3d6b2049c10f0","impliedFormat":99},{"version":"7e23736c64b05cea02ff2cbcbc3d2036a81f97c4898590ab44845dcef1c27bd6","impliedFormat":99},{"version":"f9e386957ef76dae694b95c5f19aa37a53433252e57a5d2b5331dfd7135a6e8a","impliedFormat":99},{"version":"18b661104f01291ce1553729afe96abc0be4908c54c54efbc49d9555a4a56144","impliedFormat":99},{"version":"522cfe0a11df7f12f730e295ecaff47b4c90ff099fb4da47788d52c097bd8d05","impliedFormat":99},{"version":"fa443f19b6300315ccab8e4504e26769b6be926c4f99f2447121799955ff9140","impliedFormat":99},{"version":"ddb0ad5a314c301b0bae2e63d2c51a076d20f9c2bdc194a5669ebd03fea7bf83","impliedFormat":99},{"version":"5d07a4a1711f17c847a05ec7b324e44fb23527ebace41fb5210c45180281e450","impliedFormat":99},{"version":"0de2519d49c7bb091d0bcc964b27b12e0077a394ddb0e1f974d38e9842e08a89","impliedFormat":99},{"version":"efb9bf26f8517f4d1fef69f13b46e1fa9acf72ec2cc0cdeb0f63e47733c99401","impliedFormat":99},{"version":"877d3528b408b66465d4cb4917d68f3eccd63cb7af344ae907e80b3dac5d3991","impliedFormat":99},{"version":"c8551afba2e4bca8cbdfd0ee7099cdc3102bd47c9198c852da7c2a14dd870a01","impliedFormat":99},{"version":"aaf833ebbd0054f5551585b135a1743b08fa548731d7017e0c3d1e75b487d148","impliedFormat":99},{"version":"6b12c989b0df0180e63983869770ac9c1a21181dbc799deebe2c61805923fa51","impliedFormat":99},{"version":"193269f4d8cbe2591dfc63ef95fbabc94f32dec649d9b3aeace3481d1ffcf67b","impliedFormat":99},{"version":"daf456f00bf400c95b53abda7117f3aaafba9bd159ef6517b400b6ab39156359","impliedFormat":99},{"version":"2f2000be5c5af19cbcca50eae3c154261053a9f16f9fb5c52184ccae4f46b939","impliedFormat":99},{"version":"694dc727c74c7854d20a81b67f9b518bc590c4808897a0309fd651cb1eeb6a08","impliedFormat":99},{"version":"e30d150edfa5e7554a8fdcf2283d4cbfb6b23c7335b111829df995e9fe71f31f","impliedFormat":99},{"version":"237b7ce6c9644318afefcd9461b97b38726bf8df1c6d72293f01547ba2b26b62","impliedFormat":99},{"version":"c7754f5795826414baaade6d8176a6860eaa1d1789de481a78671d4c5f17dea9","impliedFormat":99},{"version":"7e29569b643c3d4c11dcb349ebab045515a4f530c2c9ffd78ec208592158cbd7","impliedFormat":99},{"version":"67efc246d509b94eb731327008133efb0ef747300e7edf7373327725feaff64e","impliedFormat":99},{"version":"e7a14aafc5c1535d643b32f4cb37d3a8715beea2deb22311bb71ff132d804b93","impliedFormat":99},{"version":"b8ff90ca151d37e379d7650aabf8b4c20b0d21e5420cf38f5e162ff718cc351c","impliedFormat":99},{"version":"4578969961873698ec6f959822573bdb20ce78d82efb80d63a7e94c7c606226e","impliedFormat":99},{"version":"4d2ce320466b10a8e403407281c0aadc1afb91ac068a250a4361db685d6c9ae7","impliedFormat":99},{"version":"10d7201db01b6a448970ef0725a7f3106aa312b2d462ce605e112d458962d951","impliedFormat":99},{"version":"a29ab9b5b4e8440eb2cc382b874cdb7662251e4c410930c19055f6b38fb178b3","impliedFormat":99},{"version":"a2538b8c4d6da09d94dafbaa72d9a45ef38d79555fa16da1b451146add1c7fbf","impliedFormat":99},{"version":"c1b89af26c0f6ae6152e604d70391cb9dcccdb9414ef0bdf0d74a93abce72a60","impliedFormat":99},{"version":"11c1ca5f65a25245cab77bfd023567666c21056021cc1fec8a5fa1e1d19047c0","impliedFormat":99},{"version":"44663d1b397c7a0ff6eb5e53701397f4f2860ab4b873b8bba20bdf4466771199","impliedFormat":99},{"version":"32a6f6dc50aeb74e2292464be78abaa1a936afe3d64fbb1f6b1a4011f33da3a2","impliedFormat":99},{"version":"ea7466fe5f3a968d387d5dcc0090fb3de32f032f92c3e7febc66bf34987a9e86","impliedFormat":99},{"version":"c7a34ab64b8949d09085ef195b1f17716aad1654c1bfd0eaff378865514d033d","impliedFormat":99},{"version":"5e7e71d63d9fa9abac1de085bed05eeef08e4b2f60fe071dcf7fd04bff233236","impliedFormat":99},{"version":"0330f9b5e1771b24ebe461985d6f7ace49e495bda02ba4be7d3b309130a29abc","impliedFormat":99},{"version":"503c06d19b32d2728439a26e9d1527b65b4404be05a450531974b06e5d792c6f","impliedFormat":99},{"version":"c3f4b01ddf6a2f6ea61965c7eaa26f0ba6143fefe703cbf832ec145f7c41020a","impliedFormat":99},{"version":"e9d85a55cba3e8ae49ac269ea84817e6a03a9c32b08cffe62cfd351ce40d97f2","impliedFormat":99},{"version":"75f5ce6762a2f2efd63861114cc47e1a79c381b9057e0f02c7bd142f03e45fcc","impliedFormat":99},{"version":"89779bd1b8b07d83a20997c07a79b447785b236491a6a5e2c098d29cfb2a313b","impliedFormat":99},{"version":"163a51530fb9dade5b16156e95849567149eb64a96d1cbaedd25c3bfb1477862","impliedFormat":99},{"version":"8f3819557a5516072dc8badb034b5c439bcdfe07f9a98a18c3bc70143bb34e25","impliedFormat":99},{"version":"c605c510f450c490c660d3d624d9027848856f4b271480771c9eb57ad9d61162","impliedFormat":99},{"version":"3a480f6c53012270713fc3418bc3d2d71506cd0e84dae7a04eebfb8afcb41bb0","impliedFormat":99},{"version":"8ba570c60af27be5de41b064923957edf1ed521e03db4f89d7e11075b72356fe","impliedFormat":99},{"version":"4c92d3455fe91e5fa0c654d62753e320cf8b410cac5d24300ca5109ced661783","impliedFormat":99},{"version":"9b39e5b047eeaa58e5a4ef25d54e7ec36e521d1c27d865c2c9b2236883424606","impliedFormat":99},{"version":"4f481a1e39b8f92b41e5173e5a8ee55f5bbce792977221adc229954a0445048e","impliedFormat":99},{"version":"b3575b2899ecfe3317e91a1f87a836295f3f0cf714d68c57eb39f4209214bb66","impliedFormat":99},{"version":"620dddf21780dc99401ddcfb8b8467318c6fa17fa4a58e499ba3ce1055f94630","impliedFormat":99},{"version":"5c0c1a8f595c09b6c4b0d2f932f53a33a789fb7692819de06977d8ee04b74643","impliedFormat":99},{"version":"e35c99edbc8642e84a4fae1ee0a085a19e5ce74d33233c28c987be2ca1a71f72","impliedFormat":99},{"version":"ad0e729c6ad00ae3c3427ecdf95385b08e22c76e1dddf7fd22c3d0b3e98caea8","impliedFormat":99},{"version":"6bc488b7fa0f5dc1c161a9df03f8e110bf828c1b6115dabc6253e1e7caa79bdb","impliedFormat":99},{"version":"9d6acb65442be2cc87a6c6656171795917e7f01e07c43588bcbf3c2623561d1e","impliedFormat":99},{"version":"2103d32e6b011486dd885edf7de322c1de0d9f04809b2e447c3971da79386695","impliedFormat":99},{"version":"a41e8fd9bfe228685fffdc3f7f472f7fd12df4ced1c0342557a1d297ed902d96","impliedFormat":99},{"version":"db475536dbf06ef0448800f4d392cc4453e949985895df4b14916f74478e8139","impliedFormat":99},{"version":"8c5c6c5f71e475fb617dda6cc0b859043d7716c9d52f5e27cd91689f6ae5622e","impliedFormat":99},{"version":"2d1ffd807e79bf40d651af97548ceb7a599bf087bfb2163b5ecb9979c3023a03","impliedFormat":99},{"version":"04c1b194ecc333cd5e5dbecd61e06b7fe354495cb2d42e1286381b414035cad6","impliedFormat":99},{"version":"c64a12c878be8e5a85bd358ba52c8135d03695799e82cbf3e99566fb18a5b263","impliedFormat":99},{"version":"1f753d2b78d2113c0198d218ab32d1f290fbe56685b564bee1cb8db3f90165ab","impliedFormat":99},{"version":"1bd8ebb5138ea8ca3023c040930fba0b1b0a631c98276a185e7856cdce883c22","impliedFormat":99},{"version":"44fc4a62f2c807145db1c998b88b42c7de84b77f14e6fdfeb7e1badd3c88c2af","impliedFormat":99},{"version":"aef4c0c500f4085b6bbd43479706ef42a074971cb72f84cf67e51d2e1872dace","impliedFormat":99},{"version":"97fb44f23d3c55b149276bd2cd6a34122654f751322189ea3b093e6889d91702","impliedFormat":99},{"version":"84ea66736bd3219e8c8755bb4d91e0721a9f0569b38523c79984a5799ecf4627","impliedFormat":99},{"version":"9ae50cf9f4b78837b7e6d407639c33f936f34def9f57eafb9776f8e086a5450a","impliedFormat":99},{"version":"d35e1164cb987fdd7180a844a998d40cc9145118b3aa53232db1a5e1830a601c","impliedFormat":99},{"version":"130367a320f54f051107be504d15c304fbde8d8ed8aef3c6ef092f98165b189a","impliedFormat":99},{"version":"73b7dc0fcf2e804e2a7efe45624b2e92fd91b62b6f87c36294ed451e147a8cec","impliedFormat":99},{"version":"22243eb49babca8634b26eb16f63c015f29e22b3f28be02ddbb45c64dfd5141f","impliedFormat":99},{"version":"0c2441bd292a6cc16dd2f5fe2ab1310946131d5bd9f628c20e26b717050e35c4","impliedFormat":99},{"version":"c52e17c9b6cee5da51b46b430a5c845ecb7133e83b9419756e3fdfe42ee71bcf","impliedFormat":99},{"version":"666ff47070c9ba1f55e7a6b29098ec5bffee55bef93a4c53d7bac9f50c471b41","impliedFormat":99},{"version":"fab8fddbcd52688d07324ce1247e1e8e18a5dc36566600df597371913efa0e8b","impliedFormat":99},{"version":"bc52eb2d3f050c6627332eb28701fd8083faaeabf86f0559fcb2f9fb2dcc0e76","impliedFormat":99},{"version":"069644469271cfd2e73144f88282240debf7992bbe5dc42387198e07a12a34ed","impliedFormat":99},{"version":"f3b21d86b180abf06db37f389b103885e3be6128401f165d2014900468d5cd81","impliedFormat":99},{"version":"146226e48b8924f713964f7aa1fee6a990e2e5b368e7dd47c3108484c8b36165","impliedFormat":99},{"version":"a22565a9f2f7c2c25c6d162ea973eb3de9c0f4579324ea65ca28318def9c66b3","impliedFormat":99},{"version":"58db2f6e72d29b7119015c863af44d5a14b9b99de020290f4f95c491e3c6d637","impliedFormat":99},{"version":"38da4eaf9e5ff18156ca3daf1962076ba81c75c4fc67a3a3805ead974bfd8a36","impliedFormat":99},{"version":"c28e27465e7d6c02ce5b9a2ac890e237b2933a60688d2ab6ef029378dee4a639","impliedFormat":99},{"version":"fa4ce319870cd232dc311214e9919c3b99966f410d36199e0fd63cf9a7d58671","impliedFormat":99},{"version":"bcc83f9340dc335a3f8a7448fde112edec6b6150a2e016454a708d314d772665","impliedFormat":99},{"version":"88210c8f22081fb851fa0af2b8aebd055ea1075e71e7dae57a3485ca1d0b143a","impliedFormat":99},{"version":"8095c727af3d11cfd2da5017a624d15b17e36c9cb1d23fd4f3289ac7db7766ab","impliedFormat":99},{"version":"0caddcbeb9f455e086a2231b2276bb1b939d534e047d63331f91acdce1cc8ab1","impliedFormat":99},{"version":"6c562fe759a8148838a59a0a3eb93b498ad7d0a624f1d4c998e826e45185bca0","affectsGlobalScope":true,"impliedFormat":1},{"version":"a83d0ff8a44f9089545e2d4482ab47c230f22253c886e46c1acaa95d67698ab1","impliedFormat":99},{"version":"68b3b5fa750eaa923299d0fe15d77607d019e1766befb749f8e2b7e8cea5cd7b","impliedFormat":99},{"version":"532e98877b156339e1390df7821cf694f70ec4dc5fa8cf00da7cc251e63767c0","impliedFormat":99},{"version":"c7f82449201fe27d5727ad449381079aad6ff3ad9ae636d7e4072872c672230c","impliedFormat":99},{"version":"72e9cbe282e3a9a1836fbf179a77670d194769ccbec291873afd08ac4df3250c","impliedFormat":99},{"version":"8b42680e9cc29bdb0fe9268a1307d4d7614a28716dae54eb3fb13ab6ffd48d39","impliedFormat":99},{"version":"6a6e01ca00e15d6739ecd7e5e8503b1897234c97561f7c0ae697c08e5fc63c61","impliedFormat":99},{"version":"00b2f24dcfc7250dd17501953bf57a3bc0e9482b6e536d515e27611c899fd7f5","impliedFormat":99},{"version":"c9f6838dd6ce8628a233ac74f6f2353c8272217b22aa5e82453c4cd7739d9cd9","impliedFormat":99},{"version":"9aadbc55e4ee42676910c7c0a7d761b516c7e5be7205fc4072a19d8bd3d06e5b","impliedFormat":99},{"version":"26a9fbf38d1fcf994fdbca7f32e958763a6f7d2d577f5ec5ff6072b13a5b7321","impliedFormat":99},{"version":"714ede8a1b874deb84b884e97641a7a1411bace06ab60a4685dc0594d20e4b84","impliedFormat":99},{"version":"0463fe5552a4a22bfeafa83fffb82c662abb62bb4c78f44223b096dda8034083","impliedFormat":99},{"version":"9182895ee287168e3bcdb91586df3299368c23c9fe3e9297058ecacf6e4d878b","impliedFormat":99},{"version":"ab299ac4c3e703e5866a013673a90259e973455bcb24004d052e7ae37c7cee84","impliedFormat":99},{"version":"9bf5957ea21f184219ddc49300c58d64bc85baae5c57e3e921f5cc020a554a92","impliedFormat":99},"8170c783f80f1a20328d1cbdf5db70701ecb4fcae527098645039a6c21524014",{"version":"355aa3bafb3dac59968c77e7e85ca13fb0c60d4048bc8fca7b1ad166ebbc738e","signature":"80db81678de470efc6f74a89073f1b71640e443bb58b0dc639acff3592775f00"},{"version":"d2da3e0c21c2da0579588416b99d4c5d9f3e2989b72cb34c8b070febb67aca49","signature":"b9816fb46a764dcd351e4a62584c329b361d8e67a1a03c233d548ae98dd696d6"},{"version":"65460235fc68ab33dca7f8964e73d87c97a47902e47da3cf3ff7a15dc9f800a6","signature":"9ce8a8d61cea18762437ed42d7da2a6800934d8ac4e046a93724ea4833032ea6"},"144cecd713cb0416a2251ab6c5d63b44c21168453218b930b50786081e7db789","7fbf6b79703360a7dd2d8181e65feb0b55eec4c0a76b30337caa4ab1a163218f","a93e0ed29700d375cf6fe2f9fafbf83caf845f87ff217c4f05d9e2b7135a0662","3042d18078586d971fa37166b55d8666e2d172319a760bee2e49e5932a713bfd","c3799bdece760598984d9c278a0229cb61ce3fe717f74aca69c0627ca0446bee","25abaf979780fc639ae149734d81d0d3672d70a9ae18c9d3d9df18ed1d56506f","9a89adbc2f4f5d94edcb167a4068b95ae500df73adc8f3a26fb75e35be42d2ec","93f077e09b594bfbe338b30f2571ed01f5fd81792008a3760d4e12b9e417e019","a502018fcae6e0fc6ec49f507eaf9923853565b13ffa9dcbf62769a5ed0b6855","55d491dc2bcf6f4b82922e7be757093dd1442f68226ffb11805ee9f523c0dd75","b8358c521cec856d006ed818766fe280ef37751a24c9d3b0e9e95643897a3e77","dbedb48a800d9abd552cf5615241344e2bd8a297a02b09c98f37a68d7ca9c3ab","91f7609dfb9751158daa6c15fc57dfa22f3f14c4894bf13851a9ea46f62bac40","a20e6af51d887372c3e9520726c263c8277a51b6841254b674e60e662576898b","eea0935e0940de9a8be2ffb46d4ec7653707ce466b4518aea4bc2f3132d6ff45",{"version":"a956fa2346afca8220a3511dbafc82fdcec4f0abb8a2fad6af2b713e22779a0d","signature":"17efad35287a8b3be57bcca908af9a78298eeefa2d15a808a1c36288a2576422"},"ecf3115cd1494d1e28f4c0440921017ccfb6b7fec9b257294049d3c6fed82ca1","ade6052f9780e2ee239fd262070f68796b40c9faeb528bc0e53433037e40cfb1",{"version":"2f01545e28f70e762e7427ce5a09934560074a6bfc66053c5ba36564f9f6db79","signature":"016fd88f58f62f0aed6737c6c934e62e8aa8bce42b7e1b707ecdd583b007b0ae"},{"version":"da9ba3fda3eeee73cf75687a4e1f2dd4a019d23d02c88017185a5d52a81f9808","signature":"81fe8f44738afd9897c1228a5e5e4e2ba23161cc1dabb5d615b40fcc1016de60"},{"version":"6fd6ae28369f1976fe50306ddab6768f3819239081e785d4f5e80d932e151a33","signature":"aafafdd612dad5e81fcafc7ad5f3983206b50d86ae90da0981e6e8aaddd3d7f8"},"425d51e58bf1632bed07b93fb84a6f969443413f1f285d2b2917efb081b45376","2be7228c825b7a1ee92d5dbe4c17a036fcc7585c805c408ff69891cdda8f928a",{"version":"0f52b2835d2351db1d5cd124d867fd4a3f283e2ef6de0616b270727eb1782b40","impliedFormat":99},"97dbfc02b5aa7cdee2737971848fa670867b19b7bbbaa5059f096086215cb03f",{"version":"41e34e1e83b9f7b9394a4b5bbe9164a9fa9046549af53298ee4c7aa3daab8aa2","signature":"66199a144c02e51db76284b7924a98e3ac44bb49437e0e90143c36116a1abd98"},"c103de31772521df36013d50e8073f02fb7f1a8708088969cd13ad4af7050e3a","b94d33e7fc27dc642e5d56fe8bd7fc8c53a419af1674bacfd570778ff1005040","da5e7fd2a25e36e46b3dbd9b4e3ccce4da645522a2c3d2a9e2e781b2495a3bef",{"version":"3d6141cd44e3ffe7a2e027f0e68b8c189ba6a8a9342844c36493b88655836ea3","signature":"57d946289e123907d064d717dd44f539d4ede44658398cafdfe2fdacbea78153"},"96333dfc6a359ba72a1a1a46e7421e8ba32da766941d6d09a70d9ce88984a1ae",{"version":"5518295d106ddd78e60a81f200d34ff99556f22c2ccb55e9ea65e3ca2ffd0333","signature":"77af7c25ce079058b94ea53678b1301ee01171437b71c0db24a2dfdb9e3636a0"},{"version":"3fe3c5536564d5e95a845e5f928c65d4600f601def61dafea9749239e69a7f82","signature":"618536d0c87b02f53638eb837c34dc464906b89cc41cfb66500b00d85af19f90"},"5033a97d91a7b226cc0c78a0c4cacf46f02dcb0ccb7f6f2f9ad64a738523e90f",{"version":"bb468a07ed113ef031002ab3ff714001c24b53303a822fbd52e0a136ccfb9740","signature":"87514ba653efe9df9b579b72fd454167db2de927192569957d8f3f760a4dd5ea"},"3276f5a6929367fd87e65e4e08b96338b132f9d841e7a013c14dcdc2354789f5","cd20afeb2a00a56bd4b597e5dc08c7c00921548e2693b59b19d94e4431f38f12","00e4f0a910288b94c98bf3c278aa20bb33c5b398e45b6112a35d3678411bee2a","149c2df760bdaf2ce6402d67ca4476d2de727bf6b7c744a8a8125d37359f71d1","728f707b8e8b2110b81869d909a7cb92bce92d1cafafb1827f1e356c6442faec","7572b94db3d854bcd3e1f5f22d32589022a9558e0cd49e047452465f12c370d1","30975e4eb7781b9dc588e2bf875fbb208704a16f8cbb1d111f11e6f8c767bdc8","73b4ab95928e8a05f69b98dba5a9d1900a7451747895a78618c2ac52630625ee",{"version":"b574974ed4af6b36e252c29c64909c7f3509409c774139559abd112ae57a7077","signature":"13e900678a64add50e0ec2849bbccad2fdf2e8226815b6a6c24d17929bfcbfce"},"aff556cd612a926c35bf5797070cb7a9e6c31b02df2f92ff9225890d6de32b6d",{"version":"ddc62c8eb6b7fb8e8fd0f0f19809530b1e5ba5a131471a6eef65028d0d3b5a6e","impliedFormat":99},{"version":"802cbde8e06732ca0356927b4c9fbc39f5961df58a18b74fdc4e131269293a5d","impliedFormat":99},{"version":"c142b7ee0627e27db02e8d890746074ab491589f017da78283c452dafc443d3e","impliedFormat":99},{"version":"3ac6eb2cafcb89a552a4923213c705f0fc3c2b50e466eae9dc1a540e3af18bc0","impliedFormat":99},{"version":"073f96a1cfddfedf8695401f8328a8e84a4d98fa5b08b4d894c3885069083cd6","impliedFormat":99},{"version":"66ba40fa928c2fada9a280a61c2b426dfbbfa69085f99913650ff72ddec75b1a","impliedFormat":99},{"version":"4d857105510df8011cfb5b3769dec55624a1df92e85d399cd03bc82bb89d090c","impliedFormat":99},{"version":"19a22f3446387435f13445a31e3d4eb65f132d8e6b7b060d249f0fb138cec698","impliedFormat":99},{"version":"3599c20a0afc23b99fcef01c295abdae18b7f9f91169340c82997f6e4956119b","impliedFormat":99},{"version":"b887624859a2f03e78ae6018e96bd269b5318685f42adec4e93256ed7579c125","impliedFormat":99},{"version":"14191b461a91229ff4b388d15b9e15392a8a3af9bf11fa7d0d4cd31405178e75","impliedFormat":99},{"version":"23a564e852dc91b6e6f050584994b35156f6ee8a2d08c493dad04309046a8397","impliedFormat":99},{"version":"daf66c9de89f11011ef703af894970bb15985fd5a4156b8038e895ad4e4616a7","impliedFormat":99},{"version":"d59c3d0c3283c1878913fc2bc88d84160dbcdc69cf06f822ca7ffb39eefef13b","impliedFormat":99},{"version":"05128b72488ad970c2e30ae6b82c7ee232be49ce6def3b4dd56f62d8b7f7704c","impliedFormat":99},{"version":"715e969755738b0f0b2874f203c72ab38f1beee10fe20374bcb024df41f27618","impliedFormat":1},"33e1c60ba503f4eb36f9a8b1f68b236174b11656d8efea6bac65abddda2f9019",{"version":"d602ffb629af03e2a9c0858f9a0dcd790bfe92fe9a967b968bc35cfb3c58e68a","impliedFormat":1},{"version":"4faca872dbd194a17b3ee267bd8ddc3daf3d16df96f4e43a02c7d9a862022c4f","impliedFormat":99},{"version":"87654de60b5cd8d91d59632ec576fa7e313b41c2540073d52814b6cf5bb739e6","impliedFormat":99},{"version":"1ad330d513566d15d11a4bc9bcfedb85679ff677df0b586c66dc0f6204e3940a","impliedFormat":99},{"version":"66aeb47bf8638d6767f7b4ff684c2d794391c981590073025e98f98e1afed499","impliedFormat":99},{"version":"cd5b0672c9699fe169d69efd65472a874de9d1e25fa8669a934f5f326bf0f025","impliedFormat":99},{"version":"4577621880c696b0aacec6ebd2dbf97ac178ee2e2bfaa0aa3a5260a798220ab4","impliedFormat":99},{"version":"8aa3f7cb4fba7cdba176454f676b0d6664dfc9a58d6c589f6c3a2e045bcccbde","impliedFormat":99},{"version":"cbaadb95dcc68691900ffa857b3bd7eaa99eeb6c351afca15103560bc87f0d15","impliedFormat":99},{"version":"97b02501eb45f487174d5a0ff89b6a95690d50e9eae242e2162118edd5f2705c","impliedFormat":99},{"version":"bbea0619511648a92fe83d5c8eed6149106d7fbf3065310a1986d18598b83bbf","impliedFormat":99},{"version":"963ece6abb58542445eda863960cf053a98da8f4e8634b7a8826aa04f6f85a56","impliedFormat":99},{"version":"5ecea63968444d55f7c3cf677cbec9525db9229953b34f06be0386a24b0fffd2","impliedFormat":99},{"version":"1d226c1e6786584e97efede708d49f2dbd6f887905f16c785d5f09b300bc098d","impliedFormat":99},{"version":"07ff7d4360fbc945963d7a4a8105a5520d1681a00745c20a962fb36bf04452de","impliedFormat":99},{"version":"1b1c48c4d7cbe6f40616594c2a3f6f95bb1dcefd200a7e4167e47b67725b631a","impliedFormat":99},{"version":"603291d43e6936e3fe3365a630cd311a507ada5bb51f35393f5441158f72b30e","impliedFormat":99},{"version":"47b598466fad87cb8fe64b7936c16edad500c8996590278b75a80d1cfa63470b","impliedFormat":99},{"version":"2806e4d2a88e0461c3b0c8cd9e7bc8e927034690e33345aa0853439d67f801b4","impliedFormat":99},{"version":"fa5765887142dfe9634e02c638385282aa7c50881b14ddc57e16e3135488ba74","impliedFormat":99},{"version":"5f7d44f22518b5617290666fb9ae7b3bb3651f35b98558cf4074869c5d19ced9","impliedFormat":99},{"version":"26cfaec143443411bc7d5363f274f885ced430b8f4bee25a81f7827248848d7b","impliedFormat":99},{"version":"6870f32dc76ff6f8f6a419ce55add0a011909e8895252d7cca813835f431783f","impliedFormat":99},{"version":"f073036af7bc2db5c80b8d46ebfe7cb1dc9bfda95953742ddb32efce8b019389","impliedFormat":99},{"version":"5597cbcd19e16f5c9148c76c914e158680de55849b625c2f6b69723f01f1007c","impliedFormat":99},{"version":"7864233f21a3bd04eb6dfa79103a6c1d0648cf17eb4c47cc7aef19d274dd639c","impliedFormat":99},{"version":"b27f7733758db8f462dadf0ee250056e370413028c99fc723c4a93baa54a7c1c","impliedFormat":99},{"version":"5bd7f6f573ac89ec20aaf326e79394da8a89fbff8a297aa864de9137ca045678","impliedFormat":99},{"version":"81b2bebeae6ec1e73b491fe22a82c7e2d3a8369271e622ed74b6e94ba108a475","impliedFormat":99},{"version":"d0e4a184f48eba140f30e8f770853b884e01694f6aff59b38d0be55b0410d397","impliedFormat":99},{"version":"1d34b3ef8e5926334d86d305477d3592d648adb41fa0110970a68059e13d45c0","impliedFormat":99},{"version":"791e26804cd328b19fc37f7903813e8e41892e70d5241dbe2c39fdb52fdd0c9a","impliedFormat":99},{"version":"2a224d12e39aceb46edf1e5988452903c407223a2def4961bf88622f9a247ab0","impliedFormat":99},{"version":"4ac6d584eada1621a7eaa4bfb3dd54e81c2c8a82c7ffdf421ae58d84c3a3490e","impliedFormat":99},{"version":"a522abad9b9b959a9c4bdb4e6bdad96e65d97da9385be13ffc8affc3669fd786","impliedFormat":99},{"version":"ae733b8a8fc9659e24821aa3797d25cfdc205bd31674227b49411ca4d54e510c","impliedFormat":99},{"version":"258a47eaa69608e8c40d4c50aea02360fc2246fec7b92cbf0ee6f80d78dbdec5","impliedFormat":99},{"version":"1e185a3af4b4f3bd6fd52fde968f14dcf9a8cbbc4924237270e290e25d81fe40","impliedFormat":99},{"version":"ae42c6173cc8ad49d6ae21187d0bb7c7c65da10204f9e2614eeb83b29c58f4f7","impliedFormat":99},{"version":"89e661251c0a65c930b02e1f24c741e49f238b56737ad13466118d95485e20d9","impliedFormat":99},{"version":"3f896952650454552b2584ef1e3dd072e97f8498908cd2ab25e6b0217e8bfeb2","impliedFormat":99},{"version":"62076be1e1e8b668a8ddcb803402f1aec725a31d592e8722ff39ad368d9cd472","impliedFormat":99},{"version":"5998b174ccb38a61393170f40448f80152ca5518f9d2048f5b5d3cbe0a9fbac2","impliedFormat":99},{"version":"b586ed6dc8efad1107767cf39cc2a0ea95115c5b3179f565734d6c66b33136cd","impliedFormat":99},{"version":"481aab62f04afa6eab4e439fb4f39af392c5c51519f548897ff71e6bad0b6771","impliedFormat":99},{"version":"82989865638e4d0cc660b7f8c6c8c35ab5341c78171e928ce39bd069402606e7","impliedFormat":99},{"version":"907defa50a3926fcc4214e155bd54b1011c8ce8eb27b1dceb7a4e3ab39a3f0fa","impliedFormat":99},{"version":"7750e0b77e592699c0ab6aa712bbb6e5ba243d4336e52be0fba20e8925397604","impliedFormat":99},{"version":"7dde2385cbbd7c7b067ca2310d6752fae89c7e8858f6eee3c450431b0c217145","impliedFormat":99},{"version":"3ed29ed577653d8f21773f524d0bc0f64c631fcd08badf548624c800c49dd8d8","impliedFormat":99},{"version":"faf9a217d8d237b02ab6d95508d8736ae431bbeb38d98885eb5b8fb6dbe48cec","impliedFormat":99},{"version":"7a38be92db20128deaa7c97956077109fd82877c6c1d1fa9141e119eb8b796f1","impliedFormat":99},{"version":"1c10cc1b095d82ebe397bc9b1706a074c3f3e1f7bfd25745606608aa3d2190a6","impliedFormat":99},{"version":"74b564cd3da8f83d5e472a5b0cc53bf7e276b25576097cb89e6f67caf95b12dc","impliedFormat":99},{"version":"1d94e616da38ea9ef3a02768539388518ee7ff7661ee47a20872e1e9f92162dd","impliedFormat":99},{"version":"588c0f04c480beb5998d449bb0245cf80376b3e0b4f520d1a2fb68b806035d28","impliedFormat":99},{"version":"590708a598f58b156518493c563df1d03040d3b2b7f75fe614e1ada06dbb44dc","impliedFormat":99},{"version":"dc2c32ad9c49a7c3e56a18f3f42933e91474bc26ecc2ea47cf533818a54e6471","impliedFormat":99},{"version":"4b1d8c357c75bdd3ecedcc9bfac8e9c6dff49e91ca68ca0194d82c168e10907a","impliedFormat":99},{"version":"1caa90197f57af75caa2098ac6503a65b45a8011034ff28fef8435e65b50a682","impliedFormat":99},{"version":"71b9606572274d160dd124c04234eb290afa7f89c57206758c48712288bcf211","impliedFormat":99},{"version":"54355bdbfe13d20afb8829d39f21a1ee16cde6d4a00859569cc52b55a6529e2f","impliedFormat":99},{"version":"d93cac0bbb7e1fe241f4b0493cd47466df00d9f1c51a53b69e5442456cb4d102","impliedFormat":99},{"version":"9de94cfccea0da314e8554d6b2f1f01a1b63fa4c79dc24b54277e86b918e9d6f","impliedFormat":99},{"version":"b4f7f4e2e4d0e668ab7cfd94ae5b72b6c690eafeac0e7a6d2218b16afdf7432f","impliedFormat":99},{"version":"5e8d925c0b8f6f91ac0af131a83f72683f88d80a61f5eea37d8883afbe8f74fa","impliedFormat":99},{"version":"a899812bb7aa64c46565405ede3aeb2789f1d13eaff1c4b9c4976a9558b9d109","impliedFormat":99},{"version":"2eb77a708b1d812a8b0a57a6a12cbdb659bf43acf839b21b8996dbbd511d6e53","impliedFormat":99},{"version":"565f81a7c0373f6d852b4d6d985628e7e0adc92e308de1f9c62181a5a29eb569","impliedFormat":99},{"version":"0aa7461df9caf3734abd785d0974929daa925d1b58ec508186f286c29711b6c1","impliedFormat":99},{"version":"fd25bc69b757e511d1cff6dd01d94f6b360447b25b5e99e809f85a949d33d05e","impliedFormat":99},{"version":"939fdf70427033c0a05d112c2b03e8e31f037b8f2ac4617df680107162ffb423","impliedFormat":99},{"version":"9bc7a3d724ff20d2429d94e087c276b9256946b2cd66c9f9bce79ab54ec9c115","impliedFormat":99},{"version":"aa813b5adf5ecf364ddcab7bc6652db73d5c4e43ee5f6ccfdc7737f6d3184667","impliedFormat":99},{"version":"e5fcc46e6fc608a77c7efea569e56e3cb02491a9fc0d74f49e784d0a4a6aee14","impliedFormat":99},{"version":"d13a9800b8d652712b74b8410cf6028e4d2dac7ff9b07b69aac50280ac07eb59","impliedFormat":99},{"version":"126cab464ce86f9c155c0b79f9c38fa906c422ac02c856ff9874051ca35ceb14","impliedFormat":99},{"version":"b417a348eb1fcc86c77da42d9db84b6a63d1e50199c5f984335f97e052bf1a0e","impliedFormat":99},{"version":"25ada8b073df8f9b669aa007ee66298095904b83236652ee940d827c2ed5fe9b","impliedFormat":99},{"version":"24bb5860d0b4310843a2ce164c113315db19861f3fae4f2a56727ca9b98dc4b4","impliedFormat":99},{"version":"b62d96002ec0c8710d0e99aa3175434e1df0f22f5a09291b19e5ec05e8a877e6","impliedFormat":99},{"version":"1ce3d146b69a47419a46cac366dd9f6fe6ed995c3b854c662fe8a4ec8f3f6973","impliedFormat":99},{"version":"bd5ab585207de86ec129e7d2aed277ec85a814eca636843806252df1dc771d87","impliedFormat":99},{"version":"7d331ed732ddb23a5e04eb12716cff50491ba01b712f4810df496c174547403f","impliedFormat":99},{"version":"c5777c3efbe28e42cccf77a466cfece91933319bd2c4ab8ac6ad69ae8dc8cbfd","impliedFormat":99},{"version":"bf32f579d69216e4b7703a8894df803e00305d46e3282fe60a4ed8e2b232f1ab","impliedFormat":99},{"version":"39c2d0f3d8d82809c02668743fb19a50e66f05d4336d48765946e4a051d0579f","impliedFormat":99},{"version":"495e122ec7cd8b18150ec1191e48edda4e23b2587022e59805571ddf8a3b516a","impliedFormat":99},{"version":"9277cadea8fcd4c10616d7667f521274c5fc6cef385861f6962ef880db3e612d","impliedFormat":99},{"version":"20b20c535eb79b2a4a62229abf83f0fcdd3dc1f041fc3c588dbe01e3a7666ef9","impliedFormat":99},{"version":"67268c7663ec316f01ca170e4166bd3db2d7b9c81f84b86c669084484296116f","impliedFormat":99},{"version":"09c1c46f10e01ae7399f2fb391178be7ddd42d70dc6a3abc41d80ccf73badad9","impliedFormat":99},{"version":"31c9882e1d08811f5821ea24554c0bd8a0d97fb7efc661ef76393a28d9a8eb26","impliedFormat":99},{"version":"7c9aecf4946da6395949f23bacaa6d7e9ce287f5aa65e50e69332ee5f4d1960f","impliedFormat":99},{"version":"6701adb65ce407ccabefbaf20862daf55d52dbdb2a663c899d163cc6cbb59192","impliedFormat":99},{"version":"f73df64d28c41e3bc777eca2fb49cb5cda69b52c3786b32d4dc473f855fca42b","impliedFormat":99},{"version":"b342ffdc48ee317927f88cb38871b984b7edf94634428fcad875c7a9fe5515ae","impliedFormat":99},{"version":"247fa787c809e9036079d3f4bf429f5c6e4d76a31647d5547e668fa25c46477e","impliedFormat":99},{"version":"f015d64096dbfde32ec9117706e6e1376e9ed0ea8534d17d1c4035262fb82ebe","impliedFormat":99},{"version":"0af38d2d00fc29764aead613ae52e263e235289ec9e2f365e909226e8b2df2a5","impliedFormat":99},{"version":"c610c569ccfdbcb03d9e531ac1be3ed944586e099bf4f756885fce2d5e1a680c","impliedFormat":99},{"version":"2062be175b1e4f6a0b6b21b4ad08c1e241833349fd82aae558000acb2a9c905b","impliedFormat":99},{"version":"32c98d5e98a05f108f4e405c853db481f83c5a1a9cd6c53870501d8248f9afad","impliedFormat":99},{"version":"29708f8a6e40176fbb79897111acb0c168803fa1a8781216341abfd87cc3d9c0","impliedFormat":99},{"version":"8aee213fdf76ffa8b7b45d356204b931f618e184c429945307388bef84f3f252","impliedFormat":99},{"version":"d5e101bf2eaafcf94b79c0a80a8b86e26ea0b24234f8f5b2c88b58cac0842a74","impliedFormat":99},{"version":"27caf95cace62037352d836d1c547a73363248289ba8b05205cb9eef146768ba","impliedFormat":99},{"version":"8ac1275f4eef836ace2b3779aa240cece0a7094cee65e3a56fc730a270695b0b","impliedFormat":99},{"version":"4dae97da440251bfb634edda1739b3cf39e66b56076e05d7b06bd3181a6fc500","impliedFormat":99},{"version":"66a183f89f492290d10baa4bc6840fac3a0212cd3e32f2230c1506eb6b1f84db","impliedFormat":99},{"version":"bff935f9c88ede1807ce899218a6f3ab3bec4a39d9a9a5ebd1c3adc8a6cb0759","impliedFormat":99},{"version":"afcec1b45ba1bdae04efd78ea45b5b1cc185a3254c243998b9ff936fb0da4437","impliedFormat":99},{"version":"bd4e0fe18fe4dc23dec847d8ad240ba7eb1f9cf5714b4d6f6a222ec615437ae5","impliedFormat":99},{"version":"47da4ca7df09f93e4db41c3d6c49fd6febd613e5f2bb4e5207eb6e80322729ec","impliedFormat":99},{"version":"172423ba720956a2999c4e44a640d6b141c1c8646d96e8a88a333181eddb1eea","impliedFormat":99},{"version":"a779a252b59205072a04bdf5d3cc73ac1aa886f01c8af99fdaee2391a6e2a883","impliedFormat":99},{"version":"a92f39eaa4206161b7fa73de80e5e0b58f39d65e9b7c1180f697097490367668","impliedFormat":99},{"version":"258436bc14be16b94eefec3da57b4eca7a3c1df633c79d4ccc35f18eaa9d8107","impliedFormat":99},{"version":"0ad0d843d93b5bf3fdaf79de4e159e28d6f9367a945970413205f345e9797cbc","impliedFormat":99},{"version":"f9a161a77ec523402d8d7dbaf9a04e9fc3d32d0b304dca4d7a86412bfdd1b1c9","impliedFormat":99},{"version":"3ba0689d55492b8f4105b1f038ba7ba17ef982970b82dd180eed022bee53ac88","impliedFormat":99},{"version":"447fadc914dc9f980a73e0fdfc4ab0efc66a73ca0adab5a05867095eb16332e3","impliedFormat":99},{"version":"c09f4c7ec02ad3b5be269a3e220d69d3f16d43fe3843e2e75263344d3ce7981c","impliedFormat":99},{"version":"d351678cfdd7d86b5dbc0c75eaf66ada923f7ff1c76102508ac22f703cb9b927","impliedFormat":99},{"version":"b3d820765aa7672d9276e319e9a2b4d7a928b5dbfe34169e287bc2c0a03be70b","impliedFormat":99},{"version":"96ce9dcfef17a1945dbb4ec0ff2256f3847e813671bcba46381fa6673cf8b202","impliedFormat":99},{"version":"0d852b4958e9b9dee49676e33381e33280a0345bec8fde3f902b479bd0f69e37","impliedFormat":99},{"version":"af8c8a5ff716d8e3e0a2d8e2b31f1ebb5a0f3b99d9090f12612af48a124bbfec","impliedFormat":99},{"version":"c2578747e0efefde95d4def8fd7320ac489389a6bd33a70cd606d3961b7fc4f6","impliedFormat":99},{"version":"db09a47debb5f727f6b1633dbbe5056ef0fb459220dc3ac45883943314ce8d09","impliedFormat":99},{"version":"4ea3df346f98a919d0342641fa2e0a400ce21348b4430add8e2af078b82533ec","impliedFormat":99},{"version":"c130a5e599c565b49b02dfdaef22c5dc68bf648a9678339b44e8913d3d27ce71","impliedFormat":99},{"version":"06cb5fd4ff2e5cf532dfc6bbeae7b47ad7c2879909e6727ffdacc558115ebf0f","impliedFormat":99},{"version":"5cf2e81f262bee804fa9d50112c5288ed4224243b1837c653c9eec5a621a9b13","impliedFormat":99},{"version":"aeea67ef93786c8625e6c2840c5be41e6f6679f9890bc75628ac0a3cf8ea0c04","impliedFormat":99},{"version":"a37b86cc490287c9723338ed95965a938886313f0f912ba12f789462d8bad89c","impliedFormat":99},{"version":"41a073e65cbf693b4ca1f61f6847e16227d023cacfa75a84fb989efc3545cb19","impliedFormat":99},{"version":"e726badbad2c619272fe4fe528dd07cd5ef87bda456dc3656e4fd1bcc11976d0","impliedFormat":99},{"version":"4d84f055621f07107b6e882b0cb79848106d08899bde344eb6ad0c9bc3539eae","impliedFormat":99},{"version":"60b0f3b27eed4652b4cf70ff359eecf92d1dadce962239812474436b4d608da6","impliedFormat":99},{"version":"4d7002dcc54793296ab4c4b1e28c00e99cdda63ef31b83ef616c58f8773c25bd","impliedFormat":99},{"version":"040dbae8a47533338afa394e6974e753b4bfc1895c322a3a715eb1be21eab5bf","impliedFormat":99},{"version":"a330f1b2f370bbe664fb3ddd6117ab26fbdb4dee2da779b7aa6af842815b4e27","impliedFormat":99},{"version":"6170c6827bcca40ead01d9a8e92e73049b82a0e595f1c11ef39bb98282781f7d","impliedFormat":99},{"version":"5dd074521b20eeb26c76fb3e1d0f85fb4bf26cd247c7dffbed08bd888a6d29d3","impliedFormat":99},{"version":"f3a8d4b406af14afba34488fec9b89859900a8df10510a23d9f1c2e8a116d3fd","impliedFormat":99},{"version":"b03aa91aef645f9856216a2223a47001a84954caf37b7ffb1d63d1327b4231fe","impliedFormat":99},{"version":"01b6435dae2508e231ded5ca79334075da7d6ca12d909765cb335211a90ba86e","impliedFormat":99},{"version":"75fc3992422a1d3b15788ee84656da98a10ce15ce5ba257a0df623a024a0d845","impliedFormat":99},{"version":"3c8b637a833f97a085417e7d0024ac82f7fafc0834a4c61d5e48f8edd8da6c10","impliedFormat":99},{"version":"a3a4132d6c64f431b6d0cc890557c392f57eb43371bb73979ea38d27c86a1c4c","impliedFormat":99},{"version":"ec03be0777b98df75dcd97657ebfac0eb7a9153867aab050a591b6caadf1c2a2","impliedFormat":99},{"version":"c788aaea8be5712b40c3bd9cf589c9510930af7b2aa3d986125df0dedc569290","impliedFormat":99},{"version":"f1060289cfb7547a156ef5f3d86f0f22e1fe72451a8ea9d6e1b82993637ec7f8","impliedFormat":99},{"version":"b4e61f4f522304f7fce1038590ca1f6d091d58aff84833861848f8157732d8db","impliedFormat":99},{"version":"f2333bb4a221631fe506a0354fffb808507d4e5f6fe2c85b69890618226f7d9b","impliedFormat":99},{"version":"6c9114366ff07ee8f5c3cd4ba94ad189a098ce8368040909d605fb38e636d026","impliedFormat":99},{"version":"b6c5cede83853964b2f753d7e202613e1d461857cc3780a57a2a3d346c5afc0a","impliedFormat":99},{"version":"ffc4846043b7f71f310692e4bd38f349373981b832f907963e4bbdd4288f130e","impliedFormat":99},{"version":"18dd033530bbecacbab416e4eb72305b8b40bb485d405e64b45f8b189ab37794","impliedFormat":99},{"version":"1389cb1ca8557f7380f983f00c337969542d6c932b1ba294b48f97f6fd1cb69e","impliedFormat":99},{"version":"85cfc4f1cd043b1df65ba7714d292ba7c6c79c9e288db0d4a9ea6a7b567a675b","impliedFormat":99},{"version":"74cc10ca21f4fc15188d7e7aafd66de5c34c82d011f0c9e02b05b9739e0fa31c","impliedFormat":99},{"version":"abe83f442f76121715241d0fc207d2c325510c6a4dfa6b07662f550c95c6a2a2","impliedFormat":99},{"version":"4b6de64797fd57745c2856f26b4c7de6be543f9335dfde7870a186c3541ff183","impliedFormat":99},{"version":"765122fafa15af14742c91619b7e30b36e5c38f01e6ad079d2c5ecd38a4fc45d","impliedFormat":99},{"version":"1194d3241ea56738d7d8e2b4908572a350cfe7a85b82ef89828ea32e20ab1803","impliedFormat":99},{"version":"6449789627c9555d2914c88498ab494cdc4f18e28a7426a1e74dcef3401f181d","impliedFormat":99},{"version":"17326f1b693cd3a0e89fdc1248097f0135adacbc072b0ed62cab9eecb1c21743","impliedFormat":99},{"version":"1b322be99b786ec951d3d14283aeddd32c3ab25033c4cb984b5224630317b232","impliedFormat":99},{"version":"052042a82186fade9ff89d5efab1efca0493f1350a5cf80fd56e432b4d49eb85","impliedFormat":99},{"version":"8c2f5b82885f8a4e3bbfc31c7574f6532201d66a6620a5987bc2969e660e4100","impliedFormat":99},{"version":"e0f324e0637df946ad4a6dba337e073182d47477dc4fe5176908a2e1e48fad76","impliedFormat":99},{"version":"3b7b9365174c24792ba2c762637b0bd5cbb8d88a72153e3f7f82d34e115d5647","impliedFormat":99},{"version":"ffde1d8a579154089c66679fe70c871e8a12a9b4d5f0e01228a189825a83e1eb","impliedFormat":99},{"version":"5804ffbc65b78751fd510218b90827a7ca677ca34a45b4709a00783b658cbaba","impliedFormat":99},{"version":"0945a03ba41861ce8f75468e2bd1bfd424185418921fc2f55cac5eeeb5049c3d","impliedFormat":99},{"version":"246dc85745d220f0a1041d67bef89de1e02fabf49e6ce896bc1a345eab1fc507","impliedFormat":99},{"version":"87cd5a740e21f3637130a0ac922e884258d08d0ac668cf7ff6da159374071ad9","impliedFormat":99},{"version":"b894722e4b4205a60154ee3d6fa8ecc3ffdfb92a7bd38936f666d3f00be6649c","impliedFormat":99},{"version":"1f7f05258c0992bd696cf00984e640011ae5477d7aac3b80fcf61bf27f42fe88","impliedFormat":99},{"version":"25c1f2a410ead58ffda22f73899b74cb45f9534f9dfce620a4662bde9deb70b9","impliedFormat":99},{"version":"351edaf90b54a559e1759f7ceb54b7881079cba5f4d6dcf15bdb26f1877dd2c6","impliedFormat":99},{"version":"3f79205d951373afec1ca713cbda4be9816d97daa795a9e0a37fa3ae5429afbe","impliedFormat":99},{"version":"bcc4c8b5a39356915b8d366e3499a28adc89e2e0bffc02a108eaec1c4797a58e","impliedFormat":99},{"version":"5b2287eec9804a7fc7c6021ae0a7a92b0160750eb21604b77203589e2ad905f8","impliedFormat":99},{"version":"03870a19c7cbbad803b0ee2d69b777e12be7734e087ccfb0c862529a41cb493b","impliedFormat":99},{"version":"46a52d6ee42784826515dd6ab9f5afaab3a05dfb49ddd8298a2026b6c756b944","impliedFormat":99},{"version":"995f334b04df585cb2a77b74533441293ff1e1d4549c86dd5495494c1fc3969f","impliedFormat":99},{"version":"e83b7824f3d983e9b8c2785541579cd8d8c153e96959e71ab4f69bd83c71f953","impliedFormat":99},{"version":"7b0030262f3d2cc74ae1dd79f4990a7131c34935b2c177e6cfa17a88a6ea56ee","impliedFormat":99},{"version":"a923cde26c2e5431e455844ac5f31126d45976c85f347c7dfd2b9eba3e8ef63c","impliedFormat":99},{"version":"3b2738cfacb777ea1f53acdb26b4f4306fa3dbac7fc5d0f1c4750350d3f5741d","impliedFormat":99},{"version":"b95d11a17e57f0cd0ab04aa8148c8f0ca3a68f56c9a44ac9179cea8a6cccb546","impliedFormat":99},{"version":"7688f3196338007600eba7158240aaa15ad524ca42c204fdb3888446fd690086","impliedFormat":99},{"version":"514f33cfc8bf4a00d0603f6df438959657ce42f94e93e29df29fa9b58e7d54f9","impliedFormat":99},{"version":"7568cf2d6e505847c539e63406ddbde2ccc0f96f2e6c5f115a4b9774d0b55aad","impliedFormat":99},{"version":"d05bd9004c654c2583de473d77f047f03719e3e7bdbe62861371755208e36d59","impliedFormat":99},{"version":"74371225d6032ec7f73b46e736d9ff6ea3626be6fc7959e8b71fedff0bb75cf4","impliedFormat":99},{"version":"06dd247275efd44b3f91270763246700353f1add0945380bdbca8c90a517f9f1","impliedFormat":99},{"version":"0833e55be9920ff787cedb7ea623e97ac9bab28961e0e11aa4a56d36d6074dd2","impliedFormat":99},{"version":"92fbb2b6566fdefc6ba3f151299b2618bd1780cf26c2d0078dcd7f1bdc1c551e","impliedFormat":99},{"version":"3b5317db0574b276c1ecf6ebad9faa974f4e416786b682ed1f854cc85837c3df","impliedFormat":99},{"version":"5f27b1f1b03636451e90fc414bd8426a1db25ad438782354bea60f47d7efb9d6","impliedFormat":99},{"version":"53eb12cfe4c56afff32a3b8adec4fefefa12685c84202c8207351004d30c3b3a","impliedFormat":99},{"version":"05bc3698de467024d02654162f1eeb4edcb0ed9d855a96133572969a6f3675c4","impliedFormat":99},{"version":"a5ba4a306d8bc21ac2fef4e40e9076708dded0176aa21484f1f6da23a4d400e2","impliedFormat":99},{"version":"1c825d1f1bd9e70c306f6c16a0a6b76ccfe4be9350857831eba93e59b95fbb5b","impliedFormat":99},{"version":"b27224caf8db7ed9edf9b12368cedb963bbba3a9b5143c68dff53f5fb2351c96","impliedFormat":99},{"version":"eb3bfb8488f260946c5bbf5d9e730a6e23e0c4a568fbbbe782f3c365e0595dde","impliedFormat":99},{"version":"15de6ee96c8e0f6a78fed11e60c3a0f9b4535c1e6a802c55d65028d500e91e75","impliedFormat":99},{"version":"052f62cd94d56a5ca9d8ce7e68a2201fe8f399a12d7803be2619fd03dd36f1d9","impliedFormat":99},{"version":"06e98ec1e0428de740d985f3480b2e699826d5cd2fe2457f1265b32ff4797ae4","impliedFormat":99},{"version":"4cfa55a13a49f21efdaae7ed8b98995e1acbbf4148cca18a4d327099f8764ae7","impliedFormat":99},{"version":"10a3862454b449925de842e51aefaab1794c66e9a17c6d2878f05dadba20d592","impliedFormat":99},{"version":"e40aa12df628390fb3819a883c52c51ef94fd3998e74965fce6a38917a0530f4","impliedFormat":99},{"version":"0df397db19a2db183105dfe900d75798622677a5db73038608bd325f86a556ed","impliedFormat":99},{"version":"d50d0bcae1d25d9828cb3ae22dec7bbae32a3923bbc1f300a0fc1dd4df14f997","impliedFormat":99},{"version":"a0f502214e5057c200fecaeb0eec7f159d7dba328c32731e3e61d2b200d66125","impliedFormat":99},{"version":"3c74e67bc07b5a760b4a58f94bc8ddab5da383e2831ed866759b86dadea9b9a4","impliedFormat":99},{"version":"e4a995fd487783122df0848df4c871dbb536e1636e8e7b6f6186d2993e9761e8","impliedFormat":99},{"version":"45da721f9a605485a439778c248dfbc6351341d87de448ec266b74185e090631","impliedFormat":99},{"version":"ec47a4e180f0cf61787ada2d4691a1cf4f7fd65482f6fa9e01444adff3cbd6eb","impliedFormat":99},{"version":"454041aff398130c59ea786f797bf53fd90fbf484f7ac85d19b025bb37bf6306","impliedFormat":99},{"version":"c8c16f7fdc34f8bda36cd9827b11c065e94ae25473465b2b35aa71df336ecf63","impliedFormat":99},{"version":"db07a4e9f69cc9b58930c2d3a4ad1fd9f882794b92208d55ab057443081f649a","impliedFormat":99},{"version":"735a572ced293fa984b3675cce56091902a0529cef028fe016d9670e3a94dc8b","impliedFormat":99},{"version":"dcf0056dec8dc80fe76eac1e8c6fa778a2e4c094fe2d4b120e6f5bcabd820be8","impliedFormat":99},{"version":"850f8098d445a3ae375fc2a713b6770e04fd25b7c111373860e57817d63c9ed3","impliedFormat":99},{"version":"efd3f26f59c3291a0998435ad54c67191b39b4cd0d451ac807afd8da86bc1996","impliedFormat":99},{"version":"19b70aecc85035f5faef7f3da8dcbf199af4ceccbce15a670950377b388c1d9c","impliedFormat":99},{"version":"d621382b4ad80cc27b2f670e44e0bb11a7e85cb0f6a0b043aa0c9b6b21b16a15","impliedFormat":99},{"version":"5c1f5f0c20f5171a182440cd0347dbb94e5c84f5976184f2f36dec92afbd9b9c","impliedFormat":99},{"version":"1a7e2345e3b20202800bc92adbf628d22a74902b8a5c87a6ce3c361d3ba314a9","impliedFormat":99},{"version":"eea9dc67b1bd75f72aad8483567241f5fdbe46436f018df7f0719e7ee5aa85da","impliedFormat":99},{"version":"a8f9d46c292dc6ef173bb90dfabc7678fe42295051ec1639a8a6e89608682f11","impliedFormat":99},{"version":"3a49927f72440d36c50e1b62f5cbc2f296253d151ea4e5484ecebc8bc461ad4f","impliedFormat":99},{"version":"ce293a2b914083388ff1de83875cc6e82792c5dc1e99c3be4b787f6b150516bf","impliedFormat":99},{"version":"f8d6e2784bb518d523898f614b8c0ae55341968c982d4617f08867b5d11cf354","impliedFormat":99},{"version":"c7a14afda416659379eefa548bc52f5fabaa54859e221ad132f6f4cac6376043","impliedFormat":99},{"version":"c915a1338cf8a5c56f6dc464f9f12b3f1ea472a717bfdb83fafed9444b4be0f2","impliedFormat":99},{"version":"ed587fa5ca327fa023f49f4057737ed99c9b32b0f888d5b17accb404d1d42e12","impliedFormat":99},{"version":"03206d1ab6b7f08b118786a903cf849768c8c927a21022df88fe63910ddc3433","impliedFormat":99},{"version":"c8c151ca84e8832a5dcd08629722ae57ef8a7dd4c74ea3085f89a7d8768b704f","impliedFormat":99},{"version":"afbe152d1d9717172d4505ea074e96fcd13286fe13ab9b2447c7f57de65547dd","impliedFormat":99},{"version":"b310db137c44ec39915ca1b49da0143da2d07972eb4d63df988307750bbb49b4","impliedFormat":99},{"version":"94ed996eb02d683feb0893dc531a117b8c1c93aad71098abf3f127b898c9abce","impliedFormat":99},{"version":"fecc5365f9a1dd29cc8c582bc0427a7bf06a52c2a42cdb4b25012976628faa6e","impliedFormat":99},{"version":"0c073335c77c5ad0240a0303cae56c8be8da93e206591c5a5a8bd6a613d78d18","impliedFormat":99},{"version":"827c1178e5058f0aaa9047725b845d598f0f52871792441412059173f895597b","impliedFormat":99},{"version":"9e8ed20b5058a6f5f773f420c0efce5c8eb802c0af94cdb96b782cf2acf1b00b","impliedFormat":99},{"version":"8b0336c60458945b1fee185149fa4b5a512917aa171d4232b1f0805c3c12e31b","impliedFormat":99},{"version":"b15377bca02bd4d77f5d089fa0c7a13dc251b104de3b43f62dde6955cc8ef7e8","impliedFormat":99},{"version":"020409dbb29a4396e3c1c0732a0f8afa939e47c935182f6fd0603e21d5a6a8f2","impliedFormat":99},{"version":"0b58c6273e32254b592b78208a81a9c60279fedcf99049010915c357688357bc","impliedFormat":99},{"version":"824c5abc1400634500a5d10745242b2ce1e1d43530383181de545a9302e8c277","impliedFormat":99},{"version":"1e685ffce849148fe9e9649189957078d9495608e9df42cfeab20367d2d70c75","impliedFormat":99},{"version":"c6b41bcce617d511c0c4c6e392327b3b28b89bae1416f73214f2c53c466351f8","impliedFormat":99},{"version":"dfdbf270f707a1f7fe2b59031925fff53be5f27dd096262aaf53be7b7d93b999","impliedFormat":99},{"version":"4995961a9085b2959c6bed314018e2c2b995ee7f80a65fbb9b1aab1ee6a147d8","impliedFormat":99},{"version":"5fbe5cea802127f3f767970693fcdd5c325e707c53f013161b814ddaf51c7353","impliedFormat":99},{"version":"1a41f4324c84630095285f34a4cb88975c59e6784fd807e87b64d4ce701aa11b","impliedFormat":99},{"version":"63b8faed83bc87b9d5f902a09a41ec30190393959e5c86d61dc820f9cec3cd61","impliedFormat":99},{"version":"2bd0a3ea02475382ae8e87d78e3be763dba251ddf9629664ce73c706b400dc94","impliedFormat":99},{"version":"20008d2327e19c4fd051a2c0ee88ea696d704bc6d7ad39988fc509d81c27a485","impliedFormat":99},{"version":"6cee28d40bc224e61f12e867140ab6d677a03a1defc9ade08b1bd60ab1c06524","impliedFormat":99},{"version":"5ee8765acef7f28c85001f2cff791ccd73dcc02c2db7bc383f905276bb26ed54","impliedFormat":99},{"version":"17662ae9763596c2ddaa833f9e326b3de9289098a71457ee18d2db9407cc681b","impliedFormat":99},{"version":"9442dcf95088615dd8ea58077ebed1f7d5dd662caca210b245a6b19f38984038","impliedFormat":99},{"version":"c1999fac90937419a88643fa902f3c2cdcffaeeface5951bd76dfc6bbf4658c7","impliedFormat":99},{"version":"e3a929f769e33c3001244a06d6a3e025083be64599c1e961aee31145d623e824","impliedFormat":99},{"version":"083493311f28114ab250a8f379798214e91f264dce121fa2140ae58376fc48c6","impliedFormat":99},{"version":"fd03b3ac929f2bcec6710176bbcdb34969d7f9810b01f65d19cbdac143a2c7d9","impliedFormat":99},{"version":"f3e2f84bdacbe962c856add41824ccfd66fba7b320753f6e9c6871cd6fd5133c","impliedFormat":99},{"version":"d70ae743099d2615ffab06760a3571a2beb01fcb27366cce4025544603a6081a","impliedFormat":99},{"version":"530fcba9474606ca2eca0b85f91b26d5e24c31431c27d20403928d51f9c1931f","impliedFormat":99},{"version":"c483babd94cb2effd09a918f5cacae5fbc8cdcb8b65b1a28cf07c2a9381f2a0d","impliedFormat":99},{"version":"c808470b50113d547da502f2380c6674fd41908d641663e5944a6070113469cd","impliedFormat":99},{"version":"f0568ac6f1c90cb01c4a2b3d14c0c6e734cfbfa34eebc57d789db55e7d0d34f1","impliedFormat":99},{"version":"0ae4ff7dd81505058a06f617152c94802f16fc7a8d2f768c8794f53f8be57178","impliedFormat":99},{"version":"3f61a28c42e990b337e084e92d7fa7df04f8a6b6699da3754dc59611d189b40e","impliedFormat":99},{"version":"73fbbf32113d791d019c474cf474344bb36d4c375f9622728163ad5640492a39","impliedFormat":99},{"version":"91b6fbc14c8a81bc1751cc033f55e0cb6f3b346653d51e30efb7995ecf969ed2","impliedFormat":99},{"version":"77e2fd9131fc81ffaffdc85a8ab553f869f2a67b236ceb95b85b9a1bd72b8823","impliedFormat":99},{"version":"330213ff23c7adbbb6f1b5ead11fb8dfb731c5c24f8c4a18586acaaa47e74077","impliedFormat":99},{"version":"a9f07992ccd51ff2a089628480d51364e19be7e5b22e04edd7e18a519c50e2fc","impliedFormat":99},{"version":"26f62f6b63fff6ad7abd3fc5d89d36f8c74f6ddb32d64795556d0ad3ac6b2d29","impliedFormat":99},{"version":"7bfd37adbd007f1ffadc32e80d2cef7910e2cb26139b180076909413f76c4d22","impliedFormat":99},{"version":"b727fb19b28fdd8abf41b989f9ec0a6aae52cf07f3918386ad068b33d20c3468","impliedFormat":99},{"version":"17a203789c5ec41fd7b9277541797dbaf9fb6f6df73b58d679dccdb28af67776","impliedFormat":99},{"version":"22469dbd699381a169d6e02d5c080ba9d94b9d6567b7a5c41cb17f505e6a4ad7","impliedFormat":99},{"version":"44412e7238512522c472296f100c52c0accc20d3ee75db7aa503ad4d92b80754","impliedFormat":99},{"version":"99a070ee3983ec208b7bf768df63a0d3272ae22fe6833b0c791558ab753409da","impliedFormat":99},{"version":"6acb809bb284648297faaefcb03e0e4500de5f78194a08b75512e13e5887829b","impliedFormat":99},{"version":"c1fe2aa42206b793ff0c38ed166d78c46abaee848e9eeb36dbdd5493f10f7c50","impliedFormat":99},{"version":"e72af07dbb49e755da80ec0cd7949aa097c5327e6147bf300f2d7a841afb43e2","impliedFormat":99},{"version":"adf2b0d2362e1b4c99336c56293ac3da8aa0d3ebbda67f963d4a0f3d3ef2a021","impliedFormat":99},{"version":"5fc3d9350eb34ad3cbcb1b69249161a33ffe19d7c0e72e6087c947046de6f756","impliedFormat":99},{"version":"b0418e08aab8aa9e4e406428964d2adf8187dd29f6cdaea32ede28fc36e86f56","impliedFormat":99},{"version":"be4147ddded6518b57942a23f89b50b772d841a97e22c93b70eddf901c7581d2","impliedFormat":99},{"version":"0fd35eee80b06cbbcfad3e520f58ba321ee4a5e2ae9001a44016cf84312bb739","impliedFormat":99},{"version":"2f4dad0e02e51c0d630d46dd18b3a99a1d8c9f184af3e9d109027d8d11735f9f","impliedFormat":99},{"version":"41c5600e8662d67b2a149c2eebb422c80cc2337945f5b79dde92d41427499496","impliedFormat":99},{"version":"9c1e78acaead99ab9c612e54f5e16c0675cb6863627ec2dffa0c3d5651d53659","impliedFormat":99},{"version":"6df75e65602bbd54c977312ed62988e0c64423b046ed74ca126b529970233a2e","impliedFormat":99},{"version":"03f7e27eaea783040fa9576a337604405a663ea9cc7dd0d7486d734e4d9e7df7","impliedFormat":99},{"version":"4b500a9b6a2b95039e060a7cf79460d3cc5ab9177121759c82ea0cc867cfef15","impliedFormat":99},{"version":"20c7e618d868cf39018e13050535cdc41bfef95b4d4d1451faf0fbbd758e9f76","impliedFormat":99},{"version":"6576f83f333348274a02f3a9a048dfd9c0fbcc3515ec4e654def0ec5491a6261","impliedFormat":99},{"version":"1c35203ba58569dd071dd71445f9b33c4a5c298bf7b3dddc5400e7363b556a83","impliedFormat":99},{"version":"aa7f2b3a9f4bb8a225b3a5e5c611b1a034ad76c3d870a1062e241485e3968e23","impliedFormat":99},{"version":"eb41d07bb7e2d527ac33c71146a3a4802a24d39defb6b8e4d707e5510074d076","impliedFormat":99},{"version":"405bf967f547561f6810f2903df5c5b3c7528d55917fcea0cf251951bedd879b","impliedFormat":99},{"version":"0060d5fcac50ed959be8765d1f5343eda5641109a62eba69696577e004b891d0","impliedFormat":99},{"version":"def4730fa85f358f1257bf2116242bec72080b1a0c70046d0c05ff7f90164707","impliedFormat":99},{"version":"5336f4657e6ffcc8bae26bd762b09b80ae6e3b67dce0a4b4aa99f5baab00c65a","impliedFormat":99},{"version":"8e728eefe8c7160465492dafb86f25085ede8c6b05e360dd2c7129955a155da8","impliedFormat":99},{"version":"c665cdd809976f388c82e21c47a040e5e19ba6cb953d0e0c1c38e1ce61f40922","impliedFormat":99},{"version":"c3bdb6cc2b1abe32815c4894c4d011d4ea80c79d0934d264b467cc6ec0051bcc","impliedFormat":99},{"version":"d40c02d227da200dd6be4e7d56ec2c560c08b9e24a4688a071f392b37953143a","impliedFormat":99},{"version":"01b9b0a56a739482aadb7da55886fa724bc2b557e9814ef4841d2262efb9846b","impliedFormat":99},{"version":"891117d566ab7e1a7798d83c58a20957c1703e92d5a351802081c643cf58faf0","impliedFormat":99},{"version":"6f925dbb5e83ba81d632287af1706945f435bfaec89258540eaae87817804c84","impliedFormat":99},{"version":"0fadf459265643344979f57c02e7ae5fdb5c70244fc9ccece5a1a977fe0b1fb8","impliedFormat":99},{"version":"0e77a1ed700a09eae143529750cc2eef65b8e28d76cf8a6eaf78b7f1afa24c63","impliedFormat":99},{"version":"5408800bf96b2cdd0d8d77e3d52f6848514efbf1590d96d9f8aa86c8ee95bbdf","impliedFormat":99},{"version":"b6e0ad0ba28715ae23a61b1192cdb24c06a909aa58b2048e64e56574aa4da7a8","impliedFormat":99},{"version":"c20b3e5d792dae26f5bbf8d1b73ddd16d9ddc336e32a301b9dd99c68a779f61e","impliedFormat":99},{"version":"d71713801d5419399f8edaaf0471dea5e578dd8b71eefde7abf387fb372feb1b","impliedFormat":99},{"version":"89b56bb82308d69d9ea109de95fff39ef64bcabd250688da972fcea05f50dad3","impliedFormat":99},{"version":"bf66dcca91d8cb8b890727abf04207eb853352b458fb9df8e4b2003e16572bca","impliedFormat":99},{"version":"32b6a2a6fc20f85513ffb0f35e455dfbaf058f65f063a10dda07ffe9592cd98f","impliedFormat":99},{"version":"8b2bdf89d903b856b52e4d416930701068a3522e9e8c2705602c6e7e2394e86d","impliedFormat":99},{"version":"c988c702e73a0ae03ff6d7868ebc2dd0497e921c3b7ea4fbde42aa781831b8a5","impliedFormat":99},{"version":"8409e2185704c03d12e1522dc4c7b137b6b7524e2fc1f9baee7581ee28fc3d86","impliedFormat":99},{"version":"b90efd45ebc8688943ac972c807d0c6ae4d0355fc47fa5f232eaa7a7cc32010e","impliedFormat":99},{"version":"9c8746b57866938dccd94775ccc3abe27e41d182b6f6d32ce82a1044084d3778","impliedFormat":99},{"version":"be71dce0024b565b17433b79dfb73c200bd087568e24e796d712cbd42eebf8cd","impliedFormat":99},{"version":"4d9081308548bde06c710ad7bc3af5e6d7e24538378a4c10eff2e769dec31bd5","impliedFormat":99},{"version":"09e693240afe609150a21882e64d8f34b664eea485d16ae78ac86cb3a47de3f9","impliedFormat":99},{"version":"89502a94ed72858e0018b65766f8deea38577994b7df9d406afc47224fc259c9","impliedFormat":99},{"version":"70754bc9fb67c061069d54db1961f1a8a07fb3441c0fee51d9c3c091c78d4f15","impliedFormat":99},{"version":"64f095cb3b2e35a37547d242a82100da9661b26bb3d255ef4c51dfcb3985f796","impliedFormat":99},{"version":"9772a1a4b6a4a8c16e2564c0d83848bc92c5410378710f9da8fb2d912ac32b57","impliedFormat":99},{"version":"ae66b8a49700f9b0e1e857eb7989a033392b92b5a19690c9ed7f8f403a1e219c","impliedFormat":99},{"version":"a095cd74b349b5c587c52343a00871d3a522d5d00614275a608e5c3ff690468f","impliedFormat":99},{"version":"ce2b17e7bb13676b9cfe8b9d71db509625851486b845475bf336e2c6f58a2cf7","impliedFormat":99},{"version":"68ff0025b0ff8a90165ae54d417191c8dddf93c794fd54fbfef6d4ea75f6ca82","impliedFormat":99},{"version":"6ff5a35137457c0c733501de9300f1801ae9abb33aea7bc9c6bf5e9d6d98cfc5","impliedFormat":99},{"version":"602d0d687423fb0084791b00d51dcbe5b73f860f5b26245d11ac1582dfd7f862","impliedFormat":99},{"version":"5118e5ed493b74299ca53eca1e5a422fb8f3207337285fe9206dd5d1f88785ad","impliedFormat":99},{"version":"aacbc0d9b6f47db9784a2193fcc7f4bfb1fc6cc711587a4bbac43e45432332ea","impliedFormat":99},{"version":"5be892a93003f44bc4420408ec0726322928020fe22f9a68264a176dc4eb8b96","impliedFormat":99},{"version":"e9f2e0d16452a317fec917a3ff8d522d557fb88d544b127860ce65f955b8b490","impliedFormat":99},{"version":"981feaf9d706617834eb318674966a8741ea35c93ce33e0ce155498e665d2593","impliedFormat":99},{"version":"8e661b24aed6caeef42e16eba111174c13ed178a660b41fd8f82401dfe129515","impliedFormat":99},{"version":"74606837f50a3a16d02993364c004db527b47cdb828edbd770595d5e4ee8dbde","impliedFormat":99},{"version":"68c8dc5d4ff87c569a29a3142ef4a9886e74eff61c18107f6cdd79f205e489b4","impliedFormat":99},{"version":"670fb5d7b72da3028927c1f1ece12a7f653c84c1061687d06c38bec9c7a27577","impliedFormat":99},{"version":"f93b561633fc4bf5005f34f0c2f96f48c3e548d1593136cfaa9331d7294ca417","impliedFormat":99},{"version":"6a6669bb2c58eda13a7f0bec1bc5ffd52699b841429760b5e9520730e0034fd8","impliedFormat":99},{"version":"b25b2a3bb644c0a9133b4b37248959cf349ccd7251acf56aeb9eedacc530d4f8","impliedFormat":99},{"version":"2043b3a3e98c3d15f6062199cd5937e237e5922dc1e3edb6946488217b6153cf","impliedFormat":99},{"version":"bf3e9ffa532ba804833ab0a6d63633231e5dec7b9fd7276dced16f2e143d44f6","impliedFormat":99},{"version":"56c110e43d07b0165fc870fa60f7a1a268bcf612407cd9f916e002b42ba74cb1","impliedFormat":99},{"version":"338abeb4d9b0c7e14f56317ebb437f2a654dde4f8f6b52bec45657644f0688e3","impliedFormat":99},{"version":"2aa08243d9c596b3e993b558033dd391f39ba4d6525ccba992b11bb5be54c04e","impliedFormat":99},{"version":"208371c97acf811ef41ba4b217816aedb802a570129042463b198c7d72d1cca1","impliedFormat":99},{"version":"6311ecffa1680ff0f9587217df76d9556d4c8c623f12464b8beb44461d1d22af","impliedFormat":99},{"version":"16e2700613d061c8a3c21fd26bdff099948396954d5935ce913424d93c97815c","impliedFormat":99},{"version":"0890d6e6870d35b625590a98abc2bd3fa880fa46d0dc3de22dbf01628cfd34b7","impliedFormat":99},{"version":"193814fef68f60058efb9c02cffd20bcbf70eec1d32ea0fce4b5887aef746157","impliedFormat":99},{"version":"1da60cc4d8263f762b22507b0460b0b3da1c7b78a1384a14acf3044be2ea1646","impliedFormat":99},{"version":"176b51633bb331b5c4a71d8b931eefb628498df505a3d16ddb0de59f3c6bdf09","impliedFormat":99},{"version":"5cbc3c3c6475704af132c35b095da392a03815baf2e9f2853178ff9b370b64d2","impliedFormat":99},{"version":"f2894746fb20b0fca49d15bc201c54af7373083867e23a6f74f8ec02431e72f7","impliedFormat":99},{"version":"6826e70645f65e77bcceb9230962687109301a4ad9d6dbb71a7785167d4a4b9e","impliedFormat":99},{"version":"13e930c27d68ecfa906c24d599b10927b152030d07da0fa0889fd4fddc5b4115","impliedFormat":99},{"version":"8c86f563e8bcefb0b5b1ac62e5a27ee6a2a9b775e72dea5793823edeb24d36e9","impliedFormat":99},{"version":"d9134c8daef2565f20b72171f634800efb204eba63b03142d5dae5f36088e95a","impliedFormat":99},{"version":"2fbe402f0ee5aa8ab55367f88030f79d46211c0a0f342becaa9f648bf8534e9d","impliedFormat":1},{"version":"b94258ef37e67474ac5522e9c519489a55dcb3d4a8f645e335fc68ea2215fe88","impliedFormat":1},"081d2a1cb4f3aec636a6894d9eff4e90d476d5db85b82d0399c1f4d18517112e","ed0f1fc432bca4c72f9dd8741ae2268a00d2d3429ff1149bf463c4271b13d8e9","e760c470414c9e617d7292094146d906352fee3b1526b790a611d4a118e79196","b7fb499905e441a0602392ce118828425ad059079060109694d01f9fac00ad20","dcb9e899f1322158f016d75c400fa8cce9b797ea49bec65528bac686cee2ec5c","342f32b683937b826cdff2999d5e3057836d459aa43eadb0c3538b818d195b9a","5706189b86e8670499fb33f37147dd6f65c39d59eef5211e34157f2952c35e32","b28b76d6f8d0feafbbeff2220cf83f9a6b03d8c81f0145b3af4c7d19933ab2c0","dfb9f52ae3b89e4a419b336c3893ab6fb658fc324fb402759d878d54e3b2db5a","92ad5065f03d7edc98f367e729f3ec84ece1d99ee8cd0d3a8137f266d4ab3d8a","79ad8fb6644a775cc49131c1aa737ca26cae6a563beec0fa52ef6e8d85202fab",{"version":"979d4f8d0101f09db9d7397237663d856605221c70137411fadea6012e78894a","impliedFormat":99},{"version":"2cef84bf00cbdb452fdc5d8ecfe7b8c0aa3fa788bdc4ad8961e2e636530dbb60","impliedFormat":99},{"version":"24104650185414f379d5cc35c0e2c19f06684a73de5b472bae79e0d855771ecf","impliedFormat":99},{"version":"799003c0ab928582fca04977f47b8d85b43a8de610f4eef0ad2d069fbb9f9399","impliedFormat":99},{"version":"b13dd41c344a23e085f81b2f5cd96792e6b35ae814f32b25e39d9841844ad240","impliedFormat":99},{"version":"17d8b4e6416e48b6e23b73d05fd2fde407e2af8fddbe9da2a98ede14949c3489","impliedFormat":99},{"version":"6d17b2b41f874ab4369b8e04bdbe660163ea5c8239785c850f767370604959e3","impliedFormat":99},{"version":"04b4c044c8fe6af77b6c196a16c41e0f7d76b285d036d79dcaa6d92e24b4982b","impliedFormat":99},{"version":"30bdeead5293c1ddfaea4097d3e9dd5a6b0bc59a1e07ff4714ea1bbe7c5b2318","impliedFormat":99},{"version":"e7df226dcc1b0ce76b32f160556f3d1550124c894aae2d5f73cefaaf28df7779","impliedFormat":99},{"version":"f2b7eef5c46c61e6e72fba9afd7cc612a08c0c48ed44c3c5518559d8508146a2","impliedFormat":99},{"version":"00f0ba57e829398d10168b7db1e16217f87933e61bd8612b53a894bd7d6371da","impliedFormat":99},{"version":"126b20947d9fa74a88bb4e9281462bda05e529f90e22d08ee9f116a224291e84","impliedFormat":99},{"version":"40d9e43acee39702745eb5c641993978ac40f227475eacc99a83ba893ad995db","impliedFormat":99},{"version":"8a66b69b21c8de9cb88b4b6d12f655d5b7636e692a014c5aa1bd81745c8c51d5","impliedFormat":99},{"version":"ebbb846bdd5a78fdacff59ae04cea7a097912aeb1a2b34f8d88f4ebb84643069","impliedFormat":99},{"version":"7321adb29ffd637acb33ee67ea035f1a97d0aa0b14173291cc2fd58e93296e04","impliedFormat":99},{"version":"320816f1a4211188f07a782bdb6c1a44555b3e716ce13018f528ad7387108d5f","impliedFormat":99},{"version":"b2cc8a474b7657f4a03c67baf6bff75e26635fd4b5850675e8cad524a09ddd0c","impliedFormat":99},{"version":"0d081e9dc251063cc69611041c17d25847e8bdbe18164baaa89b7f1f1633c0ab","impliedFormat":99},{"version":"a64c25d8f4ec16339db49867ea2324e77060782993432a875d6e5e8608b0de1e","impliedFormat":99},{"version":"0739310b6b777f3e2baaf908c0fbc622c71160e6310eb93e0d820d86a52e2e23","impliedFormat":99},{"version":"37b32e4eadd8cd3c263e7ac1681c58b2ac54f3f77bb34c5e4326cc78516d55a9","impliedFormat":99},{"version":"9b7a8974e028c4ed6f7f9abb969e3eb224c069fd7f226e26fcc3a5b0e2a1eba8","impliedFormat":99},{"version":"e8100b569926a5592146ed68a0418109d625a045a94ed878a8c5152b1379237c","impliedFormat":99},{"version":"594201c616c318b7f3149a912abd8d6bdf338d765b7bcbde86bca2e66b144606","impliedFormat":99},{"version":"03e380975e047c5c6ded532cf8589e6cc85abb7be3629e1e4b0c9e703f2fd36f","impliedFormat":99},{"version":"fae14b53b7f52a8eb3274c67c11f261a58530969885599efe3df0277b48909e1","impliedFormat":99},{"version":"c41206757c428186f2e0d1fd373915c823504c249336bdc9a9c9bbdf9da95fef","impliedFormat":99},{"version":"e961f853b7b0111c42b763a6aa46fc70d06a697db3d8ed69b38f7ba0ae42a62b","impliedFormat":99},{"version":"3db90f79e36bcb60b3f8de1bc60321026800979c150e5615047d598c787a64b7","impliedFormat":99},{"version":"639b6fb3afbb8f6067c1564af2bd284c3e883f0f1556d59bd5eb87cdbbdd8486","impliedFormat":99},{"version":"49795f5478cb607fd5965aa337135a8e7fd1c58bc40c0b6db726adf186dd403f","impliedFormat":99},{"version":"7d8890e6e2e4e215959e71d5b5bd49482cf7a23be68d48ea446601a4c99bd511","impliedFormat":99},{"version":"d56f72c4bb518de5702b8b6ae3d3c3045c99e0fd48b3d3b54c653693a8378017","impliedFormat":99},{"version":"4c9ac40163e4265b5750510d6d2933fb7b39023eed69f7b7c68b540ad960826e","impliedFormat":99},{"version":"8dfab17cf48e7be6e023c438a9cdf6d15a9b4d2fa976c26e223ba40c53eb8da8","impliedFormat":99},{"version":"38bdf7ccacfd8e418de3a7b1e3cecc29b5625f90abc2fa4ac7843a290f3bf555","impliedFormat":99},{"version":"9819e46a914735211fbc04b8dc6ba65152c62e3a329ca0601a46ba6e05b2c897","impliedFormat":99},{"version":"50f0dc9a42931fb5d65cdd64ba0f7b378aedd36e0cfca988aa4109aad5e714cb","impliedFormat":99},{"version":"894f23066f9fafccc6e2dd006ed5bd85f3b913de90f17cf1fe15a2eb677fd603","impliedFormat":99},{"version":"abdf39173867e6c2d6045f120a316de451bbb6351a6929546b8470ddf2e4b3b9","impliedFormat":99},{"version":"aa2cb4053f948fbd606228195bbe44d78733861b6f7204558bbee603202ee440","impliedFormat":99},{"version":"6911b41bfe9942ac59c2da1bbcbe5c3c1f4e510bf65cae89ed00f434cc588860","impliedFormat":99},{"version":"7b81bc4d4e2c764e85d869a8dd9fe3652b34b45c065482ac94ffaacc642b2507","impliedFormat":99},{"version":"895df4edb46ccdcbce2ec982f5eed292cf7ea3f7168f1efea738ee346feab273","impliedFormat":99},{"version":"8692bb1a4799eda7b2e3288a6646519d4cebb9a0bddf800085fc1bd8076997a0","impliedFormat":99},{"version":"239c9e98547fe99711b01a0293f8a1a776fc10330094aa261f3970aaba957c82","impliedFormat":99},{"version":"34833ec50360a32efdc12780ae624e9a710dd1fd7013b58c540abf856b54285a","impliedFormat":99},{"version":"647538e4007dcc351a8882067310a0835b5bb8559d1cfa5f378e929bceb2e64d","impliedFormat":99},{"version":"992d6b1abcc9b6092e5a574d51d441238566b6461ade5de53cb9718e4f27da46","impliedFormat":99},{"version":"938702305649bf1050bd79f3803cf5cc2904596fc1edd4e3b91033184eae5c54","impliedFormat":99},{"version":"1e931d3c367d4b96fe043e792196d9c2cf74f672ff9c0b894be54e000280a79d","impliedFormat":99},{"version":"05bec322ea9f6eb9efcd6458bb47087e55bd688afdd232b78379eb5d526816ed","impliedFormat":99},{"version":"4c449a874c2d2e5e5bc508e6aa98f3140218e78c585597a21a508a647acd780a","impliedFormat":99},{"version":"dae15e326140a633d7693e92b1af63274f7295ea94fb7c322d5cbe3f5e48be88","impliedFormat":99},{"version":"c2b0a869713bca307e58d81d1d1f4b99ebfc7ec8b8f17e80dde40739aa8a2bc6","impliedFormat":99},{"version":"6e4b4ff6c7c54fa9c6022e88f2f3e675eac3c6923143eb8b9139150f09074049","impliedFormat":99},{"version":"69559172a9a97bbe34a32bff8c24ef1d8c8063feb5f16a6d3407833b7ee504cf","impliedFormat":99},{"version":"86b94a2a3edcb78d9bfcdb3b382547d47cb017e71abe770c9ee8721e9c84857f","impliedFormat":99},{"version":"e3fafafda82853c45c0afc075fea1eaf0df373a06daf6e6c7f382f9f61b2deb3","impliedFormat":99},{"version":"a4ba4b31de9e9140bc49c0addddbfaf96b943a7956a46d45f894822e12bf5560","impliedFormat":99},{"version":"d8a7926fc75f2ed887f17bae732ee31a4064b8a95a406c87e430c58578ee1f67","impliedFormat":99},{"version":"9886ffbb134b0a0059fd82219eba2a75f8af341d98bc6331b6ef8a921e10ec68","impliedFormat":99},{"version":"c2ead057b70d0ae7b87a771461a6222ebdb187ba6f300c974768b0ae5966d10e","impliedFormat":99},{"version":"46687d985aed8485ab2c71085f82fafb11e69e82e8552cf5d3849c00e64a00a5","impliedFormat":99},{"version":"999ca66d4b5e2790b656e0a7ce42267737577fc7a52b891e97644ec418eff7ec","impliedFormat":99},{"version":"ec948ee7e92d0888f92d4a490fdd0afb27fbf6d7aabebe2347a3e8ac82c36db9","impliedFormat":99},{"version":"03ef2386c683707ce741a1c30cb126e8c51a908aa0acc01c3471fafb9baaacd5","impliedFormat":99},{"version":"66a372e03c41d2d5e920df5282dadcec2acae4c629cb51cab850825d2a144cea","impliedFormat":99},{"version":"ddf9b157bd4c06c2e4646c9f034f36267a0fbd028bd4738214709de7ea7c548b","impliedFormat":99},{"version":"3e795aac9be23d4ad9781c00b153e7603be580602e40e5228e2dafe8a8e3aba1","impliedFormat":99},{"version":"98c461ec5953dfb1b5d5bca5fee0833c8a932383b9e651ca6548e55f1e2c71c3","impliedFormat":99},{"version":"5c42107b46cb1d36b6f1dee268df125e930b81f9b47b5fa0b7a5f2a42d556c10","impliedFormat":99},{"version":"7e32f1251d1e986e9dd98b6ff25f62c06445301b94aeebdf1f4296dbd2b8652f","impliedFormat":99},{"version":"2f7e328dda700dcb2b72db0f58c652ae926913de27391bd11505fc5e9aae6c33","impliedFormat":99},{"version":"3de7190e4d37da0c316db53a8a60096dbcd06d1a50677ccf11d182fa26882080","impliedFormat":99},{"version":"a9d6f87e59b32b02c861aade3f4477d7277c30d43939462b93f48644fa548c58","impliedFormat":99},{"version":"2bce8fd2d16a9432110bbe0ba1e663fd02f7d8b8968cd10178ea7bc306c4a5df","impliedFormat":99},{"version":"798bedbf45a8f1e55594e6879cd46023e8767757ecce1d3feaa78d16ad728703","impliedFormat":99},{"version":"62723d5ac66f7ed6885a3931dd5cfa017797e73000d590492988a944832e8bc2","impliedFormat":99},{"version":"03db8e7df7514bf17fc729c87fff56ca99567b9aa50821f544587a666537c233","impliedFormat":99},{"version":"9b1f311ba4409968b68bf20b5d892dbd3c5b1d65c673d5841c7dbde351bc0d0b","impliedFormat":99},{"version":"2d1e8b5431502739fe335ceec0aaded030b0f918e758a5d76f61effa0965b189","impliedFormat":99},{"version":"e725839b8f884dab141b42e9d7ff5659212f6e1d7b4054caa23bc719a4629071","impliedFormat":99},{"version":"4fa38a0b8ae02507f966675d0a7d230ed67c92ab8b5736d99a16c5fbe2b42036","impliedFormat":99},{"version":"50ec1e8c23bad160ddedf8debeebc722becbddda127b8fdce06c23eacd3fe689","impliedFormat":99},{"version":"9a0aea3a113064fd607f41375ade308c035911d3c8af5ae9db89593b5ca9f1f9","impliedFormat":99},{"version":"8d643903b58a0bf739ce4e6a8b0e5fb3fbdfaacbae50581b90803934b27d5b89","impliedFormat":99},{"version":"19de2915ccebc0a1482c2337b34cb178d446def2493bf775c4018a4ea355adb8","impliedFormat":99},{"version":"9be8fc03c8b5392cd17d40fd61063d73f08d0ee3457ecf075dcb3768ae1427bd","impliedFormat":99},{"version":"a2d89a8dc5a993514ca79585039eea083a56822b1d9b9d9d85b14232e4782cbe","impliedFormat":99},{"version":"f526f20cae73f17e8f38905de4c3765287575c9c4d9ecacee41cfda8c887da5b","impliedFormat":99},{"version":"d9ec0978b7023612b9b83a71fee8972e290d02f8ff894e95cdd732cd0213b070","impliedFormat":99},{"version":"7ab10c473a058ec8ac4790b05cae6f3a86c56be9b0c0a897771d428a2a48a9f9","impliedFormat":99},{"version":"451d7a93f8249d2e1453b495b13805e58f47784ef2131061821b0e456a9fd0e1","impliedFormat":99},{"version":"21c56fe515d227ed4943f275a8b242d884046001722a4ba81f342a08dbe74ae2","impliedFormat":99},{"version":"d8311f0c39381aa1825081c921efde36e618c5cf46258c351633342a11601208","impliedFormat":99},{"version":"6b50c3bcc92dc417047740810596fcb2df2502aa3f280c9e7827e87896da168a","impliedFormat":99},{"version":"18a6b318d1e7b31e5749a52be0cf9bbce1b275f63190ef32e2c79db0579328ca","impliedFormat":99},{"version":"6a2d0af2c27b993aa85414f3759898502aa198301bc58b0d410948fe908b07b0","impliedFormat":99},{"version":"2da11b6f5c374300e5e66a6b01c3c78ec21b5d3fec0748a28cc28e00be73e006","impliedFormat":99},{"version":"0729691b39c24d222f0b854776b00530877217bfc30aac1dc7fa2f4b1795c536","impliedFormat":99},{"version":"ca45bb5c98c474d669f0e47615e4a5ae65d90a2e78531fda7862ee43e687a059","impliedFormat":99},{"version":"c1c058b91d5b9a24c95a51aea814b0ad4185f411c38ac1d5eef0bf3cebec17dc","impliedFormat":99},{"version":"3ab0ed4060b8e5b5e594138aab3e7f0262d68ad671d6678bcda51568d4fc4ccc","impliedFormat":99},{"version":"e2bf1faba4ff10a6020c41df276411f641d3fdce5c6bae1db0ec84a0bf042106","impliedFormat":99},{"version":"80b0a8fe14d47a71e23d7c3d4dcee9584d4282ef1d843b70cab1a42a4ea1588c","impliedFormat":99},{"version":"a0f02a73f6e3de48168d14abe33bf5970fdacdb52d7c574e908e75ad571e78f7","impliedFormat":99},{"version":"c728002a759d8ec6bccb10eed56184e86aeff0a762c1555b62b5d0fa9d1f7d64","impliedFormat":99},{"version":"586f94e07a295f3d02f847f9e0e47dbf14c16e04ccc172b011b3f4774a28aaea","impliedFormat":99},{"version":"cfe1a0f4ed2df36a2c65ea6bc235dbb8cf6e6c25feb6629989f1fa51210b32e7","impliedFormat":99},{"version":"8ba69c9bf6de79c177329451ffde48ddab7ec495410b86972ded226552f664df","impliedFormat":99},{"version":"15111cbe020f8802ad1d150524f974a5251f53d2fe10eb55675f9df1e82dbb62","impliedFormat":99},{"version":"782dc153c56a99c9ed07b2f6f497d8ad2747764966876dbfef32f3e27ce11421","impliedFormat":99},{"version":"cc2db30c3d8bb7feb53a9c9ff9b0b859dd5e04c83d678680930b5594b2bf99cb","impliedFormat":99},{"version":"46909b8c85a6fd52e0807d18045da0991e3bdc7373435794a6ba425bc23cc6be","impliedFormat":99},{"version":"e4e511ff63bb6bd69a2a51e472c6044298bca2c27835a34a20827bc3ef9b7d13","impliedFormat":99},{"version":"2c86f279d7db3c024de0f21cd9c8c2c972972f842357016bfbbd86955723b223","impliedFormat":99},{"version":"112c895cff9554cf754f928477c7d58a21191c8089bffbf6905c87fe2dc6054f","impliedFormat":99},{"version":"8cfc293b33082003cacbf7856b8b5e2d6dd3bde46abbd575b0c935dc83af4844","impliedFormat":99},{"version":"d2c5c53f85ce0474b3a876d76c4fc44ff7bb766b14ed1bf495f9abac181d7f5f","impliedFormat":99},{"version":"3c523f27926905fcbe20b8301a0cc2da317f3f9aea2273f8fc8d9ae88b524819","impliedFormat":99},{"version":"9ca0d706f6b039cc52552323aeccb4db72e600b67ddc7a54cebc095fc6f35539","impliedFormat":99},{"version":"a64909a9f75081342ddd061f8c6b49decf0d28051bc78e698d347bdcb9746577","impliedFormat":99},{"version":"7d8d55ae58766d0d52033eae73084c4db6a93c4630a3e17f419dd8a0b2a4dcd8","impliedFormat":99},{"version":"b8b5c8ba972d9ffff313b3c8a3321e7c14523fc58173862187e8d1cb814168ac","impliedFormat":99},{"version":"9c42c0fa76ee36cf9cc7cc34b1389fbb4bd49033ec124b93674ec635fabf7ffe","impliedFormat":99},{"version":"6184c8da9d8107e3e67c0b99dedb5d2dfe5ccf6dfea55c2a71d4037caf8ca196","impliedFormat":99},{"version":"4030ceea7bf41449c1b86478b786e3b7eadd13dfe5a4f8f5fe2eb359260e08b3","impliedFormat":99},{"version":"7bf516ec5dfc60e97a5bde32a6b73d772bd9de24a2e0ec91d83138d39ac83d04","impliedFormat":99},{"version":"e6a6fb3e6525f84edf42ba92e261240d4efead3093aca3d6eb1799d5942ba393","impliedFormat":99},{"version":"45df74648934f97d26800262e9b2af2f77ef7191d4a5c2eb1df0062f55e77891","impliedFormat":99},{"version":"3fe361e4e567f32a53af1f2c67ad62d958e3d264e974b0a8763d174102fe3b29","impliedFormat":99},{"version":"28b520acee4bc6911bfe458d1ad3ebc455fa23678463f59946ad97a327c9ab2b","impliedFormat":99},{"version":"121b39b1a9ad5d23ed1076b0db2fe326025150ef476dccb8bf87778fcc4f6dd7","impliedFormat":99},{"version":"f791f92a060b52aa043dde44eb60307938f18d4c7ac13df1b52c82a1e658953f","impliedFormat":99},{"version":"df09443e7743fd6adc7eb108e760084bacdf5914403b7aac5fbd4dc4e24e0c2c","impliedFormat":99},{"version":"eeb4ff4aa06956083eaa2aad59070361c20254b865d986bc997ee345dbd44cbb","impliedFormat":99},{"version":"ed84d5043444d51e1e5908f664addc4472c227b9da8401f13daa565f23624b6e","impliedFormat":99},{"version":"146bf888b703d8baa825f3f2fb1b7b31bda5dff803e15973d9636cdda33f4af3","impliedFormat":99},{"version":"b4ec8b7a8d23bdf7e1c31e43e5beac3209deb7571d2ccf2a9572865bf242da7c","impliedFormat":99},{"version":"3fba0d61d172091638e56fba651aa1f8a8500aac02147d29bd5a9cc0bc8f9ec2","impliedFormat":99},{"version":"a5a57deb0351b03041e0a1448d3a0cc5558c48e0ed9b79b69c99163cdca64ad8","impliedFormat":99},{"version":"9bcecf0cbc2bfc17e33199864c19549905309a0f9ecc37871146107aac6e05ae","impliedFormat":99},{"version":"d6a211db4b4a821e93c978add57e484f2a003142a6aef9dbfa1fe990c66f337b","impliedFormat":99},{"version":"bd4d10bd44ce3f630dd9ce44f102422cb2814ead5711955aa537a52c8d2cae14","impliedFormat":99},{"version":"08e4c39ab1e52eea1e528ee597170480405716bae92ebe7a7c529f490afff1e0","impliedFormat":99},{"version":"625bb2bc3867557ea7912bd4581288a9fca4f3423b8dffa1d9ed57fafc8610e3","impliedFormat":99},{"version":"d1992164ecc334257e0bef56b1fd7e3e1cea649c70c64ffc39999bb480c0ecdf","impliedFormat":99},{"version":"a53ff2c4037481eb357e33b85e0d78e8236e285b6428b93aa286ceea1db2f5dc","impliedFormat":99},{"version":"4fe608d524954b6857d78857efce623852fcb0c155f010710656f9db86e973a5","impliedFormat":99},{"version":"b53b62a9838d3f57b70cc456093662302abb9962e5555f5def046172a4fe0d4e","impliedFormat":99},{"version":"9866369eb72b6e77be2a92589c9df9be1232a1a66e96736170819e8a1297b61f","impliedFormat":99},{"version":"43abfbdf4e297868d780b8f4cfdd8b781b90ecd9f588b05e845192146a86df34","impliedFormat":99},{"version":"582419791241fb851403ae4a08d0712a63d4c94787524a7419c2bc8e0eb1b031","impliedFormat":99},{"version":"18437eeb932fe48590b15f404090db0ab3b32d58f831d5ffc157f63b04885ee5","impliedFormat":99},{"version":"0c5eaedf622d7a8150f5c2ec1f79ac3d51eea1966b0b3e61bfdea35e8ca213a7","impliedFormat":99},{"version":"fac39fc7a9367c0246de3543a6ee866a0cf2e4c3a8f64641461c9f2dac0d8aae","impliedFormat":99},{"version":"3b9f559d0200134f3c196168630997caedeadc6733523c8b6076a09615d5dec8","impliedFormat":99},{"version":"932af64286d9723da5ef7b77a0c4229829ce8e085e6bcc5f874cb0b83e8310d4","impliedFormat":99},{"version":"adeb9278f11f5561157feee565171c72fd48f5fe34ed06f71abf24e561fcaa1e","impliedFormat":99},{"version":"2269fef79b4900fc6b08c840260622ca33524771ff24fda5b9101ad98ea551f3","impliedFormat":99},{"version":"73d47498a1b73d5392d40fb42a3e7b009ae900c8423f4088c4faa663cc508886","impliedFormat":99},{"version":"7efc34cdc4da0968c3ba687bc780d5cacde561915577d8d1c1e46c7ac931d023","impliedFormat":99},{"version":"3c20a3bb0c50c819419f44aa55acc58476dad4754a16884cef06012d02b0722f","impliedFormat":99},{"version":"4569abf6bc7d51a455503670f3f1c0e9b4f8632a3b030e0794c61bfbba2d13be","impliedFormat":99},{"version":"98b2297b4dc1404078a54b61758d8643e4c1d7830af724f3ed2445d77a7a2d57","impliedFormat":99},{"version":"952ba89d75f1b589e07070fea2d8174332e3028752e76fd46e1c16cc51e6e2af","impliedFormat":99},{"version":"b6c9a2deefb6a57ff68d2a38d33c34407b9939487fc9ee9f32ba3ecf2987a88a","impliedFormat":99},{"version":"f6b371377bab3018dac2bca63e27502ecbd5d06f708ad7e312658d3b5315d948","impliedFormat":99},{"version":"31947dd8f1c8eeb7841e1f139a493a73bd520f90e59a6415375d0d8e6a031f01","impliedFormat":99},{"version":"95cd83b807e10b1af408e62caf5fea98562221e8ddca9d7ccc053d482283ddda","impliedFormat":99},{"version":"19287d6b76288c2814f1633bdd68d2b76748757ffd355e73e41151644e4773d6","impliedFormat":99},{"version":"fc4e6ec7dade5f9d422b153c5d8f6ad074bd9cc4e280415b7dc58fb5c52b5df1","impliedFormat":99},{"version":"3aea973106e1184db82d8880f0ca134388b6cbc420f7309d1c8947b842886349","impliedFormat":99},{"version":"765e278c464923da94dda7c2b281ece92f58981642421ae097862effe2bd30fa","impliedFormat":99},{"version":"de260bed7f7d25593f59e859bd7c7f8c6e6bb87e8686a0fcafa3774cb5ca02d8","impliedFormat":99},{"version":"b5c341ce978f5777fbe05bc86f65e9906a492fa6b327bda3c6aae900c22e76c6","impliedFormat":99},{"version":"686ddbfaf88f06b02c6324005042f85317187866ca0f8f4c9584dd9479653344","impliedFormat":99},{"version":"7f789c0c1db29dd3aab6e159d1ba82894a046bf8df595ac48385931ae6ad83e0","impliedFormat":99},{"version":"8eb3057d4fe9b59b2492921b73a795a2455ebe94ccb3d01027a7866612ead137","impliedFormat":99},{"version":"1e43c5d7aee1c5ec20611e28b5417f5840c75d048de9d7f1800d6808499236f8","impliedFormat":99},{"version":"d42610a5a2bee4b71769968a24878885c9910cd049569daa2d2ee94208b3a7a5","impliedFormat":99},{"version":"f6ed95506a6ed2d40ed5425747529befaa4c35fcbbc1e0d793813f6d725690fa","impliedFormat":99},{"version":"a6fcc1cd6583939506c906dff1276e7ebdc38fbe12d3e108ba38ad231bd18d97","impliedFormat":99},{"version":"ed13354f0d96fb6d5878655b1fead51722b54875e91d5e53ef16de5b71a0e278","impliedFormat":99},{"version":"1193b4872c1fb65769d8b164ca48124c7ebacc33eae03abf52087c2b29e8c46c","impliedFormat":99},{"version":"af682dfabe85688289b420d939020a10eb61f0120e393d53c127f1968b3e9f66","impliedFormat":99},{"version":"0dca04006bf13f72240c6a6a502df9c0b49c41c3cab2be75e81e9b592dcd4ea8","impliedFormat":99},{"version":"79d6ac4a2a229047259116688f9cd62fda25422dee3ad304f77d7e9af53a41ef","impliedFormat":99},{"version":"64534c17173990dc4c3d9388d16675a059aac407031cfce8f7fdffa4ee2de988","impliedFormat":99},{"version":"ba46d160a192639f3ca9e5b640b870b1263f24ac77b6895ab42960937b42dcbb","impliedFormat":99},{"version":"5e5ddd6fc5b590190dde881974ab969455e7fad61012e32423415ae3d085b037","impliedFormat":99},{"version":"1c16fd00c42b60b96fe0fa62113a953af58ddf0d93b0a49cb4919cf5644616f0","impliedFormat":99},{"version":"eb240c0e6b412c57f7d9a9f1c6cd933642a929837c807b179a818f6e8d3a4e44","impliedFormat":99},{"version":"4a7bde5a1155107fc7d9483b8830099f1a6072b6afda5b78d91eb5d6549b3956","impliedFormat":99},{"version":"3c1baaffa9a24cc7ef9eea6b64742394498e0616b127ca630aca0e11e3298006","impliedFormat":99},{"version":"87ca1c31a326c898fa3feb99ec10750d775e1c84dbb7c4b37252bcf3742c7b21","impliedFormat":99},{"version":"d7bd26af1f5457f037225602035c2d7e876b80d02663ab4ca644099ad3a55888","impliedFormat":99},{"version":"2ad0a6b93e84a56b64f92f36a07de7ebcb910822f9a72ad22df5f5d642aff6f3","impliedFormat":99},{"version":"523d1775135260f53f672264937ee0f3dc42a92a39de8bee6c48c7ea60b50b5a","impliedFormat":99},{"version":"e441b9eebbc1284e5d995d99b53ed520b76a87cab512286651c4612d86cd408e","impliedFormat":99},{"version":"76f853ee21425c339a79d28e0859d74f2e53dee2e4919edafff6883dd7b7a80f","impliedFormat":99},{"version":"00cf042cd6ba1915648c8d6d2aa00e63bbbc300ea54d28ed087185f0f662e080","impliedFormat":99},{"version":"f57e6707d035ab89a03797d34faef37deefd3dd90aa17d90de2f33dce46a2c56","impliedFormat":99},{"version":"cc8b559b2cf9380ca72922c64576a43f000275c72042b2af2415ce0fb88d7077","impliedFormat":99},{"version":"1a337ca294c428ba8f2eb01e887b28d080ee4a4307ae87e02e468b1d26af4a74","impliedFormat":99},{"version":"5a15362fc2e72765a908c0d4dd89e3ab3b763e8bc8c23f19234a709ecfd202fe","impliedFormat":99},{"version":"2dffdfe62ac8af0943853234519616db6fd8958fc7ff631149fd8364e663f361","impliedFormat":99},{"version":"5dbdb2b2229b5547d8177c34705272da5a10b8d0033c49efbc9f6efba5e617f2","impliedFormat":99},{"version":"6fc0498cd8823d139004baff830343c9a0d210c687b2402c1384fb40f0aa461c","impliedFormat":99},{"version":"8492306a4864a1dc6fc7e0cc0de0ae9279cbd37f3aae3e9dc1065afcdc83dddc","impliedFormat":99},{"version":"c011b378127497d6337a93f020a05f726db2c30d55dc56d20e6a5090f05919a6","impliedFormat":99},{"version":"f4556979e95a274687ae206bbab2bb9a71c3ad923b92df241d9ab88c184b3f40","impliedFormat":99},{"version":"50e82bb6e238db008b5beba16d733b77e8b2a933c9152d1019cf8096845171a4","impliedFormat":99},{"version":"d6011f8b8bbf5163ef1e73588e64a53e8bf1f13533c375ec53e631aad95f1375","impliedFormat":99},{"version":"693cd7936ac7acfa026d4bcb5801fce71cec49835ba45c67af1ef90dbfd30af7","impliedFormat":99},{"version":"195e2cf684ecddfc1f6420564535d7c469f9611ce7a380d6e191811f84556cd2","impliedFormat":99},{"version":"1dc6b6e7b2a7f2962f31c77f4713f3a5a132bbe14c00db75d557568fe82e4311","impliedFormat":99},{"version":"add93b1180e9aaac2dae4ef3b16f7655893e2ecbe62bd9e48366c305f0063d89","impliedFormat":99},{"version":"594bd896fe37c970aafb7a376ebeec4c0d636b62a5f611e2e27d30fb839ad8a5","impliedFormat":99},{"version":"b1c6a6faf60542ba4b4271db045d7faea56e143b326ef507d2797815250f3afc","impliedFormat":99},{"version":"8c8b165beb794260f462679329b131419e9f5f35212de11c4d53e6d4d9cbedf6","impliedFormat":99},{"version":"ee5a4cf57d49fcf977249ab73c690a59995997c4672bb73fcaaf2eed65dbd1b2","impliedFormat":99},{"version":"f9f36051f138ab1c40b76b230c2a12b3ce6e1271179f4508da06a959f8bee4c1","impliedFormat":99},{"version":"9dc2011a3573d271a45c12656326530c0930f92539accbec3531d65131a14a14","impliedFormat":99},{"version":"091521ce3ede6747f784ae6f68ad2ea86bbda76b59d2bf678bcad2f9d141f629","impliedFormat":99},{"version":"202c2be951f53bafe943fb2c8d1245e35ed0e4dfed89f48c9a948e4d186dd6d4","impliedFormat":99},{"version":"c618aead1d799dbf4f5b28df5a6b9ce13d72722000a0ec3fe90a8115b1ea9226","impliedFormat":99},{"version":"9b0bf59708549c3e77fddd36530b95b55419414f88bbe5893f7bc8b534617973","impliedFormat":99},{"version":"7e216f67c4886f1bde564fb4eebdd6b185f262fe85ad1d6128cad9b229b10354","impliedFormat":99},{"version":"cd51e60b96b4d43698df74a665aa7a16604488193de86aa60ec0c44d9f114951","impliedFormat":99},{"version":"b63341fb6c7ba6f2aeabd9fc46b43e6cc2d2b9eec06534cfd583d9709f310ec2","impliedFormat":99},{"version":"be2af50c81b15bcfe54ad60f53eb1c72dae681c72d0a9dce1967825e1b5830a3","impliedFormat":99},{"version":"be5366845dfb9726f05005331b9b9645f237f1ddc594c0def851208e8b7d297b","impliedFormat":99},{"version":"5ddd536aaeadd4bf0f020492b3788ed209a7050ce27abec4e01c7563ff65da81","impliedFormat":99},{"version":"e243b24da119c1ef0d79af2a45217e50682b139cb48e7607efd66cc01bd9dcda","impliedFormat":99},{"version":"5b1398c8257fd180d0bf62e999fe0a89751c641e87089a83b24392efda720476","impliedFormat":99},{"version":"1588b1359f8507a16dbef67cd2759965fc2e8d305e5b3eb71be5aa9506277dff","impliedFormat":99},{"version":"4c99f2524eee1ec81356e2b4f67047a4b7efaf145f1c4eb530cd358c36784423","impliedFormat":99},{"version":"b30c6b9f6f30c35d6ef84daed1c3781e367f4360171b90598c02468b0db2fc3d","impliedFormat":99},{"version":"79c0d32274ccfd45fae74ac61d17a2be27aea74c70806d22c43fc625b7e9f12a","impliedFormat":99},{"version":"1b7e3958f668063c9d24ac75279f3e610755b0f49b1c02bb3b1c232deb958f54","impliedFormat":99},{"version":"779d4022c3d0a4df070f94858a33d9ebf54af3664754536c4ce9fd37c6f4a8db","impliedFormat":99},{"version":"e662f063d46aa8c088edffdf1d96cb13d9a2cbf06bc38dc6fc62b4d125fb7b49","impliedFormat":99},{"version":"d1d612df1e41c90d9678b07740d13d4f8e6acec2f17390d4ff4be5c889a6d37d","impliedFormat":99},{"version":"c95933fe140918892d569186f17b70ef6b1162f851a0f13f6a89e8f4d599c5a1","impliedFormat":99},{"version":"1d8d30677f87c13c2786980a80750ac1e281bdb65aa013ea193766fe9f0edd74","impliedFormat":99},{"version":"4661673cbc984b8a6ee5e14875a71ed529b64e7f8e347e12c0db4cecc25ad67d","impliedFormat":99},{"version":"7f980a414274f0f23658baa9a16e21d828535f9eac538e2eab2bb965325841db","impliedFormat":99},{"version":"20fb747a339d3c1d4a032a31881d0c65695f8167575e01f222df98791a65da9b","impliedFormat":99},{"version":"dd4e7ebd3f205a11becf1157422f98db675a626243d2fbd123b8b93efe5fb505","impliedFormat":99},{"version":"43ec6b74c8d31e88bb6947bb256ad78e5c6c435cbbbad991c3ff39315b1a3dba","impliedFormat":99},{"version":"b27242dd3af2a5548d0c7231db7da63d6373636d6c4e72d9b616adaa2acef7e1","impliedFormat":99},{"version":"e0ee7ba0571b83c53a3d6ec761cf391e7128d8f8f590f8832c28661b73c21b68","impliedFormat":99},{"version":"072bfd97fc61c894ef260723f43a416d49ebd8b703696f647c8322671c598873","impliedFormat":99},{"version":"e70875232f5d5528f1650dd6f5c94a5bed344ecf04bdbb998f7f78a3c1317d02","impliedFormat":99},{"version":"8e495129cb6cd8008de6f4ff8ce34fe1302a9e0dcff8d13714bd5593be3f7898","impliedFormat":99},{"version":"0345bc0b1067588c4ea4c48e34425d3284498c629bc6788ebc481c59949c9037","impliedFormat":99},{"version":"e30f5b5d77c891bc16bd65a2e46cd5384ea57ab3d216c377f482f535db48fc8f","impliedFormat":99},{"version":"f113afe92ee919df8fc29bca91cab6b2ffbdd12e4ac441d2bb56121eb5e7dbe3","impliedFormat":99},{"version":"49d567cc002efb337f437675717c04f207033f7067825b42bb59c9c269313d83","impliedFormat":99},{"version":"1d248f707d02dc76555298a934fba0f337f5028bb1163ce59cd7afb831c9070f","impliedFormat":99},{"version":"5d8debffc9e7b842dc0f17b111673fe0fc0cca65e67655a2b543db2150743385","impliedFormat":99},{"version":"5fccbedc3eb3b23bc6a3a1e44ceb110a1f1a70fa8e76941dce3ae25752caa7a9","impliedFormat":99},{"version":"f4031b95f3bab2b40e1616bd973880fb2f1a97c730bac5491d28d6484fac9560","impliedFormat":99},{"version":"dbe75b3c5ed547812656e7945628f023c4cd0bc1879db0db3f43a57fb8ec0e2b","impliedFormat":99},{"version":"b754718a546a1939399a6d2a99f9022d8a515f2db646bab09f7d2b5bff3cbb82","impliedFormat":99},{"version":"2eef10fb18ed0b4be450accf7a6d5bcce7b7f98e02cac4e6e793b7ad04fc0d79","impliedFormat":99},{"version":"c46f471e172c3be12c0d85d24876fedcc0c334b0dab48060cdb1f0f605f09fed","impliedFormat":99},{"version":"7d6ddeead1d208588586c58c26e4a23f0a826b7a143fb93de62ed094d0056a33","impliedFormat":99},{"version":"7c5782291ff6e7f2a3593295681b9a411c126e3736b83b37848032834832e6b9","impliedFormat":99},{"version":"3a3f09df6258a657dd909d06d4067ee360cd2dccc5f5d41533ae397944a11828","impliedFormat":99},{"version":"ea54615be964503fec7bce04336111a6fa455d3e8d93d44da37b02c863b93eb8","impliedFormat":99},{"version":"2a83694bc3541791b64b0e57766228ea23d92834df5bf0b0fcb93c5bb418069c","impliedFormat":99},{"version":"b5913641d6830e7de0c02366c08b1d26063b5758132d8464c938e78a45355979","impliedFormat":99},{"version":"46c095d39c1887979d9494a824eda7857ec13fb5c20a6d4f7d02c2975309bf45","impliedFormat":99},{"version":"f6e02ca076dc8e624aa38038e3488ebd0091e2faea419082ed764187ba8a6500","impliedFormat":99},{"version":"4d49e8a78aba1d4e0ad32289bf8727ae53bc2def9285dff56151a91e7d770c3e","impliedFormat":99},{"version":"63315cf08117cc728eab8f3eec8801a91d2cd86f91d0ae895d7fd928ab54596d","impliedFormat":99},{"version":"a14a6f3a5636bcaebfe9ec2ccfa9b07dc94deb1f6c30358e9d8ea800a1190d5e","impliedFormat":99},{"version":"21206e7e81876dabf2a7af7aa403f343af1c205bdcf7eff24d9d7f4eee6214c4","impliedFormat":99},{"version":"cd0a9f0ffec2486cad86b7ef1e4da42953ffeb0eb9f79f536e16ff933ec28698","impliedFormat":99},{"version":"f609a6ec6f1ab04dba769e14d6b55411262fd4627a099e333aa8876ea125b822","impliedFormat":99},{"version":"6d8052bb814be030c64cb22ca0e041fe036ad3fc8d66208170f4e90d0167d354","impliedFormat":99},{"version":"851f72a5d3e8a2bf7eeb84a3544da82628f74515c92bdf23c4a40af26dcc1d16","impliedFormat":99},{"version":"59692a7938aab65ea812a8339bbc63c160d64097fe5a457906ea734d6f36bcd4","impliedFormat":99},{"version":"8cb3b95e610c44a9986a7eab94d7b8f8462e5de457d5d10a0b9c6dd16bde563b","impliedFormat":99},{"version":"f571713abd9a676da6237fe1e624d2c6b88c0ca271c9f1acc1b4d8efeea60b66","impliedFormat":99},{"version":"16c5d3637d1517a3d17ed5ebcfbb0524f8a9997a7b60f6100f7c5309b3bb5ac8","impliedFormat":99},{"version":"ca1ec669726352c8e9d897f24899abf27ad15018a6b6bcf9168d5cd1242058ab","impliedFormat":99},{"version":"bffb1b39484facf6d0c5d5feefe6c0736d06b73540b9ce0cf0f12da2edfd8e1d","impliedFormat":99},{"version":"f1663c030754f6171b8bb429096c7d2743282de7733bccd6f67f84a4c588d96e","impliedFormat":99},{"version":"dd09693285e58504057413c3adc84943f52b07d2d2fd455917f50fa2a63c9d69","impliedFormat":99},{"version":"d94c94593d03d44a03810a85186ae6d61ebeb3a17a9b210a995d85f4b584f23d","impliedFormat":99},{"version":"c7c3bf625a8cb5a04b1c0a2fbe8066ecdbb1f383d574ca3ffdabe7571589a935","impliedFormat":99},{"version":"7a2f39a4467b819e873cd672c184f45f548511b18f6a408fe4e826136d0193bb","impliedFormat":99},{"version":"f8a0ae0d3d4993616196619da15da60a6ec5a7dfaf294fe877d274385eb07433","impliedFormat":99},{"version":"2cca80de38c80ef6c26deb4e403ca1ff4efbe3cf12451e26adae5e165421b58d","impliedFormat":99},{"version":"0070d3e17aa5ad697538bf865faaff94c41f064db9304b2b949eb8bcccb62d34","impliedFormat":99},{"version":"53df93f2db5b7eb8415e98242c1c60f6afcac2db44bce4a8830c8f21eee6b1dd","impliedFormat":99},{"version":"d67bf28dc9e6691d165357424c8729c5443290367344263146d99b2f02a72584","impliedFormat":99},{"version":"932557e93fbdf0c36cc29b9e35950f6875425b3ac917fa0d3c7c2a6b4f550078","impliedFormat":99},{"version":"e3dc7ec1597fb61de7959335fb7f8340c17bebf2feb1852ed8167a552d9a4a25","impliedFormat":99},{"version":"b64e15030511c5049542c2e0300f1fe096f926cf612662884f40227267f5cd9f","impliedFormat":99},{"version":"1932796f09c193783801972a05d8fb1bfef941bb46ac76fbe1abb0b3bfb674fa","impliedFormat":99},{"version":"d9575d5787311ee7d61ad503f5061ebcfaf76b531cfecce3dc12afb72bb2d105","impliedFormat":99},{"version":"5b41d96c9a4c2c2d83f1200949f795c3b6a4d2be432b357ad1ab687e0f0de07c","impliedFormat":99},{"version":"38ec829a548e869de4c5e51671245a909644c8fb8e7953259ebb028d36b4dd06","impliedFormat":99},{"version":"20c2c5e44d37dac953b516620b5dba60c9abd062235cdf2c3bfbf722d877a96b","impliedFormat":99},{"version":"875fe6f7103cf87c1b741a0895fda9240fed6353d5e7941c8c8cbfb686f072b4","impliedFormat":99},{"version":"c0ccccf8fbcf5d95f88ed151d0d8ce3015aa88cf98d4fd5e8f75e5f1534ee7ae","impliedFormat":99},{"version":"1b1f4aba21fd956269ced249b00b0e5bfdbd5ebd9e628a2877ab1a2cf493c919","impliedFormat":99},{"version":"939e3299952dff0869330e3324ba16efe42d2cf25456d7721d7f01a43c1b0b34","impliedFormat":99},{"version":"f0a9b52faec508ba22053dedfa4013a61c0425c8b96598cef3dea9e4a22637c6","impliedFormat":99},{"version":"d5b302f50db61181adc6e209af46ae1f27d7ef3d822de5ea808c9f44d7d219fd","impliedFormat":99},{"version":"19131632ba492c83e8eeadf91a481def0e0b39ffc3f155bc20a7f640e0570335","impliedFormat":99},{"version":"4581c03abea21396c3e1bb119e2fd785a4d91408756209cbeed0de7070f0ab5b","impliedFormat":99},{"version":"ebcd3b99e17329e9d542ef2ccdd64fddab7f39bc958ee99bbdb09056c02d6e64","impliedFormat":99},{"version":"4b148999deb1d95b8aedd1a810473a41d9794655af52b40e4894b51a8a4e6a6d","impliedFormat":99},{"version":"1781cc99a0f3b4f11668bb37cca7b8d71f136911e87269e032f15cf5baa339bf","impliedFormat":99},{"version":"33f1b7fa96117d690035a235b60ecd3cd979fb670f5f77b08206e4d8eb2eb521","impliedFormat":99},{"version":"01429b306b94ff0f1f5548ce5331344e4e0f5872b97a4776bd38fd2035ad4764","impliedFormat":99},{"version":"c1bc4f2136de7044943d784e7a18cb8411c558dbb7be4e4b4876d273cbd952af","impliedFormat":99},{"version":"5470f84a69b94643697f0d7ec2c8a54a4bea78838aaa9170189b9e0a6e75d2cf","impliedFormat":99},{"version":"36aaa44ee26b2508e9a6e93cd567e20ec700940b62595caf962249035e95b5e3","impliedFormat":99},{"version":"f8343562f283b7f701f86ad3732d0c7fd000c20fe5dc47fa4ed0073614202b4d","impliedFormat":99},{"version":"a53c572630a78cd99a25b529069c1e1370f8a5d8586d98e798875f9052ad7ad1","impliedFormat":99},{"version":"4ad3451d066711dde1430c544e30e123f39e23c744341b2dfd3859431c186c53","impliedFormat":99},{"version":"8069cbef9efa7445b2f09957ffbc27b5f8946fdbade4358fb68019e23df4c462","impliedFormat":99},{"version":"cd8b4e7ad04ba9d54eb5b28ac088315c07335b837ee6908765436a78d382b4c3","impliedFormat":99},{"version":"d533d8f8e5c80a30c51f0cbfe067b60b89b620f2321d3a581b5ba9ac8ffd7c3a","impliedFormat":99},{"version":"33f49f22fdda67e1ddbacdcba39e62924793937ea7f71f4948ed36e237555de3","impliedFormat":99},{"version":"710c31d7c30437e2b8795854d1aca43b540cb37cefd5900f09cfcd9e5b8540c4","impliedFormat":99},{"version":"b2c03a0e9628273bc26a1a58112c311ffbc7a0d39938f3878837ab14acf3bc41","impliedFormat":99},{"version":"a93beb0aa992c9b6408e355ea3f850c6f41e20328186a8e064173106375876c2","impliedFormat":99},{"version":"efdcba88fcd5421867898b5c0e8ea6331752492bd3547942dea96c7ebcb65194","impliedFormat":99},{"version":"a98e777e7a6c2c32336a017b011ba1419e327320c3556b9139413e48a8460b9a","impliedFormat":99},{"version":"ea44f7f8e1fe490516803c06636c1b33a6b82314366be1bd6ffa4ba89bc09f86","impliedFormat":99},{"version":"c25f22d78cc7f46226179c33bef0e4b29c54912bde47b62e5fdaf9312f22ffcb","impliedFormat":99},{"version":"d57579cfedc5a60fda79be303080e47dfe0c721185a5d95276523612228fcefc","impliedFormat":99},{"version":"a41630012afe0d4a9ff14707f96a7e26e1154266c008ddbd229e3f614e4d1cf7","impliedFormat":99},{"version":"298a858633dfa361bb8306bbd4cfd74f25ab7cc20631997dd9f57164bc2116d1","impliedFormat":99},{"version":"921782c45e09940feb232d8626a0b8edb881be2956520c42c44141d9b1ddb779","impliedFormat":99},{"version":"06117e4cc7399ce1c2b512aa070043464e0561f956bda39ef8971a2fcbcdbf2e","impliedFormat":99},{"version":"daccf332594b304566c7677c2732fed6e8d356da5faac8c5f09e38c2f607a4ab","impliedFormat":99},{"version":"4386051a0b6b072f35a2fc0695fecbe4a7a8a469a1d28c73be514548e95cd558","impliedFormat":99},{"version":"78e41de491fe25947a7fd8eeef7ebc8f1c28c1849a90705d6e33f34b1a083b90","impliedFormat":99},{"version":"3ccd198e0a693dd293ed22e527c8537c76b8fe188e1ebf20923589c7cfb2c270","impliedFormat":99},{"version":"2ebf2ee015d5c8008428493d4987e2af9815a76e4598025dd8c2f138edc1dcae","impliedFormat":99},{"version":"0dcc8f61382c9fcdafd48acc54b6ffda69ca4bb7e872f8ad12fb011672e8b20c","impliedFormat":99},{"version":"9db563287eb527ead0bcb9eb26fbec32f662f225869101af3cabcb6aee9259cf","impliedFormat":99},{"version":"068489bec523be43f12d8e4c5c337be4ff6a7efb4fe8658283673ae5aae14b85","impliedFormat":99},{"version":"838212d0dc5b97f7c5b5e29a89953de3906f72fce13c5ae3c5ade346f561d226","impliedFormat":99},{"version":"ddc78d29af824ad7587152ea523ed5d60f2bc0148d8741c5dacf9b5b44587b1b","impliedFormat":99},{"version":"019b522e3783e5519966927ceeb570eefcc64aba3f9545828a5fb4ae1fde53c6","impliedFormat":99},{"version":"b34623cc86497a5123de522afba770390009a56eebddba38d2aa5798b70b0a87","impliedFormat":99},{"version":"afb9b4c8bd38fb43d38a674de56e6f940698f91114fded0aa119de99c6cd049a","impliedFormat":99},{"version":"1d277860f19b8825d027947fca9928ee1f3bfaa0095e85a97dd7a681b0698dfc","impliedFormat":99},{"version":"6d32122bb1e7c0b38b6f126d166dff1f74c8020f8ba050248d182dcafc835d08","impliedFormat":99},{"version":"cfac5627d337b82d2fbeff5f0f638b48a370a8d72d653327529868a70c5bc0f8","impliedFormat":99},{"version":"8a826bc18afa4c5ed096ceb5d923e2791a5bae802219e588a999f535b1c80492","impliedFormat":99},{"version":"c860264bd6e0582515237f063a972018328d579ae3c0869cc2c4c9cf2f78cef0","impliedFormat":99},{"version":"d30a4b50cdf27ceaa58e72b9a4c6b57167e33a4a57a5fbd5c0b48cc541f21b07","impliedFormat":99},{"version":"73e94021c55ab908a1b8c53792e03bf7e0d195fee223bdc5567791b2ccbfcdec","impliedFormat":99},{"version":"5f73eb47b37f3a957fe2ac6fe654648d60185908cab930fc01c31832a5cb4b10","impliedFormat":99},{"version":"cb6372a2460010a342ba39e06e1dcfd722e696c9d63b4a71577f9a3c72d09e0a","impliedFormat":99},{"version":"6d6530e4c5f2a8d03d3e85c57030a3366c8f24198fbc7860beed9f5a35d0ad41","impliedFormat":99},{"version":"8220978de3ee01a62309bb4190fa664f34932549ff26249c92799dd58709a693","impliedFormat":99},{"version":"ac12a6010ff501e641f5a8334b8eaf521d0e0739a7e254451b6eea924c3035c7","impliedFormat":99},{"version":"97395d1e03af4928f3496cc3b118c0468b560765ab896ce811acb86f6b902b5c","impliedFormat":99},{"version":"7dcfbd6a9f1ce1ddf3050bd469aa680e5259973b4522694dc6291afe20a2ae28","impliedFormat":99},{"version":"433808ed82cf5ed8643559ea41427644e245934db5871e6b97ce49660790563e","impliedFormat":99},{"version":"efc225581aae9bb47d421a1b9f278db0238bc617b257ce6447943e59a2d1621e","impliedFormat":99},{"version":"14891c20f15be1d0d42ecbbd63de1c56a4d745e3ea2b4c56775a4d5d36855630","impliedFormat":99},{"version":"8833b88e26156b685bc6f3d6a014c2014a878ffbd240a01a8aee8a9091014e9c","impliedFormat":99},{"version":"7a2a42a1ac642a9c28646731bd77d9849cb1a05aa1b7a8e648f19ab7d72dd7dc","impliedFormat":99},{"version":"4d371c53067a3cc1a882ff16432b03291a016f4834875b77169a2d10bb1b023e","impliedFormat":99},{"version":"99b38f72e30976fd1946d7b4efe91aa227ecf0c9180e1dd6502c1d39f37445b4","impliedFormat":99},{"version":"df1bcf0b1c413e2945ce63a67a1c5a7b21dbbec156a97d55e9ea0eed90d2c604","impliedFormat":99},{"version":"6ea03bed678f170906ddf586aa742b3c122d550a8d48431796ab9081ae3b5c53","impliedFormat":99},{"version":"b4bfa90fac90c6e0d0185d2fe22f059fec67587cc34281f62294f9c4615a8082","impliedFormat":99},{"version":"b2d8bb61a776b0a150d987a01dd8411778c5ba701bb9962a0c6c3d356f590ee3","impliedFormat":99},{"version":"5ae6642588e4a72e5a62f6111cb750820034a7fbe56b5d8ec2bcb29df806ce52","impliedFormat":99},{"version":"6fca09e1abc83168caf36b751dec4ddda308b5714ec841c3ff0f3dc07b93c1b8","impliedFormat":99},{"version":"9a07957f75128ed0be5fc8a692a14da900878d5d5c21880f7c08f89688354aa4","impliedFormat":99},{"version":"8b6f3ae84eab35c50cf0f1b608c143fe95f1f765df6f753cd5855ae61b3efbe2","impliedFormat":99},{"version":"2f7268e6ac610c7122b6b416e34415ce42b51c56d080bef41786d2365f06772d","impliedFormat":99},{"version":"992491d83ff2d1e7f64a8b9117daee73724af13161f1b03171f0fa3ffe9b4e3e","impliedFormat":99},{"version":"7ca2d1a25dc4d0f1e0f1b640c0d6642087bef42c574b3fb08b172a1473776573","impliedFormat":99},{"version":"fc2266585e8f1f37e8c63d770c8e97312603006cada6c35967895500d86f8946","impliedFormat":99},{"version":"9409ac347c5779f339112000d7627f17ede6e39b0b6900679ce5454d3ad2e3c9","impliedFormat":99},{"version":"e55a1f6b198a39e38a3cea3ffe916aab6fde7965c827db3b8a1cacf144a67cd9","impliedFormat":99},{"version":"684a5c26ce2bb7956ef6b21e7f2d1c584172cd120709e5764bc8b89bac1a10eb","impliedFormat":99},{"version":"1bb71468bf39937ba312a4c028281d0c21cab9fcce9bb878bef3255cd4b7139a","impliedFormat":99},{"version":"ec9f43bbad5eca0a0397047c240e583bad29d538482824b31859baf652525869","impliedFormat":99},{"version":"9e9306805809074798cb780757190b896c347c733c101c1ef315111389dd37a0","impliedFormat":99},{"version":"66e486a9c9a86154dc9780f04325e61741f677713b7e78e515938bf54364fee2","impliedFormat":99},{"version":"33f3bdf398cc10f1c71ae14f574ad3f6d7517fe125e29608a092146b55cf1928","impliedFormat":99},{"version":"79741c2b730c696e7ae3a827081bf11da74dd079af2dbd8f7aa5af1ae80f0edd","impliedFormat":99},{"version":"2f372a4a84d0bc0709edca56f3c446697d60eadb601069b70625857a955b7a28","impliedFormat":99},{"version":"80b9fc3ad8d908bf1f97906538a90f6c55bd661c078423dfee2a46484baf252f","impliedFormat":99},{"version":"17ac6db33d189dce6d9bdb531bbdb74ad15a04991c5ecad23a642cb310055ebb","impliedFormat":99},{"version":"684bb74763606c640fe3dee0e7b9c34297b5af6aa6ceb3b265f360d39051df94","impliedFormat":99},{"version":"20c66936bdbdf6938b49053377bceea1009f491d89c2dff81efa36812a85f298","impliedFormat":99},{"version":"0c06897f7ab3830cef0701e0e083b2c684ed783ae820b306aedd501f32e9562d","impliedFormat":99},{"version":"d2a8cbeb0c0caaf531342062b4b5c227118862879f6a25033e31fad00797b7eb","impliedFormat":99},{"version":"ff786f7adefa284524a03652f2e9c2ba92e247ba6ac2f9ca81dd12f0dd4350a3","impliedFormat":99},{"version":"d128bdf00f8418209ebf75ee36d586bd7d66eb0075a8ac0f0ddf64ceb9d40a70","impliedFormat":99},{"version":"da572a1162f092f64641b8676fcfd735e4b6ca301572ec41361402842a416298","impliedFormat":99},{"version":"0d558d19c9cc65c1acfd2e209732a145aaef859082c0289ccd8a61d0cce6be46","impliedFormat":99},{"version":"c711ce68b0eabf9cfce8d871379d7c19460aa55b9d04c5e76a48623e01637697","impliedFormat":99},{"version":"56cc6eae48fd08fa709cf9163d01649f8d24d3fea5806f488d2b1b53d25e1d6c","impliedFormat":99},{"version":"57a925b13947b38c34277d93fb1e85d6f03f47be18ca5293b14082a1bd4a48f5","impliedFormat":99},{"version":"9d9d64c1fa76211dd529b6a24061b8d724e2110ee55d3829131bca47f3fe4838","impliedFormat":99},{"version":"c13042e244bb8cf65586e4131ef7aed9ca33bf1e029a43ed0ebab338b4465553","impliedFormat":99},{"version":"54be9b9c71a17cb2519b841fad294fa9dc6e0796ed86c8ac8dd9d8c0d1c3a631","impliedFormat":99},{"version":"10881be85efd595bef1d74dfa7b9a76a5ab1bfed9fb4a4ca7f73396b72d25b90","impliedFormat":99},{"version":"925e71eaa87021d9a1215b5cf5c5933f85fe2371ddc81c32d1191d7842565302","impliedFormat":99},{"version":"faed0b3f8979bfbfb54babcff9d91bd51fda90931c7716effa686b4f30a09575","impliedFormat":99},{"version":"53c72d68328780f711dbd39de7af674287d57e387ddc5a7d94f0ffd53d8d3564","impliedFormat":99},{"version":"51129924d359cdebdccbf20dbabc98c381b58bfebe2457a7defed57002a61316","impliedFormat":99},{"version":"7270a757071e3bc7b5e7a6175f1ac9a4ddf4de09f3664d80cb8805138f7d365b","impliedFormat":99},{"version":"57ae71d27ee71b7d1f2c6d867ddafbbfbaa629ad75565e63a508dbaa3ef9f859","impliedFormat":99},{"version":"60924ca0c60f0674f208bfa1eaaa54e6973ced7650df7c7a81ae069730ef665a","impliedFormat":99},{"version":"e3181c7595a89dd03ba9a20eb5065fa37e0b0a514261bed774f6ae2241634470","impliedFormat":99},{"version":"c42d5cbf94816659c01f7c2298d0370247f1a981f8ca6370301b7a03b3ced950","impliedFormat":99},{"version":"298e6b838ba9f5dcbe3a8020199b3e9b9a7963b89db618b82dda33926c35316d","impliedFormat":99},{"version":"dafbd4199902d904e3d4a233b5faf5dc4c98847fcd8c0ddd7617b2aed50e90d8","impliedFormat":99},{"version":"9fc866f9783d12d0412ed8d68af5e4c9e44f0072d442b0c33c3bda0a5c8cae15","impliedFormat":99},{"version":"5fc13d24a2d0328eac00c4e73cc052a987fbced2151bc0d3b7eb8f3ba4d0f4e2","impliedFormat":99},{"version":"aa2bbf1de7e44753a03266534f185fdf880bd2a17b63b88972c5d14885d90944","impliedFormat":99},"e7bc682eff8f2215e6b0aab40d1afb7ee927c5f5bef348ccff207631b9261658","625fce5e16577a45108a86d7afcd70b8931cb2032abe0d6a3faf1010f666d672",{"version":"e516240bc1e5e9faef055432b900bc0d3c9ca7edce177fdabbc6c53d728cced8","impliedFormat":99},{"version":"5402765feacf44e052068ccb4535a346716fa1318713e3dae1af46e1e85f29a9","impliedFormat":99},{"version":"e16ec5d4796e7a765810efee80373675cedc4aa4814cf7272025a88addf5f0be","impliedFormat":99},{"version":"1f57157fcd45f9300c6efcfc53e2071fbe43396b0a7ed2701fbd1efb5599f07f","impliedFormat":99},{"version":"9f1886f3efddfac35babcada2d454acd4e23164345d11c979966c594af63468b","impliedFormat":99},{"version":"a3541c308f223863526df064933e408eba640c0208c7345769d7dc330ad90407","impliedFormat":99},{"version":"59af208befeb7b3c9ab0cb6c511e4fec54ede11922f2ffb7b497351deaf8aa2e","impliedFormat":99},{"version":"928b16f344f6cddaba565da8238f4cf2ddf12fe03eb426ab46a7560e9b3078fa","impliedFormat":99},{"version":"120bdf62bccef4ea96562a3d30dd60c9d55481662f5cf31c19725f56c0056b34","impliedFormat":99},{"version":"39e0da933908de42ba76ea1a92e4657305ae195804cfaa8760664e80baac2d6a","impliedFormat":99},{"version":"55ce6ca8df9d774d60cef58dd5d716807d5cc8410b8b065c06d3edac13f2e726","impliedFormat":99},{"version":"788a0faf3f28d43ce3793b4147b7539418a887b4a15a00ffb037214ed8f0b7f6","impliedFormat":99},{"version":"a3e66e7b8ccdab967cd4ada0f178151f1c42746eabb589a06958482fd4ed354e","impliedFormat":99},{"version":"bf45a2964a872c9966d06b971d0823daecbd707f97e927f2368ba54bb1b13a90","impliedFormat":99},{"version":"39973a12c57e06face646fb79462aabe8002e5523eec4e86e399228eb34b32c9","impliedFormat":99},{"version":"f01091e9b5028acfb38208113ae051fad8a0b4b8ec1f7137a2a5cf903c47eefc","impliedFormat":99},{"version":"b3e87824c9e7e3a3be7f76246e45c8d603ce83d116733047200b3aa95875445b","impliedFormat":99},{"version":"7e1f7f9ae14e362d41167dc861be6a8d76eca30dde3a9893c42946dc5a5fc686","impliedFormat":99},{"version":"9308ef3b9433063ac753a55c3f36d6d89fa38a8e6c51e05d9d8329c7f1174f24","impliedFormat":99},{"version":"cd3bb1aa24726a0abd67558fde5759fe968c3c6aa3ec7bad272e718851502894","impliedFormat":99},{"version":"1ae0f22c3b8420b5c2fec118f07b7ebd5ae9716339ab3477f63c603fe7a151c8","impliedFormat":99},{"version":"919ff537fff349930acc8ad8b875fd985a17582fb1beb43e2f558c541fd6ecd9","impliedFormat":99},{"version":"4e67811e45bae6c44bd6f13a160e4188d72fd643665f40c2ac3e8a27552d3fd9","impliedFormat":99},{"version":"3d1450fd1576c1073f6f4db9ebae5104e52e2c4599afb68d7d6c3d283bdbaf4f","impliedFormat":99},{"version":"c072af873c33ff11af126c56a846dfada32461b393983a72b6da7bff373e0002","impliedFormat":99},{"version":"de66e997ea5376d4aeb16d77b86f01c7b7d6d72fbb738241966459d42a4089e0","impliedFormat":99},{"version":"d77ea3b91e4bc44d710b7c9487c2c6158e8e5a3439d25fc578befeb27b03efd7","impliedFormat":99},{"version":"a3d5c695c3d1ebc9b0bd55804afaf2ac7c97328667cbeedf2c0861b933c45d3e","impliedFormat":99},{"version":"270724545d446036f42ddea422ee4d06963db1563ccc5e18b01c76f6e67968ae","impliedFormat":99},{"version":"85441c4f6883f7cfd1c5a211c26e702d33695acbabec8044e7fa6831ed501b45","impliedFormat":99},{"version":"0f268017a6b1891fdeea69c2a11d576646d7fd9cdfc8aac74d003cd7e87e9c5a","impliedFormat":99},{"version":"9ece188c336c80358742a5a0279f2f550175f5a07264349d8e0ce64db9701c0b","impliedFormat":99},{"version":"cf41b0fc7d57643d1a8d21af07b0247db2f2d7e2391c2e55929e9c00fbe6ab9a","impliedFormat":99},{"version":"11e7ddddd9eddaac56a6f23d8699ae7a94c2a55ae8c986fdabc719d3c3e875a1","impliedFormat":99},{"version":"dd129c2d348be7dbf9f15d34661defdfc11ee00628ca6f7161bead46095c6bc3","impliedFormat":99},{"version":"c38d8e7cfc64bbfc14a63346388249c1cfa2cc02166c5f37e5a57da4790ce27f","impliedFormat":99},"7444126492d4e0b85b1270f0d83a0955350fa95b3d3fb7b88edc2a5c2b4e5d14",{"version":"56208c500dcb5f42be7e18e8cb578f257a1a89b94b3280c506818fed06391805","impliedFormat":1},{"version":"0c94c2e497e1b9bcfda66aea239d5d36cd980d12a6d9d59e66f4be1fa3da5d5a","impliedFormat":1},{"version":"eb9271b3c585ea9dc7b19b906a921bf93f30f22330408ffec6df6a22057f3296","impliedFormat":1},{"version":"aa4a927d0c7239dff845a64e676c71aeed2bbda89a7fb486baab22eb7688ba1d","impliedFormat":1},{"version":"340a990742a00862049b378aaa482b5bb8323d443c799dded51ce711f4f8eb51","impliedFormat":1},{"version":"89eeeebbc612a079c6e7ebe0bde08e06fbc46cfeaebf6157ea3051ed55967b10","impliedFormat":1},{"version":"4c72f66622e266b542fb097f4d1fe88eb858b88b98414a13ef3dd901109e03a1","impliedFormat":1},{"version":"23a933d83f3a8d595b35f3827c5e68239fb4f6eb44e96389269d183fe7ff09ba","impliedFormat":1},{"version":"2acad3ae616a9fb5a8c3d4d7bb5edb11d1d0102372ee939e7fc64359fec4046e","impliedFormat":1},{"version":"c812eabb7d2e13c8e72e216208448f92341a4094dd107cbb0bdb2cb23d1a83e7","impliedFormat":1},{"version":"f734b58ea162765ff4d4a36f671ee06da898921e985a2064510f4925ec1ed062","affectsGlobalScope":true,"impliedFormat":1},{"version":"57658b130a56abe2ba4f7eb1490038f0b31aa2f3cc2e42d24032626fcf6b1be3","impliedFormat":1},{"version":"9c5c84c449a3d74e417343410ba9f1bd8bfeb32abd16945a1b3d0592ded31bc8","impliedFormat":1},{"version":"a7f09d2aaf994dbfd872eda4f2411d619217b04dbe0916202304e7a3d4b0f5f8","impliedFormat":1},{"version":"86ac756569f83cf0571646c916b546634652e92a775e964304912ecafa81dc42","impliedFormat":99},{"version":"a7f23fecdccf1504dae27c359db676d0a1fbaaeb400b55959078924e4c3a4992","impliedFormat":1},{"version":"bee66a62aa1da254412bb2c3c8c1a0dd12efea0722d35cc6ea7b5fdaa6778fd1","impliedFormat":1},{"version":"05d80364872e31465f8a1eaf2697e4fc418f78aa336f4cea68620a23f1379f6f","impliedFormat":1},{"version":"7345ba3b9eb2182d8cdc4c961b62847c3c9918985179ddefd5ca58a80d8b9e6a","impliedFormat":1},{"version":"81c4a0e6de3d5674ec3a721e04b3eb3244180bda86a22c4185ecac0e3f051cd8","impliedFormat":1},{"version":"39975a01d837394bcac2559639e88ecdc4cfd22433327b46ea6f78eb2c584813","impliedFormat":1},{"version":"7261cabedede09ebfd50e135af40be34f76fb9dbc617e129eaec21b00161ae86","impliedFormat":1},{"version":"ea554794a0d4136c5c6ea8f59ae894c3c0848b17848468a63ed5d3a307e148ae","impliedFormat":1},{"version":"2c378d9368abcd2eba8c29b294d40909845f68557bc0b38117e4f04fc56e5f9c","impliedFormat":1},{"version":"a843946c6e71a80d5940bd48c6bd0d5e08eb64d70206d774d4ffec4206775113","affectsGlobalScope":true,"impliedFormat":1},{"version":"c60b14c297cc569c648ddaea70bc1540903b7f4da416edd46687e88a543515a1","impliedFormat":1},{"version":"acfa00e5599216bcb8c9f3095e5fec4aeddfcc65aabe0eac7e8dbc51e33691c9","impliedFormat":1},{"version":"922d8f0f46dbe9fb80def96f7bcd9d5c1a6c0022d71023afa9eb7b45189d61f2","impliedFormat":1},{"version":"90588fb5ef85f4a8a4234e8062eb97bd3c8114dfb86a0c67f62685969222da8b","impliedFormat":1},{"version":"6ce50ada4bc9d2ad69927dce35cead36da337a618de0a2daaaeeafe38c692597","impliedFormat":1},{"version":"13b8d0a9b0493191f15d11a5452e7c523f811583a983852c1c8539ab2cfdae7c","impliedFormat":1},{"version":"8932771f941e3f8f153a950c65707d0611f30f577256aa59d4b92eda1c3d8f32","impliedFormat":1},{"version":"df6251bd4b5fad52759bfe96e8ab8f2ce625d0b6739b825209b263729a9c321e","impliedFormat":1},{"version":"846068dbe466864be6e2cae9993a4e3ac492a5cb05a36d5ce36e98690fde41f4","impliedFormat":1},{"version":"94c8c60f751015c8f38923e0d1ae32dd4780b572660123fa087b0cf9884a68a8","impliedFormat":1},{"version":"db8747c785df161ef65237bac36a7716168e5ebf18976ab16fd2fff69cf9c6ce","impliedFormat":1},{"version":"3085abdf921a6d225ad037c89eb2ba26a4c3b2c262f842dd3061949d1969b784","impliedFormat":1},{"version":"8e8f7b36675be31c4e9538529c30a552538c42ff866ba59fe70f23ba18479c5a","impliedFormat":1},{"version":"f4f7fbf0e5bf2097ddee2c998cca04b063f6f9cdcb255e728c0e85967119f9e5","impliedFormat":1},{"version":"c5b47653a15ec7c0bde956e77e5ca103ddc180d40eb4b311e4a024ef7c668fb0","impliedFormat":1},{"version":"223709d7c096b4e2bb00390775e43481426c370ac8e270de7e4c36d355fc8bc9","impliedFormat":1},{"version":"0528a80462b04f2f2ad8bee604fe9db235db6a359d1208f370a236e23fc0b1e0","impliedFormat":1},{"version":"17fb3716df78592be07500e9a90bd8c9424dd70c6201226886a8e71b9d2af396","impliedFormat":1},{"version":"82ef7d775e89b200380d8a14dc6af6d985a45868478773d98850ea2449f1be56","impliedFormat":1},{"version":"b86720947f763bbb869c2b183f8e58bca9fa089ed8f9c5a1574b2bea18cfbc02","impliedFormat":1},{"version":"fb7e20b94d23d989fa7c7d20fccebef31c1ef2d3d9ca179cadba6516e4e918ad","impliedFormat":1},{"version":"8326f735a1f0d2b4ad20539cda4e0d2e7c5fc0b534e3c0d503d5ed20a5711009","impliedFormat":1},{"version":"8d720cd4ee809af1d81f4ce88f02168568d5fded574d89875afd8fe7afd9549e","impliedFormat":1},{"version":"df87c2628c5567fd71dc0b765c845b0cbfef61e7c2e56961ac527bfb615ea639","impliedFormat":1},{"version":"659a83f1dd901de4198c9c2aa70e4a46a9bd0c41ce8a42ee26f2dbff5e86b1f3","impliedFormat":1},{"version":"1db5c2491eebd894eb9be03408601cddfe1b08357d021aeb86c3fb6c329a7843","impliedFormat":1},{"version":"224f85b48786de61fb0b018fbea89620ebec6289179daa78ed33c0f83014fc75","impliedFormat":1},{"version":"05fbfcb5c5c247a8b8a1d97dd8557c78ead2fff524f0b6380b4ac9d3e35249fb","impliedFormat":1},{"version":"322f70408b4e1f550ecc411869707764d8b28da3608e4422587630b366daf9de","impliedFormat":1},{"version":"acb93abc527fa52eb2adc5602a7c3c0949861f8e4317a187bb5c3372f872eff4","impliedFormat":1},{"version":"c4ef9e9e0fcb14b52c97ce847fb26a446b7d668d9db98a7de915a22c46f44c37","impliedFormat":1},{"version":"0e447b14e81b5b3e5d83cbea58b734850f78fb883f810e46d3dedba1a5124658","impliedFormat":1},{"version":"045f36d3a830b5ae1b7586492e1a2368d0e4b4209fa656f529fd6f6bb9ac7ced","impliedFormat":1},{"version":"929939785efdef0b6781b7d3a7098238ea3af41be010f18d6627fd061b6c9edf","impliedFormat":1},{"version":"fca68ac3b92725dbf3dac3f9fbc80775b66d2a9c642e75595a4a11a2095b3c9a","impliedFormat":1},{"version":"245d13141d7f9ec6edd36b14844b247e0680950c1c3289774d431cbbd47e714e","impliedFormat":1},{"version":"4326dc453ff5bf36ad778e93b7021cdd9abcfc4efe75a5c04032324f404af558","impliedFormat":1},{"version":"27b47fbd2f2d0d3cd44b8c7231c800f8528949cc56f421093e2b829d6976f173","impliedFormat":1},{"version":"0795a213434963328e8b60e65a9d03a88efc138ae171bbcca39d9000c040e7a4","impliedFormat":1},{"version":"fc745bebefc96e2a518a2d559af6850626cada22a75f794fd40a17aae11e2d54","impliedFormat":1},{"version":"2b0fe9ba00d0d593fb475d4204214a0f604ad8a56f22a5f05c378b52205ef36b","impliedFormat":1},{"version":"3d94a259051acf8acd2108cee57ad58fee7f7b278de76a7a5746f0656eecbff6","impliedFormat":1},{"version":"46097d076be332463ea64865c41d232865614cf358a11af75095dd9cef2871cc","impliedFormat":1},{"version":"6e18a70a7c64e6fe578a8f3ecc1dd562cd0bf6843bbf8e65fde37cf63b9a8ea8","impliedFormat":1},{"version":"3f3526aea8d29f0c53f8fb99201c770c87c357b5e87349aca8494bfd0c145c26","impliedFormat":1},{"version":"6ee92d844e5a1c0eb562d110676a3a17f00d2cd2ea2aaaff0a98d7881b9a4041","impliedFormat":1},{"version":"b9dc36d1f7c5c2350feafb55c090127104e59b7d2a20729b286dab00d70e283d","impliedFormat":1},{"version":"45d3f1d53fa99783a5e3c29debb065d6060d0db650a6a1055308a8619bd6b263","impliedFormat":1},{"version":"a14febaf38fd75a88620a0808732cf9841afc403da2dc3de7a6fc9a49d36bdbc","impliedFormat":1},{"version":"6052522a593f094cfee0e99c76312a229cf2d49ac2e75095af83813ec9f4b109","impliedFormat":1},{"version":"a0ceb6ce93981581494bae078b971b17e36b67502a36a056966940377517091d","impliedFormat":1},{"version":"a63ce903dd08c662702e33700a3d28ca66ed21ac0591e1dbf4a0b309ae80e690","impliedFormat":1},{"version":"2b63d2725550866e0f2b56b2394ce001ebf1145cb4b04dc9daa29d73867b878c","impliedFormat":1},{"version":"e885933b92f26fa3204403999eddc61651cd3109faf8bffa4f6b6e558b0ab2fa","impliedFormat":1},{"version":"bd834465d4395ac3d8d55e94bf2a39c1f5e9be719c99340957b3b6a3a85ec66a","impliedFormat":1},{"version":"0b1238c0e3536321ae822c84216614bad2f3a7bd3f1de5c6ec8a85b26d900e6b","impliedFormat":1},{"version":"6e2d2b63c278fd1c8dd54da2328622c964f50afa62978ed1a73ccd85e99a4fc7","impliedFormat":1},{"version":"e151e41c82004cf09b7ea863f591348c9035e0f7a69d4189cbac89cc9611b89d","impliedFormat":1},{"version":"74d62eb5f24ae3e1fa7374380fa6ef354449757293c7434d00b702b1c7f87249","impliedFormat":1},{"version":"b83ffe71adbac91c5596133251e5ec0c9e6664017ee5b776841effe93de8f466","impliedFormat":1},{"version":"61ecf051972c69e7c992bab9cf74c511ecba51b273c4e1590574d97a542bd4ea","impliedFormat":1},{"version":"068f5afbae92a20a5fcd9cfce76f7b90de2c59a952396b5da225b61f95a1d60a","impliedFormat":1},{"version":"bdf5e07a22e661de2c7115e8364b98ef399c24c9fe62035dc1ac945a9dd3372a","impliedFormat":1},{"version":"4e024e2530feda4719448af6bdd0c0c7cfa28d1a4887900f4886bec70cd48fea","impliedFormat":1},{"version":"99c88ea4f93e883d10c04961dbf37c403c4f3c8444948b86effec0bf52176d0e","impliedFormat":1},{"version":"e88f3729fcc3d38d2a1b3cdcbd773d13d72ea3bdf4d0c0c784818e3bfbe7998d","impliedFormat":1},{"version":"f25b1264b694a647593b0a9a044a267098aaf249d646981a7f0503b8bb185352","impliedFormat":1},{"version":"964d0862660f8e46675c83793f42ab2af336f3d6106dee966a4053d5dc433063","impliedFormat":1},{"version":"292ad4203c181f33beb9eb8fe7c6aaae29f62163793278a7ffc2fcc0d0dbed19","impliedFormat":1},{"version":"4e04e6263670ad377f2f6bcd477def099ac3634d760ee8a7cca74a6f39d70a48","impliedFormat":1},{"version":"f1a4ca3688d951daa2d7740da5a0827fa34d4a7709eed7b8225215986ee87108","impliedFormat":1},{"version":"7879a9ca9f953587b6d1471d5b9c7ed0d9852f1a30e9c5b6a7227a7bb7a0894d","impliedFormat":1},{"version":"f8453a3fe0fe49ab718357120bec2b8205e15eb91ff62eada60a4780458fa91e","impliedFormat":1},{"version":"06f186bb9a6408ef8563dbf17d53cbe23e68422518b49b96afac732844ddbaa1","impliedFormat":1},{"version":"525f9c06245b5b43b1237cfd757396fd7fd8090e5d6a4ded758c7ce17a04bf42","impliedFormat":1},{"version":"04bc74b8fa987f140989e9f4d6dc37f04a307417af3e0a3767baa1eef4964e10","impliedFormat":1},{"version":"6a9d3aa58228faa62ec3d9e305f472a24441f22a8d028234577beb592ec295b2","impliedFormat":1},{"version":"683e2d454f64394931d233740b762dabc379e3ce5c4c4ad4747cdbd6d5fd8e8d","impliedFormat":1},{"version":"18594ddc7900f3e477645819bce4d824989ad296e3d70bdcdce13cabc5d97335","impliedFormat":1},{"version":"9376cce4d849f1d6ad2cb0048807c77cfeb78cee6e29b61dcfe74c7ab2980e18","impliedFormat":1},{"version":"2698935791615907eb632186119dfc307363d6a163f26017084009e44ea261f2","impliedFormat":1},{"version":"4edfc4848068bf58016856dfeb27341c15679884575e1a501e2389a1fea5c579","impliedFormat":1},{"version":"0c3d7a094ef401b3c36c8e3d88382a7e7a8b1e4f702769eba861d03db559876b","impliedFormat":1},{"version":"d3c3280f081f28e846239d27c2f77a41417e6a19f39267d20a282fd07ef36b96","impliedFormat":1},{"version":"7e3a4800683a39375bc99f0d53b21328b0a0377ab7cbb732c564ca7ca04d9b37","impliedFormat":1},{"version":"c777b498a93261d6caa5dbd1187090b79f0263a03526c64ea4f844a679e8299e","impliedFormat":1},{"version":"b4677e9d8802a82455a0f03a211b85f5d4b04cfbc89fc9aa691695b8e70df326","impliedFormat":1},{"version":"7cb0d946957daea11f78a31b85de435e00bcd8964eba66d3e8056ba9d14b9c55","impliedFormat":1},{"version":"b3e441cdb9d9e55e6e120052fe8bf2a8b5e5a46287f21d5bc39561594574e1a9","impliedFormat":1},{"version":"0870e8eb0527c044e844a1d83127f020aa7f79048218a62b2875e818355f8cb2","impliedFormat":1},{"version":"6b7446f89f9e5d47835117416e6d7656bac2bf700513d330254ae979260ce99f","impliedFormat":1},{"version":"9750752db342b88df1b860958a20fac9fd6a507f67c5cfb6bd5cfa8759338b1e","impliedFormat":1},{"version":"946de511c5e04659d9dfaf5ef83770122846d26d3ffe30e636d3339482bbf35a","impliedFormat":1},{"version":"fbcc201a8fc377a92714567491e3f81e204750b612d51a1720af452f1a254760","impliedFormat":1},{"version":"6dd704b0ba0131eb9e707aeedc39be6a224b4669544e518217a75eb7f5dd65c2","impliedFormat":1},{"version":"6effa89f483e5c83c0e0063df5f1d8b006d9d0f1de7eed2233886642424dc8fb","impliedFormat":1},{"version":"84a8c844f9562da8994c07b44dd2777178a147e06020c62a7f6e349e695e7149","impliedFormat":1},{"version":"d43130c35762a80da2299f8b59a4321b6e64acfb0b11a36183379b4c7b83314b","impliedFormat":1},{"version":"6bf44b890824799af8e20c0387ffa987e890fac5c5954a3a7352351eefe55d5d","impliedFormat":1},{"version":"892b19153694b7a3c9a69bcedb54e1c8ad3b9fa370076db4d3522838afd2cd60","impliedFormat":1},{"version":"5461fca70947a4d8fa272d3dda4c729317cec825141313352adf33bc94de142a","impliedFormat":1},{"version":"f83afa274e0f11860c6609198ecca220f5df60690923b990ca06cae21771016e","impliedFormat":1},{"version":"af31f37264ea5d5349eec50786ceca75c572ed3be91bdd7cb428fdd8cd14b17c","impliedFormat":1},{"version":"85e4673ec8507aef18afd4a9acfae0294bdfaac29458ede0b8b56f5a63738486","impliedFormat":1},{"version":"40683566071340b03c74d0a4ffa84d49fedb181a691ce04c97e11b231a7deee4","impliedFormat":1},{"version":"81c8ab81daa2286241ad27468d6fc7ad3ecc62da04b18b77ce9b9b437f6b0863","impliedFormat":1},{"version":"f158721f7427976b5510660c8e53389d5033c915496c028558c66caaf3d1db1c","impliedFormat":1},{"version":"8e56db8febfe127a9142435940c9a5a1ad17ddb2b2a6d8e9e8984785a76db1fd","impliedFormat":1},{"version":"6113c2f172a875db117357f0aa35aa7c1b6316516e813977ef98dc3b4b8baf2a","impliedFormat":1},{"version":"f25c9802b1316afbf667dd8fa6db4ed23aa5e7acc076a1054ca45d7bc9c8e811","impliedFormat":1},{"version":"e99285f74c22ad823c0b9fac55316b84144e15eb91830034badd9eb0fafe71bf","impliedFormat":1},"56ab0e0b41f0cc7902414e046ea4f710bdbf0b756028672ea8c7e5137a278f2b","56b2869e5478f38238e6a3d8aefabfbe8af565b38b2ffad06466487b3eacf154","3fdf7ef1ca89c4d85de5ecbb25813d0f572003a53b021baf43c5f3523e48ec97","6763c86c34c1c07826386d376f5da8fe2e41585bff3615716d5da88915e088d8","7ae7f0f5d907be24658652c771fee1685193102bf3200aafbf578d144d379fbf","2e65ddecb4401220be374cc6cfc42344538266cafeb2e590b3c760c7ed6b490a","282041e2e8c52cc12e7bbc717bbd3f8c9c5ba18e7fa75b97053bc79687e1181e",{"version":"64e4057dd49916d73d3c577e0751a6c8f22c6152b295ea019161966051951a74","impliedFormat":99},{"version":"caf4af98bf464ad3e10c46cf7d340556f89197aab0f87f032c7b84eb8ddb24d9","impliedFormat":99},{"version":"0a60daa545b41003e922202c01e47d0cea514b09ba0af1e4684222f5b332841e","impliedFormat":99},{"version":"8dd450de6d756cee0761f277c6dc58b0b5a66b8c274b980949318b8cad26d712","impliedFormat":99},{"version":"904d6ad970b6bd825449480488a73d9b98432357ab38cf8d31ffd651ae376ff5","impliedFormat":99},{"version":"2059b04988eece7f6e992a5c218e9c5361b8fed24cb04caddb7e1cb4339e236b","impliedFormat":99},{"version":"bb703864a1bc9ca5ac3589ffd83785f6dc86f7f6c485c97d7ffd53438777cb9e","impliedFormat":1},"8bc85def62538b4e62e8530c6b0ba167a192c38519c05f104bf6e10de73b811f","9c1eae65aa105935a34c6b736f673b71a2ef939dde53dce29d986221edbf1f9f","05c3646b452f5e3b9eb952eeba3d90657606d2b0875cb77652cdb5eadba0eb5c","2dea8a95accb1723db2454ccc858697d5b1f1454568cf4da5e8fcbd0cf7cc1ee","b7ac53d0e5d1fed25fc3f2ac5f5a36e247ca684c677ac376829fa8a7c039b79e","efc088f3d79f64cf96e222701ae23dcf385b53947b3cd57ffb7be6f5dadcbfe2","bf54bac4881b79f6a53f55e70c18640f58a058add88aed763c71be35c4f64f59","e4a6fb662c44a44c468d16d79d02b3a3e5bf7b77bd50aef1e586c4d9613df0f6","79023ca89aae39f9cffa83a9483c9c672099b4547cc821486283db61c6ae9327","d8cb7270c5893213fc00b5f270f9bc461e2d7ce5b81f77b8778253a1aeda6a2c",{"version":"b843496b17a2bbd79c83809c73fd9c59fab53d3e361e04e52e2d489524eea764","impliedFormat":1},"867f568259caac95f682d128d72e681b3fe7c31701a396eacc9284401d917a6a","9c5e45e6c4aae5b9c4c1304e1f1208d32681ab616a6e0de9de6e0d6b22690592","bdfb38a4f7b9b604e196b5cee0d86f12311dae72946f0dbf5928c8ad0638178f","cd1e872384cf5001e48b4dc3cc2bb011eba9700561d41c83a89e872b60baf175","a3715f8e8b78261f5784929c532e100759c58e831c91e842438730797bc9a124",{"version":"281308d2193eccf7b873e3349edddce7cd8109cfb5ddda59881408995d277816","impliedFormat":99},{"version":"c52ebd5d88d2f32245805e029d5cb8fa86662557c76500bdb060c90b32de1043","impliedFormat":99},"9512c586d1b0885cf3ae4f1c3dd4281cbdfec7428fe98ff48a66b4ba2a7319f4","2fedcc64ed8563a44f6697615e77574d96f819fe6e5e2fa8a555e30f8ed6bbd7","3aafb74b98c039dae4eb2c64c47ec6f5d61fdc888c057de47550346f702f434b","c8e64bf5241145b512ae18f1062da8275691edf927736496a3860bec76ad6fa1","1f3c1713e8824cca7c3c0edacf9da7b0114bfda58640771b8fe0bc6179b370ce","7376624438588e2d64b6e0c5cdf82cbc5fd4530bbcf1f0235c5f3ee08d12e877","0d8ddec9731940e104b851cf3ac67d2fa8589b289824317a9e8f326e090ba65a","43f2d6c2f5bdd4a26c57d93e6f141be20472f9bb8777d9a0d61cd8a5e281a999",{"version":"eaa851dbeb0f68f629150bf2205b533fbad4d2fd22f55f2f39ee1c65899900b3","impliedFormat":99},"ea7b85817d4dc70866e0bcf15264e7e1bd7e1893eb4404261fbc23a4d6ff9029","730c4cc49f8f1b705eb1389cd32c774056a0a2a7a48a9b092cbe44c608be3a0b","45489c502a21fdd5271481d64ad8f762181c9653d8d26bdc6f75450b78c3c852","adb432b6f9d7a4733caa45ba8bf510a8a7ff0f6d0dd3aa1f013959fb61268234","4c342102b2bbf5bd8c04fa151fe5c13c42faee2f9587a3586c27322020ca35d1","b86e51969931c3d62e88d0bdacd1cd8219105dc6d6b6a588a80011bf92e987cd","aa3322ca63f098aa1d2abf750f9d097da07362fce25939357f2cefc9b6a03988","af657e73d672f4dd205f19abf151d21a172cf36abfdca49fd7014a20e12367a9","8ddbb330e5564a1f53026da36c2322ed4b027687e91a88f63348ab10f26ae0cb","04b085443a1c6a72d408e6eb7dd45df881ca27814415b81b39f614806b52c8fc","88f2ba7877eea7f34a5da18b7fa096eb2c7bed602677ce54f5494b494c5afc2e","dd914d769bc223b5d5ea56839a7752e534eb475f8d2897d5675606c368bbca1f","9126b09f47f0b117bf748cf19aaa4987a1f72d0813d10bfc4b4b0128e6439e48","6bf41d6f6ffe602c3e28274a82ac029e6ee1331ba3c670a6d814048e1823cc28","9b006523be0fe3230cd75f7adf78a501829d594fd3506c56c55bb38211ff1850",{"version":"67722e8cd5bbfc5ddc0bb6900623fa90d4cf0b97ea3d3f8e195c5a4a811ec248","affectsGlobalScope":true,"impliedFormat":1}],"root":[[556,558],632,633,636,637,1227,1244,[1627,1637],2071,2072,2109,[2246,2252],[2260,2269],[2271,2275],[2278,2285],[2287,2301]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[556,1],[637,2],[1227,3],[1244,4],[1628,5],[1629,6],[1630,7],[1631,8],[1632,9],[1633,10],[1634,11],[1635,12],[1637,13],[1627,14],[2071,15],[2072,16],[2109,17],[2246,18],[2247,19],[2248,20],[2252,21],[2261,22],[2262,23],[2260,24],[2263,25],[2264,26],[2265,27],[2266,28],[2268,29],[2269,30],[2251,31],[2271,32],[2249,33],[2272,34],[2273,7],[2267,16],[2274,12],[2275,35],[2278,36],[2279,16],[2280,37],[2281,38],[2282,39],[2283,40],[2284,41],[2285,42],[2287,43],[2288,44],[2289,45],[1636,46],[2290,24],[2293,47],[2291,7],[2294,48],[2295,49],[2296,50],[2297,16],[2298,51],[2250,16],[2299,52],[2301,53],[2300,54],[2292,55],[633,56],[1217,57],[1221,58],[1212,59],[1205,60],[893,61],[1216,62],[892,63],[1201,63],[1200,62],[1224,64],[1179,63],[1198,65],[1197,62],[1189,62],[1195,60],[1210,63],[1193,60],[1184,60],[1218,59],[1194,66],[1196,62],[1203,67],[1204,68],[1208,59],[1215,69],[890,63],[1188,70],[1211,71],[1181,63],[1199,62],[1202,59],[1207,72],[1214,73],[1222,59],[888,59],[891,60],[1183,62],[1209,59],[1226,74],[1180,75],[638,59],[1187,76],[1186,63],[1185,59],[1219,59],[1220,77],[1213,59],[1182,78],[1225,79],[1223,80],[1192,62],[1190,60],[1191,60],[636,81],[557,82],[558,59],[1238,83],[1242,84],[1241,85],[1237,86],[1240,87],[1233,88],[1239,83],[1620,89],[1624,90],[1623,91],[1621,92],[1622,93],[1327,94],[1326,95],[1306,96],[1318,97],[1297,98],[1322,97],[1304,96],[1298,1],[1330,99],[1329,100],[1332,101],[1331,102],[1328,97],[1333,97],[1334,103],[1339,104],[1340,105],[1338,106],[1337,107],[1336,108],[1335,104],[1344,109],[1343,110],[1342,111],[1235,112],[1236,113],[1341,114],[1315,115],[1312,116],[1354,97],[1353,97],[1352,97],[1308,116],[1320,1],[1321,97],[1317,97],[1316,97],[1307,97],[1357,117],[1356,118],[1348,96],[1305,96],[1351,116],[1350,97],[1346,119],[1309,97],[1314,120],[1311,97],[1313,115],[1296,121],[1345,98],[1324,122],[1325,123],[1319,97],[1355,97],[1310,97],[1349,96],[1347,1],[1390,124],[1389,125],[1387,126],[1364,127],[1388,97],[1391,56],[1393,128],[1392,129],[1394,116],[1395,97],[1396,97],[1403,130],[1402,131],[1397,132],[1398,97],[1619,133],[1618,134],[1617,135],[1400,97],[1401,136],[1399,116],[1405,137],[1407,138],[1406,139],[1408,116],[1409,97],[1410,97],[1411,97],[1415,89],[1413,97],[1412,97],[1427,140],[1426,141],[1418,142],[1419,97],[1420,56],[1416,143],[1417,144],[1421,145],[1422,97],[1423,146],[1424,116],[1425,56],[1431,104],[1430,119],[1429,147],[1435,148],[1434,149],[1433,119],[1428,119],[1302,150],[1432,151],[1439,152],[1438,153],[1437,97],[1436,97],[1264,154],[1265,155],[1268,156],[1263,157],[1284,158],[1286,159],[1279,160],[1287,161],[1269,159],[1270,162],[1288,163],[1282,164],[1271,159],[1275,165],[1276,159],[1277,166],[1274,167],[1280,168],[1289,169],[1281,170],[1290,171],[1283,172],[1285,173],[1278,159],[1273,174],[1300,175],[1301,176],[1616,177],[1441,178],[1440,179],[1230,180],[1404,1],[1323,123],[1299,181],[1266,123],[1559,97],[1228,123],[1229,182],[1295,1],[1272,123],[1232,183],[1303,184],[1267,185],[1234,1],[1358,115],[1359,116],[1368,116],[1367,186],[1370,97],[1369,97],[1386,187],[1385,188],[1371,97],[1372,97],[1373,120],[1374,97],[1375,115],[1376,186],[1378,116],[1377,97],[1366,189],[1362,190],[1365,191],[1361,192],[1380,193],[1379,194],[1384,97],[1381,195],[1382,97],[1443,196],[1442,186],[1363,197],[1445,198],[1444,97],[1452,199],[1451,200],[1448,201],[1450,201],[1446,97],[1447,201],[1449,201],[1463,115],[1461,116],[1456,116],[1465,97],[1467,202],[1466,203],[1455,97],[1464,97],[1454,97],[1462,204],[1458,97],[1459,115],[1453,88],[1457,97],[1460,97],[1472,205],[1470,205],[1471,205],[1477,206],[1476,207],[1473,205],[1469,208],[1475,205],[1474,205],[1468,180],[1482,209],[1481,210],[1480,211],[1479,212],[1478,123],[1491,115],[1492,116],[1495,97],[1494,97],[1498,213],[1497,214],[1490,120],[1488,97],[1489,115],[1486,215],[1485,216],[1484,217],[1493,97],[1487,218],[1496,97],[1506,115],[1507,116],[1510,219],[1509,220],[1505,204],[1502,97],[1504,115],[1501,221],[1500,222],[1499,223],[1503,224],[1508,97],[1517,225],[1516,226],[1513,227],[1515,227],[1511,97],[1512,227],[1514,227],[1523,228],[1522,104],[1521,229],[1520,230],[1519,231],[1518,119],[1527,232],[1529,97],[1531,233],[1530,234],[1524,97],[1526,232],[1528,97],[1525,232],[1545,115],[1538,116],[1549,97],[1548,97],[1536,97],[1552,235],[1551,236],[1543,116],[1544,97],[1542,97],[1533,119],[1541,97],[1540,120],[1537,97],[1539,115],[1532,121],[1546,97],[1547,97],[1550,97],[1534,96],[1535,97],[1553,237],[1383,97],[1557,238],[1563,239],[1562,240],[1561,238],[1555,238],[1554,104],[1560,241],[1558,238],[1556,238],[1567,242],[1566,243],[1564,244],[1565,245],[1574,246],[1573,247],[1570,248],[1572,249],[1571,250],[1569,251],[1568,249],[1585,97],[1587,115],[1584,97],[1581,97],[1577,252],[1582,97],[1589,253],[1588,254],[1586,97],[1575,255],[1578,256],[1580,257],[1583,97],[1576,258],[1579,97],[1593,259],[1592,88],[1591,260],[1590,88],[1597,261],[1596,261],[1601,262],[1600,263],[1599,261],[1598,261],[1595,97],[1594,264],[1609,115],[1613,265],[1612,266],[1608,204],[1606,97],[1607,115],[1610,1],[1604,267],[1603,268],[1602,269],[1605,270],[1611,97],[1231,271],[1615,272],[1614,185],[1360,123],[1294,273],[1260,274],[1262,275],[1292,276],[1261,123],[1291,277],[1293,278],[1414,1],[1248,1],[1246,279],[1247,280],[1253,281],[1251,282],[1249,123],[1252,283],[1250,284],[1254,1],[1483,123],[2302,123],[2062,123],[2063,285],[2064,286],[2069,287],[2065,286],[2068,123],[2066,123],[2067,123],[1256,288],[1258,289],[1259,290],[1255,123],[1257,123],[587,291],[585,123],[586,292],[588,293],[583,294],[581,123],[584,295],[582,296],[564,123],[2253,1],[2258,297],[2255,298],[2256,298],[2257,298],[2254,1],[2124,299],[2123,59],[2121,123],[566,123],[567,300],[2276,1],[2277,301],[2133,123],[2111,302],[2134,303],[2110,123],[142,304],[143,304],[144,305],[97,306],[145,307],[146,308],[147,309],[92,123],[95,310],[93,123],[94,123],[148,311],[149,312],[150,313],[151,314],[152,315],[153,316],[154,316],[155,317],[156,318],[157,319],[158,320],[98,123],[96,123],[159,321],[160,322],[161,323],[195,324],[162,325],[163,123],[164,326],[165,327],[166,328],[167,329],[168,330],[169,331],[170,332],[171,333],[172,334],[173,334],[174,335],[175,123],[176,336],[177,337],[179,338],[178,339],[180,340],[181,341],[182,342],[183,343],[184,344],[185,345],[186,346],[187,347],[188,348],[189,349],[190,350],[191,351],[192,352],[99,123],[100,123],[101,123],[139,353],[140,123],[141,123],[193,354],[194,355],[199,356],[462,1],[200,357],[198,358],[464,359],[463,360],[196,361],[460,123],[197,362],[83,123],[85,363],[459,1],[230,1],[887,364],[1175,365],[895,366],[896,367],[1178,368],[1177,369],[1206,367],[889,367],[1176,123],[748,370],[662,371],[747,372],[746,373],[749,374],[661,375],[750,376],[751,377],[752,378],[753,379],[754,379],[755,379],[756,378],[757,379],[760,380],[761,381],[758,123],[759,382],[762,383],[728,384],[650,385],[764,386],[765,387],[727,388],[766,389],[639,123],[643,390],[676,391],[767,123],[674,123],[675,123],[768,392],[769,393],[770,394],[644,395],[645,396],[640,123],[745,397],[744,398],[679,399],[771,400],[772,123],[697,123],[698,401],[773,402],[663,403],[664,404],[665,405],[666,406],[774,407],[776,408],[777,409],[778,410],[779,409],[785,411],[775,410],[780,410],[781,409],[782,410],[783,409],[784,410],[786,123],[787,123],[876,412],[788,413],[789,414],[790,393],[791,393],[792,393],[794,415],[793,393],[796,416],[797,393],[798,417],[811,418],[799,416],[800,419],[801,416],[802,393],[795,393],[803,393],[804,420],[805,393],[806,416],[807,393],[808,393],[809,421],[810,393],[813,422],[815,423],[816,424],[817,425],[818,426],[821,427],[822,423],[824,428],[825,429],[828,430],[829,431],[831,432],[832,433],[833,434],[820,435],[819,436],[823,437],[709,438],[835,439],[708,440],[827,441],[826,442],[836,434],[838,443],[837,444],[1166,445],[1167,446],[841,447],[842,448],[843,449],[844,123],[845,450],[846,451],[847,452],[848,448],[849,448],[850,448],[840,453],[851,123],[839,454],[852,455],[1168,456],[1169,457],[1170,458],[853,459],[854,460],[684,461],[685,462],[740,463],[704,464],[686,465],[687,466],[688,467],[689,468],[690,469],[691,470],[692,468],[694,471],[693,468],[695,469],[1139,472],[1140,473],[1141,472],[1094,474],[1142,475],[1143,476],[1144,477],[1145,478],[1146,479],[1148,480],[1149,481],[1147,482],[1151,483],[1095,484],[1158,485],[1153,486],[1155,487],[1152,488],[1157,489],[696,461],[701,490],[700,491],[702,492],[703,461],[713,413],[671,493],[652,494],[651,495],[653,496],[647,497],[706,498],[855,499],[657,123],[658,500],[659,500],[660,500],[856,500],[667,501],[857,502],[858,123],[642,503],[648,504],[669,505],[646,506],[743,507],[668,508],[654,496],[834,496],[670,509],[641,510],[655,511],[649,512],[1047,513],[1048,514],[956,515],[1049,516],[913,517],[912,518],[1050,513],[1051,519],[1073,520],[957,521],[959,522],[958,523],[1072,524],[960,525],[1052,526],[962,527],[964,528],[1053,513],[965,529],[1054,530],[963,531],[966,513],[1055,513],[961,513],[1056,513],[967,532],[968,533],[969,534],[953,535],[970,536],[1057,537],[971,538],[1058,513],[942,539],[972,513],[973,540],[974,541],[1059,537],[1060,542],[975,537],[989,543],[990,544],[993,545],[992,546],[994,513],[995,513],[904,547],[905,548],[906,543],[907,513],[922,549],[897,123],[899,550],[908,543],[923,513],[898,551],[926,552],[1026,553],[1138,554],[914,555],[1137,556],[921,557],[915,123],[909,558],[991,559],[927,123],[1019,560],[916,561],[935,562],[917,123],[918,563],[928,513],[930,564],[929,513],[900,513],[931,565],[920,566],[919,551],[932,513],[903,551],[910,555],[911,567],[933,513],[934,568],[1061,513],[976,547],[1062,513],[1064,513],[977,569],[978,513],[979,570],[980,571],[981,513],[982,570],[983,513],[1065,513],[984,572],[1063,573],[985,513],[987,574],[988,575],[986,576],[1066,513],[1022,577],[1103,513],[1104,513],[1105,513],[1106,513],[1107,513],[1108,513],[1109,513],[1111,516],[1112,513],[1113,513],[1114,513],[1023,578],[1024,579],[1154,580],[1150,577],[1156,577],[996,581],[1067,513],[1068,513],[1069,513],[997,582],[1070,547],[1071,513],[1003,583],[1002,584],[1004,585],[1005,585],[1006,586],[1007,585],[1008,587],[1010,588],[1011,585],[1012,589],[924,555],[1013,590],[925,591],[1110,513],[1015,592],[1014,593],[1016,588],[1017,594],[1000,595],[1096,596],[1001,597],[1009,598],[1100,513],[1101,513],[1102,599],[955,547],[1044,513],[1028,513],[1030,600],[1031,601],[1029,547],[1032,513],[1025,602],[1020,560],[1021,603],[1018,604],[1097,513],[1098,513],[1099,513],[1115,605],[1046,606],[902,607],[936,608],[937,555],[938,548],[1045,513],[1033,513],[939,609],[940,513],[901,608],[941,543],[1034,513],[943,610],[944,555],[1035,513],[1036,513],[1037,513],[945,611],[946,513],[947,547],[948,612],[1038,513],[949,613],[950,513],[951,513],[1039,513],[954,614],[1040,513],[1041,513],[1042,513],[1043,513],[859,615],[656,373],[998,616],[677,373],[860,617],[812,618],[861,619],[814,619],[862,387],[729,620],[863,618],[742,621],[830,622],[705,623],[1118,624],[1086,625],[1083,626],[1121,627],[1081,628],[1074,123],[1125,629],[1077,630],[1082,631],[1116,123],[999,632],[1126,633],[1122,634],[1084,123],[1085,123],[1159,635],[1087,636],[673,637],[1027,373],[1119,638],[1088,639],[1127,640],[1117,641],[1076,642],[1080,643],[1090,123],[1123,644],[1160,645],[1091,646],[1128,647],[1129,648],[1130,649],[1089,650],[1131,651],[1136,652],[1165,653],[1120,654],[1132,655],[1093,656],[1133,657],[1092,634],[1171,414],[1172,414],[672,392],[952,658],[1173,659],[1124,660],[1075,661],[1078,662],[1079,663],[1135,664],[1134,400],[877,123],[878,665],[699,666],[879,667],[734,668],[735,669],[880,670],[714,671],[736,672],[737,673],[881,674],[715,123],[882,675],[883,123],[722,676],[738,677],[724,123],[721,678],[739,679],[716,123],[723,680],[884,123],[725,681],[717,682],[719,683],[720,684],[718,685],[865,686],[867,687],[763,688],[733,689],[707,690],[1161,691],[1163,692],[1164,693],[731,694],[885,695],[732,696],[710,633],[711,633],[712,697],[868,414],[869,698],[870,698],[680,699],[681,414],[864,700],[866,701],[741,699],[678,414],[730,659],[871,414],[682,496],[683,702],[873,703],[872,414],[875,704],[886,705],[1174,706],[874,123],[726,123],[554,707],[552,123],[550,123],[549,708],[551,709],[485,710],[490,711],[534,712],[548,713],[497,714],[480,715],[234,123],[242,716],[381,717],[384,718],[357,123],[370,719],[377,720],[259,123],[359,123],[240,123],[356,721],[401,722],[241,123],[232,723],[383,724],[385,725],[386,726],[457,727],[351,728],[304,729],[364,730],[365,731],[363,732],[362,123],[358,733],[382,734],[243,735],[427,123],[428,736],[270,737],[244,738],[271,737],[307,737],[210,737],[379,739],[378,123],[369,740],[475,123],[533,741],[219,123],[496,742],[435,743],[436,744],[432,745],[514,123],[334,123],[437,56],[433,746],[519,747],[518,748],[513,123],[285,123],[337,749],[336,123],[512,750],[434,1],[290,751],[297,752],[299,753],[289,123],[294,754],[296,755],[298,756],[293,757],[291,123],[295,758],[515,123],[511,123],[517,759],[516,123],[288,760],[542,1],[543,761],[506,762],[535,56],[509,763],[278,764],[277,765],[276,766],[522,1],[275,767],[264,123],[524,123],[538,768],[540,768],[537,123],[545,769],[525,1],[526,770],[202,123],[366,771],[367,772],[368,773],[206,123],[371,123],[226,774],[201,123],[449,1],[208,775],[448,776],[447,777],[438,123],[439,123],[446,123],[441,123],[444,778],[440,123],[442,779],[445,780],[443,779],[239,123],[236,123],[237,781],[390,123],[395,782],[396,783],[394,784],[392,785],[393,786],[388,123],[455,56],[231,56],[484,787],[491,788],[495,789],[325,790],[324,123],[319,123],[471,791],[479,792],[352,793],[353,794],[430,795],[341,123],[453,796],[329,1],[346,797],[456,798],[342,123],[345,799],[343,123],[454,800],[451,801],[450,123],[452,123],[349,123],[426,802],[214,803],[327,804],[331,805],[347,806],[350,807],[339,808],[332,809],[478,810],[404,811],[323,812],[211,813],[477,814],[207,815],[397,816],[389,123],[398,817],[415,818],[387,123],[414,819],[91,123],[409,820],[235,123],[429,821],[405,123],[220,123],[222,123],[361,123],[413,822],[238,123],[262,823],[348,824],[268,825],[328,123],[412,123],[391,123],[417,826],[418,827],[360,123],[420,828],[422,829],[421,830],[372,123],[411,813],[424,831],[322,832],[410,833],[416,834],[247,123],[251,123],[250,123],[249,123],[254,123],[248,123],[257,123],[256,123],[253,123],[252,123],[255,123],[258,835],[246,123],[314,836],[313,123],[318,837],[315,838],[317,839],[320,837],[316,838],[546,840],[227,841],[306,842],[474,843],[472,123],[501,844],[503,845],[467,846],[502,847],[215,848],[212,848],[245,123],[229,849],[228,850],[224,851],[225,852],[233,853],[261,853],[272,853],[308,854],[273,854],[217,855],[216,123],[312,856],[311,857],[310,858],[309,859],[218,860],[458,861],[260,862],[466,863],[431,864],[461,865],[465,866],[355,867],[354,868],[335,869],[321,870],[303,871],[305,872],[302,873],[423,874],[326,123],[489,123],[223,875],[425,876],[473,877],[333,123],[263,878],[340,879],[338,880],[265,881],[399,882],[468,123],[266,883],[400,883],[487,123],[486,123],[488,123],[470,123],[469,123],[402,884],[330,123],[300,885],[221,886],[279,123],[205,887],[267,123],[493,1],[204,123],[505,888],[287,1],[499,56],[286,889],[482,890],[284,888],[209,123],[507,891],[282,1],[283,1],[274,123],[203,123],[281,892],[280,893],[269,894],[344,333],[403,333],[419,123],[407,895],[406,123],[292,760],[213,123],[301,1],[476,774],[483,896],[86,1],[89,897],[90,898],[87,1],[88,123],[380,899],[376,900],[375,123],[374,901],[373,123],[481,902],[492,903],[494,904],[498,905],[539,906],[541,907],[544,908],[500,909],[504,910],[553,911],[508,911],[531,912],[536,913],[510,914],[520,915],[547,916],[521,741],[523,917],[527,918],[530,774],[529,123],[528,919],[532,920],[630,921],[629,123],[1162,123],[1626,922],[1625,923],[634,123],[2259,924],[84,123],[1727,925],[1706,926],[1803,123],[1707,927],[1643,925],[1644,925],[1645,925],[1646,925],[1647,925],[1648,925],[1649,925],[1650,925],[1651,925],[1652,925],[1653,925],[1654,925],[1655,925],[1656,925],[1657,925],[1658,925],[1659,925],[1660,925],[1639,123],[1661,925],[1662,925],[1663,123],[1664,925],[1665,925],[1667,925],[1666,925],[1668,925],[1669,925],[1670,925],[1671,925],[1672,925],[1673,925],[1674,925],[1675,925],[1676,925],[1677,925],[1678,925],[1679,925],[1680,925],[1681,925],[1682,925],[1683,925],[1684,925],[1685,925],[1686,925],[1688,925],[1689,925],[1690,925],[1687,925],[1691,925],[1692,925],[1693,925],[1694,925],[1695,925],[1696,925],[1697,925],[1698,925],[1699,925],[1700,925],[1701,925],[1702,925],[1703,925],[1704,925],[1705,925],[1708,928],[1709,925],[1710,925],[1711,929],[1712,930],[1713,925],[1714,925],[1715,925],[1716,925],[1719,925],[1717,925],[1718,925],[1641,123],[1720,925],[1721,925],[1722,925],[1723,925],[1724,925],[1725,925],[1726,925],[1728,931],[1729,925],[1730,925],[1731,925],[1733,925],[1732,925],[1734,925],[1735,925],[1736,925],[1737,925],[1738,925],[1739,925],[1740,925],[1741,925],[1742,925],[1743,925],[1745,925],[1744,925],[1746,925],[1747,123],[1748,123],[1749,123],[1896,932],[1750,925],[1751,925],[1752,925],[1753,925],[1754,925],[1755,925],[1756,123],[1757,925],[1758,123],[1759,925],[1760,925],[1761,925],[1762,925],[1763,925],[1764,925],[1765,925],[1766,925],[1767,925],[1768,925],[1769,925],[1770,925],[1771,925],[1772,925],[1773,925],[1774,925],[1775,925],[1776,925],[1777,925],[1778,925],[1779,925],[1780,925],[1781,925],[1782,925],[1783,925],[1784,925],[1785,925],[1786,925],[1787,925],[1788,925],[1789,925],[1790,925],[1791,123],[1792,925],[1793,925],[1794,925],[1795,925],[1796,925],[1797,925],[1798,925],[1799,925],[1800,925],[1801,925],[1802,925],[1804,933],[1992,934],[1897,927],[1899,927],[1900,927],[1901,927],[1902,927],[1903,927],[1898,927],[1904,927],[1906,927],[1905,927],[1907,927],[1908,927],[1909,927],[1910,927],[1911,927],[1912,927],[1913,927],[1914,927],[1916,927],[1915,927],[1917,927],[1918,927],[1919,927],[1920,927],[1921,927],[1922,927],[1923,927],[1924,927],[1925,927],[1926,927],[1927,927],[1928,927],[1929,927],[1930,927],[1931,927],[1933,927],[1934,927],[1932,927],[1935,927],[1936,927],[1937,927],[1938,927],[1939,927],[1940,927],[1941,927],[1942,927],[1943,927],[1944,927],[1945,927],[1946,927],[1948,927],[1947,927],[1950,927],[1949,927],[1951,927],[1952,927],[1953,927],[1954,927],[1955,927],[1956,927],[1957,927],[1958,927],[1959,927],[1960,927],[1961,927],[1962,927],[1963,927],[1965,927],[1964,927],[1966,927],[1967,927],[1968,927],[1970,927],[1969,927],[1971,927],[1972,927],[1973,927],[1974,927],[1975,927],[1976,927],[1978,927],[1977,927],[1979,927],[1980,927],[1981,927],[1982,927],[1983,927],[1640,925],[1984,927],[1985,927],[1987,927],[1986,927],[1988,927],[1989,927],[1990,927],[1991,927],[1805,925],[1806,925],[1807,123],[1808,123],[1809,123],[1810,925],[1811,123],[1812,123],[1813,123],[1814,123],[1815,123],[1816,925],[1817,925],[1818,925],[1819,925],[1820,925],[1821,925],[1822,925],[1823,925],[1828,935],[1826,936],[1827,937],[1825,938],[1824,925],[1829,925],[1830,925],[1831,925],[1832,925],[1833,925],[1834,925],[1835,925],[1836,925],[1837,925],[1838,925],[1839,123],[1840,123],[1841,925],[1842,925],[1843,123],[1844,123],[1845,123],[1846,925],[1847,925],[1848,925],[1849,925],[1850,931],[1851,925],[1852,925],[1853,925],[1854,925],[1855,925],[1856,925],[1857,925],[1858,925],[1859,925],[1860,925],[1861,925],[1862,925],[1863,925],[1864,925],[1865,925],[1866,925],[1867,925],[1868,925],[1869,925],[1870,925],[1871,925],[1872,925],[1873,925],[1874,925],[1875,925],[1876,925],[1877,925],[1878,925],[1879,925],[1880,925],[1881,925],[1882,925],[1883,925],[1884,925],[1885,925],[1886,925],[1887,925],[1888,925],[1889,925],[1890,925],[1891,925],[1642,939],[1892,123],[1893,123],[1894,123],[1895,123],[2239,123],[2107,940],[2108,941],[2073,123],[2081,942],[2075,943],[2082,123],[2104,944],[2079,945],[2103,946],[2100,947],[2083,948],[2084,123],[2077,123],[2074,123],[2105,949],[2101,950],[2085,123],[2102,951],[2086,952],[2088,953],[2089,954],[2078,955],[2090,956],[2091,955],[2093,956],[2094,957],[2095,958],[2097,959],[2092,960],[2098,961],[2099,962],[2076,963],[2096,964],[2080,965],[2087,123],[2106,966],[578,123],[2164,123],[2270,1],[1243,1],[894,123],[607,967],[605,968],[606,969],[594,970],[595,968],[602,971],[593,972],[598,973],[608,123],[599,974],[604,975],[610,976],[609,977],[592,978],[600,979],[601,980],[596,981],[603,967],[597,982],[2043,983],[2002,984],[2001,985],[2042,986],[2044,987],[1993,1],[1994,1],[1995,1],[2020,988],[1996,989],[1997,989],[1998,990],[1999,1],[2000,1],[2003,991],[2045,992],[2004,1],[2005,1],[2006,993],[2007,1],[2008,1],[2009,1],[2010,1],[2011,1],[2012,1],[2013,992],[2014,1],[2015,1],[2016,992],[2017,1],[2018,1],[2019,993],[2051,990],[2021,983],[2022,983],[2023,983],[2026,983],[2024,983],[2025,123],[2027,983],[2028,994],[2052,995],[2053,996],[2070,997],[2040,998],[2031,999],[2029,983],[2030,999],[2033,983],[2032,123],[2034,123],[2035,123],[2036,983],[2037,983],[2038,983],[2039,983],[2049,1000],[2050,1001],[2046,1002],[2047,1003],[2041,1004],[1638,1],[2048,1005],[2054,999],[2055,999],[2061,1006],[2056,983],[2057,999],[2058,999],[2059,983],[2060,999],[2286,56],[2150,123],[2148,1007],[2152,1008],[2219,1009],[2214,1010],[2118,1011],[2185,1012],[2178,1013],[2235,1014],[2116,1015],[2184,1016],[2173,1017],[2218,1018],[2215,1019],[2167,1020],[2177,1021],[2220,1022],[2221,1022],[2222,1023],[2230,1024],[2224,1024],[2232,1024],[2236,1024],[2223,1024],[2225,1024],[2228,1024],[2231,1024],[2227,1025],[2229,1024],[2233,1026],[2226,1027],[2127,1028],[2199,1],[2196,1029],[2200,1],[2138,1024],[2128,1024],[2191,1030],[2117,1031],[2137,1032],[2141,1033],[2198,1024],[2114,1],[2197,1034],[2195,1],[2194,1024],[2129,1],[2241,1035],[2209,1027],[2189,1036],[2245,1037],[2207,123],[2205,123],[2210,1038],[2208,1039],[2204,1040],[2206,1041],[2211,1042],[2213,1043],[2203,1],[2136,1044],[2113,1024],[2202,1024],[2151,1045],[2201,1],[2176,1044],[2234,1024],[2169,1046],[2125,1047],[2130,1048],[2179,1049],[2181,1046],[2160,1050],[2163,1046],[2142,1051],[2162,1052],[2171,1053],[2172,1054],[2168,1055],[2182,1056],[2170,1057],[2147,1058],[2190,1059],[2186,1060],[2187,1061],[2183,1062],[2161,1063],[2149,1064],[2154,1065],[2131,1066],[2158,1067],[2159,1068],[2155,1069],[2132,1070],[2143,1071],[2180,1054],[2126,1072],[2188,123],[2153,1073],[2146,1074],[2174,123],[2243,1075],[2244,1076],[2216,123],[2242,1077],[2237,123],[2165,123],[2139,123],[2212,1078],[2166,123],[2119,1077],[2240,1079],[2145,1080],[2175,1081],[2144,1082],[2217,1083],[2156,123],[2192,123],[2193,1084],[2140,123],[2157,123],[2238,123],[2115,1],[2122,1085],[2120,123],[1245,123],[616,1086],[569,1087],[570,1088],[573,1089],[565,1090],[572,1091],[568,1092],[563,123],[574,1093],[575,1094],[408,1095],[591,123],[635,123],[589,1096],[81,123],[82,123],[13,123],[14,123],[16,123],[15,123],[2,123],[17,123],[18,123],[19,123],[20,123],[21,123],[22,123],[23,123],[24,123],[3,123],[25,123],[26,123],[4,123],[27,123],[31,123],[28,123],[29,123],[30,123],[32,123],[33,123],[34,123],[5,123],[35,123],[36,123],[37,123],[38,123],[6,123],[42,123],[39,123],[40,123],[41,123],[43,123],[7,123],[44,123],[49,123],[50,123],[45,123],[46,123],[47,123],[48,123],[8,123],[54,123],[51,123],[52,123],[53,123],[55,123],[9,123],[56,123],[57,123],[58,123],[60,123],[59,123],[61,123],[62,123],[10,123],[63,123],[64,123],[65,123],[11,123],[66,123],[67,123],[68,123],[69,123],[70,123],[1,123],[71,123],[72,123],[12,123],[76,123],[74,123],[79,123],[78,123],[73,123],[77,123],[75,123],[80,123],[117,1097],[127,1098],[116,1097],[137,1099],[108,1100],[107,1101],[136,919],[130,1102],[135,1103],[110,1104],[124,1105],[109,1106],[133,1107],[105,1108],[104,919],[134,1109],[106,1110],[111,1111],[112,123],[115,1111],[102,123],[138,1112],[128,1113],[119,1114],[120,1115],[122,1116],[118,1117],[121,1118],[131,919],[113,1119],[114,1120],[123,1121],[103,1122],[126,1113],[125,1111],[129,123],[132,1123],[2112,1124],[2135,1125],[627,1126],[620,1127],[619,1128],[621,1129],[628,123],[631,1130],[626,1131],[622,123],[624,1132],[623,123],[625,123],[559,123],[555,123],[561,1133],[618,1134],[577,1135],[611,123],[613,1136],[612,123],[562,1133],[560,123],[576,1137],[617,123],[615,123],[579,1138],[614,1139],[571,1140],[590,1141],[580,1142],[632,1143]],"affectedFilesPendingEmit":[637,1227,1244,1628,1629,1630,1631,1632,1633,1634,1635,1637,1627,2071,2072,2109,2246,2247,2248,2252,2261,2262,2260,2263,2264,2265,2266,2268,2269,2251,2271,2249,2272,2273,2267,2274,2275,2278,2279,2280,2281,2282,2283,2284,2285,2287,2288,2289,1636,2290,2293,2291,2294,2295,2296,2297,2298,2250,2299,2301,2300,2292,633,1217,1221,1212,1205,893,1216,892,1201,1200,1224,1179,1198,1197,1189,1195,1210,1193,1184,1218,1194,1196,1203,1204,1208,1215,890,1188,1211,1181,1199,1202,1207,1214,1222,888,891,1183,1209,1226,1180,638,1187,1186,1185,1219,1220,1213,1182,1225,1223,1192,1190,1191,636,558,632],"version":"5.9.3"} \ No newline at end of file